diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8e09808f08bba2..fb11c82bb96d9c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,13 +1,10 @@ { - "image": "ghcr.io/python/devcontainer:2025.05.29.15334414373", + "image": "ghcr.io/python/devcontainer:latest", "onCreateCommand": [ // Install common tooling. "dnf", "install", "-y", - "which", - "zsh", - "fish", // For umask fix below. "/usr/bin/setfacl" ], diff --git a/.devcontainer/wasi/devcontainer.json b/.devcontainer/wasi/devcontainer.json new file mode 100644 index 00000000000000..4266144ce47639 --- /dev/null +++ b/.devcontainer/wasi/devcontainer.json @@ -0,0 +1,73 @@ +{ + "image": "ghcr.io/python/wasicontainer:latest", + "onCreateCommand": [ + // Install common tooling. + "dnf", + "install", + "-y", + // For umask fix below. + "/usr/bin/setfacl" + ], + "updateContentCommand": { + // Using the shell for `nproc` usage. + "python": "python3 Tools/wasm/wasi build --quiet -- --with-pydebug -C" + }, + "postCreateCommand": { + // https://github.com/orgs/community/discussions/26026 + "umask fix: workspace": ["sudo", "setfacl", "-bnR", "."], + "umask fix: /tmp": ["sudo", "setfacl", "-bnR", "/tmp"] + }, + "customizations": { + "vscode": { + "extensions": [ + // Highlighting for Parser/Python.asdl. + "brettcannon.zephyr-asdl", + // Highlighting for configure.ac. + "maelvalais.autoconf", + // C auto-complete. + "ms-vscode.cpptools", + // Python auto-complete. + "ms-python.python" + ], + "settings": { + "C_Cpp.default.compilerPath": "/usr/bin/clang", + "C_Cpp.default.cStandard": "c11", + "C_Cpp.default.defines": [ + "CONFIG_64", + "Py_BUILD_CORE" + ], + "C_Cpp.default.includePath": [ + "${workspaceFolder}/*", + "${workspaceFolder}/Include/**" + ], + // https://github.com/microsoft/vscode-cpptools/issues/10732 + "C_Cpp.errorSquiggles": "disabled", + "editor.insertSpaces": true, + "editor.rulers": [ + 80 + ], + "editor.tabSize": 4, + "editor.trimAutoWhitespace": true, + "files.associations": { + "*.h": "c" + }, + "files.encoding": "utf8", + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + "python.analysis.diagnosticSeverityOverrides": { + // Complains about shadowing the stdlib w/ the stdlib. + "reportShadowedImports": "none", + // Doesn't like _frozen_importlib. + "reportMissingImports": "none" + }, + "python.analysis.extraPaths": [ + "Lib" + ], + "[restructuredtext]": { + "editor.tabSize": 3 + } + } + } + } +} diff --git a/.gitattributes b/.gitattributes index 5682b9150a36e2..4d52f900b93179 100644 --- a/.gitattributes +++ b/.gitattributes @@ -34,6 +34,9 @@ Lib/test/xmltestdata/* noeol Lib/venv/scripts/common/activate text eol=lf Lib/venv/scripts/posix/* text eol=lf +# Prevent GitHub's web conflict editor from converting LF to CRLF +*.rst text eol=lf + # CRLF files [attr]dos text eol=crlf @@ -68,6 +71,7 @@ PCbuild/readme.txt dos **/clinic/*.cpp.h generated **/clinic/*.h.h generated *_db.h generated +Doc/_static/tachyon-example-*.html generated Doc/c-api/lifecycle.dot.svg generated Doc/data/stable_abi.dat generated Doc/library/token-list.inc generated @@ -80,16 +84,22 @@ Include/internal/pycore_uop_ids.h generated Include/internal/pycore_uop_metadata.h generated Include/opcode.h generated Include/opcode_ids.h generated +Include/slots_generated.h generated Include/token.h generated Lib/_opcode_metadata.py generated +Lib/idlelib/help.html generated Lib/keyword.py generated +Lib/pydoc_data/topics.py generated +Lib/pydoc_data/module_docs.py generated Lib/test/certdata/*.pem generated Lib/test/certdata/*.0 generated Lib/test/levenshtein_examples.json generated Lib/test/test_stable_abi_ctypes.py generated +Lib/test/test_zoneinfo/data/*.json generated Lib/token.py generated Misc/sbom.spdx.json generated -Objects/typeslots.inc generated +Modules/_testinternalcapi/test_cases.c.h generated +Modules/_testinternalcapi/test_targets.h generated PC/python3dll.c generated Parser/parser.c generated Parser/token.c generated @@ -99,7 +109,11 @@ Python/executor_cases.c.h generated Python/generated_cases.c.h generated Python/optimizer_cases.c.h generated Python/opcode_targets.h generated +Python/record_functions.c.h generated +Python/slots_generated.c generated Python/stdlib_module_names.h generated Tools/peg_generator/pegen/grammar_parser.py generated aclocal.m4 generated configure generated +*.min.js generated +package-lock.json generated diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 02a7b5d45b4627..ecd24bb94d7564 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,348 +1,652 @@ # See https://help.github.com/articles/about-codeowners/ -# for more info about CODEOWNERS file +# for further details about the .github/CODEOWNERS file. # It uses the same pattern rule for gitignore file # https://git-scm.com/docs/gitignore#_pattern_format - -# GitHub -.github/** @ezio-melotti @hugovk @AA-Turner - -# pre-commit -.pre-commit-config.yaml @hugovk @AlexWaygood +# Notably, a later match overrides earlier matches, so order matters. +# If using a wildcard pattern, try to be as specific as possible to avoid +# matching unintended files or overriding previous entries. +# To exclude a file from ownership, add a line with only the file. +# See the exclusions section at the end of the file for examples. + +# ======= +# Purpose +# ======= +# +# An entry in this file does not imply 'ownership', despite the name of the +# file, but instead that those listed take an interest in that part of the +# project and will automatically be added as reviewers to PRs that affect +# the matching files. +# See also the Experts Index in the Python Developer's Guide: +# https://devguide.python.org/core-developers/experts/ +# +# ========= +# Structure +# ========= +# +# The CODEOWNERS file is organised by topic area. +# Please add new entries in alphabetical order within the relevant section. +# Where possible, keep related files together. For example, documentation, +# code, and tests for a given item should all be listed in the same place. +# +# GitHub usernames should be aligned to column 31, or the next multiple +# of three if the relevant paths are too long to fit. +# +# Top-level sections are: +# +# * Buildbots, Continuous Integration, and Testing +# project-wide configuration files, internal tools for use in CI, +# linting. +# * Build System +# the Makefile, autoconf, and other autotools files. +# * Documentation +# broader sections of documentation, documentation tools +# * Internal Tools & Data +# internal tools, integration with external systems, +# entries that don't fit elsewhere +# * Platform Support +# relating to support for specific platforms +# * Interpreter Core +# the grammar, parser, compiler, interpreter, etc. +# * Standard Library +# standard library modules (from both Lib and Modules) +# and related files (such as their tests and docs) +# * Exclusions +# exclusions from .github/CODEOWNERS should go at the very end +# because the final matching pattern will take precedence. + +# ---------------------------------------------------------------------------- +# Buildbots, Continuous Integration, and Testing +# ---------------------------------------------------------------------------- + +# Azure Pipelines +.azure-pipelines/ @AA-Turner + +# GitHub & related scripts +.github/ @ezio-melotti @hugovk @AA-Turner @webknjaz @itamaro +Tools/build/compute-changes.py @AA-Turner @hugovk @webknjaz +Lib/test/test_tools/test_compute_changes.py @AA-Turner @hugovk @webknjaz +Tools/build/verify_ensurepip_wheels.py @AA-Turner @pfmoore @pradyunsg + +# Pre-commit +.pre-commit-config.yaml @hugovk .ruff.toml @hugovk @AlexWaygood @AA-Turner -# Build system -configure* @erlend-aasland @corona10 -Makefile.pre.in @erlend-aasland -Modules/Setup* @erlend-aasland +# Patchcheck +Tools/patchcheck/ @AA-Turner @itamaro -# argparse -**/*argparse* @savannahostrowski -# asyncio -**/*asyncio* @1st1 @asvetlov @kumaraditya303 @willingc +# ---------------------------------------------------------------------------- +# Build System +# ---------------------------------------------------------------------------- -# Core -**/*context* @1st1 -**/*genobject* @markshannon -**/*hamt* @1st1 -**/*jit* @brandtbucher @savannahostrowski @diegorusso -Python/perf_jit_trampoline.c # Exclude the owners of "**/*jit*", above. -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 -Objects/call.c @markshannon -Objects/object.c @ZeroIntensity -Python/ceval*.c @markshannon -Python/ceval*.h @markshannon -Python/codegen.c @markshannon @iritkatriel -Python/compile.c @markshannon @iritkatriel -Python/assemble.c @markshannon @iritkatriel -Python/flowgraph.c @markshannon @iritkatriel -Python/instruction_sequence.c @iritkatriel -Python/bytecodes.c @markshannon -Python/optimizer*.c @markshannon -Python/optimizer_analysis.c @Fidget-Spinner @tomasr8 -Python/optimizer_bytecodes.c @Fidget-Spinner @tomasr8 -Python/optimizer_symbols.c @tomasr8 -Python/symtable.c @JelleZijlstra @carljm -Lib/_pyrepl/* @pablogsal @lysnikolaou @ambv -Lib/test/test_patma.py @brandtbucher -Lib/test/test_type_*.py @JelleZijlstra -Lib/test/test_capi/test_misc.py @markshannon -Lib/test/test_pyrepl/* @pablogsal @lysnikolaou @ambv -Tools/c-analyzer/ @ericsnowcurrently +# Autotools +configure* @erlend-aasland @corona10 @AA-Turner @emmatyping @itamaro +Makefile.pre.in @erlend-aasland @AA-Turner @emmatyping @itamaro +Modules/makesetup @erlend-aasland @AA-Turner @emmatyping @itamaro +Modules/Setup* @erlend-aasland @AA-Turner @emmatyping @itamaro +Tools/build/regen-configure.sh @AA-Turner @itamaro -# 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 @ZeroIntensity -**/*pystate* @ericsnowcurrently @ZeroIntensity -**/*preconfig* @ericsnowcurrently -**/*initconfig* @ericsnowcurrently -**/*pathconfig* @ericsnowcurrently -**/*sysmodule* @ericsnowcurrently -**/*bltinmodule* @ericsnowcurrently -**/*gil* @ericsnowcurrently -Include/internal/pycore_runtime.h @ericsnowcurrently -Include/internal/pycore_interp.h @ericsnowcurrently -Include/internal/pycore_tstate.h @ericsnowcurrently -Include/internal/pycore_*_state.h @ericsnowcurrently -Include/internal/pycore_*_init.h @ericsnowcurrently -Include/internal/pycore_atexit.h @ericsnowcurrently -Include/internal/pycore_freelist.h @ericsnowcurrently -Include/internal/pycore_global_objects.h @ericsnowcurrently -Include/internal/pycore_obmalloc.h @ericsnowcurrently -Include/internal/pycore_pymem.h @ericsnowcurrently -Include/internal/pycore_stackref.h @Fidget-Spinner -Modules/main.c @ericsnowcurrently -Programs/_bootstrap_python.c @ericsnowcurrently -Programs/python.c @ericsnowcurrently -Tools/build/generate_global_objects.py @ericsnowcurrently - -# Initialization -Doc/library/sys_path_init.rst @FFY00 -Doc/c-api/init_config.rst @FFY00 +# generate-build-details +Tools/build/generate-build-details.py @FFY00 +Lib/test/test_build_details.py @FFY00 -# getpath -**/*getpath* @FFY00 -# site -**/*site.py @FFY00 -Doc/library/site.rst @FFY00 +# ---------------------------------------------------------------------------- +# Documentation +# ---------------------------------------------------------------------------- -# Exceptions -Lib/test/test_except*.py @iritkatriel -Objects/exceptions.c @iritkatriel +# Internal Docs +InternalDocs/ @AA-Turner -# 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 +# Tools, Configuration, etc +Doc/Makefile @AA-Turner @hugovk @StanFromIreland +Doc/_static/ @AA-Turner @hugovk @StanFromIreland +Doc/conf.py @AA-Turner @hugovk @StanFromIreland +Doc/make.bat @AA-Turner @hugovk @StanFromIreland +Doc/requirements.txt @AA-Turner @hugovk @StanFromIreland +Doc/tools/ @AA-Turner @hugovk @StanFromIreland -# libssl -**/*ssl* @gpshead @picnixz +# PR Previews +.readthedocs.yml @AA-Turner -# logging -**/*logging* @vsajip +# Sections +Doc/c-api/ @ZeroIntensity +Doc/reference/ @willingc @AA-Turner +Doc/whatsnew/ @AA-Turner -# venv -**/*venv* @vsajip @FFY00 -# Launcher -/PC/launcher.c @vsajip +# ---------------------------------------------------------------------------- +# Internal Tools and Data +# ---------------------------------------------------------------------------- -# HTML -/Lib/html/ @ezio-melotti -/Lib/_markupbase.py @ezio-melotti -/Lib/test/test_html*.py @ezio-melotti -/Tools/build/parse_html5_entities.py @ezio-melotti - -# Import (including importlib). -**/*import* @brettcannon @ericsnowcurrently @ncoghlan @warsaw -/Python/import.c @kumaraditya303 -Python/dynload_*.c @ericsnowcurrently -**/*freeze* @ericsnowcurrently -**/*frozen* @ericsnowcurrently -**/*modsupport* @ericsnowcurrently -**/*modulefinder* @ericsnowcurrently -**/*moduleobject* @ericsnowcurrently -**/*multiphase* @ericsnowcurrently -**/*pkgutil* @ericsnowcurrently -**/*pythonrun* @ericsnowcurrently -**/*runpy* @ericsnowcurrently -**/*singlephase* @ericsnowcurrently -Lib/test/test_module/ @ericsnowcurrently -Doc/c-api/module.rst @ericsnowcurrently -**/*importlib/resources/* @jaraco @warsaw @FFY00 -**/*importlib/metadata/* @jaraco @warsaw +# Argument Clinic +Tools/clinic/ @erlend-aasland @AA-Turner +Lib/test/test_clinic.py @erlend-aasland @AA-Turner +Doc/howto/clinic.rst @erlend-aasland @AA-Turner -# Dates and times -**/*datetime* @pganssle @abalkin -**/*str*time* @pganssle @abalkin -Doc/library/time.rst @pganssle @abalkin -Lib/test/test_time.py @pganssle @abalkin -Modules/timemodule.c @pganssle @abalkin -Python/pytime.c @pganssle @abalkin -Include/internal/pycore_time.h @pganssle @abalkin +# C Analyser +Tools/c-analyzer/ @ericsnowcurrently -# Email and related -**/*mail* @python/email-team -**/*smtp* @python/email-team -**/*mime* @python/email-team -**/*imap* @python/email-team -**/*poplib* @python/email-team +# C API Documentation Checks +Tools/check-c-api-docs/ @ZeroIntensity -# Exclude .mailmap from being owned by @python/email-team -/.mailmap +# Fuzzing +Modules/_xxtestfuzz/ @python/fuzzers +Lib/test/test_xxtestfuzz.py @python/fuzzers +.github/workflows/reusable-cifuzz.yml @python/fuzzers -# Garbage collector -/Modules/gcmodule.c @pablogsal -/Doc/library/gc.rst @pablogsal +# Limited C API & Stable ABI +Doc/c-api/stable.rst @encukou +Doc/data/*.abi @encukou +Misc/stable_abi.toml @encukou +Tools/build/stable_abi.py @encukou -# Parser -/Parser/ @pablogsal @lysnikolaou -/Tools/peg_generator/ @pablogsal @lysnikolaou -/Lib/test/test_peg_generator/ @pablogsal @lysnikolaou -/Grammar/python.gram @pablogsal @lysnikolaou -/Lib/tokenize.py @pablogsal @lysnikolaou -/Lib/test/test_tokenize.py @pablogsal @lysnikolaou +# SBOM +Misc/externals.spdx.json @sethmlarson +Misc/sbom.spdx.json @sethmlarson +Tools/build/generate_sbom.py @sethmlarson -# Code generator -/Tools/cases_generator/ @markshannon +# ABI check +Misc/libabigail.abignore @encukou -# AST -Python/ast.c @isidentical @JelleZijlstra @eclips4 @tomasr8 -Python/ast_preprocess.c @isidentical @eclips4 @tomasr8 -Parser/asdl.py @isidentical @JelleZijlstra @eclips4 @tomasr8 -Parser/asdl_c.py @isidentical @JelleZijlstra @eclips4 @tomasr8 -Lib/ast.py @isidentical @JelleZijlstra @eclips4 @tomasr8 -Lib/_ast_unparse.py @isidentical @JelleZijlstra @eclips4 @tomasr8 -Lib/test/test_ast/ @eclips4 @tomasr8 -# Mock -/Lib/unittest/mock.py @cjw296 -/Lib/test/test_unittest/testmock/* @cjw296 +# ---------------------------------------------------------------------------- +# Platform Support +# ---------------------------------------------------------------------------- + +# Android +Platforms/Android/ @mhsmith @freakboy3742 +Doc/using/android.rst @mhsmith @freakboy3742 +Lib/_android_support.py @mhsmith @freakboy3742 +Lib/test/test_android.py @mhsmith @freakboy3742 -# multiprocessing -**/*multiprocessing* @gpshead +# iOS +Doc/using/ios.rst @freakboy3742 +Lib/_ios_support.py @freakboy3742 +Platforms/Apple/ @freakboy3742 -# SQLite 3 -**/*sqlite* @berkerpeksag @erlend-aasland +# macOS +Mac/ @python/macos-team +Lib/_osx_support.py @python/macos-team +Lib/test/test__osx_support.py @python/macos-team -# subprocess -/Lib/subprocess.py @gpshead -/Lib/test/test_subprocess.py @gpshead -/Modules/*subprocess* @gpshead +# WebAssembly +Tools/wasm/README.md @brettcannon @freakboy3742 @emmatyping -# debugger -**/*pdb* @gaogaotiantian -**/*bdb* @gaogaotiantian +# WebAssembly (Emscripten) +Platforms/emscripten @freakboy3742 @emmatyping +Tools/wasm/emscripten @freakboy3742 @emmatyping -# Limited C API & stable ABI -Tools/build/stable_abi.py @encukou -Misc/stable_abi.toml @encukou -Doc/data/*.abi @encukou -Doc/c-api/stable.rst @encukou +# WebAssembly (WASI) +Platforms/WASI @brettcannon @emmatyping @savannahostrowski +Tools/wasm/wasi-env @brettcannon @emmatyping @savannahostrowski +Tools/wasm/wasi.py @brettcannon @emmatyping @savannahostrowski +Tools/wasm/wasi @brettcannon @emmatyping @savannahostrowski # Windows -/PC/ @python/windows-team -/PCbuild/ @python/windows-team - -# Urllib -**/*robotparser* @berkerpeksag +PC/ @python/windows-team +PCbuild/ @python/windows-team # Windows installer packages -/Tools/msi/ @python/windows-team -/Tools/nuget/ @python/windows-team +Tools/msi/ @python/windows-team +Tools/nuget/ @python/windows-team -# Misc -**/*itertools* @rhettinger -**/*collections* @rhettinger -**/*random* @rhettinger -**/*bisect* @rhettinger -**/*heapq* @rhettinger -**/*functools* @rhettinger +# Windows Launcher +PC/launcher.c @python/windows-team @vsajip -**/*dataclasses* @ericvsmith -**/*ensurepip* @pfmoore @pradyunsg +# ---------------------------------------------------------------------------- +# Interpreter Core +# ---------------------------------------------------------------------------- -/Doc/library/idle.rst @terryjreedy -**/*idlelib* @terryjreedy -**/*turtledemo* @terryjreedy +# AST +Lib/_ast_unparse.py @isidentical @JelleZijlstra @eclips4 @tomasr8 +Lib/ast.py @isidentical @JelleZijlstra @eclips4 @tomasr8 +Lib/test/test_ast/ @eclips4 @tomasr8 +Parser/asdl.py @isidentical @JelleZijlstra @eclips4 @tomasr8 +Parser/asdl_c.py @isidentical @JelleZijlstra @eclips4 @tomasr8 +Python/ast.c @isidentical @JelleZijlstra @eclips4 @tomasr8 +Python/ast_preprocess.c @isidentical @eclips4 @tomasr8 -**/*annotationlib* @JelleZijlstra -**/*typing* @JelleZijlstra @AlexWaygood +# Built-in types +Objects/call.c @markshannon +Objects/codeobject.c @markshannon +Objects/dict* @methane @markshannon +Objects/frameobject.c @markshannon +**/*genobject* @markshannon +Objects/object.c @ZeroIntensity +Objects/set* @rhettinger +Objects/type* @markshannon +Objects/typevarobject.c @JelleZijlstra +Objects/unionobject.c @JelleZijlstra -**/*ftplib @giampaolo -**/*shutil @giampaolo +# Byte code interpreter ('the eval loop') +Python/bytecodes.c @markshannon +Python/ceval* @markshannon +Tools/cases_generator/ @markshannon -**/*enum* @ethanfurman -**/*cgi* @ethanfurman -**/*tarfile* @ethanfurman +# Compiler (AST to byte code) +Python/assemble.c @markshannon @iritkatriel +Python/codegen.c @markshannon @iritkatriel +Python/compile.c @markshannon @iritkatriel +Python/flowgraph.c @markshannon @iritkatriel +Python/instruction_sequence.c @iritkatriel +Python/symtable.c @JelleZijlstra @carljm -**/*tomllib* @encukou @hauntsaninja +# Context variables & HAMT +**/contextvars* @1st1 +**/*hamt* @1st1 +Include/cpython/context.h @1st1 +Include/internal/pycore_context.h @1st1 +Lib/test/test_context.py @1st1 +Python/context.c @1st1 -**/*sysconfig* @FFY00 +# Core Modules +**/*bltinmodule* @ericsnowcurrently +**/*sysmodule* @ericsnowcurrently -**/*cjkcodecs* @corona10 +# Exceptions +Lib/test/test_except*.py @iritkatriel +Objects/exceptions.c @iritkatriel -# macOS -/Mac/ @python/macos-team -**/*osx_support* @python/macos-team +# Getpath +Lib/test/test_getpath.py @FFY00 +Modules/getpath* @FFY00 -# pathlib -**/*pathlib* @barneygale +# Hashing / ``hash()`` and related +Include/cpython/pyhash.h @gpshead @picnixz +Include/internal/pycore_pyhash.h @gpshead @picnixz +Include/pyhash.h @gpshead @picnixz +Python/pyhash.c @gpshead @picnixz -# zipfile.Path -**/*zipfile/_path/* @jaraco +# The import system (including importlib) +**/*import* @brettcannon @ericsnowcurrently @ncoghlan @warsaw @FFY00 +Python/import.c @brettcannon @ericsnowcurrently @ncoghlan @warsaw @FFY00 @kumaraditya303 +**/*freeze* @ericsnowcurrently +**/*frozen* @ericsnowcurrently +**/*modsupport* @ericsnowcurrently +**/*modulefinder* @ericsnowcurrently @FFY00 +**/*moduleobject* @ericsnowcurrently +**/*multiphase* @ericsnowcurrently +**/*pkgutil* @ericsnowcurrently @FFY00 +**/*pythonrun* @ericsnowcurrently +**/*runpy* @ericsnowcurrently @FFY00 +**/*singlephase* @ericsnowcurrently +Doc/c-api/module.rst @ericsnowcurrently +Lib/test/test_module/ @ericsnowcurrently +Python/dynload_*.c @ericsnowcurrently @FFY00 -# Argument Clinic -/Tools/clinic/** @erlend-aasland -/Lib/test/test_clinic.py @erlend-aasland -Doc/howto/clinic.rst @erlend-aasland - -# Subinterpreters -**/*interpreteridobject.* @ericsnowcurrently -**/*crossinterp* @ericsnowcurrently -Modules/_interp*module.c @ericsnowcurrently -Lib/test/test__interp*.py @ericsnowcurrently -Lib/concurrent/interpreters/ @ericsnowcurrently -Lib/test/support/channels.py @ericsnowcurrently -Doc/library/concurrent.interpreters.rst @ericsnowcurrently -Lib/test/test_interpreters/ @ericsnowcurrently -Lib/concurrent/futures/interpreter.py @ericsnowcurrently +# Initialisation +**/*initconfig* @ericsnowcurrently @FFY00 +**/*pathconfig* @ericsnowcurrently @FFY00 +**/*preconfig* @ericsnowcurrently @FFY00 +Doc/library/sys_path_init.rst @FFY00 +Doc/c-api/init_config.rst @FFY00 -# Android -**/*Android* @mhsmith @freakboy3742 -**/*android* @mhsmith @freakboy3742 +# Interpreter main program +Modules/main.c @ericsnowcurrently @FFY00 +Programs/_bootstrap_python.c @ericsnowcurrently @FFY00 +Programs/python.c @ericsnowcurrently @FFY00 + +# JIT +.github/workflows/jit.yml @savannahostrowski +Include/internal/pycore_jit.h @brandtbucher @savannahostrowski @diegorusso +Python/jit.c @brandtbucher @savannahostrowski @diegorusso +Tools/jit/ @brandtbucher @savannahostrowski @diegorusso +InternalDocs/jit.md @brandtbucher @savannahostrowski @diegorusso @AA-Turner + +# Lazy imports (PEP 810) +Objects/lazyimportobject.c @yhg1s @DinoV @pablogsal +Include/internal/pycore_lazyimportobject.h @yhg1s @DinoV @pablogsal +Lib/test/test_lazy_import @yhg1s @DinoV @pablogsal + +# Micro-op / μop / Tier 2 Optimiser +Python/optimizer.c @markshannon @Fidget-Spinner +Python/optimizer_analysis.c @markshannon @tomasr8 @Fidget-Spinner @savannahostrowski +Python/optimizer_bytecodes.c @markshannon @tomasr8 @Fidget-Spinner @savannahostrowski +Python/optimizer_symbols.c @markshannon @tomasr8 @Fidget-Spinner @savannahostrowski + +# Parser, Lexer, and Grammar +Grammar/python.gram @pablogsal @lysnikolaou +Lib/test/test_peg_generator/ @pablogsal @lysnikolaou +Lib/test/test_tokenize.py @pablogsal @lysnikolaou +Lib/tokenize.py @pablogsal @lysnikolaou +Parser/ @pablogsal @lysnikolaou +Tools/peg_generator/ @pablogsal @lysnikolaou + +# Runtime state/lifecycle +**/*gil* @ericsnowcurrently +**/*pylifecycle* @ericsnowcurrently @ZeroIntensity @FFY00 +**/*pystate* @ericsnowcurrently @ZeroIntensity @FFY00 +Include/internal/pycore_*_init.h @ericsnowcurrently +Include/internal/pycore_*_state.h @ericsnowcurrently +Include/internal/pycore_atexit.h @ericsnowcurrently +Include/internal/pycore_freelist.h @ericsnowcurrently +Include/internal/pycore_global_objects.h @ericsnowcurrently +Include/internal/pycore_interp.h @ericsnowcurrently +Include/internal/pycore_obmalloc.h @ericsnowcurrently +Include/internal/pycore_pymem.h @ericsnowcurrently +Include/internal/pycore_runtime.h @ericsnowcurrently +Include/internal/pycore_stackref.h @Fidget-Spinner +Include/internal/pycore_tstate.h @ericsnowcurrently +Tools/build/generate_global_objects.py @ericsnowcurrently + +# Remote Debugging +Python/remote_debug.h @pablogsal +Python/remote_debugging.c @pablogsal +Modules/_remote_debugging/ @pablogsal + +# Sub-Interpreters +**/*crossinterp* @ericsnowcurrently +**/*interpreteridobject.* @ericsnowcurrently +Doc/library/concurrent.interpreters.rst @ericsnowcurrently +Lib/concurrent/futures/interpreter.py @ericsnowcurrently +Lib/concurrent/interpreters/ @ericsnowcurrently +Lib/test/support/channels.py @ericsnowcurrently +Lib/test/test__interp*.py @ericsnowcurrently +Lib/test/test_interpreters/ @ericsnowcurrently +Modules/_interp*module.c @ericsnowcurrently + +# Template string literals (t-strings) +Lib/test/test_tstring.py @lysnikolaou +Objects/interpolationobject.c @lysnikolaou +Objects/templateobject.c @lysnikolaou + +# Tests +Lib/test/test_patma.py @brandtbucher +Lib/test/test_type_*.py @JelleZijlstra +Lib/test/test_capi/test_misc.py @markshannon -# iOS (but not termios) -**/iOS* @freakboy3742 -**/ios* @freakboy3742 -**/*_iOS* @freakboy3742 -**/*_ios* @freakboy3742 -**/*-iOS* @freakboy3742 -**/*-ios* @freakboy3742 -# WebAssembly -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 +# ---------------------------------------------------------------------------- +# Standard Library +# ---------------------------------------------------------------------------- + +# Annotationlib +Doc/library/annotationlib.rst @JelleZijlstra +Lib/annotationlib.py @JelleZijlstra +Lib/test/test_annotationlib.py @JelleZijlstra + +# Argparse +Doc/**/argparse*.rst @savannahostrowski +Lib/argparse.py @savannahostrowski +Lib/test/test_argparse.py @savannahostrowski + +# Asyncio +Doc/library/asyncio*.rst @1st1 @asvetlov @kumaraditya303 @willingc +InternalDocs/asyncio.md @1st1 @asvetlov @kumaraditya303 @willingc @AA-Turner +Lib/asyncio/ @1st1 @asvetlov @kumaraditya303 @willingc +Lib/test/test_asyncio/ @1st1 @asvetlov @kumaraditya303 @willingc +Modules/_asynciomodule.c @1st1 @asvetlov @kumaraditya303 @willingc + +# Bisect +Doc/library/bisect.rst @rhettinger +Lib/bisect.py @rhettinger +Lib/test/test_bisect.py @rhettinger +Modules/_bisectmodule.c @rhettinger + +# Calendar +Lib/calendar.py @AA-Turner +Lib/test/test_calendar.py @AA-Turner + +# Cryptographic Primitives and Applications +**/*hashlib* @gpshead @picnixz +**/*hashopenssl* @gpshead @picnixz +**/*hmac* @gpshead @picnixz +**/*ssl* @gpshead @picnixz +Modules/_hacl/ @gpshead @picnixz +Modules/*blake* @gpshead @picnixz +Modules/*md5* @gpshead @picnixz +Modules/*sha* @gpshead @picnixz + +# Codecs +Modules/cjkcodecs/ @corona10 +Tools/unicode/gencjkcodecs.py @corona10 + +# Collections +Doc/library/collections.abc.rst @rhettinger +Doc/library/collections.rst @rhettinger +Lib/_collections_abc.py @rhettinger +Lib/collections/ @rhettinger +Lib/test/test_collections.py @rhettinger +Modules/_collectionsmodule.c @rhettinger -# SBOM -/Misc/externals.spdx.json @sethmlarson -/Misc/sbom.spdx.json @sethmlarson -/Tools/build/generate_sbom.py @sethmlarson +# Colorize +Lib/_colorize.py @hugovk +Lib/test/test__colorize.py @hugovk # Config Parser Lib/configparser.py @jaraco Lib/test/test_configparser.py @jaraco -# Doc sections -Doc/reference/ @willingc @AA-Turner +# Dataclasses +Doc/library/dataclasses.rst @ericvsmith +Lib/dataclasses.py @ericvsmith +Lib/test/test_dataclasses/ @ericvsmith +# Dates and times +Doc/**/*time.rst @pganssle @StanFromIreland +Doc/library/datetime-* @pganssle @StanFromIreland +Doc/library/zoneinfo.rst @pganssle @StanFromIreland +Include/datetime.h @pganssle @StanFromIreland +Include/internal/pycore_time.h @pganssle @StanFromIreland +Lib/test/test_zoneinfo/ @pganssle @StanFromIreland +Lib/zoneinfo/ @pganssle @StanFromIreland +Lib/*time.py @pganssle @StanFromIreland +Lib/test/datetimetester.py @pganssle @StanFromIreland +Lib/test/test_*time.py @pganssle @StanFromIreland +Modules/*zoneinfo* @pganssle @StanFromIreland +Modules/*time* @pganssle @StanFromIreland +Python/pytime.c @pganssle @StanFromIreland + +# Dbm +Doc/library/dbm.rst @corona10 @erlend-aasland @serhiy-storchaka +Lib/dbm/ @corona10 @erlend-aasland @serhiy-storchaka +Lib/test/test_dbm*.py @corona10 @erlend-aasland @serhiy-storchaka +Modules/*dbm* @corona10 @erlend-aasland @serhiy-storchaka + +# Email and related +**/*mail* @python/email-team +**/*smtp* @python/email-team +**/*mime* @python/email-team +**/*imap* @python/email-team +**/*poplib* @python/email-team + +# Ensurepip +Doc/library/ensurepip.rst @pfmoore @pradyunsg +Lib/ensurepip/ @pfmoore @pradyunsg +Lib/test/test_ensurepip.py @pfmoore @pradyunsg + +# Enum +Doc/howto/enum.rst @ethanfurman +Doc/library/enum.rst @ethanfurman +Lib/enum.py @ethanfurman +Lib/test/test_enum.py @ethanfurman +Lib/test/test_json/test_enum.py @ethanfurman + +# FTP +Doc/library/ftplib.rst @giampaolo +Lib/ftplib.py @giampaolo +Lib/test/test_ftplib.py @giampaolo + +# Functools +Doc/library/functools.rst @rhettinger +Lib/functools.py @rhettinger +Lib/test/test_functools.py @rhettinger +Modules/_functoolsmodule.c @rhettinger + +# Garbage collector +Modules/gcmodule.c @pablogsal +Doc/library/gc.rst @pablogsal +InternalDocs/garbage_collector.md @pablogsal + +# Gettext +Doc/library/gettext.rst @tomasr8 +Lib/gettext.py @tomasr8 +Lib/test/test_gettext.py @tomasr8 +Tools/i18n/pygettext.py @tomasr8 + +# Heapq +Doc/library/heapq* @rhettinger +Lib/heapq.py @rhettinger +Lib/test/test_heapq.py @rhettinger +Modules/_heapqmodule.c @rhettinger + +# HTML +Doc/library/html* @ezio-melotti +Lib/html/ @ezio-melotti +Lib/_markupbase.py @ezio-melotti +Lib/test/test_html*.py @ezio-melotti +Tools/build/parse_html5_entities.py @ezio-melotti + +# IDLE +Doc/library/idle.rst @terryjreedy +Lib/idlelib/ @terryjreedy +Lib/turtledemo/ @terryjreedy + +# importlib.metadata +Doc/library/importlib.metadata.rst @jaraco @warsaw @FFY00 +Lib/importlib/metadata/ @jaraco @warsaw @FFY00 +Lib/test/test_importlib/metadata/ @jaraco @warsaw @FFY00 + +# importlib.resources +Doc/library/importlib.resources.abc.rst @jaraco @warsaw @FFY00 +Doc/library/importlib.resources.rst @jaraco @warsaw @FFY00 +Lib/importlib/resources/ @jaraco @warsaw @FFY00 +Lib/test/test_importlib/resources/ @jaraco @warsaw @FFY00 + +# Itertools +Doc/library/itertools.rst @rhettinger +Lib/test/test_itertools.py @rhettinger +Modules/itertoolsmodule.c @rhettinger + +# Logging +Doc/**/logging* @vsajip +Lib/logging/ @vsajip +Lib/test/test_logging.py @vsajip + +# Multiprocessing +Doc/library/multiprocessing*.rst @gpshead +Lib/multiprocessing/ @gpshead +Lib/test/*multiprocessing.py @gpshead +Lib/test/test_multiprocessing*/ @gpshead +Modules/_multiprocessing/ @gpshead + +# Pathlib +Doc/library/pathlib.rst @barneygale +Lib/pathlib/ @barneygale +Lib/test/test_pathlib/ @barneygale + +# Pdb & Bdb +Doc/library/bdb.rst @gaogaotiantian +Doc/library/pdb.rst @gaogaotiantian +Lib/bdb.py @gaogaotiantian +Lib/pdb.py @gaogaotiantian +Lib/test/test_bdb.py @gaogaotiantian +Lib/test/test_pdb.py @gaogaotiantian +Lib/test/test_remote_pdb.py @gaogaotiantian + +# Pydoc +Lib/pydoc.py @AA-Turner +Lib/pydoc_data/ @AA-Turner +Lib/test/test_pydoc/ @AA-Turner + +# Profiling (Sampling) +Doc/library/profiling*.rst @pablogsal +Lib/profiling/ @pablogsal +Lib/test/test_profiling/ @pablogsal + +# PyREPL +Lib/_pyrepl/ @pablogsal @lysnikolaou @ambv +Lib/test/test_pyrepl/ @pablogsal @lysnikolaou @ambv + +# Random +Doc/library/random.rst @rhettinger +Lib/random.py @rhettinger +Lib/test/test_random.py @rhettinger +Modules/_randommodule.c @rhettinger + +# Shutil +Doc/library/shutil.rst @giampaolo +Lib/shutil.py @giampaolo +Lib/test/test_shutil.py @giampaolo + +# Site +Lib/site.py @FFY00 @warsaw +Lib/test/test_site.py @FFY00 @warsaw +Doc/library/site.rst @FFY00 @warsaw + +# string.templatelib +Doc/library/string.templatelib.rst @lysnikolaou @AA-Turner +Lib/string/templatelib.py @lysnikolaou @AA-Turner +Lib/test/test_string/test_templatelib.py @lysnikolaou @AA-Turner + +# Sysconfig +**/*sysconfig* @FFY00 + +# SQLite 3 +Doc/library/sqlite3.rst @erlend-aasland +Lib/sqlite3/ @erlend-aasland +Lib/test/test_sqlite3/ @erlend-aasland +Modules/_sqlite/ @erlend-aasland + +# Subprocess +Lib/subprocess.py @gpshead +Lib/test/test_subprocess.py @gpshead +Modules/*subprocess* @gpshead + +# Tarfile +Doc/library/tarfile.rst @ethanfurman +Lib/tarfile.py @ethanfurman +Lib/test/test_tarfile.py @ethanfurman + +# TOML +Doc/library/tomllib.rst @encukou @hauntsaninja +Lib/test/test_tomllib/ @encukou @hauntsaninja +Lib/tomllib/ @encukou @hauntsaninja + +# Typing +Doc/library/typing.rst @JelleZijlstra @AlexWaygood +Lib/test/test_typing.py @JelleZijlstra @AlexWaygood +Lib/test/typinganndata/ @JelleZijlstra @AlexWaygood +Lib/typing.py @JelleZijlstra @AlexWaygood +Modules/_typingmodule.c @JelleZijlstra @AlexWaygood + +# Types +Lib/test/test_types.py @AA-Turner +Lib/types.py @AA-Turner +Modules/_typesmodule.c @AA-Turner + +# Unittest +Lib/unittest/mock.py @cjw296 +Lib/test/test_unittest/testmock/ @cjw296 + +# Venv +**/*venv* @vsajip @FFY00 + +# Weakref **/*weakref* @kumaraditya303 -# Colorize -Lib/_colorize.py @hugovk -Lib/test/test__colorize.py @hugovk +# Zipfile.Path +Lib/test/test_zipfile/_path/ @jaraco +Lib/zipfile/_path/ @jaraco -# Fuzzing -Modules/_xxtestfuzz/ @ammaraskar +# Zstandard +Lib/compression/zstd/ @AA-Turner @emmatyping +Lib/test/test_zstd.py @AA-Turner @emmatyping +Modules/_zstd/ @AA-Turner @emmatyping -# t-strings -**/*interpolationobject* @lysnikolaou -**/*templateobject* @lysnikolaou -**/*templatelib* @lysnikolaou -**/*tstring* @lysnikolaou +# ---------------------------------------------------------------------------- -# Remote debugging -Python/remote_debug.h @pablogsal -Python/remote_debugging.c @pablogsal -Modules/_remote_debugging_module.c @pablogsal @ambv @1st1 +# Exclusions from .github/CODEOWNERS should go at the very end +# because the final matching pattern will take precedence. + +# Exclude .mailmap from being owned by @python/email-team +.mailmap -# gettext -**/*gettext* @tomasr8 +# Exclude Argument Clinic directories +Modules/**/clinic/ +Objects/**/clinic/ +PC/**/clinic/ +Python/**/clinic/ diff --git a/.github/CONTRIBUTING.rst b/.github/CONTRIBUTING.rst index 2ef9cdc191481e..94b67ce3dbe341 100644 --- a/.github/CONTRIBUTING.rst +++ b/.github/CONTRIBUTING.rst @@ -4,7 +4,7 @@ Contributing to Python Build Status ------------ -- `Buildbot status overview `_ +- `Buildbot status overview `_ - `GitHub Actions status `_ @@ -28,23 +28,23 @@ Please be aware that our workflow does deviate slightly from the typical GitHub project. Details on how to properly submit a pull request are covered in `Lifecycle of a Pull Request `_. We utilize various bots and status checks to help with this, so do follow the -comments they leave and their "Details" links, respectively. The key points of -our workflow that are not covered by a bot or status check are: +comments they leave and their "Details" links, respectively. -- All discussions that are not directly related to the code in the pull request - should happen on `GitHub Issues `_. -- Upon your first non-trivial pull request (which includes documentation changes), - feel free to add yourself to ``Misc/ACKS`` +The final key part of our workflow is that all discussions that are not +directly related to the code in the pull request should happen on +`GitHub Issues `__, generally in the +pull request's parent issue. Setting Expectations -------------------- -Due to the fact that this project is entirely volunteer-run (i.e. no one is paid -to work on Python full-time), we unfortunately can make no guarantees as to if +Due to the fact that this project is run by volunteers, +unfortunately we cannot make any guarantees as to if or when a core developer will get around to reviewing your pull request. If no core developer has done a review or responded to changes made because of a -"changes requested" review, please feel free to email python-dev to ask if -someone could take a look at your pull request. +"changes requested" review within a month, you can ask for someone to +review your pull request via a post in the `Core Development Discourse +category `__. Code of Conduct diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index da70710b7ecfa3..177615621f6b8c 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -34,13 +34,13 @@ body: label: "CPython versions tested on:" multiple: true options: - - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" - "3.14" - "3.15" + - "3.16" - "CPython main branch" validations: required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 75d174307ce160..de6e8756b03d80 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -5,3 +5,6 @@ contact_links: - name: "Proposing new features" about: "Submit major feature proposal (e.g. syntax changes) to an ideas forum first." url: "https://discuss.python.org/c/ideas/6" + - name: "Python Install Manager issues" + about: "Report issues with the Python Install Manager (for Windows)" + url: "https://github.com/python/pymanager/issues" diff --git a/.github/ISSUE_TEMPLATE/crash.yml b/.github/ISSUE_TEMPLATE/crash.yml index 470ad581367b10..81ae91e5b0af97 100644 --- a/.github/ISSUE_TEMPLATE/crash.yml +++ b/.github/ISSUE_TEMPLATE/crash.yml @@ -27,13 +27,13 @@ body: label: "CPython versions tested on:" multiple: true options: - - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" - "3.14" - "3.15" + - "3.16" - "CPython main branch" validations: required: true diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md deleted file mode 100644 index 174fd39171d47d..00000000000000 --- a/.github/ISSUE_TEMPLATE/documentation.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: Documentation -about: Report a problem with the documentation -labels: "docs" ---- - -# Documentation - -(A clear and concise description of the issue.) diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 00000000000000..d720cf9c4de91e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,16 @@ +name: Documentation +description: Report a problem with the documentation +labels: ["docs"] +body: + - type: markdown + attributes: + value: | + > [!NOTE] + > Trivial changes (for example typos) don’t require an issue before opening a PR. + - type: textarea + id: description + attributes: + label: "Documentation" + description: "A clear and concise description of the issue. Include a link to the page." + validations: + required: true diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 923720bce0bc3b..5bf4b40947bea7 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1,17 +1,24 @@ # Security Policy -## Supported Versions +Python [provides a security policy and threat model](https://devguide.python.org/security/policy/) +in the Python Developer's Guide documenting what bugs are vulnerabilities, +how to structure reports, and what versions of Python accept reports. -The Python team applies security fixes according to the table -in [the devguide]( -https://devguide.python.org/versions/#supported-versions -). +Python Security Response Team (PSRT) members +balance security work against many other responsibilities. Please be thoughtful +about the time and attention your report requires. Repeated failure to respect +the security policy will result in future reports being rejected, or the +reporter being banned from the ``python`` GitHub organization, regardless of +technical merit. ## Reporting a Vulnerability -Please read the guidelines on reporting security issues [on the -official website](https://www.python.org/dev/security/) for -instructions on how to report a security-related problem to -the Python team responsibly. +The [Python security policy](https://devguide.python.org/security/policy/) +documents [how to submit a vulnerability report](https://devguide.python.org/security/policy/#how-to-submit-a-vulnerability-report) +using GitHub Security Advisories. Please read the security policy +prior to filing a vulnerability report, especially the section on [what information to +include and exclude](https://devguide.python.org/security/policy/#what-to-include-and-how-to-structure-a-vulnerability-report) +in vulnerability reports. Following the security policy means the PSRT can +quickly and efficiently triage your report, not following the security policy +will only delay triaging your report. -To reach the response team, email `security at python dot org`. diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index 68aae196357414..eacfff24889021 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -1,7 +1,3 @@ -self-hosted-runner: - # Pending https://github.com/rhysd/actionlint/issues/533 - labels: ["windows-11-arm"] - config-variables: null paths: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c8a3165d690364..4b77646e22db4b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,7 +3,7 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "monthly" + interval: "quarterly" labels: - "skip issue" - "skip news" @@ -12,10 +12,21 @@ updates: update-types: - "version-update:semver-minor" - "version-update:semver-patch" + groups: + actions: + patterns: + - "*" + cooldown: + # https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns + # Cooldowns protect against supply chain attacks by avoiding the + # highest-risk window immediately after new releases. + default-days: 14 - package-ecosystem: "pip" directory: "/Tools/" schedule: - interval: "monthly" + interval: "quarterly" labels: - "skip issue" - "skip news" + cooldown: + default-days: 14 diff --git a/.github/workflows/add-issue-header.yml b/.github/workflows/add-issue-header.yml index 3cbc23af578d10..4c25976b9c24f7 100644 --- a/.github/workflows/add-issue-header.yml +++ b/.github/workflows/add-issue-header.yml @@ -12,6 +12,8 @@ on: # Only ever run once - opened +permissions: + contents: read jobs: add-header: @@ -20,7 +22,7 @@ jobs: issues: write timeout-minutes: 5 steps: - - uses: actions/github-script@v7 + - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: # language=JavaScript script: | diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f07f5e8040acf0..43f8e0c010ed1b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,7 +64,7 @@ jobs: run: | apt update && apt install git -yq git config --global --add safe.directory "$GITHUB_WORKSPACE" - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 1 persist-credentials: false @@ -101,28 +101,16 @@ jobs: needs: build-context if: needs.build-context.outputs.run-tests == 'true' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.x' - 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 - # Include env.pythonLocation in key to avoid changes in environment when setup-python updates Python - 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" - - name: Configure ccache action - uses: hendrikmuhs/ccache-action@v1.2 - with: - save: false - name: Configure CPython run: | # Build Python with the libpython dynamic library @@ -130,7 +118,7 @@ jobs: - name: Build CPython run: | make -j4 regen-all - make regen-stdlib-module-names regen-sbom regen-unicodedata + make regen-stdlib-module-names regen-sbom - name: Check for changes run: | git add -u @@ -153,6 +141,14 @@ jobs: if: github.event_name == 'pull_request' # $GITHUB_EVENT_NAME run: make check-c-globals + check-c-api-docs: + name: C API Docs + needs: build-context + if: >- + needs.build-context.outputs.run-tests == 'true' + || needs.build-context.outputs.run-docs == 'true' + uses: ./.github/workflows/reusable-check-c-api-docs.yml + build-windows: name: >- Windows @@ -169,17 +165,25 @@ jobs: free-threading: - false - true + interpreter: + - switch-case exclude: # Skip Win32 on free-threaded builds - { arch: Win32, free-threading: true } + include: + # msvc::musttail is currently only supported on x64, + # and only supported on 3.15+. + - { arch: x64, free-threading: false, interpreter: tail-call } + - { arch: x64, free-threading: true, interpreter: tail-call } uses: ./.github/workflows/reusable-windows.yml with: arch: ${{ matrix.arch }} free-threading: ${{ matrix.free-threading }} + interpreter: ${{ matrix.interpreter }} build-windows-msi: - name: >- # ${{ '' } is a hack to nest jobs under the same sidebar category - Windows MSI${{ '' }} + # ${{ '' } is a hack to nest jobs under the same sidebar category. + name: Windows MSI${{ '' }} # zizmor: ignore[obfuscation] needs: build-context if: fromJSON(needs.build-context.outputs.run-windows-msi) strategy: @@ -198,32 +202,23 @@ jobs: macOS ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }} needs: build-context - if: needs.build-context.outputs.run-tests == 'true' + if: needs.build-context.outputs.run-macos == 'true' strategy: fail-fast: false matrix: - # Cirrus and macos-14 are M1, macos-13 is default GHA Intel. - # macOS 13 only runs tests against the GIL-enabled CPython. - # Cirrus used for upstream, macos-14 for forks. + # macos-26 is Apple Silicon, macos-26-intel is Intel. + # macos-26-intel only runs tests against the GIL-enabled CPython. os: - - ghcr.io/cirruslabs/macos-runner:sonoma - - macos-14 - - macos-13 - is-fork: # only used for the exclusion trick - - ${{ github.repository_owner != 'python' }} + - macos-26 + - macos-26-intel free-threading: - false - true exclude: - - os: ghcr.io/cirruslabs/macos-runner:sonoma - is-fork: true - - os: macos-14 - is-fork: false - - os: macos-13 + - os: macos-26-intel free-threading: true uses: ./.github/workflows/reusable-macos.yml with: - config_hash: ${{ needs.build-context.outputs.config-hash }} free-threading: ${{ matrix.free-threading }} os: ${{ matrix.os }} @@ -233,7 +228,7 @@ jobs: ${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }} ${{ fromJSON(matrix.bolt) && '(bolt)' || '' }} needs: build-context - if: needs.build-context.outputs.run-tests == 'true' + if: needs.build-context.outputs.run-ubuntu == 'true' strategy: fail-fast: false matrix: @@ -253,169 +248,160 @@ jobs: # BOLT currently crashes during instrumentation on aarch64 - os: ubuntu-24.04-arm bolt: true + include: + # Enable CPU-intensive tests on ARM (default build only) + - os: ubuntu-24.04-arm + bolt: false + free-threading: false + test-opts: '-u cpu' uses: ./.github/workflows/reusable-ubuntu.yml with: - config_hash: ${{ needs.build-context.outputs.config-hash }} bolt-optimizations: ${{ matrix.bolt }} free-threading: ${{ matrix.free-threading }} os: ${{ matrix.os }} + test-opts: ${{ matrix.test-opts || '' }} - build-ubuntu-ssltests-openssl: - name: 'Ubuntu SSL tests with OpenSSL' - runs-on: ${{ matrix.os }} - timeout-minutes: 60 - needs: build-context - if: needs.build-context.outputs.run-tests == 'true' - strategy: - fail-fast: false - matrix: - os: [ubuntu-24.04] - 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 - OPENSSL_DIR: ${{ github.workspace }}/multissl/openssl/${{ matrix.openssl_ver }} - 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_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: Install dependencies - run: sudo ./.github/workflows/posix-deps-apt.sh - - name: Configure OpenSSL env vars - run: | - echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV" - echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}" >> "$GITHUB_ENV" - echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> "$GITHUB_ENV" - - name: 'Restore OpenSSL build' - id: cache-openssl - uses: actions/cache@v4 - with: - path: ./multissl/openssl/${{ env.OPENSSL_VER }} - key: ${{ matrix.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 - - name: Add ccache to PATH - run: | - echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV" - - name: Configure ccache action - uses: hendrikmuhs/ccache-action@v1.2 - with: - save: false - - name: Configure CPython - run: ./configure CFLAGS="-fdiagnostics-format=json" --config-cache --enable-slower-safety --with-pydebug --with-openssl="$OPENSSL_DIR" - - name: Build CPython - run: make -j4 - - name: Display build info - run: make pythoninfo - - name: SSL tests - run: ./python Lib/test/ssltests.py - - build-ubuntu-ssltests-awslc: - name: 'Ubuntu SSL tests with AWS-LC' + build-ubuntu-ssltests: + name: 'Ubuntu SSL tests' runs-on: ${{ matrix.os }} timeout-minutes: 60 needs: build-context - if: needs.build-context.outputs.run-tests == 'true' + if: needs.build-context.outputs.run-ubuntu == 'true' strategy: fail-fast: false matrix: os: [ubuntu-24.04] - awslc_ver: [1.55.0] + ssllib: + # See Tools/ssl/make_ssl_data.py for notes on adding a new version + ## OpenSSL + # Keep 1.1.1w in our list despite it being upstream EOL and otherwise + # unsupported as it most resembles other 1.1.1-work-a-like ssl APIs + # supported by important vendors such as AWS-LC. + - { name: openssl, version: 1.1.1w } + - { name: openssl, version: 3.0.21 } + - { name: openssl, version: 3.4.6 } + - { name: openssl, version: 3.5.7 } + - { name: openssl, version: 3.6.3 } + - { name: openssl, version: 4.0.1 } + ## AWS-LC + - { name: aws-lc, version: 5.0.0 } env: - AWSLC_VER: ${{ matrix.awslc_ver}} + SSLLIB_VER: ${{ matrix.ssllib.version }} MULTISSL_DIR: ${{ github.workspace }}/multissl - OPENSSL_DIR: ${{ github.workspace }}/multissl/aws-lc/${{ matrix.awslc_ver }} - LD_LIBRARY_PATH: ${{ github.workspace }}/multissl/aws-lc/${{ matrix.awslc_ver }}/lib + SSLLIB_DIR: ${{ github.workspace }}/multissl/${{ matrix.ssllib.name }}/${{ matrix.ssllib.version }} + LD_LIBRARY_PATH: ${{ github.workspace }}/multissl/${{ matrix.ssllib.name }}/${{ matrix.ssllib.version }}/lib steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 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: Install dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - - name: Configure SSL lib env vars - run: | - echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV" - echo "OPENSSL_DIR=${GITHUB_WORKSPACE}/multissl/aws-lc/${AWSLC_VER}" >> "$GITHUB_ENV" - echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/aws-lc/${AWSLC_VER}/lib" >> "$GITHUB_ENV" - - name: 'Restore AWS-LC build' - id: cache-aws-lc - uses: actions/cache@v4 + - name: 'Restore SSL library build' + id: cache-ssl-lib + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: - path: ./multissl/aws-lc/${{ matrix.awslc_ver }} - key: ${{ matrix.os }}-multissl-aws-lc-${{ matrix.awslc_ver }} - - name: Install AWS-LC - if: steps.cache-aws-lc.outputs.cache-hit != 'true' + path: ./multissl/${{ matrix.ssllib.name }}/${{ matrix.ssllib.version }} + key: ${{ matrix.os }}-multissl-${{ matrix.ssllib.name }}-${{ matrix.ssllib.version }} + - name: Install SSL Library + if: steps.cache-ssl-lib.outputs.cache-hit != 'true' run: | python3 Tools/ssl/multissltests.py \ --steps=library \ --base-directory "$MULTISSL_DIR" \ - --awslc ${{ matrix.awslc_ver }} \ + '--${{ matrix.ssllib.name }}' '${{ matrix.ssllib.version }}' \ --system Linux - - name: Add ccache to PATH - run: | - echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV" - - name: Configure ccache action - uses: hendrikmuhs/ccache-action@v1.2 - with: - save: false - name: Configure CPython run: | ./configure CFLAGS="-fdiagnostics-format=json" \ --config-cache \ --enable-slower-safety \ --with-pydebug \ - --with-openssl="$OPENSSL_DIR" \ + --with-openssl="$SSLLIB_DIR" \ --with-builtin-hashlib-hashes=blake2 \ --with-ssl-default-suites=openssl - name: Build CPython - run: make -j + run: make -j4 - name: Display build info run: make pythoninfo - - name: Verify python is linked to AWS-LC - run: ./python -c 'import ssl; print(ssl.OPENSSL_VERSION)' | grep AWS-LC + - name: Verify python is linked to the right lib + run: | + ./python -c 'import ssl; print(ssl.OPENSSL_VERSION)' \ + | grep -iE '${{ matrix.ssllib.name }}.*${{ matrix.ssllib.version }}' - name: SSL tests run: ./python Lib/test/ssltests.py + build-android: + name: Android (${{ matrix.arch }}) + needs: build-context + if: needs.build-context.outputs.run-android == 'true' + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - arch: aarch64 + runs-on: macos-26 + - arch: x86_64 + runs-on: ubuntu-24.04 + + runs-on: ${{ matrix.runs-on }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Build and test + run: python3 Platforms/Android ci --fast-ci ${{ matrix.arch }}-linux-android + + build-ios: + name: iOS + needs: build-context + if: needs.build-context.outputs.run-ios == 'true' + timeout-minutes: 60 + runs-on: macos-14 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + # GitHub recommends explicitly selecting the desired Xcode version: + # https://github.com/actions/runner-images/issues/12541#issuecomment-3083850140 + # This became a necessity as a result of + # https://github.com/actions/runner-images/issues/12541 and + # https://github.com/actions/runner-images/issues/12751. + - name: Select Xcode version + run: | + sudo xcode-select --switch /Applications/Xcode_15.4.app + + - name: Build and test + run: python3 Platforms/Apple ci iOS --fast-ci --simulator 'iPhone SE (3rd generation),OS=17.5' + + build-emscripten: + name: 'Emscripten' + needs: build-context + if: needs.build-context.outputs.run-emscripten == 'true' + uses: ./.github/workflows/reusable-emscripten.yml + build-wasi: name: 'WASI' needs: build-context - if: needs.build-context.outputs.run-tests == 'true' + if: needs.build-context.outputs.run-wasi == 'true' uses: ./.github/workflows/reusable-wasi.yml - with: - config_hash: ${{ needs.build-context.outputs.config-hash }} test-hypothesis: name: "Hypothesis tests on Ubuntu" runs-on: ubuntu-24.04 timeout-minutes: 60 needs: build-context - if: needs.build-context.outputs.run-tests == 'true' + if: needs.build-context.outputs.run-ubuntu == 'true' env: - OPENSSL_VER: 3.0.16 + OPENSSL_VER: 3.5.7 PYTHONSTRICTEXTENSIONBUILD: 1 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Register gcc problem matcher @@ -429,20 +415,13 @@ jobs: echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> "$GITHUB_ENV" - name: 'Restore OpenSSL build' id: cache-openssl - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: ./multissl/openssl/${{ env.OPENSSL_VER }} key: ${{ runner.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 - - name: Add ccache to PATH - run: | - echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV" - - name: Configure ccache action - uses: hendrikmuhs/ccache-action@v1.2 - with: - save: false - name: Setup directory envs for out-of-tree builds run: | echo "CPYTHON_RO_SRCDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-ro-srcdir)" >> "$GITHUB_ENV" @@ -453,11 +432,6 @@ jobs: run: sudo mount --bind -o ro "$GITHUB_WORKSPACE" "$CPYTHON_RO_SRCDIR" - name: Runner image version 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 }}-${{ env.IMAGE_OS_VERSION }}-${{ needs.build-context.outputs.config-hash }} - name: Configure CPython out-of-tree working-directory: ${{ env.CPYTHON_BUILDDIR }} run: | @@ -488,7 +462,7 @@ jobs: ./python -m venv "$VENV_LOC" && "$VENV_PYTHON" -m pip install -r "${GITHUB_WORKSPACE}/Tools/requirements-hypothesis.txt" - name: 'Restore Hypothesis database' id: cache-hypothesis-database - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: ${{ env.CPYTHON_BUILDDIR }}/.hypothesis/ key: hypothesis-database-${{ github.head_ref || github.run_id }} @@ -515,7 +489,7 @@ jobs: -x test_subprocess \ -x test_signal \ -x test_sysconfig - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 if: always() with: name: hypothesis-example-db @@ -526,32 +500,27 @@ jobs: runs-on: ${{ matrix.os }} timeout-minutes: 60 needs: build-context - if: needs.build-context.outputs.run-tests == 'true' + if: needs.build-context.outputs.run-ubuntu == 'true' strategy: fail-fast: false matrix: os: [ubuntu-24.04] env: - OPENSSL_VER: 3.0.16 + OPENSSL_VER: 3.5.7 PYTHONSTRICTEXTENSIONBUILD: 1 ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 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: Install dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: Set up GCC-10 for ASAN - uses: egor-tensin/setup-gcc@v1 + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 with: version: 10 - name: Configure OpenSSL env vars @@ -561,23 +530,15 @@ jobs: echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> "$GITHUB_ENV" - name: 'Restore OpenSSL build' id: cache-openssl - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: ./multissl/openssl/${{ env.OPENSSL_VER }} key: ${{ matrix.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 - - name: Add ccache to PATH - run: | - echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV" - - name: Configure ccache action - uses: hendrikmuhs/ccache-action@v1.2 - with: - save: ${{ github.event_name == 'push' }} - max-size: "200M" - name: Configure CPython - run: ./configure --config-cache --with-address-sanitizer --without-pymalloc + run: ./configure --config-cache --with-address-sanitizer --without-pymalloc --with-openssl="$OPENSSL_DIR" - name: Build CPython run: make -j4 - name: Display build info @@ -586,10 +547,10 @@ jobs: run: xvfb-run make ci build-san: - name: >- # ${{ '' } is a hack to nest jobs under the same sidebar category - Sanitizers${{ '' }} + # ${{ '' } is a hack to nest jobs under the same sidebar category. + name: Sanitizers${{ '' }} # zizmor: ignore[obfuscation] needs: build-context - if: needs.build-context.outputs.run-tests == 'true' + if: needs.build-context.outputs.run-ubuntu == 'true' strategy: fail-fast: false matrix: @@ -607,7 +568,6 @@ jobs: uses: ./.github/workflows/reusable-san.yml with: sanitizer: ${{ matrix.sanitizer }} - config_hash: ${{ needs.build-context.outputs.config-hash }} free-threading: ${{ matrix.free-threading }} cross-build-linux: @@ -615,18 +575,13 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 needs: build-context - if: needs.build-context.outputs.run-tests == 'true' + if: needs.build-context.outputs.run-ubuntu == 'true' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 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 @@ -650,45 +605,49 @@ jobs: 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 + # ${{ '' } is a hack to nest jobs under the same sidebar category. + name: CIFuzz${{ '' }} # zizmor: ignore[obfuscation] needs: build-context - if: needs.build-context.outputs.run-ci-fuzz == 'true' + if: >- + needs.build-context.outputs.run-ci-fuzz == 'true' + || needs.build-context.outputs.run-ci-fuzz-stdlib == 'true' permissions: + contents: read security-events: write strategy: fail-fast: false matrix: - sanitizer: [address, undefined, memory] - steps: - - name: Build fuzzers (${{ matrix.sanitizer }}) - id: build - uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master - with: + sanitizer: + - address + oss-fuzz-project-name: + - cpython3 + - python3-libraries + include: + - sanitizer: undefined oss-fuzz-project-name: cpython3 - sanitizer: ${{ matrix.sanitizer }} - - name: Run fuzzers (${{ matrix.sanitizer }}) - uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master - with: - fuzz-seconds: 600 + - sanitizer: memory oss-fuzz-project-name: cpython3 - output-sarif: true - sanitizer: ${{ matrix.sanitizer }} - - name: Upload crash - if: failure() && steps.build.outcome == 'success' - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.sanitizer }}-artifacts - path: ./out/artifacts - - name: Upload SARIF - if: always() && steps.build.outcome == 'success' - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: cifuzz-sarif/results.sarif - checkout_path: cifuzz-sarif + exclude: + # Note that the 'no-exclude' sentinel below is to prevent + # an empty string value from excluding all jobs and causing + # GHA to create a 'default' matrix entry with all empty values. + - oss-fuzz-project-name: >- + ${{ + needs.build-context.outputs.run-ci-fuzz == 'true' + && 'no-exclude' + || 'cpython3' + }} + - oss-fuzz-project-name: >- + ${{ + needs.build-context.outputs.run-ci-fuzz-stdlib == 'true' + && 'no-exclude' + || 'python3-libraries' + }} + uses: ./.github/workflows/reusable-cifuzz.yml + with: + oss-fuzz-project-name: ${{ matrix.oss-fuzz-project-name }} + sanitizer: ${{ matrix.sanitizer }} all-required-green: # This job does nothing and is only used for the branch protection name: All required checks pass @@ -699,12 +658,14 @@ jobs: - check-docs - check-autoconf-regen - check-generated-files + - check-c-api-docs - build-windows - build-windows-msi - build-macos - build-ubuntu - - build-ubuntu-ssltests-awslc - - build-ubuntu-ssltests-openssl + - build-ubuntu-ssltests + - build-ios + - build-emscripten - build-wasi - test-hypothesis - build-asan @@ -718,48 +679,50 @@ jobs: uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe with: allowed-failures: >- + build-android, + build-emscripten, build-windows-msi, - build-ubuntu-ssltests-awslc, - build-ubuntu-ssltests-openssl, + build-ubuntu-ssltests, test-hypothesis, cifuzz, allowed-skips: >- - ${{ - !fromJSON(needs.build-context.outputs.run-docs) - && ' - check-docs, - ' - || '' - }} + ${{ !fromJSON(needs.build-context.outputs.run-docs) && 'check-docs,' || '' }} ${{ needs.build-context.outputs.run-tests != 'true' && ' check-autoconf-regen, check-generated-files, - build-macos, - build-ubuntu, - build-ubuntu-ssltests-awslc, - build-ubuntu-ssltests-openssl, - build-wasi, - test-hypothesis, - build-asan, - build-san, - cross-build-linux, ' || '' }} ${{ - !fromJSON(needs.build-context.outputs.run-windows-tests) - && ' - build-windows, - ' + !fromJSON(needs.build-context.outputs.run-tests) + && !fromJSON(needs.build-context.outputs.run-docs) + && 'check-c-api-docs,' || '' }} + ${{ !fromJSON(needs.build-context.outputs.run-windows-tests) && 'build-windows,' || '' }} ${{ !fromJSON(needs.build-context.outputs.run-ci-fuzz) + && !fromJSON(needs.build-context.outputs.run-ci-fuzz-stdlib) + && 'cifuzz,' || + '' + }} + ${{ !fromJSON(needs.build-context.outputs.run-macos) && 'build-macos,' || '' }} + ${{ + !fromJSON(needs.build-context.outputs.run-ubuntu) && ' - cifuzz, + build-ubuntu, + build-ubuntu-ssltests, + test-hypothesis, + build-asan, + build-san, + cross-build-linux, ' || '' }} + ${{ !fromJSON(needs.build-context.outputs.run-android) && 'build-android,' || '' }} + ${{ !fromJSON(needs.build-context.outputs.run-ios) && 'build-ios,' || '' }} + ${{ !fromJSON(needs.build-context.outputs.run-emscripten) && 'build-emscripten,' || '' }} + ${{ !fromJSON(needs.build-context.outputs.run-wasi) && 'build-wasi,' || '' }} jobs: ${{ toJSON(needs) }} diff --git a/.github/workflows/documentation-links.yml b/.github/workflows/documentation-links.yml deleted file mode 100644 index a09a30587b35eb..00000000000000 --- a/.github/workflows/documentation-links.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Read the Docs PR preview -# Automatically edits a pull request's descriptions with a link -# to the documentation's preview on Read the Docs. - -on: - pull_request_target: - types: - - opened - paths: - - 'Doc/**' - - '.github/workflows/doc.yml' - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - documentation-links: - runs-on: ubuntu-latest - permissions: - pull-requests: write - timeout-minutes: 5 - - steps: - - uses: readthedocs/actions/preview@v1 - with: - project-slug: "cpython-previews" - single-version: "true" diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 947badff816c09..2f024ad52f3091 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -1,25 +1,18 @@ name: JIT on: pull_request: - paths: + paths: &paths - '**jit**' - 'Python/bytecodes.c' - 'Python/optimizer*.c' - 'Python/executor_cases.c.h' - 'Python/optimizer_cases.c.h' + - '**_testinternalcapi**' - '!Python/perf_jit_trampoline.c' - '!**/*.md' - '!**/*.ini' push: - paths: - - '**jit**' - - 'Python/bytecodes.c' - - 'Python/optimizer*.c' - - 'Python/executor_cases.c.h' - - 'Python/optimizer_cases.c.h' - - '!Python/perf_jit_trampoline.c' - - '!**/*.md' - - '!**/*.ini' + paths: *paths workflow_dispatch: permissions: @@ -31,16 +24,20 @@ concurrency: env: FORCE_COLOR: 1 + LLVM_VERSION: 21 jobs: interpreter: name: Interpreter (Debug) runs-on: ubuntu-24.04 - timeout-minutes: 90 + timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Install dependencies + run: | + sudo ./.github/workflows/posix-deps-apt.sh - name: Build tier two interpreter run: | ./configure --enable-experimental-jit=interpreter --with-pydebug @@ -48,11 +45,12 @@ jobs: - name: Test tier two interpreter run: | ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 - jit: + + windows: name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }}) - needs: interpreter + runs-on: ${{ matrix.runner }} - timeout-minutes: 90 + timeout-minutes: 60 strategy: fail-fast: false matrix: @@ -60,100 +58,156 @@ jobs: - 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 debug: - true - false - llvm: - - 19 include: - target: i686-pc-windows-msvc/msvc architecture: Win32 - runner: windows-latest + runner: windows-2025-vs2026 - target: x86_64-pc-windows-msvc/msvc architecture: x64 - runner: windows-latest + runner: windows-2025-vs2026 - target: aarch64-pc-windows-msvc/msvc architecture: ARM64 runner: windows-11-arm - - 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 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.11' - # PCbuild downloads LLVM automatically: - - name: Windows - if: runner.os == 'Windows' + - name: Build run: | ./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }} + - name: Test + run: | ./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 - # the symlink to the pre-installed Python so that the Homebrew Python is used instead. - - name: macOS - if: runner.os == 'macOS' + macos: + name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }}) + + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + target: + - x86_64-apple-darwin/clang + - aarch64-apple-darwin/clang + debug: + - true + - false + include: + - target: x86_64-apple-darwin/clang + runner: macos-15-intel + - target: aarch64-apple-darwin/clang + runner: macos-15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + - name: Install LLVM run: | brew update - find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete - brew install llvm@${{ matrix.llvm }} + brew install llvm@${{ env.LLVM_VERSION }} + - name: Build + run: | export SDKROOT="$(xcrun --show-sdk-path)" + # Set MACOSX_DEPLOYMENT_TARGET and -Werror=unguarded-availability to + # make sure we don't break downstream distributors (like uv): + export CFLAGS_JIT='-Werror=unguarded-availability' + export MACOSX_DEPLOYMENT_TARGET=10.15 ./configure --enable-experimental-jit --enable-universalsdk --with-universal-archs=universal2 ${{ matrix.debug && '--with-pydebug' || '' }} make all --jobs 4 + - name: Test + run: | ./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 - - name: Linux - if: runner.os == 'Linux' + linux: + name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }}) + + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu/gcc + - aarch64-unknown-linux-gnu/gcc + debug: + - true + - false + include: + - target: x86_64-unknown-linux-gnu/gcc + runner: ubuntu-24.04 + - target: aarch64-unknown-linux-gnu/gcc + runner: ubuntu-24.04-arm + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + - name: Install dependencies + run: | + sudo ./.github/workflows/posix-deps-apt.sh + - name: Build run: | - sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }} - export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH" + sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ env.LLVM_VERSION }} + export PATH="$(llvm-config-${{ env.LLVM_VERSION }} --bindir):$PATH" ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '' }} make all --jobs 4 + - name: Test + 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 + linux-extras: + name: ${{ matrix.name }} + + runs-on: ubuntu-24.04 + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - name: Free-Threaded (Debug) + configure_flags: --enable-experimental-jit --with-pydebug --disable-gil + continue_on_error: true + - name: JIT without optimizations (Debug) + configure_flags: --enable-experimental-jit --with-pydebug + test_env: "PYTHON_UOPS_OPTIMIZE=0" + - name: JIT with tail calling interpreter + configure_flags: --enable-experimental-jit --with-tail-call-interp --with-pydebug + use_clang: true + run_tests: false + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + - name: Install dependencies + run: | + sudo ./.github/workflows/posix-deps-apt.sh + - name: Build + run: | + sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ env.LLVM_VERSION }} + export PATH="$(llvm-config-${{ env.LLVM_VERSION }} --bindir):$PATH" + if [ "${{ matrix.use_clang }}" = "true" ]; then + export CC=clang-${{ env.LLVM_VERSION }} + fi + ./configure ${{ matrix.configure_flags }} + make all --jobs 4 + - name: Test + if: matrix.run_tests != false + run: | + ${{ matrix.test_env }} ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 + continue-on-error: ${{ matrix.continue_on_error }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d74ce8fcc256dc..e9a4eb2b0808cb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,10 +19,7 @@ jobs: timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v5 - with: - python-version: "3.x" - - uses: pre-commit/action@v3.0.1 + - uses: j178/prek-action@0bb87d7f00b0c99306c8bcb8b8beba1eb581c037 # v1.1.1 diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 95133c1338b682..d748b6ff63e68a 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -13,14 +13,21 @@ on: - "Lib/test/libregrtest/**" - "Lib/tomllib/**" - "Misc/mypy/**" + - "Tools/build/check_extension_modules.py" + - "Tools/build/check_warnings.py" - "Tools/build/compute-changes.py" + - "Tools/build/consts_getter.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/generate_levenshtein_examples.py" + - "Tools/build/generate_sbom.py" + - "Tools/build/generate_stdlib_module_names.py" + - "Tools/build/mypy.ini" - "Tools/build/umarshal.py" + - "Tools/build/update_file.py" + - "Tools/build/verify_ensurepip_wheels.py" - "Tools/cases_generator/**" + - "Tools/check-c-api-docs/**" - "Tools/clinic/**" - "Tools/jit/**" - "Tools/peg_generator/**" @@ -53,19 +60,20 @@ jobs: "Lib/tomllib", "Tools/build", "Tools/cases_generator", + "Tools/check-c-api-docs", "Tools/clinic", "Tools/jit", "Tools/peg_generator", ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v5 + - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: - python-version: "3.13" - cache: pip - cache-dependency-path: Tools/requirements-dev.txt - - run: pip install -r Tools/requirements-dev.txt + python-version: "3.15" + activate-environment: true + cache-dependency-glob: Tools/requirements-dev.txt + - run: uv pip install -r Tools/requirements-dev.txt - run: python3 Misc/mypy/make_symlinks.py --symlink - - run: mypy --config-file ${{ matrix.target }}/mypy.ini + - run: mypy --num-workers 4 --config-file ${{ matrix.target }}/mypy.ini diff --git a/.github/workflows/new-bugs-announce-notifier.yml b/.github/workflows/new-bugs-announce-notifier.yml index 9f1a8a824e5f19..1267361040c81b 100644 --- a/.github/workflows/new-bugs-announce-notifier.yml +++ b/.github/workflows/new-bugs-announce-notifier.yml @@ -6,19 +6,21 @@ on: - opened permissions: - issues: read + contents: read jobs: notify-new-bugs-announce: runs-on: ubuntu-latest + permissions: + issues: read timeout-minutes: 10 steps: - - uses: actions/setup-node@v4 + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: 20 - run: npm install mailgun.js form-data - name: Send notification - uses: actions/github-script@v7 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: MAILGUN_API_KEY: ${{ secrets.MAILGUN_PYTHON_ORG_MAILGUN_KEY }} with: @@ -44,7 +46,7 @@ jobs: // We need to truncate the body size, because the max size for // the whole payload is 16kb. We want to be safe and assume that // body can take up to ~8kb of space. - body : issue.data.body.substring(0, 8000) + body : (issue.data.body || "").substring(0, 8000) }; const data = { diff --git a/.github/workflows/posix-deps-apt.sh b/.github/workflows/posix-deps-apt.sh index 0b64367e6c4562..f241fccdeb2a32 100755 --- a/.github/workflows/posix-deps-apt.sh +++ b/.github/workflows/posix-deps-apt.sh @@ -1,10 +1,9 @@ #!/bin/sh apt-get update -apt-get -yq install \ +apt-get -yq --no-install-recommends install \ build-essential \ pkg-config \ - ccache \ cmake \ gdb \ lcov \ @@ -27,9 +26,16 @@ apt-get -yq install \ xvfb \ zlib1g-dev -# Workaround missing libmpdec-dev on ubuntu 24.04: -# https://launchpad.net/~ondrej/+archive/ubuntu/php -# https://deb.sury.org/ -sudo add-apt-repository ppa:ondrej/php -apt-get update -apt-get -yq install libmpdec-dev +# Workaround missing libmpdec-dev on ubuntu 24.04 by building mpdecimal +# from source. ppa:ondrej/php (launchpad.net) are unreliable +# (https://status.canonical.com) so fetch the tarball directly +# from the upstream host. +# https://www.bytereef.org/mpdecimal/ +MPDECIMAL_VERSION=4.0.1 +curl -fsSL "https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-${MPDECIMAL_VERSION}.tar.gz" \ + | tar -xz -C /tmp +(cd "/tmp/mpdecimal-${MPDECIMAL_VERSION}" \ + && ./configure --prefix=/usr/local \ + && make -j"$(nproc)" \ + && make install) +ldconfig diff --git a/.github/workflows/project-updater.yml b/.github/workflows/project-updater.yml deleted file mode 100644 index 1d9d637ec848a6..00000000000000 --- a/.github/workflows/project-updater.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Update GH projects - -on: - issues: - types: - - opened - - labeled - -permissions: - contents: read - -jobs: - add-to-project: - name: Add issues to projects - 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 - # to the corresponding project - - { project: 2, label: "release-blocker, deferred-blocker" } - - { project: 32, label: sprint } - - steps: - - uses: actions/add-to-project@v1.0.0 - with: - project-url: https://github.com/orgs/python/projects/${{ matrix.project }} - github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} - labeled: ${{ matrix.label }} diff --git a/.github/workflows/regen-abidump.sh b/.github/workflows/regen-abidump.sh index 251bb3857ecfcb..75a1a72e370202 100644 --- a/.github/workflows/regen-abidump.sh +++ b/.github/workflows/regen-abidump.sh @@ -2,7 +2,7 @@ set -ex export DEBIAN_FRONTEND=noninteractive ./.github/workflows/posix-deps-apt.sh -apt-get install -yq abigail-tools python3 +apt-get install -yq --no-install-recommends abigail-tools python3 export CFLAGS="-g3 -O0" ./configure --enable-shared && make make regen-abidump diff --git a/.github/workflows/require-pr-label.yml b/.github/workflows/require-pr-label.yml index 7e534c58c798d1..f3e2666879530f 100644 --- a/.github/workflows/require-pr-label.yml +++ b/.github/workflows/require-pr-label.yml @@ -4,6 +4,9 @@ on: pull_request: types: [opened, reopened, labeled, unlabeled, synchronize] +permissions: + contents: read + jobs: label-dnm: name: DO-NOT-MERGE @@ -15,7 +18,7 @@ jobs: steps: - name: Check there's no DO-NOT-MERGE - uses: mheap/github-action-required-labels@v5 + uses: mheap/github-action-required-labels@0ac283b4e65c1fb28ce6079dea5546ceca98ccbe # v5.5.2 with: mode: exactly count: 0 @@ -33,7 +36,7 @@ jobs: steps: # Check that the PR is not awaiting changes from the author due to previous review. - name: Check there's no required changes - uses: mheap/github-action-required-labels@v5 + uses: mheap/github-action-required-labels@0ac283b4e65c1fb28ce6079dea5546ceca98ccbe # v5.5.2 with: mode: exactly count: 0 @@ -42,7 +45,7 @@ jobs: awaiting change review - id: is-feature name: Check whether this PR is a feature (contains a "type-feature" label) - uses: mheap/github-action-required-labels@v5 + uses: mheap/github-action-required-labels@0ac283b4e65c1fb28ce6079dea5546ceca98ccbe # v5.5.2 with: mode: exactly count: 1 @@ -53,7 +56,7 @@ jobs: - id: awaiting-merge if: steps.is-feature.outputs.status == 'success' name: Check for complete review - uses: mheap/github-action-required-labels@v5 + uses: mheap/github-action-required-labels@0ac283b4e65c1fb28ce6079dea5546ceca98ccbe # v5.5.2 with: mode: exactly count: 1 diff --git a/.github/workflows/reusable-check-c-api-docs.yml b/.github/workflows/reusable-check-c-api-docs.yml new file mode 100644 index 00000000000000..49e5ef7f768b79 --- /dev/null +++ b/.github/workflows/reusable-check-c-api-docs.yml @@ -0,0 +1,25 @@ +name: Reusable C API Docs Check + +on: + workflow_call: + +permissions: + contents: read + +env: + FORCE_COLOR: 1 + +jobs: + check-c-api-docs: + name: 'Check if all C APIs are documented' + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.x' + - name: Check for undocumented C APIs + run: python Tools/check-c-api-docs/main.py diff --git a/.github/workflows/reusable-check-html-ids.yml b/.github/workflows/reusable-check-html-ids.yml new file mode 100644 index 00000000000000..4f827c55cacd06 --- /dev/null +++ b/.github/workflows/reusable-check-html-ids.yml @@ -0,0 +1,67 @@ +name: Reusable check HTML IDs + +on: + workflow_call: + +permissions: + contents: read + +env: + FORCE_COLOR: 1 + +jobs: + check-html-ids: + name: 'Check for removed HTML IDs' + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: 'Check out PR head' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + ref: ${{ github.event.pull_request.head.sha }} + - name: 'Find merge base' + id: merge-base + run: | + BASE="${{ github.event.pull_request.base.sha }}" + HEAD="${{ github.event.pull_request.head.sha }}" + git fetch --depth=$((${{ github.event.pull_request.commits }} + 10)) --no-tags origin "$BASE" "$HEAD" + + if ! MERGE_BASE=$(git merge-base "$BASE" "$HEAD" 2>/dev/null); then + git fetch --deepen=1 --no-tags origin "$BASE" "$HEAD" + + OLDEST=$(git rev-list --reflog --max-parents=0 --reverse "${BASE}^" "${HEAD}^" | head -1) + TIMESTAMP=$(git show --format=%at --no-patch "$OLDEST") + + git fetch --shallow-since="$TIMESTAMP" --no-tags origin "$BASE" "$HEAD" + + MERGE_BASE=$(git merge-base "$BASE" "$HEAD") + fi + echo "sha=$MERGE_BASE" >> "$GITHUB_OUTPUT" + - name: 'Create worktree at merge base' + env: + MERGE_BASE: ${{ steps.merge-base.outputs.sha }} + run: git worktree add /tmp/merge-base "$MERGE_BASE" --detach + - name: 'Set up Python' + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3' + cache: 'pip' + cache-dependency-path: 'Doc/requirements.txt' + - name: 'Install build dependencies' + run: make -C /tmp/merge-base/Doc/ venv + - name: 'Build HTML documentation' + run: make -C /tmp/merge-base/Doc/ SPHINXOPTS="--quiet" html + - name: 'Collect HTML IDs' + run: python Doc/tools/check-html-ids.py collect /tmp/merge-base/Doc/build/html -o /tmp/html-ids-base.json.gz + - name: 'Download PR head HTML IDs' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: html-ids-head.json.gz + path: /tmp + - name: 'Check for removed HTML IDs' + run: | + # shellcheck disable=SC2046 + python Doc/tools/check-html-ids.py -v check \ + /tmp/html-ids-base.json.gz /tmp/html-ids-head.json.gz \ + $([ -f Doc/tools/removed-ids.txt ] && echo "--exclude-file Doc/tools/removed-ids.txt") diff --git a/.github/workflows/reusable-cifuzz.yml b/.github/workflows/reusable-cifuzz.yml new file mode 100644 index 00000000000000..0d02232686339b --- /dev/null +++ b/.github/workflows/reusable-cifuzz.yml @@ -0,0 +1,49 @@ +# CIFuzz job based on https://google.github.io/oss-fuzz/getting-started/continuous-integration/ +name: Reusable CIFuzz + +on: + workflow_call: + inputs: + oss-fuzz-project-name: + description: OSS-Fuzz project name + required: true + type: string + sanitizer: + description: OSS-Fuzz sanitizer + required: true + type: string + +permissions: + contents: read + +jobs: + cifuzz: + name: ${{ inputs.oss-fuzz-project-name }} (${{ inputs.sanitizer }}) + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Build fuzzers (${{ inputs.sanitizer }}) + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@ed23f8af80ff82b25ca67cd9b101e690b8897b3f # master + with: + oss-fuzz-project-name: ${{ inputs.oss-fuzz-project-name }} + sanitizer: ${{ inputs.sanitizer }} + - name: Run fuzzers (${{ inputs.sanitizer }}) + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@ed23f8af80ff82b25ca67cd9b101e690b8897b3f # master + with: + fuzz-seconds: 600 + oss-fuzz-project-name: ${{ inputs.oss-fuzz-project-name }} + output-sarif: true + sanitizer: ${{ inputs.sanitizer }} + - name: Upload crash + if: failure() && steps.build.outcome == 'success' + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: ${{ inputs.sanitizer }}-artifacts + path: ./out/artifacts + - name: Upload SARIF + if: always() && steps.build.outcome == 'success' + uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 + with: + sarif_file: cifuzz-sarif/results.sarif + checkout_path: cifuzz-sarif diff --git a/.github/workflows/reusable-context.yml b/.github/workflows/reusable-context.yml index d2668ddcac1a3d..b8a9e2960eca59 100644 --- a/.github/workflows/reusable-context.yml +++ b/.github/workflows/reusable-context.yml @@ -17,24 +17,45 @@ on: # yamllint disable-line rule:truthy # || 'falsy-branch' # }} # - config-hash: - description: Config hash value for use in cache keys - value: ${{ jobs.compute-changes.outputs.config-hash }} # str + run-android: + description: Whether to run the Android tests + value: ${{ jobs.compute-changes.outputs.run-android }} # bool + run-ci-fuzz: + description: Whether to run the CIFuzz job for 'cpython' fuzzer + value: ${{ jobs.compute-changes.outputs.run-ci-fuzz }} # bool + run-ci-fuzz-stdlib: + description: Whether to run the CIFuzz job for 'python3-libraries' fuzzer + value: ${{ jobs.compute-changes.outputs.run-ci-fuzz-stdlib }} # bool run-docs: description: Whether to build the docs value: ${{ jobs.compute-changes.outputs.run-docs }} # bool + run-ios: + description: Whether to run the iOS tests + value: ${{ jobs.compute-changes.outputs.run-ios }} # bool + run-macos: + description: Whether to run the macOS tests + value: ${{ jobs.compute-changes.outputs.run-macos }} # 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-ubuntu: + description: Whether to run the Ubuntu tests + value: ${{ jobs.compute-changes.outputs.run-ubuntu }} # bool + run-emscripten: + description: Whether to run the Emscripten tests + value: ${{ jobs.compute-changes.outputs.run-emscripten }} # bool + run-wasi: + description: Whether to run the WASI tests + value: ${{ jobs.compute-changes.outputs.run-wasi }} # 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 + run-windows-tests: + description: Whether to run the Windows tests + value: ${{ jobs.compute-changes.outputs.run-windows-tests }} # bool + +permissions: + contents: read jobs: compute-changes: @@ -42,22 +63,28 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 outputs: - config-hash: ${{ steps.config-hash.outputs.hash }} + run-android: ${{ steps.changes.outputs.run-android }} run-ci-fuzz: ${{ steps.changes.outputs.run-ci-fuzz }} + run-ci-fuzz-stdlib: ${{ steps.changes.outputs.run-ci-fuzz-stdlib }} run-docs: ${{ steps.changes.outputs.run-docs }} + run-ios: ${{ steps.changes.outputs.run-ios }} + run-macos: ${{ steps.changes.outputs.run-macos }} run-tests: ${{ steps.changes.outputs.run-tests }} + run-ubuntu: ${{ steps.changes.outputs.run-ubuntu }} + run-emscripten: ${{ steps.changes.outputs.run-emscripten }} + run-wasi: ${{ steps.changes.outputs.run-wasi }} 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 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3" - run: >- echo '${{ github.event_name }}' - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false ref: >- @@ -100,8 +127,3 @@ jobs: 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" diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml index 7b9dc4818577eb..7b524569f85c9e 100644 --- a/.github/workflows/reusable-docs.yml +++ b/.github/workflows/reusable-docs.yml @@ -27,7 +27,7 @@ jobs: 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 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false ref: >- @@ -52,11 +52,11 @@ jobs: git fetch origin "${refspec_base}" --shallow-since="${DATE}" \ --no-tags --prune --no-recurse-submodules - name: 'Set up Python' - uses: actions/setup-python@v5 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3' cache: 'pip' - cache-dependency-path: 'Doc/requirements.txt' + cache-dependency-path: 'Doc/pylock.toml' - name: 'Install build dependencies' run: make -C Doc/ venv @@ -75,18 +75,22 @@ jobs: --fail-if-regression \ --fail-if-improved \ --fail-if-new-news-nit - - name: 'Build EPUB documentation' - continue-on-error: true - run: | - set -Eeuo pipefail - make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet" epub - pip install epubcheck - epubcheck Doc/build/epub/Python.epub &> Doc/epubcheck.txt - - name: 'Check for fatal errors in EPUB' + - name: 'Collect HTML IDs' if: github.event_name == 'pull_request' - continue-on-error: true # until gh-136155 is fixed - run: | - python Doc/tools/check-epub.py + run: python Doc/tools/check-html-ids.py collect Doc/build/html -o Doc/build/html-ids-head.json.gz + - name: 'Upload HTML IDs' + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: html-ids-head + path: Doc/build/html-ids-head.json.gz + archive: false + + check-html-ids: + name: 'Check for removed HTML IDs' + needs: build-doc + if: github.event_name == 'pull_request' + uses: ./.github/workflows/reusable-check-html-ids.yml # Run "doctest" on HEAD as new syntax doesn't exist in the latest stable release doctest: @@ -94,17 +98,17 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/cache@v4 + - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: ~/.cache/pip key: ubuntu-doc-${{ hashFiles('Doc/requirements.txt') }} restore-keys: | ubuntu-doc- - name: 'Install Dependencies' - run: sudo ./.github/workflows/posix-deps-apt.sh && sudo apt-get install wamerican + run: sudo ./.github/workflows/posix-deps-apt.sh && sudo apt-get install --no-install-recommends wamerican - name: 'Configure CPython' run: ./configure --with-pydebug - name: 'Build CPython' @@ -114,3 +118,30 @@ jobs: # Use "xvfb-run" since some doctest tests open GUI windows - name: 'Run documentation doctest' run: xvfb-run make -C Doc/ PYTHON=../python SPHINXERRORHANDLING="--fail-on-warning" doctest + + check-epub: + name: 'Check EPUB' + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: 'Set up Python' + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3' + cache: 'pip' + cache-dependency-path: 'Doc/requirements.txt' + - name: 'Install build dependencies' + run: | + make -C Doc/ venv + python -m pip install epubcheck + - name: 'Build EPUB documentation' + run: make -C Doc/ PYTHON=../python epub + - name: 'Run epubcheck' + continue-on-error: true + run: epubcheck Doc/build/epub/Python.epub &> Doc/epubcheck.txt + - run: cat Doc/epubcheck.txt + - name: 'Check for fatal errors in EPUB' + run: python Doc/tools/check-epub.py diff --git a/.github/workflows/reusable-emscripten.yml b/.github/workflows/reusable-emscripten.yml new file mode 100644 index 00000000000000..69a780a9aebc25 --- /dev/null +++ b/.github/workflows/reusable-emscripten.yml @@ -0,0 +1,77 @@ +name: Reusable Emscripten + +on: + workflow_call: + +permissions: + contents: read + +env: + FORCE_COLOR: 1 + +jobs: + build-emscripten-reusable: + name: 'build and test' + runs-on: ubuntu-24.04 + timeout-minutes: 40 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: "Read Emscripten config" + id: emscripten-config + shell: python + run: | + import hashlib + import json + import os + import tomllib + from pathlib import Path + + config = tomllib.loads(Path("Platforms/emscripten/config.toml").read_text()) + h = hashlib.sha256() + h.update(json.dumps(config["dependencies"], sort_keys=True).encode()) + h.update(Path("Platforms/emscripten/make_libffi.sh").read_bytes()) + h.update(b'1') # Update to explicitly bust cache + emsdk_cache = Path(os.environ["RUNNER_TEMP"]) / "emsdk-cache" + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"emscripten-version={config['emscripten-version']}\n") + f.write(f"node-version={config['node-version']}\n") + f.write(f"deps-hash={h.hexdigest()}\n") + with open(os.environ["GITHUB_ENV"], "a") as f: + f.write(f"EMSDK_CACHE={emsdk_cache}\n") + - name: "Install Node.js" + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: ${{ steps.emscripten-config.outputs.node-version }} + - name: "Cache Emscripten SDK" + id: emsdk-cache + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + with: + path: ${{ env.EMSDK_CACHE }} + key: emsdk-${{ steps.emscripten-config.outputs.emscripten-version }}-${{ steps.emscripten-config.outputs.deps-hash }} + restore-keys: emsdk-${{ steps.emscripten-config.outputs.emscripten-version }} + - name: "Install Python" + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.x' + - name: "Runner image version" + run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV" + - name: "Install Emscripten" + run: python3 Platforms/emscripten install-emscripten + - name: "Configure build Python" + run: python3 Platforms/emscripten configure-build-python -- --config-cache --with-pydebug + - name: "Make build Python" + run: python3 Platforms/emscripten make-build-python + - name: "Make dependencies" + run: >- + python3 Platforms/emscripten make-dependencies + ${{ steps.emsdk-cache.outputs.cache-hit == 'true' && '--check-up-to-date' || '' }} + - name: "Configure host Python" + run: python3 Platforms/emscripten configure-host --host-runner node -- --config-cache + - name: "Make host Python" + run: python3 Platforms/emscripten make-host + - name: "Display build info" + run: python3 Platforms/emscripten run --pythoninfo + - name: "Test" + run: python3 Platforms/emscripten run --test diff --git a/.github/workflows/reusable-macos.yml b/.github/workflows/reusable-macos.yml index de0c40221364ad..65a7f857fc4c77 100644 --- a/.github/workflows/reusable-macos.yml +++ b/.github/workflows/reusable-macos.yml @@ -3,9 +3,6 @@ name: Reusable macOS on: workflow_call: inputs: - config_hash: - required: true - type: string free-threading: required: false type: boolean @@ -15,6 +12,9 @@ on: required: true type: string +permissions: + contents: read + env: FORCE_COLOR: 1 @@ -31,21 +31,15 @@ jobs: PYTHONSTRICTEXTENSIONBUILD: 1 TERM: linux steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 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 }}-${{ inputs.config_hash }} - name: Install Homebrew dependencies 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 + brew bundle --file=Misc/Brewfile + brew install make - name: Configure CPython run: | MACOSX_DEPLOYMENT_TARGET=10.15 \ @@ -58,17 +52,17 @@ jobs: --enable-safety \ ${{ inputs.free-threading && '--disable-gil' || '' }} \ --prefix=/opt/python-dev \ - --with-openssl="$(brew --prefix openssl@3.0)" + --with-openssl="$(brew --prefix openssl@3.5)" - name: Build CPython - if : ${{ inputs.free-threading || inputs.os != 'macos-13' }} + if : ${{ inputs.free-threading || inputs.os != 'macos-26-intel' }} run: gmake -j8 - name: Build CPython for compiler warning check - if : ${{ !inputs.free-threading && inputs.os == 'macos-13' }} + if : ${{ !inputs.free-threading && inputs.os == 'macos-26-intel' }} run: set -o pipefail; gmake -j8 --output-sync 2>&1 | tee compiler_output_macos.txt - name: Display build info run: make pythoninfo - name: Check compiler warnings - if : ${{ !inputs.free-threading && inputs.os == 'macos-13' }} + if : ${{ !inputs.free-threading && inputs.os == 'macos-26-intel' }} run: >- python3 Tools/build/check_warnings.py --compiler-output-file-path=compiler_output_macos.txt diff --git a/.github/workflows/reusable-san.yml b/.github/workflows/reusable-san.yml index e6ff02e4838ee6..2ceb1000e8a5a9 100644 --- a/.github/workflows/reusable-san.yml +++ b/.github/workflows/reusable-san.yml @@ -6,15 +6,15 @@ on: sanitizer: required: true type: string - config_hash: - required: true - type: string free-threading: description: Whether to use free-threaded mode required: false type: boolean default: false +permissions: + contents: read + env: FORCE_COLOR: 1 @@ -29,33 +29,26 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 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 }}-${{ inputs.sanitizer }}-${{ inputs.config_hash }} - name: Install dependencies run: | sudo ./.github/workflows/posix-deps-apt.sh # Install clang wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh + sudo ./llvm.sh 20 + sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-20 100 + sudo update-alternatives --set clang /usr/bin/clang-20 + sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-20 100 + sudo update-alternatives --set clang++ /usr/bin/clang++-20 if [ "${SANITIZER}" = "TSan" ]; then - sudo ./llvm.sh 17 # gh-121946: llvm-18 package is temporarily broken - sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-17 100 - sudo update-alternatives --set clang /usr/bin/clang-17 - sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-17 100 - sudo update-alternatives --set clang++ /usr/bin/clang++-17 # Reduce ASLR to avoid TSan crashing sudo sysctl -w vm.mmap_rnd_bits=28 - else - sudo ./llvm.sh 20 fi - name: Sanitizer option setup @@ -67,21 +60,13 @@ jobs: || '' }}.txt handle_segv=0" >> "$GITHUB_ENV" else - echo "UBSAN_OPTIONS=${SAN_LOG_OPTION}" >> "$GITHUB_ENV" + echo "UBSAN_OPTIONS=${SAN_LOG_OPTION} halt_on_error=1 suppressions=${GITHUB_WORKSPACE}/Tools/ubsan/suppressions.txt" >> "$GITHUB_ENV" fi echo "CC=clang" >> "$GITHUB_ENV" echo "CXX=clang++" >> "$GITHUB_ENV" env: SANITIZER: ${{ inputs.sanitizer }} SAN_LOG_OPTION: log_path=${{ github.workspace }}/san_log - - name: Add ccache to PATH - run: | - echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV" - - name: Configure ccache action - uses: hendrikmuhs/ccache-action@v1.2 - with: - save: ${{ github.event_name == 'push' }} - max-size: "200M" - name: Configure CPython run: >- ./configure @@ -89,7 +74,7 @@ jobs: ${{ inputs.sanitizer == 'TSan' && '--with-thread-sanitizer' - || '--with-undefined-behavior-sanitizer' + || '--with-undefined-behavior-sanitizer --with-strict-overflow' }} --with-pydebug ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }} @@ -101,18 +86,18 @@ jobs: run: >- ./python -m test ${{ inputs.sanitizer == 'TSan' && '--tsan' || '' }} - -j4 + -j4 -W - name: Parallel tests if: >- inputs.sanitizer == 'TSan' && fromJSON(inputs.free-threading) - run: ./python -m test --tsan-parallel --parallel-threads=4 -j4 + run: ./python -m test --tsan-parallel --parallel-threads=4 -j4 -W - name: Display logs if: always() run: find "${GITHUB_WORKSPACE}" -name 'san_log.*' | xargs head -n 1000 - name: Archive logs if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: >- ${{ inputs.sanitizer }}-logs-${{ diff --git a/.github/workflows/reusable-ubuntu.yml b/.github/workflows/reusable-ubuntu.yml index 76b19fd5d1a72e..f4321cefa1b598 100644 --- a/.github/workflows/reusable-ubuntu.yml +++ b/.github/workflows/reusable-ubuntu.yml @@ -3,9 +3,6 @@ name: Reusable Ubuntu on: workflow_call: inputs: - config_hash: - required: true - type: string bolt-optimizations: description: Whether to enable BOLT optimizations required: false @@ -20,6 +17,14 @@ on: description: OS to run the job required: true type: string + test-opts: + description: Extra options to pass to the test runner via TESTOPTS + required: false + type: string + default: '' + +permissions: + contents: read env: FORCE_COLOR: 1 @@ -30,11 +35,11 @@ jobs: runs-on: ${{ inputs.os }} timeout-minutes: 60 env: - OPENSSL_VER: 3.0.15 + OPENSSL_VER: 3.5.7 PYTHONSTRICTEXTENSIONBUILD: 1 TERM: linux steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Register gcc problem matcher @@ -45,7 +50,7 @@ jobs: 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 + sudo apt-get install --no-install-recommends bolt-19 echo PATH="$(llvm-config-19 --bindir):$PATH" >> $GITHUB_ENV - name: Configure OpenSSL env vars run: | @@ -54,21 +59,13 @@ jobs: echo "LD_LIBRARY_PATH=${GITHUB_WORKSPACE}/multissl/openssl/${OPENSSL_VER}/lib" >> "$GITHUB_ENV" - name: 'Restore OpenSSL build' id: cache-openssl - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: ./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 - - name: Add ccache to PATH - run: | - echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV" - - name: Configure ccache action - uses: hendrikmuhs/ccache-action@v1.2 - with: - save: ${{ github.event_name == 'push' }} - max-size: "200M" - name: Setup directory envs for out-of-tree builds run: | echo "CPYTHON_RO_SRCDIR=$(realpath -m "${GITHUB_WORKSPACE}"/../cpython-ro-srcdir)" >> "$GITHUB_ENV" @@ -79,11 +76,6 @@ jobs: run: sudo mount --bind -o ro "$GITHUB_WORKSPACE" "$CPYTHON_RO_SRCDIR" - name: Runner image version 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 }}-${{ 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 @@ -124,4 +116,6 @@ jobs: run: sudo mount "$CPYTHON_RO_SRCDIR" -oremount,rw - name: Tests working-directory: ${{ env.CPYTHON_BUILDDIR }} - run: xvfb-run make ci + run: xvfb-run make ci EXTRATESTOPTS="${TEST_OPTS}" + env: + TEST_OPTS: ${{ inputs.test-opts }} diff --git a/.github/workflows/reusable-wasi.yml b/.github/workflows/reusable-wasi.yml index 6beb91e66d4027..48fb70cbff8009 100644 --- a/.github/workflows/reusable-wasi.yml +++ b/.github/workflows/reusable-wasi.yml @@ -2,10 +2,9 @@ name: Reusable WASI on: workflow_call: - inputs: - config_hash: - required: true - type: string + +permissions: + contents: read env: FORCE_COLOR: 1 @@ -13,71 +12,55 @@ env: jobs: build-wasi-reusable: name: 'build and test' - runs-on: ubuntu-24.04 + runs-on: ubuntu-24.04-arm timeout-minutes: 60 env: - WASMTIME_VERSION: 22.0.0 - WASI_SDK_VERSION: 24 - WASI_SDK_PATH: /opt/wasi-sdk + WASMTIME_VERSION: 38.0.3 CROSS_BUILD_PYTHON: cross-build/build CROSS_BUILD_WASI: cross-build/wasm32-wasip1 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 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 + uses: bytecodealliance/actions/wasmtime/setup@9152e710e9f7182e4c29ad218e4f335a7b203613 # v1.1.3 with: version: ${{ env.WASMTIME_VERSION }} - - name: "Restore WASI SDK" - id: cache-wasi-sdk - uses: actions/cache@v4 - with: - path: ${{ env.WASI_SDK_PATH }} - key: ${{ runner.os }}-wasi-sdk-${{ env.WASI_SDK_VERSION }} - - name: "Install WASI SDK" # Hard-coded to x64. - if: steps.cache-wasi-sdk.outputs.cache-hit != 'true' + - name: "Read WASI SDK version" + id: wasi-sdk-version run: | - 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 + import tomllib + from pathlib import Path + import os + config = tomllib.loads(Path("Platforms/WASI/config.toml").read_text()) + version = config["targets"]["wasi-sdk"] + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"version={version}\n") + shell: python + - name: "Install WASI SDK" + id: install-wasi-sdk + uses: bytecodealliance/setup-wasi-sdk-action@b2de090b44eb70013ee96b393727d473b35e1728 with: - save: ${{ github.event_name == 'push' }} - max-size: "200M" - - name: "Add ccache to PATH" - run: echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV" + version: ${{ steps.wasi-sdk-version.outputs.version }} + add-to-path: false - name: "Install Python" - uses: actions/setup-python@v5 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 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: - path: ${{ env.CROSS_BUILD_PYTHON }}/config.cache - # 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 }}-${{ 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 + run: python3 Platforms/WASI configure-build-python -- --config-cache --with-pydebug - name: "Make build Python" - run: python3 Tools/wasm/wasi.py make-build-python - - name: "Restore host config.cache" - uses: actions/cache@v4 - with: - path: ${{ env.CROSS_BUILD_WASI }}/config.cache - # Should be kept in sync with the other config.cache step above. - key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ env.WASI_SDK_VERSION }}-${{ env.WASMTIME_VERSION }}-${{ inputs.config_hash }}-${{ hashFiles('Tools/wasm/wasi.py') }}-${{ env.pythonLocation }} + run: python3 Platforms/WASI make-build-python - name: "Configure host" # `--with-pydebug` inferred from configure-build-python - run: python3 Tools/wasm/wasi.py configure-host -- --config-cache + run: python3 Platforms/WASI configure-host -- --config-cache + env: + WASI_SDK_PATH: ${{ steps.install-wasi-sdk.outputs.wasi-sdk-path }} - name: "Make host" - run: python3 Tools/wasm/wasi.py make-host + run: python3 Platforms/WASI make-host - name: "Display build info" run: make --directory "${CROSS_BUILD_WASI}" pythoninfo - name: "Test" diff --git a/.github/workflows/reusable-windows-msi.yml b/.github/workflows/reusable-windows-msi.yml index a50de344bba4da..a74724323ec15f 100644 --- a/.github/workflows/reusable-windows-msi.yml +++ b/.github/workflows/reusable-windows-msi.yml @@ -17,13 +17,13 @@ env: jobs: build: name: installer for ${{ inputs.arch }} - runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-latest' }} + runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025-vs2026' }} timeout-minutes: 60 env: ARCH: ${{ inputs.arch }} IncludeFreethreaded: true steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Build CPython installer diff --git a/.github/workflows/reusable-windows.yml b/.github/workflows/reusable-windows.yml index 37c802095b0f2f..c6e8128884e90c 100644 --- a/.github/workflows/reusable-windows.yml +++ b/.github/workflows/reusable-windows.yml @@ -12,30 +12,38 @@ on: required: false type: boolean default: false + interpreter: + description: Which interpreter to build (switch-case or tail-call) + required: true + type: string + +permissions: + contents: read env: FORCE_COLOR: 1 - IncludeUwp: >- - true jobs: build: - name: Build and test (${{ inputs.arch }}) - runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-latest' }} + name: Build and test (${{ inputs.arch }}, ${{ inputs.interpreter }}) + runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2025-vs2026' }} timeout-minutes: 60 env: ARCH: ${{ inputs.arch }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Register MSVC problem matcher if: inputs.arch != 'Win32' run: echo "::add-matcher::.github/problem-matchers/msvc.json" - name: Build CPython + # msvc::musttail is not supported for debug builds, so we have to + # switch to release. run: >- .\\PCbuild\\build.bat - -e -d -v + -e -v + ${{ inputs.interpreter == 'switch-case' && '-d' || '--tail-call-interp -c Release' }} -p "${ARCH}" ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }} shell: bash @@ -45,6 +53,7 @@ jobs: run: >- .\\PCbuild\\rt.bat -p "${ARCH}" - -d -q --fast-ci + -q --fast-ci + ${{ inputs.interpreter == 'switch-case' && '-d' || '' }} ${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }} shell: bash diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index febb2dd823a8fe..37f78519dedc32 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -4,23 +4,27 @@ on: schedule: - cron: "0 */6 * * *" +permissions: + contents: read + jobs: stale: if: github.repository_owner == 'python' runs-on: ubuntu-latest permissions: + actions: write pull-requests: write timeout-minutes: 10 steps: - name: "Check PRs" - uses: actions/stale@v9 + uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-pr-message: 'This PR is stale because it has been open for 30 days with no activity.' + stale-pr-message: 'This PR is stale because it has been open for 90 days with no activity.' stale-pr-label: 'stale' days-before-issue-stale: -1 - days-before-pr-stale: 30 + days-before-pr-stale: 90 days-before-close: -1 ascending: true operations-per-run: 120 diff --git a/.github/workflows/tail-call.yml b/.github/workflows/tail-call.yml index e32cbf0aaa3c3e..656a14906b3cb7 100644 --- a/.github/workflows/tail-call.yml +++ b/.github/workflows/tail-call.yml @@ -1,19 +1,14 @@ name: Tail calling interpreter on: pull_request: - paths: + paths: &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' + paths: *paths workflow_dispatch: permissions: @@ -25,115 +20,73 @@ concurrency: env: FORCE_COLOR: 1 + LLVM_VERSION: 21 jobs: - tail-call: + macos: name: ${{ matrix.target }} runs-on: ${{ matrix.runner }} - timeout-minutes: 90 + timeout-minutes: 60 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 + runner: macos-15-intel - 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 + runner: macos-15 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 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' + - name: Install dependencies run: | brew update - find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete - brew install llvm@${{ matrix.llvm }} + brew install llvm@${{ env.LLVM_VERSION }} + - name: Build + run: | 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 + export PATH="/usr/local/opt/llvm@${{ env.LLVM_VERSION }}/bin:$PATH" + export PATH="/opt/homebrew/opt/llvm@${{ env.LLVM_VERSION }}/bin:$PATH" + CC=clang-${{ env.LLVM_VERSION }} ./configure --with-tail-call-interp make all --jobs 4 + - name: Test + run: | ./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 - - name: Native Linux (debug) - if: runner.os == 'Linux' && matrix.target != 'free-threading' + linux: + name: ${{ matrix.target }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu/gcc + runner: ubuntu-24.04 + configure_flags: --with-pydebug + - target: x86_64-unknown-linux-gnu/gcc-free-threading + runner: ubuntu-24.04 + configure_flags: --disable-gil + - target: aarch64-unknown-linux-gnu/gcc + runner: ubuntu-24.04-arm + configure_flags: --with-pydebug + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.11' + - name: Build 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 + sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ env.LLVM_VERSION }} + export PATH="$(llvm-config-${{ env.LLVM_VERSION }} --bindir):$PATH" + CC=clang-${{ env.LLVM_VERSION }} ./configure --with-tail-call-interp ${{ matrix.configure_flags }} 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' + - name: Test 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 diff --git a/.github/workflows/verify-ensurepip-wheels.yml b/.github/workflows/verify-ensurepip-wheels.yml index 463e7bf3355cc3..cb40f6abc0b3b7 100644 --- a/.github/workflows/verify-ensurepip-wheels.yml +++ b/.github/workflows/verify-ensurepip-wheels.yml @@ -25,10 +25,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3' - name: Compare checksum of bundled wheels to the ones published on PyPI diff --git a/.github/workflows/verify-expat.yml b/.github/workflows/verify-expat.yml new file mode 100644 index 00000000000000..472a11db2da5fb --- /dev/null +++ b/.github/workflows/verify-expat.yml @@ -0,0 +1,32 @@ +name: Verify bundled libexpat + +on: + workflow_dispatch: + push: + paths: + - 'Modules/expat/**' + - '.github/workflows/verify-expat.yml' + pull_request: + paths: + - 'Modules/expat/**' + - '.github/workflows/verify-expat.yml' + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + verify: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Download and verify bundled libexpat files + run: | + ./Modules/expat/refresh.sh + git diff --exit-code Modules/expat/ diff --git a/.github/zizmor.yml b/.github/zizmor.yml index 9b42b47cc85545..7c776d5ea1f941 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -1,10 +1,6 @@ -# Configuration for the zizmor static analysis tool, run via pre-commit in CI -# https://woodruffw.github.io/zizmor/configuration/ +# Configuration for the zizmor static analysis tool, run via prek in CI +# https://docs.zizmor.sh/configuration/ rules: dangerous-triggers: ignore: - documentation-links.yml - unpinned-uses: - config: - policies: - "*": ref-pin diff --git a/.gitignore b/.gitignore index 7aa6272cf8e382..118eb5ee76e805 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.cover *.iml *.o +*.o.tmp *.lto *.a *.so @@ -45,6 +46,7 @@ gmon.out .pytest_cache/ .ruff_cache/ .DS_Store +.pixi/ *.exe @@ -71,16 +73,15 @@ Lib/test/data/* /Makefile /Makefile.pre /iOSTestbed.* -iOS/Frameworks/ -iOS/Resources/Info.plist -iOS/testbed/build -iOS/testbed/Python.xcframework/ios-*/bin -iOS/testbed/Python.xcframework/ios-*/include -iOS/testbed/Python.xcframework/ios-*/lib -iOS/testbed/Python.xcframework/ios-*/Python.framework -iOS/testbed/iOSTestbed.xcodeproj/project.xcworkspace -iOS/testbed/iOSTestbed.xcodeproj/xcuserdata -iOS/testbed/iOSTestbed.xcodeproj/xcshareddata +Apple/iOS/Frameworks/ +Apple/iOS/Resources/Info.plist +Apple/testbed/build +Apple/testbed/Python.xcframework/*/bin +Apple/testbed/Python.xcframework/*/include +Apple/testbed/Python.xcframework/*/lib +Apple/testbed/Python.xcframework/*/Python.framework +Apple/testbed/*Testbed.xcodeproj/project.xcworkspace +Apple/testbed/*Testbed.xcodeproj/xcuserdata Mac/Makefile Mac/PythonLauncher/Info.plist Mac/PythonLauncher/Makefile @@ -136,10 +137,11 @@ Tools/unicode/data/ /config.log /config.status /config.status.lineno -# hendrikmuhs/ccache-action@v1 /.ccache -/cross-build/ +/cross-build*/ /jit_stencils*.h +/jit_unwind_info*.h +.jit-stamp /platform /profile-clean-stamp /profile-run-stamp diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 86410c46d1d707..6878a7d92e3bee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,48 +1,79 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.8 + rev: e05c5c0818279e5ac248ac9e954431ba58865e61 # frozen: v0.15.7 hooks: - - id: ruff + - id: ruff-check + name: Run Ruff (lint) on Platforms/Apple/ + args: [--exit-non-zero-on-fix, --config=Platforms/Apple/.ruff.toml] + files: ^Platforms/Apple/ + - id: ruff-check name: Run Ruff (lint) on Doc/ args: [--exit-non-zero-on-fix] files: ^Doc/ - - id: ruff + - id: ruff-check name: Run Ruff (lint) on Lib/test/ args: [--exit-non-zero-on-fix] files: ^Lib/test/ - - id: ruff + - id: ruff-check + name: Run Ruff (lint) on Platforms/WASI/ + args: [--exit-non-zero-on-fix, --config=Platforms/WASI/.ruff.toml] + files: ^Platforms/WASI/ + - id: ruff-check name: Run Ruff (lint) on Tools/build/ args: [--exit-non-zero-on-fix, --config=Tools/build/.ruff.toml] files: ^Tools/build/ - - id: ruff + - id: ruff-check + name: Run Ruff (lint) on Tools/i18n/ + args: [--exit-non-zero-on-fix, --config=Tools/i18n/.ruff.toml] + files: ^Tools/i18n/ + - id: ruff-check name: Run Ruff (lint) on Argument Clinic args: [--exit-non-zero-on-fix, --config=Tools/clinic/.ruff.toml] files: ^Tools/clinic/|Lib/test/test_clinic.py + - id: ruff-check + name: Run Ruff (lint) on Tools/peg_generator/ + args: [--exit-non-zero-on-fix, --config=Tools/peg_generator/.ruff.toml] + files: ^Tools/peg_generator/ + - id: ruff-check + name: Run Ruff (lint) on Tools/wasm/ + args: [--exit-non-zero-on-fix, --config=Tools/wasm/.ruff.toml] + files: ^Tools/wasm/ + - id: ruff-format + name: Run Ruff (format) on Platforms/Apple/ + args: [--exit-non-zero-on-fix, --config=Platforms/Apple/.ruff.toml] + files: ^Platforms/Apple/ - id: ruff-format name: Run Ruff (format) on Doc/ - args: [--check] + args: [--exit-non-zero-on-fix] files: ^Doc/ + - id: ruff-format + name: Run Ruff (format) on Platforms/WASI/ + args: [--exit-non-zero-on-fix, --config=Platforms/WASI/.ruff.toml] + files: ^Platforms/WASI/ - id: ruff-format name: Run Ruff (format) on Tools/build/check_warnings.py - args: [--check, --config=Tools/build/.ruff.toml] + args: [--exit-non-zero-on-fix, --config=Tools/build/.ruff.toml] files: ^Tools/build/check_warnings.py + - id: ruff-format + name: Run Ruff (format) on Tools/wasm/ + args: [--exit-non-zero-on-fix, --config=Tools/wasm/.ruff.toml] + files: ^Tools/wasm/ - repo: https://github.com/psf/black-pre-commit-mirror - rev: 25.1.0 + rev: ea488cebbfd88a5f50b8bd95d5c829d0bb76feb8 # frozen: 26.1.0 hooks: - id: black name: Run Black on Tools/jit/ files: ^Tools/jit/ - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.5.5 + rev: ad1b27d73581aa16cca06fc4a0761fc563ffe8e8 # frozen: v1.5.6 hooks: - id: remove-tabs types: [python] - exclude: ^Tools/c-analyzer/cpython/_parser.py - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - id: check-case-conflict - id: check-merge-conflict @@ -54,30 +85,33 @@ repos: exclude: Lib/test/tokenizedata/coding20731.py - id: end-of-file-fixer files: '^\.github/CODEOWNERS$' + - id: mixed-line-ending + args: [--fix=auto] + exclude: '^Lib/test/.*data/' - id: trailing-whitespace types_or: [c, inc, python, rst, yaml] - id: trailing-whitespace files: '^\.github/CODEOWNERS|\.(gram)$' - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.33.0 + rev: 9f48a48aa91a6040d749ad68ec70907d907a5a7f # frozen: 0.37.0 hooks: - id: check-dependabot - id: check-github-workflows - id: check-readthedocs - repo: https://github.com/rhysd/actionlint - rev: v1.7.7 + rev: 393031adb9afb225ee52ae2ccd7a5af5525e03e8 # frozen: v1.7.11 hooks: - id: actionlint - - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.6.0 + - repo: https://github.com/zizmorcore/zizmor-pre-commit + rev: b546b77c44c466a54a42af5499dcc0dcc1a3193f # frozen: v1.22.0 hooks: - id: zizmor - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v1.0.0 + rev: c883505f64b59c3c5c9375191e4ad9f98e727ccd # frozen: v1.0.2 hooks: - id: sphinx-lint args: [--enable=default-role] diff --git a/.readthedocs.yml b/.readthedocs.yml index 0a2c3f8345367f..3b8a30c0251873 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -12,23 +12,47 @@ build: tools: python: "3" - commands: - # https://docs.readthedocs.io/en/stable/build-customization.html#cancel-build-based-on-a-condition - # - # Cancel building pull requests when there aren't changes in the Doc directory. - # - # If there are no changes (git diff exits with 0) we force the command to return with 183. - # This is a special exit code on Read the Docs that will cancel the build immediately. - - | - if [ "$READTHEDOCS_VERSION_TYPE" = "external" ] && [ "$(git diff --quiet origin/main -- Doc/ .readthedocs.yml; echo $?)" -eq 0 ]; - then - echo "No changes to Doc/ - exiting the build."; - exit 183; - fi - - - asdf plugin add uv - - asdf install uv latest - - asdf global uv latest - - make -C Doc venv html - - mkdir _readthedocs - - mv Doc/build/html _readthedocs/html + jobs: + post_checkout: + # https://docs.readthedocs.com/platform/stable/guides/build/skip-build.html#skip-builds-based-on-conditions + # + # Cancel building pull requests when there aren't changes in the Doc + # directory or RTD configuration, or if we can't cleanly merge the base + # branch. + - | + set -eEux; + if [ "$READTHEDOCS_VERSION_TYPE" = "external" ]; + then + base_branch=main; + git fetch --depth=50 origin $base_branch:origin-$base_branch; + for attempt in $(seq 10); + do + if ! git merge-base HEAD origin-$base_branch; + then + git fetch --deepen=50 origin $base_branch; + else + break; + fi; + done; + if ! git -c "user.name=rtd" -c "user.email=no-reply@readthedocs.org" merge --no-stat --no-edit origin-$base_branch; + then + echo "Unsuccessful merge with '$base_branch' branch, skipping the build"; + exit 183; + fi; + if git diff --exit-code --stat origin-$base_branch -- Doc/ .readthedocs.yml; + then + echo "No changes to Doc/ - skipping the build."; + exit 183; + fi; + fi; + create_environment: + - echo "Skipping default environment creation" + install: + - asdf plugin add uv + - asdf install uv latest + - asdf global uv latest + build: + html: + - make -C Doc venv html + - mkdir -p "$READTHEDOCS_OUTPUT" + - mv Doc/build/html "$READTHEDOCS_OUTPUT/" diff --git a/.well-known/funding-manifest-urls b/.well-known/funding-manifest-urls new file mode 100644 index 00000000000000..d56ca9eb52f004 --- /dev/null +++ b/.well-known/funding-manifest-urls @@ -0,0 +1 @@ +https://www.python.org/funding.json diff --git a/Android/README.md b/Android/README.md deleted file mode 100644 index c42eb627006e6a..00000000000000 --- a/Android/README.md +++ /dev/null @@ -1,166 +0,0 @@ -# Python for Android - -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 - -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 . -* Create a directory `android-sdk/cmdline-tools`, and unzip the command line - tools package into it. -* Rename `android-sdk/cmdline-tools/cmdline-tools` to - `android-sdk/cmdline-tools/latest`. -* `export ANDROID_HOME=/path/to/android-sdk` - -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) - - -## Building - -Python can be built for Android on any POSIX platform supported by the 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 -you can do it in discrete steps that mirror running `configure` and `make` for -each of the two builds of Python you end up producing. - -The discrete steps for building via `android.py` are: - -```sh -./android.py configure-build -./android.py make-build -./android.py configure-host HOST -./android.py make-host HOST -``` - -`HOST` identifies which architecture to build. To see the possible values, run -`./android.py configure-host --help`. - -To do all steps in a single command, run: - -```sh -./android.py 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` -call. For example, if you want a pydebug build that also caches the results from -`configure`, you can do: - -```sh -./android.py build HOST -- -C --with-pydebug -``` - - -## 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 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. 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 -Studio Koala, 2 GB is the default for all emulators, although the user interface -may indicate otherwise. Locate the emulator's directory under `~/.android/avd`, -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. - -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 - `$ANDROID_HOME/platform-tools/adb devices -l`, then pass a device ID to the - script like this: - - ```sh - ./android.py test --connected emulator-5554 - ``` - -* In `--managed` mode, it uses a temporary headless emulator defined in the - `managedDevices` section of testbed/app/build.gradle.kts. This mode is slower, - but more reproducible. - - We currently define two devices: `minVersion` and `maxVersion`, corresponding - to our minimum and maximum supported Android versions. For example: - - ```sh - ./android.py test --managed maxVersion - ``` - -By default, the only messages the script will show are Python's own stdout and -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. -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, -such as `-j`. - -Every time you run `android.py test`, changes in pure-Python files in the -repository's `Lib` directory will be picked up immediately. Changes in C files, -and architecture-specific files such as sysconfigdata, will not take effect -until you re-run `android.py make-host` or `build`. - -The testbed app can also be used to test third-party packages. For more details, -run `android.py test --help`, paying attention to the options `--site-packages`, -`--cwd`, `-c` and `-m`. - - -## Using in your own app - -See https://docs.python.org/3/using/android.html. diff --git a/Android/android.py b/Android/android.py deleted file mode 100755 index 75f73cd30993da..00000000000000 --- a/Android/android.py +++ /dev/null @@ -1,819 +0,0 @@ -#!/usr/bin/env python3 - -import asyncio -import argparse -import os -import re -import shlex -import shutil -import signal -import subprocess -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 abspath, basename, relpath -from pathlib import Path -from subprocess import CalledProcessError -from tempfile import TemporaryDirectory - - -SCRIPT_NAME = Path(__file__).name -ANDROID_DIR = Path(__file__).resolve().parent -PYTHON_DIR = ANDROID_DIR.parent -in_source_tree = ( - ANDROID_DIR.name == "Android" and (PYTHON_DIR / "pyconfig.h.in").exists() -) - -TESTBED_DIR = ANDROID_DIR / "testbed" -CROSS_BUILD_DIR = PYTHON_DIR / "cross-build" - -HOSTS = ["aarch64-linux-android", "x86_64-linux-android"] -APP_ID = "org.python.testbed" -DECODE_ARGS = ("UTF-8", "backslashreplace") - - -try: - android_home = Path(os.environ['ANDROID_HOME']) -except KeyError: - sys.exit("The ANDROID_HOME environment variable is required.") - -adb = Path( - f"{android_home}/platform-tools/adb" - + (".exe" if os.name == "nt" else "") -) - -gradlew = Path( - f"{TESTBED_DIR}/gradlew" - + (".bat" if os.name == "nt" else "") -) - -# Whether we've seen any output from Python yet. -python_started = False - -# Buffer for verbose output which will be displayed only if a test fails and -# there has been no output from Python. -hidden_output = [] - - -def log_verbose(context, line, stream=sys.stdout): - if context.verbose: - stream.write(line) - else: - hidden_output.append((stream, line)) - - -def delete_glob(pattern): - # Path.glob doesn't accept non-relative patterns. - for path in glob(str(pattern)): - path = Path(path) - print(f"Deleting {path} ...") - if path.is_dir() and not path.is_symlink(): - shutil.rmtree(path) - else: - path.unlink() - - -def subdir(*parts, create=False): - path = CROSS_BUILD_DIR.joinpath(*parts) - if not path.exists(): - if not create: - sys.exit( - f"{path} does not exist. Create it by running the appropriate " - f"`configure` subcommand of {SCRIPT_NAME}.") - else: - path.mkdir(parents=True) - return path - - -def run(command, *, host=None, env=None, log=True, **kwargs): - kwargs.setdefault("check", True) - if env is None: - env = os.environ.copy() - - if host: - host_env = android_env(host) - print_env(host_env) - env.update(host_env) - - if log: - print(">", join_command(command)) - return subprocess.run(command, env=env, **kwargs) - - -# Format a command so it can be copied into a shell. Like shlex.join, but also -# accepts arguments which are Paths, or a single string/Path outside of a list. -def join_command(args): - if isinstance(args, (str, Path)): - return str(args) - else: - return shlex.join(map(str, args)) - - -# Format the environment so it can be pasted into a shell. -def print_env(env): - for key, value in sorted(env.items()): - print(f"export {key}={shlex.quote(value)}") - - -def android_env(host): - if host: - prefix = subdir(host) / "prefix" - else: - prefix = ANDROID_DIR / "prefix" - sysconfig_files = prefix.glob("lib/python*/_sysconfigdata__android_*.py") - sysconfig_filename = next(sysconfig_files).name - host = re.fullmatch(r"_sysconfigdata__android_(.+).py", sysconfig_filename)[1] - - env_script = ANDROID_DIR / "android-env.sh" - env_output = subprocess.run( - f"set -eu; " - f"HOST={host}; " - f"PREFIX={prefix}; " - f". {env_script}; " - f"export", - check=True, shell=True, capture_output=True, encoding='utf-8', - ).stdout - - env = {} - for line in env_output.splitlines(): - # We don't require every line to match, as there may be some other - # output from installing the NDK. - if match := re.search( - "^(declare -x |export )?(\\w+)=['\"]?(.*?)['\"]?$", line - ): - key, value = match[2], match[3] - if os.environ.get(key) != value: - env[key] = value - - if not env: - raise ValueError(f"Found no variables in {env_script.name} output:\n" - + env_output) - return env - - -def build_python_path(): - """The path to the build Python binary.""" - build_dir = subdir("build") - binary = build_dir / "python" - if not binary.is_file(): - binary = binary.with_suffix(".exe") - if not binary.is_file(): - raise FileNotFoundError("Unable to find `python(.exe)` in " - f"{build_dir}") - - return binary - - -def configure_build_python(context): - if context.clean: - clean("build") - os.chdir(subdir("build", create=True)) - - command = [relpath(PYTHON_DIR / "configure")] - if context.args: - command.extend(context.args) - run(command) - - -def make_build_python(context): - os.chdir(subdir("build")) - run(["make", "-j", str(os.cpu_count())]) - - -def unpack_deps(host, prefix_dir): - os.chdir(prefix_dir) - deps_url = "https://github.com/beeware/cpython-android-source-deps/releases/download" - for name_ver in ["bzip2-1.0.8-3", "libffi-3.4.4-3", "openssl-3.0.15-4", - "sqlite-3.49.1-0", "xz-5.4.6-1", "zstd-1.5.7-1"]: - filename = f"{name_ver}-{host}.tar.gz" - download(f"{deps_url}/{name_ver}/{filename}") - shutil.unpack_archive(filename) - os.remove(filename) - - -def download(url, target_dir="."): - out_path = f"{target_dir}/{basename(url)}" - run(["curl", "-Lf", "--retry", "5", "--retry-all-errors", "-o", out_path, url]) - return out_path - - -def configure_host_python(context): - 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() - unpack_deps(context.host, prefix_dir) - - os.chdir(host_dir) - command = [ - # Basic cross-compiling configuration - relpath(PYTHON_DIR / "configure"), - f"--host={context.host}", - f"--build={sysconfig.get_config_var('BUILD_GNU_TYPE')}", - f"--with-build-python={build_python_path()}", - "--without-ensurepip", - - # Android always uses a shared libpython. - "--enable-shared", - "--without-static-libpython", - - # Dependent libraries. The others are found using pkg-config: see - # android-env.sh. - f"--with-openssl={prefix_dir}", - ] - - if context.args: - command.extend(context.args) - run(command, host=context.host) - - -def make_host_python(context): - # The CFLAGS and LDFLAGS set in android-env include the prefix dir, so - # delete any previous Python installation to prevent it being used during - # the build. - host_dir = subdir(context.host) - prefix_dir = host_dir / "prefix" - for pattern in ("include/python*", "lib/libpython*", "lib/python*"): - delete_glob(f"{prefix_dir}/{pattern}") - - # The Android environment variables were already captured in the Makefile by - # `configure`, and passing them again when running `make` may cause some - # flags to be duplicated. So we don't use the `host` argument here. - os.chdir(host_dir) - run(["make", "-j", str(os.cpu_count())]) - run(["make", "install", f"prefix={prefix_dir}"]) - - -def build_all(context): - steps = [configure_build_python, make_build_python, configure_host_python, - make_host_python] - for step in steps: - step(context) - - -def clean(host): - delete_glob(CROSS_BUILD_DIR / host) - - -def clean_all(context): - for host in HOSTS + ["build"]: - clean(host) - - -def setup_sdk(): - sdkmanager = android_home / ( - "cmdline-tools/latest/bin/sdkmanager" - + (".bat" if os.name == "nt" else "") - ) - - # Gradle will fail if it needs to install an SDK package whose license - # hasn't been accepted, so pre-accept all licenses. - if not all((android_home / "licenses" / path).exists() for path in [ - "android-sdk-arm-dbt-license", "android-sdk-license" - ]): - run( - [sdkmanager, "--licenses"], - text=True, - capture_output=True, - input="y\n" * 100, - ) - - # Gradle may install this automatically, but we can't rely on that because - # we need to run adb within the logcat task. - if not adb.exists(): - run([sdkmanager, "platform-tools"]) - - -# 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 GitHub repository. -def setup_testbed(): - paths = ["gradlew", "gradlew.bat", "gradle/wrapper/gradle-wrapper.jar"] - if all((TESTBED_DIR / path).exists() for path in paths): - return - - # 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 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) - - -# 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. -def build_testbed(context): - setup_sdk() - setup_testbed() - run( - [gradlew, "--console", "plain", "packageDebug", "packageDebugAndroidTest"], - cwd=TESTBED_DIR, - ) - - -# Work around a bug involving sys.exit and TaskGroups -# (https://github.com/python/cpython/issues/101515). -def exit(*args): - raise MySystemExit(*args) - - -class MySystemExit(Exception): - pass - - -# The `test` subcommand runs all subprocesses through this context manager so -# that no matter what happens, they can always be cancelled from another task, -# and they will always be cleaned up on exit. -@asynccontextmanager -async def async_process(*args, **kwargs): - process = await asyncio.create_subprocess_exec(*args, **kwargs) - try: - yield process - finally: - if process.returncode is None: - # Allow a reasonably long time for Gradle to clean itself up, - # because we don't want stale emulators left behind. - timeout = 10 - process.terminate() - try: - await wait_for(process.wait(), timeout) - except TimeoutError: - print( - f"Command {args} did not terminate after {timeout} seconds " - f" - sending SIGKILL" - ) - process.kill() - - # Even after killing the process we must still wait for it, - # otherwise we'll get the warning "Exception ignored in __del__". - await wait_for(process.wait(), timeout=1) - - -async def async_check_output(*args, **kwargs): - async with async_process( - *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs - ) as process: - stdout, stderr = await process.communicate() - if process.returncode == 0: - return stdout.decode(*DECODE_ARGS) - else: - raise CalledProcessError( - process.returncode, args, - stdout.decode(*DECODE_ARGS), stderr.decode(*DECODE_ARGS) - ) - - -# Return a list of the serial numbers of connected devices. Emulators will have -# serials of the form "emulator-5678". -async def list_devices(): - serials = [] - header_found = False - - lines = (await async_check_output(adb, "devices")).splitlines() - for line in lines: - # Ignore blank lines, and all lines before the header. - line = line.strip() - if line == "List of devices attached": - header_found = True - elif header_found and line: - try: - serial, status = line.split() - except ValueError: - raise ValueError(f"failed to parse {line!r}") - if status == "device": - serials.append(serial) - - if not header_found: - raise ValueError(f"failed to parse {lines}") - return serials - - -async def find_device(context, initial_devices): - if context.managed: - print("Waiting for managed device - this may take several minutes") - while True: - new_devices = set(await list_devices()).difference(initial_devices) - if len(new_devices) == 0: - await asyncio.sleep(1) - elif len(new_devices) == 1: - serial = new_devices.pop() - print(f"Serial: {serial}") - return serial - else: - exit(f"Found more than one new device: {new_devices}") - else: - return context.connected - - -# An older version of this script in #121595 filtered the logs by UID instead. -# But logcat can't filter by UID until API level 31. If we ever switch back to -# filtering by UID, we'll also have to filter by time so we only show messages -# produced after the initial call to `stop_app`. -# -# We're more likely to miss the PID because it's shorter-lived, so there's a -# workaround in PythonSuite.kt to stop it being *too* short-lived. -async def find_pid(serial): - print("Waiting for app to start - this may take several minutes") - shown_error = False - while True: - try: - # `pidof` requires API level 24 or higher. The level 23 emulator - # includes it, but it doesn't work (it returns all processes). - pid = (await async_check_output( - adb, "-s", serial, "shell", "pidof", "-s", APP_ID - )).strip() - except CalledProcessError as e: - # If the app isn't running yet, pidof gives no output. So if there - # is output, there must have been some other error. However, this - # sometimes happens transiently, especially when running a managed - # emulator for the first time, so don't make it fatal. - if (e.stdout or e.stderr) and not shown_error: - print_called_process_error(e) - print("This may be transient, so continuing to wait") - shown_error = True - else: - # Some older devices (e.g. Nexus 4) return zero even when no process - # was found, so check whether we actually got any output. - if pid: - print(f"PID: {pid}") - return pid - - # Loop fairly rapidly to avoid missing a short-lived process. - await asyncio.sleep(0.2) - - -async def logcat_task(context, initial_devices): - # Gradle may need to do some large downloads of libraries and emulator - # images. This will happen during find_device in --managed mode, or find_pid - # in --connected mode. - startup_timeout = 600 - serial = await wait_for(find_device(context, initial_devices), startup_timeout) - pid = await wait_for(find_pid(serial), startup_timeout) - - # `--pid` requires API level 24 or higher. - args = [adb, "-s", serial, "logcat", "--pid", pid, "--format", "tag"] - logcat_started = False - async with async_process( - *args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - ) as process: - while line := (await process.stdout.readline()).decode(*DECODE_ARGS): - if match := re.fullmatch(r"([A-Z])/(.*)", line, re.DOTALL): - logcat_started = True - level, message = match.groups() - else: - # If the regex doesn't match, this is either a logcat startup - # error, or the second or subsequent line of a multi-line - # message. Python won't produce multi-line messages, but other - # components might. - level, message = None, line - - # Exclude high-volume messages which are rarely useful. - if context.verbose < 2 and "from python test_syslog" in message: - continue - - # Put high-level messages on stderr so they're highlighted in the - # buildbot logs. This will include Python's own stderr. - stream = ( - sys.stderr - if level in ["W", "E", "F"] # WARNING, ERROR, FATAL (aka ASSERT) - else sys.stdout - ) - - # To simplify automated processing of the output, e.g. a buildbot - # posting a failure notice on a GitHub PR, we strip the level and - # tag indicators from Python's stdout and stderr. - for prefix in ["python.stdout: ", "python.stderr: "]: - if message.startswith(prefix): - global python_started - python_started = True - stream.write(message.removeprefix(prefix)) - break - else: - # Non-Python messages add a lot of noise, but they may - # sometimes help explain a failure. - log_verbose(context, line, stream) - - # If the device disconnects while logcat is running, which always - # happens in --managed mode, some versions of adb return non-zero. - # Distinguish this from a logcat startup error by checking whether we've - # received any logcat messages yet. - status = await wait_for(process.wait(), timeout=1) - if status != 0 and not logcat_started: - raise CalledProcessError(status, args) - - -def stop_app(serial): - run([adb, "-s", serial, "shell", "am", "force-stop", APP_ID], log=False) - - -async def gradle_task(context): - env = os.environ.copy() - if context.managed: - task_prefix = context.managed - else: - task_prefix = "connected" - env["ANDROID_SERIAL"] = context.connected - - if context.command: - mode = "-c" - module = context.command - else: - mode = "-m" - module = context.module or "test" - - args = [ - gradlew, "--console", "plain", f"{task_prefix}DebugAndroidTest", - ] + [ - # Build-time properties - f"-Ppython.{name}={value}" - for name, value in [ - ("sitePackages", context.site_packages), ("cwd", context.cwd) - ] if value - ] + [ - # Runtime properties - f"-Pandroid.testInstrumentationRunnerArguments.python{name}={value}" - for name, value in [ - ("Mode", mode), ("Module", module), ("Args", join_command(context.args)) - ] if value - ] - if context.verbose >= 2: - args.append("--info") - log_verbose(context, f"> {join_command(args)}\n") - - try: - async with async_process( - *args, cwd=TESTBED_DIR, env=env, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - ) as process: - while line := (await process.stdout.readline()).decode(*DECODE_ARGS): - # Gradle may take several minutes to install SDK packages, so - # it's worth showing those messages even in non-verbose mode. - if line.startswith('Preparing "Install'): - sys.stdout.write(line) - else: - log_verbose(context, line) - - status = await wait_for(process.wait(), timeout=1) - if status == 0: - exit(0) - else: - raise CalledProcessError(status, args) - finally: - # Gradle does not stop the tests when interrupted. - if context.connected: - stop_app(context.connected) - - -async def run_testbed(context): - setup_sdk() - setup_testbed() - - if context.managed: - # In this mode, Gradle will create a device with an unpredictable name. - # So we save a list of the running devices before starting Gradle, and - # find_device then waits for a new device to appear. - initial_devices = await list_devices() - else: - # In case the previous shutdown was unclean, make sure the app isn't - # running, otherwise we might show logs from a previous run. This is - # unnecessary in --managed mode, because Gradle creates a new emulator - # every time. - stop_app(context.connected) - initial_devices = None - - try: - async with asyncio.TaskGroup() as tg: - tg.create_task(logcat_task(context, initial_devices)) - tg.create_task(gradle_task(context)) - except* MySystemExit as e: - raise SystemExit(*e.exceptions[0].args) from None - except* CalledProcessError as e: - # If Python produced no output, then the user probably wants to see the - # verbose output to explain why the test failed. - if not python_started: - for stream, line in hidden_output: - stream.write(line) - - # Extract it from the ExceptionGroup so it can be handled by `main`. - 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}") - - -def env(context): - print_env(android_env(getattr(context, "host", None))) - - -# 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(): - def signal_handler(*args): - os.kill(os.getpid(), signal.SIGINT) - - signal.signal(signal.SIGTERM, signal_handler) - - -def parse_args(): - parser = argparse.ArgumentParser() - subcommands = parser.add_subparsers(dest="subcommand", required=True) - - # Subcommands - build = subcommands.add_parser( - "build", help="Run configure-build, make-build, configure-host and " - "make-host") - configure_build = subcommands.add_parser( - "configure-build", help="Run `configure` for the build Python") - subcommands.add_parser( - "make-build", help="Run `make` for the build Python") - configure_host = subcommands.add_parser( - "configure-host", help="Run `configure` for Android") - make_host = subcommands.add_parser( - "make-host", help="Run `make` for Android") - - subcommands.add_parser("clean", help="Delete all build directories") - subcommands.add_parser("build-testbed", help="Build the testbed app") - test = subcommands.add_parser("test", help="Run the testbed app") - package = subcommands.add_parser("package", help="Make a release package") - env = subcommands.add_parser("env", help="Print environment variables") - - # Common arguments - for subcommand in build, configure_build, configure_host: - subcommand.add_argument( - "--clean", action="store_true", default=False, dest="clean", - help="Delete the relevant build directories first") - - host_commands = [build, configure_host, make_host, package] - if in_source_tree: - host_commands.append(env) - for subcommand in host_commands: - subcommand.add_argument( - "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`") - - # Test arguments - test.add_argument( - "-v", "--verbose", action="count", default=0, - help="Show Gradle output, and non-Python logcat messages. " - "Use twice to include high-volume messages which are rarely useful.") - - device_group = test.add_mutually_exclusive_group(required=True) - device_group.add_argument( - "--connected", metavar="SERIAL", help="Run on a connected device. " - "Connect it yourself, then get its serial from `adb devices`.") - device_group.add_argument( - "--managed", metavar="NAME", help="Run on a Gradle-managed device. " - "These are defined in `managedDevices` in testbed/app/build.gradle.kts.") - - test.add_argument( - "--site-packages", metavar="DIR", type=abspath, - help="Directory to copy as the app's site-packages.") - test.add_argument( - "--cwd", metavar="DIR", type=abspath, - help="Directory to copy as the app's working directory.") - - mode_group = test.add_mutually_exclusive_group() - mode_group.add_argument( - "-c", dest="command", help="Execute the given Python code.") - mode_group.add_argument( - "-m", dest="module", help="Execute the module with the given name.") - test.epilog = ( - "If neither -c nor -m are passed, the default is '-m test', which will " - "run Python's own test suite.") - test.add_argument( - "args", nargs="*", help=f"Arguments to add to sys.argv. " - f"Separate them from {SCRIPT_NAME}'s own arguments with `--`.") - - return parser.parse_args() - - -def main(): - install_signal_handler() - - # Under the buildbot, stdout is not a TTY, but we must still flush after - # every line to make sure our output appears in the correct order relative - # to the output of our subprocesses. - for stream in [sys.stdout, sys.stderr]: - 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, - "package": package, - "env": env, - } - - try: - result = dispatch[context.subcommand](context) - if asyncio.iscoroutine(result): - asyncio.run(result) - except CalledProcessError as e: - print_called_process_error(e) - sys.exit(1) - - -def print_called_process_error(e): - for stream_name in ["stdout", "stderr"]: - content = getattr(e, stream_name) - stream = getattr(sys, stream_name) - if content: - stream.write(content) - if not content.endswith("\n"): - stream.write("\n") - - # shlex uses single quotes, so we surround the command with double quotes. - print( - f'Command "{join_command(e.cmd)}" returned exit status {e.returncode}' - ) - - -if __name__ == "__main__": - main() diff --git a/Android/testbed/app/build.gradle.kts b/Android/testbed/app/build.gradle.kts deleted file mode 100644 index 92cffd61f86876..00000000000000 --- a/Android/testbed/app/build.gradle.kts +++ /dev/null @@ -1,268 +0,0 @@ -import com.android.build.api.variant.* -import kotlin.math.max - -plugins { - id("com.android.application") - id("org.jetbrains.kotlin.android") -} - -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 KNOWN_ABIS = mapOf( - "aarch64-linux-android" to "arm64-v8a", - "x86_64-linux-android" to "x86_64", -) - -// Discover prefixes. -val prefixes = ArrayList() -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 prefixes found: see README.md for testing instructions" - ) -} - -// Detect Python versions and ABIs. -lateinit var pythonVersion: String -var abis = HashMap() -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") - } - 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]!! -} - - -android { - val androidEnvFile = file("../../android-env.sh").absoluteFile - - namespace = "org.python.testbed" - compileSdk = 34 - - defaultConfig { - applicationId = "org.python.testbed" - - minSdk = androidEnvFile.useLines { - for (line in it) { - """ANDROID_API_LEVEL:=(\d+)""".toRegex().find(line)?.let { - return@useLines it.groupValues[1].toInt() - } - } - throw GradleException("Failed to find API level in $androidEnvFile") - } - targetSdk = 34 - - versionCode = 1 - versionName = "1.0" - - ndk.abiFilters.addAll(abis.values) - externalNativeBuild.cmake.arguments( - "-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", - ) - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - ndkVersion = androidEnvFile.useLines { - for (line in it) { - """ndk_version=(\S+)""".toRegex().find(line)?.let { - return@useLines it.groupValues[1] - } - } - throw GradleException("Failed to find NDK version in $androidEnvFile") - } - externalNativeBuild.cmake { - path("src/main/c/CMakeLists.txt") - } - - // Set this property to something non-empty, otherwise it'll use the default - // list, which ignores asset directories beginning with an underscore. - aaptOptions.ignoreAssetsPattern = ".git" - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = "1.8" - } - - testOptions { - managedDevices { - localDevices { - create("minVersion") { - device = "Small Phone" - - // Managed devices have a minimum API level of 27. - apiLevel = max(27, defaultConfig.minSdk!!) - - // ATD devices are smaller and faster, but have a minimum - // API level of 30. - systemImageSource = if (apiLevel >= 30) "aosp-atd" else "aosp" - } - - create("maxVersion") { - device = "Small Phone" - apiLevel = defaultConfig.targetSdk!! - systemImageSource = "aosp-atd" - } - } - - // If the previous test run succeeded and nothing has changed, - // Gradle thinks there's no need to run it again. Override that. - afterEvaluate { - (localDevices.names + listOf("connected")).forEach { - tasks.named("${it}DebugAndroidTest") { - outputs.upToDateWhen { false } - } - } - } - } - } -} - -dependencies { - implementation("androidx.appcompat:appcompat:1.6.1") - implementation("com.google.android.material:material:1.11.0") - implementation("androidx.constraintlayout:constraintlayout:2.1.4") - androidTestImplementation("androidx.test.ext:junit:1.1.5") - androidTestImplementation("androidx.test:rules:1.5.0") -} - - -// Create some custom tasks to copy Python and its standard library from -// elsewhere in the repository. -androidComponents.onVariants { variant -> - 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 (prefix in prefixes) { - from("$prefix/include/$pyPlusVer") - } - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - } - - into("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") { - from("$projectDir/src/main/python") - - val sitePackages = findProperty("python.sitePackages") as String? - if (!sitePackages.isNullOrEmpty()) { - if (!file(sitePackages).exists()) { - throw GradleException("$sitePackages does not exist") - } - from(sitePackages) - } - } - - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - exclude("**/__pycache__") - } - - into("cwd") { - val cwd = findProperty("python.cwd") as String? - if (!cwd.isNullOrEmpty()) { - if (!file(cwd).exists()) { - throw GradleException("$cwd does not exist") - } - from(cwd) - } - } - } - } - - generateTask(variant, variant.sources.jniLibs!!) { - for ((prefix, abi) in abis.entries) { - into(abi) { - from("$prefix/lib") - include("libpython*.*.so") - include("lib*_python.so") - } - } - } -} - - -fun generateTask( - variant: ApplicationVariant, directories: SourceDirectories, - configure: GenerateTask.() -> Unit -) { - val taskName = "generate" + - listOf(variant.name, "Python", directories.name) - .map { it.replaceFirstChar(Char::uppercase) } - .joinToString("") - - directories.addGeneratedSourceDirectory( - tasks.register(taskName) { - into(outputDir) - configure() - }, - GenerateTask::outputDir) -} - - -// addGeneratedSourceDirectory requires the task to have a DirectoryProperty. -abstract class GenerateTask: Sync() { - @get:OutputDirectory - abstract val outputDir: DirectoryProperty -} diff --git a/Android/testbed/app/src/main/c/main_activity.c b/Android/testbed/app/src/main/c/main_activity.c deleted file mode 100644 index ec7f93a3e5ee13..00000000000000 --- a/Android/testbed/app/src/main/c/main_activity.c +++ /dev/null @@ -1,152 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - - -static void throw_runtime_exception(JNIEnv *env, const char *message) { - (*env)->ThrowNew( - env, - (*env)->FindClass(env, "java/lang/RuntimeException"), - message); -} - - -// --- Stdio redirection ------------------------------------------------------ - -// Most apps won't need this, because the Python-level sys.stdout and sys.stderr -// are redirected to the Android logcat by Python itself. However, in the -// testbed it's useful to redirect the native streams as well, to debug problems -// in the Python startup or redirection process. -// -// Based on -// https://github.com/beeware/briefcase-android-gradle-template/blob/v0.3.11/%7B%7B%20cookiecutter.safe_formal_name%20%7D%7D/app/src/main/cpp/native-lib.cpp - -typedef struct { - FILE *file; - int fd; - android_LogPriority priority; - char *tag; - int pipe[2]; -} StreamInfo; - -// The FILE member can't be initialized here because stdout and stderr are not -// compile-time constants. Instead, it's initialized immediately before the -// redirection. -static StreamInfo STREAMS[] = { - {NULL, STDOUT_FILENO, ANDROID_LOG_INFO, "native.stdout", {-1, -1}}, - {NULL, STDERR_FILENO, ANDROID_LOG_WARN, "native.stderr", {-1, -1}}, - {NULL, -1, ANDROID_LOG_UNKNOWN, NULL, {-1, -1}}, -}; - -// The maximum length of a log message in bytes, including the level marker and -// tag, is defined as LOGGER_ENTRY_MAX_PAYLOAD in -// platform/system/logging/liblog/include/log/log.h. As of API level 30, messages -// longer than this will be be truncated by logcat. This limit has already been -// reduced at least once in the history of Android (from 4076 to 4068 between API -// level 23 and 26), so leave some headroom. -static const int MAX_BYTES_PER_WRITE = 4000; - -static void *redirection_thread(void *arg) { - StreamInfo *si = (StreamInfo*)arg; - ssize_t read_size; - char buf[MAX_BYTES_PER_WRITE]; - while ((read_size = read(si->pipe[0], buf, sizeof buf - 1)) > 0) { - buf[read_size] = '\0'; /* add null-terminator */ - __android_log_write(si->priority, si->tag, buf); - } - return 0; -} - -static char *redirect_stream(StreamInfo *si) { - /* make the FILE unbuffered, to ensure messages are never lost */ - if (setvbuf(si->file, 0, _IONBF, 0)) { - return "setvbuf"; - } - - /* create the pipe and redirect the file descriptor */ - if (pipe(si->pipe)) { - return "pipe"; - } - if (dup2(si->pipe[1], si->fd) == -1) { - return "dup2"; - } - - /* start the logging thread */ - pthread_t thr; - if ((errno = pthread_create(&thr, 0, redirection_thread, si))) { - return "pthread_create"; - } - if ((errno = pthread_detach(thr))) { - return "pthread_detach"; - } - return 0; -} - -JNIEXPORT void JNICALL Java_org_python_testbed_PythonTestRunner_redirectStdioToLogcat( - JNIEnv *env, jobject obj -) { - STREAMS[0].file = stdout; - STREAMS[1].file = stderr; - for (StreamInfo *si = STREAMS; si->file; si++) { - char *error_prefix; - if ((error_prefix = redirect_stream(si))) { - char error_message[1024]; - snprintf(error_message, sizeof(error_message), - "%s: %s", error_prefix, strerror(errno)); - throw_runtime_exception(env, error_message); - return; - } - } -} - - -// --- Python initialization --------------------------------------------------- - -static PyStatus set_config_string( - JNIEnv *env, PyConfig *config, wchar_t **config_str, jstring value -) { - const char *value_utf8 = (*env)->GetStringUTFChars(env, value, NULL); - PyStatus status = PyConfig_SetBytesString(config, config_str, value_utf8); - (*env)->ReleaseStringUTFChars(env, value, value_utf8); - return status; -} - -static void throw_status(JNIEnv *env, PyStatus status) { - throw_runtime_exception(env, status.err_msg ? status.err_msg : ""); -} - -JNIEXPORT int JNICALL Java_org_python_testbed_PythonTestRunner_runPython( - JNIEnv *env, jobject obj, jstring home, jstring runModule -) { - PyConfig config; - PyStatus status; - PyConfig_InitIsolatedConfig(&config); - - status = set_config_string(env, &config, &config.home, home); - if (PyStatus_Exception(status)) { - throw_status(env, status); - return 1; - } - - status = set_config_string(env, &config, &config.run_module, runModule); - if (PyStatus_Exception(status)) { - throw_status(env, status); - return 1; - } - - // Some tests generate SIGPIPE and SIGXFSZ, which should be ignored. - config.install_signal_handlers = 1; - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - throw_status(env, status); - return 1; - } - - return Py_RunMain(); -} diff --git a/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt b/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt deleted file mode 100644 index ef28948486fb52..00000000000000 --- a/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.python.testbed - -import android.content.Context -import android.os.* -import android.system.Os -import android.widget.TextView -import androidx.appcompat.app.* -import java.io.* - - -// Launching the tests from an activity is OK for a quick check, but for -// anything more complicated it'll be more convenient to use `android.py test` -// to launch the tests via PythonSuite. -class MainActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - val status = PythonTestRunner(this).run("-m", "test", "-W -uall") - findViewById(R.id.tvHello).text = "Exit status $status" - } -} - - -class PythonTestRunner(val context: Context) { - fun run(instrumentationArgs: Bundle) = run( - instrumentationArgs.getString("pythonMode")!!, - instrumentationArgs.getString("pythonModule")!!, - instrumentationArgs.getString("pythonArgs") ?: "", - ) - - /** Run Python. - * - * @param mode Either "-c" or "-m". - * @param module Python statements for "-c" mode, or a module name for - * "-m" mode. - * @param args Arguments to add to sys.argv. Will be parsed by `shlex.split`. - * @return The Python exit status: zero on success, nonzero on failure. */ - fun run(mode: String, module: String, args: String) : Int { - Os.setenv("PYTHON_MODE", mode, true) - Os.setenv("PYTHON_MODULE", module, true) - Os.setenv("PYTHON_ARGS", args, true) - - // Python needs this variable to help it find the temporary directory, - // but Android only sets it on API level 33 and later. - Os.setenv("TMPDIR", context.cacheDir.toString(), false) - - val pythonHome = extractAssets() - System.loadLibrary("main_activity") - redirectStdioToLogcat() - - // The main module is in src/main/python. We don't simply call it - // "main", as that could clash with third-party test code. - return runPython(pythonHome.toString(), "android_testbed_main") - } - - private fun extractAssets() : File { - val pythonHome = File(context.filesDir, "python") - if (pythonHome.exists() && !pythonHome.deleteRecursively()) { - throw RuntimeException("Failed to delete $pythonHome") - } - extractAssetDir("python", context.filesDir) - return pythonHome - } - - private fun extractAssetDir(path: String, targetDir: File) { - val names = context.assets.list(path) - ?: throw RuntimeException("Failed to list $path") - val targetSubdir = File(targetDir, path) - if (!targetSubdir.mkdirs()) { - throw RuntimeException("Failed to create $targetSubdir") - } - - for (name in names) { - val subPath = "$path/$name" - val input: InputStream - try { - input = context.assets.open(subPath) - } catch (e: FileNotFoundException) { - extractAssetDir(subPath, targetDir) - continue - } - input.use { - File(targetSubdir, name).outputStream().use { output -> - input.copyTo(output) - } - } - } - } - - private external fun redirectStdioToLogcat() - private external fun runPython(home: String, runModule: String) : Int -} diff --git a/Android/testbed/app/src/main/python/android_testbed_main.py b/Android/testbed/app/src/main/python/android_testbed_main.py deleted file mode 100644 index 31b8e5343a8449..00000000000000 --- a/Android/testbed/app/src/main/python/android_testbed_main.py +++ /dev/null @@ -1,48 +0,0 @@ -import os -import runpy -import shlex -import signal -import sys - -# Some tests use SIGUSR1, but that's blocked by default in an Android app in -# order to make it available to `sigwait` in the Signal Catcher thread. -# (https://cs.android.com/android/platform/superproject/+/android14-qpr3-release:art/runtime/signal_catcher.cc). -# That thread's functionality is only useful for debugging the JVM, so disabling -# it should not weaken the tests. -# -# There's no safe way of stopping the thread completely (#123982), but simply -# unblocking SIGUSR1 is enough to fix most tests. -# -# However, in tests that generate multiple different signals in quick -# succession, it's possible for SIGUSR1 to arrive while the main thread is busy -# running the C-level handler for a different signal. In that case, the SIGUSR1 -# may be sent to the Signal Catcher thread instead, which will generate a log -# message containing the text "reacting to signal". -# -# Such tests may need to be changed in one of the following ways: -# * Use a signal other than SIGUSR1 (e.g. test_stress_delivery_simultaneous in -# test_signal.py). -# * Send the signal to a specific thread rather than the whole process (e.g. -# test_signals in test_threadsignals.py. -signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGUSR1]) - -mode = os.environ["PYTHON_MODE"] -module = os.environ["PYTHON_MODULE"] -sys.argv[1:] = shlex.split(os.environ["PYTHON_ARGS"]) - -cwd = f"{sys.prefix}/cwd" -if not os.path.exists(cwd): - # Empty directories are lost in the asset packing/unpacking process. - os.mkdir(cwd) -os.chdir(cwd) - -if mode == "-c": - # In -c mode, sys.path starts with an empty string, which means whatever the current - # working directory is at the moment of each import. - sys.path.insert(0, "") - exec(module, {}) -elif mode == "-m": - sys.path.insert(0, os.getcwd()) - runpy.run_module(module, run_name="__main__", alter_sys=True) -else: - raise ValueError(f"unknown mode: {mode}") diff --git a/Doc/.ruff.toml b/Doc/.ruff.toml index 3e676e13c3f41a..6b573fd58d089b 100644 --- a/Doc/.ruff.toml +++ b/Doc/.ruff.toml @@ -32,6 +32,9 @@ ignore = [ "E501", # Ignore line length errors (we use auto-formatting) ] +[lint.per-file-ignores] +"tools/check-html-ids.py" = ["I001"] # Unsorted imports + [format] preview = true quote-style = "preserve" diff --git a/Doc/Makefile b/Doc/Makefile index 84578c5c57f478..d77ece1e681bcf 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -13,7 +13,7 @@ JOBS = auto PAPER = SOURCES = DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py) -REQUIREMENTS = requirements.txt +REQUIREMENTS = pylock.toml SPHINXERRORHANDLING = --fail-on-warning # Internal variables. @@ -58,7 +58,7 @@ build: @if [ -f ../Misc/NEWS ] ; then \ echo "Using existing Misc/NEWS file"; \ cp ../Misc/NEWS build/NEWS; \ - elif $(BLURB) help >/dev/null 2>&1 && $(SPHINXBUILD) --version >/dev/null 2>&1; then \ + elif $(BLURB) --version && $(SPHINXBUILD) --version ; then \ if [ -d ../Misc/NEWS.d ]; then \ echo "Building NEWS from Misc/NEWS.d with blurb"; \ $(BLURB) merge -f build/NEWS; \ @@ -89,7 +89,8 @@ htmlhelp: build .PHONY: latex latex: BUILDER = latex -latex: build +latex: _ensure-sphinxcontrib-svg2pdfconverter + $(MAKE) build BUILDER=$(BUILDER) @echo "Build finished; the LaTeX files are in build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." @@ -140,7 +141,8 @@ doctest: pydoc-topics: BUILDER = pydoc-topics pydoc-topics: build @echo "Building finished; now run this:" \ - "cp build/pydoc-topics/topics.py ../Lib/pydoc_data/topics.py" + "cp build/pydoc-topics/topics.py ../Lib/pydoc_data/topics.py" \ + "&& cp build/pydoc-topics/module_docs.py ../Lib/pydoc_data/module_docs.py" .PHONY: gettext gettext: BUILDER = gettext @@ -184,7 +186,7 @@ venv: fi .PHONY: dist-no-html -dist-no-html: dist-text dist-pdf dist-epub dist-texinfo +dist-no-html: dist-text dist-epub dist-texinfo .PHONY: dist dist: @@ -230,7 +232,7 @@ dist-text: @echo "Build finished and archived!" .PHONY: dist-pdf -dist-pdf: +dist-pdf: _ensure-sphinxcontrib-svg2pdfconverter # archive the A4 latex @echo "Building LaTeX (A4 paper)..." mkdir -p dist @@ -241,7 +243,8 @@ dist-pdf: # as otherwise the full latexmk process is run twice. # ($$ is needed to escape the $; https://www.gnu.org/software/make/manual/make.html#Basics-of-Variable-References) -sed -i 's/: all-$$(FMT)/:/' build/latex/Makefile - (cd build/latex; $(MAKE) clean && $(MAKE) --jobs=$$((`nproc`+1)) --output-sync LATEXMKOPTS='-quiet' all-pdf && $(MAKE) FMT=pdf zip bz2) + if [ -n "$(filter output-sync,$(value .FEATURES))" ]; then OUTPUTSYNC=--output-sync; else OUTPUTSYNC=; fi && \ + (cd build/latex; $(MAKE) clean && $(MAKE) --jobs=$$((`getconf _NPROCESSORS_ONLN`+1)) $$OUTPUTSYNC LATEXMKOPTS='-quiet' all-pdf && $(MAKE) FMT=pdf zip bz2) cp build/latex/docs-pdf.zip dist/python-$(DISTVERSION)-docs-pdf-a4.zip cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-a4.tar.bz2 @echo "Build finished and archived!" @@ -290,6 +293,10 @@ _ensure-pre-commit: _ensure-sphinx-autobuild: $(MAKE) _ensure-package PACKAGE=sphinx-autobuild +.PHONY: _ensure-sphinxcontrib-svg2pdfconverter +_ensure-sphinxcontrib-svg2pdfconverter: + $(MAKE) _ensure-package PACKAGE=sphinxcontrib-svg2pdfconverter + .PHONY: check check: _ensure-pre-commit $(VENVDIR)/bin/python3 -m pre_commit run --all-files @@ -334,3 +341,9 @@ autobuild-stable-html: exit 1;; \ esac @$(MAKE) autobuild-dev-html + +# Collect HTML IDs to a JSON document +.PHONY: html-ids +html-ids: + $(PYTHON) tools/check-html-ids.py collect build/html \ + -o build/html/html-ids.json.gz diff --git a/Doc/_static/profiling-sampling-visualization.css b/Doc/_static/profiling-sampling-visualization.css new file mode 100644 index 00000000000000..6bfbec3b8a6044 --- /dev/null +++ b/Doc/_static/profiling-sampling-visualization.css @@ -0,0 +1,570 @@ +/** + * Sampling Profiler Visualization - Scoped CSS + */ + +.sampling-profiler-viz { + /* Match docs background colors */ + --bg-page: #ffffff; + --bg-panel: #ffffff; + --bg-subtle: #f8f8f8; + --bg-code: #f8f8f8; + + /* Match docs border style */ + --border-color: #e1e4e8; + --border-accent: #3776ab; + + /* Match docs text colors */ + --text-primary: #0d0d0d; + --text-secondary: #505050; + --text-muted: #6e6e6e; + --text-code: #333333; + + /* Accent colors */ + --color-python-blue: #306998; + --color-green: #388e3c; + --color-orange: #e65100; + --color-purple: #7b1fa2; + --color-red: #c62828; + --color-teal: #00897b; + --color-yellow: #d4a910; + --color-highlight: #fff9e6; + + --radius-lg: 8px; + --radius-md: 6px; + --radius-sm: 4px; + + /* Lighter shadows to match docs style */ + --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.08); + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04); + + --container-height: 520px; + --code-panel-width: 320px; + + /* Reset for isolation */ + font-family: var(--font-ui); + line-height: 1.5; + font-weight: 400; + color: var(--text-primary); + background-color: var(--bg-page); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Layout */ + position: relative; + width: 100%; + max-width: 920px; + height: var(--container-height); + display: grid; + grid-template-columns: var(--code-panel-width) 1fr; + margin: 24px auto; + border-radius: var(--radius-lg); + overflow: hidden; + box-shadow: var(--shadow-card); + border: 1px solid var(--border-color); + background: var(--bg-panel); + /* Prevent any DOM changes inside from affecting page scroll */ + contain: strict; +} + +.sampling-profiler-viz * { + box-sizing: border-box; +} + +/* Code Panel - Left Column */ +.sampling-profiler-viz #code-panel { + background: var(--bg-panel); + border-right: 1px solid var(--border-color); + overflow-y: auto; + font-family: var(--font-mono); + font-size: 12px; + line-height: 1.6; + display: flex; + flex-direction: column; +} + +.sampling-profiler-viz #code-panel .code-panel-title { + padding: 12px 16px; + font-size: 10px; + font-weight: 600; + color: var(--text-muted); + border-bottom: 1px solid var(--border-color); + background: var(--bg-code); + text-transform: uppercase; + letter-spacing: 1px; + flex-shrink: 0; +} + +.sampling-profiler-viz #code-panel .code-container { + margin: 0; + padding: 12px 0; + overflow-x: auto; + flex: 1; +} + +.sampling-profiler-viz #code-panel .line { + display: flex; + padding: 1px 0; + min-height: 20px; + transition: background-color 0.1s ease; +} + +.sampling-profiler-viz #code-panel .line-number { + color: var(--text-muted); + min-width: 40px; + text-align: right; + padding-right: 12px; + padding-left: 12px; + user-select: none; + flex-shrink: 0; + font-size: 11px; +} + +.sampling-profiler-viz #code-panel .line-content { + flex: 1; + color: var(--text-code); + padding-right: 12px; + white-space: pre; +} + +.sampling-profiler-viz #code-panel .line.highlighted { + background: var(--color-highlight); + border-left: 3px solid var(--color-yellow); +} + +.sampling-profiler-viz #code-panel .line.highlighted .line-number { + color: var(--color-yellow); + padding-left: 9px; +} + +.sampling-profiler-viz #code-panel .line.highlighted .line-content { + font-weight: 600; +} + +/* Python Syntax Highlighting */ +.sampling-profiler-viz #code-panel .keyword { + color: var(--color-red); + font-weight: 600; +} + +.sampling-profiler-viz #code-panel .function { + color: var(--color-purple); + font-weight: 600; +} + +.sampling-profiler-viz #code-panel .number { + color: var(--color-python-blue); +} + +.sampling-profiler-viz #code-panel .string { + color: #032f62; +} + +.sampling-profiler-viz #code-panel .comment { + color: #6a737d; + font-style: italic; +} + +.sampling-profiler-viz #code-panel .builtin { + color: var(--color-python-blue); +} + +/* Visualization Column - Right Side */ +.sampling-profiler-viz .viz-column { + display: flex; + flex-direction: column; + background: var(--bg-subtle); + overflow: hidden; +} + +/* Stack Section */ +.sampling-profiler-viz .stack-section { + padding: 12px 16px; + flex: 1; + min-height: 150px; + overflow-y: auto; +} + +.sampling-profiler-viz .stack-section-title { + font-size: 10px; + font-weight: 600; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 10px; +} + +.sampling-profiler-viz .stack-visualization { + display: flex; + flex-direction: column; + gap: 4px; + min-height: 80px; +} + +/* Stack Frames - Vertical Layout */ +.sampling-profiler-viz .stack-frame { + position: relative; + width: 100%; + height: 32px; + cursor: pointer; + contain: layout style paint; + opacity: 0; + transform: translateY(-10px); +} + +.sampling-profiler-viz .stack-frame.visible { + opacity: 1; + transform: translateY(0); + transition: + opacity 0.3s ease, + transform 0.3s ease; +} + +.sampling-profiler-viz .stack-frame-bg { + position: absolute; + inset: 0; + border-radius: var(--radius-sm); + transition: opacity 0.15s; +} + +.sampling-profiler-viz .stack-frame-text { + position: absolute; + left: 10px; + top: 50%; + transform: translateY(-50%); + font: 500 11px var(--font-mono); + color: white; + pointer-events: none; +} + +.sampling-profiler-viz .stack-frame-flash { + position: absolute; + inset: 0; + background: white; + border-radius: var(--radius-sm); + opacity: 0; + pointer-events: none; +} + +.sampling-profiler-viz .stack-frame:hover .stack-frame-bg { + opacity: 0.85; +} + +/* Flying frames */ +.sampling-profiler-viz .stack-frame.flying { + pointer-events: none; + z-index: 1000; + position: fixed; + top: 0; + left: 0; + width: auto; + height: 32px; + opacity: 1; +} + +/* Flying stack clone */ +.stack-visualization.flying-clone { + transform-origin: center center; + will-change: transform, opacity; +} + +/* Sampling Panel */ +.sampling-profiler-viz .sampling-panel { + margin: 0 16px 12px 16px; + background: var(--bg-panel); + border: 1px solid var(--border-color); + border-radius: var(--radius-md); + box-shadow: var(--shadow-sm); + overflow: hidden; + display: flex; + flex-direction: column; + flex: 0 0 auto; + height: 200px; + /* Lock font size to prevent Sphinx responsive scaling */ + font-size: 12px; +} + +.sampling-profiler-viz .sampling-header { + padding: 8px 10px; + border-bottom: 1px solid var(--border-color); + flex-shrink: 0; +} + +.sampling-profiler-viz .sampling-title { + font: 600 10px var(--font-mono); + color: var(--text-primary); + margin: 0 0 3px 0; +} + +.sampling-profiler-viz .sampling-stats { + font: 400 9px var(--font-mono); + color: var(--text-secondary); + display: flex; + gap: 12px; +} + +.sampling-profiler-viz .sampling-stats .missed { + color: var(--color-red); +} + +.sampling-profiler-viz .sampling-bars { + flex: 1; + padding: 10px 12px; + overflow-y: auto; +} + +.sampling-profiler-viz .sampling-bar-row { + display: flex; + align-items: center; + height: 22px; + gap: 8px; +} + +.sampling-profiler-viz .bar-label { + font: 500 8px var(--font-mono); + color: var(--text-primary); + flex-shrink: 0; + width: 60px; + overflow: hidden; + text-overflow: ellipsis; +} + +.sampling-profiler-viz .bar-container { + flex: 1; + height: 12px; + background: var(--border-color); + border-radius: 3px; + position: relative; + overflow: hidden; +} + +.sampling-profiler-viz .bar-fill { + height: 100%; + border-radius: 3px; + transition: width 0.2s ease; + min-width: 2px; +} + +.sampling-profiler-viz .bar-percent { + font: 500 8px var(--font-mono); + color: var(--text-secondary); + width: 36px; + text-align: right; + flex-shrink: 0; +} + +/* Impact effect circle */ +.impact-circle { + position: fixed; + width: 30px; + height: 30px; + border-radius: 50%; + background: var(--color-teal); + transform: translate(-50%, -50%); + pointer-events: none; + z-index: 2000; +} + +/* Control Panel - Integrated */ +.sampling-profiler-viz #control-panel { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + background: var(--bg-panel); + border-top: 1px solid var(--border-color); + flex-shrink: 0; + flex-wrap: wrap; +} + +.sampling-profiler-viz .control-group { + display: flex; + gap: 6px; + align-items: center; +} + +.sampling-profiler-viz .control-group label { + font-size: 10px; + color: var(--text-muted); + font-weight: 500; + white-space: nowrap; +} + +.sampling-profiler-viz .control-btn { + background: var(--bg-panel); + color: var(--text-primary); + border: 1px solid var(--border-color); + padding: 6px 10px; + border-radius: var(--radius-sm); + cursor: pointer; + transition: all 0.15s ease; + font-family: var(--font-mono); + font-size: 11px; + font-weight: 500; + display: flex; + align-items: center; + gap: 4px; +} + +.sampling-profiler-viz .control-btn:hover { + background: var(--bg-subtle); + border-color: var(--text-muted); +} + +.sampling-profiler-viz .control-btn:active { + transform: scale(0.98); +} + +.sampling-profiler-viz .control-btn.active { + background: var(--color-python-blue); + color: white; + border-color: var(--color-python-blue); +} + +.sampling-profiler-viz .control-btn.active:hover { + background: #2f6493; +} + +/* Timeline Scrubber */ +.sampling-profiler-viz .timeline-scrubber { + flex: 1; + display: flex; + align-items: center; + gap: 8px; + min-width: 160px; +} + +.sampling-profiler-viz #timeline-scrubber { + flex: 1; + height: 5px; + border-radius: 3px; + background: var(--border-color); + outline: none; + appearance: none; + -webkit-appearance: none; + cursor: pointer; + min-width: 60px; +} + +.sampling-profiler-viz #timeline-scrubber::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--color-python-blue); + cursor: pointer; + transition: transform 0.15s; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); +} + +.sampling-profiler-viz #timeline-scrubber::-webkit-slider-thumb:hover { + transform: scale(1.15); +} + +.sampling-profiler-viz #timeline-scrubber::-moz-range-thumb { + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--color-python-blue); + cursor: pointer; + border: none; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); +} + +.sampling-profiler-viz #time-display { + font: 500 10px var(--font-mono); + color: var(--text-secondary); + min-width: 90px; + text-align: right; + font-variant-numeric: tabular-nums; +} + +/* Sample Interval Slider */ +.sampling-profiler-viz #sample-interval { + width: 80px; + height: 4px; + border-radius: 2px; + background: var(--border-color); + outline: none; + appearance: none; + -webkit-appearance: none; + cursor: pointer; + flex-shrink: 0; +} + +.sampling-profiler-viz #sample-interval::-webkit-slider-thumb { + -webkit-appearance: none; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--color-teal); + cursor: pointer; + transition: transform 0.15s; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.sampling-profiler-viz #sample-interval::-webkit-slider-thumb:hover { + transform: scale(1.15); +} + +.sampling-profiler-viz #sample-interval::-moz-range-thumb { + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--color-teal); + cursor: pointer; + border: none; +} + +.sampling-profiler-viz #interval-display { + font: 500 9px var(--font-mono); + color: var(--text-secondary); + min-width: 40px; + font-variant-numeric: tabular-nums; +} + +/* Flash overlay */ +.sampling-profiler-viz .flash-overlay { + position: absolute; + inset: 0; + background: white; + pointer-events: none; + opacity: 0; +} + +/* Performance optimizations */ +.sampling-profiler-viz .stack-frame, +.sampling-profiler-viz .flying-frame, +.sampling-profiler-viz .sampling-bar-row { + will-change: transform, opacity; + contain: layout style paint; +} + +/* Reduced motion support */ +@media (prefers-reduced-motion: reduce) { + .sampling-profiler-viz .stack-frame, + .sampling-profiler-viz .flying-frame, + .sampling-profiler-viz .sampling-bar-row, + .impact-circle { + animation-duration: 0.01ms !important; + transition-duration: 0.01ms !important; + } +} + +/* Responsive adjustments for narrower viewports */ +@media (max-width: 800px) { + .sampling-profiler-viz { + grid-template-columns: 280px 1fr; + --container-height: 550px; + } + + .sampling-profiler-viz #code-panel { + font-size: 11px; + } + + .sampling-profiler-viz .control-btn { + padding: 5px 8px; + font-size: 10px; + } +} diff --git a/Doc/_static/profiling-sampling-visualization.js b/Doc/_static/profiling-sampling-visualization.js new file mode 100644 index 00000000000000..3729be6795c1c8 --- /dev/null +++ b/Doc/_static/profiling-sampling-visualization.js @@ -0,0 +1,1163 @@ +/** + * Sampling Profiler Visualization + */ +(function () { + "use strict"; + + // ============================================================================ + // Configuration + // ============================================================================ + + const TIMINGS = { + sampleIntervalMin: 100, + sampleIntervalMax: 500, + sampleIntervalDefault: 200, + sampleToFlame: 600, + defaultSpeed: 0.05, + }; + + const LAYOUT = { frameSpacing: 6 }; + + // Function name to color mapping + const FUNCTION_COLORS = { + main: "#306998", + fibonacci: "#D4A910", + add: "#E65100", + multiply: "#7B1FA2", + calculate: "#D4A910", + }; + const DEFAULT_FUNCTION_COLOR = "#306998"; + + // Easing functions - cubic-bezier approximations + const EASING_MAP = { + linear: "linear", + easeOutQuad: "cubic-bezier(0.25, 0.46, 0.45, 0.94)", + easeOutCubic: "cubic-bezier(0.215, 0.61, 0.355, 1)", + }; + + function getFunctionColor(funcName) { + return FUNCTION_COLORS[funcName] || DEFAULT_FUNCTION_COLOR; + } + + // ============================================================================ + // Animation Manager + // ============================================================================ + + class AnimationManager { + constructor() { + this.activeAnimations = new Set(); + } + + to(element, props, duration, easing = "easeOutQuad", onComplete = null) { + this.killAnimationsOf(element); + + const cssEasing = EASING_MAP[easing] || EASING_MAP.easeOutQuad; + + const transformProps = {}; + const otherProps = {}; + + for (const [key, value] of Object.entries(props)) { + if (key === "position") { + if (typeof value.x === "number") transformProps.x = value.x; + if (typeof value.y === "number") transformProps.y = value.y; + } else if (key === "x" || key === "y") { + transformProps[key] = value; + } else if (key === "scale") { + transformProps.scale = value; + } else if (key === "alpha" || key === "opacity") { + otherProps.opacity = value; + } else { + otherProps[key] = value; + } + } + + const computedStyle = getComputedStyle(element); + const matrix = new DOMMatrix(computedStyle.transform); + const currentScale = Math.sqrt( + matrix.m11 * matrix.m11 + matrix.m21 * matrix.m21, + ); + + transformProps.x ??= matrix.m41; + transformProps.y ??= matrix.m42; + transformProps.scale ??= currentScale; + + const initialTransform = this._buildTransformString( + matrix.m41, + matrix.m42, + currentScale, + ); + + const finalTransform = this._buildTransformString( + transformProps.x, + transformProps.y, + transformProps.scale, + ); + + const initialKeyframe = { transform: initialTransform }; + const finalKeyframe = { transform: finalTransform }; + + for (const [key, value] of Object.entries(otherProps)) { + const currentVal = + key === "opacity" + ? element.style.opacity || computedStyle.opacity + : element.style[key]; + initialKeyframe[key] = currentVal; + finalKeyframe[key] = value; + } + + const animation = element.animate([initialKeyframe, finalKeyframe], { + duration, + easing: cssEasing, + fill: "forwards", + }); + + this.activeAnimations.add(animation); + animation.onfinish = () => { + this.activeAnimations.delete(animation); + element.style.transform = finalTransform; + for (const [key, value] of Object.entries(finalKeyframe)) { + if (key !== "transform") { + element.style[key] = typeof value === "number" ? `${value}` : value; + } + } + if (onComplete) onComplete(); + }; + + return animation; + } + + killAnimationsOf(element) { + element.getAnimations().forEach((animation) => animation.cancel()); + this.activeAnimations.forEach((animation) => { + if (animation.effect && animation.effect.target === element) { + animation.cancel(); + this.activeAnimations.delete(animation); + } + }); + } + + _buildTransformString(x, y, scale = 1) { + return `translate(${x}px, ${y}px) scale(${scale})`; + } + } + + const anim = new AnimationManager(); + + // ============================================================================ + // Execution Trace Model + // ============================================================================ + + class ExecutionEvent { + constructor( + type, + functionName, + lineno, + timestamp, + args = null, + value = null, + ) { + this.type = type; + this.functionName = functionName; + this.lineno = lineno; + this.timestamp = timestamp; + this.args = args; + this.value = value; + } + } + + class ExecutionTrace { + constructor(source, events) { + this.source = source; + this.events = events.map( + (e) => + new ExecutionEvent(e.type, e.func, e.line, e.ts, e.args, e.value), + ); + this.duration = events.length > 0 ? events[events.length - 1].ts : 0; + } + + getEventsUntil(timestamp) { + return this.events.filter((e) => e.timestamp <= timestamp); + } + + getStackAt(timestamp) { + const stack = []; + const events = this.getEventsUntil(timestamp); + + for (const event of events) { + if (event.type === "call") { + stack.push({ + func: event.functionName, + line: event.lineno, + args: event.args, + }); + } else if (event.type === "return") { + stack.pop(); + } else if (event.type === "line") { + if (stack.length > 0) { + stack[stack.length - 1].line = event.lineno; + } + } + } + return stack; + } + + getNextEvent(timestamp) { + return this.events.find((e) => e.timestamp > timestamp); + } + + getSourceLines() { + return this.source.split("\n"); + } + } + + // ============================================================================ + // Demo Data + // ============================================================================ + + // This placeholder is replaced by the profiling_trace Sphinx extension + // during the documentation build with dynamically generated trace data. + const DEMO_SIMPLE = /* PROFILING_TRACE_DATA */ null; + + // ============================================================================ + // Code Panel Component + // ============================================================================ + + class CodePanel { + constructor(source) { + this.source = source; + this.currentLine = null; + + this.element = document.createElement("div"); + this.element.id = "code-panel"; + + const title = document.createElement("div"); + title.className = "code-panel-title"; + title.textContent = "source code"; + this.element.appendChild(title); + + this.codeContainer = document.createElement("pre"); + this.codeContainer.className = "code-container"; + this.element.appendChild(this.codeContainer); + + this._renderSource(); + } + + updateSource(source) { + this.source = source; + this.codeContainer.innerHTML = ""; + this._renderSource(); + this.currentLine = null; + } + + _renderSource() { + const lines = this.source.split("\n"); + + lines.forEach((line, index) => { + const lineNumber = index + 1; + const lineDiv = document.createElement("div"); + lineDiv.className = "line"; + lineDiv.dataset.line = lineNumber; + + const lineNumSpan = document.createElement("span"); + lineNumSpan.className = "line-number"; + lineNumSpan.textContent = lineNumber; + lineDiv.appendChild(lineNumSpan); + + const codeSpan = document.createElement("span"); + codeSpan.className = "line-content"; + codeSpan.innerHTML = this._highlightSyntax(line); + lineDiv.appendChild(codeSpan); + + this.codeContainer.appendChild(lineDiv); + }); + } + + _highlightSyntax(line) { + return line + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/(f?"[^"]*"|f?'[^']*')/g, '$1') + .replace(/(#.*$)/g, '$1') + .replace( + /\b(def|if|elif|else|return|for|in|range|print|__name__|__main__)\b/g, + '$1', + ) + .replace( + /def<\/span>\s+(\w+)/g, + 'def $1', + ) + .replace(/\b(\d+)\b/g, '$1'); + } + + highlightLine(lineNumber) { + if (this.currentLine === lineNumber) return; + + if (this.currentLine !== null) { + const prevLine = this.codeContainer.querySelector( + `[data-line="${this.currentLine}"]`, + ); + if (prevLine) prevLine.classList.remove("highlighted"); + } + + if (lineNumber === null || lineNumber === undefined) { + this.currentLine = null; + return; + } + + this.currentLine = lineNumber; + const newLine = this.codeContainer.querySelector( + `[data-line="${lineNumber}"]`, + ); + if (newLine) { + newLine.classList.add("highlighted"); + } + } + + reset() { + this.highlightLine(null); + this.codeContainer.scrollTop = 0; + } + + destroy() { + this.element.remove(); + } + } + + // ============================================================================ + // Stack Frame Component + // ============================================================================ + + class DOMStackFrame { + constructor(functionName, lineno, args = null) { + this.functionName = functionName; + this.lineno = lineno; + this.args = args; + this.isActive = false; + this.color = getFunctionColor(functionName); + + this.element = document.createElement("div"); + this.element.className = "stack-frame"; + this.element.dataset.function = functionName; + + this.bgElement = document.createElement("div"); + this.bgElement.className = "stack-frame-bg"; + this.bgElement.style.backgroundColor = this.color; + this.element.appendChild(this.bgElement); + + this.textElement = document.createElement("span"); + this.textElement.className = "stack-frame-text"; + this.textElement.textContent = functionName; + this.element.appendChild(this.textElement); + + this.flashElement = document.createElement("div"); + this.flashElement.className = "stack-frame-flash"; + this.element.appendChild(this.flashElement); + + this.element.addEventListener("pointerover", this._onHover.bind(this)); + this.element.addEventListener("pointerout", this._onHoverOut.bind(this)); + } + + destroy() { + this.element.parentNode?.removeChild(this.element); + } + + updateLine(lineno) { + this.lineno = lineno; + this.textElement.textContent = this.functionName; + } + + setActive(isActive) { + if (this.isActive === isActive) return; + this.isActive = isActive; + this.bgElement.style.opacity = isActive ? "1.0" : "0.9"; + } + + _onHover() { + this.bgElement.style.opacity = "0.8"; + } + + _onHoverOut() { + this.bgElement.style.opacity = this.isActive ? "1.0" : "0.9"; + } + + flash(duration = 150) { + this.flashElement.animate([{ opacity: 1 }, { opacity: 0 }], { + duration, + easing: "ease-out", + }); + } + + getPosition() { + const rect = this.element.getBoundingClientRect(); + return { x: rect.left, y: rect.top }; + } + } + + // ============================================================================ + // Stack Visualization Component + // ============================================================================ + + class DOMStackVisualization { + constructor() { + this.frames = []; + this.frameSpacing = LAYOUT.frameSpacing; + + this.element = document.createElement("div"); + this.element.className = "stack-visualization"; + } + + processEvent(event) { + if (event.type === "call") { + this.pushFrame(event.functionName, event.lineno, event.args); + } else if (event.type === "return") { + this.popFrame(); + } else if (event.type === "line") { + this.updateTopFrameLine(event.lineno); + } + } + + updateTopFrameLine(lineno) { + if (this.frames.length > 0) { + this.frames[this.frames.length - 1].updateLine(lineno); + } + } + + pushFrame(functionName, lineno, args = null) { + if (this.frames.length > 0) { + this.frames[this.frames.length - 1].setActive(false); + } + + const frame = new DOMStackFrame(functionName, lineno, args); + frame.setActive(true); + this.element.appendChild(frame.element); + this.frames.push(frame); + + requestAnimationFrame(() => { + frame.element.classList.add("visible"); + }); + } + + popFrame() { + if (this.frames.length === 0) return; + + const frame = this.frames.pop(); + frame.element.classList.remove("visible"); + setTimeout(() => frame.destroy(), 300); + + if (this.frames.length > 0) { + this.frames[this.frames.length - 1].setActive(true); + } + } + + clear() { + this.frames.forEach((frame) => frame.destroy()); + this.frames = []; + this.element.innerHTML = ""; + } + + flashAll() { + this.frames.forEach((frame) => frame.flash()); + } + + createStackClone(container) { + const clone = this.element.cloneNode(false); + clone.className = "stack-visualization flying-clone"; + + const elementRect = this.element.getBoundingClientRect(); + const containerRect = container.getBoundingClientRect(); + + // Position relative to container since contain: strict makes position:fixed relative to container + clone.style.position = "absolute"; + clone.style.left = elementRect.left - containerRect.left + "px"; + clone.style.top = elementRect.top - containerRect.top + "px"; + clone.style.width = elementRect.width + "px"; + clone.style.pointerEvents = "none"; + clone.style.zIndex = "1000"; + + this.frames.forEach((frame) => { + const frameClone = frame.element.cloneNode(true); + frameClone.classList.add("visible"); + frameClone.style.opacity = "1"; + frameClone.style.transform = "translateY(0)"; + frameClone.style.transition = "none"; + clone.appendChild(frameClone); + }); + + container.appendChild(clone); + return clone; + } + + updateToMatch(targetStack) { + while (this.frames.length > targetStack.length) { + this.popFrame(); + } + + targetStack.forEach(({ func, line, args }, index) => { + if (index < this.frames.length) { + const frame = this.frames[index]; + if (frame.functionName !== func) { + frame.updateLine(line); + } + frame.setActive(index === targetStack.length - 1); + } else { + this.pushFrame(func, line, args); + } + }); + + if (this.frames.length > 0) { + this.frames[this.frames.length - 1].setActive(true); + } + } + } + + // ============================================================================ + // Sampling Panel Component + // ============================================================================ + + class DOMSamplingPanel { + constructor() { + this.samples = []; + this.functionCounts = {}; + this.totalSamples = 0; + this.sampleInterval = TIMINGS.sampleIntervalDefault; + this.groundTruthFunctions = new Set(); + this.bars = {}; + + this.element = document.createElement("div"); + this.element.className = "sampling-panel"; + + const header = document.createElement("div"); + header.className = "sampling-header"; + + const title = document.createElement("h3"); + title.className = "sampling-title"; + title.textContent = "Sampling Profiler"; + header.appendChild(title); + + const stats = document.createElement("div"); + stats.className = "sampling-stats"; + + this.sampleCountEl = document.createElement("span"); + this.sampleCountEl.textContent = "Samples: 0"; + stats.appendChild(this.sampleCountEl); + + this.intervalEl = document.createElement("span"); + this.intervalEl.textContent = `Interval: ${this.sampleInterval}ms`; + stats.appendChild(this.intervalEl); + + this.missedFunctionsEl = document.createElement("span"); + this.missedFunctionsEl.className = "missed"; + stats.appendChild(this.missedFunctionsEl); + + header.appendChild(stats); + this.element.appendChild(header); + + this.barsContainer = document.createElement("div"); + this.barsContainer.className = "sampling-bars"; + this.element.appendChild(this.barsContainer); + } + + setSampleInterval(interval) { + this.sampleInterval = interval; + this.intervalEl.textContent = `Interval: ${interval}ms`; + } + + setGroundTruth(allFunctions) { + this.groundTruthFunctions = new Set(allFunctions); + this._updateMissedCount(); + } + + addSample(stack) { + this.totalSamples++; + this.sampleCountEl.textContent = `Samples: ${this.totalSamples}`; + + stack.forEach((frame) => { + const funcName = frame.func; + this.functionCounts[funcName] = + (this.functionCounts[funcName] || 0) + 1; + }); + + this._updateBars(); + this._updateMissedCount(); + } + + reset() { + this.samples = []; + this.functionCounts = {}; + this.totalSamples = 0; + this.sampleCountEl.textContent = "Samples: 0"; + this.missedFunctionsEl.textContent = ""; + this.barsContainer.innerHTML = ""; + this.bars = {}; + } + + _updateMissedCount() { + if (this.groundTruthFunctions.size === 0) return; + + const capturedFunctions = new Set(Object.keys(this.functionCounts)); + const notYetSeen = [...this.groundTruthFunctions].filter( + (f) => !capturedFunctions.has(f), + ); + + if (notYetSeen.length > 0) { + this.missedFunctionsEl.textContent = `Not yet seen: ${notYetSeen.length}`; + this.missedFunctionsEl.classList.add("missed"); + this.missedFunctionsEl.style.color = ""; + } else if (this.totalSamples > 0) { + this.missedFunctionsEl.textContent = "All captured!"; + this.missedFunctionsEl.classList.remove("missed"); + this.missedFunctionsEl.style.color = "var(--color-green)"; + } else { + this.missedFunctionsEl.textContent = ""; + } + } + + _updateBars() { + const sorted = Object.entries(this.functionCounts).sort( + (a, b) => b[1] - a[1], + ); + + sorted.forEach(([funcName, count], index) => { + const percentage = + this.totalSamples > 0 ? count / this.totalSamples : 0; + + if (!this.bars[funcName]) { + const row = this._createBarRow(funcName); + this.barsContainer.appendChild(row); + this.bars[funcName] = row; + } + + const row = this.bars[funcName]; + const barFill = row.querySelector(".bar-fill"); + barFill.style.width = `${percentage * 100}%`; + + const percentEl = row.querySelector(".bar-percent"); + percentEl.textContent = `${(percentage * 100).toFixed(0)}%`; + + const currentIndex = Array.from(this.barsContainer.children).indexOf( + row, + ); + if (currentIndex !== index) { + this.barsContainer.insertBefore( + row, + this.barsContainer.children[index], + ); + } + }); + } + + _createBarRow(funcName) { + const row = document.createElement("div"); + row.className = "sampling-bar-row"; + row.dataset.function = funcName; + + const label = document.createElement("span"); + label.className = "bar-label"; + label.textContent = funcName; + row.appendChild(label); + + const barContainer = document.createElement("div"); + barContainer.className = "bar-container"; + + const barFill = document.createElement("div"); + barFill.className = "bar-fill"; + barFill.style.backgroundColor = getFunctionColor(funcName); + barContainer.appendChild(barFill); + + row.appendChild(barContainer); + + const percent = document.createElement("span"); + percent.className = "bar-percent"; + percent.textContent = "0%"; + row.appendChild(percent); + + return row; + } + + getTargetPosition() { + const rect = this.barsContainer.getBoundingClientRect(); + return { x: rect.left + rect.width / 2, y: rect.top + 50 }; + } + + showImpactEffect(position) { + const impact = document.createElement("div"); + impact.className = "impact-circle"; + impact.style.position = "fixed"; + impact.style.left = `${position.x}px`; + impact.style.top = `${position.y}px`; + + // Append to barsContainer parent to avoid triggering scroll + this.element.appendChild(impact); + + impact.animate( + [ + { transform: "translate(-50%, -50%) scale(1)", opacity: 0.6 }, + { transform: "translate(-50%, -50%) scale(4)", opacity: 0 }, + ], + { + duration: 300, + easing: "ease-out", + }, + ).onfinish = () => impact.remove(); + } + } + + // ============================================================================ + // Control Panel Component + // ============================================================================ + + class ControlPanel { + constructor( + container, + onPlay, + onPause, + onReset, + onSpeedChange, + onSeek, + onStep, + onSampleIntervalChange = null, + ) { + this.container = container; + this.onPlay = onPlay; + this.onPause = onPause; + this.onReset = onReset; + this.onSpeedChange = onSpeedChange; + this.onSeek = onSeek; + this.onStep = onStep; + this.onSampleIntervalChange = onSampleIntervalChange; + + this.isPlaying = false; + this.speed = TIMINGS.defaultSpeed; + + this._createControls(); + } + + _createControls() { + const panel = document.createElement("div"); + panel.id = "control-panel"; + + const sampleIntervalHtml = this.onSampleIntervalChange + ? ` +
+ + + ${TIMINGS.sampleIntervalDefault}ms +
+ ` + : ""; + + panel.innerHTML = ` +
+ + + +
+ + ${sampleIntervalHtml} + +
+ + 0ms +
+ `; + + this.container.appendChild(panel); + + this.playPauseBtn = panel.querySelector("#play-pause-btn"); + this.resetBtn = panel.querySelector("#reset-btn"); + this.stepBtn = panel.querySelector("#step-btn"); + this.scrubber = panel.querySelector("#timeline-scrubber"); + this.timeDisplay = panel.querySelector("#time-display"); + + this.playPauseBtn.addEventListener("click", () => + this._togglePlayPause(), + ); + this.resetBtn.addEventListener("click", () => this._handleReset()); + this.stepBtn.addEventListener("click", () => this._handleStep()); + this.scrubber.addEventListener("input", (e) => this._handleSeek(e)); + + if (this.onSampleIntervalChange) { + this.sampleIntervalSlider = panel.querySelector("#sample-interval"); + this.intervalDisplay = panel.querySelector("#interval-display"); + this.sampleIntervalSlider.addEventListener("input", (e) => + this._handleSampleIntervalChange(e), + ); + } + } + + _handleSampleIntervalChange(e) { + const interval = parseInt(e.target.value); + this.intervalDisplay.textContent = `${interval}ms`; + this.onSampleIntervalChange(interval); + } + + _togglePlayPause() { + this.isPlaying = !this.isPlaying; + + if (this.isPlaying) { + this.playPauseBtn.textContent = "⏸ Pause"; + this.playPauseBtn.classList.add("active"); + this.onPlay(); + } else { + this.playPauseBtn.textContent = "▶ Play"; + this.playPauseBtn.classList.remove("active"); + this.onPause(); + } + } + + _handleReset() { + this.isPlaying = false; + this.playPauseBtn.textContent = "▶ Play"; + this.playPauseBtn.classList.remove("active"); + this.scrubber.value = 0; + this.timeDisplay.textContent = "0ms"; + this.onReset(); + } + + _handleStep() { + if (this.onStep) this.onStep(); + } + + _handleSeek(e) { + const percentage = parseFloat(e.target.value); + this.onSeek(percentage / 100); + } + + updateTimeDisplay(currentTime, totalTime) { + this.timeDisplay.textContent = `${Math.floor(currentTime)}ms / ${Math.floor(totalTime)}ms`; + const percentage = (currentTime / totalTime) * 100; + this.scrubber.value = percentage; + } + + setDuration(duration) { + this.duration = duration; + } + + pause() { + if (this.isPlaying) this._togglePlayPause(); + } + + destroy() { + const panel = this.container.querySelector("#control-panel"); + if (panel) panel.remove(); + } + } + + // ============================================================================ + // Visual Effects Manager + // ============================================================================ + + class VisualEffectsManager { + constructor(container) { + this.container = container; + this.flyingAnimationInProgress = false; + + this.flashOverlay = document.createElement("div"); + this.flashOverlay.className = "flash-overlay"; + this.container.appendChild(this.flashOverlay); + } + + triggerSamplingEffect(stackViz, samplingPanel, currentTime, trace) { + if (this.flyingAnimationInProgress) return; + + const stack = trace.getStackAt(currentTime); + + if (stack.length === 0) { + samplingPanel.addSample(stack); + return; + } + + this.flyingAnimationInProgress = true; + stackViz.flashAll(); + + const clone = stackViz.createStackClone(this.container); + const targetPosition = samplingPanel.getTargetPosition(); + + this._animateFlash(); + this._animateFlyingStack(clone, targetPosition, () => { + samplingPanel.showImpactEffect(targetPosition); + clone.remove(); + + const currentStack = trace.getStackAt(currentTime); + samplingPanel.addSample(currentStack); + this.flyingAnimationInProgress = false; + }); + } + + _animateFlash() { + anim.to(this.flashOverlay, { opacity: 0.1 }, 0).onfinish = () => { + anim.to(this.flashOverlay, { opacity: 0 }, 150, "easeOutQuad"); + }; + } + + _animateFlyingStack(clone, targetPosition, onComplete) { + const containerRect = this.container.getBoundingClientRect(); + const cloneRect = clone.getBoundingClientRect(); + + // Convert viewport coordinates to container-relative + const startX = cloneRect.left - containerRect.left + cloneRect.width / 2; + const startY = cloneRect.top - containerRect.top + cloneRect.height / 2; + const targetX = targetPosition.x - containerRect.left; + const targetY = targetPosition.y - containerRect.top; + + const deltaX = targetX - startX; + const deltaY = targetY - startY; + + anim.to( + clone, + { + x: deltaX, + y: deltaY, + scale: 0.3, + opacity: 0.6, + }, + TIMINGS.sampleToFlame, + "easeOutCubic", + onComplete, + ); + } + } + + // ============================================================================ + // Main Visualization Class + // ============================================================================ + + class SamplingVisualization { + constructor(container) { + this.container = container; + + this.trace = new ExecutionTrace(DEMO_SIMPLE.source, DEMO_SIMPLE.trace); + + this.currentTime = 0; + this.isPlaying = false; + this.playbackSpeed = TIMINGS.defaultSpeed; + this.eventIndex = 0; + + this.sampleInterval = TIMINGS.sampleIntervalDefault; + this.lastSampleTime = 0; + + this._createLayout(); + + this.effectsManager = new VisualEffectsManager(this.vizColumn); + + this.lastTime = performance.now(); + this._animate(); + } + + _createLayout() { + this.codePanel = new CodePanel(this.trace.source); + this.container.appendChild(this.codePanel.element); + + this.vizColumn = document.createElement("div"); + this.vizColumn.className = "viz-column"; + this.container.appendChild(this.vizColumn); + + const stackSection = document.createElement("div"); + stackSection.className = "stack-section"; + + const stackTitle = document.createElement("div"); + stackTitle.className = "stack-section-title"; + stackTitle.textContent = "Call Stack"; + stackSection.appendChild(stackTitle); + + this.stackViz = new DOMStackVisualization(); + stackSection.appendChild(this.stackViz.element); + this.vizColumn.appendChild(stackSection); + + this.samplingPanel = new DOMSamplingPanel(); + this.samplingPanel.setGroundTruth(this._getGroundTruthFunctions()); + this.vizColumn.appendChild(this.samplingPanel.element); + + this.controls = new ControlPanel( + this.vizColumn, + () => this.play(), + () => this.pause(), + () => this.reset(), + (speed) => this.setSpeed(speed), + (progress) => this.seek(progress), + () => this.step(), + (interval) => this.setSampleInterval(interval), + ); + this.controls.setDuration(this.trace.duration); + } + + _getGroundTruthFunctions() { + const functions = new Set(); + this.trace.events.forEach((event) => { + if (event.type === "call") { + functions.add(event.functionName); + } + }); + return [...functions]; + } + + play() { + this.isPlaying = true; + } + + pause() { + this.isPlaying = false; + } + + reset() { + this.currentTime = 0; + this.eventIndex = 0; + this.isPlaying = false; + this.lastSampleTime = 0; + this.stackViz.clear(); + this.codePanel.reset(); + this.samplingPanel.reset(); + this.controls.updateTimeDisplay(0, this.trace.duration); + } + + setSpeed(speed) { + this.playbackSpeed = speed; + } + + setSampleInterval(interval) { + this.sampleInterval = interval; + this.samplingPanel.setSampleInterval(interval); + } + + seek(progress) { + this.currentTime = progress * this.trace.duration; + this.eventIndex = 0; + this.lastSampleTime = 0; + this._rebuildState(); + } + + step() { + this.pause(); + + const nextEvent = this.trace.getNextEvent(this.currentTime); + + if (nextEvent) { + // Calculate delta to reach next event + epsilon + const targetTime = nextEvent.timestamp + 0.1; + const delta = targetTime - this.currentTime; + if (delta > 0) { + this._advanceTime(delta); + } + } + } + + _animate(currentTime = performance.now()) { + const deltaTime = currentTime - this.lastTime; + this.lastTime = currentTime; + + this.update(deltaTime); + requestAnimationFrame((t) => this._animate(t)); + } + + update(deltaTime) { + if (!this.isPlaying) { + this.controls.updateTimeDisplay(this.currentTime, this.trace.duration); + return; + } + + const virtualDelta = deltaTime * this.playbackSpeed; + this._advanceTime(virtualDelta); + } + + _advanceTime(virtualDelta) { + this.currentTime += virtualDelta; + + if (this.currentTime >= this.trace.duration) { + this.currentTime = this.trace.duration; + this.isPlaying = false; + this.controls.pause(); + } + + while (this.eventIndex < this.trace.events.length) { + const event = this.trace.events[this.eventIndex]; + + if (event.timestamp > this.currentTime) break; + + this._processEvent(event); + this.eventIndex++; + } + + this.controls.updateTimeDisplay(this.currentTime, this.trace.duration); + + if (this.currentTime - this.lastSampleTime >= this.sampleInterval) { + this._takeSample(); + this.lastSampleTime = this.currentTime; + } + } + + _processEvent(event) { + this.stackViz.processEvent(event); + + if (event.type === "call") { + this.codePanel.highlightLine(event.lineno); + } else if (event.type === "return") { + const currentStack = this.trace.getStackAt(this.currentTime); + if (currentStack.length > 0) { + this.codePanel.highlightLine( + currentStack[currentStack.length - 1].line, + ); + } else { + this.codePanel.highlightLine(null); + } + } else if (event.type === "line") { + this.codePanel.highlightLine(event.lineno); + } + } + + _takeSample() { + this.effectsManager.triggerSamplingEffect( + this.stackViz, + this.samplingPanel, + this.currentTime, + this.trace, + ); + } + + _rebuildState() { + this.stackViz.clear(); + this.codePanel.reset(); + this.samplingPanel.reset(); + + for (let t = 0; t < this.currentTime; t += this.sampleInterval) { + const stack = this.trace.getStackAt(t); + this.samplingPanel.addSample(stack); + this.lastSampleTime = t; + } + + const stack = this.trace.getStackAt(this.currentTime); + this.stackViz.updateToMatch(stack); + + if (stack.length > 0) { + this.codePanel.highlightLine(stack[stack.length - 1].line); + } + + this.eventIndex = this.trace.getEventsUntil(this.currentTime).length; + } + } + + // ============================================================================ + // Initialize + // ============================================================================ + + function init() { + // If trace data hasn't been injected yet (local dev), don't initialize + if (!DEMO_SIMPLE) return; + + const appContainer = document.getElementById("sampling-profiler-viz"); + if (appContainer) { + new SamplingVisualization(appContainer); + } + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } +})(); diff --git a/Doc/_static/tachyon-example-flamegraph.html b/Doc/_static/tachyon-example-flamegraph.html new file mode 100644 index 00000000000000..701c959bda3ccc --- /dev/null +++ b/Doc/_static/tachyon-example-flamegraph.html @@ -0,0 +1,3260 @@ + + + + + + Tachyon Profiler - Flamegraph Report + + + + + + + +
+ +
+
+ + Tachyon + + Flamegraph Report +
+
+ + +
+
+ + + + + + + + +
+
+ + +
+ + + + +
+
+
+
+ + +
+ + Tachyon Profiler + + + Python Sampling Profiler + + + + + +
+
+ + + + diff --git a/Doc/_static/tachyon-example-heatmap.html b/Doc/_static/tachyon-example-heatmap.html new file mode 100644 index 00000000000000..f725e9475009fe --- /dev/null +++ b/Doc/_static/tachyon-example-heatmap.html @@ -0,0 +1,3804 @@ + + + + + + /tmp/tachyon_selfcontained.py - Heatmap + + + +
+ +
+
+ + Tachyon + + /tmp/tachyon_selfcontained.py +
+
+ + + + + + + + + + + +
+
+ + +
+
+
+
1,054
+
Self Samples
+
+
+
4,236
+
Cumulative
+
+
+
24
+
Lines Hit
+
+
+
100.00%
+
% of Total
+
+
+
206
+
Max Self
+
+
+
1054
+
Max Total
+
+
+
+ + +
+
+ Intensity: +
+
+ Cold + + Hot +
+ +
+
+ Self Time +
+ Total Time +
+
+ Show All +
+ Hot Only +
+
+ Heat +
+ Specialization +
+ + +
+
+
+ + +
+
+
Line
+
Self
+
Total
+
Code
+
+
+
1
+
+
+
+
"""
+ +
+
+
2
+
+
+
+
Tachyon Demo - Self-contained profiling example.
+ +
+
+
3
+
+
+
+
Pure Python with no external imports for clean heatmap output.
+ +
+
+
4
+
+
+
+
"""
+ +
+
+
5
+
+
+
+
+ +
+
+
6
+
+
+
+
+ +
+
+
7
+
1
+
1
+ +
def fibonacci(n):
+ +
+ +
+
8
+
+
+
+
"""Recursive fibonacci - creates deep call stacks."""
+ +
+
+
9
+
3
+
3
+ +
if n <= 1:
+ +
+ +
+
10
+
17
+
17
+ +
return n
+
+
+ +
+
11
+
206
+
227
+ +
return fibonacci(n - 1) + fibonacci(n - 2)
+
+
+ +
+
12
+
+
+
+
+ +
+
+
13
+
+
+
+
+ +
+
+
14
+
+
+
+
def bubble_sort(arr):
+ +
+
+
15
+
+
+
+
"""Classic bubble sort - O(n^2) comparison sorting."""
+ +
+
+
16
+
+
+
+
n = len(arr)
+ +
+
+
17
+
+
+
+
for i in range(n):
+ +
+
+
18
+
2
+
2
+ +
for j in range(0, n - i - 1):
+ +
+ +
+
19
+
8
+
8
+ +
if arr[j] > arr[j + 1]:
+
+
+ +
+
20
+
2
+
2
+ +
arr[j], arr[j + 1] = arr[j + 1], arr[j]
+ +
+ +
+
21
+
+
+
+
return arr
+ +
+
+
22
+
+
+
+
+ +
+
+
23
+
+
+
+
+ +
+
+
24
+
+
+
+
def matrix_multiply(a, b):
+ +
+
+
25
+
+
+
+
"""Matrix multiplication using nested loops."""
+ +
+
+
26
+
+
+
+
rows_a, cols_a = len(a), len(a[0])
+ +
+
+
27
+
+
+
+
rows_b, cols_b = len(b), len(b[0])
+ +
+
+
28
+
+
+
+
result = [[0] * cols_b for _ in range(rows_a)]
+ +
+
+
29
+
+
+
+
+ +
+
+
30
+
+
+
+
for i in range(rows_a):
+ +
+
+
31
+
+
+
+
for j in range(cols_b):
+ +
+
+
32
+
8
+
8
+ +
for k in range(cols_a):
+ +
+ +
+
33
+
62
+
62
+ +
result[i][j] += a[i][k] * b[k][j]
+
+
+ +
+
34
+
+
+
+
return result
+ +
+
+
35
+
+
+
+
+ +
+
+
36
+
+
+
+
+ +
+
+
37
+
+
+
+
def prime_sieve(limit):
+ +
+
+
38
+
+
+
+
"""Sieve of Eratosthenes - find all primes up to limit."""
+ +
+
+
39
+
+
+
+
is_prime = [True] * (limit + 1)
+ +
+
+
40
+
+
+
+
is_prime[0] = is_prime[1] = False
+ +
+
+
41
+
+
+
+
+ +
+
+
42
+
+
+
+
for num in range(2, int(limit ** 0.5) + 1):
+ +
+
+
43
+
+
+
+
if is_prime[num]:
+ +
+
+
44
+
1
+
1
+ +
for multiple in range(num * num, limit + 1, num):
+
+
+ +
+
45
+
+
+
+
is_prime[multiple] = False
+ +
+
+
46
+
+
+
+
+ +
+
+
47
+
2
+
2
+ +
return [num for num, prime in enumerate(is_prime) if prime]
+ +
+ +
+
48
+
+
+
+
+ +
+
+
49
+
+
+
+
+ +
+
+
50
+
+
+
+
def string_processing(iterations):
+ +
+
+
51
+
+
+
+
"""String operations - concatenation and formatting."""
+ +
+
+
52
+
+
+
+
for _ in range(iterations):
+ +
+
+
53
+
+
+
+
result = ""
+ +
+
+
54
+
+
+
+
for i in range(50):
+ +
+
+
55
+
91
+
91
+ +
result += f"item_{i}_"
+
+
+ +
+
56
+
22
+
22
+ +
parts = result.split("_")
+ +
+ +
+
57
+
10
+
10
+ +
joined = "-".join(parts)
+ +
+ +
+
58
+
+
+
+
+ +
+
+
59
+
+
+
+
+ +
+
+
60
+
+
+
+
def list_operations(iterations):
+ +
+
+
61
+
+
+
+
"""List comprehensions and operations."""
+ +
+
+
62
+
+
+
+
for _ in range(iterations):
+ +
+
+
63
+
118
+
118
+ +
squares = [x ** 2 for x in range(500)]
+ +
+ +
+
64
+
119
+
119
+ +
evens = [x for x in squares if x % 2 == 0]
+
+
+ +
+
65
+
12
+
12
+ +
total = sum(evens)
+ +
+ +
+
66
+
+
+
+
+ +
+
+
67
+
+
+
+
+ +
+
+
68
+
+
+
+
def dict_operations(iterations):
+ +
+
+
69
+
+
+
+
"""Dictionary creation and lookups."""
+ +
+
+
70
+
+
+
+
for _ in range(iterations):
+ +
+
+
71
+
206
+
206
+ +
data = {f"key_{i}": i ** 2 for i in range(200)}
+
+
+ +
+
72
+
158
+
158
+ +
values = [data[f"key_{i}"] for i in range(200)]
+ +
+ +
+
73
+
5
+
5
+ +
total = sum(values)
+ +
+ +
+
74
+
+
+
+
+ +
+
+
75
+
+
+
+
+ +
+
+
76
+
+
+
+
def compute_heavy():
+ +
+
+
77
+
+
+
+
"""CPU-intensive computation section."""
+ +
+
+
78
+
1
+
301
+ +
fibonacci(30)
+
+
+ +
+
79
+
+
+
+
+ +
+
+
80
+
+
+
+
size = 60
+ +
+
+
81
+
+
+
+
a = [[i + j for j in range(size)] for i in range(size)]
+ +
+
+
82
+
+
+
+
b = [[i * j for j in range(size)] for i in range(size)]
+ +
+
+
83
+
+
+
+
matrix_multiply(a, b)
+ +
+
+
84
+
+
+
+
+ +
+
+
85
+
+
+
+
prime_sieve(10000)
+ +
+
+
86
+
+
+
+
+ +
+
+
87
+
+
+
+
+ +
+
+
88
+
+
+
+
def data_processing():
+ +
+
+
89
+
+
+
+
"""Data structure operations."""
+ +
+
+
90
+
+
753
+ +
string_processing(2000)
+
+
+ +
+
91
+
+
+
+
list_operations(1500)
+ +
+
+
92
+
+
+
+
dict_operations(1000)
+ +
+
+
93
+
+
+
+
+ +
+
+
94
+
+
+
+
data = list(range(800))
+ +
+
+
95
+
+
+
+
for _ in range(3):
+ +
+
+
96
+
+
+
+
data = data[::-1]
+ +
+
+
97
+
+
+
+
bubble_sort(data[:200])
+ +
+
+
98
+
+
+
+
+ +
+
+
99
+
+
+
+
+ +
+
+
100
+
+
+
+
def main():
+ +
+
+
101
+
+
+
+
"""Main entry point."""
+ +
+
+
102
+
+
1,054
+ +
compute_heavy()
+
+
+ +
+
103
+
+
+
+
data_processing()
+ +
+
+
104
+
+
+
+
+ +
+
+
105
+
+
+
+
+ +
+
+
106
+
+
+
+
if __name__ == "__main__":
+ +
+
+
107
+
+
1,054
+ +
main()
+
+
+ + +
+
+ + + + diff --git a/Doc/about.rst b/Doc/about.rst index 8f635d7f743a98..5c1b497ca6bcea 100644 --- a/Doc/about.rst +++ b/Doc/about.rst @@ -32,8 +32,9 @@ Contributors to the Python documentation ---------------------------------------- Many people have contributed to the Python language, the Python standard -library, and the Python documentation. See :source:`Misc/ACKS` in the Python -source distribution for a partial list of contributors. +library, and the Python documentation. See the `CPython +GitHub repository `__ +for a partial list of contributors. It is only with the input and contributions of the Python community that Python has such wonderful documentation -- Thank You! diff --git a/Doc/bugs.rst b/Doc/bugs.rst index 5d0f68ca69675e..a6ea0a72e76f9d 100644 --- a/Doc/bugs.rst +++ b/Doc/bugs.rst @@ -9,9 +9,12 @@ stability. In order to maintain this reputation, the developers would like to know of any deficiencies you find in Python. It can be sometimes faster to fix bugs yourself and contribute patches to -Python as it streamlines the process and involves less people. Learn how to +Python as it streamlines the process and involves fewer people. Learn how to :ref:`contribute `. + +.. _reporting-documentation-bugs: + Documentation bugs ================== @@ -19,6 +22,12 @@ If you find a bug in this documentation or would like to propose an improvement, please submit a bug report on the :ref:`issue tracker `. If you have a suggestion on how to fix it, include that as well. +.. only:: translation + + If the bug or suggested improvement concerns the translation of this + documentation, submit the report to the + `translation’s repository `_ instead. + You can also open a discussion item on our `Documentation Discourse forum `_. @@ -37,8 +46,8 @@ tracker `_. `Helping with Documentation `_ Comprehensive guide for individuals that are interested in contributing to Python documentation. - `Documentation Translations `_ - A list of GitHub pages for documentation translation and their primary contacts. + `Documentation Translations `_ + A list of GitHub pages for documentation translation and their coordination teams. .. _using-the-tracker: diff --git a/Doc/c-api/allocation.rst b/Doc/c-api/allocation.rst index 59d913a0462382..09c9ed3ca54cf6 100644 --- a/Doc/c-api/allocation.rst +++ b/Doc/c-api/allocation.rst @@ -2,7 +2,7 @@ .. _allocating-objects: -Allocating Objects on the Heap +Allocating objects on the heap ============================== @@ -140,10 +140,6 @@ Allocating Objects on the Heap * :c:member:`~PyTypeObject.tp_alloc` -.. c:function:: void PyObject_Del(void *op) - - Same as :c:func:`PyObject_Free`. - .. c:var:: PyObject _Py_NoneStruct Object which is visible in Python as ``None``. This should only be accessed @@ -156,3 +152,37 @@ Allocating Objects on the Heap :ref:`moduleobjects` To allocate and create extension modules. + +Soft-deprecated aliases +^^^^^^^^^^^^^^^^^^^^^^^ + +.. soft-deprecated:: 3.15 + +These are aliases to existing functions and macros. +They exist solely for backwards compatibility. + + +.. list-table:: + :widths: auto + :header-rows: 1 + + * * Soft-deprecated alias + * Function + * * .. c:macro:: PyObject_NEW(type, typeobj) + * :c:macro:`PyObject_New` + * * .. c:macro:: PyObject_NEW_VAR(type, typeobj, n) + * :c:macro:`PyObject_NewVar` + * * .. c:macro:: PyObject_INIT(op, typeobj) + * :c:func:`PyObject_Init` + * * .. c:macro:: PyObject_INIT_VAR(op, typeobj, n) + * :c:func:`PyObject_InitVar` + * * .. c:macro:: PyObject_MALLOC(n) + * :c:func:`PyObject_Malloc` + * * .. c:macro:: PyObject_REALLOC(p, n) + * :c:func:`PyObject_Realloc` + * * .. c:macro:: PyObject_FREE(p) + * :c:func:`PyObject_Free` + * * .. c:macro:: PyObject_DEL(p) + * :c:func:`PyObject_Free` + * * .. c:macro:: PyObject_Del(p) + * :c:func:`PyObject_Free` diff --git a/Doc/c-api/apiabiversion.rst b/Doc/c-api/apiabiversion.rst index 96050f59bd5250..cb98d4307ee522 100644 --- a/Doc/c-api/apiabiversion.rst +++ b/Doc/c-api/apiabiversion.rst @@ -34,6 +34,23 @@ See :ref:`stable` for a discussion of API and ABI stability across versions. This can be ``0xA`` for alpha, ``0xB`` for beta, ``0xC`` for release candidate or ``0xF`` for final. + + .. c:namespace:: NULL + .. c:macro:: PY_RELEASE_LEVEL_ALPHA + :no-typesetting: + .. c:macro:: PY_RELEASE_LEVEL_BETA + :no-typesetting: + .. c:macro:: PY_RELEASE_LEVEL_GAMMA + :no-typesetting: + .. c:macro:: PY_RELEASE_LEVEL_FINAL + :no-typesetting: + + For completeness, the values are available as macros: + :c:macro:`!PY_RELEASE_LEVEL_ALPHA` (``0xA``), + :c:macro:`!PY_RELEASE_LEVEL_BETA` (``0xB``), + :c:macro:`!PY_RELEASE_LEVEL_GAMMA` (``0xC``), and + :c:macro:`!PY_RELEASE_LEVEL_FINAL` (``0xF``). + .. c:macro:: PY_RELEASE_SERIAL The ``2`` in ``3.4.1a2``. Zero for final releases. @@ -46,6 +63,12 @@ See :ref:`stable` for a discussion of API and ABI stability across versions. Use this for numeric comparisons, for example, ``#if PY_VERSION_HEX >= ...``. +.. c:macro:: PY_VERSION + + The Python version as a string, for example, ``"3.4.1a2"``. + +These macros are defined in :source:`Include/patchlevel.h`. + Run-time version ---------------- diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst index 3429a4eb652709..58456a36b96c15 100644 --- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -160,7 +160,7 @@ There are three ways strings and buffers can be converted to C: ``w*`` (read-write :term:`bytes-like object`) [Py_buffer] This format accepts any object which implements the read-write buffer interface. It fills a :c:type:`Py_buffer` structure provided by the caller. - The buffer may contain embedded null bytes. The caller have to call + The buffer may contain embedded null bytes. The caller has to call :c:func:`PyBuffer_Release` when it is done with the buffer. ``es`` (:class:`str`) [const char \*encoding, char \*\*buffer] @@ -305,7 +305,7 @@ the minimal value for the corresponding signed integer type of the same size. ``D`` (:class:`complex`) [Py_complex] Convert a Python complex number to a C :c:type:`Py_complex` structure. -.. deprecated:: next +.. deprecated:: 3.15 For unsigned integer formats ``B``, ``H``, ``I``, ``k`` and ``K``, :exc:`DeprecationWarning` is emitted when the value is larger than @@ -516,6 +516,28 @@ API Functions } +.. c:function:: int PyArg_ParseArray(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) + + Parse the parameters of a function that takes only array parameters into + local variables (that is, a function using the :c:macro:`METH_FASTCALL` + calling convention). + Returns true on success; on failure, it returns false and raises the + appropriate exception. + + .. versionadded:: 3.15 + + +.. c:function:: int PyArg_ParseArrayAndKeywords(PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames, const char *format, const char * const *kwlist, ...) + + Parse the parameters of a function that takes both array and keyword + parameters into local variables (that is, a function using the + :c:macro:`METH_FASTCALL` ``|`` :c:macro:`METH_KEYWORDS` calling convention). + Returns true on success; on failure, it returns false and raises the + appropriate exception. + + .. versionadded:: 3.15 + + .. c:function:: int PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, ...) A simpler form of parameter retrieval which does not use a format string to diff --git a/Doc/c-api/buffer.rst b/Doc/c-api/buffer.rst index d3081894eadaf5..dc3e0f37c36c5b 100644 --- a/Doc/c-api/buffer.rst +++ b/Doc/c-api/buffer.rst @@ -10,11 +10,6 @@ Buffer Protocol --------------- -.. sectionauthor:: Greg Stein -.. sectionauthor:: Benjamin Peterson -.. sectionauthor:: Stefan Krah - - Certain objects available in Python wrap access to an underlying memory array or *buffer*. Such objects include the built-in :class:`bytes` and :class:`bytearray`, and some extension types like :class:`array.array`. @@ -261,6 +256,12 @@ readonly, format MUST be consistent for all consumers. For example, :c:expr:`PyBUF_SIMPLE | PyBUF_WRITABLE` can be used to request a simple writable buffer. + .. c:macro:: PyBUF_WRITEABLE + + This is an alias to :c:macro:`PyBUF_WRITABLE`. + + .. soft-deprecated:: 3.13 + .. c:macro:: PyBUF_FORMAT Controls the :c:member:`~Py_buffer.format` field. If set, this field MUST @@ -501,10 +502,11 @@ Buffer-related functions *indices* must point to an array of ``view->ndim`` indices. -.. c:function:: int PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, Py_ssize_t len, char fort) +.. c:function:: int PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, Py_ssize_t len, char order) Copy contiguous *len* bytes from *buf* to *view*. - *fort* can be ``'C'`` or ``'F'`` (for C-style or Fortran-style ordering). + *order* can be ``'C'`` or ``'F'`` or ``'A'`` (for C-style or Fortran-style + ordering or either one). ``0`` is returned on success, ``-1`` on error. diff --git a/Doc/c-api/bytearray.rst b/Doc/c-api/bytearray.rst index e2b22ec3c794ae..2b36da997d4295 100644 --- a/Doc/c-api/bytearray.rst +++ b/Doc/c-api/bytearray.rst @@ -44,6 +44,10 @@ Direct API functions On failure, return ``NULL`` with an exception set. + .. note:: + If the object implements the buffer protocol, then the buffer + must not be mutated while the bytearray object is being created. + .. c:function:: PyObject* PyByteArray_FromStringAndSize(const char *string, Py_ssize_t len) @@ -58,6 +62,10 @@ Direct API functions On failure, return ``NULL`` with an exception set. + .. note:: + If the object implements the buffer protocol, then the buffer + must not be mutated while the bytearray object is being created. + .. c:function:: Py_ssize_t PyByteArray_Size(PyObject *bytearray) @@ -70,6 +78,9 @@ Direct API functions ``NULL`` pointer. The returned array always has an extra null byte appended. + .. note:: + It is not thread-safe to mutate the bytearray object while using the returned char array. + .. c:function:: int PyByteArray_Resize(PyObject *bytearray, Py_ssize_t len) @@ -89,6 +100,9 @@ These macros trade safety for speed and they don't check pointers. Similar to :c:func:`PyByteArray_AsString`, but without error checking. + .. note:: + It is not thread-safe to mutate the bytearray object while using the returned char array. + .. c:function:: Py_ssize_t PyByteArray_GET_SIZE(PyObject *bytearray) diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index d47beee68eaa33..f56bcd6333a37d 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -47,6 +47,10 @@ called with a non-bytes parameter. *len* on success, and ``NULL`` on failure. If *v* is ``NULL``, the contents of the bytes object are uninitialized. + .. soft-deprecated:: 3.15 + Use the :c:type:`PyBytesWriter` API instead of + ``PyBytes_FromStringAndSize(NULL, len)``. + .. c:function:: PyObject* PyBytes_FromFormat(const char *format, ...) @@ -123,6 +127,10 @@ called with a non-bytes parameter. Return the bytes representation of object *o* that implements the buffer protocol. + .. note:: + If the object implements the buffer protocol, then the buffer + must not be mutated while the bytes object is being created. + .. c:function:: Py_ssize_t PyBytes_Size(PyObject *o) @@ -181,6 +189,9 @@ called with a non-bytes parameter. created, the old reference to *bytes* will still be discarded and the value of *\*bytes* will be set to ``NULL``; the appropriate exception will be set. + .. note:: + If *newpart* implements the buffer protocol, then the buffer + must not be mutated while the new bytes object is being created. .. c:function:: void PyBytes_ConcatAndDel(PyObject **bytes, PyObject *newpart) @@ -188,6 +199,10 @@ called with a non-bytes parameter. appended to *bytes*. This version releases the :term:`strong reference` to *newpart* (i.e. decrements its reference count). + .. note:: + If *newpart* implements the buffer protocol, then the buffer + must not be mutated while the new bytes object is being created. + .. c:function:: PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) @@ -206,6 +221,9 @@ called with a non-bytes parameter. .. versionadded:: 3.14 + .. note:: + If *iterable* objects implement the buffer protocol, then the buffers + must not be mutated while the new bytes object is being created. .. c:function:: int _PyBytes_Resize(PyObject **bytes, Py_ssize_t newsize) @@ -219,3 +237,212 @@ called with a non-bytes parameter. reallocation fails, the original bytes object at *\*bytes* is deallocated, *\*bytes* is set to ``NULL``, :exc:`MemoryError` is set, and ``-1`` is returned. + + .. soft-deprecated:: 3.15 + Use the :c:type:`PyBytesWriter` API instead. + + +.. c:function:: PyObject *PyBytes_Repr(PyObject *bytes, int smartquotes) + + Get the string representation of *bytes*. This function is currently used to + implement :meth:`!bytes.__repr__` in Python. + + This function does not do type checking; it is undefined behavior to pass + *bytes* as a non-bytes object or ``NULL``. + + If *smartquotes* is true, the representation will use a double-quoted string + instead of single-quoted string when single-quotes are present in *bytes*. + For example, the byte string ``'Python'`` would be represented as + ``b"'Python'"`` when *smartquotes* is true, or ``b'\'Python\''`` when it is + false. + + On success, this function returns a :term:`strong reference` to a + :class:`str` object containing the representation. On failure, this + returns ``NULL`` with an exception set. + + +.. c:function:: PyObject *PyBytes_DecodeEscape(const char *s, Py_ssize_t len, const char *errors, Py_ssize_t unicode, const char *recode_encoding) + + Unescape a backslash-escaped string *s*. *s* must not be ``NULL``. + *len* must be the size of *s*. + + *errors* must be one of ``"strict"``, ``"replace"``, or ``"ignore"``. If + *errors* is ``NULL``, then ``"strict"`` is used by default. + + On success, this function returns a :term:`strong reference` to a Python + :class:`bytes` object containing the unescaped string. On failure, this + function returns ``NULL`` with an exception set. + + .. versionchanged:: 3.9 + *unicode* and *recode_encoding* are now unused. + + +.. _pybyteswriter: + +PyBytesWriter +------------- + +The :c:type:`PyBytesWriter` API can be used to create a Python :class:`bytes` +object. + +.. versionadded:: 3.15 + +.. c:type:: PyBytesWriter + + A bytes writer instance. + + The API is **not thread safe**: a writer should only be used by a single + thread at the same time. + + The instance must be destroyed by :c:func:`PyBytesWriter_Finish` on + success, or :c:func:`PyBytesWriter_Discard` on error. + + +Create, Finish, Discard +^^^^^^^^^^^^^^^^^^^^^^^ + +.. c:function:: PyBytesWriter* PyBytesWriter_Create(Py_ssize_t size) + + Create a :c:type:`PyBytesWriter` to write *size* bytes. + + If *size* is greater than zero, allocate *size* bytes, and set the + writer size to *size*. The caller is responsible to write *size* + bytes using :c:func:`PyBytesWriter_GetData`. + This function does not overallocate. + + On error, set an exception and return ``NULL``. + + *size* must be positive or zero. + +.. c:function:: PyObject* PyBytesWriter_Finish(PyBytesWriter *writer) + + Finish a :c:type:`PyBytesWriter` created by + :c:func:`PyBytesWriter_Create`. + + On success, return a Python :class:`bytes` object. + On error, set an exception and return ``NULL``. + + The writer instance is invalid after the call in any case. + No API can be called on the writer after :c:func:`PyBytesWriter_Finish`. + +.. c:function:: PyObject* PyBytesWriter_FinishWithSize(PyBytesWriter *writer, Py_ssize_t size) + + Similar to :c:func:`PyBytesWriter_Finish`, but resize the writer + to *size* bytes before creating the :class:`bytes` object. + +.. c:function:: PyObject* PyBytesWriter_FinishWithPointer(PyBytesWriter *writer, void *buf) + + Similar to :c:func:`PyBytesWriter_Finish`, but resize the writer + using *buf* pointer before creating the :class:`bytes` object. + + Set an exception and return ``NULL`` if *buf* pointer is outside the + internal buffer bounds. + + Function pseudo-code:: + + Py_ssize_t size = (char*)buf - (char*)PyBytesWriter_GetData(writer); + return PyBytesWriter_FinishWithSize(writer, size); + +.. c:function:: void PyBytesWriter_Discard(PyBytesWriter *writer) + + Discard a :c:type:`PyBytesWriter` created by :c:func:`PyBytesWriter_Create`. + + Do nothing if *writer* is ``NULL``. + + The writer instance is invalid after the call. + No API can be called on the writer after :c:func:`PyBytesWriter_Discard`. + +High-level API +^^^^^^^^^^^^^^ + +.. c:function:: int PyBytesWriter_WriteBytes(PyBytesWriter *writer, const void *bytes, Py_ssize_t size) + + Grow the *writer* internal buffer by *size* bytes, + write *size* bytes of *bytes* at the *writer* end, + and add *size* to the *writer* size. + + If *size* is equal to ``-1``, call ``strlen(bytes)`` to get the + string length. + + On success, return ``0``. + On error, set an exception and return ``-1``. + +.. c:function:: int PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...) + + Similar to :c:func:`PyBytes_FromFormat`, but write the output directly at + the writer end. Grow the writer internal buffer on demand. Then add the + written size to the writer size. + + On success, return ``0``. + On error, set an exception and return ``-1``. + + +Getters +^^^^^^^ + +.. c:function:: Py_ssize_t PyBytesWriter_GetSize(PyBytesWriter *writer) + + Get the writer size. + + The function cannot fail. + +.. c:function:: void* PyBytesWriter_GetData(PyBytesWriter *writer) + + Get the writer data: start of the internal buffer. + + The pointer is valid until :c:func:`PyBytesWriter_Finish` or + :c:func:`PyBytesWriter_Discard` is called on *writer*. + + The function cannot fail. + + +Low-level API +^^^^^^^^^^^^^ + +.. c:function:: int PyBytesWriter_Resize(PyBytesWriter *writer, Py_ssize_t size) + + Resize the writer to *size* bytes. It can be used to enlarge or to + shrink the writer. + This function typically overallocates to achieve amortized performance when + resizing multiple times. + + Newly allocated bytes are left uninitialized. + + On success, return ``0``. + On error, set an exception and return ``-1``. + + *size* must be positive or zero. + +.. c:function:: int PyBytesWriter_Grow(PyBytesWriter *writer, Py_ssize_t grow) + + Resize the writer by adding *grow* bytes to the current writer size. + This function typically overallocates to achieve amortized performance when + resizing multiple times. + + Newly allocated bytes are left uninitialized. + + On success, return ``0``. + On error, set an exception and return ``-1``. + + *size* can be negative to shrink the writer. + +.. c:function:: void* PyBytesWriter_GrowAndUpdatePointer(PyBytesWriter *writer, Py_ssize_t size, void *buf) + + Similar to :c:func:`PyBytesWriter_Grow`, but update also the *buf* + pointer. + + The *buf* pointer is moved if the internal buffer is moved in memory. + The *buf* relative position within the internal buffer is left + unchanged. + + On error, set an exception and return ``NULL``. + + *buf* must not be ``NULL``. + + Function pseudo-code:: + + Py_ssize_t pos = (char*)buf - (char*)PyBytesWriter_GetData(writer); + if (PyBytesWriter_Grow(writer, size) < 0) { + return NULL; + } + return (char*)PyBytesWriter_GetData(writer) + pos; diff --git a/Doc/c-api/call.rst b/Doc/c-api/call.rst index 7198d6bc056eb4..9838879a528934 100644 --- a/Doc/c-api/call.rst +++ b/Doc/c-api/call.rst @@ -347,6 +347,8 @@ please see individual documentation for details. .. versionadded:: 3.9 +.. c:function:: PyObject* _PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) + :no-typesetting: .. c:function:: PyObject* PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) @@ -358,7 +360,12 @@ please see individual documentation for details. Return the result of the call on success, or raise an exception and return *NULL* on failure. - .. versionadded:: 3.9 + .. versionadded:: 3.8 as ``_PyObject_Vectorcall`` + + .. versionchanged:: 3.9 + + Renamed to the current name, without the leading underscore. + The old provisional name is :term:`soft deprecated`. .. c:function:: PyObject* PyObject_VectorcallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict) diff --git a/Doc/c-api/capsule.rst b/Doc/c-api/capsule.rst index 64dc4f5275b512..03a848d68ed7ab 100644 --- a/Doc/c-api/capsule.rst +++ b/Doc/c-api/capsule.rst @@ -15,13 +15,19 @@ Refer to :ref:`using-capsules` for more information on using these objects. .. c:type:: PyCapsule This subtype of :c:type:`PyObject` represents an opaque value, useful for C - extension modules who need to pass an opaque value (as a :c:expr:`void*` + extension modules which need to pass an opaque value (as a :c:expr:`void*` pointer) through Python code to other C code. It is often used to make a C function pointer defined in one module available to other modules, so the regular import mechanism can be used to access C APIs defined in dynamically loaded modules. +.. c:var:: PyTypeObject PyCapsule_Type + + The type object corresponding to capsule objects. This is the same object + as :class:`types.CapsuleType` in the Python layer. + + .. c:type:: PyCapsule_Destructor The type of a destructor callback for a capsule. Defined as:: diff --git a/Doc/c-api/cell.rst b/Doc/c-api/cell.rst index 61eb994c370946..2501ed9580df89 100644 --- a/Doc/c-api/cell.rst +++ b/Doc/c-api/cell.rst @@ -7,7 +7,7 @@ Cell Objects "Cell" objects are used to implement variables referenced by multiple scopes. For each such variable, a cell object is created to store the value; the local -variables of each stack frame that references the value contains a reference to +variables of each stack frame that references the value contain a reference to the cells from outer scopes which also use that variable. When the value is accessed, the value contained in the cell is used instead of the cell object itself. This de-referencing of the cell object requires support from the diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 717b0da8f87c7f..57b77f92a7d2e6 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -7,8 +7,6 @@ Code Objects ------------ -.. sectionauthor:: Jeffrey Yasskin - Code objects are a low-level detail of the CPython implementation. Each one represents a chunk of executable code that hasn't yet been bound into a function. @@ -69,13 +67,14 @@ bound into a function. The old name is deprecated, but will remain available until the signature changes again. +.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(...) + :no-typesetting: + .. c:function:: PyCodeObject* PyUnstable_Code_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *exceptiontable) Similar to :c:func:`PyUnstable_Code_New`, but with an extra "posonlyargcount" for positional-only arguments. The same caveats that apply to ``PyUnstable_Code_New`` also apply to this function. - .. index:: single: PyCode_NewWithPosOnlyArgs (C function) - .. versionadded:: 3.8 as ``PyCode_NewWithPosOnlyArgs`` .. versionchanged:: 3.11 @@ -211,6 +210,19 @@ bound into a function. .. versionadded:: 3.12 +.. c:function:: PyObject *PyCode_Optimize(PyObject *code, PyObject *consts, PyObject *names, PyObject *lnotab_obj) + + This is a function that does nothing. + + Prior to Python 3.10, this function would perform basic optimizations to a + code object. + + .. versionchanged:: 3.10 + This function now does nothing. + + .. soft-deprecated:: 3.13 + + .. _c_codeobject_flags: Code Object Flags @@ -287,9 +299,12 @@ These functions are part of the unstable C API tier: this functionality is a CPython implementation detail, and the API may change without deprecation warnings. +.. c:function:: Py_ssize_t _PyEval_RequestCodeExtraIndex(freefunc free) + :no-typesetting: + .. c:function:: Py_ssize_t PyUnstable_Eval_RequestCodeExtraIndex(freefunc free) - Return a new an opaque index value used to adding data to code objects. + Return a new opaque index value used to adding data to code objects. You generally call this function once (per interpreter) and use the result with ``PyCode_GetExtra`` and ``PyCode_SetExtra`` to manipulate @@ -299,8 +314,6 @@ may change without deprecation warnings. *free* will be called on non-``NULL`` data stored under the new index. Use :c:func:`Py_DecRef` when storing :c:type:`PyObject`. - .. index:: single: _PyEval_RequestCodeExtraIndex (C function) - .. versionadded:: 3.6 as ``_PyEval_RequestCodeExtraIndex`` .. versionchanged:: 3.12 @@ -309,6 +322,9 @@ may change without deprecation warnings. The old private name is deprecated, but will be available until the API changes. +.. c:function:: int _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra) + :no-typesetting: + .. c:function:: int PyUnstable_Code_GetExtra(PyObject *code, Py_ssize_t index, void **extra) Set *extra* to the extra data stored under the given index. @@ -317,8 +333,6 @@ may change without deprecation warnings. If no data was set under the index, set *extra* to ``NULL`` and return 0 without setting an exception. - .. index:: single: _PyCode_GetExtra (C function) - .. versionadded:: 3.6 as ``_PyCode_GetExtra`` .. versionchanged:: 3.12 @@ -327,13 +341,14 @@ may change without deprecation warnings. The old private name is deprecated, but will be available until the API changes. +.. c:function:: int _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra) + :no-typesetting: + .. c:function:: int PyUnstable_Code_SetExtra(PyObject *code, Py_ssize_t index, void *extra) Set the extra data stored under the given index to *extra*. Return 0 on success. Set an exception and return -1 on failure. - .. index:: single: _PyCode_SetExtra (C function) - .. versionadded:: 3.6 as ``_PyCode_SetExtra`` .. versionchanged:: 3.12 diff --git a/Doc/c-api/codec.rst b/Doc/c-api/codec.rst index 8ae5c4fecd6248..35ee048bd5fa9f 100644 --- a/Doc/c-api/codec.rst +++ b/Doc/c-api/codec.rst @@ -7,7 +7,7 @@ Codec registry and support functions Register a new codec search function. - As side effect, this tries to load the :mod:`!encodings` package, if not yet + As a side effect, this tries to load the :mod:`!encodings` package, if not yet done, to make sure that it is always first in the list of search functions. .. c:function:: int PyCodec_Unregister(PyObject *search_function) @@ -39,7 +39,7 @@ Codec registry and support functions *object* is passed through the decoder function found for the given *encoding* using the error handling method defined by *errors*. *errors* may be ``NULL`` to use the default method defined for the codec. Raises a - :exc:`LookupError` if no encoder can be found. + :exc:`LookupError` if no decoder can be found. Codec lookup API @@ -129,3 +129,13 @@ Registry API for Unicode encoding error handlers Replace the unicode encode error with ``\N{...}`` escapes. .. versionadded:: 3.5 + + +Codec utility variables +----------------------- + +.. c:var:: const char *Py_hexdigits + + A string constant containing the lowercase hexadecimal digits: ``"0123456789abcdef"``. + + .. versionadded:: 3.3 diff --git a/Doc/c-api/complex.rst b/Doc/c-api/complex.rst index 16bd79475dc1e6..10f96c7cb75e88 100644 --- a/Doc/c-api/complex.rst +++ b/Doc/c-api/complex.rst @@ -3,92 +3,24 @@ .. _complexobjects: Complex Number Objects ----------------------- +====================== .. index:: pair: object; complex number -Python's complex number objects are implemented as two distinct types when -viewed from the C API: one is the Python object exposed to Python programs, and -the other is a C structure which represents the actual complex number value. -The API provides functions for working with both. - - -Complex Numbers as C Structures -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Note that the functions which accept these structures as parameters and return -them as results do so *by value* rather than dereferencing them through -pointers. This is consistent throughout the API. - - -.. c:type:: Py_complex - - The C structure which corresponds to the value portion of a Python complex - number object. Most of the functions for dealing with complex number objects - use structures of this type as input or output values, as appropriate. - - .. c:member:: double real - double imag - - The structure is defined as:: - - typedef struct { - double real; - double imag; - } Py_complex; - - -.. c:function:: Py_complex _Py_c_sum(Py_complex left, Py_complex right) - - Return the sum of two complex numbers, using the C :c:type:`Py_complex` - representation. - - -.. c:function:: Py_complex _Py_c_diff(Py_complex left, Py_complex right) - - Return the difference between two complex numbers, using the C - :c:type:`Py_complex` representation. - - -.. c:function:: Py_complex _Py_c_neg(Py_complex num) - - Return the negation of the complex number *num*, using the C - :c:type:`Py_complex` representation. - - -.. c:function:: Py_complex _Py_c_prod(Py_complex left, Py_complex right) - - Return the product of two complex numbers, using the C :c:type:`Py_complex` - representation. - - -.. c:function:: Py_complex _Py_c_quot(Py_complex dividend, Py_complex divisor) - - Return the quotient of two complex numbers, using the C :c:type:`Py_complex` - representation. - - If *divisor* is null, this method returns zero and sets - :c:data:`errno` to :c:macro:`!EDOM`. - - -.. c:function:: Py_complex _Py_c_pow(Py_complex num, Py_complex exp) - - Return the exponentiation of *num* by *exp*, using the C :c:type:`Py_complex` - representation. - - If *num* is null and *exp* is not a positive real number, - this method returns zero and sets :c:data:`errno` to :c:macro:`!EDOM`. - - Set :c:data:`errno` to :c:macro:`!ERANGE` on overflows. +.. c:type:: PyComplexObject -Complex Numbers as Python Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + This subtype of :c:type:`PyObject` represents a Python complex number object. + .. c:member:: Py_complex cval -.. c:type:: PyComplexObject + The complex number value, using the C :c:type:`Py_complex` representation. - This subtype of :c:type:`PyObject` represents a Python complex number object. + .. deprecated-removed:: 3.15 3.20 + Use :c:func:`PyComplex_AsCComplex` and + :c:func:`PyComplex_FromCComplex` to convert a + Python complex number to/from the C :c:type:`Py_complex` + representation. .. c:var:: PyTypeObject PyComplex_Type @@ -109,12 +41,6 @@ Complex Numbers as Python Objects :c:type:`PyComplexObject`. This function always succeeds. -.. c:function:: PyObject* PyComplex_FromCComplex(Py_complex v) - - Create a new Python complex number object from a C :c:type:`Py_complex` value. - Return ``NULL`` with an exception set on error. - - .. c:function:: PyObject* PyComplex_FromDoubles(double real, double imag) Return a new :c:type:`PyComplexObject` object from *real* and *imag*. @@ -153,6 +79,29 @@ Complex Numbers as Python Objects .. versionchanged:: 3.13 Use :meth:`~object.__complex__` if available. + +.. c:type:: Py_complex + + This C structure defines an export format for a Python complex + number object. + + .. c:member:: double real + double imag + + The structure is defined as:: + + typedef struct { + double real; + double imag; + } Py_complex; + + +.. c:function:: PyObject* PyComplex_FromCComplex(Py_complex v) + + Create a new Python complex number object from a C :c:type:`Py_complex` value. + Return ``NULL`` with an exception set on error. + + .. c:function:: Py_complex PyComplex_AsCComplex(PyObject *op) Return the :c:type:`Py_complex` value of the complex number *op*. @@ -169,3 +118,82 @@ Complex Numbers as Python Objects .. versionchanged:: 3.8 Use :meth:`~object.__index__` if available. + + +Complex Numbers as C Structures +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The API also provides functions for working with complex numbers, using the +:c:type:`Py_complex` representation. Note that the functions which accept +these structures as parameters and return them as results do so *by value* +rather than dereferencing them through pointers. + +Please note, that these functions are :term:`soft deprecated` since Python +3.15. Avoid using this API in a new code to do complex arithmetic: either use +the :ref:`Number Protocol ` API or use native complex types, like +:c:expr:`double complex`. + + +.. c:function:: Py_complex _Py_c_sum(Py_complex left, Py_complex right) + + Return the sum of two complex numbers, using the C :c:type:`Py_complex` + representation. + + .. deprecated:: 3.15 + + +.. c:function:: Py_complex _Py_c_diff(Py_complex left, Py_complex right) + + Return the difference between two complex numbers, using the C + :c:type:`Py_complex` representation. + + .. deprecated:: 3.15 + + +.. c:function:: Py_complex _Py_c_neg(Py_complex num) + + Return the negation of the complex number *num*, using the C + :c:type:`Py_complex` representation. + + .. deprecated:: 3.15 + + +.. c:function:: Py_complex _Py_c_prod(Py_complex left, Py_complex right) + + Return the product of two complex numbers, using the C :c:type:`Py_complex` + representation. + + .. deprecated:: 3.15 + + +.. c:function:: Py_complex _Py_c_quot(Py_complex dividend, Py_complex divisor) + + Return the quotient of two complex numbers, using the C :c:type:`Py_complex` + representation. + + If *divisor* is null, this method returns zero and sets + :c:data:`errno` to :c:macro:`!EDOM`. + + .. deprecated:: 3.15 + + +.. c:function:: Py_complex _Py_c_pow(Py_complex num, Py_complex exp) + + Return the exponentiation of *num* by *exp*, using the C :c:type:`Py_complex` + representation. + + If *num* is null and *exp* is not a positive real number, + this method returns zero and sets :c:data:`errno` to :c:macro:`!EDOM`. + + Set :c:data:`errno` to :c:macro:`!ERANGE` on overflows. + + .. deprecated:: 3.15 + + +.. c:function:: double _Py_c_abs(Py_complex num) + + Return the absolute value of the complex number *num*. + + Set :c:data:`errno` to :c:macro:`!ERANGE` on overflows. + + .. deprecated:: 3.15 diff --git a/Doc/c-api/concrete.rst b/Doc/c-api/concrete.rst index 880f7b15ce68e8..3f38411a52de6b 100644 --- a/Doc/c-api/concrete.rst +++ b/Doc/c-api/concrete.rst @@ -109,11 +109,21 @@ Other Objects descriptor.rst slice.rst memoryview.rst + picklebuffer.rst weakref.rst capsule.rst + sentinel.rst frame.rst gen.rst coro.rst contextvars.rst - datetime.rst typehints.rst + + +C API for extension modules +=========================== + +.. toctree:: + + curses.rst + datetime.rst diff --git a/Doc/c-api/conversion.rst b/Doc/c-api/conversion.rst index c92ef4c653a675..f7c8ef8b22b955 100644 --- a/Doc/c-api/conversion.rst +++ b/Doc/c-api/conversion.rst @@ -41,7 +41,7 @@ The return value (*rv*) for these functions should be interpreted as follows: ``rv + 1`` bytes would have been needed to succeed. ``str[size-1]`` is ``'\0'`` in this case. -* When ``rv < 0``, "something bad happened." ``str[size-1]`` is ``'\0'`` in +* When ``rv < 0``, the output conversion failed and ``str[size-1]`` is ``'\0'`` in this case too, but the rest of *str* is undefined. The exact cause of the error depends on the underlying platform. @@ -105,7 +105,7 @@ The following functions provide locale-independent string to number conversions. If ``s`` represents a value that is too large to store in a float (for example, ``"1e500"`` is such a string on many platforms) then - if ``overflow_exception`` is ``NULL`` return ``Py_INFINITY`` (with + if ``overflow_exception`` is ``NULL`` return :c:macro:`!INFINITY` (with an appropriate sign) and don't set any exception. Otherwise, ``overflow_exception`` must point to a Python exception object; raise that exception and return ``-1.0``. In both cases, set @@ -128,22 +128,46 @@ The following functions provide locale-independent string to number conversions. must be 0 and is ignored. The ``'r'`` format code specifies the standard :func:`repr` format. - *flags* can be zero or more of the values ``Py_DTSF_SIGN``, - ``Py_DTSF_ADD_DOT_0``, or ``Py_DTSF_ALT``, or-ed together: + *flags* can be zero or more of the following values or-ed together: - * ``Py_DTSF_SIGN`` means to always precede the returned string with a sign - character, even if *val* is non-negative. + .. c:namespace:: NULL - * ``Py_DTSF_ADD_DOT_0`` means to ensure that the returned string will not look - like an integer. + .. c:macro:: Py_DTSF_SIGN - * ``Py_DTSF_ALT`` means to apply "alternate" formatting rules. See the - documentation for the :c:func:`PyOS_snprintf` ``'#'`` specifier for - details. + Always precede the returned string with a sign + character, even if *val* is non-negative. - If *ptype* is non-``NULL``, then the value it points to will be set to one of - ``Py_DTST_FINITE``, ``Py_DTST_INFINITE``, or ``Py_DTST_NAN``, signifying that - *val* is a finite number, an infinite number, or not a number, respectively. + .. c:macro:: Py_DTSF_ADD_DOT_0 + + Ensure that the returned string will not look like an integer. + + .. c:macro:: Py_DTSF_ALT + + Apply "alternate" formatting rules. + See the documentation for the :c:func:`PyOS_snprintf` ``'#'`` specifier for + details. + + .. c:macro:: Py_DTSF_NO_NEG_0 + + Negative zero is converted to positive zero. + + .. versionadded:: 3.11 + + If *ptype* is non-``NULL``, then the value it points to will be set to one + of the following constants depending on the type of *val*: + + .. list-table:: + :header-rows: 1 + :align: left + + * - *\*ptype* + - type of *val* + * - .. c:macro:: Py_DTST_FINITE + - finite number + * - .. c:macro:: Py_DTST_INFINITE + - infinite number + * - .. c:macro:: Py_DTST_NAN + - not a number The return value is a pointer to *buffer* with the converted string or ``NULL`` if the conversion failed. The caller is responsible for freeing the @@ -152,13 +176,85 @@ The following functions provide locale-independent string to number conversions. .. versionadded:: 3.1 -.. c:function:: int PyOS_stricmp(const char *s1, const char *s2) +.. c:function:: int PyOS_mystricmp(const char *str1, const char *str2) + int PyOS_mystrnicmp(const char *str1, const char *str2, Py_ssize_t size) + + Case insensitive comparison of strings. These functions work almost + identically to :c:func:`!strcmp` and :c:func:`!strncmp` (respectively), + except that they ignore the case of ASCII characters. + + Return ``0`` if the strings are equal, a negative value if *str1* sorts + lexicographically before *str2*, or a positive value if it sorts after. + + In the *str1* or *str2* arguments, a NUL byte marks the end of the string. + For :c:func:`!PyOS_mystrnicmp`, the *size* argument gives the maximum size + of the string, as if NUL was present at the index given by *size*. + + These functions do not use the locale. + + +.. c:function:: int PyOS_stricmp(const char *str1, const char *str2) + int PyOS_strnicmp(const char *str1, const char *str2, Py_ssize_t size) + + Case insensitive comparison of strings. + + On Windows, these are aliases of :c:func:`!stricmp` and :c:func:`!strnicmp`, + respectively. + + On other platforms, they are aliases of :c:func:`PyOS_mystricmp` and + :c:func:`PyOS_mystrnicmp`, respectively. + + +Character classification and conversion +======================================= + +The following macros provide locale-independent (unlike the C standard library +``ctype.h``) character classification and conversion. +The argument must be a signed or unsigned :c:expr:`char`. + + +.. c:macro:: Py_ISALNUM(c) + + Return true if the character *c* is an alphanumeric character. + + +.. c:macro:: Py_ISALPHA(c) + + Return true if the character *c* is an alphabetic character (``a-z`` and ``A-Z``). + + +.. c:macro:: Py_ISDIGIT(c) + + Return true if the character *c* is a decimal digit (``0-9``). + + +.. c:macro:: Py_ISLOWER(c) + + Return true if the character *c* is a lowercase ASCII letter (``a-z``). + + +.. c:macro:: Py_ISUPPER(c) + + Return true if the character *c* is an uppercase ASCII letter (``A-Z``). + + +.. c:macro:: Py_ISSPACE(c) + + Return true if the character *c* is a whitespace character (space, tab, + carriage return, newline, vertical tab, or form feed). + + +.. c:macro:: Py_ISXDIGIT(c) + + Return true if the character *c* is a hexadecimal digit (``0-9``, ``a-f``, and + ``A-F``). + + +.. c:macro:: Py_TOLOWER(c) - Case insensitive comparison of strings. The function works almost - identically to :c:func:`!strcmp` except that it ignores the case. + Return the lowercase equivalent of the character *c*. -.. c:function:: int PyOS_strnicmp(const char *s1, const char *s2, Py_ssize_t size) +.. c:macro:: Py_TOUPPER(c) - Case insensitive comparison of strings. The function works almost - identically to :c:func:`!strncmp` except that it ignores the case. + Return the uppercase equivalent of the character *c*. diff --git a/Doc/c-api/coro.rst b/Doc/c-api/coro.rst index caa855a10d20ca..06422fb63f34f1 100644 --- a/Doc/c-api/coro.rst +++ b/Doc/c-api/coro.rst @@ -33,3 +33,9 @@ return. with ``__name__`` and ``__qualname__`` set to *name* and *qualname*. A reference to *frame* is stolen by this function. The *frame* argument must not be ``NULL``. + + .. deprecated-removed:: 3.16 3.18 + + This function has not been used since 3.10. + It is also impossible to construct a proper *frame* + object to call this function. diff --git a/Doc/c-api/curses.rst b/Doc/c-api/curses.rst new file mode 100644 index 00000000000000..5a1697c43cc969 --- /dev/null +++ b/Doc/c-api/curses.rst @@ -0,0 +1,138 @@ +.. highlight:: c + +Curses C API +------------ + +:mod:`curses` exposes a small C interface for extension modules. +Consumers must include the header file :file:`py_curses.h` (which is not +included by default by :file:`Python.h`) and :c:func:`import_curses` must +be invoked, usually as part of the module initialisation function, to populate +:c:var:`PyCurses_API`. + +.. warning:: + + Neither the C API nor the pure Python :mod:`curses` module are compatible + with subinterpreters. + +.. c:macro:: import_curses() + + Import the curses C API. The macro does not need a semi-colon to be called. + + On success, populate the :c:var:`PyCurses_API` pointer. + + On failure, set :c:var:`PyCurses_API` to NULL and set an exception. + The caller must check if an error occurred via :c:func:`PyErr_Occurred`: + + .. code-block:: + + import_curses(); // semi-colon is optional but recommended + if (PyErr_Occurred()) { /* cleanup */ } + + +.. c:var:: void **PyCurses_API + + Dynamically allocated object containing the curses C API. + This variable is only available once :c:macro:`import_curses` succeeds. + + ``PyCurses_API[0]`` corresponds to :c:data:`PyCursesWindow_Type`. + + ``PyCurses_API[1]``, ``PyCurses_API[2]``, and ``PyCurses_API[3]`` + are pointers to predicate functions of type ``int (*)(void)``. + + When called, these predicates return whether :func:`curses.setupterm`, + :func:`curses.initscr`, and :func:`curses.start_color` have been called + respectively. + + See also the convenience macros :c:macro:`PyCursesSetupTermCalled`, + :c:macro:`PyCursesInitialised`, and :c:macro:`PyCursesInitialisedColor`. + + .. note:: + + The number of entries in this structure is subject to changes. + Consider using :c:macro:`PyCurses_API_pointers` to check if + new fields are available or not. + + +.. c:macro:: PyCurses_API_pointers + + The number of accessible fields (``4``) in :c:var:`PyCurses_API`. + This number is incremented whenever new fields are added. + + +.. c:var:: PyTypeObject PyCursesWindow_Type + + The :ref:`heap type ` corresponding to :class:`curses.window`. + + +.. c:function:: int PyCursesWindow_Check(PyObject *op) + + Return true if *op* is a :class:`curses.window` instance, false otherwise. + + +The following macros are convenience macros expanding into C statements. +In particular, they can only be used as ``macro;`` or ``macro``, but not +``macro()`` or ``macro();``. + +.. c:macro:: PyCursesSetupTermCalled + + Macro checking if :func:`curses.setupterm` has been called. + + The macro expansion is roughly equivalent to: + + .. code-block:: + + { + typedef int (*predicate_t)(void); + predicate_t was_setupterm_called = (predicate_t)PyCurses_API[1]; + if (!was_setupterm_called()) { + return NULL; + } + } + + +.. c:macro:: PyCursesInitialised + + Macro checking if :func:`curses.initscr` has been called. + + The macro expansion is roughly equivalent to: + + .. code-block:: + + { + typedef int (*predicate_t)(void); + predicate_t was_initscr_called = (predicate_t)PyCurses_API[2]; + if (!was_initscr_called()) { + return NULL; + } + } + + +.. c:macro:: PyCursesInitialisedColor + + Macro checking if :func:`curses.start_color` has been called. + + The macro expansion is roughly equivalent to: + + .. code-block:: + + { + typedef int (*predicate_t)(void); + predicate_t was_start_color_called = (predicate_t)PyCurses_API[3]; + if (!was_start_color_called()) { + return NULL; + } + } + + +Internal data +------------- + +The following objects are exposed by the C API but should be considered +internal-only. + +.. c:macro:: PyCurses_CAPSULE_NAME + + Name of the curses capsule to pass to :c:func:`PyCapsule_Import`. + + Internal usage only. Use :c:macro:`import_curses` instead. + diff --git a/Doc/c-api/datetime.rst b/Doc/c-api/datetime.rst index d2d4d5309c7098..d7b4e116c49e35 100644 --- a/Doc/c-api/datetime.rst +++ b/Doc/c-api/datetime.rst @@ -8,11 +8,51 @@ DateTime Objects Various date and time objects are supplied by the :mod:`datetime` module. Before using any of these functions, the header file :file:`datetime.h` must be included in your source (note that this is not included by :file:`Python.h`), -and the macro :c:macro:`!PyDateTime_IMPORT` must be invoked, usually as part of +and the macro :c:macro:`PyDateTime_IMPORT` must be invoked, usually as part of the module initialisation function. The macro puts a pointer to a C structure -into a static variable, :c:data:`!PyDateTimeAPI`, that is used by the following +into a static variable, :c:data:`PyDateTimeAPI`, that is used by the following macros. +.. c:macro:: PyDateTime_IMPORT() + + Import the datetime C API. + + On success, populate the :c:var:`PyDateTimeAPI` pointer. + On failure, set :c:var:`PyDateTimeAPI` to ``NULL`` and set an exception. + The caller must check if an error occurred via :c:func:`PyErr_Occurred`: + + .. code-block:: + + PyDateTime_IMPORT; + if (PyErr_Occurred()) { /* cleanup */ } + + .. warning:: + + This is not compatible with subinterpreters. + + .. versionchanged:: 3.15 + + This macro is now thread safe. + +.. c:type:: PyDateTime_CAPI + + Structure containing the fields for the datetime C API. + + The fields of this structure are private and subject to change. + + Do not use this directly; prefer ``PyDateTime_*`` APIs instead. + +.. c:var:: PyDateTime_CAPI *PyDateTimeAPI + + Dynamically allocated object containing the datetime C API. + + This variable is only available once :c:macro:`PyDateTime_IMPORT` succeeds. + + .. versionchanged:: 3.15 + + This variable should not be accessed directly as direct access is not thread-safe. + Use :c:func:`PyDateTime_IMPORT` instead. + .. c:type:: PyDateTime_Date This subtype of :c:type:`PyObject` represents a Python date object. @@ -46,7 +86,7 @@ macros. .. c:var:: PyTypeObject PyDateTime_DeltaType - This instance of :c:type:`PyTypeObject` represents Python type for + This instance of :c:type:`PyTypeObject` represents the Python type for the difference between two datetime values; it is the same object as :class:`datetime.timedelta` in the Python layer. @@ -325,3 +365,16 @@ Macros for the convenience of modules implementing the DB API: Create and return a new :class:`datetime.date` object given an argument tuple suitable for passing to :meth:`datetime.date.fromtimestamp`. + + +Internal data +------------- + +The following symbols are exposed by the C API but should be considered +internal-only. + +.. c:macro:: PyDateTime_CAPSULE_NAME + + Name of the datetime capsule to pass to :c:func:`PyCapsule_Import`. + + Internal usage only. Use :c:macro:`PyDateTime_IMPORT` instead. diff --git a/Doc/c-api/descriptor.rst b/Doc/c-api/descriptor.rst index b32c113e5f0457..539c4610ce4f5b 100644 --- a/Doc/c-api/descriptor.rst +++ b/Doc/c-api/descriptor.rst @@ -8,33 +8,200 @@ Descriptor Objects "Descriptors" are objects that describe some attribute of an object. They are found in the dictionary of type objects. -.. XXX document these! +.. c:function:: PyObject* PyDescr_NewGetSet(PyTypeObject *type, struct PyGetSetDef *getset) -.. c:var:: PyTypeObject PyProperty_Type + Create a new get-set descriptor for extension type *type* from the + :c:type:`PyGetSetDef` structure *getset*. - The type object for the built-in descriptor types. + Get-set descriptors expose attributes implemented by C getter and setter + functions rather than stored directly in the instance. This is the same kind + of descriptor created for entries in :c:member:`~PyTypeObject.tp_getset`, and + it appears in Python as a :class:`types.GetSetDescriptorType` object. + On success, return a :term:`strong reference` to the descriptor. Return + ``NULL`` with an exception set on failure. -.. c:function:: PyObject* PyDescr_NewGetSet(PyTypeObject *type, struct PyGetSetDef *getset) +.. c:function:: PyObject* PyDescr_NewMember(PyTypeObject *type, struct PyMemberDef *member) + + Create a new member descriptor for extension type *type* from the + :c:type:`PyMemberDef` structure *member*. + + Member descriptors expose fields in the type's C struct as Python + attributes. This is the same kind of descriptor created for entries in + :c:member:`~PyTypeObject.tp_members`, and it appears in Python as a + :class:`types.MemberDescriptorType` object. + + On success, return a :term:`strong reference` to the descriptor. Return + ``NULL`` with an exception set on failure. + +.. c:var:: PyTypeObject PyMemberDescr_Type + + The type object for member descriptor objects created from + :c:type:`PyMemberDef` structures. These descriptors expose fields of a + C struct as attributes on a type, and correspond + to :class:`types.MemberDescriptorType` objects in Python. -.. c:function:: PyObject* PyDescr_NewMember(PyTypeObject *type, struct PyMemberDef *meth) + +.. c:var:: PyTypeObject PyGetSetDescr_Type + + The type object for get/set descriptor objects created from + :c:type:`PyGetSetDef` structures. These descriptors implement attributes + whose value is computed by C getter and setter functions, and are used + for many built-in type attributes. They correspond to + :class:`types.GetSetDescriptorType` objects in Python. .. c:function:: PyObject* PyDescr_NewMethod(PyTypeObject *type, struct PyMethodDef *meth) + Create a new method descriptor for extension type *type* from the + :c:type:`PyMethodDef` structure *meth*. + + Method descriptors expose C functions as methods on a type. This is the same + kind of descriptor created for entries in + :c:member:`~PyTypeObject.tp_methods`, and it appears in Python as a + :class:`types.MethodDescriptorType` object. + + On success, return a :term:`strong reference` to the descriptor. Return + ``NULL`` with an exception set on failure. + +.. c:var:: PyTypeObject PyMethodDescr_Type + + The type object for method descriptor objects created from + :c:type:`PyMethodDef` structures. These descriptors expose C functions as + methods on a type, and correspond to :class:`types.MethodDescriptorType` + objects in Python. + -.. c:function:: PyObject* PyDescr_NewWrapper(PyTypeObject *type, struct wrapperbase *wrapper, void *wrapped) +.. c:struct:: wrapperbase + + Describes a slot wrapper used by :c:func:`PyDescr_NewWrapper`. + + Each ``wrapperbase`` record stores the Python-visible name and metadata for a + special method implemented by a type slot, together with the wrapper + function used to adapt that slot to Python's calling convention. + +.. c:function:: PyObject* PyDescr_NewWrapper(PyTypeObject *type, struct wrapperbase *base, void *wrapped) + + Create a new wrapper descriptor for extension type *type* from the + :c:struct:`wrapperbase` structure *base* and the wrapped slot function + pointer + *wrapped*. + + Wrapper descriptors expose special methods implemented by type slots. This + is the same kind of descriptor that CPython creates for slot-based special + methods such as ``__repr__`` or ``__add__``, and it appears in Python as a + :class:`types.WrapperDescriptorType` object. + + On success, return a :term:`strong reference` to the descriptor. Return + ``NULL`` with an exception set on failure. + +.. c:var:: PyTypeObject PyWrapperDescr_Type + + The type object for wrapper descriptor objects created by + :c:func:`PyDescr_NewWrapper` and :c:func:`PyWrapper_New`. Wrapper + descriptors are used internally to expose special methods implemented + via wrapper structures, and appear in Python as + :class:`types.WrapperDescriptorType` objects. .. c:function:: PyObject* PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method) + Create a new class method descriptor for extension type *type* from the + :c:type:`PyMethodDef` structure *method*. + + Class method descriptors expose C methods that receive the class rather than + an instance when accessed. This is the same kind of descriptor created for + ``METH_CLASS`` entries in :c:member:`~PyTypeObject.tp_methods`, and it + appears in Python as a :class:`types.ClassMethodDescriptorType` object. + + On success, return a :term:`strong reference` to the descriptor. Return + ``NULL`` with an exception set on failure. .. c:function:: int PyDescr_IsData(PyObject *descr) - Return non-zero if the descriptor objects *descr* describes a data attribute, or + Return non-zero if the descriptor object *descr* describes a data attribute, or ``0`` if it describes a method. *descr* must be a descriptor object; there is no error checking. -.. c:function:: PyObject* PyWrapper_New(PyObject *, PyObject *) +.. c:function:: PyObject* PyWrapper_New(PyObject *d, PyObject *self) + + Create a new bound wrapper object from the wrapper descriptor *d* and the + instance *self*. + + This is the bound form of a wrapper descriptor created by + :c:func:`PyDescr_NewWrapper`. CPython creates these objects when a slot + wrapper is accessed through an instance, and they appear in Python as + :class:`types.MethodWrapperType` objects. + + On success, return a :term:`strong reference` to the wrapper object. Return + ``NULL`` with an exception set on failure. + +.. c:macro:: PyDescr_COMMON + + This is a macro including the common fields for a + descriptor object. + + This was included in Python's C API by mistake; do not use it in extensions. + For creating custom descriptor objects, create a class implementing the + descriptor protocol (:c:member:`~PyTypeObject.tp_descr_get` and + :c:member:`~PyTypeObject.tp_descr_set`). + + .. soft-deprecated:: 3.15 + + +Built-in descriptors +^^^^^^^^^^^^^^^^^^^^ + +.. c:var:: PyTypeObject PyProperty_Type + + The type object for property objects. This is the same object as + :class:`property` in the Python layer. + + +.. c:var:: PyTypeObject PySuper_Type + + The type object for super objects. This is the same object as + :class:`super` in the Python layer. + + +.. c:var:: PyTypeObject PyClassMethod_Type + + The type of class method objects. This is the same object as + :class:`classmethod` in the Python layer. + + +.. c:var:: PyTypeObject PyClassMethodDescr_Type + + The type object for C-level class method descriptor objects. + This is the type of the descriptors created for :func:`classmethod` defined + in C extension types, and corresponds to + :class:`types.ClassMethodDescriptorType` objects in Python. + + +.. c:function:: PyObject *PyClassMethod_New(PyObject *callable) + + Create a new :class:`classmethod` object wrapping *callable*. + *callable* must be a callable object and must not be ``NULL``. + + On success, this function returns a :term:`strong reference` to a new class + method descriptor. On failure, this function returns ``NULL`` with an + exception set. + + +.. c:var:: PyTypeObject PyStaticMethod_Type + + The type of static method objects. This is the same object as + :class:`staticmethod` in the Python layer. + + +.. c:function:: PyObject *PyStaticMethod_New(PyObject *callable) + + Create a new :class:`staticmethod` object wrapping *callable*. + *callable* must be a callable object and must not be ``NULL``. + + On success, this function returns a :term:`strong reference` to a new static + method descriptor. On failure, this function returns ``NULL`` with an + exception set. + diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index e55c5c80cb83c0..556113a97bf772 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -2,7 +2,7 @@ .. _dictobjects: -Dictionary Objects +Dictionary objects ------------------ .. index:: pair: object; dictionary @@ -42,18 +42,48 @@ Dictionary Objects enforces read-only behavior. This is normally used to create a view to prevent modification of the dictionary for non-dynamic class types. + The first argument can be a :class:`dict`, a :class:`frozendict`, or a + mapping. + + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + + +.. c:var:: PyTypeObject PyDictProxy_Type + + The type object for mapping proxy objects created by + :c:func:`PyDictProxy_New` and for the read-only ``__dict__`` attribute + of many built-in types. A :c:type:`PyDictProxy_Type` instance provides a + dynamic, read-only view of an underlying dictionary: changes to the + underlying dictionary are reflected in the proxy, but the proxy itself + does not support mutation operations. This corresponds to + :class:`types.MappingProxyType` in Python. + .. c:function:: void PyDict_Clear(PyObject *p) Empty an existing dictionary of all key-value pairs. + Do nothing if the argument is not a :class:`dict` or a :class:`!dict` + subclass. + .. c:function:: int PyDict_Contains(PyObject *p, PyObject *key) - Determine if dictionary *p* contains *key*. If an item in *p* is matches + Determine if dictionary *p* contains *key*. If an item in *p* matches *key*, return ``1``, otherwise return ``0``. On error, return ``-1``. This is equivalent to the Python expression ``key in p``. + The first argument can be a :class:`dict` or a :class:`frozendict`. + + .. note:: + + The operation is atomic on :term:`free threading ` + when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`. + + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + .. c:function:: int PyDict_ContainsString(PyObject *p, const char *key) @@ -61,14 +91,18 @@ Dictionary Objects :c:expr:`const char*` UTF-8 encoded bytes string, rather than a :c:expr:`PyObject*`. + The first argument can be a :class:`dict` or a :class:`frozendict`. + .. versionadded:: 3.13 + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + .. c:function:: PyObject* PyDict_Copy(PyObject *p) Return a new dictionary that contains the same key-value pairs as *p*. - .. c:function:: int PyDict_SetItem(PyObject *p, PyObject *key, PyObject *val) Insert *val* into the dictionary *p* with a key of *key*. *key* must be @@ -76,6 +110,11 @@ Dictionary Objects ``0`` on success or ``-1`` on failure. This function *does not* steal a reference to *val*. + .. note:: + + The operation is atomic on :term:`free threading ` + when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`. + .. c:function:: int PyDict_SetItemString(PyObject *p, const char *key, PyObject *val) @@ -91,6 +130,11 @@ Dictionary Objects If *key* is not in the dictionary, :exc:`KeyError` is raised. Return ``0`` on success or ``-1`` on failure. + .. note:: + + The operation is atomic on :term:`free threading ` + when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`. + .. c:function:: int PyDict_DelItemString(PyObject *p, const char *key) @@ -107,10 +151,20 @@ Dictionary Objects * If the key is present, set *\*result* to a new :term:`strong reference` to the value and return ``1``. * If the key is missing, set *\*result* to ``NULL`` and return ``0``. - * On error, raise an exception and return ``-1``. + * On error, raise an exception, set *\*result* to ``NULL`` and return ``-1``. + + The first argument can be a :class:`dict` or a :class:`frozendict`. + + .. note:: + + The operation is atomic on :term:`free threading ` + when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`. .. versionadded:: 3.13 + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + See also the :c:func:`PyObject_GetItem` function. @@ -120,16 +174,28 @@ Dictionary Objects has a key *key*. Return ``NULL`` if the key *key* is missing *without* setting an exception. + The first argument can be a :class:`dict` or a :class:`frozendict`. + .. note:: Exceptions that occur while this calls :meth:`~object.__hash__` and :meth:`~object.__eq__` methods are silently ignored. Prefer the :c:func:`PyDict_GetItemWithError` function instead. + .. note:: + + In the :term:`free-threaded build`, the returned + :term:`borrowed reference` may become invalid if another thread modifies + the dictionary concurrently. Prefer :c:func:`PyDict_GetItemRef`, which + returns a :term:`strong reference`. + .. versionchanged:: 3.10 Calling this API without an :term:`attached thread state` had been allowed for historical reason. It is no longer allowed. + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + .. c:function:: PyObject* PyDict_GetItemWithError(PyObject *p, PyObject *key) @@ -138,6 +204,16 @@ Dictionary Objects occurred. Return ``NULL`` **without** an exception set if the key wasn't present. + .. note:: + + In the :term:`free-threaded build`, the returned + :term:`borrowed reference` may become invalid if another thread modifies + the dictionary concurrently. Prefer :c:func:`PyDict_GetItemRef`, which + returns a :term:`strong reference`. + + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + .. c:function:: PyObject* PyDict_GetItemString(PyObject *p, const char *key) @@ -153,6 +229,16 @@ Dictionary Objects Prefer using the :c:func:`PyDict_GetItemWithError` function with your own :c:func:`PyUnicode_FromString` *key* instead. + .. note:: + + In the :term:`free-threaded build`, the returned + :term:`borrowed reference` may become invalid if another thread modifies + the dictionary concurrently. Prefer :c:func:`PyDict_GetItemStringRef`, + which returns a :term:`strong reference`. + + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + .. c:function:: int PyDict_GetItemStringRef(PyObject *p, const char *key, PyObject **result) @@ -162,6 +248,9 @@ Dictionary Objects .. versionadded:: 3.13 + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + .. c:function:: PyObject* PyDict_SetDefault(PyObject *p, PyObject *key, PyObject *defaultobj) @@ -173,6 +262,14 @@ Dictionary Objects .. versionadded:: 3.4 + .. note:: + + In the :term:`free-threaded build`, the returned + :term:`borrowed reference` may become invalid if another thread modifies + the dictionary concurrently. Prefer :c:func:`PyDict_SetDefaultRef`, + which returns a :term:`strong reference`. + + .. c:function:: int PyDict_SetDefaultRef(PyObject *p, PyObject *key, PyObject *default_value, PyObject **result) @@ -192,13 +289,18 @@ Dictionary Objects These may refer to the same object: in that case you hold two separate references to it. + .. note:: + + The operation is atomic on :term:`free threading ` + when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`. + .. versionadded:: 3.13 .. c:function:: int PyDict_Pop(PyObject *p, PyObject *key, PyObject **result) Remove *key* from dictionary *p* and optionally return the removed value. - Do not raise :exc:`KeyError` if the key missing. + Do not raise :exc:`KeyError` if the key is missing. - If the key is present, set *\*result* to a new reference to the removed value if *result* is not ``NULL``, and return ``1``. @@ -207,7 +309,12 @@ Dictionary Objects - On error, raise an exception and return ``-1``. Similar to :meth:`dict.pop`, but without the default value and - not raising :exc:`KeyError` if the key missing. + not raising :exc:`KeyError` if the key is missing. + + .. note:: + + The operation is atomic on :term:`free threading ` + when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`. .. versionadded:: 3.13 @@ -225,17 +332,32 @@ Dictionary Objects Return a :c:type:`PyListObject` containing all the items from the dictionary. + The first argument can be a :class:`dict` or a :class:`frozendict`. + + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + .. c:function:: PyObject* PyDict_Keys(PyObject *p) Return a :c:type:`PyListObject` containing all the keys from the dictionary. + The first argument can be a :class:`dict` or a :class:`frozendict`. + + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + .. c:function:: PyObject* PyDict_Values(PyObject *p) Return a :c:type:`PyListObject` containing all the values from the dictionary *p*. + The first argument can be a :class:`dict` or a :class:`frozendict`. + + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + .. c:function:: Py_ssize_t PyDict_Size(PyObject *p) @@ -244,6 +366,19 @@ Dictionary Objects Return the number of items in the dictionary. This is equivalent to ``len(p)`` on a dictionary. + The argument can be a :class:`dict` or a :class:`frozendict`. + + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + + +.. c:function:: Py_ssize_t PyDict_GET_SIZE(PyObject *p) + + Similar to :c:func:`PyDict_Size`, but without error checking. + + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. + .. c:function:: int PyDict_Next(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) @@ -258,6 +393,8 @@ Dictionary Objects value represents offsets within the internal dictionary structure, and since the structure is sparse, the offsets are not consecutive. + The first argument can be a :class:`dict` or a :class:`frozendict`. + For example:: PyObject *key, *value; @@ -291,7 +428,7 @@ Dictionary Objects } The function is not thread-safe in the :term:`free-threaded ` - build without external synchronization. You can use + build without external synchronization for a mutable :class:`dict`. You can use :c:macro:`Py_BEGIN_CRITICAL_SECTION` to lock the dictionary while iterating over it:: @@ -301,6 +438,20 @@ Dictionary Objects } Py_END_CRITICAL_SECTION(); + The function is thread-safe on a :class:`frozendict`. + + .. note:: + + On the free-threaded build, this function can be used safely inside a + critical section. However, the references returned for *pkey* and *pvalue* + are :term:`borrowed ` and are only valid while the + critical section is held. If you need to use these objects outside the + critical section or when the critical section can be suspended, create a + :term:`strong reference ` (for example, using + :c:func:`Py_NewRef`). + + .. versionchanged:: 3.15 + Also accept :class:`frozendict`. .. c:function:: int PyDict_Merge(PyObject *a, PyObject *b, int override) @@ -311,6 +462,13 @@ Dictionary Objects only be added if there is not a matching key in *a*. Return ``0`` on success or ``-1`` if an exception was raised. + .. note:: + + In the :term:`free-threaded build`, when *b* is a + :class:`dict` (with the standard iterator), both *a* and *b* are locked + for the duration of the operation. When *b* is a non-dict mapping, only + *a* is locked; *b* may be concurrently modified by another thread. + .. c:function:: int PyDict_Update(PyObject *a, PyObject *b) @@ -320,6 +478,13 @@ Dictionary Objects argument has no "keys" attribute. Return ``0`` on success or ``-1`` if an exception was raised. + .. note:: + + In the :term:`free-threaded build`, when *b* is a + :class:`dict` (with the standard iterator), both *a* and *b* are locked + for the duration of the operation. When *b* is a non-dict mapping, only + *a* is locked; *b* may be concurrently modified by another thread. + .. c:function:: int PyDict_MergeFromSeq2(PyObject *a, PyObject *seq2, int override) @@ -335,6 +500,13 @@ Dictionary Objects if override or key not in a: a[key] = value + .. note:: + + In the :term:`free-threaded ` build, only *a* is locked. + The iteration over *seq2* is not synchronized; *seq2* may be concurrently + modified by another thread. + + .. c:function:: int PyDict_AddWatcher(PyDict_WatchCallback callback) Register *callback* as a dictionary watcher. Return a non-negative integer @@ -342,6 +514,13 @@ Dictionary Objects of error (e.g. no more watcher IDs available), return ``-1`` and set an exception. + .. note:: + + This function is not internally synchronized. In the + :term:`free-threaded ` build, callers should ensure no + concurrent calls to :c:func:`PyDict_AddWatcher` or + :c:func:`PyDict_ClearWatcher` are in progress. + .. versionadded:: 3.12 .. c:function:: int PyDict_ClearWatcher(int watcher_id) @@ -350,6 +529,13 @@ Dictionary Objects :c:func:`PyDict_AddWatcher`. Return ``0`` on success, ``-1`` on error (e.g. if the given *watcher_id* was never registered.) + .. note:: + + This function is not internally synchronized. In the + :term:`free-threaded ` build, callers should ensure no + concurrent calls to :c:func:`PyDict_AddWatcher` or + :c:func:`PyDict_ClearWatcher` are in progress. + .. versionadded:: 3.12 .. c:function:: int PyDict_Watch(int watcher_id, PyObject *dict) @@ -417,3 +603,189 @@ Dictionary Objects it before returning. .. versionadded:: 3.12 + + +Dictionary view objects +^^^^^^^^^^^^^^^^^^^^^^^ + +.. c:function:: int PyDictViewSet_Check(PyObject *op) + + Return true if *op* is a view of a set inside a dictionary. This is currently + equivalent to :c:expr:`PyDictKeys_Check(op) || PyDictItems_Check(op)`. This + function always succeeds. + + +.. c:var:: PyTypeObject PyDictKeys_Type + + Type object for a view of dictionary keys. In Python, this is the type of + the object returned by :meth:`dict.keys`. + + +.. c:function:: int PyDictKeys_Check(PyObject *op) + + Return true if *op* is an instance of a dictionary keys view. This function + always succeeds. + + +.. c:var:: PyTypeObject PyDictValues_Type + + Type object for a view of dictionary values. In Python, this is the type of + the object returned by :meth:`dict.values`. + + +.. c:function:: int PyDictValues_Check(PyObject *op) + + Return true if *op* is an instance of a dictionary values view. This function + always succeeds. + + +.. c:var:: PyTypeObject PyDictItems_Type + + Type object for a view of dictionary items. In Python, this is the type of + the object returned by :meth:`dict.items`. + + +.. c:function:: int PyDictItems_Check(PyObject *op) + + Return true if *op* is an instance of a dictionary items view. This function + always succeeds. + + +Frozen dictionary objects +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.15 + + +.. c:var:: PyTypeObject PyFrozenDict_Type + + This instance of :c:type:`PyTypeObject` represents the Python frozen + dictionary type. + This is the same object as :class:`frozendict` in the Python layer. + + +.. c:function:: int PyAnyDict_Check(PyObject *p) + + Return true if *p* is a :class:`dict` object, a :class:`frozendict` object, + or an instance of a subtype of the :class:`!dict` or :class:`!frozendict` + type. + This function always succeeds. + + +.. c:function:: int PyAnyDict_CheckExact(PyObject *p) + + Return true if *p* is a :class:`dict` object or a :class:`frozendict` object, + but not an instance of a subtype of the :class:`!dict` or + :class:`!frozendict` type. + This function always succeeds. + + +.. c:function:: int PyFrozenDict_Check(PyObject *p) + + Return true if *p* is a :class:`frozendict` object or an instance of a + subtype of the :class:`!frozendict` type. + This function always succeeds. + + +.. c:function:: int PyFrozenDict_CheckExact(PyObject *p) + + Return true if *p* is a :class:`frozendict` object, but not an instance of a + subtype of the :class:`!frozendict` type. + This function always succeeds. + + +.. c:function:: PyObject* PyFrozenDict_New(PyObject *iterable) + + Return a new :class:`frozendict` from an iterable, or ``NULL`` on failure + with an exception set. + + Create an empty dictionary if *iterable* is ``NULL``. + + +Ordered dictionaries +^^^^^^^^^^^^^^^^^^^^ + +Python's C API provides interface for :class:`collections.OrderedDict` from C. +Since Python 3.7, dictionaries are ordered by default, so there is usually +little need for these functions; prefer ``PyDict*`` where possible. + + +.. c:var:: PyTypeObject PyODict_Type + + Type object for ordered dictionaries. This is the same object as + :class:`collections.OrderedDict` in the Python layer. + + +.. c:function:: int PyODict_Check(PyObject *od) + + Return true if *od* is an ordered dictionary object or an instance of a + subtype of the :class:`~collections.OrderedDict` type. This function + always succeeds. + + +.. c:function:: int PyODict_CheckExact(PyObject *od) + + Return true if *od* is an ordered dictionary object, but not an instance of + a subtype of the :class:`~collections.OrderedDict` type. + This function always succeeds. + + +.. c:var:: PyTypeObject PyODictKeys_Type + + Analogous to :c:type:`PyDictKeys_Type` for ordered dictionaries. + + +.. c:var:: PyTypeObject PyODictValues_Type + + Analogous to :c:type:`PyDictValues_Type` for ordered dictionaries. + + +.. c:var:: PyTypeObject PyODictItems_Type + + Analogous to :c:type:`PyDictItems_Type` for ordered dictionaries. + + +.. c:function:: PyObject *PyODict_New(void) + + Return a new empty ordered dictionary, or ``NULL`` on failure. + + This is analogous to :c:func:`PyDict_New`. + + +.. c:function:: int PyODict_SetItem(PyObject *od, PyObject *key, PyObject *value) + + Insert *value* into the ordered dictionary *od* with a key of *key*. + Return ``0`` on success or ``-1`` with an exception set on failure. + + This is analogous to :c:func:`PyDict_SetItem`. + + +.. c:function:: int PyODict_DelItem(PyObject *od, PyObject *key) + + Remove the entry in the ordered dictionary *od* with key *key*. + Return ``0`` on success or ``-1`` with an exception set on failure. + + This is analogous to :c:func:`PyDict_DelItem`. + + +These are :term:`soft deprecated` aliases to ``PyDict`` APIs: + + +.. list-table:: + :widths: auto + :header-rows: 1 + + * * ``PyODict`` + * ``PyDict`` + * * .. c:macro:: PyODict_GetItem(od, key) + * :c:func:`PyDict_GetItem` + * * .. c:macro:: PyODict_GetItemWithError(od, key) + * :c:func:`PyDict_GetItemWithError` + * * .. c:macro:: PyODict_GetItemString(od, key) + * :c:func:`PyDict_GetItemString` + * * .. c:macro:: PyODict_Contains(od, key) + * :c:func:`PyDict_Contains` + * * .. c:macro:: PyODict_Size(od) + * :c:func:`PyDict_Size` + * * .. c:macro:: PyODict_SIZE(od) + * :c:func:`PyDict_GET_SIZE` diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 3ff4631a8e53c4..82f594e11300a7 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -309,6 +309,14 @@ For convenience, some of these functions will always return a .. versionadded:: 3.4 +.. c:function:: void PyErr_RangedSyntaxLocationObject(PyObject *filename, int lineno, int col_offset, int end_lineno, int end_col_offset) + + Similar to :c:func:`PyErr_SyntaxLocationObject`, but also sets the + *end_lineno* and *end_col_offset* information for the current exception. + + .. versionadded:: 3.10 + + .. c:function:: void PyErr_SyntaxLocationEx(const char *filename, int lineno, int col_offset) Like :c:func:`PyErr_SyntaxLocationObject`, but *filename* is a byte string @@ -331,6 +339,23 @@ For convenience, some of these functions will always return a use. +.. c:function:: PyObject *PyErr_ProgramTextObject(PyObject *filename, int lineno) + + Get the source line in *filename* at line *lineno*. *filename* should be a + Python :class:`str` object. + + On success, this function returns a Python string object with the found line. + On failure, this function returns ``NULL`` without an exception set. + + +.. c:function:: PyObject *PyErr_ProgramText(const char *filename, int lineno) + + Similar to :c:func:`PyErr_ProgramTextObject`, but *filename* is a + :c:expr:`const char *`, which is decoded with the + :term:`filesystem encoding and error handler`, instead of a + Python object reference. + + Issuing warnings ================ @@ -387,13 +412,22 @@ an error value). .. c:function:: int PyErr_WarnFormat(PyObject *category, Py_ssize_t stack_level, const char *format, ...) - Function similar to :c:func:`PyErr_WarnEx`, but use + Function similar to :c:func:`PyErr_WarnEx`, but uses :c:func:`PyUnicode_FromFormat` to format the warning message. *format* is an ASCII-encoded string. .. versionadded:: 3.2 +.. c:function:: int PyErr_WarnExplicitFormat(PyObject *category, const char *filename, int lineno, const char *module, PyObject *registry, const char *format, ...) + + Similar to :c:func:`PyErr_WarnExplicit`, but uses + :c:func:`PyUnicode_FromFormat` to format the warning message. *format* is + an ASCII-encoded string. + + .. versionadded:: 3.2 + + .. c:function:: int PyErr_ResourceWarning(PyObject *source, Py_ssize_t stack_level, const char *format, ...) Function similar to :c:func:`PyErr_WarnFormat`, but *category* is @@ -639,28 +673,51 @@ Signal Handling single: SIGINT (C macro) single: KeyboardInterrupt (built-in exception) - This function interacts with Python's signal handling. + Handle external interruptions, such as signals or activating a debugger, + whose processing has been delayed until it is safe + to run Python code and/or raise exceptions. - If the function is called from the main thread and under the main Python - interpreter, it checks whether a signal has been sent to the processes - and if so, invokes the corresponding signal handler. If the :mod:`signal` - module is supported, this can invoke a signal handler written in Python. + For example, pressing :kbd:`Ctrl-C` causes a terminal to send the + :py:data:`signal.SIGINT` signal. + This function executes the corresponding Python signal handler, which, + by default, raises the :exc:`KeyboardInterrupt` exception. - The function attempts to handle all pending signals, and then returns ``0``. - However, if a Python signal handler raises an exception, the error - indicator is set and the function returns ``-1`` immediately (such that - other pending signals may not have been handled yet: they will be on the - next :c:func:`PyErr_CheckSignals()` invocation). + :c:func:`!PyErr_CheckSignals` should be called by long-running C code + frequently enough so that the response appears immediate to humans. - If the function is called from a non-main thread, or under a non-main - Python interpreter, it does nothing and returns ``0``. + Handlers invoked by this function currently include: - This function can be called by long-running C code that wants to - be interruptible by user requests (such as by pressing Ctrl-C). + - Signal handlers, including Python functions registered using + the :mod:`signal` module. - .. note:: - The default Python signal handler for :c:macro:`!SIGINT` raises the - :exc:`KeyboardInterrupt` exception. + Signal handlers are only run in the main thread of the main interpreter. + + (This is where the function got the name: originally, signals + were the only way to interrupt the interpreter.) + + - Running the garbage collector, if necessary. + + - Executing a pending :ref:`remote debugger ` script. + + - Raise the exception set by :c:func:`PyThreadState_SetAsyncExc`. + + If any handler raises an exception, immediately return ``-1`` with that + exception set. + Any remaining interruptions are left to be processed on the next + :c:func:`PyErr_CheckSignals()` invocation, if appropriate. + + If all handlers finish successfully, or there are no handlers to run, + return ``0``. + + .. versionchanged:: 3.12 + This function may now invoke the garbage collector. + + .. versionchanged:: 3.14 + This function may now execute a remote debugger script, if remote + debugging is enabled. + + .. versionchanged:: 3.15 + The exception set by :c:func:`PyThreadState_SetAsyncExc` is now raised. .. c:function:: void PyErr_SetInterrupt() @@ -759,9 +816,33 @@ Exception Classes Return :c:member:`~PyTypeObject.tp_name` of the exception class *ob*. +.. c:macro:: PyException_HEAD + + This is a macro including the base fields for an + exception object. + + This was included in Python's C API by mistake and is not designed for use + in extensions. For creating custom exception objects, use + :c:func:`PyErr_NewException` or otherwise create a class inheriting from + :c:data:`PyExc_BaseException`. + + .. soft-deprecated:: 3.15 + + Exception Objects ================= +.. c:function:: int PyExceptionInstance_Check(PyObject *op) + + Return true if *op* is an instance of :class:`BaseException`, false + otherwise. This function always succeeds. + + +.. c:macro:: PyExceptionInstance_Class(op) + + Equivalent to :c:func:`Py_TYPE(op) `. + + .. c:function:: PyObject* PyException_GetTraceback(PyObject *ex) Return the traceback associated with the exception as a new reference, as @@ -939,6 +1020,9 @@ because the :ref:`call protocol ` takes care of recursion handling. be concatenated to the :exc:`RecursionError` message caused by the recursion depth limit. + .. seealso:: + The :c:func:`PyUnstable_ThreadState_SetStackProtection` function. + .. versionchanged:: 3.9 This function is now also available in the :ref:`limited API `. @@ -954,7 +1038,7 @@ Properly implementing :c:member:`~PyTypeObject.tp_repr` for container types requ special recursion handling. In addition to protecting the stack, :c:member:`~PyTypeObject.tp_repr` also needs to track objects to prevent cycles. The following two functions facilitate this functionality. Effectively, -these are the C equivalent to :func:`reprlib.recursive_repr`. +these are the C equivalent to :deco:`reprlib.recursive_repr`. .. c:function:: int Py_ReprEnter(PyObject *object) @@ -979,6 +1063,27 @@ these are the C equivalent to :func:`reprlib.recursive_repr`. Ends a :c:func:`Py_ReprEnter`. Must be called once for each invocation of :c:func:`Py_ReprEnter` that returns zero. +.. c:function:: int Py_GetRecursionLimit(void) + + Get the recursion limit for the current interpreter. It can be set with + :c:func:`Py_SetRecursionLimit`. The recursion limit prevents the + Python interpreter stack from growing infinitely. + + This function cannot fail, and the caller must hold an + :term:`attached thread state`. + + .. seealso:: + :py:func:`sys.getrecursionlimit` + +.. c:function:: void Py_SetRecursionLimit(int new_limit) + + Set the recursion limit for the current interpreter. + + This function cannot fail, and the caller must hold an + :term:`attached thread state`. + + .. seealso:: + :py:func:`sys.setrecursionlimit` .. _standardexceptions: @@ -1039,6 +1144,8 @@ Exception types * :exc:`FloatingPointError` * * .. c:var:: PyObject *PyExc_GeneratorExit * :exc:`GeneratorExit` + * * .. c:var:: PyObject *PyExc_ImportCycleError + * :exc:`ImportCycleError` * * .. c:var:: PyObject *PyExc_ImportError * :exc:`ImportError` * * .. c:var:: PyObject *PyExc_IndentationError @@ -1207,3 +1314,101 @@ Warning types .. versionadded:: 3.10 :c:data:`PyExc_EncodingWarning`. + + +Tracebacks +========== + +.. c:var:: PyTypeObject PyTraceBack_Type + + Type object for traceback objects. This is available as + :class:`types.TracebackType` in the Python layer. + + +.. c:function:: int PyTraceBack_Check(PyObject *op) + + Return true if *op* is a traceback object, false otherwise. This function + does not account for subtypes. + + +.. c:function:: int PyTraceBack_Here(PyFrameObject *f) + + Replace the :attr:`~BaseException.__traceback__` attribute on the current + exception with a new traceback prepending *f* to the existing chain. + + Calling this function without an exception set is undefined behavior. + + This function returns ``0`` on success, and returns ``-1`` with an + exception set on failure. + + +.. c:function:: int PyTraceBack_Print(PyObject *tb, PyObject *f) + + Write the traceback *tb* into the file *f*. + + This function returns ``0`` on success, and returns ``-1`` with an + exception set on failure. + +.. c:function:: const char* PyUnstable_DumpTraceback(int fd, PyThreadState *tstate) + + Write a trace of the Python stack in *tstate* into the file *fd*. The format + looks like:: + + Traceback (most recent call first): + File "xxx", line xxx in + File "xxx", line xxx in + ... + File "xxx", line xxx in + + This function is meant to debug situations such as segfaults, fatal errors, + and similar. The file and function names it outputs are encoded to ASCII with + backslashreplace and truncated to 500 characters. It writes only the first + 100 frames; further frames are truncated with the line ``...``. + + This function will return ``NULL`` on success, or an error message on error. + + This function is intended for use in crash scenarios such as signal handlers + for SIGSEGV, where the interpreter may be in an inconsistent state. Given + that it reads interpreter data structures that may be partially modified, the + function might produce incomplete output or it may even crash itself. + + The caller does not need to hold an :term:`attached thread state`, nor does + *tstate* need to be attached. + + .. versionadded:: 3.15 + +.. c:function:: const char* PyUnstable_DumpTracebackThreads(int fd, PyInterpreterState *interp, PyThreadState *current_tstate, Py_ssize_t max_threads) + + Write the traces of all Python threads in *interp* into the file *fd*. + + If *interp* is ``NULL`` then this function will try to identify the current + interpreter using thread-specific storage. If it cannot, it will return an + error. + + If *current_tstate* is not ``NULL`` then it will be used to identify what the + current thread is in the written output. If it is ``NULL`` then this function + will identify the current thread using thread-specific storage. It is not an + error if the function is unable to get the current Python thread state. + + This function will return ``NULL`` on success, or an error message on error. + + This function is meant to debug situations such as segfaults, fatal + errors, and similar. It calls :c:func:`PyUnstable_DumpTraceback` for each + thread. It only writes the tracebacks of the first *max_threads* threads, + further output is truncated with the line ``...``. If *max_threads* is 0, the + function will use a default value of 100 for the argument. + + This function is intended for use in crash scenarios such as signal handlers + for SIGSEGV, where the interpreter may be in an inconsistent state. Given + that it reads interpreter data structures that may be partially modified, the + function might produce incomplete output or it may even crash itself. + + The caller does not need to hold an :term:`attached thread state`, nor does + *current_tstate* need to be attached. + + .. warning:: + On the :term:`free-threaded build`, this function is not thread-safe. If + another thread deletes its :term:`thread state` while this function is being + called, the process will likely crash. + + .. versionadded:: 3.15 diff --git a/Doc/c-api/extension-modules.rst b/Doc/c-api/extension-modules.rst index 3d331e6ec12f76..34ee86c7876ae7 100644 --- a/Doc/c-api/extension-modules.rst +++ b/Doc/c-api/extension-modules.rst @@ -8,7 +8,8 @@ Defining extension modules A C extension for CPython is a shared library (for example, a ``.so`` file on Linux, ``.pyd`` DLL on Windows), which is loadable into the Python process (for example, it is compiled with compatible compiler settings), and which -exports an :ref:`initialization function `. +exports an :dfn:`export hook` function (or an +old-style :ref:`initialization function `). To be importable by default (that is, by :py:class:`importlib.machinery.ExtensionFileLoader`), @@ -23,25 +24,127 @@ and must be named after the module name plus an extension listed in One suitable tool is Setuptools, whose documentation can be found at https://setuptools.pypa.io/en/latest/setuptools.html. -Normally, the initialization function returns a module definition initialized -using :c:func:`PyModuleDef_Init`. -This allows splitting the creation process into several phases: +.. _extension-export-hook: + +Extension export hook +..................... + +.. versionadded:: 3.15 + + Support for the :samp:`PyModExport_{}` export hook was added in Python + 3.15. The older way of defining modules is still available: consult either + the :ref:`extension-pyinit` section or earlier versions of this + documentation if you plan to support earlier Python versions. + +The export hook must be an exported function with the following signature: + +.. c:function:: PySlot *PyModExport_modulename(void) + +For modules with ASCII-only names, the :ref:`export hook ` +must be named :samp:`PyModExport_{}`, +with ```` replaced by the module's name. + +For non-ASCII module names, the export hook must instead be named +:samp:`PyModExportU_{}` (note the ``U``), with ```` encoded using +Python's *punycode* encoding with hyphens replaced by underscores. In Python: + +.. code-block:: python + + def hook_name(name): + try: + suffix = b'_' + name.encode('ascii') + except UnicodeEncodeError: + suffix = b'U_' + name.encode('punycode').replace(b'-', b'_') + return b'PyModExport' + suffix + +The export hook returns an array of :c:type:`PySlot` entries, +terminated by an entry with a slot ID of ``0``. +These slots describe how the module should be created and initialized. + +This array must remain valid and constant until interpreter shutdown. +Typically, it should use ``static`` storage. +Prefer using the :c:macro:`Py_mod_create` and :c:macro:`Py_mod_exec` slots +for any dynamic behavior. + +The export hook may return ``NULL`` with an exception set to signal failure. + +It is recommended to define the export hook function using a helper macro: + +.. c:macro:: PyMODEXPORT_FUNC + + Declare an extension module export hook. + This macro: + + * specifies the :c:expr:`PySlot*` return type, + * adds any special linkage declarations required by the platform, and + * for C++, declares the function as ``extern "C"``. +For example, a module called ``spam`` would be defined like this:: + + PyABIInfo_VAR(abi_info); + + static PySlot spam_slots[] = { + PySlot_STATIC_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_name, "spam"), + PySlot_FUNC(Py_mod_init, spam_init_function), + ... + PySlot_END + }; + + PyMODEXPORT_FUNC + PyModExport_spam(void) + { + return spam_slots; + } + +The export hook is typically the only non-\ ``static`` +item defined in the module's C source. + +The hook should be kept short -- ideally, one line as above. +If you do need to use Python C API in this function, it is recommended to call +``PyABIInfo_Check(&abi_info, "modulename")`` first to raise an exception, +rather than crash, in common cases of ABI mismatch. + + +.. note:: + + It is possible to export multiple modules from a single shared library by + defining multiple export hooks. + However, importing them requires a custom importer or suitably named + copies/links of the extension file, because Python's import machinery only + finds the function corresponding to the filename. + See the `Multiple modules in one library `__ + section in :pep:`489` for details. + + +.. _multi-phase-initialization: + +Multi-phase initialization +.......................... + +The process of creating an extension module follows several phases: + +- Python finds and calls the export hook to get information on how to + create the module. - Before any substantial code is executed, Python can determine which capabilities the module supports, and it can adjust the environment or refuse loading an incompatible extension. -- By default, Python itself creates the module object -- that is, it does - the equivalent of :py:meth:`object.__new__` for classes. - It also sets initial attributes like :attr:`~module.__package__` and - :attr:`~module.__loader__`. -- Afterwards, the module object is initialized using extension-specific - code -- the equivalent of :py:meth:`~object.__init__` on classes. + Slots like :c:data:`Py_mod_abi`, :c:data:`Py_mod_gil` and + :c:data:`Py_mod_multiple_interpreters` influence this step. +- By default, Python itself then creates the module object -- that is, it does + the equivalent of calling :py:meth:`~object.__new__` when creating an object. + This step can be overridden using the :c:data:`Py_mod_create` slot. +- Python sets initial module attributes like :attr:`~module.__package__` and + :attr:`~module.__loader__`, and inserts the module object into + :py:attr:`sys.modules`. +- Afterwards, the module object is initialized in an extension-specific way + -- the equivalent of :py:meth:`~object.__init__` when creating an object, + or of executing top-level code in a Python-language module. + The behavior is specified using the :c:data:`Py_mod_exec` slot. This is called *multi-phase initialization* to distinguish it from the legacy -(but still supported) *single-phase initialization* scheme, -where the initialization function returns a fully constructed module. -See the :ref:`single-phase-initialization section below ` -for details. +(but still supported) :ref:`single-phase initialization `, +where an initialization function returns a fully constructed module. .. versionchanged:: 3.5 @@ -53,7 +156,7 @@ Multiple module instances By default, extension modules are not singletons. For example, if the :py:attr:`sys.modules` entry is removed and the module -is re-imported, a new module object is created, and typically populated with +is re-imported, a new module object is created and, typically, populated with fresh method and type objects. The old module is subject to normal garbage collection. This mirrors the behavior of pure-Python modules. @@ -83,36 +186,34 @@ A module may also be limited to the main interpreter using the :c:data:`Py_mod_multiple_interpreters` slot. -.. _extension-export-hook: +.. _extension-pyinit: -Initialization function -....................... +``PyInit`` function +................... -The initialization function defined by an extension module has the -following signature: +.. soft-deprecated:: 3.15 + + This functionality will not get new features, + but there are no plans to remove it. + +Instead of :c:func:`PyModExport_modulename`, an extension module can define +an older-style :dfn:`initialization function` with the signature: .. c:function:: PyObject* PyInit_modulename(void) Its name should be :samp:`PyInit_{}`, with ```` replaced by the name of the module. +For non-ASCII module names, use :samp:`PyInitU_{}` instead, with +```` encoded in the same way as for the +:ref:`export hook ` (that is, using Punycode +with underscores). -For modules with ASCII-only names, the function must instead be named -:samp:`PyInit_{}`, with ```` replaced by the name of the module. -When using :ref:`multi-phase-initialization`, non-ASCII module names -are allowed. In this case, the initialization function name is -:samp:`PyInitU_{}`, with ```` encoded using Python's -*punycode* encoding with hyphens replaced by underscores. In Python: +If a module exports both :samp:`PyInit_{}` and +:samp:`PyModExport_{}`, the :samp:`PyInit_{}` function +is ignored. -.. code-block:: python - - def initfunc_name(name): - try: - suffix = b'_' + name.encode('ascii') - except UnicodeEncodeError: - suffix = b'U_' + name.encode('punycode').replace(b'-', b'_') - return b'PyInit' + suffix - -It is recommended to define the initialization function using a helper macro: +Like with :c:macro:`PyMODEXPORT_FUNC`, it is recommended to define the +initialization function using a helper macro: .. c:macro:: PyMODINIT_FUNC @@ -123,43 +224,15 @@ It is recommended to define the initialization function using a helper macro: * adds any special linkage declarations required by the platform, and * for C++, declares the function as ``extern "C"``. -For example, a module called ``spam`` would be defined like this:: - - static struct PyModuleDef spam_module = { - .m_base = PyModuleDef_HEAD_INIT, - .m_name = "spam", - ... - }; - - PyMODINIT_FUNC - PyInit_spam(void) - { - return PyModuleDef_Init(&spam_module); - } - -It is possible to export multiple modules from a single shared library by -defining multiple initialization functions. However, importing them requires -using symbolic links or a custom importer, because by default only the -function corresponding to the filename is found. -See the `Multiple modules in one library `__ -section in :pep:`489` for details. - -The initialization function is typically the only non-\ ``static`` -item defined in the module's C source. +Normally, the initialization function (``PyInit_modulename``) returns +a :c:type:`PyModuleDef` instance with non-``NULL`` +:c:member:`~PyModuleDef.m_slots`. This allows Python to use +:ref:`multi-phase initialization `. -.. _multi-phase-initialization: - -Multi-phase initialization -.......................... - -Normally, the :ref:`initialization function ` -(``PyInit_modulename``) returns a :c:type:`PyModuleDef` instance with -non-``NULL`` :c:member:`~PyModuleDef.m_slots`. Before it is returned, the ``PyModuleDef`` instance must be initialized using the following function: - .. c:function:: PyObject* PyModuleDef_Init(PyModuleDef *def) Ensure a module definition is a properly initialized Python object that @@ -167,7 +240,8 @@ using the following function: Return *def* cast to ``PyObject*``, or ``NULL`` if an error occurred. - Calling this function is required for :ref:`multi-phase-initialization`. + Calling this function is required before returning a :c:type:`PyModuleDef` + from a module initialization function. It should not be used in other contexts. Note that Python assumes that ``PyModuleDef`` structures are statically @@ -178,18 +252,36 @@ using the following function: .. versionadded:: 3.5 +For example, a module called ``spam`` would be defined like this:: + + static struct PyModuleDef spam_module = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = "spam", + ... + }; + + PyMODINIT_FUNC + PyInit_spam(void) + { + return PyModuleDef_Init(&spam_module); + } + + .. _single-phase-initialization: Legacy single-phase initialization -.................................. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. soft-deprecated:: 3.15 -.. attention:: Single-phase initialization is a legacy mechanism to initialize extension modules, with known drawbacks and design flaws. Extension module authors are encouraged to use multi-phase initialization instead. -In single-phase initialization, the -:ref:`initialization function ` (``PyInit_modulename``) + However, there are no plans to remove support for it. + +In single-phase initialization, the old-style +:ref:`initialization function ` (``PyInit_modulename``) should create, populate and return a module object. This is typically done using :c:func:`PyModule_Create` and functions like :c:func:`PyModule_AddObjectRef`. @@ -242,6 +334,8 @@ in the following ways: * Single-phase modules support module lookup functions like :c:func:`PyState_FindModule`. +* The module's :c:member:`PyModuleDef.m_slots` must be NULL. + .. [#testsinglephase] ``_testsinglephase`` is an internal module used in CPython's self-test suite; your installation may or may not include it. diff --git a/Doc/c-api/file.rst b/Doc/c-api/file.rst index e9019a0d500f7e..dcafefdc045872 100644 --- a/Doc/c-api/file.rst +++ b/Doc/c-api/file.rst @@ -2,7 +2,7 @@ .. _fileobjects: -File Objects +File objects ------------ .. index:: pair: object; file @@ -93,6 +93,29 @@ the :mod:`io` APIs instead. .. versionadded:: 3.8 +.. c:function:: PyObject *PyFile_OpenCodeObject(PyObject *path) + + Open *path* with the mode ``'rb'``. *path* must be a Python :class:`str` + object. The behavior of this function may be overridden by + :c:func:`PyFile_SetOpenCodeHook` to allow for some preprocessing of the + text. + + This is analogous to :func:`io.open_code` in Python. + + On success, this function returns a :term:`strong reference` to a Python + file object. On failure, this function returns ``NULL`` with an exception + set. + + .. versionadded:: 3.8 + + +.. c:function:: PyObject *PyFile_OpenCode(const char *path) + + Similar to :c:func:`PyFile_OpenCodeObject`, but *path* is a + UTF-8 encoded :c:expr:`const char*`. + + .. versionadded:: 3.8 + .. c:function:: int PyFile_WriteObject(PyObject *obj, PyObject *p, int flags) @@ -100,11 +123,34 @@ the :mod:`io` APIs instead. Write object *obj* to file object *p*. The only supported flag for *flags* is :c:macro:`Py_PRINT_RAW`; if given, the :func:`str` of the object is written - instead of the :func:`repr`. Return ``0`` on success or ``-1`` on failure; the - appropriate exception will be set. + instead of the :func:`repr`. + + If *obj* is ``NULL``, write the string ``""``. + Return ``0`` on success or ``-1`` on failure; the + appropriate exception will be set. .. c:function:: int PyFile_WriteString(const char *s, PyObject *p) Write string *s* to file object *p*. Return ``0`` on success or ``-1`` on failure; the appropriate exception will be set. + + +Soft-deprecated API +^^^^^^^^^^^^^^^^^^^ + +.. soft-deprecated:: 3.15 + +These are APIs that were included in Python's C API +by mistake. They are documented solely for completeness; use other +``PyFile*`` APIs instead. + +.. c:function:: PyObject *PyFile_NewStdPrinter(int fd) + + Use :c:func:`PyFile_FromFd` with defaults (``fd, NULL, "w", -1, NULL, NULL, NULL, 0``) instead. + +.. c:var:: PyTypeObject PyStdPrinter_Type + + Type of file-like objects used internally at Python startup when :py:mod:`io` is + not yet available. + Use Python :py:func:`open` or :c:func:`PyFile_FromFd` to create file objects instead. diff --git a/Doc/c-api/float.rst b/Doc/c-api/float.rst index 489676caa3a16a..a12ad11abb107d 100644 --- a/Doc/c-api/float.rst +++ b/Doc/c-api/float.rst @@ -78,6 +78,109 @@ Floating-Point Objects Return the minimum normalized positive float *DBL_MIN* as C :c:expr:`double`. +.. c:macro:: Py_INFINITY + + This macro expands to a constant expression of type :c:expr:`double`, that + represents the positive infinity. + + It is equivalent to the :c:macro:`!INFINITY` macro from the C11 standard + ```` header. + + .. soft-deprecated:: 3.15 + + +.. c:macro:: Py_NAN + + This macro expands to a constant expression of type :c:expr:`double`, that + represents a quiet not-a-number (qNaN) value. + + On most platforms, this is equivalent to the :c:macro:`!NAN` macro from + the C11 standard ```` header. + + +.. c:macro:: Py_HUGE_VAL + + Equivalent to :c:macro:`!INFINITY`. + + .. soft-deprecated:: 3.14 + + +.. c:macro:: Py_MATH_E + + The definition (accurate for a :c:expr:`double` type) of the :data:`math.e` constant. + + +.. c:macro:: Py_MATH_El + + High precision (long double) definition of :data:`~math.e` constant. + + .. deprecated-removed:: 3.15 3.20 + + +.. c:macro:: Py_MATH_PI + + The definition (accurate for a :c:expr:`double` type) of the :data:`math.pi` constant. + + +.. c:macro:: Py_MATH_PIl + + High precision (long double) definition of :data:`~math.pi` constant. + + .. deprecated-removed:: 3.15 3.20 + + +.. c:macro:: Py_MATH_TAU + + The definition (accurate for a :c:expr:`double` type) of the :data:`math.tau` constant. + + .. versionadded:: 3.6 + + +.. c:macro:: Py_RETURN_NAN + + Return :data:`math.nan` from a function. + + On most platforms, this is equivalent to ``return PyFloat_FromDouble(NAN)``. + + +.. c:macro:: Py_RETURN_INF(sign) + + Return :data:`math.inf` or :data:`-math.inf ` from a function, + depending on the sign of *sign*. + + On most platforms, this is equivalent to the following:: + + return PyFloat_FromDouble(copysign(INFINITY, sign)); + + +.. c:macro:: Py_IS_FINITE(X) + + Return ``1`` if the given floating-point number *X* is finite, + that is, it is normal, subnormal or zero, but not infinite or NaN. + Return ``0`` otherwise. + + .. soft-deprecated:: 3.14 + Use :c:macro:`!isfinite` instead. + + +.. c:macro:: Py_IS_INFINITY(X) + + Return ``1`` if the given floating-point number *X* is positive or negative + infinity. Return ``0`` otherwise. + + .. soft-deprecated:: 3.14 + Use :c:macro:`!isinf` instead. + + +.. c:macro:: Py_IS_NAN(X) + + Return ``1`` if the given floating-point number *X* is a not-a-number (NaN) + value. Return ``0`` otherwise. + + .. soft-deprecated:: 3.14 + Use :c:macro:`!isnan` instead. + + Pack and Unpack functions ------------------------- @@ -85,24 +188,23 @@ The pack and unpack functions provide an efficient platform-independent way to store floating-point values as byte strings. The Pack routines produce a bytes string from a C :c:expr:`double`, and the Unpack routines produce a C :c:expr:`double` from such a bytes string. The suffix (2, 4 or 8) specifies the -number of bytes in the bytes string. +number of bytes in the bytes string: -On platforms that appear to use IEEE 754 formats these functions work by -copying bits. On other platforms, the 2-byte format is identical to the IEEE -754 binary16 half-precision format, the 4-byte format (32-bit) is identical to -the IEEE 754 binary32 single precision format, and the 8-byte format to the -IEEE 754 binary64 double precision format, although the packing of INFs and -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. +* The 2-byte format is the IEEE 754 binary16 half-precision format. +* The 4-byte format is the IEEE 754 binary32 single-precision format. +* The 8-byte format is the IEEE 754 binary64 double-precision format. -Note that NaNs type may not be preserved on IEEE platforms (silent NaN become -quiet), for example on x86 systems in 32-bit mode. +The NaN type may not be preserved on some platforms while unpacking (signaling +NaNs become quiet NaNs), for example on x86 systems in 32-bit mode. +It's assumed that the :c:expr:`double` type has the IEEE 754 binary64 double +precision format. What happens if it's not true is partly accidental (alas). 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 -happens in such cases is partly accidental (alas). +precision, or smaller dynamic range, not all values can be unpacked. The +packing of special numbers like INFs and NaNs (if such things exist on the +platform) may not be handled correctly, and attempting to unpack a bytes string +containing an IEEE INF or NaN may raise an exception. .. versionadded:: 3.11 @@ -111,19 +213,14 @@ Pack functions The pack routines write 2, 4 or 8 bytes, starting at *p*. *le* is an :c:expr:`int` argument, non-zero if you want the bytes string in little-endian -format (exponent last, at ``p+1``, ``p+3``, or ``p+6`` ``p+7``), zero if you -want big-endian format (exponent first, at *p*). The :c:macro:`PY_BIG_ENDIAN` -constant can be used to use the native endian: it is equal to ``1`` on big -endian processor, or ``0`` on little endian processor. +format (exponent last, at ``p+1``, ``p+3``, or ``p+6`` and ``p+7``), zero if you +want big-endian format (exponent first, at *p*). Use the :c:macro:`!PY_LITTLE_ENDIAN` +constant to select the native endian: it is equal to ``0`` on big +endian processor, or ``1`` on little endian processor. Return value: ``0`` if all is OK, ``-1`` if error (and an exception is set, most likely :exc:`OverflowError`). -There are two problems on non-IEEE platforms: - -* What this does is undefined if *x* is a NaN or infinity. -* ``-0.0`` and ``+0.0`` produce the same bytes string. - .. c:function:: int PyFloat_Pack2(double x, char *p, int le) Pack a C double as the IEEE 754 binary16 half-precision format. @@ -136,6 +233,9 @@ There are two problems on non-IEEE platforms: Pack a C double as the IEEE 754 binary64 double precision format. + .. impl-detail:: + This function always succeeds in CPython. + Unpack functions ^^^^^^^^^^^^^^^^ @@ -143,16 +243,16 @@ Unpack functions The unpack routines read 2, 4 or 8 bytes, starting at *p*. *le* is an :c:expr:`int` argument, non-zero if the bytes string is in little-endian format (exponent last, at ``p+1``, ``p+3`` or ``p+6`` and ``p+7``), zero if big-endian -(exponent first, at *p*). The :c:macro:`PY_BIG_ENDIAN` constant can be used to -use the native endian: it is equal to ``1`` on big endian processor, or ``0`` +(exponent first, at *p*). Use the :c:macro:`!PY_LITTLE_ENDIAN` constant to +select the native endian: it is equal to ``0`` on big endian processor, or ``1`` on little endian processor. Return value: The unpacked double. On error, this is ``-1.0`` and :c:func:`PyErr_Occurred` is true (and an exception is set, most likely :exc:`OverflowError`). -Note that on a non-IEEE platform this will refuse to unpack a bytes string that -represents a NaN or infinity. +.. impl-detail:: + These functions always succeed in CPython. .. c:function:: double PyFloat_Unpack2(const char *p, int le) diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index 1a52e146a69751..4159ff6e5965fb 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -1,6 +1,6 @@ .. highlight:: c -Frame Objects +Frame objects ------------- .. c:type:: PyFrameObject @@ -29,6 +29,12 @@ See also :ref:`Reflection `. Previously, this type was only available after including ````. +.. c:function:: PyFrameObject *PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, PyObject *locals) + + Create a new frame object. This function returns a :term:`strong reference` + to the new frame object on success, and returns ``NULL`` with an exception + set on failure. + .. c:function:: int PyFrame_Check(PyObject *obj) Return non-zero if *obj* is a frame object. @@ -44,6 +50,7 @@ See also :ref:`Reflection `. Return a :term:`strong reference`, or ``NULL`` if *frame* has no outer frame. + This raises no exceptions. .. versionadded:: 3.9 @@ -140,7 +147,7 @@ See also :ref:`Reflection `. Return the line number that *frame* is currently executing. -Frame Locals Proxies +Frame locals proxies ^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 3.13 @@ -161,7 +168,52 @@ See :pep:`667` for more information. Return non-zero if *obj* is a frame :func:`locals` proxy. -Internal Frames + +Legacy local variable APIs +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These APIs are :term:`soft deprecated`. As of Python 3.13, they do nothing. +They exist solely for backwards compatibility. + + +.. c:function:: void PyFrame_LocalsToFast(PyFrameObject *f, int clear) + + Prior to Python 3.13, this function would copy the :attr:`~frame.f_locals` + attribute of *f* to the internal "fast" array of local variables, allowing + changes in frame objects to be visible to the interpreter. If *clear* was + true, this function would process variables that were unset in the locals + dictionary. + + .. soft-deprecated:: 3.13 + This function now does nothing. + + +.. c:function:: void PyFrame_FastToLocals(PyFrameObject *f) + + Prior to Python 3.13, this function would copy the internal "fast" array + of local variables (which is used by the interpreter) to the + :attr:`~frame.f_locals` attribute of *f*, allowing changes in local + variables to be visible to frame objects. + + .. soft-deprecated:: 3.13 + This function now does nothing. + + +.. c:function:: int PyFrame_FastToLocalsWithError(PyFrameObject *f) + + Prior to Python 3.13, this function was similar to + :c:func:`PyFrame_FastToLocals`, but would return ``0`` on success, and + ``-1`` with an exception set on failure. + + .. soft-deprecated:: 3.13 + This function now does nothing. + + +.. seealso:: + :pep:`667` + + +Internal frames ^^^^^^^^^^^^^^^ Unless using :pep:`523`, you will not need this. @@ -191,5 +243,3 @@ Unless using :pep:`523`, you will not need this. Return the currently executing line number, or -1 if there is no line number. .. versionadded:: 3.12 - - diff --git a/Doc/c-api/function.rst b/Doc/c-api/function.rst index 5fb8567ef8c95f..609b5e885b6b16 100644 --- a/Doc/c-api/function.rst +++ b/Doc/c-api/function.rst @@ -102,6 +102,15 @@ There are a few functions specific to Python functions. dictionary of arguments or ``NULL``. +.. c:function:: int PyFunction_SetKwDefaults(PyObject *op, PyObject *defaults) + + Set the keyword-only argument default values of the function object *op*. + *defaults* must be a dictionary of keyword-only arguments or ``Py_None``. + + This function returns ``0`` on success, and returns ``-1`` with an exception + set on failure. + + .. c:function:: PyObject* PyFunction_GetClosure(PyObject *op) Return the closure associated with the function object *op*. This can be ``NULL`` @@ -175,6 +184,9 @@ There are a few functions specific to Python functions. .. versionadded:: 3.12 + - ``PyFunction_PYFUNC_EVENT_MODIFY_QUALNAME`` + + .. versionadded:: 3.15 .. c:type:: int (*PyFunction_WatchCallback)(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value) @@ -197,7 +209,7 @@ There are a few functions specific to Python functions. runtime behavior depending on optimization decisions, it does not change the semantics of the Python code being executed. - If *event* is ``PyFunction_EVENT_DESTROY``, Taking a reference in the + If *event* is ``PyFunction_EVENT_DESTROY``, taking a reference in the callback to the about-to-be-destroyed function will resurrect it, preventing it from being freed at this time. When the resurrected object is destroyed later, any watcher callbacks active at that time will be called again. diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index f6fa52b36c5ab3..9c71bb3d59d5e9 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -220,50 +220,237 @@ The :c:member:`~PyTypeObject.tp_traverse` handler accepts a function parameter o detection; it's not expected that users will need to write their own visitor functions. -The :c:member:`~PyTypeObject.tp_traverse` handler must have the following type: +The :c:member:`~PyTypeObject.tp_clear` handler must be of the :c:type:`inquiry` type, or ``NULL`` +if the object is immutable. +.. c:type:: int (*inquiry)(PyObject *self) + + Drop references that may have created reference cycles. Immutable objects + do not have to define this method since they can never directly create + reference cycles. Note that the object must still be valid after calling + this method (don't just call :c:func:`Py_DECREF` on a reference). The + collector will call this method if it detects that this object is involved + in a reference cycle. + + +.. _gc-traversal: + +Traversal +--------- + +The :c:member:`~PyTypeObject.tp_traverse` handler must have the following type: + .. c:type:: int (*traverseproc)(PyObject *self, visitproc visit, void *arg) - Traversal function for a container object. Implementations must call the + Traversal function for a garbage-collected object, used by the garbage + collector to detect reference cycles. + Implementations must call the *visit* function for each object directly contained by *self*, with the parameters to *visit* being the contained object and the *arg* value passed to the handler. The *visit* function must not be called with a ``NULL`` - object argument. If *visit* returns a non-zero value that value should be + object argument. If *visit* returns a non-zero value, that value should be returned immediately. -To simplify writing :c:member:`~PyTypeObject.tp_traverse` handlers, a :c:func:`Py_VISIT` macro is -provided. In order to use this macro, the :c:member:`~PyTypeObject.tp_traverse` implementation -must name its arguments exactly *visit* and *arg*: - - -.. c:macro:: Py_VISIT(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:: + A typical :c:member:`!tp_traverse` function calls the :c:func:`Py_VISIT` + convenience macro on each of the instance's members that are Python + objects that the instance owns. + For example, this is a (slightly outdated) traversal function for + the :py:class:`threading.local` class:: static int - my_traverse(Noddy *self, visitproc visit, void *arg) + local_traverse(PyObject *op, visitproc visit, void *arg) { - Py_VISIT(self->foo); - Py_VISIT(self->bar); + localobject *self = (localobject *) op; + Py_VISIT(Py_TYPE(self)); + Py_VISIT(self->args); + Py_VISIT(self->kw); + Py_VISIT(self->dict); return 0; } -The :c:member:`~PyTypeObject.tp_clear` handler must be of the :c:type:`inquiry` type, or ``NULL`` -if the object is immutable. + .. note:: + :c:func:`Py_VISIT` requires the *visit* and *arg* parameters to + :c:func:`!local_traverse` to have these specific names; don't name them just + anything. + Instances of :ref:`heap-allocated types ` hold a reference to + their type. Their traversal function must therefore visit the type:: -.. c:type:: int (*inquiry)(PyObject *self) + Py_VISIT(Py_TYPE(self)); - Drop references that may have created reference cycles. Immutable objects - do not have to define this method since they can never directly create - reference cycles. Note that the object must still be valid after calling - this method (don't just call :c:func:`Py_DECREF` on a reference). The - collector will call this method if it detects that this object is involved - in a reference cycle. + Alternately, the type may delegate this responsibility by + calling ``tp_traverse`` of a heap-allocated superclass (or another + heap-allocated type, if applicable). + If they do not, the type object may not be garbage-collected. + + If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the + :c:member:`~PyTypeObject.tp_flags` field, the traverse function must call + :c:func:`PyObject_VisitManagedDict` like this:: + + int err = PyObject_VisitManagedDict((PyObject*)self, visit, arg); + if (err) { + return err; + } + + Only the members that the instance *owns* (by having + :term:`strong references ` to them) must be + visited. For instance, if an object supports weak references via the + :c:member:`~PyTypeObject.tp_weaklist` slot, the pointer supporting + the linked list (what *tp_weaklist* points to) must **not** be + visited as the instance does not directly own the weak references to itself. + + The traversal function has a limitation: + + .. warning:: + + The traversal function must not have any side effects. Implementations + may not modify the reference counts of any Python objects nor create or + destroy any Python objects, directly or indirectly. + + This means that *most* Python C API functions may not be used, since + they can raise a new exception, return a new reference to a result object, + have internal logic that uses side effects. + Also, unless documented otherwise, functions that happen to not have side + effects may start having them in future versions, without warning. + + For a list of safe functions, see a + :ref:`separate section ` below. + + .. note:: + + The :c:func:`Py_VISIT` call may be skipped for those members that provably + cannot participate in reference cycles. + In the ``local_traverse`` example above, there is also a ``self->key`` + member, but it can only be ``NULL`` or a Python string and therefore + cannot be part of a reference cycle. + + On the other hand, even if you know a member can never be part of a cycle, + as a debugging aid you may want to visit it anyway just so the :mod:`gc` + module's :func:`~gc.get_referents` function will include it. + + .. note:: + + The :c:member:`~PyTypeObject.tp_traverse` function can be called from any + thread. + + .. impl-detail:: + + Garbage collection is a "stop-the-world" operation: + even in :term:`free threading` builds, only one thread state is + :term:`attached ` when :c:member:`!tp_traverse` + handlers run. + + .. versionchanged:: 3.9 + + Heap-allocated types are expected to visit ``Py_TYPE(self)`` in + ``tp_traverse``. In earlier versions of Python, due to + `bug 40217 `_, doing this + may lead to crashes in subclasses. + +To simplify writing :c:member:`~PyTypeObject.tp_traverse` handlers, +a :c:func:`Py_VISIT` macro is provided. +In order to use this macro, the :c:member:`~PyTypeObject.tp_traverse` +implementation must name its arguments exactly *visit* and *arg*: + +.. c:macro:: Py_VISIT(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. + + This corresponds roughly to:: + + #define Py_VISIT(o) \ + if (op) { \ + int visit_result = visit(o, arg); \ + if (visit_result != 0) { \ + return visit_result; \ + } \ + } + + +Traversal-safe functions +^^^^^^^^^^^^^^^^^^^^^^^^ + +The following functions and macros are safe to use in a +:c:member:`~PyTypeObject.tp_traverse` handler: + +* the *visit* function passed to ``tp_traverse`` +* :c:func:`Py_VISIT` +* :c:func:`Py_SIZE` +* :c:func:`Py_TYPE`: if called from a :c:member:`!tp_traverse` handler, + :c:func:`!Py_TYPE`'s result will be valid for the duration of the handler call +* :c:func:`PyObject_VisitManagedDict` +* :c:func:`PyObject_TypeCheck`, :c:func:`PyType_IsSubtype`, + :c:func:`PyType_HasFeature` +* :samp:`Py{}_Check` and :samp:`Py{}_CheckExact` -- for example, + :c:func:`PyTuple_Check` +* :ref:`duringgc-functions` + +.. _duringgc-functions: + +"DuringGC" functions +^^^^^^^^^^^^^^^^^^^^ + +The following functions should *only* be used in a +:c:member:`~PyTypeObject.tp_traverse` handler; calling them in other +contexts may have unintended consequences. + +These functions act like their counterparts without the ``_DuringGC`` suffix, +but they are guaranteed to not have side effects, they do not set an exception +on failure, and they return/set :term:`borrowed references ` +as detailed in the individual documentation. + +Note that these functions may fail (return ``NULL`` or ``-1``), +but as they do not set an exception, no error information is available. +In some cases, failure is not distinguishable from a successful ``NULL`` result. + +.. c:function:: void *PyObject_GetTypeData_DuringGC(PyObject *o, PyTypeObject *cls) + void *PyObject_GetItemData_DuringGC(PyObject *o) + void *PyType_GetModuleState_DuringGC(PyTypeObject *type) + void *PyModule_GetState_DuringGC(PyObject *module) + int PyModule_GetToken_DuringGC(PyObject *module, void** result) + + See :ref:`duringgc-functions` for common information. + + .. versionadded:: 3.15 + + .. seealso:: + + :c:func:`PyObject_GetTypeData`, + :c:func:`PyObject_GetItemData`, + :c:func:`PyType_GetModuleState`, + :c:func:`PyModule_GetState`, + :c:func:`PyModule_GetToken`, + :c:func:`PyType_GetBaseByToken` + +.. c:function:: int PyType_GetBaseByToken_DuringGC(PyTypeObject *type, void *tp_token, PyTypeObject **result) + + See :ref:`duringgc-functions` for common information. + + Sets *\*result* to a :term:`borrowed reference` rather than a strong one. + The reference is valid for the duration + of the :c:member:`!tp_traverse` handler call. + + .. versionadded:: 3.15 + + .. seealso:: :c:func:`PyType_GetBaseByToken` + +.. c:function:: PyObject* PyType_GetModule_DuringGC(PyTypeObject *type) + PyObject* PyType_GetModuleByToken_DuringGC(PyTypeObject *type, const void *mod_token) + + See :ref:`duringgc-functions` for common information. + + These functions return a :term:`borrowed reference`, which is + valid for the duration of the :c:member:`!tp_traverse` handler call. + + .. versionadded:: 3.15 + + .. seealso:: + + :c:func:`PyType_GetModule`, + :c:func:`PyType_GetModuleByToken` Controlling the Garbage Collector State diff --git a/Doc/c-api/gen.rst b/Doc/c-api/gen.rst index 0eb5922f6da75f..7713ba2ee4f804 100644 --- a/Doc/c-api/gen.rst +++ b/Doc/c-api/gen.rst @@ -38,9 +38,79 @@ than explicitly calling :c:func:`PyGen_New` or :c:func:`PyGen_NewWithQualName`. A reference to *frame* is stolen by this function. The argument must not be ``NULL``. + .. deprecated-removed:: 3.16 3.18 + + This function has not been used since 3.10. + It is also impossible to construct a proper *frame* + object to call this function. + .. c:function:: PyObject* PyGen_NewWithQualName(PyFrameObject *frame, PyObject *name, PyObject *qualname) Create and return a new generator object based on the *frame* object, with ``__name__`` and ``__qualname__`` set to *name* and *qualname*. A reference to *frame* is stolen by this function. The *frame* argument must not be ``NULL``. + + .. deprecated-removed:: 3.16 3.18 + + This function has not been used since 3.10. + It is also impossible to construct a proper *frame* + object to call this function. + + +.. c:function:: PyCodeObject* PyGen_GetCode(PyGenObject *gen) + + Return a new :term:`strong reference` to the code object wrapped by *gen*. + This function always succeeds. + + +Asynchronous Generator Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. seealso:: + :pep:`525` + +.. c:var:: PyTypeObject PyAsyncGen_Type + + The type object corresponding to asynchronous generator objects. This is + available as :class:`types.AsyncGeneratorType` in the Python layer. + + .. versionadded:: 3.6 + +.. c:function:: PyObject *PyAsyncGen_New(PyFrameObject *frame, PyObject *name, PyObject *qualname) + + Create a new asynchronous generator wrapping *frame*, with ``__name__`` and + ``__qualname__`` set to *name* and *qualname*. *frame* is stolen by this + function and must not be ``NULL``. + + On success, this function returns a :term:`strong reference` to the + new asynchronous generator. On failure, this function returns ``NULL`` + with an exception set. + + .. versionadded:: 3.6 + + .. deprecated-removed:: 3.16 3.18 + + This function has not been used since 3.10. + It is also impossible to construct a proper *frame* + object to call this function. + +.. c:function:: int PyAsyncGen_CheckExact(PyObject *op) + + Return true if *op* is an asynchronous generator object, false otherwise. + This function always succeeds. + + .. versionadded:: 3.6 + + +Deprecated API +^^^^^^^^^^^^^^ + +.. c:macro:: PyAsyncGenASend_CheckExact(op) + + This is an API that was included in Python's C API + by mistake. + + It is solely here for completeness; do not use this API. + + .. soft-deprecated:: 3.14 diff --git a/Doc/c-api/hash.rst b/Doc/c-api/hash.rst index 00f8cb887dc7eb..1ad712b0ce4f2b 100644 --- a/Doc/c-api/hash.rst +++ b/Doc/c-api/hash.rst @@ -11,47 +11,103 @@ See also the :c:member:`PyTypeObject.tp_hash` member and :ref:`numeric-hash`. .. versionadded:: 3.2 + .. c:type:: Py_uhash_t Hash value type: unsigned integer. .. versionadded:: 3.2 + +.. c:macro:: Py_HASH_ALGORITHM + + A numerical value indicating the algorithm for hashing of :class:`str`, + :class:`bytes`, and :class:`memoryview`. + + The algorithm name is exposed by :data:`sys.hash_info.algorithm`. + + .. versionadded:: 3.4 + + +.. c:macro:: Py_HASH_FNV + Py_HASH_SIPHASH24 + Py_HASH_SIPHASH13 + + Numerical values to compare to :c:macro:`Py_HASH_ALGORITHM` to determine + which algorithm is used for hashing. The hash algorithm can be configured + via the configure :option:`--with-hash-algorithm` option. + + .. versionadded:: 3.4 + Add :c:macro:`!Py_HASH_FNV` and :c:macro:`!Py_HASH_SIPHASH24`. + + .. versionadded:: 3.11 + Add :c:macro:`!Py_HASH_SIPHASH13`. + + +.. c:macro:: Py_HASH_CUTOFF + + Buffers of length in range ``[1, Py_HASH_CUTOFF)`` are hashed using DJBX33A + instead of the algorithm described by :c:macro:`Py_HASH_ALGORITHM`. + + - A :c:macro:`!Py_HASH_CUTOFF` of 0 disables the optimization. + - :c:macro:`!Py_HASH_CUTOFF` must be non-negative and less or equal than 7. + + 32-bit platforms should use a cutoff smaller than 64-bit platforms because + it is easier to create colliding strings. A cutoff of 7 on 64-bit platforms + and 5 on 32-bit platforms should provide a decent safety margin. + + This corresponds to the :data:`sys.hash_info.cutoff` constant. + + .. versionadded:: 3.4 + + .. c:macro:: PyHASH_MODULUS - The `Mersenne prime `_ ``P = 2**n -1``, used for numeric hash scheme. + The `Mersenne prime `_ ``P = 2**n -1``, + used for numeric hash scheme. + + This corresponds to the :data:`sys.hash_info.modulus` constant. .. versionadded:: 3.13 + .. c:macro:: PyHASH_BITS The exponent ``n`` of ``P`` in :c:macro:`PyHASH_MODULUS`. .. versionadded:: 3.13 + .. c:macro:: PyHASH_MULTIPLIER Prime multiplier used in string and various other hashes. .. versionadded:: 3.13 + .. c:macro:: PyHASH_INF The hash value returned for a positive infinity. + This corresponds to the :data:`sys.hash_info.inf` constant. + .. versionadded:: 3.13 + .. c:macro:: PyHASH_IMAG The multiplier used for the imaginary part of a complex number. + This corresponds to the :data:`sys.hash_info.imag` constant. + .. versionadded:: 3.13 + .. c:type:: PyHash_FuncDef Hash function definition used by :c:func:`PyHash_GetFuncDef`. - .. c::member:: Py_hash_t (*const hash)(const void *, Py_ssize_t) + .. c:member:: Py_hash_t (*const hash)(const void *, Py_ssize_t) Hash function. @@ -59,14 +115,20 @@ See also the :c:member:`PyTypeObject.tp_hash` member and :ref:`numeric-hash`. Hash function name (UTF-8 encoded string). + This corresponds to the :data:`sys.hash_info.algorithm` constant. + .. c:member:: const int hash_bits Internal size of the hash value in bits. + This corresponds to the :data:`sys.hash_info.hash_bits` constant. + .. c:member:: const int seed_bits Size of seed input in bits. + This corresponds to the :data:`sys.hash_info.seed_bits` constant. + .. versionadded:: 3.4 diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst index 8eabc0406b11ce..ec9462931d56c2 100644 --- a/Doc/c-api/import.rst +++ b/Doc/c-api/import.rst @@ -129,8 +129,7 @@ Importing Modules of :class:`~importlib.machinery.SourceFileLoader` otherwise. The module's :attr:`~module.__file__` attribute will be set to the code - object's :attr:`~codeobject.co_filename`. If applicable, - :attr:`~module.__cached__` will also be set. + object's :attr:`~codeobject.co_filename`. This function will reload the module if it was already imported. See :c:func:`PyImport_ReloadModule` for the intended way to reload a module. @@ -142,10 +141,13 @@ Importing Modules :c:func:`PyImport_ExecCodeModuleWithPathnames`. .. versionchanged:: 3.12 - The setting of :attr:`~module.__cached__` and :attr:`~module.__loader__` + The setting of ``__cached__`` and :attr:`~module.__loader__` is deprecated. See :class:`~importlib.machinery.ModuleSpec` for alternatives. + .. versionchanged:: 3.15 + ``__cached__`` is no longer set. + .. c:function:: PyObject* PyImport_ExecCodeModuleEx(const char *name, PyObject *co, const char *pathname) @@ -157,16 +159,19 @@ Importing Modules .. c:function:: PyObject* PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname, PyObject *cpathname) - Like :c:func:`PyImport_ExecCodeModuleEx`, but the :attr:`~module.__cached__` - attribute of the module object is set to *cpathname* if it is - non-``NULL``. Of the three functions, this is the preferred one to use. + Like :c:func:`PyImport_ExecCodeModuleEx`, but the path to any compiled file + via *cpathname* is used appropriately when non-``NULL``. Of the three + functions, this is the preferred one to use. .. versionadded:: 3.3 .. versionchanged:: 3.12 - Setting :attr:`~module.__cached__` is deprecated. See + Setting ``__cached__`` is deprecated. See :class:`~importlib.machinery.ModuleSpec` for alternatives. + .. versionchanged:: 3.15 + ``__cached__`` no longer set. + .. c:function:: PyObject* PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, const char *pathname, const char *cpathname) @@ -314,6 +319,13 @@ Importing Modules initialization. +.. c:var:: struct _inittab *PyImport_Inittab + + The table of built-in modules used by Python initialization. Do not use this directly; + use :c:func:`PyImport_AppendInittab` and :c:func:`PyImport_ExtendInittab` + instead. + + .. c:function:: PyObject* PyImport_ImportModuleAttr(PyObject *mod_name, PyObject *attr_name) Import the module *mod_name* and get its attribute *attr_name*. @@ -333,3 +345,73 @@ Importing Modules strings instead of Python :class:`str` objects. .. versionadded:: 3.14 + +.. c:function:: PyImport_LazyImportsMode PyImport_GetLazyImportsMode() + + Gets the current lazy imports mode. + + .. versionadded:: 3.15 + +.. c:function:: PyObject* PyImport_GetLazyImportsFilter() + + Return a :term:`strong reference` to the current lazy imports filter, + or ``NULL`` if none exists. This function always succeeds. + + .. versionadded:: 3.15 + +.. c:function:: int PyImport_SetLazyImportsMode(PyImport_LazyImportsMode mode) + + Similar to :c:func:`PyImport_ImportModuleAttr`, but names are UTF-8 encoded + strings instead of Python :class:`str` objects. + + This function always returns ``0``. + + .. versionadded:: 3.15 + +.. c:function:: int PyImport_SetLazyImportsFilter(PyObject *filter) + + Sets the current lazy imports filter. The *filter* should be a callable that + will receive ``(importing_module_name, imported_module_name, [fromlist])`` + when an import can potentially be lazy. The ``imported_module_name`` value + is the resolved module name, so ``lazy from .spam import eggs`` passes + ``package.spam``. The callable must return ``True`` if the import should be + lazy and ``False`` otherwise. + + Return ``0`` on success and ``-1`` with an exception set otherwise. + + .. versionadded:: 3.15 + +.. c:type:: PyImport_LazyImportsMode + + Enumeration of possible lazy import modes. + + .. c:enumerator:: PyImport_LAZY_NORMAL + + Respect the ``lazy`` keyword in source code. This is the default mode. + + .. c:enumerator:: PyImport_LAZY_ALL + + Make all imports lazy by default. + + .. versionadded:: 3.15 + +.. c:function:: PyObject* PyImport_CreateModuleFromInitfunc(PyObject *spec, PyObject* (*initfunc)(void)) + + This function is a building block that enables embedders to implement + the :py:meth:`~importlib.abc.Loader.create_module` step of custom + static extension importers (e.g. of statically-linked extensions). + + *spec* must be a :class:`~importlib.machinery.ModuleSpec` object. + + *initfunc* must be an :ref:`initialization function `, + the same as for :c:func:`PyImport_AppendInittab`. + + On success, create and return a module object. + This module will not be initialized; call :c:func:`PyModule_Exec` + to initialize it. + (Custom importers should do this in their + :py:meth:`~importlib.abc.Loader.exec_module` method.) + + On error, return NULL with an exception set. + + .. versionadded:: 3.15 diff --git a/Doc/c-api/index.rst b/Doc/c-api/index.rst index e9df2a304d975b..051f6fd765e850 100644 --- a/Doc/c-api/index.rst +++ b/Doc/c-api/index.rst @@ -1,7 +1,7 @@ .. _c-api-index: ################################## - Python/C API Reference Manual + Python/C API reference manual ################################## This manual documents the API used by C and C++ programmers who want to write @@ -18,10 +18,16 @@ document the API functions in detail. refcounting.rst exceptions.rst extension-modules.rst + slots.rst utilities.rst abstract.rst concrete.rst - init.rst + interp-lifecycle.rst + threads.rst + synchronization.rst + tls.rst + subinterpreters.rst + profiling.rst init_config.rst memory.rst objimpl.rst diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index e3ad4f4cdc52cc..e56c67f95348c7 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1,2434 +1,13 @@ -.. highlight:: c +:orphan: +Initialization, finalization, and threads +========================================= -.. _initialization: +This page has been split up into the following: -***************************************** -Initialization, Finalization, and Threads -***************************************** - -See :ref:`Python Initialization Configuration ` for details -on how to configure the interpreter prior to initialization. - -.. _pre-init-safe: - -Before Python Initialization -============================ - -In an application embedding Python, the :c:func:`Py_Initialize` function must -be called before using any other Python/C API functions; with the exception of -a few functions and the :ref:`global configuration variables -`. - -The following functions can be safely called before Python is initialized: - -* Functions that initialize the interpreter: - - * :c:func:`Py_Initialize` - * :c:func:`Py_InitializeEx` - * :c:func:`Py_InitializeFromConfig` - * :c:func:`Py_BytesMain` - * :c:func:`Py_Main` - * the runtime pre-initialization functions covered in :ref:`init-config` - -* Configuration functions: - - * :c:func:`PyImport_AppendInittab` - * :c:func:`PyImport_ExtendInittab` - * :c:func:`!PyInitFrozenExtensions` - * :c:func:`PyMem_SetAllocator` - * :c:func:`PyMem_SetupDebugHooks` - * :c:func:`PyObject_SetArenaAllocator` - * :c:func:`Py_SetProgramName` - * :c:func:`Py_SetPythonHome` - * :c:func:`PySys_ResetWarnOptions` - * the configuration functions covered in :ref:`init-config` - -* Informative functions: - - * :c:func:`Py_IsInitialized` - * :c:func:`PyMem_GetAllocator` - * :c:func:`PyObject_GetArenaAllocator` - * :c:func:`Py_GetBuildInfo` - * :c:func:`Py_GetCompiler` - * :c:func:`Py_GetCopyright` - * :c:func:`Py_GetPlatform` - * :c:func:`Py_GetVersion` - * :c:func:`Py_IsInitialized` - -* Utilities: - - * :c:func:`Py_DecodeLocale` - * the status reporting and utility functions covered in :ref:`init-config` - -* Memory allocators: - - * :c:func:`PyMem_RawMalloc` - * :c:func:`PyMem_RawRealloc` - * :c:func:`PyMem_RawCalloc` - * :c:func:`PyMem_RawFree` - -* Synchronization: - - * :c:func:`PyMutex_Lock` - * :c:func:`PyMutex_Unlock` - -.. note:: - - Despite their apparent similarity to some of the functions listed above, - the following functions **should not be called** before the interpreter has - been initialized: :c:func:`Py_EncodeLocale`, :c:func:`PyEval_InitThreads`, and - :c:func:`Py_RunMain`. - - -.. _global-conf-vars: - -Global configuration variables -============================== - -Python has variables for the global configuration to control different features -and options. By default, these flags are controlled by :ref:`command line -options `. - -When a flag is set by an option, the value of the flag is the number of times -that the option was set. For example, ``-b`` sets :c:data:`Py_BytesWarningFlag` -to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. - -.. c:var:: int Py_BytesWarningFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.bytes_warning` should be used instead, see :ref:`Python - Initialization Configuration `. - - Issue a warning when comparing :class:`bytes` or :class:`bytearray` with - :class:`str` or :class:`bytes` with :class:`int`. Issue an error if greater - or equal to ``2``. - - Set by the :option:`-b` option. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_DebugFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.parser_debug` should be used instead, see :ref:`Python - Initialization Configuration `. - - Turn on parser debugging output (for expert only, depending on compilation - options). - - Set by the :option:`-d` option and the :envvar:`PYTHONDEBUG` environment - variable. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_DontWriteBytecodeFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.write_bytecode` should be used instead, see :ref:`Python - Initialization Configuration `. - - If set to non-zero, Python won't try to write ``.pyc`` files on the - import of source modules. - - Set by the :option:`-B` option and the :envvar:`PYTHONDONTWRITEBYTECODE` - environment variable. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_FrozenFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.pathconfig_warnings` should be used instead, see - :ref:`Python Initialization Configuration `. - - Private flag used by ``_freeze_module`` and ``frozenmain`` programs. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_HashRandomizationFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.hash_seed` and :c:member:`PyConfig.use_hash_seed` should - be used instead, see :ref:`Python Initialization Configuration - `. - - Set to ``1`` if the :envvar:`PYTHONHASHSEED` environment variable is set to - a non-empty string. - - If the flag is non-zero, read the :envvar:`PYTHONHASHSEED` environment - variable to initialize the secret hash seed. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_IgnoreEnvironmentFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.use_environment` should be used instead, see - :ref:`Python Initialization Configuration `. - - Ignore all :envvar:`!PYTHON*` environment variables, e.g. - :envvar:`PYTHONPATH` and :envvar:`PYTHONHOME`, that might be set. - - Set by the :option:`-E` and :option:`-I` options. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_InspectFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.inspect` should be used instead, see - :ref:`Python Initialization Configuration `. - - When a script is passed as first argument or the :option:`-c` option is used, - enter interactive mode after executing the script or the command, even when - :data:`sys.stdin` does not appear to be a terminal. - - Set by the :option:`-i` option and the :envvar:`PYTHONINSPECT` environment - variable. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_InteractiveFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.interactive` should be used instead, see - :ref:`Python Initialization Configuration `. - - Set by the :option:`-i` option. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_IsolatedFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.isolated` should be used instead, see - :ref:`Python Initialization Configuration `. - - Run Python in isolated mode. In isolated mode :data:`sys.path` contains - neither the script's directory nor the user's site-packages directory. - - Set by the :option:`-I` option. - - .. versionadded:: 3.4 - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_LegacyWindowsFSEncodingFlag - - This API is kept for backward compatibility: setting - :c:member:`PyPreConfig.legacy_windows_fs_encoding` should be used instead, see - :ref:`Python Initialization Configuration `. - - If the flag is non-zero, use the ``mbcs`` encoding with ``replace`` error - handler, instead of the UTF-8 encoding with ``surrogatepass`` error handler, - for the :term:`filesystem encoding and error handler`. - - Set to ``1`` if the :envvar:`PYTHONLEGACYWINDOWSFSENCODING` environment - variable is set to a non-empty string. - - See :pep:`529` for more details. - - .. availability:: Windows. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_LegacyWindowsStdioFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.legacy_windows_stdio` should be used instead, see - :ref:`Python Initialization Configuration `. - - If the flag is non-zero, use :class:`io.FileIO` instead of - :class:`!io._WindowsConsoleIO` for :mod:`sys` standard streams. - - Set to ``1`` if the :envvar:`PYTHONLEGACYWINDOWSSTDIO` environment - variable is set to a non-empty string. - - See :pep:`528` for more details. - - .. availability:: Windows. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_NoSiteFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.site_import` should be used instead, see - :ref:`Python Initialization Configuration `. - - Disable the import of the module :mod:`site` and the site-dependent - manipulations of :data:`sys.path` that it entails. Also disable these - manipulations if :mod:`site` is explicitly imported later (call - :func:`site.main` if you want them to be triggered). - - Set by the :option:`-S` option. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_NoUserSiteDirectory - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.user_site_directory` should be used instead, see - :ref:`Python Initialization Configuration `. - - Don't add the :data:`user site-packages directory ` to - :data:`sys.path`. - - Set by the :option:`-s` and :option:`-I` options, and the - :envvar:`PYTHONNOUSERSITE` environment variable. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_OptimizeFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.optimization_level` should be used instead, see - :ref:`Python Initialization Configuration `. - - Set by the :option:`-O` option and the :envvar:`PYTHONOPTIMIZE` environment - variable. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_QuietFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.quiet` should be used instead, see :ref:`Python - Initialization Configuration `. - - Don't display the copyright and version messages even in interactive mode. - - Set by the :option:`-q` option. - - .. versionadded:: 3.2 - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_UnbufferedStdioFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.buffered_stdio` should be used instead, see :ref:`Python - Initialization Configuration `. - - Force the stdout and stderr streams to be unbuffered. - - Set by the :option:`-u` option and the :envvar:`PYTHONUNBUFFERED` - environment variable. - - .. deprecated-removed:: 3.12 3.15 - -.. c:var:: int Py_VerboseFlag - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.verbose` should be used instead, see :ref:`Python - Initialization Configuration `. - - Print a message each time a module is initialized, showing the place - (filename or built-in module) from which it is loaded. If greater or equal - to ``2``, print a message for each file that is checked for when - searching for a module. Also provides information on module cleanup at exit. - - Set by the :option:`-v` option and the :envvar:`PYTHONVERBOSE` environment - variable. - - .. deprecated-removed:: 3.12 3.15 - - -Initializing and finalizing the interpreter -=========================================== - - -.. c:function:: void Py_Initialize() - - .. index:: - single: PyEval_InitThreads() - single: modules (in module sys) - single: path (in module sys) - pair: module; builtins - pair: module; __main__ - pair: module; sys - triple: module; search; path - single: Py_FinalizeEx (C function) - - Initialize the Python interpreter. In an application embedding Python, - this should be called before using any other Python/C API functions; see - :ref:`Before Python Initialization ` for the few exceptions. - - This initializes the table of loaded modules (``sys.modules``), and creates - the fundamental modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`. - It also initializes the module search path (``sys.path``). It does not set - ``sys.argv``; use the :ref:`Python Initialization Configuration ` - API for that. This is a no-op when called for a second time (without calling - :c:func:`Py_FinalizeEx` first). There is no return value; it is a fatal - error if the initialization fails. - - Use :c:func:`Py_InitializeFromConfig` to customize the - :ref:`Python Initialization Configuration `. - - .. note:: - On Windows, changes the console mode from ``O_TEXT`` to ``O_BINARY``, - which will also affect non-Python uses of the console using the C Runtime. - - -.. c:function:: void Py_InitializeEx(int initsigs) - - This function works like :c:func:`Py_Initialize` if *initsigs* is ``1``. If - *initsigs* is ``0``, it skips initialization registration of signal handlers, - which may be useful when CPython is embedded as part of a larger application. - - Use :c:func:`Py_InitializeFromConfig` to customize the - :ref:`Python Initialization Configuration `. - - -.. c:function:: PyStatus Py_InitializeFromConfig(const PyConfig *config) - - Initialize Python from *config* configuration, as described in - :ref:`init-from-config`. - - See the :ref:`init-config` section for details on pre-initializing the - interpreter, populating the runtime configuration structure, and querying - the returned status structure. - - -.. c:function:: int Py_IsInitialized() - - Return true (nonzero) when the Python interpreter has been initialized, false - (zero) if not. After :c:func:`Py_FinalizeEx` is called, this returns false until - :c:func:`Py_Initialize` is called again. - - -.. c:function:: int Py_IsFinalizing() - - Return true (non-zero) if the main Python interpreter is - :term:`shutting down `. Return false (zero) otherwise. - - .. versionadded:: 3.13 - - -.. c:function:: int Py_FinalizeEx() - - Undo all initializations made by :c:func:`Py_Initialize` and subsequent use of - Python/C API functions, and destroy all sub-interpreters (see - :c:func:`Py_NewInterpreter` below) that were created and not yet destroyed since - the last call to :c:func:`Py_Initialize`. This is a no-op when called for a second - time (without calling :c:func:`Py_Initialize` again first). - - Since this is the reverse of :c:func:`Py_Initialize`, it should be called - in the same thread with the same interpreter active. That means - the main thread and the main interpreter. - This should never be called while :c:func:`Py_RunMain` is running. - - Normally the return value is ``0``. - If there were errors during finalization (flushing buffered data), - ``-1`` is returned. - - Note that Python will do a best effort at freeing all memory allocated by the Python - interpreter. Therefore, any C-Extension should make sure to correctly clean up all - of the preveiously allocated PyObjects before using them in subsequent calls to - :c:func:`Py_Initialize`. Otherwise it could introduce vulnerabilities and incorrect - behavior. - - This function is provided for a number of reasons. An embedding application - might want to restart Python without having to restart the application itself. - An application that has loaded the Python interpreter from a dynamically - loadable library (or DLL) might want to free all memory allocated by Python - before unloading the DLL. During a hunt for memory leaks in an application a - developer might want to free all memory allocated by Python before exiting from - the application. - - **Bugs and caveats:** The destruction of modules and objects in modules is done - in random order; this may cause destructors (:meth:`~object.__del__` methods) to fail - when they depend on other objects (even functions) or modules. Dynamically - loaded extension modules loaded by Python are not unloaded. Small amounts of - memory allocated by the Python interpreter may not be freed (if you find a leak, - please report it). Memory tied up in circular references between objects is not - freed. Interned strings will all be deallocated regardless of their reference count. - Some memory allocated by extension modules may not be freed. Some extensions may not - work properly if their initialization routine is called more than once; this can - happen if an application calls :c:func:`Py_Initialize` and :c:func:`Py_FinalizeEx` - more than once. :c:func:`Py_FinalizeEx` must not be called recursively from - within itself. Therefore, it must not be called by any code that may be run - as part of the interpreter shutdown process, such as :py:mod:`atexit` - handlers, object finalizers, or any code that may be run while flushing the - stdout and stderr files. - - .. audit-event:: cpython._PySys_ClearAuditHooks "" c.Py_FinalizeEx - - .. versionadded:: 3.6 - - -.. c:function:: void Py_Finalize() - - This is a backwards-compatible version of :c:func:`Py_FinalizeEx` that - disregards the return value. - - -.. c:function:: int Py_BytesMain(int argc, char **argv) - - Similar to :c:func:`Py_Main` but *argv* is an array of bytes strings, - allowing the calling application to delegate the text decoding step to - the CPython runtime. - - .. versionadded:: 3.8 - - -.. c:function:: int Py_Main(int argc, wchar_t **argv) - - The main program for the standard interpreter, encapsulating a full - initialization/finalization cycle, as well as additional - behaviour to implement reading configurations settings from the environment - and command line, and then executing ``__main__`` in accordance with - :ref:`using-on-cmdline`. - - This is made available for programs which wish to support the full CPython - command line interface, rather than just embedding a Python runtime in a - larger application. - - The *argc* and *argv* parameters are similar to those which are passed to a - C program's :c:func:`main` function, except that the *argv* entries are first - converted to ``wchar_t`` using :c:func:`Py_DecodeLocale`. It is also - important to note that the argument list entries may be modified to point to - strings other than those passed in (however, the contents of the strings - pointed to by the argument list are not modified). - - The return value is ``2`` if the argument list does not represent a valid - Python command line, and otherwise the same as :c:func:`Py_RunMain`. - - In terms of the CPython runtime configuration APIs documented in the - :ref:`runtime configuration ` section (and without accounting - for error handling), ``Py_Main`` is approximately equivalent to:: - - PyConfig config; - PyConfig_InitPythonConfig(&config); - PyConfig_SetArgv(&config, argc, argv); - Py_InitializeFromConfig(&config); - PyConfig_Clear(&config); - - Py_RunMain(); - - In normal usage, an embedding application will call this function - *instead* of calling :c:func:`Py_Initialize`, :c:func:`Py_InitializeEx` or - :c:func:`Py_InitializeFromConfig` directly, and all settings will be applied - as described elsewhere in this documentation. If this function is instead - called *after* a preceding runtime initialization API call, then exactly - which environmental and command line configuration settings will be updated - is version dependent (as it depends on which settings correctly support - being modified after they have already been set once when the runtime was - first initialized). - - -.. c:function:: int Py_RunMain(void) - - Executes the main module in a fully configured CPython runtime. - - Executes the command (:c:member:`PyConfig.run_command`), the script - (:c:member:`PyConfig.run_filename`) or the module - (:c:member:`PyConfig.run_module`) specified on the command line or in the - configuration. If none of these values are set, runs the interactive Python - prompt (REPL) using the ``__main__`` module's global namespace. - - If :c:member:`PyConfig.inspect` is not set (the default), the return value - will be ``0`` if the interpreter exits normally (that is, without raising - an exception), the exit status of an unhandled :exc:`SystemExit`, or ``1`` - for any other unhandled exception. - - If :c:member:`PyConfig.inspect` is set (such as when the :option:`-i` option - is used), rather than returning when the interpreter exits, execution will - instead resume in an interactive Python prompt (REPL) using the ``__main__`` - module's global namespace. If the interpreter exited with an exception, it - is immediately raised in the REPL session. The function return value is - then determined by the way the *REPL session* terminates: ``0``, ``1``, or - the status of a :exc:`SystemExit`, as specified above. - - This function always finalizes the Python interpreter before it returns. - - See :ref:`Python Configuration ` for an example of a - customized Python that always runs in isolated mode using - :c:func:`Py_RunMain`. - -.. c:function:: int PyUnstable_AtExit(PyInterpreterState *interp, void (*func)(void *), void *data) - - Register an :mod:`atexit` callback for the target interpreter *interp*. - This is similar to :c:func:`Py_AtExit`, but takes an explicit interpreter and - data pointer for the callback. - - There must be an :term:`attached thread state` for *interp*. - - .. versionadded:: 3.13 - -Process-wide parameters -======================= - - -.. c:function:: void Py_SetProgramName(const wchar_t *name) - - .. index:: - single: Py_Initialize() - single: main() - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.program_name` should be used instead, see :ref:`Python - Initialization Configuration `. - - This function should be called before :c:func:`Py_Initialize` is called for - the first time, if it is called at all. It tells the interpreter the value - of the ``argv[0]`` argument to the :c:func:`main` function of the program - (converted to wide characters). - This is used by some other functions below to find - the Python run-time libraries relative to the interpreter executable. The - default value is ``'python'``. The argument should point to a - zero-terminated wide character string in static storage whose contents will not - change for the duration of the program's execution. No code in the Python - interpreter will change the contents of this storage. - - Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:expr:`wchar_t*` string. - - .. deprecated-removed:: 3.11 3.15 - - -.. c:function:: const char* Py_GetVersion() - - Return the version of this Python interpreter. This is a string that looks - something like :: - - "3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 4.2.3]" - - .. index:: single: version (in module sys) - - The first word (up to the first space character) is the current Python version; - the first characters are the major and minor version separated by a - period. The returned string points into static storage; the caller should not - modify its value. The value is available to Python code as :data:`sys.version`. - - See also the :c:var:`Py_Version` constant. - - -.. c:function:: const char* Py_GetPlatform() - - .. index:: single: platform (in module sys) - - Return the platform identifier for the current platform. On Unix, this is - formed from the "official" name of the operating system, converted to lower - case, followed by the major revision number; e.g., for Solaris 2.x, which is - also known as SunOS 5.x, the value is ``'sunos5'``. On macOS, it is - ``'darwin'``. On Windows, it is ``'win'``. The returned string points into - static storage; the caller should not modify its value. The value is available - to Python code as ``sys.platform``. - - -.. c:function:: const char* Py_GetCopyright() - - Return the official copyright string for the current Python version, for example - - ``'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'`` - - .. index:: single: copyright (in module sys) - - The returned string points into static storage; the caller should not modify its - value. The value is available to Python code as ``sys.copyright``. - - -.. c:function:: const char* Py_GetCompiler() - - Return an indication of the compiler used to build the current Python version, - in square brackets, for example:: - - "[GCC 2.7.2.2]" - - .. index:: single: version (in module sys) - - The returned string points into static storage; the caller should not modify its - value. The value is available to Python code as part of the variable - ``sys.version``. - - -.. c:function:: const char* Py_GetBuildInfo() - - Return information about the sequence number and build date and time of the - current Python interpreter instance, for example :: - - "#67, Aug 1 1997, 22:34:28" - - .. index:: single: version (in module sys) - - The returned string points into static storage; the caller should not modify its - value. The value is available to Python code as part of the variable - ``sys.version``. - - -.. c:function:: void PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath) - - .. index:: - single: main() - single: Py_FatalError() - single: argv (in module sys) - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.argv`, :c:member:`PyConfig.parse_argv` and - :c:member:`PyConfig.safe_path` should be used instead, see :ref:`Python - Initialization Configuration `. - - Set :data:`sys.argv` based on *argc* and *argv*. These parameters are - similar to those passed to the program's :c:func:`main` function with the - difference that the first entry should refer to the script file to be - executed rather than the executable hosting the Python interpreter. If there - isn't a script that will be run, the first entry in *argv* can be an empty - string. If this function fails to initialize :data:`sys.argv`, a fatal - condition is signalled using :c:func:`Py_FatalError`. - - If *updatepath* is zero, this is all the function does. If *updatepath* - is non-zero, the function also modifies :data:`sys.path` according to the - following algorithm: - - - If the name of an existing script is passed in ``argv[0]``, the absolute - path of the directory where the script is located is prepended to - :data:`sys.path`. - - Otherwise (that is, if *argc* is ``0`` or ``argv[0]`` doesn't point - to an existing file name), an empty string is prepended to - :data:`sys.path`, which is the same as prepending the current working - directory (``"."``). - - Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:expr:`wchar_t*` string. - - See also :c:member:`PyConfig.orig_argv` and :c:member:`PyConfig.argv` - members of the :ref:`Python Initialization Configuration `. - - .. note:: - It is recommended that applications embedding the Python interpreter - for purposes other than executing a single script pass ``0`` as *updatepath*, - and update :data:`sys.path` themselves if desired. - See :cve:`2008-5983`. - - On versions before 3.1.3, you can achieve the same effect by manually - popping the first :data:`sys.path` element after having called - :c:func:`PySys_SetArgv`, for example using:: - - PyRun_SimpleString("import sys; sys.path.pop(0)\n"); - - .. versionadded:: 3.1.3 - - .. XXX impl. doesn't seem consistent in allowing ``0``/``NULL`` for the params; - check w/ Guido. - - .. deprecated-removed:: 3.11 3.15 - - -.. c:function:: void PySys_SetArgv(int argc, wchar_t **argv) - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.argv` and :c:member:`PyConfig.parse_argv` should be used - instead, see :ref:`Python Initialization Configuration `. - - This function works like :c:func:`PySys_SetArgvEx` with *updatepath* set - to ``1`` unless the :program:`python` interpreter was started with the - :option:`-I`. - - Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:expr:`wchar_t*` string. - - See also :c:member:`PyConfig.orig_argv` and :c:member:`PyConfig.argv` - members of the :ref:`Python Initialization Configuration `. - - .. versionchanged:: 3.4 The *updatepath* value depends on :option:`-I`. - - .. deprecated-removed:: 3.11 3.15 - - -.. c:function:: void Py_SetPythonHome(const wchar_t *home) - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.home` should be used instead, see :ref:`Python - Initialization Configuration `. - - Set the default "home" directory, that is, the location of the standard - Python libraries. See :envvar:`PYTHONHOME` for the meaning of the - argument string. - - The argument should point to a zero-terminated character string in static - storage whose contents will not change for the duration of the program's - execution. No code in the Python interpreter will change the contents of - this storage. - - Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:expr:`wchar_t*` string. - - .. deprecated-removed:: 3.11 3.15 - - -.. _threads: - -Thread State and the Global Interpreter Lock -============================================ - -.. index:: - single: global interpreter lock - single: interpreter lock - single: lock, interpreter - -Unless on a :term:`free-threaded ` build of :term:`CPython`, -the Python interpreter is not fully thread-safe. In order to support -multi-threaded Python programs, there's a global lock, called the :term:`global -interpreter lock` or :term:`GIL`, that must be held by the current thread before -it can safely access Python objects. Without the lock, even the simplest -operations could cause problems in a multi-threaded program: for example, when -two threads simultaneously increment the reference count of the same object, the -reference count could end up being incremented only once instead of twice. - -.. index:: single: setswitchinterval (in module sys) - -Therefore, the rule exists that only the thread that has acquired the -:term:`GIL` may operate on Python objects or call Python/C API functions. -In order to emulate concurrency of execution, the interpreter regularly -tries to switch threads (see :func:`sys.setswitchinterval`). The lock is also -released around potentially blocking I/O operations like reading or writing -a file, so that other Python threads can run in the meantime. - -.. index:: - single: PyThreadState (C type) - -The Python interpreter keeps some thread-specific bookkeeping information -inside a data structure called :c:type:`PyThreadState`, known as a :term:`thread state`. -Each OS thread has a thread-local pointer to a :c:type:`PyThreadState`; a thread state -referenced by this pointer is considered to be :term:`attached `. - -A thread can only have one :term:`attached thread state` at a time. An attached -thread state is typically analogous with holding the :term:`GIL`, except on -:term:`free-threaded ` builds. On builds with the :term:`GIL` enabled, -:term:`attaching ` a thread state will block until the :term:`GIL` -can be acquired. However, even on builds with the :term:`GIL` disabled, it is still required -to have an attached thread state to call most of the C API. - -In general, there will always be an :term:`attached thread state` when using Python's C API. -Only in some specific cases (such as in a :c:macro:`Py_BEGIN_ALLOW_THREADS` block) will the -thread not have an attached thread state. If uncertain, check if :c:func:`PyThreadState_GetUnchecked` returns -``NULL``. - -Detaching the thread state from extension code ----------------------------------------------- - -Most extension code manipulating the :term:`thread state` has the following simple -structure:: - - Save the thread state in a local variable. - ... Do some blocking I/O operation ... - Restore the thread state from the local variable. - -This is so common that a pair of macros exists to simplify it:: - - Py_BEGIN_ALLOW_THREADS - ... Do some blocking I/O operation ... - Py_END_ALLOW_THREADS - -.. index:: - single: Py_BEGIN_ALLOW_THREADS (C macro) - single: Py_END_ALLOW_THREADS (C macro) - -The :c:macro:`Py_BEGIN_ALLOW_THREADS` macro opens a new block and declares a -hidden local variable; the :c:macro:`Py_END_ALLOW_THREADS` macro closes the -block. - -The block above expands to the following code:: - - PyThreadState *_save; - - _save = PyEval_SaveThread(); - ... Do some blocking I/O operation ... - PyEval_RestoreThread(_save); - -.. index:: - single: PyEval_RestoreThread (C function) - single: PyEval_SaveThread (C function) - -Here is how these functions work: - -The :term:`attached thread state` holds the :term:`GIL` for the entire interpreter. When detaching -the :term:`attached thread state`, the :term:`GIL` is released, allowing other threads to attach -a thread state to their own thread, thus getting the :term:`GIL` and can start executing. -The pointer to the prior :term:`attached thread state` is stored as a local variable. -Upon reaching :c:macro:`Py_END_ALLOW_THREADS`, the thread state that was -previously :term:`attached ` is passed to :c:func:`PyEval_RestoreThread`. -This function will block until another releases its :term:`thread state `, -thus allowing the old :term:`thread state ` to get re-attached and the -C API can be called again. - -For :term:`free-threaded ` builds, the :term:`GIL` is normally -out of the question, but detaching the :term:`thread state ` is still required -for blocking I/O and long operations. The difference is that threads don't have to wait for the :term:`GIL` -to be released to attach their thread state, allowing true multi-core parallelism. - -.. note:: - Calling system I/O functions is the most common use case for detaching - the :term:`thread state `, but it can also be useful before calling - long-running computations which don't need access to Python objects, such - as compression or cryptographic functions operating over memory buffers. - For example, the standard :mod:`zlib` and :mod:`hashlib` modules detach the - :term:`thread state ` when compressing or hashing data. - - -.. _gilstate: - -Non-Python created threads --------------------------- - -When threads are created using the dedicated Python APIs (such as the -:mod:`threading` module), a thread state is automatically associated to them -and the code showed above is therefore correct. However, when threads are -created from C (for example by a third-party library with its own thread -management), they don't hold the :term:`GIL`, because they don't have an -:term:`attached thread state`. - -If you need to call Python code from these threads (often this will be part -of a callback API provided by the aforementioned third-party library), -you must first register these threads with the interpreter by -creating an :term:`attached thread state` before you can start using the Python/C -API. When you are done, you should detach the :term:`thread state `, and -finally free it. - -The :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release` functions do -all of the above automatically. The typical idiom for calling into Python -from a C thread is:: - - PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); - - /* Perform Python actions here. */ - result = CallSomeFunction(); - /* evaluate result or handle exception */ - - /* Release the thread. No Python API allowed beyond this point. */ - PyGILState_Release(gstate); - -Note that the ``PyGILState_*`` functions assume there is only one global -interpreter (created automatically by :c:func:`Py_Initialize`). Python -supports the creation of additional interpreters (using -:c:func:`Py_NewInterpreter`), but mixing multiple interpreters and the -``PyGILState_*`` API is unsupported. This is because :c:func:`PyGILState_Ensure` -and similar functions default to :term:`attaching ` a -:term:`thread state` for the main interpreter, meaning that the thread can't safely -interact with the calling subinterpreter. - -Supporting subinterpreters in non-Python threads ------------------------------------------------- - -If you would like to support subinterpreters with non-Python created threads, you -must use the ``PyThreadState_*`` API instead of the traditional ``PyGILState_*`` -API. - -In particular, you must store the interpreter state from the calling -function and pass it to :c:func:`PyThreadState_New`, which will ensure that -the :term:`thread state` is targeting the correct interpreter:: - - /* The return value of PyInterpreterState_Get() from the - function that created this thread. */ - PyInterpreterState *interp = ThreadData->interp; - PyThreadState *tstate = PyThreadState_New(interp); - PyThreadState_Swap(tstate); - - /* GIL of the subinterpreter is now held. - Perform Python actions here. */ - result = CallSomeFunction(); - /* evaluate result or handle exception */ - - /* Destroy the thread state. No Python API allowed beyond this point. */ - PyThreadState_Clear(tstate); - PyThreadState_DeleteCurrent(); - -.. _fork-and-threads: - -Cautions about fork() ---------------------- - -Another important thing to note about threads is their behaviour in the face -of the C :c:func:`fork` call. On most systems with :c:func:`fork`, after a -process forks only the thread that issued the fork will exist. This has a -concrete impact both on how locks must be handled and on all stored state -in CPython's runtime. - -The fact that only the "current" thread remains -means any locks held by other threads will never be released. Python solves -this for :func:`os.fork` by acquiring the locks it uses internally before -the fork, and releasing them afterwards. In addition, it resets any -:ref:`lock-objects` in the child. When extending or embedding Python, there -is no way to inform Python of additional (non-Python) locks that need to be -acquired before or reset after a fork. OS facilities such as -:c:func:`!pthread_atfork` would need to be used to accomplish the same thing. -Additionally, when extending or embedding Python, calling :c:func:`fork` -directly rather than through :func:`os.fork` (and returning to or calling -into Python) may result in a deadlock by one of Python's internal locks -being held by a thread that is defunct after the fork. -:c:func:`PyOS_AfterFork_Child` tries to reset the necessary locks, but is not -always able to. - -The fact that all other threads go away also means that CPython's -runtime state there must be cleaned up properly, which :func:`os.fork` -does. This means finalizing all other :c:type:`PyThreadState` objects -belonging to the current interpreter and all other -:c:type:`PyInterpreterState` objects. Due to this and the special -nature of the :ref:`"main" interpreter `, -:c:func:`fork` should only be called in that interpreter's "main" -thread, where the CPython global runtime was originally initialized. -The only exception is if :c:func:`exec` will be called immediately -after. - -.. _cautions-regarding-runtime-finalization: - -Cautions regarding runtime finalization ---------------------------------------- - -In the late stage of :term:`interpreter shutdown`, after attempting to wait for -non-daemon threads to exit (though this can be interrupted by -:class:`KeyboardInterrupt`) and running the :mod:`atexit` functions, the runtime -is marked as *finalizing*: :c:func:`Py_IsFinalizing` and -:func:`sys.is_finalizing` return true. At this point, only the *finalization -thread* that initiated finalization (typically the main thread) is allowed to -acquire the :term:`GIL`. - -If any thread, other than the finalization thread, attempts to attach a :term:`thread state` -during finalization, either explicitly or -implicitly, the thread enters **a permanently blocked state** -where it remains until the program exits. In most cases this is harmless, but this can result -in deadlock if a later stage of finalization attempts to acquire a lock owned by the -blocked thread, or otherwise waits on the blocked thread. - -Gross? Yes. This prevents random crashes and/or unexpectedly skipped C++ -finalizations further up the call stack when such threads were forcibly exited -here in CPython 3.13 and earlier. The CPython runtime :term:`thread state` C APIs -have never had any error reporting or handling expectations at :term:`thread state` -attachment time that would've allowed for graceful exit from this situation. Changing that -would require new stable C APIs and rewriting the majority of C code in the -CPython ecosystem to use those with error handling. - - -High-level API --------------- - -These are the most commonly used types and functions when writing C extension -code, or when embedding the Python interpreter: - -.. c:type:: PyInterpreterState - - This data structure represents the state shared by a number of cooperating - threads. Threads belonging to the same interpreter share their module - administration and a few other internal items. There are no public members in - this structure. - - Threads belonging to different interpreters initially share nothing, except - process state like available memory, open file descriptors and such. The global - interpreter lock is also shared by all threads, regardless of to which - interpreter they belong. - - -.. c:type:: PyThreadState - - This data structure represents the state of a single thread. The only public - data member is: - - .. c:member:: PyInterpreterState *interp - - This thread's interpreter state. - - -.. c:function:: void PyEval_InitThreads() - - .. index:: - single: PyEval_AcquireThread() - single: PyEval_ReleaseThread() - single: PyEval_SaveThread() - single: PyEval_RestoreThread() - - Deprecated function which does nothing. - - In Python 3.6 and older, this function created the GIL if it didn't exist. - - .. versionchanged:: 3.9 - The function now does nothing. - - .. versionchanged:: 3.7 - This function is now called by :c:func:`Py_Initialize()`, so you don't - have to call it yourself anymore. - - .. versionchanged:: 3.2 - This function cannot be called before :c:func:`Py_Initialize()` anymore. - - .. deprecated:: 3.9 - - .. index:: pair: module; _thread - - -.. c:function:: PyThreadState* PyEval_SaveThread() - - Detach the :term:`attached thread state` and return it. - The thread will have no :term:`thread state` upon returning. - - -.. c:function:: void PyEval_RestoreThread(PyThreadState *tstate) - - Set the :term:`attached thread state` to *tstate*. - The passed :term:`thread state` **should not** be :term:`attached `, - otherwise deadlock ensues. *tstate* will be attached upon returning. - - .. note:: - Calling this function from a thread when the runtime is finalizing will - hang the thread until the program exits, even if the thread was not - created by Python. Refer to - :ref:`cautions-regarding-runtime-finalization` for more details. - - .. versionchanged:: 3.14 - Hangs the current thread, rather than terminating it, if called while the - interpreter is finalizing. - -.. c:function:: PyThreadState* PyThreadState_Get() - - Return the :term:`attached thread state`. If the thread has no attached - thread state, (such as when inside of :c:macro:`Py_BEGIN_ALLOW_THREADS` - block), then this issues a fatal error (so that the caller needn't check - for ``NULL``). - - See also :c:func:`PyThreadState_GetUnchecked`. - -.. c:function:: PyThreadState* PyThreadState_GetUnchecked() - - Similar to :c:func:`PyThreadState_Get`, but don't kill the process with a - fatal error if it is NULL. The caller is responsible to check if the result - is NULL. - - .. versionadded:: 3.13 - In Python 3.5 to 3.12, the function was private and known as - ``_PyThreadState_UncheckedGet()``. - - -.. c:function:: PyThreadState* PyThreadState_Swap(PyThreadState *tstate) - - Set the :term:`attached thread state` to *tstate*, and return the - :term:`thread state` that was attached prior to calling. - - This function is safe to call without an :term:`attached thread state`; it - will simply return ``NULL`` indicating that there was no prior thread state. - - .. seealso: - :c:func:`PyEval_ReleaseThread` - - .. note:: - Similar to :c:func:`PyGILState_Ensure`, this function will hang the - thread if the runtime is finalizing. - - -The following functions use thread-local storage, and are not compatible -with sub-interpreters: - -.. c:function:: PyGILState_STATE PyGILState_Ensure() - - Ensure that the current thread is ready to call the Python C API regardless - of the current state of Python, or of the :term:`attached thread state`. This may - be called as many times as desired by a thread as long as each call is - matched with a call to :c:func:`PyGILState_Release`. In general, other - thread-related APIs may be used between :c:func:`PyGILState_Ensure` and - :c:func:`PyGILState_Release` calls as long as the thread state is restored to - its previous state before the Release(). For example, normal usage of the - :c:macro:`Py_BEGIN_ALLOW_THREADS` and :c:macro:`Py_END_ALLOW_THREADS` macros is - acceptable. - - The return value is an opaque "handle" to the :term:`attached thread state` when - :c:func:`PyGILState_Ensure` was called, and must be passed to - :c:func:`PyGILState_Release` to ensure Python is left in the same state. Even - though recursive calls are allowed, these handles *cannot* be shared - each - unique call to :c:func:`PyGILState_Ensure` must save the handle for its call - to :c:func:`PyGILState_Release`. - - When the function returns, there will be an :term:`attached thread state` - and the thread will be able to call arbitrary Python code. Failure is a fatal error. - - .. warning:: - Calling this function when the runtime is finalizing is unsafe. Doing - so will either hang the thread until the program ends, or fully crash - the interpreter in rare cases. Refer to - :ref:`cautions-regarding-runtime-finalization` for more details. - - .. versionchanged:: 3.14 - Hangs the current thread, rather than terminating it, if called while the - interpreter is finalizing. - -.. c:function:: void PyGILState_Release(PyGILState_STATE) - - Release any resources previously acquired. After this call, Python's state will - be the same as it was prior to the corresponding :c:func:`PyGILState_Ensure` call - (but generally this state will be unknown to the caller, hence the use of the - GILState API). - - Every call to :c:func:`PyGILState_Ensure` must be matched by a call to - :c:func:`PyGILState_Release` on the same thread. - -.. c:function:: PyThreadState* PyGILState_GetThisThreadState() - - Get the :term:`attached thread state` for this thread. May return ``NULL`` if no - GILState API has been used on the current thread. Note that the main thread - always has such a thread-state, even if no auto-thread-state call has been - made on the main thread. This is mainly a helper/diagnostic function. - - .. note:: - This function does not account for :term:`thread states ` created - by something other than :c:func:`PyGILState_Ensure` (such as :c:func:`PyThreadState_New`). - Prefer :c:func:`PyThreadState_Get` or :c:func:`PyThreadState_GetUnchecked` - for most cases. - - .. seealso: :c:func:`PyThreadState_Get`` - -.. c:function:: int PyGILState_Check() - - Return ``1`` if the current thread is holding the :term:`GIL` and ``0`` otherwise. - This function can be called from any thread at any time. - Only if it has had its :term:`thread state ` initialized - via :c:func:`PyGILState_Ensure` will it return ``1``. - This is mainly a helper/diagnostic function. It can be useful - for example in callback contexts or memory allocation functions when - knowing that the :term:`GIL` is locked can allow the caller to perform sensitive - actions or otherwise behave differently. - - .. note:: - If the current Python process has ever created a subinterpreter, this - function will *always* return ``1``. Prefer :c:func:`PyThreadState_GetUnchecked` - for most cases. - - .. versionadded:: 3.4 - - -The following macros are normally used without a trailing semicolon; look for -example usage in the Python source distribution. - - -.. c:macro:: Py_BEGIN_ALLOW_THREADS - - This macro expands to ``{ PyThreadState *_save; _save = PyEval_SaveThread();``. - Note that it contains an opening brace; it must be matched with a following - :c:macro:`Py_END_ALLOW_THREADS` macro. See above for further discussion of this - macro. - - -.. c:macro:: Py_END_ALLOW_THREADS - - This macro expands to ``PyEval_RestoreThread(_save); }``. Note that it contains - a closing brace; it must be matched with an earlier - :c:macro:`Py_BEGIN_ALLOW_THREADS` macro. See above for further discussion of - this macro. - - -.. c:macro:: Py_BLOCK_THREADS - - This macro expands to ``PyEval_RestoreThread(_save);``: it is equivalent to - :c:macro:`Py_END_ALLOW_THREADS` without the closing brace. - - -.. c:macro:: Py_UNBLOCK_THREADS - - This macro expands to ``_save = PyEval_SaveThread();``: it is equivalent to - :c:macro:`Py_BEGIN_ALLOW_THREADS` without the opening brace and variable - declaration. - - -Low-level API -------------- - -All of the following functions must be called after :c:func:`Py_Initialize`. - -.. versionchanged:: 3.7 - :c:func:`Py_Initialize()` now initializes the :term:`GIL` - and sets an :term:`attached thread state`. - - -.. c:function:: PyInterpreterState* PyInterpreterState_New() - - Create a new interpreter state object. An :term:`attached thread state` is not needed, - but may optionally exist if it is necessary to serialize calls to this - function. - - .. audit-event:: cpython.PyInterpreterState_New "" c.PyInterpreterState_New - - -.. c:function:: void PyInterpreterState_Clear(PyInterpreterState *interp) - - Reset all information in an interpreter state object. There must be - an :term:`attached thread state` for the interpreter. - - .. audit-event:: cpython.PyInterpreterState_Clear "" c.PyInterpreterState_Clear - - -.. c:function:: void PyInterpreterState_Delete(PyInterpreterState *interp) - - Destroy an interpreter state object. There **should not** be an - :term:`attached thread state` for the target interpreter. The interpreter - state must have been reset with a previous call to :c:func:`PyInterpreterState_Clear`. - - -.. c:function:: PyThreadState* PyThreadState_New(PyInterpreterState *interp) - - Create a new thread state object belonging to the given interpreter object. - An :term:`attached thread state` is not needed. - -.. c:function:: void PyThreadState_Clear(PyThreadState *tstate) - - Reset all information in a :term:`thread state` object. *tstate* - must be :term:`attached ` - - .. versionchanged:: 3.9 - This function now calls the :c:member:`PyThreadState.on_delete` callback. - Previously, that happened in :c:func:`PyThreadState_Delete`. - - .. versionchanged:: 3.13 - The :c:member:`PyThreadState.on_delete` callback was removed. - - -.. c:function:: void PyThreadState_Delete(PyThreadState *tstate) - - Destroy a :term:`thread state` object. *tstate* should not - be :term:`attached ` to any thread. - *tstate* must have been reset with a previous call to - :c:func:`PyThreadState_Clear`. - - -.. c:function:: void PyThreadState_DeleteCurrent(void) - - Detach the :term:`attached thread state` (which must have been reset - with a previous call to :c:func:`PyThreadState_Clear`) and then destroy it. - - No :term:`thread state` will be :term:`attached ` upon - returning. - -.. c:function:: PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) - - Get the current frame of the Python thread state *tstate*. - - Return a :term:`strong reference`. Return ``NULL`` if no frame is currently - executing. - - See also :c:func:`PyEval_GetFrame`. - - *tstate* must not be ``NULL``, and must be :term:`attached `. - - .. versionadded:: 3.9 - - -.. c:function:: uint64_t PyThreadState_GetID(PyThreadState *tstate) - - Get the unique :term:`thread state` identifier of the Python thread state *tstate*. - - *tstate* must not be ``NULL``, and must be :term:`attached `. - - .. versionadded:: 3.9 - - -.. c:function:: PyInterpreterState* PyThreadState_GetInterpreter(PyThreadState *tstate) - - Get the interpreter of the Python thread state *tstate*. - - *tstate* must not be ``NULL``, and must be :term:`attached `. - - .. versionadded:: 3.9 - - -.. c:function:: void PyThreadState_EnterTracing(PyThreadState *tstate) - - Suspend tracing and profiling in the Python thread state *tstate*. - - Resume them using the :c:func:`PyThreadState_LeaveTracing` function. - - .. versionadded:: 3.11 - - -.. c:function:: void PyThreadState_LeaveTracing(PyThreadState *tstate) - - Resume tracing and profiling in the Python thread state *tstate* suspended - by the :c:func:`PyThreadState_EnterTracing` function. - - See also :c:func:`PyEval_SetTrace` and :c:func:`PyEval_SetProfile` - functions. - - .. versionadded:: 3.11 - - -.. c:function:: PyInterpreterState* PyInterpreterState_Get(void) - - Get the current interpreter. - - Issue a fatal error if there no :term:`attached thread state`. - It cannot return NULL. - - .. versionadded:: 3.9 - - -.. c:function:: int64_t PyInterpreterState_GetID(PyInterpreterState *interp) - - Return the interpreter's unique ID. If there was any error in doing - so then ``-1`` is returned and an error is set. - - The caller must have an :term:`attached thread state`. - - .. versionadded:: 3.7 - - -.. c:function:: PyObject* PyInterpreterState_GetDict(PyInterpreterState *interp) - - Return a dictionary in which interpreter-specific data may be stored. - If this function returns ``NULL`` then no exception has been raised and - the caller should assume no interpreter-specific dict is available. - - This is not a replacement for :c:func:`PyModule_GetState()`, which - extensions should use to store interpreter-specific state information. - - .. versionadded:: 3.8 - - -.. c:type:: PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) - - Type of a frame evaluation function. - - The *throwflag* parameter is used by the ``throw()`` method of generators: - if non-zero, handle the current exception. - - .. versionchanged:: 3.9 - The function now takes a *tstate* parameter. - - .. versionchanged:: 3.11 - The *frame* parameter changed from ``PyFrameObject*`` to ``_PyInterpreterFrame*``. - -.. c:function:: _PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp) - - Get the frame evaluation function. - - See the :pep:`523` "Adding a frame evaluation API to CPython". - - .. versionadded:: 3.9 - -.. c:function:: void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame) - - Set the frame evaluation function. - - See the :pep:`523` "Adding a frame evaluation API to CPython". - - .. versionadded:: 3.9 - - -.. c:function:: PyObject* PyThreadState_GetDict() - - Return a dictionary in which extensions can store thread-specific state - information. Each extension should use a unique key to use to store state in - the dictionary. It is okay to call this function when no :term:`thread state` - is :term:`attached `. If this function returns - ``NULL``, no exception has been raised and the caller should assume no - thread state is attached. - - -.. c:function:: int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) - - Asynchronously raise an exception in a thread. The *id* argument is the thread - id of the target thread; *exc* is the exception object to be raised. This - function does not steal any references to *exc*. To prevent naive misuse, you - must write your own C extension to call this. Must be called with an :term:`attached thread state`. - Returns the number of thread states modified; this is normally one, but will be - zero if the thread id isn't found. If *exc* is ``NULL``, the pending - exception (if any) for the thread is cleared. This raises no exceptions. - - .. versionchanged:: 3.7 - The type of the *id* parameter changed from :c:expr:`long` to - :c:expr:`unsigned long`. - -.. c:function:: void PyEval_AcquireThread(PyThreadState *tstate) - - :term:`Attach ` *tstate* to the current thread, - which must not be ``NULL`` or already :term:`attached `. - - The calling thread must not already have an :term:`attached thread state`. - - .. note:: - Calling this function from a thread when the runtime is finalizing will - hang the thread until the program exits, even if the thread was not - created by Python. Refer to - :ref:`cautions-regarding-runtime-finalization` for more details. - - .. versionchanged:: 3.8 - Updated to be consistent with :c:func:`PyEval_RestoreThread`, - :c:func:`Py_END_ALLOW_THREADS`, and :c:func:`PyGILState_Ensure`, - and terminate the current thread if called while the interpreter is finalizing. - - .. versionchanged:: 3.14 - Hangs the current thread, rather than terminating it, if called while the - interpreter is finalizing. - - :c:func:`PyEval_RestoreThread` is a higher-level function which is always - available (even when threads have not been initialized). - - -.. c:function:: void PyEval_ReleaseThread(PyThreadState *tstate) - - Detach the :term:`attached thread state`. - The *tstate* argument, which must not be ``NULL``, is only used to check - that it represents the :term:`attached thread state` --- if it isn't, a fatal error is - reported. - - :c:func:`PyEval_SaveThread` is a higher-level function which is always - available (even when threads have not been initialized). - - -.. _sub-interpreter-support: - -Sub-interpreter support -======================= - -While in most uses, you will only embed a single Python interpreter, there -are cases where you need to create several independent interpreters in the -same process and perhaps even in the same thread. Sub-interpreters allow -you to do that. - -The "main" interpreter is the first one created when the runtime initializes. -It is usually the only Python interpreter in a process. Unlike sub-interpreters, -the main interpreter has unique process-global responsibilities like signal -handling. It is also responsible for execution during runtime initialization and -is usually the active interpreter during runtime finalization. The -:c:func:`PyInterpreterState_Main` function returns a pointer to its state. - -You can switch between sub-interpreters using the :c:func:`PyThreadState_Swap` -function. You can create and destroy them using the following functions: - - -.. c:type:: PyInterpreterConfig - - Structure containing most parameters to configure a sub-interpreter. - Its values are used only in :c:func:`Py_NewInterpreterFromConfig` and - never modified by the runtime. - - .. versionadded:: 3.12 - - Structure fields: - - .. c:member:: int use_main_obmalloc - - If this is ``0`` then the sub-interpreter will use its own - "object" allocator state. - Otherwise it will use (share) the main interpreter's. - - If this is ``0`` then - :c:member:`~PyInterpreterConfig.check_multi_interp_extensions` - must be ``1`` (non-zero). - If this is ``1`` then :c:member:`~PyInterpreterConfig.gil` - must not be :c:macro:`PyInterpreterConfig_OWN_GIL`. - - .. c:member:: int allow_fork - - If this is ``0`` then the runtime will not support forking the - process in any thread where the sub-interpreter is currently active. - Otherwise fork is unrestricted. - - Note that the :mod:`subprocess` module still works - when fork is disallowed. - - .. c:member:: int allow_exec - - If this is ``0`` then the runtime will not support replacing the - current process via exec (e.g. :func:`os.execv`) in any thread - where the sub-interpreter is currently active. - Otherwise exec is unrestricted. - - Note that the :mod:`subprocess` module still works - when exec is disallowed. - - .. c:member:: int allow_threads - - If this is ``0`` then the sub-interpreter's :mod:`threading` module - won't create threads. - Otherwise threads are allowed. - - .. c:member:: int allow_daemon_threads - - If this is ``0`` then the sub-interpreter's :mod:`threading` module - won't create daemon threads. - Otherwise daemon threads are allowed (as long as - :c:member:`~PyInterpreterConfig.allow_threads` is non-zero). - - .. c:member:: int check_multi_interp_extensions - - If this is ``0`` then all extension modules may be imported, - including legacy (single-phase init) modules, - in any thread where the sub-interpreter is currently active. - Otherwise only multi-phase init extension modules - (see :pep:`489`) may be imported. - (Also see :c:macro:`Py_mod_multiple_interpreters`.) - - This must be ``1`` (non-zero) if - :c:member:`~PyInterpreterConfig.use_main_obmalloc` is ``0``. - - .. c:member:: int gil - - This determines the operation of the GIL for the sub-interpreter. - It may be one of the following: - - .. c:namespace:: NULL - - .. c:macro:: PyInterpreterConfig_DEFAULT_GIL - - Use the default selection (:c:macro:`PyInterpreterConfig_SHARED_GIL`). - - .. c:macro:: PyInterpreterConfig_SHARED_GIL - - Use (share) the main interpreter's GIL. - - .. c:macro:: PyInterpreterConfig_OWN_GIL - - Use the sub-interpreter's own GIL. - - If this is :c:macro:`PyInterpreterConfig_OWN_GIL` then - :c:member:`PyInterpreterConfig.use_main_obmalloc` must be ``0``. - - -.. c:function:: PyStatus Py_NewInterpreterFromConfig(PyThreadState **tstate_p, const PyInterpreterConfig *config) - - .. index:: - pair: module; builtins - pair: module; __main__ - pair: module; sys - single: stdout (in module sys) - single: stderr (in module sys) - single: stdin (in module sys) - - Create a new sub-interpreter. This is an (almost) totally separate environment - for the execution of Python code. In particular, the new interpreter has - separate, independent versions of all imported modules, including the - fundamental modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`. The - table of loaded modules (``sys.modules``) and the module search path - (``sys.path``) are also separate. The new environment has no ``sys.argv`` - variable. It has new standard I/O stream file objects ``sys.stdin``, - ``sys.stdout`` and ``sys.stderr`` (however these refer to the same underlying - file descriptors). - - The given *config* controls the options with which the interpreter - is initialized. - - Upon success, *tstate_p* will be set to the first :term:`thread state` - created in the new sub-interpreter. This thread state is - :term:`attached `. - Note that no actual thread is created; see the discussion of thread states - below. If creation of the new interpreter is unsuccessful, - *tstate_p* is set to ``NULL``; - no exception is set since the exception state is stored in the - :term:`attached thread state`, which might not exist. - - Like all other Python/C API functions, an :term:`attached thread state` - must be present before calling this function, but it might be detached upon - returning. On success, the returned thread state will be :term:`attached `. - If the sub-interpreter is created with its own :term:`GIL` then the - :term:`attached thread state` of the calling interpreter will be detached. - When the function returns, the new interpreter's :term:`thread state` - will be :term:`attached ` to the current thread and - the previous interpreter's :term:`attached thread state` will remain detached. - - .. versionadded:: 3.12 - - Sub-interpreters are most effective when isolated from each other, - with certain functionality restricted:: - - PyInterpreterConfig config = { - .use_main_obmalloc = 0, - .allow_fork = 0, - .allow_exec = 0, - .allow_threads = 1, - .allow_daemon_threads = 0, - .check_multi_interp_extensions = 1, - .gil = PyInterpreterConfig_OWN_GIL, - }; - PyThreadState *tstate = NULL; - PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } - - Note that the config is used only briefly and does not get modified. - During initialization the config's values are converted into various - :c:type:`PyInterpreterState` values. A read-only copy of the config - may be stored internally on the :c:type:`PyInterpreterState`. - - .. index:: - single: Py_FinalizeEx (C function) - single: Py_Initialize (C function) - - Extension modules are shared between (sub-)interpreters as follows: - - * For modules using multi-phase initialization, - e.g. :c:func:`PyModule_FromDefAndSpec`, a separate module object is - created and initialized for each interpreter. - Only C-level static and global variables are shared between these - module objects. - - * For modules using single-phase initialization, - e.g. :c:func:`PyModule_Create`, the first time a particular extension - is imported, it is initialized normally, and a (shallow) copy of its - module's dictionary is squirreled away. - When the same extension is imported by another (sub-)interpreter, a new - module is initialized and filled with the contents of this copy; the - extension's ``init`` function is not called. - Objects in the module's dictionary thus end up shared across - (sub-)interpreters, which might cause unwanted behavior (see - `Bugs and caveats`_ below). - - Note that this is different from what happens when an extension is - imported after the interpreter has been completely re-initialized by - calling :c:func:`Py_FinalizeEx` and :c:func:`Py_Initialize`; in that - case, the extension's ``initmodule`` function *is* called again. - As with multi-phase initialization, this means that only C-level static - and global variables are shared between these modules. - - .. index:: single: close (in module os) - - -.. c:function:: PyThreadState* Py_NewInterpreter(void) - - .. index:: - pair: module; builtins - pair: module; __main__ - pair: module; sys - single: stdout (in module sys) - single: stderr (in module sys) - single: stdin (in module sys) - - Create a new sub-interpreter. This is essentially just a wrapper - around :c:func:`Py_NewInterpreterFromConfig` with a config that - preserves the existing behavior. The result is an unisolated - sub-interpreter that shares the main interpreter's GIL, allows - fork/exec, allows daemon threads, and allows single-phase init - modules. - - -.. c:function:: void Py_EndInterpreter(PyThreadState *tstate) - - .. index:: single: Py_FinalizeEx (C function) - - Destroy the (sub-)interpreter represented by the given :term:`thread state`. - The given thread state must be :term:`attached `. - When the call returns, there will be no :term:`attached thread state`. - All thread states associated with this interpreter are destroyed. - - :c:func:`Py_FinalizeEx` will destroy all sub-interpreters that - haven't been explicitly destroyed at that point. - - -A Per-Interpreter GIL ---------------------- - -Using :c:func:`Py_NewInterpreterFromConfig` you can create -a sub-interpreter that is completely isolated from other interpreters, -including having its own GIL. The most important benefit of this -isolation is that such an interpreter can execute Python code without -being blocked by other interpreters or blocking any others. Thus a -single Python process can truly take advantage of multiple CPU cores -when running Python code. The isolation also encourages a different -approach to concurrency than that of just using threads. -(See :pep:`554`.) - -Using an isolated interpreter requires vigilance in preserving that -isolation. That especially means not sharing any objects or mutable -state without guarantees about thread-safety. Even objects that are -otherwise immutable (e.g. ``None``, ``(1, 5)``) can't normally be shared -because of the refcount. One simple but less-efficient approach around -this is to use a global lock around all use of some state (or object). -Alternately, effectively immutable objects (like integers or strings) -can be made safe in spite of their refcounts by making them :term:`immortal`. -In fact, this has been done for the builtin singletons, small integers, -and a number of other builtin objects. - -If you preserve isolation then you will have access to proper multi-core -computing without the complications that come with free-threading. -Failure to preserve isolation will expose you to the full consequences -of free-threading, including races and hard-to-debug crashes. - -Aside from that, one of the main challenges of using multiple isolated -interpreters is how to communicate between them safely (not break -isolation) and efficiently. The runtime and stdlib do not provide -any standard approach to this yet. A future stdlib module would help -mitigate the effort of preserving isolation and expose effective tools -for communicating (and sharing) data between interpreters. - -.. versionadded:: 3.12 - - -Bugs and caveats ----------------- - -Because sub-interpreters (and the main interpreter) are part of the same -process, the insulation between them isn't perfect --- for example, using -low-level file operations like :func:`os.close` they can -(accidentally or maliciously) affect each other's open files. Because of the -way extensions are shared between (sub-)interpreters, some extensions may not -work properly; this is especially likely when using single-phase initialization -or (static) global variables. -It is possible to insert objects created in one sub-interpreter into -a namespace of another (sub-)interpreter; this should be avoided if possible. - -Special care should be taken to avoid sharing user-defined functions, -methods, instances or classes between sub-interpreters, since import -operations executed by such objects may affect the wrong (sub-)interpreter's -dictionary of loaded modules. It is equally important to avoid sharing -objects from which the above are reachable. - -Also note that combining this functionality with ``PyGILState_*`` APIs -is delicate, because these APIs assume a bijection between Python thread states -and OS-level threads, an assumption broken by the presence of sub-interpreters. -It is highly recommended that you don't switch sub-interpreters between a pair -of matching :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release` calls. -Furthermore, extensions (such as :mod:`ctypes`) using these APIs to allow calling -of Python code from non-Python created threads will probably be broken when using -sub-interpreters. - - -Asynchronous Notifications -========================== - -A mechanism is provided to make asynchronous notifications to the main -interpreter thread. These notifications take the form of a function -pointer and a void pointer argument. - - -.. c:function:: int Py_AddPendingCall(int (*func)(void *), void *arg) - - Schedule a function to be called from the main interpreter thread. On - success, ``0`` is returned and *func* is queued for being called in the - main thread. On failure, ``-1`` is returned without setting any exception. - - When successfully queued, *func* will be *eventually* called from the - main interpreter thread with the argument *arg*. It will be called - asynchronously with respect to normally running Python code, but with - both these conditions met: - - * on a :term:`bytecode` boundary; - * with the main thread holding an :term:`attached thread state` - (*func* can therefore use the full C API). - - *func* must return ``0`` on success, or ``-1`` on failure with an exception - set. *func* won't be interrupted to perform another asynchronous - notification recursively, but it can still be interrupted to switch - threads if the :term:`thread state ` is detached. - - This function doesn't need an :term:`attached thread state`. However, to call this - function in a subinterpreter, the caller must have an :term:`attached thread state`. - Otherwise, the function *func* can be scheduled to be called from the wrong interpreter. - - .. warning:: - This is a low-level function, only useful for very special cases. - There is no guarantee that *func* will be called as quick as - possible. If the main thread is busy executing a system call, - *func* won't be called before the system call returns. This - function is generally **not** suitable for calling Python code from - arbitrary C threads. Instead, use the :ref:`PyGILState API`. - - .. versionadded:: 3.1 - - .. versionchanged:: 3.9 - If this function is called in a subinterpreter, the function *func* is - now scheduled to be called from the subinterpreter, rather than being - called from the main interpreter. Each subinterpreter now has its own - list of scheduled calls. - -.. _profiling: - -Profiling and Tracing -===================== - -.. sectionauthor:: Fred L. Drake, Jr. - - -The Python interpreter provides some low-level support for attaching profiling -and execution tracing facilities. These are used for profiling, debugging, and -coverage analysis tools. - -This C interface allows the profiling or tracing code to avoid the overhead of -calling through Python-level callable objects, making a direct C function call -instead. The essential attributes of the facility have not changed; the -interface allows trace functions to be installed per-thread, and the basic -events reported to the trace function are the same as had been reported to the -Python-level trace functions in previous versions. - - -.. c:type:: int (*Py_tracefunc)(PyObject *obj, PyFrameObject *frame, int what, PyObject *arg) - - The type of the trace function registered using :c:func:`PyEval_SetProfile` and - :c:func:`PyEval_SetTrace`. The first parameter is the object passed to the - registration function as *obj*, *frame* is the frame object to which the event - pertains, *what* is one of the constants :c:data:`PyTrace_CALL`, - :c:data:`PyTrace_EXCEPTION`, :c:data:`PyTrace_LINE`, :c:data:`PyTrace_RETURN`, - :c:data:`PyTrace_C_CALL`, :c:data:`PyTrace_C_EXCEPTION`, :c:data:`PyTrace_C_RETURN`, - or :c:data:`PyTrace_OPCODE`, and *arg* depends on the value of *what*: - - +-------------------------------+----------------------------------------+ - | Value of *what* | Meaning of *arg* | - +===============================+========================================+ - | :c:data:`PyTrace_CALL` | Always :c:data:`Py_None`. | - +-------------------------------+----------------------------------------+ - | :c:data:`PyTrace_EXCEPTION` | Exception information as returned by | - | | :func:`sys.exc_info`. | - +-------------------------------+----------------------------------------+ - | :c:data:`PyTrace_LINE` | Always :c:data:`Py_None`. | - +-------------------------------+----------------------------------------+ - | :c:data:`PyTrace_RETURN` | Value being returned to the caller, | - | | or ``NULL`` if caused by an exception. | - +-------------------------------+----------------------------------------+ - | :c:data:`PyTrace_C_CALL` | Function object being called. | - +-------------------------------+----------------------------------------+ - | :c:data:`PyTrace_C_EXCEPTION` | Function object being called. | - +-------------------------------+----------------------------------------+ - | :c:data:`PyTrace_C_RETURN` | Function object being called. | - +-------------------------------+----------------------------------------+ - | :c:data:`PyTrace_OPCODE` | Always :c:data:`Py_None`. | - +-------------------------------+----------------------------------------+ - -.. c:var:: int PyTrace_CALL - - The value of the *what* parameter to a :c:type:`Py_tracefunc` function when a new - call to a function or method is being reported, or a new entry into a generator. - Note that the creation of the iterator for a generator function is not reported - as there is no control transfer to the Python bytecode in the corresponding - frame. - - -.. c:var:: int PyTrace_EXCEPTION - - The value of the *what* parameter to a :c:type:`Py_tracefunc` function when an - exception has been raised. The callback function is called with this value for - *what* when after any bytecode is processed after which the exception becomes - set within the frame being executed. The effect of this is that as exception - propagation causes the Python stack to unwind, the callback is called upon - return to each frame as the exception propagates. Only trace functions receives - these events; they are not needed by the profiler. - - -.. c:var:: int PyTrace_LINE - - The value passed as the *what* parameter to a :c:type:`Py_tracefunc` function - (but not a profiling function) when a line-number event is being reported. - It may be disabled for a frame by setting :attr:`~frame.f_trace_lines` to - *0* on that frame. - - -.. c:var:: int PyTrace_RETURN - - The value for the *what* parameter to :c:type:`Py_tracefunc` functions when a - call is about to return. - - -.. c:var:: int PyTrace_C_CALL - - The value for the *what* parameter to :c:type:`Py_tracefunc` functions when a C - function is about to be called. - - -.. c:var:: int PyTrace_C_EXCEPTION - - The value for the *what* parameter to :c:type:`Py_tracefunc` functions when a C - function has raised an exception. - - -.. c:var:: int PyTrace_C_RETURN - - The value for the *what* parameter to :c:type:`Py_tracefunc` functions when a C - function has returned. - - -.. c:var:: int PyTrace_OPCODE - - The value for the *what* parameter to :c:type:`Py_tracefunc` functions (but not - profiling functions) when a new opcode is about to be executed. This event is - not emitted by default: it must be explicitly requested by setting - :attr:`~frame.f_trace_opcodes` to *1* on the frame. - - -.. c:function:: void PyEval_SetProfile(Py_tracefunc func, PyObject *obj) - - Set the profiler function to *func*. The *obj* parameter is passed to the - function as its first parameter, and may be any Python object, or ``NULL``. If - the profile function needs to maintain state, using a different value for *obj* - for each thread provides a convenient and thread-safe place to store it. The - profile function is called for all monitored events except :c:data:`PyTrace_LINE` - :c:data:`PyTrace_OPCODE` and :c:data:`PyTrace_EXCEPTION`. - - See also the :func:`sys.setprofile` function. - - The caller must have an :term:`attached thread state`. - -.. c:function:: void PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *obj) - - Like :c:func:`PyEval_SetProfile` but sets the profile function in all running threads - belonging to the current interpreter instead of the setting it only on the current thread. - - The caller must have an :term:`attached thread state`. - - As :c:func:`PyEval_SetProfile`, this function ignores any exceptions raised while - setting the profile functions in all threads. - -.. versionadded:: 3.12 - - -.. c:function:: void PyEval_SetTrace(Py_tracefunc func, PyObject *obj) - - Set the tracing function to *func*. This is similar to - :c:func:`PyEval_SetProfile`, except the tracing function does receive line-number - events and per-opcode events, but does not receive any event related to C function - objects being called. Any trace function registered using :c:func:`PyEval_SetTrace` - will not receive :c:data:`PyTrace_C_CALL`, :c:data:`PyTrace_C_EXCEPTION` or - :c:data:`PyTrace_C_RETURN` as a value for the *what* parameter. - - See also the :func:`sys.settrace` function. - - The caller must have an :term:`attached thread state`. - -.. c:function:: void PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *obj) - - Like :c:func:`PyEval_SetTrace` but sets the tracing function in all running threads - belonging to the current interpreter instead of the setting it only on the current thread. - - The caller must have an :term:`attached thread state`. - - As :c:func:`PyEval_SetTrace`, this function ignores any exceptions raised while - setting the trace functions in all threads. - -.. versionadded:: 3.12 - -Reference tracing -================= - -.. versionadded:: 3.13 - -.. c:type:: int (*PyRefTracer)(PyObject *, int event, void* data) - - The type of the trace function registered using :c:func:`PyRefTracer_SetTracer`. - The first parameter is a Python object that has been just created (when **event** - is set to :c:data:`PyRefTracer_CREATE`) or about to be destroyed (when **event** - is set to :c:data:`PyRefTracer_DESTROY`). The **data** argument is the opaque pointer - that was provided when :c:func:`PyRefTracer_SetTracer` was called. - -.. versionadded:: 3.13 - -.. c:var:: int PyRefTracer_CREATE - - The value for the *event* parameter to :c:type:`PyRefTracer` functions when a Python - object has been created. - -.. c:var:: int PyRefTracer_DESTROY - - The value for the *event* parameter to :c:type:`PyRefTracer` functions when a Python - object has been destroyed. - -.. c:function:: int PyRefTracer_SetTracer(PyRefTracer tracer, void *data) - - Register a reference tracer function. The function will be called when a new - Python has been created or when an object is going to be destroyed. If - **data** is provided it must be an opaque pointer that will be provided when - the tracer function is called. Return ``0`` on success. Set an exception and - return ``-1`` on error. - - Not that tracer functions **must not** create Python objects inside or - otherwise the call will be re-entrant. The tracer also **must not** clear - any existing exception or set an exception. A :term:`thread state` will be active - every time the tracer function is called. - - There must be an :term:`attached thread state` when calling this function. - -.. versionadded:: 3.13 - -.. c:function:: PyRefTracer PyRefTracer_GetTracer(void** data) - - Get the registered reference tracer function and the value of the opaque data - pointer that was registered when :c:func:`PyRefTracer_SetTracer` was called. - If no tracer was registered this function will return NULL and will set the - **data** pointer to NULL. - - There must be an :term:`attached thread state` when calling this function. - -.. versionadded:: 3.13 - -.. _advanced-debugging: - -Advanced Debugger Support -========================= - -.. sectionauthor:: Fred L. Drake, Jr. - - -These functions are only intended to be used by advanced debugging tools. - - -.. c:function:: PyInterpreterState* PyInterpreterState_Head() - - Return the interpreter state object at the head of the list of all such objects. - - -.. c:function:: PyInterpreterState* PyInterpreterState_Main() - - Return the main interpreter state object. - - -.. c:function:: PyInterpreterState* PyInterpreterState_Next(PyInterpreterState *interp) - - Return the next interpreter state object after *interp* from the list of all - such objects. - - -.. c:function:: PyThreadState * PyInterpreterState_ThreadHead(PyInterpreterState *interp) - - Return the pointer to the first :c:type:`PyThreadState` object in the list of - threads associated with the interpreter *interp*. - - -.. c:function:: PyThreadState* PyThreadState_Next(PyThreadState *tstate) - - Return the next thread state object after *tstate* from the list of all such - objects belonging to the same :c:type:`PyInterpreterState` object. - - -.. _thread-local-storage: - -Thread Local Storage Support -============================ - -.. sectionauthor:: Masayuki Yamamoto - -The Python interpreter provides low-level support for thread-local storage -(TLS) which wraps the underlying native TLS implementation to support the -Python-level thread local storage API (:class:`threading.local`). The -CPython C level APIs are similar to those offered by pthreads and Windows: -use a thread key and functions to associate a :c:expr:`void*` value per -thread. - -A :term:`thread state` does *not* need to be :term:`attached ` -when calling these functions; they suppl their own locking. - -Note that :file:`Python.h` does not include the declaration of the TLS APIs, -you need to include :file:`pythread.h` to use thread-local storage. - -.. note:: - None of these API functions handle memory management on behalf of the - :c:expr:`void*` values. You need to allocate and deallocate them yourself. - If the :c:expr:`void*` values happen to be :c:expr:`PyObject*`, these - functions don't do refcount operations on them either. - -.. _thread-specific-storage-api: - -Thread Specific Storage (TSS) API ---------------------------------- - -TSS API is introduced to supersede the use of the existing TLS API within the -CPython interpreter. This API uses a new type :c:type:`Py_tss_t` instead of -:c:expr:`int` to represent thread keys. - -.. versionadded:: 3.7 - -.. seealso:: "A New C-API for Thread-Local Storage in CPython" (:pep:`539`) - - -.. c:type:: Py_tss_t - - This data structure represents the state of a thread key, the definition of - which may depend on the underlying TLS implementation, and it has an - internal field representing the key's initialization state. There are no - public members in this structure. - - When :ref:`Py_LIMITED_API ` is not defined, static allocation of - this type by :c:macro:`Py_tss_NEEDS_INIT` is allowed. - - -.. c:macro:: Py_tss_NEEDS_INIT - - This macro expands to the initializer for :c:type:`Py_tss_t` variables. - Note that this macro won't be defined with :ref:`Py_LIMITED_API `. - - -Dynamic Allocation -~~~~~~~~~~~~~~~~~~ - -Dynamic allocation of the :c:type:`Py_tss_t`, required in extension modules -built with :ref:`Py_LIMITED_API `, where static allocation of this type -is not possible due to its implementation being opaque at build time. - - -.. c:function:: Py_tss_t* PyThread_tss_alloc() - - Return a value which is the same state as a value initialized with - :c:macro:`Py_tss_NEEDS_INIT`, or ``NULL`` in the case of dynamic allocation - failure. - - -.. c:function:: void PyThread_tss_free(Py_tss_t *key) - - Free the given *key* allocated by :c:func:`PyThread_tss_alloc`, after - first calling :c:func:`PyThread_tss_delete` to ensure any associated - thread locals have been unassigned. This is a no-op if the *key* - argument is ``NULL``. - - .. note:: - A freed key becomes a dangling pointer. You should reset the key to - ``NULL``. - - -Methods -~~~~~~~ - -The parameter *key* of these functions must not be ``NULL``. Moreover, the -behaviors of :c:func:`PyThread_tss_set` and :c:func:`PyThread_tss_get` are -undefined if the given :c:type:`Py_tss_t` has not been initialized by -:c:func:`PyThread_tss_create`. - - -.. c:function:: int PyThread_tss_is_created(Py_tss_t *key) - - Return a non-zero value if the given :c:type:`Py_tss_t` has been initialized - by :c:func:`PyThread_tss_create`. - - -.. c:function:: int PyThread_tss_create(Py_tss_t *key) - - Return a zero value on successful initialization of a TSS key. The behavior - is undefined if the value pointed to by the *key* argument is not - initialized by :c:macro:`Py_tss_NEEDS_INIT`. This function can be called - repeatedly on the same key -- calling it on an already initialized key is a - no-op and immediately returns success. - - -.. c:function:: void PyThread_tss_delete(Py_tss_t *key) - - Destroy a TSS key to forget the values associated with the key across all - threads, and change the key's initialization state to uninitialized. A - destroyed key is able to be initialized again by - :c:func:`PyThread_tss_create`. This function can be called repeatedly on - the same key -- calling it on an already destroyed key is a no-op. - - -.. c:function:: int PyThread_tss_set(Py_tss_t *key, void *value) - - Return a zero value to indicate successfully associating a :c:expr:`void*` - value with a TSS key in the current thread. Each thread has a distinct - mapping of the key to a :c:expr:`void*` value. - - -.. c:function:: void* PyThread_tss_get(Py_tss_t *key) - - Return the :c:expr:`void*` value associated with a TSS key in the current - thread. This returns ``NULL`` if no value is associated with the key in the - current thread. - - -.. _thread-local-storage-api: - -Thread Local Storage (TLS) API ------------------------------- - -.. deprecated:: 3.7 - This API is superseded by - :ref:`Thread Specific Storage (TSS) API `. - -.. note:: - This version of the API does not support platforms where the native TLS key - is defined in a way that cannot be safely cast to ``int``. On such platforms, - :c:func:`PyThread_create_key` will return immediately with a failure status, - and the other TLS functions will all be no-ops on such platforms. - -Due to the compatibility problem noted above, this version of the API should not -be used in new code. - -.. c:function:: int PyThread_create_key() -.. c:function:: void PyThread_delete_key(int key) -.. c:function:: int PyThread_set_key_value(int key, void *value) -.. c:function:: void* PyThread_get_key_value(int key) -.. c:function:: void PyThread_delete_key_value(int key) -.. c:function:: void PyThread_ReInitTLS() - -Synchronization Primitives -========================== - -The C-API provides a basic mutual exclusion lock. - -.. c:type:: PyMutex - - A mutual exclusion lock. The :c:type:`!PyMutex` should be initialized to - zero to represent the unlocked state. For example:: - - PyMutex mutex = {0}; - - Instances of :c:type:`!PyMutex` should not be copied or moved. Both the - contents and address of a :c:type:`!PyMutex` are meaningful, and it must - remain at a fixed, writable location in memory. - - .. note:: - - A :c:type:`!PyMutex` currently occupies one byte, but the size should be - considered unstable. The size may change in future Python releases - without a deprecation period. - - .. versionadded:: 3.13 - -.. c:function:: void PyMutex_Lock(PyMutex *m) - - Lock mutex *m*. If another thread has already locked it, the calling - thread will block until the mutex is unlocked. While blocked, the thread - will temporarily detach the :term:`thread state ` if one exists. - - .. versionadded:: 3.13 - -.. c:function:: void PyMutex_Unlock(PyMutex *m) - - Unlock mutex *m*. The mutex must be locked --- otherwise, the function will - issue a fatal error. - - .. versionadded:: 3.13 - -.. c:function:: int PyMutex_IsLocked(PyMutex *m) - - Returns non-zero if the mutex *m* is currently locked, zero otherwise. - - .. note:: - - This function is intended for use in assertions and debugging only and - should not be used to make concurrency control decisions, as the lock - state may change immediately after the check. - - .. versionadded:: 3.14 - -.. _python-critical-section-api: - -Python Critical Section API ---------------------------- - -The critical section API provides a deadlock avoidance layer on top of -per-object locks for :term:`free-threaded ` CPython. They are -intended to replace reliance on the :term:`global interpreter lock`, and are -no-ops in versions of Python with the global interpreter lock. - -Critical sections avoid deadlocks by implicitly suspending active critical -sections and releasing the locks during calls to :c:func:`PyEval_SaveThread`. -When :c:func:`PyEval_RestoreThread` is called, the most recent critical section -is resumed, and its locks reacquired. This means the critical section API -provides weaker guarantees than traditional locks -- they are useful because -their behavior is similar to the :term:`GIL`. - -Variants that accept :c:type:`PyMutex` pointers rather than Python objects are also -available. Use these variants to start a critical section in a situation where -there is no :c:type:`PyObject` -- for example, when working with a C type that -does not extend or wrap :c:type:`PyObject` but still needs to call into the C -API in a manner that might lead to deadlocks. - -The functions and structs used by the macros are exposed for cases -where C macros are not available. They should only be used as in the -given macro expansions. Note that the sizes and contents of the structures may -change in future Python versions. - -.. note:: - - Operations that need to lock two objects at once must use - :c:macro:`Py_BEGIN_CRITICAL_SECTION2`. You *cannot* use nested critical - sections to lock more than one object at once, because the inner critical - section may suspend the outer critical sections. This API does not provide - a way to lock more than two objects at once. - -Example usage:: - - static PyObject * - set_field(MyObject *self, PyObject *value) - { - Py_BEGIN_CRITICAL_SECTION(self); - Py_SETREF(self->field, Py_XNewRef(value)); - Py_END_CRITICAL_SECTION(); - Py_RETURN_NONE; - } - -In the above example, :c:macro:`Py_SETREF` calls :c:macro:`Py_DECREF`, which -can call arbitrary code through an object's deallocation function. The critical -section API avoids potential deadlocks due to reentrancy and lock ordering -by allowing the runtime to temporarily suspend the critical section if the -code triggered by the finalizer blocks and calls :c:func:`PyEval_SaveThread`. - -.. c:macro:: Py_BEGIN_CRITICAL_SECTION(op) - - Acquires the per-object lock for the object *op* and begins a - critical section. - - In the free-threaded build, this macro expands to:: - - { - PyCriticalSection _py_cs; - PyCriticalSection_Begin(&_py_cs, (PyObject*)(op)) - - In the default build, this macro expands to ``{``. - - .. versionadded:: 3.13 - -.. c:macro:: Py_BEGIN_CRITICAL_SECTION_MUTEX(m) - - Locks the mutex *m* and begins a critical section. - - In the free-threaded build, this macro expands to:: - - { - PyCriticalSection _py_cs; - PyCriticalSection_BeginMutex(&_py_cs, m) - - Note that unlike :c:macro:`Py_BEGIN_CRITICAL_SECTION`, there is no cast for - the argument of the macro - it must be a :c:type:`PyMutex` pointer. - - On the default build, this macro expands to ``{``. - - .. versionadded:: 3.14 - -.. c:macro:: Py_END_CRITICAL_SECTION() - - Ends the critical section and releases the per-object lock. - - In the free-threaded build, this macro expands to:: - - PyCriticalSection_End(&_py_cs); - } - - In the default build, this macro expands to ``}``. - - .. versionadded:: 3.13 - -.. c:macro:: Py_BEGIN_CRITICAL_SECTION2(a, b) - - Acquires the per-objects locks for the objects *a* and *b* and begins a - critical section. The locks are acquired in a consistent order (lowest - address first) to avoid lock ordering deadlocks. - - In the free-threaded build, this macro expands to:: - - { - PyCriticalSection2 _py_cs2; - PyCriticalSection2_Begin(&_py_cs2, (PyObject*)(a), (PyObject*)(b)) - - In the default build, this macro expands to ``{``. - - .. versionadded:: 3.13 - -.. c:macro:: Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2) - - Locks the mutexes *m1* and *m2* and begins a critical section. - - In the free-threaded build, this macro expands to:: - - { - PyCriticalSection2 _py_cs2; - PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2) - - Note that unlike :c:macro:`Py_BEGIN_CRITICAL_SECTION2`, there is no cast for - the arguments of the macro - they must be :c:type:`PyMutex` pointers. - - On the default build, this macro expands to ``{``. - - .. versionadded:: 3.14 - -.. c:macro:: Py_END_CRITICAL_SECTION2() - - Ends the critical section and releases the per-object locks. - - In the free-threaded build, this macro expands to:: - - PyCriticalSection2_End(&_py_cs2); - } - - In the default build, this macro expands to ``}``. - - .. versionadded:: 3.13 +- :ref:`initialization` +- :ref:`threads` +- :ref:`synchronization` +- :ref:`thread-local-storage` +- :ref:`sub-interpreter-support` +- :ref:`profiling` diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 24be9ead3874d1..209e48767ccfd6 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -102,7 +102,7 @@ Error Handling * Set *\*err_msg* and return ``1`` if an error is set. * Set *\*err_msg* to ``NULL`` and return ``0`` otherwise. - An error message is an UTF-8 encoded string. + An error message is a UTF-8 encoded string. If *config* has an exit code, format the exit code as an error message. @@ -544,9 +544,9 @@ Configuration Options Visibility: -* Public: Can by get by :c:func:`PyConfig_Get` and set by +* Public: Can be retrieved by :c:func:`PyConfig_Get` and set by :c:func:`PyConfig_Set`. -* Read-only: Can by get by :c:func:`PyConfig_Get`, but cannot be set by +* Read-only: Can be retrieved by :c:func:`PyConfig_Get`, but cannot be set by :c:func:`PyConfig_Set`. @@ -1153,7 +1153,7 @@ PyConfig Most ``PyConfig`` methods :ref:`preinitialize Python ` if needed. In that case, the Python preinitialization configuration - (:c:type:`PyPreConfig`) in based on the :c:type:`PyConfig`. If configuration + (:c:type:`PyPreConfig`) is based on the :c:type:`PyConfig`. If configuration fields which are in common with :c:type:`PyPreConfig` are tuned, they must be set before calling a :c:type:`PyConfig` method: @@ -1278,6 +1278,11 @@ PyConfig Default: ``0``. + .. deprecated-removed:: 3.15 3.17 + + The :option:`-b` and :option:`!-bb` options will become no-op in 3.17. + :c:member:`~PyConfig.bytes_warning` member will be removed in 3.17. + .. c:member:: int warn_default_encoding If non-zero, emit a :exc:`EncodingWarning` warning when :class:`io.TextIOWrapper` @@ -1802,10 +1807,10 @@ PyConfig .. c:member:: wchar_t* run_presite - ``package.module`` path to module that should be imported before - ``site.py`` is run. + ``module`` or ``module:func`` entry point that should be executed before + the :mod:`site` module is imported. - Set by the :option:`-X presite=package.module <-X>` command-line + Set by the :option:`-X presite=module:func <-X>` command-line option and the :envvar:`PYTHON_PRESITE` environment variable. The command-line option takes precedence. @@ -2258,6 +2263,7 @@ If a ``._pth`` file is present: * Set :c:member:`~PyConfig.isolated` to ``1``. * Set :c:member:`~PyConfig.use_environment` to ``0``. * Set :c:member:`~PyConfig.site_import` to ``0``. +* Set :c:member:`~PyConfig.user_site_directory` to ``0`` (since 3.15). * Set :c:member:`~PyConfig.safe_path` to ``1``. If :c:member:`~PyConfig.home` is not set and a ``pyvenv.cfg`` file is present in @@ -2278,6 +2284,12 @@ The ``__PYVENV_LAUNCHER__`` environment variable is used to set therefore affected by :option:`-S`. +.. versionchanged:: 3.15 + + :c:member:`~PyConfig.user_site_directory` is now set to ``0`` when a + ``._pth`` file is present. + + Py_GetArgcArgv() ================ @@ -2287,13 +2299,91 @@ Py_GetArgcArgv() See also :c:member:`PyConfig.orig_argv` member. -Delaying main module execution -============================== -In some embedding use cases, it may be desirable to separate interpreter initialization -from the execution of the main module. +Multi-Phase Initialization Private Provisional API +================================================== + +This section is a private provisional API introducing multi-phase +initialization, the core feature of :pep:`432`: + +* "Core" initialization phase, "bare minimum Python": + + * Builtin types; + * Builtin exceptions; + * Builtin and frozen modules; + * The :mod:`sys` module is only partially initialized + (ex: :data:`sys.path` doesn't exist yet). + +* "Main" initialization phase, Python is fully initialized: + + * Install and configure :mod:`importlib`; + * Apply the :ref:`Path Configuration `; + * Install signal handlers; + * Finish :mod:`sys` module initialization (ex: create :data:`sys.stdout` + and :data:`sys.path`); + * Enable optional features like :mod:`faulthandler` and :mod:`tracemalloc`; + * Import the :mod:`site` module; + * etc. + +Private provisional API: + +.. c:member:: int PyConfig._init_main + + If set to ``0``, :c:func:`Py_InitializeFromConfig` stops at the "Core" + initialization phase. + +.. c:function:: PyStatus _Py_InitializeMain(void) + + Move to the "Main" initialization phase, finish the Python initialization. + +No module is imported during the "Core" phase and the ``importlib`` module is +not configured: the :ref:`Path Configuration ` is only +applied during the "Main" phase. It may allow to customize Python in Python to +override or tune the :ref:`Path Configuration `, maybe +install a custom :data:`sys.meta_path` importer or an import hook, etc. + +It may become possible to calculate the :ref:`Path Configuration +` in Python, after the Core phase and before the Main phase, +which is one of the :pep:`432` motivation. + +The "Core" phase is not properly defined: what should be and what should +not be available at this phase is not specified yet. The API is marked +as private and provisional: the API can be modified or even be removed +anytime until a proper public API is designed. + +Example running Python code between "Core" and "Main" initialization +phases:: + + void init_python(void) + { + PyStatus status; + + PyConfig config; + PyConfig_InitPythonConfig(&config); + config._init_main = 0; + + /* ... customize 'config' configuration ... */ + + status = Py_InitializeFromConfig(&config); + PyConfig_Clear(&config); + if (PyStatus_Exception(status)) { + Py_ExitStatusException(status); + } + + /* Use sys.stderr because sys.stdout is only created + by _Py_InitializeMain() */ + int res = PyRun_SimpleString( + "import sys; " + "print('Run Python code before _Py_InitializeMain', " + "file=sys.stderr)"); + if (res < 0) { + exit(1); + } -This separation can be achieved by setting ``PyConfig.run_command`` to the empty -string during initialization (to prevent the interpreter from dropping into the -interactive prompt), and then subsequently executing the desired main module -code using ``__main__.__dict__`` as the global namespace. + /* ... put more configuration code here ... */ + + status = _Py_InitializeMain(); + if (PyStatus_Exception(status)) { + Py_ExitStatusException(status); + } + } diff --git a/Doc/c-api/interp-lifecycle.rst b/Doc/c-api/interp-lifecycle.rst new file mode 100644 index 00000000000000..46f5b1dd33963c --- /dev/null +++ b/Doc/c-api/interp-lifecycle.rst @@ -0,0 +1,974 @@ +.. highlight:: c + +.. _initialization: + +Interpreter initialization and finalization +=========================================== + +See :ref:`Python Initialization Configuration ` for details +on how to configure the interpreter prior to initialization. + +.. _pre-init-safe: + +Before Python initialization +---------------------------- + +In an application embedding Python, the :c:func:`Py_Initialize` function must +be called before using any other Python/C API functions; with the exception of +a few functions and the :ref:`global configuration variables +`. + +The following functions can be safely called before Python is initialized: + +* Functions that initialize the interpreter: + + * :c:func:`Py_Initialize` + * :c:func:`Py_InitializeEx` + * :c:func:`Py_InitializeFromConfig` + * :c:func:`Py_BytesMain` + * :c:func:`Py_Main` + * the runtime pre-initialization functions covered in :ref:`init-config` + +* Configuration functions: + + * :c:func:`PyImport_AppendInittab` + * :c:func:`PyImport_ExtendInittab` + * :c:func:`!PyInitFrozenExtensions` + * :c:func:`PyMem_SetAllocator` + * :c:func:`PyMem_SetupDebugHooks` + * :c:func:`PyObject_SetArenaAllocator` + * :c:func:`Py_SetProgramName` + * :c:func:`Py_SetPythonHome` + * the configuration functions covered in :ref:`init-config` + +* Informative functions: + + * :c:func:`Py_IsInitialized` + * :c:func:`PyMem_GetAllocator` + * :c:func:`PyObject_GetArenaAllocator` + * :c:func:`Py_GetBuildInfo` + * :c:func:`Py_GetCompiler` + * :c:func:`Py_GetCopyright` + * :c:func:`Py_GetPlatform` + * :c:func:`Py_GetVersion` + * :c:func:`Py_IsInitialized` + +* Utilities: + + * :c:func:`Py_DecodeLocale` + * the status reporting and utility functions covered in :ref:`init-config` + +* Memory allocators: + + * :c:func:`PyMem_RawMalloc` + * :c:func:`PyMem_RawRealloc` + * :c:func:`PyMem_RawCalloc` + * :c:func:`PyMem_RawFree` + +* Synchronization: + + * :c:func:`PyMutex_Lock` + * :c:func:`PyMutex_Unlock` + +.. note:: + + Despite their apparent similarity to some of the functions listed above, + the following functions **should not be called** before the interpreter has + been initialized: :c:func:`Py_EncodeLocale`, :c:func:`PyEval_InitThreads`, and + :c:func:`Py_RunMain`. + + +.. _global-conf-vars: + +Global configuration variables +------------------------------ + +Python has variables for the global configuration to control different features +and options. By default, these flags are controlled by :ref:`command line +options `. + +When a flag is set by an option, the value of the flag is the number of times +that the option was set. For example, ``-b`` sets :c:data:`Py_BytesWarningFlag` +to 1 and ``-bb`` sets :c:data:`Py_BytesWarningFlag` to 2. + + +.. c:var:: int Py_BytesWarningFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.bytes_warning` should be used instead, see :ref:`Python + Initialization Configuration `. + + Issue a warning when comparing :class:`bytes` or :class:`bytearray` with + :class:`str` or :class:`bytes` with :class:`int`. Issue an error if greater + or equal to ``2``. + + Set by the :option:`-b` option. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_DebugFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.parser_debug` should be used instead, see :ref:`Python + Initialization Configuration `. + + Turn on parser debugging output (for expert only, depending on compilation + options). + + Set by the :option:`-d` option and the :envvar:`PYTHONDEBUG` environment + variable. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_DontWriteBytecodeFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.write_bytecode` should be used instead, see :ref:`Python + Initialization Configuration `. + + If set to non-zero, Python won't try to write ``.pyc`` files on the + import of source modules. + + Set by the :option:`-B` option and the :envvar:`PYTHONDONTWRITEBYTECODE` + environment variable. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_FrozenFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.pathconfig_warnings` should be used instead, see + :ref:`Python Initialization Configuration `. + + Private flag used by ``_freeze_module`` and ``frozenmain`` programs. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_HashRandomizationFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.hash_seed` and :c:member:`PyConfig.use_hash_seed` should + be used instead, see :ref:`Python Initialization Configuration + `. + + Set to ``1`` if the :envvar:`PYTHONHASHSEED` environment variable is set to + a non-empty string. + + If the flag is non-zero, read the :envvar:`PYTHONHASHSEED` environment + variable to initialize the secret hash seed. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_IgnoreEnvironmentFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.use_environment` should be used instead, see + :ref:`Python Initialization Configuration `. + + Ignore all :envvar:`!PYTHON*` environment variables, e.g. + :envvar:`PYTHONPATH` and :envvar:`PYTHONHOME`, that might be set. + + Set by the :option:`-E` and :option:`-I` options. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_InspectFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.inspect` should be used instead, see + :ref:`Python Initialization Configuration `. + + When a script is passed as first argument or the :option:`-c` option is used, + enter interactive mode after executing the script or the command, even when + :data:`sys.stdin` does not appear to be a terminal. + + Set by the :option:`-i` option and the :envvar:`PYTHONINSPECT` environment + variable. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_InteractiveFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.interactive` should be used instead, see + :ref:`Python Initialization Configuration `. + + Set by the :option:`-i` option. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_IsolatedFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.isolated` should be used instead, see + :ref:`Python Initialization Configuration `. + + Run Python in isolated mode. In isolated mode :data:`sys.path` contains + neither the script's directory nor the user's site-packages directory. + + Set by the :option:`-I` option. + + .. versionadded:: 3.4 + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_LegacyWindowsFSEncodingFlag + + This API is kept for backward compatibility: setting + :c:member:`PyPreConfig.legacy_windows_fs_encoding` should be used instead, see + :ref:`Python Initialization Configuration `. + + If the flag is non-zero, use the ``mbcs`` encoding with ``replace`` error + handler, instead of the UTF-8 encoding with ``surrogatepass`` error handler, + for the :term:`filesystem encoding and error handler`. + + Set to ``1`` if the :envvar:`PYTHONLEGACYWINDOWSFSENCODING` environment + variable is set to a non-empty string. + + See :pep:`529` for more details. + + .. availability:: Windows. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_LegacyWindowsStdioFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.legacy_windows_stdio` should be used instead, see + :ref:`Python Initialization Configuration `. + + If the flag is non-zero, use :class:`io.FileIO` instead of + :class:`!io._WindowsConsoleIO` for :mod:`sys` standard streams. + + Set to ``1`` if the :envvar:`PYTHONLEGACYWINDOWSSTDIO` environment + variable is set to a non-empty string. + + See :pep:`528` for more details. + + .. availability:: Windows. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_NoSiteFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.site_import` should be used instead, see + :ref:`Python Initialization Configuration `. + + Disable the import of the module :mod:`site` and the site-dependent + manipulations of :data:`sys.path` that it entails. Also disable these + manipulations if :mod:`site` is explicitly imported later (call + :func:`site.main` if you want them to be triggered). + + Set by the :option:`-S` option. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_NoUserSiteDirectory + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.user_site_directory` should be used instead, see + :ref:`Python Initialization Configuration `. + + Don't add the :data:`user site-packages directory ` to + :data:`sys.path`. + + Set by the :option:`-s` and :option:`-I` options, and the + :envvar:`PYTHONNOUSERSITE` environment variable. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_OptimizeFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.optimization_level` should be used instead, see + :ref:`Python Initialization Configuration `. + + Set by the :option:`-O` option and the :envvar:`PYTHONOPTIMIZE` environment + variable. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_QuietFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.quiet` should be used instead, see :ref:`Python + Initialization Configuration `. + + Don't display the copyright and version messages even in interactive mode. + + Set by the :option:`-q` option. + + .. versionadded:: 3.2 + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_UnbufferedStdioFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.buffered_stdio` should be used instead, see :ref:`Python + Initialization Configuration `. + + Force the stdout and stderr streams to be unbuffered. + + Set by the :option:`-u` option and the :envvar:`PYTHONUNBUFFERED` + environment variable. + + .. deprecated-removed:: 3.12 3.16 + + +.. c:var:: int Py_VerboseFlag + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.verbose` should be used instead, see :ref:`Python + Initialization Configuration `. + + Print a message each time a module is initialized, showing the place + (filename or built-in module) from which it is loaded. If greater or equal + to ``2``, print a message for each file that is checked for when + searching for a module. Also provides information on module cleanup at exit. + + Set by the :option:`-v` option and the :envvar:`PYTHONVERBOSE` environment + variable. + + .. deprecated-removed:: 3.12 3.16 + + +Initializing and finalizing the interpreter +------------------------------------------- + +.. c:function:: void Py_Initialize() + + .. index:: + single: PyEval_InitThreads() + single: modules (in module sys) + single: path (in module sys) + pair: module; builtins + pair: module; __main__ + pair: module; sys + triple: module; search; path + single: Py_FinalizeEx (C function) + + Initialize the Python interpreter. In an application embedding Python, + this should be called before using any other Python/C API functions; see + :ref:`Before Python Initialization ` for the few exceptions. + + This initializes the table of loaded modules (``sys.modules``), and creates + the fundamental modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`. + It also initializes the module search path (``sys.path``). It does not set + ``sys.argv``; use the :ref:`Python Initialization Configuration ` + API for that. This is a no-op when called for a second time (without calling + :c:func:`Py_FinalizeEx` first). There is no return value; it is a fatal + error if the initialization fails. + + Use :c:func:`Py_InitializeFromConfig` to customize the + :ref:`Python Initialization Configuration `. + + .. note:: + On Windows, changes the console mode from ``O_TEXT`` to ``O_BINARY``, + which will also affect non-Python uses of the console using the C Runtime. + + +.. c:function:: void Py_InitializeEx(int initsigs) + + This function works like :c:func:`Py_Initialize` if *initsigs* is ``1``. If + *initsigs* is ``0``, it skips initialization registration of signal handlers, + which may be useful when CPython is embedded as part of a larger application. + + Use :c:func:`Py_InitializeFromConfig` to customize the + :ref:`Python Initialization Configuration `. + + +.. c:function:: PyStatus Py_InitializeFromConfig(const PyConfig *config) + + Initialize Python from *config* configuration, as described in + :ref:`init-from-config`. + + See the :ref:`init-config` section for details on pre-initializing the + interpreter, populating the runtime configuration structure, and querying + the returned status structure. + + +.. c:function:: int Py_IsInitialized() + + Return true (nonzero) when the Python interpreter has been initialized, false + (zero) if not. After :c:func:`Py_FinalizeEx` is called, this returns false until + :c:func:`Py_Initialize` is called again. + + .. versionchanged:: 3.15 + This function no longer returns true until initialization has fully + completed, including import of the :mod:`site` module. Previously it + could return true while :c:func:`Py_Initialize` was still running. + + +.. c:function:: int Py_IsFinalizing() + + Return true (non-zero) if the main Python interpreter is + :term:`shutting down `. Return false (zero) otherwise. + + .. versionadded:: 3.13 + + +.. c:function:: int Py_FinalizeEx() + + Undo all initializations made by :c:func:`Py_Initialize` and subsequent use of + Python/C API functions, and destroy all sub-interpreters (see + :c:func:`Py_NewInterpreter` below) that were created and not yet destroyed since + the last call to :c:func:`Py_Initialize`. This is a no-op when called for a second + time (without calling :c:func:`Py_Initialize` again first). + + Since this is the reverse of :c:func:`Py_Initialize`, it should be called + in the same thread with the same interpreter active. That means + the main thread and the main interpreter. + This should never be called while :c:func:`Py_RunMain` is running. + + Normally the return value is ``0``. + If there were errors during finalization (flushing buffered data), + ``-1`` is returned. + + Note that Python will do a best effort at freeing all memory allocated by the Python + interpreter. Therefore, any C-Extension should make sure to correctly clean up all + of the previously allocated PyObjects before using them in subsequent calls to + :c:func:`Py_Initialize`. Otherwise it could introduce vulnerabilities and incorrect + behavior. + + This function is provided for a number of reasons. An embedding application + might want to restart Python without having to restart the application itself. + An application that has loaded the Python interpreter from a dynamically + loadable library (or DLL) might want to free all memory allocated by Python + before unloading the DLL. During a hunt for memory leaks in an application a + developer might want to free all memory allocated by Python before exiting from + the application. + + **Bugs and caveats:** The destruction of modules and objects in modules is done + in random order; this may cause destructors (:meth:`~object.__del__` methods) to fail + when they depend on other objects (even functions) or modules. Dynamically + loaded extension modules loaded by Python are not unloaded. Small amounts of + memory allocated by the Python interpreter may not be freed (if you find a leak, + please report it). Memory tied up in circular references between objects is not + freed. Interned strings will all be deallocated regardless of their reference count. + Some memory allocated by extension modules may not be freed. Some extensions may not + work properly if their initialization routine is called more than once; this can + happen if an application calls :c:func:`Py_Initialize` and :c:func:`Py_FinalizeEx` + more than once. :c:func:`Py_FinalizeEx` must not be called recursively from + within itself. Therefore, it must not be called by any code that may be run + as part of the interpreter shutdown process, such as :py:mod:`atexit` + handlers, object finalizers, or any code that may be run while flushing the + stdout and stderr files. + + .. audit-event:: cpython._PySys_ClearAuditHooks "" c.Py_FinalizeEx + + .. versionadded:: 3.6 + + +.. c:function:: void Py_Finalize() + + This is a backwards-compatible version of :c:func:`Py_FinalizeEx` that + disregards the return value. + + +.. c:function:: int Py_BytesMain(int argc, char **argv) + + Similar to :c:func:`Py_Main` but *argv* is an array of bytes strings, + allowing the calling application to delegate the text decoding step to + the CPython runtime. + + .. versionadded:: 3.8 + + +.. c:function:: int Py_Main(int argc, wchar_t **argv) + + The main program for the standard interpreter, encapsulating a full + initialization/finalization cycle, as well as additional + behaviour to implement reading configurations settings from the environment + and command line, and then executing ``__main__`` in accordance with + :ref:`using-on-cmdline`. + + This is made available for programs which wish to support the full CPython + command line interface, rather than just embedding a Python runtime in a + larger application. + + The *argc* and *argv* parameters are similar to those which are passed to a + C program's :c:func:`main` function, except that the *argv* entries are first + converted to ``wchar_t`` using :c:func:`Py_DecodeLocale`. It is also + important to note that the argument list entries may be modified to point to + strings other than those passed in (however, the contents of the strings + pointed to by the argument list are not modified). + + The return value is ``2`` if the argument list does not represent a valid + Python command line, and otherwise the same as :c:func:`Py_RunMain`. + + In terms of the CPython runtime configuration APIs documented in the + :ref:`runtime configuration ` section (and without accounting + for error handling), ``Py_Main`` is approximately equivalent to:: + + PyConfig config; + PyConfig_InitPythonConfig(&config); + PyConfig_SetArgv(&config, argc, argv); + Py_InitializeFromConfig(&config); + PyConfig_Clear(&config); + + Py_RunMain(); + + In normal usage, an embedding application will call this function + *instead* of calling :c:func:`Py_Initialize`, :c:func:`Py_InitializeEx` or + :c:func:`Py_InitializeFromConfig` directly, and all settings will be applied + as described elsewhere in this documentation. If this function is instead + called *after* a preceding runtime initialization API call, then exactly + which environmental and command line configuration settings will be updated + is version dependent (as it depends on which settings correctly support + being modified after they have already been set once when the runtime was + first initialized). + + +.. c:function:: int Py_RunMain(void) + + Executes the main module in a fully configured CPython runtime. + + Executes the command (:c:member:`PyConfig.run_command`), the script + (:c:member:`PyConfig.run_filename`) or the module + (:c:member:`PyConfig.run_module`) specified on the command line or in the + configuration. If none of these values are set, runs the interactive Python + prompt (REPL) using the ``__main__`` module's global namespace. + + If :c:member:`PyConfig.inspect` is not set (the default), the return value + will be ``0`` if the interpreter exits normally (that is, without raising + an exception), the exit status of an unhandled :exc:`SystemExit`, or ``1`` + for any other unhandled exception. + + If :c:member:`PyConfig.inspect` is set (such as when the :option:`-i` option + is used), rather than returning when the interpreter exits, execution will + instead resume in an interactive Python prompt (REPL) using the ``__main__`` + module's global namespace. If the interpreter exited with an exception, it + is immediately raised in the REPL session. The function return value is + then determined by the way the *REPL session* terminates: ``0``, ``1``, or + the status of a :exc:`SystemExit`, as specified above. + + This function always finalizes the Python interpreter before it returns. + + See :ref:`Python Configuration ` for an example of a + customized Python that always runs in isolated mode using + :c:func:`Py_RunMain`. + +.. c:function:: int PyUnstable_AtExit(PyInterpreterState *interp, void (*func)(void *), void *data) + + Register an :mod:`atexit` callback for the target interpreter *interp*. + This is similar to :c:func:`Py_AtExit`, but takes an explicit interpreter and + data pointer for the callback. + + There must be an :term:`attached thread state` for *interp*. + + .. versionadded:: 3.13 + + +.. _cautions-regarding-runtime-finalization: + +Cautions regarding interpreter finalization +------------------------------------------- + +In the late stage of :term:`interpreter shutdown`, after attempting to wait for +non-daemon threads to exit (though this can be interrupted by +:class:`KeyboardInterrupt`) and running the :mod:`atexit` functions, the runtime +is marked as finalizing, meaning that :c:func:`Py_IsFinalizing` and +:func:`sys.is_finalizing` return true. At this point, only the finalization +thread (the thread that initiated finalization; this is typically the main thread) +is allowed to :term:`attach ` a thread state. + +Other threads that attempt to attach during finalization, either explicitly +(such as via :c:func:`PyThreadState_Ensure` or :c:macro:`Py_END_ALLOW_THREADS`) +or implicitly (such as in-between bytecode instructions), will enter a +**permanently blocked state**. Generally, this is harmless, but this can +result in deadlocks. For example, a thread may be permanently blocked while +holding a lock, meaning that the finalization thread can never acquire that +lock. + +Prior to CPython 3.13, the thread would exit instead of hanging, +which led to other issues (see the warning note at +:c:func:`PyThread_exit_thread`). + +Gross? Yes. Starting in Python 3.15, there are a number of C APIs that make +it possible to avoid these issues by temporarily preventing finalization: + +.. _interpreter-guards: + +.. seealso:: + + :pep:`788` explains the design, motivation and rationale + for these APIs. + +.. c:type:: PyInterpreterGuard + + An opaque interpreter guard structure. + + By holding an interpreter guard, the caller can ensure that the interpreter + will not finalize until the guard is closed (through + :c:func:`PyInterpreterGuard_Close`). + + When a guard is held, a thread attempting to finalize the interpreter will + block until the guard is closed before starting finalization. + After finalization has started, threads are forever unable to acquire + guards for that interpreter. This means that if you forget to close an + interpreter guard, the process will **permanently hang** during + finalization! + + Holding a guard for an interpreter is similar to holding a + :term:`strong reference` to a Python object, except finalization does not happen + automatically after all guards are released: it requires an explicit + :c:func:`Py_EndInterpreter` call. + + .. versionadded:: 3.15 + + +.. c:function:: PyInterpreterGuard *PyInterpreterGuard_FromCurrent(void) + + Create a finalization guard for the current interpreter. This will prevent + finalization until the guard is closed. + + For example: + + .. code-block:: c + + // Temporarily prevent finalization. + PyInterpreterGuard *guard = PyInterpreterGuard_FromCurrent(); + if (guard == NULL) { + // Finalization has already started or we're out of memory. + return NULL; + } + + Py_BEGIN_ALLOW_THREADS; + // Do some critical processing here. For example, we can safely acquire + // locks that might be acquired by the finalization thread. + Py_END_ALLOW_THREADS; + + // Now that we're done with our critical processing, the interpreter is + // allowed to finalize again. + PyInterpreterGuard_Close(guard); + + On success, this function returns a guard for the current interpreter; + on failure, it returns ``NULL`` with an exception set. + + This function will fail only if the current interpreter has already started + finalizing, or if the process is out of memory. + + The guard pointer returned by this function must be eventually closed + with :c:func:`PyInterpreterGuard_Close`; failing to do so will result in + the Python process infinitely hanging. + + The caller must hold an :term:`attached thread state`. + + .. versionadded:: 3.15 + + +.. c:function:: PyInterpreterGuard *PyInterpreterGuard_FromView(PyInterpreterView *view) + + Create a finalization guard for an interpreter through a view. + + On success, this function returns a guard to the interpreter + represented by *view*. The view is still valid after calling this + function. The guard must eventually be closed with + :c:func:`PyInterpreterGuard_Close`. + + If the interpreter no longer exists, is already finalizing, or out of memory, + then this function returns ``NULL`` without setting an exception. + + The caller does not need to hold an :term:`attached thread state`. + + .. versionadded:: 3.15 + + +.. c:function:: void PyInterpreterGuard_Close(PyInterpreterGuard *guard) + + Close an interpreter guard, allowing the interpreter to start + finalization if no other guards remain. If an interpreter guard + is never closed, the interpreter will infinitely wait when trying + to enter finalization! + + After an interpreter guard is closed, it may not be used in + :c:func:`PyThreadState_Ensure`. Doing so will result in undefined + behavior. + + This function cannot fail, and the caller doesn't need to hold an + :term:`attached thread state`. + + .. versionadded:: 3.15 + + +.. _interpreter-views: + +Interpreter views +----------------- + +In some cases, it may be necessary to access an interpreter that may have been +deleted. This can be done using interpreter views. + +.. c:type:: PyInterpreterView + + An opaque view of an interpreter. + + This is a thread-safe way to access an interpreter that may have be + finalizing or already destroyed. + + .. versionadded:: 3.15 + + +.. c:function:: PyInterpreterView *PyInterpreterView_FromCurrent(void) + + Create a view to the current interpreter. + + This function is generally meant to be used alongside + :c:func:`PyInterpreterGuard_FromView` or :c:func:`PyThreadState_EnsureFromView`. + + On success, this function returns a view to the current interpreter; on + failure, it returns ``NULL`` with an exception set. + + The caller must hold an :term:`attached thread state`. + + .. versionadded:: 3.15 + + +.. c:function:: void PyInterpreterView_Close(PyInterpreterView *view) + + Close an interpreter view. + + If an interpreter view is never closed, the view's memory will never be + freed, but there are no other consequences. (In contrast, forgetting to + close a guard will infinitely hang the main thread during finalization.) + + This function cannot fail, and the caller doesn't need to hold an + :term:`attached thread state`. + + .. versionadded:: 3.15 + + +.. c:function:: PyInterpreterView *PyInterpreterView_FromMain(void) + + Create a view for the main interpreter (the first and default + interpreter in a Python process; see + :c:func:`PyInterpreterState_Main`). + + On success, this function returns a view to the main + interpreter; on failure, it returns ``NULL`` without an exception set. + Failure indicates that the process is out of memory. + + Use this function when an interpreter pointer or view cannot be supplied + by the caller, such as when a native threading library does not provide a + ``void *arg`` parameter that could carry a :c:type:`PyInterpreterGuard` or + :c:type:`PyInterpreterView`. In code that supports subinterpreters, prefer + :c:func:`PyInterpreterView_FromCurrent` so the guard tracks the calling + interpreter rather than the main one. + + The caller does not need to hold an :term:`attached thread state`. + + .. versionadded:: 3.15 + + +Process-wide parameters +----------------------- + +.. c:function:: void Py_SetProgramName(const wchar_t *name) + + .. index:: + single: Py_Initialize() + single: main() + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.program_name` should be used instead, see :ref:`Python + Initialization Configuration `. + + This function should be called before :c:func:`Py_Initialize` is called for + the first time, if it is called at all. It tells the interpreter the value + of the ``argv[0]`` argument to the :c:func:`main` function of the program + (converted to wide characters). + This is used by some other functions below to find + the Python run-time libraries relative to the interpreter executable. The + default value is ``'python'``. The argument should point to a + zero-terminated wide character string in static storage whose contents will not + change for the duration of the program's execution. No code in the Python + interpreter will change the contents of this storage. + + Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a + :c:expr:`wchar_t*` string. + + .. deprecated-removed:: 3.11 3.16 + + +.. c:function:: const char* Py_GetVersion() + + Return the version of this Python interpreter. This is a string that looks + something like :: + + "3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 4.2.3]" + + .. index:: single: version (in module sys) + + The first word (up to the first space character) is the current Python version; + the first characters are the major and minor version separated by a + period. The returned string points into static storage; the caller should not + modify its value. The value is available to Python code as :data:`sys.version`. + + See also the :c:var:`Py_Version` constant. + + +.. c:function:: const char* Py_GetPlatform() + + .. index:: single: platform (in module sys) + + Return the platform identifier for the current platform. On Unix, this is + formed from the "official" name of the operating system, converted to lower + case, followed by the major revision number; e.g., for Solaris 2.x, which is + also known as SunOS 5.x, the value is ``'sunos5'``. On macOS, it is + ``'darwin'``. On Windows, it is ``'win'``. The returned string points into + static storage; the caller should not modify its value. The value is available + to Python code as ``sys.platform``. + + +.. c:function:: const char* Py_GetCopyright() + + Return the official copyright string for the current Python version, for example + + ``'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'`` + + .. index:: single: copyright (in module sys) + + The returned string points into static storage; the caller should not modify its + value. The value is available to Python code as ``sys.copyright``. + + +.. c:function:: const char* Py_GetCompiler() + + Return an indication of the compiler used to build the current Python version, + in square brackets, for example:: + + "[GCC 2.7.2.2]" + + .. index:: single: version (in module sys) + + The returned string points into static storage; the caller should not modify its + value. The value is available to Python code as part of the variable + ``sys.version``. + + +.. c:function:: const char* Py_GetBuildInfo() + + Return information about the sequence number and build date and time of the + current Python interpreter instance, for example :: + + "#67, Aug 1 1997, 22:34:28" + + .. index:: single: version (in module sys) + + The returned string points into static storage; the caller should not modify its + value. The value is available to Python code as part of the variable + ``sys.version``. + + +.. c:function:: void PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath) + + .. index:: + single: main() + single: Py_FatalError() + single: argv (in module sys) + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.argv`, :c:member:`PyConfig.parse_argv` and + :c:member:`PyConfig.safe_path` should be used instead, see :ref:`Python + Initialization Configuration `. + + Set :data:`sys.argv` based on *argc* and *argv*. These parameters are + similar to those passed to the program's :c:func:`main` function with the + difference that the first entry should refer to the script file to be + executed rather than the executable hosting the Python interpreter. If there + isn't a script that will be run, the first entry in *argv* can be an empty + string. If this function fails to initialize :data:`sys.argv`, a fatal + condition is signalled using :c:func:`Py_FatalError`. + + If *updatepath* is zero, this is all the function does. If *updatepath* + is non-zero, the function also modifies :data:`sys.path` according to the + following algorithm: + + - If the name of an existing script is passed in ``argv[0]``, the absolute + path of the directory where the script is located is prepended to + :data:`sys.path`. + - Otherwise (that is, if *argc* is ``0`` or ``argv[0]`` doesn't point + to an existing file name), an empty string is prepended to + :data:`sys.path`, which is the same as prepending the current working + directory (``"."``). + + Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a + :c:expr:`wchar_t*` string. + + See also :c:member:`PyConfig.orig_argv` and :c:member:`PyConfig.argv` + members of the :ref:`Python Initialization Configuration `. + + .. note:: + It is recommended that applications embedding the Python interpreter + for purposes other than executing a single script pass ``0`` as *updatepath*, + and update :data:`sys.path` themselves if desired. + See :cve:`2008-5983`. + + On versions before 3.1.3, you can achieve the same effect by manually + popping the first :data:`sys.path` element after having called + :c:func:`PySys_SetArgv`, for example using:: + + PyRun_SimpleString("import sys; sys.path.pop(0)\n"); + + .. versionadded:: 3.1.3 + + .. deprecated-removed:: 3.11 3.16 + + +.. c:function:: void PySys_SetArgv(int argc, wchar_t **argv) + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.argv` and :c:member:`PyConfig.parse_argv` should be used + instead, see :ref:`Python Initialization Configuration `. + + This function works like :c:func:`PySys_SetArgvEx` with *updatepath* set + to ``1`` unless the :program:`python` interpreter was started with the + :option:`-I`. + + Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a + :c:expr:`wchar_t*` string. + + See also :c:member:`PyConfig.orig_argv` and :c:member:`PyConfig.argv` + members of the :ref:`Python Initialization Configuration `. + + .. versionchanged:: 3.4 The *updatepath* value depends on :option:`-I`. + + .. deprecated-removed:: 3.11 3.16 + + +.. c:function:: void Py_SetPythonHome(const wchar_t *home) + + This API is kept for backward compatibility: setting + :c:member:`PyConfig.home` should be used instead, see :ref:`Python + Initialization Configuration `. + + Set the default "home" directory, that is, the location of the standard + Python libraries. See :envvar:`PYTHONHOME` for the meaning of the + argument string. + + The argument should point to a zero-terminated character string in static + storage whose contents will not change for the duration of the program's + execution. No code in the Python interpreter will change the contents of + this storage. + + Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a + :c:expr:`wchar_t*` string. + + .. deprecated-removed:: 3.11 3.16 diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index acce3dc215d157..500f2818e2e40a 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -107,6 +107,46 @@ header files properly declare the entry points to be ``extern "C"``. As a result there is no need to do anything special to use the API from C++. +.. _capi-system-includes: + +System includes +--------------- + + :file:`Python.h` includes several standard header files. + C extensions should include the standard headers that they use, + and should not rely on these implicit includes. + The implicit includes are: + + * ```` + * ```` (on Windows) + * ```` + * ```` + * ```` + * ```` + * ```` + * ```` + * ```` (if present) + + The following are included for backwards compatibility, unless using + :ref:`Limited API ` 3.13 or newer: + + * ```` + * ```` (on POSIX) + + The following are included for backwards compatibility, unless using + :ref:`Limited API ` 3.11 or newer: + + * ```` + * ```` + * ```` + +.. note:: + + Since Python may define some pre-processor definitions which affect the standard + headers on some systems, you *must* include :file:`Python.h` before any standard + headers are included. + + Useful macros ============= @@ -116,18 +156,279 @@ defined closer to where they are useful (for example, :c:macro:`Py_RETURN_NONE`, Others of a more general utility are defined here. This is not necessarily a complete listing. +.. c:macro:: Py_CAN_START_THREADS + + If this macro is defined, then the current system is able to start threads. + + Currently, all systems supported by CPython (per :pep:`11`), with the + exception of some WebAssembly platforms, support starting threads. + + .. versionadded:: 3.13 + +.. c:macro:: Py_GETENV(s) + + Like :samp:`getenv({s})`, but returns ``NULL`` if :option:`-E` was passed + on the command line (see :c:member:`PyConfig.use_environment`). + + +Docstring macros +---------------- + +.. c:macro:: PyDoc_STRVAR(name, str) + + Creates a variable with name *name* that can be used in docstrings. + If Python is built without docstrings (:option:`--without-doc-strings`), + the value will be an empty string. + + Example:: + + PyDoc_STRVAR(pop_doc, "Remove and return the rightmost element."); + + static PyMethodDef deque_methods[] = { + // ... + {"pop", (PyCFunction)deque_pop, METH_NOARGS, pop_doc}, + // ... + } + + Expands to :samp:`PyDoc_VAR({name}) = PyDoc_STR({str})`. + +.. c:macro:: PyDoc_STR(str) + + Expands to the given input string, or an empty string + if docstrings are disabled (:option:`--without-doc-strings`). + + Example:: + + static PyMethodDef pysqlite_row_methods[] = { + {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS, + PyDoc_STR("Returns the keys of the row.")}, + {NULL, NULL} + }; + +.. c:macro:: PyDoc_VAR(name) + + Declares a static character array variable with the given *name*. + Expands to :samp:`static const char {name}[]` + + For example:: + + PyDoc_VAR(python_doc) = PyDoc_STR( + "A genus of constricting snakes in the Pythonidae family native " + "to the tropics and subtropics of the Eastern Hemisphere."); + + +General utility macros +---------------------- + +The following macros are for common tasks not specific to Python. + +.. c:macro:: Py_UNUSED(arg) + + Use this for unused arguments in a function definition to silence compiler + warnings. Example: ``int func(int a, int Py_UNUSED(b)) { return a; }``. + + .. versionadded:: 3.4 + +.. c:macro:: Py_GCC_ATTRIBUTE(name) + + Use a GCC attribute *name*, hiding it from compilers that don't support GCC + attributes (such as MSVC). + + This expands to :samp:`__attribute__(({name)})` on a GCC compiler, + and expands to nothing on compilers that don't support GCC attributes. + + +Numeric utilities +^^^^^^^^^^^^^^^^^ .. c:macro:: Py_ABS(x) Return the absolute value of ``x``. + The argument may be evaluated more than once. + Consequently, do not pass an expression with side-effects directly + to this macro. + + If the result cannot be represented (for example, if ``x`` has + :c:macro:`!INT_MIN` value for :c:expr:`int` type), the behavior is + undefined. + + Corresponds roughly to :samp:`(({x}) < 0 ? -({x}) : ({x}))` + + .. versionadded:: 3.3 + +.. c:macro:: Py_MAX(x, y) + Py_MIN(x, y) + + Return the larger or smaller of the arguments, respectively. + + Any arguments may be evaluated more than once. + Consequently, do not pass an expression with side-effects directly + to this macro. + + :c:macro:`!Py_MAX` corresponds roughly to + :samp:`((({x}) > ({y})) ? ({x}) : ({y}))`. + .. versionadded:: 3.3 +.. c:macro:: Py_ARITHMETIC_RIGHT_SHIFT(type, integer, positions) + + Similar to :samp:`{integer} >> {positions}`, but forces sign extension, + as the C standard does not define whether a right-shift of a signed + integer will perform sign extension or a zero-fill. + + *integer* should be any signed integer type. + *positions* is the number of positions to shift to the right. + + Both *integer* and *positions* can be evaluated more than once; + consequently, avoid directly passing a function call or some other + operation with side-effects to this macro. Instead, store the result as a + variable and then pass it. + + *type* is unused and only kept for backwards compatibility. Historically, + *type* was used to cast *integer*. + + .. versionchanged:: 3.1 + + This macro is now valid for all signed integer types, not just those for + which ``unsigned type`` is legal. As a result, *type* is no longer + used. + +.. c:macro:: Py_CHARMASK(c) + + Argument must be a character or an integer in the range [-128, 127] or [0, + 255]. This macro returns ``c`` cast to an ``unsigned char``. + + +Assertion utilities +^^^^^^^^^^^^^^^^^^^ + +.. c:macro:: Py_UNREACHABLE() + + Use this when you have a code path that cannot be reached by design. + For example, in the ``default:`` clause in a ``switch`` statement for which + all possible values are covered in ``case`` statements. Use this in places + where you might be tempted to put an ``assert(0)`` or ``abort()`` call. + + In release mode, the macro helps the compiler to optimize the code, and + avoids a warning about unreachable code. For example, the macro is + implemented with ``__builtin_unreachable()`` on GCC in release mode. + + In debug mode, and on unsupported compilers, the macro expands to a call to + :c:func:`Py_FatalError`. + + A use for ``Py_UNREACHABLE()`` is following a call to a function that + never returns but that is not declared ``_Noreturn``. + + If a code path is very unlikely code but can be reached under exceptional + case, this macro must not be used. For example, under low memory condition + or if a system call returns a value out of the expected range. In this + case, it's better to report the error to the caller. If the error cannot + be reported to caller, :c:func:`Py_FatalError` can be used. + + .. versionadded:: 3.7 + +.. c:macro:: Py_SAFE_DOWNCAST(value, larger, smaller) + + Cast *value* to type *smaller* from type *larger*, validating that no + information was lost. + + On release builds of Python, this is roughly equivalent to + :samp:`(({smaller}) {value})` + (in C++, :samp:`static_cast<{smaller}>({value})` will be used instead). + + On debug builds (implying that :c:macro:`Py_DEBUG` is defined), this asserts + that no information was lost with the cast from *larger* to *smaller*. + + *value*, *larger*, and *smaller* may all be evaluated more than once in the + expression; consequently, do not pass an expression with side-effects + directly to this macro. + +.. c:macro:: Py_BUILD_ASSERT(cond) + + Asserts a compile-time condition *cond*, as a statement. + The build will fail if the condition is false or cannot be evaluated at compile time. + + Corresponds roughly to :samp:`static_assert({cond})` on C23 and above. + + For example:: + + Py_BUILD_ASSERT(sizeof(PyTime_t) == sizeof(int64_t)); + + .. versionadded:: 3.3 + +.. c:macro:: Py_BUILD_ASSERT_EXPR(cond) + + Asserts a compile-time condition *cond*, as an expression that evaluates to ``0``. + The build will fail if the condition is false or cannot be evaluated at compile time. + + For example:: + + #define foo_to_char(foo) \ + ((char *)(foo) + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0)) + + .. versionadded:: 3.3 + + +Type size utilities +^^^^^^^^^^^^^^^^^^^ + +.. c:macro:: Py_ARRAY_LENGTH(array) + + Compute the length of a statically allocated C array at compile time. + + The *array* argument must be a C array with a size known at compile time. + Passing an array with an unknown size, such as a heap-allocated array, + will result in a compilation error on some compilers, or otherwise produce + incorrect results. + + This is roughly equivalent to:: + + sizeof(array) / sizeof((array)[0]) + +.. c:macro:: Py_MEMBER_SIZE(type, member) + + Return the size of a structure (*type*) *member* in bytes. + + Corresponds roughly to :samp:`sizeof((({type} *)NULL)->{member})`. + + .. versionadded:: 3.6 + + +Macro definition utilities +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. c:macro:: Py_FORCE_EXPANSION(X) + + This is equivalent to :samp:`{X}`, which is useful for token-pasting in + macros, as macro expansions in *X* are forcefully evaluated by the + preprocessor. + +.. c:macro:: Py_STRINGIFY(x) + + Convert ``x`` to a C string. For example, ``Py_STRINGIFY(123)`` returns + ``"123"``. + + .. versionadded:: 3.4 + + +Declaration utilities +--------------------- + +The following macros can be used in declarations. +They are most useful for defining the C API itself, and have limited use +for extension authors. +Most of them expand to compiler-specific spellings of common extensions +to the C language. + .. c:macro:: Py_ALWAYS_INLINE Ask the compiler to always inline a static inline function. The compiler can ignore it and decide to not inline the function. + Corresponds to ``always_inline`` attribute in GCC and ``__forceinline`` + in MSVC. + It can be used to inline performance critical static inline functions when building Python in debug mode with function inlining disabled. For example, MSC disables function inlining when building in debug mode. @@ -145,15 +446,24 @@ complete listing. .. versionadded:: 3.11 -.. c:macro:: Py_CHARMASK(c) +.. c:macro:: Py_NO_INLINE - Argument must be a character or an integer in the range [-128, 127] or [0, - 255]. This macro returns ``c`` cast to an ``unsigned char``. + Disable inlining on a function. For example, it reduces the C stack + consumption: useful on LTO+PGO builds which heavily inline code (see + :issue:`33720`). + + Corresponds to the ``noinline`` attribute/specification on GCC and MSVC. + + Usage:: + + Py_NO_INLINE static int random(void) { return 4; } + + .. versionadded:: 3.11 .. c:macro:: Py_DEPRECATED(version) - Use this for deprecated declarations. The macro must be placed before the - symbol name. + Use this to declare APIs that were deprecated in a specific CPython version. + The macro must be placed before the symbol name. Example:: @@ -162,110 +472,153 @@ complete listing. .. versionchanged:: 3.8 MSVC support was added. -.. c:macro:: Py_GETENV(s) +.. c:macro:: Py_LOCAL(type) - Like ``getenv(s)``, but returns ``NULL`` if :option:`-E` was passed on the - command line (see :c:member:`PyConfig.use_environment`). + Declare a function returning the specified *type* using a fast-calling + qualifier for functions that are local to the current file. + Semantically, this is equivalent to :samp:`static {type}`. -.. c:macro:: Py_MAX(x, y) +.. c:macro:: Py_LOCAL_INLINE(type) - Return the maximum value between ``x`` and ``y``. + Equivalent to :c:macro:`Py_LOCAL` but additionally requests the function + be inlined. - .. versionadded:: 3.3 +.. c:macro:: Py_LOCAL_SYMBOL -.. c:macro:: Py_MEMBER_SIZE(type, member) + Macro used to declare a symbol as local to the shared library (hidden). + On supported platforms, it ensures the symbol is not exported. - Return the size of a structure (``type``) ``member`` in bytes. + On compatible versions of GCC/Clang, it + expands to ``__attribute__((visibility("hidden")))``. - .. versionadded:: 3.6 +.. c:macro:: Py_EXPORTED_SYMBOL -.. c:macro:: Py_MIN(x, y) + Macro used to declare a symbol (function or data) as exported. + On Windows, this expands to ``__declspec(dllexport)``. + On compatible versions of GCC/Clang, it + expands to ``__attribute__((visibility("default")))``. + This macro is for defining the C API itself; extension modules should not use it. - Return the minimum value between ``x`` and ``y``. - .. versionadded:: 3.3 +.. c:macro:: Py_IMPORTED_SYMBOL -.. c:macro:: Py_NO_INLINE + Macro used to declare a symbol as imported. + On Windows, this expands to ``__declspec(dllimport)``. + This macro is for defining the C API itself; extension modules should not use it. - Disable inlining on a function. For example, it reduces the C stack - consumption: useful on LTO+PGO builds which heavily inline code (see - :issue:`33720`). - Usage:: +.. c:macro:: PyAPI_FUNC(type) - Py_NO_INLINE static int random(void) { return 4; } + Macro used by CPython to declare a function as part of the C API. + Its expansion depends on the platform and build configuration. + This macro is intended for defining CPython's C API itself; + extension modules should not use it for their own symbols. - .. versionadded:: 3.11 -.. c:macro:: Py_STRINGIFY(x) +.. c:macro:: PyAPI_DATA(type) - Convert ``x`` to a C string. E.g. ``Py_STRINGIFY(123)`` returns - ``"123"``. + Macro used by CPython to declare a public global variable as part of the C API. + Its expansion depends on the platform and build configuration. + This macro is intended for defining CPython's C API itself; + extension modules should not use it for their own symbols. - .. versionadded:: 3.4 -.. c:macro:: Py_UNREACHABLE() +Outdated macros +--------------- - Use this when you have a code path that cannot be reached by design. - For example, in the ``default:`` clause in a ``switch`` statement for which - all possible values are covered in ``case`` statements. Use this in places - where you might be tempted to put an ``assert(0)`` or ``abort()`` call. +The following :term:`soft deprecated` macros have been used to features that +have been standardized in C11 (or previous standards). - In release mode, the macro helps the compiler to optimize the code, and - avoids a warning about unreachable code. For example, the macro is - implemented with ``__builtin_unreachable()`` on GCC in release mode. +.. c:macro:: Py_ALIGNED(num) - A use for ``Py_UNREACHABLE()`` is following a call a function that - never returns but that is not declared :c:macro:`_Py_NO_RETURN`. + On some GCC-like compilers, specify alignment to *num* bytes. + This does nothing on other compilers. - If a code path is very unlikely code but can be reached under exceptional - case, this macro must not be used. For example, under low memory condition - or if a system call returns a value out of the expected range. In this - case, it's better to report the error to the caller. If the error cannot - be reported to caller, :c:func:`Py_FatalError` can be used. + Use the standard ``alignas`` specifier rather than this macro. - .. versionadded:: 3.7 + .. soft-deprecated:: 3.15 -.. c:macro:: Py_UNUSED(arg) +.. c:macro:: PY_FORMAT_SIZE_T - Use this for unused arguments in a function definition to silence compiler - warnings. Example: ``int func(int a, int Py_UNUSED(b)) { return a; }``. + The :c:func:`printf` formatting modifier for :c:type:`size_t`. + Use ``"z"`` directly instead. - .. versionadded:: 3.4 + .. soft-deprecated:: 3.15 -.. c:macro:: PyDoc_STRVAR(name, str) +.. c:macro:: Py_LL(number) + Py_ULL(number) - Creates a variable with name ``name`` that can be used in docstrings. - If Python is built without docstrings, the value will be empty. + Use *number* as a ``long long`` or ``unsigned long long`` integer literal, + respectively. - Use :c:macro:`PyDoc_STRVAR` for docstrings to support building - Python without docstrings, as specified in :pep:`7`. + Expands to *number* followed by ``LL`` or ``LLU``, respectively, but will + expand to some compiler-specific suffixes on some older compilers. - Example:: + Consider using the C99 standard suffixes ``LL`` and ``LLU`` directly. - PyDoc_STRVAR(pop_doc, "Remove and return the rightmost element."); + .. soft-deprecated:: 3.15 - static PyMethodDef deque_methods[] = { - // ... - {"pop", (PyCFunction)deque_pop, METH_NOARGS, pop_doc}, - // ... - } +.. c:macro:: PY_LONG_LONG + PY_INT32_T + PY_UINT32_T + PY_INT64_T + PY_UINT64_T -.. c:macro:: PyDoc_STR(str) + Aliases for the types :c:type:`!long long`, :c:type:`!int32_t`, + :c:type:`!uint32_t`. :c:type:`!int64_t` and :c:type:`!uint64_t`, + respectively. + Historically, these types needed compiler-specific extensions. - Creates a docstring for the given input string or an empty string - if docstrings are disabled. + .. soft-deprecated:: 3.15 - Use :c:macro:`PyDoc_STR` in specifying docstrings to support - building Python without docstrings, as specified in :pep:`7`. +.. c:macro:: PY_LLONG_MIN + PY_LLONG_MAX + PY_ULLONG_MAX + PY_SIZE_MAX - Example:: + Aliases for the values :c:macro:`!LLONG_MIN`, :c:macro:`!LLONG_MAX`, + :c:macro:`!ULLONG_MAX`, and :c:macro:`!SIZE_MAX`, respectively. + Use these standard names instead. - static PyMethodDef pysqlite_row_methods[] = { - {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS, - PyDoc_STR("Returns the keys of the row.")}, - {NULL, NULL} - }; + The required header, ````, + :ref:`is included ` in ``Python.h``. + + .. soft-deprecated:: 3.15 + +.. c:macro:: Py_MEMCPY(dest, src, n) + + This is an alias to :c:func:`!memcpy`. + + .. soft-deprecated:: 3.14 + Use :c:func:`!memcpy` directly instead. + +.. c:macro:: Py_UNICODE_SIZE + + Size of the :c:type:`!wchar_t` type. + Use ``sizeof(wchar_t)`` or ``WCHAR_WIDTH/8`` instead. + + The required header for the latter, ````, + :ref:`is included ` in ``Python.h``. + + .. soft-deprecated:: 3.15 + +.. c:macro:: Py_UNICODE_WIDE + + Defined if ``wchar_t`` can hold a Unicode character (UCS-4). + Use ``sizeof(wchar_t) >= 4`` instead + + .. soft-deprecated:: 3.15 + +.. c:macro:: Py_VA_COPY + + This is an alias to the C99-standard ``va_copy`` function. + + Historically, this would use a compiler-specific method to copy a ``va_list``. + + .. versionchanged:: 3.6 + This is now an alias to ``va_copy``. + + .. soft-deprecated:: 3.15 .. _api-objects: diff --git a/Doc/c-api/iter.rst b/Doc/c-api/iter.rst index bf9df62c6f1706..6cfd24c5ae60c8 100644 --- a/Doc/c-api/iter.rst +++ b/Doc/c-api/iter.rst @@ -54,6 +54,6 @@ There are two functions specifically for working with iterators. - ``PYGEN_RETURN`` if iterator returns. Return value is returned via *presult*. - ``PYGEN_NEXT`` if iterator yields. Yielded value is returned via *presult*. - - ``PYGEN_ERROR`` if iterator has raised and exception. *presult* is set to ``NULL``. + - ``PYGEN_ERROR`` if iterator has raised an exception. *presult* is set to ``NULL``. .. versionadded:: 3.10 diff --git a/Doc/c-api/iterator.rst b/Doc/c-api/iterator.rst index 6b7ba8c9979163..bfbfe3c9279980 100644 --- a/Doc/c-api/iterator.rst +++ b/Doc/c-api/iterator.rst @@ -50,3 +50,72 @@ sentinel value is returned. callable object that can be called with no parameters; each call to it should return the next item in the iteration. When *callable* returns a value equal to *sentinel*, the iteration will be terminated. + + +Range Objects +^^^^^^^^^^^^^ + +.. c:var:: PyTypeObject PyRange_Type + + The type object for :class:`range` objects. + + +.. c:function:: int PyRange_Check(PyObject *o) + + Return true if the object *o* is an instance of a :class:`range` object. + This function always succeeds. + + +Builtin Iterator Types +^^^^^^^^^^^^^^^^^^^^^^ + +These are built-in iteration types that are included in Python's C API, but +provide no additional functions. They are here for completeness. + + +.. list-table:: + :widths: auto + :header-rows: 1 + + * * C type + * Python type + * * .. c:var:: PyTypeObject PyEnum_Type + * :py:class:`enumerate` + * * .. c:var:: PyTypeObject PyFilter_Type + * :py:class:`filter` + * * .. c:var:: PyTypeObject PyMap_Type + * :py:class:`map` + * * .. c:var:: PyTypeObject PyReversed_Type + * :py:class:`reversed` + * * .. c:var:: PyTypeObject PyZip_Type + * :py:class:`zip` + + +Other Iterator Objects +^^^^^^^^^^^^^^^^^^^^^^ + +.. c:var:: PyTypeObject PyByteArrayIter_Type +.. c:var:: PyTypeObject PyBytesIter_Type +.. c:var:: PyTypeObject PyListIter_Type +.. c:var:: PyTypeObject PyListRevIter_Type +.. c:var:: PyTypeObject PySetIter_Type +.. c:var:: PyTypeObject PyTupleIter_Type +.. c:var:: PyTypeObject PyRangeIter_Type +.. c:var:: PyTypeObject PyLongRangeIter_Type +.. c:var:: PyTypeObject PyDictIterKey_Type +.. c:var:: PyTypeObject PyDictRevIterKey_Type +.. c:var:: PyTypeObject PyDictIterValue_Type +.. c:var:: PyTypeObject PyDictRevIterValue_Type +.. c:var:: PyTypeObject PyDictIterItem_Type +.. c:var:: PyTypeObject PyDictRevIterItem_Type +.. c:var:: PyTypeObject PyODictIter_Type + + Type objects for iterators of various built-in objects. + + Do not create instances of these directly; prefer calling + :c:func:`PyObject_GetIter` instead. + + Note that there is no guarantee that a given built-in type uses a given iterator + type. For example, iterating over :class:`range` will use one of two iterator + types depending on the size of the range. Other types may start using a + similar scheme in the future, without warning. diff --git a/Doc/c-api/lifecycle.rst b/Doc/c-api/lifecycle.rst index 5a170862a26f44..531c4080a0131c 100644 --- a/Doc/c-api/lifecycle.rst +++ b/Doc/c-api/lifecycle.rst @@ -256,6 +256,8 @@ To allocate and free memory, see :ref:`allocating-objects`. collection (i.e., the :c:macro:`Py_TPFLAGS_HAVE_GC` flag is set); this may change in the future. + .. versionadded:: 3.4 + .. c:function:: int PyObject_CallFinalizerFromDealloc(PyObject *op) @@ -266,6 +268,8 @@ To allocate and free memory, see :ref:`allocating-objects`. should happen. Otherwise, this function returns 0 and destruction can continue normally. + .. versionadded:: 3.4 + .. seealso:: :c:member:`~PyTypeObject.tp_dealloc` for example code. diff --git a/Doc/c-api/list.rst b/Doc/c-api/list.rst index 758415a76e5cb4..8f560699d355e4 100644 --- a/Doc/c-api/list.rst +++ b/Doc/c-api/list.rst @@ -74,11 +74,25 @@ List Objects Like :c:func:`PyList_GetItemRef`, but returns a :term:`borrowed reference` instead of a :term:`strong reference`. + .. note:: + + In the :term:`free-threaded build`, the returned + :term:`borrowed reference` may become invalid if another thread modifies + the list concurrently. Prefer :c:func:`PyList_GetItemRef`, which returns + a :term:`strong reference`. + .. c:function:: PyObject* PyList_GET_ITEM(PyObject *list, Py_ssize_t i) Similar to :c:func:`PyList_GetItem`, but without error checking. + .. note:: + + In the :term:`free-threaded build`, the returned + :term:`borrowed reference` may become invalid if another thread modifies + the list concurrently. Prefer :c:func:`PyList_GetItemRef`, which returns + a :term:`strong reference`. + .. c:function:: int PyList_SetItem(PyObject *list, Py_ssize_t index, PyObject *item) @@ -108,6 +122,14 @@ List Objects is being replaced; any reference in *list* at position *i* will be leaked. + .. note:: + + In the :term:`free-threaded build`, this macro has no internal + synchronization. It is normally only used to fill in new lists where no + other thread has a reference to the list. If the list may be shared, + use :c:func:`PyList_SetItem` instead, which uses a :term:`per-object + lock`. + .. c:function:: int PyList_Insert(PyObject *list, Py_ssize_t index, PyObject *item) @@ -138,6 +160,12 @@ List Objects Return ``0`` on success, ``-1`` on failure. Indexing from the end of the list is not supported. + .. note:: + + In the :term:`free-threaded build`, when *itemlist* is a :class:`list`, + both *list* and *itemlist* are locked for the duration of the operation. + For other iterables (or ``NULL``), only *list* is locked. + .. c:function:: int PyList_Extend(PyObject *list, PyObject *iterable) @@ -150,6 +178,14 @@ List Objects .. versionadded:: 3.13 + .. note:: + + In the :term:`free-threaded build`, when *iterable* is a :class:`list`, + :class:`set`, :class:`dict`, or dict view, both *list* and *iterable* + (or its underlying dict) are locked for the duration of the operation. + For other iterables, only *list* is locked; *iterable* may be + concurrently modified by another thread. + .. c:function:: int PyList_Clear(PyObject *list) @@ -168,6 +204,14 @@ List Objects Sort the items of *list* in place. Return ``0`` on success, ``-1`` on failure. This is equivalent to ``list.sort()``. + .. note:: + + In the :term:`free-threaded build`, element comparison via + :meth:`~object.__lt__` can execute arbitrary Python code, during which + the :term:`per-object lock` may be temporarily released. For built-in + types (:class:`str`, :class:`int`, :class:`float`), the lock is not + released during comparison. + .. c:function:: int PyList_Reverse(PyObject *list) diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index 2d0bda76697e81..874e422d4701dd 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -40,9 +40,11 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. Return a new :c:type:`PyLongObject` object from *v*, or ``NULL`` on failure. - The current implementation keeps an array of integer objects for all integers - between ``-5`` and ``256``. When you create an int in that range you actually - just get back a reference to the existing object. + .. impl-detail:: + + CPython keeps an array of integer objects for all integers + between ``-5`` and ``1024``. When you create an int in that range + you actually just get back a reference to the existing object. .. c:function:: PyObject* PyLong_FromUnsignedLong(unsigned long v) @@ -69,6 +71,12 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. on failure. +.. c:function:: PyObject* PyLong_FromUnsignedLongLong(unsigned long long v) + + Return a new :c:type:`PyLongObject` object from a C :c:expr:`unsigned long long`, + or ``NULL`` on failure. + + .. c:function:: PyObject* PyLong_FromInt32(int32_t value) PyObject* PyLong_FromInt64(int64_t value) @@ -79,12 +87,6 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. versionadded:: 3.14 -.. c:function:: PyObject* PyLong_FromUnsignedLongLong(unsigned long long v) - - Return a new :c:type:`PyLongObject` object from a C :c:expr:`unsigned long long`, - or ``NULL`` on failure. - - .. c:function:: PyObject* PyLong_FromUInt32(uint32_t value) PyObject* PyLong_FromUInt64(uint64_t value) @@ -159,6 +161,17 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. versionadded:: 3.13 +.. c:macro:: PyLong_FromPid(pid) + + Macro for creating a Python integer from a process identifier. + + This can be defined as an alias to :c:func:`PyLong_FromLong` or + :c:func:`PyLong_FromLongLong`, depending on the size of the system's + PID type. + + .. versionadded:: 3.2 + + .. c:function:: long PyLong_AsLong(PyObject *obj) .. index:: @@ -184,12 +197,10 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. c:function:: long PyLong_AS_LONG(PyObject *obj) - A :term:`soft deprecated` alias. Exactly equivalent to the preferred ``PyLong_AsLong``. In particular, it can fail with :exc:`OverflowError` or another exception. - .. deprecated:: 3.14 - The function is soft deprecated. + .. soft-deprecated:: 3.14 .. c:function:: int PyLong_AsInt(PyObject *obj) @@ -372,6 +383,10 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. Set *\*value* to a signed C :c:expr:`int32_t` or :c:expr:`int64_t` representation of *obj*. + If *obj* is not an instance of :c:type:`PyLongObject`, first call its + :meth:`~object.__index__` method (if present) to convert it to a + :c:type:`PyLongObject`. + If the *obj* value is out of range, raise an :exc:`OverflowError`. Set *\*value* and return ``0`` on success. @@ -436,8 +451,8 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. Otherwise, returns the number of bytes required to store the value. If this is equal to or less than *n_bytes*, the entire value was copied. - All *n_bytes* of the buffer are written: large buffers are padded with - zeroes. + All *n_bytes* of the buffer are written: remaining bytes filled by + copies of the sign bit. If the returned value is greater than *n_bytes*, the value was truncated: as many of the lowest bits of the value as could fit are written, @@ -569,6 +584,17 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. .. versionadded:: 3.13 +.. c:macro:: PyLong_AsPid(pid) + + Macro for converting a Python integer into a process identifier. + + This can be defined as an alias to :c:func:`PyLong_AsLong`, + :c:func:`PyLong_FromLongLong`, or :c:func:`PyLong_AsInt`, depending on the + size of the system's PID type. + + .. versionadded:: 3.2 + + .. c:function:: int PyLong_GetSign(PyObject *obj, int *sign) Get the sign of the integer object *obj*. @@ -659,7 +685,7 @@ Export API .. versionadded:: 3.14 -.. c:struct:: PyLongLayout +.. c:type:: PyLongLayout Layout of an array of "digits" ("limbs" in the GMP terminology), used to represent absolute value for arbitrary precision integers. @@ -699,7 +725,7 @@ Export API Get the native layout of Python :class:`int` objects. - See the :c:struct:`PyLongLayout` structure. + See the :c:type:`PyLongLayout` structure. The function must not be called before Python initialization nor after Python finalization. The returned layout is valid until Python is @@ -707,7 +733,7 @@ Export API in a process, and so it can be cached. -.. c:struct:: PyLongExport +.. c:type:: PyLongExport Export of a Python :class:`int` object. @@ -741,7 +767,7 @@ Export API Export a Python :class:`int` object. - *export_long* must point to a :c:struct:`PyLongExport` structure allocated + *export_long* must point to a :c:type:`PyLongExport` structure allocated by the caller. It must not be ``NULL``. On success, fill in *\*export_long* and return ``0``. @@ -771,7 +797,7 @@ The :c:type:`PyLongWriter` API can be used to import an integer. .. versionadded:: 3.14 -.. c:struct:: PyLongWriter +.. c:type:: PyLongWriter A Python :class:`int` writer instance. @@ -799,7 +825,7 @@ The :c:type:`PyLongWriter` API can be used to import an integer. 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 + (where the :c:type:`~PyLongLayout.bits_per_digit` is the number of bits per digit). Any unused most significant digits must be set to ``0``. @@ -827,3 +853,31 @@ The :c:type:`PyLongWriter` API can be used to import an integer. If *writer* is ``NULL``, no operation is performed. The writer instance and the *digits* array are invalid after the call. + + +Deprecated API +^^^^^^^^^^^^^^ + +These macros are :term:`soft deprecated`. They describe parameters +of the internal representation of :c:type:`PyLongObject` instances. + +Use :c:func:`PyLong_GetNativeLayout` instead, along with :c:func:`PyLong_Export` +to read integer data or :c:type:`PyLongWriter` to write it. +These currently use the same layout, but are designed to continue working correctly +even if CPython's internal integer representation changes. + + +.. c:macro:: PyLong_SHIFT + + This is equivalent to :c:member:`~PyLongLayout.bits_per_digit` in + the output of :c:func:`PyLong_GetNativeLayout`. + + +.. c:macro:: PyLong_BASE + + This is currently equivalent to :c:expr:`1 << PyLong_SHIFT`. + + +.. c:macro:: PyLong_MASK + + This is currently equivalent to :c:expr:`(1 << PyLong_SHIFT) - 1` diff --git a/Doc/c-api/mapping.rst b/Doc/c-api/mapping.rst index 1f55c0aa955c75..2476ebb9b69dce 100644 --- a/Doc/c-api/mapping.rst +++ b/Doc/c-api/mapping.rst @@ -102,7 +102,7 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and .. note:: - Exceptions which occur when this calls :meth:`~object.__getitem__` + Exceptions which occur when this calls the :meth:`~object.__getitem__` method are silently ignored. For proper error handling, use :c:func:`PyMapping_HasKeyWithError`, :c:func:`PyMapping_GetOptionalItem` or :c:func:`PyObject_GetItem()` instead. @@ -116,7 +116,7 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and .. note:: - Exceptions that occur when this calls :meth:`~object.__getitem__` + Exceptions that occur when this calls the :meth:`~object.__getitem__` method or while creating the temporary :class:`str` object are silently ignored. For proper error handling, use :c:func:`PyMapping_HasKeyStringWithError`, diff --git a/Doc/c-api/marshal.rst b/Doc/c-api/marshal.rst index 61218a1bf6f171..668a163b2df5a1 100644 --- a/Doc/c-api/marshal.rst +++ b/Doc/c-api/marshal.rst @@ -82,7 +82,7 @@ The following functions allow marshalled values to be read back in. assumes that no further objects will be read from the file, allowing it to aggressively load file data into memory so that the de-serialization can operate from data in memory rather than reading a byte at a time from the - file. Only use these variant if you are certain that you won't be reading + file. Only use this variant if you are certain that you won't be reading anything else from the file. On error, sets the appropriate exception (:exc:`EOFError`, :exc:`ValueError` diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst index df1bb0ce370919..73310670ac371c 100644 --- a/Doc/c-api/memory.rst +++ b/Doc/c-api/memory.rst @@ -7,10 +7,6 @@ Memory Management ***************** -.. sectionauthor:: Vladimir Marangozov - - - .. _memoryoverview: Overview @@ -81,7 +77,7 @@ memory footprint as a whole. Consequently, under certain circumstances, the Python memory manager may or may not trigger appropriate actions, like garbage collection, memory compaction or other preventive procedures. Note that by using the C library allocator as shown in the previous example, the allocated memory -for the I/O buffer escapes completely the Python memory manager. +for the I/O buffer completely escapes the Python memory manager. .. seealso:: @@ -102,7 +98,7 @@ All allocating functions belong to one of three different "domains" (see also strategies and are optimized for different purposes. The specific details on how every domain allocates memory or what internal functions each domain calls is considered an implementation detail, but for debugging purposes a simplified -table can be found at :ref:`here `. +table can be found at :ref:`default-memory-allocators`. The APIs used to allocate and free a block of memory must be from the same domain. For example, :c:func:`PyMem_Free` must be used to free memory allocated using :c:func:`PyMem_Malloc`. @@ -161,7 +157,7 @@ zero bytes. .. c:function:: void* PyMem_RawCalloc(size_t nelem, size_t elsize) - Allocates *nelem* elements each whose size in bytes is *elsize* and returns + Allocates *nelem* elements each of size *elsize* bytes and returns a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. The memory is initialized to zeros. @@ -208,8 +204,11 @@ The following function sets, modeled after the ANSI C standard, but specifying behavior when requesting zero bytes, are available for allocating and releasing memory from the Python heap. -The :ref:`default memory allocator ` uses the -:ref:`pymalloc memory allocator `. +In the GIL-enabled build (default build) the +:ref:`default memory allocator ` uses the +:ref:`pymalloc memory allocator `, whereas in the +:term:`free-threaded build`, the default is the +:ref:`mimalloc memory allocator ` instead. .. warning:: @@ -219,6 +218,11 @@ The :ref:`default memory allocator ` uses the The default allocator is now pymalloc instead of system :c:func:`malloc`. +.. versionchanged:: 3.13 + + In the :term:`free-threaded ` build, the default allocator + is now :ref:`mimalloc `. + .. c:function:: void* PyMem_Malloc(size_t n) Allocates *n* bytes and returns a pointer of type :c:expr:`void*` to the @@ -231,7 +235,7 @@ The :ref:`default memory allocator ` uses the .. c:function:: void* PyMem_Calloc(size_t nelem, size_t elsize) - Allocates *nelem* elements each whose size in bytes is *elsize* and returns + Allocates *nelem* elements each of size *elsize* bytes and returns a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. The memory is initialized to zeros. @@ -293,17 +297,39 @@ The following type-oriented macros are provided for convenience. Note that Same as :c:func:`PyMem_Free`. -In addition, the following macro sets are provided for calling the Python memory -allocator directly, without involving the C API functions listed above. However, -note that their use does not preserve binary compatibility across Python -versions and is therefore deprecated in extension modules. -* ``PyMem_MALLOC(size)`` -* ``PyMem_NEW(type, size)`` -* ``PyMem_REALLOC(ptr, size)`` -* ``PyMem_RESIZE(ptr, type, size)`` -* ``PyMem_FREE(ptr)`` -* ``PyMem_DEL(ptr)`` +Deprecated aliases +------------------ + +These are :term:`soft deprecated` aliases to existing functions and macros. +They exist solely for backwards compatibility. + +.. list-table:: + :widths: auto + :header-rows: 1 + + * * Deprecated alias + * Corresponding function or macro + * * .. c:macro:: PyMem_MALLOC(size) + * :c:func:`PyMem_Malloc` + * * .. c:macro:: PyMem_NEW(type, size) + * :c:macro:`PyMem_New` + * * .. c:macro:: PyMem_REALLOC(ptr, size) + * :c:func:`PyMem_Realloc` + * * .. c:macro:: PyMem_RESIZE(ptr, type, size) + * :c:macro:`PyMem_Resize` + * * .. c:macro:: PyMem_FREE(ptr) + * :c:func:`PyMem_Free` + * * .. c:macro:: PyMem_DEL(ptr) + * :c:func:`PyMem_Free` + +.. versionchanged:: 3.4 + + The macros are now aliases of the corresponding functions and macros. + Previously, their behavior was the same, but their use did not necessarily + preserve binary compatibility across Python versions. + +.. deprecated:: 2.0 .. _objectinterface: @@ -322,7 +348,9 @@ memory from the Python heap. the :ref:`Customize Memory Allocators ` section. The :ref:`default object allocator ` uses the -:ref:`pymalloc memory allocator `. +:ref:`pymalloc memory allocator `. In the +:term:`free-threaded ` build, the default is the +:ref:`mimalloc memory allocator ` instead. .. warning:: @@ -340,7 +368,7 @@ The :ref:`default object allocator ` uses the .. c:function:: void* PyObject_Calloc(size_t nelem, size_t elsize) - Allocates *nelem* elements each whose size in bytes is *elsize* and returns + Allocates *nelem* elements each of size *elsize* bytes and returns a pointer of type :c:expr:`void*` to the allocated memory, or ``NULL`` if the request fails. The memory is initialized to zeros. @@ -402,14 +430,16 @@ Default Memory Allocators Default memory allocators: -=============================== ==================== ================== ===================== ==================== -Configuration Name PyMem_RawMalloc PyMem_Malloc PyObject_Malloc -=============================== ==================== ================== ===================== ==================== -Release build ``"pymalloc"`` ``malloc`` ``pymalloc`` ``pymalloc`` -Debug build ``"pymalloc_debug"`` ``malloc`` + debug ``pymalloc`` + debug ``pymalloc`` + debug -Release build, without pymalloc ``"malloc"`` ``malloc`` ``malloc`` ``malloc`` -Debug build, without pymalloc ``"malloc_debug"`` ``malloc`` + debug ``malloc`` + debug ``malloc`` + debug -=============================== ==================== ================== ===================== ==================== +=================================== ======================= ==================== ====================== ====================== +Configuration Name PyMem_RawMalloc PyMem_Malloc PyObject_Malloc +=================================== ======================= ==================== ====================== ====================== +Release build ``"pymalloc"`` ``malloc`` ``pymalloc`` ``pymalloc`` +Debug build ``"pymalloc_debug"`` ``malloc`` + debug ``pymalloc`` + debug ``pymalloc`` + debug +Release build, without pymalloc ``"malloc"`` ``malloc`` ``malloc`` ``malloc`` +Debug build, without pymalloc ``"malloc_debug"`` ``malloc`` + debug ``malloc`` + debug ``malloc`` + debug +Free-threaded build ``"mimalloc"`` ``mimalloc`` ``mimalloc`` ``mimalloc`` +Free-threaded debug build ``"mimalloc_debug"`` ``mimalloc`` + debug ``mimalloc`` + debug ``mimalloc`` + debug +=================================== ======================= ==================== ====================== ====================== Legend: @@ -417,8 +447,7 @@ Legend: * ``malloc``: system allocators from the standard C library, C functions: :c:func:`malloc`, :c:func:`calloc`, :c:func:`realloc` and :c:func:`free`. * ``pymalloc``: :ref:`pymalloc memory allocator `. -* ``mimalloc``: :ref:`mimalloc memory allocator `. The pymalloc - allocator will be used if mimalloc support isn't available. +* ``mimalloc``: :ref:`mimalloc memory allocator `. * "+ debug": with :ref:`debug hooks on the Python memory allocators `. * "Debug build": :ref:`Python build in debug mode `. @@ -655,7 +684,11 @@ The pymalloc allocator Python has a *pymalloc* allocator optimized for small objects (smaller or equal to 512 bytes) with a short lifetime. It uses memory mappings called "arenas" with a fixed size of either 256 KiB on 32-bit platforms or 1 MiB on 64-bit -platforms. It falls back to :c:func:`PyMem_RawMalloc` and +platforms. When Python is configured with :option:`--with-pymalloc-hugepages`, +the arena size on 64-bit platforms is increased to 2 MiB to match the huge page +size, and arena allocation will attempt to use huge pages (``MAP_HUGETLB`` on +Linux, ``MEM_LARGE_PAGES`` on Windows) with automatic fallback to regular pages. +It falls back to :c:func:`PyMem_RawMalloc` and :c:func:`PyMem_RawRealloc` for allocations larger than 512 bytes. *pymalloc* is the :ref:`default allocator ` of the @@ -711,9 +744,27 @@ The mimalloc allocator .. versionadded:: 3.13 -Python supports the mimalloc allocator when the underlying platform support is available. -mimalloc "is a general purpose allocator with excellent performance characteristics. -Initially developed by Daan Leijen for the runtime systems of the Koka and Lean languages." +Python supports the `mimalloc `__ +allocator when the underlying platform support is available. +mimalloc is a general purpose allocator with excellent performance +characteristics, initially developed by Daan Leijen for the runtime systems +of the Koka and Lean languages. + +Unlike :ref:`pymalloc `, which is optimized for small objects (512 +bytes or fewer), mimalloc handles allocations of any size. + +In the :term:`free-threaded ` build, mimalloc is the default +and **required** allocator for the :c:macro:`PYMEM_DOMAIN_MEM` and +:c:macro:`PYMEM_DOMAIN_OBJ` domains. It cannot be disabled in free-threaded +builds. The free-threaded build uses per-thread mimalloc heaps, which allows +allocation and deallocation to proceed without locking in most cases. + +In the default (non-free-threaded) build, mimalloc is available but not the +default allocator. It can be selected at runtime using +:envvar:`PYTHONMALLOC`\ ``=mimalloc`` (or ``mimalloc_debug`` to include +:ref:`debug hooks `). It can be disabled at build time +using the :option:`--without-mimalloc` configure option, but this option +cannot be combined with :option:`--disable-gil`. tracemalloc C API ================= diff --git a/Doc/c-api/memoryview.rst b/Doc/c-api/memoryview.rst index f6038032805259..e4ac8b57673407 100644 --- a/Doc/c-api/memoryview.rst +++ b/Doc/c-api/memoryview.rst @@ -13,6 +13,12 @@ A :class:`memoryview` object exposes the C level :ref:`buffer interface any other object. +.. c:var:: PyTypeObject PyMemoryView_Type + + This instance of :c:type:`PyTypeObject` represents the Python memoryview + type. This is the same object as :class:`memoryview` in the Python layer. + + .. c:function:: PyObject *PyMemoryView_FromObject(PyObject *obj) Create a memoryview object from an object that provides the buffer interface. diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index c8edcecc5b419f..9f68abba66bc5d 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -3,17 +3,16 @@ .. _moduleobjects: Module Objects --------------- +============== .. index:: pair: object; module - .. c:var:: PyTypeObject PyModule_Type .. index:: single: ModuleType (in module types) This instance of :c:type:`PyTypeObject` represents the Python module type. This - is exposed to Python programs as ``types.ModuleType``. + is exposed to Python programs as :py:class:`types.ModuleType`. .. c:function:: int PyModule_Check(PyObject *p) @@ -71,6 +70,9 @@ Module Objects ``PyObject_*`` functions rather than directly manipulate a module's :attr:`~object.__dict__`. + The returned reference is borrowed from the module; it is valid until + the module is destroyed. + .. c:function:: PyObject* PyModule_GetNameObject(PyObject *module) @@ -90,18 +92,19 @@ Module Objects Similar to :c:func:`PyModule_GetNameObject` but return the name encoded to ``'utf-8'``. -.. c:function:: void* PyModule_GetState(PyObject *module) - - Return the "state" of the module, that is, a pointer to the block of memory - allocated at module creation time, or ``NULL``. See - :c:member:`PyModuleDef.m_size`. - + The returned buffer is only valid until the module is renamed or destroyed. + Note that Python code may rename a module by setting its :py:attr:`~module.__name__` + attribute. .. c:function:: PyModuleDef* PyModule_GetDef(PyObject *module) Return a pointer to the :c:type:`PyModuleDef` struct from which the module was created, or ``NULL`` if the module wasn't created from a definition. + On error, return ``NULL`` with an exception set. + Use :c:func:`PyErr_Occurred` to tell this case apart from a missing + :c:type:`!PyModuleDef`. + .. c:function:: PyObject* PyModule_GetFilenameObject(PyObject *module) @@ -122,185 +125,200 @@ Module Objects Similar to :c:func:`PyModule_GetFilenameObject` but return the filename encoded to 'utf-8'. + The returned buffer is only valid until the module's :py:attr:`~module.__file__` attribute + is reassigned or the module is destroyed. + .. deprecated:: 3.2 :c:func:`PyModule_GetFilename` raises :exc:`UnicodeEncodeError` on unencodable filenames, use :c:func:`PyModule_GetFilenameObject` instead. -.. _pymoduledef: +.. _c_module_slots: +.. _pymoduledef_slot: -Module definitions ------------------- +Module definition +----------------- -The functions in the previous section work on any module object, including -modules imported from Python code. +Modules created using the C API are typically defined using an +array of :c:type:`PySlot` structs, which provides a "description" of how a +module should be created. +See :ref:`capi-slots` for more information on slots in general. -Modules defined using the C API typically use a *module definition*, -:c:type:`PyModuleDef` -- a statically allocated, constant “description" of -how a module should be created. +.. versionchanged:: 3.15 -The definition is usually used to define an extension's “main” module object -(see :ref:`extension-modules` for details). -It is also used to -:ref:`create extension modules dynamically `. + Previously, a :c:type:`PyModuleDef` struct was necessary to define modules. + The older way of defining modules is still available: consult either the + :ref:`pymoduledef` section or earlier versions of this documentation + if you plan to support earlier Python versions. -Unlike :c:func:`PyModule_New`, the definition allows management of -*module state* -- a piece of memory that is allocated and cleared together -with the module object. -Unlike the module's Python attributes, Python code cannot replace or delete -data stored in module state. +The slots array is usually used to define an extension module's “main” +module object (see :ref:`extension-modules` for details). +It can also be used to +:ref:`create extension modules dynamically `. -.. c:type:: PyModuleDef +Unless specified otherwise, the same slot ID may not be repeated +in an array of slots. - The module definition struct, which holds all information needed to create - a module object. - This structure must be statically allocated (or be otherwise guaranteed - to be valid while any modules created from it exist). - Usually, there is only one variable of this type for each extension module. - .. c:member:: PyModuleDef_Base m_base +Metadata slots +.............. - Always initialize this member to :c:macro:`PyModuleDef_HEAD_INIT`. +.. c:macro:: Py_mod_name - .. c:member:: const char *m_name + :c:member:`Slot ID ` for the name of the new module, + as a NUL-terminated UTF8-encoded ``const char *``. - Name for the new module. + Note that modules are typically created using a + :py:class:`~importlib.machinery.ModuleSpec`, and when they are, the + name from the spec will be used instead of :c:data:`!Py_mod_name`. + However, it is still recommended to include this slot for introspection + and debugging purposes. - .. c:member:: const char *m_doc + .. versionadded:: 3.15 - Docstring for the module; usually a docstring variable created with - :c:macro:`PyDoc_STRVAR` is used. + Use :c:member:`PyModuleDef.m_name` instead to support previous versions. - .. c:member:: Py_ssize_t m_size +.. c:macro:: Py_mod_doc - Module state may be kept in a per-module memory area that can be - retrieved with :c:func:`PyModule_GetState`, rather than in static globals. - This makes modules safe for use in multiple sub-interpreters. + :c:type:`Slot ID ` for the docstring of the new + module, as a NUL-terminated UTF8-encoded ``const char *``. - This memory area is allocated based on *m_size* on module creation, - and freed when the module object is deallocated, after the - :c:member:`~PyModuleDef.m_free` function has been called, if present. + Usually it is set to a variable created with :c:macro:`PyDoc_STRVAR`. - Setting it to a non-negative value means that the module can be - re-initialized and specifies the additional amount of memory it requires - for its state. + .. versionadded:: 3.15 - Setting ``m_size`` to ``-1`` means that the module does not support - sub-interpreters, because it has global state. - Negative ``m_size`` is only allowed when using - :ref:`legacy single-phase initialization ` - or when :ref:`creating modules dynamically `. + Use :c:member:`PyModuleDef.m_doc` instead to support previous versions. - See :PEP:`3121` for more details. - .. c:member:: PyMethodDef* m_methods +Feature slots +............. - A pointer to a table of module-level functions, described by - :c:type:`PyMethodDef` values. Can be ``NULL`` if no functions are present. +.. c:macro:: Py_mod_abi - .. c:member:: PyModuleDef_Slot* m_slots + :c:member:`Slot ID ` whose value points to + a :c:struct:`PyABIInfo` structure describing the ABI that + the extension is using. - An array of slot definitions for multi-phase initialization, terminated by - a ``{0, NULL}`` entry. - When using legacy single-phase initialization, *m_slots* must be ``NULL``. + A suitable :c:struct:`!PyABIInfo` variable can be defined using the + :c:macro:`PyABIInfo_VAR` macro, as in: - .. versionchanged:: 3.5 + .. code-block:: c - Prior to version 3.5, this member was always set to ``NULL``, - and was defined as: + PyABIInfo_VAR(abi_info); - .. c:member:: inquiry m_reload + static PySlot mymodule_slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + ... + }; - .. c:member:: traverseproc m_traverse + When creating a module, Python checks the value of this slot + using :c:func:`PyABIInfo_Check`. - A traversal function to call during GC traversal of the module object, or - ``NULL`` if not needed. + This slot is required, except for modules created from + :c:struct:`PyModuleDef`. - This function is not called if the module state was requested but is not - allocated yet. This is the case immediately after the module is created - and before the module is executed (:c:data:`Py_mod_exec` function). More - precisely, this function is not called if :c:member:`~PyModuleDef.m_size` is greater - than 0 and the module state (as returned by :c:func:`PyModule_GetState`) - is ``NULL``. + .. versionadded:: 3.15 - .. versionchanged:: 3.9 - No longer called before the module state is allocated. +.. c:macro:: Py_mod_multiple_interpreters - .. c:member:: inquiry m_clear + :c:member:`Slot ID ` whose value is one of: - A clear function to call during GC clearing of the module object, or - ``NULL`` if not needed. + .. c:namespace:: NULL - This function is not called if the module state was requested but is not - allocated yet. This is the case immediately after the module is created - and before the module is executed (:c:data:`Py_mod_exec` function). More - precisely, this function is not called if :c:member:`~PyModuleDef.m_size` is greater - than 0 and the module state (as returned by :c:func:`PyModule_GetState`) - is ``NULL``. + .. c:macro:: Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED - Like :c:member:`PyTypeObject.tp_clear`, this function is not *always* - called before a module 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:`~PyModuleDef.m_free` is called directly. + The module does not support being imported in subinterpreters. - .. versionchanged:: 3.9 - No longer called before the module state is allocated. + .. c:macro:: Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED + + The module supports being imported in subinterpreters, + but only when they share the main interpreter's GIL. + (See :ref:`isolating-extensions-howto`.) - .. c:member:: freefunc m_free + .. c:macro:: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED - A function to call during deallocation of the module object, or ``NULL`` - if not needed. + The module supports being imported in subinterpreters, + even when they have their own GIL. + (See :ref:`isolating-extensions-howto`.) - This function is not called if the module state was requested but is not - allocated yet. This is the case immediately after the module is created - and before the module is executed (:c:data:`Py_mod_exec` function). More - precisely, this function is not called if :c:member:`~PyModuleDef.m_size` is greater - than 0 and the module state (as returned by :c:func:`PyModule_GetState`) - is ``NULL``. + This slot determines whether or not importing this module + in a subinterpreter will fail. - .. versionchanged:: 3.9 - No longer called before the module state is allocated. + If ``Py_mod_multiple_interpreters`` is not specified, the import + machinery defaults to ``Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED``. + For historical reasons, the values are declared as pointers (``void *``). + When using :c:type:`PySlot` arrays, use :c:macro:`PySlot_DATA` for + :c:macro:`!Py_mod_multiple_interpreters`: -Module slots -............ + .. code-block:: c -.. c:type:: PyModuleDef_Slot + PySlot_DATA(Py_mod_multiple_interpreters, + Py_MOD_PER_INTERPRETER_GIL_SUPPORTED) - .. c:member:: int slot + .. versionadded:: 3.12 - A slot ID, chosen from the available values explained below. +.. c:macro:: Py_mod_gil - .. c:member:: void* value + :c:member:`Slot ID ` whose value is one of: - Value of the slot, whose meaning depends on the slot ID. + .. c:namespace:: NULL - .. versionadded:: 3.5 + .. c:macro:: Py_MOD_GIL_USED + + The module depends on the presence of the global interpreter lock (GIL), + and may access global state without synchronization. + + .. c:macro:: Py_MOD_GIL_NOT_USED + + The module is safe to run without an active GIL. + + This slot is ignored by Python builds not configured with + :option:`--disable-gil`. Otherwise, it determines whether or not importing + this module will cause the GIL to be automatically enabled. See + :ref:`whatsnew313-free-threaded-cpython` for more detail. + + If ``Py_mod_gil`` is not specified, the import machinery defaults to + ``Py_MOD_GIL_USED``. -The available slot types are: + For historical reasons, the values are declared as pointers (``void *``). + When using :c:type:`PySlot` arrays, use :c:macro:`PySlot_DATA` for + :c:macro:`!Py_mod_gil`: + + .. code-block:: c + + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED) + + .. versionadded:: 3.13 + + +Creation and initialization slots +................................. .. c:macro:: Py_mod_create - Specifies a function that is called to create the module object itself. - The *value* pointer of this slot must point to a function of the signature: + :c:member:`Slot ID ` for a function that creates + the module object itself. + The function must have the signature: .. c:function:: PyObject* create_module(PyObject *spec, PyModuleDef *def) :no-index-entry: :no-contents-entry: - The function receives a :py:class:`~importlib.machinery.ModuleSpec` - instance, as defined in :PEP:`451`, and the module definition. - It should return a new module object, or set an error + The function will be called with: + + - *spec*: a ``ModuleSpec``-like object, meaning that any attributes defined + for :py:class:`importlib.machinery.ModuleSpec` have matching semantics. + However, any of the attributes may be missing. + - *def*: ``NULL``, or the module definition if the module is created from one. + + The function should return a new module object, or set an error and return ``NULL``. This function should be kept minimal. In particular, it should not call arbitrary Python code, as trying to import the same module again may result in an infinite loop. - Multiple ``Py_mod_create`` slots may not be specified in one module - definition. - If ``Py_mod_create`` is not specified, the import machinery will create a normal module object using :c:func:`PyModule_New`. The name is taken from *spec*, not the definition, to allow extension modules to dynamically adjust @@ -308,96 +326,498 @@ The available slot types are: names through symlinks, all while sharing a single module definition. There is no requirement for the returned object to be an instance of - :c:type:`PyModule_Type`. Any type can be used, as long as it supports - setting and getting import-related attributes. - However, only ``PyModule_Type`` instances may be returned if the - ``PyModuleDef`` has non-``NULL`` ``m_traverse``, ``m_clear``, - ``m_free``; non-zero ``m_size``; or slots other than ``Py_mod_create``. + :c:type:`PyModule_Type`. + However, some slots may only be used with + :c:type:`!PyModule_Type` instances; in particular: + + - :c:macro:`Py_mod_exec`, + - :ref:`module state slots ` (``Py_mod_state_*``), + - :c:macro:`Py_mod_token`. + + .. versionadded:: 3.5 + + .. versionchanged:: 3.15 + + The *slots* argument may be a ``ModuleSpec``-like object, rather than + a true :py:class:`~importlib.machinery.ModuleSpec` instance. + Note that previous versions of CPython did not enforce this. + + The *def* argument may now be ``NULL``, since modules are not necessarily + made from definitions. .. c:macro:: Py_mod_exec - Specifies a function that is called to *execute* the module. - This is equivalent to executing the code of a Python module: typically, - this function adds classes and constants to the module. + :c:member:`Slot ID ` for a function that will + :dfn:`execute`, or initialize, the module. + This function does the equivalent to executing the code of a Python module: + typically, it adds classes and constants to the module. The signature of the function is: .. c:function:: int exec_module(PyObject* module) :no-index-entry: :no-contents-entry: - If multiple ``Py_mod_exec`` slots are specified, they are processed in the - order they appear in the *m_slots* array. + See the :ref:`capi-module-support-functions` section for some useful + functions to call. -.. c:macro:: Py_mod_multiple_interpreters + For backwards compatibility, the :c:type:`PyModuleDef.m_slots` array may + contain multiple :c:macro:`!Py_mod_exec` slots; these are processed in the + order they appear in the array. + Elsewhere (that is, in arguments to :c:func:`PyModule_FromSlotsAndSpec` + and in return values of :samp:`PyModExport_{}`), repeating the slot + is not allowed. - Specifies one of the following values: + .. versionadded:: 3.5 - .. c:namespace:: NULL + .. versionchanged:: 3.15 - .. c:macro:: Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED + Repeated ``Py_mod_exec`` slots are disallowed, except in + :c:type:`PyModuleDef.m_slots`. - The module does not support being imported in subinterpreters. +.. c:macro:: Py_mod_methods - .. c:macro:: Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED + :c:member:`Slot ID ` for a table of module-level + functions, as an array of :c:type:`PyMethodDef` values suitable as the + *functions* argument to :c:func:`PyModule_AddFunctions`. - The module supports being imported in subinterpreters, - but only when they share the main interpreter's GIL. - (See :ref:`isolating-extensions-howto`.) + Like other slot IDs, a slots array may only contain one + :c:macro:`!Py_mod_methods` entry. + To add functions from multiple :c:type:`PyMethodDef` arrays, call + :c:func:`PyModule_AddFunctions` in the :c:macro:`Py_mod_exec` function. - .. c:macro:: Py_MOD_PER_INTERPRETER_GIL_SUPPORTED + The table must be statically allocated (or otherwise guaranteed to outlive + the module object). - The module supports being imported in subinterpreters, - even when they have their own GIL. - (See :ref:`isolating-extensions-howto`.) + .. versionadded:: 3.15 - This slot determines whether or not importing this module - in a subinterpreter will fail. + Use :c:member:`PyModuleDef.m_methods` instead to support previous versions. - Multiple ``Py_mod_multiple_interpreters`` slots may not be specified - in one module definition. +.. _ext-module-state: - If ``Py_mod_multiple_interpreters`` is not specified, the import - machinery defaults to ``Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED``. +Module state +------------ - .. versionadded:: 3.12 +Extension modules can have *module state* -- a +piece of memory that is allocated on module creation, +and freed when the module object is deallocated. +The module state is specified using :ref:`dedicated slots `. -.. c:macro:: Py_mod_gil +A typical use of module state is storing an exception type -- or indeed *any* +type object defined by the module -- - Specifies one of the following values: +Unlike the module's Python attributes, Python code cannot replace or delete +data stored in module state. - .. c:namespace:: NULL +Keeping per-module information in attributes and module state, rather than in +static globals, makes module objects *isolated* and safer for use in +multiple sub-interpreters. +It also helps Python do an orderly clean-up when it shuts down. - .. c:macro:: Py_MOD_GIL_USED +Extensions that keep references to Python objects as part of module state must +implement :c:macro:`Py_mod_state_traverse` and :c:macro:`Py_mod_state_clear` +functions to avoid reference leaks. - The module depends on the presence of the global interpreter lock (GIL), - and may access global state without synchronization. +To retrieve the state from a given module, use the following functions: - .. c:macro:: Py_MOD_GIL_NOT_USED +.. c:function:: void* PyModule_GetState(PyObject *module) - The module is safe to run without an active GIL. + Return the "state" of the module, that is, a pointer to the block of memory + allocated at module creation time, or ``NULL``. See + :c:macro:`Py_mod_state_size`. - This slot is ignored by Python builds not configured with - :option:`--disable-gil`. Otherwise, it determines whether or not importing - this module will cause the GIL to be automatically enabled. See - :ref:`whatsnew313-free-threaded-cpython` for more detail. + On error, return ``NULL`` with an exception set. + Use :c:func:`PyErr_Occurred` to tell this case apart from missing + module state. - Multiple ``Py_mod_gil`` slots may not be specified in one module definition. - If ``Py_mod_gil`` is not specified, the import machinery defaults to - ``Py_MOD_GIL_USED``. +.. c:function:: int PyModule_GetStateSize(PyObject *module, Py_ssize_t *result) - .. versionadded:: 3.13 + Set *\*result* to the size of *module*'s state, as specified + using :c:macro:`Py_mod_state_size` (or :c:member:`PyModuleDef.m_size`), + and return 0. + On error, set *\*result* to -1, and return -1 with an exception set. -.. _moduledef-dynamic: + .. versionadded:: 3.15 + + + +.. _ext-module-state-slots: + +Slots for defining module state +............................... + +The following :c:member:`slot IDs ` are available for +defining the module state. + +.. c:macro:: Py_mod_state_size + + :c:member:`Slot ID ` for the size of the module state, + in bytes. + + Setting the value to a non-negative value means that the module can be + re-initialized and specifies the additional amount of memory it requires + for its state. + + See :PEP:`3121` for more details. + + Use :c:func:`PyModule_GetStateSize` to retrieve the size of a given module. + + .. versionadded:: 3.15 + + Use :c:member:`PyModuleDef.m_size` instead to support previous versions. + +.. c:macro:: Py_mod_state_traverse + + :c:member:`Slot ID ` for a traversal function to call + during GC traversal of the module object. + + The signature of the function, and meanings of the arguments, + is similar as for :c:member:`PyTypeObject.tp_traverse`: + + .. c:function:: int traverse_module_state(PyObject *module, visitproc visit, void *arg) + :no-index-entry: + :no-contents-entry: + + This function is not called if the module state was requested but is not + allocated yet. This is the case immediately after the module is created + and before the module is executed (:c:data:`Py_mod_exec` function). More + precisely, this function is not called if the state size + (:c:data:`Py_mod_state_size`) is greater than 0 and the module state + (as returned by :c:func:`PyModule_GetState`) is ``NULL``. + + .. versionadded:: 3.15 + + Use :c:member:`PyModuleDef.m_size` instead to support previous versions. + +.. c:macro:: Py_mod_state_clear + + :c:member:`Slot ID ` for a clear function to call + during GC clearing of the module object. + + The signature of the function is: + + .. c:function:: int clear_module_state(PyObject* module) + :no-index-entry: + :no-contents-entry: + + This function is not called if the module state was requested but is not + allocated yet. This is the case immediately after the module is created + and before the module is executed (:c:data:`Py_mod_exec` function). More + precisely, this function is not called if the state size + (:c:data:`Py_mod_state_size`) is greater than 0 and the module state + (as returned by :c:func:`PyModule_GetState`) is ``NULL``. + + Like :c:member:`PyTypeObject.tp_clear`, this function is not *always* + called before a module 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 + the :c:macro:`Py_mod_state_free` function is called directly. + + .. versionadded:: 3.15 + + Use :c:member:`PyModuleDef.m_clear` instead to support previous versions. + +.. c:macro:: Py_mod_state_free + + :c:member:`Slot ID ` for a function to call during + deallocation of the module object. + + The signature of the function is: + + .. c:function:: int free_module_state(PyObject* module) + :no-index-entry: + :no-contents-entry: + + This function is not called if the module state was requested but is not + allocated yet. This is the case immediately after the module is created + and before the module is executed (:c:data:`Py_mod_exec` function). More + precisely, this function is not called if the state size + (:c:data:`Py_mod_state_size`) is greater than 0 and the module state + (as returned by :c:func:`PyModule_GetState`) is ``NULL``. + + .. versionadded:: 3.15 + + Use :c:member:`PyModuleDef.m_free` instead to support previous versions. + + +.. _ext-module-token: + +Module token +............ + +Each module may have an associated *token*: a pointer-sized value intended to +identify of the module state's memory layout. +This means that if you have a module object, but you are not sure if it +“belongs” to your extension, you can check using code like this: + +.. code-block:: c + + PyObject *module = + + void *module_token; + if (PyModule_GetToken(module, &module_token) < 0) { + return NULL; + } + if (module_token != your_token) { + PyErr_SetString(PyExc_ValueError, "unexpected module") + return NULL; + } + + // This module's state has the expected memory layout; it's safe to cast + struct my_state state = (struct my_state*)PyModule_GetState(module) + +A module's token -- and the *your_token* value to use in the above code -- is: + +- For modules created with :c:type:`PyModuleDef`: the address of that + :c:type:`PyModuleDef`; +- For modules defined with the :c:macro:`Py_mod_token` slot: the value + of that slot; +- For modules created from an ``PyModExport_*`` + :ref:`export hook `: the slots array that the export + hook returned (unless overridden with :c:macro:`Py_mod_token`). + +.. c:macro:: Py_mod_token + + :c:member:`Slot ID ` for the module token. + + If you use this slot to set the module token (rather than rely on the + default), you must ensure that: + + * The pointer outlives the class, so it's not reused for something else + while the class exists. + * It "belongs" to the extension module where the class lives, so it will not + clash with other extensions. + * If the token points to a :c:type:`PyModuleDef` struct, the module should + behave as if it was created from that :c:type:`PyModuleDef`. + In particular, the module state must have matching layout and semantics. + + Modules created from :c:type:`PyModuleDef` always use the address of + the :c:type:`PyModuleDef` as the token. + This means that :c:macro:`!Py_mod_token` cannot be used in + :c:member:`PyModuleDef.m_slots`. + + .. versionadded:: 3.15 + +.. c:function:: int PyModule_GetToken(PyObject *module, void** result) + + Set *\*result* to the module token for *module* and return 0. + + On error, set *\*result* to NULL, and return -1 with an exception set. + + .. versionadded:: 3.15 + +See also :c:func:`PyType_GetModuleByToken`. + + +.. _module-from-slots: Creating extension modules dynamically -------------------------------------- -The following functions may be used to create a module outside of an -extension's :ref:`initialization function `. -They are also used in -:ref:`single-phase initialization `. +The following functions may be used to create an extension module dynamically, +rather than from an extension's :ref:`export hook `. + +.. c:function:: PyObject *PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec) + + Create a new module object, given an array of :ref:`slots ` + and the :py:class:`~importlib.machinery.ModuleSpec` *spec*. + + The *slots* argument must point to an array of :c:type:`PySlot` + structures, terminated by an entry with slot ID of 0 + (typically written as :c:macro:`PySlot_END`). + The array must include a :c:data:`Py_mod_abi` entry. + + The *spec* argument may be any ``ModuleSpec``-like object, as described + in :c:macro:`Py_mod_create` documentation. + Currently, the *spec* must have a ``name`` attribute. + + On success, return the new module. + On error, return ``NULL`` with an exception set. + + Note that this does not process the module's execution slot + (:c:data:`Py_mod_exec`). + Both :c:func:`!PyModule_FromSlotsAndSpec` and :c:func:`PyModule_Exec` + must be called to fully initialize a module. + (See also :ref:`multi-phase-initialization`.) + + .. versionadded:: 3.15 + +.. c:function:: int PyModule_Exec(PyObject *module) + + Execute the :c:data:`Py_mod_exec` slot(s) of *module*. + + On success, return 0. + On error, return -1 with an exception set. + + For clarity: If *module* has no slots, for example if it uses + :ref:`legacy single-phase initialization `, + this function does nothing and returns 0. + + .. versionadded:: 3.15 + + + +.. _pymoduledef: + +Module definition struct +------------------------ + +Traditionally, extension modules were defined using a *module definition* +as the “description" of how a module should be created. +Rather than using an array of :ref:`slots ` directly, +the definition has dedicated members for most common functionality, +and allows additional slots as an extension mechanism. + +This way of defining modules is still available and there are no plans to +remove it. + +.. c:type:: PyModuleDef + + The module definition struct, which holds information needed to create + a module object. + + This structure must be statically allocated (or be otherwise guaranteed + to be valid while any modules created from it exist). + Usually, there is only one variable of this type for each extension module + defined this way. + + The struct, including all members, is part of the + :ref:`Stable ABI ` for non-free-threaded builds (``abi3``). + In the Stable ABI for free-threaded builds (``abi3t``), + this struct is opaque, and unusable in practice; see :ref:`pymoduledef_slot` + for a replacement. + + .. c:member:: PyModuleDef_Base m_base + + Always initialize this member to :c:macro:`PyModuleDef_HEAD_INIT`: + + .. c:namespace:: NULL + + .. c:type:: PyModuleDef_Base + + The type of :c:member:`!PyModuleDef.m_base`. + + The struct is part of the :ref:`Stable ABI ` for + non-free-threaded builds (``abi3``). + In the Stable ABI for Free-Threaded Builds + (``abi3t``), this struct is opaque, and unusable in practice. + + .. c:macro:: PyModuleDef_HEAD_INIT + + The required initial value for :c:member:`!PyModuleDef.m_base`. + + .. c:member:: const char *m_name + + Corresponds to the :c:macro:`Py_mod_name` slot. + + .. c:member:: const char *m_doc + + These members correspond to the :c:macro:`Py_mod_doc` slot. + Setting this to NULL is equivalent to omitting the slot. + + .. c:member:: Py_ssize_t m_size + + Corresponds to the :c:macro:`Py_mod_state_size` slot. + Setting this to zero is equivalent to omitting the slot. + + When using :ref:`legacy single-phase initialization ` + or when creating modules dynamically using :c:func:`PyModule_Create` + or :c:func:`PyModule_Create2`, :c:member:`!m_size` may be set to -1. + This indicates that the module does not support sub-interpreters, + because it has global state. + + .. c:member:: PyMethodDef *m_methods + + Corresponds to the :c:macro:`Py_mod_methods` slot. + Setting this to NULL is equivalent to omitting the slot. + + .. c:member:: PyModuleDef_Slot* m_slots + + An array of additional slots, terminated by a ``{0, NULL}`` entry. + Note that the entries use the older :c:type:`PyModuleDef_Slot` structure, + rather than :c:type:`PySlot`. + + If the array contains slots corresponding to :c:type:`PyModuleDef` + members, the values must match. + For example, if you use :c:macro:`Py_mod_name` in :c:member:`!m_slots`, + :c:member:`PyModuleDef.m_name` must be set to the same pointer + (not just an equal string). + + .. versionchanged:: 3.5 + + Prior to version 3.5, this member was always set to ``NULL``, + and was defined as: + + .. c:member:: inquiry m_reload + + .. c:namespace:: NULL + + .. c:type:: PyModuleDef_Slot + + Older structure defining additional slots of a module. + + Note that a :c:type:`!PyModuleDef_Slot` array may be included in a + :c:type:`!PySlot` array using :c:macro:`Py_mod_slots`, + and vice versa using :c:macro:`Py_slot_subslots`. + + Each :c:type:`!PyModuleDef_Slot` structure ``modslot`` is interpreted + as the following :c:type:`PySlot` structure:: + + (PySlot){ + .sl_id=modslot.slot, + .sl_flags=PySlot_INTPTR | sub_static, + .sl_ptr=modslot.value + } + + where ``sub_static`` is ``PySlot_STATIC`` if the slot requires + the flag (such as for :c:macro:`Py_mod_methods`), or if this flag + is present on the "parent" :c:macro:`!Py_mod_slots` slot (if any). + + .. c:member:: int slot + + Corresponds to :c:member:`PySlot.sl_id`. + + .. c:member:: void* value + + Corresponds to :c:member:`PySlot.sl_ptr`. + + .. versionadded:: 3.5 + + .. c:member:: traverseproc m_traverse + inquiry m_clear + freefunc m_free + + These members correspond to the :c:macro:`Py_mod_state_traverse`, + :c:macro:`Py_mod_state_clear`, and :c:macro:`Py_mod_state_free` slots, + respectively. + + Setting these members to NULL is equivalent to omitting the + corresponding slots. + + .. versionchanged:: 3.9 + + :c:member:`m_traverse`, :c:member:`m_clear` and :c:member:`m_free` + functions are no longer called before the module state is allocated. + + +.. c:var:: PyTypeObject PyModuleDef_Type + + The type of ``PyModuleDef`` objects. + + +.. c:macro:: Py_mod_slots + + :c:member:`Slot ID ` that works like + :c:macro:`Py_slot_subslots`, except it specifies an array of + :c:type:`PyModuleDef_Slot` structures. + + .. versionadded:: 3.15 + +.. _moduledef-dynamic: + +The following API can be used to create modules from a :c:type:`!PyModuleDef` +struct: .. c:function:: PyObject* PyModule_Create(PyModuleDef *def) @@ -433,6 +853,10 @@ They are also used in .. versionadded:: 3.5 + .. soft-deprecated:: next + + Prefer :c:func:`PyModule_FromSlotsAndSpec` in new code. + .. c:function:: PyObject * PyModule_FromDefAndSpec2(PyModuleDef *def, PyObject *spec, int module_api_version) Create a new module object, given the definition in *def* and the @@ -453,33 +877,47 @@ They are also used in .. versionadded:: 3.5 + .. soft-deprecated:: next + + Prefer :c:func:`PyModule_FromSlotsAndSpec` in new code. + .. c:function:: int PyModule_ExecDef(PyObject *module, PyModuleDef *def) Process any execution slots (:c:data:`Py_mod_exec`) given in *def*. .. versionadded:: 3.5 + .. soft-deprecated:: next + + To run a module's own execution slots, prefer :c:func:`PyModule_Exec`, + which works on modules that were not created from a + :c:type:`PyModuleDef` structure. + .. c:macro:: PYTHON_API_VERSION + PYTHON_API_STRING - The C API version. Defined for backwards compatibility. + The C API version, as an integer (``1013``) and string (``"1013"``), respectively. + Defined for backwards compatibility. Currently, this constant is not updated in new Python versions, and is not useful for versioning. This may change in the future. .. c:macro:: PYTHON_ABI_VERSION + PYTHON_ABI_STRING - Defined as ``3`` for backwards compatibility. + Defined as ``3`` and ``"3"``, respectively, for backwards compatibility. Currently, this constant is not updated in new Python versions, and is not useful for versioning. This may change in the future. +.. _capi-module-support-functions: + Support functions ----------------- -The following functions are provided to help initialize a module -state. -They are intended for a module's execution slots (:c:data:`Py_mod_exec`), +The following functions are provided to help initialize a module object. +They are intended for a module's execution slot (:c:data:`Py_mod_exec`), the initialization function for legacy :ref:`single-phase initialization `, or code that creates modules dynamically. @@ -581,9 +1019,7 @@ or code that creates modules dynamically. // PyModule_AddObject() stole a reference to obj: // Py_XDECREF(obj) is not needed here. - .. deprecated:: 3.13 - - :c:func:`PyModule_AddObject` is :term:`soft deprecated`. + .. soft-deprecated:: 3.13 .. c:function:: int PyModule_AddIntConstant(PyObject *module, const char *name, long value) @@ -645,6 +1081,9 @@ or code that creates modules dynamically. :c:type:`PyMethodDef` arrays; in that case they should call this function directly. + The *functions* array must be statically allocated (or otherwise guaranteed + to outlive the module object). + .. versionadded:: 3.5 .. c:function:: int PyModule_SetDocString(PyObject *module, const char *docstring) @@ -654,6 +1093,9 @@ or code that creates modules dynamically. ``PyModuleDef`` (such as when using :ref:`multi-phase-initialization`, ``PyModule_Create``, or ``PyModule_FromDefAndSpec``). + Return ``0`` on success. + Return ``-1`` with an exception set on error. + .. versionadded:: 3.5 .. c:function:: int PyUnstable_Module_SetGIL(PyObject *module, void *gil) diff --git a/Doc/c-api/monitoring.rst b/Doc/c-api/monitoring.rst index 7926148302af0b..4bfcb86abf58ed 100644 --- a/Doc/c-api/monitoring.rst +++ b/Doc/c-api/monitoring.rst @@ -136,7 +136,7 @@ Managing the Monitoring State ----------------------------- Monitoring states can be managed with the help of monitoring scopes. A scope -would typically correspond to a python function. +would typically correspond to a Python function. .. c:function:: int PyMonitoring_EnterScope(PyMonitoringState *state_array, uint64_t *version, const uint8_t *event_types, Py_ssize_t length) @@ -205,6 +205,4 @@ would typically correspond to a python function. .. versionadded:: 3.13 - .. deprecated:: 3.14 - - This function is :term:`soft deprecated`. + .. soft-deprecated:: 3.14 diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 55f0d0f9fb7ff8..eedeb180c6b760 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -73,7 +73,7 @@ Object Protocol Flag to be used with multiple functions that print the object (like :c:func:`PyObject_Print` and :c:func:`PyFile_WriteObject`). - If passed, these function would use the :func:`str` of the object + If passed, these functions use the :func:`str` of the object instead of the :func:`repr`. @@ -85,6 +85,35 @@ Object Protocol instead of the :func:`repr`. +.. c:function:: void PyObject_Dump(PyObject *op) + + Dump an object *op* to ``stderr``. This should only be used for debugging. + + The output is intended to try dumping objects even after memory corruption: + + * Information is written starting with fields that are the least likely to + crash when accessed. + * This function can be called without an :term:`attached thread state`, but + it's not recommended to do so: it can cause deadlocks. + * An object that does not belong to the current interpreter may be dumped, + but this may also cause crashes or unintended behavior. + * Implement a heuristic to detect if the object memory has been freed. Don't + display the object contents in this case, only its memory address. + * The output format may change at any time. + + Example of output: + + .. code-block:: output + + object address : 0x7f80124702c0 + object refcount : 2 + object type : 0x9902e0 + object type name: str + object repr : 'abcdef' + + .. versionadded:: 3.15 + + .. c:function:: int PyObject_HasAttrWithError(PyObject *o, PyObject *attr_name) Returns ``1`` if *o* has the attribute *attr_name*, and ``0`` otherwise. @@ -197,11 +226,11 @@ Object Protocol in favour of using :c:func:`PyObject_DelAttr`, but there are currently no plans to remove it. - The function must not be called with ``NULL`` *v* and an an exception set. + The function must not be called with a ``NULL`` *v* and an exception set. This case can arise from forgetting ``NULL`` checks and would delete the attribute. - .. versionchanged:: next + .. versionchanged:: 3.15 Must not be called with NULL value if an exception is set. @@ -214,7 +243,7 @@ Object Protocol If *v* is ``NULL``, the attribute is deleted, but this feature is deprecated in favour of using :c:func:`PyObject_DelAttrString`. - The function must not be called with ``NULL`` *v* and an an exception set. + The function must not be called with a ``NULL`` *v* and an exception set. This case can arise from forgetting ``NULL`` checks and would delete the attribute. @@ -226,7 +255,7 @@ Object Protocol For more details, see :c:func:`PyUnicode_InternFromString`, which may be used internally to create a key object. - .. versionchanged:: next + .. versionchanged:: 3.15 Must not be called with NULL value if an exception is set. @@ -334,6 +363,8 @@ Object Protocol representation on success, ``NULL`` on failure. This is the equivalent of the Python expression ``repr(o)``. Called by the :func:`repr` built-in function. + If argument is ``NULL``, return the string ``''``. + .. versionchanged:: 3.4 This function now includes a debug assertion to help ensure that it does not silently discard an active exception. @@ -348,6 +379,8 @@ Object Protocol a string similar to that returned by :c:func:`PyObject_Repr` in Python 2. Called by the :func:`ascii` built-in function. + If argument is ``NULL``, return the string ``''``. + .. index:: string; PyObject_Str (C function) @@ -358,6 +391,8 @@ Object Protocol Python expression ``str(o)``. Called by the :func:`str` built-in function and, therefore, by the :func:`print` function. + If argument is ``NULL``, return the string ``''``. + .. versionchanged:: 3.4 This function now includes a debug assertion to help ensure that it does not silently discard an active exception. @@ -373,6 +408,8 @@ Object Protocol a TypeError is raised when *o* is an integer instead of a zero-initialized bytes object. + If argument is ``NULL``, return the :class:`bytes` object ``b''``. + .. c:function:: int PyObject_IsSubclass(PyObject *derived, PyObject *cls) @@ -600,7 +637,7 @@ Object Protocol Clear the managed dictionary of *obj*. - This function must only be called in a traverse function of the type which + This function must only be called in a clear function of the type which has the :c:macro:`Py_TPFLAGS_MANAGED_DICT` flag set. .. versionadded:: 3.13 @@ -682,10 +719,10 @@ Object Protocol :c:func:`PyUnstable_EnableTryIncRef` must have been called earlier on *obj* or this function may spuriously return ``0`` in the - :term:`free threading` build. + :term:`free-threaded build`. This function is logically equivalent to the following C code, except that - it behaves atomically in the :term:`free threading` build:: + it behaves atomically in the :term:`free-threaded build`:: if (Py_REFCNT(op) > 0) { Py_INCREF(op); @@ -762,13 +799,30 @@ Object Protocol On GIL-enabled builds, this function is equivalent to :c:expr:`Py_REFCNT(op) == 1`. - On a :term:`free threaded ` build, this checks if *op*'s + On a :term:`free-threaded 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. + 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 + +.. c:function:: int PyUnstable_SetImmortal(PyObject *op) + + Marks the object *op* :term:`immortal`. The argument should be uniquely referenced by + the calling thread. This is intended to be used for reducing reference counting contention + in the :term:`free-threaded build` for objects which are shared across threads. + + This is a one-way process: objects can only be made immortal; they cannot be + made mortal once again. Immortal objects do not participate in reference counting + and will never be garbage collected. If the object is GC-tracked, it is untracked. + + This function is intended to be used soon after *op* is created, by the code that + creates it, such as in the object's :c:member:`~PyTypeObject.tp_new` slot. + Returns 1 if the object was made immortal and returns 0 if it was not. + This function cannot fail. + + .. versionadded:: 3.15 diff --git a/Doc/c-api/perfmaps.rst b/Doc/c-api/perfmaps.rst index 76a1e9f528dc70..a962c4ee09ad77 100644 --- a/Doc/c-api/perfmaps.rst +++ b/Doc/c-api/perfmaps.rst @@ -31,7 +31,7 @@ Note that holding an :term:`attached thread state` is not required for these API or ``-2`` on failure to create a lock. Check ``errno`` for more information about the cause of a failure. -.. c:function:: int PyUnstable_WritePerfMapEntry(const void *code_addr, unsigned int code_size, const char *entry_name) +.. c:function:: int PyUnstable_WritePerfMapEntry(const void *code_addr, size_t code_size, const char *entry_name) Write one single entry to the ``/tmp/perf-$pid.map`` file. This function is thread safe. Here is what an example entry looks like:: @@ -49,3 +49,43 @@ Note that holding an :term:`attached thread state` is not required for these API This is called by the runtime itself during interpreter shut-down. In general, there shouldn't be a reason to explicitly call this, except to handle specific scenarios such as forking. + +.. c:function:: int PyUnstable_CopyPerfMapFile(const char *parent_filename) + + Open the ``/tmp/perf-$pid.map`` file and append the content of *parent_filename* + to it. + + This function is available on all platforms but only generates output on platforms + that support perf maps (currently only Linux). On other platforms, it does nothing. + + .. versionadded:: 3.13 + +.. c:function:: int PyUnstable_PerfTrampoline_CompileCode(PyCodeObject *code) + + Compile the given code object using the current perf trampoline. + + The "current" trampoline is the one set by the runtime or the most recent + :c:func:`PyUnstable_PerfTrampoline_SetPersistAfterFork` call. + + If no trampoline is set, falls back to normal compilation (no perf map entry). + + :param code: The code object to compile. + :return: 0 on success, -1 on failure. + + .. versionadded:: 3.13 + +.. c:function:: int PyUnstable_PerfTrampoline_SetPersistAfterFork(int enable) + + Set whether the perf trampoline should persist after a fork. + + * If ``enable`` is true (non-zero): perf map file remains open/valid post-fork. + Child process inherits all existing perf map entries. + * If ``enable`` is false (zero): perf map closes post-fork. + Child process gets empty perf map. + + Default: false (clears on fork). + + :param enable: 1 to enable, 0 to disable. + :return: 0 on success, -1 on failure. + + .. versionadded:: 3.13 diff --git a/Doc/c-api/picklebuffer.rst b/Doc/c-api/picklebuffer.rst new file mode 100644 index 00000000000000..9e2d92341b0f93 --- /dev/null +++ b/Doc/c-api/picklebuffer.rst @@ -0,0 +1,59 @@ +.. highlight:: c + +.. _picklebuffer-objects: + +.. index:: + pair: object; PickleBuffer + +Pickle buffer objects +--------------------- + +.. versionadded:: 3.8 + +A :class:`pickle.PickleBuffer` object wraps a :ref:`buffer-providing object +` for out-of-band data transfer with the :mod:`pickle` module. + + +.. c:var:: PyTypeObject PyPickleBuffer_Type + + This instance of :c:type:`PyTypeObject` represents the Python pickle buffer type. + This is the same object as :class:`pickle.PickleBuffer` in the Python layer. + + +.. c:function:: int PyPickleBuffer_Check(PyObject *op) + + Return true if *op* is a pickle buffer instance. + This function always succeeds. + + +.. c:function:: PyObject *PyPickleBuffer_FromObject(PyObject *obj) + + Create a pickle buffer from the object *obj*. + + This function will fail if *obj* doesn't support the :ref:`buffer protocol `. + + On success, return a new pickle buffer instance. + On failure, set an exception and return ``NULL``. + + Analogous to calling :class:`pickle.PickleBuffer` with *obj* in Python. + + +.. c:function:: const Py_buffer *PyPickleBuffer_GetBuffer(PyObject *picklebuf) + + Get a pointer to the underlying :c:type:`Py_buffer` that the pickle buffer wraps. + + The returned pointer is valid as long as *picklebuf* is alive and has not been + released. The caller must not modify or free the returned :c:type:`Py_buffer`. + If the pickle buffer has been released, raise :exc:`ValueError`. + + On success, return a pointer to the buffer view. + On failure, set an exception and return ``NULL``. + + +.. c:function:: int PyPickleBuffer_Release(PyObject *picklebuf) + + Release the underlying buffer held by the pickle buffer. + + Return ``0`` on success. On failure, set an exception and return ``-1``. + + Analogous to calling :meth:`pickle.PickleBuffer.release` in Python. diff --git a/Doc/c-api/profiling.rst b/Doc/c-api/profiling.rst new file mode 100644 index 00000000000000..0200f2eac6d908 --- /dev/null +++ b/Doc/c-api/profiling.rst @@ -0,0 +1,239 @@ +.. highlight:: c + +.. _profiling: + +Profiling and tracing +===================== + +The Python interpreter provides some low-level support for attaching profiling +and execution tracing facilities. These are used for profiling, debugging, and +coverage analysis tools. + +This C interface allows the profiling or tracing code to avoid the overhead of +calling through Python-level callable objects, making a direct C function call +instead. The essential attributes of the facility have not changed; the +interface allows trace functions to be installed per-thread, and the basic +events reported to the trace function are the same as had been reported to the +Python-level trace functions in previous versions. + + +.. c:type:: int (*Py_tracefunc)(PyObject *obj, PyFrameObject *frame, int what, PyObject *arg) + + The type of the trace function registered using :c:func:`PyEval_SetProfile` and + :c:func:`PyEval_SetTrace`. The first parameter is the object passed to the + registration function as *obj*, *frame* is the frame object to which the event + pertains, *what* is one of the constants :c:data:`PyTrace_CALL`, + :c:data:`PyTrace_EXCEPTION`, :c:data:`PyTrace_LINE`, :c:data:`PyTrace_RETURN`, + :c:data:`PyTrace_C_CALL`, :c:data:`PyTrace_C_EXCEPTION`, :c:data:`PyTrace_C_RETURN`, + or :c:data:`PyTrace_OPCODE`, and *arg* depends on the value of *what*: + + +-------------------------------+----------------------------------------+ + | Value of *what* | Meaning of *arg* | + +===============================+========================================+ + | :c:data:`PyTrace_CALL` | Always :c:data:`Py_None`. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_EXCEPTION` | Exception information as returned by | + | | :func:`sys.exc_info`. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_LINE` | Always :c:data:`Py_None`. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_RETURN` | Value being returned to the caller, | + | | or ``NULL`` if caused by an exception. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_C_CALL` | Function object being called. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_C_EXCEPTION` | Function object being called. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_C_RETURN` | Function object being called. | + +-------------------------------+----------------------------------------+ + | :c:data:`PyTrace_OPCODE` | Always :c:data:`Py_None`. | + +-------------------------------+----------------------------------------+ + +.. c:var:: int PyTrace_CALL + + The value of the *what* parameter to a :c:type:`Py_tracefunc` function when a new + call to a function or method is being reported, or a new entry into a generator. + Note that the creation of the iterator for a generator function is not reported + as there is no control transfer to the Python bytecode in the corresponding + frame. + + +.. c:var:: int PyTrace_EXCEPTION + + The value of the *what* parameter to a :c:type:`Py_tracefunc` function when an + exception has been raised. The callback function is called with this value for + *what* when after any bytecode is processed after which the exception becomes + set within the frame being executed. The effect of this is that as exception + propagation causes the Python stack to unwind, the callback is called upon + return to each frame as the exception propagates. Only trace functions receive + these events; they are not needed by the profiler. + + +.. c:var:: int PyTrace_LINE + + The value passed as the *what* parameter to a :c:type:`Py_tracefunc` function + (but not a profiling function) when a line-number event is being reported. + It may be disabled for a frame by setting :attr:`~frame.f_trace_lines` to + *0* on that frame. + + +.. c:var:: int PyTrace_RETURN + + The value for the *what* parameter to :c:type:`Py_tracefunc` functions when a + call is about to return. + + +.. c:var:: int PyTrace_C_CALL + + The value for the *what* parameter to :c:type:`Py_tracefunc` functions when a C + function is about to be called. + + +.. c:var:: int PyTrace_C_EXCEPTION + + The value for the *what* parameter to :c:type:`Py_tracefunc` functions when a C + function has raised an exception. + + +.. c:var:: int PyTrace_C_RETURN + + The value for the *what* parameter to :c:type:`Py_tracefunc` functions when a C + function has returned. + + +.. c:var:: int PyTrace_OPCODE + + The value for the *what* parameter to :c:type:`Py_tracefunc` functions (but not + profiling functions) when a new opcode is about to be executed. This event is + not emitted by default: it must be explicitly requested by setting + :attr:`~frame.f_trace_opcodes` to *1* on the frame. + + +.. c:function:: void PyEval_SetProfile(Py_tracefunc func, PyObject *obj) + + Set the profiler function to *func*. The *obj* parameter is passed to the + function as its first parameter, and may be any Python object, or ``NULL``. If + the profile function needs to maintain state, using a different value for *obj* + for each thread provides a convenient and thread-safe place to store it. The + profile function is called for all monitored events except :c:data:`PyTrace_LINE` + :c:data:`PyTrace_OPCODE` and :c:data:`PyTrace_EXCEPTION`. + + See also the :func:`sys.setprofile` function. + + The caller must have an :term:`attached thread state`. + + +.. c:function:: void PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *obj) + + Like :c:func:`PyEval_SetProfile` but sets the profile function in all running threads + belonging to the current interpreter instead of the setting it only on the current thread. + + The caller must have an :term:`attached thread state`. + + As :c:func:`PyEval_SetProfile`, this function ignores any exceptions raised while + setting the profile functions in all threads. + +.. versionadded:: 3.12 + + +.. c:function:: void PyEval_SetTrace(Py_tracefunc func, PyObject *obj) + + Set the tracing function to *func*. This is similar to + :c:func:`PyEval_SetProfile`, except the tracing function does receive line-number + events and per-opcode events, but does not receive any event related to C function + objects being called. Any trace function registered using :c:func:`PyEval_SetTrace` + will not receive :c:data:`PyTrace_C_CALL`, :c:data:`PyTrace_C_EXCEPTION` or + :c:data:`PyTrace_C_RETURN` as a value for the *what* parameter. + + See also the :func:`sys.settrace` function. + + The caller must have an :term:`attached thread state`. + + +.. c:function:: void PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *obj) + + Like :c:func:`PyEval_SetTrace` but sets the tracing function in all running threads + belonging to the current interpreter instead of the setting it only on the current thread. + + The caller must have an :term:`attached thread state`. + + As :c:func:`PyEval_SetTrace`, this function ignores any exceptions raised while + setting the trace functions in all threads. + +.. versionadded:: 3.12 + + +Reference tracing +================= + +.. versionadded:: 3.13 + + +.. c:type:: int (*PyRefTracer)(PyObject *, int event, void* data) + + The type of the trace function registered using :c:func:`PyRefTracer_SetTracer`. + The first parameter is a Python object that has been just created (when **event** + is set to :c:data:`PyRefTracer_CREATE`) or about to be destroyed (when **event** + is set to :c:data:`PyRefTracer_DESTROY`). The **data** argument is the opaque pointer + that was provided when :c:func:`PyRefTracer_SetTracer` was called. + + If a new tracing function is registered replacing the current one, a call to the + trace function will be made with the object set to **NULL** and **event** set to + :c:data:`PyRefTracer_TRACKER_REMOVED`. This will happen just before the new + function is registered. + +.. versionadded:: 3.13 + + +.. c:var:: int PyRefTracer_CREATE + + The value for the *event* parameter to :c:type:`PyRefTracer` functions when a Python + object has been created. + + +.. c:var:: int PyRefTracer_DESTROY + + The value for the *event* parameter to :c:type:`PyRefTracer` functions when a Python + object has been destroyed. + + +.. c:var:: int PyRefTracer_TRACKER_REMOVED + + The value for the *event* parameter to :c:type:`PyRefTracer` functions when the + current tracer is about to be replaced by a new one. + + .. versionadded:: 3.14 + + +.. c:function:: int PyRefTracer_SetTracer(PyRefTracer tracer, void *data) + + Register a reference tracer function. The function will be called when a new + Python object has been created or when an object is going to be destroyed. If + **data** is provided it must be an opaque pointer that will be provided when + the tracer function is called. Return ``0`` on success. Set an exception and + return ``-1`` on error. + + Note that tracer functions **must not** create Python objects inside or + otherwise the call will be re-entrant. The tracer also **must not** clear + any existing exception or set an exception. A :term:`thread state` will be active + every time the tracer function is called. + + There must be an :term:`attached thread state` when calling this function. + + If another tracer function was already registered, the old function will be + called with **event** set to :c:data:`PyRefTracer_TRACKER_REMOVED` just before + the new function is registered. + +.. versionadded:: 3.13 + + +.. c:function:: PyRefTracer PyRefTracer_GetTracer(void** data) + + Get the registered reference tracer function and the value of the opaque data + pointer that was registered when :c:func:`PyRefTracer_SetTracer` was called. + If no tracer was registered this function will return NULL and will set the + **data** pointer to NULL. + + There must be an :term:`attached thread state` when calling this function. + +.. versionadded:: 3.13 diff --git a/Doc/c-api/refcounting.rst b/Doc/c-api/refcounting.rst index 57a0728d4e9af4..4d56a92bf2af79 100644 --- a/Doc/c-api/refcounting.rst +++ b/Doc/c-api/refcounting.rst @@ -25,7 +25,7 @@ of Python objects. .. note:: - On :term:`free threaded ` builds of Python, returning 1 + On :term:`free-threaded 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. diff --git a/Doc/c-api/sentinel.rst b/Doc/c-api/sentinel.rst new file mode 100644 index 00000000000000..b1b7329a5d42c5 --- /dev/null +++ b/Doc/c-api/sentinel.rst @@ -0,0 +1,47 @@ +.. highlight:: c + +.. _sentinelobjects: + +Sentinel objects +---------------- + +.. c:var:: PyTypeObject PySentinel_Type + + This instance of :c:type:`PyTypeObject` represents the Python + :class:`sentinel` type. This is the same object as :class:`sentinel`. + + .. versionadded:: 3.15 + +.. c:function:: int PySentinel_Check(PyObject *o) + + Return true if *o* is a :class:`sentinel` object or a subtype. + The :class:`sentinel` type does not currently allow subclasses, + so this check is exact. + Future Python versions may choose to allow subtyping. + This function always succeeds. + + .. versionadded:: 3.15 + +.. c:function:: int PySentinel_CheckExact(PyObject *o) + + Return true if *o* is a :class:`sentinel` object, but not a subtype. + The :class:`sentinel` type does not currently allow subclasses. + Future Python versions may choose to allow subtyping. + This function always succeeds. + + .. versionadded:: 3.15 + +.. c:function:: PyObject* PySentinel_New(const char *name, const char *module_name, const char *repr) + + Return a new :class:`sentinel` object with :attr:`~sentinel.__name__` set to + *name* and :attr:`~sentinel.__module__` set to *module_name*. + *name* must not be ``NULL``. If *module_name* is ``NULL``, :attr:`~sentinel.__module__` + is set to ``None``. If *repr* is ``NULL``, ``repr()`` returns :attr:`~sentinel.__name__`. + Return ``NULL`` with an exception set on failure. + + For pickling to work, *module_name* must be the name of an importable + module, and the sentinel must be accessible from that module under a + path matching *name*. Pickle treats *name* as a global variable name + in *module_name* (see :meth:`object.__reduce__`). + + .. versionadded:: 3.15 diff --git a/Doc/c-api/sequence.rst b/Doc/c-api/sequence.rst index df5bf6b64a93a0..6bae8f25ad75d1 100644 --- a/Doc/c-api/sequence.rst +++ b/Doc/c-api/sequence.rst @@ -109,9 +109,8 @@ Sequence Protocol 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. + .. soft-deprecated:: 3.14 + The function should no longer be used to write new code. .. c:function:: Py_ssize_t PySequence_Index(PyObject *o, PyObject *value) diff --git a/Doc/c-api/set.rst b/Doc/c-api/set.rst index cba823aa027bd6..db537aff2e6ce5 100644 --- a/Doc/c-api/set.rst +++ b/Doc/c-api/set.rst @@ -5,9 +5,6 @@ Set Objects ----------- -.. sectionauthor:: Raymond D. Hettinger - - .. index:: pair: object; set pair: object; frozenset @@ -92,6 +89,11 @@ the constructor functions work with any iterable Python object. actually iterable. The constructor is also useful for copying a set (``c=set(s)``). + .. note:: + + The operation is atomic on :term:`free threading ` + when *iterable* is a :class:`set`, :class:`frozenset`, :class:`dict` or :class:`frozendict`. + .. c:function:: PyObject* PyFrozenSet_New(PyObject *iterable) @@ -100,6 +102,11 @@ the constructor functions work with any iterable Python object. set on success or ``NULL`` on failure. Raise :exc:`TypeError` if *iterable* is not actually iterable. + .. note:: + + The operation is atomic on :term:`free threading ` + when *iterable* is a :class:`set`, :class:`frozenset`, :class:`dict` or :class:`frozendict`. + The following functions and macros are available for instances of :class:`set` or :class:`frozenset` or instances of their subtypes. @@ -127,6 +134,10 @@ or :class:`frozenset` or instances of their subtypes. the *key* is unhashable. Raise :exc:`SystemError` if *anyset* is not a :class:`set`, :class:`frozenset`, or an instance of a subtype. + .. note:: + + The operation is atomic on :term:`free threading ` + when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`. .. c:function:: int PySet_Add(PyObject *set, PyObject *key) @@ -138,6 +149,12 @@ or :class:`frozenset` or instances of their subtypes. :exc:`SystemError` if *set* is not an instance of :class:`set` or its subtype. + .. note:: + + The operation is atomic on :term:`free threading ` + when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`. + + The following functions are available for instances of :class:`set` or its subtypes but not for instances of :class:`frozenset` or its subtypes. @@ -147,11 +164,16 @@ subtypes but not for instances of :class:`frozenset` or its subtypes. Return ``1`` if found and removed, ``0`` if not found (no action taken), and ``-1`` if an error is encountered. Does not raise :exc:`KeyError` for missing keys. Raise a - :exc:`TypeError` if the *key* is unhashable. Unlike the Python :meth:`~frozenset.discard` + :exc:`TypeError` if the *key* is unhashable. Unlike the Python :meth:`~set.discard` method, this function does not automatically convert unhashable sets into temporary frozensets. Raise :exc:`SystemError` if *set* is not an instance of :class:`set` or its subtype. + .. note:: + + The operation is atomic on :term:`free threading ` + when *key* is :class:`str`, :class:`int`, :class:`float`, :class:`bool` or :class:`bytes`. + .. c:function:: PyObject* PySet_Pop(PyObject *set) @@ -166,3 +188,28 @@ subtypes but not for instances of :class:`frozenset` or its subtypes. Empty an existing set of all elements. Return ``0`` on success. Return ``-1`` and raise :exc:`SystemError` if *set* is not an instance of :class:`set` or its subtype. + + .. note:: + + In the :term:`free-threaded build`, the set is emptied before its entries + are cleared, so other threads will observe an empty set rather than + intermediate states. + + +Deprecated API +^^^^^^^^^^^^^^ + +.. c:macro:: PySet_MINSIZE + + A constant representing the size of an internal + preallocated table inside :c:type:`PySetObject` instances. + + This is documented solely for completeness, as there are no guarantees + that a given version of CPython uses preallocated tables with a fixed + size. + In code that does not deal with unstable set internals, + :c:macro:`!PySet_MINSIZE` can be replaced with a small constant like ``8``. + + If looking for the size of a set, use :c:func:`PySet_Size` instead. + + .. soft-deprecated:: 3.14 diff --git a/Doc/c-api/slots.rst b/Doc/c-api/slots.rst new file mode 100644 index 00000000000000..84a125cb60bae7 --- /dev/null +++ b/Doc/c-api/slots.rst @@ -0,0 +1,240 @@ +.. highlight:: c + +.. _capi-slots: + +Definition slots +================ + +To define :ref:`module objects ` and +:ref:`classes ` using the C API, you may use +an array of *slots* -- essentially, key-value pairs that describe features +of the object to create. +This decouples the data from the structures used at runtime, allowing CPython +-- and other Python C API implementations -- to update the structures without +breaking backwards compatibility. + +This section documents slots in general. +For object-specific behavior and slot values, see documentation for functions +that apply slots: + +- :c:func:`PyType_FromSlots` for types; +- :c:func:`PyModule_FromSlotsAndSpec` and :ref:`extension-export-hook` + for modules. + +When slots are passed to a function that applies them, the function will +not modify the slot array, nor any data it points to (recursively). +After the function is done, the caller is allowed to modify or deallocate +the array and any data it points to (recursively), except data +explicitly marked with :c:macro:`PySlot_STATIC`. + +Except when documented otherwise, multiple slots with the same ID +(:c:member:`~PySlot.sl_id`) may not occur in a single slots array. + +.. versionadded:: 3.15 + + Slot arrays generalize an earlier way of defining objects: + using :c:type:`PyType_Spec` with :c:type:`PyType_Slot` for types, and + :c:type:`PyModuleDef` with :c:type:`PyModuleDef_Slot` for modules. + The earlier API is :term:`soft deprecated`; there are no plans to remove it. + +Entries of the slots array use the following structure: + +.. c:type:: PySlot + + An entry in a slots array. Defined as: + + .. code-block:: c + + typedef struct { + uint16_t sl_id; + uint16_t sl_flags; + uint32_t _reserved; // must be 0 + union { + void *sl_ptr; + void (*sl_func)(void); + Py_ssize_t sl_size; + int64_t sl_int64; + uint64_t sl_uint64; + }; + } PySlot; + + .. c:member:: uint16_t sl_id + + A slot ID, chosen from: + + - ``Py_slot_*`` values documented in :ref:`pyslot-common-ids` below; + - ``Py_mod_*`` values for modules, as documented in :ref:`c_module_slots`; + - Values for types, as documented in :ref:`pyslot_type_slot_ids`. + + A :c:member:`!sl_id` of zero (:c:macro:`Py_slot_end`) marks the end of a + slots array. + + .. c:member:: void *sl_ptr + void (*sl_func)(void) + Py_ssize_t sl_size + int64_t sl_int64 + uint64_t sl_uint64 + + The data for the slot. + These members are part of an anonymous union; + the member to use depends on which data type is required by the slot ID: + data pointer, function pointer, size, signed or unsigned + integer, respectively. + + Except when documented otherwise for a specific slot ID, pointers + (that is :c:member:`!sl_ptr` and :c:member:`!sl_func`) may not be NULL. + + .. c:member:: uint16_t sl_flags + + Zero or more of the following flags, OR-ed together: + + .. c:namespace:: NULL + + .. c:macro:: PySlot_STATIC + + All data the slot points to is statically allocated and constant. + Thus, the interpreter does not need to copy the information. + + This flag is implied for function pointers. + + The flag applies even to data the slot points to “indirectly”, + except for slots nested via :c:macro:`Py_slot_subslots` which may + have their own :c:macro:`!PySlot_STATIC` flags. + For example, if applied to a :c:macro:`Py_tp_members` slot that + points to an array of :c:type:`PyMemberDef` structures, + then the entire array, as well as the name and doc strings + in its elements, must be static and constant. + + .. c:macro:: PySlot_INTPTR + + The data is stored in ``sl_ptr``; CPython will cast it to + the appropriate type. + + This flag can simplify porting from the older :c:type:`PyType_Slot` + and :c:type:`PyModuleDef_Slot` structures. + + .. c:macro:: PySlot_OPTIONAL + + If the slot ID is unknown, the interpreter should ignore the + slot, rather than fail. + + For example, if Python 3.16 adds a new feature with a new slot ID,attr + the corresponding slot may be marked :c:macro:`!PySlot_OPTIONAL` + so that Python 3.15 ignores it. + + Note that the "optionality" only applies to unknown slot IDs. + This flag does not make Python skip invalid values of known slots. + + .. versionadded:: 3.15 + + +Convenience macros +------------------ + +.. c:macro:: PySlot_DATA(name, value) + PySlot_FUNC(name, value) + PySlot_SIZE(name, value) + PySlot_INT64(name, value) + PySlot_UINT64(name, value) + PySlot_STATIC_DATA(name, value) + + Convenience macros to define :c:type:`!PySlot` structures with + :c:member:`~PySlot.sl_id` and a particular union member set. + + :c:macro:`!PySlot_STATIC_DATA` sets the :c:macro:`PySlot_STATIC` flag; + others set no flags. + + Note that these macros use *designated initializers*, a C language feature + that C++ added in the 2020 version of the standard. + If your code needs to be compatible with C++11 or older, + use :c:macro:`PySlot_PTR` instead. + + Defined as:: + + #define PySlot_DATA(NAME, VALUE) \ + {.sl_id=NAME, .sl_ptr=(void*)(VALUE)} + + #define PySlot_FUNC(NAME, VALUE) \ + {.sl_id=NAME, .sl_func=(VALUE)} + + #define PySlot_SIZE(NAME, VALUE) \ + {.sl_id=NAME, .sl_size=(VALUE)} + + #define PySlot_INT64(NAME, VALUE) \ + {.sl_id=NAME, .sl_int64=(VALUE)} + + #define PySlot_UINT64(NAME, VALUE) \ + {.sl_id=NAME, .sl_uint64=(VALUE)} + + #define PySlot_STATIC_DATA(NAME, VALUE) \ + {.sl_id=NAME, .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)} + + .. versionadded:: 3.15 + +.. c:macro:: PySlot_END + + Convenience macro to mark the end of a :c:type:`!PySlot` array. + + Defined as:: + + #define PySlot_END {0} + + .. versionadded:: 3.15 + +.. c:macro:: PySlot_PTR(name, value) + PySlot_PTR_STATIC(name, value) + + Convenience macros for use in C++11-compatible code. + This version of C++ does not allow setting arbitrary union members in + literals; instead, these macros set the :c:macro:`PySlot_INTPTR` flag and cast + the value to ``(void*)``. + + Defined as:: + + #define PySlot_PTR(NAME, VALUE) \ + {NAME, PySlot_INTPTR, {0}, {(void*)(VALUE)}} + + #define PySlot_PTR_STATIC(NAME, VALUE) \ + {NAME, PySlot_INTPTR|Py_SLOT_STATIC, {0}, {(void*)(VALUE)}} + + .. versionadded:: 3.15 + +.. _pyslot-common-ids: + +Common slot IDs +--------------- + +The following slot IDs may be used in both type and module definitions. + +.. c:macro:: Py_slot_end + + Marks the end of a slots array. + Defined as zero. + + .. versionadded:: 3.15 + +.. c:macro:: Py_slot_subslots + + Nested slots array. + + The value (:c:member:`~PySlot.sl_ptr`) should point to an array of + :c:type:`PySlot` structures. + The slots in the array (up to but not including the zero-ID + terminator) will be treated as if they were inserted if the current + slot array, at the point :c:macro:`!Py_slot_subslots` appears. + + Slot nesting depth is limited to 5 levels. + This restriction may be lifted in the future. + + .. versionadded:: 3.15 + +.. c:macro:: Py_slot_invalid + + Reserved; will always be treated as an unknown slot ID. + Defined as ``UINT16_MAX`` (``0xFFFF``). + + When used with the :c:macro:`PySlot_OPTIONAL` flag, defines a slot with + no effect. + Without the flag, processing a slot with this ID will fail. + + .. versionadded:: 3.15 diff --git a/Doc/c-api/stable.rst b/Doc/c-api/stable.rst index 9b65e0b8d23d93..13e5d5c96135c0 100644 --- a/Doc/c-api/stable.rst +++ b/Doc/c-api/stable.rst @@ -2,9 +2,9 @@ .. _stable: -*************** -C API Stability -*************** +*********************** +C API and ABI Stability +*********************** Unless documented otherwise, Python's C API is covered by the Backwards Compatibility Policy, :pep:`387`. @@ -51,135 +51,212 @@ It is generally intended for specialized, low-level tools like debuggers. Projects that use this API are expected to follow CPython development and spend extra effort adjusting to changes. +.. _stable-abi: .. _stable-application-binary-interface: -Stable Application Binary Interface -=================================== +Stable Application Binary Interfaces +==================================== -For simplicity, this document talks about *extensions*, but the Limited API -and Stable ABI work the same way for all uses of the API – for example, -embedding Python. +Python's :dfn:`Stable ABI` allows extensions to be compatible with multiple +versions of Python, without recompilation. -.. _limited-c-api: +.. note:: -Limited C API -------------- + For simplicity, this document talks about *extensions*, but Stable ABI + works the same way for all uses of the API – for example, embedding Python. -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 be loaded on multiple versions of Python. -Contents of the Limited API are :ref:`listed below `. +There are two Stable ABIs: -.. c:macro:: Py_LIMITED_API +- ``abi3``, introduced in Python 3.2, is compatible with + **non**-:term:`free-threaded ` builds of CPython. - Define this macro before including ``Python.h`` to opt in to only use - the Limited API, and to select the Limited API version. +- ``abi3t``, introduced in Python 3.15, is compatible with + :term:`free-threaded ` builds of CPython. + It has stricter API limitations than ``abi3``. - 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 be ABI-compatible with all Python 3 releases - from the specified one onward, and can use Limited API introduced up to that - version. + .. versionadded:: 3.15 - Rather than using the ``PY_VERSION_HEX`` macro directly, hardcode a minimum - minor version (e.g. ``0x030A0000`` for Python 3.10) for stability when - compiling with future Python versions. + ``abi3t`` was added in :pep:`803` - You can also define ``Py_LIMITED_API`` to ``3``. This works the same as - ``0x03020000`` (Python 3.2, the version that introduced Limited API). +It is possible for an extension to be compiled for *both* ``abi3`` and +``abi3t`` at the same time; the result will be compatible with +both free-threaded and non-free-threaded builds of Python. +Currently, this has no downsides compared to compiling for ``abi3t`` only. +Each Stable ABI is versioned using the first two numbers of the Python version. +For example, Stable ABI 3.14 corresponds to Python 3.14. +An extension compiled for Stable ABI 3.x is ABI-compatible with Python 3.x +and above. + +Extensions that target a stable ABI must only use a limited subset of +the C API. This subset is known as the :dfn:`Limited API`; its contents +are :ref:`listed below `. + +On Windows, extensions that use a Stable ABI should be linked against +``python3.dll`` rather than a version-specific library such as +``python39.dll``. +This library only exposes the relevant symbols. + +On some platforms, Python will look for and load shared library files named +with the ``abi3`` or ``abi3t`` tag (for example, ``mymodule.abi3.so``). +:term:`Free-threaded ` interpreters only recognize the +``abi3t`` tag, while non-free-threaded ones will prefer ``abi3`` but fall back +to ``abi3t``. +Thus, extensions compatible with both ABIs should use the ``abi3t`` tag. + +Python does not necessarily check that extensions it loads +have compatible ABI. +Extension authors are encouraged to add a check using the :c:macro:`Py_mod_abi` +slot or the :c:func:`PyABIInfo_Check` function, but the user +(or their packaging tool) is ultimately responsible for ensuring that, +for example, extensions built for Stable ABI 3.10 are not installed for lower +versions of Python. + +All functions in Stable ABI are present as functions in Python's shared +library, not solely as macros. +This makes them usable in languages that don't use the C +preprocessor, including Python's :py:mod:`ctypes`. -.. _stable-abi: -Stable ABI ----------- +.. _abi3-compiling: -To enable this, Python provides a *Stable ABI*: a set of symbols that will -remain ABI-compatible across Python 3.x versions. +Compiling for Stable ABI +------------------------ .. 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. + Build tools (such as, for example, meson-python, scikit-build-core, + or Setuptools) often have a mechanism for setting macros and synchronizing + them with extension filenames and other metadata. + Prefer using such a mechanism, if it exists, over defining the + macros manually. -The Stable ABI contains symbols exposed in the :ref:`Limited API -`, but also other ones – for example, functions necessary to -support older versions of the Limited API. + The rest of this section is mainly relevant for tool authors, and for + people who compile extensions manually. -On Windows, extensions that use the Stable ABI should be linked against -``python3.dll`` rather than a version-specific library such as -``python39.dll``. + .. seealso:: `list of recommended tools`_ in the Python Packaging User Guide -On some platforms, Python will look for and load shared library files named -with the ``abi3`` tag (e.g. ``mymodule.abi3.so``). -It does not check if such extensions conform to a Stable ABI. -The user (or their packaging tools) need to ensure that, for example, -extensions built with the 3.10+ Limited API are not installed for lower -versions of Python. + .. _list of recommended tools: https://packaging.python.org/en/latest/guides/tool-recommendations/#build-backends-for-extension-modules + +To compile for a Stable ABI, define one or both of the following macros +to the lowest Python version your extension should support, in +:c:macro:`Py_PACK_VERSION` format. +Typically, you should choose a specific value rather than the version of +the Python headers you are compiling against. + +The macros must be defined before including ``Python.h``. +Since :c:macro:`Py_PACK_VERSION` is not available at this point, you +will need to use the numeric value directly. +For reference, the values for a few recent Python versions are: + +.. version-hex-cheatsheet:: + +When one of the macros is defined, ``Python.h`` will only expose API that is +compatible with the given Stable ABI -- that is, the +:ref:`Limited API ` plus some definitions that need to be +visible to the compiler but should not be used directly. +When both are defined, ``Python.h`` will only expose API compatible with +both Stable ABIs. + +.. c:macro:: Py_LIMITED_API + + Target ``abi3``, that is, + non-:term:`free-threaded builds ` of CPython. + See :ref:`above ` for common information. + +.. c:macro:: Py_TARGET_ABI3T -All functions in the Stable ABI are present as functions in Python's shared -library, not solely as macros. This makes them usable from languages that don't -use the C preprocessor. + Target ``abi3t``, that is, + :term:`free-threaded builds ` of CPython. + See :ref:`above ` for common information. + .. versionadded:: 3.15 -Limited API Scope and Performance ---------------------------------- +Both macros specify a target ABI; the different naming style is due to +backwards compatibility. -The goal for the Limited API is to allow everything that is possible with the +.. admonition:: Historical note + + You can also define ``Py_LIMITED_API`` as ``3``. This works the same as + ``0x03020000`` (Python 3.2, the version that introduced Stable ABI). + +When both are defined, ``Python.h`` may, or may not, redefine +:c:macro:`!Py_LIMITED_API` to match :c:macro:`!Py_TARGET_ABI3T`. + +On a :term:`free-threaded build` -- that is, when +:c:macro:`Py_GIL_DISABLED` is defined -- :c:macro:`!Py_TARGET_ABI3T` +defaults to the value of :c:macro:`!Py_LIMITED_API`. +This means that there are two ways to build for both ``abi3`` and ``abi3t``: + +- define both :c:macro:`!Py_LIMITED_API` and :c:macro:`!Py_TARGET_ABI3T`, or +- define only :c:macro:`!Py_LIMITED_API` and: + + - on Windows, define :c:macro:`!Py_GIL_DISABLED`; + - on other systems, use the headers of free-threaded build of Python. + + +.. _limited-api-scope-and-performance: + +Stable ABI Scope and Performance +-------------------------------- + +The goal for Stable ABI is to allow everything that is possible with the full C API, but possibly with a performance penalty. +Generally, compatibility with Stable ABI will require some changes to an +extension's source code. -For example, while :c:func:`PyList_GetItem` is available, its “unsafe” macro +For example, while :c:func:`PyList_GetItem` is available, its "unsafe" macro variant :c:func:`PyList_GET_ITEM` is not. The macro can be faster because it can rely on version-specific implementation details of the list object. -Without ``Py_LIMITED_API`` defined, some C API functions are inlined or -replaced by macros. -Defining ``Py_LIMITED_API`` disables this inlining, allowing stability as +For another example, when *not* compiling for Stable ABI, some C API +functions are inlined or replaced by macros. +Compiling for Stable ABI disables this inlining, allowing stability as Python's data structures are improved, but possibly reducing performance. -By leaving out the ``Py_LIMITED_API`` definition, it is possible to compile -a Limited API extension with a version-specific ABI. This can improve -performance for that Python version, but will limit compatibility. -Compiling with ``Py_LIMITED_API`` will then yield an extension that can be -distributed where a version-specific one is not available – for example, -for prereleases of an upcoming Python version. +By leaving out the :c:macro:`!Py_LIMITED_API` or :c:macro:`!Py_TARGET_ABI3T` +definition, it is possible to compile Stable-ABI-compatible source +for a version-specific ABI. +A potentially faster version-specific extension can then be distributed +alongside a version compiled for Stable ABI -- a slower but more compatible +fallback. -Limited API Caveats -------------------- +.. _limited-api-caveats: -Note that compiling with ``Py_LIMITED_API`` is *not* a complete guarantee that -code conforms to the :ref:`Limited API ` or the :ref:`Stable ABI -`. ``Py_LIMITED_API`` only covers definitions, but an API also -includes other issues, such as expected semantics. +Stable ABI Caveats +------------------ -One issue that ``Py_LIMITED_API`` does not guard against is calling a function -with arguments that are invalid in a lower Python version. +Note that compiling for Stable ABI is *not* a complete guarantee that code will +be compatible with the expected Python versions. +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. + +One issue that the :c:macro:`Py_TARGET_ABI3T` and :c:macro:`Py_LIMITED_API` +macros do not guard against is calling a function with arguments that are +invalid in a lower Python version. For example, consider a function that starts accepting ``NULL`` for an argument. In Python 3.9, ``NULL`` now selects a default behavior, but in Python 3.8, the argument will be used directly, causing a ``NULL`` dereference and crash. A similar argument works for fields of structs. -Another issue is that some struct fields are currently not hidden when -``Py_LIMITED_API`` is defined, even though they're part of the Limited API. - For these reasons, we recommend testing an extension with *all* minor Python -versions it supports, and preferably to build with the *lowest* such version. +versions it supports. We also recommend reviewing documentation of all used API to check if it is explicitly part of the Limited API. Even with ``Py_LIMITED_API`` defined, a few private declarations are exposed for technical reasons (or even unintentionally, as bugs). -Also note that the Limited API is not necessarily stable: compiling with -``Py_LIMITED_API`` with Python 3.8 means that the extension will -run with Python 3.12, but it will not necessarily *compile* with Python 3.12. -In particular, parts of the Limited API may be deprecated and removed, -provided that the Stable ABI stays stable. +Also note that while compiling with ``Py_LIMITED_API`` 3.8 means that the +extension should *load* on Python 3.12, and *compile* with Python 3.12, +the same source will not necessarily compile with ``Py_LIMITED_API`` +set to 3.12. +In general, parts of the Limited API may be deprecated and removed, +provided that Stable ABI stays stable. .. _stable-abi-platform: @@ -189,22 +266,187 @@ Platform Considerations ABI stability depends not only on Python, but also on the compiler used, lower-level libraries and compiler options. For the purposes of -the :ref:`Stable ABI `, these details define a “platform”. They +the :ref:`Stable ABIs `, these details define a “platform”. They usually depend on the OS type and processor architecture It is the responsibility of each particular distributor of Python to ensure that all Python versions on a particular platform are built -in a way that does not break the Stable ABI. +in a way that does not break the Stable ABIs, or the version-specific ABIs. This is the case with Windows and macOS releases from ``python.org`` and many third-party distributors. +ABI Checking +============ + +.. versionadded:: 3.15 + +Python includes a rudimentary check for ABI compatibility. + +This check is not comprehensive. +It only guards against common cases of incompatible modules being +installed for the wrong interpreter. +It also does not take :ref:`platform incompatibilities ` +into account. +It can only be done after an extension is successfully loaded. + +Despite these limitations, it is recommended that extension modules use this +mechanism, so that detectable incompatibilities raise exceptions rather than +crash. + +Most modules can use this check via the :c:data:`Py_mod_abi` +slot and the :c:macro:`PyABIInfo_VAR` macro, for example like this: + +.. code-block:: c + + PyABIInfo_VAR(abi_info); + + static PyModuleDef_Slot mymodule_slots[] = { + {Py_mod_abi, &abi_info}, + ... + }; + + +The full API is described below for advanced use cases. + +.. c:function:: int PyABIInfo_Check(PyABIInfo *info, const char *module_name) + + Verify that the given *info* is compatible with the currently running + interpreter. + + Return 0 on success. On failure, raise an exception and return -1. + + If the ABI is incompatible, the raised exception will be :py:exc:`ImportError`. + + The *module_name* argument can be ``NULL``, or point to a NUL-terminated + UTF-8-encoded string used for error messages. + + Note that if *info* describes the ABI that the current code uses (as defined + by :c:macro:`PyABIInfo_VAR`, for example), using any other Python C API + may lead to crashes. + In particular, it is not safe to examine the raised exception. + + .. versionadded:: 3.15 + +.. c:macro:: PyABIInfo_VAR(NAME) + + Define a static :c:struct:`PyABIInfo` variable with the given *NAME* that + describes the ABI that the current code will use. + This macro expands to: + + .. code-block:: c + + static PyABIInfo NAME = { + 1, 0, + PyABIInfo_DEFAULT_FLAGS, + PY_VERSION_HEX, + PyABIInfo_DEFAULT_ABI_VERSION + } + + .. versionadded:: 3.15 + +.. c:type:: PyABIInfo + + .. c:member:: uint8_t abiinfo_major_version + + The major version of :c:struct:`PyABIInfo`. Can be set to: + + * ``0`` to skip all checking, or + * ``1`` to specify this version of :c:struct:`!PyABIInfo`. + + .. c:member:: uint8_t abiinfo_minor_version + + The minor version of :c:struct:`PyABIInfo`. + Must be set to ``0``; larger values are reserved for backwards-compatible + future versions of :c:struct:`!PyABIInfo`. + + .. c:member:: uint16_t flags + + .. c:namespace:: NULL + + This field is usually set to the following macro: + + .. c:macro:: PyABIInfo_DEFAULT_FLAGS + + Default flags, based on current values of macros such as + :c:macro:`Py_LIMITED_API` and :c:macro:`Py_GIL_DISABLED`. + + Alternately, the field can be set to the following flags, combined + by bitwise OR. + Unused bits must be set to zero. + + ABI variant -- one of: + + .. c:macro:: PyABIInfo_STABLE + + Specifies that Stable ABI is used. + + .. c:macro:: PyABIInfo_INTERNAL + + Specifies ABI specific to a particular build of CPython. + Internal use only. + + Free-threading compatibility -- one of: + + .. c:macro:: PyABIInfo_FREETHREADED + + Specifies ABI compatible with :term:`free-threaded builds + ` of CPython. + (That is, ones compiled with :option:`--disable-gil`; with ``t`` + in :py:data:`sys.abiflags`) + + .. c:macro:: PyABIInfo_GIL + + Specifies ABI compatible with non-free-threaded builds of CPython + (ones compiled *without* :option:`--disable-gil`). + + .. c:macro:: PyABIInfo_FREETHREADING_AGNOSTIC + + Specifies ABI compatible with both free-threaded and + non-free-threaded builds of CPython, that is, both + ``abi3`` and ``abi3t``. + + .. c:member:: uint32_t build_version + + The version of the Python headers used to build the code, in the format + used by :c:macro:`PY_VERSION_HEX`. + + This can be set to ``0`` to skip any checks related to this field. + This option is meant mainly for projects that do not use the CPython + headers directly, and do not emulate a specific version of them. + + .. c:member:: uint32_t abi_version + + The ABI version. + + For Stable ABI, this field should be the value of + :c:macro:`Py_LIMITED_API` or :c:macro:`Py_TARGET_ABI3T`. + If both are defined, use the smaller value. + (If :c:macro:`Py_LIMITED_API` is ``3``; use + :c:expr:`Py_PACK_VERSION(3, 2)` instead of ``3``.) + + Otherwise, it should be set to :c:macro:`PY_VERSION_HEX`. + + It can also be set to ``0`` to skip any checks related to this field. + + .. c:namespace:: NULL + + .. c:macro:: PyABIInfo_DEFAULT_ABI_VERSION + + The value that should be used for this field, based on current + values of macros such as :c:macro:`Py_LIMITED_API`, + :c:macro:`PY_VERSION_HEX` and :c:macro:`Py_GIL_DISABLED`. + + .. versionadded:: 3.15 + + +.. _limited-c-api: .. _limited-api-list: Contents of Limited API ======================= - -Currently, the :ref:`Limited API ` includes the following items: +This is the definitive list of :ref:`Limited API ` for +Python |version|: .. limited-api-list:: diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 58dd915e04f619..1ddde5d136b3bb 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -33,6 +33,13 @@ under :ref:`reference counting `. The members must not be accessed directly; instead use macros such as :c:macro:`Py_REFCNT` and :c:macro:`Py_TYPE`. + In the :ref:`Stable ABI ` for Free-Threaded Builds (``abi3t``), + this struct is opaque; its size and layout may change between + Python versions. + In Stable ABI for non-free-threaded builds (``abi3``), the + :c:member:`!ob_refcnt` and :c:member:`!ob_type` fields are available, + but using them directly is discouraged. + .. c:member:: Py_ssize_t ob_refcnt The object's reference count, as returned by :c:macro:`Py_REFCNT`. @@ -48,6 +55,19 @@ under :ref:`reference counting `. Do not use this field directly; use :c:macro:`Py_TYPE` and :c:func:`Py_SET_TYPE` instead. + .. c:member:: PyMutex ob_mutex + + A :ref:`per-object lock `, present only in the :term:`free-threaded ` + build (when :c:macro:`Py_GIL_DISABLED` is defined). + + This field is **reserved for use by the critical section API** + (:c:macro:`Py_BEGIN_CRITICAL_SECTION` / :c:macro:`Py_END_CRITICAL_SECTION`). + Do **not** lock it directly with ``PyMutex_Lock``; doing so can cause + deadlocks. If you need your own lock, add a separate :c:type:`PyMutex` + field to your object struct. + + .. versionadded:: 3.13 + .. c:type:: PyVarObject @@ -59,6 +79,19 @@ under :ref:`reference counting `. instead use macros such as :c:macro:`Py_SIZE`, :c:macro:`Py_REFCNT` and :c:macro:`Py_TYPE`. + In the :ref:`Stable ABI ` for Free-Threaded Builds (``abi3t``), + this struct is opaque; its size and layout may change between + Python versions. + In Stable ABI for non-free-threaded builds (``abi3``), the + :c:member:`!ob_base` and :c:member:`!ob_size` fields are available, + but using them directly is discouraged. + + .. c:member:: PyObject ob_base + + Common object header. + Typically, this field is not accessed directly; instead + :c:type:`!PyVarObject` can be cast to :c:type:`PyObject`. + .. c:member:: Py_ssize_t ob_size A size field, whose contents should be considered an object's internal @@ -280,6 +313,8 @@ Implementing functions and methods Name of the method. + A ``NULL`` *ml_name* marks the end of a :c:type:`!PyMethodDef` array. + .. c:member:: PyCFunction ml_meth Pointer to the C implementation. @@ -408,7 +443,7 @@ There are these calling conventions: These two constants are not used to indicate the calling convention but the -binding when use with methods of classes. These may not be used for functions +binding when used with methods of classes. These may not be used for functions defined for modules. At most one of these flags may be set for any given method. @@ -419,8 +454,8 @@ method. The method will be passed the type object as the first parameter rather than an instance of the type. This is used to create *class methods*, - similar to what is created when using the :func:`classmethod` built-in - function. + similar to what is created when using the :deco:`classmethod` built-in + decorator. .. c:macro:: METH_STATIC @@ -429,7 +464,7 @@ method. The method will be passed ``NULL`` as the first parameter rather than an instance of the type. This is used to create *static methods*, similar to - what is created when using the :func:`staticmethod` built-in function. + what is created when using the :deco:`staticmethod` built-in decorator. One other constant controls whether a method is loaded in place of another definition with the same method name. @@ -447,6 +482,25 @@ definition with the same method name. slot. This is helpful because calls to PyCFunctions are optimized more than wrapper object calls. + +.. c:var:: PyTypeObject PyCMethod_Type + + The type object corresponding to Python C method objects. This is + available as :class:`types.BuiltinMethodType` in the Python layer. + + +.. c:function:: int PyCMethod_Check(PyObject *op) + + Return true if *op* is an instance of the :c:type:`PyCMethod_Type` type + or a subtype of it. This function always succeeds. + + +.. c:function:: int PyCMethod_CheckExact(PyObject *op) + + This is the same as :c:func:`PyCMethod_Check`, but does not account for + subtypes. + + .. c:function:: PyObject * PyCMethod_New(PyMethodDef *ml, PyObject *self, PyObject *module, PyTypeObject *cls) Turn *ml* into a Python :term:`callable` object. @@ -472,6 +526,24 @@ definition with the same method name. .. versionadded:: 3.9 +.. c:var:: PyTypeObject PyCFunction_Type + + The type object corresponding to Python C function objects. This is + available as :class:`types.BuiltinFunctionType` in the Python layer. + + +.. c:function:: int PyCFunction_Check(PyObject *op) + + Return true if *op* is an instance of the :c:type:`PyCFunction_Type` type + or a subtype of it. This function always succeeds. + + +.. c:function:: int PyCFunction_CheckExact(PyObject *op) + + This is the same as :c:func:`PyCFunction_Check`, but does not account for + subtypes. + + .. c:function:: PyObject * PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) Equivalent to ``PyCMethod_New(ml, self, module, NULL)``. @@ -482,6 +554,62 @@ definition with the same method name. Equivalent to ``PyCMethod_New(ml, self, NULL, NULL)``. +.. c:function:: int PyCFunction_GetFlags(PyObject *func) + + Get the function's flags on *func* as they were passed to + :c:member:`~PyMethodDef.ml_flags`. + + If *func* is not a C function object, this fails with an exception. + *func* must not be ``NULL``. + + This function returns the function's flags on success, and ``-1`` with an + exception set on failure. + + +.. c:function:: int PyCFunction_GET_FLAGS(PyObject *func) + + This is the same as :c:func:`PyCFunction_GetFlags`, but without error + or type checking. + + +.. c:function:: PyCFunction PyCFunction_GetFunction(PyObject *func) + + Get the function pointer on *func* as it was passed to + :c:member:`~PyMethodDef.ml_meth`. + + If *func* is not a C function object, this fails with an exception. + *func* must not be ``NULL``. + + This function returns the function pointer on success, and ``NULL`` with an + exception set on failure. + + +.. c:function:: int PyCFunction_GET_FUNCTION(PyObject *func) + + This is the same as :c:func:`PyCFunction_GetFunction`, but without error + or type checking. + + +.. c:function:: PyObject *PyCFunction_GetSelf(PyObject *func) + + Get the "self" object on *func*. This is the object that would be passed + to the first argument of a :c:type:`PyCFunction`. For C function objects + created through a :c:type:`PyMethodDef` on a :c:type:`PyModuleDef`, this + is the resulting module object. + + If *func* is not a C function object, this fails with an exception. + *func* must not be ``NULL``. + + This function returns a :term:`borrowed reference` to the "self" object + on success, and ``NULL`` with an exception set on failure. + + +.. c:function:: PyObject *PyCFunction_GET_SELF(PyObject *func) + + This is the same as :c:func:`PyCFunction_GetSelf`, but without error or + type checking. + + Accessing attributes of extension types --------------------------------------- @@ -605,14 +733,12 @@ The following flags can be used with :c:member:`PyMemberDef.flags`: entry indicates an offset from the subclass-specific data, rather than from ``PyObject``. - Can only be used as part of :c:member:`Py_tp_members ` + Can only be used as part of the :c:data:`Py_tp_members` :c:type:`slot ` when creating a class using negative :c:member:`~PyType_Spec.basicsize`. It is mandatory in that case. - - This flag is only used in :c:type:`PyType_Slot`. - When setting :c:member:`~PyTypeObject.tp_members` during - class creation, Python clears it and sets + When setting :c:member:`~PyTypeObject.tp_members` from the slot during + class creation, Python clears the flag and sets :c:member:`PyMemberDef.offset` to the offset from the ``PyObject`` struct. .. index:: diff --git a/Doc/c-api/subinterpreters.rst b/Doc/c-api/subinterpreters.rst new file mode 100644 index 00000000000000..83c3fc3d801e9b --- /dev/null +++ b/Doc/c-api/subinterpreters.rst @@ -0,0 +1,491 @@ +.. highlight:: c + +.. _sub-interpreter-support: + +Multiple interpreters in a Python process +========================================= + +While in most uses, you will only embed a single Python interpreter, there +are cases where you need to create several independent interpreters in the +same process and perhaps even in the same thread. Sub-interpreters allow +you to do that. + +The "main" interpreter is the first one created when the runtime initializes. +It is usually the only Python interpreter in a process. Unlike sub-interpreters, +the main interpreter has unique process-global responsibilities like signal +handling. It is also responsible for execution during runtime initialization and +is usually the active interpreter during runtime finalization. The +:c:func:`PyInterpreterState_Main` function returns a pointer to its state. + +You can switch between sub-interpreters using the :c:func:`PyThreadState_Swap` +function. You can create and destroy them using the following functions: + + +.. c:type:: PyInterpreterConfig + + Structure containing most parameters to configure a sub-interpreter. + Its values are used only in :c:func:`Py_NewInterpreterFromConfig` and + never modified by the runtime. + + .. versionadded:: 3.12 + + Structure fields: + + .. c:member:: int use_main_obmalloc + + If this is ``0`` then the sub-interpreter will use its own + "object" allocator state. + Otherwise it will use (share) the main interpreter's. + + If this is ``0`` then + :c:member:`~PyInterpreterConfig.check_multi_interp_extensions` + must be ``1`` (non-zero). + If this is ``1`` then :c:member:`~PyInterpreterConfig.gil` + must not be :c:macro:`PyInterpreterConfig_OWN_GIL`. + + .. c:member:: int allow_fork + + If this is ``0`` then the runtime will not support forking the + process in any thread where the sub-interpreter is currently active. + Otherwise fork is unrestricted. + + Note that the :mod:`subprocess` module still works + when fork is disallowed. + + .. c:member:: int allow_exec + + If this is ``0`` then the runtime will not support replacing the + current process via exec (e.g. :func:`os.execv`) in any thread + where the sub-interpreter is currently active. + Otherwise exec is unrestricted. + + Note that the :mod:`subprocess` module still works + when exec is disallowed. + + .. c:member:: int allow_threads + + If this is ``0`` then the sub-interpreter's :mod:`threading` module + won't create threads. + Otherwise threads are allowed. + + .. c:member:: int allow_daemon_threads + + If this is ``0`` then the sub-interpreter's :mod:`threading` module + won't create daemon threads. + Otherwise daemon threads are allowed (as long as + :c:member:`~PyInterpreterConfig.allow_threads` is non-zero). + + .. c:member:: int check_multi_interp_extensions + + If this is ``0`` then all extension modules may be imported, + including legacy (single-phase init) modules, + in any thread where the sub-interpreter is currently active. + Otherwise only multi-phase init extension modules + (see :pep:`489`) may be imported. + (Also see :c:macro:`Py_mod_multiple_interpreters`.) + + This must be ``1`` (non-zero) if + :c:member:`~PyInterpreterConfig.use_main_obmalloc` is ``0``. + + .. c:member:: int gil + + This determines the operation of the GIL for the sub-interpreter. + It may be one of the following: + + .. c:namespace:: NULL + + .. c:macro:: PyInterpreterConfig_DEFAULT_GIL + + Use the default selection (:c:macro:`PyInterpreterConfig_SHARED_GIL`). + + .. c:macro:: PyInterpreterConfig_SHARED_GIL + + Use (share) the main interpreter's GIL. + + .. c:macro:: PyInterpreterConfig_OWN_GIL + + Use the sub-interpreter's own GIL. + + If this is :c:macro:`PyInterpreterConfig_OWN_GIL` then + :c:member:`PyInterpreterConfig.use_main_obmalloc` must be ``0``. + + +.. c:function:: PyStatus Py_NewInterpreterFromConfig(PyThreadState **tstate_p, const PyInterpreterConfig *config) + + .. index:: + pair: module; builtins + pair: module; __main__ + pair: module; sys + single: stdout (in module sys) + single: stderr (in module sys) + single: stdin (in module sys) + + Create a new sub-interpreter. This is an (almost) totally separate environment + for the execution of Python code. In particular, the new interpreter has + separate, independent versions of all imported modules, including the + fundamental modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`. The + table of loaded modules (``sys.modules``) and the module search path + (``sys.path``) are also separate. The new environment has no ``sys.argv`` + variable. It has new standard I/O stream file objects ``sys.stdin``, + ``sys.stdout`` and ``sys.stderr`` (however these refer to the same underlying + file descriptors). + + The given *config* controls the options with which the interpreter + is initialized. + + Upon success, *tstate_p* will be set to the first :term:`thread state` + created in the new sub-interpreter. This thread state is + :term:`attached `. + Note that no actual thread is created; see the discussion of thread states + below. If creation of the new interpreter is unsuccessful, + *tstate_p* is set to ``NULL``; + no exception is set since the exception state is stored in the + :term:`attached thread state`, which might not exist. + + Like all other Python/C API functions, an :term:`attached thread state` + must be present before calling this function, but it might be detached upon + returning. On success, the returned thread state will be :term:`attached `. + If the sub-interpreter is created with its own :term:`GIL` then the + :term:`attached thread state` of the calling interpreter will be detached. + When the function returns, the new interpreter's :term:`thread state` + will be :term:`attached ` to the current thread and + the previous interpreter's :term:`attached thread state` will remain detached. + + .. versionadded:: 3.12 + + Sub-interpreters are most effective when isolated from each other, + with certain functionality restricted:: + + PyInterpreterConfig config = { + .use_main_obmalloc = 0, + .allow_fork = 0, + .allow_exec = 0, + .allow_threads = 1, + .allow_daemon_threads = 0, + .check_multi_interp_extensions = 1, + .gil = PyInterpreterConfig_OWN_GIL, + }; + PyThreadState *tstate = NULL; + PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config); + if (PyStatus_Exception(status)) { + Py_ExitStatusException(status); + } + + Note that the config is used only briefly and does not get modified. + During initialization the config's values are converted into various + :c:type:`PyInterpreterState` values. A read-only copy of the config + may be stored internally on the :c:type:`PyInterpreterState`. + + .. index:: + single: Py_FinalizeEx (C function) + single: Py_Initialize (C function) + + Extension modules are shared between (sub-)interpreters as follows: + + * For modules using multi-phase initialization, + e.g. :c:func:`PyModule_FromDefAndSpec`, a separate module object is + created and initialized for each interpreter. + Only C-level static and global variables are shared between these + module objects. + + * For modules using legacy + :ref:`single-phase initialization `, + e.g. :c:func:`PyModule_Create`, the first time a particular extension + is imported, it is initialized normally, and a (shallow) copy of its + module's dictionary is squirreled away. + When the same extension is imported by another (sub-)interpreter, a new + module is initialized and filled with the contents of this copy; the + extension's ``init`` function is not called. + Objects in the module's dictionary thus end up shared across + (sub-)interpreters, which might cause unwanted behavior (see + `Bugs and caveats`_ below). + + Note that this is different from what happens when an extension is + imported after the interpreter has been completely re-initialized by + calling :c:func:`Py_FinalizeEx` and :c:func:`Py_Initialize`; in that + case, the extension's ``initmodule`` function *is* called again. + As with multi-phase initialization, this means that only C-level static + and global variables are shared between these modules. + + .. index:: single: close (in module os) + + +.. c:function:: PyThreadState* Py_NewInterpreter(void) + + .. index:: + pair: module; builtins + pair: module; __main__ + pair: module; sys + single: stdout (in module sys) + single: stderr (in module sys) + single: stdin (in module sys) + + Create a new sub-interpreter. This is essentially just a wrapper + around :c:func:`Py_NewInterpreterFromConfig` with a config that + preserves the existing behavior. The result is an unisolated + sub-interpreter that shares the main interpreter's GIL, allows + fork/exec, allows daemon threads, and allows single-phase init + modules. + + +.. c:function:: void Py_EndInterpreter(PyThreadState *tstate) + + .. index:: single: Py_FinalizeEx (C function) + + Destroy the (sub-)interpreter represented by the given :term:`thread state`. + The given thread state must be :term:`attached `. + When the call returns, there will be no :term:`attached thread state`. + All thread states associated with this interpreter are destroyed. + + :c:func:`Py_FinalizeEx` will destroy all sub-interpreters that + haven't been explicitly destroyed at that point. + + +.. _per-interpreter-gil: + +A per-interpreter GIL +--------------------- + +.. versionadded:: 3.12 + +Using :c:func:`Py_NewInterpreterFromConfig` you can create +a sub-interpreter that is completely isolated from other interpreters, +including having its own GIL. The most important benefit of this +isolation is that such an interpreter can execute Python code without +being blocked by other interpreters or blocking any others. Thus a +single Python process can truly take advantage of multiple CPU cores +when running Python code. The isolation also encourages a different +approach to concurrency than that of just using threads. +(See :pep:`554` and :pep:`684`.) + +Using an isolated interpreter requires vigilance in preserving that +isolation. That especially means not sharing any objects or mutable +state without guarantees about thread-safety. Even objects that are +otherwise immutable (e.g. ``None``, ``(1, 5)``) can't normally be shared +because of the refcount. One simple but less-efficient approach around +this is to use a global lock around all use of some state (or object). +Alternately, effectively immutable objects (like integers or strings) +can be made safe in spite of their refcounts by making them :term:`immortal`. +In fact, this has been done for the builtin singletons, small integers, +and a number of other builtin objects. + +If you preserve isolation then you will have access to proper multi-core +computing without the complications that come with free-threading. +Failure to preserve isolation will expose you to the full consequences +of free-threading, including races and hard-to-debug crashes. + +Aside from that, one of the main challenges of using multiple isolated +interpreters is how to communicate between them safely (not break +isolation) and efficiently. The runtime and stdlib do not provide +any standard approach to this yet. A future stdlib module would help +mitigate the effort of preserving isolation and expose effective tools +for communicating (and sharing) data between interpreters. + + +Bugs and caveats +---------------- + +Because sub-interpreters (and the main interpreter) are part of the same +process, the insulation between them isn't perfect --- for example, using +low-level file operations like :func:`os.close` they can +(accidentally or maliciously) affect each other's open files. Because of the +way extensions are shared between (sub-)interpreters, some extensions may not +work properly; this is especially likely when using single-phase initialization +or (static) global variables. +It is possible to insert objects created in one sub-interpreter into +a namespace of another (sub-)interpreter; this should be avoided if possible. + +Special care should be taken to avoid sharing user-defined functions, +methods, instances or classes between sub-interpreters, since import +operations executed by such objects may affect the wrong (sub-)interpreter's +dictionary of loaded modules. It is equally important to avoid sharing +objects from which the above are reachable. + +Also note that combining this functionality with ``PyGILState_*`` APIs +is delicate, because these APIs assume a bijection between Python thread states +and OS-level threads, an assumption broken by the presence of sub-interpreters. +It is highly recommended that you don't switch sub-interpreters between a pair +of matching :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release` calls. +Furthermore, extensions (such as :mod:`ctypes`) using these APIs to allow calling +of Python code from non-Python created threads will probably be broken when using +sub-interpreters. + + +High-level APIs +--------------- + +.. c:type:: PyInterpreterState + + This data structure represents the state shared by a number of cooperating + threads. Threads belonging to the same interpreter share their module + administration and a few other internal items. There are no public members in + this structure. + + Threads belonging to different interpreters initially share nothing, except + process state like available memory, open file descriptors and such. The global + interpreter lock is also shared by all threads, regardless of to which + interpreter they belong. + + .. versionchanged:: 3.12 + + :pep:`684` introduced the possibility + of a :ref:`per-interpreter GIL `. + See :c:func:`Py_NewInterpreterFromConfig`. + + +.. c:function:: PyInterpreterState* PyInterpreterState_Get(void) + + Get the current interpreter. + + Issue a fatal error if there is no :term:`attached thread state`. + It cannot return NULL. + + .. versionadded:: 3.9 + + +.. c:function:: int64_t PyInterpreterState_GetID(PyInterpreterState *interp) + + Return the interpreter's unique ID. If there was any error in doing + so then ``-1`` is returned and an error is set. + + The caller must have an :term:`attached thread state`. + + .. versionadded:: 3.7 + + +.. c:function:: PyObject* PyInterpreterState_GetDict(PyInterpreterState *interp) + + Return a dictionary in which interpreter-specific data may be stored. + If this function returns ``NULL`` then no exception has been raised and + the caller should assume no interpreter-specific dict is available. + + This is not a replacement for :c:func:`PyModule_GetState()`, which + extensions should use to store interpreter-specific state information. + + The returned dictionary is borrowed from the interpreter and is valid until + interpreter shutdown. + + .. versionadded:: 3.8 + + +.. c:type:: PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) + + Type of a frame evaluation function. + + The *throwflag* parameter is used by the ``throw()`` method of generators: + if non-zero, handle the current exception. + + .. versionchanged:: 3.9 + The function now takes a *tstate* parameter. + + .. versionchanged:: 3.11 + The *frame* parameter changed from ``PyFrameObject*`` to ``_PyInterpreterFrame*``. + + +.. c:function:: _PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp) + + Get the frame evaluation function. + + See the :pep:`523` "Adding a frame evaluation API to CPython". + + .. versionadded:: 3.9 + + +.. c:function:: void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame) + + Set the frame evaluation function. + + See the :pep:`523` "Adding a frame evaluation API to CPython". + + .. versionadded:: 3.9 + +.. c:function:: void _PyInterpreterState_SetEvalFrameAllowSpecialization(PyInterpreterState *interp, int allow_specialization) + + Enables or disables specialization why a custom frame evaluator is in place. + + If *allow_specialization* is non-zero, the adaptive specializer will + continue to specialize bytecodes even though a custom eval frame function + is set. When *allow_specialization* is zero, setting a custom eval frame + disables specialization. The standard interpreter loop will continue to deopt + while a frame evaluation API is in place - the frame evaluation function needs + to handle the specialized opcodes to take advantage of this. + + .. versionadded:: 3.15 + +.. c:function:: int _PyInterpreterState_IsSpecializationEnabled(PyInterpreterState *interp) + + Return non-zero if adaptive specialization is enabled for the interpreter. + Specialization is enabled when no custom eval frame function is set, or + when one is set with *allow_specialization* enabled. + + .. versionadded:: 3.15 + + +Low-level APIs +-------------- + +All of the following functions must be called after :c:func:`Py_Initialize`. + +.. versionchanged:: 3.7 + :c:func:`Py_Initialize()` now initializes the :term:`GIL` + and sets an :term:`attached thread state`. + + +.. c:function:: PyInterpreterState* PyInterpreterState_New() + + Create a new interpreter state object. An :term:`attached thread state` is not needed, + but may optionally exist if it is necessary to serialize calls to this + function. + + .. audit-event:: cpython.PyInterpreterState_New "" c.PyInterpreterState_New + + +.. c:function:: void PyInterpreterState_Clear(PyInterpreterState *interp) + + Reset all information in an interpreter state object. There must be + an :term:`attached thread state` for the interpreter. + + .. audit-event:: cpython.PyInterpreterState_Clear "" c.PyInterpreterState_Clear + + +.. c:function:: void PyInterpreterState_Delete(PyInterpreterState *interp) + + Destroy an interpreter state object. There **should not** be an + :term:`attached thread state` for the target interpreter. The interpreter + state must have been reset with a previous call to :c:func:`PyInterpreterState_Clear`. + + +.. _advanced-debugging: + +Advanced debugger support +------------------------- + +These functions are only intended to be used by advanced debugging tools. + + +.. c:function:: PyInterpreterState* PyInterpreterState_Head() + + Return the interpreter state object at the head of the list of all such objects. + + +.. c:function:: PyInterpreterState* PyInterpreterState_Main() + + Return the main interpreter state object. + + +.. c:function:: PyInterpreterState* PyInterpreterState_Next(PyInterpreterState *interp) + + Return the next interpreter state object after *interp* from the list of all + such objects. + + +.. c:function:: PyThreadState * PyInterpreterState_ThreadHead(PyInterpreterState *interp) + + Return the pointer to the first :c:type:`PyThreadState` object in the list of + threads associated with the interpreter *interp*. + + +.. c:function:: PyThreadState* PyThreadState_Next(PyThreadState *tstate) + + Return the next thread state object after *tstate* from the list of all such + objects belonging to the same :c:type:`PyInterpreterState` object. diff --git a/Doc/c-api/synchronization.rst b/Doc/c-api/synchronization.rst new file mode 100644 index 00000000000000..6f18c047a24a92 --- /dev/null +++ b/Doc/c-api/synchronization.rst @@ -0,0 +1,351 @@ +.. highlight:: c + +.. _synchronization: + +Synchronization primitives +========================== + +The C-API provides a basic mutual exclusion lock. + +.. c:type:: PyMutex + + A mutual exclusion lock. The :c:type:`!PyMutex` should be initialized to + zero to represent the unlocked state. For example:: + + PyMutex mutex = {0}; + + Instances of :c:type:`!PyMutex` should not be copied or moved. Both the + contents and address of a :c:type:`!PyMutex` are meaningful, and it must + remain at a fixed, writable location in memory. + + .. note:: + + A :c:type:`!PyMutex` currently occupies one byte, but the size should be + considered unstable. The size may change in future Python releases + without a deprecation period. + + .. versionadded:: 3.13 + +.. c:function:: void PyMutex_Lock(PyMutex *m) + + Lock mutex *m*. If another thread has already locked it, the calling + thread will block until the mutex is unlocked. While blocked, the thread + will temporarily detach the :term:`thread state ` if one exists. + + .. versionadded:: 3.13 + +.. c:function:: void PyMutex_Unlock(PyMutex *m) + + Unlock mutex *m*. The mutex must be locked --- otherwise, the function will + issue a fatal error. + + .. versionadded:: 3.13 + +.. c:function:: int PyMutex_IsLocked(PyMutex *m) + + Returns non-zero if the mutex *m* is currently locked, zero otherwise. + + .. note:: + + This function is intended for use in assertions and debugging only and + should not be used to make concurrency control decisions, as the lock + state may change immediately after the check. + + .. versionadded:: 3.14 + +.. _python-critical-section-api: + +Python critical section API +--------------------------- + +The critical section API provides a deadlock avoidance layer on top of +per-object locks for :term:`free-threaded ` CPython. They are +intended to replace reliance on the :term:`global interpreter lock`, and are +no-ops in versions of Python with the global interpreter lock. + +Critical sections are intended to be used for custom types implemented +in C-API extensions. They should generally not be used with built-in types like +:class:`list` and :class:`dict` because their public C-APIs +already use critical sections internally, with the notable +exception of :c:func:`PyDict_Next`, which requires critical section +to be acquired externally. + +Critical sections avoid deadlocks by implicitly suspending active critical +sections, hence, they do not provide exclusive access such as provided by +traditional locks like :c:type:`PyMutex`. When a critical section is started, +the per-object lock for the object is acquired. If the code executed inside the +critical section calls C-API functions then it can suspend the critical section thereby +releasing the per-object lock, so other threads can acquire the per-object lock +for the same object. + +Variants that accept :c:type:`PyMutex` pointers rather than Python objects are also +available. Use these variants to start a critical section in a situation where +there is no :c:type:`PyObject` -- for example, when working with a C type that +does not extend or wrap :c:type:`PyObject` but still needs to call into the C +API in a manner that might lead to deadlocks. + +.. note:: + + Operations that need to lock two objects at once must use + :c:macro:`Py_BEGIN_CRITICAL_SECTION2`. You *cannot* use nested critical + sections to lock more than one object at once, because the inner critical + section may suspend the outer critical sections. This API does not provide + a way to lock more than two objects at once. + +Example usage:: + + static PyObject * + set_field(MyObject *self, PyObject *value) + { + Py_BEGIN_CRITICAL_SECTION(self); + Py_SETREF(self->field, Py_XNewRef(value)); + Py_END_CRITICAL_SECTION(); + Py_RETURN_NONE; + } + +In the above example, :c:macro:`Py_SETREF` calls :c:macro:`Py_DECREF`, which +can call arbitrary code through an object's deallocation function. The critical +section API avoids potential deadlocks due to reentrancy and lock ordering +by allowing the runtime to temporarily suspend the critical section if the +code triggered by the finalizer blocks and calls :c:func:`PyEval_SaveThread`. + +.. _critical-section-macros: + +.. c:macro:: Py_BEGIN_CRITICAL_SECTION(op) + + Acquires the per-object lock for the object *op* and begins a + critical section. + + In the free-threaded build, and when building for the + :ref:`Stable ABI `, this macro expands to:: + + { + PyCriticalSection _py_cs; + PyCriticalSection_Begin(&_py_cs, (PyObject*)(op)) + + In the default build, this macro expands to ``{``. + + .. versionadded:: 3.13 + +.. c:macro:: Py_BEGIN_CRITICAL_SECTION_MUTEX(m) + + Locks the mutex *m* and begins a critical section. + + In the free-threaded build, this macro expands to:: + + { + PyCriticalSection _py_cs; + PyCriticalSection_BeginMutex(&_py_cs, m) + + Note that unlike :c:macro:`Py_BEGIN_CRITICAL_SECTION`, there is no cast for + the argument of the macro - it must be a :c:type:`PyMutex` pointer. + + On the default build, this macro expands to ``{``. + + .. versionadded:: 3.14 + +.. c:macro:: Py_END_CRITICAL_SECTION() + + Ends the critical section and releases the per-object lock. + + In the free-threaded build, and when building for the + :ref:`Stable ABI `, this macro expands to:: + + PyCriticalSection_End(&_py_cs); + } + + In the default build, this macro expands to ``}``. + + .. versionadded:: 3.13 + +.. c:macro:: Py_BEGIN_CRITICAL_SECTION2(a, b) + + Acquires the per-object locks for the objects *a* and *b* and begins a + critical section. The locks are acquired in a consistent order (lowest + address first) to avoid lock ordering deadlocks. + + In the free-threaded build, this macro expands to:: + + { + PyCriticalSection2 _py_cs2; + PyCriticalSection2_Begin(&_py_cs2, (PyObject*)(a), (PyObject*)(b)) + + In the default build, this macro expands to ``{``. + + .. versionadded:: 3.13 + +.. c:macro:: Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2) + + Locks the mutexes *m1* and *m2* and begins a critical section. + + In the free-threaded build, and when building for the + :ref:`Stable ABI `, this macro expands to:: + + { + PyCriticalSection2 _py_cs2; + PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2) + + Note that unlike :c:macro:`Py_BEGIN_CRITICAL_SECTION2`, there is no cast for + the arguments of the macro - they must be :c:type:`PyMutex` pointers. + + On the default build, this macro expands to ``{``. + + .. versionadded:: 3.14 + +.. c:macro:: Py_END_CRITICAL_SECTION2() + + Ends the critical section and releases the per-object locks. + + In the free-threaded build, and when building for the + :ref:`Stable ABI `, this macro expands to:: + + PyCriticalSection2_End(&_py_cs2); + } + + In the default build, this macro expands to ``}``. + + .. versionadded:: 3.13 + +Low-level critical section API +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following functions and structs are exposed for cases where C macros +are not available. + +.. c:function:: void PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op) + void PyCriticalSection_End(PyCriticalSection *c) + void PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b) + void PyCriticalSection2_End(PyCriticalSection2 *c); + + To be used only as in the macro expansions + listed :ref:`earlier in this section `. + + In non-:term:`free-threaded ` builds of CPython, these + functions do nothing. + + .. versionadded:: 3.13 + +.. c:type:: PyCriticalSection + PyCriticalSection2 + + To be used only as in the macro expansions + listed :ref:`earlier in this section `. + Note that the contents of the structures are private and their meaning may + change in future Python versions. + + .. versionadded:: 3.13 + +.. c:function:: void PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m); + void PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2); + + .. (These need to be in a separate section without a Stable ABI annotation.) + + To be used only as in the macro expansions + listed :ref:`earlier in this section `. + + In non-:term:`free-threaded ` builds of CPython, these + functions do nothing. + + .. versionadded:: 3.14 + + +Legacy locking APIs +------------------- + +These APIs are obsolete since Python 3.13 with the introduction of +:c:type:`PyMutex`. + +.. versionchanged:: 3.15 + These APIs are now a simple wrapper around ``PyMutex``. + + +.. c:type:: PyThread_type_lock + + A pointer to a mutual exclusion lock. + + +.. c:type:: PyLockStatus + + The result of acquiring a lock with a timeout. + + .. c:namespace:: NULL + + .. c:enumerator:: PY_LOCK_FAILURE + + Failed to acquire the lock. + + .. c:enumerator:: PY_LOCK_ACQUIRED + + The lock was successfully acquired. + + .. c:enumerator:: PY_LOCK_INTR + + The lock was interrupted by a signal. + + +.. c:function:: PyThread_type_lock PyThread_allocate_lock(void) + + Allocate a new lock. + + On success, this function returns a lock; on failure, this + function returns ``0`` without an exception set. + + The caller does not need to hold an :term:`attached thread state`. + + .. versionchanged:: 3.15 + This function now always uses :c:type:`PyMutex`. In prior versions, this + would use a lock provided by the operating system. + + +.. c:function:: void PyThread_free_lock(PyThread_type_lock lock) + + Destroy *lock*. The lock should not be held by any thread when calling + this. + + The caller does not need to hold an :term:`attached thread state`. + + +.. c:function:: PyLockStatus PyThread_acquire_lock_timed(PyThread_type_lock lock, long long microseconds, int intr_flag) + + Acquire *lock* with a timeout. + + This will wait for *microseconds* microseconds to acquire the lock. If the + timeout expires, this function returns :c:enumerator:`PY_LOCK_FAILURE`. + If *microseconds* is ``-1``, this will wait indefinitely until the lock has + been released. + + If *intr_flag* is ``1``, acquiring the lock may be interrupted by a signal, + in which case this function returns :c:enumerator:`PY_LOCK_INTR`. Upon + interruption, it's generally expected that the caller makes a call to + :c:func:`Py_MakePendingCalls` to propagate an exception to Python code. + + If the lock is successfully acquired, this function returns + :c:enumerator:`PY_LOCK_ACQUIRED`. + + The caller does not need to hold an :term:`attached thread state`. + + +.. c:function:: int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) + + Acquire *lock*. + + If *waitflag* is ``1`` and another thread currently holds the lock, this + function will wait until the lock can be acquired and will always return + ``1``. + + If *waitflag* is ``0`` and another thread holds the lock, this function will + not wait and instead return ``0``. If the lock is not held by any other + thread, then this function will acquire it and return ``1``. + + Unlike :c:func:`PyThread_acquire_lock_timed`, acquiring the lock cannot be + interrupted by a signal. + + The caller does not need to hold an :term:`attached thread state`. + + +.. c:function:: int PyThread_release_lock(PyThread_type_lock lock) + + Release *lock*. If *lock* is not held, then this function issues a + fatal error. + + The caller does not need to hold an :term:`attached thread state`. diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst index b34936dd55e94c..ee73c1c8adaa7b 100644 --- a/Doc/c-api/sys.rst +++ b/Doc/c-api/sys.rst @@ -123,6 +123,24 @@ Operating System Utilities This is a thin wrapper around either :c:func:`!sigaction` or :c:func:`!signal`. Do not call those functions directly! + +.. c:function:: int PyOS_InterruptOccurred(void) + + Check if a :c:macro:`!SIGINT` signal has been received. + + Returns ``1`` if a :c:macro:`!SIGINT` has occurred and clears the signal flag, + or ``0`` otherwise. + + In most cases, you should prefer :c:func:`PyErr_CheckSignals` over this function. + :c:func:`!PyErr_CheckSignals` invokes the appropriate signal handlers + for all pending signals, allowing Python code to handle the signal properly. + This function only detects :c:macro:`!SIGINT` and does not invoke any Python + signal handlers. + + This function is async-signal-safe and this function cannot fail. + The caller must hold an :term:`attached thread state`. + + .. c:function:: wchar_t* Py_DecodeLocale(const char* arg, size_t *size) .. warning:: @@ -268,7 +286,7 @@ accessible to C code. They all work with the current interpreter thread's If the non-existing object should not be treated as a failure, you can use :c:func:`PySys_GetOptionalAttr` instead. - .. versionadded:: next + .. versionadded:: 3.15 .. c:function:: PyObject *PySys_GetAttrString(const char *name) @@ -279,7 +297,7 @@ accessible to C code. They all work with the current interpreter thread's If the non-existing object should not be treated as a failure, you can use :c:func:`PySys_GetOptionalAttrString` instead. - .. versionadded:: next + .. versionadded:: 3.15 .. c:function:: int PySys_GetOptionalAttr(PyObject *name, PyObject **result) @@ -293,7 +311,7 @@ accessible to C code. They all work with the current interpreter thread's * Set an exception, set *\*result* to ``NULL``, and return ``-1``, if an error occurred. - .. versionadded:: next + .. versionadded:: 3.15 .. c:function:: int PySys_GetOptionalAttrString(const char *name, PyObject **result) @@ -301,7 +319,7 @@ accessible to C code. They all work with the current interpreter thread's specified as a :c:expr:`const char*` UTF-8 encoded bytes string, rather than a :c:expr:`PyObject*`. - .. versionadded:: next + .. versionadded:: 3.15 .. c:function:: PyObject *PySys_GetObject(const char *name) @@ -316,14 +334,6 @@ accessible to C code. They all work with the current interpreter thread's case *name* is deleted from the sys module. Returns ``0`` on success, ``-1`` on error. -.. c:function:: void PySys_ResetWarnOptions() - - Reset :data:`sys.warnoptions` to an empty list. This function may be - called prior to :c:func:`Py_Initialize`. - - .. deprecated-removed:: 3.13 3.15 - Clear :data:`sys.warnoptions` and :data:`!warnings.filters` instead. - .. c:function:: void PySys_WriteStdout(const char *format, ...) Write the output string described by *format* to :data:`sys.stdout`. No diff --git a/Doc/c-api/threads.rst b/Doc/c-api/threads.rst new file mode 100644 index 00000000000000..ca34abd73d8423 --- /dev/null +++ b/Doc/c-api/threads.rst @@ -0,0 +1,994 @@ +.. highlight:: c + +.. _threads: + +Thread states and the global interpreter lock +============================================= + +.. index:: + single: global interpreter lock + single: interpreter lock + single: lock, interpreter + +Unless on a :term:`free-threaded build` of :term:`CPython`, +the Python interpreter is generally not thread-safe. In order to support +multi-threaded Python programs, there's a global lock, called the :term:`global +interpreter lock` or :term:`GIL`, that must be held by a thread before +accessing Python objects. Without the lock, even the simplest operations +could cause problems in a multi-threaded program: for example, when +two threads simultaneously increment the reference count of the same object, the +reference count could end up being incremented only once instead of twice. + +As such, only a thread that holds the GIL may operate on Python objects or +invoke Python's C API. + +.. index:: single: setswitchinterval (in module sys) + +In order to emulate concurrency, the interpreter regularly tries to switch +threads between bytecode instructions (see :func:`sys.setswitchinterval`). +This is why locks are also necessary for thread-safety in pure-Python code. + +Additionally, the global interpreter lock is released around blocking I/O +operations, such as reading or writing to a file. From the C API, this is done +by :ref:`detaching the thread state `. + + +.. index:: + single: PyThreadState (C type) + +The Python interpreter keeps some thread-local information inside +a data structure called :c:type:`PyThreadState`, known as a :term:`thread state`. +Each thread has a thread-local pointer to a :c:type:`PyThreadState`; a thread state +referenced by this pointer is considered to be :term:`attached `. + +A thread can only have one :term:`attached thread state` at a time. An attached +thread state is typically analogous with holding the GIL, except on +free-threaded builds. On builds with the GIL enabled, attaching a thread state +will block until the GIL can be acquired. However, even on builds with the GIL +disabled, it is still required to have an attached thread state, as the interpreter +needs to keep track of which threads may access Python objects. + +.. note:: + + Even on the free-threaded build, attaching a thread state may block, as the + GIL can be re-enabled or threads might be temporarily suspended (such as during + a garbage collection). + +Generally, there will always be an attached thread state when using Python's +C API, including during embedding and when implementing methods, so it's uncommon +to need to set up a thread state on your own. Only in some specific cases, such +as in a :c:macro:`Py_BEGIN_ALLOW_THREADS` block or in a fresh thread, will the +thread not have an attached thread state. +If uncertain, check if :c:func:`PyThreadState_GetUnchecked` returns ``NULL``. + +If it turns out that you do need to create a thread state, it is recommended to +use :c:func:`PyThreadState_Ensure` or :c:func:`PyThreadState_EnsureFromView`, +which will manage the thread state for you. + + +.. _detaching-thread-state: + +Detaching the thread state from extension code +---------------------------------------------- + +Most extension code manipulating the :term:`thread state` has the following simple +structure:: + + Save the thread state in a local variable. + ... Do some blocking I/O operation ... + Restore the thread state from the local variable. + +This is so common that a pair of macros exists to simplify it:: + + Py_BEGIN_ALLOW_THREADS + ... Do some blocking I/O operation ... + Py_END_ALLOW_THREADS + +.. index:: + single: Py_BEGIN_ALLOW_THREADS (C macro) + single: Py_END_ALLOW_THREADS (C macro) + +The :c:macro:`Py_BEGIN_ALLOW_THREADS` macro opens a new block and declares a +hidden local variable; the :c:macro:`Py_END_ALLOW_THREADS` macro closes the +block. + +The block above expands to the following code:: + + PyThreadState *_save; + + _save = PyEval_SaveThread(); + ... Do some blocking I/O operation ... + PyEval_RestoreThread(_save); + +.. index:: + single: PyEval_RestoreThread (C function) + single: PyEval_SaveThread (C function) + +Here is how these functions work: + +The attached thread state implies that the GIL is held for the interpreter. +To detach it, :c:func:`PyEval_SaveThread` is called and the result is stored +in a local variable. + +By detaching the thread state, the GIL is released, which allows other threads +to attach to the interpreter and execute while the current thread performs +blocking I/O. When the I/O operation is complete, the old thread state is +reattached by calling :c:func:`PyEval_RestoreThread`, which will wait until +the GIL can be acquired. + +.. note:: + Performing blocking I/O is the most common use case for detaching + the thread state, but it is also useful to call it over long-running + native code that doesn't need access to Python objects or Python's C API. + For example, the standard :mod:`zlib` and :mod:`hashlib` modules detach the + :term:`thread state ` when compressing or hashing + data. + +On a :term:`free-threaded build`, the :term:`GIL` is usually out of the question, +but **detaching the thread state is still required**, because the interpreter +periodically needs to block all threads to get a consistent view of Python objects +without the risk of race conditions. +For example, CPython currently suspends all threads for a short period of time +while running the garbage collector. + +.. warning:: + + Detaching the thread state can lead to unexpected behavior during interpreter + finalization. See :ref:`cautions-regarding-runtime-finalization` for more + details. + + +APIs +^^^^ + +The following macros are normally used without a trailing semicolon; look for +example usage in the Python source distribution. + +.. note:: + + These macros are still necessary on the :term:`free-threaded build` to prevent + deadlocks. + +.. c:macro:: Py_BEGIN_ALLOW_THREADS + + This macro expands to ``{ PyThreadState *_save; _save = PyEval_SaveThread();``. + Note that it contains an opening brace; it must be matched with a following + :c:macro:`Py_END_ALLOW_THREADS` macro. See above for further discussion of this + macro. + + +.. c:macro:: Py_END_ALLOW_THREADS + + This macro expands to ``PyEval_RestoreThread(_save); }``. Note that it contains + a closing brace; it must be matched with an earlier + :c:macro:`Py_BEGIN_ALLOW_THREADS` macro. See above for further discussion of + this macro. + + +.. c:macro:: Py_BLOCK_THREADS + + This macro expands to ``PyEval_RestoreThread(_save);``: it is equivalent to + :c:macro:`Py_END_ALLOW_THREADS` without the closing brace. + + +.. c:macro:: Py_UNBLOCK_THREADS + + This macro expands to ``_save = PyEval_SaveThread();``: it is equivalent to + :c:macro:`Py_BEGIN_ALLOW_THREADS` without the opening brace and variable + declaration. + + +.. _non-python-created-threads: +.. _c-api-foreign-threads: + + +Using the C API from foreign threads +------------------------------------ + +When threads are created using the dedicated Python APIs (such as the +:mod:`threading` module), a thread state is automatically associated with them, +However, when a thread is created from native code (for example, by a +third-party library with its own thread management), it doesn't hold an +attached thread state. + +If you need to call Python code from these threads (often this will be part +of a callback API provided by the aforementioned third-party library), +you must first register these threads with the interpreter by +creating a new thread state and attaching it. + +The easiest way to do this is through :c:func:`PyThreadState_Ensure` +or :c:func:`PyThreadState_EnsureFromView`. + +.. note:: + These functions require an argument pointing to the desired + interpreter; such a pointer can be acquired via a call to + :c:func:`PyInterpreterGuard_FromCurrent` (for ``PyThreadState_Ensure``) or + :c:func:`PyInterpreterView_FromCurrent` (for ``PyThreadState_EnsureFromView``) + from the function that creates the thread. If no pointer is available (such + as when the given native thread library doesn't provide a data argument), + :c:func:`PyInterpreterView_FromMain` can be used to get a view for the main + interpreter, but note that this will make the code incompatible with + subinterpreters. + + +For example:: + + // The return value of PyInterpreterGuard_FromCurrent() from the + // function that created this thread. + PyInterpreterGuard *guard = thread_data->guard; + + // Create a new thread state for the interpreter. + PyThreadStateToken *token = PyThreadState_Ensure(guard); + if (token == NULL) { + PyInterpreterGuard_Close(guard); + return; + } + + // We have a valid thread state -- perform Python actions here. + result = CallSomeFunction(); + // Evaluate result or handle exceptions. + + // Release the thread state. No calls to the C API are allowed beyond this + // point. + PyThreadState_Release(token); + PyInterpreterGuard_Close(guard); + + +Keep in mind that calling ``PyThreadState_Ensure`` might not always create a new +thread state, and calling ``PyThreadState_Release`` might not always detach it. +These functions may reuse an existing attached thread state, or may re-attach +a thread state that was previously attached for the current thread. + +.. seealso:: + :pep:`788` + +.. _c-api-attach-detach: + +Attaching/detaching thread states +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. c:function:: PyThreadStateToken *PyThreadState_Ensure(PyInterpreterGuard *guard) + + Ensure that the thread has an attached thread state for the + interpreter protected by *guard*, and thus can safely invoke that + interpreter. + + It is OK to call this function if the thread already has an + attached thread state, as long as there is a subsequent call to + :c:func:`PyThreadState_Release` that matches this one (meaning that "nested" + calls to this function are permitted). + + The function's effect (if any) will be reversed by the matching call to + :c:func:`PyThreadState_Release`. + + On error, this function returns ``NULL`` *without* an exception set. + Do not call :c:func:`!PyThreadState_Release` in this case. + + On success, this function returns a pointer value that must be passed + to the matching call to :c:func:`!PyThreadState_Release`. + + The conditions in which this function creates a new :term:`thread state` are + considered unstable and implementation-dependent. If you need to control the + exact lifetime of a thread state, consider using :c:func:`PyThreadState_New`. + However, do not avoid this function solely on the basis that the lifetime + of the thread state may be inconsistent across versions; changes to this + function will be done with caution and in a backwards-compatible manner. + In particular, the saving of thread-local variables and similar state will + be retained across Python versions. + + .. impl-detail:: + + The exact behavior of whether this function creates a new thread state is + described below, but be aware that this may change in the future. + + First, this function checks if an attached thread state is present. + If there is, this function then checks if the interpreter of that + thread state matches the interpreter guarded by *guard*. If that is + the case, this function simply marks the thread state as being used + by a ``PyThreadState_Ensure`` call and returns. + + If there is no attached thread state, then this function checks if any + thread state has been used by the current OS thread. (This is + returned by :c:func:`PyGILState_GetThisThreadState`.) + If there was, then this function checks if that thread state's interpreter + matches *guard*. If it does, it is re-attached and marked as used. + + Otherwise, if both of the above cases fail, a new thread state is created + for *guard*. It is then attached and marked as owned by ``PyThreadState_Ensure``. + + .. versionadded:: 3.15 + + +.. c:function:: PyThreadStateToken *PyThreadState_EnsureFromView(PyInterpreterView *view) + + Get an attached thread state for the interpreter referenced by *view*. + + The behavior and return value are the same as for :c:func:`PyThreadState_Ensure`; + additionally, if the function succeeds, the interpreter referenced by *view* will + be implicitly guarded. The guard will be released upon the corresponding + :c:func:`PyThreadState_Release` call. + + .. versionadded:: 3.15 + + +.. c:function:: void PyThreadState_Release(PyThreadStateToken *token) + + Undo a :c:func:`PyThreadState_Ensure` or + :c:func:`PyThreadState_EnsureFromView` call. + + This must be called exactly once for each successful *Ensure* call, with + *token* set to that call's return value. + + The state that was attached before the corresponding *Ensure* call + (if any) will be attached when :c:func:`PyThreadState_Release` returns. + + The exact behavior of whether this function deletes a thread state is + considered unstable and implementation-dependent. + + .. impl-detail:: + + Currently, this function will decrement an internal counter on the + attached thread state. If this counter ever reaches below zero, this + function emits a fatal error (via :c:func:`Py_FatalError`). + + If the attached thread state is owned by ``PyThreadState_Ensure``, then the + attached thread state will be deallocated and deleted upon the internal counter + reaching zero. Otherwise, nothing happens when the counter reaches zero. + + .. versionadded:: 3.15 + +.. c:type:: PyThreadStateToken + + An opaque token retrieved from a :c:func:`PyThreadState_Ensure` call + and passed to a corresponding :c:func:`PyThreadState_Release` call. + + +.. _legacy-api: +.. _gilstate: + +GIL-state APIs +-------------- + +The following APIs are generally not compatible with subinterpreters and +will hang the process during interpreter finalization (see +:ref:`cautions-regarding-runtime-finalization`). As such, these APIs were +:term:`soft deprecated` in Python 3.15 in favor of the :ref:`new APIs +`. + + +.. c:type:: PyGILState_STATE + + The type of the value returned by :c:func:`PyGILState_Ensure` and passed to + :c:func:`PyGILState_Release`. + + .. c:enumerator:: PyGILState_LOCKED + + The GIL was already held when :c:func:`PyGILState_Ensure` was called. + + .. c:enumerator:: PyGILState_UNLOCKED + + The GIL was not held when :c:func:`PyGILState_Ensure` was called. + + +.. c:function:: PyGILState_STATE PyGILState_Ensure() + + Ensure that the current thread is ready to call the Python C API regardless + of the current state of Python, or of the :term:`attached thread state`. This may + be called as many times as desired by a thread as long as each call is + matched with a call to :c:func:`PyGILState_Release`. In general, other + thread-related APIs may be used between :c:func:`PyGILState_Ensure` and + :c:func:`PyGILState_Release` calls as long as the thread state is restored to + its previous state before the Release(). For example, normal usage of the + :c:macro:`Py_BEGIN_ALLOW_THREADS` and :c:macro:`Py_END_ALLOW_THREADS` macros is + acceptable. + + The return value is an opaque "handle" to the :term:`attached thread state` when + :c:func:`PyGILState_Ensure` was called, and must be passed to + :c:func:`PyGILState_Release` to ensure Python is left in the same state. Even + though recursive calls are allowed, these handles *cannot* be shared - each + unique call to :c:func:`PyGILState_Ensure` must save the handle for its call + to :c:func:`PyGILState_Release`. + + When the function returns, there will be an :term:`attached thread state` + and the thread will be able to call arbitrary Python code. + + This function has no way to return an error. As such, errors are either fatal + (that is, they send ``SIGABRT`` and crash the process; see + :c:func:`Py_FatalError`), or the thread will be permanently blocked (such as + during interpreter finalization). + + .. warning:: + Calling this function when the interpreter is finalizing will + infinitely hang the thread, which may cause deadlocks. + :ref:`cautions-regarding-runtime-finalization` for more details. + + In addition, this function generally does not work with subinterpreters + when used from foreign threads, because this function has no way of + knowing which interpreter created the thread (and as such, will implicitly + pick the main interpreter). + + .. versionchanged:: 3.14 + Hangs the current thread, rather than terminating it, if called while the + interpreter is finalizing. + + .. soft-deprecated:: 3.15 + Use :c:func:`PyThreadState_Ensure` or + :c:func:`PyThreadState_EnsureFromView` instead. + + +.. c:function:: void PyGILState_Release(PyGILState_STATE) + + Release any resources previously acquired. After this call, Python's state will + be the same as it was prior to the corresponding :c:func:`PyGILState_Ensure` call + (but generally this state will be unknown to the caller, hence the use of the + GIL-state API). + + Every call to :c:func:`PyGILState_Ensure` must be matched by a call to + :c:func:`PyGILState_Release` on the same thread. + + .. soft-deprecated:: 3.15 + Use :c:func:`PyThreadState_Release` instead. + + +.. c:function:: PyThreadState* PyGILState_GetThisThreadState() + + Get the :term:`thread state` that was most recently :term:`attached + ` for this thread. (If the most recent thread state + has been deleted, this returns ``NULL``.) + + If the caller has an attached thread state, it is returned. + + In other terms, this function returns the thread state that will be used by + :c:func:`PyGILState_Ensure`. If this returns ``NULL``, then + ``PyGILState_Ensure`` will create a new thread state. + + This function cannot fail. + + .. soft-deprecated:: 3.15 + Use :c:func:`PyThreadState_Get` or :c:func:`PyThreadState_GetUnchecked` + instead. + + +.. c:function:: int PyGILState_Check() + + Return ``1`` if the current thread has an :term:`attached thread state` + that matches the thread state returned by + :c:func:`PyGILState_GetThisThreadState`. If the caller has no attached thread + state or it otherwise doesn't match, then this returns ``0``. + + If the current Python process has ever created a subinterpreter, this + function will *always* return ``1``. + + This is mainly a helper/diagnostic function. + + .. versionadded:: 3.4 + + .. soft-deprecated:: 3.15 + Use ``PyThreadState_GetUnchecked() != NULL`` instead. + + +.. _fork-and-threads: + +Cautions about fork() +--------------------- + +Another important thing to note about threads is their behaviour in the face +of the C :c:func:`fork` call. On most systems with :c:func:`fork`, after a +process forks only the thread that issued the fork will exist. This has a +concrete impact both on how locks must be handled and on all stored state +in CPython's runtime. + +The fact that only the "current" thread remains +means any locks held by other threads will never be released. Python solves +this for :func:`os.fork` by acquiring the locks it uses internally before +the fork, and releasing them afterwards. In addition, it resets any +:ref:`lock-objects` in the child. When extending or embedding Python, there +is no way to inform Python of additional (non-Python) locks that need to be +acquired before or reset after a fork. OS facilities such as +:c:func:`!pthread_atfork` would need to be used to accomplish the same thing. +Additionally, when extending or embedding Python, calling :c:func:`fork` +directly rather than through :func:`os.fork` (and returning to or calling +into Python) may result in a deadlock by one of Python's internal locks +being held by a thread that is defunct after the fork. +:c:func:`PyOS_AfterFork_Child` tries to reset the necessary locks, but is not +always able to. + +The fact that all other threads go away also means that CPython's +runtime state there must be cleaned up properly, which :func:`os.fork` +does. This means finalizing all other :c:type:`PyThreadState` objects +belonging to the current interpreter and all other +:c:type:`PyInterpreterState` objects. Due to this and the special +nature of the :ref:`"main" interpreter `, +:c:func:`fork` should only be called in that interpreter's "main" +thread, where the CPython global runtime was originally initialized. +The only exception is if :c:func:`exec` will be called immediately +after. + + +High-level APIs +--------------- + +These are the most commonly used types and functions when writing multi-threaded +C extensions. + + +.. c:type:: PyThreadState + + This data structure represents the state of a single thread. The only public + data member is: + + .. c:member:: PyInterpreterState *interp + + This thread's interpreter state. + + +.. c:function:: void PyEval_InitThreads() + + .. index:: + single: PyEval_AcquireThread() + single: PyEval_ReleaseThread() + single: PyEval_SaveThread() + single: PyEval_RestoreThread() + + Deprecated function which does nothing. + + In Python 3.6 and older, this function created the GIL if it didn't exist. + + .. versionchanged:: 3.9 + The function now does nothing. + + .. versionchanged:: 3.7 + This function is now called by :c:func:`Py_Initialize()`, so you don't + have to call it yourself anymore. + + .. versionchanged:: 3.2 + This function cannot be called before :c:func:`Py_Initialize()` anymore. + + .. deprecated:: 3.9 + + .. index:: pair: module; _thread + + +.. c:function:: PyThreadState* PyEval_SaveThread() + + Detach the :term:`attached thread state` and return it. + The thread will have no :term:`thread state` upon returning. + + +.. c:function:: void PyEval_RestoreThread(PyThreadState *tstate) + + Set the :term:`attached thread state` to *tstate*. + The passed :term:`thread state` **should not** be :term:`attached `, + otherwise deadlock ensues. *tstate* will be attached upon returning. + + .. note:: + Calling this function from a thread when the runtime is finalizing will + hang the thread until the program exits, even if the thread was not + created by Python. Refer to + :ref:`cautions-regarding-runtime-finalization` for more details. + + .. versionchanged:: 3.14 + Hangs the current thread, rather than terminating it, if called while the + interpreter is finalizing. + +.. c:function:: PyThreadState* PyThreadState_Get() + + Return the :term:`attached thread state`. If the thread has no attached + thread state, (such as when inside of :c:macro:`Py_BEGIN_ALLOW_THREADS` + block), then this issues a fatal error (so that the caller needn't check + for ``NULL``). + + See also :c:func:`PyThreadState_GetUnchecked`. + +.. c:function:: PyThreadState* PyThreadState_GetUnchecked() + + Similar to :c:func:`PyThreadState_Get`, but don't kill the process with a + fatal error if it is NULL. The caller is responsible to check if the result + is NULL. + + .. versionadded:: 3.13 + In Python 3.5 to 3.12, the function was private and known as + ``_PyThreadState_UncheckedGet()``. + + +.. c:function:: PyThreadState* PyThreadState_Swap(PyThreadState *tstate) + + Set the :term:`attached thread state` to *tstate*, and return the + :term:`thread state` that was attached prior to calling. + + This function is safe to call without an :term:`attached thread state`; it + will simply return ``NULL`` indicating that there was no prior thread state. + + .. seealso:: + :c:func:`PyEval_ReleaseThread` + + .. note:: + Similar to :c:func:`PyGILState_Ensure`, this function will hang the + thread if the runtime is finalizing. + + +Low-level APIs +-------------- + +.. c:function:: PyThreadState* PyThreadState_New(PyInterpreterState *interp) + + Create a new thread state object belonging to the given interpreter object. + An :term:`attached thread state` is not needed. + +.. c:function:: void PyThreadState_Clear(PyThreadState *tstate) + + Reset all information in a :term:`thread state` object. *tstate* + must be :term:`attached ` + + .. versionchanged:: 3.9 + This function now calls the :c:member:`!PyThreadState.on_delete` callback. + Previously, that happened in :c:func:`PyThreadState_Delete`. + + .. versionchanged:: 3.13 + The :c:member:`!PyThreadState.on_delete` callback was removed. + + +.. c:function:: void PyThreadState_Delete(PyThreadState *tstate) + + Destroy a :term:`thread state` object. *tstate* should not + be :term:`attached ` to any thread. + *tstate* must have been reset with a previous call to + :c:func:`PyThreadState_Clear`. + + +.. c:function:: void PyThreadState_DeleteCurrent(void) + + Detach the :term:`attached thread state` (which must have been reset + with a previous call to :c:func:`PyThreadState_Clear`) and then destroy it. + + No :term:`thread state` will be :term:`attached ` upon + returning. + +.. c:function:: PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) + + Get the current frame of the Python thread state *tstate*. + + Return a :term:`strong reference`. Return ``NULL`` if no frame is currently + executing. + + See also :c:func:`PyEval_GetFrame`. + + *tstate* must not be ``NULL``, and must be :term:`attached `. + + .. versionadded:: 3.9 + + +.. c:function:: uint64_t PyThreadState_GetID(PyThreadState *tstate) + + Get the unique :term:`thread state` identifier of the Python thread state *tstate*. + + *tstate* must not be ``NULL``, and must be :term:`attached `. + + .. versionadded:: 3.9 + + +.. c:function:: PyInterpreterState* PyThreadState_GetInterpreter(PyThreadState *tstate) + + Get the interpreter of the Python thread state *tstate*. + + *tstate* must not be ``NULL``, and must be :term:`attached `. + + .. versionadded:: 3.9 + + +.. c:function:: void PyThreadState_EnterTracing(PyThreadState *tstate) + + Suspend tracing and profiling in the Python thread state *tstate*. + + Resume them using the :c:func:`PyThreadState_LeaveTracing` function. + + .. versionadded:: 3.11 + + +.. c:function:: void PyThreadState_LeaveTracing(PyThreadState *tstate) + + Resume tracing and profiling in the Python thread state *tstate* suspended + by the :c:func:`PyThreadState_EnterTracing` function. + + See also :c:func:`PyEval_SetTrace` and :c:func:`PyEval_SetProfile` + functions. + + .. versionadded:: 3.11 + + +.. c:function:: int PyUnstable_ThreadState_SetStackProtection(PyThreadState *tstate, void *stack_start_addr, size_t stack_size) + + Set the stack protection start address and stack protection size + of a Python thread state. + + On success, return ``0``. + On failure, set an exception and return ``-1``. + + CPython implements :ref:`recursion control ` for C code by raising + :py:exc:`RecursionError` when it notices that the machine execution stack is close + to overflow. See for example the :c:func:`Py_EnterRecursiveCall` function. + For this, it needs to know the location of the current thread's stack, which it + normally gets from the operating system. + When the stack is changed, for example using context switching techniques like the + Boost library's ``boost::context``, you must call + :c:func:`~PyUnstable_ThreadState_SetStackProtection` to inform CPython of the change. + + Call :c:func:`~PyUnstable_ThreadState_SetStackProtection` either before + or after changing the stack. + Do not call any other Python C API between the call and the stack + change. + + See :c:func:`PyUnstable_ThreadState_ResetStackProtection` for undoing this operation. + + .. versionadded:: 3.15 + + +.. c:function:: void PyUnstable_ThreadState_ResetStackProtection(PyThreadState *tstate) + + Reset the stack protection start address and stack protection size + of a Python thread state to the operating system defaults. + + See :c:func:`PyUnstable_ThreadState_SetStackProtection` for an explanation. + + .. versionadded:: 3.15 + + +.. c:function:: PyObject* PyThreadState_GetDict() + + Return a dictionary in which extensions can store thread-specific state + information. Each extension should use a unique key to store a state in + the dictionary. It is okay to call this function when no :term:`thread state` + is :term:`attached `. If this function returns + ``NULL`` and no exception has been raised, then the caller should assume no + thread state is attached. + + +.. c:function:: void PyEval_AcquireThread(PyThreadState *tstate) + + :term:`Attach ` *tstate* to the current thread, + which must not be ``NULL`` or already :term:`attached `. + + The calling thread must not already have an :term:`attached thread state`. + + .. note:: + Calling this function from a thread when the runtime is finalizing will + hang the thread until the program exits, even if the thread was not + created by Python. Refer to + :ref:`cautions-regarding-runtime-finalization` for more details. + + .. versionchanged:: 3.8 + Updated to be consistent with :c:func:`PyEval_RestoreThread`, + :c:func:`Py_END_ALLOW_THREADS`, and :c:func:`PyGILState_Ensure`, + and terminate the current thread if called while the interpreter is finalizing. + + .. versionchanged:: 3.14 + Hangs the current thread, rather than terminating it, if called while the + interpreter is finalizing. + + :c:func:`PyEval_RestoreThread` is a higher-level function which is always + available (even when threads have not been initialized). + + +.. c:function:: void PyEval_ReleaseThread(PyThreadState *tstate) + + Detach the :term:`attached thread state`. + The *tstate* argument, which must not be ``NULL``, is only used to check + that it represents the :term:`attached thread state` --- if it isn't, a fatal error is + reported. + + :c:func:`PyEval_SaveThread` is a higher-level function which is always + available (even when threads have not been initialized). + + +Asynchronous notifications +========================== + +A mechanism is provided to make asynchronous notifications to the main +interpreter thread. These notifications take the form of a function +pointer and a void pointer argument. + + +.. c:function:: int Py_AddPendingCall(int (*func)(void *), void *arg) + + Schedule a function to be called from the main interpreter thread. On + success, ``0`` is returned and *func* is queued for being called in the + main thread. On failure, ``-1`` is returned without setting any exception. + + When successfully queued, *func* will be *eventually* called from the + main interpreter thread with the argument *arg*. It will be called + asynchronously with respect to normally running Python code, but with + both these conditions met: + + * on a :term:`bytecode` boundary; + * with the main thread holding an :term:`attached thread state` + (*func* can therefore use the full C API). + + *func* must return ``0`` on success, or ``-1`` on failure with an exception + set. *func* won't be interrupted to perform another asynchronous + notification recursively, but it can still be interrupted to switch + threads if the :term:`thread state ` is detached. + + This function doesn't need an :term:`attached thread state`. However, to call this + function in a subinterpreter, the caller must have an :term:`attached thread state`. + Otherwise, the function *func* can be scheduled to be called from the wrong interpreter. + + .. warning:: + This is a low-level function, only useful for very special cases. + There is no guarantee that *func* will be called as quick as + possible. If the main thread is busy executing a system call, + *func* won't be called before the system call returns. This + function is generally **not** suitable for calling Python code from + arbitrary C threads. Instead, use :c:func:`PyThreadState_EnsureFromView`. + + .. versionadded:: 3.1 + + .. versionchanged:: 3.9 + If this function is called in a subinterpreter, the function *func* is + now scheduled to be called from the subinterpreter, rather than being + called from the main interpreter. Each subinterpreter now has its own + list of scheduled calls. + + .. versionchanged:: 3.12 + This function now always schedules *func* to be run in the main + interpreter. + + +.. c:function:: int Py_MakePendingCalls(void) + + Execute all pending calls. This is usually executed automatically by the + interpreter. + + This function returns ``0`` on success, and returns ``-1`` with an exception + set on failure. + + If this is not called in the main thread of the main + interpreter, this function does nothing and returns ``0``. + The caller must hold an :term:`attached thread state`. + + .. versionadded:: 3.1 + + .. versionchanged:: 3.12 + This function only runs pending calls in the main interpreter. + + +.. c:function:: int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) + + Schedule an exception to be raised asynchronously in a thread. + If the thread has a previously scheduled exception, it is overwritten. + + The *id* argument is the thread id of the target thread, as returned by + :c:func:`PyThread_get_thread_ident`. + *exc* is the class of the exception to be raised, or ``NULL`` to clear + the pending exception (if any). + + Return the number of affected thread states. + This is normally ``1`` if *id* is found, even when no change was + made (the given *exc* was already pending, or *exc* is ``NULL`` but + no exception is pending). + If the thread id isn't found, return ``0``. This raises no exceptions. + + To prevent naive misuse, you must write your own C extension to call this. + This function must be called with an :term:`attached thread state`. + This function does not steal any references to *exc*. + This function does not necessarily interrupt system calls such as + :py:func:`~time.sleep`. + + .. versionchanged:: 3.7 + The type of the *id* parameter changed from :c:expr:`long` to + :c:expr:`unsigned long`. + + +Operating system thread APIs +============================ + +.. c:macro:: PYTHREAD_INVALID_THREAD_ID + + Sentinel value for an invalid thread ID. + + This is currently equivalent to ``(unsigned long)-1``. + + +.. c:function:: unsigned long PyThread_start_new_thread(void (*func)(void *), void *arg) + + Start function *func* in a new thread with argument *arg*. + The resulting thread is not intended to be joined. + + *func* must not be ``NULL``, but *arg* may be ``NULL``. + + On success, this function returns the identifier of the new thread; on failure, + this returns :c:macro:`PYTHREAD_INVALID_THREAD_ID`. + + The caller does not need to hold an :term:`attached thread state`. + + +.. c:function:: unsigned long PyThread_get_thread_ident(void) + + Return the identifier of the current thread, which will never be zero. + + This function cannot fail, and the caller does not need to hold an + :term:`attached thread state`. + + .. seealso:: + :py:func:`threading.get_ident` and :py:attr:`threading.Thread.ident` + expose this identifier to Python. + + +.. c:function:: PyObject *PyThread_GetInfo(void) + + Get general information about the current thread in the form of a + :ref:`struct sequence ` object. This information is + accessible as :py:attr:`sys.thread_info` in Python. + + On success, this returns a new :term:`strong reference` to the thread + information; on failure, this returns ``NULL`` with an exception set. + + The caller must hold an :term:`attached thread state`. + + +.. c:macro:: PY_HAVE_THREAD_NATIVE_ID + + This macro is defined when the system supports native thread IDs. + + +.. c:function:: unsigned long PyThread_get_thread_native_id(void) + + Get the native identifier of the current thread as it was assigned by the operating + system's kernel, which will never be less than zero. + + This function is only available when :c:macro:`PY_HAVE_THREAD_NATIVE_ID` is + defined. + + This function cannot fail, and the caller does not need to hold an + :term:`attached thread state`. + + .. seealso:: + :py:func:`threading.get_native_id` + + +.. c:function:: void PyThread_exit_thread(void) + + Terminate the current thread. This function is generally considered unsafe + and should be avoided. It is kept solely for backwards compatibility. + + This function is only safe to call if all functions in the full call + stack are written to safely allow it. + + .. warning:: + + If the current system uses POSIX threads (also known as "pthreads"), + this calls :manpage:`pthread_exit(3)`, which attempts to unwind the stack + and call C++ destructors on some libc implementations. However, if a + ``noexcept`` function is reached, it may terminate the process. + Other systems, such as macOS, do unwinding. + + On Windows, this function calls ``_endthreadex()``, which kills the thread + without calling C++ destructors. + + In any case, there is a risk of corruption on the thread's stack. + + .. deprecated:: 3.14 + + +.. c:function:: void PyThread_init_thread(void) + + Initialize ``PyThread*`` APIs. Python executes this function automatically, + so there's little need to call it from an extension module. + + +.. c:function:: int PyThread_set_stacksize(size_t size) + + Set the stack size of the current thread to *size* bytes. + + This function returns ``0`` on success, ``-1`` if *size* is invalid, or + ``-2`` if the system does not support changing the stack size. This function + does not set exceptions. + + The caller does not need to hold an :term:`attached thread state`. + + +.. c:function:: size_t PyThread_get_stacksize(void) + + Return the stack size of the current thread in bytes, or ``0`` if the system's + default stack size is in use. + + The caller does not need to hold an :term:`attached thread state`. diff --git a/Doc/c-api/tls.rst b/Doc/c-api/tls.rst new file mode 100644 index 00000000000000..93ac5557141e25 --- /dev/null +++ b/Doc/c-api/tls.rst @@ -0,0 +1,155 @@ +.. highlight:: c + +.. _thread-local-storage: + +Thread-local storage support +============================ + +The Python interpreter provides low-level support for thread-local storage +(TLS) which wraps the underlying native TLS implementation to support the +Python-level thread-local storage API (:class:`threading.local`). The +CPython C level APIs are similar to those offered by pthreads and Windows: +use a thread key and functions to associate a :c:expr:`void*` value per +thread. + +A :term:`thread state` does *not* need to be :term:`attached ` +when calling these functions; they supply their own locking. + +Note that :file:`Python.h` does not include the declaration of the TLS APIs, +you need to include :file:`pythread.h` to use thread-local storage. + +.. note:: + None of these API functions handle memory management on behalf of the + :c:expr:`void*` values. You need to allocate and deallocate them yourself. + If the :c:expr:`void*` values happen to be :c:expr:`PyObject*`, these + functions don't do refcount operations on them either. + +.. _thread-specific-storage-api: + +Thread-specific storage API +--------------------------- + +The thread-specific storage (TSS) API was introduced to supersede the use of the existing TLS API within the +CPython interpreter. This API uses a new type :c:type:`Py_tss_t` instead of +:c:expr:`int` to represent thread keys. + +.. versionadded:: 3.7 + +.. seealso:: "A New C-API for Thread-Local Storage in CPython" (:pep:`539`) + + +.. c:type:: Py_tss_t + + This data structure represents the state of a thread key, the definition of + which may depend on the underlying TLS implementation, and it has an + internal field representing the key's initialization state. There are no + public members in this structure. + + When :ref:`Py_LIMITED_API ` is not defined, static allocation of + this type by :c:macro:`Py_tss_NEEDS_INIT` is allowed. + + +.. c:macro:: Py_tss_NEEDS_INIT + + This macro expands to the initializer for :c:type:`Py_tss_t` variables. + Note that this macro won't be defined with :ref:`Py_LIMITED_API `. + + +Dynamic allocation +------------------ + +Dynamic allocation of the :c:type:`Py_tss_t`, required in extension modules +built with :ref:`Py_LIMITED_API `, where static allocation of this type +is not possible due to its implementation being opaque at build time. + + +.. c:function:: Py_tss_t* PyThread_tss_alloc() + + Return a value which is the same state as a value initialized with + :c:macro:`Py_tss_NEEDS_INIT`, or ``NULL`` in the case of dynamic allocation + failure. + + +.. c:function:: void PyThread_tss_free(Py_tss_t *key) + + Free the given *key* allocated by :c:func:`PyThread_tss_alloc`, after + first calling :c:func:`PyThread_tss_delete` to ensure any associated + thread locals have been unassigned. This is a no-op if the *key* + argument is ``NULL``. + + .. note:: + A freed key becomes a dangling pointer. You should reset the key to + ``NULL``. + + +Methods +------- + +The parameter *key* of these functions must not be ``NULL``. Moreover, the +behaviors of :c:func:`PyThread_tss_set` and :c:func:`PyThread_tss_get` are +undefined if the given :c:type:`Py_tss_t` has not been initialized by +:c:func:`PyThread_tss_create`. + + +.. c:function:: int PyThread_tss_is_created(Py_tss_t *key) + + Return a non-zero value if the given :c:type:`Py_tss_t` has been initialized + by :c:func:`PyThread_tss_create`. + + +.. c:function:: int PyThread_tss_create(Py_tss_t *key) + + Return a zero value on successful initialization of a TSS key. The behavior + is undefined if the value pointed to by the *key* argument is not + initialized by :c:macro:`Py_tss_NEEDS_INIT`. This function can be called + repeatedly on the same key -- calling it on an already initialized key is a + no-op and immediately returns success. + + +.. c:function:: void PyThread_tss_delete(Py_tss_t *key) + + Destroy a TSS key to forget the values associated with the key across all + threads, and change the key's initialization state to uninitialized. A + destroyed key is able to be initialized again by + :c:func:`PyThread_tss_create`. This function can be called repeatedly on + the same key -- calling it on an already destroyed key is a no-op. + + +.. c:function:: int PyThread_tss_set(Py_tss_t *key, void *value) + + Return a zero value to indicate successfully associating a :c:expr:`void*` + value with a TSS key in the current thread. Each thread has a distinct + mapping of the key to a :c:expr:`void*` value. + + +.. c:function:: void* PyThread_tss_get(Py_tss_t *key) + + Return the :c:expr:`void*` value associated with a TSS key in the current + thread. This returns ``NULL`` if no value is associated with the key in the + current thread. + + +.. _thread-local-storage-api: + +Legacy APIs +----------- + +.. deprecated:: 3.7 + This API is superseded by the + :ref:`thread-specific storage (TSS) API `. + +.. note:: + This version of the API does not support platforms where the native TLS key + is defined in a way that cannot be safely cast to ``int``. On such platforms, + :c:func:`PyThread_create_key` will return immediately with a failure status, + and the other TLS functions will all be no-ops on such platforms. + +Due to the compatibility problem noted above, this version of the API should not +be used in new code. + +.. c:function:: int PyThread_create_key() +.. c:function:: void PyThread_delete_key(int key) +.. c:function:: int PyThread_set_key_value(int key, void *value) +.. c:function:: void* PyThread_get_key_value(int key) +.. c:function:: void PyThread_delete_key_value(int key) +.. c:function:: void PyThread_ReInitTLS() diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst index 815afddad19df1..ba4c6b93de4c11 100644 --- a/Doc/c-api/tuple.rst +++ b/Doc/c-api/tuple.rst @@ -37,6 +37,19 @@ Tuple Objects or ``NULL`` with an exception set on failure. +.. c:function:: PyObject* PyTuple_FromArray(PyObject *const *array, Py_ssize_t size) + + Create a tuple of *size* items and copy references from *array* to the new + tuple. + + *array* can be NULL if *size* is ``0``. + + On success, return a new reference. + On error, set an exception and return ``NULL``. + + .. versionadded:: 3.15 + + .. c:function:: PyObject* PyTuple_Pack(Py_ssize_t n, ...) Return a new tuple object of size *n*, @@ -48,7 +61,7 @@ Tuple Objects .. c:function:: Py_ssize_t PyTuple_Size(PyObject *p) Take a pointer to a tuple object, and return the size of that tuple. - On error, return ``-1`` and with an exception set. + On error, return ``-1`` with an exception set. .. c:function:: Py_ssize_t PyTuple_GET_SIZE(PyObject *p) @@ -86,7 +99,8 @@ Tuple Objects Insert a reference to object *o* at position *pos* of the tuple pointed to by *p*. Return ``0`` on success. If *pos* is out of bounds, return ``-1`` - and set an :exc:`IndexError` exception. + and set an :exc:`IndexError` exception. This function should only be used to fill in brand new tuples; + using it on an existing tuple is thread-unsafe. .. note:: @@ -97,7 +111,7 @@ Tuple Objects .. c:function:: void PyTuple_SET_ITEM(PyObject *p, Py_ssize_t pos, PyObject *o) Like :c:func:`PyTuple_SetItem`, but does no error checking, and should *only* be - used to fill in brand new tuples. + used to fill in brand new tuples, using it on an existing tuple is thread-unsafe. Bounds checking is performed as an assertion if Python is built in :ref:`debug mode ` or :option:`with assertions <--with-assertions>`. @@ -135,8 +149,11 @@ Tuple Objects Struct Sequence Objects ----------------------- -Struct sequence objects are the C equivalent of :func:`~collections.namedtuple` -objects, i.e. a sequence whose items can also be accessed through attributes. +A struct sequence object is a :term:`named tuple`, that is, a sequence +whose items can also be accessed through attributes. +It is similar to :func:`collections.namedtuple`, but provides a slightly +different interface. + To create a struct sequence, you first have to create a specific struct sequence type. @@ -220,6 +237,8 @@ type. .. c:function:: PyObject* PyStructSequence_GetItem(PyObject *p, Py_ssize_t pos) Return the object at position *pos* in the struct sequence pointed to by *p*. + The returned reference is borrowed from the struct sequence *p* + (that is: it is only valid as long as you hold a reference to *p*). Bounds checking is performed as an assertion if Python is built in :ref:`debug mode ` or :option:`with assertions <--with-assertions>`. diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 5bdbff4e0ad990..4771d0a7781bd6 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -3,7 +3,7 @@ .. _typeobjects: Type Objects ------------- +============ .. index:: pair: object; type @@ -110,11 +110,30 @@ Type Objects :c:func:`!_PyType_Lookup` is not called on *type* between the modifications; this is an implementation detail and subject to change.) + The callback is also invoked when a watched heap type is deallocated. + An extension should never call ``PyType_Watch`` with a *watcher_id* that was not returned to it by a previous call to :c:func:`PyType_AddWatcher`. .. versionadded:: 3.12 + .. versionchanged:: 3.15 + The callback is now also invoked when a watched heap type is deallocated. + + +.. c:function:: int PyType_Unwatch(int watcher_id, PyObject *type) + + Mark *type* as not watched. This undoes a previous call to + :c:func:`PyType_Watch`. *type* must not be ``NULL``. + + An extension should never call this function with a *watcher_id* that was + not returned to it by a previous call to :c:func:`PyType_AddWatcher`. + + On success, this function returns ``0``. On failure, this function returns + ``-1`` with an exception set. + + .. versionadded:: 3.12 + .. c:type:: int (*PyType_WatchCallback)(PyObject *type) @@ -124,8 +143,17 @@ Type Objects called on *type* or any type in its MRO; violating this rule could cause infinite recursion. + The callback may be called during type deallocation. In this case, the type + object is temporarily resurrected (its reference count is at least 1) and all + its attributes are still valid. However, the callback should not store new + strong references to the type, as this would resurrect the object and prevent + its deallocation. + .. versionadded:: 3.12 + .. versionchanged:: 3.15 + The callback may now be called during deallocation of a watched heap type. + .. c:function:: int PyType_HasFeature(PyTypeObject *o, int feature) @@ -133,6 +161,18 @@ Type Objects Type features are denoted by single bit flags. +.. c:function:: int PyType_FastSubclass(PyTypeObject *type, int flag) + + Return non-zero if the type object *type* sets the subclass flag *flag*. + Subclass flags are denoted by + :c:macro:`Py_TPFLAGS_*_SUBCLASS `. + This function is used by many ``_Check`` functions for common types. + + .. seealso:: + :c:func:`PyObject_TypeCheck`, which is used as a slower alternative in + ``_Check`` functions for types that don't come with subclass flags. + + .. c:function:: int PyType_IS_GC(PyTypeObject *o) Return true if the type object includes support for the cycle detector; this @@ -169,12 +209,14 @@ Type Objects 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. 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) Finalize a type object. This should be called on all type objects to finish @@ -191,6 +233,7 @@ Type Objects GC protocol itself by at least implementing the :c:member:`~PyTypeObject.tp_traverse` handle. + .. c:function:: PyObject* PyType_GetName(PyTypeObject *type) Return the type's name. Equivalent to getting the type's @@ -198,6 +241,7 @@ Type Objects .. versionadded:: 3.11 + .. c:function:: PyObject* PyType_GetQualName(PyTypeObject *type) Return the type's qualified name. Equivalent to getting the @@ -213,6 +257,7 @@ Type Objects .. versionadded:: 3.13 + .. c:function:: PyObject* PyType_GetModuleName(PyTypeObject *type) Return the type's module name. Equivalent to getting the @@ -220,6 +265,7 @@ Type Objects .. versionadded:: 3.13 + .. c:function:: void* PyType_GetSlot(PyTypeObject *type, int slot) Return the function pointer stored in the given slot. If the @@ -236,11 +282,16 @@ Type Objects :c:func:`PyType_GetSlot` can now accept all types. Previously, it was limited to :ref:`heap types `. + .. c:function:: PyObject* PyType_GetModule(PyTypeObject *type) Return the module object associated with the given type when the type was created using :c:func:`PyType_FromModuleAndSpec`. + The returned reference is :term:`borrowed ` from *type*, + and will be valid as long as you hold a reference to *type*. + Do not release it with :c:func:`Py_DECREF` or similar. + If no module is associated with the given type, sets :py:class:`TypeError` and returns ``NULL``. @@ -250,11 +301,12 @@ Type Objects ``Py_TYPE(self)`` may be a *subclass* of the intended class, and subclasses are not necessarily defined in the same module as their superclass. See :c:type:`PyCMethod` to get the class that defines the method. - See :c:func:`PyType_GetModuleByDef` for cases when :c:type:`!PyCMethod` cannot - be used. + See :c:func:`PyType_GetModuleByToken` for cases when :c:type:`!PyCMethod` + cannot be used. .. versionadded:: 3.9 + .. c:function:: void* PyType_GetModuleState(PyTypeObject *type) Return the state of the module object associated with the given type. @@ -269,10 +321,11 @@ Type Objects .. versionadded:: 3.9 -.. c:function:: PyObject* PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) - Find the first superclass whose module was created from - the given :c:type:`PyModuleDef` *def*, and return that module. +.. c:function:: PyObject* PyType_GetModuleByToken(PyTypeObject *type, const void *mod_token) + + Find the first superclass whose module has the given + :ref:`module token `, and return that module. If no module is found, raises a :py:class:`TypeError` and returns ``NULL``. @@ -282,16 +335,34 @@ Type Objects and other places where a method's defining class cannot be passed using the :c:type:`PyCMethod` calling convention. + .. versionadded:: 3.15 + + +.. c:function:: PyObject* PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) + + Find the first superclass whose module was created from the given + :c:type:`PyModuleDef` *def*, or whose :ref:`module token ` + is equal to *def*, and return that module. + + Note that modules created from a :c:type:`PyModuleDef` always have their + token set to the :c:type:`PyModuleDef`'s address. + In other words, this function is equivalent to + :c:func:`PyType_GetModuleByToken`, except that it: + + - returns a borrowed reference, and + - has a non-``void*`` argument type (which is a cosmetic difference in C). + The returned reference is :term:`borrowed ` from *type*, and will be valid as long as you hold a reference to *type*. Do not release it with :c:func:`Py_DECREF` or similar. .. versionadded:: 3.11 -.. c:function:: int PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result) + +.. c:function:: int PyType_GetBaseByToken(PyTypeObject *type, void *tp_token, PyTypeObject **result) Find the first superclass in *type*'s :term:`method resolution order` whose - :c:macro:`Py_tp_token` token is equal to the given one. + :c:macro:`Py_tp_token` token is equal to *tp_token*. * If found, set *\*result* to a new :term:`strong reference` to it and return ``1``. @@ -302,10 +373,11 @@ Type Objects The *result* argument may be ``NULL``, in which case *\*result* is not set. Use this if you need only the return value. - The *token* argument may not be ``NULL``. + The *tp_token* argument may not be ``NULL``. .. versionadded:: 3.14 + .. c:function:: int PyUnstable_Type_AssignVersionTag(PyTypeObject *type) Attempt to assign a version tag to the given type. @@ -316,36 +388,29 @@ Type Objects .. versionadded:: 3.12 -Creating Heap-Allocated Types -............................. +.. c:function:: int PyType_SUPPORTS_WEAKREFS(PyTypeObject *type) -The following functions and structs are used to create -:ref:`heap types `. + Return true if instances of *type* support creating weak references, false + otherwise. This function always succeeds. *type* must not be ``NULL``. -.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) + .. seealso:: + * :ref:`weakrefobjects` + * :py:mod:`weakref` - Create and return a :ref:`heap type ` from the *spec* - (see :c:macro:`Py_TPFLAGS_HEAPTYPE`). - The metaclass *metaclass* is used to construct the resulting type object. - When *metaclass* is ``NULL``, the metaclass is derived from *bases* - (or *Py_tp_base[s]* slots if *bases* is ``NULL``, see below). +.. _creating-heap-types: - Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not - supported, except if ``tp_new`` is ``NULL``. +Creating Heap-Allocated Types +----------------------------- - The *bases* argument can be used to specify base classes; it can either - be only one class or a tuple of classes. - If *bases* is ``NULL``, the *Py_tp_bases* slot is used instead. - If that also is ``NULL``, the *Py_tp_base* slot is used instead. - If that also is ``NULL``, the new type derives from :class:`object`. +The following function is used to create :ref:`heap types `: - The *module* argument can be used to record the module in which the new - class is defined. It must be a module object or ``NULL``. - If not ``NULL``, the module is associated with the new type and can later be - retrieved with :c:func:`PyType_GetModule`. - The associated module is not inherited by subclasses; it must be specified - for each class individually. +.. c:function:: PyObject *PyType_FromSlots(const PySlot *slots) + + Create and return a :ref:`heap type ` from a :c:type:`!PySlot` + array. + See :ref:`capi-slots` for general information on slots, + and :ref:`pyslot_type_slot_ids` for slots specific to type creation. This function calls :c:func:`PyType_Ready` on the new type. @@ -362,8 +427,377 @@ The following functions and structs are used to create * :py:meth:`~object.__init_subclass__` is not called on any bases. * :py:meth:`~object.__set_name__` is not called on new descriptors. + Slots are typically defined as a global static constant arrays. + However, sometimes slot values are not statically known at compile time. + For example, slots like :c:data:`Py_tp_bases`, :c:data:`Py_tp_metaclass` + and :c:data:`Py_tp_module` require live Python objects. + In this case, it is recommended to put such slots on the stack, + and use :c:macro:`Py_slot_subslots` to refer to an array of static slots. + For example:: + + static const PySlot my_slots[] = { + PySlot_STATIC_DATA(Py_tp_name, "MyClass"), + PySlot_FUNC(Py_tp_repr, my_repr_func), + ... + PySlot_END + }; + + PyObject *make_my_class(PyObject *module) { + PySlot all_slots[] = { + PySlot_STATIC_DATA(Py_slot_subslots, my_slots), + PySlot_DATA(Py_tp_module, module), + PySlot_END + }; + return PyType_FromSlots(all_slots); + } + +Heap types created without the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag may be +modified, for example by setting attributes on them, as with classes defined +in Python code. +Sometimes, such modifications are necessary to fully initialize a type, +but you may wish to prevent users from changing the type after +the initialization is done: + +.. c:function:: int PyType_Freeze(PyTypeObject *type) + + Make a type immutable: set the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag. + + All base classes of *type* must be immutable. + + On success, return ``0``. + On error, set an exception and return ``-1``. + + The type must not be used before it's made immutable. For example, type + instances must not be created before the type is made immutable. + + .. versionadded:: 3.14 + + +.. _pyslot_type_slot_ids: + +Type slot IDs +............. + +Most type slot IDs are named like the field names of the structures +:c:type:`PyTypeObject`, :c:type:`PyNumberMethods`, +:c:type:`PySequenceMethods`, :c:type:`PyMappingMethods` and +:c:type:`PyAsyncMethods` with an added ``Py_`` prefix. +For example, use: + +* :c:data:`Py_tp_dealloc` to set :c:member:`PyTypeObject.tp_dealloc` +* :c:data:`Py_nb_add` to set :c:member:`PyNumberMethods.nb_add` +* :c:data:`Py_sq_length` to set :c:member:`PySequenceMethods.sq_length` + +The following slots need additional considerations when specified as slots: + +* :c:data:`Py_tp_name` +* :c:data:`Py_tp_basicsize` and :c:data:`Py_tp_extra_basicsize` +* :c:data:`Py_tp_itemsize` +* :c:data:`Py_tp_flags` + +Additional slots do not directly correspond to a :c:type:`!PyTypeObject` +struct field: + +* :c:data:`Py_tp_token` +* :c:data:`Py_tp_metaclass` +* :c:data:`Py_tp_module` + +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 `) + +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:data:`Py_tp_members`. +See :ref:`PyMemberDef documentation ` +for details. + +The following internal fields cannot be set at all when creating a heap +type: + +* :c:member:`~PyTypeObject.tp_dict`, + :c:member:`~PyTypeObject.tp_mro`, + :c:member:`~PyTypeObject.tp_cache`, + :c:member:`~PyTypeObject.tp_subclasses`, and + :c:member:`~PyTypeObject.tp_weaklist`. + +The :c:data:`Py_tp_base` slot is equivalent to :c:data:`Py_tp_bases`; +both may be set either to a type or a tuple of types. +If both are specified, the value of :c:data:`Py_tp_bases` +is used. + +Slot values may not be ``NULL``, except for the following: + +* :c:data:`Py_tp_doc` +* :c:data:`Py_tp_token` (for clarity, prefer :c:data:`Py_TP_USE_SPEC` + rather than ``NULL``) + +.. versionchanged:: 3.9 + 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 `. + +.. versionchanged:: 3.14 + The field :c:member:`~PyTypeObject.tp_vectorcall` can now be set + using :c:data:`Py_tp_vectorcall`. See the field's documentation + for details. + +.. versionchanged:: 3.15 + The :c:data:`Py_tp_bases` slot may be set to a single type object, + making it equivalent to the :c:data:`Py_tp_base` slot. + Previously, a tuple of types was required. + +The following slots correspond to fields in the underlying type structure, +but need extra remarks for use as slots: + +.. c:macro:: Py_tp_name + + :c:member:`Slot ID ` for the name of the type, + used to set :c:member:`PyTypeObject.tp_name`. + + This slot (or :c:func:`PyType_Spec.name`) is required to create a type. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use :c:func:`PyType_Spec.name` instead. + + .. impl-detail:: + + CPython processes slots in order. + It is recommended to put ``Py_tp_name`` at the beginning of the slots + array, so that if processing of a later slots fails, error messages + can include the name. + + .. versionadded:: 3.15 + +.. c:macro:: Py_tp_basicsize + + :c:member:`Slot ID ` for the size of the instance in bytes. + It is used to set :c:member:`PyTypeObject.tp_basicsize`. + + The value must be positive. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use :c:func:`PyType_Spec.basicsize` instead. + + This slot may not be used with :c:func:`PyType_GetSlot`. + Use :c:member:`PyTypeObject.tp_basicsize` instead if needed, but be aware + that a type's size is often considered an implementation detail. + + .. versionadded:: 3.15 + +.. c:macro:: Py_tp_extra_basicsize + + :c:member:`Slot ID ` for type data size in bytes, that is, + how much space instances of the class need *in addition* + to space needed for superclasses. + + The value is used, together with the size of superclasses, to set + :c:member:`PyTypeObject.tp_basicsize`. + Python will insert padding as needed to meet + :c:member:`!tp_basicsize`'s alignment requirements. + + Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific + memory reserved this way. + + The value must be positive. + To specify that instances need no additional size (that is, size should be + inherited), omit the :c:macro:`!Py_tp_extra_basicsize` slot rather than + set it to zero. + + Specifying both :c:macro:`Py_tp_basicsize` and + :c:macro:`!Py_tp_extra_basicsize` is an error. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use negative :c:func:`PyType_Spec.basicsize` instead. + + This slot may not be used with :c:func:`PyType_GetSlot`. + + .. versionadded:: 3.15 + +.. c:macro:: Py_tp_itemsize + + :c:member:`Slot ID ` for the size of one element of a + variable-size type, in bytes. + Used to set :c:member:`PyTypeObject.tp_itemsize`. + See :c:member:`!tp_itemsize` documentation for caveats. + + The value must be positive. + + If this slot is missing, :c:member:`~PyTypeObject.tp_itemsize` is inherited. + Extending arbitrary variable-sized classes is dangerous, + since some types use a fixed offset for variable-sized memory, + which can then overlap fixed-sized memory used by a subclass. + To help prevent mistakes, inheriting ``itemsize`` is only possible + in the following situations: + + - The base is not variable-sized (its + :c:member:`~PyTypeObject.tp_itemsize`). + - The requested :c:member:`PyType_Spec.basicsize` is positive, + suggesting that the memory layout of the base class is known. + - The requested :c:member:`PyType_Spec.basicsize` is zero, + suggesting that the subclass does not access the instance's memory + directly. + - With the :c:macro:`Py_TPFLAGS_ITEMS_AT_END` flag. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use :c:func:`PyType_Spec.itemsize` instead. + + This slot may not be used with :c:func:`PyType_GetSlot`. + + .. versionadded:: 3.15 + +.. c:macro:: Py_tp_flags + + :c:member:`Slot ID ` for type flags, used to set + :c:member:`PyTypeObject.tp_flags`. + + The ``Py_TPFLAGS_HEAPTYPE`` flag is not set, + :c:func:`PyType_FromSpecWithBases` sets it automatically. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use negative :c:func:`PyType_Spec.basicsize` instead. + + This slot may not be used with :c:func:`PyType_GetSlot`. + Use :c:func:`PyType_GetFlags` instead. + + .. versionadded:: 3.15 + +The following slots do not correspond to public fields in the +underlying structures: + +.. c:macro:: Py_tp_metaclass + + :c:member:`Slot ID ` for the metaclass used to construct + the resulting type object. + When omitted the metaclass is derived from bases + (:c:macro:`Py_tp_bases` or the *bases* argument of + :c:func:`PyType_FromMetaclass`). + + Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not + supported, except if ``tp_new`` is ``NULL``. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use :c:func:`PyType_FromMetaclass` to specify a metaclass with + :c:type:`!PyType_Spec`. + + This slot may not be used with :c:func:`PyType_GetSlot`. + Use :c:func:`Py_TYPE` on the type object instead. + + .. versionadded:: 3.15 + +.. c:macro:: Py_tp_module + + :c:member:`Slot ID ` for recording the module in which + the new class is defined. + + The value must be a module object. + The module is associated with the new type and can later be + retrieved with :c:func:`PyType_GetModule`. + The associated module is not inherited by subclasses; it must be specified + for each class individually. + + This may not be used in :c:member:`PyType_Spec.slots`. + Use :c:func:`PyType_FromMetaclass` to specify a module with + :c:type:`!PyType_Spec`. + + This slot may not be used with :c:func:`PyType_GetSlot`. + Use :c:func:`PyType_GetModule` instead. + + .. versionadded:: 3.15 + +.. c:macro:: Py_tp_token + + :c:member:`Slot ID ` for recording a static memory layout ID + for a class. + + If the class is defined using a :c:type:`PyType_Spec`, and that spec is + statically allocated, the token can be set to the spec using the special + value :c:data:`Py_TP_USE_SPEC`: + + .. code-block:: c + + static PyType_Slot foo_slots[] = { + {Py_tp_token, Py_TP_USE_SPEC}, + + It can also be set to an arbitrary pointer, but you must ensure that: + + * The pointer outlives the class, so it's not reused for something else + while the class exists. + * It "belongs" to the extension module where the class lives, so it will not + clash with other extensions. + + Use :c:func:`PyType_GetBaseByToken` to check if a class's superclass has + a given token -- that is, check whether the memory layout is compatible. + + To get the token for a given class (without considering superclasses), + use :c:func:`PyType_GetSlot` with ``Py_tp_token``. + + .. versionadded:: 3.14 + + .. c:namespace:: NULL + + .. c:macro:: Py_TP_USE_SPEC + + Used as a value with :c:data:`Py_tp_token` to set the token to the + class's :c:type:`PyType_Spec`. + May only be used for classes defined using :c:type:`!PyType_Spec`. + + Expands to ``NULL``. + + .. versionadded:: 3.14 + +.. c:macro:: Py_tp_slots + + :c:member:`Slot ID ` that works like + :c:macro:`Py_slot_subslots`, except it specifies an array of + :c:type:`PyType_Slot` structures. + + .. versionadded:: 3.15 + + +Soft-deprecated API +------------------- + +The following functions are :term:`soft deprecated`. +They will continue to work, but new features will be added as slots for +:c:func:`PyType_FromSlots`, not as arguments to new ``PyType_From*`` functions. + +.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) + + Create and return a :ref:`heap type ` from the *spec* + (see :c:macro:`Py_TPFLAGS_HEAPTYPE`). + + A non-``NULL`` *metaclass* argument corresponds to the + :c:macro:`Py_tp_metaclass` slot. + + A non-``NULL`` *bases* argument corresponds to the :c:data:`Py_tp_bases` + slot, and takes precedence over :c:data:`Py_tp_bases` and + :c:data:`Py_tp_bases` slots. + + A non-``NULL`` *module* argument corresponds to the + :c:macro:`Py_tp_module` slot. + + This function calls :c:func:`PyType_Ready` on the new type. + + Note that this function does *not* fully match the behavior of + calling :py:class:`type() ` or using the :keyword:`class` statement. + See the note in :c:func:`PyType_FromSlots` documentation for details. + .. versionadded:: 3.12 + .. soft-deprecated:: next + + Prefer :c:func:`PyType_FromSlots` in new code. + + .. c:function:: PyObject* PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) Equivalent to ``PyType_FromMetaclass(NULL, module, spec, bases)``. @@ -390,6 +824,11 @@ The following functions and structs are used to create Creating classes whose metaclass overrides :c:member:`~PyTypeObject.tp_new` is no longer allowed. + .. soft-deprecated:: next + + Prefer :c:func:`PyType_FromSlots` in new code. + + .. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) Equivalent to ``PyType_FromMetaclass(NULL, NULL, spec, bases)``. @@ -411,6 +850,11 @@ The following functions and structs are used to create Creating classes whose metaclass overrides :c:member:`~PyTypeObject.tp_new` is no longer allowed. + .. soft-deprecated:: next + + Prefer :c:func:`PyType_FromSlots` in new code. + + .. c:function:: PyObject* PyType_FromSpec(PyType_Spec *spec) Equivalent to ``PyType_FromMetaclass(NULL, NULL, spec, NULL)``. @@ -431,19 +875,9 @@ The following functions and structs are used to create Creating classes whose metaclass overrides :c:member:`~PyTypeObject.tp_new` is no longer allowed. -.. c:function:: int PyType_Freeze(PyTypeObject *type) + .. soft-deprecated:: next - Make a type immutable: set the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag. - - All base classes of *type* must be immutable. - - On success, return ``0``. - On error, set an exception and return ``-1``. - - The type must not be used before it's made immutable. For example, type - instances must not be created before the type is made immutable. - - .. versionadded:: 3.14 + Prefer :c:func:`PyType_FromSlots` in new code. .. raw:: html @@ -456,27 +890,23 @@ The following functions and structs are used to create .. c:type:: PyType_Spec - Structure defining a type's behavior. + Structure defining a type's behavior, used for soft-deprecated functions + like :c:func:`PyType_FromMetaclass`. + + This structure contains several members that can instead be specified + as :ref:`slots ` for :c:func:`PyType_FromSlots`, + and an array of slot entries with a simpler structure. .. c:member:: const char* name - Name of the type, used to set :c:member:`PyTypeObject.tp_name`. + Corresponds to :c:macro:`Py_tp_name`. .. c:member:: int basicsize - If positive, specifies the size of the instance in bytes. - It is used to set :c:member:`PyTypeObject.tp_basicsize`. - - If zero, specifies that :c:member:`~PyTypeObject.tp_basicsize` - should be inherited. + If positive, corresponds to :c:macro:`Py_tp_basicsize`. - If negative, the absolute value specifies how much space instances of the - 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. + If negative, corresponds to :c:macro:`Py_tp_extra_basicsize` set to + the absolute value. .. versionchanged:: 3.12 @@ -484,159 +914,53 @@ The following functions and structs are used to create .. c:member:: int itemsize - Size of one element of a variable-size type, in bytes. - Used to set :c:member:`PyTypeObject.tp_itemsize`. - See ``tp_itemsize`` documentation for caveats. - - If zero, :c:member:`~PyTypeObject.tp_itemsize` is inherited. - Extending arbitrary variable-sized classes is dangerous, - since some types use a fixed offset for variable-sized memory, - which can then overlap fixed-sized memory used by a subclass. - To help prevent mistakes, inheriting ``itemsize`` is only possible - in the following situations: - - - The base is not variable-sized (its - :c:member:`~PyTypeObject.tp_itemsize`). - - The requested :c:member:`PyType_Spec.basicsize` is positive, - suggesting that the memory layout of the base class is known. - - The requested :c:member:`PyType_Spec.basicsize` is zero, - suggesting that the subclass does not access the instance's memory - directly. - - With the :c:macro:`Py_TPFLAGS_ITEMS_AT_END` flag. + Corresponds to :c:macro:`Py_tp_itemsize`. .. c:member:: unsigned int flags - Type flags, used to set :c:member:`PyTypeObject.tp_flags`. - - If the ``Py_TPFLAGS_HEAPTYPE`` flag is not set, - :c:func:`PyType_FromSpecWithBases` sets it automatically. + Corresponds to :c:macro:`Py_tp_flags`. .. c:member:: PyType_Slot *slots - Array of :c:type:`PyType_Slot` structures. - Terminated by the special slot value ``{0, NULL}``. + Array of :c:type:`PyType_Slot` (not :c:type:`PySlot`) structures. + Terminated by the special slot value ``{0, NULL}``. Each slot ID should be specified at most once. -.. raw:: html - - - - - -.. c:type:: PyType_Slot - - Structure defining optional functionality of a type, containing a slot ID - and a value pointer. - - .. c:member:: int slot + .. c:namespace:: NULL - A slot ID. + .. raw:: html - Slot IDs are named like the field names of the structures - :c:type:`PyTypeObject`, :c:type:`PyNumberMethods`, - :c:type:`PySequenceMethods`, :c:type:`PyMappingMethods` and - :c:type:`PyAsyncMethods` with an added ``Py_`` prefix. - For example, use: + + + - * ``Py_tp_dealloc`` to set :c:member:`PyTypeObject.tp_dealloc` - * ``Py_nb_add`` to set :c:member:`PyNumberMethods.nb_add` - * ``Py_sq_length`` to set :c:member:`PySequenceMethods.sq_length` + .. c:type:: PyType_Slot - An additional slot is supported that does not correspond to a - :c:type:`!PyTypeObject` struct field: + Structure defining optional functionality of a type, used for + soft-deprecated functions like :c:func:`PyType_FromMetaclass`. - * :c:data:`Py_tp_token` + Note that a :c:type:`!PyType_Slot` array may be included in a + :c:type:`!PySlot` array using :c:macro:`Py_tp_slots`, + and vice versa using :c:macro:`Py_slot_subslots`. - The following “offset” fields cannot be set using :c:type:`PyType_Slot`: + Each :c:type:`!PyType_Slot` structure ``tpslot`` is interpreted + as the following :c:type:`PySlot` structure:: - * :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 `) + (PySlot){ + .sl_id=tpslot.slot, + .sl_flags=PySlot_INTPTR | sub_static, + .sl_ptr=tpslot.func + } - 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 `. - See :ref:`PyMemberDef documentation ` - for details. + where ``sub_static`` is ``PySlot_STATIC`` if the slot requires + the flag (such as for :c:macro:`Py_tp_methods`), or if this flag + is present on the "parent" :c:macro:`!Py_tp_slots` slot (if any). - The following internal fields cannot be set at all when creating a heap - type: + .. c:member:: int slot - * :c:member:`~PyTypeObject.tp_dict`, - :c:member:`~PyTypeObject.tp_mro`, - :c:member:`~PyTypeObject.tp_cache`, - :c:member:`~PyTypeObject.tp_subclasses`, and - :c:member:`~PyTypeObject.tp_weaklist`. + Corresponds to :c:member:`PySlot.sl_id`. - Setting :c:data:`Py_tp_bases` or :c:data:`Py_tp_base` may be - problematic on some platforms. - To avoid issues, use the *bases* argument of - :c:func:`PyType_FromSpecWithBases` instead. + .. c:member:: void *pfunc - .. versionchanged:: 3.9 - 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 `. - - .. 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 - - The desired value of the slot. In most cases, this is a pointer - to a function. - - *pfunc* values may not be ``NULL``, except for the following slots: - - * ``Py_tp_doc`` - * :c:data:`Py_tp_token` (for clarity, prefer :c:data:`Py_TP_USE_SPEC` - rather than ``NULL``) - -.. c:macro:: Py_tp_token - - A :c:member:`~PyType_Slot.slot` that records a static memory layout ID - for a class. - - If the :c:type:`PyType_Spec` of the class is statically - allocated, the token can be set to the spec using the special value - :c:data:`Py_TP_USE_SPEC`: - - .. code-block:: c - - static PyType_Slot foo_slots[] = { - {Py_tp_token, Py_TP_USE_SPEC}, - - It can also be set to an arbitrary pointer, but you must ensure that: - - * The pointer outlives the class, so it's not reused for something else - while the class exists. - * It "belongs" to the extension module where the class lives, so it will not - clash with other extensions. - - Use :c:func:`PyType_GetBaseByToken` to check if a class's superclass has - a given token -- that is, check whether the memory layout is compatible. - - To get the token for a given class (without considering superclasses), - use :c:func:`PyType_GetSlot` with ``Py_tp_token``. - - .. versionadded:: 3.14 - - .. c:namespace:: NULL - - .. c:macro:: Py_TP_USE_SPEC - - Used as a value with :c:data:`Py_tp_token` to set the token to the - class's :c:type:`PyType_Spec`. - Expands to ``NULL``. - - .. versionadded:: 3.14 + Corresponds to :c:member:`PySlot.sl_ptr`. diff --git a/Doc/c-api/typehints.rst b/Doc/c-api/typehints.rst index 98fe68737deb81..ec2fba6da8b043 100644 --- a/Doc/c-api/typehints.rst +++ b/Doc/c-api/typehints.rst @@ -31,7 +31,7 @@ two types exist -- :ref:`GenericAlias ` and static PyMethodDef my_obj_methods[] = { // Other methods. ... - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, "See PEP 585"} + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, "my_obj is generic over its contained type"} ... } diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 060d6f60174b41..dcc9e243c2f314 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -555,6 +555,9 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: const char* PyTypeObject.tp_name + See :c:macro:`Py_tp_name` for the corresponding + :c:member:`Slot ID `. + Pointer to a NUL-terminated string containing the name of the type. For types that are accessible as module globals, the string should be the full module name, followed by a dot, followed by the type name; for built-in types, it @@ -594,6 +597,10 @@ and :c:data:`PyType_Type` effectively act as defaults.) These fields allow calculating the size in bytes of instances of the type. + See :c:macro:`Py_tp_basicsize`, :c:macro:`Py_tp_extra_basicsize` and + :c:macro:`Py_tp_itemsize` for the corresponding + :c:member:`Slot IDs `. + There are two kinds of types: types with fixed-length instances have a zero :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 @@ -676,6 +683,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: destructor PyTypeObject.tp_dealloc + .. corresponding-type-slot:: Py_tp_dealloc + A pointer to the instance destructor function. The function signature is:: void tp_dealloc(PyObject *self); @@ -860,6 +869,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: getattrfunc PyTypeObject.tp_getattr + .. corresponding-type-slot:: Py_tp_getattr + An optional pointer to the get-attribute-string function. This field is deprecated. When it is defined, it should point to a function @@ -877,6 +888,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: setattrfunc PyTypeObject.tp_setattr + .. corresponding-type-slot:: Py_tp_setattr + An optional pointer to the function for setting and deleting attributes. This field is deprecated. When it is defined, it should point to a function @@ -909,6 +922,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: reprfunc PyTypeObject.tp_repr + .. corresponding-type-slot:: Py_tp_repr + .. index:: pair: built-in function; repr An optional pointer to a function that implements the built-in function @@ -974,6 +989,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: hashfunc PyTypeObject.tp_hash + .. corresponding-type-slot:: Py_tp_hash + .. index:: pair: built-in function; hash An optional pointer to a function that implements the built-in function @@ -1015,6 +1032,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: ternaryfunc PyTypeObject.tp_call + .. corresponding-type-slot:: Py_tp_call + An optional pointer to a function that implements calling the object. This should be ``NULL`` if the object is not callable. The signature is the same as for :c:func:`PyObject_Call`:: @@ -1028,6 +1047,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: reprfunc PyTypeObject.tp_str + .. corresponding-type-slot:: Py_tp_str + An optional pointer to a function that implements the built-in operation :func:`str`. (Note that :class:`str` is a type now, and :func:`str` calls the constructor for that type. This constructor calls :c:func:`PyObject_Str` to do @@ -1053,6 +1074,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: getattrofunc PyTypeObject.tp_getattro + .. corresponding-type-slot:: Py_tp_getattro + An optional pointer to the get-attribute function. The signature is the same as for :c:func:`PyObject_GetAttr`:: @@ -1077,6 +1100,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: setattrofunc PyTypeObject.tp_setattro + .. corresponding-type-slot:: Py_tp_setattro + An optional pointer to the function for setting and deleting attributes. The signature is the same as for :c:func:`PyObject_SetAttr`:: @@ -1115,6 +1140,9 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: unsigned long PyTypeObject.tp_flags + See :c:macro:`Py_tp_flags` for the corresponding + :c:member:`Slot ID `. + This field is a bit mask of various flags. Some flags indicate variant semantics for certain situations; others are used to indicate that certain fields in the type object (or in the extension structures referenced via @@ -1260,7 +1288,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) This bit indicates that instances of the class have a :attr:`~object.__dict__` attribute, and that the space for the dictionary is managed by the VM. - If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set. + If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set. The type traverse function must call :c:func:`PyObject_VisitManagedDict` and its clear function must call :c:func:`PyObject_ClearManagedDict`. @@ -1278,6 +1306,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) This bit indicates that instances of the class should be weakly referenceable. + If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set. + .. versionadded:: 3.12 **Inheritance:** @@ -1286,6 +1316,19 @@ and :c:data:`PyType_Type` effectively act as defaults.) :c:member:`~PyTypeObject.tp_weaklistoffset` field is set in a superclass. + .. c:macro:: Py_TPFLAGS_PREHEADER + + These bits indicate that the VM will manage some fields by storing them + before the object. Currently, this macro is equivalent to + :c:expr:`Py_TPFLAGS_MANAGED_DICT | Py_TPFLAGS_MANAGED_WEAKREF`. + + This macro value relies on the implementation of the VM, so its value is not + stable and may change in a future version. Prefer using individual + flags instead. + + .. versionadded:: 3.12 + + .. c:macro:: Py_TPFLAGS_ITEMS_AT_END Only usable with variable-size types, i.e. ones with non-zero @@ -1318,8 +1361,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:macro:: Py_TPFLAGS_BASE_EXC_SUBCLASS .. c:macro:: Py_TPFLAGS_TYPE_SUBCLASS - These flags are used by functions such as - :c:func:`PyLong_Check` to quickly determine if a type is a subclass + Functions such as :c:func:`PyLong_Check` will call :c:func:`PyType_FastSubclass` + with one of these flags to quickly determine if a type is a subclass of a built-in type; such specific checks are faster than a generic check, like :c:func:`PyObject_IsInstance`. Custom types that inherit from built-ins should have their :c:member:`~PyTypeObject.tp_flags` @@ -1340,6 +1383,9 @@ and :c:data:`PyType_Type` effectively act as defaults.) type structure. + .. c:macro:: _Py_TPFLAGS_HAVE_VECTORCALL + :no-typesetting: + .. c:macro:: Py_TPFLAGS_HAVE_VECTORCALL This bit is set when the class implements @@ -1351,7 +1397,12 @@ and :c:data:`PyType_Type` effectively act as defaults.) This bit is inherited if :c:member:`~PyTypeObject.tp_call` is also inherited. - .. versionadded:: 3.9 + .. versionadded:: 3.8 as ``_Py_TPFLAGS_HAVE_VECTORCALL`` + + .. versionchanged:: 3.9 + + Renamed to the current name, without the leading underscore. + The old provisional name is :term:`soft deprecated`. .. versionchanged:: 3.12 @@ -1458,102 +1509,75 @@ and :c:data:`PyType_Type` effectively act as defaults.) It will be removed in a future version of CPython -.. c:member:: const char* PyTypeObject.tp_doc + .. c:macro:: Py_TPFLAGS_HAVE_VERSION_TAG - An optional pointer to a NUL-terminated C string giving the docstring for this - type object. This is exposed as the :attr:`~type.__doc__` attribute on the - type and instances of the type. + This macro does nothing. + Historically, this would indicate that the + :c:member:`~PyTypeObject.tp_version_tag` field was available and + initialized. - **Inheritance:** + .. soft-deprecated:: 3.13 - This field is *not* inherited by subtypes. + .. c:macro:: Py_TPFLAGS_INLINE_VALUES -.. c:member:: traverseproc PyTypeObject.tp_traverse + This bit indicates that instances of this type will have an "inline values" + array (containing the object's attributes) placed directly after the end + of the object. - An optional pointer to a traversal function for the garbage collector. This is - only used if the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is set. The signature is:: + This requires that :c:macro:`Py_TPFLAGS_HAVE_GC` is set. - int tp_traverse(PyObject *self, visitproc visit, void *arg); + **Inheritance:** - More information about Python's garbage collection scheme can be found - in section :ref:`supporting-cycle-detection`. + This flag is not inherited. - The :c:member:`~PyTypeObject.tp_traverse` pointer is used by the garbage collector to detect - reference cycles. A typical implementation of a :c:member:`~PyTypeObject.tp_traverse` function - simply calls :c:func:`Py_VISIT` on each of the instance's members that are Python - objects that the instance owns. For example, this is function :c:func:`!local_traverse` from the - :mod:`!_thread` extension module:: + .. versionadded:: 3.13 - static int - local_traverse(PyObject *op, visitproc visit, void *arg) - { - localobject *self = (localobject *) op; - Py_VISIT(self->args); - Py_VISIT(self->kw); - Py_VISIT(self->dict); - return 0; - } - Note that :c:func:`Py_VISIT` is called only on those members that can participate - in reference cycles. Although there is also a ``self->key`` member, it can only - be ``NULL`` or a Python string and therefore cannot be part of a reference cycle. + .. c:macro:: Py_TPFLAGS_IS_ABSTRACT - On the other hand, even if you know a member can never be part of a cycle, as a - debugging aid you may want to visit it anyway just so the :mod:`gc` module's - :func:`~gc.get_referents` function will include it. + This bit indicates that this is an abstract type and therefore cannot + be instantiated. - Heap types (:c:macro:`Py_TPFLAGS_HEAPTYPE`) must visit their type with:: + **Inheritance:** - Py_VISIT(Py_TYPE(self)); + This flag is not inherited. - It is only needed since Python 3.9. To support Python 3.8 and older, this - line must be conditional:: + .. seealso:: + :mod:`abc` - #if PY_VERSION_HEX >= 0x03090000 - Py_VISIT(Py_TYPE(self)); - #endif - If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the - :c:member:`~PyTypeObject.tp_flags` field, the traverse function must call - :c:func:`PyObject_VisitManagedDict` like this:: + .. c:macro:: Py_TPFLAGS_HAVE_STACKLESS_EXTENSION - PyObject_VisitManagedDict((PyObject*)self, visit, arg); + Internal. Do not set or unset this flag. + Historically, this was a reserved flag for use in Stackless Python. - .. warning:: - When implementing :c:member:`~PyTypeObject.tp_traverse`, only the - members that the instance *owns* (by having :term:`strong references - ` to them) must be - visited. For instance, if an object supports weak references via the - :c:member:`~PyTypeObject.tp_weaklist` slot, the pointer supporting - the linked list (what *tp_weaklist* points to) must **not** be - visited as the instance does not directly own the weak references to itself - (the weakreference list is there to support the weak reference machinery, - but the instance has no strong reference to the elements inside it, as they - are allowed to be removed even if the instance is still alive). - - Note that :c:func:`Py_VISIT` requires the *visit* and *arg* parameters to - :c:func:`!local_traverse` to have these specific names; don't name them just - anything. - - Instances of :ref:`heap-allocated types ` hold a reference to - their type. Their traversal function must therefore either visit - :c:func:`Py_TYPE(self) `, or delegate this responsibility by - calling ``tp_traverse`` of another heap-allocated type (such as a - heap-allocated superclass). - If they do not, the type object may not be garbage-collected. + .. warning:: + This flag is present in header files, but is not be used. + This may be removed in a future version of CPython. - .. note:: - The :c:member:`~PyTypeObject.tp_traverse` function can be called from any - thread. +.. c:member:: const char* PyTypeObject.tp_doc - .. versionchanged:: 3.9 + .. corresponding-type-slot:: Py_tp_doc - Heap-allocated types are expected to visit ``Py_TYPE(self)`` in - ``tp_traverse``. In earlier versions of Python, due to - `bug 40217 `_, doing this - may lead to crashes in subclasses. + An optional pointer to a NUL-terminated C string giving the docstring for this + type object. This is exposed as the :attr:`~type.__doc__` attribute on the + type and instances of the type. + + **Inheritance:** + + This field is *not* inherited by subtypes. + + +.. c:member:: traverseproc PyTypeObject.tp_traverse + + .. corresponding-type-slot:: Py_tp_traverse + + An optional pointer to a traversal function for the garbage collector. This is + only used if the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is set. + + See :ref:`gc-traversal` for documentation. **Inheritance:** @@ -1567,6 +1591,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: inquiry PyTypeObject.tp_clear + .. corresponding-type-slot:: Py_tp_clear + An optional pointer to a clear function. The signature is:: int tp_clear(PyObject *); @@ -1691,7 +1717,7 @@ and :c:data:`PyType_Type` effectively act as defaults.) :c:func:`Py_CLEAR` macro performs the operations in a safe order. If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the - :c:member:`~PyTypeObject.tp_flags` field, the traverse function must call + :c:member:`~PyTypeObject.tp_flags` field, the clear function must call :c:func:`PyObject_ClearManagedDict` like this:: PyObject_ClearManagedDict((PyObject*)self); @@ -1715,6 +1741,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: richcmpfunc PyTypeObject.tp_richcompare + .. corresponding-type-slot:: Py_tp_richcompare + An optional pointer to the rich comparison function, whose signature is:: PyObject *tp_richcompare(PyObject *self, PyObject *other, int op); @@ -1817,6 +1845,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: getiterfunc PyTypeObject.tp_iter + .. corresponding-type-slot:: Py_tp_iter + An optional pointer to a function that returns an :term:`iterator` for the object. Its presence normally signals that the instances of this type are :term:`iterable` (although sequences may be iterable without this function). @@ -1832,6 +1862,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: iternextfunc PyTypeObject.tp_iternext + .. corresponding-type-slot:: Py_tp_iternext + An optional pointer to a function that returns the next item in an :term:`iterator`. The signature is:: @@ -1855,6 +1887,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: struct PyMethodDef* PyTypeObject.tp_methods + .. corresponding-type-slot:: Py_tp_methods + An optional pointer to a static ``NULL``-terminated array of :c:type:`PyMethodDef` structures, declaring regular methods of this type. @@ -1869,6 +1903,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: struct PyMemberDef* PyTypeObject.tp_members + .. corresponding-type-slot:: Py_tp_members + An optional pointer to a static ``NULL``-terminated array of :c:type:`PyMemberDef` structures, declaring regular data members (fields or slots) of instances of this type. @@ -1884,6 +1920,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: struct PyGetSetDef* PyTypeObject.tp_getset + .. corresponding-type-slot:: Py_tp_getset + An optional pointer to a static ``NULL``-terminated array of :c:type:`PyGetSetDef` structures, declaring computed attributes of instances of this type. @@ -1898,6 +1936,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: PyTypeObject* PyTypeObject.tp_base + .. corresponding-type-slot:: Py_tp_base + An optional pointer to a base type from which type properties are inherited. At this level, only single inheritance is supported; multiple inheritance require dynamically creating a type object by calling the metatype. @@ -1970,6 +2010,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: descrgetfunc PyTypeObject.tp_descr_get + .. corresponding-type-slot:: Py_tp_descr_get + An optional pointer to a "descriptor get" function. The function signature is:: @@ -1985,6 +2027,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: descrsetfunc PyTypeObject.tp_descr_set + .. corresponding-type-slot:: Py_tp_descr_set + An optional pointer to a function for setting and deleting a descriptor's value. @@ -2045,6 +2089,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: initproc PyTypeObject.tp_init + .. corresponding-type-slot:: Py_tp_init + An optional pointer to an instance initialization function. This function corresponds to the :meth:`~object.__init__` method of classes. Like @@ -2080,6 +2126,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: allocfunc PyTypeObject.tp_alloc + .. corresponding-type-slot:: Py_tp_alloc + An optional pointer to an instance allocation function. The function signature is:: @@ -2103,6 +2151,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: newfunc PyTypeObject.tp_new + .. corresponding-type-slot:: Py_tp_new + An optional pointer to an instance creation function. The function signature is:: @@ -2142,6 +2192,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: freefunc PyTypeObject.tp_free + .. corresponding-type-slot:: Py_tp_free + An optional pointer to an instance deallocation function. Its signature is:: void tp_free(void *self); @@ -2171,6 +2223,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: inquiry PyTypeObject.tp_is_gc + .. corresponding-type-slot:: Py_tp_is_gc + An optional pointer to a function called by the garbage collector. The garbage collector needs to know whether a particular object is collectible @@ -2199,12 +2253,14 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: PyObject* PyTypeObject.tp_bases + .. corresponding-type-slot:: Py_tp_bases + Tuple of base types. This field should be set to ``NULL`` and treated as read-only. Python will fill it in when the type is :c:func:`initialized `. - For dynamically created classes, the ``Py_tp_bases`` + For dynamically created classes, the :c:data:`Py_tp_bases` :c:type:`slot ` can be used instead of the *bases* argument of :c:func:`PyType_FromSpecWithBases`. The argument form is preferred. @@ -2279,6 +2335,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: destructor PyTypeObject.tp_del + .. corresponding-type-slot:: Py_tp_del + This field is deprecated. Use :c:member:`~PyTypeObject.tp_finalize` instead. @@ -2293,6 +2351,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: destructor PyTypeObject.tp_finalize + .. corresponding-type-slot:: Py_tp_finalize + An optional pointer to an instance finalization function. This is the C implementation of the :meth:`~object.__del__` special method. Its signature is:: @@ -2451,6 +2511,8 @@ and :c:data:`PyType_Type` effectively act as defaults.) .. c:member:: vectorcallfunc PyTypeObject.tp_vectorcall + .. corresponding-type-slot:: Py_tp_vectorcall + A :ref:`vectorcall function ` to use for calls of this type object (rather than instances). In other words, ``tp_vectorcall`` can be used to optimize ``type.__call__``, @@ -2544,9 +2606,6 @@ This is done by filling a :c:type:`PyType_Spec` structure and calling Number Object Structures ------------------------ -.. sectionauthor:: Amaury Forgeot d'Arc - - .. c:type:: PyNumberMethods This structure holds pointers to the functions which an object uses to @@ -2616,51 +2675,154 @@ Number Object Structures Python 3.0.1. .. c:member:: binaryfunc PyNumberMethods.nb_add + + .. corresponding-type-slot:: Py_nb_add + .. c:member:: binaryfunc PyNumberMethods.nb_subtract + + .. corresponding-type-slot:: Py_nb_subtract + .. c:member:: binaryfunc PyNumberMethods.nb_multiply + + .. corresponding-type-slot:: Py_nb_multiply + .. c:member:: binaryfunc PyNumberMethods.nb_remainder + + .. corresponding-type-slot:: Py_nb_remainder + .. c:member:: binaryfunc PyNumberMethods.nb_divmod + + .. corresponding-type-slot:: Py_nb_divmod + .. c:member:: ternaryfunc PyNumberMethods.nb_power + + .. corresponding-type-slot:: Py_nb_power + .. c:member:: unaryfunc PyNumberMethods.nb_negative + + .. corresponding-type-slot:: Py_nb_negative + .. c:member:: unaryfunc PyNumberMethods.nb_positive + + .. corresponding-type-slot:: Py_nb_positive + .. c:member:: unaryfunc PyNumberMethods.nb_absolute + + .. corresponding-type-slot:: Py_nb_absolute + .. c:member:: inquiry PyNumberMethods.nb_bool + + .. corresponding-type-slot:: Py_nb_bool + .. c:member:: unaryfunc PyNumberMethods.nb_invert + + .. corresponding-type-slot:: Py_nb_invert + .. c:member:: binaryfunc PyNumberMethods.nb_lshift + + .. corresponding-type-slot:: Py_nb_lshift + .. c:member:: binaryfunc PyNumberMethods.nb_rshift + + .. corresponding-type-slot:: Py_nb_rshift + .. c:member:: binaryfunc PyNumberMethods.nb_and + + .. corresponding-type-slot:: Py_nb_and + .. c:member:: binaryfunc PyNumberMethods.nb_xor + + .. corresponding-type-slot:: Py_nb_xor + .. c:member:: binaryfunc PyNumberMethods.nb_or + + .. corresponding-type-slot:: Py_nb_or + .. c:member:: unaryfunc PyNumberMethods.nb_int + + .. corresponding-type-slot:: Py_nb_int + .. c:member:: void *PyNumberMethods.nb_reserved + .. c:member:: unaryfunc PyNumberMethods.nb_float + + .. corresponding-type-slot:: Py_nb_float + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_add + + .. corresponding-type-slot:: Py_nb_inplace_add + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_subtract + + .. corresponding-type-slot:: Py_nb_inplace_subtract + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_multiply + + .. corresponding-type-slot:: Py_nb_inplace_multiply + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_remainder + + .. corresponding-type-slot:: Py_nb_inplace_remainder + .. c:member:: ternaryfunc PyNumberMethods.nb_inplace_power + + .. corresponding-type-slot:: Py_nb_inplace_power + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_lshift + + .. corresponding-type-slot:: Py_nb_inplace_lshift + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_rshift + + .. corresponding-type-slot:: Py_nb_inplace_rshift + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_and + + .. corresponding-type-slot:: Py_nb_inplace_and + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_xor + + .. corresponding-type-slot:: Py_nb_inplace_xor + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_or + + .. corresponding-type-slot:: Py_nb_inplace_or + .. c:member:: binaryfunc PyNumberMethods.nb_floor_divide + + .. corresponding-type-slot:: Py_nb_floor_divide + .. c:member:: binaryfunc PyNumberMethods.nb_true_divide + + .. corresponding-type-slot:: Py_nb_true_divide + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_floor_divide + + .. corresponding-type-slot:: Py_nb_inplace_floor_divide + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_true_divide + + .. corresponding-type-slot:: Py_nb_inplace_true_divide + .. c:member:: unaryfunc PyNumberMethods.nb_index + + .. corresponding-type-slot:: Py_nb_index + .. c:member:: binaryfunc PyNumberMethods.nb_matrix_multiply + + .. corresponding-type-slot:: Py_nb_matrix_multiply + .. c:member:: binaryfunc PyNumberMethods.nb_inplace_matrix_multiply + .. corresponding-type-slot:: Py_nb_inplace_matrix_multiply + + .. _mapping-structs: Mapping Object Structures ------------------------- -.. sectionauthor:: Amaury Forgeot d'Arc - - .. c:type:: PyMappingMethods This structure holds pointers to the functions which an object uses to @@ -2668,12 +2830,16 @@ Mapping Object Structures .. c:member:: lenfunc PyMappingMethods.mp_length + .. corresponding-type-slot:: Py_mp_length + This function is used by :c:func:`PyMapping_Size` and :c:func:`PyObject_Size`, and has the same signature. This slot may be set to ``NULL`` if the object has no defined length. .. c:member:: binaryfunc PyMappingMethods.mp_subscript + .. corresponding-type-slot:: Py_mp_subscript + This function is used by :c:func:`PyObject_GetItem` and :c:func:`PySequence_GetSlice`, and has the same signature as :c:func:`!PyObject_GetItem`. This slot must be filled for the @@ -2682,6 +2848,8 @@ Mapping Object Structures .. c:member:: objobjargproc PyMappingMethods.mp_ass_subscript + .. corresponding-type-slot:: Py_mp_ass_subscript + This function is used by :c:func:`PyObject_SetItem`, :c:func:`PyObject_DelItem`, :c:func:`PySequence_SetSlice` and :c:func:`PySequence_DelSlice`. It has the same signature as @@ -2695,9 +2863,6 @@ Mapping Object Structures Sequence Object Structures -------------------------- -.. sectionauthor:: Amaury Forgeot d'Arc - - .. c:type:: PySequenceMethods This structure holds pointers to the functions which an object uses to @@ -2705,6 +2870,8 @@ Sequence Object Structures .. c:member:: lenfunc PySequenceMethods.sq_length + .. corresponding-type-slot:: Py_sq_length + This function is used by :c:func:`PySequence_Size` and :c:func:`PyObject_Size`, and has the same signature. It is also used for handling negative indices via the :c:member:`~PySequenceMethods.sq_item` @@ -2712,18 +2879,24 @@ Sequence Object Structures .. c:member:: binaryfunc PySequenceMethods.sq_concat + .. corresponding-type-slot:: Py_sq_concat + This function is used by :c:func:`PySequence_Concat` and has the same signature. It is also used by the ``+`` operator, after trying the numeric addition via the :c:member:`~PyNumberMethods.nb_add` slot. .. c:member:: ssizeargfunc PySequenceMethods.sq_repeat + .. corresponding-type-slot:: Py_sq_repeat + This function is used by :c:func:`PySequence_Repeat` and has the same signature. It is also used by the ``*`` operator, after trying numeric multiplication via the :c:member:`~PyNumberMethods.nb_multiply` slot. .. c:member:: ssizeargfunc PySequenceMethods.sq_item + .. corresponding-type-slot:: Py_sq_item + This function is used by :c:func:`PySequence_GetItem` and has the same signature. It is also used by :c:func:`PyObject_GetItem`, after trying the subscription via the :c:member:`~PyMappingMethods.mp_subscript` slot. @@ -2737,6 +2910,8 @@ Sequence Object Structures .. c:member:: ssizeobjargproc PySequenceMethods.sq_ass_item + .. corresponding-type-slot:: Py_sq_ass_item + This function is used by :c:func:`PySequence_SetItem` and has the same signature. It is also used by :c:func:`PyObject_SetItem` and :c:func:`PyObject_DelItem`, after trying the item assignment and deletion @@ -2746,6 +2921,8 @@ Sequence Object Structures .. c:member:: objobjproc PySequenceMethods.sq_contains + .. corresponding-type-slot:: Py_sq_contains + This function may be used by :c:func:`PySequence_Contains` and has the same signature. This slot may be left to ``NULL``, in this case :c:func:`!PySequence_Contains` simply traverses the sequence until it @@ -2753,6 +2930,8 @@ Sequence Object Structures .. c:member:: binaryfunc PySequenceMethods.sq_inplace_concat + .. corresponding-type-slot:: Py_sq_inplace_concat + This function is used by :c:func:`PySequence_InPlaceConcat` and has the same signature. It should modify its first operand, and return it. This slot may be left to ``NULL``, in this case :c:func:`!PySequence_InPlaceConcat` @@ -2762,6 +2941,8 @@ Sequence Object Structures .. c:member:: ssizeargfunc PySequenceMethods.sq_inplace_repeat + .. corresponding-type-slot:: Py_sq_inplace_repeat + This function is used by :c:func:`PySequence_InPlaceRepeat` and has the same signature. It should modify its first operand, and return it. This slot may be left to ``NULL``, in this case :c:func:`!PySequence_InPlaceRepeat` @@ -2775,10 +2956,6 @@ Sequence Object Structures Buffer Object Structures ------------------------ -.. sectionauthor:: Greg J. Stein -.. sectionauthor:: Benjamin Peterson -.. sectionauthor:: Stefan Krah - .. c:type:: PyBufferProcs This structure holds pointers to the functions required by the @@ -2787,6 +2964,8 @@ Buffer Object Structures .. c:member:: getbufferproc PyBufferProcs.bf_getbuffer + .. corresponding-type-slot:: Py_bf_getbuffer + The signature of this function is:: int (PyObject *exporter, Py_buffer *view, int flags); @@ -2796,24 +2975,42 @@ Buffer Object Structures steps: (1) Check if the request can be met. If not, raise :exc:`BufferError`, - set :c:expr:`view->obj` to ``NULL`` and return ``-1``. + set ``view->obj`` to ``NULL`` and return ``-1``. (2) Fill in the requested fields. (3) Increment an internal counter for the number of exports. - (4) Set :c:expr:`view->obj` to *exporter* and increment :c:expr:`view->obj`. + (4) Set ``view->obj`` to *exporter* and increment ``view->obj``. (5) Return ``0``. + **Thread safety:** + + In the :term:`free-threaded build`, implementations must ensure: + + * The export counter increment in step (3) is atomic. + + * The underlying buffer data remains valid and at a stable memory + location for the lifetime of all exports. + + * For objects that support resizing or reallocation (such as + :class:`bytearray`), the export counter is checked atomically before + such operations, and :exc:`BufferError` is raised if exports exist. + + * The function is safe to call concurrently from multiple threads. + + See also :ref:`thread-safety-memoryview` for the Python-level + thread safety guarantees of :class:`memoryview` objects. + If *exporter* is part of a chain or tree of buffer providers, two main schemes can be used: * Re-export: Each member of the tree acts as the exporting object and - sets :c:expr:`view->obj` to a new reference to itself. + sets ``view->obj`` to a new reference to itself. * Redirect: The buffer request is redirected to the root object of the - tree. Here, :c:expr:`view->obj` will be a new reference to the root + tree. Here, ``view->obj`` will be a new reference to the root object. The individual fields of *view* are described in section @@ -2836,6 +3033,8 @@ Buffer Object Structures .. c:member:: releasebufferproc PyBufferProcs.bf_releasebuffer + .. corresponding-type-slot:: Py_bf_releasebuffer + The signature of this function is:: void (PyObject *exporter, Py_buffer *view); @@ -2849,13 +3048,23 @@ Buffer Object Structures (2) If the counter is ``0``, free all memory associated with *view*. + **Thread safety:** + + In the :term:`free-threaded build`: + + * The export counter decrement in step (1) must be atomic. + + * Resource cleanup when the counter reaches zero must be done atomically, + as the final release may race with concurrent releases from other + threads and dellocation must only happen once. + The exporter MUST use the :c:member:`~Py_buffer.internal` field to keep track of buffer-specific resources. This field is guaranteed to remain constant, while a consumer MAY pass a copy of the original buffer as the *view* argument. - This function MUST NOT decrement :c:expr:`view->obj`, since that is + This function MUST NOT decrement ``view->obj``, since that is done automatically in :c:func:`PyBuffer_Release` (this scheme is useful for breaking reference cycles). @@ -2870,8 +3079,6 @@ Buffer Object Structures Async Object Structures ----------------------- -.. sectionauthor:: Yury Selivanov - .. versionadded:: 3.5 .. c:type:: PyAsyncMethods @@ -2890,6 +3097,8 @@ Async Object Structures .. c:member:: unaryfunc PyAsyncMethods.am_await + .. corresponding-type-slot:: Py_am_await + The signature of this function is:: PyObject *am_await(PyObject *self); @@ -2901,6 +3110,8 @@ Async Object Structures .. c:member:: unaryfunc PyAsyncMethods.am_aiter + .. corresponding-type-slot:: Py_am_aiter + The signature of this function is:: PyObject *am_aiter(PyObject *self); @@ -2913,6 +3124,8 @@ Async Object Structures .. c:member:: unaryfunc PyAsyncMethods.am_anext + .. corresponding-type-slot:: Py_am_anext + The signature of this function is:: PyObject *am_anext(PyObject *self); @@ -2923,6 +3136,8 @@ Async Object Structures .. c:member:: sendfunc PyAsyncMethods.am_send + .. corresponding-type-slot:: Py_am_send + The signature of this function is:: PySendResult am_send(PyObject *self, PyObject *arg, PyObject **result); diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 84fee05cb4ce20..634dcbce7a5791 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -5,9 +5,6 @@ Unicode Objects and Codecs -------------------------- -.. sectionauthor:: Marc-André Lemburg -.. sectionauthor:: Georg Brandl - Unicode Objects ^^^^^^^^^^^^^^^ @@ -65,6 +62,27 @@ Python: .. versionadded:: 3.3 + The structure of a particular object can be determined using the following + macros. + The macros cannot fail; their behavior is undefined if their argument + is not a Python Unicode object. + + .. c:namespace:: NULL + + .. c:macro:: PyUnicode_IS_COMPACT(o) + + True if *o* uses the :c:struct:`PyCompactUnicodeObject` structure. + + .. versionadded:: 3.3 + + + .. c:macro:: PyUnicode_IS_COMPACT_ASCII(o) + + True if *o* uses the :c:struct:`PyASCIIObject` structure. + + .. versionadded:: 3.3 + + The following APIs are C macros and static inlined functions for fast checks and access to internal read-only data of Unicode objects: @@ -321,12 +339,22 @@ These APIs can be used to work with surrogates: Check if *ch* is a low surrogate (``0xDC00 <= ch <= 0xDFFF``). +.. c:function:: Py_UCS4 Py_UNICODE_HIGH_SURROGATE(Py_UCS4 ch) + + Return the high UTF-16 surrogate (``0xD800`` to ``0xDBFF``) for a Unicode + code point in the range ``[0x10000; 0x10FFFF]``. + +.. c:function:: Py_UCS4 Py_UNICODE_LOW_SURROGATE(Py_UCS4 ch) + + Return the low UTF-16 surrogate (``0xDC00`` to ``0xDFFF``) for a Unicode + code point in the range ``[0x10000; 0x10FFFF]``. + .. c:function:: Py_UCS4 Py_UNICODE_JOIN_SURROGATES(Py_UCS4 high, Py_UCS4 low) Join two surrogate code points and return a single :c:type:`Py_UCS4` value. *high* and *low* are respectively the leading and trailing surrogates in a - surrogate pair. *high* must be in the range [0xD800; 0xDBFF] and *low* must - be in the range [0xDC00; 0xDFFF]. + surrogate pair. *high* must be in the range ``[0xD800; 0xDBFF]`` and *low* must + be in the range ``[0xDC00; 0xDFFF]``. Creating and accessing Unicode strings @@ -734,7 +762,7 @@ APIs: 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 + Return the number of written characters, or return ``-1`` and raise an exception on error. .. versionadded:: 3.3 @@ -747,7 +775,7 @@ APIs: 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's 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. @@ -1146,7 +1174,7 @@ These are the UTF-8 codec APIs: .. versionadded:: 3.3 .. versionchanged:: 3.7 - The return type is now ``const char *`` rather of ``char *``. + The return type is now ``const char *`` rather than ``char *``. .. versionchanged:: 3.10 This function is a part of the :ref:`limited API `. @@ -1168,7 +1196,7 @@ These are the UTF-8 codec APIs: .. versionadded:: 3.3 .. versionchanged:: 3.7 - The return type is now ``const char *`` rather of ``char *``. + The return type is now ``const char *`` rather than ``char *``. UTF-32 Codecs @@ -1827,8 +1855,6 @@ object. On success, return ``0``. On error, set an exception, leave the writer unchanged, and return ``-1``. - .. versionadded:: 3.14 - .. c:function:: int PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, const wchar_t *str, Py_ssize_t size) Write the wide string *str* into *writer*. @@ -1839,7 +1865,7 @@ object. On success, return ``0``. On error, set an exception, leave the writer unchanged, and return ``-1``. -.. c:function:: int PyUnicodeWriter_WriteUCS4(PyUnicodeWriter *writer, Py_UCS4 *str, Py_ssize_t size) +.. c:function:: int PyUnicodeWriter_WriteUCS4(PyUnicodeWriter *writer, const Py_UCS4 *str, Py_ssize_t size) Writer the UCS4 string *str* into *writer*. @@ -1855,13 +1881,23 @@ object. On success, return ``0``. On error, set an exception, leave the writer unchanged, and return ``-1``. + To write a :class:`str` subclass which overrides the :meth:`~object.__str__` + method, :c:func:`PyUnicode_FromObject` can be used to get the original + string. + .. c:function:: int PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) Call :c:func:`PyObject_Repr` on *obj* and write the output into *writer*. + If *obj* is ``NULL``, write the string ``""`` into *writer*. + On success, return ``0``. On error, set an exception, leave the writer unchanged, and return ``-1``. + .. versionchanged:: 3.14.4 + + Added support for ``NULL``. + .. c:function:: int PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, Py_ssize_t start, Py_ssize_t end) Write the substring ``str[start:end]`` into *writer*. @@ -1917,7 +1953,7 @@ The following API is deprecated. whether you selected a "narrow" or "wide" Unicode version of Python at build time. - .. deprecated-removed:: 3.13 3.15 + .. deprecated-removed:: 3.13 3.16 .. c:function:: int PyUnicode_READY(PyObject *unicode) diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst index fb07fec7effce8..6256bf7a1454a9 100644 --- a/Doc/c-api/veryhigh.rst +++ b/Doc/c-api/veryhigh.rst @@ -13,8 +13,9 @@ the interpreter. Several of these functions accept a start symbol from the grammar as a parameter. The available start symbols are :c:data:`Py_eval_input`, -:c:data:`Py_file_input`, and :c:data:`Py_single_input`. These are described -following the functions which accept them as parameters. +:c:data:`Py_file_input`, :c:data:`Py_single_input`, and +:c:data:`Py_func_type_input`. These are described following the functions +which accept them as parameters. Note also that several of these functions take :c:expr:`FILE*` parameters. One particular issue which needs to be handled carefully is that the :c:type:`FILE` @@ -99,18 +100,12 @@ the same library that the Python runtime is using. Otherwise, Python may not handle script file with LF line ending correctly. -.. c:function:: int PyRun_InteractiveOne(FILE *fp, const char *filename) - - This is a simplified interface to :c:func:`PyRun_InteractiveOneFlags` below, - leaving *flags* set to ``NULL``. - - -.. c:function:: int PyRun_InteractiveOneFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) +.. c:function:: int PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags) Read and execute a single statement from a file associated with an interactive device according to the *flags* argument. The user will be - prompted using ``sys.ps1`` and ``sys.ps2``. *filename* is decoded from the - :term:`filesystem encoding and error handler`. + prompted using ``sys.ps1`` and ``sys.ps2``. *filename* must be a Python + :class:`str` object. Returns ``0`` when the input was executed successfully, ``-1`` if there was an exception, or an error code @@ -119,6 +114,19 @@ the same library that the Python runtime is using. :file:`Python.h`, so must be included specifically if needed.) +.. c:function:: int PyRun_InteractiveOne(FILE *fp, const char *filename) + + This is a simplified interface to :c:func:`PyRun_InteractiveOneFlags` below, + leaving *flags* set to ``NULL``. + + +.. c:function:: int PyRun_InteractiveOneFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) + + Similar to :c:func:`PyRun_InteractiveOneObject`, but *filename* is a + :c:expr:`const char*`, which is decoded from the + :term:`filesystem encoding and error handler`. + + .. c:function:: int PyRun_InteractiveLoop(FILE *fp, const char *filename) This is a simplified interface to :c:func:`PyRun_InteractiveLoopFlags` below, @@ -140,7 +148,7 @@ the same library that the Python runtime is using. interpreter prompt is about to become idle and wait for user input from the terminal. The return value is ignored. Overriding this hook can be used to integrate the interpreter's prompt with other - event loops, as done in the :file:`Modules/_tkinter.c` in the + event loops, as done in :file:`Modules/_tkinter.c` in the Python source code. .. versionchanged:: 3.12 @@ -183,7 +191,7 @@ the same library that the Python runtime is using. objects *globals* and *locals* with the compiler flags specified by *flags*. *globals* must be a dictionary; *locals* can be any object that implements the mapping protocol. The parameter *start* specifies - the start token that should be used to parse the source code. + the start symbol and must be one of the :ref:`available start symbols `. Returns the result of executing the code as a Python object, or ``NULL`` if an exception was raised. @@ -231,9 +239,9 @@ the same library that the Python runtime is using. .. c:function:: PyObject* Py_CompileStringObject(const char *str, PyObject *filename, int start, PyCompilerFlags *flags, int optimize) Parse and compile the Python source code in *str*, returning the resulting code - object. The start token is given by *start*; this can be used to constrain the - code which can be compiled and should be :c:data:`Py_eval_input`, - :c:data:`Py_file_input`, or :c:data:`Py_single_input`. The filename specified by + object. The start symbol is given by *start*; this can be used to constrain the + code which can be compiled and should be :ref:`available start symbols + `. The filename specified by *filename* is used to construct the code object and may appear in tracebacks or :exc:`SyntaxError` exception messages. This returns ``NULL`` if the code cannot be parsed or compiled. @@ -296,32 +304,6 @@ the same library that the Python runtime is using. true on success, false on failure. -.. c:var:: int Py_eval_input - - .. index:: single: Py_CompileString (C function) - - The start symbol from the Python grammar for isolated expressions; for use with - :c:func:`Py_CompileString`. - - -.. c:var:: int Py_file_input - - .. index:: single: Py_CompileString (C function) - - The start symbol from the Python grammar for sequences of statements as read - from a file or other source; for use with :c:func:`Py_CompileString`. This is - the symbol to use when compiling arbitrarily long Python source code. - - -.. c:var:: int Py_single_input - - .. index:: single: Py_CompileString (C function) - - The start symbol from the Python grammar for a single statement; for use with - :c:func:`Py_CompileString`. This is the symbol used for the interactive - interpreter loop. - - .. c:struct:: PyCompilerFlags This is the structure used to hold compiler flags. In cases where code is only @@ -365,3 +347,92 @@ the same library that the Python runtime is using. as :c:macro:`CO_FUTURE_ANNOTATIONS` to enable features normally selectable using :ref:`future statements `. See :ref:`c_codeobject_flags` for a complete list. + + +.. _start-symbols: + +Available start symbols +^^^^^^^^^^^^^^^^^^^^^^^ + + +.. c:var:: int Py_eval_input + + .. index:: single: Py_CompileString (C function) + + The start symbol from the Python grammar for isolated expressions; for use with + :c:func:`Py_CompileString`. + + +.. c:var:: int Py_file_input + + .. index:: single: Py_CompileString (C function) + + The start symbol from the Python grammar for sequences of statements as read + from a file or other source; for use with :c:func:`Py_CompileString`. This is + the symbol to use when compiling arbitrarily long Python source code. + + +.. c:var:: int Py_single_input + + .. index:: single: Py_CompileString (C function) + + The start symbol from the Python grammar for a single statement; for use with + :c:func:`Py_CompileString`. This is the symbol used for the interactive + interpreter loop. + + +.. c:var:: int Py_func_type_input + + .. index:: single: Py_CompileString (C function) + + The start symbol from the Python grammar for a function type; for use with + :c:func:`Py_CompileString`. This is used to parse "signature type comments" + from :pep:`484`. + + This requires the :c:macro:`PyCF_ONLY_AST` flag to be set. + + .. seealso:: + * :py:class:`ast.FunctionType` + * :pep:`484` + + .. versionadded:: 3.8 + + +Stack Effects +^^^^^^^^^^^^^ + +.. seealso:: + :py:func:`dis.stack_effect` + + +.. c:macro:: PY_INVALID_STACK_EFFECT + + Sentinel value representing an invalid stack effect. + + This is currently equivalent to ``INT_MAX``. + + .. versionadded:: 3.8 + + +.. c:function:: int PyCompile_OpcodeStackEffect(int opcode, int oparg) + + Compute the stack effect of *opcode* with argument *oparg*. + + On success, this function returns the stack effect; on failure, this + returns :c:macro:`PY_INVALID_STACK_EFFECT`. + + .. versionadded:: 3.4 + + +.. c:function:: int PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump) + + Similar to :c:func:`PyCompile_OpcodeStackEffect`, but don't include the + stack effect of jumping if *jump* is zero. + + If *jump* is ``0``, this will not include the stack effect of jumping, but + if *jump* is ``1`` or ``-1``, this will include it. + + On success, this function returns the stack effect; on failure, this + returns :c:macro:`PY_INVALID_STACK_EFFECT`. + + .. versionadded:: 3.8 diff --git a/Doc/c-api/weakref.rst b/Doc/c-api/weakref.rst index c3c6cf413dcef5..8762a003c5218d 100644 --- a/Doc/c-api/weakref.rst +++ b/Doc/c-api/weakref.rst @@ -19,7 +19,14 @@ as much as it can. .. c:function:: int PyWeakref_CheckRef(PyObject *ob) - Return non-zero if *ob* is a reference object. This function always succeeds. + Return non-zero if *ob* is a reference object or a subclass of the reference + type. This function always succeeds. + + +.. c:function:: int PyWeakref_CheckRefExact(PyObject *ob) + + Return non-zero if *ob* is a reference object, but not a subclass of the + reference type. This function always succeeds. .. c:function:: int PyWeakref_CheckProxy(PyObject *ob) @@ -36,7 +43,15 @@ as much as it can. should accept a single parameter, which will be the weak reference object itself. *callback* may also be ``None`` or ``NULL``. If *ob* is not a weakly referenceable object, or if *callback* is not callable, ``None``, or - ``NULL``, this will return ``NULL`` and raise :exc:`TypeError`. + ``NULL``, this will raise :exc:`TypeError` and return ``NULL``. + + .. versionchanged:: next + Raise :exc:`!TypeError` if *callback* is not callable, ``None``, or + ``NULL``. + + .. seealso:: + :c:func:`PyType_SUPPORTS_WEAKREFS` for checking if *ob* is weakly + referenceable. .. c:function:: PyObject* PyWeakref_NewProxy(PyObject *ob, PyObject *callback) @@ -48,7 +63,15 @@ as much as it can. collected; it should accept a single parameter, which will be the weak reference object itself. *callback* may also be ``None`` or ``NULL``. If *ob* is not a weakly referenceable object, or if *callback* is not callable, - ``None``, or ``NULL``, this will return ``NULL`` and raise :exc:`TypeError`. + ``NULL``, this will raise :exc:`TypeError` and return ``NULL``. + + .. versionchanged:: next + Raise :exc:`!TypeError` if *callback* is not callable, ``None``, or + ``NULL``. + + .. seealso:: + :c:func:`PyType_SUPPORTS_WEAKREFS` for checking if *ob* is weakly + referenceable. .. c:function:: int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) @@ -64,30 +87,6 @@ as much as it can. .. versionadded:: 3.13 -.. c:function:: PyObject* PyWeakref_GetObject(PyObject *ref) - - Return a :term:`borrowed reference` to the referenced object from a weak - reference, *ref*. If the referent is no longer live, returns ``Py_None``. - - .. note:: - - This function returns a :term:`borrowed reference` to the referenced object. - This means that you should always call :c:func:`Py_INCREF` on the object - except when it cannot be destroyed before the last usage of the borrowed - reference. - - .. deprecated-removed:: 3.13 3.15 - Use :c:func:`PyWeakref_GetRef` instead. - - -.. c:function:: PyObject* PyWeakref_GET_OBJECT(PyObject *ref) - - Similar to :c:func:`PyWeakref_GetObject`, but does no error checking. - - .. deprecated-removed:: 3.13 3.15 - 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 diff --git a/Doc/conf.py b/Doc/conf.py index 1c1f36e5bc0737..9b103a594b235c 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -8,15 +8,13 @@ import os import sys -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')) sys.path.append(os.path.abspath('includes')) -# Python specific content from Doc/Tools/extensions/pyspecific.py -from pyspecific import SOURCE_URI +from patchlevel import get_header_version_info, get_version_info # General configuration # --------------------- @@ -33,6 +31,7 @@ 'issue_role', 'lexers', 'misc_news', + 'profiling_trace', 'pydoc_topics', 'pyspecific', 'sphinx.ext.coverage', @@ -42,8 +41,10 @@ # Skip if downstream redistributors haven't installed them _OPTIONAL_EXTENSIONS = ( + 'linklint.ext', 'notfound.extension', 'sphinxext.opengraph', + 'sphinxcontrib.rsvgconverter', ) for optional_ext in _OPTIONAL_EXTENSIONS: try: @@ -70,11 +71,12 @@ # General substitutions. project = 'Python' copyright = "2001 Python Software Foundation" +_doc_authors = 'Python documentation authors' # 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 = import_module('patchlevel').get_version_info() +version, release = get_version_info() rst_epilog = f""" .. |python_version_literal| replace:: ``Python {version}`` @@ -174,6 +176,7 @@ ('c:type', '__int64'), ('c:type', 'unsigned __int64'), ('c:type', 'double'), + ('c:type', '_Float16'), # Standard C structures ('c:struct', 'in6_addr'), ('c:struct', 'in_addr'), @@ -221,31 +224,14 @@ ('envvar', 'USER'), ('envvar', 'USERNAME'), ('envvar', 'USERPROFILE'), - # Deprecated function that was never documented: - ('py:func', 'getargspec'), - ('py:func', 'inspect.getargspec'), - # Undocumented modules that users shouldn't have to worry about - # (implementation details of `os.path`): - ('py:mod', 'ntpath'), - ('py:mod', 'posixpath'), ] # Temporary undocumented names. # In future this list must be empty. nitpick_ignore += [ - # Undocumented public C macros - ('c:macro', 'Py_BUILD_ASSERT'), - ('c:macro', 'Py_BUILD_ASSERT_EXPR'), - # Do not error nit-picky mode builds when _SubParsersAction.add_parser cannot - # be resolved, as the method is currently undocumented. For context, see - # https://github.com/python/cpython/pull/103289. - ('py:meth', '_SubParsersAction.add_parser'), # Attributes/methods/etc. that definitely should be documented better, # but are deferred for now: - ('py:attr', '__annotations__'), - ('py:meth', '__missing__'), ('py:attr', '__wrapped__'), - ('py:meth', 'index'), # list.index, tuple.index, etc. ] # gh-106948: Copy standard C types declared in the "c:type" domain and C @@ -361,81 +347,85 @@ \sphinxstrong{Python Software Foundation}\\ Email: \sphinxemail{docs@python.org} } -\let\Verbatim=\OriginalVerbatim -\let\endVerbatim=\endOriginalVerbatim \setcounter{tocdepth}{2} ''', # The paper size ('letterpaper' or 'a4paper'). 'papersize': 'a4paper', # The font size ('10pt', '11pt' or '12pt'). 'pointsize': '10pt', + 'maxlistdepth': '8', # See https://github.com/python/cpython/issues/139588 } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). -_stdauthor = 'Guido van Rossum and the Python development team' latex_documents = [ - ('c-api/index', 'c-api.tex', 'The Python/C API', _stdauthor, 'manual'), + ('c-api/index', 'c-api.tex', 'The Python/C API', _doc_authors, 'manual'), ( 'extending/index', 'extending.tex', 'Extending and Embedding Python', - _stdauthor, + _doc_authors, 'manual', ), ( 'installing/index', 'installing.tex', 'Installing Python Modules', - _stdauthor, + _doc_authors, 'manual', ), ( 'library/index', 'library.tex', 'The Python Library Reference', - _stdauthor, + _doc_authors, 'manual', ), ( 'reference/index', 'reference.tex', 'The Python Language Reference', - _stdauthor, + _doc_authors, 'manual', ), ( 'tutorial/index', 'tutorial.tex', 'Python Tutorial', - _stdauthor, + _doc_authors, 'manual', ), ( 'using/index', 'using.tex', 'Python Setup and Usage', - _stdauthor, + _doc_authors, 'manual', ), ( 'faq/index', 'faq.tex', 'Python Frequently Asked Questions', - _stdauthor, + _doc_authors, 'manual', ), ( 'whatsnew/' + version, 'whatsnew.tex', 'What\'s New in Python', - 'A. M. Kuchling', + _doc_authors, 'howto', ), ] # Collect all HOWTOs individually latex_documents.extend( - ('howto/' + fn[:-4], 'howto-' + fn[:-4] + '.tex', '', _stdauthor, 'howto') + ( + 'howto/' + fn[:-4], + 'howto-' + fn[:-4] + '.tex', + '', + _doc_authors, + 'howto', + ) for fn in os.listdir('howto') if fn.endswith('.rst') and fn != 'index.rst' ) @@ -446,14 +436,38 @@ # Options for Epub output # ----------------------- -epub_author = 'Python Documentation Authors' +epub_author = _doc_authors epub_publisher = 'Python Software Foundation' -epub_exclude_files = ('index.xhtml', 'download.xhtml') +epub_exclude_files = ( + 'index.xhtml', + 'download.xhtml', + '_static/tachyon-example-flamegraph.html', + '_static/tachyon-example-heatmap.html', +) # index pages are not valid xhtml # https://github.com/sphinx-doc/sphinx/issues/12359 epub_use_index = False +# translation tag +# --------------- + +language_code = None +for arg in sys.argv: + if arg.startswith('language='): + language_code = arg.split('=', 1)[1] + +if language_code: + tags.add('translation') # noqa: F821 + + rst_epilog += f"""\ +.. _TRANSLATION_REPO: https://github.com/python/python-docs-{language_code.replace("_", "-").lower()} +""" # noqa: F821 +else: + rst_epilog += """\ +.. _TRANSLATION_REPO: https://github.com/python +""" + # Options for the coverage checker # -------------------------------- @@ -539,15 +553,20 @@ r'https://unix.org/version2/whatsnew/lp64_wp.html', ] + # Options for sphinx.ext.extlinks # ------------------------------- +v = get_header_version_info() +branch = "main" if v.releaselevel == "alpha" else f"{v.major}.{v.minor}" + # This config is a dictionary of external sites, # mapping unique short aliases to a base URL and a prefix. # https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html extlinks = { + "oss-fuzz": ("https://issues.oss-fuzz.com/issues/%s", "#%s"), "pypi": ("https://pypi.org/project/%s/", "%s"), - "source": (SOURCE_URI, "%s"), + "source": (f"https://github.com/python/cpython/tree/{branch}/%s", "%s"), } extlinks_detect_hardcoded_links = True @@ -557,6 +576,18 @@ # Relative filename of the data files refcount_file = 'data/refcounts.dat' stable_abi_file = 'data/stable_abi.dat' +threadsafety_file = 'data/threadsafety.dat' + +# Options for notfound.extension +# ------------------------------- + +if not os.getenv("READTHEDOCS"): + if language_code: + notfound_urls_prefix = ( + f'/{language_code.replace("_", "-").lower()}/{version}/' + ) + else: + notfound_urls_prefix = f'/{version}/' # Options for sphinxext-opengraph # ------------------------------- @@ -567,14 +598,11 @@ 'image': '_static/og-image.png', 'line_color': '#3776ab', } -if 'builder_html' in tags: # noqa: F821 - ogp_custom_meta_tags = [ - '', - ] - 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 += [ - '', - '', - ] +ogp_custom_meta_tags = ('',) +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 += ( + '', + '', + ) diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 144c5608e07426..60c02aabeb89c5 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -1141,6 +1141,9 @@ PyInterpreterState_Clear:PyInterpreterState*:interp:: PyInterpreterState_Delete:void::: PyInterpreterState_Delete:PyInterpreterState*:interp:: +PyInterpreterState_GetDict:PyObject*::0: +PyInterpreterState_GetDict:PyInterpreterState*:interp:: + PyInterpreterState_GetID:int64_t::: PyInterpreterState_GetID:PyInterpreterState*:interp:: @@ -1469,6 +1472,9 @@ PyModule_Create2:PyObject*::+1: PyModule_Create2:PyModuleDef*:def:: PyModule_Create2:int:module_api_version:: +PyModule_Exec:int::: +PyModule_ExecDef:PyObject*:module:0: + PyModule_ExecDef:int::: PyModule_ExecDef:PyObject*:module:0: PyModule_ExecDef:PyModuleDef*:def:: @@ -1482,6 +1488,10 @@ PyModule_FromDefAndSpec2:PyModuleDef*:def:: PyModule_FromDefAndSpec2:PyObject*:spec:0: PyModule_FromDefAndSpec2:int:module_api_version:: +PyModule_FromSlotsAndSpec:PyObject*::+1: +PyModule_FromSlotsAndSpec:const PyModuleDef_Slot *:slots:: +PyModule_FromSlotsAndSpec:PyObject*:spec:0: + PyModule_GetDef:PyModuleDef*::0: PyModule_GetDef:PyObject*:module:0: @@ -1503,6 +1513,14 @@ PyModule_GetNameObject:PyObject*:module:0: PyModule_GetState:void*::: PyModule_GetState:PyObject*:module:0: +PyModule_GetStateSize:int::: +PyModule_GetStateSize:PyObject*:module:0: +PyModule_GetToken:Py_ssize_t**:result:: + +PyModule_GetToken:int::: +PyModule_GetToken:PyObject*:module:0: +PyModule_GetToken:void**:result:: + PyModule_New:PyObject*::+1: PyModule_New:char*:name:: @@ -2019,6 +2037,11 @@ PySeqIter_Check:PyObject *:op:0: PySeqIter_New:PyObject*::+1: PySeqIter_New:PyObject*:seq:0: +PySentinel_New:PyObject*::+1: +PySentinel_New:const char*:name:: +PySentinel_New:const char*:module_name:: +PySentinel_New:const char*:repr:: + PySequence_Check:int::: PySequence_Check:PyObject*:o:0: @@ -2409,6 +2432,20 @@ PyType_GetFlags:PyTypeObject*:type:0: PyType_GetName:PyObject*::+1: PyType_GetName:PyTypeObject*:type:0: +PyType_GetModule:PyObject*::0: +PyType_GetModule:PyTypeObject*:type:0: + +PyType_GetModule_DuringGC:PyObject*::0: +PyType_GetModule_DuringGC:PyTypeObject*:type:0: + +PyType_GetModuleByToken:PyObject*::+1: +PyType_GetModuleByToken:PyTypeObject*:type:0: +PyType_GetModuleByToken:PyModuleDef*:def:: + +PyType_GetModuleByToken_DuringGC:PyObject*::0: +PyType_GetModuleByToken_DuringGC:PyTypeObject*:type:0: +PyType_GetModuleByToken_DuringGC:PyModuleDef*:mod_token:: + PyType_GetModuleByDef:PyObject*::0: PyType_GetModuleByDef:PyTypeObject*:type:0: PyType_GetModuleByDef:PyModuleDef*:def:: @@ -2947,12 +2984,6 @@ PyWeakref_CheckProxy:PyObject*:ob:0: PyWeakref_CheckRef:int::: PyWeakref_CheckRef:PyObject*:ob:0: -PyWeakref_GET_OBJECT:PyObject*::0: -PyWeakref_GET_OBJECT:PyObject*:ref:0: - -PyWeakref_GetObject:PyObject*::0: -PyWeakref_GetObject:PyObject*:ref:0: - PyWeakref_GetRef:int::: PyWeakref_GetRef:PyObject*:ref:0: PyWeakref_GetRef:PyObject**:pobj:+1: diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 0d0dfb3843260e..86080fac716383 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -1,5 +1,22 @@ role,name,added,ifdef_note,struct_abi_kind +macro,METH_CLASS,3.2,, +macro,METH_COEXIST,3.2,, +macro,METH_FASTCALL,3.10,, +macro,METH_METHOD,3.7,, +macro,METH_NOARGS,3.2,, +macro,METH_O,3.2,, +macro,METH_STATIC,3.2,, +macro,METH_VARARGS,3.2,, macro,PY_VECTORCALL_ARGUMENTS_OFFSET,3.12,, +type,PyABIInfo,3.15,,full-abi +func,PyABIInfo_Check,3.15,, +macro,PyABIInfo_DEFAULT_ABI_VERSION,3.15,, +macro,PyABIInfo_DEFAULT_FLAGS,3.15,, +macro,PyABIInfo_FREETHREADED,3.15,, +macro,PyABIInfo_FREETHREADING_AGNOSTIC,3.15,, +macro,PyABIInfo_GIL,3.15,, +macro,PyABIInfo_STABLE,3.15,, +macro,PyABIInfo_VAR,3.15,, func,PyAIter_Check,3.10,, func,PyArg_Parse,3.2,, func,PyArg_ParseTuple,3.2,, @@ -8,6 +25,26 @@ func,PyArg_UnpackTuple,3.2,, func,PyArg_VaParse,3.2,, func,PyArg_VaParseTupleAndKeywords,3.2,, func,PyArg_ValidateKeywordArguments,3.2,, +macro,PyBUF_ANY_CONTIGUOUS,3.11,, +macro,PyBUF_CONTIG,3.11,, +macro,PyBUF_CONTIG_RO,3.11,, +macro,PyBUF_C_CONTIGUOUS,3.11,, +macro,PyBUF_FORMAT,3.11,, +macro,PyBUF_FULL,3.11,, +macro,PyBUF_FULL_RO,3.11,, +macro,PyBUF_F_CONTIGUOUS,3.11,, +macro,PyBUF_INDIRECT,3.11,, +macro,PyBUF_MAX_NDIM,3.11,, +macro,PyBUF_ND,3.11,, +macro,PyBUF_READ,3.11,, +macro,PyBUF_RECORDS,3.11,, +macro,PyBUF_RECORDS_RO,3.11,, +macro,PyBUF_SIMPLE,3.11,, +macro,PyBUF_STRIDED,3.11,, +macro,PyBUF_STRIDED_RO,3.11,, +macro,PyBUF_STRIDES,3.11,, +macro,PyBUF_WRITABLE,3.11,, +macro,PyBUF_WRITE,3.11,, data,PyBaseObject_Type,3.2,, func,PyBool_FromLong,3.2,, data,PyBool_Type,3.2,, @@ -92,6 +129,12 @@ func,PyComplex_FromDoubles,3.2,, func,PyComplex_ImagAsDouble,3.2,, func,PyComplex_RealAsDouble,3.2,, data,PyComplex_Type,3.2,, +type,PyCriticalSection,3.15,,full-abi +type,PyCriticalSection2,3.15,,full-abi +func,PyCriticalSection2_Begin,3.15,, +func,PyCriticalSection2_End,3.15,, +func,PyCriticalSection_Begin,3.15,, +func,PyCriticalSection_End,3.15,, func,PyDescr_NewClassMethod,3.2,, func,PyDescr_NewGetSet,3.2,, func,PyDescr_NewMember,3.2,, @@ -123,6 +166,7 @@ func,PyDict_Merge,3.2,, func,PyDict_MergeFromSeq2,3.2,, func,PyDict_New,3.2,, func,PyDict_Next,3.2,, +func,PyDict_SetDefaultRef,3.15,, func,PyDict_SetItem,3.2,, func,PyDict_SetItemString,3.2,, func,PyDict_Size,3.2,, @@ -325,6 +369,10 @@ func,PyImport_ImportModuleLevel,3.2,, func,PyImport_ImportModuleLevelObject,3.7,, func,PyImport_ReloadModule,3.2,, func,PyIndex_Check,3.8,, +type,PyInterpreterGuard,3.15,,opaque +func,PyInterpreterGuard_Close,3.15,, +func,PyInterpreterGuard_FromCurrent,3.15,, +func,PyInterpreterGuard_FromView,3.15,, type,PyInterpreterState,3.2,,opaque func,PyInterpreterState_Clear,3.2,, func,PyInterpreterState_Delete,3.2,, @@ -332,6 +380,10 @@ func,PyInterpreterState_Get,3.9,, func,PyInterpreterState_GetDict,3.8,, func,PyInterpreterState_GetID,3.7,, func,PyInterpreterState_New,3.2,, +type,PyInterpreterView,3.15,,opaque +func,PyInterpreterView_Close,3.15,, +func,PyInterpreterView_FromCurrent,3.15,, +func,PyInterpreterView_FromMain,3.15,, func,PyIter_Check,3.8,, func,PyIter_Next,3.2,, func,PyIter_NextItem,3.14,, @@ -351,8 +403,14 @@ func,PyList_SetSlice,3.2,, func,PyList_Size,3.2,, func,PyList_Sort,3.2,, data,PyList_Type,3.2,, +type,PyLongExport,3.15,,full-abi +type,PyLongLayout,3.15,,full-abi type,PyLongObject,3.2,,opaque data,PyLongRangeIter_Type,3.2,, +type,PyLongWriter,3.15,,opaque +func,PyLongWriter_Create,3.15,, +func,PyLongWriter_Discard,3.15,, +func,PyLongWriter_Finish,3.15,, func,PyLong_AsDouble,3.2,, func,PyLong_AsInt,3.13,, func,PyLong_AsInt32,3.14,, @@ -371,6 +429,8 @@ func,PyLong_AsUnsignedLongLong,3.2,, func,PyLong_AsUnsignedLongLongMask,3.2,, func,PyLong_AsUnsignedLongMask,3.2,, func,PyLong_AsVoidPtr,3.2,, +func,PyLong_Export,3.15,, +func,PyLong_FreeExport,3.15,, func,PyLong_FromDouble,3.2,, func,PyLong_FromInt32,3.14,, func,PyLong_FromInt64,3.14,, @@ -387,7 +447,9 @@ func,PyLong_FromUnsignedLongLong,3.2,, func,PyLong_FromUnsignedNativeBytes,3.14,, func,PyLong_FromVoidPtr,3.2,, func,PyLong_GetInfo,3.2,, +func,PyLong_GetNativeLayout,3.15,, data,PyLong_Type,3.2,, +macro,PyMODEXPORT_FUNC,3.15,, data,PyMap_Type,3.2,, func,PyMapping_Check,3.2,, func,PyMapping_GetItemString,3.2,, @@ -422,9 +484,10 @@ func,PyMemoryView_GetContiguous,3.2,, data,PyMemoryView_Type,3.2,, type,PyMethodDef,3.2,,full-abi data,PyMethodDescr_Type,3.2,, -type,PyModuleDef,3.2,,full-abi -type,PyModuleDef_Base,3.2,,full-abi +type,PyModuleDef,3.2,,abi3t-opaque +type,PyModuleDef_Base,3.2,,abi3t-opaque func,PyModuleDef_Init,3.5,, +type,PyModuleDef_Slot,3.5,,full-abi data,PyModuleDef_Type,3.5,, func,PyModule_Add,3.13,, func,PyModule_AddFunctions,3.7,, @@ -434,8 +497,10 @@ func,PyModule_AddObjectRef,3.10,, func,PyModule_AddStringConstant,3.2,, func,PyModule_AddType,3.10,, func,PyModule_Create2,3.2,, +func,PyModule_Exec,3.15,, func,PyModule_ExecDef,3.7,, func,PyModule_FromDefAndSpec2,3.7,, +func,PyModule_FromSlotsAndSpec,3.15,, func,PyModule_GetDef,3.2,, func,PyModule_GetDict,3.2,, func,PyModule_GetFilename,3.2,, @@ -443,6 +508,10 @@ func,PyModule_GetFilenameObject,3.2,, func,PyModule_GetName,3.2,, func,PyModule_GetNameObject,3.7,, func,PyModule_GetState,3.2,, +func,PyModule_GetStateSize,3.15,, +func,PyModule_GetState_DuringGC,3.15,, +func,PyModule_GetToken,3.15,, +func,PyModule_GetToken_DuringGC,3.15,, func,PyModule_New,3.2,, func,PyModule_NewObject,3.7,, func,PyModule_SetDocString,3.7,, @@ -510,6 +579,7 @@ func,PyObject_ASCII,3.2,, func,PyObject_AsFileDescriptor,3.2,, func,PyObject_Bytes,3.2,, func,PyObject_Call,3.2,, +func,PyObject_CallFinalizerFromDealloc,3.15,, func,PyObject_CallFunction,3.2,, func,PyObject_CallFunctionObjArgs,3.2,, func,PyObject_CallMethod,3.2,, @@ -545,6 +615,7 @@ func,PyObject_GetIter,3.2,, func,PyObject_GetOptionalAttr,3.13,, func,PyObject_GetOptionalAttrString,3.13,, func,PyObject_GetTypeData,3.12,, +func,PyObject_GetTypeData_DuringGC,3.15,, func,PyObject_HasAttr,3.2,, func,PyObject_HasAttrString,3.2,, func,PyObject_HasAttrStringWithError,3.13,, @@ -613,6 +684,19 @@ func,PySlice_GetIndicesEx,3.2,, func,PySlice_New,3.2,, data,PySlice_Type,3.2,, func,PySlice_Unpack,3.7,, +type,PySlot,3.15,,full-abi +macro,PySlot_DATA,3.15,, +macro,PySlot_END,3.15,, +macro,PySlot_FUNC,3.15,, +macro,PySlot_INT64,3.15,, +macro,PySlot_INTPTR,3.15,, +macro,PySlot_OPTIONAL,3.15,, +macro,PySlot_PTR,3.15,, +macro,PySlot_PTR_STATIC,3.15,, +macro,PySlot_SIZE,3.15,, +macro,PySlot_STATIC,3.15,, +macro,PySlot_STATIC_DATA,3.15,, +macro,PySlot_UINT64,3.15,, func,PyState_AddModule,3.3,, func,PyState_FindModule,3.2,, func,PyState_RemoveModule,3.3,, @@ -634,21 +718,24 @@ func,PySys_GetObject,3.2,, func,PySys_GetOptionalAttr,3.15,, func,PySys_GetOptionalAttrString,3.15,, func,PySys_GetXOptions,3.7,, -func,PySys_ResetWarnOptions,3.2,, func,PySys_SetArgv,3.2,, func,PySys_SetArgvEx,3.2,, func,PySys_SetObject,3.2,, func,PySys_WriteStderr,3.2,, func,PySys_WriteStdout,3.2,, type,PyThreadState,3.2,,opaque +type,PyThreadStateToken,3.15,,opaque func,PyThreadState_Clear,3.2,, func,PyThreadState_Delete,3.2,, +func,PyThreadState_Ensure,3.15,, +func,PyThreadState_EnsureFromView,3.15,, func,PyThreadState_Get,3.2,, func,PyThreadState_GetDict,3.2,, func,PyThreadState_GetFrame,3.10,, func,PyThreadState_GetID,3.10,, func,PyThreadState_GetInterpreter,3.10,, func,PyThreadState_New,3.2,, +func,PyThreadState_Release,3.15,, func,PyThreadState_SetAsyncExc,3.2,, func,PyThreadState_Swap,3.2,, func,PyThread_GetInfo,3.3,, @@ -693,17 +780,23 @@ func,PyType_ClearCache,3.2,, func,PyType_Freeze,3.14,, func,PyType_FromMetaclass,3.12,, func,PyType_FromModuleAndSpec,3.10,, +func,PyType_FromSlots,3.15,, func,PyType_FromSpec,3.2,, func,PyType_FromSpecWithBases,3.3,, func,PyType_GenericAlloc,3.2,, func,PyType_GenericNew,3.2,, func,PyType_GetBaseByToken,3.14,, +func,PyType_GetBaseByToken_DuringGC,3.15,, func,PyType_GetFlags,3.2,, func,PyType_GetFullyQualifiedName,3.13,, func,PyType_GetModule,3.10,, func,PyType_GetModuleByDef,3.13,, +func,PyType_GetModuleByToken,3.15,, +func,PyType_GetModuleByToken_DuringGC,3.15,, func,PyType_GetModuleName,3.13,, func,PyType_GetModuleState,3.10,, +func,PyType_GetModuleState_DuringGC,3.15,, +func,PyType_GetModule_DuringGC,3.15,, func,PyType_GetName,3.11,, func,PyType_GetQualName,3.11,, func,PyType_GetSlot,3.4,, @@ -828,16 +921,25 @@ member,PyVarObject.ob_size,3.2,, func,PyVectorcall_Call,3.12,, func,PyVectorcall_NARGS,3.12,, type,PyWeakReference,3.2,,opaque -func,PyWeakref_GetObject,3.2,, func,PyWeakref_GetRef,3.13,, func,PyWeakref_NewProxy,3.2,, func,PyWeakref_NewRef,3.2,, data,PyWrapperDescr_Type,3.2,, func,PyWrapper_New,3.2,, data,PyZip_Type,3.2,, +macro,Py_ASNATIVEBYTES_ALLOW_INDEX,3.14,, +macro,Py_ASNATIVEBYTES_BIG_ENDIAN,3.14,, +macro,Py_ASNATIVEBYTES_DEFAULTS,3.14,, +macro,Py_ASNATIVEBYTES_LITTLE_ENDIAN,3.14,, +macro,Py_ASNATIVEBYTES_NATIVE_ENDIAN,3.14,, +macro,Py_ASNATIVEBYTES_REJECT_NEGATIVE,3.14,, +macro,Py_ASNATIVEBYTES_UNSIGNED_BUFFER,3.14,, +macro,Py_AUDIT_READ,3.12,, func,Py_AddPendingCall,3.2,, func,Py_AtExit,3.2,, macro,Py_BEGIN_ALLOW_THREADS,3.2,, +macro,Py_BEGIN_CRITICAL_SECTION,3.15,, +macro,Py_BEGIN_CRITICAL_SECTION2,3.15,, macro,Py_BLOCK_THREADS,3.2,, func,Py_BuildValue,3.2,, func,Py_BytesMain,3.8,, @@ -845,6 +947,8 @@ func,Py_CompileString,3.2,, func,Py_DecRef,3.2,, func,Py_DecodeLocale,3.7,, macro,Py_END_ALLOW_THREADS,3.2,, +macro,Py_END_CRITICAL_SECTION,3.15,, +macro,Py_END_CRITICAL_SECTION2,3.15,, func,Py_EncodeLocale,3.7,, func,Py_EndInterpreter,3.2,, func,Py_EnterRecursiveCall,3.9,, @@ -865,6 +969,7 @@ func,Py_GetPlatform,3.2,, func,Py_GetRecursionLimit,3.2,, func,Py_GetVersion,3.2,, data,Py_HasFileSystemDefaultEncoding,3.2,, +func,Py_IS_TYPE,3.15,, func,Py_IncRef,3.2,, func,Py_Initialize,3.2,, func,Py_InitializeEx,3.2,, @@ -881,22 +986,159 @@ func,Py_NewInterpreter,3.2,, func,Py_NewRef,3.10,, func,Py_PACK_FULL_VERSION,3.14,, func,Py_PACK_VERSION,3.14,, +macro,Py_READONLY,3.12,, func,Py_REFCNT,3.14,, +macro,Py_RELATIVE_OFFSET,3.12,, func,Py_ReprEnter,3.2,, func,Py_ReprLeave,3.2,, +func,Py_SET_SIZE,3.15,, +func,Py_SIZE,3.15,, func,Py_SetProgramName,3.2,, func,Py_SetPythonHome,3.2,, func,Py_SetRecursionLimit,3.2,, +macro,Py_TPFLAGS_BASETYPE,3.2,, +macro,Py_TPFLAGS_DEFAULT,3.2,, +macro,Py_TPFLAGS_HAVE_GC,3.2,, +macro,Py_TPFLAGS_HAVE_VECTORCALL,3.12,, +macro,Py_TPFLAGS_ITEMS_AT_END,3.12,, +macro,Py_TPFLAGS_METHOD_DESCRIPTOR,3.8,, +macro,Py_TP_USE_SPEC,3.14,, func,Py_TYPE,3.14,, +macro,Py_T_BOOL,3.12,, +macro,Py_T_BYTE,3.12,, +macro,Py_T_CHAR,3.12,, +macro,Py_T_DOUBLE,3.12,, +macro,Py_T_FLOAT,3.12,, +macro,Py_T_INT,3.12,, +macro,Py_T_LONG,3.12,, +macro,Py_T_LONGLONG,3.12,, +macro,Py_T_OBJECT_EX,3.12,, +macro,Py_T_PYSSIZET,3.12,, +macro,Py_T_SHORT,3.12,, +macro,Py_T_STRING,3.12,, +macro,Py_T_STRING_INPLACE,3.12,, +macro,Py_T_UBYTE,3.12,, +macro,Py_T_UINT,3.12,, +macro,Py_T_ULONG,3.12,, +macro,Py_T_ULONGLONG,3.12,, +macro,Py_T_USHORT,3.12,, type,Py_UCS4,3.2,, macro,Py_UNBLOCK_THREADS,3.2,, data,Py_UTF8Mode,3.8,, func,Py_VaBuildValue,3.2,, data,Py_Version,3.11,, func,Py_XNewRef,3.10,, +macro,Py_am_aiter,3.5,, +macro,Py_am_anext,3.5,, +macro,Py_am_await,3.5,, +macro,Py_am_send,3.10,, +macro,Py_bf_getbuffer,3.11,, +macro,Py_bf_releasebuffer,3.11,, type,Py_buffer,3.11,,full-abi type,Py_intptr_t,3.2,, +macro,Py_mod_abi,3.15,, +macro,Py_mod_create,3.5,, +macro,Py_mod_doc,3.15,, +macro,Py_mod_exec,3.5,, +macro,Py_mod_gil,3.13,, +macro,Py_mod_methods,3.15,, +macro,Py_mod_multiple_interpreters,3.12,, +macro,Py_mod_name,3.15,, +macro,Py_mod_slots,3.15,, +macro,Py_mod_state_clear,3.15,, +macro,Py_mod_state_free,3.15,, +macro,Py_mod_state_size,3.15,, +macro,Py_mod_state_traverse,3.15,, +macro,Py_mod_token,3.15,, +macro,Py_mp_ass_subscript,3.2,, +macro,Py_mp_length,3.2,, +macro,Py_mp_subscript,3.2,, +macro,Py_nb_absolute,3.2,, +macro,Py_nb_add,3.2,, +macro,Py_nb_and,3.2,, +macro,Py_nb_bool,3.2,, +macro,Py_nb_divmod,3.2,, +macro,Py_nb_float,3.2,, +macro,Py_nb_floor_divide,3.2,, +macro,Py_nb_index,3.2,, +macro,Py_nb_inplace_add,3.2,, +macro,Py_nb_inplace_and,3.2,, +macro,Py_nb_inplace_floor_divide,3.2,, +macro,Py_nb_inplace_lshift,3.2,, +macro,Py_nb_inplace_matrix_multiply,3.5,, +macro,Py_nb_inplace_multiply,3.2,, +macro,Py_nb_inplace_or,3.2,, +macro,Py_nb_inplace_power,3.2,, +macro,Py_nb_inplace_remainder,3.2,, +macro,Py_nb_inplace_rshift,3.2,, +macro,Py_nb_inplace_subtract,3.2,, +macro,Py_nb_inplace_true_divide,3.2,, +macro,Py_nb_inplace_xor,3.2,, +macro,Py_nb_int,3.2,, +macro,Py_nb_invert,3.2,, +macro,Py_nb_lshift,3.2,, +macro,Py_nb_matrix_multiply,3.5,, +macro,Py_nb_multiply,3.2,, +macro,Py_nb_negative,3.2,, +macro,Py_nb_or,3.2,, +macro,Py_nb_positive,3.2,, +macro,Py_nb_power,3.2,, +macro,Py_nb_remainder,3.2,, +macro,Py_nb_rshift,3.2,, +macro,Py_nb_subtract,3.2,, +macro,Py_nb_true_divide,3.2,, +macro,Py_nb_xor,3.2,, +macro,Py_slot_end,3.15,, +macro,Py_slot_invalid,3.15,, +macro,Py_slot_subslots,3.15,, +macro,Py_sq_ass_item,3.2,, +macro,Py_sq_concat,3.2,, +macro,Py_sq_contains,3.2,, +macro,Py_sq_inplace_concat,3.2,, +macro,Py_sq_inplace_repeat,3.2,, +macro,Py_sq_item,3.2,, +macro,Py_sq_length,3.2,, +macro,Py_sq_repeat,3.2,, type,Py_ssize_t,3.2,, +macro,Py_tp_alloc,3.2,, +macro,Py_tp_base,3.2,, +macro,Py_tp_bases,3.2,, +macro,Py_tp_basicsize,3.15,, +macro,Py_tp_call,3.2,, +macro,Py_tp_clear,3.2,, +macro,Py_tp_dealloc,3.2,, +macro,Py_tp_del,3.2,, +macro,Py_tp_descr_get,3.2,, +macro,Py_tp_descr_set,3.2,, +macro,Py_tp_doc,3.2,, +macro,Py_tp_extra_basicsize,3.15,, +macro,Py_tp_finalize,3.5,, +macro,Py_tp_flags,3.15,, +macro,Py_tp_free,3.2,, +macro,Py_tp_getattr,3.2,, +macro,Py_tp_getattro,3.2,, +macro,Py_tp_getset,3.2,, +macro,Py_tp_hash,3.2,, +macro,Py_tp_init,3.2,, +macro,Py_tp_is_gc,3.2,, +macro,Py_tp_itemsize,3.15,, +macro,Py_tp_iter,3.2,, +macro,Py_tp_iternext,3.2,, +macro,Py_tp_members,3.2,, +macro,Py_tp_metaclass,3.15,, +macro,Py_tp_methods,3.2,, +macro,Py_tp_module,3.15,, +macro,Py_tp_name,3.15,, +macro,Py_tp_new,3.2,, +macro,Py_tp_repr,3.2,, +macro,Py_tp_richcompare,3.2,, +macro,Py_tp_setattr,3.2,, +macro,Py_tp_setattro,3.2,, +macro,Py_tp_slots,3.15,, +macro,Py_tp_str,3.2,, +macro,Py_tp_token,3.14,, +macro,Py_tp_traverse,3.2,, +macro,Py_tp_vectorcall,3.14,, type,Py_uintptr_t,3.2,, type,allocfunc,3.2,, type,binaryfunc,3.2,, diff --git a/Doc/data/threadsafety.dat b/Doc/data/threadsafety.dat new file mode 100644 index 00000000000000..ea5a24a5505e20 --- /dev/null +++ b/Doc/data/threadsafety.dat @@ -0,0 +1,284 @@ +# Thread safety annotations for C API functions. +# +# Each line has the form: +# function_name : level +# +# Where level is one of: +# incompatible -- not safe even with external locking +# compatible -- safe if the caller serializes all access with external locks +# distinct -- safe on distinct objects without external synchronization +# shared -- safe for concurrent use on the same object +# atomic -- atomic +# +# Lines beginning with '#' are ignored. +# The function name must match the C domain identifier used in the documentation. + +# Synchronization primitives (Doc/c-api/synchronization.rst) +PyMutex_Lock:atomic: +PyMutex_Unlock:atomic: +PyMutex_IsLocked:atomic: + + +# Dictionary objects (Doc/c-api/dict.rst) + +# Type checks - read ob_type pointer, always safe +PyDict_Check:atomic: +PyDict_CheckExact:atomic: + +# Creation - pure allocation, no shared state +PyDict_New:atomic: + +# Lock-free lookups - use _Py_dict_lookup_threadsafe(), no locking. +# Atomic with simple types. +PyDict_Contains:shared: +PyDict_ContainsString:atomic: +PyDict_GetItemRef:shared: +PyDict_GetItemStringRef:atomic: +PyDict_Size:atomic: +PyDict_GET_SIZE:atomic: + +# Borrowed-reference lookups - lock-free dict access but returned +# borrowed reference is unsafe in free-threaded builds without +# external synchronization +PyDict_GetItem:compatible: +PyDict_GetItemWithError:compatible: +PyDict_GetItemString:compatible: +PyDict_SetDefault:compatible: + +# Iteration - no locking; returns borrowed refs +PyDict_Next:compatible: + +# Single-item mutations - protected by per-object critical section +PyDict_SetItem:shared: +PyDict_SetItemString:atomic: +PyDict_DelItem:shared: +PyDict_DelItemString:atomic: +PyDict_SetDefaultRef:shared: +PyDict_Pop:shared: +PyDict_PopString:atomic: + +# Bulk reads - hold per-object lock for duration +PyDict_Clear:atomic: +PyDict_Copy:atomic: +PyDict_Keys:atomic: +PyDict_Values:atomic: +PyDict_Items:atomic: + +# Merge/update - lock target dict; also lock source when it is a dict +PyDict_Update:shared: +PyDict_Merge:shared: +PyDict_MergeFromSeq2:shared: + +# Watcher registration - no synchronization on interpreter state +PyDict_AddWatcher:compatible: +PyDict_ClearWatcher:compatible: + +# Per-dict watcher tags - non-atomic RMW on _ma_watcher_tag; +# safe on distinct dicts only +PyDict_Watch:distinct: +PyDict_Unwatch:distinct: + + +# List objects (Doc/c-api/list.rst) + +# Type checks - read ob_type pointer, always safe +PyList_Check:atomic: +PyList_CheckExact:atomic: + +# Creation - pure allocation, no shared state +PyList_New:atomic: + +# Size - uses atomic load on free-threaded builds +PyList_Size:atomic: +PyList_GET_SIZE:atomic: + +# Strong-reference lookup - lock-free with atomic ops +PyList_GetItemRef:atomic: + +# Borrowed-reference lookups - no locking; returned borrowed +# reference is unsafe in free-threaded builds without +# external synchronization +PyList_GetItem:compatible: +PyList_GET_ITEM:compatible: + +# Single-item mutations - hold per-object lock for duration; +# appear atomic to lock-free readers +PyList_SetItem:atomic: +PyList_Append:atomic: + +# Insert - protected by per-object critical section; shifts +# elements so lock-free readers may observe intermediate states +PyList_Insert:shared: + +# Initialization macro - no synchronization; normally only used +# to fill in new lists where there is no previous content +PyList_SET_ITEM:compatible: + +# Bulk operations - hold per-object lock for duration +PyList_GetSlice:atomic: +PyList_AsTuple:atomic: +PyList_Clear:atomic: + +# Reverse - protected by per-object critical section; swaps +# elements so lock-free readers may observe intermediate states +PyList_Reverse:shared: + +# Slice assignment - lock target list; also lock source when it +# is a list +PyList_SetSlice:shared: + +# Sort - per-object lock held; the list is emptied before sorting +# so other threads may observe an empty list, but they won't see the +# intermediate states of the sort +PyList_Sort:shared: + +# Extend - lock target list; also lock source when it is a +# list, set, or dict +PyList_Extend:shared: + +# Creation - pure allocation, no shared state +PyBytes_FromString:atomic: +PyBytes_FromStringAndSize:atomic: +PyBytes_DecodeEscape:atomic: + +# Creation from formatting C primitives - pure allocation, no shared state +PyBytes_FromFormat:atomic: +PyBytes_FromFormatV:atomic: + +# Creation from object - uses buffer protocol so may call arbitrary code; +# safe as long as the buffer is not mutated by another thread during the operation +PyBytes_FromObject:shared: + +# Size - uses atomic load on free-threaded builds +PyBytes_Size:atomic: +PyBytes_GET_SIZE:atomic: + +# Raw data - no locking; mutating it is unsafe if the bytes object is shared between threads +PyBytes_AsString:compatible: +PyBytes_AS_STRING:compatible: +PyBytes_AsStringAndSize:compatible: + +# Concatenation - uses buffer protocol; safe as long as buffer is not mutated by another thread during the operation +PyBytes_Concat:shared: +PyBytes_ConcatAndDel:shared: +PyBytes_Join:shared: + +# Resizing - safe if the object is unique +_PyBytes_Resize:distinct: + +# Repr - atomic as bytes are immutable +PyBytes_Repr:atomic: + +# Creation from object - may call arbitrary code +PyByteArray_FromObject:shared: + +# Creation - pure allocation, no shared state +PyByteArray_FromStringAndSize:atomic: + +# Concatenation - uses buffer protocol; safe as long as buffer is not mutated by another thread during the operation +PyByteArray_Concat:shared: + +# Size - uses atomic load on free-threaded builds +PyByteArray_Size:atomic: +PyByteArray_GET_SIZE:atomic: + +# Raw data - no locking; mutating it is unsafe if the bytearray object is shared between threads +PyByteArray_AsString:compatible: +PyByteArray_AS_STRING:compatible: + +# Creation - may iterate the iterable argument, calling arbitrary code. +# Atomic for sets, frozensets, dicts, and frozendicts. +PySet_New:shared: +PyFrozenSet_New:shared: + +# Size - uses atomic load on free-threaded builds +PySet_Size:atomic: +PySet_GET_SIZE:atomic: + +# Contains - lock-free, atomic with simple types +PySet_Contains:shared: + +# Mutations - hold per-object lock for duration +# atomic with simple types +PySet_Add:shared: +PySet_Discard:shared: + +# Pop - hold per-object lock for duration +PySet_Pop:atomic: + +# Clear - empties the set before clearing +PySet_Clear:atomic: + +# Capsule objects (Doc/c-api/capsule.rst) + +# Type check - read ob_type pointer, always safe +PyCapsule_CheckExact:atomic: + +# Creation - pure allocation, no shared state +PyCapsule_New:atomic: + +# Validation - reads pointer and name fields; safe on distinct objects +PyCapsule_IsValid:distinct: + +# Getters - read struct fields; safe on distinct objects but +# concurrent access to the same capsule requires external synchronization +PyCapsule_GetPointer:distinct: +PyCapsule_GetName:distinct: +PyCapsule_GetDestructor:distinct: +PyCapsule_GetContext:distinct: + +# Setters - write struct fields; safe on distinct objects but +# concurrent access to the same capsule requires external synchronization +PyCapsule_SetPointer:distinct: +PyCapsule_SetName:distinct: +PyCapsule_SetDestructor:distinct: +PyCapsule_SetContext:distinct: + +# Import - looks up a capsule from a module attribute and +# calls PyCapsule_GetPointer; may call arbitrary code +PyCapsule_Import:compatible: + +# Tuple objects + +# Creation - pure allocation, no shared state +PyTuple_New:atomic: +PyTuple_FromArray:atomic: +PyTuple_Pack:atomic: + +# Size - tuples are immutable so size never changes +PyTuple_Size:atomic: +PyTuple_GET_SIZE:atomic: + +# Borrowed-reference lookups - tuples are immutable so items +# never change, however the tuple must be kept alive while using the borrowed reference +PyTuple_GetItem:compatible: +PyTuple_GET_ITEM:compatible: + +# Slice - creates a new tuple from an existing tuple +PyTuple_GetSlice:atomic: + +# SetItem - only usable on tuples with refcount 1 +PyTuple_SetItem:compatible: +PyTuple_SET_ITEM:compatible: + +# Resize - only usable on tuples with refcount 1 +_PyTuple_Resize:compatible: + +# Struct Sequence objects + +# Creation +PyStructSequence_NewType:atomic: +PyStructSequence_New:atomic: + +# Initialization - modifies the type object in place +PyStructSequence_InitType:distinct: +PyStructSequence_InitType2:distinct: + +# Borrowed-reference lookups - same as tuple items +PyStructSequence_GetItem:compatible: +PyStructSequence_GET_ITEM:compatible: + +# SetItem - only for filling in brand new instances +PyStructSequence_SetItem:compatible: +PyStructSequence_SET_ITEM:compatible: + diff --git a/Doc/deprecations/c-api-pending-removal-in-3.15.rst b/Doc/deprecations/c-api-pending-removal-in-3.15.rst index a3e335ecaf4324..6d0d47403ff2ed 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.15.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.15.rst @@ -3,12 +3,10 @@ Pending removal in Python 3.15 * The :c:func:`!PyImport_ImportModuleNoBlock`: Use :c:func:`PyImport_ImportModule` instead. -* :c:func:`PyWeakref_GetObject` and :c:func:`PyWeakref_GET_OBJECT`: +* :c:func:`!PyWeakref_GetObject` and :c:func:`!PyWeakref_GET_OBJECT`: Use :c:func:`PyWeakref_GetRef` instead. The `pythoncapi-compat project `__ 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. * :c:func:`!PyUnicode_AsDecodedObject`: Use :c:func:`PyCodec_Decode` instead. * :c:func:`!PyUnicode_AsDecodedUnicode`: @@ -48,88 +46,3 @@ Pending removal in Python 3.15 The `pythoncapi-compat project `__ 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. - - 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") ` instead. - * :c:var:`Py_VerboseFlag`: - Use :c:member:`PyConfig.verbose` or - :c:func:`PyConfig_Get("verbose") ` instead. - * :c:var:`Py_QuietFlag`: - Use :c:member:`PyConfig.quiet` or - :c:func:`PyConfig_Get("quiet") ` instead. - * :c:var:`Py_InteractiveFlag`: - Use :c:member:`PyConfig.interactive` or - :c:func:`PyConfig_Get("interactive") ` instead. - * :c:var:`Py_InspectFlag`: - Use :c:member:`PyConfig.inspect` or - :c:func:`PyConfig_Get("inspect") ` instead. - * :c:var:`Py_OptimizeFlag`: - Use :c:member:`PyConfig.optimization_level` or - :c:func:`PyConfig_Get("optimization_level") ` instead. - * :c:var:`Py_NoSiteFlag`: - Use :c:member:`PyConfig.site_import` or - :c:func:`PyConfig_Get("site_import") ` instead. - * :c:var:`Py_BytesWarningFlag`: - Use :c:member:`PyConfig.bytes_warning` or - :c:func:`PyConfig_Get("bytes_warning") ` instead. - * :c:var:`Py_FrozenFlag`: - Use :c:member:`PyConfig.pathconfig_warnings` or - :c:func:`PyConfig_Get("pathconfig_warnings") ` instead. - * :c:var:`Py_IgnoreEnvironmentFlag`: - Use :c:member:`PyConfig.use_environment` or - :c:func:`PyConfig_Get("use_environment") ` instead. - * :c:var:`Py_DontWriteBytecodeFlag`: - Use :c:member:`PyConfig.write_bytecode` or - :c:func:`PyConfig_Get("write_bytecode") ` instead. - * :c:var:`Py_NoUserSiteDirectory`: - Use :c:member:`PyConfig.user_site_directory` or - :c:func:`PyConfig_Get("user_site_directory") ` instead. - * :c:var:`Py_UnbufferedStdioFlag`: - Use :c:member:`PyConfig.buffered_stdio` or - :c:func:`PyConfig_Get("buffered_stdio") ` 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") ` instead. - * :c:var:`Py_IsolatedFlag`: - Use :c:member:`PyConfig.isolated` or - :c:func:`PyConfig_Get("isolated") ` instead. - * :c:var:`Py_LegacyWindowsFSEncodingFlag`: - Use :c:member:`PyPreConfig.legacy_windows_fs_encoding` or - :c:func:`PyConfig_Get("legacy_windows_fs_encoding") ` instead. - * :c:var:`Py_LegacyWindowsStdioFlag`: - Use :c:member:`PyConfig.legacy_windows_stdio` or - :c:func:`PyConfig_Get("legacy_windows_stdio") ` instead. - * :c:var:`!Py_FileSystemDefaultEncoding`, :c:var:`!Py_HasFileSystemDefaultEncoding`: - Use :c:member:`PyConfig.filesystem_encoding` or - :c:func:`PyConfig_Get("filesystem_encoding") ` instead. - * :c:var:`!Py_FileSystemDefaultEncodeErrors`: - Use :c:member:`PyConfig.filesystem_errors` or - :c:func:`PyConfig_Get("filesystem_errors") ` instead. - * :c:var:`!Py_UTF8Mode`: - Use :c:member:`PyPreConfig.utf8_mode` or - :c:func:`PyConfig_Get("utf8_mode") ` 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. diff --git a/Doc/deprecations/c-api-pending-removal-in-3.16.rst b/Doc/deprecations/c-api-pending-removal-in-3.16.rst index 9453f83799c43d..fe2d91cf316b18 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.16.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.16.rst @@ -1,4 +1,87 @@ Pending removal in Python 3.16 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* The bundled copy of ``libmpdec``. +* 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` or + :c:func:`PyConfig_Get("parser_debug") ` instead. + * :c:var:`Py_VerboseFlag`: + Use :c:member:`PyConfig.verbose` or + :c:func:`PyConfig_Get("verbose") ` instead. + * :c:var:`Py_QuietFlag`: + Use :c:member:`PyConfig.quiet` or + :c:func:`PyConfig_Get("quiet") ` instead. + * :c:var:`Py_InteractiveFlag`: + Use :c:member:`PyConfig.interactive` or + :c:func:`PyConfig_Get("interactive") ` instead. + * :c:var:`Py_InspectFlag`: + Use :c:member:`PyConfig.inspect` or + :c:func:`PyConfig_Get("inspect") ` instead. + * :c:var:`Py_OptimizeFlag`: + Use :c:member:`PyConfig.optimization_level` or + :c:func:`PyConfig_Get("optimization_level") ` instead. + * :c:var:`Py_NoSiteFlag`: + Use :c:member:`PyConfig.site_import` or + :c:func:`PyConfig_Get("site_import") ` instead. + * :c:var:`Py_BytesWarningFlag`: + Use :c:member:`PyConfig.bytes_warning` or + :c:func:`PyConfig_Get("bytes_warning") ` instead. + * :c:var:`Py_FrozenFlag`: + Use :c:member:`PyConfig.pathconfig_warnings` or + :c:func:`PyConfig_Get("pathconfig_warnings") ` instead. + * :c:var:`Py_IgnoreEnvironmentFlag`: + Use :c:member:`PyConfig.use_environment` or + :c:func:`PyConfig_Get("use_environment") ` instead. + * :c:var:`Py_DontWriteBytecodeFlag`: + Use :c:member:`PyConfig.write_bytecode` or + :c:func:`PyConfig_Get("write_bytecode") ` instead. + * :c:var:`Py_NoUserSiteDirectory`: + Use :c:member:`PyConfig.user_site_directory` or + :c:func:`PyConfig_Get("user_site_directory") ` instead. + * :c:var:`Py_UnbufferedStdioFlag`: + Use :c:member:`PyConfig.buffered_stdio` or + :c:func:`PyConfig_Get("buffered_stdio") ` 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") ` instead. + * :c:var:`Py_IsolatedFlag`: + Use :c:member:`PyConfig.isolated` or + :c:func:`PyConfig_Get("isolated") ` instead. + * :c:var:`Py_LegacyWindowsFSEncodingFlag`: + Use :c:member:`PyPreConfig.legacy_windows_fs_encoding` or + :c:func:`PyConfig_Get("legacy_windows_fs_encoding") ` instead. + * :c:var:`Py_LegacyWindowsStdioFlag`: + Use :c:member:`PyConfig.legacy_windows_stdio` or + :c:func:`PyConfig_Get("legacy_windows_stdio") ` instead. + * :c:var:`!Py_FileSystemDefaultEncoding`, :c:var:`!Py_HasFileSystemDefaultEncoding`: + Use :c:member:`PyConfig.filesystem_encoding` or + :c:func:`PyConfig_Get("filesystem_encoding") ` instead. + * :c:var:`!Py_FileSystemDefaultEncodeErrors`: + Use :c:member:`PyConfig.filesystem_errors` or + :c:func:`PyConfig_Get("filesystem_errors") ` instead. + * :c:var:`!Py_UTF8Mode`: + Use :c:member:`PyPreConfig.utf8_mode` or + :c:func:`PyConfig_Get("utf8_mode") ` 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. + +* :c:type:`Py_UNICODE` which was deprecated by :pep:`393`. diff --git a/Doc/deprecations/c-api-pending-removal-in-3.18.rst b/Doc/deprecations/c-api-pending-removal-in-3.18.rst index 564cfb79a0b513..820334ee43c82e 100644 --- a/Doc/deprecations/c-api-pending-removal-in-3.18.rst +++ b/Doc/deprecations/c-api-pending-removal-in-3.18.rst @@ -1,11 +1,12 @@ Pending removal in Python 3.18 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* Deprecated private functions (:gh:`128863`): +* The following private functions are deprecated + and planned for removal in Python 3.18: * :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:`!_PyDict_Pop()`: use :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`. @@ -31,7 +32,7 @@ Pending removal in Python 3.18 :c:func:`PyUnicodeWriter_WriteSubstring(writer, str, start, end) `. * :c:func:`!_PyUnicodeWriter_WriteASCIIString`: replace ``_PyUnicodeWriter_WriteASCIIString(&writer, str)`` with - :c:func:`PyUnicodeWriter_WriteUTF8(writer, str) `. + :c:func:`PyUnicodeWriter_WriteASCII(writer, str) `. * :c:func:`!_PyUnicodeWriter_WriteLatin1String`: replace ``_PyUnicodeWriter_WriteLatin1String(&writer, str)`` with :c:func:`PyUnicodeWriter_WriteUTF8(writer, str) `. @@ -39,7 +40,12 @@ Pending removal in Python 3.18 * :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`. + * :c:func:`PyGen_New`: (no replacement). + * :c:func:`PyGen_NewWithQualName`: (no replacement). + * :c:func:`PyCoro_New`: (no replacement). + * :c:func:`PyAsyncGen_New`: (no replacement). The `pythoncapi-compat project - `__ can be used to get these - new public functions on Python 3.13 and older. + `__ can be used to get + these new public functions on Python 3.13 and older. + (Contributed by Victor Stinner in :gh:`128863`.) diff --git a/Doc/deprecations/c-api-pending-removal-in-3.19.rst b/Doc/deprecations/c-api-pending-removal-in-3.19.rst new file mode 100644 index 00000000000000..ac9dcb8b424a17 --- /dev/null +++ b/Doc/deprecations/c-api-pending-removal-in-3.19.rst @@ -0,0 +1,4 @@ +Pending removal in Python 3.19 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* :pep:`456` embedders support for the string hashing scheme definition. diff --git a/Doc/deprecations/c-api-pending-removal-in-3.20.rst b/Doc/deprecations/c-api-pending-removal-in-3.20.rst new file mode 100644 index 00000000000000..8de55bbe7e695c --- /dev/null +++ b/Doc/deprecations/c-api-pending-removal-in-3.20.rst @@ -0,0 +1,16 @@ +Pending removal in Python 3.20 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* :c:func:`!_PyObject_CallMethodId`, :c:func:`!_PyObject_GetAttrId` and + :c:func:`!_PyUnicode_FromId` are deprecated since 3.15 and will be removed in + 3.20. Instead, use :c:func:`PyUnicode_InternFromString()` and cache the result in + the module state, then call :c:func:`PyObject_CallMethod` or + :c:func:`PyObject_GetAttr`. + (Contributed by Victor Stinner in :gh:`141049`.) + +* The ``cval`` field in :c:type:`PyComplexObject` (:gh:`128813`). + Use :c:func:`PyComplex_AsCComplex` and :c:func:`PyComplex_FromCComplex` + to convert a Python complex number to/from the C :c:type:`Py_complex` + representation. + +* Macros :c:macro:`!Py_MATH_PIl` and :c:macro:`!Py_MATH_El`. diff --git a/Doc/deprecations/index.rst b/Doc/deprecations/index.rst index d064f2bec42c22..0ac0a0289d5fd4 100644 --- a/Doc/deprecations/index.rst +++ b/Doc/deprecations/index.rst @@ -1,21 +1,31 @@ Deprecations ============ -.. include:: pending-removal-in-3.15.rst - .. include:: pending-removal-in-3.16.rst .. include:: pending-removal-in-3.17.rst +.. include:: pending-removal-in-3.18.rst + .. include:: pending-removal-in-3.19.rst +.. include:: pending-removal-in-3.20.rst + +.. include:: pending-removal-in-3.21.rst + .. include:: pending-removal-in-future.rst +.. include:: soft-deprecations.rst + C API deprecations ------------------ -.. include:: c-api-pending-removal-in-3.15.rst +.. include:: c-api-pending-removal-in-3.16.rst .. include:: c-api-pending-removal-in-3.18.rst +.. include:: c-api-pending-removal-in-3.19.rst + +.. include:: c-api-pending-removal-in-3.20.rst + .. include:: c-api-pending-removal-in-future.rst diff --git a/Doc/deprecations/pending-removal-in-3.13.rst b/Doc/deprecations/pending-removal-in-3.13.rst index 2fd2f12cc6a2c4..d5b8c80e8f9aa0 100644 --- a/Doc/deprecations/pending-removal-in-3.13.rst +++ b/Doc/deprecations/pending-removal-in-3.13.rst @@ -38,15 +38,3 @@ APIs: * :meth:`!unittest.TestProgram.usageExit` (:gh:`67048`) * :class:`!webbrowser.MacOSX` (:gh:`86421`) * :class:`classmethod` descriptor chaining (:gh:`89519`) -* :mod:`importlib.resources` deprecated methods: - - * ``contents()`` - * ``is_resource()`` - * ``open_binary()`` - * ``open_text()`` - * ``path()`` - * ``read_binary()`` - * ``read_text()`` - - Use :func:`importlib.resources.files` instead. Refer to `importlib-resources: Migrating from Legacy - `_ (:gh:`106531`) diff --git a/Doc/deprecations/pending-removal-in-3.14.rst b/Doc/deprecations/pending-removal-in-3.14.rst index 9aac10840a663f..171758156ab117 100644 --- a/Doc/deprecations/pending-removal-in-3.14.rst +++ b/Doc/deprecations/pending-removal-in-3.14.rst @@ -38,12 +38,6 @@ Pending removal in Python 3.14 is no current event loop set and it decides to create one. (Contributed by Serhiy Storchaka and Guido van Rossum in :gh:`100160`.) -* :mod:`collections.abc`: Deprecated :class:`!collections.abc.ByteString`. - Prefer :class:`!Sequence` or :class:`~collections.abc.Buffer`. - For use in typing, prefer a union, like ``bytes | bytearray``, - or :class:`collections.abc.Buffer`. - (Contributed by Shantanu Jain in :gh:`91896`.) - * :mod:`email`: Deprecated the *isdst* parameter in :func:`email.utils.localtime`. (Contributed by Alan Williams in :gh:`72346`.) @@ -96,9 +90,6 @@ Pending removal in Python 3.14 if :ref:`named placeholders ` are used and *parameters* is a sequence instead of a :class:`dict`. -* :mod:`typing`: :class:`!typing.ByteString`, deprecated since Python 3.9, - now causes a :exc:`DeprecationWarning` to be emitted when it is used. - * :mod:`urllib`: :class:`!urllib.parse.Quoter` is deprecated: it was not intended to be a public API. diff --git a/Doc/deprecations/pending-removal-in-3.15.rst b/Doc/deprecations/pending-removal-in-3.15.rst index 9505bcfa05af11..5c0e592f4caeda 100644 --- a/Doc/deprecations/pending-removal-in-3.15.rst +++ b/Doc/deprecations/pending-removal-in-3.15.rst @@ -3,14 +3,9 @@ Pending removal in Python 3.15 * The import system: - * Setting :attr:`~module.__cached__` on a module while + * Setting ``__cached__`` on a module while failing to set :attr:`__spec__.cached ` - is deprecated. In Python 3.15, :attr:`!__cached__` will cease to be set or - take into consideration by the import system or standard library. (:gh:`97879`) - - * Setting :attr:`~module.__package__` on a module while - failing to set :attr:`__spec__.parent ` - is deprecated. In Python 3.15, :attr:`!__package__` will cease to be set or + is deprecated. In Python 3.15, ``__cached__`` will cease to be set or take into consideration by the import system or standard library. (:gh:`97879`) * :mod:`ctypes`: @@ -33,16 +28,6 @@ Pending removal in Python 3.15 * ``load_module()`` method: use ``exec_module()`` instead. -* :class:`locale`: - - * The :func:`~locale.getdefaultlocale` function - has been deprecated since Python 3.11. - Its removal was originally planned for Python 3.13 (:gh:`90817`), - but has been postponed to Python 3.15. - Use :func:`~locale.getlocale`, :func:`~locale.setlocale`, - and :func:`~locale.getencoding` instead. - (Contributed by Hugo van Kemenade in :gh:`111187`.) - * :mod:`pathlib`: * :meth:`!.PurePath.is_reserved` @@ -64,13 +49,13 @@ Pending removal in Python 3.15 * :func:`~threading.RLock` will take no arguments in Python 3.15. Passing any arguments has been deprecated since Python 3.14, - as the Python version does not permit any arguments, + as the Python version does not permit any arguments, but the C version allows any number of positional or keyword arguments, ignoring every argument. * :mod:`types`: - * :class:`types.CodeType`: Accessing :attr:`~codeobject.co_lnotab` was + * :class:`types.CodeType`: Accessing :attr:`!codeobject.co_lnotab` was deprecated in :pep:`626` since 3.10 and was planned to be removed in 3.12, but it only got a proper :exc:`DeprecationWarning` in 3.12. @@ -92,7 +77,7 @@ Pending removal in Python 3.15 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 + * The :deco:`!typing.no_type_check_decorator` decorator function has been deprecated since Python 3.13. After eight years in the :mod:`typing` module, it has yet to be supported by any major type checker. @@ -107,6 +92,6 @@ Pending removal in Python 3.15 * :mod:`zipimport`: - * :meth:`~zipimport.zipimporter.load_module` has been deprecated since + * :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`.) + (:gh:`125746`.) diff --git a/Doc/deprecations/pending-removal-in-3.16.rst b/Doc/deprecations/pending-removal-in-3.16.rst index cdd76ee693f31b..5a28cc766a9596 100644 --- a/Doc/deprecations/pending-removal-in-3.16.rst +++ b/Doc/deprecations/pending-removal-in-3.16.rst @@ -8,6 +8,13 @@ Pending removal in Python 3.16 is deprecated. In Python 3.16, :attr:`!__loader__` will cease to be set or taken into consideration by the import system or the standard library. + * Setting :attr:`~module.__package__` on a module while + failing to set :attr:`__spec__.parent ` + is deprecated. In Python 3.16, :attr:`!__package__` will cease to be + taken into consideration by the import system or standard library. (:gh:`97879`) + +* The bundled copy of ``libmpdec``. + * :mod:`array`: * The ``'u'`` format code (:c:type:`wchar_t`) @@ -63,9 +70,9 @@ Pending removal in Python 3.16 * :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`.) + * 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`: @@ -84,12 +91,12 @@ Pending removal in Python 3.16 * :mod:`symtable`: - * The :meth:`Class.get_methods ` method + * The :meth:`!symtable.Class.get_methods` method has been deprecated since Python 3.14. * :mod:`sys`: - * The :func:`~sys._enablelegacywindowsfsencoding` function + * The :func:`!_enablelegacywindowsfsencoding` function has been deprecated since Python 3.13. Use the :envvar:`PYTHONLEGACYWINDOWSFSENCODING` environment variable instead. @@ -101,5 +108,5 @@ Pending removal in Python 3.16 * :mod:`tarfile`: - * The undocumented and unused :attr:`!TarFile.tarfile` attribute + * The undocumented and unused :attr:`!TarInfo.tarfile` attribute has been deprecated since Python 3.13. diff --git a/Doc/deprecations/pending-removal-in-3.17.rst b/Doc/deprecations/pending-removal-in-3.17.rst index 370b98307e5228..8ee7f335cc9514 100644 --- a/Doc/deprecations/pending-removal-in-3.17.rst +++ b/Doc/deprecations/pending-removal-in-3.17.rst @@ -1,6 +1,47 @@ Pending removal in Python 3.17 ------------------------------ +* :mod:`datetime`: + + * :meth:`~datetime.datetime.strptime` calls using a format string containing + ``%e`` (day of month) without a year. + This has been deprecated since Python 3.15. + (Contributed by Stan Ulbrych in :gh:`70647`.) + + +* :mod:`collections.abc`: + + - :class:`collections.abc.ByteString` is scheduled for removal in Python 3.17. + + Use ``isinstance(obj, collections.abc.Buffer)`` to test if ``obj`` + implements the :ref:`buffer protocol ` at runtime. For use + in type annotations, either use :class:`~collections.abc.Buffer` or a union + that explicitly specifies the types your code supports (e.g., + ``bytes | bytearray | memoryview``). + + :class:`!ByteString` was originally intended to be an abstract class that + would serve as a supertype of both :class:`bytes` and :class:`bytearray`. + However, since the ABC never had any methods, knowing that an object was an + instance of :class:`!ByteString` never actually told you anything useful + about the object. Other common buffer types such as :class:`memoryview` + were also never understood as subtypes of :class:`!ByteString` (either at + runtime or by static type checkers). + + See :pep:`PEP 688 <688#current-options>` for more details. + (Contributed by Shantanu Jain in :gh:`91896`.) + + +* :mod:`encodings`: + + - Passing non-ascii *encoding* names to :func:`encodings.normalize_encoding` + is deprecated and scheduled for removal in Python 3.17. + (Contributed by Stan Ulbrych in :gh:`136702`.) + +* :mod:`webbrowser`: + + - :class:`!webbrowser.MacOSXOSAScript` is deprecated in favour of + :class:`!webbrowser.MacOS`. (:gh:`137586`) + * :mod:`typing`: - Before Python 3.14, old-style unions were implemented using the private class @@ -8,3 +49,22 @@ Pending removal in Python 3.17 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. + - :class:`typing.ByteString`, deprecated since Python 3.9, is scheduled for removal in + Python 3.17. + + Use ``isinstance(obj, collections.abc.Buffer)`` to test if ``obj`` + implements the :ref:`buffer protocol ` at runtime. For use + in type annotations, either use :class:`~collections.abc.Buffer` or a union + that explicitly specifies the types your code supports (e.g., + ``bytes | bytearray | memoryview``). + + :class:`!ByteString` was originally intended to be an abstract class that + would serve as a supertype of both :class:`bytes` and :class:`bytearray`. + However, since the ABC never had any methods, knowing that an object was an + instance of :class:`!ByteString` never actually told you anything useful + about the object. Other common buffer types such as :class:`memoryview` + were also never understood as subtypes of :class:`!ByteString` (either at + runtime or by static type checkers). + + See :pep:`PEP 688 <688#current-options>` for more details. + (Contributed by Shantanu Jain in :gh:`91896`.) diff --git a/Doc/deprecations/pending-removal-in-3.18.rst b/Doc/deprecations/pending-removal-in-3.18.rst new file mode 100644 index 00000000000000..19113aab981bbc --- /dev/null +++ b/Doc/deprecations/pending-removal-in-3.18.rst @@ -0,0 +1,18 @@ +Pending removal in Python 3.18 +------------------------------ + +* No longer accept a boolean value when a file descriptor is expected. + (Contributed by Serhiy Storchaka in :gh:`82626`.) + +* :mod:`decimal`: + + * The non-standard and undocumented :class:`~decimal.Decimal` format + specifier ``'N'``, which is only supported in the :mod:`!decimal` module's + C implementation, has been deprecated since Python 3.13. + (Contributed by Serhiy Storchaka in :gh:`89902`.) + +* Deprecations defined by :pep:`829`: + + * ``import`` lines in :file:`{name}.pth` files are silently ignored. + + (Contributed by Barry Warsaw in :gh:`148641`.) diff --git a/Doc/deprecations/pending-removal-in-3.19.rst b/Doc/deprecations/pending-removal-in-3.19.rst index 25f9cba390de68..4a58c606ab7596 100644 --- a/Doc/deprecations/pending-removal-in-3.19.rst +++ b/Doc/deprecations/pending-removal-in-3.19.rst @@ -22,3 +22,21 @@ Pending removal in Python 3.19 supported depending on the backend implementation of hash functions. Prefer passing the initial data as a positional argument for maximum backwards compatibility. + +* :mod:`http.cookies`: + + * :meth:`http.cookies.Morsel.js_output` is deprecated and will be + removed in Python 3.19. + + * :meth:`http.cookies.BaseCookie.js_output` is deprecated and will be + removed in Python 3.19. + +* :mod:`imaplib`: + + * Altering :attr:`IMAP4.file ` is now deprecated + and slated for removal in Python 3.19. This property is now unused + and changing its value does not automatically close the current file. + + Before Python 3.14, this property was used to implement the corresponding + ``read()`` and ``readline()`` methods for :class:`~imaplib.IMAP4` but this + is no longer the case since then. diff --git a/Doc/deprecations/pending-removal-in-3.20.rst b/Doc/deprecations/pending-removal-in-3.20.rst new file mode 100644 index 00000000000000..011565dfbb090d --- /dev/null +++ b/Doc/deprecations/pending-removal-in-3.20.rst @@ -0,0 +1,55 @@ +Pending removal in Python 3.20 +------------------------------ + +* Calling the ``__new__()`` method of :class:`struct.Struct` without the + *format* argument is deprecated and will be removed in Python 3.20. Calling + :meth:`~object.__init__` method on initialized :class:`~struct.Struct` + objects is deprecated and will be removed in Python 3.20. + + (Contributed by Sergey B Kirpichev and Serhiy Storchaka in :gh:`143715`.) + +* The ``__version__``, ``version`` and ``VERSION`` attributes have been + deprecated in these standard library modules and will be removed in + Python 3.20. Use :py:data:`sys.version_info` instead. + + - :mod:`argparse` + - :mod:`csv` + - :mod:`ctypes` + - :mod:`!ctypes.macholib` + - :mod:`decimal` (use :data:`decimal.SPEC_VERSION` instead) + - :mod:`http.server` + - :mod:`imaplib` + - :mod:`ipaddress` + - :mod:`json` + - :mod:`logging` (``__date__`` also deprecated) + - :mod:`optparse` + - :mod:`pickle` + - :mod:`platform` + - :mod:`re` + - :mod:`socketserver` + - :mod:`tabnanny` + - :mod:`tarfile` + - :mod:`tkinter.font` + - :mod:`tkinter.ttk` + - :mod:`wsgiref.simple_server` + - :mod:`xml.etree.ElementTree` + - :mod:`!xml.sax.expatreader` + - :mod:`xml.sax.handler` + - :mod:`zlib` + + (Contributed by Hugo van Kemenade and Stan Ulbrych in :gh:`76007`.) + +* Deprecations defined by :pep:`829`: + + * Warnings are produced for ``import`` lines found in :file:`{name}.pth` + files. + + * :file:`{name}.pth` files are no longer decoded in the locale encoding by + default. They **MUST** be encoded in ``utf-8-sig``. + + (Contributed by Barry Warsaw in :gh:`148641`.) + +* :mod:`ast`: + + * Creating instances of abstract AST nodes (such as :class:`ast.AST` + or :class:`!ast.expr`) is deprecated and will raise an error in Python 3.20. diff --git a/Doc/deprecations/pending-removal-in-3.21.rst b/Doc/deprecations/pending-removal-in-3.21.rst new file mode 100644 index 00000000000000..18b89a20e4a208 --- /dev/null +++ b/Doc/deprecations/pending-removal-in-3.21.rst @@ -0,0 +1,19 @@ +Pending removal in Python 3.21 +------------------------------ + +* :mod:`abc` + + * Soft-deprecated since Python 3.3 :class:`abc.abstractclassmethod`, + :class:`abc.abstractstaticmethod`, and :class:`abc.abstractproperty` + now raise a :exc:`DeprecationWarning`. + These classes will be removed in Python 3.21, instead + use :func:`abc.abstractmethod` with :func:`classmethod`, + :func:`staticmethod`, and :class:`property` respectively. + +* :mod:`ast`: + + * Classes ``slice``, ``Index``, ``ExtSlice``, ``Suite``, ``Param``, + ``AugLoad`` and ``AugStore``, will be removed in Python 3.21. These types + are not generated by the parser or accepted by the code generator. + * The ``dims`` property of ``ast.Tuple`` will be removed in Python 3.21. Use + the ``ast.Tuple.elts`` property instead. diff --git a/Doc/deprecations/pending-removal-in-future.rst b/Doc/deprecations/pending-removal-in-future.rst index edb672ed8ad9a2..74f98d33a4b61f 100644 --- a/Doc/deprecations/pending-removal-in-future.rst +++ b/Doc/deprecations/pending-removal-in-future.rst @@ -15,7 +15,6 @@ although there is currently no date scheduled for their removal. * :mod:`builtins`: - * ``bool(NotImplemented)``. * Generators: ``throw(type, exc, tb)`` and ``athrow(type, exc, tb)`` signature is deprecated: use ``throw(exc)`` and ``athrow(exc)`` instead, the single argument signature. @@ -36,7 +35,6 @@ although there is currently no date scheduled for their removal. * Support for ``__complex__()`` method returning a strict subclass of :class:`complex`: these methods will be required to return an instance of :class:`complex`. - * Delegation of ``int()`` to ``__trunc__()`` method. * Passing a complex number as the *real* or *imag* argument in the :func:`complex` constructor is now deprecated; it should only be passed as a single positional argument. @@ -49,7 +47,7 @@ although there is currently no date scheduled for their removal. * :mod:`codecs`: use :func:`open` instead of :func:`codecs.open`. (:gh:`133038`) -* :attr:`codeobject.co_lnotab`: use the :meth:`codeobject.co_lines` method +* :attr:`!codeobject.co_lnotab`: use the :meth:`codeobject.co_lines` method instead. * :mod:`datetime`: @@ -77,7 +75,15 @@ although there is currently no date scheduled for their removal. * :mod:`mailbox`: Use of StringIO input and text mode is deprecated, use BytesIO and binary mode instead. -* :mod:`os`: Calling :func:`os.register_at_fork` in multi-threaded process. +* :mod:`os`: Calling :func:`os.register_at_fork` in a multi-threaded process. + +* :mod:`os.path`: :func:`os.path.commonprefix` is deprecated, use + :func:`os.path.commonpath` for path prefixes. The :func:`os.path.commonprefix` + function is being deprecated due to having a misleading name and module. + The function is not safe to use for path prefixes despite being included in a + module about path manipulation, meaning it is easy to accidentally + introduce path traversal vulnerabilities into Python programs by using this + function. * :class:`!pydoc.ErrorDuringImport`: A tuple value for *exc_info* parameter is deprecated, use an exception instance. diff --git a/Doc/deprecations/soft-deprecations.rst b/Doc/deprecations/soft-deprecations.rst new file mode 100644 index 00000000000000..a270052788ef2a --- /dev/null +++ b/Doc/deprecations/soft-deprecations.rst @@ -0,0 +1,21 @@ +Soft deprecations +----------------- + +There are no plans to remove :term:`soft deprecated` APIs. + +* :func:`re.match` and :meth:`re.Pattern.match` are now + :term:`soft deprecated` in favor of the new :func:`re.prefixmatch` and + :meth:`re.Pattern.prefixmatch` APIs, which have been added as alternate, + more explicit names. These are intended to be used to alleviate confusion + around what *match* means by following the Zen of Python's *"Explicit is + better than implicit"* mantra. Most other language regular expression + libraries use an API named *match* to mean what Python has always called + *search*. + + We **do not** plan to remove the older :func:`!match` name, as it has been + used in code for over 30 years. Code supporting older versions of Python + should continue to use :func:`!match`, while new code should prefer + :func:`!prefixmatch`. See :ref:`prefixmatch-vs-match`. + + (Contributed by Gregory P. Smith in :gh:`86519` and + Hugo van Kemenade in :gh:`148100`.) diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index a89a69043c0f9f..d33cbd2813d637 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -3,129 +3,20 @@ .. _extending-intro: -****************************** -Extending Python with C or C++ -****************************** +******************************** +Using the C API: Assorted topics +******************************** -It is quite easy to add new built-in modules to Python, if you know how to -program in C. Such :dfn:`extension modules` can do two things that can't be -done directly in Python: they can implement new built-in object types, and they -can call C library functions and system calls. - -To support extensions, the Python API (Application Programmers Interface) -defines a set of functions, macros and variables that provide access to most -aspects of the Python run-time system. The Python API is incorporated in a C -source file by including the header ``"Python.h"``. - -The compilation of an extension module depends on its intended use as well as on -your system setup; details are given in later chapters. - -.. note:: - - The C extension interface is specific to CPython, and extension modules do - not work on other Python implementations. In many cases, it is possible to - avoid writing C extensions and preserve portability to other implementations. - For example, if your use case is calling C library functions or system calls, - you should consider using the :mod:`ctypes` module or the `cffi - `_ library rather than writing - custom C code. - These modules let you write Python code to interface with C code and are more - portable between implementations of Python than writing and compiling a C - extension module. - - -.. _extending-simpleexample: - -A Simple Example -================ - -Let's create an extension module called ``spam`` (the favorite food of Monty -Python fans...) and let's say we want to create a Python interface to the C -library function :c:func:`system` [#]_. This function takes a null-terminated -character string as argument and returns an integer. We want this function to -be callable from Python as follows: - -.. code-block:: pycon - - >>> import spam - >>> status = spam.system("ls -l") - -Begin by creating a file :file:`spammodule.c`. (Historically, if a module is -called ``spam``, the C file containing its implementation is called -:file:`spammodule.c`; if the module name is very long, like ``spammify``, the -module name can be just :file:`spammify.c`.) - -The first two lines of our file can be:: - - #define PY_SSIZE_T_CLEAN - #include - -which pulls in the Python API (you can add a comment describing the purpose of -the module and a copyright notice if you like). - -.. note:: - - Since Python may define some pre-processor definitions which affect the standard - headers on some systems, you *must* include :file:`Python.h` before any standard - headers are included. - - ``#define PY_SSIZE_T_CLEAN`` was used to indicate that ``Py_ssize_t`` should be - used in some APIs instead of ``int``. - It is not necessary since Python 3.13, but we keep it here for backward compatibility. - See :ref:`arg-parsing-string-and-buffers` for a description of this macro. - -All user-visible symbols defined by :file:`Python.h` have a prefix of ``Py`` or -``PY``, except those defined in standard header files. For convenience, and -since they are used extensively by the Python interpreter, ``"Python.h"`` -includes a few standard header files: ````, ````, -````, and ````. If the latter header file does not exist on -your system, it declares the functions :c:func:`malloc`, :c:func:`free` and -:c:func:`realloc` directly. - -The next thing we add to our module file is the C function that will be called -when the Python expression ``spam.system(string)`` is evaluated (we'll see -shortly how it ends up being called):: - - static PyObject * - spam_system(PyObject *self, PyObject *args) - { - const char *command; - int sts; - - if (!PyArg_ParseTuple(args, "s", &command)) - return NULL; - sts = system(command); - return PyLong_FromLong(sts); - } - -There is a straightforward translation from the argument list in Python (for -example, the single expression ``"ls -l"``) to the arguments passed to the C -function. The C function always has two arguments, conventionally named *self* -and *args*. - -The *self* argument points to the module object for module-level functions; -for a method it would point to the object instance. - -The *args* argument will be a pointer to a Python tuple object containing the -arguments. Each item of the tuple corresponds to an argument in the call's -argument list. The arguments are Python objects --- in order to do anything -with them in our C function we have to convert them to C values. The function -:c:func:`PyArg_ParseTuple` in the Python API checks the argument types and -converts them to C values. It uses a template string to determine the required -types of the arguments as well as the types of the C variables into which to -store the converted values. More about this later. - -:c:func:`PyArg_ParseTuple` returns true (nonzero) if all arguments have the right -type and its components have been stored in the variables whose addresses are -passed. It returns false (zero) if an invalid argument list was passed. In the -latter case it also raises an appropriate exception so the calling function can -return ``NULL`` immediately (as we saw in the example). +The :ref:`tutorial ` walked you through +creating a C API extension module, but left many areas unexplained. +This document looks at several concepts that you'll need to learn +in order to write more complex extensions. .. _extending-errors: -Intermezzo: Errors and Exceptions -================================= +Errors and Exceptions +===================== An important convention throughout the Python interpreter is the following: when a function fails, it should set an exception condition and return an error value @@ -296,194 +187,14 @@ call to :c:func:`PyErr_SetString` as shown below:: } -.. _backtoexample: - -Back to the Example -=================== - -Going back to our example function, you should now be able to understand this -statement:: - - if (!PyArg_ParseTuple(args, "s", &command)) - return NULL; - -It returns ``NULL`` (the error indicator for functions returning object pointers) -if an error is detected in the argument list, relying on the exception set by -:c:func:`PyArg_ParseTuple`. Otherwise the string value of the argument has been -copied to the local variable :c:data:`!command`. This is a pointer assignment and -you are not supposed to modify the string to which it points (so in Standard C, -the variable :c:data:`!command` should properly be declared as ``const char -*command``). - -The next statement is a call to the Unix function :c:func:`system`, passing it -the string we just got from :c:func:`PyArg_ParseTuple`:: - - sts = system(command); - -Our :func:`!spam.system` function must return the value of :c:data:`!sts` as a -Python object. This is done using the function :c:func:`PyLong_FromLong`. :: - - return PyLong_FromLong(sts); - -In this case, it will return an integer object. (Yes, even integers are objects -on the heap in Python!) - -If you have a C function that returns no useful argument (a function returning -:c:expr:`void`), the corresponding Python function must return ``None``. You -need this idiom to do so (which is implemented by the :c:macro:`Py_RETURN_NONE` -macro):: - - Py_INCREF(Py_None); - return Py_None; - -:c:data:`Py_None` is the C name for the special Python object ``None``. It is a -genuine Python object rather than a ``NULL`` pointer, which means "error" in most -contexts, as we have seen. - - -.. _methodtable: - -The Module's Method Table and Initialization Function -===================================================== - -I promised to show how :c:func:`!spam_system` is called from Python programs. -First, we need to list its name and address in a "method table":: - - static PyMethodDef spam_methods[] = { - ... - {"system", spam_system, METH_VARARGS, - "Execute a shell command."}, - ... - {NULL, NULL, 0, NULL} /* Sentinel */ - }; - -Note the third entry (``METH_VARARGS``). This is a flag telling the interpreter -the calling convention to be used for the C function. It should normally always -be ``METH_VARARGS`` or ``METH_VARARGS | METH_KEYWORDS``; a value of ``0`` means -that an obsolete variant of :c:func:`PyArg_ParseTuple` is used. - -When using only ``METH_VARARGS``, the function should expect the Python-level -parameters to be passed in as a tuple acceptable for parsing via -:c:func:`PyArg_ParseTuple`; more information on this function is provided below. - -The :c:macro:`METH_KEYWORDS` bit may be set in the third field if keyword -arguments should be passed to the function. In this case, the C function should -accept a third ``PyObject *`` parameter which will be a dictionary of keywords. -Use :c:func:`PyArg_ParseTupleAndKeywords` to parse the arguments to such a -function. - -The method table must be referenced in the module definition structure:: - - static struct PyModuleDef spam_module = { - ... - .m_methods = spam_methods, - ... - }; - -This structure, in turn, must be passed to the interpreter in the module's -initialization function. The initialization function must be named -:c:func:`!PyInit_name`, where *name* is the name of the module, and should be the -only non-\ ``static`` item defined in the module file:: - - PyMODINIT_FUNC - PyInit_spam(void) - { - return PyModuleDef_Init(&spam_module); - } - -Note that :c:macro:`PyMODINIT_FUNC` declares the function as ``PyObject *`` return type, -declares any special linkage declarations required by the platform, and for C++ -declares the function as ``extern "C"``. - -:c:func:`!PyInit_spam` is called when each interpreter imports its module -:mod:`!spam` for the first time. (See below for comments about embedding Python.) -A pointer to the module definition must be returned via :c:func:`PyModuleDef_Init`, -so that the import machinery can create the module and store it in ``sys.modules``. - -When embedding Python, the :c:func:`!PyInit_spam` function is not called -automatically unless there's an entry in the :c:data:`PyImport_Inittab` table. -To add the module to the initialization table, use :c:func:`PyImport_AppendInittab`, -optionally followed by an import of the module:: - - #define PY_SSIZE_T_CLEAN - #include - - int - main(int argc, char *argv[]) - { - PyStatus status; - PyConfig config; - PyConfig_InitPythonConfig(&config); - - /* Add a built-in module, before Py_Initialize */ - if (PyImport_AppendInittab("spam", PyInit_spam) == -1) { - fprintf(stderr, "Error: could not extend in-built modules table\n"); - exit(1); - } - - /* Pass argv[0] to the Python interpreter */ - status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]); - if (PyStatus_Exception(status)) { - goto exception; - } - - /* Initialize the Python interpreter. Required. - If this step fails, it will be a fatal error. */ - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - goto exception; - } - PyConfig_Clear(&config); - - /* Optionally import the module; alternatively, - import can be deferred until the embedded script - imports it. */ - PyObject *pmodule = PyImport_ImportModule("spam"); - if (!pmodule) { - PyErr_Print(); - fprintf(stderr, "Error: could not import module 'spam'\n"); - } - - // ... use Python C API here ... - - return 0; - - exception: - PyConfig_Clear(&config); - Py_ExitStatusException(status); - } - -.. note:: - - If you declare a global variable or a local static one, the module may - experience unintended side-effects on re-initialisation, for example when - removing entries from ``sys.modules`` or importing compiled modules into - multiple interpreters within a process - (or following a :c:func:`fork` without an intervening :c:func:`exec`). - If module state is not yet fully :ref:`isolated `, - authors should consider marking the module as having no support for subinterpreters - (via :c:macro:`Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED`). - -A more substantial example module is included in the Python source distribution -as :file:`Modules/xxlimited.c`. This file may be used as a template or simply -read as an example. - - .. _compilation: -Compilation and Linkage -======================= - -There are two more things to do before you can use your new extension: compiling -and linking it with the Python system. If you use dynamic loading, the details -may depend on the style of dynamic loading your system uses; see the chapters -about building extension modules (chapter :ref:`building`) and additional -information that pertains only to building on Windows (chapter -:ref:`building-on-windows`) for more information about this. +Embedding an extension +====================== -If you can't use dynamic loading, or if you want to make your module a permanent +If you want to make your module a permanent part of the Python interpreter, you will have to change the configuration setup -and rebuild the interpreter. Luckily, this is very simple on Unix: just place +and rebuild the interpreter. On Unix, place your file (:file:`spammodule.c` for example) in the :file:`Modules/` directory of an unpacked source distribution, add a line to the file :file:`Modules/Setup.local` describing your file: @@ -511,7 +222,7 @@ on the line in the configuration file as well, for instance: Calling Python Functions from C =============================== -So far we have concentrated on making C functions callable from Python. The +The tutorial concentrated on making C functions callable from Python. The reverse is also useful: calling Python functions from C. This is especially the case for libraries that support so-called "callback" functions. If a C interface makes use of callbacks, the equivalent Python often needs to provide a @@ -556,7 +267,7 @@ be part of a module definition:: } This function must be registered with the interpreter using the -:c:macro:`METH_VARARGS` flag; this is described in section :ref:`methodtable`. The +:c:macro:`METH_VARARGS` flag in :c:type:`PyMethodDef.ml_flags`. The :c:func:`PyArg_ParseTuple` function and its arguments are documented in section :ref:`parsetuple`. @@ -651,14 +362,21 @@ the above example, we use :c:func:`Py_BuildValue` to construct the dictionary. : Py_DECREF(result); +.. index:: single: PyArg_ParseTuple (C function) + .. _parsetuple: Extracting Parameters in Extension Functions ============================================ -.. index:: single: PyArg_ParseTuple (C function) +The :ref:`tutorial ` uses a ":c:data:`METH_O`" +function, which is limited to a single Python argument. +If you want more, you can use :c:data:`METH_VARARGS` instead. +With this flag, the C function will receive a :py:class:`tuple` of arguments +instead of a single object. -The :c:func:`PyArg_ParseTuple` function is declared as follows:: +For unpacking the tuple, CPython provides the :c:func:`PyArg_ParseTuple` +function, declared as follows:: int PyArg_ParseTuple(PyObject *arg, const char *format, ...); @@ -668,6 +386,19 @@ whose syntax is explained in :ref:`arg-parsing` in the Python/C API Reference Manual. The remaining arguments must be addresses of variables whose type is determined by the format string. +For example, to receive a single Python :py:class:`str` object and turn it +into a C buffer, you would use ``"s"`` as the format string:: + + const char *command; + if (!PyArg_ParseTuple(args, "s", &command)) { + return NULL; + } + +If an error is detected in the argument list, :c:func:`!PyArg_ParseTuple` +returns ``NULL`` (the error indicator for functions returning object pointers); +your function may return ``NULL``, relying on the exception set by +:c:func:`PyArg_ParseTuple`. + Note that while :c:func:`PyArg_ParseTuple` checks that the Python arguments have the required types, it cannot check the validity of the addresses of C variables passed to the call: if you make mistakes there, your code will probably crash or @@ -678,7 +409,6 @@ Note that any Python object references which are provided to the caller are Some example calls:: - #define PY_SSIZE_T_CLEAN #include :: @@ -748,6 +478,17 @@ Some example calls:: Keyword Parameters for Extension Functions ========================================== +If you also want your function to accept +:term:`keyword arguments `, use the :c:data:`METH_KEYWORDS` +flag in combination with :c:data:`METH_VARARGS`. +(:c:data:`!METH_KEYWORDS` can also be used with other flags; see its +documentation for the allowed combinations.) + +In this case, the C function should accept a third ``PyObject *`` parameter +which will be a dictionary of keywords. +Use :c:func:`PyArg_ParseTupleAndKeywords` to parse the arguments to such a +function. + .. index:: single: PyArg_ParseTupleAndKeywords (C function) The :c:func:`PyArg_ParseTupleAndKeywords` function is declared as follows:: @@ -808,19 +549,6 @@ Philbrick (philbrick@hks.com):: {NULL, NULL, 0, NULL} /* sentinel */ }; - static struct PyModuleDef keywdarg_module = { - .m_base = PyModuleDef_HEAD_INIT, - .m_name = "keywdarg", - .m_size = 0, - .m_methods = keywdarg_methods, - }; - - PyMODINIT_FUNC - PyInit_keywdarg(void) - { - return PyModuleDef_Init(&keywdarg_module); - } - .. _buildvalue: @@ -961,11 +689,11 @@ needed. Ownership of a reference can be transferred. There are three ways to dispose of an owned reference: pass it on, store it, or call :c:func:`Py_DECREF`. Forgetting to dispose of an owned reference creates a memory leak. -It is also possible to :dfn:`borrow` [#]_ a reference to an object. The +It is also possible to :dfn:`borrow` [#borrow]_ a reference to an object. The borrower of a reference should not call :c:func:`Py_DECREF`. The borrower must not hold on to the object longer than the owner from which it was borrowed. Using a borrowed reference after the owner has disposed of it risks using freed -memory and should be avoided completely [#]_. +memory and should be avoided completely [#dont-check-refcount]_. The advantage of borrowing over owning a reference is that you don't need to take care of disposing of the reference on all possible paths through the code @@ -1059,7 +787,14 @@ references to all its items, so when item 1 is replaced, it has to dispose of the original item 1. Now let's suppose the original item 1 was an instance of a user-defined class, and let's further suppose that the class defined a :meth:`!__del__` method. If this class instance has a reference count of 1, -disposing of it will call its :meth:`!__del__` method. +disposing of it will call its :meth:`!__del__` method. Internally, +:c:func:`PyList_SetItem` calls :c:func:`Py_DECREF` on the replaced item, +which invokes replaced item's corresponding +:c:member:`~PyTypeObject.tp_dealloc` function. During +deallocation, :c:member:`~PyTypeObject.tp_dealloc` calls +:c:member:`~PyTypeObject.tp_finalize`, which is mapped to the +:meth:`!__del__` method for class instances (see :pep:`442`). This entire +sequence happens synchronously within the :c:func:`PyList_SetItem` call. Since it is written in Python, the :meth:`!__del__` method can execute arbitrary Python code. Could it perhaps do something to invalidate the reference to @@ -1137,7 +872,7 @@ checking. The C function calling mechanism guarantees that the argument list passed to C functions (``args`` in the examples) is never ``NULL`` --- in fact it guarantees -that it is always a tuple [#]_. +that it is always a tuple [#old-calling-convention]_. It is a severe error to ever let a ``NULL`` pointer "escape" to the Python user. @@ -1168,9 +903,6 @@ define this symbol). Providing a C API for an Extension Module ========================================= -.. sectionauthor:: Konrad Hinsen - - Many extension modules just provide new functions and types to be used from Python, but sometimes the code in an extension module can be useful for other extension modules. For example, an extension module could implement a type @@ -1194,8 +926,8 @@ the module whose functions one wishes to call might not have been loaded yet! Portability therefore requires not to make any assumptions about symbol visibility. This means that all symbols in extension modules should be declared ``static``, except for the module's initialization function, in order to -avoid name clashes with other extension modules (as discussed in section -:ref:`methodtable`). And it means that symbols that *should* be accessible from +avoid name clashes with other extension modules. And it means that symbols +that *should* be accessible from other extension modules must be exported in a different way. Python provides a special mechanism to pass C-level information (pointers) from @@ -1237,8 +969,9 @@ file corresponding to the module provides a macro that takes care of importing the module and retrieving its C API pointers; client modules only have to call this macro before accessing the C API. -The exporting module is a modification of the :mod:`!spam` module from section -:ref:`extending-simpleexample`. The function :func:`!spam.system` does not call +The exporting module is a modification of the :mod:`!spam` module from the +:ref:`tutorial `. +The function :func:`!spam.system` does not call the C library function :c:func:`system` directly, but a function :c:func:`!PySpam_System`, which would of course do something more complicated in reality (such as adding "spam" to every command). This function @@ -1380,15 +1113,14 @@ code distribution). .. rubric:: Footnotes -.. [#] An interface for this function already exists in the standard module :mod:`os` - --- it was chosen as a simple and straightforward example. - -.. [#] The metaphor of "borrowing" a reference is not completely correct: the owner - still has a copy of the reference. +.. [#borrow] The metaphor of "borrowing" a reference is not completely correct: + the owner still has a copy of the reference. -.. [#] Checking that the reference count is at least 1 **does not work** --- the +.. [#dont-check-refcount] Checking that the reference count is at least 1 + **does not work** --- the reference count itself could be in freed memory and may thus be reused for another object! -.. [#] These guarantees don't hold when you use the "old" style calling convention --- +.. [#old-calling-convention] These guarantees don't hold when you use the + "old" style calling convention --- this is still found in much existing code. diff --git a/Doc/extending/first-extension-module.rst b/Doc/extending/first-extension-module.rst new file mode 100644 index 00000000000000..55a772e2aca24f --- /dev/null +++ b/Doc/extending/first-extension-module.rst @@ -0,0 +1,699 @@ +.. highlight:: c + + +.. _extending-simpleexample: +.. _first-extension-module: + +********************************* +Your first C API extension module +********************************* + +This tutorial will take you through creating a simple +Python extension module written in C or C++. + +We will use the low-level Python C API directly. +For easier ways to create extension modules, see +the :ref:`recommended third party tools `. + +The tutorial assumes basic knowledge about Python: you should be able to +define functions in Python code before starting to write them in C. +See :ref:`tutorial-index` for an introduction to Python itself. + +The tutorial should be approachable for anyone who can write a basic C library. +While we will mention several concepts that a C beginner would not be expected +to know, like ``static`` functions or linkage declarations, understanding these +is not necessary for success. + +We will focus on giving you a "feel" of what Python's C API is like. +It will not teach you important concepts, like error handling +and reference counting, which are covered in later chapters. + +We will assume that you use a Unix-like system (including macOS and +Linux), or Windows. +On other systems, you might need to adjust some details -- for example, +a system command name. + +You need to have a suitable C compiler and Python development headers installed. +On Linux, headers are often in a package like ``python3-dev`` +or ``python3-devel``. + +You need to be able to install Python packages. +This tutorial uses `pip `__ (``pip install``), but you +can substitute any tool that can build and install ``pyproject.toml``-based +projects, like `uv `_ (``uv pip install``). +Preferably, have a :ref:`virtual environment ` activated. + + +.. note:: + + This tutorial uses APIs that were added in CPython 3.15. + To create an extension that's compatible with earlier versions of CPython, + please follow an earlier version of this documentation. + + This tutorial uses C syntax added in C11 and C++20. + If your extension needs to be compatible with earlier standards, + please follow tutorials in documentation for Python 3.14 or below. + + +What we'll do +============= + +Let's create an extension module called ``spam`` [#why-spam]_, +which will include a Python interface to the C +standard library function :c:func:`system`. +This function is defined in ``stdlib.h``. +It takes a C string as argument, runs the argument as a system +command, and returns a result value as an integer. +A manual page for :c:func:`system` might summarize it this way:: + + #include + int system(const char *command); + +Note that like many functions in the C standard library, +this function is already exposed in Python. +In production, use :py:func:`os.system` or :py:func:`subprocess.run` +rather than the module you'll write here. + +We want this function to be callable from Python as follows: + +.. code-block:: pycon + + >>> import spam + >>> status = spam.system("whoami") + User Name + >>> status + 0 + +.. note:: + + The system command ``whoami`` prints out your username. + It's useful in tutorials like this one because it has the same name on + both Unix and Windows. + + +Start with the headers +====================== + +Begin by creating a directory for this tutorial, and switching to it +on the command line. +Then, create a file named :file:`spammodule.c` in your directory. +[#why-spammodule]_ + +In this file, we'll include two headers: :file:`Python.h` to pull in +all declarations of the Python C API, and :file:`stdlib.h` for the +:c:func:`system` function. [#stdlib-h]_ + +Add the following lines to :file:`spammodule.c`: + +.. literalinclude:: ../includes/capi-extension/spammodule-01.c + :start-at: + :end-at: + +Be sure to put :file:`stdlib.h`, and any other standard library includes, +*after* :file:`Python.h`. +On some systems, Python may define some pre-processor definitions +that affect the standard headers. + + +Running your build tool +======================= + +With only the includes in place, your extension won't do anything. +Still, it's a good time to compile it and try to import it. +This will ensure that your build tool works, so that you can make +and test incremental changes as you follow the rest of the text. + +CPython itself does not come with a tool to build extension modules; +it is recommended to use a third-party project for this. +In this tutorial, we'll use `meson-python`_. +(If you want to use another one, see :ref:`first-extension-other-tools`.) + +.. at the time of writing, meson-python has the least overhead for a + simple extension using PyModExport. + Change this if another tool makes things easier. + +``meson-python`` requires defining a "project" using two extra files. + +First, add ``pyproject.toml`` with these contents: + +.. code-block:: toml + + [build-system] + build-backend = 'mesonpy' + requires = ['meson-python'] + + [project] + # Placeholder project information + # (change this before distributing the module) + name = 'sampleproject' + version = '0' + +Then, create ``meson.build`` containing the following: + +.. code-block:: meson + + project('sampleproject', 'c') + + py = import('python').find_installation(pure: false) + + py.extension_module( + 'spam', # name of the importable Python module + 'spammodule.c', # the C source file + install: true, + ) + +.. note:: + + See the `meson-python documentation `_ for details on + configuration. + +Now, build install the *project in the current directory* (``.``) via ``pip``: + +.. code-block:: sh + + python -m pip -v install . + +The ``-v`` (``--verbose``) option causes ``pip`` to show the output from +the compiler, which is often useful during development. + +.. tip:: + + If you don't have ``pip`` installed, run ``python -m ensurepip``, + preferably in a :ref:`virtual environment `. + (Or, if you prefer another tool that can build and install + ``pyproject.toml``-based projects, use that.) + +.. _meson-python: https://mesonbuild.com/meson-python/ +.. _virtual environment: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#create-and-use-virtual-environments + +Note that you will need to run this command again every time you change your +extension. +Unlike Python, C has an explicit compilation step. + +When your extension is compiled and installed, start Python and try to +import it. +This should fail with the following exception: + +.. code-block:: pycon + + >>> import spam + Traceback (most recent call last): + ... + ImportError: dynamic module does not define module export function (PyModExport_spam or PyInit_spam) + + +Module export hook +================== + +The exception you got when you tried to import the module told you that Python +is looking for a "module export function", also known as a +:ref:`module export hook `. +Let's define one. + +First, add a prototype below the ``#include`` lines: + +.. literalinclude:: ../includes/capi-extension/spammodule-01.c + :start-after: /// Export hook prototype + :end-before: /// + +.. tip:: + The prototype is not strictly necessary, but some modern compilers emit + warnings without it. + It's generally better to add the prototype than to disable the warning. + +The :c:macro:`PyMODEXPORT_FUNC` macro declares the function's +return type, and adds any special linkage declarations needed +to make the function visible and usable when CPython loads it. + +After the prototype, add the function itself. +For now, make it return ``NULL``: + +.. code-block:: c + + PyMODEXPORT_FUNC + PyModExport_spam(void) + { + return NULL; + } + +Compile and load the module again. +You should get a different error this time. + +.. code-block:: pycon + + >>> import spam + Traceback (most recent call last): + ... + SystemError: module export hook for module 'spam' failed without setting an exception + +Simply returning ``NULL`` is *not* correct behavior for an export hook, +and CPython complains about it. +That's good -- it means that CPython found the function! +Let's now make it do something useful. + + +The slot table +============== + +Rather than ``NULL``, the export hook should return the information needed to +create a module. +Let's start with the basics: the name and docstring. + +The information should be defined in an array of +:c:type:`PySlot` entries, which are essentially key-value pairs. +Define this array just before your export hook: + +.. code-block:: c + + PyABIInfo_VAR(abi_info); + + static PySlot spam_slots[] = { + PySlot_STATIC_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_name, "spam"), + PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"), + PySlot_END + }; + +The :c:macro:`PySlot_STATIC_DATA` macro is used when the slot value +(here: ``&abi_info``, ``"spam"``, and the docstring) is a pointer to constant, +statically allocated data. + +The ``PyABIInfo_VAR(abi_info);`` macro and the :c:data:`Py_mod_abi` slot +are a bit of boilerplate that helps prevent extensions compiled for +a different version of Python from crashing the interpreter. + +For both :c:data:`Py_mod_name` and :c:data:`Py_mod_doc`, the values are C +strings -- that is, NUL-terminated, UTF-8 encoded byte arrays. + +Note ``PySlot_END`` sentinel entry at the end. +This marks the end of the array. +If you forget it, you'll trigger undefined behavior. + +The array is defined as ``static`` -- that is, not visible outside this ``.c`` file. +This will be a common theme. +CPython only needs to access the export hook; all global variables +and all other functions should generally be ``static``, so that they don't +clash with other extensions. + +Return this array from your export hook instead of ``NULL``: + +.. code-block:: c + :emphasize-lines: 4 + + PyMODEXPORT_FUNC + PyModExport_spam(void) + { + return spam_slots; + } + +Now, recompile and try it out: + +.. code-block:: pycon + + >>> import spam + >>> print(spam) + + +You have an extension module! +Try ``help(spam)`` to see the docstring. + +The next step will be adding a function. + + +.. _backtoexample: + +Exposing a function +=================== + +To expose the :c:func:`system` C function directly to Python, +we'll need to write a layer of glue code to convert arguments from Python +objects to C values, and the C return value back to Python. + +One of the simplest ways to write glue code is a ":c:data:`METH_O`" function, +which takes two Python objects and returns one. +All Python objects -- regardless of the Python type -- are represented in C +as pointers to the :c:type:`PyObject` structure. + +Add such a function above the slots array:: + + static PyObject * + spam_system(PyObject *self, PyObject *arg) + { + Py_RETURN_NONE; + } + +For now, we ignore the arguments, and use the :c:macro:`Py_RETURN_NONE` +macro, which expands to a ``return`` statement that properly returns +a Python :py:data:`None` object. + +Recompile your extension to make sure you don't have syntax errors. +We haven't yet added ``spam_system`` to the module, so you might get a +warning that ``spam_system`` is unused. + +.. _methodtable: + +Method definitions +------------------ + +To expose the C function to Python, you will need to provide several pieces of +information in a structure called +:c:type:`PyMethodDef` [#why-pymethoddef]_: + +* ``ml_name``: the name of the Python function; +* ``ml_doc``: a docstring; +* ``ml_meth``: the C function to be called; and +* ``ml_flags``: a set of flags describing details like how Python arguments are + passed to the C function. + We'll use :c:data:`METH_O` here -- the flag that matches our + ``spam_system`` function's signature. + +Because modules typically create several functions, these definitions +need to be collected in an array, with a zero-filled sentinel at the end. +Add this array just below the ``spam_system`` function: + +.. literalinclude:: ../includes/capi-extension/spammodule-01.c + :start-after: /// Module method table + :end-before: /// + +As with module slots, a zero-filled sentinel marks the end of the array. + +Next, we'll add the method to the module. +Add a :c:data:`Py_mod_methods` slot to your :c:type:`PyMethodDef` array: + +.. literalinclude:: ../includes/capi-extension/spammodule-01.c + :start-after: /// Module slot table + :end-before: /// + :emphasize-lines: 5 + +Recompile your extension again, and test it. +Be sure to restart the Python interpreter, so that ``import spam`` picks +up the new version of the module. + +You should now be able to call the function: + +.. code-block:: pycon + + >>> import spam + >>> print(spam.system) + + >>> print(spam.system('whoami')) + None + +Note that our ``spam.system`` does not yet run the ``whoami`` command; +it only returns ``None``. + +Check that the function accepts exactly one argument, as specified by +the :c:data:`METH_O` flag: + +.. code-block:: pycon + + >>> print(spam.system('too', 'many', 'arguments')) + Traceback (most recent call last): + ... + TypeError: spam.system() takes exactly one argument (3 given) + + +Returning an integer +==================== + +Now, let's take a look at the return value. +Instead of ``None``, we'll want ``spam.system`` to return a number -- that is, +a Python :py:type:`int` object. +Eventually this will be the exit code of a system command, +but let's start with a fixed value, say, ``3``. + +The Python C API provides a function to create a Python :py:type:`int` object +from a C ``int`` value: :c:func:`PyLong_FromLong`. [#why-pylongfromlong]_ + +To call it, replace the ``Py_RETURN_NONE`` with the following 3 lines: + +.. this could be a one-liner, but we want to show the data types here + +.. code-block:: c + :emphasize-lines: 4-6 + + static PyObject * + spam_system(PyObject *self, PyObject *arg) + { + int status = 3; + PyObject *result = PyLong_FromLong(status); + return result; + } + + +Recompile, restart the Python interpreter again, +and check that the function now returns 3: + +.. code-block:: pycon + + >>> import spam + >>> spam.system('whoami') + 3 + + +Accepting a string +================== + +Finally, let's handle the function argument. + +Our C function, :c:func:`!spam_system`, takes two arguments. +The first one, ``PyObject *self``, will be set to the ``spam`` module +object. +This isn't useful in our case, so we'll ignore it. + +The other one, ``PyObject *arg``, will be set to the object that the user +passed from Python. +We expect that it should be a Python string. +In order to use the information in it, we will need +to convert it to a C value -- in this case, a C string (``const char *``). + +There's a slight type mismatch here: Python's :py:class:`str` objects store +Unicode text, but C strings are arrays of bytes. +So, we'll need to *encode* the data, and we'll use the UTF-8 encoding for it. +(UTF-8 might not always be correct for system commands, but it's what +:py:meth:`str.encode` uses by default, +and the C API has special support for it.) + +The function to encode a Python string into a UTF-8 buffer is named +:c:func:`PyUnicode_AsUTF8AndSize` [#why-pyunicodeasutf8]_. +Call it like this: + +.. code-block:: c + :emphasize-lines: 4 + + static PyObject * + spam_system(PyObject *self, PyObject *arg) + { + const char *command = PyUnicode_AsUTF8AndSize(arg, NULL); + int status = 3; + PyObject *result = PyLong_FromLong(status); + return result; + } + +If :c:func:`PyUnicode_AsUTF8AndSize` is successful, *command* will point to the +resulting C string -- a zero-terminated array of bytes [#embedded-nul]_. +This buffer is managed by the *arg* object, which means we don't need to free +it, but we must follow some rules: + +* We should only use the buffer inside the ``spam_system`` function. + After ``spam_system`` returns, *arg* and the buffer it manages might be + garbage-collected. +* We must not modify it. This is why we use ``const``. + +If :c:func:`PyUnicode_AsUTF8AndSize` was *not* successful, it returns a ``NULL`` +pointer. +When calling *any* Python C API, we always need to handle such error cases. +The way to do this in general is left for later chapters of this documentation. +For now, be assured that we are already handling errors from +:c:func:`PyLong_FromLong` correctly. + +For the :c:func:`PyUnicode_AsUTF8AndSize` call, the correct way to handle +errors is returning ``NULL`` from ``spam_system``. +Add an ``if`` block for this: + + +.. code-block:: c + :emphasize-lines: 5-7 + + static PyObject * + spam_system(PyObject *self, PyObject *arg) + { + const char *command = PyUnicode_AsUTF8AndSize(arg); + if (command == NULL) { + return NULL; + } + int status = 3; + PyObject *result = PyLong_FromLong(status); + return result; + } + +To test that error handling works, compile again, restart Python so that +``import spam`` picks up the new version of your module, and try passing +a non-string value to your function: + +.. code-block:: pycon + + >>> import spam + >>> spam.system(3) + Traceback (most recent call last): + ... + TypeError: bad argument type for built-in operation + +Now, all that is left is calling the C library function :c:func:`system` with +the ``char *`` buffer, and using its result instead of the ``3``: + +.. code-block:: c + :emphasize-lines: 8 + + static PyObject * + spam_system(PyObject *self, PyObject *arg) + { + const char *command = PyUnicode_AsUTF8AndSize(arg); + if (command == NULL) { + return NULL; + } + int status = system(command); + PyObject *result = PyLong_FromLong(status); + return result; + } + +Compile your module, restart Python, and test. +This time, you should see your username -- the output of the ``whoami`` +system command: + +.. code-block:: pycon + + >>> import spam + >>> result = spam.system('whoami') + User Name + >>> result + 0 + +You can also test with other commands, like ``ls``, ``dir``, or one +that doesn't exist: + +.. code-block:: pycon + + >>> import spam + >>> result = spam.system('nonexistent-command') + sh: line 1: nonexistent-command: command not found + >>> result + 32512 + + +The result +========== + + +Congratulations! +You have written a complete Python C API extension module, +and completed this tutorial! + +Here is the entire source file, for your convenience: + +.. _extending-spammodule-source: + +.. literalinclude:: ../includes/capi-extension/spammodule-01.c + :start-at: /// + + +.. _first-extension-other-tools: + +Appendix: Other build tools +=========================== + +You should be able to follow this tutorial -- except the +*Running your build tool* section itself -- with a build tool other +than ``meson-python``. + +The Python Packaging User Guide has a `list of recommended tools `_; +be sure to choose one for the C language. + + +Workaround for missing PyInit function +-------------------------------------- + +If your build tool output complains about missing ``PyInit_spam``, +add the following function to your module for now: + +.. code-block:: c + + // A workaround + void *PyInit_spam(void) { return NULL; } + +This is a shim for an old-style :ref:`initialization function `, +which was required in extension modules for CPython 3.14 and below. +Current CPython does not need it, but some build tools may still assume that +all extension modules need to define it. + +If you use this workaround, you will get the exception +``SystemError: initialization of spam failed without raising an exception`` +instead of +``ImportError: dynamic module does not define module export function``. + + +Compiling directly +------------------ + +Using a third-party build tool is heavily recommended, +as it will take care of various details of your platform and Python +installation, of naming the resulting extension, and, later, of distributing +your work. + +If you are building an extension for as *specific* system, or for yourself +only, you might instead want to run your compiler directly. +The way to do this is system-specific; be prepared for issues you will need +to solve yourself. + +Linux +^^^^^ + +On Linux, the Python development package may include a ``python3-config`` +command that prints out the required compiler flags. +If you use it, check that it corresponds to the CPython interpreter you'll use +to load the module. +Then, start with the following command: + +.. code-block:: sh + + gcc --shared $(python3-config --cflags --ldflags) spammodule.c -o spam.so + +This should generate a ``spam.so`` file that you need to put in a directory +on :py:attr:`sys.path`. + + +.. rubric:: Footnotes + +.. [#why-spam] ``spam`` is the favorite food of Monty Python fans... +.. [#why-spammodule] The source file name is entirely up to you, + though some tools can be picky about the ``.c`` extension. + This tutorial uses the traditional ``*module.c`` suffix. + Some people would just use :file:`spam.c` to implement a module + named ``spam``, + projects where Python isn't the primary language might use ``py_spam.c``, + and so on. +.. [#stdlib-h] Including :file:`stdlib.h` is technically not necessary, + since :file:`Python.h` includes it and + :ref:`several other standard headers ` for its own use + or for backwards compatibility. + However, it is good practice to explicitly include what you need. +.. [#why-pymethoddef] The :c:type:`!PyMethodDef` structure is also used + to create methods of classes, so there's no separate + ":c:type:`!PyFunctionDef`". +.. [#why-pylongfromlong] The name :c:func:`PyLong_FromLong` + might not seem obvious. + ``PyLong`` refers to a the Python :py:class:`int`, which was originally + called ``long``; the ``FromLong`` refers to the C ``long`` (or ``long int``) + type. +.. [#why-pyunicodeasutf8] Here, ``PyUnicode`` refers to the original name of + the Python :py:class:`str` class: ``unicode``. + + The ``AndSize`` part of the name refers to the fact that this function can + also retrieve the size of the buffer, using an output argument. + We don't need this, so we set the second argument to NULL. +.. [#embedded-nul] We're ignoring the fact that Python strings can also + contain NUL bytes, which terminate a C string. + In other words, our function will treat ``spam.system("foo\0bar")`` as + ``spam.system("foo")``. + This possibility can lead to security issues, so the real ``os.system`` + function size checks for this case and raises an error. diff --git a/Doc/extending/index.rst b/Doc/extending/index.rst index 4cc2c96d8d5b47..c0c494c3059d99 100644 --- a/Doc/extending/index.rst +++ b/Doc/extending/index.rst @@ -5,15 +5,17 @@ ################################################## This document describes how to write modules in C or C++ to extend the Python -interpreter with new modules. Those modules can not only define new functions -but also new object types and their methods. The document also describes how +interpreter with new modules. Those modules can do what Python code does -- +define functions, object types and methods -- but also interact with native +libraries or achieve better performance by avoiding the overhead of an +interpreter. The document also describes how to embed the Python interpreter in another application, for use as an extension language. Finally, it shows how to compile and link extension modules so that they can be loaded dynamically (at run time) into the interpreter, if the underlying operating system supports this feature. -This document assumes basic knowledge about Python. For an informal -introduction to the language, see :ref:`tutorial-index`. :ref:`reference-index` +This document assumes basic knowledge about C and Python. For an informal +introduction to Python, see :ref:`tutorial-index`. :ref:`reference-index` gives a more formal definition of the language. :ref:`library-index` documents the existing object types, functions and modules (both built-in and written in Python) that give the language its wide application range. @@ -21,37 +23,75 @@ Python) that give the language its wide application range. For a detailed description of the whole Python/C API, see the separate :ref:`c-api-index`. +To support extensions, Python's C API (Application Programmers Interface) +defines a set of functions, macros and variables that provide access to most +aspects of the Python run-time system. The Python API is incorporated in a C +source file by including the header ``"Python.h"``. + +.. note:: + + The C extension interface is specific to CPython, and extension modules do + not work on other Python implementations. In many cases, it is possible to + avoid writing C extensions and preserve portability to other implementations. + For example, if your use case is calling C library functions or system calls, + you should consider using the :mod:`ctypes` module or the `cffi + `_ library rather than writing + custom C code. + These modules let you write Python code to interface with C code and are more + portable between implementations of Python than writing and compiling a C + extension module. + + +.. toctree:: + :hidden: + + first-extension-module.rst + extending.rst + newtypes_tutorial.rst + newtypes.rst + building.rst + windows.rst + embedding.rst + Recommended third party tools ============================= -This guide only covers the basic tools for creating extensions provided +This document only covers the basic tools for creating extensions provided as part of this version of CPython. Some :ref:`third party tools ` offer both simpler and more sophisticated approaches to creating C and C++ extensions for Python. +While this document is aimed at extension authors, it should also be helpful to +the authors of such tools. +For example, the tutorial module can serve as a simple test case for a build +tool or sample expected output of a code generator. + + +C API Tutorial +============== + +This tutorial describes how to write a simple module in C or C++, +using the Python C API -- that is, using the basic tools provided +as part of this version of CPython. -Creating extensions without third party tools -============================================= + +#. :ref:`first-extension-module` + + +Guides for intermediate topics +============================== This section of the guide covers creating C and C++ extensions without assistance from third party tools. It is intended primarily for creators of those tools, rather than being a recommended way to create your own C extensions. -.. seealso:: - - :pep:`489` -- Multi-phase extension module initialization - -.. toctree:: - :maxdepth: 2 - :numbered: - - extending.rst - newtypes_tutorial.rst - newtypes.rst - building.rst - windows.rst +* :ref:`extending-intro` +* :ref:`defining-new-types` +* :ref:`new-types-topics` +* :ref:`building` +* :ref:`building-on-windows` Embedding the CPython runtime in a larger application ===================================================== @@ -61,8 +101,4 @@ interpreter as the main application, it is desirable to instead embed the CPython runtime inside a larger application. This section covers some of the details involved in doing that successfully. -.. toctree:: - :maxdepth: 2 - :numbered: - - embedding.rst +* :ref:`embedding` diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst index e3612f3a1875ca..26085b5cebd3ad 100644 --- a/Doc/extending/newtypes.rst +++ b/Doc/extending/newtypes.rst @@ -560,6 +560,8 @@ For an object to be weakly referenceable, the extension type must set the field. The legacy :c:member:`~PyTypeObject.tp_weaklistoffset` field should be left as zero. +If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set. + Concretely, here is how the statically declared type object would look:: static PyTypeObject TrivialType = { diff --git a/Doc/extending/newtypes_tutorial.rst b/Doc/extending/newtypes_tutorial.rst index 3bbee33bd50698..9f3cd1d6f4cf33 100644 --- a/Doc/extending/newtypes_tutorial.rst +++ b/Doc/extending/newtypes_tutorial.rst @@ -6,11 +6,6 @@ Defining Extension Types: Tutorial ********************************** -.. sectionauthor:: Michael Hudson -.. sectionauthor:: Dave Kuhlman -.. sectionauthor:: Jim Fulton - - Python allows the writer of a C extension module to define new types that can be manipulated from Python code, much like the built-in :class:`str` and :class:`list` types. The code for all extension types follows a diff --git a/Doc/extending/windows.rst b/Doc/extending/windows.rst index a97c6182553c30..cd81b443603d17 100644 --- a/Doc/extending/windows.rst +++ b/Doc/extending/windows.rst @@ -47,9 +47,6 @@ things manually, it may be instructive to study the project file for the Differences Between Unix and Windows ==================================== -.. sectionauthor:: Chris Phoenix - - Unix and Windows use completely different paradigms for run-time loading of code. Before you try to build a module that can be dynamically loaded, be aware of how your system works. @@ -109,9 +106,6 @@ separate copy. Using DLLs in Practice ====================== -.. sectionauthor:: Chris Phoenix - - Windows Python is built in Microsoft Visual C++; using other compilers may or may not work. The rest of this section is MSVC++ specific. diff --git a/Doc/faq/design.rst b/Doc/faq/design.rst index c758c019ca437b..ac0aa81e56bb07 100644 --- a/Doc/faq/design.rst +++ b/Doc/faq/design.rst @@ -591,9 +591,9 @@ exhaustive test suites that exercise every line of code in a module. An appropriate testing discipline can help build large complex applications in Python as well as having interface specifications would. In fact, it can be better because an interface specification cannot test certain properties of a -program. For example, the :meth:`!list.append` method is expected to add new elements +program. For example, the :meth:`list.append` method is expected to add new elements to the end of some internal list; an interface specification cannot test that -your :meth:`!list.append` implementation will actually do this correctly, but it's +your :meth:`list.append` implementation will actually do this correctly, but it's trivial to check this property in a test suite. Writing test suites is very helpful, and you might want to design your code to diff --git a/Doc/faq/general.rst b/Doc/faq/general.rst index 578777d7f23621..698f2117ff5c64 100644 --- a/Doc/faq/general.rst +++ b/Doc/faq/general.rst @@ -186,7 +186,7 @@ How do I get documentation on Python? ------------------------------------- The standard documentation for the current stable version of Python is available -at https://docs.python.org/3/. PDF, plain text, and downloadable HTML versions are +at https://docs.python.org/3/. EPUB, plain text, and downloadable HTML versions are also available at https://docs.python.org/3/download.html. The documentation is written in reStructuredText and processed by `the Sphinx diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 9f9e4fab685b19..c2f8f72ee1f2c4 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -8,11 +8,11 @@ Programming FAQ .. contents:: -General Questions +General questions ================= -Is there a source code level debugger with breakpoints, single-stepping, etc.? ------------------------------------------------------------------------------- +Is there a source code-level debugger with breakpoints and single-stepping? +--------------------------------------------------------------------------- Yes. @@ -25,8 +25,7 @@ Reference Manual `. You can also write your own debugger by using the code for pdb as an example. The IDLE interactive development environment, which is part of the standard -Python distribution (normally available as -`Tools/scripts/idle3 `_), +Python distribution (normally available as :mod:`idlelib`), includes a graphical debugger. PythonWin is a Python IDE that includes a GUI debugger based on pdb. The @@ -48,7 +47,6 @@ There are a number of commercial Python IDEs that include graphical debuggers. They include: * `Wing IDE `_ -* `Komodo IDE `_ * `PyCharm `_ @@ -57,13 +55,15 @@ Are there tools to help find bugs or perform static analysis? Yes. -`Pylint `_ and -`Pyflakes `_ do basic checking that will +`Ruff `__, +`Pylint `__ and +`Pyflakes `__ do basic checking that will help you catch bugs sooner. -Static type checkers such as `Mypy `_, -`Pyre `_, and -`Pytype `_ can check type hints in Python +Static type checkers such as `mypy `__, +`ty `__, +`Pyrefly `__, and +`pytype `__ can check type hints in Python source code. @@ -79,7 +79,7 @@ set of modules required by a program and bind these modules together with a Python binary to produce a single executable. One is to use the freeze tool, which is included in the Python source tree as -`Tools/freeze `_. +:source:`Tools/freeze`. It converts Python byte code to C arrays; with a C compiler you can embed all your modules into a new program, which is then linked with the standard Python modules. @@ -103,6 +103,7 @@ executables: * `py2app `_ (macOS only) * `py2exe `_ (Windows only) + Are there coding standards or a style guide for Python programs? ---------------------------------------------------------------- @@ -110,7 +111,7 @@ Yes. The coding style required for standard library modules is documented as :pep:`8`. -Core Language +Core language ============= .. _faq-unboundlocalerror: @@ -143,7 +144,7 @@ results in an :exc:`!UnboundLocalError`: >>> foo() Traceback (most recent call last): ... - UnboundLocalError: local variable 'x' referenced before assignment + UnboundLocalError: cannot access local variable 'x' where it is not associated with a value This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable @@ -208,7 +209,7 @@ Why do lambdas defined in a loop with different values all return the same resul ---------------------------------------------------------------------------------- Assume you use a for loop to define a few different lambdas (or even plain -functions), e.g.:: +functions), for example:: >>> squares = [] >>> for x in range(5): @@ -227,7 +228,7 @@ they all return ``16``:: This happens because ``x`` is not local to the lambdas, but is defined in the outer scope, and it is accessed when the lambda is called --- not when it is defined. At the end of the loop, the value of ``x`` is ``4``, so all the -functions now return ``4**2``, i.e. ``16``. You can also verify this by +functions now return ``4**2``, that is ``16``. You can also verify this by changing the value of ``x`` and see how the results of the lambdas change:: >>> x = 8 @@ -298,9 +299,9 @@ using multiple imports per line uses less screen space. It's good practice if you import modules in the following order: -1. standard library modules -- e.g. :mod:`sys`, :mod:`os`, :mod:`argparse`, :mod:`re` +1. standard library modules -- such as :mod:`sys`, :mod:`os`, :mod:`argparse`, :mod:`re` 2. third-party library modules (anything installed in Python's site-packages - directory) -- e.g. :mod:`!dateutil`, :mod:`!requests`, :mod:`!PIL.Image` + directory) -- such as :pypi:`dateutil`, :pypi:`requests`, :pypi:`tzdata` 3. locally developed modules It is sometimes necessary to move imports to a function or class to avoid @@ -454,7 +455,7 @@ There are two factors that produce this result: (the list), and both ``x`` and ``y`` refer to it. 2) Lists are :term:`mutable`, which means that you can change their content. -After the call to :meth:`!append`, the content of the mutable object has +After the call to :meth:`~sequence.append`, the content of the mutable object has changed from ``[]`` to ``[10]``. Since both the variables refer to the same object, using either name accesses the modified value ``[10]``. @@ -494,11 +495,11 @@ new objects). In other words: -* If we have a mutable object (:class:`list`, :class:`dict`, :class:`set`, - etc.), we can use some specific operations to mutate it and all the variables +* If we have a mutable object (such as :class:`list`, :class:`dict`, :class:`set`), + we can use some specific operations to mutate it and all the variables that refer to it will see the change. -* If we have an immutable object (:class:`str`, :class:`int`, :class:`tuple`, - etc.), all the variables that refer to it will always see the same value, +* If we have an immutable object (such as :class:`str`, :class:`int`, :class:`tuple`), + all the variables that refer to it will always see the same value, but operations that transform that value into a new value always return a new object. @@ -511,7 +512,7 @@ How do I write a function with output parameters (call by reference)? Remember that arguments are passed by assignment in Python. Since assignment just creates references to objects, there's no alias between an argument name in -the caller and callee, and so no call-by-reference per se. You can achieve the +the caller and callee, and consequently no call-by-reference. You can achieve the desired effect in a number of ways. 1) By returning a tuple of the results:: @@ -714,8 +715,8 @@ not:: "a" in ("b", "a") -The same is true of the various assignment operators (``=``, ``+=`` etc). They -are not truly operators but syntactic delimiters in assignment statements. +The same is true of the various assignment operators (``=``, ``+=``, and so on). +They are not truly operators but syntactic delimiters in assignment statements. Is there an equivalent of C's "?:" ternary operator? @@ -868,9 +869,9 @@ with either a space or parentheses. How do I convert a string to a number? -------------------------------------- -For integers, use the built-in :func:`int` type constructor, e.g. ``int('144') +For integers, use the built-in :func:`int` type constructor, for example, ``int('144') == 144``. Similarly, :func:`float` converts to a floating-point number, -e.g. ``float('144') == 144.0``. +for example, ``float('144') == 144.0``. By default, these interpret the number as decimal, so that ``int('0144') == 144`` holds true, and ``int('0x144')`` raises :exc:`ValueError`. ``int(string, @@ -887,18 +888,18 @@ unwanted side effects. For example, someone could pass directory. :func:`eval` also has the effect of interpreting numbers as Python expressions, -so that e.g. ``eval('09')`` gives a syntax error because Python does not allow +so that, for example, ``eval('09')`` gives a syntax error because Python does not allow leading '0' in a decimal number (except '0'). How do I convert a number to a string? -------------------------------------- -To convert, e.g., the number ``144`` to the string ``'144'``, use the built-in type +For example, to convert the number ``144`` to the string ``'144'``, use the built-in type constructor :func:`str`. If you want a hexadecimal or octal representation, use the built-in functions :func:`hex` or :func:`oct`. For fancy formatting, see -the :ref:`f-strings` and :ref:`formatstrings` sections, -e.g. ``"{:04d}".format(144)`` yields +the :ref:`f-strings` and :ref:`formatstrings` sections. +For example, ``"{:04d}".format(144)`` yields ``'0144'`` and ``"{:.3f}".format(1.0/3.0)`` yields ``'0.333'``. @@ -908,7 +909,7 @@ How do I modify a string in place? You can't, because strings are immutable. In most situations, you should simply construct a new string from the various parts you want to assemble it from. However, if you need an object with the ability to modify in-place -unicode data, try using an :class:`io.StringIO` object or the :mod:`array` +Unicode data, try using an :class:`io.StringIO` object or the :mod:`array` module:: >>> import io @@ -1066,13 +1067,14 @@ the raw string:: Also see the specification in the :ref:`language reference `. + Performance =========== My program is too slow. How do I speed it up? --------------------------------------------- -That's a tough one, in general. First, here are a list of things to +That's a tough one, in general. First, here is a list of things to remember before diving further: * Performance characteristics vary across Python implementations. This FAQ @@ -1125,6 +1127,7 @@ yourself. The wiki page devoted to `performance tips `_. + .. _efficient_string_concatenation: What is the most efficient way to concatenate many strings together? @@ -1143,7 +1146,7 @@ them into a list and call :meth:`str.join` at the end:: chunks.append(s) result = ''.join(chunks) -(another reasonably efficient idiom is to use :class:`io.StringIO`) +(Another reasonably efficient idiom is to use :class:`io.StringIO`.) To accumulate many :class:`bytes` objects, the recommended idiom is to extend a :class:`bytearray` object using in-place concatenation (the ``+=`` operator):: @@ -1153,7 +1156,7 @@ a :class:`bytearray` object using in-place concatenation (the ``+=`` operator):: result += b -Sequences (Tuples/Lists) +Sequences (tuples/lists) ======================== How do I convert between tuples and lists? @@ -1217,8 +1220,8 @@ list, deleting duplicates as you go:: else: last = mylist[i] -If all elements of the list may be used as set keys (i.e. they are all -:term:`hashable`) this is often faster :: +If all elements of the list may be used as set keys (that is, they are all +:term:`hashable`) this is often faster:: mylist = list(set(mylist)) @@ -1226,13 +1229,13 @@ This converts the list into a set, thereby removing duplicates, and then back into a list. -How do you remove multiple items from a list --------------------------------------------- +How do you remove multiple items from a list? +--------------------------------------------- As with removing duplicates, explicitly iterating in reverse with a delete condition is one possibility. However, it is easier and faster to use slice replacement with an implicit or explicit forward iteration. -Here are three variations.:: +Here are three variations:: mylist[:] = filter(keep_function, mylist) mylist[:] = (x for x in mylist if keep_condition) @@ -1254,7 +1257,7 @@ difference is that a Python list can contain objects of many different types. The ``array`` module also provides methods for creating arrays of fixed types with compact representations, but they are slower to index than lists. Also note that `NumPy `_ -and other third party packages define array-like structures with +and other third-party packages define array-like structures with various characteristics as well. To get Lisp-style linked lists, you can emulate *cons cells* using tuples:: @@ -1324,7 +1327,7 @@ Or, you can use an extension that provides a matrix datatype; `NumPy How do I apply a method or function to a sequence of objects? ------------------------------------------------------------- -To call a method or function and accumulate the return values is a list, +To call a method or function and accumulate the return values in a list, a :term:`list comprehension` is an elegant solution:: result = [obj.method() for obj in mylist] @@ -1340,6 +1343,7 @@ a plain :keyword:`for` loop will suffice:: for obj in mylist: function(obj) + .. _faq-augmented-assignment-tuple-error: Why does a_tuple[i] += ['item'] raise an exception when the addition works? @@ -1397,9 +1401,9 @@ To see why this happens, you need to know that (a) if an object implements an :meth:`~object.__iadd__` magic method, it gets called when the ``+=`` augmented assignment is executed, and its return value is what gets used in the assignment statement; -and (b) for lists, :meth:`!__iadd__` is equivalent to calling :meth:`!extend` on the list -and returning the list. That's why we say that for lists, ``+=`` is a -"shorthand" for :meth:`!list.extend`:: +and (b) for lists, :meth:`!__iadd__` is equivalent to calling +:meth:`~sequence.extend` on the list and returning the list. +That's why we say that for lists, ``+=`` is a "shorthand" for :meth:`list.extend`:: >>> a_list = [] >>> a_list += [1] @@ -1444,7 +1448,7 @@ How can I sort one list by values from another list? ---------------------------------------------------- Merge them into an iterator of tuples, sort the resulting list, and then pick -out the element you want. :: +out the element you want. >>> list1 = ["what", "I'm", "sorting", "by"] >>> list2 = ["something", "else", "to", "sort"] @@ -1504,14 +1508,15 @@ How do I check if an object is an instance of a given class or of a subclass of Use the built-in function :func:`isinstance(obj, cls) `. You can check if an object is an instance of any of a number of classes by providing a tuple instead of a -single class, e.g. ``isinstance(obj, (class1, class2, ...))``, and can also -check whether an object is one of Python's built-in types, e.g. +single class, for example, ``isinstance(obj, (class1, class2, ...))``, and can also +check whether an object is one of Python's built-in types, for example, ``isinstance(obj, str)`` or ``isinstance(obj, (int, float, complex))``. Note that :func:`isinstance` also checks for virtual inheritance from an :term:`abstract base class`. So, the test will return ``True`` for a registered class even if hasn't directly or indirectly inherited from it. To -test for "true inheritance", scan the :term:`MRO` of the class: +test for "true inheritance", scan the :term:`method resolution order` (MRO) of +the class: .. testcode:: @@ -1574,7 +1579,7 @@ call it:: What is delegation? ------------------- -Delegation is an object oriented technique (also called a design pattern). +Delegation is an object-oriented technique (also called a design pattern). Let's say you have an object ``x`` and want to change the behaviour of just one of its methods. You can create a new class that provides a new implementation of the method you're interested in changing and delegates all other methods to @@ -1645,7 +1650,7 @@ How can I organize my code to make it easier to change the base class? You could assign the base class to an alias and derive from the alias. Then all you have to change is the value assigned to the alias. Incidentally, this trick -is also handy if you want to decide dynamically (e.g. depending on availability +is also handy if you want to decide dynamically (such as depending on availability of resources) which base class to use. Example:: class Base: @@ -1710,9 +1715,9 @@ How can I overload constructors (or methods) in Python? This answer actually applies to all methods, but the question usually comes up first in the context of constructors. -In C++ you'd write +In C++ you'd write: -.. code-block:: c +.. code-block:: c++ class C { C() { cout << "No arguments\n"; } @@ -1731,7 +1736,7 @@ default arguments. For example:: This is not entirely equivalent, but close enough in practice. -You could also try a variable-length argument list, e.g. :: +You could also try a variable-length argument list, for example:: def __init__(self, *args): ... @@ -1774,6 +1779,7 @@ to use private variable names at all. The :ref:`private name mangling specifications ` for details and special cases. + My class defines __del__ but it is not called when I delete the object. ----------------------------------------------------------------------- @@ -1783,7 +1789,7 @@ The :keyword:`del` statement does not necessarily call :meth:`~object.__del__` - decrements the object's reference count, and if this reaches zero :meth:`!__del__` is called. -If your data structures contain circular links (e.g. a tree where each child has +If your data structures contain circular links (for example, a tree where each child has a parent reference and each parent has a list of children) the reference counts will never go back to zero. Once in a while Python runs an algorithm to detect such cycles, but the garbage collector might run some time after the last @@ -1852,6 +1858,8 @@ to the object: 13891296 +.. _faq-identity-with-is: + When can I rely on identity tests with the *is* operator? --------------------------------------------------------- @@ -1883,9 +1891,9 @@ are preferred. In particular, identity tests should not be used to check constants such as :class:`int` and :class:`str` which aren't guaranteed to be singletons:: - >>> a = 1000 - >>> b = 500 - >>> c = b + 500 + >>> a = 10_000_000 + >>> b = 5_000_000 + >>> c = b + 5_000_000 >>> a is c False @@ -1916,7 +1924,7 @@ correctly using identity tests: .. code-block:: python - _sentinel = object() + _sentinel = sentinel('_sentinel') def pop(self, key, default=_sentinel): if key in self: @@ -1954,9 +1962,9 @@ parent class: .. testcode:: - from datetime import date + import datetime as dt - class FirstOfMonthDate(date): + class FirstOfMonthDate(dt.date): "Always choose the first day of the month" def __new__(cls, year, month, day): return super().__new__(cls, year, month, 1) @@ -1995,11 +2003,11 @@ How do I cache method calls? ---------------------------- The two principal tools for caching methods are -:func:`functools.cached_property` and :func:`functools.lru_cache`. The +:deco:`functools.cached_property` and :deco:`functools.lru_cache`. The former stores results at the instance level and the latter at the class level. -The *cached_property* approach only works with methods that do not take +The ``cached_property`` approach only works with methods that do not take any arguments. It does not create a reference to the instance. The cached method result will be kept only as long as the instance is alive. @@ -2008,7 +2016,7 @@ method result will be released right away. The disadvantage is that if instances accumulate, so too will the accumulated method results. They can grow without bound. -The *lru_cache* approach works with methods that have :term:`hashable` +The ``lru_cache`` approach works with methods that have :term:`hashable` arguments. It creates a reference to the instance unless special efforts are made to pass in weak references. @@ -2042,11 +2050,11 @@ This example shows the various techniques:: # Depends on the station_id, date, and units. The above example assumes that the *station_id* never changes. If the -relevant instance attributes are mutable, the *cached_property* approach +relevant instance attributes are mutable, the ``cached_property`` approach can't be made to work because it cannot detect changes to the attributes. -To make the *lru_cache* approach work when the *station_id* is mutable, +To make the ``lru_cache`` approach work when the *station_id* is mutable, the class needs to define the :meth:`~object.__eq__` and :meth:`~object.__hash__` methods so that the cache can detect relevant attribute updates:: @@ -2092,10 +2100,10 @@ one user but run as another, such as if you are testing with a web server. Unless the :envvar:`PYTHONDONTWRITEBYTECODE` environment variable is set, creation of a .pyc file is automatic if you're importing a module and Python -has the ability (permissions, free space, etc...) to create a ``__pycache__`` +has the ability (permissions, free space, and so on) to create a ``__pycache__`` subdirectory and write the compiled module to that subdirectory. -Running Python on a top level script is not considered an import and no +Running Python on a top-level script is not considered an import and no ``.pyc`` will be created. For example, if you have a top-level module ``foo.py`` that imports another module ``xyz.py``, when you run ``foo`` (by typing ``python foo.py`` as a shell command), a ``.pyc`` will be created for @@ -2114,7 +2122,7 @@ the ``compile()`` function in that module interactively:: This will write the ``.pyc`` to a ``__pycache__`` subdirectory in the same location as ``foo.py`` (or you can override that with the optional parameter -``cfile``). +*cfile*). You can also automatically compile all files in a directory or directories using the :mod:`compileall` module. You can do it from the shell prompt by running @@ -2219,7 +2227,7 @@ changed module, do this:: importlib.reload(modname) Warning: this technique is not 100% fool-proof. In particular, modules -containing statements like :: +containing statements like:: from modname import some_objects diff --git a/Doc/faq/python-video-icon.png b/Doc/faq/python-video-icon.png deleted file mode 100644 index 265da50c7b38fc..00000000000000 Binary files a/Doc/faq/python-video-icon.png and /dev/null differ diff --git a/Doc/glossary.rst b/Doc/glossary.rst index b7bd547d38fd1e..3ac62270924171 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -21,7 +21,9 @@ Glossary right delimiters (parentheses, square brackets, curly braces or triple quotes), or after specifying a decorator. - * The :const:`Ellipsis` built-in constant. + .. index:: single: ...; ellipsis literal + + * The three dots form of the :ref:`Ellipsis ` object. abstract base class Abstract base classes complement :term:`duck-typing` by @@ -37,10 +39,11 @@ Glossary ABCs with the :mod:`abc` module. annotate function - A function that can be called to retrieve the :term:`annotations ` - of an object. This function is accessible as the :attr:`~object.__annotate__` - attribute of functions, classes, and modules. Annotate functions are a - subset of :term:`evaluate functions `. + A callable that can be called to retrieve the :term:`annotations ` of + an object. Annotate functions are usually :term:`functions `, + automatically generated as the :attr:`~object.__annotate__` attribute of functions, + classes, and modules. Annotate functions are a subset of + :term:`evaluate functions `. annotation A label associated with a variable, a class @@ -107,7 +110,7 @@ Glossary statements. asynchronous generator iterator - An object created by a :term:`asynchronous generator` function. + An object created by an :term:`asynchronous generator` function. This is an :term:`asynchronous iterator` which when called using the :meth:`~object.__anext__` method returns an awaitable object which will execute @@ -132,6 +135,14 @@ Glossary iterator's :meth:`~object.__anext__` method until it raises a :exc:`StopAsyncIteration` exception. Introduced by :pep:`492`. + atomic operation + An operation that appears to execute as a single, indivisible step: no + other thread can observe it half-done, and its effects become visible all + at once. Python does not guarantee that high-level statements are atomic + (for example, ``x += 1`` performs multiple bytecode operations and is not + atomic). Atomicity is only guaranteed where explicitly documented. See + also :term:`race condition` and :term:`data race`. + attached thread state A :term:`thread state` that is active for the current OS thread. @@ -150,9 +161,9 @@ Glossary On most builds of Python, having an attached thread state implies that the caller holds the :term:`GIL` for the current interpreter, so only one OS thread can have an attached thread state at a given moment. In - :term:`free-threaded ` builds of Python, threads can concurrently - hold an attached thread state, allowing for true parallelism of the bytecode - interpreter. + :term:`free-threaded builds ` of Python, threads can + concurrently hold an attached thread state, allowing for true parallelism of + the bytecode interpreter. attribute A value associated with an object which is usually referenced by name @@ -287,6 +298,22 @@ Glossary advanced mathematical feature. If you're not aware of a need for them, it's almost certain you can safely ignore them. + concurrency + The ability of a computer program to perform multiple tasks at the same + time. Python provides libraries for writing programs that make use of + different forms of concurrency. :mod:`asyncio` is a library for dealing + with asynchronous tasks and coroutines. :mod:`threading` provides + access to operating system threads and :mod:`multiprocessing` to + operating system processes. Multi-core processors can execute threads and + processes on different CPU cores at the same time (see + :term:`parallelism`). + + concurrent modification + When multiple threads modify shared data at the same time. Concurrent + modification without proper synchronization can cause + :term:`race conditions `, and might also trigger a + :term:`data race `, data corruption, or both. + context This term has different meanings depending on where and how it is used. Some common meanings: @@ -361,10 +388,32 @@ Glossary the :term:`cyclic garbage collector ` is to identify these groups and break the reference cycles so that the memory can be reclaimed. + data race + A situation where multiple threads access the same memory location + concurrently, at least one of the accesses is a write, and the threads + do not use any synchronization to control their access. Data races + lead to :term:`non-deterministic` behavior and can cause data corruption. + Proper use of :term:`locks ` and other :term:`synchronization primitives + ` prevents data races. Note that data races + can only happen in native code, but that :term:`native code` might be + exposed in a Python API. See also :term:`race condition` and + :term:`thread-safe`. + + deadlock + A situation in which two or more tasks (threads, processes, or coroutines) + wait indefinitely for each other to release resources or complete actions, + preventing any from making progress. For example, if thread A holds lock + 1 and waits for lock 2, while thread B holds lock 2 and waits for lock 1, + both threads will wait indefinitely. In Python this often arises from + acquiring multiple locks in conflicting orders or from circular + join/await dependencies. Deadlocks can be avoided by always acquiring + multiple :term:`locks ` in a consistent order. See also + :term:`lock` and :term:`reentrant`. + decorator A function returning another function, usually applied as a function transformation using the ``@wrapper`` syntax. Common examples for - decorators are :func:`classmethod` and :func:`staticmethod`. + decorators are :deco:`classmethod` and :deco:`staticmethod`. The decorator syntax is merely syntactic sugar, the following two function definitions are semantically equivalent:: @@ -435,6 +484,11 @@ Glossary with :term:`abstract base classes `.) Instead, it typically employs :func:`hasattr` tests or :term:`EAFP` programming. + dunder + An informal short-hand for "double underscore", used when talking about a + :term:`special method`. For example, ``__init__`` is often pronounced + "dunder init". + EAFP Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches @@ -462,6 +516,7 @@ Glossary core and with user code. f-string + f-strings String literals prefixed with ``f`` or ``F`` are commonly called "f-strings" which is short for :ref:`formatted string literals `. See also :pep:`498`. @@ -526,6 +581,13 @@ Glossary the :term:`global interpreter lock` which allows only one thread to execute Python bytecode at a time. See :pep:`703`. + free-threaded build + + A build of :term:`CPython` that supports :term:`free threading`, + configured using the :option:`--disable-gil` option before compilation. + + See :ref:`freethreading-python-howto`. + free variable Formally, as defined in the :ref:`language execution model `, a free variable is any variable used in a namespace which is not a local variable in that @@ -614,7 +676,7 @@ Glossary determined by the dispatch algorithm. See also the :term:`single dispatch` glossary entry, the - :func:`functools.singledispatch` decorator, and :pep:`443`. + :deco:`functools.singledispatch` decorator, and :pep:`443`. generic type A :term:`type` that can be parameterized; typically a @@ -654,6 +716,14 @@ Glossary requires the GIL to be held in order to use it. This refers to having an :term:`attached thread state`. + global state + Data that is accessible throughout a program, such as module-level + variables, class variables, or C static variables in :term:`extension modules + `. In multi-threaded programs, global state shared + between threads typically requires synchronization to avoid + :term:`race conditions ` and + :term:`data races `. + hash-based pyc A bytecode cache file that uses the hash rather than the last-modified time of the corresponding source file to determine its validity. See @@ -698,7 +768,9 @@ Glossary tuples. Such an object cannot be altered. A new object has to be created if a different value has to be stored. They play an important role in places where a constant hash value is needed, for example as a key - in a dictionary. + in a dictionary. Immutable objects are inherently :term:`thread-safe` + because their state cannot be modified after creation, eliminating concerns + about improperly synchronized :term:`concurrent modification`. import path A list of locations (or :term:`path entries `) that are @@ -715,6 +787,19 @@ Glossary An object that both finds and loads a module; both a :term:`finder` and :term:`loader` object. + index + A numeric value that represents the position of an element in + a :term:`sequence`. + + In Python, indexing starts at zero. + For example, ``things[0]`` names the *first* element of ``things``; + ``things[1]`` names the second one. + + In some contexts, Python allows negative indexes for counting from the + end of a sequence, and indexing using :term:`slices `. + + See also :term:`subscript`. + interactive Python has an interactive interpreter which means you can enter statements and expressions at the interpreter prompt, immediately @@ -788,9 +873,13 @@ Glossary CPython does not consistently apply the requirement that an iterator define :meth:`~iterator.__iter__`. - And also please note that the free-threading CPython does not guarantee - the thread-safety of iterator operations. + And also please note that :term:`free-threaded ` + CPython does not guarantee :term:`thread-safe` behavior of iterator + operations. + key + A value that identifies an entry in a :term:`mapping`. + See also :term:`subscript`. key function A key function or collation function is a callable that returns a value @@ -805,7 +894,7 @@ Glossary :func:`itertools.groupby`. There are several ways to create a key function. For example. the - :meth:`str.lower` method can serve as a key function for case insensitive + :meth:`str.casefold` method can serve as a key function for case insensitive sorts. Alternatively, a key function can be built from a :keyword:`lambda` expression such as ``lambda r: (r[0], r[2])``. Also, :func:`operator.attrgetter`, :func:`operator.itemgetter`, and @@ -827,10 +916,11 @@ Glossary :keyword:`if` statements. In a multi-threaded environment, the LBYL approach can risk introducing a - race condition between "the looking" and "the leaping". For example, the - code, ``if key in mapping: return mapping[key]`` can fail if another + :term:`race condition` between "the looking" and "the leaping". For example, + the code, ``if key in mapping: return mapping[key]`` can fail if another thread removes *key* from *mapping* after the test, but before the lookup. - This issue can be solved with locks or by using the EAFP approach. + This issue can be solved with :term:`locks ` or by using the + :term:`EAFP` approach. See also :term:`thread-safe`. lexical analyzer @@ -849,6 +939,29 @@ Glossary clause is optional. If omitted, all elements in ``range(256)`` are processed. + lock + A :term:`synchronization primitive` that allows only one thread at a + time to access a shared resource. A thread must acquire a lock before + accessing the protected resource and release it afterward. If a thread + attempts to acquire a lock that is already held by another thread, it + will block until the lock becomes available. Python's :mod:`threading` + module provides :class:`~threading.Lock` (a basic lock) and + :class:`~threading.RLock` (a :term:`reentrant` lock). Locks are used + to prevent :term:`race conditions ` and ensure + :term:`thread-safe` access to shared data. Alternative design patterns + to locks exist such as queues, producer/consumer patterns, and + thread-local state. See also :term:`deadlock`, and :term:`reentrant`. + + lock-free + An operation that does not acquire any :term:`lock` and uses atomic CPU + instructions to ensure correctness. Lock-free operations can execute + concurrently without blocking each other and cannot be blocked by + operations that hold locks. In :term:`free-threaded ` + Python, built-in types like :class:`dict` and :class:`list` provide + lock-free read operations, which means other threads may observe + intermediate states during multi-step modifications even when those + modifications hold the :term:`per-object lock`. + loader An object that loads a module. It must define the :meth:`!exec_module` and :meth:`!create_module` methods @@ -934,8 +1047,11 @@ Glossary See :term:`method resolution order`. mutable - Mutable objects can change their value but keep their :func:`id`. See - also :term:`immutable`. + An :term:`object` with state that is allowed to change during the course + of the program. In multi-threaded programs, mutable objects that are + shared between threads require careful synchronization to avoid + :term:`race conditions `. See also :term:`immutable`, + :term:`thread-safe`, and :term:`concurrent modification`. named tuple The term "named tuple" applies to any type or class that inherits from @@ -987,6 +1103,13 @@ Glossary See also :term:`module`. + native code + Code that is compiled to machine instructions and runs directly on the + processor, as opposed to code that is interpreted or runs in a virtual + machine. In the context of Python, native code typically refers to + C, C++, Rust or Fortran code in :term:`extension modules ` + that can be called from Python. See also :term:`extension module`. + nested scope The ability to refer to a variable in an enclosing definition. For instance, a function defined inside another function can refer to @@ -1003,6 +1126,15 @@ Glossary properties, :meth:`~object.__getattribute__`, class methods, and static methods. + non-deterministic + Behavior where the outcome of a program can vary between executions with + the same inputs. In multi-threaded programs, non-deterministic behavior + often results from :term:`race conditions ` where the + relative timing or interleaving of threads affects the result. + Proper synchronization using :term:`locks ` and other + :term:`synchronization primitives ` helps + ensure deterministic behavior. + object Any data with state (attributes or value) and defined behavior (methods). Also the ultimate base class of any :term:`new-style @@ -1017,6 +1149,15 @@ Glossary applied to all scopes, only those relying on a known set of local and nonlocal variable names are restricted to optimized scopes. + optional module + An :term:`extension module` that is part of the :term:`standard library`, + but may be absent in some builds of :term:`CPython`, + usually due to missing third-party libraries or because the module + is not available for a given platform. + + See :ref:`optional-module-requirements` for a list of optional modules + that require third-party libraries. + package A Python :term:`module` which can contain submodules or recursively, subpackages. Technically, a package is a Python module with a @@ -1024,6 +1165,16 @@ Glossary See also :term:`regular package` and :term:`namespace package`. + parallelism + Executing multiple operations at the same time (e.g. on multiple CPU + cores). In Python builds with the + :term:`global interpreter lock (GIL) `, only one + thread runs Python bytecode at a time, so taking advantage of multiple + CPU cores typically involves multiple processes + (e.g. :mod:`multiprocessing`) or native extensions that release the GIL. + In :term:`free-threaded ` Python, multiple Python threads + can run Python code simultaneously on different cores. + parameter A named entity in a :term:`function` (or method) definition that specifies an :term:`argument` (or in some cases, arguments) that the @@ -1077,6 +1228,16 @@ Glossary `, the :class:`inspect.Parameter` class, the :ref:`function` section, and :pep:`362`. + per-object lock + A :term:`lock` associated with an individual object instance rather than + a global lock shared across all objects. In :term:`free-threaded + ` Python, built-in types like :class:`dict` and + :class:`list` use per-object locks to allow concurrent operations on + different objects while serializing operations on the same object. + Operations that hold the per-object lock prevent other locking operations + on the same object from proceeding, but do not block :term:`lock-free` + operations. + path entry A single location on the :term:`import path` which the :term:`path based finder` consults to find modules for importing. @@ -1198,6 +1359,18 @@ Glossary >>> email.mime.text.__name__ 'email.mime.text' + race condition + A condition of a program where the behavior + depends on the relative timing or ordering of events, particularly in + multi-threaded programs. Race conditions can lead to + :term:`non-deterministic` behavior and bugs that are difficult to + reproduce. A :term:`data race` is a specific type of race condition + involving unsynchronized access to shared memory. The :term:`LBYL` + coding style is particularly susceptible to race conditions in + multi-threaded code. Using :term:`locks ` and other + :term:`synchronization primitives ` + helps prevent race conditions. + reference count The number of references to an object. When the reference count of an object drops to zero, it is deallocated. Some objects are @@ -1219,6 +1392,25 @@ Glossary See also :term:`namespace package`. + reentrant + A property of a function or :term:`lock` that allows it to be called or + acquired multiple times by the same thread without causing errors or a + :term:`deadlock`. + + For functions, reentrancy means the function can be safely called again + before a previous invocation has completed, which is important when + functions may be called recursively or from signal handlers. Thread-unsafe + functions may be :term:`non-deterministic` if they're called reentrantly in a + multithreaded program. + + For locks, Python's :class:`threading.RLock` (reentrant lock) is + reentrant, meaning a thread that already holds the lock can acquire it + again without blocking. In contrast, :class:`threading.Lock` is not + reentrant - attempting to acquire it twice from the same thread will cause + a deadlock. + + See also :term:`lock` and :term:`deadlock`. + REPL An acronym for the "read–eval–print loop", another name for the :term:`interactive` interpreter shell. @@ -1243,8 +1435,9 @@ Glossary The :class:`collections.abc.Sequence` abstract base class defines a much richer interface that goes beyond just :meth:`~object.__getitem__` and :meth:`~object.__len__`, adding - :meth:`!count`, :meth:`!index`, :meth:`~object.__contains__`, and - :meth:`~object.__reversed__`. Types that implement this expanded + :meth:`~sequence.count`, :meth:`~sequence.index`, + :meth:`~object.__contains__`, and :meth:`~object.__reversed__`. + Types that implement this expanded interface can be registered explicitly using :func:`~abc.ABCMeta.register`. For more documentation on sequence methods generally, see @@ -1261,10 +1454,11 @@ Glossary chosen based on the type of a single argument. slice - An object usually containing a portion of a :term:`sequence`. A slice is - created using the subscript notation, ``[]`` with colons between numbers - when several are given, such as in ``variable_name[1:3:5]``. The bracket - (subscript) notation uses :class:`slice` objects internally. + An object of type :class:`slice`, used to describe a portion of + a :term:`sequence`. + A slice object is created when using the :ref:`slicing ` form + of :ref:`subscript notation `, with colons inside square + brackets, such as in ``variable_name[1:3:5]``. soft deprecated A soft deprecated API should not be used in new code, @@ -1322,7 +1516,28 @@ Glossary See also :term:`borrowed reference`. + subscript + The expression in square brackets of a + :ref:`subscription expression `, for example, + the ``3`` in ``items[3]``. + Usually used to select an element of a container. + Also called a :term:`key` when subscripting a :term:`mapping`, + or an :term:`index` when subscripting a :term:`sequence`. + + synchronization primitive + A basic building block for coordinating (synchronizing) the execution of + multiple threads to ensure :term:`thread-safe` access to shared resources. + Python's :mod:`threading` module provides several synchronization primitives + including :class:`~threading.Lock`, :class:`~threading.RLock`, + :class:`~threading.Semaphore`, :class:`~threading.Condition`, + :class:`~threading.Event`, and :class:`~threading.Barrier`. Additionally, + the :mod:`queue` module provides multi-producer, multi-consumer queues + that are especially useful in multithreaded programs. These + primitives help prevent :term:`race conditions ` and + coordinate thread execution. See also :term:`lock`. + t-string + t-strings String literals prefixed with ``t`` or ``T`` are commonly called "t-strings" which is short for :ref:`template string literals `. @@ -1373,6 +1588,19 @@ Glossary See :ref:`Thread State and the Global Interpreter Lock ` for more information. + thread-safe + A module, function, or class that behaves correctly when used by multiple + threads concurrently. Thread-safe code uses appropriate + :term:`synchronization primitives ` like + :term:`locks ` to protect shared mutable state, or is designed + to avoid shared mutable state entirely. In the + :term:`free-threaded ` build, built-in types like + :class:`dict`, :class:`list`, and :class:`set` use internal locking + to make many operations thread-safe, although thread safety is not + necessarily guaranteed. Code that is not thread-safe may experience + :term:`race conditions ` and :term:`data races ` + when used in multi-threaded programs. + token A small unit of source code, generated by the @@ -1472,6 +1700,11 @@ Glossary A computer defined entirely in software. Python's virtual machine executes the :term:`bytecode` emitted by the bytecode compiler. + walrus operator + A light-hearted way to refer to the :ref:`assignment expression + ` operator ``:=`` because it looks a bit like a + walrus if you turn your head. + Zen of Python Listing of Python design principles and philosophies that are helpful in understanding and using the language. The listing can be found by typing diff --git a/Doc/howto/a-conceptual-overview-of-asyncio.rst b/Doc/howto/a-conceptual-overview-of-asyncio.rst new file mode 100644 index 00000000000000..7a7a87cb958400 --- /dev/null +++ b/Doc/howto/a-conceptual-overview-of-asyncio.rst @@ -0,0 +1,619 @@ +.. _a-conceptual-overview-of-asyncio: + +**************************************** +A conceptual overview of :mod:`!asyncio` +**************************************** + +This :ref:`HOWTO ` article seeks to help you build a sturdy mental +model of how :mod:`asyncio` fundamentally works, helping you understand the +how and why behind the recommended patterns. + +You might be curious about some key :mod:`!asyncio` concepts. +By the end of this article, you'll be able to comfortably answer these questions: + +- What's happening behind the scenes when an object is awaited? +- How does :mod:`!asyncio` differentiate between a task which doesn't need + CPU time (such as a network request or file read) as opposed to a task that + does (such as computing n-factorial)? +- How to write an asynchronous variant of an operation, such as + an async sleep or database request. + +.. seealso:: + + * The `guide `_ that inspired this HOWTO article, by Alexander Nordin. + * This in-depth `YouTube tutorial series `_ on + ``asyncio`` created by Python core team member, Łukasz Langa. + * `500 Lines or Less: A Web Crawler With asyncio Coroutines `_ by A. + Jesse Jiryu Davis and Guido van Rossum. + +-------------------------------------------- +A conceptual overview part 1: the high-level +-------------------------------------------- + +In part 1, we'll cover the main, high-level building blocks of :mod:`!asyncio`: +the event loop, coroutine functions, coroutine objects, tasks, and ``await``. + +========== +Event loop +========== + +Everything in :mod:`!asyncio` happens relative to the event loop. +It's the star of the show, but prefers to work behind the scenes, managing +and coordinating resources. +It's like an orchestra conductor. +Some power is explicitly granted to it, but a lot of its ability to get things +done comes from the respect and cooperation of its band members. + +In more technical terms, the event loop contains a collection of jobs to be run. +Some jobs are added directly by you, and some indirectly by :mod:`!asyncio`. +The event loop takes a job from its backlog of work and invokes it (or "gives +it control"), similar to calling a function, and then that job runs. +Once it pauses or completes, it returns control to the event loop. +The event loop will then select another job from its pool and invoke it. +You can *roughly* think of the collection of jobs as a queue: jobs are added and +then processed one at a time, generally (but not always) in order. +This process repeats indefinitely, with the event loop cycling endlessly +onwards. +If there are no more jobs pending execution, the event loop is smart enough to +rest and avoid needlessly wasting CPU cycles, and will come back when there's +more work to be done, such as when I/O operations complete or timers expire. + +Effective execution relies on jobs sharing well and cooperating; a greedy job +could hog control and leave the other jobs to starve, rendering the overall +event loop approach rather useless. + +:: + + import asyncio + + # This creates an event loop and indefinitely cycles through + # its collection of jobs. + event_loop = asyncio.new_event_loop() + event_loop.run_forever() + +===================================== +Asynchronous functions and coroutines +===================================== + +This is a basic, boring Python function:: + + def hello_printer(): + print( + "Hi, I am a lowly, simple printer, though I have all I " + "need in life -- \nfresh paper and my dearly beloved octopus " + "partner in crime." + ) + +Calling a regular function invokes its logic or body:: + + >>> hello_printer() + Hi, I am a lowly, simple printer, though I have all I need in life -- + fresh paper and my dearly beloved octopus partner in crime. + +The :ref:`async def `, as opposed to just a plain ``def``, makes +this an asynchronous function (or "coroutine function"). +Calling it creates and returns a :ref:`coroutine ` object. + +:: + + async def loudmouth_penguin(magic_number: int): + print( + "I am a super special talking penguin. Far cooler than that printer. " + f"By the way, my lucky number is: {magic_number}." + ) + +Calling the async function, ``loudmouth_penguin``, does not execute the print statement; +instead, it creates a coroutine object:: + + >>> loudmouth_penguin(magic_number=3) + + +The terms "coroutine function" and "coroutine object" are often conflated +as coroutine. +That can be confusing! +In this article, coroutine specifically refers to a coroutine object, or more +precisely, an instance of :class:`types.CoroutineType` (native coroutine). +Note that coroutines can also exist as instances of +:class:`collections.abc.Coroutine` -- a distinction that matters for type +checking. + +A coroutine represents the function's body or logic. +A coroutine has to be explicitly started; again, merely creating the coroutine +does not start it. +Notably, the coroutine can be paused and resumed at various points within the +function's body. +That pausing and resuming ability is what allows for asynchronous behavior! + +Coroutines and coroutine functions were built by leveraging the functionality +of :term:`generators ` and +:term:`generator functions `. +Recall, a generator function is a function that :keyword:`yield`\s, like this +one:: + + def get_random_number(): + # This would be a bad random number generator! + print("Hi") + yield 1 + print("Hello") + yield 7 + print("Howdy") + yield 4 + ... + +Similar to a coroutine function, calling a generator function does not run it. +Instead, it creates a generator object:: + + >>> get_random_number() + + +You can proceed to the next ``yield`` of a generator by using the +built-in function :func:`next`. +In other words, the generator runs, then pauses. +For example:: + + >>> generator = get_random_number() + >>> next(generator) + Hi + 1 + >>> next(generator) + Hello + 7 + +===== +Tasks +===== + +Roughly speaking, :ref:`tasks ` are coroutines (not coroutine +functions) tied to an event loop. +A task also maintains a list of callback functions whose importance will become +clear in a moment when we discuss :keyword:`await`. + +Creating a task automatically schedules it for execution (by adding a +callback to run it in the event loop's to-do list, that is, collection of jobs). +The recommended way to create tasks is via :func:`asyncio.create_task`. + +:mod:`!asyncio` automatically associates tasks with the event loop for you. +This automatic association was purposely designed into :mod:`!asyncio` for +the sake of simplicity. +Without it, you'd have to keep track of the event loop object and pass it to +any coroutine function that wants to create tasks, adding redundant clutter +to your code. + +:: + + coroutine = loudmouth_penguin(magic_number=5) + # This creates a Task object and schedules its execution via the event loop. + task = asyncio.create_task(coroutine) + +Earlier, we manually created the event loop and set it to run forever. +In practice, it's recommended to use (and common to see) :func:`asyncio.run`, +which takes care of managing the event loop and ensuring the provided +coroutine finishes before advancing. +For example, many async programs follow this setup:: + + import asyncio + + async def main(): + # Perform all sorts of wacky, wild asynchronous things... + ... + + if __name__ == "__main__": + asyncio.run(main()) + # The program will not reach the following print statement until the + # coroutine main() finishes. + print("coroutine main() is done!") + +It's important to be aware that the task itself is not added to the event loop, +only a callback to the task is. +This matters if the task object you created is garbage collected before it's +called by the event loop. +For example, consider this program: + +.. code-block:: + :linenos: + + async def hello(): + print("hello!") + + async def main(): + asyncio.create_task(hello()) + # Other asynchronous instructions which run for a while + # and cede control to the event loop... + ... + + asyncio.run(main()) + +Because there's no reference to the task object created on line 5, it *might* +be garbage collected before the event loop invokes it. +Later instructions in the coroutine ``main()`` hand control back to the event +loop so it can invoke other jobs. +When the event loop eventually tries to run the task, it might fail and +discover the task object does not exist! +This can also happen even if a coroutine keeps a reference to a task but +completes before that task finishes. +When the coroutine exits, local variables go out of scope and may be subject +to garbage collection. +In practice, ``asyncio`` and Python's garbage collector work pretty hard to +ensure this sort of thing doesn't happen. +But that's no reason to be reckless! + +===== +await +===== + +:keyword:`await` is a Python keyword that's commonly used in one of two +different ways:: + + await task + await coroutine + +In a crucial way, the behavior of ``await`` depends on the type of object +being awaited. + +^^^^^^^^^^^^^^ +Awaiting tasks +^^^^^^^^^^^^^^ + +Awaiting a task will cede control from the current task or coroutine to +the event loop. +In the process of relinquishing control, a few important things happen. +We'll use the following code example to illustrate:: + + async def plant_a_tree(): + dig_the_hole_task = asyncio.create_task(dig_the_hole()) + await dig_the_hole_task + + # Other instructions associated with planting a tree. + ... + +In this example, imagine the event loop has passed control to the start of the +coroutine ``plant_a_tree()``. +As seen above, the coroutine creates a task and then awaits it. +The ``await dig_the_hole_task`` instruction adds a callback (which will resume +``plant_a_tree()``) to the ``dig_the_hole_task`` object's list of callbacks. +And then, the instruction cedes control to the event loop. +Some time later, the event loop will pass control to ``dig_the_hole_task`` +and the task will finish whatever it needs to do. +Once the task finishes, it will add its various callbacks to the event loop, +in this case, a call to resume ``plant_a_tree()``. + +Generally speaking, when the awaited task finishes (``dig_the_hole_task``), +the original task or coroutine (``plant_a_tree()``) is added back to the event +loop's to-do list to be resumed. + +This is a basic, yet reliable mental model. +In practice, the control handoffs are slightly more complex, but not by much. +In part 2, we'll walk through the details that make this possible. + +^^^^^^^^^^^^^^^^^^^ +Awaiting coroutines +^^^^^^^^^^^^^^^^^^^ + +**Unlike tasks, awaiting a coroutine does not hand control back to the event +loop!** +Wrapping a coroutine in a task first, then awaiting that would cede +control. +The behavior of ``await coroutine`` is effectively the same as invoking a +regular, synchronous Python function. +Consider this program:: + + import asyncio + + async def coro_a(): + print("I am coro_a(). Hi!") + + async def coro_b(): + print("I am coro_b(). I sure hope no one hogs the event loop...") + + async def main(): + task_b = asyncio.create_task(coro_b()) + num_repeats = 3 + for _ in range(num_repeats): + await coro_a() + await task_b + + asyncio.run(main()) + +The first statement in the coroutine ``main()`` creates ``task_b`` and schedules +it for execution via the event loop. +Then, ``coro_a()`` is repeatedly awaited. Control never cedes to the +event loop, which is why we see the output of all three ``coro_a()`` +invocations before ``coro_b()``'s output: + +.. code-block:: none + + I am coro_a(). Hi! + I am coro_a(). Hi! + I am coro_a(). Hi! + I am coro_b(). I sure hope no one hogs the event loop... + +If we change ``await coro_a()`` to ``await asyncio.create_task(coro_a())``, the +behavior changes. +The coroutine ``main()`` cedes control to the event loop with that statement. +The event loop then proceeds through its backlog of work, calling ``task_b`` +and then the task which wraps ``coro_a()`` before resuming the coroutine +``main()``. + +.. code-block:: none + + I am coro_b(). I sure hope no one hogs the event loop... + I am coro_a(). Hi! + I am coro_a(). Hi! + I am coro_a(). Hi! + +This behavior of ``await coroutine`` can trip a lot of people up! +That example highlights how using only ``await coroutine`` could +unintentionally hog control from other tasks and effectively stall the event +loop. +:func:`asyncio.run` can help you detect such occurrences via the +``debug=True`` flag, which enables +:ref:`debug mode `. +Among other things, it will log any coroutines that monopolize execution for +100ms or longer. + +The design intentionally trades off some conceptual clarity around usage of +``await`` for improved performance. +Each time a task is awaited, control needs to be passed all the way up the +call stack to the event loop. +Then, the event loop needs to manage its internal state and work through +its processing logic to resume the next job. +That might sound minor, but in a large program with many ``await``\ s, that +overhead can add up to a non-negligible performance drag. + +------------------------------------------------ +A conceptual overview part 2: the nuts and bolts +------------------------------------------------ + +Part 2 goes into detail on the mechanisms :mod:`!asyncio` uses to manage +control flow. +This is where the magic happens. +You'll come away from this section knowing what ``await`` does behind the scenes +and how to make your own asynchronous operators. + +================================ +The inner workings of coroutines +================================ + +:mod:`!asyncio` leverages four components of Python to pass +around control. + +:meth:`coroutine.send(arg) ` is the method used to start or +resume a coroutine. +If the coroutine was paused and is now being resumed, the argument ``arg`` +will be sent in as the return value of the ``yield`` statement which originally +paused it. +If the coroutine is being used for the first time (as opposed to being resumed), +``arg`` must be ``None``. + +.. code-block:: + :linenos: + + class Rock: + def __await__(self): + value_sent_in = yield 7 + print(f"Rock.__await__ resuming with value: {value_sent_in}.") + return value_sent_in + + async def main(): + print("Beginning coroutine main().") + rock = Rock() + print("Awaiting rock...") + value_from_rock = await rock + print(f"Coroutine received value: {value_from_rock} from rock.") + return 23 + + coroutine = main() + intermediate_result = coroutine.send(None) + print(f"Coroutine paused and returned intermediate value: {intermediate_result}.") + + print(f"Resuming coroutine and sending in value: 42.") + try: + coroutine.send(42) + except StopIteration as e: + returned_value = e.value + print(f"Coroutine main() finished and provided value: {returned_value}.") + +:ref:`yield `, as usual, pauses execution and returns control +to the caller. +In the example above, the ``yield``, on line 3, is called by +``... = await rock`` on line 11. +More broadly speaking, ``await`` calls the :meth:`~object.__await__` method of +the given object. +``await`` also does one more very special thing: it propagates (or "passes +along") any ``yield``\ s it receives up the call chain. +In this case, that's back to ``... = coroutine.send(None)`` on line 16. + +The coroutine is resumed via the ``coroutine.send(42)`` call on line 21. +The coroutine picks back up from where it ``yield``\ ed (or paused) on line 3 +and executes the remaining statements in its body. +When a coroutine finishes, it raises a :exc:`StopIteration` exception with the +return value attached in the :attr:`~StopIteration.value` attribute. + +That snippet produces this output: + +.. code-block:: none + + Beginning coroutine main(). + Awaiting rock... + Coroutine paused and returned intermediate value: 7. + Resuming coroutine and sending in value: 42. + Rock.__await__ resuming with value: 42. + Coroutine received value: 42 from rock. + Coroutine main() finished and provided value: 23. + +It's worth pausing for a moment here and making sure you followed the various +ways that control flow and values were passed. A lot of important ideas were +covered and it's worth ensuring your understanding is firm. + +The only way to yield (or effectively cede control) from a coroutine is to +``await`` an object that ``yield``\ s in its ``__await__`` method. +That might sound odd to you. You might be thinking: + + 1. What about a ``yield`` directly within the coroutine function? The + coroutine function becomes an + :ref:`async generator function `, a + different beast entirely. + + 2. What about a :ref:`yield from ` within the coroutine function to a (plain) + generator? + That causes the error: ``SyntaxError: yield from not allowed in a coroutine.`` + This was intentionally designed for the sake of simplicity -- mandating only + one way of using coroutines. + Despite that, ``yield from`` and ``await`` effectively do the same thing. + Initially ``yield`` was barred as well, but was re-accepted to allow for + async generators. + +======= +Futures +======= + +A :ref:`future ` is an object meant to represent a +computation's status and result. +The term is a nod to the idea of something still to come or not yet happened, +and the object is a way to keep an eye on that something. + +A future has a few important attributes. One is its state, which can be either +"pending", "cancelled", or "done". +Another is its result, which is set when the state transitions to done. +Unlike a coroutine, a future does not represent the actual computation to be +done; instead, it represents the status and result of that computation, kind of +like a status light (red, yellow, or green) or indicator. + +:class:`asyncio.Task` subclasses :class:`asyncio.Future` in order to gain +these various capabilities. +The prior section said tasks store a list of callbacks, which wasn't entirely +accurate. +It's actually the ``Future`` class that implements this logic, which ``Task`` +inherits. + +Futures may also be used directly (not via tasks). +Tasks mark themselves as done when their coroutine is complete. +Futures are much more versatile and will be marked as done when you say so. +In this way, they're the flexible interface for you to make your own conditions +for waiting and resuming. + +======================== +A homemade asyncio.sleep +======================== + +We'll go through an example of how you could leverage a future to create your +own variant of asynchronous sleep (``async_sleep``) which mimics +:func:`asyncio.sleep`. + +This snippet registers a few tasks with the event loop and then awaits the task +created by ``asyncio.create_task``, which wraps the ``async_sleep(3)`` coroutine. +We want that task to finish only after three seconds have elapsed, but without +preventing other tasks from running. + +:: + + async def other_work(): + print("I like work. Work work.") + + async def main(): + # Add a few other tasks to the event loop, so there's something + # to do while asynchronously sleeping. + work_tasks = [ + asyncio.create_task(other_work()), + asyncio.create_task(other_work()), + asyncio.create_task(other_work()) + ] + print( + "Beginning asynchronous sleep at time: " + f"{datetime.datetime.now().strftime("%H:%M:%S")}." + ) + await asyncio.create_task(async_sleep(3)) + print( + "Done asynchronous sleep at time: " + f"{datetime.datetime.now().strftime("%H:%M:%S")}." + ) + # asyncio.gather effectively awaits each task in the collection. + await asyncio.gather(*work_tasks) + + +Below, we use a future to enable custom control over when that task will be +marked as done. +If :meth:`future.set_result() ` (the method +responsible for marking that future as done) is never called, then this task +will never finish. +We've also enlisted the help of another task, which we'll see in a moment, that +will monitor how much time has elapsed and, accordingly, call +``future.set_result()``. + +:: + + async def async_sleep(seconds: float): + future = asyncio.Future() + time_to_wake = time.time() + seconds + # Add the watcher-task to the event loop. + watcher_task = asyncio.create_task(_sleep_watcher(future, time_to_wake)) + # Block until the future is marked as done. + await future + +Below, we use a rather bare ``YieldToEventLoop()`` object to ``yield`` +from its ``__await__`` method, ceding control to the event loop. +This is effectively the same as calling ``asyncio.sleep(0)``, but this approach +offers more clarity, not to mention it's somewhat cheating to use +``asyncio.sleep`` when showcasing how to implement it! + +As usual, the event loop cycles through its tasks, giving them control +and receiving control back when they pause or finish. +The ``watcher_task``, which runs the coroutine ``_sleep_watcher(...)``, will +be invoked once per full cycle of the event loop. +On each resumption, it'll check the time and if not enough has elapsed, then +it'll pause once again and hand control back to the event loop. +Once enough time has elapsed, ``_sleep_watcher(...)`` +marks the future as done and completes by exiting its +infinite ``while`` loop. +Given this helper task is only invoked once per cycle of the event loop, +you'd be correct to note that this asynchronous sleep will sleep *at least* +three seconds, rather than exactly three seconds. +Note this is also true of ``asyncio.sleep``. + +:: + + class YieldToEventLoop: + def __await__(self): + yield + + async def _sleep_watcher(future, time_to_wake): + while True: + if time.time() >= time_to_wake: + # This marks the future as done. + future.set_result(None) + break + else: + await YieldToEventLoop() + +Here is the full program's output: + +.. code-block:: none + + $ python custom-async-sleep.py + Beginning asynchronous sleep at time: 14:52:22. + I like work. Work work. + I like work. Work work. + I like work. Work work. + Done asynchronous sleep at time: 14:52:25. + +You might feel this implementation of asynchronous sleep was unnecessarily +convoluted. +And, well, it was. +The example was meant to showcase the versatility of futures with a simple +example that could be mimicked for more complex needs. +For reference, you could implement it without futures, like so:: + + async def simpler_async_sleep(seconds): + time_to_wake = time.time() + seconds + while True: + if time.time() >= time_to_wake: + return + else: + await YieldToEventLoop() + +But that's all for now. Hopefully you're ready to more confidently dive into +some async programming or check out advanced topics in the +:mod:`rest of the documentation `. diff --git a/Doc/howto/abi3t-migration.rst b/Doc/howto/abi3t-migration.rst new file mode 100644 index 00000000000000..ed7a324c4af6f0 --- /dev/null +++ b/Doc/howto/abi3t-migration.rst @@ -0,0 +1,614 @@ +.. highlight:: c + +.. _abi3t-migration-howto: + +****************************************************** +Migrating to Stable ABI for free threading (``abi3t``) +****************************************************** + +Starting with the 3.15 release, CPython supports a variant of the Stable ABI +that supports :term:`free-threaded ` Python: +Stable ABI for Free-Threaded Builds, or ``abi3t`` for short. +This document describes how to adapt C API extensions to support free threading. + +Why do this +=========== + +The typical reason to use Stable ABI is to reduce the number of artifacts that +you need to build and distribute for each version of your library. + +Without the Stable ABI, you must build a separate shared library, and typically +a *wheel* distribution, for each feature version of CPython you wish +to support. +For example, each tag in the following table represents a separate +library/wheel: + ++-----------------+-----------------------+------------------------+ +| CPython version | Non-free-threaded | Free-threaded | ++=================+=======================+========================+ +| 3.12 | ``cpython-312`` | --- | ++-----------------+-----------------------+------------------------+ +| 3.13 | ``cpython-313`` | ``cpython-313t`` | ++-----------------+-----------------------+------------------------+ +| 3.14 | ``cpython-314`` | ``cpython-314t`` | ++-----------------+-----------------------+------------------------+ +| 3.15 | ``cpython-315`` | ``cpython-315t`` | ++-----------------+-----------------------+------------------------+ +| 3.16 | ``cpython-316`` | ``cpython-316t`` | ++-----------------+-----------------------+------------------------+ +| Later versions | :samp:`cpython-3{XX}` | :samp:`cpython-3{XX}t` | ++-----------------+-----------------------+------------------------+ + +That's a lot of builds, especially when multiplied by the number +of supported platforms. + +With the Stable ABI (``abi3``, introduced in CPython 3.2), a single extension +(per platform) can cover all *non-free-threaded* builds of CPython: + ++-----------------+-------------------+------------------------+ +| CPython version | Non-free-threaded | Free-threaded | ++=================+===================+========================+ +| 3.12 | ``abi3`` | --- | ++-----------------+ +------------------------+ +| 3.13 | | ``cpython-313t`` | ++-----------------+ +------------------------+ +| 3.14 | | ``cpython-314t`` | ++-----------------+ +------------------------+ +| 3.15 | | ``cpython-315t`` | ++-----------------+ +------------------------+ +| 3.16 | | ``cpython-316t`` | ++-----------------+ +------------------------+ +| Later versions | | :samp:`cpython-3{XX}t` | ++-----------------+-------------------+------------------------+ + +The Stable ABI for free-threaded builds (``abi3t``), introduced in +CPython 3.15, does the same for free-threaded builds. +And it's compatible with non-free-threaded ones as well: + ++-----------------+-------------------+------------------+ +| CPython version | Non-free-threaded | Free-threaded | ++=================+===================+==================+ +| 3.12 | ``abi3`` * | --- | ++-----------------+ +------------------+ +| 3.13 | | ``cpython-313t`` | ++-----------------+ +------------------+ +| 3.14 | | ``cpython-314t`` | ++-----------------+-------------------+------------------+ +| 3.15 | ``abi3t`` | ++-----------------+ + +| 3.16 | | ++-----------------+ + +| Later versions | | ++-----------------+-------------------+------------------+ + +\* (As above, the ``abi3`` extension is compatible with all non-free-threaded +builds; even the 3.15+ ones that this table "attributes" to ``abi3t``.) + +Why *not* do this +----------------- + +There are two main downsides to Stable ABI. + +First, you extension may become slower, since Stable ABI prioritizes +compatibility over performance. +The difference is usually not noticeable, and often can be mitigated by +using the same source to build both a Stable ABI build and a few +version-specific ones for "tier 1" CPython versions. + +Second, not all of the C API is available. +Extensions need to be ported to build for Stable ABI, which may be difficult +or, in rare cases, impossible. + +Specifically, ``abi3t`` requires APIs added in CPython 3.15. +If you want to build your extension for older versions of CPython from the +same source, you have two main options: + +- Use preprocessor conditionals. + + When following this guide, use ``#ifdef Py_TARGET_ABI3T`` blocks whenever + you are told to do a change that breaks the build on CPython versions you + care about. Keep the pre-existing code in ``#else`` blocks. + + For hand-written C extensions, this approach is reasonable down to + CPython 3.12, due to additions introduced in :pep:`697`. + Keeping compatibility with 3.11 and below may be worth it for code + generators (for example, Cython). + +- Do not port to ``abi3t``, and continue building separate extensions for + each version of CPython, until you can drop support for the older versions. + + This is a valid approach. Not all extensions need to switch to ``abi3t`` + right now. + + +Prerequisites +============= + +This guide assumes that you have an extension written directly in C (or C++), +which you want to port to ``abi3t``. + +If your extenstion uses a code generator (like Cython) or language binding +(like PyO3), it's best to wait until that tool has support for ``abi3t``. +If you maintain such a tool, you might be able to adapt the instructions +here for your tool. + +Non-free-threaded Stable ABI +---------------------------- + +Your extension should support the Stable ABI (``abi3t``). +If not, either port it first, or follow this guide but be prepared to fix +issues it does not mention. + +Free-threading support +---------------------- + +While it's technically not a hard prerequisite, you will most likely want to +prepare your extension for free threading before you port it to ``abi3t``. +See :ref:`freethreading-extensions-howto` for instructions. + +.. seealso:: + + `Porting Extension Modules to Support Free-Threading + `__: + A community-maintained porting guide for extension authors. + +Isolating extension modules +--------------------------- + +Your module should use :ref:`multi-phase initialization `, +and it should either be isolated or limit itself to be loaded at most once +per process. +If it is not your case, follow :ref:`isolating-extensions-howto` first. +(See the :ref:`opt-out section ` for a shortcut.) + +Avoiding variable-sized types +----------------------------- + +If your extension defines variable-sized types (using :c:macro:`Py_tp_itemsize` +or :c:member:`PyTypeObject.tp_itemsize`), it cannot be ported to +``abi3t`` 3.15. + + +Setting up the build +==================== + +If you use a build tool (such as setuptools, meson-python, scikit-build-core), +search its documentation for a way to select ``abi3t``. +At the time of writing, not all of them have this; but if your tool does, +use it. +You may want to verify that it set the right flag by temporarily adding the +following just after ``#include ``:: + + #if Py_TARGET_ABI3T+0 <= 0x30f0000 + #error "abi3t define is not set!" + #endif + +This should result in a different error than "``abt3t`` define is not set". + +.. note:: + + If your build tool doesn't support ``abi3t`` yet, set the following macro + before including ``Python.h``:: + + #define Py_TARGET_ABI3T 0x30f0000 + + or specify it as a compiler flag, for example:: + + -DPy_TARGET_ABI3T=0x30f0000 + + Once your extension builds with this setting, it will be compatible with + CPython 3.15 and above. + + If you set this macro manually, you will later need to name and tag the + resulting extension manually as well. + This is covered in :ref:`abi3t-migration-tagging` below. + +This guide will ask you to make a series of changes. +After each one, verify that your extension still builds in the original +(non-``abi3t``) configuration, and ideally run tests on all Python +versions you support. +This will ensure that nothing breaks as you are porting. + + +Module export hook +================== + +Unless you've done this step already, your extension module defines a +:ref:`module initialization function ` +named :samp:`PyInit_{}`. +You will need to port it to a :ref:`module export hook `, +:samp:`PyModExport_{}`, a feature added in CPython 3.15 in +:pep:`793`. + +Your existing init function should look like this (with your own names +for ```` and ````): + +.. code-block:: + :class: bad + + PyMODINIT_FUNC + PyInit_(void) + { + return PyModuleDef_Init(&); + } + +If there is some code before the ``return``, move it to +a :c:macro:`Py_mod_create` or :c:macro:`Py_mod_exec` slot function. +See the :ref:`PyInit documentation ` for related information. + +The function references a ``PyModuleDef`` object (```` in the code +above). +Its definition should be similar to the following, with different values +and perhaps some fields unnnamed or left out: + +.. code-block:: + :class: bad + + static PyModuleDef = { + PyModuleDef_HEAD_INIT, + .m_name = "my_module", + .m_doc = "my docstring", + .m_size = sizeof(my_state_struct), + .m_methods = my_methods, + .m_slots = my_slots, + .m_traverse = my_traverse, + .m_clear = my_clear, + .m_free = my_free, + }; + +Remove this definition and the ``PyInit`` function (or put them in +an ``#ifndef Py_TARGET_ABI3T`` block, to retain backwards compatibility), +and replace them with the following: + +.. code-block:: + :class: good + + PyABIInfo_VAR(abi_info); + + static PySlot my_slot_array[] = { + PySlot_STATIC_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_name, "my_module"), + PySlot_STATIC_DATA(Py_mod_doc, "my docstring"), + PySlot_SIZE(Py_mod_state_size, sizeof(my_state_struct)), + PySlot_STATIC_DATA(Py_mod_methods, my_methods), + PySlot_STATIC_DATA(Py_mod_slots, my_slots), + PySlot_FUNC(Py_mod_state_traverse, my_traverse), + PySlot_FUNC(Py_mod_state_clear, my_clear), + PySlot_FUNC(Py_mod_state_free, my_free), + PySlot_END + }; + + PyMODEXPORT_FUNC + PyModExport_(void) + { + return my_slot_array; + } + +Leave out any fields that were missing (except the new :c:macro:`Py_mod_abi`), +and substitute your own values. + +See the :c:type:`PySlot` and :c:ref:`export hook ` +documentation for details on this API. + +Associated ``PyModuleDef`` +-------------------------- + +Since the new API does not use a :c:type:`!PyModuleDef` structure, a definition +will not be associated with the resulting module. +This changes the behavior of the following functions: + +- :c:func:`PyModule_GetDef` +- :c:func:`PyType_GetModuleByDef` + +Check your code for these. +If you do not use them, you can skip this section. + +These functions are typically used for two purposes: + +1. To get the definition the module was created with. + This is no longer possible using the new API. + Modules no longer keep a reference to the definition, so you will need to + figure out a different way to pass the relevant data around. + +.. _abi3t-migration-module-token: + +2. To check if a given module object is “yours”. + This use case is now served by :ref:`module tokens ` -- + opaque pointers that identify a module. + To use a token, declare (or reuse) a unique static variable, for example: + + .. code-block:: + :class: good + + static char my_token; + + and add a pointer to it in a new entry to your module's ``PySlot`` array: + + .. code-block:: + :class: good + :emphasize-lines: 3 + + static PySlot my_slot_array[] = { + ... + PySlot_STATIC_DATA(Py_mod_token, &my_token), + PySlot_END + } + + Then, switch from :c:func:`PyModule_GetDef` calls such as: + + .. code-block:: + :class: bad + + PyModuleDef *def = PyModule_GetDef(module); + + to :c:func:`PyModule_GetToken` (which uses an output argument and may fail + with an exception): + + .. code-block:: + :class: good + + void *token; + if (PyModule_GetToken(module, &token) < 0) { + /* handle error */ + } + + and from :c:func:`PyType_GetModuleByDef` calls such as: + + .. code-block:: + :class: bad + + PyObject *module = PyType_GetModuleByDef(type, my_def); + /* handle error; use module */ + + to :c:func:`PyType_GetModuleByToken` (which returns a strong reference): + + .. code-block:: + :class: good + + PyObject *module = PyType_GetModuleByToken(type, my_token); + /* handle error; use module */ + Py_XDECREF(module); + +``PyObject`` opaqueness +======================= + +The :c:type:`PyObject` and :c:type:`PyVarObject` structures are opaque +in ``abi3t``. + +Accessing their members is prohibited. +If you do this, switch to getter/setter functions mentioned in +their documentation: + +- :c:member:`PyObject.ob_type` +- :c:member:`PyObject.ob_refcnt` +- :c:member:`PyVarObject.ob_size` + +Also, the *size* of the :c:type:`PyObject` structures is +unknown to the compiler. +It can -- and *does* -- change between different CPython builds. + +.. note:: + + While the size is available at runtime (for example as + ``sys.getsizeof(object())`` in Python code), you should resist the + temptation to calculate pointer offsets from it. + The object memory layout is subject to change in future + ``abi3t`` implementations. + + +Custom type definitions +----------------------- + +Since :c:type:`!PyObject` is opaque, the traditional way of defining +custom types no longer works: + +.. code-block:: + :class: bad + + typedef struct { + PyObject_HEAD // expands to `PyObject ob_base;` which has unknown size + + int my_data; + } CustomObject; + + static PyType_Spec CustomType_spec = { + ... + .basicsize = sizeof(CustomObject), + ... + }; + +Most likely, all your class definitions, *and* all code that accesses +your classes' data, will need to be rewritten. +This will probably be the biggest change you need to support ``abi3t``. + +For each such type, instead of defining a ``struct`` for the entire instance, +define one with only the “additional” fields -- ones specific to your class, +not its superclasses: + +.. code-block:: + :class: good + + typedef struct { + int my_data; + } CustomObjectData; + +Change the name. +Almost all code that uses the struct will need to change +(notably, pointers to the new structure cannot be cast to/from ``PyObject*``), +and changing the name will highlight the usages as compiler errors. +(If you use ``typeof``, C++ ``auto``, or similar ways to avoid +typing the type name, this won't work. Be extra careful, and consider running +tools to detect undefined behavior.) + +Then, to create the class, use *negative* ``basicsize`` to indicate +“extra” storage space rather than *total* instance size: + +.. code-block:: + :class: good + + static PyType_Spec CustomType_spec = { + ... + .basicsize = -sizeof(CustomObjectData), /* note the minus sign */ + ... + }; + +If you use :c:macro:`Py_tp_members`, set the :c:macro:`Py_RELATIVE_OFFSET` +flag on each member and specify the :c:member:`~PyMemberDef.offset` +relative to your new struct. + + +Custom type data access +----------------------- + +Then comes the hard part: in all code that needs to access this struct, +you will need an additional :c:func:`PyObject_GetTypeData` call to +retrieve a ``CustomObjectData *`` pointer from ``PyObject *``: + +.. code-block:: + :class: good + + PyObject *obj = ...; + CustomObjectData *data = PyObject_GetTypeData(obj, cls); + +Note that this call requires the *type object* for your class (``cls``). + +If your class is not subclassable (that is, it does not use the +:c:macro:`Py_TPFLAGS_BASETYPE` flag), ``cls`` will be ``Py_TYPE(obj)``. +Otherwise, **DO NOT USE** ``Py_TYPE`` with :c:func:`!PyObject_GetTypeData`: +it might return memory reserved to an unrelated subclass! +For example, if a user makes a subclass like this: + +.. code-block:: python + + class Sub(YourCustomClass): + __slots__ = ('a', 'b') + +then ``Py_TYPE(obj)`` is ``YourCustomClass``, and the underlying memory may +look like this: + +.. code-block:: text + + ╭─ PyObject *obj + │ ╭─ the pointer you want + │ │ ╭─ PyObject_GetTypeData(obj, Py_TYPE(obj)) + ▼ ▼ ▼ + ┌──────────┬───┬────────────────┬───┬─────────────┬───┬─────────────┐ + │ PyObject │...│ CustomTypeData │...│ PyObject *a │...│ PyObject *b │ + └──────────┴───┴────────────────┴───┴─────────────┴───┴─────────────┘ + +(Ellipses indicate possible padding. +Note that this memory layout is not guaranteed: future versions of Python may +add different padding or even switch the order of the structures.) + +There are two main ways to get the right class: + +- In instance methods, your implementation may use the :c:type:`PyCMethod` + signature (and the :c:macro:`METH_METHOD` bit in + :c:member:`PyMethodDef.ml_flags`), + and get the class as the ``defining_class`` argument. +- Otherwise, give your class a unique static token using the + :c:macro:`Py_tp_token` slot, and use: + + .. code-block:: + :class: good + + PyTypeObject cls; + if (PyType_GetBaseByToken(Py_TYPE(obj), my_tp_token, &cls) < 0) { + /* handle error */ + } + CustomObjectData *data = PyObject_GetTypeData(obj, cls); + + Type tokens work similarly to module tokens covered :ref:`earlier in this + guide `. + + + +Avoid build-time conditionals +============================= + +Check your code for API that identifies the version of Python used to +*build* your extension. +This no longer corresponds to the Python your extension runs on, so code +that uses this information often needs changing. +The macros to check for are: + +- :c:macro:`PY_VERSION_HEX`, :c:macro:`PY_MAJOR_VERSION`, + :c:macro:`PY_MINOR_VERSION`: + + - to get the run-time version, use :c:data:`Py_Version`; + - to determine what C API is available, use :c:macro:`Py_TARGET_ABI3T`. + This macro is set to the minimum supported version. + +- :c:macro:`Py_GIL_DISABLED`: under ``abi3t``, this macro is always defined. + Code that works with free-threaded Python *should* also work with + the GIL enabled (since the GIL can be enabled at run time), + and usually *does* (unless it, for some reason, requires more than one + :term:`attached thread state ` at one time). + + +Further code changes +==================== + +If you are still left with compiler errors or warnings, find a way to fix them. +Alas, this guide is limited, and cannot cover all possible code +changes extensions may need. + +If you find a problem that other extension authors might run into, +consider :ref:`reporting an issue ` (or sending +a pull request) for this guide. + +It is possible your issue cannot be fixed for the current version of ``abi3t``. +In that case, reporting it may help it get prioritized for the next version +of CPython. + + +.. _abi3t-migration-tagging: + +Tagging and distribution +======================== + +If you are using a build tool with ``abi3t`` support, your extension is ready, +but you might want to check that it was built correctly. + +Extensions built with ``abi3t`` should have the following extension: + +- On Windows: ``.pyd`` (like any other extension); +- Linux, macOS, and other systems that use the ``.so`` suffix: ``.abi3t.so`` + (**not** ``.cpython-315t.so`` or ``.abi3.so``). + Note that both free-threaded and non-free-threaded builds will + load ``.abi3t.so`` extensions; +- Other systems: consult your distributor, and perhaps update this guide. + +If you distribute the extension as a *wheel*, use the following tags: + +* Python tag: :samp:`cp3{XX}`, where *XX* is the minimum Python version + the extension is built for. + (For example, ``cp315`` if you set ``Py_TARGET_ABI3T`` to ``0x30f0000``. + See :ref:`abi3-compiling` for more values.) +* ABI tag: ``abi3.abi3t``. This is a *compressed tag set* that indicates + support for both non-free-threaded and free-threaded builds. + +For example, the wheel filename may look like this: + +.. code-block:: text + + myproject-1.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl + +.. seealso:: `Platform Compatibility Tags `__ in the PyPA package distribution metadata. + +If the filename or tags are incorrect, fix them. + + +Testing +======= + +Note that when you build an extension compatible with multiple versions of +CPython, you should always *test* it with each version it supports (for +example, 3.15, 3.16, and so on). +Stable ABI only guarantees *ABI* compatibility; there may also be behavior +changes -- both intentional ones (covered by :pep:`387`) and bugs. + +Be sure to run tests on both free-threaded and non-free-threaded builds +of CPython. + +If they pass, congratulations! You have an ``abi3t`` extension. diff --git a/Doc/howto/annotations.rst b/Doc/howto/annotations.rst index d7deb6c6bc1768..fcc4fe838c783b 100644 --- a/Doc/howto/annotations.rst +++ b/Doc/howto/annotations.rst @@ -4,8 +4,6 @@ Annotations Best Practices ************************** -:author: Larry Hastings - .. topic:: Abstract This document is designed to encapsulate the best practices @@ -154,7 +152,7 @@ on an arbitrary object ``o``: as the ``globals``, and ``dict(vars(o))`` as the ``locals``, when calling :func:`eval`. * If ``o`` is a wrapped callable using :func:`functools.update_wrapper`, - :func:`functools.wraps`, or :func:`functools.partial`, iteratively + :deco:`functools.wraps`, or :func:`functools.partial`, iteratively unwrap it by accessing either ``o.__wrapped__`` or ``o.func`` as appropriate, until you have found the root unwrapped function. * If ``o`` is a callable (but not a class), use diff --git a/Doc/howto/argparse.rst b/Doc/howto/argparse.rst index 902c50de00803c..0cb8c5cc3ebd36 100644 --- a/Doc/howto/argparse.rst +++ b/Doc/howto/argparse.rst @@ -4,8 +4,6 @@ Argparse Tutorial ***************** -:author: Tshepang Mbambo - .. currentmodule:: argparse This tutorial is intended to be a gentle introduction to :mod:`argparse`, the diff --git a/Doc/howto/curses.rst b/Doc/howto/curses.rst index 816639552d7cd6..e5f85e0110321c 100644 --- a/Doc/howto/curses.rst +++ b/Doc/howto/curses.rst @@ -6,9 +6,6 @@ .. currentmodule:: curses -:Author: A.M. Kuchling, Eric S. Raymond -:Release: 2.04 - .. topic:: Abstract diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index f6c3e473f1c36d..7233898860ada1 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -4,9 +4,6 @@ Descriptor Guide ================ -:Author: Raymond Hettinger -:Contact: - .. Contents:: @@ -28,7 +25,7 @@ This guide has four major sections: 4) The last section has pure Python equivalents for built-in descriptors that are written in C. Read this if you're curious about how functions turn into bound methods or about the implementation of common tools like - :func:`classmethod`, :func:`staticmethod`, :func:`property`, and + :deco:`classmethod`, :deco:`staticmethod`, :deco:`property`, and :term:`__slots__`. @@ -317,8 +314,8 @@ Descriptors invert that relationship and allow the data being looked-up to have a say in the matter. Descriptors are used throughout the language. It is how functions turn into -bound methods. Common tools like :func:`classmethod`, :func:`staticmethod`, -:func:`property`, and :func:`functools.cached_property` are all implemented as +bound methods. Common tools like :deco:`classmethod`, :deco:`staticmethod`, +:deco:`property`, and :deco:`functools.cached_property` are all implemented as descriptors. @@ -420,7 +417,7 @@ Here are three practical data validation utilities: def validate(self, value): if not isinstance(value, str): - raise TypeError(f'Expected {value!r} to be an str') + raise TypeError(f'Expected {value!r} to be a str') if self.minsize is not None and len(value) < self.minsize: raise ValueError( f'Expected {value!r} to be no smaller than {self.minsize!r}' @@ -594,7 +591,7 @@ a pure Python equivalent: def object_getattribute(obj, name): "Emulate PyObject_GenericGetAttr() in Objects/object.c" - null = object() + null = sentinel('null') objtype = type(obj) cls_var = find_name_in_mro(objtype, name, null) descr_get = getattr(type(cls_var), '__get__', null) @@ -1326,7 +1323,7 @@ example calls are unexciting: 30 Using the non-data descriptor protocol, a pure Python version of -:func:`staticmethod` would look like this: +:deco:`staticmethod` would look like this: .. testcode:: @@ -1466,7 +1463,7 @@ Now a new dictionary of unique keys can be constructed like this: {'a': None, 'b': None, 'r': None, 'c': None, 'd': None} Using the non-data descriptor protocol, a pure Python version of -:func:`classmethod` would look like this: +:deco:`classmethod` would look like this: .. testcode:: @@ -1604,7 +1601,7 @@ matters when a large number of instances are going to be created. 4. Improves speed. Reading instance variables is 35% faster with ``__slots__`` (as measured with Python 3.10 on an Apple M1 processor). -5. Blocks tools like :func:`functools.cached_property` which require an +5. Blocks tools like :deco:`functools.cached_property` which require an instance dictionary to function correctly: .. testcode:: @@ -1635,12 +1632,12 @@ by member descriptors: .. testcode:: - null = object() + null = sentinel('null') class Member: def __init__(self, name, clsname, offset): - 'Emulate PyMemberDef in Include/structmember.h' + 'Emulate PyMemberDef in Include/descrobject.h' # Also see descr_new() in Objects/descrobject.c self.name = name self.clsname = clsname diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst index 6441b7aed1eda8..adb9dc9a4879eb 100644 --- a/Doc/howto/enum.rst +++ b/Doc/howto/enum.rst @@ -105,8 +105,8 @@ The complete :class:`!Weekday` enum now looks like this:: Now we can find out what today is! Observe:: - >>> from datetime import date - >>> Weekday.from_date(date.today()) # doctest: +SKIP + >>> import datetime as dt + >>> Weekday.from_date(dt.date.today()) # doctest: +SKIP Of course, if you're reading this on some other day, you'll see that day instead. @@ -256,7 +256,7 @@ Ensuring unique enumeration values ---------------------------------- By default, enumerations allow multiple names as aliases for the same value. -When this behavior isn't desired, you can use the :func:`unique` decorator:: +When this behavior isn't desired, you can use the :deco:`unique` decorator:: >>> from enum import Enum, unique >>> @unique @@ -371,7 +371,7 @@ Equality comparisons are defined though:: >>> Color.BLUE == Color.BLUE True -Comparisons against non-enumeration values will always compare not equal +Equality comparisons against non-enumeration values will always return ``False`` (again, :class:`IntEnum` was explicitly designed to behave differently, see below):: @@ -509,7 +509,7 @@ to use the standard :func:`repr`. .. note:: - Adding :func:`~dataclasses.dataclass` decorator to :class:`Enum` + Adding :deco:`~dataclasses.dataclass` decorator to :class:`Enum` and its subclasses is not supported. It will not raise any errors, but it will produce very strange results at runtime, such as members being equal to each other:: @@ -965,75 +965,16 @@ want one of them to be the value:: Finer Points -^^^^^^^^^^^^ - -Supported ``__dunder__`` names -"""""""""""""""""""""""""""""" - -:attr:`~enum.EnumType.__members__` is a read-only ordered mapping of ``member_name``:``member`` -items. It is only available on the class. - -:meth:`~object.__new__`, if specified, must create and return the enum members; it is -also a very good idea to set the member's :attr:`~Enum._value_` appropriately. Once -all the members are created it is no longer used. - - -Supported ``_sunder_`` names -"""""""""""""""""""""""""""" +------------ -- :attr:`~Enum._name_` -- name of the member -- :attr:`~Enum._value_` -- value of the member; can be set in ``__new__`` -- :meth:`~Enum._missing_` -- a lookup function used when a value is not found; - may be overridden -- :attr:`~Enum._ignore_` -- a list of names, either as a :class:`list` or a - :class:`str`, that will not be transformed into members, and will be removed - from the final class -- :meth:`~Enum._generate_next_value_` -- used to get an appropriate value for - an enum member; may be overridden -- :meth:`~EnumType._add_alias_` -- adds a new name as an alias to an existing - member. -- :meth:`~EnumType._add_value_alias_` -- adds a new value as an alias to an - existing member. See `MultiValueEnum`_ for an example. +Supported ``__dunder__`` and ``_sunder_`` names +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - .. note:: - - For standard :class:`Enum` classes the next value chosen is the highest - value seen incremented by one. - - For :class:`Flag` classes the next value chosen will be the next highest - power-of-two. - - .. versionchanged:: 3.13 - Prior versions would use the last seen value instead of the highest value. - -.. versionadded:: 3.6 ``_missing_``, ``_order_``, ``_generate_next_value_`` -.. versionadded:: 3.7 ``_ignore_`` -.. versionadded:: 3.13 ``_add_alias_``, ``_add_value_alias_`` - -To help keep Python 2 / Python 3 code in sync an :attr:`~Enum._order_` attribute can -be provided. It will be checked against the actual order of the enumeration -and raise an error if the two do not match:: - - >>> class Color(Enum): - ... _order_ = 'RED GREEN BLUE' - ... RED = 1 - ... BLUE = 3 - ... GREEN = 2 - ... - Traceback (most recent call last): - ... - TypeError: member order does not match _order_: - ['RED', 'BLUE', 'GREEN'] - ['RED', 'GREEN', 'BLUE'] - -.. note:: - - In Python 2 code the :attr:`~Enum._order_` attribute is necessary as definition - order is lost before it can be recorded. +The supported ``__dunder__`` and ``_sunder_`` names can be found in the :ref:`Enum API documentation `. _Private__names -""""""""""""""" +^^^^^^^^^^^^^^^ :ref:`Private names ` are not converted to enum members, but remain normal attributes. @@ -1042,7 +983,7 @@ but remain normal attributes. ``Enum`` member type -"""""""""""""""""""" +^^^^^^^^^^^^^^^^^^^^ Enum members are instances of their enum class, and are normally accessed as ``EnumClass.member``. In certain situations, such as writing custom enum @@ -1055,7 +996,7 @@ recommended. Creating members that are mixed with other data types -""""""""""""""""""""""""""""""""""""""""""""""""""""" +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When subclassing other data types, such as :class:`int` or :class:`str`, with an :class:`Enum`, all values after the ``=`` are passed to that data type's @@ -1069,7 +1010,7 @@ constructor. For example:: Boolean value of ``Enum`` classes and members -""""""""""""""""""""""""""""""""""""""""""""" +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Enum classes that are mixed with non-:class:`Enum` types (such as :class:`int`, :class:`str`, etc.) are evaluated according to the mixed-in @@ -1084,7 +1025,7 @@ Plain :class:`Enum` classes always evaluate as :data:`True`. ``Enum`` classes with methods -""""""""""""""""""""""""""""" +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you give your enum subclass extra methods, like the `Planet`_ class below, those methods will show up in a :func:`dir` of the member, @@ -1097,7 +1038,7 @@ but not of the class:: Combining members of ``Flag`` -""""""""""""""""""""""""""""" +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Iterating over a combination of :class:`Flag` members will only return the members that are comprised of a single bit:: @@ -1117,7 +1058,7 @@ are comprised of a single bit:: ``Flag`` and ``IntFlag`` minutia -"""""""""""""""""""""""""""""""" +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Using the following snippet for our examples:: @@ -1478,6 +1419,7 @@ alias:: behaviors as well as disallowing aliases. If the only desired change is disallowing aliases, the :func:`unique` decorator can be used instead. +.. _multi-value-enum: MultiValueEnum ^^^^^^^^^^^^^^^^^ @@ -1538,8 +1480,8 @@ TimePeriod An example to show the :attr:`~Enum._ignore_` attribute in use:: - >>> from datetime import timedelta - >>> class Period(timedelta, Enum): + >>> import datetime as dt + >>> class Period(dt.timedelta, Enum): ... "different lengths of time" ... _ignore_ = 'Period i' ... Period = vars() diff --git a/Doc/howto/free-threading-extensions.rst b/Doc/howto/free-threading-extensions.rst index 577e283bb9cb4c..ad0578df0a2702 100644 --- a/Doc/howto/free-threading-extensions.rst +++ b/Doc/howto/free-threading-extensions.rst @@ -45,9 +45,12 @@ single-phase initialization. Multi-Phase Initialization .......................... -Extensions that use multi-phase initialization (i.e., -:c:func:`PyModuleDef_Init`) should add a :c:data:`Py_mod_gil` slot in the -module definition. If your extension supports older versions of CPython, +Extensions that use :ref:`multi-phase initialization ` +(functions like :c:func:`PyModuleDef_Init`, +:c:func:`PyModExport_* ` export hook, +:c:func:`PyModule_FromSlotsAndSpec`) should add a +:c:data:`Py_mod_gil` slot in the module definition. +If your extension supports older versions of CPython, you should guard the slot with a :c:data:`PY_VERSION_HEX` check. :: @@ -60,18 +63,12 @@ you should guard the slot with a :c:data:`PY_VERSION_HEX` check. {0, NULL} }; - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - .m_slots = module_slots, - ... - }; - Single-Phase Initialization ........................... -Extensions that use single-phase initialization (i.e., -:c:func:`PyModule_Create`) should call :c:func:`PyUnstable_Module_SetGIL` to +Extensions that use legacy :ref:`single-phase initialization ` +(that is, :c:func:`PyModule_Create`) should call :c:func:`PyUnstable_Module_SetGIL` to indicate that they support running with the GIL disabled. The function is only defined in the free-threaded build, so you should guard the call with ``#ifdef Py_GIL_DISABLED`` to avoid compilation errors in the regular build. @@ -173,9 +170,9 @@ that return :term:`strong references `. +-----------------------------------+-----------------------------------+ | :c:func:`PyDict_Next` | none (see :ref:`PyDict_Next`) | +-----------------------------------+-----------------------------------+ -| :c:func:`PyWeakref_GetObject` | :c:func:`PyWeakref_GetRef` | +| :c:func:`!PyWeakref_GetObject` | :c:func:`PyWeakref_GetRef` | +-----------------------------------+-----------------------------------+ -| :c:func:`PyWeakref_GET_OBJECT` | :c:func:`PyWeakref_GetRef` | +| :c:func:`!PyWeakref_GET_OBJECT` | :c:func:`PyWeakref_GetRef` | +-----------------------------------+-----------------------------------+ | :c:func:`PyImport_AddModule` | :c:func:`PyImport_AddModuleRef` | +-----------------------------------+-----------------------------------+ @@ -203,7 +200,7 @@ Memory Allocation APIs Python's memory management C API provides functions in three different :ref:`allocation domains `: "raw", "mem", and "object". For thread-safety, the free-threaded build requires that only Python objects -are allocated using the object domain, and that all Python object are +are allocated using the object domain, and that all Python objects are allocated using that domain. This differs from the prior Python versions, where this was only a best practice and not a hard requirement. @@ -221,13 +218,15 @@ Thread State and GIL APIs Python provides a set of functions and macros to manage thread state and the GIL, such as: +* :c:func:`PyThreadState_Ensure`, :c:func:`PyThreadState_EnsureFromView`, + and :c:func:`PyThreadState_Release` * :c:func:`PyGILState_Ensure` and :c:func:`PyGILState_Release` * :c:func:`PyEval_SaveThread` and :c:func:`PyEval_RestoreThread` * :c:macro:`Py_BEGIN_ALLOW_THREADS` and :c:macro:`Py_END_ALLOW_THREADS` These functions should still be used in the free-threaded build to manage thread state even when the :term:`GIL` is disabled. For example, if you -create a thread outside of Python, you must call :c:func:`PyGILState_Ensure` +create a thread outside of Python, you must call :c:func:`PyThreadState_Ensure` before calling into the Python API to ensure that the thread has a valid Python thread state. @@ -344,12 +343,12 @@ This means you cannot rely on nested critical sections to lock multiple objects at once, as the inner critical section may suspend the outer ones. Instead, use :c:macro:`Py_BEGIN_CRITICAL_SECTION2` to lock two objects simultaneously. -Note that the locks described above are only :c:type:`!PyMutex` based locks. +Note that the locks described above are only :c:type:`PyMutex` based locks. The critical section implementation does not know about or affect other locking mechanisms that might be in use, like POSIX mutexes. Also note that while -blocking on any :c:type:`!PyMutex` causes the critical sections to be +blocking on any :c:type:`PyMutex` causes the critical sections to be suspended, only the mutexes that are part of the critical sections are -released. If :c:type:`!PyMutex` is used without a critical section, it will +released. If :c:type:`PyMutex` is used without a critical section, it will not be released and therefore does not get the same deadlock avoidance. Important Considerations @@ -387,6 +386,30 @@ Important Considerations internal extension state, standard mutexes or other synchronization primitives might be more appropriate. +.. _per-object-locks: + +Per-Object Locks (``ob_mutex``) +............................... + +In the free-threaded build, each Python object contains a :c:member:`~PyObject.ob_mutex` +field of type :c:type:`PyMutex`. This mutex is **reserved for use by the +critical section API** (:c:macro:`Py_BEGIN_CRITICAL_SECTION` / +:c:macro:`Py_END_CRITICAL_SECTION`). + +.. warning:: + + Do **not** lock ``ob_mutex`` directly with ``PyMutex_Lock(&obj->ob_mutex)``. + Mixing direct ``PyMutex_Lock`` calls with the critical section API on the + same mutex can cause deadlocks. + +Even if your own code never uses critical sections on a particular object type, +**CPython internals may use the critical section API on any Python object**. + +If your extension type needs its own lock, add a separate :c:type:`PyMutex` +field (or another synchronization primitive) to your object struct. +:c:type:`PyMutex` is very lightweight, so there is negligible cost to having +an additional one. + Building Extensions for the Free-Threaded Build =============================================== @@ -395,10 +418,9 @@ C API extensions need to be built specifically for the free-threaded build. The wheels, shared libraries, and binaries are indicated by a ``t`` suffix. * `pypa/manylinux `_ supports the - free-threaded build, with the ``t`` suffix, such as ``python3.13t``. -* `pypa/cibuildwheel `_ supports the - free-threaded build if you set - `CIBW_ENABLE to cpython-freethreading `_. + free-threaded build, with the ``t`` suffix, such as ``python3.14t``. +* `pypa/cibuildwheel `_ supports + building wheels for the free-threaded build of Python 3.14 and newer. Limited C API and Stable ABI ............................ diff --git a/Doc/howto/free-threading-python.rst b/Doc/howto/free-threading-python.rst index 24069617c47ae1..53bea1db191d76 100644 --- a/Doc/howto/free-threading-python.rst +++ b/Doc/howto/free-threading-python.rst @@ -11,9 +11,7 @@ available processing power by running threads in parallel on available CPU cores While not all software will benefit from this automatically, programs designed with threading in mind will run faster on multi-core hardware. -The free-threaded mode is working and continues to be improved, but -there is some additional overhead in single-threaded workloads compared -to the regular build. Additionally, third-party packages, in particular ones +Some third-party packages, in particular ones with an :term:`extension module`, may not be ready for use in a free-threaded build, and will re-enable the :term:`GIL`. @@ -101,60 +99,42 @@ This section describes known limitations of the free-threaded CPython build. Immortalization --------------- -The free-threaded build of the 3.13 release makes some objects :term:`immortal`. +In the free-threaded build, some objects are :term:`immortal`. Immortal objects are not deallocated and have reference counts that are never modified. This is done to avoid reference count contention that would prevent efficient multi-threaded scaling. -An object will be made immortal when a new thread is started for the first time -after the main thread is running. The following objects are immortalized: +As of the 3.14 release, immortalization is limited to: -* :ref:`function ` objects declared at the module level -* :ref:`method ` descriptors -* :ref:`code ` objects -* :term:`module` objects and their dictionaries -* :ref:`classes ` (type objects) - -Because immortal objects are never deallocated, applications that create many -objects of these types may see increased memory usage. This is expected to be -addressed in the 3.14 release. - -Additionally, numeric and string literals in the code as well as strings -returned by :func:`sys.intern` are also immortalized. This behavior is -expected to remain in the 3.14 free-threaded build. +* Code constants: numeric literals, string literals, and tuple literals + composed of other constants. +* Strings interned by :func:`sys.intern`. Frame objects ------------- -It is not safe to access :ref:`frame ` objects from other -threads and doing so may cause your program to crash . This means that -:func:`sys._current_frames` is generally not safe to use in a free-threaded -build. Functions like :func:`inspect.currentframe` and :func:`sys._getframe` -are generally safe as long as the resulting frame object is not passed to -another thread. +It is not safe to access :attr:`frame.f_locals` from a :ref:`frame ` +object if that frame is currently executing in another thread, and doing so may +crash the interpreter. + Iterators --------- -Sharing the same iterator object between multiple threads is generally not -safe and threads may see duplicate or missing elements when iterating or crash -the interpreter. +It is generally not thread-safe to access the same iterator object from +multiple threads concurrently, and threads may see duplicate or missing +elements. Single-threaded performance --------------------------- The free-threaded build has additional overhead when executing Python code -compared to the default GIL-enabled build. In 3.13, this overhead is about -40% on the `pyperformance `_ suite. -Programs that spend most of their time in C extensions or I/O will see -less of an impact. The largest impact is because the specializing adaptive -interpreter (:pep:`659`) is disabled in the free-threaded build. We expect -to re-enable it in a thread-safe way in the 3.14 release. This overhead is -expected to be reduced in upcoming Python release. We are aiming for an -overhead of 10% or less on the pyperformance suite compared to the default -GIL-enabled build. +compared to the default GIL-enabled build. The amount of overhead depends +on the workload and hardware. On the pyperformance benchmark suite, the +average overhead ranges from about 1% on macOS aarch64 to 8% on x86-64 Linux +systems. Behavioral changes @@ -185,3 +165,132 @@ to false. If the flag is true then the :class:`warnings.catch_warnings` context manager uses a context variable for warning filters. If the flag is false then :class:`~warnings.catch_warnings` modifies the global filters list, which is not thread-safe. See the :mod:`warnings` module for more details. + + +Increased memory usage +---------------------- + +The free-threaded build will typically use more memory compared to the default +build. There are multiple reasons for this, mostly due to design decisions. + + +All interned strings are immortal +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For modern Python versions (since version 2.3), interning a string (e.g. with +:func:`sys.intern`) does not cause it to become immortal. Instead, if the last +reference to that string disappears, it will be removed from the interned +string table. This is not the case for the free-threaded build and any interned +string will become immortal, surviving until interpreter shutdown. + + +Non-GC objects have a larger object header +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The free-threaded build uses a different :c:type:`PyObject` structure. Instead +of having the GC related information allocated before the :c:type:`PyObject` +structure, like in the default build, the GC related info is part of the normal +object header. For example, on the AMD64 platform, ``None`` uses 32 bytes on +the free-threaded build vs 16 bytes for the default build. GC objects (such as +dicts and lists) are the same size for both builds since the free-threaded +build does not use additional space for the GC info. + + +QSBR can delay freeing of memory +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to safely implement lock-free data structures, a safe memory +reclamation (SMR) scheme is used, known as quiescent state-based reclamation +(QSBR). This means that the memory backing data structures allowing lock-free +access will use QSBR, which defers the free operation, rather than immediately +freeing the memory. Two examples of these data structures are the list object +and the dictionary keys object. See ``InternalDocs/qsbr.md`` in the CPython +source tree for more details on how QSBR is implemented. Running +:func:`gc.collect` should cause all memory being held by QSBR to be actually +freed. Note that even when QSBR frees the memory, the underlying memory +allocator may not immediately return that memory to the OS and so the resident +set size (RSS) of the process might not decrease. + + +mimalloc allocator vs pymalloc +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The default build will normally use the "pymalloc" memory allocator for small +allocations (512 bytes or smaller). The free-threaded build does not use +pymalloc and allocates all Python objects using the "mimalloc" allocator. The +pymalloc allocator has the following properties that help keep memory usage +low: small per-allocated-block overhead, effective memory fragmentation +prevention, and quick return of free memory to the operating system. The +mimalloc allocator does quite well in these respects as well but can have some +more overhead. + +In the free-threaded build, mimalloc manages memory in a number of separate +heaps (currently four). For example, all GC supporting objects are allocated +from their own heap. Using separate heaps means that free memory in one heap +cannot be used for an allocation that uses another heap. Also, some heaps are +configured to use QSBR (quiescent-state based reclamation) when freeing the +memory that backs up the heap (known as "pages" in mimalloc terminology). The +use of QSBR creates a delay between all memory blocks for a page being freed +and the memory page being released, either for new allocations or back to the +OS. + +The mimalloc allocator also defers returning freed memory back to the OS. You +can reduce that delay by setting the environment variable +:envvar:`!MIMALLOC_PURGE_DELAY` to ``0``. Note that this will likely reduce +the performance of the allocator. + + +Free-threaded reference counting can cause objects to live longer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In the default build, when an object's reference count reaches zero, it is +normally deallocated. The free-threaded build uses "biased reference +counting", with a fast-path for objects "owned" by the current thread and a +slow path for other objects. See :pep:`703` for additional details. Any time +an object's reference count ends up in a "queued" state, deallocation can be +deferred. The queued state is cleared from the "eval breaker" section of the +bytecode evaluator. + +The free-threaded build also allows a different mode of reference counting, +known as "deferred reference counting". This mode is enabled by setting a flag +on a per-object basis. Deferred reference counting is enabled for the +following types: + +* module objects +* module top-level functions +* class methods defined in the class scope +* descriptor objects +* thread-local objects, created by :class:`threading.local` + +When deferred reference counting is enabled, references from Python function +stacks are not added to the reference count. This scheme reduces the overhead +of reference counting, especially for objects used from multiple threads. +Because the stack references are not counted, objects with deferred reference +counting are not immediately freed when their internal reference count goes to +zero. Instead, they are examined by the next GC run and, if no stack +references to them are found, they are freed. This means these objects are +freed by the GC and not when their reference count goes to zero, as is typical. + + +Per-thread reference counting can delay freeing objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To avoid contention on the reference count fields of frequently shared +objects, the free-threaded build also uses "per-thread reference counting" +for a few selected object types. Rather than updating a single shared +reference count, each thread maintains its own local reference count array, +indexed by a unique id assigned to the object. The true reference count is +only computed by summing the per-thread counts when the object's local +count drops to zero. Per-thread reference counting is currently used for: + +* heap type objects (classes created in Python) +* code objects +* the ``__dict__`` of module objects + +Because the per-thread counts must be merged back to the object before it +can be deallocated, objects using per-thread reference counting are +typically freed later than they would be in the default build. In +particular, such an object is usually not freed until the thread that +referenced it reaches a safe point (for example, in the "eval breaker" +section of the bytecode evaluator) or exits. Running :func:`gc.collect` +will merge the per-thread counts and allow these objects to be freed. diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst index 053558e389030a..a61fdaee27f6b1 100644 --- a/Doc/howto/functional.rst +++ b/Doc/howto/functional.rst @@ -4,9 +4,6 @@ Functional Programming HOWTO ******************************** -:Author: A. M. Kuchling -:Release: 0.32 - In this document, we'll take a tour of Python's features suitable for implementing programs in a functional style. After an introduction to the concepts of functional programming, we'll look at language features such as @@ -1042,7 +1039,7 @@ first calculation. :: >>> functools.reduce(operator.concat, []) Traceback (most recent call last): ... - TypeError: reduce() of empty sequence with no initial value + TypeError: reduce() of empty iterable with no initial value >>> functools.reduce(operator.mul, [1, 2, 3], 1) 6 >>> functools.reduce(operator.mul, [], 1) @@ -1185,7 +1182,8 @@ about whether this lambda-free style is better. Revision History and Acknowledgements ===================================== -The author would like to thank the following people for offering suggestions, +This HOWTO was originally written by A. M. Kuchling. The author would like to +thank the following people for offering suggestions, corrections and assistance with various drafts of this article: Ian Bicking, Nick Coghlan, Nick Efford, Raymond Hettinger, Jim Jewett, Mike Krell, Leandro Lameiro, Jussi Salmela, Collin Winter, Blake Winton. @@ -1239,9 +1237,9 @@ Text Processing". Mertz also wrote a 3-part series of articles on functional programming for IBM's DeveloperWorks site; see -`part 1 `__, -`part 2 `__, and -`part 3 `__, +`part 1 `__, +`part 2 `__, and +`part 3 `__. Python documentation diff --git a/Doc/howto/gdb_helpers.rst b/Doc/howto/gdb_helpers.rst index 98ce813ca4ab02..33d1fbf8cd9e9e 100644 --- a/Doc/howto/gdb_helpers.rst +++ b/Doc/howto/gdb_helpers.rst @@ -136,7 +136,7 @@ enabled:: at Objects/unicodeobject.c:551 #7 0x0000000000440d94 in PyUnicodeUCS2_FromString (u=0x5c2b8d "__lltrace__") at Objects/unicodeobject.c:569 #8 0x0000000000584abd in PyDict_GetItemString (v= - {'Yuck': , '__builtins__': , '__file__': 'Lib/test/crashers/nasty_eq_vs_dict.py', '__package__': None, 'y': , 'dict': {0: 0, 1: 1, 2: 2, 3: 3}, '__cached__': None, '__name__': '__main__', 'z': , '__doc__': None}, key= + {'Yuck': , '__builtins__': , '__file__': 'Lib/test/crashers/nasty_eq_vs_dict.py', '__package__': None, 'y': , 'dict': {0: 0, 1: 1, 2: 2, 3: 3}, '__name__': '__main__', 'z': , '__doc__': None}, key= 0x5c2b8d "__lltrace__") at Objects/dictobject.c:2171 Notice how the dictionary argument to ``PyDict_GetItemString`` is displayed diff --git a/Doc/howto/index.rst b/Doc/howto/index.rst index f350141004c2db..57e2d6e0752447 100644 --- a/Doc/howto/index.rst +++ b/Doc/howto/index.rst @@ -1,3 +1,5 @@ +.. _how-tos: + *************** Python HOWTOs *************** @@ -11,6 +13,7 @@ Python Library Reference. :maxdepth: 1 :hidden: + a-conceptual-overview-of-asyncio.rst cporting.rst curses.rst descriptor.rst @@ -34,10 +37,12 @@ Python Library Reference. mro.rst free-threading-python.rst free-threading-extensions.rst + abi3t-migration.rst remote_debugging.rst General: +* :ref:`a-conceptual-overview-of-asyncio` * :ref:`annotations-howto` * :ref:`argparse-tutorial` * :ref:`descriptorhowto` @@ -57,6 +62,7 @@ Advanced development: * :ref:`freethreading-python-howto` * :ref:`freethreading-extensions-howto` * :ref:`isolating-extensions-howto` +* :ref:`abi3t-migration-howto` * :ref:`python_2.3_mro` * :ref:`socket-howto` * :ref:`timerfd-howto` diff --git a/Doc/howto/instrumentation.rst b/Doc/howto/instrumentation.rst index 6e03ef20a21fa3..8f0b0c41ea4861 100644 --- a/Doc/howto/instrumentation.rst +++ b/Doc/howto/instrumentation.rst @@ -6,9 +6,6 @@ Instrumenting CPython with DTrace and SystemTap =============================================== -:author: David Malcolm -:author: Łukasz Langa - DTrace and SystemTap are monitoring tools, each providing a way to inspect what the processes on a computer system are doing. They both use domain-specific languages allowing a user to write scripts which: @@ -269,6 +266,8 @@ should instead read: (assuming a :ref:`debug build ` of CPython 3.6) +.. _static-markers: + Available static markers ------------------------ @@ -339,6 +338,84 @@ Available static markers .. versionadded:: 3.8 +C Entry Points +^^^^^^^^^^^^^^ + +To simplify triggering of DTrace markers, Python's C API comes with a number +of helper functions that mirror each static marker. On builds of Python without +DTrace enabled, these do nothing. + +In general, it is not necessary to call these yourself, as Python will do +it for you. + +.. list-table:: + :widths: 50 25 25 + :header-rows: 1 + + * * C API Function + * Static Marker + * Notes + * * .. c:function:: void PyDTrace_LINE(const char *arg0, const char *arg1, int arg2) + * :c:func:`!line` + * + * * .. c:function:: void PyDTrace_FUNCTION_ENTRY(const char *arg0, const char *arg1, int arg2) + * :c:func:`!function__entry` + * + * * .. c:function:: void PyDTrace_FUNCTION_RETURN(const char *arg0, const char *arg1, int arg2) + * :c:func:`!function__return` + * + * * .. c:function:: void PyDTrace_GC_START(int arg0) + * :c:func:`!gc__start` + * + * * .. c:function:: void PyDTrace_GC_DONE(Py_ssize_t arg0) + * :c:func:`!gc__done` + * + * * .. c:function:: void PyDTrace_INSTANCE_NEW_START(int arg0) + * :c:func:`!instance__new__start` + * Not used by Python + * * .. c:function:: void PyDTrace_INSTANCE_NEW_DONE(int arg0) + * :c:func:`!instance__new__done` + * Not used by Python + * * .. c:function:: void PyDTrace_INSTANCE_DELETE_START(int arg0) + * :c:func:`!instance__delete__start` + * Not used by Python + * * .. c:function:: void PyDTrace_INSTANCE_DELETE_DONE(int arg0) + * :c:func:`!instance__delete__done` + * Not used by Python + * * .. c:function:: void PyDTrace_IMPORT_FIND_LOAD_START(const char *arg0) + * :c:func:`!import__find__load__start` + * + * * .. c:function:: void PyDTrace_IMPORT_FIND_LOAD_DONE(const char *arg0, int arg1) + * :c:func:`!import__find__load__done` + * + * * .. c:function:: void PyDTrace_AUDIT(const char *arg0, void *arg1) + * :c:func:`!audit` + * + + +C Probing Checks +^^^^^^^^^^^^^^^^ + +.. c:function:: int PyDTrace_LINE_ENABLED(void) +.. c:function:: int PyDTrace_FUNCTION_ENTRY_ENABLED(void) +.. c:function:: int PyDTrace_FUNCTION_RETURN_ENABLED(void) +.. c:function:: int PyDTrace_GC_START_ENABLED(void) +.. c:function:: int PyDTrace_GC_DONE_ENABLED(void) +.. c:function:: int PyDTrace_INSTANCE_NEW_START_ENABLED(void) +.. c:function:: int PyDTrace_INSTANCE_NEW_DONE_ENABLED(void) +.. c:function:: int PyDTrace_INSTANCE_DELETE_START_ENABLED(void) +.. c:function:: int PyDTrace_INSTANCE_DELETE_DONE_ENABLED(void) +.. c:function:: int PyDTrace_IMPORT_FIND_LOAD_START_ENABLED(void) +.. c:function:: int PyDTrace_IMPORT_FIND_LOAD_DONE_ENABLED(void) +.. c:function:: int PyDTrace_AUDIT_ENABLED(void) + + All calls to ``PyDTrace`` functions must be guarded by a call to one + of these functions. This allows Python to minimize performance impact + when probing is disabled. + + On builds without DTrace enabled, these functions do nothing and return + ``0``. + SystemTap Tapsets ----------------- diff --git a/Doc/howto/ipaddress.rst b/Doc/howto/ipaddress.rst index e852db98156fac..646c4c4d9e7ab1 100644 --- a/Doc/howto/ipaddress.rst +++ b/Doc/howto/ipaddress.rst @@ -8,9 +8,6 @@ An introduction to the ipaddress module *************************************** -:author: Peter Moody -:author: Nick Coghlan - .. topic:: Overview This document aims to provide a gentle introduction to the diff --git a/Doc/howto/isolating-extensions.rst b/Doc/howto/isolating-extensions.rst index 7da6dc8a39795e..6092c75f48fdef 100644 --- a/Doc/howto/isolating-extensions.rst +++ b/Doc/howto/isolating-extensions.rst @@ -353,7 +353,7 @@ garbage collection protocol. That is, heap types should: - Have the :c:macro:`Py_TPFLAGS_HAVE_GC` flag. -- Define a traverse function using ``Py_tp_traverse``, which +- Define a traverse function using :c:data:`Py_tp_traverse`, which visits the type (e.g. using ``Py_VISIT(Py_TYPE(self))``). Please refer to the documentation of diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 52537a91df542c..87025814aafb9a 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -4,8 +4,6 @@ Logging Cookbook ================ -:Author: Vinay Sajip - This page contains a number of recipes related to logging, which have been found useful in the past. For links to tutorial and reference information, please see :ref:`cookbook-ref-links`. @@ -229,7 +227,7 @@ messages should not. Here's how you can achieve this:: # tell the handler to use this format console.setFormatter(formatter) # add the handler to the root logger - logging.getLogger('').addHandler(console) + logging.getLogger().addHandler(console) # Now, we can log to the root logger, or any other logger. First the root... logging.info('Jackdaws love my big sphinx of quartz.') @@ -650,7 +648,7 @@ the receiving end. A simple way of doing this is attaching a import logging, logging.handlers - rootLogger = logging.getLogger('') + rootLogger = logging.getLogger() rootLogger.setLevel(logging.DEBUG) socketHandler = logging.handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT) @@ -1549,10 +1547,10 @@ to this (remembering to first import :mod:`concurrent.futures`):: for i in range(10): executor.submit(worker_process, queue, worker_configurer) -Deploying Web applications using Gunicorn and uWSGI +Deploying web applications using Gunicorn and uWSGI ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When deploying Web applications using `Gunicorn `_ or `uWSGI +When deploying web applications using `Gunicorn `_ or `uWSGI `_ (or similar), multiple worker processes are created to handle client requests. In such environments, avoid creating file-based handlers directly in your web application. Instead, use a @@ -1564,9 +1562,6 @@ process. This can be set up using a process management tool such as Supervisor - Using file rotation ------------------- -.. sectionauthor:: Doug Hellmann, Vinay Sajip (changes) -.. (see ) - Sometimes you want to let a log file grow to a certain size, then open a new file and log to that. You may want to keep a certain number of these files, and when that many files have been created, rotate the files so that the number of @@ -3619,7 +3614,6 @@ detailed information. .. code-block:: python3 - import datetime import logging import random import sys @@ -3854,7 +3848,7 @@ Logging to syslog with RFC5424 support Although :rfc:`5424` dates from 2009, most syslog servers are configured by default to use the older :rfc:`3164`, which hails from 2001. When ``logging`` was added to Python in 2003, it supported the earlier (and only existing) protocol at the time. Since -RFC5424 came out, as there has not been widespread deployment of it in syslog +RFC 5424 came out, as there has not been widespread deployment of it in syslog servers, the :class:`~logging.handlers.SysLogHandler` functionality has not been updated. @@ -3862,7 +3856,7 @@ RFC 5424 contains some useful features such as support for structured data, and need to be able to log to a syslog server with support for it, you can do so with a subclassed handler which looks something like this:: - import datetime + import datetime as dt import logging.handlers import re import socket @@ -3880,8 +3874,8 @@ subclassed handler which looks something like this:: def format(self, record): version = 1 - asctime = datetime.datetime.fromtimestamp(record.created).isoformat() - m = self.tz_offset.match(time.strftime('%z')) + asctime = dt.datetime.fromtimestamp(record.created).isoformat() + m = self.tz_offset.prefixmatch(time.strftime('%z')) has_offset = False if m and time.timezone: hrs, mins = m.groups() diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst index b7225ff1c2cbfc..c8ce0df9e937f8 100644 --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -4,8 +4,6 @@ Logging HOWTO ============= -:Author: Vinay Sajip - .. _logging-basic-tutorial: .. currentmodule:: logging @@ -28,7 +26,7 @@ When to use logging ^^^^^^^^^^^^^^^^^^^ You can access logging functionality by creating a logger via ``logger = -getLogger(__name__)``, and then calling the logger's :meth:`~Logger.debug`, +logging.getLogger(__name__)``, and then calling the logger's :meth:`~Logger.debug`, :meth:`~Logger.info`, :meth:`~Logger.warning`, :meth:`~Logger.error` and :meth:`~Logger.critical` methods. To determine when to use logging, and to see which logger methods to use when, see the table below. It states, for each of a diff --git a/Doc/howto/perf_profiling.rst b/Doc/howto/perf_profiling.rst index fc4772bbccab57..5565f99b244f11 100644 --- a/Doc/howto/perf_profiling.rst +++ b/Doc/howto/perf_profiling.rst @@ -6,8 +6,6 @@ Python support for the ``perf map`` compatible profilers ======================================================== -:author: Pablo Galindo - `The Linux perf profiler `_ and `samply `_ are powerful tools that allow you to profile and obtain information about the performance of your application. @@ -217,8 +215,9 @@ Example, using the :mod:`sys` APIs in file :file:`example.py`: How to obtain the best results ------------------------------ -For best results, Python should be compiled with -``CFLAGS="-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"`` as this allows +For best results, keep frame pointers enabled. On supported GCC-compatible +toolchains, CPython builds itself with ``-fno-omit-frame-pointer`` and similar +flags (see :option:`--without-frame-pointers` for details). These flags allow profilers to unwind using only the frame pointer and not on DWARF debug information. This is because as the code that is interposed to allow ``perf`` support is dynamically generated it doesn't have any DWARF debugging information diff --git a/Doc/howto/pyporting.rst b/Doc/howto/pyporting.rst index 9f73c811cfcbc0..f19f6006dbac9e 100644 --- a/Doc/howto/pyporting.rst +++ b/Doc/howto/pyporting.rst @@ -6,8 +6,6 @@ How to port Python 2 Code to Python 3 ************************************* -:author: Brett Cannon - Python 2 reached its official end-of-life at the start of 2020. This means that no new bug reports, fixes, or changes will be made to Python 2 - it's no longer supported: see :pep:`373` and diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst index 7486a378dbb06f..ecdb35136a99f3 100644 --- a/Doc/howto/regex.rst +++ b/Doc/howto/regex.rst @@ -1,11 +1,9 @@ .. _regex-howto: **************************** - Regular Expression HOWTO + Regular expression HOWTO **************************** -:Author: A.M. Kuchling - .. TODO: Document lookbehind assertions Better way of displaying a RE, a string, and what it matches @@ -47,7 +45,7 @@ Python code to do the processing; while Python code will be slower than an elaborate regular expression, it will also probably be more understandable. -Simple Patterns +Simple patterns =============== We'll start by learning about the simplest possible regular expressions. Since @@ -59,7 +57,7 @@ expressions (deterministic and non-deterministic finite automata), you can refer to almost any textbook on writing compilers. -Matching Characters +Matching characters ------------------- Most letters and characters will simply match themselves. For example, the @@ -159,7 +157,7 @@ match even a newline. ``.`` is often used where you want to match "any character". -Repeating Things +Repeating things ---------------- Being able to match varying sets of characters is the first thing regular @@ -210,7 +208,7 @@ this RE against the string ``'abcbd'``. | | | ``[bcd]*`` is only matching | | | | ``bc``. | +------+-----------+---------------------------------+ -| 6 | ``abcb`` | Try ``b`` again. This time | +| 7 | ``abcb`` | Try ``b`` again. This time | | | | the character at the | | | | current position is ``'b'``, so | | | | it succeeds. | @@ -255,7 +253,7 @@ is equivalent to ``+``, and ``{0,1}`` is the same as ``?``. It's better to use to read. -Using Regular Expressions +Using regular expressions ========================= Now that we've looked at some simple regular expressions, how do we actually use @@ -264,7 +262,7 @@ expression engine, allowing you to compile REs into objects and then perform matches with them. -Compiling Regular Expressions +Compiling regular expressions ----------------------------- Regular expressions are compiled into pattern objects, which have @@ -295,7 +293,7 @@ disadvantage which is the topic of the next section. .. _the-backslash-plague: -The Backslash Plague +The backslash plague -------------------- As stated earlier, regular expressions use the backslash character (``'\'``) to @@ -335,7 +333,7 @@ expressions will often be written in Python code using this raw string notation. In addition, special escape sequences that are valid in regular expressions, but not valid as Python string literals, now result in a -:exc:`DeprecationWarning` and will eventually become a :exc:`SyntaxError`, +:exc:`SyntaxWarning` and will eventually become a :exc:`SyntaxError`, which means the sequences will be invalid if raw string notation or escaping the backslashes isn't used. @@ -351,7 +349,7 @@ the backslashes isn't used. +-------------------+------------------+ -Performing Matches +Performing matches ------------------ Once you have an object representing a compiled regular expression, what do you @@ -362,20 +360,21 @@ for a complete listing. +------------------+-----------------------------------------------+ | Method/Attribute | Purpose | +==================+===============================================+ -| ``match()`` | Determine if the RE matches at the beginning | -| | of the string. | -+------------------+-----------------------------------------------+ | ``search()`` | Scan through a string, looking for any | | | location where this RE matches. | +------------------+-----------------------------------------------+ +| ``prefixmatch()``| Determine if the RE matches at the beginning | +| | of the string. Previously named :ref:`match() | +| | `. | ++------------------+-----------------------------------------------+ | ``findall()`` | Find all substrings where the RE matches, and | -| | returns them as a list. | +| | return them as a list. | +------------------+-----------------------------------------------+ | ``finditer()`` | Find all substrings where the RE matches, and | -| | returns them as an :term:`iterator`. | +| | return them as an :term:`iterator`. | +------------------+-----------------------------------------------+ -:meth:`~re.Pattern.match` and :meth:`~re.Pattern.search` return ``None`` if no match can be found. If +:meth:`~re.Pattern.search` and :meth:`~re.Pattern.prefixmatch` return ``None`` if no match can be found. If they're successful, a :ref:`match object ` instance is returned, containing information about the match: where it starts and ends, the substring it matched, and more. @@ -393,19 +392,19 @@ Python interpreter, import the :mod:`re` module, and compile a RE:: Now, you can try matching various strings against the RE ``[a-z]+``. An empty string shouldn't match at all, since ``+`` means 'one or more repetitions'. -:meth:`~re.Pattern.match` should return ``None`` in this case, which will cause the +:meth:`~re.Pattern.search` should return ``None`` in this case, which will cause the interpreter to print no output. You can explicitly print the result of -:meth:`!match` to make this clear. :: +:meth:`!search` to make this clear. :: - >>> p.match("") - >>> print(p.match("")) + >>> p.search("") + >>> print(p.search("")) None Now, let's try it on a string that it should match, such as ``tempo``. In this -case, :meth:`~re.Pattern.match` will return a :ref:`match object `, so you +case, :meth:`~re.Pattern.search` will return a :ref:`match object `, so you should store the result in a variable for later use. :: - >>> m = p.match('tempo') + >>> m = p.search('tempo') >>> m @@ -437,27 +436,28 @@ Trying these methods will soon clarify their meaning:: :meth:`~re.Match.group` returns the substring that was matched by the RE. :meth:`~re.Match.start` and :meth:`~re.Match.end` return the starting and ending index of the match. :meth:`~re.Match.span` -returns both start and end indexes in a single tuple. Since the :meth:`~re.Pattern.match` -method only checks if the RE matches at the start of a string, :meth:`!start` -will always be zero. However, the :meth:`~re.Pattern.search` method of patterns -scans through the string, so the match may not start at zero in that -case. :: +returns both start and end indexes in a single tuple. +The :meth:`~re.Pattern.search` method of patterns +scans through the string, so the match may not start at zero. +However, the :meth:`~re.Pattern.prefixmatch` +method only checks if the RE matches at the start of a string, so :meth:`!start` +will always be zero in that case. :: - >>> print(p.match('::: message')) - None >>> m = p.search('::: message'); print(m) >>> m.group() 'message' >>> m.span() (4, 11) + >>> print(p.prefixmatch('::: message')) + None In actual programs, the most common style is to store the :ref:`match object ` in a variable, and then check if it was ``None``. This usually looks like:: p = re.compile( ... ) - m = p.match( 'string goes here' ) + m = p.search( 'string goes here' ) if m: print('Match found: ', m.group()) else: @@ -473,7 +473,7 @@ Two pattern methods return all of the matches for a pattern. The ``r`` prefix, making the literal a raw string literal, is needed in this example because escape sequences in a normal "cooked" string literal that are not recognized by Python, as opposed to regular expressions, now result in a -:exc:`DeprecationWarning` and will eventually become a :exc:`SyntaxError`. See +:exc:`SyntaxWarning` and will eventually become a :exc:`SyntaxError`. See :ref:`the-backslash-plague`. :meth:`~re.Pattern.findall` has to create the entire list before it can be returned as the @@ -491,19 +491,19 @@ result. The :meth:`~re.Pattern.finditer` method returns a sequence of (29, 31) -Module-Level Functions +Module-level functions ---------------------- You don't have to create a pattern object and call its methods; the -:mod:`re` module also provides top-level functions called :func:`~re.match`, -:func:`~re.search`, :func:`~re.findall`, :func:`~re.sub`, and so forth. These functions +:mod:`re` module also provides top-level functions called :func:`~re.search`, +:func:`~re.prefixmatch`, :func:`~re.findall`, :func:`~re.sub`, and so forth. These functions take the same arguments as the corresponding pattern method with the RE string added as the first argument, and still return either ``None`` or a :ref:`match object ` instance. :: - >>> print(re.match(r'From\s+', 'Fromage amk')) + >>> print(re.prefixmatch(r'From\s+', 'Fromage amk')) None - >>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998') #doctest: +ELLIPSIS + >>> re.prefixmatch(r'From\s+', 'From amk Thu May 14 19:12:10 1998') #doctest: +ELLIPSIS Under the hood, these functions simply create a pattern object for you @@ -518,7 +518,7 @@ Outside of loops, there's not much difference thanks to the internal cache. -Compilation Flags +Compilation flags ----------------- .. currentmodule:: re @@ -642,7 +642,7 @@ of each one. whitespace is in a character class or preceded by an unescaped backslash; this lets you organize and indent the RE more clearly. This flag also lets you put comments within a RE that will be ignored by the engine; comments are marked by - a ``'#'`` that's neither in a character class or preceded by an unescaped + a ``'#'`` that's neither in a character class nor preceded by an unescaped backslash. For example, here's a RE that uses :const:`re.VERBOSE`; see how much easier it @@ -669,7 +669,7 @@ of each one. to understand than the version using :const:`re.VERBOSE`. -More Pattern Power +More pattern power ================== So far we've only covered a part of the features of regular expressions. In @@ -679,7 +679,7 @@ retrieve portions of the text that was matched. .. _more-metacharacters: -More Metacharacters +More metacharacters ------------------- There are some metacharacters that we haven't covered yet. Most of them will be @@ -812,7 +812,7 @@ of a group with a quantifier, such as ``*``, ``+``, ``?``, or ``ab``. :: >>> p = re.compile('(ab)*') - >>> print(p.match('ababababab').span()) + >>> print(p.search('ababababab').span()) (0, 10) Groups indicated with ``'('``, ``')'`` also capture the starting and ending @@ -825,7 +825,7 @@ argument. Later we'll see how to express groups that don't capture the span of text that they match. :: >>> p = re.compile('(a)b') - >>> m = p.match('ab') + >>> m = p.search('ab') >>> m.group() 'ab' >>> m.group(0) @@ -836,7 +836,7 @@ to determine the number, just count the opening parenthesis characters, going from left to right. :: >>> p = re.compile('(a(b)c)d') - >>> m = p.match('abcd') + >>> m = p.search('abcd') >>> m.group(0) 'abcd' >>> m.group(1) @@ -875,7 +875,7 @@ Backreferences like this aren't often useful for just searching through a string find out that they're *very* useful when performing string substitutions. -Non-capturing and Named Groups +Non-capturing and named groups ------------------------------ Elaborate REs may use many groups, both to capture substrings of interest, and @@ -912,10 +912,10 @@ but aren't interested in retrieving the group's contents. You can make this fact explicit by using a non-capturing group: ``(?:...)``, where you can replace the ``...`` with any other regular expression. :: - >>> m = re.match("([abc])+", "abc") + >>> m = re.search("([abc])+", "abc") >>> m.groups() ('c',) - >>> m = re.match("(?:[abc])+", "abc") + >>> m = re.search("(?:[abc])+", "abc") >>> m.groups() () @@ -949,7 +949,7 @@ given numbers, so you can retrieve information about a group in two ways:: Additionally, you can retrieve named groups as a dictionary with :meth:`~re.Match.groupdict`:: - >>> m = re.match(r'(?P\w+) (?P\w+)', 'Jane Doe') + >>> m = re.search(r'(?P\w+) (?P\w+)', 'Jane Doe') >>> m.groupdict() {'first': 'Jane', 'last': 'Doe'} @@ -979,7 +979,7 @@ current point. The regular expression for finding doubled words, 'the the' -Lookahead Assertions +Lookahead assertions -------------------- Another zero-width assertion is the lookahead assertion. Lookahead assertions @@ -1061,7 +1061,7 @@ end in either ``bat`` or ``exe``: ``.*[.](?!bat$|exe$)[^.]*$`` -Modifying Strings +Modifying strings ================= Up to this point, we've simply performed searches against a static string. @@ -1083,7 +1083,7 @@ using the following pattern methods: +------------------+-----------------------------------------------+ -Splitting Strings +Splitting strings ----------------- The :meth:`~re.Pattern.split` method of a pattern splits a string apart @@ -1137,7 +1137,7 @@ argument, but is otherwise the same. :: ['Words', 'words, words.'] -Search and Replace +Search and replace ------------------ Another common task is to find all the matches for a pattern, and replace them @@ -1236,7 +1236,7 @@ pattern object as the first parameter, or use embedded modifiers in the pattern string, e.g. ``sub("(?i)b+", "x", "bbbb BBBB")`` returns ``'x x'``. -Common Problems +Common problems =============== Regular expressions are a powerful tool for some applications, but in some ways @@ -1244,7 +1244,7 @@ their behaviour isn't intuitive and at times they don't behave the way you may expect them to. This section will point out some of the most common pitfalls. -Use String Methods +Use string methods ------------------ Sometimes using the :mod:`re` module is a mistake. If you're matching a fixed @@ -1274,21 +1274,26 @@ In short, before turning to the :mod:`re` module, consider whether your problem can be solved with a faster and simpler string method. -match() versus search() ------------------------ +.. _match-versus-search: + +prefixmatch() (aka match) versus search() +----------------------------------------- -The :func:`~re.match` function only checks if the RE matches at the beginning of the -string while :func:`~re.search` will scan forward through the string for a match. -It's important to keep this distinction in mind. Remember, :func:`!match` will -only report a successful match which will start at 0; if the match wouldn't -start at zero, :func:`!match` will *not* report it. :: +:func:`~re.prefixmatch` was added in Python 3.15 as the :ref:`preferred name +` for :func:`~re.match`. Before this, it was only known +as :func:`!match` and the distinction with :func:`~re.search` was often +misunderstood. - >>> print(re.match('super', 'superstition').span()) +:func:`!prefixmatch` aka :func:`!match` only checks if the RE matches at the +beginning of the string while :func:`!search` scans forward through the +string for a match. :: + + >>> print(re.prefixmatch('super', 'superstition').span()) (0, 5) - >>> print(re.match('super', 'insuperable')) + >>> print(re.prefixmatch('super', 'insuperable')) None -On the other hand, :func:`~re.search` will scan forward through the string, +On the other hand, :func:`~re.search` scans forward through the string, reporting the first match it finds. :: >>> print(re.search('super', 'superstition').span()) @@ -1296,21 +1301,11 @@ reporting the first match it finds. :: >>> print(re.search('super', 'insuperable').span()) (2, 7) -Sometimes you'll be tempted to keep using :func:`re.match`, and just add ``.*`` -to the front of your RE. Resist this temptation and use :func:`re.search` -instead. The regular expression compiler does some analysis of REs in order to -speed up the process of looking for a match. One such analysis figures out what -the first character of a match must be; for example, a pattern starting with -``Crow`` must match starting with a ``'C'``. The analysis lets the engine -quickly scan through the string looking for the starting character, only trying -the full match if a ``'C'`` is found. - -Adding ``.*`` defeats this optimization, requiring scanning to the end of the -string and then backtracking to find a match for the rest of the RE. Use -:func:`re.search` instead. +This distinction is important to remember when using the old :func:`~re.match` +name in code requiring compatibility with older Python versions. -Greedy versus Non-Greedy +Greedy versus non-greedy ------------------------ When repeating a regular expression, as in ``a*``, the resulting action is to @@ -1322,9 +1317,9 @@ doesn't work because of the greedy nature of ``.*``. :: >>> s = 'Title' >>> len(s) 32 - >>> print(re.match('<.*>', s).span()) + >>> print(re.prefixmatch('<.*>', s).span()) (0, 32) - >>> print(re.match('<.*>', s).group()) + >>> print(re.prefixmatch('<.*>', s).group()) Title The RE matches the ``'<'`` in ``''``, and the ``.*`` consumes the rest of @@ -1340,7 +1335,7 @@ example, the ``'>'`` is tried immediately after the first ``'<'`` matches, and when it fails, the engine advances a character at a time, retrying the ``'>'`` at every step. This produces just the right result:: - >>> print(re.match('<.*?>', s).group()) + >>> print(re.prefixmatch('<.*?>', s).group()) (Note that parsing HTML or XML with regular expressions is painful. @@ -1388,9 +1383,9 @@ Feedback ======== Regular expressions are a complicated topic. Did this document help you -understand them? Were there parts that were unclear, or Problems you +understand them? Were there parts that were unclear, or problems you encountered that weren't covered here? If so, please send suggestions for -improvements to the author. +improvements to the :ref:`issue tracker `. The most complete book on regular expressions is almost certainly Jeffrey Friedl's Mastering Regular Expressions, published by O'Reilly. Unfortunately, diff --git a/Doc/howto/remote_debugging.rst b/Doc/howto/remote_debugging.rst index 3adb6ad03e5445..1d5cf24d062843 100644 --- a/Doc/howto/remote_debugging.rst +++ b/Doc/howto/remote_debugging.rst @@ -3,6 +3,88 @@ Remote debugging attachment protocol ==================================== +This protocol enables external tools to attach to a running CPython process and +execute Python code remotely. + +Most platforms require elevated privileges to attach to another Python process. + +Disabling remote debugging +-------------------------- + +To disable remote debugging support, use any of the following: + +* Set the :envvar:`PYTHON_DISABLE_REMOTE_DEBUG` environment variable to ``1`` before + starting the interpreter. +* Use the :option:`-X disable_remote_debug` command-line option. +* Compile Python with the :option:`--without-remote-debug` build flag. + +.. _permission-requirements: + +Permission requirements +======================= + +Attaching to a running Python process for remote debugging requires elevated +privileges on most platforms. The specific requirements and troubleshooting +steps depend on your operating system: + +.. rubric:: Linux + +The tracer process must have the ``CAP_SYS_PTRACE`` capability or equivalent +privileges. You can only trace processes you own and can signal. Tracing may +fail if the process is already being traced, or if it is running with +set-user-ID or set-group-ID. Security modules like Yama may further restrict +tracing. + +To temporarily relax ptrace restrictions (until reboot), run: + + ``echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope`` + +.. note:: + + Disabling ``ptrace_scope`` reduces system hardening and should only be done + in trusted environments. + +If running inside a container, use ``--cap-add=SYS_PTRACE`` or +``--privileged``, and run as root if needed. + +Try re-running the command with elevated privileges: + + ``sudo -E !!`` + + +.. rubric:: macOS + +To attach to another process, you typically need to run your debugging tool +with elevated privileges. This can be done by using ``sudo`` or running as +root. + +Even when attaching to processes you own, macOS may block debugging unless +the debugger is run with root privileges due to system security restrictions. + + +.. rubric:: Windows + +To attach to another process, you usually need to run your debugging tool +with administrative privileges. Start the command prompt or terminal as +Administrator. + +Some processes may still be inaccessible even with Administrator rights, +unless you have the ``SeDebugPrivilege`` privilege enabled. + +To resolve file or folder access issues, adjust the security permissions: + + 1. Right-click the file or folder and select **Properties**. + 2. Go to the **Security** tab to view users and groups with access. + 3. Click **Edit** to modify permissions. + 4. Select your user account. + 5. In **Permissions**, check **Read** or **Full control** as needed. + 6. Click **Apply**, then **OK** to confirm. + + +.. note:: + + Ensure you've satisfied all :ref:`permission-requirements` before proceeding. + This section describes the low-level protocol that enables external tools to inject and execute a Python script within a running CPython process. @@ -374,13 +456,13 @@ To locate a thread: reliable thread to target. 3. Optionally, use the offset ``interpreter_state.threads_head`` to iterate -through the linked list of all thread states. Each ``PyThreadState`` structure -contains a ``native_thread_id`` field, which may be compared to a target thread -ID to find a specific thread. + through the linked list of all thread states. Each ``PyThreadState`` + structure contains a ``native_thread_id`` field, which may be compared to + a target thread ID to find a specific thread. -1. Once a valid ``PyThreadState`` has been found, its address can be used in -later steps of the protocol, such as writing debugger control fields and -scheduling execution. +4. Once a valid ``PyThreadState`` has been found, its address can be used in + later steps of the protocol, such as writing debugger control fields and + scheduling execution. The following is an example implementation that locates the main thread state:: @@ -454,15 +536,15 @@ its fields are defined by the ``_Py_DebugOffsets`` structure and include the following: - ``debugger_script_path``: A fixed-size buffer that holds the full path to a - Python source file (``.py``). This file must be accessible and readable by - the target process when execution is triggered. + Python source file (``.py``). This file must be accessible and readable by + the target process when execution is triggered. - ``debugger_pending_call``: An integer flag. Setting this to ``1`` tells the - interpreter that a script is ready to be executed. + interpreter that a script is ready to be executed. - ``eval_breaker``: A field checked by the interpreter during execution. - Setting bit 5 (``_PY_EVAL_PLEASE_STOP_BIT``, value ``1U << 5``) in this - field causes the interpreter to pause and check for debugger activity. + Setting bit 5 (``_PY_EVAL_PLEASE_STOP_BIT``, value ``1U << 5``) in this + field causes the interpreter to pause and check for debugger activity. To complete the injection, the debugger must perform the following steps: @@ -543,3 +625,57 @@ To inject and execute a Python script in a remote process: 7. Resume the process (if suspended). The script will execute at the next safe evaluation point. +.. _remote-debugging-threat-model: + +Security and threat model +========================= + +The remote debugging protocol relies on the same operating system primitives +used by native debuggers such as GDB and LLDB. Attaching to a process +requires the **same privileges** that those debuggers require, for example +``ptrace`` / Yama LSM on Linux, ``task_for_pid`` on macOS, and +``SeDebugPrivilege`` on Windows. Python does not introduce any new privilege +escalation path; if an attacker already possesses the permissions needed to +attach to a process, they could equally use GDB to read memory or inject +code. + +The following principles define what is, and is not, considered a security +vulnerability in this feature: + +Attaching requires OS-level privileges + On every supported platform the operating system gates cross-process + memory access behind privilege checks (``CAP_SYS_PTRACE``, root, or + administrator rights). A report that demonstrates an issue only after + these privileges have already been obtained is **not** a vulnerability in + CPython, since the OS security boundary was already crossed. + +Crashes or memory errors when reading a compromised process are not vulnerabilities + A tool that reads internal interpreter state from a target process must + trust that memory to be well-formed. If the target process has been + corrupted or is controlled by an attacker, the debugger or profiler may + crash, produce garbage output, or behave unpredictably. This is the same + risk accepted by every ``ptrace``-based debugger. Bugs in this category + (buffer overflows, segmentation faults, or undefined behaviour triggered + by reading corrupted state) are **not** treated as security issues, though + fixes that improve robustness are welcome. + +Vulnerabilities in the target process are not in scope + If the Python process being debugged has already been compromised, the + attacker already controls execution in that process. Demonstrating further + impact from that starting point does not constitute a vulnerability in the + remote debugging protocol. + +When to use ``PYTHON_DISABLE_REMOTE_DEBUG`` +------------------------------------------- + +The environment variable :envvar:`PYTHON_DISABLE_REMOTE_DEBUG` (and the +equivalent :option:`-X disable_remote_debug` flag) allows operators to disable +the in-process side of the protocol as a **defence-in-depth** measure. This +may be useful in hardened or sandboxed deployment environments where no +debugging or profiling of the process is expected and reducing attack surface +is a priority, even though the OS-level privilege checks already prevent +unprivileged access. + +Setting this variable does **not** affect other OS-level debugging interfaces +(``ptrace``, ``/proc``, ``task_for_pid``, etc.), which remain available +according to their own permission models. diff --git a/Doc/howto/sockets.rst b/Doc/howto/sockets.rst index cbc49d15a0771b..b17ab3f4391dad 100644 --- a/Doc/howto/sockets.rst +++ b/Doc/howto/sockets.rst @@ -4,9 +4,6 @@ Socket Programming HOWTO **************************** -:Author: Gordon McMillan - - .. topic:: Abstract Sockets are used nearly everywhere, but are one of the most severely diff --git a/Doc/howto/sorting.rst b/Doc/howto/sorting.rst index 70c34cde8a0659..b511aa4e59fe1e 100644 --- a/Doc/howto/sorting.rst +++ b/Doc/howto/sorting.rst @@ -3,9 +3,6 @@ Sorting Techniques ****************** -:Author: Andrew Dalke and Raymond Hettinger - - Python lists have a built-in :meth:`list.sort` method that modifies the list in-place. There is also a :func:`sorted` built-in function that builds a new sorted list from an iterable. @@ -375,7 +372,7 @@ Odds and Ends :meth:`~object.__lt__` is not implemented (see :func:`object.__lt__` for details on the mechanics). To avoid surprises, :pep:`8` recommends that all six comparison methods be implemented. - The :func:`~functools.total_ordering` decorator is provided to make that + The :deco:`~functools.total_ordering` decorator is provided to make that task easier. * Key functions need not depend directly on the objects being sorted. A key diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst index 254fe729355353..cbd53d5b619f5a 100644 --- a/Doc/howto/unicode.rst +++ b/Doc/howto/unicode.rst @@ -4,8 +4,6 @@ Unicode HOWTO ***************** -:Release: 1.12 - This HOWTO discusses Python's support for the Unicode specification for representing textual data, and explains various problems that people commonly encounter when trying to work with Unicode. @@ -352,6 +350,8 @@ If you don't include such a comment, the default encoding used will be UTF-8 as already mentioned. See also :pep:`263` for more information. +.. _unicode-properties: + Unicode Properties ------------------ diff --git a/Doc/howto/urllib2.rst b/Doc/howto/urllib2.rst index d79d1abe8d0577..e4f218f088ba89 100644 --- a/Doc/howto/urllib2.rst +++ b/Doc/howto/urllib2.rst @@ -4,9 +4,6 @@ HOWTO Fetch Internet Resources Using The urllib Package *********************************************************** -:Author: `Michael Foord `_ - - Introduction ============ @@ -15,7 +12,7 @@ Introduction You may also find useful the following article on fetching web resources with Python: - * `Basic Authentication `_ + * `Basic Authentication `__ A tutorial on *Basic Authentication*, with examples in Python. diff --git a/Doc/improve-page-nojs.rst b/Doc/improve-page-nojs.rst new file mode 100644 index 00000000000000..91b3a88b95d38b --- /dev/null +++ b/Doc/improve-page-nojs.rst @@ -0,0 +1,29 @@ +:orphan: + +**************************** +Improve a documentation page +**************************** + +.. This is the no-javascript version of this page. The one most people + will see (with JavaScript enabled) is improve-page.rst. If you edit + this page, please also edit that one, and vice versa. + +.. only:: html and not epub + +We are always interested to hear ideas about improvements to the documentation. + +.. only:: translation + + If the bug or suggested improvement concerns the translation of this + documentation, open an issue or edit the page in + `translation's repository `_ instead. + +You have a few ways to ask questions or suggest changes: + +- You can start a discussion about the page on the Python discussion forum. + This link will start a topic in the Documentation category: + `New Documentation topic `_. + +- You can open an issue on the Python GitHub issue tracker. This link will + create a new issue with the "docs" label: + `New docs issue `_. diff --git a/Doc/improve-page.rst b/Doc/improve-page.rst new file mode 100644 index 00000000000000..dc89fcb22fbb59 --- /dev/null +++ b/Doc/improve-page.rst @@ -0,0 +1,65 @@ +:orphan: + +**************************** +Improve a documentation page +**************************** + +.. This is the JavaScript-enabled version of this page. Another version + (for those with JavaScript disabled) is improve-page-nojs.rst. If you + edit this page, please also edit that one, and vice versa. + +.. only:: html and not epub + + .. raw:: html + + + +We are always interested to hear ideas about improvements to the documentation. + +You were reading "PAGETITLE" at ``_. The source for that page is on +`GitHub `_. + +.. only:: translation + + If the bug or suggested improvement concerns the translation of this + documentation, open an issue or edit the page in + `translation's repository `_ instead. + +You have a few ways to ask questions or suggest changes: + +- You can start a discussion about the page on the Python discussion forum. + This link will start a pre-populated topic: + `Question about page "PAGETITLE" `_. + +- You can open an issue on the Python GitHub issue tracker. This link will + create a new pre-populated issue: + `Docs: problem with page "PAGETITLE" `_. + +- You can `edit the page on GitHub `_ + to open a pull request and begin the contribution process. diff --git a/Doc/includes/capi-extension/spammodule-01.c b/Doc/includes/capi-extension/spammodule-01.c new file mode 100644 index 00000000000000..8ddb416e7d6cd0 --- /dev/null +++ b/Doc/includes/capi-extension/spammodule-01.c @@ -0,0 +1,58 @@ +/* This file needs to be kept in sync with the tutorial + * at Doc/extending/first-extension-module.rst + */ + +/// Includes + +#include +#include // for system() + +/// Implementation of spam.system + +static PyObject * +spam_system(PyObject *self, PyObject *arg) +{ + const char *command = PyUnicode_AsUTF8AndSize(arg, NULL); + if (command == NULL) { + return NULL; + } + int status = system(command); + PyObject *result = PyLong_FromLong(status); + return result; +} + +/// Module method table + +static PyMethodDef spam_methods[] = { + { + .ml_name="system", + .ml_meth=spam_system, + .ml_flags=METH_O, + .ml_doc="Execute a shell command.", + }, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +/// Module slot table + +PyABIInfo_VAR(abi_info); + +static PySlot spam_slots[] = { + PySlot_STATIC_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_name, "spam"), + PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"), + PySlot_STATIC_DATA(Py_mod_methods, spam_methods), + PySlot_END +}; + +/// Export hook prototype + +PyMODEXPORT_FUNC PyModExport_spam(void); + +/// Module export hook + +PyMODEXPORT_FUNC +PyModExport_spam(void) +{ + return spam_slots; +} diff --git a/Doc/includes/diff.py b/Doc/includes/diff.py index 001619f5f83fc0..bc4bd58ff3e3f1 100644 --- a/Doc/includes/diff.py +++ b/Doc/includes/diff.py @@ -1,4 +1,4 @@ -""" Command line interface to difflib.py providing diffs in four formats: +""" Command-line interface to difflib.py providing diffs in four formats: * ndiff: lists every line and highlights interline changes. * context: highlights clusters of changes in a before/after format. @@ -8,11 +8,11 @@ """ import sys, os, difflib, argparse -from datetime import datetime, timezone +import datetime as dt def file_mtime(path): - t = datetime.fromtimestamp(os.stat(path).st_mtime, - timezone.utc) + t = dt.datetime.fromtimestamp(os.stat(path).st_mtime, + dt.timezone.utc) return t.astimezone().isoformat() def main(): diff --git a/Doc/includes/optional-module.rst b/Doc/includes/optional-module.rst new file mode 100644 index 00000000000000..262e73f2eaa09f --- /dev/null +++ b/Doc/includes/optional-module.rst @@ -0,0 +1,9 @@ +This is an :term:`optional module`. +If it is missing from your copy of CPython, +look for documentation from your distributor (that is, +whoever provided Python to you). +If you are the distributor, see :ref:`optional-module-requirements`. + +.. Similar notes appear in the docs of the modules: + - zipfile + - tarfile diff --git a/Doc/includes/tzinfo_examples.py b/Doc/includes/tzinfo_examples.py index 1fa6e615e46a76..762b1b62fc871d 100644 --- a/Doc/includes/tzinfo_examples.py +++ b/Doc/includes/tzinfo_examples.py @@ -1,68 +1,70 @@ -from datetime import tzinfo, timedelta, datetime - -ZERO = timedelta(0) -HOUR = timedelta(hours=1) -SECOND = timedelta(seconds=1) +import datetime as dt # A class capturing the platform's idea of local time. # (May result in wrong values on historical times in # timezones where UTC offset and/or the DST rules had # changed in the past.) -import time as _time +import time + +ZERO = dt.timedelta(0) +HOUR = dt.timedelta(hours=1) +SECOND = dt.timedelta(seconds=1) -STDOFFSET = timedelta(seconds = -_time.timezone) -if _time.daylight: - DSTOFFSET = timedelta(seconds = -_time.altzone) +STDOFFSET = dt.timedelta(seconds=-time.timezone) +if time.daylight: + DSTOFFSET = dt.timedelta(seconds=-time.altzone) else: DSTOFFSET = STDOFFSET DSTDIFF = DSTOFFSET - STDOFFSET -class LocalTimezone(tzinfo): - def fromutc(self, dt): - assert dt.tzinfo is self - stamp = (dt - datetime(1970, 1, 1, tzinfo=self)) // SECOND - args = _time.localtime(stamp)[:6] +class LocalTimezone(dt.tzinfo): + + def fromutc(self, when): + assert when.tzinfo is self + stamp = (when - dt.datetime(1970, 1, 1, tzinfo=self)) // SECOND + args = time.localtime(stamp)[:6] dst_diff = DSTDIFF // SECOND # Detect fold - fold = (args == _time.localtime(stamp - dst_diff)) - return datetime(*args, microsecond=dt.microsecond, - tzinfo=self, fold=fold) + fold = (args == time.localtime(stamp - dst_diff)) + return dt.datetime(*args, microsecond=when.microsecond, + tzinfo=self, fold=fold) - def utcoffset(self, dt): - if self._isdst(dt): + def utcoffset(self, when): + if self._isdst(when): return DSTOFFSET else: return STDOFFSET - def dst(self, dt): - if self._isdst(dt): + def dst(self, when): + if self._isdst(when): return DSTDIFF else: return ZERO - def tzname(self, dt): - return _time.tzname[self._isdst(dt)] + def tzname(self, when): + return time.tzname[self._isdst(when)] - def _isdst(self, dt): - tt = (dt.year, dt.month, dt.day, - dt.hour, dt.minute, dt.second, - dt.weekday(), 0, 0) - stamp = _time.mktime(tt) - tt = _time.localtime(stamp) + def _isdst(self, when): + tt = (when.year, when.month, when.day, + when.hour, when.minute, when.second, + when.weekday(), 0, 0) + stamp = time.mktime(tt) + tt = time.localtime(stamp) return tt.tm_isdst > 0 + Local = LocalTimezone() # A complete implementation of current DST rules for major US time zones. -def first_sunday_on_or_after(dt): - days_to_go = 6 - dt.weekday() +def first_sunday_on_or_after(when): + days_to_go = 6 - when.weekday() if days_to_go: - dt += timedelta(days_to_go) - return dt + when += dt.timedelta(days_to_go) + return when # US DST Rules @@ -75,21 +77,22 @@ def first_sunday_on_or_after(dt): # # In the US, since 2007, DST starts at 2am (standard time) on the second # Sunday in March, which is the first Sunday on or after Mar 8. -DSTSTART_2007 = datetime(1, 3, 8, 2) +DSTSTART_2007 = dt.datetime(1, 3, 8, 2) # and ends at 2am (DST time) on the first Sunday of Nov. -DSTEND_2007 = datetime(1, 11, 1, 2) +DSTEND_2007 = dt.datetime(1, 11, 1, 2) # From 1987 to 2006, DST used to start at 2am (standard time) on the first # Sunday in April and to end at 2am (DST time) on the last # Sunday of October, which is the first Sunday on or after Oct 25. -DSTSTART_1987_2006 = datetime(1, 4, 1, 2) -DSTEND_1987_2006 = datetime(1, 10, 25, 2) +DSTSTART_1987_2006 = dt.datetime(1, 4, 1, 2) +DSTEND_1987_2006 = dt.datetime(1, 10, 25, 2) # From 1967 to 1986, DST used to start at 2am (standard time) on the last # Sunday in April (the one on or after April 24) and to end at 2am (DST time) # on the last Sunday of October, which is the first Sunday # on or after Oct 25. -DSTSTART_1967_1986 = datetime(1, 4, 24, 2) +DSTSTART_1967_1986 = dt.datetime(1, 4, 24, 2) DSTEND_1967_1986 = DSTEND_1987_2006 + def us_dst_range(year): # Find start and end times for US DST. For years before 1967, return # start = end for no DST. @@ -100,17 +103,17 @@ def us_dst_range(year): elif 1966 < year < 1987: dststart, dstend = DSTSTART_1967_1986, DSTEND_1967_1986 else: - return (datetime(year, 1, 1), ) * 2 + return (dt.datetime(year, 1, 1), ) * 2 start = first_sunday_on_or_after(dststart.replace(year=year)) end = first_sunday_on_or_after(dstend.replace(year=year)) return start, end -class USTimeZone(tzinfo): +class USTimeZone(dt.tzinfo): def __init__(self, hours, reprname, stdname, dstname): - self.stdoffset = timedelta(hours=hours) + self.stdoffset = dt.timedelta(hours=hours) self.reprname = reprname self.stdname = stdname self.dstname = dstname @@ -118,45 +121,45 @@ def __init__(self, hours, reprname, stdname, dstname): def __repr__(self): return self.reprname - def tzname(self, dt): - if self.dst(dt): + def tzname(self, when): + if self.dst(when): return self.dstname else: return self.stdname - def utcoffset(self, dt): - return self.stdoffset + self.dst(dt) + def utcoffset(self, when): + return self.stdoffset + self.dst(when) - def dst(self, dt): - if dt is None or dt.tzinfo is None: + def dst(self, when): + if when is None or when.tzinfo is None: # An exception may be sensible here, in one or both cases. # It depends on how you want to treat them. The default # fromutc() implementation (called by the default astimezone() - # implementation) passes a datetime with dt.tzinfo is self. + # implementation) passes a datetime with when.tzinfo is self. return ZERO - assert dt.tzinfo is self - start, end = us_dst_range(dt.year) + assert when.tzinfo is self + start, end = us_dst_range(when.year) # Can't compare naive to aware objects, so strip the timezone from - # dt first. - dt = dt.replace(tzinfo=None) - if start + HOUR <= dt < end - HOUR: + # when first. + when = when.replace(tzinfo=None) + if start + HOUR <= when < end - HOUR: # DST is in effect. return HOUR - if end - HOUR <= dt < end: - # Fold (an ambiguous hour): use dt.fold to disambiguate. - return ZERO if dt.fold else HOUR - if start <= dt < start + HOUR: + if end - HOUR <= when < end: + # Fold (an ambiguous hour): use when.fold to disambiguate. + return ZERO if when.fold else HOUR + if start <= when < start + HOUR: # Gap (a non-existent hour): reverse the fold rule. - return HOUR if dt.fold else ZERO + return HOUR if when.fold else ZERO # DST is off. return ZERO - def fromutc(self, dt): - assert dt.tzinfo is self - start, end = us_dst_range(dt.year) + def fromutc(self, when): + assert when.tzinfo is self + start, end = us_dst_range(when.year) start = start.replace(tzinfo=self) end = end.replace(tzinfo=self) - std_time = dt + self.stdoffset + std_time = when + self.stdoffset dst_time = std_time + HOUR if end <= dst_time < end + HOUR: # Repeated hour diff --git a/Doc/installing/index.rst b/Doc/installing/index.rst index 3a485a43a5a751..c372d9f4741800 100644 --- a/Doc/installing/index.rst +++ b/Doc/installing/index.rst @@ -1,16 +1,14 @@ -.. highlight:: none +.. highlight:: shell .. _installing-index: ************************* -Installing Python Modules +Installing Python modules ************************* -:Email: distutils-sig@python.org - As a popular open source development project, Python has an active supporting community of contributors and users that also make their software -available for other Python developers to use under open source license terms. +available for other Python developers to use under open-source license terms. This allows Python users to share and collaborate effectively, benefiting from the solutions others have already created to common (and sometimes @@ -34,34 +32,24 @@ creating and sharing your own Python projects, refer to the Key terms ========= -* ``pip`` is the preferred installer program. Starting with Python 3.4, it +* :program:`pip` is the preferred installer program. It is included by default with the Python binary installers. * A *virtual environment* is a semi-isolated Python environment that allows packages to be installed for use by a particular application, rather than being installed system wide. -* ``venv`` is the standard tool for creating virtual environments, and has - been part of Python since Python 3.3. Starting with Python 3.4, it - defaults to installing ``pip`` into all created virtual environments. -* ``virtualenv`` is a third party alternative (and predecessor) to - ``venv``. It allows virtual environments to be used on versions of - Python prior to 3.4, which either don't provide ``venv`` at all, or - aren't able to automatically install ``pip`` into created environments. -* The `Python Package Index `__ is a public +* ``venv`` is the standard tool for creating virtual environments. + It defaults to installing :program:`pip` into all created virtual environments. +* ``virtualenv`` is a third-party alternative (and predecessor) to + ``venv``. +* The `Python Package Index (PyPI) `__ is a public repository of open source licensed packages made available for use by other Python users. -* the `Python Packaging Authority +* The `Python Packaging Authority `__ is the group of developers and documentation authors responsible for the maintenance and evolution of the standard packaging tools and the associated metadata and file format standards. They maintain a variety of tools, documentation, and issue trackers on `GitHub `__. -* ``distutils`` is the original build and distribution system first added to - the Python standard library in 1998. While direct use of ``distutils`` is - being phased out, it still laid the foundation for the current packaging - and distribution infrastructure, and it not only remains part of the - standard library, but its name lives on in other ways (such as the name - of the mailing list used to coordinate Python packaging standards - development). .. versionchanged:: 3.5 The use of ``venv`` is now recommended for creating virtual environments. @@ -79,7 +67,7 @@ The standard packaging tools are all designed to be used from the command line. The following command will install the latest version of a module and its -dependencies from the Python Package Index:: +dependencies from PyPI:: python -m pip install SomePackage @@ -106,7 +94,7 @@ explicitly:: python -m pip install --upgrade SomePackage -More information and resources regarding ``pip`` and its capabilities can be +More information and resources regarding :program:`pip` and its capabilities can be found in the `Python Packaging User Guide `__. Creation of virtual environments is done through the :mod:`venv` module. @@ -124,19 +112,6 @@ How do I ...? These are quick answers or links for some common tasks. -... install ``pip`` in versions of Python prior to Python 3.4? --------------------------------------------------------------- - -Python only started bundling ``pip`` with Python 3.4. For earlier versions, -``pip`` needs to be "bootstrapped" as described in the Python Packaging -User Guide. - -.. seealso:: - - `Python Packaging User Guide: Requirements for Installing Packages - `__ - - .. installing-per-user-installation: ... install packages just for the current user? @@ -150,10 +125,10 @@ package just for the current user, rather than for all users of the system. --------------------------------------- A number of scientific Python packages have complex binary dependencies, and -aren't currently easy to install using ``pip`` directly. At this point in -time, it will often be easier for users to install these packages by +aren't currently easy to install using :program:`pip` directly. +It will often be easier for users to install these packages by `other means `__ -rather than attempting to install them with ``pip``. +rather than attempting to install them with :program:`pip`. .. seealso:: @@ -166,22 +141,18 @@ rather than attempting to install them with ``pip``. On Linux, macOS, and other POSIX systems, use the versioned Python commands in combination with the ``-m`` switch to run the appropriate copy of -``pip``:: +:program:`pip`:: - python2 -m pip install SomePackage # default Python 2 - python2.7 -m pip install SomePackage # specifically Python 2.7 - python3 -m pip install SomePackage # default Python 3 - python3.4 -m pip install SomePackage # specifically Python 3.4 + python3 -m pip install SomePackage # default Python 3 + python3.14 -m pip install SomePackage # specifically Python 3.14 -Appropriately versioned ``pip`` commands may also be available. +Appropriately versioned :program:`pip` commands may also be available. -On Windows, use the ``py`` Python launcher in combination with the ``-m`` +On Windows, use the :program:`py` Python launcher in combination with the ``-m`` switch:: - py -2 -m pip install SomePackage # default Python 2 - py -2.7 -m pip install SomePackage # specifically Python 2.7 - py -3 -m pip install SomePackage # default Python 3 - py -3.4 -m pip install SomePackage # specifically Python 3.4 + py -3 -m pip install SomePackage # default Python 3 + py -3.14 -m pip install SomePackage # specifically Python 3.14 .. other questions: @@ -201,39 +172,38 @@ On Linux systems, a Python installation will typically be included as part of the distribution. Installing into this Python installation requires root access to the system, and may interfere with the operation of the system package manager and other components of the system if a component -is unexpectedly upgraded using ``pip``. +is unexpectedly upgraded using :program:`pip`. On such systems, it is often better to use a virtual environment or a -per-user installation when installing packages with ``pip``. +per-user installation when installing packages with :program:`pip`. Pip not installed ----------------- -It is possible that ``pip`` does not get installed by default. One potential fix is:: +It is possible that :program:`pip` does not get installed by default. One potential fix is:: python -m ensurepip --default-pip -There are also additional resources for `installing pip. -`__ +There are also additional resources for `installing pip +`__. Installing binary extensions ---------------------------- -Python has typically relied heavily on source based distribution, with end +Python once relied heavily on source-based distribution, with end users being expected to compile extension modules from source as part of the installation process. -With the introduction of support for the binary ``wheel`` format, and the -ability to publish wheels for at least Windows and macOS through the -Python Package Index, this problem is expected to diminish over time, +With the introduction of the binary wheel format, and the +ability to publish wheels through PyPI, this problem is diminishing, as users are more regularly able to install pre-built extensions rather than needing to build them themselves. Some of the solutions for installing `scientific software `__ -that are not yet available as pre-built ``wheel`` files may also help with +that are not yet available as pre-built wheel files may also help with obtaining other binary extensions without needing to build them locally. .. seealso:: diff --git a/Doc/library/__future__.rst b/Doc/library/__future__.rst index 5d916b30112d3c..749e4543c5b823 100644 --- a/Doc/library/__future__.rst +++ b/Doc/library/__future__.rst @@ -15,7 +15,7 @@ before the release in which the feature becomes standard. While these future statements are given additional special meaning by the Python compiler, they are still executed like any other import statement and -the :mod:`__future__` exists and is handled by the import system the same way +the :mod:`!__future__` exists and is handled by the import system the same way any other Python module would be. This design serves three purposes: * To avoid confusing existing tools that analyze import statements and expect to @@ -23,17 +23,17 @@ any other Python module would be. This design serves three purposes: * To document when incompatible changes were introduced, and when they will be --- or were --- made mandatory. This is a form of executable documentation, and - can be inspected programmatically via importing :mod:`__future__` and examining + can be inspected programmatically via importing :mod:`!__future__` and examining its contents. * To ensure that :ref:`future statements ` run under releases prior to - Python 2.1 at least yield runtime exceptions (the import of :mod:`__future__` + Python 2.1 at least yield runtime exceptions (the import of :mod:`!__future__` will fail, because there was no module of that name prior to 2.1). Module Contents --------------- -No feature description will ever be deleted from :mod:`__future__`. Since its +No feature description will ever be deleted from :mod:`!__future__`. Since its introduction in Python 2.1 the following features have found their way into the language using this mechanism: diff --git a/Doc/library/_thread.rst b/Doc/library/_thread.rst index ed29ac70035597..13f463a1e95340 100644 --- a/Doc/library/_thread.rst +++ b/Doc/library/_thread.rst @@ -36,11 +36,6 @@ This module defines the following constants and functions: This is now a synonym of the built-in :exc:`RuntimeError`. -.. data:: LockType - - This is the type of lock objects. - - .. function:: start_new_thread(function, args[, kwargs]) Start a new thread and return its identifier. The thread executes the @@ -120,13 +115,16 @@ This module defines the following constants and functions: Its value may be used to uniquely identify this particular thread system-wide (until the thread terminates, after which the value may be recycled by the OS). - .. availability:: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD, GNU/kFreeBSD. + .. availability:: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD, GNU/kFreeBSD, Solaris. .. versionadded:: 3.8 .. versionchanged:: 3.13 Added support for GNU/kFreeBSD. + .. versionchanged:: 3.15 + Added support for Solaris. + .. function:: stack_size([size]) @@ -159,58 +157,66 @@ This module defines the following constants and functions: .. versionadded:: 3.2 -Lock objects have the following methods: +.. raw:: html + + + + + +.. class:: LockType -.. method:: lock.acquire(blocking=True, timeout=-1) + This is the type of lock objects. - Without any optional argument, this method acquires the lock unconditionally, if - necessary waiting until it is released by another thread (only one thread at a - time can acquire a lock --- that's their reason for existence). + Lock objects have the following methods: - If the *blocking* argument is present, the action depends on its - value: if it is false, the lock is only acquired if it can be acquired - immediately without waiting, while if it is true, the lock is acquired - unconditionally as above. + .. method:: acquire(blocking=True, timeout=-1) - If the floating-point *timeout* argument is present and positive, it - specifies the maximum wait time in seconds before returning. A negative - *timeout* argument specifies an unbounded wait. You cannot specify - a *timeout* if *blocking* is false. + Without any optional argument, this method acquires the lock unconditionally, if + necessary waiting until it is released by another thread (only one thread at a + time can acquire a lock --- that's their reason for existence). - The return value is ``True`` if the lock is acquired successfully, - ``False`` if not. + If the *blocking* argument is present, the action depends on its + value: if it is false, the lock is only acquired if it can be acquired + immediately without waiting, while if it is true, the lock is acquired + unconditionally as above. - .. versionchanged:: 3.2 - The *timeout* parameter is new. + If the floating-point *timeout* argument is present and positive, it + specifies the maximum wait time in seconds before returning. A negative + *timeout* argument specifies an unbounded wait. You cannot specify + a *timeout* if *blocking* is false. - .. versionchanged:: 3.2 - Lock acquires can now be interrupted by signals on POSIX. + The return value is ``True`` if the lock is acquired successfully, + ``False`` if not. - .. versionchanged:: 3.14 - Lock acquires can now be interrupted by signals on Windows. + .. versionchanged:: 3.2 + The *timeout* parameter is new. + .. versionchanged:: 3.2 + Lock acquires can now be interrupted by signals on POSIX. -.. method:: lock.release() + .. versionchanged:: 3.14 + Lock acquires can now be interrupted by signals on Windows. - Releases the lock. The lock must have been acquired earlier, but not - necessarily by the same thread. + .. method:: release() + Releases the lock. The lock must have been acquired earlier, but not + necessarily by the same thread. -.. method:: lock.locked() + .. method:: locked() - Return the status of the lock: ``True`` if it has been acquired by some thread, - ``False`` if not. + Return the status of the lock: ``True`` if it has been acquired by some thread, + ``False`` if not. -In addition to these methods, lock objects can also be used via the -:keyword:`with` statement, e.g.:: + In addition to these methods, lock objects can also be used via the + :keyword:`with` statement, e.g.:: - import _thread + import _thread - a_lock = _thread.allocate_lock() + a_lock = _thread.allocate_lock() - with a_lock: - print("a_lock is locked while this executes") + with a_lock: + print("a_lock is locked while this executes") **Caveats:** diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index 49e541a9d9b1cb..be25a94e7e94f6 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -4,10 +4,6 @@ .. module:: abc :synopsis: Abstract base classes according to :pep:`3119`. -.. moduleauthor:: Guido van Rossum -.. sectionauthor:: Georg Brandl -.. much of the content adapted from docstrings - **Source code:** :source:`Lib/abc.py` -------------- @@ -170,17 +166,17 @@ The :mod:`!abc` module also provides the following decorator: or is derived from it. A class that has a metaclass derived from :class:`!ABCMeta` cannot be instantiated unless all of its abstract methods and properties are overridden. The abstract methods can be called using any - of the normal 'super' call mechanisms. :func:`!abstractmethod` may be used + of the normal 'super' call mechanisms. :deco:`!abstractmethod` may be used to declare abstract methods for properties and descriptors. Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are only supported using the :func:`update_abstractmethods` function. The - :func:`!abstractmethod` only affects subclasses derived using regular + :deco:`!abstractmethod` only affects subclasses derived using regular inheritance; "virtual subclasses" registered with the ABC's :meth:`~ABCMeta.register` method are not affected. - When :func:`!abstractmethod` is applied in combination with other method + When :deco:`!abstractmethod` is applied in combination with other method descriptors, it should be applied as the innermost decorator, as shown in the following usage examples:: @@ -218,7 +214,7 @@ The :mod:`!abc` module also provides the following decorator: the descriptor must identify itself as abstract using :attr:`!__isabstractmethod__`. In general, this attribute should be ``True`` if any of the methods used to compose the descriptor are abstract. For - example, Python's built-in :class:`property` does the equivalent of:: + example, Python's built-in :deco:`property` does the equivalent of:: class Descriptor: ... @@ -241,14 +237,14 @@ The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractclassmethod .. versionadded:: 3.2 - .. deprecated:: 3.3 - It is now possible to use :class:`classmethod` with - :func:`abstractmethod`, making this decorator redundant. + .. deprecated-removed:: 3.3 3.21 + It is now possible to use :deco:`classmethod` with + :deco:`abstractmethod`, making this decorator redundant. - A subclass of the built-in :func:`classmethod`, indicating an abstract - classmethod. Otherwise it is similar to :func:`abstractmethod`. + A subclass of the built-in :class:`classmethod`, indicating an abstract + classmethod. Otherwise it is similar to :deco:`abstractmethod`. - This special case is deprecated, as the :func:`classmethod` decorator + This special case is deprecated, as the :deco:`classmethod` decorator is now correctly identified as abstract when applied to an abstract method:: @@ -262,14 +258,14 @@ The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractstaticmethod .. versionadded:: 3.2 - .. deprecated:: 3.3 - It is now possible to use :class:`staticmethod` with - :func:`abstractmethod`, making this decorator redundant. + .. deprecated-removed:: 3.3 3.21 + It is now possible to use :deco:`staticmethod` with + :deco:`abstractmethod`, making this decorator redundant. - A subclass of the built-in :func:`staticmethod`, indicating an abstract - staticmethod. Otherwise it is similar to :func:`abstractmethod`. + A subclass of the built-in :class:`staticmethod`, indicating an abstract + staticmethod. Otherwise it is similar to :deco:`abstractmethod`. - This special case is deprecated, as the :func:`staticmethod` decorator + This special case is deprecated, as the :deco:`staticmethod` decorator is now correctly identified as abstract when applied to an abstract method:: @@ -282,15 +278,15 @@ The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractproperty - .. deprecated:: 3.3 - It is now possible to use :class:`property`, :meth:`property.getter`, - :meth:`property.setter` and :meth:`property.deleter` with - :func:`abstractmethod`, making this decorator redundant. + .. deprecated-removed:: 3.3 3.21 + It is now possible to use :deco:`property`, :deco:`property.getter`, + :deco:`property.setter` and :deco:`property.deleter` with + :deco:`abstractmethod`, making this decorator redundant. - A subclass of the built-in :func:`property`, indicating an abstract + A subclass of the built-in :class:`property`, indicating an abstract property. - This special case is deprecated, as the :func:`property` decorator + This special case is deprecated, as the :deco:`property` decorator is now correctly identified as abstract when applied to an abstract method:: diff --git a/Doc/library/annotationlib.rst b/Doc/library/annotationlib.rst index 981d89be7d58d6..af28fe0e2fde2f 100644 --- a/Doc/library/annotationlib.rst +++ b/Doc/library/annotationlib.rst @@ -4,6 +4,7 @@ .. module:: annotationlib :synopsis: Functionality for introspecting annotations +.. versionadded:: 3.14 **Source code:** :source:`Lib/annotationlib.py` @@ -45,6 +46,10 @@ and :func:`call_annotate_function`, as well as the :func:`call_evaluate_function` function for working with :term:`evaluate functions `. +.. caution:: + + Most functionality in this module can execute arbitrary code; see + :ref:`the security section ` for more information. .. seealso:: @@ -335,14 +340,29 @@ Functions * VALUE: :attr:`!object.__annotations__` is tried first; if that does not exist, the :attr:`!object.__annotate__` function is called if it exists. + * FORWARDREF: If :attr:`!object.__annotations__` exists and can be evaluated successfully, it is used; otherwise, the :attr:`!object.__annotate__` function is called. If it does not exist either, :attr:`!object.__annotations__` is tried again and any error from accessing it is re-raised. + + * When calling :attr:`!object.__annotate__` it is first called with :attr:`~Format.FORWARDREF`. + If this is not implemented, it will then check if :attr:`~Format.VALUE_WITH_FAKE_GLOBALS` + is supported and use that in the fake globals environment. + If neither of these formats are supported, it will fall back to using :attr:`~Format.VALUE`. + If :attr:`~Format.VALUE` fails, the error from this call will be raised. + * STRING: If :attr:`!object.__annotate__` exists, it is called first; otherwise, :attr:`!object.__annotations__` is used and stringified using :func:`annotations_to_string`. + * When calling :attr:`!object.__annotate__` it is first called with :attr:`~Format.STRING`. + If this is not implemented, it will then check if :attr:`~Format.VALUE_WITH_FAKE_GLOBALS` + is supported and use that in the fake globals environment. + If neither of these formats are supported, it will fall back to using :attr:`~Format.VALUE` + with the result converted using :func:`annotations_to_string`. + If :attr:`~Format.VALUE` fails, the error from this call will be raised. + Returns a dict. :func:`!get_annotations` returns a new dict every time it's called; calling it twice on the same object will return two different but equivalent dicts. @@ -490,6 +510,81 @@ annotations from the class and puts them in a separate attribute: return typ +Creating a custom callable annotate function +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Custom :term:`annotate functions ` may be literal functions like those +automatically generated for functions, classes, and modules. Or, they may wish to utilise +the encapsulation provided by classes, in which case any :term:`callable` can be used as +an :term:`annotate function`. + +To provide the :attr:`~Format.VALUE`, :attr:`~Format.STRING`, or +:attr:`~Format.FORWARDREF` formats directly, an :term:`annotate function` must provide +the following attribute: + +* A callable ``__call__`` with signature ``__call__(format, /) -> dict``, that does not + raise a :exc:`NotImplementedError` when called with a supported format. + +To provide the :attr:`~Format.VALUE_WITH_FAKE_GLOBALS` format, which is used to +automatically generate :attr:`~Format.STRING` or :attr:`~Format.FORWARDREF` if they are +not supported directly, :term:`annotate functions ` must provide the +following attributes: + +* A callable ``__call__`` with signature ``__call__(format, /) -> dict``, that does not + raise a :exc:`NotImplementedError` when called with + :attr:`~Format.VALUE_WITH_FAKE_GLOBALS`. +* A :ref:`code object ` ``__code__`` containing the compiled code for the + annotate function. +* Optional: A tuple of the function's positional defaults ``__kwdefaults__``, if the + function represented by ``__code__`` uses any positional defaults. +* Optional: A dict of the function's keyword defaults ``__defaults__``, if the function + represented by ``__code__`` uses any keyword defaults. +* Optional: All other :ref:`function attributes `. + +.. code-block:: python + + class Annotate: + called_formats = [] + + def __call__(self, format=None, /, *, _self=None): + # When called with fake globals, `_self` will be the + # actual self value, and `self` will be the format. + if _self is not None: + self, format = _self, self + + self.called_formats.append(format) + if format <= 2: # VALUE or VALUE_WITH_FAKE_GLOBALS + return {"x": MyType} + raise NotImplementedError + + __code__ = __call__.__code__ + __defaults__ = (None,) + __kwdefaults__ = property(lambda self: dict(_self=self)) + + __globals__ = {} + __builtins__ = {} + __closure__ = None + +This can then be called with: + +.. code-block:: pycon + + >>> from annotationlib import call_annotate_function, Format + >>> call_annotate_function(Annotate(), format=Format.STRING) + {'x': 'MyType'} + +Or used as the annotate function for an object: + +.. code-block:: pycon + + >>> from annotationlib import get_annotations, Format + >>> class C: + ... pass + >>> C.__annotate__ = Annotate() + >>> get_annotations(Annotate(), format=Format.STRING) + {'x': 'MyType'} + + Limitations of the ``STRING`` format ------------------------------------ @@ -603,3 +698,23 @@ Below are a few examples of the behavior with unsupported expressions: >>> def ifexp(x: 1 if y else 0): ... >>> get_annotations(ifexp, format=Format.STRING) {'x': '1'} + +.. _annotationlib-security: + +Security implications of introspecting annotations +-------------------------------------------------- + +Much of the functionality in this module involves executing code related to annotations, +which can then do arbitrary things. For example, +:func:`get_annotations` may call an arbitrary :term:`annotate function`, and +:meth:`ForwardRef.evaluate` may call :func:`eval` on an arbitrary string. Code contained +in an annotation might make arbitrary system calls, enter an infinite loop, or perform any +other operation. This is also true for any access of the :attr:`~object.__annotations__` attribute, +and for various functions in the :mod:`typing` module that work with annotations, such as +:func:`typing.get_type_hints`. + +Any security issue arising from this also applies immediately after importing +code that may contain untrusted annotations: importing code can always cause arbitrary operations +to be performed. However, it is unsafe to accept strings or other input from an untrusted source and +pass them to any of the APIs for introspecting annotations, for example by editing an +``__annotations__`` dictionary or directly creating a :class:`ForwardRef` object. diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index 79e15994491eff..e4a5f4d109b499 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -4,16 +4,13 @@ .. module:: argparse :synopsis: Command-line option and argument parsing library. -.. moduleauthor:: Steven Bethard -.. sectionauthor:: Steven Bethard - .. versionadded:: 3.2 **Source code:** :source:`Lib/argparse.py` .. note:: - While :mod:`argparse` is the default recommended standard library module + While :mod:`!argparse` is the default recommended standard library module for implementing basic command line applications, authors with more exacting requirements for exactly how their command line applications behave may find it doesn't provide the necessary level of control. @@ -74,7 +71,7 @@ ArgumentParser objects prefix_chars='-', fromfile_prefix_chars=None, \ argument_default=None, conflict_handler='error', \ add_help=True, allow_abbrev=True, exit_on_error=True, \ - *, suggest_on_error=False, color=True) + *, suggest_on_error=True, color=True) Create a new :class:`ArgumentParser` object. All parameters should be passed as keyword arguments. Each parameter has its own more detailed description @@ -117,7 +114,7 @@ ArgumentParser objects error info when an error occurs. (default: ``True``) * suggest_on_error_ - Enables suggestions for mistyped argument choices - and subparser names (default: ``False``) + and subparser names (default: ``True``) * color_ - Allow color output (default: ``True``) @@ -134,6 +131,9 @@ ArgumentParser objects .. versionchanged:: 3.14 *suggest_on_error* and *color* parameters were added. + .. versionchanged:: 3.15 + *suggest_on_error* default changed to ``True``. + The following sections describe how each of these are used. @@ -442,9 +442,8 @@ is considered equivalent to the expression ``['-f', 'foo', '-f', 'bar']``. .. note:: - Empty lines are treated as empty strings (``''``), which are allowed as values but - not as arguments. Empty lines that are read as arguments will result in an - "unrecognized arguments" error. + Each line is treated as a single argument, so an empty line is read as an + empty string (``''``). :class:`ArgumentParser` uses :term:`filesystem encoding and error handler` to read the file containing arguments. @@ -596,32 +595,23 @@ suggest_on_error ^^^^^^^^^^^^^^^^ By default, when a user passes an invalid argument choice or subparser name, -:class:`ArgumentParser` will exit with error info and list the permissible -argument choices (if specified) or subparser names as part of the error message. - -If the user would like to enable suggestions for mistyped argument choices and -subparser names, the feature can be enabled by setting ``suggest_on_error`` to -``True``. Note that this only applies for arguments when the choices specified -are strings:: - - >>> parser = argparse.ArgumentParser(description='Process some integers.', - suggest_on_error=True) - >>> parser.add_argument('--action', choices=['sum', 'max']) - >>> parser.add_argument('integers', metavar='N', type=int, nargs='+', - ... help='an integer for the accumulator') - >>> parser.parse_args(['--action', 'sumn', 1, 2, 3]) - tester.py: error: argument --action: invalid choice: 'sumn', maybe you meant 'sum'? (choose from 'sum', 'max') +:class:`ArgumentParser` will exit with error info and provide suggestions for +mistyped arguments. The error message will list the permissible argument +choices (if specified) or subparser names, along with a "maybe you meant" +suggestion if a close match is found. Note that this only applies for arguments +when the choices specified are strings:: -If you're writing code that needs to be compatible with older Python versions -and want to opportunistically use ``suggest_on_error`` when it's available, you -can set it as an attribute after initializing the parser instead of using the -keyword argument:: + >>> parser = argparse.ArgumentParser(suggest_on_error=True) + >>> parser.add_argument('--action', choices=['debug', 'dryrun']) + >>> parser.parse_args(['--action', 'debugg']) + usage: tester.py [-h] [--action {debug,dryrun}] + tester.py: error: argument --action: invalid choice: 'debugg', maybe you meant 'debug'? (choose from debug, dryrun) - >>> parser = argparse.ArgumentParser(description='Process some integers.') - >>> parser.suggest_on_error = True +You can disable suggestions by setting ``suggest_on_error`` to ``False``. .. versionadded:: 3.14 - +.. versionchanged:: 3.15 + Changed default value of ``suggest_on_error`` from ``False`` to ``True``. color ^^^^^ @@ -639,8 +629,31 @@ by setting ``color`` to ``False``:: ... help='an integer for the accumulator') >>> parser.parse_args(['--help']) +Note that when ``color=True``, colored output depends on both environment +variables and terminal capabilities. However, if ``color=False``, colored +output is always disabled, even if environment variables like ``FORCE_COLOR`` +are set. + .. versionadded:: 3.14 +To highlight inline code in your description, epilog, or argument ``help`` +text, you can use single or double backticks:: + + >>> parser = argparse.ArgumentParser( + ... formatter_class=argparse.RawDescriptionHelpFormatter, + ... description='Run ``python -m myapp`` to start.', + ... epilog='''Examples: + ... `python -m myapp --verbose` + ... ``python -m myapp --config settings.json`` + ... ''') + >>> parser.add_argument('--foo', help='set the `foo` value') + +When colors are enabled, the text inside backticks will be displayed in a +distinct color to help examples stand out. When colors are disabled, backticks +are preserved as-is, which is readable in plain text. + +.. versionadded:: 3.15 + The add_argument() method ------------------------- @@ -681,6 +694,8 @@ The add_argument() method * deprecated_ - Whether or not use of the argument is deprecated. + The method returns an :class:`Action` object representing the argument. + The following sections describe how each of these are used. @@ -722,13 +737,13 @@ By default, :mod:`!argparse` automatically handles the internal naming and display names of arguments, simplifying the process without requiring additional configuration. As such, you do not need to specify the dest_ and metavar_ parameters. -The dest_ parameter defaults to the argument name with underscores ``_`` -replacing hyphens ``-`` . The metavar_ parameter defaults to the -upper-cased name. For example:: +For optional arguments, the dest_ parameter defaults to the argument name, with +underscores ``_`` replacing hyphens ``-``. The metavar_ parameter defaults to +the upper-cased name. For example:: >>> parser = argparse.ArgumentParser(prog='PROG') >>> parser.add_argument('--foo-bar') - >>> parser.parse_args(['--foo-bar', 'FOO-BAR'] + >>> parser.parse_args(['--foo-bar', 'FOO-BAR']) Namespace(foo_bar='FOO-BAR') >>> parser.print_help() usage: [-h] [--foo-bar FOO-BAR] @@ -763,9 +778,9 @@ how the command-line arguments should be handled. The supplied actions are: Namespace(foo=42) * ``'store_true'`` and ``'store_false'`` - These are special cases of - ``'store_const'`` used for storing the values ``True`` and ``False`` - respectively. In addition, they create default values of ``False`` and - ``True`` respectively:: + ``'store_const'`` that respectively store the values ``True`` and ``False`` + with default values of ``False`` and + ``True``:: >>> parser = argparse.ArgumentParser() >>> parser.add_argument('--foo', action='store_true') @@ -774,19 +789,19 @@ how the command-line arguments should be handled. The supplied actions are: >>> parser.parse_args('--foo --bar'.split()) Namespace(foo=True, bar=False, baz=True) -* ``'append'`` - This stores a list, and appends each argument value to the - list. It is useful to allow an option to be specified multiple times. - If the default value is non-empty, the default elements will be present - in the parsed value for the option, with any values from the - command line appended after those default values. Example usage:: +* ``'append'`` - This appends each argument value to a list. + It is useful for allowing an option to be specified multiple times. + If the default value is a non-empty list, the parsed value will start + with the default list's elements and any values from the command line + will be appended after those default values. Example usage:: >>> parser = argparse.ArgumentParser() - >>> parser.add_argument('--foo', action='append') + >>> parser.add_argument('--foo', action='append', default=['0']) >>> parser.parse_args('--foo 1 --foo 2'.split()) - Namespace(foo=['1', '2']) + Namespace(foo=['0', '1', '2']) -* ``'append_const'`` - This stores a list, and appends the value specified by - the const_ keyword argument to the list; note that the const_ keyword +* ``'append_const'`` - This appends the value specified by + the const_ keyword argument to a list; note that the const_ keyword argument defaults to ``None``. The ``'append_const'`` action is typically useful when multiple arguments need to store constants to the same list. For example:: @@ -797,8 +812,8 @@ how the command-line arguments should be handled. The supplied actions are: >>> parser.parse_args('--str --int'.split()) Namespace(types=[, ]) -* ``'extend'`` - This stores a list and appends each item from the multi-value - argument list to it. +* ``'extend'`` - This appends each item from a multi-value + argument to a list. The ``'extend'`` action is typically used with the nargs_ keyword argument value ``'+'`` or ``'*'``. Note that when nargs_ is ``None`` (the default) or ``'?'``, each @@ -812,7 +827,7 @@ how the command-line arguments should be handled. The supplied actions are: .. versionadded:: 3.8 -* ``'count'`` - This counts the number of times a keyword argument occurs. For +* ``'count'`` - This counts the number of times an argument occurs. For example, this is useful for increasing verbosity levels:: >>> parser = argparse.ArgumentParser() @@ -981,8 +996,8 @@ the various :class:`ArgumentParser` actions. The two most common uses of it are (like ``-f`` or ``--foo``) and ``nargs='?'``. This creates an optional argument that can be followed by zero or one command-line arguments. When parsing the command line, if the option string is encountered with no - command-line argument following it, the value of ``const`` will be assumed to - be ``None`` instead. See the nargs_ description for examples. + command-line argument following it, the value from ``const`` will be used. + See the nargs_ description for examples. .. versionchanged:: 3.11 ``const=None`` by default, including when ``action='append_const'`` or @@ -1036,6 +1051,10 @@ is used when no command-line argument was present:: >>> parser.parse_args([]) Namespace(foo=42) +Because ``nargs='*'`` gathers any supplied values into a list, an absent +positional argument yields an empty list (``[]``). Only a non-``None`` +*default* overrides this (so ``default=None`` still gives ``[]``). + For required_ arguments, the ``default`` value is ignored. For example, this applies to positional arguments with nargs_ values other than ``?`` or ``*``, or optional arguments marked as ``required=True``. @@ -1099,7 +1118,15 @@ User defined functions can be used as well: The :func:`bool` function is not recommended as a type converter. All it does is convert empty strings to ``False`` and non-empty strings to ``True``. -This is usually not what is desired. +This is usually not what is desired:: + + >>> parser = argparse.ArgumentParser() + >>> _ = parser.add_argument('--verbose', type=bool) + >>> parser.parse_args(['--verbose', 'False']) + Namespace(verbose=True) + +See :class:`BooleanOptionalAction` or ``action='store_true'`` for common +alternatives. In general, the ``type`` keyword is a convenience that should only be used for simple conversions that can only raise one of the three supported exceptions. @@ -1142,16 +1169,21 @@ if the argument was not one of the acceptable values:: game.py: error: argument move: invalid choice: 'fire' (choose from 'rock', 'paper', 'scissors') -Note that inclusion in the *choices* sequence is checked after any type_ -conversions have been performed, so the type of the objects in the *choices* -sequence should match the type_ specified. - Any sequence can be passed as the *choices* value, so :class:`list` objects, :class:`tuple` objects, and custom sequences are all supported. Use of :class:`enum.Enum` is not recommended because it is difficult to control its appearance in usage, help, and error messages. +Note that *choices* are checked after any type_ +conversions have been performed, so objects in *choices* +should match the type_ specified. This can make *choices* +appear unfamiliar in usage, help, or error messages. + +To keep *choices* user-friendly, consider a custom type wrapper that +converts and formats values, or omit type_ and handle conversion in +your application code. + Formatted choices override the default *metavar* which is normally derived from *dest*. This is usually what you want because the user never sees the *dest* parameter. If this display isn't desirable (perhaps because there are @@ -1313,8 +1345,12 @@ attribute is determined by the ``dest`` keyword argument of For optional argument actions, the value of ``dest`` is normally inferred from the option strings. :class:`ArgumentParser` generates the value of ``dest`` by -taking the first long option string and stripping away the initial ``--`` -string. If no long option strings were supplied, ``dest`` will be derived from +taking the first double-dash long option string and stripping away the initial +``-`` characters. +If no double-dash long option strings were supplied, ``dest`` will be derived +from the first single-dash long option string by stripping the initial ``-`` +character. +If no long option strings were supplied, ``dest`` will be derived from the first short option string by stripping the initial ``-`` character. Any internal ``-`` characters will be converted to ``_`` characters to make sure the string is a valid attribute name. The examples below illustrate this @@ -1322,11 +1358,12 @@ behavior:: >>> parser = argparse.ArgumentParser() >>> parser.add_argument('-f', '--foo-bar', '--foo') + >>> parser.add_argument('-q', '-quz') >>> parser.add_argument('-x', '-y') - >>> parser.parse_args('-f 1 -x 2'.split()) - Namespace(foo_bar='1', x='2') - >>> parser.parse_args('--foo 1 -y 2'.split()) - Namespace(foo_bar='1', x='2') + >>> parser.parse_args('-f 1 -q 2 -x 3'.split()) + Namespace(foo_bar='1', quz='2', x='3') + >>> parser.parse_args('--foo 1 -quz 2 -y 3'.split()) + Namespace(foo_bar='1', quz='2', x='2') ``dest`` allows a custom attribute name to be provided:: @@ -1335,6 +1372,14 @@ behavior:: >>> parser.parse_args('--foo XXX'.split()) Namespace(bar='XXX') +Multiple arguments may share the same ``dest``. By default, the value from the +last such argument given on the command line wins. Use ``action='append'`` to +collect values from all of them into a list instead. For conflicting *option +strings* rather than ``dest`` names, see conflict_handler_. + +.. versionchanged:: 3.15 + Single-dash long option now takes precedence over short options. + .. _deprecated: @@ -1428,8 +1473,18 @@ this API may be passed as the ``action`` parameter to >>> parser.parse_args(['--no-foo']) Namespace(foo=False) + Single-dash long options are also supported. + For example, negative option ``-nofoo`` is automatically added for + positive option ``-foo``. + But no additional options are added for short options such as ``-f``. + .. versionadded:: 3.9 + .. versionchanged:: 3.15 + Added support for single-dash options. + + Added support for alternate prefix_chars_. + The parse_args() method ----------------------- @@ -1652,7 +1707,7 @@ The Namespace object Other utilities --------------- -Sub-commands +Subcommands ^^^^^^^^^^^^ .. method:: ArgumentParser.add_subparsers(*, [title], [description], [prog], \ @@ -1681,7 +1736,7 @@ Sub-commands * *description* - description for the sub-parser group in help output, by default ``None`` - * *prog* - usage information that will be displayed with sub-command help, + * *prog* - usage information that will be displayed with subcommand help, by default the name of the program and any positional arguments before the subparser argument @@ -1691,7 +1746,7 @@ Sub-commands * action_ - the basic type of action to be taken when this argument is encountered at the command line - * dest_ - name of the attribute under which sub-command name will be + * dest_ - name of the attribute under which subcommand name will be stored; by default ``None`` and no value is stored * required_ - Whether or not a subcommand must be provided, by default @@ -1723,13 +1778,18 @@ Sub-commands >>> parser.parse_args(['--foo', 'b', '--baz', 'Z']) Namespace(baz='Z', foo=True) - Note that the object returned by :meth:`parse_args` will only contain + Note that the object returned by :meth:`~ArgumentParser.parse_args` will only contain attributes for the main parser and the subparser that was selected by the command line (and not any other subparsers). So in the example above, when the ``a`` command is specified, only the ``foo`` and ``bar`` attributes are present, and when the ``b`` command is specified, only the ``foo`` and ``baz`` attributes are present. + If a subparser defines an argument with the same ``dest`` as the parent + parser, the two share a single namespace attribute, so the parent's value + won't be retained. Users should give them distinct ``dest`` values to + keep both. + Similarly, when a help message is requested from a subparser, only the help for that particular parser will be printed. The help message will not include parent parser or sibling parser messages. (A help message for each @@ -1766,7 +1826,7 @@ Sub-commands -h, --help show this help message and exit --baz {X,Y,Z} baz help - The :meth:`add_subparsers` method also supports ``title`` and ``description`` + The :meth:`~ArgumentParser.add_subparsers` method also supports ``title`` and ``description`` keyword arguments. When either is present, the subparser's commands will appear in their own group in the help output. For example:: @@ -1787,34 +1847,8 @@ Sub-commands {foo,bar} additional help - Furthermore, :meth:`~_SubParsersAction.add_parser` supports an additional - *aliases* argument, - which allows multiple strings to refer to the same subparser. This example, - like ``svn``, aliases ``co`` as a shorthand for ``checkout``:: - - >>> parser = argparse.ArgumentParser() - >>> subparsers = parser.add_subparsers() - >>> checkout = subparsers.add_parser('checkout', aliases=['co']) - >>> checkout.add_argument('foo') - >>> parser.parse_args(['co', 'bar']) - Namespace(foo='bar') - - :meth:`~_SubParsersAction.add_parser` supports also an additional - *deprecated* argument, which allows to deprecate the subparser. - - >>> import argparse - >>> parser = argparse.ArgumentParser(prog='chicken.py') - >>> subparsers = parser.add_subparsers() - >>> run = subparsers.add_parser('run') - >>> fly = subparsers.add_parser('fly', deprecated=True) - >>> parser.parse_args(['fly']) # doctest: +SKIP - chicken.py: warning: command 'fly' is deprecated - Namespace() - - .. versionadded:: 3.13 - One particularly effective way of handling subcommands is to combine the use - of the :meth:`add_subparsers` method with calls to :meth:`set_defaults` so + of the :meth:`~ArgumentParser.add_subparsers` method with calls to :meth:`~ArgumentParser.set_defaults` so that each subparser knows which Python function it should execute. For example:: @@ -1850,12 +1884,12 @@ Sub-commands >>> args.func(args) ((XYZYX)) - This way, you can let :meth:`parse_args` do the job of calling the + This way, you can let :meth:`~ArgumentParser.parse_args` do the job of calling the appropriate function after argument parsing is complete. Associating functions with actions like this is typically the easiest way to handle the different actions for each of your subparsers. However, if it is necessary to check the name of the subparser that was invoked, the ``dest`` keyword - argument to the :meth:`add_subparsers` call will work:: + argument to the :meth:`~ArgumentParser.add_subparsers` call will work:: >>> parser = argparse.ArgumentParser() >>> subparsers = parser.add_subparsers(dest='subparser_name') @@ -1874,6 +1908,43 @@ Sub-commands the main parser. +.. method:: _SubParsersAction.add_parser(name, *, help=None, aliases=None, \ + deprecated=False, **kwargs) + + Create and return a new :class:`ArgumentParser` object for the + subcommand *name*. + + The *name* argument is the name of the sub-command. + + The *help* argument provides a short description for this sub-command. + + The *aliases* argument allows providing alternative names for this + sub-command. For example:: + + >>> parser = argparse.ArgumentParser() + >>> subparsers = parser.add_subparsers() + >>> checkout = subparsers.add_parser('checkout', aliases=['co']) + >>> checkout.add_argument('foo') + >>> parser.parse_args(['co', 'bar']) + Namespace(foo='bar') + + The *deprecated* argument, if ``True``, marks the sub-command as + deprecated and will issue a warning when used. For example:: + + >>> parser = argparse.ArgumentParser(prog='chicken.py') + >>> subparsers = parser.add_subparsers() + >>> fly = subparsers.add_parser('fly', deprecated=True) + >>> args = parser.parse_args(['fly']) + chicken.py: warning: command 'fly' is deprecated + Namespace() + + All other keyword arguments are passed directly to the + :class:`!ArgumentParser` constructor. + + .. versionadded:: 3.13 + Added the *deprecated* parameter. + + FileType objects ^^^^^^^^^^^^^^^^ @@ -1909,7 +1980,7 @@ FileType objects run and then use the :keyword:`with`-statement to manage the files. .. versionchanged:: 3.4 - Added the *encodings* and *errors* parameters. + Added the *encoding* and *errors* parameters. .. deprecated:: 3.14 @@ -1971,6 +2042,9 @@ Argument groups Note that any arguments not in your user-defined groups will end up back in the usual "positional arguments" and "optional arguments" sections. + Within each argument group, arguments are displayed in help output in the + order in which they are added. + .. deprecated-removed:: 3.11 3.14 Calling :meth:`add_argument_group` on an argument group now raises an exception. This nesting was never supported, often failed to work @@ -2061,7 +2135,9 @@ Parser defaults >>> parser.parse_args(['736']) Namespace(bar=42, baz='badger', foo=736) - Note that parser-level defaults always override argument-level defaults:: + Note that defaults can be set at both the parser level using :meth:`set_defaults` + and at the argument level using :meth:`add_argument`. If both are called for the + same argument, the last default set for an argument is used:: >>> parser = argparse.ArgumentParser() >>> parser.add_argument('--foo', default='bar') @@ -2169,6 +2245,9 @@ Customizing file parsing def convert_arg_line_to_args(self, arg_line): return arg_line.split() + Note that with this override an argument can no longer contain spaces, since + each space-separated word becomes a separate argument. + Exiting methods ^^^^^^^^^^^^^^^ diff --git a/Doc/library/array.rst b/Doc/library/array.rst index e0b1eb89cf6c05..c688d54318e707 100644 --- a/Doc/library/array.rst +++ b/Doc/library/array.rst @@ -9,10 +9,10 @@ -------------- This module defines an object type which can compactly represent an array of -basic values: characters, integers, floating-point numbers. Arrays are sequence +basic values: characters, integers, floating-point numbers, complex numbers. Arrays are mutable :term:`sequence` types and behave very much like lists, except that the type of objects stored in them is constrained. The type is specified at object creation time by using a -:dfn:`type code`, which is a single character. The following type codes are +:dfn:`type code`. The following type codes are defined: +-----------+--------------------+-------------------+-----------------------+-------+ @@ -22,9 +22,7 @@ defined: +-----------+--------------------+-------------------+-----------------------+-------+ | ``'B'`` | unsigned char | int | 1 | | +-----------+--------------------+-------------------+-----------------------+-------+ -| ``'u'`` | wchar_t | Unicode character | 2 | \(1) | -+-----------+--------------------+-------------------+-----------------------+-------+ -| ``'w'`` | Py_UCS4 | Unicode character | 4 | | +| ``'w'`` | Py_UCS4 | Unicode character | 4 | \(1) | +-----------+--------------------+-------------------+-----------------------+-------+ | ``'h'`` | signed short | int | 2 | | +-----------+--------------------+-------------------+-----------------------+-------+ @@ -42,23 +40,47 @@ defined: +-----------+--------------------+-------------------+-----------------------+-------+ | ``'Q'`` | unsigned long long | int | 8 | | +-----------+--------------------+-------------------+-----------------------+-------+ +| ``'e'`` | _Float16 | float | 2 | \(2) | ++-----------+--------------------+-------------------+-----------------------+-------+ | ``'f'`` | float | float | 4 | | +-----------+--------------------+-------------------+-----------------------+-------+ | ``'d'`` | double | float | 8 | | +-----------+--------------------+-------------------+-----------------------+-------+ +| ``'Zf'`` | float complex | complex | 8 | \(3) | ++-----------+--------------------+-------------------+-----------------------+-------+ +| ``'Zd'`` | double complex | complex | 16 | \(3) | ++-----------+--------------------+-------------------+-----------------------+-------+ + Notes: (1) - It can be 16 bits or 32 bits depending on the platform. + .. versionadded:: 3.13 + +(2) + The IEEE 754 binary16 "half precision" type was introduced in the 2008 + revision of the `IEEE 754 standard `_. + This type is not widely supported by C compilers. It's available + as :c:expr:`_Float16` type, if the compiler supports the Annex H + of the C23 standard. - .. versionchanged:: 3.9 - ``array('u')`` now uses :c:type:`wchar_t` as C type instead of deprecated - ``Py_UNICODE``. This change doesn't affect its behavior because - ``Py_UNICODE`` is alias of :c:type:`wchar_t` since Python 3.3. + .. versionadded:: 3.15 - .. deprecated-removed:: 3.3 3.16 - Please migrate to ``'w'`` typecode. +(3) + Complex types (``Zf`` and ``Zd``) are available unconditionally, + regardless on support for complex types (the Annex G of the C11 standard) + by the C compiler. + As specified in the C11 standard, each complex type is represented by a + two-element C array containing, respectively, the real and imaginary parts. + + .. versionadded:: 3.15 + +.. seealso:: + + The :ref:`ctypes ` and + :ref:`struct ` modules, + as well as third-party modules like `numpy `__, + use similar -- but slightly different -- type codes. The actual representation of values is determined by the machine architecture @@ -70,7 +92,10 @@ The module defines the following item: .. data:: typecodes - A string with all available type codes. + A tuple with all available type codes. + + .. versionchanged:: 3.15 + The type changed from :class:`str` to :class:`tuple`. The module defines the following type: @@ -90,12 +115,14 @@ The module defines the following type: otherwise, the initializer's iterator is passed to the :meth:`extend` method to add initial items to the array. - Array objects support the ordinary sequence operations of indexing, slicing, + Array objects support the ordinary :ref:`mutable ` :term:`sequence` operations of indexing, slicing, concatenation, and multiplication. When using slice assignment, the assigned value must be an array object with the same type code; in all other cases, :exc:`TypeError` is raised. Array objects also implement the buffer interface, and may be used wherever :term:`bytes-like objects ` are supported. + Arrays are :ref:`generic ` over the type of their contents. + .. audit-event:: array.__new__ typecode,initializer array.array @@ -109,9 +136,9 @@ The module defines the following type: The length in bytes of one array item in the internal representation. - .. method:: append(x) + .. method:: append(value, /) - Append a new item with value *x* to the end of the array. + Append a new item with the specified value to the end of the array. .. method:: buffer_info() @@ -136,17 +163,18 @@ The module defines the following type: .. method:: byteswap() "Byteswap" all items of the array. This is only supported for values which are - 1, 2, 4, or 8 bytes in size; for other types of values, :exc:`RuntimeError` is + 1, 2, 4, 8 or 16 bytes in size; for other types of values, :exc:`RuntimeError` is raised. It is useful when reading data from a file written on a machine with a - different byte order. + different byte order. Note, that for complex types the order of + components (the real part, followed by imaginary part) is preserved. - .. method:: count(x) + .. method:: count(value, /) - Return the number of occurrences of *x* in the array. + Return the number of occurrences of *value* in the array. - .. method:: extend(iterable) + .. method:: extend(iterable, /) Append items from *iterable* to the end of the array. If *iterable* is another array, it must have *exactly* the same type code; if not, :exc:`TypeError` will @@ -154,7 +182,7 @@ The module defines the following type: must be the right type to be appended to the array. - .. method:: frombytes(buffer) + .. method:: frombytes(buffer, /) Appends items from the :term:`bytes-like object`, interpreting its content as an array of machine values (as if it had been read @@ -164,7 +192,7 @@ The module defines the following type: :meth:`!fromstring` is renamed to :meth:`frombytes` for clarity. - .. method:: fromfile(f, n) + .. method:: fromfile(f, n, /) Read *n* items (as machine values) from the :term:`file object` *f* and append them to the end of the array. If less than *n* items are available, @@ -172,47 +200,47 @@ The module defines the following type: inserted into the array. - .. method:: fromlist(list) + .. method:: fromlist(list, /) Append items from the list. This is equivalent to ``for x in list: a.append(x)`` except that if there is a type error, the array is unchanged. - .. method:: fromunicode(s) + .. method:: fromunicode(ustr, /) Extends this array with data from the given Unicode string. - The array must have type code ``'u'`` or ``'w'``; otherwise a :exc:`ValueError` is raised. + The array must have type code ``'w'``; otherwise a :exc:`ValueError` is raised. Use ``array.frombytes(unicodestring.encode(enc))`` to append Unicode data to an array of some other type. - .. method:: index(x[, start[, stop]]) + .. method:: index(value[, start[, stop]]) Return the smallest *i* such that *i* is the index of the first occurrence of - *x* in the array. The optional arguments *start* and *stop* can be - specified to search for *x* within a subsection of the array. Raise - :exc:`ValueError` if *x* is not found. + *value* in the array. The optional arguments *start* and *stop* can be + specified to search for *value* within a subsection of the array. Raise + :exc:`ValueError` if *value* is not found. .. versionchanged:: 3.10 Added optional *start* and *stop* parameters. - .. method:: insert(i, x) + .. method:: insert(index, value, /) - Insert a new item with value *x* in the array before position *i*. Negative + Insert a new item *value* in the array before position *index*. Negative values are treated as being relative to the end of the array. - .. method:: pop([i]) + .. method:: pop(index=-1, /) Removes the item with the index *i* from the array and returns it. The optional argument defaults to ``-1``, so that by default the last item is removed and returned. - .. method:: remove(x) + .. method:: remove(value, /) - Remove the first occurrence of *x* from the array. + Remove the first occurrence of *value* from the array. .. method:: clear() @@ -237,7 +265,7 @@ The module defines the following type: :meth:`!tostring` is renamed to :meth:`tobytes` for clarity. - .. method:: tofile(f) + .. method:: tofile(f, /) Write all items (as machine values) to the :term:`file object` *f*. @@ -249,7 +277,7 @@ The module defines the following type: .. method:: tounicode() - Convert the array to a Unicode string. The array must have a type ``'u'`` or ``'w'``; + Convert the array to a Unicode string. The array must have a type ``'w'``; otherwise a :exc:`ValueError` is raised. Use ``array.tobytes().decode(enc)`` to obtain a Unicode string from an array of some other type. @@ -257,7 +285,7 @@ The module defines the following type: The string representation of array objects has the form ``array(typecode, initializer)``. The *initializer* is omitted if the array is empty, otherwise it is -a Unicode string if the *typecode* is ``'u'`` or ``'w'``, otherwise it is +a Unicode string if the *typecode* is ``'w'``, otherwise it is a list of numbers. The string representation is guaranteed to be able to be converted back to an array with the same type and value using :func:`eval`, so long as the @@ -279,3 +307,5 @@ Examples:: `NumPy `_ The NumPy package defines another array type. + +.. _ieee 754 standard: https://en.wikipedia.org/wiki/IEEE_754-2008_revision diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index b24459b5c6346f..4809fdb42bf3d7 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -4,9 +4,6 @@ .. module:: ast :synopsis: Abstract Syntax Tree classes and manipulation. -.. sectionauthor:: Martin v. Löwis -.. sectionauthor:: Georg Brandl - .. testsetup:: import ast @@ -15,7 +12,7 @@ -------------- -The :mod:`ast` module helps Python applications to process trees of the Python +The :mod:`!ast` module helps Python applications to process trees of the Python abstract syntax grammar. The abstract syntax itself might change with each Python release; this module helps to find out programmatically what the current grammar looks like. @@ -38,15 +35,17 @@ The abstract grammar is currently defined as follows: :language: asdl +.. _ast_nodes: + Node classes ------------ .. class:: AST - This is the base of all AST node classes. The actual node classes are + This is the abstract base of all AST node classes. The actual node classes are derived from the :file:`Parser/Python.asdl` file, which is reproduced :ref:`above `. They are defined in the :mod:`!_ast` C - module and re-exported in :mod:`ast`. + module and re-exported in :mod:`!ast`. There is one class defined for each left-hand side symbol in the abstract grammar (for example, :class:`ast.stmt` or :class:`ast.expr`). In addition, @@ -134,17 +133,26 @@ Node classes Simple indices are represented by their value, extended slices are represented as tuples. +.. versionchanged:: 3.13 + + AST node constructors were changed to provide sensible defaults for omitted + fields: optional fields now default to ``None``, list fields default to an + empty list, and fields of type :class:`!ast.expr_context` default to + :class:`Load() `. Previously, omitted attributes would not exist on constructed + nodes (accessing them raised :exc:`AttributeError`). + .. versionchanged:: 3.14 The :meth:`~object.__repr__` output of :class:`~ast.AST` nodes includes the values of the node fields. -.. deprecated:: 3.8 +.. deprecated-removed:: 3.8 3.14 - Old classes :class:`!ast.Num`, :class:`!ast.Str`, :class:`!ast.Bytes`, - :class:`!ast.NameConstant` and :class:`!ast.Ellipsis` are still available, - but they will be removed in future Python releases. In the meantime, - instantiating them will return an instance of a different class. + Previous versions of Python provided the AST classes :class:`!ast.Num`, + :class:`!ast.Str`, :class:`!ast.Bytes`, :class:`!ast.NameConstant` and + :class:`!ast.Ellipsis`, which were deprecated in Python 3.8. These classes + were removed in Python 3.14, and their functionality has been replaced with + :class:`ast.Constant`. .. deprecated:: 3.9 @@ -158,8 +166,16 @@ Node classes Previous versions of Python allowed the creation of AST nodes that were missing required fields. Similarly, AST node constructors allowed arbitrary keyword arguments that were set as attributes of the AST node, even if they did not - match any of the fields of the AST node. This behavior is deprecated and will - be removed in Python 3.15. + match any of the fields of the AST node. These cases now raise a :exc:`TypeError`. + +.. deprecated-removed:: 3.15 3.20 + + In the :ref:`grammar above `, the AST node classes that + correspond to production rules with variants (aka "sums") are abstract + classes. Previous versions of Python allowed for the creation of direct + instances of these abstract node classes. This behavior is deprecated and + will be removed in Python 3.20. + .. note:: The descriptions of the specific node classes displayed here @@ -264,18 +280,25 @@ Root nodes Literals ^^^^^^^^ -.. class:: Constant(value) +.. class:: Constant(value, kind) A constant value. The ``value`` attribute of the ``Constant`` literal contains the Python object it represents. The values represented can be instances of :class:`str`, :class:`bytes`, :class:`int`, :class:`float`, :class:`complex`, and :class:`bool`, and the constants :data:`None` and :data:`Ellipsis`. + The ``kind`` attribute is an optional string. For string literals with a + ``u`` prefix, ``kind`` is set to ``'u'``. For all other + constants, ``kind`` is ``None``. + .. doctest:: >>> print(ast.dump(ast.parse('123', mode='eval'), indent=4)) Expression( body=Constant(value=123)) + >>> print(ast.dump(ast.parse("u'hello'", mode='eval'), indent=4)) + Expression( + body=Constant(value='hello', kind='u')) .. class:: FormattedValue(value, conversion, format_spec) @@ -289,9 +312,9 @@ Literals * ``conversion`` is an integer: * -1: no formatting - * 115 (``ord('s')``): ``!s`` string formatting - * 114 (``ord('r')``): ``!r`` repr formatting - * 97 (``ord('a')``): ``!a`` ASCII formatting + * 97 (``ord('a')``): ``!a`` :func:`ASCII ` formatting + * 114 (``ord('r')``): ``!r`` :func:`repr` formatting + * 115 (``ord('s')``): ``!s`` :func:`string ` formatting * ``format_spec`` is a :class:`JoinedStr` node representing the formatting of the value, or ``None`` if no format was specified. Both @@ -325,14 +348,18 @@ Literals Constant(value='.3')]))])) -.. class:: TemplateStr(values) +.. class:: TemplateStr(values, /) - A t-string, comprising a series of :class:`Interpolation` and :class:`Constant` - nodes. + .. versionadded:: 3.14 + + Node representing a template string literal, comprising a series of + :class:`Interpolation` and :class:`Constant` nodes. + These nodes may be any order, and do not need to be interleaved. .. doctest:: - >>> print(ast.dump(ast.parse('t"{name} finished {place:ordinal}"', mode='eval'), indent=4)) + >>> expr = ast.parse('t"{name} finished {place:ordinal}"', mode='eval') + >>> print(ast.dump(expr, indent=4)) Expression( body=TemplateStr( values=[ @@ -349,28 +376,33 @@ Literals values=[ Constant(value='ordinal')]))])) - .. versionadded:: 3.14 - +.. class:: Interpolation(value, str, conversion, format_spec=None) -.. class:: Interpolation(value, str, conversion, format_spec) + .. versionadded:: 3.14 - Node representing a single interpolation field in a t-string. + Node representing a single interpolation field in a template string literal. * ``value`` is any expression node (such as a literal, a variable, or a function call). + This has the same meaning as ``FormattedValue.value``. * ``str`` is a constant containing the text of the interpolation expression. + + If ``str`` is set to ``None``, then ``value`` is used to generate code + when calling :func:`ast.unparse`. This no longer guarantees that the + generated code is identical to the original and is intended for code + generation. * ``conversion`` is an integer: * -1: no conversion - * 115: ``!s`` string conversion - * 114: ``!r`` repr conversion - * 97: ``!a`` ascii conversion + * 97 (``ord('a')``): ``!a`` :func:`ASCII ` conversion + * 114 (``ord('r')``): ``!r`` :func:`repr` conversion + * 115 (``ord('s')``): ``!s`` :func:`string ` conversion + This has the same meaning as ``FormattedValue.conversion``. * ``format_spec`` is a :class:`JoinedStr` node representing the formatting of the value, or ``None`` if no format was specified. Both ``conversion`` and ``format_spec`` can be set at the same time. - - .. versionadded:: 3.14 + This has the same meaning as ``FormattedValue.format_spec``. .. class:: List(elts, ctx) @@ -1104,7 +1136,8 @@ Imports names=[ alias(name='x'), alias(name='y'), - alias(name='z')])]) + alias(name='z')], + is_lazy=0)]) .. class:: ImportFrom(module, names, level) @@ -1125,7 +1158,8 @@ Imports alias(name='x'), alias(name='y'), alias(name='z')], - level=0)]) + level=0, + is_lazy=0)]) .. class:: alias(name, asname) @@ -1143,7 +1177,8 @@ Imports names=[ alias(name='a', asname='b'), alias(name='c')], - level=2)]) + level=2, + is_lazy=0)]) Control flow ^^^^^^^^^^^^ @@ -2190,16 +2225,16 @@ Async and await occurrences of the same value (for example, :class:`ast.Add`). -:mod:`ast` helpers ------------------- +:mod:`!ast` helpers +------------------- -Apart from the node classes, the :mod:`ast` module defines these utility functions +Apart from the node classes, the :mod:`!ast` module defines these utility functions and classes for traversing abstract syntax trees: -.. function:: parse(source, filename='', mode='exec', *, type_comments=False, feature_version=None, optimize=-1) +.. function:: parse(source, filename='', mode='exec', *, type_comments=False, feature_version=None, optimize=-1, module=None) Parse the source into an AST node. Equivalent to ``compile(source, - filename, mode, flags=FLAGS_VALUE, optimize=optimize)``, + filename, mode, flags=FLAGS_VALUE, optimize=optimize, module=module)``, where ``FLAGS_VALUE`` is ``ast.PyCF_ONLY_AST`` if ``optimize <= 0`` and ``ast.PyCF_OPTIMIZED_AST`` otherwise. @@ -2252,6 +2287,9 @@ and classes for traversing abstract syntax trees: The minimum supported version for ``feature_version`` is now ``(3, 7)``. The ``optimize`` argument was added. + .. versionadded:: 3.15 + Added the *module* parameter. + .. function:: unparse(ast_obj) @@ -2407,12 +2445,12 @@ and classes for traversing abstract syntax trees: during traversal. For this a special visitor exists (:class:`NodeTransformer`) that allows modifications. - .. deprecated:: 3.8 + .. deprecated-removed:: 3.8 3.14 Methods :meth:`!visit_Num`, :meth:`!visit_Str`, :meth:`!visit_Bytes`, - :meth:`!visit_NameConstant` and :meth:`!visit_Ellipsis` are deprecated - now and will not be called in future Python versions. Add the - :meth:`visit_Constant` method to handle all constant nodes. + :meth:`!visit_NameConstant` and :meth:`!visit_Ellipsis` will not be called + in Python 3.14+. Add the :meth:`visit_Constant` method instead to handle + all constant nodes. .. class:: NodeTransformer() @@ -2459,7 +2497,7 @@ and classes for traversing abstract syntax trees: node = YourTransformer().visit(node) -.. function:: dump(node, annotate_fields=True, include_attributes=False, *, indent=None, show_empty=False) +.. function:: dump(node, annotate_fields=True, include_attributes=False, *, color=False, indent=None, show_empty=False) Return a formatted dump of the tree in *node*. This is mainly useful for debugging purposes. If *annotate_fields* is true (by default), @@ -2469,6 +2507,10 @@ and classes for traversing abstract syntax trees: numbers and column offsets are not dumped by default. If this is wanted, *include_attributes* can be set to true. + If *color* is ``True``, the returned string is syntax highlighted using + ANSI escape sequences. + If ``False`` (the default), colored output is always disabled. + If *indent* is a non-negative integer or string, then the tree will be pretty-printed with that indent level. An indent level of 0, negative, or ``""`` will only insert newlines. ``None`` (the default) @@ -2503,9 +2545,26 @@ and classes for traversing abstract syntax trees: .. versionchanged:: 3.13 Added the *show_empty* option. - .. versionchanged:: next + .. versionchanged:: 3.15 Omit optional ``Load()`` values by default. + .. versionchanged:: 3.15 + Added the *color* parameter. + + +.. function:: compare(a, b, /, *, compare_attributes=False) + + Recursively compares two ASTs. + + *compare_attributes* affects whether AST attributes are considered + in the comparison. If *compare_attributes* is ``False`` (default), then + attributes are ignored. Otherwise they must all be equal. This + option is useful to check whether the ASTs are structurally equal but + differ in whitespace or similar details. Attributes include line numbers + and column offsets. + + .. versionadded:: 3.14 + .. _ast-compiler-flags: @@ -2542,20 +2601,6 @@ effects on the compilation of a program: .. versionadded:: 3.8 -.. function:: compare(a, b, /, *, compare_attributes=False) - - Recursively compares two ASTs. - - *compare_attributes* affects whether AST attributes are considered - in the comparison. If *compare_attributes* is ``False`` (default), then - attributes are ignored. Otherwise they must all be equal. This - option is useful to check whether the ASTs are structurally equal but - differ in whitespace or similar details. Attributes include line numbers - and column offsets. - - .. versionadded:: 3.14 - - .. _ast-cli: Command-line usage @@ -2563,7 +2608,11 @@ Command-line usage .. versionadded:: 3.9 -The :mod:`ast` module can be executed as a script from the command line. +.. versionchanged:: 3.15 + The output is now syntax highlighted by default. This can be + :ref:`controlled using environment variables `. + +The :mod:`!ast` module can be executed as a script from the command line. It is as simple as: .. code-block:: sh diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst index 7831b613bd4a60..713b40d746680a 100644 --- a/Doc/library/asyncio-dev.rst +++ b/Doc/library/asyncio-dev.rst @@ -248,3 +248,225 @@ Output in debug mode:: File "../t.py", line 4, in bug raise Exception("not consumed") Exception: not consumed + + +Asynchronous generators best practices +====================================== + +Writing correct and efficient asyncio code requires awareness of certain pitfalls. +This section outlines essential best practices that can save you hours of debugging. + + +Close asynchronous generators explicitly +---------------------------------------- + +It is recommended to manually close the +:term:`asynchronous generator `. If a generator +exits early - for example, due to an exception raised in the body of +an ``async for`` loop - its asynchronous cleanup code may run in an +unexpected context. This can occur after the tasks it depends on have completed, +or during the event loop shutdown when the async-generator's garbage collection +hook is called. + +To avoid this, explicitly close the generator by calling its +:meth:`~agen.aclose` method, or use the :func:`contextlib.aclosing` +context manager:: + + import asyncio + import contextlib + + async def gen(): + yield 1 + yield 2 + + async def func(): + async with contextlib.aclosing(gen()) as g: + async for x in g: + break # Don't iterate until the end + + asyncio.run(func()) + +As noted above, the cleanup code for these asynchronous generators is deferred. +The following example demonstrates that the finalization of an asynchronous +generator can occur in an unexpected order:: + + import asyncio + work_done = False + + async def cursor(): + try: + yield 1 + finally: + assert work_done + + async def rows(): + global work_done + try: + yield 2 + finally: + await asyncio.sleep(0.1) # imitate some async work + work_done = True + + + async def main(): + async for c in cursor(): + async for r in rows(): + break + break + + asyncio.run(main()) + +For this example, we get the following output:: + + unhandled exception during asyncio.run() shutdown + task: ()> exception=AssertionError()> + Traceback (most recent call last): + File "example.py", line 6, in cursor + yield 1 + asyncio.exceptions.CancelledError + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "example.py", line 8, in cursor + assert work_done + ^^^^^^^^^ + AssertionError + +The ``cursor()`` asynchronous generator was finalized before the ``rows`` +generator - an unexpected behavior. + +The example can be fixed by explicitly closing the +``cursor`` and ``rows`` async-generators:: + + async def main(): + async with contextlib.aclosing(cursor()) as cursor_gen: + async for c in cursor_gen: + async with contextlib.aclosing(rows()) as rows_gen: + async for r in rows_gen: + break + break + + +Create asynchronous generators only when the event loop is running +------------------------------------------------------------------ + +It is recommended to create +:term:`asynchronous generators ` only after +the event loop has been created. + +To ensure that asynchronous generators close reliably, the event loop uses the +:func:`sys.set_asyncgen_hooks` function to register callback functions. These +callbacks update the list of running asynchronous generators to keep it in a +consistent state. + +When the :meth:`loop.shutdown_asyncgens() ` +function is called, the running generators are stopped gracefully and the +list is cleared. + +The asynchronous generator invokes the corresponding system hook during its +first iteration. At the same time, the generator records that the hook has +been called and does not call it again. + +Therefore, if iteration begins before the event loop is created, +the event loop will not be able to add the generator to its list of active +generators because the hooks are set after the generator attempts to call them. +Consequently, the event loop will not be able to terminate the generator +if necessary. + +Consider the following example:: + + import asyncio + + async def agenfn(): + try: + yield 10 + finally: + await asyncio.sleep(0) + + + with asyncio.Runner() as runner: + agen = agenfn() + print(runner.run(anext(agen))) + del agen + +Output:: + + 10 + Exception ignored while closing generator : + Traceback (most recent call last): + File "example.py", line 13, in + del agen + ^^^^ + RuntimeError: async generator ignored GeneratorExit + +This example can be fixed as follows:: + + import asyncio + + async def agenfn(): + try: + yield 10 + finally: + await asyncio.sleep(0) + + async def main(): + agen = agenfn() + print(await anext(agen)) + del agen + + asyncio.run(main()) + + +Avoid concurrent iteration and closure of the same generator +------------------------------------------------------------ + +Async generators may be reentered while another +:meth:`~agen.__anext__` / :meth:`~agen.athrow` / :meth:`~agen.aclose` call is in +progress. This may lead to an inconsistent state of the async generator and can +cause errors. + +Let's consider the following example:: + + import asyncio + + async def consumer(): + for idx in range(100): + await asyncio.sleep(0) + message = yield idx + print('received', message) + + async def amain(): + agenerator = consumer() + await agenerator.asend(None) + + fa = asyncio.create_task(agenerator.asend('A')) + fb = asyncio.create_task(agenerator.asend('B')) + await fa + await fb + + asyncio.run(amain()) + +Output:: + + received A + Traceback (most recent call last): + File "test.py", line 38, in + asyncio.run(amain()) + ~~~~~~~~~~~^^^^^^^^^ + File "Lib/asyncio/runners.py", line 204, in run + return runner.run(main) + ~~~~~~~~~~^^^^^^ + File "Lib/asyncio/runners.py", line 127, in run + return self._loop.run_until_complete(task) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^ + File "Lib/asyncio/base_events.py", line 719, in run_until_complete + return future.result() + ~~~~~~~~~~~~~^^ + File "test.py", line 36, in amain + await fb + RuntimeError: anext(): asynchronous generator is already running + + +Therefore, it is recommended to avoid using asynchronous generators in parallel +tasks or across multiple event loops. diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst index 91970c282391f7..79c9516cda2d60 100644 --- a/Doc/library/asyncio-eventloop.rst +++ b/Doc/library/asyncio-eventloop.rst @@ -4,7 +4,7 @@ .. _asyncio-event-loop: ========== -Event Loop +Event loop ========== **Source code:** :source:`Lib/asyncio/events.py`, @@ -105,7 +105,7 @@ This documentation page contains the following sections: .. _asyncio-event-loop-methods: -Event Loop Methods +Event loop methods ================== Event loops have **low-level** APIs for the following: @@ -297,13 +297,20 @@ clocks to track time. are called is undefined. The optional positional *args* will be passed to the callback when - it is called. If you want the callback to be called with keyword - arguments use :func:`functools.partial`. + it is called. Use :func:`functools.partial` + :ref:`to pass keyword arguments ` to + *callback*. An optional keyword-only *context* argument allows specifying a custom :class:`contextvars.Context` for the *callback* to run in. The current context is used when no *context* is provided. + .. note:: + + For performance, callbacks scheduled with :meth:`loop.call_later` + may run up to one clock-resolution early (see + ``time.get_clock_info('monotonic').resolution``). + .. versionchanged:: 3.7 The *context* keyword-only parameter was added. See :pep:`567` for more details. @@ -324,6 +331,12 @@ clocks to track time. An instance of :class:`asyncio.TimerHandle` is returned which can be used to cancel the callback. + .. note:: + + For performance, callbacks scheduled with :meth:`loop.call_at` + may run up to one clock-resolution early (see + ``time.get_clock_info('monotonic').resolution``). + .. versionchanged:: 3.7 The *context* keyword-only parameter was added. See :pep:`567` for more details. @@ -348,7 +361,7 @@ clocks to track time. The :func:`asyncio.sleep` function. -Creating Futures and Tasks +Creating futures and tasks ^^^^^^^^^^^^^^^^^^^^^^^^^^ .. method:: loop.create_future() @@ -611,6 +624,12 @@ Opening network connections to bind the socket locally. The *local_host* and *local_port* are looked up using :meth:`getaddrinfo`. + .. note:: + + On Windows, when using the proactor event loop with ``local_addr=None``, + an :exc:`OSError` with :attr:`!errno.WSAEINVAL` will be raised + when running it. + * *remote_addr*, if given, is a ``(remote_host, remote_port)`` tuple used to connect the socket to a remote address. The *remote_host* and *remote_port* are looked up using :meth:`getaddrinfo`. @@ -943,7 +962,7 @@ Transferring files .. versionadded:: 3.7 -TLS Upgrade +TLS upgrade ^^^^^^^^^^^ .. method:: loop.start_tls(transport, protocol, \ @@ -1016,8 +1035,8 @@ Watching file descriptors .. method:: loop.add_writer(fd, callback, *args) Start monitoring the *fd* file descriptor for write availability and - invoke *callback* with the specified arguments once *fd* is available for - writing. + invoke *callback* with the specified arguments *args* once *fd* is + available for writing. Any preexisting callback registered for *fd* is cancelled and replaced by *callback*. @@ -1290,7 +1309,8 @@ Unix signals .. method:: loop.add_signal_handler(signum, callback, *args) - Set *callback* as the handler for the *signum* signal. + Set *callback* as the handler for the *signum* signal, + passing *args* as positional arguments. The callback will be invoked by *loop*, along with other queued callbacks and runnable coroutines of that event loop. Unlike signal handlers @@ -1325,7 +1345,8 @@ Executing code in thread or process pools .. awaitablemethod:: loop.run_in_executor(executor, func, *args) - Arrange for *func* to be called in the specified executor. + Arrange for *func* to be called in the specified executor + passing *args* as positional arguments. The *executor* argument should be an :class:`concurrent.futures.Executor` instance. The default executor is used if *executor* is ``None``. @@ -1410,7 +1431,7 @@ Executing code in thread or process pools :class:`~concurrent.futures.ThreadPoolExecutor`. -Error Handling API +Error handling API ^^^^^^^^^^^^^^^^^^ Allows customizing how exceptions are handled in the event loop. @@ -1513,7 +1534,7 @@ Enabling debug mode The :ref:`debug mode of asyncio `. -Running Subprocesses +Running subprocesses ^^^^^^^^^^^^^^^^^^^^ Methods described in this subsections are low-level. In regular @@ -1613,6 +1634,9 @@ async/await code consider using the high-level conforms to the :class:`asyncio.SubprocessTransport` base class and *protocol* is an object instantiated by the *protocol_factory*. + If the transport is closed or is garbage collected, the child process + is killed if it is still running. + .. method:: loop.subprocess_shell(protocol_factory, cmd, *, \ stdin=subprocess.PIPE, stdout=subprocess.PIPE, \ stderr=subprocess.PIPE, **kwargs) @@ -1636,6 +1660,9 @@ async/await code consider using the high-level conforms to the :class:`SubprocessTransport` base class and *protocol* is an object instantiated by the *protocol_factory*. + If the transport is closed or is garbage collected, the child process + is killed if it is still running. + .. note:: It is the application's responsibility to ensure that all whitespace and special characters are quoted appropriately to avoid `shell injection @@ -1645,7 +1672,7 @@ async/await code consider using the high-level are going to be used to construct shell commands. -Callback Handles +Callback handles ================ .. class:: Handle @@ -1688,7 +1715,7 @@ Callback Handles .. versionadded:: 3.7 -Server Objects +Server objects ============== Server objects are created by :meth:`loop.create_server`, @@ -1831,7 +1858,7 @@ Do not instantiate the :class:`Server` class directly. .. _asyncio-event-loops: .. _asyncio-event-loop-implementations: -Event Loop Implementations +Event loop implementations ========================== asyncio ships with two different event loop implementations: @@ -1944,10 +1971,10 @@ callback uses the :meth:`loop.call_later` method to reschedule itself after 5 seconds, and then stops the event loop:: import asyncio - import datetime + import datetime as dt def display_date(end_time, loop): - print(datetime.datetime.now()) + print(dt.datetime.now()) if (loop.time() + 1.0) < end_time: loop.call_later(1, display_date, end_time, loop) else: @@ -2028,7 +2055,7 @@ Wait until a file descriptor received some data using the Set signal handlers for SIGINT and SIGTERM ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -(This ``signals`` example only works on Unix.) +(This ``signal`` example only works on Unix.) Register handlers for signals :const:`~signal.SIGINT` and :const:`~signal.SIGTERM` using the :meth:`loop.add_signal_handler` method:: diff --git a/Doc/library/asyncio-future.rst b/Doc/library/asyncio-future.rst index 32771ba72e0002..195d99123dbd36 100644 --- a/Doc/library/asyncio-future.rst +++ b/Doc/library/asyncio-future.rst @@ -75,6 +75,7 @@ Future Functions Deprecation warning is emitted if *future* is not a Future-like object and *loop* is not specified and there is no running event loop. +.. _asyncio-future-obj: Future Object ============= @@ -100,6 +101,8 @@ Future Object implementations can inject their own optimized implementations of a Future object. + Futures are :ref:`generic ` over the type of their results. + .. versionchanged:: 3.7 Added support for the :mod:`contextvars` module. @@ -195,6 +198,10 @@ Future Object Otherwise, change the Future's state to *cancelled*, schedule the callbacks, and return ``True``. + The optional string argument *msg* is passed as the argument to the + :exc:`CancelledError` exception raised when a cancelled Future + is awaited. + .. versionchanged:: 3.9 Added the *msg* parameter. diff --git a/Doc/library/asyncio-protocol.rst b/Doc/library/asyncio-protocol.rst index 7c08d65f26bc27..58f77feb311984 100644 --- a/Doc/library/asyncio-protocol.rst +++ b/Doc/library/asyncio-protocol.rst @@ -390,11 +390,11 @@ Subprocess Transports Return the transport for the communication pipe corresponding to the integer file descriptor *fd*: - * ``0``: readable streaming transport of the standard input (*stdin*), + * ``0``: writable streaming transport of the standard input (*stdin*), or :const:`None` if the subprocess was not created with ``stdin=PIPE`` - * ``1``: writable streaming transport of the standard output (*stdout*), + * ``1``: readable streaming transport of the standard output (*stdout*), or :const:`None` if the subprocess was not created with ``stdout=PIPE`` - * ``2``: writable streaming transport of the standard error (*stderr*), + * ``2``: readable streaming transport of the standard error (*stderr*), or :const:`None` if the subprocess was not created with ``stderr=PIPE`` * other *fd*: :const:`None` @@ -1037,7 +1037,7 @@ The subprocess is created by the :meth:`loop.subprocess_exec` method:: # low-level APIs. loop = asyncio.get_running_loop() - code = 'import datetime; print(datetime.datetime.now())' + code = 'import datetime as dt; print(dt.datetime.now())' exit_future = asyncio.Future(loop=loop) # Create the subprocess controlled by DateProtocol; diff --git a/Doc/library/asyncio-queue.rst b/Doc/library/asyncio-queue.rst index 963bc1fb82c12f..a9735ae80652df 100644 --- a/Doc/library/asyncio-queue.rst +++ b/Doc/library/asyncio-queue.rst @@ -107,7 +107,7 @@ Queue The queue can no longer grow. Future calls to :meth:`~Queue.put` raise :exc:`QueueShutDown`. Currently blocked callers of :meth:`~Queue.put` will be unblocked - and will raise :exc:`QueueShutDown` in the formerly blocked thread. + and will raise :exc:`QueueShutDown` in the formerly awaiting task. If *immediate* is false (the default), the queue can be wound down normally with :meth:`~Queue.get` calls to extract tasks @@ -120,9 +120,10 @@ Queue raise :exc:`QueueShutDown`. If *immediate* is true, the queue is terminated immediately. - The queue is drained to be completely empty. All callers of - :meth:`~Queue.join` are unblocked regardless of the number - of unfinished tasks. Blocked callers of :meth:`~Queue.get` + The queue is drained to be completely empty and the count + of unfinished tasks is reduced by the number of tasks drained. + If unfinished tasks is zero, callers of :meth:`~Queue.join` + are unblocked. Also, blocked callers of :meth:`~Queue.get` are unblocked and will raise :exc:`QueueShutDown` because the queue is empty. diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst index 90c90862ca1ed3..05445219510ca5 100644 --- a/Doc/library/asyncio-stream.rst +++ b/Doc/library/asyncio-stream.rst @@ -316,11 +316,15 @@ StreamWriter If that fails, the data is queued in an internal write buffer until it can be sent. + The *data* buffer should be a bytes, bytearray, or C-contiguous one-dimensional + memoryview object. + The method should be used along with the ``drain()`` method:: stream.write(data) await stream.drain() + .. method:: writelines(data) The method writes a list (or any iterable) of bytes to the underlying socket diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst index 03e76bc868905e..a6514649bf9a0a 100644 --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -76,6 +76,9 @@ Creating Subprocesses See the documentation of :meth:`loop.subprocess_exec` for other parameters. + If the process object is garbage collected while the process is still + running, the child process will be killed. + .. versionchanged:: 3.10 Removed the *loop* parameter. @@ -95,6 +98,9 @@ Creating Subprocesses See the documentation of :meth:`loop.subprocess_shell` for other parameters. + If the process object is garbage collected while the process is still + running, the child process will be killed. + .. important:: It is the application's responsibility to ensure that all whitespace and @@ -305,8 +311,16 @@ their completion. A ``None`` value indicates that the process has not terminated yet. - A negative value ``-N`` indicates that the child was terminated - by signal ``N`` (POSIX only). + For processes created with :func:`~asyncio.create_subprocess_exec`, a negative + value ``-N`` indicates that the child was terminated by signal ``N`` + (POSIX only). + + For processes created with :func:`~asyncio.create_subprocess_shell`, the + return code reflects the exit status of the shell itself (e.g. ``/bin/sh``), + which may map signals to codes such as ``128+N``. See the + documentation of the shell (for example, the Bash manual's Exit Status) + for details. + .. _asyncio-subprocess-threads: @@ -345,7 +359,7 @@ function:: import sys async def get_date(): - code = 'import datetime; print(datetime.datetime.now())' + code = 'import datetime as dt; print(dt.datetime.now())' # Create the subprocess; redirect the standard output # into a pipe. diff --git a/Doc/library/asyncio-sync.rst b/Doc/library/asyncio-sync.rst index 968c812ee3c8e6..f9e98e05cab7ac 100644 --- a/Doc/library/asyncio-sync.rst +++ b/Doc/library/asyncio-sync.rst @@ -157,7 +157,7 @@ Event Clear (unset) the event. - Tasks awaiting on :meth:`~Event.wait` will now block until the + Subsequent tasks awaiting on :meth:`~Event.wait` will now block until the :meth:`~Event.set` method is called again. .. method:: is_set() diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index b19ffa8213a971..64f0810777e41b 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -2,7 +2,7 @@ ==================== -Coroutines and Tasks +Coroutines and tasks ==================== This section outlines high-level asyncio APIs to work with coroutines @@ -231,7 +231,7 @@ A good example of a low-level function that returns a Future object is :meth:`loop.run_in_executor`. -Creating Tasks +Creating tasks ============== **Source code:** :source:`Lib/asyncio/tasks.py` @@ -300,7 +300,7 @@ Creating Tasks Added the *eager_start* parameter by passing on all *kwargs*. -Task Cancellation +Task cancellation ================= Tasks can easily and safely be cancelled. @@ -324,7 +324,7 @@ remove the cancellation state. .. _taskgroups: -Task Groups +Task groups =========== Task groups combine a task creation API with a convenient @@ -355,6 +355,34 @@ and reliable way to wait for all tasks in the group to finish. Passes on all *kwargs* to :meth:`loop.create_task` + .. method:: cancel() + + Cancel the task group. This is a non-exceptional, early exit of the + task group's lifetime -- useful once the group's goal has been met or + its services no longer needed. + + :meth:`~asyncio.Task.cancel` will be called on any tasks in the group that + aren't yet done, as well as the parent (body) of the group. The task group + context manager will exit *without* :exc:`asyncio.CancelledError` being raised. + + If :meth:`cancel` is called before entering the task group, the group will be + cancelled upon entry. This is useful for patterns where one piece of + code passes an unused :class:`asyncio.TaskGroup` instance to another in order to have + the ability to cancel anything run within the group. + + :meth:`cancel` is idempotent and may be called after the task group has + already exited. + + Some ways to use :meth:`cancel`: + + * call it from the task group body based on some condition or event + * pass the task group instance to child tasks via :meth:`create_task`, allowing a child + task to conditionally cancel the entire entire group + * pass the task group instance or bound :meth:`cancel` method to some other task *before* + opening the task group, allowing remote cancellation + + .. versionadded:: 3.15 + Example:: async def main(): @@ -366,7 +394,8 @@ Example:: The ``async with`` statement will wait for all tasks in the group to finish. While waiting, new tasks may still be added to the group (for example, by passing ``tg`` into one of the coroutines -and calling ``tg.create_task()`` in that coroutine). +and calling ``tg.create_task()`` in that coroutine). There is also opportunity to +request termination of the entire task group with ``tg.cancel()``, based on some condition. Once the last task has finished and the ``async with`` block is exited, no new tasks may be added to the group. @@ -427,53 +456,6 @@ reported by :meth:`asyncio.Task.cancelling`. Improved handling of simultaneous internal and external cancellations and correct preservation of cancellation counts. -Terminating a Task Group ------------------------- - -While terminating a task group is not natively supported by the standard -library, termination can be achieved by adding an exception-raising task -to the task group and ignoring the raised exception: - -.. code-block:: python - - import asyncio - from asyncio import TaskGroup - - class TerminateTaskGroup(Exception): - """Exception raised to terminate a task group.""" - - async def force_terminate_task_group(): - """Used to force termination of a task group.""" - raise TerminateTaskGroup() - - async def job(task_id, sleep_time): - print(f'Task {task_id}: start') - await asyncio.sleep(sleep_time) - print(f'Task {task_id}: done') - - async def main(): - try: - async with TaskGroup() as group: - # spawn some tasks - group.create_task(job(1, 0.5)) - group.create_task(job(2, 1.5)) - # sleep for 1 second - await asyncio.sleep(1) - # add an exception-raising task to force the group to terminate - group.create_task(force_terminate_task_group()) - except* TerminateTaskGroup: - pass - - asyncio.run(main()) - -Expected output: - -.. code-block:: text - - Task 1: start - Task 2: start - Task 1: done - Sleeping ======== @@ -498,13 +480,13 @@ Sleeping for 5 seconds:: import asyncio - import datetime + import datetime as dt async def display_date(): loop = asyncio.get_running_loop() end_time = loop.time() + 5.0 while True: - print(datetime.datetime.now()) + print(dt.datetime.now()) if (loop.time() + 1.0) >= end_time: break await asyncio.sleep(1) @@ -519,7 +501,7 @@ Sleeping Raises :exc:`ValueError` if *delay* is :data:`~math.nan`. -Running Tasks Concurrently +Running tasks concurrently ========================== .. awaitablefunction:: gather(*aws, return_exceptions=False) @@ -557,7 +539,7 @@ Running Tasks Concurrently provides stronger safety guarantees than *gather* for scheduling a nesting of subtasks: if a task (or a subtask, a task scheduled by a task) raises an exception, *TaskGroup* will, while *gather* will not, - cancel the remaining scheduled tasks). + cancel the remaining scheduled tasks. .. _asyncio_example_gather: @@ -621,7 +603,7 @@ Running Tasks Concurrently .. _eager-task-factory: -Eager Task Factory +Eager task factory ================== .. function:: eager_task_factory(loop, coro, *, name=None, context=None) @@ -664,7 +646,7 @@ Eager Task Factory .. versionadded:: 3.12 -Shielding From Cancellation +Shielding from cancellation =========================== .. awaitablefunction:: shield(aw) @@ -771,6 +753,9 @@ Timeouts An :ref:`asynchronous context manager ` for cancelling overdue coroutines. + Prefer using :func:`asyncio.timeout` or :func:`asyncio.timeout_at` + rather than instantiating :class:`!Timeout` directly. + ``when`` should be an absolute time at which the context should time out, as measured by the event loop's clock: @@ -891,7 +876,7 @@ Timeouts Raises :exc:`TimeoutError` instead of :exc:`asyncio.TimeoutError`. -Waiting Primitives +Waiting primitives ================== .. function:: wait(aws, *, timeout=None, return_when=ALL_COMPLETED) @@ -1011,7 +996,7 @@ Waiting Primitives or as a plain :term:`iterator` (previously it was only a plain iterator). -Running in Threads +Running in threads ================== .. function:: to_thread(func, /, *args, **kwargs) @@ -1071,7 +1056,7 @@ Running in Threads .. versionadded:: 3.9 -Scheduling From Other Threads +Scheduling from other threads ============================= .. function:: run_coroutine_threadsafe(coro, loop) @@ -1193,8 +1178,9 @@ Introspection .. versionadded:: 3.4 +.. _asyncio-task-obj: -Task Object +Task object =========== .. class:: Task(coro, *, loop=None, name=None, context=None, eager_start=False) @@ -1220,8 +1206,8 @@ Task Object To cancel a running Task use the :meth:`cancel` method. Calling it will cause the Task to throw a :exc:`CancelledError` exception into - the wrapped coroutine. If a coroutine is awaiting on a Future - object during cancellation, the Future object will be cancelled. + the wrapped coroutine. If a coroutine is awaiting on a future-like + object during cancellation, the awaited object will be cancelled. :meth:`cancelled` can be used to check if the Task was cancelled. The method returns ``True`` if the wrapped coroutine did not @@ -1244,6 +1230,9 @@ Task Object blocks. If the coroutine returns or raises without blocking, the task will be finished eagerly and will skip scheduling to the event loop. + Tasks are :ref:`generic ` over the return type of their wrapped + coroutines. + .. versionchanged:: 3.7 Added support for the :mod:`contextvars` module. @@ -1410,6 +1399,10 @@ Task Object the cancellation, it needs to call :meth:`Task.uncancel` in addition to catching the exception. + If the Task being cancelled is currently awaiting on a future-like + object, that awaited object will also be cancelled. This cancellation + propagates down the entire chain of awaited objects. + .. versionchanged:: 3.9 Added the *msg* parameter. diff --git a/Doc/library/asyncio-threading.rst b/Doc/library/asyncio-threading.rst new file mode 100644 index 00000000000000..526901a2e7eb20 --- /dev/null +++ b/Doc/library/asyncio-threading.rst @@ -0,0 +1,154 @@ +.. currentmodule:: asyncio + +.. _asyncio-threading: + +asyncio and free-threaded Python +================================ + +asyncio uses an event loop as a scheduler to enable highly efficient +concurrency by switching between tasks to allow non-blocking I/O +operations. This results in better performance for I/O-bound use +cases. It also allows off-loading CPU-bound work to a thread or +process pool, but that is still limited by the :term:`global +interpreter lock` in CPython. + +However, in :ref:`free-threaded Python `, +the GIL is disabled and Python can run true multi-threaded code. This +means that asyncio can now take advantage of multiple CPU cores without +the limitations imposed by the GIL. + +Since Python 3.14, asyncio has first-class support for free-threaded +Python, and the implementation of asyncio is safe to use in a +multi-threaded environment. + +A single event loop on one core can handle many connections +concurrently, but the Python code that runs to handle each one still +executes serially. Once requests involve a non-trivial amount of +per-request computation, that handling becomes the bottleneck, and a +single core can no longer keep up. Combining asyncio with threads is +most useful here: by running an event loop per thread, the handling of +different requests can run in parallel across multiple CPU cores. It is +also useful when you need to run blocking or CPU-bound code from an +asyncio application. + + +.. seealso:: + + `Scaling asyncio on Free-Threaded Python + `__, + a blog post by Kumar Aditya which explains the internal changes + that make asyncio safe and efficient under free-threaded Python, + together with benchmarks of the resulting improvements. + + +Thread safety considerations +---------------------------- + +While asyncio is designed to be thread-safe in a free-threaded Python +environment, there are still some considerations to keep in mind when +using asyncio with threads: + +1. **Event loop**: Each thread should have its own event loop which + should not be shared across threads. This ensures that the event loop + can manage its own tasks and callbacks without interference from + other threads. + +2. **Task management**: Tasks and futures created in one thread should + not be awaited or manipulated from another thread. + +3. **Thread-safe APIs**: When interacting with asyncio from multiple + threads, it's important to use thread-safe APIs provided by asyncio, + such as :func:`asyncio.run_coroutine_threadsafe` for submitting + coroutines to an event loop from another thread. If you need to + call a callback from a different thread, you can use + :meth:`loop.call_soon_threadsafe` to schedule it safely. + +4. **Synchronization**: The synchronization primitives provided by + asyncio (like :class:`asyncio.Lock` and :class:`asyncio.Event`) + are not designed to be used across threads. If you need to + synchronize between threads, you should use the synchronization + primitives from the :mod:`threading` module instead. + + +Using asyncio with threads +-------------------------- + +asyncio supports running one event loop per thread, which allows you to +take advantage of multiple CPU cores in a free-threaded Python +environment. Each thread can run its own event loop, and tasks can be +scheduled on those loops independently. + +Here's an example of how to use asyncio with threads:: + + import asyncio + import threading + + async def worker(name: str) -> None: + print(f"Worker {name} starting") + await asyncio.sleep(1) + print(f"Worker {name} done") + + def run_loop(name: str) -> None: + asyncio.run(worker(name)) + + threads = [ + threading.Thread(target=run_loop, args=(f"T{i}",)) + for i in range(4) + ] + for t in threads: + t.start() + for t in threads: + t.join() + +In this example, each thread creates its own event loop with +:func:`asyncio.run` and runs a coroutine on it. The threads execute +concurrently, and in a free-threaded build they can run on separate +CPU cores in parallel. + + +Producer/consumer across threads +-------------------------------- + +When a regular (non-asyncio) thread needs to hand work to an asyncio +event loop running in another thread, use a thread-safe primitive such +as :class:`queue.Queue` rather than :class:`asyncio.Queue`, which is +only safe within a single event loop.:: + + import asyncio + import queue + import threading + + def producer(q: queue.Queue[int]) -> None: + for i in range(5): + print(f"Producing {i}") + q.put(i) + q.shutdown() + + async def consumer(q: queue.Queue[int]) -> None: + while True: + try: + item = q.get_nowait() + except queue.Empty: + await asyncio.sleep(0.1) + continue + except queue.ShutDown: + break + print(f"Consumed {item}") + await asyncio.sleep(item) + + q: queue.Queue[int] = queue.Queue() + consumer_thread = threading.Thread( + target=lambda: asyncio.run(consumer(q)) + ) + consumer_thread.start() + producer(q) + consumer_thread.join() + +The producer runs on the main thread while the consumer runs inside an +event loop on its own thread, yet they communicate safely through +``queue.Queue``. When the queue is empty the consumer sleeps briefly +and tries again. When the producer is done it calls +:meth:`~queue.Queue.shutdown`, which causes subsequent +:meth:`~queue.Queue.get_nowait` calls to raise :exc:`queue.ShutDown` +so the consumer can exit cleanly. + diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst index 7d368dae49dc1d..90a465f3e1d3af 100644 --- a/Doc/library/asyncio.rst +++ b/Doc/library/asyncio.rst @@ -29,6 +29,11 @@ database connection libraries, distributed task queues, etc. asyncio is often a perfect fit for IO-bound and high-level **structured** network code. +.. seealso:: + + :ref:`a-conceptual-overview-of-asyncio` + Explanation of the fundamentals of asyncio. + asyncio provides a set of **high-level** APIs to: * :ref:`run Python coroutines ` concurrently and @@ -74,6 +79,10 @@ You can experiment with an ``asyncio`` concurrent context in the :term:`REPL`: >>> await asyncio.sleep(10, result='hello') 'hello' +This REPL provides limited compatibility with :envvar:`PYTHON_BASIC_REPL`. +It is recommended that the default REPL is used +for full functionality and the latest features. + .. audit-event:: cpython.run_stdin "" "" .. versionchanged:: 3.12.5 (also 3.11.10, 3.10.15, 3.9.20, and 3.8.20) @@ -119,6 +128,7 @@ You can experiment with an ``asyncio`` concurrent context in the :term:`REPL`: asyncio-api-index.rst asyncio-llapi-index.rst asyncio-dev.rst + asyncio-threading.rst .. note:: The source code for asyncio can be found in :source:`Lib/asyncio/`. diff --git a/Doc/library/atexit.rst b/Doc/library/atexit.rst index 02d2f0807df8f6..b5caf5502d0e1c 100644 --- a/Doc/library/atexit.rst +++ b/Doc/library/atexit.rst @@ -4,14 +4,11 @@ .. module:: atexit :synopsis: Register and execute cleanup functions. -.. moduleauthor:: Skip Montanaro -.. sectionauthor:: Skip Montanaro - -------------- -The :mod:`atexit` module defines functions to register and unregister cleanup +The :mod:`!atexit` module defines functions to register and unregister cleanup functions. Functions thus registered are automatically executed upon normal -interpreter termination. :mod:`atexit` runs these functions in the *reverse* +interpreter termination. :mod:`!atexit` runs these functions in the *reverse* order in which they were registered; if you register ``A``, ``B``, and ``C``, at interpreter termination time they will be run in the order ``C``, ``B``, ``A``. @@ -64,7 +61,7 @@ a cleanup function is undefined. Remove *func* from the list of functions to be run at interpreter shutdown. :func:`unregister` silently does nothing if *func* was not previously registered. If *func* has been registered more than once, every occurrence - of that function in the :mod:`atexit` call stack will be removed. Equality + of that function in the :mod:`!atexit` call stack will be removed. Equality comparisons (``==``) are used internally during unregistration, so function references do not need to have matching identities. @@ -72,14 +69,14 @@ a cleanup function is undefined. .. seealso:: Module :mod:`readline` - Useful example of :mod:`atexit` to read and write :mod:`readline` history + Useful example of :mod:`!atexit` to read and write :mod:`readline` history files. .. _atexit-example: -:mod:`atexit` Example ---------------------- +:mod:`!atexit` Example +---------------------- The following simple example demonstrates how a module can initialize a counter from a file when it is imported and save the counter's updated value diff --git a/Doc/library/base64.rst b/Doc/library/base64.rst index 529a7242443820..8af40a2f8a65e3 100644 --- a/Doc/library/base64.rst +++ b/Doc/library/base64.rst @@ -16,8 +16,10 @@ This module provides functions for encoding binary data to printable ASCII characters and decoding such encodings back to binary data. This includes the :ref:`encodings specified in ` -:rfc:`4648` (Base64, Base32 and Base16) -and the non-standard :ref:`Base85 encodings `. +:rfc:`4648` (Base64, Base32 and Base16), the :ref:`Base85 encoding +` specified in `PDF 2.0 +`_, and non-standard variants +of Base85 used elsewhere. There are two interfaces provided by this module. The modern interface supports encoding :term:`bytes-like objects ` to ASCII @@ -51,7 +53,7 @@ The :rfc:`4648` encodings are suitable for encoding binary data so that it can b safely sent by email, used as parts of URLs, or included as part of an HTTP POST request. -.. function:: b64encode(s, altchars=None) +.. function:: b64encode(s, altchars=None, *, padded=True, wrapcol=0) Encode the :term:`bytes-like object` *s* using Base64 and return the encoded :class:`bytes`. @@ -61,11 +63,20 @@ POST request. This allows an application to e.g. generate URL or filesystem safe Base64 strings. The default is ``None``, for which the standard Base64 alphabet is used. - May assert or raise a :exc:`ValueError` if the length of *altchars* is not 2. Raises a - :exc:`TypeError` if *altchars* is not a :term:`bytes-like object`. + If *padded* is true (default), pad the encoded data with the '=' + character to a size multiple of 4. + If *padded* is false, do not add the pad characters. + If *wrapcol* is non-zero, insert a newline (``b'\n'``) character + after at most every *wrapcol* characters. + If *wrapcol* is zero (default), do not insert any newlines. -.. function:: b64decode(s, altchars=None, validate=False) + .. versionchanged:: 3.15 + Added the *padded* and *wrapcol* parameters. + + +.. function:: b64decode(s, altchars=None, validate=False, *, padded=True, canonical=False) + b64decode(s, altchars=None, validate=True, *, ignorechars, padded=True, canonical=False) Decode the Base64 encoded :term:`bytes-like object` or ASCII string *s* and return the decoded :class:`bytes`. @@ -74,18 +85,47 @@ POST request. of length 2 which specifies the alternative alphabet used instead of the ``+`` and ``/`` characters. + If *padded* is true, the last group of 4 base 64 alphabet characters must + be padded with the '=' character. + If *padded* is false, padding is neither required nor recognized: + the '=' character is not treated as padding but as a non-alphabet + character, which means it is silently discarded when *validate* is false, + or causes an :exc:`~binascii.Error` when *validate* is true unless + b'=' is included in *ignorechars*. + A :exc:`binascii.Error` exception is raised if *s* is incorrectly padded. - If *validate* is ``False`` (the default), characters that are neither - in the normal base-64 alphabet nor the alternative alphabet are - discarded prior to the padding check. If *validate* is ``True``, - these non-alphabet characters in the input result in a - :exc:`binascii.Error`. + If *ignorechars* is specified, it should be a :term:`bytes-like object` + containing characters to ignore from the input when *validate* is true. + If *ignorechars* contains the pad character ``'='``, the pad characters + presented before the end of the encoded data and the excess pad characters + will be ignored. + The default value of *validate* is ``True`` if *ignorechars* is specified, + ``False`` otherwise. + + If *validate* is false, characters that are neither + in the normal base-64 alphabet nor (if *ignorechars* is not specified) + the alternative alphabet are + discarded prior to the padding check, but the ``+`` and ``/`` characters + keep their meaning if they are not in *altchars* (they will be discarded + in future Python versions). + + If *validate* is true, these non-alphabet characters in the input + result in a :exc:`binascii.Error`. + + If *canonical* is true, non-zero padding bits are rejected. + See :func:`binascii.a2b_base64` for details. For more information about the strict base64 check, see :func:`binascii.a2b_base64` - May assert or raise a :exc:`ValueError` if the length of *altchars* is not 2. + .. versionchanged:: 3.15 + Added the *canonical*, *ignorechars*, and *padded* parameters. + + .. deprecated:: 3.15 + Accepting the ``+`` and ``/`` characters with an alternative alphabet + is now deprecated. + .. function:: standard_b64encode(s) @@ -99,16 +139,19 @@ POST request. Base64 alphabet and return the decoded :class:`bytes`. -.. function:: urlsafe_b64encode(s) +.. function:: urlsafe_b64encode(s, *, padded=True) Encode :term:`bytes-like object` *s* using the URL- and filesystem-safe alphabet, which substitutes ``-`` instead of ``+`` and ``_`` instead of ``/`` in the standard Base64 alphabet, and return the encoded :class:`bytes`. The result - can still contain ``=``. + can still contain ``=`` if *padded* is true (default). + + .. versionchanged:: 3.15 + Added the *padded* parameter. -.. function:: urlsafe_b64decode(s) +.. function:: urlsafe_b64decode(s, *, padded=False) Decode :term:`bytes-like object` or ASCII string *s* using the URL- and filesystem-safe @@ -116,14 +159,32 @@ POST request. ``/`` in the standard Base64 alphabet, and return the decoded :class:`bytes`. + .. versionchanged:: 3.15 + Added the *padded* parameter. + Padding of input is no longer required by default. + + .. deprecated:: 3.15 + Accepting the ``+`` and ``/`` characters is now deprecated. + -.. function:: b32encode(s) +.. function:: b32encode(s, *, padded=True, wrapcol=0) Encode the :term:`bytes-like object` *s* using Base32 and return the encoded :class:`bytes`. + If *padded* is true (default), pad the encoded data with the '=' + character to a size multiple of 8. + If *padded* is false, do not add the pad characters. -.. function:: b32decode(s, casefold=False, map01=None) + If *wrapcol* is non-zero, insert a newline (``b'\n'``) character + after at most every *wrapcol* characters. + If *wrapcol* is zero (default), do not add any newlines. + + .. versionchanged:: 3.15 + Added the *padded* and *wrapcol* parameters. + + +.. function:: b32decode(s, casefold=False, map01=None, *, padded=True, ignorechars=b'', canonical=False) Decode the Base32 encoded :term:`bytes-like object` or ASCII string *s* and return the decoded :class:`bytes`. @@ -139,20 +200,39 @@ POST request. digit 0 is always mapped to the letter O). For security purposes the default is ``None``, so that 0 and 1 are not allowed in the input. + If *padded* is true, the last group of 8 base 32 alphabet characters must + be padded with the '=' character. + If *padded* is false, padding is neither required nor recognized: + the '=' character is not treated as padding but as a non-alphabet + character, which means it raises an :exc:`~binascii.Error` unless + b'=' is included in *ignorechars*. + + *ignorechars* should be a :term:`bytes-like object` containing characters + to ignore from the input. + + If *canonical* is true, non-zero padding bits are rejected. + See :func:`binascii.a2b_base32` for details. + A :exc:`binascii.Error` is raised if *s* is incorrectly padded or if there are non-alphabet characters present in the input. + .. versionchanged:: 3.15 + Added the *canonical*, *ignorechars*, and *padded* parameters. -.. function:: b32hexencode(s) + +.. function:: b32hexencode(s, *, padded=True, wrapcol=0) Similar to :func:`b32encode` but uses the Extended Hex Alphabet, as defined in :rfc:`4648`. .. versionadded:: 3.10 + .. versionchanged:: 3.15 + Added the *padded* and *wrapcol* parameters. + -.. function:: b32hexdecode(s, casefold=False) +.. function:: b32hexdecode(s, casefold=False, *, padded=True, ignorechars=b'', canonical=False) Similar to :func:`b32decode` but uses the Extended Hex Alphabet, as defined in :rfc:`4648`. @@ -164,14 +244,24 @@ POST request. .. versionadded:: 3.10 + .. versionchanged:: 3.15 + Added the *canonical*, *ignorechars*, and *padded* parameters. + -.. function:: b16encode(s) +.. function:: b16encode(s, *, wrapcol=0) Encode the :term:`bytes-like object` *s* using Base16 and return the encoded :class:`bytes`. + If *wrapcol* is non-zero, insert a newline (``b'\n'``) character + after at most every *wrapcol* characters. + If *wrapcol* is zero (default), do not add any newlines. + + .. versionchanged:: 3.15 + Added the *wrapcol* parameter. + -.. function:: b16decode(s, casefold=False) +.. function:: b16decode(s, casefold=False, *, ignorechars=b'') Decode the Base16 encoded :term:`bytes-like object` or ASCII string *s* and return the decoded :class:`bytes`. @@ -180,28 +270,44 @@ POST request. lowercase alphabet is acceptable as input. For security purposes, the default is ``False``. + *ignorechars* should be a :term:`bytes-like object` containing characters + to ignore from the input. + A :exc:`binascii.Error` is raised if *s* is incorrectly padded or if there are non-alphabet characters present in the input. + .. versionchanged:: 3.15 + Added the *ignorechars* parameter. + + .. _base64-base-85: Base85 Encodings ----------------- -Base85 encoding is not formally specified but rather a de facto standard, -thus different systems perform the encoding differently. +Base85 encoding is a family of algorithms which represent four bytes +using five ASCII characters. Originally implemented in the Unix +``btoa(1)`` utility, a version of it was later adopted by Adobe in the +PostScript language and is standardized in PDF 2.0 (ISO 32000-2). +This version, in both its ``btoa`` and PDF variants, is implemented by +:func:`a85encode`. -The :func:`a85encode` and :func:`b85encode` functions in this module are two implementations of -the de facto standard. You should call the function with the Base85 -implementation used by the software you intend to work with. +A separate version, using a different output character set, was +defined as an April Fool's joke in :rfc:`1924` but is now used by Git +and other software. This version is implemented by :func:`b85encode`. -The two functions present in this module differ in how they handle the following: +Finally, a third version, using yet another output character set +designed for safe inclusion in programming language strings, is +defined by ZeroMQ and implemented here by :func:`z85encode`. -* Whether to include enclosing ``<~`` and ``~>`` markers -* Whether to include newline characters -* The set of ASCII characters used for encoding -* Handling of null bytes +The functions present in this module differ in how they handle the following: + +* Whether to include and expect enclosing ``<~`` and ``~>`` markers. +* Whether to fold the input into multiple lines. +* The set of ASCII characters used for encoding. +* Compact encodings of sequences of spaces and null bytes. +* The encoding of zero-padding bytes applied to the input. Refer to the documentation of the individual functions for more information. @@ -212,78 +318,137 @@ Refer to the documentation of the individual functions for more information. *foldspaces* is an optional flag that uses the special short sequence 'y' instead of 4 consecutive spaces (ASCII 0x20) as supported by 'btoa'. This - feature is not supported by the "standard" Ascii85 encoding. + feature is not supported by the standard encoding used in PDF. - *wrapcol* controls whether the output should have newline (``b'\n'``) - characters added to it. If this is non-zero, each output line will be - at most this many characters long, excluding the trailing newline. + If *wrapcol* is non-zero, insert a newline (``b'\n'``) character + after at most every *wrapcol* characters. + If *wrapcol* is zero (default), do not insert any newlines. - *pad* controls whether the input is padded to a multiple of 4 - before encoding. Note that the ``btoa`` implementation always pads. + *pad* controls whether zero-padding applied to the end of the input + is fully retained in the output encoding, as done by ``btoa``, + producing an exact multiple of 5 bytes of output. This is not part + of the standard encoding used in PDF, as it does not preserve the + length of the data. - *adobe* controls whether the encoded byte sequence is framed with ``<~`` - and ``~>``, which is used by the Adobe implementation. + *adobe* controls whether the encoded byte sequence is framed with + ``<~`` and ``~>``, as in a PostScript base-85 string literal. Note + that while ASCII85Decode streams in PDF documents *must* be + terminated with ``~>``, they *must not* use a leading ``<~``. .. versionadded:: 3.4 -.. function:: a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v') +.. function:: a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v', canonical=False) Decode the Ascii85 encoded :term:`bytes-like object` or ASCII string *b* and return the decoded :class:`bytes`. *foldspaces* is a flag that specifies whether the 'y' short sequence should be accepted as shorthand for 4 consecutive spaces (ASCII 0x20). - This feature is not supported by the "standard" Ascii85 encoding. + This feature is not supported by the standard Ascii85 encoding used in + PDF and PostScript. - *adobe* controls whether the input sequence is in Adobe Ascii85 format - (i.e. is framed with <~ and ~>). + *adobe* controls whether the ``<~`` and ``~>`` markers are + present. While the leading ``<~`` is not required, the input must + end with ``~>``, or a :exc:`ValueError` is raised. - *ignorechars* should be a :term:`bytes-like object` or ASCII string - containing characters to ignore - from the input. This should only contain whitespace characters, and by + *ignorechars* should be a :term:`bytes-like object` containing characters + to ignore from the input. + This should only contain whitespace characters, and by default contains all whitespace characters in ASCII. + If *canonical* is true, non-canonical encodings are rejected. + See :func:`binascii.a2b_ascii85` for details. + .. versionadded:: 3.4 + .. versionchanged:: 3.15 + Added the *canonical* parameter. + Single-character final groups are now always rejected as encoding + violations. + -.. function:: b85encode(b, pad=False) +.. function:: b85encode(b, pad=False, *, wrapcol=0) Encode the :term:`bytes-like object` *b* using base85 (as used in e.g. git-style binary diffs) and return the encoded :class:`bytes`. - If *pad* is true, the input is padded with ``b'\0'`` so its length is a - multiple of 4 bytes before encoding. + The input is padded with ``b'\0'`` so its length is a multiple of 4 + bytes before encoding. If *pad* is true, all the resulting + characters are retained in the output, which will always be a + multiple of 5 bytes, and thus the length of the data may not be + preserved on decoding. + + If *wrapcol* is non-zero, insert a newline (``b'\n'``) character + after at most every *wrapcol* characters. + If *wrapcol* is zero (default), do not add any newlines. .. versionadded:: 3.4 + .. versionchanged:: 3.15 + Added the *wrapcol* parameter. + -.. function:: b85decode(b) +.. function:: b85decode(b, *, ignorechars=b'', canonical=False) Decode the base85-encoded :term:`bytes-like object` or ASCII string *b* and - return the decoded :class:`bytes`. Padding is implicitly removed, if - necessary. + return the decoded :class:`bytes`. + + *ignorechars* should be a :term:`bytes-like object` containing characters + to ignore from the input. + + If *canonical* is true, non-canonical encodings are rejected. + See :func:`binascii.a2b_base85` for details. .. versionadded:: 3.4 + .. versionchanged:: 3.15 + Added the *canonical* and *ignorechars* parameters. + Single-character final groups are now always rejected as encoding + violations. -.. function:: z85encode(s) + +.. function:: z85encode(s, pad=False, *, wrapcol=0) Encode the :term:`bytes-like object` *s* using Z85 (as used in ZeroMQ) - and return the encoded :class:`bytes`. See `Z85 specification - `_ for more information. + and return the encoded :class:`bytes`. + + The input is padded with ``b'\0'`` so its length is a multiple of 4 + bytes before encoding. If *pad* is true, all the resulting + characters are retained in the output, which will always be a + multiple of 5 bytes, as required by the ZeroMQ standard. + + If *wrapcol* is non-zero, insert a newline (``b'\n'``) character + after at most every *wrapcol* characters. + If *wrapcol* is zero (default), do not add any newlines. .. versionadded:: 3.13 + .. versionchanged:: 3.15 + The *pad* parameter was added. + + .. versionchanged:: 3.15 + Added the *wrapcol* parameter. + -.. function:: z85decode(s) +.. function:: z85decode(s, *, ignorechars=b'', canonical=False) Decode the Z85-encoded :term:`bytes-like object` or ASCII string *s* and - return the decoded :class:`bytes`. See `Z85 specification - `_ for more information. + return the decoded :class:`bytes`. + + *ignorechars* should be a :term:`bytes-like object` containing characters + to ignore from the input. + + If *canonical* is true, non-canonical encodings are rejected. + See :func:`binascii.a2b_base85` for details. .. versionadded:: 3.13 + .. versionchanged:: 3.15 + Added the *canonical* and *ignorechars* parameters. + Single-character final groups are now always rejected as encoding + violations. + .. _base64-legacy: @@ -353,3 +518,11 @@ recommended to review the security section for any code deployed to production. Section 5.2, "Base64 Content-Transfer-Encoding," provides the definition of the base64 encoding. + `ISO 32000-2 Portable document format - Part 2: PDF 2.0 `_ + Section 7.4.3, "ASCII85Decode Filter," provides the definition + of the Ascii85 encoding used in PDF and PostScript, including + the output character set and the details of data length preservation + using zero-padding and partial output groups. + + `ZeroMQ RFC 32/Z85 `_ + The "Formal Specification" section provides the character set used in Z85. diff --git a/Doc/library/bdb.rst b/Doc/library/bdb.rst index c7a3e0c596b9d0..c8b48901901f98 100644 --- a/Doc/library/bdb.rst +++ b/Doc/library/bdb.rst @@ -8,7 +8,7 @@ -------------- -The :mod:`bdb` module handles basic debugger functions, like setting breakpoints +The :mod:`!bdb` module handles basic debugger functions, like setting breakpoints or managing execution via the debugger. The following exception is defined: @@ -18,7 +18,7 @@ The following exception is defined: Exception raised by the :class:`Bdb` class for quitting the debugger. -The :mod:`bdb` module also defines two classes: +The :mod:`!bdb` module also defines two classes: .. class:: Breakpoint(self, file, line, temporary=False, cond=None, funcname=None) @@ -236,7 +236,7 @@ The :mod:`bdb` module also defines two classes: Normally derived classes don't override the following methods, but they may if they want to redefine the definition of stopping and breakpoints. - .. method:: is_skipped_line(module_name) + .. method:: is_skipped_module(module_name) Return ``True`` if *module_name* matches any skip pattern. diff --git a/Doc/library/binascii.rst b/Doc/library/binascii.rst index 1bab785684bbab..ceb80a35a1a76b 100644 --- a/Doc/library/binascii.rst +++ b/Doc/library/binascii.rst @@ -10,10 +10,10 @@ -------------- -The :mod:`binascii` module contains a number of methods to convert between +The :mod:`!binascii` module contains a number of methods to convert between binary and various ASCII-encoded binary representations. Normally, you will not use these functions directly but use wrapper modules like -:mod:`base64` instead. The :mod:`binascii` module contains +:mod:`base64` instead. The :mod:`!binascii` module contains low-level functions written in C for greater speed that are used by the higher-level modules. @@ -28,7 +28,7 @@ higher-level modules. ASCII-only unicode strings are now accepted by the ``a2b_*`` functions. -The :mod:`binascii` module defines the following functions: +The :mod:`!binascii` module defines the following functions: .. function:: a2b_uu(string) @@ -48,34 +48,240 @@ The :mod:`binascii` module defines the following functions: Added the *backtick* parameter. -.. function:: a2b_base64(string, /, *, strict_mode=False) +.. function:: a2b_base64(string, /, *, padded=True, alphabet=BASE64_ALPHABET, strict_mode=False, canonical=False) + a2b_base64(string, /, *, ignorechars, padded=True, alphabet=BASE64_ALPHABET, strict_mode=True, canonical=False) Convert a block of base64 data back to binary and return the binary data. More than one line may be passed at a time. + Optional *alphabet* must be a :class:`bytes` object of length 64 which + specifies an alternative alphabet. + + If *padded* is true, the last group of 4 base 64 alphabet characters must + be padded with the '=' character. + If *padded* is false, padding is neither required nor recognized: + the '=' character is not treated as padding but as a non-alphabet + character, which means it is silently discarded when *strict_mode* is false, + or causes an :exc:`~binascii.Error` when *strict_mode* is true unless + b'=' is included in *ignorechars*. + + If *ignorechars* is specified, it should be a :term:`bytes-like object` + containing characters to ignore from the input when *strict_mode* is true. + If *ignorechars* contains the pad character ``'='``, the pad characters + presented before the end of the encoded data and the excess pad characters + will be ignored. + The default value of *strict_mode* is ``True`` if *ignorechars* is specified, + ``False`` otherwise. + If *strict_mode* is true, only valid base64 data will be converted. Invalid base64 data will raise :exc:`binascii.Error`. Valid base64: - * Conforms to :rfc:`3548`. + * Conforms to :rfc:`4648`. * Contains only characters from the base64 alphabet. * Contains no excess data after padding (including excess padding, newlines, etc.). * Does not start with a padding. + If *canonical* is true, non-zero padding bits in the last group are rejected + with :exc:`binascii.Error`, enforcing canonical encoding as defined in + :rfc:`4648` section 3.5. This check is independent of *strict_mode*. + .. versionchanged:: 3.11 Added the *strict_mode* parameter. + .. versionchanged:: 3.15 + Added the *alphabet*, *canonical*, *ignorechars*, and *padded* parameters. + + +.. function:: b2a_base64(data, *, padded=True, alphabet=BASE64_ALPHABET, wrapcol=0, newline=True) + + Convert binary data to a line(s) of ASCII characters in base64 coding, + as specified in :rfc:`4648`. + + If *padded* is true (default), pad the encoded data with the '=' + character to a size multiple of 4. + If *padded* is false, do not add the pad characters. -.. function:: b2a_base64(data, *, newline=True) + If *wrapcol* is non-zero, insert a newline (``b'\n'``) character + after at most every *wrapcol* characters. + If *wrapcol* is zero (default), do not insert any newlines. - Convert binary data to a line of ASCII characters in base64 coding. The return - value is the converted line, including a newline char if *newline* is - true. The output of this function conforms to :rfc:`3548`. + If *newline* is true (default), a newline character will be added + at the end of the output. .. versionchanged:: 3.6 Added the *newline* parameter. + .. versionchanged:: 3.15 + Added the *alphabet*, *padded* and *wrapcol* parameters. + + +.. function:: a2b_ascii85(string, /, *, foldspaces=False, adobe=False, ignorechars=b'', canonical=False) + + Convert Ascii85 data back to binary and return the binary data. + + Valid Ascii85 data contains characters from the Ascii85 alphabet in groups + of five (except for the final group, which may have from two to five + characters). Each group encodes 32 bits of binary data in the range from + ``0`` to ``2 ** 32 - 1``, inclusive. The special character ``z`` is + accepted as a short form of the group ``!!!!!``, which encodes four + consecutive null bytes. A single-character final group is always rejected + as an encoding violation. + + *foldspaces* is a flag that specifies whether the 'y' short sequence + should be accepted as shorthand for 4 consecutive spaces (ASCII 0x20). + This feature is not supported by the "standard" Ascii85 encoding. + + *adobe* controls whether the encoded byte sequence is framed with + ``<~`` and ``~>``, as in a PostScript base-85 string literal. If + *adobe* is true, a leading ``<~`` is optionally accepted, while a + trailing ``~>`` is *required*, and :exc:`binascii.Error` is raised + if it is not found. + + *ignorechars* should be a :term:`bytes-like object` containing characters + to ignore from the input. + This should only contain whitespace characters. + + If *canonical* is true, non-canonical encodings are rejected with + :exc:`binascii.Error`. Here "canonical" means the encoding that + :func:`b2a_ascii85` would produce: the ``z`` abbreviation must be used + for all-zero groups (rather than ``!!!!!``), and partial final groups + must use the same padding digits as the encoder. + + Invalid Ascii85 data will raise :exc:`binascii.Error`. + + .. versionadded:: 3.15 + + +.. function:: b2a_ascii85(data, /, *, foldspaces=False, wrapcol=0, pad=False, adobe=False) + + Convert binary data to a formatted sequence of ASCII characters in Ascii85 + coding. The return value is the converted data. + + *foldspaces* is an optional flag that uses the special short sequence 'y' + instead of 4 consecutive spaces (ASCII 0x20) as supported by 'btoa'. This + feature is not supported by the "standard" Ascii85 encoding. + + If *wrapcol* is non-zero, insert a newline (``b'\n'``) character + after at most every *wrapcol* characters. + If *wrapcol* is zero (default), do not insert any newlines. + + If *pad* is true, the zero-padding applied to the end of the input + is fully retained in the output encoding, as done by ``btoa``, + producing an exact multiple of 5 bytes of output. This is not part + of the standard encoding used in PDF, as it does not preserve the + length of the data. + + *adobe* controls whether the encoded byte sequence is framed with + ``<~`` and ``~>``, as in a PostScript base-85 string literal. Note + that while ASCII85Decode streams in PDF documents *must* be + terminated with ``~>``, they *must not* use a leading ``<~``. + + .. versionadded:: 3.15 + + +.. function:: a2b_base85(string, /, *, alphabet=BASE85_ALPHABET, ignorechars=b'', canonical=False) + + Convert Base85 data back to binary and return the binary data. + More than one line may be passed at a time. + + Valid Base85 data contains characters from the Base85 alphabet in groups + of five (except for the final group, which may have from two to five + characters). Each group encodes 32 bits of binary data in the range from + ``0`` to ``2 ** 32 - 1``, inclusive. A single-character final group is + always rejected as an encoding violation. + + Optional *alphabet* must be a :class:`bytes` object of length 85 which + specifies an alternative alphabet. + + *ignorechars* should be a :term:`bytes-like object` containing characters + to ignore from the input. + + If *canonical* is true, non-canonical encodings are rejected with + :exc:`binascii.Error`. Here "canonical" means the encoding that + :func:`b2a_base85` would produce: partial final groups must use the + same padding digits as the encoder. + + Invalid Base85 data will raise :exc:`binascii.Error`. + + .. versionadded:: 3.15 + + +.. function:: b2a_base85(data, /, *, alphabet=BASE85_ALPHABET, wrapcol=0, pad=False) + + Convert binary data to a line of ASCII characters in Base85 coding. + The return value is the converted line. + + Optional *alphabet* must be a :term:`bytes-like object` of length 85 which + specifies an alternative alphabet. + + If *wrapcol* is non-zero, insert a newline (``b'\n'``) character + after at most every *wrapcol* characters. + If *wrapcol* is zero (default), do not insert any newlines. + + If *pad* is true, the zero-padding applied to the end of the input + is retained in the output, which will always be a multiple of 5 + bytes, and thus the length of the data may not be preserved on + decoding. + + .. versionadded:: 3.15 + + +.. function:: a2b_base32(string, /, *, padded=True, alphabet=BASE32_ALPHABET, ignorechars=b'', canonical=False) + + Convert base32 data back to binary and return the binary data. + + Valid base32 data contains characters from the base32 alphabet specified + in :rfc:`4648` in groups of eight (if necessary, the final group is padded + to eight characters with ``=``). Each group encodes 40 bits of binary data + in the range from ``0`` to ``2 ** 40 - 1``, inclusive. + + .. note:: + This function does not map lowercase characters (which are invalid in + standard base32) to their uppercase counterparts, nor does it + contextually map ``0`` to ``O`` and ``1`` to ``I``/``L`` as :rfc:`4648` + allows. + + Optional *alphabet* must be a :class:`bytes` object of length 32 which + specifies an alternative alphabet. + + If *padded* is true, the last group of 8 base 32 alphabet characters must + be padded with the '=' character. + If *padded* is false, the '=' character is treated as other non-alphabet + characters (depending on the value of *ignorechars*). + + *ignorechars* should be a :term:`bytes-like object` containing characters + to ignore from the input. + If *ignorechars* contains the pad character ``'='``, the pad characters + presented before the end of the encoded data and the excess pad characters + will be ignored. + + If *canonical* is true, non-zero padding bits in the last group are rejected + with :exc:`binascii.Error`, enforcing canonical encoding as defined in + :rfc:`4648` section 3.5. + + Invalid base32 data will raise :exc:`binascii.Error`. + + .. versionadded:: 3.15 + +.. function:: b2a_base32(data, /, *, padded=True, alphabet=BASE32_ALPHABET, wrapcol=0) + + Convert binary data to a line of ASCII characters in base32 coding, + as specified in :rfc:`4648`. The return value is the converted line. + + Optional *alphabet* must be a :term:`bytes-like object` of length 32 which + specifies an alternative alphabet. + + If *padded* is true (default), pad the encoded data with the '=' + character to a size multiple of 8. + If *padded* is false, do not add the pad characters. + + If *wrapcol* is non-zero, insert a newline (``b'\n'``) character + after at most every *wrapcol* characters. + If *wrapcol* is zero (default), do not insert any newlines. + + .. versionadded:: 3.15 .. function:: a2b_qp(data, header=False) @@ -150,17 +356,23 @@ The :mod:`binascii` module defines the following functions: .. versionchanged:: 3.8 The *sep* and *bytes_per_sep* parameters were added. -.. function:: a2b_hex(hexstr) - unhexlify(hexstr) +.. function:: a2b_hex(hexstr, *, ignorechars=b'') + unhexlify(hexstr, *, ignorechars=b'') Return the binary data represented by the hexadecimal string *hexstr*. This function is the inverse of :func:`b2a_hex`. *hexstr* must contain an even number of hexadecimal digits (which can be upper or lower case), otherwise an :exc:`Error` exception is raised. - Similar functionality (accepting only text string arguments, but more - liberal towards whitespace) is also accessible using the - :meth:`bytes.fromhex` class method. + *ignorechars* should be a :term:`bytes-like object` containing characters + to ignore from the input. + + Similar functionality (but more liberal towards whitespace) is also accessible + using the :meth:`bytes.fromhex` class method. + + .. versionchanged:: 3.15 + Added the *ignorechars* parameter. + .. exception:: Error @@ -173,6 +385,69 @@ The :mod:`binascii` module defines the following functions: but may be handled by reading a little more data and trying again. +.. data:: BASE64_ALPHABET + + The Base 64 alphabet according to :rfc:`4648`. + + .. versionadded:: 3.15 + +.. data:: URLSAFE_BASE64_ALPHABET + + The "URL and filename safe" Base 64 alphabet according to :rfc:`4648`. + + .. versionadded:: 3.15 + +.. data:: UU_ALPHABET + + The uuencoding alphabet. + + .. versionadded:: 3.15 + +.. data:: CRYPT_ALPHABET + + The Base 64 alphabet used in the :manpage:`crypt(3)` routine and in the GEDCOM format. + + .. versionadded:: 3.15 + +.. data:: BINHEX_ALPHABET + + The Base 64 alphabet used in BinHex 4 (HQX) within the classic Mac OS. + + .. versionadded:: 3.15 + +.. data:: BASE85_ALPHABET + + The Base85 alphabet. + + .. versionadded:: 3.15 + +.. data:: ASCII85_ALPHABET + + The Ascii85 alphabet. + + .. versionadded:: 3.15 + +.. data:: Z85_ALPHABET + + The `Z85 `_ alphabet. + + .. versionadded:: 3.15 + +.. data:: BASE32_ALPHABET + + The Base 32 alphabet according to :rfc:`4648`. + + .. versionadded:: 3.15 + +.. data:: BASE32HEX_ALPHABET + + The "Extended Hex" Base 32 alphabet according to :rfc:`4648`. + Data encoded with this alphabet maintains its sort order during bitwise + comparisons. + + .. versionadded:: 3.15 + + .. seealso:: Module :mod:`base64` diff --git a/Doc/library/bisect.rst b/Doc/library/bisect.rst index 78da563397b625..39ab75c0904df9 100644 --- a/Doc/library/bisect.rst +++ b/Doc/library/bisect.rst @@ -3,9 +3,6 @@ .. module:: bisect :synopsis: Array bisection algorithms for binary searching. -.. sectionauthor:: Fred L. Drake, Jr. -.. sectionauthor:: Raymond Hettinger -.. example based on the PyModules FAQ entry by Aaron Watters **Source code:** :source:`Lib/bisect.py` @@ -16,7 +13,7 @@ having to sort the list after each insertion. For long lists of items with expensive comparison operations, this can be an improvement over linear searches or frequent resorting. -The module is called :mod:`bisect` because it uses a basic bisection +The module is called :mod:`!bisect` because it uses a basic bisection algorithm to do its work. Unlike other bisection tools that search for a specific value, the functions in this module are designed to locate an insertion point. Accordingly, the functions never call an :meth:`~object.__eq__` @@ -24,6 +21,16 @@ method to determine whether a value has been found. Instead, the functions only call the :meth:`~object.__lt__` method and will return an insertion point between values in an array. +.. note:: + + The functions in this module are not thread-safe. If multiple threads + concurrently use :mod:`!bisect` functions on the same sequence, this + may result in undefined behaviour. Likewise, if the provided sequence + is mutated by a different thread while a :mod:`!bisect` function + is operating on it, the result is undefined. For example, using + :py:func:`~bisect.insort_left` on the same list from multiple threads + may result in the list becoming unsorted. + .. _bisect functions: The following functions are provided: @@ -73,7 +80,7 @@ The following functions are provided: Insert *x* in *a* in sorted order. This function first runs :py:func:`~bisect.bisect_left` to locate an insertion point. - Next, it runs the :meth:`!insert` method on *a* to insert *x* at the + Next, it runs the :meth:`~sequence.insert` method on *a* to insert *x* at the appropriate position to maintain sort order. To support inserting records in a table, the *key* function (if any) is @@ -93,7 +100,7 @@ The following functions are provided: entries of *x*. This function first runs :py:func:`~bisect.bisect_right` to locate an insertion point. - Next, it runs the :meth:`!insert` method on *a* to insert *x* at the + Next, it runs the :meth:`~sequence.insert` method on *a* to insert *x* at the appropriate position to maintain sort order. To support inserting records in a table, the *key* function (if any) is @@ -122,7 +129,7 @@ thoughts in mind: they are used. Consequently, if the search functions are used in a loop, the key function may be called again and again on the same array elements. If the key function isn't fast, consider wrapping it with - :py:func:`functools.cache` to avoid duplicate computations. Alternatively, + :py:deco:`functools.cache` to avoid duplicate computations. Alternatively, consider searching an array of precomputed keys to locate the insertion point (as shown in the examples section below). @@ -193,9 +200,9 @@ example uses :py:func:`~bisect.bisect` to look up a letter grade for an exam sco based on a set of ordered numeric breakpoints: 90 and up is an 'A', 80 to 89 is a 'B', and so on:: - >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'): - ... i = bisect(breakpoints, score) - ... return grades[i] + >>> def grade(score): + ... i = bisect([60, 70, 80, 90], score) + ... return "FDCBA"[i] ... >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]] ['F', 'A', 'C', 'C', 'B', 'A', 'A'] diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst index ebe2e43febaefa..6c20e9c94a3eae 100644 --- a/Doc/library/bz2.rst +++ b/Doc/library/bz2.rst @@ -4,11 +4,6 @@ .. module:: bz2 :synopsis: Interfaces for bzip2 compression and decompression. -.. moduleauthor:: Gustavo Niemeyer -.. moduleauthor:: Nadeem Vawda -.. sectionauthor:: Gustavo Niemeyer -.. sectionauthor:: Nadeem Vawda - **Source code:** :source:`Lib/bz2.py` -------------- @@ -16,7 +11,7 @@ This module provides a comprehensive interface for compressing and decompressing data using the bzip2 compression algorithm. -The :mod:`bz2` module contains: +The :mod:`!bz2` module contains: * The :func:`.open` function and :class:`BZ2File` class for reading and writing compressed files. @@ -25,6 +20,8 @@ The :mod:`bz2` module contains: * The :func:`compress` and :func:`decompress` functions for one-shot (de)compression. +.. include:: ../includes/optional-module.rst + (De)compression of files ------------------------ @@ -315,7 +312,7 @@ One-shot (de)compression Examples of usage ----------------- -Below are some examples of typical usage of the :mod:`bz2` module. +Below are some examples of typical usage of the :mod:`!bz2` module. Using :func:`compress` and :func:`decompress` to demonstrate round-trip compression: diff --git a/Doc/library/calendar.rst b/Doc/library/calendar.rst index b292d828841f2f..31faa8c4fb43dc 100644 --- a/Doc/library/calendar.rst +++ b/Doc/library/calendar.rst @@ -5,8 +5,6 @@ :synopsis: Functions for working with calendars, including some emulation of the Unix cal program. -.. sectionauthor:: Drew Csillag - **Source code:** :source:`Lib/calendar.py` -------------- @@ -56,13 +54,13 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is .. method:: setfirstweekday(firstweekday) - Set the first weekday to *firstweekday*, passed as an :class:`int` (0--6) + Set the first weekday to *firstweekday*, passed as an :class:`int` (0--6). Identical to setting the :attr:`~Calendar.firstweekday` property. .. method:: iterweekdays() - Return an iterator for the week day numbers that will be used for one + Return an iterator for the weekday numbers that will be used for one week. The first value from the iterator will be the same as the value of the :attr:`~Calendar.firstweekday` property. @@ -88,7 +86,7 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is Return an iterator for the month *month* in the year *year* similar to :meth:`itermonthdates`, but not restricted by the :class:`datetime.date` range. Days returned will be tuples consisting of a day of the month - number and a week day number. + number and a weekday number. .. method:: itermonthdays3(year, month) @@ -158,6 +156,11 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is :class:`TextCalendar` instances have the following methods: + .. method:: prweek(theweek, width) + + Print a week's calendar as returned by :meth:`formatweek` and without a + final newline. + .. method:: formatday(theday, weekday, width) @@ -405,7 +408,7 @@ For simple text calendars this module provides the following functions. .. function:: monthrange(year, month) - Returns weekday of first day of the month and number of days in month, for the + Returns weekday of first day of the month and number of days in month, for the specified *year* and *month*. @@ -443,11 +446,11 @@ For simple text calendars this module provides the following functions. An unrelated but handy function that takes a time tuple such as returned by the :func:`~time.gmtime` function in the :mod:`time` module, and returns the corresponding Unix timestamp value, assuming an epoch of 1970, and the POSIX - encoding. In fact, :func:`time.gmtime` and :func:`timegm` are each others' + encoding. In fact, :func:`time.gmtime` and :func:`timegm` are each other's inverse. -The :mod:`calendar` module exports the following data attributes: +The :mod:`!calendar` module exports the following data attributes: .. data:: day_name @@ -501,6 +504,14 @@ The :mod:`calendar` module exports the following data attributes: >>> list(calendar.month_name) ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] + .. caution:: + + In locales with alternative month names forms, the :data:`!month_name` sequence + may not be suitable when a month name stands by itself and not as part of a date. + For instance, in Greek and in many Slavic and Baltic languages, :data:`!month_name` + will produce the month in genitive case. Use :data:`standalone_month_name` for a form + suitable for standalone use. + .. data:: month_abbr @@ -512,6 +523,31 @@ The :mod:`calendar` module exports the following data attributes: >>> list(calendar.month_abbr) ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + .. caution:: + + In locales with alternative month names forms, the :data:`!month_abbr` sequence + may not be suitable when a month name stands by itself and not as part of a date. + Use :data:`standalone_month_abbr` for a form suitable for standalone use. + + +.. data:: standalone_month_name + + A sequence that represents the months of the year in the current locale + in the standalone form if the locale provides one. Else it is equivalent + to :data:`month_name`. + + .. versionadded:: 3.15 + + +.. data:: standalone_month_abbr + + A sequence that represents the abbreviated months of the year in the current + locale in the standalone form if the locale provides one. Else it is + equivalent to :data:`month_abbr`. + + .. versionadded:: 3.15 + + .. data:: JANUARY FEBRUARY MARCH @@ -540,13 +576,18 @@ The :mod:`calendar` module exports the following data attributes: .. versionadded:: 3.12 -The :mod:`calendar` module defines the following exceptions: +The :mod:`!calendar` module defines the following exceptions: .. exception:: IllegalMonthError(month) - A subclass of :exc:`ValueError`, + A subclass of :exc:`ValueError` and :exc:`IndexError`, raised when the given month number is outside of the range 1-12 (inclusive). + .. versionchanged:: 3.12 + :exc:`IllegalMonthError` is now also a subclass of + :exc:`ValueError`. New code should avoid catching + :exc:`IndexError`. + .. attribute:: month The invalid month number. @@ -579,7 +620,7 @@ Command-line usage .. versionadded:: 2.5 -The :mod:`calendar` module can be executed as a script from the command line +The :mod:`!calendar` module can be executed as a script from the command line to interactively print a calendar. .. code-block:: shell @@ -677,8 +718,7 @@ The following options are accepted: .. option:: month The month of the specified :option:`year` to print the calendar for. - Must be a number between 1 and 12, - and may only be used in text mode. + Must be a number between 1 and 12. Defaults to printing a calendar for the full year. @@ -716,6 +756,11 @@ The following options are accepted: By default, today's date is highlighted in color and can be :ref:`controlled using environment variables `. +.. versionchanged:: 3.15 + By default, the month is now also highlighted in color, and + the days of the week are also in color. This behavior can be + :ref:`controlled using environment variables `. + *HTML-mode options:* .. option:: --css CSS, -c CSS diff --git a/Doc/library/cmath.rst b/Doc/library/cmath.rst index 26518a0458fd81..f602003e49b821 100644 --- a/Doc/library/cmath.rst +++ b/Doc/library/cmath.rst @@ -124,7 +124,7 @@ rectangular coordinates to polar coordinates and back. The modulus (absolute value) of a complex number *z* can be computed using the built-in :func:`abs` function. There is no - separate :mod:`cmath` module function for this operation. + separate :mod:`!cmath` module function for this operation. .. function:: polar(z) @@ -338,7 +338,7 @@ Constants .. data:: nan A floating-point "not a number" (NaN) value. Equivalent to - ``float('nan')``. + ``float('nan')``. See also :data:`math.nan`. .. versionadded:: 3.6 @@ -357,7 +357,7 @@ Note that the selection of functions is similar, but not identical, to that in module :mod:`math`. The reason for having two modules is that some users aren't interested in complex numbers, and perhaps don't even know what they are. They would rather have ``math.sqrt(-1)`` raise an exception than return a complex -number. Also note that the functions defined in :mod:`cmath` always return a +number. Also note that the functions defined in :mod:`!cmath` always return a complex number, even if the answer can be expressed as a real number (in which case the complex number has an imaginary part of zero). diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index 66544f82f6ff3f..c988fcebd68a01 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -4,8 +4,6 @@ .. module:: cmd :synopsis: Build line-oriented command interpreters. -.. sectionauthor:: Eric S. Raymond - **Source code:** :source:`Lib/cmd.py` -------------- @@ -243,9 +241,7 @@ Instances of :class:`Cmd` subclasses have some public instance variables: Cmd Example ----------- -.. sectionauthor:: Raymond Hettinger - -The :mod:`cmd` module is mainly useful for building custom shells that let a +The :mod:`!cmd` module is mainly useful for building custom shells that let a user work with a program interactively. This section presents a simple example of how to build a shell around a few of diff --git a/Doc/library/cmdline.rst b/Doc/library/cmdline.rst index 16c67ddbf7cec2..6418706269f1ed 100644 --- a/Doc/library/cmdline.rst +++ b/Doc/library/cmdline.rst @@ -12,27 +12,28 @@ The following modules have a command-line interface. * :ref:`calendar ` * :mod:`code` * :ref:`compileall ` -* :mod:`cProfile`: see :ref:`profile ` +* ``cProfile``: see :ref:`profiling.tracing ` * :ref:`dis ` * :ref:`doctest ` * :mod:`!encodings.rot_13` -* :mod:`ensurepip` +* :ref:`ensurepip ` * :mod:`filecmp` * :mod:`fileinput` * :mod:`ftplib` * :ref:`gzip ` * :ref:`http.server ` -* :mod:`!idlelib` +* :ref:`idlelib ` * :ref:`inspect ` * :ref:`json ` * :ref:`mimetypes ` -* :mod:`pdb` +* :ref:`pdb ` * :ref:`pickle ` * :ref:`pickletools ` * :ref:`platform ` * :mod:`poplib` -* :ref:`profile ` -* :mod:`pstats` +* :ref:`profiling.sampling ` +* :ref:`profiling.tracing ` +* :ref:`pstats ` * :ref:`py_compile ` * :mod:`pyclbr` * :mod:`pydoc` @@ -52,8 +53,8 @@ The following modules have a command-line interface. * :mod:`turtledemo` * :ref:`unittest ` * :ref:`uuid ` -* :mod:`venv` -* :mod:`webbrowser` +* :ref:`venv ` +* :ref:`webbrowser ` * :ref:`zipapp ` * :ref:`zipfile ` diff --git a/Doc/library/cmdlinelibs.rst b/Doc/library/cmdlinelibs.rst index 085d31af7bca1f..32f8c2c9f4ae32 100644 --- a/Doc/library/cmdlinelibs.rst +++ b/Doc/library/cmdlinelibs.rst @@ -1,7 +1,7 @@ .. _cmdlinelibs: ******************************** -Command Line Interface Libraries +Command-line interface libraries ******************************** The modules described in this chapter assist with implementing @@ -19,3 +19,4 @@ Here's an overview: curses.rst curses.ascii.rst curses.panel.rst + cmd.rst diff --git a/Doc/library/code.rst b/Doc/library/code.rst index 52587c4dd8f8e8..59c016d21501b0 100644 --- a/Doc/library/code.rst +++ b/Doc/library/code.rst @@ -29,7 +29,7 @@ build applications which provide an interactive interpreter prompt. module. -.. class:: InteractiveConsole(locals=None, filename="", local_exit=False) +.. class:: InteractiveConsole(locals=None, filename="", *, local_exit=False) Closely emulate the behavior of the interactive Python interpreter. This class builds on :class:`InteractiveInterpreter` and adds prompting using the familiar diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst index 1cb0e225bca043..059ed2c03acfa3 100644 --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -4,10 +4,6 @@ .. module:: codecs :synopsis: Encode and decode data and streams. -.. moduleauthor:: Marc-André Lemburg -.. sectionauthor:: Marc-André Lemburg -.. sectionauthor:: Martin v. Löwis - **Source code:** :source:`Lib/codecs.py` .. index:: @@ -68,11 +64,21 @@ The full details for each codec can also be looked up directly: Looks up the codec info in the Python codec registry and returns a :class:`CodecInfo` object as defined below. - Encodings are first looked up in the registry's cache. If not found, the list of + This function first normalizes the *encoding*: all ASCII letters are + converted to lower case, spaces are replaced with hyphens. + Then encoding is looked up in the registry's cache. If not found, the list of registered search functions is scanned. If no :class:`CodecInfo` object is found, a :exc:`LookupError` is raised. Otherwise, the :class:`CodecInfo` object is stored in the cache and returned to the caller. + .. versionchanged:: 3.9 + Any characters except ASCII letters and digits and a dot are converted to underscore. + + .. versionchanged:: 3.15 + No characters are converted to underscore anymore. + Spaces are converted to hyphens. + + .. class:: CodecInfo(encode, decode, streamreader=None, streamwriter=None, incrementalencoder=None, incrementaldecoder=None, name=None) Codec details when looking up the codec registry. The constructor @@ -167,14 +173,11 @@ function: .. function:: register(search_function, /) Register a codec search function. Search functions are expected to take one - argument, being the encoding name in all lower case letters with hyphens - and spaces converted to underscores, and return a :class:`CodecInfo` object. + argument, being the encoding name in all lower case letters with spaces + converted to hyphens, and return a :class:`CodecInfo` object. In case a search function cannot find a given encoding, it should return ``None``. - .. versionchanged:: 3.9 - Hyphens and spaces are converted to underscore. - .. function:: unregister(search_function, /) @@ -310,7 +313,7 @@ and writing to platform dependent files: Codec Base Classes ------------------ -The :mod:`codecs` module defines a set of base classes which define the +The :mod:`!codecs` module defines a set of base classes which define the interfaces for working with codec objects, and can also be used as the basis for custom codec implementations. @@ -982,17 +985,22 @@ defined in Unicode. A simple and straightforward way that can store each Unicode code point, is to store each code point as four consecutive bytes. There are two possibilities: store the bytes in big endian or in little endian order. These two encodings are called ``UTF-32-BE`` and ``UTF-32-LE`` respectively. Their -disadvantage is that if e.g. you use ``UTF-32-BE`` on a little endian machine you -will always have to swap bytes on encoding and decoding. ``UTF-32`` avoids this -problem: bytes will always be in natural endianness. When these bytes are read -by a CPU with a different endianness, then bytes have to be swapped though. To -be able to detect the endianness of a ``UTF-16`` or ``UTF-32`` byte sequence, -there's the so called BOM ("Byte Order Mark"). This is the Unicode character -``U+FEFF``. This character can be prepended to every ``UTF-16`` or ``UTF-32`` -byte sequence. The byte swapped version of this character (``0xFFFE``) is an -illegal character that may not appear in a Unicode text. So when the -first character in a ``UTF-16`` or ``UTF-32`` byte sequence -appears to be a ``U+FFFE`` the bytes have to be swapped on decoding. +disadvantage is that if, for example, you use ``UTF-32-BE`` on a little endian +machine you will always have to swap bytes on encoding and decoding. +Python's ``UTF-16`` and ``UTF-32`` codecs avoid this problem by using the +platform's native byte order when no BOM is present. +Python follows prevailing platform +practice, so native-endian data round-trips without redundant byte swapping, +even though the Unicode Standard defaults to big-endian when the byte order is +unspecified. When these bytes are read by a CPU with a different endianness, +the bytes have to be swapped. To be able to detect the endianness of a +``UTF-16`` or ``UTF-32`` byte sequence, a BOM ("Byte Order Mark") is used. +This is the Unicode character ``U+FEFF``. This character can be prepended to every +``UTF-16`` or ``UTF-32`` byte sequence. The byte swapped version of this character +(``0xFFFE``) is an illegal character that may not appear in a Unicode text. +When the first character of a ``UTF-16`` or ``UTF-32`` byte sequence is +``U+FFFE``, the bytes have to be swapped on decoding. + Unfortunately the character ``U+FEFF`` had a second purpose as a ``ZERO WIDTH NO-BREAK SPACE``: a character that has no width and doesn't allow a word to be split. It can e.g. be used to give hints to a ligature algorithm. @@ -1065,7 +1073,7 @@ or with dictionaries as mapping tables. The following table lists the codecs by name, together with a few common aliases, and the languages for which the encoding is likely used. Neither the list of aliases nor the list of languages is meant to be exhaustive. Notice that spelling alternatives that only differ in -case or use a hyphen instead of an underscore are also valid aliases +case or use a space or a hyphen instead of an underscore are also valid aliases because they are equivalent when normalized by :func:`~encodings.normalize_encoding`. For example, ``'utf-8'`` is a valid alias for the ``'utf_8'`` codec. @@ -1076,7 +1084,7 @@ alias for the ``'utf_8'`` codec. refer to the source :source:`aliases.py ` file. On Windows, ``cpXXX`` codecs are available for all code pages. -But only codecs listed in the following table are guarantead to exist on +But only codecs listed in the following table are guaranteed to exist on other platforms. .. impl-detail:: @@ -1147,7 +1155,7 @@ particular, the following variants typically exist: +-----------------+--------------------------------+--------------------------------+ | cp857 | 857, IBM857 | Turkish | +-----------------+--------------------------------+--------------------------------+ -| cp858 | 858, IBM858 | Western Europe | +| cp858 | 858, IBM00858 | Western Europe | +-----------------+--------------------------------+--------------------------------+ | cp860 | 860, IBM860 | Portuguese | +-----------------+--------------------------------+--------------------------------+ @@ -1184,7 +1192,7 @@ particular, the following variants typically exist: | | | | | | | .. versionadded:: 3.4 | +-----------------+--------------------------------+--------------------------------+ -| cp1140 | ibm1140 | Western Europe | +| cp1140 | IBM01140 | Western Europe | +-----------------+--------------------------------+--------------------------------+ | cp1250 | windows-1250 | Central and Eastern Europe | +-----------------+--------------------------------+--------------------------------+ @@ -1251,7 +1259,7 @@ particular, the following variants typically exist: +-----------------+--------------------------------+--------------------------------+ | iso8859_3 | iso-8859-3, latin3, L3 | Esperanto, Maltese | +-----------------+--------------------------------+--------------------------------+ -| iso8859_4 | iso-8859-4, latin4, L4 | Baltic languages | +| iso8859_4 | iso-8859-4, latin4, L4 | Northern Europe | +-----------------+--------------------------------+--------------------------------+ | iso8859_5 | iso-8859-5, cyrillic | Belarusian, Bulgarian, | | | | Macedonian, Russian, Serbian | @@ -1332,7 +1340,7 @@ particular, the following variants typically exist: +-----------------+--------------------------------+--------------------------------+ | utf_8 | U8, UTF, utf8, cp65001 | all languages | +-----------------+--------------------------------+--------------------------------+ -| utf_8_sig | | all languages | +| utf_8_sig | utf8-sig | all languages | +-----------------+--------------------------------+--------------------------------+ .. versionchanged:: 3.4 @@ -1483,6 +1491,36 @@ to :class:`bytes` mappings. They are not supported by :meth:`bytes.decode` Restoration of the aliases for the binary transforms. +.. _standalone-codec-functions: + +Standalone Codec Functions +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following functions provide encoding and decoding functionality similar to codecs, +but are not available as named codecs through :func:`codecs.encode` or :func:`codecs.decode`. +They are used internally (for example, by :mod:`pickle`) and behave similarly to the +``string_escape`` codec that was removed in Python 3. + +.. function:: codecs.escape_encode(input, errors=None) + + Encode *input* using escape sequences. Similar to how :func:`repr` on bytes + produces escaped byte values. + + *input* must be a :class:`bytes` object. + + Returns a tuple ``(output, length)`` where *output* is a :class:`bytes` + object and *length* is the number of bytes consumed. + +.. function:: codecs.escape_decode(input, errors=None) + + Decode *input* from escape sequences back to the original bytes. + + *input* must be a :term:`bytes-like object`. + + Returns a tuple ``(output, length)`` where *output* is a :class:`bytes` + object and *length* is the number of bytes consumed. + + .. _text-transforms: Text Transforms @@ -1509,8 +1547,8 @@ mapping. It is not supported by :meth:`str.encode` (which only produces Restoration of the ``rot13`` alias. -:mod:`encodings` --- Encodings package --------------------------------------- +:mod:`!encodings` --- Encodings package +--------------------------------------- .. module:: encodings :synopsis: Encodings package @@ -1569,12 +1607,11 @@ This module implements the following exception: Raised when a codec is invalid or incompatible. -:mod:`encodings.idna` --- Internationalized Domain Names in Applications ------------------------------------------------------------------------- +:mod:`!encodings.idna` --- Internationalized Domain Names in Applications +------------------------------------------------------------------------- .. module:: encodings.idna :synopsis: Internationalized Domain Names implementation -.. moduleauthor:: Martin v. Löwis This module implements :rfc:`3490` (Internationalized Domain Names in Applications) and :rfc:`3492` (Nameprep: A Stringprep Profile for @@ -1612,7 +1649,7 @@ When receiving host names from the wire (such as in reverse name lookup), no automatic conversion to Unicode is performed: applications wishing to present such host names to the user should decode them to Unicode. -The module :mod:`encodings.idna` also implements the nameprep procedure, which +The module :mod:`!encodings.idna` also implements the nameprep procedure, which performs certain normalizations on host names, to achieve case-insensitivity of international domain names, and to unify similar characters. The nameprep functions can be used directly if desired. @@ -1635,8 +1672,8 @@ functions can be used directly if desired. Convert a label to Unicode, as specified in :rfc:`3490`. -:mod:`encodings.mbcs` --- Windows ANSI codepage ------------------------------------------------ +:mod:`!encodings.mbcs` --- Windows ANSI codepage +------------------------------------------------ .. module:: encodings.mbcs :synopsis: Windows ANSI codepage @@ -1653,12 +1690,11 @@ This module implements the ANSI codepage (CP_ACP). Support any error handler. -:mod:`encodings.utf_8_sig` --- UTF-8 codec with BOM signature -------------------------------------------------------------- +:mod:`!encodings.utf_8_sig` --- UTF-8 codec with BOM signature +-------------------------------------------------------------- .. module:: encodings.utf_8_sig :synopsis: UTF-8 codec with BOM signature -.. moduleauthor:: Walter Dörwald This module implements a variant of the UTF-8 codec. On encoding, a UTF-8 encoded BOM will be prepended to the UTF-8 encoded bytes. For the stateful encoder this diff --git a/Doc/library/codeop.rst b/Doc/library/codeop.rst index 16f674adb4b22b..622e57d2ee63db 100644 --- a/Doc/library/codeop.rst +++ b/Doc/library/codeop.rst @@ -4,14 +4,11 @@ .. module:: codeop :synopsis: Compile (possibly incomplete) Python code. -.. sectionauthor:: Moshe Zadka -.. sectionauthor:: Michael Hudson - **Source code:** :source:`Lib/codeop.py` -------------- -The :mod:`codeop` module provides utilities upon which the Python +The :mod:`!codeop` module provides utilities upon which the Python read-eval-print loop can be emulated, as is done in the :mod:`code` module. As a result, you probably don't want to use the module directly; if you want to include such a loop in your program you probably want to use the :mod:`code` @@ -25,7 +22,7 @@ There are two parts to this job: #. Remembering which future statements the user has entered, so subsequent input can be compiled with these in effect. -The :mod:`codeop` module provides a way of doing each of these things, and a way +The :mod:`!codeop` module provides a way of doing each of these things, and a way of doing them both. To do just the former: diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst index daa9af6d1dd9c9..10e3790717ed6e 100644 --- a/Doc/library/collections.abc.rst +++ b/Doc/library/collections.abc.rst @@ -4,9 +4,6 @@ .. module:: collections.abc :synopsis: Abstract base classes for containers -.. moduleauthor:: Raymond Hettinger -.. sectionauthor:: Raymond Hettinger - .. versionadded:: 3.3 Formerly, this module was part of the :mod:`collections` module. @@ -140,6 +137,9 @@ ABC Inherits from Abstract Methods Mi ``__len__``, ``insert`` +:class:`ByteString` :class:`Sequence` ``__getitem__``, Inherited :class:`Sequence` methods + ``__len__`` + :class:`Set` :class:`Collection` ``__contains__``, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, ``__iter__``, ``__gt__``, ``__ge__``, ``__and__``, ``__or__``, ``__len__`` ``__sub__``, ``__rsub__``, ``__xor__``, ``__rxor__`` @@ -260,21 +260,50 @@ Collections Abstract Base Classes -- Detailed Descriptions .. class:: Sequence MutableSequence + ByteString ABCs for read-only and mutable :term:`sequences `. Implementation note: Some of the mixin methods, such as - :meth:`~container.__iter__`, :meth:`~object.__reversed__` and :meth:`index`, make - repeated calls to the underlying :meth:`~object.__getitem__` method. + :meth:`~container.__iter__`, :meth:`~object.__reversed__`, + and :meth:`~sequence.index` make repeated calls to the underlying + :meth:`~object.__getitem__` method. Consequently, if :meth:`~object.__getitem__` is implemented with constant access speed, the mixin methods will have linear performance; however, if the underlying method is linear (as it would be with a linked list), the mixins will have quadratic performance and will likely need to be overridden. - .. versionchanged:: 3.5 - The index() method added support for *stop* and *start* - arguments. + .. method:: index(value, start=0, stop=None) + + Return first index of *value*. + + Raises :exc:`ValueError` if the value is not present. + + Supporting the *start* and *stop* arguments is optional, but recommended. + + .. versionchanged:: 3.5 + The :meth:`~sequence.index` method gained support for + the *stop* and *start* arguments. + + .. deprecated-removed:: 3.12 3.17 + The :class:`ByteString` ABC has been deprecated. + + Use ``isinstance(obj, collections.abc.Buffer)`` to test if ``obj`` + implements the :ref:`buffer protocol ` at runtime. For use + in type annotations, either use :class:`Buffer` or a union that + explicitly specifies the types your code supports (e.g., + ``bytes | bytearray | memoryview``). + + :class:`!ByteString` was originally intended to be an abstract class that + would serve as a supertype of both :class:`bytes` and :class:`bytearray`. + However, since the ABC never had any methods, knowing that an object was + an instance of :class:`!ByteString` never actually told you anything + useful about the object. Other common buffer types such as + :class:`memoryview` were also never understood as subtypes of + :class:`!ByteString` (either at runtime or by static type checkers). + + See :pep:`PEP 688 <688#current-options>` for more details. .. class:: Set MutableSet @@ -304,7 +333,7 @@ Collections Abstract Base Classes -- Detailed Descriptions .. note:: In CPython, generator-based coroutines (:term:`generators ` - decorated with :func:`@types.coroutine `) are + decorated with :deco:`types.coroutine`) are *awaitables*, even though they do not have an :meth:`~object.__await__` method. Using ``isinstance(gencoro, Awaitable)`` for them will return ``False``. Use :func:`inspect.isawaitable` to detect them. @@ -322,7 +351,7 @@ Collections Abstract Base Classes -- Detailed Descriptions .. note:: In CPython, generator-based coroutines (:term:`generators ` - decorated with :func:`@types.coroutine `) are + decorated with :deco:`types.coroutine`) are *awaitables*, even though they do not have an :meth:`~object.__await__` method. Using ``isinstance(gencoro, Coroutine)`` for them will return ``False``. Use :func:`inspect.isawaitable` to detect them. @@ -427,7 +456,7 @@ Notes on using :class:`Set` and :class:`MutableSet` as a mixin: The :class:`Set` mixin provides a :meth:`!_hash` method to compute a hash value for the set; however, :meth:`~object.__hash__` is not defined because not all sets are :term:`hashable` or immutable. To add set hashability using mixins, - inherit from both :meth:`Set` and :meth:`Hashable`, then define + inherit from both :class:`Set` and :class:`Hashable`, then define ``__hash__ = Set._hash``. .. seealso:: diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 5fbdb12f40cafa..d09a6c92bbd37d 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -4,9 +4,6 @@ .. module:: collections :synopsis: Container datatypes -.. moduleauthor:: Raymond Hettinger -.. sectionauthor:: Raymond Hettinger - **Source code:** :source:`Lib/collections/__init__.py` .. testsetup:: * @@ -240,7 +237,9 @@ For example:: [('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631), ('you', 554), ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)] -.. class:: Counter([iterable-or-mapping]) +.. class:: Counter(**kwargs) + Counter(iterable, /, **kwargs) + Counter(mapping, /, **kwargs) A :class:`Counter` is a :class:`dict` subclass for counting :term:`hashable` objects. It is a collection where elements are stored as dictionary keys @@ -290,7 +289,7 @@ For example:: >>> sorted(c.elements()) ['a', 'a', 'a', 'a', 'b', 'b'] - .. method:: most_common([n]) + .. method:: most_common(n=None) Return a list of the *n* most common elements and their counts from the most common to the least. If *n* is omitted or ``None``, @@ -300,7 +299,9 @@ For example:: >>> Counter('abracadabra').most_common(3) [('a', 5), ('b', 2), ('r', 2)] - .. method:: subtract([iterable-or-mapping]) + .. method:: subtract(**kwargs) + subtract(iterable, /, **kwargs) + subtract(mapping, /, **kwargs) Elements are subtracted from an *iterable* or from another *mapping* (or counter). Like :meth:`dict.update` but subtracts counts instead @@ -325,13 +326,15 @@ For example:: .. versionadded:: 3.10 The usual dictionary methods are available for :class:`Counter` objects - except for two which work differently for counters. + except for these two which work differently for counters: .. method:: fromkeys(iterable) This class method is not implemented for :class:`Counter` objects. - .. method:: update([iterable-or-mapping]) + .. method:: update(**kwargs) + update(iterable, /, **kwargs) + update(mapping, /, **kwargs) Elements are counted from an *iterable* or added-in from another *mapping* (or counter). Like :meth:`dict.update` but adds counts @@ -367,9 +370,11 @@ Several mathematical operations are provided for combining :class:`Counter` objects to produce multisets (counters that have counts greater than zero). Addition and subtraction combine counters by adding or subtracting the counts of corresponding elements. Intersection and union return the minimum and -maximum of corresponding counts. Equality and inclusion compare -corresponding counts. Each operation can accept inputs with signed -counts, but the output will exclude results with counts of zero or less. +maximum of corresponding counts. Symmetric difference returns the difference +between the maximum and minimum of the corresponding counts. Equality and +inclusion compare corresponding counts. Each operation can accept inputs +with signed counts, but the output will exclude results with counts of zero +or below. .. doctest:: @@ -383,6 +388,8 @@ counts, but the output will exclude results with counts of zero or less. Counter({'a': 1, 'b': 1}) >>> c | d # union: max(c[x], d[x]) Counter({'a': 3, 'b': 2}) + >>> c ^ d # max(c[x], d[x]) - min(c[x], d[x]) + Counter({'a': 2, 'b': 1}) >>> c == d # equality: c[x] == d[x] False >>> c <= d # inclusion: c[x] <= d[x] @@ -400,6 +407,9 @@ or subtracting from an empty counter. .. versionadded:: 3.3 Added support for unary plus, unary minus, and in-place multiset operations. +.. versionadded:: 3.15 + Added support for the symmetric difference multiset operation, ``c ^ d``. + .. note:: Counters were primarily designed to work with positive integers to represent @@ -474,17 +484,19 @@ or subtracting from an empty counter. Unix. They are also useful for tracking transactions and other pools of data where only the most recent activity is of interest. + Deques are :ref:`generic ` over the type of their contents. + Deque objects support the following methods: - .. method:: append(x) + .. method:: append(item, /) - Add *x* to the right side of the deque. + Add *item* to the right side of the deque. - .. method:: appendleft(x) + .. method:: appendleft(item, /) - Add *x* to the left side of the deque. + Add *item* to the left side of the deque. .. method:: clear() @@ -499,38 +511,38 @@ or subtracting from an empty counter. .. versionadded:: 3.5 - .. method:: count(x) + .. method:: count(value, /) - Count the number of deque elements equal to *x*. + Count the number of deque elements equal to *value*. .. versionadded:: 3.2 - .. method:: extend(iterable) + .. method:: extend(iterable, /) Extend the right side of the deque by appending elements from the iterable argument. - .. method:: extendleft(iterable) + .. method:: extendleft(iterable, /) Extend the left side of the deque by appending elements from *iterable*. Note, the series of left appends results in reversing the order of elements in the iterable argument. - .. method:: index(x[, start[, stop]]) + .. method:: index(value[, start[, stop]]) - Return the position of *x* in the deque (at or after index *start* + Return the position of *value* in the deque (at or after index *start* and before index *stop*). Returns the first match or raises :exc:`ValueError` if not found. .. versionadded:: 3.5 - .. method:: insert(i, x) + .. method:: insert(index, value, /) - Insert *x* into the deque at position *i*. + Insert *value* into the deque at position *index*. If the insertion would cause a bounded deque to grow beyond *maxlen*, an :exc:`IndexError` is raised. @@ -550,7 +562,7 @@ or subtracting from an empty counter. elements are present, raises an :exc:`IndexError`. - .. method:: remove(value) + .. method:: remove(value, /) Remove the first occurrence of *value*. If not found, raises a :exc:`ValueError`. @@ -563,7 +575,7 @@ or subtracting from an empty counter. .. versionadded:: 3.2 - .. method:: rotate(n=1) + .. method:: rotate(n=1, /) Rotate the deque *n* steps to the right. If *n* is negative, rotate to the left. @@ -715,7 +727,9 @@ stack manipulations such as ``dup``, ``drop``, ``swap``, ``over``, ``pick``, :class:`defaultdict` objects ---------------------------- -.. class:: defaultdict(default_factory=None, /, [...]) +.. class:: defaultdict(default_factory=None, /, **kwargs) + defaultdict(default_factory, mapping, /, **kwargs) + defaultdict(default_factory, iterable, /, **kwargs) Return a new dictionary-like object. :class:`defaultdict` is a subclass of the built-in :class:`dict` class. It overrides one method and adds one writable @@ -727,11 +741,14 @@ stack manipulations such as ``dup``, ``drop``, ``swap``, ``over``, ``pick``, as if they were passed to the :class:`dict` constructor, including keyword arguments. + :class:`!defaultdict`\s are :ref:`generic ` over two types, + signifying (respectively) the types of the dictionary's keys and values. + :class:`defaultdict` objects support the following method in addition to the standard :class:`dict` operations: - .. method:: __missing__(key) + .. method:: __missing__(key, /) If the :attr:`default_factory` attribute is ``None``, this raises a :exc:`KeyError` exception with the *key* as argument. @@ -758,9 +775,9 @@ stack manipulations such as ``dup``, ``drop``, ``swap``, ``over``, ``pick``, .. attribute:: default_factory - This attribute is used by the :meth:`__missing__` method; it is - initialized from the first argument to the constructor, if present, or to - ``None``, if absent. + This attribute is used by the :meth:`~defaultdict.__missing__` method; + it is initialized from the first argument to the constructor, if present, + or to ``None``, if absent. .. versionchanged:: 3.9 Added merge (``|``) and update (``|=``) operators, specified in @@ -783,10 +800,10 @@ sequence of key-value pairs into a dictionary of lists: When each key is encountered for the first time, it is not already in the mapping; so an entry is automatically created using the :attr:`~defaultdict.default_factory` -function which returns an empty :class:`list`. The :meth:`!list.append` +function which returns an empty :class:`list`. The :meth:`list.append` operation then attaches the value to the new list. When keys are encountered again, the look-up proceeds normally (returning the list for that key) and the -:meth:`!list.append` operation adds another value to the list. This technique is +:meth:`list.append` operation adds another value to the list. This technique is simpler and faster than an equivalent technique using :meth:`dict.setdefault`: >>> d = {} @@ -937,7 +954,7 @@ In addition to the methods inherited from tuples, named tuples support three additional methods and two attributes. To prevent conflicts with field names, the method and attribute names start with an underscore. -.. classmethod:: somenamedtuple._make(iterable) +.. classmethod:: somenamedtuple._make(iterable, /) Class method that makes a new instance from an existing sequence or iterable. @@ -1134,7 +1151,9 @@ Some differences from :class:`dict` still remain: * Until Python 3.8, :class:`dict` lacked a :meth:`~object.__reversed__` method. -.. class:: OrderedDict([items]) +.. class:: OrderedDict(**kwargs) + OrderedDict(mapping, /, **kwargs) + OrderedDict(iterable, /, **kwargs) Return an instance of a :class:`dict` subclass that has methods specialized for rearranging dictionary order. @@ -1202,19 +1221,19 @@ If a new entry overwrites an existing entry, the original insertion position is changed and moved to the end:: class LastUpdatedOrderedDict(OrderedDict): - 'Store items in the order the keys were last added' + 'Store items in the order that the keys were last updated.' def __setitem__(self, key, value): super().__setitem__(key, value) self.move_to_end(key) An :class:`OrderedDict` would also be useful for implementing -variants of :func:`functools.lru_cache`: +variants of :deco:`functools.lru_cache`: .. testcode:: from collections import OrderedDict - from time import time + from time import monotonic class TimeBoundedLRU: "LRU Cache that invalidates and refreshes old entries." @@ -1229,10 +1248,10 @@ variants of :func:`functools.lru_cache`: if args in self.cache: self.cache.move_to_end(args) timestamp, result = self.cache[args] - if time() - timestamp <= self.maxage: + if monotonic() - timestamp <= self.maxage: return result result = self.func(*args) - self.cache[args] = time(), result + self.cache[args] = monotonic(), result if len(self.cache) > self.maxsize: self.cache.popitem(last=False) return result @@ -1315,23 +1334,31 @@ subclass directly from :class:`dict`; however, this class can be easier to work with because the underlying dictionary is accessible as an attribute. -.. class:: UserDict([initialdata]) +.. class:: UserDict(**kwargs) + UserDict(mapping, /, **kwargs) + UserDict(iterable, /, **kwargs) Class that simulates a dictionary. The instance's contents are kept in a regular dictionary, which is accessible via the :attr:`data` attribute of - :class:`UserDict` instances. If *initialdata* is provided, :attr:`data` is - initialized with its contents; note that a reference to *initialdata* will not - be kept, allowing it to be used for other purposes. + :class:`!UserDict` instances. If arguments are provided, they are used to + initialize :attr:`data`, like a regular dictionary. In addition to supporting the methods and operations of mappings, - :class:`UserDict` instances provide the following attribute: + :class:`!UserDict` instances provide the following attribute: .. attribute:: data A real dictionary used to store the contents of the :class:`UserDict` class. + :class:`!UserDict` instances also override the following method: + + .. method:: popitem + Remove and return a ``(key, value)`` pair from the wrapped dictionary. Pairs are + returned in the same order as ``data.popitem()``. (For the default + :meth:`dict.popitem`, this order is :abbr:`LIFO (last-in, first-out)`.) If the + dictionary is empty, raises a :exc:`KeyError`. :class:`UserList` objects ------------------------- diff --git a/Doc/library/colorsys.rst b/Doc/library/colorsys.rst index ffebf4e40dd609..dffc16ae8b7d47 100644 --- a/Doc/library/colorsys.rst +++ b/Doc/library/colorsys.rst @@ -4,13 +4,11 @@ .. module:: colorsys :synopsis: Conversion functions between RGB and other color systems. -.. sectionauthor:: David Ascher - **Source code:** :source:`Lib/colorsys.py` -------------- -The :mod:`colorsys` module defines bidirectional conversions of color values +The :mod:`!colorsys` module defines bidirectional conversions of color values between colors expressed in the RGB (Red Green Blue) color space used in computer monitors and three other coordinate systems: YIQ, HLS (Hue Lightness Saturation) and HSV (Hue Saturation Value). Coordinates in all of these color @@ -24,7 +22,7 @@ spaces, the coordinates are all between 0 and 1. https://poynton.ca/ColorFAQ.html and https://www.cambridgeincolour.com/tutorials/color-spaces.htm. -The :mod:`colorsys` module defines the following functions: +The :mod:`!colorsys` module defines the following functions: .. function:: rgb_to_yiq(r, g, b) diff --git a/Doc/library/compression.rst b/Doc/library/compression.rst index 618b4a3c2bd170..98719be9992acc 100644 --- a/Doc/library/compression.rst +++ b/Doc/library/compression.rst @@ -1,6 +1,8 @@ The :mod:`!compression` package =============================== +.. module:: compression + .. versionadded:: 3.14 The :mod:`!compression` package contains the canonical compression modules diff --git a/Doc/library/compression.zstd.rst b/Doc/library/compression.zstd.rst index a901403621b84f..6d99e36e1e5bb6 100644 --- a/Doc/library/compression.zstd.rst +++ b/Doc/library/compression.zstd.rst @@ -33,6 +33,8 @@ The :mod:`!compression.zstd` module contains: * The :class:`CompressionParameter`, :class:`DecompressionParameter`, and :class:`Strategy` classes for setting advanced (de)compression parameters. +.. include:: ../includes/optional-module.rst + Exceptions ---------- @@ -71,7 +73,7 @@ Reading and writing compressed files argument is not None, a :exc:`!TypeError` will be raised. When writing, the *options* argument can be a dictionary - providing advanced decompression parameters; see + providing advanced compression parameters; see :class:`CompressionParameter` for detailed information about supported parameters. The *level* argument is the compression level to use when writing compressed data. Only one of *level* or *options* may be non-None. @@ -115,7 +117,7 @@ Reading and writing compressed files argument is not None, a :exc:`!TypeError` will be raised. When writing, the *options* argument can be a dictionary - providing advanced decompression parameters; see + providing advanced compression parameters; see :class:`CompressionParameter` for detailed information about supported parameters. The *level* argument is the compression level to use when writing compressed data. Only one of *level* or *options* may be passed. The @@ -329,10 +331,14 @@ Compressing and decompressing data in memory If *max_length* is non-negative, the method returns at most *max_length* bytes of decompressed data. If this limit is reached and further - output can be produced, the :attr:`~.needs_input` attribute will - be set to ``False``. In this case, the next call to + output can be produced (or EOF is reached), the :attr:`~.needs_input` + attribute will be set to ``False``. In this case, the next call to :meth:`~.decompress` may provide *data* as ``b''`` to obtain - more of the output. + more of the output. The full content can thus be read like:: + + process_output(d.decompress(data, max_length)) + while not d.eof and not d.needs_input: + process_output(d.decompress(b"", max_length)) If all of the input data was decompressed and returned (either because this was less than *max_length* bytes, or because diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst index dd92765038c4f7..a32c3828313454 100644 --- a/Doc/library/concurrent.futures.rst +++ b/Doc/library/concurrent.futures.rst @@ -12,7 +12,7 @@ and :source:`Lib/concurrent/futures/interpreter.py` -------------- -The :mod:`concurrent.futures` module provides a high-level interface for +The :mod:`!concurrent.futures` module provides a high-level interface for asynchronously executing callables. The asynchronous execution can be performed with threads, using @@ -21,6 +21,11 @@ or separate processes, using :class:`ProcessPoolExecutor`. Each implements the same interface, which is defined by the abstract :class:`Executor` class. +:class:`concurrent.futures.Future` must not be confused with +:class:`asyncio.Future`, which is designed for use with :mod:`asyncio` +tasks and coroutines. See the :doc:`asyncio's Future ` +documentation for a detailed comparison of the two. + .. include:: ../includes/wasm-notavail.rst Executor Objects @@ -101,10 +106,10 @@ Executor Objects executor has started running will be completed prior to this method returning. The remaining futures are cancelled. - You can avoid having to call this method explicitly if you use the - :keyword:`with` statement, which will shutdown the :class:`Executor` - (waiting as if :meth:`Executor.shutdown` were called with *wait* set to - ``True``):: + You can avoid having to call this method explicitly if you use the executor + as a :term:`context manager` via the :keyword:`with` statement, which + will shutdown the :class:`Executor` (waiting as if :meth:`Executor.shutdown` + were called with *wait* set to ``True``):: import shutil with ThreadPoolExecutor(max_workers=4) as e: @@ -151,7 +156,9 @@ And:: print(f.result()) executor = ThreadPoolExecutor(max_workers=1) - executor.submit(wait_on_future) + future = executor.submit(wait_on_future) + # Note: calling future.result() would also cause a deadlock because + # the single worker thread is already waiting for wait_on_future(). .. class:: ThreadPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=()) @@ -239,6 +246,8 @@ ThreadPoolExecutor Example InterpreterPoolExecutor ----------------------- +.. versionadded:: 3.14 + The :class:`InterpreterPoolExecutor` class uses a pool of interpreters to execute calls asynchronously. It is a :class:`ThreadPoolExecutor` subclass, which means each worker is running in its own thread. @@ -306,7 +315,7 @@ the bytes over a shared :mod:`socket ` or .. note:: The executor may replace uncaught exceptions from *initializer* - with :class:`~concurrent.futures.interpreter.ExecutionFailed`. + with :class:`~concurrent.interpreters.ExecutionFailed`. Other caveats from parent :class:`ThreadPoolExecutor` apply here. @@ -318,11 +327,11 @@ likewise serializes the return value when sending it back. When a worker's current task raises an uncaught exception, the worker always tries to preserve the exception as-is. If that is successful then it also sets the ``__cause__`` to a corresponding -:class:`~concurrent.futures.interpreter.ExecutionFailed` +:class:`~concurrent.interpreters.ExecutionFailed` instance, which contains a summary of the original exception. In the uncommon case that the worker is not able to preserve the original as-is then it directly preserves the corresponding -:class:`~concurrent.futures.interpreter.ExecutionFailed` +:class:`~concurrent.interpreters.ExecutionFailed` instance instead. @@ -342,6 +351,11 @@ that :class:`ProcessPoolExecutor` will not work in the interactive interpreter. Calling :class:`Executor` or :class:`Future` methods from a callable submitted to a :class:`ProcessPoolExecutor` will result in deadlock. +Note that the restrictions on functions and arguments needing to picklable as +per :class:`multiprocessing.Process` apply when using :meth:`~Executor.submit` +and :meth:`~Executor.map` on a :class:`ProcessPoolExecutor`. A function defined +in a REPL or a lambda should not be expected to work. + .. class:: ProcessPoolExecutor(max_workers=None, mp_context=None, initializer=None, initargs=(), max_tasks_per_child=None) An :class:`Executor` subclass that executes calls asynchronously using a pool @@ -372,6 +386,11 @@ to a :class:`ProcessPoolExecutor` will result in deadlock. default in absence of a *mp_context* parameter. This feature is incompatible with the "fork" start method. + .. note:: + Bugs have been reported when using the *max_tasks_per_child* feature that + can result in the :class:`ProcessPoolExecutor` hanging in some + circumstances. Follow its eventual resolution in :gh:`115634`. + .. versionchanged:: 3.3 When one of the worker processes terminates abruptly, a :exc:`~concurrent.futures.process.BrokenProcessPool` error is now raised. @@ -708,15 +727,6 @@ Exception classes .. versionadded:: 3.14 -.. exception:: ExecutionFailed - - Raised from :class:`~concurrent.futures.InterpreterPoolExecutor` when - the given initializer fails or from - :meth:`~concurrent.futures.Executor.submit` when there's an uncaught - exception from the submitted task. - - .. versionadded:: 3.14 - .. currentmodule:: concurrent.futures.process .. exception:: BrokenProcessPool diff --git a/Doc/library/concurrent.interpreters.rst b/Doc/library/concurrent.interpreters.rst index be9d565f8e0d38..a7b115e5f6307d 100644 --- a/Doc/library/concurrent.interpreters.rst +++ b/Doc/library/concurrent.interpreters.rst @@ -4,9 +4,6 @@ .. module:: concurrent.interpreters :synopsis: Multiple interpreters in the same process -.. moduleauthor:: Eric Snow -.. sectionauthor:: Eric Snow - .. versionadded:: 3.14 **Source code:** :source:`Lib/concurrent/interpreters` @@ -29,12 +26,12 @@ Actual concurrency is available separately through .. seealso:: :class:`~concurrent.futures.InterpreterPoolExecutor` - combines threads with interpreters in a familiar interface. + Combines threads with interpreters in a familiar interface. - .. XXX Add references to the upcoming HOWTO docs in the seealso block. + .. XXX Add references to the upcoming HOWTO docs in the seealso block. :ref:`isolating-extensions-howto` - how to update an extension module to support multiple interpreters + How to update an extension module to support multiple interpreters. :pep:`554` @@ -134,7 +131,7 @@ makes them similar to processes, but they still enjoy in-process efficiency, like threads. All that said, interpreters do naturally support certain flavors of -concurrency, as a powerful side effect of that isolation. +concurrency. There's a powerful side effect of that isolation. It enables a different approach to concurrency than you can take with async or threads. It's a similar concurrency model to CSP or the actor model, diff --git a/Doc/library/configparser.rst b/Doc/library/configparser.rst index bb109a9b742cb7..4d720176fcc334 100644 --- a/Doc/library/configparser.rst +++ b/Doc/library/configparser.rst @@ -4,13 +4,6 @@ .. module:: configparser :synopsis: Configuration file parser. -.. moduleauthor:: Ken Manheimer -.. moduleauthor:: Barry Warsaw -.. moduleauthor:: Eric S. Raymond -.. moduleauthor:: Łukasz Langa -.. sectionauthor:: Christopher G. Petrilli -.. sectionauthor:: Łukasz Langa - **Source code:** :source:`Lib/configparser.py` .. index:: @@ -31,6 +24,11 @@ can be customized by end users easily. This library does *not* interpret or write the value-type prefixes used in the Windows Registry extended version of INI syntax. +.. warning:: + Be cautious when parsing data from untrusted sources. A malicious + INI file may cause the decoder to consume considerable CPU and memory + resources. Limiting the size of data to be parsed is recommended. + .. seealso:: Module :mod:`tomllib` @@ -80,7 +78,7 @@ Let's take a very basic configuration file that looks like this: The structure of INI files is described `in the following section <#supported-ini-file-structure>`_. Essentially, the file consists of sections, each of which contains keys with values. -:mod:`configparser` classes can read and write such files. Let's start by +:mod:`!configparser` classes can read and write such files. Let's start by creating the above configuration file programmatically. .. doctest:: @@ -449,7 +447,7 @@ Mapping Protocol Access .. versionadded:: 3.2 Mapping protocol access is a generic name for functionality that enables using -custom objects as if they were dictionaries. In case of :mod:`configparser`, +custom objects as if they were dictionaries. In case of :mod:`!configparser`, the mapping interface implementation is using the ``parser['section']['option']`` notation. @@ -459,7 +457,7 @@ the original parser on demand. What's even more important is that when values are changed on a section proxy, they are actually mutated in the original parser. -:mod:`configparser` objects behave as close to actual dictionaries as possible. +:mod:`!configparser` objects behave as close to actual dictionaries as possible. The mapping interface is complete and adheres to the :class:`~collections.abc.MutableMapping` ABC. However, there are a few differences that should be taken into account: @@ -507,7 +505,7 @@ Customizing Parser Behaviour ---------------------------- There are nearly as many INI format variants as there are applications using it. -:mod:`configparser` goes a long way to provide support for the largest sensible +:mod:`!configparser` goes a long way to provide support for the largest sensible set of INI styles available. The default functionality is mainly dictated by historical background and it's very likely that you will want to customize some of the features. @@ -560,7 +558,7 @@ the :meth:`!__init__` options: * *allow_no_value*, default value: ``False`` Some configuration files are known to include settings without values, but - which otherwise conform to the syntax supported by :mod:`configparser`. The + which otherwise conform to the syntax supported by :mod:`!configparser`. The *allow_no_value* parameter to the constructor can be used to indicate that such values should be accepted: @@ -615,7 +613,7 @@ the :meth:`!__init__` options: prefixes for whole line comments. .. versionchanged:: 3.2 - In previous versions of :mod:`configparser` behaviour matched + In previous versions of :mod:`!configparser` behaviour matched ``comment_prefixes=('#',';')`` and ``inline_comment_prefixes=(';',)``. Please note that config parsers don't support escaping of comment prefixes so @@ -672,7 +670,7 @@ the :meth:`!__init__` options: parsers in new applications. .. versionchanged:: 3.2 - In previous versions of :mod:`configparser` behaviour matched + In previous versions of :mod:`!configparser` behaviour matched ``strict=False``. * *empty_lines_in_values*, default value: ``True`` @@ -842,7 +840,7 @@ be overridden by subclasses or by attribute assignment. Legacy API Examples ------------------- -Mainly because of backwards compatibility concerns, :mod:`configparser` +Mainly because of backwards compatibility concerns, :mod:`!configparser` provides also a legacy API with explicit ``get``/``set`` methods. While there are valid use cases for the methods outlined below, mapping protocol access is preferred for new projects. The legacy API is at times more advanced, @@ -1378,7 +1376,7 @@ Exceptions .. exception:: Error - Base class for all other :mod:`configparser` exceptions. + Base class for all other :mod:`!configparser` exceptions. .. exception:: NoSectionError diff --git a/Doc/library/constants.rst b/Doc/library/constants.rst index c0ac4ea8412ebd..6f005f98bd3ede 100644 --- a/Doc/library/constants.rst +++ b/Doc/library/constants.rst @@ -22,7 +22,7 @@ A small number of constants live in the built-in namespace. They are: An object frequently used to represent the absence of a value, as when default arguments are not passed to a function. Assignments to ``None`` are illegal and raise a :exc:`SyntaxError`. - ``None`` is the sole instance of the :data:`~types.NoneType` type. + ``None`` is the sole instance of the :class:`~types.NoneType` type. .. data:: NotImplemented @@ -33,7 +33,7 @@ A small number of constants live in the built-in namespace. They are: the other type; may be returned by the in-place binary special methods (e.g. :meth:`~object.__imul__`, :meth:`~object.__iand__`, etc.) for the same purpose. It should not be evaluated in a boolean context. - :data:`!NotImplemented` is the sole instance of the :data:`types.NotImplementedType` type. + :data:`!NotImplemented` is the sole instance of the :class:`types.NotImplementedType` type. .. note:: @@ -65,9 +65,10 @@ A small number of constants live in the built-in namespace. They are: .. index:: single: ...; ellipsis literal .. data:: Ellipsis - The same as the ellipsis literal "``...``". Special value used mostly in conjunction - with extended slicing syntax for user-defined container data types. - ``Ellipsis`` is the sole instance of the :data:`types.EllipsisType` type. + The same as the ellipsis literal "``...``", an object frequently used to + indicate that something is omitted. Assignment to ``Ellipsis`` is possible, but + assignment to ``...`` raises a :exc:`SyntaxError`. + ``Ellipsis`` is the sole instance of the :class:`types.EllipsisType` type. .. data:: __debug__ @@ -97,15 +98,17 @@ should not be used in programs. exit(code=None) Objects that when printed, print a message like "Use quit() or Ctrl-D - (i.e. EOF) to exit", and when called, raise :exc:`SystemExit` with the + (i.e. EOF) to exit", and when accessed directly in the interactive + interpreter or called as functions, raise :exc:`SystemExit` with the specified exit code. .. data:: help :noindex: Object that when printed, prints the message "Type help() for interactive - help, or help(object) for help about object.", and when called, - acts as described :func:`elsewhere `. + help, or help(object) for help about object.", and when accessed directly + in the interactive interpreter, invokes the built-in help system + (see :func:`help`). .. data:: copyright credits diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst index 176be4ff333955..666f8599744888 100644 --- a/Doc/library/contextlib.rst +++ b/Doc/library/contextlib.rst @@ -21,9 +21,9 @@ Functions and classes provided: .. class:: AbstractContextManager An :term:`abstract base class` for classes that implement - :meth:`object.__enter__` and :meth:`object.__exit__`. A default - implementation for :meth:`object.__enter__` is provided which returns - ``self`` while :meth:`object.__exit__` is an abstract method which by default + :meth:`~object.__enter__` and :meth:`~object.__exit__`. A default + implementation for :meth:`~object.__enter__` is provided which returns + ``self`` while :meth:`~object.__exit__` is an abstract method which by default returns ``None``. See also the definition of :ref:`typecontextmanager`. .. versionadded:: 3.6 @@ -32,9 +32,9 @@ Functions and classes provided: .. class:: AbstractAsyncContextManager An :term:`abstract base class` for classes that implement - :meth:`object.__aenter__` and :meth:`object.__aexit__`. A default - implementation for :meth:`object.__aenter__` is provided which returns - ``self`` while :meth:`object.__aexit__` is an abstract method which by default + :meth:`~object.__aenter__` and :meth:`~object.__aexit__`. A default + implementation for :meth:`~object.__aenter__` is provided which returns + ``self`` while :meth:`~object.__aexit__` is an abstract method which by default returns ``None``. See also the definition of :ref:`async-context-managers`. @@ -88,11 +88,11 @@ Functions and classes provided: the exception has been handled, and execution will resume with the statement immediately following the :keyword:`!with` statement. - :func:`contextmanager` uses :class:`ContextDecorator` so the context managers + :deco:`contextmanager` uses :class:`ContextDecorator` so the context managers it creates can be used as decorators as well as in :keyword:`with` statements. When used as a decorator, a new generator instance is implicitly created on each function call (this allows the otherwise "one-shot" context managers - created by :func:`contextmanager` to meet the requirement that context + created by :deco:`contextmanager` to meet the requirement that context managers support multiple invocations in order to be used as decorators). .. versionchanged:: 3.2 @@ -101,7 +101,7 @@ Functions and classes provided: .. decorator:: asynccontextmanager - Similar to :func:`~contextlib.contextmanager`, but creates an + Similar to :deco:`~contextlib.contextmanager`, but creates an :ref:`asynchronous context manager `. This function is a :term:`decorator` that can be used to define a factory @@ -128,7 +128,7 @@ Functions and classes provided: .. versionadded:: 3.7 - Context managers defined with :func:`asynccontextmanager` can be used + Context managers defined with :deco:`asynccontextmanager` can be used either as decorators or with :keyword:`async with` statements:: import time @@ -148,11 +148,11 @@ Functions and classes provided: When used as a decorator, a new generator instance is implicitly created on each function call. This allows the otherwise "one-shot" context managers - created by :func:`asynccontextmanager` to meet the requirement that context + created by :deco:`asynccontextmanager` to meet the requirement that context managers support multiple invocations in order to be used as decorators. .. versionchanged:: 3.10 - Async context managers created with :func:`asynccontextmanager` can + Async context managers created with :deco:`asynccontextmanager` can be used as decorators. @@ -228,7 +228,7 @@ Functions and classes provided: .. function:: nullcontext(enter_result=None) - Return a context manager that returns *enter_result* from ``__enter__``, but + Return a context manager that returns *enter_result* from :meth:`~object.__enter__`, but otherwise does nothing. It is intended to be used as a stand-in for an optional context manager, for example:: @@ -327,15 +327,15 @@ Functions and classes provided: .. function:: redirect_stdout(new_target) Context manager for temporarily redirecting :data:`sys.stdout` to - another file or file-like object. + another :term:`file object`. This tool adds flexibility to existing functions or classes whose output - is hardwired to stdout. + is hardwired to :data:`sys.stdout`. For example, the output of :func:`help` normally is sent to *sys.stdout*. You can capture that output in a string by redirecting the output to an :class:`io.StringIO` object. The replacement stream is returned from the - ``__enter__`` method and so is available as the target of the + :meth:`~object.__enter__` method and so is available as the target of the :keyword:`with` statement:: with redirect_stdout(io.StringIO()) as f: @@ -366,8 +366,8 @@ Functions and classes provided: .. function:: redirect_stderr(new_target) - Similar to :func:`~contextlib.redirect_stdout` but redirecting - :data:`sys.stderr` to another file or file-like object. + Similar to :func:`~contextlib.redirect_stdout` but redirecting the global + :data:`sys.stderr` to another :term:`file object`. This context manager is :ref:`reentrant `. @@ -396,10 +396,11 @@ Functions and classes provided: A base class that enables a context manager to also be used as a decorator. Context managers inheriting from ``ContextDecorator`` have to implement - ``__enter__`` and ``__exit__`` as normal. ``__exit__`` retains its optional + :meth:`~object.__enter__` and :meth:`~object.__exit__` as normal. + ``__exit__`` retains its optional exception handling even when used as a decorator. - ``ContextDecorator`` is used by :func:`contextmanager`, so you get this + ``ContextDecorator`` is used by :deco:`contextmanager`, so you get this functionality automatically. Example of ``ContextDecorator``:: @@ -466,12 +467,40 @@ Functions and classes provided: statements. If this is not the case, then the original construct with the explicit :keyword:`!with` statement inside the function should be used. + When the decorated callable is a generator function, coroutine function, or + asynchronous generator function, the returned wrapper is of the same kind + and keeps the context manager open for the lifetime of the iteration or + await rather than only for the call that creates the generator or coroutine + object. Wrapped generators and asynchronous generators are explicitly + closed when iteration ends, as if by :func:`closing` or :func:`aclosing`. + + .. note:: + For asynchronous generators the wrapper re-yields each value with + ``async for``; values sent with :meth:`~agen.asend` and exceptions + thrown with :meth:`~agen.athrow` are not forwarded to the wrapped + generator. + .. versionadded:: 3.2 + .. versionchanged:: 3.15 + Decorating a generator function, coroutine function, or asynchronous + generator function now keeps the context manager open across iteration + or await. Previously the context manager exited as soon as the + generator or coroutine object was created. + .. class:: AsyncContextDecorator - Similar to :class:`ContextDecorator` but only for asynchronous functions. + Similar to :class:`ContextDecorator`, but the context manager is entered + and exited with :keyword:`async with`. Decorate coroutine functions and + asynchronous generator functions with this class; the returned wrapper is + of the same kind. + + .. note:: + Synchronous functions and generators are accepted, but the wrapper is + always asynchronous, so the decorated callable must then be awaited or + iterated with ``async for``. If that change of calling convention is + not intended, use :class:`ContextDecorator` instead. Example of ``AsyncContextDecorator``:: @@ -509,6 +538,13 @@ Functions and classes provided: .. versionadded:: 3.10 + .. versionchanged:: 3.15 + Decorating an asynchronous generator function now keeps the context + manager open across iteration. Previously the context manager exited + as soon as the generator object was created. Synchronous functions + and synchronous generator functions are also now accepted, with an + asynchronous wrapper returned. + .. class:: ExitStack() @@ -564,6 +600,10 @@ Functions and classes provided: Raises :exc:`TypeError` instead of :exc:`AttributeError` if *cm* is not a context manager. + .. versionchanged:: 3.15 + Added support for arbitrary descriptors :meth:`!__enter__` and + :meth:`!__exit__`. + .. method:: push(exit) Adds a context manager's :meth:`~object.__exit__` method to the callback stack. @@ -582,6 +622,9 @@ Functions and classes provided: The passed in object is returned from the function, allowing this method to be used as a function decorator. + .. versionchanged:: 3.15 + Added support for arbitrary descriptors :meth:`!__exit__`. + .. method:: callback(callback, /, *args, **kwds) Accepts an arbitrary callback function and arguments and adds it to @@ -639,11 +682,17 @@ Functions and classes provided: Raises :exc:`TypeError` instead of :exc:`AttributeError` if *cm* is not an asynchronous context manager. + .. versionchanged:: 3.15 + Added support for arbitrary descriptors :meth:`!__aenter__` and :meth:`!__aexit__`. + .. method:: push_async_exit(exit) Similar to :meth:`ExitStack.push` but expects either an asynchronous context manager or a coroutine function. + .. versionchanged:: 3.15 + Added support for arbitrary descriptors :meth:`!__aexit__`. + .. method:: push_async_callback(callback, /, *args, **kwds) Similar to :meth:`ExitStack.callback` but expects a coroutine function. @@ -653,7 +702,7 @@ Functions and classes provided: Similar to :meth:`ExitStack.close` but properly handles awaitables. - Continuing the example for :func:`asynccontextmanager`:: + Continuing the example for :deco:`asynccontextmanager`:: async with AsyncExitStack() as stack: connections = [await stack.enter_async_context(get_connection()) @@ -668,7 +717,7 @@ Examples and Recipes -------------------- This section describes some examples and recipes for making effective use of -the tools provided by :mod:`contextlib`. +the tools provided by :mod:`!contextlib`. Supporting a variable number of context managers @@ -697,9 +746,9 @@ context management protocol. Catching exceptions from ``__enter__`` methods ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -It is occasionally desirable to catch exceptions from an ``__enter__`` +It is occasionally desirable to catch exceptions from an :meth:`~object.__enter__` method implementation, *without* inadvertently catching exceptions from -the :keyword:`with` statement body or the context manager's ``__exit__`` +the :keyword:`with` statement body or the context manager's :meth:`~object.__exit__` method. By using :class:`ExitStack` the steps in the context management protocol can be separated slightly in order to allow this:: @@ -911,7 +960,7 @@ Files are an example of effectively single use context managers, since the first :keyword:`with` statement will close the file, preventing any further IO operations using that file object. -Context managers created using :func:`contextmanager` are also single use +Context managers created using :deco:`contextmanager` are also single use context managers, and will complain about the underlying generator failing to yield if an attempt is made to use them a second time:: diff --git a/Doc/library/contextvars.rst b/Doc/library/contextvars.rst index 57580ce026e96a..b0cc0be8e911bf 100644 --- a/Doc/library/contextvars.rst +++ b/Doc/library/contextvars.rst @@ -4,8 +4,6 @@ .. module:: contextvars :synopsis: Context Variables -.. sectionauthor:: Yury Selivanov - -------------- This module provides APIs to manage, store, and access context-local @@ -44,6 +42,9 @@ Context Variables references to context variables which prevents context variables from being properly garbage collected. + :class:`!ContextVar`\s are :ref:`generic ` over the type of + their contained value. + .. attribute:: ContextVar.name The name of the variable. This is a read-only property. @@ -77,6 +78,32 @@ Context Variables to restore the variable to its previous value via the :meth:`ContextVar.reset` method. + For convenience, the token object can be used as a context manager + to avoid calling :meth:`ContextVar.reset` manually:: + + var = ContextVar('var', default='default value') + + with var.set('new value'): + assert var.get() == 'new value' + + assert var.get() == 'default value' + + It is a shorthand for:: + + var = ContextVar('var', default='default value') + + token = var.set('new value') + try: + assert var.get() == 'new value' + finally: + var.reset(token) + + assert var.get() == 'default value' + + .. versionadded:: 3.14 + + Added support for using tokens as context managers. + .. method:: reset(token) Reset the context variable to the value it had before the @@ -93,24 +120,21 @@ Context Variables # After the reset call the var has no value again, so # var.get() would raise a LookupError. + The same *token* cannot be used twice. + .. class:: Token *Token* objects are returned by the :meth:`ContextVar.set` method. They can be passed to the :meth:`ContextVar.reset` method to revert the value of the variable to what it was before the corresponding - *set*. - - The token supports :ref:`context manager protocol ` - to restore the corresponding context variable value at the exit from - :keyword:`with` block:: - - var = ContextVar('var', default='default value') + *set*. A single token cannot reset a context variable more than once. - with var.set('new value'): - assert var.get() == 'new value' + Tokens support the :ref:`context manager protocol ` + to automatically reset context variables. See :meth:`ContextVar.set`. - assert var.get() == 'default value' + Tokens are :ref:`generic ` over the same type as the + :class:`ContextVar` which created them. .. versionadded:: 3.14 @@ -313,7 +337,7 @@ client:: addr = writer.transport.get_extra_info('socket').getpeername() client_addr_var.set(addr) - # In any code that we call is now possible to get + # In any code that we call, it is now possible to get the # client's address by calling 'client_addr_var.get()'. while True: diff --git a/Doc/library/copy.rst b/Doc/library/copy.rst index 210ad7188003e6..39fc7800d03a91 100644 --- a/Doc/library/copy.rst +++ b/Doc/library/copy.rst @@ -72,15 +72,19 @@ file, socket, window, or any similar types. It does "copy" functions and classes (shallow and deeply), by returning the original object unchanged; this is compatible with the way these are treated by the :mod:`pickle` module. -Shallow copies of dictionaries can be made using :meth:`dict.copy`, and -of lists by assigning a slice of the entire list, for example, -``copied_list = original_list[:]``. +Shallow copies of many collections can be made using the corresponding +:meth:`!copy` method (such as :meth:`list.copy`, :meth:`dict.copy` or +:meth:`set.copy`), and of sequences (such as lists or bytearrays) by making +a slice of the entire sequence (``sequence[:]``). +However, these methods and slicing can create an instance of the base type +when copying an instance of a subclass, whereas :func:`copy.copy` normally +returns an instance of the same type. .. index:: pair: module; pickle Classes can use the same interfaces to control copying that they use to control pickling. See the description of module :mod:`pickle` for information on these -methods. In fact, the :mod:`copy` module uses the registered +methods. In fact, the :mod:`!copy` module uses the registered pickle functions from the :mod:`copyreg` module. .. index:: diff --git a/Doc/library/copyreg.rst b/Doc/library/copyreg.rst index 6e3144824ebe91..d59936029da69d 100644 --- a/Doc/library/copyreg.rst +++ b/Doc/library/copyreg.rst @@ -12,7 +12,7 @@ -------------- -The :mod:`copyreg` module offers a way to define functions used while pickling +The :mod:`!copyreg` module offers a way to define functions used while pickling specific objects. The :mod:`pickle` and :mod:`copy` modules use those functions when pickling/copying those objects. The module provides configuration information about object constructors which are not classes. diff --git a/Doc/library/crypt.rst b/Doc/library/crypt.rst index 9ff37196ccf69f..647cb4925f31da 100644 --- a/Doc/library/crypt.rst +++ b/Doc/library/crypt.rst @@ -13,7 +13,7 @@ being deprecated in Python 3.11. The removal was decided in :pep:`594`. Applications can use the :mod:`hashlib` module from the standard library. Other possible replacements are third-party libraries from PyPI: -:pypi:`legacycrypt`, :pypi:`bcrypt`, :pypi:`argon2-cffi`, or :pypi:`passlib`. +:pypi:`legacycrypt`, :pypi:`bcrypt`, or :pypi:`argon2-cffi`. These are not supported or maintained by the Python core team. The last version of Python that provided the :mod:`!crypt` module was diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index d39c4ca4a5838b..21ecdbcc08f348 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -4,8 +4,6 @@ .. module:: csv :synopsis: Write and read tabular data to and from delimited files. -.. sectionauthor:: Skip Montanaro - **Source code:** :source:`Lib/csv.py` .. index:: @@ -25,14 +23,14 @@ similar enough that it is possible to write a single module which can efficiently manipulate such data, hiding the details of reading and writing the data from the programmer. -The :mod:`csv` module implements classes to read and write tabular data in CSV +The :mod:`!csv` module implements classes to read and write tabular data in CSV format. It allows programmers to say, "write this data in the format preferred by Excel," or "read data from this file which was generated by Excel," without knowing the precise details of the CSV format used by Excel. Programmers can also describe the CSV formats understood by other applications or define their own special-purpose CSV formats. -The :mod:`csv` module's :class:`reader` and :class:`writer` objects read and +The :mod:`!csv` module's :class:`reader` and :class:`writer` objects read and write sequences. Programmers can also read and write data in dictionary form using the :class:`DictReader` and :class:`DictWriter` classes. @@ -47,7 +45,7 @@ using the :class:`DictReader` and :class:`DictWriter` classes. Module Contents --------------- -The :mod:`csv` module defines the following functions: +The :mod:`!csv` module defines the following functions: .. index:: @@ -113,7 +111,7 @@ The :mod:`csv` module defines the following functions: spamwriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam']) -.. function:: register_dialect(name[, dialect[, **fmtparams]]) +.. function:: register_dialect(name, /, dialect='excel', **fmtparams) Associate *dialect* with *name*. *name* must be a string. The dialect can be specified either by passing a sub-class of :class:`Dialect`, or @@ -139,13 +137,14 @@ The :mod:`csv` module defines the following functions: Return the names of all registered dialects. -.. function:: field_size_limit([new_limit]) +.. function:: field_size_limit() + field_size_limit(new_limit) Returns the current maximum field size allowed by the parser. If *new_limit* is given, this becomes the new limit. -The :mod:`csv` module defines the following classes: +The :mod:`!csv` module defines the following classes: .. class:: DictReader(f, fieldnames=None, restkey=None, restval=None, \ dialect='excel', *args, **kwds) @@ -294,8 +293,8 @@ The :mod:`csv` module defines the following classes: - the second through n-th rows contain strings where at least one value's length differs from that of the putative header of that column. - Twenty rows after the first row are sampled; if more than half of columns + - rows meet the criteria, :const:`True` is returned. + Twenty-one rows after the header are sampled; if more than half of the + columns + rows meet the criteria, :const:`True` is returned. .. note:: @@ -313,7 +312,7 @@ An example for :class:`Sniffer` use:: .. _csv-constants: -The :mod:`csv` module defines the following constants: +The :mod:`!csv` module defines the following constants: .. data:: QUOTE_ALL @@ -374,7 +373,7 @@ The :mod:`csv` module defines the following constants: .. versionadded:: 3.12 -The :mod:`csv` module defines the following exception: +The :mod:`!csv` module defines the following exception: .. exception:: Error @@ -467,7 +466,8 @@ Dialects support the following attributes: .. attribute:: Dialect.skipinitialspace When :const:`True`, spaces immediately following the *delimiter* are ignored. - The default is :const:`False`. + The default is :const:`False`. When combining ``delimiter=' '`` with + ``skipinitialspace=True``, unquoted empty fields are not allowed. .. attribute:: Dialect.strict @@ -526,7 +526,7 @@ out surrounded by parens. This may cause some problems for other programs which read CSV files (assuming they support complex numbers at all). -.. method:: csvwriter.writerow(row) +.. method:: csvwriter.writerow(row, /) Write the *row* parameter to the writer's file object, formatted according to the current :class:`Dialect`. Return the return value of the call to the @@ -535,7 +535,7 @@ read CSV files (assuming they support complex numbers at all). .. versionchanged:: 3.5 Added support of arbitrary iterables. -.. method:: csvwriter.writerows(rows) +.. method:: csvwriter.writerows(rows, /) Write all elements in *rows* (an iterable of *row* objects as described above) to the writer's file object, formatted according to the current @@ -636,7 +636,7 @@ done:: .. rubric:: Footnotes .. [1] If ``newline=''`` is not specified, newlines embedded inside quoted fields - will not be interpreted correctly, and on platforms that use ``\r\n`` linendings + will not be interpreted correctly, and on platforms that use ``\r\n`` line endings on write an extra ``\r`` will be added. It should always be safe to specify ``newline=''``, since the csv module does its own (:term:`universal `) newline handling. diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index 09f596101b4d1e..51f08fdc0544c9 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -4,26 +4,30 @@ .. module:: ctypes :synopsis: A foreign function library for Python. -.. moduleauthor:: Thomas Heller - **Source code:** :source:`Lib/ctypes` -------------- -:mod:`ctypes` is a foreign function library for Python. It provides C compatible +:mod:`!ctypes` is a foreign function library for Python. It provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python. +.. include:: ../includes/optional-module.rst + +.. warning:: + + :mod:`!ctypes` provides low-level access to native libraries and the + process's memory, bypassing Python's safety mechanisms and allowing + execution of arbitrary native code. + Incorrect use can corrupt data and objects, reveal sensitive information, + cause crashes, or otherwise compromise the running process. + .. _ctypes-ctypes-tutorial: ctypes tutorial --------------- -Note: The code samples in this tutorial use :mod:`doctest` to make sure that -they actually work. Since some code samples behave differently under Linux, -Windows, or macOS, they contain doctest directives in comments. - Note: Some code samples reference the ctypes :class:`c_int` type. On platforms where ``sizeof(long) == sizeof(int)`` it is an alias to :class:`c_long`. So, you should not be confused if :class:`c_long` is printed if you would expect @@ -34,13 +38,16 @@ So, you should not be confused if :class:`c_long` is printed if you would expect Loading dynamic link libraries ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -:mod:`ctypes` exports the *cdll*, and on Windows *windll* and *oledll* +:mod:`!ctypes` exports the :py:data:`~ctypes.cdll`, and on Windows +:py:data:`~ctypes.windll` and :py:data:`~ctypes.oledll` objects, for loading dynamic link libraries. -You load libraries by accessing them as attributes of these objects. *cdll* -loads libraries which export functions using the standard ``cdecl`` calling -convention, while *windll* libraries call functions using the ``stdcall`` -calling convention. *oledll* also uses the ``stdcall`` calling convention, and +You load libraries by accessing them as attributes of these objects. +:py:data:`!cdll` loads libraries which export functions using the +standard ``cdecl`` calling convention, while :py:data:`!windll` +libraries call functions using the ``stdcall`` +calling convention. +:py:data:`~oledll` also uses the ``stdcall`` calling convention, and assumes the functions return a Windows :c:type:`!HRESULT` error code. The error code is used to automatically raise an :class:`OSError` exception when the function call fails. @@ -70,11 +77,13 @@ Windows appends the usual ``.dll`` file suffix automatically. being used by Python. Where possible, use native Python functionality, or else import and use the ``msvcrt`` module. -On Linux, it is required to specify the filename *including* the extension to +Other systems require the filename *including* the extension to load a library, so attribute access can not be used to load libraries. Either the :meth:`~LibraryLoader.LoadLibrary` method of the dll loaders should be used, -or you should load the library by creating an instance of CDLL by calling -the constructor:: +or you should load the library by creating an instance of :py:class:`CDLL` +by calling the constructor. + +For example, on Linux:: >>> cdll.LoadLibrary("libc.so.6") # doctest: +LINUX @@ -83,7 +92,14 @@ the constructor:: >>> -.. XXX Add section for macOS. +On macOS:: + + >>> cdll.LoadLibrary("libc.dylib") # doctest: +MACOS + + >>> libc = CDLL("libc.dylib") # doctest: +MACOS + >>> libc # doctest: +MACOS + + .. _ctypes-accessing-functions-from-loaded-dlls: @@ -180,7 +196,7 @@ handle (passing ``None`` as single argument to call it with a ``NULL`` pointer): To find out the correct calling convention you have to look into the C header file or the documentation for the function you want to call. -On Windows, :mod:`ctypes` uses win32 structured exception handling to prevent +On Windows, :mod:`!ctypes` uses win32 structured exception handling to prevent crashes from general protection faults when functions are called with invalid argument values:: @@ -190,10 +206,8 @@ argument values:: OSError: exception: access violation reading 0x00000020 >>> -There are, however, enough ways to crash Python with :mod:`ctypes`, so you -should be careful anyway. The :mod:`faulthandler` module can be helpful in -debugging crashes (e.g. from segmentation faults produced by erroneous C library -calls). +The :mod:`faulthandler` module can help debug crashes, +such as segmentation faults produced by erroneous C library calls. ``None``, integers, bytes objects and (unicode) strings are the only native Python objects that can directly be used as parameters in these function calls. @@ -203,7 +217,7 @@ as pointer to the memory block that contains their data (:c:expr:`char *` or :c:expr:`int` type, their value is masked to fit into the C type. Before we move on calling functions with other parameter types, we have to learn -more about :mod:`ctypes` data types. +more about :mod:`!ctypes` data types. .. _ctypes-fundamental-data-types: @@ -211,89 +225,170 @@ more about :mod:`ctypes` data types. Fundamental data types ^^^^^^^^^^^^^^^^^^^^^^ -:mod:`ctypes` defines a number of primitive C compatible data types: - -+----------------------+------------------------------------------+----------------------------+ -| ctypes type | C type | Python type | -+======================+==========================================+============================+ -| :class:`c_bool` | :c:expr:`_Bool` | bool (1) | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_char` | :c:expr:`char` | 1-character bytes object | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_wchar` | :c:type:`wchar_t` | 1-character string | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_byte` | :c:expr:`char` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_ubyte` | :c:expr:`unsigned char` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_short` | :c:expr:`short` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_ushort` | :c:expr:`unsigned short` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_int` | :c:expr:`int` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_int8` | :c:type:`int8_t` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_int16` | :c:type:`int16_t` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_int32` | :c:type:`int32_t` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_int64` | :c:type:`int64_t` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_uint` | :c:expr:`unsigned int` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_uint8` | :c:type:`uint8_t` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_uint16` | :c:type:`uint16_t` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_uint32` | :c:type:`uint32_t` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_uint64` | :c:type:`uint64_t` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_long` | :c:expr:`long` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_ulong` | :c:expr:`unsigned long` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_longlong` | :c:expr:`__int64` or :c:expr:`long long` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_ulonglong` | :c:expr:`unsigned __int64` or | int | -| | :c:expr:`unsigned long long` | | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_size_t` | :c:type:`size_t` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_ssize_t` | :c:type:`ssize_t` or | int | -| | :c:expr:`Py_ssize_t` | | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_time_t` | :c:type:`time_t` | int | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_float` | :c:expr:`float` | float | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_double` | :c:expr:`double` | float | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_longdouble`| :c:expr:`long double` | float | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_char_p` | :c:expr:`char *` (NUL terminated) | bytes object or ``None`` | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_wchar_p` | :c:expr:`wchar_t *` (NUL terminated) | string or ``None`` | -+----------------------+------------------------------------------+----------------------------+ -| :class:`c_void_p` | :c:expr:`void *` | int or ``None`` | -+----------------------+------------------------------------------+----------------------------+ - -(1) - The constructor accepts any object with a truth value. +:mod:`!ctypes` defines a number of primitive C compatible data types: + +.. list-table:: + :header-rows: 1 + + * - ctypes type + - C type + - Python type + - :py:attr:`~_SimpleCData._type_` + * - :class:`c_bool` + - :c:expr:`_Bool` + - :py:class:`bool` + - ``'?'`` + * - :class:`c_char` + - :c:expr:`char` + - 1-character :py:class:`bytes` + - ``'c'`` + * - :class:`c_wchar` + - :c:type:`wchar_t` + - 1-character :py:class:`str` + - ``'u'`` + * - :class:`c_byte` + - :c:expr:`char` + - :py:class:`int` + - ``'b'`` + * - :class:`c_ubyte` + - :c:expr:`unsigned char` + - :py:class:`int` + - ``'B'`` + * - :class:`c_short` + - :c:expr:`short` + - :py:class:`int` + - ``'h'`` + * - :class:`c_ushort` + - :c:expr:`unsigned short` + - :py:class:`int` + - ``'H'`` + * - :class:`c_int` + - :c:expr:`int` + - :py:class:`int` + - ``'i'`` \* + * - :class:`c_int8` + - :c:type:`int8_t` + - :py:class:`int` + - \* + * - :class:`c_int16` + - :c:type:`int16_t` + - :py:class:`int` + - \* + * - :class:`c_int32` + - :c:type:`int32_t` + - :py:class:`int` + - \* + * - :class:`c_int64` + - :c:type:`int64_t` + - :py:class:`int` + - \* + * - :class:`c_uint` + - :c:expr:`unsigned int` + - :py:class:`int` + - ``'I'`` \* + * - :class:`c_uint8` + - :c:type:`uint8_t` + - :py:class:`int` + - \* + * - :class:`c_uint16` + - :c:type:`uint16_t` + - :py:class:`int` + - \* + * - :class:`c_uint32` + - :c:type:`uint32_t` + - :py:class:`int` + - \* + * - :class:`c_uint64` + - :c:type:`uint64_t` + - :py:class:`int` + - \* + * - :class:`c_long` + - :c:expr:`long` + - :py:class:`int` + - ``'l'`` + * - :class:`c_ulong` + - :c:expr:`unsigned long` + - :py:class:`int` + - ``'L'`` + * - :class:`c_longlong` + - :c:expr:`long long` + - :py:class:`int` + - ``'q'`` \* + * - :class:`c_ulonglong` + - :c:expr:`unsigned long long` + - :py:class:`int` + - ``'Q'`` \* + * - :class:`c_size_t` + - :c:type:`size_t` + - :py:class:`int` + - \* + * - :class:`c_ssize_t` + - :c:type:`Py_ssize_t` + - :py:class:`int` + - \* + * - :class:`c_time_t` + - :c:type:`time_t` + - :py:class:`int` + - \* + * - :class:`c_float` + - :c:expr:`float` + - :py:class:`float` + - ``'f'`` + * - :class:`c_double` + - :c:expr:`double` + - :py:class:`float` + - ``'d'`` + * - :class:`c_longdouble` + - :c:expr:`long double` + - :py:class:`float` + - ``'g'`` \* + * - :class:`c_char_p` + - :c:expr:`char *` (NUL terminated) + - :py:class:`bytes` or ``None`` + - ``'z'`` + * - :class:`c_wchar_p` + - :c:expr:`wchar_t *` (NUL terminated) + - :py:class:`str` or ``None`` + - ``'Z'`` + * - :class:`c_void_p` + - :c:expr:`void *` + - :py:class:`int` or ``None`` + - ``'P'`` + * - :class:`py_object` + - :c:expr:`PyObject *` + - :py:class:`object` + - ``'O'`` + * - :ref:`VARIANT_BOOL ` + - :c:expr:`short int` + - :py:class:`bool` + - ``'v'`` Additionally, if IEC 60559 compatible complex arithmetic (Annex G) is supported in both C and ``libffi``, the following complex types are available: -+----------------------------------+---------------------------------+-----------------+ -| ctypes type | C type | Python type | -+==================================+=================================+=================+ -| :class:`c_float_complex` | :c:expr:`float complex` | complex | -+----------------------------------+---------------------------------+-----------------+ -| :class:`c_double_complex` | :c:expr:`double complex` | complex | -+----------------------------------+---------------------------------+-----------------+ -| :class:`c_longdouble_complex` | :c:expr:`long double complex` | complex | -+----------------------------------+---------------------------------+-----------------+ +.. list-table:: + :header-rows: 1 + + * - ctypes type + - C type + - Python type + - :py:attr:`~_SimpleCData._type_` + * - :class:`c_float_complex` + - :c:expr:`float complex` + - :py:class:`complex` + - ``'Zf'`` + * - :class:`c_double_complex` + - :c:expr:`double complex` + - :py:class:`complex` + - ``'Zd'`` + * - :class:`c_longdouble_complex` + - :c:expr:`long double complex` + - :py:class:`complex` + - ``'Zg'`` + +.. versionchanged:: 3.15 + The :py:attr:`~_SimpleCData._type_` types ``F``, ``D`` and ``G`` have been + replaced with ``Zf``, ``Zd`` and ``Zg``. All these types can be created by calling them with an optional initializer of @@ -307,6 +402,16 @@ the correct type and value:: c_ushort(65533) >>> +The constructors for numeric types will convert input using +:py:meth:`~object.__bool__`, +:py:meth:`~object.__index__` (for ``int``), +:py:meth:`~object.__float__` or :py:meth:`~object.__complex__`. +This means :py:class:`~ctypes.c_bool` accepts any object with a truth value:: + + >>> empty_list = [] + >>> c_bool(empty_list) + c_bool(False) + Since these types are mutable, their value can also be changed afterwards:: >>> i = c_int(42) @@ -395,7 +500,7 @@ from within *IDLE* or *PythonWin*:: >>> As has been mentioned before, all Python types except integers, strings, and -bytes objects have to be wrapped in their corresponding :mod:`ctypes` type, so +bytes objects have to be wrapped in their corresponding :mod:`!ctypes` type, so that they can be converted to the required C data type:: >>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14)) @@ -429,10 +534,10 @@ specify :attr:`~_CFuncPtr.argtypes` for all variadic functions. Calling functions with your own custom data types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -You can also customize :mod:`ctypes` argument conversion to allow instances of -your own classes be used as function arguments. :mod:`ctypes` looks for an +You can also customize :mod:`!ctypes` argument conversion to allow instances of +your own classes be used as function arguments. :mod:`!ctypes` looks for an :attr:`!_as_parameter_` attribute and uses this as the function argument. The -attribute must be an integer, string, bytes, a :mod:`ctypes` instance, or an +attribute must be an integer, string, bytes, a :mod:`!ctypes` instance, or an object with an :attr:`!_as_parameter_` attribute:: >>> class Bottles: @@ -446,7 +551,7 @@ object with an :attr:`!_as_parameter_` attribute:: >>> If you don't want to store the instance's data in the :attr:`!_as_parameter_` -instance variable, you could define a :class:`property` which makes the +instance variable, you could define a :deco:`property` which makes the attribute available on request. @@ -488,7 +593,7 @@ the Python object passed to the function call, it should do a typecheck or whatever is needed to make sure this object is acceptable, and then return the object itself, its :attr:`!_as_parameter_` attribute, or whatever you want to pass as the C function argument in this case. Again, the result should be an -integer, string, bytes, a :mod:`ctypes` instance, or an object with an +integer, string, bytes, a :mod:`!ctypes` instance, or an object with an :attr:`!_as_parameter_` attribute. @@ -598,7 +703,7 @@ Sometimes a C api function expects a *pointer* to a data type as parameter, probably to write into the corresponding location, or if the data is too large to be passed by value. This is also known as *passing parameters by reference*. -:mod:`ctypes` exports the :func:`byref` function which is used to pass parameters +:mod:`!ctypes` exports the :func:`byref` function which is used to pass parameters by reference. The same effect can be achieved with the :func:`pointer` function, although :func:`pointer` does a lot more work since it constructs a real pointer object, so it is faster to use :func:`byref` if you don't need the pointer @@ -623,12 +728,12 @@ Structures and unions ^^^^^^^^^^^^^^^^^^^^^ Structures and unions must derive from the :class:`Structure` and :class:`Union` -base classes which are defined in the :mod:`ctypes` module. Each subclass must +base classes which are defined in the :mod:`!ctypes` module. Each subclass must define a :attr:`~Structure._fields_` attribute. :attr:`!_fields_` must be a list of *2-tuples*, containing a *field name* and a *field type*. -The field type must be a :mod:`ctypes` type like :class:`c_int`, or any other -derived :mod:`ctypes` type: structure, union, array, pointer. +The field type must be a :mod:`!ctypes` type like :class:`c_int`, or any other +derived :mod:`!ctypes` type: structure, union, array, pointer. Here is a simple example of a POINT structure, which contains two integers named *x* and *y*, and also shows how to initialize a structure in the constructor:: @@ -687,7 +792,7 @@ See :class:`CField`:: .. warning:: - :mod:`ctypes` does not support passing unions or structures with bit-fields + :mod:`!ctypes` does not support passing unions or structures with bit-fields to functions by value. While this may work on 32-bit x86, it's not guaranteed by the library to work in the general case. Unions and structures with bit-fields should always be passed to functions by pointer. @@ -700,16 +805,12 @@ compiler does it. It is possible to override this behavior entirely by specifyi :attr:`~Structure._layout_` class attribute in the subclass definition; see the attribute documentation for details. -It is possible to specify the maximum alignment for the fields by setting -the :attr:`~Structure._pack_` class attribute to a positive integer. -This matches what ``#pragma pack(n)`` does in MSVC. +It is possible to specify the maximum alignment for the fields and/or for the +structure itself by setting the class attributes :attr:`~Structure._pack_` +and/or :attr:`~Structure._align_`, respectively. +See the attribute documentation for details. -It is also possible to set a minimum alignment for how the subclass itself is packed in the -same way ``#pragma align(n)`` works in MSVC. -This can be achieved by specifying a :attr:`~Structure._align_` class attribute -in the subclass definition. - -:mod:`ctypes` uses the native byte order for Structures and Unions. To build +:mod:`!ctypes` uses the native byte order for Structures and Unions. To build structures with non-native byte order, you can use one of the :class:`BigEndianStructure`, :class:`LittleEndianStructure`, :class:`BigEndianUnion`, and :class:`LittleEndianUnion` base classes. These @@ -798,7 +899,7 @@ Pointers ^^^^^^^^ Pointer instances are created by calling the :func:`pointer` function on a -:mod:`ctypes` type:: +:mod:`!ctypes` type:: >>> from ctypes import * >>> i = c_int(42) @@ -812,7 +913,7 @@ returns the object to which the pointer points, the ``i`` object above:: c_long(42) >>> -Note that :mod:`ctypes` does not have OOR (original object return), it constructs a +Note that :mod:`!ctypes` does not have OOR (original object return), it constructs a new, equivalent object each time you retrieve an attribute:: >>> pi.contents is i @@ -856,7 +957,7 @@ item. Behind the scenes, the :func:`pointer` function does more than simply create pointer instances, it has to create pointer *types* first. This is done with the -:func:`POINTER` function, which accepts any :mod:`ctypes` type, and returns a +:func:`POINTER` function, which accepts any :mod:`!ctypes` type, and returns a new type:: >>> PI = POINTER(c_int) @@ -878,7 +979,7 @@ Calling the pointer type without an argument creates a ``NULL`` pointer. False >>> -:mod:`ctypes` checks for ``NULL`` when dereferencing pointers (but dereferencing +:mod:`!ctypes` checks for ``NULL`` when dereferencing pointers (but dereferencing invalid non-\ ``NULL`` pointers would crash Python):: >>> null_ptr[0] @@ -898,7 +999,7 @@ invalid non-\ ``NULL`` pointers would crash Python):: Thread safety without the GIL ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -From Python 3.13 onward, the :term:`GIL` can be disabled on :term:`free threaded ` builds. +From Python 3.13 onward, the :term:`GIL` can be disabled on the :term:`free-threaded build`. In ctypes, reads and writes to a single object concurrently is safe, but not across multiple objects: .. code-block:: pycon @@ -963,7 +1064,7 @@ To set a POINTER type field to ``NULL``, you can assign ``None``:: .. XXX list other conversions... Sometimes you have instances of incompatible types. In C, you can cast one type -into another type. :mod:`ctypes` provides a :func:`cast` function which can be +into another type. :mod:`!ctypes` provides a :func:`cast` function which can be used in the same way. The ``Bar`` structure defined above accepts ``POINTER(c_int)`` pointers or :class:`c_int` arrays for its ``values`` field, but not instances of other types:: @@ -1027,7 +1128,7 @@ work:: >>> because the new ``class cell`` is not available in the class statement itself. -In :mod:`ctypes`, we can define the ``cell`` class and set the +In :mod:`!ctypes`, we can define the ``cell`` class and set the :attr:`~Structure._fields_` attribute later, after the class statement:: >>> from ctypes import * @@ -1061,7 +1162,7 @@ other, and finally follow the pointer chain a few times:: Callback functions ^^^^^^^^^^^^^^^^^^ -:mod:`ctypes` allows creating C callable function pointers from Python callables. +:mod:`!ctypes` allows creating C callable function pointers from Python callables. These are sometimes called *callback functions*. First, you must create a class for the callback function. The class knows the @@ -1160,7 +1261,7 @@ write:: .. note:: Make sure you keep references to :func:`CFUNCTYPE` objects as long as they - are used from C code. :mod:`ctypes` doesn't, and if you don't, they may be + are used from C code. :mod:`!ctypes` doesn't, and if you don't, they may be garbage collected, crashing your program when a callback is made. Also, note that if the callback function is called in a thread created @@ -1179,7 +1280,7 @@ Some shared libraries not only export functions, they also export variables. An example in the Python library itself is the :c:data:`Py_Version`, Python runtime version number encoded in a single constant integer. -:mod:`ctypes` can access values like this with the :meth:`~_CData.in_dll` class methods of +:mod:`!ctypes` can access values like this with the :meth:`~_CData.in_dll` class methods of the type. *pythonapi* is a predefined symbol giving access to the Python C api:: @@ -1198,7 +1299,7 @@ Quoting the docs for that value: tricks with this to provide a dynamically created collection of frozen modules. So manipulating this pointer could even prove useful. To restrict the example -size, we show only how this table can be read with :mod:`ctypes`:: +size, we show only how this table can be read with :mod:`!ctypes`:: >>> from ctypes import * >>> @@ -1244,7 +1345,7 @@ for testing. Try it out with ``import __hello__`` for example. Surprises ^^^^^^^^^ -There are some edges in :mod:`ctypes` where you might expect something other +There are some edges in :mod:`!ctypes` where you might expect something other than what actually happens. Consider the following example:: @@ -1312,7 +1413,7 @@ constructs a new Python object each time! Variable-sized data types ^^^^^^^^^^^^^^^^^^^^^^^^^ -:mod:`ctypes` provides some support for variable-sized arrays and structures. +:mod:`!ctypes` provides some support for variable-sized arrays and structures. The :func:`resize` function can be used to resize the memory buffer of an existing ctypes object. The function takes the object as first argument, and @@ -1346,7 +1447,7 @@ get errors accessing other elements:: IndexError: invalid index >>> -Another way to use variable-sized data types with :mod:`ctypes` is to use the +Another way to use variable-sized data types with :mod:`!ctypes` is to use the dynamic nature of Python, and (re-)define the data type after the required size is already known, on a case by case basis. @@ -1356,115 +1457,95 @@ is already known, on a case by case basis. ctypes reference ---------------- - -.. _ctypes-finding-shared-libraries: - -Finding shared libraries -^^^^^^^^^^^^^^^^^^^^^^^^ - -When programming in a compiled language, shared libraries are accessed when -compiling/linking a program, and when the program is run. - -The purpose of the :func:`~ctypes.util.find_library` function is to locate a library in a way -similar to what the compiler or runtime loader does (on platforms with several -versions of a shared library the most recent should be loaded), while the ctypes -library loaders act like when a program is run, and call the runtime loader -directly. - -The :mod:`!ctypes.util` module provides a function which can help to determine -the library to load. - - -.. data:: find_library(name) - :module: ctypes.util - :noindex: - - Try to find a library and return a pathname. *name* is the library name without - any prefix like *lib*, suffix like ``.so``, ``.dylib`` or version number (this - is the form used for the posix linker option :option:`!-l`). If no library can - be found, returns ``None``. - -The exact functionality is system dependent. - -On Linux, :func:`~ctypes.util.find_library` tries to run external programs -(``/sbin/ldconfig``, ``gcc``, ``objdump`` and ``ld``) to find the library file. -It returns the filename of the library file. - -.. versionchanged:: 3.6 - On Linux, the value of the environment variable ``LD_LIBRARY_PATH`` is used - when searching for libraries, if a library cannot be found by any other means. - -Here are some examples:: - - >>> from ctypes.util import find_library - >>> find_library("m") - 'libm.so.6' - >>> find_library("c") - 'libc.so.6' - >>> find_library("bz2") - 'libbz2.so.1.0' - >>> - -On macOS and Android, :func:`~ctypes.util.find_library` uses the system's -standard naming schemes and paths to locate the library, and returns a full -pathname if successful:: - - >>> from ctypes.util import find_library - >>> find_library("c") - '/usr/lib/libc.dylib' - >>> find_library("m") - '/usr/lib/libm.dylib' - >>> find_library("bz2") - '/usr/lib/libbz2.dylib' - >>> find_library("AGL") - '/System/Library/Frameworks/AGL.framework/AGL' - >>> - -On Windows, :func:`~ctypes.util.find_library` searches along the system search path, and -returns the full pathname, but since there is no predefined naming scheme a call -like ``find_library("c")`` will fail and return ``None``. - -If wrapping a shared library with :mod:`ctypes`, it *may* be better to determine -the shared library name at development time, and hardcode that into the wrapper -module instead of using :func:`~ctypes.util.find_library` to locate the library at runtime. - - -.. _ctypes-listing-loaded-shared-libraries: - -Listing loaded shared libraries -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When writing code that relies on code loaded from shared libraries, it can be -useful to know which shared libraries have already been loaded into the current -process. - -The :mod:`!ctypes.util` module provides the :func:`~ctypes.util.dllist` function, -which calls the different APIs provided by the various platforms to help determine -which shared libraries have already been loaded into the current process. - -The exact output of this function will be system dependent. On most platforms, -the first entry of this list represents the current process itself, which may -be an empty string. -For example, on glibc-based Linux, the return may look like:: - - >>> from ctypes.util import dllist - >>> dllist() - ['', 'linux-vdso.so.1', '/lib/x86_64-linux-gnu/libm.so.6', '/lib/x86_64-linux-gnu/libc.so.6', ... ] - .. _ctypes-loading-shared-libraries: Loading shared libraries ^^^^^^^^^^^^^^^^^^^^^^^^ There are several ways to load shared libraries into the Python process. One -way is to instantiate one of the following classes: +way is to instantiate :py:class:`CDLL` or one of its subclasses: .. class:: CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None) - Instances of this class represent loaded shared libraries. Functions in these - libraries use the standard C calling convention, and are assumed to return - :c:expr:`int`. + Represents a loaded shared library. + + Functions in this library use the standard C calling convention, and are + assumed to return :c:expr:`int`. + The Python :term:`global interpreter lock` is released before calling any + function exported by these libraries, and reacquired afterwards. + For different function behavior, use a subclass: :py:class:`~ctypes.OleDLL`, + :py:class:`~ctypes.WinDLL`, or :py:class:`~ctypes.PyDLL`. + + If you have an existing :py:attr:`handle ` to an already + loaded shared library, it can be passed as the *handle* argument to wrap + the opened library in a new :py:class:`!CDLL` object. + In this case, *name* is only used to set the :py:attr:`~ctypes.CDLL._name` + attribute, but it may be adjusted and/or validated. + + If *handle* is ``None``, the underlying platform's :manpage:`dlopen(3)` or + `LoadLibraryExW`_ function is used to load the library into + the process, and to get a handle to it. + + *name* is the pathname of the shared library to open. + If *name* does not contain a path separator, the library is found + in a platform-specific way. + + On Windows, the ``.DLL`` suffix may be missing. (For details, see + `LoadLibraryExW`_ documentation.) + Other platform-specific prefixes and suffixes (for example, ``lib``, + ``.so``, ``.dylib``, or version numbers) must be present in *name*; + they are not added automatically. + See :ref:`ctypes-finding-shared-libraries` for more information. + + On non-Windows systems, *name* can be ``None``. In this case, + :c:func:`!dlopen` is called with ``NULL``, which opens the main program + as a "library". + (Some systems do the same is *name* is empty; ``None``/``NULL`` is more + portable.) + + .. admonition:: CPython implementation detail + + Since CPython is linked to ``libc``, a ``None`` *name* is often used + to access the C standard library:: + + >>> printf = ctypes.CDLL(None).printf + >>> printf.argtypes = [ctypes.c_char_p] + >>> printf(b"hello\n") + hello + 6 + + To access the Python C API, prefer :py:data:`ctypes.pythonapi` which + works across platforms. + + The *mode* parameter can be used to specify how the library is loaded. For + details, consult the :manpage:`dlopen(3)` manpage. On Windows, *mode* is + ignored. On posix systems, RTLD_NOW is always added, and is not + configurable. + + The *use_errno* parameter, when set to true, enables a ctypes mechanism that + allows accessing the system :data:`errno` error number in a safe way. + :mod:`!ctypes` maintains a thread-local copy of the system's :data:`errno` + variable; if you call foreign functions created with ``use_errno=True`` then the + :data:`errno` value before the function call is swapped with the ctypes private + copy, the same happens immediately after the function call. + + The function :func:`ctypes.get_errno` returns the value of the ctypes private + copy, and the function :func:`ctypes.set_errno` changes the ctypes private copy + to a new value and returns the former value. + + The *use_last_error* parameter, when set to true, enables the same mechanism for + the Windows error code which is managed by the :func:`GetLastError` and + :func:`!SetLastError` Windows API functions; :func:`ctypes.get_last_error` and + :func:`ctypes.set_last_error` are used to request and change the ctypes private + copy of the windows error code. + + The *winmode* parameter is used on Windows to specify how the library is loaded + (since *mode* is ignored). It takes any value that is valid for the Win32 API + `LoadLibraryExW`_ flags parameter. When omitted, the default is to use the + flags that result in the most secure DLL load, which avoids issues such as DLL + hijacking. Passing the full path to the DLL is the safest way to ensure the + correct library and dependencies are loaded. On Windows creating a :class:`CDLL` instance may fail even if the DLL name exists. When a dependent DLL of the loaded DLL is not found, a @@ -1476,20 +1557,49 @@ way is to instantiate one of the following classes: DLLs and determine which one is not found using Windows debugging and tracing tools. + .. seealso:: + + `Microsoft DUMPBIN tool `_ + -- A tool to find DLL dependents. + + .. versionchanged:: 3.8 + Added *winmode* parameter. + .. versionchanged:: 3.12 The *name* parameter can now be a :term:`path-like object`. -.. seealso:: + Instances of this class have no public methods. Functions exported by the + shared library can be accessed as attributes or by index. Please note that + accessing the function through an attribute caches the result and therefore + accessing it repeatedly returns the same object each time. On the other hand, + accessing it through an index returns a new object each time:: + + >>> from ctypes import CDLL + >>> libc = CDLL("libc.so.6") # On Linux + >>> libc.time == libc.time + True + >>> libc['time'] == libc['time'] + False + + The following public attributes are available. Their name starts with an + underscore to not clash with exported function names: + + .. attribute:: _handle + + The system handle used to access the library. + + .. attribute:: _name + + The name of the library passed in the constructor. - `Microsoft DUMPBIN tool `_ - -- A tool to find DLL dependents. +.. _LoadLibraryExW: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw +.. class:: OleDLL -.. class:: OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None) + See :py:class:`~ctypes.CDLL`, the superclass, for common information. - Instances of this class represent loaded shared libraries, - functions in these libraries use the ``stdcall`` calling convention, and are + Functions in this library use the ``stdcall`` calling convention, and are assumed to return the windows specific :class:`HRESULT` code. :class:`HRESULT` values contain information specifying whether the function call failed or succeeded, together with additional error code. If the return value signals a @@ -1501,133 +1611,51 @@ way is to instantiate one of the following classes: :exc:`WindowsError` used to be raised, which is now an alias of :exc:`OSError`. - .. versionchanged:: 3.12 - - The *name* parameter can now be a :term:`path-like object`. +.. class:: WinDLL -.. class:: WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None) + See :py:class:`~ctypes.CDLL`, the superclass, for common information. - Instances of this class represent loaded shared libraries, - functions in these libraries use the ``stdcall`` calling convention, and are + Functions in these libraries use the ``stdcall`` calling convention, and are assumed to return :c:expr:`int` by default. .. availability:: Windows - .. versionchanged:: 3.12 - - The *name* parameter can now be a :term:`path-like object`. - - -The Python :term:`global interpreter lock` is released before calling any -function exported by these libraries, and reacquired afterwards. +.. class:: PyDLL + See :py:class:`~ctypes.CDLL`, the superclass, for common information. -.. class:: PyDLL(name, mode=DEFAULT_MODE, handle=None) - - Instances of this class behave like :class:`CDLL` instances, except that the + When functions in this library are called, the Python GIL is *not* released during the function call, and after the function execution the Python error flag is checked. If the error flag is set, a Python exception is raised. - Thus, this is only useful to call Python C api functions directly. - - .. versionchanged:: 3.12 - - The *name* parameter can now be a :term:`path-like object`. - -All these classes can be instantiated by calling them with at least one -argument, the pathname of the shared library. If you have an existing handle to -an already loaded shared library, it can be passed as the ``handle`` named -parameter, otherwise the underlying platform's :c:func:`!dlopen` or -:c:func:`!LoadLibrary` function is used to load the library into -the process, and to get a handle to it. - -The *mode* parameter can be used to specify how the library is loaded. For -details, consult the :manpage:`dlopen(3)` manpage. On Windows, *mode* is -ignored. On posix systems, RTLD_NOW is always added, and is not -configurable. - -The *use_errno* parameter, when set to true, enables a ctypes mechanism that -allows accessing the system :data:`errno` error number in a safe way. -:mod:`ctypes` maintains a thread-local copy of the system's :data:`errno` -variable; if you call foreign functions created with ``use_errno=True`` then the -:data:`errno` value before the function call is swapped with the ctypes private -copy, the same happens immediately after the function call. - -The function :func:`ctypes.get_errno` returns the value of the ctypes private -copy, and the function :func:`ctypes.set_errno` changes the ctypes private copy -to a new value and returns the former value. - -The *use_last_error* parameter, when set to true, enables the same mechanism for -the Windows error code which is managed by the :func:`GetLastError` and -:func:`!SetLastError` Windows API functions; :func:`ctypes.get_last_error` and -:func:`ctypes.set_last_error` are used to request and change the ctypes private -copy of the windows error code. - -The *winmode* parameter is used on Windows to specify how the library is loaded -(since *mode* is ignored). It takes any value that is valid for the Win32 API -``LoadLibraryEx`` flags parameter. When omitted, the default is to use the -flags that result in the most secure DLL load, which avoids issues such as DLL -hijacking. Passing the full path to the DLL is the safest way to ensure the -correct library and dependencies are loaded. - -.. versionchanged:: 3.8 - Added *winmode* parameter. + Thus, this is only useful to call Python C API functions directly. .. data:: RTLD_GLOBAL - :noindex: Flag to use as *mode* parameter. On platforms where this flag is not available, it is defined as the integer zero. .. data:: RTLD_LOCAL - :noindex: Flag to use as *mode* parameter. On platforms where this is not available, it is the same as *RTLD_GLOBAL*. .. data:: DEFAULT_MODE - :noindex: The default mode which is used to load shared libraries. On OSX 10.3, this is *RTLD_GLOBAL*, otherwise it is the same as *RTLD_LOCAL*. -Instances of these classes have no public methods. Functions exported by the -shared library can be accessed as attributes or by index. Please note that -accessing the function through an attribute caches the result and therefore -accessing it repeatedly returns the same object each time. On the other hand, -accessing it through an index returns a new object each time:: - - >>> from ctypes import CDLL - >>> libc = CDLL("libc.so.6") # On Linux - >>> libc.time == libc.time - True - >>> libc['time'] == libc['time'] - False - -The following public attributes are available, their name starts with an -underscore to not clash with exported function names: - - -.. attribute:: PyDLL._handle - - The system handle used to access the library. - - -.. attribute:: PyDLL._name - - The name of the library passed in the constructor. Shared libraries can also be loaded by using one of the prefabricated objects, which are instances of the :class:`LibraryLoader` class, either by calling the :meth:`~LibraryLoader.LoadLibrary` method, or by retrieving the library as attribute of the loader instance. - .. class:: LibraryLoader(dlltype) Class which loads shared libraries. *dlltype* should be one of the @@ -1645,44 +1673,36 @@ attribute of the loader instance. These prefabricated library loaders are available: -.. data:: cdll - :noindex: - - Creates :class:`CDLL` instances. + .. data:: cdll + Creates :class:`CDLL` instances. -.. data:: windll - :noindex: - Creates :class:`WinDLL` instances. + .. data:: windll - .. availability:: Windows + Creates :class:`WinDLL` instances. + .. availability:: Windows -.. data:: oledll - :noindex: - Creates :class:`OleDLL` instances. + .. data:: oledll - .. availability:: Windows + Creates :class:`OleDLL` instances. + .. availability:: Windows -.. data:: pydll - :noindex: - Creates :class:`PyDLL` instances. + .. data:: pydll + Creates :class:`PyDLL` instances. -For accessing the C Python api directly, a ready-to-use Python shared library -object is available: -.. data:: pythonapi - :noindex: + .. data:: pythonapi - An instance of :class:`PyDLL` that exposes Python C API functions as - attributes. Note that all these functions are assumed to return C - :c:expr:`int`, which is of course not always the truth, so you have to assign - the correct :attr:`!restype` attribute to use these functions. + An instance of :class:`PyDLL` that exposes Python C API functions as + attributes. Note that all these functions are assumed to return C + :c:expr:`int`, which is of course not always the truth, so you have to assign + the correct :attr:`!restype` attribute to use these functions. .. audit-event:: ctypes.dlopen name ctypes.LibraryLoader @@ -1702,6 +1722,134 @@ object is available: accessing a function raises an auditing event ``ctypes.dlsym/handle`` with arguments ``handle`` (the raw library handle) and ``name``. + +.. _ctypes-finding-shared-libraries: + +Finding shared libraries +^^^^^^^^^^^^^^^^^^^^^^^^ + +When programming in a compiled language, shared libraries are accessed when +compiling/linking a program, and when the program is run. +The programmer specifies a short name; the C compiler, linker, and +runtime dynamic library loader then interact in system-specific ways to find +the filename of the library to load. + +While the mapping from short names to filenames is not consistently exposed +by platforms, the :mod:`!ctypes.util` module provides a function, +:func:`!find_library`, that attempts to match it. +However, as backwards compatibility concerns make it difficult to adjust +its behavior for new platforms and configurations, the function +is :term:`soft deprecated`. + +If wrapping a shared library with :mod:`!ctypes`, consider determining the +shared library name at development time, and hardcoding it into the wrapper +module instead of using :func:`!find_library` to locate the library +at runtime. +Also consider adding a configuration option or environment variable to let +users select a library to use, and then perhaps use :func:`!find_library` +as a default or fallback. + +.. function:: find_library(name) + :module: ctypes.util + + Try to find a library and return a pathname. + + *name* is the "short" library name without any prefix like ``lib``, + suffix like ``.so``, ``.dylib`` or version number. + (This is the form used for the posix linker option :option:`!-l`.) + The result is in a format suitable for passing to :py:class:`~ctypes.CDLL`. + + If no library can be found, return ``None``. + + The exact functionality is system dependent, and is *not guaranteed* + to match the behavior of the compiler, linker, and loader used for + (or by) Python. + It is recommended to only use this function as a default or fallback, + + .. soft-deprecated:: 3.15 + + This function is kept for use in cases where it works, but not expected to + be updated for additional platforms and configurations. + +On Linux, :func:`!find_library` tries to run external +programs (``/sbin/ldconfig``, ``gcc``, ``objdump`` and ``ld``) to find the +library file. +If the output of these programs does not correspond to the dynamic +linker used by Python, the result of this function may be misleading. + +.. versionchanged:: 3.6 + On Linux, the value of the environment variable ``LD_LIBRARY_PATH`` is used + when searching for libraries, if a library cannot be found by any other means. + +Here are some examples:: + + >>> from ctypes.util import find_library + >>> find_library("m") + 'libm.so.6' + >>> find_library("c") + 'libc.so.6' + >>> find_library("bz2") + 'libbz2.so.1.0' + >>> + +On macOS and Android, :func:`!find_library` uses the system's +standard naming schemes and paths to locate the library, and returns a full +pathname if successful:: + + >>> from ctypes.util import find_library + >>> find_library("c") + '/usr/lib/libc.dylib' + >>> find_library("m") + '/usr/lib/libm.dylib' + >>> find_library("bz2") + '/usr/lib/libbz2.dylib' + >>> find_library("AGL") + '/System/Library/Frameworks/AGL.framework/AGL' + >>> + +On Windows, :func:`!find_library` searches along the system search path, and +returns the full pathname, but since there is no predefined naming scheme a call +like ``find_library("c")`` will fail and return ``None``. + +.. function:: find_msvcrt() + :module: ctypes.util + + Returns the filename of the VC runtime library used by Python, + and by the extension modules. + + If the name of the library cannot be determined, ``None`` is returned. + Notably, this will happen for recent versions of the VC runtime library, + which are not directly loadable. + + If you need to free memory, for example, allocated by an extension module + with a call to the ``free(void *)``, it is important that you use the + function in the same library that allocated the memory. + + .. availability:: Windows + + +.. _ctypes-listing-loaded-shared-libraries: + +Listing loaded shared libraries +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When writing code that relies on code loaded from shared libraries, it can be +useful to know which shared libraries have already been loaded into the current +process. + +The :mod:`!ctypes.util` module provides the :func:`~ctypes.util.dllist` function, +which calls the different APIs provided by the various platforms to help determine +which shared libraries have already been loaded into the current process. + +The exact output of this function will be system dependent. On most platforms, +the first entry of this list represents the current process itself, which may +be an empty string. +For example, on glibc-based Linux, the return may look like:: + + >>> from ctypes.util import dllist + >>> dllist() + ['', 'linux-vdso.so.1', '/lib/x86_64-linux-gnu/libm.so.6', '/lib/x86_64-linux-gnu/libc.so.6', ... ] + .. _ctypes-foreign-functions: Foreign functions @@ -1928,7 +2076,7 @@ the windows header file is this:: LPCWSTR lpCaption, UINT uType); -Here is the wrapping with :mod:`ctypes`:: +Here is the wrapping with :mod:`!ctypes`:: >>> from ctypes import c_int, WINFUNCTYPE, windll >>> from ctypes.wintypes import HWND, LPCWSTR, UINT @@ -1951,7 +2099,7 @@ function retrieves the dimensions of a specified window by copying them into HWND hWnd, LPRECT lpRect); -Here is the wrapping with :mod:`ctypes`:: +Here is the wrapping with :mod:`!ctypes`:: >>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError >>> from ctypes.wintypes import BOOL, HWND, RECT @@ -1979,7 +2127,7 @@ do the error checking, and raises an exception when the api call failed:: >>> If the :attr:`~_CFuncPtr.errcheck` function returns the argument tuple it receives -unchanged, :mod:`ctypes` continues the normal processing it does on the output +unchanged, :mod:`!ctypes` continues the normal processing it does on the output parameters. If you want to return a tuple of window coordinates instead of a ``RECT`` instance, you can retrieve the fields in the function and return them instead, the normal processing will no longer take place:: @@ -2124,31 +2272,6 @@ Utility functions .. availability:: Windows -.. function:: find_library(name) - :module: ctypes.util - - Try to find a library and return a pathname. *name* is the library name - without any prefix like ``lib``, suffix like ``.so``, ``.dylib`` or version - number (this is the form used for the posix linker option :option:`!-l`). If - no library can be found, returns ``None``. - - The exact functionality is system dependent. - - -.. function:: find_msvcrt() - :module: ctypes.util - - Returns the filename of the VC runtime library used by Python, - and by the extension modules. If the name of the library cannot be - determined, ``None`` is returned. - - If you need to free memory, for example, allocated by an extension module - with a call to the ``free(void *)``, it is important that you use the - function in the same library that allocated the memory. - - .. availability:: Windows - - .. function:: dllist() :module: ctypes.util @@ -2447,10 +2570,32 @@ Fundamental data types Python bytes object or string. When the ``value`` attribute is retrieved from a ctypes instance, usually - a new object is returned each time. :mod:`ctypes` does *not* implement + a new object is returned each time. :mod:`!ctypes` does *not* implement original object return, always a new object is constructed. The same is true for all other ctypes object instances. + Each subclass has a class attribute: + + .. attribute:: _type_ + + Class attribute that contains an internal type code, as a string. + See :ref:`ctypes-fundamental-data-types` for a summary. + + Types marked \* in the summary may be (or always are) aliases of a + different :class:`_SimpleCData` subclass, and will not necessarily + use the listed type code. + For example, if the platform's :c:expr:`long`, :c:expr:`long long` + and :c:expr:`time_t` C types are the same, then :class:`c_long`, + :class:`c_longlong` and :class:`c_time_t` all refer to a single class, + :class:`c_long`, whose :attr:`_type_` code is ``'l'``. + The ``'L'`` code will be unused. + + .. seealso:: + + The :mod:`array` and :ref:`struct ` modules, + as well as third-party modules like `numpy `__, + use similar -- but slightly different -- type codes. + Fundamental data types, when returned as foreign function call results, or, for example, by retrieving structure field members or array items, are transparently @@ -2572,6 +2717,8 @@ These are the fundamental ctypes data types: Represents the C :c:expr:`signed long long` datatype. The constructor accepts an optional integer initializer; no overflow checking is done. + On platforms where ``sizeof(long long) == sizeof(long)`` it is an alias + to :class:`c_long`. .. class:: c_short @@ -2583,11 +2730,15 @@ These are the fundamental ctypes data types: .. class:: c_size_t Represents the C :c:type:`size_t` datatype. + Usually an alias for another unsigned integer type. .. class:: c_ssize_t - Represents the C :c:type:`ssize_t` datatype. + Represents the :c:type:`Py_ssize_t` datatype. + This is a signed version of :c:type:`size_t`; + that is, the POSIX :c:type:`ssize_t` type. + Usually an alias for another integer type. .. versionadded:: 3.2 @@ -2595,6 +2746,7 @@ These are the fundamental ctypes data types: .. class:: c_time_t Represents the C :c:type:`time_t` datatype. + Usually an alias for another integer type. .. versionadded:: 3.12 @@ -2647,6 +2799,8 @@ These are the fundamental ctypes data types: Represents the C :c:expr:`unsigned long long` datatype. The constructor accepts an optional integer initializer; no overflow checking is done. + On platforms where ``sizeof(long long) == sizeof(long)`` it is an alias + to :class:`c_long`. .. class:: c_ushort @@ -2698,8 +2852,11 @@ These are the fundamental ctypes data types: .. versionchanged:: 3.14 :class:`!py_object` is now a :term:`generic type`. +.. _ctypes-wintypes: + The :mod:`!ctypes.wintypes` module provides quite some other Windows specific -data types, for example :c:type:`!HWND`, :c:type:`!WPARAM`, or :c:type:`!DWORD`. +data types, for example :c:type:`!HWND`, :c:type:`!WPARAM`, +:c:type:`!VARIANT_BOOL` or :c:type:`!DWORD`. Some useful structures like :c:type:`!MSG` or :c:type:`!RECT` are also defined. @@ -2746,7 +2903,7 @@ fields, or any other data types containing pointer type fields. Abstract base class for structures in *native* byte order. Concrete structure and union types must be created by subclassing one of these - types, and at least define a :attr:`_fields_` class variable. :mod:`ctypes` will + types, and at least define a :attr:`_fields_` class variable. :mod:`!ctypes` will create :term:`descriptor`\s which allow reading and writing the fields by direct attribute accesses. These are the @@ -2792,11 +2949,18 @@ fields, or any other data types containing pointer type fields. .. attribute:: _pack_ An optional small integer that allows overriding the alignment of - structure fields in the instance. :attr:`_pack_` must already be defined - when :attr:`_fields_` is assigned, otherwise it will have no effect. - Setting this attribute to 0 is the same as not setting it at all. + structure fields in the instance. + + This is only implemented for the MSVC-compatible memory layout + (see :attr:`_layout_`). + + Setting :attr:`!_pack_` to 0 is the same as not setting it at all. + Otherwise, the value must be a positive power of two. + The effect is equivalent to ``#pragma pack(N)`` in C, except + :mod:`!ctypes` may allow larger *n* than what the compiler accepts. - This is only implemented for the MSVC-compatible memory layout. + :attr:`!_pack_` must already be defined + when :attr:`_fields_` is assigned, otherwise it will have no effect. .. deprecated-removed:: 3.14 3.19 @@ -2809,9 +2973,22 @@ fields, or any other data types containing pointer type fields. .. attribute:: _align_ - An optional small integer that allows overriding the alignment of + An optional small integer that allows increasing the alignment of the structure when being packed or unpacked to/from memory. - Setting this attribute to 0 is the same as not setting it at all. + + The value must not be negative. + The effect is equivalent to ``__attribute__((aligned(N)))`` on GCC + or ``#pragma align(N)`` on MSVC, except :mod:`!ctypes` may allow + values that the compiler would reject. + + :attr:`!_align_` can only *increase* a structure's alignment + requirements. Setting it to 0 or 1 has no effect. + + Using values that are not powers of two is discouraged and may lead to + surprising behavior. + + :attr:`!_align_` must already be defined + when :attr:`_fields_` is assigned, otherwise it will have no effect. .. versionadded:: 3.13 @@ -2850,7 +3027,7 @@ fields, or any other data types containing pointer type fields. assigned, otherwise it will have no effect. The fields listed in this variable must be structure or union type fields. - :mod:`ctypes` will create descriptors in the structure type that allows + :mod:`!ctypes` will create descriptors in the structure type that allows accessing the nested fields directly, without the need to create the structure or union field. @@ -2994,12 +3171,14 @@ Arrays and pointers Abstract base class for arrays. The recommended way to create concrete array types is by multiplying any - :mod:`ctypes` data type with a non-negative integer. Alternatively, you can subclass + :mod:`!ctypes` data type with a non-negative integer. Alternatively, you can subclass this type and define :attr:`_length_` and :attr:`_type_` class variables. Array elements can be read and written using standard subscript and slice accesses; for slice reads, the resulting object is *not* itself an :class:`Array`. + Arrays are :ref:`generic ` over the type of their elements. + .. attribute:: _length_ @@ -3020,10 +3199,10 @@ Arrays and pointers Create an array. Equivalent to ``type * length``, where *type* is a - :mod:`ctypes` data type and *length* an integer. + :mod:`!ctypes` data type and *length* an integer. - This function is :term:`soft deprecated` in favor of multiplication. - There are no plans to remove it. + .. soft-deprecated:: 3.14 + In favor of multiplication. .. class:: _Pointer diff --git a/Doc/library/curses.ascii.rst b/Doc/library/curses.ascii.rst index cb895664ff1b11..8f8e3ddda8ef52 100644 --- a/Doc/library/curses.ascii.rst +++ b/Doc/library/curses.ascii.rst @@ -4,14 +4,11 @@ .. module:: curses.ascii :synopsis: Constants and set-membership functions for ASCII characters. -.. moduleauthor:: Eric S. Raymond -.. sectionauthor:: Eric S. Raymond - **Source code:** :source:`Lib/curses/ascii.py` -------------- -The :mod:`curses.ascii` module supplies name constants for ASCII characters and +The :mod:`!curses.ascii` module supplies name constants for ASCII characters and functions to test membership in various ASCII character classes. The constants supplied are names for control characters as follows: @@ -133,7 +130,7 @@ C library: .. function:: isgraph(c) - Checks for ASCII any printable character except space. + Checks for any ASCII printable character except space. .. function:: islower(c) @@ -148,7 +145,7 @@ C library: .. function:: ispunct(c) - Checks for any printable ASCII character which is not a space or an alphanumeric + Checks for any ASCII printable character which is not a space or an alphanumeric character. diff --git a/Doc/library/curses.panel.rst b/Doc/library/curses.panel.rst index 11fd841d381f69..5bc6b74b7f07ca 100644 --- a/Doc/library/curses.panel.rst +++ b/Doc/library/curses.panel.rst @@ -4,8 +4,6 @@ .. module:: curses.panel :synopsis: A panel stack extension that adds depth to curses windows. -.. sectionauthor:: A.M. Kuchling - -------------- Panels are windows with the added feature of depth, so they can be stacked on @@ -18,7 +16,7 @@ displayed. Panels can be added, moved up or down in the stack, and removed. Functions --------- -The module :mod:`curses.panel` defines the following functions: +The module :mod:`!curses.panel` defines the following functions: .. function:: bottom_panel() diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 0b13c559295f3c..0f1449873fcf73 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -4,16 +4,12 @@ .. module:: curses :synopsis: An interface to the curses library, providing portable terminal handling. - :platform: Unix - -.. sectionauthor:: Moshe Zadka -.. sectionauthor:: Eric Raymond **Source code:** :source:`Lib/curses` -------------- -The :mod:`curses` module provides an interface to the curses library, the +The :mod:`!curses` module provides an interface to the curses library, the de-facto standard for portable advanced terminal handling. While curses is most widely used in the Unix environment, versions are available @@ -23,6 +19,10 @@ Linux and the BSD variants of Unix. .. include:: ../includes/wasm-mobile-notavail.rst +.. include:: ../includes/optional-module.rst + +.. availability:: Unix. + .. note:: Whenever the documentation mentions a *character* it can be specified @@ -52,7 +52,7 @@ Linux and the BSD variants of Unix. Functions --------- -The module :mod:`curses` defines the following exception: +The module :mod:`!curses` defines the following exception: .. exception:: error @@ -65,7 +65,7 @@ The module :mod:`curses` defines the following exception: default to the current cursor location. Whenever *attr* is optional, it defaults to :const:`A_NORMAL`. -The module :mod:`curses` defines the following functions: +The module :mod:`!curses` defines the following functions: .. function:: assume_default_colors(fg, bg, /) @@ -579,7 +579,7 @@ The module :mod:`curses` defines the following functions: after :func:`initscr`. :func:`start_color` initializes eight basic colors (black, red, green, yellow, - blue, magenta, cyan, and white), and two global variables in the :mod:`curses` + blue, magenta, cyan, and white), and two global variables in the :mod:`!curses` module, :const:`COLORS` and :const:`COLOR_PAIRS`, containing the maximum number of colors and color-pairs the terminal can support. It also restores the colors on the terminal to the values they had when the terminal was just turned on. @@ -716,8 +716,10 @@ The module :mod:`curses` defines the following functions: Window Objects -------------- -Window objects, as returned by :func:`initscr` and :func:`newwin` above, have -the following methods and attributes: +.. class:: window + + Window objects, as returned by :func:`initscr` and :func:`newwin` above, have + the following methods and attributes: .. method:: window.addch(ch[, attr]) @@ -770,7 +772,7 @@ the following methods and attributes: .. method:: window.attron(attr) - Add attribute *attr* from the "background" set applied to all writes to the + Add attribute *attr* to the "background" set applied to all writes to the current window. @@ -1017,7 +1019,7 @@ the following methods and attributes: .. method:: window.idlok(flag) - If *flag* is ``True``, :mod:`curses` will try and use hardware line + If *flag* is ``True``, :mod:`!curses` will try and use hardware line editing facilities. Otherwise, line insertion/deletion are disabled. @@ -1105,7 +1107,7 @@ the following methods and attributes: .. method:: window.keypad(flag) If *flag* is ``True``, escape sequences generated by some keys (keypad, function keys) - will be interpreted by :mod:`curses`. If *flag* is ``False``, escape sequences will be + will be interpreted by :mod:`!curses`. If *flag* is ``False``, escape sequences will be left as is in the input stream. @@ -1331,7 +1333,7 @@ the following methods and attributes: Constants --------- -The :mod:`curses` module defines the following data members: +The :mod:`!curses` module defines the following data members: .. data:: ERR @@ -1347,7 +1349,6 @@ The :mod:`curses` module defines the following data members: .. data:: version -.. data:: __version__ A bytes object representing the current version of the module. @@ -1821,22 +1822,19 @@ The following table lists the predefined colors: +-------------------------+----------------------------+ -:mod:`curses.textpad` --- Text input widget for curses programs -=============================================================== +:mod:`!curses.textpad` --- Text input widget for curses programs +================================================================ .. module:: curses.textpad :synopsis: Emacs-like input editing in a curses window. -.. moduleauthor:: Eric Raymond -.. sectionauthor:: Eric Raymond - -The :mod:`curses.textpad` module provides a :class:`Textbox` class that handles +The :mod:`!curses.textpad` module provides a :class:`Textbox` class that handles elementary text editing in a curses window, supporting a set of keybindings resembling those of Emacs (thus, also of Netscape Navigator, BBedit 6.x, FrameMaker, and many other programs). The module also provides a rectangle-drawing function useful for framing text boxes or for other purposes. -The module :mod:`curses.textpad` defines the following function: +The module :mod:`!curses.textpad` defines the following function: .. function:: rectangle(win, uly, ulx, lry, lrx) diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst index 299c8aa399c25c..ce59d89843e761 100644 --- a/Doc/library/dataclasses.rst +++ b/Doc/library/dataclasses.rst @@ -4,9 +4,6 @@ .. module:: dataclasses :synopsis: Generate special methods on user-defined classes. -.. moduleauthor:: Eric V. Smith -.. sectionauthor:: Eric V. Smith - **Source code:** :source:`Lib/dataclasses.py` -------------- @@ -103,12 +100,25 @@ Module contents ignored. - *eq*: If true (the default), an :meth:`~object.__eq__` method will be - generated. This method compares the class as if it were a tuple - of its fields, in order. Both instances in the comparison must - be of the identical type. + generated. - If the class already defines :meth:`!__eq__`, this parameter is - ignored. + This method compares the class by comparing each field in order. Both + instances in the comparison must be of the identical type. + + If the class already defines :meth:`!__eq__`, this parameter is ignored. + + .. versionchanged:: 3.13 + The generated ``__eq__`` method now compares each field individually + (for example, ``self.a == other.a and self.b == other.b``), rather than + comparing tuples of fields as in previous versions. + + This change makes the comparison faster but it may alter results in cases + where attributes compare equal by identity but not by value (such as + ``float('nan')``). + + In Python 3.12 and earlier, the comparison was performed by creating + tuples of the fields and comparing them (for example, + ``(self.a, self.b) == (other.a, other.b)``). - *order*: If true (the default is ``False``), :meth:`~object.__lt__`, :meth:`~object.__le__`, :meth:`~object.__gt__`, and :meth:`~object.__ge__` methods will be @@ -161,9 +171,11 @@ Module contents :class:`object`, this means it will fall back to id-based hashing). - *frozen*: If true (the default is ``False``), assigning to fields will - generate an exception. This emulates read-only frozen instances. If - :meth:`~object.__setattr__` or :meth:`~object.__delattr__` is defined in the class, then - :exc:`TypeError` is raised. See the discussion below. + generate an exception. This emulates read-only frozen instances. + See the :ref:`discussion ` below. + + If :meth:`~object.__setattr__` or :meth:`~object.__delattr__` is defined in the class + and *frozen* is true, then :exc:`TypeError` is raised. - *match_args*: If true (the default is ``True``), the :attr:`~object.__match_args__` tuple will be created from the list of @@ -315,7 +327,7 @@ Module contents :func:`!field`, then the class attribute for this field will be replaced by the specified *default* value. If *default* is not provided, then the class attribute will be deleted. The intent is - that after the :func:`@dataclass ` decorator runs, the class + that after the :deco:`dataclass` decorator runs, the class attributes will all contain the default values for the fields, just as if the default value itself were specified. For example, after:: @@ -331,6 +343,10 @@ Module contents :attr:`!C.t` will be ``20``, and the class attributes :attr:`!C.x` and :attr:`!C.y` will not be set. + .. versionchanged:: 3.15 + If *metadata* is ``None``, use an empty :class:`frozendict`, instead + of a :func:`~types.MappingProxyType` of an empty :class:`dict`. + .. class:: Field :class:`!Field` objects describe each defined field. These objects @@ -368,8 +384,8 @@ Module contents Converts the dataclass *obj* to a dict (by using the factory function *dict_factory*). Each dataclass is converted to a dict of its fields, as ``name: value`` pairs. dataclasses, dicts, - lists, and tuples are recursed into. Other objects are copied with - :func:`copy.deepcopy`. + frozendicts, lists, and tuples are recursed into. Other objects are copied + with :func:`copy.deepcopy`. Example of using :func:`!asdict` on nested dataclasses:: @@ -399,8 +415,8 @@ Module contents Converts the dataclass *obj* to a tuple (by using the factory function *tuple_factory*). Each dataclass is converted - to a tuple of its field values. dataclasses, dicts, lists, and - tuples are recursed into. Other objects are copied with + to a tuple of its field values. dataclasses, dicts, frozendicts, lists, + and tuples are recursed into. Other objects are copied with :func:`copy.deepcopy`. Continuing from the previous example:: @@ -415,7 +431,7 @@ Module contents :func:`!astuple` raises :exc:`TypeError` if *obj* is not a dataclass instance. -.. function:: make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False, module=None, decorator=dataclass) +.. function:: make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False, module=None, qualname=None, decorator=dataclass) Creates a new dataclass with name *cls_name*, fields as defined in *fields*, base classes as given in *bases*, and initialized @@ -425,20 +441,23 @@ Module contents :data:`typing.Any` is used for ``type``. The values of *init*, *repr*, *eq*, *order*, *unsafe_hash*, *frozen*, *match_args*, *kw_only*, *slots*, and *weakref_slot* have - the same meaning as they do in :func:`@dataclass `. + the same meaning as they do in :deco:`dataclass`. If *module* is defined, the :attr:`!__module__` attribute of the dataclass is set to that value. By default, it is set to the module name of the caller. + If *qualname* is defined, the :attr:`~type.__qualname__` attribute of the dataclass + is set to that value. By default, it is set to the value passed to *cls_name*. + The *decorator* parameter is a callable that will be used to create the dataclass. It should take the class object as a first argument and the same keyword arguments - as :func:`@dataclass `. By default, the :func:`@dataclass ` + as :deco:`dataclass`. By default, the :deco:`dataclass` function is used. This function is not strictly required, because any Python - mechanism for creating a new class with :attr:`!__annotations__` can - then apply the :func:`@dataclass ` function to convert that class to + mechanism for creating a new class with :attr:`~object.__annotations__` can + then apply the :deco:`dataclass` function to convert that class to a dataclass. This function is provided as a convenience. For example:: @@ -461,6 +480,8 @@ Module contents .. versionadded:: 3.14 Added the *decorator* parameter. + .. versionadded:: next + Added the *qualname* parameter. .. function:: replace(obj, /, **changes) @@ -495,7 +516,8 @@ Module contents .. function:: is_dataclass(obj) Return ``True`` if its parameter is a dataclass (including subclasses of a - dataclass) or an instance of one, otherwise return ``False``. + dataclass, but not including :ref:`generic aliases `) + or an instance of one, otherwise return ``False``. If you need to know if a class is an instance of a dataclass (and not a dataclass itself), then add a further check for ``not @@ -567,7 +589,7 @@ Post-init processing def __post_init__(self): self.c = self.a + self.b -The :meth:`~object.__init__` method generated by :func:`@dataclass ` does not call base +The :meth:`~object.__init__` method generated by :deco:`dataclass` does not call base class :meth:`!__init__` methods. If the base class has an :meth:`!__init__` method that has to be called, it is common to call this method in a :meth:`__post_init__` method:: @@ -597,7 +619,7 @@ parameters to :meth:`!__post_init__`. Also see the warning about how Class variables --------------- -One of the few places where :func:`@dataclass ` actually inspects the type +One of the few places where :deco:`dataclass` actually inspects the type of a field is to determine if a field is a class variable as defined in :pep:`526`. It does this by checking if the type of the field is :data:`typing.ClassVar`. If a field is a ``ClassVar``, it is excluded @@ -610,7 +632,7 @@ module-level :func:`fields` function. Init-only variables ------------------- -Another place where :func:`@dataclass ` inspects a type annotation is to +Another place where :deco:`dataclass` inspects a type annotation is to determine if a field is an init-only variable. It does this by seeing if the type of a field is of type :class:`InitVar`. If a field is an :class:`InitVar`, it is considered a pseudo-field called an init-only @@ -644,7 +666,7 @@ Frozen instances ---------------- It is not possible to create truly immutable Python objects. However, -by passing ``frozen=True`` to the :func:`@dataclass ` decorator you can +by passing ``frozen=True`` to the :deco:`dataclass` decorator you can emulate immutability. In that case, dataclasses will add :meth:`~object.__setattr__` and :meth:`~object.__delattr__` methods to the class. These methods will raise a :exc:`FrozenInstanceError` when invoked. @@ -660,7 +682,7 @@ must use :meth:`!object.__setattr__`. Inheritance ----------- -When the dataclass is being created by the :func:`@dataclass ` decorator, +When the dataclass is being created by the :deco:`dataclass` decorator, it looks through all of the class's base classes in reverse MRO (that is, starting at :class:`object`) and, for each dataclass that it finds, adds the fields from that base class to an ordered mapping of fields. @@ -784,7 +806,7 @@ for :attr:`!x` when creating a class instance will share the same copy of :attr:`!x`. Because dataclasses just use normal Python class creation they also share this behavior. There is no general way for Data Classes to detect this condition. Instead, the -:func:`@dataclass ` decorator will raise a :exc:`ValueError` if it +:deco:`dataclass` decorator will raise a :exc:`ValueError` if it detects an unhashable default parameter. The assumption is that if a value is unhashable, it is mutable. This is a partial solution, but it does protect against many common errors. @@ -818,7 +840,7 @@ default value have the following special behaviors: :meth:`~object.__get__` or :meth:`!__set__` method is called rather than returning or overwriting the descriptor object. -* To determine whether a field contains a default value, :func:`@dataclass ` +* To determine whether a field contains a default value, :deco:`dataclass` will call the descriptor's :meth:`!__get__` method using its class access form: ``descriptor.__get__(obj=None, type=cls)``. If the descriptor returns a value in this case, it will be used as the diff --git a/Doc/library/datetime-inheritance.dot b/Doc/library/datetime-inheritance.dot new file mode 100644 index 00000000000000..3c6b9b4beb7ab1 --- /dev/null +++ b/Doc/library/datetime-inheritance.dot @@ -0,0 +1,31 @@ +// Used to generate datetime-inheritance.svg with Graphviz +// (https://graphviz.org/) for the datetime documentation. + +digraph { + comment="Generated with datetime-inheritance.dot" + graph [ + bgcolor="transparent" + fontnames="svg" + layout="dot" + ranksep=0.5 + nodesep=0.5 + splines=line + ] + node [ + fontname="Courier" + fontsize=14.0 + shape=box + style=rounded + margin="0.15,0.07" + ] + edge [ + arrowhead=none + ] + + object -> tzinfo + object -> timedelta + object -> time + object -> date + tzinfo -> timezone + date -> datetime +} diff --git a/Doc/library/datetime-inheritance.svg b/Doc/library/datetime-inheritance.svg new file mode 100644 index 00000000000000..e6b1cf877a574f --- /dev/null +++ b/Doc/library/datetime-inheritance.svg @@ -0,0 +1,84 @@ + + + + + + +datetime class hierarchy + + +object + +object + + + +tzinfo + +tzinfo + + + +object->tzinfo + + + + +timedelta + +timedelta + + + +object->timedelta + + + + +time + +time + + + +object->time + + + + +date + +date + + + +object->date + + + + +timezone + +timezone + + + +tzinfo->timezone + + + + +datetime + +datetime + + + +date->datetime + + + + diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 16ed3215bc2c1a..f3c4ef9199075c 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -4,16 +4,10 @@ .. module:: datetime :synopsis: Basic date and time types. -.. moduleauthor:: Tim Peters -.. sectionauthor:: Tim Peters -.. sectionauthor:: A.M. Kuchling - **Source code:** :source:`Lib/datetime.py` -------------- -.. XXX what order should the types be discussed in? - The :mod:`!datetime` module supplies classes for manipulating dates and times. While date and time arithmetic is supported, the focus of the implementation is @@ -38,13 +32,14 @@ on efficient attribute extraction for output formatting and manipulation. Third-party library with expanded time zone and parsing support. Package :pypi:`DateType` - Third-party library that introduces distinct static types to e.g. allow - :term:`static type checkers ` + Third-party library that introduces distinct static types to for example, + allow :term:`static type checkers ` to differentiate between naive and aware datetimes. + .. _datetime-naive-aware: -Aware and Naive Objects +Aware and naive objects ----------------------- Date and time objects may be categorized as "aware" or "naive" depending on @@ -65,7 +60,7 @@ understand and to work with, at the cost of ignoring some aspects of reality. For applications requiring aware objects, :class:`.datetime` and :class:`.time` objects have an optional time zone information attribute, :attr:`!tzinfo`, that -can be set to an instance of a subclass of the abstract :class:`tzinfo` class. +can be set to an instance of a subclass of the abstract :class:`!tzinfo` class. These :class:`tzinfo` objects capture information about the offset from UTC time, the time zone name, and whether daylight saving time is in effect. @@ -77,6 +72,7 @@ detail is up to the application. The rules for time adjustment across the world are more political than rational, change frequently, and there is no standard suitable for every application aside from UTC. + Constants --------- @@ -93,13 +89,15 @@ The :mod:`!datetime` module exports the following constants: The largest year number allowed in a :class:`date` or :class:`.datetime` object. :const:`MAXYEAR` is 9999. + .. data:: UTC Alias for the UTC time zone singleton :attr:`datetime.timezone.utc`. .. versionadded:: 3.11 -Available Types + +Available types --------------- .. class:: date @@ -142,6 +140,7 @@ Available Types time adjustment (for example, to account for time zone and/or daylight saving time). + .. class:: timezone :noindex: @@ -150,19 +149,19 @@ Available Types .. versionadded:: 3.2 + Objects of these types are immutable. -Subclass relationships:: +Subclass relationships: + +.. figure:: datetime-inheritance.svg + :class: invert-in-dark-mode + :align: center + :alt: timedelta, tzinfo, time, and date inherit from object; timezone inherits + from tzinfo; and datetime inherits from date. - object - timedelta - tzinfo - timezone - time - date - datetime -Common Properties +Common properties ^^^^^^^^^^^^^^^^^ The :class:`date`, :class:`.datetime`, :class:`.time`, and :class:`timezone` types @@ -173,7 +172,8 @@ share these common features: dictionary keys. - Objects of these types support efficient pickling via the :mod:`pickle` module. -Determining if an Object is Aware or Naive + +Determining if an object is aware or naive ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Objects of the :class:`date` type are always naive. @@ -197,10 +197,11 @@ Otherwise, ``t`` is naive. The distinction between aware and naive doesn't apply to :class:`timedelta` objects. + .. _datetime-timedelta: -:class:`timedelta` Objects --------------------------- +:class:`!timedelta` objects +--------------------------- A :class:`timedelta` object represents a duration, the difference between two :class:`.datetime` or :class:`date` instances. @@ -229,8 +230,8 @@ A :class:`timedelta` object represents a duration, the difference between two *days*, *seconds* and *microseconds* are "merged" and normalized into those three resulting attributes:: - >>> from datetime import timedelta - >>> delta = timedelta( + >>> import datetime as dt + >>> delta = dt.timedelta( ... days=50, ... seconds=27, ... microseconds=10, @@ -243,6 +244,12 @@ A :class:`timedelta` object represents a duration, the difference between two >>> delta datetime.timedelta(days=64, seconds=29156, microseconds=10) + .. tip:: + ``import datetime as dt`` instead of ``import datetime`` or + ``from datetime import datetime`` to avoid confusion between the module + and the class. See `How I Import Python’s datetime Module + `__. + If any argument is a float and there are fractional microseconds, the fractional microseconds left over from all arguments are combined and their sum is rounded to the nearest microsecond using @@ -256,8 +263,8 @@ A :class:`timedelta` object represents a duration, the difference between two Note that normalization of negative values may be surprising at first. For example:: - >>> from datetime import timedelta - >>> d = timedelta(microseconds=-1) + >>> import datetime as dt + >>> d = dt.timedelta(microseconds=-1) >>> (d.days, d.seconds, d.microseconds) (-1, 86399, 999999) @@ -296,6 +303,7 @@ Class attributes: The smallest possible difference between non-equal :class:`timedelta` objects, ``timedelta(microseconds=1)``. + Note that, because of normalization, ``timedelta.max`` is greater than ``-timedelta.min``. ``-timedelta.max`` is not representable as a :class:`timedelta` object. @@ -319,13 +327,14 @@ Instance attributes (read-only): .. doctest:: - >>> from datetime import timedelta - >>> duration = timedelta(seconds=11235813) + >>> import datetime as dt + >>> duration = dt.timedelta(seconds=11235813) >>> duration.days, duration.seconds (130, 3813) >>> duration.total_seconds() 11235813.0 + .. attribute:: timedelta.microseconds Between 0 and 999,999 inclusive. @@ -333,8 +342,6 @@ Instance attributes (read-only): Supported operations: -.. XXX this table is too wide! - +--------------------------------+-----------------------------------------------+ | Operation | Result | +================================+===============================================+ @@ -396,7 +403,6 @@ Supported operations: | | call with canonical attribute values. | +--------------------------------+-----------------------------------------------+ - Notes: (1) @@ -432,9 +438,9 @@ objects (see below). .. versionchanged:: 3.2 Floor division and true division of a :class:`timedelta` object by another - :class:`timedelta` object are now supported, as are remainder operations and + :class:`!timedelta` object are now supported, as are remainder operations and the :func:`divmod` function. True division and multiplication of a - :class:`timedelta` object by a :class:`float` object are now supported. + :class:`!timedelta` object by a :class:`float` object are now supported. :class:`timedelta` objects support equality and order comparisons. @@ -447,23 +453,24 @@ Instance methods: Return the total number of seconds contained in the duration. Equivalent to ``td / timedelta(seconds=1)``. For interval units other than seconds, use the - division form directly (e.g. ``td / timedelta(microseconds=1)``). + division form directly (for example, ``td / timedelta(microseconds=1)``). Note that for very large time intervals (greater than 270 years on most platforms) this method will lose microsecond accuracy. .. versionadded:: 3.2 -Examples of usage: :class:`timedelta` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Examples of usage: :class:`!timedelta` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ An additional example of normalization:: >>> # Components of another_year add up to exactly 365 days - >>> from datetime import timedelta - >>> year = timedelta(days=365) - >>> another_year = timedelta(weeks=40, days=84, hours=23, - ... minutes=50, seconds=600) + >>> import datetime as dt + >>> year = dt.timedelta(days=365) + >>> another_year = dt.timedelta(weeks=40, days=84, hours=23, + ... minutes=50, seconds=600) >>> year == another_year True >>> year.total_seconds() @@ -471,8 +478,8 @@ An additional example of normalization:: Examples of :class:`timedelta` arithmetic:: - >>> from datetime import timedelta - >>> year = timedelta(days=365) + >>> import datetime as dt + >>> year = dt.timedelta(days=365) >>> ten_years = 10 * year >>> ten_years datetime.timedelta(days=3650) @@ -485,10 +492,11 @@ Examples of :class:`timedelta` arithmetic:: >>> three_years, three_years.days // 365 (datetime.timedelta(days=1095), 3) + .. _datetime-date: -:class:`date` Objects ---------------------- +:class:`!date` objects +---------------------- A :class:`date` object represents a date (year, month and day) in an idealized calendar, the current Gregorian calendar indefinitely extended in both @@ -517,9 +525,10 @@ Other constructors, all class methods: This is equivalent to ``date.fromtimestamp(time.time())``. + .. classmethod:: date.fromtimestamp(timestamp) - Return the local date corresponding to the POSIX timestamp, such as is + Return the local date corresponding to the POSIX *timestamp*, such as is returned by :func:`time.time`. This may raise :exc:`OverflowError`, if the timestamp is out @@ -535,10 +544,13 @@ Other constructors, all class methods: :c:func:`localtime` function. Raise :exc:`OSError` instead of :exc:`ValueError` on :c:func:`localtime` failure. + .. versionchanged:: 3.15 + Accepts any real number as *timestamp*, not only integer or float. + .. classmethod:: date.fromordinal(ordinal) - Return the date corresponding to the proleptic Gregorian ordinal, where + Return the date corresponding to the proleptic Gregorian *ordinal*, where January 1 of year 1 has ordinal 1. :exc:`ValueError` is raised unless ``1 <= ordinal <= @@ -559,25 +571,27 @@ Other constructors, all class methods: Examples:: - >>> from datetime import date - >>> date.fromisoformat('2019-12-04') + >>> import datetime as dt + >>> dt.date.fromisoformat('2019-12-04') datetime.date(2019, 12, 4) - >>> date.fromisoformat('20191204') + >>> dt.date.fromisoformat('20191204') datetime.date(2019, 12, 4) - >>> date.fromisoformat('2021-W01-1') + >>> dt.date.fromisoformat('2021-W01-1') datetime.date(2021, 1, 4) .. versionadded:: 3.7 .. versionchanged:: 3.11 Previously, this method only supported the format ``YYYY-MM-DD``. + .. classmethod:: date.fromisocalendar(year, week, day) Return a :class:`date` corresponding to the ISO calendar date specified by - year, week and day. This is the inverse of the function :meth:`date.isocalendar`. + *year*, *week* and *day*. This is the inverse of the function :meth:`date.isocalendar`. .. versionadded:: 3.8 + .. classmethod:: date.strptime(date_string, format) Return a :class:`.date` corresponding to *date_string*, parsed according to @@ -592,20 +606,19 @@ Other constructors, all class methods: .. note:: - If *format* specifies a day of month without a year a - :exc:`DeprecationWarning` is emitted. This is to avoid a quadrennial + If *format* specifies a day of month (``%d``) without a year, + :exc:`ValueError` is raised. This is to avoid a quadrennial leap year bug in code seeking to parse only a month and day as the default year used in absence of one in the format is not a leap year. - Such *format* values may raise an error as of Python 3.15. The - workaround is to always include a year in your *format*. If parsing + The workaround is to always include a year in your *format*. If parsing *date_string* values that do not have a year, explicitly add a year that is a leap year before parsing: .. doctest:: - >>> from datetime import date + >>> import datetime as dt >>> date_string = "02/29" - >>> when = date.strptime(f"{date_string};1984", "%m/%d;%Y") # Avoids leap year bug. + >>> when = dt.date.strptime(f"{date_string};1984", "%m/%d;%Y") # Avoids leap year bug. >>> when.strftime("%B %d") # doctest: +SKIP 'February 29' @@ -697,7 +710,7 @@ Notes: In other words, ``date1 < date2`` if and only if ``date1.toordinal() < date2.toordinal()``. - Order comparison between a :class:`!date` object that is not also a + Order comparison between a :class:`date` object that is not also a :class:`.datetime` instance and a :class:`!datetime` object raises :exc:`TypeError`. @@ -720,8 +733,8 @@ Instance methods: Example:: - >>> from datetime import date - >>> d = date(2002, 12, 31) + >>> import datetime as dt + >>> d = dt.date(2002, 12, 31) >>> d.replace(day=26) datetime.date(2002, 12, 26) @@ -779,23 +792,25 @@ Instance methods: For example, 2004 begins on a Thursday, so the first week of ISO year 2004 begins on Monday, 29 Dec 2003 and ends on Sunday, 4 Jan 2004:: - >>> from datetime import date - >>> date(2003, 12, 29).isocalendar() + >>> import datetime as dt + >>> dt.date(2003, 12, 29).isocalendar() datetime.IsoCalendarDate(year=2004, week=1, weekday=1) - >>> date(2004, 1, 4).isocalendar() + >>> dt.date(2004, 1, 4).isocalendar() datetime.IsoCalendarDate(year=2004, week=1, weekday=7) .. versionchanged:: 3.9 Result changed from a tuple to a :term:`named tuple`. + .. method:: date.isoformat() Return a string representing the date in ISO 8601 format, ``YYYY-MM-DD``:: - >>> from datetime import date - >>> date(2002, 12, 4).isoformat() + >>> import datetime as dt + >>> dt.date(2002, 12, 4).isoformat() '2002-12-04' + .. method:: date.__str__() For a date ``d``, ``str(d)`` is equivalent to ``d.isoformat()``. @@ -805,8 +820,8 @@ Instance methods: Return a string representing the date:: - >>> from datetime import date - >>> date(2002, 12, 4).ctime() + >>> import datetime as dt + >>> dt.date(2002, 12, 4).ctime() 'Wed Dec 4 00:00:00 2002' ``d.ctime()`` is equivalent to:: @@ -832,19 +847,20 @@ Instance methods: literals ` and when using :meth:`str.format`. See also :ref:`strftime-strptime-behavior` and :meth:`date.isoformat`. -Examples of Usage: :class:`date` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Examples of usage: :class:`!date` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Example of counting days to an event:: >>> import time - >>> from datetime import date - >>> today = date.today() + >>> import datetime as dt + >>> today = dt.date.today() >>> today datetime.date(2007, 12, 5) - >>> today == date.fromtimestamp(time.time()) + >>> today == dt.date.fromtimestamp(time.time()) True - >>> my_birthday = date(today.year, 6, 24) + >>> my_birthday = dt.date(today.year, 6, 24) >>> if my_birthday < today: ... my_birthday = my_birthday.replace(year=today.year + 1) ... @@ -858,8 +874,8 @@ More examples of working with :class:`date`: .. doctest:: - >>> from datetime import date - >>> d = date.fromordinal(730920) # 730920th day after 1. 1. 0001 + >>> import datetime as dt + >>> d = dt.date.fromordinal(730920) # 730920th day after 1. 1. 0001 >>> d datetime.date(2002, 3, 11) @@ -875,7 +891,7 @@ More examples of working with :class:`date`: >>> 'The {1} is {0:%d}, the {2} is {0:%B}.'.format(d, "day", "month") 'The day is 11, the month is March.' - >>> # Methods for to extracting 'components' under different calendars + >>> # Methods for extracting 'components' under different calendars >>> t = d.timetuple() >>> for i in t: # doctest: +SKIP ... print(i) @@ -902,7 +918,7 @@ More examples of working with :class:`date`: .. _datetime-datetime: -:class:`.datetime` Objects +:class:`!datetime` objects -------------------------- A :class:`.datetime` object is a single object containing all the information @@ -910,7 +926,7 @@ from a :class:`date` object and a :class:`.time` object. Like a :class:`date` object, :class:`.datetime` assumes the current Gregorian calendar extended in both directions; like a :class:`.time` object, -:class:`.datetime` assumes there are exactly 3600\*24 seconds in every day. +:class:`!datetime` assumes there are exactly 3600\*24 seconds in every day. Constructor: @@ -934,6 +950,7 @@ Constructor: .. versionchanged:: 3.6 Added the *fold* parameter. + Other constructors, all class methods: .. classmethod:: datetime.today() @@ -949,6 +966,7 @@ Other constructors, all class methods: This method is functionally equivalent to :meth:`now`, but without a ``tz`` parameter. + .. classmethod:: datetime.now(tz=None) Return the current local date and time. @@ -969,6 +987,7 @@ Other constructors, all class methods: Subsequent calls to :meth:`!datetime.now` may return the same instant depending on the precision of the underlying clock. + .. classmethod:: datetime.utcnow() Return the current UTC date and time, with :attr:`.tzinfo` ``None``. @@ -1020,6 +1039,10 @@ Other constructors, all class methods: .. versionchanged:: 3.6 :meth:`fromtimestamp` may return instances with :attr:`.fold` set to 1. + .. versionchanged:: 3.15 + Accepts any real number as *timestamp*, not only integer or float. + + .. classmethod:: datetime.utcfromtimestamp(timestamp) Return the UTC :class:`.datetime` corresponding to the POSIX timestamp, with @@ -1056,6 +1079,9 @@ Other constructors, all class methods: :c:func:`gmtime` function. Raise :exc:`OSError` instead of :exc:`ValueError` on :c:func:`gmtime` failure. + .. versionchanged:: 3.15 + Accepts any real number as *timestamp*, not only integer or float. + .. deprecated:: 3.12 Use :meth:`datetime.fromtimestamp` with :const:`UTC` instead. @@ -1076,7 +1102,7 @@ Other constructors, all class methods: are equal to the given :class:`.time` object's. If the *tzinfo* argument is provided, its value is used to set the :attr:`.tzinfo` attribute of the result, otherwise the :attr:`~.time.tzinfo` attribute of the *time* argument - is used. If the *date* argument is a :class:`.datetime` object, its time components + is used. If the *date* argument is a :class:`!datetime` object, its time components and :attr:`.tzinfo` attributes are ignored. For any :class:`.datetime` object ``d``, @@ -1102,24 +1128,24 @@ Other constructors, all class methods: Examples:: - >>> from datetime import datetime - >>> datetime.fromisoformat('2011-11-04') + >>> import datetime as dt + >>> dt.datetime.fromisoformat('2011-11-04') datetime.datetime(2011, 11, 4, 0, 0) - >>> datetime.fromisoformat('20111104') + >>> dt.datetime.fromisoformat('20111104') datetime.datetime(2011, 11, 4, 0, 0) - >>> datetime.fromisoformat('2011-11-04T00:05:23') + >>> dt.datetime.fromisoformat('2011-11-04T00:05:23') datetime.datetime(2011, 11, 4, 0, 5, 23) - >>> datetime.fromisoformat('2011-11-04T00:05:23Z') + >>> dt.datetime.fromisoformat('2011-11-04T00:05:23Z') datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone.utc) - >>> datetime.fromisoformat('20111104T000523') + >>> dt.datetime.fromisoformat('20111104T000523') datetime.datetime(2011, 11, 4, 0, 5, 23) - >>> datetime.fromisoformat('2011-W01-2T00:05:23.283') + >>> dt.datetime.fromisoformat('2011-W01-2T00:05:23.283') datetime.datetime(2011, 1, 4, 0, 5, 23, 283000) - >>> datetime.fromisoformat('2011-11-04 00:05:23.283') + >>> dt.datetime.fromisoformat('2011-11-04 00:05:23.283') datetime.datetime(2011, 11, 4, 0, 5, 23, 283000) - >>> datetime.fromisoformat('2011-11-04 00:05:23.283+00:00') + >>> dt.datetime.fromisoformat('2011-11-04 00:05:23.283+00:00') datetime.datetime(2011, 11, 4, 0, 5, 23, 283000, tzinfo=datetime.timezone.utc) - >>> datetime.fromisoformat('2011-11-04T00:05:23+04:00') # doctest: +NORMALIZE_WHITESPACE + >>> dt.datetime.fromisoformat('2011-11-04T00:05:23+04:00') # doctest: +NORMALIZE_WHITESPACE datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone(datetime.timedelta(seconds=14400))) @@ -1132,12 +1158,13 @@ Other constructors, all class methods: .. classmethod:: datetime.fromisocalendar(year, week, day) Return a :class:`.datetime` corresponding to the ISO calendar date specified - by year, week and day. The non-date components of the datetime are populated + by *year*, *week* and *day*. The non-date components of the datetime are populated with their normal default values. This is the inverse of the function :meth:`datetime.isocalendar`. .. versionadded:: 3.8 + .. classmethod:: datetime.strptime(date_string, format) Return a :class:`.datetime` corresponding to *date_string*, parsed according to @@ -1152,22 +1179,21 @@ Other constructors, all class methods: time tuple. See also :ref:`strftime-strptime-behavior` and :meth:`datetime.fromisoformat`. - .. versionchanged:: 3.13 + .. versionchanged:: 3.15 - If *format* specifies a day of month without a year a - :exc:`DeprecationWarning` is now emitted. This is to avoid a quadrennial + If *format* specifies a day of month (``%d``) without a year, + :exc:`ValueError` is raised. This is to avoid a quadrennial leap year bug in code seeking to parse only a month and day as the default year used in absence of one in the format is not a leap year. - Such *format* values may raise an error as of Python 3.15. The - workaround is to always include a year in your *format*. If parsing + The workaround is to always include a year in your *format*. If parsing *date_string* values that do not have a year, explicitly add a year that is a leap year before parsing: .. doctest:: - >>> from datetime import datetime + >>> import datetime as dt >>> date_string = "02/29" - >>> when = datetime.strptime(f"{date_string};1984", "%m/%d;%Y") # Avoids leap year bug. + >>> when = dt.datetime.strptime(f"{date_string};1984", "%m/%d;%Y") # Avoids leap year bug. >>> when.strftime("%B %d") # doctest: +SKIP 'February 29' @@ -1245,6 +1271,7 @@ Instance attributes (read-only): .. versionadded:: 3.6 + Supported operations: +---------------------------------------+--------------------------------+ @@ -1280,7 +1307,7 @@ Supported operations: datetime, and no time zone adjustments are done even if the input is aware. (3) - Subtraction of a :class:`.datetime` from a :class:`.datetime` is defined only if + Subtraction of a :class:`.datetime` from a :class:`!datetime` is defined only if both operands are naive, or if both are aware. If one is aware and the other is naive, :exc:`TypeError` is raised. @@ -1298,7 +1325,7 @@ Supported operations: :class:`.datetime` objects are equal if they represent the same date and time, taking into account the time zone. - Naive and aware :class:`!datetime` objects are never equal. + Naive and aware :class:`.datetime` objects are never equal. If both comparands are aware, and have the same :attr:`!tzinfo` attribute, the :attr:`!tzinfo` and :attr:`~.datetime.fold` attributes are ignored and @@ -1306,7 +1333,7 @@ Supported operations: If both comparands are aware and have different :attr:`~.datetime.tzinfo` attributes, the comparison acts as comparands were first converted to UTC datetimes except that the implementation never overflows. - :class:`!datetime` instances in a repeated interval are never equal to + :class:`.datetime` instances in a repeated interval are never equal to :class:`!datetime` instances in other time zone. (5) @@ -1335,6 +1362,7 @@ Supported operations: The default behavior can be changed by overriding the special comparison methods in subclasses. + Instance methods: .. method:: datetime.date() @@ -1490,11 +1518,13 @@ Instance methods: ``datetime.replace(tzinfo=timezone.utc)`` to make it aware, at which point you can use :meth:`.datetime.timetuple`. + .. method:: datetime.toordinal() Return the proleptic Gregorian ordinal of the date. The same as ``self.date().toordinal()``. + .. method:: datetime.timestamp() Return POSIX timestamp corresponding to the :class:`.datetime` @@ -1503,7 +1533,7 @@ Instance methods: Naive :class:`.datetime` instances are assumed to represent local time and this method relies on platform C functions to perform - the conversion. Since :class:`.datetime` supports a wider range of + the conversion. Since :class:`!datetime` supports a wider range of values than the platform C functions on many platforms, this method may raise :exc:`OverflowError` or :exc:`OSError` for times far in the past or far in the future. @@ -1513,16 +1543,6 @@ Instance methods: (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)).total_seconds() - .. versionadded:: 3.3 - - .. versionchanged:: 3.6 - The :meth:`timestamp` method uses the :attr:`.fold` attribute to - disambiguate the times during a repeated interval. - - .. versionchanged:: 3.6 - This method no longer relies on the platform C :c:func:`mktime` - function to perform conversions. - .. note:: There is no method to obtain the POSIX timestamp directly from a @@ -1537,6 +1557,17 @@ Instance methods: timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1) + .. versionadded:: 3.3 + + .. versionchanged:: 3.6 + The :meth:`timestamp` method uses the :attr:`.fold` attribute to + disambiguate the times during a repeated interval. + + .. versionchanged:: 3.6 + This method no longer relies on the platform C :c:func:`mktime` + function to perform conversions. + + .. method:: datetime.weekday() Return the day of the week as an integer, where Monday is 0 and Sunday is 6. @@ -1572,24 +1603,24 @@ Instance methods: Examples:: - >>> from datetime import datetime, timezone - >>> datetime(2019, 5, 18, 15, 17, 8, 132263).isoformat() + >>> import datetime as dt + >>> dt.datetime(2019, 5, 18, 15, 17, 8, 132263).isoformat() '2019-05-18T15:17:08.132263' - >>> datetime(2019, 5, 18, 15, 17, tzinfo=timezone.utc).isoformat() + >>> dt.datetime(2019, 5, 18, 15, 17, tzinfo=dt.timezone.utc).isoformat() '2019-05-18T15:17:00+00:00' The optional argument *sep* (default ``'T'``) is a one-character separator, placed between the date and time portions of the result. For example:: - >>> from datetime import tzinfo, timedelta, datetime - >>> class TZ(tzinfo): + >>> import datetime as dt + >>> class TZ(dt.tzinfo): ... """A time zone with an arbitrary, constant -06:39 offset.""" - ... def utcoffset(self, dt): - ... return timedelta(hours=-6, minutes=-39) + ... def utcoffset(self, when): + ... return dt.timedelta(hours=-6, minutes=-39) ... - >>> datetime(2002, 12, 25, tzinfo=TZ()).isoformat(' ') + >>> dt.datetime(2002, 12, 25, tzinfo=TZ()).isoformat(' ') '2002-12-25 00:00:00-06:39' - >>> datetime(2009, 11, 27, microsecond=100, tzinfo=TZ()).isoformat() + >>> dt.datetime(2009, 11, 27, microsecond=100, tzinfo=TZ()).isoformat() '2009-11-27T00:00:00.000100-06:39' The optional argument *timespec* specifies the number of additional @@ -1613,11 +1644,11 @@ Instance methods: :exc:`ValueError` will be raised on an invalid *timespec* argument:: - >>> from datetime import datetime - >>> datetime.now().isoformat(timespec='minutes') # doctest: +SKIP + >>> import datetime as dt + >>> dt.datetime.now().isoformat(timespec='minutes') # doctest: +SKIP '2002-12-25T00:00' - >>> dt = datetime(2015, 1, 1, 12, 30, 59, 0) - >>> dt.isoformat(timespec='microseconds') + >>> my_datetime = dt.datetime(2015, 1, 1, 12, 30, 59, 0) + >>> my_datetime.isoformat(timespec='microseconds') '2015-01-01T12:30:59.000000' .. versionchanged:: 3.6 @@ -1634,8 +1665,8 @@ Instance methods: Return a string representing the date and time:: - >>> from datetime import datetime - >>> datetime(2002, 12, 4, 20, 30, 40).ctime() + >>> import datetime as dt + >>> dt.datetime(2002, 12, 4, 20, 30, 40).ctime() 'Wed Dec 4 20:30:40 2002' The output string will *not* include time zone information, regardless @@ -1665,34 +1696,34 @@ Instance methods: See also :ref:`strftime-strptime-behavior` and :meth:`datetime.isoformat`. -Examples of Usage: :class:`.datetime` +Examples of usage: :class:`!datetime` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Examples of working with :class:`.datetime` objects: .. doctest:: - >>> from datetime import datetime, date, time, timezone + >>> import datetime as dt >>> # Using datetime.combine() - >>> d = date(2005, 7, 14) - >>> t = time(12, 30) - >>> datetime.combine(d, t) + >>> d = dt.date(2005, 7, 14) + >>> t = dt.time(12, 30) + >>> dt.datetime.combine(d, t) datetime.datetime(2005, 7, 14, 12, 30) >>> # Using datetime.now() - >>> datetime.now() # doctest: +SKIP + >>> dt.datetime.now() # doctest: +SKIP datetime.datetime(2007, 12, 6, 16, 29, 43, 79043) # GMT +1 - >>> datetime.now(timezone.utc) # doctest: +SKIP + >>> dt.datetime.now(dt.timezone.utc) # doctest: +SKIP datetime.datetime(2007, 12, 6, 15, 29, 43, 79060, tzinfo=datetime.timezone.utc) >>> # Using datetime.strptime() - >>> dt = datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M") - >>> dt + >>> my_datetime = dt.datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M") + >>> my_datetime datetime.datetime(2006, 11, 21, 16, 30) >>> # Using datetime.timetuple() to get tuple of all attributes - >>> tt = dt.timetuple() + >>> tt = my_datetime.timetuple() >>> for it in tt: # doctest: +SKIP ... print(it) ... @@ -1707,7 +1738,7 @@ Examples of working with :class:`.datetime` objects: -1 # dst - method tzinfo.dst() returned None >>> # Date in ISO format - >>> ic = dt.isocalendar() + >>> ic = my_datetime.isocalendar() >>> for it in ic: # doctest: +SKIP ... print(it) ... @@ -1716,55 +1747,55 @@ Examples of working with :class:`.datetime` objects: 2 # ISO weekday >>> # Formatting a datetime - >>> dt.strftime("%A, %d. %B %Y %I:%M%p") + >>> my_datetime.strftime("%A, %d. %B %Y %I:%M%p") 'Tuesday, 21. November 2006 04:30PM' - >>> 'The {1} is {0:%d}, the {2} is {0:%B}, the {3} is {0:%I:%M%p}.'.format(dt, "day", "month", "time") + >>> 'The {1} is {0:%d}, the {2} is {0:%B}, the {3} is {0:%I:%M%p}.'.format(my_datetime, "day", "month", "time") 'The day is 21, the month is November, the time is 04:30PM.' The example below defines a :class:`tzinfo` subclass capturing time zone information for Kabul, Afghanistan, which used +4 UTC until 1945 and then +4:30 UTC thereafter:: - from datetime import timedelta, datetime, tzinfo, timezone + import datetime as dt - class KabulTz(tzinfo): + class KabulTz(dt.tzinfo): # Kabul used +4 until 1945, when they moved to +4:30 - UTC_MOVE_DATE = datetime(1944, 12, 31, 20, tzinfo=timezone.utc) + UTC_MOVE_DATE = dt.datetime(1944, 12, 31, 20, tzinfo=dt.timezone.utc) - def utcoffset(self, dt): - if dt.year < 1945: - return timedelta(hours=4) - elif (1945, 1, 1, 0, 0) <= dt.timetuple()[:5] < (1945, 1, 1, 0, 30): + def utcoffset(self, when): + if when.year < 1945: + return dt.timedelta(hours=4) + elif (1945, 1, 1, 0, 0) <= when.timetuple()[:5] < (1945, 1, 1, 0, 30): # An ambiguous ("imaginary") half-hour range representing # a 'fold' in time due to the shift from +4 to +4:30. - # If dt falls in the imaginary range, use fold to decide how - # to resolve. See PEP495. - return timedelta(hours=4, minutes=(30 if dt.fold else 0)) + # If when falls in the imaginary range, use fold to decide how + # to resolve. See PEP 495. + return dt.timedelta(hours=4, minutes=(30 if when.fold else 0)) else: - return timedelta(hours=4, minutes=30) + return dt.timedelta(hours=4, minutes=30) - def fromutc(self, dt): + def fromutc(self, when): # Follow same validations as in datetime.tzinfo - if not isinstance(dt, datetime): + if not isinstance(when, dt.datetime): raise TypeError("fromutc() requires a datetime argument") - if dt.tzinfo is not self: - raise ValueError("dt.tzinfo is not self") + if when.tzinfo is not self: + raise ValueError("when.tzinfo is not self") # A custom implementation is required for fromutc as # the input to this function is a datetime with utc values # but with a tzinfo set to self. # See datetime.astimezone or fromtimestamp. - if dt.replace(tzinfo=timezone.utc) >= self.UTC_MOVE_DATE: - return dt + timedelta(hours=4, minutes=30) + if when.replace(tzinfo=dt.timezone.utc) >= self.UTC_MOVE_DATE: + return when + dt.timedelta(hours=4, minutes=30) else: - return dt + timedelta(hours=4) + return when + dt.timedelta(hours=4) - def dst(self, dt): + def dst(self, when): # Kabul does not observe daylight saving time. - return timedelta(0) + return dt.timedelta(0) - def tzname(self, dt): - if dt >= self.UTC_MOVE_DATE: + def tzname(self, when): + if when >= self.UTC_MOVE_DATE: return "+04:30" return "+04" @@ -1773,17 +1804,17 @@ Usage of ``KabulTz`` from above:: >>> tz1 = KabulTz() >>> # Datetime before the change - >>> dt1 = datetime(1900, 11, 21, 16, 30, tzinfo=tz1) + >>> dt1 = dt.datetime(1900, 11, 21, 16, 30, tzinfo=tz1) >>> print(dt1.utcoffset()) 4:00:00 >>> # Datetime after the change - >>> dt2 = datetime(2006, 6, 14, 13, 0, tzinfo=tz1) + >>> dt2 = dt.datetime(2006, 6, 14, 13, 0, tzinfo=tz1) >>> print(dt2.utcoffset()) 4:30:00 >>> # Convert datetime to another time zone - >>> dt3 = dt2.astimezone(timezone.utc) + >>> dt3 = dt2.astimezone(dt.timezone.utc) >>> dt3 datetime.datetime(2006, 6, 14, 8, 30, tzinfo=datetime.timezone.utc) >>> dt2 @@ -1791,9 +1822,10 @@ Usage of ``KabulTz`` from above:: >>> dt2 == dt3 True + .. _datetime-time: -:class:`.time` Objects +:class:`!time` objects ---------------------- A :class:`.time` object represents a (local) time of day, independent of any particular @@ -1814,6 +1846,7 @@ day, and subject to adjustment via a :class:`tzinfo` object. If an argument outside those ranges is given, :exc:`ValueError` is raised. All default to 0 except *tzinfo*, which defaults to ``None``. + Class attributes: @@ -1872,6 +1905,7 @@ Instance attributes (read-only): .. versionadded:: 3.6 + :class:`.time` objects support equality and order comparisons, where ``a`` is considered less than ``b`` when ``a`` precedes ``b`` in time. @@ -1894,8 +1928,8 @@ In Boolean contexts, a :class:`.time` object is always considered to be true. .. versionchanged:: 3.5 Before Python 3.5, a :class:`.time` object was considered to be false if it represented midnight in UTC. This behavior was considered obscure and - error-prone and has been removed in Python 3.5. See :issue:`13936` for full - details. + error-prone and has been removed in Python 3.5. See :issue:`13936` for more + information. Other constructors: @@ -1916,22 +1950,22 @@ Other constructors: .. doctest:: - >>> from datetime import time - >>> time.fromisoformat('04:23:01') + >>> import datetime as dt + >>> dt.time.fromisoformat('04:23:01') datetime.time(4, 23, 1) - >>> time.fromisoformat('T04:23:01') + >>> dt.time.fromisoformat('T04:23:01') datetime.time(4, 23, 1) - >>> time.fromisoformat('T042301') + >>> dt.time.fromisoformat('T042301') datetime.time(4, 23, 1) - >>> time.fromisoformat('04:23:01.000384') + >>> dt.time.fromisoformat('04:23:01.000384') datetime.time(4, 23, 1, 384) - >>> time.fromisoformat('04:23:01,000384') + >>> dt.time.fromisoformat('04:23:01,000384') datetime.time(4, 23, 1, 384) - >>> time.fromisoformat('04:23:01+04:00') + >>> dt.time.fromisoformat('04:23:01+04:00') datetime.time(4, 23, 1, tzinfo=datetime.timezone(datetime.timedelta(seconds=14400))) - >>> time.fromisoformat('04:23:01Z') + >>> dt.time.fromisoformat('04:23:01Z') datetime.time(4, 23, 1, tzinfo=datetime.timezone.utc) - >>> time.fromisoformat('04:23:01+00:00') + >>> dt.time.fromisoformat('04:23:01+00:00') datetime.time(4, 23, 1, tzinfo=datetime.timezone.utc) @@ -1940,6 +1974,7 @@ Other constructors: Previously, this method only supported formats that could be emitted by :meth:`time.isoformat`. + .. classmethod:: time.strptime(date_string, format) Return a :class:`.time` corresponding to *date_string*, parsed according to @@ -1964,7 +1999,7 @@ Instance methods: Return a new :class:`.time` with the same values, but with specified parameters updated. Note that ``tzinfo=None`` can be specified to create a - naive :class:`.time` from an aware :class:`.time`, without conversion of the + naive :class:`!time` from an aware :class:`!time`, without conversion of the time data. :class:`.time` objects are also supported by generic function @@ -2005,13 +2040,13 @@ Instance methods: Example:: - >>> from datetime import time - >>> time(hour=12, minute=34, second=56, microsecond=123456).isoformat(timespec='minutes') + >>> import datetime as dt + >>> dt.time(hour=12, minute=34, second=56, microsecond=123456).isoformat(timespec='minutes') '12:34' - >>> dt = time(hour=12, minute=34, second=56, microsecond=0) - >>> dt.isoformat(timespec='microseconds') + >>> my_time = dt.time(hour=12, minute=34, second=56, microsecond=0) + >>> my_time.isoformat(timespec='microseconds') '12:34:56.000000' - >>> dt.isoformat(timespec='auto') + >>> my_time.isoformat(timespec='auto') '12:34:56' .. versionchanged:: 3.6 @@ -2056,29 +2091,31 @@ Instance methods: .. versionchanged:: 3.7 The DST offset is not restricted to a whole number of minutes. + .. method:: time.tzname() If :attr:`.tzinfo` is ``None``, returns ``None``, else returns ``self.tzinfo.tzname(None)``, or raises an exception if the latter doesn't return ``None`` or a string object. -Examples of Usage: :class:`.time` + +Examples of usage: :class:`!time` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Examples of working with a :class:`.time` object:: - >>> from datetime import time, tzinfo, timedelta - >>> class TZ1(tzinfo): - ... def utcoffset(self, dt): - ... return timedelta(hours=1) - ... def dst(self, dt): - ... return timedelta(0) - ... def tzname(self,dt): + >>> import datetime as dt + >>> class TZ1(dt.tzinfo): + ... def utcoffset(self, when): + ... return dt.timedelta(hours=1) + ... def dst(self, when): + ... return dt.timedelta(0) + ... def tzname(self, when): ... return "+01:00" ... def __repr__(self): ... return f"{self.__class__.__name__}()" ... - >>> t = time(12, 10, 30, tzinfo=TZ1()) + >>> t = dt.time(12, 10, 30, tzinfo=TZ1()) >>> t datetime.time(12, 10, 30, tzinfo=TZ1()) >>> t.isoformat() @@ -2095,25 +2132,25 @@ Examples of working with a :class:`.time` object:: .. _datetime-tzinfo: -:class:`tzinfo` Objects ------------------------ +:class:`!tzinfo` objects +------------------------ .. class:: tzinfo() - This is an abstract base class, meaning that this class should not be + This is an :term:`abstract base class`, meaning that this class should not be instantiated directly. Define a subclass of :class:`tzinfo` to capture information about a particular time zone. An instance of (a concrete subclass of) :class:`tzinfo` can be passed to the constructors for :class:`.datetime` and :class:`.time` objects. The latter objects - view their attributes as being in local time, and the :class:`tzinfo` object + view their attributes as being in local time, and the :class:`!tzinfo` object supports methods revealing offset of local time from UTC, the name of the time zone, and DST offset, all relative to a date or time object passed to them. You need to derive a concrete subclass, and (at least) supply implementations of the standard :class:`tzinfo` methods needed by the :class:`.datetime` methods you use. The :mod:`!datetime` module provides - :class:`timezone`, a simple concrete subclass of :class:`tzinfo` which can + :class:`timezone`, a simple concrete subclass of :class:`!tzinfo` which can represent time zones with fixed offset from UTC such as UTC itself or North American EST and EDT. @@ -2176,31 +2213,35 @@ Examples of working with a :class:`.time` object:: ``tz.utcoffset(dt) - tz.dst(dt)`` must return the same result for every :class:`.datetime` *dt* with ``dt.tzinfo == - tz``. For sane :class:`tzinfo` subclasses, this expression yields the time + tz``. For sane :class:`!tzinfo` subclasses, this expression yields the time zone's "standard offset", which should not depend on the date or the time, but only on geographic location. The implementation of :meth:`datetime.astimezone` relies on this, but cannot detect violations; it's the programmer's - responsibility to ensure it. If a :class:`tzinfo` subclass cannot guarantee + responsibility to ensure it. If a :class:`!tzinfo` subclass cannot guarantee this, it may be able to override the default implementation of :meth:`tzinfo.fromutc` to work correctly with :meth:`~.datetime.astimezone` regardless. Most implementations of :meth:`dst` will probably look like one of these two:: - def dst(self, dt): + import datetime as dt + + def dst(self, when): # a fixed-offset class: doesn't account for DST - return timedelta(0) + return dt.timedelta(0) or:: - def dst(self, dt): + import datetime as dt + + def dst(self, when): # Code to set dston and dstoff to the time zone's DST - # transition times based on the input dt.year, and expressed + # transition times based on the input when.year, and expressed # in standard local time. - if dston <= dt.replace(tzinfo=None) < dstoff: - return timedelta(hours=1) + if dston <= when.replace(tzinfo=None) < dstoff: + return dt.timedelta(hours=1) else: - return timedelta(0) + return dt.timedelta(0) The default implementation of :meth:`dst` raises :exc:`NotImplementedError`. @@ -2217,17 +2258,17 @@ Examples of working with a :class:`.time` object:: valid replies. Return ``None`` if a string name isn't known. Note that this is a method rather than a fixed string primarily because some :class:`tzinfo` subclasses will wish to return different names depending on the specific value - of *dt* passed, especially if the :class:`tzinfo` class is accounting for + of *dt* passed, especially if the :class:`!tzinfo` class is accounting for daylight time. The default implementation of :meth:`tzname` raises :exc:`NotImplementedError`. These methods are called by a :class:`.datetime` or :class:`.time` object, in -response to their methods of the same names. A :class:`.datetime` object passes -itself as the argument, and a :class:`.time` object passes ``None`` as the +response to their methods of the same names. A :class:`!datetime` object passes +itself as the argument, and a :class:`!time` object passes ``None`` as the argument. A :class:`tzinfo` subclass's methods should therefore be prepared to -accept a *dt* argument of ``None``, or of class :class:`.datetime`. +accept a *dt* argument of ``None``, or of class :class:`!datetime`. When ``None`` is passed, it's up to the class designer to decide the best response. For example, returning ``None`` is appropriate if the class wishes to @@ -2235,10 +2276,10 @@ say that time objects don't participate in the :class:`tzinfo` protocols. It may be more useful for ``utcoffset(None)`` to return the standard UTC offset, as there is no other convention for discovering the standard offset. -When a :class:`.datetime` object is passed in response to a :class:`.datetime` +When a :class:`.datetime` object is passed in response to a :class:`!datetime` method, ``dt.tzinfo`` is the same object as *self*. :class:`tzinfo` methods can -rely on this, unless user code calls :class:`tzinfo` methods directly. The -intent is that the :class:`tzinfo` methods interpret *dt* as being in local +rely on this, unless user code calls :class:`!tzinfo` methods directly. The +intent is that the :class:`!tzinfo` methods interpret *dt* as being in local time, and not need worry about objects in other time zones. There is one more :class:`tzinfo` method that a subclass may wish to override: @@ -2266,20 +2307,22 @@ There is one more :class:`tzinfo` method that a subclass may wish to override: Skipping code for error cases, the default :meth:`fromutc` implementation acts like:: - def fromutc(self, dt): - # raise ValueError error if dt.tzinfo is not self - dtoff = dt.utcoffset() - dtdst = dt.dst() + import datetime as dt + + def fromutc(self, when): + # raise ValueError error if when.tzinfo is not self + dtoff = when.utcoffset() + dtdst = when.dst() # raise ValueError if dtoff is None or dtdst is None delta = dtoff - dtdst # this is self's standard offset if delta: - dt += delta # convert to standard local time - dtdst = dt.dst() + when += delta # convert to standard local time + dtdst = when.dst() # raise ValueError if dtdst is None if dtdst: - return dt + dtdst + return when + dtdst else: - return dt + return when In the following :download:`tzinfo_examples.py <../includes/tzinfo_examples.py>` file there are some examples of @@ -2306,9 +2349,9 @@ When DST starts (the "start" line), the local wall clock leaps from 1:59 to ``astimezone(Eastern)`` won't deliver a result with ``hour == 2`` on the day DST begins. For example, at the Spring forward transition of 2016, we get:: - >>> from datetime import datetime, timezone + >>> import datetime as dt >>> from tzinfo_examples import HOUR, Eastern - >>> u0 = datetime(2016, 3, 13, 5, tzinfo=timezone.utc) + >>> u0 = dt.datetime(2016, 3, 13, 5, tzinfo=dt.timezone.utc) >>> for i in range(4): ... u = u0 + i*HOUR ... t = u.astimezone(Eastern) @@ -2331,7 +2374,9 @@ form 5:MM and 6:MM both map to 1:MM when converted to Eastern, but earlier times have the :attr:`~.datetime.fold` attribute set to 0 and the later times have it set to 1. For example, at the Fall back transition of 2016, we get:: - >>> u0 = datetime(2016, 11, 6, 4, tzinfo=timezone.utc) + >>> import datetime as dt + >>> from tzinfo_examples import HOUR, Eastern + >>> u0 = dt.datetime(2016, 11, 6, 4, tzinfo=dt.timezone.utc) >>> for i in range(4): ... u = u0 + i*HOUR ... t = u.astimezone(Eastern) @@ -2348,7 +2393,7 @@ Note that the :class:`.datetime` instances that differ only by the value of the Applications that can't bear wall-time ambiguities should explicitly check the value of the :attr:`~.datetime.fold` attribute or avoid using hybrid :class:`tzinfo` subclasses; there are no ambiguities when using :class:`timezone`, -or any other fixed-offset :class:`tzinfo` subclass (such as a class representing +or any other fixed-offset :class:`!tzinfo` subclass (such as a class representing only EST (fixed offset -5 hours), or only EDT (fixed offset -4 hours)). .. seealso:: @@ -2371,8 +2416,8 @@ only EST (fixed offset -5 hours), or only EDT (fixed offset -4 hours)). .. _datetime-timezone: -:class:`timezone` Objects -------------------------- +:class:`!timezone` objects +-------------------------- The :class:`timezone` class is a subclass of :class:`tzinfo`, each instance of which represents a time zone defined by a fixed offset from @@ -2383,7 +2428,7 @@ locations where different offsets are used in different days of the year or where historical changes have been made to civil time. -.. class:: timezone(offset, name=None) +.. class:: timezone(offset[, name]) The *offset* argument must be specified as a :class:`timedelta` object representing the difference between the local time and UTC. It must @@ -2410,6 +2455,7 @@ where historical changes have been made to civil time. .. versionchanged:: 3.7 The UTC offset is not restricted to a whole number of minutes. + .. method:: timezone.tzname(dt) Return the fixed value specified when the :class:`timezone` instance @@ -2430,11 +2476,13 @@ where historical changes have been made to civil time. Always returns ``None``. + .. method:: timezone.fromutc(dt) Return ``dt + offset``. The *dt* argument must be an aware :class:`.datetime` instance, with ``tzinfo`` set to ``self``. + Class attributes: .. attribute:: timezone.utc @@ -2447,8 +2495,8 @@ Class attributes: .. _strftime-strptime-behavior: -:meth:`~.datetime.strftime` and :meth:`~.datetime.strptime` Behavior --------------------------------------------------------------------- +:meth:`!strftime` and :meth:`!strptime` behavior +------------------------------------------------ :class:`date`, :class:`.datetime`, and :class:`.time` objects all support a ``strftime(format)`` method, to create a string representing the time under the @@ -2474,90 +2522,120 @@ versus :meth:`~.datetime.strptime`: .. _format-codes: -:meth:`~.datetime.strftime` and :meth:`~.datetime.strptime` Format Codes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:meth:`!strftime` and :meth:`!strptime` format codes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ These methods accept format codes that can be used to parse and format dates:: - >>> datetime.strptime('31/01/22 23:59:59.999999', - ... '%d/%m/%y %H:%M:%S.%f') + >>> import datetime as dt + >>> dt.datetime.strptime('31/01/22 23:59:59.999999', + ... '%d/%m/%y %H:%M:%S.%f') datetime.datetime(2022, 1, 31, 23, 59, 59, 999999) >>> _.strftime('%a %d %b %Y, %I:%M%p') 'Mon 31 Jan 2022, 11:59PM' -The following is a list of all the format codes that the 1989 C standard -requires, and these work on all platforms with a standard C implementation. +The following is a list of all the format codes that the 2011 C standard +requires, and these work on all supported platforms. +-----------+--------------------------------+------------------------+-------+ -| Directive | Meaning | Example | Notes | +| Directive | Meaning | Example | Notes | +| | | | | +===========+================================+========================+=======+ -| ``%a`` | Weekday as locale's || Sun, Mon, ..., Sat | \(1) | +| ``%a`` | Weekday as locale's || Sun, Mon, ..., Sat | \(1) | | | abbreviated name. | (en_US); | | | | || So, Mo, ..., Sa | | | | | (de_DE) | | +-----------+--------------------------------+------------------------+-------+ -| ``%A`` | Weekday as locale's full name. || Sunday, Monday, ..., | \(1) | +| ``%A`` | Weekday as locale's full name. || Sunday, Monday, ..., | \(1) | | | | Saturday (en_US); | | | | || Sonntag, Montag, ..., | | | | | Samstag (de_DE) | | +-----------+--------------------------------+------------------------+-------+ -| ``%w`` | Weekday as a decimal number, | 0, 1, ..., 6 | | -| | where 0 is Sunday and 6 is | | | -| | Saturday. | | | -+-----------+--------------------------------+------------------------+-------+ -| ``%d`` | Day of the month as a | 01, 02, ..., 31 | \(9) | -| | zero-padded decimal number. | | | -+-----------+--------------------------------+------------------------+-------+ -| ``%b`` | Month as locale's abbreviated || Jan, Feb, ..., Dec | \(1) | +| ``%b`` | Month as locale's abbreviated || Jan, Feb, ..., Dec | \(1) | | | name. | (en_US); | | | | || Jan, Feb, ..., Dez | | | | | (de_DE) | | +-----------+--------------------------------+------------------------+-------+ -| ``%B`` | Month as locale's full name. || January, February, | \(1) | +| ``%B`` | Month as locale's full name. || January, February, | \(1) | | | | ..., December (en_US);| | | | || Januar, Februar, ..., | | | | | Dezember (de_DE) | | +-----------+--------------------------------+------------------------+-------+ -| ``%m`` | Month as a zero-padded | 01, 02, ..., 12 | \(9) | -| | decimal number. | | | +| ``%c`` | Locale's appropriate date and || Tue Aug 16 21:30:00 | \(1) | +| | time representation. | 1988 (en_US); | | +| | || Di 16 Aug 21:30:00 | | +| | | 1988 (de_DE) | | +-----------+--------------------------------+------------------------+-------+ -| ``%y`` | Year without century as a | 00, 01, ..., 99 | \(9) | +| ``%C`` | The year divided by 100 and | 01, 02, ..., 99 | \(0) | +| | truncated to an integer as a | | | | | zero-padded decimal number. | | | +-----------+--------------------------------+------------------------+-------+ -| ``%Y`` | Year with century as a decimal | 0001, 0002, ..., 2013, | \(2) | -| | number. | 2014, ..., 9998, 9999 | | +| ``%d`` | Day of the month as a | 01, 02, ..., 31 | \(9), | +| | zero-padded decimal number. | | \(10) | ++-----------+--------------------------------+------------------------+-------+ +| ``%D`` | Equivalent to ``%m/%d/%y``. | 11/28/25 | \(9) | +| | | | | ++-----------+--------------------------------+------------------------+-------+ +| ``%e`` | The day of the month as a | ␣1, ␣2, ..., 31 | \(10) | +| | space-padded decimal number. | | | ++-----------+--------------------------------+------------------------+-------+ +| ``%F`` | Equivalent to ``%Y-%m-%d``, | 2025-10-11, | | +| | the ISO 8601 format. | 1001-12-30 | | ++-----------+--------------------------------+------------------------+-------+ +| ``%g`` | Last 2 digits of ISO 8601 year | 00, 01, ..., 99 | \(0) | +| | representing the year that | | | +| | contains the greater part of | | | +| | the ISO week (``%V``). | | | ++-----------+--------------------------------+------------------------+-------+ +| ``%G`` | ISO 8601 year with century | 0001, 0002, ..., 2013, | \(8) | +| | representing the year that | 2014, ..., 9998, 9999 | | +| | contains the greater part of | | | +| | the ISO week (``%V``). | | | +-----------+--------------------------------+------------------------+-------+ -| ``%H`` | Hour (24-hour clock) as a | 00, 01, ..., 23 | \(9) | +| ``%h`` | Equivalent to ``%b``. | See ``%b``. | \(0) | ++-----------+--------------------------------+------------------------+-------+ +| ``%H`` | Hour (24-hour clock) as a | 00, 01, ..., 23 | \(9) | | | zero-padded decimal number. | | | +-----------+--------------------------------+------------------------+-------+ -| ``%I`` | Hour (12-hour clock) as a | 01, 02, ..., 12 | \(9) | +| ``%I`` | Hour (12-hour clock) as a | 01, 02, ..., 12 | \(9) | | | zero-padded decimal number. | | | +-----------+--------------------------------+------------------------+-------+ -| ``%p`` | Locale's equivalent of either || AM, PM (en_US); | \(1), | -| | AM or PM. || am, pm (de_DE) | \(3) | +| ``%j`` | Day of the year as a | 001, 002, ..., 366 | \(9) | +| | zero-padded decimal number. | | | +-----------+--------------------------------+------------------------+-------+ -| ``%M`` | Minute as a zero-padded | 00, 01, ..., 59 | \(9) | +| ``%m`` | Month as a zero-padded | 01, 02, ..., 12 | \(9) | | | decimal number. | | | +-----------+--------------------------------+------------------------+-------+ -| ``%S`` | Second as a zero-padded | 00, 01, ..., 59 | \(4), | -| | decimal number. | | \(9) | +| ``%M`` | Minute as a zero-padded | 00, 01, ..., 59 | \(9) | +| | decimal number. | | | +-----------+--------------------------------+------------------------+-------+ -| ``%f`` | Microsecond as a decimal | 000000, 000001, ..., | \(5) | -| | number, zero-padded to 6 | 999999 | | -| | digits. | | | +| ``%n`` | The newline character | ``\n`` | | +| | (``'\n'``). For | | | +| | :meth:`!strptime`, zero or | | | +| | more whitespace. | | | +-----------+--------------------------------+------------------------+-------+ -| ``%z`` | UTC offset in the form | (empty), +0000, | \(6) | -| | ``±HHMM[SS[.ffffff]]`` (empty | -0400, +1030, | | -| | string if the object is | +063415, | | -| | naive). | -030712.345216 | | +| ``%p`` | Locale's equivalent of either || AM, PM (en_US); | \(1), | +| | AM or PM. || am, pm (de_DE) | \(3) | +-----------+--------------------------------+------------------------+-------+ -| ``%Z`` | Time zone name (empty string | (empty), UTC, GMT | \(6) | -| | if the object is naive). | | | +| ``%r`` | Locale's 12-hour clock time. | 12:00:00 AM | \(1), | +| | | | \(0) | +-----------+--------------------------------+------------------------+-------+ -| ``%j`` | Day of the year as a | 001, 002, ..., 366 | \(9) | -| | zero-padded decimal number. | | | +| ``%R`` | Equivalent to ``%H:%M``. | 10:01 | | ++-----------+--------------------------------+------------------------+-------+ +| ``%S`` | Second as a zero-padded | 00, 01, ..., 59 | \(4), | +| | decimal number. | | \(9) | ++-----------+--------------------------------+------------------------+-------+ +| ``%t`` | The tab character (``'\t'``). | ``\t`` | | +| | For :meth:`!strptime`, | | | +| | zero or more whitespace. | | | ++-----------+--------------------------------+------------------------+-------+ +| ``%T`` | ISO 8601 time format, | 10:01:59 | | +| | equivalent to ``%H:%M:%S``. | | | +-----------+--------------------------------+------------------------+-------+ -| ``%U`` | Week number of the year | 00, 01, ..., 53 | \(7), | +| ``%u`` | ISO 8601 weekday as a decimal | 1, 2, ..., 7 | | +| | number where 1 is Monday. | | | ++-----------+--------------------------------+------------------------+-------+ +| ``%U`` | Week number of the year | 00, 01, ..., 53 | \(7), | | | (Sunday as the first day of | | \(9) | | | the week) as a zero-padded | | | | | decimal number. All days in a | | | @@ -2565,7 +2643,17 @@ requires, and these work on all platforms with a standard C implementation. | | Sunday are considered to be in | | | | | week 0. | | | +-----------+--------------------------------+------------------------+-------+ -| ``%W`` | Week number of the year | 00, 01, ..., 53 | \(7), | +| ``%V`` | ISO 8601 week as a decimal | 01, 02, ..., 53 | \(8), | +| | number with Monday as | | \(9) | +| | the first day of the week. | | | +| | Week 01 is the week containing | | | +| | Jan 4. | | | ++-----------+--------------------------------+------------------------+-------+ +| ``%w`` | Weekday as a decimal number, | 0, 1, ..., 6 | | +| | where 0 is Sunday and 6 is | | | +| | Saturday. | | | ++-----------+--------------------------------+------------------------+-------+ +| ``%W`` | Week number of the year | 00, 01, ..., 53 | \(7), | | | (Monday as the first day of | | \(9) | | | the week) as a zero-padded | | | | | decimal number. All days in a | | | @@ -2573,40 +2661,43 @@ requires, and these work on all platforms with a standard C implementation. | | Monday are considered to be in | | | | | week 0. | | | +-----------+--------------------------------+------------------------+-------+ -| ``%c`` | Locale's appropriate date and || Tue Aug 16 21:30:00 | \(1) | -| | time representation. | 1988 (en_US); | | -| | || Di 16 Aug 21:30:00 | | -| | | 1988 (de_DE) | | -+-----------+--------------------------------+------------------------+-------+ -| ``%x`` | Locale's appropriate date || 08/16/88 (None); | \(1) | +| ``%x`` | Locale's appropriate date || 08/16/88 (None); | \(1) | | | representation. || 08/16/1988 (en_US); | | | | || 16.08.1988 (de_DE) | | +-----------+--------------------------------+------------------------+-------+ -| ``%X`` | Locale's appropriate time || 21:30:00 (en_US); | \(1) | +| ``%X`` | Locale's appropriate time || 21:30:00 (en_US); | \(1) | | | representation. || 21:30:00 (de_DE) | | +-----------+--------------------------------+------------------------+-------+ -| ``%%`` | A literal ``'%'`` character. | % | | +| ``%y`` | Year without century as a | 00, 01, ..., 99 | \(9) | +| | zero-padded decimal number. | | | ++-----------+--------------------------------+------------------------+-------+ +| ``%Y`` | Year with century as a decimal | 0001, 0002, ..., 2013, | \(2) | +| | number. | 2014, ..., 9998, 9999 | | ++-----------+--------------------------------+------------------------+-------+ +| ``%z`` | UTC offset in the form | (empty), +0000, | \(6) | +| | ``±HHMM[SS[.ffffff]]`` (empty | -0400, +1030, | | +| | string if the object is | +063415, | | +| | naive). | -030712.345216 | | +-----------+--------------------------------+------------------------+-------+ +| ``%Z`` | Time zone name (empty string | (empty), UTC, GMT | \(6) | +| | if the object is naive). | | | ++-----------+--------------------------------+------------------------+-------+ +| ``%%`` | A literal ``'%'`` character. | % | | ++-----------+--------------------------------+------------------------+-------+ + +The ISO 8601 year and ISO 8601 week directives are not interchangeable +with the year and week number directives above. Calling :meth:`~.datetime.strptime` with +incomplete or ambiguous ISO 8601 directives will raise a :exc:`ValueError`. -Several additional directives not required by the C89 standard are included for -convenience. These parameters all correspond to ISO 8601 date values. +Several additional directives not required by the C11 standard are included for +convenience. +-----------+--------------------------------+------------------------+-------+ | Directive | Meaning | Example | Notes | +===========+================================+========================+=======+ -| ``%G`` | ISO 8601 year with century | 0001, 0002, ..., 2013, | \(8) | -| | representing the year that | 2014, ..., 9998, 9999 | | -| | contains the greater part of | | | -| | the ISO week (``%V``). | | | -+-----------+--------------------------------+------------------------+-------+ -| ``%u`` | ISO 8601 weekday as a decimal | 1, 2, ..., 7 | | -| | number where 1 is Monday. | | | -+-----------+--------------------------------+------------------------+-------+ -| ``%V`` | ISO 8601 week as a decimal | 01, 02, ..., 53 | \(8), | -| | number with Monday as | | \(9) | -| | the first day of the week. | | | -| | Week 01 is the week containing | | | -| | Jan 4. | | | +| ``%f`` | Microsecond as a decimal | 000000, 000001, ..., | \(5) | +| | number, zero-padded to 6 | 999999 | | +| | digits. | | | +-----------+--------------------------------+------------------------+-------+ | ``%:z`` | UTC offset in the form | (empty), +00:00, | \(6) | | | ``±HH:MM[:SS[.ffffff]]`` | -04:00, +10:30, | | @@ -2614,11 +2705,6 @@ convenience. These parameters all correspond to ISO 8601 date values. | | naive). | -03:07:12.345216 | | +-----------+--------------------------------+------------------------+-------+ -These may not be available on all platforms when used with the :meth:`~.datetime.strftime` -method. The ISO 8601 year and ISO 8601 week directives are not interchangeable -with the year and week number directives above. Calling :meth:`~.datetime.strptime` with -incomplete or ambiguous ISO 8601 directives will raise a :exc:`ValueError`. - The full set of format codes supported varies across platforms, because Python calls the platform C library's :c:func:`strftime` function, and platform variations are common. To see the full set of format codes supported on your @@ -2629,18 +2715,61 @@ differences between platforms in handling of unsupported format specifiers. ``%G``, ``%u`` and ``%V`` were added. .. versionadded:: 3.12 - ``%:z`` was added. + ``%:z`` was added for :meth:`~.datetime.strftime`. + +.. versionadded:: 3.15 + ``%D``, ``%F``, ``%n``, ``%t``, and ``%:z`` were added for + :meth:`~.datetime.strptime`. -Technical Detail + +Technical detail ^^^^^^^^^^^^^^^^ Broadly speaking, ``d.strftime(fmt)`` acts like the :mod:`time` module's ``time.strftime(fmt, d.timetuple())`` although not all objects support a :meth:`~date.timetuple` method. -For the :meth:`.datetime.strptime` class method, the default value is -``1900-01-01T00:00:00.000``: any components not specified in the format string -will be pulled from the default value. [#]_ +For the :meth:`.datetime.strptime` and :meth:`.date.strptime` class methods, +the default value is ``1900-01-01T00:00:00.000``: any components not specified +in the format string will be pulled from the default value. + +.. note:: + Format strings without separators can be ambiguous for parsing. For + example, with ``%Y%m%d``, the string ``2026111`` may be parsed either as + ``2026-11-01`` or as ``2026-01-11``. + Use separators to ensure the input is parsed as intended. + +.. note:: + When used to parse partial dates lacking a year, :meth:`.datetime.strptime` + and :meth:`.date.strptime` will raise when encountering February 29 because + the default year of 1900 is *not* a leap year. Always add a default leap + year to partial date strings before parsing. + +.. testsetup:: + + # doctest seems to turn the warning into an error which makes it + # show up and require matching and prevents the actual interesting + # exception from being raised. + # Manually apply the catch_warnings context manager + import warnings + catch_warnings = warnings.catch_warnings() + catch_warnings.__enter__() + warnings.simplefilter("ignore") + +.. testcleanup:: + + catch_warnings.__exit__() + +.. doctest:: + + >>> import datetime as dt + >>> value = "2/29" + >>> dt.datetime.strptime(value, "%m/%d") + Traceback (most recent call last): + ... + ValueError: day 29 must be in range 1..28 for month 2 in year 1900 + >>> dt.datetime.strptime(f"1904 {value}", "%Y %m/%d") + datetime.datetime(1904, 2, 29, 0, 0) Using ``datetime.strptime(date_string, format)`` is equivalent to:: @@ -2666,6 +2795,9 @@ an empty string instead. Notes: +(0) + This format code is currently unsupported by :meth:`~.datetime.strptime`. + (1) Because the format depends on the current locale, care should be taken when making assumptions about the output value. Field orderings will vary (for @@ -2724,12 +2856,18 @@ Notes: When the ``%z`` directive is provided to the :meth:`~.datetime.strptime` method, the UTC offsets can have a colon as a separator between hours, minutes and seconds. - For example, ``'+01:00:00'`` will be parsed as an offset of one hour. - In addition, providing ``'Z'`` is identical to ``'+00:00'``. + For example, both ``'+010000'`` and ``'+01:00:00'`` will be parsed as an offset + of one hour. In addition, providing ``'Z'`` is identical to ``'+00:00'``. ``%:z`` - Behaves exactly as ``%z``, but has a colon separator added between - hours, minutes and seconds. + When used with :meth:`~.datetime.strftime`, behaves exactly as ``%z``, + except that a colon separator is added between hours, minutes and seconds. + + When used with :meth:`~.datetime.strptime`, the UTC offset is *required* + to have a colon as a separator between hours, minutes and seconds. + For example, ``'+01:00:00'`` (but *not* ``'+010000'``) will be parsed as + an offset of one hour. In addition, providing ``'Z'`` is identical to + ``'+00:00'``. ``%Z`` In :meth:`~.datetime.strftime`, ``%Z`` is replaced by an empty string if @@ -2771,23 +2909,24 @@ Notes: include a year in the format. If the value you need to parse lacks a year, append an explicit dummy leap year. Otherwise your code will raise an exception when it encounters leap day because the default year used by the - parser is not a leap year. Users run into this bug every four years... + parser (1900) is not a leap year. Users run into that bug every leap year. .. doctest:: >>> month_day = "02/29" - >>> datetime.strptime(f"{month_day};1984", "%m/%d;%Y") # No leap year bug. + >>> dt.datetime.strptime(f"{month_day};1984", "%m/%d;%Y") # No leap year bug. datetime.datetime(1984, 2, 29, 0, 0) - .. deprecated-removed:: 3.13 3.15 + .. versionchanged:: 3.15 + Using ``%d`` without a year now raises :exc:`ValueError`. + + .. deprecated-removed:: 3.15 3.17 :meth:`~.datetime.strptime` calls using a format string containing - a day of month without a year now emit a - :exc:`DeprecationWarning`. In 3.15 or later we may change this into - an error or change the default year to a leap year. See :gh:`70647`. + ``%e`` without a year now emit a :exc:`DeprecationWarning`. .. rubric:: Footnotes -.. [#] If, that is, we ignore the effects of Relativity +.. [#] If, that is, we ignore the effects of relativity. .. [#] This matches the definition of the "proleptic Gregorian" calendar in Dershowitz and Reingold's book *Calendrical Calculations*, @@ -2798,5 +2937,3 @@ Notes: .. [#] See R. H. van Gent's `guide to the mathematics of the ISO 8601 calendar `_ for a good explanation. - -.. [#] Passing ``datetime.strptime('Feb 29', '%b %d')`` will fail since 1900 is not a leap year. diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index 39e287b15214e4..646981e8692cc5 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -8,7 +8,7 @@ -------------- -:mod:`dbm` is a generic interface to variants of the DBM database: +:mod:`!dbm` is a generic interface to variants of the DBM database: * :mod:`dbm.sqlite3` * :mod:`dbm.gnu` @@ -90,10 +90,13 @@ the Oracle Berkeley DB. .. versionchanged:: 3.11 *file* accepts a :term:`path-like object`. -The object returned by :func:`~dbm.open` supports the same basic functionality as a -:class:`dict`; keys and their corresponding values can be stored, retrieved, and -deleted, and the :keyword:`in` operator and the :meth:`!keys` method are -available, as well as :meth:`!get` and :meth:`!setdefault` methods. +The object returned by :func:`~dbm.open` supports the basic +functionality of mutable :term:`mappings `; +keys and their corresponding values can be stored, retrieved, and +deleted, and iteration, the :keyword:`in` operator and methods :meth:`!keys`, +:meth:`!get`, :meth:`!setdefault` and :meth:`!clear` are available. +The :meth:`!keys` method returns a list instead of a view object. +The :meth:`!setdefault` method requires two arguments. Key and values are always stored as :class:`bytes`. This means that when strings are used they are implicitly converted to the default encoding before @@ -104,7 +107,7 @@ will automatically close them when done. .. versionchanged:: 3.2 :meth:`!get` and :meth:`!setdefault` methods are now available for all - :mod:`dbm` backends. + :mod:`!dbm` backends. .. versionchanged:: 3.4 Added native support for the context management protocol to the objects @@ -114,6 +117,10 @@ will automatically close them when done. Deleting a key from a read-only database raises a database module specific exception instead of :exc:`KeyError`. +.. versionchanged:: 3.13 + :meth:`!clear` methods are now available for all :mod:`!dbm` backends. + + The following example records some hostnames and a corresponding title, and then prints out the contents of the database:: @@ -150,11 +157,10 @@ then prints out the contents of the database:: The individual submodules are described in the following sections. -:mod:`dbm.sqlite3` --- SQLite backend for dbm ---------------------------------------------- +:mod:`!dbm.sqlite3` --- SQLite backend for dbm +---------------------------------------------- .. module:: dbm.sqlite3 - :platform: All :synopsis: SQLite backend for dbm .. versionadded:: 3.13 @@ -164,8 +170,8 @@ The individual submodules are described in the following sections. -------------- This module uses the standard library :mod:`sqlite3` module to provide an -SQLite backend for the :mod:`dbm` module. -The files created by :mod:`dbm.sqlite3` can thus be opened by :mod:`sqlite3`, +SQLite backend for the :mod:`!dbm` module. +The files created by :mod:`!dbm.sqlite3` can thus be opened by :mod:`sqlite3`, or any other SQLite browser, including the SQLite CLI. .. include:: ../includes/wasm-notavail.rst @@ -173,9 +179,6 @@ or any other SQLite browser, including the SQLite CLI. .. function:: open(filename, /, flag="r", mode=0o666) Open an SQLite database. - The returned object behaves like a :term:`mapping`, - implements a :meth:`!close` method, - and supports a "closing" context manager via the :keyword:`with` keyword. :param filename: The path to the database to be opened. @@ -192,6 +195,17 @@ or any other SQLite browser, including the SQLite CLI. The Unix file access mode of the file (default: octal ``0o666``), used only when the database has to be created. + The returned database object behaves similar to a mutable :term:`mapping`, + but the :meth:`!keys` method returns a list, and + the :meth:`!setdefault` method requires two arguments. + It also supports a "closing" context manager via the :keyword:`with` keyword. + + The following methods are also provided: + + .. method:: sqlite3.close() + + Close the SQLite database. + .. method:: sqlite3.reorganize() If you have carried out a lot of deletions and would like to shrink the space @@ -200,38 +214,45 @@ or any other SQLite browser, including the SQLite CLI. .. note:: While reorganizing, as much as two times the size of the original database is required - in free disk space. However, be aware that this factor changes for each :mod:`dbm` submodule. + in free disk space. However, be aware that this factor changes for each :mod:`!dbm` submodule. + + .. versionadded:: 3.15 - .. versionadded:: next -:mod:`dbm.gnu` --- GNU database manager ---------------------------------------- +:mod:`!dbm.gnu` --- GNU database manager +---------------------------------------- .. module:: dbm.gnu - :platform: Unix :synopsis: GNU database manager **Source code:** :source:`Lib/dbm/gnu.py` -------------- -The :mod:`dbm.gnu` module provides an interface to the :abbr:`GDBM (GNU dbm)` +The :mod:`!dbm.gnu` module provides an interface to the :abbr:`GDBM (GNU dbm)` library, similar to the :mod:`dbm.ndbm` module, but with additional functionality like crash tolerance. .. note:: - The file formats created by :mod:`dbm.gnu` and :mod:`dbm.ndbm` are incompatible + The file formats created by :mod:`!dbm.gnu` and :mod:`dbm.ndbm` are incompatible and can not be used interchangeably. .. include:: ../includes/wasm-mobile-notavail.rst +.. availability:: Unix. + .. exception:: error - Raised on :mod:`dbm.gnu`-specific errors, such as I/O errors. :exc:`KeyError` is + Raised on :mod:`!dbm.gnu`-specific errors, such as I/O errors. :exc:`KeyError` is raised for general mapping errors like specifying an incorrect key. +.. data:: open_flags + + A string of characters the *flag* parameter of :meth:`~dbm.gnu.open` supports. + + .. function:: open(filename, flag="r", mode=0o666, /) Open a GDBM database and return a :class:`!gdbm` object. @@ -254,9 +275,6 @@ functionality like crash tolerance. * ``'s'``: Synchronized mode. Changes to the database will be written immediately to the file. * ``'u'``: Do not lock database. - * ``'m'``: Do not use :manpage:`mmap(2)`. - This may harm performance, but improve crash tolerance. - .. versionadded:: next Not all flags are valid for all versions of GDBM. See the :data:`open_flags` member for a list of supported flag characters. @@ -270,14 +288,25 @@ functionality like crash tolerance. .. versionchanged:: 3.11 *filename* accepts a :term:`path-like object`. - .. data:: open_flags + :class:`!gdbm` objects behave similar to mutable :term:`mappings `, + but methods :meth:`!items`, :meth:`!values`, :meth:`!pop`, :meth:`!popitem`, + and :meth:`!update` are not supported, + the :meth:`!keys` method returns a list, and + the :meth:`!setdefault` method requires two arguments. + It also supports a "closing" context manager via the :keyword:`with` keyword. + + .. versionchanged:: 3.2 + Added the :meth:`!get` and :meth:`!setdefault` methods. - A string of characters the *flag* parameter of :meth:`~dbm.gnu.open` supports. + .. versionchanged:: 3.13 + Added the :meth:`!clear` method. - :class:`!gdbm` objects behave similar to :term:`mappings `, - but :meth:`!items` and :meth:`!values` methods are not supported. The following methods are also provided: + .. method:: gdbm.close() + + Close the GDBM database. + .. method:: gdbm.firstkey() It's possible to loop over every key in the database using this method and the @@ -306,43 +335,32 @@ functionality like crash tolerance. .. note:: While reorganizing, as much as one time the size of the original database is required - in free disk space. However, be aware that this factor changes for each :mod:`dbm` submodule. + in free disk space. However, be aware that this factor changes for each :mod:`!dbm` submodule. .. method:: gdbm.sync() When the database has been opened in fast mode, this method forces any unwritten data to be written to the disk. - .. method:: gdbm.close() - - Close the GDBM database. - - .. method:: gdbm.clear() - Remove all items from the GDBM database. - - .. versionadded:: 3.13 - - -:mod:`dbm.ndbm` --- New Database Manager ----------------------------------------- +:mod:`!dbm.ndbm` --- New Database Manager +----------------------------------------- .. module:: dbm.ndbm - :platform: Unix :synopsis: The New Database Manager **Source code:** :source:`Lib/dbm/ndbm.py` -------------- -The :mod:`dbm.ndbm` module provides an interface to the +The :mod:`!dbm.ndbm` module provides an interface to the :abbr:`NDBM (New Database Manager)` library. This module can be used with the "classic" NDBM interface or the :abbr:`GDBM (GNU dbm)` compatibility interface. .. note:: - The file formats created by :mod:`dbm.gnu` and :mod:`dbm.ndbm` are incompatible + The file formats created by :mod:`dbm.gnu` and :mod:`!dbm.ndbm` are incompatible and can not be used interchangeably. .. warning:: @@ -354,9 +372,11 @@ This module can be used with the "classic" NDBM interface or the .. include:: ../includes/wasm-mobile-notavail.rst +.. availability:: Unix. + .. exception:: error - Raised on :mod:`dbm.ndbm`-specific errors, such as I/O errors. :exc:`KeyError` is raised + Raised on :mod:`!dbm.ndbm`-specific errors, such as I/O errors. :exc:`KeyError` is raised for general mapping errors like specifying an incorrect key. @@ -383,26 +403,31 @@ This module can be used with the "classic" NDBM interface or the :param int mode: |mode_param_doc| - :class:`!ndbm` objects behave similar to :term:`mappings `, - but :meth:`!items` and :meth:`!values` methods are not supported. - The following methods are also provided: - .. versionchanged:: 3.11 Accepts :term:`path-like object` for filename. - .. method:: ndbm.close() + :class:`!ndbm` objects behave similar to mutable :term:`mappings `, + but methods :meth:`!items`, :meth:`!values`, :meth:`!pop`, :meth:`!popitem`, + and :meth:`!update` are not supported, + the :meth:`!keys` method returns a list, and + the :meth:`!setdefault` method requires two arguments. + It also supports a "closing" context manager via the :keyword:`with` keyword. - Close the NDBM database. + .. versionchanged:: 3.2 + Added the :meth:`!get` and :meth:`!setdefault` methods. - .. method:: ndbm.clear() + .. versionchanged:: 3.13 + Added the :meth:`!clear` method. - Remove all items from the NDBM database. + The following method is also provided: + + .. method:: ndbm.close() - .. versionadded:: 3.13 + Close the NDBM database. -:mod:`dbm.dumb` --- Portable DBM implementation ------------------------------------------------ +:mod:`!dbm.dumb` --- Portable DBM implementation +------------------------------------------------ .. module:: dbm.dumb :synopsis: Portable implementation of the simple DBM interface. @@ -413,32 +438,29 @@ This module can be used with the "classic" NDBM interface or the .. note:: - The :mod:`dbm.dumb` module is intended as a last resort fallback for the - :mod:`dbm` module when a more robust module is not available. The :mod:`dbm.dumb` + The :mod:`!dbm.dumb` module is intended as a last resort fallback for the + :mod:`!dbm` module when a more robust module is not available. The :mod:`!dbm.dumb` module is not written for speed and is not nearly as heavily used as the other database modules. -------------- -The :mod:`dbm.dumb` module provides a persistent :class:`dict`-like +The :mod:`!dbm.dumb` module provides a persistent :class:`dict`-like interface which is written entirely in Python. -Unlike other :mod:`dbm` backends, such as :mod:`dbm.gnu`, no +Unlike other :mod:`!dbm` backends, such as :mod:`dbm.gnu`, no external library is required. The :mod:`!dbm.dumb` module defines the following: .. exception:: error - Raised on :mod:`dbm.dumb`-specific errors, such as I/O errors. :exc:`KeyError` is + Raised on :mod:`!dbm.dumb`-specific errors, such as I/O errors. :exc:`KeyError` is raised for general mapping errors like specifying an incorrect key. .. function:: open(filename, flag="c", mode=0o666) Open a :mod:`!dbm.dumb` database. - The returned database object behaves similar to a :term:`mapping`, - in addition to providing :meth:`~dumbdbm.sync` and :meth:`~dumbdbm.close` - methods. :param filename: The basename of the database file (without extensions). @@ -463,7 +485,7 @@ The :mod:`!dbm.dumb` module defines the following: Python's AST compiler. .. warning:: - :mod:`dbm.dumb` does not support concurrent read/write access. (Multiple + :mod:`!dbm.dumb` does not support concurrent read/write access. (Multiple simultaneous read accesses are safe.) When a program has the database open for writing, no other program should have it open for reading or writing. @@ -477,14 +499,12 @@ The :mod:`!dbm.dumb` module defines the following: .. versionchanged:: 3.11 *filename* accepts a :term:`path-like object`. - In addition to the methods provided by the - :class:`collections.abc.MutableMapping` class, - the following methods are provided: - - .. method:: dumbdbm.sync() + The returned database object behaves similar to a mutable :term:`mapping`, + but the :meth:`!keys` and :meth:`!items` methods return lists, and + the :meth:`!setdefault` method requires two arguments. + It also supports a "closing" context manager via the :keyword:`with` keyword. - Synchronize the on-disk directory and data files. This method is called - by the :meth:`shelve.Shelf.sync` method. + The following methods are also provided: .. method:: dumbdbm.close() @@ -498,6 +518,11 @@ The :mod:`!dbm.dumb` module defines the following: .. note:: While reorganizing, no additional free disk space is required. However, be aware - that this factor changes for each :mod:`dbm` submodule. + that this factor changes for each :mod:`!dbm` submodule. - .. versionadded:: next + .. versionadded:: 3.15 + + .. method:: dumbdbm.sync() + + Synchronize the on-disk directory and data files. This method is called + by the :meth:`shelve.Shelf.sync` method. diff --git a/Doc/library/debug.rst b/Doc/library/debug.rst index 60223657a44043..f87c2481fb89cd 100644 --- a/Doc/library/debug.rst +++ b/Doc/library/debug.rst @@ -1,5 +1,5 @@ *********************** -Debugging and Profiling +Debugging and profiling *********************** These libraries help you with Python development: the debugger enables you to @@ -15,7 +15,8 @@ intrusive debugging or patching. bdb.rst faulthandler.rst pdb.rst - profile.rst + profiling.rst + pstats.rst timeit.rst trace.rst tracemalloc.rst diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 10ddfa02b43156..2af5dfce9612b3 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -4,14 +4,6 @@ .. module:: decimal :synopsis: Implementation of the General Decimal Arithmetic Specification. -.. moduleauthor:: Eric Price -.. moduleauthor:: Facundo Batista -.. moduleauthor:: Raymond Hettinger -.. moduleauthor:: Aahz -.. moduleauthor:: Tim Peters -.. moduleauthor:: Stefan Krah -.. sectionauthor:: Raymond D. Hettinger - **Source code:** :source:`Lib/decimal.py` .. import modules for testing inline doctests with the Sphinx doctest builder @@ -30,14 +22,16 @@ -------------- -The :mod:`decimal` module provides support for fast correctly rounded +The :mod:`!decimal` module provides support for fast correctly rounded decimal floating-point arithmetic. It offers several advantages over the :class:`float` datatype: -* Decimal "is based on a floating-point model which was designed with people - in mind, and necessarily has a paramount guiding principle -- computers must - provide an arithmetic that works in the same way as the arithmetic that - people learn at school." -- excerpt from the decimal arithmetic specification. +* Decimal "is based on a `floating-point model + `__ which was designed + with people in mind, and necessarily has a paramount guiding principle -- + computers must provide an arithmetic that works in the same way as the + arithmetic that people learn at school." -- excerpt from the decimal + arithmetic specification. * Decimal numbers can be represented exactly. In contrast, numbers like ``1.1`` and ``2.2`` do not have exact representations in binary @@ -238,6 +232,26 @@ floating-point flying circus: >>> c % a Decimal('0.77') +Decimals can be formatted (with :func:`format` built-in or :ref:`f-strings`) in +fixed-point or scientific notation, using the same formatting syntax (see +:ref:`formatspec`) as builtin :class:`float` type: + +.. doctest:: + + >>> format(Decimal('2.675'), "f") + '2.675' + >>> format(Decimal('2.675'), ".2f") + '2.68' + >>> f"{Decimal('2.675'):.2f}" + '2.68' + >>> format(Decimal('2.675'), ".2e") + '2.68e+0' + >>> with localcontext() as ctx: + ... ctx.rounding = ROUND_DOWN + ... print(format(Decimal('2.675'), ".2f")) + ... + 2.67 + And some mathematical functions are also available to Decimal: >>> getcontext().prec = 28 @@ -264,10 +278,10 @@ allows the settings to be changed. This approach meets the needs of most applications. For more advanced work, it may be useful to create alternate contexts using the -Context() constructor. To make an alternate active, use the :func:`setcontext` +:meth:`Context` constructor. To make an alternate active, use the :func:`setcontext` function. -In accordance with the standard, the :mod:`decimal` module provides two ready to +In accordance with the standard, the :mod:`!decimal` module provides two ready to use standard contexts, :const:`BasicContext` and :const:`ExtendedContext`. The former is especially useful for debugging because many of the traps are enabled: @@ -573,7 +587,7 @@ Decimal objects >>> Decimal(321).exp() Decimal('2.561702493119680037517373933E+139') - .. classmethod:: from_float(f) + .. classmethod:: from_float(f, /) Alternative constructor that only accepts instances of :class:`float` or :class:`int`. @@ -600,7 +614,7 @@ Decimal objects .. versionadded:: 3.1 - .. classmethod:: from_number(number) + .. classmethod:: from_number(number, /) Alternative constructor that only accepts instances of :class:`float`, :class:`int` or :class:`Decimal`, but not strings @@ -991,7 +1005,7 @@ Each thread has its own current context which is accessed or changed using the Return the current context for the active thread. -.. function:: setcontext(c) +.. function:: setcontext(c, /) Set the current context for the active thread to *c*. @@ -1168,11 +1182,11 @@ In addition to the three supplied contexts, new contexts can be created with the Return a duplicate of the context. - .. method:: copy_decimal(num) + .. method:: copy_decimal(num, /) Return a copy of the Decimal instance num. - .. method:: create_decimal(num) + .. method:: create_decimal(num='0', /) Creates a new Decimal instance from *num* but using *self* as context. Unlike the :class:`Decimal` constructor, the context precision, @@ -1196,7 +1210,7 @@ In addition to the three supplied contexts, new contexts can be created with the If the argument is a string, no leading or trailing whitespace or underscores are permitted. - .. method:: create_decimal_from_float(f) + .. method:: create_decimal_from_float(f, /) Creates a new Decimal instance from a float *f* but rounding using *self* as the context. Unlike the :meth:`Decimal.from_float` class method, @@ -1234,222 +1248,222 @@ In addition to the three supplied contexts, new contexts can be created with the recounted here. - .. method:: abs(x) + .. method:: abs(x, /) Returns the absolute value of *x*. - .. method:: add(x, y) + .. method:: add(x, y, /) Return the sum of *x* and *y*. - .. method:: canonical(x) + .. method:: canonical(x, /) Returns the same Decimal object *x*. - .. method:: compare(x, y) + .. method:: compare(x, y, /) Compares *x* and *y* numerically. - .. method:: compare_signal(x, y) + .. method:: compare_signal(x, y, /) Compares the values of the two operands numerically. - .. method:: compare_total(x, y) + .. method:: compare_total(x, y, /) Compares two operands using their abstract representation. - .. method:: compare_total_mag(x, y) + .. method:: compare_total_mag(x, y, /) Compares two operands using their abstract representation, ignoring sign. - .. method:: copy_abs(x) + .. method:: copy_abs(x, /) Returns a copy of *x* with the sign set to 0. - .. method:: copy_negate(x) + .. method:: copy_negate(x, /) Returns a copy of *x* with the sign inverted. - .. method:: copy_sign(x, y) + .. method:: copy_sign(x, y, /) Copies the sign from *y* to *x*. - .. method:: divide(x, y) + .. method:: divide(x, y, /) Return *x* divided by *y*. - .. method:: divide_int(x, y) + .. method:: divide_int(x, y, /) Return *x* divided by *y*, truncated to an integer. - .. method:: divmod(x, y) + .. method:: divmod(x, y, /) Divides two numbers and returns the integer part of the result. - .. method:: exp(x) + .. method:: exp(x, /) Returns ``e ** x``. - .. method:: fma(x, y, z) + .. method:: fma(x, y, z, /) Returns *x* multiplied by *y*, plus *z*. - .. method:: is_canonical(x) + .. method:: is_canonical(x, /) Returns ``True`` if *x* is canonical; otherwise returns ``False``. - .. method:: is_finite(x) + .. method:: is_finite(x, /) Returns ``True`` if *x* is finite; otherwise returns ``False``. - .. method:: is_infinite(x) + .. method:: is_infinite(x, /) Returns ``True`` if *x* is infinite; otherwise returns ``False``. - .. method:: is_nan(x) + .. method:: is_nan(x, /) Returns ``True`` if *x* is a qNaN or sNaN; otherwise returns ``False``. - .. method:: is_normal(x) + .. method:: is_normal(x, /) Returns ``True`` if *x* is a normal number; otherwise returns ``False``. - .. method:: is_qnan(x) + .. method:: is_qnan(x, /) Returns ``True`` if *x* is a quiet NaN; otherwise returns ``False``. - .. method:: is_signed(x) + .. method:: is_signed(x, /) Returns ``True`` if *x* is negative; otherwise returns ``False``. - .. method:: is_snan(x) + .. method:: is_snan(x, /) Returns ``True`` if *x* is a signaling NaN; otherwise returns ``False``. - .. method:: is_subnormal(x) + .. method:: is_subnormal(x, /) Returns ``True`` if *x* is subnormal; otherwise returns ``False``. - .. method:: is_zero(x) + .. method:: is_zero(x, /) Returns ``True`` if *x* is a zero; otherwise returns ``False``. - .. method:: ln(x) + .. method:: ln(x, /) Returns the natural (base e) logarithm of *x*. - .. method:: log10(x) + .. method:: log10(x, /) Returns the base 10 logarithm of *x*. - .. method:: logb(x) + .. method:: logb(x, /) Returns the exponent of the magnitude of the operand's MSD. - .. method:: logical_and(x, y) + .. method:: logical_and(x, y, /) Applies the logical operation *and* between each operand's digits. - .. method:: logical_invert(x) + .. method:: logical_invert(x, /) Invert all the digits in *x*. - .. method:: logical_or(x, y) + .. method:: logical_or(x, y, /) Applies the logical operation *or* between each operand's digits. - .. method:: logical_xor(x, y) + .. method:: logical_xor(x, y, /) Applies the logical operation *xor* between each operand's digits. - .. method:: max(x, y) + .. method:: max(x, y, /) Compares two values numerically and returns the maximum. - .. method:: max_mag(x, y) + .. method:: max_mag(x, y, /) Compares the values numerically with their sign ignored. - .. method:: min(x, y) + .. method:: min(x, y, /) Compares two values numerically and returns the minimum. - .. method:: min_mag(x, y) + .. method:: min_mag(x, y, /) Compares the values numerically with their sign ignored. - .. method:: minus(x) + .. method:: minus(x, /) Minus corresponds to the unary prefix minus operator in Python. - .. method:: multiply(x, y) + .. method:: multiply(x, y, /) Return the product of *x* and *y*. - .. method:: next_minus(x) + .. method:: next_minus(x, /) Returns the largest representable number smaller than *x*. - .. method:: next_plus(x) + .. method:: next_plus(x, /) Returns the smallest representable number larger than *x*. - .. method:: next_toward(x, y) + .. method:: next_toward(x, y, /) Returns the number closest to *x*, in direction towards *y*. - .. method:: normalize(x) + .. method:: normalize(x, /) Reduces *x* to its simplest form. - .. method:: number_class(x) + .. method:: number_class(x, /) Returns an indication of the class of *x*. - .. method:: plus(x) + .. method:: plus(x, /) Plus corresponds to the unary prefix plus operator in Python. This operation applies the context precision and rounding, so it is *not* an @@ -1490,7 +1504,7 @@ In addition to the three supplied contexts, new contexts can be created with the always exact. - .. method:: quantize(x, y) + .. method:: quantize(x, y, /) Returns a value equal to *x* (rounded), having the exponent of *y*. @@ -1500,7 +1514,7 @@ In addition to the three supplied contexts, new contexts can be created with the Just returns 10, as this is Decimal, :) - .. method:: remainder(x, y) + .. method:: remainder(x, y, /) Returns the remainder from integer division. @@ -1508,43 +1522,43 @@ In addition to the three supplied contexts, new contexts can be created with the dividend. - .. method:: remainder_near(x, y) + .. method:: remainder_near(x, y, /) Returns ``x - y * n``, where *n* is the integer nearest the exact value of ``x / y`` (if the result is 0 then its sign will be the sign of *x*). - .. method:: rotate(x, y) + .. method:: rotate(x, y, /) Returns a rotated copy of *x*, *y* times. - .. method:: same_quantum(x, y) + .. method:: same_quantum(x, y, /) Returns ``True`` if the two operands have the same exponent. - .. method:: scaleb (x, y) + .. method:: scaleb (x, y, /) Returns the first operand after adding the second value its exp. - .. method:: shift(x, y) + .. method:: shift(x, y, /) Returns a shifted copy of *x*, *y* times. - .. method:: sqrt(x) + .. method:: sqrt(x, /) Square root of a non-negative number to context precision. - .. method:: subtract(x, y) + .. method:: subtract(x, y, /) Return the difference between *x* and *y*. - .. method:: to_eng_string(x) + .. method:: to_eng_string(x, /) Convert to a string, using engineering notation if an exponent is needed. @@ -1553,12 +1567,12 @@ In addition to the three supplied contexts, new contexts can be created with the require the addition of either one or two trailing zeros. - .. method:: to_integral_exact(x) + .. method:: to_integral_exact(x, /) Rounds to an integer. - .. method:: to_sci_string(x) + .. method:: to_sci_string(x, /) Converts a number to a string using scientific notation. @@ -1569,7 +1583,16 @@ In addition to the three supplied contexts, new contexts can be created with the Constants --------- -The constants in this section are only relevant for the C module. They +.. data:: SPEC_VERSION + + The highest version of the General Decimal Arithmetic + Specification that this implementation complies with. + See https://speleotrove.com/decimal/decarith.html for the specification. + + .. versionadded:: 3.15 + + +The following constants are only relevant for the C module. They are also included in the pure Python version for compatibility. +---------------------------------+---------------------+-------------------------------+ @@ -1816,7 +1839,7 @@ properties of addition: >>> u * (v+w) Decimal('0.0060000') -The :mod:`decimal` module makes it possible to restore the identities by +The :mod:`!decimal` module makes it possible to restore the identities by expanding the precision sufficiently to avoid loss of significance: .. doctest:: newcontext @@ -1838,7 +1861,7 @@ expanding the precision sufficiently to avoid loss of significance: Special values ^^^^^^^^^^^^^^ -The number system for the :mod:`decimal` module provides special values +The number system for the :mod:`!decimal` module provides special values including ``NaN``, ``sNaN``, ``-Infinity``, ``Infinity``, and two zeros, ``+0`` and ``-0``. @@ -2100,20 +2123,20 @@ to work with the :class:`Decimal` class:: Decimal FAQ ----------- -Q. It is cumbersome to type ``decimal.Decimal('1234.5')``. Is there a way to +Q: It is cumbersome to type ``decimal.Decimal('1234.5')``. Is there a way to minimize typing when using the interactive interpreter? -A. Some users abbreviate the constructor to just a single letter: +A: Some users abbreviate the constructor to just a single letter: >>> D = decimal.Decimal >>> D('1.23') + D('3.45') Decimal('4.68') -Q. In a fixed-point application with two decimal places, some inputs have many +Q: In a fixed-point application with two decimal places, some inputs have many places and need to be rounded. Others are not supposed to have excess digits and need to be validated. What methods should be used? -A. The :meth:`~Decimal.quantize` method rounds to a fixed number of decimal places. If +A: The :meth:`~Decimal.quantize` method rounds to a fixed number of decimal places. If the :const:`Inexact` trap is set, it is also useful for validation: >>> TWOPLACES = Decimal(10) ** -2 # same as Decimal('0.01') @@ -2131,10 +2154,10 @@ the :const:`Inexact` trap is set, it is also useful for validation: ... Inexact: None -Q. Once I have valid two place inputs, how do I maintain that invariant +Q: Once I have valid two place inputs, how do I maintain that invariant throughout an application? -A. Some operations like addition, subtraction, and multiplication by an integer +A: Some operations like addition, subtraction, and multiplication by an integer will automatically preserve fixed point. Others operations, like division and non-integer multiplication, will change the number of decimal places and need to be followed-up with a :meth:`~Decimal.quantize` step: @@ -2166,21 +2189,21 @@ to handle the :meth:`~Decimal.quantize` step: >>> div(b, a) Decimal('0.03') -Q. There are many ways to express the same value. The numbers ``200``, +Q: There are many ways to express the same value. The numbers ``200``, ``200.000``, ``2E2``, and ``.02E+4`` all have the same value at various precisions. Is there a way to transform them to a single recognizable canonical value? -A. The :meth:`~Decimal.normalize` method maps all equivalent values to a single +A: The :meth:`~Decimal.normalize` method maps all equivalent values to a single representative: >>> values = map(Decimal, '200 200.000 2E2 .02E+4'.split()) >>> [v.normalize() for v in values] [Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2')] -Q. When does rounding occur in a computation? +Q: When does rounding occur in a computation? -A. It occurs *after* the computation. The philosophy of the decimal +A: It occurs *after* the computation. The philosophy of the decimal specification is that numbers are considered exact and are created independent of the current context. They can even have greater precision than current context. Computations process with those @@ -2198,10 +2221,10 @@ applied to the *result* of the computation:: >>> pi + 0 - Decimal('0.00005'). # Intermediate values are rounded Decimal('3.1416') -Q. Some decimal values always print with exponential notation. Is there a way +Q: Some decimal values always print with exponential notation. Is there a way to get a non-exponential representation? -A. For some values, exponential notation is the only way to express the number +A: For some values, exponential notation is the only way to express the number of significant places in the coefficient. For example, expressing ``5.0E+3`` as ``5000`` keeps the value constant but cannot show the original's two-place significance. @@ -2216,9 +2239,9 @@ value unchanged: >>> remove_exponent(Decimal('5E+3')) Decimal('5000') -Q. Is there a way to convert a regular float to a :class:`Decimal`? +Q: Is there a way to convert a regular float to a :class:`Decimal`? -A. Yes, any binary floating-point number can be exactly expressed as a +A: Yes, any binary floating-point number can be exactly expressed as a Decimal though an exact conversion may take more precision than intuition would suggest: @@ -2227,19 +2250,19 @@ suggest: >>> Decimal(math.pi) Decimal('3.141592653589793115997963468544185161590576171875') -Q. Within a complex calculation, how can I make sure that I haven't gotten a +Q: Within a complex calculation, how can I make sure that I haven't gotten a spurious result because of insufficient precision or rounding anomalies. -A. The decimal module makes it easy to test results. A best practice is to +A: The decimal module makes it easy to test results. A best practice is to re-run calculations using greater precision and with various rounding modes. Widely differing results indicate insufficient precision, rounding mode issues, ill-conditioned inputs, or a numerically unstable algorithm. -Q. I noticed that context precision is applied to the results of operations but +Q: I noticed that context precision is applied to the results of operations but not to the inputs. Is there anything to watch out for when mixing values of different precisions? -A. Yes. The principle is that all values are considered to be exact and so is +A: Yes. The principle is that all values are considered to be exact and so is the arithmetic on those values. Only the results are rounded. The advantage for inputs is that "what you type is what you get". A disadvantage is that the results can look odd if you forget that the inputs haven't been rounded: @@ -2267,9 +2290,9 @@ Alternatively, inputs can be rounded upon creation using the >>> Context(prec=5, rounding=ROUND_DOWN).create_decimal('1.2345678') Decimal('1.2345') -Q. Is the CPython implementation fast for large numbers? +Q: Is the CPython implementation fast for large numbers? -A. Yes. In the CPython and PyPy3 implementations, the C/CFFI versions of +A: Yes. In the CPython and PyPy3 implementations, the C/CFFI versions of the decimal module integrate the high speed `libmpdec `_ library for arbitrary precision correctly rounded decimal floating-point arithmetic [#]_. diff --git a/Doc/library/dialog.rst b/Doc/library/dialog.rst index e0693e8eb6ed22..5d522556235a02 100644 --- a/Doc/library/dialog.rst +++ b/Doc/library/dialog.rst @@ -1,18 +1,17 @@ Tkinter Dialogs =============== -:mod:`tkinter.simpledialog` --- Standard Tkinter input dialogs -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:mod:`!tkinter.simpledialog` --- Standard Tkinter input dialogs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. module:: tkinter.simpledialog - :platform: Tk :synopsis: Simple dialog windows **Source code:** :source:`Lib/tkinter/simpledialog.py` -------------- -The :mod:`tkinter.simpledialog` module contains convenience classes and +The :mod:`!tkinter.simpledialog` module contains convenience classes and functions for creating simple modal dialogs to get a value from the user. @@ -39,18 +38,17 @@ functions for creating simple modal dialogs to get a value from the user. -:mod:`tkinter.filedialog` --- File selection dialogs -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:mod:`!tkinter.filedialog` --- File selection dialogs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. module:: tkinter.filedialog - :platform: Tk :synopsis: Dialog classes for file selection **Source code:** :source:`Lib/tkinter/filedialog.py` -------------- -The :mod:`tkinter.filedialog` module provides classes and factory functions for +The :mod:`!tkinter.filedialog` module provides classes and factory functions for creating file/directory selection windows. Native Load/Save Dialogs @@ -204,18 +202,17 @@ These do not emulate the native look-and-feel of the platform. directory. Confirmation is required if an already existing file is selected. -:mod:`tkinter.commondialog` --- Dialog window templates -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +:mod:`!tkinter.commondialog` --- Dialog window templates +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. module:: tkinter.commondialog - :platform: Tk :synopsis: Tkinter base class for dialogs **Source code:** :source:`Lib/tkinter/commondialog.py` -------------- -The :mod:`tkinter.commondialog` module provides the :class:`Dialog` class that +The :mod:`!tkinter.commondialog` module provides the :class:`Dialog` class that is the base class for dialogs defined in other supporting modules. .. class:: Dialog(master=None, **options) diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst index ce948a6860f02c..25edb40e35a630 100644 --- a/Doc/library/difflib.rst +++ b/Doc/library/difflib.rst @@ -4,10 +4,6 @@ .. module:: difflib :synopsis: Helpers for computing differences between objects. -.. moduleauthor:: Tim Peters -.. sectionauthor:: Tim Peters -.. Markup by Fred L. Drake, Jr. - **Source code:** :source:`Lib/difflib.py` .. testsetup:: @@ -231,7 +227,7 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. *linejunk*: A function that accepts a single string argument, and returns true if the string is junk, or false if not. The default is ``None``. There is also a module-level function :func:`IS_LINE_JUNK`, which filters out lines - without visible characters, except for at most one pound character (``'#'``) + without visible characters, except for at most one hash character (``'#'``) -- however the underlying :class:`SequenceMatcher` class does a dynamic analysis of which lines are so frequent as to constitute noise, and this usually works better than using this function. @@ -278,7 +274,7 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. emu -.. function:: unified_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n') +.. function:: unified_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n', *, color=False) Compare *a* and *b* (lists of strings); return a delta (a :term:`generator` generating the delta lines) in unified diff format. @@ -297,6 +293,10 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. For inputs that do not have trailing newlines, set the *lineterm* argument to ``""`` so that the output will be uniformly newline free. + Set *color* to ``True`` to enable output in color, similar to + :program:`git diff --color`. Even if enabled, it can be + :ref:`controlled using environment variables `. + The unified diff format normally has a header for filenames and modification times. Any or all of these may be specified using strings for *fromfile*, *tofile*, *fromfiledate*, and *tofiledate*. The modification times are normally @@ -319,6 +319,10 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. See :ref:`difflib-interface` for a more detailed example. + .. versionchanged:: 3.15 + Added the *color* parameter. + + .. function:: diff_bytes(dfunc, a, b, fromfile=b'', tofile=b'', fromfiledate=b'', tofiledate=b'', n=3, lineterm=b'\n') Compare *a* and *b* (lists of bytes objects) using *dfunc*; yield a @@ -351,14 +355,14 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module. .. seealso:: - `Pattern Matching: The Gestalt Approach `_ + `Pattern Matching: The Gestalt Approach `_ Discussion of a similar algorithm by John W. Ratcliff and D. E. Metzener. This - was published in `Dr. Dobb's Journal `_ in July, 1988. + was published in Dr. Dobb's Journal in July, 1988. .. _sequence-matcher: -SequenceMatcher Objects +SequenceMatcher objects ----------------------- The :class:`SequenceMatcher` class has this constructor: @@ -586,7 +590,7 @@ are always at least as large as :meth:`~SequenceMatcher.ratio`: .. _sequencematcher-examples: -SequenceMatcher Examples +SequenceMatcher examples ------------------------ This example compares two strings, considering blanks to be "junk": @@ -637,7 +641,7 @@ If you want to know how to change the first sequence into the second, use .. _differ-objects: -Differ Objects +Differ objects -------------- Note that :class:`Differ`\ -generated deltas make no claim to be **minimal** @@ -686,7 +690,7 @@ The :class:`Differ` class has this constructor: .. _differ-examples: -Differ Example +Differ example -------------- This example compares two texts. First we set up the texts, sequences of @@ -720,7 +724,7 @@ Finally, we compare the two: >>> result = list(d.compare(text1, text2)) -``result`` is a list of strings, so let's pretty-print it: +``result`` is a list of strings, so let's pretty-print it:: >>> from pprint import pprint >>> pprint(result) @@ -735,7 +739,7 @@ Finally, we compare the two: '? ++++ ^ ^\n', '+ 5. Flat is better than nested.\n'] -As a single multi-line string it looks like this: +As a single multi-line string it looks like this:: >>> import sys >>> sys.stdout.writelines(result) diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index ac8a911c40a860..3e7ae509fedcea 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -14,7 +14,7 @@ -------------- -The :mod:`dis` module supports the analysis of CPython :term:`bytecode` by +The :mod:`!dis` module supports the analysis of CPython :term:`bytecode` by disassembling it. The CPython bytecode which this module takes as an input is defined in the file :file:`Include/opcode.h` and used by the compiler and the interpreter. @@ -38,7 +38,7 @@ interpreter. Some instructions are accompanied by one or more inline cache entries, which take the form of :opcode:`CACHE` instructions. These instructions are hidden by default, but can be shown by passing ``show_caches=True`` to - any :mod:`dis` utility. Furthermore, the interpreter now adapts the + any :mod:`!dis` utility. Furthermore, the interpreter now adapts the bytecode to specialize it for different runtime conditions. The adaptive bytecode can be shown by passing ``adaptive=True``. @@ -87,7 +87,7 @@ the following command can be used to display the disassembly of Command-line interface ---------------------- -The :mod:`dis` module can be invoked as a script from the command line: +The :mod:`!dis` module can be invoked as a script from the command line: .. code-block:: sh @@ -223,7 +223,7 @@ Example: Analysis functions ------------------ -The :mod:`dis` module also defines the following analysis functions that convert +The :mod:`!dis` module also defines the following analysis functions that convert the input directly to the desired output. They can be useful if only a single operation is being performed, so the intermediate analysis object isn't useful: @@ -400,7 +400,7 @@ operation is being performed, so the intermediate analysis object isn't useful: .. versionchanged:: 3.10 The :pep:`626` :meth:`~codeobject.co_lines` method is used instead of the - :attr:`~codeobject.co_firstlineno` and :attr:`~codeobject.co_lnotab` + :attr:`~codeobject.co_firstlineno` and :attr:`!codeobject.co_lnotab` attributes of the :ref:`code object `. .. versionchanged:: 3.13 @@ -585,6 +585,22 @@ operations on it as if it was a Python list. The top of the stack corresponds to generate line tracing events. +.. opcode:: NOT_TAKEN + + Do nothing code. + Used by the interpreter to record :monitoring-event:`BRANCH_LEFT` + and :monitoring-event:`BRANCH_RIGHT` events for :mod:`sys.monitoring`. + + .. versionadded:: 3.14 + + +.. opcode:: POP_ITER + + Removes the iterator from the top of the stack. + + .. versionadded:: 3.14 + + .. opcode:: POP_TOP Removes the top-of-stack item:: @@ -752,7 +768,7 @@ not have to be) the original ``STACK[-2]``. end = STACK.pop() start = STACK.pop() container = STACK.pop() - values = STACK.pop() + value = STACK.pop() container[start:end] = value .. versionadded:: 3.12 @@ -1122,8 +1138,8 @@ iterations of the loop. .. opcode:: BUILD_TEMPLATE - Constructs a new :class:`~string.templatelib.Template` from a tuple - of strings and a tuple of interpolations and pushes the resulting instance + Constructs a new :class:`~string.templatelib.Template` instance from a tuple + of strings and a tuple of interpolations and pushes the resulting object onto the stack:: interpolations = STACK.pop() @@ -1135,8 +1151,8 @@ iterations of the loop. .. opcode:: BUILD_INTERPOLATION (format) - Constructs a new :class:`~string.templatelib.Interpolation` from a - value and its source expression and pushes the resulting instance onto the + Constructs a new :class:`~string.templatelib.Interpolation` instance from a + value and its source expression and pushes the resulting object onto the stack. If no conversion or format specification is present, ``format`` is set to @@ -1626,7 +1642,7 @@ iterations of the loop. Pushes a ``NULL`` to the stack. Used in the call sequence to match the ``NULL`` pushed by - :opcode:`LOAD_METHOD` for non-method calls. + :opcode:`LOAD_ATTR` for non-method calls. .. versionadded:: 3.11 @@ -1657,9 +1673,13 @@ iterations of the loop. * ``0x02`` a dictionary of keyword-only parameters' default values * ``0x04`` a tuple of strings containing parameters' annotations * ``0x08`` a tuple containing cells for free variables, making a closure + * ``0x10`` the :term:`annotate function` for the function object .. versionadded:: 3.13 + .. versionchanged:: 3.14 + Added ``0x10`` to indicate the annotate function for the function object. + .. opcode:: BUILD_SLICE (argc) @@ -1807,7 +1827,7 @@ iterations of the loop. ignore it. Before, only opcodes ``>= HAVE_ARGUMENT`` had an argument. .. versionchanged:: 3.12 - Pseudo instructions were added to the :mod:`dis` module, and for them + Pseudo instructions were added to the :mod:`!dis` module, and for them it is not true that comparison with ``HAVE_ARGUMENT`` indicates whether they use their arg. @@ -1947,14 +1967,15 @@ but are replaced by real opcodes or removed before bytecode is generated. Marks the end of the code block associated with the last ``SETUP_FINALLY``, ``SETUP_CLEANUP`` or ``SETUP_WITH``. + .. opcode:: JUMP -.. opcode:: JUMP_NO_INTERRUPT + JUMP_NO_INTERRUPT Undirected relative jump instructions which are replaced by their directed (forward/backward) counterparts by the assembler. .. opcode:: JUMP_IF_TRUE -.. opcode:: JUMP_IF_FALSE + JUMP_IF_FALSE Conditional jumps which do not impact the stack. Replaced by the sequence ``COPY 1``, ``TO_BOOL``, ``POP_JUMP_IF_TRUE/FALSE``. @@ -1970,12 +1991,6 @@ but are replaced by real opcodes or removed before bytecode is generated. This opcode is now a pseudo-instruction. -.. opcode:: LOAD_METHOD - - Optimized unbound method lookup. Emitted as a ``LOAD_ATTR`` opcode - with a flag set in the arg. - - .. _opcode_collections: Opcode collections diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index 5a2c6bdd27c386..3298697af8511b 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -4,16 +4,11 @@ .. module:: doctest :synopsis: Test pieces of code within docstrings. -.. moduleauthor:: Tim Peters -.. sectionauthor:: Tim Peters -.. sectionauthor:: Moshe Zadka -.. sectionauthor:: Edward Loper - **Source code:** :source:`Lib/doctest.py` -------------- -The :mod:`doctest` module searches for pieces of text that look like interactive +The :mod:`!doctest` module searches for pieces of text that look like interactive Python sessions, and then executes those sessions to verify that they work exactly as shown. There are several common ways to use doctest: @@ -85,7 +80,7 @@ Here's a complete but small example module:: import doctest doctest.testmod() -If you run :file:`example.py` directly from the command line, :mod:`doctest` +If you run :file:`example.py` directly from the command line, :mod:`!doctest` works its magic: .. code-block:: shell-session @@ -94,7 +89,7 @@ works its magic: $ There's no output! That's normal, and it means all the examples worked. Pass -``-v`` to the script, and :mod:`doctest` prints a detailed log of what +``-v`` to the script, and :mod:`!doctest` prints a detailed log of what it's trying, and prints a summary at the end: .. code-block:: shell-session @@ -130,7 +125,7 @@ And so on, eventually ending with: Test passed. $ -That's all you need to know to start making productive use of :mod:`doctest`! +That's all you need to know to start making productive use of :mod:`!doctest`! Jump in. The following sections provide full details. Note that there are many examples of doctests in the standard Python test suite and libraries. Especially useful examples can be found in the standard test file @@ -252,7 +247,7 @@ For more information on :func:`testfile`, see section :ref:`doctest-basic-api`. Command-line Usage ------------------ -The :mod:`doctest` module can be invoked as a script from the command line: +The :mod:`!doctest` module can be invoked as a script from the command line: .. code-block:: bash @@ -350,6 +345,13 @@ searches them recursively for docstrings, which are then scanned for tests. Any classes found are recursively searched similarly, to test docstrings in their contained methods and nested classes. +.. note:: + + ``doctest`` can only automatically discover classes and functions that are + defined at the module level or inside other classes. + + Since nested classes and functions only exist when an outer function + is called, they cannot be discovered. Define them outside to make them visible. .. _doctest-finding-examples: @@ -443,7 +445,7 @@ The fine print: What's the Execution Context? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -By default, each time :mod:`doctest` finds a docstring to test, it uses a +By default, each time :mod:`!doctest` finds a docstring to test, it uses a *shallow copy* of :mod:`!M`'s globals, so that running tests doesn't change the module's real globals, and so that one test in :mod:`!M` can't leave behind crumbs that accidentally allow another test to work. This means examples can @@ -723,7 +725,7 @@ The second group of options controls how test failures are reported: There is also a way to register new option flag names, though this isn't -useful unless you intend to extend :mod:`doctest` internals via subclassing: +useful unless you intend to extend :mod:`!doctest` internals via subclassing: .. function:: register_optionflag(name) @@ -826,7 +828,7 @@ disabling an option via ``-`` in a directive can be useful. Warnings ^^^^^^^^ -:mod:`doctest` is serious about requiring exact matches in expected output. If +:mod:`!doctest` is serious about requiring exact matches in expected output. If even a single character doesn't match, the test fails. This will probably surprise you a few times, as you learn exactly what Python does and doesn't guarantee about output. For example, when printing a set, Python doesn't @@ -1028,7 +1030,7 @@ Unittest API ------------ As your collection of doctest'ed modules grows, you'll want a way to run all -their doctests systematically. :mod:`doctest` provides two functions that can +their doctests systematically. :mod:`!doctest` provides two functions that can be used to create :mod:`unittest` test suites from modules and text files containing doctests. To integrate with :mod:`unittest` test discovery, include a :ref:`load_tests ` function in your test module:: @@ -1116,7 +1118,7 @@ from text files and modules with doctests: The global ``__file__`` is added to the globals provided to doctests loaded from a text file using :func:`DocFileSuite`. - .. versionchanged:: next + .. versionchanged:: 3.15 Run each example as a :ref:`subtest `. @@ -1157,7 +1159,7 @@ from text files and modules with doctests: :func:`DocTestSuite` returns an empty :class:`unittest.TestSuite` if *module* contains no docstrings instead of raising :exc:`ValueError`. - .. versionchanged:: next + .. versionchanged:: 3.15 Run each example as a :ref:`subtest `. Under the covers, :func:`DocTestSuite` creates a :class:`unittest.TestSuite` out @@ -1172,7 +1174,7 @@ of :class:`!DocTestCase`. So both ways of creating a :class:`unittest.TestSuite` run instances of :class:`!DocTestCase`. This is important for a subtle reason: when you run -:mod:`doctest` functions yourself, you can control the :mod:`!doctest` options in +:mod:`!doctest` functions yourself, you can control the :mod:`!doctest` options in use directly, by passing option flags to :mod:`!doctest` functions. However, if you're writing a :mod:`unittest` framework, :mod:`!unittest` ultimately controls when and how tests get run. The framework author typically wants to control @@ -1180,13 +1182,13 @@ when and how tests get run. The framework author typically wants to control options), but there's no way to pass options through :mod:`!unittest` to :mod:`!doctest` test runners. -For this reason, :mod:`doctest` also supports a notion of :mod:`!doctest` +For this reason, :mod:`!doctest` also supports a notion of :mod:`!doctest` reporting flags specific to :mod:`unittest` support, via this function: .. function:: set_unittest_reportflags(flags) - Set the :mod:`doctest` reporting flags to use. + Set the :mod:`!doctest` reporting flags to use. Argument *flags* takes the :ref:`bitwise OR ` of option flags. See section :ref:`doctest-options`. Only "reporting flags" can be used. @@ -1557,7 +1559,7 @@ DocTestRunner objects containing *example*. *out* is the output function that was passed to :meth:`DocTestRunner.run`. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: report_start(out, test, example) @@ -1916,7 +1918,7 @@ There are two exceptions that may be raised by :class:`DebugRunner` instances: Soapbox ------- -As mentioned in the introduction, :mod:`doctest` has grown to have three primary +As mentioned in the introduction, :mod:`!doctest` has grown to have three primary uses: #. Checking examples in docstrings. @@ -1934,7 +1936,7 @@ this that needs to be learned---it may not be natural at first. Examples should add genuine value to the documentation. A good example can often be worth many words. If done with care, the examples will be invaluable for your users, and will pay back the time it takes to collect them many times over as the years go -by and things change. I'm still amazed at how often one of my :mod:`doctest` +by and things change. I'm still amazed at how often one of my :mod:`!doctest` examples stops working after a "harmless" change. Doctest also makes an excellent tool for regression testing, especially if you diff --git a/Doc/library/email.charset.rst b/Doc/library/email.charset.rst index 6875af2be49d7a..76a57031862c85 100644 --- a/Doc/library/email.charset.rst +++ b/Doc/library/email.charset.rst @@ -19,7 +19,7 @@ registry and several convenience methods for manipulating this registry. Instances of :class:`Charset` are used in several other modules within the :mod:`email` package. -Import this class from the :mod:`email.charset` module. +Import this class from the :mod:`!email.charset` module. .. class:: Charset(input_charset=DEFAULT_CHARSET) @@ -164,7 +164,7 @@ Import this class from the :mod:`email.charset` module. This method allows you to compare two :class:`Charset` instances for inequality. -The :mod:`email.charset` module also provides the following functions for adding +The :mod:`!email.charset` module also provides the following functions for adding new entries to the global character set, alias, and codec registries: diff --git a/Doc/library/email.contentmanager.rst b/Doc/library/email.contentmanager.rst index b33fe82a6e4c9f..04a41667f7dc2c 100644 --- a/Doc/library/email.contentmanager.rst +++ b/Doc/library/email.contentmanager.rst @@ -4,9 +4,6 @@ .. module:: email.contentmanager :synopsis: Storing and Retrieving Content from MIME Parts -.. moduleauthor:: R. David Murray -.. sectionauthor:: R. David Murray - **Source code:** :source:`Lib/email/contentmanager.py` ------------ diff --git a/Doc/library/email.encoders.rst b/Doc/library/email.encoders.rst index 9c8c8c9234ed7a..1a9a1cad3a619e 100644 --- a/Doc/library/email.encoders.rst +++ b/Doc/library/email.encoders.rst @@ -25,7 +25,7 @@ is especially true for :mimetype:`image/\*` and :mimetype:`text/\*` type message containing binary data. The :mod:`email` package provides some convenient encoders in its -:mod:`~email.encoders` module. These encoders are actually used by the +:mod:`!encoders` module. These encoders are actually used by the :class:`~email.mime.audio.MIMEAudio` and :class:`~email.mime.image.MIMEImage` class constructors to provide default encodings. All encoder functions take exactly one argument, the message object to encode. They usually extract the diff --git a/Doc/library/email.errors.rst b/Doc/library/email.errors.rst index 689e7397cbcf1f..2f7c9140cfcbe5 100644 --- a/Doc/library/email.errors.rst +++ b/Doc/library/email.errors.rst @@ -8,7 +8,7 @@ -------------- -The following exception classes are defined in the :mod:`email.errors` module: +The following exception classes are defined in the :mod:`!email.errors` module: .. exception:: MessageError() diff --git a/Doc/library/email.generator.rst b/Doc/library/email.generator.rst index a3132d02687bc9..6f4f813a0f84d8 100644 --- a/Doc/library/email.generator.rst +++ b/Doc/library/email.generator.rst @@ -232,7 +232,7 @@ a formatted string representation of a message object. For more detail, see :mod:`email.message`. -The :mod:`email.generator` module also provides a derived class, +The :mod:`!email.generator` module also provides a derived class, :class:`DecodedGenerator`, which is like the :class:`Generator` base class, except that non-\ :mimetype:`text` parts are not serialized, but are instead represented in the output stream by a string derived from a template filled diff --git a/Doc/library/email.header.rst b/Doc/library/email.header.rst index f49885b8785235..e7e21d036e07de 100644 --- a/Doc/library/email.header.rst +++ b/Doc/library/email.header.rst @@ -28,13 +28,13 @@ transferred using only 7-bit ASCII characters, so a slew of RFCs have been written describing how to encode email containing non-ASCII characters into :rfc:`2822`\ -compliant format. These RFCs include :rfc:`2045`, :rfc:`2046`, :rfc:`2047`, and :rfc:`2231`. The :mod:`email` package supports these standards -in its :mod:`email.header` and :mod:`email.charset` modules. +in its :mod:`!email.header` and :mod:`email.charset` modules. If you want to include non-ASCII characters in your email headers, say in the :mailheader:`Subject` or :mailheader:`To` fields, you should use the :class:`Header` class and assign the field in the :class:`~email.message.Message` object to an instance of :class:`Header` instead of using a string for the header -value. Import the :class:`Header` class from the :mod:`email.header` module. +value. Import the :class:`Header` class from the :mod:`!email.header` module. For example:: >>> from email.message import Message @@ -170,7 +170,7 @@ Here is the :class:`Header` class description: This method allows you to compare two :class:`Header` instances for inequality. -The :mod:`email.header` module also provides the following convenient functions. +The :mod:`!email.header` module also provides the following convenient functions. .. function:: decode_header(header) diff --git a/Doc/library/email.headerregistry.rst b/Doc/library/email.headerregistry.rst index 7f8044932fae99..619c17c98e8d89 100644 --- a/Doc/library/email.headerregistry.rst +++ b/Doc/library/email.headerregistry.rst @@ -4,9 +4,6 @@ .. module:: email.headerregistry :synopsis: Automatic Parsing of headers based on the field name -.. moduleauthor:: R. David Murray -.. sectionauthor:: R. David Murray - **Source code:** :source:`Lib/email/headerregistry.py` -------------- @@ -96,9 +93,10 @@ headers. ``kwds`` is a dictionary containing one pre-initialized key, ``defects``. ``defects`` is an empty list. The parse method should append any detected defects to this list. On return, the ``kwds`` dictionary *must* contain - values for at least the keys ``decoded`` and ``defects``. ``decoded`` - should be the string value for the header (that is, the header value fully - decoded to unicode). The parse method should assume that *string* may + values for at least the keys ``decoded``, ``defects`` and ``parse_tree``. + ``decoded`` should be the string value for the header (that is, the header + value fully decoded to unicode). ``parse_tree`` is set to the parse tree obtained + from parsing the header. The parse method should assume that *string* may contain content-transfer-encoded parts, but should correctly handle all valid unicode characters as well so that it can parse un-encoded header values. @@ -269,6 +267,10 @@ variant, :attr:`~.BaseHeader.max_count` is set to 1. A dictionary mapping parameter names to parameter values. + .. versionchanged:: 3.15 + It is now a :class:`frozendict` instead of a + :class:`types.MappingProxyType`. + .. class:: ContentTypeHeader @@ -294,7 +296,7 @@ variant, :attr:`~.BaseHeader.max_count` is set to 1. ``inline`` and ``attachment`` are the only valid values in common use. -.. class:: ContentTransferEncoding +.. class:: ContentTransferEncodingHeader Handles the :mailheader:`Content-Transfer-Encoding` header. diff --git a/Doc/library/email.iterators.rst b/Doc/library/email.iterators.rst index 090981d84b4de3..ed300cdb30fdd6 100644 --- a/Doc/library/email.iterators.rst +++ b/Doc/library/email.iterators.rst @@ -10,7 +10,7 @@ Iterating over a message object tree is fairly easy with the :meth:`Message.walk ` method. The -:mod:`email.iterators` module provides some useful higher level iterations over +:mod:`!email.iterators` module provides some useful higher level iterations over message object trees. diff --git a/Doc/library/email.message.rst b/Doc/library/email.message.rst index 71d6e321f387bc..b70df130e06dfa 100644 --- a/Doc/library/email.message.rst +++ b/Doc/library/email.message.rst @@ -3,9 +3,6 @@ .. module:: email.message :synopsis: The base class representing email messages. -.. moduleauthor:: R. David Murray -.. sectionauthor:: R. David Murray , - Barry A. Warsaw **Source code:** :source:`Lib/email/message.py` @@ -14,7 +11,7 @@ .. versionadded:: 3.6 [1]_ The central class in the :mod:`email` package is the :class:`EmailMessage` -class, imported from the :mod:`email.message` module. It is the base class for +class, imported from the :mod:`!email.message` module. It is the base class for the :mod:`email` object model. :class:`EmailMessage` provides the core functionality for setting and querying header fields, for accessing message bodies, and for creating or modifying structured messages. @@ -57,7 +54,7 @@ message objects. :class:`~email.policy.default` policy, which follows the rules of the email RFCs except for line endings (instead of the RFC mandated ``\r\n``, it uses the Python standard ``\n`` line endings). For more information see the - :mod:`~email.policy` documentation. + :mod:`~email.policy` documentation. [2]_ .. method:: as_string(unixfrom=False, maxheaderlen=None, policy=None) @@ -749,3 +746,9 @@ message objects. .. [1] Originally added in 3.4 as a :term:`provisional module `. Docs for legacy message class moved to :ref:`compat32_message`. + +.. [2] The :class:`EmailMessage` class requires a policy that provides a + ``content_manager`` attribute for content management methods like + ``set_content()`` and ``get_content()`` to work. The legacy + :const:`~email.policy.compat32` policy does not support these methods + and should not be used with :class:`EmailMessage`. diff --git a/Doc/library/email.parser.rst b/Doc/library/email.parser.rst index 6a70714dc3ee42..6a67bf7c8e555d 100644 --- a/Doc/library/email.parser.rst +++ b/Doc/library/email.parser.rst @@ -125,10 +125,10 @@ Here is the API for the :class:`BytesFeedParser`: Parser API ^^^^^^^^^^ -The :class:`BytesParser` class, imported from the :mod:`email.parser` module, +The :class:`BytesParser` class, imported from the :mod:`!email.parser` module, provides an API that can be used to parse a message when the complete contents of the message are available in a :term:`bytes-like object` or file. The -:mod:`email.parser` module also provides :class:`Parser` for parsing strings, +:mod:`!email.parser` module also provides :class:`Parser` for parsing strings, and header-only parsers, :class:`BytesHeaderParser` and :class:`HeaderParser`, which can be used if you're only interested in the headers of the message. :class:`BytesHeaderParser` and :class:`HeaderParser` @@ -155,7 +155,7 @@ message body, instead setting the payload to the raw body. Read all the data from the binary file-like object *fp*, parse the resulting bytes, and return the message object. *fp* must support - both the :meth:`~io.IOBase.readline` and the :meth:`~io.IOBase.read` + both the :meth:`~io.IOBase.readline` and the :meth:`~io.BufferedIOBase.read` methods. The bytes contained in *fp* must be formatted as a block of :rfc:`5322` diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst index 6b997ee784f6e4..816d02d86f4fc4 100644 --- a/Doc/library/email.policy.rst +++ b/Doc/library/email.policy.rst @@ -4,9 +4,6 @@ .. module:: email.policy :synopsis: Controlling the parsing and generating of messages -.. moduleauthor:: R. David Murray -.. sectionauthor:: R. David Murray - .. versionadded:: 3.3 **Source code:** :source:`Lib/email/policy.py` @@ -406,11 +403,26 @@ added matters. To illustrate:: .. attribute:: utf8 If ``False``, follow :rfc:`5322`, supporting non-ASCII characters in - headers by encoding them as "encoded words". If ``True``, follow - :rfc:`6532` and use ``utf-8`` encoding for headers. Messages + headers by encoding them as :rfc:`2047` "encoded words". If ``True``, + follow :rfc:`6532` and use ``utf-8`` encoding for headers. Messages formatted in this way may be passed to SMTP servers that support the ``SMTPUTF8`` extension (:rfc:`6531`). + When ``False``, the generator will raise + :exc:`~email.errors.HeaderWriteError` if any header includes non-ASCII + characters in a context where :rfc:`2047` does not permit encoded words. + This particularly applies to mailboxes ("addr-spec") with non-ASCII + characters, which can be created via + :class:`~email.headerregistry.Address`. To use a mailbox with a non-ASCII + domain name with ``utf8=False``, first encode the domain using the + third-party :pypi:`idna` or :pypi:`uts46` module or with + :mod:`encodings.idna`. It is not possible to use a non-ASCII username + ("local-part") in a mailbox when ``utf8=False``. + + .. versionchanged:: 3.15 + Can trigger the raising of :exc:`~email.errors.HeaderWriteError`. + (Earlier versions incorrectly applied :rfc:`2047` in certain contexts, + mostly notably in addr-specs.) .. attribute:: refold_source @@ -602,7 +614,7 @@ The header objects and their attributes are described in This concrete :class:`Policy` is the backward compatibility policy. It replicates the behavior of the email package in Python 3.2. The - :mod:`~email.policy` module also defines an instance of this class, + :mod:`!policy` module also defines an instance of this class, :const:`compat32`, that is used as the default policy. Thus the default behavior of the email package is to maintain compatibility with Python 3.2. @@ -662,6 +674,13 @@ The header objects and their attributes are described in An instance of :class:`Compat32`, providing backward compatibility with the behavior of the email package in Python 3.2. + .. note:: + + The :const:`compat32` policy should not be used as a policy for + :class:`~email.message.EmailMessage` objects, and should only be used + to serialize messages that were created using the :const:`compat32` + policy. + .. rubric:: Footnotes diff --git a/Doc/library/email.rst b/Doc/library/email.rst index 66c42e4a5008ee..98b47ffd74096c 100644 --- a/Doc/library/email.rst +++ b/Doc/library/email.rst @@ -4,18 +4,15 @@ .. module:: email :synopsis: Package supporting the parsing, manipulating, and generating email messages. -.. moduleauthor:: Barry A. Warsaw , - R. David Murray -.. sectionauthor:: R. David Murray **Source code:** :source:`Lib/email/__init__.py` -------------- -The :mod:`email` package is a library for managing email messages. It is +The :mod:`!email` package is a library for managing email messages. It is specifically *not* designed to do any sending of email messages to SMTP (:rfc:`2821`), NNTP, or other servers; those are functions of modules such as -:mod:`smtplib`. The :mod:`email` package attempts to be as +:mod:`smtplib`. The :mod:`!email` package attempts to be as RFC-compliant as possible, supporting :rfc:`5322` and :rfc:`6532`, as well as such MIME-related RFCs as :rfc:`2045`, :rfc:`2046`, :rfc:`2047`, :rfc:`2183`, and :rfc:`2231`. @@ -68,7 +65,7 @@ high level structure in question, and not the details of how those structures are represented. Since MIME content types are used widely in modern internet software (not just email), this will be a familiar concept to many programmers. -The following sections describe the functionality of the :mod:`email` package. +The following sections describe the functionality of the :mod:`!email` package. We start with the :mod:`~email.message` object model, which is the primary interface an application will use, and follow that with the :mod:`~email.parser` and :mod:`~email.generator` components. Then we cover the @@ -102,7 +99,7 @@ compatibility reasons. :class:`~email.message.EmailMessage`/:class:`~email.policy.EmailPolicy` API. -Contents of the :mod:`email` package documentation: +Contents of the :mod:`!email` package documentation: .. toctree:: diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst index 611549604fda15..e0d2c19a3b0737 100644 --- a/Doc/library/email.utils.rst +++ b/Doc/library/email.utils.rst @@ -8,7 +8,7 @@ -------------- -There are a couple of useful utilities provided in the :mod:`email.utils` +There are a couple of useful utilities provided in the :mod:`!email.utils` module: .. function:: localtime(dt=None) diff --git a/Doc/library/ensurepip.rst b/Doc/library/ensurepip.rst index fa102c4a080103..e0d77229b11802 100644 --- a/Doc/library/ensurepip.rst +++ b/Doc/library/ensurepip.rst @@ -11,7 +11,7 @@ -------------- -The :mod:`ensurepip` package provides support for bootstrapping the ``pip`` +The :mod:`!ensurepip` package provides support for bootstrapping the ``pip`` installer into an existing Python installation or virtual environment. This bootstrapping approach reflects the fact that ``pip`` is an independent project with its own release cycle, and the latest available stable version @@ -30,6 +30,8 @@ when creating a virtual environment) or after explicitly uninstalling needed to bootstrap ``pip`` are included as internal parts of the package. +.. include:: ../includes/optional-module.rst + .. seealso:: :ref:`installing-index` @@ -40,7 +42,9 @@ when creating a virtual environment) or after explicitly uninstalling .. include:: ../includes/wasm-mobile-notavail.rst -Command line interface +.. _ensurepip-cli: + +Command-line interface ---------------------- .. program:: ensurepip @@ -95,7 +99,7 @@ Providing both of the script selection options will trigger an exception. Module API ---------- -:mod:`ensurepip` exposes two functions for programmatic use: +:mod:`!ensurepip` exposes two functions for programmatic use: .. function:: version() diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst index 2cfc2f4962979f..559a76bb3963e0 100644 --- a/Doc/library/enum.rst +++ b/Doc/library/enum.rst @@ -4,11 +4,6 @@ .. module:: enum :synopsis: Implementation of an enumeration class. -.. moduleauthor:: Ethan Furman -.. sectionauthor:: Barry Warsaw -.. sectionauthor:: Eli Bendersky -.. sectionauthor:: Ethan Furman - .. versionadded:: 3.4 **Source code:** :source:`Lib/enum.py` @@ -61,7 +56,7 @@ are not normal Python classes. See --------------- -Module Contents +Module contents --------------- :class:`EnumType` @@ -120,30 +115,30 @@ Module Contents :class:`StrEnum` defaults to the lower-cased version of the member name, while other Enums default to 1 and increase from there. - :func:`~enum.property` + :deco:`~enum.property` Allows :class:`Enum` members to have attributes without conflicting with member names. The ``value`` and ``name`` attributes are implemented this way. - :func:`unique` + :deco:`unique` Enum class decorator that ensures only one name is bound to any one value. - :func:`verify` + :deco:`verify` Enum class decorator that checks user-selectable constraints on an enumeration. - :func:`member` + :deco:`member` Make ``obj`` a member. Can be used as a decorator. - :func:`nonmember` + :deco:`nonmember` Do not make ``obj`` a member. Can be used as a decorator. - :func:`global_enum` + :deco:`global_enum` Modify the :class:`str() ` and :func:`repr` of an enum to show its members as belonging to the module instead of its class, @@ -153,6 +148,12 @@ Module Contents Return a list of all power-of-two integers contained in a flag. + :func:`enum.bin` + + Like built-in :func:`bin`, except negative values are represented in + two's complement, and the leading bit always indicates sign + (``0`` implies positive, ``1`` implies negative). + .. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto`` .. versionadded:: 3.11 ``StrEnum``, ``EnumCheck``, ``ReprEnum``, ``FlagBoundary``, ``property``, ``member``, ``nonmember``, ``global_enum``, ``show_flag_values`` @@ -160,7 +161,7 @@ Module Contents --------------- -Data Types +Data types ---------- @@ -175,6 +176,10 @@ Data Types final *enum*, as well as creating the enum members, properly handling duplicates, providing iteration over the enum class, etc. + .. versionadded:: 3.11 + + Before 3.11 ``EnumType`` was called ``EnumMeta``, which is still available as an alias. + .. method:: EnumType.__call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None) This method is called in two different ways: @@ -206,7 +211,7 @@ Data Types >>> Color.RED.value in Color True - .. versionchanged:: 3.12 + .. versionchanged:: 3.12 Before Python 3.12, a ``TypeError`` is raised if a non-Enum-member is used in a containment check. @@ -217,7 +222,7 @@ Data Types names of the members in *cls*:: >>> dir(Color) - ['BLUE', 'GREEN', 'RED', '__class__', '__contains__', '__doc__', '__getitem__', '__init_subclass__', '__iter__', '__len__', '__members__', '__module__', '__name__', '__qualname__'] + ['BLUE', 'GREEN', 'RED', '__class__', '__contains__', '__doc__', '__getitem__', '__init_subclass__', '__iter__', '__len__', '__members__', '__module__', '__name__', '__qualname__', '_generate_next_value_', '_missing_'] .. method:: EnumType.__getitem__(cls, name) @@ -235,7 +240,7 @@ Data Types .. method:: EnumType.__len__(cls) - Returns the number of member in *cls*:: + Returns the number of members in *cls*:: >>> len(Color) 3 @@ -251,20 +256,6 @@ Data Types >>> list(reversed(Color)) [, , ] - .. method:: EnumType._add_alias_ - - Adds a new name as an alias to an existing member. Raises a - :exc:`NameError` if the name is already assigned to a different member. - - .. method:: EnumType._add_value_alias_ - - Adds a new value as an alias to an existing member. Raises a - :exc:`ValueError` if the value is already linked with a different member. - - .. versionadded:: 3.11 - - Before 3.11 ``EnumType`` was called ``EnumMeta``, which is still available as an alias. - .. class:: Enum @@ -311,6 +302,28 @@ Data Types No longer used, kept for backward compatibility. (class attribute, removed during class creation). + The :attr:`~Enum._order_` attribute can be provided to help keep Python 2 / Python 3 code in sync. + It will be checked against the actual order of the enumeration and raise an error if the two do not match:: + + >>> class Color(Enum): + ... _order_ = 'RED GREEN BLUE' + ... RED = 1 + ... BLUE = 3 + ... GREEN = 2 + ... + Traceback (most recent call last): + ... + TypeError: member order does not match _order_: + ['RED', 'BLUE', 'GREEN'] + ['RED', 'GREEN', 'BLUE'] + + .. note:: + + In Python 2 code the :attr:`~Enum._order_` attribute is necessary as definition + order is lost before it can be recorded. + + .. versionadded:: 3.6 + .. attribute:: Enum._ignore_ ``_ignore_`` is only used during creation and is removed from the @@ -320,12 +333,15 @@ Data Types names will also be removed from the completed enumeration. See :ref:`TimePeriod ` for an example. + .. versionadded:: 3.7 + .. method:: Enum.__dir__(self) Returns ``['__class__', '__doc__', '__module__', 'name', 'value']`` and any public methods defined on *self.__class__*:: - >>> from datetime import date + >>> from enum import Enum + >>> import datetime as dt >>> class Weekday(Enum): ... MONDAY = 1 ... TUESDAY = 2 @@ -336,10 +352,10 @@ Data Types ... SUNDAY = 7 ... @classmethod ... def today(cls): - ... print('today is %s' % cls(date.today().isoweekday()).name) + ... print(f'today is {cls(dt.date.today().isoweekday()).name}') ... >>> dir(Weekday.SATURDAY) - ['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'today', 'value'] + ['__class__', '__doc__', '__eq__', '__hash__', '__module__', '_add_alias_', '_add_value_alias_', '_generate_next_value_', '_missing_', 'name', 'today', 'value'] .. method:: Enum._generate_next_value_(name, start, count, last_values) @@ -349,9 +365,18 @@ Data Types :last_values: A list of the previous values. A *staticmethod* that is used to determine the next value returned by - :class:`auto`:: + :class:`auto`. - >>> from enum import auto + .. note:: + For standard :class:`Enum` classes the next value chosen is the highest + value seen incremented by one. + + For :class:`Flag` classes the next value chosen will be the next highest + power-of-two. + + This method may be overridden, e.g.:: + + >>> from enum import auto, Enum >>> class PowersOfThree(Enum): ... @staticmethod ... def _generate_next_value_(name, start, count, last_values): @@ -362,6 +387,10 @@ Data Types >>> PowersOfThree.SECOND.value 9 + .. versionadded:: 3.6 + .. versionchanged:: 3.13 + Prior versions would use the last seen value instead of the highest value. + .. method:: Enum.__init__(self, *args, **kwds) By default, does nothing. If multiple values are given in the member @@ -383,7 +412,7 @@ Data Types A *classmethod* for looking up values not found in *cls*. By default it does nothing, but can be overridden to implement custom search behavior:: - >>> from enum import StrEnum + >>> from enum import auto, StrEnum >>> class Build(StrEnum): ... DEBUG = auto() ... OPTIMIZED = auto() @@ -400,6 +429,8 @@ Data Types >>> Build('deBUG') + .. versionadded:: 3.6 + .. method:: Enum.__new__(cls, *args, **kwds) By default, doesn't exist. If specified, either in the enum class @@ -422,6 +453,7 @@ Data Types Returns the string used for *repr()* calls. By default, returns the *Enum* name, member name, and value, but can be overridden:: + >>> from enum import auto, Enum >>> class OtherStyle(Enum): ... ALTERNATE = auto() ... OTHER = auto() @@ -438,6 +470,7 @@ Data Types Returns the string used for *str()* calls. By default, returns the *Enum* name and member name, but can be overridden:: + >>> from enum import auto, Enum >>> class OtherStyle(Enum): ... ALTERNATE = auto() ... OTHER = auto() @@ -453,6 +486,7 @@ Data Types Returns the string used for *format()* and *f-string* calls. By default, returns :meth:`__str__` return value, but can be overridden:: + >>> from enum import auto, Enum >>> class OtherStyle(Enum): ... ALTERNATE = auto() ... OTHER = auto() @@ -468,7 +502,32 @@ Data Types Using :class:`auto` with :class:`Enum` results in integers of increasing value, starting with ``1``. - .. versionchanged:: 3.12 Added :ref:`enum-dataclass-support` + .. versionchanged:: 3.12 Added :ref:`enum-dataclass-support`. + + .. method:: Enum._add_alias_ + + Adds a new name as an alias to an existing member:: + + >>> Color.RED._add_alias_("ERROR") + >>> Color.ERROR + + + Raises a :exc:`NameError` if the name is already assigned to a different member. + + .. versionadded:: 3.13 + + .. method:: Enum._add_value_alias_ + + Adds a new value as an alias to an existing member:: + + >>> Color.RED._add_value_alias_(42) + >>> Color(42) + + + | Raises a :exc:`ValueError` if the value is already linked with a different member. + | See :ref:`multi-value-enum` for an example. + + .. versionadded:: 3.13 .. class:: IntEnum @@ -570,7 +629,7 @@ Data Types >>> white in purple False - .. method:: __iter__(self): + .. method:: __iter__(self) Returns all contained non-alias members:: @@ -581,7 +640,7 @@ Data Types .. versionadded:: 3.11 - .. method:: __len__(self): + .. method:: __len__(self) Returns number of members in flag:: @@ -592,7 +651,7 @@ Data Types .. versionadded:: 3.11 - .. method:: __bool__(self): + .. method:: __bool__(self) Returns *True* if any members in flag, *False* otherwise:: @@ -629,7 +688,7 @@ Data Types >>> purple ^ Color.GREEN - .. method:: __invert__(self): + .. method:: __invert__(self) Returns all the flags in *type(self)* that are not in *self*:: @@ -865,6 +924,8 @@ Data Types --------------- +.. _enum-dunder-sunder: + Supported ``__dunder__`` names """""""""""""""""""""""""""""" @@ -872,17 +933,13 @@ Supported ``__dunder__`` names items. It is only available on the class. :meth:`~Enum.__new__`, if specified, must create and return the enum members; -it is also a very good idea to set the member's :attr:`!_value_` appropriately. +it is also a very good idea to set the member's :attr:`~Enum._value_` appropriately. Once all the members are created it is no longer used. Supported ``_sunder_`` names """""""""""""""""""""""""""" -- :meth:`~EnumType._add_alias_` -- adds a new name as an alias to an existing - member. -- :meth:`~EnumType._add_value_alias_` -- adds a new value as an alias to an - existing member. - :attr:`~Enum._name_` -- name of the member - :attr:`~Enum._value_` -- value of the member; can be set in ``__new__`` - :meth:`~Enum._missing_` -- a lookup function used when a value is not found; @@ -892,16 +949,14 @@ Supported ``_sunder_`` names from the final class - :attr:`~Enum._order_` -- no longer used, kept for backward compatibility (class attribute, removed during class creation) + - :meth:`~Enum._generate_next_value_` -- used to get an appropriate value for an enum member; may be overridden - .. note:: - - For standard :class:`Enum` classes the next value chosen is the highest - value seen incremented by one. - - For :class:`Flag` classes the next value chosen will be the next highest - power-of-two. +- :meth:`~Enum._add_alias_` -- adds a new name as an alias to an existing + member. +- :meth:`~Enum._add_value_alias_` -- adds a new value as an alias to an + existing member. - While ``_sunder_`` names are generally reserved for the further development of the :class:`Enum` class and can not be used, some are explicitly allowed: @@ -915,26 +970,27 @@ Supported ``_sunder_`` names --------------- -Utilities and Decorators +Utilities and decorators ------------------------ .. class:: auto *auto* can be used in place of a value. If used, the *Enum* machinery will call an :class:`Enum`'s :meth:`~Enum._generate_next_value_` to get an appropriate value. - For :class:`Enum` and :class:`IntEnum` that appropriate value will be the last value plus - one; for :class:`Flag` and :class:`IntFlag` it will be the first power-of-two greater - than the highest value; for :class:`StrEnum` it will be the lower-cased version of + For :class:`Enum` and :class:`IntEnum` that appropriate value will be the highest value seen + plus one; for :class:`Flag` and :class:`IntFlag` it will be the first power-of-two greater + than the highest value seen; for :class:`StrEnum` it will be the lower-cased version of the member's name. Care must be taken if mixing *auto()* with manually specified values. - *auto* instances are only resolved when at the top level of an assignment: + *auto* instances are only resolved when at the top level of an assignment, either by + itself or as part of a tuple: * ``FIRST = auto()`` will work (auto() is replaced with ``1``); * ``SECOND = auto(), -2`` will work (auto is replaced with ``2``, so ``2, -2`` is used to create the ``SECOND`` enum member; - * ``THREE = [auto(), -3]`` will *not* work (``, -3`` is used to - create the ``THREE`` enum member) + * ``THIRD = [auto(), -3]`` will *not* work (``[, -3]`` is used to + create the ``THIRD`` enum member) .. versionchanged:: 3.11.1 @@ -944,17 +1000,17 @@ Utilities and Decorators ``_generate_next_value_`` can be overridden to customize the values used by *auto*. - .. note:: in 3.13 the default ``_generate_next_value_`` will always return + .. note:: In version 3.13 the default ``_generate_next_value_`` will always return the highest member value incremented by 1, and will fail if any member is an incompatible type. .. decorator:: property - A decorator similar to the built-in *property*, but specifically for + A decorator similar to the built-in :deco:`property`, but specifically for enumerations. It allows member attributes to have the same names as members themselves. - .. note:: the *property* and the member must be defined in separate classes; + .. note:: The *property* and the member must be defined in separate classes; for example, the *value* and *name* attributes are defined in the *Enum* class, and *Enum* subclasses can define members with the names ``value`` and ``name``. @@ -1015,6 +1071,20 @@ Utilities and Decorators .. versionadded:: 3.11 +.. function:: bin(num, max_bits=None) + + Like built-in :func:`bin`, except negative values are represented in + two's complement, and the leading bit always indicates sign + (``0`` implies positive, ``1`` implies negative). + + >>> import enum + >>> enum.bin(10) + '0b0 1010' + >>> enum.bin(~10) # ~10 is -11 + '0b1 0101' + + .. versionadded:: 3.11 + --------------- Notes diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index a73cc2e8a2dd3e..3775d5ac81a273 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -221,7 +221,7 @@ The following exceptions are the exceptions that are usually raised. .. exception:: EOFError Raised when the :func:`input` function hits an end-of-file condition (EOF) - without reading any data. (Note: the :meth:`!io.IOBase.read` and + without reading any data. (Note: the :meth:`io.TextIOBase.read` and :meth:`io.IOBase.readline` methods return an empty string when they hit EOF.) @@ -266,6 +266,12 @@ The following exceptions are the exceptions that are usually raised. .. versionadded:: 3.6 +.. exception:: ImportCycleError + + A subclass of :exc:`ImportError` which is raised when a lazy import fails + because it (directly or indirectly) tries to import itself. + + .. versionadded:: 3.15 .. exception:: IndexError @@ -450,7 +456,7 @@ The following exceptions are the exceptions that are usually raised. :meth:`threading.Thread.join` can now raise this exception. - .. versionchanged:: next + .. versionchanged:: 3.15 This exception may be raised when acquiring :meth:`threading.Lock` or :meth:`threading.RLock`. @@ -742,8 +748,8 @@ depending on the system error code. .. attribute:: characters_written - An integer containing the number of characters written to the stream - before it blocked. This attribute is available when using the + An integer containing the number of **bytes** written to the stream + before it blocked. This attribute is available when using the buffered I/O classes from the :mod:`io` module. .. exception:: ChildProcessError @@ -897,6 +903,9 @@ The following exceptions are used as warning categories; see the Base class for warnings about dubious syntax. + This warning is typically emitted when compiling Python source code, and usually won't be reported + when running already compiled code. + .. exception:: RuntimeWarning @@ -975,6 +984,15 @@ their subgroups based on the types of the contained exceptions. raises a :exc:`TypeError` if any contained exception is not an :exc:`Exception` subclass. + Exception groups are :ref:`generic ` over the type of their + contained exceptions. + + .. impl-detail:: + + The ``excs`` parameter may be any sequence, but lists and tuples are + specifically processed more efficiently here. For optimal performance, + pass a tuple as ``excs``. + .. attribute:: message The ``msg`` argument to the constructor. This is a read-only attribute. diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst index 677966a8b2eaab..905e1acd433617 100644 --- a/Doc/library/faulthandler.rst +++ b/Doc/library/faulthandler.rst @@ -31,7 +31,8 @@ tracebacks: * Each string is limited to 500 characters. * Only the filename, the function name and the line number are displayed. (no source code) -* It is limited to 100 frames and 100 threads. +* It is limited to 100 frames per thread, and 100 threads + (configurable via *max_threads*). * The order is reversed: the most recent call is shown first. By default, the Python traceback is written to :data:`sys.stderr`. To see @@ -55,16 +56,20 @@ at Python startup. Dumping the traceback --------------------- -.. function:: dump_traceback(file=sys.stderr, all_threads=True) +.. function:: dump_traceback(file=sys.stderr, all_threads=True, *, max_threads=100) Dump the tracebacks of all threads into *file*. If *all_threads* is - ``False``, dump only the current thread. + ``False``, dump only the current thread. *max_threads* caps the number + of threads dumped. .. seealso:: :func:`traceback.print_tb`, which can be used to print a traceback object. .. versionchanged:: 3.5 Added support for passing file descriptor to this function. + .. versionchanged:: 3.15 + Added the *max_threads* keyword argument. + Dumping the C stack ------------------- @@ -100,7 +105,7 @@ instead of the stack, even if the operating system supports dumping stacks. Fault handler state ------------------- -.. function:: enable(file=sys.stderr, all_threads=True, c_stack=True) +.. function:: enable(file=sys.stderr, all_threads=True, c_stack=True, *, max_threads=100) Enable the fault handler: install handlers for the :const:`~signal.SIGSEGV`, :const:`~signal.SIGFPE`, :const:`~signal.SIGABRT`, :const:`~signal.SIGBUS` @@ -116,6 +121,8 @@ Fault handler state traceback, unless the system does not support it. See :func:`dump_c_stack` for more information on compatibility. + *max_threads* caps the number of threads dumped when a fatal signal fires. + .. versionchanged:: 3.5 Added support for passing file descriptor to this function. @@ -133,6 +140,9 @@ Fault handler state .. versionchanged:: 3.14 The dump now displays the C stack trace if *c_stack* is true. + .. versionchanged:: 3.15 + Added the *max_threads* keyword argument. + .. function:: disable() Disable the fault handler: uninstall the signal handlers installed by @@ -146,7 +156,7 @@ Fault handler state Dumping the tracebacks after a timeout -------------------------------------- -.. function:: dump_traceback_later(timeout, repeat=False, file=sys.stderr, exit=False) +.. function:: dump_traceback_later(timeout, repeat=False, file=sys.stderr, exit=False, *, max_threads=100) Dump the tracebacks of all threads, after a timeout of *timeout* seconds, or every *timeout* seconds if *repeat* is ``True``. If *exit* is ``True``, call @@ -154,7 +164,7 @@ Dumping the tracebacks after a timeout :c:func:`!_exit` exits the process immediately, which means it doesn't do any cleanup like flushing file buffers.) If the function is called twice, the new call replaces previous parameters and resets the timeout. The timer has a - sub-second resolution. + sub-second resolution. *max_threads* caps the number of threads dumped. The *file* must be kept open until the traceback is dumped or :func:`cancel_dump_traceback_later` is called: see :ref:`issue with file @@ -168,6 +178,9 @@ Dumping the tracebacks after a timeout .. versionchanged:: 3.7 This function is now always available. + .. versionchanged:: 3.15 + Added the *max_threads* keyword argument. + .. function:: cancel_dump_traceback_later() Cancel the last call to :func:`dump_traceback_later`. @@ -176,11 +189,12 @@ Dumping the tracebacks after a timeout Dumping the traceback on a user signal -------------------------------------- -.. function:: register(signum, file=sys.stderr, all_threads=True, chain=False) +.. function:: register(signum, file=sys.stderr, all_threads=True, chain=False, *, max_threads=100) Register a user signal: install a handler for the *signum* signal to dump the traceback of all threads, or of the current thread if *all_threads* is ``False``, into *file*. Call the previous handler if chain is ``True``. + *max_threads* caps the number of threads dumped. The *file* must be kept open until the signal is unregistered by :func:`unregister`: see :ref:`issue with file descriptors `. @@ -190,6 +204,9 @@ Dumping the traceback on a user signal .. versionchanged:: 3.5 Added support for passing file descriptor to this function. + .. versionchanged:: 3.15 + Added the *max_threads* keyword argument. + .. function:: unregister(signum) Unregister a user signal: uninstall the handler of the *signum* signal diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index 5c078df44ffa8b..c28e4d6c0cc80c 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -2,11 +2,8 @@ ========================================================== .. module:: fcntl - :platform: Unix :synopsis: The fcntl() and ioctl() system calls. -.. sectionauthor:: Jaap Vermeulen - .. index:: pair: UNIX; file control pair: UNIX; I/O control @@ -53,7 +50,7 @@ descriptor. the latter setting ``FD_CLOEXEC`` flag in addition. .. versionchanged:: 3.12 - On Linux >= 4.5, the :mod:`fcntl` module exposes the ``FICLONE`` and + On Linux >= 4.5, the :mod:`!fcntl` module exposes the ``FICLONE`` and ``FICLONERANGE`` constants, which allow to share some data of one file with another file by reflinking on some filesystems (e.g., btrfs, OCFS2, and XFS). This behavior is commonly referred to as "copy-on-write". @@ -91,7 +88,7 @@ The module defines the following functions: Perform the operation *cmd* on file descriptor *fd* (file objects providing a :meth:`~io.IOBase.fileno` method are accepted as well). The values used for *cmd* are operating system dependent, and are available as constants - in the :mod:`fcntl` module, using the same names as used in the relevant C + in the :mod:`!fcntl` module, using the same names as used in the relevant C header files. The argument *arg* can either be an integer value, a :term:`bytes-like object`, or a string. The type and size of *arg* must match the type and size of @@ -125,7 +122,7 @@ The module defines the following functions: Add support of arbitrary :term:`bytes-like objects `, not only :class:`bytes`. - .. versionchanged:: next + .. versionchanged:: 3.15 The size of bytes-like objects is no longer limited to 1024 bytes. @@ -187,7 +184,7 @@ The module defines the following functions: The GIL is always released during a system call. System calls failing with EINTR are automatically retried. - .. versionchanged:: next + .. versionchanged:: 3.15 The size of not mutated bytes-like objects is no longer limited to 1024 bytes. diff --git a/Doc/library/filecmp.rst b/Doc/library/filecmp.rst index abd1b8c826d170..f8365b44c5a502 100644 --- a/Doc/library/filecmp.rst +++ b/Doc/library/filecmp.rst @@ -4,17 +4,15 @@ .. module:: filecmp :synopsis: Compare files efficiently. -.. sectionauthor:: Moshe Zadka - **Source code:** :source:`Lib/filecmp.py` -------------- -The :mod:`filecmp` module defines functions to compare files and directories, +The :mod:`!filecmp` module defines functions to compare files and directories, with various optional time/correctness trade-offs. For comparing files, see also the :mod:`difflib` module. -The :mod:`filecmp` module defines the following functions: +The :mod:`!filecmp` module defines the following functions: .. function:: cmp(f1, f2, shallow=True) diff --git a/Doc/library/fileinput.rst b/Doc/library/fileinput.rst index 8f32b11e565365..5be16797be908c 100644 --- a/Doc/library/fileinput.rst +++ b/Doc/library/fileinput.rst @@ -4,9 +4,6 @@ .. module:: fileinput :synopsis: Loop over standard input or a list of files. -.. moduleauthor:: Guido van Rossum -.. sectionauthor:: Fred L. Drake, Jr. - **Source code:** :source:`Lib/fileinput.py` -------------- diff --git a/Doc/library/fnmatch.rst b/Doc/library/fnmatch.rst index ee654b7a83e203..fc99b7fd1e7523 100644 --- a/Doc/library/fnmatch.rst +++ b/Doc/library/fnmatch.rst @@ -51,7 +51,7 @@ Unless stated otherwise, "filename string" and "pattern string" either refer to functions documented below do not allow to mix a :class:`!bytes` pattern with a :class:`!str` filename, and vice-versa. -Finally, note that :func:`functools.lru_cache` with a *maxsize* of 32768 +Finally, note that :deco:`functools.lru_cache` with a *maxsize* of 32768 is used to cache the (typed) compiled regex patterns in the following functions: :func:`fnmatch`, :func:`fnmatchcase`, :func:`.filter`, :func:`.filterfalse`. @@ -103,7 +103,8 @@ functions: :func:`fnmatch`, :func:`fnmatchcase`, :func:`.filter`, :func:`.filter .. function:: translate(pat) Return the shell-style pattern *pat* converted to a regular expression for - using with :func:`re.match`. The pattern is expected to be a :class:`str`. + using with :func:`re.prefixmatch`. The pattern is expected to be a + :class:`str`. Example: @@ -113,7 +114,7 @@ functions: :func:`fnmatch`, :func:`fnmatchcase`, :func:`.filter`, :func:`.filter >>> regex '(?s:.*\\.txt)\\z' >>> reobj = re.compile(regex) - >>> reobj.match('foobar.txt') + >>> reobj.prefixmatch('foobar.txt') diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst index 8796056b4b8722..b02e7b5b641136 100644 --- a/Doc/library/fractions.rst +++ b/Doc/library/fractions.rst @@ -4,18 +4,15 @@ .. module:: fractions :synopsis: Rational numbers. -.. moduleauthor:: Jeffrey Yasskin -.. sectionauthor:: Jeffrey Yasskin - **Source code:** :source:`Lib/fractions.py` -------------- -The :mod:`fractions` module provides support for rational number arithmetic. +The :mod:`!fractions` module provides support for rational number arithmetic. -A Fraction instance can be constructed from a pair of integers, from -another rational number, or from a string. +A Fraction instance can be constructed from a pair of rational numbers, from +a single number, or from a string. .. index:: single: as_integer_ratio() @@ -25,8 +22,8 @@ another rational number, or from a string. The first version requires that *numerator* and *denominator* are instances of :class:`numbers.Rational` and returns a new :class:`Fraction` instance - with value equal to ``numerator/denominator`` where the denominator is positive. - If *denominator* is ``0``, it raises a :exc:`ZeroDivisionError`. + with a value equal to ``numerator/denominator``. + If *denominator* is zero, it raises a :exc:`ZeroDivisionError`. The second version requires that *number* is an instance of :class:`numbers.Rational` or has the :meth:`!as_integer_ratio` method @@ -125,7 +122,8 @@ another rational number, or from a string. .. attribute:: denominator - Denominator of the Fraction in lowest term. + Denominator of the Fraction in lowest terms. + Guaranteed to be positive. .. method:: as_integer_ratio() diff --git a/Doc/library/frameworks.rst b/Doc/library/frameworks.rst index 15ceeec9c255ed..f8e2f6bb18cb1c 100644 --- a/Doc/library/frameworks.rst +++ b/Doc/library/frameworks.rst @@ -1,18 +1,13 @@ +:orphan: + .. _frameworks: ****************** -Program Frameworks +Program frameworks ****************** -The modules described in this chapter are frameworks that will largely dictate -the structure of your program. Currently the modules described here are all -oriented toward writing command-line interfaces. - -The full list of modules described in this chapter is: - - -.. toctree:: +This chapter is no longer maintained, and the modules it contained have been moved to their respective topical documentation. - turtle.rst - cmd.rst - shlex.rst +* :mod:`cmd` — :doc:`Command Line Interface Libraries <./cmdlinelibs>` +* :mod:`shlex` — :doc:`Unix Specific Services <./unix>` +* :mod:`turtle` — :doc:`Graphical User Interfaces with Tk <./tk>` diff --git a/Doc/library/ftplib.rst b/Doc/library/ftplib.rst index bb15322067245e..e1baeff3f373bf 100644 --- a/Doc/library/ftplib.rst +++ b/Doc/library/ftplib.rst @@ -23,7 +23,7 @@ The default encoding is UTF-8, following :rfc:`2640`. .. include:: ../includes/wasm-notavail.rst -Here's a sample session using the :mod:`ftplib` module:: +Here's a sample session using the :mod:`!ftplib` module:: >>> from ftplib import FTP >>> ftp = FTP('ftp.us.debian.org') # connect to host, default port @@ -524,14 +524,9 @@ FTP_TLS objects :class:`!FTP_TLS` class inherits from :class:`FTP`, defining these additional methods and attributes: - .. attribute:: FTP_TLS.ssl_version - - The SSL version to use (defaults to :data:`ssl.PROTOCOL_SSLv23`). - .. method:: FTP_TLS.auth() - Set up a secure control connection by using TLS or SSL, depending on what - is specified in the :attr:`ssl_version` attribute. + Set up a secure control connection by using TLS. .. versionchanged:: 3.4 The method now supports hostname check with @@ -548,7 +543,7 @@ FTP_TLS objects .. method:: FTP_TLS.prot_p() - Set up secure data connection. + Set up secure data connection by using TLS. .. method:: FTP_TLS.prot_c() diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 80bd1275973f8d..a4d37beb436899 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -19,24 +19,25 @@ are always available. They are listed here in alphabetical order. | | :func:`ascii` | | :func:`filter` | | :func:`map` | | **S** | | | | | :func:`float` | | :func:`max` | | |func-set|_ | | | **B** | | :func:`format` | | |func-memoryview|_ | | :func:`setattr` | -| | :func:`bin` | | |func-frozenset|_ | | :func:`min` | | :func:`slice` | -| | :func:`bool` | | | | | | :func:`sorted` | -| | :func:`breakpoint` | | **G** | | **N** | | :func:`staticmethod` | -| | |func-bytearray|_ | | :func:`getattr` | | :func:`next` | | |func-str|_ | -| | |func-bytes|_ | | :func:`globals` | | | | :func:`sum` | -| | | | | | **O** | | :func:`super` | -| | **C** | | **H** | | :func:`object` | | | -| | :func:`callable` | | :func:`hasattr` | | :func:`oct` | | **T** | -| | :func:`chr` | | :func:`hash` | | :func:`open` | | |func-tuple|_ | -| | :func:`classmethod` | | :func:`help` | | :func:`ord` | | :func:`type` | -| | :func:`compile` | | :func:`hex` | | | | | -| | :func:`complex` | | | | **P** | | **V** | -| | | | **I** | | :func:`pow` | | :func:`vars` | -| | **D** | | :func:`id` | | :func:`print` | | | -| | :func:`delattr` | | :func:`input` | | :func:`property` | | **Z** | -| | |func-dict|_ | | :func:`int` | | | | :func:`zip` | -| | :func:`dir` | | :func:`isinstance` | | | | | -| | :func:`divmod` | | :func:`issubclass` | | | | **_** | +| | :func:`bin` | | |func-frozendict|_ | | :func:`min` | | :func:`sentinel` | +| | :func:`bool` | | |func-frozenset|_ | | | | :func:`slice` | +| | :func:`breakpoint` | | | | **N** | | :func:`sorted` | +| | |func-bytearray|_ | | **G** | | :func:`next` | | :func:`staticmethod` | +| | |func-bytes|_ | | :func:`getattr` | | | | |func-str|_ | +| | | | :func:`globals` | | **O** | | :func:`sum` | +| | **C** | | | | :func:`object` | | :func:`super` | +| | :func:`callable` | | **H** | | :func:`oct` | | | +| | :func:`chr` | | :func:`hasattr` | | :func:`open` | | **T** | +| | :func:`classmethod` | | :func:`hash` | | :func:`ord` | | |func-tuple|_ | +| | :func:`compile` | | :func:`help` | | | | :func:`type` | +| | :func:`complex` | | :func:`hex` | | **P** | | | +| | | | | | :func:`pow` | | **V** | +| | **D** | | **I** | | :func:`print` | | :func:`vars` | +| | :func:`delattr` | | :func:`id` | | :func:`property` | | | +| | |func-dict|_ | | :func:`input` | | | | **Z** | +| | :func:`dir` | | :func:`int` | | | | :func:`zip` | +| | :func:`divmod` | | :func:`isinstance` | | | | | +| | | | :func:`issubclass` | | | | **_** | | | | | :func:`iter` | | | | :func:`__import__` | +-------------------------+-----------------------+-----------------------+-------------------------+ @@ -44,6 +45,7 @@ are always available. They are listed here in alphabetical order. used, with replacement texts to make the output in the table consistent .. |func-dict| replace:: ``dict()`` +.. |func-frozendict| replace:: ``frozendict()`` .. |func-frozenset| replace:: ``frozenset()`` .. |func-memoryview| replace:: ``memoryview()`` .. |func-set| replace:: ``set()`` @@ -54,7 +56,7 @@ are always available. They are listed here in alphabetical order. .. |func-bytearray| replace:: ``bytearray()`` .. |func-bytes| replace:: ``bytes()`` -.. function:: abs(x) +.. function:: abs(number, /) Return the absolute value of a number. The argument may be an integer, a floating-point number, or an object implementing @@ -62,7 +64,7 @@ are always available. They are listed here in alphabetical order. If the argument is a complex number, its magnitude is returned. -.. function:: aiter(async_iterable) +.. function:: aiter(async_iterable, /) Return an :term:`asynchronous iterator` for an :term:`asynchronous iterable`. Equivalent to calling ``x.__aiter__()``. @@ -71,7 +73,7 @@ are always available. They are listed here in alphabetical order. .. versionadded:: 3.10 -.. function:: all(iterable) +.. function:: all(iterable, /) Return ``True`` if all elements of the *iterable* are true (or if the iterable is empty). Equivalent to:: @@ -83,8 +85,8 @@ are always available. They are listed here in alphabetical order. return True -.. awaitablefunction:: anext(async_iterator) - anext(async_iterator, default) +.. awaitablefunction:: anext(async_iterator, /) + anext(async_iterator, default, /) When awaited, return the next item from the given :term:`asynchronous iterator`, or *default* if given and the iterator is exhausted. @@ -99,7 +101,7 @@ are always available. They are listed here in alphabetical order. .. versionadded:: 3.10 -.. function:: any(iterable) +.. function:: any(iterable, /) Return ``True`` if any element of the *iterable* is true. If the iterable is empty, return ``False``. Equivalent to:: @@ -111,7 +113,7 @@ are always available. They are listed here in alphabetical order. return False -.. function:: ascii(object) +.. function:: ascii(object, /) As :func:`repr`, return a string containing a printable representation of an object, but escape the non-ASCII characters in the string returned by @@ -119,10 +121,10 @@ are always available. They are listed here in alphabetical order. similar to that returned by :func:`repr` in Python 2. -.. function:: bin(x) +.. function:: bin(integer, /) Convert an integer number to a binary string prefixed with "0b". The result - is a valid Python expression. If *x* is not a Python :class:`int` object, it + is a valid Python expression. If *integer* is not a Python :class:`int` object, it has to define an :meth:`~object.__index__` method that returns an integer. Some examples: @@ -138,6 +140,8 @@ are always available. They are listed here in alphabetical order. >>> f'{14:#b}', f'{14:b}' ('0b1110', '1110') + See also :func:`enum.bin` to represent negative values as twos-complement. + See also :func:`format` for more information. @@ -183,8 +187,7 @@ are always available. They are listed here in alphabetical order. .. _func-bytearray: .. class:: bytearray(source=b'') - bytearray(source, encoding) - bytearray(source, encoding, errors) + bytearray(source, encoding, errors='strict') :noindex: Return a new array of bytes. The :class:`bytearray` class is a mutable @@ -215,8 +218,7 @@ are always available. They are listed here in alphabetical order. .. _func-bytes: .. class:: bytes(source=b'') - bytes(source, encoding) - bytes(source, encoding, errors) + bytes(source, encoding, errors='strict') :noindex: Return a new "bytes" object which is an immutable sequence of integers in @@ -231,7 +233,7 @@ are always available. They are listed here in alphabetical order. See also :ref:`binaryseq`, :ref:`typebytes`, and :ref:`bytes-methods`. -.. function:: callable(object) +.. function:: callable(object, /) Return :const:`True` if the *object* argument appears callable, :const:`False` if not. If this returns ``True``, it is still possible that a @@ -244,14 +246,14 @@ are always available. They are listed here in alphabetical order. in Python 3.2. -.. function:: chr(i) +.. function:: chr(codepoint, /) - Return the string representing a character whose Unicode code point is the - integer *i*. For example, ``chr(97)`` returns the string ``'a'``, while + Return the string representing a character with the specified Unicode code point. + For example, ``chr(97)`` returns the string ``'a'``, while ``chr(8364)`` returns the string ``'€'``. This is the inverse of :func:`ord`. The valid range for the argument is from 0 through 1,114,111 (0x10FFFF in - base 16). :exc:`ValueError` will be raised if *i* is outside that range. + base 16). :exc:`ValueError` will be raised if it is outside that range. .. decorator:: classmethod @@ -294,7 +296,9 @@ are always available. They are listed here in alphabetical order. :func:`property`. -.. function:: compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1) +.. function:: compile(source, filename, mode, flags=0, \ + dont_inherit=False, optimize=-1, \ + *, module=None) Compile the *source* into a code or AST object. Code objects can be executed by :func:`exec` or :func:`eval`. *source* can either be a normal string, a @@ -336,8 +340,12 @@ are always available. They are listed here in alphabetical order. ``__debug__`` is true), ``1`` (asserts are removed, ``__debug__`` is false) or ``2`` (docstrings are removed too). - This function raises :exc:`SyntaxError` if the compiled source is invalid, - and :exc:`ValueError` if the source contains null bytes. + The optional argument *module* specifies the module name. + It is needed to unambiguous :ref:`filter ` syntax warnings + by module name. + + This function raises :exc:`SyntaxError` or :exc:`ValueError` if the compiled + source is invalid. If you want to parse Python code into its AST representation, see :func:`ast.parse`. @@ -373,6 +381,9 @@ are always available. They are listed here in alphabetical order. ``ast.PyCF_ALLOW_TOP_LEVEL_AWAIT`` can now be passed in flags to enable support for top-level ``await``, ``async for``, and ``async with``. + .. versionadded:: 3.15 + Added the *module* parameter. + .. class:: complex(number=0, /) complex(string, /) @@ -458,7 +469,7 @@ are always available. They are listed here in alphabetical order. deprecated; it should only be passed as a single positional argument. -.. function:: delattr(object, name) +.. function:: delattr(object, name, /) This is a relative of :func:`setattr`. The arguments are an object and a string. The string must be the name of one of the object's attributes. The @@ -468,20 +479,20 @@ are always available. They are listed here in alphabetical order. .. _func-dict: -.. class:: dict(**kwarg) - dict(mapping, **kwarg) - dict(iterable, **kwarg) +.. class:: dict(**kwargs) + dict(mapping, /, **kwargs) + dict(iterable, /, **kwargs) :noindex: Create a new dictionary. The :class:`dict` object is the dictionary class. - See :class:`dict` and :ref:`typesmapping` for documentation about this class. + See also :ref:`typesmapping` for documentation about this class. - For other containers see the built-in :class:`list`, :class:`set`, and - :class:`tuple` classes, as well as the :mod:`collections` module. + For other containers see the built-in :class:`frozendict`, :class:`list`, + :class:`set`, and :class:`tuple` classes, as well as the :mod:`collections` module. .. function:: dir() - dir(object) + dir(object, /) Without arguments, return the list of names in the current local scope. With an argument, attempt to return a list of valid attributes for that object. @@ -519,7 +530,7 @@ are always available. They are listed here in alphabetical order. >>> dir() # show the names in the module namespace # doctest: +SKIP ['__builtins__', '__name__', 'struct'] >>> dir(struct) # show the names in the struct module # doctest: +SKIP - ['Struct', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', + ['Struct', '__all__', '__builtins__', '__doc__', '__file__', '__initializing__', '__loader__', '__name__', '__package__', '_clearcache', 'calcsize', 'error', 'pack', 'pack_into', 'unpack', 'unpack_from'] @@ -541,7 +552,7 @@ are always available. They are listed here in alphabetical order. class. -.. function:: divmod(a, b) +.. function:: divmod(a, b, /) Take two (non-complex) numbers as arguments and return a pair of numbers consisting of their quotient and remainder when using integer division. With @@ -585,7 +596,7 @@ are always available. They are listed here in alphabetical order. :param globals: The global namespace (default: ``None``). - :type globals: :class:`dict` | ``None`` + :type globals: :class:`dict` | :class:`frozendict` | ``None`` :param locals: The local namespace (default: ``None``). @@ -597,18 +608,19 @@ are always available. They are listed here in alphabetical order. .. warning:: This function executes arbitrary code. Calling it with - user-supplied input may lead to security vulnerabilities. + untrusted user-supplied input will lead to security vulnerabilities. - The *expression* argument is parsed and evaluated as a Python expression + The *source* argument is parsed and evaluated as a Python expression (technically speaking, a condition list) using the *globals* and *locals* mappings as global and local namespace. If the *globals* dictionary is present and does not contain a value for the key ``__builtins__``, a reference to the dictionary of the built-in module :mod:`builtins` is - inserted under that key before *expression* is parsed. That way you can - control what builtins are available to the executed code by inserting your - own ``__builtins__`` dictionary into *globals* before passing it to - :func:`eval`. If the *locals* mapping is omitted it defaults to the - *globals* dictionary. If both mappings are omitted, the expression is + inserted under that key before *source* is parsed. + Overriding ``__builtins__`` can be used to restrict or change the available + names, but this is **not** a security mechanism: the executed code can + still access all builtins. + If the *locals* mapping is omitted it defaults to the + *globals* dictionary. If both mappings are omitted, the source is executed with the *globals* and *locals* in the environment where :func:`eval` is called. Note, *eval()* will only have access to the :term:`nested scopes ` (non-locals) in the enclosing @@ -634,7 +646,7 @@ are always available. They are listed here in alphabetical order. If the given source is a string, then leading and trailing spaces and tabs are stripped. - See :func:`ast.literal_eval` for a function that can safely evaluate strings + See :func:`ast.literal_eval` for a function to evaluate strings with expressions containing only literals. .. audit-event:: exec code_object eval @@ -651,6 +663,10 @@ are always available. They are listed here in alphabetical order. The semantics of the default *locals* namespace have been adjusted as described for the :func:`locals` builtin. + .. versionchanged:: 3.15 + + *globals* can now be a :class:`frozendict`. + .. index:: pair: built-in function; exec .. function:: exec(source, /, globals=None, locals=None, *, closure=None) @@ -658,7 +674,7 @@ are always available. They are listed here in alphabetical order. .. warning:: This function executes arbitrary code. Calling it with - user-supplied input may lead to security vulnerabilities. + untrusted user-supplied input will lead to security vulnerabilities. This function supports dynamic execution of Python code. *source* must be either a string or a code object. If it is a string, the string is parsed as @@ -689,9 +705,10 @@ are always available. They are listed here in alphabetical order. If the *globals* dictionary does not contain a value for the key ``__builtins__``, a reference to the dictionary of the built-in module - :mod:`builtins` is inserted under that key. That way you can control what - builtins are available to the executed code by inserting your own - ``__builtins__`` dictionary into *globals* before passing it to :func:`exec`. + :mod:`builtins` is inserted under that key. + Overriding ``__builtins__`` can be used to restrict or change the available + names, but this is **not** a security mechanism: the executed code can + still access all builtins. The *closure* argument specifies a closure--a tuple of cellvars. It's only valid when the *object* is a code object containing @@ -728,8 +745,12 @@ are always available. They are listed here in alphabetical order. The semantics of the default *locals* namespace have been adjusted as described for the :func:`locals` builtin. + .. versionchanged:: 3.15 + + *globals* can now be a :class:`frozendict`. + -.. function:: filter(function, iterable) +.. function:: filter(function, iterable, /) Construct an iterator from those elements of *iterable* for which *function* is true. *iterable* may be either a sequence, a container which @@ -823,7 +844,7 @@ are always available. They are listed here in alphabetical order. single: __format__ single: string; format() (built-in function) -.. function:: format(value, format_spec="") +.. function:: format(value, format_spec="", /) Convert a *value* to a "formatted" representation, as controlled by *format_spec*. The interpretation of *format_spec* will depend on the type @@ -845,12 +866,27 @@ are always available. They are listed here in alphabetical order. if *format_spec* is not an empty string. +.. _func-frozendict: +.. class:: frozendict(**kwargs) + frozendict(mapping, /, **kwargs) + frozendict(iterable, /, **kwargs) + :noindex: + + Create a new frozen dictionary. The :class:`frozendict` object is a built-in class. + See also :ref:`typesmapping` for documentation about this class. + + For other containers see the built-in :class:`dict`, :class:`list`, :class:`set`, + and :class:`tuple` classes, as well as the :mod:`collections` module. + + .. versionadded:: 3.15 + + .. _func-frozenset: -.. class:: frozenset(iterable=set()) +.. class:: frozenset(iterable=(), /) :noindex: Return a new :class:`frozenset` object, optionally with elements taken from - *iterable*. ``frozenset`` is a built-in class. See :class:`frozenset` and + *iterable*. :class:`frozenset` is a built-in class. See also :ref:`types-set` for documentation about this class. For other containers see the built-in :class:`set`, :class:`list`, @@ -858,8 +894,8 @@ are always available. They are listed here in alphabetical order. module. -.. function:: getattr(object, name) - getattr(object, name, default) +.. function:: getattr(object, name, /) + getattr(object, name, default, /) Return the value of the named attribute of *object*. *name* must be a string. If the string is the name of one of the object's attributes, the result is the @@ -883,7 +919,7 @@ are always available. They are listed here in alphabetical order. regardless of where the function is called. -.. function:: hasattr(object, name) +.. function:: hasattr(object, name, /) The arguments are an object and a string. The result is ``True`` if the string is the name of one of the object's attributes, ``False`` if not. (This @@ -891,7 +927,7 @@ are always available. They are listed here in alphabetical order. raises an :exc:`AttributeError` or not.) -.. function:: hash(object) +.. function:: hash(object, /) Return the hash value of the object (if it has one). Hash values are integers. They are used to quickly compare dictionary keys during a @@ -926,10 +962,10 @@ are always available. They are listed here in alphabetical order. signatures for callables are now more comprehensive and consistent. -.. function:: hex(x) +.. function:: hex(integer, /) Convert an integer number to a lowercase hexadecimal string prefixed with - "0x". If *x* is not a Python :class:`int` object, it has to define an + "0x". If *integer* is not a Python :class:`int` object, it has to define an :meth:`~object.__index__` method that returns an integer. Some examples: >>> hex(255) @@ -958,7 +994,7 @@ are always available. They are listed here in alphabetical order. :meth:`float.hex` method. -.. function:: id(object) +.. function:: id(object, /) Return the "identity" of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. @@ -971,7 +1007,7 @@ are always available. They are listed here in alphabetical order. .. function:: input() - input(prompt) + input(prompt, /) If the *prompt* argument is present, it is written to standard output without a trailing newline. The function then reads a line from input, converts it @@ -1071,7 +1107,7 @@ are always available. They are listed here in alphabetical order. .. versionchanged:: 3.14 :func:`int` no longer delegates to the :meth:`~object.__trunc__` method. -.. function:: isinstance(object, classinfo) +.. function:: isinstance(object, classinfo, /) Return ``True`` if the *object* argument is an instance of the *classinfo* argument, or of a (direct, indirect, or :term:`virtual `) of *classinfo*. A class is considered a subclass of itself. *classinfo* may be a tuple of class objects (or recursively, other such tuples) - or a :ref:`types-union`, in which case return ``True`` if *class* is a + or a :ref:`types-union`, in which case return ``True`` if *cls* is a subclass of any entry in *classinfo*. In any other case, a :exc:`TypeError` exception is raised. @@ -1102,19 +1138,19 @@ are always available. They are listed here in alphabetical order. *classinfo* can be a :ref:`types-union`. -.. function:: iter(object) - iter(object, sentinel) +.. function:: iter(iterable, /) + iter(callable, sentinel, /) Return an :term:`iterator` object. The first argument is interpreted very differently depending on the presence of the second argument. Without a - second argument, *object* must be a collection object which supports the + second argument, the single argument must be a collection object which supports the :term:`iterable` protocol (the :meth:`~object.__iter__` method), or it must support the sequence protocol (the :meth:`~object.__getitem__` method with integer arguments starting at ``0``). If it does not support either of those protocols, :exc:`TypeError` is raised. If the second argument, *sentinel*, is given, - then *object* must be a callable object. The iterator created in this case - will call *object* with no arguments for each call to its + then the first argument must be a callable object. The iterator created in this case + will call *callable* with no arguments for each call to its :meth:`~iterator.__next__` method; if the value returned is equal to *sentinel*, :exc:`StopIteration` will be raised, otherwise the value will be returned. @@ -1131,7 +1167,7 @@ are always available. They are listed here in alphabetical order. process_block(block) -.. function:: len(s) +.. function:: len(object, /) Return the length (the number of items) of an object. The argument may be a sequence (such as a string, bytes, tuple, list, or range) or a collection @@ -1144,8 +1180,7 @@ are always available. They are listed here in alphabetical order. .. _func-list: -.. class:: list() - list(iterable) +.. class:: list(iterable=(), /) :noindex: Rather than being a function, :class:`list` is actually a mutable @@ -1220,9 +1255,9 @@ are always available. They are listed here in alphabetical order. Added the *strict* parameter. -.. function:: max(iterable, *, key=None) - max(iterable, *, default, key=None) - max(arg1, arg2, *args, key=None) +.. function:: max(iterable, /, *, key=None) + max(iterable, /, *, default, key=None) + max(arg1, arg2, /, *args, key=None) Return the largest item in an iterable or the largest of two or more arguments. @@ -1258,9 +1293,9 @@ are always available. They are listed here in alphabetical order. :ref:`typememoryview` for more information. -.. function:: min(iterable, *, key=None) - min(iterable, *, default, key=None) - min(arg1, arg2, *args, key=None) +.. function:: min(iterable, /, *, key=None) + min(iterable, /, *, default, key=None) + min(arg1, arg2, /, *args, key=None) Return the smallest item in an iterable or the smallest of two or more arguments. @@ -1288,8 +1323,8 @@ are always available. They are listed here in alphabetical order. The *key* can be ``None``. -.. function:: next(iterator) - next(iterator, default) +.. function:: next(iterator, /) + next(iterator, default, /) Retrieve the next item from the :term:`iterator` by calling its :meth:`~iterator.__next__` method. If *default* is given, it is returned @@ -1310,10 +1345,10 @@ are always available. They are listed here in alphabetical order. :class:`object`. -.. function:: oct(x) +.. function:: oct(integer, /) Convert an integer number to an octal string prefixed with "0o". The result - is a valid Python expression. If *x* is not a Python :class:`int` object, it + is a valid Python expression. If *integer* is not a Python :class:`int` object, it has to define an :meth:`~object.__index__` method that returns an integer. For example: @@ -1422,38 +1457,10 @@ are always available. They are listed here in alphabetical order. *errors* is an optional string that specifies how encoding and decoding errors are to be handled—this cannot be used in binary mode. - A variety of standard error handlers are available - (listed under :ref:`error-handlers`), though any - error handling name that has been registered with + A variety of standard error handlers are available, + though any error handling name that has been registered with :func:`codecs.register_error` is also valid. The standard names - include: - - * ``'strict'`` to raise a :exc:`ValueError` exception if there is - an encoding error. The default value of ``None`` has the same - effect. - - * ``'ignore'`` ignores errors. Note that ignoring encoding errors - can lead to data loss. - - * ``'replace'`` causes a replacement marker (such as ``'?'``) to be inserted - where there is malformed data. - - * ``'surrogateescape'`` will represent any incorrect bytes as low - surrogate code units ranging from U+DC80 to U+DCFF. - These surrogate code units will then be turned back into - the same bytes when the ``surrogateescape`` error handler is used - when writing data. This is useful for processing files in an - unknown encoding. - - * ``'xmlcharrefreplace'`` is only supported when writing to a file. - Characters not supported by the encoding are replaced with the - appropriate XML character reference :samp:`&#{nnn};`. - - * ``'backslashreplace'`` replaces malformed data by Python's backslashed - escape sequences. - - * ``'namereplace'`` (also only supported when writing) - replaces unsupported characters with ``\N{...}`` escape sequences. + can be found in :ref:`error-handlers`. .. index:: single: universal newlines; open() built-in function @@ -1562,13 +1569,19 @@ are always available. They are listed here in alphabetical order. .. versionchanged:: 3.11 The ``'U'`` mode has been removed. -.. function:: ord(c) +.. function:: ord(character, /) + + Return the ordinal value of a character. - Given a string representing one Unicode character, return an integer - representing the Unicode code point of that character. For example, + If the argument is a one-character string, return the Unicode code point + of that character. For example, ``ord('a')`` returns the integer ``97`` and ``ord('€')`` (Euro sign) returns ``8364``. This is the inverse of :func:`chr`. + If the argument is a :class:`bytes` or :class:`bytearray` object of + length 1, return its single byte value. + For example, ``ord(b'a')`` returns the integer ``97``. + .. function:: pow(base, exp, mod=None) @@ -1577,7 +1590,7 @@ are always available. They are listed here in alphabetical order. ``pow(base, exp) % mod``). The two-argument form ``pow(base, exp)`` is equivalent to using the power operator: ``base**exp``. - The arguments must have numeric types. With mixed operand types, the + When arguments are builtin numeric types with mixed operand types, the coercion rules for binary arithmetic operators apply. For :class:`int` operands, the result has the same type as the operands (after coercion) unless the second argument is negative; in that case, all arguments are @@ -1667,7 +1680,7 @@ are always available. They are listed here in alphabetical order. If given, *doc* will be the docstring of the property attribute. Otherwise, the property will copy *fget*'s docstring (if it exists). This makes it possible to - create read-only properties easily using :func:`property` as a :term:`decorator`:: + create read-only properties easily using :deco:`property` as a :term:`decorator`:: class Parrot: def __init__(self): @@ -1729,15 +1742,15 @@ are always available. They are listed here in alphabetical order. .. _func-range: -.. class:: range(stop) - range(start, stop, step=1) +.. class:: range(stop, /) + range(start, stop, step=1, /) :noindex: Rather than being a function, :class:`range` is actually an immutable sequence type, as documented in :ref:`typesseq-range` and :ref:`typesseq`. -.. function:: repr(object) +.. function:: repr(object, /) Return a string containing a printable representation of an object. For many types, this function makes an attempt to return a string that would yield an @@ -1758,12 +1771,12 @@ are always available. They are listed here in alphabetical order. self.age = age def __repr__(self): - return f"Person('{self.name}', {self.age})" + return f"Person({self.name!r}, {self.age!r})" -.. function:: reversed(seq) +.. function:: reversed(object, /) - Return a reverse :term:`iterator`. *seq* must be an object which has + Return a reverse :term:`iterator`. The argument must be an object which has a :meth:`~object.__reversed__` method or supports the sequence protocol (the :meth:`~object.__len__` method and the :meth:`~object.__getitem__` method with integer arguments starting at ``0``). @@ -1797,12 +1810,11 @@ are always available. They are listed here in alphabetical order. .. _func-set: -.. class:: set() - set(iterable) +.. class:: set(iterable=(), /) :noindex: Return a new :class:`set` object, optionally with elements taken from - *iterable*. ``set`` is a built-in class. See :class:`set` and + *iterable*. :class:`set` is a built-in class. See also :ref:`types-set` for documentation about this class. For other containers see the built-in :class:`frozenset`, :class:`list`, @@ -1810,7 +1822,7 @@ are always available. They are listed here in alphabetical order. module. -.. function:: setattr(object, name, value) +.. function:: setattr(object, name, value, /) This is the counterpart of :func:`getattr`. The arguments are an object, a string, and an arbitrary value. The string may name an existing attribute or a @@ -1832,26 +1844,89 @@ are always available. They are listed here in alphabetical order. :func:`setattr`. -.. class:: slice(stop) - slice(start, stop, step=None) +.. class:: sentinel(name, /, *, repr=None) + + Return a new unique sentinel object. *name* must be a :class:`str`, and is + used by default as the returned object's representation:: + + >>> MISSING = sentinel("MISSING") + >>> MISSING + MISSING + + The optional *repr* argument can be used to specify a different representation:: + + >>> MISSING = sentinel("MISSING", repr="") + >>> MISSING + + + Sentinel objects are truthy and compare equal only to themselves. They are + intended to be compared with the :keyword:`is` operator. + + ``sentinel`` does not support subclassing. + + Shallow and deep copies of a sentinel object return the object itself. + + Sentinels are conventionally assigned to a variable with a matching name. + Sentinels defined in this way can be used in :term:`type hints `:: + + MISSING = sentinel("MISSING") + + def next_value(default: int | MISSING = MISSING): + ... + + Sentinel objects support the :ref:`| ` operator for use in type expressions. + + :mod:`Pickling ` is supported for sentinel objects that are + placed in the global scope of a module under a name matching the sentinel's + name, and for sentinels placed in class scopes with a name matching the + :term:`qualified name` of the sentinel. Other sentinels, such as those + defined in a function scope, are not picklable. The identity of the sentinel is preserved + after pickling:: + + import pickle + + PICKLABLE = sentinel("PICKLABLE") + + assert pickle.loads(pickle.dumps(PICKLABLE)) is PICKLABLE + + class Cls: + PICKLABLE = sentinel("Cls.PICKLABLE") + + assert pickle.loads(pickle.dumps(Cls.PICKLABLE)) is Cls.PICKLABLE + + Sentinel objects have the following attributes: + + .. attribute:: __name__ + + The sentinel's name. + + .. attribute:: __module__ + + The name of the module where the sentinel was created. This attribute is writable. + + .. versionadded:: 3.15 + + +.. class:: slice(stop, /) + slice(start, stop, step=None, /) Return a :term:`slice` object representing the set of indices specified by ``range(start, stop, step)``. The *start* and *step* arguments default to ``None``. - Slice objects have read-only data attributes :attr:`!start`, - :attr:`!stop`, and :attr:`!step` which merely return the argument - values (or their default). They have no other explicit functionality; - however, they are used by NumPy and other third-party packages. + Slice objects are also generated when :ref:`slicing syntax ` + is used. For example: ``a[start:stop:step]`` or ``a[start:stop, i]``. + + See :func:`itertools.islice` for an alternate version that returns an + :term:`iterator`. .. attribute:: slice.start - .. attribute:: slice.stop - .. attribute:: slice.step + slice.stop + slice.step - Slice objects are also generated when extended indexing syntax is used. For - example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See - :func:`itertools.islice` for an alternate version that returns an - :term:`iterator`. + These read-only attributes are set to the argument values + (or their default). They have no other explicit functionality; + however, they are used by NumPy and other third-party packages. .. versionchanged:: 3.12 Slice objects are now :term:`hashable` (provided :attr:`~slice.start`, @@ -1885,7 +1960,7 @@ are always available. They are listed here in alphabetical order. the same data with other ordering tools such as :func:`max` that rely on a different underlying method. Implementing all six comparisons also helps avoid confusion for mixed type comparisons which can call - reflected the :meth:`~object.__gt__` method. + the reflected :meth:`~object.__gt__` method. For sorting examples and a brief sorting tutorial, see :ref:`sortinghowto`. @@ -1909,7 +1984,7 @@ are always available. They are listed here in alphabetical order. be used in the class definition (such as ``f()``). Static methods in Python are similar to those found in Java or C++. Also, see - :func:`classmethod` for a variant that is useful for creating alternate class + :deco:`classmethod` for a variant that is useful for creating alternate class constructors. Like all decorators, it is also possible to call ``staticmethod`` as @@ -1938,8 +2013,10 @@ are always available. They are listed here in alphabetical order. single: string; str() (built-in function) .. _func-str: -.. class:: str(object='') - str(object=b'', encoding='utf-8', errors='strict') +.. class:: str(*, encoding='utf-8', errors='strict') + str(object) + str(object, encoding, errors='strict') + str(object, *, errors) :noindex: Return a :class:`str` version of *object*. See :func:`str` for details. @@ -1972,7 +2049,7 @@ are always available. They are listed here in alphabetical order. .. class:: super() - super(type, object_or_type=None) + super(type, object_or_type=None, /) Return a proxy object that delegates method calls to a parent or sibling class of *type*. This is useful for accessing inherited methods that have @@ -2054,16 +2131,15 @@ are always available. They are listed here in alphabetical order. .. _func-tuple: -.. class:: tuple() - tuple(iterable) +.. class:: tuple(iterable=(), /) :noindex: Rather than being a function, :class:`tuple` is actually an immutable sequence type, as documented in :ref:`typesseq-tuple` and :ref:`typesseq`. -.. class:: type(object) - type(name, bases, dict, **kwds) +.. class:: type(object, /) + type(name, bases, dict, /, **kwargs) .. index:: pair: object; type @@ -2105,8 +2181,12 @@ are always available. They are listed here in alphabetical order. Subclasses of :class:`!type` which don't override ``type.__new__`` may no longer use the one-argument form to get the type of an object. + .. versionchanged:: 3.15 + + *dict* can now be a :class:`frozendict`. + .. function:: vars() - vars(object) + vars(object, /) Return the :attr:`~object.__dict__` attribute for a module, class, instance, or any other object with a :attr:`!__dict__` attribute. diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst index beec9b942afc0f..2b46978f058102 100644 --- a/Doc/library/functools.rst +++ b/Doc/library/functools.rst @@ -4,13 +4,6 @@ .. module:: functools :synopsis: Higher-order functions and operations on callable objects. -.. moduleauthor:: Peter Harris -.. moduleauthor:: Raymond Hettinger -.. moduleauthor:: Nick Coghlan -.. moduleauthor:: Łukasz Langa -.. moduleauthor:: Pablo Galindo -.. sectionauthor:: Peter Harris - **Source code:** :source:`Lib/functools.py` .. testsetup:: default @@ -20,11 +13,11 @@ -------------- -The :mod:`functools` module is for higher-order functions: functions that act on +The :mod:`!functools` module is for higher-order functions: functions that act on or return other functions. In general, any callable object can be treated as a function for the purposes of this module. -The :mod:`functools` module defines the following functions: +The :mod:`!functools` module defines the following functions: .. decorator:: cache(user_function) @@ -34,7 +27,7 @@ The :mod:`functools` module defines the following functions: Returns the same as ``lru_cache(maxsize=None)``, creating a thin wrapper around a dictionary lookup for the function arguments. Because it never needs to evict old values, this is smaller and faster than - :func:`lru_cache` with a size limit. + :deco:`lru_cache` with a size limit. For example:: @@ -42,11 +35,11 @@ The :mod:`functools` module defines the following functions: def factorial(n): return n * factorial(n-1) if n else 1 - >>> factorial(10) # no previously cached result, makes 11 recursive calls + >>> factorial(10) # no previously cached result, makes 11 recursive calls 3628800 - >>> factorial(5) # just looks up cached value result + >>> factorial(5) # no new calls, just returns the cached result 120 - >>> factorial(12) # makes two new recursive calls, the other 10 are cached + >>> factorial(12) # two new recursive calls, factorial(10) is cached 479001600 The cache is threadsafe so that the wrapped function can be used in @@ -57,6 +50,10 @@ The :mod:`functools` module defines the following functions: another thread makes an additional call before the initial call has been completed and cached. + Call-once behavior is not guaranteed because locks are not held during the + function call. Potentially another call with the same arguments could + occur while the first call is still running. + .. versionadded:: 3.9 @@ -64,7 +61,7 @@ The :mod:`functools` module defines the following functions: Transform a method of a class into a property whose value is computed once and then cached as a normal attribute for the life of the instance. Similar - to :func:`property`, with the addition of caching. Useful for expensive + to :deco:`property`, with the addition of caching. Useful for expensive computed properties of instances that are otherwise effectively immutable. Example:: @@ -78,8 +75,8 @@ The :mod:`functools` module defines the following functions: def stdev(self): return statistics.stdev(self._data) - The mechanics of :func:`cached_property` are somewhat different from - :func:`property`. A regular property blocks attribute writes unless a + The mechanics of :deco:`cached_property` are somewhat different from + :deco:`property`. A regular property blocks attribute writes unless a setter is defined. In contrast, a *cached_property* allows writes. The *cached_property* decorator only runs on lookups and only when an @@ -111,14 +108,14 @@ The :mod:`functools` module defines the following functions: (as such classes don't provide a ``__dict__`` attribute at all). If a mutable mapping is not available or if space-efficient key sharing is - desired, an effect similar to :func:`cached_property` can also be achieved by - stacking :func:`property` on top of :func:`lru_cache`. See - :ref:`faq-cache-method-calls` for more details on how this differs from :func:`cached_property`. + desired, an effect similar to :deco:`cached_property` can also be achieved by + stacking :deco:`property` on top of :deco:`lru_cache`. See + :ref:`faq-cache-method-calls` for more details on how this differs from :deco:`cached_property`. .. versionadded:: 3.8 .. versionchanged:: 3.12 - Prior to Python 3.12, ``cached_property`` included an undocumented lock to + Prior to Python 3.12, :deco:`!cached_property` included an undocumented lock to ensure that in multi-threaded usage the getter function was guaranteed to run only once per instance. However, the lock was per-property, not per-instance, which could result in unacceptably high lock contention. In @@ -176,8 +173,8 @@ The :mod:`functools` module defines the following functions: the *maxsize* at its default value of 128:: @lru_cache - def count_vowels(sentence): - return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou') + def count_vowels(word): + return sum(word.count(vowel) for vowel in 'AEIOUaeiou') If *maxsize* is set to ``None``, the LRU feature is disabled and the cache can grow without bound. @@ -190,7 +187,7 @@ The :mod:`functools` module defines the following functions: Note, type specificity applies only to the function's immediate arguments rather than their contents. The scalar arguments, ``Decimal(42)`` and - ``Fraction(42)`` are be treated as distinct calls with distinct results. + ``Fraction(42)`` are treated as distinct calls with distinct results. In contrast, the tuple arguments ``('answer', Decimal(42))`` and ``('answer', Fraction(42))`` are treated as equivalent. @@ -471,7 +468,7 @@ The :mod:`functools` module defines the following functions: Roughly equivalent to:: - initial_missing = object() + initial_missing = sentinel('initial_missing') def reduce(function, iterable, /, initial=initial_missing): it = iter(iterable) @@ -672,7 +669,7 @@ The :mod:`functools` module defines the following functions: dispatch>` :term:`generic function`. To define a generic method, decorate it with the ``@singledispatchmethod`` - decorator. When defining a function using ``@singledispatchmethod``, note + decorator. When defining a method using ``@singledispatchmethod``, note that the dispatch happens on the type of the first non-*self* or non-*cls* argument:: @@ -690,7 +687,7 @@ The :mod:`functools` module defines the following functions: return not arg ``@singledispatchmethod`` supports nesting with other decorators such as - :func:`@classmethod`. Note that to allow for + :deco:`classmethod`. Note that to allow for ``dispatcher.register``, ``singledispatchmethod`` must be the *outer most* decorator. Here is the ``Negator`` class with the ``neg`` methods bound to the class, rather than an instance of the class:: @@ -712,11 +709,13 @@ The :mod:`functools` module defines the following functions: return not arg The same pattern can be used for other similar decorators: - :func:`@staticmethod`, - :func:`@abstractmethod`, and others. + :deco:`staticmethod`, :deco:`~abc.abstractmethod`, and others. .. versionadded:: 3.8 + .. versionchanged:: 3.15 + Added support of non-:term:`descriptor` callables. + .. function:: update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES) @@ -733,7 +732,7 @@ The :mod:`functools` module defines the following functions: function's :attr:`~function.__dict__`, i.e. the instance dictionary). To allow access to the original function for introspection and other purposes - (e.g. bypassing a caching decorator such as :func:`lru_cache`), this function + (e.g. bypassing a caching decorator such as :deco:`lru_cache`), this function automatically adds a ``__wrapped__`` attribute to the wrapper that refers to the function being wrapped. diff --git a/Doc/library/gc.rst b/Doc/library/gc.rst index 2ef5c4b35a25cc..65533e7e57adc3 100644 --- a/Doc/library/gc.rst +++ b/Doc/library/gc.rst @@ -4,9 +4,6 @@ .. module:: gc :synopsis: Interface to the cycle-detecting garbage collector. -.. moduleauthor:: Neil Schemenauer -.. sectionauthor:: Neil Schemenauer - -------------- This module provides an interface to the optional garbage collector. It @@ -20,7 +17,7 @@ can be disabled by calling ``gc.disable()``. To debug a leaking program call ``gc.DEBUG_SAVEALL``, causing garbage-collected objects to be saved in gc.garbage for inspection. -The :mod:`gc` module provides the following functions: +The :mod:`!gc` module provides the following functions: .. function:: enable() @@ -40,18 +37,11 @@ The :mod:`gc` module provides the following functions: .. function:: collect(generation=2) - Perform a collection. The optional argument *generation* + With no arguments, run a full collection. The optional argument *generation* may be an integer specifying which generation to collect (from 0 to 2). A :exc:`ValueError` is raised if the generation number is invalid. The sum of collected objects and uncollectable objects is returned. - Calling ``gc.collect(0)`` will perform a GC collection on the young generation. - - Calling ``gc.collect(1)`` will perform a GC collection on the young generation - and an increment of the old generation. - - Calling ``gc.collect(2)`` or ``gc.collect()`` performs a full collection - The free lists maintained for a number of built-in types are cleared whenever a full collection or collection of the highest generation (2) is run. Not all items in some free lists may be freed due to the @@ -63,6 +53,9 @@ The :mod:`gc` module provides the following functions: .. versionchanged:: 3.14 ``generation=1`` performs an increment of collection. + .. versionchanged:: 3.14.5 + ``generation=1`` performs collection of the middle generation. + .. function:: set_debug(flags) @@ -78,13 +71,9 @@ The :mod:`gc` module provides the following functions: .. function:: get_objects(generation=None) - Returns a list of all objects tracked by the collector, excluding the list - returned. If *generation* is not ``None``, return only the objects as follows: - - * 0: All objects in the young generation - * 1: No objects, as there is no generation 1 (as of Python 3.14) - * 2: All objects in the old generation + returned. If *generation* is not ``None``, return only the objects tracked by + the collector that are in that generation. .. versionchanged:: 3.8 New *generation* parameter. @@ -92,6 +81,9 @@ The :mod:`gc` module provides the following functions: .. versionchanged:: 3.14 Generation 1 is removed + .. versionchanged:: 3.14.5 + Generation 1 is reintroduced to maintain GC behavior from 3.13. + .. audit-event:: gc.get_objects generation gc.get_objects .. function:: get_stats() @@ -108,43 +100,47 @@ The :mod:`gc` module provides the following functions: * ``uncollectable`` is the total number of objects which were found to be uncollectable (and were therefore moved to the :data:`garbage` - list) inside this generation. + list) inside this generation; + + * ``candidates`` is the total number of objects in this generation which were + considered for collection and traversed; + + * ``duration`` is the total time in seconds spent in collections for this + generation. .. versionadded:: 3.4 + .. versionchanged:: 3.15 + Add ``duration`` and ``candidates``. + .. function:: set_threshold(threshold0, [threshold1, [threshold2]]) Set the garbage collection thresholds (the collection frequency). Setting *threshold0* to zero disables collection. - The GC classifies objects into two generations depending on whether they have - survived a collection. New objects are placed in the young generation. If an - object survives a collection it is moved into the old generation. - - In order to decide when to run, the collector keeps track of the number of object + The GC classifies objects into three generations depending on how many + collection sweeps they have survived. New objects are placed in the youngest + generation (generation ``0``). If an object survives a collection it is moved + into the next older generation. Since generation ``2`` is the oldest + generation, objects in that generation remain there after a collection. In + order to decide when to run, the collector keeps track of the number object allocations and deallocations since the last collection. When the number of allocations minus the number of deallocations exceeds *threshold0*, collection - starts. For each collection, all the objects in the young generation and some - fraction of the old generation is collected. - - In the free-threaded build, the increase in process memory usage is also - checked before running the collector. If the memory usage has not increased - by 10% since the last collection and the net number of object allocations - has not exceeded 40 times *threshold0*, the collection is not run. - - The fraction of the old generation that is collected is **inversely** proportional - to *threshold1*. The larger *threshold1* is, the slower objects in the old generation - are collected. - For the default value of 10, 1% of the old generation is scanned during each collection. - - *threshold2* is ignored. + starts. Initially only generation ``0`` is examined. If generation ``0`` has + been examined more than *threshold1* times since generation ``1`` has been + examined, then generation ``1`` is examined as well. + With the third generation, things are a bit more complicated, + see `Collecting the oldest generation `_ for more information. - See `Garbage collector design `_ for more information. + See `Garbage collector design `_ for more information. .. versionchanged:: 3.14 *threshold2* is ignored + .. versionchanged:: 3.14.5 + *threshold2* is restored to match Python 3.13 behavior. + .. function:: get_count() @@ -313,6 +309,12 @@ values but should not rebind them): "uncollectable": When *phase* is "stop", the number of objects that could not be collected and were put in :data:`garbage`. + "candidates": When *phase* is "stop", the total number of objects in this + generation which were considered for collection and traversed. + + "duration": When *phase* is "stop", the time in seconds spent in the + collection. + Applications can add their own callbacks to this list. The primary use cases are: @@ -325,6 +327,9 @@ values but should not rebind them): .. versionadded:: 3.3 + .. versionchanged:: 3.15 + Add "duration" and "candidates". + The following constants are provided for use with :func:`set_debug`: diff --git a/Doc/library/getpass.rst b/Doc/library/getpass.rst index 0fb0fc88683c03..f60cc63414f764 100644 --- a/Doc/library/getpass.rst +++ b/Doc/library/getpass.rst @@ -4,17 +4,13 @@ .. module:: getpass :synopsis: Portable reading of passwords and retrieval of the userid. -.. moduleauthor:: Piers Lauder -.. sectionauthor:: Fred L. Drake, Jr. -.. Windows (& Mac?) support by Guido van Rossum. - **Source code:** :source:`Lib/getpass.py` -------------- .. include:: ../includes/wasm-notavail.rst -The :mod:`getpass` module provides two functions: +The :mod:`!getpass` module provides two functions: .. function:: getpass(prompt='Password: ', stream=None, *, echo_char=None) @@ -22,26 +18,48 @@ The :mod:`getpass` module provides two functions: the string *prompt*, which defaults to ``'Password: '``. On Unix, the prompt is written to the file-like object *stream* using the replace error handler if needed. *stream* defaults to the controlling terminal - (:file:`/dev/tty`) or if that is unavailable to ``sys.stderr`` (this + (:file:`/dev/tty`) or if that is unavailable to :data:`sys.stderr` (this argument is ignored on Windows). The *echo_char* argument controls how user input is displayed while typing. If *echo_char* is ``None`` (default), input remains hidden. Otherwise, - *echo_char* must be a printable ASCII string and each typed character - is replaced by it. For example, ``echo_char='*'`` will display - asterisks instead of the actual input. + *echo_char* must be a single printable ASCII character and each + typed character is replaced by it. For example, ``echo_char='*'`` will + display asterisks instead of the actual input. - If echo free input is unavailable getpass() falls back to printing - a warning message to *stream* and reading from ``sys.stdin`` and + If echo-free input is unavailable, :func:`getpass` falls back to printing + a warning message to *stream* and reading from :data:`sys.stdin` and issuing a :exc:`GetPassWarning`. .. note:: - If you call getpass from within IDLE, the input may be done in the + If you call :func:`getpass` from within IDLE, the input may be done in the terminal you launched IDLE from rather than the idle window itself. + .. note:: + On Unix systems, when *echo_char* is set, the terminal will be + configured to operate in + :manpage:`noncanonical mode `. + Common terminal control characters are supported: + + * :kbd:`Ctrl+A` - Move cursor to beginning of line + * :kbd:`Ctrl+E` - Move cursor to end of line + * :kbd:`Ctrl+K` - Kill (delete) from cursor to end of line + * :kbd:`Ctrl+U` - Kill (delete) entire line + * :kbd:`Ctrl+W` - Erase previous word + * :kbd:`Ctrl+V` - Insert next character literally (quote) + * :kbd:`Backspace`/:kbd:`DEL` - Delete character before cursor + + These shortcuts work by reading the terminal's configured control + character mappings from termios settings. + .. versionchanged:: 3.14 Added the *echo_char* parameter for keyboard feedback. + .. versionchanged:: 3.15 + When using non-empty *echo_char* on Unix, keyboard shortcuts (including + cursor movement and line editing) are now properly handled using the + terminal's control character configuration. + .. exception:: GetPassWarning A :exc:`UserWarning` subclass issued when password input may be echoed. diff --git a/Doc/library/gettext.rst b/Doc/library/gettext.rst index d0de83907eb297..2ab7ba7df19cf1 100644 --- a/Doc/library/gettext.rst +++ b/Doc/library/gettext.rst @@ -4,14 +4,11 @@ .. module:: gettext :synopsis: Multilingual internationalization services. -.. moduleauthor:: Barry A. Warsaw -.. sectionauthor:: Barry A. Warsaw - **Source code:** :source:`Lib/gettext.py` -------------- -The :mod:`gettext` module provides internationalization (I18N) and localization +The :mod:`!gettext` module provides internationalization (I18N) and localization (L10N) services for your Python modules and applications. It supports both the GNU :program:`gettext` message catalog API and a higher level, class-based API that may be more appropriate for Python files. The interface described below allows you @@ -25,7 +22,7 @@ Some hints on localizing your Python modules and applications are also given. GNU :program:`gettext` API -------------------------- -The :mod:`gettext` module defines the following API, which is very similar to +The :mod:`!gettext` module defines the following API, which is very similar to the GNU :program:`gettext` API. If you use this API you will affect the translation of your entire application globally. Often this is what you want if your application is monolingual, with the choice of language dependent on the @@ -37,7 +34,7 @@ class-based API instead. .. function:: bindtextdomain(domain, localedir=None) Bind the *domain* to the locale directory *localedir*. More concretely, - :mod:`gettext` will look for binary :file:`.mo` files for the given domain using + :mod:`!gettext` will look for binary :file:`.mo` files for the given domain using the path (on Unix): :file:`{localedir}/{language}/LC_MESSAGES/{domain}.mo`, where *language* is searched for in the environment variables :envvar:`LANGUAGE`, :envvar:`LC_ALL`, :envvar:`LC_MESSAGES`, and :envvar:`LANG` respectively. @@ -54,19 +51,19 @@ class-based API instead. .. index:: single: _ (underscore); gettext -.. function:: gettext(message) +.. function:: gettext(message, /) Return the localized translation of *message*, based on the current global domain, language, and locale directory. This function is usually aliased as :func:`!_` in the local namespace (see examples below). -.. function:: dgettext(domain, message) +.. function:: dgettext(domain, message, /) Like :func:`.gettext`, but look the message up in the specified *domain*. -.. function:: ngettext(singular, plural, n) +.. function:: ngettext(singular, plural, n, /) Like :func:`.gettext`, but consider plural forms. If a translation is found, apply the plural formula to *n*, and return the resulting message (some @@ -81,15 +78,15 @@ class-based API instead. formulas for a variety of languages. -.. function:: dngettext(domain, singular, plural, n) +.. function:: dngettext(domain, singular, plural, n, /) Like :func:`ngettext`, but look the message up in the specified *domain*. -.. function:: pgettext(context, message) -.. function:: dpgettext(domain, context, message) -.. function:: npgettext(context, singular, plural, n) -.. function:: dnpgettext(domain, context, singular, plural, n) +.. function:: pgettext(context, message, /) +.. function:: dpgettext(domain, context, message, /) +.. function:: npgettext(context, singular, plural, n, /) +.. function:: dnpgettext(domain, context, singular, plural, n, /) Similar to the corresponding functions without the ``p`` in the prefix (that is, :func:`gettext`, :func:`dgettext`, :func:`ngettext`, :func:`dngettext`), @@ -114,7 +111,7 @@ Here's an example of typical usage for this API:: Class-based API --------------- -The class-based API of the :mod:`gettext` module gives you more flexibility and +The class-based API of the :mod:`!gettext` module gives you more flexibility and greater convenience than the GNU :program:`gettext` API. It is the recommended way of localizing your Python applications and modules. :mod:`!gettext` defines a :class:`GNUTranslations` class which implements the parsing of GNU :file:`.mo` format @@ -226,20 +223,20 @@ are the methods of :class:`!NullTranslations`: translation for a given message. - .. method:: gettext(message) + .. method:: gettext(message, /) If a fallback has been set, forward :meth:`!gettext` to the fallback. Otherwise, return *message*. Overridden in derived classes. - .. method:: ngettext(singular, plural, n) + .. method:: ngettext(singular, plural, n, /) If a fallback has been set, forward :meth:`!ngettext` to the fallback. Otherwise, return *singular* if *n* is 1; return *plural* otherwise. Overridden in derived classes. - .. method:: pgettext(context, message) + .. method:: pgettext(context, message, /) If a fallback has been set, forward :meth:`pgettext` to the fallback. Otherwise, return the translated message. Overridden in derived classes. @@ -247,7 +244,7 @@ are the methods of :class:`!NullTranslations`: .. versionadded:: 3.8 - .. method:: npgettext(context, singular, plural, n) + .. method:: npgettext(context, singular, plural, n, /) If a fallback has been set, forward :meth:`npgettext` to the fallback. Otherwise, return the translated message. Overridden in derived classes. @@ -325,7 +322,7 @@ unexpected, or if other problems occur while reading the file, instantiating a The following methods are overridden from the base class implementation: - .. method:: gettext(message) + .. method:: gettext(message, /) Look up the *message* id in the catalog and return the corresponding message string, as a Unicode string. If there is no entry in the catalog for the @@ -334,7 +331,7 @@ unexpected, or if other problems occur while reading the file, instantiating a *message* id is returned. - .. method:: ngettext(singular, plural, n) + .. method:: ngettext(singular, plural, n, /) Do a plural-forms lookup of a message id. *singular* is used as the message id for purposes of lookup in the catalog, while *n* is used to determine which @@ -355,7 +352,7 @@ unexpected, or if other problems occur while reading the file, instantiating a n) % {'num': n} - .. method:: pgettext(context, message) + .. method:: pgettext(context, message, /) Look up the *context* and *message* id in the catalog and return the corresponding message string, as a Unicode string. If there is no @@ -366,7 +363,7 @@ unexpected, or if other problems occur while reading the file, instantiating a .. versionadded:: 3.8 - .. method:: npgettext(context, singular, plural, n) + .. method:: npgettext(context, singular, plural, n, /) Do a plural-forms lookup of a message id. *singular* is used as the message id for purposes of lookup in the catalog, while *n* is used to @@ -393,7 +390,7 @@ The Catalog constructor .. index:: single: GNOME -GNOME uses a version of the :mod:`gettext` module by James Henstridge, but this +GNOME uses a version of the :mod:`!gettext` module by James Henstridge, but this version has a slightly different API. Its documented usage was:: import gettext @@ -425,7 +422,7 @@ take the following steps: #. create language-specific translations of the message catalogs -#. use the :mod:`gettext` module so that message strings are properly translated +#. use the :mod:`!gettext` module so that message strings are properly translated In order to prepare your code for I18N, you need to look at all the strings in your files. Any string that needs to be translated should be marked by wrapping @@ -473,10 +470,10 @@ supported natural language. They send back the completed language-specific versions as a :file:`.po` file that's compiled into a machine-readable :file:`.mo` binary catalog file using the :program:`msgfmt` program. The :file:`.mo` files are used by the -:mod:`gettext` module for the actual translation processing at +:mod:`!gettext` module for the actual translation processing at run-time. -How you use the :mod:`gettext` module in your code depends on whether you are +How you use the :mod:`!gettext` module in your code depends on whether you are internationalizing a single module or your entire application. The next two sections will discuss each case. diff --git a/Doc/library/glob.rst b/Doc/library/glob.rst index 59ad1b07f27338..942f23d216fc07 100644 --- a/Doc/library/glob.rst +++ b/Doc/library/glob.rst @@ -18,23 +18,27 @@ single: - (minus); in glob-style wildcards single: . (dot); in glob-style wildcards -The :mod:`glob` module finds all the pathnames matching a specified pattern -according to the rules used by the Unix shell, although results are returned in -arbitrary order. No tilde expansion is done, but ``*``, ``?``, and character +The :mod:`!glob` module finds pathnames +using pattern matching rules similar to the Unix shell. +No tilde expansion is done, but ``*``, ``?``, and character ranges expressed with ``[]`` will be correctly matched. This is done by using the :func:`os.scandir` and :func:`fnmatch.fnmatch` functions in concert, and not by actually invoking a subshell. -Note that files beginning with a dot (``.``) can only be matched by +.. note:: + The pathnames are returned in no particular order. If you need a specific + order, sort the results. + +Files beginning with a dot (``.``) can only be matched by patterns that also start with a dot, unlike :func:`fnmatch.fnmatch` or :func:`pathlib.Path.glob`. -(For tilde and shell variable expansion, use :func:`os.path.expanduser` and -:func:`os.path.expandvars`.) +For tilde and shell variable expansion, use :func:`os.path.expanduser` and +:func:`os.path.expandvars`. For a literal match, wrap the meta-characters in brackets. For example, ``'[?]'`` matches the character ``'?'``. -The :mod:`glob` module defines the following functions: +The :mod:`!glob` module defines the following functions: .. function:: glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, \ @@ -51,7 +55,7 @@ The :mod:`glob` module defines the following functions: If *root_dir* is not ``None``, it should be a :term:`path-like object` specifying the root directory for searching. It has the same effect on - :func:`glob` as changing the current directory before calling it. If + :func:`!glob` as changing the current directory before calling it. If *pathname* is relative, the result will contain paths relative to *root_dir*. @@ -79,6 +83,11 @@ The :mod:`glob` module defines the following functions: This function may return duplicate path names if *pathname* contains multiple "``**``" patterns and *recursive* is true. + .. note:: + Any :exc:`OSError` exceptions raised from scanning the filesystem are + suppressed. This includes :exc:`PermissionError` when accessing + directories without read permission. + .. versionchanged:: 3.5 Support for recursive globs using "``**``". @@ -102,6 +111,11 @@ The :mod:`glob` module defines the following functions: This function may return duplicate path names if *pathname* contains multiple "``**``" patterns and *recursive* is true. + .. note:: + Any :exc:`OSError` exceptions raised from scanning the filesystem are + suppressed. This includes :exc:`PermissionError` when accessing + directories without read permission. + .. versionchanged:: 3.5 Support for recursive globs using "``**``". @@ -126,7 +140,8 @@ The :mod:`glob` module defines the following functions: .. function:: translate(pathname, *, recursive=False, include_hidden=False, seps=None) Convert the given path specification to a regular expression for use with - :func:`re.match`. The path specification can contain shell-style wildcards. + :func:`re.prefixmatch`. The path specification can contain shell-style + wildcards. For example: @@ -136,7 +151,7 @@ The :mod:`glob` module defines the following functions: >>> regex '(?s:(?:.+/)?[^/]*\\.txt)\\z' >>> reobj = re.compile(regex) - >>> reobj.match('foo/bar/baz.txt') + >>> reobj.prefixmatch('foo/bar/baz.txt') Path separators and segments are meaningful to this function, unlike diff --git a/Doc/library/graphlib.rst b/Doc/library/graphlib.rst index 053d5f8231ba0e..21f4d1fb938038 100644 --- a/Doc/library/graphlib.rst +++ b/Doc/library/graphlib.rst @@ -204,7 +204,7 @@ Exceptions ---------- -The :mod:`graphlib` module defines the following exception classes: +The :mod:`!graphlib` module defines the following exception classes: .. exception:: CycleError diff --git a/Doc/library/grp.rst b/Doc/library/grp.rst index d1c7f22a209780..f436970e791ace 100644 --- a/Doc/library/grp.rst +++ b/Doc/library/grp.rst @@ -2,7 +2,6 @@ ================================== .. module:: grp - :platform: Unix :synopsis: The group database (getgrnam() and friends). -------------- diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst index c59014a6f5bdce..9211e5f18c6b6e 100644 --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -11,9 +11,11 @@ This module provides a simple interface to compress and decompress files just like the GNU programs :program:`gzip` and :program:`gunzip` would. +.. include:: ../includes/optional-module.rst + The data compression is provided by the :mod:`zlib` module. -The :mod:`gzip` module provides the :class:`GzipFile` class, as well as the +The :mod:`!gzip` module provides the :class:`GzipFile` class, as well as the :func:`.open`, :func:`compress` and :func:`decompress` convenience functions. The :class:`GzipFile` class reads and writes :program:`gzip`\ -format files, automatically compressing or decompressing the data so that it looks like an @@ -26,7 +28,7 @@ Note that additional file formats which can be decompressed by the The module defines the following items: -.. function:: open(filename, mode='rb', compresslevel=6, encoding=None, errors=None, newline=None) +.. function:: open(filename, mode='rb', compresslevel=6, encoding=None, errors=None, newline=None, *, mtime=None) Open a gzip-compressed file in binary or text mode, returning a :term:`file object`. @@ -41,9 +43,12 @@ The module defines the following items: The *compresslevel* argument is an integer from 0 to 9, as for the :class:`GzipFile` constructor. + The keyword-only argument *mtime* represents a Unix timestamp. + For binary mode, this function is equivalent to the :class:`GzipFile` - constructor: ``GzipFile(filename, mode, compresslevel)``. In this case, the - *encoding*, *errors* and *newline* arguments must not be provided. + constructor: ``GzipFile(filename, mode, compresslevel, mtime=mtime)``. + In this case, the *encoding*, *errors* and *newline* arguments must not + be provided. For text mode, a :class:`GzipFile` object is created, and wrapped in an :class:`io.TextIOWrapper` instance with the specified encoding, error @@ -59,11 +64,15 @@ The module defines the following items: .. versionchanged:: 3.6 Accepts a :term:`path-like object`. - .. versionchanged:: next + .. versionchanged:: 3.15 The default compression level was reduced to 6 (down from 9). It is the default level used by most compression tools and a better tradeoff between speed and performance. + .. versionchanged:: next + Added keyword-only argument *mtime* which is passed to the class + constructor of :class:`~gzip.GzipFile`. + .. exception:: BadGzipFile An exception raised for invalid gzip files. It inherits from :exc:`OSError`. @@ -106,9 +115,13 @@ The module defines the following items: is no compression. The default is ``9``. The optional *mtime* argument is the timestamp requested by gzip. The time - is in Unix format, i.e., seconds since 00:00:00 UTC, January 1, 1970. - If *mtime* is omitted or ``None``, the current time is used. Use *mtime* = 0 - to generate a compressed stream that does not depend on creation time. + is in Unix format, i.e., seconds since 00:00:00 UTC, January 1, 1970. Set + *mtime* to ``0`` to generate a compressed stream that does not depend on + creation time. If *mtime* is omitted or ``None``, the current time is used; + however, if the current time is outside the range 00:00:00 UTC, January 1, + 1970 through 06:28:15 UTC, February 7, 2106, or explicitly passed *mtime* + argument is outside the range ``0`` to ``2**32-1``, then the value ``0`` + is used instead. See below for the :attr:`mtime` attribute that is set when decompressing. @@ -186,7 +199,7 @@ The module defines the following items: Remove the ``filename`` attribute, use the :attr:`~GzipFile.name` attribute instead. - .. versionchanged:: next + .. versionchanged:: 3.15 The default compression level was reduced to 6 (down from 9). It is the default level used by most compression tools and a better tradeoff between speed and performance. @@ -216,7 +229,7 @@ The module defines the following items: The *mtime* parameter now defaults to 0 for reproducible output. For the previous behaviour of using the current time, pass ``None`` to *mtime*. - .. versionchanged:: next + .. versionchanged:: 3.15 The default compression level was reduced to 6 (down from 9). It is the default level used by most compression tools and a better tradeoff between speed and performance. @@ -281,20 +294,20 @@ Example of how to GZIP compress a binary string:: .. _gzip-cli: -Command Line Interface +Command-line interface ---------------------- -The :mod:`gzip` module provides a simple command line interface to compress or +The :mod:`!gzip` module provides a simple command line interface to compress or decompress files. -Once executed the :mod:`gzip` module keeps the input file(s). +Once executed the :mod:`!gzip` module keeps the input file(s). .. versionchanged:: 3.8 Add a new command line interface with a usage. By default, when you will execute the CLI, the default compression level is 6. -Command line options +Command-line options ^^^^^^^^^^^^^^^^^^^^ .. option:: file diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst index 8bba6700930bf4..ed0b0b2735b5c3 100644 --- a/Doc/library/hashlib.rst +++ b/Doc/library/hashlib.rst @@ -4,9 +4,6 @@ .. module:: hashlib :synopsis: Secure hash and message digest algorithms. -.. moduleauthor:: Gregory P. Smith -.. sectionauthor:: Gregory P. Smith - **Source code:** :source:`Lib/hashlib.py` .. index:: @@ -61,7 +58,7 @@ if you are using a rare "FIPS compliant" build of Python. These correspond to :data:`algorithms_guaranteed`. Additional algorithms may also be available if your Python distribution's -:mod:`hashlib` was linked against a build of OpenSSL that provides others. +:mod:`!hashlib` was linked against a build of OpenSSL that provides others. Others *are not guaranteed available* on all installations and will only be accessible by name via :func:`new`. See :data:`algorithms_available`. @@ -310,7 +307,7 @@ a file or file-like object. .. versionadded:: 3.11 .. versionchanged:: 3.14 - Now raises a :exc:`BlockingIOError` if the file is opened in blocking + Now raises a :exc:`BlockingIOError` if the file is opened in non-blocking mode. Previously, spurious null bytes were added to the digest. @@ -379,8 +376,6 @@ include a `salt `_. BLAKE2 ------ -.. sectionauthor:: Dmitry Chestnykh - .. index:: single: blake2b, blake2s @@ -397,7 +392,7 @@ BLAKE2 supports **keyed mode** (a faster and simpler replacement for HMAC_), **salted hashing**, **personalization**, and **tree hashing**. Hash objects from this module follow the API of standard library's -:mod:`hashlib` objects. +:mod:`!hashlib` objects. Creating hash objects diff --git a/Doc/library/heapq.rst b/Doc/library/heapq.rst index 462b65bc7afba4..26cffa7c643028 100644 --- a/Doc/library/heapq.rst +++ b/Doc/library/heapq.rst @@ -4,11 +4,6 @@ .. module:: heapq :synopsis: Heap queue algorithm (a.k.a. priority queue). -.. moduleauthor:: Kevin O'Connor -.. sectionauthor:: Guido van Rossum -.. sectionauthor:: François Pinard -.. sectionauthor:: Raymond Hettinger - **Source code:** :source:`Lib/heapq.py` -------------- @@ -58,6 +53,11 @@ functions, respectively. The following functions are provided for min-heaps: +.. function:: heapify(x) + + Transform list *x* into a min-heap, in-place, in linear time. + + .. function:: heappush(heap, item) Push the value *item* onto the *heap*, maintaining the min-heap invariant. @@ -77,11 +77,6 @@ The following functions are provided for min-heaps: followed by a separate call to :func:`heappop`. -.. function:: heapify(x) - - Transform list *x* into a min-heap, in-place, in linear time. - - .. function:: heapreplace(heap, item) Pop and return the smallest item from the *heap*, and also push the new *item*. @@ -231,6 +226,42 @@ Heap elements can be tuples. This is useful for assigning comparison values (1, 'write spec') +Other Applications +------------------ + +`Medians `_ are a measure of +central tendency for a set of numbers. In distributions skewed by +outliers, the median provides a more stable estimate than an average +(arithmetic mean). A running median is an `online algorithm +`_ that updates +continuously as new data arrives. + +A running median can be efficiently implemented by balancing two heaps, +a max-heap for values at or below the midpoint and a min-heap for values +above the midpoint. When the two heaps have the same size, the new +median is the average of the tops of the two heaps; otherwise, the +median is at the top of the larger heap:: + + def running_median(iterable): + "Yields the cumulative median of values seen so far." + + lo = [] # max-heap + hi = [] # min-heap (same size as or one smaller than lo) + + for x in iterable: + if len(lo) == len(hi): + heappush_max(lo, heappushpop(hi, x)) + yield lo[0] + else: + heappush(hi, heappushpop_max(lo, x)) + yield (lo[0] + hi[0]) / 2 + +For example:: + + >>> list(running_median([5.0, 9.0, 4.0, 12.0, 8.0, 9.0])) + [5.0, 7.0, 5.0, 7.0, 8.0, 8.5] + + Priority Queue Implementation Notes ----------------------------------- diff --git a/Doc/library/hmac.rst b/Doc/library/hmac.rst index 57076c38086c79..2ee0c0bd9128b8 100644 --- a/Doc/library/hmac.rst +++ b/Doc/library/hmac.rst @@ -4,9 +4,6 @@ .. module:: hmac :synopsis: Keyed-Hashing for Message Authentication (HMAC) implementation -.. moduleauthor:: Gerhard Häring -.. sectionauthor:: Gerhard Häring - **Source code:** :source:`Lib/hmac.py` -------------- @@ -50,7 +47,9 @@ cannot be used with HMAC. .. versionadded:: 3.7 -An HMAC object has the following methods: +.. class:: HMAC + + An HMAC object has the following methods: .. method:: HMAC.update(msg) diff --git a/Doc/library/html.entities.rst b/Doc/library/html.entities.rst index add18e4c87d220..15d2dc2e9aa6bc 100644 --- a/Doc/library/html.entities.rst +++ b/Doc/library/html.entities.rst @@ -4,8 +4,6 @@ .. module:: html.entities :synopsis: Definitions of HTML general entities. -.. sectionauthor:: Fred L. Drake, Jr. - **Source code:** :source:`Lib/html/entities.py` -------------- diff --git a/Doc/library/html.parser.rst b/Doc/library/html.parser.rst index dd67fc34e856f1..11f851d4f6c4b7 100644 --- a/Doc/library/html.parser.rst +++ b/Doc/library/html.parser.rst @@ -15,14 +15,18 @@ This module defines a class :class:`HTMLParser` which serves as the basis for parsing text files formatted in HTML (HyperText Mark-up Language) and XHTML. -.. class:: HTMLParser(*, convert_charrefs=True) +.. class:: HTMLParser(*, convert_charrefs=True, scripting=False) Create a parser instance able to parse invalid markup. - If *convert_charrefs* is ``True`` (the default), all character - references (except the ones in ``script``/``style`` elements) are + If *convert_charrefs* is true (the default), all character + references (except the ones in elements like ``script`` and ``style``) are automatically converted to the corresponding Unicode characters. + If *scripting* is false (the default), the content of the ``noscript`` + element is parsed normally; if it's true, it's returned as is without + being parsed. + An :class:`.HTMLParser` instance is fed HTML data and calls handler methods when start tags, end tags, text, comments, and other markup elements are encountered. The user should subclass :class:`.HTMLParser` and override its @@ -37,6 +41,9 @@ parsing text files formatted in HTML (HyperText Mark-up Language) and XHTML. .. versionchanged:: 3.5 The default value for argument *convert_charrefs* is now ``True``. + .. versionchanged:: 3.14.1 + Added the *scripting* parameter. + Example HTML Parser Application ------------------------------- @@ -134,7 +141,7 @@ implementations do nothing (except for :meth:`~HTMLParser.handle_startendtag`): argument is a list of ``(name, value)`` pairs containing the attributes found inside the tag's ``<>`` brackets. The *name* will be translated to lower case, and quotes in the *value* have been removed, and character and entity references - have been replaced. + have been replaced. For empty attributes, *value* is ``None``. For instance, for the tag ````, this method would be called as ``handle_starttag('a', [('href', 'https://www.cwi.nl/')])``. @@ -161,15 +168,15 @@ implementations do nothing (except for :meth:`~HTMLParser.handle_startendtag`): .. method:: HTMLParser.handle_data(data) This method is called to process arbitrary data (e.g. text nodes and the - content of ```` and ````). + content of elements like ``script`` and ``style``). .. method:: HTMLParser.handle_entityref(name) This method is called to process a named character reference of the form ``&name;`` (e.g. ``>``), where *name* is a general entity reference - (e.g. ``'gt'``). This method is never called if *convert_charrefs* is - ``True``. + (e.g. ``'gt'``). + This method is only called if *convert_charrefs* is false. .. method:: HTMLParser.handle_charref(name) @@ -177,8 +184,8 @@ implementations do nothing (except for :meth:`~HTMLParser.handle_startendtag`): This method is called to process decimal and hexadecimal numeric character references of the form :samp:`&#{NNN};` and :samp:`&#x{NNN};`. For example, the decimal equivalent for ``>`` is ``>``, whereas the hexadecimal is ``>``; - in this case the method will receive ``'62'`` or ``'x3E'``. This method - is never called if *convert_charrefs* is ``True``. + in this case the method will receive ``'62'`` or ``'x3E'``. + This method is only called if *convert_charrefs* is false. .. method:: HTMLParser.handle_comment(data) @@ -292,8 +299,8 @@ Parsing an element with a few attributes and a title: Data : Python End tag : h1 -The content of ``script`` and ``style`` elements is returned as is, without -further parsing: +The content of elements like ``script`` and ``style`` is returned as is, +without further parsing: .. doctest:: @@ -304,12 +311,24 @@ further parsing: End tag : style >>> parser.feed('') + ... 'alert("hello! ☺");') Start tag: script attr: ('type', 'text/javascript') - Data : alert("hello!"); + Data : alert("hello! ☺"); End tag : script +Attribute names are converted to lowercase, quotes from attribute values removed, +and ``None`` is returned as *value* for empty attributes (such as ``checked``): + +.. doctest:: + + >>> parser.feed("") + Start tag: input + attr: ('type', 'checkbox') + attr: ('checked', None) + attr: ('required', '') + attr: ('disabled', 'disabled') + Parsing comments: .. doctest:: @@ -336,7 +355,7 @@ correct char (note: these 3 references are all equivalent to ``'>'``): Feeding incomplete chunks to :meth:`~HTMLParser.feed` works, but :meth:`~HTMLParser.handle_data` might be called more than once -(unless *convert_charrefs* is set to ``True``): +if *convert_charrefs* is false: .. doctest:: diff --git a/Doc/library/html.rst b/Doc/library/html.rst index 9aa39ba9a42b0f..65c49a4107a048 100644 --- a/Doc/library/html.rst +++ b/Doc/library/html.rst @@ -14,9 +14,12 @@ This module defines utilities to manipulate HTML. Convert the characters ``&``, ``<`` and ``>`` in string *s* to HTML-safe sequences. Use this if you need to display text that might contain such - characters in HTML. If the optional flag *quote* is true, the characters - (``"``) and (``'``) are also translated; this helps for inclusion in an HTML - attribute value delimited by quotes, as in ````. + characters in HTML. If the optional flag *quote* is true (the default), the + characters (``"``) and (``'``) are also translated; this helps for inclusion + in an HTML attribute value delimited by quotes, as in ````. + If *quote* is set to false, the characters (``"``) and (``'``) are not + translated. + .. versionadded:: 3.2 diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index 07f5ebf57c9b54..ddf3d40d221fcd 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -68,7 +68,7 @@ The module provides the following classes: .. versionchanged:: 3.7 *blocksize* parameter was added. - .. versionchanged:: next + .. versionchanged:: 3.15 *max_response_headers* parameter was added. @@ -114,7 +114,7 @@ The module provides the following classes: The deprecated *key_file*, *cert_file* and *check_hostname* parameters have been removed. - .. versionchanged:: next + .. versionchanged:: 3.15 *max_response_headers* parameter was added. @@ -133,7 +133,7 @@ This module provides the following function: Parse the headers from a file pointer *fp* representing a HTTP request/response. The file has to be a :class:`~io.BufferedIOBase` reader - (i.e. not text) and must provide a valid :rfc:`2822` style header. + (i.e. not text) and must provide a valid :rfc:`5322` style header. This function returns an instance of :class:`http.client.HTTPMessage` that holds the header fields, but no payload @@ -319,6 +319,12 @@ HTTPConnection Objects :class:`str` or bytes-like object that is not also a file as the body representation. + .. note:: + + Note that you must have read the whole response or call :meth:`close` + if :meth:`getresponse` raised an non-:exc:`ConnectionError` exception + before you can send a new request to the server. + .. versionchanged:: 3.2 *body* can now be an iterable. @@ -334,16 +340,15 @@ HTTPConnection Objects Should be called after a request is sent to get the response from the server. Returns an :class:`HTTPResponse` instance. - .. note:: - - Note that you must have read the whole response before you can send a new - request to the server. - .. versionchanged:: 3.5 If a :exc:`ConnectionError` or subclass is raised, the :class:`HTTPConnection` object will be ready to reconnect when a new request is sent. + Note that this does not apply to :exc:`OSError`\s raised by the underlying + socket. Instead the caller is responsible to call :meth:`close` on the + existing connection. + .. method:: HTTPConnection.set_debuglevel(level) @@ -429,7 +434,7 @@ HTTPConnection Objects The maximum number of allowed response headers to help prevent denial-of-service attacks. By default, the maximum number of allowed headers is set to 100. - .. versionadded:: next + .. versionadded:: 3.15 As an alternative to using the :meth:`~HTTPConnection.request` method described above, you can diff --git a/Doc/library/http.cookiejar.rst b/Doc/library/http.cookiejar.rst index 251aea891c3f98..5ee783b7fae950 100644 --- a/Doc/library/http.cookiejar.rst +++ b/Doc/library/http.cookiejar.rst @@ -4,15 +4,12 @@ .. module:: http.cookiejar :synopsis: Classes for automatic handling of HTTP cookies. -.. moduleauthor:: John J. Lee -.. sectionauthor:: John J. Lee - **Source code:** :source:`Lib/http/cookiejar.py` -------------- -The :mod:`http.cookiejar` module defines classes for automatic handling of HTTP -cookies. It is useful for accessing web sites that require small pieces of data +The :mod:`!http.cookiejar` module defines classes for automatic handling of HTTP +cookies. It is useful for accessing websites that require small pieces of data -- :dfn:`cookies` -- to be set on the client machine by an HTTP response from a web server, and then returned to the server in later HTTP requests. @@ -21,7 +18,7 @@ Both the regular Netscape cookie protocol and the protocol defined by :rfc:`2109` cookies are parsed as Netscape cookies and subsequently treated either as Netscape or RFC 2965 cookies according to the 'policy' in effect. Note that the great majority of cookies on the internet are Netscape cookies. -:mod:`http.cookiejar` attempts to follow the de-facto Netscape cookie protocol (which +:mod:`!http.cookiejar` attempts to follow the de-facto Netscape cookie protocol (which differs substantially from that set out in the original Netscape specification), including taking note of the ``max-age`` and ``port`` cookie-attributes introduced with RFC 2965. @@ -109,7 +106,7 @@ The following classes are provided: .. class:: Cookie() This class represents Netscape, :rfc:`2109` and :rfc:`2965` cookies. It is not - expected that users of :mod:`http.cookiejar` construct their own :class:`Cookie` + expected that users of :mod:`!http.cookiejar` construct their own :class:`Cookie` instances. Instead, if necessary, call :meth:`make_cookies` on a :class:`CookieJar` instance. @@ -121,13 +118,13 @@ The following classes are provided: Module :mod:`http.cookies` HTTP cookie classes, principally useful for server-side code. The - :mod:`http.cookiejar` and :mod:`http.cookies` modules do not depend on each + :mod:`!http.cookiejar` and :mod:`http.cookies` modules do not depend on each other. https://curl.se/rfc/cookie_spec.html The specification of the original Netscape cookie protocol. Though this is still the dominant protocol, the 'Netscape cookie protocol' implemented by all - the major browsers (and :mod:`http.cookiejar`) only bears a passing resemblance to + the major browsers (and :mod:`!http.cookiejar`) only bears a passing resemblance to the one sketched out in ``cookie_spec.html``. :rfc:`2109` - HTTP State Management Mechanism @@ -617,7 +614,7 @@ standard cookie-attributes specified in the various cookie standards. The correspondence is not one-to-one, because there are complicated rules for assigning default values, because the ``max-age`` and ``expires`` cookie-attributes contain equivalent information, and because :rfc:`2109` cookies -may be 'downgraded' by :mod:`http.cookiejar` from version 1 to version 0 (Netscape) +may be 'downgraded' by :mod:`!http.cookiejar` from version 1 to version 0 (Netscape) cookies. Assignment to these attributes should not be necessary other than in rare @@ -629,7 +626,7 @@ internal consistency, so you should know what you're doing if you do that. Integer or :const:`None`. Netscape cookies have :attr:`version` 0. :rfc:`2965` and :rfc:`2109` cookies have a ``version`` cookie-attribute of 1. However, note that - :mod:`http.cookiejar` may 'downgrade' RFC 2109 cookies to Netscape cookies, in which + :mod:`!http.cookiejar` may 'downgrade' RFC 2109 cookies to Netscape cookies, in which case :attr:`version` is 0. @@ -692,7 +689,7 @@ internal consistency, so you should know what you're doing if you do that. ``True`` if this cookie was received as an :rfc:`2109` cookie (ie. the cookie arrived in a :mailheader:`Set-Cookie` header, and the value of the Version cookie-attribute in that header was 1). This attribute is provided because - :mod:`http.cookiejar` may 'downgrade' RFC 2109 cookies to Netscape cookies, in + :mod:`!http.cookiejar` may 'downgrade' RFC 2109 cookies to Netscape cookies, in which case :attr:`version` is 0. @@ -744,7 +741,7 @@ The :class:`Cookie` class also defines the following method: Examples -------- -The first example shows the most common usage of :mod:`http.cookiejar`:: +The first example shows the most common usage of :mod:`!http.cookiejar`:: import http.cookiejar, urllib.request cj = http.cookiejar.CookieJar() diff --git a/Doc/library/http.cookies.rst b/Doc/library/http.cookies.rst index eb196320721194..4965c5fc3ba1d8 100644 --- a/Doc/library/http.cookies.rst +++ b/Doc/library/http.cookies.rst @@ -4,14 +4,11 @@ .. module:: http.cookies :synopsis: Support for HTTP state management (cookies). -.. moduleauthor:: Timothy O'Malley -.. sectionauthor:: Moshe Zadka - **Source code:** :source:`Lib/http/cookies.py` -------------- -The :mod:`http.cookies` module defines classes for abstracting the concept of +The :mod:`!http.cookies` module defines classes for abstracting the concept of cookies, an HTTP state management mechanism. It supports both simple string-only cookies, and provides an abstraction for having any serializable data-type as cookie value. @@ -65,7 +62,7 @@ in a cookie name (as :attr:`~Morsel.key`). Module :mod:`http.cookiejar` HTTP cookie handling for web *clients*. The :mod:`http.cookiejar` and - :mod:`http.cookies` modules do not depend on each other. + :mod:`!http.cookies` modules do not depend on each other. :rfc:`2109` - HTTP State Management Mechanism This is the state management specification implemented by this module. @@ -110,6 +107,12 @@ Cookie Objects The meaning for *attrs* is the same as in :meth:`output`. + .. deprecated-removed:: 3.15 3.19 + This method generates a JavaScript snippet to set cookies in the browser, + which is no longer considered a standard or recommended approach. + Use :meth:`~http.cookies.BaseCookie.output` instead to generate HTTP + headers. + .. method:: BaseCookie.load(rawdata) @@ -148,9 +151,12 @@ Morsel Objects in HTTP requests, and is not accessible through JavaScript. This is intended to mitigate some forms of cross-site scripting. - The attribute :attr:`samesite` specifies that the browser is not allowed to - send the cookie along with cross-site requests. This helps to mitigate CSRF - attacks. Valid values for this attribute are "Strict" and "Lax". + The attribute :attr:`samesite` controls when the browser sends the cookie with + cross-site requests. This helps to mitigate CSRF attacks. Valid values are + "Strict" (only sent with same-site requests), "Lax" (sent with same-site + requests and top-level navigations), and "None" (sent with same-site and + cross-site requests). When using "None", the "secure" attribute must also + be set, as required by modern browsers. The attribute :attr:`partitioned` indicates to user agents that these cross-site cookies *should* only be available in the same top-level context @@ -223,6 +229,12 @@ Morsel Objects The meaning for *attrs* is the same as in :meth:`output`. + .. deprecated-removed:: 3.15 3.19 + This method generates a JavaScript snippet to set cookies in the browser, + which is no longer considered a standard or recommended approach. + Use :meth:`~http.cookies.Morsel.output` instead to generate HTTP + headers. + .. method:: Morsel.OutputString(attrs=None) @@ -261,7 +273,7 @@ Morsel Objects Example ------- -The following example demonstrates how to use the :mod:`http.cookies` module. +The following example demonstrates how to use the :mod:`!http.cookies` module. .. doctest:: :options: +NORMALIZE_WHITESPACE @@ -289,9 +301,9 @@ The following example demonstrates how to use the :mod:`http.cookies` module. Set-Cookie: chips=ahoy Set-Cookie: vienna=finger >>> C = cookies.SimpleCookie() - >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') + >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=;";') >>> print(C) - Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;" + Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=;" >>> C = cookies.SimpleCookie() >>> C["oreo"] = "doublestuff" >>> C["oreo"]["path"] = "/" diff --git a/Doc/library/http.rst b/Doc/library/http.rst index ce3fb9f8120502..43a801416e24f9 100644 --- a/Doc/library/http.rst +++ b/Doc/library/http.rst @@ -12,7 +12,7 @@ -------------- -:mod:`http` is a package that collects several modules for working with the +:mod:`!http` is a package that collects several modules for working with the HyperText Transfer Protocol: * :mod:`http.client` is a low-level HTTP protocol client; for high-level URL @@ -22,7 +22,7 @@ HyperText Transfer Protocol: * :mod:`http.cookiejar` provides persistence of cookies -The :mod:`http` module also defines the following enums that help you work with http related code: +The :mod:`!http` module also defines the following enums that help you work with http related code: .. class:: HTTPStatus @@ -139,7 +139,8 @@ equal to the constant name (i.e. ``http.HTTPStatus.OK`` is also available as .. versionchanged:: 3.13 Implemented RFC9110 naming for status constants. Old constant names are preserved for - backwards compatibility. + backwards compatibility: ``413 REQUEST_ENTITY_TOO_LARGE``, ``414 REQUEST_URI_TOO_LONG``, + ``416 REQUESTED_RANGE_NOT_SATISFIABLE`` and ``422 UNPROCESSABLE_ENTITY``. HTTP status category -------------------- diff --git a/Doc/library/http.server.rst b/Doc/library/http.server.rst index 063344e0284258..c4b9173f9e34eb 100644 --- a/Doc/library/http.server.rst +++ b/Doc/library/http.server.rst @@ -19,7 +19,7 @@ This module defines classes for implementing HTTP servers. .. warning:: - :mod:`http.server` is not recommended for production. It only implements + :mod:`!http.server` is not recommended for production. It only implements :ref:`basic security checks `. .. include:: ../includes/wasm-notavail.rst @@ -99,7 +99,7 @@ instantiation, of which this module provides three different variants: This class is used to handle the HTTP requests that arrive at the server. By itself, it cannot respond to any actual HTTP requests; it must be subclassed - to handle each request method (e.g. GET or POST). + to handle each request method (for example, ``'GET'`` or ``'POST'``). :class:`BaseHTTPRequestHandler` provides a number of class and instance variables, and methods for use by subclasses. @@ -154,7 +154,7 @@ instantiation, of which this module provides three different variants: variable. This instance parses and manages the headers in the HTTP request. The :func:`~http.client.parse_headers` function from :mod:`http.client` is used to parse the headers and it requires that the - HTTP request provide a valid :rfc:`2822` style header. + HTTP request provide a valid :rfc:`5322` style header. .. attribute:: rfile @@ -241,7 +241,7 @@ instantiation, of which this module provides three different variants: request header it responds back with a ``100 Continue`` followed by ``200 OK`` headers. This method can be overridden to raise an error if the server does not - want the client to continue. For e.g. server can choose to send ``417 + want the client to continue. For example, the server can choose to send ``417 Expectation Failed`` as a response header and ``return False``. .. versionadded:: 3.2 @@ -287,6 +287,8 @@ instantiation, of which this module provides three different variants: specifying its value. Note that, after the send_header calls are done, :meth:`end_headers` MUST BE called in order to complete the operation. + This method does not reject input containing CRLF sequences. + .. versionchanged:: 3.2 Headers are stored in an internal buffer. @@ -297,6 +299,8 @@ instantiation, of which this module provides three different variants: buffered and sent directly the output stream.If the *message* is not specified, the HTTP message corresponding the response *code* is sent. + This method does not reject *message* containing CRLF sequences. + .. versionadded:: 3.2 .. method:: end_headers() @@ -362,7 +366,8 @@ instantiation, of which this module provides three different variants: delays, it now always returns the IP address. -.. class:: SimpleHTTPRequestHandler(request, client_address, server, directory=None) +.. class:: SimpleHTTPRequestHandler(request, client_address, server, \ + *, directory=None, extra_response_headers=None) This class serves files from the directory *directory* and below, or the current directory if *directory* is not provided, directly @@ -374,6 +379,9 @@ instantiation, of which this module provides three different variants: .. versionchanged:: 3.9 The *directory* parameter accepts a :term:`path-like object`. + .. versionchanged:: 3.15 + Added *extra_response_headers* parameter. + A lot of the work, such as parsing the request, is done by the base class :class:`BaseHTTPRequestHandler`. This class implements the :func:`do_GET` and :func:`do_HEAD` functions. @@ -386,6 +394,14 @@ instantiation, of which this module provides three different variants: This will be ``"SimpleHTTP/" + __version__``, where ``__version__`` is defined at the module level. + .. attribute:: default_content_type + + Specifies the Content-Type header value sent when the MIME type + cannot be guessed from the file extension of the requested URL. + By default, it is set to ``'application/octet-stream'``. + + .. versionadded:: 3.15 + .. attribute:: extensions_map A dictionary mapping suffixes into MIME types, contains custom overrides @@ -396,6 +412,15 @@ instantiation, of which this module provides three different variants: This dictionary is no longer filled with the default system mappings, but only contains overrides. + .. attribute:: extra_response_headers + + A sequence of ``(name, value)`` pairs containing user-defined extra HTTP + response headers to add to each successful HTTP status 200 response. These + headers are not included in other status code responses. + + Headers that the server sends automatically such as ``Content-Type`` + will not be overwritten by :attr:`!extra_response_headers`. + The :class:`SimpleHTTPRequestHandler` class defines the following methods: .. method:: do_HEAD() @@ -428,6 +453,9 @@ instantiation, of which this module provides three different variants: followed by a ``'Content-Length:'`` header with the file's size and a ``'Last-Modified:'`` header with the file's modification time. + The instance attribute :attr:`extra_response_headers` is a sequence of + ``(name, value)`` pairs containing user-defined extra response headers. + Then follows a blank line signifying the end of the headers, and then the contents of the file are output. @@ -463,9 +491,11 @@ such as using different index file names by overriding the class attribute Command-line interface ---------------------- -:mod:`http.server` can also be invoked directly using the :option:`-m` +:mod:`!http.server` can also be invoked directly using the :option:`-m` switch of the interpreter. The following example illustrates how to serve -files relative to the current directory:: +files relative to the current directory: + +.. code-block:: bash python -m http.server [OPTIONS] [port] @@ -476,7 +506,9 @@ The following options are accepted: .. option:: port The server listens to port 8000 by default. The default can be overridden - by passing the desired port number as an argument:: + by passing the desired port number as an argument: + + .. code-block:: bash python -m http.server 9000 @@ -485,7 +517,9 @@ The following options are accepted: Specifies a specific address to which it should bind. Both IPv4 and IPv6 addresses are supported. By default, the server binds itself to all interfaces. For example, the following command causes the server to bind - to localhost only:: + to localhost only: + + .. code-block:: bash python -m http.server --bind 127.0.0.1 @@ -498,7 +532,9 @@ The following options are accepted: Specifies a directory to which it should serve the files. By default, the server uses the current directory. For example, the following command - uses a specific directory:: + uses a specific directory: + + .. code-block:: bash python -m http.server --directory /tmp/ @@ -508,15 +544,31 @@ The following options are accepted: Specifies the HTTP version to which the server is conformant. By default, the server is conformant to HTTP/1.0. For example, the following command - runs an HTTP/1.1 conformant server:: + runs an HTTP/1.1 conformant server: + + .. code-block:: bash python -m http.server --protocol HTTP/1.1 .. versionadded:: 3.11 +.. option:: --content-type + + Specifies the default Content-Type HTTP header used when the MIME type + cannot be guessed from the URL's file extension. By default, the server + uses ``'application/octet-stream'``: + + .. code-block:: bash + + python -m http.server --content-type text/html + + .. versionadded:: 3.15 + .. option:: --tls-cert - Specifies a TLS certificate chain for HTTPS connections:: + Specifies a TLS certificate chain for HTTPS connections: + + .. code-block:: bash python -m http.server --tls-cert fullchain.pem @@ -532,17 +584,28 @@ The following options are accepted: .. option:: --tls-password-file - Specifies the password file for password-protected private keys:: + Specifies the password file for password-protected private keys: + + .. code-block:: bash python -m http.server \ --tls-cert cert.pem \ --tls-key key.pem \ --tls-password-file password.txt - This option requires `--tls-cert`` to be specified. + This option requires ``--tls-cert`` to be specified. .. versionadded:: 3.14 +.. option:: -H, --header
+ + Specify an additional extra HTTP Response Header to send on successful HTTP + 200 responses. Can be used multiple times to send additional custom response + headers. Headers that are sent automatically by the server (for instance + Content-Type) will not be overwritten by the server. + + .. versionadded:: 3.15 + .. _http.server-security: @@ -555,6 +618,11 @@ Security considerations requests, this makes it possible for files outside of the specified directory to be served. +Methods :meth:`BaseHTTPRequestHandler.send_header` and +:meth:`BaseHTTPRequestHandler.send_response_only` assume sanitized input +and do not perform input validation such as checking for the presence of CRLF +sequences. Untrusted input may result in HTTP Header injection attacks. + Earlier versions of Python did not scrub control characters from the log messages emitted to stderr from ``python -m http.server`` or the default :class:`BaseHTTPRequestHandler` ``.log_message`` diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst index fabea611e0ebcd..c7c30e5300c2a4 100644 --- a/Doc/library/idle.rst +++ b/Doc/library/idle.rst @@ -3,8 +3,6 @@ IDLE --- Python editor and shell ================================ -.. moduleauthor:: Guido van Rossum - **Source code:** :source:`Lib/idlelib/` .. index:: @@ -13,7 +11,7 @@ IDLE --- Python editor and shell single: Integrated Development Environment .. - Remember to update Lib/idlelib/help.html with idlelib.help.copy_source() when modifying this file. + Remember to update Lib/idlelib/help.html with idlelib.help.copy_strip() when modifying this file. -------------- @@ -37,6 +35,10 @@ IDLE has the following features: * configuration, browsers, and other dialogs +The IDLE application is implemented in the :mod:`idlelib` package. + +.. include:: ../includes/optional-module.rst + Menus ----- @@ -88,7 +90,7 @@ Save Save As... Save the current window with a Save As dialog. The file saved becomes the - new associated file for the window. (If your file namager is set to hide + new associated file for the window. (If your file manager is set to hide extensions, the current extension will be omitted in the file name box. If the new filename has no '.', '.py' and '.txt' will be added for Python and text files, except that on macOS Aqua,'.py' is added for all files.) @@ -154,7 +156,7 @@ Go to Line Show Completions Open a scrollable list allowing selection of existing names. See - :ref:`Completions ` in the Editing and navigation section below. + :ref:`Completions ` in the Editing and Navigation section below. Expand Word Expand a prefix you have typed to match a full word in the same window; @@ -163,7 +165,7 @@ Expand Word Show Call Tip After an unclosed parenthesis for a function, open a small window with function parameter hints. See :ref:`Calltips ` in the - Editing and navigation section below. + Editing and Navigation section below. Show Surrounding Parens Highlight the surrounding parenthesis. @@ -174,9 +176,9 @@ Format menu (Editor window only) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Format Paragraph - Reformat the current blank-line-delimited paragraph in comment block or - multiline string or selected line in a string. All lines in the - paragraph will be formatted to less than N columns, where N defaults to 72. + Rewrap the text block containing the text insert cursor. + Avoid code lines. See :ref:`Format block` in the + Editing and Navigation section below. Indent Region Shift selected lines right by the indent width (default 4 spaces). @@ -204,9 +206,9 @@ New Indent Width Open a dialog to change indent width. The accepted default by the Python community is 4 spaces. -Strip Trailing Chitespace +Strip Trailing Whitespace Remove trailing space and other whitespace characters after the last - non-whitespace character of a line by applying str.rstrip to each line, + non-whitespace character of a line by applying :meth:`str.rstrip` to each line, including lines within multiline strings. Except for Shell windows, remove extra newlines at the end of the file. @@ -562,6 +564,20 @@ In an editor, import statements have no effect until one runs the file. One might want to run a file after writing import statements, after adding function definitions, or after opening an existing file. +.. _format-block: + +Format block +^^^^^^^^^^^^ + +Reformat Paragraph rewraps a block ('paragraph') of contiguous equally +indented non-blank comments, a similar block of text within a multiline +string, or a selected subset of either. +If needed, add a blank line to separate string from code. +Partial lines in a selection expand to complete lines. +The resulting lines have the same indent as before +but have maximum total length of N columns (characters). +Change the default N of 72 on the Window tab of IDLE Settings. + .. _code-context: Code Context @@ -657,7 +673,9 @@ looked for in the user's home directory. Statements in this file will be executed in the Tk namespace, so this file is not useful for importing functions to be used from IDLE's Python shell. -Command line usage +.. _idlelib-cli: + +Command-line usage ^^^^^^^^^^^^^^^^^^ .. program:: idle diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst index 9f198aebcb66b0..df2468f7124e6d 100644 --- a/Doc/library/imaplib.rst +++ b/Doc/library/imaplib.rst @@ -4,14 +4,6 @@ .. module:: imaplib :synopsis: IMAP4 protocol client (requires sockets). -.. moduleauthor:: Piers Lauder -.. sectionauthor:: Piers Lauder -.. revised by ESR, January 2000 -.. changes for IMAP4_SSL by Tino Lange , March 2002 -.. changes for IMAP4_stream by Piers Lauder , - November 2002 -.. changes for IMAP4 IDLE by Forest , August 2024 - **Source code:** :source:`Lib/imaplib.py` .. index:: @@ -29,7 +21,7 @@ note that the ``STATUS`` command is not supported in IMAP4. .. include:: ../includes/wasm-notavail.rst -Three classes are provided by the :mod:`imaplib` module, :class:`IMAP4` is the +Three classes are provided by the :mod:`!imaplib` module, :class:`IMAP4` is the base class: @@ -207,6 +199,11 @@ An :class:`IMAP4` instance has the following methods: Append *message* to named mailbox. + *flags* may be ``None`` or a string of IMAP flag tokens. Multiple + flags are separated by spaces, for example ``r'\Seen \Answered'``. + If *flags* is not already enclosed in parentheses, parentheses are + added automatically. + .. method:: IMAP4.authenticate(mechanism, authobject) @@ -413,6 +410,9 @@ An :class:`IMAP4` instance has the following methods: the password. Will only work if the server ``CAPABILITY`` response includes the phrase ``AUTH=CRAM-MD5``. + .. versionchanged:: 3.15 + An :exc:`IMAP4.error` is raised if MD5 support is not available. + .. method:: IMAP4.logout() @@ -700,6 +700,16 @@ The following attributes are defined on instances of :class:`IMAP4`: .. versionadded:: 3.5 +.. property:: IMAP4.file + + Internal :class:`~io.BufferedReader` associated with the underlying socket. + This property is documented for legacy purposes but not part of the public + interface. The caller is responsible to ensure that the current file is + closed before changing it. + + .. deprecated-removed:: 3.15 3.19 + + .. _imap4-example: IMAP4 Example diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index 12014309e26ec9..e11db37b9fad50 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -18,11 +18,9 @@ the metadata of an installed `Distribution Package `_\s, modules, if any). Built in part on Python's import system, this library -intends to replace similar functionality in the `entry point -API`_ and `metadata API`_ of ``pkg_resources``. Along with -:mod:`importlib.resources`, -this package can eliminate the need to use the older and less efficient -``pkg_resources`` package. +provides the entry point and metadata APIs that were previously +exposed by the now-removed ``pkg_resources`` package. Along with +:mod:`importlib.resources`, it supersedes ``pkg_resources``. ``importlib.metadata`` operates on third-party *distribution packages* installed into Python's ``site-packages`` directory via tools such as @@ -107,6 +105,13 @@ You can also get a :ref:`distribution's version number `, list its current Python environment. +.. exception:: MetadataNotFound + + Subclass of :class:`FileNotFoundError` raised when attempting to load metadata + from a distribution folder that is empty or otherwise does not contain a + metadata file. + + Functional API ============== @@ -125,8 +130,8 @@ Entry points :meth:`!select` method for comparison to the attributes of the individual entry point definitions. - Note: it is not currently possible to query for entry points based on - their :attr:`!EntryPoint.dist` attribute (as different :class:`!Distribution` + Note: to query for entry points based on :attr:`!EntryPoint.dist` attribute, + use :meth:`Distribution.entry_points` instead (as different :class:`Distribution` instances do not currently compare equal, even if they have the same attributes) .. class:: EntryPoints @@ -226,6 +231,9 @@ Distribution metadata Raises :exc:`PackageNotFoundError` if the named distribution package is not installed in the current Python environment. + Raises :exc:`MetadataNotFound` if a distribution package is + present but no METADATA file is present. + .. class:: PackageMetadata A concrete implementation of the @@ -254,6 +262,12 @@ all the metadata in a JSON-compatible form per :PEP:`566`:: The full set of available metadata is not described here. See the PyPA `Core metadata specification `_ for additional details. +.. versionchanged:: 3.15 + Previously and incidentally, if a METADATA file was missing from a distribution, an + empty ``PackageMetadata`` would be returned, indistinguishable from + an empty METADATA file. Now, a missing METADATA file triggers a + ``MetadataNotFound`` exception. + .. versionchanged:: 3.10 The ``Description`` is now included in the metadata when presented through the payload. Line continuation characters have been removed. @@ -291,7 +305,7 @@ Distribution files .. function:: files(distribution_name) Return the full set of files contained within the named - distribution package. + distribution package as :class:`PackagePath` instances. Raises :exc:`PackageNotFoundError` if the named distribution package is not installed in the current Python environment. @@ -304,12 +318,22 @@ Distribution files A :class:`pathlib.PurePath` derived object with additional ``dist``, ``size``, and ``hash`` properties corresponding to the distribution - package's installation metadata for that file. + package's installation metadata for that file, also: + + .. method:: locate() + + If possible, return the concrete :class:`SimplePath` allowing to access data, + or raise a :exc:`NotImplementedError` otherwise. + +.. class:: SimplePath + + A protocol representing a minimal subset of :class:`pathlib.Path` that allows to + check if it ``exists()``, to traverse using ``joinpath()`` and ``parent``, + and to retrieve data using ``read_text()`` and ``read_bytes()``. The :func:`!files` function takes a `Distribution Package `_ -name and returns all of the files installed by this distribution. Each file is reported -as a :class:`PackagePath` instance. For example:: +name and returns all of the files installed by this distribution. For example:: >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0] # doctest: +SKIP >>> util # doctest: +SKIP @@ -402,6 +426,18 @@ function is not reliable with such installs. Distributions ============= +While the module level API described above is the most common and convenient usage, +all that information is accessible from the :class:`Distribution` class. +:class:`!Distribution` is an abstract object that represents the metadata for +a Python `Distribution Package `_. +Get the concrete :class:`!Distribution` subclass instance for an installed +distribution package by calling the :func:`distribution` function:: + + >>> from importlib.metadata import distribution # doctest: +SKIP + >>> dist = distribution('wheel') # doctest: +SKIP + >>> type(dist) # doctest: +SKIP + + .. function:: distribution(distribution_name) Return a :class:`Distribution` instance describing the named @@ -410,6 +446,14 @@ Distributions Raises :exc:`PackageNotFoundError` if the named distribution package is not installed in the current Python environment. +Thus, an alternative way to get e.g. the version number is through the +:attr:`Distribution.version` attribute:: + + >>> dist.version # doctest: +SKIP + '0.32.3' + +The same applies for :func:`entry_points` and :func:`files`. + .. class:: Distribution Details of an installed distribution package. @@ -418,43 +462,88 @@ Distributions equal, even if they relate to the same installed distribution and accordingly have the same attributes. -While the module level API described above is the most common and convenient usage, -you can get all of that information from the :class:`!Distribution` class. -:class:`!Distribution` is an abstract object that represents the metadata for -a Python `Distribution Package `_. -You can get the concrete :class:`!Distribution` subclass instance for an installed -distribution package by calling the :func:`distribution` function:: + .. staticmethod:: at(path) + .. classmethod:: from_name(name) - >>> from importlib.metadata import distribution # doctest: +SKIP - >>> dist = distribution('wheel') # doctest: +SKIP - >>> type(dist) # doctest: +SKIP - + Return a :class:`!Distribution` instance at the given path or + with the given name. -Thus, an alternative way to get the version number is through the -:class:`!Distribution` instance:: + .. classmethod:: discover(*, context=None, **kwargs) - >>> dist.version # doctest: +SKIP - '0.32.3' + Returns an iterable of :class:`!Distribution` instances for all packages + (see distribution-discovery_). -There are all kinds of additional metadata available on :class:`!Distribution` -instances:: + The optional argument *context* is a :class:`DistributionFinder.Context` + instance, used to modify the search for distributions. Alternatively, + *kwargs* may contain keyword arguments for constructing a new + :class:`!DistributionFinder.Context`. - >>> dist.metadata['Requires-Python'] # doctest: +SKIP - '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' - >>> dist.metadata['License'] # doctest: +SKIP - 'MIT' + .. attribute:: metadata + :type: PackageMetadata -For editable packages, an ``origin`` property may present :pep:`610` -metadata:: + Raises :exc:`MetadataNotFound` if the METADATA file is not present in + the distribution. - >>> dist.origin.url - 'file:///path/to/wheel-0.32.3.editable-py3-none-any.whl' + There are all kinds of additional metadata available on :class:`!Distribution` + instances as a :class:`PackageMetadata` instance:: -The full set of available metadata is not described here. -See the PyPA `Core metadata specification `_ for additional details. + >>> dist.metadata['Requires-Python'] # doctest: +SKIP + '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' + >>> dist.metadata['License'] # doctest: +SKIP + 'MIT' + + The full set of available metadata is not described here. + See the PyPA `Core metadata specification `_ for additional details. -.. versionadded:: 3.13 - The ``.origin`` property was added. + .. attribute:: name + :type: str + .. attribute:: requires + :type: list[str] + .. attribute:: version + :type: str + + A few metadata fields are also available as shortcut properties. + + .. versionadded:: 3.10 + + The ``name`` shortcut was added. + + .. attribute:: origin + + For editable packages, an ``origin`` property may present :pep:`610` + metadata (for non-editable packages, ``origin`` is :const:`None`):: + + >>> dist.origin.url + 'file:///path/to/wheel-0.32.3.editable-py3-none-any.whl' + + The ``origin`` object follows the `Direct URL Data Structure + `_. + + .. versionadded:: 3.13 + + .. attribute:: entry_points + :type: EntryPoints + + The entry points provided by this distribution package. + + .. attribute:: files + :type: list[PackagePath] | None + + All files contained in this distribution package. + Like :func:`files`, this returns :const:`None` if there are no records. + + The following two abstract methods need to be implemented when implementing-custom-providers_: + + .. method:: locate_file(path) + + Like :meth:`!PackagePath.locate`, return a :class:`SimplePath` for the given path. + Takes a :class:`os.PathLike` or a :class:`str`. + + .. method:: read_text(filename) + + A shortcut for ``distribution.locate_file(filename).read_text()``. + +.. _distribution-discovery: Distribution Discovery ====================== @@ -466,6 +555,61 @@ This metadata finder search defaults to ``sys.path``, but varies slightly in how - ``importlib.metadata`` does not honor :class:`bytes` objects on ``sys.path``. - ``importlib.metadata`` will incidentally honor :py:class:`pathlib.Path` objects on ``sys.path`` even though such values will be ignored for imports. +.. class:: DistributionFinder + + A :class:`~importlib.abc.MetaPathFinder` subclass capable of discovering + installed distributions. + + Custom providers should implement this interface in order to + supply metadata. + + .. class:: Context(**kwargs) + + A :class:`!Context` gives a custom provider a means to + solicit additional details from the callers of distribution discovery + functions like :func:`distributions` or :meth:`Distribution.discover` + beyond :attr:`!.name` and :attr:`!.path` when searching + for distributions. + + For example, a provider could expose suites of packages in either a + "public" or "private" ``realm``. A caller of distribution discovery + functions may wish to query only for distributions in a particular realm + and could call ``distributions(realm="private")`` to signal to the + custom provider to only include distributions from that + realm. + + Each :class:`!DistributionFinder` must expect any parameters and should + attempt to honor the canonical parameters defined below when + appropriate. + + See the section on :ref:`implementing-custom-providers` for more details. + + .. attribute:: name + + Specific name for which a distribution finder should match. + + A :attr:`!.name` of ``None`` matches all distributions. + + .. attribute:: path + + A property providing the sequence of directory paths that a + distribution finder should search. + + Typically refers to Python installed package paths such as + "site-packages" directories and defaults to :attr:`sys.path`. + + +.. function:: distributions(**kwargs) + + Returns an iterable of :class:`Distribution` instances for all packages. + + The *kwargs* argument may contain either a keyword argument ``context``, a + :class:`DistributionFinder.Context` instance, or pass keyword arguments for + constructing a new :class:`!DistributionFinder.Context`. The + :class:`!DistributionFinder.Context` is used to modify the search for + distributions. + +.. _implementing-custom-providers: Implementing Custom Providers ============================= @@ -493,7 +637,7 @@ interface expected of finders by Python's import system. ``importlib.metadata`` extends this protocol by looking for an optional ``find_distributions`` callable on the finders from :data:`sys.meta_path` and presents this extended interface as the -``DistributionFinder`` abstract base class, which defines this abstract +:class:`DistributionFinder` abstract base class, which defines this abstract method:: @abc.abstractmethod @@ -502,14 +646,16 @@ method:: loading the metadata for packages for the indicated ``context``. """ -The ``DistributionFinder.Context`` object provides ``.path`` and ``.name`` -properties indicating the path to search and name to match and may -supply other relevant context sought by the consumer. +The :class:`DistributionFinder.Context` object provides +:attr:`~DistributionFinder.Context.path` and +:attr:`~DistributionFinder.Context.name` properties indicating the path to +search and name to match and may supply other relevant context sought by the +consumer. In practice, to support finding distribution package metadata in locations other than the file system, subclass -``Distribution`` and implement the abstract methods. Then from -a custom finder, return instances of this derived ``Distribution`` in the +:class:`!Distribution` and implement the abstract methods. Then from +a custom finder, return instances of this derived :class:`!Distribution` in the ``find_distributions()`` method. Example @@ -529,7 +675,7 @@ Imagine a custom finder that loads Python modules from a database:: That importer now presumably provides importable modules from a database, but it provides no metadata or entry points. For this custom importer to provide metadata, it would also need to implement -``DistributionFinder``:: +:class:`DistributionFinder`:: from importlib.metadata import DistributionFinder @@ -586,9 +732,5 @@ packages served by the ``DatabaseImporter``, assuming that the ``.entry_points`` attributes. The ``DatabaseDistribution`` may also provide other metadata files, like -``RECORD`` (required for ``Distribution.files``) or override the -implementation of ``Distribution.files``. See the source for more inspiration. - - -.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points -.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api +``RECORD`` (required for :attr:`!Distribution.files`) or override the +implementation of :attr:`!Distribution.files`. See the source for more inspiration. diff --git a/Doc/library/importlib.resources.abc.rst b/Doc/library/importlib.resources.abc.rst index 8253a33f591a0b..45979c5691270a 100644 --- a/Doc/library/importlib.resources.abc.rst +++ b/Doc/library/importlib.resources.abc.rst @@ -63,11 +63,14 @@ If the resource does not concretely exist on the file system, raise :exc:`FileNotFoundError`. - .. method:: is_resource(name) + .. method:: is_resource(path) :abstractmethod: - Returns ``True`` if the named *name* is considered a resource. - :exc:`FileNotFoundError` is raised if *name* does not exist. + Returns ``True`` if the named *path* is considered a resource. + :exc:`FileNotFoundError` is raised if *path* does not exist. + + .. versionchanged:: 3.10 + The argument *name* was renamed to *path*. .. method:: contents() :abstractmethod: diff --git a/Doc/library/importlib.resources.rst b/Doc/library/importlib.resources.rst index 7a11f4fe069004..72db66f9f06f89 100644 --- a/Doc/library/importlib.resources.rst +++ b/Doc/library/importlib.resources.rst @@ -31,15 +31,13 @@ not** have to exist as physical files and directories on the file system: for example, a package and its resources can be imported from a zip file using :py:mod:`zipimport`. -.. note:: +.. warning:: + + :mod:`importlib.resources` follows the same security model as the built-in + :func:`open` function. Passing untrusted inputs to the functions + in this module is unsafe. - This module provides functionality similar to `pkg_resources - `_ `Basic - Resource Access - `_ - without the performance overhead of that package. This makes reading - resources included in packages easier, with more stable and consistent - semantics. +.. note:: The standalone backport of this module provides more information on `using importlib.resources @@ -73,12 +71,15 @@ for example, a package and its resources can be imported from a zip file using .. versionadded:: 3.9 .. versionchanged:: 3.12 - *package* parameter was renamed to *anchor*. *anchor* can now - be a non-package module and if omitted will default to the caller's - module. *package* is still accepted for compatibility but will raise - a :exc:`DeprecationWarning`. Consider passing the anchor positionally or - using ``importlib_resources >= 5.10`` for a compatible interface - on older Pythons. + *package* parameter was renamed to *anchor*. + *package* was still accepted, but deprecated. + + .. versionchanged:: 3.15 + *package* parameter was fully removed. *anchor* can now be a + non-package module and if omitted will default to the caller's module. + *package* is no longer accepted since Python 3.15. Consider passing the + anchor positionally or using ``importlib_resources >= 5.10`` for a + compatible interface on older Pythons. .. function:: as_file(traversable) @@ -239,7 +240,6 @@ For all the following functions: .. versionchanged:: 3.13 Multiple *path_names* are accepted. - *encoding* and *errors* must be given as keyword arguments. .. function:: is_resource(anchor, *path_names) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 4f374be778d6b3..0b76020eacc1da 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -4,9 +4,6 @@ .. module:: importlib :synopsis: The implementation of the import machinery. -.. moduleauthor:: Brett Cannon -.. sectionauthor:: Brett Cannon - .. versionadded:: 3.1 **Source code:** :source:`Lib/importlib/__init__.py` @@ -17,7 +14,7 @@ Introduction ------------ -The purpose of the :mod:`importlib` package is three-fold. +The purpose of the :mod:`!importlib` package is three-fold. One is to provide the implementation of the :keyword:`import` statement (and thus, by extension, the @@ -124,6 +121,10 @@ Functions need to call :func:`invalidate_caches` in order for the new module to be noticed by the import system. + If the module cannot be imported, :func:`import_module` will raise + :exc:`ImportError` or an appropriate subclass like + :exc:`ModuleNotFoundError`. + .. versionchanged:: 3.3 Parent packages are automatically imported. @@ -211,8 +212,8 @@ Functions in unexpected behavior. It's recommended to use the :class:`threading.Lock` or other synchronization primitives for thread-safe module reloading. -:mod:`importlib.abc` -- Abstract base classes related to import ---------------------------------------------------------------- +:mod:`!importlib.abc` -- Abstract base classes related to import +---------------------------------------------------------------- .. module:: importlib.abc :synopsis: Abstract base classes related to import @@ -222,7 +223,7 @@ Functions -------------- -The :mod:`importlib.abc` module contains all of the core abstract base classes +The :mod:`!importlib.abc` module contains all of the core abstract base classes used by :keyword:`import`. Some subclasses of the core abstract base classes are also provided to help in implementing the core ABCs. @@ -271,6 +272,28 @@ ABC hierarchy:: .. versionchanged:: 3.4 Returns ``None`` when called instead of :data:`NotImplemented`. + .. method:: discover(parent=None) + + An optional method which searches for possible specs with given *parent* + module spec. If *parent* is *None*, :meth:`MetaPathFinder.discover` will + search for top-level modules. + + Returns an iterable of possible specs. + + Raises :exc:`ValueError` if *parent* is not a package module. + + .. warning:: + This method can potentially yield a very large number of objects, and + it may carry out IO operations when computing these values. + + Because of this, it will generally be desirable to compute the result + values on-the-fly, as they are needed. As such, the returned object is + only guaranteed to be an :class:`iterable `, + instead of a :class:`list` or other + :class:`collection ` type. + + .. versionadded:: 3.15 + .. class:: PathEntryFinder @@ -303,6 +326,28 @@ ABC hierarchy:: :meth:`importlib.machinery.PathFinder.invalidate_caches` when invalidating the caches of all cached finders. + .. method:: discover(parent=None) + + An optional method which searches for possible specs with given *parent* + module spec. If *parent* is *None*, :meth:`PathEntryFinder.discover` will + search for top-level modules. + + Returns an iterable of possible specs. + + Raises :exc:`ValueError` if *parent* is not a package module. + + .. warning:: + This method can potentially yield a very large number of objects, and + it may carry out IO operations when computing these values. + + Because of this, it will generally be desirable to compute the result + values on-the-fly, as they are needed. As such, the returned object is + only guaranteed to be an :class:`iterable `, + instead of a :class:`list` or other + :class:`collection ` type. + + .. versionadded:: 3.15 + .. class:: Loader @@ -316,6 +361,9 @@ ABC hierarchy:: .. versionchanged:: 3.7 Introduced the optional :meth:`get_resource_reader` method. + .. versionchanged:: 3.15 + Removed the ``load_module()`` method. + .. method:: create_module(spec) A method that returns the module object to use when @@ -340,47 +388,6 @@ ABC hierarchy:: .. versionchanged:: 3.6 :meth:`create_module` must also be defined. - .. method:: load_module(fullname) - - A legacy method for loading a module. If the module cannot be - loaded, :exc:`ImportError` is raised, otherwise the loaded module is - returned. - - If the requested module already exists in :data:`sys.modules`, that - module should be used and reloaded. - Otherwise the loader should create a new module and insert it into - :data:`sys.modules` before any loading begins, to prevent recursion - from the import. If the loader inserted a module and the load fails, it - must be removed by the loader from :data:`sys.modules`; modules already - in :data:`sys.modules` before the loader began execution should be left - alone. - - The loader should set several attributes on the module - (note that some of these attributes can change when a module is - reloaded): - - - :attr:`module.__name__` - - :attr:`module.__file__` - - :attr:`module.__cached__` *(deprecated)* - - :attr:`module.__path__` - - :attr:`module.__package__` *(deprecated)* - - :attr:`module.__loader__` *(deprecated)* - - When :meth:`exec_module` is available then backwards-compatible - functionality is provided. - - .. versionchanged:: 3.4 - Raise :exc:`ImportError` when called instead of - :exc:`NotImplementedError`. Functionality provided when - :meth:`exec_module` is available. - - .. deprecated-removed:: 3.4 3.15 - The recommended API for loading a module is :meth:`exec_module` - (and :meth:`create_module`). Loaders should implement it instead of - :meth:`load_module`. The import machinery takes care of all the - other responsibilities of :meth:`load_module` when - :meth:`exec_module` is implemented. - .. class:: ResourceLoader @@ -393,6 +400,8 @@ ABC hierarchy:: .. deprecated:: 3.7 This ABC is deprecated in favour of supporting resource loading through :class:`importlib.resources.abc.TraversableResources`. + This class exists for backwards compatibility only with other ABCs in + this module. .. method:: get_data(path) :abstractmethod: @@ -453,7 +462,7 @@ ABC hierarchy:: .. versionchanged:: 3.4 Raises :exc:`ImportError` instead of :exc:`NotImplementedError`. - .. staticmethod:: source_to_code(data, path='') + .. staticmethod:: source_to_code(data, path='', fullname=None) Create a code object from Python source. @@ -465,24 +474,25 @@ ABC hierarchy:: With the subsequent code object one can execute it in a module by running ``exec(code, module.__dict__)``. + The optional argument *fullname* specifies the module name. + It is needed to unambiguous :ref:`filter ` syntax + warnings by module name. + .. versionadded:: 3.4 .. versionchanged:: 3.5 Made the method static. + .. versionadded:: 3.15 + Added the *fullname* parameter. + + .. method:: exec_module(module) Implementation of :meth:`Loader.exec_module`. .. versionadded:: 3.4 - .. method:: load_module(fullname) - - Implementation of :meth:`Loader.load_module`. - - .. deprecated-removed:: 3.4 3.15 - use :meth:`exec_module` instead. - .. class:: ExecutionLoader @@ -516,6 +526,9 @@ ABC hierarchy:: .. versionadded:: 3.3 + .. versionchanged:: 3.15 + Removed the ``load_module()`` method. + .. attribute:: name The name of the module the loader can handle. @@ -524,13 +537,6 @@ ABC hierarchy:: Path to the file of the module. - .. method:: load_module(fullname) - - Calls super's ``load_module()``. - - .. deprecated-removed:: 3.4 3.15 - Use :meth:`Loader.exec_module` instead. - .. method:: get_filename(fullname) :abstractmethod: @@ -562,6 +568,9 @@ ABC hierarchy:: optimization to speed up loading by removing the parsing step of Python's compiler, and so no bytecode-specific API is exposed. + .. versionchanged:: 3.15 + Removed the ``load_module()`` method. + .. method:: path_stats(path) Optional abstract method which returns a :class:`dict` containing @@ -615,13 +624,6 @@ ABC hierarchy:: .. versionadded:: 3.4 - .. method:: load_module(fullname) - - Concrete implementation of :meth:`Loader.load_module`. - - .. deprecated-removed:: 3.4 3.15 - Use :meth:`exec_module` instead. - .. method:: get_source(fullname) Concrete implementation of :meth:`InspectLoader.get_source`. @@ -635,174 +637,8 @@ ABC hierarchy:: itself does not end in ``__init__``. -.. class:: ResourceReader - - *Superseded by TraversableResources* - - An :term:`abstract base class` to provide the ability to read - *resources*. - - From the perspective of this ABC, a *resource* is a binary - artifact that is shipped within a package. Typically this is - something like a data file that lives next to the ``__init__.py`` - file of the package. The purpose of this class is to help abstract - out the accessing of such data files so that it does not matter if - the package and its data file(s) are stored e.g. in a zip file - versus on the file system. - - For any of methods of this class, a *resource* argument is - expected to be a :term:`path-like object` which represents - conceptually just a file name. This means that no subdirectory - paths should be included in the *resource* argument. This is - because the location of the package the reader is for, acts as the - "directory". Hence the metaphor for directories and file - names is packages and resources, respectively. This is also why - instances of this class are expected to directly correlate to - a specific package (instead of potentially representing multiple - packages or a module). - - Loaders that wish to support resource reading are expected to - provide a method called ``get_resource_reader(fullname)`` which - returns an object implementing this ABC's interface. If the module - specified by fullname is not a package, this method should return - :const:`None`. An object compatible with this ABC should only be - returned when the specified module is a package. - - .. versionadded:: 3.7 - - .. deprecated-removed:: 3.12 3.14 - Use :class:`importlib.resources.abc.TraversableResources` instead. - - .. method:: open_resource(resource) - :abstractmethod: - - Returns an opened, :term:`file-like object` for binary reading - of the *resource*. - - If the resource cannot be found, :exc:`FileNotFoundError` is - raised. - - .. method:: resource_path(resource) - :abstractmethod: - - Returns the file system path to the *resource*. - - If the resource does not concretely exist on the file system, - raise :exc:`FileNotFoundError`. - - .. method:: is_resource(name) - :abstractmethod: - - Returns ``True`` if the named *name* is considered a resource. - :exc:`FileNotFoundError` is raised if *name* does not exist. - - .. method:: contents() - :abstractmethod: - - Returns an :term:`iterable` of strings over the contents of - the package. Do note that it is not required that all names - returned by the iterator be actual resources, e.g. it is - acceptable to return names for which :meth:`is_resource` would - be false. - - Allowing non-resource names to be returned is to allow for - situations where how a package and its resources are stored - are known a priori and the non-resource names would be useful. - For instance, returning subdirectory names is allowed so that - when it is known that the package and resources are stored on - the file system then those subdirectory names can be used - directly. - - The abstract method returns an iterable of no items. - - -.. class:: Traversable - - An object with a subset of :class:`pathlib.Path` methods suitable for - traversing directories and opening files. - - For a representation of the object on the file-system, use - :meth:`importlib.resources.as_file`. - - .. versionadded:: 3.9 - - .. deprecated-removed:: 3.12 3.14 - Use :class:`importlib.resources.abc.Traversable` instead. - - .. attribute:: name - - Abstract. The base name of this object without any parent references. - - .. method:: iterdir() - :abstractmethod: - - Yield ``Traversable`` objects in ``self``. - - .. method:: is_dir() - :abstractmethod: - - Return ``True`` if ``self`` is a directory. - - .. method:: is_file() - :abstractmethod: - - Return ``True`` if ``self`` is a file. - - .. method:: joinpath(child) - :abstractmethod: - - Return Traversable child in ``self``. - - .. method:: __truediv__(child) - :abstractmethod: - - Return ``Traversable`` child in ``self``. - - .. method:: open(mode='r', *args, **kwargs) - :abstractmethod: - - *mode* may be 'r' or 'rb' to open as text or binary. Return a handle - suitable for reading (same as :attr:`pathlib.Path.open`). - - When opening as text, accepts encoding parameters such as those - accepted by :class:`io.TextIOWrapper`. - - .. method:: read_bytes() - - Read contents of ``self`` as bytes. - - .. method:: read_text(encoding=None) - - Read contents of ``self`` as text. - - -.. class:: TraversableResources - - An abstract base class for resource readers capable of serving - the :meth:`importlib.resources.files` interface. Subclasses - :class:`importlib.resources.abc.ResourceReader` and provides - concrete implementations of the :class:`importlib.resources.abc.ResourceReader`'s - abstract methods. Therefore, any loader supplying - :class:`importlib.abc.TraversableResources` also supplies ResourceReader. - - Loaders that wish to support resource reading are expected to - implement this interface. - - .. versionadded:: 3.9 - - .. deprecated-removed:: 3.12 3.14 - Use :class:`importlib.resources.abc.TraversableResources` instead. - - .. method:: files() - :abstractmethod: - - Returns a :class:`importlib.resources.abc.Traversable` object for the loaded - package. - - - -:mod:`importlib.machinery` -- Importers and path hooks ------------------------------------------------------- +:mod:`!importlib.machinery` -- Importers and path hooks +------------------------------------------------------- .. module:: importlib.machinery :synopsis: Importers and path hooks @@ -1007,6 +843,36 @@ find and load modules. :exc:`ImportError` is raised. +.. class:: NamespacePath(name, path, path_finder) + + Represents a :term:`namespace package`'s path (:attr:`module.__path__`). + + When its ``__path__`` value is accessed it will be recomputed if necessary. + This keeps it in-sync with the global state (:attr:`sys.modules`). + + The *name* argument is the name of the namespace module. + + The *path* argument is the initial path value. + + The *path_finder* argument is the callable used to recompute the path value. + The callable has the same signature as :meth:`importlib.abc.MetaPathFinder.find_spec`. + + When the parent's :attr:`module.__path__` attribute is updated, the path + value is recomputed. + + If the parent module is missing from :data:`sys.modules`, then + :exc:`ModuleNotFoundError` will be raised. + + For top-level modules, the parent module's path is :data:`sys.path`. + + .. note:: + + :meth:`PathFinder.invalidate_caches` invalidates :class:`NamespacePath`, + forcing the path value to be recomputed next time it is accessed. + + .. versionadded:: 3.15 + + .. class:: SourceFileLoader(fullname, path) A concrete implementation of :class:`importlib.abc.SourceLoader` by @@ -1015,6 +881,9 @@ find and load modules. .. versionadded:: 3.3 + .. versionchanged:: 3.15 + Removed the ``load_module()`` method. + .. attribute:: name The name of the module that this loader will handle. @@ -1035,15 +904,6 @@ find and load modules. Concrete implementation of :meth:`importlib.abc.SourceLoader.set_data`. - .. method:: load_module(name=None) - - Concrete implementation of :meth:`importlib.abc.Loader.load_module` where - specifying the name of the module to load is optional. - - .. deprecated-removed:: 3.6 3.15 - - Use :meth:`importlib.abc.Loader.exec_module` instead. - .. class:: SourcelessFileLoader(fullname, path) @@ -1057,6 +917,9 @@ find and load modules. .. versionadded:: 3.3 + .. versionchanged:: 3.15 + Removed the ``load_module()`` method. + .. attribute:: name The name of the module the loader will handle. @@ -1078,15 +941,6 @@ find and load modules. Returns ``None`` as bytecode files have no source when this loader is used. - .. method:: load_module(name=None) - - Concrete implementation of :meth:`importlib.abc.Loader.load_module` where - specifying the name of the module to load is optional. - - .. deprecated-removed:: 3.6 3.15 - - Use :meth:`importlib.abc.Loader.exec_module` instead. - .. class:: ExtensionFileLoader(fullname, path) @@ -1218,8 +1072,7 @@ find and load modules. .. attribute:: cached - The filename of a compiled version of the module's code - (see :attr:`module.__cached__`). + The filename of a compiled version of the module's code. The :term:`finder` should always set this attribute but it may be ``None`` for modules that do not need compiled code stored. @@ -1251,7 +1104,7 @@ find and load modules. To accommodate this requirement, when running on iOS, extension module binaries are *not* packaged as ``.so`` files on ``sys.path``, but as individual standalone frameworks. To discover those frameworks, this loader - is be registered against the ``.fwork`` file extension, with a ``.fwork`` + is registered against the ``.fwork`` file extension, with a ``.fwork`` file acting as a placeholder in the original location of the binary on ``sys.path``. The ``.fwork`` file contains the path of the actual binary in the ``Frameworks`` folder, relative to the app bundle. To allow for @@ -1300,8 +1153,8 @@ find and load modules. Path to the ``.fwork`` file for the extension module. -:mod:`importlib.util` -- Utility code for importers ---------------------------------------------------- +:mod:`!importlib.util` -- Utility code for importers +---------------------------------------------------- .. module:: importlib.util :synopsis: Utility code for importers @@ -1321,7 +1174,7 @@ an :term:`importer`. .. versionadded:: 3.4 -.. function:: cache_from_source(path, debug_override=None, *, optimization=None) +.. function:: cache_from_source(path, *, optimization=None) Return the :pep:`3147`/:pep:`488` path to the byte-compiled file associated with the source *path*. For example, if *path* is ``/foo/bar/baz.py`` the return @@ -1340,12 +1193,6 @@ an :term:`importer`. ``/foo/bar/__pycache__/baz.cpython-32.opt-2.pyc``. The string representation of *optimization* can only be alphanumeric, else :exc:`ValueError` is raised. - The *debug_override* parameter is deprecated and can be used to override - the system's value for ``__debug__``. A ``True`` value is the equivalent of - setting *optimization* to the empty string. A ``False`` value is the same as - setting *optimization* to ``1``. If both *debug_override* an *optimization* - are not ``None`` then :exc:`TypeError` is raised. - .. versionadded:: 3.4 .. versionchanged:: 3.5 @@ -1355,6 +1202,9 @@ an :term:`importer`. .. versionchanged:: 3.6 Accepts a :term:`path-like object`. + .. versionchanged:: 3.15 + The *debug_override* parameter was removed. + .. function:: source_from_cache(path) diff --git a/Doc/library/index.rst b/Doc/library/index.rst index 44b218948d07e1..8fc77be520d426 100644 --- a/Doc/library/index.rst +++ b/Doc/library/index.rst @@ -43,6 +43,7 @@ the `Python Package Index `_. constants.rst stdtypes.rst exceptions.rst + threadsafety.rst text.rst binary.rst @@ -63,7 +64,6 @@ the `Python Package Index `_. internet.rst mm.rst i18n.rst - frameworks.rst tk.rst development.rst debug.rst diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index e8d1176f477866..a0f7379b12a8a6 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -9,14 +9,11 @@ .. module:: inspect :synopsis: Extract information and source code from live objects. -.. moduleauthor:: Ka-Ping Yee -.. sectionauthor:: Ka-Ping Yee - **Source code:** :source:`Lib/inspect.py` -------------- -The :mod:`inspect` module provides several useful functions to help get +The :mod:`!inspect` module provides several useful functions to help get information about live objects such as modules, classes, methods, functions, tracebacks, frame objects, and code objects. For example, it can help you examine the contents of a class, retrieve the source code of a method, extract @@ -198,10 +195,6 @@ attributes (see :ref:`import-mod-attrs` for module attributes): | | | read more :ref:`here | | | | `| +-----------------+-------------------+---------------------------+ -| | co_lnotab | encoded mapping of line | -| | | numbers to bytecode | -| | | indices | -+-----------------+-------------------+---------------------------+ | | co_freevars | tuple of names of free | | | | variables (referenced via | | | | a function's closure) | @@ -253,12 +246,21 @@ attributes (see :ref:`import-mod-attrs` for module attributes): +-----------------+-------------------+---------------------------+ | | gi_running | is the generator running? | +-----------------+-------------------+---------------------------+ +| | gi_suspended | is the generator | +| | | suspended? | ++-----------------+-------------------+---------------------------+ | | gi_code | code | +-----------------+-------------------+---------------------------+ | | gi_yieldfrom | object being iterated by | | | | ``yield from``, or | | | | ``None`` | +-----------------+-------------------+---------------------------+ +| | gi_state | state of the generator, | +| | | one of ``GEN_CREATED``, | +| | | ``GEN_RUNNING``, | +| | | ``GEN_SUSPENDED``, or | +| | | ``GEN_CLOSED`` | ++-----------------+-------------------+---------------------------+ | async generator | __name__ | name | +-----------------+-------------------+---------------------------+ | | __qualname__ | qualified name | @@ -270,8 +272,18 @@ attributes (see :ref:`import-mod-attrs` for module attributes): +-----------------+-------------------+---------------------------+ | | ag_running | is the generator running? | +-----------------+-------------------+---------------------------+ +| | ag_suspended | is the generator | +| | | suspended? | ++-----------------+-------------------+---------------------------+ | | ag_code | code | +-----------------+-------------------+---------------------------+ +| | ag_state | state of the async | +| | | generator, one of | +| | | ``AGEN_CREATED``, | +| | | ``AGEN_RUNNING``, | +| | | ``AGEN_SUSPENDED``, or | +| | | ``AGEN_CLOSED`` | ++-----------------+-------------------+---------------------------+ | coroutine | __name__ | name | +-----------------+-------------------+---------------------------+ | | __qualname__ | qualified name | @@ -283,12 +295,21 @@ attributes (see :ref:`import-mod-attrs` for module attributes): +-----------------+-------------------+---------------------------+ | | cr_running | is the coroutine running? | +-----------------+-------------------+---------------------------+ +| | cr_suspended | is the coroutine | +| | | suspended? | ++-----------------+-------------------+---------------------------+ | | cr_code | code | +-----------------+-------------------+---------------------------+ | | cr_origin | where coroutine was | | | | created, or ``None``. See | | | | |coroutine-origin-link| | +-----------------+-------------------+---------------------------+ +| | cr_state | state of the coroutine, | +| | | one of ``CORO_CREATED``, | +| | | ``CORO_RUNNING``, | +| | | ``CORO_SUSPENDED``, or | +| | | ``CORO_CLOSED`` | ++-----------------+-------------------+---------------------------+ | builtin | __doc__ | documentation string | +-----------------+-------------------+---------------------------+ | | __name__ | original name of this | @@ -316,10 +337,27 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Add ``__builtins__`` attribute to functions. +.. versionchanged:: 3.11 + + Add ``gi_suspended`` attribute to generators. + +.. versionchanged:: 3.11 + + Add ``cr_suspended`` attribute to coroutines. + +.. versionchanged:: 3.12 + + Add ``ag_suspended`` attribute to async generators. + .. versionchanged:: 3.14 Add ``f_generator`` attribute to frames. +.. versionchanged:: 3.15 + + Add ``gi_state`` attribute to generators, ``cr_state`` attribute to + coroutines, and ``ag_state`` attribute to async generators. + .. function:: getmembers(object[, predicate]) Return all the members of an object in a list of ``(name, value)`` @@ -378,17 +416,47 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Return ``True`` if the object is a class, whether built-in or created in Python code. + This function returns ``False`` for :ref:`generic aliases ` of classes, + such as ``list[int]``. + .. function:: ismethod(object) Return ``True`` if the object is a bound method written in Python. + .. note:: -.. function:: ispackage(object) + For example, given this class:: - Return ``True`` if the object is a :term:`package`. + >>> class Greeter: + ... def say_hello(self): + ... print('hello!') - .. versionadded:: 3.14 + A bound method (also known as an *instance method*) is created when + accessing ``say_hello`` (a :term:`function` defined in the + ``Greeter`` namespace) through an instance of the ``Greeter`` class:: + + >>> instance = Greeter() + + >>> instance.say_hello + > + >>> ismethod(instance.say_hello) + True + >>> isfunction(instance.say_hello) + False + + Accessing ``say_hello`` through the ``Greeter`` class will return the + function itself. For this function, :func:`ismethod` will return + ``False``, but :func:`isfunction` will return ``True``:: + + >>> Greeter.say_hello + + >>> ismethod(Greeter.say_hello) + False + >>> isfunction(Greeter.say_hello) + True + + See :ref:`typesmethods` for details. .. function:: isfunction(object) @@ -396,11 +464,23 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Return ``True`` if the object is a Python function, which includes functions created by a :term:`lambda` expression. + See the note for :func:`~inspect.ismethod` for an example. + + +.. function:: ispackage(object) + + Return ``True`` if the object is a :term:`package`. + + .. versionadded:: 3.14 + .. function:: isgeneratorfunction(object) Return ``True`` if the object is a Python generator function. + It also returns ``True`` for bound methods created from Python generator functions + (see :ref:`typesmethods` for more information). + .. versionchanged:: 3.8 Functions wrapped in :func:`functools.partial` now return ``True`` if the wrapped function is a Python generator function. @@ -503,7 +583,7 @@ attributes (see :ref:`import-mod-attrs` for module attributes): .. versionchanged:: 3.13 Functions wrapped in :func:`functools.partialmethod` now return ``True`` - if the wrapped function is a :term:`coroutine function`. + if the wrapped function is a :term:`asynchronous generator` function. .. function:: isasyncgen(object) @@ -616,17 +696,29 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Retrieving source code ---------------------- -.. function:: getdoc(object) +.. function:: getdoc(object, *, inherit_class_doc=True, fallback_to_class_doc=True) Get the documentation string for an object, cleaned up with :func:`cleandoc`. - If the documentation string for an object is not provided and the object is - a class, a method, a property or a descriptor, retrieve the documentation - string from the inheritance hierarchy. + If the documentation string for an object is not provided: + + * if the object is a class and *inherit_class_doc* is true (by default), + retrieve the documentation string from the inheritance hierarchy; + * if the object is a method, a property or a descriptor, retrieve + the documentation string from the inheritance hierarchy; + * otherwise, if *fallback_to_class_doc* is true (by default), retrieve + the documentation string from the class of the object. + Return ``None`` if the documentation string is invalid or missing. .. versionchanged:: 3.5 Documentation strings are now inherited if not overridden. + .. versionchanged:: 3.15 + Added parameters *inherit_class_doc* and *fallback_to_class_doc*. + + Documentation strings on :class:`~functools.cached_property` + objects are now inherited if not overridden. + .. function:: getcomments(object) @@ -1139,7 +1231,7 @@ Classes and functions times. -.. function:: getfullargspec(func) +.. function:: getfullargspec(func, *, annotation_format=Format.VALUE) Get the names and default values of a Python function's parameters. A :term:`named tuple` is returned: @@ -1169,6 +1261,14 @@ Classes and functions APIs. This function is retained primarily for use in code that needs to maintain compatibility with the Python 2 ``inspect`` module API. + A member of the + :class:`annotationlib.Format` enum can be passed to the + *annotation_format* parameter to control the format of the returned + annotations. For example, use + ``annotation_format=annotationlib.Format.STRING`` to return annotations in string + format. Note that with the default ``VALUE`` format, creation of some argspecs + may raise an exception. + .. versionchanged:: 3.4 This function is now based on :func:`signature`, but still ignores ``__wrapped__`` attributes and includes the already bound first @@ -1179,13 +1279,16 @@ Classes and functions :func:`signature` in Python 3.5, but that decision has been reversed in order to restore a clearly supported standard interface for single-source Python 2/3 code migrating away from the legacy - :func:`getargspec` API. + :func:`!getargspec` API. .. versionchanged:: 3.7 Python only explicitly guaranteed that it preserved the declaration order of keyword-only parameters as of version 3.7, although in practice this order had always been preserved in Python 3. + .. versionchanged:: 3.15 + The *annotation_format* parameter was added. + .. function:: getargvalues(frame) @@ -1289,6 +1392,11 @@ Classes and functions This is an alias for :func:`annotationlib.get_annotations`; see the documentation of that function for more information. + .. caution:: + + This function may execute arbitrary code contained in annotations. + See :ref:`annotationlib-security` for more information. + .. versionadded:: 3.10 .. versionchanged:: 3.14 @@ -1506,10 +1614,11 @@ properties, will be invoked and :meth:`~object.__getattr__` and may be called. For cases where you want passive introspection, like documentation tools, this -can be inconvenient. :func:`getattr_static` has the same signature as :func:`getattr` +can be inconvenient. :func:`getattr_static` has a similar signature as :func:`getattr` but avoids executing code when it fetches attributes. -.. function:: getattr_static(obj, attr, default=None) +.. function:: getattr_static(obj, attr) + getattr_static(obj, attr, default) Retrieve attributes without triggering dynamic lookup via the descriptor protocol, :meth:`~object.__getattr__` @@ -1729,7 +1838,7 @@ which is a bitmap of the following flags: The flags are specific to CPython, and may not be defined in other Python implementations. Furthermore, the flags are an implementation detail, and can be removed or deprecated in future Python releases. - It's recommended to use public APIs from the :mod:`inspect` module + It's recommended to use public APIs from the :mod:`!inspect` module for any introspection needs. @@ -1768,18 +1877,25 @@ Buffer flags .. _inspect-module-cli: -Command Line Interface +Command-line interface ---------------------- -The :mod:`inspect` module also provides a basic introspection capability +The :mod:`!inspect` module also provides a basic introspection capability from the command line. .. program:: inspect By default, accepts the name of a module and prints the source of that module. A class or function within the module can be printed instead by -appended a colon and the qualified name of the target object. +appending a colon and the qualified name of the target object. .. option:: --details Print information about the specified object rather than the source code + +.. versionchanged:: 3.15 + + The ``--details`` option now supports basic introspection for modules + without available source code and indicates when modules are frozen. + It also indicates when the given target reference is not the canonical + name of the referenced object. diff --git a/Doc/library/io.rst b/Doc/library/io.rst index dfebccb5a9cb91..d47b74efe22de9 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -4,14 +4,6 @@ .. module:: io :synopsis: Core tools for working with streams. -.. moduleauthor:: Guido van Rossum -.. moduleauthor:: Mike Verdone -.. moduleauthor:: Mark Russell -.. moduleauthor:: Antoine Pitrou -.. moduleauthor:: Amaury Forgeot d'Arc -.. moduleauthor:: Benjamin Peterson -.. sectionauthor:: Benjamin Peterson - **Source code:** :source:`Lib/io.py` -------------- @@ -24,7 +16,7 @@ Overview .. index:: single: file object; io module -The :mod:`io` module provides Python's main facilities for dealing with various +The :mod:`!io` module provides Python's main facilities for dealing with various types of I/O. There are three main types of I/O: *text I/O*, *binary I/O* and *raw I/O*. These are generic categories, and various backing stores can be used for each of them. A concrete object belonging to any of these @@ -46,6 +38,7 @@ will raise a :exc:`TypeError`. So will giving a :class:`bytes` object to the Operations that used to raise :exc:`IOError` now raise :exc:`OSError`, since :exc:`IOError` is now an alias of :exc:`OSError`. +.. _text-io: Text I/O ^^^^^^^^ @@ -73,6 +66,7 @@ In-memory text streams are also available as :class:`StringIO` objects:: The text stream API is described in detail in the documentation of :class:`TextIOBase`. +.. _binary-io: Binary I/O ^^^^^^^^^^ @@ -111,6 +105,13 @@ stream by opening a file in binary mode with buffering disabled:: The raw stream API is described in detail in the docs of :class:`RawIOBase`. +.. warning:: + Raw I/O is a low-level interface and methods generally must have their return + values checked and be explicitly retried to ensure an operation completes. + For instance :meth:`~RawIOBase.write` returns the number of bytes written + which may be less than the number of bytes provided (a partial write). + High-level I/O objects like :ref:`binary-io` and :ref:`text-io` implement + retry behavior. .. _io-text-encoding: @@ -292,7 +293,7 @@ interface to a buffered raw stream (:class:`BufferedIOBase`). Finally, Argument names are not part of the specification, and only the arguments of :func:`open` are intended to be used as keyword arguments. -The following table summarizes the ABCs provided by the :mod:`io` module: +The following table summarizes the ABCs provided by the :mod:`!io` module: .. tabularcolumns:: |l|l|L|L| @@ -486,8 +487,11 @@ I/O Base Classes Read up to *size* bytes from the object and return them. As a convenience, if *size* is unspecified or -1, all bytes until EOF are returned. - Otherwise, only one system call is ever made. Fewer than *size* bytes may - be returned if the operating system call returns fewer than *size* bytes. + + Attempts to make only one system call but will retry if interrupted and + the signal handler does not raise an exception (see :pep:`475` for the + rationale). This means fewer than *size* bytes may be returned if the + operating system call returns fewer than *size* bytes. If 0 bytes are returned, and *size* was not 0, this indicates end of file. If the object is in non-blocking mode and no bytes are available, @@ -501,13 +505,19 @@ I/O Base Classes Read and return all the bytes from the stream until EOF, using multiple calls to the stream if necessary. + If ``0`` bytes are returned this indicates end of file. If the object is in + non-blocking mode and the underlying :meth:`read` returns ``None`` + indicating no bytes are available, ``None`` is returned. + .. method:: readinto(b, /) Read bytes into a pre-allocated, writable :term:`bytes-like object` *b*, and return the number of bytes read. For example, *b* might be a :class:`bytearray`. - If the object is in non-blocking mode and no bytes - are available, ``None`` is returned. + + If ``0`` is returned and ``len(b)`` is not ``0``, this indicates end of file. If + the object is in non-blocking mode and no bytes are available, ``None`` is + returned. .. method:: write(b, /) @@ -521,6 +531,13 @@ I/O Base Classes this method returns, so the implementation should only access *b* during the method call. + .. warning:: + + This function does not ensure all bytes are written or an exception is + thrown. Callers may implement that behavior by checking the return + value and, if it is less than the length of *b*, looping with additional + write calls until all unwritten bytes are written. High-level I/O + objects like :ref:`binary-io` and :ref:`text-io` implement retry behavior. .. class:: BufferedIOBase @@ -587,7 +604,7 @@ I/O Base Classes When the underlying raw stream is non-blocking, implementations may either raise :exc:`BlockingIOError` or return ``None`` if no data is - available. :mod:`io` implementations return ``None``. + available. :mod:`!io` implementations return ``None``. .. method:: read1(size=-1, /) @@ -600,7 +617,7 @@ I/O Base Classes When the underlying raw stream is non-blocking, implementations may either raise :exc:`BlockingIOError` or return ``None`` if no data is - available. :mod:`io` implementations return ``None``. + available. :mod:`!io` implementations return ``None``. .. method:: readinto(b, /) @@ -649,7 +666,11 @@ Raw File I/O .. class:: FileIO(name, mode='r', closefd=True, opener=None) A raw binary stream representing an OS-level file containing bytes data. It - inherits from :class:`RawIOBase`. + inherits from :class:`RawIOBase` and implements its low-level access design. + This means :meth:`~RawIOBase.write` does not guarantee all bytes are written + and :meth:`~RawIOBase.read` may read less bytes than requested even when more + bytes may be present in the underlying file. To get "write all" and + "read at least" behavior, use :ref:`binary-io`. The *name* can be one of two things: @@ -669,10 +690,6 @@ Raw File I/O implies writing, so this mode behaves in a similar way to ``'w'``. Add a ``'+'`` to the mode to allow simultaneous reading and writing. - The :meth:`~RawIOBase.read` (when called with a positive argument), - :meth:`~RawIOBase.readinto` and :meth:`~RawIOBase.write` methods on this - class will only make one system call. - A custom opener can be used by passing a callable as *opener*. The underlying file descriptor for the file object is then obtained by calling *opener* with (*name*, *flags*). *opener* must return an open file descriptor (passing @@ -684,6 +701,13 @@ Raw File I/O See the :func:`open` built-in function for examples on using the *opener* parameter. + .. warning:: + :class:`FileIO` is a low-level I/O object and members, such as + :meth:`~RawIOBase.read` and :meth:`~RawIOBase.write`, need to have their + return values checked explicitly in a retry loop to implement "write all" + and "read at least" behavior. High-level I/O objects :ref:`binary-io` and + :ref:`text-io` implement retry behavior. + .. versionchanged:: 3.3 The *opener* parameter was added. The ``'x'`` mode was added. @@ -720,7 +744,7 @@ than raw I/O does. contains initial data. Methods may be used from multiple threads without external locking in - :term:`free threading` builds. + :term:`free-threaded builds `. :class:`BytesIO` provides or overrides these methods in addition to those from :class:`BufferedIOBase` and :class:`IOBase`: @@ -832,9 +856,9 @@ than raw I/O does. .. class:: BufferedRandom(raw, buffer_size=DEFAULT_BUFFER_SIZE) - A buffered binary stream providing higher-level access to a seekable - :class:`RawIOBase` raw binary stream. It inherits from :class:`BufferedReader` - and :class:`BufferedWriter`. + A buffered binary stream implementing :class:`BufferedIOBase` interfaces + providing higher-level access to a seekable :class:`RawIOBase` raw binary + stream. The constructor creates a reader and writer for a seekable raw stream, given in the first argument. If the *buffer_size* is omitted it defaults to diff --git a/Doc/library/ipaddress.rst b/Doc/library/ipaddress.rst index 9e887d8e65741b..9ccd8602bcb2c3 100644 --- a/Doc/library/ipaddress.rst +++ b/Doc/library/ipaddress.rst @@ -4,13 +4,11 @@ .. module:: ipaddress :synopsis: IPv4/IPv6 manipulation library. -.. moduleauthor:: Peter Moody - **Source code:** :source:`Lib/ipaddress.py` -------------- -:mod:`ipaddress` provides the capabilities to create, manipulate and +:mod:`!ipaddress` provides the capabilities to create, manipulate and operate on IPv4 and IPv6 addresses and networks. The functions and classes in this module make it straightforward to handle @@ -34,7 +32,7 @@ This is the full module API reference—for an overview and introduction, see Convenience factory functions ----------------------------- -The :mod:`ipaddress` module provides factory functions to conveniently create +The :mod:`!ipaddress` module provides factory functions to conveniently create IP addresses, networks and interfaces: .. function:: ip_address(address) @@ -369,9 +367,9 @@ write code that handles both IP versions correctly. Address objects are .. attribute:: ipv4_mapped - For addresses that appear to be IPv4 mapped addresses (starting with - ``::FFFF/96``), this property will report the embedded IPv4 address. - For any other address, this property will be ``None``. + For addresses that appear to be IPv4 mapped addresses in the range + ``::FFFF:0:0/96`` as defined by :RFC:`4291`, this property reports the + embedded IPv4 address. For any other address, this property will be ``None``. .. attribute:: scope_id @@ -1027,7 +1025,7 @@ The module also provides the following module level functions: IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24') doesn't make sense. There are some times however, where you may wish to - have :mod:`ipaddress` sort these anyway. If you need to do this, you can use + have :mod:`!ipaddress` sort these anyway. If you need to do this, you can use this function as the *key* argument to :func:`sorted`. *obj* is either a network or address object. diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index aa46920d3526f0..06f8bf2a8b6fa8 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -4,9 +4,6 @@ .. module:: itertools :synopsis: Functions creating iterators for efficient looping. -.. moduleauthor:: Raymond Hettinger -.. sectionauthor:: Raymond Hettinger - .. testsetup:: from itertools import * @@ -30,32 +27,24 @@ For instance, SML provides a tabulation tool: ``tabulate(f)`` which produces a sequence ``f(0), f(1), ...``. The same effect can be achieved in Python by combining :func:`map` and :func:`count` to form ``map(f, count())``. - -**Infinite iterators:** - -================== ================= ================================================= ========================================= -Iterator Arguments Results Example -================== ================= ================================================= ========================================= -:func:`count` [start[, step]] start, start+step, start+2*step, ... ``count(10) → 10 11 12 13 14 ...`` -:func:`cycle` p p0, p1, ... plast, p0, p1, ... ``cycle('ABCD') → A B C D A B C D ...`` -:func:`repeat` elem [,n] elem, elem, elem, ... endlessly or up to n times ``repeat(10, 3) → 10 10 10`` -================== ================= ================================================= ========================================= - -**Iterators terminating on the shortest input sequence:** +**General iterators:** ============================ ============================ ================================================= ============================================================= Iterator Arguments Results Example ============================ ============================ ================================================= ============================================================= :func:`accumulate` p [,func] p0, p0+p1, p0+p1+p2, ... ``accumulate([1,2,3,4,5]) → 1 3 6 10 15`` -:func:`batched` p, n (p0, p1, ..., p_n-1), ... ``batched('ABCDEFG', n=2) → AB CD EF G`` +:func:`batched` p, n (p0, p1, ..., p_n-1), ... ``batched('ABCDEFG', n=3) → ABC DEF G`` :func:`chain` p, q, ... p0, p1, ... plast, q0, q1, ... ``chain('ABC', 'DEF') → A B C D E F`` :func:`chain.from_iterable` iterable p0, p1, ... plast, q0, q1, ... ``chain.from_iterable(['ABC', 'DEF']) → A B C D E F`` :func:`compress` data, selectors (d[0] if s[0]), (d[1] if s[1]), ... ``compress('ABCDEF', [1,0,1,0,1,1]) → A C E F`` +:func:`count` [start[, step]] start, start+step, start+2*step, ... ``count(10) → 10 11 12 13 14 ...`` +:func:`cycle` p p0, p1, ... plast, p0, p1, ... ``cycle('ABCD') → A B C D A B C D ...`` :func:`dropwhile` predicate, seq seq[n], seq[n+1], starting when predicate fails ``dropwhile(lambda x: x<5, [1,4,6,3,8]) → 6 3 8`` :func:`filterfalse` predicate, seq elements of seq where predicate(elem) fails ``filterfalse(lambda x: x<5, [1,4,6,3,8]) → 6 8`` :func:`groupby` iterable[, key] sub-iterators grouped by value of key(v) ``groupby(['A','B','DEF'], len) → (1, A B) (3, DEF)`` :func:`islice` seq, [start,] stop [, step] elements from seq[start:stop:step] ``islice('ABCDEFG', 2, None) → C D E F G`` :func:`pairwise` iterable (p[0], p[1]), (p[1], p[2]) ``pairwise('ABCDEFG') → AB BC CD DE EF FG`` +:func:`repeat` elem [,n] elem, elem, elem, ... endlessly or up to n times ``repeat(10, 3) → 10 10 10`` :func:`starmap` func, seq func(\*seq[0]), func(\*seq[1]), ... ``starmap(pow, [(2,5), (3,2), (10,3)]) → 32 9 1000`` :func:`takewhile` predicate, seq seq[0], seq[1], until predicate fails ``takewhile(lambda x: x<5, [1,4,6,3,8]) → 1 4`` :func:`tee` it, n it1, it2, ... itn splits one iterator into n ``tee('ABC', 2) → A B C, A B C`` @@ -181,7 +170,7 @@ loops that truncate the stream. Roughly equivalent to:: def batched(iterable, n, *, strict=False): - # batched('ABCDEFG', 2) → AB CD EF G + # batched('ABCDEFG', 3) → ABC DEF G if n < 1: raise ValueError('n must be at least one') iterator = iter(iterable) @@ -819,7 +808,7 @@ well as with the built-in itertools such as ``map()``, ``filter()``, A secondary purpose of the recipes is to serve as an incubator. The ``accumulate()``, ``compress()``, and ``pairwise()`` itertools started out as -recipes. Currently, the ``sliding_window()``, ``iter_index()``, and ``sieve()`` +recipes. Currently, the ``sliding_window()``, ``derangements()``, and ``sieve()`` recipes are being tested to see whether they prove their worth. Substantially all of these recipes and many, many others can be installed from @@ -838,11 +827,18 @@ and :term:`generators ` which incur interpreter overhead. .. testcode:: + from itertools import (accumulate, batched, chain, combinations, compress, + count, cycle, filterfalse, groupby, islice, permutations, product, + repeat, starmap, tee, zip_longest) from collections import Counter, deque from contextlib import suppress from functools import reduce - from math import comb, prod, sumprod, isqrt - from operator import itemgetter, getitem, mul, neg + from heapq import heappush, heappushpop, heappush_max, heappushpop_max + from math import comb, isqrt, prod, sumprod + from operator import getitem, is_not, itemgetter, mul, neg, truediv + + + # ==== Basic one liners ==== def take(n, iterable): "Return first n items of the iterable as a list." @@ -853,10 +849,6 @@ and :term:`generators ` which incur interpreter overhead. # prepend(1, [2, 3, 4]) → 1 2 3 4 return chain([value], iterable) - def tabulate(function, start=0): - "Return function(0), function(1), ..." - return map(function, count(start)) - def repeatfunc(function, times=None, *args): "Repeat calls to a function with specified arguments." if times is None: @@ -899,8 +891,8 @@ and :term:`generators ` which incur interpreter overhead. def first_true(iterable, default=False, predicate=None): "Returns the first true value or the *default* if there is no true value." - # first_true([a,b,c], x) → a or b or c or x - # first_true([a,b], x, f) → a if f(a) else b if f(b) else x + # first_true([a, b, c], x) → a or b or c or x + # first_true([a, b], x, f) → a if f(a) else b if f(b) else x return next(filter(predicate, iterable), default) def all_equal(iterable, key=None): @@ -908,6 +900,9 @@ and :term:`generators ` which incur interpreter overhead. # all_equal('4٤௪౪໔', key=int) → True return len(take(2, groupby(iterable, key))) <= 1 + + # ==== Data pipelines ==== + def unique_justseen(iterable, key=None): "Yield unique elements, preserving order. Remember only the element just seen." # unique_justseen('AAAABBBCCDAABBB') → A B C D A B @@ -933,14 +928,14 @@ and :term:`generators ` which incur interpreter overhead. yield element def unique(iterable, key=None, reverse=False): - "Yield unique elements in sorted order. Supports unhashable inputs." - # unique([[1, 2], [3, 4], [1, 2]]) → [1, 2] [3, 4] - sequenced = sorted(iterable, key=key, reverse=reverse) - return unique_justseen(sequenced, key=key) + "Yield unique elements in sorted order. Supports unhashable inputs." + # unique([[1, 2], [3, 4], [1, 2]]) → [1, 2] [3, 4] + sequenced = sorted(iterable, key=key, reverse=reverse) + return unique_justseen(sequenced, key=key) def sliding_window(iterable, n): "Collect data into overlapping fixed-length chunks or blocks." - # sliding_window('ABCDEFG', 4) → ABCD BCDE CDEF DEFG + # sliding_window('ABCDEFG', 3) → ABC BCD CDE DEF EFG iterator = iter(iterable) window = deque(islice(iterator, n - 1), maxlen=n) for x in iterator: @@ -949,7 +944,7 @@ and :term:`generators ` which incur interpreter overhead. def grouper(iterable, n, *, incomplete='fill', fillvalue=None): "Collect data into non-overlapping fixed-length chunks or blocks." - # grouper('ABCDEFG', 3, fillvalue='x') → ABC DEF Gxx + # grouper('ABCDEFG', 3, fillvalue='x') → ABC DEF Gxx # grouper('ABCDEFG', 3, incomplete='strict') → ABC DEF ValueError # grouper('ABCDEFG', 3, incomplete='ignore') → ABC DEF iterators = [iter(iterable)] * n @@ -978,6 +973,16 @@ and :term:`generators ` which incur interpreter overhead. slices = starmap(slice, combinations(range(len(seq) + 1), 2)) return map(getitem, repeat(seq), slices) + def derangements(iterable, r=None): + "Produce r length permutations without fixed points." + # derangements('ABCD') → BADC BCDA BDAC CADB CDAB CDBA DABC DCAB DCBA + # Algorithm credited to Stefan Pochmann + seq = tuple(iterable) + pos = tuple(range(len(seq))) + have_moved = map(map, repeat(is_not), repeat(pos), permutations(pos, r=r)) + valid_derangements = map(all, have_moved) + return compress(permutations(seq, r=r), valid_derangements) + def iter_index(iterable, value, start=0, stop=None): "Return indices where a value occurs in a sequence or iterable." # iter_index('AABCADEAF', 'A') → 0 1 4 7 @@ -1005,9 +1010,7 @@ and :term:`generators ` which incur interpreter overhead. yield function() -The following recipes have a more mathematical flavor: - -.. testcode:: + # ==== Mathematical operations ==== def multinomial(*counts): "Number of distinct arrangements of a multiset." @@ -1026,9 +1029,12 @@ The following recipes have a more mathematical flavor: # sum_of_squares([10, 20, 30]) → 1400 return sumprod(*tee(iterable)) + + # ==== Matrix operations ==== + def reshape(matrix, columns): "Reshape a 2-D matrix to have a given number of columns." - # reshape([(0, 1), (2, 3), (4, 5)], 3) → (0, 1, 2), (3, 4, 5) + # reshape([(0, 1), (2, 3), (4, 5)], 3) → (0, 1, 2) (3, 4, 5) return batched(chain.from_iterable(matrix), columns, strict=True) def transpose(matrix): @@ -1038,10 +1044,13 @@ The following recipes have a more mathematical flavor: def matmul(m1, m2): "Multiply two matrices." - # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) → (49, 80), (41, 60) + # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) → (49, 80) (41, 60) n = len(m2[0]) return batched(starmap(sumprod, product(m1, transpose(m2))), n) + + # ==== Polynomial arithmetic ==== + def convolve(signal, kernel): """Discrete linear convolution of two iterables. Equivalent to polynomial multiplication. @@ -1096,6 +1105,9 @@ The following recipes have a more mathematical flavor: powers = reversed(range(1, n)) return list(map(mul, coefficients, powers)) + + # ==== Number theory ==== + def sieve(n): "Primes less than n." # sieve(30) → 2 3 5 7 11 13 17 19 23 29 @@ -1134,6 +1146,49 @@ The following recipes have a more mathematical flavor: return n + # ==== Running statistics ==== + + def running_mean(iterable): + "Average of values seen so far." + # running_mean([37, 33, 38, 28]) → 37 35 36 34 + return map(truediv, accumulate(iterable), count(1)) + + def running_min(iterable): + "Smallest of values seen so far." + # running_min([37, 33, 38, 28]) → 37 33 33 28 + return accumulate(iterable, func=min) + + def running_max(iterable): + "Largest of values seen so far." + # running_max([37, 33, 38, 28]) → 37 37 38 38 + return accumulate(iterable, func=max) + + def running_median(iterable): + "Median of values seen so far." + # running_median([37, 33, 38, 28]) → 37 35 37 35 + read = iter(iterable).__next__ + lo = [] # max-heap + hi = [] # min-heap the same size as or one smaller than lo + with suppress(StopIteration): + while True: + heappush_max(lo, heappushpop(hi, read())) + yield lo[0] + heappush(hi, heappushpop_max(lo, read())) + yield (lo[0] + hi[0]) / 2 + + def running_statistics(iterable): + "Aggregate statistics for values seen so far." + # Generate tuples: (size, minimum, median, maximum, mean) + t0, t1, t2, t3 = tee(iterable, 4) + return zip( + count(1), + running_min(t0), + running_median(t1), + running_max(t2), + running_mean(t3), + ) + + .. doctest:: :hide: @@ -1210,10 +1265,6 @@ The following recipes have a more mathematical flavor: [(0, 'a'), (1, 'b'), (2, 'c')] - >>> list(islice(tabulate(lambda x: 2*x), 4)) - [0, 2, 4, 6] - - >>> for _ in loops(5): ... print('hi') ... @@ -1663,6 +1714,36 @@ The following recipes have a more mathematical flavor: ['A', 'AB', 'ABC', 'ABCD', 'B', 'BC', 'BCD', 'C', 'CD', 'D'] + >>> ' '.join(map(''.join, derangements('ABCD'))) + 'BADC BCDA BDAC CADB CDAB CDBA DABC DCAB DCBA' + >>> ' '.join(map(''.join, derangements('ABCD', 3))) + 'BAD BCA BCD BDA CAB CAD CDA CDB DAB DCA DCB' + >>> ' '.join(map(''.join, derangements('ABCD', 2))) + 'BA BC BD CA CD DA DC' + >>> ' '.join(map(''.join, derangements('ABCD', 1))) + 'B C D' + >>> ' '.join(map(''.join, derangements('ABCD', 0))) + '' + >>> # Compare number of derangements to https://oeis.org/A000166 + >>> [len(list(derangements(range(n)))) for n in range(10)] + [1, 0, 1, 2, 9, 44, 265, 1854, 14833, 133496] + >>> # Verify that identical objects are treated as unique by position + >>> identical = 'X' + >>> distinct = 'x' + >>> seq1 = ('A', identical, 'B', identical) + >>> result1 = ' '.join(map(''.join, derangements(seq1))) + >>> result1 + 'XAXB XBXA XXAB BAXX BXAX BXXA XAXB XBAX XBXA' + >>> seq2 = ('A', identical, 'B', distinct) + >>> result2 = ' '.join(map(''.join, derangements(seq2))) + >>> result2 + 'XAxB XBxA XxAB BAxX BxAX BxXA xAXB xBAX xBXA' + >>> result1 == result2 + False + >>> result1.casefold() == result2.casefold() + True + + >>> list(powerset([1,2,3])) [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] >>> all(len(list(powerset(range(n)))) == 2**n for n in range(18)) @@ -1743,11 +1824,37 @@ The following recipes have a more mathematical flavor: True + >>> list(running_mean([8.5, 9.5, 7.5, 6.5])) + [8.5, 9.0, 8.5, 8.0] + >>> list(running_mean([37, 33, 38, 28])) + [37.0, 35.0, 36.0, 34.0] + + + >>> list(running_min([37, 33, 38, 28])) + [37, 33, 33, 28] + + + >>> list(running_max([37, 33, 38, 28])) + [37, 37, 38, 38] + + + >>> list(running_median([37, 33, 38, 28])) + [37, 35.0, 37, 35.0] + + + >>> list(running_statistics([37, 33, 38, 28])) + [(1, 37, 37, 37, 37.0), (2, 33, 35.0, 37, 35.0), (3, 33, 37, 38, 36.0), (4, 28, 35.0, 38, 34.0)] + + .. testcode:: :hide: # Old recipes and their tests which are guaranteed to continue to work. + def tabulate(function, start=0): + "Return function(0), function(1), ..." + return map(function, count(start)) + def old_sumprod_recipe(vec1, vec2): "Compute a sum of products." return sum(starmap(operator.mul, zip(vec1, vec2, strict=True))) @@ -1827,6 +1934,10 @@ The following recipes have a more mathematical flavor: .. doctest:: :hide: + >>> list(islice(tabulate(lambda x: 2*x), 4)) + [0, 2, 4, 6] + + >>> dotproduct([1,2,3], [4,5,6]) 32 diff --git a/Doc/library/json.rst b/Doc/library/json.rst index 12a5a96a3c56f3..383ccad9df041b 100644 --- a/Doc/library/json.rst +++ b/Doc/library/json.rst @@ -4,9 +4,6 @@ .. module:: json :synopsis: Encode and decode the JSON format. -.. moduleauthor:: Bob Ippolito -.. sectionauthor:: Bob Ippolito - **Source code:** :source:`Lib/json/__init__.py` -------------- @@ -121,7 +118,7 @@ Extending :class:`JSONEncoder`:: ['[2.0', ', 1.0', ']'] -Using :mod:`json` from the shell to validate and pretty-print: +Using :mod:`!json` from the shell to validate and pretty-print: .. code-block:: shell-session @@ -183,8 +180,10 @@ Basic Usage :param bool ensure_ascii: If ``True`` (the default), the output is guaranteed to - have all incoming non-ASCII characters escaped. - If ``False``, these characters will be outputted as-is. + have all incoming non-ASCII and non-printable characters escaped. + If ``False``, all characters will be outputted as-is, except for + the characters that must be escaped: quotation mark, reverse solidus, + and the control characters U+0000 through U+001F. :param bool check_circular: If ``False``, the circular reference check for container types is skipped @@ -212,7 +211,7 @@ Basic Usage a string (such as ``"\t"``) is used to indent each level. If zero, negative, or ``""`` (the empty string), only newlines are inserted. - If ``None`` (the default), the most compact representation is used. + If ``None`` (the default), no newlines are inserted. :type indent: int | str | None :param separators: @@ -265,7 +264,7 @@ Basic Usage .. function:: load(fp, *, cls=None, object_hook=None, parse_float=None, \ parse_int=None, parse_constant=None, \ - object_pairs_hook=None, **kw) + object_pairs_hook=None, array_hook=None, **kw) Deserialize *fp* to a Python object using the :ref:`JSON-to-Python conversion table `. @@ -302,6 +301,15 @@ Basic Usage Default ``None``. :type object_pairs_hook: :term:`callable` | None + :param array_hook: + If set, a function that is called with the result of + any JSON array literal decoded with as a Python list. + The return value of this function will be used + instead of the :class:`list`. + This feature can be used to implement custom decoders. + Default ``None``. + :type array_hook: :term:`callable` | None + :param parse_float: If set, a function that is called with the string of every JSON float to be decoded. @@ -350,7 +358,10 @@ Basic Usage conversion length limitation ` to help avoid denial of service attacks. -.. function:: loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) + .. versionchanged:: 3.15 + Added the optional *array_hook* parameter. + +.. function:: loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, array_hook=None, **kw) Identical to :func:`load`, but instead of a file-like object, deserialize *s* (a :class:`str`, :class:`bytes` or :class:`bytearray` @@ -368,7 +379,7 @@ Basic Usage Encoders and Decoders --------------------- -.. class:: JSONDecoder(*, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None) +.. class:: JSONDecoder(*, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None, array_hook=None) Simple JSON decoder. @@ -413,6 +424,14 @@ Encoders and Decoders .. versionchanged:: 3.1 Added support for *object_pairs_hook*. + *array_hook* is an optional function that will be called with the + result of every JSON array decoded as a list. The return value of + *array_hook* will be used instead of the :class:`list`. This feature can be + used to implement custom decoders. + + .. versionchanged:: 3.15 + Added support for *array_hook*. + *parse_float* is an optional function that will be called with the string of every JSON float to be decoded. By default, this is equivalent to ``float(num_str)``. This can be used to use another datatype or parser for @@ -495,8 +514,10 @@ Encoders and Decoders :class:`bool` or ``None``. If *skipkeys* is true, such items are simply skipped. If *ensure_ascii* is true (the default), the output is guaranteed to - have all incoming non-ASCII characters escaped. If *ensure_ascii* is - false, these characters will be output as-is. + have all incoming non-ASCII and non-printable characters escaped. + If *ensure_ascii* is false, all characters will be output as-is, except for + the characters that must be escaped: quotation mark, reverse solidus, + and the control characters U+0000 through U+001F. If *check_circular* is true (the default), then lists, dicts, and custom encoded objects will be checked for circular references during encoding to @@ -636,7 +657,7 @@ UTF-32, with UTF-8 being the recommended default for maximum interoperability. As permitted, though not required, by the RFC, this module's serializer sets *ensure_ascii=True* by default, thus escaping the output so that the resulting -strings only contain ASCII characters. +strings only contain printable ASCII characters. Other than the *ensure_ascii* parameter, this module is defined strictly in terms of conversion between Python objects and @@ -743,8 +764,8 @@ Command-line interface -------------- -The :mod:`json` module can be invoked as a script via ``python -m json`` -to validate and pretty-print JSON objects. The :mod:`json.tool` submodule +The :mod:`!json` module can be invoked as a script via ``python -m json`` +to validate and pretty-print JSON objects. The :mod:`!json.tool` submodule implements this interface. If the optional ``infile`` and ``outfile`` arguments are not @@ -765,7 +786,7 @@ specified, :data:`sys.stdin` and :data:`sys.stdout` will be used respectively: alphabetically by key. .. versionchanged:: 3.14 - The :mod:`json` module may now be directly executed as + The :mod:`!json` module may now be directly executed as ``python -m json``. For backwards compatibility, invoking the CLI as ``python -m json.tool`` remains supported. diff --git a/Doc/library/linecache.rst b/Doc/library/linecache.rst index e766a9280946d3..ff07499e58f0f6 100644 --- a/Doc/library/linecache.rst +++ b/Doc/library/linecache.rst @@ -4,13 +4,11 @@ .. module:: linecache :synopsis: Provides random access to individual lines from text files. -.. sectionauthor:: Moshe Zadka - **Source code:** :source:`Lib/linecache.py` -------------- -The :mod:`linecache` module allows one to get any line from a Python source file, while +The :mod:`!linecache` module allows one to get any line from a Python source file, while attempting to optimize internally, using a cache, the common case where many lines are read from a single file. This is used by the :mod:`traceback` module to retrieve source lines for inclusion in the formatted traceback. @@ -19,7 +17,7 @@ The :func:`tokenize.open` function is used to open files. This function uses :func:`tokenize.detect_encoding` to get the encoding of the file; in the absence of an encoding token, the file encoding defaults to UTF-8. -The :mod:`linecache` module defines the following functions: +The :mod:`!linecache` module defines the following functions: .. function:: getline(filename, lineno, module_globals=None) @@ -31,7 +29,7 @@ The :mod:`linecache` module defines the following functions: .. index:: triple: module; search; path If *filename* indicates a frozen module (starting with ``' -.. sectionauthor:: Martin von Löwis - **Source code:** :source:`Lib/locale.py` -------------- -The :mod:`locale` module opens access to the POSIX locale database and +The :mod:`!locale` module opens access to the POSIX locale database and functionality. The POSIX locale mechanism allows programmers to deal with certain cultural issues in an application, without requiring the programmer to know all the specifics of each country where the software is executed. .. index:: pair: module; _locale -The :mod:`locale` module is implemented on top of the :mod:`!_locale` module, +The :mod:`!locale` module is implemented on top of the :mod:`!_locale` module, which in turn uses an ANSI C locale implementation if available. -The :mod:`locale` module defines the following exception and functions: +The :mod:`!locale` module defines the following exception and functions: .. exception:: Error @@ -34,17 +31,33 @@ The :mod:`locale` module defines the following exception and functions: If *locale* is given and not ``None``, :func:`setlocale` modifies the locale setting for the *category*. The available categories are listed in the data - description below. *locale* may be a string, or an iterable of two strings - (language code and encoding). If it's an iterable, it's converted to a locale - name using the locale aliasing engine. An empty string specifies the user's + description below. *locale* may be a :ref:`string `, or a pair, + language code and encoding. An empty string specifies the user's default settings. If the modification of the locale fails, the exception :exc:`Error` is raised. If successful, the new locale setting is returned. + If *locale* is a pair, it is converted to a locale name using + the locale aliasing engine. + The language code has the same format as a :ref:`locale name `, + but without encoding. + The language code and encoding can be ``None``. + If *locale* is omitted or ``None``, the current setting for *category* is returned. + Example:: + + >>> import locale + >>> loc = locale.setlocale(locale.LC_ALL) # get current locale + # use German locale; name and availability varies with platform + >>> locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8') + >>> locale.strcoll('f\xe4n', 'foo') # compare a string containing an umlaut + >>> locale.setlocale(locale.LC_ALL, '') # use user's preferred locale + >>> locale.setlocale(locale.LC_ALL, 'C') # use default (C) locale + >>> locale.setlocale(locale.LC_ALL, loc) # restore saved locale + :func:`setlocale` is not thread-safe on most systems. Applications typically - start with a call of :: + start with a call of:: import locale locale.setlocale(locale.LC_ALL, '') @@ -53,6 +66,9 @@ The :mod:`locale` module defines the following exception and functions: specified in the :envvar:`LANG` environment variable). If the locale is not changed thereafter, using multithreading should not cause problems. + .. versionchanged:: 3.15 + Support language codes with ``@``-modifiers. + .. function:: localeconv() @@ -345,22 +361,28 @@ The :mod:`locale` module defines the following exception and functions: ``'LANG'``. The GNU gettext search path contains ``'LC_ALL'``, ``'LC_CTYPE'``, ``'LANG'`` and ``'LANGUAGE'``, in that order. - Except for the code ``'C'``, the language code corresponds to :rfc:`1766`. - *language code* and *encoding* may be ``None`` if their values cannot be + The language code has the same format as a :ref:`locale name `, + but without encoding and ``@``-modifier. + The language code and encoding may be ``None`` if their values cannot be determined. - - .. deprecated-removed:: 3.11 3.15 + The "C" locale is represented as ``(None, None)``. .. function:: getlocale(category=LC_CTYPE) - Returns the current setting for the given locale category as sequence containing - *language code*, *encoding*. *category* may be one of the :const:`!LC_\*` values - except :const:`LC_ALL`. It defaults to :const:`LC_CTYPE`. + Returns the current setting for the given locale category as a tuple containing + the language code and encoding. *category* may be one of the :const:`!LC_\*` + values except :const:`LC_ALL`. It defaults to :const:`LC_CTYPE`. - Except for the code ``'C'``, the language code corresponds to :rfc:`1766`. - *language code* and *encoding* may be ``None`` if their values cannot be + The language code has the same format as a :ref:`locale name `, + but without encoding. + The language code and encoding may be ``None`` if their values cannot be determined. + The "C" locale is represented as ``(None, None)``. + + .. versionchanged:: 3.15 + ``@``-modifier are no longer silently removed, but included in + the language code. .. function:: getpreferredencoding(do_setlocale=True) @@ -508,14 +530,14 @@ The :mod:`locale` module defines the following exception and functions: SSH connections. Python doesn't internally use locale-dependent character transformation functions - from ``ctype.h``. Instead, an internal ``pyctype.h`` provides locale-independent - equivalents like :c:macro:`!Py_TOLOWER`. + from ``ctype.h``. Instead, ``pyctype.h`` provides locale-independent + equivalents like :c:macro:`Py_TOLOWER`. .. data:: LC_COLLATE Locale category for sorting strings. The functions :func:`strcoll` and - :func:`strxfrm` of the :mod:`locale` module are affected. + :func:`strxfrm` of the :mod:`!locale` module are affected. .. data:: LC_TIME @@ -544,7 +566,7 @@ The :mod:`locale` module defines the following exception and functions: .. data:: LC_NUMERIC Locale category for formatting numbers. The functions :func:`format_string`, - :func:`atoi`, :func:`atof` and :func:`.str` of the :mod:`locale` module are + :func:`atoi`, :func:`atof` and :func:`.str` of the :mod:`!locale` module are affected by that category. All other numeric formatting operations are not affected. @@ -564,18 +586,6 @@ The :mod:`locale` module defines the following exception and functions: :func:`localeconv`. -Example:: - - >>> import locale - >>> loc = locale.getlocale() # get current locale - # use German locale; name might vary with platform - >>> locale.setlocale(locale.LC_ALL, 'de_DE') - >>> locale.strcoll('f\xe4n', 'foo') # compare a string containing an umlaut - >>> locale.setlocale(locale.LC_ALL, '') # use user's preferred locale - >>> locale.setlocale(locale.LC_ALL, 'C') # use default (C) locale - >>> locale.setlocale(locale.LC_ALL, loc) # restore saved locale - - Background, details, hints, tips and caveats -------------------------------------------- @@ -615,6 +625,61 @@ whose high bit is set (i.e., non-ASCII bytes) are never converted or considered part of a character class such as letter or whitespace. +.. _locale_name: + +Locale names +------------ + +The format of the locale name is platform dependent, and the set of supported +locales can depend on the system configuration. + +On Posix platforms, it usually has the format [1]_: + +.. productionlist:: locale_name + : language ["_" territory] ["." charset] ["@" modifier] + +where *language* is a two- or three-letter language code from `ISO 639`_, +*territory* is a two-letter country or region code from `ISO 3166`_, +*charset* is a locale encoding, and *modifier* is a script name, +a language subtag, a sort order identifier, or other locale modifier +(for example, "latin", "valencia", "stroke" and "euro"). + +On Windows, several formats are supported. [2]_ [3]_ +A subset of `IETF BCP 47`_ tags: + +.. productionlist:: locale_name + : language ["-" script] ["-" territory] ["." charset] + : language ["-" script] "-" territory "-" modifier + +where *language* and *territory* have the same meaning as in Posix, +*script* is a four-letter script code from `ISO 15924`_, +and *modifier* is a language subtag, a sort order identifier +or custom modifier (for example, "valencia", "stroke" or "x-python"). +Both hyphen (``'-'``) and underscore (``'_'``) separators are supported. +Only UTF-8 encoding is allowed for BCP 47 tags. + +Windows also supports locale names in the format: + +.. productionlist:: locale_name + : language ["_" territory] ["." charset] + +where *language* and *territory* are full names, such as "English" and +"United States", and *charset* is either a code page number (for example, "1252") +or UTF-8. +Only the underscore separator is supported in this format. + +The "C" locale is supported on all platforms. + +.. _ISO 639: https://www.iso.org/iso-639-language-code +.. _ISO 3166: https://www.iso.org/iso-3166-country-codes.html +.. _IETF BCP 47: https://www.rfc-editor.org/info/bcp47 +.. _ISO 15924: https://www.unicode.org/iso15924/ + +.. [1] `IEEE Std 1003.1-2024; 8.2 Internationalization Variables `_ +.. [2] `UCRT Locale names, Languages, and Country/Region strings `_ +.. [3] `Locale Names `_ + + .. _embedding-locale: For extension writers and programs that embed Python @@ -625,7 +690,7 @@ the current locale is. But since the return value can only be used portably to restore it, that is not very useful (except perhaps to find out whether or not the locale is ``C``). -When Python code uses the :mod:`locale` module to change the locale, this also +When Python code uses the :mod:`!locale` module to change the locale, this also affects the embedding application. If the embedding application doesn't want this to happen, it should remove the :mod:`!_locale` extension module (which does all the work) from the table of built-in modules in the :file:`config.c` file, diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 96cca3073fec7e..30bf7860a75119 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -4,9 +4,6 @@ .. module:: logging.config :synopsis: Configuration of the logging module. -.. moduleauthor:: Vinay Sajip -.. sectionauthor:: Vinay Sajip - **Source code:** :source:`Lib/logging/config.py` .. sidebar:: Important @@ -28,7 +25,7 @@ Configuration functions ^^^^^^^^^^^^^^^^^^^^^^^ The following functions configure the logging module. They are located in the -:mod:`logging.config` module. Their use is optional --- you can configure the +:mod:`!logging.config` module. Their use is optional --- you can configure the logging module using these functions or by making calls to the main API (defined in :mod:`logging` itself) and defining handlers which are declared either in :mod:`logging` or :mod:`logging.handlers`. @@ -55,7 +52,7 @@ in :mod:`logging` itself) and defining handlers which are declared either in Parsing is performed by the :class:`DictConfigurator` class, whose constructor is passed the dictionary used for configuration, and - has a :meth:`configure` method. The :mod:`logging.config` module + has a :meth:`configure` method. The :mod:`!logging.config` module has a callable attribute :attr:`dictConfigClass` which is initially set to :class:`DictConfigurator`. You can replace the value of :attr:`dictConfigClass` with a diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index d74ef73ee28497..5152c7561fa1f2 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -4,9 +4,6 @@ .. module:: logging.handlers :synopsis: Handlers for the logging module. -.. moduleauthor:: Vinay Sajip -.. sectionauthor:: Vinay Sajip - **Source code:** :source:`Lib/logging/handlers.py` .. sidebar:: Important @@ -160,7 +157,7 @@ WatchedFileHandler .. currentmodule:: logging.handlers -The :class:`WatchedFileHandler` class, located in the :mod:`logging.handlers` +The :class:`WatchedFileHandler` class, located in the :mod:`!logging.handlers` module, is a :class:`FileHandler` which watches the file it is logging to. If the file changes, it is closed and reopened using the file name. @@ -213,7 +210,7 @@ for this value. BaseRotatingHandler ^^^^^^^^^^^^^^^^^^^ -The :class:`BaseRotatingHandler` class, located in the :mod:`logging.handlers` +The :class:`BaseRotatingHandler` class, located in the :mod:`!logging.handlers` module, is the base class for the rotating file handlers, :class:`RotatingFileHandler` and :class:`TimedRotatingFileHandler`. You should not need to instantiate this class, but it has attributes and methods you may @@ -307,7 +304,7 @@ For an example, see :ref:`cookbook-rotator-namer`. RotatingFileHandler ^^^^^^^^^^^^^^^^^^^ -The :class:`RotatingFileHandler` class, located in the :mod:`logging.handlers` +The :class:`RotatingFileHandler` class, located in the :mod:`!logging.handlers` module, supports rotation of disk log files. @@ -362,7 +359,7 @@ TimedRotatingFileHandler ^^^^^^^^^^^^^^^^^^^^^^^^ The :class:`TimedRotatingFileHandler` class, located in the -:mod:`logging.handlers` module, supports rotation of disk log files at certain +:mod:`!logging.handlers` module, supports rotation of disk log files at certain timed intervals. @@ -405,7 +402,8 @@ timed intervals. rollover interval. When computing the next rollover time for the first time (when the handler - is created), the last modification time of an existing log file, or else + is created), the creation time (if supported by the OS and file system) + or the last modification of an existing log file, or else the current time, is used to compute when the next rotation will occur. If the *utc* argument is true, times in UTC will be used; otherwise @@ -452,6 +450,10 @@ timed intervals. .. versionchanged:: 3.9 The *errors* parameter was added. + .. versionchanged:: next + Use the creation time instead of the last modification time, if supported by the OS and file system. + + .. method:: doRollover() Does a rollover, as described above. @@ -463,6 +465,7 @@ timed intervals. .. method:: getFilesToDelete() Returns a list of filenames which should be deleted as part of rollover. These + are the absolute paths of the oldest backup log files written by the handler. .. method:: shouldRollover(record) @@ -474,7 +477,7 @@ timed intervals. SocketHandler ^^^^^^^^^^^^^ -The :class:`SocketHandler` class, located in the :mod:`logging.handlers` module, +The :class:`SocketHandler` class, located in the :mod:`!logging.handlers` module, sends logging output to a network socket. The base class uses a TCP socket. @@ -570,7 +573,7 @@ sends logging output to a network socket. The base class uses a TCP socket. DatagramHandler ^^^^^^^^^^^^^^^ -The :class:`DatagramHandler` class, located in the :mod:`logging.handlers` +The :class:`DatagramHandler` class, located in the :mod:`!logging.handlers` module, inherits from :class:`SocketHandler` to support sending logging messages over UDP sockets. @@ -617,7 +620,7 @@ over UDP sockets. SysLogHandler ^^^^^^^^^^^^^ -The :class:`SysLogHandler` class, located in the :mod:`logging.handlers` module, +The :class:`SysLogHandler` class, located in the :mod:`!logging.handlers` module, supports sending logging messages to a remote or local Unix syslog. @@ -796,7 +799,7 @@ supports sending logging messages to a remote or local Unix syslog. NTEventLogHandler ^^^^^^^^^^^^^^^^^ -The :class:`NTEventLogHandler` class, located in the :mod:`logging.handlers` +The :class:`NTEventLogHandler` class, located in the :mod:`!logging.handlers` module, supports sending logging messages to a local Windows NT, Windows 2000 or Windows XP event log. Before you can use it, you need Mark Hammond's Win32 extensions for Python installed. @@ -863,7 +866,7 @@ extensions for Python installed. SMTPHandler ^^^^^^^^^^^ -The :class:`SMTPHandler` class, located in the :mod:`logging.handlers` module, +The :class:`SMTPHandler` class, located in the :mod:`!logging.handlers` module, supports sending logging messages to an email address via SMTP. @@ -904,7 +907,7 @@ supports sending logging messages to an email address via SMTP. MemoryHandler ^^^^^^^^^^^^^ -The :class:`MemoryHandler` class, located in the :mod:`logging.handlers` module, +The :class:`MemoryHandler` class, located in the :mod:`!logging.handlers` module, supports buffering of logging records in memory, periodically flushing them to a :dfn:`target` handler. Flushing occurs whenever the buffer is full, or when an event of a certain severity or greater is seen. @@ -984,7 +987,7 @@ should, then :meth:`flush` is expected to do the flushing. HTTPHandler ^^^^^^^^^^^ -The :class:`HTTPHandler` class, located in the :mod:`logging.handlers` module, +The :class:`HTTPHandler` class, located in the :mod:`!logging.handlers` module, supports sending logging messages to a web server, using either ``GET`` or ``POST`` semantics. @@ -1036,7 +1039,7 @@ QueueHandler .. versionadded:: 3.2 -The :class:`QueueHandler` class, located in the :mod:`logging.handlers` module, +The :class:`QueueHandler` class, located in the :mod:`!logging.handlers` module, supports sending logging messages to a queue, such as those implemented in the :mod:`queue` or :mod:`multiprocessing` modules. @@ -1129,7 +1132,7 @@ QueueListener .. versionadded:: 3.2 -The :class:`QueueListener` class, located in the :mod:`logging.handlers` +The :class:`QueueListener` class, located in the :mod:`!logging.handlers` module, supports receiving logging messages from a queue, such as those implemented in the :mod:`queue` or :mod:`multiprocessing` modules. The messages are received from a queue in an internal thread and passed, on diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index e37683e32d01f4..aba530844d7177 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -4,9 +4,6 @@ .. module:: logging :synopsis: Flexible event logging system for applications. -.. moduleauthor:: Vinay Sajip -.. sectionauthor:: Vinay Sajip - **Source code:** :source:`Lib/logging/__init__.py` .. index:: pair: Errors; logging @@ -671,8 +668,7 @@ Formatter Objects which is just the logged message. :type fmt: str - :param datefmt: A format string in the given *style* for - the date/time portion of the logged output. + :param datefmt: A format string for the date/time portion of the logged output. If not specified, the default described in :meth:`formatTime` is used. :type datefmt: str @@ -680,7 +676,7 @@ Formatter Objects how the format string will be merged with its data: using one of :ref:`old-string-formatting` (``%``), :meth:`str.format` (``{``) or :class:`string.Template` (``$``). This only applies to - *fmt* and *datefmt* (e.g. ``'%(message)s'`` versus ``'{message}'``), + *fmt* (e.g. ``'%(message)s'`` versus ``'{message}'``), not to the actual log messages passed to the logging methods. However, there are :ref:`other ways ` to use ``{``- and ``$``-formatting for log messages. @@ -1012,6 +1008,11 @@ the options available to you. | exc_info | You shouldn't need to | Exception tuple (à la ``sys.exc_info``) or, | | | format this yourself. | if no exception has occurred, ``None``. | +----------------+-------------------------+-----------------------------------------------+ +| exc_text | You shouldn't need to | Exception information formatted as a string. | +| | format this yourself. | This is set when :meth:`Formatter.format` is | +| | | invoked, or ``None`` if no exception has | +| | | occurred. | ++----------------+-------------------------+-----------------------------------------------+ | filename | ``%(filename)s`` | Filename portion of ``pathname``. | +----------------+-------------------------+-----------------------------------------------+ | funcName | ``%(funcName)s`` | Name of function containing the logging call. | @@ -1083,12 +1084,13 @@ LoggerAdapter Objects information into logging calls. For a usage example, see the section on :ref:`adding contextual information to your logging output `. -.. class:: LoggerAdapter(logger, extra, merge_extra=False) +.. class:: LoggerAdapter(logger, extra=None, merge_extra=False) Returns an instance of :class:`LoggerAdapter` initialized with an - underlying :class:`Logger` instance, a dict-like object (*extra*), and a - boolean (*merge_extra*) indicating whether or not the *extra* argument of - individual log calls should be merged with the :class:`LoggerAdapter` extra. + underlying :class:`Logger` instance, an optional dict-like object (*extra*), + and an optional boolean (*merge_extra*) indicating whether or not + the *extra* argument of individual log calls should be merged with + the :class:`LoggerAdapter` extra. The default behavior is to ignore the *extra* argument of individual log calls and only use the one of the :class:`LoggerAdapter` instance @@ -1128,16 +1130,20 @@ information into logging calls. For a usage example, see the section on Attribute :attr:`!manager` and method :meth:`!_log` were added, which delegate to the underlying logger and allow adapters to be nested. + .. versionchanged:: 3.10 + + The *extra* argument is now optional. + .. versionchanged:: 3.13 - The *merge_extra* argument was added. + The *merge_extra* parameter was added. Thread Safety ------------- The logging module is intended to be thread-safe without any special work -needing to be done by its clients. It achieves this though using threading +needing to be done by its clients. It achieves this through using threading locks; there is one lock to serialize access to the module's shared data, and each handler also creates a lock to serialize access to its underlying I/O. @@ -1542,7 +1548,7 @@ Module-Level Attributes Integration with the warnings module ------------------------------------ -The :func:`captureWarnings` function can be used to integrate :mod:`logging` +The :func:`captureWarnings` function can be used to integrate :mod:`!logging` with the :mod:`warnings` module. .. function:: captureWarnings(capture) @@ -1573,7 +1579,7 @@ with the :mod:`warnings` module. library. `Original Python logging package `_ - This is the original source for the :mod:`logging` package. The version of the + This is the original source for the :mod:`!logging` package. The version of the package available from this site is suitable for use with Python 1.5.2, 2.1.x - and 2.2.x, which do not include the :mod:`logging` package in the standard + and 2.2.x, which do not include the :mod:`!logging` package in the standard library. diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst index 69f7cb8d48d7ae..cd72174d54f6e6 100644 --- a/Doc/library/lzma.rst +++ b/Doc/library/lzma.rst @@ -4,9 +4,6 @@ .. module:: lzma :synopsis: A Python wrapper for the liblzma compression library. -.. moduleauthor:: Nadeem Vawda -.. sectionauthor:: Nadeem Vawda - .. versionadded:: 3.3 **Source code:** :source:`Lib/lzma.py` @@ -23,6 +20,8 @@ module. Note that :class:`LZMAFile` and :class:`bz2.BZ2File` are *not* thread-safe, so if you need to use a single :class:`LZMAFile` instance from multiple threads, it is necessary to protect it with a lock. +.. include:: ../includes/optional-module.rst + .. exception:: LZMAError @@ -357,12 +356,26 @@ options. Valid filter IDs are as follows: * Branch-Call-Jump (BCJ) filters: - * :const:`FILTER_X86` - * :const:`FILTER_IA64` - * :const:`FILTER_ARM` - * :const:`FILTER_ARMTHUMB` - * :const:`FILTER_POWERPC` - * :const:`FILTER_SPARC` + * :const:`!FILTER_X86` + * :const:`!FILTER_IA64` + * :const:`!FILTER_ARM` + * :const:`!FILTER_ARMTHUMB` + * :const:`!FILTER_POWERPC` + * :const:`!FILTER_SPARC` + + The above work on all lzma runtime library versions. + + * :const:`!FILTER_ARM64` + + Only works if the lzma version is 5.4.0 or later. + + .. versionadded:: next + + * :const:`!FILTER_RISCV` + + Only works if the lzma version is 5.6.0 or later. + + .. versionadded:: next A filter chain can consist of up to 4 filters, and cannot be empty. The last filter in the chain must be a compression filter, and any other filters must be diff --git a/Doc/library/mailbox.rst b/Doc/library/mailbox.rst index e8a96f29ea185e..5b9741bdbcad19 100644 --- a/Doc/library/mailbox.rst +++ b/Doc/library/mailbox.rst @@ -4,9 +4,6 @@ .. module:: mailbox :synopsis: Manipulate mailboxes in various formats -.. moduleauthor:: Gregory K. Johnson -.. sectionauthor:: Gregory K. Johnson - **Source code:** :source:`Lib/mailbox.py` -------------- @@ -78,6 +75,14 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. message. Failing to lock the mailbox runs the risk of losing messages or corrupting the entire mailbox. + The :class:`!Mailbox` class supports the :keyword:`with` statement. When used + as a context manager, :class:`!Mailbox` calls :meth:`lock` when the context is entered, + returns the mailbox object as the context object, and at context end calls :meth:`close`, + thereby releasing the lock. + + .. versionchanged:: 3.15 + Support for the :keyword:`with` statement was added. + :class:`!Mailbox` instances have the following methods: @@ -917,7 +922,7 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. copied; furthermore, any format-specific information is converted insofar as possible if *message* is a :class:`!Message` instance. If *message* is a string, a byte string, - or a file, it should contain an :rfc:`2822`\ -compliant message, which is read + or a file, it should contain an :rfc:`5322`\ -compliant message, which is read and parsed. Files should be open in binary mode, but text mode files are accepted for backward compatibility. @@ -1025,7 +1030,7 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF. .. method:: remove_flag(flag) Unset the flag(s) specified by *flag* without changing other flags. To - remove more than one flag at a time, *flag* maybe a string of more than + remove more than one flag at a time, *flag* may be a string of more than one character. If "info" contains experimental information rather than flags, the current "info" is not modified. @@ -1190,7 +1195,7 @@ When a :class:`!MaildirMessage` instance is created based upon a .. method:: remove_flag(flag) Unset the flag(s) specified by *flag* without changing other flags. To - remove more than one flag at a time, *flag* maybe a string of more than + remove more than one flag at a time, *flag* may be a string of more than one character. When an :class:`!mboxMessage` instance is created based upon a @@ -1562,7 +1567,7 @@ When a :class:`!BabylMessage` instance is created based upon an .. method:: remove_flag(flag) Unset the flag(s) specified by *flag* without changing other flags. To - remove more than one flag at a time, *flag* maybe a string of more than + remove more than one flag at a time, *flag* may be a string of more than one character. When an :class:`!MMDFMessage` instance is created based upon a @@ -1641,7 +1646,7 @@ The following exception classes are defined in the :mod:`!mailbox` module: .. exception:: Error() - The based class for all other module-specific exceptions. + The base class for all other module-specific exceptions. .. exception:: NoSuchMailboxError() @@ -1661,7 +1666,7 @@ The following exception classes are defined in the :mod:`!mailbox` module: Raised when some mailbox-related condition beyond the control of the program causes it to be unable to proceed, such as when failing to acquire a lock that - another program already holds a lock, or when a uniquely generated file name + another program already holds, or when a uniquely generated file name already exists. diff --git a/Doc/library/marshal.rst b/Doc/library/marshal.rst index e8e9071a5c9ef4..4fe34f0a3a3f91 100644 --- a/Doc/library/marshal.rst +++ b/Doc/library/marshal.rst @@ -20,7 +20,7 @@ rarely does). [#]_ This is not a general "persistence" module. For general persistence and transfer of Python objects through RPC calls, see the modules :mod:`pickle` and -:mod:`shelve`. The :mod:`marshal` module exists mainly to support reading and +:mod:`shelve`. The :mod:`!marshal` module exists mainly to support reading and writing the "pseudo-compiled" code for Python modules of :file:`.pyc` files. Therefore, the Python maintainers reserve the right to modify the marshal format in backward incompatible ways should the need arise. @@ -34,7 +34,7 @@ supports a substantially wider range of objects than marshal. .. warning:: - The :mod:`marshal` module is not intended to be secure against erroneous or + The :mod:`!marshal` module is not intended to be secure against erroneous or maliciously constructed data. Never unmarshal data received from an untrusted or unauthenticated source. @@ -51,8 +51,9 @@ this module. The following types are supported: * Strings (:class:`str`) and :class:`bytes`. :term:`Bytes-like objects ` like :class:`bytearray` are marshalled as :class:`!bytes`. -* Containers: :class:`tuple`, :class:`list`, :class:`set`, :class:`frozenset`, - and (since :data:`version` 5), :class:`slice`. +* Containers: :class:`tuple`, :class:`list`, :class:`dict`, :class:`frozendict` + (since :data:`version` 6), :class:`set`, :class:`frozenset`, and + :class:`slice` (since :data:`version` 5). It should be understood that these are supported only if the values contained therein are themselves supported. Recursive containers are supported since :data:`version` 3. @@ -71,6 +72,10 @@ this module. The following types are supported: Added format version 5, which allows marshalling slices. +.. versionchanged:: 3.15 + + Added format version 6, which allows marshalling :class:`frozendict`. + The module defines these functions: @@ -173,6 +178,8 @@ In addition, the following constants are defined: 4 Python 3.4 Efficient representation of short strings ------- --------------- ---------------------------------------------------- 5 Python 3.14 Support for :class:`slice` objects + ------- --------------- ---------------------------------------------------- + 6 Python 3.15 Support for :class:`frozendict` objects ======= =============== ==================================================== diff --git a/Doc/library/math.integer.rst b/Doc/library/math.integer.rst new file mode 100644 index 00000000000000..c3f34cdfd85410 --- /dev/null +++ b/Doc/library/math.integer.rst @@ -0,0 +1,85 @@ +:mod:`!math.integer` --- integer-specific mathematics functions +=============================================================== + +.. module:: math.integer + :synopsis: Integer-specific mathematics functions. + +.. versionadded:: 3.15 + +-------------- + +This module provides access to the mathematical functions defined for integer arguments. +These functions accept integers and objects that implement the +:meth:`~object.__index__` method which is used to convert the object to an integer +number. + +The following functions are provided by this module. All return values are +computed exactly and are integers. + + +.. function:: comb(n, k, /) + + Return the number of ways to choose *k* items from *n* items without repetition + and without order. + + Evaluates to ``n! / (k! * (n - k)!)`` when ``k <= n`` and evaluates + to zero when ``k > n``. + + Also called the binomial coefficient because it is equivalent + to the coefficient of k-th term in polynomial expansion of + ``(1 + x)ⁿ``. + + Raises :exc:`ValueError` if either of the arguments are negative. + + +.. function:: factorial(n, /) + + Return factorial of the nonnegative integer *n*. + + +.. function:: gcd(*integers) + + Return the greatest common divisor of the specified integer arguments. + If any of the arguments is nonzero, then the returned value is the largest + positive integer that is a divisor of all arguments. If all arguments + are zero, then the returned value is ``0``. ``gcd()`` without arguments + returns ``0``. + + +.. function:: isqrt(n, /) + + Return the integer square root of the nonnegative integer *n*. This is the + floor of the exact square root of *n*, or equivalently the greatest integer + *a* such that *a*\ ² |nbsp| ≤ |nbsp| *n*. + + For some applications, it may be more convenient to have the least integer + *a* such that *n* |nbsp| ≤ |nbsp| *a*\ ², or in other words the ceiling of + the exact square root of *n*. For positive *n*, this can be computed using + ``a = 1 + isqrt(n - 1)``. + + + .. |nbsp| unicode:: 0xA0 + :trim: + + +.. function:: lcm(*integers) + + Return the least common multiple of the specified integer arguments. + If all arguments are nonzero, then the returned value is the smallest + positive integer that is a multiple of all arguments. If any of the arguments + is zero, then the returned value is ``0``. ``lcm()`` without arguments + returns ``1``. + + +.. function:: perm(n, k=None, /) + + Return the number of ways to choose *k* items from *n* items + without repetition and with order. + + Evaluates to ``n! / (n - k)!`` when ``k <= n`` and evaluates + to zero when ``k > n``. + + If *k* is not specified or is ``None``, then *k* defaults to *n* + and the function returns ``n!``. + + Raises :exc:`ValueError` if either of the arguments are negative. diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 55f2de07553d56..efe411e5a43f27 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -27,15 +27,6 @@ noted otherwise, all return values are floats. ==================================================== ============================================ -**Number-theoretic functions** --------------------------------------------------------------------------------------------------- -:func:`comb(n, k) ` Number of ways to choose *k* items from *n* items without repetition and without order -:func:`factorial(n) ` *n* factorial -:func:`gcd(*integers) ` Greatest common divisor of the integer arguments -:func:`isqrt(n) ` Integer square root of a nonnegative integer *n* -:func:`lcm(*integers) ` Least common multiple of the integer arguments -:func:`perm(n, k) ` Number of ways to choose *k* items from *n* items without repetition and with order - **Floating point arithmetic** -------------------------------------------------------------------------------------------------- :func:`ceil(x) ` Ceiling of *x*, the smallest integer greater than or equal to *x* @@ -92,13 +83,20 @@ noted otherwise, all return values are floats. **Trigonometric functions** -------------------------------------------------------------------------------------------------- -:func:`acos(x) ` Arc cosine of *x* -:func:`asin(x) ` Arc sine of *x* -:func:`atan(x) ` Arc tangent of *x* -:func:`atan2(y, x) ` ``atan(y / x)`` -:func:`cos(x) ` Cosine of *x* -:func:`sin(x) ` Sine of *x* -:func:`tan(x) ` Tangent of *x* +:func:`acos(x) ` Arc cosine of *x*, in radians +:func:`acospi(x) ` Arc cosine of *x*, in half-turns +:func:`asin(x) ` Arc sine of *x*, in radians +:func:`asinpi(x) ` Arc sine of *x*, in half-turns +:func:`atan(x) ` Arc tangent of *x*, in radians +:func:`atanpi(x) ` Arc tangent of *x*, in half-turns +:func:`atan2(y, x) ` ``atan(y / x)``, in radians +:func:`atan2pi(y, x) ` ``atan(y / x)``, in half-turns +:func:`cos(x) ` Cosine of *x* radians +:func:`cospi(x) ` Cosine of *x⋅π* radians +:func:`sin(x) ` Sine of *x* radians +:func:`sinpi(x) ` Sine of *x⋅π* radians +:func:`tan(x) ` Tangent of *x* radians +:func:`tanpi(x) ` Tangent of *x⋅π* radians **Hyperbolic functions** -------------------------------------------------------------------------------------------------- @@ -126,92 +124,6 @@ noted otherwise, all return values are floats. ==================================================== ============================================ -Number-theoretic functions --------------------------- - -.. function:: comb(n, k) - - Return the number of ways to choose *k* items from *n* items without repetition - and without order. - - Evaluates to ``n! / (k! * (n - k)!)`` when ``k <= n`` and evaluates - to zero when ``k > n``. - - Also called the binomial coefficient because it is equivalent - to the coefficient of k-th term in polynomial expansion of - ``(1 + x)ⁿ``. - - Raises :exc:`TypeError` if either of the arguments are not integers. - Raises :exc:`ValueError` if either of the arguments are negative. - - .. versionadded:: 3.8 - - -.. function:: factorial(n) - - Return factorial of the nonnegative integer *n*. - - .. versionchanged:: 3.10 - Floats with integral values (like ``5.0``) are no longer accepted. - - -.. function:: gcd(*integers) - - Return the greatest common divisor of the specified integer arguments. - If any of the arguments is nonzero, then the returned value is the largest - positive integer that is a divisor of all arguments. If all arguments - are zero, then the returned value is ``0``. ``gcd()`` without arguments - returns ``0``. - - .. versionadded:: 3.5 - - .. versionchanged:: 3.9 - Added support for an arbitrary number of arguments. Formerly, only two - arguments were supported. - - -.. function:: isqrt(n) - - Return the integer square root of the nonnegative integer *n*. This is the - floor of the exact square root of *n*, or equivalently the greatest integer - *a* such that *a*\ ² |nbsp| ≤ |nbsp| *n*. - - For some applications, it may be more convenient to have the least integer - *a* such that *n* |nbsp| ≤ |nbsp| *a*\ ², or in other words the ceiling of - the exact square root of *n*. For positive *n*, this can be computed using - ``a = 1 + isqrt(n - 1)``. - - .. versionadded:: 3.8 - - -.. function:: lcm(*integers) - - Return the least common multiple of the specified integer arguments. - If all arguments are nonzero, then the returned value is the smallest - positive integer that is a multiple of all arguments. If any of the arguments - is zero, then the returned value is ``0``. ``lcm()`` without arguments - returns ``1``. - - .. versionadded:: 3.9 - - -.. function:: perm(n, k=None) - - Return the number of ways to choose *k* items from *n* items - without repetition and with order. - - Evaluates to ``n! / (n - k)!`` when ``k <= n`` and evaluates - to zero when ``k > n``. - - If *k* is not specified or is ``None``, then *k* defaults to *n* - and the function returns ``n!``. - - Raises :exc:`TypeError` if either of the arguments are not integers. - Raises :exc:`ValueError` if either of the arguments are negative. - - .. versionadded:: 3.8 - - Floating point arithmetic ------------------------- @@ -259,7 +171,7 @@ Floating point arithmetic is, :func:`!fmax` is not required to be sensitive to the sign of such operands (see Annex F of the C11 standard, §F.10.0.3 and §F.10.9.2). - .. versionadded:: next + .. versionadded:: 3.15 .. function:: fmin(x, y) @@ -271,7 +183,7 @@ Floating point arithmetic is, :func:`!fmin` is not required to be sensitive to the sign of such operands (see Annex F of the C11 standard, §F.10.0.3 and §F.10.9.3). - .. versionadded:: next + .. versionadded:: 3.15 .. function:: fmod(x, y) @@ -350,10 +262,12 @@ Floating point manipulation functions .. function:: frexp(x) - Return the mantissa and exponent of *x* as the pair ``(m, e)``. *m* is a float - and *e* is an integer such that ``x == m * 2**e`` exactly. If *x* is zero, - returns ``(0.0, 0)``, otherwise ``0.5 <= abs(m) < 1``. This is used to "pick - apart" the internal representation of a float in a portable way. + Return the mantissa and exponent of *x* as the pair ``(m, e)``. + If *x* is a finite nonzero number, then *m* is a float with + ``0.5 <= abs(m) < 1.0`` and an integer *e* is such that + ``x == m * 2**e`` exactly. Else, return ``(x, 0)``. + This is used to "pick apart" the internal representation of + a float in a portable way. Note that :func:`frexp` has a different call/return pattern than its C equivalents: it takes a single argument and return a pair of @@ -408,7 +322,7 @@ Floating point manipulation functions nonzero number that is not a subnormal (see :func:`issubnormal`). Return ``False`` otherwise. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: issubnormal(x) @@ -417,7 +331,7 @@ Floating point manipulation functions nonzero number with a magnitude smaller than :data:`sys.float_info.min`. Return ``False`` otherwise. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: isinf(x) @@ -464,7 +378,7 @@ Floating point manipulation functions This is useful to detect the sign bit of zeroes, infinities and NaNs. - .. versionadded:: next + .. versionadded:: 3.15 .. function:: ulp(x) @@ -601,7 +515,7 @@ Summation and product functions Roughly equivalent to:: - sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q))) + sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q, strict=True))) .. versionadded:: 3.8 @@ -692,18 +606,42 @@ Trigonometric functions ``pi``. +.. function:: acospi(x) + + Return the arc cosine of *x*, in half-turns. The result is between ``0`` and + ``1``. + + .. versionadded:: next + + .. function:: asin(x) Return the arc sine of *x*, in radians. The result is between ``-pi/2`` and ``pi/2``. +.. function:: asinpi(x) + + Return the arc sine of *x*, in half-turns. The result is between ``-0.5`` and + ``0.5``. + + .. versionadded:: next + + .. function:: atan(x) Return the arc tangent of *x*, in radians. The result is between ``-pi/2`` and ``pi/2``. +.. function:: atanpi(x) + + Return the arc tangent of *x*, in half-turns. The result is between ``-0.5`` and + ``0.5``. + + .. versionadded:: next + + .. function:: atan2(y, x) Return ``atan(y / x)``, in radians. The result is between ``-pi`` and ``pi``. @@ -714,21 +652,54 @@ Trigonometric functions -1)`` is ``-3*pi/4``. +.. function:: atan2pi(y, x) + + Return ``atanpi(y / x)``, in half-turns. The result is between ``-1`` and ``1``. + The vector in the plane from the origin to point ``(x, y)`` makes this angle + with the positive X axis. The point of :func:`atan2pi` is that the signs of both + inputs are known to it, so it can compute the correct quadrant for the angle. + For example, ``atanpi(1)`` and ``atan2pi(1, 1)`` are both ``0.25``, but + ``atan2pi(-1, -1)`` is ``-0.75``. + + .. versionadded:: next + + .. function:: cos(x) Return the cosine of *x* radians. +.. function:: cospi(x) + + Return the cosine of *x* half-turns (*x⋅π* radians). + + .. versionadded:: next + + .. function:: sin(x) Return the sine of *x* radians. +.. function:: sinpi(x) + + Return the sine of *x* half-turns (*x⋅π* radians). + + .. versionadded:: next + + .. function:: tan(x) Return the tangent of *x* radians. +.. function:: tanpi(x) + + Return the tangent of *x* half-turns (*x⋅π* radians). + + .. versionadded:: next + + Hyperbolic functions -------------------- @@ -812,6 +783,74 @@ Special functions .. versionadded:: 3.2 +Number-theoretic functions +-------------------------- + +For backward compatibility, the :mod:`!math` module provides also aliases of +the following functions from the :mod:`math.integer` module: + +.. list-table:: + + * - .. function:: comb(n, k) + :no-typesetting: + + :func:`comb(n, k) ` + - Number of ways to choose *k* items from *n* items without repetition + and without order + + * - .. function:: factorial(n) + :no-typesetting: + + :func:`factorial(n) ` + - *n* factorial + + * - .. function:: gcd(*integers) + :no-typesetting: + + :func:`gcd(*integers) ` + - Greatest common divisor of the integer arguments + + * - .. function:: isqrt(n) + :no-typesetting: + + :func:`isqrt(n) ` + - Integer square root of a nonnegative integer *n* + + * - .. function:: lcm(*integers) + :no-typesetting: + + :func:`lcm(*integers) ` + - Least common multiple of the integer arguments + + * - .. function:: perm(n, k) + :no-typesetting: + + :func:`perm(n, k) ` + - Number of ways to choose *k* items from *n* items without repetition + and with order + +.. versionadded:: 3.5 + The :func:`gcd` function. + +.. versionadded:: 3.8 + The :func:`comb`, :func:`perm` and :func:`isqrt` functions. + +.. versionadded:: 3.9 + The :func:`lcm` function. + +.. versionchanged:: 3.9 + Added support for an arbitrary number of arguments in the :func:`gcd` + function. + Formerly, only two arguments were supported. + +.. versionchanged:: 3.10 + Floats with integral values (like ``5.0``) are no longer accepted in the + :func:`factorial` function. + +.. soft-deprecated:: 3.15 + Use the :mod:`math.integer` functions instead of these aliases. + + Constants --------- @@ -872,7 +911,7 @@ Constants .. impl-detail:: - The :mod:`math` module consists mostly of thin wrappers around the platform C + The :mod:`!math` module consists mostly of thin wrappers around the platform C math library functions. Behavior in exceptional cases follows Annex F of the C99 standard where appropriate. The current implementation will raise :exc:`ValueError` for invalid operations like ``sqrt(-1.0)`` or ``log(0.0)`` @@ -894,5 +933,5 @@ Constants Module :mod:`cmath` Complex number versions of many of these functions. -.. |nbsp| unicode:: 0xA0 - :trim: + Module :mod:`math.integer` + Integer-specific mathematics functions. diff --git a/Doc/library/mimetypes.rst b/Doc/library/mimetypes.rst index 13511b16a0ed8c..f33098faf7d8a7 100644 --- a/Doc/library/mimetypes.rst +++ b/Doc/library/mimetypes.rst @@ -4,15 +4,13 @@ .. module:: mimetypes :synopsis: Mapping of filename extensions to MIME types. -.. sectionauthor:: Fred L. Drake, Jr. - **Source code:** :source:`Lib/mimetypes.py` .. index:: pair: MIME; content type -------------- -The :mod:`mimetypes` module converts between a filename or URL and the MIME type +The :mod:`!mimetypes` module converts between a filename or URL and the MIME type associated with the filename extension. Conversions are provided from filename to MIME type and from MIME type to filename extension; encodings are not supported for the latter conversion. @@ -56,8 +54,8 @@ the information :func:`init` sets up. .. versionchanged:: 3.8 Added support for *url* being a :term:`path-like object`. - .. deprecated:: 3.13 - Passing a file path instead of URL is :term:`soft deprecated`. + .. soft-deprecated:: 3.13 + Passing a file path instead of URL. Use :func:`guess_file_type` for this. @@ -131,10 +129,18 @@ behavior of the module. Add a mapping from the MIME type *type* to the extension *ext*. When the extension is already known, the new type will replace the old one. When the type is already known the extension will be added to the list of known extensions. + Valid extensions are empty or start with a ``'.'``. When *strict* is ``True`` (the default), the mapping will be added to the official MIME types, otherwise to the non-standard ones. + .. deprecated:: 3.14 + *ext* values that do not start with ``'.'`` are deprecated. + + .. versionchanged:: next + *ext* now must start with ``'.'``. Otherwise :exc:`ValueError` is raised. + + .. data:: inited @@ -196,7 +202,7 @@ MimeTypes objects The :class:`MimeTypes` class may be useful for applications which may want more than one MIME-type database; it provides an interface similar to the one of the -:mod:`mimetypes` module. +:mod:`!mimetypes` module. .. class:: MimeTypes(filenames=(), strict=True) @@ -309,9 +315,11 @@ than one MIME-type database; it provides an interface similar to the one of the When *strict* is ``True`` (the default), the mapping will be added to the official MIME types, otherwise to the non-standard ones. - .. deprecated-removed:: 3.14 3.16 - Invalid, undotted extensions will raise a - :exc:`ValueError` in Python 3.16. + .. deprecated:: 3.14 + *ext* values that do not start with ``'.'`` are deprecated. + + .. versionchanged:: next + *ext* now must start with ``'.'``. Otherwise :exc:`ValueError` is raised. .. _mimetypes-cli: @@ -350,7 +358,7 @@ it converts file extensions to MIME types. For each ``type`` entry, the script writes a line into the standard output stream. If an unknown type occurs, it writes an error message into the -standard error stream and exits with the return code ``1``. +standard output stream and exits with the return code ``1``. .. mimetypes-cli-example: @@ -377,7 +385,7 @@ interface: $ # get a MIME type for a rare file extension $ python -m mimetypes filename.pict - error: unknown extension of filename.pict + error: media type unknown for filename.pict $ # now look in the extended database built into Python $ python -m mimetypes --lenient filename.pict @@ -399,7 +407,8 @@ interface: $ python -m mimetypes filename.sh filename.nc filename.xxx filename.txt type: application/x-sh encoding: None type: application/x-netcdf encoding: None - error: unknown extension of filename.xxx + error: media type unknown for filename.xxx + type: text/plain encoding: None $ # try to feed an unknown MIME type $ python -m mimetypes --extension audio/aac audio/opus audio/future audio/x-wav diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst index 8fca79b23e4e15..774d05fdcaa300 100644 --- a/Doc/library/mmap.rst +++ b/Doc/library/mmap.rst @@ -48,10 +48,11 @@ update the underlying file. To map anonymous memory, -1 should be passed as the fileno along with the length. -.. class:: mmap(fileno, length, tagname=None, access=ACCESS_DEFAULT, offset=0) +.. class:: mmap(fileno, length, tagname=None, \ + access=ACCESS_DEFAULT, offset=0, *, trackfd=True) **(Windows version)** Maps *length* bytes from the file specified by the - file handle *fileno*, and creates a mmap object. If *length* is larger + file descriptor *fileno*, and creates a mmap object. If *length* is larger than the current size of the file, the file is extended to contain *length* bytes. If *length* is ``0``, the maximum length of the map is the current size of the file, except that if the file is empty Windows raises an @@ -69,6 +70,17 @@ To map anonymous memory, -1 should be passed as the fileno along with the length will be relative to the offset from the beginning of the file. *offset* defaults to 0. *offset* must be a multiple of the :const:`ALLOCATIONGRANULARITY`. + If *trackfd* is ``False``, the file handle corresponding to *fileno* will + not be duplicated, and the resulting :class:`!mmap` object will not + be associated with the map's underlying file. + This means that the :meth:`~mmap.mmap.size` and :meth:`~mmap.mmap.resize` + methods will fail. + This mode is useful to limit the number of open file handles. + The original file can be renamed (but not deleted) after closing *fileno*. + + .. versionchanged:: 3.15 + The *trackfd* parameter was added. + .. audit-event:: mmap.__new__ fileno,length,access,offset mmap.mmap .. class:: mmap(fileno, length, flags=MAP_SHARED, prot=PROT_WRITE|PROT_READ, \ @@ -200,7 +212,7 @@ To map anonymous memory, -1 should be passed as the fileno along with the length Writable :term:`bytes-like object` is now accepted. - .. method:: flush([offset[, size]]) + .. method:: flush([offset[, size]], *, flags=MS_SYNC) Flushes changes made to the in-memory copy of a file back to disk. Without use of this call there is no guarantee that changes are written back before @@ -209,6 +221,12 @@ To map anonymous memory, -1 should be passed as the fileno along with the length whole extent of the mapping is flushed. *offset* must be a multiple of the :const:`PAGESIZE` or :const:`ALLOCATIONGRANULARITY`. + The *flags* parameter specifies the synchronization behavior. + *flags* must be one of the :ref:`MS_* constants ` available + on the system. + + On Windows, the *flags* parameter is ignored. + ``None`` is returned to indicate success. An exception is raised when the call failed. @@ -217,6 +235,15 @@ To map anonymous memory, -1 should be passed as the fileno along with the length on error under Windows. A zero value was returned on success; an exception was raised on error under Unix. + .. versionchanged:: 3.15 + Allow specifying *offset* without *size*. Previously, both *offset* + and *size* parameters were required together. Now *offset* can be + specified alone, and the flush operation will extend from *offset* + to the end of the mmap. + + .. versionchanged:: 3.15 + Added *flags* parameter to control synchronization behavior. + .. method:: madvise(option[, start[, length]]) @@ -277,6 +304,8 @@ To map anonymous memory, -1 should be passed as the fileno along with the length pagefile) will silently create a new map with the original data copied over up to the length of the new size. + Availability: Windows and systems with the ``mremap()`` system call. + .. versionchanged:: 3.11 Correctly fails if attempting to resize when another map is held Allows resize against an anonymous map on Windows @@ -308,10 +337,25 @@ To map anonymous memory, -1 should be passed as the fileno along with the length .. versionadded:: 3.13 + .. method:: set_name(name, /) + + Annotate the memory mapping with the given *name* for easier identification + in ``/proc//maps`` if the kernel supports the feature and :option:`-X dev <-X>` is passed + to Python or if Python is built in :ref:`debug mode `. + The length of *name* must not exceed 67 bytes including the ``'\0'`` terminator. + + .. availability:: Linux >= 5.17 (kernel built with ``CONFIG_ANON_VMA_NAME`` option) + + .. versionadded:: 3.15 + .. method:: size() Return the length of the file, which can be larger than the size of the memory-mapped area. + For an anonymous mapping, return its size. + + .. versionchanged:: 3.15 + Anonymous mappings are now supported on Unix. .. method:: tell() @@ -426,3 +470,22 @@ MAP_* Constants :data:`MAP_TPRO`, :data:`MAP_TRANSLATED_ALLOW_EXECUTE`, and :data:`MAP_UNIX03` constants. +.. _ms-constants: + +MS_* Constants +++++++++++++++ + +.. data:: MS_SYNC + MS_ASYNC + MS_INVALIDATE + + These flags control the synchronization behavior for :meth:`mmap.flush`: + + * :data:`MS_SYNC` - Synchronous flush: writes are scheduled and the call + blocks until they are physically written to storage. + * :data:`MS_ASYNC` - Asynchronous flush: writes are scheduled but the call + returns immediately without waiting for completion. + * :data:`MS_INVALIDATE` - Invalidate cached data: invalidates other mappings + of the same file so they can see the changes. + + .. versionadded:: 3.15 diff --git a/Doc/library/modulefinder.rst b/Doc/library/modulefinder.rst index 823d853f1ed8eb..d26dcbd5f68725 100644 --- a/Doc/library/modulefinder.rst +++ b/Doc/library/modulefinder.rst @@ -4,8 +4,6 @@ .. module:: modulefinder :synopsis: Find modules used by a script. -.. sectionauthor:: A.M. Kuchling - **Source code:** :source:`Lib/modulefinder.py` -------------- diff --git a/Doc/library/msvcrt.rst b/Doc/library/msvcrt.rst index 327cc3602b1a77..6b49c1a9ccd6e1 100644 --- a/Doc/library/msvcrt.rst +++ b/Doc/library/msvcrt.rst @@ -2,10 +2,9 @@ =========================================================== .. module:: msvcrt - :platform: Windows :synopsis: Miscellaneous useful routines from the MS VC++ runtime. -.. sectionauthor:: Fred L. Drake, Jr. +**Source code:** :source:`PC/msvcrtmodule.c` -------------- @@ -22,6 +21,8 @@ api. The normal API deals only with ASCII characters and is of limited use for internationalized applications. The wide char API should be used where ever possible. +.. availability:: Windows. + .. versionchanged:: 3.3 Operations in this module now raise :exc:`OSError` where :exc:`IOError` was raised. diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst index c80f78e614818e..2d13053915830b 100644 --- a/Doc/library/multiprocessing.rst +++ b/Doc/library/multiprocessing.rst @@ -13,17 +13,16 @@ Introduction ------------ -:mod:`multiprocessing` is a package that supports spawning processes using an -API similar to the :mod:`threading` module. The :mod:`multiprocessing` package +:mod:`!multiprocessing` is a package that supports spawning processes using an +API similar to the :mod:`threading` module. The :mod:`!multiprocessing` package offers both local and remote concurrency, effectively side-stepping the :term:`Global Interpreter Lock ` by using subprocesses instead of threads. Due -to this, the :mod:`multiprocessing` module allows the programmer to fully +to this, the :mod:`!multiprocessing` module allows the programmer to fully leverage multiple processors on a given machine. It runs on both POSIX and Windows. -The :mod:`multiprocessing` module also introduces APIs which do not have -analogs in the :mod:`threading` module. A prime example of this is the +The :mod:`!multiprocessing` module also introduces the :class:`~multiprocessing.pool.Pool` object which offers a convenient means of parallelizing the execution of a function across multiple input values, distributing the input data across processes (data parallelism). The following @@ -44,6 +43,10 @@ will print to standard output :: [1, 4, 9] +The :mod:`!multiprocessing` module also introduces APIs which do not have +analogs in the :mod:`threading` module, like the ability to :meth:`terminate +`, :meth:`interrupt ` or :meth:`kill +` a running process. .. seealso:: @@ -58,7 +61,7 @@ will print to standard output :: The :class:`Process` class ^^^^^^^^^^^^^^^^^^^^^^^^^^ -In :mod:`multiprocessing`, processes are spawned by creating a :class:`Process` +In :mod:`!multiprocessing`, processes are spawned by creating a :class:`Process` object and then calling its :meth:`~Process.start` method. :class:`Process` follows the API of :class:`threading.Thread`. A trivial example of a multiprocess program is :: @@ -97,6 +100,10 @@ To show the individual process IDs involved, here is an expanded example:: For an explanation of why the ``if __name__ == '__main__'`` part is necessary, see :ref:`multiprocessing-programming`. +The arguments to :class:`Process` usually need to be picklable so they can be +passed to the child process. If you tried typing the above example directly +into a REPL it could lead to an :exc:`AttributeError` in the child process +trying to locate the *f* function in the ``__main__`` module. .. _multiprocessing-start-methods: @@ -104,7 +111,7 @@ necessary, see :ref:`multiprocessing-programming`. Contexts and start methods ^^^^^^^^^^^^^^^^^^^^^^^^^^ -Depending on the platform, :mod:`multiprocessing` supports three ways +Depending on the platform, :mod:`!multiprocessing` supports three ways to start a process. These *start methods* are .. _multiprocessing-start-method-spawn: @@ -233,9 +240,12 @@ processes for a different context. In particular, locks created using the *fork* context cannot be passed to processes started using the *spawn* or *forkserver* start methods. -A library which wants to use a particular start method should probably -use :func:`get_context` to avoid interfering with the choice of the -library user. +Libraries using :mod:`!multiprocessing` or +:class:`~concurrent.futures.ProcessPoolExecutor` should be designed to allow +their users to provide their own multiprocessing context. Using a specific +context of your own within a library can lead to incompatibilities with the +rest of the library user's application. Always document if your library +requires a specific start method. .. warning:: @@ -248,7 +258,7 @@ library user. Exchanging objects between processes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -:mod:`multiprocessing` supports two types of communication channel between +:mod:`!multiprocessing` supports two types of communication channel between processes: **Queues** @@ -269,7 +279,7 @@ processes: p.join() Queues are thread and process safe. - Any object put into a :mod:`~multiprocessing` queue will be serialized. + Any object put into a :mod:`!multiprocessing` queue will be serialized. **Pipes** @@ -303,7 +313,7 @@ processes: Synchronization between processes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -:mod:`multiprocessing` contains equivalents of all the synchronization +:mod:`!multiprocessing` contains equivalents of all the synchronization primitives from :mod:`threading`. For instance one can use a lock to ensure that only one process prints to standard output at a time:: @@ -334,7 +344,7 @@ avoid using shared state as far as possible. This is particularly true when using multiple processes. However, if you really do need to use some shared data then -:mod:`multiprocessing` provides a couple of ways of doing so. +:mod:`!multiprocessing` provides a couple of ways of doing so. **Shared memory** @@ -508,9 +518,24 @@ process which created it. Reference --------- -The :mod:`multiprocessing` package mostly replicates the API of the +The :mod:`!multiprocessing` package mostly replicates the API of the :mod:`threading` module. +.. _global-start-method: + +Global start method +^^^^^^^^^^^^^^^^^^^ + +Python supports several ways to create and initialize a process. +The global start method sets the default mechanism for creating a process. + +Several multiprocessing functions and methods that may also instantiate +certain objects will implicitly set the global start method to the system's default, +if it hasn’t been set already. The global start method can only be set once. +If you need to change the start method from the system default, you must +proactively set the global start method before calling functions or methods, +or creating these objects. + :class:`Process` and exceptions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -538,9 +563,42 @@ The :mod:`multiprocessing` package mostly replicates the API of the to pass to *target*. If a subclass overrides the constructor, it must make sure it invokes the - base class constructor (:meth:`Process.__init__`) before doing anything else + base class constructor (``super().__init__()``) before doing anything else to the process. + .. note:: + + In general, all arguments to :class:`Process` must be picklable. This is + frequently observed when trying to create a :class:`Process` or use a + :class:`concurrent.futures.ProcessPoolExecutor` from a REPL with a + locally defined *target* function. + + Passing a callable object defined in the current REPL session causes the + child process to die via an uncaught :exc:`AttributeError` exception when + starting as *target* must have been defined within an importable module + in order to be loaded during unpickling. + + Example of this uncatchable error from the child:: + + >>> import multiprocessing as mp + >>> def knigit(): + ... print("Ni!") + ... + >>> process = mp.Process(target=knigit) + >>> process.start() + >>> Traceback (most recent call last): + File ".../multiprocessing/spawn.py", line ..., in spawn_main + File ".../multiprocessing/spawn.py", line ..., in _main + AttributeError: module '__main__' has no attribute 'knigit' + >>> process + + + See :ref:`multiprocessing-programming-spawn`. While this restriction is + not true if using the ``"fork"`` start method, as of Python ``3.14`` that + is no longer the default on any platform. See + :ref:`multiprocessing-start-methods`. + See also :gh:`132898`. + .. versionchanged:: 3.3 Added the *daemon* parameter. @@ -646,7 +704,7 @@ The :mod:`multiprocessing` package mostly replicates the API of the The process's authentication key (a byte string). - When :mod:`multiprocessing` is initialized the main process is assigned a + When :mod:`!multiprocessing` is initialized the main process is assigned a random string using :func:`os.urandom`. When a :class:`Process` object is created, it will inherit the @@ -747,7 +805,7 @@ The :mod:`multiprocessing` package mostly replicates the API of the .. exception:: ProcessError - The base class of all :mod:`multiprocessing` exceptions. + The base class of all :mod:`!multiprocessing` exceptions. .. exception:: BufferTooShort @@ -787,19 +845,19 @@ If you use :class:`JoinableQueue` then you **must** call semaphore used to count the number of unfinished tasks may eventually overflow, raising an exception. -One difference from other Python queue implementations, is that :mod:`multiprocessing` +One difference from other Python queue implementations, is that :mod:`!multiprocessing` queues serializes all objects that are put into them using :mod:`pickle`. -The object return by the get method is a re-created object that does not share memory -with the original object. +The object returned by the get method is a re-created object that does not share +memory with the original object. Note that one can also create a shared queue by using a manager object -- see :ref:`multiprocessing-managers`. .. note:: - :mod:`multiprocessing` uses the usual :exc:`queue.Empty` and + :mod:`!multiprocessing` uses the usual :exc:`queue.Empty` and :exc:`queue.Full` exceptions to signal a timeout. They are not available in - the :mod:`multiprocessing` namespace so you need to import them from + the :mod:`!multiprocessing` namespace so you need to import them from :mod:`queue`. .. note:: @@ -847,7 +905,7 @@ For an example of the usage of queues for interprocess communication see :ref:`multiprocessing-examples`. -.. function:: Pipe([duplex]) +.. function:: Pipe(duplex=True) Returns a pair ``(conn1, conn2)`` of :class:`~multiprocessing.connection.Connection` objects representing the @@ -867,11 +925,15 @@ For an example of the usage of queues for interprocess communication see locks/semaphores. When a process first puts an item on the queue a feeder thread is started which transfers objects from a buffer into the pipe. + Instantiating this class may set the global start method. See + :ref:`global-start-method` for more details. + The usual :exc:`queue.Empty` and :exc:`queue.Full` exceptions from the standard library's :mod:`queue` module are raised to signal timeouts. :class:`Queue` implements all the methods of :class:`queue.Queue` except for - :meth:`~queue.Queue.task_done` and :meth:`~queue.Queue.join`. + :meth:`~queue.Queue.task_done`, :meth:`~queue.Queue.join`, and + :meth:`~queue.Queue.shutdown`. .. method:: qsize() @@ -982,6 +1044,9 @@ For an example of the usage of queues for interprocess communication see It is a simplified :class:`Queue` type, very close to a locked :class:`Pipe`. + Instantiating this class may set the global start method. See + :ref:`global-start-method` for more details. + .. method:: close() Close the queue: release internal resources. @@ -1012,6 +1077,9 @@ For an example of the usage of queues for interprocess communication see :class:`JoinableQueue`, a :class:`Queue` subclass, is a queue which additionally has :meth:`task_done` and :meth:`join` methods. + Instantiating this class may set the global start method. See + :ref:`global-start-method` for more details. + .. method:: task_done() Indicate that a formerly enqueued task is complete. Used by queue @@ -1085,7 +1153,7 @@ Miscellaneous .. function:: freeze_support() - Add support for when a program which uses :mod:`multiprocessing` has been + Add support for when a program which uses :mod:`!multiprocessing` has been frozen to produce an executable. (Has been tested with **py2exe**, **PyInstaller** and **cx_Freeze**.) @@ -1121,11 +1189,11 @@ Miscellaneous .. function:: get_context(method=None) Return a context object which has the same attributes as the - :mod:`multiprocessing` module. + :mod:`!multiprocessing` module. If *method* is ``None`` then the default context is returned. Note that if - the global start method has not been set, this will set it to the - default method. + the global start method has not been set, this will set it to the system default + See :ref:`global-start-method` for more details. Otherwise *method* should be ``'fork'``, ``'spawn'``, ``'forkserver'``. :exc:`ValueError` is raised if the specified start method is not available. See :ref:`multiprocessing-start-methods`. @@ -1136,10 +1204,9 @@ Miscellaneous Return the name of start method used for starting processes. - If the global start method has not been set and *allow_none* is - ``False``, then the start method is set to the default and the name - is returned. If the start method has not been set and *allow_none* is - ``True`` then ``None`` is returned. + If the global start method is not set and *allow_none* is ``False``, the global start + method is set to the default, and its name is returned. See + :ref:`global-start-method` for more details. The return value can be ``'fork'``, ``'spawn'``, ``'forkserver'`` or ``None``. See :ref:`multiprocessing-start-methods`. @@ -1156,7 +1223,7 @@ Miscellaneous Set the path of the Python interpreter to use when starting a child process. (By default :data:`sys.executable` is used). Embedders will probably need to - do some thing like :: + do something like :: set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe')) @@ -1168,22 +1235,32 @@ Miscellaneous .. versionchanged:: 3.11 Accepts a :term:`path-like object`. -.. function:: set_forkserver_preload(module_names) +.. function:: set_forkserver_preload(module_names, *, on_error='ignore') Set a list of module names for the forkserver main process to attempt to import so that their already imported state is inherited by forked - processes. Any :exc:`ImportError` when doing so is silently ignored. - This can be used as a performance enhancement to avoid repeated work - in every process. + processes. This can be used as a performance enhancement to avoid repeated + work in every process. For this to work, it must be called before the forkserver process has been launched (before creating a :class:`Pool` or starting a :class:`Process`). + The *on_error* parameter controls how :exc:`ImportError` exceptions during + module preloading are handled: ``"ignore"`` (default) silently ignores + failures, ``"warn"`` causes the forkserver subprocess to emit an + :exc:`ImportWarning` to stderr, and ``"fail"`` causes the forkserver + subprocess to exit with the exception traceback on stderr, making + subsequent process creation fail with :exc:`EOFError` or + :exc:`ConnectionError`. + Only meaningful when using the ``'forkserver'`` start method. See :ref:`multiprocessing-start-methods`. .. versionadded:: 3.4 + .. versionchanged:: 3.15 + Added the *on_error* parameter. + .. function:: set_start_method(method, force=False) Set the method which should be used to start child processes. @@ -1203,7 +1280,7 @@ Miscellaneous .. note:: - :mod:`multiprocessing` contains no analogues of + :mod:`!multiprocessing` contains no analogues of :func:`threading.active_count`, :func:`threading.enumerate`, :func:`threading.settrace`, :func:`threading.setprofile`, :class:`threading.Timer`, or :class:`threading.local`. @@ -1259,12 +1336,12 @@ Connection objects are usually created using Note that multiple connection objects may be polled at once by using :func:`multiprocessing.connection.wait`. - .. method:: send_bytes(buffer[, offset[, size]]) + .. method:: send_bytes(buf[, offset[, size]]) Send byte data from a :term:`bytes-like object` as a complete message. - If *offset* is given then data is read from that position in *buffer*. If - *size* is given then that many bytes will be read from buffer. Very large + If *offset* is given then data is read from that position in *buf*. If + *size* is given then that many bytes will be read from *buf*. Very large buffers (approximately 32 MiB+, though it depends on the OS) may raise a :exc:`ValueError` exception @@ -1284,18 +1361,18 @@ Connection objects are usually created using alias of :exc:`OSError`. - .. method:: recv_bytes_into(buffer[, offset]) + .. method:: recv_bytes_into(buf[, offset]) - Read into *buffer* a complete message of byte data sent from the other end + Read into *buf* a complete message of byte data sent from the other end of the connection and return the number of bytes in the message. Blocks until there is something to receive. Raises :exc:`EOFError` if there is nothing left to receive and the other end was closed. - *buffer* must be a writable :term:`bytes-like object`. If + *buf* must be a writable :term:`bytes-like object`. If *offset* is given then the message will be written into the buffer from that position. Offset must be a non-negative integer less than the - length of *buffer* (in bytes). + length of *buf* (in bytes). If the buffer is too short then a :exc:`BufferTooShort` exception is raised and the complete message is available as ``e.args[0]`` where ``e`` @@ -1366,6 +1443,9 @@ object -- see :ref:`multiprocessing-managers`. A barrier object: a clone of :class:`threading.Barrier`. + Instantiating this class may set the global start method. See + :ref:`global-start-method` for more details. + .. versionadded:: 3.3 .. class:: BoundedSemaphore([value]) @@ -1373,6 +1453,9 @@ object -- see :ref:`multiprocessing-managers`. A bounded semaphore object: a close analog of :class:`threading.BoundedSemaphore`. + Instantiating this class may set the global start method. See + :ref:`global-start-method` for more details. + A solitary difference from its close analog exists: its ``acquire`` method's first argument is named *block*, as is consistent with :meth:`Lock.acquire`. @@ -1391,7 +1474,10 @@ object -- see :ref:`multiprocessing-managers`. A condition variable: an alias for :class:`threading.Condition`. If *lock* is specified then it should be a :class:`Lock` or :class:`RLock` - object from :mod:`multiprocessing`. + object from :mod:`!multiprocessing`. + + Instantiating this class may set the global start method. See + :ref:`global-start-method` for more details. .. versionchanged:: 3.3 The :meth:`~threading.Condition.wait_for` method was added. @@ -1400,6 +1486,8 @@ object -- see :ref:`multiprocessing-managers`. A clone of :class:`threading.Event`. + Instantiating this class may set the global start method. See + :ref:`global-start-method` for more details. .. class:: Lock() @@ -1415,6 +1503,9 @@ object -- see :ref:`multiprocessing-managers`. instance of ``multiprocessing.synchronize.Lock`` initialized with a default context. + Instantiating this class may set the global start method. See + :ref:`global-start-method` for more details. + :class:`Lock` supports the :term:`context manager` protocol and thus may be used in :keyword:`with` statements. @@ -1472,6 +1563,9 @@ object -- see :ref:`multiprocessing-managers`. instance of ``multiprocessing.synchronize.RLock`` initialized with a default context. + Instantiating this class may set the global start method. See + :ref:`global-start-method` for more details. + :class:`RLock` supports the :term:`context manager` protocol and thus may be used in :keyword:`with` statements. @@ -1531,15 +1625,28 @@ object -- see :ref:`multiprocessing-managers`. A semaphore object: a close analog of :class:`threading.Semaphore`. + Instantiating this class may set the global start method. See + :ref:`global-start-method` for more details. + A solitary difference from its close analog exists: its ``acquire`` method's first argument is named *block*, as is consistent with :meth:`Lock.acquire`. + + .. method:: get_value() + + Return the current value of semaphore. + + Note that this may raise :exc:`NotImplementedError` on platforms like + macOS where ``sem_getvalue()`` is not implemented. + + .. method:: locked() Return a boolean indicating whether this object is locked right now. .. versionadded:: 3.14 + .. note:: On macOS, ``sem_timedwait`` is unsupported, so calling ``acquire()`` with @@ -1597,11 +1704,14 @@ inherited by child processes. value is actually a synchronized wrapper for the array. *typecode_or_type* determines the type of the elements of the returned array: - it is either a ctypes type or a one character typecode of the kind used by - the :mod:`array` module. If *size_or_initializer* is an integer, then it - determines the length of the array, and the array will be initially zeroed. - Otherwise, *size_or_initializer* is a sequence which is used to initialize - the array and whose length determines the length of the array. + it is either a :ref:`ctypes type ` or a one + character typecode of the kind used by the :mod:`array` module with the + exception of ``'w'``, which is not supported. In addition, the ``'c'`` + typecode is an alias for :class:`ctypes.c_char`. If *size_or_initializer* + is an integer, then it determines the length of the array, and the array + will be initially zeroed. Otherwise, *size_or_initializer* is a sequence + which is used to initialize the array and whose length determines the length + of the array. If *lock* is ``True`` (the default) then a new lock object is created to synchronize access to the value. If *lock* is a :class:`Lock` or @@ -1613,16 +1723,19 @@ inherited by child processes. Note that *lock* is a keyword only argument. Note that an array of :data:`ctypes.c_char` has *value* and *raw* - attributes which allow one to use it to store and retrieve strings. + attributes which can both be used to store and retrieve byte strings. + While *raw* allows interaction with a :class:`bytes` object the full size of + the array, reading *value* will terminate after a null byte, like most + programming languages handle strings. -The :mod:`multiprocessing.sharedctypes` module -"""""""""""""""""""""""""""""""""""""""""""""" +The :mod:`!multiprocessing.sharedctypes` module +""""""""""""""""""""""""""""""""""""""""""""""" .. module:: multiprocessing.sharedctypes :synopsis: Allocate ctypes objects from shared memory. -The :mod:`multiprocessing.sharedctypes` module provides functions for allocating +The :mod:`!multiprocessing.sharedctypes` module provides functions for allocating :mod:`ctypes` objects from shared memory which can be inherited by child processes. @@ -1665,7 +1778,7 @@ processes. attributes which allow one to use it to store and retrieve strings -- see documentation for :mod:`ctypes`. -.. function:: Array(typecode_or_type, size_or_initializer, *, lock=True) +.. function:: Array(typecode_or_type, size_or_initializer, *, lock=True, ctx=None) The same as :func:`RawArray` except that depending on the value of *lock* a process-safe synchronization wrapper may be returned instead of a raw ctypes @@ -1679,9 +1792,13 @@ processes. automatically protected by a lock, so it will not necessarily be "process-safe". - Note that *lock* is a keyword-only argument. + *ctx* is a context object, or ``None`` (use the current context). If ``None``, + calling this may set the global start method. See + :ref:`global-start-method` for more details. -.. function:: Value(typecode_or_type, *args, lock=True) + Note that *lock* and *ctx* are keyword-only parameters. + +.. function:: Value(typecode_or_type, *args, lock=True, ctx=None) The same as :func:`RawValue` except that depending on the value of *lock* a process-safe synchronization wrapper may be returned instead of a raw ctypes @@ -1694,19 +1811,27 @@ processes. automatically protected by a lock, so it will not necessarily be "process-safe". - Note that *lock* is a keyword-only argument. + *ctx* is a context object, or ``None`` (use the current context). If ``None``, + calling this may set the global start method. See + :ref:`global-start-method` for more details. + + Note that *lock* and *ctx* are keyword-only parameters. .. function:: copy(obj) Return a ctypes object allocated from shared memory which is a copy of the ctypes object *obj*. -.. function:: synchronized(obj[, lock]) +.. function:: synchronized(obj, lock=None, ctx=None) Return a process-safe wrapper object for a ctypes object which uses *lock* to synchronize access. If *lock* is ``None`` (the default) then a :class:`multiprocessing.RLock` object is created automatically. + *ctx* is a context object, or ``None`` (use the current context). If ``None``, + calling this may set the global start method. See + :ref:`global-start-method` for more details. + A synchronized wrapper will have two methods in addition to those of the object it wraps: :meth:`get_obj` returns the wrapped object and :meth:`get_lock` returns the lock object used for synchronization. @@ -1824,8 +1949,9 @@ their parent process exits. The manager classes are defined in the *serializer* must be ``'pickle'`` (use :mod:`pickle` serialization) or ``'xmlrpclib'`` (use :mod:`xmlrpc.client` serialization). - *ctx* is a context object, or ``None`` (use the current context). See the - :func:`get_context` function. + *ctx* is a context object, or ``None`` (use the current context). If ``None``, + calling this may set the global start method. See + :ref:`global-start-method` for more details. *shutdown_timeout* is a timeout in seconds used to wait until the process used by the manager completes in the :meth:`shutdown` method. If the @@ -2209,7 +2335,7 @@ demonstrates a level of control over the synchronization. .. note:: - The proxy types in :mod:`multiprocessing` do nothing to support comparisons + The proxy types in :mod:`!multiprocessing` do nothing to support comparisons by value. So, for instance, we have: .. doctest:: @@ -2318,7 +2444,9 @@ with the :class:`Pool` class. the worker processes. Usually a pool is created using the function :func:`multiprocessing.Pool` or the :meth:`Pool` method of a context object. In both cases *context* is set - appropriately. + appropriately. If ``None``, calling this function will have the side effect + of setting the current global start method if it has not been set already. + See the :func:`get_context` function. Note that the methods of the pool object should only be called by the process which created the pool. @@ -2349,7 +2477,7 @@ with the :class:`Pool` class. duration of the Pool's work queue. A frequent pattern found in other systems (such as Apache, mod_wsgi, etc) to free resources held by workers is to allow a worker within a pool to complete only a set - amount of work before being exiting, being cleaned up and a new + amount of work before exiting, being cleaned up and a new process spawned to replace the old one. The *maxtasksperchild* argument to the :class:`Pool` exposes this ability to the end user. @@ -2534,7 +2662,7 @@ Usually message passing between processes is done using queues or by using :class:`~Connection` objects returned by :func:`~multiprocessing.Pipe`. -However, the :mod:`multiprocessing.connection` module allows some extra +However, the :mod:`!multiprocessing.connection` module allows some extra flexibility. It basically gives a high level message oriented API for dealing with sockets or Windows named pipes. It also has support for *digest authentication* using the :mod:`hmac` module, and for polling @@ -2792,6 +2920,16 @@ between themselves. Suitable authentication keys can also be generated by using :func:`os.urandom`. +This authentication protects :class:`Listener` and :func:`Client` connections, +which are reachable by address. It is not applied to the anonymous pipes +created by :func:`~multiprocessing.Pipe` or used internally by +:class:`~multiprocessing.Queue`. +:mod:`multiprocessing` treats all local processes running as the same user as +trusted; on most operating systems such processes can access each other's pipe +file descriptors regardless. Applications that require isolation between +processes of the same user must arrange it at the operating-system level -- +for example, by running workers under a different user account or in a sandbox. + Logging ^^^^^^^ @@ -2803,7 +2941,7 @@ handler type) for messages from different processes to get mixed up. .. currentmodule:: multiprocessing .. function:: get_logger() - Returns the logger used by :mod:`multiprocessing`. If necessary, a new one + Returns the logger used by :mod:`!multiprocessing`. If necessary, a new one will be created. When first created the logger has level :const:`logging.NOTSET` and no @@ -2841,18 +2979,18 @@ Below is an example session with logging turned on:: For a full table of logging levels, see the :mod:`logging` module. -The :mod:`multiprocessing.dummy` module -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The :mod:`!multiprocessing.dummy` module +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. module:: multiprocessing.dummy :synopsis: Dumb wrapper around threading. -:mod:`multiprocessing.dummy` replicates the API of :mod:`multiprocessing` but is +:mod:`!multiprocessing.dummy` replicates the API of :mod:`!multiprocessing` but is no more than a wrapper around the :mod:`threading` module. .. currentmodule:: multiprocessing.pool -In particular, the ``Pool`` function provided by :mod:`multiprocessing.dummy` +In particular, the ``Pool`` function provided by :mod:`!multiprocessing.dummy` returns an instance of :class:`ThreadPool`, which is a subclass of :class:`Pool` that supports all the same method calls but uses a pool of worker threads rather than worker processes. @@ -2897,7 +3035,7 @@ Programming guidelines ---------------------- There are certain guidelines and idioms which should be adhered to when using -:mod:`multiprocessing`. +:mod:`!multiprocessing`. All start methods @@ -2938,7 +3076,7 @@ Joining zombie processes Better to inherit than pickle/unpickle When using the *spawn* or *forkserver* start methods many types - from :mod:`multiprocessing` need to be picklable so that child + from :mod:`!multiprocessing` need to be picklable so that child processes can use them. However, one should generally avoid sending shared objects to other processes using pipes or queues. Instead you should arrange the program so that a process which @@ -3028,7 +3166,7 @@ Explicitly pass resources to child processes Beware of replacing :data:`sys.stdin` with a "file like object" - :mod:`multiprocessing` originally unconditionally called:: + :mod:`!multiprocessing` originally unconditionally called:: os.close(sys.stdin.fileno()) @@ -3070,10 +3208,10 @@ start method. More picklability - Ensure that all arguments to :meth:`Process.__init__` are picklable. - Also, if you subclass :class:`~multiprocessing.Process` then make sure that - instances will be picklable when the :meth:`Process.start - ` method is called. + Ensure that all arguments to :class:`~multiprocessing.Process` are + picklable. Also, if you subclass ``Process.__init__``, you must make sure + that instances will be picklable when the + :meth:`Process.start ` method is called. Global variables diff --git a/Doc/library/netrc.rst b/Doc/library/netrc.rst index 74c97e8c9a9759..3fbd2b57426ebe 100644 --- a/Doc/library/netrc.rst +++ b/Doc/library/netrc.rst @@ -4,9 +4,6 @@ .. module:: netrc :synopsis: Loading of .netrc files. -.. moduleauthor:: Eric S. Raymond -.. sectionauthor:: Eric S. Raymond - **Source code:** :source:`Lib/netrc.py` -------------- diff --git a/Doc/library/numbers.rst b/Doc/library/numbers.rst index 681d0b76f2a14b..57b35017072c97 100644 --- a/Doc/library/numbers.rst +++ b/Doc/library/numbers.rst @@ -69,11 +69,11 @@ The numeric tower .. attribute:: numerator - Abstract. + Abstract. The numerator of this rational number. .. attribute:: denominator - Abstract. + Abstract. The denominator of this rational number. .. class:: Integral diff --git a/Doc/library/numeric.rst b/Doc/library/numeric.rst index 7c76a479d73b26..a2109a29bfb6cc 100644 --- a/Doc/library/numeric.rst +++ b/Doc/library/numeric.rst @@ -19,6 +19,7 @@ The following modules are documented in this chapter: numbers.rst math.rst + math.integer.rst cmath.rst decimal.rst fractions.rst diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst index e8e71068dd99eb..3d1c8cda13be38 100644 --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -4,8 +4,6 @@ .. module:: operator :synopsis: Functions corresponding to the standard operators. -.. sectionauthor:: Skip Montanaro - **Source code:** :source:`Lib/operator.py` .. testsetup:: @@ -15,7 +13,7 @@ -------------- -The :mod:`operator` module exports a set of efficient functions corresponding to +The :mod:`!operator` module exports a set of efficient functions corresponding to the intrinsic operators of Python. For example, ``operator.add(x, y)`` is equivalent to the expression ``x+y``. Many function names are those used for special methods, without the double underscores. For backward compatibility, @@ -112,7 +110,7 @@ The mathematical and bitwise operations are the most numerous: .. function:: and_(a, b) __and__(a, b) - Return the bitwise and of *a* and *b*. + Return ``a & b``. .. function:: floordiv(a, b) @@ -136,13 +134,13 @@ The mathematical and bitwise operations are the most numerous: __inv__(obj) __invert__(obj) - Return the bitwise inverse of the number *obj*. This is equivalent to ``~obj``. + Return ``~obj``. .. function:: lshift(a, b) __lshift__(a, b) - Return *a* shifted left by *b*. + Return ``a << b``. .. function:: mod(a, b) @@ -154,7 +152,7 @@ The mathematical and bitwise operations are the most numerous: .. function:: mul(a, b) __mul__(a, b) - Return ``a * b``, for *a* and *b* numbers. + Return ``a * b``. .. function:: matmul(a, b) @@ -174,25 +172,25 @@ The mathematical and bitwise operations are the most numerous: .. function:: or_(a, b) __or__(a, b) - Return the bitwise or of *a* and *b*. + Return ``a | b``. .. function:: pos(obj) __pos__(obj) - Return *obj* positive (``+obj``). + Return ``+obj``. .. function:: pow(a, b) __pow__(a, b) - Return ``a ** b``, for *a* and *b* numbers. + Return ``a ** b``. .. function:: rshift(a, b) __rshift__(a, b) - Return *a* shifted right by *b*. + Return ``a >> b``. .. function:: sub(a, b) @@ -211,7 +209,7 @@ The mathematical and bitwise operations are the most numerous: .. function:: xor(a, b) __xor__(a, b) - Return the bitwise exclusive or of *a* and *b*. + Return ``a ^ b``. Operations which work with sequences (some of them with mappings too) include: @@ -275,7 +273,7 @@ The following operation works with callables: .. versionadded:: 3.11 -The :mod:`operator` module also defines tools for generalized attribute and item +The :mod:`!operator` module also defines tools for generalized attribute and item lookups. These are useful for making fast field extractors as arguments for :func:`map`, :func:`sorted`, :meth:`itertools.groupby`, or other functions that expect a function argument. @@ -390,7 +388,7 @@ Mapping Operators to Functions ------------------------------ This table shows how abstract operations correspond to operator symbols in the -Python syntax and the functions in the :mod:`operator` module. +Python syntax and the functions in the :mod:`!operator` module. +-----------------------+-------------------------+---------------------------------------+ | Operation | Syntax | Function | @@ -405,13 +403,18 @@ Python syntax and the functions in the :mod:`operator` module. +-----------------------+-------------------------+---------------------------------------+ | Division | ``a // b`` | ``floordiv(a, b)`` | +-----------------------+-------------------------+---------------------------------------+ -| Bitwise And | ``a & b`` | ``and_(a, b)`` | +| Bitwise And, or | ``a & b`` | ``and_(a, b)`` | +| Intersection | | | +-----------------------+-------------------------+---------------------------------------+ -| Bitwise Exclusive Or | ``a ^ b`` | ``xor(a, b)`` | +| Bitwise Exclusive Or, | ``a ^ b`` | ``xor(a, b)`` | +| or Symmetric | | | +| Difference | | | +-----------------------+-------------------------+---------------------------------------+ -| Bitwise Inversion | ``~ a`` | ``invert(a)`` | +| Bitwise Inversion, or | ``~ a`` | ``invert(a)`` | +| Complement | | | +-----------------------+-------------------------+---------------------------------------+ -| Bitwise Or | ``a | b`` | ``or_(a, b)`` | +| Bitwise Or, or | ``a | b`` | ``or_(a, b)`` | +| Union | | | +-----------------------+-------------------------+---------------------------------------+ | Exponentiation | ``a ** b`` | ``pow(a, b)`` | +-----------------------+-------------------------+---------------------------------------+ diff --git a/Doc/library/optparse.rst b/Doc/library/optparse.rst index ff327cf9162a8c..905212965bd70f 100644 --- a/Doc/library/optparse.rst +++ b/Doc/library/optparse.rst @@ -4,9 +4,6 @@ .. module:: optparse :synopsis: Command-line option parsing library. -.. moduleauthor:: Greg Ward -.. sectionauthor:: Greg Ward - **Source code:** :source:`Lib/optparse.py` -------------- @@ -20,7 +17,7 @@ The standard library includes three argument parsing libraries: * :mod:`getopt`: a module that closely mirrors the procedural C ``getopt`` API. Included in the standard library since before the initial Python 1.0 release. -* :mod:`optparse`: a declarative replacement for ``getopt`` that +* :mod:`!optparse`: a declarative replacement for ``getopt`` that provides equivalent functionality without requiring each application to implement its own procedural option parsing logic. Included in the standard library since the Python 2.3 release. @@ -37,10 +34,10 @@ the highest level of baseline functionality with the least application level cod However, it also serves a niche use case as a tool for prototyping and testing command line argument handling in ``getopt``-based C applications. -:mod:`optparse` should be considered as an alternative to :mod:`argparse` in the +:mod:`!optparse` should be considered as an alternative to :mod:`argparse` in the following cases: -* an application is already using :mod:`optparse` and doesn't want to risk the +* an application is already using :mod:`!optparse` and doesn't want to risk the subtle behavioural changes that may arise when migrating to :mod:`argparse` * the application requires additional control over the way options and positional parameters are interleaved on the command line (including @@ -55,7 +52,7 @@ following cases: behavior which ``argparse`` does not support, but which can be implemented in terms of the lower level interface offered by ``optparse`` -These considerations also mean that :mod:`optparse` is likely to provide a +These considerations also mean that :mod:`!optparse` is likely to provide a better foundation for library authors writing third party command line argument processing libraries. @@ -126,15 +123,15 @@ application use case. Introduction ------------ -:mod:`optparse` is a more convenient, flexible, and powerful library for parsing +:mod:`!optparse` is a more convenient, flexible, and powerful library for parsing command-line options than the minimalist :mod:`getopt` module. -:mod:`optparse` uses a more declarative style of command-line parsing: +:mod:`!optparse` uses a more declarative style of command-line parsing: you create an instance of :class:`OptionParser`, populate it with options, and parse the command line. -:mod:`optparse` allows users to specify options in the conventional +:mod:`!optparse` allows users to specify options in the conventional GNU/POSIX syntax, and additionally generates usage and help messages for you. -Here's an example of using :mod:`optparse` in a simple script:: +Here's an example of using :mod:`!optparse` in a simple script:: from optparse import OptionParser ... @@ -152,11 +149,11 @@ on the command-line, for example:: --file=outfile -q -As it parses the command line, :mod:`optparse` sets attributes of the +As it parses the command line, :mod:`!optparse` sets attributes of the ``options`` object returned by :meth:`~OptionParser.parse_args` based on user-supplied command-line values. When :meth:`~OptionParser.parse_args` returns from parsing this command line, ``options.filename`` will be ``"outfile"`` and ``options.verbose`` will be -``False``. :mod:`optparse` supports both long and short options, allows short +``False``. :mod:`!optparse` supports both long and short options, allows short options to be merged together, and allows options to be associated with their arguments in a variety of ways. Thus, the following command lines are all equivalent to the above example:: @@ -171,7 +168,7 @@ Additionally, users can run one of the following :: -h --help -and :mod:`optparse` will print out a brief summary of your script's options: +and :mod:`!optparse` will print out a brief summary of your script's options: .. code-block:: text @@ -191,7 +188,7 @@ where the value of *yourscript* is determined at runtime (normally from Background ---------- -:mod:`optparse` was explicitly designed to encourage the creation of programs +:mod:`!optparse` was explicitly designed to encourage the creation of programs with straightforward command-line interfaces that follow the conventions established by the :c:func:`!getopt` family of functions available to C developers. To that end, it supports only the most common command-line syntax and semantics @@ -223,7 +220,7 @@ option options to be merged into a single argument, e.g. ``-x -F`` is equivalent to ``-xF``. The GNU project introduced ``--`` followed by a series of hyphen-separated words, e.g. ``--file`` or ``--dry-run``. These are the - only two option syntaxes provided by :mod:`optparse`. + only two option syntaxes provided by :mod:`!optparse`. Some other option syntaxes that the world has seen include: @@ -240,7 +237,7 @@ option * a slash followed by a letter, or a few letters, or a word, e.g. ``/f``, ``/file`` - These option syntaxes are not supported by :mod:`optparse`, and they never + These option syntaxes are not supported by :mod:`!optparse`, and they never will be. This is deliberate: the first three are non-standard on any environment, and the last only makes sense if you're exclusively targeting Windows or certain legacy platforms (e.g. VMS, MS-DOS). @@ -248,7 +245,7 @@ option option argument an argument that follows an option, is closely associated with that option, and is consumed from the argument list when that option is. With - :mod:`optparse`, option arguments may either be in a separate argument from + :mod:`!optparse`, option arguments may either be in a separate argument from their option: .. code-block:: text @@ -268,7 +265,7 @@ option argument will take an argument if they see it, and won't if they don't. This is somewhat controversial, because it makes parsing ambiguous: if ``-a`` takes an optional argument and ``-b`` is another option entirely, how do we - interpret ``-ab``? Because of this ambiguity, :mod:`optparse` does not + interpret ``-ab``? Because of this ambiguity, :mod:`!optparse` does not support this feature. positional argument @@ -278,7 +275,7 @@ positional argument required option an option that must be supplied on the command-line; note that the phrase - "required option" is self-contradictory in English. :mod:`optparse` doesn't + "required option" is self-contradictory in English. :mod:`!optparse` doesn't prevent you from implementing required options, but doesn't give you much help at it either. @@ -357,9 +354,9 @@ too many options can overwhelm users and make your code much harder to maintain. Tutorial -------- -While :mod:`optparse` is quite flexible and powerful, it's also straightforward +While :mod:`!optparse` is quite flexible and powerful, it's also straightforward to use in most cases. This section covers the code patterns that are common to -any :mod:`optparse`\ -based program. +any :mod:`!optparse`\ -based program. First, you need to import the OptionParser class; then, early in the main program, create an OptionParser instance:: @@ -374,7 +371,7 @@ Then you can start defining options. The basic syntax is:: attr=value, ...) Each option has one or more option strings, such as ``-f`` or ``--file``, -and several option attributes that tell :mod:`optparse` what to expect and what +and several option attributes that tell :mod:`!optparse` what to expect and what to do when it encounters that option on the command line. Typically, each option will have one short option string and one long option @@ -389,10 +386,10 @@ string overall. The option strings passed to :meth:`OptionParser.add_option` are effectively labels for the option defined by that call. For brevity, we will frequently refer to -*encountering an option* on the command line; in reality, :mod:`optparse` +*encountering an option* on the command line; in reality, :mod:`!optparse` encounters *option strings* and looks up options from them. -Once all of your options are defined, instruct :mod:`optparse` to parse your +Once all of your options are defined, instruct :mod:`!optparse` to parse your program's command line:: (options, args) = parser.parse_args() @@ -420,14 +417,14 @@ most fundamental. Understanding option actions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Actions tell :mod:`optparse` what to do when it encounters an option on the -command line. There is a fixed set of actions hard-coded into :mod:`optparse`; +Actions tell :mod:`!optparse` what to do when it encounters an option on the +command line. There is a fixed set of actions hard-coded into :mod:`!optparse`; adding new actions is an advanced topic covered in section -:ref:`optparse-extending-optparse`. Most actions tell :mod:`optparse` to store +:ref:`optparse-extending-optparse`. Most actions tell :mod:`!optparse` to store a value in some variable---for example, take a string from the command line and store it in an attribute of ``options``. -If you don't specify an option action, :mod:`optparse` defaults to ``store``. +If you don't specify an option action, :mod:`!optparse` defaults to ``store``. .. _optparse-store-action: @@ -435,7 +432,7 @@ If you don't specify an option action, :mod:`optparse` defaults to ``store``. The store action ^^^^^^^^^^^^^^^^ -The most common option action is ``store``, which tells :mod:`optparse` to take +The most common option action is ``store``, which tells :mod:`!optparse` to take the next argument (or the remainder of the current argument), ensure that it is of the correct type, and store it to your chosen destination. @@ -444,16 +441,16 @@ For example:: parser.add_option("-f", "--file", action="store", type="string", dest="filename") -Now let's make up a fake command line and ask :mod:`optparse` to parse it:: +Now let's make up a fake command line and ask :mod:`!optparse` to parse it:: args = ["-f", "foo.txt"] (options, args) = parser.parse_args(args) -When :mod:`optparse` sees the option string ``-f``, it consumes the next +When :mod:`!optparse` sees the option string ``-f``, it consumes the next argument, ``foo.txt``, and stores it in ``options.filename``. So, after this call to :meth:`~OptionParser.parse_args`, ``options.filename`` is ``"foo.txt"``. -Some other option types supported by :mod:`optparse` are ``int`` and ``float``. +Some other option types supported by :mod:`!optparse` are ``int`` and ``float``. Here's an option that expects an integer argument:: parser.add_option("-n", type="int", dest="num") @@ -470,19 +467,19 @@ right up against the option: since ``-n42`` (one argument) is equivalent to will print ``42``. -If you don't specify a type, :mod:`optparse` assumes ``string``. Combined with +If you don't specify a type, :mod:`!optparse` assumes ``string``. Combined with the fact that the default action is ``store``, that means our first example can be a lot shorter:: parser.add_option("-f", "--file", dest="filename") -If you don't supply a destination, :mod:`optparse` figures out a sensible +If you don't supply a destination, :mod:`!optparse` figures out a sensible default from the option strings: if the first long option string is ``--foo-bar``, then the default destination is ``foo_bar``. If there are no -long option strings, :mod:`optparse` looks at the first short option string: the +long option strings, :mod:`!optparse` looks at the first short option string: the default destination for ``-f`` is ``f``. -:mod:`optparse` also includes the built-in ``complex`` type. Adding +:mod:`!optparse` also includes the built-in ``complex`` type. Adding types is covered in section :ref:`optparse-extending-optparse`. @@ -492,7 +489,7 @@ Handling boolean (flag) options ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Flag options---set a variable to true or false when a particular option is -seen---are quite common. :mod:`optparse` supports them with two separate actions, +seen---are quite common. :mod:`!optparse` supports them with two separate actions, ``store_true`` and ``store_false``. For example, you might have a ``verbose`` flag that is turned on with ``-v`` and off with ``-q``:: @@ -503,7 +500,7 @@ Here we have two different options with the same destination, which is perfectly OK. (It just means you have to be a bit careful when setting default values---see below.) -When :mod:`optparse` encounters ``-v`` on the command line, it sets +When :mod:`!optparse` encounters ``-v`` on the command line, it sets ``options.verbose`` to ``True``; when it encounters ``-q``, ``options.verbose`` is set to ``False``. @@ -513,7 +510,7 @@ When :mod:`optparse` encounters ``-v`` on the command line, it sets Other actions ^^^^^^^^^^^^^ -Some other actions supported by :mod:`optparse` are: +Some other actions supported by :mod:`!optparse` are: ``"store_const"`` store a constant value, pre-set via :attr:`Option.const` @@ -539,11 +536,11 @@ Default values All of the above examples involve setting some variable (the "destination") when certain command-line options are seen. What happens if those options are never seen? Since we didn't supply any defaults, they are all set to ``None``. This -is usually fine, but sometimes you want more control. :mod:`optparse` lets you +is usually fine, but sometimes you want more control. :mod:`!optparse` lets you supply a default value for each destination, which is assigned before the command line is parsed. -First, consider the verbose/quiet example. If we want :mod:`optparse` to set +First, consider the verbose/quiet example. If we want :mod:`!optparse` to set ``verbose`` to ``True`` unless ``-q`` is seen, then we can do this:: parser.add_option("-v", action="store_true", dest="verbose", default=True) @@ -582,7 +579,7 @@ values, not both. Generating help ^^^^^^^^^^^^^^^ -:mod:`optparse`'s ability to generate help and usage text automatically is +:mod:`!optparse`'s ability to generate help and usage text automatically is useful for creating user-friendly command-line interfaces. All you have to do is supply a :attr:`~Option.help` value for each option, and optionally a short usage message for your whole program. Here's an OptionParser populated with @@ -603,7 +600,7 @@ user-friendly (documented) options:: help="interaction mode: novice, intermediate, " "or expert [default: %default]") -If :mod:`optparse` encounters either ``-h`` or ``--help`` on the +If :mod:`!optparse` encounters either ``-h`` or ``--help`` on the command-line, or if you just call :meth:`parser.print_help`, it prints the following to standard output: @@ -620,26 +617,26 @@ following to standard output: -m MODE, --mode=MODE interaction mode: novice, intermediate, or expert [default: intermediate] -(If the help output is triggered by a help option, :mod:`optparse` exits after +(If the help output is triggered by a help option, :mod:`!optparse` exits after printing the help text.) -There's a lot going on here to help :mod:`optparse` generate the best possible +There's a lot going on here to help :mod:`!optparse` generate the best possible help message: * the script defines its own usage message:: usage = "usage: %prog [options] arg1 arg2" - :mod:`optparse` expands ``%prog`` in the usage string to the name of the + :mod:`!optparse` expands ``%prog`` in the usage string to the name of the current program, i.e. ``os.path.basename(sys.argv[0])``. The expanded string is then printed before the detailed option help. - If you don't supply a usage string, :mod:`optparse` uses a bland but sensible + If you don't supply a usage string, :mod:`!optparse` uses a bland but sensible default: ``"Usage: %prog [options]"``, which is fine if your script doesn't take any positional arguments. * every option defines a help string, and doesn't worry about - line-wrapping---\ :mod:`optparse` takes care of wrapping lines and making + line-wrapping---\ :mod:`!optparse` takes care of wrapping lines and making the help output look good. * options that take a value indicate this fact in their automatically generated @@ -649,7 +646,7 @@ help message: Here, "MODE" is called the meta-variable: it stands for the argument that the user is expected to supply to ``-m``/``--mode``. By default, - :mod:`optparse` converts the destination variable name to uppercase and uses + :mod:`!optparse` converts the destination variable name to uppercase and uses that for the meta-variable. Sometimes, that's not what you want---for example, the ``--filename`` option explicitly sets ``metavar="FILE"``, resulting in this automatically generated option description:: @@ -663,7 +660,7 @@ help message: way to make your help text a lot clearer and more useful for end users. * options that have a default value can include ``%default`` in the help - string---\ :mod:`optparse` will replace it with :func:`str` of the option's + string---\ :mod:`!optparse` will replace it with :func:`str` of the option's default value. If an option has no default value (or the default value is ``None``), ``%default`` expands to ``none``. @@ -779,14 +776,14 @@ option groups is: Printing a version string ^^^^^^^^^^^^^^^^^^^^^^^^^ -Similar to the brief usage string, :mod:`optparse` can also print a version +Similar to the brief usage string, :mod:`!optparse` can also print a version string for your program. You have to supply the string as the ``version`` argument to OptionParser:: parser = OptionParser(usage="%prog [-f] [-q]", version="%prog 1.0") ``%prog`` is expanded just like it is in ``usage``. Apart from that, -``version`` can contain anything you like. When you supply it, :mod:`optparse` +``version`` can contain anything you like. When you supply it, :mod:`!optparse` automatically adds a ``--version`` option to your parser. If it encounters this option on the command line, it expands your ``version`` string (by replacing ``%prog``), prints it to stdout, and exits. @@ -815,10 +812,10 @@ The following two methods can be used to print and get the ``version`` string: .. _optparse-how-optparse-handles-errors: -How :mod:`optparse` handles errors -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +How :mod:`!optparse` handles errors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -There are two broad classes of errors that :mod:`optparse` has to worry about: +There are two broad classes of errors that :mod:`!optparse` has to worry about: programmer errors and user errors. Programmer errors are usually erroneous calls to :func:`OptionParser.add_option`, e.g. invalid option strings, unknown option attributes, missing option attributes, etc. These are dealt with in the @@ -826,7 +823,7 @@ usual way: raise an exception (either :exc:`optparse.OptionError` or :exc:`TypeError`) and let the program crash. Handling user errors is much more important, since they are guaranteed to happen -no matter how stable your code is. :mod:`optparse` can automatically detect +no matter how stable your code is. :mod:`!optparse` can automatically detect some user errors, such as bad option arguments (passing ``-n 4x`` where ``-n`` takes an integer argument), missing arguments (``-n`` at the end of the command line, where ``-n`` takes an argument of any type). Also, @@ -838,7 +835,7 @@ condition:: if options.a and options.b: parser.error("options -a and -b are mutually exclusive") -In either case, :mod:`optparse` handles the error the same way: it prints the +In either case, :mod:`!optparse` handles the error the same way: it prints the program's usage message and an error message to standard error and exits with error status 2. @@ -861,11 +858,11 @@ Or, where the user fails to pass a value at all: foo: error: -n option requires an argument -:mod:`optparse`\ -generated error messages take care always to mention the +:mod:`!optparse`\ -generated error messages take care always to mention the option involved in the error; be sure to do the same when calling :func:`OptionParser.error` from your application code. -If :mod:`optparse`'s default error-handling behaviour does not suit your needs, +If :mod:`!optparse`'s default error-handling behaviour does not suit your needs, you'll need to subclass OptionParser and override its :meth:`~OptionParser.exit` and/or :meth:`~OptionParser.error` methods. @@ -875,7 +872,7 @@ and/or :meth:`~OptionParser.error` methods. Putting it all together ^^^^^^^^^^^^^^^^^^^^^^^ -Here's what :mod:`optparse`\ -based scripts usually look like:: +Here's what :mod:`!optparse`\ -based scripts usually look like:: from optparse import OptionParser ... @@ -911,7 +908,7 @@ Reference Guide Creating the parser ^^^^^^^^^^^^^^^^^^^ -The first step in using :mod:`optparse` is to create an OptionParser instance. +The first step in using :mod:`!optparse` is to create an OptionParser instance. .. class:: OptionParser(...) @@ -921,7 +918,7 @@ The first step in using :mod:`optparse` is to create an OptionParser instance. ``usage`` (default: ``"%prog [options]"``) The usage summary to print when your program is run incorrectly or with a - help option. When :mod:`optparse` prints the usage string, it expands + help option. When :mod:`!optparse` prints the usage string, it expands ``%prog`` to ``os.path.basename(sys.argv[0])`` (or to ``prog`` if you passed that keyword argument). To suppress a usage message, pass the special value :const:`optparse.SUPPRESS_USAGE`. @@ -938,7 +935,7 @@ The first step in using :mod:`optparse` is to create an OptionParser instance. ``version`` (default: ``None``) A version string to print when the user supplies a version option. If you - supply a true value for ``version``, :mod:`optparse` automatically adds a + supply a true value for ``version``, :mod:`!optparse` automatically adds a version option with the single option string ``--version``. The substring ``%prog`` is expanded the same as for ``usage``. @@ -949,17 +946,17 @@ The first step in using :mod:`optparse` is to create an OptionParser instance. ``description`` (default: ``None``) A paragraph of text giving a brief overview of your program. - :mod:`optparse` reformats this paragraph to fit the current terminal width + :mod:`!optparse` reformats this paragraph to fit the current terminal width and prints it when the user requests help (after ``usage``, but before the list of options). ``formatter`` (default: a new :class:`IndentedHelpFormatter`) An instance of optparse.HelpFormatter that will be used for printing help - text. :mod:`optparse` provides two concrete classes for this purpose: + text. :mod:`!optparse` provides two concrete classes for this purpose: IndentedHelpFormatter and TitledHelpFormatter. ``add_help_option`` (default: ``True``) - If true, :mod:`optparse` will add a help option (with option strings ``-h`` + If true, :mod:`!optparse` will add a help option (with option strings ``-h`` and ``--help``) to the parser. ``prog`` @@ -997,7 +994,7 @@ the OptionParser constructor, as in:: (:func:`make_option` is a factory function for creating Option instances; currently it is an alias for the Option constructor. A future version of -:mod:`optparse` may split Option into several classes, and :func:`make_option` +:mod:`!optparse` may split Option into several classes, and :func:`make_option` will pick the right class to instantiate. Do not instantiate Option directly.) @@ -1027,12 +1024,12 @@ The canonical way to create an :class:`Option` instance is with the The keyword arguments define attributes of the new Option object. The most important option attribute is :attr:`~Option.action`, and it largely determines which other attributes are relevant or required. If you pass - irrelevant option attributes, or fail to pass required ones, :mod:`optparse` + irrelevant option attributes, or fail to pass required ones, :mod:`!optparse` raises an :exc:`OptionError` exception explaining your mistake. - An option's *action* determines what :mod:`optparse` does when it encounters + An option's *action* determines what :mod:`!optparse` does when it encounters this option on the command-line. The standard option actions hard-coded into - :mod:`optparse` are: + :mod:`!optparse` are: ``"store"`` store this option's argument (default) @@ -1066,7 +1063,7 @@ The canonical way to create an :class:`Option` instance is with the attributes; see :ref:`optparse-standard-option-actions`.) As you can see, most actions involve storing or updating a value somewhere. -:mod:`optparse` always creates a special object for this, conventionally called +:mod:`!optparse` always creates a special object for this, conventionally called ``options``, which is an instance of :class:`optparse.Values`. .. class:: Values @@ -1084,7 +1081,7 @@ For example, when you call :: parser.parse_args() -one of the first things :mod:`optparse` does is create the ``options`` object:: +one of the first things :mod:`!optparse` does is create the ``options`` object:: options = Values() @@ -1099,7 +1096,7 @@ and the command-line being parsed includes any of the following:: --file=foo --file foo -then :mod:`optparse`, on seeing this option, will do the equivalent of :: +then :mod:`!optparse`, on seeing this option, will do the equivalent of :: options.filename = "foo" @@ -1124,13 +1121,13 @@ Option attributes The following option attributes may be passed as keyword arguments to :meth:`OptionParser.add_option`. If you pass an option attribute that is not relevant to a particular option, or fail to pass a required option attribute, -:mod:`optparse` raises :exc:`OptionError`. +:mod:`!optparse` raises :exc:`OptionError`. .. attribute:: Option.action (default: ``"store"``) - Determines :mod:`optparse`'s behaviour when this option is seen on the + Determines :mod:`!optparse`'s behaviour when this option is seen on the command line; the available options are documented :ref:`here `. @@ -1147,8 +1144,8 @@ relevant to a particular option, or fail to pass a required option attribute, (default: derived from option strings) If the option's action implies writing or modifying a value somewhere, this - tells :mod:`optparse` where to write it: :attr:`~Option.dest` names an - attribute of the ``options`` object that :mod:`optparse` builds as it parses + tells :mod:`!optparse` where to write it: :attr:`~Option.dest` names an + attribute of the ``options`` object that :mod:`!optparse` builds as it parses the command line. .. attribute:: Option.default @@ -1161,7 +1158,7 @@ relevant to a particular option, or fail to pass a required option attribute, (default: 1) How many arguments of type :attr:`~Option.type` should be consumed when this - option is seen. If > 1, :mod:`optparse` will store a tuple of values to + option is seen. If > 1, :mod:`!optparse` will store a tuple of values to :attr:`~Option.dest`. .. attribute:: Option.const @@ -1207,7 +1204,7 @@ Standard option actions The various option actions all have slightly different requirements and effects. Most actions have several relevant option attributes which you may specify to -guide :mod:`optparse`'s behaviour; a few have required attributes, which you +guide :mod:`!optparse`'s behaviour; a few have required attributes, which you must specify for any option using that action. * ``"store"`` [relevant: :attr:`~Option.type`, :attr:`~Option.dest`, @@ -1225,9 +1222,9 @@ must specify for any option using that action. If :attr:`~Option.type` is not supplied, it defaults to ``"string"``. - If :attr:`~Option.dest` is not supplied, :mod:`optparse` derives a destination + If :attr:`~Option.dest` is not supplied, :mod:`!optparse` derives a destination from the first long option string (e.g., ``--foo-bar`` implies - ``foo_bar``). If there are no long option strings, :mod:`optparse` derives a + ``foo_bar``). If there are no long option strings, :mod:`!optparse` derives a destination from the first short option string (e.g., ``-f`` implies ``f``). Example:: @@ -1239,7 +1236,7 @@ must specify for any option using that action. -f foo.txt -p 1 -3.5 4 -fbar.txt - :mod:`optparse` will set :: + :mod:`!optparse` will set :: options.f = "foo.txt" options.point = (1.0, -3.5, 4.0) @@ -1259,7 +1256,7 @@ must specify for any option using that action. parser.add_option("--noisy", action="store_const", const=2, dest="verbose") - If ``--noisy`` is seen, :mod:`optparse` will set :: + If ``--noisy`` is seen, :mod:`!optparse` will set :: options.verbose = 2 @@ -1282,7 +1279,7 @@ must specify for any option using that action. The option must be followed by an argument, which is appended to the list in :attr:`~Option.dest`. If no default value for :attr:`~Option.dest` is - supplied, an empty list is automatically created when :mod:`optparse` first + supplied, an empty list is automatically created when :mod:`!optparse` first encounters this option on the command-line. If :attr:`~Option.nargs` > 1, multiple arguments are consumed, and a tuple of length :attr:`~Option.nargs` is appended to :attr:`~Option.dest`. @@ -1294,7 +1291,7 @@ must specify for any option using that action. parser.add_option("-t", "--tracks", action="append", type="int") - If ``-t3`` is seen on the command-line, :mod:`optparse` does the equivalent + If ``-t3`` is seen on the command-line, :mod:`!optparse` does the equivalent of:: options.tracks = [] @@ -1333,7 +1330,7 @@ must specify for any option using that action. parser.add_option("-v", action="count", dest="verbosity") - The first time ``-v`` is seen on the command line, :mod:`optparse` does the + The first time ``-v`` is seen on the command line, :mod:`!optparse` does the equivalent of:: options.verbosity = 0 @@ -1364,7 +1361,7 @@ must specify for any option using that action. listed in the help message. To omit an option entirely, use the special value :const:`optparse.SUPPRESS_HELP`. - :mod:`optparse` automatically adds a :attr:`~Option.help` option to all + :mod:`!optparse` automatically adds a :attr:`~Option.help` option to all OptionParsers, so you do not normally need to create one. Example:: @@ -1382,7 +1379,7 @@ must specify for any option using that action. help="Input file to read data from") parser.add_option("--secret", help=SUPPRESS_HELP) - If :mod:`optparse` sees either ``-h`` or ``--help`` on the command line, + If :mod:`!optparse` sees either ``-h`` or ``--help`` on the command line, it will print something like the following help message to stdout (assuming ``sys.argv[0]`` is ``"foo.py"``): @@ -1395,7 +1392,7 @@ must specify for any option using that action. -v Be moderately verbose --file=FILENAME Input file to read data from - After printing the help message, :mod:`optparse` terminates your process with + After printing the help message, :mod:`!optparse` terminates your process with ``sys.exit(0)``. * ``"version"`` @@ -1405,7 +1402,7 @@ must specify for any option using that action. ``print_version()`` method of OptionParser. Generally only relevant if the ``version`` argument is supplied to the OptionParser constructor. As with :attr:`~Option.help` options, you will rarely create ``version`` options, - since :mod:`optparse` automatically adds them when needed. + since :mod:`!optparse` automatically adds them when needed. .. _optparse-standard-option-types: @@ -1413,7 +1410,7 @@ must specify for any option using that action. Standard option types ^^^^^^^^^^^^^^^^^^^^^ -:mod:`optparse` has five built-in option types: ``"string"``, ``"int"``, +:mod:`!optparse` has five built-in option types: ``"string"``, ``"int"``, ``"choice"``, ``"float"`` and ``"complex"``. If you need to add new option types, see section :ref:`optparse-extending-optparse`. @@ -1432,7 +1429,7 @@ Integer arguments (type ``"int"``) are parsed as follows: The conversion is done by calling :func:`int` with the appropriate base (2, 8, -10, or 16). If this fails, so will :mod:`optparse`, although with a more useful +10, or 16). If this fails, so will :mod:`!optparse`, although with a more useful error message. ``"float"`` and ``"complex"`` option arguments are converted directly with @@ -1471,7 +1468,7 @@ The whole point of creating and populating an OptionParser is to call its ``options`` the same object that was passed in as *values*, or the ``optparse.Values`` - instance created by :mod:`optparse` + instance created by :mod:`!optparse` ``args`` the leftover positional arguments after all options have been processed @@ -1499,7 +1496,7 @@ provides several methods to help you out: .. method:: OptionParser.disable_interspersed_args() Set parsing to stop on the first non-option. For example, if ``-a`` and - ``-b`` are both simple options that take no arguments, :mod:`optparse` + ``-b`` are both simple options that take no arguments, :mod:`!optparse` normally accepts this syntax:: prog -a arg1 -b arg2 @@ -1554,7 +1551,7 @@ strings:: (This is particularly true if you've defined your own OptionParser subclass with some standard options.) -Every time you add an option, :mod:`optparse` checks for conflicts with existing +Every time you add an option, :mod:`!optparse` checks for conflicts with existing options. If it finds any, it invokes the current conflict-handling mechanism. You can set the conflict-handling mechanism either in the constructor:: @@ -1581,7 +1578,7 @@ intelligently and add conflicting options to it:: parser.add_option("-n", "--dry-run", ..., help="do no harm") parser.add_option("-n", "--noisy", ..., help="be noisy") -At this point, :mod:`optparse` detects that a previously added option is already +At this point, :mod:`!optparse` detects that a previously added option is already using the ``-n`` option string. Since ``conflict_handler`` is ``"resolve"``, it resolves the situation by removing ``-n`` from the earlier option's list of option strings. Now ``--dry-run`` is the only way for the user to activate @@ -1594,14 +1591,14 @@ that option. If the user asks for help, the help message will reflect that:: It's possible to whittle away the option strings for a previously added option until there are none left, and the user has no way of invoking that option from -the command-line. In that case, :mod:`optparse` removes that option completely, +the command-line. In that case, :mod:`!optparse` removes that option completely, so it doesn't show up in help text or anywhere else. Carrying on with our existing OptionParser:: parser.add_option("--dry-run", ..., help="new dry-run option") At this point, the original ``-n``/``--dry-run`` option is no longer -accessible, so :mod:`optparse` removes it, leaving this help text:: +accessible, so :mod:`!optparse` removes it, leaving this help text:: Options: ... @@ -1676,9 +1673,9 @@ OptionParser supports several other public methods: Option Callbacks ---------------- -When :mod:`optparse`'s built-in actions and types aren't quite enough for your -needs, you have two choices: extend :mod:`optparse` or define a callback option. -Extending :mod:`optparse` is more general, but overkill for a lot of simple +When :mod:`!optparse`'s built-in actions and types aren't quite enough for your +needs, you have two choices: extend :mod:`!optparse` or define a callback option. +Extending :mod:`!optparse` is more general, but overkill for a lot of simple cases. Quite often a simple callback is all you need. There are two steps to defining a callback option: @@ -1702,14 +1699,14 @@ only option attribute you must specify is ``callback``, the function to call:: ``callback`` is a function (or other callable object), so you must have already defined ``my_callback()`` when you create this callback option. In this simple -case, :mod:`optparse` doesn't even know if ``-c`` takes any arguments, +case, :mod:`!optparse` doesn't even know if ``-c`` takes any arguments, which usually means that the option takes no arguments---the mere presence of ``-c`` on the command-line is all it needs to know. In some circumstances, though, you might want your callback to consume an arbitrary number of command-line arguments. This is where writing callbacks gets tricky; it's covered later in this section. -:mod:`optparse` always passes four particular arguments to your callback, and it +:mod:`!optparse` always passes four particular arguments to your callback, and it will only pass additional arguments if you specify them via :attr:`~Option.callback_args` and :attr:`~Option.callback_kwargs`. Thus, the minimal callback function signature is:: @@ -1723,12 +1720,12 @@ callback option: :attr:`~Option.type` has its usual meaning: as with the ``"store"`` or ``"append"`` actions, it - instructs :mod:`optparse` to consume one argument and convert it to + instructs :mod:`!optparse` to consume one argument and convert it to :attr:`~Option.type`. Rather than storing the converted value(s) anywhere, - though, :mod:`optparse` passes it to your callback function. + though, :mod:`!optparse` passes it to your callback function. :attr:`~Option.nargs` - also has its usual meaning: if it is supplied and > 1, :mod:`optparse` will + also has its usual meaning: if it is supplied and > 1, :mod:`!optparse` will consume :attr:`~Option.nargs` arguments, each of which must be convertible to :attr:`~Option.type`. It then passes a tuple of converted values to your callback. @@ -1762,7 +1759,7 @@ where ``"--foobar"``.) ``value`` - is the argument to this option seen on the command-line. :mod:`optparse` will + is the argument to this option seen on the command-line. :mod:`!optparse` will only expect an argument if :attr:`~Option.type` is set; the type of ``value`` will be the type implied by the option's type. If :attr:`~Option.type` for this option is ``None`` (no argument expected), then ``value`` will be ``None``. If :attr:`~Option.nargs` @@ -1787,7 +1784,7 @@ where ``parser.values`` the object where option values are by default stored (an instance of optparse.OptionValues). This lets callbacks use the same mechanism as the - rest of :mod:`optparse` for storing option values; you don't need to mess + rest of :mod:`!optparse` for storing option values; you don't need to mess around with globals or closures. You can also access or modify the value(s) of any options already encountered on the command-line. @@ -1806,7 +1803,7 @@ Raising errors in a callback ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The callback function should raise :exc:`OptionValueError` if there are any -problems with the option or its argument(s). :mod:`optparse` catches this and +problems with the option or its argument(s). :mod:`!optparse` catches this and terminates the program, printing the error message you supply to stderr. Your message should be clear, concise, accurate, and mention the option at fault. Otherwise, the user will have a hard time figuring out what they did wrong. @@ -1906,7 +1903,7 @@ Here's an example that just emulates the standard ``"store"`` action:: action="callback", callback=store_value, type="int", nargs=3, dest="foo") -Note that :mod:`optparse` takes care of consuming 3 arguments and converting +Note that :mod:`!optparse` takes care of consuming 3 arguments and converting them to integers for you; all you have to do is store them. (Or whatever; obviously you don't need a callback for this example.) @@ -1917,9 +1914,9 @@ Callback example 6: variable arguments ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Things get hairy when you want an option to take a variable number of arguments. -For this case, you must write a callback, as :mod:`optparse` doesn't provide any +For this case, you must write a callback, as :mod:`!optparse` doesn't provide any built-in capabilities for it. And you have to deal with certain intricacies of -conventional Unix command-line parsing that :mod:`optparse` normally handles for +conventional Unix command-line parsing that :mod:`!optparse` normally handles for you. In particular, callbacks should implement the conventional rules for bare ``--`` and ``-`` arguments: @@ -1934,7 +1931,7 @@ you. In particular, callbacks should implement the conventional rules for bare If you want an option that takes a variable number of arguments, there are several subtle, tricky issues to worry about. The exact implementation you choose will be based on which trade-offs you're willing to make for your -application (which is why :mod:`optparse` doesn't support this sort of thing +application (which is why :mod:`!optparse` doesn't support this sort of thing directly). Nevertheless, here's a stab at a callback for an option with variable @@ -1970,10 +1967,10 @@ arguments:: .. _optparse-extending-optparse: -Extending :mod:`optparse` -------------------------- +Extending :mod:`!optparse` +-------------------------- -Since the two major controlling factors in how :mod:`optparse` interprets +Since the two major controlling factors in how :mod:`!optparse` interprets command-line options are the action and type of each option, the most likely direction of extension is to add new actions and new types. @@ -1983,9 +1980,9 @@ direction of extension is to add new actions and new types. Adding new types ^^^^^^^^^^^^^^^^ -To add new types, you need to define your own subclass of :mod:`optparse`'s +To add new types, you need to define your own subclass of :mod:`!optparse`'s :class:`Option` class. This class has a couple of attributes that define -:mod:`optparse`'s types: :attr:`~Option.TYPES` and :attr:`~Option.TYPE_CHECKER`. +:mod:`!optparse`'s types: :attr:`~Option.TYPES` and :attr:`~Option.TYPE_CHECKER`. .. attribute:: Option.TYPES @@ -2015,7 +2012,7 @@ To add new types, you need to define your own subclass of :mod:`optparse`'s Here's a silly example that demonstrates adding a ``"complex"`` option type to parse Python-style complex numbers on the command line. (This is even sillier -than it used to be, because :mod:`optparse` 1.3 added built-in support for +than it used to be, because :mod:`!optparse` 1.3 added built-in support for complex numbers, but never mind.) First, the necessary imports:: @@ -2041,12 +2038,12 @@ Finally, the Option subclass:: TYPE_CHECKER["complex"] = check_complex (If we didn't make a :func:`copy` of :attr:`Option.TYPE_CHECKER`, we would end -up modifying the :attr:`~Option.TYPE_CHECKER` attribute of :mod:`optparse`'s +up modifying the :attr:`~Option.TYPE_CHECKER` attribute of :mod:`!optparse`'s Option class. This being Python, nothing stops you from doing that except good manners and common sense.) That's it! Now you can write a script that uses the new option type just like -any other :mod:`optparse`\ -based script, except you have to instruct your +any other :mod:`!optparse`\ -based script, except you have to instruct your OptionParser to use MyOption instead of Option:: parser = OptionParser(option_class=MyOption) @@ -2066,10 +2063,10 @@ Adding new actions ^^^^^^^^^^^^^^^^^^ Adding new actions is a bit trickier, because you have to understand that -:mod:`optparse` has a couple of classifications for actions: +:mod:`!optparse` has a couple of classifications for actions: "store" actions - actions that result in :mod:`optparse` storing a value to an attribute of the + actions that result in :mod:`!optparse` storing a value to an attribute of the current OptionValues instance; these options require a :attr:`~Option.dest` attribute to be supplied to the Option constructor. @@ -2101,7 +2098,7 @@ of the following class attributes of Option (all are lists of strings): .. attribute:: Option.ALWAYS_TYPED_ACTIONS Actions that always take a type (i.e. whose options always take a value) are - additionally listed here. The only effect of this is that :mod:`optparse` + additionally listed here. The only effect of this is that :mod:`!optparse` assigns the default type, ``"string"``, to options with no explicit type whose action is listed in :attr:`ALWAYS_TYPED_ACTIONS`. @@ -2144,12 +2141,12 @@ Features of note: somewhere, so it goes in both :attr:`~Option.STORE_ACTIONS` and :attr:`~Option.TYPED_ACTIONS`. -* to ensure that :mod:`optparse` assigns the default type of ``"string"`` to +* to ensure that :mod:`!optparse` assigns the default type of ``"string"`` to ``"extend"`` actions, we put the ``"extend"`` action in :attr:`~Option.ALWAYS_TYPED_ACTIONS` as well. * :meth:`MyOption.take_action` implements just this one new action, and passes - control back to :meth:`Option.take_action` for the standard :mod:`optparse` + control back to :meth:`Option.take_action` for the standard :mod:`!optparse` actions. * ``values`` is an instance of the optparse_parser.Values class, which provides diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst index d27023cde4cfe9..808187061733be 100644 --- a/Doc/library/os.path.rst +++ b/Doc/library/os.path.rst @@ -36,14 +36,14 @@ the :mod:`glob` module.) Since different operating systems have different path name conventions, there are several versions of this module in the standard library. The - :mod:`os.path` module is always the path module suitable for the operating + :mod:`!os.path` module is always the path module suitable for the operating system Python is running on, and therefore usable for local paths. However, you can also import and use the individual modules if you want to manipulate a path that is *always* in one of the different formats. They all have the same interface: - * :mod:`posixpath` for UNIX-style paths - * :mod:`ntpath` for Windows paths + * :mod:`!posixpath` for UNIX-style paths + * :mod:`!ntpath` for Windows paths .. versionchanged:: 3.8 @@ -57,8 +57,9 @@ the :mod:`glob` module.) .. function:: abspath(path) Return a normalized absolutized version of the pathname *path*. On most - platforms, this is equivalent to calling the function :func:`normpath` as - follows: ``normpath(join(os.getcwd(), path))``. + platforms, this is equivalent to calling ``normpath(join(os.getcwd(), path))``. + + .. seealso:: :func:`os.path.join` and :func:`os.path.normpath`. .. versionchanged:: 3.6 Accepts a :term:`path-like object`. @@ -96,15 +97,17 @@ the :mod:`glob` module.) .. function:: commonprefix(list, /) - Return the longest path prefix (taken character-by-character) that is a - prefix of all paths in *list*. If *list* is empty, return the empty string + Return the longest string prefix (taken character-by-character) that is a + prefix of all strings in *list*. If *list* is empty, return the empty string (``''``). - .. note:: + .. warning:: This function may return invalid paths because it works a - character at a time. To obtain a valid path, see - :func:`commonpath`. + character at a time. + If you need a **common path prefix**, then the algorithm + implemented in this function is not secure. Use + :func:`commonpath` for finding a common path prefix. :: @@ -117,6 +120,14 @@ the :mod:`glob` module.) .. versionchanged:: 3.6 Accepts a :term:`path-like object`. + .. deprecated:: 3.15 + Deprecated in favor of :func:`os.path.commonpath` for path prefixes. + The :func:`os.path.commonprefix` function is being deprecated due to + having a misleading name and module. The function is not safe to use for + path prefixes despite being included in a module about path manipulation, + meaning it is easy to accidentally introduce path traversal + vulnerabilities into Python programs by using this function. + .. function:: dirname(path, /) @@ -243,6 +254,8 @@ the :mod:`glob` module.) begins with a slash, on Windows that it begins with two (back)slashes, or a drive letter, colon, and (back)slash together. + .. seealso:: :func:`abspath` + .. versionchanged:: 3.6 Accepts a :term:`path-like object`. @@ -357,14 +370,28 @@ the :mod:`glob` module.) concatenation of *path* and all members of *\*paths*, with exactly one directory separator following each non-empty part, except the last. That is, the result will only end in a separator if the last part is either empty or - ends in a separator. If a segment is an absolute path (which on Windows - requires both a drive and a root), then all previous segments are ignored and - joining continues from the absolute path segment. + ends in a separator. + + If a segment is an absolute path (which on Windows requires both a drive and + a root), then all previous segments are ignored and joining continues from the + absolute path segment. On Linux, for example:: + + >>> os.path.join('/home/foo', 'bar') + '/home/foo/bar' + >>> os.path.join('/home/foo', '/home/bar') + '/home/bar' On Windows, the drive is not reset when a rooted path segment (e.g., ``r'\foo'``) is encountered. If a segment is on a different drive or is an - absolute path, all previous segments are ignored and the drive is reset. Note - that since there is a current directory for each drive, + absolute path, all previous segments are ignored and the drive is reset. For + example:: + + >>> os.path.join('c:\\', 'foo') + 'c:\\foo' + >>> os.path.join('c:\\foo', 'd:\\bar') + 'd:\\bar' + + Note that since there is a current directory for each drive, ``os.path.join("c:", "foo")`` represents a path relative to the current directory on drive :file:`C:` (:file:`c:foo`), not :file:`c:\\foo`. @@ -424,6 +451,8 @@ the :mod:`glob` module.) re-raised. In particular, :exc:`FileNotFoundError` is raised if *path* does not exist, or another :exc:`OSError` if it is otherwise inaccessible. + If *strict* is :data:`ALL_BUT_LAST`, the last component of the path + is allowed to be missing, but all other errors are raised. If *strict* is :py:data:`os.path.ALLOW_MISSING`, errors other than :exc:`FileNotFoundError` are re-raised (as with ``strict=True``). @@ -447,15 +476,22 @@ the :mod:`glob` module.) .. versionchanged:: 3.10 The *strict* parameter was added. - .. versionchanged:: next - The :py:data:`~os.path.ALLOW_MISSING` value for the *strict* parameter - was added. + .. versionchanged:: 3.15 + The :data:`ALL_BUT_LAST` and :data:`ALLOW_MISSING` values for + the *strict* parameter was added. + +.. data:: ALL_BUT_LAST + + Special value used for the *strict* argument in :func:`realpath`. + + .. versionadded:: 3.15 .. data:: ALLOW_MISSING Special value used for the *strict* argument in :func:`realpath`. - .. versionadded:: next + .. versionadded:: 3.15 + .. function:: relpath(path, start=os.curdir) @@ -518,8 +554,8 @@ the :mod:`glob` module.) *path* is empty, both *head* and *tail* are empty. Trailing slashes are stripped from *head* unless it is the root (one or more slashes only). In all cases, ``join(head, tail)`` returns a path to the same location as *path* - (but the strings may differ). Also see the functions :func:`dirname` and - :func:`basename`. + (but the strings may differ). Also see the functions :func:`join`, + :func:`dirname` and :func:`basename`. .. versionchanged:: 3.6 Accepts a :term:`path-like object`. diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 45ec6c7a51b7b0..9327d616ffa05d 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -25,7 +25,7 @@ Notes on the availability of these functions: with the POSIX interface). * Extensions peculiar to a particular operating system are also available - through the :mod:`os` module, but using them is of course a threat to + through the :mod:`!os` module, but using them is of course a threat to portability. * All functions accepting path or file names accept both bytes and string @@ -34,7 +34,7 @@ Notes on the availability of these functions: * On VxWorks, os.popen, os.fork, os.execv and os.spawn*p* are not supported. -* On WebAssembly platforms, Android and iOS, large parts of the :mod:`os` module are +* On WebAssembly platforms, Android and iOS, large parts of the :mod:`!os` module are not available or behave differently. APIs related to processes (e.g. :func:`~os.fork`, :func:`~os.execve`) and resources (e.g. :func:`~os.nice`) are not available. Others like :func:`~os.getuid` and :func:`~os.getpid` are @@ -108,10 +108,10 @@ Python UTF-8 Mode .. versionadded:: 3.7 See :pep:`540` for more details. -.. versionchanged:: next +.. versionchanged:: 3.15 Python UTF-8 mode is now enabled by default (:pep:`686`). - It may be disabled with by setting :envvar:`PYTHONUTF8=0 ` as + It may be disabled by setting :envvar:`PYTHONUTF8=0 ` as an environment variable or by using the :option:`-X utf8=0 <-X>` command line option. The Python UTF-8 Mode ignores the :term:`locale encoding` and forces the usage @@ -185,7 +185,7 @@ process and user. of your home directory (on some platforms), and is equivalent to ``getenv("HOME")`` in C. - This mapping is captured the first time the :mod:`os` module is imported, + This mapping is captured the first time the :mod:`!os` module is imported, typically during Python startup as part of processing :file:`site.py`. Changes to the environment made after this time are not reflected in :data:`os.environ`, except for changes made by modifying :data:`os.environ` directly. @@ -216,8 +216,16 @@ process and user. You can delete items in this mapping to unset environment variables. :func:`unsetenv` will be called automatically when an item is deleted from - :data:`os.environ`, and when one of the :meth:`pop` or :meth:`clear` methods is - called. + :data:`os.environ`, and when one of the :meth:`~dict.pop` or + :meth:`~dict.clear` methods is called. + + If the :manpage:`clearenv(3)` function is available, the :meth:`~dict.clear` method + uses it and emits a single ``os._clearenv`` audit event. Otherwise, it emits + an ``os.unsetenv`` event on each deleted variable. + + .. audit-event:: os.unsetenv key os.unsetenv + + .. audit-event:: os._clearenv "" os._clearenv .. seealso:: @@ -226,6 +234,10 @@ process and user. .. versionchanged:: 3.9 Updated to support :pep:`584`'s merge (``|``) and update (``|=``) operators. + .. versionchanged:: 3.15 + The :meth:`~dict.clear` method can now emit an ``os._clearenv`` audit + event. + .. data:: environb @@ -254,7 +266,7 @@ process and user. .. warning:: This function is not thread-safe. Calling it while the environment is - being modified in an other thread is an undefined behavior. Reading from + being modified in another thread is an undefined behavior. Reading from :data:`os.environ` or :data:`os.environb`, or calling :func:`os.getenv` while reloading, may return an empty result. @@ -430,8 +442,8 @@ process and user. associated with the effective user id of the process; the group access list may change over the lifetime of the process, it is not affected by calls to :func:`setgroups`, and its length is not limited to 16. The - deployment target value, :const:`MACOSX_DEPLOYMENT_TARGET`, can be - obtained with :func:`sysconfig.get_config_var`. + deployment target value can be obtained with + :func:`sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') `. .. function:: getlogin() @@ -558,7 +570,7 @@ process and user. .. function:: initgroups(username, gid, /) - Call the system initgroups() to initialize the group access list with all of + Call the system ``initgroups()`` to initialize the group access list with all of the groups of which the specified username is a member, plus the specified group id. @@ -1279,7 +1291,7 @@ as internal buffering of data. For a description of the flag and mode values, see the C run-time documentation; flag constants (like :const:`O_RDONLY` and :const:`O_WRONLY`) are defined in - the :mod:`os` module. In particular, on Windows adding + the :mod:`!os` module. In particular, on Windows adding :const:`O_BINARY` is needed to open files in binary mode. This function can support :ref:`paths relative to directory descriptors @@ -1294,8 +1306,8 @@ as internal buffering of data. This function is intended for low-level I/O. For normal usage, use the built-in function :func:`open`, which returns a :term:`file object` with - :meth:`~file.read` and :meth:`~file.write` methods (and many more). To - wrap a file descriptor in a file object, use :func:`fdopen`. + :meth:`~io.BufferedIOBase.read` and :meth:`~io.BufferedIOBase.write` methods. + To wrap a file descriptor in a file object, use :func:`fdopen`. .. versionchanged:: 3.3 Added the *dir_fd* parameter. @@ -1501,6 +1513,7 @@ or `the MSDN `_ on Windo - :data:`RWF_HIPRI` - :data:`RWF_NOWAIT` + - :data:`RWF_DONTCACHE` Return the total number of bytes actually read which can be less than the total capacity of all the objects. @@ -1546,6 +1559,24 @@ or `the MSDN `_ on Windo .. versionadded:: 3.7 +.. data:: RWF_DONTCACHE + + Use uncached buffered IO. + + .. availability:: Linux >= 6.14 + + .. versionadded:: 3.15 + + +.. data:: RWF_ATOMIC + + Write data atomically. Requires alignment to the device's atomic write unit. + + .. availability:: Linux >= 6.11 + + .. versionadded:: 3.15 + + .. function:: ptsname(fd, /) Return the name of the slave pseudo-terminal device associated with the @@ -1587,6 +1618,8 @@ or `the MSDN `_ on Windo - :data:`RWF_DSYNC` - :data:`RWF_SYNC` - :data:`RWF_APPEND` + - :data:`RWF_DONTCACHE` + - :data:`RWF_ATOMIC` Return the total number of bytes actually written. @@ -1649,7 +1682,7 @@ or `the MSDN `_ on Windo descriptor as returned by :func:`os.open` or :func:`pipe`. To read a "file object" returned by the built-in function :func:`open` or by :func:`popen` or :func:`fdopen`, or :data:`sys.stdin`, use its - :meth:`~file.read` or :meth:`~file.readline` methods. + :meth:`~io.TextIOBase.read` or :meth:`~io.IOBase.readline` methods. .. versionchanged:: 3.5 If the system call is interrupted and the signal handler does not raise an @@ -1884,7 +1917,7 @@ or `the MSDN `_ on Windo descriptor as returned by :func:`os.open` or :func:`pipe`. To write a "file object" returned by the built-in function :func:`open` or by :func:`popen` or :func:`fdopen`, or :data:`sys.stdout` or :data:`sys.stderr`, use its - :meth:`~file.write` method. + :meth:`~io.TextIOBase.write` method. .. versionchanged:: 3.5 If the system call is interrupted and the signal handler does not raise an @@ -1958,7 +1991,8 @@ can be inherited by child processes. Since Python 3.4, file descriptors created by Python are non-inheritable by default. On UNIX, non-inheritable file descriptors are closed in child processes at the -execution of a new program, other file descriptors are inherited. +execution of a new program, other file descriptors are inherited. Note that +non-inheritable file descriptors are still *inherited* by child processes on :func:`os.fork`. On Windows, non-inheritable handles and file descriptors are closed in child processes, except for standard streams (file descriptors 0, 1 and 2: stdin, stdout @@ -2002,12 +2036,12 @@ features: .. _path_fd: * **specifying a file descriptor:** - Normally the *path* argument provided to functions in the :mod:`os` module + Normally the *path* argument provided to functions in the :mod:`!os` module must be a string specifying a file path. However, some functions now alternatively accept an open file descriptor for their *path* argument. The function will then operate on the file referred to by the descriptor. - (For POSIX systems, Python will call the variant of the function prefixed - with ``f`` (e.g. call ``fchdir`` instead of ``chdir``).) + For POSIX systems, Python will call the variant of the function prefixed + with ``f`` (e.g. call ``fchdir`` instead of ``chdir``). You can check whether or not *path* can be specified as a file descriptor for a particular function on your platform using :data:`os.supports_fd`. @@ -2022,7 +2056,7 @@ features: * **paths relative to directory descriptors:** If *dir_fd* is not ``None``, it should be a file descriptor referring to a directory, and the path to operate on should be relative; path will then be relative to that directory. If the - path is absolute, *dir_fd* is ignored. (For POSIX systems, Python will call + path is absolute, *dir_fd* is ignored. For POSIX systems, Python will call the variant of the function with an ``at`` suffix and possibly prefixed with ``f`` (e.g. call ``faccessat`` instead of ``access``). @@ -2035,8 +2069,8 @@ features: * **not following symlinks:** If *follow_symlinks* is ``False``, and the last element of the path to operate on is a symbolic link, the function will operate on the symbolic link itself rather than the file - pointed to by the link. (For POSIX systems, Python will call the ``l...`` - variant of the function.) + pointed to by the link. For POSIX systems, Python will call the ``l...`` + variant of the function. You can check whether or not *follow_symlinks* is supported for a particular function on your platform using :data:`os.supports_follow_symlinks`. @@ -2387,6 +2421,10 @@ features: .. versionchanged:: 3.6 Accepts a :term:`path-like object`. + .. versionchanged:: 3.15 + ``os.listdir(-1)`` now fails with ``OSError(errno.EBADF)`` rather than + listing the current directory. + .. function:: listdrives() @@ -2523,7 +2561,8 @@ features: Windows now handles a *mode* of ``0o700``. -.. function:: makedirs(name, mode=0o777, exist_ok=False) +.. function:: makedirs(name, mode=0o777, exist_ok=False, *, \ + parent_mode=None) .. index:: single: directory; creating @@ -2541,6 +2580,12 @@ features: If *exist_ok* is ``False`` (the default), a :exc:`FileExistsError` is raised if the target directory already exists. + If *parent_mode* is not ``None``, it is used as the mode for any + newly-created, intermediate-level directories. Like *mode*, it is + combined with the process's umask value; see :ref:`the mkdir() + description `. Otherwise, intermediate directories are + created with the default mode, which is also subject to the umask. + .. note:: :func:`makedirs` will become confused if the path elements to create @@ -2567,6 +2612,11 @@ features: The *mode* argument no longer affects the file permission bits of newly created intermediate-level directories. + .. versionadded:: 3.15 + The *parent_mode* parameter. To match the behavior from Python 3.6 and + earlier (where *mode* was applied to all created directories), pass + ``parent_mode=mode``. + .. function:: mkfifo(path, mode=0o666, *, dir_fd=None) @@ -2595,10 +2645,10 @@ features: Create a filesystem node (file, device special file or named pipe) named *path*. *mode* specifies both the permissions to use and the type of node - to be created, being combined (bitwise OR) with one of ``stat.S_IFREG``, - ``stat.S_IFCHR``, ``stat.S_IFBLK``, and ``stat.S_IFIFO`` (those constants are - available in :mod:`stat`). For ``stat.S_IFCHR`` and ``stat.S_IFBLK``, - *device* defines the newly created device special file (probably using + to be created, being combined (bitwise OR) with one of :const:`stat.S_IFREG`, + :const:`stat.S_IFCHR`, :const:`stat.S_IFBLK`, and :const:`stat.S_IFIFO`. + For :const:`stat.S_IFCHR` and :const:`stat.S_IFBLK`, *device* defines the + newly created device special file (probably using :func:`os.makedev`), otherwise it is ignored. This function can also support :ref:`paths relative to directory descriptors @@ -2616,13 +2666,13 @@ features: .. function:: major(device, /) Extract the device major number from a raw device number (usually the - :attr:`st_dev` or :attr:`st_rdev` field from :c:struct:`stat`). + :attr:`~stat_result.st_dev` or :attr:`~stat_result.st_rdev` field from :c:struct:`stat`). .. function:: minor(device, /) Extract the device minor number from a raw device number (usually the - :attr:`st_dev` or :attr:`st_rdev` field from :c:struct:`stat`). + :attr:`~stat_result.st_dev` or :attr:`~stat_result.st_rdev` field from :c:struct:`stat`). .. function:: makedev(major, minor, /) @@ -2630,6 +2680,13 @@ features: Compose a raw device number from the major and minor device numbers. +.. data:: NODEV + + Non-existent device. + + .. versionadded:: 3.15 + + .. function:: pathconf(path, name) Return system configuration information relevant to a named file. *name* @@ -2910,6 +2967,10 @@ features: .. versionchanged:: 3.7 Added support for :ref:`file descriptors ` on Unix. + .. versionchanged:: 3.15 + ``os.scandir(-1)`` now fails with ``OSError(errno.EBADF)`` rather than + listing the current directory. + .. class:: DirEntry @@ -2933,6 +2994,9 @@ features: To be directly usable as a :term:`path-like object`, ``os.DirEntry`` implements the :class:`PathLike` interface. + :class:`!DirEntry` objects are :ref:`generic ` over the type of the + path (:class:`str` or :class:`bytes`). + Attributes and methods on a ``os.DirEntry`` instance are as follows: .. attribute:: name @@ -3346,8 +3410,8 @@ features: .. versionchanged:: 3.8 On Windows, the :attr:`st_mode` member now identifies special - files as :const:`S_IFCHR`, :const:`S_IFIFO` or :const:`S_IFBLK` - as appropriate. + files as :const:`~stat.S_IFCHR`, :const:`~stat.S_IFIFO` or + :const:`~stat.S_IFBLK` as appropriate. .. versionchanged:: 3.12 On Windows, :attr:`st_ctime` is deprecated. Eventually, it will @@ -3365,6 +3429,359 @@ features: Added the :attr:`st_birthtime` member on Windows. +.. function:: statx(path, mask, *, flags=0, dir_fd=None, follow_symlinks=True) + + Get the status of a file or file descriptor by performing a :c:func:`!statx` + system call on the given path. + + *path* is a :term:`path-like object` or an open file descriptor. *mask* is a + combination of the module-level :const:`STATX_* ` constants + specifying the information to retrieve. *flags* is a combination of the + module-level :const:`AT_STATX_* ` constants and/or + :const:`AT_NO_AUTOMOUNT`. Returns a :class:`statx_result` object whose + :attr:`~os.statx_result.stx_mask` attribute specifies the information + actually retrieved (which may differ from *mask*). + + This function supports :ref:`specifying a file descriptor `, + :ref:`paths relative to directory descriptors `, and + :ref:`not following symlinks `. + + .. seealso:: The :manpage:`statx(2)` man page. + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: 3.15 + + +.. class:: statx_result + + Information about a file returned by :func:`os.statx`. + + :class:`!statx_result` has the following attributes: + + .. attribute:: stx_atime + + Time of most recent access expressed in seconds. + + Equal to ``None`` if :data:`STATX_ATIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_atime_ns + + Time of most recent access expressed in nanoseconds as an integer. + + Equal to ``None`` if :data:`STATX_ATIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_atomic_write_segments_max + + Maximum iovecs for direct I/O with torn-write protection. + + Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.11. + + .. attribute:: stx_atomic_write_unit_max + + Maximum size for direct I/O with torn-write protection. + + Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.11. + + .. attribute:: stx_atomic_write_unit_max_opt + + Maximum optimized size for direct I/O with torn-write protection. + + Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.16. + + .. attribute:: stx_atomic_write_unit_min + + Minimum size for direct I/O with torn-write protection. + + Equal to ``None`` if :data:`STATX_WRITE_ATOMIC` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.11. + + .. attribute:: stx_attributes + + Bitmask of :const:`STATX_ATTR_* ` constants + specifying the attributes of this file. + + .. attribute:: stx_attributes_mask + + A mask indicating which bits in :attr:`stx_attributes` are supported by + the VFS and the filesystem. + + .. attribute:: stx_blksize + + "Preferred" blocksize for efficient file system I/O. Writing to a file in + smaller chunks may cause an inefficient read-modify-rewrite. + + .. attribute:: stx_blocks + + Number of 512-byte blocks allocated for file. + This may be smaller than :attr:`stx_size`/512 when the file has holes. + + Equal to ``None`` if :data:`STATX_BLOCKS` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_btime + + Time of file creation expressed in seconds. + + Equal to ``None`` if :data:`STATX_BTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_btime_ns + + Time of file creation expressed in nanoseconds as an integer. + + Equal to ``None`` if :data:`STATX_BTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_ctime + + Time of most recent metadata change expressed in seconds. + + Equal to ``None`` if :data:`STATX_CTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_ctime_ns + + Time of most recent metadata change expressed in nanoseconds as an + integer. + + Equal to ``None`` if :data:`STATX_CTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_dev + + Identifier of the device on which this file resides. + + .. attribute:: stx_dev_major + + Major number of the device on which this file resides. + + .. attribute:: stx_dev_minor + + Minor number of the device on which this file resides. + + .. attribute:: stx_dio_mem_align + + Direct I/O memory buffer alignment requirement. + + Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.1. + + .. attribute:: stx_dio_offset_align + + Direct I/O file offset alignment requirement. + + Equal to ``None`` if :data:`STATX_DIOALIGN` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.1. + + .. attribute:: stx_dio_read_offset_align + + Direct I/O file offset alignment requirement for reads. + + Equal to ``None`` if :data:`STATX_DIO_READ_ALIGN` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.14. + + .. attribute:: stx_gid + + Group identifier of the file owner. + + Equal to ``None`` if :data:`STATX_GID` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_ino + + Inode number. + + Equal to ``None`` if :data:`STATX_INO` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_mask + + Bitmask of :const:`STATX_* ` constants specifying the + information retrieved, which may differ from what was requested. + + .. attribute:: stx_mnt_id + + Mount identifier. + + Equal to ``None`` if :data:`STATX_MNT_ID` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 5.8. + + .. attribute:: stx_mode + + File mode: file type and file mode bits (permissions). + + Equal to ``None`` if :data:`STATX_TYPE | STATX_MODE ` + is missing from :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_mtime + + Time of most recent content modification expressed in seconds. + + Equal to ``None`` if :data:`STATX_MTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_mtime_ns + + Time of most recent content modification expressed in nanoseconds as an + integer. + + Equal to ``None`` if :data:`STATX_MTIME` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_nlink + + Number of hard links. + + Equal to ``None`` if :data:`STATX_NLINK` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_rdev + + Type of device if an inode device. + + .. attribute:: stx_rdev_major + + Major number of the device this file represents. + + .. attribute:: stx_rdev_minor + + Minor number of the device this file represents. + + .. attribute:: stx_size + + Size of the file in bytes, if it is a regular file or a symbolic link. + The size of a symbolic link is the length of the pathname it contains, + without a terminating null byte. + + Equal to ``None`` if :data:`STATX_SIZE` is missing from + :attr:`~statx_result.stx_mask`. + + .. attribute:: stx_subvol + + Subvolume identifier. + + Equal to ``None`` if :data:`STATX_SUBVOL` is missing from + :attr:`~statx_result.stx_mask`. + + .. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel + userspace API headers >= 6.10. + + .. attribute:: stx_uid + + User identifier of the file owner. + + Equal to ``None`` if :data:`STATX_UID` is missing from + :attr:`~statx_result.stx_mask`. + + .. seealso:: The :manpage:`statx(2)` man page. + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: 3.15 + + +.. data:: STATX_TYPE + STATX_MODE + STATX_NLINK + STATX_UID + STATX_GID + STATX_ATIME + STATX_MTIME + STATX_CTIME + STATX_INO + STATX_SIZE + STATX_BLOCKS + STATX_BASIC_STATS + STATX_BTIME + STATX_MNT_ID + STATX_DIOALIGN + STATX_MNT_ID_UNIQUE + STATX_SUBVOL + STATX_WRITE_ATOMIC + STATX_DIO_READ_ALIGN + + Bitflags for use in the *mask* parameter to :func:`os.statx`. Some of these + flags may be available even when their corresponding members in + :class:`statx_result` are not available. + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: 3.15 + +.. data:: AT_STATX_FORCE_SYNC + + A flag for the :func:`os.statx` function. Requests that the kernel return + up-to-date information even when doing so is expensive (for example, + requiring a round trip to the server for a file on a network filesystem). + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: 3.15 + +.. data:: AT_STATX_DONT_SYNC + + A flag for the :func:`os.statx` function. Requests that the kernel return + cached information if possible. + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: 3.15 + +.. data:: AT_STATX_SYNC_AS_STAT + + A flag for the :func:`os.statx` function. This flag is defined as ``0``, so + it has no effect, but it can be used to explicitly indicate neither + :data:`AT_STATX_FORCE_SYNC` nor :data:`AT_STATX_DONT_SYNC` is being passed. + In the absence of the other two flags, the kernel will generally return + information as fresh as :func:`os.stat` would return. + + .. availability:: Linux >= 4.11 with glibc >= 2.28. + + .. versionadded:: 3.15 + + +.. data:: AT_NO_AUTOMOUNT + + If the final component of a path is an automount point, operate on the + automount point instead of performing the automount. On Linux, + :func:`os.stat`, :func:`os.fstat` and :func:`os.lstat` always behave this + way. + + .. availability:: Linux. + + .. versionadded:: 3.15 + + .. function:: statvfs(path) Perform a :c:func:`!statvfs` system call on the given path. The return value is @@ -3413,7 +3830,7 @@ features: .. data:: supports_dir_fd - A :class:`set` object indicating which functions in the :mod:`os` + A :class:`set` object indicating which functions in the :mod:`!os` module accept an open file descriptor for their *dir_fd* parameter. Different platforms provide different features, and the underlying functionality Python uses to implement the *dir_fd* parameter is not @@ -3458,7 +3875,7 @@ features: .. data:: supports_fd A :class:`set` object indicating which functions in the - :mod:`os` module permit specifying their *path* parameter as an open file + :mod:`!os` module permit specifying their *path* parameter as an open file descriptor on the local platform. Different platforms provide different features, and the underlying functionality Python uses to accept open file descriptors as *path* arguments is not available on all platforms Python @@ -3477,7 +3894,7 @@ features: .. data:: supports_follow_symlinks - A :class:`set` object indicating which functions in the :mod:`os` module + A :class:`set` object indicating which functions in the :mod:`!os` module accept ``False`` for their *follow_symlinks* parameter on the local platform. Different platforms provide different features, and the underlying functionality Python uses to implement *follow_symlinks* is not available @@ -3502,6 +3919,9 @@ features: Create a symbolic link pointing to *src* named *dst*. + The *src* parameter refers to the target of the link (the file or directory being linked to), + and *dst* is the name of the link being created. + On Windows, a symlink represents either a file or a directory, and does not morph to the target dynamically. If the target is present, the type of the symlink will be created to match. Otherwise, the symlink will be created @@ -3600,7 +4020,8 @@ features: where each member is an int expressing nanoseconds. - If *times* is not ``None``, it must be a 2-tuple of the form ``(atime, mtime)`` - where each member is an int or float expressing seconds. + where each member is a real number expressing seconds, + rounded down to nanoseconds. - If *times* is ``None`` and *ns* is unspecified, this is equivalent to specifying ``ns=(atime_ns, mtime_ns)`` where both times are the current time. @@ -3627,6 +4048,9 @@ features: .. versionchanged:: 3.6 Accepts a :term:`path-like object`. + .. versionchanged:: 3.15 + Accepts any real numbers as *times*, not only integers or floats. + .. function:: walk(top, topdown=True, onerror=None, followlinks=False) @@ -3757,9 +4181,9 @@ features: import os for root, dirs, files, rootfd in os.fwalk('python/Lib/xml'): - print(root, "consumes", end="") + print(root, "consumes", end=" ") print(sum([os.stat(name, dir_fd=rootfd).st_size for name in files]), - end="") + end=" ") print("bytes in", len(files), "non-directory files") if '__pycache__' in dirs: dirs.remove('__pycache__') # don't visit __pycache__ directories @@ -3873,7 +4297,7 @@ features: import os # semaphore with start value '1' - fd = os.eventfd(1, os.EFD_SEMAPHORE | os.EFC_CLOEXEC) + fd = os.eventfd(1, os.EFD_SEMAPHORE | os.EFD_CLOEXEC) try: # acquire semaphore v = os.eventfd_read(fd) @@ -3982,7 +4406,7 @@ Naturally, they are all only available on Linux. except it includes any time that the system is suspended. The file descriptor's behaviour can be modified by specifying a *flags* value. - Any of the following variables may used, combined using bitwise OR + Any of the following variables may be used, combined using bitwise OR (the ``|`` operator): - :const:`TFD_NONBLOCK` @@ -4014,7 +4438,7 @@ Naturally, they are all only available on Linux. *fd* must be a valid timer file descriptor. The timer's behaviour can be modified by specifying a *flags* value. - Any of the following variables may used, combined using bitwise OR + Any of the following variables may be used, combined using bitwise OR (the ``|`` operator): - :const:`TFD_TIMER_ABSTIME` @@ -4032,7 +4456,7 @@ Naturally, they are all only available on Linux. the timer will fire when the timer's clock (set by *clockid* in :func:`timerfd_create`) reaches *initial* seconds. - The timer's interval is set by the *interval* :py:class:`float`. + The timer's interval is set by the *interval* real number. If *interval* is zero, the timer only fires once, on the initial expiration. If *interval* is greater than zero, the timer fires every time *interval* seconds have elapsed since the previous expiration. @@ -4083,7 +4507,7 @@ Naturally, they are all only available on Linux. Return a two-item tuple of floats (``next_expiration``, ``interval``). - ``next_expiration`` denotes the relative time until next the timer next fires, + ``next_expiration`` denotes the relative time until the timer next fires, regardless of if the :const:`TFD_TIMER_ABSTIME` flag is set. ``interval`` denotes the timer's interval. @@ -4185,6 +4609,10 @@ These functions are all available on Linux only. .. versionchanged:: 3.6 Accepts a :term:`path-like object`. + .. versionchanged:: 3.15 + ``os.listxattr(-1)`` now fails with ``OSError(errno.EBADF)`` rather than + listing extended attributes of the current directory. + .. function:: removexattr(path, attribute, *, follow_symlinks=True) @@ -4263,10 +4691,10 @@ to be ignored. .. function:: abort() - Generate a :const:`SIGABRT` signal to the current process. On Unix, the default + Generate a :const:`~signal.SIGABRT` signal to the current process. On Unix, the default behavior is to produce a core dump; on Windows, the process immediately returns an exit code of ``3``. Be aware that calling this function will not call the - Python signal handler registered for :const:`SIGABRT` with + Python signal handler registered for :const:`~signal.SIGABRT` with :func:`signal.signal`. @@ -4319,7 +4747,7 @@ to be ignored. The current process is replaced immediately. Open file objects and descriptors are not flushed, so if there may be data buffered on these open files, you should flush them using - :func:`sys.stdout.flush` or :func:`os.fsync` before calling an + :func:`~io.IOBase.flush` or :func:`os.fsync` before calling an :func:`exec\* ` function. The "l" and "v" variants of the :func:`exec\* ` functions differ in how @@ -4570,6 +4998,8 @@ written in Python, such as a mail server's external command delivery program. master end of the pseudo-terminal. For a more portable approach, use the :mod:`pty` module. If an error occurs :exc:`OSError` is raised. + The returned file descriptor *fd* is :ref:`non-inheritable `. + .. audit-event:: os.forkpty "" os.forkpty .. warning:: @@ -4586,6 +5016,9 @@ written in Python, such as a mail server's external command delivery program. threads, this now raises a :exc:`DeprecationWarning`. See the longer explanation on :func:`os.fork`. + .. versionchanged:: 3.15 + The returned file descriptor is now made non-inheritable. + .. availability:: Unix, not WASI, not Android, not iOS. @@ -4656,6 +5089,18 @@ written in Python, such as a mail server's external command delivery program. .. availability:: Linux >= 5.10 .. versionadded:: 3.12 +.. function:: pidfd_getfd(pidfd, targetfd, *, flags=0) + + Duplicate *targetfd* from the process referred to by the process file + descriptor *pidfd*, into the calling process. The returned file descriptor + is :ref:`non-inheritable `. + + *flags* is reserved, and currently must be ``0``. + + See the :manpage:`pidfd_getfd(2)` man page for more details. + + .. availability:: Linux >= 5.6, Android >= :func:`build-time ` API level 31 + .. versionadded:: next .. function:: plock(op, /) @@ -4704,9 +5149,8 @@ written in Python, such as a mail server's external command delivery program. Use :class:`subprocess.Popen` or :func:`subprocess.run` to control options like encodings. - .. deprecated:: 3.14 - The function is :term:`soft deprecated` and should no longer be used to - write new code. The :mod:`subprocess` module is recommended instead. + .. soft-deprecated:: 3.14 + The :mod:`subprocess` module is recommended instead. .. function:: posix_spawn(path, argv, env, *, file_actions=None, \ @@ -4934,9 +5378,8 @@ written in Python, such as a mail server's external command delivery program. .. versionchanged:: 3.6 Accepts a :term:`path-like object`. - .. deprecated:: 3.14 - These functions are :term:`soft deprecated` and should no longer be used - to write new code. The :mod:`subprocess` module is recommended instead. + .. soft-deprecated:: 3.14 + The :mod:`subprocess` module is recommended instead. .. data:: P_NOWAIT @@ -5610,7 +6053,7 @@ Miscellaneous System Information .. versionchanged:: 3.13 If :option:`-X cpu_count <-X>` is given or :envvar:`PYTHON_CPU_COUNT` is set, - :func:`cpu_count` returns the overridden value *n*. + :func:`cpu_count` returns the override value *n*. .. function:: getloadavg() @@ -5632,7 +6075,7 @@ Miscellaneous System Information in the **system**. If :option:`-X cpu_count <-X>` is given or :envvar:`PYTHON_CPU_COUNT` is set, - :func:`process_cpu_count` returns the overridden value *n*. + :func:`process_cpu_count` returns the override value *n*. See also the :func:`sched_getaffinity` function. diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index ebf5756146df92..ab92c142c37a4f 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -311,7 +311,7 @@ Pure paths provide the following methods and properties: .. attribute:: PurePath.parser The implementation of the :mod:`os.path` module used for low-level path - parsing and joining: either :mod:`posixpath` or :mod:`ntpath`. + parsing and joining: either :mod:`!posixpath` or :mod:`!ntpath`. .. versionadded:: 3.13 @@ -486,6 +486,10 @@ Pure paths provide the following methods and properties: >>> PurePosixPath('my/library').stem 'library' + .. versionchanged:: 3.14 + + A single dot ("``.``") is considered a valid suffix. + .. method:: PurePath.as_posix() @@ -1262,6 +1266,8 @@ Reading and writing files >>> p.read_text() 'Text file contents' + Return the number of characters written. + An existing file of the same name is overwritten. The optional parameters have the same meaning as in :func:`open`. @@ -1282,6 +1288,8 @@ Reading and writing files >>> p.read_bytes() b'Binary file contents' + Return the number of bytes written. + An existing file of the same name is overwritten. .. versionadded:: 3.5 @@ -1331,6 +1339,10 @@ Reading directories PosixPath('setup.py'), PosixPath('test_pathlib.py')] + .. note:: + The paths are returned in no particular order. + If you need a specific order, sort the results. + .. seealso:: :ref:`pathlib-pattern-language` documentation. @@ -1343,6 +1355,11 @@ Reading directories ``False``, this method follows symlinks except when expanding "``**``" wildcards. Set *recurse_symlinks* to ``True`` to always follow symlinks. + .. note:: + Any :exc:`OSError` exceptions raised from scanning the filesystem are + suppressed. This includes :exc:`PermissionError` when accessing + directories without read permission. + .. audit-event:: pathlib.Path.glob self,pattern pathlib.Path.glob .. versionchanged:: 3.12 @@ -1365,6 +1382,15 @@ Reading directories Glob the given relative *pattern* recursively. This is like calling :func:`Path.glob` with "``**/``" added in front of the *pattern*. + .. note:: + The paths are returned in no particular order. + If you need a specific order, sort the results. + + .. note:: + Any :exc:`OSError` exceptions raised from scanning the filesystem are + suppressed. This includes :exc:`PermissionError` when accessing + directories without read permission. + .. seealso:: :ref:`pathlib-pattern-language` and :meth:`Path.glob` documentation. @@ -1492,7 +1518,8 @@ Creating files and directories :meth:`~Path.write_bytes` methods are often used to create files. -.. method:: Path.mkdir(mode=0o777, parents=False, exist_ok=False) +.. method:: Path.mkdir(mode=0o777, parents=False, exist_ok=False, *, \ + parent_mode=None) Create a new directory at this given path. If *mode* is given, it is combined with the process's ``umask`` value to determine the file mode @@ -1503,6 +1530,12 @@ Creating files and directories as needed; they are created with the default permissions without taking *mode* into account (mimicking the POSIX ``mkdir -p`` command). + If *parent_mode* is not ``None``, it is used as the mode for any + newly-created, intermediate-level directories when *parents* is true. + Like *mode*, it is combined with the process's ``umask`` value. + Otherwise, intermediate directories are created with the default + permissions (also subject to the umask). + If *parents* is false (the default), a missing parent raises :exc:`FileNotFoundError`. @@ -1516,6 +1549,9 @@ Creating files and directories .. versionchanged:: 3.5 The *exist_ok* parameter was added. + .. versionadded:: 3.15 + The *parent_mode* parameter. + .. method:: Path.symlink_to(target, target_is_directory=False) @@ -1875,7 +1911,7 @@ Below is a table mapping various :mod:`os` functions to their corresponding :class:`PurePath`/:class:`Path` equivalent. ===================================== ============================================== -:mod:`os` and :mod:`os.path` :mod:`pathlib` +:mod:`os` and :mod:`os.path` :mod:`!pathlib` ===================================== ============================================== :func:`os.path.dirname` :attr:`PurePath.parent` :func:`os.path.basename` :attr:`PurePath.name` @@ -1934,7 +1970,7 @@ Protocols :synopsis: pathlib types for static type checking -The :mod:`pathlib.types` module provides types for static type checking. +The :mod:`!pathlib.types` module provides types for static type checking. .. versionadded:: 3.14 diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index f4b51664545be5..bfe017a5c8fe1c 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -1,7 +1,7 @@ .. _debugger: -:mod:`pdb` --- The Python Debugger -================================== +:mod:`!pdb` --- The Python Debugger +=================================== .. module:: pdb :synopsis: The Python debugger for interactive interpreters. @@ -12,7 +12,7 @@ -------------- -The module :mod:`pdb` defines an interactive source code debugger for Python +The module :mod:`!pdb` defines an interactive source code debugger for Python programs. It supports setting (conditional) breakpoints and single stepping at the source line level, inspection of stack frames, source code listing, and evaluation of arbitrary Python code in the context of any stack frame. It also @@ -75,9 +75,14 @@ The debugger's prompt is ``(Pdb)``, which is the indicator that you are in debug arguments of the ``p`` command. +.. _pdb-cli: + +Command-line interface +---------------------- + .. program:: pdb -You can also invoke :mod:`pdb` from the command line to debug other scripts. For +You can also invoke :mod:`!pdb` from the command line to debug other scripts. For example:: python -m pdb [-c command] (-m module | -p pid | pyfile) [args ...] @@ -333,7 +338,7 @@ access further features, you have to do this yourself: .. _debugger-commands: -Debugger Commands +Debugger commands ----------------- The commands recognized by the debugger are listed below. Most commands can be @@ -515,7 +520,8 @@ can be overridden by the local file. To remove all commands from a breakpoint, type ``commands`` and follow it immediately with ``end``; that is, give no commands. - With no *bpnumber* argument, ``commands`` refers to the last breakpoint set. + With no *bpnumber* argument, ``commands`` refers to the most recently set + breakpoint that still exists. You can use breakpoint commands to start your program up again. Simply use the :pdbcmd:`continue` command, or :pdbcmd:`step`, diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 007c9fe1b950cf..8eadc2cf2b1ef0 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -4,9 +4,6 @@ .. module:: pickle :synopsis: Convert Python objects to streams of bytes and back. -.. sectionauthor:: Jim Kerr . -.. sectionauthor:: Barry Warsaw - **Source code:** :source:`Lib/pickle.py` .. index:: @@ -19,7 +16,7 @@ -------------- -The :mod:`pickle` module implements binary protocols for serializing and +The :mod:`!pickle` module implements binary protocols for serializing and de-serializing a Python object structure. *"Pickling"* is the process whereby a Python object hierarchy is converted into a byte stream, and *"unpickling"* is the inverse operation, whereby a byte stream @@ -50,35 +47,22 @@ Comparison with ``marshal`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Python has a more primitive serialization module called :mod:`marshal`, but in -general :mod:`pickle` should always be the preferred way to serialize Python +general :mod:`!pickle` should always be the preferred way to serialize Python objects. :mod:`marshal` exists primarily to support Python's :file:`.pyc` files. -The :mod:`pickle` module differs from :mod:`marshal` in several significant ways: - -* The :mod:`pickle` module keeps track of the objects it has already serialized, - so that later references to the same object won't be serialized again. - :mod:`marshal` doesn't do this. - - This has implications both for recursive objects and object sharing. Recursive - objects are objects that contain references to themselves. These are not - handled by marshal, and in fact, attempting to marshal recursive objects will - crash your Python interpreter. Object sharing happens when there are multiple - references to the same object in different places in the object hierarchy being - serialized. :mod:`pickle` stores such objects only once, and ensures that all - other references point to the master copy. Shared objects remain shared, which - can be very important for mutable objects. +The :mod:`!pickle` module differs from :mod:`marshal` in several significant ways: * :mod:`marshal` cannot be used to serialize user-defined classes and their - instances. :mod:`pickle` can save and restore class instances transparently, + instances. :mod:`!pickle` can save and restore class instances transparently, however the class definition must be importable and live in the same module as - when the object was stored. + when the object was pickled. * The :mod:`marshal` serialization format is not guaranteed to be portable across Python versions. Because its primary job in life is to support :file:`.pyc` files, the Python implementers reserve the right to change the serialization format in non-backwards compatible ways should the need arise. - The :mod:`pickle` serialization format is guaranteed to be backwards compatible + The :mod:`!pickle` serialization format is guaranteed to be backwards compatible across Python releases provided a compatible pickle protocol is chosen and pickling and unpickling code deals with Python 2 to Python 3 type differences if your data is crossing that unique breaking change language boundary. @@ -123,17 +107,17 @@ Data stream format .. index:: single: External Data Representation -The data format used by :mod:`pickle` is Python-specific. This has the +The data format used by :mod:`!pickle` is Python-specific. This has the advantage that there are no restrictions imposed by external standards such as JSON (which can't represent pointer sharing); however it means that non-Python programs may not be able to reconstruct pickled Python objects. -By default, the :mod:`pickle` data format uses a relatively compact binary +By default, the :mod:`!pickle` data format uses a relatively compact binary representation. If you need optimal size characteristics, you can efficiently :doc:`compress ` pickled data. The module :mod:`pickletools` contains tools for analyzing data streams -generated by :mod:`pickle`. :mod:`pickletools` source code has extensive +generated by :mod:`!pickle`. :mod:`pickletools` source code has extensive comments about opcodes used by pickle protocols. There are currently 6 different protocols which can be used for pickling. @@ -167,9 +151,9 @@ to read the pickle produced. .. note:: Serialization is a more primitive notion than persistence; although - :mod:`pickle` reads and writes file objects, it does not handle the issue of + :mod:`!pickle` reads and writes file objects, it does not handle the issue of naming persistent objects, nor the (even more complicated) issue of concurrent - access to persistent objects. The :mod:`pickle` module can transform a complex + access to persistent objects. The :mod:`!pickle` module can transform a complex object into a byte stream and it can transform the byte stream into an object with the same internal structure. Perhaps the most obvious thing to do with these byte streams is to write them onto a file, but it is also conceivable to @@ -186,7 +170,7 @@ Similarly, to de-serialize a data stream, you call the :func:`loads` function. However, if you want more control over serialization and de-serialization, you can create a :class:`Pickler` or an :class:`Unpickler` object, respectively. -The :mod:`pickle` module provides the following constants: +The :mod:`!pickle` module provides the following constants: .. data:: HIGHEST_PROTOCOL @@ -217,7 +201,7 @@ The :mod:`pickle` module provides the following constants: The default protocol is 5. -The :mod:`pickle` module provides the following functions to make the pickling +The :mod:`!pickle` module provides the following functions to make the pickling process more convenient: .. function:: dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None) @@ -275,7 +259,7 @@ process more convenient: The *buffers* argument was added. -The :mod:`pickle` module defines three exceptions: +The :mod:`!pickle` module defines three exceptions: .. exception:: PickleError @@ -300,7 +284,7 @@ The :mod:`pickle` module defines three exceptions: IndexError. -The :mod:`pickle` module exports three classes, :class:`Pickler`, +The :mod:`!pickle` module exports three classes, :class:`Pickler`, :class:`Unpickler` and :class:`PickleBuffer`: .. class:: Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None) @@ -532,7 +516,7 @@ The following types can be pickled: * classes accessible from the top level of a module; -* instances of such classes whose the result of calling :meth:`~object.__getstate__` +* instances of such classes for which the result of calling :meth:`~object.__getstate__` is picklable (see section :ref:`pickle-inst` for details). Attempts to pickle unpicklable objects will raise the :exc:`PicklingError` @@ -601,7 +585,7 @@ methods: .. method:: object.__getnewargs_ex__() - In protocols 2 and newer, classes that implements the + In protocols 2 and newer, classes that implement the :meth:`__getnewargs_ex__` method can dictate the values passed to the :meth:`__new__` method upon unpickling. The method must return a pair ``(args, kwargs)`` where *args* is a tuple of positional arguments @@ -709,7 +693,10 @@ or both. If a string is returned, the string should be interpreted as the name of a global variable. It should be the object's local name relative to its module; the pickle module searches the module namespace to determine the - object's module. This behaviour is typically useful for singletons. + object's module: for a given ``obj`` to be pickled, the ``__module__`` + attribute is looked up on ``obj`` directly, which falls back to a lookup + on the type of ``obj`` if no ``__module__`` instance attribute is set. + This behaviour is typically useful for singletons. When a tuple is returned, it must be between two and six items long. Optional items can either be omitted, or ``None`` can be provided as their @@ -732,8 +719,8 @@ or both. These items will be appended to the object either using ``obj.append(item)`` or, in batch, using ``obj.extend(list_of_items)``. This is primarily used for list subclasses, but may be used by other - classes as long as they have - :ref:`append and extend methods ` with + classes as long as they have :meth:`~sequence.append` + and :meth:`~sequence.extend` methods with the appropriate signature. (Whether :meth:`!append` or :meth:`!extend` is used depends on which pickle protocol version is used as well as the number of items to append, so both must be supported.) @@ -773,13 +760,13 @@ Persistence of External Objects single: persistent_id (pickle protocol) single: persistent_load (pickle protocol) -For the benefit of object persistence, the :mod:`pickle` module supports the +For the benefit of object persistence, the :mod:`!pickle` module supports the notion of a reference to an object outside the pickled data stream. Such objects are referenced by a persistent ID, which should be either a string of alphanumeric characters (for protocol 0) [#]_ or just an arbitrary object (for any newer protocol). -The resolution of such persistent IDs is not defined by the :mod:`pickle` +The resolution of such persistent IDs is not defined by the :mod:`!pickle` module; it will delegate this resolution to the user-defined methods on the pickler and unpickler, :meth:`~Pickler.persistent_id` and :meth:`~Unpickler.persistent_load` respectively. @@ -973,10 +960,10 @@ Out-of-band Buffers .. versionadded:: 3.8 -In some contexts, the :mod:`pickle` module is used to transfer massive amounts +In some contexts, the :mod:`!pickle` module is used to transfer massive amounts of data. Therefore, it can be important to minimize the number of memory copies, to preserve performance and resource consumption. However, normal -operation of the :mod:`pickle` module, as it transforms a graph-like structure +operation of the :mod:`!pickle` module, as it transforms a graph-like structure of objects into a sequential stream of bytes, intrinsically involves copying data to and from the pickle stream. @@ -995,8 +982,8 @@ for any large data. A :class:`PickleBuffer` object *signals* that the underlying buffer is eligible for out-of-band data transfer. Those objects remain compatible -with normal usage of the :mod:`pickle` module. However, consumers can also -opt-in to tell :mod:`pickle` that they will handle those buffers by +with normal usage of the :mod:`!pickle` module. However, consumers can also +opt-in to tell :mod:`!pickle` that they will handle those buffers by themselves. Consumer API @@ -1172,7 +1159,7 @@ Performance Recent versions of the pickle protocol (from protocol 2 and upwards) feature efficient binary encodings for several common features and built-in types. -Also, the :mod:`pickle` module has a transparent optimizer written in C. +Also, the :mod:`!pickle` module has a transparent optimizer written in C. .. _pickle-example: @@ -1215,7 +1202,7 @@ The following example reads the resulting pickled data. :: Command-line interface ---------------------- -The :mod:`pickle` module can be invoked as a script from the command line, +The :mod:`!pickle` module can be invoked as a script from the command line, it will display contents of the pickle files. However, when the pickle file that you want to examine comes from an untrusted source, ``-m pickletools`` is a safer option because it does not execute pickle bytecode, see @@ -1243,7 +1230,7 @@ The following option is accepted: Tools for working with and analyzing pickled data. Module :mod:`shelve` - Indexed databases of objects; uses :mod:`pickle`. + Indexed databases of objects; uses :mod:`!pickle`. Module :mod:`copy` Shallow and deep object copying. diff --git a/Doc/library/pickletools.rst b/Doc/library/pickletools.rst index 30fc2962e0bf78..769ca60af1837e 100644 --- a/Doc/library/pickletools.rst +++ b/Doc/library/pickletools.rst @@ -15,7 +15,7 @@ This module contains various constants relating to the intimate details of the few useful functions for analyzing pickled data. The contents of this module are useful for Python core developers who are working on the :mod:`pickle`; ordinary users of the :mod:`pickle` module probably won't find the -:mod:`pickletools` module relevant. +:mod:`!pickletools` module relevant. .. _pickletools-cli: @@ -79,6 +79,9 @@ Command-line options A pickle file to read, or ``-`` to indicate reading from standard input. +.. versionadded:: 3.15 + Output is in color by default and can be + :ref:`controlled using environment variables `. Programmatic interface diff --git a/Doc/library/pkgutil.rst b/Doc/library/pkgutil.rst index 47d24b6f7d06bb..5473a367c49a3a 100644 --- a/Doc/library/pkgutil.rst +++ b/Doc/library/pkgutil.rst @@ -151,26 +151,50 @@ support. :meth:`get_data ` API. The *package* argument should be the name of a package, in standard module format (``foo.bar``). The *resource* argument should be in the form of a relative - filename, using ``/`` as the path separator. The parent directory name - ``..`` is not allowed, and nor is a rooted name (starting with a ``/``). + filename, using ``/`` as the path separator. The function returns a binary string that is the contents of the specified resource. + This function uses the :term:`loader` method + :func:`~importlib.abc.FileLoader.get_data` + to support modules installed in the filesystem, but also in zip files, + databases, or elsewhere. + For packages located in the filesystem, which have already been imported, this is the rough equivalent of:: d = os.path.dirname(sys.modules[package].__file__) data = open(os.path.join(d, resource), 'rb').read() + Like the :func:`open` function, :func:`!get_data` can follow parent + directories (``../``) and absolute paths (starting with ``/`` or ``C:/``, + for example). + It can open compilation/installation artifacts like ``.py`` and ``.pyc`` + files or files with :func:`reserved filenames `. + To be compatible with non-filesystem loaders, avoid using these features. + + .. warning:: + + This function is intended for trusted input. + It does not verify that *resource* "belongs" to *package*. + + If you use a user-provided *resource* path, consider verifying it. + For example, require an alphanumeric filename with a known extension, or + install and check a list of known resources. + If the package cannot be located or loaded, or it uses a :term:`loader` which does not support :meth:`get_data `, then ``None`` is returned. In particular, the :term:`loader` for :term:`namespace packages ` does not support :meth:`get_data `. + .. seealso:: -.. function:: resolve_name(name) + The :mod:`importlib.resources` module provides structured access to + module resources. + +.. function:: resolve_name(name, *, strict=False) Resolve a name to an object. @@ -184,6 +208,7 @@ support. * ``W(.W)*`` * ``W(.W)*:(W(.W)*)?`` + * ``W(.W)*:(W(.W)*)`` The first form is intended for backward compatibility only. It assumes that some part of the dotted name is a package, and the rest is an object @@ -198,6 +223,11 @@ support. hierarchy within that package. Only one import is needed in this form. If it ends with the colon, then a module object is returned. + The first two forms are accepted when ``strict=False`` (the default). + + The third form requires both the module name and callable, separated by + a colon. Only this form is accepted when ``strict=True``. + The function will return an object (which might be a module), or raise one of the following exceptions: @@ -209,3 +239,7 @@ support. hierarchy within the imported package to get to the desired object. .. versionadded:: 3.9 + + .. versionchanged:: 3.15 + + The optional keyword-only ``strict`` flag was added. diff --git a/Doc/library/platform.rst b/Doc/library/platform.rst index 37df13f8a1eb8c..1d30966794fd1b 100644 --- a/Doc/library/platform.rst +++ b/Doc/library/platform.rst @@ -4,9 +4,6 @@ .. module:: platform :synopsis: Retrieves as much platform identifying data as possible. -.. moduleauthor:: Marc-André Lemburg -.. sectionauthor:: Bjorn Pettersen - **Source code:** :source:`Lib/platform.py` -------------- @@ -56,6 +53,8 @@ Cross platform Returns the machine type, e.g. ``'AMD64'``. An empty string is returned if the value cannot be determined. + The output is platform-dependent and may differ in casing and naming conventions. + .. function:: node() @@ -187,6 +186,14 @@ Cross platform .. versionchanged:: 3.9 :attr:`processor` is resolved late instead of immediately. +.. function:: invalidate_caches() + + Clear out the internal cache of information, such as the :func:`uname`. + This is typically useful when the platform's :func:`node` is changed + by an external process and one needs to retrieve the updated value. + + .. versionadded:: 3.14 + Windows platform ---------------- @@ -347,7 +354,7 @@ Android platform Command-line usage ------------------ -:mod:`platform` can also be invoked directly using the :option:`-m` +:mod:`!platform` can also be invoked directly using the :option:`-m` switch of the interpreter:: python -m platform [--terse] [--nonaliased] [{nonaliased,terse} ...] @@ -370,14 +377,3 @@ The following options are accepted: You can also pass one or more positional arguments (``terse``, ``nonaliased``) to explicitly control the output format. These behave similarly to their corresponding options. - -Miscellaneous -------------- - -.. function:: invalidate_caches() - - Clear out the internal cache of information, such as the :func:`uname`. - This is typically useful when the platform's :func:`node` is changed - by an external process and one needs to retrieve the updated value. - - .. versionadded:: 3.14 diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst index 415c4b45c4f100..72140e41675c35 100644 --- a/Doc/library/plistlib.rst +++ b/Doc/library/plistlib.rst @@ -4,10 +4,6 @@ .. module:: plistlib :synopsis: Generate and parse Apple plist files. -.. moduleauthor:: Jack Jansen -.. sectionauthor:: Georg Brandl -.. (harvested from docstrings in the original file) - **Source code:** :source:`Lib/plistlib.py` .. index:: @@ -22,7 +18,7 @@ and XML plist files. The property list (``.plist``) file format is a simple serialization supporting basic object types, like dictionaries, lists, numbers and strings. Usually the -top level object is a dictionary. +top level object is a dictionary or a frozen dictionary. To write out and to parse a plist file, use the :func:`dump` and :func:`load` functions. @@ -184,7 +180,7 @@ Examples Generating a plist:: - import datetime + import datetime as dt import plistlib pl = dict( @@ -200,7 +196,7 @@ Generating a plist:: ), someData = b"", someMoreData = b"" * 10, - aDate = datetime.datetime.now() + aDate = dt.datetime.now() ) print(plistlib.dumps(pl).decode()) diff --git a/Doc/library/poplib.rst b/Doc/library/poplib.rst index 23f20b00e6dc6d..cd3a58016e9c12 100644 --- a/Doc/library/poplib.rst +++ b/Doc/library/poplib.rst @@ -4,9 +4,6 @@ .. module:: poplib :synopsis: POP3 protocol client (requires sockets). -.. sectionauthor:: Andrew T. Csillag -.. revised by ESR, January 2000 - **Source code:** :source:`Lib/poplib.py` .. index:: pair: POP3; protocol @@ -30,7 +27,7 @@ mailserver supports IMAP, you would be better off using the .. include:: ../includes/wasm-notavail.rst -The :mod:`poplib` module provides two classes: +The :mod:`!poplib` module provides two classes: .. class:: POP3(host, port=POP3_PORT[, timeout]) @@ -86,7 +83,7 @@ The :mod:`poplib` module provides two classes: .. versionchanged:: 3.12 The deprecated *keyfile* and *certfile* parameters have been removed. -One exception is defined as an attribute of the :mod:`poplib` module: +One exception is defined as an attribute of the :mod:`!poplib` module: .. exception:: error_proto diff --git a/Doc/library/posix.rst b/Doc/library/posix.rst index 14ab3e91e8a8e4..7d43b5d4cf7657 100644 --- a/Doc/library/posix.rst +++ b/Doc/library/posix.rst @@ -2,7 +2,6 @@ ==================================================== .. module:: posix - :platform: Unix :synopsis: The most common POSIX system calls (normally used via module os). -------------- @@ -17,10 +16,10 @@ interface). **Do not import this module directly.** Instead, import the module :mod:`os`, which provides a *portable* version of this interface. On Unix, the :mod:`os` -module provides a superset of the :mod:`posix` interface. On non-Unix operating -systems the :mod:`posix` module is not available, but a subset is always +module provides a superset of the :mod:`!posix` interface. On non-Unix operating +systems the :mod:`!posix` module is not available, but a subset is always available through the :mod:`os` interface. Once :mod:`os` is imported, there is -*no* performance penalty in using it instead of :mod:`posix`. In addition, +*no* performance penalty in using it instead of :mod:`!posix`. In addition, :mod:`os` provides some additional functionality, such as automatically calling :func:`~os.putenv` when an entry in ``os.environ`` is changed. @@ -37,8 +36,6 @@ Large File Support single: large files single: file; large files -.. sectionauthor:: Steve Clift - Several operating systems (including AIX and Solaris) provide support for files that are larger than 2 GiB from a C programming model where :c:expr:`int` and :c:expr:`long` are 32-bit values. This is typically accomplished @@ -67,7 +64,7 @@ Notable Module Contents ----------------------- In addition to many functions described in the :mod:`os` module documentation, -:mod:`posix` defines the following data item: +:mod:`!posix` defines the following data item: .. data:: environ @@ -91,4 +88,4 @@ In addition to many functions described in the :mod:`os` module documentation, which updates the environment on modification. Note also that updating :data:`os.environ` will render this dictionary obsolete. Use of the :mod:`os` module version of this is recommended over direct access to the - :mod:`posix` module. + :mod:`!posix` module. diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index 2985f31bacb47a..4f043fbb3a46df 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -4,14 +4,11 @@ .. module:: pprint :synopsis: Data pretty printer. -.. moduleauthor:: Fred L. Drake, Jr. -.. sectionauthor:: Fred L. Drake, Jr. - **Source code:** :source:`Lib/pprint.py` -------------- -The :mod:`pprint` module provides a capability to "pretty-print" arbitrary +The :mod:`!pprint` module provides a capability to "pretty-print" arbitrary Python data structures in a form which can be used as input to the interpreter. If the formatted structures include objects which are not fundamental Python types, the representation may not be loadable. This may be the case if objects @@ -22,8 +19,6 @@ The formatted representation keeps objects on a single line if it can, and breaks them onto multiple lines if they don't fit within the allowed width, adjustable by the *width* parameter defaulting to 80 characters. -Dictionaries are sorted by key before the display is computed. - .. versionchanged:: 3.9 Added support for pretty-printing :class:`types.SimpleNamespace`. @@ -36,7 +31,8 @@ Functions --------- .. function:: pp(object, stream=None, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=False, underscore_numbers=False) + compact=False, expand=False, sort_dicts=False, \ + underscore_numbers=False) Prints the formatted representation of *object*, followed by a newline. This function may be used in the interactive interpreter @@ -74,6 +70,13 @@ Functions each item of a sequence will be formatted on a separate line, otherwise as many items as will fit within the *width* will be formatted on each output line. + Incompatible with *expand*. + + :param bool expand: + If ``True``, + opening parentheses and brackets will be followed by a newline and the + following content will be indented by one level, similar to + pretty-printed JSON. Incompatible with *compact*. :param bool sort_dicts: If ``True``, dictionaries will be formatted with @@ -100,7 +103,8 @@ Functions .. function:: pprint(object, stream=None, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False) + compact=False, expand=False, sort_dicts=True, \ + underscore_numbers=False) Alias for :func:`~pprint.pp` with *sort_dicts* set to ``True`` by default, which would automatically sort the dictionaries' keys, @@ -108,10 +112,11 @@ Functions .. function:: pformat(object, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False) + compact=False, expand=False, sort_dicts=True, \ + underscore_numbers=False) Return the formatted representation of *object* as a string. *indent*, - *width*, *depth*, *compact*, *sort_dicts* and *underscore_numbers* are + *width*, *depth*, *compact*, *expand*, *sort_dicts* and *underscore_numbers* are passed to the :class:`PrettyPrinter` constructor as formatting parameters and their meanings are as described in the documentation above. @@ -155,7 +160,8 @@ PrettyPrinter Objects .. index:: single: ...; placeholder .. class:: PrettyPrinter(indent=1, width=80, depth=None, stream=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False) + compact=False, expand=False, sort_dicts=True, \ + underscore_numbers=False) Construct a :class:`PrettyPrinter` instance. @@ -179,6 +185,22 @@ PrettyPrinter Objects 'knights', 'ni'], 'spam', 'eggs', 'lumberjack', 'knights', 'ni'] + >>> pp = pprint.PrettyPrinter(width=41, expand=True, indent=3) + >>> pp.pprint(stuff) + [ + [ + 'spam', + 'eggs', + 'lumberjack', + 'knights', + 'ni', + ], + 'spam', + 'eggs', + 'lumberjack', + 'knights', + 'ni', + ] >>> tup = ('spam', ('eggs', ('lumberjack', ('knights', ('ni', ('dead', ... ('parrot', ('fresh fruit',)))))))) >>> pp = pprint.PrettyPrinter(depth=6) @@ -198,6 +220,9 @@ PrettyPrinter Objects .. versionchanged:: 3.11 No longer attempts to write to :data:`!sys.stdout` if it is ``None``. + .. versionchanged:: 3.15 + Added the *expand* parameter. + :class:`PrettyPrinter` instances have the following methods: @@ -420,3 +445,72 @@ cannot be split, the specified width will be exceeded:: 'requires_python': None, 'summary': 'A sample Python project', 'version': '1.2.0'} + +Lastly, we can format like pretty-printed JSON with the *expand* parameter. +Best results are achieved with a higher *indent* value:: + + >>> pprint.pp(project_info, indent=4, expand=True) + { + 'author': 'The Python Packaging Authority', + 'author_email': 'pypa-dev@googlegroups.com', + 'bugtrack_url': None, + 'classifiers': [ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Topic :: Software Development :: Build Tools', + ], + 'description': 'A sample Python project\n' + '=======================\n' + '\n' + 'This is the description file for the project.\n' + '\n' + 'The file should use UTF-8 encoding and be written using ReStructured ' + 'Text. It\n' + 'will be used to generate the project webpage on PyPI, and should be ' + 'written for\n' + 'that purpose.\n' + '\n' + 'Typical contents for this file would include an overview of the project, ' + 'basic\n' + 'usage examples, etc. Generally, including the project changelog in here ' + 'is not\n' + 'a good idea, although a simple "What\'s New" section for the most recent ' + 'version\n' + 'may be appropriate.', + 'description_content_type': None, + 'docs_url': None, + 'download_url': 'UNKNOWN', + 'downloads': {'last_day': -1, 'last_month': -1, 'last_week': -1}, + 'dynamic': None, + 'home_page': 'https://github.com/pypa/sampleproject', + 'keywords': 'sample setuptools development', + 'license': 'MIT', + 'license_expression': None, + 'license_files': None, + 'maintainer': None, + 'maintainer_email': None, + 'name': 'sampleproject', + 'package_url': 'https://pypi.org/project/sampleproject/', + 'platform': 'UNKNOWN', + 'project_url': 'https://pypi.org/project/sampleproject/', + 'project_urls': { + 'Download': 'UNKNOWN', + 'Homepage': 'https://github.com/pypa/sampleproject', + }, + 'provides_extra': None, + 'release_url': 'https://pypi.org/project/sampleproject/1.2.0/', + 'requires_dist': None, + 'requires_python': None, + 'summary': 'A sample Python project', + 'version': '1.2.0', + 'yanked': False, + 'yanked_reason': None, + } diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst index c5b88c22325404..f7e85d1598727f 100644 --- a/Doc/library/profile.rst +++ b/Doc/library/profile.rst @@ -1,523 +1,64 @@ .. _profile: -******************** -The Python Profilers -******************** +**************************************** +:mod:`!profile` --- Pure Python profiler +**************************************** -**Source code:** :source:`Lib/profile.py`, :source:`Lib/pstats.py`, and :source:`Lib/profile/sample.py` +.. module:: profile + :synopsis: Pure Python profiler (deprecated). + :deprecated: --------------- +**Source code:** :source:`Lib/profile.py` -.. _profiler-introduction: +-------------- -Introduction to the profilers -============================= +.. deprecated-removed:: 3.15 3.17 -.. index:: - single: statistical profiling - single: profiling, statistical - single: deterministic profiling - single: profiling, deterministic +The :mod:`!profile` module is deprecated and will be removed in Python 3.17. +Use :mod:`profiling.tracing` instead. -Python provides both :dfn:`statistical profiling` and :dfn:`deterministic profiling` of -Python programs. A :dfn:`profile` is a set of statistics that describes how -often and for how long various parts of the program executed. These statistics -can be formatted into reports via the :mod:`pstats` module. +The :mod:`!profile` module provides a pure Python implementation of a +deterministic profiler. While useful for understanding profiler internals or +extending profiler behavior through subclassing, its pure Python implementation +introduces significant overhead compared to the C-based :mod:`profiling.tracing` +module. -The Python standard library provides three different profiling implementations: +For most profiling tasks, use: -**Statistical Profiler:** +- :mod:`profiling.sampling` for production debugging with zero overhead +- :mod:`profiling.tracing` for development and testing -1. :mod:`profile.sample` provides statistical profiling of running Python processes - using periodic stack sampling. It can attach to any running Python process without - requiring code modification or restart, making it ideal for production debugging. -**Deterministic Profilers:** +Migration +========= -2. :mod:`cProfile` is recommended for development and testing; it's a C extension with - reasonable overhead that makes it suitable for profiling long-running - programs. Based on :mod:`lsprof`, contributed by Brett Rosen and Ted - Czotter. +Migrating from :mod:`!profile` to :mod:`profiling.tracing` is straightforward. +The APIs are compatible:: -3. :mod:`profile`, a pure Python module whose interface is imitated by - :mod:`cProfile`, but which adds significant overhead to profiled programs. - If you're trying to extend the profiler in some way, the task might be easier - with this module. Originally designed and written by Jim Roskind. + # Old (deprecated) + import profile + profile.run('my_function()') -.. note:: + # New (recommended) + import profiling.tracing + profiling.tracing.run('my_function()') - The profiler modules are designed to provide an execution profile for a given - program, not for benchmarking purposes (for that, there is :mod:`timeit` for - reasonably accurate results). This particularly applies to benchmarking - Python code against C code: the profilers introduce overhead for Python code, - but not for C-level functions, and so the C code would seem faster than any - Python one. - -**Profiler Comparison:** - -+-------------------+----------------------+----------------------+----------------------+ -| Feature | Statistical | Deterministic | Deterministic | -| | (``profile.sample``) | (``cProfile``) | (``profile``) | -+===================+======================+======================+======================+ -| **Target** | Running process | Code you run | Code you run | -+-------------------+----------------------+----------------------+----------------------+ -| **Overhead** | Virtually none | Moderate | High | -+-------------------+----------------------+----------------------+----------------------+ -| **Accuracy** | Statistical approx. | Exact call counts | Exact call counts | -+-------------------+----------------------+----------------------+----------------------+ -| **Setup** | Attach to any PID | Instrument code | Instrument code | -+-------------------+----------------------+----------------------+----------------------+ -| **Use Case** | Production debugging | Development/testing | Profiler extension | -+-------------------+----------------------+----------------------+----------------------+ -| **Implementation**| C extension | C extension | Pure Python | -+-------------------+----------------------+----------------------+----------------------+ +For most code, replacing ``import profile`` with ``import profiling.tracing`` +(and using ``profiling.tracing`` instead of ``profile`` throughout) provides +a straightforward migration path. .. note:: - The statistical profiler (:mod:`profile.sample`) is recommended for most production - use cases due to its extremely low overhead and ability to profile running processes - without modification. It can attach to any Python process and collect performance - data with minimal impact on execution speed, making it ideal for debugging - performance issues in live applications. - - -.. _statistical-profiling: - -What Is Statistical Profiling? -============================== - -:dfn:`Statistical profiling` works by periodically interrupting a running -program to capture its current call stack. Rather than monitoring every -function entry and exit like deterministic profilers, it takes snapshots at -regular intervals to build a statistical picture of where the program spends -its time. - -The sampling profiler uses process memory reading (via system calls like -``process_vm_readv`` on Linux, ``vm_read`` on macOS, and ``ReadProcessMemory`` on -Windows) to attach to a running Python process and extract stack trace -information without requiring any code modification or restart of the target -process. This approach provides several key advantages over traditional -profiling methods. - -The fundamental principle is that if a function appears frequently in the -collected stack samples, it is likely consuming significant CPU time. By -analyzing thousands of samples, the profiler can accurately estimate the -relative time spent in different parts of the program. The statistical nature -means that while individual measurements may vary, the aggregate results -converge to represent the true performance characteristics of the application. - -Since statistical profiling operates externally to the target process, it -introduces virtually no overhead to the running program. The profiler process -runs separately and reads the target process memory without interrupting its -execution. This makes it suitable for profiling production systems where -performance impact must be minimized. - -The accuracy of statistical profiling improves with the number of samples -collected. Short-lived functions may be missed or underrepresented, while -long-running functions will be captured proportionally to their execution time. -This characteristic makes statistical profiling particularly effective for -identifying the most significant performance bottlenecks rather than providing -exhaustive coverage of all function calls. - -Statistical profiling excels at answering questions like "which functions -consume the most CPU time?" and "where should I focus optimization efforts?" -rather than "exactly how many times was this function called?" The trade-off -between precision and practicality makes it an invaluable tool for performance -analysis in real-world applications. - -.. _profile-instant: - -Instant User's Manual -===================== - -This section is provided for users that "don't want to read the manual." It -provides a very brief overview, and allows a user to rapidly perform profiling -on an existing application. - -**Statistical Profiling (Recommended for Production):** - -To profile an existing running process:: - - python -m profile.sample 1234 - -To profile with custom settings:: - - python -m profile.sample -i 50 -d 30 1234 - -**Deterministic Profiling (Development/Testing):** - -To profile a function that takes a single argument, you can do:: - - import cProfile - import re - cProfile.run('re.compile("foo|bar")') - -(Use :mod:`profile` instead of :mod:`cProfile` if the latter is not available on -your system.) - -The above action would run :func:`re.compile` and print profile results like -the following:: - - 214 function calls (207 primitive calls) in 0.002 seconds - - Ordered by: cumulative time - - ncalls tottime percall cumtime percall filename:lineno(function) - 1 0.000 0.000 0.002 0.002 {built-in method builtins.exec} - 1 0.000 0.000 0.001 0.001 :1() - 1 0.000 0.000 0.001 0.001 __init__.py:250(compile) - 1 0.000 0.000 0.001 0.001 __init__.py:289(_compile) - 1 0.000 0.000 0.000 0.000 _compiler.py:759(compile) - 1 0.000 0.000 0.000 0.000 _parser.py:937(parse) - 1 0.000 0.000 0.000 0.000 _compiler.py:598(_code) - 1 0.000 0.000 0.000 0.000 _parser.py:435(_parse_sub) - -The first line indicates that 214 calls were monitored. Of those calls, 207 -were :dfn:`primitive`, meaning that the call was not induced via recursion. The -next line: ``Ordered by: cumulative time`` indicates the output is sorted -by the ``cumtime`` values. The column headings include: - -ncalls - for the number of calls. - -tottime - for the total time spent in the given function (and excluding time made in - calls to sub-functions) - -percall - is the quotient of ``tottime`` divided by ``ncalls`` - -cumtime - is the cumulative time spent in this and all subfunctions (from invocation - till exit). This figure is accurate *even* for recursive functions. - -percall - is the quotient of ``cumtime`` divided by primitive calls - -filename:lineno(function) - provides the respective data of each function - -When there are two numbers in the first column (for example ``3/1``), it means -that the function recursed. The second value is the number of primitive calls -and the former is the total number of calls. Note that when the function does -not recurse, these two values are the same, and only the single figure is -printed. - -Instead of printing the output at the end of the profile run, you can save the -results to a file by specifying a filename to the :func:`run` function:: - - import cProfile - import re - cProfile.run('re.compile("foo|bar")', 'restats') - -The :class:`pstats.Stats` class reads profile results from a file and formats -them in various ways. - -.. _sampling-profiler-cli: - -Statistical Profiler Command Line Interface -=========================================== - -.. program:: profile.sample - -The :mod:`profile.sample` module can be invoked as a script to profile running processes:: - - python -m profile.sample [options] PID - -**Basic Usage Examples:** - -Profile process 1234 for 10 seconds with default settings:: - - python -m profile.sample 1234 - -Profile with custom interval and duration, save to file:: - - python -m profile.sample -i 50 -d 30 -o profile.stats 1234 - -Generate collapsed stacks to use with tools like `flamegraph.pl -`_:: - - python -m profile.sample --collapsed 1234 - -Profile all threads, sort by total time:: - - python -m profile.sample -a --sort-tottime 1234 - -Profile with real-time sampling statistics:: - - python -m profile.sample --realtime-stats 1234 - -**Command Line Options:** - -.. option:: PID - - Process ID of the Python process to profile (required) - -.. option:: -i, --interval INTERVAL - - Sampling interval in microseconds (default: 100) - -.. option:: -d, --duration DURATION - - Sampling duration in seconds (default: 10) - -.. option:: -a, --all-threads - - Sample all threads in the process instead of just the main thread - -.. option:: --realtime-stats - - Print real-time sampling statistics during profiling - -.. option:: --pstats - - Generate pstats output (default) - -.. option:: --collapsed - - Generate collapsed stack traces for flamegraphs - -.. option:: -o, --outfile OUTFILE - - Save output to a file - -**Sorting Options (pstats format only):** - -.. option:: --sort-nsamples - - Sort by number of direct samples - -.. option:: --sort-tottime - - Sort by total time - -.. option:: --sort-cumtime - - Sort by cumulative time (default) - -.. option:: --sort-sample-pct - - Sort by sample percentage - -.. option:: --sort-cumul-pct - - Sort by cumulative sample percentage - -.. option:: --sort-nsamples-cumul - - Sort by cumulative samples - -.. option:: --sort-name - - Sort by function name - -.. option:: -l, --limit LIMIT + The ``cProfile`` module remains available as a backward-compatible alias + to :mod:`profiling.tracing`. Existing code using ``import cProfile`` will + continue to work without modification. - Limit the number of rows in the output (default: 15) -.. option:: --no-summary +:mod:`!profile` and :mod:`!profiling.tracing` module reference +============================================================== - Disable the summary section in the output - -**Understanding Statistical Profile Output:** - -The statistical profiler produces output similar to deterministic profilers but with different column meanings:: - - Profile Stats: - nsamples sample% tottime (ms) cumul% cumtime (ms) filename:lineno(function) - 45/67 12.5 23.450 18.6 56.780 mymodule.py:42(process_data) - 23/23 6.4 15.230 6.4 15.230 :0(len) - -**Column Meanings:** - -- **nsamples**: ``direct/cumulative`` - Times function was directly executing / on call stack -- **sample%**: Percentage of total samples where function was directly executing -- **tottime**: Estimated time spent directly in this function -- **cumul%**: Percentage of samples where function was anywhere on call stack -- **cumtime**: Estimated cumulative time including called functions -- **filename:lineno(function)**: Location and name of the function - -.. _profile-cli: - -:mod:`profile.sample` Module Reference -======================================================= - -.. module:: profile.sample - :synopsis: Python statistical profiler. - -This section documents the programmatic interface for the :mod:`profile.sample` module. -For command-line usage, see :ref:`sampling-profiler-cli`. For conceptual information -about statistical profiling, see :ref:`statistical-profiling` - -.. function:: sample(pid, *, sort=2, sample_interval_usec=100, duration_sec=10, filename=None, all_threads=False, limit=None, show_summary=True, output_format="pstats", realtime_stats=False) - - Sample a Python process and generate profiling data. - - This is the main entry point for statistical profiling. It creates a - :class:`SampleProfiler`, collects stack traces from the target process, and - outputs the results in the specified format. - - :param int pid: Process ID of the target Python process - :param int sort: Sort order for pstats output (default: 2 for cumulative time) - :param int sample_interval_usec: Sampling interval in microseconds (default: 100) - :param int duration_sec: Duration to sample in seconds (default: 10) - :param str filename: Output filename (None for stdout/default naming) - :param bool all_threads: Whether to sample all threads (default: False) - :param int limit: Maximum number of functions to display (default: None) - :param bool show_summary: Whether to show summary statistics (default: True) - :param str output_format: Output format - 'pstats' or 'collapsed' (default: 'pstats') - :param bool realtime_stats: Whether to display real-time statistics (default: False) - - :raises ValueError: If output_format is not 'pstats' or 'collapsed' - - Examples:: - - # Basic usage - profile process 1234 for 10 seconds - import profile.sample - profile.sample.sample(1234) - - # Profile with custom settings - profile.sample.sample(1234, duration_sec=30, sample_interval_usec=50, all_threads=True) - - # Generate collapsed stack traces for flamegraph.pl - profile.sample.sample(1234, output_format='collapsed', filename='profile.collapsed') - -.. class:: SampleProfiler(pid, sample_interval_usec, all_threads) - - Low-level API for the statistical profiler. - - This profiler uses periodic stack sampling to collect performance data - from running Python processes with minimal overhead. It can attach to - any Python process by PID and collect stack traces at regular intervals. - - :param int pid: Process ID of the target Python process - :param int sample_interval_usec: Sampling interval in microseconds - :param bool all_threads: Whether to sample all threads or just the main thread - - .. method:: sample(collector, duration_sec=10) - - Sample the target process for the specified duration. - - Collects stack traces from the target process at regular intervals - and passes them to the provided collector for processing. - - :param collector: Object that implements ``collect()`` method to process stack traces - :param int duration_sec: Duration to sample in seconds (default: 10) - - The method tracks sampling statistics and can display real-time - information if realtime_stats is enabled. - -.. seealso:: - - :ref:`sampling-profiler-cli` - Command-line interface documentation for the statistical profiler. - -Deterministic Profiler Command Line Interface -============================================= - -.. program:: cProfile - -The files :mod:`cProfile` and :mod:`profile` can also be invoked as a script to -profile another script. For example:: - - python -m cProfile [-o output_file] [-s sort_order] (-m module | myscript.py) - -.. option:: -o - - Writes the profile results to a file instead of to stdout. - -.. option:: -s - - Specifies one of the :func:`~pstats.Stats.sort_stats` sort values - to sort the output by. - This only applies when :option:`-o ` is not supplied. - -.. option:: -m - - Specifies that a module is being profiled instead of a script. - - .. versionadded:: 3.7 - Added the ``-m`` option to :mod:`cProfile`. - - .. versionadded:: 3.8 - Added the ``-m`` option to :mod:`profile`. - -The :mod:`pstats` module's :class:`~pstats.Stats` class has a variety of methods -for manipulating and printing the data saved into a profile results file:: - - import pstats - from pstats import SortKey - p = pstats.Stats('restats') - p.strip_dirs().sort_stats(-1).print_stats() - -The :meth:`~pstats.Stats.strip_dirs` method removed the extraneous path from all -the module names. The :meth:`~pstats.Stats.sort_stats` method sorted all the -entries according to the standard module/line/name string that is printed. The -:meth:`~pstats.Stats.print_stats` method printed out all the statistics. You -might try the following sort calls:: - - p.sort_stats(SortKey.NAME) - p.print_stats() - -The first call will actually sort the list by function name, and the second call -will print out the statistics. The following are some interesting calls to -experiment with:: - - p.sort_stats(SortKey.CUMULATIVE).print_stats(10) - -This sorts the profile by cumulative time in a function, and then only prints -the ten most significant lines. If you want to understand what algorithms are -taking time, the above line is what you would use. - -If you were looking to see what functions were looping a lot, and taking a lot -of time, you would do:: - - p.sort_stats(SortKey.TIME).print_stats(10) - -to sort according to time spent within each function, and then print the -statistics for the top ten functions. - -You might also try:: - - p.sort_stats(SortKey.FILENAME).print_stats('__init__') - -This will sort all the statistics by file name, and then print out statistics -for only the class init methods (since they are spelled with ``__init__`` in -them). As one final example, you could try:: - - p.sort_stats(SortKey.TIME, SortKey.CUMULATIVE).print_stats(.5, 'init') - -This line sorts statistics with a primary key of time, and a secondary key of -cumulative time, and then prints out some of the statistics. To be specific, the -list is first culled down to 50% (re: ``.5``) of its original size, then only -lines containing ``init`` are maintained, and that sub-sub-list is printed. - -If you wondered what functions called the above functions, you could now (``p`` -is still sorted according to the last criteria) do:: - - p.print_callers(.5, 'init') - -and you would get a list of callers for each of the listed functions. - -If you want more functionality, you're going to have to read the manual, or -guess what the following functions do:: - - p.print_callees() - p.add('restats') - -Invoked as a script, the :mod:`pstats` module is a statistics browser for -reading and examining profile dumps. It has a simple line-oriented interface -(implemented using :mod:`cmd`) and interactive help. - -:mod:`profile` and :mod:`cProfile` Module Reference -======================================================= - -.. module:: cProfile -.. module:: profile - :synopsis: Python source profiler. - -Both the :mod:`profile` and :mod:`cProfile` modules provide the following -functions: +Both the :mod:`!profile` and :mod:`profiling.tracing` modules provide the +following functions: .. function:: run(command, filename=None, sort=-1) @@ -545,7 +86,7 @@ functions: .. class:: Profile(timer=None, timeunit=0.0, subcalls=True, builtins=True) This class is normally only used if more precise control over profiling is - needed than what the :func:`cProfile.run` function provides. + needed than what the :func:`profiling.tracing.run` function provides. A custom timer can be supplied for measuring how long code takes to run via the *timer* argument. This must be a function that returns a single number @@ -557,9 +98,12 @@ functions: Directly using the :class:`Profile` class allows formatting profile results without writing the profile data to a file:: - import cProfile, pstats, io + import profiling.tracing + import pstats + import io from pstats import SortKey - pr = cProfile.Profile() + + pr = profiling.tracing.Profile() pr.enable() # ... do something ... pr.disable() @@ -570,11 +114,12 @@ functions: print(s.getvalue()) The :class:`Profile` class can also be used as a context manager (supported - only in :mod:`cProfile` module. see :ref:`typecontextmanager`):: + only in :mod:`profiling.tracing`, not in the deprecated :mod:`!profile` + module; see :ref:`typecontextmanager`):: - import cProfile + import profiling.tracing - with cProfile.Profile() as pr: + with profiling.tracing.Profile() as pr: # ... do something ... pr.print_stats() @@ -584,11 +129,11 @@ functions: .. method:: enable() - Start collecting profiling data. Only in :mod:`cProfile`. + Start collecting profiling data. Only in :mod:`profiling.tracing`. .. method:: disable() - Stop collecting profiling data. Only in :mod:`cProfile`. + Stop collecting profiling data. Only in :mod:`profiling.tracing`. .. method:: create_stats() @@ -602,7 +147,7 @@ functions: The *sort* parameter specifies the sorting order of the displayed statistics. It accepts a single key or a tuple of keys to enable - multi-level sorting, as in :func:`Stats.sort_stats `. + multi-level sorting, as in :meth:`pstats.Stats.sort_stats`. .. versionadded:: 3.13 :meth:`~Profile.print_stats` now accepts a tuple of keys. @@ -629,237 +174,43 @@ returns. If the interpreter is terminated (e.g. via a :func:`sys.exit` call during the called command/function execution) no profiling results will be printed. -.. _profile-stats: -The :class:`Stats` Class -======================== +Differences from :mod:`!profiling.tracing` +========================================== -Analysis of the profiler data is done using the :class:`~pstats.Stats` class. +The :mod:`!profile` module differs from :mod:`profiling.tracing` in several +ways: -.. module:: pstats - :synopsis: Statistics object for use with the profiler. +**Higher overhead.** The pure Python implementation is significantly slower +than the C implementation, making it unsuitable for profiling long-running +programs or performance-sensitive code. -.. class:: Stats(*filenames or profile, stream=sys.stdout) +**Calibration support.** The :mod:`!profile` module supports calibration to +compensate for profiling overhead. This is not needed in :mod:`profiling.tracing` +because the C implementation has negligible overhead. - This class constructor creates an instance of a "statistics object" from a - *filename* (or list of filenames) or from a :class:`Profile` instance. Output - will be printed to the stream specified by *stream*. +**Custom timers.** Both modules support custom timers, but :mod:`!profile` +accepts timer functions that return tuples (like :func:`os.times`), while +:mod:`profiling.tracing` requires a function returning a single number. - The file selected by the above constructor must have been created by the - corresponding version of :mod:`profile` or :mod:`cProfile`. To be specific, - there is *no* file compatibility guaranteed with future versions of this - profiler, and there is no compatibility with files produced by other - profilers, or the same profiler run on a different operating system. If - several files are provided, all the statistics for identical functions will - be coalesced, so that an overall view of several processes can be considered - in a single report. If additional files need to be combined with data in an - existing :class:`~pstats.Stats` object, the :meth:`~pstats.Stats.add` method - can be used. +**Subclassing.** The pure Python implementation is easier to subclass and +extend for custom profiling behavior. - Instead of reading the profile data from a file, a :class:`cProfile.Profile` - or :class:`profile.Profile` object can be used as the profile data source. - - :class:`Stats` objects have the following methods: - - .. method:: strip_dirs() - - This method for the :class:`Stats` class removes all leading path - information from file names. It is very useful in reducing the size of - the printout to fit within (close to) 80 columns. This method modifies - the object, and the stripped information is lost. After performing a - strip operation, the object is considered to have its entries in a - "random" order, as it was just after object initialization and loading. - If :meth:`~pstats.Stats.strip_dirs` causes two function names to be - indistinguishable (they are on the same line of the same filename, and - have the same function name), then the statistics for these two entries - are accumulated into a single entry. - - - .. method:: add(*filenames) - - This method of the :class:`Stats` class accumulates additional profiling - information into the current profiling object. Its arguments should refer - to filenames created by the corresponding version of :func:`profile.run` - or :func:`cProfile.run`. Statistics for identically named (re: file, line, - name) functions are automatically accumulated into single function - statistics. - - - .. method:: dump_stats(filename) - - Save the data loaded into the :class:`Stats` object to a file named - *filename*. The file is created if it does not exist, and is overwritten - if it already exists. This is equivalent to the method of the same name - on the :class:`profile.Profile` and :class:`cProfile.Profile` classes. - - - .. method:: sort_stats(*keys) - - This method modifies the :class:`Stats` object by sorting it according to - the supplied criteria. The argument can be either a string or a SortKey - enum identifying the basis of a sort (example: ``'time'``, ``'name'``, - ``SortKey.TIME`` or ``SortKey.NAME``). The SortKey enums argument have - advantage over the string argument in that it is more robust and less - error prone. - - When more than one key is provided, then additional keys are used as - secondary criteria when there is equality in all keys selected before - them. For example, ``sort_stats(SortKey.NAME, SortKey.FILE)`` will sort - all the entries according to their function name, and resolve all ties - (identical function names) by sorting by file name. - - For the string argument, abbreviations can be used for any key names, as - long as the abbreviation is unambiguous. - - The following are the valid string and SortKey: - - +------------------+---------------------+----------------------+ - | Valid String Arg | Valid enum Arg | Meaning | - +==================+=====================+======================+ - | ``'calls'`` | SortKey.CALLS | call count | - +------------------+---------------------+----------------------+ - | ``'cumulative'`` | SortKey.CUMULATIVE | cumulative time | - +------------------+---------------------+----------------------+ - | ``'cumtime'`` | N/A | cumulative time | - +------------------+---------------------+----------------------+ - | ``'file'`` | N/A | file name | - +------------------+---------------------+----------------------+ - | ``'filename'`` | SortKey.FILENAME | file name | - +------------------+---------------------+----------------------+ - | ``'module'`` | N/A | file name | - +------------------+---------------------+----------------------+ - | ``'ncalls'`` | N/A | call count | - +------------------+---------------------+----------------------+ - | ``'pcalls'`` | SortKey.PCALLS | primitive call count | - +------------------+---------------------+----------------------+ - | ``'line'`` | SortKey.LINE | line number | - +------------------+---------------------+----------------------+ - | ``'name'`` | SortKey.NAME | function name | - +------------------+---------------------+----------------------+ - | ``'nfl'`` | SortKey.NFL | name/file/line | - +------------------+---------------------+----------------------+ - | ``'stdname'`` | SortKey.STDNAME | standard name | - +------------------+---------------------+----------------------+ - | ``'time'`` | SortKey.TIME | internal time | - +------------------+---------------------+----------------------+ - | ``'tottime'`` | N/A | internal time | - +------------------+---------------------+----------------------+ - - Note that all sorts on statistics are in descending order (placing most - time consuming items first), where as name, file, and line number searches - are in ascending order (alphabetical). The subtle distinction between - ``SortKey.NFL`` and ``SortKey.STDNAME`` is that the standard name is a - sort of the name as printed, which means that the embedded line numbers - get compared in an odd way. For example, lines 3, 20, and 40 would (if - the file names were the same) appear in the string order 20, 3 and 40. - In contrast, ``SortKey.NFL`` does a numeric compare of the line numbers. - In fact, ``sort_stats(SortKey.NFL)`` is the same as - ``sort_stats(SortKey.NAME, SortKey.FILENAME, SortKey.LINE)``. - - For backward-compatibility reasons, the numeric arguments ``-1``, ``0``, - ``1``, and ``2`` are permitted. They are interpreted as ``'stdname'``, - ``'calls'``, ``'time'``, and ``'cumulative'`` respectively. If this old - style format (numeric) is used, only one sort key (the numeric key) will - be used, and additional arguments will be silently ignored. - - .. For compatibility with the old profiler. - - .. versionadded:: 3.7 - Added the SortKey enum. - - .. method:: reverse_order() - - This method for the :class:`Stats` class reverses the ordering of the - basic list within the object. Note that by default ascending vs - descending order is properly selected based on the sort key of choice. - - .. This method is provided primarily for compatibility with the old - profiler. - - - .. method:: print_stats(*restrictions) - - This method for the :class:`Stats` class prints out a report as described - in the :func:`profile.run` definition. - - The order of the printing is based on the last - :meth:`~pstats.Stats.sort_stats` operation done on the object (subject to - caveats in :meth:`~pstats.Stats.add` and - :meth:`~pstats.Stats.strip_dirs`). - - The arguments provided (if any) can be used to limit the list down to the - significant entries. Initially, the list is taken to be the complete set - of profiled functions. Each restriction is either an integer (to select a - count of lines), or a decimal fraction between 0.0 and 1.0 inclusive (to - select a percentage of lines), or a string that will interpreted as a - regular expression (to pattern match the standard name that is printed). - If several restrictions are provided, then they are applied sequentially. - For example:: - - print_stats(.1, 'foo:') - - would first limit the printing to first 10% of list, and then only print - functions that were part of filename :file:`.\*foo:`. In contrast, the - command:: - - print_stats('foo:', .1) - - would limit the list to all functions having file names :file:`.\*foo:`, - and then proceed to only print the first 10% of them. - - - .. method:: print_callers(*restrictions) - - This method for the :class:`Stats` class prints a list of all functions - that called each function in the profiled database. The ordering is - identical to that provided by :meth:`~pstats.Stats.print_stats`, and the - definition of the restricting argument is also identical. Each caller is - reported on its own line. The format differs slightly depending on the - profiler that produced the stats: - - * With :mod:`profile`, a number is shown in parentheses after each caller - to show how many times this specific call was made. For convenience, a - second non-parenthesized number repeats the cumulative time spent in the - function at the right. - - * With :mod:`cProfile`, each caller is preceded by three numbers: the - number of times this specific call was made, and the total and - cumulative times spent in the current function while it was invoked by - this specific caller. - - - .. method:: print_callees(*restrictions) - - This method for the :class:`Stats` class prints a list of all function - that were called by the indicated function. Aside from this reversal of - direction of calls (re: called vs was called by), the arguments and - ordering are identical to the :meth:`~pstats.Stats.print_callers` method. - - - .. method:: get_stats_profile() - - This method returns an instance of StatsProfile, which contains a mapping - of function names to instances of FunctionProfile. Each FunctionProfile - instance holds information related to the function's profile such as how - long the function took to run, how many times it was called, etc... - - .. versionadded:: 3.9 - Added the following dataclasses: StatsProfile, FunctionProfile. - Added the following function: get_stats_profile. .. _deterministic-profiling: -What Is Deterministic Profiling? +What is deterministic profiling? ================================ :dfn:`Deterministic profiling` is meant to reflect the fact that all *function call*, *function return*, and *exception* events are monitored, and precise timings are made for the intervals between these events (during which time the user's code is executing). In contrast, :dfn:`statistical profiling` (which is -provided by the :mod:`profile.sample` module) periodically samples the effective instruction pointer, and -deduces where time is being spent. The latter technique traditionally involves -less overhead (as the code does not need to be instrumented), but provides only -relative indications of where time is being spent. +provided by the :mod:`profiling.sampling` module) periodically samples the +effective instruction pointer, and deduces where time is being spent. The +latter technique traditionally involves less overhead (as the code does not +need to be instrumented), but provides only relative indications of where time +is being spent. In Python, since there is an interpreter active during execution, the presence of instrumented code is not required in order to do deterministic profiling. @@ -903,15 +254,16 @@ this error. The error that accumulates in this fashion is typically less than the accuracy of the clock (less than one clock tick), but it *can* accumulate and become very significant. -The problem is more important with :mod:`profile` than with the lower-overhead -:mod:`cProfile`. For this reason, :mod:`profile` provides a means of -calibrating itself for a given platform so that this error can be -probabilistically (on the average) removed. After the profiler is calibrated, it -will be more accurate (in a least square sense), but it will sometimes produce -negative numbers (when call counts are exceptionally low, and the gods of -probability work against you :-). ) Do *not* be alarmed by negative numbers in -the profile. They should *only* appear if you have calibrated your profiler, -and the results are actually better than without calibration. +The problem is more important with the deprecated :mod:`!profile` module than +with the lower-overhead :mod:`profiling.tracing`. For this reason, +:mod:`!profile` provides a means of calibrating itself for a given platform so +that this error can be probabilistically (on the average) removed. After the +profiler is calibrated, it will be more accurate (in a least square sense), but +it will sometimes produce negative numbers (when call counts are exceptionally +low, and the gods of probability work against you :-). ) Do *not* be alarmed +by negative numbers in the profile. They should *only* appear if you have +calibrated your profiler, and the results are actually better than without +calibration. .. _profile-calibration: @@ -919,7 +271,7 @@ and the results are actually better than without calibration. Calibration =========== -The profiler of the :mod:`profile` module subtracts a constant from each event +The profiler of the :mod:`!profile` module subtracts a constant from each event handling time to compensate for the overhead of calling the time function, and socking away the results. By default, the constant is 0. The following procedure can be used to obtain a better constant for a given platform (see @@ -957,6 +309,7 @@ When you have a consistent answer, there are three ways you can use it:: If you have a choice, you are better off choosing a smaller constant, and then your results will "less often" show up as negative in profile statistics. + .. _profile-timers: Using a custom timer @@ -969,7 +322,7 @@ to the :class:`Profile` class constructor:: pr = profile.Profile(your_time_func) The resulting profiler will then call ``your_time_func``. Depending on whether -you are using :class:`profile.Profile` or :class:`cProfile.Profile`, +you are using :class:`profile.Profile` or :class:`profiling.tracing.Profile`, ``your_time_func``'s return value will be interpreted differently: :class:`profile.Profile` @@ -988,20 +341,32 @@ you are using :class:`profile.Profile` or :class:`cProfile.Profile`, replacement dispatch method that best handles your timer call, along with the appropriate calibration constant. -:class:`cProfile.Profile` +:class:`profiling.tracing.Profile` ``your_time_func`` should return a single number. If it returns integers, you can also invoke the class constructor with a second argument specifying the real duration of one unit of time. For example, if ``your_integer_time_func`` returns times measured in thousands of seconds, you would construct the :class:`Profile` instance as follows:: - pr = cProfile.Profile(your_integer_time_func, 0.001) + pr = profiling.tracing.Profile(your_integer_time_func, 0.001) - As the :class:`cProfile.Profile` class cannot be calibrated, custom timer - functions should be used with care and should be as fast as possible. For - the best results with a custom timer, it might be necessary to hard-code it - in the C source of the internal :mod:`!_lsprof` module. + As the :class:`profiling.tracing.Profile` class cannot be calibrated, custom + timer functions should be used with care and should be as fast as possible. + For the best results with a custom timer, it might be necessary to hard-code + it in the C source of the internal :mod:`!_lsprof` module. Python 3.3 adds several new functions in :mod:`time` that can be used to make precise measurements of process or wall-clock time. For example, see :func:`time.perf_counter`. + + +.. seealso:: + + :mod:`profiling` + Overview of Python profiling tools. + + :mod:`profiling.tracing` + Recommended replacement for this module. + + :mod:`pstats` + Statistical analysis and formatting for profile data. diff --git a/Doc/library/profiling-sampling-visualization.html b/Doc/library/profiling-sampling-visualization.html new file mode 100644 index 00000000000000..0cbd0f2374deaa --- /dev/null +++ b/Doc/library/profiling-sampling-visualization.html @@ -0,0 +1 @@ +
diff --git a/Doc/library/profiling.rst b/Doc/library/profiling.rst new file mode 100644 index 00000000000000..f4ac47826b28ef --- /dev/null +++ b/Doc/library/profiling.rst @@ -0,0 +1,270 @@ +.. highlight:: sh + +.. _profiling-module: + +************************************** +:mod:`!profiling` --- Python profilers +************************************** + +.. module:: profiling + :synopsis: Python profiling tools for performance analysis. + +.. versionadded:: 3.15 + +**Source code:** :source:`Lib/profiling/` + +-------------- + +.. index:: + single: statistical profiling + single: profiling, statistical + single: deterministic profiling + single: profiling, deterministic + + +Introduction to profiling +========================= + +A :dfn:`profile` is a set of statistics that describes how often and for how +long various parts of a program execute. These statistics help identify +performance bottlenecks and guide optimization efforts. Python provides two +fundamentally different approaches to collecting this information: statistical +sampling and deterministic tracing. + +The :mod:`!profiling` package organizes Python's built-in profiling tools under +a single namespace. It contains two submodules, each implementing a different +profiling methodology: + +:mod:`profiling.sampling` + A statistical profiler that periodically samples the call stack. Run scripts + directly or attach to running processes by PID. Provides multiple output + formats (flame graphs, heatmaps, Firefox Profiler), GIL analysis, GC tracking, + and multiple profiling modes (wall-clock, CPU, GIL) with virtually no overhead. + +:mod:`profiling.tracing` + A deterministic profiler that traces every function call, return, and + exception event. Provides exact call counts and precise timing information, + capturing every invocation including very fast functions. + +.. note:: + + The profiler modules are designed to provide an execution profile for a + given program, not for benchmarking purposes. For benchmarking, use the + :mod:`timeit` module, which provides reasonably accurate timing + measurements. This distinction is particularly important when comparing + Python code against C code: deterministic profilers introduce overhead for + Python code but not for C-level functions, which can skew comparisons. + + +.. _choosing-a-profiler: + +Choosing a profiler +=================== + +For most performance analysis, use the statistical profiler +(:mod:`profiling.sampling`). It has minimal overhead, works for both development +and production, and provides rich visualization options including flame graphs, +heatmaps, GIL analysis, and more. + +Use the deterministic profiler (:mod:`profiling.tracing`) when you need **exact +call counts** and cannot afford to miss any function calls. Since it instruments +every function call and return, it will capture even very fast functions that +complete between sampling intervals. The tradeoff is higher overhead. + +The following table summarizes the key differences: + ++--------------------+------------------------------+------------------------------+ +| Feature | Statistical sampling | Deterministic | +| | (:mod:`profiling.sampling`) | (:mod:`profiling.tracing`) | ++====================+==============================+==============================+ +| **Overhead** | Virtually none | Moderate | ++--------------------+------------------------------+------------------------------+ +| **Accuracy** | Statistical estimate | Exact call counts | ++--------------------+------------------------------+------------------------------+ +| **Output formats** | pstats, flame graph, heatmap,| pstats | +| | gecko, collapsed | | ++--------------------+------------------------------+------------------------------+ +| **Profiling modes**| Wall-clock, CPU, GIL | Wall-clock | ++--------------------+------------------------------+------------------------------+ +| **Special frames** | GC, native (C extensions) | N/A | ++--------------------+------------------------------+------------------------------+ +| **Attach to PID** | Yes | No | ++--------------------+------------------------------+------------------------------+ + + +When to use statistical sampling +-------------------------------- + +The statistical profiler (:mod:`profiling.sampling`) is recommended for most +performance analysis tasks. Use it the same way you would use +:mod:`profiling.tracing`:: + + python -m profiling.sampling run script.py + +One of the main strengths of the sampling profiler is its variety of output +formats. Beyond traditional pstats tables, it can generate interactive +flame graphs that visualize call hierarchies, line-level source heatmaps that +show exactly where time is spent in your code, and Firefox Profiler output for +timeline-based analysis. + +The profiler also provides insight into Python interpreter behavior that +deterministic profiling cannot capture. Use ``--mode gil`` to identify GIL +contention in multi-threaded code, ``--mode cpu`` to measure actual CPU time +excluding I/O waits, or inspect ```` frames to understand garbage collection +overhead. The ``--native`` option reveals time spent in C extensions, helping +distinguish Python overhead from library performance. + +For multi-threaded applications, the ``-a`` option samples all threads +simultaneously, showing how work is distributed. And for production debugging, +the ``attach`` command connects to any running Python process by PID without +requiring a restart or code changes. + + +When to use deterministic tracing +--------------------------------- + +The deterministic profiler (:mod:`profiling.tracing`) instruments every function +call and return. This approach has higher overhead than sampling, but guarantees +complete coverage of program execution. + +The primary reason to choose deterministic tracing is when you need exact call +counts. Statistical profiling estimates frequency based on sampling, which may +undercount short-lived functions that complete between samples. If you need to +verify that an optimization actually reduced the number of function calls, or +if you want to trace the complete call graph to understand caller-callee +relationships, deterministic tracing is the right choice. + +Deterministic tracing also excels at capturing functions that execute in +microseconds. Such functions may not appear frequently enough in statistical +samples, but deterministic tracing records every invocation regardless of +duration. + + +Quick start +=========== + +This section provides the minimal steps needed to start profiling. For complete +documentation, see the dedicated pages for each profiler. + + +Statistical profiling +--------------------- + +To profile a script, use the :mod:`profiling.sampling` module with the ``run`` +command:: + + python -m profiling.sampling run script.py + python -m profiling.sampling run -m mypackage.module + +This runs the script under the profiler and prints a summary of where time was +spent. For an interactive flame graph:: + + python -m profiling.sampling run --flamegraph script.py + +To profile an already-running process, use the ``attach`` command with the +process ID:: + + python -m profiling.sampling attach 1234 + +For custom settings, specify the sampling interval (in microseconds) and +duration (in seconds):: + + python -m profiling.sampling run -i 50 -d 30 script.py + + +Deterministic profiling +----------------------- + +To profile a script from the command line:: + + python -m profiling.tracing script.py + +To profile a piece of code programmatically: + +.. code-block:: python + + import profiling.tracing + profiling.tracing.run('my_function()') + +This executes the given code under the profiler and prints a summary showing +exact function call counts and timing. + + +.. _profile-output: + +Understanding profile output +============================ + +Both profilers collect function-level statistics, though they present them in +different formats. The sampling profiler offers multiple visualizations +(flame graphs, heatmaps, Firefox Profiler, pstats tables), while the +deterministic profiler produces pstats-compatible output. Regardless of format, +the underlying concepts are the same. + +Key profiling concepts: + +**Direct time** (also called *self time* or *tottime*) + Time spent executing code in the function itself, excluding time spent in + functions it called. High direct time indicates the function contains + expensive operations. + +**Cumulative time** (also called *total time* or *cumtime*) + Time spent in the function and all functions it called. This measures the + total cost of calling a function, including its entire call subtree. + +**Call count** (also called *ncalls* or *samples*) + How many times the function was called (deterministic) or sampled + (statistical). In deterministic profiling, this is exact. In statistical + profiling, it represents the number of times the function appeared in a + stack sample. + +**Primitive calls** + Calls that are not induced by recursion. When a function recurses, the total + call count includes recursive invocations, but primitive calls counts only + the initial entry. Displayed as ``total/primitive`` (for example, ``3/1`` + means three total calls, one primitive). + +**Caller/Callee relationships** + Which functions called a given function (callers) and which functions it + called (callees). Flame graphs visualize this as nested rectangles; pstats + can display it via the :meth:`~pstats.Stats.print_callers` and + :meth:`~pstats.Stats.print_callees` methods. + + +Legacy compatibility +==================== + +For backward compatibility, the ``cProfile`` module remains available as an +alias to :mod:`profiling.tracing`. Existing code using ``import cProfile`` will +continue to work without modification in all future Python versions. + +.. deprecated:: 3.15 + + The pure Python :mod:`profile` module is deprecated and will be removed in + Python 3.17. Use :mod:`profiling.tracing` (or its alias ``cProfile``) + instead. See :mod:`profile` for migration guidance. + + +.. seealso:: + + :mod:`profiling.sampling` + Statistical sampling profiler with flame graphs, heatmaps, and GIL analysis. + Recommended for most users. + + :mod:`profiling.tracing` + Deterministic tracing profiler for exact call counts. + + :mod:`pstats` + Statistics analysis and formatting for profile data. + + :mod:`timeit` + Module for measuring execution time of small code snippets. + + +.. rubric:: Submodules + +.. toctree:: + :maxdepth: 1 + + profiling.tracing.rst + profiling.sampling.rst diff --git a/Doc/library/profiling.sampling.rst b/Doc/library/profiling.sampling.rst new file mode 100644 index 00000000000000..aeb1a429b58515 --- /dev/null +++ b/Doc/library/profiling.sampling.rst @@ -0,0 +1,1735 @@ +.. highlight:: sh + +.. _profiling-sampling: + +*************************************************** +:mod:`!profiling.sampling` --- Statistical profiler +*************************************************** + +.. module:: profiling.sampling + :synopsis: Statistical sampling profiler for Python processes. + +.. versionadded:: 3.15 + +**Source code:** :source:`Lib/profiling/sampling/` + +.. program:: profiling.sampling + +-------------- + +.. image:: ../../Lib/profiling/sampling/_assets/tachyon-logo.png + :alt: Tachyon logo + :align: center + :width: 300px + +The :mod:`!profiling.sampling` module, named **Tachyon**, provides statistical +profiling of Python programs through periodic stack sampling. Tachyon can +run scripts directly or attach to any running Python process without requiring +code changes or restarts. Because sampling occurs externally to the target +process, overhead is virtually zero, making Tachyon suitable for both +development and production environments. + + +What is statistical profiling? +============================== + +Statistical profiling builds a picture of program behavior by periodically +capturing snapshots of the call stack. Rather than instrumenting every function +call and return as deterministic profilers do, Tachyon reads the call stack at +regular intervals to record what code is currently running. + +This approach rests on a simple principle: functions that consume significant +CPU time will appear frequently in the collected samples. By gathering thousands +of samples over a profiling session, Tachyon constructs an accurate statistical +estimate of where time is spent. The more samples collected, the +more precise this estimate becomes. + +.. only:: html + + The following interactive visualization demonstrates how sampling profiling + works. Press **Play** to watch a Python program execute, and observe how the + profiler periodically captures snapshots of the call stack. Adjust the + **sample interval** to see how sampling frequency affects the results. + + .. raw:: html + :file: profiling-sampling-visualization.html + +.. only:: not html + + .. note:: + + An interactive visualization of sampling profiling is available in the + HTML version of this documentation. + + +How time is estimated +--------------------- + +The time values shown in Tachyon's output are **estimates derived from sample +counts**, not direct measurements. Tachyon counts how many times each function +appears in the collected samples, then multiplies by the sampling interval to +estimate time. + +For example, with a 10 kHz sampling rate over a 10-second profile, +Tachyon collects approximately 100,000 samples. If a function appears in 5,000 +samples (5% of total), Tachyon estimates it consumed 5% of the 10-second +duration, or about 500 milliseconds. This is a statistical estimate, not a +precise measurement. + +The accuracy of these estimates depends on sample count. With 100,000 samples, +a function showing 5% has a margin of error of roughly ±0.5%. With only 1,000 +samples, the same 5% measurement could actually represent anywhere from 3% to +7% of real time. + +This is why longer profiling durations and shorter sampling intervals produce +more reliable results---they collect more samples. For most performance +analysis, the default settings provide sufficient accuracy to identify +bottlenecks and guide optimization efforts. + +Because sampling is statistical, results will vary slightly between runs. A +function showing 12% in one run might show 11% or 13% in the next. This is +normal and expected. Focus on the overall pattern rather than exact percentages, +and don't worry about small variations between runs. + + +When to use a different approach +-------------------------------- + +Statistical sampling is not ideal for every situation. + +For very short scripts that complete in under one second, the profiler may not +collect enough samples for reliable results. Use :mod:`profiling.tracing` +instead, or run the script in a loop to extend profiling time. + +When you need exact call counts, sampling cannot provide them. Sampling +estimates frequency from snapshots, so if you need to know precisely how many +times a function was called, use :mod:`profiling.tracing`. + +When comparing two implementations where the difference might be only 1-2%, +sampling noise can obscure real differences. Use :mod:`timeit` for +micro-benchmarks or :mod:`profiling.tracing` for precise measurements. + + +The key difference from :mod:`profiling.tracing` is how measurement happens. +A tracing profiler instruments your code, recording every function call and +return. This provides exact call counts and precise timing but adds overhead +to every function call. A sampling profiler, by contrast, observes the program +from outside at fixed intervals without modifying its execution. Think of the +difference like this: tracing is like having someone follow you and write down +every step you take, while sampling is like taking photographs every second +and inferring your path from those snapshots. + +This external observation model is what makes sampling profiling practical for +production use. The profiled program runs at full speed because there is no +instrumentation code running inside it, and the target process is never stopped +or paused during sampling---Tachyon reads the call stack directly from the +process's memory while it continues to run. You can attach to a live server, +collect data, and detach without the application ever knowing it was observed. +The trade-off is that very short-lived functions may be missed if they happen +to complete between samples. + +Statistical profiling excels at answering the question, "Where is my program +spending time?" It reveals hotspots and bottlenecks in production code where +deterministic profiling overhead would be unacceptable. For exact call counts +and complete call graphs, use :mod:`profiling.tracing` instead. + + +Quick examples +============== + +Profile a script and see the results immediately:: + + python -m profiling.sampling run script.py + +Profile a module with arguments:: + + python -m profiling.sampling run -m mypackage.module arg1 arg2 + +Generate an interactive flame graph:: + + python -m profiling.sampling run --flamegraph -o profile.html script.py + +Attach to a running process by PID:: + + python -m profiling.sampling attach 12345 + +Print a single snapshot of a running process's stack:: + + python -m profiling.sampling dump 12345 + +Use live mode for real-time monitoring (press ``q`` to quit):: + + python -m profiling.sampling run --live script.py + +Profile for 60 seconds with a faster sampling rate:: + + python -m profiling.sampling run -d 60 -r 20khz script.py + +Generate a line-by-line heatmap:: + + python -m profiling.sampling run --heatmap script.py + +Enable opcode-level profiling to see which bytecode instructions are executing:: + + python -m profiling.sampling run --opcodes --flamegraph script.py + + +Commands +======== + +Tachyon operates through several subcommands. ``run`` and ``attach`` collect +samples over time; ``dump`` captures a single snapshot; ``replay`` converts +binary profiles to other formats. + + +The ``run`` command +------------------- + +The ``run`` command launches a Python script or module and profiles it from +startup:: + + python -m profiling.sampling run script.py + python -m profiling.sampling run -m mypackage.module + +When profiling a script, the profiler starts the target in a subprocess, waits +for it to initialize, then begins collecting samples. The ``-m`` flag +indicates that the target should be run as a module (equivalent to +``python -m``). Arguments after the target are passed through to the +profiled program:: + + python -m profiling.sampling run script.py --config settings.yaml + + +The ``attach`` command +---------------------- + +The ``attach`` command connects to an already-running Python process by its +process ID:: + + python -m profiling.sampling attach 12345 + +This command is particularly valuable for investigating performance issues in +production systems. The target process requires no modification and need not +be restarted. The profiler attaches, collects samples for the specified +duration, then detaches and produces output. + +:: + + python -m profiling.sampling attach --live 12345 + python -m profiling.sampling attach --flamegraph -d 30 -o profile.html 12345 + +On most systems, attaching to another process requires appropriate permissions. +See :ref:`profiling-permissions` for platform-specific requirements. + + +.. _dump-command: + +The ``dump`` command +-------------------- + +The ``dump`` command prints a single snapshot of a running process's Python +stack and exits, similar to a traceback:: + + python -m profiling.sampling dump 12345 + +Unlike ``attach``, ``dump`` does not run a sampling loop: it reads the +stack once. This is useful for investigating hung or unresponsive +processes, or for answering "what is this process doing right now?". + +The output mirrors a traceback (most recent call last) and annotates each +thread with its current state (main thread, has GIL, on CPU, waiting for +GIL, has exception, or idle): + +.. code-block:: text + + Stack dump for PID 12345, thread 140735 (main thread, has GIL, on CPU; most recent call last): + File "server.py", line 28, in serve + await handle_request(req) + File "handler.py", line 91, in handle_request + result = expensive_call(req) + +When the target's source files are readable, ``dump`` prints the source +line for each frame and highlights the executing expression. + +Like ``attach``, ``dump`` requires permission to read the target process's +memory. See :ref:`profiling-permissions`. + +The ``dump`` command supports the following options: + +``-a``, ``--all-threads`` + Dump every thread in the target process. Without this flag only the main + thread is shown. + +``--native`` + Include synthetic ```` frames marking transitions into C + extensions or other non-Python code. + +``--no-gc`` + Hide the synthetic ```` frames that mark active garbage collection. + +``--opcodes`` + Annotate each frame with the bytecode opcode the thread is currently + executing (for example, ``opcode=CALL_KW``). Useful for + instruction-level investigation, including identifying specializations + chosen by the adaptive interpreter. + +``--async-aware`` + Reconstruct stacks across ``await`` boundaries. ``dump`` walks the task + graph and emits one section per task, with ```` markers separating + coroutines awaiting each other. + +``--async-mode {running,all}`` + Controls which tasks are included when ``--async-aware`` is enabled. + ``running`` shows only the task currently executing on each thread; + ``all`` (the default for ``dump``) also includes tasks suspended on a + wait. ``attach``'s default for this flag is ``running``; ``dump`` + defaults to ``all`` because a single snapshot is most useful when it + shows the full task graph. + +``--blocking`` + Pause every thread in the target while reading its stack and resume + them after. Guarantees a fully consistent snapshot at the cost of + briefly stopping the target. Without it, ``dump`` reads memory while + the target keeps running, which is faster but can occasionally produce + a torn stack. + + +.. _replay-command: + +The ``replay`` command +---------------------- + +The ``replay`` command converts binary profile files to other output formats:: + + python -m profiling.sampling replay profile.bin + python -m profiling.sampling replay --flamegraph -o profile.html profile.bin + +This command is useful when you have captured profiling data in binary format +and want to analyze it later or convert it to a visualization format. Binary +profiles can be replayed multiple times to different formats without +re-profiling. + +:: + + # Convert binary to pstats (default, prints to stdout) + python -m profiling.sampling replay profile.bin + + # Convert binary to flame graph + python -m profiling.sampling replay --flamegraph -o output.html profile.bin + + # Convert binary to gecko format for Firefox Profiler + python -m profiling.sampling replay --gecko -o profile.json profile.bin + + # Convert binary to heatmap + python -m profiling.sampling replay --heatmap -o my_heatmap profile.bin + + +Profiling in production +----------------------- + +The sampling profiler is designed for production use. It imposes no measurable +overhead on the target process because it reads memory externally rather than +instrumenting code. The target application continues running at full speed and +is unaware it is being profiled. + +When profiling production systems, keep these guidelines in mind: + +Start with shorter durations (10-30 seconds) to get quick results, then extend +if you need more statistical accuracy. By default, profiling runs until the +target process completes, which is usually sufficient to identify major hotspots. + +If possible, profile during representative load rather than peak traffic. +Profiles collected during normal operation are easier to interpret than those +collected during unusual spikes. + +The profiler itself consumes some CPU on the machine where it runs (not on the +target process). On the same machine, this is typically negligible. When +profiling remote processes, network latency does not affect the target. + +Results from production may differ from development due to different data +sizes, concurrent load, or caching effects. This is expected and is often +exactly what you want to capture. + + +.. _profiling-permissions: + +Platform requirements +--------------------- + +The profiler reads the target process's memory to capture stack traces. This +requires elevated permissions on most operating systems. + +**Linux** + +On Linux, the profiler uses ``ptrace`` or ``process_vm_readv`` to read the +target process's memory. This typically requires one of: + +- Running as root +- Having the ``CAP_SYS_PTRACE`` capability +- Adjusting the Yama ptrace scope: ``/proc/sys/kernel/yama/ptrace_scope`` + +The default ptrace_scope of 1 restricts ptrace to parent processes only. To +allow attaching to any process owned by the same user, set it to 0:: + + echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope + +**macOS** + +On macOS, the profiler uses ``task_for_pid()`` to access the target process. +This requires one of: + +- Running as root +- The profiler binary having the ``com.apple.security.cs.debugger`` entitlement +- System Integrity Protection (SIP) being disabled (not recommended) + +**Windows** + +On Windows, the profiler requires administrative privileges or the +``SeDebugPrivilege`` privilege to read another process's memory. + + +Version compatibility +--------------------- + +The profiler and target process must run the same Python minor version (for +example, both Python 3.15). Attaching from Python 3.14 to a Python 3.15 process +is not supported. + +Additional restrictions apply to pre-release Python versions: if either the +profiler or target is running a pre-release (alpha, beta, or release candidate), +both must run the exact same version. + +On free-threaded Python builds, the profiler cannot attach from a free-threaded +build to a standard build, or vice versa. + + +Sampling configuration +====================== + +Before exploring the various output formats and visualization options, it is +important to understand how to configure the sampling process itself. The +profiler offers several options that control how frequently samples are +collected, how long profiling runs, which threads are observed, and what +additional context is captured in each sample. + +The default configuration works well for most use cases: + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - Option + - Default + * - Default for ``--sampling-rate`` / ``-r`` + - 1 kHz + * - Default for ``--duration`` / ``-d`` + - Run to completion + * - Default for ``--all-threads`` / ``-a`` + - Main thread only + * - Default for ``--native`` + - No ```` frames (C code time attributed to caller) + * - Default for ``--no-gc`` + - ```` frames included when garbage collection is active + * - Default for ``--mode`` + - Wall-clock mode (all samples recorded) + * - Default for ``--realtime-stats`` + - Disabled + * - Default for ``--subprocesses`` + - Disabled + * - Default for ``--blocking`` + - Disabled (non-blocking sampling) + + +Sampling rate and duration +-------------------------- + +The two most fundamental parameters are the sampling rate and duration. +Together, these determine how many samples will be collected during a profiling +session. + +The :option:`--sampling-rate` option (:option:`-r`) sets how frequently samples +are collected. The default is 1 kHz (1,000 samples per second):: + + python -m profiling.sampling run -r 20khz script.py + +Higher rates capture more samples and provide finer-grained data at the +cost of slightly higher profiler CPU usage. Lower rates reduce profiler +overhead but may miss short-lived functions. For most applications, the +default rate provides a good balance between accuracy and overhead. + +The :option:`--duration` option (:option:`-d`) sets how long to profile in seconds. By +default, profiling continues until the target process exits or is interrupted:: + + python -m profiling.sampling run -d 60 script.py + +Specifying a duration is useful when attaching to long-running processes or when +you want to limit profiling to a specific time window. When profiling a script, +the default behavior of running to completion is usually what you want. + + +Thread selection +---------------- + +Python programs often use multiple threads, whether explicitly through the +:mod:`threading` module or implicitly through libraries that manage thread +pools. + +By default, the profiler samples only the main thread. The :option:`--all-threads` +option (:option:`-a`) enables sampling of all threads in the process:: + + python -m profiling.sampling run -a script.py + +Multi-thread profiling reveals how work is distributed across threads and can +identify threads that are blocked or starved. Each thread's samples are +combined in the output, with the ability to filter by thread in some formats. +This option is particularly useful when investigating concurrency issues or +when work is distributed across a thread pool. + + +.. _blocking-mode: + +Blocking mode +------------- + +By default, Tachyon reads the target process's memory without stopping it. +This non-blocking approach is ideal for most profiling scenarios because it +imposes virtually zero overhead on the target application: the profiled +program runs at full speed and is unaware it is being observed. + +However, non-blocking sampling can occasionally produce incomplete or +inconsistent stack traces in applications with many generators or coroutines +that rapidly switch between yield points, or in programs with very fast-changing +call stacks where functions enter and exit between the start and end of a single +stack read, resulting in reconstructed stacks that mix frames from different +execution states or that never actually existed. + +For these cases, the :option:`--blocking` option stops the target process during +each sample:: + + python -m profiling.sampling run --blocking script.py + python -m profiling.sampling attach --blocking 12345 + +When blocking mode is enabled, the profiler suspends the target process, +reads its stack, then resumes it. This guarantees that each captured stack +represents a real, consistent snapshot of what the process was doing at that +instant. The trade-off is that the target process runs slower because it is +repeatedly paused. + +.. warning:: + + Do not use very high sample rates (low ``--interval`` values) with blocking + mode. Suspending and resuming a process takes time, and if the sampling + interval is too short, the target will spend more time stopped than running. + For blocking mode, intervals of 1000 microseconds (1 millisecond) or higher + are recommended. The default 100 microsecond interval may cause noticeable + slowdown in the target application. + +Use blocking mode only when you observe inconsistent stacks in your profiles, +particularly with generator-heavy or coroutine-heavy code. For most +applications, the default non-blocking mode provides accurate results with +zero impact on the target process. + + +Special frames +-------------- + +The profiler can inject artificial frames into the captured stacks to provide +additional context about what the interpreter is doing at the moment each +sample is taken. These synthetic frames help distinguish different types of +execution that would otherwise be invisible. + +The :option:`--native` option adds ```` frames to indicate when Python has +called into C code (extension modules, built-in functions, or the interpreter +itself):: + + python -m profiling.sampling run --native script.py + +These frames help distinguish time spent in Python code versus time spent in +native libraries. Without this option, native code execution appears as time +in the Python function that made the call. This is useful when optimizing +code that makes heavy use of C extensions like NumPy or database drivers. + +By default, the profiler includes ```` frames when garbage collection is +active. The :option:`--no-gc` option suppresses these frames:: + + python -m profiling.sampling run --no-gc script.py + +GC frames help identify programs where garbage collection consumes significant +time, which may indicate memory allocation patterns worth optimizing. If you +see substantial time in ```` frames, consider investigating object +allocation rates or using object pooling. + + +Opcode-aware profiling +---------------------- + +The :option:`--opcodes` option enables instruction-level profiling that captures +which Python bytecode instructions are executing at each sample:: + + python -m profiling.sampling run --opcodes --flamegraph script.py + +This feature provides visibility into Python's bytecode execution, including +adaptive specialization optimizations. When a generic instruction like +``LOAD_ATTR`` is specialized at runtime into a more efficient variant like +``LOAD_ATTR_INSTANCE_VALUE``, the profiler shows both the specialized name +and the base instruction. + +Opcode information appears in several output formats: + +- **Flame graphs**: Hovering over a frame displays a tooltip with a bytecode + instruction breakdown, showing which opcodes consumed time in that function +- **Heatmap**: Expandable bytecode panels per source line show instruction + breakdown with specialization percentages +- **Live mode**: An opcode panel shows instruction-level statistics for the + selected function, accessible via keyboard navigation +- **Gecko format**: Opcode transitions are emitted as interval markers in the + Firefox Profiler timeline + +This level of detail is particularly useful for: + +- Understanding the performance impact of Python's adaptive specialization +- Identifying hot bytecode instructions that might benefit from optimization +- Analyzing the effectiveness of different code patterns at the instruction level +- Debugging performance issues that occur at the bytecode level + +The :option:`--opcodes` option is compatible with :option:`--live`, :option:`--flamegraph`, +:option:`--heatmap`, and :option:`--gecko` formats. It requires additional memory to store +opcode information and may slightly reduce sampling performance, but provides +unprecedented visibility into Python's execution model. + + +Real-time statistics +-------------------- + +The :option:`--realtime-stats` option displays sampling rate statistics during +profiling:: + + python -m profiling.sampling run --realtime-stats script.py + +This shows the actual achieved sampling rate, which may be lower than requested +if the profiler cannot keep up. The statistics help verify that profiling is +working correctly and that sufficient samples are being collected. See +:ref:`sampling-efficiency` for details on interpreting these metrics. + + +Subprocess profiling +-------------------- + +The :option:`--subprocesses` option enables automatic profiling of subprocesses +spawned by the target:: + + python -m profiling.sampling run --subprocesses script.py + python -m profiling.sampling attach --subprocesses 12345 + +When enabled, the profiler monitors the target process for child process +creation. When a new Python child process is detected, a separate profiler +instance is automatically spawned to profile it. This is useful for +applications that use :mod:`multiprocessing`, :mod:`subprocess`, +:mod:`concurrent.futures` with :class:`~concurrent.futures.ProcessPoolExecutor`, +or other process spawning mechanisms. + +.. code-block:: python + :caption: worker_pool.py + + from concurrent.futures import ProcessPoolExecutor + import math + + def compute_factorial(n): + total = 0 + for i in range(50): + total += math.factorial(n) + return total + + if __name__ == "__main__": + numbers = [5000 + i * 100 for i in range(50)] + with ProcessPoolExecutor(max_workers=4) as executor: + results = list(executor.map(compute_factorial, numbers)) + print(f"Computed {len(results)} factorials") + +:: + + python -m profiling.sampling run --subprocesses --flamegraph worker_pool.py + +This produces separate flame graphs for the main process and each worker +process: ``flamegraph_.html``, ``flamegraph_.html``, +and so on. + +Each subprocess receives its own output file. The filename is derived from +the specified output path (or the default) with the subprocess's process ID +appended: + +- If you specify ``-o profile.html``, subprocesses produce ``profile_12345.html``, + ``profile_12346.html``, and so on +- With default output, subprocesses produce files like ``flamegraph_12345.html`` + or directories like ``heatmap_12345`` +- For pstats format (which defaults to stdout), subprocesses produce files like + ``profile_12345.pstats`` + +The subprocess profilers inherit most sampling options from the parent (sampling +rate, duration, thread selection, native frames, GC frames, async-aware mode, +and output format). All Python descendant processes are profiled recursively, +including grandchildren and further descendants. + +Subprocess detection works by periodically scanning for new descendants of +the target process and checking whether each new process is a Python process +by probing the process memory for Python runtime structures. Non-Python +subprocesses (such as shell commands or external tools) are ignored. + +There is a limit of 100 concurrent subprocess profilers to prevent resource +exhaustion in programs that spawn many processes. If this limit is reached, +additional subprocesses are not profiled and a warning is printed. + +The :option:`--subprocesses` option is incompatible with :option:`--live` mode +because live mode uses an interactive terminal interface that cannot +accommodate multiple concurrent profiler displays. + + +.. _sampling-efficiency: + +Sampling efficiency +------------------- + +Sampling efficiency metrics help assess the quality of the collected data. +These metrics appear in the profiler's terminal output and in the flame graph +sidebar. + +**Sampling efficiency** is the percentage of sample attempts that succeeded. +Each sample attempt reads the target process's call stack from memory. An +attempt can fail if the process is in an inconsistent state at the moment of +reading, such as during a context switch or while the interpreter is updating +its internal structures. A low efficiency may indicate that the profiler could +not keep up with the requested sampling rate, often due to system load or an +overly aggressive interval setting. + +**Missed samples** is the percentage of expected samples that were not +collected. Based on the configured interval and duration, the profiler expects +to collect a certain number of samples. Some samples may be missed if the +profiler falls behind schedule, for example when the system is under heavy +load. A small percentage of missed samples is normal and does not significantly +affect the statistical accuracy of the profile. + +Both metrics are informational. Even with some failed attempts or missed +samples, the profile remains statistically valid as long as enough samples +were collected. The profiler reports the actual number of samples captured, +which you can use to judge whether the data is sufficient for your analysis. + + +Profiling modes +=============== + +The sampling profiler supports four modes that control which samples are +recorded. The mode determines what the profile measures: total elapsed time, +CPU execution time, time spent holding the global interpreter lock, or +exception handling. + + +Wall-clock mode +--------------- + +Wall-clock mode (:option:`--mode`\ ``=wall``) captures all samples regardless of what the +thread is doing. This is the default mode and provides a complete picture of +where time passes during program execution:: + + python -m profiling.sampling run --mode=wall script.py + +In wall-clock mode, samples are recorded whether the thread is actively +executing Python code, waiting for I/O, blocked on a lock, or sleeping. +This makes wall-clock profiling ideal for understanding the overall time +distribution in your program, including time spent waiting. + +If your program spends significant time in I/O operations, network calls, or +sleep, wall-clock mode will show these waits as time attributed to the calling +function. This is often exactly what you want when optimizing end-to-end +latency. + + +CPU mode +-------- + +CPU mode (:option:`--mode`\ ``=cpu``) records samples only when the thread is actually +executing on a CPU core:: + + python -m profiling.sampling run --mode=cpu script.py + +Samples taken while the thread is sleeping, blocked on I/O, or waiting for +a lock are discarded. The resulting profile shows where CPU cycles are consumed, +filtering out idle time. + +CPU mode is useful when you want to focus on computational hotspots without +being distracted by I/O waits. If your program alternates between computation +and network calls, CPU mode reveals which computational sections are most +expensive. + + +Comparing wall-clock and CPU profiles +------------------------------------- + +Running both wall-clock and CPU mode profiles can reveal whether a function's +time is spent computing or waiting. + +If a function appears prominently in both profiles, it is a true computational +hotspot---actively using the CPU. Optimization should focus on algorithmic +improvements or more efficient code. + +If a function is high in wall-clock mode but low or absent in CPU mode, it is +I/O-bound or waiting. The function spends most of its time waiting for network, +disk, locks, or sleep. CPU optimization won't help here; consider async I/O, +connection pooling, or reducing wait time instead. + +.. code-block:: python + + import time + + def do_sleep(): + time.sleep(2) + + def do_compute(): + sum(i**2 for i in range(1000000)) + + if __name__ == "__main__": + do_sleep() + do_compute() + +:: + + python -m profiling.sampling run --mode=wall script.py # do_sleep ~98%, do_compute ~1% + python -m profiling.sampling run --mode=cpu script.py # do_sleep absent, do_compute dominates + + +GIL mode +-------- + +GIL mode (:option:`--mode`\ ``=gil``) records samples only when the thread holds Python's +global interpreter lock:: + + python -m profiling.sampling run --mode=gil script.py + +The GIL is held only while executing Python bytecode. When Python calls into +C extensions, performs I/O operations, or executes native code, the GIL is +typically released. This means GIL mode effectively measures time spent +running Python code specifically, filtering out time in native libraries. + +In multi-threaded programs, GIL mode reveals which code is preventing other +threads from running Python bytecode. Since only one thread can hold the GIL +at a time, functions that appear frequently in GIL mode profiles are +monopolizing the interpreter. + +GIL mode helps answer questions like "which functions are monopolizing the +GIL?" and "why are my other threads starving?" It can also be useful in +single-threaded programs to distinguish Python execution time from time spent +in C extensions or I/O. + +.. code-block:: python + + import hashlib + + def hash_work(): + # C extension - releases GIL during computation + for _ in range(200): + hashlib.sha256(b"data" * 250000).hexdigest() + + def python_work(): + # Pure Python - holds GIL during computation + for _ in range(3): + sum(i**2 for i in range(1000000)) + + if __name__ == "__main__": + hash_work() + python_work() + +:: + + python -m profiling.sampling run --mode=cpu script.py # hash_work ~42%, python_work ~38% + python -m profiling.sampling run --mode=gil script.py # hash_work ~5%, python_work ~60% + + +Exception mode +-------------- + +Exception mode (``--mode=exception``) records samples only when a thread has +an active exception:: + + python -m profiling.sampling run --mode=exception script.py + +Samples are recorded in two situations: when an exception is being propagated +up the call stack (after ``raise`` but before being caught), or when code is +executing inside an ``except`` block where exception information is still +present in the thread state. + +The following example illustrates which code regions are captured: + +.. code-block:: python + + def example(): + try: + raise ValueError("error") # Captured: exception being raised + except ValueError: + process_error() # Captured: inside except block + finally: + cleanup() # NOT captured: exception already handled + + def example_propagating(): + try: + try: + raise ValueError("error") + finally: + cleanup() # Captured: exception propagating through + except ValueError: + pass + + def example_no_exception(): + try: + do_work() + finally: + cleanup() # NOT captured: no exception involved + +Note that ``finally`` blocks are only captured when an exception is actively +propagating through them. Once an ``except`` block finishes executing, Python +clears the exception information before running any subsequent ``finally`` +block. Similarly, ``finally`` blocks that run during normal execution (when no +exception was raised) are not captured because no exception state is present. + +This mode is useful for understanding where your program spends time handling +errors. Exception handling can be a significant source of overhead in code +that uses exceptions for flow control (such as ``StopIteration`` in iterators) +or in applications that process many error conditions (such as network servers +handling connection failures). + +Exception mode helps answer questions like "how much time is spent handling +exceptions?" and "which exception handlers are the most expensive?" It can +reveal hidden performance costs in code that catches and processes many +exceptions, even when those exceptions are handled gracefully. For example, +if a parsing library uses exceptions internally to signal format errors, this +mode will capture time spent in those handlers even if the calling code never +sees the exceptions. + + +Output formats +============== + +The profiler produces output in several formats, each suited to different +analysis workflows. The format is selected with a command-line flag, and +output goes to stdout, a file, or a directory depending on the format. + + +pstats format +------------- + +The pstats format (:option:`--pstats`) produces a text table similar to what +deterministic profilers generate. This is the default output format:: + + python -m profiling.sampling run script.py + python -m profiling.sampling run --pstats script.py + +.. figure:: tachyon-pstats.png + :alt: Tachyon pstats terminal output + :align: center + :width: 100% + + The pstats format displays profiling results in a color-coded table showing + function hotspots, sample counts, and timing estimates. + +Output appears on stdout by default:: + + Profile Stats (Mode: wall): + nsamples sample% tottime (ms) cumul% cumtime (ms) filename:lineno(function) + 234/892 11.7% 234.00 44.6% 892.00 server.py:145(handle_request) + 156/156 7.8% 156.00 7.8% 156.00 :0(socket.recv) + 98/421 4.9% 98.00 21.1% 421.00 parser.py:67(parse_message) + +The columns show sampling counts and estimated times: + +- **nsamples**: Displayed as ``direct/cumulative`` (for example, ``10/50``). + Direct samples are when the function was at the top of the stack, actively + executing. Cumulative samples are when the function appeared anywhere on the + stack, including when it was waiting for functions it called. If a function + shows ``10/50``, it was directly executing in 10 samples and was on the call + stack in 50 samples total. + +- **sample%** and **cumul%**: Percentages of total samples for direct and + cumulative counts respectively. + +- **tottime** and **cumtime**: Estimated wall-clock time based on sample counts + and the profiling duration. Time units are selected automatically based on + the magnitude: seconds for large values, milliseconds for moderate values, + or microseconds for small values. + +The output includes a legend explaining each column and a summary of +interesting functions that highlights: + +- **Hot spots**: Functions with high direct/cumulative sample ratio (ratio + close to 1.0). These functions spend most of their time executing their own + code rather than waiting for callees. High ratios indicate where CPU time + is actually consumed. + +- **Indirect calls**: Functions with large differences between cumulative and + direct samples. These are orchestration functions that delegate work to + other functions. They appear frequently on the stack but rarely at the top. + +- **Call magnification**: Functions where cumulative samples far exceed direct + samples (high cumulative/direct multiplier). These are frequently-nested + functions that appear deep in many call chains. + +Use :option:`--no-summary` to suppress both the legend and summary sections. + +To save pstats output to a binary file instead of stdout:: + + python -m profiling.sampling run -o profile.pstats script.py + +The pstats format supports several options for controlling the display. +The :option:`--sort` option determines the column used for ordering results:: + + python -m profiling.sampling run --sort=tottime script.py + python -m profiling.sampling run --sort=cumtime script.py + python -m profiling.sampling run --sort=nsamples script.py + +The :option:`--limit` option restricts output to the top N entries:: + + python -m profiling.sampling run --limit=30 script.py + +The :option:`--no-summary` option suppresses the header summary that precedes the +statistics table. + + +Collapsed stacks format +----------------------- + +Collapsed stacks format (:option:`--collapsed`) produces one line per unique call +stack, with a count of how many times that stack was sampled:: + + python -m profiling.sampling run --collapsed script.py + +The output looks like: + +.. code-block:: text + + main;process_data;parse_json;decode_utf8 42 + main;process_data;parse_json 156 + main;handle_request;send_response 89 + +Each line contains semicolon-separated function names representing the call +stack from bottom to top, followed by a space and the sample count. This +format is designed for compatibility with external flame graph tools, +particularly Brendan Gregg's ``flamegraph.pl`` script. + +To generate a flame graph from collapsed stacks:: + + python -m profiling.sampling run --collapsed script.py > stacks.txt + flamegraph.pl stacks.txt > profile.svg + +The resulting SVG can be viewed in any web browser and provides an interactive +visualization where you can click to zoom into specific call paths. + + +Flame graph format +------------------ + +Flame graph format (:option:`--flamegraph`) produces a self-contained HTML file with +an interactive flame graph visualization:: + + python -m profiling.sampling run --flamegraph script.py + python -m profiling.sampling run --flamegraph -o profile.html script.py + +.. figure:: tachyon-flamegraph.png + :alt: Tachyon interactive flame graph + :align: center + :width: 100% + + The flame graph visualization shows call stacks as nested rectangles, with + width proportional to time spent. The sidebar displays runtime statistics, + GIL metrics, and hotspot functions. + +.. only:: html + + `Try the interactive example <../_static/tachyon-example-flamegraph.html>`__! + +If no output file is specified, the profiler generates a filename based on +the process ID (for example, ``flamegraph.12345.html``). + +The generated HTML file requires no external dependencies and can be opened +directly in a web browser. The visualization displays call stacks as nested +rectangles, with width proportional to time spent. Hovering over a rectangle +shows details about that function including source code context, and clicking +zooms into that portion of the call tree. + +The flame graph interface includes: + +- A sidebar showing profile summary, thread statistics, sampling efficiency + metrics (see :ref:`sampling-efficiency`), and top hotspot functions +- Search functionality supporting both function name matching and + ``file.py:42`` line patterns +- Per-thread filtering via dropdown +- Dark/light theme toggle (preference saved across sessions) +- SVG export for saving the current view + +The thread statistics section shows runtime behavior metrics: + +- **GIL Held**: percentage of samples where a thread held the global interpreter + lock (actively running Python code) +- **GIL Released**: percentage of samples where no thread held the GIL +- **Waiting GIL**: percentage of samples where a thread was waiting to acquire + the GIL +- **GC**: percentage of samples during garbage collection + +These statistics help identify GIL contention and understand how time is +distributed between Python execution, native code, and waiting. + +Flame graphs are particularly effective for identifying deep call stacks and +understanding the hierarchical structure of time consumption. Wide rectangles +at the top indicate functions that consume significant time either directly +or through their callees. + + +Differential flame graphs +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Differential flame graphs compare two profiling runs to highlight where +performance changed. This helps identify regressions introduced by code changes +and validate that optimizations achieved their intended effect:: + + # Capture baseline profile + python -m profiling.sampling run --binary -o baseline.bin script.py + + # After modifying code, generate differential flamegraph + python -m profiling.sampling run --diff-flamegraph baseline.bin -o diff.html script.py + +The visualization draws the current profile with frame widths showing current +time consumption, then applies color to indicate how each function changed +relative to the baseline. + +**Color coding**: + +- **Red**: Functions consuming more time (regressions). Lighter shades indicate + modest increases, while darker shades show severe regressions. + +- **Blue**: Functions consuming less time (improvements). Lighter shades for + modest reductions, darker shades for significant speedups. + +- **Gray**: Minimal or no change. + +- **Purple**: New functions not present in the baseline. + +Frame colors indicate changes in **direct time** (time when the function was at +the top of the stack, actively executing), not cumulative time including callees. +Hovering over a frame shows comparison details including baseline time, current +time, and the percentage change. + +Some call paths may disappear entirely between profiles. These are called +**elided stacks** and occur when optimizations eliminate code paths or certain +branches stop executing. If elided stacks are present, an elided toggle appears +allowing you to switch between the main differential view and an elided-only +view that shows just the removed paths (colored purple). + + +Gecko format +------------ + +Gecko format (:option:`--gecko`) produces JSON output compatible with the Firefox +Profiler:: + + python -m profiling.sampling run --gecko script.py + python -m profiling.sampling run --gecko -o profile.json script.py + +The `Firefox Profiler `__ is a sophisticated +web-based tool originally built for profiling Firefox itself. It provides +features beyond basic flame graphs, including a timeline view, call tree +exploration, and marker visualization. See the +`Firefox Profiler documentation `__ for +detailed usage instructions. + +To use the output, open the Firefox Profiler in your browser and load the +JSON file. The profiler runs entirely client-side, so your profiling data +never leaves your machine. + +Gecko format automatically collects additional metadata about GIL state and +CPU activity, enabling analysis features specific to Python's threading model. +The profiler emits interval markers that appear as colored bands in the +Firefox Profiler timeline: + +- **GIL markers**: show when threads hold or release the global interpreter lock +- **CPU markers**: show when threads are executing on CPU versus idle +- **Code type markers**: distinguish Python code from native (C extension) code +- **GC markers**: indicate garbage collection activity + +For this reason, the :option:`--mode` option is not available with Gecko format; +all relevant data is captured automatically. + +.. figure:: tachyon-gecko-calltree.png + :alt: Firefox Profiler Call Tree view + :align: center + :width: 100% + + The Call Tree view shows the complete call hierarchy with sample counts + and percentages. The sidebar displays detailed statistics for the + selected function including running time and sample distribution. + +.. figure:: tachyon-gecko-flamegraph.png + :alt: Firefox Profiler Flame Graph view + :align: center + :width: 100% + + The Flame Graph visualization shows call stacks as nested rectangles. + Functions names are visible in the call hierarchy. + +.. figure:: tachyon-gecko-opcodes.png + :alt: Firefox Profiler Marker Chart with opcodes + :align: center + :width: 100% + + The Marker Chart displays interval markers including CPU state, GIL + status, and opcodes. With ``--opcodes`` enabled, bytecode instructions + like ``BINARY_OP_ADD_FLOAT``, ``CALL_PY_EXACT_ARGS``, and + ``CALL_LIST_APPEND`` appear as markers showing execution over time. + + +Heatmap format +-------------- + +Heatmap format (:option:`--heatmap`) generates an interactive HTML visualization +showing sample counts at the source line level:: + + python -m profiling.sampling run --heatmap script.py + python -m profiling.sampling run --heatmap -o my_heatmap script.py + +.. figure:: tachyon-heatmap.png + :alt: Tachyon heatmap visualization + :align: center + :width: 100% + + The heatmap overlays sample counts directly on your source code. Lines are + color-coded from cool (few samples) to hot (many samples). Navigation + buttons (▲▼) let you jump between callers and callees. + +Unlike other formats that produce a single file, heatmap output creates a +directory containing HTML files for each profiled source file. If no output +path is specified, the directory is named ``heatmap_PID``. + +The heatmap visualization displays your source code with a color gradient +indicating how many samples were collected at each line. Hot lines (many +samples) appear in warm colors, while cold lines (few or no samples) appear +in cool colors. This view helps pinpoint exactly which lines of code are +responsible for time consumption. + +The heatmap interface provides several interactive features: + +- **Coloring modes**: toggle between "Self Time" (direct execution) and + "Total Time" (cumulative, including time in called functions) +- **Cold code filtering**: show all lines or only lines with samples +- **Call graph navigation**: each line shows navigation buttons (▲ for callers, + ▼ for callees) that let you trace execution paths through your code. When + multiple functions called or were called from a line, a menu appears showing + all options with their sample counts. +- **Scroll minimap**: a vertical overview showing the heat distribution across + the entire file +- **Hierarchical index**: files organized by type (stdlib, site-packages, + project) with aggregate sample counts per folder +- **Dark/light theme**: toggle with preference saved across sessions +- **Line linking**: click line numbers to create shareable URLs + +When opcode-level profiling is enabled with :option:`--opcodes`, each hot line +can be expanded to show which bytecode instructions consumed time: + +.. figure:: tachyon-heatmap-with-opcodes.png + :alt: Heatmap with expanded bytecode panel + :align: center + :width: 100% + + Expanding a hot line reveals the bytecode instructions executed, including + specialized variants. The panel shows sample counts per instruction and the + overall specialization percentage for the line. + +.. only:: html + + `Try the interactive example <../_static/tachyon-example-heatmap.html>`__! + +Heatmaps are especially useful when you know which file contains a performance +issue but need to identify the specific lines. Many developers prefer this +format because it maps directly to their source code, making it easy to read +and navigate. For smaller scripts and focused analysis, heatmaps provide an +intuitive view that shows exactly where time is spent without requiring +interpretation of hierarchical visualizations. + + +Binary format +------------- + +Binary format (:option:`--binary`) produces a compact binary file for efficient +storage of profiling data:: + + python -m profiling.sampling run --binary -o profile.bin script.py + python -m profiling.sampling attach --binary -o profile.bin 12345 + +The :option:`--compression` option controls data compression: + +- ``auto`` (default): Use zstd compression if available, otherwise no + compression +- ``zstd``: Force zstd compression (requires :mod:`compression.zstd` support) +- ``none``: Disable compression + +:: + + python -m profiling.sampling run --binary --compression=zstd -o profile.bin script.py + +To analyze binary profiles, use the :ref:`replay-command` to convert them to +other formats like flame graphs or pstats output. + + +Record and replay workflow +========================== + +The binary format combined with the replay command enables a record-and-replay +workflow that separates data capture from analysis. Rather than generating +visualizations during profiling, you capture raw data to a compact binary file +and convert it to different formats later. + +This approach has three main benefits: + +- Sampling runs faster because the work of building data structures for + visualization is deferred until replay. +- A single binary capture can be converted to multiple output formats + without re-profiling: pstats for a quick overview, flame graph for visual + exploration, heatmap for line-level detail. +- Binary files are compact and easy to share with colleagues who can convert + them to their preferred format. + +A typical workflow:: + + # Capture profile in production or during tests + python -m profiling.sampling attach --binary -o profile.bin 12345 + + # Later, analyze with different formats + python -m profiling.sampling replay profile.bin + python -m profiling.sampling replay --flamegraph -o profile.html profile.bin + python -m profiling.sampling replay --heatmap -o heatmap profile.bin + + +Live mode +========= + +Live mode (:option:`--live`) provides a terminal-based real-time view of profiling +data, similar to the ``top`` command for system processes:: + + python -m profiling.sampling run --live script.py + python -m profiling.sampling attach --live 12345 + +.. only:: not latex + + .. figure:: tachyon-live-mode-2.gif + :alt: Tachyon live mode showing all threads + :align: center + :width: 100% + + Live mode displays real-time profiling statistics, showing combined + data from multiple threads in a multi-threaded application. + +The display updates continuously as new samples arrive, showing the current +hottest functions. This mode requires the :mod:`curses` module, which is +available on Unix-like systems but not on Windows. The terminal must be at +least 60 columns wide and 12 lines tall; larger terminals display more columns. + +The header displays the top 3 hottest functions, sampling efficiency metrics, +and thread status statistics (GIL held percentage, CPU usage, GC time). The +main table shows function statistics with the currently sorted column indicated +by an arrow (▼). + +When :option:`--opcodes` is enabled, an additional opcode panel appears below the +main table, showing instruction-level statistics for the currently selected +function. This panel displays which bytecode instructions are executing most +frequently, including specialized variants and their base opcodes. + +.. only:: not latex + + .. figure:: tachyon-live-mode-1.gif + :alt: Tachyon live mode with opcode panel + :align: center + :width: 100% + + Live mode with ``--opcodes`` enabled shows an opcode panel with a bytecode + instruction breakdown for the selected function. + + +Keyboard commands +----------------- + +Within live mode, keyboard commands control the display: + +:kbd:`q` + Quit the profiler and return to the shell. + +:kbd:`s` / :kbd:`S` + Cycle through sort orders forward/backward (sample count, percentage, + total time, cumulative percentage, cumulative time). + +:kbd:`p` + Pause or resume display updates. Sampling continues in the background + while the display is paused, so you can freeze the view to examine results + without stopping data collection. + +:kbd:`r` + Reset all statistics and start fresh. This is disabled after profiling + finishes to prevent accidental data loss. + +:kbd:`/` + Enter filter mode to search for functions by name. The filter uses + case-insensitive substring matching against the filename and function name. + Type a pattern and press Enter to apply, or Escape to cancel. Glob patterns + and regular expressions are not supported. + +:kbd:`c` + Clear the current filter and show all functions again. + +:kbd:`t` + Toggle between viewing all threads combined or per-thread statistics. + In per-thread mode, a thread counter (for example, ``1/4``) appears showing + your position among the available threads. + +:kbd:`←` :kbd:`→` or :kbd:`↑` :kbd:`↓` + In per-thread view, navigate between threads. Navigation wraps around + from the last thread to the first and vice versa. + +:kbd:`+` / :kbd:`-` + Increase or decrease the display refresh rate. The range is 0.05 seconds + (20 Hz, very responsive) to 1.0 second (1 Hz, lower overhead). Faster refresh + rates use more CPU. The default is 0.1 seconds (10 Hz). + +:kbd:`x` + Toggle trend indicators that show whether functions are becoming hotter + or cooler over time. When enabled, increasing metrics appear in green and + decreasing metrics appear in red, comparing each update to the previous one. + +:kbd:`h` or :kbd:`?` + Show the help screen with all available commands. + +:kbd:`j` / :kbd:`k` (or :kbd:`Up` / :kbd:`Down`) + Navigate through opcode entries in the opcode panel (when ``--opcodes`` is + enabled). These keys scroll through the instruction-level statistics for the + currently selected function. + +When profiling finishes (duration expires or target process exits), the display +shows a "PROFILING COMPLETE" banner and freezes the final results. You can +still navigate, sort, and filter the results before pressing :kbd:`q` to exit. + +Live mode is incompatible with output format options (:option:`--collapsed`, +:option:`--flamegraph`, and so on) because it uses an interactive terminal +interface rather than producing file output. + + +Async-aware profiling +===================== + +For programs using :mod:`asyncio`, the profiler offers async-aware mode +(:option:`--async-aware`) that reconstructs call stacks based on the task structure +rather than the raw Python frames:: + + python -m profiling.sampling run --async-aware async_script.py + +Standard profiling of async code can be confusing because the physical call +stack often shows event loop internals rather than the logical flow of your +coroutines. Async-aware mode addresses this by tracking which task is running +and presenting stacks that reflect the ``await`` chain. + +.. code-block:: python + + import asyncio + + async def fetch(url): + await asyncio.sleep(0.1) + return url + + async def main(): + for _ in range(50): + await asyncio.gather(fetch("a"), fetch("b"), fetch("c")) + + if __name__ == "__main__": + asyncio.run(main()) + +:: + + python -m profiling.sampling run --async-aware --flamegraph -o out.html script.py + +.. note:: + + Async-aware profiling requires the target process to have the :mod:`asyncio` + module loaded. If you profile a script before it imports asyncio, async-aware + mode will not be able to capture task information. + + +Async modes +----------- + +The :option:`--async-mode` option controls which tasks appear in the profile:: + + python -m profiling.sampling run --async-aware --async-mode=running async_script.py + python -m profiling.sampling run --async-aware --async-mode=all async_script.py + +With :option:`--async-mode`\ ``=running`` (the default), only the task currently executing +on the CPU is profiled. This shows where your program is actively spending time +and is the typical choice for performance analysis. + +With :option:`--async-mode`\ ``=all``, tasks that are suspended (awaiting I/O, locks, or +other tasks) are also included. This mode is useful for understanding what your +program is waiting on, but produces larger profiles since every suspended task +appears in each sample. + + +Task markers and stack reconstruction +------------------------------------- + +In async-aware profiles, you will see ```` frames that mark boundaries +between asyncio tasks. These are synthetic frames inserted by the profiler to +show the task structure. The task name appears as the function name in these +frames. + +When a task awaits another task, the profiler reconstructs the logical call +chain by following the ``await`` relationships. Only "leaf" tasks (tasks that +no other task is currently awaiting) generate their own stack entries. Tasks +being awaited by other tasks appear as part of their awaiter's stack instead. + +If a task has multiple awaiters (a diamond pattern in the task graph), the +profiler deterministically selects one parent and annotates the task marker +with the number of parents, for example ``MyTask (2 parents)``. This indicates +that alternate execution paths exist but are not shown in this particular stack. + + +Option restrictions +------------------- + +Async-aware mode uses a different stack reconstruction mechanism and is +incompatible with: :option:`--native`, :option:`--no-gc`, :option:`--all-threads`, and +:option:`--mode`\ ``=cpu`` or :option:`--mode`\ ``=gil``. + + +Command-line interface +====================== + +.. program:: profiling.sampling + +The complete command-line interface for reference. + + +Global options +-------------- + +.. option:: run + + Run and profile a Python script or module. + +.. option:: attach + + Attach to and profile a running process by PID. + +.. option:: dump + + Print a single one-shot snapshot of a running process's Python stack. + +.. option:: replay + + Convert a binary profile file to another output format. + + +Dump options +------------ + +The following options apply to the ``dump`` subcommand: + +.. option:: -a, --all-threads + + Dump all threads in the target process instead of just the main thread. + +.. option:: --native + + Include ```` frames for non-Python code. + +.. option:: --no-gc + + Exclude ```` frames for active garbage collection. + +.. option:: --opcodes + + Show bytecode opcode names when available. + +.. option:: --async-aware + + Reconstruct the stack across ``await`` boundaries for asyncio + applications. + +.. option:: --async-mode + + Async stack mode: ``running`` (only the running task) or ``all`` + (all tasks including waiting). Defaults to ``all`` for ``dump``. + Requires :option:`--async-aware`. + +.. option:: --blocking + + Pause all threads in the target process while reading the stack. + + +Sampling options +---------------- + +.. option:: -r , --sampling-rate + + Sampling rate (for example, ``10000``, ``10khz``, ``10k``). Default: ``1khz``. + +.. option:: -d , --duration + + Profiling duration in seconds. Default: run to completion. + +.. option:: -a, --all-threads + + Sample all threads, not just the main thread. + +.. option:: --realtime-stats + + Display sampling statistics during profiling. + +.. option:: --native + + Include ```` frames for non-Python code. + +.. option:: --no-gc + + Exclude ```` frames for garbage collection. + +.. option:: --async-aware + + Enable async-aware profiling for asyncio programs. + +.. option:: --opcodes + + Gather bytecode opcode information for instruction-level profiling. Shows + which bytecode instructions are executing, including specializations. + Compatible with ``--live``, ``--flamegraph``, ``--heatmap``, and ``--gecko`` + formats only. + +.. option:: --subprocesses + + Also profile subprocesses. Each subprocess gets its own profiler + instance and output file. Incompatible with ``--live``. + +.. option:: --blocking + + Pause the target process during each sample. This ensures consistent + stack traces at the cost of slowing down the target. Use with longer + intervals (1000 µs or higher) to minimize impact. See :ref:`blocking-mode` + for details. + + +Mode options +------------ + +.. option:: --mode + + Sampling mode: ``wall`` (default), ``cpu``, ``gil``, or ``exception``. + The ``cpu``, ``gil``, and ``exception`` modes are incompatible with + ``--async-aware``. + +.. option:: --async-mode + + Async profiling mode: ``running`` (default) or ``all``. + Requires ``--async-aware``. + + +Output options +-------------- + +.. option:: --pstats + + Generate pstats statistics. This is the default. + When written to stdout, the output is a text table; with :option:`-o`, + it is a binary pstats file. + +.. option:: --collapsed + + Generate collapsed stack format for external flame graph tools. + +.. option:: --flamegraph + + Generate self-contained HTML flame graph. + +.. option:: --diff-flamegraph + + Generate differential flamegraph comparing to a baseline binary profile. + +.. option:: --gecko + + Generate Gecko JSON format for Firefox Profiler. + +.. option:: --heatmap + + Generate HTML heatmap with line-level sample counts. + +.. option:: --binary + + Generate high-performance binary format for later conversion with the + ``replay`` command. + +.. option:: --compression + + Compression for binary format: ``auto`` (use zstd if available, default), + ``zstd``, or ``none``. + +.. option:: -o , --output + + Output file or directory path. Default behavior varies by format: + :option:`--pstats` prints a text table to stdout, while ``-o`` writes a + binary pstats file. Other formats generate a file named + ``_.`` (for example, ``flamegraph_12345.html``). + :option:`--heatmap` creates a directory named ``heatmap_``. + +.. option:: --browser + + Automatically open HTML output (:option:`--flamegraph` and + :option:`--heatmap`) in your default web browser after generation. + When profiling with :option:`--subprocesses`, only the main process + opens the browser; subprocess outputs are never auto-opened. + + +pstats display options +---------------------- + +These options apply only to pstats format output. + +.. option:: --sort + + Sort order: ``nsamples``, ``tottime``, ``cumtime``, ``sample-pct``, + ``cumul-pct``, ``nsamples-cumul``, or ``name``. Default: ``nsamples``. + +.. option:: -l , --limit + + Maximum number of entries to display. Default: 15. + +.. option:: --no-summary + + Omit the Legend and Summary of Interesting Functions sections from output. + + +Run command options +------------------- + +.. option:: -m, --module + + Treat the target as a module name rather than a script path. + +.. option:: --live + + Start interactive terminal interface instead of batch profiling. + + +.. seealso:: + + :mod:`profiling` + Overview of Python profiling tools and guidance on choosing a profiler. + + :mod:`profiling.tracing` + Deterministic tracing profiler for exact call counts and timing. + + :mod:`pstats` + Statistics analysis for profile data. + + `Firefox Profiler `__ + Web-based profiler that accepts Gecko format output. See the + `documentation `__ for usage details. + + `FlameGraph `__ + Tools for generating flame graphs from collapsed stack format. diff --git a/Doc/library/profiling.tracing.rst b/Doc/library/profiling.tracing.rst new file mode 100644 index 00000000000000..d45423cf0d8a72 --- /dev/null +++ b/Doc/library/profiling.tracing.rst @@ -0,0 +1,331 @@ +.. _profiling-tracing: + +**************************************************** +:mod:`!profiling.tracing` --- Deterministic profiler +**************************************************** + +.. module:: profiling.tracing + :synopsis: Deterministic tracing profiler for Python programs. + +.. module:: cProfile + :synopsis: Alias for profiling.tracing (backward compatibility). + :noindex: + +.. versionadded:: 3.15 + +**Source code:** :source:`Lib/profiling/tracing/` + +-------------- + +The :mod:`!profiling.tracing` module provides deterministic profiling of Python +programs. It monitors every function call, function return, and exception event, +recording precise timing for each. This approach provides exact call counts and +complete visibility into program execution, making it ideal for development and +testing scenarios. + +.. note:: + + This module is also available as ``cProfile`` for backward compatibility. + The ``cProfile`` name will continue to work in all future Python versions. + Use whichever import style suits your codebase:: + + # Preferred (new style) + import profiling.tracing + profiling.tracing.run('my_function()') + + # Also works (backward compatible) + import cProfile + cProfile.run('my_function()') + + +What is deterministic profiling? +================================ + +:dfn:`Deterministic profiling` captures every function call, function return, +and exception event during program execution. The profiler measures the precise +time intervals between these events, providing exact statistics about how the +program behaves. + +In contrast to :ref:`statistical profiling `, which samples +the call stack periodically to estimate where time is spent, deterministic +profiling records every event. This means you get exact call counts rather than +statistical approximations. The trade-off is that instrumenting every event +introduces overhead that can slow down program execution. + +Python's interpreted nature makes deterministic profiling practical. The +interpreter already dispatches events for function calls and returns, so the +profiler can hook into this mechanism without requiring code modification. The +overhead tends to be moderate relative to the inherent cost of interpretation, +making deterministic profiling suitable for most development workflows. + +Deterministic profiling helps answer questions like: + +- How many times was this function called? +- What is the complete call graph of my program? +- Which functions are called by a particular function? +- Are there unexpected function calls happening? + +Call count statistics can identify bugs (surprising counts) and inline +expansion opportunities (high call counts). Internal time statistics reveal +"hot loops" that warrant optimization. Cumulative time statistics help identify +algorithmic inefficiencies. The handling of cumulative times in this profiler +allows direct comparison of recursive and iterative implementations. + + +.. _profiling-tracing-cli: + +Command-line interface +====================== + +.. program:: profiling.tracing + +The :mod:`!profiling.tracing` module can be invoked as a script to profile +another script or module: + +.. code-block:: shell-session + + python -m profiling.tracing [-o output_file] [-s sort_order] (-m module | script.py) + +This runs the specified script or module under the profiler and prints the +results to standard output (or saves them to a file). + +.. option:: -o + + Write the profile results to a file instead of standard output. The output + file can be read by the :mod:`pstats` module for later analysis. + +.. option:: -s + + Sort the output by the specified key. This accepts any of the sort keys + recognized by :meth:`pstats.Stats.sort_stats`, such as ``cumulative``, + ``time``, ``calls``, or ``name``. This option only applies when + :option:`-o ` is not specified. + +.. option:: -m + + Profile a module instead of a script. The module is located using the + standard import mechanism. + + .. versionadded:: 3.7 + The ``-m`` option for ``cProfile``. + + .. versionadded:: 3.8 + The ``-m`` option for :mod:`profile`. + + +Programmatic usage examples +=========================== + +For more control over profiling, use the module's functions and classes +directly. + + +Basic profiling +--------------- + +The simplest approach uses the :func:`!run` function:: + + import profiling.tracing + profiling.tracing.run('my_function()') + +This profiles the given code string and prints a summary to standard output. +To save results for later analysis:: + + profiling.tracing.run('my_function()', 'output.prof') + + +Using the :class:`!Profile` class +--------------------------------- + +The :class:`!Profile` class provides fine-grained control:: + + import profiling.tracing + import pstats + from io import StringIO + + pr = profiling.tracing.Profile() + pr.enable() + # ... code to profile ... + pr.disable() + + # Print results + s = StringIO() + ps = pstats.Stats(pr, stream=s).sort_stats(pstats.SortKey.CUMULATIVE) + ps.print_stats() + print(s.getvalue()) + +The :class:`!Profile` class also works as a context manager:: + + import profiling.tracing + + with profiling.tracing.Profile() as pr: + # ... code to profile ... + + pr.print_stats() + + +Module reference +================ + +.. currentmodule:: profiling.tracing + +.. function:: run(command, filename=None, sort=-1) + + Profile execution of a command and print or save the results. + + This function executes the *command* string using :func:`exec` in the + ``__main__`` module's namespace:: + + exec(command, __main__.__dict__, __main__.__dict__) + + If *filename* is not provided, the function creates a :class:`pstats.Stats` + instance and prints a summary to standard output. If *filename* is + provided, the raw profile data is saved to that file for later analysis + with :mod:`pstats`. + + The *sort* argument specifies the sort order for printed output, accepting + any value recognized by :meth:`pstats.Stats.sort_stats`. + + +.. function:: runctx(command, globals, locals, filename=None, sort=-1) + + Profile execution of a command with explicit namespaces. + + Like :func:`run`, but executes the command with the specified *globals* + and *locals* mappings instead of using the ``__main__`` module's namespace:: + + exec(command, globals, locals) + + +.. class:: Profile(timer=None, timeunit=0.0, subcalls=True, builtins=True) + + A profiler object that collects execution statistics. + + The optional *timer* argument specifies a custom timing function. If not + provided, the profiler uses a platform-appropriate default timer. When + supplying a custom timer, it must return a single number representing the + current time. If the timer returns integers, use *timeunit* to specify the + duration of one time unit (for example, ``0.001`` for milliseconds). + + The *subcalls* argument controls whether the profiler tracks call + relationships between functions. The *builtins* argument controls whether + built-in functions are profiled. + + .. versionchanged:: 3.8 + Added context manager support. + + .. method:: enable() + + Start collecting profiling data. + + .. method:: disable() + + Stop collecting profiling data. + + .. method:: create_stats() + + Stop collecting data and record the results internally as the current + profile. + + .. method:: print_stats(sort=-1) + + Create a :class:`pstats.Stats` object from the current profile and print + the results to standard output. + + The *sort* argument specifies the sorting order. It accepts a single + key or a tuple of keys for multi-level sorting, using the same values + as :meth:`pstats.Stats.sort_stats`. + + .. versionadded:: 3.13 + Support for a tuple of sort keys. + + .. method:: dump_stats(filename) + + Write the current profile data to *filename*. The file can be read by + :class:`pstats.Stats` for later analysis. + + .. method:: run(cmd) + + Profile the command string via :func:`exec`. + + .. method:: runctx(cmd, globals, locals) + + Profile the command string via :func:`exec` with the specified + namespaces. + + .. method:: runcall(func, /, *args, **kwargs) + + Profile a function call. Returns whatever *func* returns:: + + result = pr.runcall(my_function, arg1, arg2, keyword=value) + +.. note:: + + Profiling requires that the profiled code returns normally. If the + interpreter terminates (for example, via :func:`sys.exit`) during + profiling, no results will be available. + + +Using a custom timer +==================== + +The :class:`Profile` class accepts a custom timing function, allowing you to +measure different aspects of execution such as wall-clock time or CPU time. +Pass the timing function to the constructor:: + + pr = profiling.tracing.Profile(my_timer_function) + +The timer function must return a single number representing the current time. +If it returns integers, also specify *timeunit* to indicate the duration of +one unit:: + + # Timer returns time in milliseconds + pr = profiling.tracing.Profile(my_ms_timer, 0.001) + +For best performance, the timer function should be as fast as possible. The +profiler calls it frequently, so timer overhead directly affects profiling +overhead. + +The :mod:`time` module provides several functions suitable for use as custom +timers: + +- :func:`time.perf_counter` for high-resolution wall-clock time +- :func:`time.process_time` for CPU time (excluding sleep) +- :func:`time.monotonic` for monotonic clock time + + +Limitations +=========== + +Deterministic profiling has inherent limitations related to timing accuracy. + +The underlying timer typically has a resolution of about one millisecond. +Measurements cannot be more accurate than this resolution. With enough +measurements, timing errors tend to average out, but individual measurements +may be imprecise. + +There is also latency between when an event occurs and when the profiler +captures the timestamp. Similarly, there is latency after reading the +timestamp before user code resumes. Functions called frequently accumulate +this latency, which can make them appear slower than they actually are. This +error is typically less than one clock tick per call but can become +significant for functions called many times. + +The :mod:`!profiling.tracing` module (and its ``cProfile`` alias) is +implemented as a C extension with low overhead, so these timing issues are +less pronounced than with the deprecated pure Python :mod:`profile` module. + + +.. seealso:: + + :mod:`profiling` + Overview of Python profiling tools and guidance on choosing a profiler. + + :mod:`profiling.sampling` + Statistical sampling profiler for production use. + + :mod:`pstats` + Statistics analysis and formatting for profile data. + + :mod:`profile` + Deprecated pure Python profiler (includes calibration documentation). diff --git a/Doc/library/pstats.rst b/Doc/library/pstats.rst new file mode 100644 index 00000000000000..585f17bdb99a70 --- /dev/null +++ b/Doc/library/pstats.rst @@ -0,0 +1,362 @@ +.. _pstats-module: + +******************************************* +:mod:`!pstats` --- Statistics for profilers +******************************************* + +.. module:: pstats + :synopsis: Statistics object for analyzing profiler output. + +**Source code:** :source:`Lib/pstats.py` + +-------------- + +The :mod:`!pstats` module provides tools for reading, manipulating, and +displaying profiling statistics generated by Python's profilers. It reads +output from both :mod:`profiling.tracing` (deterministic profiler) and +:mod:`profiling.sampling` (statistical profiler). + + +Reading and displaying profile data +=================================== + +The :class:`Stats` class is the primary interface for working with profile +data. It can read statistics from files or directly from a +:class:`~profiling.tracing.Profile` object. + +Load statistics from a file and print a basic report:: + + import pstats + + p = pstats.Stats('profile_output.prof') + p.print_stats() + +The :class:`Stats` object provides methods for sorting and filtering the +data before printing. For example, to see the ten functions with the highest +cumulative time:: + + from pstats import SortKey + + p = pstats.Stats('profile_output.prof') + p.sort_stats(SortKey.CUMULATIVE).print_stats(10) + + +Working with statistics +----------------------- + +The :class:`Stats` class supports method chaining, making it convenient to +perform multiple operations:: + + p = pstats.Stats('restats') + p.strip_dirs().sort_stats(-1).print_stats() + +The :meth:`~Stats.strip_dirs` method removes directory paths from filenames, +making the output more compact. The :meth:`~Stats.sort_stats` method accepts +various keys to control the sort order. + +Different sort keys highlight different aspects of performance:: + + from pstats import SortKey + + # Functions that consume the most cumulative time + p.sort_stats(SortKey.CUMULATIVE).print_stats(10) + + # Functions that consume the most time in their own code + p.sort_stats(SortKey.TIME).print_stats(10) + + # Functions sorted by name + p.sort_stats(SortKey.NAME).print_stats() + + +Filtering output +---------------- + +The :meth:`~Stats.print_stats` method accepts restrictions that filter +which functions are displayed. Restrictions can be integers (limiting the +count), floats between 0 and 1 (selecting a percentage), or strings (matching +function names via regular expression). + +Print only the top 10%:: + + p.print_stats(.1) + +Print only functions whose names contain "init":: + + p.print_stats('init') + +Combine restrictions (they apply sequentially):: + + # Top 10%, then only those containing "init" + p.print_stats(.1, 'init') + + # Functions in files matching "foo:", limited to top 50% + p.sort_stats(SortKey.FILENAME).print_stats('foo:', .5) + + +Analyzing call relationships +---------------------------- + +The :meth:`~Stats.print_callers` method shows which functions called each +displayed function:: + + p.print_callers() + +The :meth:`~Stats.print_callees` method shows the opposite relationship, +listing which functions each displayed function called:: + + p.print_callees() + +Both methods accept the same restriction arguments as :meth:`~Stats.print_stats`. + + +Combining multiple profiles +--------------------------- + +Statistics from multiple profiling runs can be combined into a single +:class:`Stats` object:: + + # Load multiple files at once + p = pstats.Stats('run1.prof', 'run2.prof', 'run3.prof') + + # Or add files incrementally + p = pstats.Stats('run1.prof') + p.add('run2.prof') + p.add('run3.prof') + +When files are combined, statistics for identical functions (same file, line, +and name) are accumulated, giving an aggregate view across all profiling runs. + + +The :class:`!Stats` class +========================= + +.. class:: Stats(*filenames_or_profile, stream=sys.stdout) + + Create a statistics object from profile data. + + The arguments can be filenames (strings or path-like objects) or + :class:`~profiling.tracing.Profile` objects. If multiple sources are + provided, their statistics are combined. + + The *stream* argument specifies where output from :meth:`print_stats` and + related methods is written. It defaults to :data:`sys.stdout`. + + The profile data format is specific to the Python version that created it. + There is no compatibility guarantee between Python versions or between + different profilers. + + .. method:: strip_dirs() + + Remove leading path information from all filenames. + + This method modifies the object in place and returns it for method + chaining. After stripping, the statistics are considered to be in + random order. + + If stripping causes two functions to become indistinguishable (same + filename, line number, and function name), their statistics are + combined into a single entry. + + .. method:: add(*filenames) + + Add profiling data from additional files. + + The files must have been created by the same profiler type. Statistics + for identical functions are accumulated. + + .. method:: dump_stats(filename) + + Save the current statistics to a file. + + The file is created if it does not exist and overwritten if it does. + The saved data can be loaded by creating a new :class:`Stats` object. + + .. method:: sort_stats(*keys) + + Sort the statistics according to the specified criteria. + + Each key can be a string or a :class:`SortKey` enum member. When + multiple keys are provided, later keys break ties in earlier keys. + + Using :class:`SortKey` enum members is preferred over strings as it + provides better error checking:: + + from pstats import SortKey + p.sort_stats(SortKey.CUMULATIVE) + + Valid sort keys: + + +------------------+------------------------+----------------------+ + | String | Enum | Meaning | + +==================+========================+======================+ + | ``'calls'`` | ``SortKey.CALLS`` | call count | + +------------------+------------------------+----------------------+ + | ``'cumulative'`` | ``SortKey.CUMULATIVE`` | cumulative time | + +------------------+------------------------+----------------------+ + | ``'cumtime'`` | N/A | cumulative time | + +------------------+------------------------+----------------------+ + | ``'file'`` | N/A | file name | + +------------------+------------------------+----------------------+ + | ``'filename'`` | ``SortKey.FILENAME`` | file name | + +------------------+------------------------+----------------------+ + | ``'module'`` | N/A | file name | + +------------------+------------------------+----------------------+ + | ``'ncalls'`` | N/A | call count | + +------------------+------------------------+----------------------+ + | ``'pcalls'`` | ``SortKey.PCALLS`` | primitive call count | + +------------------+------------------------+----------------------+ + | ``'line'`` | ``SortKey.LINE`` | line number | + +------------------+------------------------+----------------------+ + | ``'name'`` | ``SortKey.NAME`` | function name | + +------------------+------------------------+----------------------+ + | ``'nfl'`` | ``SortKey.NFL`` | name/file/line | + +------------------+------------------------+----------------------+ + | ``'stdname'`` | ``SortKey.STDNAME`` | standard name | + +------------------+------------------------+----------------------+ + | ``'time'`` | ``SortKey.TIME`` | internal time | + +------------------+------------------------+----------------------+ + | ``'tottime'`` | N/A | internal time | + +------------------+------------------------+----------------------+ + + All sorts on statistics are in descending order (most time consuming + first), while name, file, and line number sorts are ascending + (alphabetical). + + The difference between ``SortKey.NFL`` and ``SortKey.STDNAME`` is that + NFL sorts line numbers numerically while STDNAME sorts them as strings. + ``sort_stats(SortKey.NFL)`` is equivalent to + ``sort_stats(SortKey.NAME, SortKey.FILENAME, SortKey.LINE)``. + + For backward compatibility, the numeric arguments ``-1``, ``0``, ``1``, + and ``2`` are also accepted, meaning ``'stdname'``, ``'calls'``, + ``'time'``, and ``'cumulative'`` respectively. + + .. versionadded:: 3.7 + The :class:`SortKey` enum. + + .. method:: reverse_order() + + Reverse the current sort order. + + By default, the sort direction is chosen appropriately for the sort key + (descending for time-based keys, ascending for name-based keys). This + method inverts that choice. + + .. method:: print_stats(*restrictions) + + Print a report of the profiling statistics. + + The output includes a header line summarizing the data, followed by a + table of function statistics sorted according to the last + :meth:`sort_stats` call. + + Restrictions filter the output. Each restriction is either: + + - An integer: limits output to that many entries + - A float between 0.0 and 1.0: selects that fraction of entries + - A string: matches function names via regular expression + + Restrictions are applied sequentially. For example:: + + print_stats(.1, 'foo:') + + First limits to the top 10%, then filters to functions matching 'foo:'. + + .. method:: print_callers(*restrictions) + + Print the callers of each function in the statistics. + + For each function in the filtered results, shows which functions called + it and how often. + + With :mod:`profiling.tracing` (or ``cProfile``), each caller line + shows three numbers: the number of calls from that caller, and the + total and cumulative times for those specific calls. + + Accepts the same restriction arguments as :meth:`print_stats`. + + .. method:: print_callees(*restrictions) + + Print the functions called by each function in the statistics. + + This is the inverse of :meth:`print_callers`, showing which functions + each listed function called. + + Accepts the same restriction arguments as :meth:`print_stats`. + + .. method:: get_stats_profile() + + Return a ``StatsProfile`` object containing the statistics. + + The returned object provides programmatic access to the profile data, + with function names mapped to ``FunctionProfile`` objects + containing timing and call count information. + + .. versionadded:: 3.9 + + +.. class:: SortKey + + An enumeration of valid sort keys for :meth:`Stats.sort_stats`. + + .. attribute:: CALLS + + Sort by call count. + + .. attribute:: CUMULATIVE + + Sort by cumulative time. + + .. attribute:: FILENAME + + Sort by file name. + + .. attribute:: LINE + + Sort by line number. + + .. attribute:: NAME + + Sort by function name. + + .. attribute:: NFL + + Sort by name, then file, then line number (numeric line sort). + + .. attribute:: PCALLS + + Sort by primitive (non-recursive) call count. + + .. attribute:: STDNAME + + Sort by standard name (string-based line sort). + + .. attribute:: TIME + + Sort by internal time (time in function excluding subcalls). + + +.. _pstats-cli: + +Command-line interface +====================== + +The :mod:`!pstats` module can be invoked as a script to interactively browse +profile data:: + + python -m pstats profile_output.prof + +This opens a line-oriented interface (built on :mod:`cmd`) for examining the +statistics. Type ``help`` at the prompt for available commands. + + +.. seealso:: + + :mod:`profiling` + Overview of Python profiling tools. + + :mod:`profiling.tracing` + Deterministic tracing profiler. + + :mod:`profiling.sampling` + Statistical sampling profiler. diff --git a/Doc/library/pty.rst b/Doc/library/pty.rst index 1a44bb13a841de..a7be5779fb2620 100644 --- a/Doc/library/pty.rst +++ b/Doc/library/pty.rst @@ -2,17 +2,13 @@ ========================================= .. module:: pty - :platform: Unix :synopsis: Pseudo-Terminal Handling for Unix. -.. moduleauthor:: Steen Lumholt -.. sectionauthor:: Moshe Zadka - **Source code:** :source:`Lib/pty.py` -------------- -The :mod:`pty` module defines operations for handling the pseudo-terminal +The :mod:`!pty` module defines operations for handling the pseudo-terminal concept: starting another process and being able to write to and read from its controlling terminal programmatically. @@ -22,7 +18,7 @@ Pseudo-terminal handling is highly platform dependent. This code is mainly tested on Linux, FreeBSD, and macOS (it is supposed to work on other POSIX platforms but it's not been thoroughly tested). -The :mod:`pty` module defines the following functions: +The :mod:`!pty` module defines the following functions: .. function:: fork() @@ -33,9 +29,14 @@ The :mod:`pty` module defines the following functions: file descriptor connected to the child's controlling terminal (and also to the child's standard input and output). + The returned file descriptor *fd* is :ref:`non-inheritable `. + .. warning:: On macOS the use of this function is unsafe when mixed with using higher-level system APIs, and that includes using :mod:`urllib.request`. + .. versionchanged:: 3.15 + The returned file descriptor is now made non-inheritable. + .. function:: openpty() @@ -88,8 +89,6 @@ The :mod:`pty` module defines the following functions: Example ------- -.. sectionauthor:: Steen Lumholt - The following program acts like the Unix command :manpage:`script(1)`, using a pseudo-terminal to record all input and output of a terminal session in a "typescript". :: diff --git a/Doc/library/pwd.rst b/Doc/library/pwd.rst index e1ff32912132f7..7691fed2c7cb83 100644 --- a/Doc/library/pwd.rst +++ b/Doc/library/pwd.rst @@ -2,7 +2,6 @@ ===================================== .. module:: pwd - :platform: Unix :synopsis: The password database (getpwnam() and friends). -------------- diff --git a/Doc/library/py_compile.rst b/Doc/library/py_compile.rst index 75aa739d1003b8..7aa960de3f2345 100644 --- a/Doc/library/py_compile.rst +++ b/Doc/library/py_compile.rst @@ -4,16 +4,13 @@ .. module:: py_compile :synopsis: Generate byte-code files from Python source files. -.. sectionauthor:: Fred L. Drake, Jr. -.. documentation based on module docstrings - **Source code:** :source:`Lib/py_compile.py` .. index:: pair: file; byte-code -------------- -The :mod:`py_compile` module provides a function to generate a byte-code file +The :mod:`!py_compile` module provides a function to generate a byte-code file from a source file, and another function used when the module source file is invoked as a script. diff --git a/Doc/library/pyclbr.rst b/Doc/library/pyclbr.rst index 5efb11d89dd143..ed9fc6d0b5cf8c 100644 --- a/Doc/library/pyclbr.rst +++ b/Doc/library/pyclbr.rst @@ -4,13 +4,11 @@ .. module:: pyclbr :synopsis: Supports information extraction for a Python module browser. -.. sectionauthor:: Fred L. Drake, Jr. - **Source code:** :source:`Lib/pyclbr.py` -------------- -The :mod:`pyclbr` module provides limited information about the +The :mod:`!pyclbr` module provides limited information about the functions, classes, and methods defined in a Python-coded module. The information is sufficient to implement a module browser. The information is extracted from the Python source code rather than by diff --git a/Doc/library/pydoc.rst b/Doc/library/pydoc.rst index e8f153ee1b35ce..a0cfb440a36ffa 100644 --- a/Doc/library/pydoc.rst +++ b/Doc/library/pydoc.rst @@ -4,9 +4,6 @@ .. module:: pydoc :synopsis: Documentation generator and online help system. -.. moduleauthor:: Ka-Ping Yee -.. sectionauthor:: Ka-Ping Yee - **Source code:** :source:`Lib/pydoc.py` .. index:: @@ -71,6 +68,11 @@ will start a HTTP server on port 1234, allowing you to browse the documentation at ``http://localhost:1234/`` in your preferred web browser. Specifying ``0`` as the port number will select an arbitrary unused port. +.. warning:: + + The :mod:`!pydoc` HTTP server is intended for local use during + development and is not suitable for production use. + :program:`python -m pydoc -n ` will start the server listening at the given hostname. By default the hostname is 'localhost' but if you want the server to be reached from other machines, you may want to change the host name that the diff --git a/Doc/library/pyexpat.rst b/Doc/library/pyexpat.rst index 5506ac828e5abe..c88411ce0b7b91 100644 --- a/Doc/library/pyexpat.rst +++ b/Doc/library/pyexpat.rst @@ -4,8 +4,6 @@ .. module:: xml.parsers.expat :synopsis: An interface to the Expat non-validating XML parser. -.. moduleauthor:: Paul Prescod - -------------- .. Markup notes: @@ -24,7 +22,7 @@ .. index:: single: Expat -The :mod:`xml.parsers.expat` module is a Python interface to the Expat +The :mod:`!xml.parsers.expat` module is a Python interface to the Expat non-validating XML parser. The module provides a single extension type, :class:`xmlparser`, that represents the current state of an XML parser. After an :class:`xmlparser` object has been created, various attributes of the object @@ -55,7 +53,7 @@ This module provides one exception and one type object: The type of the return values from the :func:`ParserCreate` function. -The :mod:`xml.parsers.expat` module contains two functions: +The :mod:`!xml.parsers.expat` module contains two functions: .. function:: ErrorString(errno) @@ -65,12 +63,33 @@ The :mod:`xml.parsers.expat` module contains two functions: .. function:: ParserCreate(encoding=None, namespace_separator=None) - Creates and returns a new :class:`xmlparser` object. *encoding*, if specified, - must be a string naming the encoding used by the XML data. Expat doesn't - support as many encodings as Python does, and its repertoire of encodings can't - be extended; it supports UTF-8, UTF-16, ISO-8859-1 (Latin1), and ASCII. If - *encoding* [1]_ is given it will override the implicit or explicit encoding of the - document. + Creates and returns a new :class:`xmlparser` object. + *encoding* [1]_, if specified, must be a string naming the encoding + used by the XML data. + If it is given it will override the implicit or explicit encoding + of the document. + + .. impl-detail:: + + Expat natively understands and processes UTF-8, UTF-16, UTF-16BE, + UTF-16LE, ISO-8859-1, and US-ASCII. + For other encodings (including aliases like Latin1 and ASCII) it + falls back to Python. + It supports most of 8-bit encodings and many multi-byte encodings + like Shift_JIS, although only BMP characters (``U+0000-U+FFFF``) + are supported with non-native encodings (this restriction is also + applied to aliases like UTF8). + These restrictions only apply if *encoding* is not given. + + .. versionchanged:: next + Added support for multi-byte encodings. + + .. _xmlparser-non-root: + + Parsers created through :func:`!ParserCreate` are called "root" parsers, + in the sense that they do not have any parent parser attached. Non-root + parsers are created by :meth:`parser.ExternalEntityParserCreate + `. Expat can optionally do XML namespace processing for you, enabled by providing a value for *namespace_separator*. The value must be a one-character string; a @@ -108,7 +127,6 @@ The :mod:`xml.parsers.expat` module contains two functions: XML document. Call ``ParserCreate`` for each document to provide unique parser instances. - .. seealso:: `The Expat XML Parser `_ @@ -216,10 +234,10 @@ XMLParser Objects Calling ``SetReparseDeferralEnabled(True)`` allows re-enabling reparse deferral. - Note that :meth:`SetReparseDeferralEnabled` has been backported to some - prior releases of CPython as a security fix. Check for availability of - :meth:`SetReparseDeferralEnabled` using :func:`hasattr` if used in code - running across a variety of Python versions. + :meth:`!SetReparseDeferralEnabled` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. .. versionadded:: 3.13 @@ -231,6 +249,131 @@ XMLParser Objects .. versionadded:: 3.13 +:class:`!xmlparser` objects have the following methods to tune protections +against some common XML vulnerabilities. + +.. method:: xmlparser.SetBillionLaughsAttackProtectionActivationThreshold(threshold, /) + + Sets the number of output bytes needed to activate protection against + `billion laughs`_ attacks. + + The number of output bytes includes amplification from entity expansion + and reading DTD files. + + Parser objects usually have a protection activation threshold of 8 MiB, + but the actual default value depends on the underlying Expat library. + + An :exc:`ExpatError` is raised if this method is called on a + |xml-non-root-parser| parser. + The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset` + should not be used as they may have no special meaning. + + :meth:`!SetBillionLaughsAttackProtectionActivationThreshold` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. + + .. note:: + + Activation thresholds below 4 MiB are known to break support for DITA 1.3 + payload and are hence not recommended. + + .. versionadded:: 3.15 + +.. method:: xmlparser.SetBillionLaughsAttackProtectionMaximumAmplification(max_factor, /) + + Sets the maximum tolerated amplification factor for protection against + `billion laughs`_ attacks. + + The amplification factor is calculated as ``(direct + indirect) / direct`` + while parsing, where ``direct`` is the number of bytes read from + the primary document in parsing and ``indirect`` is the number of + bytes added by expanding entities and reading of external DTD files. + + The *max_factor* value must be a non-NaN :class:`float` value greater than + or equal to 1.0. Peak amplifications of factor 15,000 for the entire payload + and of factor 30,000 in the middle of parsing have been observed with small + benign files in practice. In particular, the activation threshold should be + carefully chosen to avoid false positives. + + Parser objects usually have a maximum amplification factor of 100, + but the actual default value depends on the underlying Expat library. + + An :exc:`ExpatError` is raised if this method is called on a + |xml-non-root-parser| parser or if *max_factor* is outside the valid range. + The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset` + should not be used as they may have no special meaning. + + :meth:`!SetBillionLaughsAttackProtectionMaximumAmplification` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. + + .. note:: + + The maximum amplification factor is only considered if the threshold + that can be adjusted by :meth:`.SetBillionLaughsAttackProtectionActivationThreshold` + is exceeded. + + .. versionadded:: 3.15 + +.. method:: xmlparser.SetAllocTrackerActivationThreshold(threshold, /) + + Sets the number of allocated bytes of dynamic memory needed to activate + protection against disproportionate use of RAM. + + Parser objects usually have an allocation activation threshold of 64 MiB, + but the actual default value depends on the underlying Expat library. + + An :exc:`ExpatError` is raised if this method is called on a + |xml-non-root-parser| parser. + The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset` + should not be used as they may have no special meaning. + + :meth:`!SetAllocTrackerActivationThreshold` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. + + .. versionadded:: 3.15 + +.. method:: xmlparser.SetAllocTrackerMaximumAmplification(max_factor, /) + + Sets the maximum amplification factor between direct input and bytes + of dynamic memory allocated. + + The amplification factor is calculated as ``allocated / direct`` + while parsing, where ``direct`` is the number of bytes read from + the primary document in parsing and ``allocated`` is the number + of bytes of dynamic memory allocated in the parser hierarchy. + + The *max_factor* value must be a non-NaN :class:`float` value greater than + or equal to 1.0. Amplification factors greater than 100.0 can be observed + near the start of parsing even with benign files in practice. In particular, + the activation threshold should be carefully chosen to avoid false positives. + + Parser objects usually have a maximum amplification factor of 100, + but the actual default value depends on the underlying Expat library. + + An :exc:`ExpatError` is raised if this method is called on a + |xml-non-root-parser| parser or if *max_factor* is outside the valid range. + The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset` + should not be used as they may have no special meaning. + + :meth:`!SetAllocTrackerMaximumAmplification` + has been backported to some prior releases of CPython as a security fix. + Check for availability using :func:`hasattr` if used in code running + across a variety of Python versions. + + .. note:: + + The maximum amplification factor is only considered if the threshold + that can be adjusted by :meth:`.SetAllocTrackerActivationThreshold` + is exceeded. + + .. versionadded:: 3.15 + + :class:`xmlparser` objects have the following attributes: @@ -353,7 +496,7 @@ otherwise stated. ...``). The *doctypeName* is provided exactly as presented. The *systemId* and *publicId* parameters give the system and public identifiers if specified, or ``None`` if omitted. *has_internal_subset* will be true if the document - contains and internal document declaration subset. This requires Expat version + contains an internal document declaration subset. This requires Expat version 1.2 or newer. @@ -502,6 +645,15 @@ otherwise stated. .. method:: xmlparser.ExternalEntityRefHandler(context, base, systemId, publicId) + .. warning:: + + Implementing a handler that accesses local files and/or the network + may create a vulnerability to + `external entity attacks `_ + if :class:`xmlparser` is used with user-provided XML content. + Please reflect on your `threat model `_ + before implementing this handler. + Called for references to external entities. *base* is the current base, as set by a previous call to :meth:`SetBase`. The public and system identifiers, *systemId* and *publicId*, are strings if given; if the public identifier is not @@ -525,9 +677,6 @@ otherwise stated. ExpatError Exceptions --------------------- -.. sectionauthor:: Fred L. Drake, Jr. - - :exc:`ExpatError` exceptions have a number of interesting attributes: @@ -611,14 +760,12 @@ Content Model Descriptions .. module:: xml.parsers.expat.model -.. sectionauthor:: Fred L. Drake, Jr. - Content models are described using nested tuples. Each tuple contains four values: the type, the quantifier, the name, and a tuple of children. Children are simply additional content model descriptions. The values of the first two fields are constants defined in the -:mod:`xml.parsers.expat.model` module. These constants can be collected in two +:mod:`!xml.parsers.expat.model` module. These constants can be collected in two groups: the model type group and the quantifier group. The constants in the model type group are: @@ -692,7 +839,7 @@ Expat error constants .. module:: xml.parsers.expat.errors -The following constants are provided in the :mod:`xml.parsers.expat.errors` +The following constants are provided in the :mod:`!xml.parsers.expat.errors` module. These constants are useful in interpreting some of the attributes of the :exc:`ExpatError` exception objects raised when an error has occurred. Since for backwards compatibility reasons, the constants' value is the error @@ -839,7 +986,7 @@ The ``errors`` module has the following attributes: An operation was requested that requires DTD support to be compiled in, but Expat was configured without DTD support. This should never be reported by a - standard build of the :mod:`xml.parsers.expat` module. + standard build of the :mod:`!xml.parsers.expat` module. .. data:: XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING @@ -949,8 +1096,13 @@ The ``errors`` module has the following attributes: .. rubric:: Footnotes -.. [1] The encoding string included in XML output should conform to the - appropriate standards. For example, "UTF-8" is valid, but "UTF8" is - not. See https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDecl +.. [1] The encoding string included in XML output should conform to + the appropriate standards. For example, "UTF-8" is valid, but + "UTF8" is not valid in an XML document's declaration, even though + Python accepts it as an encoding name. + See https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDecl and https://www.iana.org/assignments/character-sets/character-sets.xhtml. + +.. _billion laughs: https://en.wikipedia.org/wiki/Billion_laughs_attack +.. |xml-non-root-parser| replace:: :ref:`non-root ` diff --git a/Doc/library/queue.rst b/Doc/library/queue.rst index 6dcf06aab00295..f5326aff7236bd 100644 --- a/Doc/library/queue.rst +++ b/Doc/library/queue.rst @@ -8,7 +8,7 @@ -------------- -The :mod:`queue` module implements multi-producer, multi-consumer queues. +The :mod:`!queue` module implements multi-producer, multi-consumer queues. It is especially useful in threaded programming when information must be exchanged safely between multiple threads. The :class:`Queue` class in this module implements all the required locking semantics. @@ -30,7 +30,7 @@ In addition, the module implements a "simple" specific implementation provides additional guarantees in exchange for the smaller functionality. -The :mod:`queue` module defines the following classes and exceptions: +The :mod:`!queue` module defines the following classes and exceptions: .. class:: Queue(maxsize=0) @@ -76,6 +76,8 @@ The :mod:`queue` module defines the following classes and exceptions: Constructor for an unbounded :abbr:`FIFO (first-in, first-out)` queue. Simple queues lack advanced functionality such as task tracking. + Simple queues are :ref:`generic ` over the type of their items. + .. versionadded:: 3.7 @@ -256,9 +258,10 @@ until empty or terminated immediately with a hard shutdown. raise :exc:`ShutDown`. If *immediate* is true, the queue is terminated immediately. - The queue is drained to be completely empty. All callers of - :meth:`~Queue.join` are unblocked regardless of the number - of unfinished tasks. Blocked callers of :meth:`~Queue.get` + The queue is drained to be completely empty and the count + of unfinished tasks is reduced by the number of tasks drained. + If unfinished tasks is zero, callers of :meth:`~Queue.join` + are unblocked. Also, blocked callers of :meth:`~Queue.get` are unblocked and will raise :exc:`ShutDown` because the queue is empty. diff --git a/Doc/library/random.rst b/Doc/library/random.rst index b1120b3a4d8eb4..73a37e189ddf2a 100644 --- a/Doc/library/random.rst +++ b/Doc/library/random.rst @@ -37,7 +37,7 @@ Class :class:`Random` can also be subclassed if you want to use a different basic generator of your own devising: see the documentation on that class for more details. -The :mod:`random` module also provides the :class:`SystemRandom` class which +The :mod:`!random` module also provides the :class:`SystemRandom` class which uses the system function :func:`os.urandom` to generate random numbers from sources provided by the operating system. @@ -78,7 +78,7 @@ Bookkeeping functions instead of the system time (see the :func:`os.urandom` function for details on availability). - If *a* is an int, it is used directly. + If *a* is an int, its absolute value is used directly. With version 2 (the default), a :class:`str`, :class:`bytes`, or :class:`bytearray` object gets converted to an :class:`int` and all of its bits are used. @@ -410,7 +410,7 @@ Alternative Generator .. class:: Random([seed]) Class that implements the default pseudo-random number generator used by the - :mod:`random` module. + :mod:`!random` module. .. versionchanged:: 3.11 Formerly the *seed* could be any hashable object. Now it is limited to: @@ -630,14 +630,16 @@ Recipes ------- These recipes show how to efficiently make random selections -from the combinatoric iterators in the :mod:`itertools` module: +from the combinatoric iterators in the :mod:`itertools` module +or the :pypi:`more-itertools` project: .. testcode:: + import random - def random_product(*args, repeat=1): - "Random selection from itertools.product(*args, **kwds)" - pools = [tuple(pool) for pool in args] * repeat + def random_product(*iterables, repeat=1): + "Random selection from itertools.product(*iterables, repeat=repeat)" + pools = tuple(map(tuple, iterables)) * repeat return tuple(map(random.choice, pools)) def random_permutation(iterable, r=None): @@ -661,6 +663,91 @@ from the combinatoric iterators in the :mod:`itertools` module: indices = sorted(random.choices(range(n), k=r)) return tuple(pool[i] for i in indices) + def random_derangement(iterable): + "Choose a permutation where no element stays in its original position." + seq = tuple(iterable) + if len(seq) < 2: + if not seq: + return () + raise IndexError('No derangments to choose from') + perm = list(range(len(seq))) + start = tuple(perm) + while True: + random.shuffle(perm) + if all(p != q for p, q in zip(start, perm)): + return tuple([seq[i] for i in perm]) + +.. doctest:: + :hide: + + >>> import random + + + >>> random.seed(8675309) + >>> random_product('ABCDEFG', repeat=5) + ('D', 'B', 'E', 'F', 'E') + + + >>> random.seed(8675309) + >>> random_permutation('ABCDEFG') + ('D', 'B', 'E', 'C', 'G', 'A', 'F') + >>> random_permutation('ABCDEFG', 5) + ('A', 'G', 'D', 'C', 'B') + + + >>> random.seed(8675309) + >>> random_combination('ABCDEFG', 7) + ('A', 'B', 'C', 'D', 'E', 'F', 'G') + >>> random_combination('ABCDEFG', 6) + ('A', 'B', 'C', 'D', 'F', 'G') + >>> random_combination('ABCDEFG', 5) + ('A', 'B', 'C', 'E', 'F') + >>> random_combination('ABCDEFG', 4) + ('B', 'C', 'D', 'G') + >>> random_combination('ABCDEFG', 3) + ('B', 'E', 'G') + >>> random_combination('ABCDEFG', 2) + ('E', 'G') + >>> random_combination('ABCDEFG', 1) + ('C',) + >>> random_combination('ABCDEFG', 0) + () + + + >>> random.seed(8675309) + >>> random_combination_with_replacement('ABCDEFG', 7) + ('B', 'C', 'D', 'E', 'E', 'E', 'G') + >>> random_combination_with_replacement('ABCDEFG', 3) + ('A', 'B', 'E') + >>> random_combination_with_replacement('ABCDEFG', 2) + ('A', 'G') + >>> random_combination_with_replacement('ABCDEFG', 1) + ('E',) + >>> random_combination_with_replacement('ABCDEFG', 0) + () + + + >>> random.seed(8675309) + >>> random_derangement('') + () + >>> random_derangement('A') + Traceback (most recent call last): + ... + IndexError: No derangments to choose from + >>> random_derangement('AB') + ('B', 'A') + >>> random_derangement('ABC') + ('C', 'A', 'B') + >>> random_derangement('ABCD') + ('B', 'A', 'D', 'C') + >>> random_derangement('ABCDE') + ('B', 'C', 'A', 'E', 'D') + >>> # Identical inputs treated as distinct + >>> identical = 20 + >>> random_derangement((10, identical, 30, identical)) + (20, 30, 10, 20) + + The default :func:`.random` returns multiples of 2⁻⁵³ in the range *0.0 ≤ x < 1.0*. All such numbers are evenly spaced and are exactly representable as Python floats. However, many other representable diff --git a/Doc/library/re.rst b/Doc/library/re.rst index 75ebbf11c8e47c..4745c1b98a4554 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -4,9 +4,6 @@ .. module:: re :synopsis: Regular expression operations. -.. moduleauthor:: Fredrik Lundh -.. sectionauthor:: Andrew M. Kuchling - **Source code:** :source:`Lib/re/` -------------- @@ -49,13 +46,13 @@ fine-tuning parameters. .. seealso:: The third-party :pypi:`regex` module, - which has an API compatible with the standard library :mod:`re` module, + which has an API compatible with the standard library :mod:`!re` module, but offers additional functionality and a more thorough Unicode support. .. _re-syntax: -Regular Expression Syntax +Regular expression syntax ------------------------- A regular expression (or RE) specifies a set of strings that matches it; the @@ -208,7 +205,7 @@ The special characters are: *without* establishing any backtracking points. This is the possessive version of the quantifier above. For example, on the 6-character string ``'aaaaaa'``, ``a{3,5}+aa`` - attempt to match 5 ``'a'`` characters, then, requiring 2 more ``'a'``\ s, + attempts to match 5 ``'a'`` characters, then, requiring 2 more ``'a'``\ s, will need more characters than available and thus fail, while ``a{3,5}aa`` will match with ``a{3,5}`` capturing 5, then 4 ``'a'``\ s by backtracking and then the final 2 ``'a'``\ s are matched by the final @@ -720,7 +717,7 @@ three digits in length. .. _contents-of-module-re: -Module Contents +Module contents --------------- The module defines several functions, constants, and an exception. Some of the @@ -836,8 +833,8 @@ Flags will be conditionally ORed with other flags. Example of use as a default value:: - def myfunc(text, flag=re.NOFLAG): - return re.match(text, flag) + def myfunc(pattern, text, flag=re.NOFLAG): + return re.search(pattern, text, flag) .. versionadded:: 3.11 @@ -893,8 +890,8 @@ Functions Compile a regular expression pattern into a :ref:`regular expression object `, which can be used for matching using its - :func:`~Pattern.match`, :func:`~Pattern.search` and other methods, described - below. + :func:`~Pattern.prefixmatch`, + :func:`~Pattern.search`, and other methods, described below. The expression's behaviour can be modified by specifying a *flags* value. Values can be any of the `flags`_ variables, combined using bitwise OR @@ -903,11 +900,11 @@ Functions The sequence :: prog = re.compile(pattern) - result = prog.match(string) + result = prog.search(string) is equivalent to :: - result = re.match(pattern, string) + result = re.search(pattern, string) but using :func:`re.compile` and saving the resulting regular expression object for reuse is more efficient when the expression will be used several @@ -933,15 +930,17 @@ Functions (the ``|`` operator). -.. function:: match(pattern, string, flags=0) +.. function:: prefixmatch(pattern, string, flags=0) If zero or more characters at the beginning of *string* match the regular expression *pattern*, return a corresponding :class:`~re.Match`. Return ``None`` if the string does not match the pattern; note that this is different from a zero-length match. - Note that even in :const:`MULTILINE` mode, :func:`re.match` will only match - at the beginning of the string and not at the beginning of each line. + .. note:: + + Even in :const:`MULTILINE` mode, this will only match at the + beginning of the string and not at the beginning of each line. If you want to locate a match anywhere in *string*, use :func:`search` instead (see also :ref:`search-vs-match`). @@ -950,6 +949,23 @@ Functions Values can be any of the `flags`_ variables, combined using bitwise OR (the ``|`` operator). + This function now has two names and has long been known as + :func:`~re.match`. Use that name when you need to retain compatibility with + older Python versions. + + .. versionadded:: 3.15 + +.. function:: match(pattern, string, flags=0) + + .. soft-deprecated:: 3.15 + :func:`~re.match` has been :term:`soft deprecated` in favor of + the alternate :func:`~re.prefixmatch` name of this API which is + more explicitly descriptive. Use it to better + express intent. The norm in other languages and regular expression + implementations is to use the term *match* to refer to the behavior of + what Python has always called :func:`~re.search`. + See :ref:`prefixmatch-vs-match`. + .. function:: fullmatch(pattern, string, flags=0) @@ -1234,13 +1250,16 @@ Exceptions .. _re-objects: -Regular Expression Objects +Regular expression objects -------------------------- .. class:: Pattern Compiled regular expression object returned by :func:`re.compile`. + Patterns are :ref:`generic ` over the type of string they handle + (:class:`str` or :class:`bytes`). + .. versionchanged:: 3.9 :py:class:`re.Pattern` supports ``[]`` to indicate a Unicode (str) or bytes pattern. See :ref:`types-genericalias`. @@ -1271,24 +1290,44 @@ Regular Expression Objects >>> pattern.search("dog", 1) # No match; search doesn't include the "d" -.. method:: Pattern.match(string[, pos[, endpos]]) +.. method:: Pattern.prefixmatch(string[, pos[, endpos]]) If zero or more characters at the *beginning* of *string* match this regular expression, return a corresponding :class:`~re.Match`. Return ``None`` if the string does not match the pattern; note that this is different from a zero-length match. + Note that even in :const:`MULTILINE` mode, this will only match at the + beginning of the string and not at the beginning of each line. + The optional *pos* and *endpos* parameters have the same meaning as for the :meth:`~Pattern.search` method. :: >>> pattern = re.compile("o") - >>> pattern.match("dog") # No match as "o" is not at the start of "dog". - >>> pattern.match("dog", 1) # Match as "o" is the 2nd character of "dog". + >>> pattern.prefixmatch("dog") # No match as "o" is not at the start of "dog". + >>> pattern.prefixmatch("dog", 1) # Match as "o" is the 2nd character of "dog". If you want to locate a match anywhere in *string*, use :meth:`~Pattern.search` instead (see also :ref:`search-vs-match`). + This method now has two names and has long been known as + :meth:`~Pattern.match`. Use that name when you need to retain compatibility + with older Python versions. + + .. versionadded:: 3.15 + +.. method:: Pattern.match(string[, pos[, endpos]]) + + .. soft-deprecated:: 3.15 + :meth:`~Pattern.match` has been :term:`soft deprecated` in favor of + the alternate :meth:`~Pattern.prefixmatch` name of this API which is + more explicitly descriptive. Use it to + better express intent. The norm in other languages and regular expression + implementations is to use the term *match* to refer to the behavior of + what Python has always called :meth:`~Pattern.search`. + See :ref:`prefixmatch-vs-match`. + .. method:: Pattern.fullmatch(string[, pos[, endpos]]) @@ -1368,7 +1407,7 @@ Regular Expression Objects .. _match-objects: -Match Objects +Match objects ------------- Match objects always have a boolean value of ``True``. @@ -1376,14 +1415,16 @@ Since :meth:`~Pattern.match` and :meth:`~Pattern.search` return ``None`` when there is no match, you can test whether there was a match with a simple ``if`` statement:: - match = re.search(pattern, string) - if match: + if match := re.search(pattern, string): process(match) .. class:: Match Match object returned by successful ``match``\ es and ``search``\ es. + Matches are :ref:`generic ` over the type of string which was + matched (:class:`str` or :class:`bytes`). + .. versionchanged:: 3.9 :py:class:`re.Match` supports ``[]`` to indicate a Unicode (str) or bytes match. See :ref:`types-genericalias`. @@ -1407,23 +1448,23 @@ when there is no match, you can test whether there was a match with a simple result is a single string; if there are multiple arguments, the result is a tuple with one item per argument. Without arguments, *group1* defaults to zero (the whole match is returned). If a *groupN* argument is zero, the corresponding - return value is the entire matching string; if it is in the inclusive range - [1..99], it is the string matching the corresponding parenthesized group. If a - group number is negative or larger than the number of groups defined in the - pattern, an :exc:`IndexError` exception is raised. If a group is contained in a + return value is the entire matching string; if it is a positive integer, it is + the string matching the corresponding parenthesized group. If a group number is + negative or larger than the number of groups defined in the pattern, an + :exc:`IndexError` exception is raised. If a group is contained in a part of the pattern that did not match, the corresponding result is ``None``. If a group is contained in a part of the pattern that matched multiple times, the last match is returned. :: - >>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") + >>> m = re.search(r"\A(\w+) (\w+)", "Norwegian Blue, pining for the fjords") >>> m.group(0) # The entire match - 'Isaac Newton' + 'Norwegian Blue' >>> m.group(1) # The first parenthesized subgroup. - 'Isaac' + 'Norwegian' >>> m.group(2) # The second parenthesized subgroup. - 'Newton' + 'Blue' >>> m.group(1, 2) # Multiple arguments give us a tuple. - ('Isaac', 'Newton') + ('Norwegian', 'Blue') If the regular expression uses the ``(?P...)`` syntax, the *groupN* arguments may also be strings identifying groups by their group name. If a @@ -1432,23 +1473,23 @@ when there is no match, you can test whether there was a match with a simple A moderately complicated example:: - >>> m = re.match(r"(?P\w+) (?P\w+)", "Malcolm Reynolds") - >>> m.group('first_name') - 'Malcolm' - >>> m.group('last_name') - 'Reynolds' + >>> m = re.search(r"(?P\w+) (?P\w+)", "killer rabbit") + >>> m.group('adjective') + 'killer' + >>> m.group('animal') + 'rabbit' Named groups can also be referred to by their index:: >>> m.group(1) - 'Malcolm' + 'killer' >>> m.group(2) - 'Reynolds' + 'rabbit' If a group matches multiple times, only the last match is accessible:: - >>> m = re.match(r"(..)+", "a1b2c3") # Matches 3 times. - >>> m.group(1) # Returns only the last match. + >>> m = re.search(r"(..)+", "a1b2c3") # Matches 3 times. + >>> m.group(1) # Returns only the last match. 'c3' @@ -1457,21 +1498,21 @@ when there is no match, you can test whether there was a match with a simple This is identical to ``m.group(g)``. This allows easier access to an individual group from a match:: - >>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") + >>> m = re.search(r"(\w+) (\w+)", "Norwegian Blue, pining for the fjords") >>> m[0] # The entire match - 'Isaac Newton' + 'Norwegian Blue' >>> m[1] # The first parenthesized subgroup. - 'Isaac' + 'Norwegian' >>> m[2] # The second parenthesized subgroup. - 'Newton' + 'Blue' Named groups are supported as well:: - >>> m = re.match(r"(?P\w+) (?P\w+)", "Isaac Newton") - >>> m['first_name'] - 'Isaac' - >>> m['last_name'] - 'Newton' + >>> m = re.search(r"(?P\w+) (?P\w+)", "killer rabbit") + >>> m['adjective'] + 'killer' + >>> m['animal'] + 'rabbit' .. versionadded:: 3.6 @@ -1484,7 +1525,7 @@ when there is no match, you can test whether there was a match with a simple For example:: - >>> m = re.match(r"(\d+)\.(\d+)", "24.1632") + >>> m = re.search(r"(\d+)\.(\d+)", "24.1632") >>> m.groups() ('24', '1632') @@ -1492,7 +1533,7 @@ when there is no match, you can test whether there was a match with a simple might participate in the match. These groups will default to ``None`` unless the *default* argument is given:: - >>> m = re.match(r"(\d+)\.?(\d+)?", "24") + >>> m = re.search(r"(\d+)\.?(\d+)?", "24") >>> m.groups() # Second group defaults to None. ('24', None) >>> m.groups('0') # Now, the second group defaults to '0'. @@ -1505,9 +1546,9 @@ when there is no match, you can test whether there was a match with a simple the subgroup name. The *default* argument is used for groups that did not participate in the match; it defaults to ``None``. For example:: - >>> m = re.match(r"(?P\w+) (?P\w+)", "Malcolm Reynolds") + >>> m = re.search(r"(?P\w+) (?P\w+)", "killer rabbit") >>> m.groupdict() - {'first_name': 'Malcolm', 'last_name': 'Reynolds'} + {'adjective': 'killer', 'animal': 'rabbit'} .. method:: Match.start([group]) @@ -1588,11 +1629,11 @@ when there is no match, you can test whether there was a match with a simple .. _re-examples: -Regular Expression Examples +Regular expression examples --------------------------- -Checking for a Pair +Checking for a pair ^^^^^^^^^^^^^^^^^^^ In this example, we'll use the following helper function to display match @@ -1610,42 +1651,41 @@ representing the card with that value. To see if a given string is a valid hand, one could do the following:: - >>> valid = re.compile(r"^[a2-9tjqk]{5}$") - >>> displaymatch(valid.match("akt5q")) # Valid. + >>> valid_hand_re = re.compile(r"^[a2-9tjqk]{5}$") + >>> displaymatch(valid_hand_re.search("akt5q")) # Valid. "" - >>> displaymatch(valid.match("akt5e")) # Invalid. - >>> displaymatch(valid.match("akt")) # Invalid. - >>> displaymatch(valid.match("727ak")) # Valid. + >>> displaymatch(valid_hand_re.search("akt5e")) # Invalid. + >>> displaymatch(valid_hand_re.search("akt")) # Invalid. + >>> displaymatch(valid_hand_re.search("727ak")) # Valid. "" That last hand, ``"727ak"``, contained a pair, or two of the same valued cards. To match this with a regular expression, one could use backreferences as such:: - >>> pair = re.compile(r".*(.).*\1") - >>> displaymatch(pair.match("717ak")) # Pair of 7s. + >>> pair_re = re.compile(r".*(.).*\1") + >>> displaymatch(pair_re.prefixmatch("717ak")) # Pair of 7s. "" - >>> displaymatch(pair.match("718ak")) # No pairs. - >>> displaymatch(pair.match("354aa")) # Pair of aces. + >>> displaymatch(pair_re.prefixmatch("718ak")) # No pairs. + >>> displaymatch(pair_re.prefixmatch("354aa")) # Pair of aces. "" To find out what card the pair consists of, one could use the :meth:`~Match.group` method of the match object in the following manner:: - >>> pair = re.compile(r".*(.).*\1") - >>> pair.match("717ak").group(1) + >>> pair_re = re.compile(r".*(.).*\1") + >>> pair_re.prefixmatch("717ak").group(1) '7' - # Error because re.match() returns None, which doesn't have a group() method: - >>> pair.match("718ak").group(1) + # Error because prefixmatch() returns None, which doesn't have a group() method: + >>> pair_re.prefixmatch("718ak").group(1) Traceback (most recent call last): File "", line 1, in - re.match(r".*(.).*\1", "718ak").group(1) + pair_re.prefixmatch("718ak").group(1) AttributeError: 'NoneType' object has no attribute 'group' - >>> pair.match("354aa").group(1) + >>> pair_re.prefixmatch("354aa").group(1) 'a' - Simulating scanf() ^^^^^^^^^^^^^^^^^^ @@ -1679,38 +1719,41 @@ expressions. | ``%x``, ``%X`` | ``[-+]?(0[xX])?[\dA-Fa-f]+`` | +--------------------------------+---------------------------------------------+ -To extract the filename and numbers from a string like :: +To extract the filename and numbers from a string like: + +.. code-block:: text /usr/sbin/sendmail - 0 errors, 4 warnings -you would use a :c:func:`!scanf` format like :: +you would use a :c:func:`!scanf` format like: + +.. code-block:: text %s - %d errors, %d warnings -The equivalent regular expression would be :: +The equivalent regular expression would be: + +.. code-block:: text (\S+) - (\d+) errors, (\d+) warnings .. _search-vs-match: -search() vs. match() -^^^^^^^^^^^^^^^^^^^^ - -.. sectionauthor:: Fred L. Drake, Jr. +search() vs. prefixmatch() +^^^^^^^^^^^^^^^^^^^^^^^^^^ Python offers different primitive operations based on regular expressions: -+ :func:`re.match` checks for a match only at the beginning of the string ++ :func:`re.prefixmatch` checks for a match only at the beginning of the string + :func:`re.search` checks for a match anywhere in the string (this is what Perl does by default) + :func:`re.fullmatch` checks for entire string to be a match - For example:: - >>> re.match("c", "abcdef") # No match - >>> re.search("c", "abcdef") # Match + >>> re.prefixmatch("c", "abcdef") # No match + >>> re.search("c", "abcdef") # Match >>> re.fullmatch("p.*n", "python") # Match @@ -1719,21 +1762,54 @@ For example:: Regular expressions beginning with ``'^'`` can be used with :func:`search` to restrict the match at the beginning of the string:: - >>> re.match("c", "abcdef") # No match - >>> re.search("^c", "abcdef") # No match - >>> re.search("^a", "abcdef") # Match + >>> re.prefixmatch("c", "abcdef") # No match + >>> re.search("^c", "abcdef") # No match + >>> re.search("^a", "abcdef") # Match -Note however that in :const:`MULTILINE` mode :func:`match` only matches at the +Note however that in :const:`MULTILINE` mode :func:`prefixmatch` only matches at the beginning of the string, whereas using :func:`search` with a regular expression beginning with ``'^'`` will match at the beginning of each line. :: - >>> re.match("X", "A\nB\nX", re.MULTILINE) # No match + >>> re.prefixmatch("X", "A\nB\nX", re.MULTILINE) # No match >>> re.search("^X", "A\nB\nX", re.MULTILINE) # Match +.. _prefixmatch-vs-match: + +prefixmatch() vs. match() +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Why is the :func:`~re.match` function and method discouraged in +favor of the longer :func:`~re.prefixmatch` spelling? + +Many other languages have gained regex support libraries since regular +expressions were added to Python. However in the most popular of those, they +use the term *match* in their APIs to mean the unanchored behavior provided in +Python by :func:`~re.search`. Thus use of the plain term *match* can be +unclear to those used to other languages when reading or writing code and +not familiar with the Python API's divergence from what otherwise become the +industry norm. + +Quoting from the Zen Of Python (``python3 -m this``): *"Explicit is better than +implicit"*. Anyone reading the name :func:`!prefixmatch` is likely to +understand the intended semantics. When reading :func:`!match` there remains +a seed of doubt about the intended behavior to anyone not already familiar with +this old Python gotcha. + +We **do not** plan to remove the older :func:`!match` name, +as it has been used in code for over 30 years. +It has been :term:`soft deprecated`: +code supporting older versions of Python should continue to use :func:`!match`, +while new code should prefer :func:`!prefixmatch`. + +.. versionadded:: 3.15 + :func:`!prefixmatch` + +.. soft-deprecated:: 3.15 + :func:`!match` -Making a Phonebook +Making a phonebook ^^^^^^^^^^^^^^^^^^ :func:`split` splits a string into a list delimited by the passed pattern. The @@ -1794,7 +1870,7 @@ house number from the street name: ['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']] -Text Munging +Text munging ^^^^^^^^^^^^ :func:`sub` replaces every occurrence of a pattern with a string or the @@ -1814,7 +1890,7 @@ in each word of a sentence except for the first and last characters:: 'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.' -Finding all Adverbs +Finding all adverbs ^^^^^^^^^^^^^^^^^^^ :func:`findall` matches *all* occurrences of a pattern, not just the first @@ -1827,7 +1903,7 @@ the following manner:: ['carefully', 'quickly'] -Finding all Adverbs and their Positions +Finding all adverbs and their positions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If one wants more information about all matches of a pattern than the matched @@ -1843,7 +1919,7 @@ to find all of the adverbs *and their positions* in some text, they would use 40-47: quickly -Raw String Notation +Raw string notation ^^^^^^^^^^^^^^^^^^^ Raw string notation (``r"text"``) keeps regular expressions sane. Without it, @@ -1851,9 +1927,9 @@ every backslash (``'\'``) in a regular expression would have to be prefixed with another one to escape it. For example, the two following lines of code are functionally identical:: - >>> re.match(r"\W(.)\1\W", " ff ") + >>> re.search(r"\W(.)\1\W", " ff ") - >>> re.match("\\W(.)\\1\\W", " ff ") + >>> re.search("\\W(.)\\1\\W", " ff ") When one wants to match a literal backslash, it must be escaped in the regular @@ -1861,13 +1937,13 @@ expression. With raw string notation, this means ``r"\\"``. Without raw string notation, one must use ``"\\\\"``, making the following lines of code functionally identical:: - >>> re.match(r"\\", r"\\") + >>> re.search(r"\\", r"\\") - >>> re.match("\\\\", r"\\") + >>> re.search("\\\\", r"\\") -Writing a Tokenizer +Writing a tokenizer ^^^^^^^^^^^^^^^^^^^ A `tokenizer or scanner `_ @@ -1883,7 +1959,7 @@ successive matches:: class Token(NamedTuple): type: str - value: str + value: int | float | str line: int column: int diff --git a/Doc/library/readline.rst b/Doc/library/readline.rst index f649fce5efc377..234af8d191e3e3 100644 --- a/Doc/library/readline.rst +++ b/Doc/library/readline.rst @@ -2,14 +2,11 @@ =========================================== .. module:: readline - :platform: Unix :synopsis: GNU readline support for Python. -.. sectionauthor:: Skip Montanaro - -------------- -The :mod:`readline` module defines a number of functions to facilitate +The :mod:`!readline` module defines a number of functions to facilitate completion and reading/writing of history files from the Python interpreter. This module can be used directly, or via the :mod:`rlcompleter` module, which supports completion of Python identifiers at the interactive prompt. Settings @@ -26,11 +23,15 @@ Readline library in general. .. include:: ../includes/wasm-mobile-notavail.rst +.. include:: ../includes/optional-module.rst + +.. availability:: Unix. + .. note:: The underlying Readline library API may be implemented by the ``editline`` (``libedit``) library instead of GNU readline. - On macOS the :mod:`readline` module detects which library is being used + On macOS the :mod:`!readline` module detects which library is being used at run time. The configuration file for ``editline`` is different from that @@ -244,6 +245,15 @@ Startup hooks if Python was compiled for a version of the library that supports it. +.. function:: get_pre_input_hook() + + Get the current pre-input hook function, or ``None`` if no pre-input hook + function has been set. This function only exists if Python was compiled + for a version of the library that supports it. + + .. versionadded:: 3.15 + + .. _readline-completion: Completion @@ -253,7 +263,7 @@ The following functions relate to implementing a custom word completion function. This is typically operated by the Tab key, and can suggest and automatically complete a word being typed. By default, Readline is set up to be used by :mod:`rlcompleter` to complete Python identifiers for -the interactive interpreter. If the :mod:`readline` module is to be used +the interactive interpreter. If the :mod:`!readline` module is to be used with a custom completer, a different set of word delimiters should be set. @@ -322,7 +332,7 @@ with a custom completer, a different set of word delimiters should be set. Example ------- -The following example demonstrates how to use the :mod:`readline` module's +The following example demonstrates how to use the :mod:`!readline` module's history reading and writing functions to automatically load and save a history file named :file:`.python_history` from the user's home directory. The code below would normally be executed automatically during interactive sessions @@ -392,3 +402,9 @@ support history save/restore. :: def save_history(self, histfile): readline.set_history_length(1000) readline.write_history_file(histfile) + +.. note:: + + The new :term:`REPL` introduced in version 3.13 doesn't support readline. + However, readline can still be used by setting the :envvar:`PYTHON_BASIC_REPL` + environment variable. diff --git a/Doc/library/reprlib.rst b/Doc/library/reprlib.rst index 28c7855dfeeef3..d269d8bbaa55da 100644 --- a/Doc/library/reprlib.rst +++ b/Doc/library/reprlib.rst @@ -4,8 +4,6 @@ .. module:: reprlib :synopsis: Alternate repr() implementation with size limits. -.. sectionauthor:: Fred L. Drake, Jr. - **Source code:** :source:`Lib/reprlib.py` -------------- diff --git a/Doc/library/resource.rst b/Doc/library/resource.rst index 0515d205bbca0b..561b2976ecea22 100644 --- a/Doc/library/resource.rst +++ b/Doc/library/resource.rst @@ -2,12 +2,8 @@ =============================================== .. module:: resource - :platform: Unix :synopsis: An interface to provide resource usage information on the current process. -.. moduleauthor:: Jeremy Hylton -.. sectionauthor:: Jeremy Hylton - -------------- This module provides basic mechanisms for measuring and controlling system @@ -51,6 +47,20 @@ this module for those platforms. Constant used to represent the limit for an unlimited resource. + .. versionchanged:: 3.15 + It is now always positive. + Previously, it could be negative, such as -1 or -3. + + +.. data:: RLIM_SAVED_CUR +.. data:: RLIM_SAVED_MAX + + Constants used to represent the soft and hard limit values if they + cannot be represented in the ``rlim_t`` value in C. + Can be equal to :data:`RLIM_INFINITY`. + + .. versionadded:: 3.15 + .. function:: getrlimit(resource) @@ -63,12 +73,12 @@ this module for those platforms. Sets new limits of consumption of *resource*. The *limits* argument must be a tuple ``(soft, hard)`` of two integers describing the new limits. A value of - :data:`~resource.RLIM_INFINITY` can be used to request a limit that is + :const:`~resource.RLIM_INFINITY` can be used to request a limit that is unlimited. Raises :exc:`ValueError` if an invalid resource is specified, if the new soft limit exceeds the hard limit, or if a process tries to raise its hard limit. - Specifying a limit of :data:`~resource.RLIM_INFINITY` when the hard or + Specifying a limit of :const:`~resource.RLIM_INFINITY` when the hard or system limit for that resource is not unlimited will result in a :exc:`ValueError`. A process with the effective UID of super-user can request any valid limit value, including unlimited, but :exc:`ValueError` @@ -78,7 +88,7 @@ this module for those platforms. ``setrlimit`` may also raise :exc:`error` if the underlying system call fails. - VxWorks only supports setting :data:`RLIMIT_NOFILE`. + VxWorks only supports setting :const:`RLIMIT_NOFILE`. .. audit-event:: resource.setrlimit resource,limits resource.setrlimit @@ -127,7 +137,7 @@ platform. .. data:: RLIMIT_CPU The maximum amount of processor time (in seconds) that a process can use. If - this limit is exceeded, a :const:`SIGXCPU` signal is sent to the process. (See + this limit is exceeded, a :const:`~signal.SIGXCPU` signal is sent to the process. (See the :mod:`signal` module documentation for information about how to catch this signal and do something useful, e.g. flush open files to disk.) @@ -176,8 +186,9 @@ platform. .. data:: RLIMIT_VMEM The largest area of mapped memory which the process may occupy. + Usually an alias of :const:`RLIMIT_AS`. - .. availability:: FreeBSD >= 11. + .. availability:: Solaris, FreeBSD, NetBSD. .. data:: RLIMIT_AS @@ -230,16 +241,18 @@ platform. .. versionadded:: 3.4 + .. data:: RLIMIT_SBSIZE The maximum size (in bytes) of socket buffer usage for this user. This limits the amount of network memory, and hence the amount of mbufs, that this user may hold at any time. - .. availability:: FreeBSD. + .. availability:: FreeBSD, NetBSD. .. versionadded:: 3.4 + .. data:: RLIMIT_SWAP The maximum size (in bytes) of the swap space that may be reserved or @@ -249,18 +262,20 @@ platform. `tuning(7) `__ for a complete description of this sysctl. - .. availability:: FreeBSD. + .. availability:: FreeBSD >= 8. .. versionadded:: 3.4 + .. data:: RLIMIT_NPTS The maximum number of pseudo-terminals created by this user id. - .. availability:: FreeBSD. + .. availability:: FreeBSD >= 8. .. versionadded:: 3.4 + .. data:: RLIMIT_KQUEUES The maximum number of kqueues this user id is allowed to create. @@ -269,6 +284,46 @@ platform. .. versionadded:: 3.10 + +.. data:: RLIMIT_NTHR + + The maximum number of threads for this user id, not counting the main + and kernel threads. + + .. availability:: NetBSD >= 7.0. + + .. versionadded:: 3.15 + + +.. data:: RLIMIT_PIPEBUF + + The maximum total size of in-kernel buffers for bi-directional pipes/fifos + that this user id is allowed to consume. + + .. availability:: FreeBSD >= 14.2. + + .. versionadded:: 3.15 + + +.. data:: RLIMIT_THREADS + + The maximum number of threads each process can create. + + .. availability:: AIX. + + .. versionadded:: 3.15 + + +.. data:: RLIMIT_UMTXP + + The limit of the number of process-shared Posix thread library objects + allocated by user id. + + .. availability:: FreeBSD >= 11. + + .. versionadded:: 3.15 + + Resource Usage -------------- @@ -297,54 +352,54 @@ These functions are used to retrieve resource usage information: print(getrusage(RUSAGE_SELF)) The fields of the return value each describe how a particular system resource - has been used, e.g. amount of time spent running is user mode or number of times + has been used, e.g. amount of time spent running in user mode or number of times the process was swapped out of main memory. Some values are dependent on the - clock tick internal, e.g. the amount of memory the process is using. + clock tick interval, e.g. the amount of memory the process is using. For backward compatibility, the return value is also accessible as a tuple of 16 elements. - The fields :attr:`ru_utime` and :attr:`ru_stime` of the return value are + The fields :attr:`!ru_utime` and :attr:`!ru_stime` of the return value are floating-point values representing the amount of time spent executing in user mode and the amount of time spent executing in system mode, respectively. The remaining values are integers. Consult the :manpage:`getrusage(2)` man page for detailed information about these values. A brief summary is presented here: - +--------+---------------------+---------------------------------------+ - | Index | Field | Resource | - +========+=====================+=======================================+ - | ``0`` | :attr:`ru_utime` | time in user mode (float seconds) | - +--------+---------------------+---------------------------------------+ - | ``1`` | :attr:`ru_stime` | time in system mode (float seconds) | - +--------+---------------------+---------------------------------------+ - | ``2`` | :attr:`ru_maxrss` | maximum resident set size | - +--------+---------------------+---------------------------------------+ - | ``3`` | :attr:`ru_ixrss` | shared memory size | - +--------+---------------------+---------------------------------------+ - | ``4`` | :attr:`ru_idrss` | unshared memory size | - +--------+---------------------+---------------------------------------+ - | ``5`` | :attr:`ru_isrss` | unshared stack size | - +--------+---------------------+---------------------------------------+ - | ``6`` | :attr:`ru_minflt` | page faults not requiring I/O | - +--------+---------------------+---------------------------------------+ - | ``7`` | :attr:`ru_majflt` | page faults requiring I/O | - +--------+---------------------+---------------------------------------+ - | ``8`` | :attr:`ru_nswap` | number of swap outs | - +--------+---------------------+---------------------------------------+ - | ``9`` | :attr:`ru_inblock` | block input operations | - +--------+---------------------+---------------------------------------+ - | ``10`` | :attr:`ru_oublock` | block output operations | - +--------+---------------------+---------------------------------------+ - | ``11`` | :attr:`ru_msgsnd` | messages sent | - +--------+---------------------+---------------------------------------+ - | ``12`` | :attr:`ru_msgrcv` | messages received | - +--------+---------------------+---------------------------------------+ - | ``13`` | :attr:`ru_nsignals` | signals received | - +--------+---------------------+---------------------------------------+ - | ``14`` | :attr:`ru_nvcsw` | voluntary context switches | - +--------+---------------------+---------------------------------------+ - | ``15`` | :attr:`ru_nivcsw` | involuntary context switches | - +--------+---------------------+---------------------------------------+ + +--------+----------------------+---------------------------------------+ + | Index | Field | Resource | + +========+======================+=======================================+ + | ``0`` | :attr:`!ru_utime` | time in user mode (float seconds) | + +--------+----------------------+---------------------------------------+ + | ``1`` | :attr:`!ru_stime` | time in system mode (float seconds) | + +--------+----------------------+---------------------------------------+ + | ``2`` | :attr:`!ru_maxrss` | maximum resident set size | + +--------+----------------------+---------------------------------------+ + | ``3`` | :attr:`!ru_ixrss` | shared memory size | + +--------+----------------------+---------------------------------------+ + | ``4`` | :attr:`!ru_idrss` | unshared memory size | + +--------+----------------------+---------------------------------------+ + | ``5`` | :attr:`!ru_isrss` | unshared stack size | + +--------+----------------------+---------------------------------------+ + | ``6`` | :attr:`!ru_minflt` | page faults not requiring I/O | + +--------+----------------------+---------------------------------------+ + | ``7`` | :attr:`!ru_majflt` | page faults requiring I/O | + +--------+----------------------+---------------------------------------+ + | ``8`` | :attr:`!ru_nswap` | number of swap outs | + +--------+----------------------+---------------------------------------+ + | ``9`` | :attr:`!ru_inblock` | block input operations | + +--------+----------------------+---------------------------------------+ + | ``10`` | :attr:`!ru_oublock` | block output operations | + +--------+----------------------+---------------------------------------+ + | ``11`` | :attr:`!ru_msgsnd` | messages sent | + +--------+----------------------+---------------------------------------+ + | ``12`` | :attr:`!ru_msgrcv` | messages received | + +--------+----------------------+---------------------------------------+ + | ``13`` | :attr:`!ru_nsignals` | signals received | + +--------+----------------------+---------------------------------------+ + | ``14`` | :attr:`!ru_nvcsw` | voluntary context switches | + +--------+----------------------+---------------------------------------+ + | ``15`` | :attr:`!ru_nivcsw` | involuntary context switches | + +--------+----------------------+---------------------------------------+ This function will raise a :exc:`ValueError` if an invalid *who* parameter is specified. It may also raise :exc:`error` exception in unusual circumstances. diff --git a/Doc/library/rlcompleter.rst b/Doc/library/rlcompleter.rst index 91779feb525013..2acd1df3c49007 100644 --- a/Doc/library/rlcompleter.rst +++ b/Doc/library/rlcompleter.rst @@ -4,8 +4,6 @@ .. module:: rlcompleter :synopsis: Python identifier completion, suitable for the GNU readline library. -.. sectionauthor:: Moshe Zadka - **Source code:** :source:`Lib/rlcompleter.py` -------------- diff --git a/Doc/library/runpy.rst b/Doc/library/runpy.rst index b07ec6e93f80ab..536b5980f86a81 100644 --- a/Doc/library/runpy.rst +++ b/Doc/library/runpy.rst @@ -4,13 +4,11 @@ .. module:: runpy :synopsis: Locate and run Python modules without importing them first. -.. moduleauthor:: Nick Coghlan - **Source code:** :source:`Lib/runpy.py` -------------- -The :mod:`runpy` module is used to locate and run Python modules without +The :mod:`!runpy` module is used to locate and run Python modules without importing them first. Its main use is to implement the :option:`-m` command line switch that allows scripts to be located using the Python module namespace rather than the filesystem. @@ -20,11 +18,11 @@ current process, and any side effects (such as cached imports of other modules) will remain in place after the functions have returned. Furthermore, any functions and classes defined by the executed code are not -guaranteed to work correctly after a :mod:`runpy` function has returned. +guaranteed to work correctly after a :mod:`!runpy` function has returned. If that limitation is not acceptable for a given use case, :mod:`importlib` is likely to be a more suitable choice than this module. -The :mod:`runpy` module provides two functions: +The :mod:`!runpy` module provides two functions: .. function:: run_module(mod_name, init_globals=None, run_name=None, alter_sys=False) @@ -50,10 +48,10 @@ The :mod:`runpy` module provides two functions: overridden by :func:`run_module`. The special global variables ``__name__``, ``__spec__``, ``__file__``, - ``__cached__``, ``__loader__`` and ``__package__`` are set in the globals - dictionary before the module code is executed. (Note that this is a - minimal set of variables - other variables may be set implicitly as an - interpreter implementation detail.) + ``__loader__`` and ``__package__`` are set in the globals dictionary before + the module code is executed. (Note that this is a minimal set of variables - + other variables may be set implicitly as an interpreter implementation + detail.) ``__name__`` is set to *run_name* if this optional argument is not :const:`None`, to ``mod_name + '.__main__'`` if the named module is a @@ -63,7 +61,7 @@ The :mod:`runpy` module provides two functions: module (that is, ``__spec__.name`` will always be *mod_name* or ``mod_name + '.__main__'``, never *run_name*). - ``__file__``, ``__cached__``, ``__loader__`` and ``__package__`` are + ``__file__``, ``__loader__`` and ``__package__`` are :ref:`set as normal ` based on the module spec. If the argument *alter_sys* is supplied and evaluates to :const:`True`, @@ -98,6 +96,9 @@ The :mod:`runpy` module provides two functions: ``__package__`` are deprecated. See :class:`~importlib.machinery.ModuleSpec` for alternatives. + .. versionchanged:: 3.15 + ``__cached__`` is no longer set. + .. function:: run_path(path_name, init_globals=None, run_name=None) .. index:: @@ -125,23 +126,23 @@ The :mod:`runpy` module provides two functions: overridden by :func:`run_path`. The special global variables ``__name__``, ``__spec__``, ``__file__``, - ``__cached__``, ``__loader__`` and ``__package__`` are set in the globals - dictionary before the module code is executed. (Note that this is a - minimal set of variables - other variables may be set implicitly as an - interpreter implementation detail.) + ``__loader__`` and ``__package__`` are set in the globals dictionary before + the module code is executed. (Note that this is a minimal set of variables - + other variables may be set implicitly as an interpreter implementation + detail.) ``__name__`` is set to *run_name* if this optional argument is not :const:`None` and to ``''`` otherwise. If *file_path* directly references a script file (whether as source or as precompiled byte code), then ``__file__`` will be set to - *file_path*, and ``__spec__``, ``__cached__``, ``__loader__`` and + *file_path*, and ``__spec__``, ``__loader__`` and ``__package__`` will all be set to :const:`None`. If *file_path* is a reference to a valid :data:`sys.path` entry, then ``__spec__`` will be set appropriately for the imported :mod:`__main__` module (that is, ``__spec__.name`` will always be ``__main__``). - ``__file__``, ``__cached__``, ``__loader__`` and ``__package__`` will be + ``__file__``, ``__loader__`` and ``__package__`` will be :ref:`set as normal ` based on the module spec. A number of alterations are also made to the :mod:`sys` module. Firstly, @@ -173,6 +174,9 @@ The :mod:`runpy` module provides two functions: The setting of ``__cached__``, ``__loader__``, and ``__package__`` are deprecated. + .. versionchanged:: 3.15 + ``__cached__`` is no longer set. + .. seealso:: :pep:`338` -- Executing modules as scripts diff --git a/Doc/library/sched.rst b/Doc/library/sched.rst index 517dbe8c321898..037e27f031d0c8 100644 --- a/Doc/library/sched.rst +++ b/Doc/library/sched.rst @@ -4,15 +4,13 @@ .. module:: sched :synopsis: General purpose event scheduler. -.. sectionauthor:: Moshe Zadka - **Source code:** :source:`Lib/sched.py` .. index:: single: event scheduling -------------- -The :mod:`sched` module defines a class which implements a general purpose event +The :mod:`!sched` module defines a class which implements a general purpose event scheduler: .. class:: scheduler(timefunc=time.monotonic, delayfunc=time.sleep) @@ -119,9 +117,11 @@ Scheduler Objects function passed to the constructor) for the next event, then execute it and so on until there are no more scheduled events. - If *blocking* is false executes the scheduled events due to expire soonest - (if any) and then return the deadline of the next scheduled call in the - scheduler (if any). + If *blocking* is false, immediately executes all events in the queue which have + a time value less than or equal to the current *timefunc* value (if any) and + returns the difference between the current *timefunc* value and the time value + of the next scheduled event in the scheduler's event queue. If the queue is + empty, returns ``None``. Either *action* or *delayfunc* can raise an exception. In either case, the scheduler will maintain a consistent state and propagate the exception. If an diff --git a/Doc/library/secrets.rst b/Doc/library/secrets.rst index 75dafc54d40ca5..3b5b57fb1c2170 100644 --- a/Doc/library/secrets.rst +++ b/Doc/library/secrets.rst @@ -4,8 +4,6 @@ .. module:: secrets :synopsis: Generate secure random numbers for managing secrets. -.. moduleauthor:: Steven D'Aprano -.. sectionauthor:: Steven D'Aprano .. versionadded:: 3.6 .. testsetup:: @@ -17,11 +15,11 @@ ------------- -The :mod:`secrets` module is used for generating cryptographically strong +The :mod:`!secrets` module is used for generating cryptographically strong random numbers suitable for managing data such as passwords, account authentication, security tokens, and related secrets. -In particular, :mod:`secrets` should be used in preference to the +In particular, :mod:`!secrets` should be used in preference to the default pseudo-random number generator in the :mod:`random` module, which is designed for modelling and simulation, not security or cryptography. @@ -33,7 +31,7 @@ is designed for modelling and simulation, not security or cryptography. Random numbers -------------- -The :mod:`secrets` module provides access to the most secure source of +The :mod:`!secrets` module provides access to the most secure source of randomness that your operating system provides. .. class:: SystemRandom @@ -58,43 +56,48 @@ randomness that your operating system provides. Generating tokens ----------------- -The :mod:`secrets` module provides functions for generating secure +The :mod:`!secrets` module provides functions for generating secure tokens, suitable for applications such as password resets, hard-to-guess URLs, and similar. -.. function:: token_bytes([nbytes=None]) +.. function:: token_bytes(nbytes=None) Return a random byte string containing *nbytes* number of bytes. - If *nbytes* is ``None`` or not supplied, a reasonable default is - used. + + If *nbytes* is not specified or ``None``, :const:`DEFAULT_ENTROPY` + is used instead. .. doctest:: - >>> token_bytes(16) #doctest:+SKIP + >>> token_bytes(16) # doctest: +SKIP b'\xebr\x17D*t\xae\xd4\xe3S\xb6\xe2\xebP1\x8b' -.. function:: token_hex([nbytes=None]) +.. function:: token_hex(nbytes=None) Return a random text string, in hexadecimal. The string has *nbytes* - random bytes, each byte converted to two hex digits. If *nbytes* is - ``None`` or not supplied, a reasonable default is used. + random bytes, each byte converted to two hex digits. + + If *nbytes* is not specified or ``None``, :const:`DEFAULT_ENTROPY` + is used instead. .. doctest:: - >>> token_hex(16) #doctest:+SKIP + >>> token_hex(16) # doctest: +SKIP 'f9bf78b9a18ce6d46a0cd2b0b86df9da' -.. function:: token_urlsafe([nbytes=None]) +.. function:: token_urlsafe(nbytes=None) Return a random URL-safe text string, containing *nbytes* random bytes. The text is Base64 encoded, so on average each byte results - in approximately 1.3 characters. If *nbytes* is ``None`` or not - supplied, a reasonable default is used. + in approximately 1.3 characters. + + If *nbytes* is not specified or ``None``, :const:`DEFAULT_ENTROPY` + is used instead. .. doctest:: - >>> token_urlsafe(16) #doctest:+SKIP + >>> token_urlsafe(16) # doctest: +SKIP 'Drmhze6EPcv0fN_81Bj-nA' @@ -107,7 +110,7 @@ tokens need to have sufficient randomness. Unfortunately, what is considered sufficient will necessarily increase as computers get more powerful and able to make more guesses in a shorter period. As of 2015, it is believed that 32 bytes (256 bits) of randomness is sufficient for -the typical use-case expected for the :mod:`secrets` module. +the typical use-case expected for the :mod:`!secrets` module. For those who want to manage their own token length, you can explicitly specify how much randomness is used for tokens by giving an :class:`int` @@ -115,11 +118,13 @@ argument to the various ``token_*`` functions. That argument is taken as the number of bytes of randomness to use. Otherwise, if no argument is provided, or if the argument is ``None``, -the ``token_*`` functions will use a reasonable default instead. +the ``token_*`` functions use :const:`DEFAULT_ENTROPY` instead. -.. note:: +.. data:: DEFAULT_ENTROPY + + Default number of bytes of randomness used by the ``token_*`` functions. - That default is subject to change at any time, including during + The exact value is subject to change at any time, including during maintenance releases. @@ -139,7 +144,7 @@ Other functions Recipes and best practices -------------------------- -This section shows recipes and best practices for using :mod:`secrets` +This section shows recipes and best practices for using :mod:`!secrets` to manage a basic level of security. Generate an eight-character alphanumeric password: diff --git a/Doc/library/select.rst b/Doc/library/select.rst index d2094283d54736..6400005871746a 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -18,7 +18,7 @@ it was last read. .. note:: The :mod:`selectors` module allows high-level and efficient I/O - multiplexing, built upon the :mod:`select` module primitives. Users are + multiplexing, built upon the :mod:`!select` module primitives. Users are encouraged to use the :mod:`selectors` module instead, unless they want precise control over the OS-level primitives used. @@ -37,7 +37,7 @@ The module defines the following: .. function:: devpoll() - (Only supported on Solaris and derivatives.) Returns a ``/dev/poll`` + Returns a ``/dev/poll`` polling object; see section :ref:`devpoll-objects` below for the methods supported by devpoll objects. @@ -54,15 +54,17 @@ The module defines the following: .. versionchanged:: 3.4 The new file descriptor is now non-inheritable. + .. availability:: Solaris. + .. function:: epoll(sizehint=-1, flags=0) - (Only supported on Linux 2.5.44 and newer.) Return an edge polling object, + Return an edge polling object, which can be used as Edge or Level Triggered interface for I/O events. *sizehint* informs epoll about the expected number of events to be registered. It must be positive, or ``-1`` to use the default. It is only - used on older systems where :c:func:`!epoll_create1` is not available; + used on older systems where :manpage:`epoll_create1(2)` is not available; otherwise it has no effect (though its value is still checked). *flags* is deprecated and completely ignored. However, when supplied, its @@ -89,18 +91,27 @@ The module defines the following: The *flags* parameter. ``select.EPOLL_CLOEXEC`` is used by default now. Use :func:`os.set_inheritable` to make the file descriptor inheritable. + .. versionchanged:: 3.15 + + When CPython is built, this function may be disabled using + :option:`--disable-epoll`. + + .. availability:: Linux >= 2.5.44. + .. function:: poll() - (Not supported by all operating systems.) Returns a polling object, which + Returns a polling object, which supports registering and unregistering file descriptors, and then polling them for I/O events; see section :ref:`poll-objects` below for the methods supported by polling objects. + .. availability:: Unix. + .. function:: kqueue() - (Only supported on BSD.) Returns a kernel queue object; see section + Returns a kernel queue object; see section :ref:`kqueue-objects` below for the methods supported by kqueue objects. The new file descriptor is :ref:`non-inheritable `. @@ -108,14 +119,18 @@ The module defines the following: .. versionchanged:: 3.4 The new file descriptor is now non-inheritable. + .. availability:: BSD, macOS. + .. function:: kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0) - (Only supported on BSD.) Returns a kernel event object; see section + Returns a kernel event object; see section :ref:`kevent-objects` below for the methods supported by kevent objects. + .. availability:: BSD, macOS. + -.. function:: select(rlist, wlist, xlist[, timeout]) +.. function:: select(rlist, wlist, xlist, timeout=None) This is a straightforward interface to the Unix :c:func:`!select` system call. The first three arguments are iterables of 'waitable objects': either @@ -129,8 +144,9 @@ The module defines the following: Empty iterables are allowed, but acceptance of three empty iterables is platform-dependent. (It is known to work on Unix but not on Windows.) The - optional *timeout* argument specifies a time-out as a floating-point number - in seconds. When the *timeout* argument is omitted the function blocks until + optional *timeout* argument specifies a time-out in seconds; it may be + a non-integer to specify fractions of seconds. + When the *timeout* argument is omitted or ``None``, the function blocks until at least one file descriptor is ready. A time-out value of zero specifies a poll and never blocks. @@ -164,13 +180,16 @@ The module defines the following: :pep:`475` for the rationale), instead of raising :exc:`InterruptedError`. + .. versionchanged:: 3.15 + Accepts any real number as *timeout*, not only integer or float. + .. data:: PIPE_BUF The minimum number of bytes which can be written without blocking to a pipe when the pipe has been reported as ready for writing by :func:`~select.select`, :func:`!poll` or another interface in this module. This doesn't apply - to other kind of file-like objects such as sockets. + to other kinds of file-like objects such as sockets. This value is guaranteed by POSIX to be at least 512. @@ -181,7 +200,7 @@ The module defines the following: .. _devpoll-objects: -``/dev/poll`` Polling Objects +``/dev/poll`` polling objects ----------------------------- Solaris and derivatives have ``/dev/poll``. While :c:func:`!select` is @@ -222,7 +241,7 @@ object. implement :meth:`!fileno`, so they can also be used as the argument. *eventmask* is an optional bitmask describing the type of events you want to - check for. The constants are the same that with :c:func:`!poll` + check for. The constants are the same as with :c:func:`!poll` object. The default value is a combination of the constants :const:`POLLIN`, :const:`POLLPRI`, and :const:`POLLOUT`. @@ -237,7 +256,7 @@ object. .. method:: devpoll.modify(fd[, eventmask]) This method does an :meth:`unregister` followed by a - :meth:`register`. It is (a bit) more efficient that doing the same + :meth:`register`. It is (a bit) more efficient than doing the same explicitly. @@ -270,55 +289,58 @@ object. :pep:`475` for the rationale), instead of raising :exc:`InterruptedError`. + .. versionchanged:: 3.15 + Accepts any real number as *timeout*, not only integer or float. + .. _epoll-objects: -Edge and Level Trigger Polling (epoll) Objects +Edge and level trigger polling (epoll) objects ---------------------------------------------- https://linux.die.net/man/4/epoll - *eventmask* - - +-------------------------+-----------------------------------------------+ - | Constant | Meaning | - +=========================+===============================================+ - | :const:`EPOLLIN` | Available for read | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLOUT` | Available for write | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLPRI` | Urgent data for read | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLERR` | Error condition happened on the assoc. fd | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLHUP` | Hang up happened on the assoc. fd | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLET` | Set Edge Trigger behavior, the default is | - | | Level Trigger behavior | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLONESHOT` | Set one-shot behavior. After one event is | - | | pulled out, the fd is internally disabled | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLEXCLUSIVE` | Wake only one epoll object when the | - | | associated fd has an event. The default (if | - | | this flag is not set) is to wake all epoll | - | | objects polling on a fd. | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLRDHUP` | Stream socket peer closed connection or shut | - | | down writing half of connection. | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLRDNORM` | Equivalent to :const:`EPOLLIN` | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLRDBAND` | Priority data band can be read. | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLWRNORM` | Equivalent to :const:`EPOLLOUT` | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLWRBAND` | Priority data may be written. | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLMSG` | Ignored. | - +-------------------------+-----------------------------------------------+ - | :const:`EPOLLWAKEUP` | Prevents sleep during event waiting. | - +-------------------------+-----------------------------------------------+ + The *eventmask* is a bit mask using the following constants: + + +-------------------------+------------------------------------------------+ + | Constant | Meaning | + +=========================+================================================+ + | :const:`EPOLLIN` | Available for read. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLOUT` | Available for write. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLPRI` | Urgent data for read. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLERR` | Error condition happened on the associated fd. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLHUP` | Hang up happened on the associated fd. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLET` | Set Edge Trigger behavior, the default is | + | | Level Trigger behavior. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLONESHOT` | Set one-shot behavior. After one event is | + | | pulled out, the fd is internally disabled. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLEXCLUSIVE` | Wake only one epoll object when the | + | | associated fd has an event. The default (if | + | | this flag is not set) is to wake all epoll | + | | objects polling on an fd. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLRDHUP` | Stream socket peer closed connection or shut | + | | down writing half of connection. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLRDNORM` | Equivalent to :const:`EPOLLIN` | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLRDBAND` | Priority data band can be read. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLWRNORM` | Equivalent to :const:`EPOLLOUT`. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLWRBAND` | Priority data may be written. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLMSG` | Ignored. | + +-------------------------+------------------------------------------------+ + | :const:`EPOLLWAKEUP` | Prevents sleep during event waiting. | + +-------------------------+------------------------------------------------+ .. versionadded:: 3.6 :const:`EPOLLEXCLUSIVE` was added. It's only supported by Linux Kernel 4.5 @@ -350,12 +372,12 @@ Edge and Level Trigger Polling (epoll) Objects .. method:: epoll.register(fd[, eventmask]) - Register a fd descriptor with the epoll object. + Register a file descriptor *fd* with the epoll object. .. method:: epoll.modify(fd, eventmask) - Modify a registered file descriptor. + Modify a registered file descriptor *fd*. .. method:: epoll.unregister(fd) @@ -368,7 +390,9 @@ Edge and Level Trigger Polling (epoll) Objects .. method:: epoll.poll(timeout=None, maxevents=-1) - Wait for events. timeout in seconds (float) + Wait for events. + If *timeout* is given, it specifies the length of time in seconds + (may be non-integer) which the system will wait for events before returning. .. versionchanged:: 3.5 The function is now retried with a recomputed timeout when interrupted by @@ -376,10 +400,13 @@ Edge and Level Trigger Polling (epoll) Objects :pep:`475` for the rationale), instead of raising :exc:`InterruptedError`. + .. versionchanged:: 3.15 + Accepts any real number as *timeout*, not only integer or float. + .. _poll-objects: -Polling Objects +Polling objects --------------- The :c:func:`!poll` system call, supported on most Unix systems, provides better @@ -404,24 +431,24 @@ linearly scanned again. :c:func:`!select` is *O*\ (*highest file descriptor*), w :const:`POLLPRI`, and :const:`POLLOUT`, described in the table below. If not specified, the default value used will check for all 3 types of events. - +-------------------+------------------------------------------+ - | Constant | Meaning | - +===================+==========================================+ - | :const:`POLLIN` | There is data to read | - +-------------------+------------------------------------------+ - | :const:`POLLPRI` | There is urgent data to read | - +-------------------+------------------------------------------+ - | :const:`POLLOUT` | Ready for output: writing will not block | - +-------------------+------------------------------------------+ - | :const:`POLLERR` | Error condition of some sort | - +-------------------+------------------------------------------+ - | :const:`POLLHUP` | Hung up | - +-------------------+------------------------------------------+ - | :const:`POLLRDHUP`| Stream socket peer closed connection, or | - | | shut down writing half of connection | - +-------------------+------------------------------------------+ - | :const:`POLLNVAL` | Invalid request: descriptor not open | - +-------------------+------------------------------------------+ + +-------------------+-------------------------------------------+ + | Constant | Meaning | + +===================+===========================================+ + | :const:`POLLIN` | There is data to read. | + +-------------------+-------------------------------------------+ + | :const:`POLLPRI` | There is urgent data to read. | + +-------------------+-------------------------------------------+ + | :const:`POLLOUT` | Ready for output: writing will not block. | + +-------------------+-------------------------------------------+ + | :const:`POLLERR` | Error condition of some sort. | + +-------------------+-------------------------------------------+ + | :const:`POLLHUP` | Hung up. | + +-------------------+-------------------------------------------+ + | :const:`POLLRDHUP`| Stream socket peer closed connection, or | + | | shut down writing half of connection. | + +-------------------+-------------------------------------------+ + | :const:`POLLNVAL` | Invalid request: descriptor not open. | + +-------------------+-------------------------------------------+ Registering a file descriptor that's already registered is not an error, and has the same effect as registering the descriptor exactly once. @@ -464,10 +491,15 @@ linearly scanned again. :c:func:`!select` is *O*\ (*highest file descriptor*), w :pep:`475` for the rationale), instead of raising :exc:`InterruptedError`. + .. versionchanged:: 3.15 + Accepts any real number as *timeout*, not only integer or float. + If ``ppoll()`` function is available, *timeout* has a resolution + of ``1`` ns (``1e-6`` ms) instead of ``1`` ms. + .. _kqueue-objects: -Kqueue Objects +Kqueue objects -------------- .. method:: kqueue.close() @@ -496,7 +528,7 @@ Kqueue Objects - changelist must be an iterable of kevent objects or ``None`` - max_events must be 0 or a positive integer - - timeout in seconds (floats possible); the default is ``None``, + - timeout in seconds (non-integers are possible); the default is ``None``, to wait forever .. versionchanged:: 3.5 @@ -505,10 +537,13 @@ Kqueue Objects :pep:`475` for the rationale), instead of raising :exc:`InterruptedError`. + .. versionchanged:: 3.15 + Accepts any real number as *timeout*, not only integer or float. + .. _kevent-objects: -Kevent Objects +Kevent objects -------------- https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2 @@ -528,66 +563,66 @@ https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2 | Constant | Meaning | +===========================+=============================================+ | :const:`KQ_FILTER_READ` | Takes a descriptor and returns whenever | - | | there is data available to read | + | | there is data available to read. | +---------------------------+---------------------------------------------+ | :const:`KQ_FILTER_WRITE` | Takes a descriptor and returns whenever | - | | there is data available to write | + | | there is data available to write. | +---------------------------+---------------------------------------------+ - | :const:`KQ_FILTER_AIO` | AIO requests | + | :const:`KQ_FILTER_AIO` | AIO requests. | +---------------------------+---------------------------------------------+ | :const:`KQ_FILTER_VNODE` | Returns when one or more of the requested | - | | events watched in *fflag* occurs | + | | events watched in *fflag* occurs. | +---------------------------+---------------------------------------------+ - | :const:`KQ_FILTER_PROC` | Watch for events on a process id | + | :const:`KQ_FILTER_PROC` | Watch for events on a process ID. | +---------------------------+---------------------------------------------+ | :const:`KQ_FILTER_NETDEV` | Watch for events on a network device | - | | [not available on macOS] | + | | (not available on macOS). | +---------------------------+---------------------------------------------+ | :const:`KQ_FILTER_SIGNAL` | Returns whenever the watched signal is | - | | delivered to the process | + | | delivered to the process. | +---------------------------+---------------------------------------------+ - | :const:`KQ_FILTER_TIMER` | Establishes an arbitrary timer | + | :const:`KQ_FILTER_TIMER` | Establishes an arbitrary timer. | +---------------------------+---------------------------------------------+ .. attribute:: kevent.flags Filter action. - +---------------------------+---------------------------------------------+ - | Constant | Meaning | - +===========================+=============================================+ - | :const:`KQ_EV_ADD` | Adds or modifies an event | - +---------------------------+---------------------------------------------+ - | :const:`KQ_EV_DELETE` | Removes an event from the queue | - +---------------------------+---------------------------------------------+ - | :const:`KQ_EV_ENABLE` | Permitscontrol() to returns the event | - +---------------------------+---------------------------------------------+ - | :const:`KQ_EV_DISABLE` | Disablesevent | - +---------------------------+---------------------------------------------+ - | :const:`KQ_EV_ONESHOT` | Removes event after first occurrence | - +---------------------------+---------------------------------------------+ - | :const:`KQ_EV_CLEAR` | Reset the state after an event is retrieved | - +---------------------------+---------------------------------------------+ - | :const:`KQ_EV_SYSFLAGS` | internal event | - +---------------------------+---------------------------------------------+ - | :const:`KQ_EV_FLAG1` | internal event | - +---------------------------+---------------------------------------------+ - | :const:`KQ_EV_EOF` | Filter specific EOF condition | - +---------------------------+---------------------------------------------+ - | :const:`KQ_EV_ERROR` | See return values | - +---------------------------+---------------------------------------------+ + +---------------------------+----------------------------------------------+ + | Constant | Meaning | + +===========================+==============================================+ + | :const:`KQ_EV_ADD` | Adds or modifies an event. | + +---------------------------+----------------------------------------------+ + | :const:`KQ_EV_DELETE` | Removes an event from the queue. | + +---------------------------+----------------------------------------------+ + | :const:`KQ_EV_ENABLE` | Permits control() to return the event. | + +---------------------------+----------------------------------------------+ + | :const:`KQ_EV_DISABLE` | Disables event. | + +---------------------------+----------------------------------------------+ + | :const:`KQ_EV_ONESHOT` | Removes event after first occurrence. | + +---------------------------+----------------------------------------------+ + | :const:`KQ_EV_CLEAR` | Reset the state after an event is retrieved. | + +---------------------------+----------------------------------------------+ + | :const:`KQ_EV_SYSFLAGS` | Internal event. | + +---------------------------+----------------------------------------------+ + | :const:`KQ_EV_FLAG1` | Internal event. | + +---------------------------+----------------------------------------------+ + | :const:`KQ_EV_EOF` | Filter-specific EOF condition. | + +---------------------------+----------------------------------------------+ + | :const:`KQ_EV_ERROR` | See return values. | + +---------------------------+----------------------------------------------+ .. attribute:: kevent.fflags - Filter specific flags. + Filter-specific flags. :const:`KQ_FILTER_READ` and :const:`KQ_FILTER_WRITE` filter flags: +----------------------------+--------------------------------------------+ | Constant | Meaning | +============================+============================================+ - | :const:`KQ_NOTE_LOWAT` | low water mark of a socket buffer | + | :const:`KQ_NOTE_LOWAT` | Low water mark of a socket buffer. | +----------------------------+--------------------------------------------+ :const:`KQ_FILTER_VNODE` filter flags: @@ -595,19 +630,19 @@ https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2 +----------------------------+--------------------------------------------+ | Constant | Meaning | +============================+============================================+ - | :const:`KQ_NOTE_DELETE` | *unlink()* was called | + | :const:`KQ_NOTE_DELETE` | *unlink()* was called. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_WRITE` | a write occurred | + | :const:`KQ_NOTE_WRITE` | A write occurred. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_EXTEND` | the file was extended | + | :const:`KQ_NOTE_EXTEND` | The file was extended. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_ATTRIB` | an attribute was changed | + | :const:`KQ_NOTE_ATTRIB` | An attribute was changed. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_LINK` | the link count has changed | + | :const:`KQ_NOTE_LINK` | The link count has changed. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_RENAME` | the file was renamed | + | :const:`KQ_NOTE_RENAME` | The file was renamed. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_REVOKE` | access to the file was revoked | + | :const:`KQ_NOTE_REVOKE` | Access to the file was revoked. | +----------------------------+--------------------------------------------+ :const:`KQ_FILTER_PROC` filter flags: @@ -615,22 +650,22 @@ https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2 +----------------------------+--------------------------------------------+ | Constant | Meaning | +============================+============================================+ - | :const:`KQ_NOTE_EXIT` | the process has exited | + | :const:`KQ_NOTE_EXIT` | The process has exited. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_FORK` | the process has called *fork()* | + | :const:`KQ_NOTE_FORK` | The process has called *fork()*. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_EXEC` | the process has executed a new process | + | :const:`KQ_NOTE_EXEC` | The process has executed a new process. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_PCTRLMASK` | internal filter flag | + | :const:`KQ_NOTE_PCTRLMASK` | Internal filter flag. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_PDATAMASK` | internal filter flag | + | :const:`KQ_NOTE_PDATAMASK` | Internal filter flag. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_TRACK` | follow a process across *fork()* | + | :const:`KQ_NOTE_TRACK` | Follow a process across *fork()*. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_CHILD` | returned on the child process for | - | | *NOTE_TRACK* | + | :const:`KQ_NOTE_CHILD` | Returned on the child process for | + | | *NOTE_TRACK*. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_TRACKERR` | unable to attach to a child | + | :const:`KQ_NOTE_TRACKERR` | Unable to attach to a child. | +----------------------------+--------------------------------------------+ :const:`KQ_FILTER_NETDEV` filter flags (not available on macOS): @@ -638,19 +673,19 @@ https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2 +----------------------------+--------------------------------------------+ | Constant | Meaning | +============================+============================================+ - | :const:`KQ_NOTE_LINKUP` | link is up | + | :const:`KQ_NOTE_LINKUP` | Link is up. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_LINKDOWN` | link is down | + | :const:`KQ_NOTE_LINKDOWN` | Link is down. | +----------------------------+--------------------------------------------+ - | :const:`KQ_NOTE_LINKINV` | link state is invalid | + | :const:`KQ_NOTE_LINKINV` | Link state is invalid. | +----------------------------+--------------------------------------------+ .. attribute:: kevent.data - Filter specific data. + Filter-specific data. .. attribute:: kevent.udata - User defined value. + User-defined value. diff --git a/Doc/library/selectors.rst b/Doc/library/selectors.rst index ee556f1f3cecae..2d523a9d2ea440 100644 --- a/Doc/library/selectors.rst +++ b/Doc/library/selectors.rst @@ -54,7 +54,7 @@ Classes hierarchy:: In the following, *events* is a bitwise mask indicating which I/O events should -be waited for on a given file object. It can be a combination of the modules +be waited for on a given file object. It can be a combination of the module's constants below: +-----------------------+-----------------------------------------------+ diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst index b88fe4157bdc29..bd3d56f6af595a 100644 --- a/Doc/library/shelve.rst +++ b/Doc/library/shelve.rst @@ -42,7 +42,7 @@ lots of shared sub-objects. The keys are ordinary strings. determine which accessed entries are mutable, nor which ones were actually mutated). - By default, :mod:`shelve` uses :func:`pickle.dumps` and :func:`pickle.loads` + By default, :mod:`!shelve` uses :func:`pickle.dumps` and :func:`pickle.loads` for serializing and deserializing. This can be changed by supplying *serializer* and *deserializer*, respectively. @@ -51,7 +51,7 @@ lots of shared sub-objects. The keys are ordinary strings. :term:`bytes-like object`; the *protocol* value may be ignored by the serializer. - The *deserializer* argument must be callable which takes a serialized object + The *deserializer* argument must be a callable which takes a serialized object given as a :class:`bytes` object and returns the corresponding object. A :exc:`ShelveError` is raised if *serializer* is given but *deserializer* @@ -64,7 +64,7 @@ lots of shared sub-objects. The keys are ordinary strings. .. versionchanged:: 3.11 Accepts :term:`path-like object` for filename. - .. versionchanged:: next + .. versionchanged:: 3.15 Accepts custom *serializer* and *deserializer* functions in place of :func:`pickle.dumps` and :func:`pickle.loads`. @@ -81,11 +81,11 @@ lots of shared sub-objects. The keys are ordinary strings. .. warning:: - Because the :mod:`shelve` module is backed by :mod:`pickle`, it is insecure + Because the :mod:`!shelve` module is backed by :mod:`pickle`, it is insecure to load a shelf from an untrusted source. Like with pickle, loading a shelf can execute arbitrary code. -Shelf objects support most of methods and operations supported by dictionaries +Shelf objects support most of the methods and operations supported by dictionaries (except copying, constructors and operators ``|`` and ``|=``). This eases the transition from dictionary based scripts to those requiring persistent storage. @@ -103,7 +103,7 @@ Two additional methods are supported: Calls :meth:`sync` and attempts to shrink space used on disk by removing empty space resulting from deletions. - .. versionadded:: next + .. versionadded:: 3.15 .. method:: Shelf.close() @@ -133,7 +133,7 @@ Restrictions database should be fairly small, and in rare cases key collisions may cause the database to refuse updates. -* The :mod:`shelve` module does not support *concurrent* read/write access to +* The :mod:`!shelve` module does not support *concurrent* read/write access to shelved objects. (Multiple simultaneous read accesses are safe.) When a program has a shelf open for writing, no other program should have it open for reading or writing. Unix file locking can be used to solve this, but this @@ -185,7 +185,7 @@ Restrictions :const:`pickle.DEFAULT_PROTOCOL` is now used as the default pickle protocol. - .. versionchanged:: next + .. versionchanged:: 3.15 Added the *serializer* and *deserializer* parameters. @@ -204,7 +204,7 @@ Restrictions optional *protocol*, *writeback*, *keyencoding*, *serializer* and *deserializer* parameters have the same interpretation as in :func:`~shelve.open`. - .. versionchanged:: next + .. versionchanged:: 3.15 Added the *serializer* and *deserializer* parameters. @@ -220,7 +220,7 @@ Restrictions and *deserializer* parameters have the same interpretation as in :func:`~shelve.open`. - .. versionchanged:: next + .. versionchanged:: 3.15 Added the *serializer* and *deserializer* parameters. @@ -274,7 +274,7 @@ Exceptions The *deserializer* and *serializer* arguments must be given together. - .. versionadded:: next + .. versionadded:: 3.15 .. seealso:: @@ -283,5 +283,5 @@ Exceptions Generic interface to ``dbm``-style databases. Module :mod:`pickle` - Object serialization used by :mod:`shelve`. + Object serialization used by :mod:`!shelve`. diff --git a/Doc/library/shlex.rst b/Doc/library/shlex.rst index a96f0864dc1260..2dfb0246d5d90c 100644 --- a/Doc/library/shlex.rst +++ b/Doc/library/shlex.rst @@ -4,11 +4,6 @@ .. module:: shlex :synopsis: Simple lexical analysis for Unix shell-like languages. -.. moduleauthor:: Eric S. Raymond -.. moduleauthor:: Gustavo Niemeyer -.. sectionauthor:: Eric S. Raymond -.. sectionauthor:: Gustavo Niemeyer - **Source code:** :source:`Lib/shlex.py` -------------- @@ -18,7 +13,7 @@ simple syntaxes resembling that of the Unix shell. This will often be useful for writing minilanguages, (for example, in run control files for Python applications) or for parsing quoted strings. -The :mod:`shlex` module defines the following functions: +The :mod:`!shlex` module defines the following functions: .. function:: split(s, comments=False, posix=True) @@ -49,12 +44,15 @@ The :mod:`shlex` module defines the following functions: .. versionadded:: 3.8 -.. function:: quote(s) +.. function:: quote(s, *, force=False) Return a shell-escaped version of the string *s*. The returned value is a string that can safely be used as one token in a shell command line, for cases where you cannot use a list. + If *force* is :const:`True`, then *s* is unconditionally quoted, + even if it is already safe for a shell without being quoted. + .. _shlex-quote-warning: .. warning:: @@ -96,9 +94,24 @@ The :mod:`shlex` module defines the following functions: >>> command ['ls', '-l', 'somefile; rm -rf ~'] + The *force* keyword can be used to produce consistent behavior when + escaping multiple strings: + + >>> from shlex import quote + >>> filenames = ['my first file', 'file2', 'file 3'] + >>> filenames_some_escaped = [quote(f) for f in filenames] + >>> filenames_some_escaped + ["'my first file'", 'file2', "'file 3'"] + >>> filenames_all_escaped = [quote(f, force=True) for f in filenames] + >>> filenames_all_escaped + ["'my first file'", "'file2'", "'file 3'"] + .. versionadded:: 3.3 -The :mod:`shlex` module defines the following class: + .. versionchanged:: next + The *force* keyword was added. + +The :mod:`!shlex` module defines the following class: .. class:: shlex(instream=None, infile=None, posix=False, punctuation_chars=False) @@ -214,7 +227,7 @@ A :class:`~shlex.shlex` instance has the following methods: with the name of the current source file and the ``%d`` with the current input line number (the optional arguments can be used to override these). - This convenience is provided to encourage :mod:`shlex` users to generate error + This convenience is provided to encourage :mod:`!shlex` users to generate error messages in the standard, parseable format understood by Emacs and other Unix tools. @@ -343,7 +356,7 @@ variables which either control lexical analysis or can be used for debugging: Parsing Rules ------------- -When operating in non-POSIX mode, :class:`~shlex.shlex` will try to obey to the +When operating in non-POSIX mode, :class:`~shlex.shlex` will try to obey the following rules. * Quote characters are not recognized within words (``Do"Not"Separate`` is @@ -366,7 +379,7 @@ following rules. * It's not possible to parse empty strings, even if quoted. -When operating in POSIX mode, :class:`~shlex.shlex` will try to obey to the +When operating in POSIX mode, :class:`~shlex.shlex` will try to obey the following parsing rules. * Quotes are stripped out, and do not separate words (``"Do"Not"Separate"`` is @@ -382,7 +395,7 @@ following parsing rules. * Enclosing characters in quotes which are part of :attr:`~shlex.escapedquotes` (e.g. ``'"'``) preserves the literal value of all characters within the quotes, with the exception of the characters - mentioned in :attr:`~shlex.escape`. The escape characters retain its + mentioned in :attr:`~shlex.escape`. The escape characters retain their special meaning only when followed by the quote in use, or the escape character itself. Otherwise the escape character will be considered a normal character. diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst index dde38498206c46..6febc7a187a15f 100644 --- a/Doc/library/shutil.rst +++ b/Doc/library/shutil.rst @@ -4,9 +4,6 @@ .. module:: shutil :synopsis: High-level file operations, including copying. -.. sectionauthor:: Fred L. Drake, Jr. -.. partly based on the docstrings - **Source code:** :source:`Lib/shutil.py` .. index:: @@ -15,7 +12,7 @@ -------------- -The :mod:`shutil` module offers a number of high-level operations on files and +The :mod:`!shutil` module offers a number of high-level operations on files and collections of files. In particular, functions are provided which support file copying and removal. For operations on individual files, see also the :mod:`os` module. @@ -90,6 +87,13 @@ Directory and files operations copy the file more efficiently. See :ref:`shutil-platform-dependent-efficient-copy-operations` section. +.. exception:: SpecialFileError + + This exception is raised when :func:`copyfile` or :func:`copytree` attempt + to copy a named pipe. + + .. versionadded:: 2.7 + .. exception:: SameFileError This exception is raised if source and destination in :func:`copyfile` @@ -381,10 +385,14 @@ Directory and files operations If *dst* already exists but is not a directory, it may be overwritten depending on :func:`os.rename` semantics. - If the destination is on the current filesystem, then :func:`os.rename` is - used. Otherwise, *src* is copied to the destination using *copy_function* - and then removed. In case of symlinks, a new symlink pointing to the target - of *src* will be created as the destination and *src* will be removed. + :func:`os.rename` is preferably used internally when *src* and the destination are on + the same filesystem. In case :func:`os.rename` fails due to :exc:`OSError` + (e.g. the user has write permission to the destination file but not to its parent + directory), this method falls back to using *copy_function*, in which case + *src* is copied to the destination using *copy_function* and then removed. + + In case of symlinks, a new symlink pointing to the target of *src* will be + created in or as the destination, and *src* will be removed. If *copy_function* is given, it must be a callable that takes two arguments, *src* and the destination, and will be used to copy *src* to the destination @@ -508,7 +516,7 @@ Directory and files operations .. exception:: Error - This exception collects exceptions that are raised during a multi-file + Subclass of :exc:`OSError` collecting exceptions raised during a multi-file operation. For :func:`copytree`, the exception argument is a list of 3-tuples (*srcname*, *dstname*, *exception*). @@ -533,10 +541,12 @@ On Solaris :func:`os.sendfile` is used. On Windows :func:`shutil.copyfile` uses a bigger default buffer size (1 MiB instead of 64 KiB) and a :func:`memoryview`-based variant of -:func:`shutil.copyfileobj` is used. +:func:`shutil.copyfileobj` is used, which still reads and writes in a loop. +:func:`shutil.copy2` uses the native ``CopyFile2`` call on Windows, which is the most +efficient method, supports copy-on-write, and preserves metadata. If the fast-copy operation fails and no data was written in the destination -file then shutil will silently fallback on using less efficient +file then shutil will silently fall back to less efficient :func:`copyfileobj` function internally. .. versionchanged:: 3.8 @@ -612,8 +622,8 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. Create an archive file (such as zip or tar) and return its name. - *base_name* is the name of the file to create, including the path, minus - any format-specific extension. + *base_name* is a string or :term:`path-like object` specifying the name of + the file to create, including the path, minus any format-specific extension. *format* is the archive format: one of "zip" (if the :mod:`zlib` module is available), "tar", "gztar" (if the @@ -621,13 +631,14 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. available), "xztar" (if the :mod:`lzma` module is available), or "zstdtar" (if the :mod:`compression.zstd` module is available). - *root_dir* is a directory that will be the root directory of the - archive, all paths in the archive will be relative to it; for example, - we typically chdir into *root_dir* before creating the archive. + *root_dir* is a string or :term:`path-like object` specifying a directory + that will be the root directory of the archive, all paths in the archive + will be relative to it; for example, we typically chdir into *root_dir* + before creating the archive. - *base_dir* is the directory where we start archiving from; - i.e. *base_dir* will be the common prefix of all files and - directories in the archive. *base_dir* must be given relative + *base_dir* is a string or :term:`path-like object` specifying a directory + where we start archiving from; i.e. *base_dir* will be the common prefix of + all files and directories in the archive. *base_dir* must be given relative to *root_dir*. See :ref:`shutil-archiving-example-with-basedir` for how to use *base_dir* and *root_dir* together. @@ -662,12 +673,16 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. This function is now made thread-safe during creation of standard ``.zip`` and tar archives. + .. versionchanged:: 3.15 + Accepts a :term:`path-like object` for *base_name*, *root_dir* and + *base_dir*. + .. function:: get_archive_formats() Return a list of supported formats for archiving. Each element of the returned sequence is a tuple ``(name, description)``. - By default :mod:`shutil` provides these formats: + By default :mod:`!shutil` provides these formats: - *zip*: ZIP file (if the :mod:`zlib` module is available). - *tar*: Uncompressed tar file. Uses POSIX.1-2001 pax format for new archives. @@ -685,7 +700,7 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. Register an archiver for the format *name*. - *function* is the callable that will be used to unpack archives. The callable + *function* is the callable that will be used to create archives. The callable will receive the *base_name* of the file to create, followed by the *base_dir* (which defaults to :data:`os.curdir`) to start archiving from. Further arguments are passed as keyword arguments: *owner*, *group*, @@ -738,8 +753,8 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. Never extract archives from untrusted sources without prior inspection. It is possible that files are created outside of the path specified in - the *extract_dir* argument, e.g. members that have absolute filenames - starting with "/" or filenames with two dots "..". + the *extract_dir* argument, for example, members that have absolute filenames + or filenames with ".." components. Since Python 3.14, the defaults for both built-in formats (zip and tar files) will prevent the most dangerous of such security issues, @@ -784,7 +799,7 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules. Each element of the returned sequence is a tuple ``(name, extensions, description)``. - By default :mod:`shutil` provides these formats: + By default :mod:`!shutil` provides these formats: - *zip*: ZIP file (unpacking compressed files works only if the corresponding module is available). @@ -860,7 +875,7 @@ In the final archive, :file:`please_add.txt` should be included, but ... root_dir='tmp/root', ... base_dir='structure/content', ... ) - '/Users/tarek/my_archive.tar' + '/Users/tarek/myarchive.tar' Listing the files in the resulting archive gives us: diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst index b0307d3dea1170..12ad45f557e6db 100644 --- a/Doc/library/signal.rst +++ b/Doc/library/signal.rst @@ -36,7 +36,7 @@ Execution of Python signal handlers A Python signal handler does not get executed inside the low-level (C) signal handler. Instead, the low-level signal handler sets a flag which tells the :term:`virtual machine` to execute the corresponding Python signal handler -at a later point(for example at the next :term:`bytecode` instruction). +at a later point (for example, at the next :term:`bytecode` instruction). This has consequences: * It makes little sense to catch synchronous errors like :const:`SIGFPE` or @@ -68,6 +68,11 @@ the synchronization primitives from the :mod:`threading` module instead. Besides, only the main thread of the main interpreter is allowed to set a new signal handler. +.. warning:: + + Synchronization primitives such as :class:`threading.Lock` should not be used + within signal handlers. Doing so can lead to unexpected deadlocks. + Module contents --------------- @@ -92,13 +97,13 @@ The signal module defines three enums: .. class:: Handlers - :class:`enum.IntEnum` collection the constants :const:`SIG_DFL` and :const:`SIG_IGN`. + :class:`enum.IntEnum` collection of the constants :const:`SIG_DFL` and :const:`SIG_IGN`. .. versionadded:: 3.5 .. class:: Sigmasks - :class:`enum.IntEnum` collection the constants :const:`SIG_BLOCK`, :const:`SIG_UNBLOCK` and :const:`SIG_SETMASK`. + :class:`enum.IntEnum` collection of the constants :const:`SIG_BLOCK`, :const:`SIG_UNBLOCK` and :const:`SIG_SETMASK`. .. availability:: Unix. @@ -108,7 +113,7 @@ The signal module defines three enums: .. versionadded:: 3.5 -The variables defined in the :mod:`signal` module are: +The variables defined in the :mod:`!signal` module are: .. data:: SIG_DFL @@ -205,10 +210,28 @@ The variables defined in the :mod:`signal` module are: .. availability:: Unix. +.. data:: SIGPROF + + Profiling timer expired. + + .. availability:: Unix. + +.. data:: SIGQUIT + + Terminal quit signal. + + .. availability:: Unix. + .. data:: SIGSEGV Segmentation fault: invalid memory reference. +.. data:: SIGSTOP + + Stop executing (cannot be caught or ignored). + + .. availability:: Unix. + .. data:: SIGSTKFLT Stack fault on coprocessor. The Linux kernel does not raise this signal: it @@ -237,18 +260,30 @@ The variables defined in the :mod:`signal` module are: .. availability:: Unix. +.. data:: SIGVTALRM + + Virtual timer expired. + + .. availability:: Unix. + .. data:: SIGWINCH Window resize signal. .. availability:: Unix. +.. data:: SIGXCPU + + CPU time limit exceeded. + + .. availability:: Unix. + .. data:: SIG* All the signal numbers are defined symbolically. For example, the hangup signal is defined as :const:`signal.SIGHUP`; the variable names are identical to the names used in C programs, as found in ````. The Unix man page for - ':c:func:`signal`' lists the existing signals (on some systems this is + '``signal``' lists the existing signals (on some systems this is :manpage:`signal(2)`, on others the list is in :manpage:`signal(7)`). Note that not all systems define the same set of signal names; only those names defined by the system are defined by this module. @@ -322,7 +357,7 @@ The variables defined in the :mod:`signal` module are: .. versionadded:: 3.3 -The :mod:`signal` module defines one exception: +The :mod:`!signal` module defines one exception: .. exception:: ItimerError @@ -336,7 +371,7 @@ The :mod:`signal` module defines one exception: alias of :exc:`OSError`. -The :mod:`signal` module defines the following functions: +The :mod:`!signal` module defines the following functions: .. function:: alarm(time) @@ -478,11 +513,11 @@ The :mod:`signal` module defines the following functions: .. versionadded:: 3.3 -.. function:: setitimer(which, seconds, interval=0.0) +.. function:: setitimer(which, seconds, interval=0) Sets given interval timer (one of :const:`signal.ITIMER_REAL`, :const:`signal.ITIMER_VIRTUAL` or :const:`signal.ITIMER_PROF`) specified - by *which* to fire after *seconds* (float is accepted, different from + by *which* to fire after *seconds* (rounded up to microseconds, different from :func:`alarm`) and after that every *interval* seconds (if *interval* is non-zero). The interval timer specified by *which* can be cleared by setting *seconds* to zero. @@ -493,13 +528,18 @@ The :mod:`signal` module defines the following functions: :const:`signal.ITIMER_VIRTUAL` sends :const:`SIGVTALRM`, and :const:`signal.ITIMER_PROF` will deliver :const:`SIGPROF`. - The old values are returned as a tuple: (delay, interval). + The old values are returned as a two-tuple of floats: + (``delay``, ``interval``). Attempting to pass an invalid interval timer will cause an :exc:`ItimerError`. .. availability:: Unix. + .. versionchanged:: 3.15 + Accepts any real numbers as *seconds* and *interval*, not only integers + or floats. + .. function:: getitimer(which) @@ -639,9 +679,8 @@ The :mod:`signal` module defines the following functions: *sigset*. The return value is an object representing the data contained in the - :c:type:`siginfo_t` structure, namely: :attr:`si_signo`, :attr:`si_code`, - :attr:`si_errno`, :attr:`si_pid`, :attr:`si_uid`, :attr:`si_status`, - :attr:`si_band`. + ``siginfo_t`` structure, namely: ``si_signo``, ``si_code``, + ``si_errno``, ``si_pid``, ``si_uid``, ``si_status``, ``si_band``. .. availability:: Unix. @@ -676,6 +715,9 @@ The :mod:`signal` module defines the following functions: by a signal not in *sigset* and the signal handler does not raise an exception (see :pep:`475` for the rationale). + .. versionchanged:: 3.15 + Accepts any real number as *timeout*, not only integer or float. + .. _signal-example: diff --git a/Doc/library/site.rst b/Doc/library/site.rst index e98dd83b60eb60..11a5484c2b1336 100644 --- a/Doc/library/site.rst +++ b/Doc/library/site.rst @@ -17,7 +17,7 @@ import can be suppressed using the interpreter's :option:`-S` option. Importing this module normally appends site-specific paths to the module search path and adds :ref:`callables `, including :func:`help` to the built-in -namespace. However, Python startup option :option:`-S` blocks this and this module +namespace. However, Python startup option :option:`-S` blocks this, and this module can be safely imported with no automatic modifications to the module search path or additions to the builtins. To explicitly trigger the usual site-specific additions, call the :func:`main` function. @@ -34,7 +34,7 @@ For the head part, it uses ``sys.prefix`` and ``sys.exec_prefix``; empty heads are skipped. For the tail part, it uses the empty string and then :file:`lib/site-packages` (on Windows) or :file:`lib/python{X.Y[t]}/site-packages` (on Unix and macOS). (The -optional suffix "t" indicates the :term:`free threading` build, and is +optional suffix "t" indicates the :term:`free-threaded build`, and is appended if ``"t"`` is present in the :data:`sys.abiflags` constant.) For each of the distinct head-tail combinations, it sees if it refers to an existing @@ -51,11 +51,11 @@ added path for configuration files. .. versionchanged:: 3.14 - :mod:`site` is no longer responsible for updating :data:`sys.prefix` and + :mod:`!site` is no longer responsible for updating :data:`sys.prefix` and :data:`sys.exec_prefix` on :ref:`sys-path-init-virtual-environments`. This is now done during the :ref:`path initialization `. As a result, under :ref:`sys-path-init-virtual-environments`, :data:`sys.prefix` and - :data:`sys.exec_prefix` no longer depend on the :mod:`site` initialization, + :data:`sys.exec_prefix` no longer depend on the :mod:`!site` initialization, and are therefore unaffected by :option:`-S`. .. _site-virtual-environments-configuration: @@ -64,46 +64,128 @@ When running under a :ref:`virtual environment ` :file:`{name}.start` file exists. + + Errors on individual lines no longer abort processing of the rest of the + file. Each error is reported and the remaining lines continue to be + processed. + +.. deprecated-removed:: 3.15 3.20 + + Decoding :file:`{name}.pth` files in any encoding other than ``utf-8-sig`` + is deprecated in Python 3.15, and support for decoding from the locale + encoding will be removed in Python 3.20. + + ``import`` lines in :file:`{name}.pth` files are deprecated and will be + silently ignored in Python 3.18 and 3.19. In Python 3.20 a warning will be + produced for ``import`` lines in :file:`{name}.pth` files. + + +.. _site-start-files: + +Startup entry points (:file:`.start` files) +------------------------------------------- + +.. versionadded:: 3.15 + +A startup entry point file is a file whose name has the form +:file:`{name}.start` and exists in one of the site-packages directories +described above. Each file specifies entry points to be called during +interpreter startup, using the ``pkg.mod:callable`` syntax understood by +:func:`pkgutil.resolve_name`. + +Each non-blank line that does not begin with ``#`` must contain an entry +point reference in the form ``pkg.mod:callable``. The colon and callable +portion are mandatory. Each callable is invoked with no arguments, and +any return value is discarded. + +:file:`.start` files are processed after all :file:`.pth` path extensions +have been applied to :data:`sys.path`, ensuring that paths are available +before any startup code runs. + +Unlike :data:`sys.path` extensions from :file:`.pth` files, duplicate entry +points are **not** de-duplicated --- if an entry point appears more than once, +it will be called more than once. + +If an exception occurs during resolution or invocation of an entry point, +a traceback is printed to :data:`sys.stderr` and processing continues with +the remaining entry points. + +:file:`.start` files must be encoded in UTF-8. + +:pep:`829` defined the original specification for these features. + +.. note:: + + If a :file:`{name}.start` file exists alongside a :file:`{name}.pth` file + with the same base name, any ``import`` lines in the :file:`.pth` file are + ignored in favor of the entry points in the :file:`.start` file. + +.. note:: + + Executable lines (``import`` lines in :file:`{name}.pth` files and + :file:`{name}.start` file entry points) are always run at Python startup + (unless :option:`-S` is given to disable the ``site.py`` module entirely), + regardless of whether a particular module is actually going to be used. + +.. note:: + + :file:`{name}.start` files invoke :func:`pkgutil.resolve_name` with + ``strict=True``, which requires the full ``pkg.mod:callable`` form. + .. index:: single: package triple: path; configuration; file + +Startup file examples +--------------------- + For example, suppose ``sys.prefix`` and ``sys.exec_prefix`` are set to :file:`/usr/local`. The Python X.Y library is then installed in :file:`/usr/local/lib/python{X.Y}`. Suppose this has a subdirectory :file:`/usr/local/lib/python{X.Y}/site-packages` with three -subsubdirectories, :file:`foo`, :file:`bar` and :file:`spam`, and two path +sub-subdirectories, :file:`foo`, :file:`bar` and :file:`spam`, and two path configuration files, :file:`foo.pth` and :file:`bar.pth`. Assume :file:`foo.pth` contains the following:: @@ -130,38 +212,77 @@ directory precedes the :file:`foo` directory because :file:`bar.pth` comes alphabetically before :file:`foo.pth`; and :file:`spam` is omitted because it is not mentioned in either path configuration file. -:mod:`sitecustomize` --------------------- +Let's say that there is also a :file:`foo.start` file containing the +following:: + + # foo package startup code + + foo.submod:initialize + +Now, after ``sys.path`` has been extended as above, and before Python turns +control over to user code, the ``foo.submod`` module is imported and the +``initialize()`` function from that module is called. + + +.. _site-migration-guide: + +Migrating from ``import`` lines in ``.pth`` files to ``.start`` files +--------------------------------------------------------------------- + +If your package currently ships a :file:`{name}.pth` file, you can keep all +``sys.path`` extension lines unchanged. Only ``import`` lines need to be +migrated. + +To migrate, create a callable (taking zero arguments) within an importable +module in your package. Reference it as a ``pkg.mod:callable`` entry point +in a matching :file:`{name}.start` file. Move everything on your ``import`` +line after the first semi-colon into the ``callable()`` function. + +If your package must straddle older Pythons that do not support :pep:`829` +and newer Pythons that do, change the ``import`` lines in your +:file:`{name}.pth` to use the following form: + +.. code-block:: python + + import pkg.mod; pkg.mod.callable() + +Older Pythons will execute these ``import`` lines, while newer Pythons will +ignore them in favor of the :file:`{name}.start` file. After the straddling +period, remove all ``import`` lines from your :file:`.pth` files. + + +:mod:`!sitecustomize` +--------------------- .. module:: sitecustomize After these path manipulations, an attempt is made to import a module named -:mod:`sitecustomize`, which can perform arbitrary site-specific customizations. +:mod:`!sitecustomize`, which can perform arbitrary site-specific customizations. It is typically created by a system administrator in the site-packages directory. If this import fails with an :exc:`ImportError` or its subclass exception, and the exception's :attr:`~ImportError.name` -attribute equals to ``'sitecustomize'``, +attribute equals ``'sitecustomize'``, it is silently ignored. If Python is started without output streams available, as with :file:`pythonw.exe` on Windows (which is used by default to start IDLE), -attempted output from :mod:`sitecustomize` is ignored. Any other exception +attempted output from :mod:`!sitecustomize` is ignored. Any other exception causes a silent and perhaps mysterious failure of the process. -:mod:`usercustomize` --------------------- +:mod:`!usercustomize` +--------------------- .. module:: usercustomize -After this, an attempt is made to import a module named :mod:`usercustomize`, +After this, an attempt is made to import a module named :mod:`!usercustomize`, which can perform arbitrary user-specific customizations, if :data:`~site.ENABLE_USER_SITE` is true. This file is intended to be created in the user site-packages directory (see below), which is part of ``sys.path`` unless disabled by :option:`-s`. If this import fails with an :exc:`ImportError` or its subclass exception, and the exception's :attr:`~ImportError.name` -attribute equals to ``'usercustomize'``, it is silently ignored. +attribute equals ``'usercustomize'``, it is silently ignored. Note that for some non-Unix systems, ``sys.prefix`` and ``sys.exec_prefix`` are empty, and the path manipulations are skipped; however the import of -:mod:`sitecustomize` and :mod:`usercustomize` is still attempted. +:mod:`sitecustomize` and :mod:`!usercustomize` is still attempted. .. currentmodule:: site @@ -173,7 +294,7 @@ Readline configuration On systems that support :mod:`readline`, this module will also import and configure the :mod:`rlcompleter` module, if Python is started in :ref:`interactive mode ` and without the :option:`-S` option. -The default behavior is enable tab-completion and to use +The default behavior is to enable tab completion and to use :file:`~/.python_history` as the history save file. To disable it, delete (or override) the :data:`sys.__interactivehook__` attribute in your :mod:`sitecustomize` or :mod:`usercustomize` module or your @@ -235,10 +356,97 @@ Module contents This function used to be called unconditionally. +.. function:: makepath(*paths) + + Join *paths* with :func:`os.path.join`, attempt to make the result + absolute with :func:`os.path.abspath`, and return a 2-tuple containing + the absolute path and its case-normalized form as produced by + :func:`os.path.normcase`. If :func:`os.path.abspath` raises + :exc:`OSError`, the joined path is used unchanged for the + case-normalization step. + + The second element of the returned tuple is the form used throughout the + :mod:`!site` module to compare paths on case-insensitive file systems, and + is what populates the ``known_paths`` sets that prevent duplicate + :data:`sys.path` entries in various APIs within this module. + + +.. class:: StartupState(known_paths=None) + + Instances of this class accumulate interpreter startup configuration data + from one or more site directories. They are the preferred interface for + batching the processing of :file:`.pth` and :file:`.start` files across + multiple site directories, so that every :data:`sys.path` extension is + visible before any startup code runs. + + The optional *known_paths* argument is a set of case-normalized paths + (which can be produced by :func:`makepath`) used to prevent duplicate + :data:`sys.path` entries. When ``None`` (the default), the set is built + from the current :data:`sys.path`. :func:`main` implicitly uses an + instance of this class. + + Typical use: + + .. code-block:: python + + state = site.StartupState() + for sitedir in site_dirs: + state.addsitedir(sitedir) + state.process() + + .. versionadded:: 3.15 + + .. method:: addsitedir(sitedir) + + Read the :file:`.pth` and :file:`.start` files in *sitedir* and + record their :data:`sys.path` extensions, deprecated :file:`.pth` + ``import`` lines, and :file:`.start` entry points on this state. + The recorded data is not applied until :meth:`process` is called. + + .. method:: addusersitepackages() + + Add the per-user site-packages directory, if enabled and if it exists. + The directory's startup data is accumulated for later processing by + :meth:`process`. + + .. method:: addsitepackages(prefixes=None) + + Add global site-packages directories, computed from *prefixes* or from + the global :data:`PREFIXES` when *prefixes* is ``None``. Each + directory's startup data is accumulated for later processing by + :meth:`process`. + + .. method:: process() + + Apply the accumulated state by first adding the path extensions to + :data:`sys.path`, then executing the :file:`.start` file entry points + and :file:`.pth` file ``import`` lines (:ref:`deprecated + `). + + This method is not idempotent and must not be called more than once + on the same instance. Doing so will apply the accumulated state + more than once, re-running entry points and ``import`` lines. + + .. function:: addsitedir(sitedir, known_paths=None) - Add a directory to sys.path and process its :file:`.pth` files. Typically - used in :mod:`sitecustomize` or :mod:`usercustomize` (see above). + Add a directory to sys.path and parse the :file:`.pth` and :file:`.start` + files found in that directory. Typically used in :mod:`sitecustomize` or + :mod:`usercustomize` (see above). + + The *known_paths* argument is an optional set of case-normalized paths + used to prevent duplicate :data:`sys.path` entries. When ``None`` (the + default), the set is built from the current :data:`sys.path`. + + For batched processing across multiple site directories, build a + :class:`StartupState` explicitly and call :meth:`StartupState.addsitedir` + on it; that defers :file:`.pth` and :file:`.start` processing until a + single :meth:`StartupState.process` call, ensuring every :data:`sys.path` + extension is visible before any startup code runs. + + .. versionchanged:: 3.15 + + Also processes :file:`.start` files. See :ref:`site-start-files`. .. function:: getsitepackages() @@ -270,12 +478,12 @@ Module contents .. _site-commandline: -Command Line Interface +Command-line interface ---------------------- .. program:: site -The :mod:`site` module also provides a way to get the user directories from the +The :mod:`!site` module also provides a way to get the user directories from the command line: .. code-block:: shell-session @@ -307,5 +515,5 @@ value greater than 2 if there is an error. .. seealso:: * :pep:`370` -- Per user site-packages directory + * :pep:`829` -- Startup entry points and the deprecation of import lines in ``.pth`` files * :ref:`sys-path-init` -- The initialization of :data:`sys.path`. - diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst index c5f8516f768a68..5c97199bc453e8 100644 --- a/Doc/library/smtplib.rst +++ b/Doc/library/smtplib.rst @@ -4,8 +4,6 @@ .. module:: smtplib :synopsis: SMTP protocol client (requires sockets). -.. sectionauthor:: Eric S. Raymond - **Source code:** :source:`Lib/smtplib.py` .. index:: @@ -14,7 +12,7 @@ -------------- -The :mod:`smtplib` module defines an SMTP client session object that can be used +The :mod:`!smtplib` module defines an SMTP client session object that can be used to send mail to any internet machine with an SMTP or ESMTP listener daemon. For details of SMTP and ESMTP operation, consult :rfc:`821` (Simple Mail Transfer Protocol) and :rfc:`1869` (SMTP Service Extensions). @@ -24,10 +22,13 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). .. class:: SMTP(host='', port=0, local_hostname=None[, timeout], source_address=None) An :class:`SMTP` instance encapsulates an SMTP connection. It has methods - that support a full repertoire of SMTP and ESMTP operations. If the optional - *host* and *port* parameters are given, the SMTP :meth:`connect` method is - called with those parameters during initialization. If specified, - *local_hostname* is used as the FQDN of the local host in the HELO/EHLO + that support a full repertoire of SMTP and ESMTP operations. + + If the host parameter is set to a truthy value, :meth:`SMTP.connect` is called with + host and port automatically when the object is created; otherwise, :meth:`!connect` must + be called manually. + + If specified, *local_hostname* is used as the FQDN of the local host in the HELO/EHLO command. Otherwise, the local hostname is found using :func:`socket.getfqdn`. If the :meth:`connect` call returns anything other than a success code, an :exc:`SMTPConnectError` is raised. The optional @@ -62,6 +63,10 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). ``smtplib.SMTP.send`` with arguments ``self`` and ``data``, where ``data`` is the bytes about to be sent to the remote host. + .. attribute:: SMTP.default_port + + The default port used for SMTP connections (25). + .. versionchanged:: 3.3 Support for the :keyword:`with` statement was added. @@ -80,15 +85,23 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). An :class:`SMTP_SSL` instance behaves exactly the same as instances of :class:`SMTP`. :class:`SMTP_SSL` should be used for situations where SSL is - required from the beginning of the connection and using :meth:`starttls` is - not appropriate. If *host* is not specified, the local host is used. If - *port* is zero, the standard SMTP-over-SSL port (465) is used. The optional - arguments *local_hostname*, *timeout* and *source_address* have the same + required from the beginning of the connection and using :meth:`SMTP.starttls` is + not appropriate. + + If the host parameter is set to a truthy value, :meth:`SMTP.connect` is called with host + and port automatically when the object is created; otherwise, :meth:`!SMTP.connect` must + be called manually. + + The optional arguments *local_hostname*, *timeout* and *source_address* have the same meaning as they do in the :class:`SMTP` class. *context*, also optional, can contain a :class:`~ssl.SSLContext` and allows configuring various aspects of the secure connection. Please read :ref:`ssl-security` for best practices. + .. attribute:: SMTP_SSL.default_port + + The default port used for SMTP-over-SSL connections (465). + .. versionchanged:: 3.3 *context* was added. @@ -112,7 +125,7 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions). The LMTP protocol, which is very similar to ESMTP, is heavily based on the standard SMTP client. It's common to use Unix sockets for LMTP, so our - :meth:`connect` method must support that as well as a regular host:port + :meth:`~SMTP.connect` method must support that as well as a regular host:port server. The optional arguments *local_hostname* and *source_address* have the same meaning as they do in the :class:`SMTP` class. To specify a Unix socket, you must use an absolute path for *host*, starting with a '/'. @@ -147,9 +160,15 @@ A nice selection of exceptions is defined as well: .. exception:: SMTPResponseException Base class for all exceptions that include an SMTP error code. These exceptions - are generated in some instances when the SMTP server returns an error code. The - error code is stored in the :attr:`smtp_code` attribute of the error, and the - :attr:`smtp_error` attribute is set to the error message. + are generated in some instances when the SMTP server returns an error code. + + .. attribute:: smtp_code + + The error code. + + .. attribute:: smtp_error + + The error message. .. exception:: SMTPSenderRefused @@ -161,9 +180,13 @@ A nice selection of exceptions is defined as well: .. exception:: SMTPRecipientsRefused - All recipient addresses refused. The errors for each recipient are accessible - through the attribute :attr:`recipients`, which is a dictionary of exactly the - same sort as :meth:`SMTP.sendmail` returns. + All recipient addresses refused. + + .. attribute:: recipients + + A dictionary of exactly the same sort as returned + by :meth:`SMTP.sendmail` containing the errors for + each recipient. .. exception:: SMTPDataError @@ -213,7 +236,6 @@ SMTP Objects An :class:`SMTP` instance has the following methods: - .. method:: SMTP.set_debuglevel(level) Set the debug output level. A value of 1 or ``True`` for *level* results in @@ -250,6 +272,9 @@ An :class:`SMTP` instance has the following methods: 2-tuple of the response code and message sent by the server in its connection response. + If port is not changed from its default value of 0, the value of the :attr:`default_port` + attribute is used. + .. audit-event:: smtplib.connect self,host,port smtplib.SMTP.connect @@ -327,7 +352,7 @@ An :class:`SMTP` instance has the following methods: :exc:`SMTPException` No suitable authentication method was found. - Each of the authentication methods supported by :mod:`smtplib` are tried in + Each of the authentication methods supported by :mod:`!smtplib` are tried in turn if they are advertised as supported by the server. See :meth:`auth` for a list of supported authentication methods. *initial_response_ok* is passed through to :meth:`auth`. @@ -379,7 +404,7 @@ An :class:`SMTP` instance has the following methods: call the :meth:`login` method, which will try each of the above mechanisms in turn, in the order listed. ``auth`` is exposed to facilitate the implementation of authentication methods not (or not yet) supported - directly by :mod:`smtplib`. + directly by :mod:`!smtplib`. .. versionadded:: 3.5 @@ -417,7 +442,7 @@ An :class:`SMTP` instance has the following methods: .. versionchanged:: 3.4 The method now supports hostname check with - :attr:`SSLContext.check_hostname` and *Server Name Indicator* (see + :attr:`ssl.SSLContext.check_hostname` and *Server Name Indicator* (see :const:`~ssl.HAS_SNI`). .. versionchanged:: 3.5 @@ -431,11 +456,13 @@ An :class:`SMTP` instance has the following methods: Send mail. The required arguments are an :rfc:`822` from-address string, a list of :rfc:`822` to-address strings (a bare string will be treated as a list with 1 address), and a message string. The caller may pass a list of ESMTP options - (such as ``8bitmime``) to be used in ``MAIL FROM`` commands as *mail_options*. + (such as ``"8bitmime"``) to be used in ``MAIL FROM`` commands as *mail_options*. ESMTP options (such as ``DSN`` commands) that should be used with all ``RCPT`` - commands can be passed as *rcpt_options*. (If you need to use different ESMTP + commands can be passed as *rcpt_options*. Each option should be passed as a string + containing the full text of the option, including any potential key + (for instance, ``"NOTIFY=SUCCESS,FAILURE"``). (If you need to use different ESMTP options to different recipients you have to use the low-level methods such as - :meth:`mail`, :meth:`rcpt` and :meth:`data` to send the message.) + :meth:`!mail`, :meth:`!rcpt` and :meth:`!data` to send the message.) .. note:: @@ -467,10 +494,7 @@ An :class:`SMTP` instance has the following methods: This method may raise the following exceptions: :exc:`SMTPRecipientsRefused` - All recipients were refused. Nobody got the mail. The :attr:`recipients` - attribute of the exception object is a dictionary with information about the - refused recipients (like the one returned when at least one recipient was - accepted). + All recipients were refused. Nobody got the mail. :exc:`SMTPHeloError` The server didn't reply properly to the ``HELO`` greeting. @@ -546,6 +570,30 @@ Low-level methods corresponding to the standard SMTP/ESMTP commands ``HELP``, Normally these do not need to be called directly, so they are not documented here. For details, consult the module code. +Additionally, an SMTP instance has the following attributes: + + +.. attribute:: SMTP.helo_resp + + The response to the ``HELO`` command, see :meth:`helo`. + + +.. attribute:: SMTP.ehlo_resp + + The response to the ``EHLO`` command, see :meth:`ehlo`. + + +.. attribute:: SMTP.does_esmtp + + A boolean value indicating whether the server supports ESMTP, see + :meth:`ehlo`. + + +.. attribute:: SMTP.esmtp_features + + A dictionary of the names of SMTP service extensions supported by the server, + see :meth:`ehlo`. + .. _smtp-example: diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index bc89a3228f0ed9..836aa91bb0885b 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -39,6 +39,8 @@ is implicit on send operations. A TLS/SSL wrapper for socket objects. +.. _socket-addresses: + Socket families --------------- @@ -83,7 +85,7 @@ created. Socket addresses are represented as follows: - For :const:`AF_INET6` address family, a four-tuple ``(host, port, flowinfo, scope_id)`` is used, where *flowinfo* and *scope_id* represent the ``sin6_flowinfo`` and ``sin6_scope_id`` members in :const:`struct sockaddr_in6` in C. For - :mod:`socket` module methods, *flowinfo* and *scope_id* can be omitted just for + :mod:`!socket` module methods, *flowinfo* and *scope_id* can be omitted just for backward compatibility. Note, however, omission of *scope_id* can cause problems in manipulating scoped IPv6 addresses. @@ -118,10 +120,10 @@ created. Socket addresses are represented as follows: ``'can0'``. The network interface name ``''`` can be used to receive packets from all network interfaces of this family. - - :const:`CAN_ISOTP` protocol require a tuple ``(interface, rx_addr, tx_addr)`` + - :const:`CAN_ISOTP` protocol requires a tuple ``(interface, rx_addr, tx_addr)`` where both additional parameters are unsigned long integer that represent a CAN identifier (standard or extended). - - :const:`CAN_J1939` protocol require a tuple ``(interface, name, pgn, addr)`` + - :const:`CAN_J1939` protocol requires a tuple ``(interface, name, pgn, addr)`` where additional parameters are 64-bit unsigned integer representing the ECU name, a 32-bit unsigned integer representing the Parameter Group Number (PGN), and an 8-bit integer representing the address. @@ -302,7 +304,7 @@ generalization of this based on timeouts is supported through Module contents --------------- -The module :mod:`socket` exports the following elements. +The module :mod:`!socket` exports the following elements. Exceptions @@ -482,6 +484,10 @@ The AF_* and SOCK_* constants are now :class:`AddressFamily` and .. versionchanged:: 3.14 Added support for ``TCP_QUICKACK`` on Windows platforms when available. + .. versionchanged:: 3.15 + ``IPV6_HDRINCL`` was added. + Added support for ``SO_PASSRIGHTS`` on Linux platforms when available. + .. data:: AF_CAN PF_CAN @@ -836,71 +842,14 @@ Creating sockets The following functions all create :ref:`socket objects `. -.. class:: socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) - - Create a new socket using the given address family, socket type and protocol - number. The address family should be :const:`AF_INET` (the default), - :const:`AF_INET6`, :const:`AF_UNIX`, :const:`AF_CAN`, :const:`AF_PACKET`, - or :const:`AF_RDS`. The socket type should be :const:`SOCK_STREAM` (the - default), :const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other - ``SOCK_`` constants. The protocol number is usually zero and may be omitted - or in the case where the address family is :const:`AF_CAN` the protocol - should be one of :const:`CAN_RAW`, :const:`CAN_BCM`, :const:`CAN_ISOTP` or - :const:`CAN_J1939`. - - If *fileno* is specified, the values for *family*, *type*, and *proto* are - auto-detected from the specified file descriptor. Auto-detection can be - overruled by calling the function with explicit *family*, *type*, or *proto* - arguments. This only affects how Python represents e.g. the return value - of :meth:`socket.getpeername` but not the actual OS resource. Unlike - :func:`socket.fromfd`, *fileno* will return the same socket and not a - duplicate. This may help close a detached socket using - :meth:`socket.close`. - - The newly created socket is :ref:`non-inheritable `. - - .. audit-event:: socket.__new__ self,family,type,protocol socket.socket - - .. versionchanged:: 3.3 - The AF_CAN family was added. - The AF_RDS family was added. - - .. versionchanged:: 3.4 - The CAN_BCM protocol was added. - - .. versionchanged:: 3.4 - The returned socket is now non-inheritable. - - .. versionchanged:: 3.7 - The CAN_ISOTP protocol was added. - - .. versionchanged:: 3.7 - When :const:`SOCK_NONBLOCK` or :const:`SOCK_CLOEXEC` - bit flags are applied to *type* they are cleared, and - :attr:`socket.type` will not reflect them. They are still passed - to the underlying system ``socket()`` call. Therefore, - - :: - - sock = socket.socket( - socket.AF_INET, - socket.SOCK_STREAM | socket.SOCK_NONBLOCK) - - will still create a non-blocking socket on OSes that support - ``SOCK_NONBLOCK``, but ``sock.type`` will be set to - ``socket.SOCK_STREAM``. - - .. versionchanged:: 3.9 - The CAN_J1939 protocol was added. - - .. versionchanged:: 3.10 - The IPPROTO_MPTCP protocol was added. +The :class:`socket ` class constructor creates a new socket +directly; see :ref:`socket-objects` for its parameters and full description. .. function:: socketpair([family[, type[, proto]]]) Build a pair of connected socket objects using the given address family, socket type, and protocol number. Address family, socket type, and protocol number are - as for the :func:`~socket.socket` function above. The default family is :const:`AF_UNIX` + as for the :func:`~socket.socket` function. The default family is :const:`AF_UNIX` if defined on the platform; otherwise, the default is :const:`AF_INET`. The newly created sockets are :ref:`non-inheritable `. @@ -996,8 +945,8 @@ The following functions all create :ref:`socket objects `. Duplicate the file descriptor *fd* (an integer as returned by a file object's :meth:`~io.IOBase.fileno` method) and build a socket object from the result. Address - family, socket type and protocol number are as for the :func:`~socket.socket` function - above. The file descriptor should refer to a socket, but this is not checked --- + family, socket type and protocol number are as for the :func:`~socket.socket` function. + The file descriptor should refer to a socket, but this is not checked --- subsequent operations on the object may fail if the file descriptor is invalid. This function is rarely needed, but can be used to get or set socket options on a socket passed to a program as standard input or output (such as a server @@ -1019,22 +968,16 @@ The following functions all create :ref:`socket objects `. .. versionadded:: 3.3 -.. data:: SocketType - - This is a Python type object that represents the socket object type. It is the - same as ``type(socket(...))``. - - Other functions ''''''''''''''' -The :mod:`socket` module also offers various network-related services: +The :mod:`!socket` module also offers various network-related services: .. function:: close(fd) Close a socket file descriptor. This is like :func:`os.close`, but for - sockets. On some platforms (most noticeable Windows) :func:`os.close` + sockets. On some platforms (most notably Windows) :func:`os.close` does not work for socket file descriptors. .. versionadded:: 3.7 @@ -1069,10 +1012,16 @@ The :mod:`socket` module also offers various network-related services: a string representing the canonical name of the *host* if :const:`AI_CANONNAME` is part of the *flags* argument; else *canonname* will be empty. *sockaddr* is a tuple describing a socket address, whose - format depends on the returned *family* (a ``(address, port)`` 2-tuple for - :const:`AF_INET`, a ``(address, port, flowinfo, scope_id)`` 4-tuple for - :const:`AF_INET6`), and is meant to be passed to the :meth:`socket.connect` - method. + format depends on the returned *family* and flags Python was compiled with, + and is meant to be passed to the :meth:`socket.connect` method. + + *sockaddr* can be one of the following: + + * a ``(address, port)`` 2-tuple for :const:`AF_INET` + * a ``(address, port, flowinfo, scope_id)`` 4-tuple for :const:`AF_INET6` if + Python was compiled with ``--enable-ipv6`` (the default) + * a 2-tuple containing raw data for :const:`AF_INET6` if Python was + compiled with ``--disable-ipv6`` .. note:: @@ -1407,11 +1356,14 @@ The :mod:`socket` module also offers various network-related services: .. function:: setdefaulttimeout(timeout) - Set the default timeout in seconds (float) for new socket objects. When + Set the default timeout in seconds (real number) for new socket objects. When the socket module is first imported, the default is ``None``. See :meth:`~socket.settimeout` for possible values and their respective meanings. + .. versionchanged:: 3.15 + Accepts any real number, not only integer or float. + .. function:: sethostname(name) @@ -1523,641 +1475,709 @@ The :mod:`socket` module also offers various network-related services: Socket Objects -------------- -Socket objects have the following methods. Except for -:meth:`~socket.makefile`, these correspond to Unix system calls applicable -to sockets. +.. class:: socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) -.. versionchanged:: 3.2 - Support for the :term:`context manager` protocol was added. Exiting the - context manager is equivalent to calling :meth:`~socket.close`. + Create a new socket using the given address family, socket type and protocol + number. The address family should be :const:`AF_INET` (the default), + :const:`AF_INET6`, :const:`AF_UNIX`, :const:`AF_CAN`, :const:`AF_PACKET`, + or :const:`AF_RDS`. The socket type should be :const:`SOCK_STREAM` (the + default), :const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other + ``SOCK_`` constants. The protocol number is usually zero and may be omitted + or in the case where the address family is :const:`AF_CAN` the protocol + should be one of :const:`CAN_RAW`, :const:`CAN_BCM`, :const:`CAN_ISOTP` or + :const:`CAN_J1939`. + If *fileno* is specified, the values for *family*, *type*, and *proto* are + auto-detected from the specified file descriptor. Auto-detection can be + overruled by calling the function with explicit *family*, *type*, or *proto* + arguments. This only affects how Python represents e.g. the return value + of :meth:`socket.getpeername` but not the actual OS resource. Unlike + :func:`socket.fromfd`, *fileno* will return the same socket and not a + duplicate. This may help close a detached socket using + :meth:`socket.close`. -.. method:: socket.accept() + The newly created socket is :ref:`non-inheritable `. - Accept a connection. The socket must be bound to an address and listening for - connections. The return value is a pair ``(conn, address)`` where *conn* is a - *new* socket object usable to send and receive data on the connection, and - *address* is the address bound to the socket on the other end of the connection. + .. audit-event:: socket.__new__ self,family,type,protocol socket.socket - The newly created socket is :ref:`non-inheritable `. + .. versionchanged:: 3.3 + The AF_CAN family was added. + The AF_RDS family was added. .. versionchanged:: 3.4 - The socket is now non-inheritable. + The CAN_BCM protocol was added. - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + .. versionchanged:: 3.4 + The returned socket is now non-inheritable. + .. versionchanged:: 3.7 + The CAN_ISOTP protocol was added. -.. method:: socket.bind(address) + .. versionchanged:: 3.7 + When :const:`SOCK_NONBLOCK` or :const:`SOCK_CLOEXEC` + bit flags are applied to *type* they are cleared, and + :attr:`socket.type` will not reflect them. They are still passed + to the underlying system ``socket()`` call. Therefore, - Bind the socket to *address*. The socket must not already be bound. (The format - of *address* depends on the address family --- see above.) + :: - .. audit-event:: socket.bind self,address socket.socket.bind + sock = socket.socket( + socket.AF_INET, + socket.SOCK_STREAM | socket.SOCK_NONBLOCK) - .. availability:: not WASI. + will still create a non-blocking socket on OSes that support + ``SOCK_NONBLOCK``, but ``sock.type`` will be set to + ``socket.SOCK_STREAM``. + .. versionchanged:: 3.9 + The CAN_J1939 protocol was added. -.. method:: socket.close() + .. versionchanged:: 3.10 + The IPPROTO_MPTCP protocol was added. - Mark the socket closed. The underlying system resource (e.g. a file - descriptor) is also closed when all file objects from :meth:`makefile` - are closed. Once that happens, all future operations on the socket - object will fail. The remote end will receive no more data (after - queued data is flushed). + Socket objects have the following methods. Except for + :meth:`~socket.makefile`, these correspond to Unix system calls applicable + to sockets. - Sockets are automatically closed when they are garbage-collected, but - it is recommended to :meth:`close` them explicitly, or to use a - :keyword:`with` statement around them. + .. versionchanged:: 3.2 + Support for the :term:`context manager` protocol was added. Exiting the + context manager is equivalent to calling :meth:`~socket.close`. - .. versionchanged:: 3.6 - :exc:`OSError` is now raised if an error occurs when the underlying - :c:func:`close` call is made. - .. note:: + .. method:: accept() - :meth:`close` releases the resource associated with a connection but - does not necessarily close the connection immediately. If you want - to close the connection in a timely fashion, call :meth:`shutdown` - before :meth:`close`. + Accept a connection. The socket must be bound to an address and listening for + connections. The return value is a pair ``(conn, address)`` where *conn* is a + *new* socket object usable to send and receive data on the connection, and + *address* is the address bound to the socket on the other end of the connection. + The newly created socket is :ref:`non-inheritable `. -.. method:: socket.connect(address) + .. versionchanged:: 3.4 + The socket is now non-inheritable. - Connect to a remote socket at *address*. (The format of *address* depends on the - address family --- see above.) + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - If the connection is interrupted by a signal, the method waits until the - connection completes, or raise a :exc:`TimeoutError` on timeout, if the - signal handler doesn't raise an exception and the socket is blocking or has - a timeout. For non-blocking sockets, the method raises an - :exc:`InterruptedError` exception if the connection is interrupted by a - signal (or the exception raised by the signal handler). - .. audit-event:: socket.connect self,address socket.socket.connect + .. method:: bind(address) - .. versionchanged:: 3.5 - The method now waits until the connection completes instead of raising an + Bind the socket to *address*. The socket must not already be bound. The format + of *address* depends on the address family --- see :ref:`socket-addresses`. + + .. audit-event:: socket.bind self,address socket.socket.bind + + .. availability:: not WASI. + + + .. method:: close() + + Mark the socket closed. The underlying system resource (e.g. a file + descriptor) is also closed when all file objects from :meth:`makefile` + are closed. Once that happens, all future operations on the socket + object will fail. The remote end will receive no more data (after + queued data is flushed). + + Sockets are automatically closed when they are garbage-collected, but + it is recommended to :meth:`close` them explicitly, or to use a + :keyword:`with` statement around them. + + .. versionchanged:: 3.6 + :exc:`OSError` is now raised if an error occurs when the underlying + :c:func:`!close` call is made. + + .. note:: + + :meth:`close` releases the resource associated with a connection but + does not necessarily close the connection immediately. If you want + to close the connection in a timely fashion, call :meth:`shutdown` + before :meth:`close`. + + + .. method:: connect(address) + + Connect to a remote socket at *address*. The format of *address* depends on the + address family --- see :ref:`socket-addresses`. + + If the connection is interrupted by a signal, the method waits until the + connection completes, or raises a :exc:`TimeoutError` on timeout, if the + signal handler doesn't raise an exception and the socket is blocking or has + a timeout. For non-blocking sockets, the method raises an :exc:`InterruptedError` exception if the connection is interrupted by a - signal, the signal handler doesn't raise an exception and the socket is - blocking or has a timeout (see the :pep:`475` for the rationale). + signal (or the exception raised by the signal handler). - .. availability:: not WASI. + .. audit-event:: socket.connect self,address socket.socket.connect + .. versionchanged:: 3.5 + The method now waits until the connection completes instead of raising an + :exc:`InterruptedError` exception if the connection is interrupted by a + signal, the signal handler doesn't raise an exception and the socket is + blocking or has a timeout (see the :pep:`475` for the rationale). -.. method:: socket.connect_ex(address) + .. availability:: not WASI. - Like ``connect(address)``, but return an error indicator instead of raising an - exception for errors returned by the C-level :c:func:`connect` call (other - problems, such as "host not found," can still raise exceptions). The error - indicator is ``0`` if the operation succeeded, otherwise the value of the - :c:data:`errno` variable. This is useful to support, for example, asynchronous - connects. - .. audit-event:: socket.connect self,address socket.socket.connect_ex + .. method:: connect_ex(address) - .. availability:: not WASI. + Like ``connect(address)``, but return an error indicator instead of raising an + exception for errors returned by the C-level :c:func:`!connect` call (other + problems, such as "host not found," can still raise exceptions). The error + indicator is ``0`` if the operation succeeded, otherwise the value of the + :c:data:`errno` variable. This is useful to support, for example, asynchronous + connects. -.. method:: socket.detach() + .. audit-event:: socket.connect self,address socket.socket.connect_ex - Put the socket object into closed state without actually closing the - underlying file descriptor. The file descriptor is returned, and can - be reused for other purposes. + .. availability:: not WASI. - .. versionadded:: 3.2 + .. method:: detach() + Put the socket object into closed state without actually closing the + underlying file descriptor. The file descriptor is returned, and can + be reused for other purposes. -.. method:: socket.dup() + .. versionadded:: 3.2 - Duplicate the socket. - The newly created socket is :ref:`non-inheritable `. + .. method:: dup() - .. versionchanged:: 3.4 - The socket is now non-inheritable. + Duplicate the socket. - .. availability:: not WASI. + The newly created socket is :ref:`non-inheritable `. + .. versionchanged:: 3.4 + The socket is now non-inheritable. -.. method:: socket.fileno() + .. availability:: not WASI. - Return the socket's file descriptor (a small integer), or -1 on failure. This - is useful with :func:`select.select`. - Under Windows the small integer returned by this method cannot be used where a - file descriptor can be used (such as :func:`os.fdopen`). Unix does not have - this limitation. + .. method:: fileno() -.. method:: socket.get_inheritable() + Return the socket's file descriptor (a small integer), or -1 on failure. This + is useful with :func:`select.select`. - Get the :ref:`inheritable flag ` of the socket's file - descriptor or socket's handle: ``True`` if the socket can be inherited in - child processes, ``False`` if it cannot. + Under Windows the small integer returned by this method cannot be used where a + file descriptor can be used (such as :func:`os.fdopen`). Unix does not have + this limitation. - .. versionadded:: 3.4 + .. method:: get_inheritable() + Get the :ref:`inheritable flag ` of the socket's file + descriptor or socket's handle: ``True`` if the socket can be inherited in + child processes, ``False`` if it cannot. -.. method:: socket.getpeername() + .. versionadded:: 3.4 - Return the remote address to which the socket is connected. This is useful to - find out the port number of a remote IPv4/v6 socket, for instance. (The format - of the address returned depends on the address family --- see above.) On some - systems this function is not supported. + .. method:: getpeername() -.. method:: socket.getsockname() + Return the remote address to which the socket is connected. This is useful to + find out the port number of a remote IPv4/v6 socket, for instance. The format + of the address returned depends on the address family --- see :ref:`socket-addresses`. + On some systems this function is not supported. - Return the socket's own address. This is useful to find out the port number of - an IPv4/v6 socket, for instance. (The format of the address returned depends on - the address family --- see above.) + .. method:: getsockname() -.. method:: socket.getsockopt(level, optname[, buflen]) + Return the socket's own address. This is useful to find out the port number of + an IPv4/v6 socket, for instance. The format of the address returned depends on + the address family --- see :ref:`socket-addresses`. - Return the value of the given socket option (see the Unix man page - :manpage:`getsockopt(2)`). The needed symbolic constants (:ref:`SO_\* etc. `) - are defined in this module. If *buflen* is absent, an integer option is assumed - and its integer value is returned by the function. If *buflen* is present, it - specifies the maximum length of the buffer used to receive the option in, and - this buffer is returned as a bytes object. It is up to the caller to decode the - contents of the buffer (see the optional built-in module :mod:`struct` for a way - to decode C structures encoded as byte strings). - .. availability:: not WASI. + .. method:: getsockopt(level, optname[, buflen]) + Return the value of the given socket option (see the Unix man page + :manpage:`getsockopt(2)`). The needed symbolic constants (:ref:`SO_\* etc. `) + are defined in this module. If *buflen* is absent, an integer option is assumed + and its integer value is returned by the function. If *buflen* is present, it + specifies the maximum length of the buffer used to receive the option in, and + this buffer is returned as a bytes object. It is up to the caller to decode the + contents of the buffer (see the optional built-in module :mod:`struct` for a way + to decode C structures encoded as byte strings). -.. method:: socket.getblocking() + .. availability:: not WASI. - Return ``True`` if socket is in blocking mode, ``False`` if in - non-blocking. - This is equivalent to checking ``socket.gettimeout() != 0``. + .. method:: getblocking() - .. versionadded:: 3.7 + Return ``True`` if socket is in blocking mode, ``False`` if in + non-blocking. + This is equivalent to checking ``socket.gettimeout() != 0``. -.. method:: socket.gettimeout() + .. versionadded:: 3.7 - Return the timeout in seconds (float) associated with socket operations, - or ``None`` if no timeout is set. This reflects the last call to - :meth:`setblocking` or :meth:`settimeout`. + .. method:: gettimeout() -.. method:: socket.ioctl(control, option) + Return the timeout in seconds (float) associated with socket operations, + or ``None`` if no timeout is set. This reflects the last call to + :meth:`setblocking` or :meth:`settimeout`. - The :meth:`ioctl` method is a limited interface to the WSAIoctl system - interface. Please refer to the `Win32 documentation - `_ for more - information. - On other platforms, the generic :func:`fcntl.fcntl` and :func:`fcntl.ioctl` - functions may be used; they accept a socket object as their first argument. + .. method:: ioctl(control, option) - Currently only the following control codes are supported: - ``SIO_RCVALL``, ``SIO_KEEPALIVE_VALS``, and ``SIO_LOOPBACK_FAST_PATH``. + The :meth:`ioctl` method is a limited interface to the WSAIoctl system + interface. Please refer to the `Win32 documentation + `_ for more + information. - .. availability:: Windows + On other platforms, the generic :func:`fcntl.fcntl` and :func:`fcntl.ioctl` + functions may be used; they accept a socket object as their first argument. - .. versionchanged:: 3.6 - ``SIO_LOOPBACK_FAST_PATH`` was added. + Currently only the following control codes are supported: + ``SIO_RCVALL``, ``SIO_KEEPALIVE_VALS``, and ``SIO_LOOPBACK_FAST_PATH``. + .. availability:: Windows -.. method:: socket.listen([backlog]) + .. versionchanged:: 3.6 + ``SIO_LOOPBACK_FAST_PATH`` was added. - Enable a server to accept connections. If *backlog* is specified, it must - be at least 0 (if it is lower, it is set to 0); it specifies the number of - unaccepted connections that the system will allow before refusing new - connections. If not specified, a default reasonable value is chosen. - .. availability:: not WASI. + .. method:: listen([backlog]) - .. versionchanged:: 3.5 - The *backlog* parameter is now optional. + Enable a server to accept connections. If *backlog* is specified, it must + be at least 0 (if it is lower, it is set to 0); it specifies the number of + unaccepted connections that the system will allow before refusing new + connections. If not specified, a default reasonable value is chosen. + .. availability:: not WASI. -.. method:: socket.makefile(mode='r', buffering=None, *, encoding=None, \ - errors=None, newline=None) + .. versionchanged:: 3.5 + The *backlog* parameter is now optional. - .. index:: single: I/O control; buffering - Return a :term:`file object` associated with the socket. The exact returned - type depends on the arguments given to :meth:`makefile`. These arguments are - interpreted the same way as by the built-in :func:`open` function, except - the only supported *mode* values are ``'r'`` (default), ``'w'``, ``'b'``, or - a combination of those. + .. method:: makefile(mode='r', buffering=None, *, encoding=None, \ + errors=None, newline=None) - The socket must be in blocking mode; it can have a timeout, but the file - object's internal buffer may end up in an inconsistent state if a timeout - occurs. + .. index:: single: I/O control; buffering - Closing the file object returned by :meth:`makefile` won't close the - original socket unless all other file objects have been closed and - :meth:`socket.close` has been called on the socket object. + Return a :term:`file object` associated with the socket. The exact returned + type depends on the arguments given to :meth:`makefile`. These arguments are + interpreted the same way as by the built-in :func:`open` function, except + the only supported *mode* values are ``'r'`` (default), ``'w'``, ``'b'``, or + a combination of those. - .. note:: + The socket must be in blocking mode; it can have a timeout, but the file + object's internal buffer may end up in an inconsistent state if a timeout + occurs. - On Windows, the file-like object created by :meth:`makefile` cannot be - used where a file object with a file descriptor is expected, such as the - stream arguments of :meth:`subprocess.Popen`. + Closing the file object returned by :meth:`makefile` won't close the + original socket unless all other file objects have been closed and + :meth:`socket.close` has been called on the socket object. + .. note:: -.. method:: socket.recv(bufsize[, flags]) + On Windows, the file-like object created by :meth:`makefile` cannot be + used where a file object with a file descriptor is expected, such as the + stream arguments of :meth:`subprocess.Popen`. - Receive data from the socket. The return value is a bytes object representing the - data received. The maximum amount of data to be received at once is specified - by *bufsize*. A returned empty bytes object indicates that the client has disconnected. - See the Unix manual page :manpage:`recv(2)` for the meaning of the optional argument - *flags*; it defaults to zero. - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + .. method:: recv(bufsize[, flags]) + Receive data from the socket. The return value is a bytes object representing the + data received. The maximum amount of data to be received at once is specified + by *bufsize*. A returned empty bytes object indicates that the client has disconnected. + See the Unix manual page :manpage:`recv(2)` for the meaning of the optional argument + *flags*; it defaults to zero. -.. method:: socket.recvfrom(bufsize[, flags]) + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - Receive data from the socket. The return value is a pair ``(bytes, address)`` - where *bytes* is a bytes object representing the data received and *address* is the - address of the socket sending the data. See the Unix manual page - :manpage:`recv(2)` for the meaning of the optional argument *flags*; it defaults - to zero. (The format of *address* depends on the address family --- see above.) - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + .. method:: recvfrom(bufsize[, flags]) - .. versionchanged:: 3.7 - For multicast IPv6 address, first item of *address* does not contain - ``%scope_id`` part anymore. In order to get full IPv6 address use - :func:`getnameinfo`. - -.. method:: socket.recvmsg(bufsize[, ancbufsize[, flags]]) - - Receive normal data (up to *bufsize* bytes) and ancillary data from - the socket. The *ancbufsize* argument sets the size in bytes of - the internal buffer used to receive the ancillary data; it defaults - to 0, meaning that no ancillary data will be received. Appropriate - buffer sizes for ancillary data can be calculated using - :func:`CMSG_SPACE` or :func:`CMSG_LEN`, and items which do not fit - into the buffer might be truncated or discarded. The *flags* - argument defaults to 0 and has the same meaning as for - :meth:`recv`. - - The return value is a 4-tuple: ``(data, ancdata, msg_flags, - address)``. The *data* item is a :class:`bytes` object holding the - non-ancillary data received. The *ancdata* item is a list of zero - or more tuples ``(cmsg_level, cmsg_type, cmsg_data)`` representing - the ancillary data (control messages) received: *cmsg_level* and - *cmsg_type* are integers specifying the protocol level and - protocol-specific type respectively, and *cmsg_data* is a - :class:`bytes` object holding the associated data. The *msg_flags* - item is the bitwise OR of various flags indicating conditions on - the received message; see your system documentation for details. - If the receiving socket is unconnected, *address* is the address of - the sending socket, if available; otherwise, its value is - unspecified. - - On some systems, :meth:`sendmsg` and :meth:`recvmsg` can be used to - pass file descriptors between processes over an :const:`AF_UNIX` - socket. When this facility is used (it is often restricted to - :const:`SOCK_STREAM` sockets), :meth:`recvmsg` will return, in its - ancillary data, items of the form ``(socket.SOL_SOCKET, - socket.SCM_RIGHTS, fds)``, where *fds* is a :class:`bytes` object - representing the new file descriptors as a binary array of the - native C :c:expr:`int` type. If :meth:`recvmsg` raises an - exception after the system call returns, it will first attempt to - close any file descriptors received via this mechanism. - - Some systems do not indicate the truncated length of ancillary data - items which have been only partially received. If an item appears - to extend beyond the end of the buffer, :meth:`recvmsg` will issue - a :exc:`RuntimeWarning`, and will return the part of it which is - inside the buffer provided it has not been truncated before the - start of its associated data. - - On systems which support the :const:`SCM_RIGHTS` mechanism, the - following function will receive up to *maxfds* file descriptors, - returning the message data and a list containing the descriptors - (while ignoring unexpected conditions such as unrelated control - messages being received). See also :meth:`sendmsg`. :: - - import socket, array - - def recv_fds(sock, msglen, maxfds): - fds = array.array("i") # Array of ints - msg, ancdata, flags, addr = sock.recvmsg(msglen, socket.CMSG_LEN(maxfds * fds.itemsize)) - for cmsg_level, cmsg_type, cmsg_data in ancdata: - if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS: - # Append data, ignoring any truncated integers at the end. - fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) - return msg, list(fds) - - .. availability:: Unix. + Receive data from the socket. The return value is a pair ``(bytes, address)`` + where *bytes* is a bytes object representing the data received and *address* is the + address of the socket sending the data. See the Unix manual page + :manpage:`recv(2)` for the meaning of the optional argument *flags*; it defaults + to zero. The format of *address* depends on the address family --- see + :ref:`socket-addresses`. - Most Unix platforms. + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - .. versionadded:: 3.3 + .. versionchanged:: 3.7 + For multicast IPv6 address, first item of *address* does not contain + ``%scope_id`` part anymore. In order to get full IPv6 address use + :func:`getnameinfo`. - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - - -.. method:: socket.recvmsg_into(buffers[, ancbufsize[, flags]]) - - Receive normal data and ancillary data from the socket, behaving as - :meth:`recvmsg` would, but scatter the non-ancillary data into a - series of buffers instead of returning a new bytes object. The - *buffers* argument must be an iterable of objects that export - writable buffers (e.g. :class:`bytearray` objects); these will be - filled with successive chunks of the non-ancillary data until it - has all been written or there are no more buffers. The operating - system may set a limit (:func:`~os.sysconf` value ``SC_IOV_MAX``) - on the number of buffers that can be used. The *ancbufsize* and - *flags* arguments have the same meaning as for :meth:`recvmsg`. - - The return value is a 4-tuple: ``(nbytes, ancdata, msg_flags, - address)``, where *nbytes* is the total number of bytes of - non-ancillary data written into the buffers, and *ancdata*, - *msg_flags* and *address* are the same as for :meth:`recvmsg`. - - Example:: - - >>> import socket - >>> s1, s2 = socket.socketpair() - >>> b1 = bytearray(b'----') - >>> b2 = bytearray(b'0123456789') - >>> b3 = bytearray(b'--------------') - >>> s1.send(b'Mary had a little lamb') - 22 - >>> s2.recvmsg_into([b1, memoryview(b2)[2:9], b3]) - (22, [], 0, None) - >>> [b1, b2, b3] - [bytearray(b'Mary'), bytearray(b'01 had a 9'), bytearray(b'little lamb---')] - - .. availability:: Unix. + .. method:: recvmsg(bufsize[, ancbufsize[, flags]]) + + Receive normal data (up to *bufsize* bytes) and ancillary data from + the socket. The *ancbufsize* argument sets the size in bytes of + the internal buffer used to receive the ancillary data; it defaults + to 0, meaning that no ancillary data will be received. Appropriate + buffer sizes for ancillary data can be calculated using + :func:`CMSG_SPACE` or :func:`CMSG_LEN`, and items which do not fit + into the buffer might be truncated or discarded. The *flags* + argument defaults to 0 and has the same meaning as for + :meth:`recv`. + + The return value is a 4-tuple: ``(data, ancdata, msg_flags, + address)``. The *data* item is a :class:`bytes` object holding the + non-ancillary data received. The *ancdata* item is a list of zero + or more tuples ``(cmsg_level, cmsg_type, cmsg_data)`` representing + the ancillary data (control messages) received: *cmsg_level* and + *cmsg_type* are integers specifying the protocol level and + protocol-specific type respectively, and *cmsg_data* is a + :class:`bytes` object holding the associated data. The *msg_flags* + item is the bitwise OR of various flags indicating conditions on + the received message; see your system documentation for details. + If the receiving socket is unconnected, *address* is the address of + the sending socket, if available; otherwise, its value is + unspecified. + + On some systems, :meth:`sendmsg` and :meth:`recvmsg` can be used to + pass file descriptors between processes over an :const:`AF_UNIX` + socket. When this facility is used (it is often restricted to + :const:`SOCK_STREAM` sockets), :meth:`recvmsg` will return, in its + ancillary data, items of the form ``(socket.SOL_SOCKET, + socket.SCM_RIGHTS, fds)``, where *fds* is a :class:`bytes` object + representing the new file descriptors as a binary array of the + native C :c:expr:`int` type. If :meth:`recvmsg` raises an + exception after the system call returns, it will first attempt to + close any file descriptors received via this mechanism. + + Some systems do not indicate the truncated length of ancillary data + items which have been only partially received. If an item appears + to extend beyond the end of the buffer, :meth:`recvmsg` will issue + a :exc:`RuntimeWarning`, and will return the part of it which is + inside the buffer provided it has not been truncated before the + start of its associated data. + + On systems which support the :const:`!SCM_RIGHTS` mechanism, the + following function will receive up to *maxfds* file descriptors, + returning the message data and a list containing the descriptors + (while ignoring unexpected conditions such as unrelated control + messages being received). See also :meth:`sendmsg`. :: + + import socket, array + + def recv_fds(sock, msglen, maxfds): + fds = array.array("i") # Array of ints + msg, ancdata, flags, addr = sock.recvmsg(msglen, socket.CMSG_LEN(maxfds * fds.itemsize)) + for cmsg_level, cmsg_type, cmsg_data in ancdata: + if cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS: + # Append data, ignoring any truncated integers at the end. + fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) + return msg, list(fds) + + .. availability:: Unix. + + Most Unix platforms. + + .. versionadded:: 3.3 + + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + + + .. method:: recvmsg_into(buffers[, ancbufsize[, flags]]) + + Receive normal data and ancillary data from the socket, behaving as + :meth:`recvmsg` would, but scatter the non-ancillary data into a + series of buffers instead of returning a new bytes object. The + *buffers* argument must be an iterable of objects that export + writable buffers (e.g. :class:`bytearray` objects); these will be + filled with successive chunks of the non-ancillary data until it + has all been written or there are no more buffers. The operating + system may set a limit (:func:`~os.sysconf` value ``SC_IOV_MAX``) + on the number of buffers that can be used. The *ancbufsize* and + *flags* arguments have the same meaning as for :meth:`recvmsg`. + + The return value is a 4-tuple: ``(nbytes, ancdata, msg_flags, + address)``, where *nbytes* is the total number of bytes of + non-ancillary data written into the buffers, and *ancdata*, + *msg_flags* and *address* are the same as for :meth:`recvmsg`. + + Example:: + + >>> import socket + >>> s1, s2 = socket.socketpair() + >>> b1 = bytearray(b'----') + >>> b2 = bytearray(b'0123456789') + >>> b3 = bytearray(b'--------------') + >>> s1.send(b'Mary had a little lamb') + 22 + >>> s2.recvmsg_into([b1, memoryview(b2)[2:9], b3]) + (22, [], 0, None) + >>> [b1, b2, b3] + [bytearray(b'Mary'), bytearray(b'01 had a 9'), bytearray(b'little lamb---')] + + .. availability:: Unix. + + Most Unix platforms. + + .. versionadded:: 3.3 + + + .. method:: recvfrom_into(buffer[, nbytes[, flags]]) + + Receive data from the socket, writing it into *buffer* instead of creating a + new bytestring. The return value is a pair ``(nbytes, address)`` where *nbytes* is + the number of bytes received and *address* is the address of the socket sending + the data. See the Unix manual page :manpage:`recv(2)` for the meaning of the + optional argument *flags*; it defaults to zero. The format of *address* + depends on the address family --- see :ref:`socket-addresses`. + + + .. method:: recv_into(buffer[, nbytes[, flags]]) + + Receive up to *nbytes* bytes from the socket, storing the data into a buffer + rather than creating a new bytestring. If *nbytes* is not specified (or 0), + receive up to the size available in the given buffer. Returns the number of + bytes received. See the Unix manual page :manpage:`recv(2)` for the meaning + of the optional argument *flags*; it defaults to zero. - Most Unix platforms. - .. versionadded:: 3.3 + .. method:: send(bytes[, flags]) + Send data to the socket. The socket must be connected to a remote socket. The + optional *flags* argument has the same meaning as for :meth:`recv`. + Returns the number of bytes sent. Applications are responsible for checking that + all data has been sent; if only some of the data was transmitted, the + application needs to attempt delivery of the remaining data. For further + information on this topic, consult the :ref:`socket-howto`. -.. method:: socket.recvfrom_into(buffer[, nbytes[, flags]]) + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - Receive data from the socket, writing it into *buffer* instead of creating a - new bytestring. The return value is a pair ``(nbytes, address)`` where *nbytes* is - the number of bytes received and *address* is the address of the socket sending - the data. See the Unix manual page :manpage:`recv(2)` for the meaning of the - optional argument *flags*; it defaults to zero. (The format of *address* - depends on the address family --- see above.) + .. method:: sendall(bytes[, flags]) -.. method:: socket.recv_into(buffer[, nbytes[, flags]]) + Send data to the socket. The socket must be connected to a remote socket. The + optional *flags* argument has the same meaning as for :meth:`recv`. + Unlike :meth:`send`, this method continues to send data from *bytes* until + either all data has been sent or an error occurs. ``None`` is returned on + success. On error, an exception is raised, and there is no way to determine how + much data, if any, was successfully sent. - Receive up to *nbytes* bytes from the socket, storing the data into a buffer - rather than creating a new bytestring. If *nbytes* is not specified (or 0), - receive up to the size available in the given buffer. Returns the number of - bytes received. See the Unix manual page :manpage:`recv(2)` for the meaning - of the optional argument *flags*; it defaults to zero. + .. versionchanged:: 3.5 + The socket timeout is no longer reset each time data is sent successfully. + The socket timeout is now the maximum total duration to send all data. + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). -.. method:: socket.send(bytes[, flags]) - Send data to the socket. The socket must be connected to a remote socket. The - optional *flags* argument has the same meaning as for :meth:`recv` above. - Returns the number of bytes sent. Applications are responsible for checking that - all data has been sent; if only some of the data was transmitted, the - application needs to attempt delivery of the remaining data. For further - information on this topic, consult the :ref:`socket-howto`. + .. method:: sendto(bytes, address) + sendto(bytes, flags, address) - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + Send data to the socket. The socket should not be connected to a remote socket, + since the destination socket is specified by *address*. The optional *flags* + argument has the same meaning as for :meth:`recv`. Return the number of + bytes sent. The format of *address* depends on the address family --- see + :ref:`socket-addresses`. + .. audit-event:: socket.sendto self,address socket.socket.sendto -.. method:: socket.sendall(bytes[, flags]) + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - Send data to the socket. The socket must be connected to a remote socket. The - optional *flags* argument has the same meaning as for :meth:`recv` above. - Unlike :meth:`send`, this method continues to send data from *bytes* until - either all data has been sent or an error occurs. ``None`` is returned on - success. On error, an exception is raised, and there is no way to determine how - much data, if any, was successfully sent. - .. versionchanged:: 3.5 - The socket timeout is no longer reset each time data is sent successfully. - The socket timeout is now the maximum total duration to send all data. + .. method:: sendmsg(buffers[, ancdata[, flags[, address]]]) - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + Send normal and ancillary data to the socket, gathering the + non-ancillary data from a series of buffers and concatenating it + into a single message. The *buffers* argument specifies the + non-ancillary data as an iterable of + :term:`bytes-like objects ` + (e.g. :class:`bytes` objects); the operating system may set a limit + (:func:`~os.sysconf` value ``SC_IOV_MAX``) on the number of buffers + that can be used. The *ancdata* argument specifies the ancillary + data (control messages) as an iterable of zero or more tuples + ``(cmsg_level, cmsg_type, cmsg_data)``, where *cmsg_level* and + *cmsg_type* are integers specifying the protocol level and + protocol-specific type respectively, and *cmsg_data* is a + bytes-like object holding the associated data. Note that + some systems (in particular, systems without :func:`CMSG_SPACE`) + might support sending only one control message per call. The + *flags* argument defaults to 0 and has the same meaning as for + :meth:`send`. If *address* is supplied and not ``None``, it sets a + destination address for the message. The return value is the + number of bytes of non-ancillary data sent. + The following function sends the list of file descriptors *fds* + over an :const:`AF_UNIX` socket, on systems which support the + :const:`!SCM_RIGHTS` mechanism. See also :meth:`recvmsg`. :: -.. method:: socket.sendto(bytes, address) - socket.sendto(bytes, flags, address) + import socket, array - Send data to the socket. The socket should not be connected to a remote socket, - since the destination socket is specified by *address*. The optional *flags* - argument has the same meaning as for :meth:`recv` above. Return the number of - bytes sent. (The format of *address* depends on the address family --- see - above.) + def send_fds(sock, msg, fds): + return sock.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", fds))]) - .. audit-event:: socket.sendto self,address socket.socket.sendto + .. availability:: Unix, not WASI. - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - - -.. method:: socket.sendmsg(buffers[, ancdata[, flags[, address]]]) - - Send normal and ancillary data to the socket, gathering the - non-ancillary data from a series of buffers and concatenating it - into a single message. The *buffers* argument specifies the - non-ancillary data as an iterable of - :term:`bytes-like objects ` - (e.g. :class:`bytes` objects); the operating system may set a limit - (:func:`~os.sysconf` value ``SC_IOV_MAX``) on the number of buffers - that can be used. The *ancdata* argument specifies the ancillary - data (control messages) as an iterable of zero or more tuples - ``(cmsg_level, cmsg_type, cmsg_data)``, where *cmsg_level* and - *cmsg_type* are integers specifying the protocol level and - protocol-specific type respectively, and *cmsg_data* is a - bytes-like object holding the associated data. Note that - some systems (in particular, systems without :func:`CMSG_SPACE`) - might support sending only one control message per call. The - *flags* argument defaults to 0 and has the same meaning as for - :meth:`send`. If *address* is supplied and not ``None``, it sets a - destination address for the message. The return value is the - number of bytes of non-ancillary data sent. - - The following function sends the list of file descriptors *fds* - over an :const:`AF_UNIX` socket, on systems which support the - :const:`SCM_RIGHTS` mechanism. See also :meth:`recvmsg`. :: - - import socket, array - - def send_fds(sock, msg, fds): - return sock.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", fds))]) + Most Unix platforms. - .. availability:: Unix, not WASI. + .. audit-event:: socket.sendmsg self,address socket.socket.sendmsg - Most Unix platforms. + .. versionadded:: 3.3 - .. audit-event:: socket.sendmsg self,address socket.socket.sendmsg + .. versionchanged:: 3.5 + If the system call is interrupted and the signal handler does not raise + an exception, the method now retries the system call instead of raising + an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). - .. versionadded:: 3.3 + .. method:: sendmsg_afalg([msg], *, op[, iv[, assoclen[, flags]]]) - .. versionchanged:: 3.5 - If the system call is interrupted and the signal handler does not raise - an exception, the method now retries the system call instead of raising - an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). + Specialized version of :meth:`~socket.sendmsg` for :const:`AF_ALG` socket. + Set mode, IV, AEAD associated data length and flags for :const:`AF_ALG` socket. -.. method:: socket.sendmsg_afalg([msg], *, op[, iv[, assoclen[, flags]]]) + .. availability:: Linux >= 2.6.38. - Specialized version of :meth:`~socket.sendmsg` for :const:`AF_ALG` socket. - Set mode, IV, AEAD associated data length and flags for :const:`AF_ALG` socket. + .. versionadded:: 3.6 - .. availability:: Linux >= 2.6.38. + .. method:: sendfile(file, offset=0, count=None) - .. versionadded:: 3.6 + Send a file until EOF is reached by using high-performance + :mod:`os.sendfile` and return the total number of bytes which were sent. + *file* must be a regular file object opened in binary mode. If + :mod:`os.sendfile` is not available (e.g. Windows) or *file* is not a + regular file :meth:`send` will be used instead. *offset* tells from where to + start reading the file. If specified, *count* is the total number of bytes + to transmit as opposed to sending the file until EOF is reached. File + position is updated on return or also in case of error in which case + :meth:`file.tell() ` can be used to figure out the number of + bytes which were sent. The socket must be of :const:`SOCK_STREAM` type. + Non-blocking sockets are not supported. -.. method:: socket.sendfile(file, offset=0, count=None) + .. versionadded:: 3.5 - Send a file until EOF is reached by using high-performance - :mod:`os.sendfile` and return the total number of bytes which were sent. - *file* must be a regular file object opened in binary mode. If - :mod:`os.sendfile` is not available (e.g. Windows) or *file* is not a - regular file :meth:`send` will be used instead. *offset* tells from where to - start reading the file. If specified, *count* is the total number of bytes - to transmit as opposed to sending the file until EOF is reached. File - position is updated on return or also in case of error in which case - :meth:`file.tell() ` can be used to figure out the number of - bytes which were sent. The socket must be of :const:`SOCK_STREAM` type. - Non-blocking sockets are not supported. + .. method:: set_inheritable(inheritable) - .. versionadded:: 3.5 + Set the :ref:`inheritable flag ` of the socket's file + descriptor or socket's handle. -.. method:: socket.set_inheritable(inheritable) + .. versionadded:: 3.4 - Set the :ref:`inheritable flag ` of the socket's file - descriptor or socket's handle. - .. versionadded:: 3.4 + .. method:: setblocking(flag) + Set blocking or non-blocking mode of the socket: if *flag* is false, the + socket is set to non-blocking, else to blocking mode. -.. method:: socket.setblocking(flag) + This method is a shorthand for certain :meth:`~socket.settimeout` calls: - Set blocking or non-blocking mode of the socket: if *flag* is false, the - socket is set to non-blocking, else to blocking mode. + * ``sock.setblocking(True)`` is equivalent to ``sock.settimeout(None)`` - This method is a shorthand for certain :meth:`~socket.settimeout` calls: + * ``sock.setblocking(False)`` is equivalent to ``sock.settimeout(0.0)`` - * ``sock.setblocking(True)`` is equivalent to ``sock.settimeout(None)`` + .. versionchanged:: 3.7 + The method no longer applies :const:`SOCK_NONBLOCK` flag on + :attr:`socket.type`. - * ``sock.setblocking(False)`` is equivalent to ``sock.settimeout(0.0)`` - .. versionchanged:: 3.7 - The method no longer applies :const:`SOCK_NONBLOCK` flag on - :attr:`socket.type`. + .. method:: settimeout(value) + Set a timeout on blocking socket operations. The *value* argument can be a + nonnegative real number expressing seconds, or ``None``. + If a non-zero value is given, subsequent socket operations will raise a + :exc:`timeout` exception if the timeout period *value* has elapsed before + the operation has completed. If zero is given, the socket is put in + non-blocking mode. If ``None`` is given, the socket is put in blocking mode. -.. method:: socket.settimeout(value) + For further information, please consult the :ref:`notes on socket timeouts `. - Set a timeout on blocking socket operations. The *value* argument can be a - nonnegative floating-point number expressing seconds, or ``None``. - If a non-zero value is given, subsequent socket operations will raise a - :exc:`timeout` exception if the timeout period *value* has elapsed before - the operation has completed. If zero is given, the socket is put in - non-blocking mode. If ``None`` is given, the socket is put in blocking mode. + .. versionchanged:: 3.7 + The method no longer toggles :const:`SOCK_NONBLOCK` flag on + :attr:`socket.type`. - For further information, please consult the :ref:`notes on socket timeouts `. + .. versionchanged:: 3.15 + Accepts any real number, not only integer or float. - .. versionchanged:: 3.7 - The method no longer toggles :const:`SOCK_NONBLOCK` flag on - :attr:`socket.type`. + .. method:: setsockopt(level, optname, value: int | Buffer) + setsockopt(level, optname, None, optlen: int) -.. method:: socket.setsockopt(level, optname, value: int) -.. method:: socket.setsockopt(level, optname, value: buffer) - :noindex: -.. method:: socket.setsockopt(level, optname, None, optlen: int) - :noindex: + .. index:: pair: module; struct - .. index:: pair: module; struct + Set the value of the given socket option (see the Unix manual page + :manpage:`setsockopt(2)`). The needed symbolic constants are defined in this + module (:ref:`!SO_\* etc. `). The value can be an integer, + ``None`` or a :term:`bytes-like object` representing a buffer. In the latter + case it is up to the caller to ensure that the bytestring contains the + proper bits (see the optional built-in module :mod:`struct` for a way to + encode C structures as bytestrings). When *value* is set to ``None``, + *optlen* argument is required. It's equivalent to calling :c:func:`!setsockopt` C + function with ``optval=NULL`` and ``optlen=optlen``. - Set the value of the given socket option (see the Unix manual page - :manpage:`setsockopt(2)`). The needed symbolic constants are defined in this - module (:ref:`!SO_\* etc. `). The value can be an integer, - ``None`` or a :term:`bytes-like object` representing a buffer. In the later - case it is up to the caller to ensure that the bytestring contains the - proper bits (see the optional built-in module :mod:`struct` for a way to - encode C structures as bytestrings). When *value* is set to ``None``, - *optlen* argument is required. It's equivalent to call :c:func:`setsockopt` C - function with ``optval=NULL`` and ``optlen=optlen``. + .. versionchanged:: 3.5 + Writable :term:`bytes-like object` is now accepted. - .. versionchanged:: 3.5 - Writable :term:`bytes-like object` is now accepted. + .. versionchanged:: 3.6 + setsockopt(level, optname, None, optlen: int) form added. - .. versionchanged:: 3.6 - setsockopt(level, optname, None, optlen: int) form added. + .. availability:: not WASI. - .. availability:: not WASI. + .. method:: shutdown(how) -.. method:: socket.shutdown(how) + Shut down one or both halves of the connection. If *how* is :const:`SHUT_RD`, + further receives are disallowed. If *how* is :const:`SHUT_WR`, further sends + are disallowed. If *how* is :const:`SHUT_RDWR`, further sends and receives are + disallowed. - Shut down one or both halves of the connection. If *how* is :const:`SHUT_RD`, - further receives are disallowed. If *how* is :const:`SHUT_WR`, further sends - are disallowed. If *how* is :const:`SHUT_RDWR`, further sends and receives are - disallowed. + .. availability:: not WASI. - .. availability:: not WASI. + .. method:: share(process_id) -.. method:: socket.share(process_id) + Duplicate a socket and prepare it for sharing with a target process. The + target process must be provided with *process_id*. The resulting bytes object + can then be passed to the target process using some form of interprocess + communication and the socket can be recreated there using :func:`fromshare`. + Once this method has been called, it is safe to close the socket since + the operating system has already duplicated it for the target process. - Duplicate a socket and prepare it for sharing with a target process. The - target process must be provided with *process_id*. The resulting bytes object - can then be passed to the target process using some form of interprocess - communication and the socket can be recreated there using :func:`fromshare`. - Once this method has been called, it is safe to close the socket since - the operating system has already duplicated it for the target process. + .. availability:: Windows. - .. availability:: Windows. + .. versionadded:: 3.3 - .. versionadded:: 3.3 + Note that there are no methods :meth:`!read` or :meth:`!write`; use + :meth:`~socket.recv` and :meth:`~socket.send` without *flags* argument instead. + + Socket objects also have these (read-only) attributes that correspond to the + values given to the :class:`~socket.socket` constructor. -Note that there are no methods :meth:`read` or :meth:`write`; use -:meth:`~socket.recv` and :meth:`~socket.send` without *flags* argument instead. -Socket objects also have these (read-only) attributes that correspond to the -values given to the :class:`~socket.socket` constructor. + .. attribute:: family + The socket family. -.. attribute:: socket.family - The socket family. + .. attribute:: type + The socket type. -.. attribute:: socket.type - The socket type. + .. attribute:: proto + The socket protocol. -.. attribute:: socket.proto - The socket protocol. +.. class:: SocketType + The base class of the :class:`~socket.socket` type, re-exported from + :mod:`!_socket`. An instance check such as + ``isinstance(socket(...), SocketType)`` is true, but ``SocketType`` is not + the same as ``type(socket(...))``, which is :class:`~socket.socket` itself. .. _socket-timeouts: @@ -2415,7 +2435,7 @@ lead to this error:: This is because the previous execution has left the socket in a ``TIME_WAIT`` state, and can't be immediately reused. -There is a :mod:`socket` flag to set, in order to prevent this, +There is a :mod:`!socket` flag to set, in order to prevent this, :const:`socket.SO_REUSEADDR`:: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst index 7fb629f7d2f256..4c98bb8e3b9c9b 100644 --- a/Doc/library/socketserver.rst +++ b/Doc/library/socketserver.rst @@ -8,7 +8,7 @@ -------------- -The :mod:`socketserver` module simplifies the task of writing network servers. +The :mod:`!socketserver` module simplifies the task of writing network servers. .. include:: ../includes/wasm-notavail.rst @@ -24,7 +24,7 @@ There are four basic concrete server classes: :meth:`~BaseServer.server_activate`. The other parameters are passed to the :class:`BaseServer` base class. - .. versionchanged:: next + .. versionchanged:: 3.15 The default queue size is now ``socket.SOMAXCONN`` for :class:`socketserver.TCPServer`. .. class:: UDPServer(server_address, RequestHandlerClass, bind_and_activate=True) @@ -546,7 +546,7 @@ The difference is that the ``readline()`` call in the second handler will call first handler had to use a ``recv()`` loop to accumulate data until a newline itself. If it had just used a single ``recv()`` without the loop it would just have returned what has been received so far from the client. -TCP is stream based: data arrives in the order it was sent, but there no +TCP is stream based: data arrives in the order it was sent, but there is no correlation between client ``send()`` or ``sendall()`` calls and the number of ``recv()`` calls on the server required to receive it. diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index a14af6d3d88df2..3a75d44f3f7d21 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -4,8 +4,6 @@ .. module:: sqlite3 :synopsis: A DB-API 2.0 implementation using SQLite 3.x. -.. sectionauthor:: Gerhard Häring - **Source code:** :source:`Lib/sqlite3/` .. Make sure we always doctest the tutorial with an empty database. @@ -31,7 +29,9 @@ PostgreSQL or Oracle. The :mod:`!sqlite3` module was written by Gerhard Häring. It provides an SQL interface compliant with the DB-API 2.0 specification described by :pep:`249`, and -requires SQLite 3.15.2 or newer. +requires the third-party `SQLite `_ library. + +.. include:: ../includes/optional-module.rst This document includes four main sections: @@ -55,7 +55,7 @@ This document includes four main sections: PEP written by Marc-André Lemburg. -.. We use the following practises for SQL code: +.. We use the following practices for SQL code: - UPPERCASE for keywords - snake_case for schema - single quotes for string literals @@ -289,7 +289,7 @@ Module functions Set it to any combination (using ``|``, bitwise or) of :const:`PARSE_DECLTYPES` and :const:`PARSE_COLNAMES` to enable this. - Column names takes precedence over declared types if both flags are set. + Column names take precedence over declared types if both flags are set. By default (``0``), type detection is disabled. :param isolation_level: @@ -509,12 +509,12 @@ Module constants .. data:: SQLITE_KEYWORDS - A :class:`tuple` containing all sqlite3 keywords. + A :class:`tuple` containing all SQLite keywords. This constant is only available if Python was compiled with SQLite 3.24.0 or greater. - .. versionadded:: next + .. versionadded:: 3.15 .. data:: threadsafety @@ -620,7 +620,7 @@ Connection objects supplied, this must be a :term:`callable` returning an instance of :class:`Cursor` or its subclasses. - .. method:: blobopen(table, column, row, /, *, readonly=False, name="main") + .. method:: blobopen(table, column, rowid, /, *, readonly=False, name="main") Open a :class:`Blob` handle to an existing :abbr:`BLOB (Binary Large OBject)`. @@ -631,8 +631,8 @@ Connection objects :param str column: The name of the column where the blob is located. - :param str row: - The name of the row where the blob is located. + :param int rowid: + The row id where the blob is located. :param bool readonly: Set to ``True`` if the blob should be opened without write @@ -1417,6 +1417,9 @@ Connection objects See :ref:`sqlite3-howto-row-factory` for more details. + .. versionchanged:: next + Deleting the ``row_factory`` attribute is no longer allowed. + .. attribute:: text_factory A :term:`callable` that accepts a :class:`bytes` parameter @@ -1426,6 +1429,9 @@ Connection objects See :ref:`sqlite3-howto-encoding` for more details. + .. versionchanged:: next + Deleting the ``text_factory`` attribute is no longer allowed. + .. attribute:: total_changes Return the total number of database rows that have been modified, inserted, or @@ -1611,6 +1617,9 @@ Cursor objects If the *size* parameter is used, then it is best for it to retain the same value from one :meth:`fetchmany` call to the next. + .. versionchanged:: 3.15 + Negative *size* values are rejected by raising :exc:`ValueError`. + .. method:: fetchall() Return all (remaining) rows of a query result as a :class:`list`. @@ -1638,6 +1647,9 @@ Cursor objects Read/write attribute that controls the number of rows returned by :meth:`fetchmany`. The default value is 1 which means a single row would be fetched per call. + .. versionchanged:: 3.15 + Negative values are rejected by raising :exc:`ValueError`. + .. attribute:: connection Read-only attribute that provides the SQLite database :class:`Connection` @@ -1703,6 +1715,9 @@ Cursor objects See :ref:`sqlite3-howto-row-factory` for more details. + .. versionchanged:: next + Deleting the ``row_factory`` attribute is no longer allowed. + .. The sqlite3.Row example used to be a how-to. It has now been incorporated into the Row reference. We keep the anchor here in order not to break @@ -2279,7 +2294,7 @@ This section shows recipes for common adapters and converters. .. testcode:: - import datetime + import datetime as dt import sqlite3 def adapt_date_iso(val): @@ -2294,21 +2309,21 @@ This section shows recipes for common adapters and converters. """Adapt datetime.datetime to Unix timestamp.""" return int(val.timestamp()) - sqlite3.register_adapter(datetime.date, adapt_date_iso) - sqlite3.register_adapter(datetime.datetime, adapt_datetime_iso) - sqlite3.register_adapter(datetime.datetime, adapt_datetime_epoch) + sqlite3.register_adapter(dt.date, adapt_date_iso) + sqlite3.register_adapter(dt.datetime, adapt_datetime_iso) + sqlite3.register_adapter(dt.datetime, adapt_datetime_epoch) def convert_date(val): """Convert ISO 8601 date to datetime.date object.""" - return datetime.date.fromisoformat(val.decode()) + return dt.date.fromisoformat(val.decode()) def convert_datetime(val): """Convert ISO 8601 datetime to datetime.datetime object.""" - return datetime.datetime.fromisoformat(val.decode()) + return dt.datetime.fromisoformat(val.decode()) def convert_timestamp(val): """Convert Unix epoch timestamp to datetime.datetime object.""" - return datetime.datetime.fromtimestamp(int(val)) + return dt.datetime.fromtimestamp(int(val)) sqlite3.register_converter("date", convert_date) sqlite3.register_converter("datetime", convert_datetime) @@ -2317,17 +2332,17 @@ This section shows recipes for common adapters and converters. .. testcode:: :hide: - dt = datetime.datetime(2019, 5, 18, 15, 17, 8, 123456) + when = dt.datetime(2019, 5, 18, 15, 17, 8, 123456) - assert adapt_date_iso(dt.date()) == "2019-05-18" - assert convert_date(b"2019-05-18") == dt.date() + assert adapt_date_iso(when.date()) == "2019-05-18" + assert convert_date(b"2019-05-18") == when.date() - assert adapt_datetime_iso(dt) == "2019-05-18T15:17:08.123456" - assert convert_datetime(b"2019-05-18T15:17:08.123456") == dt + assert adapt_datetime_iso(when) == "2019-05-18T15:17:08.123456" + assert convert_datetime(b"2019-05-18T15:17:08.123456") == when # Using current time as fromtimestamp() returns local date/time. # Dropping microseconds as adapt_datetime_epoch truncates fractional second part. - now = datetime.datetime.now().replace(microsecond=0) + now = dt.datetime.now().replace(microsecond=0) current_timestamp = int(now.timestamp()) assert adapt_datetime_epoch(now) == current_timestamp diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index a9930183f9a400..66fe6c7aee4862 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -4,9 +4,6 @@ .. module:: ssl :synopsis: TLS/SSL wrapper for socket objects -.. moduleauthor:: Bill Janssen -.. sectionauthor:: Bill Janssen - **Source code:** :source:`Lib/ssl.py` .. index:: single: OpenSSL; (use in module ssl) @@ -18,8 +15,9 @@ This module provides access to Transport Layer Security (often known as "Secure Sockets Layer") encryption and peer authentication facilities for network sockets, both client-side and server-side. This module uses the OpenSSL -library. It is available on all modern Unix systems, Windows, macOS, and -probably additional platforms, as long as OpenSSL is installed on that platform. +library. + +.. include:: ../includes/optional-module.rst .. note:: @@ -69,7 +67,7 @@ by SSL sockets created through the :meth:`SSLContext.wrap_socket` method. Use of deprecated constants and functions result in deprecation warnings. -Functions, Constants, and Exceptions +Functions, constants, and exceptions ------------------------------------ @@ -125,10 +123,11 @@ Context creation A convenience function helps create :class:`SSLContext` objects for common purposes. -.. function:: create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None, capath=None, cadata=None) +.. function:: create_default_context(purpose=Purpose.SERVER_AUTH, *,\ + cafile=None, capath=None, cadata=None) Return a new :class:`SSLContext` object with default settings for - the given *purpose*. The settings are chosen by the :mod:`ssl` module, + the given *purpose*. The settings are chosen by the :mod:`!ssl` module, and usually represent a higher security level than when calling the :class:`SSLContext` constructor directly. @@ -147,9 +146,9 @@ purposes. *cadata* is given) or uses :meth:`SSLContext.load_default_certs` to load default CA certificates. - When :attr:`~SSLContext.keylog_filename` is supported and the environment - variable :envvar:`SSLKEYLOGFILE` is set, :func:`create_default_context` - enables key logging. + When the environment variable :envvar:`!SSLKEYLOGFILE` is set, + :func:`create_default_context` enables key logging by setting + :attr:`~SSLContext.keylog_filename` to the variable's value. The default settings for this context include :data:`VERIFY_X509_PARTIAL_CHAIN` and :data:`VERIFY_X509_STRICT`. @@ -215,6 +214,25 @@ purposes. :data:`VERIFY_X509_STRICT` in its default verify flags. +Signature algorithms +^^^^^^^^^^^^^^^^^^^^ + +.. function:: get_sigalgs() + + Return a list of available TLS signature algorithm names used + by servers to complete the TLS handshake or clients requesting + certificate-based authentication. For example:: + + >>> ssl.get_sigalgs() # doctest: +SKIP + ['ecdsa_secp256r1_sha256', 'ecdsa_secp384r1_sha384', ...] + + These names can be used when building string values to pass to the + :meth:`SSLContext.set_client_sigalgs` and + :meth:`SSLContext.set_server_sigalgs` methods. + + .. versionadded:: 3.15 + + Exceptions ^^^^^^^^^^ @@ -314,7 +332,7 @@ Exceptions Random generation ^^^^^^^^^^^^^^^^^ -.. function:: RAND_bytes(num) +.. function:: RAND_bytes(num, /) Return *num* cryptographically strong pseudo-random bytes. Raises an :class:`SSLError` if the PRNG has not been seeded with enough data or if the @@ -334,11 +352,10 @@ Random generation .. function:: RAND_status() Return ``True`` if the SSL pseudo-random number generator has been seeded - with 'enough' randomness, and ``False`` otherwise. You can use - :func:`ssl.RAND_egd` and :func:`ssl.RAND_add` to increase the randomness of - the pseudo-random number generator. + with 'enough' randomness, and ``False`` otherwise. Use :func:`ssl.RAND_add` + to increase the randomness of the pseudo-random number generator. -.. function:: RAND_add(bytes, entropy) +.. function:: RAND_add(bytes, entropy, /) Mix the given *bytes* into the SSL pseudo-random number generator. The parameter *entropy* (a float) is a lower bound on the entropy contained in @@ -357,7 +374,7 @@ Certificate handling .. function:: cert_time_to_seconds(cert_time) - Return the time in seconds since the Epoch, given the ``cert_time`` + Return the time in seconds since the epoch, given the ``cert_time`` string representing the "notBefore" or "notAfter" date from a certificate in ``"%b %d %H:%M:%S %Y %Z"`` strptime format (C locale). @@ -367,12 +384,12 @@ Certificate handling .. doctest:: newcontext >>> import ssl + >>> import datetime as dt >>> timestamp = ssl.cert_time_to_seconds("Jan 5 09:34:43 2018 GMT") >>> timestamp # doctest: +SKIP 1515144883 - >>> from datetime import datetime - >>> print(datetime.utcfromtimestamp(timestamp)) # doctest: +SKIP - 2018-01-05 09:34:43 + >>> print(dt.datetime.fromtimestamp(timestamp, dt.UTC)) # doctest: +SKIP + 2018-01-05 09:34:43+00:00 "notBefore" or "notAfter" dates must use GMT (:rfc:`5280`). @@ -406,12 +423,12 @@ Certificate handling .. versionchanged:: 3.10 The *timeout* parameter was added. -.. function:: DER_cert_to_PEM_cert(DER_cert_bytes) +.. function:: DER_cert_to_PEM_cert(der_cert_bytes) Given a certificate as a DER-encoded blob of bytes, returns a PEM-encoded string version of the same certificate. -.. function:: PEM_cert_to_DER_cert(PEM_cert_string) +.. function:: PEM_cert_to_DER_cert(pem_cert_string) Given a certificate as an ASCII PEM string, returns a DER-encoded sequence of bytes for that same certificate. @@ -939,7 +956,7 @@ Constants Whether the OpenSSL library has built-in support for External PSKs in TLS 1.3 as described in :rfc:`9258`. - .. versionadded:: next + .. versionadded:: 3.15 .. data:: HAS_PHA @@ -1055,7 +1072,7 @@ Constants :attr:`TLSVersion.TLSv1_3` are deprecated. -SSL Sockets +SSL sockets ----------- .. class:: SSLSocket(socket.socket) @@ -1104,7 +1121,7 @@ SSL Sockets :meth:`SSLContext.wrap_socket` to wrap a socket. .. versionchanged:: 3.7 - :class:`SSLSocket` instances must to created with + :class:`SSLSocket` instances must be created with :meth:`~SSLContext.wrap_socket`. In earlier versions, it was possible to create instances directly. This was never documented or officially supported. @@ -1114,7 +1131,7 @@ SSL Sockets functions support reading and writing of data larger than 2 GB. Writing zero-length data no longer fails with a protocol violation error. - .. versionchanged:: next + .. versionchanged:: 3.15 Python now uses ``SSL_sendfile`` internally when possible. The function sends a file more efficiently because it performs TLS encryption in the kernel to avoid additional context switches. @@ -1141,10 +1158,10 @@ SSL sockets also have the following additional methods and attributes: .. deprecated:: 3.6 Use :meth:`~SSLSocket.recv` instead of :meth:`~SSLSocket.read`. -.. method:: SSLSocket.write(buf) +.. method:: SSLSocket.write(data) - Write *buf* to the SSL socket and return the number of bytes written. The - *buf* argument must be an object supporting the buffer interface. + Write *data* to the SSL socket and return the number of bytes written. The + *data* argument must be an object supporting the buffer interface. Raise :exc:`SSLWantReadError` or :exc:`SSLWantWriteError` if the socket is :ref:`non-blocking ` and the write would block. @@ -1154,7 +1171,7 @@ SSL sockets also have the following additional methods and attributes: .. versionchanged:: 3.5 The socket timeout is no longer reset each time bytes are received or sent. - The socket timeout is now the maximum total duration to write *buf*. + The socket timeout is now the maximum total duration to write *data*. .. deprecated:: 3.6 Use :meth:`~SSLSocket.send` instead of :meth:`~SSLSocket.write`. @@ -1171,12 +1188,15 @@ SSL sockets also have the following additional methods and attributes: :meth:`~socket.socket.recv` and :meth:`~socket.socket.send` instead of these methods. -.. method:: SSLSocket.do_handshake() +.. method:: SSLSocket.do_handshake(block=False) Perform the SSL setup handshake. + If *block* is true and the timeout obtained by :meth:`~socket.socket.gettimeout` + is zero, the socket is set in blocking mode until the handshake is performed. + .. versionchanged:: 3.4 - The handshake method also performs :func:`match_hostname` when the + The handshake method also performs :func:`!match_hostname` when the :attr:`~SSLContext.check_hostname` attribute of the socket's :attr:`~SSLSocket.context` is true. @@ -1186,7 +1206,7 @@ SSL sockets also have the following additional methods and attributes: .. versionchanged:: 3.7 Hostname or IP address is matched by OpenSSL during handshake. The - function :func:`match_hostname` is no longer used. In case OpenSSL + function :func:`!match_hostname` is no longer used. In case OpenSSL refuses a hostname or IP address, the handshake is aborted early and a TLS alert message is sent to the peer. @@ -1290,6 +1310,29 @@ SSL sockets also have the following additional methods and attributes: .. versionadded:: 3.5 +.. method:: SSLSocket.group() + + Return the group used for doing key agreement on this connection. If no + connection has been established, returns ``None``. + + .. versionadded:: 3.15 + +.. method:: SSLSocket.client_sigalg() + + Return the signature algorithm used for performing certificate-based client + authentication on this connection, or ``None`` if no connection has been + established or client authentication didn't occur. + + .. versionadded:: 3.15 + +.. method:: SSLSocket.server_sigalg() + + Return the signature algorithm used by the server to complete the TLS + handshake on this connection, or ``None`` if no connection has been + established or the cipher suite has no signature. + + .. versionadded:: 3.15 + .. method:: SSLSocket.compression() Return the compression algorithm being used as a string, or ``None`` @@ -1419,7 +1462,7 @@ SSL sockets also have the following additional methods and attributes: .. versionadded:: 3.6 -SSL Contexts +SSL contexts ------------ .. versionadded:: 3.2 @@ -1464,7 +1507,7 @@ to speed up repeated connections from the same clients. TLS 1.3. .. seealso:: - :func:`create_default_context` lets the :mod:`ssl` module choose + :func:`create_default_context` lets the :mod:`!ssl` module choose security settings for a given purpose. .. versionchanged:: 3.6 @@ -1647,6 +1690,25 @@ to speed up repeated connections from the same clients. .. versionadded:: 3.6 +.. method:: SSLContext.get_groups(*, include_aliases=False) + + Get a list of groups implemented for key agreement, taking into + account the current TLS :attr:`~SSLContext.minimum_version` and + :attr:`~SSLContext.maximum_version` values. For example:: + + >>> ctx = ssl.create_default_context() + >>> ctx.minimum_version = ssl.TLSVersion.TLSv1_3 + >>> ctx.maximum_version = ssl.TLSVersion.TLSv1_3 + >>> ctx.get_groups() # doctest: +SKIP + ['secp256r1', 'secp384r1', 'secp521r1', 'x25519', 'x448', ...] + + By default, this method returns only the preferred IANA names for the + available groups. However, if the ``include_aliases`` parameter is set to + :const:`True` this method will also return any associated aliases such as + the ECDH curve names supported in older versions of OpenSSL. + + .. versionadded:: 3.15 + .. method:: SSLContext.set_default_verify_paths() Load a set of default "certification authority" (CA) certificates from @@ -1656,23 +1718,79 @@ to speed up repeated connections from the same clients. provided as part of the operating system, though, it is likely to be configured properly. -.. method:: SSLContext.set_ciphers(ciphers) +.. method:: SSLContext.set_ciphers(ciphers, /) - Set the available ciphers for sockets created with this context. - It should be a string in the `OpenSSL cipher list format + Set the allowed ciphers for sockets created with this context when + connecting using TLS 1.2 and earlier. The *ciphers* argument should + be a string in the `OpenSSL cipher list format `_. + To set allowed TLS 1.3 ciphers, use :meth:`SSLContext.set_ciphersuites`. + If no cipher can be selected (because compile-time options or other configuration forbids use of all the specified ciphers), an :class:`SSLError` will be raised. .. note:: - when connected, the :meth:`SSLSocket.cipher` method of SSL sockets will - give the currently selected cipher. + When connected, the :meth:`SSLSocket.cipher` method of SSL sockets will + return details about the negotiated cipher. + +.. method:: SSLContext.set_ciphersuites(ciphersuites, /) + + Set the allowed ciphers for sockets created with this context when + connecting using TLS 1.3. The *ciphersuites* argument should be a + colon-separate string of TLS 1.3 cipher names. If no cipher can be + selected (because compile-time options or other configuration forbids + use of all the specified ciphers), an :class:`SSLError` will be raised. + + .. note:: + When connected, the :meth:`SSLSocket.cipher` method of SSL sockets will + return details about the negotiated cipher. + + .. versionadded:: 3.15 + +.. method:: SSLContext.set_groups(groups, /) + + Set the groups allowed for key agreement for sockets created with this + context. It should be a string in the `OpenSSL group list format + `_. + + .. note:: + + When connected, the :meth:`SSLSocket.group` method of SSL sockets will + return the group used for key agreement on that connection. + + .. versionadded:: 3.15 + +.. method:: SSLContext.set_client_sigalgs(sigalgs, /) + + Set the signature algorithms allowed for certificate-based client + authentication. It should be a string in the `OpenSSL client sigalgs + list format + `_. + + .. note:: + + When connected, the :meth:`SSLSocket.client_sigalg` method of SSL + sockets will return the signature algorithm used for performing + certificate-based client authentication on that connection. + + .. versionadded:: 3.15 + +.. method:: SSLContext.set_server_sigalgs(sigalgs, /) + + Set the signature algorithms allowed for the server to complete the TLS + handshake. It should be a string in the `OpenSSL sigalgs list format + `_. + + .. note:: + + When connected, the :meth:`SSLSocket.server_sigalg` method of SSL + sockets will return the signature algorithm used by the server to + complete the TLS handshake on that connection. - TLS 1.3 cipher suites cannot be disabled with - :meth:`~SSLContext.set_ciphers`. + .. versionadded:: 3.15 -.. method:: SSLContext.set_alpn_protocols(protocols) +.. method:: SSLContext.set_alpn_protocols(alpn_protocols) Specify which protocols the socket should advertise during the SSL/TLS handshake. It should be a list of ASCII strings, like ``['http/1.1', @@ -1686,7 +1804,7 @@ to speed up repeated connections from the same clients. .. versionadded:: 3.5 -.. method:: SSLContext.set_npn_protocols(protocols) +.. method:: SSLContext.set_npn_protocols(npn_protocols) Specify which protocols the socket should advertise during the SSL/TLS handshake. It should be a list of strings, like ``['http/1.1', 'spdy/2']``, @@ -1753,7 +1871,7 @@ to speed up repeated connections from the same clients. .. versionadded:: 3.7 -.. attribute:: SSLContext.set_servername_callback(server_name_callback) +.. method:: SSLContext.set_servername_callback(server_name_callback) This is a legacy API retained for backwards compatibility. When possible, you should use :attr:`sni_callback` instead. The given *server_name_callback* @@ -1767,7 +1885,7 @@ to speed up repeated connections from the same clients. .. versionadded:: 3.4 -.. method:: SSLContext.load_dh_params(dhfile) +.. method:: SSLContext.load_dh_params(dhfile, /) Load the key generation parameters for Diffie-Hellman (DH) key exchange. Using DH key exchange improves forward secrecy at the expense of @@ -1780,7 +1898,7 @@ to speed up repeated connections from the same clients. .. versionadded:: 3.3 -.. method:: SSLContext.set_ecdh_curve(curve_name) +.. method:: SSLContext.set_ecdh_curve(curve_name, /) Set the curve name for Elliptic Curve-based Diffie-Hellman (ECDH) key exchange. ECDH is significantly faster than regular DH while arguably @@ -1859,8 +1977,9 @@ to speed up repeated connections from the same clients. .. attribute:: SSLContext.sslsocket_class The return type of :meth:`SSLContext.wrap_socket`, defaults to - :class:`SSLSocket`. The attribute can be overridden on instance of class - in order to return a custom subclass of :class:`SSLSocket`. + :class:`SSLSocket`. The attribute can be assigned to on instances of + :class:`SSLContext` in order to return a custom subclass of + :class:`SSLSocket`. .. versionadded:: 3.7 @@ -1957,7 +2076,7 @@ to speed up repeated connections from the same clients. :attr:`~SSLContext.minimum_version` and :attr:`SSLContext.options` all affect the supported SSL and TLS versions of the context. The implementation does not prevent - invalid combination. For example a context with + invalid combinations. For example a context with :attr:`OP_NO_TLSv1_2` in :attr:`~SSLContext.options` and :attr:`~SSLContext.maximum_version` set to :attr:`TLSVersion.TLSv1_2` will not be able to establish a TLS 1.2 connection. @@ -2534,7 +2653,7 @@ thus several things you need to be aware of: as well. -Memory BIO Support +Memory BIO support ------------------ .. versionadded:: 3.5 @@ -2653,12 +2772,12 @@ purpose. It wraps an OpenSSL memory BIO (Basic IO) object: A boolean indicating whether the memory BIO is current at the end-of-file position. - .. method:: MemoryBIO.read(n=-1) + .. method:: MemoryBIO.read(n=-1, /) Read up to *n* bytes from the memory buffer. If *n* is not specified or negative, all bytes are returned. - .. method:: MemoryBIO.write(buf) + .. method:: MemoryBIO.write(buf, /) Write the bytes from *buf* to the memory BIO. The *buf* argument must be an object supporting the buffer protocol. @@ -2741,7 +2860,7 @@ This common check is automatically performed when .. versionchanged:: 3.7 Hostname matchings is now performed by OpenSSL. Python no longer uses - :func:`match_hostname`. + :func:`!match_hostname`. In server mode, if you want to authenticate your clients using the SSL layer (rather than using a higher-level authentication mechanism), you'll also have @@ -2760,11 +2879,11 @@ disabled by default. :: >>> client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - >>> client_context.minimum_version = ssl.TLSVersion.TLSv1_3 + >>> client_context.minimum_version = ssl.TLSVersion.TLSv1_2 >>> client_context.maximum_version = ssl.TLSVersion.TLSv1_3 -The SSL context created above will only allow TLSv1.3 and later (if +The SSL client context created above will only allow TLSv1.2 and TLSv1.3 (if supported by your system) connections to a server. :const:`PROTOCOL_TLS_CLIENT` implies certificate validation and hostname checks by default. You have to load certificates into the context. @@ -2805,10 +2924,15 @@ TLS 1.3 The TLS 1.3 protocol behaves slightly differently than previous version of TLS/SSL. Some new TLS 1.3 features are not yet available. -- TLS 1.3 uses a disjunct set of cipher suites. All AES-GCM and - ChaCha20 cipher suites are enabled by default. The method - :meth:`SSLContext.set_ciphers` cannot enable or disable any TLS 1.3 - ciphers yet, but :meth:`SSLContext.get_ciphers` returns them. +- TLS 1.3 uses a disjunct set of cipher suites. All AES-GCM and ChaCha20 + cipher suites are enabled by default. To restrict which TLS 1.3 ciphers + are allowed, the :meth:`SSLContext.set_ciphersuites` method should be + called instead of :meth:`SSLContext.set_ciphers`, which only affects + ciphers in older TLS versions. The :meth:`SSLContext.get_ciphers` method + returns information about ciphers for both TLS 1.3 and earlier versions + and the method :meth:`SSLSocket.cipher` returns information about the + negotiated cipher for both TLS 1.3 and earlier versions once a connection + is established. - Session tickets are no longer sent as part of the initial handshake and are handled differently. :attr:`SSLSocket.session` and :class:`SSLSession` are not compatible with TLS 1.3. @@ -2817,7 +2941,7 @@ of TLS/SSL. Some new TLS 1.3 features are not yet available. process certificate requests while they send or receive application data from the server. - TLS 1.3 features like early data, deferred TLS client cert request, - signature algorithm configuration, and rekeying are not supported yet. + and rekeying are not supported yet. .. seealso:: @@ -2832,16 +2956,16 @@ of TLS/SSL. Some new TLS 1.3 features are not yet available. Steve Kent :rfc:`RFC 4086: Randomness Requirements for Security <4086>` - Donald E., Jeffrey I. Schiller + Donald E. Eastlake, Jeffrey I. Schiller, Steve Crocker :rfc:`RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile <5280>` - D. Cooper + David Cooper et al. :rfc:`RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 <5246>` - T. Dierks et. al. + Tim Dierks and Eric Rescorla. :rfc:`RFC 6066: Transport Layer Security (TLS) Extensions <6066>` - D. Eastlake + Donald E. Eastlake `IANA TLS: Transport Layer Security (TLS) Parameters `_ IANA diff --git a/Doc/library/stat.rst b/Doc/library/stat.rst index 8434b2e8c75cf4..5c5f1858ba4476 100644 --- a/Doc/library/stat.rst +++ b/Doc/library/stat.rst @@ -5,13 +5,11 @@ :synopsis: Utilities for interpreting the results of os.stat(), os.lstat() and os.fstat(). -.. sectionauthor:: Skip Montanaro - **Source code:** :source:`Lib/stat.py` -------------- -The :mod:`stat` module defines constants and functions for interpreting the +The :mod:`!stat` module defines constants and functions for interpreting the results of :func:`os.stat`, :func:`os.fstat` and :func:`os.lstat` (if they exist). For complete details about the :c:func:`stat`, :c:func:`!fstat` and :c:func:`!lstat` calls, consult the documentation for your system. @@ -19,7 +17,7 @@ exist). For complete details about the :c:func:`stat`, :c:func:`!fstat` and .. versionchanged:: 3.4 The stat module is backed by a C implementation. -The :mod:`stat` module defines the following functions to test for specific file +The :mod:`!stat` module defines the following functions to test for specific file types: @@ -493,3 +491,22 @@ constants, but are not an exhaustive list. IO_REPARSE_TAG_APPEXECLINK .. versionadded:: 3.8 + +On Linux, the following file attribute constants are available for use when +testing bits in the :attr:`~os.statx_result.stx_attributes` and +:attr:`~os.statx_result.stx_attributes_mask` members returned by +:func:`os.statx`. See the :manpage:`statx(2)` man page for more detail on the +meaning of these constants. + +.. data:: STATX_ATTR_COMPRESSED + STATX_ATTR_IMMUTABLE + STATX_ATTR_APPEND + STATX_ATTR_NODUMP + STATX_ATTR_ENCRYPTED + STATX_ATTR_AUTOMOUNT + STATX_ATTR_MOUNT_ROOT + STATX_ATTR_VERITY + STATX_ATTR_DAX + STATX_ATTR_WRITE_ATOMIC + + .. versionadded:: 3.15 diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst index 614f5b905a4a2e..dba0e26787d951 100644 --- a/Doc/library/statistics.rst +++ b/Doc/library/statistics.rst @@ -4,9 +4,6 @@ .. module:: statistics :synopsis: Mathematical statistics functions -.. moduleauthor:: Steven D'Aprano -.. sectionauthor:: Steven D'Aprano - .. versionadded:: 3.4 **Source code:** :source:`Lib/statistics.py` @@ -716,7 +713,7 @@ However, for reading convenience, most of the examples show sorted sequences. .. function:: covariance(x, y, /) - Return the sample covariance of two inputs *x* and *y*. Covariance + Return the sample covariance of two sequence inputs *x* and *y*. Covariance is a measure of the joint variability of two inputs. Both inputs must be of the same length (no less than two), otherwise @@ -742,7 +739,7 @@ However, for reading convenience, most of the examples show sorted sequences. Return the `Pearson's correlation coefficient `_ - for two inputs. Pearson's correlation coefficient *r* takes values + for two sequence inputs. Pearson's correlation coefficient *r* takes values between -1 and +1. It measures the strength and direction of a linear relationship. @@ -805,7 +802,7 @@ However, for reading convenience, most of the examples show sorted sequences. (it is equal to the difference between predicted and actual values of the dependent variable). - Both inputs must be of the same length (no less than two), and + Both inputs must be sequences of the same length (no less than two), and the independent variable *x* cannot be constant; otherwise a :exc:`StatisticsError` is raised. diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 90683c0b00d78a..a47e1ffb1a6afb 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -46,8 +46,10 @@ Any object can be tested for truth value, for use in an :keyword:`if` or By default, an object is considered true unless its class defines either a :meth:`~object.__bool__` method that returns ``False`` or a :meth:`~object.__len__` method that -returns zero, when called with the object. [1]_ Here are most of the built-in -objects considered false: +returns zero, when called with the object. [1]_ If one of the methods raises an +exception when called, the exception is propagated and the object does +not have a truth value (for example, :data:`NotImplemented`). +Here are most of the built-in objects considered false: .. index:: single: None (Built-in object) @@ -164,7 +166,7 @@ This table summarizes the comparison operations: pair: object; numeric pair: objects; comparing -Objects of different types, except different numeric types, never compare equal. +Unless stated otherwise, objects of different types never compare equal. The ``==`` operator is always defined but for some object types (for example, class objects) is equivalent to :keyword:`is`. The ``<``, ``<=``, ``>`` and ``>=`` operators are only defined where they make sense; for example, they raise a @@ -263,9 +265,17 @@ The constructors :func:`int`, :func:`float`, and pair: operator; % (percent) pair: operator; ** +.. _stdtypes-mixed-arithmetic: + Python fully supports mixed arithmetic: when a binary arithmetic operator has -operands of different numeric types, the operand with the "narrower" type is -widened to that of the other, where integer is narrower than floating point. +operands of different built-in numeric types, the operand with the "narrower" +type is widened to that of the other: + +* If both arguments are complex numbers, no conversion is performed; +* if either argument is a complex or a floating-point number, the other is + converted to a floating-point number; +* otherwise, both must be integers and no conversion is necessary. + Arithmetic with complex and real operands is defined by the usual mathematical formula, for example:: @@ -1000,8 +1010,6 @@ operations have the same priority as the corresponding numeric operations. [3]_ pair: slice; operation pair: operator; in pair: operator; not in - single: count() (sequence method) - single: index() (sequence method) +--------------------------+--------------------------------+----------+ | Operation | Result | Notes | @@ -1018,7 +1026,7 @@ operations have the same priority as the corresponding numeric operations. [3]_ | ``s * n`` or | equivalent to adding *s* to | (2)(7) | | ``n * s`` | itself *n* times | | +--------------------------+--------------------------------+----------+ -| ``s[i]`` | *i*\ th item of *s*, origin 0 | (3)(9) | +| ``s[i]`` | *i*\ th item of *s*, origin 0 | (3)(8) | +--------------------------+--------------------------------+----------+ | ``s[i:j]`` | slice of *s* from *i* to *j* | (3)(4) | +--------------------------+--------------------------------+----------+ @@ -1031,13 +1039,6 @@ operations have the same priority as the corresponding numeric operations. [3]_ +--------------------------+--------------------------------+----------+ | ``max(s)`` | largest item of *s* | | +--------------------------+--------------------------------+----------+ -| ``s.index(x[, i[, j]])`` | index of the first occurrence | \(8) | -| | of *x* in *s* (at or after | | -| | index *i* and before index *j*)| | -+--------------------------+--------------------------------+----------+ -| ``s.count(x)`` | total number of occurrences of | | -| | *x* in *s* | | -+--------------------------+--------------------------------+----------+ Sequences of the same type also support comparisons. In particular, tuples and lists are compared lexicographically by comparing corresponding elements. @@ -1100,11 +1101,14 @@ Notes: still ``0``. (4) - The slice of *s* from *i* to *j* is defined as the sequence of items with index - *k* such that ``i <= k < j``. If *i* or *j* is greater than ``len(s)``, use - ``len(s)``. If *i* is omitted or ``None``, use ``0``. If *j* is omitted or - ``None``, use ``len(s)``. If *i* is greater than or equal to *j*, the slice is - empty. + The slice of *s* from *i* to *j* is defined as the sequence of items with + index *k* such that ``i <= k < j``. + + * If *i* is omitted or ``None``, use ``0``. + * If *j* is omitted or ``None``, use ``len(s)``. + * If *i* or *j* is less than ``-len(s)``, use ``0``. + * If *i* or *j* is greater than ``len(s)``, use ``len(s)``. + * If *i* is greater than or equal to *j*, the slice is empty. (5) The slice of *s* from *i* to *j* with step *k* is defined as the sequence of @@ -1143,16 +1147,42 @@ Notes: concatenation or repetition. (8) - ``index`` raises :exc:`ValueError` when *x* is not found in *s*. - Not all implementations support passing the additional arguments *i* and *j*. - These arguments allow efficient searching of subsections of the sequence. Passing - the extra arguments is roughly equivalent to using ``s[i:j].index(x)``, only - without copying any data and with the returned index being relative to - the start of the sequence rather than the start of the slice. - -(9) An :exc:`IndexError` is raised if *i* is outside the sequence range. +.. rubric:: Sequence Methods + +Sequence types also support the following methods: + +.. method:: list.count(value, /) + range.count(value, /) + tuple.count(value, /) + :no-contents-entry: + :no-index-entry: + :no-typesetting: +.. method:: sequence.count(value, /) + + Return the total number of occurrences of *value* in *sequence*. + +.. method:: list.index(value[, start[, stop]]) + range.index(value[, start[, stop]]) + tuple.index(value[, start[, stop]]) + :no-contents-entry: + :no-index-entry: + :no-typesetting: +.. method:: sequence.index(value[, start[, stop]]) + + Return the index of the first occurrence of *value* in *sequence*. + + Raises :exc:`ValueError` if *value* is not found in *sequence*. + + The *start* or *stop* arguments allow for efficient searching + of subsections of the sequence, beginning at *start* and ending at *stop*. + This is roughly equivalent to ``start + sequence[start:stop].index(value)``, + only without copying any data. + + .. caution:: + Not all sequence types support passing the *start* and *stop* arguments. + .. _typesseq-immutable: @@ -1202,14 +1232,6 @@ accepts integers that meet the value restriction ``0 <= x <= 255``). pair: subscript; assignment pair: slice; assignment pair: statement; del - single: append() (sequence method) - single: clear() (sequence method) - single: copy() (sequence method) - single: extend() (sequence method) - single: insert() (sequence method) - single: pop() (sequence method) - single: remove() (sequence method) - single: reverse() (sequence method) +------------------------------+--------------------------------+---------------------+ | Operation | Result | Notes | @@ -1233,39 +1255,14 @@ accepts integers that meet the value restriction ``0 <= x <= 255``). | ``del s[i:j:k]`` | removes the elements of | | | | ``s[i:j:k]`` from the list | | +------------------------------+--------------------------------+---------------------+ -| ``s.append(x)`` | appends *x* to the end of the | | -| | sequence (same as | | -| | ``s[len(s):len(s)] = [x]``) | | -+------------------------------+--------------------------------+---------------------+ -| ``s.clear()`` | removes all items from *s* | \(5) | -| | (same as ``del s[:]``) | | -+------------------------------+--------------------------------+---------------------+ -| ``s.copy()`` | creates a shallow copy of *s* | \(5) | -| | (same as ``s[:]``) | | -+------------------------------+--------------------------------+---------------------+ -| ``s.extend(t)`` or | extends *s* with the | | -| ``s += t`` | contents of *t* (for the | | +| ``s += t`` | extends *s* with the | | +| | contents of *t* (for the | | | | most part the same as | | | | ``s[len(s):len(s)] = t``) | | +------------------------------+--------------------------------+---------------------+ -| ``s *= n`` | updates *s* with its contents | \(6) | +| ``s *= n`` | updates *s* with its contents | \(2) | | | repeated *n* times | | +------------------------------+--------------------------------+---------------------+ -| ``s.insert(i, x)`` | inserts *x* into *s* at the | | -| | index given by *i* | | -| | (same as ``s[i:i] = [x]``) | | -+------------------------------+--------------------------------+---------------------+ -| ``s.pop()`` or ``s.pop(i)`` | retrieves the item at *i* and | \(2) | -| | also removes it from *s* | | -+------------------------------+--------------------------------+---------------------+ -| ``s.remove(x)`` | removes the first item from | \(3) | -| | *s* where ``s[i]`` is equal to | | -| | *x* | | -+------------------------------+--------------------------------+---------------------+ -| ``s.reverse()`` | reverses the items of *s* in | \(4) | -| | place | | -+------------------------------+--------------------------------+---------------------+ - Notes: @@ -1273,32 +1270,105 @@ Notes: If *k* is not equal to ``1``, *t* must have the same length as the slice it is replacing. (2) - The optional argument *i* defaults to ``-1``, so that by default the last - item is removed and returned. + The value *n* is an integer, or an object implementing + :meth:`~object.__index__`. Zero and negative values of *n* clear + the sequence. Items in the sequence are not copied; they are referenced + multiple times, as explained for ``s * n`` under :ref:`typesseq-common`. -(3) - :meth:`remove` raises :exc:`ValueError` when *x* is not found in *s*. +.. rubric:: Mutable Sequence Methods -(4) - The :meth:`reverse` method modifies the sequence in place for economy of - space when reversing a large sequence. To remind users that it operates by - side effect, it does not return the reversed sequence. +Mutable sequence types also support the following methods: -(5) - :meth:`clear` and :meth:`!copy` are included for consistency with the - interfaces of mutable containers that don't support slicing operations - (such as :class:`dict` and :class:`set`). :meth:`!copy` is not part of the - :class:`collections.abc.MutableSequence` ABC, but most concrete - mutable sequence classes provide it. +.. method:: bytearray.append(value, /) + list.append(value, /) + :no-contents-entry: + :no-index-entry: + :no-typesetting: +.. method:: sequence.append(value, /) + + Append *value* to the end of the sequence. + This is equivalent to writing ``seq[len(seq):len(seq)] = [value]``. + +.. method:: bytearray.clear() + list.clear() + :no-contents-entry: + :no-index-entry: + :no-typesetting: +.. method:: sequence.clear() .. versionadded:: 3.3 - :meth:`clear` and :meth:`!copy` methods. -(6) - The value *n* is an integer, or an object implementing - :meth:`~object.__index__`. Zero and negative values of *n* clear - the sequence. Items in the sequence are not copied; they are referenced - multiple times, as explained for ``s * n`` under :ref:`typesseq-common`. + Remove all items from *sequence*. + This is equivalent to writing ``del sequence[:]``. + +.. method:: bytearray.copy() + list.copy() + :no-contents-entry: + :no-index-entry: + :no-typesetting: +.. method:: sequence.copy() + + .. versionadded:: 3.3 + + Create a shallow copy of *sequence*. + This is equivalent to writing ``sequence[:]``. + + .. hint:: The :meth:`!copy` method is not part of the + :class:`~collections.abc.MutableSequence` :class:`~abc.ABC`, + but most concrete mutable sequence types provide it. + +.. method:: bytearray.extend(iterable, /) + list.extend(iterable, /) + :no-contents-entry: + :no-index-entry: + :no-typesetting: +.. method:: sequence.extend(iterable, /) + + Extend *sequence* with the contents of *iterable*. + For the most part, this is the same as writing + ``seq[len(seq):len(seq)] = iterable``. + +.. method:: bytearray.insert(index, value, /) + list.insert(index, value, /) + :no-contents-entry: + :no-index-entry: + :no-typesetting: +.. method:: sequence.insert(index, value, /) + + Insert *value* into *sequence* at the given *index*. + This is equivalent to writing ``sequence[index:index] = [value]``. + +.. method:: bytearray.pop(index=-1, /) + list.pop(index=-1, /) + :no-contents-entry: + :no-index-entry: + :no-typesetting: +.. method:: sequence.pop(index=-1, /) + + Retrieve the item at *index* and also removes it from *sequence*. + By default, the last item in *sequence* is removed and returned. + +.. method:: bytearray.remove(value, /) + list.remove(value, /) + :no-contents-entry: + :no-index-entry: + :no-typesetting: +.. method:: sequence.remove(value, /) + + Remove the first item from *sequence* where ``sequence[i] == value``. + + Raises :exc:`ValueError` if *value* is not found in *sequence*. + +.. method:: bytearray.reverse() + list.reverse() + :no-contents-entry: + :no-index-entry: + :no-typesetting: +.. method:: sequence.reverse() + + Reverse the items of *sequence* in place. + This method maintains economy of space when reversing a large sequence. + To remind users that it operates by side-effect, it returns ``None``. .. _typesseq-list: @@ -1312,7 +1382,7 @@ Lists are mutable sequences, typically used to store collections of homogeneous items (where the precise degree of similarity will vary by application). -.. class:: list([iterable]) +.. class:: list(iterable=(), /) Lists may be constructed in several ways: @@ -1333,6 +1403,8 @@ application). Many other operations also produce lists, including the :func:`sorted` built-in. + Lists are :ref:`generic ` over the types of their items. + Lists implement all of the :ref:`common ` and :ref:`mutable ` sequence operations. Lists also provide the following additional method: @@ -1379,6 +1451,11 @@ application). list appear empty for the duration, and raises :exc:`ValueError` if it can detect that the list has been mutated during a sort. +.. seealso:: + + For detailed information on thread-safety guarantees for :class:`list` + objects, see :ref:`thread-safety-list`. + .. _typesseq-tuple: @@ -1393,7 +1470,7 @@ built-in). Tuples are also used for cases where an immutable sequence of homogeneous data is needed (such as allowing storage in a :class:`set` or :class:`dict` instance). -.. class:: tuple([iterable]) +.. class:: tuple(iterable=(), /) Tuples may be constructed in a number of ways: @@ -1419,6 +1496,10 @@ homogeneous data is needed (such as allowing storage in a :class:`set` or Tuples implement all of the :ref:`common ` sequence operations. + Tuples are :ref:`generic ` over the types of their contents. + For more information, refer to + :ref:`the typing documentation on annotating tuples `. + For heterogeneous collections of data where access by name is clearer than access by index, :func:`collections.namedtuple` may be a more appropriate choice than a simple tuple object. @@ -1435,8 +1516,8 @@ The :class:`range` type represents an immutable sequence of numbers and is commonly used for looping a specific number of times in :keyword:`for` loops. -.. class:: range(stop) - range(start, stop[, step]) +.. class:: range(stop, /) + range(start, stop, step=1, /) The arguments to the range constructor must be integers (either built-in :class:`int` or any object that implements the :meth:`~object.__index__` special @@ -1696,8 +1777,10 @@ multiple fragments. .. index:: single: string; str (built-in class) -.. class:: str(object='') - str(object=b'', encoding='utf-8', errors='strict') +.. class:: str(*, encoding='utf-8', errors='strict') + str(object) + str(object, encoding, errors='strict') + str(object, *, errors) Return a :ref:`string ` version of *object*. If *object* is not provided, returns the empty string. Otherwise, the behavior of ``str()`` @@ -1783,15 +1866,23 @@ expression support in the :mod:`re` module). lowercase letter ``'ß'`` is equivalent to ``"ss"``. Since it is already lowercase, :meth:`lower` would do nothing to ``'ß'``; :meth:`casefold` converts it to ``"ss"``. + For example: - The casefolding algorithm is - `described in section 3.13 'Default Case Folding' of the Unicode Standard - `__. + .. doctest:: + + >>> 'straße'.lower() + 'straße' + >>> 'straße'.casefold() + 'strasse' + + The casefolding algorithm is `described in section 3.13.3 'Default Case + Folding' of the Unicode Standard + `__. .. versionadded:: 3.3 -.. method:: str.center(width[, fillchar]) +.. method:: str.center(width, fillchar=' ', /) Return centered in a string of length *width*. Padding is done using the specified *fillchar* (default is an ASCII space). The original string is @@ -1909,6 +2000,14 @@ expression support in the :mod:`re` module). Return the lowest index in the string where substring *sub* is found within the slice ``s[start:end]``. Optional arguments *start* and *end* are interpreted as in slice notation. Return ``-1`` if *sub* is not found. + For example:: + + >>> 'spam, spam, spam'.find('sp') + 0 + >>> 'spam, spam, spam'.find('sp', 5) + 6 + + See also :meth:`rfind` and :meth:`index`. .. note:: @@ -1927,10 +2026,16 @@ expression support in the :mod:`re` module). ``{}``. Each replacement field contains either the numeric index of a positional argument, or the name of a keyword argument. Returns a copy of the string where each replacement field is replaced with the string value of - the corresponding argument. + the corresponding argument. For example: + + .. doctest:: >>> "The sum of 1 + 2 is {0}".format(1+2) 'The sum of 1 + 2 is 3' + >>> "The sum of {a} + {b} is {answer}".format(answer=1+2, a=1, b=2) + 'The sum of 1 + 2 is 3' + >>> "{1} expects the {0} Inquisition!".format("Spanish", "Nobody") + 'Nobody expects the Spanish Inquisition!' See :ref:`formatstrings` for a description of the various formatting options that can be specified in format strings. @@ -1970,7 +2075,20 @@ expression support in the :mod:`re` module). .. method:: str.index(sub[, start[, end]]) Like :meth:`~str.find`, but raise :exc:`ValueError` when the substring is - not found. + not found. For example: + + .. doctest:: + + >>> 'spam, spam, spam'.index('spam') + 0 + >>> 'spam, spam, spam'.index('eggs') + Traceback (most recent call last): + File "", line 1, in + 'spam, spam, spam'.index('eggs') + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^ + ValueError: substring not found + + See also :meth:`rindex`. .. method:: str.isalnum() @@ -1978,7 +2096,18 @@ expression support in the :mod:`re` module). Return ``True`` if all characters in the string are alphanumeric and there is at least one character, ``False`` otherwise. A character ``c`` is alphanumeric if one of the following returns ``True``: ``c.isalpha()``, ``c.isdecimal()``, - ``c.isdigit()``, or ``c.isnumeric()``. + ``c.isdigit()``, or ``c.isnumeric()``. For example: + + .. doctest:: + + >>> 'abc123'.isalnum() + True + >>> 'abc123!@#'.isalnum() + False + >>> ''.isalnum() + False + >>> ' '.isalnum() + False .. method:: str.isalpha() @@ -1989,14 +2118,33 @@ expression support in the :mod:`re` module). property being one of "Lm", "Lt", "Lu", "Ll", or "Lo". Note that this is different from the `Alphabetic property defined in the section 4.10 'Letters, Alphabetic, and Ideographic' of the Unicode Standard - `_. + `__. + For example: + + .. doctest:: + + >>> 'Letters and spaces'.isalpha() + False + >>> 'LettersOnly'.isalpha() + True + >>> 'µ'.isalpha() # non-ASCII characters can be considered alphabetical too + True + + See :ref:`unicode-properties`. .. method:: str.isascii() Return ``True`` if the string is empty or all characters in the string are ASCII, ``False`` otherwise. - ASCII characters have code points in the range U+0000-U+007F. + ASCII characters have code points in the range U+0000-U+007F. For example: + + .. doctest:: + + >>> 'ASCII characters'.isascii() + True + >>> 'µ'.isascii() + False .. versionadded:: 3.7 @@ -2006,9 +2154,18 @@ expression support in the :mod:`re` module). Return ``True`` if all characters in the string are decimal characters and there is at least one character, ``False`` otherwise. Decimal characters are those that can be used to form - numbers in base 10, e.g. U+0660, ARABIC-INDIC DIGIT + numbers in base 10, such as U+0660, ARABIC-INDIC DIGIT ZERO. Formally a decimal character is a character in the Unicode - General Category "Nd". + General Category "Nd". For example: + + .. doctest:: + + >>> '0123456789'.isdecimal() + True + >>> '٠١٢٣٤٥٦٧٨٩'.isdecimal() # Arabic-Indic digits zero to nine + True + >>> 'alphabetic'.isdecimal() + False .. method:: str.isdigit() @@ -2017,9 +2174,25 @@ expression support in the :mod:`re` module). character, ``False`` otherwise. Digits include decimal characters and digits that need special handling, such as the compatibility superscript digits. This covers digits which cannot be used to form numbers in base 10, - like the Kharosthi numbers. Formally, a digit is a character that has the + like the `Kharosthi numbers `__. + Formally, a digit is a character that has the property value Numeric_Type=Digit or Numeric_Type=Decimal. + For example: + + .. doctest:: + + >>> '0123456789'.isdigit() + True + >>> '٠١٢٣٤٥٦٧٨٩'.isdigit() # Arabic-Indic digits zero to nine + True + >>> '⅕'.isdigit() # Vulgar fraction one fifth + False + >>> '²'.isdecimal(), '²'.isdigit(), '²'.isnumeric() + (False, True, True) + + See also :meth:`isdecimal` and :meth:`isnumeric`. + .. method:: str.isidentifier() @@ -2054,6 +2227,20 @@ expression support in the :mod:`re` module). that have the Unicode numeric value property, e.g. U+2155, VULGAR FRACTION ONE FIFTH. Formally, numeric characters are those with the property value Numeric_Type=Digit, Numeric_Type=Decimal or Numeric_Type=Numeric. + For example: + + .. doctest:: + + >>> '0123456789'.isnumeric() + True + >>> '٠١٢٣٤٥٦٧٨٩'.isnumeric() # Arabic-Indic digits zero to nine + True + >>> '⅕'.isnumeric() # Vulgar fraction one fifth + True + >>> '²'.isdecimal(), '²'.isdigit(), '²'.isnumeric() + (False, True, True) + + See also :meth:`isdecimal` and :meth:`isdigit`. .. method:: str.isprintable() @@ -2072,17 +2259,43 @@ expression support in the :mod:`re` module). Nonprintable characters are those in group Separator or Other (Z or C), except the ASCII space. + For example: + + .. doctest:: + + >>> ''.isprintable(), ' '.isprintable() + (True, True) + >>> '\t'.isprintable(), '\n'.isprintable() + (False, False) + + See also :meth:`isspace`. + .. method:: str.isspace() Return ``True`` if there are only whitespace characters in the string and there is at least one character, ``False`` otherwise. + For example: + + .. doctest:: + + >>> ''.isspace() + False + >>> ' '.isspace() + True + >>> '\t\n'.isspace() # TAB and BREAK LINE + True + >>> '\u3000'.isspace() # IDEOGRAPHIC SPACE + True + A character is *whitespace* if in the Unicode character database (see :mod:`unicodedata`), either its general category is ``Zs`` ("Separator, space"), or its bidirectional class is one of ``WS``, ``B``, or ``S``. + See also :meth:`isprintable`. + .. method:: str.istitle() @@ -2090,6 +2303,19 @@ expression support in the :mod:`re` module). character, for example uppercase characters may only follow uncased characters and lowercase characters only cased ones. Return ``False`` otherwise. + For example: + + .. doctest:: + + >>> 'Spam, Spam, Spam'.istitle() + True + >>> 'spam, spam, spam'.istitle() + False + >>> 'SPAM, SPAM, SPAM'.istitle() + False + + See also :meth:`title`. + .. method:: str.isupper() @@ -2109,36 +2335,64 @@ expression support in the :mod:`re` module). .. _meth-str-join: -.. method:: str.join(iterable) +.. method:: str.join(iterable, /) Return a string which is the concatenation of the strings in *iterable*. A :exc:`TypeError` will be raised if there are any non-string values in *iterable*, including :class:`bytes` objects. The separator between - elements is the string providing this method. + elements is the string providing this method. For example: + .. doctest:: + + >>> ', '.join(['spam', 'spam', 'spam']) + 'spam, spam, spam' + >>> '-'.join('Python') + 'P-y-t-h-o-n' + + See also :meth:`split`. -.. method:: str.ljust(width[, fillchar]) + +.. method:: str.ljust(width, fillchar=' ', /) Return the string left justified in a string of length *width*. Padding is done using the specified *fillchar* (default is an ASCII space). The original string is returned if *width* is less than or equal to ``len(s)``. + For example: + + .. doctest:: + + >>> 'Python'.ljust(10) + 'Python ' + >>> 'Python'.ljust(10, '.') + 'Python....' + >>> 'Monty Python'.ljust(10, '.') + 'Monty Python' + + See also :meth:`rjust`. + .. method:: str.lower() Return a copy of the string with all the cased characters [4]_ converted to - lowercase. + lowercase. For example: - The lowercasing algorithm used is - `described in section 3.13 'Default Case Folding' of the Unicode Standard - `__. + .. doctest:: + >>> 'Lower Method Example'.lower() + 'lower method example' -.. method:: str.lstrip([chars]) + The lowercasing algorithm used is `described in section 3.13.2 'Default Case + Conversion' of the Unicode Standard + `__. + + +.. method:: str.lstrip(chars=None, /) Return a copy of the string with leading characters removed. The *chars* argument is a string specifying the set of characters to be removed. If omitted - or ``None``, the *chars* argument defaults to removing whitespace. The *chars* + or ``None``, the *chars* argument defaults to removing whitespace, that is + characters for which :meth:`str.isspace` is true. The *chars* argument is not a prefix; rather, all combinations of its values are stripped:: >>> ' spacious '.lstrip() @@ -2155,7 +2409,8 @@ expression support in the :mod:`re` module). 'three!' -.. staticmethod:: str.maketrans(x[, y[, z]]) +.. staticmethod:: str.maketrans(dict, /) + str.maketrans(from, to, remove='', /) This static method returns a translation table usable for :meth:`str.translate`. @@ -2165,24 +2420,43 @@ expression support in the :mod:`re` module). converted to ordinals. If there are two arguments, they must be strings of equal length, and in the - resulting dictionary, each character in x will be mapped to the character at - the same position in y. If there is a third argument, it must be a string, + resulting dictionary, each character in *from* will be mapped to the character at + the same position in *to*. If there is a third argument, it must be a string, whose characters will be mapped to ``None`` in the result. + .. versionchanged:: 3.15 + + *dict* can now be a :class:`frozendict`. -.. method:: str.partition(sep) + +.. method:: str.partition(sep, /) Split the 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. + For example: + + .. doctest:: + + >>> 'Monty Python'.partition(' ') + ('Monty', ' ', 'Python') + >>> "Monty Python's Flying Circus".partition(' ') + ('Monty', ' ', "Python's Flying Circus") + >>> 'Monty Python'.partition('-') + ('Monty Python', '', '') + + See also :meth:`rpartition`. + .. method:: str.removeprefix(prefix, /) If the string starts with the *prefix* string, return ``string[len(prefix):]``. Otherwise, return a copy of the original - string:: + string: + + .. doctest:: >>> 'TestHook'.removeprefix('Test') 'Hook' @@ -2191,12 +2465,16 @@ expression support in the :mod:`re` module). .. versionadded:: 3.9 + See also :meth:`removesuffix` and :meth:`startswith`. + .. method:: str.removesuffix(suffix, /) If the string ends with the *suffix* string and that *suffix* is not empty, return ``string[:-len(suffix)]``. Otherwise, return a copy of the - original string:: + original string: + + .. doctest:: >>> 'MiscTests'.removesuffix('Tests') 'Misc' @@ -2205,12 +2483,22 @@ expression support in the :mod:`re` module). .. versionadded:: 3.9 + See also :meth:`removeprefix` and :meth:`endswith`. -.. method:: str.replace(old, new, count=-1) + +.. method:: str.replace(old, new, /, count=-1) Return a copy of the string with all occurrences of substring *old* replaced by *new*. If *count* is given, only the first *count* occurrences are replaced. If *count* is not specified or ``-1``, then all occurrences are replaced. + For example: + + .. doctest:: + + >>> 'spam, spam, spam'.replace('spam', 'eggs') + 'eggs, eggs, eggs' + >>> 'spam, spam, spam'.replace('spam', 'eggs', 1) + 'eggs, spam, spam' .. versionchanged:: 3.13 *count* is now supported as a keyword argument. @@ -2221,28 +2509,78 @@ expression support in the :mod:`re` module). Return the highest index in the string where substring *sub* is found, such that *sub* is contained within ``s[start:end]``. Optional arguments *start* and *end* are interpreted as in slice notation. Return ``-1`` on failure. + For example: + + .. doctest:: + + >>> 'spam, spam, spam'.rfind('sp') + 12 + >>> 'spam, spam, spam'.rfind('sp', 0, 10) + 6 + + See also :meth:`find` and :meth:`rindex`. .. method:: str.rindex(sub[, start[, end]]) Like :meth:`rfind` but raises :exc:`ValueError` when the substring *sub* is not found. + For example: + + .. doctest:: + + >>> 'spam, spam, spam'.rindex('spam') + 12 + >>> 'spam, spam, spam'.rindex('eggs') + Traceback (most recent call last): + File "", line 1, in + 'spam, spam, spam'.rindex('eggs') + ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^ + ValueError: substring not found + + See also :meth:`index` and :meth:`find`. -.. method:: str.rjust(width[, fillchar]) +.. method:: str.rjust(width, fillchar=' ', /) Return the string right justified in a string of length *width*. Padding is done using the specified *fillchar* (default is an ASCII space). The original string is returned if *width* is less than or equal to ``len(s)``. + For example: + + .. doctest:: + + >>> 'Python'.rjust(10) + ' Python' + >>> 'Python'.rjust(10, '.') + '....Python' + >>> 'Monty Python'.rjust(10, '.') + 'Monty Python' + + See also :meth:`ljust` and :meth:`zfill`. + -.. method:: str.rpartition(sep) +.. method:: str.rpartition(sep, /) Split the string at the last 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 two empty strings, followed by the string itself. + For example: + + .. doctest:: + + >>> 'Monty Python'.rpartition(' ') + ('Monty', ' ', 'Python') + >>> "Monty Python's Flying Circus".rpartition(' ') + ("Monty Python's Flying", ' ', 'Circus') + >>> 'Monty Python'.rpartition('-') + ('', '', 'Monty Python') + + See also :meth:`partition`. + .. method:: str.rsplit(sep=None, maxsplit=-1) @@ -2253,19 +2591,23 @@ expression support in the :mod:`re` module). :meth:`split` which is described in detail below. -.. method:: str.rstrip([chars]) +.. method:: str.rstrip(chars=None, /) Return a copy of the string with trailing characters removed. The *chars* argument is a string specifying the set of characters to be removed. If omitted - or ``None``, the *chars* argument defaults to removing whitespace. The *chars* - argument is not a suffix; rather, all combinations of its values are stripped:: + or ``None``, the *chars* argument defaults to removing whitespace, that is + characters for which :meth:`str.isspace` is true. The *chars* + argument is not a suffix; rather, all combinations of its values are stripped. + For example: + + .. doctest:: >>> ' spacious '.rstrip() ' spacious' >>> 'mississippi'.rstrip('ipz') 'mississ' - See :meth:`str.removesuffix` for a method that will remove a single suffix + See :meth:`removesuffix` for a method that will remove a single suffix string rather than all of a set of characters. For example:: >>> 'Monty Python'.rstrip(' Python') @@ -2273,6 +2615,9 @@ expression support in the :mod:`re` module). >>> 'Monty Python'.removesuffix(' Python') 'Monty' + See also :meth:`strip`. + + .. method:: str.split(sep=None, maxsplit=-1) Return a list of the words in the string, using *sep* as the delimiter @@ -2288,7 +2633,9 @@ expression support in the :mod:`re` module). :func:`re.split`). Splitting an empty string with a specified separator returns ``['']``. - For example:: + For example: + + .. doctest:: >>> '1,2,3'.split(',') ['1', '2', '3'] @@ -2306,7 +2653,9 @@ expression support in the :mod:`re` module). string or a string consisting of just whitespace with a ``None`` separator returns ``[]``. - For example:: + For example: + + .. doctest:: >>> '1 2 3'.split() ['1', '2', '3'] @@ -2318,7 +2667,9 @@ expression support in the :mod:`re` module). If *sep* is not specified or is ``None`` and *maxsplit* is ``0``, only leading runs of consecutive whitespace are considered. - For example:: + For example: + + .. doctest:: >>> "".split(None, 0) [] @@ -2327,6 +2678,8 @@ expression support in the :mod:`re` module). >>> " foo ".split(maxsplit=0) ['foo '] + See also :meth:`join` and :meth:`rsplit`. + .. index:: single: universal newlines; str.splitlines method @@ -2401,14 +2754,31 @@ expression support in the :mod:`re` module). test string beginning at that position. With optional *end*, stop comparing string at that position. + For example: + + .. doctest:: + + >>> 'Python'.startswith('Py') + True + >>> 'a tuple of prefixes'.startswith(('at', 'a')) + True + >>> 'Python is amazing'.startswith('is', 7) + True + + See also :meth:`endswith` and :meth:`removeprefix`. + -.. method:: str.strip([chars]) +.. method:: str.strip(chars=None, /) Return a copy of the string with the leading and trailing characters removed. The *chars* argument is a string specifying the set of characters to be removed. - If omitted or ``None``, the *chars* argument defaults to removing whitespace. - The *chars* argument is not a prefix or suffix; rather, all combinations of its - values are stripped:: + If omitted or ``None``, the *chars* argument defaults to removing whitespace, + that is characters for which :meth:`str.isspace` is true. The *chars* argument + is not a prefix or suffix; rather, all combinations of its values are stripped. + + For example: + + .. doctest:: >>> ' spacious '.strip() 'spacious' @@ -2419,18 +2789,37 @@ expression support in the :mod:`re` module). from the string. Characters are removed from the leading end until reaching a string character that is not contained in the set of characters in *chars*. A similar action takes place on the trailing end. - For example:: + + For example: + + .. doctest:: >>> comment_string = '#....... Section 3.2.1 Issue #32 .......' >>> comment_string.strip('.#! ') 'Section 3.2.1 Issue #32' + See also :meth:`rstrip`. + .. method:: str.swapcase() Return a copy of the string with uppercase characters converted to lowercase and - vice versa. Note that it is not necessarily true that - ``s.swapcase().swapcase() == s``. + vice versa. For example: + + .. doctest:: + + >>> 'Hello World'.swapcase() + 'hELLO wORLD' + + Note that it is not necessarily true that ``s.swapcase().swapcase() == s``. + For example: + + .. doctest:: + + >>> 'straße'.swapcase().swapcase() + 'strasse' + + See also :meth:`str.lower` and :meth:`str.upper`. .. method:: str.title() @@ -2466,8 +2855,10 @@ expression support in the :mod:`re` module). >>> titlecase("they're bill's friends.") "They're Bill's Friends." + See also :meth:`istitle`. + -.. method:: str.translate(table) +.. method:: str.translate(table, /) Return a copy of the string in which each character has been mapped through the given translation table. The table must be an object that implements @@ -2481,6 +2872,14 @@ expression support in the :mod:`re` module). You can use :meth:`str.maketrans` to create a translation map from character-to-character mappings in different formats. + The following example uses a mapping to replace ``'a'`` with ``'X'``, + ``'b'`` with ``'Y'``, and delete ``'c'``: + + .. doctest:: + + >>> 'abc123'.translate({ord('a'): 'X', ord('b'): 'Y', ord('c'): None}) + 'XY123' + See also the :mod:`codecs` module for a more flexible approach to custom character mappings. @@ -2493,12 +2892,12 @@ expression support in the :mod:`re` module). character(s) is not "Lu" (Letter, uppercase), but e.g. "Lt" (Letter, titlecase). - The uppercasing algorithm used is - `described in section 3.13 'Default Case Folding' of the Unicode Standard - `__. + The uppercasing algorithm used is `described in section 3.13.2 'Default Case + Conversion' of the Unicode Standard + `__. -.. method:: str.zfill(width) +.. method:: str.zfill(width, /) Return a copy of the string left filled with ASCII ``'0'`` digits to make a string of length *width*. A leading sign prefix (``'+'``/``'-'``) @@ -2506,13 +2905,17 @@ expression support in the :mod:`re` module). than before. The original string is returned if *width* is less than or equal to ``len(s)``. - For example:: + For example: + + .. doctest:: >>> "42".zfill(5) '00042' >>> "-42".zfill(5) '-0042' + See also :meth:`rjust`. + .. index:: single: ! formatted string literal @@ -2528,6 +2931,8 @@ expression support in the :mod:`re` module). single: : (colon); in formatted string literal single: = (equals); for help in debugging using string literals +.. _stdtypes-fstrings: + Formatted String Literals (f-strings) ------------------------------------- @@ -2536,123 +2941,147 @@ Formatted String Literals (f-strings) The :keyword:`await` and :keyword:`async for` can be used in expressions within f-strings. .. versionchanged:: 3.8 - Added the debugging operator (``=``) + Added the debug specifier (``=``) .. versionchanged:: 3.12 Many restrictions on expressions within f-strings have been removed. Notably, nested strings, comments, and backslashes are now permitted. An :dfn:`f-string` (formally a :dfn:`formatted string literal`) is a string literal that is prefixed with ``f`` or ``F``. -This type of string literal allows embedding arbitrary Python expressions -within *replacement fields*, which are delimited by curly brackets (``{}``). -These expressions are evaluated at runtime, similarly to :meth:`str.format`, -and are converted into regular :class:`str` objects. -For example: +This type of string literal allows embedding the results of arbitrary Python +expressions within *replacement fields*, which are delimited by curly +brackets (``{}``). +Each replacement field must contain an expression, optionally followed by: -.. doctest:: +* a *debug specifier* -- an equal sign (``=``); +* a *conversion specifier* -- ``!s``, ``!r`` or ``!a``; and/or +* a *format specifier* prefixed with a colon (``:``). - >>> who = 'nobody' - >>> nationality = 'Spanish' - >>> f'{who.title()} expects the {nationality} Inquisition!' - 'Nobody expects the Spanish Inquisition!' +See the :ref:`Lexical Analysis section on f-strings ` for details +on the syntax of these fields. -It is also possible to use a multi line f-string: +Debug specifier +^^^^^^^^^^^^^^^ -.. doctest:: +.. versionadded:: 3.8 - >>> f'''This is a string - ... on two lines''' - 'This is a string\non two lines' +If a debug specifier -- an equal sign (``=``) -- appears after the replacement +field expression, the resulting f-string will contain the expression's source, +the equal sign, and the value of the expression. +This is often useful for debugging:: -A single opening curly bracket, ``'{'``, marks a *replacement field* that -can contain any Python expression: + >>> number = 14.3 + >>> f'{number=}' + 'number=14.3' -.. doctest:: - - >>> nationality = 'Spanish' - >>> f'The {nationality} Inquisition!' - 'The Spanish Inquisition!' - -To include a literal ``{`` or ``}``, use a double bracket: - -.. doctest:: +Whitespace before, inside and after the expression, as well as whitespace +after the equal sign, is significant --- it is retained in the result:: - >>> x = 42 - >>> f'{{x}} is {x}' - '{x} is 42' + >>> f'{ number - 4 = }' + ' number - 4 = 10.3' -Functions can also be used, and :ref:`format specifiers `: -.. doctest:: - - >>> from math import sqrt - >>> f'√2 \N{ALMOST EQUAL TO} {sqrt(2):.5f}' - '√2 ≈ 1.41421' +Conversion specifier +^^^^^^^^^^^^^^^^^^^^ -Any non-string expression is converted using :func:`str`, by default: - -.. doctest:: +By default, the value of a replacement field expression is converted to +a string using :func:`str`:: >>> from fractions import Fraction - >>> f'{Fraction(1, 3)}' + >>> one_third = Fraction(1, 3) + >>> f'{one_third}' '1/3' -To use an explicit conversion, use the ``!`` (exclamation mark) operator, -followed by any of the valid formats, which are: +When a debug specifier but no format specifier is used, the default conversion +instead uses :func:`repr`:: -========== ============== -Conversion Meaning -========== ============== -``!a`` :func:`ascii` -``!r`` :func:`repr` -``!s`` :func:`str` -========== ============== + >>> f'{one_third = }' + 'one_third = Fraction(1, 3)' -For example: +The conversion can be specified explicitly using one of these specifiers: -.. doctest:: +* ``!s`` for :func:`str` +* ``!r`` for :func:`repr` +* ``!a`` for :func:`ascii` - >>> from fractions import Fraction - >>> f'{Fraction(1, 3)!s}' +For example:: + + >>> str(one_third) '1/3' - >>> f'{Fraction(1, 3)!r}' + >>> repr(one_third) 'Fraction(1, 3)' - >>> question = '¿Dónde está el Presidente?' - >>> print(f'{question!a}') - '\xbfD\xf3nde est\xe1 el Presidente?' - -While debugging it may be helpful to see both the expression and its value, -by using the equals sign (``=``) after the expression. -This preserves spaces within the brackets, and can be used with a converter. -By default, the debugging operator uses the :func:`repr` (``!r``) conversion. -For example: -.. doctest:: + >>> f'{one_third!s} is {one_third!r}' + '1/3 is Fraction(1, 3)' - >>> from fractions import Fraction - >>> calculation = Fraction(1, 3) - >>> f'{calculation=}' - 'calculation=Fraction(1, 3)' - >>> f'{calculation = }' - 'calculation = Fraction(1, 3)' - >>> f'{calculation = !s}' - 'calculation = 1/3' - -Once the output has been evaluated, it can be formatted using a -:ref:`format specifier ` following a colon (``':'``). -After the expression has been evaluated, and possibly converted to a string, -the :meth:`!__format__` method of the result is called with the format specifier, -or the empty string if no format specifier is given. -The formatted result is then used as the final value for the replacement field. -For example: + >>> string = "¡kočka 😸!" + >>> ascii(string) + "'\\xa1ko\\u010dka \\U0001f638!'" -.. doctest:: + >>> f'{string = !a}' + "string = '\\xa1ko\\u010dka \\U0001f638!'" + + +Format specifier +^^^^^^^^^^^^^^^^ + +After the expression has been evaluated, and possibly converted using an +explicit conversion specifier, it is formatted using the :func:`format` function. +If the replacement field includes a *format specifier* introduced by a colon +(``:``), the specifier is passed to :func:`!format` as the second argument. +The result of :func:`!format` is then used as the final value for the +replacement field. For example:: >>> from fractions import Fraction - >>> f'{Fraction(1, 7):.6f}' - '0.142857' - >>> f'{Fraction(1, 7):_^+10}' - '___+1/7___' + >>> one_third = Fraction(1, 3) + >>> f'{one_third:.6f}' + '0.333333' + >>> f'{one_third:_^+10}' + '___+1/3___' + >>> >>> f'{one_third!r:_^20}' + '___Fraction(1, 3)___' + >>> f'{one_third = :~>10}~' + 'one_third = ~~~~~~~1/3~' + +.. _stdtypes-tstrings: + +Template String Literals (t-strings) +------------------------------------ + +An :dfn:`t-string` (formally a :dfn:`template string literal`) is +a string literal that is prefixed with ``t`` or ``T``. + +These strings follow the same syntax and evaluation rules as +:ref:`formatted string literals `, +with for the following differences: + +* Rather than evaluating to a ``str`` object, template string literals evaluate + to a :class:`string.templatelib.Template` object. + +* The :func:`format` protocol is not used. + Instead, the format specifier and conversions (if any) are passed to + a new :class:`~string.templatelib.Interpolation` object that is created + for each evaluated expression. + It is up to code that processes the resulting :class:`~string.templatelib.Template` + object to decide how to handle format specifiers and conversions. + +* Format specifiers containing nested replacement fields are evaluated eagerly, + prior to being passed to the :class:`~string.templatelib.Interpolation` object. + For instance, an interpolation of the form ``{amount:.{precision}f}`` will + evaluate the inner expression ``{precision}`` to determine the value of the + ``format_spec`` attribute. + If ``precision`` were to be ``2``, the resulting format specifier + would be ``'.2f'``. + +* When the equals sign ``'='`` is provided in an interpolation expression, + the text of the expression is appended to the literal string that precedes + the relevant interpolation. + This includes the equals sign and any surrounding whitespace. + The :class:`!Interpolation` instance for the expression will be created as + normal, except that :attr:`~string.templatelib.Interpolation.conversion` will + be set to '``r``' (:func:`repr`) by default. + If an explicit conversion or format specifier are provided, + this will override the default behaviour. .. _old-string-formatting: @@ -2673,9 +3102,10 @@ For example: The formatting operations described here exhibit a variety of quirks that lead to a number of common errors (such as failing to display tuples and - dictionaries correctly). Using the newer :ref:`formatted string literals - `, the :meth:`str.format` interface, or :ref:`template strings - ($-strings) ` may help avoid these errors. + dictionaries correctly). + + Using :ref:`formatted string literals `, the :meth:`str.format` + interface, or :class:`string.Template` may help avoid these errors. Each of these alternatives provides their own trade-offs and benefits of simplicity, flexibility, and/or extensibility. @@ -2815,6 +3245,10 @@ The conversion types are: | | character in the result. | | +------------+-----------------------------------------------------+-------+ +For floating-point formats, the result should be correctly rounded to a given +precision ``p`` of digits after the decimal point. The rounding mode matches +that of the :func:`round` builtin. + Notes: (1) @@ -2889,7 +3323,8 @@ binary protocols are based on the ASCII text encoding, bytes objects offer several methods that are only valid when working with ASCII compatible data and are closely related to string objects in a variety of other ways. -.. class:: bytes([source[, encoding[, errors]]]) +.. class:: bytes(source=b'') + bytes(source, encoding, errors='strict') Firstly, the syntax for bytes literals is largely the same as that for string literals, except that a ``b`` prefix is added: @@ -2929,7 +3364,7 @@ data and are closely related to string objects in a variety of other ways. numbers are a commonly used format for describing binary data. Accordingly, the bytes type has an additional class method to read data in that format: - .. classmethod:: fromhex(string) + .. classmethod:: fromhex(string, /) This :class:`bytes` class method returns a bytes object, decoding the given string object. The string must contain two hexadecimal digits per @@ -2949,7 +3384,8 @@ data and are closely related to string objects in a variety of other ways. A reverse conversion function exists to transform a bytes object into its hexadecimal representation. - .. method:: hex([sep[, bytes_per_sep]]) + .. method:: hex(*, bytes_per_sep=1) + hex(sep, bytes_per_sep=1) Return a string object containing two hexadecimal digits for each byte in the instance. @@ -2998,7 +3434,8 @@ Bytearray Objects :class:`bytearray` objects are a mutable counterpart to :class:`bytes` objects. -.. class:: bytearray([source[, encoding[, errors]]]) +.. class:: bytearray(source=b'') + bytearray(source, encoding, errors='strict') There is no dedicated literal syntax for bytearray objects, instead they are always created by calling the constructor: @@ -3018,7 +3455,7 @@ objects. numbers are a commonly used format for describing binary data. Accordingly, the bytearray type has an additional class method to read data in that format: - .. classmethod:: fromhex(string) + .. classmethod:: fromhex(string, /) This :class:`bytearray` class method returns bytearray object, decoding the given string object. The string must contain two hexadecimal digits @@ -3038,7 +3475,8 @@ objects. A reverse conversion function exists to transform a bytearray object into its hexadecimal representation. - .. method:: hex([sep[, bytes_per_sep]]) + .. method:: hex(*, bytes_per_sep=1) + hex(sep, bytes_per_sep=1) Return a string object containing two hexadecimal digits for each byte in the instance. @@ -3053,7 +3491,7 @@ objects. optional *sep* and *bytes_per_sep* parameters to insert separators between bytes in the hex output. - .. method:: resize(size) + .. method:: resize(size, /) Resize the :class:`bytearray` to contain *size* bytes. *size* must be greater than or equal to 0. @@ -3085,6 +3523,30 @@ objects. .. versionadded:: 3.14 + .. method:: take_bytes(n=None, /) + + Remove the first *n* bytes from the bytearray and return them as an immutable + :class:`bytes`. + By default (if *n* is ``None``), return all bytes and clear the bytearray. + + If *n* is negative, index from the end and take the first :func:`len` + plus *n* bytes. If *n* is out of bounds, raise :exc:`IndexError`. + + Taking less than the full length will leave remaining bytes in the + :class:`bytearray`, which requires a copy. If the remaining bytes should be + discarded, use :func:`~bytearray.resize` or :keyword:`del` to truncate + then :func:`~bytearray.take_bytes` without a size. + + .. impl-detail:: + + Taking all bytes is a zero-copy operation. + + .. versionadded:: 3.15 + + See the :ref:`What's New ` entry for + common code patterns which can be optimized with + :meth:`bytearray.take_bytes`. + Since bytearray objects are sequences of integers (akin to a list), for a bytearray object *b*, ``b[0]`` will be an integer, while ``b[0:1]`` will be a bytearray object of length 1. (This contrasts with text strings, where @@ -3095,6 +3557,11 @@ The representation of bytearray objects uses the bytes literal format ``bytearray([46, 46, 46])``. You can always convert a bytearray object into a list of integers using ``list(b)``. +.. seealso:: + + For detailed information on thread-safety guarantees for :class:`bytearray` + objects, see :ref:`thread-safety-bytearray`. + .. _bytes-methods: @@ -3276,8 +3743,8 @@ arbitrary binary data. Also accept an integer in the range 0 to 255 as the subsequence. -.. method:: bytes.join(iterable) - bytearray.join(iterable) +.. method:: bytes.join(iterable, /) + bytearray.join(iterable, /) Return a bytes or bytearray object which is the concatenation of the binary data sequences in *iterable*. A :exc:`TypeError` will be raised @@ -3287,8 +3754,8 @@ arbitrary binary data. bytearray object providing this method. -.. staticmethod:: bytes.maketrans(from, to) - bytearray.maketrans(from, to) +.. staticmethod:: bytes.maketrans(from, to, /) + bytearray.maketrans(from, to, /) This static method returns a translation table usable for :meth:`bytes.translate` that will map each character in *from* into the @@ -3298,8 +3765,8 @@ arbitrary binary data. .. versionadded:: 3.1 -.. method:: bytes.partition(sep) - bytearray.partition(sep) +.. method:: bytes.partition(sep, /) + bytearray.partition(sep, /) Split the sequence at the first occurrence of *sep*, and return a 3-tuple containing the part before the separator, the separator itself or its @@ -3311,12 +3778,13 @@ arbitrary binary data. The separator to search for may be any :term:`bytes-like object`. -.. method:: bytes.replace(old, new[, count]) - bytearray.replace(old, new[, count]) +.. method:: bytes.replace(old, new, /, count=-1) + bytearray.replace(old, new, /, count=-1) Return a copy of the sequence with all occurrences of subsequence *old* - replaced by *new*. If the optional argument *count* is given, only the - first *count* occurrences are replaced. + replaced by *new*. If *count* is given, only the first *count* occurrences + are replaced. If *count* is not specified or ``-1``, then all occurrences + are replaced. The subsequence to search for and its replacement may be any :term:`bytes-like object`. @@ -3326,6 +3794,9 @@ arbitrary binary data. The bytearray version of this method does *not* operate in place - it always produces a new object, even if no changes were made. + .. versionchanged:: 3.15 + *count* is now supported as a keyword argument. + .. method:: bytes.rfind(sub[, start[, end]]) bytearray.rfind(sub[, start[, end]]) @@ -3355,8 +3826,8 @@ arbitrary binary data. Also accept an integer in the range 0 to 255 as the subsequence. -.. method:: bytes.rpartition(sep) - bytearray.rpartition(sep) +.. method:: bytes.rpartition(sep, /) + bytearray.rpartition(sep, /) Split the sequence at the last occurrence of *sep*, and return a 3-tuple containing the part before the separator, the separator itself or its @@ -3406,8 +3877,8 @@ with arbitrary binary data by passing appropriate arguments. Note that all of the bytearray methods in this section do *not* operate in place, and instead produce new objects. -.. method:: bytes.center(width[, fillbyte]) - bytearray.center(width[, fillbyte]) +.. method:: bytes.center(width, fillbyte=b' ', /) + bytearray.center(width, fillbyte=b' ', /) Return a copy of the object centered in a sequence of length *width*. Padding is done using the specified *fillbyte* (default is an ASCII @@ -3420,8 +3891,8 @@ produce new objects. it always produces a new object, even if no changes were made. -.. method:: bytes.ljust(width[, fillbyte]) - bytearray.ljust(width[, fillbyte]) +.. method:: bytes.ljust(width, fillbyte=b' ', /) + bytearray.ljust(width, fillbyte=b' ', /) Return a copy of the object left justified in a sequence of length *width*. Padding is done using the specified *fillbyte* (default is an ASCII @@ -3434,14 +3905,13 @@ produce new objects. it always produces a new object, even if no changes were made. -.. method:: bytes.lstrip([chars]) - bytearray.lstrip([chars]) +.. method:: bytes.lstrip(bytes=None, /) + bytearray.lstrip(bytes=None, /) Return a copy of the sequence with specified leading bytes removed. The - *chars* argument is a binary sequence specifying the set of byte values to - be removed - the name refers to the fact this method is usually used with - ASCII characters. If omitted or ``None``, the *chars* argument defaults - to removing ASCII whitespace. The *chars* argument is not a prefix; + *bytes* argument is a binary sequence specifying the set of byte values to + be removed. If omitted or ``None``, the *bytes* argument defaults + to removing ASCII whitespace. The *bytes* argument is not a prefix; rather, all combinations of its values are stripped:: >>> b' spacious '.lstrip() @@ -3465,8 +3935,8 @@ produce new objects. it always produces a new object, even if no changes were made. -.. method:: bytes.rjust(width[, fillbyte]) - bytearray.rjust(width[, fillbyte]) +.. method:: bytes.rjust(width, fillbyte=b' ', /) + bytearray.rjust(width, fillbyte=b' ', /) Return a copy of the object right justified in a sequence of length *width*. Padding is done using the specified *fillbyte* (default is an ASCII @@ -3490,14 +3960,13 @@ produce new objects. :meth:`split` which is described in detail below. -.. method:: bytes.rstrip([chars]) - bytearray.rstrip([chars]) +.. method:: bytes.rstrip(bytes=None, /) + bytearray.rstrip(bytes=None, /) Return a copy of the sequence with specified trailing bytes removed. The - *chars* argument is a binary sequence specifying the set of byte values to - be removed - the name refers to the fact this method is usually used with - ASCII characters. If omitted or ``None``, the *chars* argument defaults to - removing ASCII whitespace. The *chars* argument is not a suffix; rather, + *bytes* argument is a binary sequence specifying the set of byte values to + be removed. If omitted or ``None``, the *bytes* argument defaults to + removing ASCII whitespace. The *bytes* argument is not a suffix; rather, all combinations of its values are stripped:: >>> b' spacious '.rstrip() @@ -3567,14 +4036,13 @@ produce new objects. [b'1', b'2', b'3'] -.. method:: bytes.strip([chars]) - bytearray.strip([chars]) +.. method:: bytes.strip(bytes=None, /) + bytearray.strip(bytes=None, /) Return a copy of the sequence with specified leading and trailing bytes - removed. The *chars* argument is a binary sequence specifying the set of - byte values to be removed - the name refers to the fact this method is - usually used with ASCII characters. If omitted or ``None``, the *chars* - argument defaults to removing ASCII whitespace. The *chars* argument is + removed. The *bytes* argument is a binary sequence specifying the set of + byte values to be removed. If omitted or ``None``, the *bytes* + argument defaults to removing ASCII whitespace. The *bytes* argument is not a prefix or suffix; rather, all combinations of its values are stripped:: @@ -3896,8 +4364,8 @@ place, and instead produce new objects. always produces a new object, even if no changes were made. -.. method:: bytes.zfill(width) - bytearray.zfill(width) +.. method:: bytes.zfill(width, /) + bytearray.zfill(width, /) Return a copy of the sequence left filled with ASCII ``b'0'`` digits to make a sequence of length *width*. A leading sign prefix (``b'+'``/ @@ -4145,7 +4613,10 @@ copying. types such as :class:`bytes` and :class:`bytearray`, an element is a single byte, but other types such as :class:`array.array` may have bigger elements. - ``len(view)`` is equal to the length of :class:`~memoryview.tolist`, which + :class:`!memoryview`\s are :ref:`generic ` over the type of their + underlying data. + + ``len(view)`` is equal to the length of :meth:`~memoryview.tolist`, which is the nested list representation of the view. If ``view.ndim = 1``, this is equal to the number of elements in the view. @@ -4309,7 +4780,8 @@ copying. in-memory Fortran order is preserved. For non-contiguous views, the data is converted to C first. *order=None* is the same as *order='C'*. - .. method:: hex([sep[, bytes_per_sep]]) + .. method:: hex(*, bytes_per_sep=1) + hex(sep, bytes_per_sep=1) Return a string object containing two hexadecimal digits for each byte in the buffer. :: @@ -4394,7 +4866,8 @@ copying. .. versionadded:: 3.2 - .. method:: cast(format[, shape]) + .. method:: cast(format, /) + cast(format, shape, /) Cast a memoryview to a new format or shape. *shape* defaults to ``[byte_length//new_itemsize]``, which means that the result view @@ -4493,7 +4966,7 @@ copying. .. versionadded:: 3.14 - .. method:: index(value, start=0, stop=sys.maxsize, /) + .. method:: index(value, start=0, stop=sys.maxsize, /) Return the index of the first occurrence of *value* (at or after index *start* and before index *stop*). @@ -4622,6 +5095,9 @@ copying. .. versionadded:: 3.3 +For information on the thread safety of :class:`memoryview` objects in +the :term:`free-threaded build`, see :ref:`thread-safety-memoryview`. + .. _types-set: @@ -4644,11 +5120,12 @@ other sequence-like behavior. There are currently two built-in set types, :class:`set` and :class:`frozenset`. The :class:`set` type is mutable --- the contents can be changed using methods -like :meth:`~set.add` and :meth:`~set.remove`. Since it is mutable, it has no -hash value and cannot be used as either a dictionary key or as an element of -another set. The :class:`frozenset` type is immutable and :term:`hashable` --- -its contents cannot be altered after it is created; it can therefore be used as -a dictionary key or as an element of another set. +like :meth:`~set.add` and :meth:`~set.remove`. +Since it is mutable, it has no hash value and cannot be used as +either a dictionary key or as an element of another set. +The :class:`frozenset` type is immutable and :term:`hashable` --- +its contents cannot be altered after it is created; +it can therefore be used as a dictionary key or as an element of another set. Non-empty sets (not frozensets) can be created by placing a comma-separated list of elements within braces, for example: ``{'jack', 'sjoerd'}``, in addition to the @@ -4656,8 +5133,8 @@ of elements within braces, for example: ``{'jack', 'sjoerd'}``, in addition to t The constructors for both classes work the same: -.. class:: set([iterable]) - frozenset([iterable]) +.. class:: set(iterable=(), /) + frozenset(iterable=(), /) Return a new set or frozenset object whose elements are taken from *iterable*. The elements of a set must be :term:`hashable`. To @@ -4665,170 +5142,185 @@ The constructors for both classes work the same: objects. If *iterable* is not specified, a new empty set is returned. - Sets can be created by several means: +Sets can be created by several means: - * Use a comma-separated list of elements within braces: ``{'jack', 'sjoerd'}`` - * Use a set comprehension: ``{c for c in 'abracadabra' if c not in 'abc'}`` - * Use the type constructor: ``set()``, ``set('foobar')``, ``set(['a', 'b', 'foo'])`` +* Use a comma-separated list of elements within braces: ``{'jack', 'sjoerd'}`` +* Use a set comprehension: ``{c for c in 'abracadabra' if c not in 'abc'}`` +* Use the type constructor: ``set()``, ``set('foobar')``, ``set(['a', 'b', 'foo'])`` - Instances of :class:`set` and :class:`frozenset` provide the following - operations: +Instances of :class:`set` and :class:`frozenset` provide the following +operations: - .. describe:: len(s) +.. describe:: len(s) - Return the number of elements in set *s* (cardinality of *s*). + Return the number of elements in set *s* (cardinality of *s*). - .. describe:: x in s +.. describe:: x in s - Test *x* for membership in *s*. + Test *x* for membership in *s*. - .. describe:: x not in s +.. describe:: x not in s - Test *x* for non-membership in *s*. + Test *x* for non-membership in *s*. - .. method:: isdisjoint(other) +.. method:: frozenset.isdisjoint(other, /) + set.isdisjoint(other, /) - Return ``True`` if the set has no elements in common with *other*. Sets are - disjoint if and only if their intersection is the empty set. + Return ``True`` if the set has no elements in common with *other*. Sets are + disjoint if and only if their intersection is the empty set. - .. method:: issubset(other) - set <= other +.. method:: frozenset.issubset(other, /) + set.issubset(other, /) +.. describe:: set <= other - Test whether every element in the set is in *other*. + Test whether every element in the set is in *other*. - .. method:: set < other +.. describe:: set < other - Test whether the set is a proper subset of *other*, that is, - ``set <= other and set != other``. + Test whether the set is a proper subset of *other*, that is, + ``set <= other and set != other``. - .. method:: issuperset(other) - set >= other +.. method:: frozenset.issuperset(other, /) + set.issuperset(other, /) +.. describe:: set >= other - Test whether every element in *other* is in the set. + Test whether every element in *other* is in the set. - .. method:: set > other +.. describe:: set > other - Test whether the set is a proper superset of *other*, that is, ``set >= - other and set != other``. + Test whether the set is a proper superset of *other*, that is, ``set >= + other and set != other``. - .. method:: union(*others) - set | other | ... +.. method:: frozenset.union(*others) + set.union(*others) +.. describe:: set | other | ... - Return a new set with elements from the set and all others. + Return a new set with elements from the set and all others. - .. method:: intersection(*others) - set & other & ... +.. method:: frozenset.intersection(*others) + set.intersection(*others) +.. describe:: set & other & ... - Return a new set with elements common to the set and all others. + Return a new set with elements common to the set and all others. - .. method:: difference(*others) - set - other - ... +.. method:: frozenset.difference(*others) + set.difference(*others) +.. describe:: set - other - ... - Return a new set with elements in the set that are not in the others. + Return a new set with elements in the set that are not in the others. - .. method:: symmetric_difference(other) - set ^ other +.. method:: frozenset.symmetric_difference(other, /) + set.symmetric_difference(other, /) +.. describe:: set ^ other - Return a new set with elements in either the set or *other* but not both. + Return a new set with elements in either the set or *other* but not both. - .. method:: copy() +.. method:: frozenset.copy() + set.copy() - Return a shallow copy of the set. + Return a shallow copy of the set. - Note, the non-operator versions of :meth:`union`, :meth:`intersection`, - :meth:`difference`, :meth:`symmetric_difference`, :meth:`issubset`, and - :meth:`issuperset` methods will accept any iterable as an argument. In - contrast, their operator based counterparts require their arguments to be - sets. This precludes error-prone constructions like ``set('abc') & 'cbs'`` - in favor of the more readable ``set('abc').intersection('cbs')``. +Note, the non-operator versions of :meth:`~frozenset.union`, +:meth:`~frozenset.intersection`, :meth:`~frozenset.difference`, :meth:`~frozenset.symmetric_difference`, :meth:`~frozenset.issubset`, and +:meth:`~frozenset.issuperset` methods will accept any iterable as an argument. In +contrast, their operator based counterparts require their arguments to be +sets. This precludes error-prone constructions like ``set('abc') & 'cbs'`` +in favor of the more readable ``set('abc').intersection('cbs')``. - Both :class:`set` and :class:`frozenset` support set to set comparisons. Two - sets are equal if and only if every element of each set is contained in the - other (each is a subset of the other). A set is less than another set if and - only if the first set is a proper subset of the second set (is a subset, but - is not equal). A set is greater than another set if and only if the first set - is a proper superset of the second set (is a superset, but is not equal). +Both :class:`set` and :class:`frozenset` support set to set comparisons. Two +sets are equal if and only if every element of each set is contained in the +other (each is a subset of the other). A set is less than another set if and +only if the first set is a proper subset of the second set (is a subset, but +is not equal). A set is greater than another set if and only if the first set +is a proper superset of the second set (is a superset, but is not equal). - Instances of :class:`set` are compared to instances of :class:`frozenset` - based on their members. For example, ``set('abc') == frozenset('abc')`` - returns ``True`` and so does ``set('abc') in set([frozenset('abc')])``. +Instances of :class:`set` are compared to instances of :class:`frozenset` +based on their members. For example, ``set('abc') == frozenset('abc')`` +returns ``True`` and so does ``set('abc') in set([frozenset('abc')])``. - The subset and equality comparisons do not generalize to a total ordering - function. For example, any two nonempty disjoint sets are not equal and are not - subsets of each other, so *all* of the following return ``False``: ``ab``. +The subset and equality comparisons do not generalize to a total ordering +function. For example, any two nonempty disjoint sets are not equal and are not +subsets of each other, so *all* of the following return ``False``: ``ab``. - Since sets only define partial ordering (subset relationships), the output of - the :meth:`list.sort` method is undefined for lists of sets. +Since sets only define partial ordering (subset relationships), the output of +the :meth:`list.sort` method is undefined for lists of sets. - Set elements, like dictionary keys, must be :term:`hashable`. +Set elements, like dictionary keys, must be :term:`hashable`. - Binary operations that mix :class:`set` instances with :class:`frozenset` - return the type of the first operand. For example: ``frozenset('ab') | - set('bc')`` returns an instance of :class:`frozenset`. +Binary operations that mix :class:`set` instances with :class:`frozenset` +return the type of the first operand. For example: ``frozenset('ab') | +set('bc')`` returns an instance of :class:`frozenset`. - The following table lists operations available for :class:`set` that do not - apply to immutable instances of :class:`frozenset`: +The following table lists operations available for :class:`set` that do not +apply to immutable instances of :class:`frozenset`: - .. method:: update(*others) - set |= other | ... +.. method:: set.update(*others) +.. describe:: set |= other | ... - Update the set, adding elements from all others. + Update the set, adding elements from all others. - .. method:: intersection_update(*others) - set &= other & ... +.. method:: set.intersection_update(*others) +.. describe:: set &= other & ... - Update the set, keeping only elements found in it and all others. + Update the set, keeping only elements found in it and all others. - .. method:: difference_update(*others) - set -= other | ... +.. method:: set.difference_update(*others) +.. describe:: set -= other | ... - Update the set, removing elements found in others. + Update the set, removing elements found in others. - .. method:: symmetric_difference_update(other) - set ^= other +.. method:: set.symmetric_difference_update(other, /) +.. describe:: set ^= other - Update the set, keeping only elements found in either set, but not in both. + Update the set, keeping only elements found in either set, but not in both. - .. method:: add(elem) +.. method:: set.add(elem, /) - Add element *elem* to the set. + Add element *elem* to the set. - .. method:: remove(elem) +.. method:: set.remove(elem, /) - Remove element *elem* from the set. Raises :exc:`KeyError` if *elem* is - not contained in the set. + Remove element *elem* from the set. Raises :exc:`KeyError` if *elem* is + not contained in the set. - .. method:: discard(elem) +.. method:: set.discard(elem, /) - Remove element *elem* from the set if it is present. + Remove element *elem* from the set if it is present. - .. method:: pop() +.. method:: set.pop() - Remove and return an arbitrary element from the set. Raises - :exc:`KeyError` if the set is empty. + Remove and return an arbitrary element from the set. Raises + :exc:`KeyError` if the set is empty. - .. method:: clear() +.. method:: set.clear() + + Remove all elements from the set. + + +Note, the non-operator versions of the :meth:`~set.update`, +:meth:`~set.intersection_update`, :meth:`~set.difference_update`, and +:meth:`~set.symmetric_difference_update` methods will accept any iterable as an +argument. - Remove all elements from the set. +Note, the *elem* argument to the :meth:`~object.__contains__`, +:meth:`~set.remove`, and +:meth:`~set.discard` methods may be a set. To support searching for an equivalent +frozenset, a temporary one is created from *elem*. +Sets and frozensets are :ref:`generic ` over the type of their elements. - Note, the non-operator versions of the :meth:`update`, - :meth:`intersection_update`, :meth:`difference_update`, and - :meth:`symmetric_difference_update` methods will accept any iterable as an - argument. +.. seealso:: - Note, the *elem* argument to the :meth:`~object.__contains__`, - :meth:`remove`, and - :meth:`discard` methods may be a set. To support searching for an equivalent - frozenset, a temporary one is created from *elem*. + For detailed information on thread-safety guarantees for :class:`set` + objects, see :ref:`thread-safety-set`. .. _typesmapping: -Mapping Types --- :class:`dict` -=============================== +Mapping types --- :class:`!dict`, :class:`!frozendict` +====================================================== .. index:: pair: object; mapping @@ -4839,8 +5331,9 @@ Mapping Types --- :class:`dict` pair: built-in function; len A :term:`mapping` object maps :term:`hashable` values to arbitrary objects. -Mappings are mutable objects. There is currently only one standard mapping -type, the :dfn:`dictionary`. (For other containers see the built-in +There are currently two standard mapping types, the :dfn:`dictionary` and +:class:`frozendict`. +(For other containers see the built-in :class:`list`, :class:`set`, and :class:`tuple` classes, and the :mod:`collections` module.) @@ -4852,8 +5345,8 @@ Values that compare equal (such as ``1``, ``1.0``, and ``True``) can be used interchangeably to index the same dictionary entry. .. class:: dict(**kwargs) - dict(mapping, **kwargs) - dict(iterable, **kwargs) + dict(mapping, /, **kwargs) + dict(iterable, /, **kwargs) Return a new dictionary initialized from an optional positional argument and a possibly empty set of keyword arguments. @@ -4881,9 +5374,6 @@ can be used interchangeably to index the same dictionary entry. being added is already present, the value from the keyword argument replaces the value from the positional argument. - Providing keyword arguments as in the first example only works for keys that - are valid Python identifiers. Otherwise, any valid keys can be used. - Dictionaries compare equal if and only if they have the same ``(key, value)`` pairs (regardless of ordering). Order comparisons ('<', '<=', '>=', '>') raise :exc:`TypeError`. To illustrate dictionary creation and equality, @@ -4924,6 +5414,9 @@ can be used interchangeably to index the same dictionary entry. Dictionary order is guaranteed to be insertion order. This behavior was an implementation detail of CPython from 3.6. + Dictionaries are :ref:`generic ` over two types, signifying + (respectively) the types of the dictionary's keys and values. + These are the operations that dictionaries support (and therefore, custom mapping types should support too): @@ -4942,13 +5435,13 @@ can be used interchangeably to index the same dictionary entry. .. index:: __missing__() - If a subclass of dict defines a method :meth:`__missing__` and *key* + If a subclass of dict defines a method :meth:`~object.__missing__` and *key* is not present, the ``d[key]`` operation calls that method with the key *key* as argument. The ``d[key]`` operation then returns or raises whatever is returned or raised by the ``__missing__(key)`` call. - No other operations or methods invoke :meth:`__missing__`. If - :meth:`__missing__` is not defined, :exc:`KeyError` is raised. - :meth:`__missing__` must be a method; it cannot be an instance variable:: + No other operations or methods invoke :meth:`~object.__missing__`. If + :meth:`~object.__missing__` is not defined, :exc:`KeyError` is raised. + :meth:`~object.__missing__` must be a method; it cannot be an instance variable:: >>> class Counter(dict): ... def __missing__(self, key): @@ -4962,7 +5455,8 @@ can be used interchangeably to index the same dictionary entry. 1 The example above shows part of the implementation of - :class:`collections.Counter`. A different ``__missing__`` method is used + :class:`collections.Counter`. + A different :meth:`!__missing__` method is used by :class:`collections.defaultdict`. .. describe:: d[key] = value @@ -5021,7 +5515,8 @@ can be used interchangeably to index the same dictionary entry. Return a new view of the dictionary's keys. See the :ref:`documentation of view objects `. - .. method:: pop(key[, default]) + .. method:: pop(key, /) + pop(key, default, /) If *key* is in the dictionary, remove it and return its value, else return *default*. If *default* is not given and *key* is not in the dictionary, @@ -5053,9 +5548,11 @@ can be used interchangeably to index the same dictionary entry. with a value of *default* and return *default*. *default* defaults to ``None``. - .. method:: update([other]) + .. method:: update(**kwargs) + update(mapping, /, **kwargs) + update(iterable, /, **kwargs) - Update the dictionary with the key/value pairs from *other*, overwriting + Update the dictionary with the key/value pairs from *mapping* or *iterable* and *kwargs*, overwriting existing keys. Return ``None``. :meth:`update` accepts either another object with a ``keys()`` method (in @@ -5109,9 +5606,14 @@ can be used interchangeably to index the same dictionary entry. Dictionaries are now reversible. + .. seealso:: + :class:`frozendict` and :class:`types.MappingProxyType` can be used to + create a read-only view of a :class:`dict`. + .. seealso:: - :class:`types.MappingProxyType` can be used to create a read-only view - of a :class:`dict`. + + For detailed information on thread-safety guarantees for :class:`dict` + objects, see :ref:`thread-safety-dict`. .. _dict-views: @@ -5220,6 +5722,44 @@ An example of dictionary view usage:: 500 +Frozen dictionaries +------------------- + +.. class:: frozendict(**kwargs) + frozendict(mapping, /, **kwargs) + frozendict(iterable, /, **kwargs) + + Return a new frozen dictionary initialized from an optional positional + argument and a possibly empty set of keyword arguments. + + A :class:`!frozendict` has a similar API to the :class:`dict` API, with the + following differences: + + * :class:`!dict` has more methods than :class:`!frozendict`: + + * :meth:`!__delitem__` + * :meth:`!__setitem__` + * :meth:`~dict.clear` + * :meth:`~dict.pop` + * :meth:`~dict.popitem` + * :meth:`~dict.setdefault` + * :meth:`~dict.update` + + * A :class:`!frozendict` can be hashed with ``hash(frozendict)`` if all keys and + values can be hashed. + + * ``frozendict |= other`` does not modify the :class:`!frozendict` in-place but + creates a new frozen dictionary. + + :class:`!frozendict` is not a :class:`!dict` subclass but inherits directly + from ``object``. + + Like dictionaries, frozendicts are :ref:`generic ` over two types, + signifying (respectively) the types of the frozendict's keys and values. + + .. versionadded:: 3.15 + + .. _typecontextmanager: Context Manager Types @@ -5265,9 +5805,11 @@ before the statement body is executed and exited when the statement ends: Returning a true value from this method will cause the :keyword:`with` statement to suppress the exception and continue execution with the statement immediately following the :keyword:`!with` statement. Otherwise the exception continues - propagating after this method has finished executing. Exceptions that occur - during execution of this method will replace any exception that occurred in the - body of the :keyword:`!with` statement. + propagating after this method has finished executing. + + If this method raises an exception while handling an earlier exception from the + :keyword:`with` block, the new exception is raised, and the original exception + is stored in its :attr:`~BaseException.__context__` attribute. The exception passed in should never be reraised explicitly - instead, this method should return a false value to indicate that the method completed @@ -5356,7 +5898,8 @@ type and the :class:`bytes` data type: ``GenericAlias`` objects are instances of the class :class:`types.GenericAlias`, which can also be used to create ``GenericAlias`` -objects directly. +objects directly. Specializations of user-defined :ref:`generic classes ` +may not be instances of :class:`types.GenericAlias`, but they provide similar functionality. .. describe:: T[X, Y, ...] @@ -5405,6 +5948,15 @@ creation:: >>> type(l) + +Instances of ``GenericAlias`` are not classes at runtime, even though they behave like classes (they can be instantiated and subclassed):: + + >>> import inspect + >>> inspect.isclass(list[int]) + False + +This is true for :ref:`user-defined generics ` also. + Calling :func:`repr` or :func:`str` on a generic shows the parameterized type:: >>> repr(list[int]) @@ -5441,6 +5993,7 @@ list is non-exhaustive. * :class:`list` * :class:`dict` * :class:`set` +* :class:`frozendict` * :class:`frozenset` * :class:`type` * :class:`asyncio.Future` @@ -5468,6 +6021,7 @@ list is non-exhaustive. * :class:`collections.abc.MutableMapping` * :class:`collections.abc.Sequence` * :class:`collections.abc.MutableSequence` +* :class:`collections.abc.ByteString` * :class:`collections.abc.MappingView` * :class:`collections.abc.KeysView` * :class:`collections.abc.ItemsView` @@ -5743,9 +6297,10 @@ Methods .. index:: pair: object; method -Methods are functions that are called using the attribute notation. There are -two flavors: :ref:`built-in methods ` (such as :meth:`append` -on lists) and :ref:`class instance method `. +Methods are functions that are called using the attribute notation. +There are two flavors: :ref:`built-in methods ` +(such as :meth:`~list.append` on lists) +and :ref:`class instance method `. Built-in methods are described with the types that support them. If you access a method (a function defined in a class namespace) through an @@ -5851,13 +6406,34 @@ It is written as ``None``. The Ellipsis Object ------------------- -This object is commonly used by slicing (see :ref:`slicings`). It supports no -special operations. There is exactly one ellipsis object, named +This object is commonly used to indicate that something is omitted. +It supports no special operations. There is exactly one ellipsis object, named :const:`Ellipsis` (a built-in name). ``type(Ellipsis)()`` produces the :const:`Ellipsis` singleton. It is written as ``Ellipsis`` or ``...``. +In typical use, ``...`` as the ``Ellipsis`` object appears in a few different +places, for instance: + +- In type annotations, such as :ref:`callable arguments ` + or :ref:`tuple elements `. + +- As the body of a function instead of a :ref:`pass statement `. + +- In third-party libraries, such as `Numpy's slicing and striding + `_. + +Python also uses three dots in ways that are not ``Ellipsis`` objects, for instance: + +- Doctest's :const:`ELLIPSIS `, as a pattern for missing content. + +- The default Python prompt of the :term:`interactive` shell when partial input is incomplete. + +Lastly, the Python documentation often uses three dots in conventional English +usage to mean omitted content, even in code examples that also use them as the +``Ellipsis``. + .. _bltin-notimplemented-object: diff --git a/Doc/library/string.rst b/Doc/library/string.rst index 83e8ee2722ed8a..be968a3c53d843 100644 --- a/Doc/library/string.rst +++ b/Doc/library/string.rst @@ -4,7 +4,7 @@ .. module:: string :synopsis: Common string operations. -**Source code:** :source:`Lib/string.py` +**Source code:** :source:`Lib/string/__init__.py` -------------- @@ -82,12 +82,12 @@ The constants defined in this module are: .. _string-formatting: -Custom String Formatting +Custom string formatting ------------------------ The built-in string class provides the ability to do complex variable substitutions and value formatting via the :meth:`~str.format` method described in -:pep:`3101`. The :class:`Formatter` class in the :mod:`string` module allows +:pep:`3101`. The :class:`Formatter` class in the :mod:`!string` module allows you to create and customize your own string formatting behaviors using the same implementation as the built-in :meth:`~str.format` method. @@ -192,7 +192,7 @@ implementation as the built-in :meth:`~str.format` method. .. _formatstrings: -Format String Syntax +Format string syntax -------------------- The :meth:`str.format` method and the :class:`Formatter` class share the same @@ -200,7 +200,7 @@ syntax for format strings (although in the case of :class:`Formatter`, subclasses can define their own format string syntax). The syntax is related to that of :ref:`formatted string literals ` and :ref:`template string literals `, but it is less sophisticated -and, in particular, does not support arbitrary expressions. +and, in particular, does not support arbitrary expressions in interpolations. .. index:: single: {} (curly brackets); in string formatting @@ -304,7 +304,7 @@ See the :ref:`formatexamples` section for some examples. .. _formatspec: -Format Specification Mini-Language +Format specification mini-language ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "Format specifications" are used within replacement fields contained within a @@ -472,7 +472,9 @@ of a number respectively. It can be one of the following: | | this option is not supported. | +---------+----------------------------------------------------------+ -For a locale aware separator, use the ``'n'`` presentation type instead. +For a locale-aware separator, use the ``'n'`` +:ref:`float presentation type ` or +:ref:`integer presentation type ` instead. .. versionchanged:: 3.1 Added the ``','`` option (see also :pep:`378`). @@ -518,9 +520,14 @@ The available integer presentation types are: | | In case ``'#'`` is specified, the prefix ``'0x'`` will | | | be upper-cased to ``'0X'`` as well. | +---------+----------------------------------------------------------+ - | ``'n'`` | Number. This is the same as ``'d'``, except that it uses | + | ``'n'`` | .. _n-format-integer: | + | | | + | | Number. This is the same as ``'d'``, except that it uses | | | the current locale setting to insert the appropriate | - | | digit group separators. | + | | digit group separators. Note that the default locale is | + | | not the system locale. Depending on your use case, you | + | | may wish to set :const:`~locale.LC_NUMERIC` with | + | | :func:`locale.setlocale` before using ``'n'``. | +---------+----------------------------------------------------------+ | None | The same as ``'d'``. | +---------+----------------------------------------------------------+ @@ -546,6 +553,9 @@ The available presentation types for :class:`float` and | | :class:`float`, and shows all coefficient digits | | | for :class:`~decimal.Decimal`. If ``p=0``, the decimal | | | point is omitted unless the ``#`` option is used. | + | | | + | | For :class:`float`, the exponent always contains at | + | | least two digits, and is zero if the value is zero. | +---------+----------------------------------------------------------+ | ``'E'`` | Scientific notation. Same as ``'e'`` except it uses | | | an upper case 'E' as the separator character. | @@ -600,10 +610,15 @@ The available presentation types for :class:`float` and | | ``'E'`` if the number gets too large. The | | | representations of infinity and NaN are uppercased, too. | +---------+----------------------------------------------------------+ - | ``'n'`` | Number. This is the same as ``'g'``, except that it uses | + | ``'n'`` | .. _n-format-float: | + | | | + | | Number. This is the same as ``'g'``, except that it uses | | | the current locale setting to insert the appropriate | - | | digit group separators | - | | for the integral part of a number. | + | | digit group separators for the integral part of a | + | | number. Note that the default locale is not the system | + | | locale. Depending on your use case, you may wish to set | + | | :const:`~locale.LC_NUMERIC` with | + | | :func:`locale.setlocale` before using ``'n'``. | +---------+----------------------------------------------------------+ | ``'%'`` | Percentage. Multiplies the number by 100 and displays | | | in fixed (``'f'``) format, followed by a percent sign. | @@ -756,8 +771,8 @@ Expressing a percentage:: Using type-specific formatting:: - >>> import datetime - >>> d = datetime.datetime(2010, 7, 4, 12, 15, 58) + >>> import datetime as dt + >>> d = dt.datetime(2010, 7, 4, 12, 15, 58) >>> '{:%Y-%m-%d %H:%M:%S}'.format(d) '2010-07-04 12:15:58' @@ -799,13 +814,15 @@ Template strings ($-strings) .. note:: - The feature described here was introduced in Python 2.4. It is unrelated - to, and should not be confused with, the newer - :ref:`template strings ` and - :ref:`t-string literal syntax ` introduced in Python 3.14. - T-string literals evaluate to instances of a different - :class:`~string.templatelib.Template` class, found in the - :mod:`string.templatelib` module. + The feature described here was introduced in Python 2.4; + a simple templating method based upon regular expressions. + It predates :meth:`str.format`, :ref:`formatted string literals `, + and :ref:`template string literals `. + + It is unrelated to template string literals (t-strings), + which were introduced in Python 3.14. + These evaluate to :class:`string.templatelib.Template` objects, + found in the :mod:`string.templatelib` module. Template strings provide simpler string substitutions as described in :pep:`292`. A primary use case for template strings is for @@ -835,7 +852,7 @@ Template strings support ``$``-based substitutions, using the following rules: Any other appearance of ``$`` in the string will result in a :exc:`ValueError` being raised. -The :mod:`string` module provides a :class:`Template` class that implements +The :mod:`!string` module provides a :class:`Template` class that implements these rules. The methods of :class:`Template` are: diff --git a/Doc/library/string.templatelib.rst b/Doc/library/string.templatelib.rst index 31b90d75f411f0..6e91850fdf59ca 100644 --- a/Doc/library/string.templatelib.rst +++ b/Doc/library/string.templatelib.rst @@ -11,8 +11,8 @@ .. seealso:: * :ref:`Format strings ` - * :ref:`T-string literal syntax ` - + * :ref:`Template string literal (t-string) syntax ` + * :pep:`750` .. _template-strings: @@ -21,278 +21,316 @@ Template strings .. versionadded:: 3.14 -Template strings are a formatting mechanism that allows for deep control over -how strings are processed. You can create templates using -:ref:`t-string literal syntax `, which is identical to -:ref:`f-string syntax ` but uses a ``t`` instead of an ``f``. -While f-strings evaluate to ``str``, t-strings create a :class:`Template` -instance that gives you access to the static and interpolated (in curly braces) -parts of a string *before* they are combined. - - -.. _templatelib-template: +Template strings are a mechanism for custom string processing. +They have the full flexibility of Python's :ref:`f-strings`, +but return a :class:`Template` instance that gives access +to the static and interpolated (in curly brackets) parts of a string +*before* they are combined. -Template --------- +To write a t-string, use a ``'t'`` prefix instead of an ``'f'``, like so: -The :class:`!Template` class describes the contents of a template string. +.. code-block:: pycon -:class:`!Template` instances are immutable: their attributes cannot be -reassigned. + >>> pi = 3.14 + >>> t't-strings are new in Python {pi!s}!' + Template( + strings=('t-strings are new in Python ', '!'), + interpolations=(Interpolation(3.14, 'pi', 's', ''),) + ) -.. class:: Template(*args) +Types +----- - Create a new :class:`!Template` object. +.. class:: Template - :param args: A mix of strings and :class:`Interpolation` instances in any order. - :type args: str | Interpolation + The :class:`!Template` class describes the contents of a template string. + It is immutable, meaning that attributes of a template cannot be reassigned. The most common way to create a :class:`!Template` instance is to use the - :ref:`t-string literal syntax `. This syntax is identical to that of - :ref:`f-strings ` except that it uses a ``t`` instead of an ``f``: + :ref:`template string literal syntax `. + This syntax is identical to that of :ref:`f-strings `, + except that it uses a ``t`` prefix in place of an ``f``: - >>> name = "World" - >>> template = t"Hello {name}!" + >>> cheese = 'Red Leicester' + >>> template = t"We're fresh out of {cheese}, sir." >>> type(template) - Templates ars stored as sequences of literal :attr:`~Template.strings` + Templates are stored as sequences of literal :attr:`~Template.strings` and dynamic :attr:`~Template.interpolations`. - A :attr:`~Template.values` attribute holds the interpolation values: + A :attr:`~Template.values` attribute holds the values of the interpolations: + >>> cheese = 'Camembert' + >>> template = t'Ah! We do have {cheese}.' >>> template.strings - ('Hello ', '!') + ('Ah! We do have ', '.') >>> template.interpolations - (Interpolation('World', ...),) + (Interpolation('Camembert', ...),) >>> template.values - ('World',) + ('Camembert',) The :attr:`!strings` tuple has one more element than :attr:`!interpolations` and :attr:`!values`; the interpolations “belong” between the strings. - This may be easier to understand when tuples are aligned:: + This may be easier to understand when tuples are aligned - template.strings: ('Hello ', '!') - template.values: ( 'World', ) + .. code-block:: python - While literal syntax is the most common way to create :class:`!Template` - instances, it is also possible to create them directly using the constructor: + template.strings: ('Ah! We do have ', '.') + template.values: ( 'Camembert', ) - >>> from string.templatelib import Interpolation, Template - >>> name = "World" - >>> template = Template("Hello, ", Interpolation(name, "name"), "!") - >>> list(template) - ['Hello, ', Interpolation('World', 'name', None, ''), '!'] + .. rubric:: Attributes - If two or more consecutive strings are passed, they will be concatenated - into a single value in the :attr:`~Template.strings` attribute. For example, - the following code creates a :class:`Template` with a single final string: + .. attribute:: strings + :type: tuple[str, ...] - >>> from string.templatelib import Template - >>> template = Template("Hello ", "World", "!") - >>> template.strings - ('Hello World!',) + A :class:`tuple` of the static strings in the template. - If two or more consecutive interpolations are passed, they will be treated - as separate interpolations and an empty string will be inserted between them. - For example, the following code creates a template with empty placeholders - in the :attr:`~Template.strings` attribute: + >>> cheese = 'Camembert' + >>> template = t'Ah! We do have {cheese}.' + >>> template.strings + ('Ah! We do have ', '.') - >>> from string.templatelib import Interpolation, Template - >>> template = Template(Interpolation("World", "name"), Interpolation("!", "punctuation")) - >>> template.strings - ('', '', '') + Empty strings *are* included in the tuple: - .. attribute:: strings - :type: tuple[str, ...] + >>> response = 'We do have ' + >>> cheese = 'Camembert' + >>> template = t'Ah! {response}{cheese}.' + >>> template.strings + ('Ah! ', '', '.') - A :ref:`tuple ` of the static strings in the template. + The ``strings`` tuple is never empty, and always contains one more + string than the ``interpolations`` and ``values`` tuples: - >>> name = "World" - >>> t"Hello {name}!".strings - ('Hello ', '!') + >>> t''.strings + ('',) + >>> t''.values + () + >>> t'{'cheese'}'.strings + ('', '') + >>> t'{'cheese'}'.values + ('cheese',) - Empty strings *are* included in the tuple: + .. attribute:: interpolations + :type: tuple[Interpolation, ...] - >>> name = "World" - >>> t"Hello {name}{name}!".strings - ('Hello ', '', '!') + A :class:`tuple` of the interpolations in the template. - The ``strings`` tuple is never empty, and always contains one more - string than the ``interpolations`` and ``values`` tuples: + >>> cheese = 'Camembert' + >>> template = t'Ah! We do have {cheese}.' + >>> template.interpolations + (Interpolation('Camembert', 'cheese', None, ''),) - >>> t"".strings - ('',) - >>> t"".values - () - >>> t"{'cheese'}".strings - ('', '') - >>> t"{'cheese'}".values - ('cheese',) + The ``interpolations`` tuple may be empty and always contains one fewer + values than the ``strings`` tuple: - .. attribute:: interpolations - :type: tuple[Interpolation, ...] + >>> t'Red Leicester'.interpolations + () - A tuple of the interpolations in the template. + .. attribute:: values + :type: tuple[object, ...] - >>> name = "World" - >>> t"Hello {name}!".interpolations - (Interpolation('World', 'name', None, ''),) + A tuple of all interpolated values in the template. - The ``interpolations`` tuple may be empty and always contains one fewer - values than the ``strings`` tuple: + >>> cheese = 'Camembert' + >>> template = t'Ah! We do have {cheese}.' + >>> template.values + ('Camembert',) - >>> t"Hello!".interpolations - () + The ``values`` tuple always has the same length as the + ``interpolations`` tuple. It is always equivalent to + ``tuple(i.value for i in template.interpolations)``. - .. attribute:: values - :type: tuple[Any, ...] + .. rubric:: Methods - A tuple of all interpolated values in the template. + .. method:: __new__(*args: str | Interpolation) - >>> name = "World" - >>> t"Hello {name}!".values - ('World',) + While literal syntax is the most common way to create a :class:`!Template`, + it is also possible to create them directly using the constructor: - The ``values`` tuple always has the same length as the - ``interpolations`` tuple. It is equivalent to - ``tuple(i.value for i in template.interpolations)``. + >>> from string.templatelib import Interpolation, Template + >>> cheese = 'Camembert' + >>> template = Template( + ... 'Ah! We do have ', Interpolation(cheese, 'cheese'), '.' + ... ) + >>> list(template) + ['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.'] - .. describe:: iter(template) + If multiple strings are passed consecutively, they will be concatenated + into a single value in the :attr:`~Template.strings` attribute. For example, + the following code creates a :class:`Template` with a single final string: - Iterate over the template, yielding each string and - :class:`Interpolation` in order. + >>> from string.templatelib import Template + >>> template = Template('Ah! We do have ', 'Camembert', '.') + >>> template.strings + ('Ah! We do have Camembert.',) - >>> name = "World" - >>> list(t"Hello {name}!") - ['Hello ', Interpolation('World', 'name', None, ''), '!'] + If multiple interpolations are passed consecutively, they will be treated + as separate interpolations and an empty string will be inserted between them. + For example, the following code creates a template with empty placeholders + in the :attr:`~Template.strings` attribute: - Empty strings are *not* included in the iteration: + >>> from string.templatelib import Interpolation, Template + >>> template = Template( + ... Interpolation('Camembert', 'cheese'), + ... Interpolation('.', 'punctuation'), + ... ) + >>> template.strings + ('', '', '') - >>> name = "World" - >>> list(t"Hello {name}{name}") - ['Hello ', Interpolation('World', 'name', None, ''), Interpolation('World', 'name', None, '')] + .. describe:: iter(template) - .. describe:: template + other - template += other + Iterate over the template, yielding each non-empty string and + :class:`Interpolation` in the correct order: - Concatenate this template with another, returning a new - :class:`!Template` instance: + >>> cheese = 'Camembert' + >>> list(t'Ah! We do have {cheese}.') + ['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.'] - >>> name = "World" - >>> list(t"Hello " + t"there {name}!") - ['Hello there ', Interpolation('World', 'name', None, ''), '!'] + .. caution:: - Concatenation between a :class:`!Template` and a ``str`` is *not* supported. - This is because it is ambiguous whether the string should be treated as - a static string or an interpolation. If you want to concatenate a - :class:`!Template` with a string, you should either wrap the string - directly in a :class:`!Template` (to treat it as a static string) or use - an :class:`!Interpolation` (to treat it as dynamic): + Empty strings are **not** included in the iteration: - >>> from string.templatelib import Template, Interpolation - >>> template = t"Hello " - >>> # Treat "there " as a static string - >>> template += Template("there ") - >>> # Treat name as an interpolation - >>> name = "World" - >>> template += Template(Interpolation(name, "name")) - >>> list(template) - ['Hello there ', Interpolation('World', 'name', None, '')] + >>> response = 'We do have ' + >>> cheese = 'Camembert' + >>> list(t'Ah! {response}{cheese}.') # doctest: +NORMALIZE_WHITESPACE + ['Ah! ', + Interpolation('We do have ', 'response', None, ''), + Interpolation('Camembert', 'cheese', None, ''), + '.'] + .. describe:: template + other + template += other -.. class:: Interpolation(value, expression="", conversion=None, format_spec="") + Concatenate this template with another, returning a new + :class:`!Template` instance: - Create a new :class:`!Interpolation` object. + >>> cheese = 'Camembert' + >>> list(t'Ah! ' + t'We do have {cheese}.') + ['Ah! We do have ', Interpolation('Camembert', 'cheese', None, ''), '.'] - :param value: The evaluated, in-scope result of the interpolation. - :type value: object + Concatenating a :class:`!Template` and a ``str`` is **not** supported. + This is because it is unclear whether the string should be treated as + a static string or an interpolation. + If you want to concatenate a :class:`!Template` with a string, + you should either wrap the string directly in a :class:`!Template` + (to treat it as a static string) + or use an :class:`!Interpolation` (to treat it as dynamic): - :param expression: The text of a valid Python expression, or an empty string. - :type expression: str + >>> from string.templatelib import Interpolation, Template + >>> template = t'Ah! ' + >>> # Treat 'We do have ' as a static string + >>> template += Template('We do have ') + >>> # Treat cheese as an interpolation + >>> cheese = 'Camembert' + >>> template += Template(Interpolation(cheese, 'cheese')) + >>> list(template) + ['Ah! We do have ', Interpolation('Camembert', 'cheese', None, '')] - :param conversion: The optional :ref:`conversion ` to be used, one of r, s, and a. - :type conversion: ``Literal["a", "r", "s"] | None`` - :param format_spec: An optional, arbitrary string used as the :ref:`format specification ` to present the value. - :type format_spec: str +.. class:: Interpolation The :class:`!Interpolation` type represents an expression inside a template string. + It is immutable, meaning that attributes of an interpolation cannot be reassigned. - :class:`!Interpolation` instances are immutable: their attributes cannot be - reassigned. + Interpolations support pattern matching, allowing you to match against + their attributes with the :ref:`match statement `: + + >>> from string.templatelib import Interpolation + >>> interpolation = t'{1. + 2.:.2f}'.interpolations[0] + >>> interpolation + Interpolation(3.0, '1. + 2.', None, '.2f') + >>> match interpolation: + ... case Interpolation(value, expression, conversion, format_spec): + ... print(value, expression, conversion, format_spec, sep=' | ') + ... + 3.0 | 1. + 2. | None | .2f + + Interpolations are :ref:`generic ` over the types of their values. + + .. rubric:: Attributes .. attribute:: value + :type: object - :returns: The evaluated value of the interpolation. - :type: object + The evaluated value of the interpolation. - >>> t"{1 + 2}".interpolations[0].value - 3 + >>> t'{1 + 2}'.interpolations[0].value + 3 .. attribute:: expression + :type: str - :returns: The text of a valid Python expression, or an empty string. - :type: str + For interpolations created by t-string literals, :attr:`!expression` + is the expression text found inside the curly brackets (``{`` & ``}``), + including any whitespace, excluding the curly brackets themselves, + and ending before the first ``!``, ``:``, or ``=`` if any is present. + For manually created interpolations, :attr:`!expression` is the arbitrary + string provided when constructing the interpolation instance. - The :attr:`~Interpolation.expression` is the original text of the - interpolation's Python expression, if the interpolation was created - from a t-string literal. Developers creating interpolations manually - should either set this to an empty string or choose a suitable valid - Python expression. + We recommend using valid Python expressions or the empty string for the + ``expression`` field of manually created :class:`!Interpolation` + instances, although this is not enforced at runtime. - >>> t"{1 + 2}".interpolations[0].expression - '1 + 2' + >>> t'{1 + 2}'.interpolations[0].expression + '1 + 2' .. attribute:: conversion + :type: typing.Literal['a', 'r', 's'] | None - :returns: The conversion to apply to the value, or ``None``. - :type: ``Literal["a", "r", "s"] | None`` + The conversion to apply to the value, or ``None``. - The :attr:`!Interpolation.conversion` is the optional conversion to apply - to the value: + The :attr:`!conversion` is the optional conversion to apply + to the value: - >>> t"{1 + 2!a}".interpolations[0].conversion - 'a' + >>> t'{1 + 2!a}'.interpolations[0].conversion + 'a' - .. note:: + .. note:: Unlike f-strings, where conversions are applied automatically, the expected behavior with t-strings is that code that *processes* the :class:`!Template` will decide how to interpret and whether to apply - the :attr:`!Interpolation.conversion`. + the :attr:`!conversion`. + For convenience, the :func:`convert` function can be used to mimic + f-string conversion semantics. .. attribute:: format_spec + :type: str - :returns: The format specification to apply to the value. - :type: str + The format specification to apply to the value. - The :attr:`!Interpolation.format_spec` is an optional, arbitrary string - used as the format specification to present the value: + The :attr:`!format_spec` is an optional, arbitrary string + used as the format specification to present the value: - >>> t"{1 + 2:.2f}".interpolations[0].format_spec - '.2f' + >>> t'{1 + 2:.2f}'.interpolations[0].format_spec + '.2f' - .. note:: + .. note:: Unlike f-strings, where format specifications are applied automatically via the :func:`format` protocol, the expected behavior with - t-strings is that code that *processes* the :class:`!Template` will + t-strings is that code that *processes* the interpolation will decide how to interpret and whether to apply the format specification. - As a result, :attr:`!Interpolation.format_spec` values in - :class:`!Template` instances can be arbitrary strings, even those that - do not necessarily conform to the rules of Python's :func:`format` - protocol. + As a result, :attr:`!format_spec` values in interpolations + can be arbitrary strings, + including those that do not conform to the :func:`format` protocol. - Interpolations support pattern matching, allowing you to match against - their attributes with the :ref:`match statement `: + .. rubric:: Methods - >>> from string.templatelib import Interpolation - >>> interpolation = Interpolation(3.0, "1 + 2", None, ".2f") - >>> match interpolation: - ... case Interpolation(value, expression, conversion, format_spec): - ... print(value, expression, conversion, format_spec) - ... - 3.0 1 + 2 None .2f + .. method:: __new__(value: object, \ + expression: str, \ + conversion: typing.Literal['a', 'r', 's'] | None = None, \ + format_spec: str = '') + + Create a new :class:`!Interpolation` object from component parts. + + :param value: The evaluated, in-scope result of the interpolation. + :param expression: The text of a valid Python expression, + or an empty string. + :param conversion: The :ref:`conversion ` to be used, + one of ``None``, ``'a'``, ``'r'``, or ``'s'``. + :param format_spec: An optional, arbitrary string used as the + :ref:`format specification ` to present the value. Helper functions @@ -306,8 +344,8 @@ Helper functions Three conversion flags are currently supported: - * ``'s'`` which calls :func:`str` on the value, - * ``'r'`` which calls :func:`repr`, and - * ``'a'`` which calls :func:`ascii`. + * ``'s'`` which calls :func:`str` on the value (like ``!s``), + * ``'r'`` which calls :func:`repr` (like ``!r``), and + * ``'a'`` which calls :func:`ascii` (like ``!a``). If the conversion flag is ``None``, *obj* is returned unchanged. diff --git a/Doc/library/stringprep.rst b/Doc/library/stringprep.rst index 37d5adf0fa9541..325ac9ae7c5293 100644 --- a/Doc/library/stringprep.rst +++ b/Doc/library/stringprep.rst @@ -4,9 +4,6 @@ .. module:: stringprep :synopsis: String preparation, as per RFC 3453 -.. moduleauthor:: Martin v. Löwis -.. sectionauthor:: Martin v. Löwis - **Source code:** :source:`Lib/stringprep.py` -------------- @@ -26,14 +23,14 @@ define which tables it uses, and what other optional parts of the ``stringprep`` procedure are part of the profile. One example of a ``stringprep`` profile is ``nameprep``, which is used for internationalized domain names. -The module :mod:`stringprep` only exposes the tables from :rfc:`3454`. As these +The module :mod:`!stringprep` only exposes the tables from :rfc:`3454`. As these tables would be very large to represent as dictionaries or lists, the module uses the Unicode character database internally. The module source code itself was generated using the ``mkstringprep.py`` utility. As a result, these tables are exposed as functions, not as data structures. There are two kinds of tables in the RFC: sets and mappings. For a set, -:mod:`stringprep` provides the "characteristic function", i.e. a function that +:mod:`!stringprep` provides the "characteristic function", i.e. a function that returns ``True`` if the parameter is part of the set. For mappings, it provides the mapping function: given the key, it returns the associated value. Below is a list of all functions available in the module. diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst index 17fc479fd0c8c9..ec872fddee05c7 100644 --- a/Doc/library/struct.rst +++ b/Doc/library/struct.rst @@ -36,7 +36,7 @@ and the C layer. responsible for defining byte ordering and padding between elements. See :ref:`struct-alignment` for details. -Several :mod:`struct` functions (and methods of :class:`Struct`) take a *buffer* +Several :mod:`!struct` functions (and methods of :class:`Struct`) take a *buffer* argument. This refers to objects that implement the :ref:`bufferobjects` and provide either a readable or read-writable buffer. The most common types used for that purpose are :class:`bytes` and :class:`bytearray`, but many other types @@ -111,7 +111,7 @@ Format Strings -------------- Format strings describe the data layout when -packing and unpacking data. They are built up from :ref:`format characters`, +packing and unpacking data. They are built up from :ref:`type codes `, which specify the type of data being packed/unpacked. In addition, special characters control the :ref:`byte order, size and alignment`. Each format string consists of an optional prefix character which @@ -183,8 +183,8 @@ Use :data:`sys.byteorder` to check the endianness of your system. Native size and alignment are determined using the C compiler's ``sizeof`` expression. This is always combined with native byte order. -Standard size depends only on the format character; see the table in -the :ref:`format-characters` section. +Standard size depends only on the type code; see the table in +the :ref:`type-codes` section. Note the difference between ``'@'`` and ``'='``: both use native byte order, but the size and alignment of the latter is standardized. @@ -208,12 +208,13 @@ Notes: count of zero. See :ref:`struct-examples`. +.. _type-codes: .. _format-characters: -Format Characters -^^^^^^^^^^^^^^^^^ +Type Codes +^^^^^^^^^^ -Format characters have the following meaning; the conversion between C and +Type codes (or format codes) have the following meaning; the conversion between C and Python values should be obvious given their types. The 'Standard size' column refers to the size of the packed value in bytes when using standard size; that is, when the format string starts with one of ``'<'``, ``'>'``, ``'!'`` or @@ -227,34 +228,34 @@ platform-dependent. +--------+--------------------------+--------------------+----------------+------------+ | ``c`` | :c:expr:`char` | bytes of length 1 | 1 | | +--------+--------------------------+--------------------+----------------+------------+ -| ``b`` | :c:expr:`signed char` | integer | 1 | \(1), \(2) | +| ``b`` | :c:expr:`signed char` | int | 1 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``B`` | :c:expr:`unsigned char` | integer | 1 | \(2) | +| ``B`` | :c:expr:`unsigned char` | int | 1 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ | ``?`` | :c:expr:`_Bool` | bool | 1 | \(1) | +--------+--------------------------+--------------------+----------------+------------+ -| ``h`` | :c:expr:`short` | integer | 2 | \(2) | +| ``h`` | :c:expr:`short` | int | 2 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``H`` | :c:expr:`unsigned short` | integer | 2 | \(2) | +| ``H`` | :c:expr:`unsigned short` | int | 2 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``i`` | :c:expr:`int` | integer | 4 | \(2) | +| ``i`` | :c:expr:`int` | int | 4 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``I`` | :c:expr:`unsigned int` | integer | 4 | \(2) | +| ``I`` | :c:expr:`unsigned int` | int | 4 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``l`` | :c:expr:`long` | integer | 4 | \(2) | +| ``l`` | :c:expr:`long` | int | 4 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``L`` | :c:expr:`unsigned long` | integer | 4 | \(2) | +| ``L`` | :c:expr:`unsigned long` | int | 4 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``q`` | :c:expr:`long long` | integer | 8 | \(2) | +| ``q`` | :c:expr:`long long` | int | 8 | \(2) | +--------+--------------------------+--------------------+----------------+------------+ -| ``Q`` | :c:expr:`unsigned long | integer | 8 | \(2) | +| ``Q`` | :c:expr:`unsigned long | int | 8 | \(2) | | | long` | | | | +--------+--------------------------+--------------------+----------------+------------+ -| ``n`` | :c:type:`ssize_t` | integer | | \(3) | +| ``n`` | :c:type:`ssize_t` | int | | \(2), \(3) | +--------+--------------------------+--------------------+----------------+------------+ -| ``N`` | :c:type:`size_t` | integer | | \(3) | +| ``N`` | :c:type:`size_t` | int | | \(2), \(3) | +--------+--------------------------+--------------------+----------------+------------+ -| ``e`` | \(6) | float | 2 | \(4) | +| ``e`` | :c:expr:`_Float16` | float | 2 | \(4), \(6) | +--------+--------------------------+--------------------+----------------+------------+ | ``f`` | :c:expr:`float` | float | 4 | \(4) | +--------+--------------------------+--------------------+----------------+------------+ @@ -264,11 +265,15 @@ platform-dependent. +--------+--------------------------+--------------------+----------------+------------+ | ``D`` | :c:expr:`double complex` | complex | 16 | \(10) | +--------+--------------------------+--------------------+----------------+------------+ +| ``Zf`` | :c:expr:`float complex` | complex | 8 | \(10) | ++--------+--------------------------+--------------------+----------------+------------+ +| ``Zd`` | :c:expr:`double complex` | complex | 16 | \(10) | ++--------+--------------------------+--------------------+----------------+------------+ | ``s`` | :c:expr:`char[]` | bytes | | \(9) | +--------+--------------------------+--------------------+----------------+------------+ | ``p`` | :c:expr:`char[]` | bytes | | \(8) | +--------+--------------------------+--------------------+----------------+------------+ -| ``P`` | :c:expr:`void \*` | integer | | \(5) | +| ``P`` | :c:expr:`void \*` | int | | \(2), \(5) | +--------+--------------------------+--------------------+----------------+------------+ .. versionchanged:: 3.3 @@ -280,6 +285,15 @@ platform-dependent. .. versionchanged:: 3.14 Added support for the ``'F'`` and ``'D'`` formats. +.. versionchanged:: 3.15 + Added support for the ``'Zf'`` and ``'Zd'`` formats. + +.. seealso:: + + The :mod:`array` and :ref:`ctypes ` modules, + as well as third-party modules like `numpy `__, + use similar -- but slightly different -- type codes. + Notes: @@ -311,7 +325,7 @@ Notes: format used by the platform. (5) - The ``'P'`` format character is only available for the native byte ordering + The ``'P'`` type code is only available for the native byte ordering (selected as the default or with the ``'@'`` byte order character). The byte order character ``'='`` chooses to use little- or big-endian ordering based on the host system. The struct module does not interpret this as native @@ -322,7 +336,9 @@ Notes: revision of the `IEEE 754 standard `_. It has a sign bit, a 5-bit exponent and 11-bit precision (with 10 bits explicitly stored), and can represent numbers between approximately ``6.1e-05`` and ``6.5e+04`` - at full precision. This type is not widely supported by C compilers: on a + at full precision. This type is not widely supported by C compilers: + it's available as :c:expr:`_Float16` type, if the compiler supports the Annex H + of the C23 standard. On a typical machine, an unsigned short can be used for storage, but not for math operations. See the Wikipedia page on the `half-precision floating-point format `_ for more information. @@ -331,42 +347,46 @@ Notes: When packing, ``'x'`` inserts one NUL byte. (8) - The ``'p'`` format character encodes a "Pascal string", meaning a short + The ``'p'`` type code encodes a "Pascal string", meaning a short variable-length string stored in a *fixed number of bytes*, given by the count. The first byte stored is the length of the string, or 255, whichever is - smaller. The bytes of the string follow. If the string passed in to + smaller. The bytes of the string follow. If the byte string passed in to :func:`pack` is too long (longer than the count minus 1), only the leading - ``count-1`` bytes of the string are stored. If the string is shorter than + ``count-1`` bytes of the string are stored. If the byte string is shorter than ``count-1``, it is padded with null bytes so that exactly count bytes in all - are used. Note that for :func:`unpack`, the ``'p'`` format character consumes - ``count`` bytes, but that the string returned can never contain more than 255 + are used. Note that for :func:`unpack`, the ``'p'`` type code consumes + ``count`` bytes, but that the :class:`!bytes` object returned can never contain more than 255 bytes. + When packing, arguments of types :class:`bytes` and :class:`bytearray` + are accepted. (9) - For the ``'s'`` format character, the count is interpreted as the length of the - bytes, not a repeat count like for the other format characters; for example, + For the ``'s'`` type code, the count is interpreted as the length of the + byte string, not a repeat count like for the other type codes; for example, ``'10s'`` means a single 10-byte string mapping to or from a single Python byte string, while ``'10c'`` means 10 separate one byte character elements (e.g., ``cccccccccc``) mapping to or from ten different Python byte objects. (See :ref:`struct-examples` for a concrete demonstration of the difference.) - If a count is not given, it defaults to 1. For packing, the string is + If a count is not given, it defaults to 1. For packing, the byte string is truncated or padded with null bytes as appropriate to make it fit. For - unpacking, the resulting bytes object always has exactly the specified number - of bytes. As a special case, ``'0s'`` means a single, empty string (while + unpacking, the resulting :class:`!bytes` object always has exactly the specified number + of bytes. As a special case, ``'0s'`` means a single, empty byte string (while ``'0c'`` means 0 characters). + When packing, arguments of types :class:`bytes` and :class:`bytearray` + are accepted. (10) - For the ``'F'`` and ``'D'`` format characters, the packed representation uses + For the ``'F'`` and ``'D'`` type codes, the packed representation uses the IEEE 754 binary32 and binary64 format for components of the complex number, regardless of the floating-point format used by the platform. - Note that complex types (``F`` and ``D``) are available unconditionally, + Note that complex types (``F``/``Zf`` and ``D``/``Zd``) are available unconditionally, despite complex types being an optional feature in C. As specified in the C11 standard, each complex type is represented by a two-element C array containing, respectively, the real and imaginary parts. -A format character may be preceded by an integral repeat count. For example, +A type code may be preceded by an integral repeat count. For example, the format string ``'4h'`` means exactly the same as ``'hhhh'``. Whitespace characters between formats are ignored; a count and its format must @@ -383,7 +403,7 @@ then :exc:`struct.error` is raised. .. index:: single: ? (question mark); in struct format strings -For the ``'?'`` format character, the return value is either :const:`True` or +For the ``'?'`` type code, the return value is either :const:`True` or :const:`False`. When packing, the truth value of the argument object is used. Either 0 or 1 in the native or standard bool representation will be packed, and any non-zero value will be ``True`` when unpacking. @@ -438,7 +458,7 @@ the result in a named tuple:: >>> Student._make(unpack('<10sHHb', record)) Student(name=b'raymond ', serialnum=4658, school=264, gradelevel=8) -The ordering of format characters may have an impact on size in native +The ordering of type codes may have an impact on size in native mode since padding is implicit. In standard mode, the user is responsible for inserting any desired padding. Note in @@ -479,7 +499,7 @@ at the end, assuming the platform's longs are aligned on 4-byte boundaries:: Applications ------------ -Two main applications for the :mod:`struct` module exist, data +Two main applications for the :mod:`!struct` module exist, data interchange between Python and C code within an application or another application compiled using the same compiler (:ref:`native formats`), and data interchange between applications using agreed upon data layout @@ -496,7 +516,7 @@ When constructing format strings which mimic native layouts, the compiler and machine architecture determine byte ordering and padding. In such cases, the ``@`` format character should be used to specify native byte ordering and data sizes. Internal pad bytes are normally inserted -automatically. It is possible that a zero-repeat format code will be +automatically. It is possible that a zero-repeat type code will be needed at the end of a format string to round up to the correct byte boundary for proper alignment of consecutive chunks of data. @@ -515,7 +535,7 @@ code solves that problem:: >>> calcsize('@llh0l') 24 -The ``'x'`` format code can be used to specify the repeat, but for +The ``'x'`` type code can be used to specify the repeat, but for native formats it is better to use a zero-repeat format like ``'0l'``. By default, native byte ordering and alignment is used, but it is @@ -571,7 +591,7 @@ below were executed on a 32-bit machine:: Classes ------- -The :mod:`struct` module also defines the following type: +The :mod:`!struct` module also defines the following type: .. class:: Struct(format) diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index 028a7861f36798..fe64daa3291d67 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -4,21 +4,18 @@ .. module:: subprocess :synopsis: Subprocess management. -.. moduleauthor:: Peter Åstrand -.. sectionauthor:: Peter Åstrand - **Source code:** :source:`Lib/subprocess.py` -------------- -The :mod:`subprocess` module allows you to spawn new processes, connect to their +The :mod:`!subprocess` module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several older modules and functions:: os.system os.spawn* -Information about how the :mod:`subprocess` module can be used to replace these +Information about how the :mod:`!subprocess` module can be used to replace these modules and functions can be found in the following sections. .. seealso:: @@ -27,8 +24,8 @@ modules and functions can be found in the following sections. .. include:: ../includes/wasm-mobile-notavail.rst -Using the :mod:`subprocess` Module ----------------------------------- +Using the :mod:`!subprocess` Module +----------------------------------- The recommended approach to invoking subprocesses is to use the :func:`run` function for all use cases it can handle. For more advanced use cases, the @@ -630,6 +627,12 @@ functions. the value in ``pw_uid`` will be used. If the value is an integer, it will be passed verbatim. (POSIX only) + .. note:: + + Specifying *user* will not drop existing supplementary group memberships! + The caller must also pass ``extra_groups=()`` to reduce the group membership + of the child process for security purposes. + .. availability:: POSIX .. versionadded:: 3.9 @@ -649,7 +652,7 @@ functions. If specified, *env* must provide any variables required for the program to execute. On Windows, in order to run a `side-by-side assembly`_ the - specified *env* **must** include a valid :envvar:`SystemRoot`. + specified *env* **must** include a valid ``%SystemRoot%``. .. _side-by-side assembly: https://en.wikipedia.org/wiki/Side-by-Side_Assembly @@ -803,14 +806,29 @@ Instances of the :class:`Popen` class have the following methods: .. note:: - When the ``timeout`` parameter is not ``None``, then (on POSIX) the - function is implemented using a busy loop (non-blocking call and short - sleeps). Use the :mod:`asyncio` module for an asynchronous wait: see + When ``timeout`` is not ``None`` and the platform supports it, an + efficient event-driven mechanism is used to wait for process termination: + + - Linux >= 5.3 uses :func:`os.pidfd_open` + :func:`select.poll` + - macOS and other BSD variants use :func:`select.kqueue` + + ``KQ_FILTER_PROC`` + ``KQ_NOTE_EXIT`` + - Windows uses ``WaitForSingleObject`` + + If none of these mechanisms are available, the function falls back to a + busy loop (non-blocking call and short sleeps). + + .. note:: + + Use the :mod:`asyncio` module for an asynchronous wait: see :class:`asyncio.create_subprocess_exec`. .. versionchanged:: 3.3 *timeout* was added. + .. versionchanged:: 3.15 + if *timeout* is not ``None``, use efficient event-driven implementation + on Linux >= 5.3 and macOS / BSD. + .. method:: Popen.communicate(input=None, timeout=None) Interact with process: Send data to stdin. Read data from stdout and stderr, @@ -831,7 +849,9 @@ Instances of the :class:`Popen` class have the following methods: If the process does not terminate after *timeout* seconds, a :exc:`TimeoutExpired` exception will be raised. Catching this exception and - retrying communication will not lose any output. + retrying communication will not lose any output. Supplying *input* to a + subsequent post-timeout :meth:`communicate` call is in undefined behavior + and may become an error in the future. The child process is not killed if the timeout expires, so in order to cleanup properly a well-behaved application should kill the child process and @@ -844,6 +864,11 @@ Instances of the :class:`Popen` class have the following methods: proc.kill() outs, errs = proc.communicate() + After a call to :meth:`~Popen.communicate` raises :exc:`TimeoutExpired`, do + not call :meth:`~Popen.wait`. Use an additional :meth:`~Popen.communicate` + call to finish handling pipes and populate the :attr:`~Popen.returncode` + attribute. + .. note:: The data read is buffered in memory, so do not use this method if the data @@ -945,6 +970,11 @@ Reassigning them to new values is unsupported: A negative value ``-N`` indicates that the child was terminated by signal ``N`` (POSIX only). + When ``shell=True``, the return code reflects the exit status of the shell + itself (e.g. ``/bin/sh``), which may map signals to codes such as + ``128+N``. See the documentation of the shell (for example, the Bash + manual's Exit Status) for details. + Windows Popen Helpers --------------------- @@ -1034,7 +1064,7 @@ on Windows. Windows Constants ^^^^^^^^^^^^^^^^^ -The :mod:`subprocess` module exposes the following constants. +The :mod:`!subprocess` module exposes the following constants. .. data:: STD_INPUT_HANDLE @@ -1323,8 +1353,8 @@ calls these functions. .. _subprocess-replacements: -Replacing Older Functions with the :mod:`subprocess` Module ------------------------------------------------------------ +Replacing Older Functions with the :mod:`!subprocess` Module +------------------------------------------------------------ In this section, "a becomes b" means that b can be used as a replacement for a. @@ -1340,7 +1370,7 @@ In this section, "a becomes b" means that b can be used as a replacement for a. :attr:`~CalledProcessError.output` attribute of the raised exception. In the following examples, we assume that the relevant functions have already -been imported from the :mod:`subprocess` module. +been imported from the :mod:`!subprocess` module. Replacing :program:`/bin/sh` shell command substitution @@ -1400,7 +1430,7 @@ Notes: * The :func:`os.system` function ignores SIGINT and SIGQUIT signals while the command is running, but the caller must do this separately when - using the :mod:`subprocess` module. + using the :mod:`!subprocess` module. A more realistic example would look like this:: @@ -1473,7 +1503,7 @@ handling consistency are valid for these functions. Return ``(exitcode, output)`` of executing *cmd* in a shell. - Execute the string *cmd* in a shell with :meth:`Popen.check_output` and + Execute the string *cmd* in a shell with :func:`check_output` and return a 2-tuple ``(exitcode, output)``. *encoding* and *errors* are used to decode output; see the notes on :ref:`frequently-used-arguments` for more details. @@ -1584,7 +1614,7 @@ runtime): Disable use of ``posix_spawn()`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On Linux, :mod:`subprocess` defaults to using the ``vfork()`` system call +On Linux, :mod:`!subprocess` defaults to using the ``vfork()`` system call internally when it is safe to do so rather than ``fork()``. This greatly improves performance. diff --git a/Doc/library/superseded.rst b/Doc/library/superseded.rst index d120c6acf621e3..b6b2f28d067299 100644 --- a/Doc/library/superseded.rst +++ b/Doc/library/superseded.rst @@ -1,7 +1,7 @@ .. _superseded: ****************** -Superseded Modules +Superseded modules ****************** The modules described in this chapter have been superseded by other modules @@ -24,3 +24,4 @@ currently no modules in this latter category. :maxdepth: 1 getopt.rst + profile.rst diff --git a/Doc/library/symtable.rst b/Doc/library/symtable.rst index 54e19af4bd69a6..95f20b06b5aa1e 100644 --- a/Doc/library/symtable.rst +++ b/Doc/library/symtable.rst @@ -8,24 +8,26 @@ -------------- -.. moduleauthor:: Jeremy Hylton -.. sectionauthor:: Benjamin Peterson - - Symbol tables are generated by the compiler from AST just before bytecode is generated. The symbol table is responsible for calculating the scope of every -identifier in the code. :mod:`symtable` provides an interface to examine these +identifier in the code. :mod:`!symtable` provides an interface to examine these tables. Generating Symbol Tables ------------------------ -.. function:: symtable(code, filename, compile_type) +.. function:: symtable(code, filename, compile_type, *, module=None) Return the toplevel :class:`SymbolTable` for the Python source *code*. *filename* is the name of the file containing the code. *compile_type* is like the *mode* argument to :func:`compile`. + The optional argument *module* specifies the module name. + It is needed to unambiguous :ref:`filter ` syntax warnings + by module name. + + .. versionadded:: 3.15 + Added the *module* parameter. Examining Symbol Tables @@ -174,61 +176,16 @@ Examining Symbol Tables Return a tuple containing names of :term:`free (closure) variables ` in this function. + .. method:: get_cells() -.. class:: Class - - A namespace of a class. This class inherits from :class:`SymbolTable`. - - .. method:: get_methods() - - Return a tuple containing the names of method-like functions declared - in the class. - - Here, the term 'method' designates *any* function defined in the class - body via :keyword:`def` or :keyword:`async def`. - - Functions defined in a deeper scope (e.g., in an inner class) are not - picked up by :meth:`get_methods`. + Return a tuple containing names of :term:`cell variables ` in this table. - For example: + .. versionadded:: 3.15 - .. testsetup:: symtable.Class.get_methods - import warnings - context = warnings.catch_warnings() - context.__enter__() - warnings.simplefilter("ignore", category=DeprecationWarning) - - .. testcleanup:: symtable.Class.get_methods - - context.__exit__() - - .. doctest:: symtable.Class.get_methods - - >>> import symtable - >>> st = symtable.symtable(''' - ... def outer(): pass - ... - ... class A: - ... def f(): - ... def w(): pass - ... - ... def g(self): pass - ... - ... @classmethod - ... async def h(cls): pass - ... - ... global outer - ... def outer(self): pass - ... ''', 'test', 'exec') - >>> class_A = st.get_children()[2] - >>> class_A.get_methods() - ('f', 'g', 'h') - - Although ``A().f()`` raises :exc:`TypeError` at runtime, ``A.f`` is still - considered as a method-like function. +.. class:: Class - .. deprecated-removed:: 3.14 3.16 + A namespace of a class. This class inherits from :class:`SymbolTable`. .. class:: Symbol @@ -285,6 +242,12 @@ Examining Symbol Tables Return ``True`` if the symbol is referenced in its block, but not assigned to. + .. method:: is_cell() + + Return ``True`` if the symbol is referenced but not assigned in a nested block. + + .. versionadded:: 3.15 + .. method:: is_free_class() Return *True* if a class-scoped symbol is free from @@ -355,7 +318,7 @@ Command-Line Usage .. versionadded:: 3.13 -The :mod:`symtable` module can be executed as a script from the command line. +The :mod:`!symtable` module can be executed as a script from the command line. .. code-block:: sh diff --git a/Doc/library/sys.monitoring.rst b/Doc/library/sys.monitoring.rst index 0f986aa580b3c9..7cca6f2bcdae91 100644 --- a/Doc/library/sys.monitoring.rst +++ b/Doc/library/sys.monitoring.rst @@ -10,17 +10,17 @@ .. note:: - :mod:`sys.monitoring` is a namespace within the :mod:`sys` module, - not an independent module, so there is no need to - ``import sys.monitoring``, simply ``import sys`` and then use - ``sys.monitoring``. + :mod:`!sys.monitoring` is a namespace within the :mod:`sys` module, + not an independent module, and ``import sys.monitoring`` would fail + with a :exc:`ModuleNotFoundError`. Instead, simply ``import sys`` + and then use ``sys.monitoring``. This namespace provides access to the functions and constants necessary to activate and control event monitoring. As programs execute, events occur that might be of interest to tools that -monitor execution. The :mod:`sys.monitoring` namespace provides means to +monitor execution. The :mod:`!sys.monitoring` namespace provides means to receive callbacks when events of interest occur. The monitoring API consists of three components: @@ -180,8 +180,8 @@ Local events '''''''''''' Local events are associated with normal execution of the program and happen -at clearly defined locations. All local events can be disabled. -The local events are: +at clearly defined locations. All local events can be disabled +per location. The local events are: * :monitoring-event:`PY_START` * :monitoring-event:`PY_RESUME` @@ -205,6 +205,8 @@ Using :monitoring-event:`BRANCH_LEFT` and :monitoring-event:`BRANCH_RIGHT` events will give much better performance as they can be disabled independently. +.. _monitoring-ancillary-events: + Ancillary events '''''''''''''''' @@ -216,14 +218,17 @@ by another event: The :monitoring-event:`C_RETURN` and :monitoring-event:`C_RAISE` events are controlled by the :monitoring-event:`CALL` event. -:monitoring-event:`C_RETURN` and :monitoring-event:`C_RAISE` events will only be seen if the -corresponding :monitoring-event:`CALL` event is being monitored. +:monitoring-event:`C_RETURN` and :monitoring-event:`C_RAISE` events will only be +seen if the corresponding :monitoring-event:`CALL` event is being monitored. + + +.. _monitoring-event-global: Other events '''''''''''' Other events are not necessarily tied to a specific location in the -program and cannot be individually disabled. +program and cannot be individually disabled per location. The other events that can be monitored are: @@ -231,6 +236,12 @@ The other events that can be monitored are: * :monitoring-event:`PY_UNWIND` * :monitoring-event:`RAISE` * :monitoring-event:`EXCEPTION_HANDLED` +* :monitoring-event:`RERAISE` + +.. versionchanged:: 3.15 + Other events can now be turned on and disabled on a per code object + basis. Returning :data:`DISABLE` from a callback disables the event + for the entire code object (for the current tool). The STOP_ITERATION event @@ -244,8 +255,7 @@ raise an exception unless it would be visible to other code. To allow tools to monitor for real exceptions without slowing down generators and coroutines, the :monitoring-event:`STOP_ITERATION` event is provided. -:monitoring-event:`STOP_ITERATION` can be locally disabled, unlike -:monitoring-event:`RAISE`. +:monitoring-event:`STOP_ITERATION` can be locally disabled. Note that the :monitoring-event:`STOP_ITERATION` event and the :monitoring-event:`RAISE` event for a :exc:`StopIteration` exception are @@ -289,12 +299,13 @@ in Python (see :ref:`c-api-monitoring`). .. function:: get_local_events(tool_id: int, code: CodeType, /) -> int - Returns all the local events for *code* + Returns all the :ref:`local events ` for *code* .. function:: set_local_events(tool_id: int, code: CodeType, event_set: int, /) -> None - Activates all the local events for *code* which are set in *event_set*. - Raises a :exc:`ValueError` if *tool_id* is not in use. + Activates all the :ref:`local events ` for *code* + which are set in *event_set*. Raises a :exc:`ValueError` if *tool_id* is not + in use. Disabling events @@ -305,14 +316,19 @@ Disabling events A special value that can be returned from a callback function to disable events for the current code location. -Local events can be disabled for a specific code location by returning -:data:`sys.monitoring.DISABLE` from a callback function. This does not change -which events are set, or any other code locations for the same event. +:ref:`Local events ` can be disabled for a specific code +location by returning :data:`sys.monitoring.DISABLE` from a callback function. +This does not change which events are set, or any other code locations for the +same event. + +:ref:`Other events ` can be disabled on a per code +object basis by returning :data:`sys.monitoring.DISABLE` from a callback +function. This disables the event for the entire code object (for the current +tool). -Disabling events for specific locations is very important for high -performance monitoring. For example, a program can be run under a -debugger with no overhead if the debugger disables all monitoring -except for a few breakpoints. +Disabling events for specific locations is very important for high performance +monitoring. For example, a program can be run under a debugger with no overhead +if the debugger disables all monitoring except for a few breakpoints. .. function:: restart_events() -> None diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 52f0af31c68726..d0fe0625deb513 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -11,6 +11,51 @@ interpreter and to functions that interact strongly with the interpreter. It is always available. Unless explicitly noted otherwise, all variables are read-only. +.. data:: abi_info + + .. versionadded:: 3.15 + + An object containing information about the ABI of the currently running + Python interpreter. + It should include information that affect the CPython ABI in ways that + require a specific build of the interpreter chosen from variants that can + co-exist on a single machine. + For example, it does not encode the base OS (Linux or Windows), but does + include pointer size since some systems support both 32- and 64-bit builds. + The available entries are the same on all platforms; + e.g. *pointer_size* is available even on 64-bit-only architectures. + + The following attributes are available: + + .. attribute:: abi_info.pointer_bits + + The width of pointers in bits, as an integer, + equivalent to ``8 * sizeof(void *)``. + Usually, this is ``32`` or ``64``. + + .. attribute:: abi_info.free_threaded + + A Boolean indicating whether the interpreter was built with + :term:`free threading` support. + This reflects either the presence of the :option:`--disable-gil` + :file:`configure` option (on Unix) + or setting the ``DisableGil`` property (on Windows). + + .. attribute:: abi_info.debug + + A Boolean indicating whether the interpreter was built in + :ref:`debug mode `. + This reflects either the presence of the :option:`--with-pydebug` + :file:`configure` option (on Unix) + or the ``Debug`` configuration (on Windows). + + .. attribute:: abi_info.byteorder + + A string indicating the native byte order, + either ``'big'`` or ``'little'``. + This is the same as the :data:`byteorder` attribute. + + .. data:: abiflags On POSIX systems where Python was built with the standard ``configure`` @@ -515,7 +560,7 @@ always available. Unless explicitly noted otherwise, all variables are read-only in the range 0--127, and produce undefined results otherwise. Some systems have a convention for assigning specific meanings to specific exit codes, but these are generally underdeveloped; Unix programs generally use 2 for command - line syntax errors and 1 for all other kind of errors. If another type of + line syntax errors and 1 for all other kinds of errors. If another type of object is passed, ``None`` is equivalent to passing zero, and any other object is printed to :data:`stderr` and results in an exit code of 1. In particular, ``sys.exit("some error message")`` is a quick way to exit a @@ -523,8 +568,9 @@ always available. Unless explicitly noted otherwise, all variables are read-only Since :func:`exit` ultimately "only" raises an exception, it will only exit the process when called from the main thread, and the exception is not - intercepted. Cleanup actions specified by finally clauses of :keyword:`try` statements - are honored, and it is possible to intercept the exit attempt at an outer level. + intercepted. Cleanup actions specified by :keyword:`finally` clauses of + :keyword:`try` statements are honored, and it is possible to intercept the + exit attempt at an outer level. .. versionchanged:: 3.6 If an error occurs in the cleanup after the Python interpreter @@ -833,7 +879,7 @@ always available. Unless explicitly noted otherwise, all variables are read-only .. versionchanged:: 3.6 Windows is no longer guaranteed to return ``'mbcs'``. See :pep:`529` - and :func:`_enablelegacywindowsfsencoding` for more information. + for more information. .. versionchanged:: 3.7 Return ``'utf-8'`` if the :ref:`Python UTF-8 Mode ` is @@ -865,6 +911,33 @@ always available. Unless explicitly noted otherwise, all variables are read-only .. versionadded:: 3.11 + +.. function:: get_lazy_imports() + + Returns the current lazy imports mode as a string. + + * ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword + are lazy + * ``"all"``: All top-level imports are potentially lazy + + See also :func:`set_lazy_imports` and :pep:`810`. + + .. versionadded:: 3.15 + + +.. function:: get_lazy_imports_filter() + + Returns the current lazy imports filter function, or ``None`` if no + filter is set. + + The filter function is called for every potentially lazy import to + determine whether it should actually be lazy. See + :func:`set_lazy_imports_filter` for details on the filter function + signature. + + .. versionadded:: 3.15 + + .. function:: getrefcount(object) Return the reference count of the *object*. The count returned is generally one @@ -1130,10 +1203,14 @@ always available. Unless explicitly noted otherwise, all variables are read-only The size of the seed key of the hash algorithm + .. attribute:: hash_info.cutoff + + Cutoff for small string DJBX33A optimization in range ``[1, cutoff)``. + .. versionadded:: 3.2 .. versionchanged:: 3.4 - Added *algorithm*, *hash_bits* and *seed_bits* + Added *algorithm*, *hash_bits*, *seed_bits*, and *cutoff*. .. data:: hexversion @@ -1404,6 +1481,21 @@ always available. Unless explicitly noted otherwise, all variables are read-only They hold the legacy representation of ``sys.last_exc``, as returned from :func:`exc_info` above. + +.. data:: lazy_modules + + A :class:`set` of fully qualified module name strings that have been lazily + imported in the current interpreter but not yet loaded. When a + lazily imported module is accessed for the first time, its name is removed + from this set. + + This attribute is intended for debugging and introspection. + + See also :func:`set_lazy_imports` and :pep:`810`. + + .. versionadded:: 3.15 + + .. data:: maxsize An integer giving the maximum value a variable of type :c:type:`Py_ssize_t` can @@ -1669,6 +1761,61 @@ always available. Unless explicitly noted otherwise, all variables are read-only .. versionadded:: 3.11 + +.. function:: set_lazy_imports(mode) + + Sets the global lazy imports mode. The *mode* parameter must be one of + the following strings: + + * ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword + are lazy + * ``"all"``: All top-level imports become potentially lazy + + This function is intended for advanced users who need to control lazy + imports across their entire application. Library developers should + generally not use this function as it affects the runtime execution of + applications. + + In addition to the mode, lazy imports can be controlled via the filter + provided by :func:`set_lazy_imports_filter`. + + See also :func:`get_lazy_imports` and :pep:`810`. + + .. versionadded:: 3.15 + + +.. function:: set_lazy_imports_filter(filter) + + Sets the lazy imports filter callback. The *filter* parameter must be a + callable or ``None`` to clear the filter. + + The filter function is called for every potentially lazy import to + determine whether it should actually be lazy. It must have the following + signature:: + + def filter(importing_module: str, imported_module: str, + fromlist: tuple[str, ...] | None) -> bool + + Where: + + * *importing_module* is the name of the module doing the import + * *imported_module* is the resolved name of the module being imported + (for example, ``lazy from .spam import eggs`` passes + ``package.spam``) + * *fromlist* is the tuple of names being imported (for ``from ... import`` + statements), or ``None`` for regular imports + + The filter should return ``True`` to allow the import to be lazy, or + ``False`` to force an eager import. + + This is an advanced feature intended for specialized users who need + fine-grained control over lazy import behavior. + + See also :func:`get_lazy_imports_filter` and :pep:`810`. + + .. versionadded:: 3.15 + + .. function:: setprofile(profilefunc) .. index:: @@ -1764,7 +1911,7 @@ always available. Unless explicitly noted otherwise, all variables are read-only :func:`settrace` for each thread being debugged or use :func:`threading.settrace`. Trace functions should have three arguments: *frame*, *event*, and - *arg*. *frame* is the current stack frame. *event* is a string: ``'call'``, + *arg*. *frame* is the :ref:`current stack frame `. *event* is a string: ``'call'``, ``'line'``, ``'return'``, ``'exception'`` or ``'opcode'``. *arg* depends on the event type. @@ -1947,6 +2094,9 @@ always available. Unless explicitly noted otherwise, all variables are read-only interpreter is pre-release (alpha, beta, or release candidate) then the local and remote interpreters must be the same exact version. + See :ref:`remote-debugging` for more information about the remote debugging + mechanism. + .. audit-event:: sys.remote_exec pid script_path When the code is executed in the remote process, an @@ -1965,33 +2115,9 @@ always available. Unless explicitly noted otherwise, all variables are read-only .. availability:: Unix, Windows. .. versionadded:: 3.14 + See :pep:`768` for more details. -.. function:: _enablelegacywindowsfsencoding() - - Changes the :term:`filesystem encoding and error handler` to 'mbcs' and - 'replace' respectively, for consistency with versions of Python prior to - 3.6. - - This is equivalent to defining the :envvar:`PYTHONLEGACYWINDOWSFSENCODING` - environment variable before launching Python. - - See also :func:`sys.getfilesystemencoding` and - :func:`sys.getfilesystemencodeerrors`. - - .. availability:: Windows. - - .. note:: - Changing the filesystem encoding after Python startup is risky because - the old fsencoding or paths encoded by the old fsencoding may be cached - somewhere. Use :envvar:`PYTHONLEGACYWINDOWSFSENCODING` instead. - - .. versionadded:: 3.6 - See :pep:`529` for more details. - - .. deprecated-removed:: 3.13 3.16 - Use :envvar:`PYTHONLEGACYWINDOWSFSENCODING` instead. - .. data:: stdin stdout stderr @@ -2152,11 +2278,16 @@ always available. Unless explicitly noted otherwise, all variables are read-only The default hook formats :attr:`!err_msg` and :attr:`!object` as: ``f'{err_msg}: {object!r}'``; use "Exception ignored in" error message - if :attr:`!err_msg` is ``None``. + if :attr:`!err_msg` is ``None``. Similar to the :mod:`traceback` module, + this adds color to exceptions by default. This can be disabled using + :ref:`environment variables `. :func:`sys.unraisablehook` can be overridden to control how unraisable exceptions are handled. + .. versionchanged:: 3.15 + Exceptions are now printed with colorful text. + .. seealso:: :func:`excepthook` which handles uncaught exceptions. @@ -2222,7 +2353,7 @@ always available. Unless explicitly noted otherwise, all variables are read-only The version number used to form registry keys on Windows platforms. This is stored as string resource 1000 in the Python DLL. The value is normally the - major and minor versions of the running Python interpreter. It is provided in the :mod:`sys` + major and minor versions of the running Python interpreter. It is provided in the :mod:`!sys` module for informational purposes; modifying this value has no effect on the registry keys used by Python. diff --git a/Doc/library/sys_path_init.rst b/Doc/library/sys_path_init.rst index a37bb59e7cec76..e6c2cddbe84248 100644 --- a/Doc/library/sys_path_init.rst +++ b/Doc/library/sys_path_init.rst @@ -57,15 +57,19 @@ otherwise they are set to the same value as :data:`sys.base_prefix` and :data:`sys.base_exec_prefix`, respectively. This is used by :ref:`sys-path-init-virtual-environments`. -Finally, the :mod:`site` module is processed and :file:`site-packages` directories -are added to the module search path. A common way to customize the search path is -to create :mod:`sitecustomize` or :mod:`usercustomize` modules as described in -the :mod:`site` module documentation. +Finally, the :mod:`site` module is processed and :file:`site-packages` +directories are added to the module search path. The :envvar:`PYTHONUSERBASE` +environment variable controls where is searched for user site-packages and the +:envvar:`PYTHONNOUSERSITE` environment variable prevents searching for user +site-packages all together. A common way to customize the search path is to +create :mod:`sitecustomize` or :mod:`usercustomize` modules as described in the +:mod:`site` module documentation. .. note:: - Certain command line options may further affect path calculations. - See :option:`-E`, :option:`-I`, :option:`-s` and :option:`-S` for further details. + The command line options :option:`-E`, :option:`-P`, :option:`-I`, + :option:`-S` and :option:`-s` further affect path calculations, see their + documentation for details. .. versionchanged:: 3.14 @@ -96,11 +100,10 @@ Please refer to :mod:`site`'s .. note:: - There are other ways how "virtual environments" could be implemented, this - documentation refers implementations based on the ``pyvenv.cfg`` mechanism, - such as :mod:`venv`. Most virtual environment implementations follow the - model set by :mod:`venv`, but there may be exotic implementations that - diverge from it. + There are other ways "virtual environments" could be implemented. + This documentation refers to implementations based on the ``pyvenv.cfg`` + mechanism, such as :mod:`venv`, that many virtual environment implementations + follow. _pth files ---------- diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst index 684d14a74c48ab..8aa912d99ba756 100644 --- a/Doc/library/sysconfig.rst +++ b/Doc/library/sysconfig.rst @@ -4,9 +4,6 @@ .. module:: sysconfig :synopsis: Python's configuration information -.. moduleauthor:: Tarek Ziadé -.. sectionauthor:: Tarek Ziadé - .. versionadded:: 3.2 **Source code:** :source:`Lib/sysconfig` @@ -16,7 +13,7 @@ -------------- -The :mod:`sysconfig` module provides access to Python's configuration +The :mod:`!sysconfig` module provides access to Python's configuration information like the list of installation paths and the configuration variables relevant for the current platform. @@ -28,7 +25,7 @@ A Python distribution contains a :file:`Makefile` and a :file:`pyconfig.h` header file that are necessary to build both the Python binary itself and third-party C extensions compiled using ``setuptools``. -:mod:`sysconfig` puts all variables found in these files in a dictionary that +:mod:`!sysconfig` puts all variables found in these files in a dictionary that can be accessed using :func:`get_config_vars` or :func:`get_config_var`. Notice that on Windows, it's a much smaller set. @@ -68,7 +65,7 @@ Installation paths ------------------ Python uses an installation scheme that differs depending on the platform and on -the installation options. These schemes are stored in :mod:`sysconfig` under +the installation options. These schemes are stored in :mod:`!sysconfig` under unique identifiers based on the value returned by :const:`os.name`. The schemes are used by package installers to determine where to copy files to. @@ -258,12 +255,12 @@ Path Installation directory Installation path functions --------------------------- -:mod:`sysconfig` provides some functions to determine these installation paths. +:mod:`!sysconfig` provides some functions to determine these installation paths. .. function:: get_scheme_names() Return a tuple containing all schemes currently supported in - :mod:`sysconfig`. + :mod:`!sysconfig`. .. function:: get_default_scheme() @@ -285,7 +282,7 @@ Installation path functions *key* must be either ``"prefix"``, ``"home"``, or ``"user"``. The return value is a scheme name listed in :func:`get_scheme_names`. It - can be passed to :mod:`sysconfig` functions that take a *scheme* argument, + can be passed to :mod:`!sysconfig` functions that take a *scheme* argument, such as :func:`get_paths`. .. versionadded:: 3.10 @@ -313,7 +310,7 @@ Installation path functions .. function:: get_path_names() Return a tuple containing all path names currently supported in - :mod:`sysconfig`. + :mod:`!sysconfig`. .. function:: get_path(name, [scheme, [vars, [expand]]]) @@ -323,7 +320,7 @@ Installation path functions *name* has to be a value from the list returned by :func:`get_path_names`. - :mod:`sysconfig` stores installation paths corresponding to each path name, + :mod:`!sysconfig` stores installation paths corresponding to each path name, for each platform, with variables to be expanded. For instance the *stdlib* path for the *nt* scheme is: ``{base}/Lib``. @@ -382,22 +379,22 @@ Other functions Examples of returned values: - - linux-i586 - - linux-alpha (?) + - linux-x86_64 + - linux-aarch64 - solaris-2.6-sun4u - Windows will return one of: + Windows: - win-amd64 (64-bit Windows on AMD64, aka x86_64, Intel64, and EM64T) - win-arm64 (64-bit Windows on ARM64, aka AArch64) - win32 (all others - specifically, sys.platform is returned) - macOS can return: + POSIX based OS: - - macosx-10.6-ppc - - macosx-10.4-ppc64 - - macosx-10.3-i386 - - macosx-10.4-fat + - linux-x86_64 + - macosx-15.5-arm64 + - macosx-26.0-universal2 (macOS on Apple Silicon or Intel) + - android-24-arm64_v8a For other non-POSIX platforms, currently just returns :data:`sys.platform`. @@ -434,7 +431,7 @@ Other functions Command-line usage ------------------ -You can use :mod:`sysconfig` as a script with Python's *-m* option: +You can use :mod:`!sysconfig` as a script with Python's *-m* option: .. code-block:: shell-session diff --git a/Doc/library/syslog.rst b/Doc/library/syslog.rst index 548898a37bc6ea..b6bf01240951eb 100644 --- a/Doc/library/syslog.rst +++ b/Doc/library/syslog.rst @@ -2,7 +2,6 @@ =============================================== .. module:: syslog - :platform: Unix :synopsis: An interface to the Unix syslog library routines. -------------- diff --git a/Doc/library/tabnanny.rst b/Doc/library/tabnanny.rst index 4f61b3dd761400..570cc7fd93beef 100644 --- a/Doc/library/tabnanny.rst +++ b/Doc/library/tabnanny.rst @@ -5,11 +5,6 @@ :synopsis: Tool for detecting white space related problems in Python source files in a directory tree. -.. moduleauthor:: Tim Peters -.. sectionauthor:: Peter Funk - -.. rudimentary documentation based on module comments - **Source code:** :source:`Lib/tabnanny.py` -------------- diff --git a/Doc/library/tachyon-flamegraph.png b/Doc/library/tachyon-flamegraph.png new file mode 100644 index 00000000000000..a17cd304f8bb28 Binary files /dev/null and b/Doc/library/tachyon-flamegraph.png differ diff --git a/Doc/library/tachyon-gecko-calltree.png b/Doc/library/tachyon-gecko-calltree.png new file mode 100644 index 00000000000000..71b096940e8fcd Binary files /dev/null and b/Doc/library/tachyon-gecko-calltree.png differ diff --git a/Doc/library/tachyon-gecko-flamegraph.png b/Doc/library/tachyon-gecko-flamegraph.png new file mode 100644 index 00000000000000..d427ed85ac04e7 Binary files /dev/null and b/Doc/library/tachyon-gecko-flamegraph.png differ diff --git a/Doc/library/tachyon-gecko-opcodes.png b/Doc/library/tachyon-gecko-opcodes.png new file mode 100644 index 00000000000000..9741eb659120b8 Binary files /dev/null and b/Doc/library/tachyon-gecko-opcodes.png differ diff --git a/Doc/library/tachyon-heatmap-with-opcodes.png b/Doc/library/tachyon-heatmap-with-opcodes.png new file mode 100644 index 00000000000000..5ad67d131548e4 Binary files /dev/null and b/Doc/library/tachyon-heatmap-with-opcodes.png differ diff --git a/Doc/library/tachyon-heatmap.png b/Doc/library/tachyon-heatmap.png new file mode 100644 index 00000000000000..47ac1119f4e572 Binary files /dev/null and b/Doc/library/tachyon-heatmap.png differ diff --git a/Doc/library/tachyon-live-mode-1.gif b/Doc/library/tachyon-live-mode-1.gif new file mode 100644 index 00000000000000..2d58e807d6a7c1 Binary files /dev/null and b/Doc/library/tachyon-live-mode-1.gif differ diff --git a/Doc/library/tachyon-live-mode-2.gif b/Doc/library/tachyon-live-mode-2.gif new file mode 100644 index 00000000000000..bbc2163fe603eb Binary files /dev/null and b/Doc/library/tachyon-live-mode-2.gif differ diff --git a/Doc/library/tachyon-pstats.png b/Doc/library/tachyon-pstats.png new file mode 100644 index 00000000000000..d0281ade660914 Binary files /dev/null and b/Doc/library/tachyon-pstats.png differ diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index acaec5b592bf6e..29a329fdfeab15 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -4,14 +4,11 @@ .. module:: tarfile :synopsis: Read and write tar-format archive files. -.. moduleauthor:: Lars Gustäbel -.. sectionauthor:: Lars Gustäbel - **Source code:** :source:`Lib/tarfile.py` -------------- -The :mod:`tarfile` module makes it possible to read and write tar +The :mod:`!tarfile` module makes it possible to read and write tar archives, including those using gzip, bz2 and lzma compression. Use the :mod:`zipfile` module to read or write :file:`.zip` files, or the higher-level functions in :ref:`shutil `. @@ -21,6 +18,14 @@ Some facts and figures: * reads and writes :mod:`gzip`, :mod:`bz2`, :mod:`compression.zstd`, and :mod:`lzma` compressed archives if the respective modules are available. + .. + The following paragraph should be similar to ../includes/optional-module.rst + + If any of these :term:`optional modules ` are missing from + your copy of CPython, look for documentation from your distributor (that is, + whoever provided Python to you). + If you are the distributor, see :ref:`optional-module-requirements`. + * read/write support for the POSIX.1-1988 (ustar) format. * read/write support for the GNU tar format including *longname* and *longlink* @@ -137,6 +142,12 @@ Some facts and figures: a Zstandard dictionary used to improve compression of smaller amounts of data. + For modes ``'w:gz'`` and ``'w|gz'``, :func:`tarfile.open` accepts the + keyword argument *mtime* to create a gzip archive header with that mtime. By + default, the mtime is set to the time of creation of the archive. Use + *mtime* ``0`` to generate a compressed stream that does not depend on + creation time, for reproducible output. + For special purposes, there is a second format for *mode*: ``'filemode|[compression]'``. :func:`tarfile.open` will return a :class:`TarFile` object that processes its data as a stream of blocks. No random seeking will @@ -198,7 +209,7 @@ Some facts and figures: .. versionchanged:: 3.14 The *preset* keyword argument also works for streams. - .. versionchanged:: next + .. versionchanged:: 3.15 The default compression level was reduced to 6 (down from 9). It is the default level used by most compression tools and a better tradeoff between speed and performance. @@ -212,25 +223,25 @@ Some facts and figures: .. function:: is_tarfile(name) - Return :const:`True` if *name* is a tar archive file, that the :mod:`tarfile` + Return :const:`True` if *name* is a tar archive file, that the :mod:`!tarfile` module can read. *name* may be a :class:`str`, file, or file-like object. .. versionchanged:: 3.9 Support for file and file-like objects. -The :mod:`tarfile` module defines the following exceptions: +The :mod:`!tarfile` module defines the following exceptions: .. exception:: TarError - Base class for all :mod:`tarfile` exceptions. + Base class for all :mod:`!tarfile` exceptions. .. exception:: ReadError Is raised when a tar archive is opened, that either cannot be handled by the - :mod:`tarfile` module or is somehow invalid. + :mod:`!tarfile` module or is somehow invalid. .. exception:: CompressionError @@ -294,7 +305,7 @@ The :mod:`tarfile` module defines the following exceptions: The exception that was raised to reject the replacement member is available as :attr:`!BaseException.__context__`. - .. versionadded:: next + .. versionadded:: 3.15 The following constants are available at the module level: @@ -351,7 +362,7 @@ The following constants are available at the module level: Each of the following constants defines a tar archive format that the -:mod:`tarfile` module is able to create. See section :ref:`tar-formats` for +:mod:`!tarfile` module is able to create. See section :ref:`tar-formats` for details. @@ -659,7 +670,7 @@ be finalized; only the internally used file object will be closed. See the it is best practice to only do so in top-level applications or :mod:`site configuration `. To set a global default this way, a filter function needs to be wrapped in - :func:`staticmethod` to prevent injection of a ``self`` argument. + :deco:`staticmethod` to prevent injection of a ``self`` argument. .. versionchanged:: 3.14 @@ -1146,7 +1157,7 @@ reused in custom filters: Note that this filter does not block *all* dangerous archive features. See :ref:`tarfile-further-verification` for details. - .. versionchanged:: next + .. versionchanged:: 3.15 Link targets are now normalized. @@ -1281,7 +1292,7 @@ Command-Line Interface .. versionadded:: 3.4 -The :mod:`tarfile` module provides a simple command-line interface to interact +The :mod:`!tarfile` module provides a simple command-line interface to interact with tar archives. If you want to create a new tar archive, specify its name after the :option:`-c` @@ -1442,7 +1453,7 @@ parameter in :meth:`TarFile.add`:: Supported tar formats --------------------- -There are three tar formats that can be created with the :mod:`tarfile` module: +There are three tar formats that can be created with the :mod:`!tarfile` module: * The POSIX.1-1988 ustar format (:const:`USTAR_FORMAT`). It supports filenames up to a length of at best 256 characters and linknames up to 100 characters. @@ -1451,7 +1462,7 @@ There are three tar formats that can be created with the :mod:`tarfile` module: * The GNU tar format (:const:`GNU_FORMAT`). It supports long filenames and linknames, files bigger than 8 GiB and sparse files. It is the de facto - standard on GNU/Linux systems. :mod:`tarfile` fully supports the GNU tar + standard on GNU/Linux systems. :mod:`!tarfile` fully supports the GNU tar extensions for long names, sparse file support is read-only. * The POSIX.1-2001 pax format (:const:`PAX_FORMAT`). It is the most flexible @@ -1496,7 +1507,7 @@ Unfortunately, there is no way to autodetect the encoding of an archive. The pax format was designed to solve this problem. It stores non-ASCII metadata using the universal character encoding *UTF-8*. -The details of character conversion in :mod:`tarfile` are controlled by the +The details of character conversion in :mod:`!tarfile` are controlled by the *encoding* and *errors* keyword arguments of the :class:`TarFile` class. *encoding* defines the character encoding to use for the metadata in the diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst index f0a81a093b435b..bf9198e175a0e1 100644 --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -4,8 +4,6 @@ .. module:: tempfile :synopsis: Generate temporary files and directories. -.. sectionauthor:: Zack Weinberg - **Source code:** :source:`Lib/tempfile.py` .. index:: @@ -225,8 +223,9 @@ The module defines the following user-callable items: properly implements the :const:`os.O_EXCL` flag for :func:`os.open`. The file is readable and writable only by the creating user ID. If the platform uses permission bits to indicate whether a file is executable, - the file is executable by no one. The file descriptor is not inherited - by child processes. + the file is executable by no one. + + The file descriptor is :ref:`not inherited by child processes `. Unlike :func:`TemporaryFile`, the user of :func:`mkstemp` is responsible for deleting the temporary file when done with it. @@ -385,7 +384,7 @@ not surprise other unsuspecting code by changing global API behavior. Examples -------- -Here are some examples of typical usage of the :mod:`tempfile` module:: +Here are some examples of typical usage of the :mod:`!tempfile` module:: >>> import tempfile diff --git a/Doc/library/termios.rst b/Doc/library/termios.rst index 0c6f3059fe71d1..537dfcedd8cf5a 100644 --- a/Doc/library/termios.rst +++ b/Doc/library/termios.rst @@ -2,7 +2,6 @@ =========================================== .. module:: termios - :platform: Unix :synopsis: POSIX style tty control. .. index:: @@ -38,7 +37,7 @@ The module defines the following functions: items with indices :const:`VMIN` and :const:`VTIME`, which are integers when these fields are defined). The interpretation of the flags and the speeds as well as the indexing in the *cc* array must be done using the symbolic - constants defined in the :mod:`termios` module. + constants defined in the :mod:`!termios` module. .. function:: tcsetattr(fd, when, attributes) diff --git a/Doc/library/test.rst b/Doc/library/test.rst index 9fdb21b8badbbf..4e21e1ded82724 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -4,10 +4,8 @@ .. module:: test :synopsis: Regression tests package containing the testing suite for Python. -.. sectionauthor:: Brett Cannon - .. note:: - The :mod:`test` package is meant for internal use by Python only. It is + The :mod:`!test` package is meant for internal use by Python only. It is documented for the benefit of the core developers of Python. Any use of this package outside of Python's standard library is discouraged as code mentioned here can change or be removed without notice between releases of @@ -15,12 +13,12 @@ -------------- -The :mod:`test` package contains all regression tests for Python as well as the +The :mod:`!test` package contains all regression tests for Python as well as the modules :mod:`test.support` and :mod:`test.regrtest`. :mod:`test.support` is used to enhance your tests while :mod:`test.regrtest` drives the testing suite. -Each module in the :mod:`test` package whose name starts with ``test_`` is a +Each module in the :mod:`!test` package whose name starts with ``test_`` is a testing suite for a specific module or feature. All new tests should be written using the :mod:`unittest` or :mod:`doctest` module. Some older tests are written using a "traditional" testing style that compares output printed to @@ -38,8 +36,8 @@ written using a "traditional" testing style that compares output printed to .. _writing-tests: -Writing Unit Tests for the :mod:`test` package ----------------------------------------------- +Writing Unit Tests for the :mod:`!test` package +----------------------------------------------- It is preferred that tests that use the :mod:`unittest` module follow a few guidelines. One is to name the test module by starting it with ``test_`` and end @@ -162,12 +160,12 @@ Running tests using the command-line interface .. module:: test.regrtest :synopsis: Drives the regression test suite. -The :mod:`test` package can be run as a script to drive Python's regression +The :mod:`!test` package can be run as a script to drive Python's regression test suite, thanks to the :option:`-m` option: :program:`python -m test`. Under -the hood, it uses :mod:`test.regrtest`; the call :program:`python -m +the hood, it uses :mod:`!test.regrtest`; the call :program:`python -m test.regrtest` used in previous Python versions still works. Running the script by itself automatically starts running all regression tests in the -:mod:`test` package. It does this by finding all modules in the package whose +:mod:`!test` package. It does this by finding all modules in the package whose name starts with ``test_``, importing them, and executing the function :func:`test_main` if present or loading the tests via unittest.TestLoader.loadTestsFromModule if ``test_main`` does not exist. The @@ -175,14 +173,14 @@ names of tests to execute may also be passed to the script. Specifying a single regression test (:program:`python -m test test_spam`) will minimize output and only print whether the test passed or failed. -Running :mod:`test` directly allows what resources are available for +Running :mod:`!test` directly allows what resources are available for tests to use to be set. You do this by using the ``-u`` command-line option. Specifying ``all`` as the value for the ``-u`` option enables all possible resources: :program:`python -m test -uall`. If all but one resource is desired (a more common case), a comma-separated list of resources that are not desired may be listed after ``all``. The command :program:`python -m test -uall,-audio,-largefile` -will run :mod:`test` with all resources except the ``audio`` and +will run :mod:`!test` with all resources except the ``audio`` and ``largefile`` resources. For a list of all resources and more command-line options, run :program:`python -m test -h`. @@ -197,19 +195,19 @@ regression tests. :ref:`controlled using environment variables `. -:mod:`test.support` --- Utilities for the Python test suite -=========================================================== +:mod:`!test.support` --- Utilities for the Python test suite +============================================================ .. module:: test.support :synopsis: Support for Python's regression test suite. -The :mod:`test.support` module provides support for Python's regression +The :mod:`!test.support` module provides support for Python's regression test suite. .. note:: - :mod:`test.support` is not a public module. It is documented here to help + :mod:`!test.support` is not a public module. It is documented here to help Python developers write tests. The API of this module is subject to change without backwards compatibility concerns between releases. @@ -230,7 +228,7 @@ This module defines the following exceptions: function. -The :mod:`test.support` module defines the following constants: +The :mod:`!test.support` module defines the following constants: .. data:: verbose @@ -363,7 +361,7 @@ The :mod:`test.support` module defines the following constants: .. data:: TEST_SUPPORT_DIR - Set to the top level directory that contains :mod:`test.support`. + Set to the top level directory that contains :mod:`!test.support`. .. data:: TEST_HOME_DIR @@ -438,7 +436,7 @@ The :mod:`test.support` module defines the following constants: Used to test mixed type comparison. -The :mod:`test.support` module defines the following functions: +The :mod:`!test.support` module defines the following functions: .. function:: busy_retry(timeout, err_msg=None, /, *, error=True) @@ -492,6 +490,12 @@ The :mod:`test.support` module defines the following functions: tests. +.. function:: get_resource_value(resource) + + Return the value specified for *resource* (as :samp:`-u {resource}={value}`). + Return ``None`` if *resource* is disabled or no value is specified. + + .. function:: python_is_optimized() Return ``True`` if Python was not built with ``-O0`` or ``-Og``. @@ -714,7 +718,7 @@ The :mod:`test.support` module defines the following functions: .. decorator:: anticipate_failure(condition) A decorator to conditionally mark tests with - :func:`unittest.expectedFailure`. Any use of this decorator should + :deco:`unittest.expectedFailure`. Any use of this decorator should have an associated comment identifying the relevant tracker issue. @@ -851,7 +855,7 @@ The :mod:`test.support` module defines the following functions: Decorator for tests that fill the address space. -.. function:: linked_with_musl() +.. function:: linked_to_musl() Return ``False`` if there is no evidence the interpreter was compiled with ``musl``, otherwise return a version triple, either ``(0, 0, 0)`` if the @@ -1037,7 +1041,7 @@ The :mod:`test.support` module defines the following functions: .. versionadded:: 3.11 -The :mod:`test.support` module defines the following classes: +The :mod:`!test.support` module defines the following classes: .. class:: SuppressCrashReport() @@ -1083,14 +1087,14 @@ The :mod:`test.support` module defines the following classes: Try to match a single stored value (*dv*) with a supplied value (*v*). -:mod:`test.support.socket_helper` --- Utilities for socket tests -================================================================ +:mod:`!test.support.socket_helper` --- Utilities for socket tests +================================================================= .. module:: test.support.socket_helper :synopsis: Support for socket tests. -The :mod:`test.support.socket_helper` module provides support for socket tests. +The :mod:`!test.support.socket_helper` module provides support for socket tests. .. versionadded:: 3.9 @@ -1161,14 +1165,14 @@ The :mod:`test.support.socket_helper` module provides support for socket tests. exceptions. -:mod:`test.support.script_helper` --- Utilities for the Python execution tests -============================================================================== +:mod:`!test.support.script_helper` --- Utilities for the Python execution tests +=============================================================================== .. module:: test.support.script_helper :synopsis: Support for Python's script execution tests. -The :mod:`test.support.script_helper` module provides support for Python's +The :mod:`!test.support.script_helper` module provides support for Python's script execution tests. .. function:: interpreter_requires_environment() @@ -1272,13 +1276,13 @@ script execution tests. path and the archive name for the zip file. -:mod:`test.support.bytecode_helper` --- Support tools for testing correct bytecode generation -============================================================================================= +:mod:`!test.support.bytecode_helper` --- Support tools for testing correct bytecode generation +============================================================================================== .. module:: test.support.bytecode_helper :synopsis: Support tools for testing correct bytecode generation. -The :mod:`test.support.bytecode_helper` module provides support for testing +The :mod:`!test.support.bytecode_helper` module provides support for testing and inspecting bytecode generation. .. versionadded:: 3.9 @@ -1304,13 +1308,13 @@ The module defines the following class: Throws :exc:`AssertionError` if *opname* is found. -:mod:`test.support.threading_helper` --- Utilities for threading tests -====================================================================== +:mod:`!test.support.threading_helper` --- Utilities for threading tests +======================================================================= .. module:: test.support.threading_helper :synopsis: Support for threading tests. -The :mod:`test.support.threading_helper` module provides support for threading tests. +The :mod:`!test.support.threading_helper` module provides support for threading tests. .. versionadded:: 3.10 @@ -1391,13 +1395,13 @@ The :mod:`test.support.threading_helper` module provides support for threading t finished. -:mod:`test.support.os_helper` --- Utilities for os tests -======================================================================== +:mod:`!test.support.os_helper` --- Utilities for os tests +========================================================= .. module:: test.support.os_helper :synopsis: Support for os tests. -The :mod:`test.support.os_helper` module provides support for os tests. +The :mod:`!test.support.os_helper` module provides support for os tests. .. versionadded:: 3.10 @@ -1586,13 +1590,13 @@ The :mod:`test.support.os_helper` module provides support for os tests. wrapped with a wait loop that checks for the existence of the file. -:mod:`test.support.import_helper` --- Utilities for import tests -================================================================ +:mod:`!test.support.import_helper` --- Utilities for import tests +================================================================= .. module:: test.support.import_helper :synopsis: Support for import tests. -The :mod:`test.support.import_helper` module provides support for import tests. +The :mod:`!test.support.import_helper` module provides support for import tests. .. versionadded:: 3.10 @@ -1700,13 +1704,13 @@ The :mod:`test.support.import_helper` module provides support for import tests. will be reverted at the end of the block. -:mod:`test.support.warnings_helper` --- Utilities for warnings tests -==================================================================== +:mod:`!test.support.warnings_helper` --- Utilities for warnings tests +===================================================================== .. module:: test.support.warnings_helper :synopsis: Support for warnings tests. -The :mod:`test.support.warnings_helper` module provides support for warnings tests. +The :mod:`!test.support.warnings_helper` module provides support for warnings tests. .. versionadded:: 3.10 diff --git a/Doc/library/textwrap.rst b/Doc/library/textwrap.rst index a58b460fef409c..d12968dee91f3c 100644 --- a/Doc/library/textwrap.rst +++ b/Doc/library/textwrap.rst @@ -4,14 +4,11 @@ .. module:: textwrap :synopsis: Text wrapping and filling -.. moduleauthor:: Greg Ward -.. sectionauthor:: Greg Ward - **Source code:** :source:`Lib/textwrap.py` -------------- -The :mod:`textwrap` module provides some convenience functions, +The :mod:`!textwrap` module provides some convenience functions, as well as :class:`TextWrapper`, the class that does all the work. If you're just wrapping or filling one or two text strings, the convenience functions should be good enough; otherwise, you should use an instance of @@ -102,6 +99,10 @@ functions should be good enough; otherwise, you should use an instance of print(repr(s)) # prints ' hello\n world\n ' print(repr(dedent(s))) # prints 'hello\n world\n' + .. versionchanged:: 3.14 + The :func:`!dedent` function now correctly normalizes blank lines containing + only whitespace characters. Previously, the implementation only normalized + blank lines containing tabs and spaces. .. function:: indent(text, prefix, predicate=None) diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index cabb41442f8419..5d9a7b6314b166 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -191,13 +191,16 @@ This module defines the following functions: Its value may be used to uniquely identify this particular thread system-wide (until the thread terminates, after which the value may be recycled by the OS). - .. availability:: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD, GNU/kFreeBSD. + .. availability:: Windows, FreeBSD, Linux, macOS, OpenBSD, NetBSD, AIX, DragonFlyBSD, GNU/kFreeBSD, Solaris. .. versionadded:: 3.8 .. versionchanged:: 3.13 Added support for GNU/kFreeBSD. + .. versionchanged:: 3.15 + Added support for Solaris. + .. function:: enumerate() @@ -512,7 +515,7 @@ since it is impossible to detect the termination of alien threads. This constructor should always be called with keyword arguments. Arguments are: - *group* should be ``None``; reserved for future extension when a + *group* must be ``None`` as it is reserved for future extension when a :class:`!ThreadGroup` class is implemented. *target* is the callable object to be invoked by the :meth:`run` method. @@ -605,7 +608,7 @@ since it is impossible to detect the termination of alien threads. timeout occurs. When the *timeout* argument is present and not ``None``, it should be a - floating-point number specifying a timeout for the operation in seconds + real number specifying a timeout for the operation in seconds (or fractions thereof). As :meth:`~Thread.join` always returns ``None``, you must call :meth:`~Thread.is_alive` after :meth:`~Thread.join` to decide whether a timeout happened -- if the thread is still alive, the @@ -629,6 +632,9 @@ since it is impossible to detect the termination of alien threads. May raise :exc:`PythonFinalizationError`. + .. versionchanged:: 3.15 + Accepts any real number as *timeout*, not only integer or float. + .. attribute:: name A string used for identification purposes only. It has no semantics. @@ -761,7 +767,7 @@ All methods are executed atomically. If a call with *blocking* set to ``True`` would block, return ``False`` immediately; otherwise, set the lock to locked and return ``True``. - When invoked with the floating-point *timeout* argument set to a positive + When invoked with the *timeout* argument set to a positive value, block for at most the number of seconds specified by *timeout* and as long as the lock cannot be acquired. A *timeout* argument of ``-1`` specifies an unbounded wait. It is forbidden to specify a *timeout* @@ -780,6 +786,9 @@ All methods are executed atomically. .. versionchanged:: 3.14 Lock acquisition can now be interrupted by signals on Windows. + .. versionchanged:: 3.15 + Accepts any real number as *timeout*, not only integer or float. + .. method:: release() @@ -860,7 +869,7 @@ call release as many times the lock has been acquired can lead to deadlock. * If no thread owns the lock, acquire the lock and return immediately. * If another thread owns the lock, block until we are able to acquire - lock, or *timeout*, if set to a positive float value. + lock, or *timeout*, if set to a positive value. * If the same thread owns the lock, acquire the lock again, and return immediately. This is the difference between :class:`Lock` and @@ -887,6 +896,9 @@ call release as many times the lock has been acquired can lead to deadlock. .. versionchanged:: 3.2 The *timeout* parameter is new. + .. versionchanged:: 3.15 + Accepts any real number as *timeout*, not only integer or float. + .. method:: release() @@ -1020,7 +1032,7 @@ item to the buffer only needs to wake up one consumer thread. occurs. Once awakened or timed out, it re-acquires the lock and returns. When the *timeout* argument is present and not ``None``, it should be a - floating-point number specifying a timeout for the operation in seconds + real number specifying a timeout for the operation in seconds (or fractions thereof). When the underlying lock is an :class:`RLock`, it is not released using @@ -1147,6 +1159,9 @@ Semaphores also support the :ref:`context management protocol `. .. versionchanged:: 3.2 The *timeout* parameter is new. + .. versionchanged:: 3.15 + Accepts any real number as *timeout*, not only integer or float. + .. method:: release(n=1) Release a semaphore, incrementing the internal counter by *n*. When it @@ -1247,7 +1262,7 @@ method. The :meth:`~Event.wait` method blocks until the flag is true. the internal flag did not become true within the given wait time. When the timeout argument is present and not ``None``, it should be a - floating-point number specifying a timeout for the operation in seconds, + real number specifying a timeout for the operation in seconds, or fractions thereof. .. versionchanged:: 3.1 @@ -1421,3 +1436,159 @@ is equivalent to:: Currently, :class:`Lock`, :class:`RLock`, :class:`Condition`, :class:`Semaphore`, and :class:`BoundedSemaphore` objects may be used as :keyword:`with` statement context managers. + + +Iterator synchronization +------------------------ + +By default, Python iterators do not support concurrent access. Most iterators make +no guarantees when accessed simultaneously from multiple threads. Generator +iterators, for example, raise :exc:`ValueError` if one of their iterator methods +is called while the generator is already executing. The tools in this section +allow reliable concurrency support to be added to ordinary iterators and +iterator-producing callables. + +The :class:`serialize_iterator` wrapper lets multiple threads share a single iterator and +take turns consuming from it. While one thread is running ``__next__()``, the +others block until the iterator becomes available. Each value produced by the +underlying iterator is delivered to exactly one caller. + +The :func:`concurrent_tee` function lets multiple threads each receive the full +stream of values from one underlying iterator. It creates independent iterators +that all draw from the same source. Values are buffered until consumed by all +of the derived iterators. + +.. class:: serialize_iterator(iterable) + + Return an iterator wrapper that serializes concurrent calls to + :meth:`~iterator.__next__` using a lock. + + If the wrapped iterator also defines :meth:`~generator.send`, + :meth:`~generator.throw`, or :meth:`~generator.close`, those calls + are serialized as well. + + This makes it possible to share a single iterator, including a generator + iterator, between multiple threads. A lock ensures that calls are handled + one at a time. No values are duplicated or skipped by the wrapper itself. + Each item from the underlying iterator is given to exactly one caller. + + This wrapper does not copy or buffer values. Threads that call + :func:`next` while another thread is already advancing the iterator will + block until the active call completes. + + Example: + + .. code-block:: python + + import threading + + def squares(n): + for x in range(n): + yield x * x + + def consume(name, iterable): + for item in iterable: + print(name, item) + + source = threading.serialize_iterator(squares(5)) + + t1 = threading.Thread(target=consume, args=("left", source)) + t2 = threading.Thread(target=consume, args=("right", source)) + t1.start() + t2.start() + t1.join() + t2.join() + + In this example, each number is printed exactly once, but the work is shared + between the two threads. + + .. versionadded:: 3.15 + + +.. function:: synchronized_iterator(func) + + Wrap an iterator-producing callable so that each iterator it returns is + automatically passed through :class:`serialize_iterator`. + + This is especially useful as a :term:`decorator` for generator functions, + allowing their generator-iterators to be consumed from multiple threads. + + Example: + + .. code-block:: python + + import threading + + @threading.synchronized_iterator + def squares(n): + for x in range(n): + yield x * x + + def consume(name, iterable): + for item in iterable: + print(name, item) + + source = squares(5) + + t1 = threading.Thread(target=consume, args=("left", source)) + t2 = threading.Thread(target=consume, args=("right", source)) + t1.start() + t2.start() + t1.join() + t2.join() + + The returned wrapper preserves the metadata of *func*, such as its name and + wrapped function reference. + + .. versionadded:: 3.15 + + +.. function:: concurrent_tee(iterable, n=2) + + Return *n* independent iterators from a single input *iterable*, with + guaranteed behavior when the derived iterators are consumed concurrently. + + This function is similar to :func:`itertools.tee`, but is intended for cases + where the source iterator may feed consumers running in different threads. + Each returned iterator yields every value from the underlying iterable, in + the same order. + + Internally, values are buffered until every derived iterator has consumed + them. + + The returned iterators share the same underlying synchronization lock. Each + individual derived iterator is intended to be consumed by one thread at a + time. If a single derived iterator must itself be shared by multiple + threads, wrap it with :class:`serialize_iterator`. + + If *n* is ``0``, return an empty tuple. If *n* is negative, raise + :exc:`ValueError`. + + Example: + + .. code-block:: python + + import threading + + def squares(n): + for x in range(n): + yield x * x + + def consume(name, iterable): + for item in iterable: + print(name, item) + + source = squares(5) + left, right = threading.concurrent_tee(source) + + t1 = threading.Thread(target=consume, args=("left", left)) + t2 = threading.Thread(target=consume, args=("right", right)) + t1.start() + t2.start() + t1.join() + t2.join() + + In this example, both consumer threads see the full sequence of squares + from a single generator expression. + + .. versionadded:: 3.15 diff --git a/Doc/library/threadsafety.rst b/Doc/library/threadsafety.rst new file mode 100644 index 00000000000000..a529f7803affbc --- /dev/null +++ b/Doc/library/threadsafety.rst @@ -0,0 +1,606 @@ +.. _threadsafety: + +************************ +Thread Safety Guarantees +************************ + +This page documents thread-safety guarantees for built-in types in Python's +free-threaded build. The guarantees described here apply when using Python with +the :term:`GIL` disabled (free-threaded mode). When the GIL is enabled, most +operations are implicitly serialized. + +For general guidance on writing thread-safe code in free-threaded Python, see +:ref:`freethreading-python-howto`. + + +.. _threadsafety-levels: + +Thread safety levels +==================== + +The C API documentation uses the following levels to describe the thread +safety guarantees of each function. The levels are listed from least to +most safe. + +.. _threadsafety-level-incompatible: + +Incompatible +------------ + +A function or operation that cannot be made safe for concurrent use even +with external synchronization. Incompatible code typically accesses +global state in an unsynchronized way and must only be called from a single +thread throughout the program's lifetime. + +Example: a function that modifies process-wide state such as signal handlers +or environment variables, where concurrent calls from any threads, even with +external locking, can conflict with the runtime or other libraries. + +.. _threadsafety-level-compatible: + +Compatible +---------- + +A function or operation that is safe to call from multiple threads +*provided* the caller supplies appropriate external synchronization, for +example by holding a :term:`lock` for the duration of each call. Without +such synchronization, concurrent calls may produce :term:`race conditions +` or :term:`data races `. + +Example: a function that reads from or writes to an object whose internal +state is not protected by a lock. Callers must ensure that no two threads +access the same object at the same time. + +.. _threadsafety-level-distinct: + +Safe on distinct objects +------------------------ + +A function or operation that is safe to call from multiple threads without +external synchronization, as long as each thread operates on a **different** +object. Two threads may call the function at the same time, but they must +not pass the same object (or objects that share underlying state) as +arguments. + +Example: a function that modifies fields of a struct using non-atomic +writes. Two threads can each call the function on their own struct +instance safely, but concurrent calls on the *same* instance require +external synchronization. + +.. _threadsafety-level-shared: + +Safe on shared objects +---------------------- + +A function or operation that is safe for concurrent use on the **same** +object. The implementation uses internal synchronization (such as +:term:`per-object locks ` or +:ref:`critical sections `) to protect shared +mutable state, so callers do not need to supply their own locking. + +Example: :c:func:`PyList_GetItemRef` can be called from multiple threads on the +same :c:type:`PyListObject` - it uses internal synchronization to serialize +access. + +.. _threadsafety-level-atomic: + +Atomic +------ + +A function or operation that appears :term:`atomic ` with +respect to other threads - it executes instantaneously from the perspective +of other threads. This is the strongest form of thread safety. + +Example: :c:func:`PyMutex_IsLocked` performs an atomic read of the mutex +state and can be called from any thread at any time. + + +.. _thread-safety-list: + +Thread safety for list objects +============================== + +Reading a single element from a :class:`list` is +:term:`atomic `: + +.. code-block:: + :class: good + + lst[i] # list.__getitem__ + +The following methods traverse the list and use :term:`atomic ` +reads of each item to perform their function. That means that they may +return results affected by concurrent modifications: + +.. code-block:: + :class: maybe + + item in lst + lst.index(item) + lst.count(item) + +All of the above operations avoid acquiring :term:`per-object locks +`. They do not block concurrent modifications. Other +operations that hold a lock will not block these from observing intermediate +states. + +All other operations from here on block using the :term:`per-object lock`. + +Writing a single item via ``lst[i] = x`` is safe to call from multiple +threads and will not corrupt the list. + +The following operations return new objects and appear +:term:`atomic ` to other threads: + +.. code-block:: + :class: good + + lst1 + lst2 # concatenates two lists into a new list + x * lst # repeats lst x times into a new list + lst.copy() # returns a shallow copy of the list + +The following methods that only operate on a single element with no shifting +required are :term:`atomic `: + +.. code-block:: + :class: good + + lst.append(x) # append to the end of the list, no shifting required + lst.pop() # pop element from the end of the list, no shifting required + +The :meth:`~list.clear` method is also :term:`atomic `. +Other threads cannot observe elements being removed. + +The :meth:`~list.sort` method is not :term:`atomic `. +Other threads cannot observe intermediate states during sorting, but the +list appears empty for the duration of the sort. + +The following operations may allow :term:`lock-free` operations to observe +intermediate states since they modify multiple elements in place: + +.. code-block:: + :class: maybe + + lst.insert(idx, item) # shifts elements + lst.pop(idx) # idx not at the end of the list, shifts elements + lst *= x # copies elements in place + +The :meth:`~list.remove` method may allow concurrent modifications since +element comparison may execute arbitrary Python code (via +:meth:`~object.__eq__`). + +:meth:`~list.extend` is safe to call from multiple threads. However, its +guarantees depend on the iterable passed to it. If it is a :class:`list`, a +:class:`tuple`, a :class:`set`, a :class:`frozenset`, a :class:`dict` or a +:ref:`dictionary view object ` (but not their subclasses), the +``extend`` operation is safe from concurrent modifications to the iterable. +Otherwise, an iterator is created which can be concurrently modified by +another thread. The same applies to inplace concatenation of a list with +other iterables when using ``lst += iterable``. + +Similarly, assigning to a list slice with ``lst[i:j] = iterable`` is safe +to call from multiple threads, but ``iterable`` is only locked when it is +also a :class:`list` (but not its subclasses). + +Operations that involve multiple accesses, as well as iteration, are never +atomic. For example: + +.. code-block:: + :class: bad + + # NOT atomic: read-modify-write + lst[i] = lst[i] + 1 + + # NOT atomic: check-then-act + if lst: + item = lst.pop() + + # NOT thread-safe: iteration while modifying + for item in lst: + process(item) # another thread may modify lst + +Consider external synchronization when sharing :class:`list` instances +across threads. + + +.. _thread-safety-dict: + +Thread safety for dict objects +============================== + +Creating a dictionary with the :class:`dict` constructor is atomic when the +argument to it is a :class:`dict` or a :class:`tuple`. When using the +:meth:`dict.fromkeys` method, dictionary creation is atomic when the +argument is a :class:`dict`, :class:`tuple`, :class:`set` or +:class:`frozenset`. + +The following operations and functions are :term:`lock-free` and +:term:`atomic `. + +.. code-block:: + :class: good + + d[key] # dict.__getitem__ + d.get(key) # dict.get + key in d # dict.__contains__ + len(d) # dict.__len__ + +All other operations from here on hold the :term:`per-object lock`. + +Writing or removing a single item is safe to call from multiple threads +and will not corrupt the dictionary: + +.. code-block:: + :class: good + + d[key] = value # write + del d[key] # delete + d.pop(key) # remove and return + d.popitem() # remove and return last item + d.setdefault(key, v) # insert if missing + +These operations may compare keys using :meth:`~object.__eq__`, which can +execute arbitrary Python code. During such comparisons, the dictionary may +be modified by another thread. For built-in types like :class:`str`, +:class:`int`, and :class:`float`, that implement :meth:`~object.__eq__` in C, +the underlying lock is not released during comparisons and this is not a +concern. + +The following operations return new objects and hold the :term:`per-object lock` +for the duration of the operation: + +.. code-block:: + :class: good + + d.copy() # returns a shallow copy of the dictionary + d | other # merges two dicts into a new dict + d.keys() # returns a new dict_keys view object + d.values() # returns a new dict_values view object + d.items() # returns a new dict_items view object + +The :meth:`~dict.clear` method holds the lock for its duration. Other +threads cannot observe elements being removed. + +The following operations lock both dictionaries. For :meth:`~dict.update` +and ``|=``, this applies only when the other operand is a :class:`dict` +that uses the standard dict iterator (but not subclasses that override +iteration). For equality comparison, this applies to :class:`dict` and +its subclasses: + +.. code-block:: + :class: good + + d.update(other_dict) # both locked when other_dict is a dict + d |= other_dict # both locked when other_dict is a dict + d == other_dict # both locked for dict and subclasses + +All comparison operations also compare values using :meth:`~object.__eq__`, +so for non-built-in types the lock may be released during comparison. + +:meth:`~dict.fromkeys` locks both the new dictionary and the iterable +when the iterable is exactly a :class:`dict`, :class:`set`, or +:class:`frozenset` (not subclasses): + +.. code-block:: + :class: good + + dict.fromkeys(a_dict) # locks both + dict.fromkeys(a_set) # locks both + dict.fromkeys(a_frozenset) # locks both + +When updating from a non-dict iterable, only the target dictionary is +locked. The iterable may be concurrently modified by another thread: + +.. code-block:: + :class: maybe + + d.update(iterable) # iterable is not a dict: only d locked + d |= iterable # iterable is not a dict: only d locked + dict.fromkeys(iterable) # iterable is not a dict/set/frozenset: only result locked + +Operations that involve multiple accesses, as well as iteration, are never +atomic: + +.. code-block:: + :class: bad + + # NOT atomic: read-modify-write + d[key] = d[key] + 1 + + # NOT atomic: check-then-act (TOCTOU) + if key in d: + del d[key] + + # NOT thread-safe: iteration while modifying + for key, value in d.items(): + process(key) # another thread may modify d + +To avoid time-of-check to time-of-use (TOCTOU) issues, use atomic +operations or handle exceptions: + +.. code-block:: + :class: good + + # Use pop() with default instead of check-then-delete + d.pop(key, None) + + # Or handle the exception + try: + del d[key] + except KeyError: + pass + +To safely iterate over a dictionary that may be modified by another +thread, iterate over a copy: + +.. code-block:: + :class: good + + # Make a copy to iterate safely + for key, value in d.copy().items(): + process(key) + +Consider external synchronization when sharing :class:`dict` instances +across threads. + + +.. _thread-safety-set: + +Thread safety for set objects +============================== + +The :func:`len` function is lock-free and :term:`atomic `. + +The following read operation is lock-free. It does not block concurrent +modifications and may observe intermediate states from operations that +hold the per-object lock: + +.. code-block:: + :class: good + + elem in s # set.__contains__ + +This operation may compare elements using :meth:`~object.__eq__`, which can +execute arbitrary Python code. During such comparisons, the set may be +modified by another thread. For built-in types like :class:`str`, +:class:`int`, and :class:`float`, :meth:`!__eq__` does not release the +underlying lock during comparisons and this is not a concern. + +All other operations from here on hold the per-object lock. + +Adding or removing a single element is safe to call from multiple threads +and will not corrupt the set: + +.. code-block:: + :class: good + + s.add(elem) # add element + s.remove(elem) # remove element, raise if missing + s.discard(elem) # remove element if present + s.pop() # remove and return arbitrary element + +These operations also compare elements, so the same :meth:`~object.__eq__` +considerations as above apply. + +The :meth:`~set.copy` method returns a new object and holds the per-object lock +for the duration so that it is always atomic. + +The :meth:`~set.clear` method holds the lock for its duration. Other +threads cannot observe elements being removed. + +The following operations only accept :class:`set` or :class:`frozenset` +as operands and always lock both objects: + +.. code-block:: + :class: good + + s |= other # other must be set/frozenset + s &= other # other must be set/frozenset + s -= other # other must be set/frozenset + s ^= other # other must be set/frozenset + s & other # other must be set/frozenset + s | other # other must be set/frozenset + s - other # other must be set/frozenset + s ^ other # other must be set/frozenset + +:meth:`set.update`, :meth:`set.union`, :meth:`set.intersection` and +:meth:`set.difference` can take multiple iterables as arguments. They all +iterate through all the passed iterables and do the following: + + * :meth:`set.update` and :meth:`set.union` lock both objects only when + the other operand is a :class:`set`, :class:`frozenset`, or :class:`dict`. + * :meth:`set.intersection` and :meth:`set.difference` always try to lock + all objects. + +:meth:`set.symmetric_difference` tries to lock both objects. + +The update variants of the above methods also have some differences between +them: + + * :meth:`set.difference_update` and :meth:`set.intersection_update` try + to lock all objects one-by-one. + * :meth:`set.symmetric_difference_update` only locks the arguments if it is + of type :class:`set`, :class:`frozenset`, or :class:`dict`. + +The following methods always try to lock both objects: + +.. code-block:: + :class: good + + s.isdisjoint(other) # both locked + s.issubset(other) # both locked + s.issuperset(other) # both locked + +Operations that involve multiple accesses, as well as iteration, are never +atomic: + +.. code-block:: + :class: bad + + # NOT atomic: check-then-act + if elem in s: + s.remove(elem) + + # NOT thread-safe: iteration while modifying + for elem in s: + process(elem) # another thread may modify s + +Consider external synchronization when sharing :class:`set` instances +across threads. See :ref:`freethreading-python-howto` for more information. + + +.. _thread-safety-bytearray: + +Thread safety for bytearray objects +=================================== + + The :func:`len` function is lock-free and :term:`atomic `. + + Concatenation and comparisons use the buffer protocol, which prevents + resizing but does not hold the per-object lock. These operations may + observe intermediate states from concurrent modifications: + + .. code-block:: + :class: maybe + + ba + other # may observe concurrent writes + ba == other # may observe concurrent writes + ba < other # may observe concurrent writes + + All other operations from here on hold the per-object lock. + + Reading a single element or slice is safe to call from multiple threads: + + .. code-block:: + :class: good + + ba[i] # bytearray.__getitem__ + ba[i:j] # slice + + The following operations are safe to call from multiple threads and will + not corrupt the bytearray: + + .. code-block:: + :class: good + + ba[i] = x # write single byte + ba[i:j] = values # write slice + ba.append(x) # append single byte + ba.extend(other) # extend with iterable + ba.insert(i, x) # insert single byte + ba.pop() # remove and return last byte + ba.pop(i) # remove and return byte at index + ba.remove(x) # remove first occurrence + ba.reverse() # reverse in place + ba.clear() # remove all bytes + + Slice assignment locks both objects when *values* is a :class:`bytearray`: + + .. code-block:: + :class: good + + ba[i:j] = other_bytearray # both locked + + The following operations return new objects and hold the per-object lock + for the duration: + + .. code-block:: + :class: good + + ba.copy() # returns a shallow copy + ba * n # repeat into new bytearray + + The membership test holds the lock for its duration: + + .. code-block:: + :class: good + + x in ba # bytearray.__contains__ + + All other bytearray methods (such as :meth:`~bytearray.find`, + :meth:`~bytearray.replace`, :meth:`~bytearray.split`, + :meth:`~bytearray.decode`, etc.) hold the per-object lock for their + duration. + + Operations that involve multiple accesses, as well as iteration, are never + atomic: + + .. code-block:: + :class: bad + + # NOT atomic: check-then-act + if x in ba: + ba.remove(x) + + # NOT thread-safe: iteration while modifying + for byte in ba: + process(byte) # another thread may modify ba + + To safely iterate over a bytearray that may be modified by another + thread, iterate over a copy: + + .. code-block:: + :class: good + + # Make a copy to iterate safely + for byte in ba.copy(): + process(byte) + + Consider external synchronization when sharing :class:`bytearray` instances + across threads. See :ref:`freethreading-python-howto` for more information. + + +.. _thread-safety-memoryview: + +Thread safety for memoryview objects +==================================== + +:class:`memoryview` objects provide access to the internal data of an +underlying object without copying. Thread safety depends on both the +memoryview itself and the underlying buffer exporter. + +The memoryview implementation uses atomic operations to track its own +exports in the :term:`free-threaded build`. Creating and +releasing a memoryview are thread-safe. Attribute access (e.g., +:attr:`~memoryview.shape`, :attr:`~memoryview.format`) reads fields that +are immutable for the lifetime of the memoryview, so concurrent reads +are safe as long as the memoryview has not been released. + +However, the actual data accessed through the memoryview is owned by the +underlying object. Concurrent access to this data is only safe if the +underlying object supports it: + +* For immutable objects like :class:`bytes`, concurrent reads through + multiple memoryviews are safe. + +* For mutable objects like :class:`bytearray`, reading and writing the + same memory region from multiple threads without external + synchronization is not safe and may result in data corruption. + Note that even read-only memoryviews of mutable objects do not + prevent data races if the underlying object is modified from + another thread. + +.. code-block:: + :class: bad + + # NOT safe: concurrent writes to the same buffer + data = bytearray(1000) + view = memoryview(data) + # Thread 1: view[0:500] = b'x' * 500 + # Thread 2: view[0:500] = b'y' * 500 + +.. code-block:: + :class: good + + # Safe: use a lock for concurrent access + import threading + lock = threading.Lock() + data = bytearray(1000) + view = memoryview(data) + + with lock: + view[0:500] = b'x' * 500 + +Resizing or reallocating the underlying object (such as calling +:meth:`bytearray.resize`) while a memoryview is exported raises +:exc:`BufferError`. This is enforced regardless of threading. diff --git a/Doc/library/time.rst b/Doc/library/time.rst index df9be68bf4f69d..a931134331f0a5 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -189,7 +189,7 @@ Functions .. versionadded:: 3.7 -.. function:: clock_settime(clk_id, time: float) +.. function:: clock_settime(clk_id, time) Set the time of the specified clock *clk_id*. Currently, :data:`CLOCK_REALTIME` is the only accepted value for *clk_id*. @@ -201,6 +201,9 @@ Functions .. versionadded:: 3.3 + .. versionchanged:: 3.15 + Accepts any real number as *time*, not only integer or float. + .. function:: clock_settime_ns(clk_id, time: int) @@ -223,6 +226,9 @@ Functions ``asctime(localtime(secs))``. Locale information is not used by :func:`ctime`. + .. versionchanged:: 3.15 + Accepts any real number, not only integer or float. + .. function:: get_clock_info(name) @@ -238,8 +244,8 @@ Functions The result has the following attributes: - - *adjustable*: ``True`` if the clock can be changed automatically (e.g. by - a NTP daemon) or manually by the system administrator, ``False`` otherwise + - *adjustable*: ``True`` if the clock can be set to jump forward or backward + in time, ``False`` otherwise. Does not refer to gradual NTP rate adjustments. - *implementation*: The name of the underlying C function used to get the clock value. Refer to :ref:`time-clock-id-constants` for possible values. - *monotonic*: ``True`` if the clock cannot go backward, @@ -258,6 +264,9 @@ Functions :class:`struct_time` object. See :func:`calendar.timegm` for the inverse of this function. + .. versionchanged:: 3.15 + Accepts any real number, not only integer or float. + .. function:: localtime([secs]) @@ -271,6 +280,9 @@ Functions :c:func:`gmtime` failure. It's common for this to be restricted to years between 1970 and 2038. + .. versionchanged:: 3.15 + Accepts any real number, not only integer or float. + .. function:: mktime(t) @@ -382,8 +394,7 @@ Functions .. function:: sleep(secs) Suspend execution of the calling thread for the given number of seconds. - The argument may be a floating-point number to indicate a more precise sleep - time. + The argument may be a non-integer to indicate a more precise sleep time. If the sleep is interrupted by a signal and no exception is raised by the signal handler, the sleep is restarted with a recomputed timeout. @@ -396,9 +407,9 @@ Functions On Windows, if *secs* is zero, the thread relinquishes the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run, the function returns immediately, and the thread - continues execution. On Windows 8.1 and newer the implementation uses + continues execution. On Windows 10 and newer the implementation uses a `high-resolution timer - `_ + `_ which provides resolution of 100 nanoseconds. If *secs* is zero, ``Sleep(0)`` is used. .. rubric:: Unix implementation @@ -428,6 +439,9 @@ Functions .. versionchanged:: 3.13 Raises an auditing event. + .. versionchanged:: 3.15 + Accepts any real number, not only integer or float. + .. index:: single: % (percent); datetime format @@ -570,7 +584,7 @@ Functions calculations when the day of the week and the year are specified. Here is an example, a format for dates compatible with that specified in the - :rfc:`2822` Internet email standard. [1]_ :: + :rfc:`5322` Internet email standard. [1]_ :: >>> from time import gmtime, strftime >>> strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime()) @@ -935,7 +949,7 @@ These constants are used as parameters for :func:`clock_getres` and .. data:: CLOCK_TAI - `International Atomic Time `_ + `International Atomic Time `_ The system must have a current leap second table in order for this to give the correct answer. PTP or NTP software can maintain a leap second table. @@ -1052,4 +1066,5 @@ Timezone Constants strict reading of the original 1982 :rfc:`822` standard calls for a two-digit year (``%y`` rather than ``%Y``), but practice moved to 4-digit years long before the year 2000. After that, :rfc:`822` became obsolete and the 4-digit year has - been first recommended by :rfc:`1123` and then mandated by :rfc:`2822`. + been first recommended by :rfc:`1123` and then mandated by :rfc:`2822`, + with :rfc:`5322` continuing this requirement. diff --git a/Doc/library/timeit.rst b/Doc/library/timeit.rst index 548a3ee0540506..dd574fce09f5fc 100644 --- a/Doc/library/timeit.rst +++ b/Doc/library/timeit.rst @@ -19,7 +19,7 @@ See also Tim Peters' introduction to the "Algorithms" chapter in the second edition of *Python Cookbook*, published by O'Reilly. -Basic Examples +Basic examples -------------- The following example shows how the :ref:`timeit-command-line-interface` @@ -56,7 +56,7 @@ repetitions only when the command-line interface is used. In the .. _python-interface: -Python Interface +Python interface ---------------- The module defines three convenience functions and a public class: @@ -143,21 +143,24 @@ The module defines three convenience functions and a public class: timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit() - .. method:: Timer.autorange(callback=None) + .. method:: Timer.autorange(callback=None, target_time=None) Automatically determine how many times to call :meth:`.timeit`. This is a convenience function that calls :meth:`.timeit` repeatedly - so that the total time >= 0.2 second, returning the eventual + so that the total time >= *Timer.target_time* seconds, returning the eventual (number of loops, time taken for that number of loops). It calls :meth:`.timeit` with increasing numbers from the sequence 1, 2, 5, - 10, 20, 50, ... until the time taken is at least 0.2 seconds. + 10, 20, 50, ... until the time taken is at least *target_time* seconds. If *callback* is given and is not ``None``, it will be called after each trial with two arguments: ``callback(number, time_taken)``. .. versionadded:: 3.6 + .. versionchanged:: 3.15 + The optional *target_time* parameter was added. + .. method:: Timer.repeat(repeat=5, number=1000000) @@ -203,7 +206,7 @@ The module defines three convenience functions and a public class: .. _timeit-command-line-interface: -Command-Line Interface +Command-line interface ---------------------- When called as a program from the command line, the following form is used:: @@ -239,6 +242,13 @@ Where the following options are understood: .. versionadded:: 3.5 +.. option:: -t, --target-time=T + + if :option:`--number` is 0, the code will run until it takes at + least this many seconds (default: 0.2) + + .. versionadded:: 3.15 + .. option:: -v, --verbose print raw timing results; repeat for more digits precision @@ -254,7 +264,7 @@ similarly. If :option:`-n` is not given, a suitable number of loops is calculated by trying increasing numbers from the sequence 1, 2, 5, 10, 20, 50, ... until the total -time is at least 0.2 seconds. +time is at least :option:`--target-time` seconds (default: 0.2). :func:`default_timer` measurements can be affected by other programs running on the same machine, so the best thing to do when accurate timing is necessary is @@ -269,6 +279,9 @@ most cases. You can use :func:`time.process_time` to measure CPU time. baseline overhead can be measured by invoking the program without arguments, and it might differ between Python versions. +.. versionadded:: 3.15 + Output is in color by default and can be + :ref:`controlled using environment variables `. .. _timeit-examples: @@ -355,7 +368,7 @@ to test for missing and present object attributes: 0.08588060699912603 -To give the :mod:`timeit` module access to functions you define, you can pass a +To give the :mod:`!timeit` module access to functions you define, you can pass a *setup* parameter which contains an import statement:: def test(): diff --git a/Doc/library/tk.rst b/Doc/library/tk.rst index 0593f8b73ea545..fa3c7e910ce21f 100644 --- a/Doc/library/tk.rst +++ b/Doc/library/tk.rst @@ -1,7 +1,7 @@ .. _tkinter: ********************************* -Graphical User Interfaces with Tk +Graphical user interfaces with Tk ********************************* .. index:: @@ -39,6 +39,7 @@ alternative `GUI frameworks and tools - **Source code:** :source:`Lib/tkinter/__init__.py` -------------- -The :mod:`tkinter` package ("Tk interface") is the standard Python interface to -the Tcl/Tk GUI toolkit. Both Tk and :mod:`tkinter` are available on most Unix +The :mod:`!tkinter` package ("Tk interface") is the standard Python interface to +the Tcl/Tk GUI toolkit. Both Tk and :mod:`!tkinter` are available on most Unix platforms, including macOS, as well as on Windows systems. Running ``python -m tkinter`` from the command line should open a window -demonstrating a simple Tk interface, letting you know that :mod:`tkinter` is +demonstrating a simple Tk interface, letting you know that :mod:`!tkinter` is properly installed on your system, and also showing what version of Tcl/Tk is installed, so you can read the Tcl/Tk documentation specific to that version. @@ -36,6 +34,8 @@ details that are unchanged. Most documentation you will find online still uses the old API and can be woefully outdated. +.. include:: ../includes/optional-module.rst + .. seealso:: * `TkDocs `_ @@ -106,7 +106,7 @@ Internally, Tk and Ttk use facilities of the underlying operating system, i.e., Xlib on Unix/X11, Cocoa on macOS, GDI on Windows. When your Python application uses a class in Tkinter, e.g., to create a widget, -the :mod:`tkinter` module first assembles a Tcl/Tk command string. It passes that +the :mod:`!tkinter` module first assembles a Tcl/Tk command string. It passes that Tcl command string to an internal :mod:`_tkinter` binary module, which then calls the Tcl interpreter to evaluate it. The Tcl interpreter will then call into the Tk and/or Ttk packages, which will in turn make calls to Xlib, Cocoa, or GDI. @@ -116,7 +116,7 @@ Tkinter Modules --------------- Support for Tkinter is spread across several modules. Most applications will need the -main :mod:`tkinter` module, as well as the :mod:`tkinter.ttk` module, which provides +main :mod:`!tkinter` module, as well as the :mod:`tkinter.ttk` module, which provides the modern themed widget set and API:: @@ -175,12 +175,12 @@ the modern themed widget set and API:: .. attribute:: master The widget object that contains this widget. For :class:`Tk`, the - *master* is :const:`None` because it is the main window. The terms + :attr:`!master` is :const:`None` because it is the main window. The terms *master* and *parent* are similar and sometimes used interchangeably as argument names; however, calling :meth:`winfo_parent` returns a - string of the widget name whereas :attr:`master` returns the object. + string of the widget name whereas :attr:`!master` returns the object. *parent*/*child* reflects the tree-like relationship while - *master*/*slave* reflects the container structure. + *master* (or *container*)/*content* reflects the container structure. .. attribute:: children @@ -202,7 +202,7 @@ the modern themed widget set and API:: The modules that provide Tk support include: -:mod:`tkinter` +:mod:`!tkinter` Main Tkinter module. :mod:`tkinter.colorchooser` @@ -228,7 +228,7 @@ The modules that provide Tk support include: :mod:`tkinter.ttk` Themed widget set introduced in Tk 8.5, providing modern alternatives - for many of the classic widgets in the main :mod:`tkinter` module. + for many of the classic widgets in the main :mod:`!tkinter` module. Additional modules: @@ -237,22 +237,22 @@ Additional modules: :mod:`_tkinter` A binary module that contains the low-level interface to Tcl/Tk. - It is automatically imported by the main :mod:`tkinter` module, + It is automatically imported by the main :mod:`!tkinter` module, and should never be used directly by application programmers. It is usually a shared library (or DLL), but might in some cases be statically linked with the Python interpreter. :mod:`idlelib` Python's Integrated Development and Learning Environment (IDLE). Based - on :mod:`tkinter`. + on :mod:`!tkinter`. :mod:`tkinter.constants` Symbolic constants that can be used in place of strings when passing various parameters to Tkinter calls. Automatically imported by the - main :mod:`tkinter` module. + main :mod:`!tkinter` module. :mod:`tkinter.dnd` - (experimental) Drag-and-drop support for :mod:`tkinter`. This will + (experimental) Drag-and-drop support for :mod:`!tkinter`. This will become deprecated when it is replaced with the Tk DND. :mod:`turtle` @@ -392,7 +392,7 @@ by spaces. Without getting into too many details, notice the following: * Operations which are implemented as separate *commands* in Tcl (like ``grid`` or ``destroy``) are represented as *methods* on Tkinter widget objects. As you'll see shortly, at other times Tcl uses what appear to be - method calls on widget objects, which more closely mirror what would is + method calls on widget objects, which more closely mirror what is used in Tkinter. @@ -502,7 +502,7 @@ documentation for all of these in the Threading model --------------- -Python and Tcl/Tk have very different threading models, which :mod:`tkinter` +Python and Tcl/Tk have very different threading models, which :mod:`!tkinter` tries to bridge. If you use threads, you may need to be aware of this. A Python interpreter may have many threads associated with it. In Tcl, multiple @@ -510,9 +510,9 @@ threads can be created, but each thread has a separate Tcl interpreter instance associated with it. Threads can also create more than one interpreter instance, though each interpreter instance can be used only by the one thread that created it. -Each :class:`Tk` object created by :mod:`tkinter` contains a Tcl interpreter. +Each :class:`Tk` object created by :mod:`!tkinter` contains a Tcl interpreter. It also keeps track of which thread created that interpreter. Calls to -:mod:`tkinter` can be made from any Python thread. Internally, if a call comes +:mod:`!tkinter` can be made from any Python thread. Internally, if a call comes from a thread other than the one that created the :class:`Tk` object, an event is posted to the interpreter's event queue, and when executed, the result is returned to the calling Python thread. @@ -527,17 +527,17 @@ toolkits where the GUI runs in a completely separate thread from all application code including event handlers. If the Tcl interpreter is not running the event loop and processing events, any -:mod:`tkinter` calls made from threads other than the one running the Tcl +:mod:`!tkinter` calls made from threads other than the one running the Tcl interpreter will fail. A number of special cases exist: * Tcl/Tk libraries can be built so they are not thread-aware. In this case, - :mod:`tkinter` calls the library from the originating Python thread, even + :mod:`!tkinter` calls the library from the originating Python thread, even if this is different than the thread that created the Tcl interpreter. A global lock ensures only one call occurs at a time. -* While :mod:`tkinter` allows you to create more than one instance of a :class:`Tk` +* While :mod:`!tkinter` allows you to create more than one instance of a :class:`Tk` object (with its own interpreter), all interpreters that are part of the same thread share a common event queue, which gets ugly fast. In practice, don't create more than one instance of :class:`Tk` at a time. Otherwise, it's best to create @@ -548,7 +548,7 @@ A number of special cases exist: or abandon the event loop entirely. If you're doing anything tricky when it comes to events or threads, be aware of these possibilities. -* There are a few select :mod:`tkinter` functions that presently work only when +* There are a few select :mod:`!tkinter` functions that presently work only when called from the thread that created the Tcl interpreter. @@ -636,15 +636,15 @@ The Packer .. index:: single: packing (widgets) The packer is one of Tk's geometry-management mechanisms. Geometry managers -are used to specify the relative positioning of widgets within their container - -their mutual *master*. In contrast to the more cumbersome *placer* (which is +are used to specify the relative positioning of widgets within their container. +In contrast to the more cumbersome *placer* (which is used less commonly, and we do not cover here), the packer takes qualitative relationship specification - *above*, *to the left of*, *filling*, etc - and works everything out to determine the exact placement coordinates for you. -The size of any *master* widget is determined by the size of the "slave widgets" -inside. The packer is used to control where slave widgets appear inside the -master into which they are packed. You can pack widgets into frames, and frames +The size of any container widget is determined by the size of the "content widgets" +inside. The packer is used to control where content widgets appear inside the +container into which they are packed. You can pack widgets into frames, and frames into other frames, in order to achieve the kind of layout you desire. Additionally, the arrangement is dynamically adjusted to accommodate incremental changes to the configuration, once it is packed. @@ -671,7 +671,7 @@ For more extensive information on the packer and the options that it can take, see the man pages and page 183 of John Ousterhout's book. anchor - Anchor type. Denotes where the packer is to place each slave in its parcel. + Anchor type. Denotes where the packer is to place each content in its parcel. expand Boolean, ``0`` or ``1``. @@ -680,10 +680,10 @@ fill Legal values: ``'x'``, ``'y'``, ``'both'``, ``'none'``. ipadx and ipady - A distance - designating internal padding on each side of the slave widget. + A distance - designating internal padding on each side of the content. padx and pady - A distance - designating external padding on each side of the slave widget. + A distance - designating external padding on each side of the content. side Legal values are: ``'left'``, ``'right'``, ``'top'``, ``'bottom'``. @@ -698,11 +698,11 @@ options are ``variable``, ``textvariable``, ``onvalue``, ``offvalue``, and ``value``. This connection works both ways: if the variable changes for any reason, the widget it's connected to will be updated to reflect the new value. -Unfortunately, in the current implementation of :mod:`tkinter` it is not +Unfortunately, in the current implementation of :mod:`!tkinter` it is not possible to hand over an arbitrary Python variable to a widget through a ``variable`` or ``textvariable`` option. The only kinds of variables for which this works are variables that are subclassed from a class called Variable, -defined in :mod:`tkinter`. +defined in :mod:`!tkinter`. There are many useful subclasses of Variable already defined: :class:`StringVar`, :class:`IntVar`, :class:`DoubleVar`, and @@ -750,14 +750,14 @@ The Window Manager In Tk, there is a utility command, ``wm``, for interacting with the window manager. Options to the ``wm`` command allow you to control things like titles, -placement, icon bitmaps, and the like. In :mod:`tkinter`, these commands have +placement, icon bitmaps, and the like. In :mod:`!tkinter`, these commands have been implemented as methods on the :class:`Wm` class. Toplevel widgets are subclassed from the :class:`Wm` class, and so can call the :class:`Wm` methods directly. To get at the toplevel window that contains a given widget, you can often just -refer to the widget's master. Of course if the widget has been packed inside of -a frame, the master won't represent a toplevel window. To get at the toplevel +refer to the widget's :attr:`master`. Of course if the widget has been packed inside of +a frame, the :attr:`!master` won't represent a toplevel window. To get at the toplevel window that contains an arbitrary widget, you can call the :meth:`_root` method. This method begins with an underscore to denote the fact that this function is part of the implementation, and not an interface to Tk functionality. @@ -839,8 +839,7 @@ geometry For example: ``fred["geometry"] = "200x100"``. justify - Legal values are the strings: ``"left"``, ``"center"``, ``"right"``, and - ``"fill"``. + Legal values are the strings: ``"left"``, ``"center"``, and ``"right"``. region This is a string with four space-delimited elements, each of which is a legal @@ -933,7 +932,7 @@ Entry widget, or to particular menu items in a Menu widget. Entry widget indexes (index, view index, etc.) Entry widgets have options that refer to character positions in the text being - displayed. You can use these :mod:`tkinter` functions to access these special + displayed. You can use these :mod:`!tkinter` functions to access these special points in text widgets: Text widget indexes diff --git a/Doc/library/tkinter.scrolledtext.rst b/Doc/library/tkinter.scrolledtext.rst index 763e24929d74b5..eb30b9c3eacc1b 100644 --- a/Doc/library/tkinter.scrolledtext.rst +++ b/Doc/library/tkinter.scrolledtext.rst @@ -2,16 +2,13 @@ ===================================================== .. module:: tkinter.scrolledtext - :platform: Tk :synopsis: Text widget with a vertical scroll bar. -.. sectionauthor:: Fred L. Drake, Jr. - **Source code:** :source:`Lib/tkinter/scrolledtext.py` -------------- -The :mod:`tkinter.scrolledtext` module provides a class of the same name which +The :mod:`!tkinter.scrolledtext` module provides a class of the same name which implements a basic text widget which has a vertical scroll bar configured to do the "right thing." Using the :class:`ScrolledText` class is a lot easier than setting up a text widget and scroll bar directly. diff --git a/Doc/library/tkinter.ttk.rst b/Doc/library/tkinter.ttk.rst index 628e9f945ac365..e1383e189a31a2 100644 --- a/Doc/library/tkinter.ttk.rst +++ b/Doc/library/tkinter.ttk.rst @@ -4,20 +4,18 @@ .. module:: tkinter.ttk :synopsis: Tk themed widget set -.. sectionauthor:: Guilherme Polo - **Source code:** :source:`Lib/tkinter/ttk.py` .. index:: single: ttk -------------- -The :mod:`tkinter.ttk` module provides access to the Tk themed widget set, +The :mod:`!tkinter.ttk` module provides access to the Tk themed widget set, introduced in Tk 8.5. It provides additional benefits including anti-aliased font rendering under X11 and window transparency (requiring a composition window manager on X11). -The basic idea for :mod:`tkinter.ttk` is to separate, to the extent possible, +The basic idea for :mod:`!tkinter.ttk` is to separate, to the extent possible, the code implementing a widget's behavior from the code implementing its appearance. @@ -40,7 +38,7 @@ To override the basic Tk widgets, the import should follow the Tk import:: from tkinter import * from tkinter.ttk import * -That code causes several :mod:`tkinter.ttk` widgets (:class:`Button`, +That code causes several :mod:`!tkinter.ttk` widgets (:class:`Button`, :class:`Checkbutton`, :class:`Entry`, :class:`Frame`, :class:`Label`, :class:`LabelFrame`, :class:`Menubutton`, :class:`PanedWindow`, :class:`Radiobutton`, :class:`Scale` and :class:`Scrollbar`) to diff --git a/Doc/library/token.rst b/Doc/library/token.rst index c228006d4c1e1d..3253be96238c35 100644 --- a/Doc/library/token.rst +++ b/Doc/library/token.rst @@ -4,8 +4,6 @@ .. module:: token :synopsis: Constants representing terminal nodes of the parse tree. -.. sectionauthor:: Fred L. Drake, Jr. - **Source code:** :source:`Lib/token.py` -------------- @@ -50,8 +48,7 @@ The token constants are: .. data:: NAME - Token value that indicates an :ref:`identifier `. - Note that keywords are also initially tokenized as ``NAME`` tokens. + Token value that indicates an :ref:`identifier or keyword `. .. data:: NUMBER diff --git a/Doc/library/tokenize.rst b/Doc/library/tokenize.rst index b80917eae66f8b..2eea51734fde03 100644 --- a/Doc/library/tokenize.rst +++ b/Doc/library/tokenize.rst @@ -4,14 +4,11 @@ .. module:: tokenize :synopsis: Lexical scanner for Python source code. -.. moduleauthor:: Ka Ping Yee -.. sectionauthor:: Fred L. Drake, Jr. - **Source code:** :source:`Lib/tokenize.py` -------------- -The :mod:`tokenize` module provides a lexical scanner for Python source code, +The :mod:`!tokenize` module provides a lexical scanner for Python source code, implemented in Python. The scanner in this module returns comments as tokens as well, making it useful for implementing "pretty-printers", including colorizers for on-screen displays. @@ -31,7 +28,7 @@ type can be determined by checking the ``exact_type`` property on the **undefined** when providing invalid Python code and it can change at any point. -Tokenizing Input +Tokenizing input ---------------- The primary entry point is a :term:`generator`: @@ -78,7 +75,7 @@ The primary entry point is a :term:`generator`: :func:`.tokenize`. It does not yield an :data:`~token.ENCODING` token. All constants from the :mod:`token` module are also exported from -:mod:`tokenize`. +:mod:`!tokenize`. Another function is provided to reverse the tokenization process. This is useful for creating tools that tokenize a script, modify the token stream, and @@ -149,12 +146,12 @@ function it uses to do this is available: .. _tokenize-cli: -Command-Line Usage +Command-line usage ------------------ .. versionadded:: 3.3 -The :mod:`tokenize` module can be executed as a script from the command line. +The :mod:`!tokenize` module can be executed as a script from the command line. It is as simple as: .. code-block:: sh @@ -176,8 +173,12 @@ The following options are accepted: If :file:`filename.py` is specified its contents are tokenized to stdout. Otherwise, tokenization is performed on stdin. +.. versionadded:: 3.15 + Output is in color by default and can be + :ref:`controlled using environment variables `. + Examples ------------------- +-------- Example of a script rewriter that transforms float literals into Decimal objects:: @@ -230,7 +231,7 @@ Example of tokenizing from the command line. The script:: will be tokenized to the following output where the first column is the range of the line/column coordinates where the token is found, the second column is -the name of the token, and the final column is the value of the token (if any) +the name of the token, and the final column is the value of the token (if any): .. code-block:: shell-session diff --git a/Doc/library/tomllib.rst b/Doc/library/tomllib.rst index 30d7ff50a1acc1..55610784362eb8 100644 --- a/Doc/library/tomllib.rst +++ b/Doc/library/tomllib.rst @@ -4,19 +4,28 @@ .. module:: tomllib :synopsis: Parse TOML files. -.. versionadded:: 3.11 - -.. moduleauthor:: Taneli Hukkinen -.. sectionauthor:: Taneli Hukkinen - **Source code:** :source:`Lib/tomllib` -------------- -This module provides an interface for parsing TOML 1.0.0 (Tom's Obvious Minimal +This module provides an interface for parsing TOML 1.1.0 (Tom's Obvious Minimal Language, `https://toml.io `_). This module does not support writing TOML. +.. versionadded:: 3.11 + The module was added with support for TOML 1.0.0. + +.. versionchanged:: 3.15 + Added TOML 1.1.0 support. + See the :ref:`What's New ` for details. + +.. warning:: + + Be cautious when parsing data from untrusted sources. + A malicious TOML string may cause the decoder to consume considerable + CPU and memory resources. + Limiting the size of data to be parsed is recommended. + .. seealso:: The :pypi:`Tomli-W package ` diff --git a/Doc/library/trace.rst b/Doc/library/trace.rst index cae94ea08e17e5..57b8fa29eb3600 100644 --- a/Doc/library/trace.rst +++ b/Doc/library/trace.rst @@ -8,7 +8,7 @@ -------------- -The :mod:`trace` module allows you to trace program execution, generate +The :mod:`!trace` module allows you to trace program execution, generate annotated statement coverage listings, print caller/callee relationships and list functions executed during a program run. It can be used in another program or from the command line. @@ -24,7 +24,7 @@ or from the command line. Command-Line Usage ------------------ -The :mod:`trace` module can be invoked from the command line. It can be as +The :mod:`!trace` module can be invoked from the command line. It can be as simple as :: python -m trace --count -C . somefile.py ... @@ -43,13 +43,13 @@ all Python modules imported during the execution into the current directory. Display the version of the module and exit. .. versionadded:: 3.8 - Added ``--module`` option that allows to run an executable module. + Added ``--module`` option that allows running an executable module. Main options ^^^^^^^^^^^^ At least one of the following options must be specified when invoking -:mod:`trace`. The :option:`--listfuncs <-l>` option is mutually exclusive with +:mod:`!trace`. The :option:`--listfuncs <-l>` option is mutually exclusive with the :option:`--trace <-t>` and :option:`--count <-c>` options. When :option:`--listfuncs <-l>` is provided, neither :option:`--count <-c>` nor :option:`--trace <-t>` are accepted, and vice versa. diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index b5464ac55ddfa9..aa48cea357cfd3 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -147,9 +147,7 @@ Module-Level Functions :ref:`traceback object ` *tb*. It is useful for alternate formatting of stack traces. The optional *limit* argument has the same meaning as for :func:`print_tb`. A "pre-processed" stack trace - entry is a :class:`FrameSummary` object containing attributes - :attr:`~FrameSummary.filename`, :attr:`~FrameSummary.lineno`, - :attr:`~FrameSummary.name`, and :attr:`~FrameSummary.line` representing the + entry is a :class:`FrameSummary` object with attributes representing the information that is usually printed for a stack trace. @@ -181,7 +179,7 @@ Module-Level Functions .. function:: format_exception_only(exc, /[, value], *, show_group=False) Format the exception part of a traceback using an exception value such as - given by :data:`sys.last_value`. The return value is a list of strings, each + given by :data:`sys.last_exc`. The return value is a list of strings, each ending in a newline. The list contains the exception's message, which is normally a single string; however, for :exc:`SyntaxError` exceptions, it contains several lines that (when printed) display detailed information @@ -347,7 +345,7 @@ the module-level functions described above. .. attribute:: exc_type - The class of the original traceback. + The class of the original exception. .. deprecated:: 3.13 @@ -391,7 +389,7 @@ the module-level functions described above. For syntax errors - the compiler error message. - .. classmethod:: from_exception(exc, *, limit=None, lookup_lines=True, capture_locals=False) + .. classmethod:: from_exception(exc, *, limit=None, lookup_lines=True, capture_locals=False, compact=False, max_group_width=15, max_group_depth=10) Capture an exception for later rendering. *limit*, *lookup_lines* and *capture_locals* are as for the :class:`StackSummary` class. diff --git a/Doc/library/tracemalloc.rst b/Doc/library/tracemalloc.rst index 2370d927292eb0..0fa70389f1f577 100644 --- a/Doc/library/tracemalloc.rst +++ b/Doc/library/tracemalloc.rst @@ -307,7 +307,7 @@ Functions .. function:: get_object_traceback(obj) Get the traceback where the Python object *obj* was allocated. - Return a :class:`Traceback` instance, or ``None`` if the :mod:`tracemalloc` + Return a :class:`Traceback` instance, or ``None`` if the :mod:`!tracemalloc` module is not tracing memory allocations or did not trace the allocation of the object. @@ -318,7 +318,7 @@ Functions Get the maximum number of frames stored in the traceback of a trace. - The :mod:`tracemalloc` module must be tracing memory allocations to + The :mod:`!tracemalloc` module must be tracing memory allocations to get the limit, otherwise an exception is raised. The limit is set by the :func:`start` function. @@ -327,15 +327,15 @@ Functions .. function:: get_traced_memory() Get the current size and peak size of memory blocks traced by the - :mod:`tracemalloc` module as a tuple: ``(current: int, peak: int)``. + :mod:`!tracemalloc` module as a tuple: ``(current: int, peak: int)``. .. function:: reset_peak() - Set the peak size of memory blocks traced by the :mod:`tracemalloc` module + Set the peak size of memory blocks traced by the :mod:`!tracemalloc` module to the current size. - Do nothing if the :mod:`tracemalloc` module is not tracing memory + Do nothing if the :mod:`!tracemalloc` module is not tracing memory allocations. This function only modifies the recorded peak size, and does not modify or @@ -350,14 +350,14 @@ Functions .. function:: get_tracemalloc_memory() - Get the memory usage in bytes of the :mod:`tracemalloc` module used to store + Get the memory usage in bytes of the :mod:`!tracemalloc` module used to store traces of memory blocks. Return an :class:`int`. .. function:: is_tracing() - ``True`` if the :mod:`tracemalloc` module is tracing Python memory + ``True`` if the :mod:`!tracemalloc` module is tracing Python memory allocations, ``False`` otherwise. See also :func:`start` and :func:`stop` functions. @@ -378,8 +378,8 @@ Functions :meth:`Snapshot.compare_to` and :meth:`Snapshot.statistics` methods. Storing more frames increases the memory and CPU overhead of the - :mod:`tracemalloc` module. Use the :func:`get_tracemalloc_memory` function - to measure how much memory is used by the :mod:`tracemalloc` module. + :mod:`!tracemalloc` module. Use the :func:`get_tracemalloc_memory` function + to measure how much memory is used by the :mod:`!tracemalloc` module. The :envvar:`PYTHONTRACEMALLOC` environment variable (``PYTHONTRACEMALLOC=NFRAME``) and the :option:`-X` ``tracemalloc=NFRAME`` @@ -408,12 +408,12 @@ Functions :class:`Snapshot` instance. The snapshot does not include memory blocks allocated before the - :mod:`tracemalloc` module started to trace memory allocations. + :mod:`!tracemalloc` module started to trace memory allocations. Tracebacks of traces are limited to :func:`get_traceback_limit` frames. Use the *nframe* parameter of the :func:`start` function to store more frames. - The :mod:`tracemalloc` module must be tracing memory allocations to take a + The :mod:`!tracemalloc` module must be tracing memory allocations to take a snapshot, see the :func:`start` function. See also the :func:`get_object_traceback` function. @@ -457,7 +457,7 @@ Filter * ``Filter(True, subprocess.__file__)`` only includes traces of the :mod:`subprocess` module * ``Filter(False, tracemalloc.__file__)`` excludes traces of the - :mod:`tracemalloc` module + :mod:`!tracemalloc` module * ``Filter(False, "")`` excludes empty tracebacks @@ -589,7 +589,7 @@ Snapshot If *cumulative* is ``True``, cumulate size and count of memory blocks of all frames of the traceback of a trace, not only the most recent frame. - The cumulative mode can only be used with *key_type* equals to + The cumulative mode can only be used with *key_type* equal to ``'filename'`` and ``'lineno'``. The result is sorted from the biggest to the smallest by: @@ -720,11 +720,10 @@ Traceback When a snapshot is taken, tracebacks of traces are limited to :func:`get_traceback_limit` frames. See the :func:`take_snapshot` function. The original number of frames of the traceback is stored in the - :attr:`Traceback.total_nframe` attribute. That allows to know if a traceback + :attr:`Traceback.total_nframe` attribute. That allows one to know if a traceback has been truncated by the traceback limit. - The :attr:`Trace.traceback` attribute is an instance of :class:`Traceback` - instance. + The :attr:`Trace.traceback` attribute is a :class:`Traceback` instance. .. versionchanged:: 3.7 Frames are now sorted from the oldest to the most recent, instead of most recent to oldest. diff --git a/Doc/library/tty.rst b/Doc/library/tty.rst index 37778bf20bdcc7..9a8e69f09e8946 100644 --- a/Doc/library/tty.rst +++ b/Doc/library/tty.rst @@ -2,24 +2,20 @@ ========================================== .. module:: tty - :platform: Unix :synopsis: Utility functions that perform common terminal control operations. -.. moduleauthor:: Steen Lumholt -.. sectionauthor:: Moshe Zadka - **Source code:** :source:`Lib/tty.py` -------------- -The :mod:`tty` module defines functions for putting the tty into cbreak and raw +The :mod:`!tty` module defines functions for putting the tty into cbreak and raw modes. .. availability:: Unix. Because it requires the :mod:`termios` module, it will work only on Unix. -The :mod:`tty` module defines the following functions: +The :mod:`!tty` module defines the following functions: .. function:: cfmakeraw(mode) diff --git a/Doc/library/tulip_coro.dia b/Doc/library/tulip_coro.dia deleted file mode 100644 index 70a33e3c00cf6e..00000000000000 Binary files a/Doc/library/tulip_coro.dia and /dev/null differ diff --git a/Doc/library/tulip_coro.png b/Doc/library/tulip_coro.png deleted file mode 100644 index aad41c93015d09..00000000000000 Binary files a/Doc/library/tulip_coro.png and /dev/null differ diff --git a/Doc/library/turtle.rst b/Doc/library/turtle.rst index fea6b57edf0f1f..20c659756fe1c1 100644 --- a/Doc/library/turtle.rst +++ b/Doc/library/turtle.rst @@ -5,8 +5,6 @@ .. module:: turtle :synopsis: An educational framework for simple graphics applications -.. sectionauthor:: Gregor Lingl - **Source code:** :source:`Lib/turtle.py` .. testsetup:: default @@ -18,6 +16,9 @@ import os os.remove("my_drawing.ps") + # Destroy the turtle window after tests are complete + # Imported via star import in testsetup + bye() -------------- @@ -29,6 +30,8 @@ introduced in Logo `_, developed by Wally Feurzeig, Seymour Papert and Cynthia Solomon in 1967. +.. include:: ../includes/optional-module.rst + Get started =========== @@ -777,13 +780,17 @@ Turtle motion 180.0 -.. function:: dot(size=None, *color) +.. function:: dot() + dot(size) + dot(color, /) + dot(size, color, /) + dot(size, r, g, b, /) :param size: an integer >= 1 (if given) :param color: a colorstring or a numeric color tuple Draw a circular dot with diameter *size*, using *color*. If *size* is - not given, the maximum of pensize+4 and 2*pensize is used. + not given, the maximum of ``pensize+4`` and ``2*pensize`` is used. .. doctest:: @@ -1152,7 +1159,9 @@ Drawing state Color control ~~~~~~~~~~~~~ -.. function:: pencolor(*args) +.. function:: pencolor() + pencolor(color, /) + pencolor(r, g, b, /) Return or set the pencolor. @@ -1161,7 +1170,7 @@ Color control ``pencolor()`` Return the current pencolor as color specification string or as a tuple (see example). May be used as input to another - color/pencolor/fillcolor call. + color/pencolor/fillcolor/bgcolor call. ``pencolor(colorstring)`` Set pencolor to *colorstring*, which is a Tk color specification string, @@ -1201,7 +1210,9 @@ Color control (50.0, 193.0, 143.0) -.. function:: fillcolor(*args) +.. function:: fillcolor() + fillcolor(color, /) + fillcolor(r, g, b, /) Return or set the fillcolor. @@ -1210,7 +1221,7 @@ Color control ``fillcolor()`` Return the current fillcolor as color specification string, possibly in tuple format (see example). May be used as input to another - color/pencolor/fillcolor call. + color/pencolor/fillcolor/bgcolor call. ``fillcolor(colorstring)`` Set fillcolor to *colorstring*, which is a Tk color specification string, @@ -1244,7 +1255,10 @@ Color control (255.0, 255.0, 255.0) -.. function:: color(*args) +.. function:: color() + color(color, /) + color(r, g, b, /) + color(pencolor, fillcolor, /) Return or set pencolor and fillcolor. @@ -1870,13 +1884,32 @@ Most of the examples in this section refer to a TurtleScreen instance called Window control -------------- -.. function:: bgcolor(*args) +.. function:: bgcolor() + bgcolor(color, /) + bgcolor(r, g, b, /) + + Return or set the background color of the TurtleScreen. + + Four input formats are allowed: + + ``bgcolor()`` + Return the current background color as color specification string or + as a tuple (see example). May be used as input to another + color/pencolor/fillcolor/bgcolor call. - :param args: a color string or three numbers in the range 0..colormode or a - 3-tuple of such numbers + ``bgcolor(colorstring)`` + Set the background color to *colorstring*, which is a Tk color + specification string, such as ``"red"``, ``"yellow"``, or ``"#33cc8c"``. + ``bgcolor((r, g, b))`` + Set the background color to the RGB color represented by the tuple of + *r*, *g*, and *b*. + Each of *r*, *g*, and *b* must be in the range 0..colormode, where + colormode is either 1.0 or 255 (see :func:`colormode`). - Set or return background color of the TurtleScreen. + ``bgcolor(r, g, b)`` + Set the background color to the RGB color represented by *r*, *g*, and *b*. Each of + *r*, *g*, and *b* must be in the range 0..colormode. .. doctest:: :skipif: _tkinter is None @@ -2216,7 +2249,7 @@ Settings and special methods Set turtle mode ("standard", "logo" or "world") and perform reset. If mode is not given, current mode is returned. - Mode "standard" is compatible with old :mod:`turtle`. Mode "logo" is + Mode "standard" is compatible with old :mod:`!turtle`. Mode "logo" is compatible with most Logo turtle graphics. Mode "world" uses user-defined "world coordinates". **Attention**: in this mode angles appear distorted if ``x/y`` unit-ratio doesn't equal 1. @@ -2657,7 +2690,7 @@ Screen and Turtle. Python script :file:`{filename}.py`. It is intended to serve as a template for translation of the docstrings into different languages. -If you (or your students) want to use :mod:`turtle` with online help in your +If you (or your students) want to use :mod:`!turtle` with online help in your native language, you have to translate the docstrings and save the resulting file as e.g. :file:`turtle_docstringdict_german.py`. @@ -2720,7 +2753,7 @@ Short explanation of selected entries: auto``. - If you set e.g. ``language = italian`` the docstringdict :file:`turtle_docstringdict_italian.py` will be loaded at import time (if - present on the import path, e.g. in the same directory as :mod:`turtle`). + present on the import path, e.g. in the same directory as :mod:`!turtle`). - The entries *exampleturtle* and *examplescreen* define the names of these objects as they occur in the docstrings. The transformation of method-docstrings to function-docstrings will delete these names from the @@ -2729,7 +2762,7 @@ Short explanation of selected entries: switch ("no subprocess"). This will prevent :func:`exitonclick` to enter the mainloop. -There can be a :file:`turtle.cfg` file in the directory where :mod:`turtle` is +There can be a :file:`turtle.cfg` file in the directory where :mod:`!turtle` is stored and an additional one in the current working directory. The latter will override the settings of the first one. @@ -2738,13 +2771,13 @@ study it as an example and see its effects when running the demos (preferably not from within the demo-viewer). -:mod:`turtledemo` --- Demo scripts -================================== +:mod:`!turtledemo` --- Demo scripts +=================================== .. module:: turtledemo :synopsis: A viewer for example turtle scripts -The :mod:`turtledemo` package includes a set of demo scripts. These +The :mod:`!turtledemo` package includes a set of demo scripts. These scripts can be run and viewed using the supplied demo viewer as follows:: python -m turtledemo @@ -2753,11 +2786,11 @@ Alternatively, you can run the demo scripts individually. For example, :: python -m turtledemo.bytedesign -The :mod:`turtledemo` package directory contains: +The :mod:`!turtledemo` package directory contains: - A demo viewer :file:`__main__.py` which can be used to view the sourcecode of the scripts and run them at the same time. -- Multiple scripts demonstrating different features of the :mod:`turtle` +- Multiple scripts demonstrating different features of the :mod:`!turtle` module. Examples can be accessed via the Examples menu. They can also be run standalone. - A :file:`turtle.cfg` file which serves as an example of how to write @@ -2769,68 +2802,68 @@ The demo scripts are: .. tabularcolumns:: |l|L|L| -+----------------+------------------------------+-----------------------+ -| Name | Description | Features | -+================+==============================+=======================+ -| bytedesign | complex classical | :func:`tracer`, delay,| -| | turtle graphics pattern | :func:`update` | -+----------------+------------------------------+-----------------------+ -| chaos | graphs Verhulst dynamics, | world coordinates | -| | shows that computer's | | -| | computations can generate | | -| | results sometimes against the| | -| | common sense expectations | | -+----------------+------------------------------+-----------------------+ -| clock | analog clock showing time | turtles as clock's | -| | of your computer | hands, ontimer | -+----------------+------------------------------+-----------------------+ -| colormixer | experiment with r, g, b | :func:`ondrag` | -+----------------+------------------------------+-----------------------+ -| forest | 3 breadth-first trees | randomization | -+----------------+------------------------------+-----------------------+ -| fractalcurves | Hilbert & Koch curves | recursion | -+----------------+------------------------------+-----------------------+ -| lindenmayer | ethnomathematics | L-System | -| | (indian kolams) | | -+----------------+------------------------------+-----------------------+ -| minimal_hanoi | Towers of Hanoi | Rectangular Turtles | -| | | as Hanoi discs | -| | | (shape, shapesize) | -+----------------+------------------------------+-----------------------+ -| nim | play the classical nim game | turtles as nimsticks, | -| | with three heaps of sticks | event driven (mouse, | -| | against the computer. | keyboard) | -+----------------+------------------------------+-----------------------+ -| paint | super minimalistic | :func:`onclick` | -| | drawing program | | -+----------------+------------------------------+-----------------------+ -| peace | elementary | turtle: appearance | -| | | and animation | -+----------------+------------------------------+-----------------------+ -| penrose | aperiodic tiling with | :func:`stamp` | -| | kites and darts | | -+----------------+------------------------------+-----------------------+ -| planet_and_moon| simulation of | compound shapes, | -| | gravitational system | :class:`Vec2D` | -+----------------+------------------------------+-----------------------+ -| rosette | a pattern from the wikipedia | :func:`clone`, | -| | article on turtle graphics | :func:`undo` | -+----------------+------------------------------+-----------------------+ -| round_dance | dancing turtles rotating | compound shapes, clone| -| | pairwise in opposite | shapesize, tilt, | -| | direction | get_shapepoly, update | -+----------------+------------------------------+-----------------------+ -| sorting_animate| visual demonstration of | simple alignment, | -| | different sorting methods | randomization | -+----------------+------------------------------+-----------------------+ -| tree | a (graphical) breadth | :func:`clone` | -| | first tree (using generators)| | -+----------------+------------------------------+-----------------------+ -| two_canvases | simple design | turtles on two | -| | | canvases | -+----------------+------------------------------+-----------------------+ -| yinyang | another elementary example | :func:`circle` | -+----------------+------------------------------+-----------------------+ ++------------------------+------------------------------+--------------------------------------+ +| Name | Description | Features | ++========================+==============================+======================================+ +| ``bytedesign`` | complex classical | :func:`tracer`, :func:`delay`, | +| | turtle graphics pattern | :func:`update` | ++------------------------+------------------------------+--------------------------------------+ +| ``chaos`` | graphs Verhulst dynamics, | world coordinates | +| | shows that computer's | | +| | computations can generate | | +| | results sometimes against the| | +| | common sense expectations | | ++------------------------+------------------------------+--------------------------------------+ +| ``clock`` | analog clock showing time | turtles as clock's | +| | of your computer | hands, :func:`ontimer` | ++------------------------+------------------------------+--------------------------------------+ +| ``colormixer`` | experiment with r, g, b | :func:`ondrag` | ++------------------------+------------------------------+--------------------------------------+ +| ``forest`` | 3 breadth-first trees | randomization | ++------------------------+------------------------------+--------------------------------------+ +| ``fractalcurves`` | Hilbert & Koch curves | recursion | ++------------------------+------------------------------+--------------------------------------+ +| ``lindenmayer`` | ethnomathematics | L-System | +| | (indian kolams) | | ++------------------------+------------------------------+--------------------------------------+ +| ``minimal_hanoi`` | Towers of Hanoi | Rectangular Turtles | +| | | as Hanoi discs | +| | | (:func:`shape`, :func:`shapesize`) | ++------------------------+------------------------------+--------------------------------------+ +| ``nim`` | play the classical nim game | turtles as nimsticks, | +| | with three heaps of sticks | event driven (mouse, | +| | against the computer. | keyboard) | ++------------------------+------------------------------+--------------------------------------+ +| ``paint`` | super minimalistic | :func:`onclick` | +| | drawing program | | ++------------------------+------------------------------+--------------------------------------+ +| ``peace`` | elementary | turtle: appearance | +| | | and animation | ++------------------------+------------------------------+--------------------------------------+ +| ``penrose`` | aperiodic tiling with | :func:`stamp` | +| | kites and darts | | ++------------------------+------------------------------+--------------------------------------+ +| ``planet_and_moon`` | simulation of | compound shapes, | +| | gravitational system | :class:`Vec2D` | ++------------------------+------------------------------+--------------------------------------+ +| ``rosette`` | a pattern from the wikipedia | :func:`clone`, | +| | article on turtle graphics | :func:`undo` | ++------------------------+------------------------------+--------------------------------------+ +| ``round_dance`` | dancing turtles rotating | compound shapes, :func:`clone` | +| | pairwise in opposite | :func:`shapesize`, :func:`tilt`, | +| | direction | :func:`get_shapepoly`, :func:`update`| ++------------------------+------------------------------+--------------------------------------+ +| ``sorting_animate`` | visual demonstration of | simple alignment, | +| | different sorting methods | randomization | ++------------------------+------------------------------+--------------------------------------+ +| ``tree`` | a (graphical) breadth | :func:`clone` | +| | first tree (using generators)| | ++------------------------+------------------------------+--------------------------------------+ +| ``two_canvases`` | simple design | turtles on two | +| | | canvases | ++------------------------+------------------------------+--------------------------------------+ +| ``yinyang`` | another elementary example | :func:`circle` | ++------------------------+------------------------------+--------------------------------------+ Have fun! diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 207024a7619902..38a77119769d72 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -143,15 +143,15 @@ If you instantiate any of these types, note that signatures may vary between Pyt Standard names are defined for the following types: -.. data:: NoneType +.. class:: NoneType The type of :data:`None`. .. versionadded:: 3.10 -.. data:: FunctionType - LambdaType +.. class:: FunctionType + LambdaType The type of user-defined functions and functions created by :keyword:`lambda` expressions. @@ -162,13 +162,13 @@ Standard names are defined for the following types: and is not raised for normal compilation. -.. data:: GeneratorType +.. class:: GeneratorType The type of :term:`generator`-iterator objects, created by generator functions. -.. data:: CoroutineType +.. class:: CoroutineType The type of :term:`coroutine` objects, created by :keyword:`async def` functions. @@ -176,7 +176,7 @@ Standard names are defined for the following types: .. versionadded:: 3.5 -.. data:: AsyncGeneratorType +.. class:: AsyncGeneratorType The type of :term:`asynchronous generator`-iterator objects, created by asynchronous generator functions. @@ -196,7 +196,7 @@ Standard names are defined for the following types: required by the initializer. The audit event only occurs for direct instantiation of code objects, and is not raised for normal compilation. -.. data:: CellType +.. class:: CellType The type for cell objects: such objects are used as containers for a function's :term:`closure variables `. @@ -204,20 +204,20 @@ Standard names are defined for the following types: .. versionadded:: 3.8 -.. data:: MethodType +.. class:: MethodType The type of methods of user-defined class instances. -.. data:: BuiltinFunctionType - BuiltinMethodType +.. class:: BuiltinFunctionType + BuiltinMethodType The type of built-in functions like :func:`len` or :func:`sys.exit`, and methods of built-in classes. (Here, the term "built-in" means "written in C".) -.. data:: WrapperDescriptorType +.. class:: WrapperDescriptorType The type of methods of some built-in data types and base classes such as :meth:`object.__init__` or :meth:`object.__lt__`. @@ -225,7 +225,7 @@ Standard names are defined for the following types: .. versionadded:: 3.7 -.. data:: MethodWrapperType +.. class:: MethodWrapperType The type of *bound* methods of some built-in data types and base classes. For example it is the type of :code:`object().__str__`. @@ -233,21 +233,21 @@ Standard names are defined for the following types: .. versionadded:: 3.7 -.. data:: NotImplementedType +.. class:: NotImplementedType The type of :data:`NotImplemented`. .. versionadded:: 3.10 -.. data:: MethodDescriptorType +.. class:: MethodDescriptorType The type of methods of some built-in data types such as :meth:`str.join`. .. versionadded:: 3.7 -.. data:: ClassMethodDescriptorType +.. class:: ClassMethodDescriptorType The type of *unbound* class methods of some built-in data types such as ``dict.__dict__['fromkeys']``. @@ -273,7 +273,7 @@ Standard names are defined for the following types: creating :class:`!ModuleType` instances which ensures the various attributes are set appropriately. -.. data:: EllipsisType +.. class:: EllipsisType The type of :data:`Ellipsis`. @@ -327,23 +327,35 @@ Standard names are defined for the following types: dynamically. -.. data:: FrameType +.. class:: FrameType The type of :ref:`frame objects ` such as found in :attr:`tb.tb_frame ` if ``tb`` is a traceback object. -.. data:: FrameLocalsProxyType +.. class:: FrameLocalsProxyType The type of frame locals proxy objects, as found on the :attr:`frame.f_locals` attribute. - .. versionadded:: next + .. versionadded:: 3.15 .. seealso:: :pep:`667` -.. data:: GetSetDescriptorType +.. class:: LazyImportType + + The type of lazy import proxy objects. These objects are created when a + module is lazily imported and serve as placeholders until the module is + actually accessed. This type can be used to detect lazy imports + programmatically. + + .. versionadded:: 3.15 + + .. seealso:: :pep:`810` + + +.. class:: GetSetDescriptorType The type of objects defined in extension modules with ``PyGetSetDef``, such as :attr:`FrameType.f_locals ` or ``array.array.typecode``. @@ -352,7 +364,7 @@ Standard names are defined for the following types: :class:`property` type, but for classes defined in extension modules. -.. data:: MemberDescriptorType +.. class:: MemberDescriptorType The type of objects defined in extension modules with ``PyMemberDef``, such as ``datetime.timedelta.days``. This type is used as descriptor for simple C @@ -374,6 +386,10 @@ Standard names are defined for the following types: entries, which means that when the mapping changes, the view reflects these changes. + :class:`!MappingProxyType`\s are :ref:`generic ` over two types, + signifying (respectively) the types of the underlying mapping's keys and + values. + .. versionadded:: 3.3 .. versionchanged:: 3.9 diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 69df09c779592a..c909b8bad6d726 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -45,15 +45,15 @@ provides backports of these new features to older versions of Python. .. seealso:: - `"Typing cheat sheet" `_ + `Typing cheat sheet `_ A quick overview of type hints (hosted at the mypy docs) - "Type System Reference" section of `the mypy docs `_ + Type System Reference section of `the mypy docs `_ The Python typing system is standardised via PEPs, so this reference should broadly apply to most Python type checkers. (Some parts may still be specific to mypy.) - `"Static Typing with Python" `_ + `Static Typing with Python `_ Type-checker-agnostic documentation written by the community detailing type system features, useful typing related tools and typing best practices. @@ -64,7 +64,7 @@ Specification for the Python Type System ======================================== The canonical, up-to-date specification of the Python type system can be -found at `"Specification for the Python type system" `_. +found at `Specification for the Python type system `_. .. _type-aliases: @@ -230,9 +230,11 @@ For example: callback: Callable[[str], Awaitable[None]] = on_update +.. index:: single: ...; ellipsis literal + The subscription syntax must always be used with exactly two values: the argument list and the return type. The argument list must be a list of types, -a :class:`ParamSpec`, :data:`Concatenate`, or an ellipsis. The return type must +a :class:`ParamSpec`, :data:`Concatenate`, or an ellipsis (``...``). The return type must be a single type. If a literal ellipsis ``...`` is given as the argument list, it indicates that @@ -375,8 +377,11 @@ accepts *any number* of type arguments:: # but ``z`` has been assigned to a tuple of length 3 z: tuple[int] = (1, 2, 3) +.. index:: single: ...; ellipsis literal + To denote a tuple which could be of *any* length, and in which all elements are -of the same type ``T``, use ``tuple[T, ...]``. To denote an empty tuple, use +of the same type ``T``, use the literal ellipsis ``...``: ``tuple[T, ...]``. +To denote an empty tuple, use ``tuple[()]``. Using plain ``tuple`` as an annotation is equivalent to using ``tuple[Any, ...]``:: @@ -714,8 +719,8 @@ The :data:`Any` type ==================== A special kind of type is :data:`Any`. A static type checker will treat -every type as being compatible with :data:`Any` and :data:`Any` as being -compatible with every type. +every type as assignable to :data:`Any` and :data:`Any` as assignable to +every type. This means that it is possible to perform any operation or method call on a value of type :data:`Any` and assign it to any variable:: @@ -780,7 +785,7 @@ it as a return value) of a more specialized type is a type error. For example:: hash_a(42) hash_a("foo") - # Passes type checking, since Any is compatible with all types + # Passes type checking, since Any is assignable to all types hash_b(42) hash_b("foo") @@ -808,7 +813,7 @@ For example, this conforms to :pep:`484`:: def __len__(self) -> int: ... def __iter__(self) -> Iterator[int]: ... -:pep:`544` allows to solve this problem by allowing users to write +:pep:`544` solves this problem by allowing users to write the above code without explicit base classes in the class definition, allowing ``Bucket`` to be implicitly considered a subtype of both ``Sized`` and ``Iterable[int]`` by static type checkers. This is known as @@ -846,8 +851,8 @@ using ``[]``. Special type indicating an unconstrained type. - * Every type is compatible with :data:`Any`. - * :data:`Any` is compatible with every type. + * Every type is assignable to :data:`Any`. + * :data:`Any` is assignable to every type. .. versionchanged:: 3.11 :data:`Any` can now be used as a base class. This can be useful for @@ -1162,12 +1167,15 @@ These can be used as types in annotations. They all support subscription using Special form for annotating higher-order functions. + .. index:: single: ...; ellipsis literal + ``Concatenate`` can be used in conjunction with :ref:`Callable ` and :class:`ParamSpec` to annotate a higher-order callable which adds, removes, or transforms parameters of another callable. Usage is in the form ``Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable]``. ``Concatenate`` - is currently only valid when used as the first argument to a :ref:`Callable `. + is valid when used in :ref:`Callable ` type hints + and when instantiating user-defined generic classes with :class:`ParamSpec` parameters. The last parameter to ``Concatenate`` must be a :class:`ParamSpec` or ellipsis (``...``). @@ -1284,10 +1292,10 @@ These can be used as types in annotations. They all support subscription using :data:`ClassVar` accepts only types and cannot be further subscribed. - :data:`ClassVar` is not a class itself, and should not + :data:`ClassVar` is not a class itself, and cannot be used with :func:`isinstance` or :func:`issubclass`. :data:`ClassVar` does not change Python runtime behavior, but - it can be used by third-party type checkers. For example, a type checker + it can be used by static type checkers. For example, a type checker might flag the following code as an error:: enterprise_d = Starship(3000) @@ -1357,7 +1365,7 @@ These can be used as types in annotations. They all support subscription using def mutate_movie(m: Movie) -> None: m["year"] = 1999 # allowed - m["title"] = "The Matrix" # typechecker error + m["title"] = "The Matrix" # type checker error There is no runtime checking for this property. @@ -1383,7 +1391,7 @@ These can be used as types in annotations. They all support subscription using Using ``Annotated[T, x]`` as an annotation still allows for static typechecking of ``T``, as type checkers will simply ignore the metadata ``x``. In this way, ``Annotated`` differs from the - :func:`@no_type_check ` decorator, which can also be used for + :deco:`no_type_check` decorator, which can also be used for adding annotations outside the scope of the typing system, but completely disables typechecking for a function or class. @@ -1517,6 +1525,35 @@ These can be used as types in annotations. They all support subscription using .. versionadded:: 3.9 +.. data:: TypeForm + + A special form representing the value that results from evaluating a + type expression. + + This value encodes the information supplied in the type expression, and + it represents the type described by that type expression. + + When used in a type expression, ``TypeForm`` describes a set of type form + objects. It accepts a single type argument, which must be a valid type + expression. ``TypeForm[T]`` describes the set of all type form objects that + represent the type ``T`` or types assignable to ``T``. + + ``TypeForm(obj)`` simply returns ``obj`` unchanged. This is useful for + explicitly marking a value as a type form for static type checkers. + + Example:: + + from typing import Any, TypeForm + + def cast[T](typ: TypeForm[T], value: Any) -> T: ... + + reveal_type(cast(int, "x")) # Revealed type is "int" + + See :pep:`747` for details. + + .. versionadded:: 3.15 + + .. data:: TypeIs Special typing construct for marking user-defined type predicate functions. @@ -1944,7 +1981,7 @@ without the dedicated syntax, as documented below. .. _typevartuple: -.. class:: TypeVarTuple(name, *, default=typing.NoDefault) +.. class:: TypeVarTuple(name, *, bound=None, covariant=False, contravariant=False, infer_variance=False, default=typing.NoDefault) Type variable tuple. A specialized form of :ref:`type variable ` that enables *variadic* generics. @@ -2054,6 +2091,24 @@ without the dedicated syntax, as documented below. The name of the type variable tuple. + .. attribute:: __covariant__ + + Whether the type variable tuple has been explicitly marked as covariant. + + .. versionadded:: 3.15 + + .. attribute:: __contravariant__ + + Whether the type variable tuple has been explicitly marked as contravariant. + + .. versionadded:: 3.15 + + .. attribute:: __infer_variance__ + + Whether the type variable tuple's variance should be inferred by type checkers. + + .. versionadded:: 3.15 + .. attribute:: __default__ The default value of the type variable tuple, or :data:`typing.NoDefault` if it @@ -2080,6 +2135,11 @@ without the dedicated syntax, as documented below. .. versionadded:: 3.13 + Type variable tuples created with ``covariant=True`` or + ``contravariant=True`` can be used to declare covariant or contravariant + generic types. The ``bound`` argument is also accepted, similar to + :class:`TypeVar`, but its actual semantics are yet to be decided. + .. versionadded:: 3.11 .. versionchanged:: 3.12 @@ -2091,7 +2151,12 @@ without the dedicated syntax, as documented below. Support for default values was added. -.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False, default=typing.NoDefault) + .. versionchanged:: 3.15 + + Added support for the ``bound``, ``covariant``, ``contravariant``, and + ``infer_variance`` parameters. + +.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False, infer_variance=False, default=typing.NoDefault) Parameter specification variable. A specialized version of :ref:`type variables `. @@ -2160,6 +2225,20 @@ without the dedicated syntax, as documented below. The name of the parameter specification. + .. attribute:: __covariant__ + + Whether the parameter specification has been explicitly marked as covariant. + + .. attribute:: __contravariant__ + + Whether the parameter specification has been explicitly marked as contravariant. + + .. attribute:: __infer_variance__ + + Whether the parameter specification's variance should be inferred by type checkers. + + .. versionadded:: 3.12 + .. attribute:: __default__ The default value of the parameter specification, or :data:`typing.NoDefault` if it @@ -2213,8 +2292,8 @@ without the dedicated syntax, as documented below. * :data:`Concatenate` * :ref:`annotating-callables` -.. data:: ParamSpecArgs - ParamSpecKwargs +.. class:: ParamSpecArgs + ParamSpecKwargs Arguments and keyword arguments attributes of a :class:`ParamSpec`. The ``P.args`` attribute of a ``ParamSpec`` is an instance of ``ParamSpecArgs``, @@ -2236,7 +2315,7 @@ without the dedicated syntax, as documented below. .. versionadded:: 3.10 -.. class:: TypeAliasType(name, value, *, type_params=()) +.. class:: TypeAliasType(name, value, *, type_params=(), qualname=None) The type of type aliases created through the :keyword:`type` statement. @@ -2260,14 +2339,34 @@ without the dedicated syntax, as documented below. >>> Alias.__name__ 'Alias' + .. attribute:: __qualname__ + + The :term:`qualified name` of the type alias: + + .. doctest:: + + >>> class Class: + ... type Alias = int + ... + >>> Class.Alias.__qualname__ + 'Class.Alias' + + .. versionadded:: 3.15 + .. attribute:: __module__ - The module in which the type alias was defined:: + The name of the module in which the type alias was defined:: >>> type Alias = int >>> Alias.__module__ '__main__' + This attribute is writable. + + .. versionchanged:: 3.15 + + The attribute is now writable. + .. attribute:: __type_params__ The type parameters of the type alias, or an empty tuple if the alias is @@ -2373,9 +2472,9 @@ types. Fields with a default value must come after any fields without a default. - The resulting class has an extra attribute ``__annotations__`` giving a - dict that maps the field names to the field types. (The field names are in - the ``_fields`` attribute and the default values are in the + The types for each field name can be retrieved by calling + :func:`annotationlib.get_annotations` on the resulting class. (The field + names are in the ``_fields`` attribute and the default values are in the ``_field_defaults`` attribute, both of which are part of the :func:`~collections.namedtuple` API.) @@ -2421,6 +2520,10 @@ types. Removed the ``_field_types`` attribute in favor of the more standard ``__annotations__`` attribute which has the same information. + .. versionchanged:: 3.9 + ``NamedTuple`` is now a function rather than a class. + It can still be used as a class base, as described above. + .. versionchanged:: 3.11 Added support for generic namedtuples. @@ -2428,24 +2531,11 @@ types. Using :func:`super` (and the ``__class__`` :term:`closure variable`) in methods of ``NamedTuple`` subclasses is unsupported and causes a :class:`TypeError`. - .. deprecated-removed:: 3.13 3.15 - The undocumented keyword argument syntax for creating NamedTuple classes - (``NT = NamedTuple("NT", x=int)``) is deprecated, and will be disallowed - in 3.15. Use the class-based syntax or the functional syntax instead. - - .. deprecated-removed:: 3.13 3.15 - When using the functional syntax to create a NamedTuple class, failing to - pass a value to the 'fields' parameter (``NT = NamedTuple("NT")``) is - deprecated. Passing ``None`` to the 'fields' parameter - (``NT = NamedTuple("NT", None)``) is also deprecated. Both will be - disallowed in Python 3.15. To create a NamedTuple class with 0 fields, - use ``class NT(NamedTuple): pass`` or ``NT = NamedTuple("NT", [])``. - .. class:: NewType(name, tp) Helper class to create low-overhead :ref:`distinct types `. - A ``NewType`` is considered a distinct type by a typechecker. At runtime, + A ``NewType`` is considered a distinct type by a type checker. At runtime, however, calling a ``NewType`` returns its argument unchanged. Usage:: @@ -2455,7 +2545,7 @@ types. .. attribute:: __module__ - The module in which the new type is defined. + The name of the module in which the new type is defined. .. attribute:: __name__ @@ -2493,7 +2583,7 @@ types. func(C()) # Passes static type check See :pep:`544` for more details. Protocol classes decorated with - :func:`runtime_checkable` (described later) act as simple-minded runtime + :deco:`runtime_checkable` (described later) act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes without this decorator cannot be used as the second argument to :func:`isinstance` or :func:`issubclass`. @@ -2515,12 +2605,18 @@ types. .. versionadded:: 3.8 + .. deprecated-removed:: 3.15 3.20 + It is deprecated to call :func:`isinstance` and :func:`issubclass` checks on + protocol classes that were not explicitly decorated with :func:`!runtime_checkable` + but that inherit from a runtime-checkable protocol class. This will throw + a :exc:`TypeError` in Python 3.20. + .. decorator:: runtime_checkable Mark a protocol class as a runtime protocol. Such a protocol can be used with :func:`isinstance` and :func:`issubclass`. - This allows a simple-minded structural check, very similar to "one trick ponies" + This allows a simple-minded structural check, very similar to "one-trick ponies" in :mod:`collections.abc` such as :class:`~collections.abc.Iterable`. For example:: @runtime_checkable @@ -2536,11 +2632,23 @@ types. import threading assert isinstance(threading.Thread(name='Bob'), Named) + Runtime checkability of protocols is not inherited. A subclass of a runtime-checkable protocol + is only runtime-checkable if it is explicitly marked as such, regardless of class hierarchy:: + + @runtime_checkable + class Iterable(Protocol): + def __iter__(self): ... + + # Without @runtime_checkable, Reversible would no longer be runtime-checkable. + @runtime_checkable + class Reversible(Iterable, Protocol): + def __reversed__(self): ... + This decorator raises :exc:`TypeError` when applied to a non-protocol class. .. note:: - :func:`!runtime_checkable` will check only the presence of the required + :deco:`!runtime_checkable` will check only the presence of the required methods or attributes, not their type signatures or types. For example, :class:`ssl.SSLObject` is a class, therefore it passes an :func:`issubclass` @@ -2573,14 +2681,19 @@ types. at runtime as soon as the class has been created. Monkey-patching attributes onto a runtime-checkable protocol will still work, but will have no impact on :func:`isinstance` checks comparing objects to the - protocol. See :ref:`"What's new in Python 3.12" ` + protocol. See :ref:`What's new in Python 3.12 ` for more details. + .. deprecated-removed:: 3.15 3.20 + It is deprecated to call :func:`isinstance` and :func:`issubclass` checks on + protocol classes that were not explicitly decorated with :func:`!runtime_checkable` + but that inherit from a runtime-checkable protocol class. This will throw + a :exc:`TypeError` in Python 3.20. .. class:: TypedDict(dict) Special construct to add type hints to a dictionary. - At runtime it is a plain :class:`dict`. + At runtime ":class:`!TypedDict` instances" are simply :class:`dicts `. ``TypedDict`` declares a dictionary type that expects all of its instances to have a certain set of keys, where each key is @@ -2679,6 +2792,37 @@ types. y: int z: int + By default, a ``TypedDict`` is open, meaning that it may contain additional keys + at runtime beyond those defined in the class body. The *closed* class argument can + be used to control this; if ``closed=True``, the ``TypedDict`` cannot contain additional keys. + + :: + + class ClosedPoint(TypedDict, closed=True): + x: int + y: int + + class ClosedPoint3D(ClosedPoint): # type checker error: cannot add keys to a closed TypedDict + z: int + + Setting ``closed=False`` explicitly requests the default open behavior. If the argument is not + passed, this state is inherited from the parent class. + + In addition to being open or closed, a ``TypedDict`` can also be configured to have extra items. + If the *extra_items* class argument is set to a type, the ``TypedDict`` can contain arbitrary + additional keys, but the values of those keys must be of the specified type. + + :: + + class ExtraItemsPoint(TypedDict, extra_items=int): + x: int + y: int + + point: ExtraItemsPoint = {'x': 1, 'y': 2, 'anything': 3} # OK + + The *extra_items* argument is also inherited through subclassing. It is unset + by default, and it may not be used together with the *closed* argument. + A ``TypedDict`` cannot inherit from a non-\ ``TypedDict`` class, except for :class:`Generic`. For example:: @@ -2711,9 +2855,9 @@ types. key: T group: list[T] - A ``TypedDict`` can be introspected via annotations dicts - (see :ref:`annotations-howto` for more information on annotations best practices), - :attr:`__total__`, :attr:`__required_keys__`, and :attr:`__optional_keys__`. + A ``TypedDict`` can be introspected via :func:`annotationlib.get_annotations` + (see :ref:`annotations-howto` for more information on annotations best practices) + and the following attributes: .. attribute:: __total__ @@ -2754,7 +2898,7 @@ types. For backwards compatibility with Python 3.10 and below, it is also possible to use inheritance to declare both required and - non-required keys in the same ``TypedDict`` . This is done by declaring a + non-required keys in the same ``TypedDict``. This is done by declaring a ``TypedDict`` with one value for the ``total`` argument and then inheriting from it in another ``TypedDict`` with a different value for ``total``: @@ -2783,8 +2927,6 @@ types. ``__required_keys__`` and ``__optional_keys__`` rely on may not work properly, and the values of the attributes may be incorrect. - Support for :data:`ReadOnly` is reflected in the following attributes: - .. attribute:: __readonly_keys__ A :class:`frozenset` containing the names of all read-only keys. Keys @@ -2799,10 +2941,22 @@ types. .. versionadded:: 3.13 + .. attribute:: __closed__ + + The value of the *closed* class argument. It can be ``True``, ``False``, or :data:`None`. + + .. attribute:: __extra_items__ + + The value of the *extra_items* class argument. It can be a valid type or :data:`NoExtraItems`. + See the `TypedDict `_ section in the typing documentation for more examples and detailed rules. .. versionadded:: 3.8 + .. versionchanged:: 3.9 + ``TypedDict`` is now a function rather than a class. + It can still be used as a class base, as described above. + .. versionchanged:: 3.11 Added support for marking individual keys as :data:`Required` or :data:`NotRequired`. See :pep:`655`. @@ -2814,52 +2968,48 @@ types. Removed support for the keyword-argument method of creating ``TypedDict``\ s. .. versionchanged:: 3.13 - Support for the :data:`ReadOnly` qualifier was added. + Support for the :data:`ReadOnly` qualifier was added. See :pep:`705`. + + .. versionchanged:: 3.15 + Support for the *closed* and *extra_items* class arguments was added. See :pep:`728`. - .. deprecated-removed:: 3.13 3.15 - When using the functional syntax to create a TypedDict class, failing to - pass a value to the 'fields' parameter (``TD = TypedDict("TD")``) is - deprecated. Passing ``None`` to the 'fields' parameter - (``TD = TypedDict("TD", None)``) is also deprecated. Both will be - disallowed in Python 3.15. To create a TypedDict class with 0 fields, - use ``class TD(TypedDict): pass`` or ``TD = TypedDict("TD", {})``. Protocols --------- The following protocols are provided by the :mod:`!typing` module. All are decorated -with :func:`@runtime_checkable `. +with :deco:`runtime_checkable`. .. class:: SupportsAbs - An ABC with one abstract method ``__abs__`` that is covariant + A protocol with one abstract method ``__abs__`` that is covariant in its return type. .. class:: SupportsBytes - An ABC with one abstract method ``__bytes__``. + A protocol with one abstract method ``__bytes__``. .. class:: SupportsComplex - An ABC with one abstract method ``__complex__``. + A protocol with one abstract method ``__complex__``. .. class:: SupportsFloat - An ABC with one abstract method ``__float__``. + A protocol with one abstract method ``__float__``. .. class:: SupportsIndex - An ABC with one abstract method ``__index__``. + A protocol with one abstract method ``__index__``. .. versionadded:: 3.8 .. class:: SupportsInt - An ABC with one abstract method ``__int__``. + A protocol with one abstract method ``__int__``. .. class:: SupportsRound - An ABC with one abstract method ``__round__`` + A protocol with one abstract method ``__round__`` that is covariant in its return type. .. _typing-io: @@ -2868,8 +3018,8 @@ ABCs and Protocols for working with I/O --------------------------------------- .. class:: IO[AnyStr] - TextIO[AnyStr] - BinaryIO[AnyStr] + TextIO + BinaryIO Generic class ``IO[AnyStr]`` and its subclasses ``TextIO(IO[str])`` and ``BinaryIO(IO[bytes])`` @@ -3004,12 +3154,12 @@ Functions and decorators Decorator to mark an object as providing :func:`dataclass `-like behavior. - ``dataclass_transform`` may be used to + ``@dataclass_transform`` may be used to decorate a class, metaclass, or a function that is itself a decorator. The presence of ``@dataclass_transform()`` tells a static type checker that the decorated object performs runtime "magic" that transforms a class in a similar way to - :func:`@dataclasses.dataclass `. + :deco:`dataclasses.dataclass`. Example usage with a decorator function: @@ -3047,19 +3197,19 @@ Functions and decorators The ``CustomerModel`` classes defined above will be treated by type checkers similarly to classes created with - :func:`@dataclasses.dataclass `. + :deco:`dataclasses.dataclass`. For example, type checkers will assume these classes have ``__init__`` methods that accept ``id`` and ``name``. The decorated class, metaclass, or function may accept the following bool arguments which type checkers will assume have the same effect as they would have on the - :func:`@dataclasses.dataclass` decorator: ``init``, + :deco:`dataclasses.dataclass` decorator: ``init``, ``eq``, ``order``, ``unsafe_hash``, ``frozen``, ``match_args``, ``kw_only``, and ``slots``. It must be possible for the value of these arguments (``True`` or ``False``) to be statically evaluated. - The arguments to the ``dataclass_transform`` decorator can be used to + The arguments to the ``@dataclass_transform`` decorator can be used to customize the default behaviors of the decorated class, metaclass, or function: @@ -3123,8 +3273,8 @@ Functions and decorators keyword-only. If ``True``, the field will be keyword-only. If ``False``, it will not be keyword-only. If unspecified, the value of the ``kw_only`` parameter on the object decorated with - ``dataclass_transform`` will be used, or if that is unspecified, the - value of ``kw_only_default`` on ``dataclass_transform`` will be used. + ``@dataclass_transform`` will be used, or if that is unspecified, the + value of ``kw_only_default`` on ``@dataclass_transform`` will be used. * - ``alias`` - Provides an alternative name for the field. This alternative name is used in the synthesized ``__init__`` method. @@ -3182,12 +3332,12 @@ Functions and decorators .. function:: get_overloads(func) - Return a sequence of :func:`@overload `-decorated definitions for + Return a sequence of :deco:`overload`-decorated definitions for *func*. *func* is the function object for the implementation of the overloaded function. For example, given the definition of ``process`` in - the documentation for :func:`@overload `, + the documentation for :deco:`overload`, ``get_overloads(process)`` will return a sequence of three function objects for the three defined overloads. If called on a function with no overloads, ``get_overloads()`` returns an empty sequence. @@ -3257,17 +3407,6 @@ Functions and decorators ``@no_type_check`` mutates the decorated object in place. -.. decorator:: no_type_check_decorator - - Decorator to give another decorator the :func:`no_type_check` effect. - - This wraps the decorator with something that wraps the decorated - function in :func:`no_type_check`. - - .. deprecated-removed:: 3.13 3.15 - No type checker ever added support for ``@no_type_check_decorator``. It - is therefore deprecated, and will be removed in Python 3.15. - .. decorator:: override Decorator to indicate that a method in a subclass is intended to override a @@ -3308,6 +3447,36 @@ Functions and decorators .. versionadded:: 3.12 +.. decorator:: disjoint_base + + Decorator to mark a class as a disjoint base. + + Type checkers do not allow child classes of a disjoint base ``C`` to + inherit from other disjoint bases that are not parent or child classes of ``C``. + + For example:: + + @disjoint_base + class Disjoint1: pass + + @disjoint_base + class Disjoint2: pass + + class Disjoint3(Disjoint1, Disjoint2): pass # Type checker error + + Type checkers can use knowledge of disjoint bases to detect unreachable code + and determine when two types can overlap. + + The corresponding runtime concept is a solid base (see :ref:`multiple-inheritance`). + Classes that are solid bases at runtime can be marked with ``@disjoint_base`` in stub files. + Users may also mark other classes as disjoint bases to indicate to type checkers that + multiple inheritance with other disjoint bases should not be allowed. + + Note that the concept of a solid base is a CPython implementation + detail, and the exact set of standard library classes that are + disjoint bases at runtime may change in future versions of Python. + + .. versionadded:: 3.15 .. decorator:: type_check_only @@ -3330,13 +3499,13 @@ Functions and decorators Introspection helpers --------------------- -.. function:: get_type_hints(obj, globalns=None, localns=None, include_extras=False) +.. function:: get_type_hints(obj, globalns=None, localns=None, include_extras=False, *, format=Format.VALUE) - Return a dictionary containing type hints for a function, method, module - or class object. + Return a dictionary containing type hints for a function, method, module, + class object, or other callable object. - This is often the same as ``obj.__annotations__``, but this function makes - the following changes to the annotations dictionary: + This is often the same as :func:`annotationlib.get_annotations`, but this + function makes the following changes to the annotations dictionary: * Forward references encoded as string literals or :class:`ForwardRef` objects are handled by evaluating them in *globalns*, *localns*, and @@ -3344,29 +3513,41 @@ Introspection helpers If *globalns* or *localns* is not given, appropriate namespace dictionaries are inferred from *obj*. * ``None`` is replaced with :class:`types.NoneType`. - * If :func:`@no_type_check ` has been applied to *obj*, an + * If :deco:`no_type_check` has been applied to *obj*, an empty dictionary is returned. * If *obj* is a class ``C``, the function returns a dictionary that merges annotations from ``C``'s base classes with those on ``C`` directly. This is done by traversing :attr:`C.__mro__ ` and iteratively combining - ``__annotations__`` dictionaries. Annotations on classes appearing - earlier in the :term:`method resolution order` always take precedence over - annotations on classes appearing later in the method resolution order. - * The function recursively replaces all occurrences of ``Annotated[T, ...]`` + :term:`annotations ` of each base class. Annotations + on classes appearing earlier in the :term:`method resolution order` always + take precedence over annotations on classes appearing later in the method + resolution order. + * The function recursively replaces all occurrences of + ``Annotated[T, ...]``, ``Required[T]``, ``NotRequired[T]``, and ``ReadOnly[T]`` with ``T``, unless *include_extras* is set to ``True`` (see :class:`Annotated` for more information). - See also :func:`inspect.get_annotations`, a lower-level function that - returns annotations more directly. + .. caution:: + + This function may execute arbitrary code contained in annotations. + See :ref:`annotationlib-security` for more information. .. note:: - If any forward references in the annotations of *obj* are not resolvable - or are not valid Python code, this function will raise an exception - such as :exc:`NameError`. For example, this can happen with imported - :ref:`type aliases ` that include forward references, - or with names imported under :data:`if TYPE_CHECKING `. + If :attr:`Format.VALUE ` is used and any + forward references in the annotations of *obj* are not resolvable, a + :exc:`NameError` exception is raised. For example, this can happen + with names imported under :data:`if TYPE_CHECKING `. + More generally, any kind of exception can be raised if an annotation + contains invalid Python code. + + .. note:: + + Calling :func:`get_type_hints` on an instance is not supported. + To retrieve annotations for an instance, call + :func:`get_type_hints` on the instance's class instead + (for example, ``get_type_hints(type(obj))``). .. versionchanged:: 3.9 Added ``include_extras`` parameter as part of :pep:`593`. @@ -3377,6 +3558,15 @@ Introspection helpers if a default value equal to ``None`` was set. Now the annotation is returned unchanged. + .. versionchanged:: 3.14 + Added the ``format`` parameter. See the documentation on + :func:`annotationlib.get_annotations` for more information. + + .. versionchanged:: 3.14 + Calling :func:`get_type_hints` on instances is no longer supported. + Some instances were accepted in earlier versions as an undocumented + implementation detail. + .. function:: get_origin(tp) Get the unsubscripted version of a type: for a typing object of the form @@ -3443,14 +3633,27 @@ Introspection helpers Determine if a type is a :class:`Protocol`. - For example:: + For example: + + .. testcode:: class P(Protocol): def a(self) -> str: ... b: int - is_protocol(P) # => True - is_protocol(int) # => False + assert is_protocol(P) + assert not is_protocol(int) + + This function only returns true for ``Protocol`` classes, not for + :ref:`generic aliases ` of them: + + .. testcode:: + + class GenericP[T](Protocol): + def a(self) -> T: ... + b: int + + assert not is_protocol(GenericP[int]) .. versionadded:: 3.13 @@ -3473,6 +3676,17 @@ Introspection helpers # not a typed dict itself assert not is_typeddict(TypedDict) + This function only returns true for ``TypedDict`` classes, not for + :ref:`generic aliases ` of them: + + .. testcode:: + + class GenericFilm[T](TypedDict): + title: str + year: T + + assert not is_typeddict(GenericFilm[int]) + .. versionadded:: 3.10 .. class:: ForwardRef @@ -3506,6 +3720,11 @@ Introspection helpers See the documentation for :meth:`annotationlib.ForwardRef.evaluate` for the meaning of the *owner*, *globals*, *locals*, *type_params*, and *format* parameters. + .. caution:: + + This function may execute arbitrary code contained in annotations. + See :ref:`annotationlib-security` for more information. + .. versionadded:: 3.14 .. data:: NoDefault @@ -3524,12 +3743,27 @@ Introspection helpers .. versionadded:: 3.13 +.. data:: NoExtraItems + + A :class:`sentinel` object used to indicate that a :class:`TypedDict` + does not have the *extra_items* class argument. + + .. doctest:: + + >>> from typing import TypedDict, NoExtraItems + >>> class Point(TypedDict): + ... x: int + ... y: int + ... + >>> Point.__extra_items__ is NoExtraItems + True + Constant -------- .. data:: TYPE_CHECKING - A special constant that is assumed to be ``True`` by 3rd party static + A special constant that is assumed to be ``True`` by static type checkers. It's ``False`` at runtime. A module which is expensive to import, and which only contain types @@ -3724,7 +3958,7 @@ Aliases to other concrete types Match Deprecated aliases corresponding to the return types from - :func:`re.compile` and :func:`re.match`. + :func:`re.compile` and :func:`re.search`. These types (and the corresponding functions) are generic over :data:`AnyStr`. ``Pattern`` can be specialised as ``Pattern[str]`` or @@ -3771,6 +4005,28 @@ Aliases to container ABCs in :mod:`collections.abc` :class:`collections.abc.Set` now supports subscripting (``[]``). See :pep:`585` and :ref:`types-genericalias`. +.. class:: ByteString(Sequence[int]) + + Deprecated alias to :class:`collections.abc.ByteString`. + + Use ``isinstance(obj, collections.abc.Buffer)`` to test if ``obj`` + implements the :ref:`buffer protocol ` at runtime. For use in + type annotations, either use :class:`~collections.abc.Buffer` or a union + that explicitly specifies the types your code supports (e.g., + ``bytes | bytearray | memoryview``). + + :class:`!ByteString` was originally intended to be an abstract class that + would serve as a supertype of both :class:`bytes` and :class:`bytearray`. + However, since the ABC never had any methods, knowing that an object was an + instance of :class:`!ByteString` never actually told you anything useful + about the object. Other common buffer types such as :class:`memoryview` were + also never understood as subtypes of :class:`!ByteString` (either at runtime + or by static type checkers). + + See :pep:`PEP 688 <688#current-options>` for more details. + + .. deprecated-removed:: 3.9 3.17 + .. class:: Collection(Sized, Iterable[T_co], Container[T_co]) Deprecated alias to :class:`collections.abc.Collection`. @@ -4064,6 +4320,10 @@ convenience. This is subject to change, and not all deprecations are listed. - 3.9 - Undecided (see :ref:`deprecated-aliases` for more information) - :pep:`585` + * - :class:`typing.ByteString` + - 3.9 + - 3.17 + - :gh:`91896` * - :data:`typing.Text` - 3.11 - Undecided @@ -4076,10 +4336,6 @@ convenience. This is subject to change, and not all deprecations are listed. - 3.12 - Undecided - :pep:`695` - * - :func:`@typing.no_type_check_decorator ` - - 3.13 - - 3.15 - - :gh:`106309` * - :data:`typing.AnyStr` - 3.13 - 3.18 diff --git a/Doc/library/unicodedata.rst b/Doc/library/unicodedata.rst index 0aef597d064e0e..25bf872e0ab55a 100644 --- a/Doc/library/unicodedata.rst +++ b/Doc/library/unicodedata.rst @@ -4,10 +4,6 @@ .. module:: unicodedata :synopsis: Access the Unicode Database. -.. moduleauthor:: Marc-André Lemburg -.. sectionauthor:: Marc-André Lemburg -.. sectionauthor:: Martin v. Löwis - .. index:: single: Unicode single: character @@ -17,91 +13,245 @@ This module provides access to the Unicode Character Database (UCD) which defines character properties for all Unicode characters. The data contained in -this database is compiled from the `UCD version 16.0.0 -`_. +this database is compiled from the `UCD version 17.0.0 +`_. The module uses the same names and symbols as defined by Unicode Standard Annex #44, `"Unicode Character Database" -`_. It defines the -following functions: - - -.. function:: lookup(name) +`_. + +.. seealso:: + + The :ref:`unicode-howto` for more information about Unicode and how to use + this module. + + +============================================================ =========================================================== +**Lookup** +------------------------------------------------------------------------------------------------------------------------- +:func:`lookup(name) ` Look up character by name +:func:`name(chr) ` Return the name assigned to a character + +**Numeric values** +------------------------------------------------------------------------------------------------------------------------- +:func:`decimal(chr) ` Decimal value of a character +:func:`digit(chr) ` Digit value of a character +:func:`numeric(chr) ` Numeric value of a character + +**Properties** +------------------------------------------------------------------------------------------------------------------------- +:func:`bidirectional(chr) ` Bidirectional class of a character +:func:`block(chr) ` Unicode block of a character +:func:`category(chr) ` General category of a character +:func:`combining(chr) ` Canonical combining class of a character +:func:`decomposition(chr) ` Character decomposition mapping +:func:`east_asian_width(chr) ` East Asian width of a character +:func:`extended_pictographic(chr) ` Check if a character has the Extended_Pictographic property +:func:`grapheme_cluster_break(chr) ` Grapheme_Cluster_Break property of a character +:func:`indic_conjunct_break(chr) ` Indic_Conjunct_Break property of a character +:func:`isxidcontinue(chr) ` Check if a character is a valid identifier continuation +:func:`isxidstart(chr) ` Check if a character is a valid identifier start +:func:`mirrored(chr) ` Mirrored property of a character + +**Normalization** +------------------------------------------------------------------------------------------------------------------------- +:func:`normalize(form, unistr) ` Return the normalized form of a string +:func:`is_normalized(form, unistr) ` Check if a Unicode string is normalized + +**Text segmentation** +------------------------------------------------------------------------------------------------------------------------- +:func:`iter_graphemes(unistr) ` Iterate over grapheme clusters in a string +============================================================ =========================================================== + + +.. function:: lookup(name, /) Look up character by name. If a character with the given name is found, return the corresponding character. If not found, :exc:`KeyError` is raised. + For example:: + + >>> unicodedata.lookup('LEFT CURLY BRACKET') + '{' + + The characters returned by this function are the same as those produced by + ``\N`` escape sequence in string literals. For example:: + + >>> unicodedata.lookup('MIDDLE DOT') == '\N{MIDDLE DOT}' + True .. versionchanged:: 3.3 Support for name aliases [#]_ and named sequences [#]_ has been added. -.. function:: name(chr[, default]) +.. function:: name(chr, default=None, /) Returns the name assigned to the character *chr* as a string. If no name is defined, *default* is returned, or, if not given, :exc:`ValueError` is - raised. + raised. For example:: + + >>> unicodedata.name('½') + 'VULGAR FRACTION ONE HALF' + >>> unicodedata.name('\uFFFF', 'fallback') + 'fallback' -.. function:: decimal(chr[, default]) +.. function:: decimal(chr, default=None, /) Returns the decimal value assigned to the character *chr* as integer. If no such value is defined, *default* is returned, or, if not given, - :exc:`ValueError` is raised. + :exc:`ValueError` is raised. For example:: + >>> unicodedata.decimal('\N{ARABIC-INDIC DIGIT NINE}') + 9 + >>> unicodedata.decimal('\N{SUPERSCRIPT NINE}', -1) + -1 -.. function:: digit(chr[, default]) + +.. function:: digit(chr, default=None, /) Returns the digit value assigned to the character *chr* as integer. If no such value is defined, *default* is returned, or, if not given, - :exc:`ValueError` is raised. + :exc:`ValueError` is raised:: + + >>> unicodedata.digit('\N{SUPERSCRIPT NINE}') + 9 -.. function:: numeric(chr[, default]) +.. function:: numeric(chr, default=None, /) Returns the numeric value assigned to the character *chr* as float. If no such value is defined, *default* is returned, or, if not given, - :exc:`ValueError` is raised. + :exc:`ValueError` is raised:: + + >>> unicodedata.numeric('½') + 0.5 -.. function:: category(chr) +.. function:: category(chr, /) Returns the general category assigned to the character *chr* as - string. + string. General category names consist of two letters. + See the `General Category Values section of the Unicode Character + Database documentation `_ + for a list of category codes. For example:: + >>> unicodedata.category('A') # 'L'etter, 'u'ppercase + 'Lu' -.. function:: bidirectional(chr) + +.. function:: bidirectional(chr, /) Returns the bidirectional class assigned to the character *chr* as string. If no such value is defined, an empty string is returned. + See the `Bidirectional Class Values section of the Unicode Character + Database `_ + documentation for a list of bidirectional codes. For example:: + + >>> unicodedata.bidirectional('\N{ARABIC-INDIC DIGIT SEVEN}') # 'A'rabic, 'N'umber + 'AN' -.. function:: combining(chr) +.. function:: combining(chr, /) Returns the canonical combining class assigned to the character *chr* as integer. Returns ``0`` if no combining class is defined. + See the `Canonical Combining Class Values section of the Unicode Character + Database `_ + for more information. -.. function:: east_asian_width(chr) +.. function:: east_asian_width(chr, /) Returns the east asian width assigned to the character *chr* as - string. + string. For a list of widths and or more information, see the + `Unicode Standard Annex #11 `_. + +.. function:: block(chr, /) -.. function:: mirrored(chr) + Returns the `block + `_ + assigned to the character *chr*. For example:: + + >>> unicodedata.block('S') + 'Basic Latin' + + .. versionadded:: 3.15 + + +.. function:: mirrored(chr, /) Returns the mirrored property assigned to the character *chr* as integer. Returns ``1`` if the character has been identified as a "mirrored" - character in bidirectional text, ``0`` otherwise. + character in bidirectional text, ``0`` otherwise. For example:: + + >>> unicodedata.mirrored('>') + 1 + + +.. function:: isxidstart(chr, /) + + Return ``True`` if *chr* is a valid identifier start per the + `Unicode Standard Annex #31 `_, + that is, it has the ``XID_Start`` property. Return ``False`` otherwise. + For example:: + + >>> unicodedata.isxidstart('S') + True + >>> unicodedata.isxidstart('0') + False + + .. versionadded:: 3.15 + + +.. function:: isxidcontinue(chr, /) + Return ``True`` if *chr* is a valid identifier character per the + `Unicode Standard Annex #31 `_, + that is, it has the ``XID_Continue`` property. Return ``False`` otherwise. + For example:: -.. function:: decomposition(chr) + >>> unicodedata.isxidcontinue('S') + True + >>> unicodedata.isxidcontinue(' ') + False + + .. versionadded:: 3.15 + + +.. function:: decomposition(chr, /) Returns the character decomposition mapping assigned to the character *chr* as string. An empty string is returned in case no such mapping is - defined. + defined. For example:: + + >>> unicodedata.decomposition('Ã') + '0041 0303' + + +.. function:: grapheme_cluster_break(chr, /) + + Returns the Grapheme_Cluster_Break property assigned to the character. + + .. versionadded:: 3.15 + + +.. function:: indic_conjunct_break(chr, /) + + Returns the Indic_Conjunct_Break property assigned to the character. + + .. versionadded:: 3.15 -.. function:: normalize(form, unistr) +.. function:: extended_pictographic(chr, /) + + Returns ``True`` if the character has the Extended_Pictographic property, + ``False`` otherwise. + + .. versionadded:: 3.15 + + +.. function:: normalize(form, unistr, /) Return the normal form *form* for the Unicode string *unistr*. Valid values for *form* are 'NFC', 'NFKC', 'NFD', and 'NFKD'. @@ -122,9 +272,9 @@ following functions: normally would be unified with other characters. For example, U+2160 (ROMAN NUMERAL ONE) is really the same thing as U+0049 (LATIN CAPITAL LETTER I). However, it is supported in Unicode for compatibility with existing character - sets (e.g. gb2312). + sets (for example, gb2312). - The normal form KD (NFKD) will apply the compatibility decomposition, i.e. + The normal form KD (NFKD) will apply the compatibility decomposition, that is, replace all compatibility characters with their equivalents. The normal form KC (NFKC) first applies the compatibility decomposition, followed by the canonical composition. @@ -133,7 +283,8 @@ following functions: a human reader, if one has combining characters and the other doesn't, they may not compare equal. -.. function:: is_normalized(form, unistr) + +.. function:: is_normalized(form, unistr, /) Return whether the Unicode string *unistr* is in the normal form *form*. Valid values for *form* are 'NFC', 'NFKC', 'NFD', and 'NFKD'. @@ -141,7 +292,25 @@ following functions: .. versionadded:: 3.8 -In addition, the module exposes the following constant: +.. function:: iter_graphemes(unistr, start=0, end=sys.maxsize, /) + + Returns an iterator to iterate over grapheme clusters. + With optional *start*, iteration begins at that position. + With optional *end*, iteration stops at that position. + + Converting an emitted item to string returns a substring corresponding to + the grapheme cluster. + Its ``start`` and ``end`` attributes denote the start and end of + the grapheme cluster. + + It uses extended grapheme cluster rules defined by Unicode + Standard Annex #29, `"Unicode Text Segmentation" + `_. + + .. versionadded:: 3.15 + + +In addition, the module exposes the following constants: .. data:: unidata_version @@ -150,31 +319,13 @@ In addition, the module exposes the following constant: .. data:: ucd_3_2_0 - This is an object that has the same methods as the entire module, but uses the + This is an object that has most of the methods of the entire module, but uses the Unicode database version 3.2 instead, for applications that require this specific version of the Unicode database (such as IDNA). -Examples: - - >>> import unicodedata - >>> unicodedata.lookup('LEFT CURLY BRACKET') - '{' - >>> unicodedata.name('/') - 'SOLIDUS' - >>> unicodedata.decimal('9') - 9 - >>> unicodedata.decimal('a') - Traceback (most recent call last): - File "", line 1, in - ValueError: not a decimal - >>> unicodedata.category('A') # 'L'etter, 'u'ppercase - 'Lu' - >>> unicodedata.bidirectional('\u0660') # 'A'rabic, 'N'umber - 'AN' - .. rubric:: Footnotes -.. [#] https://www.unicode.org/Public/16.0.0/ucd/NameAliases.txt +.. [#] https://www.unicode.org/Public/17.0.0/ucd/NameAliases.txt -.. [#] https://www.unicode.org/Public/16.0.0/ucd/NamedSequences.txt +.. [#] https://www.unicode.org/Public/17.0.0/ucd/NamedSequences.txt diff --git a/Doc/library/unittest.mock-examples.rst b/Doc/library/unittest.mock-examples.rst index 00cc9bfc0a5f2b..b8aa04b9ab246d 100644 --- a/Doc/library/unittest.mock-examples.rst +++ b/Doc/library/unittest.mock-examples.rst @@ -1,7 +1,6 @@ :mod:`!unittest.mock` --- getting started ========================================= -.. moduleauthor:: Michael Foord .. currentmodule:: unittest.mock .. versionadded:: 3.3 @@ -26,7 +25,7 @@ Using Mock ---------- -Mock Patching Methods +Mock patching methods ~~~~~~~~~~~~~~~~~~~~~ Common uses for :class:`Mock` objects include: @@ -72,7 +71,7 @@ the ``something`` method: -Mock for Method Calls on an Object +Mock for method calls on an object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In the last example we patched a method directly on an object to check that it @@ -102,7 +101,7 @@ accessing it in the test will create it, but :meth:`~Mock.assert_called_with` will raise a failure exception. -Mocking Classes +Mocking classes ~~~~~~~~~~~~~~~ A common use case is to mock out classes instantiated by your code under test. @@ -140,7 +139,7 @@ name is also propagated to attributes or methods of the mock: -Tracking all Calls +Tracking all calls ~~~~~~~~~~~~~~~~~~ Often you want to track more than a single call to a method. The @@ -177,7 +176,7 @@ possible to track nested calls where the parameters used to create ancestors are True -Setting Return Values and Attributes +Setting return values and attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Setting the return values on a mock object is trivially easy: @@ -318,7 +317,7 @@ return an async function. >>> mock_instance.__aexit__.assert_awaited_once() -Creating a Mock from an Existing Object +Creating a mock from an existing object ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ One problem with over use of mocking is that it couples your tests to the @@ -385,7 +384,7 @@ contents per file stored in a dictionary:: assert file2.read() == "default" -Patch Decorators +Patch decorators ---------------- .. note:: @@ -519,7 +518,7 @@ decorator individually to every method whose name starts with "test". .. _further-examples: -Further Examples +Further examples ---------------- @@ -600,13 +599,13 @@ this list of calls for us:: Partial mocking ~~~~~~~~~~~~~~~ -In some tests I wanted to mock out a call to :meth:`datetime.date.today` -to return a known date, but I didn't want to prevent the code under test from -creating new date objects. Unfortunately :class:`datetime.date` is written in C, and -so I couldn't just monkey-patch out the static :meth:`datetime.date.today` method. +For some tests, you may want to mock out a call to :meth:`datetime.date.today` +to return a known date, but don't want to prevent the code under test from +creating new date objects. Unfortunately :class:`datetime.date` is written in C, +so you cannot just monkey-patch out the static :meth:`datetime.date.today` method. -I found a simple way of doing this that involved effectively wrapping the date -class with a mock, but passing through calls to the constructor to the real +Instead, you can effectively wrap the date +class with a mock, while passing through calls to the constructor to the real class (and returning real instances). The :func:`patch decorator ` is used here to @@ -615,13 +614,13 @@ attribute on the mock date class is then set to a lambda function that returns a real date. When the mock date class is called a real date will be constructed and returned by ``side_effect``. :: - >>> from datetime import date + >>> import datetime as dt >>> with patch('mymodule.date') as mock_date: - ... mock_date.today.return_value = date(2010, 10, 8) - ... mock_date.side_effect = lambda *args, **kw: date(*args, **kw) + ... mock_date.today.return_value = dt.date(2010, 10, 8) + ... mock_date.side_effect = lambda *args, **kw: dt.date(*args, **kw) ... - ... assert mymodule.date.today() == date(2010, 10, 8) - ... assert mymodule.date(2009, 6, 8) == date(2009, 6, 8) + ... assert mymodule.date.today() == dt.date(2010, 10, 8) + ... assert mymodule.date(2009, 6, 8) == dt.date(2009, 6, 8) Note that we don't patch :class:`datetime.date` globally, we patch ``date`` in the module that *uses* it. See :ref:`where to patch `. @@ -639,7 +638,7 @@ is discussed in `this blog entry `_. -Mocking a Generator Method +Mocking a generator method ~~~~~~~~~~~~~~~~~~~~~~~~~~ A Python generator is a function or method that uses the :keyword:`yield` statement @@ -740,19 +739,18 @@ exception is raised in the setUp then tearDown is not called. >>> MyTest('test_foo').run() -Mocking Unbound Methods +Mocking unbound methods ~~~~~~~~~~~~~~~~~~~~~~~ -Whilst writing tests today I needed to patch an *unbound method* (patching the -method on the class rather than on the instance). I needed self to be passed -in as the first argument because I want to make asserts about which objects -were calling this particular method. The issue is that you can't patch with a -mock for this, because if you replace an unbound method with a mock it doesn't -become a bound method when fetched from the instance, and so it doesn't get -self passed in. The workaround is to patch the unbound method with a real -function instead. The :func:`patch` decorator makes it so simple to -patch out methods with a mock that having to create a real function becomes a -nuisance. +Sometimes a test needs to patch an *unbound method*, which means patching the +method on the class rather than on the instance. In order to make assertions +about which objects were calling this particular method, you need to pass +``self`` as the first argument. The issue is that you can't patch with a mock for +this, because if you replace an unbound method with a mock it doesn't become +a bound method when fetched from the instance, and so it doesn't get ``self`` +passed in. The workaround is to patch the unbound method with a real function +instead. The :func:`patch` decorator makes it so simple to patch out methods +with a mock that having to create a real function becomes a nuisance. If you pass ``autospec=True`` to patch then it does the patching with a *real* function object. This function object has the same signature as the one @@ -760,8 +758,8 @@ it is replacing, but delegates to a mock under the hood. You still get your mock auto-created in exactly the same way as before. What it means though, is that if you use it to patch out an unbound method on a class the mocked function will be turned into a bound method if it is fetched from an instance. -It will have ``self`` passed in as the first argument, which is exactly what I -wanted: +It will have ``self`` passed in as the first argument, which is exactly what +was needed: >>> class Foo: ... def foo(self): @@ -864,9 +862,9 @@ Here's one solution that uses the :attr:`~Mock.side_effect` functionality. If you provide a ``side_effect`` function for a mock then ``side_effect`` will be called with the same args as the mock. This gives us an opportunity to copy the arguments and store them for later assertions. In this -example I'm using *another* mock to store the arguments so that I can use the +example we're using *another* mock to store the arguments so that we can use the mock methods for doing the assertion. Again a helper function sets this up for -me. :: +us. :: >>> from copy import deepcopy >>> from unittest.mock import Mock, patch, DEFAULT @@ -939,7 +937,7 @@ and the ``return_value`` will use your subclass automatically. That means all children of a ``CopyingMock`` will also have the type ``CopyingMock``. -Nesting Patches +Nesting patches ~~~~~~~~~~~~~~~ Using patch as a context manager is nice, but if you do multiple patches you diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst index 091562cc9aef98..5e28a8ada595ef 100644 --- a/Doc/library/unittest.mock.rst +++ b/Doc/library/unittest.mock.rst @@ -4,7 +4,6 @@ .. module:: unittest.mock :synopsis: Mock object library. -.. moduleauthor:: Michael Foord .. currentmodule:: unittest.mock .. versionadded:: 3.3 @@ -13,11 +12,11 @@ -------------- -:mod:`unittest.mock` is a library for testing in Python. It allows you to +:mod:`!unittest.mock` is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and make assertions about how they have been used. -:mod:`unittest.mock` provides a core :class:`Mock` class removing the need to +:mod:`!unittest.mock` provides a core :class:`Mock` class removing the need to create a host of stubs throughout your test suite. After performing an action, you can make assertions about which methods / attributes were used and arguments they were called with. You can also specify return values and @@ -33,7 +32,7 @@ Mock is designed for use with :mod:`unittest` and is based on the 'action -> assertion' pattern instead of 'record -> replay' used by many mocking frameworks. -There is a backport of :mod:`unittest.mock` for earlier versions of Python, +There is a backport of :mod:`!unittest.mock` for earlier versions of Python, available as :pypi:`mock` on PyPI. @@ -639,7 +638,7 @@ the *new_callable* argument to :func:`patch`. This is either ``None`` (if the mock hasn't been called), or the arguments that the mock was last called with. This will be in the form of a tuple: the first member, which can also be accessed through - the ``args`` property, is any ordered arguments the mock was + the ``args`` property, is any positional arguments the mock was called with (or an empty tuple) and the second member, which can also be accessed through the ``kwargs`` property, is any keyword arguments (or an empty dictionary). @@ -2224,7 +2223,7 @@ return something else:: >>> mock == 3 True -The return value of :meth:`MagicMock.__iter__` can be any iterable object and isn't +The return value of :meth:`!__iter__` can be any iterable object and isn't required to be an iterator: >>> mock = MagicMock() @@ -2540,7 +2539,7 @@ Alternatively you can just use ``vars(my_mock)`` (instance members) and mock_open ~~~~~~~~~ -.. function:: mock_open(mock=None, read_data=None) +.. function:: mock_open(mock=None, read_data='') A helper function to create a mock to replace the use of :func:`open`. It works for :func:`open` called directly or used as a context manager. @@ -2638,7 +2637,7 @@ unit tests. Testing everything in isolation is all fine and dandy, but if you don't test how your units are "wired together" there is still lots of room for bugs that tests might have caught. -:mod:`unittest.mock` already provides a feature to help with this, called speccing. If you +:mod:`!unittest.mock` already provides a feature to help with this, called speccing. If you use a class or instance as the :attr:`!spec` for a mock then you can only access attributes on the mock that exist on the real class: diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index ec96e8416120fa..7afcdb368a3562 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -4,11 +4,6 @@ .. module:: unittest :synopsis: Unit testing framework for Python. -.. moduleauthor:: Steve Purcell -.. sectionauthor:: Steve Purcell -.. sectionauthor:: Fred L. Drake, Jr. -.. sectionauthor:: Raymond Hettinger - **Source code:** :source:`Lib/unittest/__init__.py` -------------- @@ -16,13 +11,13 @@ (If you are already familiar with the basic concepts of testing, you might want to skip to :ref:`the list of assert methods `.) -The :mod:`unittest` unit testing framework was originally inspired by JUnit +The :mod:`!unittest` unit testing framework was originally inspired by JUnit and has a similar flavor as major unit testing frameworks in other languages. It supports test automation, sharing of setup and shutdown code for tests, aggregation of tests into collections, and independence of the tests from the reporting framework. -To achieve this, :mod:`unittest` supports some important concepts in an +To achieve this, :mod:`!unittest` supports some important concepts in an object-oriented way: test fixture @@ -33,7 +28,7 @@ test fixture test case A :dfn:`test case` is the individual unit of testing. It checks for a specific - response to a particular set of inputs. :mod:`unittest` provides a base class, + response to a particular set of inputs. :mod:`!unittest` provides a base class, :class:`TestCase`, which may be used to create new test cases. test suite @@ -53,7 +48,7 @@ test runner `Simple Smalltalk Testing: With Patterns `_ Kent Beck's original paper on testing frameworks using the pattern shared - by :mod:`unittest`. + by :mod:`!unittest`. `pytest `_ Third-party unittest framework with a lighter-weight syntax for writing @@ -81,7 +76,7 @@ test runner Basic example ------------- -The :mod:`unittest` module provides a rich set of tools for constructing and +The :mod:`!unittest` module provides a rich set of tools for constructing and running tests. This section demonstrates that a small subset of the tools suffice to meet the needs of most users. @@ -147,7 +142,7 @@ to enable a higher level of verbosity, and produce the following output:: OK -The above examples show the most commonly used :mod:`unittest` features which +The above examples show the most commonly used :mod:`!unittest` features which are sufficient to meet many everyday testing needs. The remainder of the documentation explores the full feature set from first principles. @@ -365,7 +360,7 @@ Organizing test code -------------------- The basic building blocks of unit testing are :dfn:`test cases` --- single -scenarios that must be set up and checked for correctness. In :mod:`unittest`, +scenarios that must be set up and checked for correctness. In :mod:`!unittest`, test cases are represented by :class:`unittest.TestCase` instances. To make your own test cases you must write subclasses of :class:`TestCase` or use :class:`FunctionTestCase`. @@ -387,7 +382,7 @@ testing code:: Note that in order to test something, we use one of the :ref:`assert\* methods ` provided by the :class:`TestCase` base class. If the test fails, an -exception will be raised with an explanatory message, and :mod:`unittest` +exception will be raised with an explanatory message, and :mod:`!unittest` will identify the test case as a :dfn:`failure`. Any other exceptions will be treated as :dfn:`errors`. @@ -438,12 +433,12 @@ run whether the test method succeeded or not. Such a working environment for the testing code is called a :dfn:`test fixture`. A new TestCase instance is created as a unique test fixture used to execute each individual test method. Thus -:meth:`~TestCase.setUp`, :meth:`~TestCase.tearDown`, and :meth:`~TestCase.__init__` +:meth:`~TestCase.setUp`, :meth:`~TestCase.tearDown`, and :meth:`!TestCase.__init__` will be called once per test. It is recommended that you use TestCase implementations to group tests together -according to the features they test. :mod:`unittest` provides a mechanism for -this: the :dfn:`test suite`, represented by :mod:`unittest`'s +according to the features they test. :mod:`!unittest` provides a mechanism for +this: the :dfn:`test suite`, represented by :mod:`!unittest`'s :class:`TestSuite` class. In most cases, calling :func:`unittest.main` will do the right thing and collect all the module's test cases for you and execute them. @@ -489,10 +484,10 @@ Re-using old test code ---------------------- Some users will find that they have existing test code that they would like to -run from :mod:`unittest`, without converting every old test function to a +run from :mod:`!unittest`, without converting every old test function to a :class:`TestCase` subclass. -For this reason, :mod:`unittest` provides a :class:`FunctionTestCase` class. +For this reason, :mod:`!unittest` provides a :class:`FunctionTestCase` class. This subclass of :class:`TestCase` can be used to wrap an existing test function. Set-up and tear-down functions can also be provided. @@ -513,12 +508,12 @@ set-up and tear-down methods:: .. note:: Even though :class:`FunctionTestCase` can be used to quickly convert an - existing test base over to a :mod:`unittest`\ -based system, this approach is + existing test base over to a :mod:`!unittest`\ -based system, this approach is not recommended. Taking the time to set up proper :class:`TestCase` subclasses will make future test refactorings infinitely easier. In some cases, the existing tests may have been written using the :mod:`doctest` -module. If so, :mod:`doctest` provides a :class:`DocTestSuite` class that can +module. If so, :mod:`doctest` provides a :class:`~doctest.DocTestSuite` class that can automatically build :class:`unittest.TestSuite` instances from the existing :mod:`doctest`\ -based tests. @@ -535,7 +530,7 @@ tests. In addition, it supports marking a test as an "expected failure," a test that is broken and will fail, but shouldn't be counted as a failure on a :class:`TestResult`. -Skipping a test is simply a matter of using the :func:`skip` :term:`decorator` +Skipping a test is simply a matter of using the :deco:`skip` :term:`decorator` or one of its conditional variants, calling :meth:`TestCase.skipTest` within a :meth:`~TestCase.setUp` or test method, or raising :exc:`SkipTest` directly. @@ -586,7 +581,7 @@ Classes can be skipped just like methods:: :meth:`TestCase.setUp` can also skip the test. This is useful when a resource that needs to be set up is not available. -Expected failures use the :func:`expectedFailure` decorator. :: +Expected failures use the :deco:`expectedFailure` decorator. :: class ExpectedFailureTestCase(unittest.TestCase): @unittest.expectedFailure @@ -709,7 +704,7 @@ wouldn't be displayed:: Classes and functions --------------------- -This section describes in depth the API of :mod:`unittest`. +This section describes in depth the API of :mod:`!unittest`. .. _testcase-objects: @@ -720,7 +715,7 @@ Test cases .. class:: TestCase(methodName='runTest') Instances of the :class:`TestCase` class represent the logical test units - in the :mod:`unittest` universe. This class is intended to be used as a base + in the :mod:`!unittest` universe. This class is intended to be used as a base class, with specific tests being implemented by concrete subclasses. This class implements the interface needed by the test runner to allow it to drive the tests, and methods that the test code can use to check for and report various @@ -768,7 +763,7 @@ Test cases A class method called before tests in an individual class are run. ``setUpClass`` is called with the class as the only argument - and must be decorated as a :func:`classmethod`:: + and must be decorated as a :deco:`classmethod`:: @classmethod def setUpClass(cls): @@ -783,7 +778,7 @@ Test cases A class method called after tests in an individual class have run. ``tearDownClass`` is called with the class as the only argument - and must be decorated as a :meth:`classmethod`:: + and must be decorated as a :deco:`classmethod`:: @classmethod def tearDownClass(cls): @@ -1023,7 +1018,7 @@ Test cases additional keyword argument *msg*. The context manager will store the caught exception object in its - :attr:`exception` attribute. This can be useful if the intention + :attr:`!exception` attribute. This can be useful if the intention is to perform additional checks on the exception raised:: with self.assertRaises(SomeException) as cm: @@ -1036,7 +1031,7 @@ Test cases Added the ability to use :meth:`assertRaises` as a context manager. .. versionchanged:: 3.2 - Added the :attr:`exception` attribute. + Added the :attr:`!exception` attribute. .. versionchanged:: 3.3 Added the *msg* keyword argument when used as a context manager. @@ -1089,8 +1084,8 @@ Test cases additional keyword argument *msg*. The context manager will store the caught warning object in its - :attr:`warning` attribute, and the source line which triggered the - warnings in the :attr:`filename` and :attr:`lineno` attributes. + :attr:`!warning` attribute, and the source line which triggered the + warnings in the :attr:`!filename` and :attr:`!lineno` attributes. This can be useful if the intention is to perform additional checks on the warning caught:: @@ -1100,6 +1095,13 @@ Test cases self.assertIn('myfile.py', cm.filename) self.assertEqual(320, cm.lineno) + The context managers can be nested to test that multiple different + warnings are emitted:: + + with (self.assertWarns(SomeWarning), + self.assertWarns(OtherWarning)): + do_something() + This method works regardless of the warning filters in place when it is called. @@ -1108,6 +1110,10 @@ Test cases .. versionchanged:: 3.3 Added the *msg* keyword argument when used as a context manager. + .. versionchanged:: 3.15 + Warnings that do not match the specified category are no longer + swallowed. + Nested context managers are now supported. .. method:: assertWarnsRegex(warning, regex, callable, *args, **kwds) assertWarnsRegex(warning, regex, *, msg=None) @@ -1126,11 +1132,23 @@ Test cases with self.assertWarnsRegex(RuntimeWarning, 'unsafe frobnicating'): frobnicate('/etc/passwd') + The context managers can be nested to test that multiple different + warnings are emitted:: + + with (self.assertWarns(SomeWarning, regex1), + self.assertWarns(OtherWarning, regex2)): + do_something() + .. versionadded:: 3.2 .. versionchanged:: 3.3 Added the *msg* keyword argument when used as a context manager. + .. versionchanged:: 3.15 + Warnings that do not match the specified category or regex are + no longer swallowed. + Nested context managers are now supported. + .. method:: assertLogs(logger=None, level=None, formatter=None) A context manager to test that at least one message is logged on @@ -1177,7 +1195,7 @@ Test cases .. versionadded:: 3.4 - .. versionchanged:: next + .. versionchanged:: 3.15 Now accepts a *formatter* to control how messages are formatted. .. method:: assertNoLogs(logger=None, level=None) @@ -1228,9 +1246,9 @@ Test cases | :meth:`assertNotRegex(s, r) | ``not r.search(s)`` | 3.2 | | ` | | | +---------------------------------------+--------------------------------+--------------+ - | :meth:`assertCountEqual(a, b) | *a* and *b* have the same | 3.2 | - | ` | elements in the same number, | | - | | regardless of their order. | | + | :meth:`assertCountEqual(a, b) | *a* contains the same elements | 3.2 | + | ` | as *b*, regardless of their | | + | | order. | | +---------------------------------------+--------------------------------+--------------+ | :meth:`assertStartsWith(a, b) | ``a.startswith(b)`` | 3.14 | | ` | | | @@ -1244,10 +1262,10 @@ Test cases | :meth:`assertNotEndsWith(a, b) | ``not a.endswith(b)`` | 3.14 | | ` | | | +---------------------------------------+--------------------------------+--------------+ - | :meth:`assertHasAttr(a, b) | ``hastattr(a, b)`` | 3.14 | + | :meth:`assertHasAttr(a, b) | ``hasattr(a, b)`` | 3.14 | | ` | | | +---------------------------------------+--------------------------------+--------------+ - | :meth:`assertNotHasAttr(a, b) | ``not hastattr(a, b)`` | 3.14 | + | :meth:`assertNotHasAttr(a, b) | ``not hasattr(a, b)`` | 3.14 | | ` | | | +---------------------------------------+--------------------------------+--------------+ @@ -1437,7 +1455,7 @@ Test cases that lists the differences between the sets. This method is used by default when comparing sets or frozensets with :meth:`assertEqual`. - Fails if either of *first* or *second* does not have a :meth:`set.difference` + Fails if either of *first* or *second* does not have a :meth:`~frozenset.difference` method. .. versionadded:: 3.1 @@ -1645,7 +1663,7 @@ Test cases .. method:: asyncSetUp() :async: - Method called to prepare the test fixture. This is called after :meth:`setUp`. + Method called to prepare the test fixture. This is called after :meth:`TestCase.setUp`. This is called immediately before calling the test method; other than :exc:`AssertionError` or :exc:`SkipTest`, any exception raised by this method will be considered an error rather than a test failure. The default implementation @@ -1655,7 +1673,7 @@ Test cases :async: Method called immediately after the test method has been called and the - result recorded. This is called before :meth:`tearDown`. This is called even if + result recorded. This is called before :meth:`~TestCase.tearDown`. This is called even if the test method raised an exception, so the implementation in subclasses may need to be particularly careful about checking internal state. Any exception, other than :exc:`AssertionError` or :exc:`SkipTest`, raised by this method will be @@ -1684,7 +1702,7 @@ Test cases Sets up a new event loop to run the test, collecting the result into the :class:`TestResult` object passed as *result*. If *result* is omitted or ``None``, a temporary result object is created (by calling - the :meth:`defaultTestResult` method) and used. The result object is + the :meth:`~TestCase.defaultTestResult` method) and used. The result object is returned to :meth:`run`'s caller. At the end of the test all the tasks in the event loop are cancelled. @@ -1734,7 +1752,7 @@ Test cases allows the test runner to drive the test, but does not provide the methods which test code can use to check and report errors. This is used to create test cases using legacy test code, allowing it to be integrated into a - :mod:`unittest`-based test framework. + :mod:`!unittest`-based test framework. .. _testsuite-objects: @@ -1805,7 +1823,7 @@ Grouping tests returned by repeated iterations before :meth:`TestSuite.run` must be the same for each call iteration. After :meth:`TestSuite.run`, callers should not rely on the tests returned by this method unless the caller uses a - subclass that overrides :meth:`TestSuite._removeTestAtIndex` to preserve + subclass that overrides :meth:`!TestSuite._removeTestAtIndex` to preserve test references. .. versionchanged:: 3.2 @@ -1816,10 +1834,10 @@ Grouping tests .. versionchanged:: 3.4 In earlier versions the :class:`TestSuite` held references to each :class:`TestCase` after :meth:`TestSuite.run`. Subclasses can restore - that behavior by overriding :meth:`TestSuite._removeTestAtIndex`. + that behavior by overriding :meth:`!TestSuite._removeTestAtIndex`. In the typical usage of a :class:`TestSuite` object, the :meth:`run` method - is invoked by a :class:`TestRunner` rather than by the end-user test harness. + is invoked by a :class:`!TestRunner` rather than by the end-user test harness. Loading and running tests @@ -1829,7 +1847,7 @@ Loading and running tests The :class:`TestLoader` class is used to create test suites from classes and modules. Normally, there is no need to create an instance of this class; the - :mod:`unittest` module provides an instance that can be shared as + :mod:`!unittest` module provides an instance that can be shared as :data:`unittest.defaultTestLoader`. Using a subclass or instance, however, allows customization of some configurable properties. @@ -1853,12 +1871,12 @@ Loading and running tests .. method:: loadTestsFromTestCase(testCaseClass) Return a suite of all test cases contained in the :class:`TestCase`\ -derived - :class:`testCaseClass`. + :class:`!testCaseClass`. A test case instance is created for each method named by :meth:`getTestCaseNames`. By default these are the method names beginning with ``test``. If :meth:`getTestCaseNames` returns no - methods, but the :meth:`runTest` method is implemented, a single test + methods, but the :meth:`!runTest` method is implemented, a single test case is created for that method instead. @@ -1905,13 +1923,13 @@ Loading and running tests case class will be picked up as "a test method within a test case class", rather than "a callable object". - For example, if you have a module :mod:`SampleTests` containing a - :class:`TestCase`\ -derived class :class:`SampleTestCase` with three test - methods (:meth:`test_one`, :meth:`test_two`, and :meth:`test_three`), the + For example, if you have a module :mod:`!SampleTests` containing a + :class:`TestCase`\ -derived class :class:`!SampleTestCase` with three test + methods (:meth:`!test_one`, :meth:`!test_two`, and :meth:`!test_three`), the specifier ``'SampleTests.SampleTestCase'`` would cause this method to return a suite which will run all three test methods. Using the specifier ``'SampleTests.SampleTestCase.test_two'`` would cause it to return a test - suite which will run only the :meth:`test_two` test method. The specifier + suite which will run only the :meth:`!test_two` test method. The specifier can refer to modules and packages which have not been imported; they will be imported as a side-effect. @@ -2055,10 +2073,10 @@ Loading and running tests properly recorded; test authors do not need to worry about recording the outcome of tests. - Testing frameworks built on top of :mod:`unittest` may want access to the + Testing frameworks built on top of :mod:`!unittest` may want access to the :class:`TestResult` object generated by running a set of tests for reporting purposes; a :class:`TestResult` instance is returned by the - :meth:`TestRunner.run` method for this purpose. + :meth:`!TestRunner.run` method for this purpose. :class:`TestResult` instances have the following attributes that will be of interest when inspecting the results of running a set of tests: @@ -2138,18 +2156,18 @@ Loading and running tests .. versionchanged:: 3.4 Returns ``False`` if there were any :attr:`unexpectedSuccesses` - from tests marked with the :func:`expectedFailure` decorator. + from tests marked with the :deco:`expectedFailure` decorator. .. method:: stop() This method can be called to signal that the set of tests being run should be aborted by setting the :attr:`shouldStop` attribute to ``True``. - :class:`TestRunner` objects should respect this flag and return without + :class:`!TestRunner` objects should respect this flag and return without running any additional tests. For example, this feature is used by the :class:`TextTestRunner` class to stop the test framework when the user signals an interrupt from the - keyboard. Interactive tools which provide :class:`TestRunner` + keyboard. Interactive tools which provide :class:`!TestRunner` implementations can use this in a similar manner. The following methods of the :class:`TestResult` class are used to maintain @@ -2221,7 +2239,7 @@ Loading and running tests .. method:: addExpectedFailure(test, err) Called when the test case *test* fails or errors, but was marked with - the :func:`expectedFailure` decorator. + the :deco:`expectedFailure` decorator. The default implementation appends a tuple ``(test, formatted_err)`` to the instance's :attr:`expectedFailures` attribute, where *formatted_err* @@ -2231,7 +2249,7 @@ Loading and running tests .. method:: addUnexpectedSuccess(test) Called when the test case *test* was marked with the - :func:`expectedFailure` decorator, but succeeded. + :deco:`expectedFailure` decorator, but succeeded. The default implementation appends the test to the instance's :attr:`unexpectedSuccesses` attribute. @@ -2469,9 +2487,9 @@ Class and Module Fixtures ------------------------- Class and module level fixtures are implemented in :class:`TestSuite`. When -the test suite encounters a test from a new class then :meth:`tearDownClass` -from the previous class (if there is one) is called, followed by -:meth:`setUpClass` from the new class. +the test suite encounters a test from a new class then +:meth:`~TestCase.tearDownClass` from the previous class (if there is one) +is called, followed by :meth:`~TestCase.setUpClass` from the new class. Similarly if a test is from a different module from the previous test then ``tearDownModule`` from the previous module is run, followed by @@ -2532,6 +2550,10 @@ instead of as an error. setUpModule and tearDownModule ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. function:: setUpModule + tearDownModule + :no-typesetting: + These should be implemented as functions:: def setUpModule(): diff --git a/Doc/library/unix.rst b/Doc/library/unix.rst index 4553a104d15a24..bae32b362803f1 100644 --- a/Doc/library/unix.rst +++ b/Doc/library/unix.rst @@ -1,7 +1,7 @@ .. _unix: ********************** -Unix Specific Services +Unix-specific services ********************** The modules described in this chapter provide interfaces to features that are @@ -11,6 +11,7 @@ of it. Here's an overview: .. toctree:: + shlex.rst posix.rst pwd.rst grp.rst diff --git a/Doc/library/urllib.error.rst b/Doc/library/urllib.error.rst index 1686ddd09caa48..b8864e36981ab9 100644 --- a/Doc/library/urllib.error.rst +++ b/Doc/library/urllib.error.rst @@ -4,17 +4,14 @@ .. module:: urllib.error :synopsis: Exception classes raised by urllib.request. -.. moduleauthor:: Jeremy Hylton -.. sectionauthor:: Senthil Kumaran - **Source code:** :source:`Lib/urllib/error.py` -------------- -The :mod:`urllib.error` module defines the exception classes for exceptions +The :mod:`!urllib.error` module defines the exception classes for exceptions raised by :mod:`urllib.request`. The base exception class is :exc:`URLError`. -The following exceptions are raised by :mod:`urllib.error` as appropriate: +The following exceptions are raised by :mod:`!urllib.error` as appropriate: .. exception:: URLError diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst index 44a9c79cba2216..ef48addaba03e9 100644 --- a/Doc/library/urllib.parse.rst +++ b/Doc/library/urllib.parse.rst @@ -35,7 +35,7 @@ Resource Locators. It supports the following URL schemes: ``file``, ``ftp``, macOS, it *may* be removed if CPython has been built with the :option:`--with-app-store-compliance` option. -The :mod:`urllib.parse` module defines functions that fall into two broad +The :mod:`!urllib.parse` module defines functions that fall into two broad categories: URL parsing and URL quoting. These are covered in detail in the following sections. @@ -50,12 +50,17 @@ URL Parsing The URL parsing functions focus on splitting a URL string into its components, or on combining URL components into a URL string. -.. function:: urlparse(urlstring, scheme='', allow_fragments=True) - - Parse a URL into six components, returning a 6-item :term:`named tuple`. This - corresponds to the general structure of a URL: - ``scheme://netloc/path;parameters?query#fragment``. - Each tuple item is a string, possibly empty. The components are not broken up +.. function:: urlsplit(urlstring, scheme=None, allow_fragments=True, *, missing_as_none=False) + + Parse a URL into five components, returning a 5-item :term:`named tuple` + :class:`SplitResult` or :class:`SplitResultBytes`. + This corresponds to the general structure of a URL: + ``scheme://netloc/path?query#fragment``. + Each tuple item is a string, possibly empty, or ``None`` if + *missing_as_none* is true. + Not defined component are represented an empty string (by default) or + ``None`` if *missing_as_none* is true. + The components are not broken up into smaller parts (for example, the network location is a single string), and % escapes are not expanded. The delimiters as shown above are not part of the result, except for a leading slash in the *path* component, which is retained if @@ -64,15 +69,15 @@ or on combining URL components into a URL string. .. doctest:: :options: +NORMALIZE_WHITESPACE - >>> from urllib.parse import urlparse - >>> urlparse("scheme://netloc/path;parameters?query#fragment") - ParseResult(scheme='scheme', netloc='netloc', path='/path;parameters', params='', + >>> from urllib.parse import urlsplit + >>> urlsplit("scheme://netloc/path?query#fragment") + SplitResult(scheme='scheme', netloc='netloc', path='/path', query='query', fragment='fragment') - >>> o = urlparse("http://docs.python.org:80/3/library/urllib.parse.html?" + >>> o = urlsplit("http://docs.python.org:80/3/library/urllib.parse.html?" ... "highlight=params#url-parsing") >>> o - ParseResult(scheme='http', netloc='docs.python.org:80', - path='/3/library/urllib.parse.html', params='', + SplitResult(scheme='http', netloc='docs.python.org:80', + path='/3/library/urllib.parse.html', query='highlight=params', fragment='url-parsing') >>> o.scheme 'http' @@ -84,8 +89,14 @@ or on combining URL components into a URL string. 80 >>> o._replace(fragment="").geturl() 'http://docs.python.org:80/3/library/urllib.parse.html?highlight=params' + >>> urlsplit("http://docs.python.org?") + SplitResult(scheme='http', netloc='docs.python.org', path='', + query='', fragment='') + >>> urlsplit("http://docs.python.org?", missing_as_none=True) + SplitResult(scheme='http', netloc='docs.python.org', path='', + query='', fragment=None) - Following the syntax specifications in :rfc:`1808`, urlparse recognizes + Following the syntax specifications in :rfc:`1808`, :func:`!urlsplit` recognizes a netloc only if it is properly introduced by '//'. Otherwise the input is presumed to be a relative URL and thus to start with a path component. @@ -93,55 +104,58 @@ or on combining URL components into a URL string. .. doctest:: :options: +NORMALIZE_WHITESPACE - >>> from urllib.parse import urlparse - >>> urlparse('//www.cwi.nl:80/%7Eguido/Python.html') - ParseResult(scheme='', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', - params='', query='', fragment='') - >>> urlparse('www.cwi.nl/%7Eguido/Python.html') - ParseResult(scheme='', netloc='', path='www.cwi.nl/%7Eguido/Python.html', - params='', query='', fragment='') - >>> urlparse('help/Python.html') - ParseResult(scheme='', netloc='', path='help/Python.html', params='', + >>> from urllib.parse import urlsplit + >>> urlsplit('//www.cwi.nl:80/%7Eguido/Python.html') + SplitResult(scheme='', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', query='', fragment='') + >>> urlsplit('www.cwi.nl/%7Eguido/Python.html') + SplitResult(scheme='', netloc='', path='www.cwi.nl/%7Eguido/Python.html', + query='', fragment='') + >>> urlsplit('help/Python.html') + SplitResult(scheme='', netloc='', path='help/Python.html', + query='', fragment='') + >>> urlsplit('help/Python.html', missing_as_none=True) + SplitResult(scheme=None, netloc=None, path='help/Python.html', + query=None, fragment=None) The *scheme* argument gives the default addressing scheme, to be used only if the URL does not specify one. It should be the same type - (text or bytes) as *urlstring*, except that the default value ``''`` is + (text or bytes) as *urlstring* or ``None``, except that the ``''`` is always allowed, and is automatically converted to ``b''`` if appropriate. If the *allow_fragments* argument is false, fragment identifiers are not - recognized. Instead, they are parsed as part of the path, parameters - or query component, and :attr:`fragment` is set to the empty string in - the return value. + recognized. Instead, they are parsed as part of the path + or query component, and :attr:`fragment` is set to ``None`` or the empty + string (depending on the value of *missing_as_none*) in the return value. The return value is a :term:`named tuple`, which means that its items can be accessed by index or as named attributes, which are: - +------------------+-------+-------------------------+------------------------+ - | Attribute | Index | Value | Value if not present | - +==================+=======+=========================+========================+ - | :attr:`scheme` | 0 | URL scheme specifier | *scheme* parameter | - +------------------+-------+-------------------------+------------------------+ - | :attr:`netloc` | 1 | Network location part | empty string | - +------------------+-------+-------------------------+------------------------+ - | :attr:`path` | 2 | Hierarchical path | empty string | - +------------------+-------+-------------------------+------------------------+ - | :attr:`params` | 3 | Parameters for last | empty string | - | | | path element | | - +------------------+-------+-------------------------+------------------------+ - | :attr:`query` | 4 | Query component | empty string | - +------------------+-------+-------------------------+------------------------+ - | :attr:`fragment` | 5 | Fragment identifier | empty string | - +------------------+-------+-------------------------+------------------------+ - | :attr:`username` | | User name | :const:`None` | - +------------------+-------+-------------------------+------------------------+ - | :attr:`password` | | Password | :const:`None` | - +------------------+-------+-------------------------+------------------------+ - | :attr:`hostname` | | Host name (lower case) | :const:`None` | - +------------------+-------+-------------------------+------------------------+ - | :attr:`port` | | Port number as integer, | :const:`None` | - | | | if present | | - +------------------+-------+-------------------------+------------------------+ + +------------------+-------+-------------------------+-------------------------------+ + | Attribute | Index | Value | Value if not present | + +==================+=======+=========================+===============================+ + | :attr:`scheme` | 0 | URL scheme specifier | *scheme* parameter or | + | | | | empty string [1]_ | + +------------------+-------+-------------------------+-------------------------------+ + | :attr:`netloc` | 1 | Network location part | ``None`` or empty string [1]_ | + +------------------+-------+-------------------------+-------------------------------+ + | :attr:`path` | 2 | Hierarchical path | empty string | + +------------------+-------+-------------------------+-------------------------------+ + | :attr:`query` | 3 | Query component | ``None`` or empty string [1]_ | + +------------------+-------+-------------------------+-------------------------------+ + | :attr:`fragment` | 4 | Fragment identifier | ``None`` or empty string [1]_ | + +------------------+-------+-------------------------+-------------------------------+ + | :attr:`username` | | User name | ``None`` | + +------------------+-------+-------------------------+-------------------------------+ + | :attr:`password` | | Password | ``None`` | + +------------------+-------+-------------------------+-------------------------------+ + | :attr:`hostname` | | Host name (lower case) | ``None`` | + +------------------+-------+-------------------------+-------------------------------+ + | :attr:`port` | | Port number as integer, | ``None`` | + | | | if present | | + +------------------+-------+-------------------------+-------------------------------+ + + .. [1] Depending on the value of the *missing_as_none* argument. Reading the :attr:`port` attribute will raise a :exc:`ValueError` if an invalid port is specified in the URL. See section @@ -155,26 +169,30 @@ or on combining URL components into a URL string. ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is decomposed before parsing, no error will be raised. + Following some of the `WHATWG spec`_ that updates :rfc:`3986`, leading C0 + control and space characters are stripped from the URL. ``\n``, + ``\r`` and tab ``\t`` characters are removed from the URL at any position. + As is the case with all named tuples, the subclass has a few additional methods and attributes that are particularly useful. One such method is :meth:`_replace`. - The :meth:`_replace` method will return a new ParseResult object replacing specified - fields with new values. + The :meth:`_replace` method will return a new :class:`SplitResult` object + replacing specified fields with new values. .. doctest:: :options: +NORMALIZE_WHITESPACE - >>> from urllib.parse import urlparse - >>> u = urlparse('//www.cwi.nl:80/%7Eguido/Python.html') + >>> from urllib.parse import urlsplit + >>> u = urlsplit('//www.cwi.nl:80/%7Eguido/Python.html') >>> u - ParseResult(scheme='', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', - params='', query='', fragment='') + SplitResult(scheme='', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', + query='', fragment='') >>> u._replace(scheme='http') - ParseResult(scheme='http', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', - params='', query='', fragment='') + SplitResult(scheme='http', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', + query='', fragment='') .. warning:: - :func:`urlparse` does not perform validation. See :ref:`URL parsing + :func:`urlsplit` does not perform validation. See :ref:`URL parsing security ` for details. .. versionchanged:: 3.2 @@ -187,12 +205,23 @@ or on combining URL components into a URL string. .. versionchanged:: 3.6 Out-of-range port numbers now raise :exc:`ValueError`, instead of - returning :const:`None`. + returning ``None``. .. versionchanged:: 3.8 Characters that affect netloc parsing under NFKC normalization will now raise :exc:`ValueError`. + .. versionchanged:: 3.10 + ASCII newline and tab characters are stripped from the URL. + + .. versionchanged:: 3.12 + Leading WHATWG C0 control and space characters are stripped from the URL. + + .. versionchanged:: 3.15 + Added the *missing_as_none* parameter. + +.. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser + .. function:: parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None, separator='&') @@ -287,96 +316,63 @@ or on combining URL components into a URL string. separator key, with ``&`` as the default separator. -.. function:: urlunparse(parts) - - Construct a URL from a tuple as returned by ``urlparse()``. The *parts* - argument can be any six-item iterable. This may result in a slightly - different, but equivalent URL, if the URL that was parsed originally had - unnecessary delimiters (for example, a ``?`` with an empty query; the RFC - states that these are equivalent). - - -.. function:: urlsplit(urlstring, scheme='', allow_fragments=True) - - This is similar to :func:`urlparse`, but does not split the params from the URL. - This should generally be used instead of :func:`urlparse` if the more recent URL - syntax allowing parameters to be applied to each segment of the *path* portion - of the URL (see :rfc:`2396`) is wanted. A separate function is needed to - separate the path segments and parameters. This function returns a 5-item - :term:`named tuple`:: - - (addressing scheme, network location, path, query, fragment identifier). - - The return value is a :term:`named tuple`, its items can be accessed by index - or as named attributes: +.. function:: urlunsplit(parts) + urlunsplit(parts, *, keep_empty) - +------------------+-------+-------------------------+----------------------+ - | Attribute | Index | Value | Value if not present | - +==================+=======+=========================+======================+ - | :attr:`scheme` | 0 | URL scheme specifier | *scheme* parameter | - +------------------+-------+-------------------------+----------------------+ - | :attr:`netloc` | 1 | Network location part | empty string | - +------------------+-------+-------------------------+----------------------+ - | :attr:`path` | 2 | Hierarchical path | empty string | - +------------------+-------+-------------------------+----------------------+ - | :attr:`query` | 3 | Query component | empty string | - +------------------+-------+-------------------------+----------------------+ - | :attr:`fragment` | 4 | Fragment identifier | empty string | - +------------------+-------+-------------------------+----------------------+ - | :attr:`username` | | User name | :const:`None` | - +------------------+-------+-------------------------+----------------------+ - | :attr:`password` | | Password | :const:`None` | - +------------------+-------+-------------------------+----------------------+ - | :attr:`hostname` | | Host name (lower case) | :const:`None` | - +------------------+-------+-------------------------+----------------------+ - | :attr:`port` | | Port number as integer, | :const:`None` | - | | | if present | | - +------------------+-------+-------------------------+----------------------+ + Construct a URL from a tuple as returned by :func:`urlsplit`. The *parts* + argument can be any five-item iterable. - Reading the :attr:`port` attribute will raise a :exc:`ValueError` if - an invalid port is specified in the URL. See section - :ref:`urlparse-result-object` for more information on the result object. + This may result in a slightly different, but equivalent URL, if the + URL that was parsed originally had unnecessary delimiters (for example, + a ``?`` with an empty query; the RFC states that these are equivalent). - Unmatched square brackets in the :attr:`netloc` attribute will raise a - :exc:`ValueError`. + If *keep_empty* is true, empty strings are kept in the result (for example, + a ``?`` for an empty query), only ``None`` components are omitted. + This allows rebuilding a URL that was parsed with option + ``missing_as_none=True``. + By default, *keep_empty* is true if *parts* is the result of the + :func:`urlsplit` call with ``missing_as_none=True``. - Characters in the :attr:`netloc` attribute that decompose under NFKC - normalization (as used by the IDNA encoding) into any of ``/``, ``?``, - ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is - decomposed before parsing, no error will be raised. + .. versionchanged:: 3.15 + Added the *keep_empty* parameter. - Following some of the `WHATWG spec`_ that updates RFC 3986, leading C0 - control and space characters are stripped from the URL. ``\n``, - ``\r`` and tab ``\t`` characters are removed from the URL at any position. - .. warning:: - - :func:`urlsplit` does not perform validation. See :ref:`URL parsing - security ` for details. +.. function:: urlparse(urlstring, scheme=None, allow_fragments=True, *, missing_as_none=False) - .. versionchanged:: 3.6 - Out-of-range port numbers now raise :exc:`ValueError`, instead of - returning :const:`None`. + This is similar to :func:`urlsplit`, but additionally splits the *path* + component on *path* and *params*. + This function returns a 6-item :term:`named tuple` :class:`ParseResult` + or :class:`ParseResultBytes`. + Its items are the same as for the :func:`!urlsplit` result, except that + *params* is inserted at index 3, between *path* and *query*. - .. versionchanged:: 3.8 - Characters that affect netloc parsing under NFKC normalization will - now raise :exc:`ValueError`. + This function is based on obsoleted :rfc:`1738` and :rfc:`1808`, which + listed *params* as the main URL component. + The more recent URL syntax allows parameters to be applied to each segment + of the *path* portion of the URL (see :rfc:`3986`). + :func:`urlsplit` should generally be used instead of :func:`urlparse`. + A separate function is needed to separate the path segments and parameters. - .. versionchanged:: 3.10 - ASCII newline and tab characters are stripped from the URL. +.. function:: urlunparse(parts) + urlunparse(parts, *, keep_empty) - .. versionchanged:: 3.12 - Leading WHATWG C0 control and space characters are stripped from the URL. + Combine the elements of a tuple as returned by :func:`urlparse` into a + complete URL as a string. The *parts* argument can be any six-item + iterable. -.. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser + This may result in a slightly different, but equivalent URL, if the + URL that was parsed originally had unnecessary delimiters (for example, + a ``?`` with an empty query; the RFC states that these are equivalent). -.. function:: urlunsplit(parts) + If *keep_empty* is true, empty strings are kept in the result (for example, + a ``?`` for an empty query), only ``None`` components are omitted. + This allows rebuilding a URL that was parsed with option + ``missing_as_none=True``. + By default, *keep_empty* is true if *parts* is the result of the + :func:`urlparse` call with ``missing_as_none=True``. - Combine the elements of a tuple as returned by :func:`urlsplit` into a - complete URL as a string. The *parts* argument can be any five-item - iterable. This may result in a slightly different, but equivalent URL, if the - URL that was parsed originally had unnecessary delimiters (for example, a ? - with an empty query; the RFC states that these are equivalent). + .. versionchanged:: 3.15 + Added the *keep_empty* parameter. .. function:: urljoin(base, url, allow_fragments=True) @@ -391,7 +387,7 @@ or on combining URL components into a URL string. 'http://www.cwi.nl/%7Eguido/FAQ.html' The *allow_fragments* argument has the same meaning and default as for - :func:`urlparse`. + :func:`urlsplit`. .. note:: @@ -422,23 +418,25 @@ or on combining URL components into a URL string. Behavior updated to match the semantics defined in :rfc:`3986`. -.. function:: urldefrag(url) +.. function:: urldefrag(url, *, missing_as_none=False) If *url* contains a fragment identifier, return a modified version of *url* with no fragment identifier, and the fragment identifier as a separate string. If there is no fragment identifier in *url*, return *url* unmodified - and an empty string. + and an empty string (by default) or ``None`` if *missing_as_none* is true. The return value is a :term:`named tuple`, its items can be accessed by index or as named attributes: - +------------------+-------+-------------------------+----------------------+ - | Attribute | Index | Value | Value if not present | - +==================+=======+=========================+======================+ - | :attr:`url` | 0 | URL with no fragment | empty string | - +------------------+-------+-------------------------+----------------------+ - | :attr:`fragment` | 1 | Fragment identifier | empty string | - +------------------+-------+-------------------------+----------------------+ + +------------------+-------+-------------------------+-------------------------------+ + | Attribute | Index | Value | Value if not present | + +==================+=======+=========================+===============================+ + | :attr:`url` | 0 | URL with no fragment | empty string | + +------------------+-------+-------------------------+-------------------------------+ + | :attr:`fragment` | 1 | Fragment identifier | ``None`` or empty string [3]_ | + +------------------+-------+-------------------------+-------------------------------+ + + .. [3] Depending on the value of the *missing_as_none* argument. See section :ref:`urlparse-result-object` for more information on the result object. @@ -446,6 +444,9 @@ or on combining URL components into a URL string. .. versionchanged:: 3.2 Result is a structured object rather than a simple 2-tuple. + .. versionchanged:: 3.15 + Added the *missing_as_none* parameter. + .. function:: unwrap(url) Extract the url from a wrapped URL (that is, a string formatted as @@ -465,8 +466,9 @@ URLs elsewhere. Their purpose is for practical functionality rather than purity. Instead of raising an exception on unusual input, they may instead return some -component parts as empty strings. Or components may contain more than perhaps -they should. +component parts as empty strings or ``None`` (depending on the value of the +*missing_as_none* argument). +Or components may contain more than perhaps they should. We recommend that users of these APIs where the values may be used anywhere with security implications code defensively. Do some verification within your @@ -531,7 +533,7 @@ individual URL quoting functions. Structured Parse Results ------------------------ -The result objects from the :func:`urlparse`, :func:`urlsplit` and +The result objects from the :func:`urlsplit`, :func:`urlparse` and :func:`urldefrag` functions are subclasses of the :class:`tuple` type. These subclasses add the attributes listed in the documentation for those functions, the encoding and decoding support described in the @@ -542,7 +544,8 @@ previous section, as well as an additional method: Return the re-combined version of the original URL as a string. This may differ from the original URL in that the scheme may be normalized to lower case and empty components may be dropped. Specifically, empty parameters, - queries, and fragment identifiers will be removed. + queries, and fragment identifiers will be removed unless the URL was parsed + with ``missing_as_none=True``. For :func:`urldefrag` results, only empty fragment identifiers will be removed. For :func:`urlsplit` and :func:`urlparse` results, all noted changes will be @@ -559,6 +562,9 @@ previous section, as well as an additional method: >>> r2 = urlsplit(r1.geturl()) >>> r2.geturl() 'http://www.Python.org/doc/' + >>> r3 = urlsplit(url, missing_as_none=True) + >>> r3.geturl() + 'http://www.Python.org/doc/#' The following classes provide the implementations of the structured parse diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index e514b98fc5d553..03518d49d437ce 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -4,15 +4,11 @@ .. module:: urllib.request :synopsis: Extensible library for opening URLs. -.. moduleauthor:: Jeremy Hylton -.. sectionauthor:: Moshe Zadka -.. sectionauthor:: Senthil Kumaran - **Source code:** :source:`Lib/urllib/request.py` -------------- -The :mod:`urllib.request` module defines functions and classes which help in +The :mod:`!urllib.request` module defines functions and classes which help in opening URLs (mostly HTTP) in a complex world --- basic and digest authentication, redirections, cookies and more. @@ -31,7 +27,7 @@ authentication, redirections, cookies and more. .. include:: ../includes/wasm-notavail.rst -The :mod:`urllib.request` module defines the following functions: +The :mod:`!urllib.request` module defines the following functions: .. function:: urlopen(url, data=None[, timeout], *, context=None) @@ -837,7 +833,7 @@ The following attribute and methods should only be used by classes derived from 1. a :class:`Request` object, #. a file-like object with the HTTP error body, #. the three-digit code of the error, as a string, - #. the user-visible explanation of the code, as as string, and + #. the user-visible explanation of the code, as a string, and #. the headers of the error, as a mapping object. Return values and exceptions raised should be the same as those of @@ -1055,7 +1051,7 @@ AbstractBasicAuthHandler Objects *headers* should be the error headers. *host* is either an authority (e.g. ``"python.org"``) or a URL containing an - authority component (e.g. ``"http://python.org/"``). In either case, the + authority component (e.g. ``"https://python.org/"``). In either case, the authority must not contain a userinfo component (so, ``"python.org"`` and ``"python.org:80"`` are fine, ``"joe:password@python.org"`` is not). @@ -1251,10 +1247,14 @@ This example gets the python.org main page and displays the first 300 bytes of it:: >>> import urllib.request - >>> with urllib.request.urlopen('http://www.python.org/') as f: - ... print(f.read(300)) - ... - b'\n\n\n - >> import urllib.request - >>> f = urllib.request.urlopen('http://www.python.org/') + >>> f = urllib.request.urlopen('https://www.python.org/') >>> try: - ... print(f.read(100).decode('utf-8')) + ... enc = f.headers.get('Content-Encoding') + ... data = f.read() + ... if enc == 'gzip': + ... import gzip + ... data = gzip.decompress(data) + ... print(data[:100].decode('utf-8', errors='replace')) ... finally: ... f.close() - ... - - - + Modules --- diff --git a/InternalDocs/asyncio.md b/InternalDocs/asyncio.md index 22159852ca54db..ca7e8a92dec1ce 100644 --- a/InternalDocs/asyncio.md +++ b/InternalDocs/asyncio.md @@ -205,7 +205,7 @@ the current task is found and returned. If no matching thread state is found, `None` is returned. In free-threading, it avoids contention on a global dictionary as -threads can access the current task of thier running loop without any +threads can access the current task of their running loop without any locking. --- diff --git a/InternalDocs/code_objects.md b/InternalDocs/code_objects.md index a91a7043c1b8d4..cccbe71588622c 100644 --- a/InternalDocs/code_objects.md +++ b/InternalDocs/code_objects.md @@ -70,14 +70,6 @@ The `co_linetable` bytes object of code objects contains a compact representation of the source code positions of instructions, which are returned by the `co_positions()` iterator. -> [!NOTE] -> `co_linetable` is not to be confused with `co_lnotab`. -> For backwards compatibility, `co_lnotab` exposes the format -> as it existed in Python 3.10 and lower: this older format -> stores only the start line for each instruction. -> It is lazily created from `co_linetable` when accessed. -> See [`Objects/lnotab_notes.txt`](../Objects/lnotab_notes.txt) for more details. - `co_linetable` consists of a sequence of location entries. Each entry starts with a byte with the most significant bit set, followed by zero or more bytes with the most significant bit unset. diff --git a/InternalDocs/compiler.md b/InternalDocs/compiler.md index 02bbdf6071fa12..9ed4d0eb65a0bd 100644 --- a/InternalDocs/compiler.md +++ b/InternalDocs/compiler.md @@ -359,19 +359,19 @@ in [Python/compile.c](../Python/compile.c) into a sequence of pseudo instruction These are similar to bytecode, but in some cases they are more abstract, and are resolved later into actual bytecode. The construction of this instruction sequence is handled by several functions that break the task down by various AST node types. -The functions are all named `compiler_visit_{xx}` where *xx* is the name of the node +The functions are all named `codegen_visit_{xx}` where *xx* is the name of the node type (such as `stmt`, `expr`, etc.). Each function receives a `struct compiler *` and `{xx}_ty` where *xx* is the AST node type. Typically these functions consist of a large 'switch' statement, branching based on the kind of node type passed to it. Simple things are handled inline in the 'switch' statement with more complex transformations farmed out to other -functions named `compiler_{xx}` with *xx* being a descriptive name of what is +functions named `codegen_{xx}` with *xx* being a descriptive name of what is being handled. When transforming an arbitrary AST node, use the `VISIT()` macro. -The appropriate `compiler_visit_{xx}` function is called, based on the value +The appropriate `codegen_visit_{xx}` function is called, based on the value passed in for (so `VISIT({c}, expr, {node})` calls -`compiler_visit_expr({c}, {node})`). The `VISIT_SEQ()` macro is very similar, +`codegen_visit_expr({c}, {node})`). The `VISIT_SEQ()` macro is very similar, but is called on AST node sequences (those values that were created as arguments to a node that used the '*' modifier). @@ -414,8 +414,8 @@ which is added at the end of a function is not associated with any line in the source code. There are several helper functions that will emit pseudo-instructions -and are named `compiler_{xx}()` where *xx* is what the function helps -with (`list`, `boolop`, etc.). A rather useful one is `compiler_nameop()`. +and are named `codegen_{xx}()` where *xx* is what the function helps +with (`list`, `boolop`, etc.). A rather useful one is `codegen_nameop()`. This function looks up the scope of a variable and, based on the expression context, emits the proper opcode to load, store, or delete the variable. @@ -604,4 +604,4 @@ References pp. 213--227, 1997. [^2]: The Zephyr Abstract Syntax Description Language.: - https://www.cs.princeton.edu/research/techreps/TR-554-97 + https://www.cs.princeton.edu/research/techreps/254 diff --git a/InternalDocs/frames.md b/InternalDocs/frames.md index 804d7436018a10..60ab2055afa7b1 100644 --- a/InternalDocs/frames.md +++ b/InternalDocs/frames.md @@ -111,6 +111,55 @@ The shim frame points to a special code object containing the `INTERPRETER_EXIT` instruction which cleans up the shim frame and returns. +### Base frame + +Each thread state contains an embedded `_PyInterpreterFrame` called the "base frame" +that serves as a sentinel at the bottom of the frame stack. This frame is allocated +in `_PyThreadStateImpl` (the internal extension of `PyThreadState`) and initialized +when the thread state is created. The `owner` field is set to `FRAME_OWNED_BY_INTERPRETER`. + +External profilers and sampling tools can validate that they have successfully unwound +the complete call stack by checking that the frame chain terminates at the base frame. +The `PyThreadState.base_frame` pointer provides the expected address to compare against. +If a stack walk doesn't reach this frame, the sample is incomplete (possibly due to a +race condition) and should be discarded. + +The base frame is embedded in `_PyThreadStateImpl` rather than `PyThreadState` because +`_PyInterpreterFrame` is defined in internal headers that cannot be exposed in the +public API. A pointer (`PyThreadState.base_frame`) is provided for profilers to access +the address without needing internal headers. + +See the initialization in `new_threadstate()` in [Python/pystate.c](../Python/pystate.c). + +#### How profilers should use the base frame + +External profilers should read `tstate->base_frame` before walking the stack, then +walk from `tstate->current_frame` following `frame->previous` pointers until reaching +a frame with `owner == FRAME_OWNED_BY_INTERPRETER`. After the walk, verify that the +last frame address matches `base_frame`. If not, discard the sample as incomplete +since the frame chain may have been in an inconsistent state due to concurrent updates. + + +### Remote Profiling Frame Cache + +The `last_profiled_frame` field in `PyThreadState` supports an optimization for +remote profilers that sample call stacks from external processes. When a remote +profiler reads the call stack, it writes the current frame address to this field. +The eval loop then keeps this pointer valid by updating it to the parent frame +whenever a frame returns (in `_PyEval_FrameClearAndPop`). + +This creates a "high-water mark" that always points to a frame still on the stack. +On subsequent samples, the profiler can walk from `current_frame` until it reaches +`last_profiled_frame`, knowing that frames from that point downward are unchanged +and can be retrieved from a cache. This significantly reduces the amount of remote +memory reads needed when call stacks are deep and stable at their base. + +The update in `_PyEval_FrameClearAndPop` is guarded: it only writes when +`last_profiled_frame` is non-NULL AND matches the frame being popped. This +prevents transient frames (called and returned between profiler samples) from +corrupting the cache pointer, while avoiding any overhead when profiling is inactive. + + ### The Instruction Pointer `_PyInterpreterFrame` has two fields which are used to maintain the instruction diff --git a/InternalDocs/garbage_collector.md b/InternalDocs/garbage_collector.md index 9c35684c945b3e..0ef45ff8e02bc5 100644 --- a/InternalDocs/garbage_collector.md +++ b/InternalDocs/garbage_collector.md @@ -107,7 +107,7 @@ As is explained later in the [Optimization: reusing fields to save memory](#optimization-reusing-fields-to-save-memory) section, these two extra fields are normally used to keep doubly linked lists of all the objects tracked by the garbage collector (these lists are the GC generations, more on -that in the [Optimization: incremental collection](#Optimization-incremental-collection) section), but +that in the [Optimization: generations](#Optimization-generations) section), but they are also reused to fulfill other purposes when the full doubly linked list structure is not needed as a memory optimization. @@ -153,7 +153,11 @@ pointer-sized (that is, eight bytes on a 64-bit platform). The garbage collector also temporarily repurposes the `ob_tid` (thread ID) and `ob_ref_local` (local reference count) fields for other purposes during -collections. +collections. The `ob_tid` field is later restored from the containing +mimalloc segment data structure. In some cases, such as when the original +allocating thread exits, this can result in a different `ob_tid` value. +Code should not rely on `ob_tid` being stable across operations that may +trigger garbage collection. C APIs @@ -199,22 +203,22 @@ unreachable: ```pycon >>> import gc ->>> +>>> >>> class Link: ... def __init__(self, next_link=None): ... self.next_link = next_link -... +... >>> link_3 = Link() >>> link_2 = Link(link_3) >>> link_1 = Link(link_2) >>> link_3.next_link = link_1 >>> A = link_1 >>> del link_1, link_2, link_3 ->>> +>>> >>> link_4 = Link() >>> link_4.next_link = link_4 >>> del link_4 ->>> +>>> >>> # Collect the unreachable Link object (and its .__dict__ dict). >>> gc.collect() 2 @@ -329,15 +333,16 @@ Once the GC knows the list of unreachable objects, a very delicate process start with the objective of completely destroying these objects. Roughly, the process follows these steps in order: -1. Handle and clear weak references (if any). Weak references to unreachable objects - are set to `None`. If the weak reference has an associated callback, the callback - is enqueued to be called once the clearing of weak references is finished. We only - invoke callbacks for weak references that are themselves reachable. If both the weak - reference and the pointed-to object are unreachable we do not execute the callback. - This is partly for historical reasons: the callback could resurrect an unreachable - object and support for weak references predates support for object resurrection. - Ignoring the weak reference's callback is fine because both the object and the weakref - are going away, so it's legitimate to say the weak reference is going away first. +1. Handle weak references with callbacks (if any). If the weak reference has + an associated callback, the callback is enqueued to be called after the weak + reference is cleared. We only invoke callbacks for weak references that + are themselves reachable. If both the weak reference and the pointed-to + object are unreachable we do not execute the callback. This is partly for + historical reasons: the callback could resurrect an unreachable object + and support for weak references predates support for object resurrection. + Ignoring the weak reference's callback is fine because both the object and + the weakref are going away, so it's legitimate to say the weak reference is + going away first. 2. If an object has legacy finalizers (`tp_del` slot) move it to the `gc.garbage` list. 3. Call the finalizers (`tp_finalize` slot) and mark the objects as already @@ -346,15 +351,21 @@ follows these steps in order: 4. Deal with resurrected objects. If some objects have been resurrected, the GC finds the new subset of objects that are still unreachable by running the cycle detection algorithm again and continues with them. -5. Call the `tp_clear` slot of every object so all internal links are broken and +5. Clear any weak references that still refer to unreachable objects. The + `wr_object` attribute for these weakrefs are set to `None`. Note that some + of these weak references maybe have been newly created during the running of + finalizers in step 3. Also, clear any weak references that are part of the + unreachable set. +6. Call the `tp_clear` slot of every object so all internal links are broken and the reference counts fall to 0, triggering the destruction of all unreachable objects. -Optimization: incremental collection -==================================== +Optimization: generations +========================= -In order to bound the length of each garbage collection pause, the GC implementation -for the default build uses incremental collection with two generations. +In order to limit the time each garbage collection takes, the GC +implementation for the default build uses a popular optimization: +generations. Generational garbage collection takes advantage of what is known as the weak generational hypothesis: Most objects die young. @@ -362,76 +373,29 @@ This has proven to be very close to the reality of many Python programs as many temporary objects are created and destroyed very quickly. To take advantage of this fact, all container objects are segregated into -two generations: young and old. Every new object starts in the young generation. -Each garbage collection scans the entire young generation and part of the old generation. - -The time taken to scan the young generation can be controlled by controlling its -size, but the size of the old generation cannot be controlled. -In order to keep pause times down, scanning of the old generation of the heap -occurs in increments. - -To keep track of what has been scanned, the old generation contains two lists: - -* Those objects that have not yet been scanned, referred to as the `pending` list. -* Those objects that have been scanned, referred to as the `visited` list. - -To detect and collect all unreachable objects in the heap, the garbage collector -must scan the whole heap. This whole heap scan is called a full scavenge. - -Increments ----------- - -Each full scavenge is performed in a series of increments. -For each full scavenge, the combined increments will cover the whole heap. - -Each increment is made up of: - -* The young generation -* The old generation's least recently scanned objects -* All objects reachable from those objects that have not yet been scanned this full scavenge - -The surviving objects (those that are not collected) are moved to the back of the -`visited` list in the old generation. - -When a full scavenge starts, no objects in the heap are considered to have been scanned, -so all objects in the old generation must be in the `pending` space. -When all objects in the heap have been scanned a cycle ends, and all objects are moved -to the `pending` list again. To avoid having to traverse the entire list, which list is -`pending` and which is `visited` is determined by a field in the `GCState` struct. -The `visited` and `pending` lists can be swapped by toggling this bit. - -Correctness ------------ - -The [algorithm for identifying cycles](#Identifying-reference-cycles) will find all -unreachable cycles in a list of objects, but will not find any cycles that are -even partly outside of that list. -Therefore, to be guaranteed that a full scavenge will find all unreachable cycles, -each cycle must be fully contained within a single increment. - -To make sure that no partial cycles are included in the increment we perform a -[transitive closure](https://en.wikipedia.org/wiki/Transitive_closure) -over reachable, unscanned objects from the initial increment. -Since the transitive closure of objects reachable from an object must be a (non-strict) -superset of any unreachable cycle including that object, we are guaranteed that a -transitive closure cannot contain any partial cycles. -We can exclude scanned objects, as they must have been reachable when scanned. -If a scanned object becomes part of an unreachable cycle after being scanned, it will -not be collected at this time, but it will be collected in the next full scavenge. +three spaces/generations. Every new +object starts in the first generation (generation 0). The previous algorithm is +executed only over the objects of a particular generation and if an object +survives a collection of its generation it will be moved to the next one +(generation 1), where it will be surveyed for collection less often. If +the same object survives another GC round in this new generation (generation 1) +it will be moved to the last generation (generation 2) where it will be +surveyed the least often. > [!NOTE] -> The GC implementation for the free-threaded build does not use incremental collection. -> Every collection operates on the entire heap. +> The GC implementation for the free-threaded build does not use generational +> collection. Every collection operates on the entire heap. + In order to decide when to run, the collector keeps track of the number of object allocations and deallocations since the last collection. When the number of allocations minus the number of deallocations exceeds `threshold0`, -collection starts. `threshold1` determines the fraction of the old -collection that is included in the increment. -The fraction is inversely proportional to `threshold1`, -as historically a larger `threshold1` meant that old generation -collections were performed less frequently. -`threshold2` is ignored. +collection starts. Initially only generation 0 is examined. If generation 0 has +been examined more than `threshold_1` times since generation 1 has been +examined, then generation 1 is examined as well. With generation 2, +things are a bit more complicated; see +[Collecting the oldest generation](#Collecting-the-oldest-generation) for +more information. These thresholds can be examined using the [`gc.get_threshold()`](https://docs.python.org/3/library/gc.html#gc.get_threshold) @@ -440,7 +404,7 @@ function: ```pycon >>> import gc >>> gc.get_threshold() -(700, 10, 10) +(2000, 10, 10) ``` The content of these generations can be examined using the @@ -453,84 +417,61 @@ specifically in a generation by calling `gc.collect(generation=NUM)`. ... pass ... >>> # Move everything to the old generation so it's easier to inspect ->>> # the young generation. +>>> # the younger generation. >>> gc.collect() 0 >>> # Create a reference cycle. >>> x = MyObj() >>> x.self = x ->>> ->>> # Initially the object is in the young generation. +>>> +>>> # Initially the object is in the youngest generation. >>> gc.get_objects(generation=0) [..., <__main__.MyObj object at 0x7fbcc12a3400>, ...] ->>> +>>> >>> # After a collection of the youngest generation the object ->>> # moves to the old generation. +>>> # moves to the next generation. >>> gc.collect(generation=0) 0 >>> gc.get_objects(generation=0) [] >>> gc.get_objects(generation=1) -[] ->>> gc.get_objects(generation=2) [..., <__main__.MyObj object at 0x7fbcc12a3400>, ...] ``` +Collecting the oldest generation +-------------------------------- + +In addition to the various configurable thresholds, the GC only triggers a full +collection of the oldest generation if the ratio `long_lived_pending / long_lived_total` +is above a given value (hardwired to 25%). The reason is that, while "non-full" +collections (that is, collections of the young and middle generations) will always +examine roughly the same number of objects (determined by the aforementioned +thresholds) the cost of a full collection is proportional to the total +number of long-lived objects, which is virtually unbounded. Indeed, it has +been remarked that doing a full collection every of object +creations entails a dramatic performance degradation in workloads which consist +of creating and storing lots of long-lived objects (for example, building a large list +of GC-tracked objects would show quadratic performance, instead of linear as +expected). Using the above ratio, instead, yields amortized linear performance +in the total number of objects (the effect of which can be summarized thusly: +"each full garbage collection is more and more costly as the number of objects +grows, but we do fewer and fewer of them"). + Optimization: excluding reachable objects ========================================= An object cannot be garbage if it can be reached. To avoid having to identify -reference cycles across the whole heap, we can reduce the amount of work done -considerably by first identifying objects reachable from objects known to be -alive. These objects are excluded from the normal cyclic detection process. - -The default and free-threaded build both implement this optimization but in -slightly different ways. - -Finding reachable objects for the default build GC --------------------------------------------------- - -This works by first moving most reachable objects to the `visited` space. -Empirically, most reachable objects can be reached from a small set of global -objects and local variables. This step does much less work per object, so -reduces the time spent performing garbage collection by at least half. - -> [!NOTE] -> Objects that are not determined to be reachable by this pass are not necessarily -> unreachable. We still need to perform the main algorithm to determine which objects -> are actually unreachable. -We use the same technique of forming a transitive closure as the incremental -collector does to find reachable objects, seeding the list with some global -objects and the currently executing frames. - -This phase moves objects to the `visited` space, as follows: - -1. All objects directly referred to by any builtin class, the `sys` module, the `builtins` -module and all objects directly referred to from stack frames are added to a working -set of reachable objects. -2. Until this working set is empty: - 1. Pop an object from the set and move it to the `visited` space - 2. For each object directly reachable from that object: - * If it is not already in `visited` space and it is a GC object, - add it to the working set - - -Before each increment of collection is performed, the stacks are scanned -to check for any new stack frames that have been created since the last -increment. All objects directly referred to from those stack frames are -added to the working set. -Then the above algorithm is repeated, starting from step 2. - +reference cycles across the whole heap, the free-threaded build first identifies +objects reachable from objects known to be alive. These objects are excluded +from the normal cyclic detection process. Finding reachable objects for the free-threaded GC -------------------------------------------------- Within the `gc_free_threading.c` implementation, this is known as the "mark -alive" pass or phase. It is similar in concept to what is done for the default -build GC. Rather than moving objects between double-linked lists, the -free-threaded GC uses a flag in `ob_gc_bits` to track if an object is -found to be definitely alive (not garbage). +alive" pass or phase. The free-threaded GC uses a flag in `ob_gc_bits` to track +if an object is found to be definitely alive (not garbage). To find objects reachable from known alive objects, known as the "roots", the `gc_mark_alive_from_roots()` function is used. Root objects include @@ -761,6 +702,14 @@ tuples can be untracked. A tuple can be untracked if all of its contents are already not tracked. Tuples are examined for untracking in all garbage collection cycles. +Dictionaries are always tracked from creation and are not untracked by the +garbage collector. Earlier versions (up to 3.13) used lazy tracking: empty or +atomic-only dicts were untracked on creation and re-tracked when a trackable +value was inserted (via `MAINTAIN_TRACKING`), and full collections called +`_PyDict_MaybeUntrack` to prune dicts whose values had become atomic. That +machinery was removed in 3.14 (GH-127010) because the per-set-item cost of +checking the tracking invariant outweighed the savings on full collections. + The garbage collector module provides the Python function `is_tracked(obj)`, which returns the current tracking status of the object. Subsequent garbage collections may change the tracking status of the object. diff --git a/InternalDocs/generators.md b/InternalDocs/generators.md index 979a5b51521e4e..fa652cd231cfa9 100644 --- a/InternalDocs/generators.md +++ b/InternalDocs/generators.md @@ -64,7 +64,7 @@ Iteration The [`FOR_ITER`](https://docs.python.org/dev/library/dis.html#opcode-FOR_ITER) instruction calls `__next__` on the iterator which is on the top of the stack, -and pushes the result to the stack. It has [`specializations`](adaptive.md) +and pushes the result to the stack. It has [`specializations`](interpreter.md) for a few common iterator types, including `FOR_ITER_GEN`, for iterating over a generator. `FOR_ITER_GEN` bypasses the call to `__next__`, and instead directly pushes the generator stack and resumes its execution from the diff --git a/InternalDocs/interpreter.md b/InternalDocs/interpreter.md index 38e9f6fced6088..7fc41a807dd566 100644 --- a/InternalDocs/interpreter.md +++ b/InternalDocs/interpreter.md @@ -226,10 +226,11 @@ Up through 3.10, the call stack was implemented as a singly-linked list of heap allocation for the stack frame. Since 3.11, frames are no longer fully-fledged objects. Instead, a leaner internal -`_PyInterpreterFrame` structure is used, which is allocated using a custom allocator -function (`_PyThreadState_BumpFramePointer()`), which allocates and initializes a -frame structure. Usually a frame allocation is just a pointer bump, which improves -memory locality. +`_PyInterpreterFrame` structure is used. Most frames are allocated contiguously in a +per-thread stack (see `_PyThreadState_PushFrame` in [Python/pystate.c](../Python/pystate.c)), +which improves memory locality and reduces overhead. +If the current `datastack_chunk` has enough space (`_PyThreadState_HasStackSpace`) +then the lightweight `_PyFrame_PushUnchecked` can be used instead of `_PyThreadState_PushFrame`. Sometimes an actual `PyFrameObject` is needed, such as when Python code calls `sys._getframe()` or an extension module calls @@ -506,6 +507,38 @@ After the last `DEOPT_IF` has passed, a hit should be recorded with After an optimization has been deferred in the adaptive instruction, that should be recorded with `STAT_INC(BASE_INSTRUCTION, deferred)`. +## Interpreter types +There are three different types of interpreters to choose from based on compiler support: + + * traditional switch-case interpreter + + Supported by all compilers covered in PEP 7. + + * computed-gotos interpreter + + Enabled using configure option `--with-computed-gotos` and used by default on supported compilers. + It uses [Labels as Values](https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html) + for more efficient dispatching. + + * tail-calling interpreter + + Enabled using configure option `--with-tail-call-interp` (or `--tail-call-interp` for build.bat on Windows). + It uses [tail calls](https://clang.llvm.org/docs/AttributeReference.html#musttail) and the + [preserve_none](https://clang.llvm.org/docs/AttributeReference.html#preserve-none) + calling convention between the small C functions that implement individual Python opcodes. + + Not all compilers support these and if they do not all targets might be supported (for example, + MSVC currently only supports x64 and only in optimized builds). + + In addition, compilers must do [escape analysis](https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-musttail) + of the lifetimes of automatic variables, function parameters, and temporaries to ensure proper tail-calls. They + emit a compile error in case of a violation or detection failure. The ability to detect this varies depending on the compiler and + also on the optimization level. Following techniques are particularly helpful to the MSVC compiler in this regard + * [Introducing additional scopes](https://github.com/python/cpython/blob/3908593039bde9d4b591ab09919003ee57418d64/Python/bytecodes.c#L2526) + * [extracting problematic code paths into a separate function](https://github.com/python/cpython/pull/143068/files#diff-729a985b0cb8b431cb291f1edb561bbbfea22e3f8c262451cd83328a0936a342R3724) + * [returning a pointer instead of taking it as an output parameter](https://github.com/python/cpython/blob/3908593039bde9d4b591ab09919003ee57418d64/Include/internal/pycore_ceval.h#L489-L492) + + Using `restrict` is another (currently unused) remedy. Additional resources -------------------- diff --git a/InternalDocs/jit.md b/InternalDocs/jit.md index c98ccb3ace8d9e..1345f2db8b34db 100644 --- a/InternalDocs/jit.md +++ b/InternalDocs/jit.md @@ -11,24 +11,54 @@ and this enables optimizations that span multiple instructions. Historically, the adaptive interpreter was referred to as `tier 1` and the JIT as `tier 2`. You will see remnants of this in the code. -## The Optimizer and Executors +## The Trace Recorder and Executors -The program begins running on the adaptive interpreter, until a `JUMP_BACKWARD` -instruction determines that it is "hot" because the counter in its +There are two interpreters in this section: + 1. Adaptive interpreter (the default behavior) + 2. Trace recording interpreter (enabled on JIT builds) + +The program begins running on the adaptive interpreter, until a `JUMP_BACKWARD` or +`RESUME` instruction determines that it is "hot" because the counter in its [inline cache](interpreter.md#inline-cache-entries) indicates that it executed more than some threshold number of times (see [`backoff_counter_triggers`](../Include/internal/pycore_backoff.h)). -It then calls the function `_PyOptimizer_Optimize()` in +It then calls the function `_PyJit_TryInitializeTracing` in [`Python/optimizer.c`](../Python/optimizer.c), passing it the current -[frame](frames.md) and instruction pointer. `_PyOptimizer_Optimize()` -constructs an object of type -[`_PyExecutorObject`](../Include/internal/pycore_optimizer.h) which implements -an optimized version of the instruction trace beginning at this jump. - -The optimizer determines where the trace ends, and the executor is set up +[frame](frames.md), instruction pointer and state. +The interpreter then switches into "tracing mode" via the macro +`ENTER_TRACING()`. On platforms that support computed goto and tail-calling +interpreters, the dispatch table is swapped out, while other platforms that do +not support either use a single flag in the opcode. +Execution between the normal interpreter and tracing interpreter are +interleaved via this dispatch mechanism. This means that while logically +there are two interpreters, the implementation appears to be a single +interpreter. + +During tracing mode, after each interpreter instruction's `DISPATCH()`, +the interpreter jumps to the `TRACE_RECORD` instruction. This instruction +records the previous instruction executed and also any live values of the next +operation it may require. It then translates the previous instruction to +a sequence of micro-ops using `_PyJit_translate_single_bytecode_to_trace`. +To ensure that the adaptive interpreter instructions +and cache entries are up-to-date, the trace recording interpreter always resets +the adaptive counters of adaptive instructions it sees. +This forces a re-specialization of any new instruction should an instruction +deoptimize. Thus, feeding the trace recorder up-to-date information. +Finally, the `TRACE_RECORD` instruction decides when to stop tracing +using various heuristics. + +Once trace recording concludes, `LEAVE_TRACING()` swaps out the dispatch +table/the opcode flag set earlier by `ENTER_TRACING()` is unset. +`stop_tracing_and_jit()` then calls `_PyOptimizer_Optimize()` which optimizes +the trace and constructs an +[`_PyExecutorObject`](../Include/internal/pycore_optimizer.h). + +JIT execution is set up to either return to the adaptive interpreter and resume execution, or transfer control to another executor (see `_PyExitData` in -Include/internal/pycore_optimizer.h). +Include/internal/pycore_optimizer.h). When resuming to the adaptive interpreter, +a "side exit", generated by an `EXIT_IF` may trigger recording of another trace. +While a "deopt", generated by a `DEOPT_IF`, does not trigger recording. The executor is stored on the [`code object`](code_objects.md) of the frame, in the `co_executors` field which is an array of executors. The start @@ -40,12 +70,7 @@ executor in `co_executors`. The micro-op (abbreviated `uop` to approximate `μop`) optimizer is defined in [`Python/optimizer.c`](../Python/optimizer.c) as `_PyOptimizer_Optimize`. -It translates an instruction trace into a sequence of micro-ops by replacing -each bytecode by an equivalent sequence of micro-ops (see -`_PyOpcode_macro_expansion` in -[pycore_opcode_metadata.h](../Include/internal/pycore_opcode_metadata.h) -which is generated from [`Python/bytecodes.c`](../Python/bytecodes.c)). -The micro-op sequence is then optimized by +It takes a micro-op sequence from the trace recorder and optimizes with `_Py_uop_analyze_and_optimize` in [`Python/optimizer_analysis.c`](../Python/optimizer_analysis.c) and an instance of `_PyUOpExecutor_Type` is created to contain it. @@ -53,7 +78,7 @@ and an instance of `_PyUOpExecutor_Type` is created to contain it. ## The JIT interpreter After a `JUMP_BACKWARD` instruction invokes the uop optimizer to create a uop -executor, it transfers control to this executor via the `GOTO_TIER_TWO` macro. +executor, it transfers control to this executor via the `TIER1_TO_TIER2` macro. CPython implements two executors. Here we describe the JIT interpreter, which is the simpler of them and is therefore useful for debugging and analyzing @@ -78,8 +103,8 @@ and execution returns to the adaptive interpreter. ## Invalidating Executors In addition to being stored on the code object, each executor is also -inserted into a list of all executors, which is stored in the interpreter -state's `executor_list_head` field. This list is used when it is necessary +inserted into contiguous arrays (`executor_blooms` and `executor_ptrs`) +stored in the interpreter state. These arrays are used when it is necessary to invalidate executors because values they used in their construction may have changed. @@ -89,7 +114,7 @@ When the full jit is enabled (python was configured with [`--enable-experimental-jit`](https://docs.python.org/dev/using/configure.html#cmdoption-enable-experimental-jit), the uop executor's `jit_code` field is populated with a pointer to a compiled C function that implements the executor logic. This function's signature is -defined by `jit_func` in [`pycore_jit.h`](Include/internal/pycore_jit.h). +defined by `jit_func` in [`pycore_jit.h`](../Include/internal/pycore_jit.h). When the executor is invoked by `ENTER_EXECUTOR`, instead of jumping to the uop interpreter at `tier2_dispatch`, the executor runs the function that `jit_code` points to. This function returns the instruction pointer diff --git a/InternalDocs/parser.md b/InternalDocs/parser.md index 1d0ffe6d40d078..1bb4cdea5439f1 100644 --- a/InternalDocs/parser.md +++ b/InternalDocs/parser.md @@ -819,6 +819,13 @@ directory on the CPython repository and manually call the parser generator by ex $ python -m pegen python ``` +> [!CAUTION] +> Python's grammar (the `Grammar/python.gram` file) is written for the +> C backend. To experiment, you will need to write a grammar +> without C-specific parts like actions and the trailer. +> See [#133560](https://github.com/python/cpython/issues/133560) +> and [#96424](https://github.com/python/cpython/issues/96424) for more information. + This will generate a file called `parse.py` in the same directory that you can use to parse some input: diff --git a/InternalDocs/profiling_binary_format.md b/InternalDocs/profiling_binary_format.md new file mode 100644 index 00000000000000..7e4592a0d89705 --- /dev/null +++ b/InternalDocs/profiling_binary_format.md @@ -0,0 +1,541 @@ +# Profiling Binary Format + +The profiling module includes a binary file format for storing sampling +profiler data. This document describes the format's structure and the +design decisions behind it. + +The implementation is in +[`Modules/_remote_debugging/binary_io_writer.c`](../Modules/_remote_debugging/binary_io_writer.c) +and [`Modules/_remote_debugging/binary_io_reader.c`](../Modules/_remote_debugging/binary_io_reader.c), +with declarations in +[`Modules/_remote_debugging/binary_io.h`](../Modules/_remote_debugging/binary_io.h). + +## Overview + +The sampling profiler can generate enormous amounts of data. A typical +profiling session sampling at 1000 Hz for 60 seconds produces 60,000 samples. +Each sample contains a full call stack, often 20-50 frames deep, and each +frame includes a filename, function name, and line number. In a text-based +format like collapsed stacks, this would mean repeating the same long file +paths and function names thousands of times. + +The binary format addresses this through two key strategies: + +1. **Deduplication**: Strings and frames are stored once in lookup tables, + then referenced by small integer indices. A 100-character file path that + appears in 50,000 samples is stored once, not 50,000 times. + +2. **Compact encoding**: Variable-length integers (varints) encode small + values in fewer bytes. Since most indices are small (under 128), they + typically need only one byte instead of four. + +Together with optional zstd compression, these techniques reduce file sizes +by 10-50x compared to text formats while also enabling faster I/O. + +## File Layout + +The file consists of five sections: + +``` ++------------------+ Offset 0 +| Header | 64 bytes (fixed) ++------------------+ Offset 64 +| | +| Sample Data | Variable size (optionally compressed) +| | ++------------------+ string_table_offset +| String Table | Variable size ++------------------+ frame_table_offset +| Frame Table | Variable size ++------------------+ file_size - 32 +| Footer | 32 bytes (fixed) ++------------------+ file_size +``` + +The layout is designed for streaming writes during profiling. The profiler +cannot know in advance how many unique strings or frames will be encountered, +so these tables must be built incrementally and written at the end. + +The header comes first so readers can quickly validate the file and locate +the metadata tables. The sample data follows immediately, allowing the writer +to stream samples directly to disk (or through a compression stream) without +buffering the entire dataset in memory. + +The string and frame tables are placed after sample data because they grow +as new unique entries are discovered during profiling. By deferring their +output until finalization, the writer avoids the complexity of reserving +space or rewriting portions of the file. + +The footer at the end contains counts needed to allocate arrays before +parsing the tables. Placing it at a fixed offset from the end (rather than +at a variable offset recorded in the header) means readers can locate it +with a single seek to `file_size - 32`, without first reading the header. + +## Header + +``` + Offset Size Type Description ++--------+------+---------+----------------------------------------+ +| 0 | 4 | uint32 | Magic number (0x54414348 = "TACH") | +| 4 | 4 | uint32 | Format version | +| 8 | 4 | bytes | Python version (major, minor, micro, | +| | | | reserved) | +| 12 | 8 | uint64 | Start timestamp (microseconds) | +| 20 | 8 | uint64 | Sample interval (microseconds) | +| 28 | 4 | uint32 | Total sample count | +| 32 | 4 | uint32 | Thread count | +| 36 | 8 | uint64 | String table offset | +| 44 | 8 | uint64 | Frame table offset | +| 52 | 4 | uint32 | Compression type (0=none, 1=zstd) | +| 56 | 8 | bytes | Reserved (zero-filled) | ++--------+------+---------+----------------------------------------+ +``` + +The magic number `0x54414348` ("TACH" for Tachyon) identifies the file format +and also serves as an **endianness marker**. When read on a system with +different byte order than the writer, it appears as `0x48434154`. The reader +uses this to detect cross-endian files and automatically byte-swap all +multi-byte integer fields. + +The Python version field records the major, minor, and micro version numbers +of the Python interpreter that generated the file. This allows analysis tools +to detect version mismatches when replaying data collected on a different +Python version, which may have different internal structures or behaviors. + +The header is written as zeros initially, then overwritten with actual values +during finalization. This requires the output stream to be seekable, which +is acceptable since the format targets regular files rather than pipes or +network streams. + +## Sample Data + +Sample data begins at offset 64 and extends to `string_table_offset`. Samples +use delta compression to minimize redundancy when consecutive samples from the +same thread have identical or similar call stacks. + +### Stack Encoding Types + +Each sample record begins with thread identification, then an encoding byte: + +| Code | Name | Description | +|------|------|-------------| +| 0x00 | REPEAT | RLE: identical stack repeated N times | +| 0x01 | FULL | Complete stack (first sample or no match) | +| 0x02 | SUFFIX | Shares N frames from bottom of previous stack | +| 0x03 | POP_PUSH | Remove M frames from top, add N new frames | + +### Record Formats + +**REPEAT (0x00) - Run-Length Encoded Identical Stacks:** +``` ++-----------------+-----------+----------------------------------------+ +| thread_id | 8 bytes | Thread identifier (uint64, fixed) | +| interpreter_id | 4 bytes | Interpreter ID (uint32, fixed) | +| encoding | 1 byte | 0x00 (REPEAT) | +| count | varint | Number of samples in this RLE group | +| samples | varies | Interleaved: [delta: varint, status: 1]| +| | | repeated count times | ++-----------------+-----------+----------------------------------------+ +``` +The stack is inherited from this thread's previous sample. Each sample in the +group gets its own timestamp delta and status byte, stored as interleaved pairs +(delta1, status1, delta2, status2, ...) rather than separate arrays. + +**FULL (0x01) - Complete Stack:** +``` ++-----------------+-----------+----------------------------------------+ +| thread_id | 8 bytes | Thread identifier (uint64, fixed) | +| interpreter_id | 4 bytes | Interpreter ID (uint32, fixed) | +| encoding | 1 byte | 0x01 (FULL) | +| timestamp_delta | varint | Microseconds since thread's last sample| +| status | 1 byte | Thread state flags | +| stack_depth | varint | Number of frames in call stack | +| frame_indices | varint[] | Array of frame table indices | ++-----------------+-----------+----------------------------------------+ +``` +Used for the first sample from a thread, or when delta encoding would not +provide savings. + +**SUFFIX (0x02) - Shared Suffix Match:** +``` ++-----------------+-----------+----------------------------------------+ +| thread_id | 8 bytes | Thread identifier (uint64, fixed) | +| interpreter_id | 4 bytes | Interpreter ID (uint32, fixed) | +| encoding | 1 byte | 0x02 (SUFFIX) | +| timestamp_delta | varint | Microseconds since thread's last sample| +| status | 1 byte | Thread state flags | +| shared_count | varint | Frames shared from bottom of prev stack| +| new_count | varint | New frames at top of stack | +| new_frames | varint[] | Array of new_count frame indices | ++-----------------+-----------+----------------------------------------+ +``` +Used when a function call added frames to the top of the stack. The shared +frames from the previous stack are kept, and new frames are prepended. + +**POP_PUSH (0x03) - Pop and Push:** +``` ++-----------------+-----------+----------------------------------------+ +| thread_id | 8 bytes | Thread identifier (uint64, fixed) | +| interpreter_id | 4 bytes | Interpreter ID (uint32, fixed) | +| encoding | 1 byte | 0x03 (POP_PUSH) | +| timestamp_delta | varint | Microseconds since thread's last sample| +| status | 1 byte | Thread state flags | +| pop_count | varint | Frames to remove from top of prev stack| +| push_count | varint | New frames to add at top | +| new_frames | varint[] | Array of push_count frame indices | ++-----------------+-----------+----------------------------------------+ +``` +Used when the code path changed: some frames were popped (function returns) +and new frames were pushed (different function calls). + +### Thread and Interpreter Identification + +Thread IDs are 64-bit values that can be large (memory addresses on some +platforms) and vary unpredictably. Using a fixed 8-byte encoding avoids +the overhead of varint encoding for large values and simplifies parsing +since the reader knows exactly where each field begins. + +The interpreter ID identifies which Python sub-interpreter the thread +belongs to, allowing analysis tools to separate activity across interpreters +in processes using multiple sub-interpreters. + +### Status Byte + +The status byte is a bitfield encoding thread state at sample time: + +| Bit | Flag | Meaning | +|-----|-----------------------|--------------------------------------------| +| 0 | THREAD_STATUS_HAS_GIL | Thread holds the GIL (Global Interpreter Lock) | +| 1 | THREAD_STATUS_ON_CPU | Thread is actively running on a CPU core | +| 2 | THREAD_STATUS_UNKNOWN | Thread state could not be determined | +| 3 | THREAD_STATUS_GIL_REQUESTED | Thread is waiting to acquire the GIL | +| 4 | THREAD_STATUS_HAS_EXCEPTION | Thread has a pending exception | + +Multiple flags can be set simultaneously (e.g., a thread can hold the GIL +while also running on CPU). Analysis tools use these to filter samples or +visualize thread states over time. + +### Timestamp Delta Encoding + +Timestamps use delta encoding rather than absolute values. Absolute +timestamps in microseconds require 8 bytes each, but consecutive samples +from the same thread are typically separated by the sampling interval +(e.g., 1000 microseconds), so the delta between them is small and fits +in 1-2 varint bytes. The writer tracks the previous timestamp for each +thread separately. The first sample from a thread encodes its delta from +the profiling start time; subsequent samples encode the delta from that +thread's previous sample. This per-thread tracking is necessary because +samples are interleaved across threads in arrival order, not grouped by +thread. + +For REPEAT (RLE) records, timestamp deltas and status bytes are stored as +interleaved pairs (delta, status, delta, status, ...) - one pair per +repeated sample - allowing efficient batching while preserving the exact +timing and state of each sample. + +### Frame Indexing + +Each frame in a call stack is represented by an index into the frame table +rather than inline data. This provides massive space savings because call +stacks are highly repetitive: the same function appears in many samples +(hot functions), call stacks often share common prefixes (main -> app -> +handler -> ...), and recursive functions create repeated frame sequences. +A frame index is typically 1-2 varint bytes. Inline frame data would be +20-200+ bytes (two strings plus a line number). For a profile with 100,000 +samples averaging 30 frames each, this reduces frame data from potentially +gigabytes to tens of megabytes. + +Frame indices are written innermost-first (the currently executing frame +has index 0 in the array). This ordering works well with delta compression: +function calls typically add frames at the top (index 0), while shared +frames remain at the bottom. + +## String Table + +The string table stores deduplicated UTF-8 strings (filenames and function +names). It begins at `string_table_offset` and contains entries in order of +their assignment during writing: + +``` ++----------------+ +| length: varint | +| data: bytes | ++----------------+ (repeated for each string) +``` + +Strings are stored in the order they were first encountered during writing. +The first unique filename gets index 0, the second gets index 1, and so on. +Length-prefixing (rather than null-termination) allows strings containing +null bytes and enables readers to allocate exact-sized buffers. The varint +length encoding means short strings (under 128 bytes) need only one length +byte. + +## Frame Table + +The frame table stores deduplicated frame entries with full source position +information and bytecode opcode: + +``` ++----------------------------+ +| filename_idx: varint | +| funcname_idx: varint | +| lineno: svarint | +| end_lineno_delta: svarint | +| column: svarint | +| end_column_delta: svarint | +| opcode: u8 | ++----------------------------+ (repeated for each frame) +``` + +### Field Definitions + +| Field | Type | Description | +|------------------|---------------|----------------------------------------------------------| +| filename_idx | varint | Index into string table for file name | +| funcname_idx | varint | Index into string table for function name | +| lineno | zigzag varint | Start line number (-1 for synthetic frames) | +| end_lineno_delta | zigzag varint | Delta from lineno (end_lineno = lineno + delta) | +| column | zigzag varint | Start column offset in UTF-8 bytes (-1 if not available) | +| end_column_delta | zigzag varint | Delta from column (end_column = column + delta) | +| opcode | u8 | Python bytecode opcode (0-254) or 255 for None | + +### Delta Encoding + +Position end values use delta encoding for efficiency: + +- `end_lineno = lineno + end_lineno_delta` +- `end_column = column + end_column_delta` + +Typical values: +- `end_lineno_delta`: Usually 0 (single-line expressions) → encodes to 1 byte +- `end_column_delta`: Usually 5-20 (expression width) → encodes to 1 byte + +This saves ~1-2 bytes per frame compared to absolute encoding. When the base +value (lineno or column) is -1 (not available), the delta is stored as 0 and +the reconstructed value is -1. + +### Sentinel Values + +- `opcode = 255`: No opcode captured +- `lineno = -1`: Synthetic frame (no source location) +- `column = -1`: Column offset not available + +### Deduplication + +Each unique (filename, funcname, lineno, end_lineno, column, end_column, +opcode) combination gets one entry. This enables instruction-level profiling +where multiple bytecode instructions on the same line can be distinguished. + +Strings and frames are deduplicated separately because they have different +cardinalities and reference patterns. A codebase might have hundreds of +unique source files but thousands of unique functions. Many functions share +the same filename, so storing the filename index in each frame entry (rather +than the full string) provides an additional layer of deduplication. A frame +entry is typically 7-9 bytes rather than two full strings plus location data. + +### Size Analysis + +Typical frame size with delta encoding: +- file_idx: 1-2 bytes +- func_idx: 1-2 bytes +- lineno: 1-2 bytes +- end_lineno_delta: 1 byte (usually 0) +- column: 1 byte (usually < 64) +- end_column_delta: 1 byte (usually < 64) +- opcode: 1 byte + +**Total: ~7-9 bytes per frame** + +Line numbers and columns use signed varint (zigzag encoding) to handle +sentinel values efficiently. Synthetic frames—generated frames that don't +correspond directly to Python source code, such as C extension boundaries or +internal interpreter frames—use -1 to indicate the absence of a source +location. Zigzag encoding ensures these small negative values encode +efficiently (−1 becomes 1, which is one byte) rather than requiring the +maximum varint length. + +## Footer + +``` + Offset Size Type Description ++--------+------+---------+----------------------------------------+ +| 0 | 4 | uint32 | String count | +| 4 | 4 | uint32 | Frame count | +| 8 | 8 | uint64 | Total file size | +| 16 | 16 | bytes | Checksum (reserved, currently zeros) | ++--------+------+---------+----------------------------------------+ +``` + +The string and frame counts allow readers to pre-allocate arrays of the +correct size before parsing the tables. Without these counts, readers would +need to either scan the tables twice (once to count, once to parse) or use +dynamically-growing arrays. + +The file size field provides a consistency check: if the actual file size +does not match, the file may be truncated or corrupted. + +The checksum field is reserved for future use. A checksum would allow +detection of corruption but adds complexity and computation cost. The +current implementation leaves this as zeros. + +## Variable-Length Integer Encoding + +The format uses LEB128 (Little Endian Base 128) for unsigned integers and +zigzag + LEB128 for signed integers. These encodings are widely used +(Protocol Buffers, DWARF debug info, WebAssembly) and well-understood. + +### Unsigned Varint (LEB128) + +Each byte stores 7 bits of data. The high bit indicates whether more bytes +follow: + +``` +Value Encoded bytes +0-127 [0xxxxxxx] (1 byte) +128-16383 [1xxxxxxx] [0xxxxxxx] (2 bytes) +16384+ [1xxxxxxx] [1xxxxxxx] ... (3+ bytes) +``` + +Most indices in profiling data are small. A profile with 1000 unique frames +needs at most 2 bytes per frame index. The common case (indices under 128) +needs only 1 byte. + +### Signed Varint (Zigzag) + +Standard LEB128 encodes −1 as a very large unsigned value, requiring many +bytes. Zigzag encoding interleaves positive and negative values: + +``` + 0 -> 0 -1 -> 1 1 -> 2 -2 -> 3 2 -> 4 +``` + +This ensures small-magnitude values (whether positive or negative) encode +in few bytes. + +## Compression + +When compression is enabled, the sample data region contains a zstd stream. +The string table, frame table, and footer remain uncompressed so readers can +access metadata without decompressing the entire file. A tool that only needs +to report "this file contains 50,000 samples of 3 threads" can read the header +and footer without touching the compressed sample data. This also simplifies +the format: the header's offset fields point directly to the tables rather +than to positions within a decompressed stream. + +Zstd provides an excellent balance of compression ratio and speed. Profiling +data compresses very well (often 5-10x) due to repetitive patterns: the same +small set of frame indices appears repeatedly, and delta-encoded timestamps +cluster around the sampling interval. Zstd's streaming API allows compression +without buffering the entire dataset. The writer feeds sample data through +the compressor incrementally, flushing compressed chunks to disk as they +become available. + +Level 5 compression is used as a default. Lower levels (1-3) are faster but +compress less; higher levels (6+) compress more but slow down writing. Level +5 provides good compression with minimal impact on profiling overhead. + +## Reading and Writing + +### Writing + +1. Open the output file and write 64 zero bytes as a placeholder header +2. Initialize empty string and frame dictionaries for deduplication +3. For each sample: + - Intern any new strings, assigning sequential indices + - Intern any new frames, assigning sequential indices + - Encode the sample record and write to the buffer + - Flush the buffer through compression (if enabled) when full +4. Flush remaining buffered data and finalize compression +5. Write the string table (length-prefixed strings in index order) +6. Write the frame table (varint-encoded entries in index order) +7. Write the footer with final counts +8. Seek to offset 0 and write the header with actual values + +The writer maintains two dictionaries: one mapping strings to indices, one +mapping (filename_idx, funcname_idx, lineno) tuples to frame indices. These +enable O(1) lookup during interning. + +### Reading + +1. Read the header magic number to detect endianness (set `needs_swap` flag + if the magic appears byte-swapped) +2. Validate version and read remaining header fields (byte-swapping if needed) +3. Seek to end − 32 and read the footer (byte-swapping counts if needed) +4. Allocate string array of `string_count` elements +5. Parse the string table, populating the array +6. Allocate frame array of `frame_count * 3` uint32 elements +7. Parse the frame table, populating the array +8. If compressed, decompress the sample data region +9. Iterate through samples, resolving indices to strings/frames + (byte-swapping thread_id and interpreter_id if needed) + +The reader builds lookup arrays rather than dictionaries since it only needs +index-to-value mapping, not value-to-index. + +## Platform Considerations + +### Byte Ordering and Cross-Platform Portability + +The binary format uses **native byte order** for all multi-byte integer +fields when writing. However, the reader supports **cross-endian reading**: +files written on a little-endian system (x86, ARM) can be read on a +big-endian system (s390x, PowerPC), and vice versa. + +The magic number doubles as an endianness marker. When read on a system with +different byte order, it appears byte-swapped (`0x48434154` instead of +`0x54414348`). The reader detects this and automatically byte-swaps all +fixed-width integer fields during parsing. + +Writers must use `memcpy()` from properly-sized integer types when writing +fixed-width integer fields. When the source variable's type differs from the +field width (e.g., `size_t` written as 4 bytes), explicit casting to the +correct type (e.g., `uint32_t`) is required before `memcpy()`. On big-endian +systems, copying from an oversized type would copy the wrong bytes—high-order +zeros instead of the actual value. + +The reader tracks whether byte-swapping is needed via a `needs_swap` flag set +during header parsing. All fixed-width fields in the header, footer, and +sample data are conditionally byte-swapped using Python's internal byte-swap +functions (`_Py_bswap32`, `_Py_bswap64` from `pycore_bitutils.h`). + +Variable-length integers (varints) are byte-order independent since they +encode values one byte at a time using the LEB128 scheme, so they require +no special handling for cross-endian reading. + +### Memory-Mapped I/O + +On Unix systems (Linux, macOS), the reader uses `mmap()` to map the file +into the process address space. The kernel handles paging data in and out +as needed, no explicit read() calls or buffer management are required, +multiple readers can share the same physical pages, and sequential access +patterns benefit from kernel read-ahead. + +The implementation uses `madvise()` to hint the access pattern to the kernel: +`MADV_SEQUENTIAL` indicates the file will be read linearly, enabling +aggressive read-ahead. `MADV_WILLNEED` requests pre-faulting of pages. +On Linux, `MAP_POPULATE` pre-faults all pages at mmap time rather than on +first access, moving page fault overhead from the parsing loop to the +initial mapping for more predictable performance. For large files (over +32 MB), `MADV_HUGEPAGE` requests transparent huge pages (2 MB instead of +4 KB) to reduce TLB pressure when accessing large amounts of data. + +On Windows, the implementation falls back to standard file I/O with full +file buffering. Profiling data files are typically small enough (tens to +hundreds of megabytes) that this is acceptable. + +The writer uses a 512 KB buffer to batch small writes. Each sample record +is typically tens of bytes; writing these individually would incur excessive +syscall overhead. The buffer accumulates data until full, then flushes in +one write() call (or feeds through the compression stream). + +## Future Considerations + +The format reserves space for future extensions. The 12 reserved bytes in +the header could hold additional metadata. The 16-byte checksum field in +the footer is currently unused. The version field allows incompatible +changes with graceful rejection. New compression types could be added +(compression_type > 1). + +Any changes that alter the meaning of existing fields or the parsing logic +should increment the version number to prevent older readers from +misinterpreting new files. diff --git a/InternalDocs/qsbr.md b/InternalDocs/qsbr.md index 1c4a79a7b44436..d511396fdab645 100644 --- a/InternalDocs/qsbr.md +++ b/InternalDocs/qsbr.md @@ -101,15 +101,39 @@ Periodically, a polling mechanism processes this deferred-free list: To reduce memory contention from frequent updates to the global `wr_seq`, its advancement is sometimes deferred. Instead of incrementing `wr_seq` on every -reclamation request, each thread tracks its number of deferrals locally. Once -the deferral count reaches a limit (QSBR_DEFERRED_LIMIT, currently 10), the -thread advances the global `wr_seq` and resets its local count. - -When an object is added to the deferred-free list, its qsbr_goal is set to -`wr_seq` + 2. By setting the goal to the next sequence value, we ensure it's safe -to defer the global counter advancement. This optimization improves runtime -speed but may increase peak memory usage by slightly delaying when memory can -be reclaimed. +reclamation request, the object's qsbr_goal is set to `wr_seq` + 2 (the value +the counter *would* take on its next advance) without actually advancing the +global counter. This is safe because the goal still corresponds to a future +sequence value that no thread has yet observed as quiescent. + +Whether to actually advance `wr_seq` is decided per request, based on how +much memory and how many items the calling thread has already deferred since +its last advance: + +* For deferred object frees (`_PyMem_FreeDelayed`), the thread tracks both a + count (`deferred_count`) and an estimate of the held memory + (`deferred_memory`). The global `wr_seq` is advanced when the freed block + is larger than `QSBR_FREE_MEM_LIMIT` (1 MiB), when the accumulated deferred + memory exceeds that limit, or when the count exceeds `QSBR_DEFERRED_LIMIT` + (127, sized so a chunk of work items is processed before it overflows). + Crossing any of these thresholds also sets a per-thread `should_process` + flag, signalling that the deferred-free list should be drained. + +* For mimalloc pages held by QSBR, the thread tracks `deferred_page_memory` + and advances `wr_seq` when either the individual page or the accumulated + page memory exceeds `QSBR_PAGE_MEM_LIMIT` (4096 * 20 bytes). Advancing + promptly here matters because a held page cannot be reused for a different + size class or by a different thread. + +Processing of the deferred-free list normally happens from the eval breaker +(rather than from inside `_PyMem_FreeDelayed`), which gives the global +`rd_seq` a better chance to have advanced far enough that items can actually +be freed. `_PyMem_ProcessDelayed` is still called from the free path as a +safety valve when a work-item chunk fills up. + +This optimization improves runtime speed but may increase peak memory usage +by slightly delaying when memory can be reclaimed; the size-based thresholds +above bound that extra memory. ## Limitations diff --git a/InternalDocs/stack_protection.md b/InternalDocs/stack_protection.md new file mode 100644 index 00000000000000..14802e57d095f4 --- /dev/null +++ b/InternalDocs/stack_protection.md @@ -0,0 +1,68 @@ +# Stack Protection + +CPython protects against stack overflow in the form of runaway, or just very deep, recursion by raising a `RecursionError` instead of just crashing. +Protection against pure Python stack recursion has existed since very early, but in 3.12 we added protection against stack overflow +in C code. This was initially implemented using a counter and later improved in 3.14 to use the actual stack depth. +For those platforms that support it (Windows, Mac, and most Linuxes) we query the operating system to find the stack bounds. +For other platforms we use conservative estimates. + + +The C stack looks like this: + +``` + +-------+ <--- Top of machine stack + | | + | | + + ~~ + + | | + | | + +-------+ <--- Soft limit + | | + | | _PyOS_STACK_MARGIN_BYTES + | | + +-------+ <--- Hard limit + | | + | | _PyOS_STACK_MARGIN_BYTES + | | + +-------+ <--- Bottom of machine stack +``` + + +We get the current stack pointer using compiler intrinsics where available, or by taking the address of a C local variable. See `_Py_get_machine_stack_pointer()`. + +The soft and hard limits pointers are set by calling `_Py_InitializeRecursionLimits()` during thread initialization. + +Recursion checks are performed by `_Py_EnterRecursiveCall()` or `_Py_EnterRecursiveCallTstate()` which compare the stack pointer to the soft limit. If the stack pointer is lower than the soft limit, then `_Py_CheckRecursiveCall()` is called which checks against both the hard and soft limits: + +```python +kb_used = (stack_top - stack_pointer)>>10 +if stack_pointer < bottom_of_machine_stack: + pass # Our stack limits could be wrong so it is safest to do nothing. +elif stack_pointer < hard_limit: + FatalError(f"Unrecoverable stack overflow (used {kb_used} kB)") +elif stack_pointer < soft_limit: + raise RecursionError(f"Stack overflow (used {kb_used} kB)") +``` + +### User space threads and other oddities + +Some libraries provide user-space threads. These will change the C stack at runtime. +To guard against this we only raise if the stack pointer is in the window between the expected stack base and the soft limit. + +### Diagnosing and fixing stack overflows + +For stack protection to work correctly the amount of stack consumed between calls to `_Py_EnterRecursiveCall()` must be less than `_PyOS_STACK_MARGIN_BYTES`. + +If you see a traceback ending in: `RecursionError: Stack overflow (used ... kB)` then the stack protection is working as intended. If you don't expect to see the error, then check the amount of stack used. If it seems low then CPython may not be configured properly. + +However, if you see a fatal error or crash, then something is not right. +Either a recursive call is not checking `_Py_EnterRecursiveCall()`, or the amount of C stack consumed by a single call exceeds `_PyOS_STACK_MARGIN_BYTES`. If a hard crash occurs, it probably means that the amount of C stack consumed is more than double `_PyOS_STACK_MARGIN_BYTES`. + +Likely causes: +* Recursive code is not calling `_Py_EnterRecursiveCall()` +* `-O0` compilation flags, especially for Clang. With no optimization, C calls can consume a lot of stack space +* Giant, complex functions in third-party C extensions. This is unlikely as the function in question would need to be more complicated than the bytecode interpreter. +* `_PyOS_STACK_MARGIN_BYTES` is just too low. +* `_Py_InitializeRecursionLimits()` is not setting the soft and hard limits correctly for that platform. diff --git a/InternalDocs/stackrefs.md b/InternalDocs/stackrefs.md new file mode 100644 index 00000000000000..5774be9c56d363 --- /dev/null +++ b/InternalDocs/stackrefs.md @@ -0,0 +1,79 @@ +# Stack references (`_PyStackRef`) + +Stack references are the interpreter's tagged representation of values on the evaluation stack. +They carry metadata to track ownership and support optimizations such as tagged small ints. + +## Shape and tagging + +- A `_PyStackRef` is a tagged pointer-sized value (see `Include/internal/pycore_stackref.h`). +- Tag bits distinguish three cases: + - `Py_TAG_REFCNT` unset - reference count lives on the pointed-to object. + - `Py_TAG_REFCNT` set - ownership is "borrowed" (no refcount to drop on close) or the object is immortal. + - `Py_INT_TAG` set - tagged small integer stored directly in the stackref (no heap allocation). +- Special constants: `PyStackRef_NULL`, `PyStackRef_ERROR`, and embedded `None`/`True`/`False`. + +In GIL builds, most objects carry their refcount; tagged borrowed refs skip decref on close. In free +threading builds, the tag is also used to mark deferred refcounted objects so the GC can see them and +to avoid refcount contention on commonly shared objects. + +## Converting to and from PyObject* + +Three conversions control ownership: + +- `PyStackRef_FromPyObjectNew(obj)` - create a new reference (INCREF if mortal). +- `PyStackRef_FromPyObjectSteal(obj)` - take over ownership without changing the count unless the + object is immortal. +- `PyStackRef_FromPyObjectBorrow(obj)` - create a borrowed stackref (never decref on close). + +The `obj` argument must not be `NULL`. + +Going back to `PyObject*` mirrors this: + +- `PyStackRef_AsPyObjectBorrow(ref)` - borrow the underlying pointer +- `PyStackRef_AsPyObjectSteal(ref)` - transfer ownership from the stackref; if ref is borrowed or + deferred, this creates a new owning `PyObject*` reference. +- `PyStackRef_AsPyObjectNew(ref)` - create a new owning reference + +Only `PyStackRef_AsPyObjectBorrow` allows ref to be `PyStackRef_NULL`. + +## Operations on stackrefs + +The interpreter treats `_PyStackRef` as the unit of stack storage. Ownership must be managed with +the stackref primitives: + +- `PyStackRef_DUP` - like `Py_NewRef` for stackrefs; preserves the original. +- `PyStackRef_Borrow` - create a borrowed stackref from another stackref. +- `PyStackRef_CLOSE` / `PyStackRef_XCLOSE` - like `Py_DECREF`; invalidates the stackref. +- `PyStackRef_CLEAR` - like `Py_CLEAR`; closes and sets the stackref to `PyStackRef_NULL` +- `PyStackRef_MakeHeapSafe` - converts borrowed reference to owning reference + +Borrow tracking (for debug builds with `Py_STACKREF_DEBUG`) records who you borrowed from and reports +double-close, leaked borrows, or use-after-close via fatal errors. + +## Borrow-friendly opcodes + +The interpreter can push borrowed references directly. For example, `LOAD_FAST_BORROW` loads a local +variable as a borrowed `_PyStackRef`, avoiding both INCREF and DECREF for the temporary lifetime on +the evaluation stack. + +## Tagged integers on the stack + +Small ints can be stored inline with `Py_INT_TAG`, so no heap object is involved. Helpers like +`PyStackRef_TagInt`, `PyStackRef_UntagInt`, and `PyStackRef_IncrementTaggedIntNoOverflow` operate on +these values. Type checks use `PyStackRef_IsTaggedInt` and `PyStackRef_LongCheck`. + +## Free threading considerations + +Objects that support deferred reference counting can be pushed to the evaluation +stack and stored in local variables without directly incrementing the reference +count because they are only freed during cyclic garbage collection. This avoids +reference count contention on commonly shared objects such as methods and types. The GC +scans each thread's locals and evaluation stack to keep objects that use +deferred reference counting alive. + +## Debugging support + +`Py_STACKREF_DEBUG` builds replace the inline tags with table-backed IDs so the runtime can track +creation sites, borrows, closes, and leaks. Enabling `Py_STACKREF_CLOSE_DEBUG` additionally records +double closes. The tables live on `PyInterpreterState` and are initialized in `pystate.c`; helper +routines reside in `Python/stackrefs.c`. diff --git a/InternalDocs/string_interning.md b/InternalDocs/string_interning.md index 26a5197c6e70f3..829a27654a37d3 100644 --- a/InternalDocs/string_interning.md +++ b/InternalDocs/string_interning.md @@ -16,8 +16,8 @@ dynamic interning. The 256 possible one-character latin-1 strings, which can be retrieved with `_Py_LATIN1_CHR(c)`, are stored in statically allocated arrays, -`_PyRuntime.static_objects.strings.ascii` and -`_PyRuntime.static_objects.strings.latin1`. +`_PyRuntime.static_objects.singletons.strings.ascii` and +`_PyRuntime.static_objects.singletons.strings.latin1`. Longer singleton strings are marked in C source with `_Py_ID` (if the string is a valid C identifier fragment) or `_Py_STR` (if it needs a separate @@ -52,15 +52,9 @@ The key and value of each entry in this dict reference the same object. ## Immortality and reference counting -Invariant: Every immortal string is interned. +In the GIL-enabled build interned strings may be mortal or immortal. In the +free-threaded build, interned strings are always immortal. -In practice, this means that you must not use `_Py_SetImmortal` on -a string. (If you know it's already immortal, don't immortalize it; -if you know it's not interned you might be immortalizing a redundant copy; -if it's interned and mortal it needs extra processing in -`_PyUnicode_InternImmortal`.) - -The converse is not true: interned strings can be mortal. For mortal interned strings: - the 2 references from the interned dict (key & value) are excluded from diff --git a/InternalDocs/structure.md b/InternalDocs/structure.md new file mode 100644 index 00000000000000..75c8476aa0ad98 --- /dev/null +++ b/InternalDocs/structure.md @@ -0,0 +1,40 @@ +# CPython source code + +This section gives an overview of CPython's code structure and provides +a summary of file locations for modules and built-ins. + + +## Source code layout + +For a Python module, the typical layout is: + +* `Lib/.py` +* `Modules/_.c` (if there's also a C accelerator module) +* `Lib/test/test_.py` +* `Doc/library/.rst` + +For an extension module, the typical layout is: + +* `Modules/module.c` +* `Lib/test/test_.py` +* `Doc/library/.rst` + +For builtin types, the typical layout is: + +* `Objects/object.c` +* `Lib/test/test_.py` +* [`Doc/library/stdtypes.rst`](../Doc/library/stdtypes.rst) + +For builtin functions, the typical layout is: + +* [`Python/bltinmodule.c`](../Python/bltinmodule.c) +* [`Lib/test/test_builtin.py`](../Lib/test/test_builtin.py) +* [`Doc/library/functions.rst`](../Doc/library/functions.rst) + +Some exceptions to these layouts are: + +* built-in type `int` is at [`Objects/longobject.c`](../Objects/longobject.c) +* built-in type `str` is at [`Objects/unicodeobject.c`](../Objects/unicodeobject.c) +* built-in module `sys` is at [`Python/sysmodule.c`](../Python/sysmodule.c) +* built-in module `marshal` is at [`Python/marshal.c`](../Python/marshal.c) +* Windows-only module `winreg` is at [`PC/winreg.c`](../PC/winreg.c) diff --git a/Lib/_android_support.py b/Lib/_android_support.py index ae506f6a4b57b8..320dab52acdc0b 100644 --- a/Lib/_android_support.py +++ b/Lib/_android_support.py @@ -29,15 +29,19 @@ def init_streams(android_log_write, stdout_prio, stderr_prio): global logcat logcat = Logcat(android_log_write) - - sys.stdout = TextLogStream( - stdout_prio, "python.stdout", sys.stdout.fileno()) - sys.stderr = TextLogStream( - stderr_prio, "python.stderr", sys.stderr.fileno()) + sys.stdout = TextLogStream(stdout_prio, "python.stdout", sys.stdout) + sys.stderr = TextLogStream(stderr_prio, "python.stderr", sys.stderr) class TextLogStream(io.TextIOWrapper): - def __init__(self, prio, tag, fileno=None, **kwargs): + def __init__(self, prio, tag, original=None, **kwargs): + # Respect the -u option. + if original: + kwargs.setdefault("write_through", original.write_through) + fileno = original.fileno() + else: + fileno = None + # The default is surrogateescape for stdout and backslashreplace for # stderr, but in the context of an Android log, readability is more # important than reversibility. @@ -164,6 +168,13 @@ def write(self, prio, tag, message): # message. message = message.replace(b"\x00", b"\xc0\x80") + # On API level 30 and higher, Logcat will strip any number of leading + # newlines. This is visible in all `logcat` modes, even --binary. Work + # around this by adding a leading space, which shouldn't make any + # difference to the log's usability. + if message.startswith(b"\n"): + message = b" " + message + with self._lock: now = time() self._bucket_level += ( diff --git a/Lib/_ast_unparse.py b/Lib/_ast_unparse.py index 16cf56f62cc1e5..916bb25d74dee9 100644 --- a/Lib/_ast_unparse.py +++ b/Lib/_ast_unparse.py @@ -239,11 +239,11 @@ def visit_NamedExpr(self, node): self.traverse(node.value) def visit_Import(self, node): - self.fill("import ") + self.fill("lazy import " if node.is_lazy else "import ") self.interleave(lambda: self.write(", "), self.traverse, node.names) def visit_ImportFrom(self, node): - self.fill("from ") + self.fill("lazy from " if node.is_lazy else "from ") self.write("." * (node.level or 0)) if node.module: self.write(node.module) @@ -658,9 +658,9 @@ def _unparse_interpolation_value(self, inner): unparser.set_precedence(_Precedence.TEST.next(), inner) return unparser.visit(inner) - def _write_interpolation(self, node, is_interpolation=False): + def _write_interpolation(self, node, use_str_attr=False): with self.delimit("{", "}"): - if is_interpolation: + if use_str_attr: expr = node.str else: expr = self._unparse_interpolation_value(node.value) @@ -678,7 +678,8 @@ def visit_FormattedValue(self, node): self._write_interpolation(node) def visit_Interpolation(self, node): - self._write_interpolation(node, is_interpolation=True) + # If `str` is set to `None`, use the `value` to generate the source code. + self._write_interpolation(node, use_str_attr=node.str is not None) def visit_Name(self, node): self.write(node.id) @@ -737,9 +738,13 @@ def visit_SetComp(self, node): def visit_DictComp(self, node): with self.delimit("{", "}"): - self.traverse(node.key) - self.write(": ") - self.traverse(node.value) + if node.value: + self.traverse(node.key) + self.write(": ") + self.traverse(node.value) + else: + self.write("**") + self.traverse(node.key) for gen in node.generators: self.traverse(gen) diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py index 51263d696a1777..0e1d8ccf44a435 100644 --- a/Lib/_collections_abc.py +++ b/Lib/_collections_abc.py @@ -461,8 +461,8 @@ def __subclasshook__(cls, C): class _CallableGenericAlias(GenericAlias): """ Represent `Callable[argtypes, resulttype]`. - This sets ``__args__`` to a tuple containing the flattened ``argtypes`` - followed by ``resulttype``. + This sets ``__args__`` to a tuple containing the flattened + ``argtypes`` followed by ``resulttype``. Example: ``Callable[[int, str], float]`` sets ``__args__`` to ``(int, str, float)``. @@ -823,6 +823,7 @@ def __eq__(self, other): __reversed__ = None +Mapping.register(frozendict) Mapping.register(mappingproxy) Mapping.register(framelocalsproxy) @@ -927,8 +928,9 @@ def __delitem__(self, key): __marker = object() def pop(self, key, default=__marker): - '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. + '''D.pop(k[,d]) -> v, remove specified key and return the corresponding + value. If key is not found, d is returned if given, otherwise + KeyError is raised. ''' try: value = self[key] @@ -962,9 +964,12 @@ def clear(self): def update(self, other=(), /, **kwds): ''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F. - If E present and has a .keys() method, does: for k in E.keys(): D[k] = E[k] - If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v - In either case, this is followed by: for k, v in F.items(): D[k] = v + If E present and has a .keys() method, does: + for k in E.keys(): D[k] = E[k] + If E present and lacks .keys() method, does: + for (k, v) in E: D[k] = v + In either case, this is followed by: + for k, v in F.items(): D[k] = v ''' if isinstance(other, Mapping): for key in other: @@ -1029,8 +1034,8 @@ def __reversed__(self): yield self[i] def index(self, value, start=0, stop=None): - '''S.index(value, [start, [stop]]) -> integer -- return first index of value. - Raises ValueError if the value is not present. + '''S.index(value, [start, [stop]]) -> integer -- return first index of + value. Raises ValueError if the value is not present. Supporting start and stop arguments is optional, but recommended. @@ -1061,6 +1066,41 @@ def count(self, value): Sequence.register(range) Sequence.register(memoryview) +class _DeprecateByteStringMeta(ABCMeta): + def __new__(cls, name, bases, namespace, **kwargs): + if name != "ByteString": + import warnings + + warnings._deprecated( + "collections.abc.ByteString", + remove=(3, 17), + ) + return super().__new__(cls, name, bases, namespace, **kwargs) + + def __instancecheck__(cls, instance): + import warnings + + warnings._deprecated( + "collections.abc.ByteString", + remove=(3, 17), + ) + return super().__instancecheck__(instance) + +class ByteString(Sequence, metaclass=_DeprecateByteStringMeta): + """Deprecated ABC serving as a common supertype of ``bytes`` and ``bytearray``. + + This ABC is scheduled for removal in Python 3.17. + Use ``isinstance(obj, collections.abc.Buffer)`` to test if ``obj`` + implements the buffer protocol at runtime. For use in type annotations, + either use ``Buffer`` or a union that explicitly specifies the types your + code supports (e.g., ``bytes | bytearray | memoryview``). + """ + + __slots__ = () + +ByteString.register(bytes) +ByteString.register(bytearray) + class MutableSequence(Sequence): """All the operations on a read-write sequence. @@ -1103,15 +1143,16 @@ def reverse(self): self[i], self[n-i-1] = self[n-i-1], self[i] def extend(self, values): - 'S.extend(iterable) -- extend sequence by appending elements from the iterable' + """S.extend(iterable) -- extend sequence by appending elements from the + iterable""" if values is self: values = list(values) for v in values: self.append(v) def pop(self, index=-1): - '''S.pop([index]) -> item -- remove and return item at index (default last). - Raise IndexError if list is empty or index is out of range. + '''S.pop([index]) -> item -- remove and return item at index (default + last). Raise IndexError if list is empty or index is out of range. ''' v = self[index] del self[index] @@ -1130,3 +1171,13 @@ def __iadd__(self, values): MutableSequence.register(list) MutableSequence.register(bytearray) + +_deprecated_ByteString = globals().pop("ByteString") + +def __getattr__(attr): + if attr == "ByteString": + import warnings + warnings._deprecated("collections.abc.ByteString", remove=(3, 17)) + globals()["ByteString"] = _deprecated_ByteString + return _deprecated_ByteString + raise AttributeError(f"module 'collections.abc' has no attribute {attr!r}") diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 4a310a402358b6..5e0c0124e597b8 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -1,4 +1,4 @@ -import io +import builtins import os import sys @@ -10,13 +10,12 @@ # types if False: - from typing import IO, Self, ClassVar + from typing import IO, Literal, Self, ClassVar _theme: Theme class ANSIColors: RESET = "\x1b[0m" - BLACK = "\x1b[30m" BLUE = "\x1b[34m" CYAN = "\x1b[36m" @@ -75,6 +74,19 @@ class ANSIColors: setattr(NoColors, attr, "") +class CursesColors: + """Curses color constants for terminal UI theming.""" + BLACK = 0 + RED = 1 + GREEN = 2 + YELLOW = 3 + BLUE = 4 + MAGENTA = 5 + CYAN = 6 + WHITE = 7 + DEFAULT = -1 + + # # Experimental theming support (see gh-133346) # @@ -155,7 +167,7 @@ def __iter__(self) -> Iterator[str]: return iter(self.__dataclass_fields__) -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) class Argparse(ThemeSection): usage: str = ANSIColors.BOLD_BLUE prog: str = ANSIColors.BOLD_MAGENTA @@ -169,13 +181,231 @@ class Argparse(ThemeSection): short_option: str = ANSIColors.BOLD_GREEN label: str = ANSIColors.BOLD_YELLOW action: str = ANSIColors.BOLD_GREEN + default: str = ANSIColors.GREY + interpolated_value: str = ANSIColors.YELLOW + reset: str = ANSIColors.RESET + error: str = ANSIColors.BOLD_MAGENTA + warning: str = ANSIColors.BOLD_YELLOW + message: str = ANSIColors.MAGENTA + + +@dataclass(frozen=True, kw_only=True) +class Ast(ThemeSection): + node: str = ANSIColors.CYAN + field: str = ANSIColors.BLUE + attribute: str = ANSIColors.GREY + string: str = ANSIColors.GREEN + number: str = ANSIColors.YELLOW + keyword: str = ANSIColors.BOLD_BLUE + reset: str = ANSIColors.RESET + + +@dataclass(frozen=True, kw_only=True) +class Calendar(ThemeSection): + header: str = ANSIColors.BOLD + highlight: str = ANSIColors.BLACK + ANSIColors.BACKGROUND_YELLOW + weekday: str = ANSIColors.CYAN + reset: str = ANSIColors.RESET + + +@dataclass(frozen=True, kw_only=True) +class Difflib(ThemeSection): + """A 'git diff'-like theme for `difflib.unified_diff`.""" + added: str = ANSIColors.GREEN + context: str = ANSIColors.RESET # context lines + header: str = ANSIColors.BOLD # eg "---" and "+++" lines + hunk: str = ANSIColors.CYAN # the "@@" lines + removed: str = ANSIColors.RED + reset: str = ANSIColors.RESET + + +@dataclass(frozen=True, kw_only=True) +class FancyCompleter(ThemeSection): + # functions and methods + function: builtins.str = ANSIColors.BOLD_BLUE + builtin_function_or_method: builtins.str = ANSIColors.BOLD_BLUE + method: builtins.str = ANSIColors.BOLD_CYAN + method_wrapper: builtins.str = ANSIColors.BOLD_CYAN + wrapper_descriptor: builtins.str = ANSIColors.BOLD_CYAN + method_descriptor: builtins.str = ANSIColors.BOLD_CYAN + + # numbers + int: builtins.str = ANSIColors.BOLD_YELLOW + float: builtins.str = ANSIColors.BOLD_YELLOW + complex: builtins.str = ANSIColors.BOLD_YELLOW + bool: builtins.str = ANSIColors.BOLD_YELLOW + + # others + type: builtins.str = ANSIColors.BOLD_MAGENTA + module: builtins.str = ANSIColors.CYAN + NoneType: builtins.str = ANSIColors.GREY + bytes: builtins.str = ANSIColors.BOLD_GREEN + str: builtins.str = ANSIColors.BOLD_GREEN + + +@dataclass(frozen=True, kw_only=True) +class HttpServer(ThemeSection): + error: str = ANSIColors.YELLOW + path: str = ANSIColors.CYAN + serving: str = ANSIColors.GREEN + size: str = ANSIColors.GREY + status_informational: str = ANSIColors.RESET + status_ok: str = ANSIColors.GREEN + status_redirect: str = ANSIColors.INTENSE_CYAN + status_client_error: str = ANSIColors.YELLOW + status_server_error: str = ANSIColors.RED + timestamp: str = ANSIColors.GREY + url: str = ANSIColors.CYAN + reset: str = ANSIColors.RESET + + +@dataclass(frozen=True, kw_only=True) +class LiveProfiler(ThemeSection): + """Theme section for the live profiling TUI (Tachyon profiler). + + Colors use CursesColors constants (BLACK, RED, GREEN, YELLOW, + BLUE, MAGENTA, CYAN, WHITE, DEFAULT). + """ + # Header colors + title_fg: int = CursesColors.CYAN + title_bg: int = CursesColors.DEFAULT + + # Status display colors + pid_fg: int = CursesColors.CYAN + uptime_fg: int = CursesColors.GREEN + time_fg: int = CursesColors.YELLOW + interval_fg: int = CursesColors.MAGENTA + + # Thread view colors + thread_all_fg: int = CursesColors.GREEN + thread_single_fg: int = CursesColors.MAGENTA + + # Progress bar colors + bar_good_fg: int = CursesColors.GREEN + bar_bad_fg: int = CursesColors.RED + + # Stats colors + on_gil_fg: int = CursesColors.GREEN + off_gil_fg: int = CursesColors.RED + waiting_gil_fg: int = CursesColors.YELLOW + gc_fg: int = CursesColors.MAGENTA + + # Function display colors + func_total_fg: int = CursesColors.CYAN + func_exec_fg: int = CursesColors.GREEN + func_stack_fg: int = CursesColors.YELLOW + func_shown_fg: int = CursesColors.MAGENTA + + # Table header colors (for sorted column highlight) + sorted_header_fg: int = CursesColors.BLACK + sorted_header_bg: int = CursesColors.CYAN + + # Normal header colors (non-sorted columns) - use reverse video style + normal_header_fg: int = CursesColors.BLACK + normal_header_bg: int = CursesColors.WHITE + + # Data row colors + samples_fg: int = CursesColors.CYAN + file_fg: int = CursesColors.GREEN + func_fg: int = CursesColors.YELLOW + + # Trend indicator colors + trend_up_fg: int = CursesColors.GREEN + trend_down_fg: int = CursesColors.RED + + # Medal colors for top functions + medal_gold_fg: int = CursesColors.RED + medal_silver_fg: int = CursesColors.YELLOW + medal_bronze_fg: int = CursesColors.GREEN + + # Background style: 'dark' or 'light' + background_style: Literal["dark", "light"] = "dark" + + +LiveProfilerLight = LiveProfiler( + # Header colors + title_fg=CursesColors.BLUE, # Blue is more readable than cyan on light bg + + # Status display colors - darker colors for light backgrounds + pid_fg=CursesColors.BLUE, + uptime_fg=CursesColors.BLACK, + time_fg=CursesColors.BLACK, + interval_fg=CursesColors.BLUE, + + # Thread view colors + thread_all_fg=CursesColors.BLACK, + thread_single_fg=CursesColors.BLUE, + + # Stats colors + waiting_gil_fg=CursesColors.RED, + gc_fg=CursesColors.BLUE, + + # Function display colors + func_total_fg=CursesColors.BLUE, + func_exec_fg=CursesColors.BLACK, + func_stack_fg=CursesColors.BLACK, + func_shown_fg=CursesColors.BLUE, + + # Table header colors (for sorted column highlight) + sorted_header_fg=CursesColors.WHITE, + sorted_header_bg=CursesColors.BLUE, + + # Normal header colors (non-sorted columns) + normal_header_fg=CursesColors.WHITE, + normal_header_bg=CursesColors.BLACK, + + # Data row colors - use dark colors readable on white + samples_fg=CursesColors.BLACK, + file_fg=CursesColors.BLACK, + func_fg=CursesColors.BLUE, # Blue is more readable than magenta on light bg + + # Medal colors for top functions + medal_silver_fg=CursesColors.BLUE, + + # Background style + background_style="light", +) + + +@dataclass(frozen=True, kw_only=True) +class ProfilerDump(ThemeSection): + header: str = ANSIColors.BOLD_BLUE + interpreter: str = ANSIColors.GREY + thread: str = ANSIColors.BOLD_CYAN + status: str = ANSIColors.YELLOW + frame_index: str = ANSIColors.GREY + frame: str = ANSIColors.BOLD_GREEN + filename: str = ANSIColors.CYAN + line_no: str = ANSIColors.YELLOW + source: str = ANSIColors.WHITE + source_highlight: str = ANSIColors.BOLD_YELLOW + opcode: str = ANSIColors.GREY + warning: str = ANSIColors.YELLOW + reset: str = ANSIColors.RESET + + +@dataclass(frozen=True, kw_only=True) +class Pickletools(ThemeSection): + annotation: str = ANSIColors.GREY + arg_number: str = ANSIColors.YELLOW + arg_string: str = ANSIColors.GREEN + mark: str = ANSIColors.GREY + op_call: str = ANSIColors.GREEN + op_container: str = ANSIColors.INTENSE_BLUE + op_memo: str = ANSIColors.MAGENTA + op_meta: str = ANSIColors.GREY + op_stack: str = ANSIColors.BOLD_RED + opcode_code: str = ANSIColors.CYAN + position: str = ANSIColors.GREY + proto: str = ANSIColors.YELLOW reset: str = ANSIColors.RESET -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) class Syntax(ThemeSection): prompt: str = ANSIColors.BOLD_MAGENTA keyword: str = ANSIColors.BOLD_BLUE + keyword_constant: str = ANSIColors.BOLD_BLUE builtin: str = ANSIColors.CYAN comment: str = ANSIColors.RED string: str = ANSIColors.GREEN @@ -186,10 +416,31 @@ class Syntax(ThemeSection): reset: str = ANSIColors.RESET -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) +class Timeit(ThemeSection): + timing: str = ANSIColors.CYAN + best: str = ANSIColors.BOLD_GREEN + per_loop: str = ANSIColors.GREEN + punctuation: str = ANSIColors.GREY + warning: str = ANSIColors.YELLOW + warning_worst: str = ANSIColors.MAGENTA + warning_best: str = ANSIColors.GREEN + reset: str = ANSIColors.RESET + + +@dataclass(frozen=True, kw_only=True) +class Tokenize(ThemeSection): + whitespace: str = ANSIColors.GREY + error: str = ANSIColors.BOLD_RED + position: str = ANSIColors.GREY + delimiter: str = ANSIColors.RESET + + +@dataclass(frozen=True, kw_only=True) class Traceback(ThemeSection): type: str = ANSIColors.BOLD_MAGENTA message: str = ANSIColors.MAGENTA + note: str = ANSIColors.CYAN filename: str = ANSIColors.MAGENTA line_no: str = ANSIColors.MAGENTA frame: str = ANSIColors.MAGENTA @@ -198,7 +449,7 @@ class Traceback(ThemeSection): reset: str = ANSIColors.RESET -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) class Unittest(ThemeSection): passed: str = ANSIColors.GREEN warn: str = ANSIColors.YELLOW @@ -207,7 +458,7 @@ class Unittest(ThemeSection): reset: str = ANSIColors.RESET -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) class Theme: """A suite of themes for all sections of Python. @@ -215,7 +466,17 @@ class Theme: below. """ argparse: Argparse = field(default_factory=Argparse) + ast: Ast = field(default_factory=Ast) + calendar: Calendar = field(default_factory=Calendar) + difflib: Difflib = field(default_factory=Difflib) + fancycompleter: FancyCompleter = field(default_factory=FancyCompleter) + http_server: HttpServer = field(default_factory=HttpServer) + live_profiler: LiveProfiler = field(default_factory=LiveProfiler) + pickletools: Pickletools = field(default_factory=Pickletools) + profiler_dump: ProfilerDump = field(default_factory=ProfilerDump) syntax: Syntax = field(default_factory=Syntax) + timeit: Timeit = field(default_factory=Timeit) + tokenize: Tokenize = field(default_factory=Tokenize) traceback: Traceback = field(default_factory=Traceback) unittest: Unittest = field(default_factory=Unittest) @@ -223,7 +484,17 @@ def copy_with( self, *, argparse: Argparse | None = None, + ast: Ast | None = None, + calendar: Calendar | None = None, + difflib: Difflib | None = None, + fancycompleter: FancyCompleter | None = None, + http_server: HttpServer | None = None, + live_profiler: LiveProfiler | None = None, + pickletools: Pickletools | None = None, + profiler_dump: ProfilerDump | None = None, syntax: Syntax | None = None, + timeit: Timeit | None = None, + tokenize: Tokenize | None = None, traceback: Traceback | None = None, unittest: Unittest | None = None, ) -> Self: @@ -234,7 +505,17 @@ def copy_with( """ return type(self)( argparse=argparse or self.argparse, + ast=ast or self.ast, + calendar=calendar or self.calendar, + difflib=difflib or self.difflib, + fancycompleter=fancycompleter or self.fancycompleter, + http_server=http_server or self.http_server, + live_profiler=live_profiler or self.live_profiler, + pickletools=pickletools or self.pickletools, + profiler_dump=profiler_dump or self.profiler_dump, syntax=syntax or self.syntax, + timeit=timeit or self.timeit, + tokenize=tokenize or self.tokenize, traceback=traceback or self.traceback, unittest=unittest or self.unittest, ) @@ -249,7 +530,17 @@ def no_colors(cls) -> Self: """ return cls( argparse=Argparse.no_colors(), + ast=Ast.no_colors(), + calendar=Calendar.no_colors(), + difflib=Difflib.no_colors(), + fancycompleter=FancyCompleter.no_colors(), + http_server=HttpServer.no_colors(), + live_profiler=LiveProfiler.no_colors(), + pickletools=Pickletools.no_colors(), + profiler_dump=ProfilerDump.no_colors(), syntax=Syntax.no_colors(), + timeit=Timeit.no_colors(), + tokenize=Tokenize.no_colors(), traceback=Traceback.no_colors(), unittest=Unittest.no_colors(), ) @@ -272,21 +563,29 @@ def decolor(text: str) -> str: def can_colorize(*, file: IO[str] | IO[bytes] | None = None) -> bool: + + def _safe_getenv(k: str, fallback: str | None = None) -> str | None: + """Exception-safe environment retrieval. See gh-128636.""" + try: + return os.environ.get(k, fallback) + except Exception: + return fallback + if file is None: file = sys.stdout if not sys.flags.ignore_environment: - if os.environ.get("PYTHON_COLORS") == "0": + if _safe_getenv("PYTHON_COLORS") == "0": return False - if os.environ.get("PYTHON_COLORS") == "1": + if _safe_getenv("PYTHON_COLORS") == "1": return True - if os.environ.get("NO_COLOR"): + if _safe_getenv("NO_COLOR"): return False if not COLORIZE: return False - if os.environ.get("FORCE_COLOR"): + if _safe_getenv("FORCE_COLOR"): return True - if os.environ.get("TERM") == "dumb": + if _safe_getenv("TERM") == "dumb": return False if not hasattr(file, "fileno"): @@ -303,13 +602,16 @@ def can_colorize(*, file: IO[str] | IO[bytes] | None = None) -> bool: try: return os.isatty(file.fileno()) - except io.UnsupportedOperation: + except OSError: return hasattr(file, "isatty") and file.isatty() default_theme = Theme() theme_no_color = default_theme.no_colors() +# Convenience theme with light profiler colors (for white/light terminal backgrounds) +light_profiler_theme = default_theme.copy_with(live_profiler=LiveProfilerLight) + def get_theme( *, @@ -329,7 +631,8 @@ def get_theme( environment (including environment variable state and console configuration on Windows) can also change in the course of the application life cycle. """ - if force_color or (not force_no_color and can_colorize(file=tty_file)): + if force_color or (not force_no_color and + can_colorize(file=tty_file)): return _theme return theme_no_color diff --git a/Lib/_compat_pickle.py b/Lib/_compat_pickle.py index a981326432429b..928db663b446ba 100644 --- a/Lib/_compat_pickle.py +++ b/Lib/_compat_pickle.py @@ -240,6 +240,7 @@ REVERSE_NAME_MAPPING[('builtins', excname)] = ('exceptions', 'OSError') PYTHON3_IMPORTERROR_EXCEPTIONS = ( + 'ImportCycleError', 'ModuleNotFoundError', ) diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index f168d169a32948..183d0af30acf43 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -2,364 +2,386 @@ # from: # Python/bytecodes.c # Do not edit! -_specializations = { - "RESUME": [ - "RESUME_CHECK", - ], - "TO_BOOL": [ - "TO_BOOL_ALWAYS_TRUE", - "TO_BOOL_BOOL", - "TO_BOOL_INT", - "TO_BOOL_LIST", - "TO_BOOL_NONE", - "TO_BOOL_STR", - ], - "BINARY_OP": [ - "BINARY_OP_MULTIPLY_INT", - "BINARY_OP_ADD_INT", - "BINARY_OP_SUBTRACT_INT", - "BINARY_OP_MULTIPLY_FLOAT", - "BINARY_OP_ADD_FLOAT", - "BINARY_OP_SUBTRACT_FLOAT", - "BINARY_OP_ADD_UNICODE", - "BINARY_OP_SUBSCR_LIST_INT", - "BINARY_OP_SUBSCR_LIST_SLICE", - "BINARY_OP_SUBSCR_TUPLE_INT", - "BINARY_OP_SUBSCR_STR_INT", - "BINARY_OP_SUBSCR_DICT", - "BINARY_OP_SUBSCR_GETITEM", - "BINARY_OP_EXTEND", - "BINARY_OP_INPLACE_ADD_UNICODE", - ], - "STORE_SUBSCR": [ - "STORE_SUBSCR_DICT", - "STORE_SUBSCR_LIST_INT", - ], - "SEND": [ - "SEND_GEN", - ], - "UNPACK_SEQUENCE": [ - "UNPACK_SEQUENCE_TWO_TUPLE", - "UNPACK_SEQUENCE_TUPLE", - "UNPACK_SEQUENCE_LIST", - ], - "STORE_ATTR": [ - "STORE_ATTR_INSTANCE_VALUE", - "STORE_ATTR_SLOT", - "STORE_ATTR_WITH_HINT", - ], - "LOAD_GLOBAL": [ - "LOAD_GLOBAL_MODULE", - "LOAD_GLOBAL_BUILTIN", - ], - "LOAD_SUPER_ATTR": [ - "LOAD_SUPER_ATTR_ATTR", - "LOAD_SUPER_ATTR_METHOD", - ], - "LOAD_ATTR": [ - "LOAD_ATTR_INSTANCE_VALUE", - "LOAD_ATTR_MODULE", - "LOAD_ATTR_WITH_HINT", - "LOAD_ATTR_SLOT", - "LOAD_ATTR_CLASS", - "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK", - "LOAD_ATTR_PROPERTY", - "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN", - "LOAD_ATTR_METHOD_WITH_VALUES", - "LOAD_ATTR_METHOD_NO_DICT", - "LOAD_ATTR_METHOD_LAZY_DICT", - "LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", - "LOAD_ATTR_NONDESCRIPTOR_NO_DICT", - ], - "COMPARE_OP": [ - "COMPARE_OP_FLOAT", - "COMPARE_OP_INT", - "COMPARE_OP_STR", - ], - "CONTAINS_OP": [ - "CONTAINS_OP_SET", - "CONTAINS_OP_DICT", - ], - "JUMP_BACKWARD": [ - "JUMP_BACKWARD_NO_JIT", - "JUMP_BACKWARD_JIT", - ], - "FOR_ITER": [ - "FOR_ITER_LIST", - "FOR_ITER_TUPLE", - "FOR_ITER_RANGE", - "FOR_ITER_GEN", - ], - "CALL": [ - "CALL_BOUND_METHOD_EXACT_ARGS", - "CALL_PY_EXACT_ARGS", - "CALL_TYPE_1", - "CALL_STR_1", - "CALL_TUPLE_1", - "CALL_BUILTIN_CLASS", - "CALL_BUILTIN_O", - "CALL_BUILTIN_FAST", - "CALL_BUILTIN_FAST_WITH_KEYWORDS", - "CALL_LEN", - "CALL_ISINSTANCE", - "CALL_LIST_APPEND", - "CALL_METHOD_DESCRIPTOR_O", - "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", - "CALL_METHOD_DESCRIPTOR_NOARGS", - "CALL_METHOD_DESCRIPTOR_FAST", - "CALL_ALLOC_AND_ENTER_INIT", - "CALL_PY_GENERAL", - "CALL_BOUND_METHOD_GENERAL", - "CALL_NON_PY_GENERAL", - ], - "CALL_KW": [ - "CALL_KW_BOUND_METHOD", - "CALL_KW_PY", - "CALL_KW_NON_PY", - ], -} +_specializations = frozendict( + RESUME=( + "RESUME_CHECK", + "RESUME_CHECK_JIT", + ), + TO_BOOL=( + "TO_BOOL_ALWAYS_TRUE", + "TO_BOOL_BOOL", + "TO_BOOL_INT", + "TO_BOOL_LIST", + "TO_BOOL_NONE", + "TO_BOOL_STR", + ), + BINARY_OP=( + "BINARY_OP_MULTIPLY_INT", + "BINARY_OP_ADD_INT", + "BINARY_OP_SUBTRACT_INT", + "BINARY_OP_MULTIPLY_FLOAT", + "BINARY_OP_ADD_FLOAT", + "BINARY_OP_SUBTRACT_FLOAT", + "BINARY_OP_ADD_UNICODE", + "BINARY_OP_SUBSCR_LIST_INT", + "BINARY_OP_SUBSCR_LIST_SLICE", + "BINARY_OP_SUBSCR_TUPLE_INT", + "BINARY_OP_SUBSCR_STR_INT", + "BINARY_OP_SUBSCR_USTR_INT", + "BINARY_OP_SUBSCR_DICT", + "BINARY_OP_SUBSCR_GETITEM", + "BINARY_OP_INPLACE_ADD_UNICODE", + "BINARY_OP_EXTEND", + ), + STORE_SUBSCR=( + "STORE_SUBSCR_DICT", + "STORE_SUBSCR_LIST_INT", + ), + SEND=( + "SEND_GEN", + "SEND_VIRTUAL", + "SEND_ASYNC_GEN", + ), + UNPACK_SEQUENCE=( + "UNPACK_SEQUENCE_TWO_TUPLE", + "UNPACK_SEQUENCE_TUPLE", + "UNPACK_SEQUENCE_LIST", + ), + STORE_ATTR=( + "STORE_ATTR_INSTANCE_VALUE", + "STORE_ATTR_SLOT", + "STORE_ATTR_WITH_HINT", + ), + LOAD_GLOBAL=( + "LOAD_GLOBAL_MODULE", + "LOAD_GLOBAL_BUILTIN", + ), + LOAD_SUPER_ATTR=( + "LOAD_SUPER_ATTR_ATTR", + "LOAD_SUPER_ATTR_METHOD", + ), + LOAD_ATTR=( + "LOAD_ATTR_INSTANCE_VALUE", + "LOAD_ATTR_MODULE", + "LOAD_ATTR_WITH_HINT", + "LOAD_ATTR_SLOT", + "LOAD_ATTR_CLASS", + "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK", + "LOAD_ATTR_PROPERTY", + "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN", + "LOAD_ATTR_METHOD_WITH_VALUES", + "LOAD_ATTR_METHOD_NO_DICT", + "LOAD_ATTR_METHOD_LAZY_DICT", + "LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", + "LOAD_ATTR_NONDESCRIPTOR_NO_DICT", + ), + COMPARE_OP=( + "COMPARE_OP_FLOAT", + "COMPARE_OP_INT", + "COMPARE_OP_STR", + ), + CONTAINS_OP=( + "CONTAINS_OP_SET", + "CONTAINS_OP_DICT", + ), + JUMP_BACKWARD=( + "JUMP_BACKWARD_NO_JIT", + "JUMP_BACKWARD_JIT", + ), + GET_ITER=( + "GET_ITER_SELF", + "GET_ITER_VIRTUAL", + ), + FOR_ITER=( + "FOR_ITER_LIST", + "FOR_ITER_TUPLE", + "FOR_ITER_RANGE", + "FOR_ITER_GEN", + "FOR_ITER_VIRTUAL", + ), + CALL=( + "CALL_BOUND_METHOD_EXACT_ARGS", + "CALL_PY_EXACT_ARGS", + "CALL_TYPE_1", + "CALL_STR_1", + "CALL_TUPLE_1", + "CALL_BUILTIN_CLASS", + "CALL_BUILTIN_O", + "CALL_BUILTIN_FAST", + "CALL_BUILTIN_FAST_WITH_KEYWORDS", + "CALL_LEN", + "CALL_ISINSTANCE", + "CALL_LIST_APPEND", + "CALL_METHOD_DESCRIPTOR_O", + "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", + "CALL_METHOD_DESCRIPTOR_NOARGS", + "CALL_METHOD_DESCRIPTOR_FAST", + "CALL_ALLOC_AND_ENTER_INIT", + "CALL_PY_GENERAL", + "CALL_BOUND_METHOD_GENERAL", + "CALL_NON_PY_GENERAL", + ), + CALL_KW=( + "CALL_KW_BOUND_METHOD", + "CALL_KW_PY", + "CALL_KW_NON_PY", + ), + CALL_FUNCTION_EX=( + "CALL_EX_PY", + "CALL_EX_NON_PY_GENERAL", + ), +) -_specialized_opmap = { - 'BINARY_OP_ADD_FLOAT': 129, - 'BINARY_OP_ADD_INT': 130, - 'BINARY_OP_ADD_UNICODE': 131, - 'BINARY_OP_EXTEND': 132, - 'BINARY_OP_INPLACE_ADD_UNICODE': 3, - 'BINARY_OP_MULTIPLY_FLOAT': 133, - 'BINARY_OP_MULTIPLY_INT': 134, - 'BINARY_OP_SUBSCR_DICT': 135, - 'BINARY_OP_SUBSCR_GETITEM': 136, - 'BINARY_OP_SUBSCR_LIST_INT': 137, - 'BINARY_OP_SUBSCR_LIST_SLICE': 138, - 'BINARY_OP_SUBSCR_STR_INT': 139, - 'BINARY_OP_SUBSCR_TUPLE_INT': 140, - 'BINARY_OP_SUBTRACT_FLOAT': 141, - 'BINARY_OP_SUBTRACT_INT': 142, - 'CALL_ALLOC_AND_ENTER_INIT': 143, - 'CALL_BOUND_METHOD_EXACT_ARGS': 144, - 'CALL_BOUND_METHOD_GENERAL': 145, - 'CALL_BUILTIN_CLASS': 146, - 'CALL_BUILTIN_FAST': 147, - 'CALL_BUILTIN_FAST_WITH_KEYWORDS': 148, - 'CALL_BUILTIN_O': 149, - 'CALL_ISINSTANCE': 150, - 'CALL_KW_BOUND_METHOD': 151, - 'CALL_KW_NON_PY': 152, - 'CALL_KW_PY': 153, - 'CALL_LEN': 154, - 'CALL_LIST_APPEND': 155, - 'CALL_METHOD_DESCRIPTOR_FAST': 156, - 'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 157, - 'CALL_METHOD_DESCRIPTOR_NOARGS': 158, - 'CALL_METHOD_DESCRIPTOR_O': 159, - 'CALL_NON_PY_GENERAL': 160, - 'CALL_PY_EXACT_ARGS': 161, - 'CALL_PY_GENERAL': 162, - 'CALL_STR_1': 163, - 'CALL_TUPLE_1': 164, - 'CALL_TYPE_1': 165, - 'COMPARE_OP_FLOAT': 166, - 'COMPARE_OP_INT': 167, - 'COMPARE_OP_STR': 168, - 'CONTAINS_OP_DICT': 169, - 'CONTAINS_OP_SET': 170, - 'FOR_ITER_GEN': 171, - 'FOR_ITER_LIST': 172, - 'FOR_ITER_RANGE': 173, - 'FOR_ITER_TUPLE': 174, - 'JUMP_BACKWARD_JIT': 175, - 'JUMP_BACKWARD_NO_JIT': 176, - 'LOAD_ATTR_CLASS': 177, - 'LOAD_ATTR_CLASS_WITH_METACLASS_CHECK': 178, - 'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 179, - 'LOAD_ATTR_INSTANCE_VALUE': 180, - 'LOAD_ATTR_METHOD_LAZY_DICT': 181, - 'LOAD_ATTR_METHOD_NO_DICT': 182, - 'LOAD_ATTR_METHOD_WITH_VALUES': 183, - 'LOAD_ATTR_MODULE': 184, - 'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 185, - 'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 186, - 'LOAD_ATTR_PROPERTY': 187, - 'LOAD_ATTR_SLOT': 188, - 'LOAD_ATTR_WITH_HINT': 189, - 'LOAD_GLOBAL_BUILTIN': 190, - 'LOAD_GLOBAL_MODULE': 191, - 'LOAD_SUPER_ATTR_ATTR': 192, - 'LOAD_SUPER_ATTR_METHOD': 193, - 'RESUME_CHECK': 194, - 'SEND_GEN': 195, - 'STORE_ATTR_INSTANCE_VALUE': 196, - 'STORE_ATTR_SLOT': 197, - 'STORE_ATTR_WITH_HINT': 198, - 'STORE_SUBSCR_DICT': 199, - 'STORE_SUBSCR_LIST_INT': 200, - 'TO_BOOL_ALWAYS_TRUE': 201, - 'TO_BOOL_BOOL': 202, - 'TO_BOOL_INT': 203, - 'TO_BOOL_LIST': 204, - 'TO_BOOL_NONE': 205, - 'TO_BOOL_STR': 206, - 'UNPACK_SEQUENCE_LIST': 207, - 'UNPACK_SEQUENCE_TUPLE': 208, - 'UNPACK_SEQUENCE_TWO_TUPLE': 209, -} +_specialized_opmap = frozendict( + BINARY_OP_ADD_FLOAT=129, + BINARY_OP_ADD_INT=130, + BINARY_OP_ADD_UNICODE=131, + BINARY_OP_EXTEND=132, + BINARY_OP_INPLACE_ADD_UNICODE=3, + BINARY_OP_MULTIPLY_FLOAT=133, + BINARY_OP_MULTIPLY_INT=134, + BINARY_OP_SUBSCR_DICT=135, + BINARY_OP_SUBSCR_GETITEM=136, + BINARY_OP_SUBSCR_LIST_INT=137, + BINARY_OP_SUBSCR_LIST_SLICE=138, + BINARY_OP_SUBSCR_STR_INT=139, + BINARY_OP_SUBSCR_TUPLE_INT=140, + BINARY_OP_SUBSCR_USTR_INT=141, + BINARY_OP_SUBTRACT_FLOAT=142, + BINARY_OP_SUBTRACT_INT=143, + CALL_ALLOC_AND_ENTER_INIT=144, + CALL_BOUND_METHOD_EXACT_ARGS=145, + CALL_BOUND_METHOD_GENERAL=146, + CALL_BUILTIN_CLASS=147, + CALL_BUILTIN_FAST=148, + CALL_BUILTIN_FAST_WITH_KEYWORDS=149, + CALL_BUILTIN_O=150, + CALL_EX_NON_PY_GENERAL=151, + CALL_EX_PY=152, + CALL_ISINSTANCE=153, + CALL_KW_BOUND_METHOD=154, + CALL_KW_NON_PY=155, + CALL_KW_PY=156, + CALL_LEN=157, + CALL_LIST_APPEND=158, + CALL_METHOD_DESCRIPTOR_FAST=159, + CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS=160, + CALL_METHOD_DESCRIPTOR_NOARGS=161, + CALL_METHOD_DESCRIPTOR_O=162, + CALL_NON_PY_GENERAL=163, + CALL_PY_EXACT_ARGS=164, + CALL_PY_GENERAL=165, + CALL_STR_1=166, + CALL_TUPLE_1=167, + CALL_TYPE_1=168, + COMPARE_OP_FLOAT=169, + COMPARE_OP_INT=170, + COMPARE_OP_STR=171, + CONTAINS_OP_DICT=172, + CONTAINS_OP_SET=173, + FOR_ITER_GEN=174, + FOR_ITER_LIST=175, + FOR_ITER_RANGE=176, + FOR_ITER_TUPLE=177, + FOR_ITER_VIRTUAL=178, + GET_ITER_SELF=179, + GET_ITER_VIRTUAL=180, + JUMP_BACKWARD_JIT=181, + JUMP_BACKWARD_NO_JIT=182, + LOAD_ATTR_CLASS=183, + LOAD_ATTR_CLASS_WITH_METACLASS_CHECK=184, + LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN=185, + LOAD_ATTR_INSTANCE_VALUE=186, + LOAD_ATTR_METHOD_LAZY_DICT=187, + LOAD_ATTR_METHOD_NO_DICT=188, + LOAD_ATTR_METHOD_WITH_VALUES=189, + LOAD_ATTR_MODULE=190, + LOAD_ATTR_NONDESCRIPTOR_NO_DICT=191, + LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES=192, + LOAD_ATTR_PROPERTY=193, + LOAD_ATTR_SLOT=194, + LOAD_ATTR_WITH_HINT=195, + LOAD_GLOBAL_BUILTIN=196, + LOAD_GLOBAL_MODULE=197, + LOAD_SUPER_ATTR_ATTR=198, + LOAD_SUPER_ATTR_METHOD=199, + RESUME_CHECK=200, + RESUME_CHECK_JIT=201, + SEND_ASYNC_GEN=202, + SEND_GEN=203, + SEND_VIRTUAL=204, + STORE_ATTR_INSTANCE_VALUE=205, + STORE_ATTR_SLOT=206, + STORE_ATTR_WITH_HINT=207, + STORE_SUBSCR_DICT=208, + STORE_SUBSCR_LIST_INT=209, + TO_BOOL_ALWAYS_TRUE=210, + TO_BOOL_BOOL=211, + TO_BOOL_INT=212, + TO_BOOL_LIST=213, + TO_BOOL_NONE=214, + TO_BOOL_STR=215, + UNPACK_SEQUENCE_LIST=216, + UNPACK_SEQUENCE_TUPLE=217, + UNPACK_SEQUENCE_TWO_TUPLE=218, +) -opmap = { - 'CACHE': 0, - 'RESERVED': 17, - 'RESUME': 128, - 'INSTRUMENTED_LINE': 254, - 'ENTER_EXECUTOR': 255, - 'BINARY_SLICE': 1, - 'BUILD_TEMPLATE': 2, - 'CALL_FUNCTION_EX': 4, - 'CHECK_EG_MATCH': 5, - 'CHECK_EXC_MATCH': 6, - 'CLEANUP_THROW': 7, - 'DELETE_SUBSCR': 8, - 'END_FOR': 9, - 'END_SEND': 10, - 'EXIT_INIT_CHECK': 11, - 'FORMAT_SIMPLE': 12, - 'FORMAT_WITH_SPEC': 13, - 'GET_AITER': 14, - 'GET_ANEXT': 15, - 'GET_ITER': 16, - 'GET_LEN': 18, - 'GET_YIELD_FROM_ITER': 19, - 'INTERPRETER_EXIT': 20, - 'LOAD_BUILD_CLASS': 21, - 'LOAD_LOCALS': 22, - 'MAKE_FUNCTION': 23, - 'MATCH_KEYS': 24, - 'MATCH_MAPPING': 25, - 'MATCH_SEQUENCE': 26, - 'NOP': 27, - 'NOT_TAKEN': 28, - 'POP_EXCEPT': 29, - 'POP_ITER': 30, - 'POP_TOP': 31, - 'PUSH_EXC_INFO': 32, - 'PUSH_NULL': 33, - 'RETURN_GENERATOR': 34, - 'RETURN_VALUE': 35, - 'SETUP_ANNOTATIONS': 36, - 'STORE_SLICE': 37, - 'STORE_SUBSCR': 38, - 'TO_BOOL': 39, - 'UNARY_INVERT': 40, - 'UNARY_NEGATIVE': 41, - 'UNARY_NOT': 42, - 'WITH_EXCEPT_START': 43, - 'BINARY_OP': 44, - 'BUILD_INTERPOLATION': 45, - 'BUILD_LIST': 46, - 'BUILD_MAP': 47, - 'BUILD_SET': 48, - 'BUILD_SLICE': 49, - 'BUILD_STRING': 50, - 'BUILD_TUPLE': 51, - 'CALL': 52, - 'CALL_INTRINSIC_1': 53, - 'CALL_INTRINSIC_2': 54, - 'CALL_KW': 55, - 'COMPARE_OP': 56, - 'CONTAINS_OP': 57, - 'CONVERT_VALUE': 58, - 'COPY': 59, - 'COPY_FREE_VARS': 60, - 'DELETE_ATTR': 61, - 'DELETE_DEREF': 62, - 'DELETE_FAST': 63, - 'DELETE_GLOBAL': 64, - 'DELETE_NAME': 65, - 'DICT_MERGE': 66, - 'DICT_UPDATE': 67, - 'END_ASYNC_FOR': 68, - 'EXTENDED_ARG': 69, - 'FOR_ITER': 70, - 'GET_AWAITABLE': 71, - 'IMPORT_FROM': 72, - 'IMPORT_NAME': 73, - 'IS_OP': 74, - 'JUMP_BACKWARD': 75, - 'JUMP_BACKWARD_NO_INTERRUPT': 76, - 'JUMP_FORWARD': 77, - 'LIST_APPEND': 78, - 'LIST_EXTEND': 79, - 'LOAD_ATTR': 80, - 'LOAD_COMMON_CONSTANT': 81, - 'LOAD_CONST': 82, - 'LOAD_DEREF': 83, - 'LOAD_FAST': 84, - 'LOAD_FAST_AND_CLEAR': 85, - 'LOAD_FAST_BORROW': 86, - 'LOAD_FAST_BORROW_LOAD_FAST_BORROW': 87, - 'LOAD_FAST_CHECK': 88, - 'LOAD_FAST_LOAD_FAST': 89, - 'LOAD_FROM_DICT_OR_DEREF': 90, - 'LOAD_FROM_DICT_OR_GLOBALS': 91, - 'LOAD_GLOBAL': 92, - 'LOAD_NAME': 93, - 'LOAD_SMALL_INT': 94, - 'LOAD_SPECIAL': 95, - 'LOAD_SUPER_ATTR': 96, - 'MAKE_CELL': 97, - 'MAP_ADD': 98, - 'MATCH_CLASS': 99, - 'POP_JUMP_IF_FALSE': 100, - 'POP_JUMP_IF_NONE': 101, - 'POP_JUMP_IF_NOT_NONE': 102, - 'POP_JUMP_IF_TRUE': 103, - 'RAISE_VARARGS': 104, - 'RERAISE': 105, - 'SEND': 106, - 'SET_ADD': 107, - 'SET_FUNCTION_ATTRIBUTE': 108, - 'SET_UPDATE': 109, - 'STORE_ATTR': 110, - 'STORE_DEREF': 111, - 'STORE_FAST': 112, - 'STORE_FAST_LOAD_FAST': 113, - 'STORE_FAST_STORE_FAST': 114, - 'STORE_GLOBAL': 115, - 'STORE_NAME': 116, - 'SWAP': 117, - 'UNPACK_EX': 118, - 'UNPACK_SEQUENCE': 119, - 'YIELD_VALUE': 120, - 'INSTRUMENTED_END_FOR': 234, - 'INSTRUMENTED_POP_ITER': 235, - 'INSTRUMENTED_END_SEND': 236, - 'INSTRUMENTED_FOR_ITER': 237, - 'INSTRUMENTED_INSTRUCTION': 238, - 'INSTRUMENTED_JUMP_FORWARD': 239, - 'INSTRUMENTED_NOT_TAKEN': 240, - 'INSTRUMENTED_POP_JUMP_IF_TRUE': 241, - 'INSTRUMENTED_POP_JUMP_IF_FALSE': 242, - 'INSTRUMENTED_POP_JUMP_IF_NONE': 243, - 'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 244, - 'INSTRUMENTED_RESUME': 245, - 'INSTRUMENTED_RETURN_VALUE': 246, - 'INSTRUMENTED_YIELD_VALUE': 247, - 'INSTRUMENTED_END_ASYNC_FOR': 248, - 'INSTRUMENTED_LOAD_SUPER_ATTR': 249, - 'INSTRUMENTED_CALL': 250, - 'INSTRUMENTED_CALL_KW': 251, - 'INSTRUMENTED_CALL_FUNCTION_EX': 252, - 'INSTRUMENTED_JUMP_BACKWARD': 253, - 'ANNOTATIONS_PLACEHOLDER': 256, - 'JUMP': 257, - 'JUMP_IF_FALSE': 258, - 'JUMP_IF_TRUE': 259, - 'JUMP_NO_INTERRUPT': 260, - 'LOAD_CLOSURE': 261, - 'POP_BLOCK': 262, - 'SETUP_CLEANUP': 263, - 'SETUP_FINALLY': 264, - 'SETUP_WITH': 265, - 'STORE_FAST_MAYBE_NULL': 266, -} +opmap = frozendict( + CACHE=0, + RESERVED=17, + RESUME=128, + INSTRUMENTED_LINE=253, + ENTER_EXECUTOR=254, + TRACE_RECORD=255, + BINARY_SLICE=1, + BUILD_TEMPLATE=2, + CALL_FUNCTION_EX=4, + CHECK_EG_MATCH=5, + CHECK_EXC_MATCH=6, + CLEANUP_THROW=7, + DELETE_SUBSCR=8, + END_FOR=9, + END_SEND=10, + EXIT_INIT_CHECK=11, + FORMAT_SIMPLE=12, + FORMAT_WITH_SPEC=13, + GET_AITER=14, + GET_ANEXT=15, + GET_LEN=16, + INTERPRETER_EXIT=18, + LOAD_BUILD_CLASS=19, + LOAD_LOCALS=20, + MAKE_FUNCTION=21, + MATCH_KEYS=22, + MATCH_MAPPING=23, + MATCH_SEQUENCE=24, + NOP=25, + NOT_TAKEN=26, + POP_EXCEPT=27, + POP_ITER=28, + POP_TOP=29, + PUSH_EXC_INFO=30, + PUSH_NULL=31, + RETURN_GENERATOR=32, + RETURN_VALUE=33, + SETUP_ANNOTATIONS=34, + STORE_SLICE=35, + STORE_SUBSCR=36, + TO_BOOL=37, + UNARY_INVERT=38, + UNARY_NEGATIVE=39, + UNARY_NOT=40, + WITH_EXCEPT_START=41, + BINARY_OP=42, + BUILD_INTERPOLATION=43, + BUILD_LIST=44, + BUILD_MAP=45, + BUILD_SET=46, + BUILD_SLICE=47, + BUILD_STRING=48, + BUILD_TUPLE=49, + CALL=50, + CALL_INTRINSIC_1=51, + CALL_INTRINSIC_2=52, + CALL_KW=53, + COMPARE_OP=54, + CONTAINS_OP=55, + CONVERT_VALUE=56, + COPY=57, + COPY_FREE_VARS=58, + DELETE_ATTR=59, + DELETE_DEREF=60, + DELETE_FAST=61, + DELETE_GLOBAL=62, + DELETE_NAME=63, + DICT_MERGE=64, + DICT_UPDATE=65, + END_ASYNC_FOR=66, + EXTENDED_ARG=67, + FOR_ITER=68, + GET_AWAITABLE=69, + GET_ITER=70, + IMPORT_FROM=71, + IMPORT_NAME=72, + IS_OP=73, + JUMP_BACKWARD=74, + JUMP_BACKWARD_NO_INTERRUPT=75, + JUMP_FORWARD=76, + LIST_APPEND=77, + LIST_EXTEND=78, + LOAD_ATTR=79, + LOAD_COMMON_CONSTANT=80, + LOAD_CONST=81, + LOAD_DEREF=82, + LOAD_FAST=83, + LOAD_FAST_AND_CLEAR=84, + LOAD_FAST_BORROW=85, + LOAD_FAST_BORROW_LOAD_FAST_BORROW=86, + LOAD_FAST_CHECK=87, + LOAD_FAST_LOAD_FAST=88, + LOAD_FROM_DICT_OR_DEREF=89, + LOAD_FROM_DICT_OR_GLOBALS=90, + LOAD_GLOBAL=91, + LOAD_NAME=92, + LOAD_SMALL_INT=93, + LOAD_SPECIAL=94, + LOAD_SUPER_ATTR=95, + MAKE_CELL=96, + MAP_ADD=97, + MATCH_CLASS=98, + POP_JUMP_IF_FALSE=99, + POP_JUMP_IF_NONE=100, + POP_JUMP_IF_NOT_NONE=101, + POP_JUMP_IF_TRUE=102, + RAISE_VARARGS=103, + RERAISE=104, + SEND=105, + SET_ADD=106, + SET_FUNCTION_ATTRIBUTE=107, + SET_UPDATE=108, + STORE_ATTR=109, + STORE_DEREF=110, + STORE_FAST=111, + STORE_FAST_LOAD_FAST=112, + STORE_FAST_STORE_FAST=113, + STORE_GLOBAL=114, + STORE_NAME=115, + SWAP=116, + UNPACK_EX=117, + UNPACK_SEQUENCE=118, + YIELD_VALUE=119, + INSTRUMENTED_END_FOR=233, + INSTRUMENTED_POP_ITER=234, + INSTRUMENTED_END_SEND=235, + INSTRUMENTED_FOR_ITER=236, + INSTRUMENTED_INSTRUCTION=237, + INSTRUMENTED_JUMP_FORWARD=238, + INSTRUMENTED_NOT_TAKEN=239, + INSTRUMENTED_POP_JUMP_IF_TRUE=240, + INSTRUMENTED_POP_JUMP_IF_FALSE=241, + INSTRUMENTED_POP_JUMP_IF_NONE=242, + INSTRUMENTED_POP_JUMP_IF_NOT_NONE=243, + INSTRUMENTED_RESUME=244, + INSTRUMENTED_RETURN_VALUE=245, + INSTRUMENTED_YIELD_VALUE=246, + INSTRUMENTED_END_ASYNC_FOR=247, + INSTRUMENTED_LOAD_SUPER_ATTR=248, + INSTRUMENTED_CALL=249, + INSTRUMENTED_CALL_KW=250, + INSTRUMENTED_CALL_FUNCTION_EX=251, + INSTRUMENTED_JUMP_BACKWARD=252, + ANNOTATIONS_PLACEHOLDER=256, + JUMP=257, + JUMP_IF_FALSE=258, + JUMP_IF_TRUE=259, + JUMP_NO_INTERRUPT=260, + LOAD_CLOSURE=261, + POP_BLOCK=262, + SETUP_CLEANUP=263, + SETUP_FINALLY=264, + SETUP_WITH=265, + STORE_FAST_MAYBE_NULL=266, +) -HAVE_ARGUMENT = 43 -MIN_INSTRUMENTED_OPCODE = 234 +HAVE_ARGUMENT = 41 +MIN_INSTRUMENTED_OPCODE = 233 diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py index 0cb064fcd791be..29b89e311cb1fe 100644 --- a/Lib/_osx_support.py +++ b/Lib/_osx_support.py @@ -17,7 +17,8 @@ _UNIVERSAL_CONFIG_VARS = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', 'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS', - 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS') + 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS', + 'PY_CORE_EXE_LDFLAGS') # configuration variables that may contain compiler calls _COMPILER_CONFIG_VARS = ('BLDSHARED', 'LDSHARED', 'CC', 'CXX') diff --git a/Lib/_py_warnings.py b/Lib/_py_warnings.py index cbaa94458629ac..ab09913de6812d 100644 --- a/Lib/_py_warnings.py +++ b/Lib/_py_warnings.py @@ -369,9 +369,15 @@ def _setoption(arg): if message or module: import re if message: - message = re.escape(message) + if len(message) >= 2 and message[0] == message[-1] == '/': + message = message[1:-1] + else: + message = re.escape(message) if module: - module = re.escape(module) + r'\z' + if len(module) >= 2 and module[0] == module[-1] == '/': + module = module[1:-1] + else: + module = re.escape(module) + r'\z' if lineno: try: lineno = int(lineno) @@ -381,7 +387,23 @@ def _setoption(arg): raise _wm._OptionError("invalid lineno %r" % (lineno,)) from None else: lineno = 0 - _wm.filterwarnings(action, message, category, module, lineno) + try: + _wm.filterwarnings(action, message, category, module, lineno) + except re.PatternError if message or module else (): + if message: + try: + re.compile(message) + except re.PatternError: + raise _wm._OptionError(f"invalid regular expression for " + f"message: {message!r}") from None + if module: + try: + re.compile(module) + except re.PatternError: + raise _wm._OptionError(f"invalid regular expression for " + f"module: {module!r}") from None + # Should never happen. + raise # Helper for _setoption() @@ -449,9 +471,12 @@ def warn(message, category=None, stacklevel=1, source=None, # Check category argument if category is None: category = UserWarning - if not (isinstance(category, type) and issubclass(category, Warning)): - raise TypeError("category must be a Warning subclass, " - "not '{:s}'".format(type(category).__name__)) + elif not isinstance(category, type): + raise TypeError(f"category must be a Warning subclass, not " + f"'{type(category).__name__}'") + elif not issubclass(category, Warning): + raise TypeError(f"category must be a Warning subclass, not " + f"class '{category.__name__}'") if not isinstance(skip_file_prefixes, tuple): # The C version demands a tuple for implementation performance. raise TypeError('skip_file_prefixes must be a tuple of strs.') @@ -495,14 +520,43 @@ def warn(message, category=None, stacklevel=1, source=None, ) +def _match_filename(pattern, filename, *, MS_WINDOWS=(sys.platform == 'win32')): + if not filename: + return pattern.match('') is not None + if filename[0] == '<' and filename[-1] == '>': + return pattern.match(filename) is not None + + is_py = (filename[-3:].lower() == '.py' + if MS_WINDOWS else + filename.endswith('.py')) + if is_py: + filename = filename[:-3] + if pattern.match(filename): # for backward compatibility + return True + if MS_WINDOWS: + if not is_py and filename[-4:].lower() == '.pyw': + filename = filename[:-4] + is_py = True + if is_py and filename[-9:].lower() in (r'\__init__', '/__init__'): + filename = filename[:-9] + filename = filename.replace('\\', '/') + else: + if is_py and filename.endswith('/__init__'): + filename = filename[:-9] + filename = filename.replace('/', '.') + i = 0 + while True: + if pattern.match(filename, i): + return True + i = filename.find('.', i) + 1 + if not i: + return False + + def warn_explicit(message, category, filename, lineno, module=None, registry=None, module_globals=None, source=None): lineno = int(lineno) - if module is None: - module = filename or "" - if module[-3:].lower() == ".py": - module = module[:-3] # XXX What about leading pathname? if isinstance(message, Warning): text = str(message) category = message.__class__ @@ -524,9 +578,11 @@ def warn_explicit(message, category, filename, lineno, action, msg, cat, mod, ln = item if ((msg is None or msg.match(text)) and issubclass(category, cat) and - (mod is None or mod.match(module)) and - (ln == 0 or lineno == ln)): - break + (ln == 0 or lineno == ln) and + (mod is None or (_match_filename(mod, filename) + if module is None else + mod.match(module)))): + break else: action = _wm.defaultaction # Early exit actions @@ -564,17 +620,18 @@ def warn_explicit(message, category, filename, lineno, linecache.getlines(filename, module_globals) # Print message and context - msg = _wm.WarningMessage(message, category, filename, lineno, source=source) + msg = _wm.WarningMessage(message, category, filename, lineno, + module=module, source=source) _wm._showwarnmsg(msg) class WarningMessage(object): _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file", - "line", "source") + "line", "source", "module") def __init__(self, message, category, filename, lineno, file=None, - line=None, source=None): + line=None, source=None, module=None): self.message = message self.category = category self.filename = filename @@ -582,12 +639,17 @@ def __init__(self, message, category, filename, lineno, file=None, self.file = file self.line = line self.source = source + self.module = module self._category_name = category.__name__ if category else None def __str__(self): - return ("{message : %r, category : %r, filename : %r, lineno : %s, " - "line : %r}" % (self.message, self._category_name, - self.filename, self.lineno, self.line)) + return ("{message : %r, category : %r, module : %r, " + "filename : %r, lineno : %s, line : %r}" % ( + self.message, self._category_name, self.module, + self.filename, self.lineno, self.line)) + + def __repr__(self): + return f'<{type(self).__qualname__} {self}>' class catch_warnings(object): @@ -644,8 +706,8 @@ def __enter__(self): context = None self._filters = self._module.filters self._module.filters = self._filters[:] - self._showwarning = self._module.showwarning self._showwarnmsg_impl = self._module._showwarnmsg_impl + self._showwarning = self._module.showwarning self._module._filters_mutated_lock_held() if self._record: if _use_context: @@ -653,9 +715,9 @@ def __enter__(self): else: log = [] self._module._showwarnmsg_impl = log.append - # Reset showwarning() to the default implementation to make sure - # that _showwarnmsg() calls _showwarnmsg_impl() - self._module.showwarning = self._module._showwarning_orig + # Reset showwarning() to the default implementation to make sure + # that _showwarnmsg() calls _showwarnmsg_impl() + self._module.showwarning = self._module._showwarning_orig else: log = None if self._filter is not None: @@ -670,8 +732,8 @@ def __exit__(self, *exc_info): self._module._warnings_context.set(self._saved_context) else: self._module.filters = self._filters - self._module.showwarning = self._showwarning self._module._showwarnmsg_impl = self._showwarnmsg_impl + self._module.showwarning = self._showwarning self._module._filters_mutated_lock_held() @@ -762,27 +824,27 @@ def __new__(cls, /, *args, **kwargs): arg.__new__ = staticmethod(__new__) - original_init_subclass = arg.__init_subclass__ - # We need slightly different behavior if __init_subclass__ - # is a bound method (likely if it was implemented in Python) - if isinstance(original_init_subclass, MethodType): - original_init_subclass = original_init_subclass.__func__ + if "__init_subclass__" in arg.__dict__: + # __init_subclass__ is directly present on the decorated class. + # Synthesize a wrapper that calls this method directly. + original_init_subclass = arg.__init_subclass__ + # We need slightly different behavior if __init_subclass__ + # is a bound method (likely if it was implemented in Python). + # Otherwise, it likely means it's a builtin such as + # object's implementation of __init_subclass__. + if isinstance(original_init_subclass, MethodType): + original_init_subclass = original_init_subclass.__func__ @functools.wraps(original_init_subclass) def __init_subclass__(*args, **kwargs): _wm.warn(msg, category=category, stacklevel=stacklevel + 1) return original_init_subclass(*args, **kwargs) - - arg.__init_subclass__ = classmethod(__init_subclass__) - # Or otherwise, which likely means it's a builtin such as - # object's implementation of __init_subclass__. else: - @functools.wraps(original_init_subclass) - def __init_subclass__(*args, **kwargs): + def __init_subclass__(cls, *args, **kwargs): _wm.warn(msg, category=category, stacklevel=stacklevel + 1) - return original_init_subclass(*args, **kwargs) + return super(arg, cls).__init_subclass__(*args, **kwargs) - arg.__init_subclass__ = __init_subclass__ + arg.__init_subclass__ = classmethod(__init_subclass__) arg.__deprecated__ = __new__.__deprecated__ = msg __init_subclass__.__deprecated__ = msg diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index bc35823f70144e..b6d68f2372850a 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -213,17 +213,6 @@ def _need_normalize_century(): _normalize_century = True return _normalize_century -_supports_c99 = None -def _can_support_c99(): - global _supports_c99 - if _supports_c99 is None: - try: - _supports_c99 = ( - _time.strftime("%F", (1900, 1, 1, 0, 0, 0, 0, 1, 0)) == "1900-01-01") - except ValueError: - _supports_c99 = False - return _supports_c99 - # Correctly substitute for %z and %Z escapes in strftime formats. def _wrap_strftime(object, format, timetuple): # Don't call utcoffset() or tzname() unless actually needed. @@ -283,7 +272,7 @@ def _wrap_strftime(object, format, timetuple): newformat.append(Zreplace) # Note that datetime(1000, 1, 1).strftime('%G') == '1000' so # year 1000 for %G can go on the fast path. - elif ((ch in 'YG' or ch in 'FC' and _can_support_c99()) and + elif ((ch in 'YG' or ch in 'FC') and object.year < 1000 and _need_normalize_century()): if ch == 'G': year = int(_time.strftime("%G", timetuple)) @@ -1083,7 +1072,11 @@ def fromisocalendar(cls, year, week, day): @classmethod def strptime(cls, date_string, format): - """Parse a date string according to the given format (like time.strptime()).""" + """Parse string according to the given date format (like time.strptime()). + + For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes + """ import _strptime return _strptime._strptime_datetime_date(cls, date_string, format) @@ -1120,6 +1113,8 @@ def strftime(self, format): Format using strftime(). Example: "%d/%m/%Y, %H:%M:%S" + For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes """ return _wrap_strftime(self, format, self.timetuple()) @@ -1310,7 +1305,7 @@ def __reduce__(self): class tzinfo: - """Abstract base class for time zone info classes. + """Abstract base class for time zone info objects. Subclasses must override the tzname(), utcoffset() and dst() methods. """ @@ -1467,8 +1462,13 @@ def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold return self @classmethod + def strptime(cls, date_string, format): - """string, format -> new time parsed from a string (like time.strptime()).""" + """Parse string according to the given time format (like time.strptime()). + + For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes + """ import _strptime return _strptime._strptime_datetime_time(cls, date_string, format) @@ -1661,6 +1661,9 @@ def fromisoformat(cls, time_string): def strftime(self, format): """Format using strftime(). The date part of the timestamp passed to underlying strftime should not be used. + + For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes """ # The year must be >= 1000 else Python's strftime implementation # can raise a bogus exception. @@ -1776,7 +1779,7 @@ def __reduce__(self): class datetime(date): - """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]]) + """A combination of a date and a time. The year, month and day arguments are required. tzinfo may be None, or an instance of a tzinfo subclass. The remaining arguments may be ints. @@ -2209,7 +2212,11 @@ def __str__(self): @classmethod def strptime(cls, date_string, format): - 'string, format -> new datetime parsed from a string (like time.strptime()).' + """Parse string according to the given time format (like time.strptime()). + + For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes + """ import _strptime return _strptime._strptime_datetime_datetime(cls, date_string, format) @@ -2435,6 +2442,8 @@ def _isoweek1monday(year): class timezone(tzinfo): + """Fixed offset from UTC implementation of tzinfo.""" + __slots__ = '_offset', '_name' # Sentinel value to disallow None diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 9b8e42a2342536..8c0afd14d616e8 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -47,13 +47,16 @@ 'HAVE_THREADS', # C version: compile time choice that enables the coroutine local context - 'HAVE_CONTEXTVAR' + 'HAVE_CONTEXTVAR', + + # Highest version of the spec this module complies with + 'SPEC_VERSION', ] __xname__ = __name__ # sys.modules lookup (--without-threads) __name__ = 'decimal' # For pickling -__version__ = '1.70' # Highest version of the spec this complies with - # See http://speleotrove.com/decimal/ +SPEC_VERSION = '1.70' # Highest version of the spec this complies with + # See https://speleotrove.com/decimal/decarith.html __libmpdec_version__ = "2.4.2" # compatible libmpdec version import math as _math @@ -104,8 +107,8 @@ class DecimalException(ArithmeticError): anything, though. handle -- Called when context._raise_error is called and the - trap_enabler is not set. First argument is self, second is the - context. More arguments can be given, those being after + trap_enabler is not set. First argument is self, second is + the context. More arguments can be given, those being after the explanation in _raise_error (For example, context._raise_error(NewError, '(-x)!', self._sign) would call NewError().handle(context, self._sign).) @@ -222,11 +225,12 @@ class InvalidContext(InvalidOperation): """Invalid context. Unknown rounding, for example. This occurs and signals invalid-operation if an invalid context was - detected during an operation. This can occur if contexts are not checked - on creation and either the precision exceeds the capability of the - underlying concrete representation or an unknown or unsupported rounding - was specified. These aspects of the context need only be checked when - the values are required to be used. The result is [0,qNaN]. + detected during an operation. This can occur if contexts are not + checked on creation and either the precision exceeds the capability of + the underlying concrete representation or an unknown or unsupported + rounding was specified. These aspects of the context need only be + checked when the values are required to be used. The result is + [0,qNaN]. """ def handle(self, context, *args): @@ -319,8 +323,9 @@ class FloatOperation(DecimalException, TypeError): Decimal.from_float() or context.create_decimal_from_float() do not set the flag. - Otherwise (the signal is trapped), only equality comparisons and explicit - conversions are silent. All other mixed operations raise FloatOperation. + Otherwise (the signal is trapped), only equality comparisons and + explicit conversions are silent. All other mixed operations raise + FloatOperation. """ # List of public traps and flags @@ -2898,8 +2903,8 @@ def compare_total(self, other, context=None): """Compares self to other using the abstract representations. This is not like the standard compare, which use their numerical - value. Note that a total ordering is defined for all possible abstract - representations. + value. Note that a total ordering is defined for all possible + abstract representations. """ other = _convert_other(other, raiseit=True) @@ -2970,7 +2975,8 @@ def compare_total(self, other, context=None): def compare_total_mag(self, other, context=None): """Compares self to other using abstract repr., ignoring sign. - Like compare_total, but with operand's sign ignored and assumed to be 0. + Like compare_total, but with operand's sign ignored and assumed to + be 0. """ other = _convert_other(other, raiseit=True) @@ -3340,7 +3346,10 @@ def _fill_logical(self, context, opa, opb): return opa, opb def logical_and(self, other, context=None): - """Applies an 'and' operation between self and other's digits.""" + """Applies an 'and' operation between self and other's digits. + + Both self and other must be logical numbers. + """ if context is None: context = getcontext() @@ -3357,14 +3366,20 @@ def logical_and(self, other, context=None): return _dec_from_triple(0, result.lstrip('0') or '0', 0) def logical_invert(self, context=None): - """Invert all its digits.""" + """Invert all its digits. + + The self must be logical number. + """ if context is None: context = getcontext() return self.logical_xor(_dec_from_triple(0,'1'*context.prec,0), context) def logical_or(self, other, context=None): - """Applies an 'or' operation between self and other's digits.""" + """Applies an 'or' operation between self and other's digits. + + Both self and other must be logical numbers. + """ if context is None: context = getcontext() @@ -3381,7 +3396,10 @@ def logical_or(self, other, context=None): return _dec_from_triple(0, result.lstrip('0') or '0', 0) def logical_xor(self, other, context=None): - """Applies an 'xor' operation between self and other's digits.""" + """Applies an 'xor' operation between self and other's digits. + + Both self and other must be logical numbers. + """ if context is None: context = getcontext() @@ -4095,9 +4113,9 @@ def create_decimal_from_float(self, f): def abs(self, a): """Returns the absolute value of the operand. - If the operand is negative, the result is the same as using the minus - operation on the operand. Otherwise, the result is the same as using - the plus operation on the operand. + If the operand is negative, the result is the same as using the + minus operation on the operand. Otherwise, the result is the same + as using the plus operation on the operand. >>> ExtendedContext.abs(Decimal('2.1')) Decimal('2.1') @@ -4153,16 +4171,17 @@ def canonical(self, a): def compare(self, a, b): """Compares values numerically. - If the signs of the operands differ, a value representing each operand - ('-1' if the operand is less than zero, '0' if the operand is zero or - negative zero, or '1' if the operand is greater than zero) is used in - place of that operand for the comparison instead of the actual - operand. + If the signs of the operands differ, a value representing each + operand ('-1' if the operand is less than zero, '0' if the operand + is zero or negative zero, or '1' if the operand is greater than + zero) is used in place of that operand for the comparison instead of + the actual operand. - The comparison is then effected by subtracting the second operand from - the first and then returning a value according to the result of the - subtraction: '-1' if the result is less than zero, '0' if the result is - zero or negative zero, or '1' if the result is greater than zero. + The comparison is then effected by subtracting the second operand + from the first and then returning a value according to the result of + the subtraction: '-1' if the result is less than zero, '0' if the + result is zero or negative zero, or '1' if the result is greater + than zero. >>> ExtendedContext.compare(Decimal('2.1'), Decimal('3')) Decimal('-1') @@ -4225,8 +4244,8 @@ def compare_total(self, a, b): """Compares two operands using their abstract representation. This is not like the standard compare, which use their numerical - value. Note that a total ordering is defined for all possible abstract - representations. + value. Note that a total ordering is defined for all possible + abstract representations. >>> ExtendedContext.compare_total(Decimal('12.73'), Decimal('127.9')) Decimal('-1') @@ -4253,7 +4272,8 @@ def compare_total(self, a, b): def compare_total_mag(self, a, b): """Compares two operands using their abstract representation ignoring sign. - Like compare_total, but with operand's sign ignored and assumed to be 0. + Like compare_total, but with operand's sign ignored and assumed to + be 0. """ a = _convert_other(a, raiseit=True) return a.compare_total_mag(b) @@ -4911,8 +4931,8 @@ def multiply(self, a, b): If either operand is a special value then the general rules apply. Otherwise, the operands are multiplied together - ('long multiplication'), resulting in a number which may be as long as - the sum of the lengths of the two operands. + ('long multiplication'), resulting in a number which may be as long + as the sum of the lengths of the two operands. >>> ExtendedContext.multiply(Decimal('1.20'), Decimal('3')) Decimal('3.60') @@ -5188,19 +5208,19 @@ def quantize(self, a, b): """Returns a value equal to 'a' (rounded), having the exponent of 'b'. The coefficient of the result is derived from that of the left-hand - operand. It may be rounded using the current rounding setting (if the - exponent is being increased), multiplied by a positive power of ten (if - the exponent is being decreased), or is unchanged (if the exponent is - already equal to that of the right-hand operand). + operand. It may be rounded using the current rounding setting (if + the exponent is being increased), multiplied by a positive power of + ten (if the exponent is being decreased), or is unchanged (if the + exponent is already equal to that of the right-hand operand). Unlike other operations, if the length of the coefficient after the quantize operation would be greater than precision then an Invalid - operation condition is raised. This guarantees that, unless there is - an error condition, the exponent of the result of a quantize is always - equal to that of the right-hand operand. + operation condition is raised. This guarantees that, unless there + is an error condition, the exponent of the result of a quantize is + always equal to that of the right-hand operand. - Also unlike other operations, quantize will never raise Underflow, even - if the result is subnormal and inexact. + Also unlike other operations, quantize will never raise Underflow, + even if the result is subnormal and inexact. >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('0.001')) Decimal('2.170') @@ -5254,13 +5274,13 @@ def remainder(self, a, b): """Returns the remainder from integer division. The result is the residue of the dividend after the operation of - calculating integer division as described for divide-integer, rounded - to precision digits if necessary. The sign of the result, if - non-zero, is the same as that of the original dividend. + calculating integer division as described for divide-integer, + rounded to precision digits if necessary. The sign of the result, + if non-zero, is the same as that of the original dividend. - This operation will fail under the same conditions as integer division - (that is, if integer division on the same two operands would fail, the - remainder cannot be calculated). + This operation will fail under the same conditions as integer + division (that is, if integer division on the same two operands + would fail, the remainder cannot be calculated). >>> ExtendedContext.remainder(Decimal('2.1'), Decimal('3')) Decimal('2.1') @@ -5294,9 +5314,9 @@ def remainder_near(self, a, b): is chosen). If the result is equal to 0 then its sign will be the sign of a. - This operation will fail under the same conditions as integer division - (that is, if integer division on the same two operands would fail, the - remainder cannot be calculated). + This operation will fail under the same conditions as integer + division (that is, if integer division on the same two operands + would fail, the remainder cannot be calculated). >>> ExtendedContext.remainder_near(Decimal('2.1'), Decimal('3')) Decimal('-0.9') @@ -5354,8 +5374,8 @@ def rotate(self, a, b): def same_quantum(self, a, b): """Returns True if the two operands have the same exponent. - The result is never affected by either the sign or the coefficient of - either operand. + The result is never affected by either the sign or the coefficient + of either operand. >>> ExtendedContext.same_quantum(Decimal('2.17'), Decimal('0.001')) False @@ -5427,8 +5447,8 @@ def shift(self, a, b): def sqrt(self, a): """Square root of a non-negative number to context precision. - If the result must be inexact, it is rounded using the round-half-even - algorithm. + If the result must be inexact, it is rounded using the + round-half-even algorithm. >>> ExtendedContext.sqrt(Decimal('0')) Decimal('0') @@ -6387,3 +6407,11 @@ def _format_number(is_negative, intpart, fracpart, exp, spec): # _PyHASH_10INV is the inverse of 10 modulo the prime _PyHASH_MODULUS _PyHASH_10INV = pow(10, _PyHASH_MODULUS - 2, _PyHASH_MODULUS) del sys + +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return SPEC_VERSION + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 5db8ce9244b5ba..993f94bc055b2e 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -10,7 +10,7 @@ import sys # Import _thread instead of threading to reduce startup cost from _thread import allocate_lock as Lock -if sys.platform in {'win32', 'cygwin'}: +if sys.platform == 'win32': from msvcrt import setmode as _setmode else: _setmode = None @@ -83,27 +83,28 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, wrapped. (If a file descriptor is given, it is closed when the returned I/O object is closed, unless closefd is set to False.) - mode is an optional string that specifies the mode in which the file is - opened. It defaults to 'r' which means open for reading in text mode. Other - common values are 'w' for writing (truncating the file if it already - exists), 'x' for exclusive creation of a new file, and 'a' for appending - (which on some Unix systems, means that all writes append to the end of the - file regardless of the current seek position). In text mode, if encoding is - not specified the encoding used is platform dependent. (For reading and - writing raw bytes use binary mode and leave encoding unspecified.) The - available modes are: - - ========= =============================================================== + mode is an optional string that specifies the mode in which the file + is opened. It defaults to 'r' which means open for reading in text + mode. Other common values are 'w' for writing (truncating the file if + it already exists), 'x' for exclusive creation of a new file, and + 'a' for appending (which on some Unix systems, means that all writes + append to the end of the file regardless of the current seek position). + In text mode, if encoding is not specified the encoding used is platform + dependent. (For reading and writing raw bytes use binary mode and leave + encoding unspecified.) The available modes are: + + ========= ========================================================== Character Meaning - --------- --------------------------------------------------------------- + --------- ---------------------------------------------------------- 'r' open for reading (default) 'w' open for writing, truncating the file first 'x' create a new file and open it for writing - 'a' open for writing, appending to the end of the file if it exists + 'a' open for writing, appending to the end of the file if it + exists 'b' binary mode 't' text mode (default) '+' open a disk file for updating (reading and writing) - ========= =============================================================== + ========= ========================================================== The default mode is 'rt' (open for reading text). For binary random access, the mode 'w+b' opens and truncates the file to 0 bytes, while @@ -111,23 +112,23 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, raises an `FileExistsError` if the file already exists. Python distinguishes between files opened in binary and text modes, - even when the underlying operating system doesn't. Files opened in + even when the underlying operating system doesn't. Files opened in binary mode (appending 'b' to the mode argument) return contents as - bytes objects without any decoding. In text mode (the default, or when + bytes objects without any decoding. In text mode (the default, or when 't' is appended to the mode argument), the contents of the file are returned as strings, the bytes having been first decoded using a platform-dependent encoding or using the specified encoding if given. buffering is an optional integer used to set the buffering policy. - Pass 0 to switch buffering off (only allowed in binary mode), 1 to select - line buffering (only usable in text mode), and an integer > 1 to indicate - the size of a fixed-size chunk buffer. When no buffering argument is - given, the default buffering policy works as follows: + Pass 0 to switch buffering off (only allowed in binary mode), 1 to + select line buffering (only usable in text mode), and an integer > 1 to + indicate the size of a fixed-size chunk buffer. When no buffering + argument is given, the default buffering policy works as follows: - * Binary files are buffered in fixed-size chunks; the size of the buffer - is max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE) - when the device block size is available. - On most systems, the buffer will typically be 128 kilobytes long. + * Binary files are buffered in fixed-size chunks; the size of the buffer + is max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE) when the device + block size is available. + On most systems, the buffer will typically be 128 kilobytes long. * "Interactive" text files (files for which isatty() returns True) use line buffering. Other text files use the policy described above @@ -147,8 +148,8 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, encoding error strings. newline is a string controlling how universal newlines works (it only - applies to text mode). It can be None, '', '\n', '\r', and '\r\n'. It works - as follows: + applies to text mode). It can be None, '', '\n', '\r', and '\r\n'. It + works as follows: * On input, if newline is None, universal newlines mode is enabled. Lines in the input can end in '\n', '\r', or '\r\n', and @@ -164,17 +165,17 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, other legal values, any '\n' characters written are translated to the given string. - closedfd is a bool. If closefd is False, the underlying file descriptor will - be kept open when the file is closed. This does not work when a file name is - given and must be True in that case. + closedfd is a bool. If closefd is False, the underlying file descriptor + will be kept open when the file is closed. This does not work when + a file name is given and must be True in that case. The newly created file is non-inheritable. A custom opener can be used by passing a callable as *opener*. The - underlying file descriptor for the file object is then obtained by calling - *opener* with (*file*, *flags*). *opener* must return an open file - descriptor (passing os.open as *opener* results in functionality similar to - passing None). + underlying file descriptor for the file object is then obtained by + calling *opener* with (*file*, *flags*). *opener* must return an open + file descriptor (passing os.open as *opener* results in functionality + similar to passing None). open() returns a file object whose type depends on the mode, and through which the standard file operations such as reading and writing @@ -351,10 +352,12 @@ def seek(self, pos, whence=0): interpreted relative to the position indicated by whence. Values for whence are ints: - * 0 -- start of stream (the default); offset should be zero or positive + * 0 -- start of stream (the default); offset should be zero or + positive * 1 -- current stream position; offset may be negative * 2 -- end of stream; offset is usually negative - Some operating systems / file systems could provide additional values. + Some operating systems / file systems could provide additional + values. Return an int indicating the new absolute position. """ @@ -367,8 +370,8 @@ def tell(self): def truncate(self, pos=None): """Truncate file to size bytes. - Size defaults to the current IO position as reported by tell(). Return - the new size. + Size defaults to the current IO position as reported by tell(). + Return the new size. """ self._unsupported("truncate") @@ -492,7 +495,8 @@ def __exit__(self, *args): def fileno(self): """Returns underlying file descriptor (an int) if one exists. - An OSError is raised if the IO object does not use a file descriptor. + An OSError is raised if the IO object does not use a file + descriptor. """ self._unsupported("fileno") @@ -546,7 +550,7 @@ def nreadahead(): res += b if res.endswith(b"\n"): break - return bytes(res) + return res.take_bytes() def __iter__(self): self._checkClosed() @@ -617,8 +621,10 @@ def read(self, size=-1): n = self.readinto(b) if n is None: return None + if n < 0 or n > len(b): + raise ValueError(f"readinto returned {n} outside buffer size {len(b)}") del b[n:] - return bytes(b) + return b.take_bytes() def readall(self): """Read until EOF, using multiple read() call.""" @@ -626,7 +632,7 @@ def readall(self): while data := self.read(DEFAULT_BUFFER_SIZE): res += data if res: - return bytes(res) + return res.take_bytes() else: # b'' or None return data @@ -939,7 +945,7 @@ def read(self, size=-1): newpos = min(len(self._buffer), self._pos + size) b = self._buffer[self._pos : newpos] self._pos = newpos - return bytes(b) + return b.take_bytes() def read1(self, size=-1): """This is the same as read. @@ -947,23 +953,24 @@ def read1(self, size=-1): return self.read(size) def write(self, b): - if self.closed: - raise ValueError("write to closed file") if isinstance(b, str): raise TypeError("can't write str to binary stream") with memoryview(b) as view: + if self.closed: + raise ValueError("write to closed file") + n = view.nbytes # Size of any bytes-like object - if n == 0: - return 0 + if n == 0: + return 0 - with self._lock: - pos = self._pos - if pos > len(self._buffer): - # Pad buffer to pos with null bytes. - self._buffer.resize(pos) - self._buffer[pos:pos + n] = b - self._pos += n - return n + with self._lock: + pos = self._pos + if pos > len(self._buffer): + # Pad buffer to pos with null bytes. + self._buffer.resize(pos) + self._buffer[pos:pos + n] = view + self._pos += n + return n def seek(self, pos, whence=0): if self.closed: @@ -1498,20 +1505,26 @@ class FileIO(RawIOBase): _writable = False _appending = False _seekable = None + _truncate = False _closefd = True def __init__(self, file, mode='r', closefd=True, opener=None): - """Open a file. The mode can be 'r' (default), 'w', 'x' or 'a' for reading, - writing, exclusive creation or appending. The file will be created if it - doesn't exist when opened for writing or appending; it will be truncated - when opened for writing. A FileExistsError will be raised if it already - exists when opened for creating. Opening a file for creating implies - writing so this mode behaves in a similar way to 'w'. Add a '+' to the mode - to allow simultaneous reading and writing. A custom opener can be used by - passing a callable as *opener*. The underlying file descriptor for the file - object is then obtained by calling opener with (*name*, *flags*). - *opener* must return an open file descriptor (passing os.open as *opener* - results in functionality similar to passing None). + """Open a file. + + The mode can be 'r' (default), 'w', 'x' or 'a' for reading, + writing, exclusive creation or appending. The file will be created + if it doesn't exist when opened for writing or appending; it will be + truncated when opened for writing. A FileExistsError will be raised + if it already exists when opened for creating. Opening a file for + creating implies writing so this mode behaves in a similar way to + 'w'. Add a '+' to the mode to allow simultaneous reading and + writing. + + A custom opener can be used by passing a callable as *opener*. + The underlying file descriptor for the file object is then obtained + by calling opener with (*name*, *flags*). *opener* must return + an open file descriptor (passing os.open as *opener* results in + functionality similar to passing None). """ if self._fd >= 0: # Have to close the existing file first. @@ -1553,6 +1566,7 @@ def __init__(self, file, mode='r', closefd=True, opener=None): flags = 0 elif 'w' in mode: self._writable = True + self._truncate = True flags = os.O_CREAT | os.O_TRUNC elif 'a' in mode: self._writable = True @@ -1734,7 +1748,7 @@ def readall(self): assert len(result) - bytes_read >= 1, \ "os.readinto buffer size 0 will result in erroneous EOF / returns 0" result.resize(bytes_read) - return bytes(result) + return result.take_bytes() def readinto(self, buffer): """Same as RawIOBase.readinto().""" @@ -1749,8 +1763,8 @@ def write(self, b): """Write bytes b to file, return number written. Only makes one system call, so not all of the data may be written. - The number of bytes actually written is returned. In non-blocking mode, - returns None if the write would block. + The number of bytes actually written is returned. In non-blocking + mode, returns None if the write would block. """ self._checkClosed() self._checkWritable() @@ -1762,11 +1776,12 @@ def write(self, b): def seek(self, pos, whence=SEEK_SET): """Move to new file position. - Argument offset is a byte count. Optional argument whence defaults to - SEEK_SET or 0 (offset from start of file, offset should be >= 0); other values - are SEEK_CUR or 1 (move relative to current position, positive or negative), - and SEEK_END or 2 (move relative to end of file, usually negative, although - many platforms allow seeking beyond the end of a file). + Argument offset is a byte count. Optional argument whence defaults + to SEEK_SET or 0 (offset from start of file, offset should be >= 0); + other values are SEEK_CUR or 1 (move relative to current position, + positive or negative), and SEEK_END or 2 (move relative to end of + file, usually negative, although many platforms allow seeking beyond + the end of a file). Note that not all file objects are seekable. """ @@ -1799,8 +1814,8 @@ def truncate(self, size=None): def close(self): """Close the file. - A closed file cannot be used for further I/O operations. close() may be - called more than once without error. + A closed file cannot be used for further I/O operations. + close() may be called more than once without error. """ if not self.closed: self._stat_atopen = None @@ -1877,7 +1892,10 @@ def mode(self): return 'ab' elif self._readable: if self._writable: - return 'rb+' + if self._truncate: + return 'wb+' + else: + return 'rb+' else: return 'rb' else: @@ -1895,8 +1913,8 @@ class TextIOBase(IOBase): def read(self, size=-1): """Read at most size characters from stream, where size is an int. - Read from underlying buffer until we have size characters or we hit EOF. - If size is negative or omitted, read until EOF. + Read from underlying buffer until we have size characters or we hit + EOF. If size is negative or omitted, read until EOF. Returns a string. """ diff --git a/Lib/_pyrepl/_module_completer.py b/Lib/_pyrepl/_module_completer.py index 1e9462a42156d4..17bf5cdc819542 100644 --- a/Lib/_pyrepl/_module_completer.py +++ b/Lib/_pyrepl/_module_completer.py @@ -1,19 +1,47 @@ from __future__ import annotations +import importlib +import os import pkgutil +import re import sys import token import tokenize +from importlib.machinery import FileFinder from io import StringIO from contextlib import contextmanager from dataclasses import dataclass from itertools import chain from tokenize import TokenInfo +from .fancycompleter import safe_getattr TYPE_CHECKING = False if TYPE_CHECKING: + from types import ModuleType from typing import Any, Iterable, Iterator, Mapping + from .types import CompletionAction + + +HARDCODED_SUBMODULES = { + # Standard library submodules that are not detected by pkgutil.iter_modules + # but can be imported, so should be proposed in completion + "collections": ["abc"], + "math": ["integer"], + "os": ["path"], + "xml.parsers.expat": ["errors", "model"], +} + +AUTO_IMPORT_DENYLIST = { + # Standard library modules/submodules that have import side effects + # and must not be automatically imported to complete attributes + re.compile(r"antigravity"), # Calls webbrowser.open + re.compile(r"idlelib\..+"), # May open IDLE GUI + re.compile(r"test\..+"), # Various side-effects + re.compile(r"this"), # Prints to stdout + re.compile(r"_ios_support"), # Spawns a subprocess + re.compile(r".+\.__main__"), # Should not be imported +} def make_default_module_completer() -> ModuleCompleter: @@ -40,36 +68,73 @@ class ModuleCompleter: def __init__(self, namespace: Mapping[str, Any] | None = None) -> None: self.namespace = namespace or {} self._global_cache: list[pkgutil.ModuleInfo] = [] + self._failed_imports: set[str] = set() self._curr_sys_path: list[str] = sys.path[:] + self._stdlib_path = os.path.dirname(importlib.__path__[0]) - def get_completions(self, line: str) -> list[str] | None: - """Return the next possible import completions for 'line'.""" + def get_completions( + self, line: str, *, include_values: bool = True + ) -> tuple[list[str], list[Any], CompletionAction | None] | None: + """Return the next possible import completions for 'line'. + + For attributes completion, if the module to complete from is not + imported, also return an action (prompt + callback to run if the + user press TAB again) to import the module. + + If *include_values* is false, the returned values list is empty and + attribute values are not resolved. + """ result = ImportParser(line).parse() if not result: return None try: - return self.complete(*result) + return self.complete(*result, include_values=include_values) except Exception: # Some unexpected error occurred, make it look like # no completions are available - return [] - - def complete(self, from_name: str | None, name: str | None) -> list[str]: + return [], [], None + + def complete( + self, + from_name: str | None, + name: str | None, + *, + include_values: bool = True, + ) -> tuple[list[str], list[Any], CompletionAction | None]: if from_name is None: # import x.y.z assert name is not None path, prefix = self.get_path_and_prefix(name) modules = self.find_modules(path, prefix) - return [self.format_completion(path, module) for module in modules] + names = [self.format_completion(path, module) for module in modules] + # These are always modules, use dummy values to get the right color + values = [sys] * len(names) if include_values else [] + return names, values, None if name is None: # from x.y.z path, prefix = self.get_path_and_prefix(from_name) modules = self.find_modules(path, prefix) - return [self.format_completion(path, module) for module in modules] + names = [self.format_completion(path, module) for module in modules] + # These are always modules, use dummy values to get the right color + values = [sys] * len(names) if include_values else [] + return names, values, None # from x.y import z - return self.find_modules(from_name, name) + submodules = self.find_modules(from_name, name) + attr_names, attr_module, action = self._find_attributes(from_name, name) + all_names = sorted({*submodules, *attr_names}) + if not include_values: + return all_names, [], action + + # Build values list matching the sorted order: + # submodules use `sys` as a dummy value so they get the 'module' color, + # attributes use their actual value. + attr_map = {} + if attr_module is not None: + attr_map = {n: safe_getattr(attr_module, n) for n in attr_names} + all_values = [attr_map[n] if n in attr_map else sys for n in all_names] + return all_names, all_values, action def find_modules(self, path: str, prefix: str) -> list[str]: """Find all modules under 'path' that start with 'prefix'.""" @@ -87,20 +152,86 @@ def _find_modules(self, path: str, prefix: str) -> list[str]: if self.is_suggestion_match(module.name, prefix)] return sorted(builtin_modules + third_party_modules) - if path.startswith('.'): - # Convert relative path to absolute path - package = self.namespace.get('__package__', '') - path = self.resolve_relative_name(path, package) # type: ignore[assignment] - if path is None: - return [] + path = self._resolve_relative_path(path) # type: ignore[assignment] + if path is None: + return [] modules: Iterable[pkgutil.ModuleInfo] = self.global_cache + imported_module = sys.modules.get(path.split('.')[0]) + if imported_module: + # Filter modules to those whose name and specs match the + # imported module to avoid invalid suggestions + spec = imported_module.__spec__ + if spec: + def _safe_find_spec(mod: pkgutil.ModuleInfo) -> bool: + try: + return mod.module_finder.find_spec(mod.name, None) == spec + except Exception: + return False + modules = [mod for mod in modules + if mod.name == spec.name + and _safe_find_spec(mod)] + else: + modules = [] + + is_stdlib_import: bool | None = None for segment in path.split('.'): modules = [mod_info for mod_info in modules if mod_info.ispkg and mod_info.name == segment] + if is_stdlib_import is None: + # Top-level import decide if we import from stdlib or not + is_stdlib_import = all( + self._is_stdlib_module(mod_info) for mod_info in modules + ) modules = self.iter_submodules(modules) - return [module.name for module in modules - if self.is_suggestion_match(module.name, prefix)] + + module_names = [module.name for module in modules] + if is_stdlib_import: + module_names.extend(HARDCODED_SUBMODULES.get(path, ())) + return [module_name for module_name in module_names + if self.is_suggestion_match(module_name, prefix)] + + def _is_stdlib_module(self, module_info: pkgutil.ModuleInfo) -> bool: + return (isinstance(module_info.module_finder, FileFinder) + and module_info.module_finder.path == self._stdlib_path) + + def find_attributes( + self, path: str, prefix: str + ) -> tuple[list[str], list[Any], CompletionAction | None]: + """Find all attributes of module 'path' that start with 'prefix'.""" + attributes, module, action = self._find_attributes(path, prefix) + if module is not None: + values = [safe_getattr(module, attr) for attr in attributes] + else: + values = [] + return attributes, values, action + + def _find_attributes( + self, path: str, prefix: str + ) -> tuple[list[str], ModuleType | None, CompletionAction | None]: + path = self._resolve_relative_path(path) # type: ignore[assignment] + if path is None: + return [], None, None + + imported_module = sys.modules.get(path) + if not imported_module: + if path in self._failed_imports: # Do not propose to import again + return [], None, None + imported_module = self._maybe_import_module(path) + if not imported_module: + return [], None, self._get_import_completion_action(path) + try: + module_attributes = dir(imported_module) + except Exception: + module_attributes = [] + # Filter out invalid attribute names, such as dashes that cannot be + # imported with 'import'. + names = [ + attr_name for attr_name in module_attributes + if (self.is_suggestion_match(attr_name, prefix) + and attr_name.isidentifier()) + ] + return names, imported_module, None def is_suggestion_match(self, module_name: str, prefix: str) -> bool: if prefix: @@ -146,6 +277,13 @@ def format_completion(self, path: str, module: str) -> str: return f'{path}{module}' return f'{path}.{module}' + def _resolve_relative_path(self, path: str) -> str | None: + """Resolve a relative import path to absolute. Returns None if unresolvable.""" + if path.startswith('.'): + package = self.namespace.get('__package__', '') + return self.resolve_relative_name(path, package) + return path + def resolve_relative_name(self, name: str, package: str) -> str | None: """Resolve a relative module name to an absolute name. @@ -169,10 +307,40 @@ def global_cache(self) -> list[pkgutil.ModuleInfo]: """Global module cache""" if not self._global_cache or self._curr_sys_path != sys.path: self._curr_sys_path = sys.path[:] - # print('getting packages') self._global_cache = list(pkgutil.iter_modules()) + self._failed_imports.clear() # retry on sys.path change return self._global_cache + def _maybe_import_module(self, fqname: str) -> ModuleType | None: + if any(pattern.fullmatch(fqname) for pattern in AUTO_IMPORT_DENYLIST): + # Special-cased modules with known import side-effects + return None + root = fqname.split(".")[0] + mod_info = next((m for m in self.global_cache if m.name == root), None) + if not mod_info or not self._is_stdlib_module(mod_info): + # Only import stdlib modules (no risk of import side-effects) + return None + try: + return importlib.import_module(fqname) + except Exception: + sys.modules.pop(fqname, None) # Clean half-imported module + return None + + def _get_import_completion_action(self, path: str) -> CompletionAction: + prompt = ("[ module not imported, press again to import it " + "and propose attributes ]") + + def _do_import() -> str | None: + try: + importlib.import_module(path) + return None + except Exception as exc: + sys.modules.pop(path, None) # Clean half-imported module + self._failed_imports.add(path) + return f"[ error during import: {exc} ]" + + return (prompt, _do_import) + class ImportParser: """ diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index 50c824995d85b8..e79fbfa6bb0b38 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -22,6 +22,7 @@ from __future__ import annotations import os import time +from typing import TYPE_CHECKING # Categories of actions: # killing @@ -32,10 +33,11 @@ # finishing # [completion] +from .render import RenderedScreen from .trace import trace # types -if False: +if TYPE_CHECKING: from .historical_reader import HistoricalReader @@ -74,7 +76,7 @@ def kill_range(self, start: int, end: int) -> None: else: r.kill_ring.append(text) r.pos = start - r.dirty = True + r.invalidate_buffer(start) class YankCommand(Command): @@ -125,24 +127,27 @@ def do(self) -> None: r.arg = 10 * r.arg - d else: r.arg = 10 * r.arg + d - r.dirty = True + r.invalidate_prompt() class clear_screen(Command): def do(self) -> None: r = self.reader + trace("command.clear_screen") r.console.clear() - r.dirty = True + r.invalidate_full() class refresh(Command): def do(self) -> None: - self.reader.dirty = True + trace("command.refresh") + self.reader.invalidate_full() class repaint(Command): def do(self) -> None: - self.reader.dirty = True + trace("command.repaint") + self.reader.invalidate_full() self.reader.console.repaint() @@ -208,9 +213,10 @@ def do(self) -> None: repl = len(r.kill_ring[-1]) r.kill_ring.insert(0, r.kill_ring.pop()) t = r.kill_ring[-1] + start = r.pos - repl b[r.pos - repl : r.pos] = t r.pos = r.pos - repl + len(t) - r.dirty = True + r.invalidate_buffer(start) class interrupt(FinishCommand): @@ -242,8 +248,9 @@ def do(self) -> None: r.console.prepare() r.pos = p # r.posxy = 0, 0 # XXX this is invalid - r.dirty = True - r.console.screen = [] + r.invalidate_full() + trace("command.suspend sync_rendered_screen") + r.console.sync_rendered_screen(RenderedScreen.empty(), r.console.posxy) class up(MotionCommand): @@ -369,6 +376,7 @@ class self_insert(EditCommand): def do(self) -> None: r = self.reader text = self.event * r.get_arg() + start = r.pos r.insert(text) if r.paste_mode: data = "" @@ -376,7 +384,7 @@ def do(self) -> None: data += ev.data if data: r.insert(data) - r.last_refresh_cache.invalidated = True + r.invalidate_buffer(start) class insert_nl(EditCommand): @@ -400,40 +408,49 @@ def do(self) -> None: del b[s] b.insert(t, c) r.pos = t - r.dirty = True + r.invalidate_buffer(s) class backspace(EditCommand): def do(self) -> None: r = self.reader b = r.buffer + changed_from: int | None = None for i in range(r.get_arg()): if r.pos > 0: r.pos -= 1 del b[r.pos] - r.dirty = True + changed_from = r.pos if changed_from is None else min(changed_from, r.pos) else: self.reader.error("can't backspace at start") + if changed_from is not None: + r.invalidate_buffer(changed_from) class delete(EditCommand): def do(self) -> None: r = self.reader b = r.buffer - if ( - r.pos == 0 - and len(b) == 0 # this is something of a hack - and self.event[-1] == "\004" - ): - r.update_screen() - r.console.finish() - raise EOFError + if self.event[-1] == "\004": + if b and b[-1].endswith("\n"): + self.finish = True + elif ( + r.pos == 0 + and len(b) == 0 # this is something of a hack + ): + r.update_screen() + r.console.finish() + raise EOFError + + changed_from: int | None = None for i in range(r.get_arg()): if r.pos != len(b): del b[r.pos] - r.dirty = True + changed_from = r.pos if changed_from is None else min(changed_from, r.pos) else: self.reader.error("end of buffer") + if changed_from is not None: + r.invalidate_buffer(changed_from) class accept(FinishCommand): @@ -447,6 +464,7 @@ def do(self) -> None: with self.reader.suspend(): self.reader.msg = _sitebuiltins._Helper()() # type: ignore[assignment] + self.reader.invalidate_prompt() class invalid_key(Command): @@ -467,22 +485,24 @@ def do(self) -> None: from .pager import get_pager from site import gethistoryfile + # After the pager exits, the screen state is unknown (Unix may + # restore via alternate screen, Windows shows pager output). + # Clear and force a full redraw at the end for consistency. + self.reader.console.clear() + history = os.linesep.join(self.reader.history[:]) self.reader.console.restore() pager = get_pager() pager(history, gethistoryfile()) self.reader.console.prepare() - # We need to copy over the state so that it's consistent between - # console and reader, and console does not overwrite/append stuff - self.reader.console.screen = self.reader.screen.copy() - self.reader.console.posxy = self.reader.cxy + self.reader.invalidate_full() class paste_mode(Command): def do(self) -> None: self.reader.paste_mode = not self.reader.paste_mode - self.reader.dirty = True + self.reader.invalidate_prompt() class perform_bracketed_paste(Command): @@ -499,4 +519,3 @@ def do(self) -> None: s=time.time() - start, ) self.reader.insert(data.replace(done, "")) - self.reader.last_refresh_cache.invalidated = True diff --git a/Lib/_pyrepl/completing_reader.py b/Lib/_pyrepl/completing_reader.py index 9d2d43be5144e8..f783e8db36b028 100644 --- a/Lib/_pyrepl/completing_reader.py +++ b/Lib/_pyrepl/completing_reader.py @@ -21,16 +21,18 @@ from __future__ import annotations from dataclasses import dataclass, field +from typing import TYPE_CHECKING import re from . import commands, console, reader +from .render import RenderLine, ScreenOverlay from .reader import Reader # types Command = commands.Command -if False: - from .types import KeySpec, CommandName +if TYPE_CHECKING: + from .types import CommandName, CompletionAction, Keymap, KeySpec def prefix(wordlist: list[str], j: int = 0) -> str: @@ -168,39 +170,68 @@ def do(self) -> None: r: CompletingReader r = self.reader # type: ignore[assignment] last_is_completer = r.last_command_is(self.__class__) + if r.cmpltn_action: + if last_is_completer: # double-tab: execute action + msg = r.cmpltn_action[1]() + r.cmpltn_action = None # consumed + if msg: + r.msg = msg + r.cmpltn_message_visible = True + r.invalidate_message() + else: # other input since last tab: cancel action + r.cmpltn_action = None + immutable_completions = r.assume_immutable_completions completions_unchangable = last_is_completer and immutable_completions stem = r.get_stem() if not completions_unchangable: - r.cmpltn_menu_choices = r.get_completions(stem) + r.cmpltn_menu_choices, r.cmpltn_action = r.get_completions(stem) completions = r.cmpltn_menu_choices if not completions: - r.error("no matches") + if not r.cmpltn_action: + r.error("no matches") elif len(completions) == 1: - if completions_unchangable and len(completions[0]) == len(stem): + completion = stripcolor(completions[0]) + if completions_unchangable and len(completion) == len(stem): r.msg = "[ sole completion ]" - r.dirty = True - r.insert(completions[0][len(stem):]) + r.cmpltn_message_visible = True + r.invalidate_message() + r.insert(completion[len(stem):]) else: - p = prefix(completions, len(stem)) + clean_completions = [stripcolor(word) for word in completions] + p = prefix(clean_completions, len(stem)) if p: r.insert(p) if last_is_completer: r.cmpltn_menu_visible = True - r.cmpltn_message_visible = False r.cmpltn_menu, r.cmpltn_menu_end = build_menu( r.console, completions, r.cmpltn_menu_end, r.use_brackets, r.sort_in_column) - r.dirty = True + if r.msg: + r.msg = "" + r.cmpltn_message_visible = False + r.invalidate_message() + r.invalidate_overlay() elif not r.cmpltn_menu_visible: - r.cmpltn_message_visible = True - if stem + p in completions: + if stem + p in clean_completions: r.msg = "[ complete but not unique ]" - r.dirty = True + r.cmpltn_message_visible = True + r.invalidate_message() else: r.msg = "[ not unique ]" - r.dirty = True + r.cmpltn_message_visible = True + r.invalidate_message() + + if r.cmpltn_action: + if r.msg and r.cmpltn_message_visible: + # There is already a message (eg. [ not unique ]) that + # would conflict for next tab: cancel action + r.cmpltn_action = None + else: + r.msg = r.cmpltn_action[0] + r.cmpltn_message_visible = True + r.invalidate_message() class self_insert(commands.self_insert): @@ -215,11 +246,12 @@ def do(self) -> None: r.cmpltn_reset() else: completions = [w for w in r.cmpltn_menu_choices - if w.startswith(stem)] + if stripcolor(w).startswith(stem)] if completions: r.cmpltn_menu, r.cmpltn_menu_end = build_menu( r.console, completions, 0, r.use_brackets, r.sort_in_column) + r.invalidate_overlay() else: r.cmpltn_reset() @@ -240,6 +272,7 @@ class CompletingReader(Reader): cmpltn_message_visible: bool = field(init=False) cmpltn_menu_end: int = field(init=False) cmpltn_menu_choices: list[str] = field(init=False) + cmpltn_action: CompletionAction | None = field(init=False) def __post_init__(self) -> None: super().__post_init__() @@ -248,7 +281,7 @@ def __post_init__(self) -> None: self.commands[c.__name__] = c self.commands[c.__name__.replace('_', '-')] = c - def collect_keymap(self) -> tuple[tuple[KeySpec, CommandName], ...]: + def collect_keymap(self) -> Keymap: return super().collect_keymap() + ( (r'\t', 'complete'),) @@ -257,30 +290,30 @@ def after_command(self, cmd: Command) -> None: if not isinstance(cmd, (complete, self_insert)): self.cmpltn_reset() - def calc_screen(self) -> list[str]: - screen = super().calc_screen() - if self.cmpltn_menu_visible: - # We display the completions menu below the current prompt - ly = self.lxy[1] + 1 - screen[ly:ly] = self.cmpltn_menu - # If we're not in the middle of multiline edit, don't append to screeninfo - # since that screws up the position calculation in pos2xy function. - # This is a hack to prevent the cursor jumping - # into the completions menu when pressing left or down arrow. - if self.pos != len(self.buffer): - self.screeninfo[ly:ly] = [(0, [])]*len(self.cmpltn_menu) - return screen + def get_screen_overlays(self) -> tuple[ScreenOverlay, ...]: + if not self.cmpltn_menu_visible: + return () + return ( + ScreenOverlay( + self.lxy[1] + 1, + tuple(RenderLine.from_rendered_text(line) for line in self.cmpltn_menu), + insert=True, + ), + ) def finish(self) -> None: super().finish() self.cmpltn_reset() def cmpltn_reset(self) -> None: + if getattr(self, "cmpltn_menu_visible", False): + self.invalidate_overlay() self.cmpltn_menu = [] self.cmpltn_menu_visible = False self.cmpltn_message_visible = False self.cmpltn_menu_end = 0 self.cmpltn_menu_choices = [] + self.cmpltn_action = None def get_stem(self) -> str: st = self.syntax_table @@ -291,8 +324,8 @@ def get_stem(self) -> str: p -= 1 return ''.join(b[p+1:self.pos]) - def get_completions(self, stem: str) -> list[str]: - return [] + def get_completions(self, stem: str) -> tuple[list[str], CompletionAction | None]: + return [], None def get_line(self) -> str: """Return the current line until the cursor position.""" diff --git a/Lib/_pyrepl/console.py b/Lib/_pyrepl/console.py index e0535d50396316..2a53d5ff581fa2 100644 --- a/Lib/_pyrepl/console.py +++ b/Lib/_pyrepl/console.py @@ -19,23 +19,25 @@ from __future__ import annotations +import os import _colorize from abc import ABC, abstractmethod import ast import code import linecache -from dataclasses import dataclass, field -import os.path +from dataclasses import dataclass import re import sys +from typing import TYPE_CHECKING - -TYPE_CHECKING = False +from .render import RenderedScreen +from .trace import trace if TYPE_CHECKING: - from typing import IO - from typing import Callable + from typing import Callable, IO + + from .types import CursorXY @dataclass @@ -47,10 +49,17 @@ class Event: @dataclass class Console(ABC): - posxy: tuple[int, int] - screen: list[str] = field(default_factory=list) + posxy: CursorXY = (0, 0) height: int = 25 width: int = 80 + _redraw_debug_palette: tuple[str, ...] = ( + "\x1b[41m", + "\x1b[42m", + "\x1b[43m", + "\x1b[44m", + "\x1b[45m", + "\x1b[46m", + ) def __init__( self, @@ -71,8 +80,52 @@ def __init__( else: self.output_fd = f_out.fileno() + self.posxy = (0, 0) + self.height = 25 + self.width = 80 + self._rendered_screen = RenderedScreen.empty() + self._redraw_visual_cycle = 0 + + @property + def screen(self) -> list[str]: + return list(self._rendered_screen.screen_lines) + + def sync_rendered_screen( + self, + rendered_screen: RenderedScreen, + posxy: CursorXY | None = None, + ) -> None: + if posxy is None: + posxy = rendered_screen.cursor + self.posxy = posxy + self._rendered_screen = rendered_screen + trace( + "console.sync_rendered_screen lines={lines} cursor={cursor}", + lines=len(rendered_screen.composed_lines), + cursor=posxy, + ) + + def invalidate_render_state(self) -> None: + self._rendered_screen = RenderedScreen.empty() + trace("console.invalidate_render_state") + + def begin_redraw_visualization(self) -> str | None: + if "PYREPL_VISUALIZE_REDRAWS" not in os.environ: + return None + + palette = self._redraw_debug_palette + cycle = self._redraw_visual_cycle + style = palette[cycle % len(palette)] + self._redraw_visual_cycle = cycle + 1 + trace( + "console.begin_redraw_visualization cycle={cycle} style={style!r}", + cycle=cycle, + style=style, + ) + return style + @abstractmethod - def refresh(self, screen: list[str], xy: tuple[int, int]) -> None: ... + def refresh(self, rendered_screen: RenderedScreen) -> None: ... @abstractmethod def prepare(self) -> None: ... diff --git a/Lib/_pyrepl/content.py b/Lib/_pyrepl/content.py new file mode 100644 index 00000000000000..3cb22fee84b740 --- /dev/null +++ b/Lib/_pyrepl/content.py @@ -0,0 +1,136 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from .utils import ColorSpan, StyleRef, THEME, iter_display_chars, unbracket, wlen + + +@dataclass(frozen=True, slots=True) +class ContentFragment: + """A single display character with its visual width and style. + + The body of ``>>> def greet`` becomes one fragment per character:: + + d e f g r e e t + ╰──┴──╯ ╰──┴──┴──┴──╯ + keyword (unstyled) + + e.g. ``ContentFragment("d", 1, StyleRef(tag="keyword"))``. + """ + + text: str + width: int + style: StyleRef = StyleRef() + + +@dataclass(frozen=True, slots=True) +class PromptContent: + """The prompt split into leading full-width lines and an inline portion. + + For the common ``">>> "`` prompt (no newlines):: + + >>> def greet(name): + ╰─╯ + text=">>> ", width=4, leading_lines=() + + If ``sys.ps1`` contains newlines, e.g. ``"Python 3.13\\n>>> "``:: + + Python 3.13 ← leading_lines[0] + >>> def greet(name): + ╰─╯ + text=">>> ", width=4 + """ + + leading_lines: tuple[ContentFragment, ...] + text: str + width: int + + +@dataclass(frozen=True, slots=True) +class SourceLine: + """One logical line from the editor buffer, before styling. + + Given this two-line input in the REPL:: + + >>> def greet(name): + ... return name + ▲ cursor + + The buffer ``"def greet(name):\\n return name"`` yields:: + + SourceLine(lineno=0, text="def greet(name):", + start_offset=0, has_newline=True) + SourceLine(lineno=1, text=" return name", + start_offset=17, cursor_index=14) + """ + + lineno: int + text: str + start_offset: int + has_newline: bool + cursor_index: int | None = None + + @property + def cursor_on_line(self) -> bool: + return self.cursor_index is not None + + +@dataclass(frozen=True, slots=True) +class ContentLine: + """A logical line paired with its prompt and styled body. + + For ``>>> def greet(name):``:: + + >>> def greet(name): + ╰─╯ ╰──────────────╯ + prompt body: one ContentFragment per character + """ + + source: SourceLine + prompt: PromptContent + body: tuple[ContentFragment, ...] + + +def process_prompt(prompt: str) -> PromptContent: + r"""Return prompt content with width measured without zero-width markup.""" + + prompt_text = unbracket(prompt, including_content=False) + visible_prompt = unbracket(prompt, including_content=True) + leading_lines: list[ContentFragment] = [] + + while "\n" in prompt_text: + leading_text, _, prompt_text = prompt_text.partition("\n") + visible_leading, _, visible_prompt = visible_prompt.partition("\n") + leading_lines.append(ContentFragment(leading_text, wlen(visible_leading))) + + return PromptContent(tuple(leading_lines), prompt_text, wlen(visible_prompt)) + + +def build_body_fragments( + buffer: str, + colors: list[ColorSpan] | None, + start_index: int, +) -> tuple[ContentFragment, ...]: + """Convert a line's text into styled content fragments.""" + # Two separate loops to avoid the THEME() call in the common uncolored path. + if colors is None: + return tuple( + ContentFragment( + styled_char.text, + styled_char.width, + StyleRef(), + ) + for styled_char in iter_display_chars(buffer, colors, start_index) + ) + + theme = THEME() + return tuple( + ContentFragment( + styled_char.text, + styled_char.width, + StyleRef.from_tag(styled_char.tag, theme[styled_char.tag]) + if styled_char.tag + else StyleRef(), + ) + for styled_char in iter_display_chars(buffer, colors, start_index) + ) diff --git a/Lib/_pyrepl/fancy_termios.py b/Lib/_pyrepl/fancy_termios.py index 0468b9a2670267..8d5bd183f21339 100644 --- a/Lib/_pyrepl/fancy_termios.py +++ b/Lib/_pyrepl/fancy_termios.py @@ -20,19 +20,25 @@ import termios +TYPE_CHECKING = False + +if TYPE_CHECKING: + from typing import cast +else: + cast = lambda typ, val: val + + class TermState: - def __init__(self, tuples): - ( - self.iflag, - self.oflag, - self.cflag, - self.lflag, - self.ispeed, - self.ospeed, - self.cc, - ) = tuples + def __init__(self, attrs: list[int | list[bytes]]) -> None: + self.iflag = cast(int, attrs[0]) + self.oflag = cast(int, attrs[1]) + self.cflag = cast(int, attrs[2]) + self.lflag = cast(int, attrs[3]) + self.ispeed = cast(int, attrs[4]) + self.ospeed = cast(int, attrs[5]) + self.cc = cast(list[bytes], attrs[6]) - def as_list(self): + def as_list(self) -> list[int | list[bytes]]: return [ self.iflag, self.oflag, @@ -45,32 +51,32 @@ def as_list(self): self.cc[:], ] - def copy(self): + def copy(self) -> "TermState": return self.__class__(self.as_list()) -def tcgetattr(fd): +def tcgetattr(fd: int) -> TermState: return TermState(termios.tcgetattr(fd)) -def tcsetattr(fd, when, attrs): +def tcsetattr(fd: int, when: int, attrs: TermState) -> None: termios.tcsetattr(fd, when, attrs.as_list()) class Term(TermState): TS__init__ = TermState.__init__ - def __init__(self, fd=0): + def __init__(self, fd: int = 0) -> None: self.TS__init__(termios.tcgetattr(fd)) self.fd = fd - self.stack = [] + self.stack: list[list[int | list[bytes]]] = [] - def save(self): + def save(self) -> None: self.stack.append(self.as_list()) - def set(self, when=termios.TCSANOW): + def set(self, when: int = termios.TCSANOW) -> None: termios.tcsetattr(self.fd, when, self.as_list()) - def restore(self): + def restore(self) -> None: self.TS__init__(self.stack.pop()) self.set() diff --git a/Lib/_pyrepl/fancycompleter.py b/Lib/_pyrepl/fancycompleter.py new file mode 100644 index 00000000000000..ac4f0afdbc721c --- /dev/null +++ b/Lib/_pyrepl/fancycompleter.py @@ -0,0 +1,213 @@ +# Copyright 2010-2025 Antonio Cuni +# Daniel Hahler +# +# All Rights Reserved +"""Colorful tab completion for Python prompt""" +from __future__ import annotations + +from _colorize import ANSIColors, get_colors, get_theme +import rlcompleter +import keyword +import types + +TYPE_CHECKING = False + +if TYPE_CHECKING: + from typing import Any + from _colorize import Theme + + +def safe_getattr(obj, name): + # Mirror rlcompleter's safeguards so completion does not + # call properties or reify lazy module attributes. + if isinstance(getattr(type(obj), name, None), property): + return None + if (isinstance(obj, types.ModuleType) + and isinstance(obj.__dict__.get(name), types.LazyImportType) + ): + return obj.__dict__.get(name) + return getattr(obj, name, None) + + +def colorize_matches(names: list[str], values: list[Any], theme: Theme) -> list[str]: + return [ + _color_for_obj(name, obj, theme) + for name, obj in zip(names, values) + ] + +def _color_for_obj(name: str, value: Any, theme: Theme) -> str: + t = type(value) + color = _color_by_type(t, theme) + return f"{color}{name}{ANSIColors.RESET}" + + +def _color_by_type(t, theme): + typename = t.__name__ + # this is needed e.g. to turn method-wrapper into method_wrapper, + # because if we want _colorize.FancyCompleter to be "dataclassable" + # our keys need to be valid identifiers. + typename = typename.replace('-', '_').replace('.', '_') + return getattr(theme.fancycompleter, typename, ANSIColors.RESET) + + +class Completer(rlcompleter.Completer): + """ + When doing something like a.b., keep the full a.b.attr completion + stem so readline-style completion can keep refining the menu as you type. + + Optionally, display the various completions in different colors + depending on the type. + """ + def __init__( + self, + namespace=None, + *, + use_colors='auto', + consider_getitems=True, + ): + from _pyrepl import readline + rlcompleter.Completer.__init__(self, namespace) + if use_colors == 'auto': + # use colors only if we can + use_colors = get_colors().RED != "" + self.use_colors = use_colors + self.consider_getitems = consider_getitems + + if self.use_colors: + # In GNU readline, this prevents escaping of ANSI control + # characters in completion results. pyrepl's parse_and_bind() + # is a no-op, but pyrepl handles ANSI sequences natively + # via real_len()/stripcolor(). + readline.parse_and_bind('set dont-escape-ctrl-chars on') + self.theme = get_theme() + else: + self.theme = None + + if self.consider_getitems: + delims = readline.get_completer_delims() + delims = delims.replace('[', '') + delims = delims.replace(']', '') + readline.set_completer_delims(delims) + + def complete(self, text, state): + # if you press at the beginning of a line, insert an actual + # \t. Else, trigger completion. + if text == "": + return ('\t', None)[state] + else: + return rlcompleter.Completer.complete(self, text, state) + + def _callable_postfix(self, val, word): + # disable automatic insertion of '(' for global callables + return word + + def _callable_attr_postfix(self, val, word): + return rlcompleter.Completer._callable_postfix(self, val, word) + + def global_matches(self, text): + names = rlcompleter.Completer.global_matches(self, text) + prefix = commonprefix(names) + if prefix and prefix != text: + return [prefix] + + names.sort() + values = [] + for name in names: + clean_name = name.rstrip(': ') + if keyword.iskeyword(clean_name) or keyword.issoftkeyword(clean_name): + values.append(None) + else: + try: + values.append(eval(name, self.namespace)) + except Exception: + values.append(None) + if self.use_colors and names: + return self.colorize_matches(names, values) + return names + + def attr_matches(self, text): + try: + expr, attr, names, values = self._attr_matches(text) + except ValueError: + return [] + + if not names: + return [] + + if len(names) == 1: + # No coloring: when returning a single completion, readline + # inserts it directly into the prompt, so ANSI codes would + # appear as literal characters. + return [self._callable_attr_postfix(values[0], f'{expr}.{names[0]}')] + + prefix = commonprefix(names) + if prefix and prefix != attr: + return [f'{expr}.{prefix}'] # autocomplete prefix + + names = [f'{expr}.{name}' for name in names] + if self.use_colors: + return self.colorize_matches(names, values) + return names + + def _attr_matches(self, text): + expr, attr = text.rsplit('.', 1) + if '(' in expr or ')' in expr: # don't call functions + return expr, attr, [], [] + try: + thisobject = eval(expr, self.namespace) + except Exception: + return expr, attr, [], [] + + # get the content of the object, except __builtins__ + words = set(dir(thisobject)) - {'__builtins__'} + + if hasattr(thisobject, '__class__'): + words.add('__class__') + words.update(rlcompleter.get_class_members(thisobject.__class__)) + names = [] + values = [] + n = len(attr) + if attr == '': + noprefix = '_' + elif attr == '_': + noprefix = '__' + else: + noprefix = None + + # sort the words now to make sure to return completions in + # alphabetical order. It's easier to do it now, else we would need to + # sort 'names' later but make sure that 'values' in kept in sync, + # which is annoying. + words = sorted(words) + while True: + for word in words: + if ( + word[:n] == attr + and not (noprefix and word[:n+1] == noprefix) + ): + value = safe_getattr(thisobject, word) + names.append(word) + values.append(value) + if names or not noprefix: + break + if noprefix == '_': + noprefix = '__' + else: + noprefix = None + + return expr, attr, names, values + + def colorize_matches(self, names, values): + return colorize_matches(names, values, self.theme) + + +def commonprefix(names): + """Return the common prefix of all 'names'""" + if not names: + return '' + s1 = min(names) + s2 = max(names) + for i, c in enumerate(s1): + if c != s2[i]: + return s1[:i] + return s1 diff --git a/Lib/_pyrepl/historical_reader.py b/Lib/_pyrepl/historical_reader.py index c4b95fa2e81ee6..09b969d80bc231 100644 --- a/Lib/_pyrepl/historical_reader.py +++ b/Lib/_pyrepl/historical_reader.py @@ -90,7 +90,7 @@ def do(self) -> None: if r.get_unicode() != r.history[r.historyi]: r.buffer = list(r.history[r.historyi]) r.pos = len(r.buffer) - r.dirty = True + r.invalidate_buffer(0) class first_history(commands.Command): @@ -130,10 +130,11 @@ def do(self) -> None: o = len(r.yank_arg_yanked) else: o = 0 + start = r.pos - o b[r.pos - o : r.pos] = list(w) r.yank_arg_yanked = w r.pos += len(w) - o - r.dirty = True + r.invalidate_buffer(start) class forward_history_isearch(commands.Command): @@ -142,7 +143,7 @@ def do(self) -> None: r.isearch_direction = ISEARCH_DIRECTION_FORWARDS r.isearch_start = r.historyi, r.pos r.isearch_term = "" - r.dirty = True + r.invalidate_prompt() r.push_input_trans(r.isearch_trans) @@ -150,7 +151,7 @@ class reverse_history_isearch(commands.Command): def do(self) -> None: r = self.reader r.isearch_direction = ISEARCH_DIRECTION_BACKWARDS - r.dirty = True + r.invalidate_prompt() r.isearch_term = "" r.push_input_trans(r.isearch_trans) r.isearch_start = r.historyi, r.pos @@ -163,7 +164,7 @@ def do(self) -> None: r.pop_input_trans() r.select_item(r.isearch_start[0]) r.pos = r.isearch_start[1] - r.dirty = True + r.invalidate_prompt() class isearch_add_character(commands.Command): @@ -171,7 +172,7 @@ def do(self) -> None: r = self.reader b = r.buffer r.isearch_term += self.event[-1] - r.dirty = True + r.invalidate_prompt() p = r.pos + len(r.isearch_term) - 1 if b[p : p + 1] != [r.isearch_term[-1]]: r.isearch_next() @@ -182,7 +183,7 @@ def do(self) -> None: r = self.reader if len(r.isearch_term) > 0: r.isearch_term = r.isearch_term[:-1] - r.dirty = True + r.invalidate_prompt() else: r.error("nothing to rubout") @@ -207,7 +208,7 @@ def do(self) -> None: r.isearch_direction = ISEARCH_DIRECTION_NONE r.console.forgetinput() r.pop_input_trans() - r.dirty = True + r.invalidate_prompt() @dataclass @@ -241,7 +242,6 @@ def __post_init__(self) -> None: isearch_end, isearch_add_character, isearch_cancel, - isearch_add_character, isearch_backspace, isearch_forwards, isearch_backwards, @@ -279,8 +279,7 @@ def select_item(self, i: int) -> None: self.buffer = list(buf) self.historyi = i self.pos = len(self.buffer) - self.dirty = True - self.last_refresh_cache.invalidated = True + self.invalidate_buffer(0) def get_item(self, i: int) -> str: if i != len(self.history): @@ -358,7 +357,7 @@ def search_next(self, *, forwards: bool) -> None: if forwards and not match_prefix: self.pos = 0 self.buffer = [] - self.dirty = True + self.invalidate_buffer(0) else: self.error("not found") return diff --git a/Lib/_pyrepl/input.py b/Lib/_pyrepl/input.py index 21c24eb5cde3e3..2d65246c700f27 100644 --- a/Lib/_pyrepl/input.py +++ b/Lib/_pyrepl/input.py @@ -38,10 +38,11 @@ from abc import ABC, abstractmethod import unicodedata from collections import deque +from typing import TYPE_CHECKING # types -if False: +if TYPE_CHECKING: from .types import EventTuple diff --git a/Lib/_pyrepl/layout.py b/Lib/_pyrepl/layout.py new file mode 100644 index 00000000000000..6d854d1142dd9f --- /dev/null +++ b/Lib/_pyrepl/layout.py @@ -0,0 +1,268 @@ +"""Wrap content lines to the terminal width before rendering.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Self + +from .content import ContentFragment, ContentLine +from .types import CursorXY, ScreenInfoRow + + +@dataclass(frozen=True, slots=True) +class LayoutRow: + """Metadata for one physical screen row. + + For the row ``>>> def greet(name):``:: + + >>> def greet(name): + ╰─╯ ╰──────────────╯ + 4 char_widths=(1,1,1,…) ← 16 entries + buffer_advance=17 ← includes the newline + """ + + prompt_width: int + char_widths: tuple[int, ...] + suffix_width: int = 0 + buffer_advance: int = 0 + + @property + def width(self) -> int: + return self.prompt_width + sum(self.char_widths) + self.suffix_width + + @property + def screeninfo(self) -> ScreenInfoRow: + widths = list(self.char_widths) + if self.suffix_width: + widths.append(self.suffix_width) + return self.prompt_width, widths + + +@dataclass(frozen=True, slots=True) +class LayoutMap: + """Mapping between buffer positions and screen coordinates. + + Single source of truth for cursor placement. Given:: + + >>> def greet(name): ← row 0, buffer_advance=17 + ... return name ← row 1, buffer_advance=15 + ▲cursor + + ``pos_to_xy(31)`` → ``(18, 1)``: prompt width 4 + 14 body chars. + """ + rows: tuple[LayoutRow, ...] + + @classmethod + def empty(cls) -> Self: + return cls((LayoutRow(0, ()),)) + + @property + def screeninfo(self) -> list[ScreenInfoRow]: + return [row.screeninfo for row in self.rows] + + def max_column(self, y: int) -> int: + return self.rows[y].width + + def max_row(self) -> int: + return len(self.rows) - 1 + + def pos_to_xy(self, pos: int) -> CursorXY: + if not self.rows: + return 0, 0 + + remaining = pos + for y, row in enumerate(self.rows): + if remaining <= len(row.char_widths): + # Prompt-only leading rows are terminal scenery, not real + # buffer positions. Treating them as real just manufactures + # bugs. + if remaining == 0 and not row.char_widths and row.buffer_advance == 0 and y < len(self.rows) - 1: + continue + x = row.prompt_width + for width in row.char_widths[:remaining]: + x += width + return x, y + remaining -= row.buffer_advance + last_row = self.rows[-1] + return last_row.width - last_row.suffix_width, len(self.rows) - 1 + + def xy_to_pos(self, x: int, y: int) -> int: + if not self.rows: + return 0 + + pos = 0 + for row in self.rows[:y]: + pos += row.buffer_advance + + row = self.rows[y] + cur_x = row.prompt_width + char_widths = row.char_widths + i = 0 + for i, width in enumerate(char_widths): + if cur_x >= x: + # Include trailing zero-width (combining) chars at this position + for trailing_width in char_widths[i:]: + if trailing_width == 0: + pos += 1 + else: + break + return pos + if width == 0: + pos += 1 + continue + cur_x += width + pos += 1 + return pos + + +@dataclass(frozen=True, slots=True) +class WrappedRow: + """One physical screen row after wrapping, ready for rendering. + + When a line overflows the terminal width, it splits into + multiple rows with a ``\\`` continuation marker:: + + >>> x = "a very long li\\ ← suffix="\\", suffix_width=1 + ne that wraps" ← prompt_text="" (continuation) + """ + prompt_text: str = "" + prompt_width: int = 0 + fragments: tuple[ContentFragment, ...] = () + layout_widths: tuple[int, ...] = () + suffix: str = "" + suffix_width: int = 0 + buffer_advance: int = 0 + + +@dataclass(frozen=True, slots=True) +class LayoutResult: + wrapped_rows: tuple[WrappedRow, ...] + layout_map: LayoutMap + line_end_offsets: tuple[int, ...] + + +def layout_content_lines( + lines: tuple[ContentLine, ...], + width: int, + start_offset: int, +) -> LayoutResult: + """Wrap content lines to fit *width* columns. + + A short line passes through as one ``WrappedRow``; a long line is + split at the column boundary with ``\\`` markers:: + + >>> short = 1 ← one WrappedRow + >>> x = "a long stri\\ ← two WrappedRows, first has suffix="\\" + ng" + """ + if width <= 0: + return LayoutResult((), LayoutMap(()), ()) + + offset = start_offset + wrapped_rows: list[WrappedRow] = [] + layout_rows: list[LayoutRow] = [] + line_end_offsets: list[int] = [] + + for line in lines: + newline_advance = int(line.source.has_newline) + for leading in line.prompt.leading_lines: + line_end_offsets.append(offset) + wrapped_rows.append( + WrappedRow( + fragments=(leading,), + ) + ) + layout_rows.append(LayoutRow(0, (), buffer_advance=0)) + + prompt_text = line.prompt.text + prompt_width = line.prompt.width + body = tuple(line.body) + body_widths = tuple(fragment.width for fragment in body) + + # Fast path: line fits on one row. + if not body_widths or (sum(body_widths) + prompt_width) < width: + offset += len(body) + newline_advance + line_end_offsets.append(offset) + wrapped_rows.append( + WrappedRow( + prompt_text=prompt_text, + prompt_width=prompt_width, + fragments=body, + layout_widths=body_widths, + buffer_advance=len(body) + newline_advance, + ) + ) + layout_rows.append( + LayoutRow( + prompt_width, + body_widths, + buffer_advance=len(body) + newline_advance, + ) + ) + continue + + # Slow path: line needs wrapping. + current_prompt = prompt_text + current_prompt_width = prompt_width + start = 0 + total = len(body) + while True: + # Find how many characters fit on this row. + index_to_wrap_before = 0 + column = 0 + for char_width in body_widths[start:]: + if column + char_width + current_prompt_width >= width: + break + index_to_wrap_before += 1 + column += char_width + + if index_to_wrap_before == 0 and start < total: + index_to_wrap_before = 1 # force progress + + at_line_end = (start + index_to_wrap_before) >= total + if at_line_end: + offset += index_to_wrap_before + newline_advance + suffix = "" + suffix_width = 0 + buffer_advance = index_to_wrap_before + newline_advance + else: + offset += index_to_wrap_before + suffix = "\\" + suffix_width = 1 + buffer_advance = index_to_wrap_before + + end = start + index_to_wrap_before + row_fragments = body[start:end] + row_widths = body_widths[start:end] + line_end_offsets.append(offset) + wrapped_rows.append( + WrappedRow( + prompt_text=current_prompt, + prompt_width=current_prompt_width, + fragments=row_fragments, + layout_widths=row_widths, + suffix=suffix, + suffix_width=suffix_width, + buffer_advance=buffer_advance, + ) + ) + layout_rows.append( + LayoutRow( + current_prompt_width, + row_widths, + suffix_width=suffix_width, + buffer_advance=buffer_advance, + ) + ) + + start = end + current_prompt = "" + current_prompt_width = 0 + if at_line_end: + break + + return LayoutResult( + tuple(wrapped_rows), + LayoutMap(tuple(layout_rows)), + tuple(line_end_offsets), + ) diff --git a/Lib/_pyrepl/pager.py b/Lib/_pyrepl/pager.py index 1fddc63e3ee3ad..92afaa5933a225 100644 --- a/Lib/_pyrepl/pager.py +++ b/Lib/_pyrepl/pager.py @@ -138,7 +138,7 @@ def pipe_pager(text: str, cmd: str, title: str = '') -> None: '.' '?e (END):?pB %pB\\%..' ' (press h for help or q to quit)') - env['LESS'] = '-RmPm{0}$PM{0}$'.format(prompt_string) + env['LESS'] = '-RcmPm{0}$PM{0}$'.format(prompt_string) proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, errors='backslashreplace', env=env) assert proc.stdin is not None diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index 0ebd9162eca4bb..7e4dd801c84d5c 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -25,16 +25,41 @@ import _colorize from contextlib import contextmanager -from dataclasses import dataclass, field, fields +from dataclasses import dataclass, field, fields, replace +from typing import Self from . import commands, console, input -from .utils import wlen, unbracket, disp_str, gen_colors, THEME +from .content import ( + ContentFragment, + ContentLine, + SourceLine, + build_body_fragments, + process_prompt as build_prompt_content, +) +from .layout import LayoutMap, LayoutResult, LayoutRow, WrappedRow, layout_content_lines +from .render import RenderCell, RenderLine, RenderedScreen, ScreenOverlay +from .utils import ANSI_ESCAPE_SEQUENCE, ColorSpan, THEME, StyleRef, wlen, gen_colors from .trace import trace # types Command = commands.Command -from .types import Callback, SimpleContextManager, KeySpec, CommandName +from collections.abc import Callable, Iterator +from .types import ( + Callback, + CommandName, + CursorXY, + Dimensions, + EventData, + KeySpec, + Keymap, + ScreenInfoRow, + SimpleContextManager, +) + +type CommandClass = type[Command] +type CommandInput = tuple[CommandName | CommandClass, EventData] +type PromptCellCacheKey = tuple[str, bool] # syntax classes @@ -52,8 +77,8 @@ def make_default_syntax_table() -> dict[str, int]: return st -def make_default_commands() -> dict[CommandName, type[Command]]: - result: dict[CommandName, type[Command]] = {} +def make_default_commands() -> dict[CommandName, CommandClass]: + result: dict[CommandName, CommandClass] = {} for v in vars(commands).values(): if isinstance(v, type) and issubclass(v, Command) and v.__name__[0].islower(): result[v.__name__] = v @@ -61,7 +86,7 @@ def make_default_commands() -> dict[CommandName, type[Command]]: return result -default_keymap: tuple[tuple[KeySpec, CommandName], ...] = tuple( +default_keymap: Keymap = tuple( [ (r"\C-a", "beginning-of-line"), (r"\C-b", "left"), @@ -131,6 +156,77 @@ def make_default_commands() -> dict[CommandName, type[Command]]: ) +@dataclass(frozen=True, slots=True) +class RefreshInvalidation: + """Which parts of the screen need to be recomputed on the next refresh.""" + + cursor_only: bool = False + buffer_from_pos: int | None = None + prompt: bool = False + layout: bool = False + theme: bool = False + message: bool = False + overlay: bool = False + full: bool = False + + @classmethod + def empty(cls) -> Self: + return cls() + + @property + def needs_screen_refresh(self) -> bool: + return any( + ( + self.buffer_from_pos is not None, + self.prompt, + self.layout, + self.theme, + self.message, + self.overlay, + self.full, + ) + ) + + @property + def is_cursor_only(self) -> bool: + return self.cursor_only and not self.needs_screen_refresh + + @property + def buffer_rebuild_from_pos(self) -> int | None: + if self.full or self.prompt or self.layout or self.theme: + return 0 + return self.buffer_from_pos + + def with_cursor(self) -> Self: + if self.needs_screen_refresh: + return self + return replace(self, cursor_only=True) + + def with_buffer(self, from_pos: int) -> Self: + current = from_pos + if self.buffer_from_pos is not None: + current = min(current, self.buffer_from_pos) + return replace(self, cursor_only=False, buffer_from_pos=current) + + def with_prompt(self) -> Self: + return replace(self, cursor_only=False, prompt=True) + + def with_layout(self) -> Self: + return replace(self, cursor_only=False, layout=True) + + def with_theme(self) -> Self: + return replace(self, cursor_only=False, theme=True) + + def with_message(self) -> Self: + return replace(self, cursor_only=False, message=True) + + def with_overlay(self) -> Self: + return replace(self, cursor_only=False, overlay=True) + + def with_full(self) -> Self: + return replace(self, cursor_only=False, full=True) + + @dataclass(slots=True) class Reader: """The Reader class implements the bare bones of a command reader, @@ -148,10 +244,9 @@ class Reader: * pos: A 0-based index into 'buffer' for where the insertion point is. - * screeninfo: - A list of screen position tuples. Each list element is a tuple - representing information on visible line length for a given line. - Allows for efficient skipping of color escape sequences. + * layout: + A mapping between buffer positions and rendered rows/columns. + It is the internal source of truth for cursor placement. * cxy, lxy: the position of the insertion point in screen ... * syntax_table: @@ -162,8 +257,6 @@ class Reader: * arg: The emacs-style prefix argument. It will be None if no such argument has been provided. - * dirty: - True if we need to refresh the display. * kill_ring: The emacs-style kill-ring; manipulated with yank & yank-pop * ps1, ps2, ps3, ps4: @@ -198,66 +291,89 @@ class Reader: kill_ring: list[list[str]] = field(default_factory=list) msg: str = "" arg: int | None = None - dirty: bool = False finished: bool = False paste_mode: bool = False - commands: dict[str, type[Command]] = field(default_factory=make_default_commands) - last_command: type[Command] | None = None + commands: dict[CommandName, CommandClass] = field(default_factory=make_default_commands) + last_command: CommandClass | None = None syntax_table: dict[str, int] = field(default_factory=make_default_syntax_table) - keymap: tuple[tuple[str, str], ...] = () + keymap: Keymap = () input_trans: input.KeymapTranslator = field(init=False) input_trans_stack: list[input.KeymapTranslator] = field(default_factory=list) - screen: list[str] = field(default_factory=list) - screeninfo: list[tuple[int, list[int]]] = field(init=False) - cxy: tuple[int, int] = field(init=False) - lxy: tuple[int, int] = field(init=False) - scheduled_commands: list[str] = field(default_factory=list) + rendered_screen: RenderedScreen = field(init=False) + layout: LayoutMap = field(init=False) + cxy: CursorXY = field(init=False) + lxy: CursorXY = field(init=False) + scheduled_commands: list[CommandName] = field(default_factory=list) can_colorize: bool = False + gen_colors: Callable[[str], Iterator[ColorSpan]] = gen_colors threading_hook: Callback | None = None + invalidation: RefreshInvalidation = field(init=False) ## cached metadata to speed up screen refreshes @dataclass class RefreshCache: - screen: list[str] = field(default_factory=list) - screeninfo: list[tuple[int, list[int]]] = field(init=False) + """Previously computed render/layout data for incremental refresh.""" + + render_lines: list[RenderLine] = field(default_factory=list) + layout_rows: list[LayoutRow] = field(default_factory=list) line_end_offsets: list[int] = field(default_factory=list) - pos: int = field(init=False) - cxy: tuple[int, int] = field(init=False) - dimensions: tuple[int, int] = field(init=False) - invalidated: bool = False + pos: int = 0 + dimensions: Dimensions = (0, 0) def update_cache(self, reader: Reader, - screen: list[str], - screeninfo: list[tuple[int, list[int]]], + render_lines: list[RenderLine], + layout_rows: list[LayoutRow], + line_end_offsets: list[int], ) -> None: - self.screen = screen.copy() - self.screeninfo = screeninfo.copy() + self.render_lines = render_lines.copy() + self.layout_rows = layout_rows.copy() + self.line_end_offsets = line_end_offsets.copy() self.pos = reader.pos - self.cxy = reader.cxy self.dimensions = reader.console.width, reader.console.height - self.invalidated = False def valid(self, reader: Reader) -> bool: - if self.invalidated: - return False dimensions = reader.console.width, reader.console.height dimensions_changed = dimensions != self.dimensions return not dimensions_changed - def get_cached_location(self, reader: Reader) -> tuple[int, int]: - if self.invalidated: - raise ValueError("Cache is invalidated") - offset = 0 - earliest_common_pos = min(reader.pos, self.pos) + def get_cached_location( + self, + reader: Reader, + buffer_from_pos: int | None = None, + *, + reuse_full: bool = False, + ) -> tuple[int, int]: + """Return (buffer_offset, num_reusable_lines) for incremental refresh. + + Three paths: + - reuse_full (overlay/message-only): reuse all cached lines. + - buffer_from_pos=None (full rebuild): rewind to common cursor pos. + - explicit buffer_from_pos: reuse lines before that position. + """ + if reuse_full: + if self.line_end_offsets: + last_offset = self.line_end_offsets[-1] + if last_offset >= len(reader.buffer): + return last_offset, len(self.line_end_offsets) + return 0, 0 + if buffer_from_pos is None: + buffer_from_pos = min(reader.pos, self.pos) num_common_lines = len(self.line_end_offsets) while num_common_lines > 0: - offset = self.line_end_offsets[num_common_lines - 1] - if earliest_common_pos > offset: + candidate = self.line_end_offsets[num_common_lines - 1] + if buffer_from_pos > candidate: break num_common_lines -= 1 - else: - offset = 0 + # Prompt-only leading rows consume no buffer content. Reusing them + # in isolation causes the next incremental rebuild to emit them a + # second time. + while ( + num_common_lines > 0 + and self.layout_rows[num_common_lines - 1].buffer_advance == 0 + ): + num_common_lines -= 1 + offset = self.line_end_offsets[num_common_lines - 1] if num_common_lines else 0 return offset, num_common_lines last_refresh_cache: RefreshCache = field(default_factory=RefreshCache) @@ -270,123 +386,267 @@ def __post_init__(self) -> None: self.input_trans = input.KeymapTranslator( self.keymap, invalid_cls="invalid-key", character_cls="self-insert" ) - self.screeninfo = [(0, [])] + self.layout = LayoutMap.empty() self.cxy = self.pos2xy() self.lxy = (self.pos, 0) + self.rendered_screen = RenderedScreen.empty() self.can_colorize = _colorize.can_colorize() + self.invalidation = RefreshInvalidation.empty() - self.last_refresh_cache.screeninfo = self.screeninfo + self.last_refresh_cache.layout_rows = list(self.layout.rows) self.last_refresh_cache.pos = self.pos - self.last_refresh_cache.cxy = self.cxy + self.last_refresh_cache.dimensions = (0, 0) - def collect_keymap(self) -> tuple[tuple[KeySpec, CommandName], ...]: - return default_keymap + @property + def screen(self) -> list[str]: + return list(self.rendered_screen.screen_lines) - def calc_screen(self) -> list[str]: - """Translate changes in self.buffer into changes in self.console.screen.""" - # Since the last call to calc_screen: - # screen and screeninfo may differ due to a completion menu being shown - # pos and cxy may differ due to edits, cursor movements, or completion menus + @property + def screeninfo(self) -> list[ScreenInfoRow]: + return self.layout.screeninfo - # Lines that are above both the old and new cursor position can't have changed, - # unless the terminal has been resized (which might cause reflowing) or we've - # entered or left paste mode (which changes prompts, causing reflowing). + def collect_keymap(self) -> Keymap: + return default_keymap + + def calc_screen(self) -> RenderedScreen: + """Translate the editable buffer into a base rendered screen.""" num_common_lines = 0 offset = 0 if self.last_refresh_cache.valid(self): - offset, num_common_lines = self.last_refresh_cache.get_cached_location(self) - - screen = self.last_refresh_cache.screen - del screen[num_common_lines:] - - screeninfo = self.last_refresh_cache.screeninfo - del screeninfo[num_common_lines:] - - last_refresh_line_end_offsets = self.last_refresh_cache.line_end_offsets - del last_refresh_line_end_offsets[num_common_lines:] + if ( + self.invalidation.buffer_from_pos is None + and not ( + self.invalidation.full + or self.invalidation.prompt + or self.invalidation.layout + or self.invalidation.theme + ) + and (self.invalidation.message or self.invalidation.overlay) + ): + # Fast path: only overlays or messages changed. + offset, num_common_lines = self.last_refresh_cache.get_cached_location( + self, + reuse_full=True, + ) + assert not self.last_refresh_cache.line_end_offsets or ( + self.last_refresh_cache.line_end_offsets[-1] >= len(self.buffer) + ), "Buffer modified without invalidate_buffer() call" + else: + offset, num_common_lines = self.last_refresh_cache.get_cached_location( + self, + self._buffer_refresh_from_pos(), + ) + + base_render_lines = self.last_refresh_cache.render_lines[:num_common_lines] + layout_rows = self.last_refresh_cache.layout_rows[:num_common_lines] + last_refresh_line_end_offsets = self.last_refresh_cache.line_end_offsets[:num_common_lines] + + source_lines = self._build_source_lines(offset, num_common_lines) + content_lines = self._build_content_lines( + source_lines, + prompt_from_cache=bool(offset and self.buffer[offset - 1] != "\n"), + ) + layout_result = self._layout_content(content_lines, offset) + base_render_lines.extend(self._render_wrapped_rows(layout_result.wrapped_rows)) + layout_rows.extend(layout_result.layout_map.rows) + last_refresh_line_end_offsets.extend(layout_result.line_end_offsets) - pos = self.pos - pos -= offset + self.layout = LayoutMap(tuple(layout_rows)) + self.cxy = self.pos2xy() + if not source_lines: + # reuse_full path: _build_source_lines didn't run, + # so lxy wasn't updated. Derive it from the buffer. + self.lxy = self._compute_lxy() + self.last_refresh_cache.update_cache( + self, + base_render_lines, + layout_rows, + last_refresh_line_end_offsets, + ) + return RenderedScreen(tuple(base_render_lines), self.cxy) - prompt_from_cache = (offset and self.buffer[offset - 1] != "\n") + def _buffer_refresh_from_pos(self) -> int: + """Return buffer position from which to rebuild content. - if self.can_colorize: - colors = list(gen_colors(self.get_unicode())) + Returns 0 (full rebuild) when no incremental position is known. + """ + buffer_from_pos = self.invalidation.buffer_rebuild_from_pos + if buffer_from_pos is not None: + return buffer_from_pos + return 0 + + def _compute_lxy(self) -> CursorXY: + """Derive logical cursor (col, lineno) from the buffer and pos.""" + text = "".join(self.buffer[:self.pos]) + lineno = text.count("\n") + if lineno: + col = self.pos - text.rindex("\n") - 1 else: - colors = None - trace("colors = {colors}", colors=colors) + col = self.pos + return col, lineno + + def _build_source_lines( + self, + offset: int, + first_lineno: int, + ) -> tuple[SourceLine, ...]: + if offset == len(self.buffer) and (offset > 0 or first_lineno > 0): + return () + + pos = self.pos - offset lines = "".join(self.buffer[offset:]).split("\n") cursor_found = False lines_beyond_cursor = 0 - for ln, line in enumerate(lines, num_common_lines): + source_lines: list[SourceLine] = [] + current_offset = offset + + for line_index, line in enumerate(lines): + lineno = first_lineno + line_index + has_newline = line_index < len(lines) - 1 line_len = len(line) + cursor_index: int | None = None if 0 <= pos <= line_len: - self.lxy = pos, ln + cursor_index = pos + self.lxy = pos, lineno cursor_found = True elif cursor_found: lines_beyond_cursor += 1 if lines_beyond_cursor > self.console.height: - # No need to keep formatting lines. - # The console can't show them. break + + source_lines.append( + SourceLine( + lineno=lineno, + text=line, + start_offset=current_offset, + has_newline=has_newline, + cursor_index=cursor_index, + ) + ) + pos -= line_len + 1 + current_offset += line_len + (1 if has_newline else 0) + + return tuple(source_lines) + + def _build_content_lines( + self, + source_lines: tuple[SourceLine, ...], + *, + prompt_from_cache: bool, + ) -> tuple[ContentLine, ...]: + if self.can_colorize: + colors = list(self.gen_colors(self.get_unicode())) + else: + colors = None + trace("colors = {colors}", colors=colors) + + content_lines: list[ContentLine] = [] + for source_line in source_lines: if prompt_from_cache: - # Only the first line's prompt can come from the cache prompt_from_cache = False prompt = "" else: - prompt = self.get_prompt(ln, line_len >= pos >= 0) - while "\n" in prompt: - pre_prompt, _, prompt = prompt.partition("\n") - last_refresh_line_end_offsets.append(offset) - screen.append(pre_prompt) - screeninfo.append((0, [])) - pos -= line_len + 1 - prompt, prompt_len = self.process_prompt(prompt) - chars, char_widths = disp_str(line, colors, offset) - wrapcount = (sum(char_widths) + prompt_len) // self.console.width - if wrapcount == 0 or not char_widths: - offset += line_len + 1 # Takes all of the line plus the newline - last_refresh_line_end_offsets.append(offset) - screen.append(prompt + "".join(chars)) - screeninfo.append((prompt_len, char_widths)) - else: - pre = prompt - prelen = prompt_len - for wrap in range(wrapcount + 1): - index_to_wrap_before = 0 - column = 0 - for char_width in char_widths: - if column + char_width + prelen >= self.console.width: - break - index_to_wrap_before += 1 - column += char_width - if len(chars) > index_to_wrap_before: - offset += index_to_wrap_before - post = "\\" - after = [1] - else: - offset += index_to_wrap_before + 1 # Takes the newline - post = "" - after = [] - last_refresh_line_end_offsets.append(offset) - render = pre + "".join(chars[:index_to_wrap_before]) + post - render_widths = char_widths[:index_to_wrap_before] + after - screen.append(render) - screeninfo.append((prelen, render_widths)) - chars = chars[index_to_wrap_before:] - char_widths = char_widths[index_to_wrap_before:] - pre = "" - prelen = 0 - self.screeninfo = screeninfo - self.cxy = self.pos2xy() - if self.msg: - for mline in self.msg.split("\n"): - screen.append(mline) - screeninfo.append((0, [])) - - self.last_refresh_cache.update_cache(self, screen, screeninfo) - return screen + prompt = self.get_prompt(source_line.lineno, source_line.cursor_on_line) + content_lines.append( + ContentLine( + source=source_line, + prompt=build_prompt_content(prompt), + body=build_body_fragments( + source_line.text, + colors, + source_line.start_offset, + ), + ) + ) + return tuple(content_lines) + + def _layout_content( + self, + content_lines: tuple[ContentLine, ...], + offset: int, + ) -> LayoutResult: + return layout_content_lines(content_lines, self.console.width, offset) + + def _render_wrapped_rows( + self, + wrapped_rows: tuple[WrappedRow, ...], + ) -> list[RenderLine]: + return [ + self._render_line( + row.prompt_text, + row.fragments, + row.suffix, + ) + for row in wrapped_rows + ] + + def _render_message_lines(self) -> tuple[RenderLine, ...]: + if not self.msg: + return () + width = self.console.width + render_lines: list[RenderLine] = [] + for message_line in self.msg.split("\n"): + # If self.msg is larger than console width, make it fit. + # TODO: try to split between words? + if not message_line: + render_lines.append(RenderLine.from_rendered_text("")) + continue + for offset in range(0, len(message_line), width): + render_lines.append( + RenderLine.from_rendered_text(message_line[offset : offset + width]) + ) + return tuple(render_lines) + + def get_screen_overlays(self) -> tuple[ScreenOverlay, ...]: + return () + + def compose_rendered_screen(self, base_screen: RenderedScreen) -> RenderedScreen: + overlays = list(self.get_screen_overlays()) + message_lines = self._render_message_lines() + if message_lines: + overlays.append(ScreenOverlay(len(base_screen.lines), message_lines)) + if not overlays: + return base_screen + return RenderedScreen(base_screen.lines, base_screen.cursor, tuple(overlays)) + + _prompt_cell_cache: dict[PromptCellCacheKey, tuple[RenderCell, ...]] = field( + init=False, default_factory=dict, repr=False + ) + + def _render_line( + self, + prefix: str, + fragments: tuple[ContentFragment, ...], + suffix: str = "", + ) -> RenderLine: + cells: list[RenderCell] = [] + if prefix: + cache_key = (prefix, self.can_colorize) + cached = self._prompt_cell_cache.get(cache_key) + if cached is None: + prompt_cells = RenderLine.from_rendered_text(prefix).cells + if self.can_colorize and prompt_cells and not ANSI_ESCAPE_SEQUENCE.search(prefix): + prompt_style = StyleRef.from_tag("prompt", THEME()["prompt"]) + prompt_cells = tuple( + RenderCell( + cell.text, + cell.width, + style=prompt_style if cell.text else cell.style, + controls=cell.controls, + ) + for cell in prompt_cells + ) + self._prompt_cell_cache[cache_key] = prompt_cells + cached = prompt_cells + cells.extend(cached) + cells.extend( + RenderCell(fragment.text, fragment.width, style=fragment.style) + for fragment in fragments + ) + if suffix: + cells.extend(RenderLine.from_rendered_text(suffix).cells) + return RenderLine.from_cells(cells) @staticmethod def process_prompt(prompt: str) -> tuple[str, int]: @@ -396,9 +656,8 @@ def process_prompt(prompt: str) -> tuple[str, int]: (\x01 and \x02) removed. The length ignores anything between those brackets as well as any ANSI escape sequences. """ - out_prompt = unbracket(prompt, including_content=False) - visible_prompt = unbracket(prompt, including_content=True) - return out_prompt, wlen(visible_prompt) + prompt_content = build_prompt_content(prompt) + return prompt_content.text, prompt_content.width def bow(self, p: int | None = None) -> int: """Return the 0-based index of the word break preceding p most @@ -460,10 +719,10 @@ def eol(self, p: int | None = None) -> int: def max_column(self, y: int) -> int: """Return the last x-offset for line y""" - return self.screeninfo[y][0] + sum(self.screeninfo[y][1]) + return self.layout.max_column(y) def max_row(self) -> int: - return len(self.screeninfo) - 1 + return self.layout.max_row() def get_arg(self, default: int = 1) -> int: """Return any prefix argument that the user has supplied, @@ -489,10 +748,6 @@ def get_prompt(self, lineno: int, cursor_on_line: bool) -> str: prompt = self.ps3 else: prompt = self.ps1 - - if self.can_colorize: - t = THEME() - prompt = f"{t.prompt}{prompt}{t.reset}" return prompt def push_input_trans(self, itrans: input.KeymapTranslator) -> None: @@ -504,65 +759,48 @@ def pop_input_trans(self) -> None: def setpos_from_xy(self, x: int, y: int) -> None: """Set pos according to coordinates x, y""" - pos = 0 - i = 0 - while i < y: - prompt_len, char_widths = self.screeninfo[i] - offset = len(char_widths) - in_wrapped_line = prompt_len + sum(char_widths) >= self.console.width - if in_wrapped_line: - pos += offset - 1 # -1 cause backslash is not in buffer - else: - pos += offset + 1 # +1 cause newline is in buffer - i += 1 - - j = 0 - cur_x = self.screeninfo[i][0] - while cur_x < x: - if self.screeninfo[i][1][j] == 0: - j += 1 # prevent potential future infinite loop - continue - cur_x += self.screeninfo[i][1][j] - j += 1 - pos += 1 - - self.pos = pos + self.pos = self.layout.xy_to_pos(x, y) - def pos2xy(self) -> tuple[int, int]: + def pos2xy(self) -> CursorXY: """Return the x, y coordinates of position 'pos'.""" + assert 0 <= self.pos <= len(self.buffer) + return self.layout.pos_to_xy(self.pos) + + def insert(self, text: str | list[str]) -> None: + """Insert 'text' at the insertion point.""" + start = self.pos + self.buffer[self.pos : self.pos] = list(text) + self.pos += len(text) + self.invalidate_buffer(start) - prompt_len, y = 0, 0 - char_widths: list[int] = [] - pos = self.pos - assert 0 <= pos <= len(self.buffer) + def invalidate_cursor(self) -> None: + self.invalidation = self.invalidation.with_cursor() - # optimize for the common case: typing at the end of the buffer - if pos == len(self.buffer) and len(self.screeninfo) > 0: - y = len(self.screeninfo) - 1 - prompt_len, char_widths = self.screeninfo[y] - return prompt_len + sum(char_widths), y + def invalidate_buffer(self, from_pos: int) -> None: + self.invalidation = self.invalidation.with_buffer(from_pos) - for prompt_len, char_widths in self.screeninfo: - offset = len(char_widths) - in_wrapped_line = prompt_len + sum(char_widths) >= self.console.width - if in_wrapped_line: - offset -= 1 # need to remove line-wrapping backslash + def invalidate_prompt(self) -> None: + self._prompt_cell_cache.clear() + self.invalidation = self.invalidation.with_prompt() - if offset >= pos: - break + def invalidate_layout(self) -> None: + self.invalidation = self.invalidation.with_layout() - if not in_wrapped_line: - offset += 1 # there's a newline in buffer + def invalidate_theme(self) -> None: + self._prompt_cell_cache.clear() + self.invalidation = self.invalidation.with_theme() - pos -= offset - y += 1 - return prompt_len + sum(char_widths[:pos]), y + def invalidate_message(self) -> None: + self.invalidation = self.invalidation.with_message() - def insert(self, text: str | list[str]) -> None: - """Insert 'text' at the insertion point.""" - self.buffer[self.pos : self.pos] = list(text) - self.pos += len(text) - self.dirty = True + def invalidate_overlay(self) -> None: + self.invalidation = self.invalidation.with_overlay() + + def invalidate_full(self) -> None: + self.invalidation = self.invalidation.with_full() + + def clear_invalidation(self) -> None: + self.invalidation = RefreshInvalidation.empty() def update_cursor(self) -> None: """Move the cursor to reflect changes in self.pos""" @@ -574,7 +812,7 @@ def after_command(self, cmd: Command) -> None: """This function is called to allow post command cleanup.""" if getattr(cmd, "kills_digit_arg", True): if self.arg is not None: - self.dirty = True + self.invalidate_prompt() self.arg = None def prepare(self) -> None: @@ -587,9 +825,15 @@ def prepare(self) -> None: self.finished = False del self.buffer[:] self.pos = 0 - self.dirty = True + self.layout = LayoutMap.empty() + self.cxy = self.pos2xy() + self.lxy = (self.pos, 0) + self.rendered_screen = RenderedScreen.empty() + self.invalidate_full() self.last_command = None - self.calc_screen() + base_screen = self.calc_screen() + self.rendered_screen = self.compose_rendered_screen(base_screen) + self.invalidation = RefreshInvalidation.empty() except BaseException: self.restore() raise @@ -598,7 +842,7 @@ def prepare(self) -> None: cmd = self.scheduled_commands.pop() self.do_cmd((cmd, [])) - def last_command_is(self, cls: type) -> bool: + def last_command_is(self, cls: CommandClass) -> bool: if not self.last_command: return False return issubclass(cls, self.last_command) @@ -619,27 +863,51 @@ def suspend(self) -> SimpleContextManager: setattr(self, arg, prev_state[arg]) self.prepare() + @contextmanager + def suspend_colorization(self) -> SimpleContextManager: + try: + old_can_colorize = self.can_colorize + self.can_colorize = False + yield + finally: + self.can_colorize = old_can_colorize + def finish(self) -> None: """Called when a command signals that we're finished.""" pass def error(self, msg: str = "none") -> None: self.msg = "! " + msg + " " - self.dirty = True + self.invalidate_message() self.console.beep() def update_screen(self) -> None: - if self.dirty: + if self.invalidation.is_cursor_only: + self.update_cursor() + self.clear_invalidation() + elif self.invalidation.needs_screen_refresh: self.refresh() def refresh(self) -> None: """Recalculate and refresh the screen.""" + self.console.height, self.console.width = self.console.getheightwidth() # this call sets up self.cxy, so call it first. - self.screen = self.calc_screen() - self.console.refresh(self.screen, self.cxy) - self.dirty = False + base_screen = self.calc_screen() + rendered_screen = self.compose_rendered_screen(base_screen) + self.rendered_screen = rendered_screen + trace( + "reader.refresh cursor={cursor} lines={lines} " + "dims=({width},{height}) invalidation={invalidation}", + cursor=self.cxy, + lines=len(rendered_screen.composed_lines), + width=self.console.width, + height=self.console.height, + invalidation=self.invalidation, + ) + self.console.refresh(rendered_screen) + self.clear_invalidation() - def do_cmd(self, cmd: tuple[str, list[str]]) -> None: + def do_cmd(self, cmd: CommandInput) -> None: """`cmd` is a tuple of "event_name" and "event", which in the current implementation is always just the "buffer" which happens to be a list of single-character strings.""" @@ -656,13 +924,14 @@ def do_cmd(self, cmd: tuple[str, list[str]]) -> None: command.do() self.after_command(command) - - if self.dirty: - self.refresh() - else: - self.update_cursor() - - if not isinstance(cmd, commands.digit_arg): + if ( + not self.invalidation.needs_screen_refresh + and not self.invalidation.is_cursor_only + ): + self.invalidate_cursor() + self.update_screen() + + if command_type is not commands.digit_arg: self.last_command = command_type self.finished = bool(command.finish) @@ -695,7 +964,7 @@ def handle1(self, block: bool = True) -> bool: if self.msg: self.msg = "" - self.dirty = True + self.invalidate_message() while True: # We use the same timeout as in readline.c: 100ms @@ -712,9 +981,13 @@ def handle1(self, block: bool = True) -> bool: if event.evt == "key": self.input_trans.push(event) elif event.evt == "scroll": + self.invalidate_full() self.refresh() + return True elif event.evt == "resize": + self.invalidate_full() self.refresh() + return True else: translate = False diff --git a/Lib/_pyrepl/readline.py b/Lib/_pyrepl/readline.py index 23b8fa6b9c7625..e4370b0d1462ea 100644 --- a/Lib/_pyrepl/readline.py +++ b/Lib/_pyrepl/readline.py @@ -37,9 +37,10 @@ from rlcompleter import Completer as RLCompleter from . import commands, historical_reader -from .completing_reader import CompletingReader +from .completing_reader import CompletingReader, stripcolor from .console import Console as ConsoleType from ._module_completer import ModuleCompleter, make_default_module_completer +from .fancycompleter import Completer as FancyCompleter, colorize_matches Console: type[ConsoleType] _error: tuple[type[Exception], ...] | type[Exception] @@ -55,7 +56,7 @@ # types Command = commands.Command from collections.abc import Callable, Collection -from .types import Callback, Completer, KeySpec, CommandName +from .types import Callback, Completer, KeySpec, CommandName, CompletionAction TYPE_CHECKING = False @@ -103,6 +104,7 @@ class ReadlineConfig: readline_completer: Completer | None = None completer_delims: frozenset[str] = frozenset(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?") module_completer: ModuleCompleter = field(default_factory=make_default_module_completer) + colorize_completions: Callable[[list[str], list[Any]], list[str]] | None = None @dataclass(kw_only=True) class ReadlineAlikeReader(historical_reader.HistoricalReader, CompletingReader): @@ -134,7 +136,7 @@ def get_stem(self) -> str: p -= 1 return "".join(b[p + 1 : self.pos]) - def get_completions(self, stem: str) -> list[str]: + def get_completions(self, stem: str) -> tuple[list[str], CompletionAction | None]: module_completions = self.get_module_completions() if module_completions is not None: return module_completions @@ -144,7 +146,7 @@ def get_completions(self, stem: str) -> list[str]: while p > 0 and b[p - 1] != "\n": p -= 1 num_spaces = 4 - ((self.pos - p) % 4) - return [" " * num_spaces] + return [" " * num_spaces], None result = [] function = self.config.readline_completer if function is not None: @@ -162,14 +164,23 @@ def get_completions(self, stem: str) -> list[str]: break result.append(next) state += 1 - # emulate the behavior of the standard readline that sorts - # the completions before displaying them. - result.sort() - return result - - def get_module_completions(self) -> list[str] | None: - line = self.get_line() - return self.config.module_completer.get_completions(line) + # Emulate readline's sorting using the visible text rather than + # the raw ANSI escape sequences used for colorized matches. + result.sort(key=stripcolor) + return result, None + + def get_module_completions(self) -> tuple[list[str], CompletionAction | None] | None: + line = stripcolor(self.get_line()) + colorize_completions = self.config.colorize_completions + result = self.config.module_completer.get_completions( + line, include_values=bool(colorize_completions) + ) + if result is None: + return None + names, values, action = result + if colorize_completions: + names = colorize_completions(names, values) + return names, action def get_trimmed_history(self, maxlength: int) -> list[str]: if maxlength >= 0: @@ -276,7 +287,7 @@ class maybe_accept(commands.Command): def do(self) -> None: r: ReadlineAlikeReader r = self.reader # type: ignore[assignment] - r.dirty = True # this is needed to hide the completion menu, if visible + r.invalidate_overlay() # hide completion menu, if visible # if there are already several lines and the cursor # is not on the last one, always insert a new \n. @@ -336,7 +347,7 @@ def do(self) -> None: break r.pos -= repeat del b[r.pos : r.pos + repeat] - r.dirty = True + r.invalidate_buffer(r.pos) else: self.reader.error("can't backspace at start") @@ -412,8 +423,12 @@ def set_completer_delims(self, delimiters: Collection[str]) -> None: def get_completer_delims(self) -> str: return "".join(sorted(self.config.completer_delims)) - def _histline(self, line: str) -> str: + def _histline(self, line: str, *, sanitize_nuls: bool = False) -> str: line = line.rstrip("\n") + if "\0" in line: + if not sanitize_nuls: + raise ValueError("embedded null character") + line = line.replace("\0", "") return line def get_history_length(self) -> int: @@ -446,9 +461,12 @@ def read_history_file(self, filename: str = gethistoryfile()) -> None: if line.endswith("\r"): buffer.append(line+'\n') else: - line = self._histline(line) + line = self._histline(line, sanitize_nuls=True) if buffer: - line = self._histline("".join(buffer).replace("\r", "") + line) + line = self._histline( + "".join(buffer).replace("\r", "") + line, + sanitize_nuls=True, + ) del buffer[:] if line: history.append(line) @@ -608,8 +626,19 @@ def _setup(namespace: Mapping[str, Any]) -> None: # set up namespace in rlcompleter, which requires it to be a bona fide dict if not isinstance(namespace, dict): namespace = dict(namespace) + use_basic_completer = ( + not sys.flags.ignore_environment + and os.getenv("PYTHON_BASIC_COMPLETER") + ) + completer_cls = RLCompleter if use_basic_completer else FancyCompleter + completer = completer_cls(namespace) + _wrapper.config.readline_completer = completer.complete + if isinstance(completer, FancyCompleter) and completer.use_colors: + theme = completer.theme + def _colorize(names: list[str], values: list[object]) -> list[str]: + return colorize_matches(names, values, theme) + _wrapper.config.colorize_completions = _colorize _wrapper.config.module_completer = ModuleCompleter(namespace) - _wrapper.config.readline_completer = RLCompleter(namespace).complete # this is not really what readline.c does. Better than nothing I guess import builtins diff --git a/Lib/_pyrepl/render.py b/Lib/_pyrepl/render.py new file mode 100644 index 00000000000000..b821f35d850825 --- /dev/null +++ b/Lib/_pyrepl/render.py @@ -0,0 +1,397 @@ +from __future__ import annotations + +from collections.abc import Iterable, Sequence +from dataclasses import dataclass, field +from typing import Literal, Protocol, Self + +from .utils import ANSI_ESCAPE_SEQUENCE, THEME, StyleRef, str_width +from .types import CursorXY + +type RenderStyle = StyleRef | str | None +type LineUpdateKind = Literal[ + "insert_char", + "replace_char", + "replace_span", + "delete_then_insert", + "rewrite_suffix", +] + + +class _ThemeSyntax(Protocol): + """Protocol for theme objects that map tag names to SGR escape strings.""" + def __getitem__(self, key: str, /) -> str: ... + + +@dataclass(frozen=True, slots=True) +class RenderCell: + """One terminal cell: a character, its column width, and SGR style. + + A screen row like ``>>> def`` is a sequence of cells:: + + > > > d e f + ╰─╯╰─╯╰─╯╰─╯╰─╯╰─╯╰─╯ + """ + + text: str + width: int + style: StyleRef = field(default_factory=StyleRef) + controls: tuple[str, ...] = () + + @property + def terminal_text(self) -> str: + return render_cells((self,)) + + +def _theme_style(theme: _ThemeSyntax, tag: str) -> str: + return theme[tag] + + +def _style_escape(style: StyleRef) -> str: + if style.sgr: + return style.sgr + if style.tag is None: + return "" + return _theme_style(THEME(), style.tag) + + +def _update_terminal_state(state: str, escape: str) -> str: + if escape in {"\x1b[0m", "\x1b[m"}: + return "" + return state + escape + + +def _cells_from_rendered_text(text: str) -> tuple[RenderCell, ...]: + if not text: + return () + + cells: list[RenderCell] = [] + pending_controls: list[str] = [] + active_sgr = "" + index = 0 + + def append_plain_text(segment: str) -> None: + nonlocal pending_controls + if not segment: + return + if pending_controls: + cells.append(RenderCell("", 0, controls=tuple(pending_controls))) + pending_controls = [] + for char in segment: + cells.append( + RenderCell( + char, + str_width(char), + style=StyleRef.from_sgr(active_sgr), + ) + ) + + for match in ANSI_ESCAPE_SEQUENCE.finditer(text): + append_plain_text(text[index : match.start()]) + escape = match.group(0) + if escape.endswith("m"): + active_sgr = _update_terminal_state(active_sgr, escape) + else: + pending_controls.append(escape) + index = match.end() + + append_plain_text(text[index:]) + if pending_controls: + cells.append(RenderCell("", 0, controls=tuple(pending_controls))) + + return tuple(cells) + + +@dataclass(frozen=True, slots=True) +class RenderLine: + """One physical screen row as a tuple of :class:`RenderCell` objects. + + ``text`` is the pre-rendered terminal string (characters + SGR escapes); + ``width`` is the total visible column count. + """ + + cells: tuple[RenderCell, ...] + text: str + width: int + + @classmethod + def from_cells(cls, cells: Iterable[RenderCell]) -> Self: + cell_tuple = tuple(cells) + return cls( + cells=cell_tuple, + text=render_cells(cell_tuple), + width=sum(cell.width for cell in cell_tuple), + ) + + @classmethod + def from_parts( + cls, + parts: Sequence[str], + widths: Sequence[int], + styles: Sequence[RenderStyle] | None = None, + ) -> Self: + if styles is None: + return cls.from_cells( + RenderCell(text, width) + for text, width in zip(parts, widths) + ) + + cells: list[RenderCell] = [] + for text, width, style in zip(parts, widths, styles): + if isinstance(style, StyleRef): + cells.append(RenderCell(text, width, style=style)) + elif style is None: + cells.append(RenderCell(text, width)) + else: + cells.append(RenderCell(text, width, style=StyleRef.from_tag(style))) + return cls.from_cells(cells) + + @classmethod + def from_rendered_text(cls, text: str) -> Self: + return cls.from_cells(_cells_from_rendered_text(text)) + + +@dataclass(frozen=True, slots=True) +class ScreenOverlay: + """An overlay that replaces or inserts lines at a screen position. + + If *insert* is True, lines are spliced in (shifting content down); + if False (default), lines replace existing content at *y*. + + Overlays are used to display tab completion menus and status messages. + For example, a tab-completion menu inserted below the input:: + + >>> os.path.j ← line 0 (base content) + join ← ScreenOverlay(y=1, insert=True) + junction ← (pushes remaining lines down) + ... ← line 1 (shifted down by 2) + """ + y: int + lines: tuple[RenderLine, ...] + insert: bool = False + + +@dataclass(frozen=True, slots=True) +class RenderedScreen: + """The complete screen state: content lines, cursor, and overlays. + + ``lines`` holds the base content; ``composed_lines`` is the final + result after overlays (completion menus, messages) are applied:: + + lines: composed_lines: + ┌──────────────────┐ ┌──────────────────┐ + │>>> os.path.j │ │>>> os.path.j │ + │... │ ──► │ join │ ← overlay + └──────────────────┘ │... │ + └──────────────────┘ + """ + + lines: tuple[RenderLine, ...] + cursor: CursorXY + overlays: tuple[ScreenOverlay, ...] = () + composed_lines: tuple[RenderLine, ...] = field(init=False, default=()) + + def __post_init__(self) -> None: + object.__setattr__(self, "composed_lines", self._compose()) + + def _compose(self) -> tuple[RenderLine, ...]: + """Apply overlays in tuple order; inserts shift subsequent positions.""" + if not self.overlays: + return self.lines + + lines = list(self.lines) + y_offset = 0 + for overlay in self.overlays: + adjusted_y = overlay.y + y_offset + assert adjusted_y >= 0, ( + f"Overlay y={overlay.y} with offset={y_offset} is negative; " + "overlays must be sorted by ascending y" + ) + if overlay.insert: + # Splice overlay lines in, pushing existing content down. + lines[adjusted_y:adjusted_y] = overlay.lines + y_offset += len(overlay.lines) + else: + # Replace existing lines at the overlay position. + target_len = adjusted_y + len(overlay.lines) + if len(lines) < target_len: + lines.extend([EMPTY_RENDER_LINE] * (target_len - len(lines))) + for index, line in enumerate(overlay.lines): + lines[adjusted_y + index] = line + return tuple(lines) + + @classmethod + def empty(cls) -> Self: + return cls((), (0, 0), ()) + + @classmethod + def from_screen_lines( + cls, + screen: Sequence[str], + cursor: CursorXY, + ) -> Self: + return cls( + tuple(RenderLine.from_rendered_text(line) for line in screen), + cursor, + (), + ) + + def with_overlay( + self, + y: int, + lines: Iterable[RenderLine], + ) -> Self: + return type(self)( + self.lines, + self.cursor, + self.overlays + (ScreenOverlay(y, tuple(lines)),), + ) + + @property + def screen_lines(self) -> tuple[str, ...]: + return tuple(line.text for line in self.composed_lines) + + +@dataclass(frozen=True, slots=True) +class LineDiff: + """The changed region between an old and new version of one screen row. + + When the user types ``e`` so the row changes from + ``>>> nam`` to ``>>> name``:: + + >>> n a m old + >>> n a m e new + ╰─╯ + start_cell=7, new_cells=("m","e"), old_cells=("m",) + """ + + start_cell: int + start_x: int + old_cells: tuple[RenderCell, ...] + new_cells: tuple[RenderCell, ...] + old_width: int + new_width: int + + @property + def old_text(self) -> str: + return render_cells(self.old_cells) + + @property + def new_text(self) -> str: + return render_cells(self.new_cells) + + @property + def old_changed_width(self) -> int: + return sum(cell.width for cell in self.old_cells) + + @property + def new_changed_width(self) -> int: + return sum(cell.width for cell in self.new_cells) + + +EMPTY_RENDER_LINE = RenderLine(cells=(), text="", width=0) + + +@dataclass(frozen=True, slots=True) +class LineUpdate: + kind: LineUpdateKind + y: int + start_cell: int + start_x: int + """Screen x-coordinate where the update begins. Used for cursor positioning.""" + cells: tuple[RenderCell, ...] + char_width: int = 0 + clear_eol: bool = False + reset_to_margin: bool = False + """If True, the console must resync the cursor position after writing + (needed when cells contain non-SGR escape sequences that may move the cursor).""" + text: str = field(init=False, default="") + + def __post_init__(self) -> None: + object.__setattr__(self, "text", render_cells(self.cells)) + + +def _controls_require_cursor_resync(controls: Sequence[str]) -> bool: + # Anything beyond SGR means the cursor may no longer be where we left it. + return any(not control.endswith("m") for control in controls) + + +def requires_cursor_resync(cells: Sequence[RenderCell]) -> bool: + return any(_controls_require_cursor_resync(cell.controls) for cell in cells) + + +def render_cells( + cells: Sequence[RenderCell], + visual_style: str | None = None, +) -> str: + """Render a sequence of cells into a terminal string with SGR escapes. + + Tracks the active SGR state to emit resets only when the style + actually changes, minimizing output bytes. + + If *visual_style* is given (used by redraw visualization), it is appended + to every cell's style. + """ + rendered: list[str] = [] + active_escape = "" + for cell in cells: + if cell.controls: + rendered.extend(cell.controls) + if not cell.text: + continue + + target_escape = _style_escape(cell.style) + if visual_style is not None: + target_escape += visual_style + if target_escape != active_escape: + if active_escape: + rendered.append("\x1b[0m") + if target_escape: + rendered.append(target_escape) + active_escape = target_escape + rendered.append(cell.text) + + if active_escape: + rendered.append("\x1b[0m") + return "".join(rendered) + + +def diff_render_lines(old: RenderLine, new: RenderLine) -> LineDiff | None: + if old == new: + return None + + prefix = 0 + start_x = 0 + max_prefix = min(len(old.cells), len(new.cells)) + while prefix < max_prefix and old.cells[prefix] == new.cells[prefix]: + # Stop at any cell with non-SGR controls, since those might affect + # cursor position and must be re-emitted. + if old.cells[prefix].controls: + break + start_x += old.cells[prefix].width + prefix += 1 + + old_suffix = len(old.cells) + new_suffix = len(new.cells) + while old_suffix > prefix and new_suffix > prefix: + old_cell = old.cells[old_suffix - 1] + new_cell = new.cells[new_suffix - 1] + if old_cell.controls or new_cell.controls or old_cell != new_cell: + break + old_suffix -= 1 + new_suffix -= 1 + + # Extend diff range to include trailing zero-width combining characters, + # so we never render a combining char without its base character. + while old_suffix < len(old.cells) and old.cells[old_suffix].width == 0: + old_suffix += 1 + while new_suffix < len(new.cells) and new.cells[new_suffix].width == 0: + new_suffix += 1 + + return LineDiff( + start_cell=prefix, + start_x=start_x, + old_cells=old.cells[prefix:old_suffix], + new_cells=new.cells[prefix:new_suffix], + old_width=old.width, + new_width=new.width, + ) diff --git a/Lib/_pyrepl/simple_interact.py b/Lib/_pyrepl/simple_interact.py index 965b853c34b392..c169d0191bd833 100644 --- a/Lib/_pyrepl/simple_interact.py +++ b/Lib/_pyrepl/simple_interact.py @@ -31,7 +31,6 @@ import sys import code import warnings -import errno from .readline import _get_reader, multiline_input, append_history_file @@ -125,7 +124,7 @@ def maybe_run_command(statement: str) -> bool: command = REPL_COMMANDS[statement] if callable(command): # Make sure that history does not change because of commands - with reader.suspend_history(): + with reader.suspend_history(), reader.suspend_colorization(): command() return True return False @@ -158,10 +157,11 @@ def maybe_run_command(statement: str) -> bool: input_n += 1 except KeyboardInterrupt: r = _get_reader() + r.cmpltn_reset() if r.input_trans is r.isearch_trans: r.do_cmd(("isearch-end", [""])) r.pos = len(r.get_unicode()) - r.dirty = True + r.invalidate_full() r.refresh() console.write("\nKeyboardInterrupt\n") console.resetbuffer() diff --git a/Lib/_pyrepl/trace.py b/Lib/_pyrepl/trace.py index 943ee12f964b29..395867805196a5 100644 --- a/Lib/_pyrepl/trace.py +++ b/Lib/_pyrepl/trace.py @@ -32,3 +32,9 @@ def trace(line: str, *k: object, **kw: object) -> None: line = line.format(*k, **kw) trace_file.write(line + "\n") trace_file.flush() + + +def trace_text(text: str, limit: int = 60) -> str: + if len(text) > limit: + text = text[:limit] + "..." + return repr(text) diff --git a/Lib/_pyrepl/types.py b/Lib/_pyrepl/types.py index c5b7ebc1a406bd..919763158eb123 100644 --- a/Lib/_pyrepl/types.py +++ b/Lib/_pyrepl/types.py @@ -4,7 +4,13 @@ type SimpleContextManager = Iterator[None] type KeySpec = str # like r"\C-c" type CommandName = str # like "interrupt" -type EventTuple = tuple[CommandName, str] +type EventData = list[str] +type EventTuple = tuple[CommandName, EventData] +type CursorXY = tuple[int, int] +type Dimensions = tuple[int, int] +type ScreenInfoRow = tuple[int, list[int]] +type Keymap = tuple[tuple[KeySpec, CommandName], ...] type Completer = Callable[[str, int], str | None] type CharBuffer = list[str] type CharWidths = list[int] +type CompletionAction = tuple[str, Callable[[], str | None]] diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index a7e49923191c07..9c4644db53e343 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -31,14 +31,25 @@ import time import types import platform +from collections.abc import Callable +from dataclasses import dataclass from fcntl import ioctl +from typing import TYPE_CHECKING, cast, overload from . import terminfo from .console import Console, Event -from .fancy_termios import tcgetattr, tcsetattr -from .trace import trace +from .fancy_termios import tcgetattr, tcsetattr, TermState +from .render import ( + EMPTY_RENDER_LINE, + LineUpdate, + RenderLine, + RenderedScreen, + requires_cursor_resync, + diff_render_lines, + render_cells, +) +from .trace import trace, trace_text from .unix_eventqueue import EventQueue -from .utils import wlen # declare posix optional to allow None assignment on other platforms posix: types.ModuleType | None @@ -47,20 +58,21 @@ except ImportError: posix = None -TYPE_CHECKING = False - # types if TYPE_CHECKING: - from typing import IO, Literal, overload -else: - overload = lambda func: None + from typing import AbstractSet, IO, Literal + +type _MoveFunc = Callable[[int, int], None] +type _PendingWrite = tuple[str | bytes, bool] class InvalidTerminal(RuntimeError): - pass + def __init__(self, message: str) -> None: + super().__init__(errno.EIO, message) _error = (termios.error, InvalidTerminal) +_error_codes_to_ignore = frozenset([errno.EIO, errno.ENXIO, errno.EPERM]) SIGWINCH_EVENT = "repaint" @@ -125,18 +137,59 @@ def __init__(self): def register(self, fd, flag): self.fd = fd + # note: The 'timeout' argument is received as *milliseconds* def poll(self, timeout: float | None = None) -> list[int]: if timeout is None: r, w, e = select.select([self.fd], [], []) else: - r, w, e = select.select([self.fd], [], [], timeout/1000) + r, w, e = select.select([self.fd], [], [], timeout / 1000) return r poll = MinimalPoll # type: ignore[assignment] +@dataclass(frozen=True, slots=True) +class UnixRefreshPlan: + """Instructions for updating the terminal after a screen change. + + After the user types ``e`` to complete ``name``:: + + Before: >>> def greet(nam|): + ▲ + LineUpdate here: insert_char "e" + + After: >>> def greet(name|): + ▲ + + Only the changed cells are sent to the terminal; unchanged rows + are skipped entirely. + """ + + grow_lines: int + """Number of blank lines to append at the bottom to accommodate new content.""" + use_tall_mode: bool + """Use absolute cursor addressing via ``cup`` instead of relative moves. + Activated when content exceeds one screen height.""" + offset: int + """Vertical scroll offset: the buffer row displayed at the top of the terminal window.""" + reverse_scroll: int + """Number of lines to scroll backwards (content moves down).""" + forward_scroll: int + """Number of lines to scroll forwards (content moves up).""" + line_updates: tuple[LineUpdate, ...] + cleared_lines: tuple[int, ...] + """Row indices to erase (old content with no replacement).""" + rendered_screen: RenderedScreen + cursor: tuple[int, int] + + class UnixConsole(Console): + __buffer: list[_PendingWrite] + __gone_tall: bool + __move: _MoveFunc + __offset: int + def __init__( self, f_in: IO[bytes] | int = 0, @@ -159,9 +212,20 @@ def __init__( self.pollob.register(self.input_fd, select.POLLIN) self.terminfo = terminfo.TermInfo(term or None) self.term = term + self.is_apple_terminal = ( + platform.system() == "Darwin" + and os.getenv("TERM_PROGRAM") == "Apple_Terminal" + ) + + try: + self.__input_fd_set(tcgetattr(self.input_fd), ignore=frozenset()) + except _error as e: + raise RuntimeError(f"termios failure ({e.args[1]})") @overload - def _my_getstr(cap: str, optional: Literal[False] = False) -> bytes: ... + def _my_getstr( + cap: str, optional: Literal[False] = False + ) -> bytes: ... @overload def _my_getstr(cap: str, optional: bool) -> bytes | None: ... @@ -201,8 +265,10 @@ def _my_getstr(cap: str, optional: bool = False) -> bytes | None: self.__setup_movement() - self.event_queue = EventQueue(self.input_fd, self.encoding, self.terminfo) - self.cursor_visible = 1 + self.event_queue = EventQueue( + self.input_fd, self.encoding, self.terminfo + ) + self.cursor_visible = True signal.signal(signal.SIGCONT, self._sigcont_handler) @@ -213,7 +279,6 @@ def _sigcont_handler(self, signum, frame): def __read(self, n: int) -> bytes: return os.read(self.input_fd, n) - def change_encoding(self, encoding: str) -> None: """ Change the encoding used for I/O operations. @@ -223,33 +288,50 @@ def change_encoding(self, encoding: str) -> None: """ self.encoding = encoding - def refresh(self, screen, c_xy): + def refresh(self, rendered_screen: RenderedScreen) -> None: """ Refresh the console screen. Parameters: - - screen (list): List of strings representing the screen contents. - - c_xy (tuple): Cursor position (x, y) on the screen. + - rendered_screen: Structured rendered screen contents and cursor. """ + c_xy = rendered_screen.cursor + trace( + "unix.refresh start cursor={cursor} lines={lines} prev_lines={prev_lines} " + "offset={offset} posxy={posxy}", + cursor=c_xy, + lines=len(rendered_screen.composed_lines), + prev_lines=len(self._rendered_screen.composed_lines), + offset=self.__offset, + posxy=self.posxy, + ) + plan = self.__plan_refresh(rendered_screen, c_xy) + self.__apply_refresh_plan(plan) + + def __plan_refresh( + self, + rendered_screen: RenderedScreen, + c_xy: tuple[int, int], + ) -> UnixRefreshPlan: cx, cy = c_xy + height = self.height + old_offset = offset = self.__offset + prev_composed = self._rendered_screen.composed_lines + previous_lines = list(prev_composed) + next_lines = list(rendered_screen.composed_lines) + line_count = len(next_lines) + + grow_lines = 0 if not self.__gone_tall: - while len(self.screen) < min(len(screen), self.height): - self.__hide_cursor() - self.__move(0, len(self.screen) - 1) - self.__write("\n") - self.posxy = 0, len(self.screen) - self.screen.append("") - else: - while len(self.screen) < len(screen): - self.screen.append("") + grow_lines = max( + min(line_count, height) - len(prev_composed), + 0, + ) + previous_lines.extend([EMPTY_RENDER_LINE] * grow_lines) + elif len(previous_lines) < line_count: + previous_lines.extend([EMPTY_RENDER_LINE] * (line_count - len(previous_lines))) - if len(screen) > self.height: - self.__gone_tall = 1 - self.__move = self.__move_tall - - px, py = self.posxy - old_offset = offset = self.__offset - height = self.height + use_tall_mode = self.__gone_tall or line_count > height # we make sure the cursor is on the screen, and that we're # using all of the screen if we can @@ -257,56 +339,115 @@ def refresh(self, screen, c_xy): offset = cy elif cy >= offset + height: offset = cy - height + 1 - elif offset > 0 and len(screen) < offset + height: - offset = max(len(screen) - height, 0) - screen.append("") + elif offset > 0 and line_count < offset + height: + offset = max(line_count - height, 0) + next_lines.append(EMPTY_RENDER_LINE) - oldscr = self.screen[old_offset : old_offset + height] - newscr = screen[offset : offset + height] + oldscr = previous_lines[old_offset : old_offset + height] + newscr = next_lines[offset : offset + height] - # use hardware scrolling if we have it. + reverse_scroll = 0 + forward_scroll = 0 if old_offset > offset and self._ri: + reverse_scroll = old_offset - offset + for _ in range(reverse_scroll): + if oldscr: + oldscr.pop(-1) + oldscr.insert(0, EMPTY_RENDER_LINE) + elif old_offset < offset and self._ind: + forward_scroll = offset - old_offset + for _ in range(forward_scroll): + if oldscr: + oldscr.pop(0) + oldscr.append(EMPTY_RENDER_LINE) + + line_updates: list[LineUpdate] = [] + px, _ = self.posxy + for y, oldline, newline in zip(range(offset, offset + height), oldscr, newscr): + update = self.__plan_changed_line(y, oldline, newline, px) + if update is not None: + line_updates.append(update) + + cleared_lines = tuple(range(offset + len(newscr), offset + len(oldscr))) + console_rendered_screen = RenderedScreen(tuple(next_lines), c_xy) + trace( + "unix.refresh plan grow={grow} tall={tall} offset={offset} " + "reverse_scroll={reverse_scroll} forward_scroll={forward_scroll} " + "updates={updates} clears={clears}", + grow=grow_lines, + tall=use_tall_mode, + offset=offset, + reverse_scroll=reverse_scroll, + forward_scroll=forward_scroll, + updates=len(line_updates), + clears=len(cleared_lines), + ) + return UnixRefreshPlan( + grow_lines=grow_lines, + use_tall_mode=use_tall_mode, + offset=offset, + reverse_scroll=reverse_scroll, + forward_scroll=forward_scroll, + line_updates=tuple(line_updates), + cleared_lines=cleared_lines, + rendered_screen=console_rendered_screen, + cursor=(cx, cy), + ) + + def __apply_refresh_plan(self, plan: UnixRefreshPlan) -> None: + cx, cy = plan.cursor + trace( + "unix.refresh apply cursor={cursor} updates={updates} clears={clears}", + cursor=plan.cursor, + updates=len(plan.line_updates), + clears=len(plan.cleared_lines), + ) + visual_style = self.begin_redraw_visualization() + screen_line_count = len(self._rendered_screen.composed_lines) + + for _ in range(plan.grow_lines): + self.__hide_cursor() + if screen_line_count: + self.__move(0, screen_line_count - 1) + self.__write("\n") + self.posxy = 0, screen_line_count + screen_line_count += 1 + + if plan.use_tall_mode and not self.__gone_tall: + self.__gone_tall = True + self.__move = self.__move_tall + + old_offset = self.__offset + if plan.reverse_scroll: self.__hide_cursor() self.__write_code(self._cup, 0, 0) self.posxy = 0, old_offset - for i in range(old_offset - offset): + for _ in range(plan.reverse_scroll): self.__write_code(self._ri) - oldscr.pop(-1) - oldscr.insert(0, "") - elif old_offset < offset and self._ind: + elif plan.forward_scroll: self.__hide_cursor() self.__write_code(self._cup, self.height - 1, 0) self.posxy = 0, old_offset + self.height - 1 - for i in range(offset - old_offset): + for _ in range(plan.forward_scroll): self.__write_code(self._ind) - oldscr.pop(0) - oldscr.append("") - self.__offset = offset + self.__offset = plan.offset - for ( - y, - oldline, - newline, - ) in zip(range(offset, offset + height), oldscr, newscr): - if oldline != newline: - self.__write_changed_line(y, oldline, newline, px) + for update in plan.line_updates: + self.__apply_line_update(update, visual_style) - y = len(newscr) - while y < len(oldscr): + for y in plan.cleared_lines: self.__hide_cursor() self.__move(0, y) self.posxy = 0, y self.__write_code(self._el) - y += 1 self.__show_cursor() - - self.screen = screen.copy() self.move_cursor(cx, cy) self.flushoutput() + self.sync_rendered_screen(plan.rendered_screen, self.posxy) - def move_cursor(self, x, y): + def move_cursor(self, x: int, y: int) -> None: """ Move the cursor to the specified position on the screen. @@ -315,16 +456,27 @@ def move_cursor(self, x, y): - y (int): Y coordinate. """ if y < self.__offset or y >= self.__offset + self.height: - self.event_queue.insert(Event("scroll", None)) + trace( + "unix.move_cursor offscreen x={x} y={y} offset={offset} height={height}", + x=x, + y=y, + offset=self.__offset, + height=self.height, + ) + self.event_queue.insert(Event("scroll", "")) else: + trace("unix.move_cursor x={x} y={y}", x=x, y=y) self.__move(x, y) self.posxy = x, y self.flushoutput() - def prepare(self): + def prepare(self) -> None: """ Prepare the console for input/output operations. """ + trace("unix.prepare") + self.__buffer = [] + self.__svtermstate = tcgetattr(self.input_fd) raw = self.__svtermstate.copy() raw.iflag &= ~(termios.INPCK | termios.ISTRIP | termios.IXON) @@ -334,23 +486,22 @@ def prepare(self): raw.iflag |= termios.BRKINT raw.lflag &= ~(termios.ICANON | termios.ECHO | termios.IEXTEN) raw.lflag |= termios.ISIG - raw.cc[termios.VMIN] = 1 - raw.cc[termios.VTIME] = 0 - tcsetattr(self.input_fd, termios.TCSADRAIN, raw) + raw.cc[termios.VMIN] = b"\x01" + raw.cc[termios.VTIME] = b"\x00" + self.__input_fd_set(raw) - # In macOS terminal we need to deactivate line wrap via ANSI escape code - if platform.system() == "Darwin" and os.getenv("TERM_PROGRAM") == "Apple_Terminal": + # Apple Terminal will re-wrap lines for us unless we preempt the + # damage. + if self.is_apple_terminal: os.write(self.output_fd, b"\033[?7l") - self.screen = [] self.height, self.width = self.getheightwidth() - self.__buffer = [] - self.posxy = 0, 0 - self.__gone_tall = 0 + self.__gone_tall = False self.__move = self.__move_short self.__offset = 0 + self.sync_rendered_screen(RenderedScreen.empty(), self.posxy) self.__maybe_write_code(self._smkx) @@ -361,20 +512,26 @@ def prepare(self): self.__enable_bracketed_paste() - def restore(self): + def restore(self) -> None: """ Restore the console to the default state """ + trace("unix.restore") self.__disable_bracketed_paste() self.__maybe_write_code(self._rmkx) self.flushoutput() - tcsetattr(self.input_fd, termios.TCSADRAIN, self.__svtermstate) + self.__input_fd_set(self.__svtermstate) - if platform.system() == "Darwin" and os.getenv("TERM_PROGRAM") == "Apple_Terminal": + if self.is_apple_terminal: os.write(self.output_fd, b"\033[?7h") if hasattr(self, "old_sigwinch"): - signal.signal(signal.SIGWINCH, self.old_sigwinch) + try: + signal.signal(signal.SIGWINCH, self.old_sigwinch) + except ValueError as e: + import threading + if threading.current_thread() is threading.main_thread(): + raise e del self.old_sigwinch def push_char(self, char: int | bytes) -> None: @@ -407,6 +564,8 @@ def get_event(self, block: bool = True) -> Event | None: return self.event_queue.get() else: continue + elif err.errno == errno.EIO: + raise SystemExit(errno.EIO) else: raise else: @@ -422,7 +581,7 @@ def wait(self, timeout: float | None = None) -> bool: or bool(self.pollob.poll(timeout)) ) - def set_cursor_vis(self, visible): + def set_cursor_vis(self, visible: bool) -> None: """ Set the visibility of the cursor. @@ -490,8 +649,9 @@ def finish(self): """ Finish console operations and flush the output buffer. """ - y = len(self.screen) - 1 - while y >= 0 and not self.screen[y]: + rendered_lines = self._rendered_screen.composed_lines + y = len(rendered_lines) - 1 + while y >= 0 and not rendered_lines[y].text: y -= 1 self.__move(0, min(y, self.height + self.__offset - 1)) self.__write("\n\r") @@ -518,7 +678,7 @@ def getpending(self): while not self.event_queue.empty(): e2 = self.event_queue.get() e.data += e2.data - e.raw += e.raw + e.raw += e2.raw amount = struct.unpack("i", ioctl(self.input_fd, FIONREAD, b"\0\0\0\0"))[0] trace("getpending({a})", a=amount) @@ -542,7 +702,7 @@ def getpending(self): while not self.event_queue.empty(): e2 = self.event_queue.get() e.data += e2.data - e.raw += e.raw + e.raw += e2.raw amount = 10000 raw = self.__read(amount) @@ -555,11 +715,12 @@ def clear(self): """ Clear the console screen. """ + trace("unix.clear") self.__write_code(self._clear) - self.__gone_tall = 1 + self.__gone_tall = True self.__move = self.__move_tall self.posxy = 0, 0 - self.screen = [] + self.sync_rendered_screen(RenderedScreen.empty(), self.posxy) @property def input_hook(self): @@ -610,98 +771,178 @@ def __setup_movement(self): self.__move = self.__move_short - def __write_changed_line(self, y, oldline, newline, px_coord): - # this is frustrating; there's no reason to test (say) - # self.dch1 inside the loop -- but alternative ways of - # structuring this function are equally painful (I'm trying to - # avoid writing code generators these days...) - minlen = min(wlen(oldline), wlen(newline)) - x_pos = 0 - x_coord = 0 - - px_pos = 0 - j = 0 - for c in oldline: - if j >= px_coord: - break - j += wlen(c) - px_pos += 1 - - # reuse the oldline as much as possible, but stop as soon as we - # encounter an ESCAPE, because it might be the start of an escape - # sequence - while ( - x_coord < minlen - and oldline[x_pos] == newline[x_pos] - and newline[x_pos] != "\x1b" - ): - x_coord += wlen(newline[x_pos]) - x_pos += 1 + @staticmethod + def __cell_index_from_x(line: RenderLine, x_coord: int) -> int: + width = 0 + index = 0 + while index < len(line.cells) and width < x_coord: + width += line.cells[index].width + index += 1 + return index + + def __plan_changed_line( + self, + y: int, + oldline: RenderLine, + newline: RenderLine, + px_coord: int, + ) -> LineUpdate | None: + # NOTE: The shared replace_char / replace_span / rewrite_suffix logic + # is duplicated in WindowsConsole.__plan_changed_line. Keep changes to + # these common cases synchronised between the two files. Yes, this is + # duplicated on purpose; the two backends agree just enough to make a + # shared helper a trap. Unix-only cases (insert_char, delete_then_insert) + # rely on terminal capabilities (ich1/dch1) that are unavailable on + # Windows. + diff = diff_render_lines(oldline, newline) + if diff is None: + return None - # if we need to insert a single character right after the first detected change - if oldline[x_pos:] == newline[x_pos + 1 :] and self.ich1: + start_cell = diff.start_cell + start_x = diff.start_x + + if ( + self.ich1 + and not diff.old_cells + and (visible_new_cells := tuple( + cell for cell in diff.new_cells if cell.width + )) + and len(visible_new_cells) == 1 + and all(cell.width == 0 for cell in diff.new_cells[1:]) + and oldline.cells[start_cell:] == newline.cells[start_cell + 1 :] + ): + px_cell = self.__cell_index_from_x(oldline, px_coord) if ( y == self.posxy[1] - and x_coord > self.posxy[0] - and oldline[px_pos:x_pos] == newline[px_pos + 1 : x_pos + 1] + and start_x > self.posxy[0] + and oldline.cells[px_cell:start_cell] + == newline.cells[px_cell + 1 : start_cell + 1] ): - x_pos = px_pos - x_coord = px_coord - character_width = wlen(newline[x_pos]) - self.__move(x_coord, y) - self.__write_code(self.ich1) - self.__write(newline[x_pos]) - self.posxy = x_coord + character_width, y - - # if it's a single character change in the middle of the line - elif ( - x_coord < minlen - and oldline[x_pos + 1 :] == newline[x_pos + 1 :] - and wlen(oldline[x_pos]) == wlen(newline[x_pos]) + start_cell = px_cell + start_x = px_coord + planned_cells = diff.new_cells + changed_cell = visible_new_cells[0] + return LineUpdate( + kind="insert_char", + y=y, + start_cell=start_cell, + start_x=start_x, + cells=planned_cells, + char_width=changed_cell.width, + reset_to_margin=requires_cursor_resync(planned_cells), + ) + + if ( + len(diff.old_cells) == 1 + and len(diff.new_cells) == 1 + and diff.old_cells[0].width == diff.new_cells[0].width ): - character_width = wlen(newline[x_pos]) - self.__move(x_coord, y) - self.__write(newline[x_pos]) - self.posxy = x_coord + character_width, y - - # if this is the last character to fit in the line and we edit in the middle of the line - elif ( + planned_cells = diff.new_cells + changed_cell = planned_cells[0] + return LineUpdate( + kind="replace_char", + y=y, + start_cell=start_cell, + start_x=start_x, + cells=planned_cells, + char_width=changed_cell.width, + reset_to_margin=requires_cursor_resync(planned_cells), + ) + + if diff.old_changed_width == diff.new_changed_width: + planned_cells = diff.new_cells + return LineUpdate( + kind="replace_span", + y=y, + start_cell=start_cell, + start_x=start_x, + cells=planned_cells, + char_width=diff.new_changed_width, + reset_to_margin=requires_cursor_resync(planned_cells), + ) + + if ( self.dch1 and self.ich1 - and wlen(newline) == self.width - and x_coord < wlen(newline) - 2 - and newline[x_pos + 1 : -1] == oldline[x_pos:-2] + and newline.width == self.width + and start_x < newline.width - 2 + and newline.cells[start_cell + 1 : -1] == oldline.cells[start_cell:-2] ): + planned_cells = (newline.cells[start_cell],) + changed_cell = planned_cells[0] + return LineUpdate( + kind="delete_then_insert", + y=y, + start_cell=start_cell, + start_x=start_x, + cells=planned_cells, + char_width=changed_cell.width, + reset_to_margin=requires_cursor_resync(planned_cells), + ) + + suffix_cells = newline.cells[start_cell:] + return LineUpdate( + kind="rewrite_suffix", + y=y, + start_cell=start_cell, + start_x=start_x, + cells=suffix_cells, + char_width=sum(cell.width for cell in suffix_cells), + clear_eol=oldline.width > newline.width, + reset_to_margin=requires_cursor_resync(suffix_cells), + ) + + def __apply_line_update( + self, + update: LineUpdate, + visual_style: str | None = None, + ) -> None: + text = render_cells(update.cells, visual_style) if visual_style else update.text + trace( + "unix.refresh update kind={kind} y={y} x={x} text={text} " + "clear_eol={clear_eol} reset_to_margin={reset}", + kind=update.kind, + y=update.y, + x=update.start_x, + text=trace_text(text), + clear_eol=update.clear_eol, + reset=update.reset_to_margin, + ) + if update.kind == "insert_char": + self.__move(update.start_x, update.y) + self.__write_code(self.ich1) + self.__write(text) + self.posxy = update.start_x + update.char_width, update.y + elif update.kind in {"replace_char", "replace_span"}: + self.__move(update.start_x, update.y) + self.__write(text) + self.posxy = update.start_x + update.char_width, update.y + elif update.kind == "delete_then_insert": self.__hide_cursor() - self.__move(self.width - 2, y) - self.posxy = self.width - 2, y + self.__move(self.width - 2, update.y) + self.posxy = self.width - 2, update.y self.__write_code(self.dch1) - - character_width = wlen(newline[x_pos]) - self.__move(x_coord, y) + self.__move(update.start_x, update.y) self.__write_code(self.ich1) - self.__write(newline[x_pos]) - self.posxy = character_width + 1, y - + self.__write(text) + self.posxy = update.start_x + update.char_width, update.y else: self.__hide_cursor() - self.__move(x_coord, y) - if wlen(oldline) > wlen(newline): + self.__move(update.start_x, update.y) + if update.clear_eol: self.__write_code(self._el) - self.__write(newline[x_pos:]) - self.posxy = wlen(newline), y + self.__write(text) + self.posxy = update.start_x + update.char_width, update.y - if "\x1b" in newline: - # ANSI escape characters are present, so we can't assume - # anything about the position of the cursor. Moving the cursor - # to the left margin should work to get to a known position. - self.move_cursor(0, y) + if update.reset_to_margin: + # Non-SGR terminal controls can affect the cursor position. + self.move_cursor(0, update.y) def __write(self, text): - self.__buffer.append((text, 0)) + self.__buffer.append((text, False)) def __write_code(self, fmt, *args): - self.__buffer.append((terminfo.tparm(fmt, *args), 1)) + self.__buffer.append((terminfo.tparm(fmt, *args), True)) def __maybe_write_code(self, fmt, *args): if fmt: @@ -752,30 +993,38 @@ def __move_tall(self, x, y): self.__write_code(self._cup, y - self.__offset, x) def __sigwinch(self, signum, frame): - self.height, self.width = self.getheightwidth() - self.event_queue.insert(Event("resize", None)) + self.event_queue.insert(Event("resize", "")) def __hide_cursor(self): if self.cursor_visible: self.__maybe_write_code(self._civis) - self.cursor_visible = 0 + self.cursor_visible = False def __show_cursor(self): if not self.cursor_visible: self.__maybe_write_code(self._cnorm) - self.cursor_visible = 1 + self.cursor_visible = True def repaint(self): + composed = self._rendered_screen.composed_lines + trace( + "unix.repaint gone_tall={gone_tall} screen_lines={lines} offset={offset}", + gone_tall=self.__gone_tall, + lines=len(composed), + offset=self.__offset, + ) if not self.__gone_tall: self.posxy = 0, self.posxy[1] self.__write("\r") - ns = len(self.screen) * ["\000" * self.width] - self.screen = ns + ns = len(composed) * ["\000" * self.width] else: self.posxy = 0, self.__offset self.__move(0, self.__offset) ns = self.height * ["\000" * self.width] - self.screen = ns + self.sync_rendered_screen( + RenderedScreen.from_screen_lines(ns, self.posxy), + self.posxy, + ) def __tputs(self, fmt, prog=delayprog): """A Python implementation of the curses tputs function; the @@ -785,7 +1034,7 @@ def __tputs(self, fmt, prog=delayprog): will never do anyone any good.""" # using .get() means that things will blow up # only if the bps is actually needed (which I'm - # betting is pretty unlkely) + # betting is pretty unlikely) bps = ratedict.get(self.__svtermstate.ospeed) while True: m = prog.search(fmt) @@ -803,3 +1052,17 @@ def __tputs(self, fmt, prog=delayprog): os.write(self.output_fd, self._pad * nchars) else: time.sleep(float(delay) / 1000.0) + + def __input_fd_set( + self, + state: TermState, + ignore: AbstractSet[int] = _error_codes_to_ignore, + ) -> bool: + try: + tcsetattr(self.input_fd, termios.TCSADRAIN, state) + except termios.error as te: + if te.args[0] not in ignore: + raise + return False + else: + return True diff --git a/Lib/_pyrepl/utils.py b/Lib/_pyrepl/utils.py index fd788c8429e15b..f2837a1b8eb95e 100644 --- a/Lib/_pyrepl/utils.py +++ b/Lib/_pyrepl/utils.py @@ -9,6 +9,7 @@ import _colorize from collections import deque +from dataclasses import dataclass from io import StringIO from tokenize import TokenInfo as TI from typing import Iterable, Iterator, Match, NamedTuple, Self @@ -16,11 +17,13 @@ from .types import CharBuffer, CharWidths from .trace import trace + ANSI_ESCAPE_SEQUENCE = re.compile(r"\x1b\[[ -@]*[A-~]") ZERO_WIDTH_BRACKET = re.compile(r"\x01.*?\x02") ZERO_WIDTH_TRANS = str.maketrans({"\x01": "", "\x02": ""}) -IDENTIFIERS_AFTER = {"def", "class"} -BUILTINS = {str(name) for name in dir(builtins) if not name.startswith('_')} +IDENTIFIERS_AFTER = frozenset({"def", "class"}) +KEYWORD_CONSTANTS = frozenset({"True", "False", "None"}) +BUILTINS = frozenset({str(name) for name in dir(builtins) if not name.startswith('_')}) def THEME(**kwargs): @@ -58,10 +61,31 @@ class ColorSpan(NamedTuple): tag: str +class StyledChar(NamedTuple): + text: str + width: int + tag: str | None = None + + +def _ascii_control_repr(c: str) -> str | None: + code = ord(c) + if code < 32: + return "^" + chr(code + 64) + if code == 127: + return "^?" + return None + + @functools.cache def str_width(c: str) -> int: if ord(c) < 128: return 1 + # gh-139246 for zero-width joiner and combining characters + if unicodedata.combining(c): + return 0 + category = unicodedata.category(c) + if category == "Cf" and c != "\u00ad": + return 0 w = unicodedata.east_asian_width(c) if w in ("N", "Na", "H", "A"): return 1 @@ -197,8 +221,11 @@ def gen_colors_from_token_stream( span = Span.from_token(token, line_lengths) yield ColorSpan(span, "definition") elif keyword.iskeyword(token.string): + span_cls = "keyword" + if token.string in KEYWORD_CONSTANTS: + span_cls = "keyword_constant" span = Span.from_token(token, line_lengths) - yield ColorSpan(span, "keyword") + yield ColorSpan(span, span_cls) if token.string in IDENTIFIERS_AFTER: is_def_name = True elif ( @@ -208,13 +235,16 @@ def gen_colors_from_token_stream( ): span = Span.from_token(token, line_lengths) yield ColorSpan(span, "soft_keyword") - elif token.string in BUILTINS: + elif ( + token.string in BUILTINS + and not (prev_token and prev_token.exact_type == T.DOT) + ): span = Span.from_token(token, line_lengths) yield ColorSpan(span, "builtin") -keyword_first_sets_match = {"False", "None", "True", "await", "lambda", "not"} -keyword_first_sets_case = {"False", "None", "True"} +keyword_first_sets_match = frozenset({"False", "None", "True", "await", "lambda", "not"}) +keyword_first_sets_case = frozenset({"False", "None", "True"}) def is_soft_keyword_used(*tokens: TI | None) -> bool: @@ -244,7 +274,7 @@ def is_soft_keyword_used(*tokens: TI | None) -> bool: None | TI(T.NEWLINE) | TI(T.INDENT) | TI(T.DEDENT) | TI(string=":"), TI(string="case"), TI(T.NUMBER | T.STRING | T.FSTRING_START | T.TSTRING_START) - | TI(T.OP, string="(" | "*" | "-" | "[" | "{") + | TI(T.OP, string="(" | "*" | "-" | "+" | "[" | "{") ): return True case ( @@ -257,10 +287,77 @@ def is_soft_keyword_used(*tokens: TI | None) -> bool: return True case (TI(string="case"), TI(string="_"), TI(string=":")): return True + case ( + None | TI(T.NEWLINE) | TI(T.INDENT) | TI(T.DEDENT) | TI(string=":"), + TI(string="type"), + TI(T.NAME, string=s) + ): + return not keyword.iskeyword(s) + case ( + None | TI(T.NEWLINE) | TI(T.INDENT) | TI(T.DEDENT) | TI(string=":" | ";"), + TI(string="lazy"), + TI(string="import") | TI(string="from") + ): + return True case _: return False +def iter_display_chars( + buffer: str, + colors: list[ColorSpan] | None = None, + start_index: int = 0, +) -> Iterator[StyledChar]: + """Yield visible display characters with widths and semantic color tags. + + Note: ``colors`` is consumed in place as spans are processed -- callers + that split a buffer across multiple calls rely on this mutation to track + which spans have already been handled. + """ + + if not buffer: + return + + color_idx = 0 + if colors: + while color_idx < len(colors) and colors[color_idx].span.end < start_index: + color_idx += 1 + + active_tag = None + if colors and color_idx < len(colors) and colors[color_idx].span.start < start_index: + active_tag = colors[color_idx].tag + + for i, c in enumerate(buffer, start_index): + if colors and color_idx < len(colors) and colors[color_idx].span.start == i: + active_tag = colors[color_idx].tag + + if control := _ascii_control_repr(c): + text = control + width = len(control) + elif ord(c) < 128: + text = c + width = 1 + elif unicodedata.category(c).startswith("C"): + text = r"\u%04x" % ord(c) + width = len(text) + else: + text = c + width = str_width(c) + + yield StyledChar(text, width, active_tag) + + if colors and color_idx < len(colors) and colors[color_idx].span.end == i: + color_idx += 1 + active_tag = None + # Check if the next span starts at the same position + if color_idx < len(colors) and colors[color_idx].span.start == i: + active_tag = colors[color_idx].tag + + # Remove consumed spans so callers see the mutation + if color_idx > 0 and colors: + del colors[:color_idx] + + def disp_str( buffer: str, colors: list[ColorSpan] | None = None, @@ -296,53 +393,18 @@ def disp_str( (['\x1b[1;34mw', 'h', 'i', 'l', 'e\x1b[0m', ' ', '1', ':'], [1, 1, 1, 1, 1, 1, 1, 1]) """ + styled_chars = list(iter_display_chars(buffer, colors, start_index)) chars: CharBuffer = [] char_widths: CharWidths = [] - - if not buffer: - return chars, char_widths - - while colors and colors[0].span.end < start_index: - # move past irrelevant spans - colors.pop(0) - theme = THEME(force_color=force_color) - pre_color = "" - post_color = "" - if colors and colors[0].span.start < start_index: - # looks like we're continuing a previous color (e.g. a multiline str) - pre_color = theme[colors[0].tag] - for i, c in enumerate(buffer, start_index): - if colors and colors[0].span.start == i: # new color starts now - pre_color = theme[colors[0].tag] - - if c == "\x1a": # CTRL-Z on Windows - chars.append(c) - char_widths.append(2) - elif ord(c) < 128: - chars.append(c) - char_widths.append(1) - elif unicodedata.category(c).startswith("C"): - c = r"\u%04x" % ord(c) - chars.append(c) - char_widths.append(len(c)) - else: - chars.append(c) - char_widths.append(str_width(c)) - - if colors and colors[0].span.end == i: # current color ends now - post_color = theme.reset - colors.pop(0) - - chars[-1] = pre_color + chars[-1] + post_color - pre_color = "" - post_color = "" - - if colors and colors[0].span.start < i and colors[0].span.end > i: - # even though the current color should be continued, reset it for now. - # the next call to `disp_str()` will revive it. - chars[-1] += theme.reset + for index, styled_char in enumerate(styled_chars): + previous_tag = styled_chars[index - 1].tag if index else None + next_tag = styled_chars[index + 1].tag if index + 1 < len(styled_chars) else None + prefix = theme[styled_char.tag] if styled_char.tag and styled_char.tag != previous_tag else "" + suffix = theme.reset if styled_char.tag and styled_char.tag != next_tag else "" + chars.append(prefix + styled_char.text + suffix) + char_widths.append(styled_char.width) return chars, char_widths @@ -360,13 +422,35 @@ def prev_next_window[T]( """ iterator = iter(iterable) - window = deque((None, next(iterator)), maxlen=3) + try: + first = next(iterator) + except StopIteration: + return + window = deque((None, first), maxlen=3) try: for x in iterator: window.append(x) yield tuple(window) - except Exception: - raise finally: window.append(None) yield tuple(window) + + +@dataclass(frozen=True, slots=True) +class StyleRef: + tag: str | None = None # From THEME().syntax, e.g. "keyword", "builtin" + sgr: str = "" + + @classmethod + def from_tag(cls, tag: str, sgr: str = "") -> Self: + return cls(tag=tag, sgr=sgr) + + @classmethod + def from_sgr(cls, sgr: str) -> Self: + if not sgr: + return cls() + return cls(sgr=sgr) + + @property + def is_plain(self) -> bool: + return self.tag is None and not self.sgr diff --git a/Lib/_pyrepl/windows_console.py b/Lib/_pyrepl/windows_console.py index c56dcd6d7dd434..c1f9a19545d35f 100644 --- a/Lib/_pyrepl/windows_console.py +++ b/Lib/_pyrepl/windows_console.py @@ -25,6 +25,7 @@ import ctypes import types +from dataclasses import dataclass from ctypes.wintypes import ( _COORD, WORD, @@ -37,20 +38,26 @@ SHORT, ) from ctypes import Structure, POINTER, Union +from typing import TYPE_CHECKING from .console import Event, Console -from .trace import trace -from .utils import wlen +from .render import ( + EMPTY_RENDER_LINE, + LineUpdate, + RenderLine, + RenderedScreen, + requires_cursor_resync, + diff_render_lines, + render_cells, +) +from .trace import trace, trace_text from .windows_eventqueue import EventQueue try: - from ctypes import get_last_error, GetLastError, WinDLL, windll, WinError # type: ignore[attr-defined] + from ctypes import get_last_error, WinDLL, windll, WinError # type: ignore[attr-defined] except: # Keep MyPy happy off Windows from ctypes import CDLL as WinDLL, cdll as windll - def GetLastError() -> int: - return 42 - def get_last_error() -> int: return 42 @@ -66,8 +73,6 @@ def __init__(self, err: int | None, descr: str | None = None) -> None: except ImportError: nt = None -TYPE_CHECKING = False - if TYPE_CHECKING: from typing import IO @@ -126,6 +131,17 @@ def __init__(self, err: int | None, descr: str | None = None) -> None: class _error(Exception): pass + +@dataclass(frozen=True, slots=True) +class WindowsRefreshPlan: + grow_lines: int + offset: int + scroll_lines: int + line_updates: tuple[LineUpdate, ...] + cleared_lines: tuple[int, ...] + rendered_screen: RenderedScreen + cursor: tuple[int, int] + def _supports_vt(): try: return nt._supports_virtual_terminal() @@ -149,18 +165,19 @@ def __init__( # Save original console modes so we can recover on cleanup. original_input_mode = DWORD() - GetConsoleMode(InHandle, original_input_mode) + if not GetConsoleMode(InHandle, original_input_mode): + raise WinError(get_last_error()) trace(f'saved original input mode 0x{original_input_mode.value:x}') self.__original_input_mode = original_input_mode.value - SetConsoleMode( + if not SetConsoleMode( OutHandle, ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING, - ) + ): + raise WinError(get_last_error()) - self.screen: list[str] = [] self.width = 80 self.height = 25 self.__offset = 0 @@ -171,73 +188,124 @@ def __init__( # Console I/O is redirected, fallback... self.out = None - def refresh(self, screen: list[str], c_xy: tuple[int, int]) -> None: + def refresh(self, rendered_screen: RenderedScreen) -> None: """ Refresh the console screen. Parameters: - - screen (list): List of strings representing the screen contents. - - c_xy (tuple): Cursor position (x, y) on the screen. + - rendered_screen: Structured rendered screen contents and cursor. """ - cx, cy = c_xy - - while len(self.screen) < min(len(screen), self.height): - self._hide_cursor() - self._move_relative(0, len(self.screen) - 1) - self.__write("\n") - self.posxy = 0, len(self.screen) - self.screen.append("") + c_xy = rendered_screen.cursor + trace( + "windows.refresh start cursor={cursor} lines={lines} prev_lines={prev_lines} " + "offset={offset} posxy={posxy}", + cursor=c_xy, + lines=len(rendered_screen.composed_lines), + prev_lines=len(self._rendered_screen.composed_lines), + offset=self.__offset, + posxy=self.posxy, + ) + plan = self.__plan_refresh(rendered_screen, c_xy) + self.__apply_refresh_plan(plan) - px, py = self.posxy - old_offset = offset = self.__offset + def __plan_refresh( + self, + rendered_screen: RenderedScreen, + c_xy: tuple[int, int], + ) -> WindowsRefreshPlan: + cx, cy = c_xy height = self.height + old_offset = offset = self.__offset + prev_composed = self._rendered_screen.composed_lines + previous_lines = list(prev_composed) + next_lines = list(rendered_screen.composed_lines) + line_count = len(next_lines) + + grow_lines = max( + min(line_count, height) - len(prev_composed), + 0, + ) + previous_lines.extend([EMPTY_RENDER_LINE] * grow_lines) - # we make sure the cursor is on the screen, and that we're - # using all of the screen if we can + scroll_lines = 0 if cy < offset: offset = cy elif cy >= offset + height: offset = cy - height + 1 scroll_lines = offset - old_offset + previous_lines.extend([EMPTY_RENDER_LINE] * scroll_lines) + elif offset > 0 and line_count < offset + height: + offset = max(line_count - height, 0) + next_lines.append(EMPTY_RENDER_LINE) + + oldscr = previous_lines[old_offset : old_offset + height] + newscr = next_lines[offset : offset + height] + + line_updates: list[LineUpdate] = [] + px, _ = self.posxy + for y, oldline, newline in zip(range(offset, offset + height), oldscr, newscr): + update = self.__plan_changed_line(y, oldline, newline, px) + if update is not None: + line_updates.append(update) + + cleared_lines = tuple(range(offset + len(newscr), offset + len(oldscr))) + console_rendered_screen = RenderedScreen(tuple(next_lines), c_xy) + trace( + "windows.refresh plan grow={grow} offset={offset} scroll_lines={scroll_lines} " + "updates={updates} clears={clears}", + grow=grow_lines, + offset=offset, + scroll_lines=scroll_lines, + updates=len(line_updates), + clears=len(cleared_lines), + ) + return WindowsRefreshPlan( + grow_lines=grow_lines, + offset=offset, + scroll_lines=scroll_lines, + line_updates=tuple(line_updates), + cleared_lines=cleared_lines, + rendered_screen=console_rendered_screen, + cursor=(cx, cy), + ) - # Scrolling the buffer as the current input is greater than the visible - # portion of the window. We need to scroll the visible portion and the - # entire history - self._scroll(scroll_lines, self._getscrollbacksize()) - self.posxy = self.posxy[0], self.posxy[1] + scroll_lines - self.__offset += scroll_lines + def __apply_refresh_plan(self, plan: WindowsRefreshPlan) -> None: + cx, cy = plan.cursor + trace( + "windows.refresh apply cursor={cursor} updates={updates} clears={clears}", + cursor=plan.cursor, + updates=len(plan.line_updates), + clears=len(plan.cleared_lines), + ) + visual_style = self.begin_redraw_visualization() + screen_line_count = len(self._rendered_screen.composed_lines) - for i in range(scroll_lines): - self.screen.append("") - elif offset > 0 and len(screen) < offset + height: - offset = max(len(screen) - height, 0) - screen.append("") + for _ in range(plan.grow_lines): + self._hide_cursor() + if screen_line_count: + self._move_relative(0, screen_line_count - 1) + self.__write("\n") + self.posxy = 0, screen_line_count + screen_line_count += 1 - oldscr = self.screen[old_offset : old_offset + height] - newscr = screen[offset : offset + height] + if plan.scroll_lines: + self._scroll(plan.scroll_lines, self._getscrollbacksize()) + self.posxy = self.posxy[0], self.posxy[1] + plan.scroll_lines - self.__offset = offset + self.__offset = plan.offset self._hide_cursor() - for ( - y, - oldline, - newline, - ) in zip(range(offset, offset + height), oldscr, newscr): - if oldline != newline: - self.__write_changed_line(y, oldline, newline, px) - - y = len(newscr) - while y < len(oldscr): + for update in plan.line_updates: + self.__apply_line_update(update, visual_style) + + for y in plan.cleared_lines: self._move_relative(0, y) self.posxy = 0, y self._erase_to_end() - y += 1 self._show_cursor() - - self.screen = screen self.move_cursor(cx, cy) + self.sync_rendered_screen(plan.rendered_screen, self.posxy) @property def input_hook(self): @@ -246,54 +314,98 @@ def input_hook(self): if nt is not None and nt._is_inputhook_installed(): return nt._inputhook - def __write_changed_line( - self, y: int, oldline: str, newline: str, px_coord: int - ) -> None: - # this is frustrating; there's no reason to test (say) - # self.dch1 inside the loop -- but alternative ways of - # structuring this function are equally painful (I'm trying to - # avoid writing code generators these days...) - minlen = min(wlen(oldline), wlen(newline)) - x_pos = 0 - x_coord = 0 - - px_pos = 0 - j = 0 - for c in oldline: - if j >= px_coord: - break - j += wlen(c) - px_pos += 1 - - # reuse the oldline as much as possible, but stop as soon as we - # encounter an ESCAPE, because it might be the start of an escape - # sequence - while ( - x_coord < minlen - and oldline[x_pos] == newline[x_pos] - and newline[x_pos] != "\x1b" + def __plan_changed_line( # keep in sync with UnixConsole.__plan_changed_line + self, + y: int, + oldline: RenderLine, + newline: RenderLine, + px_coord: int, + ) -> LineUpdate | None: + diff = diff_render_lines(oldline, newline) + if diff is None: + return None + + start_cell = diff.start_cell + start_x = diff.start_x + if ( + len(diff.old_cells) == 1 + and len(diff.new_cells) == 1 + and diff.old_cells[0].width == diff.new_cells[0].width ): - x_coord += wlen(newline[x_pos]) - x_pos += 1 + changed_cell = diff.new_cells[0] + # Ctrl-Z (SUB) can reach here via RenderLine.from_rendered_text() + # for prompt/message lines, which bypasses iter_display_chars(). + # On Windows, raw \x1a causes console cursor anomalies, so we + # force a cursor resync when it appears. + return LineUpdate( + kind="replace_char", + y=y, + start_cell=start_cell, + start_x=start_x, + cells=diff.new_cells, + char_width=changed_cell.width, + reset_to_margin=( + requires_cursor_resync(diff.new_cells) + or "\x1a" in changed_cell.text + ), + ) + + if diff.old_changed_width == diff.new_changed_width: + return LineUpdate( + kind="replace_span", + y=y, + start_cell=start_cell, + start_x=start_x, + cells=diff.new_cells, + char_width=diff.new_changed_width, + reset_to_margin=( + requires_cursor_resync(diff.new_cells) + or any("\x1a" in cell.text for cell in diff.new_cells) + ), + ) + + suffix_cells = newline.cells[start_cell:] + return LineUpdate( + kind="rewrite_suffix", + y=y, + start_cell=start_cell, + start_x=start_x, + cells=suffix_cells, + char_width=sum(cell.width for cell in suffix_cells), + clear_eol=oldline.width > newline.width, + reset_to_margin=( + requires_cursor_resync(suffix_cells) + or any("\x1a" in cell.text for cell in suffix_cells) + ), + ) - self._hide_cursor() - self._move_relative(x_coord, y) - if wlen(oldline) > wlen(newline): + def __apply_line_update( + self, + update: LineUpdate, + visual_style: str | None = None, + ) -> None: + text = render_cells(update.cells, visual_style) if visual_style else update.text + trace( + "windows.refresh update kind={kind} y={y} x={x} text={text} " + "clear_eol={clear_eol} reset_to_margin={reset}", + kind=update.kind, + y=update.y, + x=update.start_x, + text=trace_text(text), + clear_eol=update.clear_eol, + reset=update.reset_to_margin, + ) + original_y = self.posxy[1] + self._move_relative(update.start_x, update.y) + if update.clear_eol: self._erase_to_end() - self.__write(newline[x_pos:]) - if wlen(newline) == self.width: - # If we wrapped we want to start at the next line - self._move_relative(0, y + 1) - self.posxy = 0, y + 1 - else: - self.posxy = wlen(newline), y + self.__write(text) + self.posxy = min(update.start_x + update.char_width, self.width - 1), update.y - if "\x1b" in newline or y != self.posxy[1] or '\x1a' in newline: - # ANSI escape characters are present, so we can't assume - # anything about the position of the cursor. Moving the cursor - # to the left margin should work to get to a known position. - self.move_cursor(0, y) + if update.reset_to_margin or update.y != original_y: + # Non-SGR terminal controls or vertical movement require a cursor sync. + self.move_cursor(0, update.y) def _scroll( self, top: int, bottom: int, left: int | None = None, right: int | None = None @@ -312,7 +424,7 @@ def _scroll( if not ScrollConsoleScreenBuffer( OutHandle, scroll_rect, None, destination_origin, fill_info ): - raise WinError(GetLastError()) + raise WinError(get_last_error()) def _hide_cursor(self): self.__write("\x1b[?25l") @@ -334,7 +446,7 @@ def _disable_bracketed_paste(self) -> None: def __write(self, text: str) -> None: if "\x1a" in text: - text = ''.join(["^Z" if x == '\x1a' else x for x in text]) + text = text.replace("\x1a", "^Z") if self.out is not None: self.out.write(text.encode(self.encoding, "replace")) @@ -346,30 +458,32 @@ def __write(self, text: str) -> None: def screen_xy(self) -> tuple[int, int]: info = CONSOLE_SCREEN_BUFFER_INFO() if not GetConsoleScreenBufferInfo(OutHandle, info): - raise WinError(GetLastError()) + raise WinError(get_last_error()) return info.dwCursorPosition.X, info.dwCursorPosition.Y def _erase_to_end(self) -> None: self.__write(ERASE_IN_LINE) def prepare(self) -> None: - trace("prepare") - self.screen = [] + trace("windows.prepare") self.height, self.width = self.getheightwidth() self.posxy = 0, 0 - self.__gone_tall = 0 self.__offset = 0 + self.sync_rendered_screen(RenderedScreen.empty(), self.posxy) if self.__vt_support: - SetConsoleMode(InHandle, self.__original_input_mode | ENABLE_VIRTUAL_TERMINAL_INPUT) + if not SetConsoleMode(InHandle, self.__original_input_mode | ENABLE_VIRTUAL_TERMINAL_INPUT): + raise WinError(get_last_error()) self._enable_bracketed_paste() def restore(self) -> None: + trace("windows.restore") if self.__vt_support: # Recover to original mode before running REPL self._disable_bracketed_paste() - SetConsoleMode(InHandle, self.__original_input_mode) + if not SetConsoleMode(InHandle, self.__original_input_mode): + raise WinError(get_last_error()) def _move_relative(self, x: int, y: int) -> None: """Moves relative to the current posxy""" @@ -390,8 +504,16 @@ def move_cursor(self, x: int, y: int) -> None: raise ValueError(f"Bad cursor position {x}, {y}") if y < self.__offset or y >= self.__offset + self.height: + trace( + "windows.move_cursor offscreen x={x} y={y} offset={offset} height={height}", + x=x, + y=y, + offset=self.__offset, + height=self.height, + ) self.event_queue.insert(Event("scroll", "")) else: + trace("windows.move_cursor x={x} y={y}", x=x, y=y) self._move_relative(x, y) self.posxy = x, y @@ -406,7 +528,7 @@ def getheightwidth(self) -> tuple[int, int]: and width of the terminal window in characters.""" info = CONSOLE_SCREEN_BUFFER_INFO() if not GetConsoleScreenBufferInfo(OutHandle, info): - raise WinError(GetLastError()) + raise WinError(get_last_error()) return ( info.srWindow.Bottom - info.srWindow.Top + 1, info.srWindow.Right - info.srWindow.Left + 1, @@ -415,7 +537,7 @@ def getheightwidth(self) -> tuple[int, int]: def _getscrollbacksize(self) -> int: info = CONSOLE_SCREEN_BUFFER_INFO() if not GetConsoleScreenBufferInfo(OutHandle, info): - raise WinError(GetLastError()) + raise WinError(get_last_error()) return info.srWindow.Bottom # type: ignore[no-any-return] @@ -423,7 +545,7 @@ def _read_input(self) -> INPUT_RECORD | None: rec = INPUT_RECORD() read = DWORD() if not ReadConsoleInput(InHandle, rec, 1, read): - raise WinError(GetLastError()) + raise WinError(get_last_error()) return rec @@ -433,7 +555,7 @@ def _read_input_bulk( rec = (n * INPUT_RECORD)() read = DWORD() if not ReadConsoleInput(InHandle, rec, n, read): - raise WinError(GetLastError()) + raise WinError(get_last_error()) return rec, read.value @@ -512,15 +634,17 @@ def beep(self) -> None: def clear(self) -> None: """Wipe the screen""" + trace("windows.clear") self.__write(CLEAR) self.posxy = 0, 0 - self.screen = [""] + self.sync_rendered_screen(RenderedScreen.empty(), self.posxy) def finish(self) -> None: """Move the cursor to the end of the display and otherwise get ready for end. XXX could be merged with restore? Hmm.""" - y = len(self.screen) - 1 - while y >= 0 and not self.screen[y]: + rendered_lines = self._rendered_screen.composed_lines + y = len(rendered_lines) - 1 + while y >= 0 and not rendered_lines[y].text: y -= 1 self._move_relative(0, min(y, self.height + self.__offset - 1)) self.__write("\r\n") @@ -535,7 +659,7 @@ def flushoutput(self) -> None: def forgetinput(self) -> None: """Forget all pending, but not yet processed input.""" if not FlushConsoleInputBuffer(InHandle): - raise WinError(GetLastError()) + raise WinError(get_last_error()) def getpending(self) -> Event: """Return the characters that have been typed but not yet @@ -562,11 +686,11 @@ def getpending(self) -> Event: # ignore SHIFT_PRESSED and special keys continue if ch == "\r": - ch += "\n" + ch = "\n" e.data += ch return e - def wait(self, timeout: float | None) -> bool: + def wait_for_event(self, timeout: float | None) -> bool: """Wait for an event.""" if timeout is None: timeout = INFINITE @@ -579,7 +703,17 @@ def wait(self, timeout: float | None) -> bool: return False return True + def wait(self, timeout: float | None) -> bool: + """ + Wait for events on the console. + """ + return ( + not self.event_queue.empty() + or self.wait_for_event(timeout) + ) + def repaint(self) -> None: + trace("windows.repaint unsupported") raise NotImplementedError("No repaint support") diff --git a/Lib/_sitebuiltins.py b/Lib/_sitebuiltins.py index c66269a571967f..5259a36123ea27 100644 --- a/Lib/_sitebuiltins.py +++ b/Lib/_sitebuiltins.py @@ -36,7 +36,7 @@ def __init__(self, name, data, files=(), dirs=()): import os self.__name = name self.__data = data - self.__lines = None + self.__lines = [] self.__filenames = [os.path.join(dir, filename) for dir in dirs for filename in files] @@ -55,7 +55,6 @@ def __setup(self): if not data: data = self.__data self.__lines = data.split('\n') - self.__linecnt = len(self.__lines) def __repr__(self): self.__setup() @@ -65,24 +64,22 @@ def __repr__(self): return "Type %s() to see the full %s text" % ((self.__name,)*2) def __call__(self): - self.__setup() - prompt = 'Hit Return for more, or q (and Return) to quit: ' - lineno = 0 - while 1: + try: + from _pyrepl.pager import get_pager + except ModuleNotFoundError: try: - for i in range(lineno, lineno + self.MAXLINES): - print(self.__lines[i]) - except IndexError: - break - else: - lineno += self.MAXLINES - key = None - while key is None: - key = input(prompt) - if key not in ('', 'q'): - key = None - if key == 'q': - break + from pydoc import get_pager + except ModuleNotFoundError: + def get_pager(): + def _print(text, title=None): + print(text) + return _print + + self.__setup() + + pager = get_pager() + text = "\n".join(self.__lines) + pager(text, title=self.__name) class _Helper(object): diff --git a/Lib/_strptime.py b/Lib/_strptime.py index cdc55e8daaffa6..746b0907c1d9f4 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -7,7 +7,7 @@ FUNCTIONS: _getlang -- Figure out what language is being used for the locale - strptime -- Calculates the time struct represented by the passed-in string + _strptime -- Calculates the time struct represented by the passed-in string """ import os @@ -371,7 +371,9 @@ def __init__(self, locale_time=None): # W is set below by using 'U' 'y': r"(?P\d\d)", 'Y': r"(?P\d\d\d\d)", + # See gh-121237: "z" must support colons for backwards compatibility. 'z': r"(?P([+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?)|(?-i:Z))?", + ':z': r"(?P([+-]\d\d:[0-5]\d(:[0-5]\d(\.\d{1,6})?)?)|(?-i:Z))?", 'A': self.__seqToRE(self.locale_time.f_weekday, 'A'), 'a': self.__seqToRE(self.locale_time.a_weekday, 'a'), 'B': self.__seqToRE(_fixmonths(self.locale_time.f_month[1:]), 'B'), @@ -380,7 +382,10 @@ def __init__(self, locale_time=None): 'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone for tz in tz_names), 'Z'), - '%': '%'} + 'n': r'\s*', + 't': r'\s*', + '%': '%', + } if self.locale_time.LC_alt_digits is None: for d in 'dmyCHIMS': mapping['O' + d] = r'(?P<%s>\d\d|\d| \d)' % d @@ -416,6 +421,8 @@ def __init__(self, locale_time=None): mapping['W'] = mapping['U'].replace('U', 'W') base.__init__(mapping) + base.__setitem__('D', self.pattern('%m/%d/%y')) + base.__setitem__('F', self.pattern('%Y-%m-%d')) base.__setitem__('T', self.pattern('%H:%M:%S')) base.__setitem__('R', self.pattern('%H:%M')) base.__setitem__('r', self.pattern(self.locale_time.LC_time_ampm)) @@ -457,28 +464,39 @@ def pattern(self, format): format = re_sub(r'\s+', r'\\s+', format) format = re_sub(r"'", "['\u02bc]", format) # needed for br_FR year_in_format = False - day_of_month_in_format = False + day_d_in_format = False + day_e_in_format = False def repl(m): - format_char = m[1] - match format_char: + directive = m.group()[1:] # exclude `%` symbol + match directive: case 'Y' | 'y' | 'G': nonlocal year_in_format year_in_format = True case 'd': - nonlocal day_of_month_in_format - day_of_month_in_format = True - return self[format_char] - format = re_sub(r'%[-_0^#]*[0-9]*([OE]?\\?.?)', repl, format) - if day_of_month_in_format and not year_in_format: - import warnings - warnings.warn("""\ + nonlocal day_d_in_format + day_d_in_format = True + case 'e': + nonlocal day_e_in_format + day_e_in_format = True + return self[directive] + format = re_sub(r'%[-_0^#]*[0-9]*([OE]?[:\\]?.?)', repl, format) + if not year_in_format: + if day_d_in_format: + raise ValueError( + "Day of month directive '%d' may not be used without " + "a year directive. Parsing dates involving a day of " + "month without a year is ambiguous and fails to parse " + "leap day. Add a year to the input and format. " + "See https://github.com/python/cpython/issues/70647.") + if day_e_in_format: + import warnings + warnings.warn("""\ Parsing dates involving a day of month without a year specified is ambiguous -and fails to parse leap day. The default behavior will change in Python 3.15 -to either always raise an exception or to use a different default year (TBD). -To avoid trouble, add a specific year to the input & format. +and fails to parse leap day. '%e' without a year will become an error in Python 3.17. +To avoid trouble, add a specific year to the input and format. See https://github.com/python/cpython/issues/70647.""", - DeprecationWarning, - skip_file_prefixes=(os.path.dirname(__file__),)) + DeprecationWarning, + skip_file_prefixes=(os.path.dirname(__file__),)) return format def compile(self, format): @@ -514,9 +532,10 @@ def _calc_julian_from_U_or_W(year, week_of_year, day_of_week, week_starts_Mon): def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): - """Return a 2-tuple consisting of a time struct and an int containing - the number of microseconds based on the input string and the - format string.""" + """Return a 3-tuple consisting of a tuple with time components, + an int containing the number of microseconds, and an int + containing the microseconds part of the GMT offset, based on the + input string and the format string.""" for index, arg in enumerate([data_string, format]): if not isinstance(arg, str): @@ -555,8 +574,17 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): raise ValueError("time data %r does not match format %r" % (data_string, format)) if len(data_string) != found.end(): - raise ValueError("unconverted data remains: %s" % - data_string[found.end():]) + rest = data_string[found.end():] + # Specific check for '%:z' directive + if ( + "colon_z" in found.re.groupindex + and found.group("colon_z") is not None + and rest[0] != ":" + ): + raise ValueError( + f"Missing colon in %:z before '{rest}', got '{data_string}'" + ) + raise ValueError("unconverted data remains: %s" % rest) iso_year = year = None month = day = 1 @@ -616,18 +644,18 @@ def parse_int(s): hour = parse_int(found_dict['I']) ampm = found_dict.get('p', '').lower() # If there was no AM/PM indicator, we'll treat this like AM - if ampm in ('', locale_time.am_pm[0]): - # We're in AM so the hour is correct unless we're - # looking at 12 midnight. - # 12 midnight == 12 AM == hour 0 - if hour == 12: - hour = 0 - elif ampm == locale_time.am_pm[1]: + if ampm == locale_time.am_pm[1]: # We're in PM so we need to add 12 to the hour unless # we're looking at 12 noon. # 12 noon == 12 PM == hour 12 if hour != 12: hour += 12 + else: + # We're in AM so the hour is correct unless we're + # looking at 12 midnight. + # 12 midnight == 12 AM == hour 0 + if hour == 12: + hour = 0 elif group_key == 'M': minute = parse_int(found_dict['M']) elif group_key == 'S': @@ -662,8 +690,8 @@ def parse_int(s): week_of_year_start = 0 elif group_key == 'V': iso_week = int(found_dict['V']) - elif group_key == 'z': - z = found_dict['z'] + elif group_key in ('z', 'colon_z'): + z = found_dict[group_key] if z: if z == 'Z': gmtoff = 0 @@ -672,7 +700,7 @@ def parse_int(s): z = z[:3] + z[4:] if len(z) > 5: if z[5] != ':': - msg = f"Inconsistent use of : in {found_dict['z']}" + msg = f"Inconsistent use of : in {found_dict[group_key]}" raise ValueError(msg) z = z[:5] + z[6:] hours = int(z[1:3]) diff --git a/Lib/_threading_local.py b/Lib/_threading_local.py index 0b9e5d3bbf6ef6..2af3885458b54f 100644 --- a/Lib/_threading_local.py +++ b/Lib/_threading_local.py @@ -57,7 +57,7 @@ def thread_deleted(_, idt=idt): # as soon as the OS-level thread ends instead. local = wrlocal() if local is not None: - dct = local.dicts.pop(idt) + local.dicts.pop(idt) wrlocal = ref(self, local_deleted) wrthread = ref(thread, thread_deleted) thread.__dict__[key] = wrlocal diff --git a/Lib/abc.py b/Lib/abc.py index f8a4e11ce9c3b1..08f708d1b89208 100644 --- a/Lib/abc.py +++ b/Lib/abc.py @@ -36,11 +36,15 @@ class C(ABC): def my_abstract_classmethod(cls, ...): ... + .. deprecated-removed: 3.3 3.21 + """ __isabstractmethod__ = True def __init__(self, callable): + import warnings + warnings._deprecated('abc.abstractclassmethod', remove=(3, 21)) callable.__isabstractmethod__ = True super().__init__(callable) @@ -56,11 +60,15 @@ class C(ABC): def my_abstract_staticmethod(...): ... + .. deprecated-removed: 3.3 3.21 + """ __isabstractmethod__ = True def __init__(self, callable): + import warnings + warnings._deprecated('abc.abstractstaticmethod', remove=(3, 21)) callable.__isabstractmethod__ = True super().__init__(callable) @@ -76,10 +84,23 @@ class C(ABC): def my_abstract_property(self): ... + .. deprecated-removed: 3.3 3.21 + """ __isabstractmethod__ = True + def __init__( + self, + fget=None, + fset=None, + fdel=None, + doc=None, + ): + import warnings + warnings._deprecated('abc.abstractproperty', remove=(3, 21)) + super().__init__(fget, fset, fdel, doc) + try: from _abc import (get_cache_token, _abc_init, _abc_register, diff --git a/Lib/annotationlib.py b/Lib/annotationlib.py index c83a1573ccd3d1..8204c762cce8a2 100644 --- a/Lib/annotationlib.py +++ b/Lib/annotationlib.py @@ -47,6 +47,7 @@ class Format(enum.IntEnum): "__cell__", "__owner__", "__stringifier_dict__", + "__resolved_str_cache__", ) @@ -85,12 +86,16 @@ def __init__( # These are always set to None here but may be non-None if a ForwardRef # is created through __class__ assignment on a _Stringifier object. self.__globals__ = None + # This may be either a cell object (for a ForwardRef referring to a single name) + # or a dict mapping cell names to cell objects (for a ForwardRef containing references + # to multiple names). self.__cell__ = None self.__extra_names__ = None # These are initially None but serve as a cache and may be set to a non-None # value later. self.__code__ = None self.__ast_node__ = None + self.__resolved_str_cache__ = None def __init_subclass__(cls, /, *args, **kwds): raise TypeError("Cannot subclass ForwardRef") @@ -110,14 +115,14 @@ def evaluate( """ match format: case Format.STRING: - return self.__forward_arg__ + return self.__resolved_str__ case Format.VALUE: is_forwardref_format = False case Format.FORWARDREF: is_forwardref_format = True case _: raise NotImplementedError(format) - if self.__cell__ is not None: + if isinstance(self.__cell__, types.CellType): try: return self.__cell__.cell_contents except ValueError: @@ -147,34 +152,42 @@ def evaluate( if globals is None: globals = {} + if type_params is None and owner is not None: + type_params = getattr(owner, "__type_params__", None) + if locals is None: locals = {} if isinstance(owner, type): locals.update(vars(owner)) + elif ( + type_params is not None + or isinstance(self.__cell__, dict) + or self.__extra_names__ + ): + # Create a new locals dict if necessary, + # to avoid mutating the argument. + locals = dict(locals) - if type_params is None and owner is not None: - # "Inject" type parameters into the local namespace - # (unless they are shadowed by assignments *in* the local namespace), - # as a way of emulating annotation scopes when calling `eval()` - type_params = getattr(owner, "__type_params__", None) - - # type parameters require some special handling, - # as they exist in their own scope - # but `eval()` does not have a dedicated parameter for that scope. - # For classes, names in type parameter scopes should override - # names in the global scope (which here are called `localns`!), - # but should in turn be overridden by names in the class scope - # (which here are called `globalns`!) + # "Inject" type parameters into the local namespace + # (unless they are shadowed by assignments *in* the local namespace), + # as a way of emulating annotation scopes when calling `eval()` if type_params is not None: - globals = dict(globals) - locals = dict(locals) for param in type_params: - param_name = param.__name__ - if not self.__forward_is_class__ or param_name not in globals: - globals[param_name] = param - locals.pop(param_name, None) + locals.setdefault(param.__name__, param) + + # Similar logic can be used for nonlocals, which should not + # override locals. + if isinstance(self.__cell__, dict): + for cell_name, cell in self.__cell__.items(): + try: + cell_value = cell.cell_contents + except ValueError: + pass + else: + locals.setdefault(cell_name, cell_value) + if self.__extra_names__: - locals = {**locals, **self.__extra_names__} + locals.update(self.__extra_names__) arg = self.__forward_arg__ if arg.isidentifier() and not keyword.iskeyword(arg): @@ -195,8 +208,11 @@ def evaluate( except Exception: if not is_forwardref_format: raise + + # All variables, in scoping order, should be checked before + # triggering __missing__ to create a _Stringifier. new_locals = _StringifierDict( - {**builtins.__dict__, **locals}, + {**builtins.__dict__, **globals, **locals}, globals=globals, owner=owner, is_class=self.__forward_is_class__, @@ -207,32 +223,9 @@ def evaluate( except Exception: return self else: - new_locals.transmogrify() + new_locals.transmogrify(self.__cell__) return result - def _evaluate(self, globalns, localns, type_params=_sentinel, *, recursive_guard): - import typing - import warnings - - if type_params is _sentinel: - typing._deprecation_warning_for_no_type_params_passed( - "typing.ForwardRef._evaluate" - ) - type_params = () - warnings._deprecated( - "ForwardRef._evaluate", - "{name} is a private API and is retained for compatibility, but will be removed" - " in Python 3.16. Use ForwardRef.evaluate() or typing.evaluate_forward_ref() instead.", - remove=(3, 16), - ) - return typing.evaluate_forward_ref( - self, - globals=globalns, - locals=localns, - type_params=type_params, - _recursive_guard=recursive_guard, - ) - @property def __forward_arg__(self): if self.__arg__ is not None: @@ -244,20 +237,31 @@ def __forward_arg__(self): "Attempted to access '__forward_arg__' on an uninitialized ForwardRef" ) + @property + def __resolved_str__(self): + # __forward_arg__ with any names from __extra_names__ replaced + # with the type_repr of the value they represent + if self.__resolved_str_cache__ is None: + resolved_str = self.__forward_arg__ + names = self.__extra_names__ + + if names: + visitor = _ExtraNameFixer(names) + ast_expr = ast.parse(resolved_str, mode="eval").body + node = visitor.visit(ast_expr) + resolved_str = ast.unparse(node) + + self.__resolved_str_cache__ = resolved_str + + return self.__resolved_str_cache__ + @property def __forward_code__(self): if self.__code__ is not None: return self.__code__ arg = self.__forward_arg__ - # If we do `def f(*args: *Ts)`, then we'll have `arg = '*Ts'`. - # Unfortunately, this isn't a valid expression on its own, so we - # do the unpacking manually. - if arg.startswith("*"): - arg_to_compile = f"({arg},)[0]" # E.g. (*Ts,)[0] or (*tuple[int, int],)[0] - else: - arg_to_compile = arg try: - self.__code__ = compile(arg_to_compile, "", "eval") + self.__code__ = compile(_rewrite_star_unpack(arg), "", "eval") except SyntaxError: raise SyntaxError(f"Forward reference must be an expression -- got {arg!r}") return self.__code__ @@ -272,7 +276,13 @@ def __eq__(self, other): # because dictionaries are not hashable. and self.__globals__ is other.__globals__ and self.__forward_is_class__ == other.__forward_is_class__ - and self.__cell__ == other.__cell__ + # Two separate cells are always considered unequal in forward refs. + and ( + {name: id(cell) for name, cell in self.__cell__.items()} + == {name: id(cell) for name, cell in other.__cell__.items()} + if isinstance(self.__cell__, dict) and isinstance(other.__cell__, dict) + else self.__cell__ is other.__cell__ + ) and self.__owner__ == other.__owner__ and ( (tuple(sorted(self.__extra_names__.items())) if self.__extra_names__ else None) == @@ -286,7 +296,10 @@ def __hash__(self): self.__forward_module__, id(self.__globals__), # dictionaries are not hashable, so hash by identity self.__forward_is_class__, - self.__cell__, + ( # cells are not hashable as well + tuple(sorted([(name, id(cell)) for name, cell in self.__cell__.items()])) + if isinstance(self.__cell__, dict) else id(self.__cell__), + ), self.__owner__, tuple(sorted(self.__extra_names__.items())) if self.__extra_names__ else None, )) @@ -305,7 +318,7 @@ def __repr__(self): extra.append(", is_class=True") if self.__owner__ is not None: extra.append(f", owner={self.__owner__!r}") - return f"ForwardRef({self.__forward_arg__!r}{''.join(extra)})" + return f"ForwardRef({self.__resolved_str__!r}{''.join(extra)})" _Template = type(t"") @@ -341,6 +354,7 @@ def __init__( self.__cell__ = cell self.__owner__ = owner self.__stringifier_dict__ = stringifier_dict + self.__resolved_str_cache__ = None # Needed for ForwardRef def __convert_to_ast(self, other): if isinstance(other, _Stringifier): @@ -568,32 +582,70 @@ def unary_op(self): del _make_unary_op -def _template_to_ast(template): +def _template_to_ast_constructor(template): + """Convert a `template` instance to a non-literal AST.""" + args = [] + for part in template: + match part: + case str(): + args.append(ast.Constant(value=part)) + case _: + interp = ast.Call( + func=ast.Name(id="Interpolation"), + args=[ + ast.Constant(value=part.value), + ast.Constant(value=part.expression), + ast.Constant(value=part.conversion), + ast.Constant(value=part.format_spec), + ] + ) + args.append(interp) + return ast.Call(func=ast.Name(id="Template"), args=args, keywords=[]) + + +def _template_to_ast_literal(template, parsed): + """Convert a `template` instance to a t-string literal AST.""" values = [] + interp_count = 0 for part in template: match part: case str(): values.append(ast.Constant(value=part)) - # Interpolation, but we don't want to import the string module case _: interp = ast.Interpolation( str=part.expression, - value=ast.parse(part.expression), - conversion=( - ord(part.conversion) - if part.conversion is not None - else -1 - ), - format_spec=( - ast.Constant(value=part.format_spec) - if part.format_spec != "" - else None - ), + value=parsed[interp_count], + conversion=ord(part.conversion) if part.conversion else -1, + format_spec=ast.Constant(value=part.format_spec) + if part.format_spec + else None, ) values.append(interp) + interp_count += 1 return ast.TemplateStr(values=values) +def _template_to_ast(template): + """Make a best-effort conversion of a `template` instance to an AST.""" + # gh-138558: Not all Template instances can be represented as t-string + # literals. Return the most accurate AST we can. See issue for details. + + # If any expr is empty or whitespace only, we cannot convert to a literal. + if any(part.expression.strip() == "" for part in template.interpolations): + return _template_to_ast_constructor(template) + + try: + # Wrap in parens to allow whitespace inside interpolation curly braces + parsed = tuple( + ast.parse(f"({part.expression})", mode="eval").body + for part in template.interpolations + ) + except SyntaxError: + return _template_to_ast_constructor(template) + + return _template_to_ast_literal(template, parsed) + + class _StringifierDict(dict): def __init__(self, namespace, *, globals=None, owner=None, is_class=False, format): super().__init__(namespace) @@ -616,13 +668,15 @@ def __missing__(self, key): self.stringifiers.append(fwdref) return fwdref - def transmogrify(self): + def transmogrify(self, cell_dict): for obj in self.stringifiers: obj.__class__ = ForwardRef obj.__stringifier_dict__ = None # not needed for ForwardRef if isinstance(obj.__ast_node__, str): obj.__arg__ = obj.__ast_node__ obj.__ast_node__ = None + if cell_dict is not None and obj.__cell__ is None: + obj.__cell__ = cell_dict def create_unique_name(self): name = f"__annotationlib_name_{self.next_id}__" @@ -672,9 +726,21 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): # possibly constants if the annotate function uses them directly). We then # convert each of those into a string to get an approximation of the # original source. + + # Attempt to call with VALUE_WITH_FAKE_GLOBALS to check if it is implemented + # See: https://github.com/python/cpython/issues/138764 + # Only fail on NotImplementedError + try: + annotate(Format.VALUE_WITH_FAKE_GLOBALS) + except NotImplementedError: + # Both STRING and VALUE_WITH_FAKE_GLOBALS are not implemented: fallback to VALUE + return annotations_to_string(annotate(Format.VALUE)) + except Exception: + pass + globals = _StringifierDict({}, format=format) is_class = isinstance(owner, type) - closure = _build_closure( + closure, _ = _build_closure( annotate, owner, is_class, globals, allow_evaluation=False ) func = types.FunctionType( @@ -718,7 +784,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): is_class=is_class, format=format, ) - closure = _build_closure( + closure, cell_dict = _build_closure( annotate, owner, is_class, globals, allow_evaluation=True ) func = types.FunctionType( @@ -730,10 +796,13 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): ) try: result = func(Format.VALUE_WITH_FAKE_GLOBALS) + except NotImplementedError: + # FORWARDREF and VALUE_WITH_FAKE_GLOBALS not supported, fall back to VALUE + return annotate(Format.VALUE) except Exception: pass else: - globals.transmogrify() + globals.transmogrify(cell_dict) return result # Try again, but do not provide any globals. This allows us to return @@ -745,7 +814,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): is_class=is_class, format=format, ) - closure = _build_closure( + closure, cell_dict = _build_closure( annotate, owner, is_class, globals, allow_evaluation=False ) func = types.FunctionType( @@ -756,7 +825,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): kwdefaults=annotate.__kwdefaults__, ) result = func(Format.VALUE_WITH_FAKE_GLOBALS) - globals.transmogrify() + globals.transmogrify(cell_dict) if _is_evaluate: if isinstance(result, ForwardRef): return result.evaluate(format=Format.FORWARDREF) @@ -781,14 +850,11 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): def _build_closure(annotate, owner, is_class, stringifier_dict, *, allow_evaluation): if not annotate.__closure__: - return None - freevars = annotate.__code__.co_freevars + return None, None new_closure = [] - for i, cell in enumerate(annotate.__closure__): - if i < len(freevars): - name = freevars[i] - else: - name = "__cell__" + cell_dict = {} + for name, cell in zip(annotate.__code__.co_freevars, annotate.__closure__, strict=True): + cell_dict[name] = cell new_cell = None if allow_evaluation: try: @@ -809,7 +875,7 @@ def _build_closure(annotate, owner, is_class, stringifier_dict, *, allow_evaluat stringifier_dict.stringifiers.append(fwdref) new_cell = types.CellType(fwdref) new_closure.append(new_cell) - return tuple(new_closure) + return tuple(new_closure), cell_dict def _stringify_single(anno): @@ -851,7 +917,7 @@ def get_annotations( does not exist, the __annotate__ function is called. The FORWARDREF format uses __annotations__ if it exists and can be evaluated, and otherwise falls back to calling the __annotate__ function. - The SOURCE format tries __annotate__ first, and falls back to + The STRING format tries __annotate__ first, and falls back to using __annotations__, stringified using annotations_to_string(). This function handles several details for you: @@ -969,13 +1035,26 @@ def get_annotations( obj_globals = obj_locals = unwrap = None if unwrap is not None: + # Use an id-based visited set to detect cycles in the __wrapped__ + # and functools.partial.func chain (e.g. f.__wrapped__ = f). + # On cycle detection we stop and use whatever __globals__ we have + # found so far, mirroring the approach of inspect.unwrap(). + _seen_ids = {id(unwrap)} while True: if hasattr(unwrap, "__wrapped__"): - unwrap = unwrap.__wrapped__ + candidate = unwrap.__wrapped__ + if id(candidate) in _seen_ids: + break + _seen_ids.add(id(candidate)) + unwrap = candidate continue if functools := sys.modules.get("functools"): if isinstance(unwrap, functools.partial): - unwrap = unwrap.func + candidate = unwrap.func + if id(candidate) in _seen_ids: + break + _seen_ids.add(id(candidate)) + unwrap = candidate continue break if hasattr(unwrap, "__globals__"): @@ -995,7 +1074,8 @@ def get_annotations( locals = {param.__name__: param for param in type_params} | locals return_value = { - key: value if not isinstance(value, str) else eval(value, globals, locals) + key: value if not isinstance(value, str) + else eval(_rewrite_star_unpack(value), globals, locals) for key, value in ann.items() } return return_value @@ -1032,6 +1112,16 @@ def annotations_to_string(annotations): } +def _rewrite_star_unpack(arg): + """If the given argument annotation expression is a star unpack e.g. `'*Ts'` + rewrite it to a valid expression. + """ + if arg.lstrip().startswith("*"): + return f"({arg},)[0]" # E.g. (*Ts,)[0] or (*tuple[int, int],)[0] + else: + return arg + + def _get_and_call_annotate(obj, format): """Get the __annotate__ function and call it. @@ -1071,3 +1161,14 @@ def _get_dunder_annotations(obj): if not isinstance(ann, dict): raise ValueError(f"{obj!r}.__annotations__ is neither a dict nor None") return ann + + +class _ExtraNameFixer(ast.NodeTransformer): + """Fixer for __extra_names__ items in ForwardRef __repr__ and string evaluation""" + def __init__(self, extra_names): + self.extra_names = extra_names + + def visit_Name(self, node: ast.Name): + if (new_name := self.extra_names.get(node.id, _sentinel)) is not _sentinel: + node = ast.Name(id=type_repr(new_name)) + return node diff --git a/Lib/argparse.py b/Lib/argparse.py index 2144c81886ad19..29e6ebb9634261 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -64,7 +64,6 @@ still considered an implementation detail.) """ -__version__ = '1.1' __all__ = [ 'ArgumentParser', 'ArgumentError', @@ -90,8 +89,10 @@ import os as _os import re as _re import sys as _sys +from gettext import gettext as _ +from gettext import ngettext -from gettext import gettext as _, ngettext +lazy import _colorize SUPPRESS = '==SUPPRESS==' @@ -149,10 +150,25 @@ def _copy_items(items): return copy.copy(items) +def _identity(value): + return value + + # =============== # Formatting Help # =============== +class _ColorlessTheme: + # A 'fake' theme for no colors + def __getattr__(self, name): + # _colorize's no_color themes are just all empty strings + # by directly using empty strings the import is avoided + if name.startswith("_"): + raise AttributeError(name) + return "" + +_colorless_theme = _ColorlessTheme() + class HelpFormatter(object): """Formatter for generating usage messages and argument help strings. @@ -167,7 +183,6 @@ def __init__( indent_increment=2, max_help_position=24, width=None, - color=True, ): # default setting for width if width is None: @@ -175,7 +190,6 @@ def __init__( width = shutil.get_terminal_size().columns width -= 2 - self._set_color(color) self._prog = prog self._indent_increment = indent_increment self._max_help_position = min(max_help_position, @@ -192,15 +206,35 @@ def __init__( self._whitespace_matcher = _re.compile(r'\s+', _re.ASCII) self._long_break_matcher = _re.compile(r'\n\n\n+') - def _set_color(self, color): - from _colorize import can_colorize, decolor, get_theme + self._set_color(False) + + def _set_color(self, color, *, file=None): + # Set a new color setting and file, clear caches for theme and decolor + self._theme_color = color + self._theme_file = file + self._cached_theme = None + self._cached_decolor = None - if color and can_colorize(): - self._theme = get_theme(force_color=True).argparse - self._decolor = decolor + def _get_theme_and_decolor(self): + # If self._theme_color is false, this prevents _colorize from importing + if self._theme_color and _colorize.can_colorize(file=self._theme_file): + self._cached_theme = _colorize.get_theme(force_color=True).argparse + self._cached_decolor = _colorize.decolor else: - self._theme = get_theme(force_no_color=True).argparse - self._decolor = lambda text: text + self._cached_theme = _colorless_theme + self._cached_decolor = _identity + + @property + def _theme(self): + if self._cached_theme is None: + self._get_theme_and_decolor() + return self._cached_theme + + @property + def _decolor(self): + if self._cached_decolor is None: + self._get_theme_and_decolor() + return self._cached_decolor # =============================== # Section and indentation methods @@ -281,7 +315,7 @@ def add_argument(self, action): if action.help is not SUPPRESS: # find all invocations - get_invocation = self._format_action_invocation + get_invocation = lambda x: self._decolor(self._format_action_invocation(x)) invocation_lengths = [len(get_invocation(action)) + self._current_indent] for subaction in self._iter_indented_subactions(action): invocation_lengths.append(len(get_invocation(subaction)) + self._current_indent) @@ -337,27 +371,17 @@ def _format_usage(self, usage, actions, groups, prefix): elif usage is None: prog = '%(prog)s' % dict(prog=self._prog) - # split optionals from positionals - optionals = [] - positionals = [] - for action in actions: - if action.option_strings: - optionals.append(action) - else: - positionals.append(action) - + parts, pos_start = self._get_actions_usage_parts(actions, groups) # build full usage string - format = self._format_actions_usage - action_usage = format(optionals + positionals, groups) - usage = ' '.join([s for s in [prog, action_usage] if s]) + usage = ' '.join(filter(None, [prog, *parts])) # wrap the usage parts if it's too long text_width = self._width - self._current_indent if len(prefix) + len(self._decolor(usage)) > text_width: # break usage into wrappable parts - opt_parts = self._get_actions_usage_parts(optionals, groups) - pos_parts = self._get_actions_usage_parts(positionals, groups) + opt_parts = parts[:pos_start] + pos_parts = parts[pos_start:] # helper for wrapping lines def get_lines(parts, indent, prefix=None): @@ -414,117 +438,141 @@ def get_lines(parts, indent, prefix=None): # prefix with 'usage:' return f'{t.usage}{prefix}{t.reset}{usage}\n\n' - def _format_actions_usage(self, actions, groups): - return ' '.join(self._get_actions_usage_parts(actions, groups)) - def _is_long_option(self, string): return len(string) > 2 def _get_actions_usage_parts(self, actions, groups): - # find group indices and identify actions in groups - group_actions = set() - inserts = {} - for group in groups: - if not group._group_actions: - raise ValueError(f'empty group {group}') + """Get usage parts with split index for optionals/positionals. - if all(action.help is SUPPRESS for action in group._group_actions): - continue - - try: - start = min(actions.index(item) for item in group._group_actions) - except ValueError: - continue - else: - end = start + len(group._group_actions) - if set(actions[start:end]) == set(group._group_actions): - group_actions.update(group._group_actions) - inserts[start, end] = group + Returns (parts, pos_start) where pos_start is the index in parts + where positionals begin. + This preserves mutually exclusive group formatting across the + optionals/positionals boundary (gh-75949). + """ + actions = [action for action in actions if action.help is not SUPPRESS] + # group actions by mutually exclusive groups + action_groups = dict.fromkeys(actions) + for group in groups: + for action in group._group_actions: + if action in action_groups: + action_groups[action] = group + # positional arguments keep their position + positionals = [] + for action in actions: + if not action.option_strings: + group = action_groups.pop(action) + if group: + group_actions = [ + action2 for action2 in group._group_actions + if action2.option_strings and + action_groups.pop(action2, None) + ] + [action] + positionals.append((group.required, group_actions)) + else: + positionals.append((None, [action])) + # the remaining optional arguments are sorted by the position of + # the first option in the group + optionals = [] + for action in actions: + if action.option_strings and action in action_groups: + group = action_groups.pop(action) + if group: + group_actions = [action] + [ + action2 for action2 in group._group_actions + if action2.option_strings and + action_groups.pop(action2, None) + ] + optionals.append((group.required, group_actions)) + else: + optionals.append((None, [action])) # collect all actions format strings parts = [] t = self._theme - for action in actions: - - # suppressed arguments are marked with None - if action.help is SUPPRESS: - part = None - - # produce all arg strings - elif not action.option_strings: - default = self._get_default_metavar_for_positional(action) - part = ( - t.summary_action - + self._format_args(action, default) - + t.reset - ) - - # if it's in a group, strip the outer [] - if action in group_actions: - if part[0] == '[' and part[-1] == ']': - part = part[1:-1] - - # produce the first way to invoke the option in brackets - else: - option_string = action.option_strings[0] - if self._is_long_option(option_string): - option_color = t.summary_long_option + pos_start = None + for i, (required, group) in enumerate(optionals + positionals): + start = len(parts) + if i == len(optionals): + pos_start = start + in_group = len(group) > 1 + for action in group: + # produce all arg strings + if not action.option_strings: + default = self._get_default_metavar_for_positional(action) + part = self._format_args(action, default) + # if it's in a group, strip the outer [] + if in_group: + if part[0] == '[' and part[-1] == ']': + part = part[1:-1] + part = t.summary_action + part + t.reset + + # produce the first way to invoke the option in brackets else: - option_color = t.summary_short_option + option_string = action.option_strings[0] + if self._is_long_option(option_string): + option_color = t.summary_long_option + else: + option_color = t.summary_short_option - # if the Optional doesn't take a value, format is: - # -s or --long - if action.nargs == 0: - part = action.format_usage() - part = f"{option_color}{part}{t.reset}" + # if the Optional doesn't take a value, format is: + # -s or --long + if action.nargs == 0: + part = action.format_usage() + part = f"{option_color}{part}{t.reset}" - # if the Optional takes a value, format is: - # -s ARGS or --long ARGS - else: - default = self._get_default_metavar_for_optional(action) - args_string = self._format_args(action, default) - part = ( - f"{option_color}{option_string} " - f"{t.summary_label}{args_string}{t.reset}" - ) - - # make it look optional if it's not required or in a group - if not action.required and action not in group_actions: - part = '[%s]' % part - - # add the action string to the list - parts.append(part) - - # group mutually exclusive actions - inserted_separators_indices = set() - for start, end in sorted(inserts, reverse=True): - group = inserts[start, end] - group_parts = [item for item in parts[start:end] if item is not None] - group_size = len(group_parts) - if group.required: - open, close = "()" if group_size > 1 else ("", "") - else: - open, close = "[]" - group_parts[0] = open + group_parts[0] - group_parts[-1] = group_parts[-1] + close - for i, part in enumerate(group_parts[:-1], start=start): - # insert a separator if not already done in a nested group - if i not in inserted_separators_indices: - parts[i] = part + ' |' - inserted_separators_indices.add(i) - parts[start + group_size - 1] = group_parts[-1] - for i in range(start + group_size, end): - parts[i] = None - - # return the usage parts - return [item for item in parts if item is not None] + # if the Optional takes a value, format is: + # -s ARGS or --long ARGS + else: + default = self._get_default_metavar_for_optional(action) + args_string = self._format_args(action, default) + part = ( + f"{option_color}{option_string} " + f"{t.summary_label}{args_string}{t.reset}" + ) + + # make it look optional if it's not required or in a group + if not (action.required or required or in_group): + part = '[%s]' % part + + # add the action string to the list + parts.append(part) + + if in_group: + parts[start] = ('(' if required else '[') + parts[start] + for i in range(start, len(parts) - 1): + parts[i] += ' |' + parts[-1] += ')' if required else ']' + + if pos_start is None: + pos_start = len(parts) + return parts, pos_start def _format_text(self, text): if '%(prog)' in text: text = text % dict(prog=self._prog) text_width = max(self._width - self._current_indent, 11) indent = ' ' * self._current_indent - return self._fill_text(text, text_width, indent) + '\n\n' + text = self._fill_text(text, text_width, indent) + text = self._apply_text_markup(text) + return text + '\n\n' + + def _apply_text_markup(self, text): + """Apply color markup to text. + + Supported markup: + `...` or ``...`` - inline code (rendered with prog_extra color) + + When colors are disabled, backticks are preserved as-is. + """ + t = self._theme + if not t.reset: + return text + text = _re.sub( + r'(`{1,2})([^`]+)\1', + rf'{t.prog_extra}\2{t.reset}', + text, + ) + return text def _format_action(self, action): # determine the required width and the entry label @@ -665,7 +713,7 @@ def _format_args(self, action, default_metavar): def _expand_help(self, action): help_string = self._get_help_string(action) if '%' not in help_string: - return help_string + return self._apply_text_markup(help_string) params = dict(vars(action), prog=self._prog) for name in list(params): value = params[name] @@ -675,7 +723,43 @@ def _expand_help(self, action): params[name] = value.__name__ if params.get('choices') is not None: params['choices'] = ', '.join(map(str, params['choices'])) - return help_string % params + + t = self._theme + + result = help_string % params + + if not t.reset: + return result + + # Match format specifiers like: %s, %d, %(key)s, etc. + fmt_spec = r''' + % + (?: + % # %% escape + | + (?:\((?P[^)]*)\))? # key + [-#0\ +]* # flags + (?:\*|\d+)? # width + (?:\.(?:\*|\d+))? # precision + [hlL]? # length modifier + [diouxXeEfFgGcrsa] # conversion type + ) + ''' + + def colorize(match): + spec, key = match.group(0, 'key') + if spec == '%%': + return '%' + if key is not None: + # %(key)... - format and colorize + formatted = spec % {key: params[key]} + return f'{t.interpolated_value}{formatted}{t.reset}' + # bare %s etc. - format with full params dict, no colorization + return spec % params + + return self._apply_text_markup( + _re.sub(fmt_spec, colorize, help_string, flags=_re.VERBOSE) + ) def _iter_indented_subactions(self, action): try: @@ -745,11 +829,21 @@ def _get_help_string(self, action): if help is None: help = '' - if '%(default)' not in help: - if action.default is not SUPPRESS: - defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] - if action.option_strings or action.nargs in defaulting_nargs: - help += _(' (default: %(default)s)') + if ( + '%(default)' not in help + and action.default is not SUPPRESS + and not action.required + ): + defaulting_nargs = (OPTIONAL, ZERO_OR_MORE) + if action.option_strings or action.nargs in defaulting_nargs: + t = self._theme + default_str = _(" (default: %(default)s)") + prefix, suffix = default_str.split("%(default)s") + help += ( + f" {t.default}{prefix.lstrip()}{t.reset}" + f"%(default)s" + f"{t.default}{suffix}{t.reset}" + ) return help @@ -933,15 +1027,26 @@ def __init__(self, deprecated=False): _option_strings = [] + neg_option_strings = [] for option_string in option_strings: _option_strings.append(option_string) - if option_string.startswith('--'): - if option_string.startswith('--no-'): + if len(option_string) > 2 and option_string[0] == option_string[1]: + # two-dash long option: '--foo' -> '--no-foo' + if option_string.startswith('no-', 2): + raise ValueError(f'invalid option name {option_string!r} ' + f'for BooleanOptionalAction') + option_string = option_string[:2] + 'no-' + option_string[2:] + _option_strings.append(option_string) + neg_option_strings.append(option_string) + elif len(option_string) > 2 and option_string[0] != option_string[1]: + # single-dash long option: '-foo' -> '-nofoo' + if option_string.startswith('no', 1): raise ValueError(f'invalid option name {option_string!r} ' f'for BooleanOptionalAction') - option_string = '--no-' + option_string[2:] + option_string = option_string[:1] + 'no' + option_string[1:] _option_strings.append(option_string) + neg_option_strings.append(option_string) super().__init__( option_strings=_option_strings, @@ -951,11 +1056,12 @@ def __init__(self, required=required, help=help, deprecated=deprecated) + self.neg_option_strings = neg_option_strings def __call__(self, parser, namespace, values, option_string=None): if option_string in self.option_strings: - setattr(namespace, self.dest, not option_string.startswith('--no-')) + setattr(namespace, self.dest, option_string not in self.neg_option_strings) def format_usage(self): return ' | '.join(self.option_strings) @@ -1552,8 +1658,8 @@ def add_argument(self, *args, **kwargs): f'instance of it must be passed') # raise an error if the metavar does not match the type - if hasattr(self, "_get_formatter"): - formatter = self._get_formatter() + if hasattr(self, "_get_validation_formatter"): + formatter = self._get_validation_formatter() try: formatter._format_args(action, None) except TypeError: @@ -1661,29 +1767,35 @@ def _get_positional_kwargs(self, dest, **kwargs): def _get_optional_kwargs(self, *args, **kwargs): # determine short and long option strings option_strings = [] - long_option_strings = [] for option_string in args: # error on strings that don't start with an appropriate prefix - if not option_string[0] in self.prefix_chars: + if option_string[0] not in self.prefix_chars: raise ValueError( f'invalid option string {option_string!r}: ' f'must start with a character {self.prefix_chars!r}') - - # strings starting with two prefix characters are long options option_strings.append(option_string) - if len(option_string) > 1 and option_string[1] in self.prefix_chars: - long_option_strings.append(option_string) # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' dest = kwargs.pop('dest', None) if dest is None: - if long_option_strings: - dest_option_string = long_option_strings[0] - else: - dest_option_string = option_strings[0] - dest = dest_option_string.lstrip(self.prefix_chars) + priority = 0 + for option_string in option_strings: + if len(option_string) <= 2: + # short option: '-x' -> 'x' + if priority < 1: + dest = option_string.lstrip(self.prefix_chars) + priority = 1 + elif option_string[1] not in self.prefix_chars: + # single-dash long option: '-foo' -> 'foo' + if priority < 2: + dest = option_string.lstrip(self.prefix_chars) + priority = 2 + else: + # two-dash long option: '--foo' -> 'foo' + dest = option_string.lstrip(self.prefix_chars) + break if not dest: - msg = f'dest= is required for options like {option_string!r}' + msg = f'dest= is required for options like {repr(option_strings)[1:-1]}' raise TypeError(msg) dest = dest.replace('-', '_') @@ -1741,8 +1853,8 @@ def _handle_conflict_resolve(self, action, conflicting_actions): action.container._remove_action(action) def _check_help(self, action): - if action.help and hasattr(self, "_get_formatter"): - formatter = self._get_formatter() + if action.help and hasattr(self, "_get_validation_formatter"): + formatter = self._get_validation_formatter() try: formatter._expand_help(action) except (ValueError, TypeError, KeyError) as exc: @@ -1858,7 +1970,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): - exit_on_error -- Determines whether or not ArgumentParser exits with error info when an error occurs - suggest_on_error - Enables suggestions for mistyped argument choices - and subparser names (default: ``False``) + and subparser names (default: ``True``) - color - Allow color output in help messages (default: ``False``) """ @@ -1877,7 +1989,7 @@ def __init__(self, allow_abbrev=True, exit_on_error=True, *, - suggest_on_error=False, + suggest_on_error=True, color=True, ): superinit = super(ArgumentParser, self).__init__ @@ -1897,15 +2009,16 @@ def __init__(self, self.suggest_on_error = suggest_on_error self.color = color + # Cached formatter for validation (avoids repeated _set_color calls) + self._cached_formatter = None + add_group = self.add_argument_group self._positionals = add_group(_('positional arguments')) self._optionals = add_group(_('options')) self._subparsers = None # register types - def identity(string): - return string - self.register('type', None, identity) + self.register('type', None, _identity) # add help argument if necessary # (using explicit default to override global argument_default) @@ -1958,12 +2071,16 @@ def add_subparsers(self, **kwargs): self._subparsers = self._positionals # prog defaults to the usage message of this parser, skipping - # optional arguments and with no "usage:" prefix + # non-required optional arguments and with no "usage:" prefix if kwargs.get('prog') is None: - formatter = self._get_formatter() + # Create formatter without color to avoid storing ANSI codes in prog + formatter = self.formatter_class(prog=self.prog) + formatter._set_color(False) positionals = self._get_positional_actions() + required_optionals = [action for action in self._get_optional_actions() + if action.required] groups = self._mutually_exclusive_groups - formatter.add_usage(None, positionals, groups, '') + formatter.add_usage(None, required_optionals + positionals, groups, '') kwargs['prog'] = formatter.format_help().strip() # create the parsers action and add it to the positionals list @@ -2430,7 +2547,7 @@ def _parse_optional(self, arg_string): return None # if it doesn't start with a prefix, it was meant to be positional - if not arg_string[0] in self.prefix_chars: + if arg_string[0] not in self.prefix_chars: return None # if the option string is present in the parser, return the action @@ -2539,7 +2656,7 @@ def _get_nargs_pattern(self, action): # allow any number of options or arguments elif nargs == REMAINDER: - nargs_pattern = '([AO]*)' if option else '(.*)' + nargs_pattern = '(.*)' # allow one argument followed by any number of options or arguments elif nargs == PARSER: @@ -2674,7 +2791,7 @@ def _check_value(self, action, value): if value not in choices: args = {'value': str(value), - 'choices': ', '.join(map(str, action.choices))} + 'choices': ', '.join(repr(str(choice)) for choice in action.choices)} msg = _('invalid choice: %(value)r (choose from %(choices)s)') if self.suggest_on_error and isinstance(value, str): @@ -2692,14 +2809,16 @@ def _check_value(self, action, value): # Help-formatting methods # ======================= - def format_usage(self): - formatter = self._get_formatter() + def format_usage(self, formatter=None): + if formatter is None: + formatter = self._get_formatter() formatter.add_usage(self.usage, self._actions, self._mutually_exclusive_groups) return formatter.format_help() - def format_help(self): - formatter = self._get_formatter() + def format_help(self, formatter=None): + if formatter is None: + formatter = self._get_formatter() # usage formatter.add_usage(self.usage, self._actions, @@ -2721,11 +2840,22 @@ def format_help(self): # determine help from format above return formatter.format_help() - def _get_formatter(self): + def _get_formatter(self, file=None): formatter = self.formatter_class(prog=self.prog) - formatter._set_color(self.color) + formatter._set_color(self.color, file=file) return formatter + def _get_validation_formatter(self): + # Return cached formatter for read-only validation operations + # (_expand_help and _format_args). Avoids repeated slow _set_color calls. + # Validation never renders output, so force color off to avoid + # importing _colorize during add_argument. + if self._cached_formatter is None: + formatter = self.formatter_class(prog=self.prog) + formatter._set_color(False) + self._cached_formatter = formatter + return self._cached_formatter + # ===================== # Help-printing methods # ===================== @@ -2733,12 +2863,26 @@ def _get_formatter(self): def print_usage(self, file=None): if file is None: file = _sys.stdout - self._print_message(self.format_usage(), file) + formatter = self._get_formatter(file=file) + try: + usage_text = self.format_usage(formatter=formatter) + except TypeError: + # Backward compatibility for formatter classes that + # do not accept the 'formatter' keyword argument. + usage_text = self.format_usage() + self._print_message(usage_text, file) def print_help(self, file=None): if file is None: file = _sys.stdout - self._print_message(self.format_help(), file) + formatter = self._get_formatter(file=file) + try: + help_text = self.format_help(formatter=formatter) + except TypeError: + # Backward compatibility for formatter classes that + # do not accept the 'formatter' keyword argument. + help_text = self.format_help() + self._print_message(help_text, file) def _print_message(self, message, file=None): if message: @@ -2748,6 +2892,13 @@ def _print_message(self, message, file=None): except (AttributeError, OSError): pass + def _get_theme(self, file=None): + # If self.color is False, _colorize is not imported + if self.color and _colorize.can_colorize(file=file): + return _colorize.get_theme(force_color=True).argparse + else: + return _colorless_theme + # =============== # Exiting methods # =============== @@ -2767,9 +2918,26 @@ def error(self, message): should either exit or raise an exception. """ self.print_usage(_sys.stderr) + theme = self._get_theme(file=_sys.stderr) + fmt = _('%(prog)s: error: %(message)s\n') + fmt = fmt.replace('error: %(message)s', + f'{theme.error}error:{theme.reset} {theme.message}%(message)s{theme.reset}') + args = {'prog': self.prog, 'message': message} - self.exit(2, _('%(prog)s: error: %(message)s\n') % args) + self.exit(2, fmt % args) def _warning(self, message): + theme = self._get_theme(file=_sys.stderr) + fmt = _('%(prog)s: warning: %(message)s\n') + fmt = fmt.replace('warning: %(message)s', + f'{theme.warning}warning:{theme.reset} {theme.message}%(message)s{theme.reset}') args = {'prog': self.prog, 'message': message} - self._print_message(_('%(prog)s: warning: %(message)s\n') % args, _sys.stderr) + self._print_message(fmt % args, _sys.stderr) + +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "1.1" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/ast.py b/Lib/ast.py index 6d3daf64f5c6d7..f445b32040e5fb 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -21,10 +21,10 @@ :license: Python License. """ from _ast import * - +lazy import warnings def parse(source, filename='', mode='exec', *, - type_comments=False, feature_version=None, optimize=-1): + type_comments=False, feature_version=None, optimize=-1, module=None): """ Parse the source into an AST node. Equivalent to compile(source, filename, mode, PyCF_ONLY_AST). @@ -44,7 +44,8 @@ def parse(source, filename='', mode='exec', *, feature_version = minor # Else it should be an int giving the minor version for 3.x. return compile(source, filename, mode, flags, - _feature_version=feature_version, optimize=optimize) + _feature_version=feature_version, optimize=optimize, + module=module) def literal_eval(node_or_string): @@ -57,73 +58,93 @@ def literal_eval(node_or_string): Caution: A complex expression can overflow the C stack and cause a crash. """ if isinstance(node_or_string, str): - node_or_string = parse(node_or_string.lstrip(" \t"), mode='eval') - if isinstance(node_or_string, Expression): + node_or_string = parse(node_or_string.lstrip(" \t"), mode='eval').body + elif isinstance(node_or_string, Expression): node_or_string = node_or_string.body - def _raise_malformed_node(node): - msg = "malformed node or string" - if lno := getattr(node, 'lineno', None): - msg += f' on line {lno}' - raise ValueError(msg + f': {node!r}') - def _convert_num(node): - if not isinstance(node, Constant) or type(node.value) not in (int, float, complex): - _raise_malformed_node(node) + return _convert_literal(node_or_string) + + +def _convert_literal(node): + """ + Used by `literal_eval` to convert an AST node into a value. + """ + if isinstance(node, Constant): return node.value - def _convert_signed_num(node): - if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)): - operand = _convert_num(node.operand) - if isinstance(node.op, UAdd): - return + operand - else: - return - operand - return _convert_num(node) - def _convert(node): - if isinstance(node, Constant): - return node.value - elif isinstance(node, Tuple): - return tuple(map(_convert, node.elts)) - elif isinstance(node, List): - return list(map(_convert, node.elts)) - elif isinstance(node, Set): - return set(map(_convert, node.elts)) - elif (isinstance(node, Call) and isinstance(node.func, Name) and - node.func.id == 'set' and node.args == node.keywords == []): - return set() - elif isinstance(node, Dict): - if len(node.keys) != len(node.values): - _raise_malformed_node(node) - return dict(zip(map(_convert, node.keys), - map(_convert, node.values))) - elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)): - left = _convert_signed_num(node.left) - right = _convert_num(node.right) - if isinstance(left, (int, float)) and isinstance(right, complex): - if isinstance(node.op, Add): - return left + right - else: - return left - right - return _convert_signed_num(node) - return _convert(node_or_string) + if isinstance(node, Dict) and len(node.keys) == len(node.values): + return dict(zip( + map(_convert_literal, node.keys), + map(_convert_literal, node.values), + )) + if isinstance(node, Tuple): + return tuple(map(_convert_literal, node.elts)) + if isinstance(node, List): + return list(map(_convert_literal, node.elts)) + if isinstance(node, Set): + return set(map(_convert_literal, node.elts)) + if ( + isinstance(node, Call) and isinstance(node.func, Name) + and node.func.id == 'set' and node.args == node.keywords == [] + ): + return set() + if ( + isinstance(node, UnaryOp) + and isinstance(node.op, (UAdd, USub)) + and isinstance(node.operand, Constant) + and type(operand := node.operand.value) in (int, float, complex) + ): + if isinstance(node.op, UAdd): + return + operand + else: + return - operand + if ( + isinstance(node, BinOp) + and isinstance(node.op, (Add, Sub)) + and isinstance(node.left, (Constant, UnaryOp)) + and isinstance(node.right, Constant) + and type(left := _convert_literal(node.left)) in (int, float) + and type(right := _convert_literal(node.right)) is complex + ): + if isinstance(node.op, Add): + return left + right + else: + return left - right + msg = "malformed node or string" + if lno := getattr(node, 'lineno', None): + msg += f' on line {lno}' + raise ValueError(msg + f': {node!r}') def dump( node, annotate_fields=True, include_attributes=False, *, - indent=None, show_empty=False, + color=False, indent=None, show_empty=False, ): """ Return a formatted dump of the tree in node. This is mainly useful for - debugging purposes. If annotate_fields is true (by default), - the returned string will show the names and the values for fields. - If annotate_fields is false, the result string will be more compact by - omitting unambiguous field names. Attributes such as line - numbers and column offsets are not dumped by default. If this is wanted, - include_attributes can be set to true. If indent is a non-negative - integer or string, then the tree will be pretty-printed with that indent - level. None (the default) selects the single line representation. + debugging purposes. + + If annotate_fields is true (by default), the returned string will show the + names and the values for fields. If annotate_fields is false, the result + string will be more compact by omitting unambiguous field names. + + Attributes such as line numbers and column offsets are not dumped by default. + If this is wanted, include_attributes can be set to true. + + If color is true, the returned string is syntax highlighted using ANSI + escape sequences. If color is false (the default), colored output is always + disabled. + + If indent is a non-negative integer or string, then the tree will be + pretty-printed with that indent level. If indent is None (the default), + the tree is dumped on a single line. + If show_empty is False, then empty lists and fields that are None will be omitted from the output for better readability. """ + from _colorize import get_theme + + t = get_theme(force_color=color, force_no_color=not color).ast + def _format(node, level=0): if indent is not None: level += 1 @@ -158,7 +179,9 @@ def _format(node, level=0): field_type = cls._field_types.get(name, object) if field_type is expr_context: if not keywords: - args_buffer.append(repr(value)) + args_buffer.append( + f'{t.node}{type(value).__name__}' + f'{t.reset}()') continue if not keywords: args.extend(args_buffer) @@ -166,7 +189,7 @@ def _format(node, level=0): value, simple = _format(value, level) allsimple = allsimple and simple if keywords: - args.append('%s=%s' % (name, value)) + args.append(f'{t.field}{name}{t.reset}={value}') else: args.append(value) if include_attributes and node._attributes: @@ -179,14 +202,21 @@ def _format(node, level=0): continue value, simple = _format(value, level) allsimple = allsimple and simple - args.append('%s=%s' % (name, value)) + args.append(f'{t.attribute}{name}{t.reset}={value}') + cls_name = f'{t.node}{cls.__name__}{t.reset}' if allsimple and len(args) <= 3: - return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args - return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False + return f'{cls_name}({", ".join(args)})', not args + return f'{cls_name}({prefix}{sep.join(args)})', False elif isinstance(node, list): if not node: return '[]', True return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False + if isinstance(node, bool) or node is None or node is Ellipsis: + return f'{t.keyword}{node!r}{t.reset}', True + if isinstance(node, (int, float, complex)): + return f'{t.number}{node!r}{t.reset}', True + if isinstance(node, (str, bytes)): + return f'{t.string}{node!r}{t.reset}', True return repr(node), True if not isinstance(node, AST): @@ -600,9 +630,11 @@ def __new__(cls, dims=(), **kwargs): def _dims_getter(self): """Deprecated. Use elts instead.""" + warnings._deprecated(f"ast.Tuple.dims", remove=(3, 21)) return self.elts def _dims_setter(self, value): + warnings._deprecated(f"ast.Tuple.dims", remove=(3, 21)) self.elts = value Tuple.dims = property(_dims_getter, _dims_setter) @@ -634,9 +666,9 @@ def main(args=None): import argparse import sys - parser = argparse.ArgumentParser(color=True) + parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('infile', nargs='?', default='-', - help='the file to parse; defaults to stdin') + help='the file to parse; defaults to `stdin`') parser.add_argument('-m', '--mode', default='exec', choices=('exec', 'single', 'eval', 'func_type'), help='specify what kind of code must be parsed') @@ -649,11 +681,11 @@ def main(args=None): help='indentation of nodes (number of spaces)') parser.add_argument('--feature-version', type=str, default=None, metavar='VERSION', - help='Python version in the format 3.x ' - '(for example, 3.10)') + help='Python version in the format `3.x` ' + '(for example, `3.10`)') parser.add_argument('-O', '--optimize', type=int, default=-1, metavar='LEVEL', - help='optimization level for parser (default -1)') + help='optimization level for parser') parser.add_argument('--show-empty', default=False, action='store_true', help='show empty lists and fields in dump output') args = parser.parse_args(args) @@ -679,8 +711,28 @@ def main(args=None): tree = parse(source, name, args.mode, type_comments=args.no_type_comments, feature_version=feature_version, optimize=args.optimize) + from _colorize import can_colorize print(dump(tree, include_attributes=args.include_attributes, + color=can_colorize(file=sys.stdout), indent=args.indent, show_empty=args.show_empty)) +_deprecated = { + 'slice': globals().pop("slice"), + 'Index': globals().pop("Index"), + 'ExtSlice': globals().pop("ExtSlice"), + 'Suite': globals().pop("Suite"), + 'AugLoad': globals().pop("AugLoad"), + 'AugStore': globals().pop("AugStore"), + 'Param': globals().pop("Param") +} + +def __getattr__(attr): + try: + val = _deprecated[attr] + except KeyError: + raise AttributeError(f"module 'ast' has no attribute {attr!r}") from None + warnings._deprecated(f"ast.{attr}", remove=(3, 21)) + return val + if __name__ == '__main__': main() diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index ff3a69d1e17297..7f0565d0b8ddc7 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -12,13 +12,16 @@ import types import warnings -from _colorize import get_theme -from _pyrepl.console import InteractiveColoredConsole +try: + from _colorize import get_theme + from _pyrepl.console import InteractiveColoredConsole as InteractiveConsole +except ModuleNotFoundError: + from code import InteractiveConsole from . import futures -class AsyncIOInteractiveConsole(InteractiveColoredConsole): +class AsyncIOInteractiveConsole(InteractiveConsole): def __init__(self, locals, loop): super().__init__(locals, filename="") @@ -74,7 +77,8 @@ def callback(): return except BaseException: if keyboard_interrupted: - self.write("\nKeyboardInterrupt\n") + if not CAN_USE_PYREPL: + self.write("\nKeyboardInterrupt\n") else: self.showtraceback() return self.STATEMENT_FAILED @@ -85,34 +89,43 @@ def run(self): global return_code try: - banner = ( - f'asyncio REPL {sys.version} on {sys.platform}\n' - f'Use "await" directly instead of "asyncio.run()".\n' - f'Type "help", "copyright", "credits" or "license" ' - f'for more information.\n' - ) + if not sys.flags.quiet: + banner = ( + f'asyncio REPL {sys.version} on {sys.platform}\n' + f'Use "await" directly instead of "asyncio.run()".\n' + f'Type "help", "copyright", "credits" or "license" ' + f'for more information.\n' + ) - console.write(banner) + console.write(banner) - if startup_path := os.getenv("PYTHONSTARTUP"): + if not sys.flags.isolated and (startup_path := os.getenv("PYTHONSTARTUP")): sys.audit("cpython.run_startup", startup_path) - - import tokenize - with tokenize.open(startup_path) as f: - startup_code = compile(f.read(), startup_path, "exec") + try: + import tokenize + with tokenize.open(startup_path) as f: + startup_code = compile(f.read(), startup_path, "exec") exec(startup_code, console.locals) + except SystemExit: + raise + except BaseException: + console.showtraceback() ps1 = getattr(sys, "ps1", ">>> ") if CAN_USE_PYREPL: theme = get_theme().syntax ps1 = f"{theme.prompt}{ps1}{theme.reset}" - console.write(f"{ps1}import asyncio\n") + import_line = f'{theme.keyword}import{theme.reset} asyncio' + else: + import_line = "import asyncio" + console.write(f"{ps1}{import_line}\n") if CAN_USE_PYREPL: from _pyrepl.simple_interact import ( run_multiline_interactive_console, ) try: + sys.ps1 = ps1 run_multiline_interactive_console(console) except SystemExit: # expected via the `exit` and `quit` commands @@ -146,24 +159,35 @@ def interrupt(self) -> None: parser = argparse.ArgumentParser( prog="python3 -m asyncio", description="Interactive asyncio shell and CLI tools", - color=True, ) subparsers = parser.add_subparsers(help="sub-commands", dest="command") ps = subparsers.add_parser( "ps", help="Display a table of all pending tasks in a process" ) ps.add_argument("pid", type=int, help="Process ID to inspect") + ps.add_argument( + "--retries", + type=int, + default=3, + help="Number of retries on transient attach errors", + ) pstree = subparsers.add_parser( "pstree", help="Display a tree of all pending tasks in a process" ) pstree.add_argument("pid", type=int, help="Process ID to inspect") + pstree.add_argument( + "--retries", + type=int, + default=3, + help="Number of retries on transient attach errors", + ) args = parser.parse_args() match args.command: case "ps": - asyncio.tools.display_awaited_by_tasks_table(args.pid) + asyncio.tools.display_awaited_by_tasks_table(args.pid, retries=args.retries) sys.exit(0) case "pstree": - asyncio.tools.display_awaited_by_tasks_tree(args.pid) + asyncio.tools.display_awaited_by_tasks_tree(args.pid, retries=args.retries) sys.exit(0) case None: pass # continue to the interactive shell @@ -179,7 +203,10 @@ def interrupt(self) -> None: if os.getenv('PYTHON_BASIC_REPL'): CAN_USE_PYREPL = False else: - from _pyrepl.main import CAN_USE_PYREPL + try: + from _pyrepl.main import CAN_USE_PYREPL + except ModuleNotFoundError: + CAN_USE_PYREPL = False return_code = 0 loop = asyncio.new_event_loop() @@ -235,4 +262,5 @@ def interrupt(self) -> None: break console.write('exiting asyncio REPL...\n') + loop.close() sys.exit(return_code) diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 520d4b398545bf..e6c72e3d5b5487 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -14,21 +14,24 @@ """ import collections +import contextvars import collections.abc import concurrent.futures import errno import heapq import itertools +import math import os import socket import stat import subprocess +import sys import threading import time import traceback -import sys import warnings import weakref +import inspect try: import ssl @@ -289,6 +292,7 @@ def __init__(self, loop, sockets, protocol_factory, ssl_context, backlog, self._ssl_shutdown_timeout = ssl_shutdown_timeout self._serving = False self._serving_forever_fut = None + self._context = contextvars.copy_context() def __repr__(self): return f'<{self.__class__.__name__} sockets={self.sockets!r}>' @@ -318,7 +322,7 @@ def _start_serving(self): self._loop._start_serving( self._protocol_factory, sock, self._ssl_context, self, self._backlog, self._ssl_handshake_timeout, - self._ssl_shutdown_timeout) + self._ssl_shutdown_timeout, context=self._context) def get_loop(self): return self._loop @@ -380,6 +384,7 @@ async def serve_forever(self): except exceptions.CancelledError: try: self.close() + self.close_clients() await self.wait_closed() finally: raise @@ -483,10 +488,10 @@ def set_task_factory(self, factory): If factory is None the default task factory will be set. If factory is a callable, it should have a signature matching - '(loop, coro, **kwargs)', where 'loop' will be a reference to the active - event loop, 'coro' will be a coroutine object, and **kwargs will be - arbitrary keyword arguments that should be passed on to Task. - The callable must return a Task. + '(loop, coro, **kwargs)', where 'loop' will be a reference to the + active event loop, 'coro' will be a coroutine object, and **kwargs + will be arbitrary keyword arguments that should be passed on to + Task. The callable must return a Task. """ if factory is not None and not callable(factory): raise TypeError('task factory must be a callable or None') @@ -507,7 +512,8 @@ def _make_ssl_transport( extra=None, server=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, - call_connection_made=True): + call_connection_made=True, + context=None): """Create SSL transport.""" raise NotImplementedError @@ -636,7 +642,7 @@ def _check_running(self): def _run_forever_setup(self): """Prepare the run loop to process events. - This method exists so that custom custom event loop subclasses (e.g., event loops + This method exists so that custom event loop subclasses (e.g., event loops that integrate a GUI event loop with Python's event loop) have access to all the loop setup logic. """ @@ -656,7 +662,7 @@ def _run_forever_setup(self): def _run_forever_cleanup(self): """Clean up after an event loop finishes the looping over events. - This method exists so that custom custom event loop subclasses (e.g., event loops + This method exists so that custom event loop subclasses (e.g., event loops that integrate a GUI event loop with Python's event loop) have access to all the loop cleanup logic. """ @@ -721,8 +727,8 @@ def run_until_complete(self, future): def stop(self): """Stop running the event loop. - Every callback already scheduled will still run. This simply informs - run_forever to stop looping after a complete iteration. + Every callback already scheduled will still run. This simply + informs run_forever to stop looping after a complete iteration. """ self._stopping = True @@ -835,7 +841,7 @@ def call_soon(self, callback, *args, context=None): def _check_callback(self, callback, method): if (coroutines.iscoroutine(callback) or - coroutines._iscoroutinefunction(callback)): + inspect.iscoroutinefunction(callback)): raise TypeError( f"coroutines cannot be used with {method}()") if not callable(callback): @@ -949,7 +955,7 @@ async def sock_sendfile(self, sock, file, offset=0, count=None, try: return await self._sock_sendfile_native(sock, file, offset, count) - except exceptions.SendfileNotAvailableError as exc: + except exceptions.SendfileNotAvailableError: if not fallback: raise return await self._sock_sendfile_fallback(sock, file, @@ -963,7 +969,7 @@ async def _sock_sendfile_native(self, sock, file, offset, count): f"and file {file!r} combination") async def _sock_sendfile_fallback(self, sock, file, offset, count): - if offset: + if hasattr(file, 'seek'): file.seek(offset) blocksize = ( min(count, constants.SENDFILE_FALLBACK_READBUFFER_SIZE) @@ -1070,12 +1076,12 @@ async def create_connection( Create a streaming transport connection to a given internet host and port: socket family AF_INET or socket.AF_INET6 depending on host (or - family if specified), socket type SOCK_STREAM. protocol_factory must be - a callable returning a protocol instance. + family if specified), socket type SOCK_STREAM. protocol_factory must + be a callable returning a protocol instance. - This method is a coroutine which will try to establish the connection - in the background. When successful, the coroutine returns a - (transport, protocol) pair. + This method is a coroutine which will try to establish the + connection in the background. When successful, the coroutine + returns a (transport, protocol) pair. """ if server_hostname is not None and not ssl: raise ValueError('server_hostname is only meaningful with ssl') @@ -1211,9 +1217,10 @@ async def _create_connection_transport( self, sock, protocol_factory, ssl, server_hostname, server_side=False, ssl_handshake_timeout=None, - ssl_shutdown_timeout=None): + ssl_shutdown_timeout=None, context=None): sock.setblocking(False) + context = context if context is not None else contextvars.copy_context() protocol = protocol_factory() waiter = self.create_future() @@ -1223,9 +1230,10 @@ async def _create_connection_transport( sock, protocol, sslcontext, waiter, server_side=server_side, server_hostname=server_hostname, ssl_handshake_timeout=ssl_handshake_timeout, - ssl_shutdown_timeout=ssl_shutdown_timeout) + ssl_shutdown_timeout=ssl_shutdown_timeout, + context=context) else: - transport = self._make_socket_transport(sock, protocol, waiter) + transport = self._make_socket_transport(sock, protocol, waiter, context=context) try: await waiter @@ -1270,7 +1278,7 @@ async def sendfile(self, transport, file, offset=0, count=None, try: return await self._sendfile_native(transport, file, offset, count) - except exceptions.SendfileNotAvailableError as exc: + except exceptions.SendfileNotAvailableError: if not fallback: raise @@ -1278,7 +1286,6 @@ async def sendfile(self, transport, file, offset=0, count=None, raise RuntimeError( f"fallback is disabled and native sendfile is not " f"supported for transport {transport!r}") - return await self._sendfile_fallback(transport, file, offset, count) @@ -1287,7 +1294,7 @@ async def _sendfile_native(self, transp, file, offset, count): "sendfile syscall is not supported") async def _sendfile_fallback(self, transp, file, offset, count): - if offset: + if hasattr(file, 'seek'): file.seek(offset) blocksize = min(count, 16384) if count else 16384 buf = bytearray(blocksize) @@ -1345,6 +1352,17 @@ async def start_tls(self, transport, protocol, sslcontext, *, # have a chance to get called before "ssl_protocol.connection_made()". transport.pause_reading() + # gh-142352: move buffered StreamReader data to SSLProtocol + if server_side: + from .streams import StreamReaderProtocol + if isinstance(protocol, StreamReaderProtocol): + stream_reader = getattr(protocol, '_stream_reader', None) + if stream_reader is not None: + buffer = stream_reader._buffer + if buffer: + ssl_protocol._incoming.write(buffer) + buffer.clear() + transport.set_protocol(ssl_protocol) conmade_cb = self.call_soon(ssl_protocol.connection_made, transport) resume_cb = self.call_soon(transport.resume_reading) @@ -1532,11 +1550,11 @@ async def create_server( The host parameter can be a string, in that case the TCP server is bound to host and port. - The host parameter can also be a sequence of strings and in that case - the TCP server is bound to all hosts of the sequence. If a host - appears multiple times (possibly indirectly e.g. when hostnames - resolve to the same IP address), the server is only bound once to that - host. + The host parameter can also be a sequence of strings and in that + case the TCP server is bound to all hosts of the sequence. If + a host appears multiple times (possibly indirectly e.g. when + hostnames resolve to the same IP address), the server is only bound + once to that host. Return a Server object which can be used to stop the service. @@ -2011,7 +2029,10 @@ def _run_once(self): event_list = None # Handle 'later' callbacks that are ready. - end_time = self.time() + self._clock_resolution + now = self.time() + # Ensure that `end_time` is strictly increasing + # when the clock resolution is too small. + end_time = now + max(self._clock_resolution, math.ulp(now)) while self._scheduled: handle = self._scheduled[0] if handle._when >= end_time: diff --git a/Lib/asyncio/base_subprocess.py b/Lib/asyncio/base_subprocess.py index d40af422e614c1..224b1883808a41 100644 --- a/Lib/asyncio/base_subprocess.py +++ b/Lib/asyncio/base_subprocess.py @@ -26,6 +26,7 @@ def __init__(self, loop, protocol, args, shell, self._pending_calls = collections.deque() self._pipes = {} self._finished = False + self._pipes_connected = False if stdin == subprocess.PIPE: self._pipes[0] = None @@ -213,6 +214,7 @@ async def _connect_pipes(self, waiter): else: if waiter is not None and not waiter.cancelled(): waiter.set_result(None) + self._pipes_connected = True def _call(self, cb, *data): if self._pending_calls is not None: @@ -256,6 +258,15 @@ def _try_finish(self): assert not self._finished if self._returncode is None: return + if not self._pipes_connected: + # self._pipes_connected can be False if not all pipes were connected + # because either the process failed to start or the self._connect_pipes task + # got cancelled. In this broken state we consider all pipes disconnected and + # to avoid hanging forever in self._wait as otherwise _exit_waiters + # would never be woken up, we wake them up here. + for waiter in self._exit_waiters: + if not waiter.done(): + waiter.set_result(self._returncode) if all(p is not None and p.disconnected for p in self._pipes.values()): self._finished = True @@ -267,7 +278,7 @@ def _call_connection_lost(self, exc): finally: # wake up futures waiting for wait() for waiter in self._exit_waiters: - if not waiter.cancelled(): + if not waiter.done(): waiter.set_result(self._returncode) self._exit_waiters = None self._loop = None diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py index a51319cb72a6a9..3a4246f6ccd4c2 100644 --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -1,7 +1,6 @@ -__all__ = 'iscoroutinefunction', 'iscoroutine' +__all__ = ('iscoroutine',) import collections.abc -import inspect import os import sys import types @@ -13,25 +12,6 @@ def _is_debug_mode(): bool(os.environ.get('PYTHONASYNCIODEBUG'))) -# A marker for iscoroutinefunction. -_is_coroutine = object() - - -def iscoroutinefunction(func): - import warnings - """Return True if func is a decorated coroutine function.""" - warnings._deprecated("asyncio.iscoroutinefunction", - f"{warnings._DEPRECATED_MSG}; " - "use inspect.iscoroutinefunction() instead", - remove=(3,16)) - return _iscoroutinefunction(func) - - -def _iscoroutinefunction(func): - return (inspect.iscoroutinefunction(func) or - getattr(func, '_is_coroutine', None) is _is_coroutine) - - # Prioritize native coroutine check to speed-up # asyncio.iscoroutine. _COROUTINE_TYPES = (types.CoroutineType, collections.abc.Coroutine) diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index a7fb55982abe9c..42d8408a38dfd1 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -374,8 +374,8 @@ async def create_server( If host is an empty string or None all interfaces are assumed and a list of multiple sockets will be returned (most likely - one for IPv4 and another one for IPv6). The host parameter can also be - a sequence (e.g. list) of hosts to bind to. + one for IPv4 and another one for IPv6). The host parameter can also + be a sequence (e.g. list) of hosts to bind to. family can be set to either AF_INET or AF_INET6 to force the socket to use IPv4 or IPv6. If not set it will be determined @@ -415,8 +415,9 @@ async def create_server( start_serving set to True (default) causes the created server to start accepting connections immediately. When set to False, - the user should await Server.start_serving() or Server.serve_forever() - to make the server to start accepting connections. + the user should await Server.start_serving() or + Server.serve_forever() to make the server to start accepting + connections. """ raise NotImplementedError @@ -479,8 +480,9 @@ async def create_unix_server( start_serving set to True (default) causes the created server to start accepting connections immediately. When set to False, - the user should await Server.start_serving() or Server.serve_forever() - to make the server to start accepting connections. + the user should await Server.start_serving() or + Server.serve_forever() to make the server to start accepting + connections. """ raise NotImplementedError @@ -511,8 +513,8 @@ async def create_datagram_endpoint(self, protocol_factory, protocol_factory must be a callable returning a protocol instance. - socket family AF_INET, socket.AF_INET6 or socket.AF_UNIX depending on - host (or family if specified), socket type SOCK_DGRAM. + socket family AF_INET, socket.AF_INET6 or socket.AF_UNIX depending + on host (or family if specified), socket type SOCK_DGRAM. reuse_address tells the kernel to reuse a local socket in TIME_WAIT state, without waiting for its natural timeout to @@ -552,7 +554,8 @@ async def connect_read_pipe(self, protocol_factory, pipe): async def connect_write_pipe(self, protocol_factory, pipe): """Register write pipe in event loop. - protocol_factory should instantiate object with BaseProtocol interface. + protocol_factory should instantiate object with BaseProtocol + interface. Pipe is file-like object already switched to nonblocking. Return pair (transport, protocol), where transport support WriteTransport interface.""" diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 6bd00a644789f1..11858a0274a69f 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -79,6 +79,10 @@ def __init__(self, *, loop=None): loop object used by the future. If it's not provided, the future uses the default event loop. """ + if self._loop is not None: + raise RuntimeError(f"{self.__class__.__name__} object is already " + "initialized") + if loop is None: self._loop = events.get_event_loop() else: @@ -389,7 +393,7 @@ def _set_state(future, other): def _call_check_cancel(destination): if destination.cancelled(): - if source_loop is None or source_loop is dest_loop: + if source_loop is None or source_loop is events._get_running_loop(): source.cancel() else: source_loop.call_soon_threadsafe(source.cancel) @@ -398,7 +402,7 @@ def _call_set_state(source): if (destination.cancelled() and dest_loop is not None and dest_loop.is_closed()): return - if dest_loop is None or dest_loop is source_loop: + if dest_loop is None or dest_loop is events._get_running_loop(): _set_state(destination, source) else: if dest_loop.is_closed(): diff --git a/Lib/asyncio/graph.py b/Lib/asyncio/graph.py index b5bfeb1630a159..35f7fa62140bd4 100644 --- a/Lib/asyncio/graph.py +++ b/Lib/asyncio/graph.py @@ -112,13 +112,13 @@ def capture_call_graph( optional keyword-only 'depth' argument can be used to skip the specified number of frames from top of the stack. - If the optional keyword-only 'limit' argument is provided, each call stack - in the resulting graph is truncated to include at most ``abs(limit)`` - entries. If 'limit' is positive, the entries left are the closest to - the invocation point. If 'limit' is negative, the topmost entries are - left. If 'limit' is omitted or None, all entries are present. - If 'limit' is 0, the call stack is not captured at all, only - "awaited by" information is present. + If the optional keyword-only 'limit' argument is provided, each call + stack in the resulting graph is truncated to include at most + ``abs(limit)`` entries. If 'limit' is positive, the entries left are + the closest to the invocation point. If 'limit' is negative, the + topmost entries are left. If 'limit' is omitted or None, all entries + are present. If 'limit' is 0, the call stack is not captured at all, + only "awaited by" information is present. """ loop = events._get_running_loop() diff --git a/Lib/asyncio/locks.py b/Lib/asyncio/locks.py index fa3a94764b507a..c59ea15111bef2 100644 --- a/Lib/asyncio/locks.py +++ b/Lib/asyncio/locks.py @@ -145,10 +145,7 @@ def _wake_up_first(self): """Ensure that the first waiter will wake up.""" if not self._waiters: return - try: - fut = next(iter(self._waiters)) - except StopIteration: - return + fut = next(iter(self._waiters)) # .done() means that the waiter is already set to wake up. if not fut.done(): @@ -158,10 +155,10 @@ def _wake_up_first(self): class Event(mixins._LoopBoundMixin): """Asynchronous equivalent to threading.Event. - Class implementing event objects. An event manages a flag that can be set - to true with the set() method and reset to false with the clear() method. - The wait() method blocks until the flag is true. The flag is initially - false. + Class implementing event objects. An event manages a flag that can be + set to true with the set() method and reset to false with the clear() + method. The wait() method blocks until the flag is true. The flag is + initially false. """ def __init__(self): @@ -353,9 +350,9 @@ class Semaphore(_ContextManagerMixin, mixins._LoopBoundMixin): """A Semaphore implementation. A semaphore manages an internal counter which is decremented by each - acquire() call and incremented by each release() call. The counter - can never go below zero; when acquire() finds that it is zero, it blocks, - waiting until some other thread calls release(). + acquire() call and incremented by each release() call. The counter + can never go below zero; when acquire() finds that it is zero, it + blocks, waiting until some other thread calls release(). Semaphores also support the context management protocol. @@ -511,8 +508,8 @@ async def __aexit__(self, *args): async def wait(self): """Wait for the barrier. - When the specified number of tasks have started waiting, they are all - simultaneously awoken. + When the specified number of tasks have started waiting, they are + all simultaneously awoken. Returns an unique and individual index number from 0 to 'parties-1'. """ async with self._cond: diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index 7eb55bd63ddb73..cf2902b4c76559 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -460,6 +460,8 @@ def _pipe_closed(self, fut): class _ProactorDatagramTransport(_ProactorBasePipeTransport, transports.DatagramTransport): max_size = 256 * 1024 + _header_size = 8 + def __init__(self, loop, sock, protocol, address=None, waiter=None, extra=None): self._address = address @@ -499,7 +501,7 @@ def sendto(self, data, addr=None): # Ensure that what we buffer is immutable. self._buffer.append((bytes(data), addr)) - self._buffer_size += len(data) + 8 # include header bytes + self._buffer_size += len(data) + self._header_size if self._write_fut is None: # No current write operations are active, kick one off @@ -526,7 +528,7 @@ def _loop_writing(self, fut=None): return data, addr = self._buffer.popleft() - self._buffer_size -= len(data) + self._buffer_size -= len(data) + self._header_size if self._address is not None: self._write_fut = self._loop._proactor.send(self._sock, data) @@ -640,7 +642,7 @@ def __init__(self, proactor): signal.set_wakeup_fd(self._csock.fileno()) def _make_socket_transport(self, sock, protocol, waiter=None, - extra=None, server=None): + extra=None, server=None, context=None): return _ProactorSocketTransport(self, sock, protocol, waiter, extra, server) @@ -649,7 +651,7 @@ def _make_ssl_transport( *, server_side=False, server_hostname=None, extra=None, server=None, ssl_handshake_timeout=None, - ssl_shutdown_timeout=None): + ssl_shutdown_timeout=None, context=None): ssl_protocol = sslproto.SSLProtocol( self, protocol, sslcontext, waiter, server_side, server_hostname, @@ -731,7 +733,7 @@ async def sock_accept(self, sock): async def _sock_sendfile_native(self, sock, file, offset, count): try: fileno = file.fileno() - except (AttributeError, io.UnsupportedOperation) as err: + except (AttributeError, io.UnsupportedOperation): raise exceptions.SendfileNotAvailableError("not a regular file") try: fsize = os.fstat(fileno).st_size @@ -754,8 +756,7 @@ async def _sock_sendfile_native(self, sock, file, offset, count): offset += blocksize total_sent += blocksize finally: - if total_sent > 0: - file.seek(offset) + file.seek(offset) async def _sendfile_native(self, transp, file, offset, count): resume_reading = transp.is_reading() @@ -835,7 +836,7 @@ def _write_to_self(self): def _start_serving(self, protocol_factory, sock, sslcontext=None, server=None, backlog=100, ssl_handshake_timeout=None, - ssl_shutdown_timeout=None): + ssl_shutdown_timeout=None, context=None): def loop(f=None): try: diff --git a/Lib/asyncio/queues.py b/Lib/asyncio/queues.py index e5d6f2e4b61e17..30004f2bc9bacd 100644 --- a/Lib/asyncio/queues.py +++ b/Lib/asyncio/queues.py @@ -33,11 +33,11 @@ class QueueShutDown(Exception): class Queue(mixins._LoopBoundMixin): """A queue, useful for coordinating producer and consumer coroutines. - If maxsize is less than or equal to zero, the queue size is infinite. If it - is an integer greater than 0, then "await put()" will block when the - queue reaches maxsize, until an item is removed by get(). + If maxsize is less than or equal to zero, the queue size is infinite. + If it is an integer greater than 0, then "await put()" will block when + the queue reaches maxsize, until an item is removed by get(). - Unlike the standard library Queue, you can reliably know this Queue's size + Unlike queue.Queue, you can reliably know this Queue's size with qsize(), since your single-threaded asyncio application won't be interrupted between calling qsize() and doing an operation on the Queue. """ @@ -174,8 +174,8 @@ async def get(self): If queue is empty, wait until an item is available. - Raises QueueShutDown if the queue has been shut down and is empty, or - if the queue has been shut down immediately. + Raises QueueShutDown if the queue has been shut down and is empty, + or if the queue has been shut down immediately. """ while self.empty(): if self._is_shutdown and self.empty(): @@ -203,10 +203,11 @@ async def get(self): def get_nowait(self): """Remove and return an item from the queue. - Return an item if one is immediately available, else raise QueueEmpty. + Return an item if one is immediately available, else raise + QueueEmpty. - Raises QueueShutDown if the queue has been shut down and is empty, or - if the queue has been shut down immediately. + Raises QueueShutDown if the queue has been shut down and is empty, + or if the queue has been shut down immediately. """ if self.empty(): if self._is_shutdown: @@ -223,12 +224,12 @@ def task_done(self): a subsequent call to task_done() tells the queue that the processing on the task is complete. - If a join() is currently blocking, it will resume when all items have - been processed (meaning that a task_done() call was received for every - item that had been put() into the queue). + If a join() is currently blocking, it will resume when all items + have been processed (meaning that a task_done() call was received + for every item that had been put() into the queue). - Raises ValueError if called more times than there were items placed in - the queue. + Raises ValueError if called more times than there were items placed + in the queue. """ if self._unfinished_tasks <= 0: raise ValueError('task_done() called too many times') @@ -239,10 +240,11 @@ def task_done(self): async def join(self): """Block until all items in the queue have been gotten and processed. - The count of unfinished tasks goes up whenever an item is added to the - queue. The count goes down whenever a consumer calls task_done() to - indicate that the item was retrieved and all work on it is complete. - When the count of unfinished tasks drops to zero, join() unblocks. + The count of unfinished tasks goes up whenever an item is added to + the queue. The count goes down whenever a consumer calls + task_done() to indicate that the item was retrieved and all work on + it is complete. When the count of unfinished tasks drops to zero, + join() unblocks. """ if self._unfinished_tasks > 0: await self._finished.wait() @@ -253,9 +255,11 @@ def shutdown(self, immediate=False): By default, gets will only raise once the queue is empty. Set 'immediate' to True to make gets raise immediately instead. - All blocked callers of put() and get() will be unblocked. If - 'immediate', unblock callers of join() regardless of the - number of unfinished tasks. + All blocked callers of put() and get() will be unblocked. + + If 'immediate', the queue is drained and unfinished tasks + is reduced by the number of drained tasks. If unfinished tasks + is reduced to zero, callers of Queue.join are unblocked. """ self._is_shutdown = True if immediate: diff --git a/Lib/asyncio/runners.py b/Lib/asyncio/runners.py index ba37e003a655c0..774a317564df22 100644 --- a/Lib/asyncio/runners.py +++ b/Lib/asyncio/runners.py @@ -35,7 +35,8 @@ class Runner: with asyncio.Runner(debug=True) as runner: runner.run(main()) - The run() method can be called multiple times within the runner's context. + The run() method can be called multiple times within the runner's + context. This can be useful for interactive console (e.g. IPython), unittest runners, console tools, -- everywhere when async code diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index 6ad84044adf146..4fa4dd5a885236 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -53,7 +53,7 @@ def _test_selector_event(selector, fd, event): class BaseSelectorEventLoop(base_events.BaseEventLoop): """Selector event loop. - See events.EventLoop for API specification. + See events.AbstractEventLoop for API specification. """ def __init__(self, selector=None): @@ -67,10 +67,10 @@ def __init__(self, selector=None): self._transports = weakref.WeakValueDictionary() def _make_socket_transport(self, sock, protocol, waiter=None, *, - extra=None, server=None): + extra=None, server=None, context=None): self._ensure_fd_no_transport(sock) return _SelectorSocketTransport(self, sock, protocol, waiter, - extra, server) + extra, server, context=context) def _make_ssl_transport( self, rawsock, protocol, sslcontext, waiter=None, @@ -78,16 +78,17 @@ def _make_ssl_transport( extra=None, server=None, ssl_handshake_timeout=constants.SSL_HANDSHAKE_TIMEOUT, ssl_shutdown_timeout=constants.SSL_SHUTDOWN_TIMEOUT, + context=None, ): self._ensure_fd_no_transport(rawsock) ssl_protocol = sslproto.SSLProtocol( self, protocol, sslcontext, waiter, server_side, server_hostname, ssl_handshake_timeout=ssl_handshake_timeout, - ssl_shutdown_timeout=ssl_shutdown_timeout + ssl_shutdown_timeout=ssl_shutdown_timeout, ) _SelectorSocketTransport(self, rawsock, ssl_protocol, - extra=extra, server=server) + extra=extra, server=server, context=context) return ssl_protocol._app_transport def _make_datagram_transport(self, sock, protocol, @@ -159,16 +160,16 @@ def _write_to_self(self): def _start_serving(self, protocol_factory, sock, sslcontext=None, server=None, backlog=100, ssl_handshake_timeout=constants.SSL_HANDSHAKE_TIMEOUT, - ssl_shutdown_timeout=constants.SSL_SHUTDOWN_TIMEOUT): + ssl_shutdown_timeout=constants.SSL_SHUTDOWN_TIMEOUT, context=None): self._add_reader(sock.fileno(), self._accept_connection, protocol_factory, sock, sslcontext, server, backlog, - ssl_handshake_timeout, ssl_shutdown_timeout) + ssl_handshake_timeout, ssl_shutdown_timeout, context) def _accept_connection( self, protocol_factory, sock, sslcontext=None, server=None, backlog=100, ssl_handshake_timeout=constants.SSL_HANDSHAKE_TIMEOUT, - ssl_shutdown_timeout=constants.SSL_SHUTDOWN_TIMEOUT): + ssl_shutdown_timeout=constants.SSL_SHUTDOWN_TIMEOUT, context=None): # This method is only called once for each event loop tick where the # listening socket has triggered an EVENT_READ. There may be multiple # connections waiting for an .accept() so it is called in a loop. @@ -204,21 +205,22 @@ def _accept_connection( self._start_serving, protocol_factory, sock, sslcontext, server, backlog, ssl_handshake_timeout, - ssl_shutdown_timeout) + ssl_shutdown_timeout, context) else: raise # The event loop will catch, log and ignore it. else: extra = {'peername': addr} + conn_context = context.copy() if context is not None else None accept = self._accept_connection2( protocol_factory, conn, extra, sslcontext, server, - ssl_handshake_timeout, ssl_shutdown_timeout) - self.create_task(accept) + ssl_handshake_timeout, ssl_shutdown_timeout, context=conn_context) + self.create_task(accept, context=conn_context) async def _accept_connection2( self, protocol_factory, conn, extra, sslcontext=None, server=None, ssl_handshake_timeout=constants.SSL_HANDSHAKE_TIMEOUT, - ssl_shutdown_timeout=constants.SSL_SHUTDOWN_TIMEOUT): + ssl_shutdown_timeout=constants.SSL_SHUTDOWN_TIMEOUT, context=None): protocol = None transport = None try: @@ -229,11 +231,12 @@ async def _accept_connection2( conn, protocol, sslcontext, waiter=waiter, server_side=True, extra=extra, server=server, ssl_handshake_timeout=ssl_handshake_timeout, - ssl_shutdown_timeout=ssl_shutdown_timeout) + ssl_shutdown_timeout=ssl_shutdown_timeout, + context=context) else: transport = self._make_socket_transport( conn, protocol, waiter=waiter, extra=extra, - server=server) + server=server, context=context) try: await waiter @@ -275,9 +278,9 @@ def _ensure_fd_no_transport(self, fd): f'File descriptor {fd!r} is used by transport ' f'{transport!r}') - def _add_reader(self, fd, callback, *args): + def _add_reader(self, fd, callback, *args, context=None): self._check_closed() - handle = events.Handle(callback, args, self, None) + handle = events.Handle(callback, args, self, context=context) key = self._selector.get_map().get(fd) if key is None: self._selector.register(fd, selectors.EVENT_READ, @@ -309,9 +312,9 @@ def _remove_reader(self, fd): else: return False - def _add_writer(self, fd, callback, *args): + def _add_writer(self, fd, callback, *args, context=None): self._check_closed() - handle = events.Handle(callback, args, self, None) + handle = events.Handle(callback, args, self, context=context) key = self._selector.get_map().get(fd) if key is None: self._selector.register(fd, selectors.EVENT_WRITE, @@ -530,11 +533,12 @@ def _sock_recvfrom_into(self, fut, sock, buf, bufsize): async def sock_sendall(self, sock, data): """Send data to the socket. - The socket must be connected to a remote socket. This method continues - to send data from data until either all data has been sent or an - error occurs. None is returned on success. On error, an exception is - raised, and there is no way to determine how much data, if any, was - successfully processed by the receiving end of the connection. + The socket must be connected to a remote socket. This method + continues to send data from data until either all data has been + sent or an error occurs. None is returned on success. On error, + an exception is raised, and there is no way to determine how much + data, if any, was successfully processed by the receiving end of + the connection. """ base_events._check_ssl_socket(sock) if self._debug and sock.gettimeout() != 0: @@ -583,11 +587,12 @@ def _sock_sendall(self, fut, sock, view, pos): async def sock_sendto(self, sock, data, address): """Send data to the socket. - The socket must be connected to a remote socket. This method continues - to send data from data until either all data has been sent or an - error occurs. None is returned on success. On error, an exception is - raised, and there is no way to determine how much data, if any, was - successfully processed by the receiving end of the connection. + The socket must be connected to a remote socket. This method + continues to send data from data until either all data has been + sent or an error occurs. None is returned on success. On error, + an exception is raised, and there is no way to determine how much + data, if any, was successfully processed by the receiving end of + the connection. """ base_events._check_ssl_socket(sock) if self._debug and sock.gettimeout() != 0: @@ -698,10 +703,11 @@ def _sock_connect_cb(self, fut, sock, address): async def sock_accept(self, sock): """Accept a connection. - The socket must be bound to an address and listening for connections. - The return value is a pair (conn, address) where conn is a new socket - object usable to send and receive data on the connection, and address - is the address bound to the socket on the other end of the connection. + The socket must be bound to an address and listening for + connections. The return value is a pair (conn, address) where + conn is a new socket object usable to send and receive data on the + connection, and address is the address bound to the socket on the + other end of the connection. """ base_events._check_ssl_socket(sock) if self._debug and sock.gettimeout() != 0: @@ -770,7 +776,7 @@ class _SelectorTransport(transports._FlowControlMixin, # exception) _sock = None - def __init__(self, loop, sock, protocol, extra=None, server=None): + def __init__(self, loop, sock, protocol, extra=None, server=None, context=None): super().__init__(extra, loop) self._extra['socket'] = trsock.TransportSocket(sock) try: @@ -784,12 +790,13 @@ def __init__(self, loop, sock, protocol, extra=None, server=None): self._extra['peername'] = None self._sock = sock self._sock_fd = sock.fileno() - + self._context = context self._protocol_connected = False self.set_protocol(protocol) self._server = server self._buffer = collections.deque() + self._buffer_size = 0 self._conn_lost = 0 # Set when call to connection_lost scheduled. self._closing = False # Set when close() called. self._paused = False # Set when pause_reading() called @@ -866,7 +873,7 @@ def close(self): if not self._buffer: self._conn_lost += 1 self._loop._remove_writer(self._sock_fd) - self._loop.call_soon(self._call_connection_lost, None) + self._call_soon(self._call_connection_lost, None) def __del__(self, _warn=warnings.warn): if self._sock is not None: @@ -894,12 +901,13 @@ def _force_close(self, exc): return if self._buffer: self._buffer.clear() + self._buffer_size = 0 self._loop._remove_writer(self._sock_fd) if not self._closing: self._closing = True self._loop._remove_reader(self._sock_fd) self._conn_lost += 1 - self._loop.call_soon(self._call_connection_lost, exc) + self._call_soon(self._call_connection_lost, exc) def _call_connection_lost(self, exc): try: @@ -916,13 +924,18 @@ def _call_connection_lost(self, exc): self._server = None def get_write_buffer_size(self): - return sum(map(len, self._buffer)) + return self._buffer_size def _add_reader(self, fd, callback, *args): if not self.is_reading(): return - self._loop._add_reader(fd, callback, *args) + self._loop._add_reader(fd, callback, *args, context=self._context) + def _add_writer(self, fd, callback, *args): + self._loop._add_writer(fd, callback, *args, context=self._context) + + def _call_soon(self, callback, *args): + self._loop.call_soon(callback, *args, context=self._context) class _SelectorSocketTransport(_SelectorTransport): @@ -930,10 +943,9 @@ class _SelectorSocketTransport(_SelectorTransport): _sendfile_compatible = constants._SendfileMode.TRY_NATIVE def __init__(self, loop, sock, protocol, waiter=None, - extra=None, server=None): - + extra=None, server=None, context=None): self._read_ready_cb = None - super().__init__(loop, sock, protocol, extra, server) + super().__init__(loop, sock, protocol, extra, server, context) self._eof = False self._empty_waiter = None if _HAS_SENDMSG: @@ -945,14 +957,12 @@ def __init__(self, loop, sock, protocol, waiter=None, # decreases the latency (in some cases significantly.) base_events._set_nodelay(self._sock) - self._loop.call_soon(self._protocol.connection_made, self) + self._call_soon(self._protocol.connection_made, self) # only start reading when connection_made() has been called - self._loop.call_soon(self._add_reader, - self._sock_fd, self._read_ready) + self._call_soon(self._add_reader, self._sock_fd, self._read_ready) if waiter is not None: # only wake up the waiter when connection_made() has been called - self._loop.call_soon(futures._set_result_unless_cancelled, - waiter, None) + self._call_soon(futures._set_result_unless_cancelled, waiter, None) def set_protocol(self, protocol): if isinstance(protocol, protocols.BufferedProtocol): @@ -1050,8 +1060,8 @@ def _read_ready__on_eof(self): def write(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): - raise TypeError(f'data argument must be a bytes-like object, ' - f'not {type(data).__name__!r}') + raise TypeError(f'data argument must be a bytes, bytearray, or memoryview ' + f'object, not {type(data).__name__!r}') if self._eof: raise RuntimeError('Cannot call write() after write_eof()') if self._empty_waiter is not None: @@ -1081,10 +1091,11 @@ def write(self, data): if not data: return # Not all was written; register write handler. - self._loop._add_writer(self._sock_fd, self._write_ready) + self._add_writer(self._sock_fd, self._write_ready) # Add it to the buffer. self._buffer.append(data) + self._buffer_size += len(data) self._maybe_pause_protocol() def _get_sendmsg_buffer(self): @@ -1104,6 +1115,7 @@ def _write_sendmsg(self): except BaseException as exc: self._loop._remove_writer(self._sock_fd) self._buffer.clear() + self._buffer_size = 0 self._fatal_error(exc, 'Fatal write error on socket transport') if self._empty_waiter is not None: self._empty_waiter.set_exception(exc) @@ -1119,6 +1131,7 @@ def _write_sendmsg(self): self._sock.shutdown(socket.SHUT_WR) def _adjust_leftover_buffer(self, nbytes: int) -> None: + self._buffer_size -= nbytes buffer = self._buffer while nbytes: b = buffer.popleft() @@ -1139,13 +1152,16 @@ def _write_send(self): if n != len(buffer): # Not all data was written self._buffer.appendleft(buffer[n:]) + self._buffer_size -= n except (BlockingIOError, InterruptedError): - pass + self._buffer.appendleft(buffer) + return except (SystemExit, KeyboardInterrupt): raise except BaseException as exc: self._loop._remove_writer(self._sock_fd) self._buffer.clear() + self._buffer_size = 0 self._fatal_error(exc, 'Fatal write error on socket transport') if self._empty_waiter is not None: self._empty_waiter.set_exception(exc) @@ -1174,11 +1190,20 @@ def writelines(self, list_of_data): raise RuntimeError('unable to writelines; sendfile is in progress') if not list_of_data: return - self._buffer.extend([memoryview(data) for data in list_of_data]) + + if self._conn_lost: + if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES: + logger.warning('socket.send() raised exception.') + self._conn_lost += 1 + return + + for data in list_of_data: + self._buffer.append(memoryview(data)) + self._buffer_size += len(data) self._write_ready() # If the entire buffer couldn't be written, register a write handler if self._buffer: - self._loop._add_writer(self._sock_fd, self._write_ready) + self._add_writer(self._sock_fd, self._write_ready) self._maybe_pause_protocol() def can_write_eof(self): @@ -1211,21 +1236,19 @@ def close(self): class _SelectorDatagramTransport(_SelectorTransport, transports.DatagramTransport): - _buffer_factory = collections.deque + _header_size = 8 def __init__(self, loop, sock, protocol, address=None, waiter=None, extra=None): super().__init__(loop, sock, protocol, extra) self._address = address self._buffer_size = 0 - self._loop.call_soon(self._protocol.connection_made, self) + self._call_soon(self._protocol.connection_made, self) # only start reading when connection_made() has been called - self._loop.call_soon(self._add_reader, - self._sock_fd, self._read_ready) + self._call_soon(self._add_reader, self._sock_fd, self._read_ready) if waiter is not None: # only wake up the waiter when connection_made() has been called - self._loop.call_soon(futures._set_result_unless_cancelled, - waiter, None) + self._call_soon(futures._set_result_unless_cancelled, waiter, None) def get_write_buffer_size(self): return self._buffer_size @@ -1272,7 +1295,7 @@ def sendto(self, data, addr=None): self._sock.sendto(data, addr) return except (BlockingIOError, InterruptedError): - self._loop._add_writer(self._sock_fd, self._sendto_ready) + self._add_writer(self._sock_fd, self._sendto_ready) except OSError as exc: self._protocol.error_received(exc) return @@ -1285,13 +1308,13 @@ def sendto(self, data, addr=None): # Ensure that what we buffer is immutable. self._buffer.append((bytes(data), addr)) - self._buffer_size += len(data) + 8 # include header bytes + self._buffer_size += len(data) + self._header_size self._maybe_pause_protocol() def _sendto_ready(self): while self._buffer: data, addr = self._buffer.popleft() - self._buffer_size -= len(data) + self._buffer_size -= len(data) + self._header_size try: if self._extra['peername']: self._sock.send(data) @@ -1299,7 +1322,7 @@ def _sendto_ready(self): self._sock.sendto(data, addr) except (BlockingIOError, InterruptedError): self._buffer.appendleft((data, addr)) # Try again later. - self._buffer_size += len(data) + self._buffer_size += len(data) + self._header_size break except OSError as exc: self._protocol.error_received(exc) diff --git a/Lib/asyncio/staggered.py b/Lib/asyncio/staggered.py index 2ad65d8648e6c5..845aed4c6a3b35 100644 --- a/Lib/asyncio/staggered.py +++ b/Lib/asyncio/staggered.py @@ -62,7 +62,6 @@ async def staggered_race(coro_fns, delay, *, loop=None): coroutine's entry is ``None``. """ - # TODO: when we have aiter() and anext(), allow async iterables in coro_fns. loop = loop or events.get_running_loop() parent_task = tasks.current_task(loop) enum_coro_fns = enumerate(coro_fns) diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 64aac4cc50d15a..a28c11e928f806 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -214,7 +214,6 @@ def _stream_reader(self): return self._stream_reader_wr() def _replace_transport(self, transport): - loop = self._loop self._transport = transport self._over_ssl = transport.get_extra_info('sslcontext') is not None @@ -271,7 +270,6 @@ def connection_lost(self, exc): self._closed.set_exception(exc) super().connection_lost(exc) self._stream_reader_wr = None - self._stream_writer = None self._task = None self._transport = None @@ -541,17 +539,17 @@ async def _wait_for_data(self, func_name): self._waiter = None async def readline(self): - """Read chunk of data from the stream until newline (b'\n') is found. + r"""Read chunk of data from the stream until newline (b'\n') is found. - On success, return chunk that ends with newline. If only partial + On success, return chunk that ends with newline. If only partial line can be read due to EOF, return incomplete line without - terminating newline. When EOF was reached while no bytes read, empty - bytes object is returned. + terminating newline. When EOF was reached while no bytes read, + empty bytes object is returned. - If limit is reached, ValueError will be raised. In that case, if + If limit is reached, ValueError will be raised. In that case, if newline was found, complete line including newline will be removed - from internal buffer. Else, internal buffer will be cleared. Limit is - compared against part of the line without newline. + from internal buffer. Else, internal buffer will be cleared. + Limit is compared against part of the line without newline. If stream was paused, this function will automatically resume it if needed. @@ -669,8 +667,7 @@ async def readuntil(self, separator=b'\n'): # adds data which makes separator be found. That's why we check for # EOF *after* inspecting the buffer. if self._eof: - chunk = bytes(self._buffer) - self._buffer.clear() + chunk = self._buffer.take_bytes() raise exceptions.IncompleteReadError(chunk, None) # _wait_for_data() will resume reading if stream was paused. @@ -680,10 +677,9 @@ async def readuntil(self, separator=b'\n'): raise exceptions.LimitOverrunError( 'Separator is found, but chunk is longer than limit', match_start) - chunk = self._buffer[:match_end] - del self._buffer[:match_end] + chunk = self._buffer.take_bytes(match_end) self._maybe_resume_transport() - return bytes(chunk) + return chunk async def read(self, n=-1): """Read up to `n` bytes from the stream. @@ -718,20 +714,16 @@ async def read(self, n=-1): # collect everything in self._buffer, but that would # deadlock if the subprocess sends more than self.limit # bytes. So just call self.read(self._limit) until EOF. - blocks = [] - while True: - block = await self.read(self._limit) - if not block: - break - blocks.append(block) - return b''.join(blocks) + joined = bytearray() + while block := await self.read(self._limit): + joined += block + return joined.take_bytes() if not self._buffer and not self._eof: await self._wait_for_data('read') # This will work right even if buffer is less than n bytes - data = bytes(memoryview(self._buffer)[:n]) - del self._buffer[:n] + data = self._buffer.take_bytes(min(len(self._buffer), n)) self._maybe_resume_transport() return data @@ -762,18 +754,12 @@ async def readexactly(self, n): while len(self._buffer) < n: if self._eof: - incomplete = bytes(self._buffer) - self._buffer.clear() + incomplete = self._buffer.take_bytes() raise exceptions.IncompleteReadError(incomplete, n) await self._wait_for_data('readexactly') - if len(self._buffer) == n: - data = bytes(self._buffer) - self._buffer.clear() - else: - data = bytes(memoryview(self._buffer)[:n]) - del self._buffer[:n] + data = self._buffer.take_bytes(n) self._maybe_resume_transport() return data diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 00e8f6d5d1a68b..e1ec025791a52e 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -37,6 +37,7 @@ def __init__(self): self._errors = [] self._base_error = None self._on_completed_fut = None + self._cancel_on_enter = False def __repr__(self): info = [''] @@ -63,6 +64,8 @@ async def __aenter__(self): raise RuntimeError( f'TaskGroup {self!r} cannot determine the parent task') self._entered = True + if self._cancel_on_enter: + self.cancel() return self @@ -178,6 +181,9 @@ async def _aexit(self, et, exc): finally: exc = None + # Suppress any remaining exception (exceptions deserving to be raised + # were raised above). + return True def create_task(self, coro, **kwargs): """Create a new task in this group and return it. @@ -278,3 +284,30 @@ def _on_task_done(self, task): self._abort() self._parent_cancel_requested = True self._parent_task.cancel() + + def cancel(self): + """Cancel the task group + + `cancel()` will be called on any tasks in the group that aren't yet + done, as well as the parent (body) of the group. This will cause + the task group context manager to exit *without* + `asyncio.CancelledError` being raised. + + If `cancel()` is called before entering the task group, the group + will be cancelled upon entry. This is useful for patterns where + one piece of code passes an unused TaskGroup instance to another in + order to have the ability to cancel anything run within the group. + + `cancel()` is idempotent and may be called after the task group has + already exited. + """ + if not self._entered: + self._cancel_on_enter = True + return + if self._exiting and not self._tasks: + return + if not self._aborting: + self._abort() + if self._parent_task and not self._parent_cancel_requested: + self._parent_cancel_requested = True + self._parent_task.cancel() diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index fbd5c39a7c56ac..14d3c1ceb58cac 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -624,10 +624,11 @@ def as_completed(fs, *, timeout=None): Run the supplied awaitables concurrently. The returned object can be iterated to obtain the results of the awaitables as they finish. - The object returned can be iterated as an asynchronous iterator or a plain - iterator. When asynchronous iteration is used, the originally-supplied - awaitables are yielded if they are tasks or futures. This makes it easy to - correlate previously-scheduled tasks with their results: + The object returned can be iterated as an asynchronous iterator or + a plain iterator. When asynchronous iteration is used, the + originally-supplied awaitables are yielded if they are tasks or + futures. This makes it easy to correlate previously-scheduled tasks + with their results: ipv4_connect = create_task(open_connection("127.0.0.1", 80)) ipv6_connect = create_task(open_connection("::1", 80)) @@ -643,26 +644,27 @@ def as_completed(fs, *, timeout=None): else: print("IPv4 connection established.") - During asynchronous iteration, implicitly-created tasks will be yielded for - supplied awaitables that aren't tasks or futures. + During asynchronous iteration, implicitly-created tasks will be + yielded for supplied awaitables that aren't tasks or futures. - When used as a plain iterator, each iteration yields a new coroutine that - returns the result or raises the exception of the next completed awaitable. - This pattern is compatible with Python versions older than 3.13: + When used as a plain iterator, each iteration yields a new coroutine + that returns the result or raises the exception of the next completed + awaitable. This pattern is compatible with Python versions older than + 3.13: ipv4_connect = create_task(open_connection("127.0.0.1", 80)) ipv6_connect = create_task(open_connection("::1", 80)) tasks = [ipv4_connect, ipv6_connect] for next_connect in as_completed(tasks): - # next_connect is not one of the original task objects. It must be - # awaited to obtain the result value or raise the exception of the - # awaitable that finishes next. + # next_connect is not one of the original task objects. It must + # be awaited to obtain the result value or raise the exception + # of the awaitable that finishes next. reader, writer = await next_connect - A TimeoutError is raised if the timeout occurs before all awaitables are - done. This is raised by the async for loop during asynchronous iteration or - by the coroutines yielded during plain iteration. + A TimeoutError is raised if the timeout occurs before all awaitables + are done. This is raised by the async for loop during asynchronous + iteration or by the coroutines yielded during plain iteration. """ if inspect.isawaitable(fs): raise TypeError( @@ -1030,21 +1032,22 @@ def callback(): def create_eager_task_factory(custom_task_constructor): """Create a function suitable for use as a task factory on an event-loop. - Example usage: + Example usage: - loop.set_task_factory( - asyncio.create_eager_task_factory(my_task_constructor)) + loop.set_task_factory( + asyncio.create_eager_task_factory(my_task_constructor)) - Now, tasks created will be started immediately (rather than being first - scheduled to an event loop). The constructor argument can be any callable - that returns a Task-compatible object and has a signature compatible - with `Task.__init__`; it must have the `eager_start` keyword argument. + Now, tasks created will be started immediately (rather than being first + scheduled to an event loop). The constructor argument can be any + callable that returns a Task-compatible object and has a signature + compatible with `Task.__init__`; it must have the `eager_start` + keyword argument. - Most applications will use `Task` for `custom_task_constructor` and in - this case there's no need to call `create_eager_task_factory()` - directly. Instead the global `eager_task_factory` instance can be - used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`. - """ + Most applications will use `Task` for `custom_task_constructor` and in + this case there's no need to call `create_eager_task_factory()` + directly. Instead the global `eager_task_factory` instance can be + used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`. + """ def factory(loop, coro, *, eager_start=True, **kwargs): return custom_task_constructor( diff --git a/Lib/asyncio/threads.py b/Lib/asyncio/threads.py index db048a8231de16..5001351b0976ec 100644 --- a/Lib/asyncio/threads.py +++ b/Lib/asyncio/threads.py @@ -17,7 +17,8 @@ async def to_thread(func, /, *args, **kwargs): allowing context variables from the main thread to be accessed in the separate thread. - Return a coroutine that can be awaited to get the eventual result of *func*. + Return a coroutine that can be awaited to get the eventual result of + *func*. """ loop = events.get_running_loop() ctx = contextvars.copy_context() diff --git a/Lib/asyncio/timeouts.py b/Lib/asyncio/timeouts.py index 09342dc7c1310b..65ddc285abd971 100644 --- a/Lib/asyncio/timeouts.py +++ b/Lib/asyncio/timeouts.py @@ -25,7 +25,8 @@ class _State(enum.Enum): class Timeout: """Asynchronous context manager for cancelling overdue coroutines. - Use `timeout()` or `timeout_at()` rather than instantiating this class directly. + Use `timeout()` or `timeout_at()` rather than instantiating this class + directly. """ def __init__(self, when: float | None) -> None: diff --git a/Lib/asyncio/tools.py b/Lib/asyncio/tools.py index 2683f34cc7113b..2ac1738d15c6c7 100644 --- a/Lib/asyncio/tools.py +++ b/Lib/asyncio/tools.py @@ -1,6 +1,6 @@ """Tools to analyze tasks running in asyncio programs.""" -from collections import defaultdict, namedtuple +from collections import defaultdict from itertools import count from enum import Enum import sys @@ -27,10 +27,10 @@ def __init__( # ─── indexing helpers ─────────────────────────────────────────── def _format_stack_entry(elem: str|FrameInfo) -> str: if not isinstance(elem, str): - if elem.lineno == 0 and elem.filename == "": + if elem.location.lineno == 0 and elem.filename == "": return f"{elem.funcname}" else: - return f"{elem.funcname} {elem.filename}:{elem.lineno}" + return f"{elem.funcname} {elem.filename}:{elem.location.lineno}" return elem @@ -222,20 +222,47 @@ def _print_cycle_exception(exception: CycleFoundException): print(f"cycle: {inames}", file=sys.stderr) -def _get_awaited_by_tasks(pid: int) -> list: - try: - return get_all_awaited_by(pid) - except RuntimeError as e: - while e.__context__ is not None: - e = e.__context__ - print(f"Error retrieving tasks: {e}") - sys.exit(1) +def exit_with_permission_help_text(): + """ + Prints a message pointing to platform-specific permission help text and exits the program. + This function is called when a PermissionError is encountered while trying + to attach to a process. + """ + print( + "Error: The specified process cannot be attached to due to insufficient permissions.\n" + "See the Python documentation for details on required privileges and troubleshooting:\n" + "https://docs.python.org/3/howto/remote_debugging.html#permission-requirements\n", + file=sys.stderr, + ) + sys.exit(1) + + +_TRANSIENT_ERRORS = (RuntimeError, OSError, UnicodeDecodeError, MemoryError) + + +def _get_awaited_by_tasks(pid: int, retries: int = 3) -> list: + for attempt in range(retries + 1): + try: + return get_all_awaited_by(pid) + except PermissionError: + exit_with_permission_help_text() + except ProcessLookupError: + print(f"Error: process {pid} not found.", file=sys.stderr) + sys.exit(1) + except _TRANSIENT_ERRORS as e: + if attempt < retries: + continue + if isinstance(e, RuntimeError): + while e.__context__ is not None: + e = e.__context__ + print(f"Error retrieving tasks: {e}", file=sys.stderr) + sys.exit(1) -def display_awaited_by_tasks_table(pid: int) -> None: +def display_awaited_by_tasks_table(pid: int, retries: int = 3) -> None: """Build and print a table of all pending tasks under `pid`.""" - tasks = _get_awaited_by_tasks(pid) + tasks = _get_awaited_by_tasks(pid, retries=retries) table = build_task_table(tasks) # Print the table in a simple tabular format print( @@ -246,10 +273,10 @@ def display_awaited_by_tasks_table(pid: int) -> None: print(f"{row[0]:<10} {row[1]:<20} {row[2]:<20} {row[3]:<50} {row[4]:<50} {row[5]:<15} {row[6]:<15}") -def display_awaited_by_tasks_tree(pid: int) -> None: +def display_awaited_by_tasks_tree(pid: int, retries: int = 3) -> None: """Build and print a tree of all pending tasks under `pid`.""" - tasks = _get_awaited_by_tasks(pid) + tasks = _get_awaited_by_tasks(pid, retries=retries) try: result = build_async_tree(tasks) except CycleFoundException as e: diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 1c1458127db5ac..676e0b670faa49 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -12,6 +12,7 @@ import sys import threading import warnings +import inspect from . import base_events from . import base_subprocess @@ -54,7 +55,8 @@ def waitstatus_to_exitcode(status): class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop): """Unix event loop. - Adds signal handling and UNIX Domain Socket support to SelectorEventLoop. + Adds signal handling and UNIX Domain Socket support to + SelectorEventLoop. """ def __init__(self, selector=None): @@ -94,7 +96,7 @@ def add_signal_handler(self, sig, callback, *args): Raise RuntimeError if there is a problem setting up the handler. """ if (coroutines.iscoroutine(callback) or - coroutines._iscoroutinefunction(callback)): + inspect.iscoroutinefunction(callback)): raise TypeError("coroutines cannot be used " "with add_signal_handler()") self._check_signal(sig) @@ -359,7 +361,7 @@ async def _sock_sendfile_native(self, sock, file, offset, count): "os.sendfile() is not available") try: fileno = file.fileno() - except (AttributeError, io.UnsupportedOperation) as err: + except (AttributeError, io.UnsupportedOperation): raise exceptions.SendfileNotAvailableError("not a regular file") try: fsize = os.fstat(fileno).st_size @@ -384,12 +386,12 @@ def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno, # order to simplify the common case. self.remove_writer(registered_fd) if fut.cancelled(): - self._sock_sendfile_update_filepos(fileno, offset, total_sent) + self._sock_sendfile_update_filepos(fileno, offset) return if count: blocksize = count - total_sent if blocksize <= 0: - self._sock_sendfile_update_filepos(fileno, offset, total_sent) + self._sock_sendfile_update_filepos(fileno, offset) fut.set_result(total_sent) return @@ -423,20 +425,20 @@ def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno, # plain send(). err = exceptions.SendfileNotAvailableError( "os.sendfile call failed") - self._sock_sendfile_update_filepos(fileno, offset, total_sent) + self._sock_sendfile_update_filepos(fileno, offset) fut.set_exception(err) else: - self._sock_sendfile_update_filepos(fileno, offset, total_sent) + self._sock_sendfile_update_filepos(fileno, offset) fut.set_exception(exc) except (SystemExit, KeyboardInterrupt): raise except BaseException as exc: - self._sock_sendfile_update_filepos(fileno, offset, total_sent) + self._sock_sendfile_update_filepos(fileno, offset) fut.set_exception(exc) else: if sent == 0: # EOF - self._sock_sendfile_update_filepos(fileno, offset, total_sent) + self._sock_sendfile_update_filepos(fileno, offset) fut.set_result(total_sent) else: offset += sent @@ -447,9 +449,9 @@ def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno, fd, sock, fileno, offset, count, blocksize, total_sent) - def _sock_sendfile_update_filepos(self, fileno, offset, total_sent): - if total_sent > 0: - os.lseek(fileno, offset, os.SEEK_SET) + def _sock_sendfile_update_filepos(self, fileno, offset): + # After this helper runs, the source fd's lseek pointer is at offset." + os.lseek(fileno, offset, os.SEEK_SET) def _sock_add_cancellation_callback(self, fut, sock): def cb(fut): @@ -834,7 +836,8 @@ class _UnixSubprocessTransport(base_subprocess.BaseSubprocessTransport): def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs): stdin_w = None - if stdin == subprocess.PIPE and sys.platform.startswith('aix'): + if (stdin == subprocess.PIPE + and (sys.platform.startswith('aix') or sys.platform == 'cygwin')): # Use a socket pair for stdin on AIX, since it does not # support selecting read events on the write end of a # socket (which we use in order to detect closing of the @@ -886,8 +889,8 @@ def _do_wait(self, pid, pidfd, callback, args): pid) else: returncode = waitstatus_to_exitcode(status) - - os.close(pidfd) + finally: + os.close(pidfd) callback(pid, returncode, *args) class _ThreadedChildWatcher: diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py index 5f75b17d8ca649..0bf7732136f1f8 100644 --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -610,6 +610,9 @@ def sendfile(self, sock, file, offset, count): ov = _overlapped.Overlapped(NULL) offset_low = offset & 0xffff_ffff offset_high = (offset >> 32) & 0xffff_ffff + # TransmitFile ignores OVERLAPPED.Offset for handles not opened with + # FILE_FLAG_OVERLAPPED, so seek the CRT file pointer to match. + file.seek(offset) ov.TransmitFile(sock.fileno(), msvcrt.get_osfhandle(file.fileno()), offset_low, offset_high, diff --git a/Lib/asyncio/windows_utils.py b/Lib/asyncio/windows_utils.py index ef277fac3e291c..d6393f0b1ffee5 100644 --- a/Lib/asyncio/windows_utils.py +++ b/Lib/asyncio/windows_utils.py @@ -10,7 +10,6 @@ import msvcrt import os import subprocess -import tempfile import warnings @@ -24,6 +23,7 @@ PIPE = subprocess.PIPE STDOUT = subprocess.STDOUT _mmap_counter = itertools.count() +_MAX_PIPE_ATTEMPTS = 20 # Replacement for os.pipe() using handles instead of fds @@ -31,10 +31,6 @@ def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE): """Like os.pipe() but with overlapped support and using handles not fds.""" - address = tempfile.mktemp( - prefix=r'\\.\pipe\python-pipe-{:d}-{:d}-'.format( - os.getpid(), next(_mmap_counter))) - if duplex: openmode = _winapi.PIPE_ACCESS_DUPLEX access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE @@ -56,9 +52,20 @@ def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE): h1 = h2 = None try: - h1 = _winapi.CreateNamedPipe( - address, openmode, _winapi.PIPE_WAIT, - 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL) + for attempts in itertools.count(): + address = r'\\.\pipe\python-pipe-{:d}-{:d}-{}'.format( + os.getpid(), next(_mmap_counter), os.urandom(8).hex()) + try: + h1 = _winapi.CreateNamedPipe( + address, openmode, _winapi.PIPE_WAIT, + 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL) + break + except OSError as e: + if attempts >= _MAX_PIPE_ATTEMPTS: + raise + if e.winerror not in (_winapi.ERROR_PIPE_BUSY, + _winapi.ERROR_ACCESS_DENIED): + raise h2 = _winapi.CreateFile( address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING, @@ -104,8 +111,9 @@ def fileno(self): def close(self, *, CloseHandle=_winapi.CloseHandle): if self._handle is not None: - CloseHandle(self._handle) + handle = self._handle self._handle = None + CloseHandle(handle) def __del__(self, _warn=warnings.warn): if self._handle is not None: diff --git a/Lib/base64.py b/Lib/base64.py index 5d78cc09f40cd3..fa562f74a81034 100644 --- a/Lib/base64.py +++ b/Lib/base64.py @@ -1,10 +1,9 @@ -"""Base16, Base32, Base64 (RFC 3548), Base85 and Ascii85 data encodings""" +"""Base16, Base32, Base64 (RFC 4648), Base85 and Ascii85 data encodings""" # Modified 04-Oct-1995 by Jack Jansen to use binascii module # Modified 30-Dec-2003 by Barry Warsaw to add full RFC 3548 support # Modified 22-May-2007 by Guido van Rossum to use bytes everywhere -import struct import binascii @@ -26,6 +25,8 @@ ] +_NOT_SPECIFIED = ['NOT SPECIFIED'] + bytes_types = (bytes, bytearray) # Types acceptable as binary data def _bytes_from_decode_data(s): @@ -45,44 +46,90 @@ def _bytes_from_decode_data(s): # Base64 encoding/decoding uses binascii -def b64encode(s, altchars=None): +def b64encode(s, altchars=None, *, padded=True, wrapcol=0): """Encode the bytes-like object s using Base64 and return a bytes object. Optional altchars should be a byte string of length 2 which specifies an alternative alphabet for the '+' and '/' characters. This allows an application to e.g. generate url or filesystem safe Base64 strings. + + If padded is false, omit padding in the output. + + If wrapcol is non-zero, insert a newline (b'\\n') character after at most + every wrapcol characters. """ - encoded = binascii.b2a_base64(s, newline=False) if altchars is not None: - assert len(altchars) == 2, repr(altchars) - return encoded.translate(bytes.maketrans(b'+/', altchars)) - return encoded + if len(altchars) != 2: + raise ValueError(f'invalid altchars: {altchars!r}') + alphabet = binascii.BASE64_ALPHABET[:-2] + altchars + return binascii.b2a_base64(s, padded=padded, wrapcol=wrapcol, newline=False, + alphabet=alphabet) + return binascii.b2a_base64(s, padded=padded, wrapcol=wrapcol, newline=False) -def b64decode(s, altchars=None, validate=False): +def b64decode(s, altchars=None, validate=_NOT_SPECIFIED, + *, padded=True, ignorechars=_NOT_SPECIFIED, canonical=False): """Decode the Base64 encoded bytes-like object or ASCII string s. Optional altchars must be a bytes-like object or ASCII string of length 2 which specifies the alternative alphabet used instead of the '+' and '/' characters. + If padded is false, padding in input is not required. + The result is returned as a bytes object. A binascii.Error is raised if s is incorrectly padded. - If validate is False (the default), characters that are neither in the - normal base-64 alphabet nor the alternative alphabet are discarded prior - to the padding check. If validate is True, these non-alphabet characters - in the input result in a binascii.Error. + If ignorechars is specified, it should be a byte string containing + characters to ignore from the input. The default value of validate is + True if ignorechars is specified, False otherwise. + + If validate is false, characters that are neither in the normal base-64 + alphabet nor the alternative alphabet are discarded prior to the + padding check. If validate is true, these non-alphabet characters in + the input result in a binascii.Error if they are not in ignorechars. For more information about the strict base64 check, see: https://docs.python.org/3.11/library/binascii.html#binascii.a2b_base64 """ s = _bytes_from_decode_data(s) + if validate is _NOT_SPECIFIED: + validate = ignorechars is not _NOT_SPECIFIED + badchar = None if altchars is not None: altchars = _bytes_from_decode_data(altchars) - assert len(altchars) == 2, repr(altchars) - s = s.translate(bytes.maketrans(altchars, b'+/')) - return binascii.a2b_base64(s, strict_mode=validate) + if len(altchars) != 2: + raise ValueError(f'invalid altchars: {altchars!r}') + if ignorechars is _NOT_SPECIFIED: + for b in b'+/': + if b not in altchars and b in s: + badchar = b + break + s = s.translate(bytes.maketrans(altchars, b'+/')) + else: + alphabet = binascii.BASE64_ALPHABET[:-2] + altchars + return binascii.a2b_base64(s, strict_mode=validate, + alphabet=alphabet, + padded=padded, ignorechars=ignorechars, + canonical=canonical) + if ignorechars is _NOT_SPECIFIED: + ignorechars = b'' + result = binascii.a2b_base64(s, strict_mode=validate, + padded=padded, ignorechars=ignorechars, + canonical=canonical) + if badchar is not None: + import warnings + if validate: + warnings.warn(f'invalid character {chr(badchar)!a} in Base64 data ' + f'with altchars={altchars!r} and validate=True ' + f'will be an error in future Python versions', + DeprecationWarning, stacklevel=2) + else: + warnings.warn(f'invalid character {chr(badchar)!a} in Base64 data ' + f'with altchars={altchars!r} and validate=False ' + f'will be discarded in future Python versions', + FutureWarning, stacklevel=2) + return result def standard_b64encode(s): @@ -103,19 +150,21 @@ def standard_b64decode(s): return b64decode(s) -_urlsafe_encode_translation = bytes.maketrans(b'+/', b'-_') _urlsafe_decode_translation = bytes.maketrans(b'-_', b'+/') -def urlsafe_b64encode(s): +def urlsafe_b64encode(s, *, padded=True): """Encode bytes using the URL- and filesystem-safe Base64 alphabet. Argument s is a bytes-like object to encode. The result is returned as a bytes object. The alphabet uses '-' instead of '+' and '_' instead of '/'. + + If padded is false, omit padding in the output. """ - return b64encode(s).translate(_urlsafe_encode_translation) + return binascii.b2a_base64(s, padded=padded, newline=False, + alphabet=binascii.URLSAFE_BASE64_ALPHABET) -def urlsafe_b64decode(s): +def urlsafe_b64decode(s, *, padded=False): """Decode bytes using the URL- and filesystem-safe Base64 alphabet. Argument s is a bytes-like object or ASCII string to decode. The result @@ -124,30 +173,53 @@ def urlsafe_b64decode(s): alphabet, and are not a plus '+' or slash '/', are discarded prior to the padding check. + If padded is false, padding in input is not required. + The alphabet uses '-' instead of '+' and '_' instead of '/'. """ s = _bytes_from_decode_data(s) + badchar = None + for b in b'+/': + if b in s: + badchar = b + break s = s.translate(_urlsafe_decode_translation) - return b64decode(s) + result = binascii.a2b_base64(s, strict_mode=False, padded=padded) + if badchar is not None: + import warnings + warnings.warn(f'invalid character {chr(badchar)!a} in URL-safe Base64 data ' + f'will be discarded in future Python versions', + FutureWarning, stacklevel=2) + return result # Base32 encoding/decoding must be done in Python _B32_ENCODE_DOCSTRING = ''' Encode the bytes-like objects using {encoding} and return a bytes object. + +If padded is false, omit padding in the output. + +If wrapcol is non-zero, insert a newline (b'\\n') character after at most +every wrapcol characters. ''' _B32_DECODE_DOCSTRING = ''' Decode the {encoding} encoded bytes-like object or ASCII string s. Optional casefold is a flag specifying whether a lowercase alphabet is acceptable as input. For security purposes, the default is False. + +If padded is false, padding in input is not required. + +ignorechars should be a byte string containing characters to ignore +from the input. {extra_args} The result is returned as a bytes object. A binascii.Error is raised if the input is incorrectly padded or if there are non-alphabet characters present in the input. ''' _B32_DECODE_MAP01_DOCSTRING = ''' -RFC 3548 allows for optional mapping of the digit 0 (zero) to the +RFC 4648 allows for optional mapping of the digit 0 (zero) to the letter O (oh), and for optional mapping of the digit 1 (one) to either the letter I (eye) or letter L (el). The optional argument map01 when not None, specifies which letter the digit 1 should be @@ -155,371 +227,183 @@ def urlsafe_b64decode(s): the letter O). For security purposes the default is None, so that 0 and 1 are not allowed in the input. ''' -_b32alphabet = b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' -_b32hexalphabet = b'0123456789ABCDEFGHIJKLMNOPQRSTUV' -_b32tab2 = {} -_b32rev = {} - -def _b32encode(alphabet, s): - # Delay the initialization of the table to not waste memory - # if the function is never called - if alphabet not in _b32tab2: - b32tab = [bytes((i,)) for i in alphabet] - _b32tab2[alphabet] = [a + b for a in b32tab for b in b32tab] - b32tab = None - - if not isinstance(s, bytes_types): - s = memoryview(s).tobytes() - leftover = len(s) % 5 - # Pad the last quantum with zero bits if necessary - if leftover: - s = s + b'\0' * (5 - leftover) # Don't use += ! - encoded = bytearray() - from_bytes = int.from_bytes - b32tab2 = _b32tab2[alphabet] - for i in range(0, len(s), 5): - c = from_bytes(s[i: i + 5]) # big endian - encoded += (b32tab2[c >> 30] + # bits 1 - 10 - b32tab2[(c >> 20) & 0x3ff] + # bits 11 - 20 - b32tab2[(c >> 10) & 0x3ff] + # bits 21 - 30 - b32tab2[c & 0x3ff] # bits 31 - 40 - ) - # Adjust for any leftover partial quanta - if leftover == 1: - encoded[-6:] = b'======' - elif leftover == 2: - encoded[-4:] = b'====' - elif leftover == 3: - encoded[-3:] = b'===' - elif leftover == 4: - encoded[-1:] = b'=' - return bytes(encoded) - -def _b32decode(alphabet, s, casefold=False, map01=None): - # Delay the initialization of the table to not waste memory - # if the function is never called - if alphabet not in _b32rev: - _b32rev[alphabet] = {v: k for k, v in enumerate(alphabet)} + +def b32encode(s, *, padded=True, wrapcol=0): + return binascii.b2a_base32(s, padded=padded, wrapcol=wrapcol) +b32encode.__doc__ = _B32_ENCODE_DOCSTRING.format(encoding='base32') + +def b32decode(s, casefold=False, map01=None, *, padded=True, ignorechars=b'', + canonical=False): s = _bytes_from_decode_data(s) - if len(s) % 8: - raise binascii.Error('Incorrect padding') # Handle section 2.4 zero and one mapping. The flag map01 will be either # False, or the character to map the digit 1 (one) to. It should be # either L (el) or I (eye). if map01 is not None: map01 = _bytes_from_decode_data(map01) - assert len(map01) == 1, repr(map01) s = s.translate(bytes.maketrans(b'01', b'O' + map01)) if casefold: s = s.upper() - # Strip off pad characters from the right. We need to count the pad - # characters because this will tell us how many null bytes to remove from - # the end of the decoded string. - l = len(s) - s = s.rstrip(b'=') - padchars = l - len(s) - # Now decode the full quanta - decoded = bytearray() - b32rev = _b32rev[alphabet] - for i in range(0, len(s), 8): - quanta = s[i: i + 8] - acc = 0 - try: - for c in quanta: - acc = (acc << 5) + b32rev[c] - except KeyError: - raise binascii.Error('Non-base32 digit found') from None - decoded += acc.to_bytes(5) # big endian - # Process the last, partial quanta - if l % 8 or padchars not in {0, 1, 3, 4, 6}: - raise binascii.Error('Incorrect padding') - if padchars and decoded: - acc <<= 5 * padchars - last = acc.to_bytes(5) # big endian - leftover = (43 - 5 * padchars) // 8 # 1: 4, 3: 3, 4: 2, 6: 1 - decoded[-5:] = last[:leftover] - return bytes(decoded) - - -def b32encode(s): - return _b32encode(_b32alphabet, s) -b32encode.__doc__ = _B32_ENCODE_DOCSTRING.format(encoding='base32') - -def b32decode(s, casefold=False, map01=None): - return _b32decode(_b32alphabet, s, casefold, map01) + return binascii.a2b_base32(s, padded=padded, ignorechars=ignorechars, + canonical=canonical) b32decode.__doc__ = _B32_DECODE_DOCSTRING.format(encoding='base32', extra_args=_B32_DECODE_MAP01_DOCSTRING) -def b32hexencode(s): - return _b32encode(_b32hexalphabet, s) +def b32hexencode(s, *, padded=True, wrapcol=0): + return binascii.b2a_base32(s, padded=padded, wrapcol=wrapcol, + alphabet=binascii.BASE32HEX_ALPHABET) b32hexencode.__doc__ = _B32_ENCODE_DOCSTRING.format(encoding='base32hex') -def b32hexdecode(s, casefold=False): +def b32hexdecode(s, casefold=False, *, padded=True, ignorechars=b'', + canonical=False): + s = _bytes_from_decode_data(s) # base32hex does not have the 01 mapping - return _b32decode(_b32hexalphabet, s, casefold) + if casefold: + s = s.upper() + return binascii.a2b_base32(s, alphabet=binascii.BASE32HEX_ALPHABET, + padded=padded, ignorechars=ignorechars, + canonical=canonical) b32hexdecode.__doc__ = _B32_DECODE_DOCSTRING.format(encoding='base32hex', extra_args='') -# RFC 3548, Base 16 Alphabet specifies uppercase, but hexlify() returns +# RFC 4648, Base 16 Alphabet specifies uppercase, but hexlify() returns # lowercase. The RFC also recommends against accepting input case # insensitively. -def b16encode(s): +def b16encode(s, *, wrapcol=0): """Encode the bytes-like object s using Base16 and return a bytes object. + + If wrapcol is non-zero, insert a newline (b'\\n') character after at most + every wrapcol characters. """ - return binascii.hexlify(s).upper() + if not wrapcol: + return binascii.hexlify(s).upper() + if wrapcol < 0: + raise ValueError('Negative wrapcol') + if wrapcol < 2: + wrapcol = 2 + return binascii.hexlify(s, bytes_per_sep=-(wrapcol//2), sep=b'\n').upper() -def b16decode(s, casefold=False): +def b16decode(s, casefold=False, *, ignorechars=b''): """Decode the Base16 encoded bytes-like object or ASCII string s. Optional casefold is a flag specifying whether a lowercase alphabet is acceptable as input. For security purposes, the default is False. + ignorechars should be a byte string containing characters to ignore + from the input. + The result is returned as a bytes object. A binascii.Error is raised if s is incorrectly padded or if there are non-alphabet characters present in the input. """ - s = _bytes_from_decode_data(s) - if casefold: - s = s.upper() - if s.translate(None, delete=b'0123456789ABCDEF'): - raise binascii.Error('Non-base16 digit found') - return binascii.unhexlify(s) + if not casefold: + s = _bytes_from_decode_data(s) + if not isinstance(ignorechars, bytes): + ignorechars = bytes(memoryview(ignorechars)) + for b in b'abcdef': + if b in s and b not in ignorechars: + raise binascii.Error('Non-base16 digit found') + s = s.translate(None, delete=b'abcdef') + return binascii.unhexlify(s, ignorechars=ignorechars) # # Ascii85 encoding/decoding # - -_a85chars = None -_a85chars2 = None -_A85START = b"<~" -_A85END = b"~>" - -def _85encode(b, chars, chars2, pad=False, foldnuls=False, foldspaces=False): - # Helper function for a85encode and b85encode - if not isinstance(b, bytes_types): - b = memoryview(b).tobytes() - - padding = (-len(b)) % 4 - if padding: - b = b + b'\0' * padding - words = struct.Struct('!%dI' % (len(b) // 4)).unpack(b) - - chunks = [b'z' if foldnuls and not word else - b'y' if foldspaces and word == 0x20202020 else - (chars2[word // 614125] + - chars2[word // 85 % 7225] + - chars[word % 85]) - for word in words] - - if padding and not pad: - if chunks[-1] == b'z': - chunks[-1] = chars[0] * 5 - chunks[-1] = chunks[-1][:-padding] - - return b''.join(chunks) - def a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False): """Encode bytes-like object b using Ascii85 and return a bytes object. foldspaces is an optional flag that uses the special short sequence 'y' instead of 4 consecutive spaces (ASCII 0x20) as supported by 'btoa'. This - feature is not supported by the "standard" Adobe encoding. + feature is not supported by the standard encoding used in PDF. - wrapcol controls whether the output should have newline (b'\\n') characters - added to it. If this is non-zero, each output line will be at most this - many characters long, excluding the trailing newline. + If wrapcol is non-zero, insert a newline (b'\\n') character after at most + every wrapcol characters. - pad controls whether the input is padded to a multiple of 4 before - encoding. Note that the btoa implementation always pads. + pad controls whether zero-padding applied to the end of the input + is fully retained in the output encoding, as done by btoa, + producing an exact multiple of 5 bytes of output. - adobe controls whether the encoded byte sequence is framed with <~ and ~>, - which is used by the Adobe implementation. - """ - global _a85chars, _a85chars2 - # Delay the initialization of tables to not waste memory - # if the function is never called - if _a85chars2 is None: - _a85chars = [bytes((i,)) for i in range(33, 118)] - _a85chars2 = [(a + b) for a in _a85chars for b in _a85chars] - - result = _85encode(b, _a85chars, _a85chars2, pad, True, foldspaces) - - if adobe: - result = _A85START + result - if wrapcol: - wrapcol = max(2 if adobe else 1, wrapcol) - chunks = [result[i: i + wrapcol] - for i in range(0, len(result), wrapcol)] - if adobe: - if len(chunks[-1]) + 2 > wrapcol: - chunks.append(b'') - result = b'\n'.join(chunks) - if adobe: - result += _A85END + adobe controls whether the encoded byte sequence is framed with <~ + and ~>, as in a PostScript base-85 string literal. Note that + while ASCII85Decode streams in PDF documents must be terminated + with ~>, they must not use a leading <~. - return result + """ + return binascii.b2a_ascii85(b, foldspaces=foldspaces, + adobe=adobe, wrapcol=wrapcol, pad=pad) -def a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v'): +def a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v', + canonical=False): """Decode the Ascii85 encoded bytes-like object or ASCII string b. - foldspaces is a flag that specifies whether the 'y' short sequence should be - accepted as shorthand for 4 consecutive spaces (ASCII 0x20). This feature is - not supported by the "standard" Adobe encoding. + foldspaces is a flag that specifies whether the 'y' short sequence + should be accepted as shorthand for 4 consecutive spaces (ASCII + 0x20). This feature is not supported by the standard Ascii85 + encoding used in PDF and PostScript. - adobe controls whether the input sequence is in Adobe Ascii85 format (i.e. - is framed with <~ and ~>). + adobe controls whether the <~ and ~> markers are present. While + the leading <~ is not required, the input must end with ~>, or a + ValueError is raised. ignorechars should be a byte string containing characters to ignore from the input. This should only contain whitespace characters, and by default contains all whitespace characters in ASCII. + If canonical is true, non-canonical encodings are rejected. + The result is returned as a bytes object. """ - b = _bytes_from_decode_data(b) - if adobe: - if not b.endswith(_A85END): - raise ValueError( - "Ascii85 encoded byte sequences must end " - "with {!r}".format(_A85END) - ) - if b.startswith(_A85START): - b = b[2:-2] # Strip off start/end markers - else: - b = b[:-2] - # - # We have to go through this stepwise, so as to ignore spaces and handle - # special short sequences - # - packI = struct.Struct('!I').pack - decoded = [] - decoded_append = decoded.append - curr = [] - curr_append = curr.append - curr_clear = curr.clear - for x in b + b'u' * 4: - if b'!'[0] <= x <= b'u'[0]: - curr_append(x) - if len(curr) == 5: - acc = 0 - for x in curr: - acc = 85 * acc + (x - 33) - try: - decoded_append(packI(acc)) - except struct.error: - raise ValueError('Ascii85 overflow') from None - curr_clear() - elif x == b'z'[0]: - if curr: - raise ValueError('z inside Ascii85 5-tuple') - decoded_append(b'\0\0\0\0') - elif foldspaces and x == b'y'[0]: - if curr: - raise ValueError('y inside Ascii85 5-tuple') - decoded_append(b'\x20\x20\x20\x20') - elif x in ignorechars: - # Skip whitespace - continue - else: - raise ValueError('Non-Ascii85 digit found: %c' % x) - - result = b''.join(decoded) - padding = 4 - len(curr) - if padding: - # Throw away the extra padding - result = result[:-padding] - return result - -# The following code is originally taken (with permission) from Mercurial + return binascii.a2b_ascii85(b, foldspaces=foldspaces, + adobe=adobe, ignorechars=ignorechars, + canonical=canonical) -_b85alphabet = (b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - b"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~") -_b85chars = None -_b85chars2 = None -_b85dec = None - -def b85encode(b, pad=False): +def b85encode(b, pad=False, *, wrapcol=0): """Encode bytes-like object b in base85 format and return a bytes object. - If pad is true, the input is padded with b'\\0' so its length is a multiple of - 4 bytes before encoding. + If wrapcol is non-zero, insert a newline (b'\\n') character after at most + every wrapcol characters. + + The input is padded with b'\0' so its length is a multiple of 4 + bytes before encoding. If pad is true, all the resulting + characters are retained in the output, which will always be a + multiple of 5 bytes. """ - global _b85chars, _b85chars2 - # Delay the initialization of tables to not waste memory - # if the function is never called - if _b85chars2 is None: - _b85chars = [bytes((i,)) for i in _b85alphabet] - _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars] - return _85encode(b, _b85chars, _b85chars2, pad) - -def b85decode(b): + return binascii.b2a_base85(b, wrapcol=wrapcol, pad=pad) + +def b85decode(b, *, ignorechars=b'', canonical=False): """Decode the base85-encoded bytes-like object or ASCII string b + If canonical is true, non-canonical encodings are rejected. + The result is returned as a bytes object. """ - global _b85dec - # Delay the initialization of tables to not waste memory - # if the function is never called - if _b85dec is None: - _b85dec = [None] * 256 - for i, c in enumerate(_b85alphabet): - _b85dec[c] = i - - b = _bytes_from_decode_data(b) - padding = (-len(b)) % 5 - b = b + b'~' * padding - out = [] - packI = struct.Struct('!I').pack - for i in range(0, len(b), 5): - chunk = b[i:i + 5] - acc = 0 - try: - for c in chunk: - acc = acc * 85 + _b85dec[c] - except TypeError: - for j, c in enumerate(chunk): - if _b85dec[c] is None: - raise ValueError('bad base85 character at position %d' - % (i + j)) from None - raise - try: - out.append(packI(acc)) - except struct.error: - raise ValueError('base85 overflow in hunk starting at byte %d' - % i) from None - - result = b''.join(out) - if padding: - result = result[:-padding] - return result + return binascii.a2b_base85(b, ignorechars=ignorechars, + canonical=canonical) + +def z85encode(s, pad=False, *, wrapcol=0): + """Encode bytes-like object b in z85 format and return a bytes object. -_z85alphabet = (b'0123456789abcdefghijklmnopqrstuvwxyz' - b'ABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#') -# Translating b85 valid but z85 invalid chars to b'\x00' is required -# to prevent them from being decoded as b85 valid chars. -_z85_b85_decode_diff = b';_`|~' -_z85_decode_translation = bytes.maketrans( - _z85alphabet + _z85_b85_decode_diff, - _b85alphabet + b'\x00' * len(_z85_b85_decode_diff) -) -_z85_encode_translation = bytes.maketrans(_b85alphabet, _z85alphabet) - -def z85encode(s): - """Encode bytes-like object b in z85 format and return a bytes object.""" - return b85encode(s).translate(_z85_encode_translation) - -def z85decode(s): + If wrapcol is non-zero, insert a newline (b'\\n') character after at most + every wrapcol characters. + + The input is padded with b'\0' so its length is a multiple of + bytes before encoding. If pad is true, all the resulting + characters are retained in the output, which will always be a + multiple of 5 bytes, as required by the ZeroMQ standard. + """ + return binascii.b2a_base85(s, wrapcol=wrapcol, pad=pad, + alphabet=binascii.Z85_ALPHABET) + +def z85decode(s, *, ignorechars=b'', canonical=False): """Decode the z85-encoded bytes-like object or ASCII string b + If canonical is true, non-canonical encodings are rejected. + The result is returned as a bytes object. """ - s = _bytes_from_decode_data(s) - s = s.translate(_z85_decode_translation) - try: - return b85decode(s) - except ValueError as e: - raise ValueError(e.args[0].replace('base85', 'z85')) from None + return binascii.a2b_base85(s, alphabet=binascii.Z85_ALPHABET, + ignorechars=ignorechars, canonical=canonical) # Legacy interface. This code could be cleaned up since I don't believe # binascii has any line length limitations. It just doesn't seem worth it @@ -563,11 +447,10 @@ def encodebytes(s): """Encode a bytestring into a bytes object containing multiple lines of base-64 data.""" _input_type_check(s) - pieces = [] - for i in range(0, len(s), MAXBINSIZE): - chunk = s[i : i + MAXBINSIZE] - pieces.append(binascii.b2a_base64(chunk)) - return b"".join(pieces) + result = binascii.b2a_base64(s, wrapcol=MAXLINESIZE) + if result == b'\n': + return b'' + return result def decodebytes(s): @@ -601,7 +484,14 @@ def main(): with open(args[0], 'rb') as f: func(f, sys.stdout.buffer) else: - func(sys.stdin.buffer, sys.stdout.buffer) + if sys.stdin.isatty(): + # gh-138775: read terminal input data all at once to detect EOF + import io + data = sys.stdin.buffer.read() + buffer = io.BytesIO(data) + else: + buffer = sys.stdin.buffer + func(buffer, sys.stdout.buffer) if __name__ == '__main__': diff --git a/Lib/bdb.py b/Lib/bdb.py index efc3e0a235ac8e..50cf2b3f5b3e45 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -199,6 +199,8 @@ def __init__(self, skip=None, backend='settrace'): self.frame_returning = None self.trace_opcodes = False self.enterframe = None + self.cmdframe = None + self.cmdlineno = None self.code_linenos = weakref.WeakKeyDictionary() self.backend = backend if backend == 'monitoring': @@ -297,7 +299,12 @@ def dispatch_line(self, frame): self.user_line(). Raise BdbQuit if self.quitting is set. Return self.trace_dispatch to continue tracing in this scope. """ - if self.stop_here(frame) or self.break_here(frame): + # GH-136057 + # For line events, we don't want to stop at the same line where + # the latest next/step command was issued. + if (self.stop_here(frame) or self.break_here(frame)) and not ( + self.cmdframe == frame and self.cmdlineno == frame.f_lineno + ): self.user_line(frame) self.restart_events() if self.quitting: raise BdbQuit @@ -526,7 +533,8 @@ def _set_trace_opcodes(self, trace_opcodes): if self.monitoring_tracer: self.monitoring_tracer.update_local_events() - def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False): + def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False, + cmdframe=None, cmdlineno=None): """Set the attributes for stopping. If stoplineno is greater than or equal to 0, then stop at line @@ -539,6 +547,10 @@ def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False): # stoplineno >= 0 means: stop at line >= the stoplineno # stoplineno -1 means: don't stop at all self.stoplineno = stoplineno + # cmdframe/cmdlineno is the frame/line number when the user issued + # step/next commands. + self.cmdframe = cmdframe + self.cmdlineno = cmdlineno self._set_trace_opcodes(opcode) def _set_caller_tracefunc(self, current_frame): @@ -564,7 +576,9 @@ def set_until(self, frame, lineno=None): def set_step(self): """Stop after one line of code.""" - self._set_stopinfo(None, None) + # set_step() could be called from signal handler so enterframe might be None + self._set_stopinfo(None, None, cmdframe=self.enterframe, + cmdlineno=getattr(self.enterframe, 'f_lineno', None)) def set_stepinstr(self): """Stop before the next instruction.""" @@ -572,7 +586,7 @@ def set_stepinstr(self): def set_next(self, frame): """Stop on the next line in or below the given frame.""" - self._set_stopinfo(frame, None) + self._set_stopinfo(frame, None, cmdframe=frame, cmdlineno=frame.f_lineno) def set_return(self, frame): """Stop when returning from the given frame.""" diff --git a/Lib/cProfile.py b/Lib/cProfile.py index 770d26f79628fe..cc6255f61ae211 100644 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -1,205 +1,13 @@ -"""Python interface for the 'lsprof' profiler. - Compatible with the 'profile' module. -""" - -__all__ = ["run", "runctx", "Profile"] - -import _lsprof -import importlib.machinery -import importlib.util -import io -import profile as _pyprofile - -# ____________________________________________________________ -# Simple interface - -def run(statement, filename=None, sort=-1): - return _pyprofile._Utils(Profile).run(statement, filename, sort) - -def runctx(statement, globals, locals, filename=None, sort=-1): - return _pyprofile._Utils(Profile).runctx(statement, globals, locals, - filename, sort) - -run.__doc__ = _pyprofile.run.__doc__ -runctx.__doc__ = _pyprofile.runctx.__doc__ - -# ____________________________________________________________ - -class Profile(_lsprof.Profiler): - """Profile(timer=None, timeunit=None, subcalls=True, builtins=True) - - Builds a profiler object using the specified timer function. - The default timer is a fast built-in one based on real time. - For custom timer functions returning integers, timeunit can - be a float specifying a scale (i.e. how long each integer unit - is, in seconds). - """ - - # Most of the functionality is in the base class. - # This subclass only adds convenient and backward-compatible methods. - - def print_stats(self, sort=-1): - import pstats - if not isinstance(sort, tuple): - sort = (sort,) - pstats.Stats(self).strip_dirs().sort_stats(*sort).print_stats() - - def dump_stats(self, file): - import marshal - with open(file, 'wb') as f: - self.create_stats() - marshal.dump(self.stats, f) - - def create_stats(self): - self.disable() - self.snapshot_stats() +"""Compatibility wrapper for cProfile module. - def snapshot_stats(self): - entries = self.getstats() - self.stats = {} - callersdicts = {} - # call information - for entry in entries: - func = label(entry.code) - nc = entry.callcount # ncalls column of pstats (before '/') - cc = nc - entry.reccallcount # ncalls column of pstats (after '/') - tt = entry.inlinetime # tottime column of pstats - ct = entry.totaltime # cumtime column of pstats - callers = {} - callersdicts[id(entry.code)] = callers - self.stats[func] = cc, nc, tt, ct, callers - # subcall information - for entry in entries: - if entry.calls: - func = label(entry.code) - for subentry in entry.calls: - try: - callers = callersdicts[id(subentry.code)] - except KeyError: - continue - nc = subentry.callcount - cc = nc - subentry.reccallcount - tt = subentry.inlinetime - ct = subentry.totaltime - if func in callers: - prev = callers[func] - nc += prev[0] - cc += prev[1] - tt += prev[2] - ct += prev[3] - callers[func] = nc, cc, tt, ct - - # The following two methods can be called by clients to use - # a profiler to profile a statement, given as a string. - - def run(self, cmd): - import __main__ - dict = __main__.__dict__ - return self.runctx(cmd, dict, dict) - - def runctx(self, cmd, globals, locals): - self.enable() - try: - exec(cmd, globals, locals) - finally: - self.disable() - return self - - # This method is more useful to profile a single function call. - def runcall(self, func, /, *args, **kw): - self.enable() - try: - return func(*args, **kw) - finally: - self.disable() - - def __enter__(self): - self.enable() - return self - - def __exit__(self, *exc_info): - self.disable() - -# ____________________________________________________________ - -def label(code): - if isinstance(code, str): - return ('~', 0, code) # built-in functions ('~' sorts at the end) - else: - return (code.co_filename, code.co_firstlineno, code.co_name) - -# ____________________________________________________________ - -def main(): - import os - import sys - import runpy - import pstats - from optparse import OptionParser - usage = "cProfile.py [-o output_file_path] [-s sort] [-m module | scriptfile] [arg] ..." - parser = OptionParser(usage=usage) - parser.allow_interspersed_args = False - parser.add_option('-o', '--outfile', dest="outfile", - help="Save stats to ", default=None) - parser.add_option('-s', '--sort', dest="sort", - help="Sort order when printing to stdout, based on pstats.Stats class", - default=2, - choices=sorted(pstats.Stats.sort_arg_dict_default)) - parser.add_option('-m', dest="module", action="store_true", - help="Profile a library module", default=False) - - if not sys.argv[1:]: - parser.print_usage() - sys.exit(2) - - (options, args) = parser.parse_args() - sys.argv[:] = args - - # The script that we're profiling may chdir, so capture the absolute path - # to the output file at startup. - if options.outfile is not None: - options.outfile = os.path.abspath(options.outfile) +This module maintains backward compatibility by importing from the new +profiling.tracing module. +""" - if len(args) > 0: - if options.module: - code = "run_module(modname, run_name='__main__')" - globs = { - 'run_module': runpy.run_module, - 'modname': args[0] - } - else: - progname = args[0] - sys.path.insert(0, os.path.dirname(progname)) - with io.open_code(progname) as fp: - code = compile(fp.read(), progname, 'exec') - spec = importlib.machinery.ModuleSpec(name='__main__', loader=None, - origin=progname) - module = importlib.util.module_from_spec(spec) - # Set __main__ so that importing __main__ in the profiled code will - # return the same namespace that the code is executing under. - sys.modules['__main__'] = module - # Ensure that we're using the same __dict__ instance as the module - # for the global variables so that updates to globals are reflected - # in the module's namespace. - globs = module.__dict__ - globs.update({ - '__spec__': spec, - '__file__': spec.origin, - '__name__': spec.name, - '__package__': None, - '__cached__': None, - }) +from profiling.tracing import run, runctx, Profile - try: - runctx(code, globs, None, options.outfile, options.sort) - except BrokenPipeError as exc: - # Prevent "Exception ignored" during interpreter shutdown. - sys.stdout = None - sys.exit(exc.errno) - else: - parser.print_usage() - return parser +__all__ = ["run", "runctx", "Profile"] -# When invoked as main program, invoke the profiler on a script -if __name__ == '__main__': +if __name__ == "__main__": + from profiling.tracing.__main__ import main main() diff --git a/Lib/calendar.py b/Lib/calendar.py index 3be1b50500eb07..92fe6b7723fe26 100644 --- a/Lib/calendar.py +++ b/Lib/calendar.py @@ -14,8 +14,9 @@ __all__ = ["IllegalMonthError", "IllegalWeekdayError", "setfirstweekday", "firstweekday", "isleap", "leapdays", "weekday", "monthrange", "monthcalendar", "prmonth", "month", "prcal", "calendar", - "timegm", "month_name", "month_abbr", "day_name", "day_abbr", - "Calendar", "TextCalendar", "HTMLCalendar", "LocaleTextCalendar", + "timegm", "month_name", "month_abbr", "standalone_month_name", + "standalone_month_abbr", "day_name", "day_abbr", "Calendar", + "TextCalendar", "HTMLCalendar", "LocaleTextCalendar", "LocaleHTMLCalendar", "weekheader", "Day", "Month", "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", @@ -139,6 +140,24 @@ def __len__(self): month_name = _localized_month('%B') month_abbr = _localized_month('%b') +# On platforms that support the %OB and %Ob specifiers, they are used +# to get the standalone form of the month name. This is required for +# some languages such as Greek, Slavic, and Baltic languages. +try: + standalone_month_name = _localized_month('%OB') + standalone_month_abbr = _localized_month('%Ob') +except ValueError: + standalone_month_name = month_name + standalone_month_abbr = month_abbr +else: + # Some systems that do not support '%OB' will keep it as-is (i.e., + # we get [..., '%OB', '%OB', '%OB']), so for non-distinct names, + # we fall back to month_name/month_abbr. + if len(set(standalone_month_name)) != len(set(month_name)): + standalone_month_name = month_name + if len(set(standalone_month_abbr)) != len(set(month_abbr)): + standalone_month_abbr = month_abbr + def isleap(year): """Return True for leap years, False for non-leap years.""" @@ -359,7 +378,7 @@ def formatweekday(self, day, width): """ Returns a formatted week day name. """ - if width >= 9: + if width >= max(map(len, day_name)): names = day_name else: names = day_abbr @@ -377,7 +396,7 @@ def formatmonthname(self, theyear, themonth, width, withyear=True): """ _validate_month(themonth) - s = month_name[themonth] + s = standalone_month_name[themonth] if withyear: s = "%s %r" % (s, theyear) return s.center(width) @@ -479,30 +498,29 @@ def formatday(self, day, weekday): """ if day == 0: # day outside month - return ' ' % self.cssclass_noday + return f' ' else: - return '%d' % (self.cssclasses[weekday], day) + return f'{day}' def formatweek(self, theweek): """ Return a complete week as a table row. """ s = ''.join(self.formatday(d, wd) for (d, wd) in theweek) - return '%s' % s + return f'{s}' def formatweekday(self, day): """ Return a weekday name as a table header. """ - return '%s' % ( - self.cssclasses_weekday_head[day], day_abbr[day]) + return f'{day_abbr[day]}' def formatweekheader(self): """ Return a header for a week as a table row. """ s = ''.join(self.formatweekday(i) for i in self.iterweekdays()) - return '%s' % s + return f'{s}' def formatmonthname(self, theyear, themonth, withyear=True): """ @@ -510,11 +528,10 @@ def formatmonthname(self, theyear, themonth, withyear=True): """ _validate_month(themonth) if withyear: - s = '%s %s' % (month_name[themonth], theyear) + s = f'{standalone_month_name[themonth]} {theyear}' else: - s = '%s' % month_name[themonth] - return '%s' % ( - self.cssclass_month_head, s) + s = standalone_month_name[themonth] + return f'{s}' def formatmonth(self, theyear, themonth, withyear=True): """ @@ -522,8 +539,7 @@ def formatmonth(self, theyear, themonth, withyear=True): """ v = [] a = v.append - a('' % ( - self.cssclass_month)) + a(f'
') a('\n') a(self.formatmonthname(theyear, themonth, withyear=withyear)) a('\n') @@ -543,11 +559,9 @@ def formatyear(self, theyear, width=3): v = [] a = v.append width = max(width, 1) - a('
' % - self.cssclass_year) + a(f'
') a('\n') - a('' % ( - width, self.cssclass_year_head, theyear)) + a(f'') for i in range(JANUARY, JANUARY+12, width): # months in this row months = range(i, min(i+width, 13)) @@ -560,29 +574,48 @@ def formatyear(self, theyear, width=3): a('
%s
{theyear}
') return ''.join(v) - def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): + def _format_html_page(self, theyear, content, css, encoding): """ - Return a formatted year as a complete HTML page. + Return a complete HTML page with the given content. """ if encoding is None: encoding = 'utf-8' v = [] a = v.append - a('\n' % encoding) - a('\n') - a('\n') + a('\n') + a('\n') a('\n') - a('\n' % encoding) + a(f'\n') + a('\n') + a(f'Calendar for {theyear}\n') + a('\n') if css is not None: - a('\n' % css) - a('Calendar for %d\n' % theyear) + a(f'\n') a('\n') a('\n') - a(self.formatyear(theyear, width)) + a(content) a('\n') a('\n') return ''.join(v).encode(encoding, "xmlcharrefreplace") + def formatyearpage(self, theyear, width=3, css='calendar.css', encoding=None): + """ + Return a formatted year as a complete HTML page. + """ + content = self.formatyear(theyear, width) + return self._format_html_page(theyear, content, css, encoding) + + def formatmonthpage(self, theyear, themonth, width=3, css='calendar.css', encoding=None): + """ + Return a formatted month as a complete HTML page. + """ + content = self.formatmonth(theyear, themonth, width) + return self._format_html_page(theyear, content, css, encoding) + class different_locale: def __init__(self, locale): @@ -653,28 +686,61 @@ def __init__(self, highlight_day=None, *args, **kwargs): super().__init__(*args, **kwargs) self.highlight_day = highlight_day - def formatweek(self, theweek, width, *, highlight_day=None): + def _get_theme(self): + from _colorize import get_theme + + return get_theme(tty_file=sys.stdout) + + def formatday(self, day, weekday, width, *, highlight_day=None): """ - Returns a single week in a string (no newline). + Returns a formatted day. """ - if highlight_day: - from _colorize import get_colors - - ansi = get_colors() - highlight = f"{ansi.BLACK}{ansi.BACKGROUND_YELLOW}" - reset = ansi.RESET + if day == 0: + s = '' else: - highlight = reset = "" + s = f'{day:2}' + s = s.center(width) + if day == highlight_day: + theme = self._get_theme().calendar + s = f"{theme.highlight}{s}{theme.reset}" + return s + def formatweek(self, theweek, width, *, highlight_day=None): + """ + Returns a single week in a string (no newline). + """ return ' '.join( - ( - f"{highlight}{self.formatday(d, wd, width)}{reset}" - if d == highlight_day - else self.formatday(d, wd, width) - ) + self.formatday(d, wd, width, highlight_day=highlight_day) for (d, wd) in theweek ) + def formatweekheader(self, width): + """ + Return a header for a week. + """ + header = super().formatweekheader(width) + theme = self._get_theme().calendar + return f"{theme.weekday}{header}{theme.reset}" + + def formatmonthname(self, theyear, themonth, width, withyear=True): + """ + Return a formatted month name. + """ + name = super().formatmonthname(theyear, themonth, width, withyear) + theme = self._get_theme().calendar + if ( + self.highlight_day + and self.highlight_day.year == theyear + and self.highlight_day.month == themonth + ): + color = theme.highlight + name_only = name.strip() + colored_name = f"{color}{name_only}{theme.reset}" + return name.replace(name_only, colored_name, 1) + else: + color = theme.header + return f"{color}{name}{theme.reset}" + def formatmonth(self, theyear, themonth, w=0, l=0): """ Return a month's calendar string (multi-line). @@ -709,7 +775,9 @@ def formatyear(self, theyear, w=2, l=1, c=6, m=3): colwidth = (w + 1) * 7 - 1 v = [] a = v.append - a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip()) + theme = self._get_theme().calendar + year = repr(theyear).center(colwidth*m+c*(m-1)).rstrip() + a(f"{theme.header}{year}{theme.reset}") a('\n'*l) header = self.formatweekheader(w) for (i, row) in enumerate(self.yeardays2calendar(theyear, m)): @@ -810,28 +878,30 @@ def timegm(tuple): def main(args=None): import argparse - parser = argparse.ArgumentParser(color=True) + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) textgroup = parser.add_argument_group('text only arguments') htmlgroup = parser.add_argument_group('html only arguments') textgroup.add_argument( "-w", "--width", type=int, default=2, - help="width of date column (default 2)" + help="width of date column" ) textgroup.add_argument( "-l", "--lines", type=int, default=1, - help="number of lines for each week (default 1)" + help="number of lines for each week" ) textgroup.add_argument( "-s", "--spacing", type=int, default=6, - help="spacing between months (default 6)" + help="spacing between months" ) textgroup.add_argument( "-m", "--months", type=int, default=3, - help="months per row (default 3)" + help="months per row" ) htmlgroup.add_argument( "-c", "--css", @@ -846,18 +916,18 @@ def main(args=None): parser.add_argument( "-e", "--encoding", default=None, - help="encoding to use for output (default utf-8)" + help="encoding to use for output" ) parser.add_argument( "-t", "--type", default="text", choices=("text", "html"), - help="output type (text or html)" + help="output type (`text` or `html`)" ) parser.add_argument( "-f", "--first-weekday", type=int, default=0, - help="weekday (0 is Monday, 6 is Sunday) to start each week (default 0)" + help="weekday (0 is Monday, 6 is Sunday) to start each week" ) parser.add_argument( "year", @@ -867,7 +937,7 @@ def main(args=None): parser.add_argument( "month", nargs='?', type=int, - help="month number (1-12, text only)" + help="month number (1-12)" ) options = parser.parse_args(args) @@ -880,9 +950,6 @@ def main(args=None): today = datetime.date.today() if options.type == "html": - if options.month: - parser.error("incorrect number of arguments") - sys.exit(1) if options.locale: cal = LocaleHTMLCalendar(locale=locale) else: @@ -893,10 +960,14 @@ def main(args=None): encoding = 'utf-8' optdict = dict(encoding=encoding, css=options.css) write = sys.stdout.buffer.write + if options.year is None: write(cal.formatyearpage(today.year, **optdict)) else: - write(cal.formatyearpage(options.year, **optdict)) + if options.month: + write(cal.formatmonthpage(options.year, options.month, **optdict)) + else: + write(cal.formatyearpage(options.year, **optdict)) else: if options.locale: cal = _CLIDemoLocaleCalendar(highlight_day=today, locale=locale) diff --git a/Lib/code.py b/Lib/code.py index f7e275d8801b7c..df1d7199e33934 100644 --- a/Lib/code.py +++ b/Lib/code.py @@ -385,7 +385,7 @@ def interact(banner=None, readfunc=None, local=None, exitmsg=None, local_exit=Fa if __name__ == "__main__": import argparse - parser = argparse.ArgumentParser(color=True) + parser = argparse.ArgumentParser() parser.add_argument('-q', action='store_true', help="don't print version and copyright messages") args = parser.parse_args() diff --git a/Lib/codecs.py b/Lib/codecs.py index e4a8010aba90a5..af6ab031157e79 100644 --- a/Lib/codecs.py +++ b/Lib/codecs.py @@ -93,7 +93,7 @@ class CodecInfo(tuple): def __new__(cls, encode, decode, streamreader=None, streamwriter=None, incrementalencoder=None, incrementaldecoder=None, name=None, - *, _is_text_encoding=None): + *, _is_text_encoding=None, _expat_decoding_table=None): self = tuple.__new__(cls, (encode, decode, streamreader, streamwriter)) self.name = name self.encode = encode @@ -104,6 +104,8 @@ def __new__(cls, encode, decode, streamreader=None, streamwriter=None, self.streamreader = streamreader if _is_text_encoding is not None: self._is_text_encoding = _is_text_encoding + if _expat_decoding_table is not None: + self._expat_decoding_table = _expat_decoding_table return self def __repr__(self): diff --git a/Lib/codeop.py b/Lib/codeop.py index 8cac00442d99e3..40e88423119bc4 100644 --- a/Lib/codeop.py +++ b/Lib/codeop.py @@ -66,9 +66,9 @@ def _maybe_compile(compiler, source, filename, symbol, flags): try: compiler(source + "\n", filename, symbol, flags=flags) return None - except _IncompleteInputError as e: + except _IncompleteInputError: return None - except SyntaxError as e: + except SyntaxError: pass # fallthrough diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index b8653f40a942f0..20f1e728733fec 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -32,6 +32,8 @@ _sys.modules['collections.abc'] = _collections_abc abc = _collections_abc +lazy from copy import copy as _copy +lazy from heapq import nlargest as _nlargest from itertools import chain as _chain from itertools import repeat as _repeat from itertools import starmap as _starmap @@ -59,8 +61,6 @@ except ImportError: pass -heapq = None # Lazily imported - ################################################################################ ### OrderedDict @@ -328,14 +328,14 @@ def __ior__(self, other): return self def __or__(self, other): - if not isinstance(other, dict): + if not isinstance(other, (dict, frozendict)): return NotImplemented new = self.__class__(self) new.update(other) return new def __ror__(self, other): - if not isinstance(other, dict): + if not isinstance(other, (dict, frozendict)): return NotImplemented new = self.__class__(other) new.update(self) @@ -634,12 +634,7 @@ def most_common(self, n=None): if n is None: return sorted(self.items(), key=_itemgetter(1), reverse=True) - # Lazy import to speedup Python startup time - global heapq - if heapq is None: - import heapq - - return heapq.nlargest(n, self.items(), key=_itemgetter(1)) + return _nlargest(n, self.items(), key=_itemgetter(1)) def elements(self): '''Iterator over elements repeating each as many times as its count. @@ -796,6 +791,7 @@ def __repr__(self): # set(cp - cq) == sp - sq # set(cp | cq) == sp | sq # set(cp & cq) == sp & sq + # set(cp ^ cq) == sp ^ sq def __eq__(self, other): 'True if all counts agree. Missing counts are treated as zero.' @@ -908,6 +904,33 @@ def __and__(self, other): result[elem] = newcount return result + def __xor__(self, other): + '''Symmetric difference. Absolute value of count differences. + + The symmetric difference p ^ q is equivalent to: + + (p - q) | (q - p). + + For each element, symmetric difference gives the same result as: + + max(p[elem], q[elem]) - min(p[elem], q[elem]) + + >>> Counter(a=5, b=3, c=2, d=2) ^ Counter(a=1, b=3, c=5, e=1) + Counter({'a': 4, 'c': 3, 'd': 2, 'e': 1}) + + ''' + if not isinstance(other, Counter): + return NotImplemented + result = Counter() + for elem, count in self.items(): + newcount = abs(count - other[elem]) + if newcount: + result[elem] = newcount + for elem, count in other.items(): + if elem not in self and count: + result[elem] = abs(count) + return result + def __pos__(self): 'Adds an empty counter, effectively stripping negative and zero counts' result = Counter() @@ -990,6 +1013,22 @@ def __iand__(self, other): self[elem] = other_count return self._keep_positive() + def __ixor__(self, other): + '''Inplace symmetric difference. Absolute value of count differences. + + >>> c = Counter(a=5, b=3, c=2, d=2) + >>> c ^= Counter(a=1, b=3, c=5, e=1) + >>> c + Counter({'a': 4, 'c': 3, 'd': 2, 'e': 1}) + + ''' + for elem, count in self.items(): + self[elem] = abs(count - other[elem]) + for elem, count in other.items(): + if elem not in self: + self[elem] = abs(count) + return self._keep_positive() + ######################################################################## ### ChainMap @@ -1177,14 +1216,14 @@ def __repr__(self): def __or__(self, other): if isinstance(other, UserDict): return self.__class__(self.data | other.data) - if isinstance(other, dict): + if isinstance(other, (dict, frozendict)): return self.__class__(self.data | other) return NotImplemented def __ror__(self, other): if isinstance(other, UserDict): return self.__class__(other.data | self.data) - if isinstance(other, dict): + if isinstance(other, (dict, frozendict)): return self.__class__(other | self.data) return NotImplemented @@ -1205,16 +1244,29 @@ def __copy__(self): def copy(self): if self.__class__ is UserDict: return UserDict(self.data.copy()) - import copy data = self.data try: self.data = {} - c = copy.copy(self) + c = _copy(self) finally: self.data = data c.update(self) return c + + # This method has a default implementation in MutableMapping, but dict's + # equivalent is last-in, first-out instead of first-in, first-out. + def popitem(self): + """Remove and return a (key, value) pair as a 2-tuple. + + Removes pairs in the same order as the wrapped mapping's popitem() + method. For dict objects (the default), that order is last-in, + first-out (LIFO). + Raises KeyError if the UserDict is empty. + """ + return self.data.popitem() + + @classmethod def fromkeys(cls, iterable, value=None): d = cls() @@ -1498,6 +1550,8 @@ def format_map(self, mapping): return self.data.format_map(mapping) def index(self, sub, start=0, end=_sys.maxsize): + if isinstance(sub, UserString): + sub = sub.data return self.data.index(sub, start, end) def isalpha(self): @@ -1566,6 +1620,8 @@ def rfind(self, sub, start=0, end=_sys.maxsize): return self.data.rfind(sub, start, end) def rindex(self, sub, start=0, end=_sys.maxsize): + if isinstance(sub, UserString): + sub = sub.data return self.data.rindex(sub, start, end) def rjust(self, width, *args): diff --git a/Lib/compileall.py b/Lib/compileall.py index 67fe370451e1ef..812a496611e043 100644 --- a/Lib/compileall.py +++ b/Lib/compileall.py @@ -165,6 +165,14 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, stripdir = os.fspath(stripdir) if stripdir is not None else None name = os.path.basename(fullname) + # Without a cache_tag, we can only create legacy .pyc files. None of our + # callers seem to expect this, so the best we can do is fail without raising + if not legacy and sys.implementation.cache_tag is None: + if not quiet: + print("No cache tag is available to generate .pyc path for", + repr(fullname)) + return False + dfile = None if ddir is not None: @@ -223,7 +231,7 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, cfile = importlib.util.cache_from_source(fullname) opt_cfiles[opt_level] = cfile - head, tail = name[:-3], name[-3:] + tail = name[-3:] if tail == '.py': if not force: try: @@ -318,7 +326,6 @@ def main(): parser = argparse.ArgumentParser( description='Utilities to support installing Python libraries.', - color=True, ) parser.add_argument('-l', action='store_const', const=0, default=None, dest='maxlevels', @@ -330,10 +337,10 @@ def main(): parser.add_argument('-f', action='store_true', dest='force', help='force rebuild even if timestamps are up to date') parser.add_argument('-q', action='count', dest='quiet', default=0, - help='output only error messages; -qq will suppress ' + help='output only error messages; `-qq` will suppress ' 'the error messages as well.') parser.add_argument('-b', action='store_true', dest='legacy', - help='use legacy (pre-PEP3147) compiled file locations') + help='use legacy (pre-PEP 3147) compiled file locations') parser.add_argument('-d', metavar='DESTDIR', dest='ddir', default=None, help=('directory to prepend to file paths for use in ' 'compile-time tracebacks and in runtime ' @@ -359,28 +366,28 @@ def main(): 'of each file considered for compilation')) parser.add_argument('-i', metavar='FILE', dest='flist', help=('add all the files and directories listed in ' - 'FILE to the list considered for compilation; ' - 'if "-", names are read from stdin')) + '`FILE` to the list considered for compilation; ' + 'if `"-"`, names are read from `stdin`')) parser.add_argument('compile_dest', metavar='FILE|DIR', nargs='*', help=('zero or more file and directory names ' 'to compile; if no arguments given, defaults ' - 'to the equivalent of -l sys.path')) + 'to the equivalent of `-l` `sys.path`')) parser.add_argument('-j', '--workers', default=1, type=int, help='Run compileall concurrently') invalidation_modes = [mode.name.lower().replace('_', '-') for mode in py_compile.PycInvalidationMode] parser.add_argument('--invalidation-mode', choices=sorted(invalidation_modes), - help=('set .pyc invalidation mode; defaults to ' - '"checked-hash" if the SOURCE_DATE_EPOCH ' + help=('set `.pyc` invalidation mode; defaults to ' + '`"checked-hash"` if the `SOURCE_DATE_EPOCH` ' 'environment variable is set, and ' - '"timestamp" otherwise.')) + '`"timestamp"` otherwise.')) parser.add_argument('-o', action='append', type=int, dest='opt_levels', help=('Optimization levels to run compilation with. ' - 'Default is -1 which uses the optimization level ' - 'of the Python interpreter itself (see -O).')) + 'Default is `-1` which uses the optimization level ' + 'of the Python interpreter itself (see `-O`).')) parser.add_argument('-e', metavar='DIR', dest='limit_sl_dest', - help='Ignore symlinks pointing outsite of the DIR') + help='Ignore symlinks pointing outsite of the `DIR`') parser.add_argument('--hardlink-dupes', action='store_true', dest='hardlink_dupes', help='Hardlink duplicated pyc files') diff --git a/Lib/compression/zstd/__init__.py b/Lib/compression/zstd/__init__.py index 84b25914b0aa93..5326cf9b19cf88 100644 --- a/Lib/compression/zstd/__init__.py +++ b/Lib/compression/zstd/__init__.py @@ -61,8 +61,9 @@ def __setattr__(self, name, _): def get_frame_info(frame_buffer): """Get Zstandard frame information from a frame header. - *frame_buffer* is a bytes-like object. It should start from the beginning - of a frame, and needs to include at least the frame header (6 to 18 bytes). + *frame_buffer* is a bytes-like object. It should start from the + beginning of a frame, and needs to include at least the frame header + (6 to 18 bytes). The returned FrameInfo object has two attributes. 'decompressed_size' is the size in bytes of the data in the frame when @@ -103,16 +104,17 @@ def finalize_dict(zstd_dict, /, samples, dict_size, level): finalize *zstd_dict* by adding headers and statistics according to the Zstandard dictionary format. - You may compose an effective dictionary content by hand, which is used as - basis dictionary, and use some samples to finalize a dictionary. The basis - dictionary may be a "raw content" dictionary. See *is_raw* in ZstdDict. + You may compose an effective dictionary content by hand, which is used + as basis dictionary, and use some samples to finalize a dictionary. The + basis dictionary may be a "raw content" dictionary. See *is_raw* in + ZstdDict. - *samples* is an iterable of samples, where a sample is a bytes-like object - representing a file. + *samples* is an iterable of samples, where a sample is a bytes-like + object representing a file. *dict_size* is the dictionary's maximum size, in bytes. *level* is the expected compression level. The statistics for each - compression level differ, so tuning the dictionary to the compression level - can provide improvements. + compression level differ, so tuning the dictionary to the compression + level can provide improvements. """ if not isinstance(zstd_dict, ZstdDict): @@ -140,8 +142,8 @@ def compress(data, level=None, options=None, zstd_dict=None): COMPRESSION_LEVEL_DEFAULT ('3'). *options* is a dict object that contains advanced compression parameters. See CompressionParameter for more on options. - *zstd_dict* is a ZstdDict object, a pre-trained Zstandard dictionary. See - the function train_dict for how to train a ZstdDict on sample data. + *zstd_dict* is a ZstdDict object, a pre-trained Zstandard dictionary. + See the function train_dict for how to train a ZstdDict on sample data. For incremental compression, use a ZstdCompressor instead. """ @@ -152,8 +154,8 @@ def compress(data, level=None, options=None, zstd_dict=None): def decompress(data, zstd_dict=None, options=None): """Decompress one or more frames of Zstandard compressed *data*. - *zstd_dict* is a ZstdDict object, a pre-trained Zstandard dictionary. See - the function train_dict for how to train a ZstdDict on sample data. + *zstd_dict* is a ZstdDict object, a pre-trained Zstandard dictionary. + See the function train_dict for how to train a ZstdDict on sample data. *options* is a dict object that contains advanced compression parameters. See DecompressionParameter for more on options. diff --git a/Lib/compression/zstd/_zstdfile.py b/Lib/compression/zstd/_zstdfile.py index d709f5efc658fa..8d3358152e9a88 100644 --- a/Lib/compression/zstd/_zstdfile.py +++ b/Lib/compression/zstd/_zstdfile.py @@ -36,9 +36,9 @@ def __init__(self, file, /, mode='r', *, *file* can be either an file-like object, or a file name to open. - *mode* can be 'r' for reading (default), 'w' for (over)writing, 'x' for - creating exclusively, or 'a' for appending. These can equivalently be - given as 'rb', 'wb', 'xb' and 'ab' respectively. + *mode* can be 'r' for reading (default), 'w' for (over)writing, 'x' + for creating exclusively, or 'a' for appending. These can + equivalently be given as 'rb', 'wb', 'xb' and 'ab' respectively. *level* is an optional int specifying the compression level to use, or COMPRESSION_LEVEL_DEFAULT if not given. @@ -296,26 +296,27 @@ def open(file, /, mode='rb', *, level=None, options=None, zstd_dict=None, encoding=None, errors=None, newline=None): """Open a Zstandard compressed file in binary or text mode. - file can be either a file name (given as a str, bytes, or PathLike object), - in which case the named file is opened, or it can be an existing file object - to read from or write to. + file can be either a file name (given as a str, bytes, or PathLike + object), in which case the named file is opened, or it can be + an existing file object to read from or write to. - The mode parameter can be 'r', 'rb' (default), 'w', 'wb', 'x', 'xb', 'a', - 'ab' for binary mode, or 'rt', 'wt', 'xt', 'at' for text mode. + The mode parameter can be 'r', 'rb' (default), 'w', 'wb', 'x', 'xb', + 'a', 'ab' for binary mode, or 'rt', 'wt', 'xt', 'at' for text mode. - The level, options, and zstd_dict parameters specify the settings the same - as ZstdFile. + The level, options, and zstd_dict parameters specify the settings the + same as ZstdFile. When using read mode (decompression), the options parameter is a dict - representing advanced decompression options. The level parameter is not - supported in this case. When using write mode (compression), only one of - level, an int representing the compression level, or options, a dict - representing advanced compression options, may be passed. In both modes, - zstd_dict is a ZstdDict instance containing a trained Zstandard dictionary. - - For binary mode, this function is equivalent to the ZstdFile constructor: - ZstdFile(filename, mode, ...). In this case, the encoding, errors and - newline parameters must not be provided. + representing advanced decompression options. The level parameter is not + supported in this case. When using write mode (compression), only one + of level, an int representing the compression level, or options, a dict + representing advanced compression options, may be passed. In both + modes, zstd_dict is a ZstdDict instance containing a trained Zstandard + dictionary. + + For binary mode, this function is equivalent to the ZstdFile + constructor: ZstdFile(filename, mode, ...). In this case, the encoding, + errors and newline parameters must not be provided. For text mode, an ZstdFile object is created, and wrapped in an io.TextIOWrapper instance with the specified encoding, error handling diff --git a/Lib/concurrent/futures/__init__.py b/Lib/concurrent/futures/__init__.py index e717222cf98b32..d6ac4b3e0b675f 100644 --- a/Lib/concurrent/futures/__init__.py +++ b/Lib/concurrent/futures/__init__.py @@ -44,7 +44,7 @@ def __dir__(): - return __all__ + ('__author__', '__doc__') + return __all__ + ['__author__', '__doc__'] def __getattr__(name): diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py index f506ce68aea5b2..4e71331f9a0a59 100644 --- a/Lib/concurrent/futures/_base.py +++ b/Lib/concurrent/futures/_base.py @@ -194,15 +194,15 @@ def as_completed(fs, timeout=None): """An iterator over the given futures that yields each as it completes. Args: - fs: The sequence of Futures (possibly created by different Executors) to - iterate over. - timeout: The maximum number of seconds to wait. If None, then there - is no limit on the wait time. + fs: The sequence of Futures (possibly created by different + Executors) to iterate over. + timeout: The maximum number of seconds to wait. If None, then + there is no limit on the wait time. Returns: - An iterator that yields the given Futures as they complete (finished or - cancelled). If any given Futures are duplicated, they will be returned - once. + An iterator that yields the given Futures as they complete + (finished or cancelled). If any given Futures are duplicated, + they will be returned once. Raises: TimeoutError: If the entire result iterator could not be generated @@ -258,19 +258,20 @@ def wait(fs, timeout=None, return_when=ALL_COMPLETED): """Wait for the futures in the given sequence to complete. Args: - fs: The sequence of Futures (possibly created by different Executors) to - wait upon. - timeout: The maximum number of seconds to wait. If None, then there - is no limit on the wait time. - return_when: Indicates when this function should return. The options - are: + fs: The sequence of Futures (possibly created by different + Executors) to wait upon. + timeout: The maximum number of seconds to wait. If None, then + there is no limit on the wait time. + return_when: Indicates when this function should return. + The options are: FIRST_COMPLETED - Return when any future finishes or is cancelled. FIRST_EXCEPTION - Return when any future finishes by raising an - exception. If no future raises an exception + exception. If no future raises an exception then it is equivalent to ALL_COMPLETED. - ALL_COMPLETED - Return when all futures finish or are cancelled. + ALL_COMPLETED - Return when all futures finish or are + cancelled. Returns: A named 2-tuple of sets. The first set, named 'done', contains the @@ -404,11 +405,12 @@ def add_done_callback(self, fn): Args: fn: A callable that will be called with this future as its only - argument when the future completes or is cancelled. The callable - will always be called by a thread in the same process in which - it was added. If the future has already completed or been - cancelled then the callable will be called immediately. These - callables are called in the order that they were added. + argument when the future completes or is cancelled. The + callable will always be called by a thread in the same + process in which it was added. If the future has already + completed or been cancelled then the callable will be + called immediately. These callables are called in the + order that they were added. """ with self._condition: if self._state not in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]: @@ -423,17 +425,19 @@ def result(self, timeout=None): """Return the result of the call that the future represents. Args: - timeout: The number of seconds to wait for the result if the future - isn't done. If None, then there is no limit on the wait time. + timeout: The number of seconds to wait for the result if the + future isn't done. If None, then there is no limit on the + wait time. Returns: The result of the call that the future represents. Raises: CancelledError: If the future was cancelled. - TimeoutError: If the future didn't finish executing before the given - timeout. - Exception: If the call raised then that exception will be raised. + TimeoutError: If the future didn't finish executing before the + given timeout. + Exception: If the call raised then that exception will be + raised. """ try: with self._condition: @@ -459,17 +463,17 @@ def exception(self, timeout=None): Args: timeout: The number of seconds to wait for the exception if the - future isn't done. If None, then there is no limit on the wait - time. + future isn't done. If None, then there is no limit on the + wait time. Returns: - The exception raised by the call that the future represents or None - if the call completed without raising. + The exception raised by the call that the future represents or + None if the call completed without raising. Raises: CancelledError: If the future was cancelled. - TimeoutError: If the future didn't finish executing before the given - timeout. + TimeoutError: If the future didn't finish executing before the + given timeout. """ with self._condition: @@ -494,22 +498,23 @@ def set_running_or_notify_cancel(self): Should only be used by Executor implementations and unit tests. If the future has been cancelled (cancel() was called and returned - True) then any threads waiting on the future completing (though calls - to as_completed() or wait()) are notified and False is returned. + True) then any threads waiting on the future completing (though + calls to as_completed() or wait()) are notified and False is + returned. If the future was not cancelled then it is put in the running state (future calls to running() will return True) and True is returned. This method should be called by Executor implementations before - executing the work associated with this future. If this method returns - False then the work should not be executed. + executing the work associated with this future. If this method + returns False then the work should not be executed. Returns: False if the Future was cancelled, True otherwise. Raises: - RuntimeError: if this method was already called or if set_result() - or set_exception() was called. + RuntimeError: if this method was already called or if + set_result() or set_exception() was called. """ with self._condition: if self._state == CANCELLED: @@ -593,8 +598,9 @@ class Executor(object): def submit(self, fn, /, *args, **kwargs): """Submits a callable to be executed with the given arguments. - Schedules the callable to be executed as fn(*args, **kwargs) and returns - a Future instance representing the execution of the callable. + Schedules the callable to be executed as fn(*args, **kwargs) and + returns a Future instance representing the execution of the + callable. Returns: A Future representing the given call. @@ -607,25 +613,25 @@ def map(self, fn, *iterables, timeout=None, chunksize=1, buffersize=None): Args: fn: A callable that will take as many arguments as there are passed iterables. - timeout: The maximum number of seconds to wait. If None, then there - is no limit on the wait time. - chunksize: The size of the chunks the iterable will be broken into - before being passed to a child process. This argument is only - used by ProcessPoolExecutor; it is ignored by + timeout: The maximum number of seconds to wait. If None, then + there is no limit on the wait time. + chunksize: The size of the chunks the iterable will be broken + into before being passed to a child process. This argument + is only used by ProcessPoolExecutor; it is ignored by ThreadPoolExecutor. buffersize: The number of submitted tasks whose results have not - yet been yielded. If the buffer is full, iteration over the + yet been yielded. If the buffer is full, iteration over the iterables pauses until a result is yielded from the buffer. - If None, all input elements are eagerly collected, and a task is - submitted for each. + If None, all input elements are eagerly collected, and + a task is submitted for each. Returns: - An iterator equivalent to: map(func, *iterables) but the calls may - be evaluated out-of-order. + An iterator equivalent to: map(func, *iterables) but the calls + may be evaluated out-of-order. Raises: - TimeoutError: If the entire result iterator could not be generated - before the given timeout. + TimeoutError: If the entire result iterator could not be + generated before the given timeout. Exception: If fn(*args) raises for any values. """ if buffersize is not None and not isinstance(buffersize, int): @@ -679,8 +685,8 @@ def shutdown(self, wait=True, *, cancel_futures=False): Args: wait: If True then shutdown will not return until all running - futures have finished executing and the resources used by the - executor have been reclaimed. + futures have finished executing and the resources used by + the executor have been reclaimed. cancel_futures: If True then shutdown will cancel all pending futures. Futures that are completed or running will not be cancelled. diff --git a/Lib/concurrent/futures/interpreter.py b/Lib/concurrent/futures/interpreter.py index 53c6e757ded2e3..fd3d45144b49a7 100644 --- a/Lib/concurrent/futures/interpreter.py +++ b/Lib/concurrent/futures/interpreter.py @@ -2,7 +2,6 @@ from concurrent import interpreters import sys -import textwrap from . import thread as _thread import traceback @@ -111,8 +110,8 @@ def __init__(self, max_workers=None, thread_name_prefix='', """Initializes a new InterpreterPoolExecutor instance. Args: - max_workers: The maximum number of interpreters that can be used to - execute the given calls. + max_workers: The maximum number of interpreters that can be used + to execute the given calls. thread_name_prefix: An optional name prefix to give our threads. initializer: A callable or script used to initialize each worker interpreter. diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index a14650bf5fa47c..10d4ac89d72571 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -469,17 +469,33 @@ def _terminate_broken(self, cause): executor._shutdown_thread = True executor = None - # All pending tasks are to be marked failed with the following - # BrokenProcessPool error - bpe = BrokenProcessPool("A process in the process pool was " - "terminated abruptly while the future was " - "running or pending.") + # All pending tasks are to be marked failed with a + # BrokenProcessPool error, as separate instances to avoid sharing + # a traceback (gh-101267). + cause_str = None if cause is not None: - bpe.__cause__ = _RemoteTraceback( - f"\n'''\n{''.join(cause)}'''") + cause_str = ''.join(cause) + else: + # No cause known, so report any processes that have + # terminated with nonzero exit codes, e.g. from a + # segfault. Multiple may terminate simultaneously, + # so include all of them in the traceback. + errors = [] + for p in self.processes.values(): + if p.exitcode is not None and p.exitcode != 0: + errors.append(f"Process {p.pid} terminated abruptly " + f"with exit code {p.exitcode}") + if errors: + cause_str = "\n".join(errors) + cause_tb = f"\n'''\n{cause_str}'''" if cause_str else None # Mark pending tasks as failed. for work_id, work_item in self.pending_work_items.items(): + bpe = BrokenProcessPool("A process in the process pool was " + "terminated abruptly while the future was " + "running or pending.") + if cause_tb is not None: + bpe.__cause__ = _RemoteTraceback(cause_tb) try: work_item.future.set_exception(bpe) except _base.InvalidStateError: @@ -642,19 +658,21 @@ def __init__(self, max_workers=None, mp_context=None, Args: max_workers: The maximum number of processes that can be used to - execute the given calls. If None or not given then as many - worker processes will be created as the machine has processors. - mp_context: A multiprocessing context to launch the workers created - using the multiprocessing.get_context('start method') API. This - object should provide SimpleQueue, Queue and Process. + execute the given calls. If None or not given then as many + worker processes will be created as the machine has + processors. + mp_context: A multiprocessing context to launch the workers + created using the multiprocessing.get_context('start method') + API. This object should provide SimpleQueue, Queue and + Process. initializer: A callable used to initialize worker processes. initargs: A tuple of arguments to pass to the initializer. - max_tasks_per_child: The maximum number of tasks a worker process - can complete before it will exit and be replaced with a fresh - worker process. The default of None means worker process will - live as long as the executor. Requires a non-'fork' mp_context - start method. When given, we default to using 'spawn' if no - mp_context is supplied. + max_tasks_per_child: The maximum number of tasks a worker + process can complete before it will exit and be replaced + with a fresh worker process. The default of None means + worker process will live as long as the executor. Requires + a non-'fork' mp_context start method. When given, we + default to using 'spawn' if no mp_context is supplied. """ _check_system_limits() @@ -824,24 +842,25 @@ def map(self, fn, *iterables, timeout=None, chunksize=1, buffersize=None): Args: fn: A callable that will take as many arguments as there are passed iterables. - timeout: The maximum number of seconds to wait. If None, then there - is no limit on the wait time. - chunksize: If greater than one, the iterables will be chopped into - chunks of size chunksize and submitted to the process pool. - If set to one, the items in the list will be sent one at a time. + timeout: The maximum number of seconds to wait. If None, then + there is no limit on the wait time. + chunksize: If greater than one, the iterables will be chopped + into chunks of size chunksize and submitted to the process + pool. If set to one, the items in the list will be sent + one at a time. buffersize: The number of submitted tasks whose results have not - yet been yielded. If the buffer is full, iteration over the + yet been yielded. If the buffer is full, iteration over the iterables pauses until a result is yielded from the buffer. - If None, all input elements are eagerly collected, and a task is - submitted for each. + If None, all input elements are eagerly collected, and + a task is submitted for each. Returns: - An iterator equivalent to: map(func, *iterables) but the calls may - be evaluated out-of-order. + An iterator equivalent to: map(func, *iterables) but the calls + may be evaluated out-of-order. Raises: - TimeoutError: If the entire result iterator could not be generated - before the given timeout. + TimeoutError: If the entire result iterator could not be + generated before the given timeout. Exception: If fn(*args) raises for any values. """ if chunksize < 1: diff --git a/Lib/concurrent/interpreters/__init__.py b/Lib/concurrent/interpreters/__init__.py index aa46a2b37a48d5..ea4147ee9a25da 100644 --- a/Lib/concurrent/interpreters/__init__.py +++ b/Lib/concurrent/interpreters/__init__.py @@ -149,12 +149,17 @@ def __del__(self): def __reduce__(self): return (type(self), (self._id,)) - def _decref(self): + # gh-135729: Globals might be destroyed by the time this is called, so we + # need to keep references ourself + def _decref(self, *, + InterpreterNotFoundError=InterpreterNotFoundError, + _interp_decref=_interpreters.decref, + ): if not self._ownsref: return self._ownsref = False try: - _interpreters.decref(self._id) + _interp_decref(self._id) except InterpreterNotFoundError: pass diff --git a/Lib/concurrent/interpreters/_queues.py b/Lib/concurrent/interpreters/_queues.py index 9c12b2c8c24664..5f3ee0934de59d 100644 --- a/Lib/concurrent/interpreters/_queues.py +++ b/Lib/concurrent/interpreters/_queues.py @@ -170,13 +170,13 @@ def full(self): def qsize(self): return _queues.get_count(self._id) - def put(self, obj, timeout=None, *, + def put(self, obj, block=True, timeout=None, *, unbounditems=None, _delay=10 / 1000, # 10 milliseconds ): """Add the object to the queue. - This blocks while the queue is full. + If "block" is true, this blocks while the queue is full. For most objects, the object received through Queue.get() will be a new one, equivalent to the original and not sharing any @@ -185,7 +185,8 @@ def put(self, obj, timeout=None, *, underlying data is actually shared. Furthermore, some types can be sent through a queue more efficiently than others. This group includes various immutable types like int, str, bytes, and - tuple (if the items are likewise efficiently shareable). See interpreters.is_shareable(). + tuple (if the items are likewise efficiently shareable). + See interpreters.is_shareable(). "unbounditems" controls the behavior of Queue.get() for the given object if the current interpreter (calling put()) is later @@ -209,6 +210,8 @@ def put(self, obj, timeout=None, *, If "unbounditems" is UNBOUND then it is returned by get() in place of the unbound item. """ + if not block: + return self.put_nowait(obj, unbounditems=unbounditems) if unbounditems is None: unboundop = -1 else: @@ -221,7 +224,7 @@ def put(self, obj, timeout=None, *, while True: try: _queues.put(self._id, obj, unboundop) - except QueueFull as exc: + except QueueFull: if timeout is not None and time.time() >= end: raise # re-raise time.sleep(_delay) @@ -235,17 +238,19 @@ def put_nowait(self, obj, *, unbounditems=None): unboundop, = _serialize_unbound(unbounditems) _queues.put(self._id, obj, unboundop) - def get(self, timeout=None, *, + def get(self, block=True, timeout=None, *, _delay=10 / 1000, # 10 milliseconds ): """Return the next object from the queue. - This blocks while the queue is empty. + If "block" is true, this blocks while the queue is empty. If the next item's original interpreter has been destroyed then the "next object" is determined by the value of the "unbounditems" argument to put(). """ + if not block: + return self.get_nowait() if timeout is not None: timeout = int(timeout) if timeout < 0: @@ -254,7 +259,7 @@ def get(self, timeout=None, *, while True: try: obj, unboundop = _queues.get(self._id) - except QueueEmpty as exc: + except QueueEmpty: if timeout is not None and time.time() >= end: raise # re-raise time.sleep(_delay) @@ -273,7 +278,7 @@ def get_nowait(self): """ try: obj, unboundop = _queues.get(self._id) - except QueueEmpty as exc: + except QueueEmpty: raise # re-raise if unboundop is not None: assert obj is None, repr(obj) diff --git a/Lib/configparser.py b/Lib/configparser.py index 18af1eadaad111..a53ac87276445a 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -315,12 +315,15 @@ def __init__(self, source, *args): def append(self, lineno, line): self.errors.append((lineno, line)) - self.message += '\n\t[line %2d]: %s' % (lineno, repr(line)) + self.message += f'\n\t[line {lineno:2d}]: {line!r}' def combine(self, others): + messages = [self.message] for other in others: - for error in other.errors: - self.append(*error) + for lineno, line in other.errors: + self.errors.append((lineno, line)) + messages.append(f'\n\t[line {lineno:2d}]: {line!r}') + self.message = "".join(messages) return self @staticmethod @@ -613,7 +616,9 @@ class RawConfigParser(MutableMapping): \] # ] """ _OPT_TMPL = r""" - (?P

The IDLE application is implemented in the idlelib package.

+

This is an optional module. +If it is missing from your copy of CPython, +look for documentation from your distributor (that is, +whoever provided Python to you). +If you are the distributor, see Requirements for optional modules.

@@ -390,7 +396,7 @@

Search and ReplaceC-space. If one types a prefix for the desired name +key is C-space. If one types a prefix for the desired name before opening the box, the first match or near miss is made visible. The result is the same as if one enters a prefix after the box is displayed. Show Completions after a quote completes @@ -437,8 +443,19 @@

Search and Replace +

Format block

+

Reformat Paragraph rewraps a block (‘paragraph’) of contiguous equally +indented non-blank comments, a similar block of text within a multiline +string, or a selected subset of either. +If needed, add a blank line to separate string from code. +Partial lines in a selection expand to complete lines. +The resulting lines have the same indent as before +but have maximum total length of N columns (characters). +Change the default N of 72 on the Window tab of IDLE Settings.

+

-

Code Context

+

Code Context

Within an editor window containing Python code, code context can be toggled in order to show or hide a pane at the top of the window. When shown, this pane freezes the opening lines for block code, such as those beginning with @@ -473,9 +490,9 @@

Shell window -
  • C-c attempts to interrupt statement execution (but may fail).

  • -
  • C-d closes Shell if typed at a >>> prompt.

  • -
  • Alt-p and Alt-n (C-p and C-n on macOS) +

  • C-c attempts to interrupt statement execution (but may fail).

  • +
  • C-d closes Shell if typed at a >>> prompt.

  • +
  • Alt-p and Alt-n (C-p and C-n on macOS) retrieve to the current prompt the previous or next previously entered statement that matches anything already typed.

  • Return while the cursor is on any previous statement @@ -516,28 +533,74 @@

    Startup and Code Execution -

    Command line usage

    -
    idle.py [-c command] [-d] [-e] [-h] [-i] [-r file] [-s] [-t title] [-] [arg] ...
    -
    --c command  run command in the shell window
    --d          enable debugger and open shell window
    --e          open editor window
    --h          print help message with legal combinations and exit
    --i          open shell window
    --r file     run file in shell window
    --s          run $IDLESTARTUP or $PYTHONSTARTUP first, in shell window
    --t title    set title of shell window
    --           run stdin in shell (- must be last option before args)
    +

    Command-line usage

    +

    IDLE can be invoked from the command line with various options. The general syntax is:

    +
    python -m idlelib [options] [file ...]
     
    -

    If there are arguments:

    +

    The following options are available:

    +
    +
    +-c <command>
    +

    Run the specified Python command in the shell window. +For example, pass -c "print('Hello, World!')". +On Windows, the outer quotes must be double quotes as shown.

    +
    + +
    +
    +-d
    +

    Enable the debugger and open the shell window.

    +
    + +
    +
    +-e
    +

    Open an editor window.

    +
    + +
    +
    +-h
    +

    Print a help message with legal combinations of options and exit.

    +
    + +
    +
    +-i
    +

    Open a shell window.

    +
    + +
    +
    +-r <file>
    +

    Run the specified file in the shell window.

    +
    + +
    +
    +-s
    +

    Run the startup file (as defined by the environment variables IDLESTARTUP or PYTHONSTARTUP) before opening the shell window.

    +
    + +
    +
    +-t <title>
    +

    Set the title of the shell window.

    +
    + +
    +
    +-
    +

    Read and execute standard input in the shell window. This option must be the last one before any arguments.

    +
    + +

    If arguments are provided:

      -
    • If -, -c, or r is used, all arguments are placed in -sys.argv[1:...] and sys.argv[0] is set to '', '-c', -or '-r'. No editor window is opened, even if that is the default -set in the Options dialog.

    • -
    • Otherwise, arguments are files opened for editing and -sys.argv reflects the arguments passed to IDLE itself.

    • +
    • If -, -c, or -r is used, all arguments are placed in sys.argv[1:], +and sys.argv[0] is set to '', '-c', or '-r' respectively. +No editor window is opened, even if that is the default set in the Options dialog.

    • +
    • Otherwise, arguments are treated as files to be opened for editing, and sys.argv reflects the arguments passed to IDLE itself.

  • @@ -739,7 +802,7 @@

    Running without a subprocess

    Help and Preferences

    -

    Help sources

    +

    Help sources

    Help menu entry “IDLE Help” displays a formatted html version of the IDLE chapter of the Library Reference. The result, in a read-only tkinter text window, is close to what one sees in a web browser. @@ -798,7 +861,7 @@

    ExtensionsPEP 434).

    +sense that feature changes can be backported (see PEP 434).

    diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index 063a749df54bc0..48e7eca280ebf8 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -23,7 +23,12 @@ copy_strip - Copy the text part of idle.html to help.html while rstripping each line. show_idlehelp - Create HelpWindow. Called in EditorWindow.help_dialog. + +_get_dochome() - Return path to docs on user's system if present, +otherwise return link to docs.python.org. """ +import os +import sys from html.parser import HTMLParser from os.path import abspath, dirname, isfile, join from platform import python_version @@ -289,6 +294,47 @@ def show_idlehelp(parent): return HelpWindow(parent, filename, 'IDLE Doc (%s)' % python_version()) +def _get_dochome(): + "Return path to local docs if present, otherwise link to docs.python.org." + + dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html') + if sys.platform.count('linux'): + # look for html docs in a couple of standard places + pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3] + if os.path.isdir('/var/www/html/python/'): # rpm package manager + dochome = '/var/www/html/python/index.html' + else: + basepath = '/usr/share/doc/' # dnf/apt package managers + dochome = os.path.join(basepath, pyver, 'Doc', 'index.html') + + elif sys.platform[:3] == 'win': + import winreg # Windows only, block only executed once. + docfile = '' + KEY = (rf"Software\Python\PythonCore\{sys.winver}" + r"\Help\Main Python Documentation") + try: + docfile = winreg.QueryValue(winreg.HKEY_CURRENT_USER, KEY) + except FileNotFoundError: + try: + docfile = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE, KEY) + except FileNotFoundError: + pass + if os.path.isfile(docfile): + dochome = docfile + elif sys.platform == 'darwin': + # documentation may be stored inside a python framework + dochome = os.path.join(sys.base_prefix, + 'Resources/English.lproj/Documentation/index.html') + dochome = os.path.normpath(dochome) + if os.path.isfile(dochome): + if sys.platform == 'darwin': + # Safari requires real file:-URLs + return 'file://' + dochome + return dochome + else: + return "https://docs.python.org/%d.%d/" % sys.version_info[:2] + + if __name__ == '__main__': from unittest import main main('idlelib.idle_test.test_help', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/__init__.py b/Lib/idlelib/idle_test/__init__.py index 79b5d102dd7da5..8eed2699c41211 100644 --- a/Lib/idlelib/idle_test/__init__.py +++ b/Lib/idlelib/idle_test/__init__.py @@ -20,8 +20,4 @@ def load_tests(loader, standard_tests, pattern): pattern='test_*.py', # Insert here. top_level_dir=top_dir) standard_tests.addTests(module_tests) -## module_tests = loader.discover(start_dir=this_dir, -## pattern='test_*.py', # Insert here. -## top_level_dir=top_dir) -## standard_tests.addTests(module_tests) return standard_tests diff --git a/Lib/idlelib/idle_test/test_colorizer.py b/Lib/idlelib/idle_test/test_colorizer.py index 308bc389384d33..fb6ee825086750 100644 --- a/Lib/idlelib/idle_test/test_colorizer.py +++ b/Lib/idlelib/idle_test/test_colorizer.py @@ -36,6 +36,7 @@ async def f(): await g() # All valid prefixes for unicode and byte strings should be colored. r'x', u'x', R'x', U'x', f'x', F'x' fr'x', Fr'x', fR'x', FR'x', rf'x', rF'x', Rf'x', RF'x' + tr'x', Tr'x', tR'x', TR'x', rt'x', rT'x', Rt'x', RT'x' b'x',B'x', br'x',Br'x',bR'x',BR'x', rb'x', rB'x',Rb'x',RB'x' # Invalid combinations of legal characters should be half colored. ur'x', ru'x', uf'x', fu'x', UR'x', ufr'x', rfu'x', xf'x', fx'x' @@ -390,19 +391,19 @@ def test_recolorize_main(self, mock_notify): ('6.0', ('KEYWORD',)), ('6.10', ('DEFINITION',)), ('6.11', ()), ('8.0', ('STRING',)), ('8.4', ()), ('8.5', ('STRING',)), ('8.12', ()), ('8.14', ('STRING',)), - ('19.0', ('KEYWORD',)), - ('20.4', ('KEYWORD',)), ('20.16', ('KEYWORD',)),# ('20.19', ('KEYWORD',)), - #('22.4', ('KEYWORD',)), ('22.10', ('KEYWORD',)), ('22.14', ('KEYWORD',)), ('22.19', ('STRING',)), - #('23.12', ('KEYWORD',)), - ('24.8', ('KEYWORD',)), - ('25.4', ('KEYWORD',)), ('25.9', ('KEYWORD',)), - ('25.11', ('KEYWORD',)), ('25.15', ('STRING',)), - ('25.19', ('KEYWORD',)), ('25.22', ()), - ('25.24', ('KEYWORD',)), ('25.29', ('BUILTIN',)), ('25.37', ('KEYWORD',)), - ('26.4', ('KEYWORD',)), ('26.9', ('KEYWORD',)),# ('26.11', ('KEYWORD',)), ('26.14', (),), - ('27.25', ('STRING',)), ('27.38', ('STRING',)), - ('29.0', ('STRING',)), - ('30.1', ('STRING',)), + ('20.0', ('KEYWORD',)), + ('21.4', ('KEYWORD',)), ('21.16', ('KEYWORD',)),# ('21.19', ('KEYWORD',)), + #('23.4', ('KEYWORD',)), ('23.10', ('KEYWORD',)), ('23.14', ('KEYWORD',)), ('23.19', ('STRING',)), + #('24.12', ('KEYWORD',)), + ('25.8', ('KEYWORD',)), + ('26.4', ('KEYWORD',)), ('26.9', ('KEYWORD',)), + ('26.11', ('KEYWORD',)), ('26.15', ('STRING',)), + ('26.19', ('KEYWORD',)), ('26.22', ()), + ('26.24', ('KEYWORD',)), ('26.29', ('BUILTIN',)), ('26.37', ('KEYWORD',)), + ('27.4', ('KEYWORD',)), ('27.9', ('KEYWORD',)),# ('27.11', ('KEYWORD',)), ('27.14', (),), + ('28.25', ('STRING',)), ('28.38', ('STRING',)), + ('30.0', ('STRING',)), + ('31.1', ('STRING',)), # SYNC at the end of every line. ('1.55', ('SYNC',)), ('2.50', ('SYNC',)), ('3.34', ('SYNC',)), ) @@ -433,7 +434,7 @@ def test_recolorize_main(self, mock_notify): eq(text.tag_nextrange('STRING', '8.12'), ('8.14', '8.17')) eq(text.tag_nextrange('STRING', '8.17'), ('8.19', '8.26')) eq(text.tag_nextrange('SYNC', '8.0'), ('8.26', '9.0')) - eq(text.tag_nextrange('SYNC', '30.0'), ('30.10', '32.0')) + eq(text.tag_nextrange('SYNC', '31.0'), ('31.10', '33.0')) def _assert_highlighting(self, source, tag_ranges): """Check highlighting of a given piece of code. @@ -541,6 +542,24 @@ def test_case_soft_keyword(self): self._assert_highlighting('case _:', {'KEYWORD': [('1.0', '1.4'), ('1.5', '1.6')]}) + def test_lazy_soft_keyword(self): + # lazy followed by import + self._assert_highlighting('lazy import foo', + {'KEYWORD': [('1.0', '1.4'), + ('1.5', '1.11')]}) + self._assert_highlighting(' lazy import foo', + {'KEYWORD': [('1.4', '1.8'), + ('1.9', '1.15')]}) + + # lazy followed by from + self._assert_highlighting('lazy from foo import bar', + {'KEYWORD': [('1.0', '1.4'), ('1.5', '1.9'), + ('1.14', '1.20')]}) + + # lazy not followed by import/from (not highlighted) + self._assert_highlighting('lazy = 1', {}) + self._assert_highlighting('lazy foo', {}) + def test_long_multiline_string(self): source = textwrap.dedent('''\ """a diff --git a/Lib/idlelib/idle_test/test_outwin.py b/Lib/idlelib/idle_test/test_outwin.py index 81f4aad7e95e95..0f13363f84f361 100644 --- a/Lib/idlelib/idle_test/test_outwin.py +++ b/Lib/idlelib/idle_test/test_outwin.py @@ -1,6 +1,7 @@ "Test outwin, coverage 76%." from idlelib import outwin +import platform import sys import unittest from test.support import requires @@ -41,7 +42,7 @@ def test_ispythonsource(self): self.assertFalse(w.ispythonsource(__file__)) def test_window_title(self): - self.assertEqual(self.window.top.title(), 'Output') + self.assertEqual(self.window.top.title(), 'Output' + ' (%s)' % platform.python_version()) def test_maybesave(self): w = self.window diff --git a/Lib/idlelib/idle_test/test_run.py b/Lib/idlelib/idle_test/test_run.py index 83ecbffa2a197e..57bf5559c0fa88 100644 --- a/Lib/idlelib/idle_test/test_run.py +++ b/Lib/idlelib/idle_test/test_run.py @@ -44,7 +44,7 @@ def __eq__(self, other): "Or did you forget to import 'abc'?\n"), ('int.reel', AttributeError, "type object 'int' has no attribute 'reel'. " - "Did you mean: 'real'?\n"), + "Did you mean '.real' instead of '.reel'?\n"), ) @force_not_colorized @@ -82,6 +82,99 @@ def test_get_multiple_message(self, mock): subtests += 1 self.assertEqual(subtests, len(data2)) # All subtests ran? + def _capture_exception(self): + """Call run.print_exception() and return its stderr output.""" + with captured_stderr() as output: + with mock.patch.object(run, 'cleanup_traceback') as ct: + ct.side_effect = lambda t, e: t + run.print_exception() + return output.getvalue() + + @force_not_colorized + def test_print_exception_group_nested(self): + try: + try: + raise ExceptionGroup('inner', [ValueError('v1')]) + except ExceptionGroup as inner: + raise ExceptionGroup('outer', [inner, TypeError('t1')]) + except ExceptionGroup: + tb = self._capture_exception() + + self.assertIn('ExceptionGroup: outer (2 sub-exceptions)', tb) + self.assertIn('ExceptionGroup: inner', tb) + self.assertIn('ValueError: v1', tb) + self.assertIn('TypeError: t1', tb) + # Verify tree structure characters. + self.assertIn('+-+---------------- 1 ----------------', tb) + self.assertIn('+---------------- 2 ----------------', tb) + self.assertIn('+------------------------------------', tb) + + @force_not_colorized + def test_print_exception_group_chaining(self): + # __cause__ on a sub-exception exercises the prefixed + # chaining-message path (margin chars on separator lines). + sub = TypeError('t1') + sub.__cause__ = ValueError('original') + try: + raise ExceptionGroup('eg1', [sub]) + except ExceptionGroup: + tb = self._capture_exception() + self.assertIn('ValueError: original', tb) + self.assertIn('| The above exception was the direct cause', tb) + self.assertIn('ExceptionGroup: eg1', tb) + + # __context__ (implicit chaining) on a sub-exception. + sub = TypeError('t2') + sub.__context__ = ValueError('first') + try: + raise ExceptionGroup('eg2', [sub]) + except ExceptionGroup: + tb = self._capture_exception() + self.assertIn('ValueError: first', tb) + self.assertIn('| During handling of the above exception', tb) + self.assertIn('ExceptionGroup: eg2', tb) + + @force_not_colorized + def test_print_exception_group_seen(self): + shared = ValueError('shared') + try: + raise ExceptionGroup('eg', [shared, shared]) + except ExceptionGroup: + tb = self._capture_exception() + + self.assertIn('ValueError: shared', tb) + self.assertIn('', tb) + + @force_not_colorized + def test_print_exception_group_max_width(self): + excs = [ValueError(f'v{i}') for i in range(20)] + try: + raise ExceptionGroup('eg', excs) + except ExceptionGroup: + tb = self._capture_exception() + + self.assertIn('+---------------- 15 ----------------', tb) + self.assertIn('+---------------- ... ----------------', tb) + self.assertIn('and 5 more exceptions', tb) + self.assertNotIn('+---------------- 16 ----------------', tb) + + @force_not_colorized + def test_print_exception_group_max_depth(self): + def make_nested(depth): + if depth == 0: + return ValueError('leaf') + return ExceptionGroup(f'level{depth}', + [make_nested(depth - 1)]) + + try: + raise make_nested(15) + except ExceptionGroup: + tb = self._capture_exception() + + self.assertIn('... (max_group_depth is 10)', tb) + self.assertIn('ExceptionGroup: level15', tb) + self.assertNotIn('ValueError: leaf', tb) + # StdioFile tests. class S(str): diff --git a/Lib/idlelib/idle_test/test_squeezer.py b/Lib/idlelib/idle_test/test_squeezer.py index 86c5d41b629719..86c21f00bb8d00 100644 --- a/Lib/idlelib/idle_test/test_squeezer.py +++ b/Lib/idlelib/idle_test/test_squeezer.py @@ -170,6 +170,7 @@ def test_write_not_stdout(self): def test_write_stdout(self): """Test Squeezer's overriding of the EditorWindow's write() method.""" + requires('gui') editwin = self.make_mock_editor_window() for text in ['', 'TEXT']: diff --git a/Lib/idlelib/idle_test/test_zzdummy.py b/Lib/idlelib/idle_test/test_zzdummy.py index 209d8564da0664..14c343cf9b3087 100644 --- a/Lib/idlelib/idle_test/test_zzdummy.py +++ b/Lib/idlelib/idle_test/test_zzdummy.py @@ -38,38 +38,8 @@ def __init__(self, root, text): self.text.undo_block_stop = mock.Mock() -class ZZDummyTest(unittest.TestCase): - - @classmethod - def setUpClass(cls): - requires('gui') - root = cls.root = Tk() - root.withdraw() - text = cls.text = Text(cls.root) - cls.editor = DummyEditwin(root, text) - zzdummy.idleConf.userCfg = testcfg - - @classmethod - def tearDownClass(cls): - zzdummy.idleConf.userCfg = usercfg - del cls.editor, cls.text - cls.root.update_idletasks() - for id in cls.root.tk.call('after', 'info'): - cls.root.after_cancel(id) # Need for EditorWindow. - cls.root.destroy() - del cls.root - - def setUp(self): - text = self.text - text.insert('1.0', code_sample) - text.undo_block_start.reset_mock() - text.undo_block_stop.reset_mock() - zz = self.zz = zzdummy.ZzDummy(self.editor) - zzdummy.ZzDummy.ztext = '# ignore #' - - def tearDown(self): - self.text.delete('1.0', 'end') - del self.zz +class ZZDummyMixin: + """Shared tests for ZzDummy with default and user configs.""" def checklines(self, text, value): # Verify that there are lines being checked. @@ -89,7 +59,8 @@ def test_init(self): def test_reload(self): self.assertEqual(self.zz.ztext, '# ignore #') - testcfg['extensions'].SetOption('ZzDummy', 'z-text', 'spam') + zzdummy.idleConf.userCfg['extensions'].SetOption( + 'ZzDummy', 'z-text', 'spam') zzdummy.ZzDummy.reload() self.assertEqual(self.zz.ztext, 'spam') @@ -148,5 +119,75 @@ def test_roundtrip(self): self.assertEqual(text.get('1.0', 'end-1c'), code_sample) +class ZZDummyTest(ZZDummyMixin, unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + root = cls.root = Tk() + root.withdraw() + text = cls.text = Text(cls.root) + cls.editor = DummyEditwin(root, text) + zzdummy.idleConf.userCfg = testcfg + + @classmethod + def tearDownClass(cls): + zzdummy.idleConf.userCfg = usercfg + del cls.editor, cls.text + cls.root.update_idletasks() + for id in cls.root.tk.call('after', 'info'): + cls.root.after_cancel(id) # Need for EditorWindow. + cls.root.destroy() + del cls.root + + def setUp(self): + text = self.text + text.insert('1.0', code_sample) + text.undo_block_start.reset_mock() + text.undo_block_stop.reset_mock() + zz = self.zz = zzdummy.ZzDummy(self.editor) + zzdummy.ZzDummy.ztext = '# ignore #' + + def tearDown(self): + self.text.delete('1.0', 'end') + del self.zz + + def test_exists(self): + conf = zzdummy.idleConf + self.assertEqual( + conf.GetSectionList('user', 'extensions'), []) + self.assertEqual( + conf.GetSectionList('default', 'extensions'), + ['AutoComplete', 'CodeContext', 'FormatParagraph', + 'ParenMatch', 'ZzDummy', 'ZzDummy_cfgBindings', + 'ZzDummy_bindings']) + self.assertIn("ZzDummy", conf.GetExtensions(False)) + self.assertNotIn("ZzDummy", conf.GetExtensions()) + self.assertEqual( + conf.GetExtensionKeys("ZzDummy"), {}) + self.assertEqual( + conf.GetExtensionBindings("ZzDummy"), + {'<>': ['']}) + + def test_exists_user(self): + conf = zzdummy.idleConf + conf.userCfg["extensions"].read_dict({ + "ZzDummy": {'enable': 'True'} + }) + self.assertEqual( + conf.GetSectionList('user', 'extensions'), + ["ZzDummy"]) + self.assertIn("ZzDummy", conf.GetExtensions()) + self.assertEqual( + conf.GetExtensionKeys("ZzDummy"), + {'<>': ['']}) + self.assertEqual( + conf.GetExtensionBindings("ZzDummy"), + {'<>': [''], + '<>': ['']}) + # Restore + conf.userCfg["extensions"].remove_section("ZzDummy") + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_zzdummy_user.py b/Lib/idlelib/idle_test/test_zzdummy_user.py new file mode 100644 index 00000000000000..1d3f2ac3096fb0 --- /dev/null +++ b/Lib/idlelib/idle_test/test_zzdummy_user.py @@ -0,0 +1,108 @@ +"Test zzdummy with user config, coverage 100%." + +from idlelib import zzdummy +import unittest +from test.support import requires +from tkinter import Tk, Text +from idlelib import config + +from idlelib.idle_test.test_zzdummy import ( + ZZDummyMixin, DummyEditwin, code_sample, +) + + +real_usercfg = zzdummy.idleConf.userCfg +test_usercfg = { + 'main': config.IdleUserConfParser(''), + 'highlight': config.IdleUserConfParser(''), + 'keys': config.IdleUserConfParser(''), + 'extensions': config.IdleUserConfParser(''), +} +test_usercfg["extensions"].read_dict({ + "ZzDummy": {'enable': 'True', 'enable_shell': 'False', + 'enable_editor': 'True', 'z-text': 'Z'}, + "ZzDummy_cfgBindings": { + 'z-in': ''}, + "ZzDummy_bindings": { + 'z-out': ''}, +}) +real_defaultcfg = zzdummy.idleConf.defaultCfg +test_defaultcfg = { + 'main': config.IdleUserConfParser(''), + 'highlight': config.IdleUserConfParser(''), + 'keys': config.IdleUserConfParser(''), + 'extensions': config.IdleUserConfParser(''), +} +test_defaultcfg["extensions"].read_dict({ + "AutoComplete": {'popupwait': '2000'}, + "CodeContext": {'maxlines': '15'}, + "FormatParagraph": {'max-width': '72'}, + "ParenMatch": {'style': 'expression', + 'flash-delay': '500', 'bell': 'True'}, +}) +test_defaultcfg["main"].read_dict({ + "Theme": {"default": 1, "name": "IDLE Classic", "name2": ""}, + "Keys": {"default": 1, "name": "IDLE Classic", "name2": ""}, +}) +for key in ("keys",): + real_default = real_defaultcfg[key] + value = {name: dict(real_default[name]) for name in real_default} + test_defaultcfg[key].read_dict(value) + + +class ZZDummyTest(ZZDummyMixin, unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + root = cls.root = Tk() + root.withdraw() + text = cls.text = Text(cls.root) + cls.editor = DummyEditwin(root, text) + zzdummy.idleConf.userCfg = test_usercfg + zzdummy.idleConf.defaultCfg = test_defaultcfg + + @classmethod + def tearDownClass(cls): + zzdummy.idleConf.defaultCfg = real_defaultcfg + zzdummy.idleConf.userCfg = real_usercfg + del cls.editor, cls.text + cls.root.update_idletasks() + for id in cls.root.tk.call('after', 'info'): + cls.root.after_cancel(id) # Need for EditorWindow. + cls.root.destroy() + del cls.root + + def setUp(self): + text = self.text + text.insert('1.0', code_sample) + text.undo_block_start.reset_mock() + text.undo_block_stop.reset_mock() + zz = self.zz = zzdummy.ZzDummy(self.editor) + zzdummy.ZzDummy.ztext = '# ignore #' + + def tearDown(self): + self.text.delete('1.0', 'end') + del self.zz + + def test_exists(self): + self.assertEqual( + zzdummy.idleConf.GetSectionList('user', 'extensions'), + ['ZzDummy', 'ZzDummy_cfgBindings', 'ZzDummy_bindings']) + self.assertEqual( + zzdummy.idleConf.GetSectionList('default', 'extensions'), + ['AutoComplete', 'CodeContext', 'FormatParagraph', + 'ParenMatch']) + self.assertIn("ZzDummy", + zzdummy.idleConf.GetExtensions()) + self.assertEqual( + zzdummy.idleConf.GetExtensionKeys("ZzDummy"), + {'<>': ['']}) + self.assertEqual( + zzdummy.idleConf.GetExtensionBindings("ZzDummy"), + {'<>': [''], + '<>': ['']}) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py index 464126e2df0668..fc502f7fde1780 100644 --- a/Lib/idlelib/iomenu.py +++ b/Lib/idlelib/iomenu.py @@ -61,6 +61,7 @@ def set_filename_change_hook(self, hook): self.filename_change_hook = hook filename = None + file_timestamp = None dirname = None def set_filename(self, filename): @@ -127,6 +128,7 @@ def loadfile(self, filename): chars = f.read() fileencoding = f.encoding eol_convention = f.newlines + file_timestamp = self.getmtime(filename) converted = False except (UnicodeDecodeError, SyntaxError): # Wait for the editor window to appear @@ -142,6 +144,7 @@ def loadfile(self, filename): chars = f.read() fileencoding = f.encoding eol_convention = f.newlines + file_timestamp = self.getmtime(filename) converted = True except OSError as err: messagebox.showerror("I/O Error", str(err), parent=self.text) @@ -170,6 +173,7 @@ def loadfile(self, filename): self.text.insert("1.0", chars) self.reset_undo() self.set_filename(filename) + self.file_timestamp = file_timestamp if converted: # We need to save the conversion results first # before being able to execute the code @@ -206,7 +210,26 @@ def save(self, event): if not self.filename: self.save_as(event) else: + # Check the time of most recent content modification so the + # user doesn't accidentally overwrite a newer version of the file. + try: + file_timestamp = self.getmtime(self.filename) + except OSError: + pass + else: + if self.file_timestamp != file_timestamp: + confirm = messagebox.askokcancel( + title="File has changed", + message=( + "The file has changed on disk since reading it!\n\n" + "Do you really want to overwrite it?"), + default=messagebox.CANCEL, + parent=self.text) + if not confirm: + return "break" + if self.writefile(self.filename): + self.file_timestamp = self.getmtime(self.filename) self.set_saved(True) try: self.editwin.store_file_breaks() @@ -219,6 +242,7 @@ def save_as(self, event): filename = self.asksavefile() if filename: if self.writefile(filename): + self.file_timestamp = self.getmtime(filename) self.set_filename(filename) self.set_saved(1) try: @@ -251,6 +275,9 @@ def writefile(self, filename): parent=self.text) return False + def getmtime(self, filename): + return os.stat(filename).st_mtime + def fixnewlines(self): """Return text with os eols. diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 74a0e03994f69a..b80c8e56c92810 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -22,7 +22,6 @@ import linecache import os import os.path -from platform import python_version import re import socket import subprocess @@ -499,7 +498,6 @@ def restart_subprocess(self, with_cwd=False, filename=''): self.rpcclt.close() self.terminate_subprocess() console = self.tkconsole - was_executing = console.executing console.executing = False self.spawn_subprocess() try: @@ -841,7 +839,7 @@ def display_executing_dialog(self): class PyShell(OutputWindow): from idlelib.squeezer import Squeezer - shell_title = "IDLE Shell " + python_version() + shell_title = "IDLE Shell" # Override classes ColorDelegator = ModifiedColorDelegator diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index 57230e2aaca66d..5f9bdc031e544b 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -289,8 +289,6 @@ def askfilename(self, filetypes, initdir, initfile): # htest # def browse_file(self): filetypes = [ ("HTML Files", "*.htm *.html", "TEXT"), - ("PDF Files", "*.pdf", "TEXT"), - ("Windows Help Files", "*.chm"), ("Text Files", "*.txt", "TEXT"), ("All Files", "*")] path = self.pathvar.get() diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index a30db99a619a93..e1c40fee8f4805 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -249,31 +249,94 @@ def print_exception(): sys.last_type, sys.last_value, sys.last_traceback = excinfo sys.last_exc = val seen = set() + exclude = ("run.py", "rpc.py", "threading.py", "queue.py", + "debugger_r.py", "bdb.py") + max_group_width = 15 + max_group_depth = 10 + group_depth = 0 + + def print_exc_group(typ, exc, tb, prefix=""): + nonlocal group_depth + group_depth += 1 + prefix2 = prefix or " " + if group_depth > max_group_depth: + print(f"{prefix2}| ... (max_group_depth is {max_group_depth})", + file=efile) + group_depth -= 1 + return + if tb: + if not prefix: + print(" + Exception Group Traceback (most recent call last):", file=efile) + else: + print(f"{prefix}| Exception Group Traceback (most recent call last):", file=efile) + tbe = traceback.extract_tb(tb) + cleanup_traceback(tbe, exclude) + for line in traceback.format_list(tbe): + for subline in line.rstrip().splitlines(): + print(f"{prefix2}| {subline}", file=efile) + lines = get_message_lines(typ, exc, tb) + for line in lines: + print(f"{prefix2}| {line}", end="", file=efile) + num_excs = len(exc.exceptions) + if num_excs <= max_group_width: + n = num_excs + else: + n = max_group_width + 1 + for i, sub in enumerate(exc.exceptions[:n], 1): + truncated = (i > max_group_width) + first_line_pre = "+-" if i == 1 else " " + title = str(i) if not truncated else '...' + print(f"{prefix2}{first_line_pre}+---------------- {title} ----------------", file=efile) + if truncated: + remaining = num_excs - max_group_width + plural = 's' if remaining > 1 else '' + print(f"{prefix2} | and {remaining} more exception{plural}", + file=efile) + need_print_underline = True + elif id(sub) not in seen: + if not prefix: + print_exc(type(sub), sub, sub.__traceback__, " ") + else: + print_exc(type(sub), sub, sub.__traceback__, prefix + " ") + need_print_underline = not isinstance(sub, BaseExceptionGroup) + else: + print(f"{prefix2} | ", file=efile) + need_print_underline = True + if need_print_underline and i == n: + print(f"{prefix2} +------------------------------------", file=efile) + group_depth -= 1 - def print_exc(typ, exc, tb): + def print_exc(typ, exc, tb, prefix=""): seen.add(id(exc)) context = exc.__context__ cause = exc.__cause__ + prefix2 = f"{prefix}| " if prefix else "" if cause is not None and id(cause) not in seen: - print_exc(type(cause), cause, cause.__traceback__) - print("\nThe above exception was the direct cause " - "of the following exception:\n", file=efile) + print_exc(type(cause), cause, cause.__traceback__, prefix) + print(f"{prefix2}\n{prefix2}The above exception was the direct cause " + f"of the following exception:\n{prefix2}", file=efile) elif (context is not None and not exc.__suppress_context__ and id(context) not in seen): - print_exc(type(context), context, context.__traceback__) - print("\nDuring handling of the above exception, " - "another exception occurred:\n", file=efile) - if tb: - tbe = traceback.extract_tb(tb) - print('Traceback (most recent call last):', file=efile) - exclude = ("run.py", "rpc.py", "threading.py", "queue.py", - "debugger_r.py", "bdb.py") - cleanup_traceback(tbe, exclude) - traceback.print_list(tbe, file=efile) - lines = get_message_lines(typ, exc, tb) - for line in lines: - print(line, end='', file=efile) + print_exc(type(context), context, context.__traceback__, prefix) + print(f"{prefix2}\n{prefix2}During handling of the above exception, " + f"another exception occurred:\n{prefix2}", file=efile) + if isinstance(exc, BaseExceptionGroup): + print_exc_group(typ, exc, tb, prefix=prefix) + else: + if tb: + print(f"{prefix2}Traceback (most recent call last):", file=efile) + tbe = traceback.extract_tb(tb) + cleanup_traceback(tbe, exclude) + if prefix: + for line in traceback.format_list(tbe): + for subline in line.rstrip().splitlines(): + print(f"{prefix}| {subline}", file=efile) + else: + traceback.print_list(tbe, file=efile) + lines = get_message_lines(typ, exc, tb) + for line in lines: + print(f"{prefix2}{line}", end="", file=efile) print_exc(typ, val, tb) diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index 23f0f4cb5027ec..0f719a06883ad7 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -129,8 +129,8 @@ def __init__(self, parent, title, contents, modal=True, wrap=WORD, self.title(title) self.viewframe = ViewFrame(self, contents, wrap=wrap) self.protocol("WM_DELETE_WINDOW", self.ok) - self.button_ok = button_ok = Button(self, text='Close', - command=self.ok, takefocus=False) + self.button_ok = Button(self, text='Close', + command=self.ok, takefocus=False) self.viewframe.pack(side='top', expand=True, fill='both') self.is_modal = modal diff --git a/Lib/imaplib.py b/Lib/imaplib.py index 2c3925958d011b..497b5a60cecb08 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -21,8 +21,6 @@ # GET/SETANNOTATION contributed by Tomas Lindroos June 2005. # IDLE contributed by Forest August 2024. -__version__ = "2.59" - import binascii, errno, random, re, socket, subprocess, sys, time, calendar from datetime import datetime, timezone, timedelta from io import DEFAULT_BUFFER_SIZE @@ -131,7 +129,7 @@ # We compile these in _mode_xxx. _Literal = br'.*{(?P\d+)}$' _Untagged_status = br'\* (?P\d+) (?P[A-Z-]+)( (?P.*))?' - +_control_chars = re.compile(b'[\x00-\x1F\x7F]') class IMAP4: @@ -247,7 +245,6 @@ def _connect(self): self._cmd_log_idx = 0 self._cmd_log = {} # Last '_cmd_log_len' interactions if self.debug >= 1: - self._mesg('imaplib version %s' % __version__) self._mesg('new IMAP4 connection, tag=%s' % self.tagpre) self.welcome = self._get_response() @@ -316,25 +313,34 @@ def open(self, host='', port=IMAP4_PORT, timeout=None): self.host = host self.port = port self.sock = self._create_socket(timeout) - self._file = self.sock.makefile('rb') - + # Since IMAP4 implements its own read() and readline() buffering, + # the '_imaplib_file' attribute is unused. Nonetheless it is kept + # and exposed solely for backward compatibility purposes. + self._imaplib_file = self.sock.makefile('rb') @property def file(self): - # The old 'file' attribute is no longer used now that we do our own - # read() and readline() buffering, with which it conflicts. - # As an undocumented interface, it should never have been accessed by - # external code, and therefore does not warrant deprecation. - # Nevertheless, we provide this property for now, to avoid suddenly - # breaking any code in the wild that might have been using it in a - # harmless way. import warnings - warnings.warn( - 'IMAP4.file is unsupported, can cause errors, and may be removed.', - RuntimeWarning, - stacklevel=2) - return self._file + warnings._deprecated("IMAP4.file", remove=(3, 19)) + return self._imaplib_file + @file.setter + def file(self, value): + import warnings + warnings._deprecated("IMAP4.file", remove=(3, 19)) + # Ideally, we would want to close the previous file, + # but since we do not know how subclasses will use + # that setter, it is probably better to leave it to + # the caller. + self._imaplib_file = value + + def _close_imaplib_file(self): + file = self._imaplib_file + if file is not None: + try: + file.close() + except OSError: + pass def read(self, size): """Read 'size' bytes from remote.""" @@ -420,7 +426,7 @@ def send(self, data): def shutdown(self): """Close I/O established in "open".""" - self._file.close() + self._close_imaplib_file() try: self.sock.shutdown(socket.SHUT_RDWR) except OSError as exc: @@ -497,8 +503,6 @@ def append(self, mailbox, flags, date_time, message): else: date_time = None literal = MapCRLF.sub(CRLF, message) - if self.utf8_enabled: - literal = b'UTF8 (' + literal + b')' self.literal = literal return self._simple_command(name, mailbox, flags, date_time) @@ -708,7 +712,7 @@ def login(self, user, password): """ typ, dat = self._simple_command('LOGIN', user, self._quote(password)) if typ != 'OK': - raise self.error(dat[-1]) + raise self.error(dat[-1].decode('UTF-8', 'replace')) self.state = 'AUTH' return typ, dat @@ -725,9 +729,17 @@ def login_cram_md5(self, user, password): def _CRAM_MD5_AUTH(self, challenge): """ Authobject to use with CRAM-MD5 authentication. """ import hmac - pwd = (self.password.encode('utf-8') if isinstance(self.password, str) - else self.password) - return self.user + " " + hmac.HMAC(pwd, challenge, 'md5').hexdigest() + + if isinstance(self.password, str): + password = self.password.encode('utf-8') + else: + password = self.password + + try: + authcode = hmac.HMAC(password, challenge, 'md5') + except ValueError: # HMAC-MD5 is not available + raise self.error("CRAM-MD5 authentication is not supported") + return f"{self.user} {authcode.hexdigest()}" def logout(self): @@ -805,7 +817,7 @@ def proxyauth(self, user): """ name = 'PROXYAUTH' - return self._simple_command('PROXYAUTH', user) + return self._simple_command(name, user) def rename(self, oldmailbox, newmailbox): @@ -918,9 +930,10 @@ def starttls(self, ssl_context=None): ssl_context = ssl._create_stdlib_context() typ, dat = self._simple_command(name) if typ == 'OK': + self._close_imaplib_file() self.sock = ssl_context.wrap_socket(self.sock, server_hostname=self.host) - self._file = self.sock.makefile('rb') + self._imaplib_file = self.sock.makefile('rb') self._tls_established = True self._get_capabilities() else: @@ -1102,6 +1115,8 @@ def _command(self, name, *args): if arg is None: continue if isinstance(arg, str): arg = bytes(arg, self._encoding) + if _control_chars.search(arg): + raise ValueError("Control characters not allowed in commands") data = data + b' ' + arg literal = self.literal @@ -1111,7 +1126,11 @@ def _command(self, name, *args): literator = literal else: literator = None - data = data + bytes(' {%s}' % len(literal), self._encoding) + if self.utf8_enabled: + data = data + bytes(' UTF8 (~{%s}' % len(literal), self._encoding) + literal = literal + b')' + else: + data = data + bytes(' {%s}' % len(literal), self._encoding) if __debug__: if self.debug >= 4: @@ -1303,7 +1322,7 @@ def _get_tagged_response(self, tag, expect_bye=False): try: self._get_response() - except self.abort as val: + except self.abort: if __debug__: if self.debug >= 1: self.print_log() @@ -1671,7 +1690,7 @@ def open(self, host=None, port=None, timeout=None): self.host = None # For compatibility with parent class self.port = None self.sock = None - self._file = None + self._imaplib_file = None self.process = subprocess.Popen(self.command, bufsize=DEFAULT_BUFFER_SIZE, stdin=subprocess.PIPE, stdout=subprocess.PIPE, @@ -1860,7 +1879,7 @@ def Time2Internaldate(date_time): try: optlist, args = getopt.getopt(sys.argv[1:], 'd:s:') - except getopt.error as val: + except getopt.error: optlist, args = (), () stream_command = None @@ -1955,3 +1974,12 @@ def run(cmd, args): ''' % sys.argv[0]) raise + + +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "2.60" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/importlib/_abc.py b/Lib/importlib/_abc.py index 693b466112638f..2598036dac4918 100644 --- a/Lib/importlib/_abc.py +++ b/Lib/importlib/_abc.py @@ -19,21 +19,3 @@ def create_module(self, spec): # We don't define exec_module() here since that would break # hasattr checks we do to support backward compatibility. - - def load_module(self, fullname): - """Return the loaded module. - - The module must be added to sys.modules and have import-related - attributes set properly. The fullname is a str. - - ImportError is raised on failure. - - This method is deprecated in favor of loader.exec_module(). If - exec_module() exists then it is used to provide a backwards-compatible - functionality for this method. - - """ - if not hasattr(self, 'exec_module'): - raise ImportError - # Warning implemented in _load_module_shim(). - return _bootstrap._load_module_shim(self, fullname) diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 499da1e04efea8..eb1686a5c8217c 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -20,12 +20,6 @@ # reference any injected objects! This includes not only global code but also # anything specified at the class level. -def _object_name(obj): - try: - return obj.__qualname__ - except AttributeError: - return type(obj).__qualname__ - # Bootstrap-related code ###################################################### # Modules injected manually by _setup() @@ -424,6 +418,64 @@ def __exit__(self, *args, **kwargs): self._lock.release() +def _get_module_chain(name): + """Return the chain of dotted-name prefixes from root to leaf. + + For example: 'a.b.c' -> ['a', 'a.b', 'a.b.c'] + """ + parts = name.split('.') + return ['.'.join(parts[:i+1]) for i in range(len(parts))] + + +class _HierarchicalLockManager: + """Manages acquisition of multiple module locks in hierarchical order. + + This prevents deadlocks by ensuring all threads acquire locks in the + same order (parent modules before child modules). + """ + + def __init__(self, name): + self._name = name + self._module_chain = _get_module_chain(name) + self._locks = [] + + def __enter__(self): + try: + for module_name in self._module_chain: + # Only acquire lock if module is not already fully loaded + module = sys.modules.get(module_name) + if (module is None or + getattr(getattr(module, "__spec__", None), + "_initializing", False)): + lock = _get_module_lock(module_name) + try: + lock.acquire() + except _DeadlockError: + if module_name == self._name: + raise + # The parent is being initialised by a thread that + # is (transitively) waiting on a lock we hold. + # Apply the same policy as _lock_unlock_module(): + # accept a partially-initialised parent for circular + # imports rather than failing the whole chain. + continue + self._locks.append((module_name, lock)) + except: + # __exit__ is not called when __enter__ raises (e.g. _DeadlockError + # on the leaf lock, or KeyboardInterrupt), so release whatever we + # already hold to avoid permanently leaking held module locks. + for module_name, lock in reversed(self._locks): + lock.release() + self._locks.clear() + raise + return self + + def __exit__(self, *args, **kwargs): + for module_name, lock in reversed(self._locks): + lock.release() + self._locks.clear() + + # The following two functions are for consumption by Python/import.c. def _get_module_lock(name): @@ -521,24 +573,6 @@ def _requires_frozen_wrapper(self, fullname): return _requires_frozen_wrapper -# Typically used by loader classes as a method replacement. -def _load_module_shim(self, fullname): - """Load the specified module into sys.modules and return it. - - This method is deprecated. Use loader.exec_module() instead. - - """ - msg = ("the load_module() method is deprecated and slated for removal in " - "Python 3.15; use exec_module() instead") - _warnings.warn(msg, DeprecationWarning) - spec = spec_from_loader(fullname, self) - if fullname in sys.modules: - module = sys.modules[fullname] - _exec(spec, module) - return sys.modules[fullname] - else: - return _load(spec) - # Module specifications ####################################################### def _module_repr(module): @@ -583,8 +617,7 @@ class ModuleSpec: `has_location` indicates that a spec's "origin" reflects a location. When this is True, `__file__` attribute of the module is set. - `cached` is the location of the cached bytecode file, if any. It - corresponds to the `__cached__` attribute. + `cached` is the location of the cached bytecode file, if any. `submodule_search_locations` is the sequence of path entries to search when importing submodules. If set, is_package should be @@ -717,10 +750,6 @@ def _spec_from_module(module, loader=None, origin=None): origin = getattr(loader, '_ORIGIN', None) if not origin and location is not None: origin = location - try: - cached = module.__cached__ - except AttributeError: - cached = None try: submodule_search_locations = list(module.__path__) except AttributeError: @@ -728,7 +757,7 @@ def _spec_from_module(module, loader=None, origin=None): spec = ModuleSpec(name, loader, origin=origin) spec._set_fileattr = False if location is None else (origin == location) - spec.cached = cached + spec.cached = None spec.submodule_search_locations = submodule_search_locations return spec @@ -763,8 +792,7 @@ def _init_module_attrs(spec, module, *, override=False): # should also be None for consistency. While a bit of a hack, # this is the best place to ensure this consistency. # - # See # https://docs.python.org/3/library/importlib.html#importlib.abc.Loader.load_module - # and bpo-32305 + # See bpo-32305 module.__file__ = None try: module.__loader__ = loader @@ -789,7 +817,7 @@ def _init_module_attrs(spec, module, *, override=False): module.__path__ = spec.submodule_search_locations except AttributeError: pass - # __file__/__cached__ + # __file__ if spec.has_location: if override or getattr(module, '__file__', None) is None: try: @@ -797,12 +825,6 @@ def _init_module_attrs(spec, module, *, override=False): except AttributeError: pass - if override or getattr(module, '__cached__', None) is None: - if spec.cached is not None: - try: - module.__cached__ = spec.cached - except AttributeError: - pass return module @@ -844,7 +866,7 @@ def _module_repr_from_spec(spec): return f'' -# Used by importlib.reload() and _load_module_shim(). +# Used by importlib.reload(). def _exec(spec, module): """Execute the spec's specified module in an existing module's namespace.""" name = spec.name @@ -860,13 +882,7 @@ def _exec(spec, module): _init_module_attrs(spec, module, override=True) else: _init_module_attrs(spec, module, override=True) - if not hasattr(spec.loader, 'exec_module'): - msg = (f"{_object_name(spec.loader)}.exec_module() not found; " - "falling back to load_module()") - _warnings.warn(msg, ImportWarning) - spec.loader.load_module(name) - else: - spec.loader.exec_module(module) + spec.loader.exec_module(module) finally: # Update the order of insertion into sys.modules for module # clean-up at shutdown. @@ -874,53 +890,8 @@ def _exec(spec, module): sys.modules[spec.name] = module return module - -def _load_backward_compatible(spec): - # It is assumed that all callers have been warned about using load_module() - # appropriately before calling this function. - try: - spec.loader.load_module(spec.name) - except: - if spec.name in sys.modules: - module = sys.modules.pop(spec.name) - sys.modules[spec.name] = module - raise - # The module must be in sys.modules at this point! - # Move it to the end of sys.modules. - module = sys.modules.pop(spec.name) - sys.modules[spec.name] = module - if getattr(module, '__loader__', None) is None: - try: - module.__loader__ = spec.loader - except AttributeError: - pass - if getattr(module, '__package__', None) is None: - try: - # Since module.__path__ may not line up with - # spec.submodule_search_paths, we can't necessarily rely - # on spec.parent here. - module.__package__ = module.__name__ - if not hasattr(module, '__path__'): - module.__package__ = spec.name.rpartition('.')[0] - except AttributeError: - pass - if getattr(module, '__spec__', None) is None: - try: - module.__spec__ = spec - except AttributeError: - pass - return module - def _load_unlocked(spec): # A helper for direct use by the import system. - if spec.loader is not None: - # Not a namespace package. - if not hasattr(spec.loader, 'exec_module'): - msg = (f"{_object_name(spec.loader)}.exec_module() not found; " - "falling back to load_module()") - _warnings.warn(msg, ImportWarning) - return _load_backward_compatible(spec) - module = module_from_spec(spec) # This must be done before putting the module in sys.modules @@ -954,8 +925,7 @@ def _load_unlocked(spec): return module -# A method used during testing of _load_unlocked() and by -# _load_module_shim(). +# A method used during testing of _load_unlocked(). def _load(spec): """Return a new module object, loaded by the spec's loader. @@ -1020,8 +990,6 @@ def is_package(cls, fullname): """Return False as built-in modules are never packages.""" return False - load_module = classmethod(_load_module_shim) - class FrozenImporter: @@ -1178,25 +1146,6 @@ def exec_module(module): code = _call_with_frames_removed(_imp.get_frozen_object, name) exec(code, module.__dict__) - @classmethod - def load_module(cls, fullname): - """Load a frozen module. - - This method is deprecated. Use exec_module() instead. - - """ - # Warning about deprecation implemented in _load_module_shim(). - module = _load_module_shim(cls, fullname) - info = _imp.find_frozen(fullname) - assert info is not None - _, ispkg, origname = info - module.__origname__ = origname - vars(module).pop('__file__', None) - if ispkg: - module.__path__ = [] - cls._fix_up_module(module) - return module - @classmethod @_requires_frozen def get_code(cls, fullname): @@ -1307,6 +1256,14 @@ def _sanity_check(name, package, level): def _find_and_load_unlocked(name, import_): path = None + sys.audit( + "import", + name, + path, + getattr(sys, "path", None), + getattr(sys, "meta_path", None), + getattr(sys, "path_hooks", None) + ) parent = name.rpartition('.')[0] parent_spec = None if parent: @@ -1351,6 +1308,12 @@ def _find_and_load_unlocked(name, import_): except AttributeError: msg = f"Cannot set an attribute on {parent!r} for child module {child!r}" _warnings.warn(msg, ImportWarning) + # Set attributes to lazy submodules on the module. + try: + _imp._set_lazy_attributes(module, name) + except Exception as e: + msg = f"Cannot set lazy attributes on {name!r}: {e!r}" + _warnings.warn(msg, ImportWarning) return module @@ -1365,7 +1328,13 @@ def _find_and_load(name, import_): module = sys.modules.get(name, _NEEDS_LOADING) if (module is _NEEDS_LOADING or getattr(getattr(module, "__spec__", None), "_initializing", False)): - with _ModuleLockManager(name): + + if '.' in name: + lock_manager = _HierarchicalLockManager(name) + else: + lock_manager = _ModuleLockManager(name) + + with lock_manager: module = sys.modules.get(name, _NEEDS_LOADING) if module is _NEEDS_LOADING: return _find_and_load_unlocked(name, import_) @@ -1375,6 +1344,14 @@ def _find_and_load(name, import_): # NOTE: because of this, initializing must be set *before* # putting the new module in sys.modules. _lock_unlock_module(name) + else: + # Verify the module is still in sys.modules. Another thread may have + # removed it (due to import failure) between our sys.modules.get() + # above and the _initializing check. If removed, we retry the import + # to preserve normal semantics: the caller gets the exception from + # the actual import failure rather than a synthetic error. + if sys.modules.get(name) is not module: + return _find_and_load(name, import_) if module is None: message = f'import of {name} halted; None in sys.modules' diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 8bcd741c446bd2..a1cb729efb7fef 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -208,12 +208,8 @@ def _write_atomic(path, data, mode=0o666): try: # We first write data to a temporary file, and then use os.replace() to # perform an atomic rename. - with _io.FileIO(fd, 'wb') as file: - bytes_written = file.write(data) - if bytes_written != len(data): - # Raise an OSError so the 'except' below cleans up the partially - # written file. - raise OSError("os.write() didn't write the full pyc file") + with _io.open(fd, 'wb') as file: + file.write(data) _os.replace(path_tmp, path) except OSError: try: @@ -240,7 +236,7 @@ def _write_atomic(path, data, mode=0o666): # Deprecated. DEBUG_BYTECODE_SUFFIXES = OPTIMIZED_BYTECODE_SUFFIXES = BYTECODE_SUFFIXES -def cache_from_source(path, debug_override=None, *, optimization=None): +def cache_from_source(path, *, optimization=None): """Given the path to a .py file, return the path to its .pyc file. The .py file does not need to exist; this simply returns the path to the @@ -251,20 +247,9 @@ def cache_from_source(path, debug_override=None, *, optimization=None): of the argument is taken and verified to be alphanumeric (else ValueError is raised). - The debug_override parameter is deprecated. If debug_override is not None, - a True value is the same as setting 'optimization' to the empty string - while a False value is equivalent to setting 'optimization' to '1'. - If sys.implementation.cache_tag is None then NotImplementedError is raised. """ - if debug_override is not None: - _warnings.warn('the debug_override parameter is deprecated; use ' - "'optimization' instead", DeprecationWarning) - if optimization is not None: - message = 'debug_override or optimization must be set to None' - raise TypeError(message) - optimization = '' if debug_override else 1 path = _os.fspath(path) head, tail = _path_split(path) base, sep, rest = tail.rpartition('.') @@ -297,7 +282,8 @@ def cache_from_source(path, debug_override=None, *, optimization=None): # Strip initial drive from a Windows path. We know we have an absolute # path here, so the second part of the check rules out a POSIX path that # happens to contain a colon at the second character. - if head[1] == ':' and head[0] not in path_separators: + # Slicing avoids issues with an empty (or short) `head`. + if head[1:2] == ':' and head[0:1] not in path_separators: head = head[2:] # Strip initial path separator from `head` to complete the conversion @@ -551,8 +537,7 @@ def decode_source(source_bytes): import tokenize # To avoid bootstrap issues. source_bytes_readline = _io.BytesIO(source_bytes).readline encoding = tokenize.detect_encoding(source_bytes_readline) - newline_decoder = _io.IncrementalNewlineDecoder(None, True) - return newline_decoder.decode(source_bytes.decode(encoding[0])) + return _io.TextIOWrapper(_io.BytesIO(source_bytes), encoding=encoding[0], newline=None).read() # Module specifications ####################################################### @@ -761,11 +746,6 @@ def exec_module(self, module): 'get_code() returns None') _bootstrap._call_with_frames_removed(exec, code, module.__dict__) - def load_module(self, fullname): - """This method is deprecated.""" - # Warning implemented in _load_module_shim(). - return _bootstrap._load_module_shim(self, fullname) - class SourceLoader(_LoaderBasics): @@ -818,13 +798,14 @@ def get_source(self, fullname): name=fullname) from exc return decode_source(source_bytes) - def source_to_code(self, data, path, *, _optimize=-1): + def source_to_code(self, data, path, fullname=None, *, _optimize=-1): """Return the code object compiled from source. The 'data' argument can be any object type that compile() supports. """ return _bootstrap._call_with_frames_removed(compile, data, path, 'exec', - dont_inherit=True, optimize=_optimize) + dont_inherit=True, optimize=_optimize, + module=fullname) def get_code(self, fullname): """Concrete implementation of InspectLoader.get_code. @@ -893,7 +874,7 @@ def get_code(self, fullname): source_path=source_path) if source_bytes is None: source_bytes = self.get_data(source_path) - code_object = self.source_to_code(source_bytes, source_path) + code_object = self.source_to_code(source_bytes, source_path, fullname) _bootstrap._verbose_message('code object from {}', source_path) if (not sys.dont_write_bytecode and bytecode_path is not None and source_mtime is not None): @@ -930,18 +911,6 @@ def __eq__(self, other): def __hash__(self): return hash(self.name) ^ hash(self.path) - @_check_name - def load_module(self, fullname): - """Load a module from a file. - - This method is deprecated. Use exec_module() instead. - - """ - # The only reason for this method is for the name check. - # Issue #14857: Avoid the zero-argument form of super so the implementation - # of that form can be updated without breaking the frozen module. - return super(FileLoader, self).load_module(fullname) - @_check_name def get_filename(self, fullname): """Return the path to the source file as found by the finder.""" @@ -949,7 +918,7 @@ def get_filename(self, fullname): def get_data(self, path): """Return the data from path as raw bytes.""" - if isinstance(self, (SourceLoader, ExtensionFileLoader)): + if isinstance(self, (SourceLoader, SourcelessFileLoader, ExtensionFileLoader)): with _io.open_code(str(path)) as file: return file.read() else: @@ -1085,12 +1054,18 @@ def get_filename(self, fullname): return self.path -class _NamespacePath: - """Represents a namespace package's path. It uses the module name - to find its parent module, and from there it looks up the parent's - __path__. When this changes, the module's own path is recomputed, - using path_finder. For top-level modules, the parent module's path - is sys.path.""" +class NamespacePath: + """Represents a namespace package's path. + + It uses the module *name* to find its parent module, and from there it looks + up the parent's __path__. When this changes, the module's own path is + recomputed, using *path_finder*. The initial value is set to *path*. + + For top-level modules, the parent module's path is sys.path. + + *path_finder* should be a callable with the same signature as + MetaPathFinder.find_spec((fullname, path, target=None) -> spec). + """ # When invalidate_caches() is called, this epoch is incremented # https://bugs.python.org/issue45703 @@ -1115,7 +1090,15 @@ def _find_parent_path_names(self): def _get_parent_path(self): parent_module_name, path_attr_name = self._find_parent_path_names() - return getattr(sys.modules[parent_module_name], path_attr_name) + try: + module = sys.modules[parent_module_name] + except KeyError as e: + raise ModuleNotFoundError( + f"{parent_module_name!r} must be imported before finding {self._name!r}.", + name=parent_module_name, + ) from e + else: + return getattr(module, path_attr_name) def _recalculate(self): # If the parent's path has changed, recalculate _path @@ -1144,7 +1127,7 @@ def __len__(self): return len(self._recalculate()) def __repr__(self): - return f'_NamespacePath({self._path!r})' + return f'NamespacePath({self._path!r})' def __contains__(self, item): return item in self._recalculate() @@ -1153,12 +1136,16 @@ def append(self, item): self._path.append(item) +# For backwards-compatibility for anyone desperate enough to get at the class back in the day. +_NamespacePath = NamespacePath + + # This class is actually exposed publicly in a namespace package's __loader__ # attribute, so it should be available through a non-private name. # https://github.com/python/cpython/issues/92054 class NamespaceLoader: def __init__(self, name, path, path_finder): - self._path = _NamespacePath(name, path, path_finder) + self._path = NamespacePath(name, path, path_finder) def is_package(self, fullname): return True @@ -1167,7 +1154,7 @@ def get_source(self, fullname): return '' def get_code(self, fullname): - return compile('', '', 'exec', dont_inherit=True) + return compile('', '', 'exec', dont_inherit=True, module=fullname) def create_module(self, spec): """Use default semantics for module creation.""" @@ -1175,18 +1162,6 @@ def create_module(self, spec): def exec_module(self, module): pass - def load_module(self, fullname): - """Load a namespace module. - - This method is deprecated. Use exec_module() instead. - - """ - # The import system never calls this method. - _bootstrap._verbose_message('namespace module loaded with path {!r}', - self._path) - # Warning implemented in _load_module_shim(). - return _bootstrap._load_module_shim(self, fullname) - def get_resource_reader(self, module): from importlib.readers import NamespaceReader return NamespaceReader(self._path) @@ -1213,9 +1188,9 @@ def invalidate_caches(): del sys.path_importer_cache[name] elif hasattr(finder, 'invalidate_caches'): finder.invalidate_caches() - # Also invalidate the caches of _NamespacePaths + # Also invalidate the caches of NamespacePaths # https://bugs.python.org/issue45703 - _NamespacePath._epoch += 1 + NamespacePath._epoch += 1 from importlib.metadata import MetadataPathFinder MetadataPathFinder.invalidate_caches() @@ -1301,13 +1276,30 @@ def find_spec(cls, fullname, path=None, target=None): # We found at least one namespace path. Return a spec which # can create the namespace package. spec.origin = None - spec.submodule_search_locations = _NamespacePath(fullname, namespace_path, cls._get_spec) + spec.submodule_search_locations = NamespacePath(fullname, namespace_path, cls._get_spec) return spec else: return None else: return spec + @classmethod + def discover(cls, parent=None): + if parent is None: + path = sys.path + elif parent.submodule_search_locations is None: + raise ValueError(f'{parent} is not a package module') + else: + path = parent.submodule_search_locations + + for entry in set(path): + if not isinstance(entry, str): + continue + if (finder := cls._path_importer_cache(entry)) is None: + continue + if discover := getattr(finder, 'discover', None): + yield from discover(parent) + @staticmethod def find_distributions(*args, **kwargs): """ @@ -1457,6 +1449,37 @@ def path_hook_for_FileFinder(path): return path_hook_for_FileFinder + def _find_children(self): + with _os.scandir(self.path) as scan_iterator: + while True: + try: + entry = next(scan_iterator) + if entry.name == _PYCACHE: + continue + # packages + if entry.is_dir() and '.' not in entry.name: + yield entry.name + # files + if entry.is_file(): + yield from { + entry.name.removesuffix(suffix) + for suffix, _ in self._loaders + if entry.name.endswith(suffix) + } + except OSError: + pass # ignore exceptions from next(scan_iterator) and os.DirEntry + except StopIteration: + break + + def discover(self, parent=None): + if parent and parent.submodule_search_locations is None: + raise ValueError(f'{parent} is not a package module') + + module_prefix = f'{parent.name}.' if parent else '' + for child_name in self._find_children(): + if spec := self.find_spec(module_prefix + child_name): + yield spec + def __repr__(self): return f'FileFinder({self.path!r})' @@ -1497,7 +1520,13 @@ def create_module(self, spec): ) # Ensure that the __file__ points at the .fwork location - module.__file__ = path + try: + module.__file__ = path + except AttributeError: + # Not important enough to report. + # (The error is also ignored in _bootstrap._init_module_attrs or + # import_run_extension in import.c) + pass return module @@ -1522,7 +1551,6 @@ def _fix_up_module(ns, name, pathname, cpathname=None): ns['__spec__'] = spec ns['__loader__'] = loader ns['__file__'] = pathname - ns['__cached__'] = cpathname except Exception: # Not important enough to report. pass diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py index 29f01f77eff4a0..9ca127ad9c7d0f 100644 --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -45,6 +45,16 @@ def invalidate_caches(self): This method is used by importlib.invalidate_caches(). """ + def discover(self, parent=None): + """An optional method which searches for possible specs with given *parent* + module spec. If *parent* is *None*, MetaPathFinder.discover will search + for top-level modules. + + Returns an iterable of possible specs. + """ + return () + + _register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter, machinery.PathFinder, machinery.WindowsRegistryFinder) @@ -58,26 +68,29 @@ def invalidate_caches(self): This method is used by PathFinder.invalidate_caches(). """ + def discover(self, parent=None): + """An optional method which searches for possible specs with given + *parent* module spec. If *parent* is *None*, PathEntryFinder.discover + will search for top-level modules. + + Returns an iterable of possible specs. + """ + return () + _register(PathEntryFinder, machinery.FileFinder) class ResourceLoader(Loader): """Abstract base class for loaders which can return data from their - back-end storage. + back-end storage to facilitate reading data to perform an import. This ABC represents one of the optional protocols specified by PEP 302. - """ - - def __init__(self): - import warnings - warnings.warn('importlib.abc.ResourceLoader is deprecated in ' - 'favour of supporting resource loading through ' - 'importlib.resources.abc.TraversableResources.', - DeprecationWarning, stacklevel=2) - super().__init__() + For directly loading resources, use TraversableResources instead. This class + primarily exists for backwards compatibility with other ABCs in this module. + """ @abc.abstractmethod def get_data(self, path): @@ -114,7 +127,7 @@ def get_code(self, fullname): source = self.get_source(fullname) if source is None: return None - return self.source_to_code(source) + return self.source_to_code(source, '', fullname) @abc.abstractmethod def get_source(self, fullname): @@ -126,15 +139,14 @@ def get_source(self, fullname): raise ImportError @staticmethod - def source_to_code(data, path=''): + def source_to_code(data, path='', fullname=None): """Compile 'data' into a code object. The 'data' argument can be anything that compile() can handle. The'path' argument should be where the data was retrieved (when applicable).""" - return compile(data, path, 'exec', dont_inherit=True) + return compile(data, path, 'exec', dont_inherit=True, module=fullname) exec_module = _bootstrap_external._LoaderBasics.exec_module - load_module = _bootstrap_external._LoaderBasics.load_module _register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter, machinery.NamespaceLoader) @@ -169,9 +181,8 @@ def get_code(self, fullname): try: path = self.get_filename(fullname) except ImportError: - return self.source_to_code(source) - else: - return self.source_to_code(source, path) + path = '' + return self.source_to_code(source, path, fullname) _register( ExecutionLoader, diff --git a/Lib/importlib/machinery.py b/Lib/importlib/machinery.py index 63d726445c3d96..023f77d750fd2b 100644 --- a/Lib/importlib/machinery.py +++ b/Lib/importlib/machinery.py @@ -16,6 +16,7 @@ from ._bootstrap_external import ExtensionFileLoader from ._bootstrap_external import AppleFrameworkLoader from ._bootstrap_external import NamespaceLoader +from ._bootstrap_external import NamespacePath def all_suffixes(): diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py index 8ce62dd864fc27..32f4b7d2d6e08b 100644 --- a/Lib/importlib/metadata/__init__.py +++ b/Lib/importlib/metadata/__init__.py @@ -1,40 +1,50 @@ +""" +APIs exposing metadata from third-party Python packages. + +This codebase is shared between importlib.metadata in the stdlib +and importlib_metadata in PyPI. See +https://github.com/python/importlib_metadata/wiki/Development-Methodology +for more detail. +""" + from __future__ import annotations -import os -import re import abc -import sys -import json +import collections import email -import types -import inspect -import pathlib -import zipfile -import operator -import textwrap -import warnings import functools import itertools +import operator +import os +import pathlib import posixpath -import collections - -from . import _meta -from ._collections import FreezableDefaultDict, Pair -from ._functools import method_cache, pass_none -from ._itertools import always_iterable, unique_everseen -from ._meta import PackageMetadata, SimplePath - +import re +import sys +import textwrap +import types +from collections.abc import Iterable, Mapping from contextlib import suppress from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap -from typing import Any, Iterable, List, Mapping, Match, Optional, Set, cast +from typing import Any + +from . import _meta +from ._collections import FreezableDefaultDict, Pair +from ._context import ExceptionTrap +from ._functools import method_cache, noop, pass_none, passthrough +from ._itertools import always_iterable, bucket, unique_everseen +from ._meta import PackageMetadata, SimplePath +from ._typing import md_none __all__ = [ 'Distribution', 'DistributionFinder', 'PackageMetadata', 'PackageNotFoundError', + 'PackagePath', + 'MetadataNotFound', + 'SimplePath', 'distribution', 'distributions', 'entry_points', @@ -53,11 +63,15 @@ def __str__(self) -> str: return f"No package metadata was found for {self.name}" @property - def name(self) -> str: # type: ignore[override] + def name(self) -> str: # type: ignore[override] # make readonly (name,) = self.args return name +class MetadataNotFound(FileNotFoundError): + """No metadata file is present in the distribution.""" + + class Sectioned: """ A simple entry point config parser for performance @@ -123,6 +137,12 @@ def valid(line: str): return line and not line.startswith('#') +class _EntryPointMatch(types.SimpleNamespace): + module: str + attr: str + extras: str + + class EntryPoint: """An entry point as defined by Python packaging conventions. @@ -138,6 +158,30 @@ class EntryPoint: 'attr' >>> ep.extras ['extra1', 'extra2'] + + If the value package or module are not valid identifiers, a + ValueError is raised on access. + + >>> EntryPoint(name=None, group=None, value='invalid-name').module + Traceback (most recent call last): + ... + ValueError: ('Invalid object reference...invalid-name... + >>> EntryPoint(name=None, group=None, value='invalid-name').attr + Traceback (most recent call last): + ... + ValueError: ('Invalid object reference...invalid-name... + >>> EntryPoint(name=None, group=None, value='invalid-name').extras + Traceback (most recent call last): + ... + ValueError: ('Invalid object reference...invalid-name... + + The same thing happens on construction. + + >>> EntryPoint(name=None, group=None, value='invalid-name') + Traceback (most recent call last): + ... + ValueError: ('Invalid object reference...invalid-name... + """ pattern = re.compile( @@ -165,38 +209,44 @@ class EntryPoint: value: str group: str - dist: Optional[Distribution] = None + dist: Distribution | None = None def __init__(self, name: str, value: str, group: str) -> None: vars(self).update(name=name, value=value, group=group) + self.module def load(self) -> Any: """Load the entry point from its definition. If only a module is indicated by the value, return that module. Otherwise, return the named object. """ - match = cast(Match, self.pattern.match(self.value)) - module = import_module(match.group('module')) - attrs = filter(None, (match.group('attr') or '').split('.')) + module = import_module(self.module) + attrs = filter(None, (self.attr or '').split('.')) return functools.reduce(getattr, attrs, module) @property def module(self) -> str: - match = self.pattern.match(self.value) - assert match is not None - return match.group('module') + return self._match.module @property def attr(self) -> str: - match = self.pattern.match(self.value) - assert match is not None - return match.group('attr') + return self._match.attr @property - def extras(self) -> List[str]: + def extras(self) -> list[str]: + return re.findall(r'\w+', self._match.extras or '') + + @functools.cached_property + def _match(self) -> _EntryPointMatch: match = self.pattern.match(self.value) - assert match is not None - return re.findall(r'\w+', match.group('extras') or '') + if not match: + raise ValueError( + 'Invalid object reference. ' + 'See https://packaging.python.org' + '/en/latest/specifications/entry-points/#data-model', + self.value, + ) + return _EntryPointMatch(**match.groupdict()) def _for(self, dist): vars(self).update(dist=dist) @@ -222,9 +272,26 @@ def matches(self, **params): >>> ep.matches(attr='bong') True """ + self._disallow_dist(params) attrs = (getattr(self, param) for param in params) return all(map(operator.eq, params.values(), attrs)) + @staticmethod + def _disallow_dist(params): + """ + Querying by dist is not allowed (dist objects are not comparable). + >>> EntryPoint(name='fan', value='fav', group='fag').matches(dist='foo') + Traceback (most recent call last): + ... + ValueError: "dist" is not suitable for matching... + """ + if "dist" in params: + raise ValueError( + '"dist" is not suitable for matching. ' + "Instead, use Distribution.entry_points.select() on a " + "located distribution." + ) + def _key(self): return self.name, self.value, self.group @@ -254,7 +321,7 @@ class EntryPoints(tuple): __slots__ = () - def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override] + def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override] # Work with str instead of int """ Get the EntryPoint in self matching name. """ @@ -278,14 +345,14 @@ def select(self, **params) -> EntryPoints: return EntryPoints(ep for ep in self if ep.matches(**params)) @property - def names(self) -> Set[str]: + def names(self) -> set[str]: """ Return the set of all names of all entry points. """ return {ep.name for ep in self} @property - def groups(self) -> Set[str]: + def groups(self) -> set[str]: """ Return the set of all groups of all entry points. """ @@ -306,11 +373,11 @@ def _from_text(text): class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" - hash: Optional[FileHash] + hash: FileHash | None size: int dist: Distribution - def read_text(self, encoding: str = 'utf-8') -> str: # type: ignore[override] + def read_text(self, encoding: str = 'utf-8') -> str: return self.locate().read_text(encoding=encoding) def read_binary(self) -> bytes: @@ -329,27 +396,7 @@ def __repr__(self) -> str: return f'' -class DeprecatedNonAbstract: - # Required until Python 3.14 - def __new__(cls, *args, **kwargs): - all_names = { - name for subclass in inspect.getmro(cls) for name in vars(subclass) - } - abstract = { - name - for name in all_names - if getattr(getattr(cls, name), '__isabstractmethod__', False) - } - if abstract: - warnings.warn( - f"Unimplemented abstract methods {abstract}", - DeprecationWarning, - stacklevel=2, - ) - return super().__new__(cls) - - -class Distribution(DeprecatedNonAbstract): +class Distribution(metaclass=abc.ABCMeta): """ An abstract Python distribution package. @@ -361,7 +408,7 @@ class Distribution(DeprecatedNonAbstract): """ @abc.abstractmethod - def read_text(self, filename) -> Optional[str]: + def read_text(self, filename) -> str | None: """Attempt to load metadata file given by the name. Python distribution metadata is organized by blobs of text @@ -388,6 +435,17 @@ def locate_file(self, path: str | os.PathLike[str]) -> SimplePath: """ Given a path to a file in this distribution, return a SimplePath to it. + + This method is used by callers of ``Distribution.files()`` to + locate files within the distribution. If it's possible for a + Distribution to represent files in the distribution as + ``SimplePath`` objects, it should implement this method + to resolve such objects. + + Some Distribution providers may elect not to resolve SimplePath + objects within the distribution by raising a + NotImplementedError, but consumers of such a Distribution would + be unable to invoke ``Distribution.files()``. """ @classmethod @@ -404,13 +462,13 @@ def from_name(cls, name: str) -> Distribution: if not name: raise ValueError("A distribution name is required.") try: - return next(iter(cls.discover(name=name))) + return next(iter(cls._prefer_valid(cls.discover(name=name)))) except StopIteration: - raise PackageNotFoundError(name) + raise PackageNotFoundError(name) from None @classmethod def discover( - cls, *, context: Optional[DistributionFinder.Context] = None, **kwargs + cls, *, context: DistributionFinder.Context | None = None, **kwargs ) -> Iterable[Distribution]: """Return an iterable of Distribution objects for all packages. @@ -428,6 +486,21 @@ def discover( resolver(context) for resolver in cls._discover_resolvers() ) + @staticmethod + def _prefer_valid(dists: Iterable[Distribution]) -> Iterable[Distribution]: + """ + Prefer (move to the front) distributions that have metadata. + + Ref python/importlib_resources#489. + """ + + has_metadata = ExceptionTrap(MetadataNotFound).passes( + operator.attrgetter('metadata') + ) + + buckets = bucket(dists, has_metadata) + return itertools.chain(buckets[True], buckets[False]) + @staticmethod def at(path: str | os.PathLike[str]) -> Distribution: """Return a Distribution for the indicated metadata path. @@ -455,11 +528,11 @@ def metadata(self) -> _meta.PackageMetadata: Custom providers may provide the METADATA file or override this property. + + :raises MetadataNotFound: If no metadata file is present. """ - # deferred for performance (python/cpython#109829) - from . import _adapters - opt_text = ( + text = ( self.read_text('METADATA') or self.read_text('PKG-INFO') # This last clause is here to support old egg-info files. Its @@ -467,9 +540,21 @@ def metadata(self) -> _meta.PackageMetadata: # (which points to the egg-info file) attribute unchanged. or self.read_text('') ) - text = cast(str, opt_text) + return self._assemble_message(self._ensure_metadata_present(text)) + + @staticmethod + def _assemble_message(text: str) -> _meta.PackageMetadata: + # deferred for performance (python/cpython#109829) + from . import _adapters + return _adapters.Message(email.message_from_string(text)) + def _ensure_metadata_present(self, text: str | None) -> str: + if text is not None: + return text + + raise MetadataNotFound('No package metadata was found.') + @property def name(self) -> str: """Return the 'Name' metadata for the distribution package.""" @@ -496,7 +581,7 @@ def entry_points(self) -> EntryPoints: return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self) @property - def files(self) -> Optional[List[PackagePath]]: + def files(self) -> list[PackagePath] | None: """Files in this distribution. :return: List of PackagePath for this distribution or None @@ -589,7 +674,7 @@ def _read_files_egginfo_sources(self): return text and map('"{}"'.format, text.splitlines()) @property - def requires(self) -> Optional[List[str]]: + def requires(self) -> list[str] | None: """Generated requirements specified for this Distribution""" reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs() return reqs and list(reqs) @@ -645,6 +730,9 @@ def origin(self): return self._load_json('direct_url.json') def _load_json(self, filename): + # Deferred for performance (python/importlib_metadata#503) + import json + return pass_none(json.loads)( self.read_text(filename), object_hook=lambda data: types.SimpleNamespace(**data), @@ -692,7 +780,7 @@ def __init__(self, **kwargs): vars(self).update(kwargs) @property - def path(self) -> List[str]: + def path(self) -> list[str]: """ The sequence of directory path that a distribution finder should search. @@ -713,6 +801,20 @@ def find_distributions(self, context=Context()) -> Iterable[Distribution]: """ +@passthrough +def _clear_after_fork(cached): + """Ensure ``func`` clears cached state after ``fork`` when supported. + + ``FastPath`` caches zip-backed ``pathlib.Path`` objects that retain a + reference to the parent's open ``ZipFile`` handle. Re-using a cached + instance in a forked child can therefore resurrect invalid file pointers + and trigger ``BadZipFile``/``OSError`` failures (python/importlib_metadata#520). + Registering ``cache_clear`` with ``os.register_at_fork`` keeps each process + on its own cache. + """ + getattr(os, 'register_at_fork', noop)(after_in_child=cached.cache_clear) + + class FastPath: """ Micro-optimized class for searching a root for children. @@ -729,7 +831,8 @@ class FastPath: True """ - @functools.lru_cache() # type: ignore + @_clear_after_fork # type: ignore[misc] + @functools.lru_cache() def __new__(cls, root): return super().__new__(cls) @@ -747,6 +850,9 @@ def children(self): return [] def zip_children(self): + # deferred for performance (python/importlib_metadata#502) + import zipfile + zip_path = zipfile.Path(self.root) names = zip_path.root.namelist() self.joinpath = zip_path.joinpath @@ -841,7 +947,7 @@ class Prepared: normalized = None legacy_normalized = None - def __init__(self, name: Optional[str]): + def __init__(self, name: str | None): self.name = name if name is None: return @@ -852,8 +958,15 @@ def __init__(self, name: Optional[str]): def normalize(name): """ PEP 503 normalization plus dashes as underscores. + + Specifically avoids ``re.sub`` as prescribed for performance + benefits (see python/cpython#143658). """ - return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') + value = name.lower().replace("-", "_").replace(".", "_") + # Condense repeats + while "__" in value: + value = value.replace("__", "_") + return value @staticmethod def legacy_normalize(name): @@ -904,7 +1017,7 @@ def __init__(self, path: SimplePath) -> None: """ self._path = path - def read_text(self, filename: str | os.PathLike[str]) -> Optional[str]: + def read_text(self, filename: str | os.PathLike[str]) -> str | None: with suppress( FileNotFoundError, IsADirectoryError, @@ -973,6 +1086,7 @@ def metadata(distribution_name: str) -> _meta.PackageMetadata: :param distribution_name: The name of the distribution package to query. :return: A PackageMetadata containing the parsed metadata. + :raises MetadataNotFound: If no metadata file is present in the distribution. """ return Distribution.from_name(distribution_name).metadata @@ -1011,7 +1125,7 @@ def entry_points(**params) -> EntryPoints: return EntryPoints(eps).select(**params) -def files(distribution_name: str) -> Optional[List[PackagePath]]: +def files(distribution_name: str) -> list[PackagePath] | None: """Return a list of files for the named package. :param distribution_name: The name of the distribution package to query. @@ -1020,7 +1134,7 @@ def files(distribution_name: str) -> Optional[List[PackagePath]]: return distribution(distribution_name).files -def requires(distribution_name: str) -> Optional[List[str]]: +def requires(distribution_name: str) -> list[str] | None: """ Return a list of requirements for the named package. @@ -1030,7 +1144,7 @@ def requires(distribution_name: str) -> Optional[List[str]]: return distribution(distribution_name).requires -def packages_distributions() -> Mapping[str, List[str]]: +def packages_distributions() -> Mapping[str, list[str]]: """ Return a mapping of top-level packages to their distributions. @@ -1051,7 +1165,7 @@ def _top_level_declared(dist): return (dist.read_text('top_level.txt') or '').split() -def _topmost(name: PackagePath) -> Optional[str]: +def _topmost(name: PackagePath) -> str | None: """ Return the top-most parent as long as there is a parent. """ @@ -1077,11 +1191,10 @@ def _get_toplevel_name(name: PackagePath) -> str: >>> _get_toplevel_name(PackagePath('foo.dist-info')) 'foo.dist-info' """ - return _topmost(name) or ( - # python/typeshed#10328 - inspect.getmodulename(name) # type: ignore - or str(name) - ) + # Defer import of inspect for performance (python/cpython#118761) + import inspect + + return _topmost(name) or inspect.getmodulename(name) or str(name) def _top_level_inferred(dist): diff --git a/Lib/importlib/metadata/_adapters.py b/Lib/importlib/metadata/_adapters.py index 591168808953ba..dede395d79a38b 100644 --- a/Lib/importlib/metadata/_adapters.py +++ b/Lib/importlib/metadata/_adapters.py @@ -1,22 +1,59 @@ -import functools -import warnings +import email.message +import email.policy import re import textwrap -import email.message from ._text import FoldedCase -# Do not remove prior to 2024-01-01 or Python 3.14 -_warn = functools.partial( - warnings.warn, - "Implicit None on return values is deprecated and will raise KeyErrors.", - DeprecationWarning, - stacklevel=2, -) +class RawPolicy(email.policy.EmailPolicy): + def fold(self, name, value): + folded = self.linesep.join( + textwrap + .indent(value, prefix=' ' * 8, predicate=lambda line: True) + .lstrip() + .splitlines() + ) + return f'{name}: {folded}{self.linesep}' class Message(email.message.Message): + r""" + Specialized Message subclass to handle metadata naturally. + + Reads values that may have newlines in them and converts the + payload to the Description. + + >>> msg_text = textwrap.dedent(''' + ... Name: Foo + ... Version: 3.0 + ... License: blah + ... de-blah + ... + ... First line of description. + ... Second line of description. + ... + ... Fourth line! + ... ''').lstrip().replace('', '') + >>> msg = Message(email.message_from_string(msg_text)) + >>> msg['Description'] + 'First line of description.\nSecond line of description.\n\nFourth line!\n' + + Message should render even if values contain newlines. + + >>> print(msg) + Name: Foo + Version: 3.0 + License: blah + de-blah + Description: First line of description. + Second line of description. + + Fourth line! + + + """ + multiple_use_keys = set( map( FoldedCase, @@ -52,26 +89,36 @@ def __iter__(self): def __getitem__(self, item): """ - Warn users that a ``KeyError`` can be expected when a - missing key is supplied. Ref python/importlib_metadata#371. + Override parent behavior to typical dict behavior. + + ``email.message.Message`` will emit None values for missing + keys. Typical mappings, including this ``Message``, will raise + a key error for missing keys. + + Ref python/importlib_metadata#371. """ res = super().__getitem__(item) if res is None: - _warn() + raise KeyError(item) return res def _repair_headers(self): def redent(value): "Correct for RFC822 indentation" - if not value or '\n' not in value: + indent = ' ' * 8 + if not value or '\n' + indent not in value: return value - return textwrap.dedent(' ' * 8 + value) + return textwrap.dedent(indent + value) headers = [(key, redent(value)) for key, value in vars(self)['_headers']] if self._payload: headers.append(('Description', self.get_payload())) + self.set_payload('') return headers + def as_string(self): + return super().as_string(policy=RawPolicy()) + @property def json(self): """ diff --git a/Lib/importlib/metadata/_collections.py b/Lib/importlib/metadata/_collections.py index cf0954e1a30546..fc5045d36be572 100644 --- a/Lib/importlib/metadata/_collections.py +++ b/Lib/importlib/metadata/_collections.py @@ -1,4 +1,5 @@ import collections +import typing # from jaraco.collections 3.3 @@ -24,7 +25,10 @@ def freeze(self): self._frozen = lambda key: self.default_factory() -class Pair(collections.namedtuple('Pair', 'name value')): +class Pair(typing.NamedTuple): + name: str + value: str + @classmethod def parse(cls, text): return cls(*map(str.strip, text.split("=", 1))) diff --git a/Lib/importlib/metadata/_context.py b/Lib/importlib/metadata/_context.py new file mode 100644 index 00000000000000..2635b164ce8923 --- /dev/null +++ b/Lib/importlib/metadata/_context.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +import functools +import operator + + +# from jaraco.context 6.1 +class ExceptionTrap: + """ + A context manager that will catch certain exceptions and provide an + indication they occurred. + + >>> with ExceptionTrap() as trap: + ... raise Exception() + >>> bool(trap) + True + + >>> with ExceptionTrap() as trap: + ... pass + >>> bool(trap) + False + + >>> with ExceptionTrap(ValueError) as trap: + ... raise ValueError("1 + 1 is not 3") + >>> bool(trap) + True + >>> trap.value + ValueError('1 + 1 is not 3') + >>> trap.tb + + + >>> with ExceptionTrap(ValueError) as trap: + ... raise Exception() + Traceback (most recent call last): + ... + Exception + + >>> bool(trap) + False + """ + + exc_info = None, None, None + + def __init__(self, exceptions=(Exception,)): + self.exceptions = exceptions + + def __enter__(self): + return self + + @property + def type(self): + return self.exc_info[0] + + @property + def value(self): + return self.exc_info[1] + + @property + def tb(self): + return self.exc_info[2] + + def __exit__(self, *exc_info): + type = exc_info[0] + matches = type and issubclass(type, self.exceptions) + if matches: + self.exc_info = exc_info + return matches + + def __bool__(self): + return bool(self.type) + + def raises(self, func, *, _test=bool): + """ + Wrap func and replace the result with the truth + value of the trap (True if an exception occurred). + + First, give the decorator an alias to support Python 3.8 + Syntax. + + >>> raises = ExceptionTrap(ValueError).raises + + Now decorate a function that always fails. + + >>> @raises + ... def fail(): + ... raise ValueError('failed') + >>> fail() + True + """ + + @functools.wraps(func) + def wrapper(*args, **kwargs): + with ExceptionTrap(self.exceptions) as trap: + func(*args, **kwargs) + return _test(trap) + + return wrapper + + def passes(self, func): + """ + Wrap func and replace the result with the truth + value of the trap (True if no exception). + + First, give the decorator an alias to support Python 3.8 + Syntax. + + >>> passes = ExceptionTrap(ValueError).passes + + Now decorate a function that always fails. + + >>> @passes + ... def fail(): + ... raise ValueError('failed') + + >>> fail() + False + """ + return self.raises(func, _test=operator.not_) diff --git a/Lib/importlib/metadata/_functools.py b/Lib/importlib/metadata/_functools.py index 71f66bd03cb713..c159b46e48959c 100644 --- a/Lib/importlib/metadata/_functools.py +++ b/Lib/importlib/metadata/_functools.py @@ -1,5 +1,7 @@ -import types import functools +import types +from collections.abc import Callable +from typing import TypeVar # from jaraco.functools 3.3 @@ -102,3 +104,33 @@ def wrapper(param, *args, **kwargs): return func(param, *args, **kwargs) return wrapper + + +# From jaraco.functools 4.4 +def noop(*args, **kwargs): + """ + A no-operation function that does nothing. + + >>> noop(1, 2, three=3) + """ + + +_T = TypeVar('_T') + + +# From jaraco.functools 4.4 +def passthrough(func: Callable[..., object]) -> Callable[[_T], _T]: + """ + Wrap the function to always return the first parameter. + + >>> passthrough(print)('3') + 3 + '3' + """ + + @functools.wraps(func) + def wrapper(first: _T, *args, **kwargs) -> _T: + func(first, *args, **kwargs) + return first + + return wrapper # type: ignore[return-value] diff --git a/Lib/importlib/metadata/_itertools.py b/Lib/importlib/metadata/_itertools.py index d4ca9b9140e3f0..79d37198ce7aff 100644 --- a/Lib/importlib/metadata/_itertools.py +++ b/Lib/importlib/metadata/_itertools.py @@ -1,3 +1,4 @@ +from collections import defaultdict, deque from itertools import filterfalse @@ -71,3 +72,100 @@ def always_iterable(obj, base_type=(str, bytes)): return iter(obj) except TypeError: return iter((obj,)) + + +# Copied from more_itertools 10.3 +class bucket: + """Wrap *iterable* and return an object that buckets the iterable into + child iterables based on a *key* function. + + >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3'] + >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character + >>> sorted(list(s)) # Get the keys + ['a', 'b', 'c'] + >>> a_iterable = s['a'] + >>> next(a_iterable) + 'a1' + >>> next(a_iterable) + 'a2' + >>> list(s['b']) + ['b1', 'b2', 'b3'] + + The original iterable will be advanced and its items will be cached until + they are used by the child iterables. This may require significant storage. + + By default, attempting to select a bucket to which no items belong will + exhaust the iterable and cache all values. + If you specify a *validator* function, selected buckets will instead be + checked against it. + + >>> from itertools import count + >>> it = count(1, 2) # Infinite sequence of odd numbers + >>> key = lambda x: x % 10 # Bucket by last digit + >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only + >>> s = bucket(it, key=key, validator=validator) + >>> 2 in s + False + >>> list(s[2]) + [] + + """ + + def __init__(self, iterable, key, validator=None): + self._it = iter(iterable) + self._key = key + self._cache = defaultdict(deque) + self._validator = validator or (lambda x: True) + + def __contains__(self, value): + if not self._validator(value): + return False + + try: + item = next(self[value]) + except StopIteration: + return False + else: + self._cache[value].appendleft(item) + + return True + + def _get_values(self, value): + """ + Helper to yield items from the parent iterator that match *value*. + Items that don't match are stored in the local cache as they + are encountered. + """ + while True: + # If we've cached some items that match the target value, emit + # the first one and evict it from the cache. + if self._cache[value]: + yield self._cache[value].popleft() + # Otherwise we need to advance the parent iterator to search for + # a matching item, caching the rest. + else: + while True: + try: + item = next(self._it) + except StopIteration: + return + item_value = self._key(item) + if item_value == value: + yield item + break + elif self._validator(item_value): + self._cache[item_value].append(item) + + def __iter__(self): + for item in self._it: + item_value = self._key(item) + if self._validator(item_value): + self._cache[item_value].append(item) + + yield from self._cache.keys() + + def __getitem__(self, value): + if not self._validator(value): + return iter(()) + + return self._get_values(value) diff --git a/Lib/importlib/metadata/_meta.py b/Lib/importlib/metadata/_meta.py index 1927d0f624d82f..0c20eff3da7522 100644 --- a/Lib/importlib/metadata/_meta.py +++ b/Lib/importlib/metadata/_meta.py @@ -1,9 +1,13 @@ from __future__ import annotations import os -from typing import Protocol -from typing import Any, Dict, Iterator, List, Optional, TypeVar, Union, overload - +from collections.abc import Iterator +from typing import ( + Any, + Protocol, + TypeVar, + overload, +) _T = TypeVar("_T") @@ -20,25 +24,25 @@ def __iter__(self) -> Iterator[str]: ... # pragma: no cover @overload def get( self, name: str, failobj: None = None - ) -> Optional[str]: ... # pragma: no cover + ) -> str | None: ... # pragma: no cover @overload - def get(self, name: str, failobj: _T) -> Union[str, _T]: ... # pragma: no cover + def get(self, name: str, failobj: _T) -> str | _T: ... # pragma: no cover # overload per python/importlib_metadata#435 @overload def get_all( self, name: str, failobj: None = None - ) -> Optional[List[Any]]: ... # pragma: no cover + ) -> list[Any] | None: ... # pragma: no cover @overload - def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]: + def get_all(self, name: str, failobj: _T) -> list[Any] | _T: """ Return all values associated with a possibly multi-valued key. """ @property - def json(self) -> Dict[str, Union[str, List[str]]]: + def json(self) -> dict[str, str | list[str]]: """ A JSON-compatible form of the metadata. """ @@ -50,11 +54,11 @@ class SimplePath(Protocol): """ def joinpath( - self, other: Union[str, os.PathLike[str]] + self, other: str | os.PathLike[str] ) -> SimplePath: ... # pragma: no cover def __truediv__( - self, other: Union[str, os.PathLike[str]] + self, other: str | os.PathLike[str] ) -> SimplePath: ... # pragma: no cover @property diff --git a/Lib/importlib/metadata/_typing.py b/Lib/importlib/metadata/_typing.py new file mode 100644 index 00000000000000..32b1d2b98ac987 --- /dev/null +++ b/Lib/importlib/metadata/_typing.py @@ -0,0 +1,15 @@ +import functools +import typing + +from ._meta import PackageMetadata + +md_none = functools.partial(typing.cast, PackageMetadata) +""" +Suppress type errors for optional metadata. + +Although Distribution.metadata can return None when metadata is corrupt +and thus None, allow callers to assume it's not None and crash if +that's the case. + +# python/importlib_metadata#493 +""" diff --git a/Lib/importlib/resources/__init__.py b/Lib/importlib/resources/__init__.py index 723c9f9eb33ce1..27d6c7f89307ef 100644 --- a/Lib/importlib/resources/__init__.py +++ b/Lib/importlib/resources/__init__.py @@ -8,12 +8,11 @@ """ from ._common import ( + Anchor, + Package, as_file, files, - Package, - Anchor, ) - from ._functional import ( contents, is_resource, @@ -23,10 +22,8 @@ read_binary, read_text, ) - from .abc import ResourceReader - __all__ = [ 'Package', 'Anchor', diff --git a/Lib/importlib/resources/_common.py b/Lib/importlib/resources/_common.py index 4e9014c45a056e..6f87d77492f249 100644 --- a/Lib/importlib/resources/_common.py +++ b/Lib/importlib/resources/_common.py @@ -1,54 +1,20 @@ -import os -import pathlib -import tempfile -import functools import contextlib -import types +import functools import importlib import inspect -import warnings import itertools +import os +import pathlib +import tempfile +import types +from typing import Optional, cast -from typing import Union, Optional, cast from .abc import ResourceReader, Traversable -Package = Union[types.ModuleType, str] +Package = types.ModuleType | str Anchor = Package -def package_to_anchor(func): - """ - Replace 'package' parameter as 'anchor' and warn about the change. - - Other errors should fall through. - - >>> files('a', 'b') - Traceback (most recent call last): - TypeError: files() takes from 0 to 1 positional arguments but 2 were given - - Remove this compatibility in Python 3.14. - """ - undefined = object() - - @functools.wraps(func) - def wrapper(anchor=undefined, package=undefined): - if package is not undefined: - if anchor is not undefined: - return func(anchor, package) - warnings.warn( - "First parameter to files is renamed to 'anchor'", - DeprecationWarning, - stacklevel=2, - ) - return func(package) - elif anchor is undefined: - return func() - return func(anchor) - - return wrapper - - -@package_to_anchor def files(anchor: Optional[Anchor] = None) -> Traversable: """ Get a Traversable resource for an anchor. @@ -105,6 +71,19 @@ def is_wrapper(frame_info): return next(callers).frame +def _assert_spec(package: types.ModuleType) -> None: + """ + Provide a nicer error message when package is ``__main__`` + and its ``__spec__`` is ``None`` + (https://docs.python.org/3/reference/import.html#main-spec). + """ + if package.__spec__ is None: + raise TypeError( + f"Cannot access resources for '{package.__name__}' " + "as it does not appear to correspond to an importable module (its __spec__ is None)." + ) + + def from_package(package: types.ModuleType): """ Return a Traversable object for the given package. @@ -113,6 +92,7 @@ def from_package(package: types.ModuleType): # deferred for performance (python/cpython#109829) from ._adapters import wrap_spec + _assert_spec(package) spec = wrap_spec(package) reader = spec.loader.get_resource_reader(spec.name) return reader.files() diff --git a/Lib/importlib/resources/_functional.py b/Lib/importlib/resources/_functional.py index f59416f2dd627d..b08a5c6efe22a2 100644 --- a/Lib/importlib/resources/_functional.py +++ b/Lib/importlib/resources/_functional.py @@ -2,8 +2,8 @@ import warnings -from ._common import files, as_file - +from ._common import as_file, files +from .abc import TraversalError _MISSING = object() @@ -42,7 +42,10 @@ def is_resource(anchor, *path_names): Otherwise returns ``False``. """ - return _get_resource(anchor, path_names).is_file() + try: + return _get_resource(anchor, path_names).is_file() + except TraversalError: + return False def contents(anchor, *path_names): diff --git a/Lib/importlib/resources/abc.py b/Lib/importlib/resources/abc.py index 6750a7aaf14aa9..0b5fdee80e8796 100644 --- a/Lib/importlib/resources/abc.py +++ b/Lib/importlib/resources/abc.py @@ -1,14 +1,22 @@ import abc -import io import itertools import os import pathlib -from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional -from typing import runtime_checkable, Protocol -from typing import Union - - -StrPath = Union[str, os.PathLike[str]] +from collections.abc import Iterable, Iterator +from typing import ( + Any, + BinaryIO, + Literal, + NoReturn, + Optional, + Protocol, + Text, + TextIO, + overload, + runtime_checkable, +) + +StrPath = str | os.PathLike[str] __all__ = ["ResourceReader", "Traversable", "TraversableResources"] @@ -82,11 +90,13 @@ def read_bytes(self) -> bytes: with self.open('rb') as strm: return strm.read() - def read_text(self, encoding: Optional[str] = None) -> str: + def read_text( + self, encoding: Optional[str] = None, errors: Optional[str] = None + ) -> str: """ Read contents of self as text """ - with self.open(encoding=encoding) as strm: + with self.open(encoding=encoding, errors=errors) as strm: return strm.read() @abc.abstractmethod @@ -132,8 +142,14 @@ def __truediv__(self, child: StrPath) -> "Traversable": """ return self.joinpath(child) + @overload + def open(self, mode: Literal['r'] = 'r', *args: Any, **kwargs: Any) -> TextIO: ... + + @overload + def open(self, mode: Literal['rb'], *args: Any, **kwargs: Any) -> BinaryIO: ... + @abc.abstractmethod - def open(self, mode='r', *args, **kwargs): + def open(self, mode: str = 'r', *args: Any, **kwargs: Any) -> TextIO | BinaryIO: """ mode may be 'r' or 'rb' to open as text or binary. Return a handle suitable for reading (same as pathlib.Path.open). @@ -160,7 +176,7 @@ class TraversableResources(ResourceReader): def files(self) -> "Traversable": """Return a Traversable object for the loaded package.""" - def open_resource(self, resource: StrPath) -> io.BufferedReader: + def open_resource(self, resource: StrPath) -> BinaryIO: return self.files().joinpath(resource).open('rb') def resource_path(self, resource: Any) -> NoReturn: diff --git a/Lib/importlib/resources/readers.py b/Lib/importlib/resources/readers.py index 70fc7e2b9c0145..5d0ae46d672f53 100644 --- a/Lib/importlib/resources/readers.py +++ b/Lib/importlib/resources/readers.py @@ -3,15 +3,14 @@ import collections import contextlib import itertools -import pathlib import operator +import pathlib import re import warnings import zipfile from collections.abc import Iterator from . import abc - from ._itertools import only diff --git a/Lib/importlib/resources/simple.py b/Lib/importlib/resources/simple.py index 2e75299b13aabf..5e182d12607c45 100644 --- a/Lib/importlib/resources/simple.py +++ b/Lib/importlib/resources/simple.py @@ -5,7 +5,7 @@ import abc import io import itertools -from typing import BinaryIO, List +from typing import BinaryIO from .abc import Traversable, TraversableResources @@ -24,14 +24,14 @@ def package(self) -> str: """ @abc.abstractmethod - def children(self) -> List['SimpleReader']: + def children(self) -> list['SimpleReader']: """ Obtain an iterable of SimpleReader for available child containers (e.g. directories). """ @abc.abstractmethod - def resources(self) -> List[str]: + def resources(self) -> list[str]: """ Obtain available named resources for this virtual package. """ diff --git a/Lib/inspect.py b/Lib/inspect.py index 183e67fabf966e..af6aa3eb37a53b 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1,7 +1,7 @@ """Get useful information from live Python objects. This module encapsulates the interface provided by the internal special -attributes (co_*, im_*, tb_*, etc.) in a friendlier fashion. +attributes (co_*, tb_*, etc.) in a friendlier fashion. It also provides some help for examining source code and class layout. Here are some of the useful functions provided by this module: @@ -153,9 +153,9 @@ import itertools import linecache import os -import re +lazy import re import sys -import tokenize +lazy import tokenize import token import types import functools @@ -163,9 +163,9 @@ from keyword import iskeyword from operator import attrgetter from collections import namedtuple, OrderedDict -from weakref import ref as make_weakref +from _weakref import ref as make_weakref -# Create constants for the compiler flags in Include/code.h +# Create constants for the compiler flags in Include/cpython/code.h # We try to get them from dis to avoid duplication mod_dict = globals() for k, v in dis.COMPILER_FLAG_NAMES.items(): @@ -348,6 +348,7 @@ def isgenerator(object): gi_frame frame object or possibly None once the generator has been exhausted gi_running set to 1 when generator is executing, 0 otherwise + gi_suspended set to 1 when the generator is suspended at a yield point, 0 otherwise gi_yieldfrom object being iterated by yield from or None __iter__() defined to support iteration over container @@ -415,7 +416,6 @@ def iscode(object): co_freevars tuple of names of free variables co_posonlyargcount number of positional only arguments co_kwonlyargcount number of keyword only arguments (not including ** arg) - co_lnotab encoded mapping of line numbers to bytecode indices co_name name with which this code object was defined co_names tuple of names other than arguments and function locals co_nlocals number of local variables @@ -706,8 +706,8 @@ def _findclass(func): return None return cls -def _finddoc(obj): - if isclass(obj): +def _finddoc(obj, *, search_in_class=True): + if search_in_class and isclass(obj): for base in obj.__mro__: if base is not object: try: @@ -747,6 +747,12 @@ def _finddoc(obj): cls = _findclass(obj.fget) if cls is None or getattr(cls, name) is not obj: return None + # Should be tested before ismethoddescriptor() + elif isinstance(obj, functools.cached_property): + name = obj.attrname + cls = _findclass(obj.func) + if cls is None or getattr(cls, name) is not obj: + return None elif ismethoddescriptor(obj) or isdatadescriptor(obj): name = obj.__name__ cls = obj.__objclass__ @@ -767,19 +773,37 @@ def _finddoc(obj): return doc return None -def getdoc(object): +def _getowndoc(obj): + """Get the documentation string for an object if it is not + inherited from its class.""" + try: + doc = object.__getattribute__(obj, '__doc__') + if doc is None: + return None + if obj is not type: + typedoc = type(obj).__doc__ + if isinstance(typedoc, str) and typedoc == doc: + return None + return doc + except AttributeError: + return None + +def getdoc(object, *, fallback_to_class_doc=True, inherit_class_doc=True): """Get the documentation string for an object. All tabs are expanded to spaces. To clean up docstrings that are indented to line up with blocks of code, any whitespace than can be uniformly removed from the second line onwards is removed.""" - try: - doc = object.__doc__ - except AttributeError: - return None + if fallback_to_class_doc: + try: + doc = object.__doc__ + except AttributeError: + return None + else: + doc = _getowndoc(object) if doc is None: try: - doc = _finddoc(object) + doc = _finddoc(object, search_in_class=inherit_class_doc) except (AttributeError, TypeError): return None if not isinstance(doc, str): @@ -1056,7 +1080,7 @@ class BlockFinder: """Provide a tokeneater() method to detect the end of a code block.""" def __init__(self): self.indent = 0 - self.islambda = False + self.singleline = False self.started = False self.passline = False self.indecorator = False @@ -1065,19 +1089,24 @@ def __init__(self): def tokeneater(self, type, token, srowcol, erowcol, line): if not self.started and not self.indecorator: + if type in (tokenize.INDENT, tokenize.COMMENT, tokenize.NL): + pass + elif token == "async": + pass # skip any decorators - if token == "@": + elif token == "@": self.indecorator = True - # look for the first "def", "class" or "lambda" - elif token in ("def", "class", "lambda"): - if token == "lambda": - self.islambda = True + else: + # For "def" and "class" scan to the end of the block. + # For "lambda" and generator expression scan to + # the end of the logical line. + self.singleline = token not in ("def", "class") self.started = True self.passline = True # skip to the end of the line elif type == tokenize.NEWLINE: self.passline = False # stop skipping when a NEWLINE is seen self.last = srowcol[0] - if self.islambda: # lambdas always end at the first NEWLINE + if self.singleline: raise EndOfBlock # hitting a NEWLINE when in a decorator without args # ends the decorator @@ -1225,11 +1254,10 @@ def getargs(co): FullArgSpec = namedtuple('FullArgSpec', 'args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations') -def getfullargspec(func): +def getfullargspec(func, *, annotation_format=Format.VALUE): """Get the names and default values of a callable object's parameters. - A tuple of seven things is returned: - (args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations). + A FullArgSpec namedtuple is returned, which has the following attributes: 'args' is a list of the parameter names. 'varargs' and 'varkw' are the names of the * and ** parameters or None. 'defaults' is an n-tuple of the default values of the last n parameters. @@ -1237,6 +1265,9 @@ def getfullargspec(func): 'kwonlydefaults' is a dictionary mapping names from kwonlyargs to defaults. 'annotations' is a dictionary mapping parameter names to annotations. + The *annotation_format* parameter controls the format of the annotations. + See the annotationlib documentation for details. + Notable differences from inspect.signature(): - the "self" parameter is always reported, even for bound methods - wrapper chains defined by __wrapped__ *not* unwrapped automatically @@ -1262,7 +1293,8 @@ def getfullargspec(func): follow_wrapper_chains=False, skip_bound_arg=False, sigcls=Signature, - eval_str=False) + eval_str=False, + annotation_format=annotation_format) except Exception as ex: # Most of the times 'signature' will raise ValueError. # But, it can also raise AttributeError, and, maybe something @@ -1604,7 +1636,6 @@ def getframeinfo(frame, context=1): def getlineno(frame): """Get the line number from a frame object, allowing for optimization.""" - # FrameType.f_lineno is now a descriptor that grovels co_lnotab return frame.f_lineno _FrameInfo = namedtuple('_FrameInfo', ('frame',) + Traceback._fields) @@ -1678,9 +1709,13 @@ def _check_instance(obj, attr): def _check_class(klass, attr): + last_meta = None for entry in _static_getmro(klass): - if _shadowed_dict(type(entry)) is _sentinel and attr in entry.__dict__: - return entry.__dict__[attr] + meta = type(entry) + if meta is last_meta or _shadowed_dict(meta) is _sentinel: + last_meta = meta + if attr in entry.__dict__: + return entry.__dict__[attr] return _sentinel @@ -1698,7 +1733,8 @@ def _shadowed_dict_from_weakref_mro_tuple(*weakref_mro): class_dict = dunder_dict['__dict__'] if not (type(class_dict) is types.GetSetDescriptorType and class_dict.__name__ == "__dict__" and - class_dict.__objclass__ is entry): + (class_dict.__objclass__ is object or + class_dict.__objclass__ is entry)): return class_dict return _sentinel @@ -1711,6 +1747,9 @@ def _shadowed_dict(klass): # destroyed, and the dynamically created classes happen to be the only # objects that hold strong references to other objects that take up a # significant amount of memory. + # Fast path: `type` is the dominant caller; result is always _sentinel. + if klass is type: + return _sentinel return _shadowed_dict_from_weakref_mro_tuple( *[make_weakref(entry) for entry in _static_getmro(klass)] ) @@ -1782,13 +1821,7 @@ def getgeneratorstate(generator): GEN_SUSPENDED: Currently suspended at a yield expression. GEN_CLOSED: Execution has completed. """ - if generator.gi_running: - return GEN_RUNNING - if generator.gi_suspended: - return GEN_SUSPENDED - if generator.gi_frame is None: - return GEN_CLOSED - return GEN_CREATED + return generator.gi_state def getgeneratorlocals(generator): @@ -1824,13 +1857,7 @@ def getcoroutinestate(coroutine): CORO_SUSPENDED: Currently suspended at an await expression. CORO_CLOSED: Execution has completed. """ - if coroutine.cr_running: - return CORO_RUNNING - if coroutine.cr_suspended: - return CORO_SUSPENDED - if coroutine.cr_frame is None: - return CORO_CLOSED - return CORO_CREATED + return coroutine.cr_state def getcoroutinelocals(coroutine): @@ -1863,13 +1890,7 @@ def getasyncgenstate(agen): AGEN_SUSPENDED: Currently suspended at a yield expression. AGEN_CLOSED: Execution has completed. """ - if agen.ag_running: - return AGEN_RUNNING - if agen.ag_suspended: - return AGEN_SUSPENDED - if agen.ag_frame is None: - return AGEN_CLOSED - return AGEN_CREATED + return agen.ag_state def getasyncgenlocals(agen): @@ -1913,17 +1934,21 @@ def _signature_get_user_defined_method(cls, method_name, *, follow_wrapper_chain if meth is None: return None + # NOTE: The meth may wraps a non-user-defined callable. + # In this case, we treat the meth as non-user-defined callable too. + # (e.g. cls.__new__ generated by @warnings.deprecated) + unwrapped_meth = None if follow_wrapper_chains: - meth = unwrap(meth, stop=(lambda m: hasattr(m, "__signature__") + unwrapped_meth = unwrap(meth, stop=(lambda m: hasattr(m, "__signature__") or _signature_is_builtin(m))) - if isinstance(meth, _NonUserDefinedCallables): + + if (isinstance(meth, _NonUserDefinedCallables) + or isinstance(unwrapped_meth, _NonUserDefinedCallables)): # Once '__signature__' will be added to 'C'-level # callables, this check won't be necessary return None if method_name != '__new__': meth = _descriptor_get(meth, cls) - if follow_wrapper_chains: - meth = unwrap(meth, stop=lambda m: hasattr(m, "__signature__")) return meth @@ -2106,7 +2131,6 @@ def _signature_strip_non_python_syntax(signature): current_parameter = 0 OP = token.OP - ERRORTOKEN = token.ERRORTOKEN # token stream always starts with ENCODING token, skip it t = next(token_stream) @@ -2183,7 +2207,8 @@ def wrap_value(s): except NameError: raise ValueError - if isinstance(value, (str, int, float, bytes, bool, type(None))): + if isinstance(value, (str, int, float, bytes, bool, type(None), + sentinel)): return ast.Constant(value) raise ValueError @@ -2650,11 +2675,12 @@ class Parameter: The annotation for the parameter if specified. If the parameter has no annotation, this attribute is set to `Parameter.empty`. - * kind : str + * kind Describes how argument values are bound to the parameter. Possible values: `Parameter.POSITIONAL_ONLY`, `Parameter.POSITIONAL_OR_KEYWORD`, `Parameter.VAR_POSITIONAL`, `Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`. + Every value has a `description` attribute describing meaning. """ __slots__ = ('_name', '_kind', '_default', '_annotation') @@ -3336,22 +3362,113 @@ class BufferFlags(enum.IntFlag): WRITE = 0x200 +def _get_details_for_cli(module, nominal_target, resolved_target): + # Determine if the given module name is an alias for another module, + # or if it is reexporting a name that is actually defined elsewhere + resolved_module = getmodule(resolved_target) + if resolved_module is not None and resolved_module is not module: + # Referenced target indicates it was defined somewhere else, + # so report the details of that module rather than the lookup module + module = resolved_module + reported_module_name = module.__name__ + # Ensure the reported source file reflects the actual defining location + try: + source_file = getsourcefile(resolved_target) + except Exception: + try: + source_file = getsourcefile(module) + except Exception: + source_file = None + # Determine if the nominal target location is its defining location + if resolved_target is module: + reported_target = reported_module_name + else: + reported_qualname = getattr(resolved_target, "__qualname__", None) + if not reported_qualname: + reported_qualname = nominal_target.partition(":")[2] + reported_target = f"{reported_module_name}:{reported_qualname}" + # Special case for looking up functions in frozen modules + if source_file == f"": + source_file = module.__file__ + # Populate the actual details to be reported + details = { + "target": reported_target, + "origin": module.__spec__.origin, + "cached": module.__spec__.cached, + "source": source_file, + } + if reported_target != nominal_target: + details["alias"] = nominal_target + error = None + if not source_file: + if module.__name__ in sys.builtin_module_names: + error = "No source code available for builtin module" + else: + error = "No source code available for defining module" + if resolved_target is module: + details["loader"] = repr(module.__spec__.loader) + if hasattr(module, '__path__'): + details["submodule_paths"] = str(module.__path__) + elif source_file: + try: + __, lineno = findsource(resolved_target) + except Exception: + error = "Failed to retrieve source code for given target" + else: + details["lineno"] = lineno + if error: + details["error"] = error + return details + +def _render_details_for_cli(details): + resolved_target = details["target"] + alias = details.get("alias") + if alias: + rendered_target = f'{resolved_target} (looked up as "{alias}")' + else: + rendered_target = resolved_target + lines = [ + f'Target: {rendered_target}', + f'Origin: {details["origin"]}', + f'Source: {details["source"]}', + f'Cached: {details["cached"]}', + ] + loader = details.get("loader") + if loader: + lines.append(f'Loader: {loader}') + submodule_paths = details.get("submodule_paths") + if submodule_paths: + lines.append(f'Submodule search paths: {submodule_paths}') + else: + error = details.get("error") + if error: + # The error is only informational when retrieving object details + lines.append(error) + else: + lines.append(f'Line: {details["lineno"]}') + + lines.append("") + return "\n".join(lines) + + def _main(): """ Logic for inspecting an object given at command line """ import argparse import importlib - parser = argparse.ArgumentParser(color=True) + parser = argparse.ArgumentParser() parser.add_argument( 'object', help="The object to be analysed. " - "It supports the 'module:qualname' syntax") + "It supports the `module:qualname` syntax") parser.add_argument( '-d', '--details', action='store_true', help='Display info about the module rather than its source code') args = parser.parse_args() + # We don't use `pkgutil.resolve_name` here because we want to obtain + # references to both the module *and* the fully resolved target object target = args.object mod_name, has_attrs, attrs = target.partition(":") try: @@ -3369,29 +3486,16 @@ def _main(): for part in parts: obj = getattr(obj, part) - if module.__name__ in sys.builtin_module_names: - print("Can't get info for builtin modules.", file=sys.stderr) - sys.exit(1) - + details = _get_details_for_cli(module, target, obj) if args.details: - print('Target: {}'.format(target)) - print('Origin: {}'.format(getsourcefile(module))) - print('Cached: {}'.format(module.__cached__)) - if obj is module: - print('Loader: {}'.format(repr(module.__loader__))) - if hasattr(module, '__path__'): - print('Submodule search path: {}'.format(module.__path__)) - else: - try: - __, lineno = findsource(obj) - except Exception: - pass - else: - print('Line: {}'.format(lineno)) - - print('\n') + print(_render_details_for_cli(details)) else: - print(getsource(obj)) + # Attempt to render target source details + error = details.get("error") + if error: + sys.exit(error) + else: + print(getsource(obj)) if __name__ == "__main__": diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index 8b60b9d5c9cd51..f1062a8cd052a5 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -8,9 +8,6 @@ """ -__version__ = '1.0' - - import functools IPV4LENGTH = 32 @@ -1479,6 +1476,10 @@ def with_hostmask(self): return '%s/%s' % (self._string_from_ip_int(self._ip), self.hostmask) + @property + def is_unspecified(self): + return self._ip == 0 and self.network.is_unspecified + class IPv4Network(_BaseV4, _BaseNetwork): @@ -1545,7 +1546,7 @@ def __init__(self, address, strict=True): if self._prefixlen == (self.max_prefixlen - 1): self.hosts = self.__iter__ elif self._prefixlen == (self.max_prefixlen): - self.hosts = lambda: [IPv4Address(addr)] + self.hosts = lambda: iter((IPv4Address(addr),)) @property @functools.lru_cache() @@ -2336,7 +2337,7 @@ def __init__(self, address, strict=True): if self._prefixlen == (self.max_prefixlen - 1): self.hosts = self.__iter__ elif self._prefixlen == self.max_prefixlen: - self.hosts = lambda: [IPv6Address(addr)] + self.hosts = lambda: iter((IPv6Address(addr),)) def hosts(self): """Generate Iterator over usable hosts in a network. @@ -2415,3 +2416,12 @@ class _IPv6Constants: IPv6Address._constants = _IPv6Constants IPv6Network._constants = _IPv6Constants + + +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "1.0" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/json/__init__.py b/Lib/json/__init__.py index 1d972d22ded072..37a86831ff9483 100644 --- a/Lib/json/__init__.py +++ b/Lib/json/__init__.py @@ -95,7 +95,6 @@ $ echo '{ 1.2:3.4}' | python -m json Expecting property name enclosed in double quotes: line 1 column 3 (char 2) """ -__version__ = '2.0.9' __all__ = [ 'dump', 'dumps', 'load', 'loads', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder', @@ -128,8 +127,9 @@ def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, instead of raising a ``TypeError``. If ``ensure_ascii`` is false, then the strings written to ``fp`` can - contain non-ASCII characters if they appear in strings contained in - ``obj``. Otherwise, all such characters are escaped in JSON strings. + contain non-ASCII and non-printable characters if they appear in strings + contained in ``obj``. Otherwise, all such characters are escaped in JSON + strings. If ``check_circular`` is false, then the circular reference check for container types will be skipped and a circular reference will @@ -142,13 +142,14 @@ def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, If ``indent`` is a non-negative integer, then JSON array elements and object members will be pretty-printed with that indent level. An indent - level of 0 will only insert newlines. ``None`` is the most compact - representation. + level of 0 will only insert newlines. ``None`` is the default and gives + a representation with no newlines inserted. - If specified, ``separators`` should be an ``(item_separator, key_separator)`` - tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and - ``(',', ': ')`` otherwise. To get the most compact JSON representation, - you should specify ``(',', ':')`` to eliminate whitespace. + If specified, ``separators`` should be an ``(item_separator, + key_separator)`` tuple. The default is ``(', ', ': ')`` if *indent* is + ``None`` and ``(',', ': ')`` otherwise. To get the most compact JSON + representation, you should specify ``(',', ':')`` to eliminate + whitespace. ``default(obj)`` is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError. @@ -189,9 +190,10 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, (``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped instead of raising a ``TypeError``. - If ``ensure_ascii`` is false, then the return value can contain non-ASCII - characters if they appear in strings contained in ``obj``. Otherwise, all - such characters are escaped in JSON strings. + If ``ensure_ascii`` is false, then the return value can contain + non-ASCII and non-printable characters if they appear in strings + contained in ``obj``. Otherwise, all such characters are escaped in + JSON strings. If ``check_circular`` is false, then the circular reference check for container types will be skipped and a circular reference will @@ -204,13 +206,14 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, If ``indent`` is a non-negative integer, then JSON array elements and object members will be pretty-printed with that indent level. An indent - level of 0 will only insert newlines. ``None`` is the most compact - representation. + level of 0 will only insert newlines. ``None`` is the default and gives + a representation with no newlines inserted. - If specified, ``separators`` should be an ``(item_separator, key_separator)`` - tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and - ``(',', ': ')`` otherwise. To get the most compact JSON representation, - you should specify ``(',', ':')`` to eliminate whitespace. + If specified, ``separators`` should be an ``(item_separator, + key_separator)`` tuple. The default is ``(', ', ': ')`` if *indent* is + ``None`` and ``(',', ': ')`` otherwise. To get the most compact JSON + representation, you should specify ``(',', ':')`` to eliminate + whitespace. ``default(obj)`` is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError. @@ -238,7 +241,7 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, **kw).encode(obj) -_default_decoder = JSONDecoder(object_hook=None, object_pairs_hook=None) +_default_decoder = JSONDecoder() def detect_encoding(b): @@ -272,7 +275,8 @@ def detect_encoding(b): def load(fp, *, cls=None, object_hook=None, parse_float=None, - parse_int=None, parse_constant=None, object_pairs_hook=None, **kw): + parse_int=None, parse_constant=None, object_pairs_hook=None, + array_hook=None, **kw): """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing a JSON document) to a Python object. @@ -281,11 +285,20 @@ def load(fp, *, cls=None, object_hook=None, parse_float=None, ``object_hook`` will be used instead of the ``dict``. This feature can be used to implement custom decoders (e.g. JSON-RPC class hinting). - ``object_pairs_hook`` is an optional function that will be called with the - result of any object literal decoded with an ordered list of pairs. The - return value of ``object_pairs_hook`` will be used instead of the ``dict``. - This feature can be used to implement custom decoders. If ``object_hook`` - is also defined, the ``object_pairs_hook`` takes priority. + ``object_pairs_hook`` is an optional function that will be called with + the result of any object literal decoded with an ordered list of pairs. + The return value of ``object_pairs_hook`` will be used instead of the + ``dict``. This feature can be used to implement custom decoders. If + ``object_hook`` is also defined, the ``object_pairs_hook`` takes + priority. + + ``array_hook`` is an optional function that will be called with the + result of any literal array decode (a ``list``). The return value of + this function will be used instead of the ``list``. This feature can + be used along ``object_pairs_hook`` to customize the resulting data + structure - for example, by setting that to ``frozendict`` and + ``array_hook`` to ``tuple``, one can get a deep immutable data structure + from any JSON data. To use a custom ``JSONDecoder`` subclass, specify it with the ``cls`` kwarg; otherwise ``JSONDecoder`` is used. @@ -293,11 +306,13 @@ def load(fp, *, cls=None, object_hook=None, parse_float=None, return loads(fp.read(), cls=cls, object_hook=object_hook, parse_float=parse_float, parse_int=parse_int, - parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw) + parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, + array_hook=array_hook, **kw) def loads(s, *, cls=None, object_hook=None, parse_float=None, - parse_int=None, parse_constant=None, object_pairs_hook=None, **kw): + parse_int=None, parse_constant=None, object_pairs_hook=None, + array_hook=None, **kw): """Deserialize ``s`` (a ``str``, ``bytes`` or ``bytearray`` instance containing a JSON document) to a Python object. @@ -306,11 +321,20 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None, ``object_hook`` will be used instead of the ``dict``. This feature can be used to implement custom decoders (e.g. JSON-RPC class hinting). - ``object_pairs_hook`` is an optional function that will be called with the - result of any object literal decoded with an ordered list of pairs. The - return value of ``object_pairs_hook`` will be used instead of the ``dict``. - This feature can be used to implement custom decoders. If ``object_hook`` - is also defined, the ``object_pairs_hook`` takes priority. + ``object_pairs_hook`` is an optional function that will be called with + the result of any object literal decoded with an ordered list of pairs. + The return value of ``object_pairs_hook`` will be used instead of the + ``dict``. This feature can be used to implement custom decoders. If + ``object_hook`` is also defined, the ``object_pairs_hook`` takes + priority. + + ``array_hook`` is an optional function that will be called with the + result of any literal array decode (a ``list``). The return value of + this function will be used instead of the ``list``. This feature can + be used along ``object_pairs_hook`` to customize the resulting data + structure - for example, by setting that to ``frozendict`` and + ``array_hook`` to ``tuple``, one can get a deep immutable data structure + from any JSON data. ``parse_float``, if specified, will be called with the string of every JSON float to be decoded. By default this is equivalent to @@ -342,7 +366,8 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None, if (cls is None and object_hook is None and parse_int is None and parse_float is None and - parse_constant is None and object_pairs_hook is None and not kw): + parse_constant is None and object_pairs_hook is None + and array_hook is None and not kw): return _default_decoder.decode(s) if cls is None: cls = JSONDecoder @@ -350,6 +375,8 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None, kw['object_hook'] = object_hook if object_pairs_hook is not None: kw['object_pairs_hook'] = object_pairs_hook + if array_hook is not None: + kw['array_hook'] = array_hook if parse_float is not None: kw['parse_float'] = parse_float if parse_int is not None: @@ -357,3 +384,12 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None, if parse_constant is not None: kw['parse_constant'] = parse_constant return cls(**kw).decode(s) + + +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "2.0.9" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/json/decoder.py b/Lib/json/decoder.py index ff4bfcdcc407b9..364e44d40cc307 100644 --- a/Lib/json/decoder.py +++ b/Lib/json/decoder.py @@ -43,11 +43,11 @@ def __reduce__(self): return self.__class__, (self.msg, self.doc, self.pos) -_CONSTANTS = { +_CONSTANTS = frozendict({ '-Infinity': NegInf, 'Infinity': PosInf, 'NaN': NaN, -} +}) HEXDIGITS = re.compile(r'[0-9A-Fa-f]{4}', FLAGS) @@ -218,7 +218,7 @@ def JSONObject(s_and_end, strict, scan_once, object_hook, object_pairs_hook, pairs = object_hook(pairs) return pairs, end -def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR): +def JSONArray(s_and_end, scan_once, array_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR): s, end = s_and_end values = [] nextchar = s[end:end + 1] @@ -227,6 +227,8 @@ def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR): nextchar = s[end:end + 1] # Look-ahead for trivial empty array if nextchar == ']': + if array_hook is not None: + values = array_hook(values) return values, end + 1 _append = values.append while True: @@ -256,6 +258,8 @@ def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR): if nextchar == ']': raise JSONDecodeError("Illegal trailing comma before end of array", s, comma_idx) + if array_hook is not None: + values = array_hook(values) return values, end @@ -291,19 +295,27 @@ class JSONDecoder(object): def __init__(self, *, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, - object_pairs_hook=None): + object_pairs_hook=None, array_hook=None): """``object_hook``, if specified, will be called with the result of every JSON object decoded and its return value will be used in place of the given ``dict``. This can be used to provide custom deserializations (e.g. to support JSON-RPC class hinting). - ``object_pairs_hook``, if specified will be called with the result of - every JSON object decoded with an ordered list of pairs. The return - value of ``object_pairs_hook`` will be used instead of the ``dict``. - This feature can be used to implement custom decoders. + ``object_pairs_hook``, if specified will be called with the result + of every JSON object decoded with an ordered list of pairs. The + return value of ``object_pairs_hook`` will be used instead of the + ``dict``. This feature can be used to implement custom decoders. If ``object_hook`` is also defined, the ``object_pairs_hook`` takes priority. + ``array_hook`` is an optional function that will be called with the + result of any literal array decode (a ``list``). The return value of + this function will be used instead of the ``list``. This feature can + be used along ``object_pairs_hook`` to customize the resulting data + structure - for example, by setting that to ``frozendict`` and + ``array_hook`` to ``tuple``, one can get a deep immutable data + structute from any JSON data. + ``parse_float``, if specified, will be called with the string of every JSON float to be decoded. By default this is equivalent to float(num_str). This can be used to use another datatype or parser @@ -330,6 +342,7 @@ def __init__(self, *, object_hook=None, parse_float=None, self.parse_constant = parse_constant or _CONSTANTS.__getitem__ self.strict = strict self.object_pairs_hook = object_pairs_hook + self.array_hook = array_hook self.parse_object = JSONObject self.parse_array = JSONArray self.parse_string = scanstring diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py index bc446e0f377a11..718b3254241c56 100644 --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -79,7 +79,7 @@ class JSONEncoder(object): +-------------------+---------------+ | Python | JSON | +===================+===============+ - | dict | object | + | dict, frozendict | object | +-------------------+---------------+ | list, tuple | array | +-------------------+---------------+ @@ -111,9 +111,10 @@ def __init__(self, *, skipkeys=False, ensure_ascii=True, encoding of keys that are not str, int, float, bool or None. If skipkeys is True, such items are simply skipped. - If ensure_ascii is true, the output is guaranteed to be str - objects with all incoming non-ASCII characters escaped. If - ensure_ascii is false, the output can contain non-ASCII characters. + If ensure_ascii is true, the output is guaranteed to be str objects + with all incoming non-ASCII and non-printable characters escaped. + If ensure_ascii is false, the output can contain non-ASCII and + non-printable characters. If check_circular is true, then lists, dicts, and custom encoded objects will be checked for circular references during encoding to @@ -134,14 +135,15 @@ def __init__(self, *, skipkeys=False, ensure_ascii=True, indent level. An indent level of 0 will only insert newlines. None is the most compact representation. - If specified, separators should be an (item_separator, key_separator) - tuple. The default is (', ', ': ') if *indent* is ``None`` and - (',', ': ') otherwise. To get the most compact JSON representation, - you should specify (',', ':') to eliminate whitespace. + If specified, separators should be an (item_separator, + key_separator) tuple. The default is (', ', ': ') if *indent* is + ``None`` and (',', ': ') otherwise. To get the most compact JSON + representation, you should specify (',', ':') to eliminate + whitespace. If specified, default is a function that gets called for objects - that can't otherwise be serialized. It should return a JSON encodable - version of the object or raise a ``TypeError``. + that can't otherwise be serialized. It should return a JSON + encodable version of the object or raise a ``TypeError``. """ @@ -262,17 +264,6 @@ def floatstr(o, allow_nan=self.allow_nan, def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot, - ## HACK: hand-optimized bytecode; turn globals into locals - ValueError=ValueError, - dict=dict, - float=float, - id=id, - int=int, - isinstance=isinstance, - list=list, - str=str, - tuple=tuple, - _intstr=int.__repr__, ): def _iterencode_list(lst, _current_indent_level): @@ -309,7 +300,7 @@ def _iterencode_list(lst, _current_indent_level): # Subclasses of int/float may override __repr__, but we still # want to encode them as integers/floats in JSON. One example # within the standard library is IntEnum. - yield buf + _intstr(value) + yield buf + int.__repr__(value) elif isinstance(value, float): # see comment above for int yield buf + _floatstr(value) @@ -317,7 +308,7 @@ def _iterencode_list(lst, _current_indent_level): yield buf if isinstance(value, (list, tuple)): chunks = _iterencode_list(value, _current_indent_level) - elif isinstance(value, dict): + elif isinstance(value, (dict, frozendict)): chunks = _iterencode_dict(value, _current_indent_level) else: chunks = _iterencode(value, _current_indent_level) @@ -372,7 +363,7 @@ def _iterencode_dict(dct, _current_indent_level): key = 'null' elif isinstance(key, int): # see comment for int/float in _make_iterencode - key = _intstr(key) + key = int.__repr__(key) elif _skipkeys: continue else: @@ -397,14 +388,14 @@ def _iterencode_dict(dct, _current_indent_level): yield 'false' elif isinstance(value, int): # see comment for int/float in _make_iterencode - yield _intstr(value) + yield int.__repr__(value) elif isinstance(value, float): # see comment for int/float in _make_iterencode yield _floatstr(value) else: if isinstance(value, (list, tuple)): chunks = _iterencode_list(value, _current_indent_level) - elif isinstance(value, dict): + elif isinstance(value, (dict, frozendict)): chunks = _iterencode_dict(value, _current_indent_level) else: chunks = _iterencode(value, _current_indent_level) @@ -432,13 +423,13 @@ def _iterencode(o, _current_indent_level): yield 'false' elif isinstance(o, int): # see comment for int/float in _make_iterencode - yield _intstr(o) + yield int.__repr__(o) elif isinstance(o, float): # see comment for int/float in _make_iterencode yield _floatstr(o) elif isinstance(o, (list, tuple)): yield from _iterencode_list(o, _current_indent_level) - elif isinstance(o, dict): + elif isinstance(o, (dict, frozendict)): yield from _iterencode_dict(o, _current_indent_level) else: if markers is not None: @@ -456,4 +447,13 @@ def _iterencode(o, _current_indent_level): raise if markers is not None: del markers[markerid] - return _iterencode + + def _iterencode_once(o, _current_indent_level): + nonlocal _iterencode, _iterencode_dict, _iterencode_list + try: + yield from _iterencode(o, _current_indent_level) + finally: + # Break reference cycles due to mutually recursive closures: + del _iterencode, _iterencode_dict, _iterencode_list + + return _iterencode_once diff --git a/Lib/json/scanner.py b/Lib/json/scanner.py index 090897515fe2f3..b484e00be0fd2a 100644 --- a/Lib/json/scanner.py +++ b/Lib/json/scanner.py @@ -23,6 +23,7 @@ def py_make_scanner(context): parse_constant = context.parse_constant object_hook = context.object_hook object_pairs_hook = context.object_pairs_hook + array_hook = context.array_hook memo = context.memo def _scan_once(string, idx): @@ -37,7 +38,7 @@ def _scan_once(string, idx): return parse_object((string, idx + 1), strict, _scan_once, object_hook, object_pairs_hook, memo) elif nextchar == '[': - return parse_array((string, idx + 1), _scan_once) + return parse_array((string, idx + 1), _scan_once, array_hook) elif nextchar == 'n' and string[idx:idx + 4] == 'null': return None, idx + 4 elif nextchar == 't' and string[idx:idx + 4] == 'true': diff --git a/Lib/json/tool.py b/Lib/json/tool.py index 1967817add8abc..6385d971f73304 100644 --- a/Lib/json/tool.py +++ b/Lib/json/tool.py @@ -22,13 +22,13 @@ (?Pnull) ''', re.VERBOSE) -_group_to_theme_color = { +_group_to_theme_color = frozendict({ "key": "definition", "string": "string", "number": "number", "boolean": "keyword", "null": "keyword", -} +}) def _colorize_json(json_str, theme): @@ -44,12 +44,13 @@ def _replace_match_callback(match): def main(): description = ('A simple command line interface for json module ' 'to validate and pretty-print JSON objects.') - parser = argparse.ArgumentParser(description=description, color=True) + parser = argparse.ArgumentParser(description=description) parser.add_argument('infile', nargs='?', - help='a JSON file to be validated or pretty-printed', + help='a JSON file to be validated or pretty-printed; ' + 'defaults to `stdin`', default='-') parser.add_argument('outfile', nargs='?', - help='write the output of infile to outfile', + help='write the output of `infile` to `outfile`', default=None) parser.add_argument('--sort-keys', action='store_true', default=False, help='sort the output of dictionaries alphabetically by key') @@ -57,7 +58,7 @@ def main(): help='disable escaping of non-ASCII characters') parser.add_argument('--json-lines', action='store_true', default=False, help='parse input using the JSON Lines format. ' - 'Use with --no-indent or --compact to produce valid JSON Lines output.') + 'Use with `--no-indent` or `--compact` to produce valid JSON Lines output.') group = parser.add_mutually_exclusive_group() group.add_argument('--indent', default=4, type=int, help='separate items with newlines and use this number ' @@ -88,7 +89,8 @@ def main(): infile = open(options.infile, encoding='utf-8') try: if options.json_lines: - objs = (json.loads(line) for line in infile) + lines = infile.readlines() + objs = (json.loads(line) for line in lines) else: objs = (json.load(infile),) finally: diff --git a/Lib/keyword.py b/Lib/keyword.py index e22c837835e740..98ffe2de28b1a1 100644 --- a/Lib/keyword.py +++ b/Lib/keyword.py @@ -56,6 +56,7 @@ softkwlist = [ '_', 'case', + 'lazy', 'match', 'type' ] diff --git a/Lib/linecache.py b/Lib/linecache.py index ef73d1aa99774a..b5bf9dbdd3cbc7 100644 --- a/Lib/linecache.py +++ b/Lib/linecache.py @@ -123,9 +123,12 @@ def updatecache(filename, module_globals=None): if _source_unavailable(filename): return [] - if filename.startswith('')): return None - # Try for a __loader__, if available - if module_globals and '__name__' in module_globals: - spec = module_globals.get('__spec__') - name = getattr(spec, 'name', None) or module_globals['__name__'] - loader = getattr(spec, 'loader', None) - if loader is None: - loader = module_globals.get('__loader__') - get_source = getattr(loader, 'get_source', None) - - if name and get_source: - def get_lines(name=name, *args, **kwargs): - return get_source(name, *args, **kwargs) - return (get_lines,) - return None + if module_globals is not None and not isinstance(module_globals, dict): + raise TypeError(f'module_globals must be a dict, not {type(module_globals).__qualname__}') + if not module_globals or '__name__' not in module_globals: + return None + + spec = module_globals.get('__spec__') + name = getattr(spec, 'name', None) or module_globals['__name__'] + if name is None: + return None + + loader = _bless_my_loader(module_globals) + if loader is None: + return None + + get_source = getattr(loader, 'get_source', None) + if get_source is None: + return None + + def get_lines(name=name, *args, **kwargs): + return get_source(name, *args, **kwargs) + return (get_lines,) + +def _bless_my_loader(module_globals): + # Similar to _bless_my_loader() in importlib._bootstrap_external, + # but always emits warnings instead of errors. + loader = module_globals.get('__loader__') + if loader is None and '__spec__' not in module_globals: + return None + spec = module_globals.get('__spec__') + + # The __main__ module has __spec__ = None. + if spec is None and module_globals.get('__name__') == '__main__': + return loader + + spec_loader = getattr(spec, 'loader', None) + if spec_loader is None: + import warnings + warnings.warn( + 'Module globals is missing a __spec__.loader', + DeprecationWarning) + return loader + + assert spec_loader is not None + if loader is not None and loader != spec_loader: + import warnings + warnings.warn( + 'Module globals; __loader__ != __spec__.loader', + DeprecationWarning) + return loader + + return spec_loader def _register_code(code, string, name): diff --git a/Lib/locale.py b/Lib/locale.py index 0bde7ed51c66c1..25efff5b856854 100644 --- a/Lib/locale.py +++ b/Lib/locale.py @@ -17,17 +17,14 @@ from builtins import str as _builtin_str import functools -# Try importing the _locale module. -# -# If this fails, fall back on a basic 'C' locale emulation. - # Yuck: LC_MESSAGES is non-standard: can't tell whether it exists before # trying the import. So __all__ is also fiddled at the end of the file. __all__ = ["getlocale", "getdefaultlocale", "getpreferredencoding", "Error", "setlocale", "localeconv", "strcoll", "strxfrm", "str", "atof", "atoi", "format_string", "currency", "normalize", "LC_CTYPE", "LC_COLLATE", "LC_TIME", "LC_MONETARY", - "LC_NUMERIC", "LC_ALL", "CHAR_MAX", "getencoding"] + "LC_NUMERIC", "LC_ALL", "CHAR_MAX", "getencoding", "delocalize", + "localize"] def _strcoll(a,b): """ strcoll(string,string) -> int. @@ -41,6 +38,9 @@ def _strxfrm(s): """ return s +# Try importing the _locale module. +# +# If this fails, fall back on a basic 'C' locale emulation. try: from _locale import * @@ -91,6 +91,29 @@ def setlocale(category, value=None): raise Error('_locale emulation only supports "C" locale') return 'C' +else: + _conditional_constants_names = ["ABDAY_1", "ABDAY_2", "ABDAY_3", + "ABDAY_4", "ABDAY_5", "ABDAY_6", + "ABDAY_7", "ABMON_1", "ABMON_10", + "ABMON_11", "ABMON_12", "ABMON_2", + "ABMON_3", "ABMON_4", "ABMON_5", + "ABMON_6", "ABMON_7", "ABMON_8", + "ABMON_9", "ALT_DIGITS", "CODESET", + "CRNCYSTR", "DAY_1", "DAY_2", "DAY_3", + "DAY_4", "DAY_5", "DAY_6", + "DAY_7", "D_FMT", "D_T_FMT", + "ERA", "ERA_D_FMT", "ERA_D_T_FMT", + "ERA_T_FMT", "MON_1", "MON_10", + "MON_11", "MON_12", "MON_2", "MON_3", + "MON_4", "MON_5", "MON_6", "MON_7", + "MON_8", "MON_9", "NOEXPR", + "RADIXCHAR", "THOUSEP", "T_FMT", + "T_FMT_AMPM", "YESEXPR", "AM_STR", + "PM_STR"] + # The constants defined in _locale are platform-dependent, + # so we only include those that are available on the current platform. + __all__.extend(vars().keys() & _conditional_constants_names) + # These may or may not exist in _locale, so be sure to set them. if 'strxfrm' not in globals(): strxfrm = _strxfrm @@ -214,7 +237,7 @@ def format_string(f, val, grouping=False, monetary=False): Grouping is applied if the third parameter is true. Conversion uses monetary thousands separator and grouping strings if - forth parameter monetary is true.""" + fourth parameter monetary is true.""" global _percent_re if _percent_re is None: import re @@ -375,12 +398,14 @@ def _replace_encoding(code, encoding): def _append_modifier(code, modifier): if modifier == 'euro': if '.' not in code: - return code + '.ISO8859-15' + # Linux appears to require keeping the "@euro" modifier in place, + # even when using the ".ISO8859-15" encoding. + return code + '.ISO8859-15@euro' _, _, encoding = code.partition('.') - if encoding in ('ISO8859-15', 'UTF-8'): + if encoding == 'UTF-8': return code if encoding == 'ISO8859-1': - return _replace_encoding(code, 'ISO8859-15') + code = _replace_encoding(code, 'ISO8859-15') return code + '@' + modifier def normalize(localename): @@ -485,13 +510,18 @@ def _parse_localename(localename): # Deal with locale modifiers code, modifier = code.split('@', 1) if modifier == 'euro' and '.' not in code: - # Assume Latin-9 for @euro locales. This is bogus, - # since some systems may use other encodings for these - # locales. Also, we ignore other modifiers. - return code, 'iso-8859-15' + # Assume ISO8859-15 for @euro locales. Do note that some systems + # may use other encodings for these locales, so this may not always + # be correct. + return code + '@euro', 'ISO8859-15' + else: + modifier = '' if '.' in code: - return tuple(code.split('.')[:2]) + code, encoding = code.split('.')[:2] + if modifier: + code += '@' + modifier + return code, encoding elif code == 'C': return None, None elif code == 'UTF-8': @@ -516,7 +546,14 @@ def _build_localename(localetuple): if encoding is None: return language else: - return language + '.' + encoding + if '@' in language: + language, modifier = language.split('@', 1) + else: + modifier = '' + localename = language + '.' + encoding + if modifier: + localename += '@' + modifier + return localename except (TypeError, ValueError): raise TypeError('Locale must be None, a string, or an iterable of ' 'two strings -- language code, encoding.') from None @@ -545,12 +582,6 @@ def getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')): """ - import warnings - warnings._deprecated( - "locale.getdefaultlocale", - "{name!r} is deprecated and slated for removal in Python {remove}. " - "Use setlocale(), getencoding() and getlocale() instead.", - remove=(3, 15)) return _getdefaultlocale(envvars) @@ -562,10 +593,6 @@ def _getdefaultlocale(envvars=('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE')): except (ImportError, AttributeError): pass else: - # make sure the code/encoding values are valid - if sys.platform == "win32" and code and code[:2] == "0x": - # map windows language identifier to language name - code = windows_locale.get(int(code, 0)) # ...add other platform-specific processing here, if # necessary... return code, encoding @@ -888,6 +915,12 @@ def getpreferredencoding(do_setlocale=True): # SS 2025-06-10: # Remove 'c.utf8' -> 'en_US.UTF-8' because 'en_US.UTF-8' does not exist # on all platforms. +# +# SS 2025-07-30: +# Remove conflicts with GNU libc. +# +# removed 'el_gr@euro' +# removed 'uz_uz@cyrillic' locale_alias = { 'a3': 'az_AZ.KOI8-C', @@ -1021,7 +1054,6 @@ def getpreferredencoding(do_setlocale=True): 'el': 'el_GR.ISO8859-7', 'el_cy': 'el_CY.ISO8859-7', 'el_gr': 'el_GR.ISO8859-7', - 'el_gr@euro': 'el_GR.ISO8859-15', 'en': 'en_US.ISO8859-1', 'en_ag': 'en_AG.UTF-8', 'en_au': 'en_AU.ISO8859-1', @@ -1456,7 +1488,6 @@ def getpreferredencoding(do_setlocale=True): 'ur_pk': 'ur_PK.CP1256', 'uz': 'uz_UZ.UTF-8', 'uz_uz': 'uz_UZ.UTF-8', - 'uz_uz@cyrillic': 'uz_UZ.UTF-8', 've': 've_ZA.UTF-8', 've_za': 've_ZA.UTF-8', 'vi': 'vi_VN.TCVN', @@ -1497,8 +1528,8 @@ def getpreferredencoding(do_setlocale=True): # This maps Windows language identifiers to locale strings. # # This list has been updated from -# http://msdn.microsoft.com/library/default.asp?url=/library/en-us/intl/nls_238z.asp -# to include every locale up to Windows Vista. +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f +# to include every locale up to protocol revision 16.0 (2024-04-23). # # NOTE: this mapping is incomplete. If your language is missing, please # submit a bug report as detailed in the Python devguide at: @@ -1508,10 +1539,15 @@ def getpreferredencoding(do_setlocale=True): # windows_locale = { - 0x0436: "af_ZA", # Afrikaans - 0x041c: "sq_AL", # Albanian - 0x0484: "gsw_FR",# Alsatian - France + 0x0036: "af", # Afrikaans + 0x0436: "af_ZA", # Afrikaans - South Africa + 0x001c: "sq", # Albanian + 0x041c: "sq_AL", # Albanian - Albania + 0x0084: "gsw", # Alsatian + 0x0484: "gsw_FR", # Alsatian - France + 0x005e: "am", # Amharic 0x045e: "am_ET", # Amharic - Ethiopia + 0x0001: "ar", # Arabic 0x0401: "ar_SA", # Arabic - Saudi Arabia 0x0801: "ar_IQ", # Arabic - Iraq 0x0c01: "ar_EG", # Arabic - Egypt @@ -1525,39 +1561,72 @@ def getpreferredencoding(do_setlocale=True): 0x2c01: "ar_JO", # Arabic - Jordan 0x3001: "ar_LB", # Arabic - Lebanon 0x3401: "ar_KW", # Arabic - Kuwait - 0x3801: "ar_AE", # Arabic - United Arab Emirates + 0x3801: "ar_AE", # Arabic - U.A.E. 0x3c01: "ar_BH", # Arabic - Bahrain 0x4001: "ar_QA", # Arabic - Qatar - 0x042b: "hy_AM", # Armenian + 0x002b: "hy", # Armenian + 0x042b: "hy_AM", # Armenian - Armenia + 0x004d: "as", # Assamese 0x044d: "as_IN", # Assamese - India - 0x042c: "az_AZ", # Azeri - Latin - 0x082c: "az_AZ", # Azeri - Cyrillic - 0x046d: "ba_RU", # Bashkir - 0x042d: "eu_ES", # Basque - Russia - 0x0423: "be_BY", # Belarusian - 0x0445: "bn_IN", # Begali - 0x201a: "bs_BA", # Bosnian - Cyrillic - 0x141a: "bs_BA", # Bosnian - Latin + 0x002c: "az", # Azerbaijani (Latin) + 0x742c: "az", # Azerbaijani (Cyrillic) + 0x782c: "az", # Azerbaijani (Latin) + 0x042c: "az_AZ", # Azerbaijani (Latin) - Azerbaijan + 0x0045: "bn", # Bangla + 0x0445: "bn_IN", # Bangla - India + 0x0845: "bn_BD", # Bangla - Bangladesh + 0x006d: "ba", # Bashkir + 0x046d: "ba_RU", # Bashkir - Russia + 0x002d: "eu", # Basque + 0x042d: "eu_ES", # Basque - Spain + 0x0023: "be", # Belarusian + 0x0423: "be_BY", # Belarusian - Belarus + 0x641a: "bs", # Bosnian (Cyrillic) + 0x681a: "bs", # Bosnian (Latin) + 0x141a: "bs_BA", # Bosnian (Latin) - Bosnia and Herzegovina + 0x201a: "bs_BA", # Bosnian (Cyrillic) - Bosnia and Herzegovina + 0x781a: "bs", # Bosnian (Latin) + 0x007e: "br", # Breton 0x047e: "br_FR", # Breton - France - 0x0402: "bg_BG", # Bulgarian -# 0x0455: "my_MM", # Burmese - Not supported - 0x0403: "ca_ES", # Catalan - 0x0004: "zh_CHS",# Chinese - Simplified - 0x0404: "zh_TW", # Chinese - Taiwan - 0x0804: "zh_CN", # Chinese - PRC - 0x0c04: "zh_HK", # Chinese - Hong Kong S.A.R. - 0x1004: "zh_SG", # Chinese - Singapore - 0x1404: "zh_MO", # Chinese - Macao S.A.R. - 0x7c04: "zh_CHT",# Chinese - Traditional + 0x0002: "bg", # Bulgarian + 0x0402: "bg_BG", # Bulgarian - Bulgaria + 0x0055: "my", # Burmese + 0x0455: "my_MM", # Burmese - Myanmar + 0x0003: "ca", # Catalan + 0x0403: "ca_ES", # Catalan - Spain + 0x0803: "ca_ES", # Valencian - Spain + 0x0092: "ku", # Central Kurdish + 0x7c92: "ku", # Central Kurdish + 0x0492: "ku_IQ", # Central Kurdish - Iraq + 0x005c: "chr", # Cherokee + 0x7c5c: "chr", # Cherokee + 0x045c: "chr_US", # Cherokee - United States + 0x0004: "zh", # Chinese (Simplified) + 0x7804: "zh", # Chinese (Simplified) + 0x7c04: "zh", # Chinese (Traditional) + 0x0404: "zh_TW", # Chinese (Traditional) - Taiwan + 0x0804: "zh_CN", # Chinese (Simplified) - People's Republic of China + 0x0c04: "zh_HK", # Chinese (Traditional) - Hong Kong S.A.R. + 0x1004: "zh_SG", # Chinese (Simplified) - Singapore + 0x1404: "zh_MO", # Chinese (Traditional) - Macao S.A.R. + 0x0083: "co", # Corsican 0x0483: "co_FR", # Corsican - France - 0x041a: "hr_HR", # Croatian - 0x101a: "hr_BA", # Croatian - Bosnia - 0x0405: "cs_CZ", # Czech - 0x0406: "da_DK", # Danish - 0x048c: "gbz_AF",# Dari - Afghanistan - 0x0465: "div_MV",# Divehi - Maldives - 0x0413: "nl_NL", # Dutch - The Netherlands + 0x001a: "hr", # Croatian + 0x041a: "hr_HR", # Croatian - Croatia + 0x101a: "hr_BA", # Croatian (Latin) - Bosnia and Herzegovina + 0x0005: "cs", # Czech + 0x0405: "cs_CZ", # Czech - Czech Republic + 0x0006: "da", # Danish + 0x0406: "da_DK", # Danish - Denmark + 0x008c: "prs", # Dari + 0x048c: "prs_AF", # Dari - Afghanistan + 0x0065: "dv", # Divehi + 0x0465: "dv_MV", # Divehi - Maldives + 0x0013: "nl", # Dutch + 0x0413: "nl_NL", # Dutch - Netherlands 0x0813: "nl_BE", # Dutch - Belgium + 0x0c51: "dz_BT", # Dzongkha - Bhutan + 0x0009: "en", # English 0x0409: "en_US", # English - United States 0x0809: "en_GB", # English - United Kingdom 0x0c09: "en_AU", # English - Australia @@ -1565,122 +1634,248 @@ def getpreferredencoding(do_setlocale=True): 0x1409: "en_NZ", # English - New Zealand 0x1809: "en_IE", # English - Ireland 0x1c09: "en_ZA", # English - South Africa - 0x2009: "en_JA", # English - Jamaica - 0x2409: "en_CB", # English - Caribbean + 0x2009: "en_JM", # English - Jamaica 0x2809: "en_BZ", # English - Belize - 0x2c09: "en_TT", # English - Trinidad + 0x2c09: "en_TT", # English - Trinidad and Tobago 0x3009: "en_ZW", # English - Zimbabwe - 0x3409: "en_PH", # English - Philippines + 0x3409: "en_PH", # English - Republic of the Philippines + 0x3c09: "en_HK", # English - Hong Kong 0x4009: "en_IN", # English - India 0x4409: "en_MY", # English - Malaysia - 0x4809: "en_IN", # English - Singapore - 0x0425: "et_EE", # Estonian - 0x0438: "fo_FO", # Faroese - 0x0464: "fil_PH",# Filipino - 0x040b: "fi_FI", # Finnish + 0x4809: "en_SG", # English - Singapore + 0x4c09: "en_AE", # English - United Arab Emirates + 0x0025: "et", # Estonian + 0x0425: "et_EE", # Estonian - Estonia + 0x0038: "fo", # Faroese + 0x0438: "fo_FO", # Faroese - Faroe Islands + 0x0064: "fil", # Filipino + 0x0464: "fil_PH", # Filipino - Philippines + 0x000b: "fi", # Finnish + 0x040b: "fi_FI", # Finnish - Finland + 0x000c: "fr", # French 0x040c: "fr_FR", # French - France 0x080c: "fr_BE", # French - Belgium 0x0c0c: "fr_CA", # French - Canada 0x100c: "fr_CH", # French - Switzerland 0x140c: "fr_LU", # French - Luxembourg - 0x180c: "fr_MC", # French - Monaco + 0x180c: "fr_MC", # French - Principality of Monaco + 0x1c0c: "fr_029", # French - Caribbean + 0x200c: "fr_RE", # French - Reunion + 0x240c: "fr_CD", # French - Congo, DRC + 0x280c: "fr_SN", # French - Senegal + 0x2c0c: "fr_CM", # French - Cameroon + 0x300c: "fr_CI", # French - Côte d'Ivoire + 0x340c: "fr_ML", # French - Mali + 0x380c: "fr_MA", # French - Morocco + 0x3c0c: "fr_HT", # French - Haiti + 0x0062: "fy", # Frisian 0x0462: "fy_NL", # Frisian - Netherlands - 0x0456: "gl_ES", # Galician - 0x0437: "ka_GE", # Georgian + 0x0067: "ff", # Fulah + 0x7c67: "ff", # Fulah (Latin) + 0x0467: "ff_NG", + 0x0867: "ff_SN", # Fulah - Senegal + 0x0056: "gl", # Galician + 0x0456: "gl_ES", # Galician - Spain + 0x0037: "ka", # Georgian + 0x0437: "ka_GE", # Georgian - Georgia + 0x0007: "de", # German 0x0407: "de_DE", # German - Germany 0x0807: "de_CH", # German - Switzerland 0x0c07: "de_AT", # German - Austria 0x1007: "de_LU", # German - Luxembourg 0x1407: "de_LI", # German - Liechtenstein - 0x0408: "el_GR", # Greek + 0x0008: "el", # Greek + 0x0408: "el_GR", # Greek - Greece + 0x006f: "kl", # Greenlandic 0x046f: "kl_GL", # Greenlandic - Greenland - 0x0447: "gu_IN", # Gujarati - 0x0468: "ha_NG", # Hausa - Latin - 0x040d: "he_IL", # Hebrew - 0x0439: "hi_IN", # Hindi - 0x040e: "hu_HU", # Hungarian - 0x040f: "is_IS", # Icelandic - 0x0421: "id_ID", # Indonesian - 0x045d: "iu_CA", # Inuktitut - Syllabics - 0x085d: "iu_CA", # Inuktitut - Latin + 0x0074: "gn", # Guarani + 0x0474: "gn_PY", # Guarani - Paraguay + 0x0047: "gu", # Gujarati + 0x0447: "gu_IN", # Gujarati - India + 0x0068: "ha", # Hausa (Latin) + 0x7c68: "ha", # Hausa (Latin) + 0x0468: "ha_NG", # Hausa (Latin) - Nigeria + 0x0075: "haw", # Hawaiian + 0x0475: "haw_US", # Hawaiian - United States + 0x000d: "he", # Hebrew + 0x040d: "he_IL", # Hebrew - Israel + 0x0039: "hi", # Hindi + 0x0439: "hi_IN", # Hindi - India + 0x000e: "hu", # Hungarian + 0x040e: "hu_HU", # Hungarian - Hungary + 0x000f: "is", # Icelandic + 0x040f: "is_IS", # Icelandic - Iceland + 0x0070: "ig", # Igbo + 0x0470: "ig_NG", # Igbo - Nigeria + 0x0021: "id", # Indonesian + 0x0421: "id_ID", # Indonesian - Indonesia + 0x005d: "iu", # Inuktitut (Latin) + 0x785d: "iu", # Inuktitut (Syllabics) + 0x7c5d: "iu", # Inuktitut (Latin) + 0x045d: "iu_CA", # Inuktitut (Syllabics) - Canada + 0x085d: "iu_CA", # Inuktitut (Latin) - Canada + 0x003c: "ga", # Irish 0x083c: "ga_IE", # Irish - Ireland + 0x0010: "it", # Italian 0x0410: "it_IT", # Italian - Italy 0x0810: "it_CH", # Italian - Switzerland - 0x0411: "ja_JP", # Japanese + 0x0011: "ja", # Japanese + 0x0411: "ja_JP", # Japanese - Japan + 0x004b: "kn", # Kannada 0x044b: "kn_IN", # Kannada - India - 0x043f: "kk_KZ", # Kazakh - 0x0453: "kh_KH", # Khmer - Cambodia - 0x0486: "qut_GT",# K'iche - Guatemala + 0x0471: "kr_NG", # Kanuri (Latin) - Nigeria + 0x0060: "ks", # Kashmiri + 0x0460: "ks", # Kashmiri - Perso_Arabic + 0x0860: "ks_IN", # Kashmiri (Devanagari) - India + 0x003f: "kk", # Kazakh + 0x043f: "kk_KZ", # Kazakh - Kazakhstan + 0x0053: "km", # Khmer + 0x0453: "km_KH", # Khmer - Cambodia + 0x0087: "rw", # Kinyarwanda 0x0487: "rw_RW", # Kinyarwanda - Rwanda - 0x0457: "kok_IN",# Konkani - 0x0412: "ko_KR", # Korean - 0x0440: "ky_KG", # Kyrgyz - 0x0454: "lo_LA", # Lao - Lao PDR - 0x0426: "lv_LV", # Latvian - 0x0427: "lt_LT", # Lithuanian - 0x082e: "dsb_DE",# Lower Sorbian - Germany - 0x046e: "lb_LU", # Luxembourgish - 0x042f: "mk_MK", # FYROM Macedonian + 0x0041: "sw", # Kiswahili + 0x0441: "sw_KE", # Kiswahili - Kenya + 0x0057: "kok", # Konkani + 0x0457: "kok_IN", # Konkani - India + 0x0012: "ko", # Korean + 0x0412: "ko_KR", # Korean - Korea + 0x0040: "ky", # Kyrgyz + 0x0440: "ky_KG", # Kyrgyz - Kyrgyzstan + 0x0054: "lo", # Lao + 0x0454: "lo_LA", # Lao - Lao P.D.R. + 0x0476: "la_VA", # Latin - Vatican City + 0x0026: "lv", # Latvian + 0x0426: "lv_LV", # Latvian - Latvia + 0x0027: "lt", # Lithuanian + 0x0427: "lt_LT", # Lithuanian - Lithuania + 0x7c2e: "dsb", # Lower Sorbian + 0x082e: "dsb_DE", # Lower Sorbian - Germany + 0x006e: "lb", # Luxembourgish + 0x046e: "lb_LU", # Luxembourgish - Luxembourg + 0x002f: "mk", # Macedonian + 0x042f: "mk_MK", # Macedonian - North Macedonia + 0x003e: "ms", # Malay 0x043e: "ms_MY", # Malay - Malaysia 0x083e: "ms_BN", # Malay - Brunei Darussalam + 0x004c: "ml", # Malayalam 0x044c: "ml_IN", # Malayalam - India - 0x043a: "mt_MT", # Maltese - 0x0481: "mi_NZ", # Maori - 0x047a: "arn_CL",# Mapudungun - 0x044e: "mr_IN", # Marathi - 0x047c: "moh_CA",# Mohawk - Canada - 0x0450: "mn_MN", # Mongolian - Cyrillic - 0x0850: "mn_CN", # Mongolian - PRC - 0x0461: "ne_NP", # Nepali - 0x0414: "nb_NO", # Norwegian - Bokmal - 0x0814: "nn_NO", # Norwegian - Nynorsk + 0x003a: "mt", # Maltese + 0x043a: "mt_MT", # Maltese - Malta + 0x0081: "mi", # Maori + 0x0481: "mi_NZ", # Maori - New Zealand + 0x007a: "arn", # Mapudungun + 0x047a: "arn_CL", # Mapudungun - Chile + 0x004e: "mr", # Marathi + 0x044e: "mr_IN", # Marathi - India + 0x007c: "moh", # Mohawk + 0x047c: "moh_CA", # Mohawk - Canada + 0x0050: "mn", # Mongolian (Cyrillic) + 0x7850: "mn", # Mongolian (Cyrillic) + 0x7c50: "mn", # Mongolian (Traditional Mongolian) + 0x0450: "mn_MN", # Mongolian (Cyrillic) - Mongolia + 0x0c50: "mn_MN", # Mongolian (Traditional Mongolian) - Mongolia + 0x0061: "ne", # Nepali + 0x0461: "ne_NP", # Nepali - Nepal + 0x0861: "ne_IN", # Nepali - India + 0x0014: "no", # Norwegian (Bokmal) + 0x0414: "nb_NO", # Norwegian (Bokmal) - Norway + 0x0814: "nn_NO", # Norwegian (Nynorsk) - Norway + 0x7814: "nn", # Norwegian (Nynorsk) + 0x7c14: "nb", # Norwegian (Bokmal) + 0x0082: "oc", # Occitan 0x0482: "oc_FR", # Occitan - France - 0x0448: "or_IN", # Oriya - India + 0x0048: "or", # Odia + 0x0448: "or_IN", # Odia - India + 0x0072: "om", # Oromo + 0x0472: "om_ET", # Oromo - Ethiopia + 0x0063: "ps", # Pashto 0x0463: "ps_AF", # Pashto - Afghanistan - 0x0429: "fa_IR", # Persian - 0x0415: "pl_PL", # Polish + 0x0029: "fa", # Persian + 0x0429: "fa_IR", # Persian - Iran + 0x0015: "pl", # Polish + 0x0415: "pl_PL", # Polish - Poland + 0x0016: "pt", # Portuguese 0x0416: "pt_BR", # Portuguese - Brazil 0x0816: "pt_PT", # Portuguese - Portugal - 0x0446: "pa_IN", # Punjabi - 0x046b: "quz_BO",# Quechua (Bolivia) - 0x086b: "quz_EC",# Quechua (Ecuador) - 0x0c6b: "quz_PE",# Quechua (Peru) + 0x0046: "pa", # Punjabi + 0x7c46: "pa", # Punjabi + 0x0446: "pa_IN", # Punjabi - India + 0x0846: "pa_PK", # Punjabi - Islamic Republic of Pakistan + 0x006b: "quz", # Quechua + 0x046b: "quz_BO", # Quechua - Bolivia + 0x086b: "quz_EC", # Quechua - Ecuador + 0x0c6b: "quz_PE", # Quechua - Peru + 0x0018: "ro", # Romanian 0x0418: "ro_RO", # Romanian - Romania - 0x0417: "rm_CH", # Romansh - 0x0419: "ru_RU", # Russian - 0x243b: "smn_FI",# Sami Finland - 0x103b: "smj_NO",# Sami Norway - 0x143b: "smj_SE",# Sami Sweden - 0x043b: "se_NO", # Sami Northern Norway - 0x083b: "se_SE", # Sami Northern Sweden - 0x0c3b: "se_FI", # Sami Northern Finland - 0x203b: "sms_FI",# Sami Skolt - 0x183b: "sma_NO",# Sami Southern Norway - 0x1c3b: "sma_SE",# Sami Southern Sweden - 0x044f: "sa_IN", # Sanskrit - 0x0c1a: "sr_SP", # Serbian - Cyrillic - 0x1c1a: "sr_BA", # Serbian - Bosnia Cyrillic - 0x081a: "sr_SP", # Serbian - Latin - 0x181a: "sr_BA", # Serbian - Bosnia Latin + 0x0818: "ro_MD", # Romanian - Moldova + 0x0017: "rm", # Romansh + 0x0417: "rm_CH", # Romansh - Switzerland + 0x0019: "ru", # Russian + 0x0419: "ru_RU", # Russian - Russia + 0x0819: "ru_MD", # Russian - Moldova + 0x0085: "sah", # Sakha + 0x0485: "sah_RU", # Sakha - Russia + 0x003b: "se", # Sami (Northern) + 0x043b: "se_NO", # Sami (Northern) - Norway + 0x083b: "se_SE", # Sami (Northern) - Sweden + 0x0c3b: "se_FI", # Sami (Northern) - Finland + 0x7c3b: "smj", # Sami (Lule) + 0x103b: "smj_NO", # Sami (Lule) - Norway + 0x143b: "smj_SE", # Sami (Lule) - Sweden + 0x783b: "sma", # Sami (Southern) + 0x183b: "sma_NO", # Sami (Southern) - Norway + 0x1c3b: "sma_SE", # Sami (Southern) - Sweden + 0x743b: "sms", # Sami (Skolt) + 0x203b: "sms_FI", # Sami (Skolt) - Finland + 0x703b: "smn", # Sami (Inari) + 0x243b: "smn_FI", # Sami (Inari) - Finland + 0x004f: "sa", # Sanskrit + 0x044f: "sa_IN", # Sanskrit - India + 0x0091: "gd", # Scottish Gaelic + 0x0491: "gd_GB", # Scottish Gaelic - United Kingdom + 0x6c1a: "sr", # Serbian (Cyrillic) + 0x701a: "sr", # Serbian (Latin) + 0x7c1a: "sr", # Serbian (Latin) + 0x081a: "sr_CS", # Serbian (Latin) - Serbia and Montenegro (Former) + 0x0c1a: "sr_CS", # Serbian (Cyrillic) - Serbia and Montenegro (Former) + 0x181a: "sr_BA", # Serbian (Latin) - Bosnia and Herzegovina + 0x1c1a: "sr_BA", # Serbian (Cyrillic) - Bosnia and Herzegovina + 0x241a: "sr_RS", # Serbian (Latin) - Serbia + 0x281a: "sr_RS", # Serbian (Cyrillic) - Serbia + 0x2c1a: "sr_ME", # Serbian (Latin) - Montenegro + 0x301a: "sr_ME", # Serbian (Cyrillic) - Montenegro + 0x006c: "nso", # Sesotho sa Leboa + 0x046c: "nso_ZA", # Sesotho sa Leboa - South Africa + 0x0032: "tn", # Setswana + 0x0432: "tn_ZA", # Setswana - South Africa + 0x0832: "tn_BW", # Setswana - Botswana + 0x0059: "sd", # Sindhi + 0x7c59: "sd", # Sindhi + 0x0859: "sd_PK", # Sindhi - Islamic Republic of Pakistan + 0x005b: "si", # Sinhala 0x045b: "si_LK", # Sinhala - Sri Lanka - 0x046c: "ns_ZA", # Northern Sotho - 0x0432: "tn_ZA", # Setswana - Southern Africa - 0x041b: "sk_SK", # Slovak - 0x0424: "sl_SI", # Slovenian + 0x001b: "sk", # Slovak + 0x041b: "sk_SK", # Slovak - Slovakia + 0x0024: "sl", # Slovenian + 0x0424: "sl_SI", # Slovenian - Slovenia + 0x0477: "so_SO", # Somali - Somalia + 0x0030: "st", # Sotho + 0x0430: "st_ZA", # Sotho - South Africa + 0x000a: "es", # Spanish 0x040a: "es_ES", # Spanish - Spain 0x080a: "es_MX", # Spanish - Mexico - 0x0c0a: "es_ES", # Spanish - Spain (Modern) + 0x0c0a: "es_ES", # Spanish - Spain 0x100a: "es_GT", # Spanish - Guatemala 0x140a: "es_CR", # Spanish - Costa Rica 0x180a: "es_PA", # Spanish - Panama 0x1c0a: "es_DO", # Spanish - Dominican Republic - 0x200a: "es_VE", # Spanish - Venezuela + 0x200a: "es_VE", # Spanish - Bolivarian Republic of Venezuela 0x240a: "es_CO", # Spanish - Colombia 0x280a: "es_PE", # Spanish - Peru 0x2c0a: "es_AR", # Spanish - Argentina 0x300a: "es_EC", # Spanish - Ecuador 0x340a: "es_CL", # Spanish - Chile - 0x380a: "es_UR", # Spanish - Uruguay + 0x380a: "es_UY", # Spanish - Uruguay 0x3c0a: "es_PY", # Spanish - Paraguay 0x400a: "es_BO", # Spanish - Bolivia 0x440a: "es_SV", # Spanish - El Salvador @@ -1688,36 +1883,87 @@ def getpreferredencoding(do_setlocale=True): 0x4c0a: "es_NI", # Spanish - Nicaragua 0x500a: "es_PR", # Spanish - Puerto Rico 0x540a: "es_US", # Spanish - United States -# 0x0430: "", # Sutu - Not supported - 0x0441: "sw_KE", # Swahili + 0x5c0a: "es_CU", # Spanish - Cuba + 0x001d: "sv", # Swedish 0x041d: "sv_SE", # Swedish - Sweden 0x081d: "sv_FI", # Swedish - Finland - 0x045a: "syr_SY",# Syriac - 0x0428: "tg_TJ", # Tajik - Cyrillic - 0x085f: "tmz_DZ",# Tamazight - Latin - 0x0449: "ta_IN", # Tamil - 0x0444: "tt_RU", # Tatar - 0x044a: "te_IN", # Telugu - 0x041e: "th_TH", # Thai - 0x0851: "bo_BT", # Tibetan - Bhutan - 0x0451: "bo_CN", # Tibetan - PRC - 0x041f: "tr_TR", # Turkish - 0x0442: "tk_TM", # Turkmen - Cyrillic - 0x0480: "ug_CN", # Uighur - Arabic - 0x0422: "uk_UA", # Ukrainian - 0x042e: "wen_DE",# Upper Sorbian - Germany - 0x0420: "ur_PK", # Urdu + 0x005a: "syr", # Syriac + 0x045a: "syr_SY", # Syriac - Syria + 0x0028: "tg", # Tajik (Cyrillic) + 0x7c28: "tg", # Tajik (Cyrillic) + 0x0428: "tg_TJ", # Tajik (Cyrillic) - Tajikistan + 0x005f: "tzm", # Tamazight (Latin) + 0x785f: "tzm", + 0x7c5f: "tzm", # Tamazight (Latin) + 0x085f: "tzm_DZ", # Tamazight (Latin) - Algeria + 0x045f: "tzm_MA", # Central Atlas Tamazight (Arabic) - Morocco + 0x105f: "tzm_MA", + 0x0049: "ta", # Tamil + 0x0449: "ta_IN", # Tamil - India + 0x0849: "ta_LK", # Tamil - Sri Lanka + 0x0044: "tt", # Tatar + 0x0444: "tt_RU", # Tatar - Russia + 0x004a: "te", # Telugu + 0x044a: "te_IN", # Telugu - India + 0x001e: "th", # Thai + 0x041e: "th_TH", # Thai - Thailand + 0x0051: "bo", # Tibetan + 0x0451: "bo_CN", # Tibetan - People's Republic of China + 0x0073: "ti", # Tigrinya + 0x0473: "ti_ET", # Tigrinya - Ethiopia + 0x0873: "ti_ER", # Tigrinya - Eritrea + 0x0031: "ts", # Tsonga + 0x0431: "ts_ZA", # Tsonga - South Africa + 0x001f: "tr", # Turkish + 0x041f: "tr_TR", # Turkish - Turkey + 0x0042: "tk", # Turkmen + 0x0442: "tk_TM", # Turkmen - Turkmenistan + 0x0022: "uk", # Ukrainian + 0x0422: "uk_UA", # Ukrainian - Ukraine + 0x002e: "hsb", # Upper Sorbian + 0x042e: "hsb_DE", # Upper Sorbian - Germany + 0x0020: "ur", # Urdu + 0x0420: "ur_PK", # Urdu - Islamic Republic of Pakistan 0x0820: "ur_IN", # Urdu - India - 0x0443: "uz_UZ", # Uzbek - Latin - 0x0843: "uz_UZ", # Uzbek - Cyrillic - 0x042a: "vi_VN", # Vietnamese - 0x0452: "cy_GB", # Welsh + 0x0080: "ug", # Uyghur + 0x0480: "ug_CN", # Uyghur - People's Republic of China + 0x0043: "uz", # Uzbek (Latin) + 0x7843: "uz", # Uzbek (Cyrillic) + 0x7c43: "uz", # Uzbek (Latin) + 0x0443: "uz_UZ", # Uzbek (Latin) - Uzbekistan + 0x0033: "ve", # Venda + 0x0433: "ve_ZA", # Venda - South Africa + 0x002a: "vi", # Vietnamese + 0x042a: "vi_VN", # Vietnamese - Vietnam + 0x0052: "cy", # Welsh + 0x0452: "cy_GB", # Welsh - United Kingdom + 0x0088: "wo", # Wolof 0x0488: "wo_SN", # Wolof - Senegal + 0x0034: "xh", # Xhosa 0x0434: "xh_ZA", # Xhosa - South Africa - 0x0485: "sah_RU",# Yakut - Cyrillic - 0x0478: "ii_CN", # Yi - PRC + 0x0078: "ii", # Yi + 0x0478: "ii_CN", # Yi - People's Republic of China + 0x043d: "yi_001", # Yiddish - World + 0x006a: "yo", # Yoruba 0x046a: "yo_NG", # Yoruba - Nigeria - 0x0435: "zu_ZA", # Zulu + 0x0035: "zu", # Zulu + 0x0435: "zu_ZA", # Zulu - South Africa + 0x0086: "qut", + +# 0x0001007f: "x-IV-mathan", # math alphanumeric sorting + 0x00010407: "de_DE", + 0x0001040e: "hu_HU", + 0x00010437: "ka_GE", + 0x00020804: "zh_CN", + 0x00021004: "zh_SG", + 0x00021404: "zh_MO", + 0x00030404: "zh_TW", + 0x00040404: "zh_TW", + 0x00040411: "ja_JP", + 0x00040c04: "zh_HK", + 0x00041404: "zh_MO", + 0x00050804: "zh_CN", + 0x00051004: "zh_SG", } def _print_locale(): diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index c5860d53b1bdff..b4a5f5cc2f598f 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -45,9 +45,6 @@ __author__ = "Vinay Sajip " __status__ = "production" -# The following module attributes are no longer updated. -__version__ = "0.5.1.2" -__date__ = "07 February 2010" #--------------------------------------------------------------------------- # Miscellaneous module data @@ -625,6 +622,9 @@ def __init__(self, fmt=None, datefmt=None, style='%', validate=True, *, self._fmt = self._style._fmt self.datefmt = datefmt + def __repr__(self): + return '<%s (%s)>' % (self.__class__.__name__, self._fmt) + default_time_format = '%Y-%m-%d %H:%M:%S' default_msec_format = '%s,%03d' @@ -797,6 +797,9 @@ def __init__(self, name=''): self.name = name self.nlen = len(name) + def __repr__(self): + return '<%s (%s)>' % (self.__class__.__name__, self.name) + def filter(self, record): """ Determine if the specified record is to be logged. @@ -1370,9 +1373,17 @@ def getLogger(self, name): logger and fix up the parent/child references which pointed to the placeholder to now point to the logger. """ - rv = None if not isinstance(name, str): raise TypeError('A logger name must be a string') + # Fast path: an already-registered, non-placeholder logger can be + # returned without taking the lock. dict.get() is atomic under both + # the GIL and free threading. A Logger is inserted into loggerDict only + # after it is fully wired up (parent/child references fixed) under the + # lock, so the fast path never observes a logger whose parent is not yet + # set. + rv = self.loggerDict.get(name) + if rv is not None and not isinstance(rv, PlaceHolder): + return rv with _lock: if name in self.loggerDict: rv = self.loggerDict[name] @@ -1380,14 +1391,18 @@ def getLogger(self, name): ph = rv rv = (self.loggerClass or _loggerClass)(name) rv.manager = self - self.loggerDict[name] = rv self._fixupChildren(ph, rv) self._fixupParents(rv) + # Publish only after rv is fully wired: the fast path reads + # loggerDict without the lock. + self.loggerDict[name] = rv else: rv = (self.loggerClass or _loggerClass)(name) rv.manager = self - self.loggerDict[name] = rv self._fixupParents(rv) + # Publish only after rv is fully wired: the fast path reads + # loggerDict without the lock. + self.loggerDict[name] = rv return rv def setLoggerClass(self, klass): @@ -1852,9 +1867,9 @@ class LoggerAdapter(object): def __init__(self, logger, extra=None, merge_extra=False): """ - Initialize the adapter with a logger and a dict-like object which - provides contextual information. This constructor signature allows - easy stacking of LoggerAdapters, if so desired. + Initialize the adapter with a logger and an optional dict-like object + which provides contextual information. This constructor signature + allows easy stacking of LoggerAdapters, if so desired. You can effectively pass keyword arguments as shown in the following example: @@ -1885,8 +1900,9 @@ def process(self, msg, kwargs): Normally, you'll only need to override this one method in a LoggerAdapter subclass for your specific needs. """ - if self.merge_extra and "extra" in kwargs: - kwargs["extra"] = {**self.extra, **kwargs["extra"]} + if self.merge_extra and kwargs.get("extra") is not None: + if self.extra is not None: + kwargs["extra"] = {**self.extra, **kwargs["extra"]} else: kwargs["extra"] = self.extra return msg, kwargs @@ -2341,3 +2357,16 @@ def captureWarnings(capture): if _warnings_showwarning is not None: warnings.showwarning = _warnings_showwarning _warnings_showwarning = None + + +def __getattr__(name): + if name in ("__version__", "__date__"): + from warnings import _deprecated + + _deprecated(name, remove=(3, 20)) + return { # Do not change + "__version__": "0.5.1.2", + "__date__": "07 February 2010", + }[name] + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 3d9aa00fa52d11..35233e731eb42d 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -21,7 +21,7 @@ Copyright (C) 2001-2022 Vinay Sajip. All Rights Reserved. -To use, simply 'import logging' and log away! +To use, simply 'import logging.config' and log away! """ import errno @@ -36,6 +36,7 @@ import threading import traceback +from bisect import bisect_left from socketserver import ThreadingTCPServer, StreamRequestHandler @@ -186,9 +187,8 @@ def _handle_existing_loggers(existing, child_loggers, disable_existing): what was intended by the user. Also, allow existing loggers to NOT be disabled if disable_existing is false. """ - root = logging.root for log in existing: - logger = root.manager.loggerDict[log] + logger = logging.root.manager.loggerDict[log] if log in child_loggers: if not isinstance(logger, logging.PlaceHolder): logger.setLevel(logging.NOTSET) @@ -197,6 +197,20 @@ def _handle_existing_loggers(existing, child_loggers, disable_existing): else: logger.disabled = disable_existing +def _forget_existing_logger(name, existing, existing_set, child_loggers): + """Forget a configured logger and record its existing children.""" + prefixed = name + "." + i = bisect_left(existing, prefixed) + num_existing = len(existing) + while i < num_existing: + child = existing[i] + if not child.startswith(prefixed): + break + if child in existing_set: + child_loggers[child] = None + i += 1 + existing_set.remove(name) + def _install_loggers(cp, handlers, disable_existing): """Create and install loggers""" @@ -235,25 +249,18 @@ def _install_loggers(cp, handlers, disable_existing): #named loggers. With a sorted list it is easier #to find the child loggers. existing.sort() + existing_set = set(existing) #We'll keep the list of existing loggers #which are children of named loggers here... - child_loggers = [] + child_loggers = {} #now set up the new ones... for log in llist: section = cp["logger_%s" % log] qn = section["qualname"] propagate = section.getint("propagate", fallback=1) logger = logging.getLogger(qn) - if qn in existing: - i = existing.index(qn) + 1 # start with the entry after qn - prefixed = qn + "." - pflen = len(prefixed) - num_existing = len(existing) - while i < num_existing: - if existing[i][:pflen] == prefixed: - child_loggers.append(existing[i]) - i += 1 - existing.remove(qn) + if qn in existing_set: + _forget_existing_logger(qn, existing, existing_set, child_loggers) if "level" in section: level = section["level"] logger.setLevel(level) @@ -281,6 +288,7 @@ def _install_loggers(cp, handlers, disable_existing): # logger.propagate = 1 # elif disable_existing_loggers: # logger.disabled = 1 + existing = [name for name in existing if name in existing_set] _handle_existing_loggers(existing, child_loggers, disable_existing) @@ -638,22 +646,16 @@ def configure(self): #named loggers. With a sorted list it is easier #to find the child loggers. existing.sort() + existing_set = set(existing) #We'll keep the list of existing loggers #which are children of named loggers here... - child_loggers = [] + child_loggers = {} #now set up the new ones... loggers = config.get('loggers', EMPTY_DICT) for name in loggers: - if name in existing: - i = existing.index(name) + 1 # look after name - prefixed = name + "." - pflen = len(prefixed) - num_existing = len(existing) - while i < num_existing: - if existing[i][:pflen] == prefixed: - child_loggers.append(existing[i]) - i += 1 - existing.remove(name) + if name in existing_set: + _forget_existing_logger(name, existing, existing_set, + child_loggers) try: self.configure_logger(name, loggers[name]) except Exception as e: @@ -673,6 +675,7 @@ def configure(self): # logger.propagate = True # elif disable_existing: # logger.disabled = True + existing = [name for name in existing if name in existing_set] _handle_existing_loggers(existing, child_loggers, disable_existing) @@ -865,28 +868,7 @@ def configure_handler(self, config): else: factory = klass kwargs = {k: config[k] for k in config if (k != '.' and valid_ident(k))} - # When deprecation ends for using the 'strm' parameter, remove the - # "except TypeError ..." - try: - result = factory(**kwargs) - except TypeError as te: - if "'stream'" not in str(te): - raise - #The argument name changed from strm to stream - #Retry with old name. - #This is so that code can be used with older Python versions - #(e.g. by Django) - kwargs['strm'] = kwargs.pop('stream') - result = factory(**kwargs) - - import warnings - warnings.warn( - "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.", - DeprecationWarning, - stacklevel=2, - ) + result = factory(**kwargs) if formatter: result.setFormatter(formatter) if level is not None: diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 2748b5941eade2..a5394d2dbea649 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -196,7 +196,11 @@ def shouldRollover(self, record): if self.stream is None: # delay was set... self.stream = self._open() if self.maxBytes > 0: # are we rolling over? - pos = self.stream.tell() + try: + pos = self.stream.tell() + except io.UnsupportedOperation: + # gh-143237: Never rollover a named pipe. + return False if not pos: # gh-116263: Never rollover an empty file return False @@ -278,7 +282,30 @@ def __init__(self, filename, when='h', interval=1, backupCount=0, # path object (see Issue #27493), but self.baseFilename will be a string filename = self.baseFilename if os.path.exists(filename): - t = int(os.stat(filename).st_mtime) + # Use the minimum of file creation and modification time as + # the base of the rollover calculation + creation_time = modification_time = None + if hasattr(os, 'statx'): + statx_result = os.statx(filename, + os.STATX_BTIME|os.STATX_CTIME|os.STATX_MTIME) + # Use stx_btime whenever it is available or use stx_ctime + # instead otherwise + creation_time = statx_result.stx_btime + if creation_time is None: + creation_time = statx_result.stx_ctime + modification_time = statx_result.stx_mtime + if creation_time is None or modification_time is None: + stat_result = os.stat(filename) + # Use st_birthtime whenever it is available or use st_ctime + # instead otherwise + if creation_time is None: + try: + creation_time = stat_result.st_birthtime + except AttributeError: + creation_time = stat_result.st_ctime + if modification_time is None: + modification_time = stat_result.st_mtime + t = int(min(creation_time, modification_time)) else: t = int(time.time()) self.rolloverAt = self.computeRollover(t) @@ -1125,7 +1152,7 @@ class NTEventLogHandler(logging.Handler): """ A handler class which sends events to the NT Event Log. Adds a registry entry for the specified application name. If no dllname is - provided, win32service.pyd (which contains some basic message + provided and pywin32 installed, win32service.pyd (which contains some basic message placeholders) is used. Note that use of these placeholders will make your event logs big, as the entire message source is held in the log. If you want slimmer logs, you have to pass in the name of your own DLL @@ -1133,38 +1160,46 @@ class NTEventLogHandler(logging.Handler): """ def __init__(self, appname, dllname=None, logtype="Application"): logging.Handler.__init__(self) - try: - import win32evtlogutil, win32evtlog - self.appname = appname - self._welu = win32evtlogutil - if not dllname: - dllname = os.path.split(self._welu.__file__) + import _winapi + self._winapi = _winapi + self.appname = appname + if not dllname: + # backward compatibility + try: + import win32evtlogutil + dllname = os.path.split(win32evtlogutil.__file__) dllname = os.path.split(dllname[0]) dllname = os.path.join(dllname[0], r'win32service.pyd') - self.dllname = dllname - self.logtype = logtype - # Administrative privileges are required to add a source to the registry. - # This may not be available for a user that just wants to add to an - # existing source - handle this specific case. - try: - self._welu.AddSourceToRegistry(appname, dllname, logtype) - except Exception as e: - # This will probably be a pywintypes.error. Only raise if it's not - # an "access denied" error, else let it pass - if getattr(e, 'winerror', None) != 5: # not access denied - raise - self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE - self.typemap = { - logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE, - logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE, - logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE, - logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE, - logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE, - } - except ImportError: - print("The Python Win32 extensions for NT (service, event "\ - "logging) appear not to be available.") - self._welu = None + except ImportError: + pass + self.dllname = dllname + self.logtype = logtype + # Administrative privileges are required to add a source to the registry. + # This may not be available for a user that just wants to add to an + # existing source - handle this specific case. + try: + self._add_source_to_registry(appname, dllname, logtype) + except PermissionError: + pass + self.deftype = _winapi.EVENTLOG_ERROR_TYPE + self.typemap = { + logging.DEBUG: _winapi.EVENTLOG_INFORMATION_TYPE, + logging.INFO: _winapi.EVENTLOG_INFORMATION_TYPE, + logging.WARNING: _winapi.EVENTLOG_WARNING_TYPE, + logging.ERROR: _winapi.EVENTLOG_ERROR_TYPE, + logging.CRITICAL: _winapi.EVENTLOG_ERROR_TYPE, + } + + @staticmethod + def _add_source_to_registry(appname, dllname, logtype): + import winreg + + key_path = f"SYSTEM\\CurrentControlSet\\Services\\EventLog\\{logtype}\\{appname}" + + with winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, key_path) as key: + if dllname: + winreg.SetValueEx(key, "EventMessageFile", 0, winreg.REG_EXPAND_SZ, dllname) + winreg.SetValueEx(key, "TypesSupported", 0, winreg.REG_DWORD, 7) # All types are supported def getMessageID(self, record): """ @@ -1205,15 +1240,20 @@ def emit(self, record): Determine the message ID, event category and event type. Then log the message in the NT event log. """ - if self._welu: + try: + id = self.getMessageID(record) + cat = self.getEventCategory(record) + type = self.getEventType(record) + msg = self.format(record) + + # Get a handle to the event log + handle = self._winapi.RegisterEventSource(None, self.appname) try: - id = self.getMessageID(record) - cat = self.getEventCategory(record) - type = self.getEventType(record) - msg = self.format(record) - self._welu.ReportEvent(self.appname, id, cat, type, [msg]) - except Exception: - self.handleError(record) + self._winapi.ReportEvent(handle, type, cat, id, msg) + finally: + self._winapi.DeregisterEventSource(handle) + except Exception: + self.handleError(record) def close(self): """ diff --git a/Lib/lzma.py b/Lib/lzma.py index 316066d024ea02..fb4bbf650f849a 100644 --- a/Lib/lzma.py +++ b/Lib/lzma.py @@ -13,6 +13,7 @@ "CHECK_ID_MAX", "CHECK_UNKNOWN", "FILTER_LZMA1", "FILTER_LZMA2", "FILTER_DELTA", "FILTER_X86", "FILTER_IA64", "FILTER_ARM", "FILTER_ARMTHUMB", "FILTER_POWERPC", "FILTER_SPARC", + "FILTER_ARM64", "FILTER_RISCV", "FORMAT_AUTO", "FORMAT_XZ", "FORMAT_ALONE", "FORMAT_RAW", "MF_HC3", "MF_HC4", "MF_BT2", "MF_BT3", "MF_BT4", "MODE_FAST", "MODE_NORMAL", "PRESET_DEFAULT", "PRESET_EXTREME", diff --git a/Lib/mailbox.py b/Lib/mailbox.py index b00d9e8634c785..99426220154360 100644 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -39,6 +39,13 @@ def __init__(self, path, factory=None, create=True): self._path = os.path.abspath(os.path.expanduser(path)) self._factory = factory + def __enter__(self): + self.lock() + return self + + def __exit__(self, type, value, traceback): + self.close() + def add(self, message): """Add message and return assigned key.""" raise NotImplementedError('Method must be implemented by subclass') @@ -2090,8 +2097,6 @@ def closed(self): return False return self._file.closed - __class_getitem__ = classmethod(GenericAlias) - class _PartialFile(_ProxyFile): """A read-only wrapper of part of a file.""" @@ -2183,11 +2188,7 @@ def _unlock_file(f): def _create_carefully(path): """Create a file if it doesn't exist and open for reading and writing.""" - fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR, 0o666) - try: - return open(path, 'rb+') - finally: - os.close(fd) + return open(path, 'xb+') def _create_temporary(path): """Create a temp file based on path and open for reading and writing.""" diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index 33e86d51a0fe50..15e8c0a437bfd9 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -93,14 +93,7 @@ def add_type(self, type, ext, strict=True): Valid extensions are empty or start with a '.'. """ if ext and not ext.startswith('.'): - from warnings import _deprecated - - _deprecated( - "Undotted extensions", - "Using undotted extensions is deprecated and " - "will raise a ValueError in Python {remove}", - remove=(3, 16), - ) + raise ValueError(f"Extension {ext!r} must start with '.'") if not type: return @@ -477,6 +470,8 @@ def _default_mime_types(): types_map = _types_map_default = { '.js' : 'text/javascript', '.mjs' : 'text/javascript', + '.dcm' : 'application/dicom', + '.efi' : 'application/efi', '.epub' : 'application/epub+zip', '.gz' : 'application/gzip', '.json' : 'application/json', @@ -486,26 +481,35 @@ def _default_mime_types(): '.wiz' : 'application/msword', '.nq' : 'application/n-quads', '.nt' : 'application/n-triples', + '.cjs' : 'application/node', '.bin' : 'application/octet-stream', '.a' : 'application/octet-stream', - '.dll' : 'application/octet-stream', - '.exe' : 'application/octet-stream', '.o' : 'application/octet-stream', '.obj' : 'application/octet-stream', '.so' : 'application/octet-stream', '.oda' : 'application/oda', '.ogx' : 'application/ogg', '.pdf' : 'application/pdf', + '.ai' : 'application/pdf', '.p7c' : 'application/pkcs7-mime', '.ps' : 'application/postscript', - '.ai' : 'application/postscript', '.eps' : 'application/postscript', + '.rtf' : 'application/rtf', + '.sql' : 'application/sql', + '.texi' : 'application/texinfo', + '.texinfo': 'application/texinfo', + '.toml' : 'application/toml', '.trig' : 'application/trig', '.m3u' : 'application/vnd.apple.mpegurl', '.m3u8' : 'application/vnd.apple.mpegurl', + '.dll' : 'application/vnd.microsoft.portable-executable', + '.exe' : 'application/vnd.microsoft.portable-executable', + '.cab' : 'application/vnd.ms-cab-compressed', '.xls' : 'application/vnd.ms-excel', '.xlb' : 'application/vnd.ms-excel', '.eot' : 'application/vnd.ms-fontobject', + '.chm' : 'application/vnd.ms-htmlhelp', + '.thmx' : 'application/vnd.ms-officetheme', '.ppt' : 'application/vnd.ms-powerpoint', '.pot' : 'application/vnd.ms-powerpoint', '.ppa' : 'application/vnd.ms-powerpoint', @@ -519,6 +523,8 @@ def _default_mime_types(): '.xlsx' : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', '.docx' : 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', '.rar' : 'application/vnd.rar', + '.sqlite3': 'application/vnd.sqlite3', + '.sqlite' : 'application/vnd.sqlite3', '.wasm' : 'application/wasm', '.7z' : 'application/x-7z-compressed', '.bcpio' : 'application/x-bcpio', @@ -548,8 +554,6 @@ def _default_mime_types(): '.tar' : 'application/x-tar', '.tcl' : 'application/x-tcl', '.tex' : 'application/x-tex', - '.texi' : 'application/x-texinfo', - '.texinfo': 'application/x-texinfo', '.roff' : 'application/x-troff', '.t' : 'application/x-troff', '.tr' : 'application/x-troff', @@ -587,11 +591,15 @@ def _default_mime_types(): '.aiff' : 'audio/x-aiff', '.ra' : 'audio/x-pn-realaudio', '.wav' : 'audio/vnd.wave', + '.weba' : 'audio/webm', + '.ttc' : 'font/collection', '.otf' : 'font/otf', '.ttf' : 'font/ttf', - '.weba' : 'audio/webm', '.woff' : 'font/woff', '.woff2' : 'font/woff2', + '.hjif' : 'haptics/hjif', + '.hmpg' : 'haptics/hmpg', + '.ivs' : 'haptics/ivs', '.avif' : 'image/avif', '.bmp' : 'image/bmp', '.emf' : 'image/emf', @@ -605,6 +613,7 @@ def _default_mime_types(): '.jpeg' : 'image/jpeg', '.jpm' : 'image/jpm', '.jpx' : 'image/jpx', + '.jxl' : 'image/jxl', '.heic' : 'image/heic', '.heif' : 'image/heif', '.png' : 'image/png', @@ -647,7 +656,6 @@ def _default_mime_types(): '.pl' : 'text/plain', '.srt' : 'text/plain', '.rtx' : 'text/richtext', - '.rtf' : 'text/rtf', '.tsv' : 'text/tab-separated-values', '.vtt' : 'text/vtt', '.py' : 'text/x-python', @@ -680,11 +688,9 @@ def _default_mime_types(): # Please sort these too common_types = _common_types_default = { - '.rtf' : 'application/rtf', '.apk' : 'application/vnd.android.package-archive', '.midi': 'audio/midi', '.mid' : 'audio/midi', - '.jpg' : 'image/jpg', '.pict': 'image/pict', '.pct' : 'image/pict', '.pic' : 'image/pict', @@ -699,7 +705,7 @@ def _parse_args(args): from argparse import ArgumentParser parser = ArgumentParser( - description='map filename extensions to MIME types', color=True + description='map filename extensions to MIME types', ) parser.add_argument( '-e', '--extension', @@ -718,24 +724,30 @@ def _parse_args(args): def _main(args=None): """Run the mimetypes command-line interface and return a text to print.""" - import sys - args, help_text = _parse_args(args) + results = [] if args.extension: for gtype in args.type: guess = guess_extension(gtype, not args.lenient) if guess: - return str(guess) - sys.exit(f"error: unknown type {gtype}") + results.append(str(guess)) + else: + results.append(f"error: unknown type {gtype}") + return results else: for gtype in args.type: guess, encoding = guess_type(gtype, not args.lenient) if guess: - return f"type: {guess} encoding: {encoding}" - sys.exit(f"error: media type unknown for {gtype}") - return help_text + results.append(f"type: {guess} encoding: {encoding}") + else: + results.append(f"error: media type unknown for {gtype}") + return results if __name__ == '__main__': - print(_main()) + import sys + + results = _main() + print("\n".join(results)) + sys.exit(any(result.startswith("error: ") for result in results)) diff --git a/Lib/modulefinder.py b/Lib/modulefinder.py index ac478ee7f51722..b8dccc2dd33afe 100644 --- a/Lib/modulefinder.py +++ b/Lib/modulefinder.py @@ -66,7 +66,12 @@ def _find_module(name, path=None): file_path = spec.origin - if spec.loader.is_package(name): + # On namespace packages, spec.loader might be None, but + # spec.submodule_search_locations should always be set — check it instead. + if isinstance(spec.submodule_search_locations, importlib.machinery.NamespacePath): + return None, spec.submodule_search_locations, ("", "", _PKG_DIRECTORY) + + if spec.loader.is_package(name): # non-namespace package return None, os.path.dirname(file_path), ("", "", _PKG_DIRECTORY) if isinstance(spec.loader, importlib.machinery.SourceFileLoader): @@ -334,7 +339,7 @@ def load_module(self, fqname, fp, pathname, file_info): self.msgout(2, "load_module ->", m) return m if type == _PY_SOURCE: - co = compile(fp.read(), pathname, 'exec') + co = compile(fp.read(), pathname, 'exec', module=fqname) elif type == _PY_COMPILED: try: data = fp.read() @@ -400,7 +405,6 @@ def scan_opcodes(self, co): yield "relative_import", (level, fromlist, name) def scan_code(self, co, m): - code = co.co_code scanner = self.scan_opcodes for what, args in scanner(co): if what == "store": @@ -454,6 +458,11 @@ def load_package(self, fqname, pathname): if newname: fqname = newname m = self.add_module(fqname) + + if isinstance(pathname, importlib.machinery.NamespacePath): + m.__path__ = pathname + return m + m.__file__ = pathname m.__path__ = [pathname] diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py index fc00d2861260a8..be2fc3edf5dce6 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -16,7 +16,6 @@ import sys import socket import struct -import tempfile import time @@ -46,11 +45,12 @@ CONNECTION_TIMEOUT = 20. _mmap_counter = itertools.count() +_MAX_PIPE_ATTEMPTS = 100 default_family = 'AF_INET' families = ['AF_INET'] -if hasattr(socket, 'AF_UNIX'): +if hasattr(socket, 'AF_UNIX') and sys.platform != 'cygwin': default_family = 'AF_UNIX' families += ['AF_UNIX'] @@ -76,10 +76,14 @@ def arbitrary_address(family): if family == 'AF_INET': return ('localhost', 0) elif family == 'AF_UNIX': - return tempfile.mktemp(prefix='sock-', dir=util.get_temp_dir()) + # NOTE: util.get_temp_dir() is a 0o700 per-process directory. A + # mktemp-style ToC vs ToU concern is not important; bind() surfaces + # the extremely unlikely collision as EADDRINUSE. + return os.path.join(util.get_temp_dir(), + f'sock-{os.urandom(6).hex()}') elif family == 'AF_PIPE': - return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' % - (os.getpid(), next(_mmap_counter)), dir="") + return (r'\\.\pipe\pyc-%d-%d-%s' % + (os.getpid(), next(_mmap_counter), os.urandom(8).hex())) else: raise ValueError('unrecognized family') @@ -472,17 +476,29 @@ class Listener(object): def __init__(self, address=None, family=None, backlog=1, authkey=None): family = family or (address and address_type(address)) \ or default_family - address = address or arbitrary_address(family) - _validate_family(family) + if authkey is not None and not isinstance(authkey, bytes): + raise TypeError('authkey should be a byte string') + if family == 'AF_PIPE': - self._listener = PipeListener(address, backlog) + if address: + self._listener = PipeListener(address, backlog) + else: + for attempts in itertools.count(): + address = arbitrary_address(family) + try: + self._listener = PipeListener(address, backlog) + break + except OSError as e: + if attempts >= _MAX_PIPE_ATTEMPTS: + raise + if e.winerror not in (_winapi.ERROR_PIPE_BUSY, + _winapi.ERROR_ACCESS_DENIED): + raise else: + address = address or arbitrary_address(family) self._listener = SocketListener(address, family, backlog) - if authkey is not None and not isinstance(authkey, bytes): - raise TypeError('authkey should be a byte string') - self._authkey = authkey def accept(self): @@ -570,7 +586,6 @@ def Pipe(duplex=True): ''' Returns pair of connection objects at either end of a pipe ''' - address = arbitrary_address('AF_PIPE') if duplex: openmode = _winapi.PIPE_ACCESS_DUPLEX access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE @@ -580,15 +595,25 @@ def Pipe(duplex=True): access = _winapi.GENERIC_WRITE obsize, ibsize = 0, BUFSIZE - h1 = _winapi.CreateNamedPipe( - address, openmode | _winapi.FILE_FLAG_OVERLAPPED | - _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE, - _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE | - _winapi.PIPE_WAIT, - 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, - # default security descriptor: the handle cannot be inherited - _winapi.NULL - ) + for attempts in itertools.count(): + address = arbitrary_address('AF_PIPE') + try: + h1 = _winapi.CreateNamedPipe( + address, openmode | _winapi.FILE_FLAG_OVERLAPPED | + _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE, + _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE | + _winapi.PIPE_WAIT, + 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, + # default security descriptor: the handle cannot be inherited + _winapi.NULL + ) + break + except OSError as e: + if attempts >= _MAX_PIPE_ATTEMPTS: + raise + if e.winerror not in (_winapi.ERROR_PIPE_BUSY, + _winapi.ERROR_ACCESS_DENIED): + raise h2 = _winapi.CreateFile( address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING, _winapi.FILE_FLAG_OVERLAPPED, _winapi.NULL @@ -709,8 +734,7 @@ def accept(self): # written data and then disconnected -- see Issue 14725. else: try: - res = _winapi.WaitForMultipleObjects( - [ov.event], False, INFINITE) + _winapi.WaitForMultipleObjects([ov.event], False, INFINITE) except: ov.cancel() _winapi.CloseHandle(handle) @@ -1064,14 +1088,22 @@ def wait(object_list, timeout=None): Returns list of those objects in object_list which are ready/readable. ''' + object_list = list(object_list) + + if not object_list: + if timeout is None: + while True: + time.sleep(1e6) + elif timeout > 0: + time.sleep(timeout) + return [] + if timeout is None: timeout = INFINITE elif timeout < 0: timeout = 0 else: timeout = int(timeout * 1000 + 0.5) - - object_list = list(object_list) waithandle_to_obj = {} ov_list = [] ready_objects = set() diff --git a/Lib/multiprocessing/context.py b/Lib/multiprocessing/context.py index 051d567d457928..45c393798deaca 100644 --- a/Lib/multiprocessing/context.py +++ b/Lib/multiprocessing/context.py @@ -145,7 +145,13 @@ def freeze_support(self): '''Check whether this is a fake forked process in a frozen executable. If so then run code specified by commandline and exit. ''' - if self.get_start_method() == 'spawn' and getattr(sys, 'frozen', False): + # gh-140814: allow_none=True avoids locking in the default start + # method, which would cause a later set_start_method() to fail. + # None is safe to pass through: spawn.freeze_support() + # independently detects whether this process is a spawned + # child, so the start method check here is only an optimization. + if (getattr(sys, 'frozen', False) + and self.get_start_method(allow_none=True) in ('spawn', None)): from .spawn import freeze_support freeze_support() @@ -177,12 +183,15 @@ def set_executable(self, executable): from .spawn import set_executable set_executable(executable) - def set_forkserver_preload(self, module_names): + def set_forkserver_preload(self, module_names, *, on_error='ignore'): '''Set list of module names to try to load in forkserver process. - This is really just a hint. + + The on_error parameter controls how import failures are handled: + "ignore" (default) silently ignores failures, "warn" emits warnings, + and "fail" raises exceptions breaking the forkserver context. ''' from .forkserver import set_forkserver_preload - set_forkserver_preload(module_names) + set_forkserver_preload(module_names, on_error=on_error) def get_context(self, method=None): if method is None: @@ -317,8 +326,10 @@ def _check_available(self): _concrete_contexts = { 'fork': ForkContext(), 'spawn': SpawnContext(), - 'forkserver': ForkServerContext(), } + if reduction.HAVE_SEND_HANDLE: + _concrete_contexts['forkserver'] = ForkServerContext() + # bpo-33725: running arbitrary code after fork() is no longer reliable # on macOS since macOS 10.14 (Mojave). Use spawn by default instead. # gh-84559: We changed everyones default to a thread safeish one in 3.14. diff --git a/Lib/multiprocessing/dummy/__init__.py b/Lib/multiprocessing/dummy/__init__.py index 6a1468609e347b..7dc5d1c8dde848 100644 --- a/Lib/multiprocessing/dummy/__init__.py +++ b/Lib/multiprocessing/dummy/__init__.py @@ -33,7 +33,7 @@ class DummyProcess(threading.Thread): - def __init__(self, group=None, target=None, name=None, args=(), kwargs={}): + def __init__(self, group=None, target=None, name=None, args=(), kwargs=None): threading.Thread.__init__(self, group, target, name, args, kwargs) self._pid = None self._children = weakref.WeakKeyDictionary() diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index c91891ff162c2d..b204a3ad0f1f01 100644 --- a/Lib/multiprocessing/forkserver.py +++ b/Lib/multiprocessing/forkserver.py @@ -42,6 +42,7 @@ def __init__(self): self._inherited_fds = None self._lock = threading.Lock() self._preload_modules = ['__main__'] + self._preload_on_error = 'ignore' def _stop(self): # Method used by unit tests to stop the server @@ -64,11 +65,22 @@ def _stop_unlocked(self): self._forkserver_address = None self._forkserver_authkey = None - def set_forkserver_preload(self, modules_names): - '''Set list of module names to try to load in forkserver process.''' + def set_forkserver_preload(self, modules_names, *, on_error='ignore'): + '''Set list of module names to try to load in forkserver process. + + The on_error parameter controls how import failures are handled: + "ignore" (default) silently ignores failures, "warn" emits warnings, + and "fail" raises exceptions breaking the forkserver context. + ''' if not all(type(mod) is str for mod in modules_names): raise TypeError('module_names must be a list of strings') + if on_error not in ('ignore', 'warn', 'fail'): + raise ValueError( + f"on_error must be 'ignore', 'warn', or 'fail', " + f"not {on_error!r}" + ) self._preload_modules = modules_names + self._preload_on_error = on_error def get_inherited_fds(self): '''Return list of fds inherited from parent process. @@ -107,6 +119,14 @@ def connect_to_new_process(self, fds): wrapped_client, self._forkserver_authkey) connection.deliver_challenge( wrapped_client, self._forkserver_authkey) + except (EOFError, ConnectionError, BrokenPipeError) as exc: + if (self._preload_modules and + self._preload_on_error == 'fail'): + exc.add_note( + "Forkserver process may have crashed during module " + "preloading. Check stderr." + ) + raise finally: wrapped_client._detach() del wrapped_client @@ -142,15 +162,27 @@ def ensure_running(self): self._forkserver_alive_fd = None self._forkserver_pid = None - cmd = ('from multiprocessing.forkserver import main; ' + - 'main(%d, %d, %r, **%r)') - + # gh-144503: sys_argv is passed as real argv elements after the + # ``-c cmd`` rather than repr'd into main_kws so that a large + # parent sys.argv cannot push the single ``-c`` command string + # over the OS per-argument length limit (MAX_ARG_STRLEN on Linux). + # The child sees them as sys.argv[1:]. + cmd = ('import sys; ' + 'from multiprocessing.forkserver import main; ' + 'main(%d, %d, %r, sys_argv=sys.argv[1:], **%r)') + + main_kws = {} + sys_argv = None if self._preload_modules: - desired_keys = {'main_path', 'sys_path'} data = spawn.get_preparation_data('ignore') - main_kws = {x: y for x, y in data.items() if x in desired_keys} - else: - main_kws = {} + if 'sys_path' in data: + main_kws['sys_path'] = data['sys_path'] + if 'init_main_from_path' in data: + main_kws['main_path'] = data['init_main_from_path'] + if 'sys_argv' in data: + sys_argv = data['sys_argv'] + if self._preload_on_error != 'ignore': + main_kws['on_error'] = self._preload_on_error with socket.socket(socket.AF_UNIX) as listener: address = connection.arbitrary_address('AF_UNIX') @@ -172,6 +204,8 @@ def ensure_running(self): exe = spawn.get_executable() args = [exe] + util._args_from_interpreter_flags() args += ['-c', cmd] + if sys_argv is not None: + args += sys_argv pid = util.spawnv_passfds(exe, args, fds_to_pass) except: os.close(alive_w) @@ -195,8 +229,69 @@ def ensure_running(self): # # +def _handle_import_error(on_error, modinfo, exc, *, warn_stacklevel): + """Handle an import error according to the on_error policy.""" + match on_error: + case 'fail': + raise + case 'warn': + warnings.warn( + f"Failed to preload {modinfo}: {exc}", + ImportWarning, + stacklevel=warn_stacklevel + 1 + ) + case 'ignore': + pass + + +def _handle_preload(preload, main_path=None, sys_path=None, sys_argv=None, + on_error='ignore'): + """Handle module preloading with configurable error handling. + + Args: + preload: List of module names to preload. + main_path: Path to __main__ module if '__main__' is in preload. + sys_path: sys.path to use for imports (None means use current). + sys_argv: sys.argv to use (None means use current). + on_error: How to handle import errors ("ignore", "warn", or "fail"). + """ + if not preload: + return + + if sys_argv is not None: + sys.argv[:] = sys_argv + if sys_path is not None: + sys.path[:] = sys_path + + if '__main__' in preload and main_path is not None: + process.current_process()._inheriting = True + try: + spawn.import_main_path(main_path) + except Exception as e: + # Catch broad Exception because import_main_path() uses + # runpy.run_path() which executes the script and can raise + # any exception, not just ImportError + _handle_import_error( + on_error, f"__main__ from {main_path!r}", e, warn_stacklevel=2 + ) + finally: + del process.current_process()._inheriting + + for modname in preload: + try: + __import__(modname) + except ImportError as e: + _handle_import_error( + on_error, f"module {modname!r}", e, warn_stacklevel=2 + ) + + # gh-135335: flush stdout/stderr in case any of the preloaded modules + # wrote to them, otherwise children might inherit buffered data + util._flush_std_streams() + + def main(listener_fd, alive_r, preload, main_path=None, sys_path=None, - *, authkey_r=None): + *, sys_argv=None, authkey_r=None, on_error='ignore'): """Run forkserver.""" if authkey_r is not None: try: @@ -207,24 +302,7 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None, else: authkey = b'' - if preload: - if sys_path is not None: - sys.path[:] = sys_path - if '__main__' in preload and main_path is not None: - process.current_process()._inheriting = True - try: - spawn.import_main_path(main_path) - finally: - del process.current_process()._inheriting - for modname in preload: - try: - __import__(modname) - except ImportError: - pass - - # gh-135335: flush stdout/stderr in case any of the preloaded modules - # wrote to them, otherwise children might inherit buffered data - util._flush_std_streams() + _handle_preload(preload, main_path, sys_path, sys_argv, on_error) util._close_stdin() diff --git a/Lib/multiprocessing/heap.py b/Lib/multiprocessing/heap.py index 6217dfe12689b3..5c835648395f79 100644 --- a/Lib/multiprocessing/heap.py +++ b/Lib/multiprocessing/heap.py @@ -324,10 +324,6 @@ class BufferWrapper(object): _heap = Heap() def __init__(self, size): - if size < 0: - raise ValueError("Size {0:n} out of range".format(size)) - if sys.maxsize <= size: - raise OverflowError("Size {0:n} too large".format(size)) block = BufferWrapper._heap.malloc(size) self._state = (block, size) util.Finalize(self, BufferWrapper._heap.free, args=(block,)) diff --git a/Lib/multiprocessing/popen_fork.py b/Lib/multiprocessing/popen_fork.py index 7affa1b985f091..a02a53b6a176da 100644 --- a/Lib/multiprocessing/popen_fork.py +++ b/Lib/multiprocessing/popen_fork.py @@ -67,7 +67,17 @@ def _launch(self, process_obj): code = 1 parent_r, child_w = os.pipe() child_r, parent_w = os.pipe() - self.pid = os.fork() + # gh-146313: Tell the resource tracker's at-fork handler to keep + # the inherited pipe fd so this child reuses the parent's tracker + # (gh-80849) rather than closing it and launching its own. + from .resource_tracker import _fork_intent + _fork_intent.preserve_fd = True + try: + self.pid = os.fork() + finally: + # Reset in both parent and child so the flag does not leak + # into a subsequent raw os.fork() or nested Process launch. + _fork_intent.preserve_fd = False if self.pid == 0: try: atexit._clear() diff --git a/Lib/multiprocessing/popen_spawn_posix.py b/Lib/multiprocessing/popen_spawn_posix.py index 24b8634523e5f2..cccd659ae77637 100644 --- a/Lib/multiprocessing/popen_spawn_posix.py +++ b/Lib/multiprocessing/popen_spawn_posix.py @@ -57,6 +57,10 @@ def _launch(self, process_obj): self._fds.extend([child_r, child_w]) self.pid = util.spawnv_passfds(spawn.get_executable(), cmd, self._fds) + os.close(child_r) + child_r = None + os.close(child_w) + child_w = None self.sentinel = parent_r with open(parent_w, 'wb', closefd=False) as f: f.write(fp.getbuffer()) diff --git a/Lib/multiprocessing/process.py b/Lib/multiprocessing/process.py index 9db322be1aa6d6..262513f295fde5 100644 --- a/Lib/multiprocessing/process.py +++ b/Lib/multiprocessing/process.py @@ -77,7 +77,7 @@ class BaseProcess(object): def _Popen(self): raise NotImplementedError - def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, + def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None): assert group is None, 'group argument must be None for now' count = next(_process_counter) @@ -89,7 +89,7 @@ def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, self._closed = False self._target = target self._args = tuple(args) - self._kwargs = dict(kwargs) + self._kwargs = dict(kwargs) if kwargs else {} self._name = name or type(self).__name__ + '-' + \ ':'.join(str(i) for i in self._identity) if daemon is not None: diff --git a/Lib/multiprocessing/queues.py b/Lib/multiprocessing/queues.py index 925f043900004e..981599acf5ef26 100644 --- a/Lib/multiprocessing/queues.py +++ b/Lib/multiprocessing/queues.py @@ -121,7 +121,7 @@ def get(self, block=True, timeout=None): def qsize(self): # Raises NotImplementedError on Mac OSX because of broken sem_getvalue() - return self._maxsize - self._sem._semlock._get_value() + return self._maxsize - self._sem.get_value() def empty(self): return not self._poll() diff --git a/Lib/multiprocessing/resource_tracker.py b/Lib/multiprocessing/resource_tracker.py index c4d0ca81e7034a..d3328a8c6170a6 100644 --- a/Lib/multiprocessing/resource_tracker.py +++ b/Lib/multiprocessing/resource_tracker.py @@ -15,11 +15,16 @@ # this resource tracker process, "killall python" would probably leave unlinked # resources. +import base64 import os import signal import sys import threading +import time import warnings +from collections import deque + +import json from . import spawn from . import util @@ -62,6 +67,18 @@ def __init__(self): self._fd = None self._pid = None self._exitcode = None + self._reentrant_messages = deque() + + # True to use colon-separated lines, rather than JSON lines, + # for internal communication. (Mainly for testing). + # Filenames not supported by the simple format will always be sent + # using JSON. + # The reader should understand all formats. + self._use_simple_format = False + + # Set to True by _stop_locked() if the waitpid polling loop ran to + # its timeout without reaping the tracker. Exposed for tests. + self._waitpid_timed_out = False def _reentrant_call_error(self): # gh-109629: this happens if an explicit call to the ResourceTracker @@ -75,16 +92,51 @@ def __del__(self): # making sure child processess are cleaned before ResourceTracker # gets destructed. # see https://github.com/python/cpython/issues/88887 - self._stop(use_blocking_lock=False) + # gh-146313: use a timeout to avoid deadlocking if a forked child + # still holds the pipe's write end open. + self._stop(use_blocking_lock=False, wait_timeout=1.0) + + def _after_fork_in_child(self): + # gh-146313: Called in the child right after os.fork(). + # + # The tracker process is a child of the *parent*, not of us, so we + # could never waitpid() it anyway. Clearing _pid means our __del__ + # becomes a no-op (the early return for _pid is None). + # + # Whether we keep the inherited _fd depends on who forked us: + # + # - multiprocessing.Process with the 'fork' start method sets + # _fork_intent.preserve_fd before forking. The child keeps the + # fd and reuses the parent's tracker (gh-80849). This is safe + # because multiprocessing's atexit handler joins all children + # before the parent's __del__ runs, so by then the fd copies + # are gone and the parent can reap the tracker promptly. + # + # - A raw os.fork() leaves the flag unset. We close the fd in the child after forking so + # the parent's __del__ can reap the tracker without waiting + # for the child to exit. If we later need a tracker, ensure_running() + # will launch a fresh one. + self._lock._at_fork_reinit() + self._reentrant_messages.clear() + self._pid = None + self._exitcode = None + if (self._fd is not None and + not getattr(_fork_intent, 'preserve_fd', False)): + fd = self._fd + self._fd = None + try: + os.close(fd) + except OSError: + pass - def _stop(self, use_blocking_lock=True): + def _stop(self, use_blocking_lock=True, wait_timeout=None): if use_blocking_lock: with self._lock: - self._stop_locked() + self._stop_locked(wait_timeout=wait_timeout) else: acquired = self._lock.acquire(blocking=False) try: - self._stop_locked() + self._stop_locked(wait_timeout=wait_timeout) finally: if acquired: self._lock.release() @@ -94,11 +146,15 @@ def _stop_locked( close=os.close, waitpid=os.waitpid, waitstatus_to_exitcode=os.waitstatus_to_exitcode, + monotonic=time.monotonic, + sleep=time.sleep, + WNOHANG=getattr(os, 'WNOHANG', None), + wait_timeout=None, ): # This shouldn't happen (it might when called by a finalizer) # so we check for it anyway. if self._lock._recursion_count() > 1: - return self._reentrant_call_error() + raise self._reentrant_call_error() if self._fd is None: # not running return @@ -109,7 +165,35 @@ def _stop_locked( close(self._fd) self._fd = None - _, status = waitpid(self._pid, 0) + try: + if wait_timeout is None: + _, status = waitpid(self._pid, 0) + else: + # gh-146313: A forked child may still hold the pipe's write + # end open, preventing the tracker from seeing EOF and + # exiting. Poll with WNOHANG to avoid blocking forever. + deadline = monotonic() + wait_timeout + delay = 0.001 + while True: + result_pid, status = waitpid(self._pid, WNOHANG) + if result_pid != 0: + break + remaining = deadline - monotonic() + if remaining <= 0: + # The tracker is still running; it will be + # reparented to PID 1 (or the nearest subreaper) + # when we exit, and reaped there once all pipe + # holders release their fd. + self._pid = None + self._exitcode = None + self._waitpid_timed_out = True + return + delay = min(delay * 2, remaining, 0.1) + sleep(delay) + except ChildProcessError: + self._pid = None + self._exitcode = None + return self._pid = None @@ -128,76 +212,119 @@ def ensure_running(self): This can be run from any process. Usually a child process will use the resource created by its parent.''' + return self._ensure_running_and_write() + + def _teardown_dead_process(self): + os.close(self._fd) + + # Clean-up to avoid dangling processes. + try: + # _pid can be None if this process is a child from another + # python process, which has started the resource_tracker. + if self._pid is not None: + os.waitpid(self._pid, 0) + except ChildProcessError: + # The resource_tracker has already been terminated. + pass + self._fd = None + self._pid = None + self._exitcode = None + + warnings.warn('resource_tracker: process died unexpectedly, ' + 'relaunching. Some resources might leak.') + + def _launch(self): + fds_to_pass = [] + try: + fds_to_pass.append(sys.stderr.fileno()) + except Exception: + pass + r, w = os.pipe() + try: + fds_to_pass.append(r) + # process will out live us, so no need to wait on pid + exe = spawn.get_executable() + args = [ + exe, + *util._args_from_interpreter_flags(), + '-c', + f'from multiprocessing.resource_tracker import main;main({r})', + ] + # bpo-33613: Register a signal mask that will block the signals. + # This signal mask will be inherited by the child that is going + # to be spawned and will protect the child from a race condition + # that can make the child die before it registers signal handlers + # for SIGINT and SIGTERM. The mask is unregistered after spawning + # the child. + prev_sigmask = None + try: + if _HAVE_SIGMASK: + prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS) + pid = util.spawnv_passfds(exe, args, fds_to_pass) + finally: + if prev_sigmask is not None: + signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask) + except: + os.close(w) + raise + else: + self._fd = w + self._pid = pid + finally: + os.close(r) + + def _make_probe_message(self): + """Return a probe message.""" + if self._use_simple_format: + return b'PROBE:0:noop\n' + return ( + json.dumps( + {"cmd": "PROBE", "rtype": "noop"}, + ensure_ascii=True, + separators=(",", ":"), + ) + + "\n" + ).encode("ascii") + + def _ensure_running_and_write(self, msg=None): with self._lock: if self._lock._recursion_count() > 1: # The code below is certainly not reentrant-safe, so bail out - return self._reentrant_call_error() + if msg is None: + raise self._reentrant_call_error() + return self._reentrant_messages.append(msg) + if self._fd is not None: # resource tracker was launched before, is it still running? - if self._check_alive(): - # => still alive - return - # => dead, launch it again - os.close(self._fd) - - # Clean-up to avoid dangling processes. + if msg is None: + to_send = self._make_probe_message() + else: + to_send = msg try: - # _pid can be None if this process is a child from another - # python process, which has started the resource_tracker. - if self._pid is not None: - os.waitpid(self._pid, 0) - except ChildProcessError: - # The resource_tracker has already been terminated. - pass - self._fd = None - self._pid = None - self._exitcode = None + self._write(to_send) + except OSError: + self._teardown_dead_process() + self._launch() - warnings.warn('resource_tracker: process died unexpectedly, ' - 'relaunching. Some resources might leak.') + msg = None # message was sent in probe + else: + self._launch() - fds_to_pass = [] + while True: try: - fds_to_pass.append(sys.stderr.fileno()) - except Exception: - pass - cmd = 'from multiprocessing.resource_tracker import main;main(%d)' - r, w = os.pipe() - try: - fds_to_pass.append(r) - # process will out live us, so no need to wait on pid - exe = spawn.get_executable() - args = [exe] + util._args_from_interpreter_flags() - args += ['-c', cmd % r] - # bpo-33613: Register a signal mask that will block the signals. - # This signal mask will be inherited by the child that is going - # to be spawned and will protect the child from a race condition - # that can make the child die before it registers signal handlers - # for SIGINT and SIGTERM. The mask is unregistered after spawning - # the child. - prev_sigmask = None - try: - if _HAVE_SIGMASK: - prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS) - pid = util.spawnv_passfds(exe, args, fds_to_pass) - finally: - if prev_sigmask is not None: - signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask) - except: - os.close(w) - raise - else: - self._fd = w - self._pid = pid - finally: - os.close(r) + reentrant_msg = self._reentrant_messages.popleft() + except IndexError: + break + self._write(reentrant_msg) + if msg is not None: + self._write(msg) def _check_alive(self): '''Check that the pipe has not been closed by sending a probe.''' try: # We cannot use send here as it calls ensure_running, creating # a cycle. - os.write(self._fd, b'PROBE:0:noop\n') + os.write(self._fd, self._make_probe_message()) except OSError: return False else: @@ -211,27 +338,50 @@ def unregister(self, name, rtype): '''Unregister name of resource with resource tracker.''' self._send('UNREGISTER', name, rtype) - def _send(self, cmd, name, rtype): - try: - self.ensure_running() - except ReentrantCallError: - # The code below might or might not work, depending on whether - # the resource tracker was already running and still alive. - # Better warn the user. - # (XXX is warnings.warn itself reentrant-safe? :-) - warnings.warn( - f"ResourceTracker called reentrantly for resource cleanup, " - f"which is unsupported. " - f"The {rtype} object {name!r} might leak.") - msg = '{0}:{1}:{2}\n'.format(cmd, name, rtype).encode('ascii') - if len(msg) > 512: - # posix guarantees that writes to a pipe of less than PIPE_BUF - # bytes are atomic, and that PIPE_BUF >= 512 - raise ValueError('msg too long') + def _write(self, msg): nbytes = os.write(self._fd, msg) - assert nbytes == len(msg), "nbytes {0:n} but len(msg) {1:n}".format( - nbytes, len(msg)) + assert nbytes == len(msg), f"{nbytes=} != {len(msg)=}" + + def _send(self, cmd, name, rtype): + if self._use_simple_format and '\n' not in name: + msg = f"{cmd}:{name}:{rtype}\n".encode("ascii") + if len(msg) > 512: + # posix guarantees that writes to a pipe of less than PIPE_BUF + # bytes are atomic, and that PIPE_BUF >= 512 + raise ValueError('msg too long') + self._ensure_running_and_write(msg) + return + # POSIX guarantees that writes to a pipe of less than PIPE_BUF (512 on Linux) + # bytes are atomic. Therefore, we want the message to be shorter than 512 bytes. + # POSIX shm_open() and sem_open() require the name, including its leading slash, + # to be at most NAME_MAX bytes (255 on Linux) + # With json.dump(..., ensure_ascii=True) every non-ASCII byte becomes a 6-char + # escape like \uDC80. + # As we want the overall message to be kept atomic and therefore smaller than 512, + # we encode encode the raw name bytes with URL-safe Base64 - so a 255 long name + # will not exceed 340 bytes. + b = name.encode('utf-8', 'surrogateescape') + if len(b) > 255: + raise ValueError('shared memory name too long (max 255 bytes)') + b64 = base64.urlsafe_b64encode(b).decode('ascii') + + payload = {"cmd": cmd, "rtype": rtype, "base64_name": b64} + msg = (json.dumps(payload, ensure_ascii=True, separators=(",", ":")) + "\n").encode("ascii") + + # The entire JSON message is guaranteed < PIPE_BUF (512 bytes) by construction. + assert len(msg) <= 512, f"internal error: message too long ({len(msg)} bytes)" + assert msg.startswith(b'{') + + self._ensure_running_and_write(msg) + +# gh-146313: Per-thread flag set by .popen_fork.Popen._launch() just before +# os.fork(), telling _after_fork_in_child() to keep the inherited pipe fd so +# the child can reuse this tracker (gh-80849). Unset for raw os.fork() calls, +# where the child instead closes the fd so the parent's __del__ can reap the +# tracker. Using threading.local() keeps multiple threads calling +# popen_fork.Popen._launch() at once from clobbering eachothers intent. +_fork_intent = threading.local() _resource_tracker = ResourceTracker() ensure_running = _resource_tracker.ensure_running @@ -239,6 +389,34 @@ def _send(self, cmd, name, rtype): unregister = _resource_tracker.unregister getfd = _resource_tracker.getfd +# gh-146313: See _after_fork_in_child docstring. +if hasattr(os, 'register_at_fork'): + os.register_at_fork(after_in_child=_resource_tracker._after_fork_in_child) + + +def _decode_message(line): + if line.startswith(b'{'): + try: + obj = json.loads(line.decode('ascii')) + except Exception as e: + raise ValueError("malformed resource_tracker message: %r" % (line,)) from e + + cmd = obj["cmd"] + rtype = obj["rtype"] + b64 = obj.get("base64_name", "") + + if not isinstance(cmd, str) or not isinstance(rtype, str) or not isinstance(b64, str): + raise ValueError("malformed resource_tracker fields: %r" % (obj,)) + + try: + name = base64.urlsafe_b64decode(b64).decode('utf-8', 'surrogateescape') + except ValueError as e: + raise ValueError("malformed resource_tracker base64_name: %r" % (b64,)) from e + else: + cmd, rest = line.strip().decode('ascii').split(':', maxsplit=1) + name, rtype = rest.rsplit(':', maxsplit=1) + return cmd, rtype, name + def main(fd): '''Run resource tracker.''' @@ -262,7 +440,7 @@ def main(fd): with open(fd, 'rb') as f: for line in f: try: - cmd, name, rtype = line.strip().decode('ascii').split(':') + cmd, rtype, name = _decode_message(line) cleanup_func = _CLEANUP_FUNCS.get(rtype, None) if cleanup_func is None: raise ValueError( diff --git a/Lib/multiprocessing/spawn.py b/Lib/multiprocessing/spawn.py index daac1ecc34b55e..d43864c939cb63 100644 --- a/Lib/multiprocessing/spawn.py +++ b/Lib/multiprocessing/spawn.py @@ -184,7 +184,7 @@ def get_preparation_data(name): sys_argv=sys.argv, orig_dir=process.ORIGINAL_DIR, dir=os.getcwd(), - start_method=get_start_method(), + start_method=get_start_method(allow_none=True), ) # Figure out whether to initialise main in the subprocess as a module diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py index 30425047e9801a..9188114ae284c7 100644 --- a/Lib/multiprocessing/synchronize.py +++ b/Lib/multiprocessing/synchronize.py @@ -135,11 +135,16 @@ def __init__(self, value=1, *, ctx): SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX, ctx=ctx) def get_value(self): + '''Returns current value of Semaphore. + + Raises NotImplementedError on Mac OSX + because of broken sem_getvalue(). + ''' return self._semlock._get_value() def __repr__(self): try: - value = self._semlock._get_value() + value = self.get_value() except Exception: value = 'unknown' return '<%s(value=%s)>' % (self.__class__.__name__, value) @@ -155,7 +160,7 @@ def __init__(self, value=1, *, ctx): def __repr__(self): try: - value = self._semlock._get_value() + value = self.get_value() except Exception: value = 'unknown' return '<%s(value=%s, maxvalue=%s)>' % \ @@ -247,8 +252,8 @@ def _make_methods(self): def __repr__(self): try: - num_waiters = (self._sleeping_count._semlock._get_value() - - self._woken_count._semlock._get_value()) + num_waiters = (self._sleeping_count.get_value() - + self._woken_count.get_value()) except Exception: num_waiters = 'unknown' return '<%s(%s, %s)>' % (self.__class__.__name__, self._lock, num_waiters) diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py index a1a537dd48dea7..549fb07c27549e 100644 --- a/Lib/multiprocessing/util.py +++ b/Lib/multiprocessing/util.py @@ -126,12 +126,14 @@ def is_abstract_socket_namespace(address): # Function returning a temp directory which will be removed on exit # -# Maximum length of a socket file path is usually between 92 and 108 [1], -# but Linux is known to use a size of 108 [2]. BSD-based systems usually -# use a size of 104 or 108 and Windows does not create AF_UNIX sockets. +# Maximum length of a NULL-terminated [1] socket file path is usually +# between 92 and 108 [2], but Linux is known to use a size of 108 [3]. +# BSD-based systems usually use a size of 104 or 108 and Windows does +# not create AF_UNIX sockets. # -# [1]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html -# [2]: https://man7.org/linux/man-pages/man7/unix.7.html. +# [1]: https://github.com/python/cpython/issues/140734 +# [2]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html +# [3]: https://man7.org/linux/man-pages/man7/unix.7.html if sys.platform == 'linux': _SUN_PATH_MAX = 108 @@ -171,11 +173,13 @@ def _get_base_temp_dir(tempfile): # generated by tempfile._RandomNameSequence, which, by design, # is 8 characters long. # - # Thus, the length of socket filename will be: + # Thus, the socket file path length (without NULL terminator) will be: # # len(base_tempdir + '/pymp-XXXXXXXX' + '/sock-XXXXXXXX') sun_path_len = len(base_tempdir) + 14 + 14 - if sun_path_len <= _SUN_PATH_MAX: + # Strict inequality to account for the NULL terminator. + # See https://github.com/python/cpython/issues/140734. + if sun_path_len < _SUN_PATH_MAX: return base_tempdir # Fallback to the default system-wide temporary directory. # This ignores user-defined environment variables. @@ -201,7 +205,7 @@ def _get_base_temp_dir(tempfile): return base_tempdir warn("Ignoring user-defined temporary directory: %s", base_tempdir) # at most max(map(len, dirlist)) + 14 + 14 = 36 characters - assert len(base_system_tempdir) + 14 + 14 <= _SUN_PATH_MAX + assert len(base_system_tempdir) + 14 + 14 < _SUN_PATH_MAX return base_system_tempdir def get_temp_dir(): diff --git a/Lib/netrc.py b/Lib/netrc.py index 2f502c1d53364f..a28ea297df894b 100644 --- a/Lib/netrc.py +++ b/Lib/netrc.py @@ -95,7 +95,7 @@ def _parse(self, file, fp, default_netrc): while 1: # Look for a machine, default, or macdef top-level keyword saved_lineno = lexer.lineno - toplevel = tt = lexer.get_token() + tt = lexer.get_token() if not tt: break elif tt[0] == '#': @@ -152,23 +152,28 @@ def _parse(self, file, fp, default_netrc): else: raise NetrcParseError("bad follower token %r" % tt, file, lexer.lineno) - self._security_check(fp, default_netrc, self.hosts[entryname][0]) - - def _security_check(self, fp, default_netrc, login): - if _can_security_check() and default_netrc and login != "anonymous": - prop = os.fstat(fp.fileno()) - current_user_id = os.getuid() - if prop.st_uid != current_user_id: - fowner = _getpwuid(prop.st_uid) - user = _getpwuid(current_user_id) - raise NetrcParseError( - f"~/.netrc file owner ({fowner}) does not match" - f" current user ({user})") - if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)): - raise NetrcParseError( - "~/.netrc access too permissive: access" - " permissions must restrict access to only" - " the owner") + + if _can_security_check() and default_netrc: + for entry in self.hosts.values(): + if entry[0] != "anonymous": + # Raises on security issue; once passed once can exit. + self._security_check(fp) + return + + def _security_check(self, fp): + prop = os.fstat(fp.fileno()) + current_user_id = os.getuid() + if prop.st_uid != current_user_id: + fowner = _getpwuid(prop.st_uid) + user = _getpwuid(current_user_id) + raise NetrcParseError( + f"~/.netrc file owner ({fowner}) does not match" + f" current user ({user})") + if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)): + raise NetrcParseError( + "~/.netrc access too permissive: access" + " permissions must restrict access to only" + " the owner") def authenticators(self, host): """Return a (user, account, password) tuple for given host.""" diff --git a/Lib/ntpath.py b/Lib/ntpath.py index fad15430a373fb..811e796f7766e9 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -29,7 +29,7 @@ "abspath","curdir","pardir","sep","pathsep","defpath","altsep", "extsep","devnull","realpath","supports_unicode_filenames","relpath", "samefile", "sameopenfile", "samestat", "commonpath", "isjunction", - "isdevdrive", "ALLOW_MISSING"] + "isdevdrive", "ALL_BUT_LAST", "ALLOW_MISSING"] def _get_bothseps(path): if isinstance(path, bytes): @@ -152,12 +152,14 @@ def splitdrive(p, /): It is always true that: result[0] + result[1] == p - If the path contained a drive letter, drive_or_unc will contain everything - up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir") + If the path contained a drive letter, drive_or_unc will contain + everything up to and including the colon. e.g. splitdrive("c:/dir") + returns ("c:", "/dir") - If the path contained a UNC path, the drive_or_unc will contain the host name - and share up to but not including the fourth directory separator character. - e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir") + If the path contained a UNC path, the drive_or_unc will contain the + host name and share up to but not including the fourth directory + separator character. e.g. splitdrive("//host/computer/dir") returns + ("//host/computer", "/dir") Paths cannot contain both a drive letter and a UNC path. @@ -222,8 +224,8 @@ def splitroot(p, /): def split(p, /): """Split a pathname. - Return tuple (head, tail) where tail is everything after the final slash. - Either part may be empty.""" + Return tuple (head, tail) where tail is everything after the final + slash. Either part may be empty.""" p = os.fspath(p) seps = _get_bothseps(p) d, r, p = splitroot(p) @@ -400,17 +402,23 @@ def expanduser(path): # XXX With COMMAND.COM you can use any characters in a variable name, # XXX except '^|<>='. +_varpattern = r"'[^']*'?|%(%|[^%]*%?)|\$(\$|[-\w]+|\{[^}]*\}?)" +_varsub = None +_varsubb = None + def expandvars(path): """Expand shell variables of the forms $var, ${var} and %var%. Unknown variables are left unchanged.""" path = os.fspath(path) + global _varsub, _varsubb if isinstance(path, bytes): if b'$' not in path and b'%' not in path: return path - import string - varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii') - quote = b'\'' + if not _varsubb: + import re + _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub + sub = _varsubb percent = b'%' brace = b'{' rbrace = b'}' @@ -419,94 +427,44 @@ def expandvars(path): else: if '$' not in path and '%' not in path: return path - import string - varchars = string.ascii_letters + string.digits + '_-' - quote = '\'' + if not _varsub: + import re + _varsub = re.compile(_varpattern, re.ASCII).sub + sub = _varsub percent = '%' brace = '{' rbrace = '}' dollar = '$' environ = os.environ - res = path[:0] - index = 0 - pathlen = len(path) - while index < pathlen: - c = path[index:index+1] - if c == quote: # no expansion within single quotes - path = path[index + 1:] - pathlen = len(path) - try: - index = path.index(c) - res += c + path[:index + 1] - except ValueError: - res += c + path - index = pathlen - 1 - elif c == percent: # variable or '%' - if path[index + 1:index + 2] == percent: - res += c - index += 1 - else: - path = path[index+1:] - pathlen = len(path) - try: - index = path.index(percent) - except ValueError: - res += percent + path - index = pathlen - 1 - else: - var = path[:index] - try: - if environ is None: - value = os.fsencode(os.environ[os.fsdecode(var)]) - else: - value = environ[var] - except KeyError: - value = percent + var + percent - res += value - elif c == dollar: # variable or '$$' - if path[index + 1:index + 2] == dollar: - res += c - index += 1 - elif path[index + 1:index + 2] == brace: - path = path[index+2:] - pathlen = len(path) - try: - index = path.index(rbrace) - except ValueError: - res += dollar + brace + path - index = pathlen - 1 - else: - var = path[:index] - try: - if environ is None: - value = os.fsencode(os.environ[os.fsdecode(var)]) - else: - value = environ[var] - except KeyError: - value = dollar + brace + var + rbrace - res += value - else: - var = path[:0] - index += 1 - c = path[index:index + 1] - while c and c in varchars: - var += c - index += 1 - c = path[index:index + 1] - try: - if environ is None: - value = os.fsencode(os.environ[os.fsdecode(var)]) - else: - value = environ[var] - except KeyError: - value = dollar + var - res += value - if c: - index -= 1 + + def repl(m): + lastindex = m.lastindex + if lastindex is None: + return m[0] + name = m[lastindex] + if lastindex == 1: + if name == percent: + return name + if not name.endswith(percent): + return m[0] + name = name[:-1] else: - res += c - index += 1 - return res + if name == dollar: + return name + if name.startswith(brace): + if not name.endswith(rbrace): + return m[0] + name = name[1:-1] + + try: + if environ is None: + return os.fsencode(os.environ[os.fsdecode(name)]) + else: + return environ[name] + except KeyError: + return m[0] + + return sub(repl, path) # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B. @@ -726,7 +684,8 @@ def realpath(path, /, *, strict=False): if strict is ALLOW_MISSING: ignored_error = FileNotFoundError - strict = True + elif strict is ALL_BUT_LAST: + ignored_error = FileNotFoundError elif strict: ignored_error = () else: @@ -746,6 +705,12 @@ def realpath(path, /, *, strict=False): raise OSError(str(ex)) from None path = normpath(path) except ignored_error as ex: + if strict is ALL_BUT_LAST: + dirname, basename = split(path) + if not basename: + dirname, basename = split(path) + if not isdir(dirname): + raise initial_winerror = ex.winerror path = _getfinalpathname_nonstrict(path, ignored_error=ignored_error) @@ -763,7 +728,7 @@ def realpath(path, /, *, strict=False): try: if _getfinalpathname(spath) == path: path = spath - except ValueError as ex: + except ValueError: # Unexpected, as an invalid path should not have gained a prefix # at any point, but we ignore this error just in case. pass diff --git a/Lib/numbers.py b/Lib/numbers.py index a2913e32cfada7..37fddb8917727b 100644 --- a/Lib/numbers.py +++ b/Lib/numbers.py @@ -290,18 +290,27 @@ def conjugate(self): class Rational(Real): - """.numerator and .denominator should be in lowest terms.""" + """To Real, Rational adds numerator and denominator properties. + + The numerator and denominator values should be in lowest terms, + with a positive denominator. + """ __slots__ = () @property @abstractmethod def numerator(self): + """The numerator of a rational number in lowest terms.""" raise NotImplementedError @property @abstractmethod def denominator(self): + """The denominator of a rational number in lowest terms. + + This denominator should be positive. + """ raise NotImplementedError # Concrete implementation of Real's conversion to float. diff --git a/Lib/opcode.py b/Lib/opcode.py index 0e9520b6832499..750d83b2c87af5 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -40,83 +40,96 @@ _intrinsic_2_descs = _opcode.get_intrinsic2_descs() _special_method_names = _opcode.get_special_method_names() _common_constants = [builtins.AssertionError, builtins.NotImplementedError, - builtins.tuple, builtins.all, builtins.any] + builtins.tuple, builtins.all, builtins.any, builtins.list, + builtins.set, + # Append-only — must match CONSTANT_* in + # Include/internal/pycore_opcode_utils.h. + None, "", True, False, -1, builtins.frozenset, ()] _nb_ops = _opcode.get_nb_ops() hascompare = [opmap["COMPARE_OP"]] -_cache_format = { - "LOAD_GLOBAL": { - "counter": 1, - "index": 1, - "module_keys_version": 1, - "builtin_keys_version": 1, - }, - "BINARY_OP": { - "counter": 1, - "descr": 4, - }, - "UNPACK_SEQUENCE": { - "counter": 1, - }, - "COMPARE_OP": { - "counter": 1, - }, - "CONTAINS_OP": { - "counter": 1, - }, - "FOR_ITER": { - "counter": 1, - }, - "LOAD_SUPER_ATTR": { - "counter": 1, - }, - "LOAD_ATTR": { - "counter": 1, - "version": 2, - "keys_version": 2, - "descr": 4, - }, - "STORE_ATTR": { - "counter": 1, - "version": 2, - "index": 1, - }, - "CALL": { - "counter": 1, - "func_version": 2, - }, - "CALL_KW": { - "counter": 1, - "func_version": 2, - }, - "STORE_SUBSCR": { - "counter": 1, - }, - "SEND": { - "counter": 1, - }, - "JUMP_BACKWARD": { - "counter": 1, - }, - "TO_BOOL": { - "counter": 1, - "version": 2, - }, - "POP_JUMP_IF_TRUE": { - "counter": 1, - }, - "POP_JUMP_IF_FALSE": { - "counter": 1, - }, - "POP_JUMP_IF_NONE": { - "counter": 1, - }, - "POP_JUMP_IF_NOT_NONE": { - "counter": 1, - }, -} +_cache_format = frozendict( + LOAD_GLOBAL=frozendict( + counter=1, + index=1, + module_keys_version=1, + builtin_keys_version=1, + ), + BINARY_OP=frozendict( + counter=1, + descr=4, + ), + UNPACK_SEQUENCE=frozendict( + counter=1, + ), + COMPARE_OP=frozendict( + counter=1, + ), + CONTAINS_OP=frozendict( + counter=1, + ), + FOR_ITER=frozendict( + counter=1, + ), + GET_ITER=frozendict( + counter=1, + ), + LOAD_SUPER_ATTR=frozendict( + counter=1, + ), + LOAD_ATTR=frozendict( + counter=1, + version=2, + keys_version=2, + descr=4, + ), + STORE_ATTR=frozendict( + counter=1, + version=2, + index=1, + ), + CALL=frozendict( + counter=1, + func_version=2, + ), + CALL_KW=frozendict( + counter=1, + func_version=2, + ), + CALL_FUNCTION_EX=frozendict( + counter=1, + ), + STORE_SUBSCR=frozendict( + counter=1, + ), + SEND=frozendict( + counter=1, + ), + JUMP_BACKWARD=frozendict( + counter=1, + ), + TO_BOOL=frozendict( + counter=1, + version=2, + ), + POP_JUMP_IF_TRUE=frozendict( + counter=1, + ), + POP_JUMP_IF_FALSE=frozendict( + counter=1, + ), + POP_JUMP_IF_NONE=frozendict( + counter=1, + ), + POP_JUMP_IF_NOT_NONE=frozendict( + counter=1, + ), + RESUME=frozendict( + counter=1, + ), +) -_inline_cache_entries = { +_inline_cache_entries = frozendict({ name : sum(value.values()) for (name, value) in _cache_format.items() -} +}) diff --git a/Lib/optparse.py b/Lib/optparse.py index 38cf16d21efffa..de1082442ef7f2 100644 --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -21,8 +21,6 @@ (options, args) = parser.parse_args() """ -__version__ = "1.5.3" - __all__ = ['Option', 'make_option', 'SUPPRESS_HELP', @@ -409,10 +407,12 @@ def _parse_num(val, type): def _parse_int(val): return _parse_num(val, int) -_builtin_cvt = { "int" : (_parse_int, _("integer")), - "long" : (_parse_int, _("integer")), - "float" : (float, _("floating-point")), - "complex" : (complex, _("complex")) } +_builtin_cvt = frozendict({ + "int": (_parse_int, _("integer")), + "long": (_parse_int, _("integer")), + "float": (float, _("floating-point")), + "complex": (complex, _("complex")), +}) def check_builtin(option, opt, value): (cvt, what) = _builtin_cvt[option.type] @@ -1374,7 +1374,7 @@ def parse_args(self, args=None, values=None): self.values = values try: - stop = self._process_args(largs, rargs, values) + self._process_args(largs, rargs, values) except (BadOptionError, OptionValueError) as err: self.error(str(err)) @@ -1669,3 +1669,12 @@ def _match_abbrev(s, wordmap): # which will become a factory function when there are many Option # classes. make_option = Option + + +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "1.5.3" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/os.py b/Lib/os.py index 12926c832f5ba5..a5e1d805556998 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -58,6 +58,11 @@ def _get_exports_list(module): __all__.append('_exit') except ImportError: pass + try: + from posix import _clearenv + __all__.append('_clearenv') + except ImportError: + pass import posixpath as path try: @@ -131,6 +136,8 @@ def _add(str, fn): _add("HAVE_UNLINKAT", "unlink") _add("HAVE_UNLINKAT", "rmdir") _add("HAVE_UTIMENSAT", "utime") + if _exists("statx"): + _set.add(statx) supports_dir_fd = _set _set = set() @@ -152,6 +159,8 @@ def _add(str, fn): _add("HAVE_FPATHCONF", "pathconf") if _exists("statvfs") and _exists("fstatvfs"): # mac os x10.3 _add("HAVE_FSTATVFS", "statvfs") + if _exists("statx"): + _set.add(statx) supports_fd = _set _set = set() @@ -190,6 +199,8 @@ def _add(str, fn): _add("HAVE_FSTATAT", "stat") _add("HAVE_UTIMENSAT", "utime") _add("MS_WINDOWS", "stat") + if _exists("statx"): + _set.add(statx) supports_follow_symlinks = _set del _set @@ -208,14 +219,17 @@ def _add(str, fn): # Super directory utilities. # (Inspired by Eric Raymond; the doc strings are mostly his) -def makedirs(name, mode=0o777, exist_ok=False): - """makedirs(name [, mode=0o777][, exist_ok=False]) +def makedirs(name, mode=0o777, exist_ok=False, *, parent_mode=None): + """makedirs(name [, mode=0o777][, exist_ok=False][, parent_mode=None]) - Super-mkdir; create a leaf directory and all intermediate ones. Works like - mkdir, except that any intermediate path segment (not just the rightmost) - will be created if it does not exist. If the target directory already - exists, raise an OSError if exist_ok is False. Otherwise no exception is - raised. This is recursive. + Super-mkdir; create a leaf directory and all intermediate ones. Works + like mkdir, except that any intermediate path segment (not just the + rightmost) will be created if it does not exist. If the target + directory already exists, raise an OSError if exist_ok is False. + Otherwise no exception is raised. If parent_mode is not None, it will + be used as the mode for any newly-created, intermediate-level + directories. Otherwise, intermediate directories are created with the + default permissions (respecting umask). This is recursive. """ head, tail = path.split(name) @@ -223,7 +237,11 @@ def makedirs(name, mode=0o777, exist_ok=False): head, tail = path.split(head) if head and tail and not path.exists(head): try: - makedirs(head, exist_ok=exist_ok) + if parent_mode is not None: + makedirs(head, mode=parent_mode, exist_ok=exist_ok, + parent_mode=parent_mode) + else: + makedirs(head, exist_ok=exist_ok) except FileExistsError: # Defeats race condition when another thread created the path pass @@ -303,12 +321,12 @@ def walk(top, topdown=True, onerror=None, followlinks=False): dirpath, dirnames, filenames dirpath is a string, the path to the directory. dirnames is a list of - the names of the subdirectories in dirpath (including symlinks to directories, - and excluding '.' and '..'). + the names of the subdirectories in dirpath (including symlinks to + directories, and excluding '.' and '..'). filenames is a list of the names of the non-directory files in dirpath. - Note that the names in the lists are just names, with no path components. - To get a full path (which begins with top) to a file or directory in - dirpath, do os.path.join(dirpath, name). + Note that the names in the lists are just names, with no path + components. To get a full path (which begins with top) to a file or + directory in dirpath, do os.path.join(dirpath, name). If optional arg 'topdown' is true or not specified, the triple for a directory is generated before the triples for any of its subdirectories @@ -318,13 +336,13 @@ def walk(top, topdown=True, onerror=None, followlinks=False): When topdown is true, the caller can modify the dirnames list in-place (e.g., via del or slice assignment), and walk will only recurse into the - subdirectories whose names remain in dirnames; this can be used to prune the - search, or to impose a specific order of visiting. Modifying dirnames when - topdown is false has no effect on the behavior of os.walk(), since the - directories in dirnames have already been generated by the time dirnames - itself is generated. No matter the value of topdown, the list of - subdirectories is retrieved before the tuples for the directory and its - subdirectories are generated. + subdirectories whose names remain in dirnames; this can be used to prune + the search, or to impose a specific order of visiting. Modifying + dirnames when topdown is false has no effect on the behavior of + os.walk(), since the directories in dirnames have already been generated + by the time dirnames itself is generated. No matter the value of + topdown, the list of subdirectories is retrieved before the tuples for + the directory and its subdirectories are generated. By default errors from the os.scandir() call are ignored. If optional arg 'onerror' is specified, it should be a function; it @@ -417,14 +435,16 @@ def walk(top, topdown=True, onerror=None, followlinks=False): # Yield before sub-directory traversal if going top down yield top, dirs, nondirs # Traverse into sub-directories - for dirname in reversed(dirs): - new_path = join(top, dirname) - # bpo-23605: os.path.islink() is used instead of caching - # entry.is_symlink() result during the loop on os.scandir() because - # the caller can replace the directory entry during the "yield" - # above. - if followlinks or not islink(new_path): - stack.append(new_path) + if dirs: + prefix = join(top, top[:0]) # Add trailing slash + for dirname in reversed(dirs): + new_path = prefix + dirname + # bpo-23605: os.path.islink() is used instead of caching + # entry.is_symlink() result during the loop on os.scandir() because + # the caller can replace the directory entry during the "yield" + # above. + if followlinks or not islink(new_path): + stack.append(new_path) else: # Yield after sub-directory traversal if going bottom up stack.append((top, dirs, nondirs)) @@ -449,9 +469,9 @@ def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd= The advantage of fwalk() over walk() is that it's safe against symlink races (when follow_symlinks is False). - If dir_fd is not None, it should be a file descriptor open to a directory, - and top should be relative; top will then be relative to that directory. - (dir_fd is always supported for fwalk.) + If dir_fd is not None, it should be a file descriptor open to + a directory, and top should be relative; top will then be relative to + that directory. (dir_fd is always supported for fwalk.) Caution: Since fwalk() yields file descriptors, those are only valid until the @@ -766,6 +786,12 @@ def __ror__(self, other): new.update(self) return new + if _exists("_clearenv"): + def clear(self): + _clearenv() + self._data.clear() + + def _create_environ_mapping(): if name == 'nt': # Where Env Var Names Must Be UPPERCASE @@ -813,6 +839,7 @@ def reload_environ(): env_data.clear() env_data.update(data) + __all__.append("reload_environ") def getenv(key, default=None): """Get an environment variable, return None if it doesn't exist. diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py index fd073445e89b62..ffec9c545ee11f 100644 --- a/Lib/pathlib/__init__.py +++ b/Lib/pathlib/__init__.py @@ -12,10 +12,13 @@ import posixpath import sys from errno import * -from glob import _StringGlobber, _no_recurse_symlinks from itertools import chain -from stat import S_ISDIR, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO +from stat import ( + S_IMODE, S_ISDIR, S_ISREG, S_ISLNK, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO, +) from _collections_abc import Sequence +lazy import shutil +lazy from glob import _StringGlobber, _no_recurse_symlinks try: import pwd @@ -27,10 +30,9 @@ grp = None from pathlib._os import ( - PathInfo, DirEntryInfo, - magic_open, vfspath, + vfsopen, vfspath, ensure_different_files, ensure_distinct_paths, - copyfile2, copyfileobj, copy_info, + copyfile2, copyfileobj, ) @@ -188,9 +190,6 @@ def __reduce__(self): def __repr__(self): return "{}({!r})".format(self.__class__.__name__, self.as_posix()) - def __fspath__(self): - return str(self) - def __bytes__(self): """Return the bytes representation of the path. This is only recommended to use under Unix.""" @@ -259,6 +258,9 @@ def __str__(self): self._tail) or '.' return self._str + __fspath__ = __str__ + __vfspath__ = __str__ + @classmethod def _format_parsed_parts(cls, drv, root, tail): if drv or root: @@ -335,13 +337,8 @@ def _raw_path(self): return paths[0] elif paths: # Join path segments from the initializer. - path = self.parser.join(*paths) - # Cache the joined path. - paths.clear() - paths.append(path) - return path + return self.parser.join(*paths) else: - paths.append('') return '' @property @@ -489,16 +486,19 @@ def relative_to(self, other, *, walk_up=False): """ if not hasattr(other, 'with_segments'): other = self.with_segments(other) - for step, path in enumerate(chain([other], other.parents)): + parts = [] + for path in chain([other], other.parents): if path == self or path in self.parents: break elif not walk_up: raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}") elif path.name == '..': raise ValueError(f"'..' segment in {str(other)!r} cannot be walked") + else: + parts.append('..') else: raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors") - parts = ['..'] * step + self._tail[len(path._tail):] + parts.extend(self._tail[len(path._tail):]) return self._from_parsed_parts('', '', parts) def is_relative_to(self, other): @@ -612,6 +612,211 @@ class PureWindowsPath(PurePath): __slots__ = () +_STAT_RESULT_ERROR = [] # falsy sentinel indicating stat() failed. + + +class _Info: + """Implementation of pathlib.types.PathInfo that provides status + information by querying a wrapped os.stat_result object. Don't try to + construct it yourself.""" + __slots__ = ('_path', '_entry', '_stat_result', '_lstat_result') + + def __init__(self, path, entry=None): + self._path = path + self._entry = entry + self._stat_result = None + self._lstat_result = None + + def __repr__(self): + path_type = "WindowsPath" if os.name == "nt" else "PosixPath" + return f"<{path_type}.info>" + + def _stat(self, *, follow_symlinks=True): + """Return the status as an os.stat_result.""" + if self._entry: + return self._entry.stat(follow_symlinks=follow_symlinks) + if follow_symlinks: + if not self._stat_result: + try: + self._stat_result = os.stat(self._path) + except (OSError, ValueError): + self._stat_result = _STAT_RESULT_ERROR + raise + return self._stat_result + else: + if not self._lstat_result: + try: + self._lstat_result = os.lstat(self._path) + except (OSError, ValueError): + self._lstat_result = _STAT_RESULT_ERROR + raise + return self._lstat_result + + def exists(self, *, follow_symlinks=True): + """Whether this path exists.""" + if self._entry: + if not follow_symlinks: + return True + if follow_symlinks: + if self._stat_result is _STAT_RESULT_ERROR: + return False + else: + if self._lstat_result is _STAT_RESULT_ERROR: + return False + try: + self._stat(follow_symlinks=follow_symlinks) + except (OSError, ValueError): + return False + return True + + def is_dir(self, *, follow_symlinks=True): + """Whether this path is a directory.""" + if self._entry: + try: + return self._entry.is_dir(follow_symlinks=follow_symlinks) + except OSError: + return False + if follow_symlinks: + if self._stat_result is _STAT_RESULT_ERROR: + return False + else: + if self._lstat_result is _STAT_RESULT_ERROR: + return False + try: + st = self._stat(follow_symlinks=follow_symlinks) + except (OSError, ValueError): + return False + return S_ISDIR(st.st_mode) + + def is_file(self, *, follow_symlinks=True): + """Whether this path is a regular file.""" + if self._entry: + try: + return self._entry.is_file(follow_symlinks=follow_symlinks) + except OSError: + return False + if follow_symlinks: + if self._stat_result is _STAT_RESULT_ERROR: + return False + else: + if self._lstat_result is _STAT_RESULT_ERROR: + return False + try: + st = self._stat(follow_symlinks=follow_symlinks) + except (OSError, ValueError): + return False + return S_ISREG(st.st_mode) + + def is_symlink(self): + """Whether this path is a symbolic link.""" + if self._entry: + try: + return self._entry.is_symlink() + except OSError: + return False + if self._lstat_result is _STAT_RESULT_ERROR: + return False + try: + st = self._stat(follow_symlinks=False) + except (OSError, ValueError): + return False + return S_ISLNK(st.st_mode) + + def _posix_permissions(self, *, follow_symlinks=True): + """Return the POSIX file permissions.""" + return S_IMODE(self._stat(follow_symlinks=follow_symlinks).st_mode) + + def _file_id(self, *, follow_symlinks=True): + """Returns the identifier of the file.""" + st = self._stat(follow_symlinks=follow_symlinks) + return st.st_dev, st.st_ino + + def _access_time_ns(self, *, follow_symlinks=True): + """Return the access time in nanoseconds.""" + return self._stat(follow_symlinks=follow_symlinks).st_atime_ns + + def _mod_time_ns(self, *, follow_symlinks=True): + """Return the modify time in nanoseconds.""" + return self._stat(follow_symlinks=follow_symlinks).st_mtime_ns + + if hasattr(os.stat_result, 'st_flags'): + def _bsd_flags(self, *, follow_symlinks=True): + """Return the flags.""" + return self._stat(follow_symlinks=follow_symlinks).st_flags + + if hasattr(os, 'listxattr'): + def _xattrs(self, *, follow_symlinks=True): + """Return the xattrs as a list of (attr, value) pairs, or an empty + list if extended attributes aren't supported.""" + try: + return [ + (attr, os.getxattr(self._path, attr, follow_symlinks=follow_symlinks)) + for attr in os.listxattr(self._path, follow_symlinks=follow_symlinks)] + except OSError as err: + if err.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES): + raise + return [] + + +def _copy_info(info, target, follow_symlinks=True): + """Copy metadata from the given PathInfo to the given local path.""" + copy_times_ns = ( + hasattr(info, '_access_time_ns') and + hasattr(info, '_mod_time_ns') and + (follow_symlinks or os.utime in os.supports_follow_symlinks)) + if copy_times_ns: + t0 = info._access_time_ns(follow_symlinks=follow_symlinks) + t1 = info._mod_time_ns(follow_symlinks=follow_symlinks) + os.utime(target, ns=(t0, t1), follow_symlinks=follow_symlinks) + + # We must copy extended attributes before the file is (potentially) + # chmod()'ed read-only, otherwise setxattr() will error with -EACCES. + copy_xattrs = ( + hasattr(info, '_xattrs') and + hasattr(os, 'setxattr') and + (follow_symlinks or os.setxattr in os.supports_follow_symlinks)) + if copy_xattrs: + xattrs = info._xattrs(follow_symlinks=follow_symlinks) + for attr, value in xattrs: + try: + os.setxattr(target, attr, value, follow_symlinks=follow_symlinks) + except OSError as e: + if e.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES): + raise + + copy_posix_permissions = ( + hasattr(info, '_posix_permissions') and + (follow_symlinks or os.chmod in os.supports_follow_symlinks)) + if copy_posix_permissions: + posix_permissions = info._posix_permissions(follow_symlinks=follow_symlinks) + try: + os.chmod(target, posix_permissions, follow_symlinks=follow_symlinks) + except NotImplementedError: + # if we got a NotImplementedError, it's because + # * follow_symlinks=False, + # * lchown() is unavailable, and + # * either + # * fchownat() is unavailable or + # * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW. + # (it returned ENOSUP.) + # therefore we're out of options--we simply cannot chown the + # symlink. give up, suppress the error. + # (which is what shutil always did in this circumstance.) + pass + + copy_bsd_flags = ( + hasattr(info, '_bsd_flags') and + hasattr(os, 'chflags') and + (follow_symlinks or os.chflags in os.supports_follow_symlinks)) + if copy_bsd_flags: + bsd_flags = info._bsd_flags(follow_symlinks=follow_symlinks) + try: + os.chflags(target, bsd_flags, follow_symlinks=follow_symlinks) + except OSError as why: + if why.errno not in (EOPNOTSUPP, ENOTSUP): + raise + + class Path(PurePath): """PurePath subclass that can make system calls. @@ -637,7 +842,7 @@ def info(self): try: return self._info except AttributeError: - self._info = PathInfo(self) + self._info = _Info(str(self)) return self._info def stat(self, *, follow_symlinks=True): @@ -784,6 +989,7 @@ def read_text(self, encoding=None, errors=None, newline=None): def write_bytes(self, data): """ Open the file in bytes mode, write to it, and close the file. + Return the number of bytes written. """ # type-check for the buffer interface before truncating the file view = memoryview(data) @@ -793,6 +999,7 @@ def write_bytes(self, data): def write_text(self, data, encoding=None, errors=None, newline=None): """ Open the file in text mode, write to it, and close the file. + Return the number of characters written. """ # Call io.text_encoding() here to ensure any warning is raised at an # appropriate stack level. @@ -817,7 +1024,7 @@ def _filter_trailing_slash(self, paths): def _from_dir_entry(self, dir_entry, path_str): path = self.with_segments(path_str) path._str = path_str - path._info = DirEntryInfo(dir_entry) + path._info = _Info(dir_entry.path, dir_entry) return path def iterdir(self): @@ -997,7 +1204,7 @@ def touch(self, mode=0o666, exist_ok=True): fd = os.open(self, flags, mode) os.close(fd) - def mkdir(self, mode=0o777, parents=False, exist_ok=False): + def mkdir(self, mode=0o777, parents=False, exist_ok=False, *, parent_mode=None): """ Create a new directory at this given path. """ @@ -1006,7 +1213,11 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False): except FileNotFoundError: if not parents or self.parent == self: raise - self.parent.mkdir(parents=True, exist_ok=True) + if parent_mode is not None: + self.parent.mkdir(mode=parent_mode, parents=True, exist_ok=True, + parent_mode=parent_mode) + else: + self.parent.mkdir(parents=True, exist_ok=True) self.mkdir(mode, parents=False, exist_ok=exist_ok) except OSError: # Cannot rely on checking for EEXIST, since the operating system @@ -1051,8 +1262,6 @@ def _delete(self): if self.is_symlink() or self.is_junction(): self.unlink() elif self.is_dir(): - # Lazy import to improve module import time - import shutil shutil.rmtree(self) else: self.unlink() @@ -1123,17 +1332,17 @@ def _copy_from(self, source, follow_symlinks=True, preserve_metadata=False): self.joinpath(child.name)._copy_from( child, follow_symlinks, preserve_metadata) if preserve_metadata: - copy_info(source.info, self) + _copy_info(source.info, self) else: self._copy_from_file(source, preserve_metadata) def _copy_from_file(self, source, preserve_metadata=False): ensure_different_files(source, self) - with magic_open(source, 'rb') as source_f: + with vfsopen(source, 'rb') as source_f: with open(self, 'wb') as target_f: copyfileobj(source_f, target_f) if preserve_metadata: - copy_info(source.info, self) + _copy_info(source.info, self) if copyfile2: # Use fast OS routine for local file copying where available. @@ -1155,12 +1364,12 @@ def _copy_from_file(self, source, preserve_metadata=False): def _copy_from_symlink(self, source, preserve_metadata=False): os.symlink(vfspath(source.readlink()), self, source.info.is_dir()) if preserve_metadata: - copy_info(source.info, self, follow_symlinks=False) + _copy_info(source.info, self, follow_symlinks=False) else: def _copy_from_symlink(self, source, preserve_metadata=False): os.symlink(vfspath(source.readlink()), self) if preserve_metadata: - copy_info(source.info, self, follow_symlinks=False) + _copy_info(source.info, self, follow_symlinks=False) def move(self, target): """ diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py new file mode 100644 index 00000000000000..58e137f2a92d3b --- /dev/null +++ b/Lib/pathlib/_local.py @@ -0,0 +1,12 @@ +""" +This module exists so that pathlib objects pickled under Python 3.13 can be +unpickled in 3.14+. +""" + +from pathlib import * + +__all__ = [ + "UnsupportedOperation", + "PurePath", "PurePosixPath", "PureWindowsPath", + "Path", "PosixPath", "WindowsPath", +] diff --git a/Lib/pathlib/_os.py b/Lib/pathlib/_os.py index 62a4adb555ea89..79a1969d5f83d6 100644 --- a/Lib/pathlib/_os.py +++ b/Lib/pathlib/_os.py @@ -4,7 +4,6 @@ from errno import * from io import TextIOWrapper, text_encoding -from stat import S_ISDIR, S_ISREG, S_ISLNK, S_IMODE import os import sys try: @@ -166,68 +165,100 @@ def copyfileobj(source_f, target_f): write_target(buf) -def magic_open(path, mode='r', buffering=-1, encoding=None, errors=None, - newline=None): +def _open_reader(obj): + cls = type(obj) + try: + open_reader = cls.__open_reader__ + except AttributeError: + cls_name = cls.__name__ + raise TypeError(f"{cls_name} can't be opened for reading") from None + else: + return open_reader(obj) + + +def _open_writer(obj, mode): + cls = type(obj) + try: + open_writer = cls.__open_writer__ + except AttributeError: + cls_name = cls.__name__ + raise TypeError(f"{cls_name} can't be opened for writing") from None + else: + return open_writer(obj, mode) + + +def _open_updater(obj, mode): + cls = type(obj) + try: + open_updater = cls.__open_updater__ + except AttributeError: + cls_name = cls.__name__ + raise TypeError(f"{cls_name} can't be opened for updating") from None + else: + return open_updater(obj, mode) + + +def vfsopen(obj, mode='r', buffering=-1, encoding=None, errors=None, + newline=None): """ Open the file pointed to by this path and return a file object, as the built-in open() function does. + + Unlike the built-in open() function, this function additionally accepts + 'openable' objects, which are objects with any of these special methods: + + __open_reader__() + __open_writer__(mode) + __open_updater__(mode) + + '__open_reader__' is called for 'r' mode; '__open_writer__' for 'a', 'w' + and 'x' modes; and '__open_updater__' for 'r+' and 'w+' modes. If text + mode is requested, the result is wrapped in an io.TextIOWrapper object. """ + if buffering != -1: + raise ValueError("buffer size can't be customized") text = 'b' not in mode if text: # Call io.text_encoding() here to ensure any warning is raised at an # appropriate stack level. encoding = text_encoding(encoding) try: - return open(path, mode, buffering, encoding, errors, newline) + return open(obj, mode, buffering, encoding, errors, newline) except TypeError: pass - cls = type(path) + if not text: + if encoding is not None: + raise ValueError("binary mode doesn't take an encoding argument") + if errors is not None: + raise ValueError("binary mode doesn't take an errors argument") + if newline is not None: + raise ValueError("binary mode doesn't take a newline argument") mode = ''.join(sorted(c for c in mode if c not in 'bt')) - if text: - try: - attr = getattr(cls, f'__open_{mode}__') - except AttributeError: - pass - else: - return attr(path, buffering, encoding, errors, newline) - elif encoding is not None: - raise ValueError("binary mode doesn't take an encoding argument") - elif errors is not None: - raise ValueError("binary mode doesn't take an errors argument") - elif newline is not None: - raise ValueError("binary mode doesn't take a newline argument") - - try: - attr = getattr(cls, f'__open_{mode}b__') - except AttributeError: - pass + if mode == 'r': + stream = _open_reader(obj) + elif mode in ('a', 'w', 'x'): + stream = _open_writer(obj, mode) + elif mode in ('+r', '+w'): + stream = _open_updater(obj, mode[1]) else: - stream = attr(path, buffering) - if text: - stream = TextIOWrapper(stream, encoding, errors, newline) - return stream - - raise TypeError(f"{cls.__name__} can't be opened with mode {mode!r}") + raise ValueError(f'invalid mode: {mode}') + if text: + stream = TextIOWrapper(stream, encoding, errors, newline) + return stream -def vfspath(path): +def vfspath(obj): """ Return the string representation of a virtual path object. """ + cls = type(obj) try: - return os.fsdecode(path) - except TypeError: - pass - - path_type = type(path) - try: - return path_type.__vfspath__(path) + vfspath_method = cls.__vfspath__ except AttributeError: - if hasattr(path_type, '__vfspath__'): - raise - - raise TypeError("expected str, bytes, os.PathLike or JoinablePath " - "object, not " + path_type.__name__) + cls_name = cls.__name__ + raise TypeError(f"expected JoinablePath object, not {cls_name}") from None + else: + return vfspath_method(obj) def ensure_distinct_paths(source, target): @@ -270,281 +301,3 @@ def ensure_different_files(source, target): err.filename = vfspath(source) err.filename2 = vfspath(target) raise err - - -def copy_info(info, target, follow_symlinks=True): - """Copy metadata from the given PathInfo to the given local path.""" - copy_times_ns = ( - hasattr(info, '_access_time_ns') and - hasattr(info, '_mod_time_ns') and - (follow_symlinks or os.utime in os.supports_follow_symlinks)) - if copy_times_ns: - t0 = info._access_time_ns(follow_symlinks=follow_symlinks) - t1 = info._mod_time_ns(follow_symlinks=follow_symlinks) - os.utime(target, ns=(t0, t1), follow_symlinks=follow_symlinks) - - # We must copy extended attributes before the file is (potentially) - # chmod()'ed read-only, otherwise setxattr() will error with -EACCES. - copy_xattrs = ( - hasattr(info, '_xattrs') and - hasattr(os, 'setxattr') and - (follow_symlinks or os.setxattr in os.supports_follow_symlinks)) - if copy_xattrs: - xattrs = info._xattrs(follow_symlinks=follow_symlinks) - for attr, value in xattrs: - try: - os.setxattr(target, attr, value, follow_symlinks=follow_symlinks) - except OSError as e: - if e.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES): - raise - - copy_posix_permissions = ( - hasattr(info, '_posix_permissions') and - (follow_symlinks or os.chmod in os.supports_follow_symlinks)) - if copy_posix_permissions: - posix_permissions = info._posix_permissions(follow_symlinks=follow_symlinks) - try: - os.chmod(target, posix_permissions, follow_symlinks=follow_symlinks) - except NotImplementedError: - # if we got a NotImplementedError, it's because - # * follow_symlinks=False, - # * lchown() is unavailable, and - # * either - # * fchownat() is unavailable or - # * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW. - # (it returned ENOSUP.) - # therefore we're out of options--we simply cannot chown the - # symlink. give up, suppress the error. - # (which is what shutil always did in this circumstance.) - pass - - copy_bsd_flags = ( - hasattr(info, '_bsd_flags') and - hasattr(os, 'chflags') and - (follow_symlinks or os.chflags in os.supports_follow_symlinks)) - if copy_bsd_flags: - bsd_flags = info._bsd_flags(follow_symlinks=follow_symlinks) - try: - os.chflags(target, bsd_flags, follow_symlinks=follow_symlinks) - except OSError as why: - if why.errno not in (EOPNOTSUPP, ENOTSUP): - raise - - -class _PathInfoBase: - __slots__ = ('_path', '_stat_result', '_lstat_result') - - def __init__(self, path): - self._path = str(path) - - def __repr__(self): - path_type = "WindowsPath" if os.name == "nt" else "PosixPath" - return f"<{path_type}.info>" - - def _stat(self, *, follow_symlinks=True, ignore_errors=False): - """Return the status as an os.stat_result, or None if stat() fails and - ignore_errors is true.""" - if follow_symlinks: - try: - result = self._stat_result - except AttributeError: - pass - else: - if ignore_errors or result is not None: - return result - try: - self._stat_result = os.stat(self._path) - except (OSError, ValueError): - self._stat_result = None - if not ignore_errors: - raise - return self._stat_result - else: - try: - result = self._lstat_result - except AttributeError: - pass - else: - if ignore_errors or result is not None: - return result - try: - self._lstat_result = os.lstat(self._path) - except (OSError, ValueError): - self._lstat_result = None - if not ignore_errors: - raise - return self._lstat_result - - def _posix_permissions(self, *, follow_symlinks=True): - """Return the POSIX file permissions.""" - return S_IMODE(self._stat(follow_symlinks=follow_symlinks).st_mode) - - def _file_id(self, *, follow_symlinks=True): - """Returns the identifier of the file.""" - st = self._stat(follow_symlinks=follow_symlinks) - return st.st_dev, st.st_ino - - def _access_time_ns(self, *, follow_symlinks=True): - """Return the access time in nanoseconds.""" - return self._stat(follow_symlinks=follow_symlinks).st_atime_ns - - def _mod_time_ns(self, *, follow_symlinks=True): - """Return the modify time in nanoseconds.""" - return self._stat(follow_symlinks=follow_symlinks).st_mtime_ns - - if hasattr(os.stat_result, 'st_flags'): - def _bsd_flags(self, *, follow_symlinks=True): - """Return the flags.""" - return self._stat(follow_symlinks=follow_symlinks).st_flags - - if hasattr(os, 'listxattr'): - def _xattrs(self, *, follow_symlinks=True): - """Return the xattrs as a list of (attr, value) pairs, or an empty - list if extended attributes aren't supported.""" - try: - return [ - (attr, os.getxattr(self._path, attr, follow_symlinks=follow_symlinks)) - for attr in os.listxattr(self._path, follow_symlinks=follow_symlinks)] - except OSError as err: - if err.errno not in (EPERM, ENOTSUP, ENODATA, EINVAL, EACCES): - raise - return [] - - -class _WindowsPathInfo(_PathInfoBase): - """Implementation of pathlib.types.PathInfo that provides status - information for Windows paths. Don't try to construct it yourself.""" - __slots__ = ('_exists', '_is_dir', '_is_file', '_is_symlink') - - def exists(self, *, follow_symlinks=True): - """Whether this path exists.""" - if not follow_symlinks and self.is_symlink(): - return True - try: - return self._exists - except AttributeError: - if os.path.exists(self._path): - self._exists = True - return True - else: - self._exists = self._is_dir = self._is_file = False - return False - - def is_dir(self, *, follow_symlinks=True): - """Whether this path is a directory.""" - if not follow_symlinks and self.is_symlink(): - return False - try: - return self._is_dir - except AttributeError: - if os.path.isdir(self._path): - self._is_dir = self._exists = True - return True - else: - self._is_dir = False - return False - - def is_file(self, *, follow_symlinks=True): - """Whether this path is a regular file.""" - if not follow_symlinks and self.is_symlink(): - return False - try: - return self._is_file - except AttributeError: - if os.path.isfile(self._path): - self._is_file = self._exists = True - return True - else: - self._is_file = False - return False - - def is_symlink(self): - """Whether this path is a symbolic link.""" - try: - return self._is_symlink - except AttributeError: - self._is_symlink = os.path.islink(self._path) - return self._is_symlink - - -class _PosixPathInfo(_PathInfoBase): - """Implementation of pathlib.types.PathInfo that provides status - information for POSIX paths. Don't try to construct it yourself.""" - __slots__ = () - - def exists(self, *, follow_symlinks=True): - """Whether this path exists.""" - st = self._stat(follow_symlinks=follow_symlinks, ignore_errors=True) - if st is None: - return False - return True - - def is_dir(self, *, follow_symlinks=True): - """Whether this path is a directory.""" - st = self._stat(follow_symlinks=follow_symlinks, ignore_errors=True) - if st is None: - return False - return S_ISDIR(st.st_mode) - - def is_file(self, *, follow_symlinks=True): - """Whether this path is a regular file.""" - st = self._stat(follow_symlinks=follow_symlinks, ignore_errors=True) - if st is None: - return False - return S_ISREG(st.st_mode) - - def is_symlink(self): - """Whether this path is a symbolic link.""" - st = self._stat(follow_symlinks=False, ignore_errors=True) - if st is None: - return False - return S_ISLNK(st.st_mode) - - -PathInfo = _WindowsPathInfo if os.name == 'nt' else _PosixPathInfo - - -class DirEntryInfo(_PathInfoBase): - """Implementation of pathlib.types.PathInfo that provides status - information by querying a wrapped os.DirEntry object. Don't try to - construct it yourself.""" - __slots__ = ('_entry',) - - def __init__(self, entry): - super().__init__(entry.path) - self._entry = entry - - def _stat(self, *, follow_symlinks=True, ignore_errors=False): - try: - return self._entry.stat(follow_symlinks=follow_symlinks) - except OSError: - if not ignore_errors: - raise - return None - - def exists(self, *, follow_symlinks=True): - """Whether this path exists.""" - if not follow_symlinks: - return True - return self._stat(ignore_errors=True) is not None - - def is_dir(self, *, follow_symlinks=True): - """Whether this path is a directory.""" - try: - return self._entry.is_dir(follow_symlinks=follow_symlinks) - except OSError: - return False - - def is_file(self, *, follow_symlinks=True): - """Whether this path is a regular file.""" - try: - return self._entry.is_file(follow_symlinks=follow_symlinks) - except OSError: - return False - - def is_symlink(self): - """Whether this path is a symbolic link.""" - try: - return self._entry.is_symlink() - except OSError: - return False diff --git a/Lib/pathlib/types.py b/Lib/pathlib/types.py index 42b80221608bcc..bb4a521223da04 100644 --- a/Lib/pathlib/types.py +++ b/Lib/pathlib/types.py @@ -13,7 +13,7 @@ from abc import ABC, abstractmethod from glob import _GlobberBase from io import text_encoding -from pathlib._os import (magic_open, vfspath, ensure_distinct_paths, +from pathlib._os import (vfsopen, vfspath, ensure_distinct_paths, ensure_different_files, copyfileobj) from pathlib import PurePath, Path from typing import Optional, Protocol, runtime_checkable @@ -234,6 +234,33 @@ def parents(self): parent = split(path)[0] return tuple(parents) + def relative_to(self, other, *, walk_up=False): + """Return the relative path to another path identified by the passed + arguments. If the operation is not possible (because this is not + related to the other path), raise ValueError. + + The *walk_up* parameter controls whether `..` may be used to resolve + the path. + """ + parts = [] + for path in (other,) + other.parents: + if self.is_relative_to(path): + break + elif not walk_up: + raise ValueError(f"{self!r} is not in the subpath of {other!r}") + elif path.name == '..': + raise ValueError(f"'..' segment in {other!r} cannot be walked") + else: + parts.append('..') + else: + raise ValueError(f"{self!r} and {other!r} have different anchors") + return self.with_segments(*parts, *self.parts[len(path.parts):]) + + def is_relative_to(self, other): + """Return True if the path is relative to another path or False. + """ + return other == self or other in self.parents + def full_match(self, pattern): """ Return True if this path matches the given glob-style pattern. The @@ -264,10 +291,10 @@ def info(self): raise NotImplementedError @abstractmethod - def __open_rb__(self, buffering=-1): + def __open_reader__(self): """ Open the file pointed to by this path for reading in binary mode and - return a file object, like open(mode='rb'). + return a file object. """ raise NotImplementedError @@ -275,7 +302,7 @@ def read_bytes(self): """ Open the file in bytes mode, read it, and close the file. """ - with magic_open(self, mode='rb', buffering=0) as f: + with vfsopen(self, mode='rb') as f: return f.read() def read_text(self, encoding=None, errors=None, newline=None): @@ -285,7 +312,7 @@ def read_text(self, encoding=None, errors=None, newline=None): # Call io.text_encoding() here to ensure any warning is raised at an # appropriate stack level. encoding = text_encoding(encoding) - with magic_open(self, mode='r', encoding=encoding, errors=errors, newline=newline) as f: + with vfsopen(self, mode='r', encoding=encoding, errors=errors, newline=newline) as f: return f.read() @abstractmethod @@ -394,25 +421,27 @@ def mkdir(self): raise NotImplementedError @abstractmethod - def __open_wb__(self, buffering=-1): + def __open_writer__(self, mode): """ Open the file pointed to by this path for writing in binary mode and - return a file object, like open(mode='wb'). + return a file object. """ raise NotImplementedError def write_bytes(self, data): """ Open the file in bytes mode, write to it, and close the file. + Return the number of bytes written. """ # type-check for the buffer interface before truncating the file view = memoryview(data) - with magic_open(self, mode='wb') as f: + with vfsopen(self, mode='wb') as f: return f.write(view) def write_text(self, data, encoding=None, errors=None, newline=None): """ Open the file in text mode, write to it, and close the file. + Return the number of characters written. """ # Call io.text_encoding() here to ensure any warning is raised at an # appropriate stack level. @@ -420,7 +449,7 @@ def write_text(self, data, encoding=None, errors=None, newline=None): if not isinstance(data, str): raise TypeError('data must be str, not %s' % data.__class__.__name__) - with magic_open(self, mode='w', encoding=encoding, errors=errors, newline=newline) as f: + with vfsopen(self, mode='w', encoding=encoding, errors=errors, newline=newline) as f: return f.write(data) def _copy_from(self, source, follow_symlinks=True): @@ -439,8 +468,8 @@ def _copy_from(self, source, follow_symlinks=True): stack.append((child, dst.joinpath(child.name))) else: ensure_different_files(src, dst) - with magic_open(src, 'rb') as source_f: - with magic_open(dst, 'wb') as target_f: + with vfsopen(src, 'rb') as source_f: + with vfsopen(dst, 'wb') as target_f: copyfileobj(source_f, target_f) diff --git a/Lib/pdb.py b/Lib/pdb.py index fc83728fb6dc94..01451f0229cacb 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -97,13 +97,16 @@ import selectors import threading import _colorize -import _pyrepl.utils from contextlib import ExitStack, closing, contextmanager -from rlcompleter import Completer from types import CodeType from warnings import deprecated +try: + import _pyrepl.utils +except ModuleNotFoundError: + _pyrepl = None + class Restart(Exception): """Causes a debugger to be restarted for the debugged python program.""" @@ -131,7 +134,7 @@ def find_first_executable_line(code): return code.co_firstlineno def find_function(funcname, filename): - cre = re.compile(r'def\s+%s(\s*\[.+\])?\s*[(]' % re.escape(funcname)) + cre = re.compile(r'(?:async\s+)?def\s+%s(\s*\[.+\])?\s*[(]' % re.escape(funcname)) try: fp = tokenize.open(filename) except OSError: @@ -184,19 +187,37 @@ class _ExecutableTarget: class _ScriptTarget(_ExecutableTarget): def __init__(self, target): - self._target = os.path.realpath(target) + self._check(target) + self._target = self._safe_realpath(target) + + # If PYTHONSAFEPATH (-P) is not set, sys.path[0] is the directory + # of pdb, and we should replace it with the directory of the script + if not sys.flags.safe_path: + sys.path[0] = os.path.dirname(self._target) - if not os.path.exists(self._target): + @staticmethod + def _check(target): + """ + Check that target is plausibly a script. + """ + if not os.path.exists(target): print(f'Error: {target} does not exist') sys.exit(1) - if os.path.isdir(self._target): + if os.path.isdir(target): print(f'Error: {target} is a directory') sys.exit(1) - # If safe_path(-P) is not set, sys.path[0] is the directory - # of pdb, and we should replace it with the directory of the script - if not sys.flags.safe_path: - sys.path[0] = os.path.dirname(self._target) + @staticmethod + def _safe_realpath(path): + """ + Return the canonical path (realpath) if it is accessible from the userspace. + Otherwise (for example, if the path is a symlink to an anonymous pipe), + return the original path. + + See GH-142315. + """ + realpath = os.path.realpath(path) + return realpath if os.path.exists(realpath) else path def __repr__(self): return self._target @@ -297,12 +318,34 @@ def namespace(self): class _PdbInteractiveConsole(code.InteractiveConsole): - def __init__(self, ns, message): + def __init__(self, ns=None, message=None): self._message = message super().__init__(locals=ns, local_exit=True) def write(self, data): - self._message(data, end='') + if self._message is not None: + self._message(data, end='') + else: + super().write(data) + + def more_lines(self, text): + # Generic Python multi-line completeness heuristic. + # Strips pyrepl's trailing auto-indent before compiling. + # This should be functionally identical to simple_interact._more_lines + src = text.rstrip(" \t") + n = len(src) + if n > 0 and text[n-1] == '\n': + text = src + try: + code_obj = self.compile(text, "", "single") + except (OverflowError, SyntaxError, ValueError): + lines = text.splitlines(keepends=True) + if len(lines) == 1: + return False + last = lines[-1] + return ((last.startswith((" ", "\t")) or last.strip() != "") + and not last.endswith("\n")) + return code_obj is None # Interaction prompt line will separate file and call info from code @@ -331,6 +374,117 @@ def get_default_backend(): return _default_backend +def _pyrepl_available(): + """return whether pdb should use _pyrepl for input""" + if os.getenv("PYTHON_BASIC_REPL"): + CAN_USE_PYREPL = False + else: + try: + from _pyrepl.main import CAN_USE_PYREPL + except ModuleNotFoundError: + CAN_USE_PYREPL = False + return CAN_USE_PYREPL + + +class PdbPyReplInput: + def __init__(self, pdb_instance, stdin, stdout, prompt): + import _pyrepl.readline + + self.pdb_instance = pdb_instance + self.prompt = prompt + self.console = _PdbInteractiveConsole() + if not (os.isatty(stdin.fileno())): + raise ValueError("stdin is not a TTY") + self.readline_wrapper = _pyrepl.readline._ReadlineWrapper( + f_in=stdin.fileno(), + f_out=stdout.fileno(), + config=_pyrepl.readline.ReadlineConfig( + completer_delims=frozenset(' \t\n`@#%^&*()=+[{]}\\|;:\'",<>?') + ) + ) + self.readline_wrapper.get_reader().gen_colors = self.gen_colors + + def readline(self): + + def more_lines(text): + if text.strip() == "\x1a": + # Ctrl + Z raises EOFError to quit pdb + # This is similarly handled in simple_interact.py + raise EOFError + cmd, _, line = self.pdb_instance.parseline(text) + if not line or not cmd: + return False + func = getattr(self.pdb_instance, 'do_' + cmd, None) + if func is not None: + return False + return self.console.more_lines(text) + + try: + pyrepl_completer = self.readline_wrapper.get_completer() + self.readline_wrapper.set_completer(self.complete) + multiline = ( + self.readline_wrapper.multiline_input( + more_lines, + self.prompt, + '... ' + ' ' * (len(self.prompt) - 4) + ) + '\n' + ) + return multiline + except EOFError: + return 'EOF' + finally: + self.readline_wrapper.set_completer(pyrepl_completer) + + def complete(self, text, state): + """ + This function is very similar to cmd.Cmd.complete. + However, cmd.Cmd.complete assumes that we use readline module, but + pyrepl does not use it. + """ + if state == 0: + origline = self.readline_wrapper.get_line_buffer() + line = origline.lstrip() + stripped = len(origline) - len(line) + begidx = self.readline_wrapper.get_begidx() - stripped + endidx = self.readline_wrapper.get_endidx() - stripped + if begidx > 0: + cmd, args, foo = self.pdb_instance.parseline(line) + if not cmd: + compfunc = self.pdb_instance.completedefault + else: + try: + compfunc = getattr(self.pdb_instance, 'complete_' + cmd) + except AttributeError: + compfunc = self.pdb_instance.completedefault + else: + compfunc = self.pdb_instance.completenames + self.completion_matches = compfunc(text, line, begidx, endidx) + try: + return self.completion_matches[state] + except IndexError: + return None + + def gen_colors(self, buffer): + from _pyrepl.utils import ColorSpan, Span + + if not buffer.strip(): + return + + leading_spaces = len(buffer) - len(buffer.lstrip()) + leading_text = buffer.split()[0] + if hasattr(self.pdb_instance, 'do_' + leading_text): + yield ColorSpan( + Span(leading_spaces, leading_spaces + len(leading_text) - 1), + "soft_keyword" + ) + # Redact the command text with spaces so there will be no duplicated + # color span generated for it later. + redact_length = leading_spaces + len(leading_text) + buffer = ' ' * redact_length + buffer[redact_length:] + + yield from _pyrepl.utils.gen_colors(buffer) + + class Pdb(bdb.Bdb, cmd.Cmd): _previous_sigint_handler = None @@ -347,8 +501,8 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, bdb.Bdb.__init__(self, skip=skip, backend=backend if backend else get_default_backend()) cmd.Cmd.__init__(self, completekey, stdin, stdout) sys.audit("pdb.Pdb") - if stdout: - self.use_rawinput = 0 + if stdin is not None and stdin is not sys.stdin: + self.use_rawinput = False self.prompt = '(Pdb) ' self.aliases = {} self.displaying = {} @@ -364,6 +518,13 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, readline.set_completer_delims(' \t\n`@#%^&*()=+[{]}\\|;:\'",<>?') except ImportError: pass + + self.pyrepl_input = None + if _pyrepl_available(): + try: + self.pyrepl_input = PdbPyReplInput(self, self.stdin, self.stdout, self.prompt) + except Exception: + pass self.allow_kbdint = False self.nosigint = nosigint # Consider these characters as part of the command so when the users type @@ -373,17 +534,22 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, # Read ~/.pdbrc and ./.pdbrc self.rcLines = [] if readrc: + home_rcfile = os.path.expanduser("~/.pdbrc") + local_rcfile = os.path.abspath(".pdbrc") + try: - with open(os.path.expanduser('~/.pdbrc'), encoding='utf-8') as rcFile: - self.rcLines.extend(rcFile) - except OSError: - pass - try: - with open(".pdbrc", encoding='utf-8') as rcFile: - self.rcLines.extend(rcFile) + with open(home_rcfile, encoding='utf-8') as rcfile: + self.rcLines.extend(rcfile) except OSError: pass + if local_rcfile != home_rcfile: + try: + with open(local_rcfile, encoding='utf-8') as rcfile: + self.rcLines.extend(rcfile) + except OSError: + pass + self.commands = {} # associates a command list to breakpoint numbers self.commands_defining = False # True while in the process of defining # a command list @@ -398,6 +564,12 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, self._current_task = None + self.lineno = None + self.stack = [] + self.curindex = 0 + self.curframe = None + self._user_requested_quit = False + def set_trace(self, frame=None, *, commands=None): Pdb._last_pdb_instance = self if frame is None: @@ -474,7 +646,7 @@ def forget(self): self.lineno = None self.stack = [] self.curindex = 0 - if hasattr(self, 'curframe') and self.curframe: + if self.curframe: self.curframe.f_globals.pop('__pdb_convenience_variables', None) self.curframe = None self.tb_lineno.clear() @@ -591,6 +763,31 @@ def user_exception(self, frame, exc_info): self.message('%s%s' % (prefix, self._format_exc(exc_value))) self.interaction(frame, exc_traceback) + @contextmanager + def _replace_attribute(self, attrs): + original_attrs = {} + for attr, value in attrs.items(): + original_attrs[attr] = getattr(self, attr) + setattr(self, attr, value) + try: + yield + finally: + for attr, value in original_attrs.items(): + setattr(self, attr, value) + + @contextmanager + def _maybe_use_pyrepl_as_stdin(self): + if self.pyrepl_input is None: + yield + return + + with self._replace_attribute({ + 'stdin': self.pyrepl_input, + 'use_rawinput': False, + 'prompt': '', + }): + yield + # General interaction function def _cmdloop(self): while True: @@ -598,7 +795,8 @@ def _cmdloop(self): # keyboard interrupts allow for an easy way to cancel # the current command, so allow them during interactive input self.allow_kbdint = True - self.cmdloop() + with self._maybe_use_pyrepl_as_stdin(): + self.cmdloop() self.allow_kbdint = False break except KeyboardInterrupt: @@ -648,7 +846,7 @@ def _show_display(self): def _get_tb_and_exceptions(self, tb_or_exc): """ - Given a tracecack or an exception, return a tuple of chained exceptions + Given a traceback or an exception, return a tuple of chained exceptions and current traceback to inspect. This will deal with selecting the right ``__cause__`` or ``__context__`` @@ -859,7 +1057,7 @@ def _exec_in_closure(self, source, globals, locals): locals.update(pdb_eval["write_back"]) eval_result = pdb_eval["result"] if eval_result is not None: - print(repr(eval_result)) + self.message(repr(eval_result)) return True @@ -1068,7 +1266,7 @@ def handle_command_def(self, line): return False def _colorize_code(self, code): - if self.colorize: + if self.colorize and _pyrepl: colors = list(_pyrepl.utils.gen_colors(code)) chars, _ = _pyrepl.utils.disp_str(code, colors=colors, force_color=True) code = "".join(chars) @@ -1092,6 +1290,31 @@ def set_convenience_variable(self, frame, name, value): # Generic completion functions. Individual complete_foo methods can be # assigned below to one of these functions. + @property + def rlcompleter(self): + """Return the `Completer` class from `rlcompleter`, while avoiding the + side effects of changing the completer from `import rlcompleter`. + + This is a compromise between GH-138860 and GH-139289. If GH-139289 is + fixed, then we don't need this and we can just `import rlcompleter` in + `Pdb.__init__`. + """ + if not hasattr(self, "_rlcompleter"): + try: + import readline + except ImportError: + # readline is not available, just get the Completer + from rlcompleter import Completer + self._rlcompleter = Completer + else: + # importing rlcompleter could have side effect of changing + # the current completer, we need to restore it + prev_completer = readline.get_completer() + from rlcompleter import Completer + self._rlcompleter = Completer + readline.set_completer(prev_completer) + return self._rlcompleter + def completenames(self, text, line, begidx, endidx): # Overwrite completenames() of cmd so for the command completion, # if no current command matches, check for expressions as well @@ -1186,10 +1409,9 @@ def completedefault(self, text, line, begidx, endidx): conv_vars = self.curframe.f_globals.get('__pdb_convenience_variables', {}) return [f"${name}" for name in conv_vars if name.startswith(text[1:])] - # Use rlcompleter to do the completion state = 0 matches = [] - completer = Completer(self.curframe.f_globals | self.curframe.f_locals) + completer = self.rlcompleter(self.curframe.f_globals | self.curframe.f_locals) while (match := completer.complete(text, state)) is not None: matches.append(match) state += 1 @@ -1204,8 +1426,8 @@ def _enable_rlcompleter(self, ns): return try: + completer = self.rlcompleter(ns) old_completer = readline.get_completer() - completer = Completer(ns) readline.set_completer(completer.complete) yield finally: @@ -1267,7 +1489,14 @@ def do_commands(self, arg): reached. """ if not arg: - bnum = len(bdb.Breakpoint.bpbynumber) - 1 + for bp in reversed(bdb.Breakpoint.bpbynumber): + if bp is None: + continue + bnum = bp.number + break + else: + self.error('cannot set commands: no existing breakpoint') + return else: try: bnum = int(arg) @@ -1457,7 +1686,9 @@ def lineinfo(self, identifier): f = self.lookupmodule(parts[0]) if f: fname = f - item = parts[1] + item = parts[1] + else: + return failed answer = find_function(item, self.canonic(fname)) return answer or failed @@ -1469,7 +1700,7 @@ def checkline(self, filename, lineno, module_globals=None): """ # this method should be callable before starting debugging, so default # to "no globals" if there is no current frame - frame = getattr(self, 'curframe', None) + frame = self.curframe if module_globals is None: module_globals = frame.f_globals if frame else None line = linecache.getline(filename, lineno, module_globals) @@ -2298,10 +2529,21 @@ def do_interact(self, arg): contains all the (global and local) names found in the current scope. """ ns = {**self.curframe.f_globals, **self.curframe.f_locals} - with self._enable_rlcompleter(ns): - console = _PdbInteractiveConsole(ns, message=self.message) - console.interact(banner="*pdb interact start*", - exitmsg="*exit from pdb interact command*") + console = _PdbInteractiveConsole(ns, message=self.message) + banner = "*pdb interact start*" + exitmsg = "*exit from pdb interact command*" + if self.pyrepl_input is not None: + from _pyrepl.simple_interact import run_multiline_interactive_console + self.message(banner) + try: + run_multiline_interactive_console(console) + except SystemExit: + pass + self.message(exitmsg) + else: + with self._enable_rlcompleter(ns): + console.interact(banner=banner, + exitmsg=exitmsg) def do_alias(self, arg): """alias [name [command]] @@ -2399,7 +2641,9 @@ def print_stack_trace(self, count=None): except KeyboardInterrupt: pass - def print_stack_entry(self, frame_lineno, prompt_prefix=line_prefix): + def print_stack_entry(self, frame_lineno, prompt_prefix=None): + if prompt_prefix is None: + prompt_prefix = line_prefix frame, lineno = frame_lineno if frame is self.curframe: prefix = '> ' @@ -3490,21 +3734,43 @@ def help(): pydoc.pager(__doc__) _usage = """\ -Debug the Python program given by pyfile. Alternatively, +Debug the Python program given by `pyfile`. Alternatively, an executable module or package to debug can be specified using -the -m switch. You can also attach to a running Python process -using the -p option with its PID. +the `-m` switch. You can also attach to a running Python process +using the `-p` option with its PID. -Initial commands are read from .pdbrc files in your home directory +Initial commands are read from `.pdbrc` files in your home directory and in the current directory, if they exist. Commands supplied with --c are executed after commands from .pdbrc files. +`-c` are executed after commands from `.pdbrc` files. -To let the script run until an exception occurs, use "-c continue". +To let the script run until an exception occurs, use `-c continue`. To let the script run up to a given line X in the debugged file, use -"-c 'until X'".""" +`-c 'until X'`.""" -def main(): +def exit_with_permission_help_text(): + """ + Prints a message pointing to platform-specific permission help text and exits the program. + This function is called when a PermissionError is encountered while trying + to attach to a process. + """ + print( + "Error: The specified process cannot be attached to due to insufficient permissions.\n" + "See the Python documentation for details on required privileges and troubleshooting:\n" + "https://docs.python.org/3.14/howto/remote_debugging.html#permission-requirements\n" + ) + sys.exit(1) + + +def parse_args(): + # We want pdb to be as intuitive as possible to users, so we need to do some + # heuristic parsing to deal with ambiguity. + # For example: + # "python -m pdb -m foo -p 1" should pass "-p 1" to "foo". + # "python -m pdb foo.py -m bar" should pass "-m bar" to "foo.py". + # "python -m pdb -m foo -m bar" should pass "-m bar" to "foo". + # This require some customized parsing logic to find the actual debug target. + import argparse parser = argparse.ArgumentParser( @@ -3512,58 +3778,69 @@ def main(): description=_usage, formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False, - color=True, ) - # We need to maunally get the script from args, because the first positional - # arguments could be either the script we need to debug, or the argument - # to the -m module + # Get all the commands out first. For backwards compatibility, we allow + # -c commands to be after the target. parser.add_argument('-c', '--command', action='append', default=[], metavar='command', dest='commands', - help='pdb commands to execute as if given in a .pdbrc file') - parser.add_argument('-m', metavar='module', dest='module') - parser.add_argument('-p', '--pid', type=int, help="attach to the specified PID", default=None) + help='pdb commands to execute as if given in a `.pdbrc` file') + + opts, args = parser.parse_known_args() - if len(sys.argv) == 1: + if not args: # If no arguments were given (python -m pdb), print the whole help message. # Without this check, argparse would only complain about missing required arguments. + # We need to add the arguments definitions here to get a proper help message. + parser.add_argument('-m', metavar='module', dest='module') + parser.add_argument('-p', '--pid', type=int, help="attach to the specified PID", default=None) parser.print_help() sys.exit(2) + elif args[0] == '-p' or args[0] == '--pid': + # Attach to a pid + parser.add_argument('-p', '--pid', type=int, help="attach to the specified PID", default=None) + opts, args = parser.parse_known_args() + if args: + # For --pid, any extra arguments are invalid. + parser.error(f"unrecognized arguments: {' '.join(args)}") + elif args[0] == '-m': + # Debug a module, we only need the first -m module argument. + # The rest is passed to the module itself. + parser.add_argument('-m', metavar='module', dest='module') + opt_module = parser.parse_args(args[:2]) + opts.module = opt_module.module + args = args[2:] + elif args[0] == '--': + args.pop(0) + if not args: + parser.error("missing script or module to run") + elif args[0].startswith('-'): + # Invalid argument before the script name. + invalid_args = list(itertools.takewhile(lambda a: a.startswith('-'), args)) + parser.error(f"unrecognized arguments: {' '.join(invalid_args)}") - opts, args = parser.parse_known_args() + # Otherwise it's debugging a script and we already parsed all -c commands. - if opts.pid: - # If attaching to a remote pid, unrecognized arguments are not allowed. - # This will raise an error if there are extra unrecognized arguments. - opts = parser.parse_args() - if opts.module: - parser.error("argument -m: not allowed with argument --pid") - attach(opts.pid, opts.commands) - return - elif opts.module: - # If a module is being debugged, we consider the arguments after "-m module" to - # be potential arguments to the module itself. We need to parse the arguments - # before "-m" to check if there is any invalid argument. - # e.g. "python -m pdb -m foo --spam" means passing "--spam" to "foo" - # "python -m pdb --spam -m foo" means passing "--spam" to "pdb" and is invalid - idx = sys.argv.index('-m') - args_to_pdb = sys.argv[1:idx] - # This will raise an error if there are invalid arguments - parser.parse_args(args_to_pdb) - else: - # If a script is being debugged, then pdb expects the script name as the first argument. - # Anything before the script is considered an argument to pdb itself, which would - # be invalid because it's not parsed by argparse. - invalid_args = list(itertools.takewhile(lambda a: a.startswith('-'), args)) - if invalid_args: - parser.error(f"unrecognized arguments: {' '.join(invalid_args)}") - sys.exit(2) + return opts, args + +def main(): + opts, args = parse_args() - if opts.module: + if getattr(opts, 'pid', None) is not None: + try: + attach(opts.pid, opts.commands) + except RuntimeError: + print( + f"Cannot attach to pid {opts.pid}, please make sure that the process exists " + "and is using the same Python version." + ) + sys.exit(1) + except PermissionError: + exit_with_permission_help_text() + return + elif getattr(opts, 'module', None) is not None: file = opts.module target = _ModuleTarget(file) else: - if not args: - parser.error("no module or script to run") file = args.pop(0) if file.endswith('.pyz'): target = _ZipTarget(file) diff --git a/Lib/pickle.py b/Lib/pickle.py index beaefae0479d3c..f92b1fde768fc7 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -17,7 +17,6 @@ Misc variables: - __version__ format_version compatible_formats @@ -190,6 +189,11 @@ def __init__(self, value): __all__.extend(x for x in dir() if x.isupper() and not x.startswith('_')) +# Data larger than this will be read in chunks, to prevent extreme +# overallocation. +_MIN_READ_BUF_SIZE = (1 << 20) + + class _Framer: _FRAME_SIZE_MIN = 4 @@ -288,7 +292,7 @@ def read(self, n): "pickle exhausted before end of frame") return data else: - return self.file_read(n) + return self._chunked_file_read(n) def readline(self): if self.current_frame: @@ -303,11 +307,23 @@ def readline(self): else: return self.file_readline() + def _chunked_file_read(self, size): + cursize = min(size, _MIN_READ_BUF_SIZE) + b = self.file_read(cursize) + while cursize < size and len(b) == cursize: + delta = min(cursize, size - cursize) + b += self.file_read(delta) + cursize += delta + return b + def load_frame(self, frame_size): if self.current_frame and self.current_frame.read() != b'': raise UnpicklingError( "beginning of a new frame before end of current frame") - self.current_frame = io.BytesIO(self.file_read(frame_size)) + data = self._chunked_file_read(frame_size) + if len(data) < frame_size: + raise EOFError + self.current_frame = io.BytesIO(data) # Tools used for pickling. @@ -904,17 +920,11 @@ def save_picklebuffer(self, obj): # Write data in-band # XXX The C implementation avoids a copy here buf = m.tobytes() - in_memo = id(buf) in self.memo if m.readonly: - if in_memo: - self._save_bytes_no_memo(buf) - else: - self.save_bytes(buf) + self._save_bytes_no_memo(buf) else: - if in_memo: - self._save_bytearray_no_memo(buf) - else: - self.save_bytearray(buf) + self._save_bytearray_no_memo(buf) + self.memoize(obj) else: # Write data out-of-band self.write(NEXT_BUFFER) @@ -1153,13 +1163,23 @@ def save_frozenset(self, obj): def save_global(self, obj, name=None): write = self.write - memo = self.memo if name is None: name = getattr(obj, '__qualname__', None) if name is None: name = obj.__name__ + if '.__' in name: + # Mangle names of private attributes. + dotted_path = name.split('.') + for i, subpath in enumerate(dotted_path): + if i and subpath.startswith('__') and not subpath.endswith('__'): + prev = prev.lstrip('_') + if prev: + dotted_path[i] = f"_{prev.lstrip('_')}{subpath}" + prev = subpath + name = '.'.join(dotted_path) + module_name = whichmodule(obj, name) if self.proto >= 2: code = _extension_registry.get((module_name, name), _NoValue) @@ -1497,12 +1517,17 @@ def load_binbytes8(self): dispatch[BINBYTES8[0]] = load_binbytes8 def load_bytearray8(self): - len, = unpack(' maxsize: + size, = unpack(' maxsize: raise UnpicklingError("BYTEARRAY8 exceeds system's maximum size " "of %d bytes" % maxsize) - b = bytearray(len) - self.readinto(b) + cursize = min(size, _MIN_READ_BUF_SIZE) + b = bytearray(cursize) + if self.readinto(b) == cursize: + while cursize < size and len(b) == cursize: + delta = min(cursize, size - cursize) + b += self.read(delta) + cursize += delta self.append(b) dispatch[BYTEARRAY8[0]] = load_bytearray8 @@ -1735,7 +1760,7 @@ def load_binget(self): i = self.read(1)[0] try: self.append(self.memo[i]) - except KeyError as exc: + except KeyError: msg = f'Memo value not found at index {i}' raise UnpicklingError(msg) from None dispatch[BINGET[0]] = load_binget @@ -1744,7 +1769,7 @@ def load_long_binget(self): i, = unpack(' 50: annocol = annotate - line += ' ' + opcode.doc.split('\n', 1)[0] + doc = opcode.doc.split('\n', 1)[0] + line += f" {t.annotation}{doc}{t.reset}" print(line, file=out) if errormsg: @@ -2541,7 +2583,11 @@ def dis(pickle, out=None, memo=None, indentlevel=4, annotate=0): stack.extend(after) - print("highest protocol among opcodes =", maxproto, file=out) + print( + "highest protocol among opcodes =", + f"{t.proto}{maxproto}{t.reset}", + file=out, + ) if stack: raise ValueError("stack not empty after STOP: %r" % stack) @@ -2839,12 +2885,9 @@ def __init__(self, value): } -if __name__ == "__main__": +def _main(args=None): import argparse - parser = argparse.ArgumentParser( - description='disassemble one or more pickle files', - color=True, - ) + parser = argparse.ArgumentParser(description='disassemble one or more pickle files') parser.add_argument( 'pickle_file', nargs='+', help='the pickle file') @@ -2856,7 +2899,7 @@ def __init__(self, value): help='preserve memo between disassemblies') parser.add_argument( '-l', '--indentlevel', default=4, type=int, - help='the number of blanks by which to indent a new MARK level') + help='the number of blanks by which to indent a new `MARK` level') parser.add_argument( '-a', '--annotate', action='store_true', help='annotate each line with a short opcode description') @@ -2864,7 +2907,7 @@ def __init__(self, value): '-p', '--preamble', default="==> {name} <==", help='if more than one pickle file is specified, print this before' ' each disassembly') - args = parser.parse_args() + args = parser.parse_args(args) annotate = 30 if args.annotate else 0 memo = {} if args.memo else None if args.output is None: @@ -2885,3 +2928,7 @@ def __init__(self, value): finally: if output is not sys.stdout: output.close() + + +if __name__ == "__main__": + _main() diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py index 8772a66791a3c9..11c2a4b0ef4635 100644 --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -9,6 +9,9 @@ import os.path import sys +lazy import re + + __all__ = [ 'get_importer', 'iter_importers', 'walk_packages', 'iter_modules', 'get_data', @@ -398,9 +401,10 @@ def get_data(package, resource): return loader.get_data(resource_name) -_NAME_PATTERN = None +_LENIENT_PATTERN = None +_STRICT_PATTERN = None -def resolve_name(name): +def resolve_name(name, *, strict=False): """ Resolve a name to an object. @@ -410,6 +414,7 @@ def resolve_name(name): W(.W)* W(.W)*:(W(.W)*)? + W(.W)*:(W(.W)*) The first form is intended for backward compatibility only. It assumes that some part of the dotted name is a package, and the rest is an object @@ -424,6 +429,11 @@ def resolve_name(name): hierarchy within that package. Only one import is needed in this form. If it ends with the colon, then a module object is returned. + The first two forms are accepted when `strict=False` (the default). + + The third form requires both the module name and callable, separated by + a colon. Only this form is accepted when `strict=True`. + The function will return an object (which might be a module), or raise one of the following exceptions: @@ -432,18 +442,26 @@ def resolve_name(name): AttributeError - if a failure occurred when traversing the object hierarchy within the imported package to get to the desired object. """ - global _NAME_PATTERN - if _NAME_PATTERN is None: - # Lazy import to speedup Python startup time - import re - dotted_words = r'(?!\d)(\w+)(\.(?!\d)(\w+))*' - _NAME_PATTERN = re.compile(f'^(?P{dotted_words})' - f'(?P:(?P{dotted_words})?)?$', - re.UNICODE) - - m = _NAME_PATTERN.match(name) - if not m: + global _LENIENT_PATTERN, _STRICT_PATTERN + dotted_words = r'(?!\d)(\w+)(\.(?!\d)(\w+))*' + if strict: + if _STRICT_PATTERN is None: + _STRICT_PATTERN = re.compile( + f'^(?P{dotted_words})' + f'(?P:(?P{dotted_words}))$', + re.UNICODE) + pattern = _STRICT_PATTERN + else: + if _LENIENT_PATTERN is None: + _LENIENT_PATTERN = re.compile( + f'^(?P{dotted_words})' + f'(?P:(?P{dotted_words})?)?$', + re.UNICODE) + pattern = _LENIENT_PATTERN + + if (m := pattern.match(name)) is None: raise ValueError(f'invalid format: {name!r}') + gd = m.groupdict() if gd.get('cln'): # there is a colon - a one-step import is all that's needed diff --git a/Lib/platform.py b/Lib/platform.py index da15bb4717bb96..36489d4fdd98ae 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -110,8 +110,6 @@ """ -__version__ = '1.1.0' - import collections import os import re @@ -129,7 +127,7 @@ # Based on the description of the PHP's version_compare(): # http://php.net/manual/en/function.version-compare.php -_ver_stages = { +_ver_stages = frozendict({ # any string not found in this dict, will get 0 assigned 'dev': 10, 'alpha': 20, 'a': 20, @@ -138,7 +136,7 @@ 'RC': 50, 'rc': 50, # number, will get 100 assigned 'pl': 200, 'p': 200, -} +}) def _comparable_version(version): @@ -173,6 +171,11 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384): """ if not executable: + if sys.platform == "emscripten": + # Emscripten's os.confstr reports that it is glibc, so special case + # it. + ver = ".".join(str(x) for x in sys._emscripten_info.emscripten_version) + return ("emscripten", ver) try: ver = os.confstr('CS_GNU_LIBC_VERSION') # parse 'glibc 2.28' as ('glibc', '2.28') @@ -194,6 +197,7 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384): | (GLIBC_([0-9.]+)) | (libc(_\w+)?\.so(?:\.(\d[0-9.]*))?) | (musl-([0-9.]+)) + | ((?:libc\.|ld-)musl(?:-\w+)?.so(?:\.(\d[0-9.]*))?) """, re.ASCII | re.VERBOSE) @@ -219,9 +223,10 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384): continue if not m: break - libcinit, glibc, glibcversion, so, threads, soversion, musl, muslversion = [ - s.decode('latin1') if s is not None else s - for s in m.groups()] + decoded_groups = [s.decode('latin1') if s is not None else s + for s in m.groups()] + (libcinit, glibc, glibcversion, so, threads, soversion, + musl, muslversion, musl_so, musl_sover) = decoded_groups if libcinit and not lib: lib = 'libc' elif glibc: @@ -231,7 +236,7 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384): elif V(glibcversion) > V(ver): ver = glibcversion elif so: - if lib != 'glibc': + if lib not in ('glibc', 'musl'): lib = 'libc' if soversion and (not ver or V(soversion) > V(ver)): ver = soversion @@ -241,6 +246,10 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384): lib = 'musl' if not ver or V(muslversion) > V(ver): ver = muslversion + elif musl_so: + lib = 'musl' + if musl_sover and (not ver or V(musl_sover) > V(ver)): + ver = musl_sover pos = m.end() return lib, version if ver is None else ver @@ -296,8 +305,7 @@ def _syscmd_ver(system='', release='', version='', text=True, encoding="locale", shell=True) - except (OSError, subprocess.CalledProcessError) as why: - #print('Command %s failed: %s' % (cmd, why)) + except (OSError, subprocess.CalledProcessError): continue else: break @@ -697,11 +705,11 @@ def _syscmd_file(target, default=''): # Default values for architecture; non-empty strings override the # defaults given as parameters -_default_architecture = { +_default_architecture = frozendict({ 'win32': ('', 'WindowsPE'), 'win16': ('', 'Windows'), 'dos': ('', 'MSDOS'), -} +}) def architecture(executable=sys.executable, bits='', linkage=''): @@ -1392,7 +1400,7 @@ def invalidate_caches(): def _parse_args(args: list[str] | None): import argparse - parser = argparse.ArgumentParser(color=True) + parser = argparse.ArgumentParser() parser.add_argument("args", nargs="*", choices=["nonaliased", "terse"]) parser.add_argument( "--terse", @@ -1425,5 +1433,14 @@ def _main(args: list[str] | None = None): print(platform(aliased, terse)) +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "1.1.0" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + if __name__ == "__main__": _main() diff --git a/Lib/plistlib.py b/Lib/plistlib.py index 67e832db217319..93f3ef5e38af84 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -2,7 +2,7 @@ The property list (.plist) file format is a simple XML pickle supporting basic object types, like dictionaries, lists, numbers and strings. -Usually the top level object is a dictionary. +Usually the top level object is a dictionary or a frozen dictionary. To write out a plist file, use the dump(value, file) function. 'value' is the top level object, 'file' is @@ -21,7 +21,7 @@ Generate Plist example: - import datetime + import datetime as dt import plistlib pl = dict( @@ -37,7 +37,7 @@ ), someData = b"", someMoreData = b"" * 10, - aDate = datetime.datetime.now() + aDate = dt.datetime.now() ) print(plistlib.dumps(pl).decode()) @@ -73,6 +73,9 @@ PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__) globals().update(PlistFormat.__members__) +# Data larger than this will be read in chunks, to prevent extreme +# overallocation. +_MIN_READ_BUF_SIZE = 1 << 20 class UID: def __init__(self, data): @@ -119,13 +122,7 @@ def __hash__(self): r"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]") def _encode_base64(s, maxlinelength=76): - # copied from base64.encodebytes(), with added maxlinelength argument - maxbinsize = (maxlinelength//4)*3 - pieces = [] - for i in range(0, len(s), maxbinsize): - chunk = s[i : i + maxbinsize] - pieces.append(binascii.b2a_base64(chunk)) - return b''.join(pieces) + return binascii.b2a_base64(s, wrapcol=maxlinelength, newline=False) def _decode_base64(s): if isinstance(s, str): @@ -360,7 +357,7 @@ def write_value(self, value): elif isinstance(value, float): self.simple_element("real", repr(value)) - elif isinstance(value, dict): + elif isinstance(value, (dict, frozendict)): self.write_dict(value) elif isinstance(value, (bytes, bytearray)): @@ -379,11 +376,10 @@ def write_value(self, value): def write_bytes(self, data): self.begin_element("data") self._indent_level -= 1 - maxlinelength = max( - 16, - 76 - len(self.indent.replace(b"\t", b" " * 8) * self._indent_level)) - - for line in _encode_base64(data, maxlinelength).split(b"\n"): + wrapcol = 76 - len((self.indent * self._indent_level).expandtabs()) + wrapcol = max(16, wrapcol) + encoded = binascii.b2a_base64(data, wrapcol=wrapcol, newline=False) + for line in encoded.split(b"\n"): if line: self.writeln(line) self._indent_level += 1 @@ -457,7 +453,7 @@ class InvalidFileException (ValueError): def __init__(self, message="Invalid file"): ValueError.__init__(self, message) -_BINARY_FORMAT = {1: 'B', 2: 'H', 4: 'L', 8: 'Q'} +_BINARY_FORMAT = frozendict({1: 'B', 2: 'H', 4: 'L', 8: 'Q'}) _undefined = object() @@ -508,12 +504,24 @@ def _get_size(self, tokenL): return tokenL + def _read(self, size): + cursize = min(size, _MIN_READ_BUF_SIZE) + data = self._fp.read(cursize) + while True: + if len(data) != cursize: + raise InvalidFileException + if cursize == size: + return data + delta = min(cursize, size - cursize) + data += self._fp.read(delta) + cursize += delta + def _read_ints(self, n, size): - data = self._fp.read(size * n) + data = self._read(size * n) if size in _BINARY_FORMAT: return struct.unpack(f'>{n}{_BINARY_FORMAT[size]}', data) else: - if not size or len(data) != size * n: + if not size: raise InvalidFileException() return tuple(int.from_bytes(data[i: i + size], 'big') for i in range(0, size * n, size)) @@ -573,22 +581,16 @@ def _read_object(self, ref): elif tokenH == 0x40: # data s = self._get_size(tokenL) - result = self._fp.read(s) - if len(result) != s: - raise InvalidFileException() + result = self._read(s) elif tokenH == 0x50: # ascii string s = self._get_size(tokenL) - data = self._fp.read(s) - if len(data) != s: - raise InvalidFileException() + data = self._read(s) result = data.decode('ascii') elif tokenH == 0x60: # unicode string s = self._get_size(tokenL) * 2 - data = self._fp.read(s) - if len(data) != s: - raise InvalidFileException() + data = self._read(s) result = data.decode('utf-16be') elif tokenH == 0x80: # UID @@ -713,7 +715,7 @@ def _flatten(self, value): self._objidtable[id(value)] = refnum # And finally recurse into containers - if isinstance(value, dict): + if isinstance(value, (dict, frozendict)): keys = [] values = [] items = value.items() @@ -834,7 +836,7 @@ def _write_object(self, value): self._write_size(0xA0, s) self._fp.write(struct.pack('>' + self._ref_format * s, *refs)) - elif isinstance(value, dict): + elif isinstance(value, (dict, frozendict)): keyRefs, valRefs = [], [] if self._sort_keys: @@ -867,18 +869,18 @@ def _is_fmt_binary(header): # Generic bits # -_FORMATS={ - FMT_XML: dict( +_FORMATS=frozendict({ + FMT_XML: frozendict( detect=_is_fmt_xml, parser=_PlistParser, writer=_PlistWriter, ), - FMT_BINARY: dict( + FMT_BINARY: frozendict( detect=_is_fmt_binary, parser=_BinaryPlistParser, writer=_BinaryPlistWriter, ) -} +}) def load(fp, *, fmt=None, dict_type=dict, aware_datetime=False): diff --git a/Lib/poplib.py b/Lib/poplib.py index 4469bff44b4c45..b97274c5c32ee6 100644 --- a/Lib/poplib.py +++ b/Lib/poplib.py @@ -122,6 +122,8 @@ def _putline(self, line): def _putcmd(self, line): if self._debugging: print('*cmd*', repr(line)) line = bytes(line, self.encoding) + if re.search(b'[\x00-\x1F\x7F]', line): + raise ValueError('Control characters not allowed in commands') self._putline(line) diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 5b5cde239e6275..8025b063397a03 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -36,7 +36,8 @@ "samefile","sameopenfile","samestat", "curdir","pardir","sep","pathsep","defpath","altsep","extsep", "devnull","realpath","supports_unicode_filenames","relpath", - "commonpath", "isjunction","isdevdrive","ALLOW_MISSING"] + "commonpath","isjunction","isdevdrive", + "ALL_BUT_LAST","ALLOW_MISSING"] def _get_sep(path): @@ -284,42 +285,41 @@ def expanduser(path): # This expands the forms $variable and ${variable} only. # Non-existent variables are left unchanged. -_varprog = None -_varprogb = None +_varpattern = r'\$(\w+|\{[^}]*\}?)' +_varsub = None +_varsubb = None def expandvars(path): """Expand shell variables of form $var and ${var}. Unknown variables are left unchanged.""" path = os.fspath(path) - global _varprog, _varprogb + global _varsub, _varsubb if isinstance(path, bytes): if b'$' not in path: return path - if not _varprogb: + if not _varsubb: import re - _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII) - search = _varprogb.search + _varsubb = re.compile(_varpattern.encode(), re.ASCII).sub + sub = _varsubb start = b'{' end = b'}' environ = getattr(os, 'environb', None) else: if '$' not in path: return path - if not _varprog: + if not _varsub: import re - _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII) - search = _varprog.search + _varsub = re.compile(_varpattern, re.ASCII).sub + sub = _varsub start = '{' end = '}' environ = os.environ - i = 0 - while True: - m = search(path, i) - if not m: - break - i, j = m.span(0) - name = m.group(1) - if name.startswith(start) and name.endswith(end): + + def repl(m): + name = m[1] + if name.startswith(start): + if not name.endswith(end): + return m[0] name = name[1:-1] try: if environ is None: @@ -327,13 +327,11 @@ def expandvars(path): else: value = environ[name] except KeyError: - i = j + return m[0] else: - tail = path[j:] - path = path[:i] + value - i = len(path) - path += tail - return path + return value + + return sub(repl, path) # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. @@ -404,7 +402,8 @@ def realpath(filename, /, *, strict=False): getcwd = os.getcwd if strict is ALLOW_MISSING: ignored_error = FileNotFoundError - strict = True + elif strict is ALL_BUT_LAST: + ignored_error = FileNotFoundError elif strict: ignored_error = () else: @@ -418,7 +417,7 @@ def realpath(filename, /, *, strict=False): # indicates that a symlink target has been resolved, and that the original # symlink path can be retrieved by popping again. The [::-1] slice is a # very fast way of spelling list(reversed(...)). - rest = filename.split(sep)[::-1] + rest = filename.rstrip(sep).split(sep)[::-1] # Number of unprocessed parts in 'rest'. This can differ from len(rest) # later, because 'rest' might contain markers for unresolved symlinks. @@ -427,6 +426,7 @@ def realpath(filename, /, *, strict=False): # The resolved path, which is absolute throughout this function. # Note: getcwd() returns a normalized and symlink-free path. path = sep if filename.startswith(sep) else getcwd() + trailing_sep = filename.endswith(sep) # Mapping from symlink paths to *fully resolved* symlink targets. If a # symlink is encountered but not yet resolved, the value is None. This is @@ -459,7 +459,8 @@ def realpath(filename, /, *, strict=False): try: st_mode = lstat(newpath).st_mode if not stat.S_ISLNK(st_mode): - if strict and part_count and not stat.S_ISDIR(st_mode): + if (strict and (part_count or trailing_sep) + and not stat.S_ISDIR(st_mode)): raise OSError(errno.ENOTDIR, os.strerror(errno.ENOTDIR), newpath) path = newpath @@ -486,7 +487,8 @@ def realpath(filename, /, *, strict=False): continue target = readlink(newpath) except ignored_error: - pass + if strict is ALL_BUT_LAST and part_count: + raise else: # Resolve the symbolic link if target.startswith(sep): @@ -540,7 +542,7 @@ def relpath(path, start=None): start_list = start_tail.split(sep) if start_tail else [] path_list = path_tail.split(sep) if path_tail else [] # Work out how much of the filepath is shared by start and path. - i = len(commonprefix([start_list, path_list])) + i = len(genericpath._commonprefix([start_list, path_list])) rel_list = [pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: diff --git a/Lib/pprint.py b/Lib/pprint.py index 92a2c543ac279c..7355021998081d 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -44,20 +44,22 @@ def pprint(object, stream=None, indent=1, width=80, depth=None, *, - compact=False, sort_dicts=True, underscore_numbers=False): + compact=False, expand=False, sort_dicts=True, + underscore_numbers=False): """Pretty-print a Python object to a stream [default is sys.stdout].""" printer = PrettyPrinter( stream=stream, indent=indent, width=width, depth=depth, - compact=compact, sort_dicts=sort_dicts, + compact=compact, expand=expand, sort_dicts=sort_dicts, underscore_numbers=underscore_numbers) printer.pprint(object) def pformat(object, indent=1, width=80, depth=None, *, - compact=False, sort_dicts=True, underscore_numbers=False): + compact=False, expand=False, sort_dicts=True, + underscore_numbers=False): """Format a Python object into a pretty-printed representation.""" return PrettyPrinter(indent=indent, width=width, depth=depth, - compact=compact, sort_dicts=sort_dicts, + compact=compact, expand=expand, sort_dicts=sort_dicts, underscore_numbers=underscore_numbers).pformat(object) @@ -111,7 +113,8 @@ def _safe_tuple(t): class PrettyPrinter: def __init__(self, indent=1, width=80, depth=None, stream=None, *, - compact=False, sort_dicts=True, underscore_numbers=False): + compact=False, expand=False, sort_dicts=True, + underscore_numbers=False): """Handle pretty printing operations onto a stream using a set of configured parameters. @@ -130,6 +133,12 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *, compact If true, several items will be combined in one line. + Incompatible with expand mode. + + expand + If true, the output will be formatted similar to + pretty-printed json.dumps() when ``indent`` is supplied. + Incompatible with compact mode. sort_dicts If true, dict keys are sorted. @@ -146,6 +155,8 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *, raise ValueError('depth must be > 0') if not width: raise ValueError('width must be != 0') + if compact and expand: + raise ValueError('compact and expand are incompatible') self._depth = depth self._indent_per_level = indent self._width = width @@ -154,6 +165,7 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *, else: self._stream = _sys.stdout self._compact = bool(compact) + self._expand = bool(expand) self._sort_dicts = sort_dicts self._underscore_numbers = underscore_numbers @@ -205,24 +217,48 @@ def _format(self, object, stream, indent, allowance, context, level): return stream.write(rep) + def _format_block_start(self, start_str, indent): + if self._expand: + return f"{start_str}\n{' ' * indent}" + return start_str + + def _format_block_end(self, end_str, indent): + if self._expand: + return f"\n{' ' * indent}{end_str}" + return end_str + + def _child_indent(self, indent, prefix_len): + if self._expand: + return indent + return indent + prefix_len + + def _write_indent_padding(self, write): + if self._expand: + if self._indent_per_level > 0: + write(self._indent_per_level * " ") + elif self._indent_per_level > 1: + write((self._indent_per_level - 1) * " ") + def _pprint_dataclass(self, object, stream, indent, allowance, context, level): # Lazy import to improve module import time from dataclasses import fields as dataclass_fields cls_name = object.__class__.__name__ - indent += len(cls_name) + 1 + if self._expand: + indent += self._indent_per_level + else: + indent += len(cls_name) + 1 items = [(f.name, getattr(object, f.name)) for f in dataclass_fields(object) if f.repr] - stream.write(cls_name + '(') + stream.write(self._format_block_start(cls_name + '(', indent)) self._format_namespace_items(items, stream, indent, allowance, context, level) - stream.write(')') + stream.write(self._format_block_end(')', indent - self._indent_per_level)) _dispatch = {} def _pprint_dict(self, object, stream, indent, allowance, context, level): write = stream.write - write('{') - if self._indent_per_level > 1: - write((self._indent_per_level - 1) * ' ') + write(self._format_block_start('{', indent)) + self._write_indent_padding(write) length = len(object) if length: if self._sort_dicts: @@ -231,19 +267,50 @@ def _pprint_dict(self, object, stream, indent, allowance, context, level): items = object.items() self._format_dict_items(items, stream, indent, allowance + 1, context, level) - write('}') + write(self._format_block_end('}', indent)) _dispatch[dict.__repr__] = _pprint_dict + def _pprint_frozendict(self, object, stream, indent, allowance, context, level): + write = stream.write + cls = object.__class__ + if not len(object): + write(repr(object)) + return + + write(self._format_block_start(cls.__name__ + "({", indent)) + self._write_indent_padding(write) + + if self._sort_dicts: + items = sorted(object.items(), key=_safe_tuple) + else: + items = object.items() + self._format_dict_items( + items, + stream, + self._child_indent(indent, len(cls.__name__) + 1), + allowance + 2, + context, + level, + ) + write(self._format_block_end("})", indent)) + + _dispatch[frozendict.__repr__] = _pprint_frozendict + def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level): if not len(object): stream.write(repr(object)) return cls = object.__class__ stream.write(cls.__name__ + '(') - self._format(list(object.items()), stream, - indent + len(cls.__name__) + 1, allowance + 1, - context, level) + self._format( + list(object.items()), + stream, + self._child_indent(indent, len(cls.__name__) + 1), + allowance + 1, + context, + level, + ) stream.write(')') _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict @@ -254,19 +321,21 @@ def _pprint_dict_view(self, object, stream, indent, allowance, context, level): key = _safe_tuple else: key = _safe_key + write = stream.write - write(object.__class__.__name__ + '([') - if self._indent_per_level > 1: - write((self._indent_per_level - 1) * ' ') - length = len(object) - if length: + write( + self._format_block_start(object.__class__.__name__ + "([", indent) + ) + + if len(object): if self._sort_dicts: entries = sorted(object, key=key) else: entries = object - self._format_items(entries, stream, indent, allowance + 1, - context, level) - write('])') + self._format_items( + entries, stream, indent, allowance + 2, context, level + ) + write(self._format_block_end("])", indent)) def _pprint_mapping_abc_view(self, object, stream, indent, allowance, context, level): """Pretty print mapping views from collections.abc.""" @@ -292,19 +361,22 @@ def _pprint_mapping_abc_view(self, object, stream, indent, allowance, context, l _collections.abc.MappingView)} def _pprint_list(self, object, stream, indent, allowance, context, level): - stream.write('[') + stream.write(self._format_block_start('[', indent)) self._format_items(object, stream, indent, allowance + 1, context, level) - stream.write(']') + stream.write(self._format_block_end(']', indent)) _dispatch[list.__repr__] = _pprint_list def _pprint_tuple(self, object, stream, indent, allowance, context, level): - stream.write('(') - endchar = ',)' if len(object) == 1 else ')' + stream.write(self._format_block_start('(', indent)) + if len(object) == 1 and not self._expand: + endchar = ',)' + else: + endchar = ')' self._format_items(object, stream, indent, allowance + len(endchar), context, level) - stream.write(endchar) + stream.write(self._format_block_end(endchar, indent)) _dispatch[tuple.__repr__] = _pprint_tuple @@ -314,16 +386,17 @@ def _pprint_set(self, object, stream, indent, allowance, context, level): return typ = object.__class__ if typ is set: - stream.write('{') + stream.write(self._format_block_start('{', indent)) endchar = '}' else: - stream.write(typ.__name__ + '({') + stream.write(self._format_block_start(typ.__name__ + '({', indent)) endchar = '})' - indent += len(typ.__name__) + 1 + if not self._expand: + indent += len(typ.__name__) + 1 object = sorted(object, key=_safe_key) self._format_items(object, stream, indent, allowance + len(endchar), context, level) - stream.write(endchar) + stream.write(self._format_block_end(endchar, indent)) _dispatch[set.__repr__] = _pprint_set _dispatch[frozenset.__repr__] = _pprint_set @@ -336,7 +409,10 @@ def _pprint_str(self, object, stream, indent, allowance, context, level): chunks = [] lines = object.splitlines(True) if level == 1: - indent += 1 + if self._expand: + indent += self._indent_per_level + else: + indent += 1 allowance += 1 max_width1 = max_width = self._width - indent for i, line in enumerate(lines): @@ -372,13 +448,13 @@ def _pprint_str(self, object, stream, indent, allowance, context, level): write(rep) return if level == 1: - write('(') + write(self._format_block_start("(", indent)) for i, rep in enumerate(chunks): if i > 0: write('\n' + ' '*indent) write(rep) if level == 1: - write(')') + write(self._format_block_end(")", indent - self._indent_per_level)) _dispatch[str.__repr__] = _pprint_str @@ -389,9 +465,12 @@ def _pprint_bytes(self, object, stream, indent, allowance, context, level): return parens = level == 1 if parens: - indent += 1 + if self._expand: + indent += self._indent_per_level + else: + indent += 1 allowance += 1 - write('(') + write(self._format_block_start('(', indent)) delim = '' for rep in _wrap_bytes_repr(object, self._width - indent, allowance): write(delim) @@ -399,23 +478,34 @@ def _pprint_bytes(self, object, stream, indent, allowance, context, level): if not delim: delim = '\n' + ' '*indent if parens: - write(')') + write(self._format_block_end(')', indent - self._indent_per_level)) _dispatch[bytes.__repr__] = _pprint_bytes def _pprint_bytearray(self, object, stream, indent, allowance, context, level): write = stream.write - write('bytearray(') - self._pprint_bytes(bytes(object), stream, indent + 10, + write(self._format_block_start('bytearray(', indent)) + if self._expand: + write(' ' * self._indent_per_level) + recursive_indent = indent + self._indent_per_level + else: + recursive_indent = indent + 10 + self._pprint_bytes(bytes(object), stream, recursive_indent, allowance + 1, context, level + 1) - write(')') + write(self._format_block_end(')', indent)) _dispatch[bytearray.__repr__] = _pprint_bytearray def _pprint_mappingproxy(self, object, stream, indent, allowance, context, level): stream.write('mappingproxy(') - self._format(object.copy(), stream, indent + 13, allowance + 1, - context, level) + self._format( + object.copy(), + stream, + self._child_indent(indent, 13), + allowance + 1, + context, + level, + ) stream.write(')') _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy @@ -427,11 +517,15 @@ def _pprint_simplenamespace(self, object, stream, indent, allowance, context, le cls_name = 'namespace' else: cls_name = object.__class__.__name__ - indent += len(cls_name) + 1 + if self._expand: + indent += self._indent_per_level + else: + indent += len(cls_name) + 1 items = object.__dict__.items() - stream.write(cls_name + '(') - self._format_namespace_items(items, stream, indent, allowance, context, level) - stream.write(')') + stream.write(self._format_block_start(cls_name + '(', indent)) + self._format_namespace_items(items, stream, indent, allowance, context, + level) + stream.write(self._format_block_end(')', indent - self._indent_per_level)) _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace @@ -446,11 +540,18 @@ def _format_dict_items(self, items, stream, indent, allowance, context, rep = self._repr(key, context, level) write(rep) write(': ') - self._format(ent, stream, indent + len(rep) + 2, - allowance if last else 1, - context, level) + self._format( + ent, + stream, + self._child_indent(indent, len(rep) + 2), + allowance if last else 1, + context, + level, + ) if not last: write(delimnl) + elif self._expand: + write(',') def _format_namespace_items(self, items, stream, indent, allowance, context, level): write = stream.write @@ -465,17 +566,23 @@ def _format_namespace_items(self, items, stream, indent, allowance, context, lev # recursive dataclass repr. write("...") else: - self._format(ent, stream, indent + len(key) + 1, - allowance if last else 1, - context, level) + self._format( + ent, + stream, + self._child_indent(indent, len(key) + 1), + allowance if last else 1, + context, + level, + ) if not last: write(delimnl) + elif self._expand: + write(',') def _format_items(self, items, stream, indent, allowance, context, level): write = stream.write indent += self._indent_per_level - if self._indent_per_level > 1: - write((self._indent_per_level - 1) * ' ') + self._write_indent_padding(write) delimnl = ',\n' + ' ' * indent delim = '' width = max_width = self._width - indent + 1 @@ -511,6 +618,8 @@ def _format_items(self, items, stream, indent, allowance, context, level): self._format(ent, stream, indent, allowance if last else 1, context, level) + if last and self._expand: + write(',') def _repr(self, object, context, level): repr, readable, recursive = self.format(object, context.copy(), @@ -534,9 +643,13 @@ def _pprint_default_dict(self, object, stream, indent, allowance, context, level return rdf = self._repr(object.default_factory, context, level) cls = object.__class__ - indent += len(cls.__name__) + 1 - stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent)) - self._pprint_dict(object, stream, indent, allowance + 1, context, level) + if self._expand: + stream.write('%s(%s, ' % (cls.__name__, rdf)) + else: + indent += len(cls.__name__) + 1 + stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent)) + self._pprint_dict(object, stream, indent, allowance + 1, context, + level) stream.write(')') _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict @@ -546,14 +659,18 @@ def _pprint_counter(self, object, stream, indent, allowance, context, level): stream.write(repr(object)) return cls = object.__class__ - stream.write(cls.__name__ + '({') - if self._indent_per_level > 1: - stream.write((self._indent_per_level - 1) * ' ') + stream.write(self._format_block_start(cls.__name__ + '({', indent)) + self._write_indent_padding(stream.write) items = object.most_common() - self._format_dict_items(items, stream, - indent + len(cls.__name__) + 1, allowance + 2, - context, level) - stream.write('})') + self._format_dict_items( + items, + stream, + self._child_indent(indent, len(cls.__name__) + 1), + allowance + 2, + context, + level, + ) + stream.write(self._format_block_end('})', indent)) _dispatch[_collections.Counter.__repr__] = _pprint_counter @@ -562,12 +679,18 @@ def _pprint_chain_map(self, object, stream, indent, allowance, context, level): stream.write(repr(object)) return cls = object.__class__ - stream.write(cls.__name__ + '(') - indent += len(cls.__name__) + 1 + stream.write(self._format_block_start(cls.__name__ + '(', + indent + self._indent_per_level)) + if self._expand: + indent += self._indent_per_level + else: + indent += len(cls.__name__) + 1 for i, m in enumerate(object.maps): if i == len(object.maps) - 1: self._format(m, stream, indent, allowance + 1, context, level) - stream.write(')') + if self._expand: + stream.write(',') + stream.write(self._format_block_end(')', indent - self._indent_per_level)) else: self._format(m, stream, indent, 1, context, level) stream.write(',\n' + ' ' * indent) @@ -579,18 +702,21 @@ def _pprint_deque(self, object, stream, indent, allowance, context, level): stream.write(repr(object)) return cls = object.__class__ - stream.write(cls.__name__ + '(') - indent += len(cls.__name__) + 1 - stream.write('[') + stream.write(self._format_block_start(cls.__name__ + '([', indent)) + if not self._expand: + indent += len(cls.__name__) + 1 if object.maxlen is None: self._format_items(object, stream, indent, allowance + 2, context, level) - stream.write('])') + stream.write(self._format_block_end('])', indent)) else: self._format_items(object, stream, indent, 2, context, level) rml = self._repr(object.maxlen, context, level) - stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml)) + if self._expand: + stream.write('%s], maxlen=%s)' % ('\n' + ' ' * indent, rml)) + else: + stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml)) _dispatch[_collections.deque.__repr__] = _pprint_deque @@ -609,6 +735,61 @@ def _pprint_user_string(self, object, stream, indent, allowance, context, level) _dispatch[_collections.UserString.__repr__] = _pprint_user_string + def _pprint_template(self, object, stream, indent, allowance, context, level): + cls_name = object.__class__.__name__ + if self._expand: + indent += self._indent_per_level + else: + indent += len(cls_name) + 1 + + items = ( + ("strings", object.strings), + ("interpolations", object.interpolations), + ) + stream.write(self._format_block_start(cls_name + "(", indent)) + self._format_namespace_items( + items, stream, indent, allowance, context, level + ) + stream.write( + self._format_block_end(")", indent - self._indent_per_level) + ) + + def _pprint_interpolation(self, object, stream, indent, allowance, context, level): + cls_name = object.__class__.__name__ + if self._expand: + indent += self._indent_per_level + items = ( + ("value", object.value), + ("expression", object.expression), + ("conversion", object.conversion), + ("format_spec", object.format_spec), + ) + stream.write(self._format_block_start(cls_name + "(", indent)) + self._format_namespace_items( + items, stream, indent, allowance, context, level + ) + stream.write( + self._format_block_end(")", indent - self._indent_per_level) + ) + else: + indent += len(cls_name) + items = ( + object.value, + object.expression, + object.conversion, + object.format_spec, + ) + stream.write(cls_name + "(") + self._format_items( + items, stream, indent, allowance, context, level + ) + stream.write(")") + + t = t"{0}" + _dispatch[type(t).__repr__] = _pprint_template + _dispatch[type(t.interpolations[0]).__repr__] = _pprint_interpolation + del t + def _safe_repr(self, object, context, maxlevels, level): # Return triple (repr_string, isreadable, isrecursive). typ = type(object) @@ -623,12 +804,21 @@ def _safe_repr(self, object, context, maxlevels, level): else: return repr(object), True, False - if issubclass(typ, dict) and r is dict.__repr__: + if ((issubclass(typ, dict) and r is dict.__repr__) + or (issubclass(typ, frozendict) and r is frozendict.__repr__)): + is_frozendict = issubclass(typ, frozendict) if not object: - return "{}", True, False + if is_frozendict: + rep = f"{object.__class__.__name__}()" + else: + rep = "{}" + return rep, True, False objid = id(object) if maxlevels and level >= maxlevels: - return "{...}", False, objid in context + rep = "{...}" + if is_frozendict: + rep = f"{object.__class__.__name__}({rep})" + return rep, False, objid in context if objid in context: return _recursion(object), False, True context[objid] = 1 @@ -651,7 +841,10 @@ def _safe_repr(self, object, context, maxlevels, level): if krecur or vrecur: recursive = True del context[objid] - return "{%s}" % ", ".join(components), readable, recursive + rep = "{%s}" % ", ".join(components) + if is_frozendict: + rep = f"{object.__class__.__name__}({rep})" + return rep, readable, recursive if (issubclass(typ, list) and r is list.__repr__) or \ (issubclass(typ, tuple) and r is tuple.__repr__): diff --git a/Lib/profile/profile.py b/Lib/profile.py similarity index 89% rename from Lib/profile/profile.py rename to Lib/profile.py index 1d9e2fd41f85b4..304284da421163 100644 --- a/Lib/profile/profile.py +++ b/Lib/profile.py @@ -28,9 +28,18 @@ import sys import time import marshal +import warnings __all__ = ["run", "runctx", "Profile"] +# Emit deprecation warning as per PEP 799 +warnings.warn( + "The profile module is deprecated and will be removed in Python 3.17. " + "Use profiling.tracing (or cProfile) for tracing profilers instead.", + DeprecationWarning, + stacklevel=2 +) + # Sample timer for use with #i_count = 0 #def integer_timer(): @@ -550,3 +559,65 @@ def f(m, f1=f1): return mean #**************************************************************************** + +def main(): + import os + from optparse import OptionParser + + usage = "profile.py [-o output_file_path] [-s sort] [-m module | scriptfile] [arg] ..." + parser = OptionParser(usage=usage) + parser.allow_interspersed_args = False + parser.add_option('-o', '--outfile', dest="outfile", + help="Save stats to ", default=None) + parser.add_option('-m', dest="module", action="store_true", + help="Profile a library module.", default=False) + parser.add_option('-s', '--sort', dest="sort", + help="Sort order when printing to stdout, based on pstats.Stats class", + default=-1) + + if not sys.argv[1:]: + parser.print_usage() + sys.exit(2) + + (options, args) = parser.parse_args() + sys.argv[:] = args + + # The script that we're profiling may chdir, so capture the absolute path + # to the output file at startup. + if options.outfile is not None: + options.outfile = os.path.abspath(options.outfile) + + if len(args) > 0: + if options.module: + import runpy + code = "run_module(modname, run_name='__main__')" + globs = { + 'run_module': runpy.run_module, + 'modname': args[0] + } + else: + progname = args[0] + sys.path.insert(0, os.path.dirname(progname)) + with io.open_code(progname) as fp: + code = compile(fp.read(), progname, 'exec') + spec = importlib.machinery.ModuleSpec(name='__main__', loader=None, + origin=progname) + globs = { + '__spec__': spec, + '__file__': spec.origin, + '__name__': spec.name, + '__package__': None, + } + try: + runctx(code, globs, None, options.outfile, options.sort) + except BrokenPipeError as exc: + # Prevent "Exception ignored" during interpreter shutdown. + sys.stdout = None + sys.exit(exc.errno) + else: + parser.print_usage() + return parser + +# When invoked as main program, invoke the profiler on a script +if __name__ == '__main__': + main() diff --git a/Lib/profile/__init__.py b/Lib/profile/__init__.py deleted file mode 100644 index 21c886448ac2af..00000000000000 --- a/Lib/profile/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .profile import run -from .profile import runctx -from .profile import Profile -from .profile import _Utils - -__all__ = ['run', 'runctx', 'Profile'] diff --git a/Lib/profile/__main__.py b/Lib/profile/__main__.py deleted file mode 100644 index d900e547330e76..00000000000000 --- a/Lib/profile/__main__.py +++ /dev/null @@ -1,69 +0,0 @@ -import io -import importlib.machinery -import os -import sys -from optparse import OptionParser - -from .profile import runctx - - -def main(): - - usage = "profile.py [-o output_file_path] [-s sort] [-m module | scriptfile] [arg] ..." - parser = OptionParser(usage=usage) - parser.allow_interspersed_args = False - parser.add_option('-o', '--outfile', dest="outfile", - help="Save stats to ", default=None) - parser.add_option('-m', dest="module", action="store_true", - help="Profile a library module.", default=False) - parser.add_option('-s', '--sort', dest="sort", - help="Sort order when printing to stdout, based on pstats.Stats class", - default=-1) - - if not sys.argv[1:]: - parser.print_usage() - sys.exit(2) - - (options, args) = parser.parse_args() - sys.argv[:] = args - - # The script that we're profiling may chdir, so capture the absolute path - # to the output file at startup. - if options.outfile is not None: - options.outfile = os.path.abspath(options.outfile) - - if len(args) > 0: - if options.module: - import runpy - code = "run_module(modname, run_name='__main__')" - globs = { - 'run_module': runpy.run_module, - 'modname': args[0] - } - else: - progname = args[0] - sys.path.insert(0, os.path.dirname(progname)) - with io.open_code(progname) as fp: - code = compile(fp.read(), progname, 'exec') - spec = importlib.machinery.ModuleSpec(name='__main__', loader=None, - origin=progname) - globs = { - '__spec__': spec, - '__file__': spec.origin, - '__name__': spec.name, - '__package__': None, - '__cached__': None, - } - try: - runctx(code, globs, None, options.outfile, options.sort) - except BrokenPipeError as exc: - # Prevent "Exception ignored" during interpreter shutdown. - sys.stdout = None - sys.exit(exc.errno) - else: - parser.print_usage() - return parser - -# When invoked as main program, invoke the profiler on a script -if __name__ == '__main__': - main() diff --git a/Lib/profile/collector.py b/Lib/profile/collector.py deleted file mode 100644 index 28286120aefc67..00000000000000 --- a/Lib/profile/collector.py +++ /dev/null @@ -1,11 +0,0 @@ -from abc import ABC, abstractmethod - - -class Collector(ABC): - @abstractmethod - def collect(self, stack_frames): - """Collect profiling data from stack frames.""" - - @abstractmethod - def export(self, filename): - """Export collected data to a file.""" diff --git a/Lib/profile/pstats_collector.py b/Lib/profile/pstats_collector.py deleted file mode 100644 index f58da1159c21a7..00000000000000 --- a/Lib/profile/pstats_collector.py +++ /dev/null @@ -1,81 +0,0 @@ -import collections -import marshal - -from .collector import Collector - - -class PstatsCollector(Collector): - def __init__(self, sample_interval_usec): - self.result = collections.defaultdict( - lambda: dict(total_rec_calls=0, direct_calls=0, cumulative_calls=0) - ) - self.stats = {} - self.sample_interval_usec = sample_interval_usec - self.callers = collections.defaultdict( - lambda: collections.defaultdict(int) - ) - - def collect(self, stack_frames): - for thread_id, frames in stack_frames: - if not frames: - continue - - # Process each frame in the stack to track cumulative calls - for frame in frames: - location = (frame.filename, frame.lineno, frame.funcname) - self.result[location]["cumulative_calls"] += 1 - - # The top frame gets counted as an inline call (directly executing) - top_frame = frames[0] - top_location = ( - top_frame.filename, - top_frame.lineno, - top_frame.funcname, - ) - - self.result[top_location]["direct_calls"] += 1 - - # Track caller-callee relationships for call graph - for i in range(1, len(frames)): - callee_frame = frames[i - 1] - caller_frame = frames[i] - - callee = ( - callee_frame.filename, - callee_frame.lineno, - callee_frame.funcname, - ) - caller = ( - caller_frame.filename, - caller_frame.lineno, - caller_frame.funcname, - ) - - self.callers[callee][caller] += 1 - - def export(self, filename): - self.create_stats() - self._dump_stats(filename) - - def _dump_stats(self, file): - stats_with_marker = dict(self.stats) - stats_with_marker[("__sampled__",)] = True - with open(file, "wb") as f: - marshal.dump(stats_with_marker, f) - - # Needed for compatibility with pstats.Stats - def create_stats(self): - sample_interval_sec = self.sample_interval_usec / 1_000_000 - callers = {} - for fname, call_counts in self.result.items(): - total = call_counts["direct_calls"] * sample_interval_sec - cumulative_calls = call_counts["cumulative_calls"] - cumulative = cumulative_calls * sample_interval_sec - callers = dict(self.callers.get(fname, {})) - self.stats[fname] = ( - call_counts["direct_calls"], # cc = direct calls for sample percentage - cumulative_calls, # nc = cumulative calls for cumulative percentage - total, - cumulative, - callers, - ) diff --git a/Lib/profile/sample.py b/Lib/profile/sample.py deleted file mode 100644 index 97d23611e67ad7..00000000000000 --- a/Lib/profile/sample.py +++ /dev/null @@ -1,730 +0,0 @@ -import argparse -import _remote_debugging -import os -import pstats -import statistics -import sys -import sysconfig -import time -from collections import deque -from _colorize import ANSIColors - -from .pstats_collector import PstatsCollector -from .stack_collector import CollapsedStackCollector - -FREE_THREADED_BUILD = sysconfig.get_config_var("Py_GIL_DISABLED") is not None - -class SampleProfiler: - def __init__(self, pid, sample_interval_usec, all_threads): - self.pid = pid - self.sample_interval_usec = sample_interval_usec - self.all_threads = all_threads - if FREE_THREADED_BUILD: - self.unwinder = _remote_debugging.RemoteUnwinder( - self.pid, all_threads=self.all_threads - ) - else: - only_active_threads = bool(self.all_threads) - self.unwinder = _remote_debugging.RemoteUnwinder( - self.pid, only_active_thread=only_active_threads - ) - # Track sample intervals and total sample count - self.sample_intervals = deque(maxlen=100) - self.total_samples = 0 - self.realtime_stats = False - - def sample(self, collector, duration_sec=10): - sample_interval_sec = self.sample_interval_usec / 1_000_000 - running_time = 0 - num_samples = 0 - errors = 0 - start_time = next_time = time.perf_counter() - last_sample_time = start_time - realtime_update_interval = 1.0 # Update every second - last_realtime_update = start_time - - while running_time < duration_sec: - current_time = time.perf_counter() - if next_time < current_time: - try: - stack_frames = self.unwinder.get_stack_trace() - collector.collect(stack_frames) - except ProcessLookupError: - break - except (RuntimeError, UnicodeDecodeError, MemoryError, OSError): - errors += 1 - except Exception as e: - if not self._is_process_running(): - break - raise e from None - - # Track actual sampling intervals for real-time stats - if num_samples > 0: - actual_interval = current_time - last_sample_time - self.sample_intervals.append( - 1.0 / actual_interval - ) # Convert to Hz - self.total_samples += 1 - - # Print real-time statistics if enabled - if ( - self.realtime_stats - and (current_time - last_realtime_update) - >= realtime_update_interval - ): - self._print_realtime_stats() - last_realtime_update = current_time - - last_sample_time = current_time - num_samples += 1 - next_time += sample_interval_sec - - running_time = time.perf_counter() - start_time - - # Clear real-time stats line if it was being displayed - if self.realtime_stats and len(self.sample_intervals) > 0: - print() # Add newline after real-time stats - - print(f"Captured {num_samples} samples in {running_time:.2f} seconds") - print(f"Sample rate: {num_samples / running_time:.2f} samples/sec") - print(f"Error rate: {(errors / num_samples) * 100:.2f}%") - - expected_samples = int(duration_sec / sample_interval_sec) - if num_samples < expected_samples: - print( - f"Warning: missed {expected_samples - num_samples} samples " - f"from the expected total of {expected_samples} " - f"({(expected_samples - num_samples) / expected_samples * 100:.2f}%)" - ) - - def _is_process_running(self): - if sys.platform == "linux" or sys.platform == "darwin": - try: - os.kill(self.pid, 0) - return True - except ProcessLookupError: - return False - elif sys.platform == "win32": - try: - _remote_debugging.RemoteUnwinder(self.pid) - except Exception: - return False - return True - else: - raise ValueError(f"Unsupported platform: {sys.platform}") - - def _print_realtime_stats(self): - """Print real-time sampling statistics.""" - if len(self.sample_intervals) < 2: - return - - # Calculate statistics on the Hz values (deque automatically maintains rolling window) - hz_values = list(self.sample_intervals) - mean_hz = statistics.mean(hz_values) - min_hz = min(hz_values) - max_hz = max(hz_values) - - # Calculate microseconds per sample for all metrics (1/Hz * 1,000,000) - mean_us_per_sample = (1.0 / mean_hz) * 1_000_000 if mean_hz > 0 else 0 - min_us_per_sample = ( - (1.0 / max_hz) * 1_000_000 if max_hz > 0 else 0 - ) # Min time = Max Hz - max_us_per_sample = ( - (1.0 / min_hz) * 1_000_000 if min_hz > 0 else 0 - ) # Max time = Min Hz - - # Clear line and print stats - print( - f"\r\033[K{ANSIColors.BOLD_BLUE}Real-time sampling stats:{ANSIColors.RESET} " - f"{ANSIColors.YELLOW}Mean: {mean_hz:.1f}Hz ({mean_us_per_sample:.2f}µs){ANSIColors.RESET} " - f"{ANSIColors.GREEN}Min: {min_hz:.1f}Hz ({max_us_per_sample:.2f}µs){ANSIColors.RESET} " - f"{ANSIColors.RED}Max: {max_hz:.1f}Hz ({min_us_per_sample:.2f}µs){ANSIColors.RESET} " - f"{ANSIColors.CYAN}Samples: {self.total_samples}{ANSIColors.RESET}", - end="", - flush=True, - ) - - -def _determine_best_unit(max_value): - """Determine the best unit (s, ms, μs) and scale factor for a maximum value.""" - if max_value >= 1.0: - return "s", 1.0 - elif max_value >= 0.001: - return "ms", 1000.0 - else: - return "μs", 1000000.0 - - -def print_sampled_stats( - stats, sort=-1, limit=None, show_summary=True, sample_interval_usec=100 -): - # Get the stats data - stats_list = [] - for func, ( - direct_calls, - cumulative_calls, - total_time, - cumulative_time, - callers, - ) in stats.stats.items(): - stats_list.append( - ( - func, - direct_calls, - cumulative_calls, - total_time, - cumulative_time, - callers, - ) - ) - - # Calculate total samples for percentage calculations (using direct_calls) - total_samples = sum( - direct_calls for _, direct_calls, _, _, _, _ in stats_list - ) - - # Sort based on the requested field - sort_field = sort - if sort_field == -1: # stdname - stats_list.sort(key=lambda x: str(x[0])) - elif sort_field == 0: # nsamples (direct samples) - stats_list.sort(key=lambda x: x[1], reverse=True) # direct_calls - elif sort_field == 1: # tottime - stats_list.sort(key=lambda x: x[3], reverse=True) # total_time - elif sort_field == 2: # cumtime - stats_list.sort(key=lambda x: x[4], reverse=True) # cumulative_time - elif sort_field == 3: # sample% - stats_list.sort( - key=lambda x: (x[1] / total_samples * 100) - if total_samples > 0 - else 0, - reverse=True, # direct_calls percentage - ) - elif sort_field == 4: # cumul% - stats_list.sort( - key=lambda x: (x[2] / total_samples * 100) - if total_samples > 0 - else 0, - reverse=True, # cumulative_calls percentage - ) - elif sort_field == 5: # nsamples (cumulative samples) - stats_list.sort(key=lambda x: x[2], reverse=True) # cumulative_calls - - # Apply limit if specified - if limit is not None: - stats_list = stats_list[:limit] - - # Determine the best unit for time columns based on maximum values - max_total_time = max( - (total_time for _, _, _, total_time, _, _ in stats_list), default=0 - ) - max_cumulative_time = max( - (cumulative_time for _, _, _, _, cumulative_time, _ in stats_list), - default=0, - ) - - total_time_unit, total_time_scale = _determine_best_unit(max_total_time) - cumulative_time_unit, cumulative_time_scale = _determine_best_unit( - max_cumulative_time - ) - - # Define column widths for consistent alignment - col_widths = { - "nsamples": 15, # "nsamples" column (inline/cumulative format) - "sample_pct": 8, # "sample%" column - "tottime": max(12, len(f"tottime ({total_time_unit})")), - "cum_pct": 8, # "cumul%" column - "cumtime": max(12, len(f"cumtime ({cumulative_time_unit})")), - } - - # Print header with colors and proper alignment - print(f"{ANSIColors.BOLD_BLUE}Profile Stats:{ANSIColors.RESET}") - - header_nsamples = f"{ANSIColors.BOLD_BLUE}{'nsamples':>{col_widths['nsamples']}}{ANSIColors.RESET}" - header_sample_pct = f"{ANSIColors.BOLD_BLUE}{'sample%':>{col_widths['sample_pct']}}{ANSIColors.RESET}" - header_tottime = f"{ANSIColors.BOLD_BLUE}{f'tottime ({total_time_unit})':>{col_widths['tottime']}}{ANSIColors.RESET}" - header_cum_pct = f"{ANSIColors.BOLD_BLUE}{'cumul%':>{col_widths['cum_pct']}}{ANSIColors.RESET}" - header_cumtime = f"{ANSIColors.BOLD_BLUE}{f'cumtime ({cumulative_time_unit})':>{col_widths['cumtime']}}{ANSIColors.RESET}" - header_filename = ( - f"{ANSIColors.BOLD_BLUE}filename:lineno(function){ANSIColors.RESET}" - ) - - print( - f"{header_nsamples} {header_sample_pct} {header_tottime} {header_cum_pct} {header_cumtime} {header_filename}" - ) - - # Print each line with proper alignment - for ( - func, - direct_calls, - cumulative_calls, - total_time, - cumulative_time, - callers, - ) in stats_list: - # Calculate percentages - sample_pct = ( - (direct_calls / total_samples * 100) if total_samples > 0 else 0 - ) - cum_pct = ( - (cumulative_calls / total_samples * 100) - if total_samples > 0 - else 0 - ) - - # Format values with proper alignment - always use A/B format - nsamples_str = f"{direct_calls}/{cumulative_calls}" - nsamples_str = f"{nsamples_str:>{col_widths['nsamples']}}" - sample_pct_str = f"{sample_pct:{col_widths['sample_pct']}.1f}" - tottime = f"{total_time * total_time_scale:{col_widths['tottime']}.3f}" - cum_pct_str = f"{cum_pct:{col_widths['cum_pct']}.1f}" - cumtime = f"{cumulative_time * cumulative_time_scale:{col_widths['cumtime']}.3f}" - - # Format the function name with colors - func_name = ( - f"{ANSIColors.GREEN}{func[0]}{ANSIColors.RESET}:" - f"{ANSIColors.YELLOW}{func[1]}{ANSIColors.RESET}(" - f"{ANSIColors.CYAN}{func[2]}{ANSIColors.RESET})" - ) - - # Print the formatted line with consistent spacing - print( - f"{nsamples_str} {sample_pct_str} {tottime} {cum_pct_str} {cumtime} {func_name}" - ) - - # Print legend - print(f"\n{ANSIColors.BOLD_BLUE}Legend:{ANSIColors.RESET}") - print( - f" {ANSIColors.YELLOW}nsamples{ANSIColors.RESET}: Direct/Cumulative samples (direct executing / on call stack)" - ) - print( - f" {ANSIColors.YELLOW}sample%{ANSIColors.RESET}: Percentage of total samples this function was directly executing" - ) - print( - f" {ANSIColors.YELLOW}tottime{ANSIColors.RESET}: Estimated total time spent directly in this function" - ) - print( - f" {ANSIColors.YELLOW}cumul%{ANSIColors.RESET}: Percentage of total samples when this function was on the call stack" - ) - print( - f" {ANSIColors.YELLOW}cumtime{ANSIColors.RESET}: Estimated cumulative time (including time in called functions)" - ) - print( - f" {ANSIColors.YELLOW}filename:lineno(function){ANSIColors.RESET}: Function location and name" - ) - - def _format_func_name(func): - """Format function name with colors.""" - return ( - f"{ANSIColors.GREEN}{func[0]}{ANSIColors.RESET}:" - f"{ANSIColors.YELLOW}{func[1]}{ANSIColors.RESET}(" - f"{ANSIColors.CYAN}{func[2]}{ANSIColors.RESET})" - ) - - def _print_top_functions(stats_list, title, key_func, format_line, n=3): - """Print top N functions sorted by key_func with formatted output.""" - print(f"\n{ANSIColors.BOLD_BLUE}{title}:{ANSIColors.RESET}") - sorted_stats = sorted(stats_list, key=key_func, reverse=True) - for stat in sorted_stats[:n]: - if line := format_line(stat): - print(f" {line}") - - # Print summary of interesting functions if enabled - if show_summary and stats_list: - print( - f"\n{ANSIColors.BOLD_BLUE}Summary of Interesting Functions:{ANSIColors.RESET}" - ) - - # Aggregate stats by fully qualified function name (ignoring line numbers) - func_aggregated = {} - for ( - func, - direct_calls, - cumulative_calls, - total_time, - cumulative_time, - callers, - ) in stats_list: - # Use filename:function_name as the key to get fully qualified name - qualified_name = f"{func[0]}:{func[2]}" - if qualified_name not in func_aggregated: - func_aggregated[qualified_name] = [ - 0, - 0, - 0, - 0, - ] # direct_calls, cumulative_calls, total_time, cumulative_time - func_aggregated[qualified_name][0] += direct_calls - func_aggregated[qualified_name][1] += cumulative_calls - func_aggregated[qualified_name][2] += total_time - func_aggregated[qualified_name][3] += cumulative_time - - # Convert aggregated data back to list format for processing - aggregated_stats = [] - for qualified_name, ( - prim_calls, - total_calls, - total_time, - cumulative_time, - ) in func_aggregated.items(): - # Parse the qualified name back to filename and function name - if ":" in qualified_name: - filename, func_name = qualified_name.rsplit(":", 1) - else: - filename, func_name = "", qualified_name - # Create a dummy func tuple with filename and function name for display - dummy_func = (filename, "", func_name) - aggregated_stats.append( - ( - dummy_func, - prim_calls, - total_calls, - total_time, - cumulative_time, - {}, - ) - ) - - # Determine best units for summary metrics - max_total_time = max( - (total_time for _, _, _, total_time, _, _ in aggregated_stats), - default=0, - ) - max_cumulative_time = max( - ( - cumulative_time - for _, _, _, _, cumulative_time, _ in aggregated_stats - ), - default=0, - ) - - total_unit, total_scale = _determine_best_unit(max_total_time) - cumulative_unit, cumulative_scale = _determine_best_unit( - max_cumulative_time - ) - - # Functions with highest direct/cumulative ratio (hot spots) - def format_hotspots(stat): - func, direct_calls, cumulative_calls, total_time, _, _ = stat - if direct_calls > 0 and cumulative_calls > 0: - ratio = direct_calls / cumulative_calls - direct_pct = ( - (direct_calls / total_samples * 100) - if total_samples > 0 - else 0 - ) - return ( - f"{ratio:.3f} direct/cumulative ratio, " - f"{direct_pct:.1f}% direct samples: {_format_func_name(func)}" - ) - return None - - _print_top_functions( - aggregated_stats, - "Functions with Highest Direct/Cumulative Ratio (Hot Spots)", - key_func=lambda x: (x[1] / x[2]) if x[2] > 0 else 0, - format_line=format_hotspots, - ) - - # Functions with highest call frequency (cumulative/direct difference) - def format_call_frequency(stat): - func, direct_calls, cumulative_calls, total_time, _, _ = stat - if cumulative_calls > direct_calls: - call_frequency = cumulative_calls - direct_calls - cum_pct = ( - (cumulative_calls / total_samples * 100) - if total_samples > 0 - else 0 - ) - return ( - f"{call_frequency:d} indirect calls, " - f"{cum_pct:.1f}% total stack presence: {_format_func_name(func)}" - ) - return None - - _print_top_functions( - aggregated_stats, - "Functions with Highest Call Frequency (Indirect Calls)", - key_func=lambda x: x[2] - x[1], # Sort by (cumulative - direct) - format_line=format_call_frequency, - ) - - # Functions with highest cumulative-to-direct multiplier (call magnification) - def format_call_magnification(stat): - func, direct_calls, cumulative_calls, total_time, _, _ = stat - if direct_calls > 0 and cumulative_calls > direct_calls: - multiplier = cumulative_calls / direct_calls - indirect_calls = cumulative_calls - direct_calls - return ( - f"{multiplier:.1f}x call magnification, " - f"{indirect_calls:d} indirect calls from {direct_calls:d} direct: {_format_func_name(func)}" - ) - return None - - _print_top_functions( - aggregated_stats, - "Functions with Highest Call Magnification (Cumulative/Direct)", - key_func=lambda x: (x[2] / x[1]) - if x[1] > 0 - else 0, # Sort by cumulative/direct ratio - format_line=format_call_magnification, - ) - - -def sample( - pid, - *, - sort=2, - sample_interval_usec=100, - duration_sec=10, - filename=None, - all_threads=False, - limit=None, - show_summary=True, - output_format="pstats", - realtime_stats=False, -): - profiler = SampleProfiler( - pid, sample_interval_usec, all_threads=all_threads - ) - profiler.realtime_stats = realtime_stats - - collector = None - match output_format: - case "pstats": - collector = PstatsCollector(sample_interval_usec) - case "collapsed": - collector = CollapsedStackCollector() - filename = filename or f"collapsed.{pid}.txt" - case _: - raise ValueError(f"Invalid output format: {output_format}") - - profiler.sample(collector, duration_sec) - - if output_format == "pstats" and not filename: - stats = pstats.SampledStats(collector).strip_dirs() - print_sampled_stats( - stats, sort, limit, show_summary, sample_interval_usec - ) - else: - collector.export(filename) - - -def _validate_collapsed_format_args(args, parser): - # Check for incompatible pstats options - invalid_opts = [] - - # Get list of pstats-specific options - pstats_options = {"sort": None, "limit": None, "no_summary": False} - - # Find the default values from the argument definitions - for action in parser._actions: - if action.dest in pstats_options and hasattr(action, "default"): - pstats_options[action.dest] = action.default - - # Check if any pstats-specific options were provided by comparing with defaults - for opt, default in pstats_options.items(): - if getattr(args, opt) != default: - invalid_opts.append(opt.replace("no_", "")) - - if invalid_opts: - parser.error( - f"The following options are only valid with --pstats format: {', '.join(invalid_opts)}" - ) - - # Set default output filename for collapsed format - if not args.outfile: - args.outfile = f"collapsed.{args.pid}.txt" - - -def main(): - # Create the main parser - parser = argparse.ArgumentParser( - description=( - "Sample a process's stack frames and generate profiling data.\n" - "Supports two output formats:\n" - " - pstats: Detailed profiling statistics with sorting options\n" - " - collapsed: Stack traces for generating flamegraphs\n" - "\n" - "Examples:\n" - " # Profile process 1234 for 10 seconds with default settings\n" - " python -m profile.sample 1234\n" - "\n" - " # Profile with custom interval and duration, save to file\n" - " python -m profile.sample -i 50 -d 30 -o profile.stats 1234\n" - "\n" - " # Generate collapsed stacks for flamegraph\n" - " python -m profile.sample --collapsed 1234\n" - "\n" - " # Profile all threads, sort by total time\n" - " python -m profile.sample -a --sort-tottime 1234\n" - "\n" - " # Profile for 1 minute with 1ms sampling interval\n" - " python -m profile.sample -i 1000 -d 60 1234\n" - "\n" - " # Show only top 20 functions sorted by direct samples\n" - " python -m profile.sample --sort-nsamples -l 20 1234\n" - "\n" - " # Profile all threads and save collapsed stacks\n" - " python -m profile.sample -a --collapsed -o stacks.txt 1234\n" - "\n" - " # Profile with real-time sampling statistics\n" - " python -m profile.sample --realtime-stats 1234\n" - "\n" - " # Sort by sample percentage to find most sampled functions\n" - " python -m profile.sample --sort-sample-pct 1234\n" - "\n" - " # Sort by cumulative samples to find functions most on call stack\n" - " python -m profile.sample --sort-nsamples-cumul 1234" - ), - formatter_class=argparse.RawDescriptionHelpFormatter, - ) - - # Required arguments - parser.add_argument("pid", type=int, help="Process ID to sample") - - # Sampling options - sampling_group = parser.add_argument_group("Sampling configuration") - sampling_group.add_argument( - "-i", - "--interval", - type=int, - default=100, - help="Sampling interval in microseconds (default: 100)", - ) - sampling_group.add_argument( - "-d", - "--duration", - type=int, - default=10, - help="Sampling duration in seconds (default: 10)", - ) - sampling_group.add_argument( - "-a", - "--all-threads", - action="store_true", - help="Sample all threads in the process instead of just the main thread", - ) - sampling_group.add_argument( - "--realtime-stats", - action="store_true", - default=False, - help="Print real-time sampling statistics (Hz, mean, min, max, stdev) during profiling", - ) - - # Output format selection - output_group = parser.add_argument_group("Output options") - output_format = output_group.add_mutually_exclusive_group() - output_format.add_argument( - "--pstats", - action="store_const", - const="pstats", - dest="format", - default="pstats", - help="Generate pstats output (default)", - ) - output_format.add_argument( - "--collapsed", - action="store_const", - const="collapsed", - dest="format", - help="Generate collapsed stack traces for flamegraphs", - ) - - output_group.add_argument( - "-o", - "--outfile", - help="Save output to a file (if omitted, prints to stdout for pstats, " - "or saves to collapsed..txt for collapsed format)", - ) - - # pstats-specific options - pstats_group = parser.add_argument_group("pstats format options") - sort_group = pstats_group.add_mutually_exclusive_group() - sort_group.add_argument( - "--sort-nsamples", - action="store_const", - const=0, - dest="sort", - help="Sort by number of direct samples (nsamples column)", - ) - sort_group.add_argument( - "--sort-tottime", - action="store_const", - const=1, - dest="sort", - help="Sort by total time (tottime column)", - ) - sort_group.add_argument( - "--sort-cumtime", - action="store_const", - const=2, - dest="sort", - help="Sort by cumulative time (cumtime column, default)", - ) - sort_group.add_argument( - "--sort-sample-pct", - action="store_const", - const=3, - dest="sort", - help="Sort by sample percentage (sample%% column)", - ) - sort_group.add_argument( - "--sort-cumul-pct", - action="store_const", - const=4, - dest="sort", - help="Sort by cumulative sample percentage (cumul%% column)", - ) - sort_group.add_argument( - "--sort-nsamples-cumul", - action="store_const", - const=5, - dest="sort", - help="Sort by cumulative samples (nsamples column, cumulative part)", - ) - sort_group.add_argument( - "--sort-name", - action="store_const", - const=-1, - dest="sort", - help="Sort by function name", - ) - - pstats_group.add_argument( - "-l", - "--limit", - type=int, - help="Limit the number of rows in the output", - default=15, - ) - pstats_group.add_argument( - "--no-summary", - action="store_true", - help="Disable the summary section in the output", - ) - - args = parser.parse_args() - - # Validate format-specific arguments - if args.format == "collapsed": - _validate_collapsed_format_args(args, parser) - - sort_value = args.sort if args.sort is not None else 2 - - sample( - args.pid, - sample_interval_usec=args.interval, - duration_sec=args.duration, - filename=args.outfile, - all_threads=args.all_threads, - limit=args.limit, - sort=sort_value, - show_summary=not args.no_summary, - output_format=args.format, - realtime_stats=args.realtime_stats, - ) - - -if __name__ == "__main__": - main() diff --git a/Lib/profile/stack_collector.py b/Lib/profile/stack_collector.py deleted file mode 100644 index 97dbcb5b2fb4b2..00000000000000 --- a/Lib/profile/stack_collector.py +++ /dev/null @@ -1,37 +0,0 @@ -import collections -import os - -from .collector import Collector - - -class StackTraceCollector(Collector): - def __init__(self): - self.call_trees = [] - self.function_samples = collections.defaultdict(int) - - def collect(self, stack_frames): - for thread_id, frames in stack_frames: - if frames: - # Store the complete call stack (reverse order - root first) - call_tree = list(reversed(frames)) - self.call_trees.append(call_tree) - - # Count samples per function - for frame in frames: - self.function_samples[frame] += 1 - - -class CollapsedStackCollector(StackTraceCollector): - def export(self, filename): - stack_counter = collections.Counter() - for call_tree in self.call_trees: - # Call tree is already in root->leaf order - stack_str = ";".join( - f"{os.path.basename(f[0])}:{f[2]}:{f[1]}" for f in call_tree - ) - stack_counter[stack_str] += 1 - - with open(filename, "w") as f: - for stack, count in stack_counter.items(): - f.write(f"{stack} {count}\n") - print(f"Collapsed stack output written to {filename}") diff --git a/Lib/profiling/__init__.py b/Lib/profiling/__init__.py new file mode 100644 index 00000000000000..c94e1e16e35ba2 --- /dev/null +++ b/Lib/profiling/__init__.py @@ -0,0 +1,13 @@ +"""Python profiling tools. + +This package provides two types of profilers: + +- profiling.tracing: Deterministic tracing profiler that instruments every + function call and return. Higher overhead but provides exact call counts + and timing. + +- profiling.sampling: Statistical sampling profiler that periodically samples + the call stack. Low overhead and suitable for production use. +""" + +__all__ = ("tracing", "sampling") diff --git a/Lib/profiling/sampling/__init__.py b/Lib/profiling/sampling/__init__.py new file mode 100644 index 00000000000000..71579a3903253e --- /dev/null +++ b/Lib/profiling/sampling/__init__.py @@ -0,0 +1,23 @@ +"""Statistical sampling profiler for Python. + +This module provides low-overhead profiling by periodically sampling the +call stack rather than tracing every function call. +""" + +from .collector import Collector +from .pstats_collector import PstatsCollector +from .stack_collector import CollapsedStackCollector +from .heatmap_collector import HeatmapCollector +from .gecko_collector import GeckoCollector +from .jsonl_collector import JsonlCollector +from .string_table import StringTable + +__all__ = ( + "Collector", + "PstatsCollector", + "CollapsedStackCollector", + "HeatmapCollector", + "GeckoCollector", + "JsonlCollector", + "StringTable", +) diff --git a/Lib/profiling/sampling/__main__.py b/Lib/profiling/sampling/__main__.py new file mode 100644 index 00000000000000..a45b645eae05fc --- /dev/null +++ b/Lib/profiling/sampling/__main__.py @@ -0,0 +1,73 @@ +"""Run the sampling profiler from the command line.""" + +import sys + +MACOS_PERMISSION_ERROR = """\ +🔒 Permission Error: Unable to access process memory on macOS + +Tachyon needs elevated permissions to profile processes. Try one of these solutions: + +1. Try running again with elevated permissions by running 'sudo -E !!'. + +2. If targeting system Python processes: + Note: Apple's System Integrity Protection (SIP) may block access to system + Python binaries. Consider using a user-installed Python instead. +""" + +LINUX_PERMISSION_ERROR = """ +🔒 Tachyon was unable to access process memory. This could be because tachyon +has insufficient privileges (the required capability is CAP_SYS_PTRACE). +Unprivileged processes cannot trace processes that they cannot send signals +to or those running set-user-ID/set-group-ID programs, for security reasons. + +If your uid matches the uid of the target process you want to analyze, you +can do one of the following to get 'ptrace' scope permissions: + +* If you are running inside a Docker container, you need to make sure you + start the container using the '--cap-add=SYS_PTRACE' or '--privileged' + command line arguments. Notice that this may not be enough if you are not + running as 'root' inside the Docker container as you may need to disable + hardening (see next points). + +* Try running again with elevated permissions by running 'sudo -E !!'. + +* You can disable kernel hardening for the current session temporarily (until + a reboot happens) by running 'echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope'. +""" + +WINDOWS_PERMISSION_ERROR = """ +🔒 Tachyon requires administrator rights to access process memory on Windows. +Please run your command prompt as Administrator and try again. +""" + +GENERIC_PERMISSION_ERROR = """ +🔒 Tachyon was unable to access the target process due to operating +system restrictions or missing privileges. +""" + +from .cli import main +from .errors import SamplingUnknownProcessError, SamplingModuleNotFoundError, SamplingScriptNotFoundError + +def handle_permission_error(): + """Handle PermissionError by displaying appropriate error message.""" + if sys.platform == "darwin": + print(MACOS_PERMISSION_ERROR, file=sys.stderr) + elif sys.platform.startswith("linux"): + print(LINUX_PERMISSION_ERROR, file=sys.stderr) + elif sys.platform.startswith("win"): + print(WINDOWS_PERMISSION_ERROR, file=sys.stderr) + else: + print(GENERIC_PERMISSION_ERROR, file=sys.stderr) + sys.exit(1) + +if __name__ == '__main__': + try: + main() + except PermissionError: + handle_permission_error() + except SamplingUnknownProcessError as err: + print(f"Tachyon cannot find the process: {err}", file=sys.stderr) + sys.exit(1) + except (SamplingModuleNotFoundError, SamplingScriptNotFoundError) as err: + print(f"Tachyon cannot find the target: {err}", file=sys.stderr) + sys.exit(1) diff --git a/Lib/profiling/sampling/_assets/python-logo-only.png b/Lib/profiling/sampling/_assets/python-logo-only.png new file mode 100644 index 00000000000000..e590b5c6480d94 Binary files /dev/null and b/Lib/profiling/sampling/_assets/python-logo-only.png differ diff --git a/Lib/profiling/sampling/_assets/tachyon-logo.png b/Lib/profiling/sampling/_assets/tachyon-logo.png new file mode 100644 index 00000000000000..03e67823d5afc9 Binary files /dev/null and b/Lib/profiling/sampling/_assets/tachyon-logo.png differ diff --git a/Lib/profiling/sampling/_child_monitor.py b/Lib/profiling/sampling/_child_monitor.py new file mode 100644 index 00000000000000..ec56f75719f9d1 --- /dev/null +++ b/Lib/profiling/sampling/_child_monitor.py @@ -0,0 +1,279 @@ +""" +Child process monitoring for the sampling profiler. + +This module monitors a target process for child process creation and spawns +separate profiler instances for each discovered child. +""" + +import subprocess +import sys +import threading +import time + +import _remote_debugging + +# Polling interval for child process discovery +_CHILD_POLL_INTERVAL_SEC = 0.1 + +# Default timeout for waiting on child profilers +_DEFAULT_WAIT_TIMEOUT_SEC = 30.0 + +# Maximum number of child profilers to spawn (prevents resource exhaustion) +_MAX_CHILD_PROFILERS = 100 + +# Interval for cleaning up completed profilers (in polling cycles) +_CLEANUP_INTERVAL_CYCLES = 10 + + +def get_child_pids(pid, recursive=True): + """ + Get all child process IDs of the given process. + + Args: + pid: Process ID of the parent process + recursive: If True, return all descendants (children, grandchildren, etc.) + + Returns: + List of child PIDs + """ + return _remote_debugging.get_child_pids(pid, recursive=recursive) + + +def is_python_process(pid): + """ + Check if a process is a Python process. + + Args: + pid: Process ID to check + + Returns: + bool: True if the process appears to be a Python process, False otherwise + """ + return _remote_debugging.is_python_process(pid) + + +class ChildProcessMonitor: + """ + Monitors a target process for child processes and spawns profilers for them. + + Use as a context manager: + with ChildProcessMonitor(pid, cli_args, output_pattern) as monitor: + # monitoring runs here + monitor.wait_for_profilers() # optional: wait before cleanup + # cleanup happens automatically + """ + + def __init__(self, pid, cli_args, output_pattern): + """ + Initialize the child process monitor. + + Args: + pid: Parent process ID to monitor + cli_args: CLI arguments to pass to child profilers + output_pattern: Pattern for output files (format string with {pid}) + """ + self.parent_pid = pid + self.cli_args = cli_args + self.output_pattern = output_pattern + + self._known_children = set() + self._spawned_profilers = [] + self._lock = threading.Lock() + self._stop_event = threading.Event() + self._monitor_thread = None + self._poll_count = 0 + + def __enter__(self): + self._monitor_thread = threading.Thread( + target=self._monitor_loop, + daemon=True, + name=f"child-monitor-{self.parent_pid}", + ) + self._monitor_thread.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._stop_event.set() + if self._monitor_thread is not None: + self._monitor_thread.join(timeout=2.0) + if self._monitor_thread.is_alive(): + print( + "Warning: Monitor thread did not stop cleanly", + file=sys.stderr, + ) + + # Wait for child profilers to complete naturally + self.wait_for_profilers() + + # Terminate any remaining profilers + with self._lock: + profilers_to_cleanup = list(self._spawned_profilers) + self._spawned_profilers.clear() + + for proc in profilers_to_cleanup: + self._cleanup_process(proc) + return False + + def _cleanup_process(self, proc, terminate_timeout=2.0, kill_timeout=1.0): + if proc.poll() is not None: + return # Already terminated + + proc.terminate() + try: + proc.wait(timeout=terminate_timeout) + except subprocess.TimeoutExpired: + proc.kill() + try: + proc.wait(timeout=kill_timeout) + except subprocess.TimeoutExpired: + # Last resort: wait indefinitely to avoid zombie + # SIGKILL should always work, but we must reap the process + try: + proc.wait() + except Exception: + pass + + @property + def spawned_profilers(self): + with self._lock: + return list(self._spawned_profilers) + + def wait_for_profilers(self, timeout=_DEFAULT_WAIT_TIMEOUT_SEC): + """ + Wait for all spawned child profilers to complete. + + Call this before exiting the context if you want profilers to finish + their work naturally rather than being terminated. + + Args: + timeout: Maximum time to wait in seconds + """ + profilers = self.spawned_profilers + if not profilers: + return + + print( + f"Waiting for {len(profilers)} child profiler(s) to complete...", + file=sys.stderr, + ) + + deadline = time.monotonic() + timeout + for proc in profilers: + remaining = deadline - time.monotonic() + if remaining <= 0: + break + try: + proc.wait(timeout=max(0.1, remaining)) + except subprocess.TimeoutExpired: + pass + + def _monitor_loop(self): + # Note: There is an inherent TOCTOU race between discovering a child + # process and checking if it's Python. This is expected for process monitoring. + while not self._stop_event.is_set(): + try: + self._poll_count += 1 + + # Periodically clean up completed profilers to avoid memory buildup + if self._poll_count % _CLEANUP_INTERVAL_CYCLES == 0: + self._cleanup_completed_profilers() + + children = set(get_child_pids(self.parent_pid, recursive=True)) + + with self._lock: + new_children = children - self._known_children + self._known_children.update(new_children) + + for child_pid in new_children: + # Only spawn profiler if this is actually a Python process + if is_python_process(child_pid): + self._spawn_profiler_for_child(child_pid) + + except ProcessLookupError: + # Parent process exited, stop monitoring + break + except Exception as e: + # Log error but continue monitoring + print( + f"Warning: Error in child monitor loop: {e}", + file=sys.stderr, + ) + + self._stop_event.wait(timeout=_CHILD_POLL_INTERVAL_SEC) + + def _cleanup_completed_profilers(self): + with self._lock: + # Keep only profilers that are still running + self._spawned_profilers = [ + p for p in self._spawned_profilers if p.poll() is None + ] + + def _spawn_profiler_for_child(self, child_pid): + if self._stop_event.is_set(): + return + + # Check if we've reached the maximum number of child profilers + with self._lock: + if len(self._spawned_profilers) >= _MAX_CHILD_PROFILERS: + print( + f"Warning: Max child profilers ({_MAX_CHILD_PROFILERS}) reached, " + f"skipping PID {child_pid}", + file=sys.stderr, + ) + return + + cmd = [ + sys.executable, + "-m", + "profiling.sampling", + "attach", + str(child_pid), + ] + cmd.extend(self._build_child_cli_args(child_pid)) + + proc = None + try: + proc = subprocess.Popen( + cmd, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + with self._lock: + if self._stop_event.is_set(): + self._cleanup_process( + proc, terminate_timeout=1.0, kill_timeout=1.0 + ) + return + self._spawned_profilers.append(proc) + + print( + f"Started profiler for child process {child_pid}", + file=sys.stderr, + ) + except Exception as e: + if proc is not None: + self._cleanup_process( + proc, terminate_timeout=1.0, kill_timeout=1.0 + ) + print( + f"Warning: Failed to start profiler for child {child_pid}: {e}", + file=sys.stderr, + ) + + def _build_child_cli_args(self, child_pid): + args = list(self.cli_args) + + if self.output_pattern: + # Use replace() instead of format() to handle user filenames with braces + output_file = self.output_pattern.replace("{pid}", str(child_pid)) + found_output = False + for i, arg in enumerate(args): + if arg in ("-o", "--output") and i + 1 < len(args): + args[i + 1] = output_file + found_output = True + break + if not found_output: + args.extend(["-o", output_file]) + + return args diff --git a/Lib/profiling/sampling/_css_utils.py b/Lib/profiling/sampling/_css_utils.py new file mode 100644 index 00000000000000..40912e9b3528e3 --- /dev/null +++ b/Lib/profiling/sampling/_css_utils.py @@ -0,0 +1,22 @@ +import importlib.resources + + +def get_combined_css(component: str) -> str: + template_dir = importlib.resources.files(__package__) + + base_css = (template_dir / "_shared_assets" / "base.css").read_text(encoding="utf-8") + + if component == "flamegraph": + component_css = ( + template_dir / "_flamegraph_assets" / "flamegraph.css" + ).read_text(encoding="utf-8") + elif component == "heatmap": + component_css = (template_dir / "_heatmap_assets" / "heatmap.css").read_text( + encoding="utf-8" + ) + else: + raise ValueError( + f"Unknown component: {component}. Expected 'flamegraph' or 'heatmap'." + ) + + return f"{base_css}\n\n{component_css}" diff --git a/Lib/profiling/sampling/_flamegraph_assets/flamegraph.css b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.css new file mode 100644 index 00000000000000..c93ee1e9dd470e --- /dev/null +++ b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.css @@ -0,0 +1,1085 @@ +/* ========================================================================== + Flamegraph Viewer - Component-Specific CSS + + DEPENDENCY: Requires _shared_assets/base.css to be loaded first + This file extends the shared foundation with flamegraph-specific styles. + ========================================================================== */ + +/* -------------------------------------------------------------------------- + Differential Flamegraph + -------------------------------------------------------------------------- */ + +:root { + /* Regression colors */ + --diff-regression-deep: #d32f2f; + --diff-regression-medium: #e57373; + --diff-regression-light: #ef9a9a; + --diff-regression-verylight: #ffcdd2; + + /* Improvement colors */ + --diff-improvement-deep: #1976d2; + --diff-improvement-medium: #42a5f5; + --diff-improvement-light: #64b5f6; + --diff-improvement-verylight: #90caf9; + + /* Other differential colors */ + --diff-neutral: #bdbdbd; + --diff-new: #9575cd; + --diff-elided: #b39ddb; +} + +/* Dark mode differential colors - adjusted for contrast against dark backgrounds */ +[data-theme="dark"] { + --diff-regression-deep: #ef5350; + --diff-regression-medium: #e57373; + --diff-regression-light: #ef9a9a; + --diff-regression-verylight: #ffcdd2; + + --diff-improvement-deep: #42a5f5; + --diff-improvement-medium: #64b5f6; + --diff-improvement-light: #90caf9; + --diff-improvement-verylight: #bbdefb; + + --diff-neutral: #757575; + --diff-new: #b39ddb; + --diff-elided: #ce93d8; +} + +/* -------------------------------------------------------------------------- + Layout Overrides (Flamegraph-specific) + -------------------------------------------------------------------------- */ + +html, body { + height: 100%; + overflow: hidden; +} + +.app-layout { + height: 100vh; +} + +.main-content { + display: flex; + flex: 1; + min-height: 0; +} + +/* -------------------------------------------------------------------------- + Search Input (Flamegraph-specific) + -------------------------------------------------------------------------- */ + +.search-wrapper { + flex: 1; + max-width: 360px; + position: relative; +} + +.search-input { + width: 100%; + padding: 8px 36px 8px 14px; + font-family: var(--font-sans); + font-size: 13px; + color: #2e3338; + background: rgba(255, 255, 255, 0.95); + border: 2px solid rgba(255, 255, 255, 0.3); + border-radius: 20px; + outline: none; + transition: all var(--transition-fast); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.search-input::placeholder { + color: #6c757d; +} + +.search-input:focus { + border-color: rgba(255, 255, 255, 0.8); + background: white; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); +} + +/* Dark theme search input */ +[data-theme="dark"] .search-input { + color: #e6edf3; + background: rgba(33, 38, 45, 0.95); + border: 2px solid rgba(88, 166, 255, 0.3); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +[data-theme="dark"] .search-input::placeholder { + color: #8b949e; +} + +[data-theme="dark"] .search-input:focus { + border-color: rgba(88, 166, 255, 0.6); + background: rgba(33, 38, 45, 1); + box-shadow: 0 4px 16px rgba(88, 166, 255, 0.2); +} + +.search-input.has-matches { + border-color: rgba(40, 167, 69, 0.8); + box-shadow: 0 4px 16px rgba(40, 167, 69, 0.2); +} + +.search-input.no-matches { + border-color: rgba(220, 53, 69, 0.8); + box-shadow: 0 4px 16px rgba(220, 53, 69, 0.2); +} + +.search-clear { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + width: 20px; + height: 20px; + padding: 0; + display: none; + align-items: center; + justify-content: center; + font-size: 14px; + line-height: 1; + color: #6c757d; + background: transparent; + border: none; + border-radius: 50%; + cursor: pointer; + transition: color var(--transition-fast); +} + +.search-clear:hover { + color: #2e3338; +} + +[data-theme="dark"] .search-clear { + color: #8b949e; +} + +[data-theme="dark"] .search-clear:hover { + color: #e6edf3; +} + +.search-wrapper.has-value .search-clear { + display: flex; +} + +/* -------------------------------------------------------------------------- + Sidebar + -------------------------------------------------------------------------- */ + +.sidebar { + width: var(--sidebar-width); + background: var(--bg-secondary); + border-right: 1px solid var(--border); + display: flex; + flex-direction: column; + flex-shrink: 0; + overflow: hidden; + position: relative; +} + +.sidebar.collapsed { + width: var(--sidebar-collapsed) !important; + transition: width var(--transition-normal); +} + +.sidebar-toggle { + position: absolute; + top: 12px; + right: 10px; + width: 26px; + height: 26px; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + background: var(--bg-primary); + border: 1px solid var(--border); + border-radius: 6px; + cursor: pointer; + transition: all var(--transition-fast); + z-index: 10; +} + +.sidebar-toggle svg { + transition: transform var(--transition-fast); +} + +.sidebar-toggle:hover { + color: var(--accent); + border-color: var(--accent); + background: var(--accent-glow); +} + +.sidebar.collapsed .sidebar-toggle { + right: 9px; +} + +.sidebar.collapsed .sidebar-toggle svg { + transform: rotate(180deg); +} + +.sidebar-content { + flex: 1; + overflow-y: auto; + padding: 44px 14px 14px; +} + +.sidebar.collapsed .sidebar-content { + display: none; +} + +.sidebar-resize-handle { + position: absolute; + top: 0; + right: 0; + width: 6px; + height: 100%; + cursor: col-resize; + background: transparent; + transition: background var(--transition-fast); + z-index: 11; +} + +.sidebar-resize-handle:hover, +.sidebar-resize-handle.resizing { + background: var(--python-gold); +} + +.sidebar-resize-handle::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 2px; + height: 40px; + background: var(--border); + border-radius: 1px; + opacity: 0; + transition: opacity var(--transition-fast); +} + +.sidebar-resize-handle:hover::before { + opacity: 1; +} + +.sidebar.collapsed .sidebar-resize-handle { + display: none; +} + +body.resizing-sidebar { + cursor: col-resize; + user-select: none; +} + +/* Sidebar Logo */ +.sidebar-logo { + display: flex; + justify-content: center; + margin-bottom: 16px; +} + +.sidebar-logo-img { + width: 220px; + height: 180px; + display: flex; + align-items: center; + justify-content: center; +} + +.sidebar-logo-img svg, +.sidebar-logo-img img { + width: 100%; + height: 100%; + object-fit: contain; +} + +/* Sidebar sections */ +.sidebar-section { + margin-bottom: 20px; +} + +.sidebar-section:last-child { + margin-bottom: 0; +} + +.section-title { + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.8px; + color: var(--accent); + margin: 0; + flex: 1; +} + +/* View Mode Section */ +.view-mode-section { + display: flex; + flex-direction: column; + gap: 8px; +} + +.view-mode-section .section-content { + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; +} + +/* Collapsible sections */ +.collapsible .section-header { + display: flex; + align-items: center; + width: 100%; + padding: 0 0 8px 0; + margin-bottom: 10px; + background: none; + border: none; + border-bottom: 2px solid var(--python-gold); + cursor: pointer; + transition: all var(--transition-fast); +} + +.collapsible .section-header:hover { + opacity: 0.8; +} + +.section-chevron { + color: var(--text-muted); + transition: transform var(--transition-fast); +} + +.collapsible.collapsed .section-chevron { + transform: rotate(-90deg); +} + +.section-content { + transition: max-height var(--transition-slow) ease-out, opacity var(--transition-normal) ease-out, padding var(--transition-normal) ease-out; + max-height: 1000px; + opacity: 1; +} + +.collapsible.collapsed .section-content { + max-height: 0; + opacity: 0; + padding-top: 0; + pointer-events: none; + transition: max-height var(--transition-slow) ease-in, opacity var(--transition-normal) ease-in, padding var(--transition-normal) ease-in; +} + +/* -------------------------------------------------------------------------- + Profile Summary Cards + -------------------------------------------------------------------------- */ + +.summary-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 6px; +} + +.summary-card { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 10px; + background: var(--bg-primary); + border: 2px solid var(--border); + border-radius: 8px; + transition: all var(--transition-fast); + animation: slideUp 0.4s ease-out backwards; + animation-delay: calc(var(--i, 0) * 0.08s); + overflow: hidden; + position: relative; +} + +.summary-card:nth-child(1) { --i: 0; --card-color: var(--card-blue); } +.summary-card:nth-child(2) { --i: 1; --card-color: var(--card-green); } +.summary-card:nth-child(3) { --i: 2; --card-color: var(--card-yellow); } +.summary-card:nth-child(4) { --i: 3; --card-color: var(--card-purple); } + +.summary-card:hover { + border-color: rgba(var(--card-color), 0.6); + background: linear-gradient(135deg, rgba(var(--card-color), 0.08) 0%, var(--bg-primary) 100%); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(var(--card-color), 0.15); +} + +.summary-icon { + font-size: 14px; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, rgba(var(--card-color), 0.15) 0%, rgba(var(--card-color), 0.05) 100%); + border: 1px solid rgba(var(--card-color), 0.2); + border-radius: 6px; + flex-shrink: 0; + transition: all var(--transition-fast); +} + +.summary-card:hover .summary-icon { + transform: scale(1.05); + background: linear-gradient(135deg, rgba(var(--card-color), 0.25) 0%, rgba(var(--card-color), 0.1) 100%); +} + +.summary-data { + min-width: 0; + flex: 1; + overflow: hidden; +} + +.summary-value { + font-family: var(--font-mono); + font-size: 13px; + font-weight: 800; + color: rgb(var(--card-color)); + line-height: 1.2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.summary-label { + font-size: 8px; + font-weight: 600; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* -------------------------------------------------------------------------- + Progress Bars + -------------------------------------------------------------------------- */ + +.bar-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 5px; +} + +.bar-label { + font-size: 9px; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.2px; +} + +.bar-value { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 700; + color: var(--accent); +} + +.bar { + height: 6px; + background: var(--bg-tertiary); + border-radius: 3px; + overflow: hidden; +} + +.bar-fill { + height: 100%; + background: linear-gradient(90deg, #28a745 0%, #20c997 50%, #17a2b8 100%); + border-radius: 3px; + transition: width 0.6s ease-out; + position: relative; + overflow: hidden; +} + +.bar-fill::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 255, 255, 0.4) 50%, + transparent 100% + ); + animation: shimmer 2s ease-in-out infinite; +} + +/* -------------------------------------------------------------------------- + Efficiency Section Container + -------------------------------------------------------------------------- */ + +.efficiency-section { + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid var(--border); +} + +/* -------------------------------------------------------------------------- + Thread Stats Progress Bars (in Sidebar) + -------------------------------------------------------------------------- */ + +.thread-stats-section { + display: block; +} + +.stats-container { + display: flex; + flex-direction: column; + gap: 10px; +} + +.stat-item { + animation: fadeIn 0.4s ease-out backwards; + animation-delay: calc(var(--i, 0) * 0.05s); +} + +.stat-item:nth-child(1) { --i: 0; } +.stat-item:nth-child(2) { --i: 1; } +.stat-item:nth-child(3) { --i: 2; } +.stat-item:nth-child(4) { --i: 3; } +.stat-item:nth-child(5) { --i: 4; } + +/* Color variants for bar-fill */ +.bar-fill--green { + background: linear-gradient(90deg, #28a745 0%, #20c997 100%); +} + +.bar-fill--yellow { + background: linear-gradient(90deg, #ffc107 0%, #ffdb4d 100%); +} + +.bar-fill--purple { + background: linear-gradient(90deg, #6f42c1 0%, #9b6dd6 100%); +} + +.bar-fill--red { + background: linear-gradient(90deg, #dc3545 0%, #ff6b7a 100%); +} + +/* -------------------------------------------------------------------------- + Hotspot Cards + -------------------------------------------------------------------------- */ + +.hotspot { + display: flex; + align-items: flex-start; + gap: 10px; + padding: 10px; + margin-bottom: 8px; + background: var(--bg-primary); + border: 1px solid var(--border); + border-radius: 8px; + cursor: pointer; + transition: all var(--transition-fast); + opacity: 0; + transform: translateY(8px); + box-shadow: var(--shadow-sm); +} + +.hotspot.visible { + opacity: 1; + transform: translateY(0); +} + +.hotspot:hover { + border-color: var(--accent); + box-shadow: var(--shadow-md); + transform: translateY(-2px); +} + +.hotspot.active { + border-color: var(--python-gold); + background: var(--accent-glow); + box-shadow: 0 0 0 3px var(--accent-glow); +} + +.hotspot:last-child { + margin-bottom: 0; +} + +.hotspot-rank { + width: 26px; + height: 26px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + font-size: 12px; + flex-shrink: 0; + background: linear-gradient(135deg, var(--python-blue) 0%, var(--python-blue-light) 100%); + color: white; + box-shadow: 0 2px 4px rgba(55, 118, 171, 0.3); +} + +.hotspot-rank--1 { background: linear-gradient(135deg, #d4af37, #f4d03f); color: #5a4a00; } +.hotspot-rank--2 { background: linear-gradient(135deg, #a8a8a8, #c0c0c0); color: #4a4a4a; } +.hotspot-rank--3 { background: linear-gradient(135deg, #cd7f32, #e6a55a); color: #5a3d00; } + +.hotspot-info { + flex: 1; + min-width: 0; +} + +.hotspot-func { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 600; + color: var(--text-primary); + line-height: 1.3; + word-break: break-word; + margin-bottom: 2px; +} + +.hotspot-file { + font-family: var(--font-mono); + font-size: 10px; + color: var(--text-muted); + margin-bottom: 3px; + word-break: break-all; +} + +.hotspot-stats { + font-family: var(--font-mono); + font-size: 10px; + color: var(--text-secondary); +} + +.hotspot-percent { + color: var(--accent); + font-weight: 600; +} + +/* -------------------------------------------------------------------------- + Legend + -------------------------------------------------------------------------- */ + + +.legend { + display: flex; + flex-direction: column; + gap: 4px; +} + +.legend-item { + display: flex; + align-items: center; + gap: 8px; + padding: 5px 8px; + background: var(--bg-primary); + border-radius: 4px; + border: 1px solid var(--border-subtle); + font-size: 11px; +} + +.legend-color { + width: 20px; + height: 10px; + border-radius: 2px; + flex-shrink: 0; + border: 1px solid rgba(0, 0, 0, 0.08); +} + +.legend-label { + color: var(--text-primary); + font-weight: 500; + flex: 1; +} + +.legend-range { + font-family: var(--font-mono); + font-size: 9px; + color: var(--text-muted); +} + +/* -------------------------------------------------------------------------- + Thread Filter + -------------------------------------------------------------------------- */ + +.filter-section { + padding-top: 12px; + border-top: 1px solid var(--border); +} + +.filter-label { + display: block; + font-size: 10px; + font-weight: 600; + color: var(--text-muted); + margin-bottom: 6px; +} + +.filter-select { + width: 100%; + padding: 7px 28px 7px 10px; + font-family: var(--font-mono); + font-size: 11px; + color: var(--text-primary); + background: var(--bg-primary); + border: 2px solid var(--accent); + border-radius: 6px; + cursor: pointer; + outline: none; + appearance: none; + background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233776ab' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right 6px center; + background-size: 14px; + transition: all var(--transition-fast); +} + +.filter-select:hover { + border-color: var(--accent-hover); + box-shadow: 0 2px 6px var(--accent-glow); +} + +.filter-select:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-glow); +} + +/* -------------------------------------------------------------------------- + Chart Area + -------------------------------------------------------------------------- */ + +.chart-area { + flex: 1; + min-width: 0; + overflow: hidden; + background: var(--bg-primary); + position: relative; +} + +#chart { + width: 100%; + height: 100%; + padding: 16px; + overflow: auto; +} + +/* D3 Flamegraph overrides */ +.d3-flame-graph rect { + stroke: rgba(55, 118, 171, 0.3); + stroke-width: 1px; + cursor: pointer; + transition: filter 0.1s ease; +} + +.d3-flame-graph rect:hover { + stroke: var(--python-blue); + stroke-width: 2px; + filter: brightness(1.08); +} + +.d3-flame-graph text { + font-family: var(--font-sans); + font-size: 12px; + font-weight: 500; + fill: var(--text-primary); + pointer-events: none; +} + +/* Search highlight */ +.d3-flame-graph rect.search-match { + stroke: #ff6b35 !important; + stroke-width: 2px !important; + stroke-dasharray: 4 2; +} + +.d3-flame-graph rect.search-dim { + opacity: 0.25; +} + +/* -------------------------------------------------------------------------- + Tooltip + -------------------------------------------------------------------------- */ + +.python-tooltip { + position: absolute; + z-index: 1000; + pointer-events: none; + background: var(--bg-primary); + border: 1px solid var(--border); + border-radius: 8px; + padding: 14px; + max-width: 480px; + box-shadow: var(--shadow-lg); + font-family: var(--font-sans); + font-size: 13px; + color: var(--text-primary); + word-wrap: break-word; + overflow-wrap: break-word; + line-height: 1.5; +} + +.tooltip-header { + margin-bottom: 10px; +} + +.tooltip-title { + font-size: 14px; + font-weight: 600; + color: var(--accent); + line-height: 1.3; + word-break: break-word; + margin-bottom: 4px; +} + +.tooltip-location { + font-family: var(--font-mono); + font-size: 11px; + color: var(--text-secondary); + background: var(--bg-tertiary); + padding: 4px 8px; + border-radius: 4px; + word-break: break-all; +} + +.tooltip-stats { + display: grid; + grid-template-columns: auto 1fr; + gap: 4px 14px; + font-size: 12px; +} + +.tooltip-stat-label { + color: var(--text-secondary); + font-weight: 500; +} + +.tooltip-stat-value { + color: var(--text-primary); + font-weight: 600; +} + +.tooltip-stat-value.accent { + color: var(--accent); +} + +.tooltip-diff { + margin-top: 12px; + padding-top: 12px; + border-top: 1px solid var(--border); +} + +.tooltip-diff-title { + font-size: 11px; + font-weight: 600; + color: var(--accent); + margin-bottom: 8px; +} + +.tooltip-diff-row { + display: grid; + grid-template-columns: auto 1fr; + gap: 4px 14px; + font-size: 12px; + margin-bottom: 4px; +} + +.tooltip-diff-row.regression .tooltip-stat-value { + color: var(--diff-regression-deep); + font-weight: 700; +} + +.tooltip-diff-row.improvement .tooltip-stat-value { + color: var(--diff-improvement-deep); + font-weight: 700; +} + +.tooltip-diff-row.neutral .tooltip-stat-value { + color: var(--text-secondary); +} + +.tooltip-source { + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid var(--border); +} + +.tooltip-source-title { + font-size: 11px; + font-weight: 600; + color: var(--accent); + margin-bottom: 6px; +} + +.tooltip-source-code { + font-family: var(--font-mono); + font-size: 10px; + line-height: 1.5; + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: 6px; + padding: 8px; + max-height: 140px; + overflow-y: auto; + white-space: pre-wrap; + word-break: break-all; +} + +.tooltip-source-line { + color: var(--text-secondary); + padding: 1px 0; +} + +.tooltip-source-line.current { + color: var(--accent); + font-weight: 600; +} + +.tooltip-hint { + margin-top: 10px; + padding-top: 8px; + border-top: 1px solid var(--border); + font-size: 11px; + color: var(--text-muted); + text-align: center; +} + +/* -------------------------------------------------------------------------- + Tooltip Bytecode/Opcode Section + -------------------------------------------------------------------------- */ + +.tooltip-opcodes { + margin-top: 16px; + padding-top: 12px; + border-top: 1px solid var(--border); +} + +.tooltip-opcodes-title { + color: var(--accent); + font-size: 13px; + margin-bottom: 8px; + font-weight: 600; +} + +.tooltip-opcodes-list { + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: 6px; + padding: 10px; +} + +.tooltip-opcode-row { + display: grid; + grid-template-columns: 1fr 60px 60px; + gap: 8px; + align-items: center; + padding: 3px 0; +} + +.tooltip-opcode-name { + font-family: var(--font-mono); + font-size: 11px; + color: var(--text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.tooltip-opcode-name.specialized { + color: var(--spec-high-text); +} + +.tooltip-opcode-base-hint { + color: var(--text-muted); + font-size: 11px; + margin-left: 4px; +} + +.tooltip-opcode-badge { + background: var(--spec-high); + color: white; + font-size: 9px; + padding: 1px 4px; + border-radius: 3px; + margin-left: 4px; +} + +.tooltip-opcode-count { + text-align: right; + font-size: 11px; + color: var(--text-secondary); +} + +.tooltip-opcode-bar { + background: var(--bg-secondary); + border-radius: 2px; + height: 8px; + overflow: hidden; +} + +.tooltip-opcode-bar-fill { + background: linear-gradient(90deg, var(--python-blue), var(--python-blue-light)); + height: 100%; +} + +/* -------------------------------------------------------------------------- + Responsive (Flamegraph-specific) + -------------------------------------------------------------------------- */ + +@media (max-width: 900px) { + .sidebar { + position: fixed; + left: 0; + top: var(--topbar-height); + bottom: var(--statusbar-height); + z-index: 100; + box-shadow: var(--shadow-lg); + } + + .sidebar.collapsed { + width: var(--sidebar-collapsed); + } + + .search-wrapper { + max-width: 220px; + } +} + +@media (max-width: 600px) { + .search-wrapper { + max-width: 160px; + } + + .brand-info { + display: none; + } +} + +/* -------------------------------------------------------------------------- + Flamegraph Root Node Styling + -------------------------------------------------------------------------- */ + +/* Style the root node - no border, themed text */ +.d3-flame-graph g:first-of-type rect { + stroke: none; +} + +.d3-flame-graph g:first-of-type .d3-flame-graph-label { + color: var(--text-muted); +} + +/* -------------------------------------------------------------------------- + Flamegraph-Specific Toggle Override + -------------------------------------------------------------------------- */ + +#toggle-invert .toggle-track.on, +#toggle-elided .toggle-track.on, +#toggle-path-display .toggle-track.on { + background: #8e44ad; + border-color: #8e44ad; + box-shadow: 0 0 8px rgba(142, 68, 173, 0.3); +} + +.toggle-switch:focus-visible { + border-radius: 4px; +} diff --git a/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js new file mode 100644 index 00000000000000..840acf2c27d120 --- /dev/null +++ b/Lib/profiling/sampling/_flamegraph_assets/flamegraph.js @@ -0,0 +1,1519 @@ +const EMBEDDED_DATA = {{FLAMEGRAPH_DATA}}; + +// Global string table for resolving string indices +let stringTable = []; +let normalData = null; +let invertedData = null; +let currentThreadFilter = 'all'; +let isInverted = false; +let useModuleNames = true; +let zoomedNodeValue = null; + +// Heat colors are now defined in CSS variables (--heat-1 through --heat-8) +// and automatically switch with theme changes - no JS color arrays needed! + +// Opcode mappings - loaded from embedded data (generated by Python) +let OPCODE_NAMES = {}; +let DEOPT_MAP = {}; + +// Initialize opcode mappings from embedded data +function initOpcodeMapping(data) { + if (data && data.opcode_mapping) { + OPCODE_NAMES = data.opcode_mapping.names || {}; + DEOPT_MAP = data.opcode_mapping.deopt || {}; + } +} + +// Get opcode info from opcode number +function getOpcodeInfo(opcode) { + const opname = OPCODE_NAMES[opcode] || `<${opcode}>`; + const baseOpcode = DEOPT_MAP[opcode]; + const isSpecialized = baseOpcode !== undefined; + const baseOpname = isSpecialized ? (OPCODE_NAMES[baseOpcode] || `<${baseOpcode}>`) : opname; + + return { + opname: opname, + baseOpname: baseOpname, + isSpecialized: isSpecialized + }; +} + +// ============================================================================ +// String Resolution +// ============================================================================ + +function resolveString(index, table = stringTable) { + if (index === null || index === undefined) { + return null; + } + if (typeof index === 'number' && index >= 0 && index < table.length) { + return table[index]; + } + return String(index); +} + +function resolveStringIndices(node, table) { + if (!node) return node; + + const resolved = { ...node }; + + if (typeof resolved.name === 'number') { + resolved.name = resolveString(resolved.name, table); + } + if (typeof resolved.filename === 'number') { + resolved.filename = resolveString(resolved.filename, table); + } + if (typeof resolved.funcname === 'number') { + resolved.funcname = resolveString(resolved.funcname, table); + } + if (typeof resolved.module === 'number') { + resolved.module = resolveString(resolved.module, table); + } + if (typeof resolved.label === 'number') { + resolved.label = resolveString(resolved.label, table); + } + + if (Array.isArray(resolved.source)) { + resolved.source = resolved.source.map(index => + typeof index === 'number' ? resolveString(index, table) : index + ); + } + + if (Array.isArray(resolved.children)) { + resolved.children = resolved.children.map(child => resolveStringIndices(child, table)); + } + + return resolved; +} + +// Escape HTML special characters +function escapeHtml(str) { + return str.replace(/&/g, "&").replace(//g, ">"); +} + +// Get display path based on user preference (module or full path) +function getDisplayName(moduleName, filename) { + if (useModuleNames) { + return moduleName || filename; + } + return filename; +} + +function selectFlamegraphData() { + const baseData = isShowingElided ? elidedFlamegraphData : normalData; + + if (!isInverted) { + return baseData; + } + + if (isShowingElided) { + if (!invertedElidedData) { + invertedElidedData = generateInvertedFlamegraph(baseData); + } + return invertedElidedData; + } + + if (!invertedData) { + invertedData = generateInvertedFlamegraph(baseData); + } + return invertedData; +} + +function updateFlamegraphView() { + const selectedData = selectFlamegraphData(); + const selectedThreadId = currentThreadFilter !== 'all' ? parseInt(currentThreadFilter, 10) : null; + const filteredData = selectedThreadId !== null ? filterDataByThread(selectedData, selectedThreadId) : selectedData; + const tooltip = createPythonTooltip(filteredData); + const chart = createFlamegraph(tooltip, filteredData.value, filteredData); + renderFlamegraph(chart, filteredData); + populateThreadStats(selectedData, selectedThreadId); +} + +// ============================================================================ +// Theme & UI Controls +// ============================================================================ + +function toggleTheme() { + toggleAndSaveTheme(); + + // Re-render flamegraph with new theme colors + if (window.flamegraphData && normalData) { + updateFlamegraphView(); + } +} + +function toggleSidebar() { + const sidebar = document.getElementById('sidebar'); + if (sidebar) { + const isCollapsing = !sidebar.classList.contains('collapsed'); + + if (isCollapsing) { + // Save current width before collapsing + const currentWidth = sidebar.offsetWidth; + sidebar.dataset.expandedWidth = currentWidth; + localStorage.setItem('flamegraph-sidebar-width', currentWidth); + } else { + // Restore width when expanding + const savedWidth = sidebar.dataset.expandedWidth || localStorage.getItem('flamegraph-sidebar-width'); + if (savedWidth) { + sidebar.style.width = savedWidth + 'px'; + } + } + + sidebar.classList.toggle('collapsed'); + localStorage.setItem('flamegraph-sidebar', sidebar.classList.contains('collapsed') ? 'collapsed' : 'expanded'); + + // Resize chart after sidebar animation + setTimeout(() => { + resizeChart(); + }, 300); + } +} + +function resizeChart() { + if (window.flamegraphChart && window.flamegraphData) { + const chartArea = document.querySelector('.chart-area'); + if (chartArea) { + window.flamegraphChart.width(chartArea.clientWidth - 32); + d3.select("#chart").datum(window.flamegraphData).call(window.flamegraphChart); + } + } +} + +function toggleSection(sectionId) { + const section = document.getElementById(sectionId); + if (section) { + section.classList.toggle('collapsed'); + // Save state + const collapsedSections = JSON.parse(localStorage.getItem('flamegraph-collapsed-sections') || '{}'); + collapsedSections[sectionId] = section.classList.contains('collapsed'); + localStorage.setItem('flamegraph-collapsed-sections', JSON.stringify(collapsedSections)); + } +} + +// Restore theme from localStorage, or use browser preference +function restoreUIState() { + applyTheme(getPreferredTheme()); + + // Restore sidebar state + const savedSidebar = localStorage.getItem('flamegraph-sidebar'); + if (savedSidebar === 'collapsed') { + const sidebar = document.getElementById('sidebar'); + if (sidebar) sidebar.classList.add('collapsed'); + } + + // Restore sidebar width + const savedWidth = localStorage.getItem('flamegraph-sidebar-width'); + if (savedWidth) { + const sidebar = document.getElementById('sidebar'); + if (sidebar) { + sidebar.style.width = savedWidth + 'px'; + } + } + + // Restore collapsed sections + const collapsedSections = JSON.parse(localStorage.getItem('flamegraph-collapsed-sections') || '{}'); + for (const [sectionId, isCollapsed] of Object.entries(collapsedSections)) { + if (isCollapsed) { + const section = document.getElementById(sectionId); + if (section) section.classList.add('collapsed'); + } + } +} + +// ============================================================================ +// Logo/Favicon Setup +// ============================================================================ + +function setupLogos() { + const logo = document.querySelector('.sidebar-logo-img img'); + if (!logo) return; + + const navbarLogoContainer = document.getElementById('navbar-logo'); + if (navbarLogoContainer) { + const navbarLogo = logo.cloneNode(true); + navbarLogoContainer.appendChild(navbarLogo); + } + + const favicon = document.createElement('link'); + favicon.rel = 'icon'; + favicon.type = 'image/png'; + favicon.href = logo.src; + document.head.appendChild(favicon); +} + +// ============================================================================ +// Status Bar +// ============================================================================ + +function updateStatusBar(nodeData, rootValue) { + const funcname = resolveString(nodeData.funcname) || resolveString(nodeData.name) || "--"; + const filename = resolveString(nodeData.filename) || ""; + const moduleName = resolveString(nodeData.module) || ""; + const lineno = nodeData.lineno; + const timeMs = (nodeData.value / 1000).toFixed(2); + const percent = rootValue > 0 ? ((nodeData.value / rootValue) * 100).toFixed(1) : "0.0"; + + const brandEl = document.getElementById('status-brand'); + const taglineEl = document.getElementById('status-tagline'); + if (brandEl) brandEl.style.display = 'none'; + if (taglineEl) taglineEl.style.display = 'none'; + + const locationEl = document.getElementById('status-location'); + const funcItem = document.getElementById('status-func-item'); + const timeItem = document.getElementById('status-time-item'); + const percentItem = document.getElementById('status-percent-item'); + + if (locationEl) locationEl.style.display = filename && filename !== "~" ? 'flex' : 'none'; + if (funcItem) funcItem.style.display = 'flex'; + if (timeItem) timeItem.style.display = 'flex'; + if (percentItem) percentItem.style.display = 'flex'; + + const fileEl = document.getElementById('status-file'); + if (fileEl && filename && filename !== "~") { + const displayName = getDisplayName(moduleName, filename); + fileEl.textContent = lineno ? `${displayName}:${lineno}` : displayName; + } + + const funcEl = document.getElementById('status-func'); + if (funcEl) funcEl.textContent = funcname.length > 40 ? funcname.substring(0, 37) + '...' : funcname; + + const timeEl = document.getElementById('status-time'); + if (timeEl) timeEl.textContent = `${timeMs} ms`; + + const percentEl = document.getElementById('status-percent'); + if (percentEl) percentEl.textContent = `${percent}%`; +} + +function clearStatusBar() { + const ids = ['status-location', 'status-func-item', 'status-time-item', 'status-percent-item']; + ids.forEach(id => { + const el = document.getElementById(id); + if (el) el.style.display = 'none'; + }); + + const brandEl = document.getElementById('status-brand'); + const taglineEl = document.getElementById('status-tagline'); + if (brandEl) brandEl.style.display = 'flex'; + if (taglineEl) taglineEl.style.display = 'flex'; +} + +// ============================================================================ +// Tooltip +// ============================================================================ + +function createPythonTooltip(data) { + const pythonTooltip = flamegraph.tooltip.defaultFlamegraphTooltip(); + + pythonTooltip.show = function (d, element) { + if (!this._tooltip) { + this._tooltip = d3.select("body") + .append("div") + .attr("class", "python-tooltip") + .style("opacity", 0); + } + + const timeMs = (d.data.value / 1000).toFixed(2); + const selfSamples = d.data.self || 0; + const selfMs = (selfSamples / 1000).toFixed(2); + const percentage = ((d.data.value / data.value) * 100).toFixed(2); + const relativePercentage = Math.min(100, ((d.data.value / (zoomedNodeValue ?? data.value)) * 100)).toFixed(2); + const calls = d.data.calls || 0; + const childCount = d.children ? d.children.length : 0; + const source = d.data.source; + + const funcname = resolveString(d.data.funcname) || resolveString(d.data.name); + const filename = resolveString(d.data.filename) || ""; + const moduleName = resolveString(d.data.module) || ""; + const displayName = escapeHtml(useModuleNames ? (moduleName || filename) : filename); + const isSpecialFrame = filename === "~"; + + // Build source section + let sourceSection = ""; + if (source && Array.isArray(source) && source.length > 0) { + const sourceLines = source + .map((line) => { + const isCurrent = line.startsWith("→"); + const escaped = escapeHtml(line); + return `
    ${escaped}
    `; + }) + .join(""); + + sourceSection = ` +
    +
    Source Code:
    +
    ${sourceLines}
    +
    `; + } + + // Create bytecode/opcode section if available + let opcodeSection = ""; + const opcodes = d.data.opcodes; + if (opcodes && typeof opcodes === 'object' && Object.keys(opcodes).length > 0) { + // Sort opcodes by sample count (descending) + const sortedOpcodes = Object.entries(opcodes) + .sort((a, b) => b[1] - a[1]) + .slice(0, 8); // Limit to top 8 + + const totalOpcodeSamples = sortedOpcodes.reduce((sum, [, count]) => sum + count, 0); + const maxCount = sortedOpcodes[0][1] || 1; + + const opcodeLines = sortedOpcodes.map(([opcode, count]) => { + const opcodeInfo = getOpcodeInfo(parseInt(opcode, 10)); + const pct = ((count / totalOpcodeSamples) * 100).toFixed(1); + const barWidth = (count / maxCount) * 100; + const specializedBadge = opcodeInfo.isSpecialized + ? 'SPECIALIZED' + : ''; + const baseOpHint = opcodeInfo.isSpecialized + ? `(${opcodeInfo.baseOpname})` + : ''; + const nameClass = opcodeInfo.isSpecialized + ? 'tooltip-opcode-name specialized' + : 'tooltip-opcode-name'; + + return ` +
    +
    + ${opcodeInfo.opname}${baseOpHint}${specializedBadge} +
    +
    ${count.toLocaleString()} (${pct}%)
    +
    +
    +
    +
    `; + }).join(''); + + opcodeSection = ` +
    +
    Bytecode Instructions:
    +
    + ${opcodeLines} +
    +
    `; + } + + const fileLocationHTML = isSpecialFrame ? "" : ` +
    ${displayName}${d.data.lineno ? ":" + d.data.lineno : ""}
    `; + + // Differential stats section + let diffSection = ""; + if (d.data.diff !== undefined && d.data.baseline !== undefined) { + const baselineSelf = (d.data.baseline / 1000).toFixed(2); + const currentSelf = ((d.data.self_time || 0) / 1000).toFixed(2); + const diffMs = (d.data.diff / 1000).toFixed(2); + const diffPct = d.data.diff_pct; + const sign = d.data.diff >= 0 ? "+" : ""; + const diffClass = d.data.diff > 0 ? "regression" : (d.data.diff < 0 ? "improvement" : "neutral"); + + diffSection = ` +
    +
    Self-Time Comparison:
    +
    + Baseline Self: + ${baselineSelf} ms +
    +
    + Current Self: + ${currentSelf} ms +
    +
    + Difference: + ${sign}${diffMs} ms (${sign}${diffPct.toFixed(1)}%) +
    +
    `; + } + + const tooltipHTML = ` +
    +
    ${funcname}
    + ${fileLocationHTML} +
    +
    + Total Time: + ${timeMs} ms + + ${selfSamples > 0 ? ` + Self Time: + ${selfMs} ms + ` : ''} + + Percentage: + ${percentage}% + + ${relativePercentage != percentage && relativePercentage != "100.00" ? ` + Relative Percentage: + ${relativePercentage}% + ` : ''} + + ${calls > 0 ? ` + Function Calls: + ${calls.toLocaleString()} + ` : ''} + + ${childCount > 0 ? ` + Child Functions: + ${childCount} + ` : ''} +
    + ${diffSection} + ${sourceSection} + ${opcodeSection} +
    + ${childCount > 0 ? "Click to zoom into this function" : "Leaf function - no children"} +
    + `; + + // Position tooltip + const event = d3.event || window.event; + const mouseX = event.pageX || event.clientX; + const mouseY = event.pageY || event.clientY; + const padding = 12; + + this._tooltip.html(tooltipHTML); + + // Measure tooltip + const node = this._tooltip.style("display", "block").style("opacity", 0).node(); + const tooltipWidth = node.offsetWidth || 320; + const tooltipHeight = node.offsetHeight || 200; + + // Calculate position + let left = mouseX + padding; + let top = mouseY + padding; + + if (left + tooltipWidth > window.innerWidth) { + left = mouseX - tooltipWidth - padding; + if (left < 0) left = padding; + } + + if (top + tooltipHeight > window.innerHeight) { + top = mouseY - tooltipHeight - padding; + if (top < 0) top = padding; + } + + this._tooltip + .style("left", left + "px") + .style("top", top + "px") + .transition() + .duration(150) + .style("opacity", 1); + + // Update status bar + updateStatusBar(d.data, data.value); + }; + + pythonTooltip.hide = function () { + if (this._tooltip) { + this._tooltip.transition().duration(150).style("opacity", 0); + } + clearStatusBar(); + }; + + return pythonTooltip; +} + +// ============================================================================ +// Flamegraph Creation +// ============================================================================ + +function ensureLibraryLoaded() { + if (typeof flamegraph === "undefined") { + console.error("d3-flame-graph library not loaded"); + document.getElementById("chart").innerHTML = + '
    Error: d3-flame-graph library failed to load
    '; + throw new Error("d3-flame-graph library failed to load"); + } +} + +const HEAT_THRESHOLDS = [ + [0.6, 8], + [0.35, 7], + [0.18, 6], + [0.12, 5], + [0.06, 4], + [0.03, 3], + [0.01, 2], +]; + +function getHeatLevel(percentage) { + for (const [threshold, level] of HEAT_THRESHOLDS) { + if (percentage >= threshold) return level; + } + return 1; +} + +function getHeatColors() { + const style = getComputedStyle(document.documentElement); + const colors = {}; + for (let i = 1; i <= 8; i++) { + colors[i] = style.getPropertyValue(`--heat-${i}`).trim(); + } + return colors; +} + +function getDiffColors() { + const style = getComputedStyle(document.documentElement); + return { + elided: style.getPropertyValue('--diff-elided').trim(), + new: style.getPropertyValue('--diff-new').trim(), + neutral: style.getPropertyValue('--diff-neutral').trim(), + regressionDeep: style.getPropertyValue('--diff-regression-deep').trim(), + regressionMedium: style.getPropertyValue('--diff-regression-medium').trim(), + regressionLight: style.getPropertyValue('--diff-regression-light').trim(), + regressionVerylight: style.getPropertyValue('--diff-regression-verylight').trim(), + improvementDeep: style.getPropertyValue('--diff-improvement-deep').trim(), + improvementMedium: style.getPropertyValue('--diff-improvement-medium').trim(), + improvementLight: style.getPropertyValue('--diff-improvement-light').trim(), + improvementVerylight: style.getPropertyValue('--diff-improvement-verylight').trim(), + }; +} + +function getDiffColorForNode(node, diffColors) { + if (isShowingElided) { + return diffColors.elided; + } + + const diff_pct = node.data.diff_pct || 0; + const diff_samples = node.data.diff || 0; + const self_time = node.data.self_time || 0; + + if (diff_pct === 100 && self_time > 0 && Math.abs(diff_samples - self_time) < 0.1) { + return diffColors.new; + } + + // Neutral zone: small percentage change + if (Math.abs(diff_pct) < 15) { + return diffColors.neutral; + } + + // Regression (red scale) + if (diff_pct > 0) { + if (diff_pct >= 100) return diffColors.regressionDeep; + if (diff_pct > 50) return diffColors.regressionMedium; + if (diff_pct > 30) return diffColors.regressionLight; + return diffColors.regressionVerylight; + } + + // Improvement (blue scale) + if (diff_pct <= -100) return diffColors.improvementDeep; + if (diff_pct < -50) return diffColors.improvementMedium; + if (diff_pct < -30) return diffColors.improvementLight; + return diffColors.improvementVerylight; +} + +function createFlamegraph(tooltip, rootValue, data) { + const chartArea = document.querySelector('.chart-area'); + const width = chartArea ? chartArea.clientWidth - 32 : window.innerWidth - 320; + const heatColors = getHeatColors(); + + const isDifferential = data && data.stats && data.stats.is_differential; + const diffColors = isDifferential ? getDiffColors() : null; + + let chart = flamegraph() + .width(width) + .cellHeight(20) + .transitionDuration(300) + .minFrameSize(1) + .tooltip(tooltip) + .inverted(true) + .getName(d => resolveString(useModuleNames ? d.data.label : d.data.name) || resolveString(d.data.name) || '') + .setColorMapper(function (d) { + if (d.depth === 0) return 'transparent'; + + if (isDifferential) { + return getDiffColorForNode(d, diffColors); + } + + const percentage = d.data.value / rootValue; + const level = getHeatLevel(percentage); + return heatColors[level]; + }) + .onClick(function (d) { + zoomedNodeValue = d.data.value; + }); + + return chart; +} + +function renderFlamegraph(chart, data) { + d3.select("#chart").datum(data).call(chart); + window.flamegraphChart = chart; + window.flamegraphData = data; + zoomedNodeValue = null; + populateStats(data); +} + +// ============================================================================ +// Search +// ============================================================================ + +function updateSearchHighlight(searchTerm, searchInput) { + d3.selectAll("#chart rect") + .classed("search-match", false) + .classed("search-dim", false); + + // Clear active state from all hotspots + document.querySelectorAll('.hotspot').forEach(h => h.classList.remove('active')); + + if (searchTerm && searchTerm.length > 0) { + let matchCount = 0; + + d3.selectAll("#chart rect").each(function (d) { + if (d && d.data) { + const name = resolveString(d.data.name) || ""; + const funcname = resolveString(d.data.funcname) || ""; + const filename = resolveString(d.data.filename) || ""; + const moduleName = resolveString(d.data.module) || ""; + const displayName = getDisplayName(moduleName, filename); + const lineno = d.data.lineno; + const term = searchTerm.toLowerCase(); + + // Check if search term looks like path:line pattern + const fileLineMatch = term.match(/^(.+):(\d+)$/); + let matches = false; + + if (fileLineMatch) { + const searchFile = fileLineMatch[1]; + const searchLine = parseInt(fileLineMatch[2], 10); + matches = displayName.toLowerCase().includes(searchFile) && lineno === searchLine; + } else { + // Regular substring search + matches = + name.toLowerCase().includes(term) || + funcname.toLowerCase().includes(term) || + displayName.toLowerCase().includes(term); + } + + if (matches) { + matchCount++; + d3.select(this).classed("search-match", true); + } else { + d3.select(this).classed("search-dim", true); + } + } + }); + + if (searchInput) { + searchInput.classList.remove("has-matches", "no-matches"); + searchInput.classList.add(matchCount > 0 ? "has-matches" : "no-matches"); + } + + // Mark matching hotspot as active + document.querySelectorAll('.hotspot').forEach(h => { + if (h.dataset.searchterm && h.dataset.searchterm.toLowerCase() === searchTerm.toLowerCase()) { + h.classList.add('active'); + } + }); + } else if (searchInput) { + searchInput.classList.remove("has-matches", "no-matches"); + } +} + +function searchForHotspot(funcname) { + const searchInput = document.getElementById('search-input'); + const searchWrapper = document.querySelector('.search-wrapper'); + if (searchInput) { + // Toggle: if already searching for this term, clear it + if (searchInput.value.trim() === funcname) { + clearSearch(); + } else { + searchInput.value = funcname; + if (searchWrapper) { + searchWrapper.classList.add('has-value'); + } + performSearch(); + } + } +} + +function initSearchHandlers() { + const searchInput = document.getElementById("search-input"); + const searchWrapper = document.querySelector(".search-wrapper"); + if (!searchInput) return; + + let searchTimeout; + function performSearch() { + const term = searchInput.value.trim(); + updateSearchHighlight(term, searchInput); + // Toggle has-value class for clear button visibility + if (searchWrapper) { + searchWrapper.classList.toggle("has-value", term.length > 0); + } + } + + searchInput.addEventListener("input", function () { + clearTimeout(searchTimeout); + searchTimeout = setTimeout(performSearch, 150); + }); + + window.performSearch = performSearch; +} + +function clearSearch() { + const searchInput = document.getElementById("search-input"); + const searchWrapper = document.querySelector(".search-wrapper"); + if (searchInput) { + searchInput.value = ""; + searchInput.classList.remove("has-matches", "no-matches"); + if (searchWrapper) { + searchWrapper.classList.remove("has-value"); + } + // Clear highlights + d3.selectAll("#chart rect") + .classed("search-match", false) + .classed("search-dim", false); + // Clear active hotspot + document.querySelectorAll('.hotspot').forEach(h => h.classList.remove('active')); + } +} + +// ============================================================================ +// Resize Handler +// ============================================================================ + +function handleResize() { + let resizeTimeout; + window.addEventListener("resize", function () { + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(resizeChart, 100); + }); +} + +function initSidebarResize() { + const sidebar = document.getElementById('sidebar'); + const resizeHandle = document.getElementById('sidebar-resize-handle'); + if (!sidebar || !resizeHandle) return; + + let isResizing = false; + let startX = 0; + let startWidth = 0; + const minWidth = 200; + const maxWidth = 600; + + resizeHandle.addEventListener('mousedown', function(e) { + isResizing = true; + startX = e.clientX; + startWidth = sidebar.offsetWidth; + resizeHandle.classList.add('resizing'); + document.body.classList.add('resizing-sidebar'); + e.preventDefault(); + }); + + document.addEventListener('mousemove', function(e) { + if (!isResizing) return; + + const deltaX = e.clientX - startX; + const newWidth = Math.min(Math.max(startWidth + deltaX, minWidth), maxWidth); + sidebar.style.width = newWidth + 'px'; + e.preventDefault(); + }); + + document.addEventListener('mouseup', function() { + if (isResizing) { + isResizing = false; + resizeHandle.classList.remove('resizing'); + document.body.classList.remove('resizing-sidebar'); + + // Save the new width + const width = sidebar.offsetWidth; + localStorage.setItem('flamegraph-sidebar-width', width); + + // Resize chart after sidebar resize + setTimeout(() => { + resizeChart(); + }, 10); + } + }); +} + +// ============================================================================ +// Thread Stats +// ============================================================================ + +// Mode constants (must match constants.py) +const PROFILING_MODE_WALL = 0; +const PROFILING_MODE_CPU = 1; +const PROFILING_MODE_GIL = 2; +const PROFILING_MODE_ALL = 3; + +function populateThreadStats(data, selectedThreadId = null) { + const stats = data?.stats; + if (!stats || !stats.thread_stats) { + return; + } + + const mode = stats.mode !== undefined ? stats.mode : PROFILING_MODE_WALL; + let threadStats; + + if (selectedThreadId !== null && stats.per_thread_stats && stats.per_thread_stats[selectedThreadId]) { + threadStats = stats.per_thread_stats[selectedThreadId]; + } else { + threadStats = stats.thread_stats; + } + + if (!threadStats || typeof threadStats.total !== 'number' || threadStats.total <= 0) { + return; + } + + const section = document.getElementById('thread-stats-bar'); + if (!section) { + return; + } + + section.style.display = 'block'; + + const gilHeldStat = document.getElementById('gil-held-stat'); + const gilReleasedStat = document.getElementById('gil-released-stat'); + const gilWaitingStat = document.getElementById('gil-waiting-stat'); + + if (mode === PROFILING_MODE_GIL) { + // In GIL mode, hide GIL-related stats + if (gilHeldStat) gilHeldStat.style.display = 'none'; + if (gilReleasedStat) gilReleasedStat.style.display = 'none'; + if (gilWaitingStat) gilWaitingStat.style.display = 'none'; + } else { + // Show all stats + if (gilHeldStat) gilHeldStat.style.display = 'block'; + if (gilReleasedStat) gilReleasedStat.style.display = 'block'; + if (gilWaitingStat) gilWaitingStat.style.display = 'block'; + + const gilHeldPct = threadStats.has_gil_pct || 0; + const gilHeldPctElem = document.getElementById('gil-held-pct'); + if (gilHeldPctElem) gilHeldPctElem.textContent = `${gilHeldPct.toFixed(1)}%`; + const gilHeldFill = document.getElementById('gil-held-fill'); + if (gilHeldFill) gilHeldFill.style.width = `${gilHeldPct}%`; + + // GIL Released = not holding GIL and not waiting for it + const gilReleasedPct = Math.max(0, 100 - (threadStats.has_gil_pct || 0) - (threadStats.gil_requested_pct || 0)); + const gilReleasedPctElem = document.getElementById('gil-released-pct'); + if (gilReleasedPctElem) gilReleasedPctElem.textContent = `${gilReleasedPct.toFixed(1)}%`; + const gilReleasedFill = document.getElementById('gil-released-fill'); + if (gilReleasedFill) gilReleasedFill.style.width = `${gilReleasedPct}%`; + + const gilWaitingPct = threadStats.gil_requested_pct || 0; + const gilWaitingPctElem = document.getElementById('gil-waiting-pct'); + if (gilWaitingPctElem) gilWaitingPctElem.textContent = `${gilWaitingPct.toFixed(1)}%`; + const gilWaitingFill = document.getElementById('gil-waiting-fill'); + if (gilWaitingFill) gilWaitingFill.style.width = `${gilWaitingPct}%`; + } + + const gcPct = threadStats.gc_pct || 0; + const gcPctElem = document.getElementById('gc-pct'); + if (gcPctElem) gcPctElem.textContent = `${gcPct.toFixed(1)}%`; + const gcFill = document.getElementById('gc-fill'); + if (gcFill) gcFill.style.width = `${gcPct}%`; + + // Exception stats + const excPct = threadStats.has_exception_pct || 0; + const excPctElem = document.getElementById('exc-pct'); + if (excPctElem) excPctElem.textContent = `${excPct.toFixed(1)}%`; + const excFill = document.getElementById('exc-fill'); + if (excFill) excFill.style.width = `${excPct}%`; +} + +// ============================================================================ +// Profile Summary Stats +// ============================================================================ + +function formatNumber(num) { + if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M'; + if (num >= 1000) return (num / 1000).toFixed(1) + 'K'; + return num.toLocaleString(); +} + +function formatDuration(seconds) { + if (seconds >= 3600) { + const h = Math.floor(seconds / 3600); + const m = Math.floor((seconds % 3600) / 60); + return `${h}h ${m}m`; + } + if (seconds >= 60) { + const m = Math.floor(seconds / 60); + const s = Math.floor(seconds % 60); + return `${m}m ${s}s`; + } + return seconds.toFixed(2) + 's'; +} + +function populateProfileSummary(data) { + const stats = data.stats || {}; + const totalSamples = stats.total_samples || data.value || 0; + const duration = stats.duration_sec || 0; + const sampleRate = stats.sample_rate || (duration > 0 ? totalSamples / duration : 0); + const errorRate = stats.error_rate || 0; + const missedSamples= stats.missed_samples || 0; + + const samplesEl = document.getElementById('stat-total-samples'); + if (samplesEl) samplesEl.textContent = formatNumber(totalSamples); + + const durationEl = document.getElementById('stat-duration'); + if (durationEl) durationEl.textContent = duration > 0 ? formatDuration(duration) : '--'; + + const rateEl = document.getElementById('stat-sample-rate'); + if (rateEl) rateEl.textContent = sampleRate > 0 ? formatNumber(Math.round(sampleRate)) : '--'; + + // Count unique functions + // Use normal (non-inverted) tree structure, but respect thread filtering + const uniqueFunctions = new Set(); + function collectUniqueFunctions(node) { + if (!node) return; + const filename = resolveString(node.filename) || 'unknown'; + const funcname = resolveString(node.funcname) || resolveString(node.name) || 'unknown'; + const lineno = node.lineno || 0; + const key = `${filename}|${lineno}|${funcname}`; + uniqueFunctions.add(key); + if (node.children) node.children.forEach(collectUniqueFunctions); + } + // In inverted mode, use normalData (with thread filter if active) + // In normal mode, use the passed data (already has thread filter applied if any) + let functionCountSource; + if (!normalData) { + functionCountSource = data; + } else if (isInverted) { + if (currentThreadFilter !== 'all') { + functionCountSource = filterDataByThread(normalData, parseInt(currentThreadFilter)); + } else { + functionCountSource = normalData; + } + } else { + functionCountSource = data; + } + collectUniqueFunctions(functionCountSource); + + const functionsEl = document.getElementById('stat-functions'); + if (functionsEl) functionsEl.textContent = formatNumber(uniqueFunctions.size); + + // Efficiency bar + if (errorRate !== undefined && errorRate !== null) { + const efficiency = Math.max(0, Math.min(100, (100 - errorRate))); + + const efficiencySection = document.getElementById('efficiency-section'); + if (efficiencySection) efficiencySection.style.display = 'block'; + + const efficiencyValue = document.getElementById('stat-efficiency'); + if (efficiencyValue) efficiencyValue.textContent = efficiency.toFixed(1) + '%'; + + const efficiencyFill = document.getElementById('efficiency-fill'); + if (efficiencyFill) efficiencyFill.style.width = efficiency + '%'; + } + // MissedSamples bar + if (missedSamples !== undefined && missedSamples !== null) { + const sampleEfficiency = Math.max(0, missedSamples); + + const efficiencySection = document.getElementById('efficiency-section'); + if (efficiencySection) efficiencySection.style.display = 'block'; + + const sampleEfficiencyValue = document.getElementById('stat-missed-samples'); + if (sampleEfficiencyValue) sampleEfficiencyValue.textContent = sampleEfficiency.toFixed(1) + '%'; + + const sampleEfficiencyFill = document.getElementById('missed-samples-fill'); + if (sampleEfficiencyFill) sampleEfficiencyFill.style.width = sampleEfficiency + '%'; + } +} + +// ============================================================================ +// Elided Stacks (Differential) +// ============================================================================ + +let elidedFlamegraphData = null; +let invertedElidedData = null; +let isShowingElided = false; + +function setupElidedToggle(data) { + const stats = data.stats || {}; + const elidedCount = stats.elided_count || 0; + const elidedFlamegraph = stats.elided_flamegraph; + + if (!elidedCount || !elidedFlamegraph) { + return; + } + + elidedFlamegraphData = resolveStringIndices(elidedFlamegraph, elidedFlamegraph.strings); + + const toggleElided = document.getElementById('toggle-elided'); + if (toggleElided) { + toggleElided.style.display = 'flex'; + + toggleElided.onclick = function() { + isShowingElided = !isShowingElided; + updateToggleUI('toggle-elided', isShowingElided); + updateFlamegraphView(); + }; + } +} + +// ============================================================================ +// Hotspot Stats +// ============================================================================ + +function populateStats(data) { + // Populate profile summary + populateProfileSummary(data); + + // Populate thread statistics if available + populateThreadStats(data); + + // Setup elided stacks toggle if this is a differential flamegraph + setupElidedToggle(data); + + // For hotspots: use normal (non-inverted) tree structure, but respect thread filtering. + // In inverted view, the tree structure changes but the hottest functions remain the same. + // However, if a thread filter is active, we need to show that thread's hotspots. + let hotspotSource; + if (!normalData) { + hotspotSource = data; + } else if (isInverted) { + // In inverted mode, use normalData (with thread filter if active) + if (currentThreadFilter !== 'all') { + hotspotSource = filterDataByThread(normalData, parseInt(currentThreadFilter)); + } else { + hotspotSource = normalData; + } + } else { + // In normal mode, use the passed data (already has thread filter applied if any) + hotspotSource = data; + } + const totalSamples = hotspotSource.value || 0; + + const functionMap = new Map(); + + function collectFunctions(node) { + if (!node) return; + + let filename = resolveString(node.filename); + let funcname = resolveString(node.funcname); + let moduleName = resolveString(node.module); + + if (!filename || !funcname) { + const nameStr = resolveString(node.name); + if (nameStr?.includes('(')) { + const match = nameStr.match(/^(.+?)\s*\((.+?):(\d+)\)$/); + if (match) { + funcname = funcname || match[1]; + filename = filename || match[2]; + } + } + } + + filename = filename || 'unknown'; + funcname = funcname || 'unknown'; + moduleName = moduleName || 'unknown'; + + if (filename !== 'unknown' && funcname !== 'unknown' && node.value > 0) { + const directSamples = node.self || 0; + + const funcKey = `${filename}:${node.lineno || '?'}:${funcname}`; + + if (functionMap.has(funcKey)) { + const existing = functionMap.get(funcKey); + existing.directSamples += directSamples; + existing.directPercent = (existing.directSamples / totalSamples) * 100; + if (directSamples > existing.maxSingleSamples) { + existing.filename = filename; + existing.module = moduleName; + existing.lineno = node.lineno || '?'; + existing.maxSingleSamples = directSamples; + } + } else { + functionMap.set(funcKey, { + filename: filename, + module: moduleName, + lineno: node.lineno || '?', + funcname: funcname, + directSamples, + directPercent: (directSamples / totalSamples) * 100, + maxSingleSamples: directSamples + }); + } + } + + if (node.children) { + node.children.forEach(child => collectFunctions(child)); + } + } + + collectFunctions(hotspotSource); + + const hotSpots = Array.from(functionMap.values()) + .filter(f => f.directPercent > 0.5) + .sort((a, b) => b.directPercent - a.directPercent) + .slice(0, 3); + + // Populate and animate hotspot cards + for (let i = 0; i < 3; i++) { + const num = i + 1; + const card = document.getElementById(`hotspot-${num}`); + const funcEl = document.getElementById(`hotspot-func-${num}`); + const fileEl = document.getElementById(`hotspot-file-${num}`); + const percentEl = document.getElementById(`hotspot-percent-${num}`); + const samplesEl = document.getElementById(`hotspot-samples-${num}`); + + if (i < hotSpots.length && hotSpots[i]) { + const h = hotSpots[i]; + const filename = h.filename || 'unknown'; + const lineno = h.lineno ?? '?'; + const moduleName = h.module || 'unknown'; + const isSpecialFrame = filename === '~' && (lineno === 0 || lineno === '?'); + + let funcDisplay = h.funcname || 'unknown'; + if (funcDisplay.length > 28) funcDisplay = funcDisplay.substring(0, 25) + '...'; + + if (funcEl) funcEl.textContent = funcDisplay; + if (fileEl) { + if (isSpecialFrame) { + fileEl.textContent = '--'; + } else { + const displayName = getDisplayName(moduleName, filename); + fileEl.textContent = `${displayName}:${lineno}`; + } + } + if (percentEl) percentEl.textContent = `${h.directPercent.toFixed(1)}%`; + if (samplesEl) samplesEl.textContent = ` (${h.directSamples.toLocaleString()})`; + } else { + if (funcEl) funcEl.textContent = '--'; + if (fileEl) fileEl.textContent = '--'; + if (percentEl) percentEl.textContent = '--'; + if (samplesEl) samplesEl.textContent = ''; + } + + // Add click handler and animate entrance + if (card) { + if (i < hotSpots.length && hotSpots[i]) { + const h = hotSpots[i]; + const moduleName = h.module || 'unknown'; + const filename = h.filename || 'unknown'; + const displayName = getDisplayName(moduleName, filename); + const hasValidLocation = displayName !== 'unknown' && h.lineno !== '?'; + const searchTerm = hasValidLocation ? `${displayName}:${h.lineno}` : h.funcname; + card.dataset.searchterm = searchTerm; + card.onclick = () => searchForHotspot(searchTerm); + card.style.cursor = 'pointer'; + } else { + card.onclick = null; + delete card.dataset.searchterm; + card.style.cursor = 'default'; + } + + setTimeout(() => { + card.classList.add('visible'); + }, 100 + i * 80); + } + } +} + +// ============================================================================ +// Thread Filter +// ============================================================================ + +function initThreadFilter(data) { + const threadFilter = document.getElementById('thread-filter'); + const threadSection = document.getElementById('thread-section'); + + if (!threadFilter || !data.threads) return; + + threadFilter.innerHTML = ''; + + const threads = data.threads || []; + threads.forEach(threadId => { + const option = document.createElement('option'); + option.value = threadId; + option.textContent = `Thread ${threadId}`; + threadFilter.appendChild(option); + }); + + if (threads.length > 1 && threadSection) { + threadSection.style.display = 'block'; + } +} + +function filterByThread() { + const threadFilter = document.getElementById('thread-filter'); + if (!threadFilter || !normalData) return; + + const selectedThread = threadFilter.value; + currentThreadFilter = selectedThread; + + updateFlamegraphView(); +} + +function filterDataByThread(data, threadId) { + function filterNode(node) { + if (!node.threads || !node.threads.includes(threadId)) { + return null; + } + + const filteredNode = { ...node, children: [] }; + + if (node.children && Array.isArray(node.children)) { + filteredNode.children = node.children + .map(child => filterNode(child)) + .filter(child => child !== null); + } + + return filteredNode; + } + + function recalculateValue(node) { + if (!node.children || node.children.length === 0) { + return node.value || 0; + } + const childrenValue = node.children.reduce((sum, child) => sum + recalculateValue(child), 0); + node.value = Math.max(node.value || 0, childrenValue); + return node.value; + } + + const filteredRoot = { ...data, children: [] }; + + if (data.children && Array.isArray(data.children)) { + filteredRoot.children = data.children + .map(child => filterNode(child)) + .filter(child => child !== null); + } + + recalculateValue(filteredRoot); + return filteredRoot; +} + +// ============================================================================ +// Control Functions +// ============================================================================ + +function resetZoom() { + if (window.flamegraphChart) { + zoomedNodeValue = null; + window.flamegraphChart.resetZoom(); + } +} + +function exportSVG() { + const svgElement = document.querySelector("#chart svg"); + if (!svgElement) { + console.warn("Cannot export: No flamegraph SVG found"); + return; + } + const serializer = new XMLSerializer(); + const svgString = serializer.serializeToString(svgElement); + const blob = new Blob([svgString], { type: "image/svg+xml" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = "python-performance-flamegraph.svg"; + a.click(); + URL.revokeObjectURL(url); +} + +// ============================================================================ +// Inverted Flamegraph +// ============================================================================ + +// Example: "file.py|10|foo" or "~|0|" for special frames +function getInvertNodeKey(node) { + return `${node.filename || '~'}|${node.lineno || 0}|${node.funcname || node.name}`; +} + +function accumulateInvertedNode(parent, stackFrame, leaf, isDifferential) { + const key = getInvertNodeKey(stackFrame); + + if (!parent.children[key]) { + const newNode = { + name: stackFrame.name, + label: stackFrame.label, + value: 0, + self: 0, + children: {}, + filename: stackFrame.filename, + module: stackFrame.module, + lineno: stackFrame.lineno, + funcname: stackFrame.funcname, + source: stackFrame.source, + opcodes: null, + threads: new Set() + }; + + if (isDifferential) { + newNode.baseline = 0; + newNode.baseline_total = 0; + newNode.self_time = 0; + newNode.diff = 0; + newNode.diff_pct = 0; + } + + parent.children[key] = newNode; + } + + const node = parent.children[key]; + node.value += leaf.value; + node.self += stackFrame.self || 0; + if (leaf.threads) { + leaf.threads.forEach(t => node.threads.add(t)); + } + if (stackFrame.opcodes) { + if (!node.opcodes) { + node.opcodes = { ...stackFrame.opcodes }; + } else { + for (const [op, count] of Object.entries(stackFrame.opcodes)) { + node.opcodes[op] = (node.opcodes[op] || 0) + count; + } + } + } + + if (isDifferential) { + node.baseline += stackFrame.baseline || 0; + node.baseline_total += stackFrame.baseline_total || 0; + node.self_time += stackFrame.self_time || 0; + node.diff += stackFrame.diff || 0; + + if (node.baseline > 0) { + node.diff_pct = (node.diff / node.baseline) * 100.0; + } else if (node.self_time > 0) { + node.diff_pct = 100.0; + } + } + + return node; +} + +function processLeaf(invertedRoot, path, leafNode, isDifferential) { + if (!path || path.length === 0) { + return; + } + + let invertedParent = accumulateInvertedNode(invertedRoot, leafNode, leafNode, isDifferential); + + // Walk backwards through the call stack + for (let i = path.length - 2; i >= 0; i--) { + invertedParent = accumulateInvertedNode(invertedParent, path[i], leafNode, isDifferential); + } +} + +function traverseInvert(path, currentNode, invertedRoot, isDifferential) { + const selfValue = currentNode.self || 0; + + if (selfValue > 0) { + processLeaf(invertedRoot, path, { ...currentNode, value: selfValue }, isDifferential); + } + + const children = currentNode.children || []; + children.forEach(child => traverseInvert(path.concat([child]), child, invertedRoot, isDifferential)); +} + +function convertInvertDictToArray(node) { + if (node.threads instanceof Set) { + node.threads = Array.from(node.threads).sort((a, b) => a - b); + } + + const children = node.children; + if (children && typeof children === 'object' && !Array.isArray(children)) { + node.children = Object.values(children); + node.children.sort((a, b) => b.value - a.value || a.name.localeCompare(b.name)); + node.children.forEach(convertInvertDictToArray); + } + return node; +} + +function generateInvertedFlamegraph(data) { + const isDifferential = data && data.stats && data.stats.is_differential; + + const invertedRoot = { + name: data.name, + label: data.label, + value: data.value, + children: {}, + stats: data.stats, + threads: data.threads + }; + + const children = data.children || []; + if (children.length === 0) { + // Single-frame tree: the root is its own leaf + processLeaf(invertedRoot, [data], data, isDifferential); + } else { + children.forEach(child => traverseInvert([child], child, invertedRoot, isDifferential)); + } + + convertInvertDictToArray(invertedRoot); + return invertedRoot; +} + +function toggleInvert() { + isInverted = !isInverted; + updateToggleUI('toggle-invert', isInverted); + updateFlamegraphView(); +} + +function togglePathDisplay() { + useModuleNames = !useModuleNames; + updateToggleUI('toggle-path-display', useModuleNames); + updateFlamegraphView(); +} + +// ============================================================================ +// Initialization +// ============================================================================ + +function initFlamegraph() { + ensureLibraryLoaded(); + restoreUIState(); + setupLogos(); + + if (EMBEDDED_DATA.strings) { + stringTable = EMBEDDED_DATA.strings; + normalData = resolveStringIndices(EMBEDDED_DATA, EMBEDDED_DATA.strings); + } else { + normalData = EMBEDDED_DATA; + } + + // Initialize opcode mapping from embedded data + initOpcodeMapping(EMBEDDED_DATA); + + // Inverted data will be built on first toggle + invertedData = null; + + initThreadFilter(normalData); + + // Toggle legend based on differential mode + const isDifferential = normalData && normalData.stats && normalData.stats.is_differential; + const heatmapLegend = document.getElementById('heatmap-legend-section'); + const diffLegend = document.getElementById('diff-legend-section'); + if (isDifferential) { + if (heatmapLegend) heatmapLegend.style.display = 'none'; + if (diffLegend) diffLegend.style.display = 'block'; + } else { + if (heatmapLegend) heatmapLegend.style.display = 'block'; + if (diffLegend) diffLegend.style.display = 'none'; + } + + const tooltip = createPythonTooltip(normalData); + const chart = createFlamegraph(tooltip, normalData.value, normalData); + renderFlamegraph(chart, normalData); + initSearchHandlers(); + initSidebarResize(); + handleResize(); + + const toggleInvertBtn = document.getElementById('toggle-invert'); + if (toggleInvertBtn) { + toggleInvertBtn.addEventListener('click', toggleInvert); + } + + const togglePathDisplayBtn = document.getElementById('toggle-path-display'); + if (togglePathDisplayBtn) { + togglePathDisplayBtn.addEventListener('click', togglePathDisplay); + } +} + +// Keyboard shortcut: Enter/Space activates toggle switches +document.addEventListener('keydown', function(e) { + if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') { + return; + } + if ((e.key === 'Enter' || e.key === ' ') && e.target.classList.contains('toggle-switch')) { + e.preventDefault(); + e.target.click(); + } +}); + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", initFlamegraph); +} else { + initFlamegraph(); +} diff --git a/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html b/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html new file mode 100644 index 00000000000000..f1c5bb0300679a --- /dev/null +++ b/Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html @@ -0,0 +1,464 @@ + + + + + + {{TITLE}} + + + + + + + +
    + +
    +
    + + Tachyon + + {{SUBTITLE}} +
    +
    + + +
    +
    + + + + + + + + +
    +
    + + +
    + + + + +
    +
    +
    +
    + + +
    + + Tachyon Profiler + + + Python Sampling Profiler + + + + + +
    +
    + + + + diff --git a/Lib/profiling/sampling/_format_utils.py b/Lib/profiling/sampling/_format_utils.py new file mode 100644 index 00000000000000..237a4f4186bf20 --- /dev/null +++ b/Lib/profiling/sampling/_format_utils.py @@ -0,0 +1,5 @@ +import locale + + +def fmt(value: int | float, decimals: int = 1) -> str: + return locale.format_string(f'%.{decimals}f', value, grouping=True) diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap.css b/Lib/profiling/sampling/_heatmap_assets/heatmap.css new file mode 100644 index 00000000000000..8f7f034ba7e596 --- /dev/null +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap.css @@ -0,0 +1,1555 @@ +/* ========================================================================== + Heatmap Viewer - Component-Specific CSS + + DEPENDENCY: Requires _shared_assets/base.css to be loaded first + This file extends the shared foundation with heatmap-specific styles. + ========================================================================== */ + +/* Heatmap heat colors - using base.css colors with 60% opacity */ +[data-theme="dark"] { + --heat-1: rgba(90, 123, 167, 0.60); + --heat-2: rgba(106, 148, 168, 0.60); + --heat-3: rgba(122, 172, 172, 0.60); + --heat-4: rgba(142, 196, 152, 0.60); + --heat-5: rgba(168, 216, 136, 0.60); + --heat-6: rgba(200, 222, 122, 0.60); + --heat-7: rgba(244, 212, 93, 0.60); + --heat-8: rgba(255, 122, 69, 0.60); +} + +/* -------------------------------------------------------------------------- + Layout Overrides (Heatmap-specific) + -------------------------------------------------------------------------- */ + +.app-layout { + min-height: 100vh; +} + +/* Sticky top bar for heatmap views */ +.top-bar { + position: sticky; + top: 0; + z-index: 100; +} + +/* -------------------------------------------------------------------------- + Main Content Area + -------------------------------------------------------------------------- */ + +.main-content { + flex: 1; + padding: 24px 3%; + width: 100%; + max-width: 100%; +} + +/* -------------------------------------------------------------------------- + Stats Summary Cards - Enhanced with Icons & Animations + -------------------------------------------------------------------------- */ + +.stats-summary { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12px; + margin-bottom: 24px; +} + +.stat-card { + display: flex; + align-items: center; + gap: 12px; + background: var(--bg-primary); + border: 2px solid var(--border); + border-radius: 10px; + padding: 14px 16px; + transition: all var(--transition-fast); + animation: slideUp 0.5s ease-out backwards; + animation-delay: calc(var(--i, 0) * 0.08s); + position: relative; + overflow: hidden; +} + +.stat-card:nth-child(1) { --i: 0; --card-color: var(--card-blue); } +.stat-card:nth-child(2) { --i: 1; --card-color: var(--card-green); } +.stat-card:nth-child(3) { --i: 2; --card-color: var(--card-yellow); } +.stat-card:nth-child(4) { --i: 3; --card-color: var(--card-purple); } + +.stat-card:hover { + border-color: rgba(var(--card-color), 0.6); + background: linear-gradient(135deg, rgba(var(--card-color), 0.08) 0%, var(--bg-primary) 100%); + transform: translateY(-2px); + box-shadow: 0 4px 16px rgba(var(--card-color), 0.15); +} + +.stat-icon { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + background: linear-gradient(135deg, rgba(var(--card-color), 0.15) 0%, rgba(var(--card-color), 0.05) 100%); + border: 1px solid rgba(var(--card-color), 0.2); + border-radius: 10px; + flex-shrink: 0; + transition: all var(--transition-fast); +} + +.stat-card:hover .stat-icon { + transform: scale(1.05) rotate(-2deg); + background: linear-gradient(135deg, rgba(var(--card-color), 0.25) 0%, rgba(var(--card-color), 0.1) 100%); +} + +.stat-data { + flex: 1; + min-width: 0; +} + +.stat-value { + font-family: var(--font-mono); + font-size: 1.35em; + font-weight: 800; + color: rgb(var(--card-color)); + display: block; + line-height: 1.1; + letter-spacing: -0.3px; +} + +.stat-label { + font-size: 10px; + font-weight: 600; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.3px; + margin-top: 2px; +} + +/* Sparkline decoration for stats */ +.stat-sparkline { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 30px; + opacity: 0.1; + background: linear-gradient(180deg, + transparent 0%, + rgba(var(--card-color), 0.3) 100% + ); + pointer-events: none; +} + +/* -------------------------------------------------------------------------- + Rate Cards (Error Rate, Missed Samples) with Progress Bars + -------------------------------------------------------------------------- */ + +.rate-card { + display: flex; + flex-direction: column; + gap: 12px; + background: var(--bg-primary); + border: 2px solid var(--border); + border-radius: 12px; + padding: 18px 20px; + transition: all var(--transition-fast); + animation: slideUp 0.5s ease-out backwards; + position: relative; + overflow: hidden; +} + +.rate-card:nth-child(5) { animation-delay: 0.32s; --rate-color: var(--card-red); } +.rate-card:nth-child(6) { animation-delay: 0.40s; --rate-color: var(--card-orange); } + +.rate-card:hover { + border-color: rgba(var(--rate-color), 0.5); + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(var(--rate-color), 0.15); +} + +.rate-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.rate-info { + display: flex; + align-items: center; + gap: 10px; +} + +.rate-icon { + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + background: linear-gradient(135deg, rgba(var(--rate-color), 0.15) 0%, rgba(var(--rate-color), 0.05) 100%); + border: 1px solid rgba(var(--rate-color), 0.2); + border-radius: 10px; + flex-shrink: 0; +} + +.rate-label { + font-size: 12px; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.3px; +} + +.rate-value { + font-family: var(--font-mono); + font-size: 1.4em; + font-weight: 800; + color: rgb(var(--rate-color)); +} + +.rate-bar { + height: 8px; + background: var(--bg-tertiary); + border-radius: 4px; + overflow: hidden; + position: relative; +} + +.rate-fill { + height: 100%; + border-radius: 4px; + transition: width 0.8s ease-out; + position: relative; + overflow: hidden; +} + +.rate-fill.error { + background: linear-gradient(90deg, #dc3545 0%, #ff6b6b 100%); +} + +.rate-fill.warning { + background: linear-gradient(90deg, #ff9800 0%, #ffc107 100%); +} + +.rate-fill.good { + background: linear-gradient(90deg, #28a745 0%, #20c997 100%); +} + +/* Shimmer animation on rate bars */ +.rate-fill::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 255, 255, 0.4) 50%, + transparent 100% + ); + animation: shimmer 2.5s ease-in-out infinite; +} + +/* -------------------------------------------------------------------------- + Section Headers + -------------------------------------------------------------------------- */ + +.section-header { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 16px; + padding-bottom: 12px; + border-bottom: 2px solid var(--python-gold); +} + +.section-title { + font-size: 18px; + font-weight: 700; + color: var(--text-primary); + margin: 0; + flex: 1; +} + +/* -------------------------------------------------------------------------- + Filter Controls + -------------------------------------------------------------------------- */ + +.filter-controls { + display: flex; + gap: 8px; + flex-wrap: wrap; + align-items: center; + margin-bottom: 16px; +} + +.control-btn { + padding: 8px 16px; + background: var(--bg-secondary); + color: var(--text-primary); + border: 1px solid var(--border); + border-radius: 6px; + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: all var(--transition-fast); +} + +.control-btn:hover { + background: var(--accent); + color: white; + border-color: var(--accent); +} + +/* -------------------------------------------------------------------------- + Type Sections (stdlib, project, etc) + -------------------------------------------------------------------------- */ + +.type-section { + background: var(--bg-primary); + border: 1px solid var(--border); + border-radius: 8px; + overflow: hidden; + margin-bottom: 12px; +} + +.type-header { + padding: 12px 16px; + background: var(--header-gradient); + color: white; + cursor: pointer; + display: flex; + align-items: center; + gap: 10px; + user-select: none; + transition: all var(--transition-fast); + font-weight: 600; +} + +.type-header:hover { + opacity: 0.95; +} + +.type-icon { + font-size: 12px; + transition: transform var(--transition-fast); + min-width: 12px; +} + +.type-title { + font-size: 14px; + flex: 1; +} + +.type-stats { + font-size: 12px; + opacity: 0.9; + background: rgba(255, 255, 255, 0.15); + padding: 4px 10px; + border-radius: 4px; + font-family: var(--font-mono); +} + +.type-content { + padding: 12px; +} + +/* -------------------------------------------------------------------------- + Folder Nodes (hierarchical structure) + -------------------------------------------------------------------------- */ + +.folder-node { + margin-bottom: 6px; +} + +.folder-header { + padding: 8px 12px; + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 6px; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + user-select: none; + transition: all var(--transition-fast); +} + +.folder-header:hover { + background: var(--accent-glow); + border-color: var(--accent); +} + +.folder-icon { + font-size: 10px; + color: var(--accent); + transition: transform var(--transition-fast); + min-width: 12px; +} + +.folder-name { + flex: 1; + font-weight: 500; + color: var(--text-primary); + font-size: 13px; +} + +.folder-stats { + font-size: 11px; + color: var(--text-secondary); + background: var(--bg-tertiary); + padding: 2px 8px; + border-radius: 4px; + font-family: var(--font-mono); +} + +.folder-content { + padding-left: 20px; + margin-top: 6px; +} + +/* -------------------------------------------------------------------------- + File Items + -------------------------------------------------------------------------- */ + +.files-list { + display: flex; + flex-direction: column; + gap: 4px; + margin-top: 8px; +} + +.file-item { + display: flex; + align-items: center; + gap: 12px; + padding: 8px 12px; + background: var(--bg-primary); + border: 1px solid var(--border-subtle); + border-radius: 6px; + transition: all var(--transition-fast); +} + +.file-item:hover { + background: var(--bg-secondary); + border-color: var(--border); +} + +.file-item .file-link { + flex: 1; + min-width: 0; + font-size: 13px; +} + +.file-samples { + font-size: 12px; + color: var(--text-secondary); + font-weight: 600; + white-space: nowrap; + width: 130px; + flex-shrink: 0; + text-align: right; + font-family: var(--font-mono); +} + +.heatmap-bar-container { + width: 120px; + flex-shrink: 0; + display: flex; + align-items: center; +} + +.heatmap-bar { + flex-shrink: 0; + border-radius: 2px; +} + +/* Links */ +.file-link { + color: var(--accent); + text-decoration: none; + font-weight: 500; + transition: color var(--transition-fast); +} + +.file-link:hover { + color: var(--accent-hover); + text-decoration: underline; +} + +/* -------------------------------------------------------------------------- + Module Badges + -------------------------------------------------------------------------- */ + +.module-badge { + display: inline-block; + padding: 3px 8px; + border-radius: 4px; + font-size: 11px; + font-weight: 600; +} + +.badge-stdlib { + background: rgba(40, 167, 69, 0.15); + color: #28a745; +} + +.badge-site-packages { + background: rgba(0, 123, 255, 0.15); + color: #007bff; +} + +.badge-project { + background: rgba(255, 193, 7, 0.2); + color: #d39e00; +} + +.badge-other { + background: var(--bg-tertiary); + color: var(--text-secondary); +} + +[data-theme="dark"] .badge-stdlib { + background: rgba(40, 167, 69, 0.25); + color: #5dd879; +} + +[data-theme="dark"] .badge-site-packages { + background: rgba(88, 166, 255, 0.25); + color: #79b8ff; +} + +[data-theme="dark"] .badge-project { + background: rgba(255, 212, 59, 0.25); + color: #ffd43b; +} + +/* ========================================================================== + FILE VIEW STYLES (Code Display) + ========================================================================== */ + +.code-view { + font-family: var(--font-mono); + min-height: 100vh; +} + +/* Code Header (Top Bar for file view) */ +.code-header { + height: var(--topbar-height); + background: var(--header-gradient); + display: flex; + align-items: center; + padding: 0 16px; + gap: 16px; + box-shadow: 0 2px 10px rgba(55, 118, 171, 0.25); + border-bottom: 2px solid var(--python-gold); + position: sticky; + top: 0; + z-index: 100; +} + +.code-header-content { + display: flex; + align-items: center; + justify-content: space-between; + width: 94%; + max-width: 100%; + margin: 0 auto; +} + +.code-header h1 { + font-size: 14px; + font-weight: 600; + color: white; + margin: 0; + font-family: var(--font-mono); + display: flex; + align-items: center; + gap: 8px; +} + +/* File Stats Bar */ +.file-stats { + background: var(--bg-secondary); + padding: 16px 24px; + border-bottom: 1px solid var(--border); +} + +.file-stats .stats-grid { + width: 94%; + max-width: 100%; + margin: 0 auto; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + gap: 12px; +} + +.stat-item { + background: var(--bg-primary); + padding: 12px; + border-radius: 8px; + box-shadow: var(--shadow-sm); + text-align: center; + border: 1px solid var(--border); + transition: all var(--transition-fast); +} + +.stat-item:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-md); + border-color: var(--accent); +} + +.stat-item .stat-value { + font-size: 1.4em; + font-weight: 700; + color: var(--accent); +} + +.stat-item .stat-label { + color: var(--text-muted); + font-size: 10px; + margin-top: 2px; +} + +/* Legend */ +.legend { + background: var(--bg-secondary); + padding: 12px 24px; + border-bottom: 1px solid var(--border); +} + +.legend-content { + width: 100%; + display: flex; + align-items: center; + gap: 20px; + flex-wrap: nowrap; +} + +.legend-separator { + width: 1px; + height: 24px; + background: var(--border); + flex-shrink: 0; +} + +.legend-title { + font-weight: 600; + color: var(--text-primary); + font-size: 13px; + font-family: var(--font-sans); + flex-shrink: 0; +} + +.legend-gradient { + width: 150px; + flex-shrink: 0; + height: 20px; + background: linear-gradient(90deg, + var(--bg-tertiary) 0%, + var(--heat-2) 25%, + var(--heat-4) 50%, + var(--heat-6) 75%, + var(--heat-8) 100% + ); + border-radius: 4px; + border: 1px solid var(--border); +} + +.legend-labels { + display: flex; + gap: 12px; + font-size: 11px; + color: var(--text-muted); + font-family: var(--font-sans); + flex-shrink: 0; +} + +/* Legend Controls Group - wraps toggles and bytecode button together */ +.legend-controls { + display: flex; + align-items: center; + gap: 20px; + flex-shrink: 0; + margin-left: auto; +} + +/* Heatmap-Specific Toggle Overrides */ +#toggle-color-mode .toggle-track.on { + background: #8e44ad; + border-color: #8e44ad; + box-shadow: 0 0 8px rgba(142, 68, 173, 0.3); +} + +#toggle-cold .toggle-track.on { + background: #e67e22; + border-color: #e67e22; + box-shadow: 0 0 8px rgba(230, 126, 34, 0.3); +} + +/* Code Container */ +.code-container { + width: 94%; + max-width: 100%; + margin: 16px auto; + background: var(--bg-primary); + border: 1px solid var(--border); + border-radius: 8px 8px 8px 8px; + box-shadow: var(--shadow-sm); + /* Allow horizontal scroll for long lines, but don't clip sticky header */ +} + +/* Code Header Row */ +.code-header-row { + position: sticky; + top: var(--topbar-height); + z-index: 50; + display: flex; + background: var(--bg-secondary); + border-bottom: 2px solid var(--border); + font-weight: 700; + font-size: 11px; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; + border-radius: 8px 8px 0 0; +} + +.header-line-number { + flex-shrink: 0; + width: 60px; + padding: 8px 10px; + text-align: right; + border-right: 1px solid var(--border); +} + +.header-samples-self, +.header-samples-cumulative { + flex-shrink: 0; + width: 90px; + padding: 8px 10px; + text-align: right; + border-right: 1px solid var(--border); +} + +.header-samples-self { + color: var(--heat-8); +} + +.header-samples-cumulative { + color: var(--accent); +} + +.header-content { + flex: 1; + padding: 8px 15px; +} + +/* Code Lines */ +.code-line { + position: relative; + display: flex; + min-height: 20px; + line-height: 20px; + font-size: 13px; + transition: background var(--transition-fast); + scroll-margin-top: calc(var(--topbar-height) + 50px); +} + +.code-line:hover { + filter: brightness(0.97); +} + +[data-theme="dark"] .code-line:hover { + filter: brightness(1.1); +} + +.line-number { + flex-shrink: 0; + width: 60px; + padding: 0 10px; + text-align: right; + color: var(--text-muted); + background: var(--bg-secondary); + border-right: 1px solid var(--border); + user-select: none; + transition: all var(--transition-fast); +} + +.line-number:hover { + background: var(--accent); + color: white; + cursor: pointer; +} + +.line-samples-self, +.line-samples-cumulative { + flex-shrink: 0; + width: 90px; + padding: 0 10px; + text-align: right; + background: var(--bg-secondary); + border-right: 1px solid var(--border); + font-weight: 600; + user-select: none; + font-size: 12px; +} + +.line-samples-self { + color: var(--heat-8); +} + +.line-samples-cumulative { + color: var(--accent); +} + +.line-content { + flex: 1; + padding: 0 15px; + white-space: pre; + overflow-x: auto; +} + +/* Scrollbar Styling */ +.line-content::-webkit-scrollbar { + height: 6px; +} + +.line-content::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 3px; +} + +.line-content::-webkit-scrollbar-thumb:hover { + background: var(--text-muted); +} + +/* Navigation Buttons */ +.line-nav-buttons { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + display: flex; + gap: 4px; + align-items: center; +} + +.nav-btn { + padding: 2px 6px; + font-size: 12px; + font-weight: 500; + border: 1px solid var(--accent); + border-radius: 4px; + background: var(--bg-primary); + color: var(--accent); + cursor: pointer; + transition: all var(--transition-fast); + user-select: none; + line-height: 1; +} + +.nav-btn:hover:not(:disabled) { + background: var(--accent); + color: white; + transform: translateY(-1px); + box-shadow: var(--shadow-sm); +} + +.nav-btn:active:not(:disabled) { + transform: translateY(0); +} + +.nav-btn:disabled { + opacity: 0.3; + cursor: not-allowed; + color: var(--text-muted); + background: var(--bg-secondary); + border-color: var(--border); +} + +.nav-btn.caller { + color: var(--nav-caller); + border-color: var(--nav-caller); +} + +.nav-btn.callee { + color: var(--nav-callee); + border-color: var(--nav-callee); +} + +.nav-btn.caller:hover:not(:disabled) { + background: var(--nav-caller-hover); + color: white; +} + +.nav-btn.callee:hover:not(:disabled) { + background: var(--nav-callee-hover); + color: white; +} + +/* Highlighted target line */ +.code-line:target { + animation: highlight-line 2s ease-out; +} + +@keyframes highlight-line { + 0% { + background: rgba(255, 212, 59, 0.6) !important; + outline: 3px solid var(--python-gold); + outline-offset: -3px; + } + 50% { + background: rgba(255, 212, 59, 0.5) !important; + outline: 3px solid var(--python-gold); + outline-offset: -3px; + } + 100% { + outline: 3px solid transparent; + outline-offset: -3px; + } +} + +/* Popup menu for multiple callees */ +.callee-menu { + position: absolute; + background: var(--bg-primary); + border: 1px solid var(--border); + border-radius: 8px; + box-shadow: var(--shadow-lg); + padding: 8px; + z-index: 1000; + min-width: 250px; + max-width: 400px; + max-height: 300px; + overflow-y: auto; +} + +.callee-menu-header { + font-weight: 600; + color: var(--text-primary); + margin-bottom: 8px; + padding-bottom: 8px; + border-bottom: 1px solid var(--border); + font-size: 13px; + font-family: var(--font-sans); +} + +.callee-menu-item { + padding: 8px; + margin: 4px 0; + border-radius: 6px; + cursor: pointer; + transition: background var(--transition-fast); + display: flex; + flex-direction: column; + gap: 4px; +} + +.callee-menu-item:hover { + background: var(--bg-secondary); +} + +.callee-menu-func { + font-weight: 500; + color: var(--accent); + font-size: 12px; +} + +.callee-menu-file { + font-size: 11px; + color: var(--text-muted); +} + +.count-badge { + display: inline-block; + background: var(--accent); + color: white; + font-size: 10px; + padding: 2px 6px; + border-radius: 4px; + font-weight: 600; + margin-left: 6px; +} + +/* Callee menu scrollbar */ +.callee-menu::-webkit-scrollbar { + width: 6px; +} + +.callee-menu::-webkit-scrollbar-track { + background: var(--bg-secondary); + border-radius: 3px; +} + +.callee-menu::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 3px; +} + +/* -------------------------------------------------------------------------- + Scroll Minimap Marker + -------------------------------------------------------------------------- */ + +#scroll_marker { + position: fixed; + z-index: 1000; + right: 0; + top: 0; + width: 12px; + height: 100%; + background: var(--bg-secondary); + border-left: 1px solid var(--border); + pointer-events: none; +} + +#scroll_marker .marker { + position: absolute; + min-height: 3px; + width: 100%; + pointer-events: none; +} + +#scroll_marker .marker.cold { + background: var(--heat-1); +} + +#scroll_marker .marker.cool { + background: var(--heat-2); +} + +#scroll_marker .marker.mild { + background: var(--heat-3); +} + +#scroll_marker .marker.warm { + background: var(--heat-4); +} + +#scroll_marker .marker.hot { + background: var(--heat-5); +} + +#scroll_marker .marker.very-hot { + background: var(--heat-6); +} + +#scroll_marker .marker.intense { + background: var(--heat-7); +} + +#scroll_marker .marker.extreme { + background: var(--heat-8); +} + +/* -------------------------------------------------------------------------- + Responsive (Heatmap-specific) + -------------------------------------------------------------------------- */ + +@media (max-width: 1100px) { + .stats-summary { + grid-template-columns: repeat(2, 1fr); + } + + .legend-content { + flex-wrap: wrap; + justify-content: center; + } + + .legend-controls { + margin-left: 0; + } +} + +@media (max-width: 900px) { + .main-content { + padding: 16px; + } +} + +@media (max-width: 600px) { + .stats-summary { + grid-template-columns: 1fr; + } + + .file-stats .stats-grid { + grid-template-columns: repeat(2, 1fr); + } + + .legend-content { + flex-direction: column; + align-items: center; + gap: 12px; + } + + .legend-gradient { + width: 100%; + max-width: none; + } + + .legend-separator { + width: 80%; + height: 1px; + } + + .legend-controls { + flex-direction: column; + gap: 12px; + } + + .legend-controls .toggle-switch { + justify-content: center; + } + + .legend-controls .toggle-switch .toggle-label:first-child { + width: 70px; + text-align: right; + } + + .legend-controls .toggle-switch .toggle-label:last-child { + width: 90px; + text-align: left; + } + + /* Compact code columns on small screens */ + .header-line-number, + .line-number { + width: 40px; + } + + .header-samples-self, + .header-samples-cumulative, + .line-samples-self, + .line-samples-cumulative { + width: 55px; + font-size: 10px; + } + + /* Adjust padding - headers need vertical, data rows don't */ + .header-line-number, + .header-samples-self, + .header-samples-cumulative { + padding: 8px 4px; + } + + .line-number, + .line-samples-self, + .line-samples-cumulative { + padding: 0 4px; + } + + .bytecode-panel { + margin: 8px 10px 8px 160px; + } +} + +.bytecode-toggle { + flex-shrink: 0; + width: 20px; + height: 20px; + padding: 0; + margin: 0 4px; + border: none; + background: transparent; + color: var(--code-accent); + cursor: pointer; + font-size: 10px; + transition: transform var(--transition-fast), color var(--transition-fast); + display: inline-flex; + align-items: center; + justify-content: center; +} + +.bytecode-toggle:hover { + color: var(--accent); +} + +.bytecode-spacer { + flex-shrink: 0; + width: 20px; + height: 20px; + margin: 0 4px; +} + +.bytecode-panel { + background: var(--bg-primary); + border: 1px solid var(--border); + border-radius: 8px; + box-shadow: var(--shadow-md); + font-family: var(--font-mono); + font-size: 12px; + color: var(--text-primary); + line-height: 1.5; + word-wrap: break-word; + overflow-wrap: break-word; + padding: 0; + margin: 8px 10px 8px 250px; + position: relative; + z-index: 1; + overflow-y: auto; + max-height: 500px; + flex: 1; + transition: padding 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.bytecode-panel.expanded { + padding: 14px; +} + +.bytecode-wrapper { + position: relative; + display: flex; + overflow: visible; + max-height: 0; + opacity: 0; + transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease-in-out; +} + +.bytecode-wrapper.expanded { + max-height: 600px; + opacity: 1; + transition: max-height 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease-in-out; +} + +/* Column backdrop matching table header columns (line/self/total) */ +.bytecode-columns { + display: none; + position: absolute; + left: 0; + overflow: hidden; + pointer-events: none; + z-index: 0; +} + +.bytecode-wrapper.expanded .bytecode-columns { + display: flex; + top: 0; + bottom: 0; +} + +.bytecode-panel::-webkit-scrollbar { + width: 8px; +} + +.bytecode-panel::-webkit-scrollbar-track { + background: var(--bg-secondary); + border-radius: 4px; +} + +.bytecode-panel::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 4px; +} + +.bytecode-panel::-webkit-scrollbar-thumb:hover { + background: var(--text-muted); +} + +/* Specialization summary bar */ +.bytecode-spec-summary { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + margin-bottom: 10px; + border-radius: var(--radius-sm); + background: rgba(100, 100, 100, 0.1); +} + +.bytecode-spec-summary .spec-pct { + font-size: 1.4em; + font-weight: 700; +} + +.bytecode-spec-summary .spec-label { + font-weight: 500; + text-transform: uppercase; + font-size: 0.85em; + letter-spacing: 0.5px; +} + +.bytecode-spec-summary .spec-detail { + color: var(--text-secondary); + font-size: 0.9em; + margin-left: auto; +} + +.bytecode-spec-summary.high { + background: var(--spec-high-bg); + border-left: 3px solid var(--spec-high); +} +.bytecode-spec-summary.high .spec-pct, +.bytecode-spec-summary.high .spec-label { + color: var(--spec-high-text); +} + +.bytecode-spec-summary.medium { + background: var(--spec-medium-bg); + border-left: 3px solid var(--spec-medium); +} +.bytecode-spec-summary.medium .spec-pct, +.bytecode-spec-summary.medium .spec-label { + color: var(--spec-medium-text); +} + +.bytecode-spec-summary.low { + background: var(--spec-low-bg); + border-left: 3px solid var(--spec-low); +} +.bytecode-spec-summary.low .spec-pct, +.bytecode-spec-summary.low .spec-label { + color: var(--spec-low-text); +} + +.bytecode-header { + display: grid; + grid-template-columns: 1fr 80px 80px; + gap: 12px; + padding: 4px 8px; + font-weight: 600; + color: var(--text-secondary); + border-bottom: 1px solid var(--code-border); + margin-bottom: 4px; +} + +.bytecode-expand-all { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + background: var(--bg-secondary); + border: 1px solid var(--code-border); + border-radius: var(--radius-sm); + color: var(--text-secondary); + font-size: 12px; + font-weight: 500; + cursor: pointer; + transition: all var(--transition-fast); + flex-shrink: 0; +} + +.bytecode-expand-all:hover, +.bytecode-expand-all.expanded { + background: var(--accent); + color: white; + border-color: var(--accent); +} + +.bytecode-expand-all .expand-icon { + font-size: 10px; + transition: transform var(--transition-fast); +} + +.bytecode-expand-all.expanded .expand-icon { + transform: rotate(90deg); +} + +/* ======================================== + INSTRUCTION SPAN HIGHLIGHTING + (triggered only from bytecode panel hover) + ======================================== */ + +/* Highlight from bytecode panel hover */ +.instr-span.highlight-from-bytecode { + outline: 3px solid #ff6b6b !important; + background-color: rgba(255, 107, 107, 0.4) !important; + border-radius: 2px; +} + +/* Bytecode instruction row */ +.bytecode-instruction { + display: grid; + grid-template-columns: 1fr 80px 80px; + gap: 12px; + align-items: center; + padding: 4px 8px; + margin: 2px 0; + border-radius: var(--radius-sm); + cursor: pointer; + transition: background-color var(--transition-fast); +} + +.bytecode-instruction:hover, +.bytecode-instruction.highlight { + background-color: rgba(55, 118, 171, 0.15); +} + +.bytecode-instruction[data-locations] { + cursor: pointer; +} + +.bytecode-instruction[data-locations]:hover { + background-color: rgba(255, 107, 107, 0.2); +} + +.bytecode-opname { + font-weight: 600; + font-family: var(--font-mono); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.bytecode-opname.specialized { + color: #2e7d32; +} + +[data-theme="dark"] .bytecode-opname.specialized { + color: #81c784; +} + +.bytecode-opname .base-op { + color: var(--code-text-muted); + font-weight: normal; + font-size: 0.9em; + margin-left: 4px; +} + +.bytecode-samples { + text-align: right; + font-weight: 600; + color: var(--accent); + font-family: var(--font-mono); +} + +.bytecode-samples.hot { + color: #ff6b6b; +} + +.bytecode-heatbar { + width: 60px; + height: 12px; + background: var(--bg-secondary); + border-radius: 2px; + overflow: hidden; + border: 1px solid var(--code-border); +} + +.bytecode-heatbar-fill { + height: 100%; + background: linear-gradient(90deg, #00d4ff 0%, #ff6b00 100%); +} + +.specialization-badge { + display: inline-block; + padding: 1px 6px; + font-size: 0.75em; + background: #e8f5e9; + color: #2e7d32; + border-radius: 3px; + margin-left: 6px; + font-weight: 600; +} + +[data-theme="dark"] .specialization-badge { + background: rgba(129, 199, 132, 0.2); + color: #81c784; +} + +.bytecode-empty { + color: var(--code-text-muted); + font-style: italic; + padding: 8px; +} + +.bytecode-error { + color: #d32f2f; + font-style: italic; + padding: 8px; +} + +/* ======================================== + SPAN TOOLTIPS + ======================================== */ + +.span-tooltip { + position: absolute; + z-index: 10000; + background: var(--bg-primary); + color: var(--text-primary); + padding: 10px 14px; + border-radius: var(--radius-md); + border: 1px solid var(--border); + font-family: var(--font-sans); + font-size: 12px; + box-shadow: var(--shadow-lg); + pointer-events: none; + min-width: 160px; + max-width: 300px; +} + +.span-tooltip::after { + content: ''; + position: absolute; + bottom: -7px; + left: 50%; + transform: translateX(-50%); + border-width: 7px 7px 0; + border-style: solid; + border-color: var(--bg-primary) transparent transparent; + filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.1)); +} + +.span-tooltip-header { + font-weight: 600; + margin-bottom: 8px; + padding-bottom: 6px; + border-bottom: 1px solid var(--border); + color: var(--text-primary); +} + +.span-tooltip-header.hot { + color: #e65100; +} + +.span-tooltip-header.warm { + color: #f59e0b; +} + +.span-tooltip-header.cold { + color: var(--text-muted); +} + +.span-tooltip-row { + display: flex; + justify-content: space-between; + margin: 4px 0; + gap: 16px; +} + +.span-tooltip-label { + color: var(--text-secondary); +} + +.span-tooltip-value { + font-weight: 600; + text-align: right; + color: var(--text-primary); +} + +.span-tooltip-value.highlight { + color: var(--accent); +} + +.span-tooltip-section { + font-weight: 600; + color: var(--text-secondary); + font-size: 11px; + margin-top: 8px; + margin-bottom: 4px; + padding-top: 6px; + border-top: 1px solid var(--border); +} + +.span-tooltip-opcode { + font-family: var(--font-mono); + font-size: 11px; + color: var(--text-primary); + background: var(--bg-secondary); + padding: 3px 8px; + margin: 2px 0; + border-radius: var(--radius-sm); + border-left: 2px solid var(--accent); +} diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap.js b/Lib/profiling/sampling/_heatmap_assets/heatmap.js new file mode 100644 index 00000000000000..1f698779f3a46e --- /dev/null +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap.js @@ -0,0 +1,738 @@ +// Tachyon Profiler - Heatmap JavaScript +// Interactive features for the heatmap visualization +// Aligned with Flamegraph viewer design patterns + +// ============================================================================ +// State Management +// ============================================================================ + +let currentMenu = null; +let colorMode = 'self'; // 'self' or 'cumulative' - default to self +let coldCodeHidden = false; + +// ============================================================================ +// Theme Support +// ============================================================================ + +function toggleTheme() { + toggleAndSaveTheme(); + applyLineColors(); + + // Rebuild scroll marker with new theme colors + buildScrollMarker(); +} + +// ============================================================================ +// Utility Functions +// ============================================================================ + +function createElement(tag, className, textContent = '') { + const el = document.createElement(tag); + if (className) el.className = className; + if (textContent) el.textContent = textContent; + return el; +} + +function calculateMenuPosition(buttonRect, menuWidth, menuHeight) { + const viewport = { width: window.innerWidth, height: window.innerHeight }; + const scroll = { + x: window.pageXOffset || document.documentElement.scrollLeft, + y: window.pageYOffset || document.documentElement.scrollTop + }; + + const left = buttonRect.right + menuWidth + 10 < viewport.width + ? buttonRect.right + scroll.x + 10 + : Math.max(scroll.x + 10, buttonRect.left + scroll.x - menuWidth - 10); + + const top = buttonRect.bottom + menuHeight + 10 < viewport.height + ? buttonRect.bottom + scroll.y + 5 + : Math.max(scroll.y + 10, buttonRect.top + scroll.y - menuHeight - 10); + + return { left, top }; +} + +// ============================================================================ +// Menu Management +// ============================================================================ + +function closeMenu() { + if (currentMenu) { + currentMenu.remove(); + currentMenu = null; + } +} + +function showNavigationMenu(button, items, title) { + closeMenu(); + + const menu = createElement('div', 'callee-menu'); + menu.appendChild(createElement('div', 'callee-menu-header', title)); + + items.forEach(linkData => { + const item = createElement('div', 'callee-menu-item'); + + const funcDiv = createElement('div', 'callee-menu-func'); + funcDiv.textContent = linkData.func; + + if (linkData.count !== undefined && linkData.count > 0) { + const countBadge = createElement('span', 'count-badge'); + countBadge.textContent = linkData.count.toLocaleString(); + countBadge.title = `${linkData.count.toLocaleString()} samples`; + funcDiv.appendChild(document.createTextNode(' ')); + funcDiv.appendChild(countBadge); + } + + item.appendChild(funcDiv); + item.appendChild(createElement('div', 'callee-menu-file', linkData.file)); + item.addEventListener('click', () => navigateToLine(linkData.link)); + menu.appendChild(item); + }); + + const pos = calculateMenuPosition(button.getBoundingClientRect(), 350, 300); + menu.style.left = `${pos.left}px`; + menu.style.top = `${pos.top}px`; + + document.body.appendChild(menu); + currentMenu = menu; +} + +// ============================================================================ +// Navigation +// ============================================================================ + +function handleNavigationClick(button, e) { + e.stopPropagation(); + + const navData = button.getAttribute('data-nav'); + if (navData) { + navigateToLine(JSON.parse(navData).link); + return; + } + + const navMulti = button.getAttribute('data-nav-multi'); + if (navMulti) { + const items = JSON.parse(navMulti); + const title = button.classList.contains('caller') ? 'Choose a caller:' : 'Choose a callee:'; + showNavigationMenu(button, items, title); + } +} + +function restartLineHighlight(target) { + target.style.animation = 'none'; + // Force style recalculation so restoring the animation restarts it. + void target.offsetWidth; + target.style.animation = ''; +} + +function navigateToLine(link) { + const url = new URL(link, window.location.href); + + if (url.href === window.location.href) { + scrollToTargetLine(); + } else { + window.location.href = link; + } +} + +function scrollToTargetLine() { + if (!window.location.hash) return; + const target = document.querySelector(window.location.hash); + if (target) { + target.scrollIntoView({ behavior: 'smooth', block: 'start' }); + restartLineHighlight(target); + } +} + +// ============================================================================ +// Sample Count & Intensity +// ============================================================================ + +function getSampleCount(line) { + let text; + if (colorMode === 'self') { + text = line.querySelector('.line-samples-self')?.textContent.trim().replace(/,/g, ''); + } else { + text = line.querySelector('.line-samples-cumulative')?.textContent.trim().replace(/,/g, ''); + } + return parseInt(text) || 0; +} + +// ============================================================================ +// Scroll Minimap +// ============================================================================ + +function buildScrollMarker() { + const existing = document.getElementById('scroll_marker'); + if (existing) existing.remove(); + + if (document.body.scrollHeight <= window.innerHeight) return; + + const allLines = document.querySelectorAll('.code-line'); + const lines = Array.from(allLines).filter(line => line.style.display !== 'none'); + const markerScale = window.innerHeight / document.body.scrollHeight; + const lineHeight = Math.min(Math.max(3, window.innerHeight / lines.length), 10); + const maxSamples = Math.max(...Array.from(lines, getSampleCount)); + + const scrollMarker = createElement('div', ''); + scrollMarker.id = 'scroll_marker'; + + let prevLine = -99, lastMark, lastTop; + + lines.forEach((line, index) => { + const samples = getSampleCount(line); + if (samples === 0) return; + + const lineTop = Math.floor(line.offsetTop * markerScale); + const lineNumber = index + 1; + const intensityClass = maxSamples > 0 ? (intensityToClass(samples / maxSamples) || 'cold') : 'cold'; + + if (lineNumber === prevLine + 1 && lastMark?.classList.contains(intensityClass)) { + lastMark.style.height = `${lineTop + lineHeight - lastTop}px`; + } else { + lastMark = createElement('div', `marker ${intensityClass}`); + lastMark.style.height = `${lineHeight}px`; + lastMark.style.top = `${lineTop}px`; + scrollMarker.appendChild(lastMark); + lastTop = lineTop; + } + + prevLine = lineNumber; + }); + + document.body.appendChild(scrollMarker); +} + +function applyLineColors() { + const lines = document.querySelectorAll('.code-line'); + lines.forEach(line => { + let intensity; + if (colorMode === 'self') { + intensity = parseFloat(line.getAttribute('data-self-intensity')) || 0; + } else { + intensity = parseFloat(line.getAttribute('data-cumulative-intensity')) || 0; + } + + const color = intensityToColor(intensity); + line.style.background = color; + }); +} + +// ============================================================================ +// Toggle Controls +// ============================================================================ + +function toggleColdCode() { + coldCodeHidden = !coldCodeHidden; + applyHotFilter(); + updateToggleUI('toggle-cold', coldCodeHidden); + buildScrollMarker(); +} + +function applyHotFilter() { + const lines = document.querySelectorAll('.code-line'); + + lines.forEach(line => { + const selfSamples = line.querySelector('.line-samples-self')?.textContent.trim(); + const cumulativeSamples = line.querySelector('.line-samples-cumulative')?.textContent.trim(); + + let isCold; + if (colorMode === 'self') { + isCold = !selfSamples || selfSamples === ''; + } else { + isCold = !cumulativeSamples || cumulativeSamples === ''; + } + + if (isCold) { + line.style.display = coldCodeHidden ? 'none' : 'flex'; + } else { + line.style.display = 'flex'; + } + }); +} + +function toggleColorMode() { + colorMode = colorMode === 'self' ? 'cumulative' : 'self'; + applyLineColors(); + + updateToggleUI('toggle-color-mode', colorMode === 'cumulative'); + + if (coldCodeHidden) { + applyHotFilter(); + } + + buildScrollMarker(); +} + +// ============================================================================ +// Initialization +// ============================================================================ + +document.addEventListener('DOMContentLoaded', function() { + restoreUIState(); + applyLineColors(); + + // Initialize navigation buttons + document.querySelectorAll('.nav-btn').forEach(button => { + button.addEventListener('click', e => handleNavigationClick(button, e)); + }); + + // Initialize line number permalink handlers + document.querySelectorAll('.line-number').forEach(lineNum => { + lineNum.style.cursor = 'pointer'; + lineNum.addEventListener('click', e => { + window.location.hash = `line-${e.target.textContent.trim()}`; + }); + }); + + // Initialize toggle buttons + const toggleColdBtn = document.getElementById('toggle-cold'); + if (toggleColdBtn) toggleColdBtn.addEventListener('click', toggleColdCode); + + const colorModeBtn = document.getElementById('toggle-color-mode'); + if (colorModeBtn) colorModeBtn.addEventListener('click', toggleColorMode); + + // Initialize specialization view toggle (hide if no bytecode data) + const hasBytecode = document.querySelectorAll('.bytecode-toggle').length > 0; + + const specViewBtn = document.getElementById('toggle-spec-view'); + if (specViewBtn) { + if (hasBytecode) { + specViewBtn.addEventListener('click', toggleSpecView); + } else { + specViewBtn.style.display = 'none'; + } + } + + // Initialize expand-all bytecode button + const expandAllBtn = document.getElementById('toggle-all-bytecode'); + if (expandAllBtn) { + if (hasBytecode) { + expandAllBtn.addEventListener('click', toggleAllBytecode); + } else { + expandAllBtn.style.display = 'none'; + } + } + + // Initialize span tooltips + initSpanTooltips(); + + // Build scroll marker and scroll to target + setTimeout(buildScrollMarker, 200); + setTimeout(scrollToTargetLine, 100); +}); + +// Close menu when clicking outside +document.addEventListener('click', e => { + if (currentMenu && !currentMenu.contains(e.target) && !e.target.classList.contains('nav-btn')) { + closeMenu(); + } +}); + +// ======================================== +// SPECIALIZATION VIEW TOGGLE +// ======================================== + +let specViewEnabled = false; + +/** + * Calculate heat color for given intensity (0-1) + * Hot spans (>30%) get warm orange, cold spans get dimmed gray + * @param {number} intensity - Value between 0 and 1 + * @returns {string} rgba color string + */ +function calculateHeatColor(intensity) { + // Hot threshold: only spans with >30% of max samples get color + if (intensity > 0.3) { + // Normalize intensity above threshold to 0-1 + const normalizedIntensity = (intensity - 0.3) / 0.7; + // Warm orange-red with increasing opacity for hotter spans + const alpha = 0.25 + normalizedIntensity * 0.35; // 0.25 to 0.6 + const hotColor = getComputedStyle(document.documentElement).getPropertyValue('--span-hot-base').trim(); + return `rgba(${hotColor}, ${alpha})`; + } else if (intensity > 0) { + // Cold spans: very subtle gray, almost invisible + const coldColor = getComputedStyle(document.documentElement).getPropertyValue('--span-cold-base').trim(); + return `rgba(${coldColor}, 0.1)`; + } + return 'transparent'; +} + +/** + * Apply intensity-based heat colors to source spans + * Hot spans get orange highlight, cold spans get dimmed + * @param {boolean} enable - Whether to enable or disable span coloring + */ +function applySpanHeatColors(enable) { + document.querySelectorAll('.instr-span').forEach(span => { + const samples = enable ? (parseInt(span.dataset.samples) || 0) : 0; + if (samples > 0) { + const intensity = samples / (parseInt(span.dataset.maxSamples) || 1); + span.style.backgroundColor = calculateHeatColor(intensity); + span.style.borderRadius = '2px'; + span.style.padding = '0 1px'; + span.style.cursor = 'pointer'; + } else { + span.style.cssText = ''; + } + }); +} + +// ======================================== +// SPAN TOOLTIPS +// ======================================== + +let activeTooltip = null; + +/** + * Create and show tooltip for a span + */ +function showSpanTooltip(span) { + hideSpanTooltip(); + + const samples = parseInt(span.dataset.samples) || 0; + const maxSamples = parseInt(span.dataset.maxSamples) || 1; + const pct = span.dataset.pct || '0'; + const opcodes = span.dataset.opcodes || ''; + + if (samples === 0) return; + + const intensity = samples / maxSamples; + const isHot = intensity > 0.7; + const isWarm = intensity > 0.3; + const hotnessText = isHot ? 'Hot' : isWarm ? 'Warm' : 'Cold'; + const hotnessClass = isHot ? 'hot' : isWarm ? 'warm' : 'cold'; + + // Build opcodes rows - each opcode on its own row + let opcodesHtml = ''; + if (opcodes) { + const opcodeList = opcodes.split(',').map(op => op.trim()).filter(op => op); + if (opcodeList.length > 0) { + opcodesHtml = ` +
    Opcodes:
    + ${opcodeList.map(op => `
    ${op}
    `).join('')} + `; + } + } + + const tooltip = document.createElement('div'); + tooltip.className = 'span-tooltip'; + tooltip.innerHTML = ` +
    ${hotnessText}
    +
    + Samples: + ${samples.toLocaleString()} +
    +
    + % of line: + ${pct}% +
    + ${opcodesHtml} + `; + + document.body.appendChild(tooltip); + activeTooltip = tooltip; + + // Position tooltip above the span + const rect = span.getBoundingClientRect(); + const tooltipRect = tooltip.getBoundingClientRect(); + + let left = rect.left + (rect.width / 2) - (tooltipRect.width / 2); + let top = rect.top - tooltipRect.height - 8; + + // Keep tooltip in viewport + if (left < 5) left = 5; + if (left + tooltipRect.width > window.innerWidth - 5) { + left = window.innerWidth - tooltipRect.width - 5; + } + if (top < 5) { + top = rect.bottom + 8; // Show below if no room above + } + + tooltip.style.left = `${left + window.scrollX}px`; + tooltip.style.top = `${top + window.scrollY}px`; +} + +/** + * Hide active tooltip + */ +function hideSpanTooltip() { + if (activeTooltip) { + activeTooltip.remove(); + activeTooltip = null; + } +} + +/** + * Initialize span tooltip handlers + */ +function initSpanTooltips() { + document.addEventListener('mouseover', (e) => { + const span = e.target.closest('.instr-span'); + if (span && specViewEnabled) { + showSpanTooltip(span); + } + }); + + document.addEventListener('mouseout', (e) => { + const span = e.target.closest('.instr-span'); + if (span) { + hideSpanTooltip(); + } + }); +} + +function toggleSpecView() { + specViewEnabled = !specViewEnabled; + const lines = document.querySelectorAll('.code-line'); + + if (specViewEnabled) { + lines.forEach(line => { + const specColor = line.getAttribute('data-spec-color'); + line.style.background = specColor || 'transparent'; + }); + } else { + applyLineColors(); + } + + applySpanHeatColors(specViewEnabled); + updateToggleUI('toggle-spec-view', specViewEnabled); + + // Disable/enable color mode toggle based on spec view state + const colorModeToggle = document.getElementById('toggle-color-mode'); + if (colorModeToggle) { + colorModeToggle.classList.toggle('disabled', specViewEnabled); + } + + buildScrollMarker(); +} + +// ======================================== +// BYTECODE PANEL TOGGLE +// ======================================== + +/** + * Toggle bytecode panel visibility for a source line + * @param {HTMLElement} button - The toggle button that was clicked + */ +function toggleBytecode(button) { + const lineDiv = button.closest('.code-line'); + const lineId = lineDiv.id; + const lineNum = lineId.replace('line-', ''); + const panel = document.getElementById(`bytecode-${lineNum}`); + const wrapper = document.getElementById(`bytecode-wrapper-${lineNum}`); + + if (!panel || !wrapper) return; + + const isExpanded = panel.classList.contains('expanded'); + + if (isExpanded) { + panel.classList.remove('expanded'); + wrapper.classList.remove('expanded'); + button.classList.remove('expanded'); + button.innerHTML = '▶'; // Right arrow + } else { + if (!panel.dataset.populated) { + populateBytecodePanel(panel, button); + } + panel.classList.add('expanded'); + wrapper.classList.add('expanded'); + button.classList.add('expanded'); + button.innerHTML = '▼'; // Down arrow + } +} + +/** + * Populate bytecode panel with instruction data + * @param {HTMLElement} panel - The panel element to populate + * @param {HTMLElement} button - The button containing the bytecode data + */ +function populateBytecodePanel(panel, button) { + const bytecodeJson = button.getAttribute('data-bytecode'); + if (!bytecodeJson) return; + + // Get line number from parent + const lineDiv = button.closest('.code-line'); + const lineNum = lineDiv ? lineDiv.id.replace('line-', '') : null; + + try { + const instructions = JSON.parse(bytecodeJson); + if (!instructions.length) { + panel.innerHTML = '
    No bytecode data
    '; + panel.dataset.populated = 'true'; + return; + } + + const maxSamples = Math.max(...instructions.map(i => i.samples), 1); + + // Calculate specialization stats + const totalSamples = instructions.reduce((sum, i) => sum + i.samples, 0); + const specializedSamples = instructions + .filter(i => i.is_specialized) + .reduce((sum, i) => sum + i.samples, 0); + const specPct = totalSamples > 0 ? Math.round(100 * specializedSamples / totalSamples) : 0; + const specializedCount = instructions.filter(i => i.is_specialized).length; + + // Determine specialization level class + let specClass = 'low'; + if (specPct >= 67) specClass = 'high'; + else if (specPct >= 33) specClass = 'medium'; + + // Build specialization summary + const instruction_word = instructions.length === 1 ? 'instruction' : 'instructions'; + const sample_word = totalSamples === 1 ? 'sample' : 'samples'; + let html = `
    + ${specPct}% + specialized + (${specializedCount}/${instructions.length} ${instruction_word}, ${specializedSamples.toLocaleString()}/${totalSamples.toLocaleString()} ${sample_word}) +
    `; + + html += '
    ' + + 'Instruction' + + 'Samples' + + 'Heat
    '; + + for (const instr of instructions) { + const heatPct = (instr.samples / maxSamples) * 100; + const isHot = heatPct > 50; + const specializedClass = instr.is_specialized ? ' specialized' : ''; + const baseOpHtml = instr.is_specialized + ? `(${escapeHtml(instr.base_opname)})` : ''; + const badge = instr.is_specialized + ? 'SPECIALIZED' : ''; + + // Build location data attributes for cross-referencing with source spans + const hasLocations = instr.locations && instr.locations.length > 0; + const locationData = hasLocations + ? `data-locations='${JSON.stringify(instr.locations)}' data-line="${lineNum}" data-opcode="${instr.opcode}"` + : ''; + + html += `
    + ${escapeHtml(instr.opname)}${baseOpHtml}${badge} + ${instr.samples.toLocaleString()} +
    +
    `; + } + + panel.innerHTML = html; + panel.dataset.populated = 'true'; + + // Add hover handlers for bytecode instructions to highlight source spans + panel.querySelectorAll('.bytecode-instruction[data-locations]').forEach(instrEl => { + instrEl.addEventListener('mouseenter', highlightSourceFromBytecode); + instrEl.addEventListener('mouseleave', unhighlightSourceFromBytecode); + }); + } catch (e) { + panel.innerHTML = '
    Error loading bytecode
    '; + console.error('Error parsing bytecode data:', e); + } +} + +/** + * Highlight source spans when hovering over bytecode instruction + */ +function highlightSourceFromBytecode(e) { + const instrEl = e.currentTarget; + const lineNum = instrEl.dataset.line; + const locationsStr = instrEl.dataset.locations; + + if (!lineNum) return; + + const lineDiv = document.getElementById(`line-${lineNum}`); + if (!lineDiv) return; + + // Parse locations and highlight matching spans by column range + try { + const locations = JSON.parse(locationsStr || '[]'); + const spans = lineDiv.querySelectorAll('.instr-span'); + spans.forEach(span => { + const spanStart = parseInt(span.dataset.colStart); + const spanEnd = parseInt(span.dataset.colEnd); + for (const loc of locations) { + // Match if span's range matches instruction's location + if (spanStart === loc.col_offset && spanEnd === loc.end_col_offset) { + span.classList.add('highlight-from-bytecode'); + break; + } + } + }); + } catch (err) { + console.error('Error parsing locations:', err); + } + + // Also highlight the instruction row itself + instrEl.classList.add('highlight'); +} + +/** + * Remove highlighting from source spans + */ +function unhighlightSourceFromBytecode(e) { + const instrEl = e.currentTarget; + const lineNum = instrEl.dataset.line; + + if (!lineNum) return; + + const lineDiv = document.getElementById(`line-${lineNum}`); + if (!lineDiv) return; + + const spans = lineDiv.querySelectorAll('.instr-span.highlight-from-bytecode'); + spans.forEach(span => { + span.classList.remove('highlight-from-bytecode'); + }); + + instrEl.classList.remove('highlight'); +} + +/** + * Escape HTML special characters + * @param {string} text - Text to escape + * @returns {string} Escaped HTML + */ +function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; +} + +/** + * Toggle all bytecode panels at once + */ +function toggleAllBytecode() { + const buttons = document.querySelectorAll('.bytecode-toggle'); + if (buttons.length === 0) return; + + const someExpanded = Array.from(buttons).some(b => b.classList.contains('expanded')); + const expandAllBtn = document.getElementById('toggle-all-bytecode'); + + buttons.forEach(button => { + const isExpanded = button.classList.contains('expanded'); + if (someExpanded ? isExpanded : !isExpanded) { + toggleBytecode(button); + } + }); + + // Update the expand-all button state + if (expandAllBtn) { + expandAllBtn.classList.toggle('expanded', !someExpanded); + } +} + +// Keyboard shortcut: 'b' toggles all bytecode panels, Enter/Space activates toggle switches +document.addEventListener('keydown', function(e) { + if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') { + return; + } + if (e.key === 'b' && !e.ctrlKey && !e.altKey && !e.metaKey) { + toggleAllBytecode(); + } + if ((e.key === 'Enter' || e.key === ' ') && e.target.classList.contains('toggle-switch')) { + e.preventDefault(); + e.target.click(); + } +}); + +// Handle hash changes +window.addEventListener('hashchange', () => setTimeout(scrollToTargetLine, 50)); + +// Rebuild scroll marker on resize +window.addEventListener('resize', buildScrollMarker); diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js b/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js new file mode 100644 index 00000000000000..db4b5485056217 --- /dev/null +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_index.js @@ -0,0 +1,104 @@ +// Tachyon Profiler - Heatmap Index JavaScript +// Index page specific functionality + +// ============================================================================ +// Heatmap Bar Coloring +// ============================================================================ + +function applyHeatmapBarColors() { + const bars = document.querySelectorAll('.heatmap-bar[data-intensity]'); + bars.forEach(bar => { + const intensity = parseFloat(bar.getAttribute('data-intensity')) || 0; + const color = intensityToColor(intensity); + bar.style.backgroundColor = color; + }); +} + +// ============================================================================ +// Theme Support +// ============================================================================ + +function toggleTheme() { + toggleAndSaveTheme(); + applyHeatmapBarColors(); +} + +// ============================================================================ +// Type Section Toggle (stdlib, project, etc) +// ============================================================================ + +function toggleTypeSection(header) { + const section = header.parentElement; + const content = section.querySelector('.type-content'); + const icon = header.querySelector('.type-icon'); + + if (content.style.display === 'none') { + content.style.display = 'block'; + icon.textContent = '\u25BC'; + } else { + content.style.display = 'none'; + icon.textContent = '\u25B6'; + } +} + +// ============================================================================ +// Folder Toggle +// ============================================================================ + +function toggleFolder(header) { + const folder = header.parentElement; + const content = folder.querySelector('.folder-content'); + const icon = header.querySelector('.folder-icon'); + + if (content.style.display === 'none') { + content.style.display = 'block'; + icon.textContent = '\u25BC'; + folder.classList.remove('collapsed'); + } else { + content.style.display = 'none'; + icon.textContent = '\u25B6'; + folder.classList.add('collapsed'); + } +} + +// ============================================================================ +// Expand/Collapse All +// ============================================================================ + +function expandAll() { + // Expand all type sections + document.querySelectorAll('.type-section').forEach(section => { + const content = section.querySelector('.type-content'); + const icon = section.querySelector('.type-icon'); + content.style.display = 'block'; + icon.textContent = '\u25BC'; + }); + + // Expand all folders + document.querySelectorAll('.folder-node').forEach(folder => { + const content = folder.querySelector('.folder-content'); + const icon = folder.querySelector('.folder-icon'); + content.style.display = 'block'; + icon.textContent = '\u25BC'; + folder.classList.remove('collapsed'); + }); +} + +function collapseAll() { + document.querySelectorAll('.folder-node').forEach(folder => { + const content = folder.querySelector('.folder-content'); + const icon = folder.querySelector('.folder-icon'); + content.style.display = 'none'; + icon.textContent = '\u25B6'; + folder.classList.add('collapsed'); + }); +} + +// ============================================================================ +// Initialization +// ============================================================================ + +document.addEventListener('DOMContentLoaded', function() { + restoreUIState(); + applyHeatmapBarColors(); +}); diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html b/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html new file mode 100644 index 00000000000000..8d04149abe3014 --- /dev/null +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_index_template.html @@ -0,0 +1,136 @@ + + + + + + Tachyon Profiler - Heatmap Report + + + +
    + +
    +
    + + Tachyon + + Heatmap Report +
    +
    + + + + + + +
    +
    + + +
    + +
    +
    +
    📄
    +
    + + Files Profiled +
    +
    +
    +
    +
    📊
    +
    + + Total Snapshots +
    +
    +
    +
    +
    +
    + + Duration +
    +
    +
    +
    +
    +
    + + Samples/sec +
    +
    +
    +
    +
    +
    +
    + Error Rate +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    💥
    + Missed Samples +
    + +
    +
    +
    +
    +
    +
    + + +
    +

    Profiled Files

    +
    + +
    + + +
    + +
    + +
    +
    + + +
    + + Tachyon Profiler + + + Python Sampling Profiler + +
    +
    + + + + diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html b/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html new file mode 100644 index 00000000000000..2a9c07647e64cd --- /dev/null +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_pyfile_template.html @@ -0,0 +1,131 @@ + + + + + + <!-- FILENAME --> - Heatmap + + + +
    + +
    +
    + + Tachyon + + +
    +
    + + + + + + + + + + + +
    +
    + + +
    +
    +
    +
    +
    Self Samples
    +
    +
    +
    +
    Cumulative
    +
    +
    +
    +
    Lines Hit
    +
    +
    +
    %
    +
    % of Total
    +
    +
    +
    +
    Max Self
    +
    +
    +
    +
    Max Total
    +
    +
    +
    + + +
    +
    + Intensity: +
    +
    + Cold + + Hot +
    + +
    +
    + Self Time +
    + Total Time +
    +
    + Show All +
    + Hot Only +
    +
    + Heat +
    + Specialization +
    + + +
    +
    +
    + + +
    +
    +
    Line
    +
    Self
    +
    Total
    +
    Code
    +
    + +
    +
    + + + + diff --git a/Lib/profiling/sampling/_heatmap_assets/heatmap_shared.js b/Lib/profiling/sampling/_heatmap_assets/heatmap_shared.js new file mode 100644 index 00000000000000..fb761335876b0f --- /dev/null +++ b/Lib/profiling/sampling/_heatmap_assets/heatmap_shared.js @@ -0,0 +1,64 @@ +// Tachyon Profiler - Shared Heatmap JavaScript +// Common utilities shared between index and file views + +// ============================================================================ +// Heat Level Mapping (Single source of truth for intensity thresholds) +// ============================================================================ + +// Maps intensity (0-1) to heat level (0-8). Level 0 = no heat, 1-8 = heat levels. +function intensityToHeatLevel(intensity) { + if (intensity <= 0) return 0; + if (intensity <= 0.125) return 1; + if (intensity <= 0.25) return 2; + if (intensity <= 0.375) return 3; + if (intensity <= 0.5) return 4; + if (intensity <= 0.625) return 5; + if (intensity <= 0.75) return 6; + if (intensity <= 0.875) return 7; + return 8; +} + +// Class names corresponding to heat levels 1-8 (used by scroll marker) +const HEAT_CLASS_NAMES = ['cold', 'cool', 'mild', 'warm', 'hot', 'very-hot', 'intense', 'extreme']; + +function intensityToClass(intensity) { + const level = intensityToHeatLevel(intensity); + return level === 0 ? null : HEAT_CLASS_NAMES[level - 1]; +} + +// ============================================================================ +// Color Mapping (Intensity to Heat Color) +// ============================================================================ + +function intensityToColor(intensity) { + const level = intensityToHeatLevel(intensity); + if (level === 0) { + return 'transparent'; + } + const rootStyle = getComputedStyle(document.documentElement); + return rootStyle.getPropertyValue(`--heat-${level}`).trim(); +} + +// ============================================================================ +// Theme Support +// ============================================================================ + +// Restore theme from localStorage, or use browser preference +function restoreUIState() { + applyTheme(getPreferredTheme()); +} + +// ============================================================================ +// Favicon (Reuse logo image as favicon) +// ============================================================================ + +(function() { + const logo = document.querySelector('.brand-logo img'); + if (logo) { + const favicon = document.createElement('link'); + favicon.rel = 'icon'; + favicon.type = 'image/png'; + favicon.href = logo.src; + document.head.appendChild(favicon); + } +})(); diff --git a/Lib/profiling/sampling/_shared_assets/base.css b/Lib/profiling/sampling/_shared_assets/base.css new file mode 100644 index 00000000000000..2164f3f3aa13c7 --- /dev/null +++ b/Lib/profiling/sampling/_shared_assets/base.css @@ -0,0 +1,514 @@ +/* ========================================================================== + Python Profiler - Shared CSS Foundation + Design system shared between Flamegraph and Heatmap viewers + ========================================================================== */ + +/* -------------------------------------------------------------------------- + CSS Variables & Theme System + -------------------------------------------------------------------------- */ + +:root { + /* Typography */ + --font-sans: "Source Sans Pro", "Lucida Grande", "Lucida Sans Unicode", + "Geneva", "Verdana", sans-serif; + --font-mono: 'SF Mono', 'Monaco', 'Consolas', 'Liberation Mono', monospace; + + /* Python brand colors (theme-independent) */ + --python-blue: #3776ab; + --python-blue-light: #4584bb; + --python-blue-lighter: #5592cc; + --python-gold: #ffd43b; + --python-gold-dark: #ffcd02; + --python-gold-light: #ffdc5c; + + /* Heat palette - defined per theme below */ + + /* Layout */ + --sidebar-width: 280px; + --sidebar-collapsed: 44px; + --topbar-height: 56px; + --statusbar-height: 32px; + + /* Border radius */ + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + + /* Transitions */ + --transition-fast: 0.15s ease; + --transition-normal: 0.25s ease; + --transition-slow: 0.3s ease; +} + +/* Light theme (default) */ +:root, [data-theme="light"] { + --bg-primary: #ffffff; + --bg-secondary: #f8f9fa; + --bg-tertiary: #e9ecef; + --border: #e9ecef; + --border-subtle: #f0f2f5; + + --text-primary: #2e3338; + --text-secondary: #5a6c7d; + --text-muted: #6f767e; + + --accent: #3776ab; + --accent-hover: #2d5aa0; + --accent-glow: rgba(55, 118, 171, 0.15); + + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.15); + + --header-gradient: linear-gradient(135deg, #3776ab 0%, #4584bb 100%); + + /* Light mode heat palette - blue to yellow to orange to red (cold to hot) */ + --heat-1: #7ba3d1; + --heat-2: #a8d0ef; + --heat-3: #d6e9f8; + --heat-4: #ffe6a8; + --heat-5: #ffd43b; + --heat-6: #ffb84d; + --heat-7: #ff9966; + --heat-8: #ff6347; + + /* Code view specific */ + --code-bg: #ffffff; + --code-bg-line: #f8f9fa; + --code-border: #e9ecef; + --code-text: #2e3338; + --code-text-muted: #8b949e; + --code-accent: #3776ab; + + /* Navigation colors */ + --nav-caller: #2563eb; + --nav-caller-hover: #1d4ed8; + --nav-callee: #dc2626; + --nav-callee-hover: #b91c1c; + + /* Specialization status colors */ + --spec-high: #4caf50; + --spec-high-text: #2e7d32; + --spec-high-bg: rgba(76, 175, 80, 0.15); + --spec-medium: #ff9800; + --spec-medium-text: #e65100; + --spec-medium-bg: rgba(255, 152, 0, 0.15); + --spec-low: #9e9e9e; + --spec-low-text: #616161; + --spec-low-bg: rgba(158, 158, 158, 0.15); + + /* Heatmap span highlighting colors */ + --span-hot-base: 255, 100, 50; + --span-cold-base: 150, 150, 150; + + /* Summary card colors - optimized for 4.5:1 contrast on light bg */ + --card-blue: 44, 102, 149; + --card-green: 26, 116, 49; + --card-yellow: 134, 100, 4; + --card-purple: 102, 57, 166; + --card-red: 180, 40, 50; + --card-orange: 166, 90, 0; +} + +/* Dark theme */ +[data-theme="dark"] { + --bg-primary: #0d1117; + --bg-secondary: #161b22; + --bg-tertiary: #21262d; + --border: #30363d; + --border-subtle: #21262d; + + --text-primary: #e6edf3; + --text-secondary: #8b949e; + --text-muted: #757e8a; + + --accent: #58a6ff; + --accent-hover: #79b8ff; + --accent-glow: rgba(88, 166, 255, 0.15); + + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5); + + --header-gradient: linear-gradient(135deg, #21262d 0%, #30363d 100%); + + /* Dark mode heat palette - cool to warm gradient for visualization */ + --heat-1: rgba(90, 123, 167, 1); + --heat-2: rgba(106, 148, 168, 1); + --heat-3: rgba(122, 172, 172, 1); + --heat-4: rgba(142, 196, 152, 1); + --heat-5: rgba(168, 216, 136, 1); + --heat-6: rgba(200, 222, 122, 1); + --heat-7: rgba(244, 212, 93, 1); + --heat-8: rgba(255, 122, 69, 1); + + /* Code view specific - dark mode */ + --code-bg: #0d1117; + --code-bg-line: #161b22; + --code-border: #30363d; + --code-text: #e6edf3; + --code-text-muted: #6e7681; + --code-accent: #58a6ff; + + /* Navigation colors - dark theme friendly */ + --nav-caller: #58a6ff; + --nav-caller-hover: #4184e4; + --nav-callee: #f87171; + --nav-callee-hover: #e53e3e; + + /* Specialization status colors - dark theme */ + --spec-high: #81c784; + --spec-high-text: #81c784; + --spec-high-bg: rgba(129, 199, 132, 0.2); + --spec-medium: #ffb74d; + --spec-medium-text: #ffb74d; + --spec-medium-bg: rgba(255, 183, 77, 0.2); + --spec-low: #bdbdbd; + --spec-low-text: #9e9e9e; + --spec-low-bg: rgba(189, 189, 189, 0.15); + + /* Heatmap span highlighting colors - dark theme */ + --span-hot-base: 255, 107, 53; + --span-cold-base: 189, 189, 189; + + /* Summary card colors - optimized for 4.5:1 contrast on dark bg */ + --card-blue: 88, 166, 255; + --card-green: 63, 185, 80; + --card-yellow: 210, 153, 34; + --card-purple: 163, 113, 247; + --card-red: 248, 113, 113; + --card-orange: 251, 146, 60; +} + +/* -------------------------------------------------------------------------- + Base Styles + -------------------------------------------------------------------------- */ + +*, *::before, *::after { + box-sizing: border-box; +} + +html, body { + margin: 0; + padding: 0; +} + +body { + font-family: var(--font-sans); + font-size: 14px; + line-height: 1.6; + color: var(--text-primary); + background: var(--bg-primary); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + transition: background var(--transition-normal), color var(--transition-normal); +} + +/* -------------------------------------------------------------------------- + Layout Structure + -------------------------------------------------------------------------- */ + +.app-layout { + display: flex; + flex-direction: column; +} + +/* -------------------------------------------------------------------------- + Top Bar + -------------------------------------------------------------------------- */ + +.top-bar { + height: var(--topbar-height); + background: var(--header-gradient); + display: flex; + align-items: center; + padding: 0 16px; + gap: 16px; + flex-shrink: 0; + box-shadow: 0 2px 10px rgba(55, 118, 171, 0.25); + border-bottom: 2px solid var(--python-gold); +} + +/* Brand / Logo */ +.brand { + display: flex; + align-items: center; + gap: 12px; + color: white; + text-decoration: none; + flex-shrink: 0; +} + +.brand-logo { + display: flex; + align-items: center; + justify-content: center; + width: 48px; + height: 40px; + flex-shrink: 0; +} + +/* Style the inlined SVG/img inside brand-logo */ +.brand-logo svg, +.brand-logo img { + width: 48px; + height: 40px; + display: block; + object-fit: contain; + filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2)); +} + +.brand-info { + display: flex; + flex-direction: column; + line-height: 1.15; +} + +.brand-text { + font-family: var(--font-sans); + font-weight: 700; + font-size: 16px; + letter-spacing: -0.3px; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); + color: inherit; + text-decoration: none; +} + +.brand-subtitle { + font-weight: 500; + font-size: 10px; + opacity: 0.9; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.brand-divider { + width: 1px; + height: 16px; + background: rgba(255, 255, 255, 0.3); +} + +/* Toolbar */ +.toolbar { + display: flex; + align-items: center; + gap: 6px; + margin-left: auto; +} + +.toolbar-btn { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + padding: 0; + font-size: 15px; + color: white; + background: rgba(255, 255, 255, 0.12); + border: 1px solid rgba(255, 255, 255, 0.18); + border-radius: 6px; + cursor: pointer; + text-decoration: none; + transition: all var(--transition-fast); +} + +.toolbar-btn:hover { + background: rgba(255, 255, 255, 0.22); + border-color: rgba(255, 255, 255, 0.35); +} + +.toolbar-btn:active { + transform: scale(0.95); +} + +/* -------------------------------------------------------------------------- + Status Bar + -------------------------------------------------------------------------- */ + +.status-bar { + height: var(--statusbar-height); + background: var(--bg-secondary); + border-top: 1px solid var(--border); + display: flex; + align-items: center; + padding: 0 16px; + gap: 16px; + font-family: var(--font-mono); + font-size: 11px; + color: var(--text-secondary); + flex-shrink: 0; +} + +.status-item { + display: flex; + align-items: center; + gap: 5px; +} + +.status-item::before { + content: ''; + width: 4px; + height: 4px; + background: var(--python-gold); + border-radius: 50%; +} + +.status-item:first-child::before { + display: none; +} + +.status-label { + color: var(--text-muted); +} + +.status-value { + color: var(--text-primary); + font-weight: 500; +} + +.status-value.accent { + color: var(--accent); + font-weight: 600; +} + +/* -------------------------------------------------------------------------- + Animations + -------------------------------------------------------------------------- */ + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(12px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes shimmer { + 0% { left: -100%; } + 100% { left: 100%; } +} + +/* -------------------------------------------------------------------------- + Focus States (Accessibility) + -------------------------------------------------------------------------- */ + +button:focus-visible, +select:focus-visible, +input:focus-visible, +.toggle-switch:focus-visible, +a.toolbar-btn:focus-visible { + outline: 2px solid var(--python-gold); + outline-offset: 2px; +} + +/* -------------------------------------------------------------------------- + Shared Responsive + -------------------------------------------------------------------------- */ + +@media (max-width: 900px) { + .brand-subtitle { + display: none; + } +} + +@media (max-width: 600px) { + .toolbar-btn:not(.theme-toggle) { + display: none; + } +} + +/* -------------------------------------------------------------------------- + Toggle Switch + -------------------------------------------------------------------------- */ + +.toggle-switch { + display: inline-flex; + align-items: center; + gap: 8px; + cursor: pointer; + user-select: none; + font-family: var(--font-sans); + transition: opacity var(--transition-fast); + flex-shrink: 0; +} + +.toggle-switch:hover { + opacity: 0.85; +} + +.toggle-switch .toggle-label { + font-size: 11px; + font-weight: 500; + color: var(--text-muted); + transition: color var(--transition-fast); + white-space: nowrap; + display: inline-flex; + flex-direction: column; +} + +.toggle-switch .toggle-label.active { + color: var(--text-primary); + font-weight: 600; +} + +/* Reserve space for bold text to prevent layout shift on toggle */ +.toggle-switch .toggle-label::after { + content: attr(data-text); + font-weight: 600; + height: 0; + visibility: hidden; +} + +.toggle-switch.disabled { + opacity: 0.4; + pointer-events: none; + cursor: not-allowed; +} + +.toggle-track { + position: relative; + width: 36px; + height: 20px; + background: var(--bg-tertiary); + border: 2px solid var(--border); + border-radius: 12px; + transition: all var(--transition-fast); + box-shadow: inset var(--shadow-sm); +} + +.toggle-track:hover { + border-color: var(--text-muted); +} + +.toggle-track.on { + background: var(--accent); + border-color: var(--accent); + box-shadow: 0 0 8px var(--accent-glow); +} + +.toggle-track::after { + content: ''; + position: absolute; + top: 1px; + left: 1px; + width: 14px; + height: 14px; + background: white; + border-radius: 50%; + box-shadow: var(--shadow-sm); + transition: all var(--transition-fast); +} + +.toggle-track.on::after { + transform: translateX(16px); + box-shadow: var(--shadow-md); +} diff --git a/Lib/profiling/sampling/_shared_assets/base.js b/Lib/profiling/sampling/_shared_assets/base.js new file mode 100644 index 00000000000000..da8b5851c85f62 --- /dev/null +++ b/Lib/profiling/sampling/_shared_assets/base.js @@ -0,0 +1,58 @@ +// Tachyon Profiler - Shared JavaScript +// Common utilities shared between flamegraph and heatmap views + +// ============================================================================ +// Theme Support +// ============================================================================ + +// Storage key for theme preference +const THEME_STORAGE_KEY = 'tachyon-theme'; + +// Get the preferred theme from localStorage or system preference +function getPreferredTheme() { + const saved = localStorage.getItem(THEME_STORAGE_KEY); + if (saved) return saved; + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; +} + +// Apply theme and update UI +function applyTheme(theme) { + document.documentElement.setAttribute('data-theme', theme); + const btn = document.getElementById('theme-btn'); + if (btn) { + const moonIcon = btn.querySelector('.icon-moon'); + const sunIcon = btn.querySelector('.icon-sun'); + if (moonIcon) moonIcon.style.display = theme === 'dark' ? 'none' : ''; + if (sunIcon) sunIcon.style.display = theme === 'dark' ? '' : 'none'; + } +} + +// Toggle theme and save preference. Returns the new theme. +function toggleAndSaveTheme() { + const current = document.documentElement.getAttribute('data-theme') || 'light'; + const next = current === 'light' ? 'dark' : 'light'; + applyTheme(next); + localStorage.setItem(THEME_STORAGE_KEY, next); + return next; +} + +// ============================================================================ +// Toggle Switch UI +// ============================================================================ + +function updateToggleUI(toggleId, isOn) { + const toggle = document.getElementById(toggleId); + if (toggle) { + const track = toggle.querySelector('.toggle-track'); + const labels = toggle.querySelectorAll('.toggle-label'); + if (isOn) { + track.classList.add('on'); + labels[0].classList.remove('active'); + labels[1].classList.add('active'); + } else { + track.classList.remove('on'); + labels[0].classList.add('active'); + labels[1].classList.remove('active'); + } + } +} diff --git a/Lib/profiling/sampling/_sync_coordinator.py b/Lib/profiling/sampling/_sync_coordinator.py new file mode 100644 index 00000000000000..a1cce314b33b19 --- /dev/null +++ b/Lib/profiling/sampling/_sync_coordinator.py @@ -0,0 +1,256 @@ +""" +Internal synchronization coordinator for the sample profiler. + +This module is used internally by the sample profiler to coordinate +the startup of target processes. It should not be called directly by users. +""" + +import importlib.util +import os +import sys +import socket +import runpy +import time +import types +from typing import List, NoReturn + + +class CoordinatorError(Exception): + """Base exception for coordinator errors.""" + pass + + +class ArgumentError(CoordinatorError): + """Raised when invalid arguments are provided.""" + pass + + +class SyncError(CoordinatorError): + """Raised when synchronization with profiler fails.""" + pass + + +class TargetError(CoordinatorError): + """Raised when target execution fails.""" + pass + + +def _validate_arguments(args: List[str]) -> tuple[int, str, List[str]]: + """ + Validate and parse command line arguments. + + Args: + args: Command line arguments including script name + + Returns: + Tuple of (sync_port, working_directory, target_args) + + Raises: + ArgumentError: If arguments are invalid + """ + if len(args) < 4: + raise ArgumentError( + "Insufficient arguments. Expected: [args...]" + ) + + try: + sync_port = int(args[1]) + if not (1 <= sync_port <= 65535): + raise ValueError("Port out of range") + except ValueError as e: + raise ArgumentError(f"Invalid sync port '{args[1]}': {e}") from e + + cwd = args[2] + if not os.path.isdir(cwd): + raise ArgumentError(f"Working directory does not exist: {cwd}") + + target_args = args[3:] + if not target_args: + raise ArgumentError("No target specified") + + return sync_port, cwd, target_args + + +# Constants for socket communication +_MAX_RETRIES = 3 +_INITIAL_RETRY_DELAY_SEC = 0.1 +_SOCKET_TIMEOUT_SEC = 2.0 +_READY_MESSAGE = b"ready" + + +def _signal_readiness(sync_port: int) -> None: + """ + Signal readiness to the profiler via TCP socket. + + Args: + sync_port: Port number where profiler is listening + + Raises: + SyncError: If unable to signal readiness + """ + last_error = None + + for attempt in range(_MAX_RETRIES): + try: + # Use context manager for automatic cleanup + with socket.create_connection(("127.0.0.1", sync_port), timeout=_SOCKET_TIMEOUT_SEC) as sock: + sock.send(_READY_MESSAGE) + return + except (socket.error, OSError) as e: + last_error = e + if attempt < _MAX_RETRIES - 1: + # Exponential backoff before retry + time.sleep(_INITIAL_RETRY_DELAY_SEC * (2 ** attempt)) + + # If we get here, all retries failed + raise SyncError(f"Failed to signal readiness after {_MAX_RETRIES} attempts: {last_error}") from last_error + + +def _setup_environment(cwd: str) -> None: + """ + Set up the execution environment. + + Args: + cwd: Working directory to change to + + Raises: + TargetError: If unable to set up environment + """ + try: + os.chdir(cwd) + except OSError as e: + raise TargetError(f"Failed to change to directory {cwd}: {e}") from e + + # Add current directory to sys.path if not present (for module imports) + if cwd not in sys.path: + sys.path.insert(0, cwd) + + +def _execute_module(module_name: str, module_args: List[str]) -> None: + """ + Execute a Python module. + + Args: + module_name: Name of the module to execute + module_args: Arguments to pass to the module + + Raises: + TargetError: If module cannot be found + """ + # Replace sys.argv to match how Python normally runs modules + # When running 'python -m module args', sys.argv is ["__main__.py", "args"] + sys.argv = ["__main__.py"] + module_args + + try: + runpy.run_module(module_name, run_name="__main__", alter_sys=True) + except ImportError as e: + raise TargetError(f"Module '{module_name}' not found: {e}") from e + # Let other exceptions (including SystemExit) propagate naturally + # so Python prints the full traceback to stderr + + +def _execute_script(script_path: str, script_args: List[str], cwd: str) -> None: + """ + Execute a Python script. + + Args: + script_path: Path to the script to execute + script_args: Arguments to pass to the script + cwd: Current working directory for path resolution + + Raises: + TargetError: If script execution fails + """ + # Make script path absolute if it isn't already + if not os.path.isabs(script_path): + script_path = os.path.join(cwd, script_path) + + if not os.path.isfile(script_path): + raise TargetError(f"Script not found: {script_path}") + + # Replace sys.argv to match original script call + sys.argv = [script_path] + script_args + + try: + with open(script_path, 'rb') as f: + source_code = f.read() + + except FileNotFoundError as e: + raise TargetError(f"Script file not found: {script_path}") from e + except PermissionError as e: + raise TargetError(f"Permission denied reading script: {script_path}") from e + + main_module = types.ModuleType("__main__") + main_module.__file__ = script_path + main_module.__builtins__ = __builtins__ + # gh-140729: Create a __mp_main__ module to allow pickling + sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module + + try: + code = compile(source_code, script_path, 'exec', module='__main__') + except SyntaxError as e: + raise TargetError(f"Syntax error in script {script_path}: {e}") from e + + # Execute the script - let exceptions propagate naturally so Python + # prints the full traceback to stderr + exec(code, main_module.__dict__) + + +def main() -> NoReturn: + """ + Main coordinator function. + + This function coordinates the startup of a target Python process + with the sample profiler by signaling when the process is ready + to be profiled. + """ + # Phase 1: Parse arguments and set up environment + # Errors here are coordinator errors, not script errors + try: + # Parse and validate arguments + sync_port, cwd, target_args = _validate_arguments(sys.argv) + + # Set up execution environment + _setup_environment(cwd) + + # Determine execution type and validate target exists + is_module = target_args[0] == "-m" + if is_module: + if len(target_args) < 2: + raise ArgumentError("Module name required after -m") + module_name = target_args[1] + module_args = target_args[2:] + + if importlib.util.find_spec(module_name) is None: + raise TargetError(f"Module not found: {module_name}") + else: + script_path = target_args[0] + script_args = target_args[1:] + # Match the path resolution logic in _execute_script + check_path = script_path if os.path.isabs(script_path) else os.path.join(cwd, script_path) + if not os.path.isfile(check_path): + raise TargetError(f"Script not found: {script_path}") + + # Signal readiness to profiler + _signal_readiness(sync_port) + + except CoordinatorError as e: + print(f"Profiler coordinator error: {e}", file=sys.stderr) + sys.exit(1) + except KeyboardInterrupt: + print("Interrupted", file=sys.stderr) + sys.exit(1) + + # Phase 2: Execute the target script/module + # Let exceptions propagate naturally so Python prints full tracebacks + if is_module: + _execute_module(module_name, module_args) + else: + _execute_script(script_path, script_args, cwd) + + # Normal exit + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/Lib/profiling/sampling/_vendor/d3-flame-graph/4.1.3/d3-flamegraph-tooltip.min.js b/Lib/profiling/sampling/_vendor/d3-flame-graph/4.1.3/d3-flamegraph-tooltip.min.js new file mode 100644 index 00000000000000..7468437093bb99 --- /dev/null +++ b/Lib/profiling/sampling/_vendor/d3-flame-graph/4.1.3/d3-flamegraph-tooltip.min.js @@ -0,0 +1 @@ +!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.flamegraph=n():(t.flamegraph=t.flamegraph||{},t.flamegraph.tooltip=n())}(self,(function(){return(()=>{"use strict";var t={d:(n,e)=>{for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},o:(t,n)=>Object.prototype.hasOwnProperty.call(t,n),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},n={};function e(){}function r(t){return null==t?e:function(){return this.querySelector(t)}}function i(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function o(){return[]}function u(t){return null==t?o:function(){return this.querySelectorAll(t)}}function a(t){return function(){return this.matches(t)}}function s(t){return function(n){return n.matches(t)}}t.r(n),t.d(n,{defaultFlamegraphTooltip:()=>Ae});var l=Array.prototype.find;function c(){return this.firstElementChild}var f=Array.prototype.filter;function h(){return Array.from(this.children)}function p(t){return new Array(t.length)}function d(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function y(t){return function(){return t}}function v(t,n,e,r,i,o){for(var u,a=0,s=n.length,l=o.length;an?1:t>=n?0:NaN}d.prototype={constructor:d,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,n){return this._parent.insertBefore(t,n)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var b="http://www.w3.org/1999/xhtml";const x={svg:"http://www.w3.org/2000/svg",xhtml:b,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function A(t){var n=t+="",e=n.indexOf(":");return e>=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),x.hasOwnProperty(n)?{space:x[n],local:t}:t}function N(t){return function(){this.removeAttribute(t)}}function k(t){return function(){this.removeAttributeNS(t.space,t.local)}}function M(t,n){return function(){this.setAttribute(t,n)}}function E(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function S(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function C(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function P(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function O(t){return function(){this.style.removeProperty(t)}}function q(t,n,e){return function(){this.style.setProperty(t,n,e)}}function j(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function T(t,n){return t.style.getPropertyValue(n)||P(t).getComputedStyle(t,null).getPropertyValue(n)}function X(t){return function(){delete this[t]}}function R(t,n){return function(){this[t]=n}}function I(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function L(t){return t.trim().split(/^|\s+/)}function D(t){return t.classList||new H(t)}function H(t){this._node=t,this._names=L(t.getAttribute("class")||"")}function Y(t,n){for(var e=D(t),r=-1,i=n.length;++r=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}function st(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var pt=[null];function dt(t,n){this._groups=t,this._parents=n}function yt(){return new dt([[document.documentElement]],pt)}dt.prototype=yt.prototype={constructor:dt,select:function(t){"function"!=typeof t&&(t=r(t));for(var n=this._groups,e=n.length,i=new Array(e),o=0;o=M&&(M=k+1);!(N=b[M])&&++M=0;)(r=i[o])&&(u&&4^r.compareDocumentPosition(u)&&u.parentNode.insertBefore(r,u),u=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=w);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?O:"function"==typeof n?j:q)(t,n,null==e?"":e)):T(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?X:"function"==typeof n?I:R)(t,n)):this.node()[t]},classed:function(t,n){var e=L(t+"");if(arguments.length<2){for(var r=D(this.node()),i=-1,o=e.length;++i{}};function _t(){for(var t,n=0,e=arguments.length,r={};n=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}}))}function bt(t,n){for(var e,r=0,i=t.length;r0)for(var e,r,i=new Array(e),o=0;o=0&&n._call.call(void 0,t),n=n._next;--Mt}()}finally{Mt=0,function(){var t,n,e=Nt,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Nt=n);kt=t,Ht(r)}(),Pt=0}}function Dt(){var t=qt.now(),n=t-Ct;n>1e3&&(Ot-=n,Ct=t)}function Ht(t){Mt||(Et&&(Et=clearTimeout(Et)),t-Pt>24?(t<1/0&&(Et=setTimeout(Lt,t-qt.now()-Ot)),St&&(St=clearInterval(St))):(St||(Ct=qt.now(),St=setInterval(Dt,1e3)),Mt=1,jt(Lt)))}function Yt(t,n,e){var r=new Rt;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}Rt.prototype=It.prototype={constructor:Rt,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?Tt():+e)+(null==n?0:+n),this._next||kt===this||(kt?kt._next=this:Nt=this,kt=this),this._call=t,this._time=e,Ht()},stop:function(){this._call&&(this._call=null,this._time=1/0,Ht())}};var Bt=At("start","end","cancel","interrupt"),$t=[];function Vt(t,n,e,r,i,o){var u=t.__transition;if(u){if(e in u)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(t){e.state=1,e.timer.restart(u,e.delay,e.time),e.delay<=t&&u(t-e.delay)}function u(o){var l,c,f,h;if(1!==e.state)return s();for(l in i)if((h=i[l]).name===e.name){if(3===h.state)return Yt(u);4===h.state?(h.state=6,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[l]):+l0)throw new Error("too late; already scheduled");return e}function Ut(t,n){var e=Ft(t,n);if(e.state>3)throw new Error("too late; already running");return e}function Ft(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function Kt(t,n){return t=+t,n=+n,function(e){return t*(1-e)+n*e}}var Wt,Gt=180/Math.PI,Jt={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Qt(t,n,e,r,i,o){var u,a,s;return(u=Math.sqrt(t*t+n*n))&&(t/=u,n/=u),(s=t*e+n*r)&&(e-=t*s,r-=n*s),(a=Math.sqrt(e*e+r*r))&&(e/=a,r/=a,s/=a),t*r180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Kt(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,u.rotate,a,s),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Kt(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,u.skewX,a,s),function(t,n,e,r,o,u){if(t!==e||n!==r){var a=o.push(i(o)+"scale(",null,",",null,")");u.push({i:a-4,x:Kt(t,e)},{i:a-2,x:Kt(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,u.scaleX,u.scaleY,a,s),o=u=null,function(t){for(var n,e=-1,r=s.length;++e>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?Mn(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?Mn(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=yn.exec(t))?new Cn(n[1],n[2],n[3],1):(n=vn.exec(t))?new Cn(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=gn.exec(t))?Mn(n[1],n[2],n[3],n[4]):(n=_n.exec(t))?Mn(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=mn.exec(t))?jn(n[1],n[2]/100,n[3]/100,1):(n=wn.exec(t))?jn(n[1],n[2]/100,n[3]/100,n[4]):bn.hasOwnProperty(t)?kn(bn[t]):"transparent"===t?new Cn(NaN,NaN,NaN,0):null}function kn(t){return new Cn(t>>16&255,t>>8&255,255&t,1)}function Mn(t,n,e,r){return r<=0&&(t=n=e=NaN),new Cn(t,n,e,r)}function En(t){return t instanceof sn||(t=Nn(t)),t?new Cn((t=t.rgb()).r,t.g,t.b,t.opacity):new Cn}function Sn(t,n,e,r){return 1===arguments.length?En(t):new Cn(t,n,e,null==r?1:r)}function Cn(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Pn(){return"#"+qn(this.r)+qn(this.g)+qn(this.b)}function On(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function qn(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function jn(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Xn(t,n,e,r)}function Tn(t){if(t instanceof Xn)return new Xn(t.h,t.s,t.l,t.opacity);if(t instanceof sn||(t=Nn(t)),!t)return new Xn;if(t instanceof Xn)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,s=(o+i)/2;return a?(u=n===o?(e-r)/a+6*(e0&&s<1?0:u,new Xn(u,a,s,t.opacity)}function Xn(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Rn(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}function In(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}un(sn,Nn,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:xn,formatHex:xn,formatHsl:function(){return Tn(this).formatHsl()},formatRgb:An,toString:An}),un(Cn,Sn,an(sn,{brighter:function(t){return t=null==t?cn:Math.pow(cn,t),new Cn(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?ln:Math.pow(ln,t),new Cn(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Pn,formatHex:Pn,formatRgb:On,toString:On})),un(Xn,(function(t,n,e,r){return 1===arguments.length?Tn(t):new Xn(t,n,e,null==r?1:r)}),an(sn,{brighter:function(t){return t=null==t?cn:Math.pow(cn,t),new Xn(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?ln:Math.pow(ln,t),new Xn(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new Cn(Rn(t>=240?t-240:t+120,i,r),Rn(t,i,r),Rn(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const Ln=t=>()=>t;function Dn(t,n){return function(e){return t+e*n}}function Hn(t){return 1==(t=+t)?Yn:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):Ln(isNaN(n)?e:n)}}function Yn(t,n){var e=n-t;return e?Dn(t,e):Ln(isNaN(t)?n:t)}const Bn=function t(n){var e=Hn(n);function r(t,n){var r=e((t=Sn(t)).r,(n=Sn(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),u=Yn(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=u(n),t+""}}return r.gamma=t,r}(1);function $n(t){return function(n){var e,r,i=n.length,o=new Array(i),u=new Array(i),a=new Array(i);for(e=0;e=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=ro&&(i=n.slice(o,i),a[u]?a[u]+=i:a[++u]=i),(e=e[0])===(r=r[0])?a[u]?a[u]+=r:a[++u]=r:(a[++u]=null,s.push({i:u,x:Kt(e,r)})),o=zn.lastIndex;return o=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?zt:Ut;return function(){var u=o(this,t),a=u.on;a!==r&&(i=(r=a).copy()).on(n,e),u.on=i}}var ce=vt.prototype.constructor;function fe(t){return function(){this.style.removeProperty(t)}}function he(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}function pe(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&he(t,o,e)),r}return o._value=n,o}function de(t){return function(n){this.textContent=t.call(this,n)}}function ye(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&de(r)),n}return r._value=t,r}var ve=0;function ge(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function _e(){return++ve}var me=vt.prototype;ge.prototype=function(t){return vt().transition(t)}.prototype={constructor:ge,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=r(t));for(var i=this._groups,o=i.length,u=new Array(o),a=0;a2&&e.state<5,e.state=6,e.timer.stop(),e.on.call(r?"interrupt":"cancel",t,t.__data__,e.index,e.group),delete o[i]):u=!1;u&&delete t.__transition}}(this,t)}))},vt.prototype.transition=function(t){var n,e;t instanceof ge?(n=t._id,t=t._name):(n=_e(),(e=we).time=Tt(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,o=0;o{"use strict";var t={d:(n,e)=>{for(var r in e)t.o(e,r)&&!t.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:e[r]})},o:(t,n)=>Object.prototype.hasOwnProperty.call(t,n)},n={};function e(){}function r(t){return null==t?e:function(){return this.querySelector(t)}}function i(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function o(){return[]}function u(t){return null==t?o:function(){return this.querySelectorAll(t)}}function a(t){return function(){return this.matches(t)}}function l(t){return function(n){return n.matches(t)}}t.d(n,{default:()=>xr});var s=Array.prototype.find;function c(){return this.firstElementChild}var h=Array.prototype.filter;function f(){return Array.from(this.children)}function p(t){return new Array(t.length)}function d(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function g(t){return function(){return t}}function v(t,n,e,r,i,o){for(var u,a=0,l=n.length,s=o.length;an?1:t>=n?0:NaN}d.prototype={constructor:d,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,n){return this._parent.insertBefore(t,n)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var b="http://www.w3.org/1999/xhtml";const x={svg:"http://www.w3.org/2000/svg",xhtml:b,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function M(t){var n=t+="",e=n.indexOf(":");return e>=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),x.hasOwnProperty(n)?{space:x[n],local:t}:t}function A(t){return function(){this.removeAttribute(t)}}function N(t){return function(){this.removeAttributeNS(t.space,t.local)}}function k(t,n){return function(){this.setAttribute(t,n)}}function E(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function S(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function C(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function j(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function q(t){return function(){this.style.removeProperty(t)}}function O(t,n,e){return function(){this.style.setProperty(t,n,e)}}function P(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function H(t,n){return t.style.getPropertyValue(n)||j(t).getComputedStyle(t,null).getPropertyValue(n)}function L(t){return function(){delete this[t]}}function T(t,n){return function(){this[t]=n}}function D(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function B(t){return t.trim().split(/^|\s+/)}function X(t){return t.classList||new z(t)}function z(t){this._node=t,this._names=B(t.getAttribute("class")||"")}function R(t,n){for(var e=X(t),r=-1,i=n.length;++r=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}function lt(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var pt=[null];function dt(t,n){this._groups=t,this._parents=n}function gt(){return new dt([[document.documentElement]],pt)}dt.prototype=gt.prototype={constructor:dt,select:function(t){"function"!=typeof t&&(t=r(t));for(var n=this._groups,e=n.length,i=new Array(e),o=0;o=k&&(k=N+1);!(A=b[k])&&++k=0;)(r=i[o])&&(u&&4^r.compareDocumentPosition(u)&&u.parentNode.insertBefore(r,u),u=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=_);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?q:"function"==typeof n?P:O)(t,n,null==e?"":e)):H(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?L:"function"==typeof n?D:T)(t,n)):this.node()[t]},classed:function(t,n){var e=B(t+"");if(arguments.length<2){for(var r=X(this.node()),i=-1,o=e.length;++i1?r[0]+r.slice(2):r,+t.slice(e+1)]}function wt(t){return(t=mt(Math.abs(t)))?t[1]:NaN}var _t,bt=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function xt(t){if(!(n=bt.exec(t)))throw new Error("invalid format: "+t);var n;return new Mt({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function Mt(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function At(t,n){var e=mt(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}xt.prototype=Mt.prototype,Mt.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};const Nt={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>At(100*t,n),r:At,s:function(t,n){var e=mt(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(_t=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,u=r.length;return o===u?r:o>u?r+new Array(o-u+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+mt(t,Math.max(0,n+o-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function kt(t){return t}var Et,St,Ct,jt=Array.prototype.map,qt=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function Ot(t){var n,e,r=void 0===t.grouping||void 0===t.thousands?kt:(n=jt.call(t.grouping,Number),e=t.thousands+"",function(t,r){for(var i=t.length,o=[],u=0,a=n[0],l=0;i>0&&a>0&&(l+a+1>r&&(a=Math.max(1,r-l)),o.push(t.substring(i-=a,i+a)),!((l+=a+1)>r));)a=n[u=(u+1)%n.length];return o.reverse().join(e)}),i=void 0===t.currency?"":t.currency[0]+"",o=void 0===t.currency?"":t.currency[1]+"",u=void 0===t.decimal?".":t.decimal+"",a=void 0===t.numerals?kt:function(t){return function(n){return n.replace(/[0-9]/g,(function(n){return t[+n]}))}}(jt.call(t.numerals,String)),l=void 0===t.percent?"%":t.percent+"",s=void 0===t.minus?"−":t.minus+"",c=void 0===t.nan?"NaN":t.nan+"";function h(t){var n=(t=xt(t)).fill,e=t.align,h=t.sign,f=t.symbol,p=t.zero,d=t.width,g=t.comma,v=t.precision,y=t.trim,m=t.type;"n"===m?(g=!0,m="g"):Nt[m]||(void 0===v&&(v=12),y=!0,m="g"),(p||"0"===n&&"="===e)&&(p=!0,n="0",e="=");var w="$"===f?i:"#"===f&&/[boxX]/.test(m)?"0"+m.toLowerCase():"",_="$"===f?o:/[%p]/.test(m)?l:"",b=Nt[m],x=/[defgprs%]/.test(m);function M(t){var i,o,l,f=w,M=_;if("c"===m)M=b(t)+M,t="";else{var A=(t=+t)<0||1/t<0;if(t=isNaN(t)?c:b(Math.abs(t),v),y&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),A&&0==+t&&"+"!==h&&(A=!1),f=(A?"("===h?h:s:"-"===h||"("===h?"":h)+f,M=("s"===m?qt[8+_t/3]:"")+M+(A&&"("===h?")":""),x)for(i=-1,o=t.length;++i(l=t.charCodeAt(i))||l>57){M=(46===l?u+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}g&&!p&&(t=r(t,1/0));var N=f.length+t.length+M.length,k=N>1)+f+t+M+k.slice(N);break;default:t=k+f+t+M}return a(t)}return v=void 0===v?6:/[gprs]/.test(m)?Math.max(1,Math.min(21,v)):Math.max(0,Math.min(20,v)),M.toString=function(){return t+""},M}return{format:h,formatPrefix:function(t,n){var e=h(((t=xt(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(wt(n)/3))),i=Math.pow(10,-r),o=qt[8+r/3];return function(t){return e(i*t)+o}}}}function Pt(t,n){return null==t||null==n?NaN:tn?1:t>=n?0:NaN}function Ht(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)}function Lt(){var t=1,n=1,e=0,r=!1;function i(i){var o=i.height+1;return i.x0=i.y0=e,i.x1=t,i.y1=n/o,i.eachBefore(function(t,n){return function(r){r.children&&function(t,n,e,r,i){for(var o,u=t.children,a=-1,l=u.length,s=t.value&&(r-n)/t.value;++a=0;)n+=e[r].value;else n=1;t.value=n}function Dt(t,n){t instanceof Map?(t=[void 0,t],void 0===n&&(n=Xt)):void 0===n&&(n=Bt);for(var e,r,i,o,u,a=new It(t),l=[a];e=l.pop();)if((i=n(e.data))&&(u=(i=Array.from(i)).length))for(e.children=i,o=u-1;o>=0;--o)l.push(r=i[o]=new It(i[o])),r.parent=e,r.depth=e.depth+1;return a.eachBefore(Rt)}function Bt(t){return t.children}function Xt(t){return Array.isArray(t)?t[1]:null}function zt(t){void 0!==t.data.value&&(t.value=t.data.value),t.data=t.data.data}function Rt(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function It(t){this.data=t,this.depth=this.height=0,this.parent=null}Et=Ot({thousands:",",grouping:[3],currency:["$",""]}),St=Et.format,Ct=Et.formatPrefix,It.prototype=Dt.prototype={constructor:It,count:function(){return this.eachAfter(Tt)},each:function(t,n){let e=-1;for(const r of this)t.call(n,r,++e,this);return this},eachAfter:function(t,n){for(var e,r,i,o=this,u=[o],a=[],l=-1;o=u.pop();)if(a.push(o),e=o.children)for(r=0,i=e.length;r=0;--r)o.push(e[r]);return this},find:function(t,n){let e=-1;for(const r of this)if(t.call(n,r,++e,this))return r},sum:function(t){return this.eachAfter((function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e}))},sort:function(t){return this.eachBefore((function(n){n.children&&n.children.sort(t)}))},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;t=e.pop(),n=r.pop();for(;t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){return Array.from(this)},leaves:function(){var t=[];return this.eachBefore((function(n){n.children||t.push(n)})),t},links:function(){var t=this,n=[];return t.each((function(e){e!==t&&n.push({source:e.parent,target:e})})),n},copy:function(){return Dt(this).eachBefore(zt)},[Symbol.iterator]:function*(){var t,n,e,r,i=this,o=[i];do{for(t=o.reverse(),o=[];i=t.pop();)if(yield i,n=i.children)for(e=0,r=n.length;e=0?(o>=$t?10:o>=Vt?5:o>=Yt?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=$t?10:o>=Vt?5:o>=Yt?2:1)}function Ut(t){let n=t,e=t,r=t;function i(t,n,i=0,o=t.length){if(i>>1;r(t[e],n)<0?i=e+1:o=e}while(it(n)-e,e=Pt,r=(n,e)=>Pt(t(n),e)),{left:i,center:function(t,e,r=0,o=t.length){const u=i(t,e,r,o-1);return u>r&&n(t[u-1],e)>-n(t[u],e)?u-1:u},right:function(t,n,i=0,o=t.length){if(i>>1;r(t[e],n)<=0?i=e+1:o=e}while(i>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?mn(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?mn(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=an.exec(t))?new bn(n[1],n[2],n[3],1):(n=ln.exec(t))?new bn(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=sn.exec(t))?mn(n[1],n[2],n[3],n[4]):(n=cn.exec(t))?mn(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=hn.exec(t))?Nn(n[1],n[2]/100,n[3]/100,1):(n=fn.exec(t))?Nn(n[1],n[2]/100,n[3]/100,n[4]):pn.hasOwnProperty(t)?yn(pn[t]):"transparent"===t?new bn(NaN,NaN,NaN,0):null}function yn(t){return new bn(t>>16&255,t>>8&255,255&t,1)}function mn(t,n,e,r){return r<=0&&(t=n=e=NaN),new bn(t,n,e,r)}function wn(t){return t instanceof Qt||(t=vn(t)),t?new bn((t=t.rgb()).r,t.g,t.b,t.opacity):new bn}function _n(t,n,e,r){return 1===arguments.length?wn(t):new bn(t,n,e,null==r?1:r)}function bn(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function xn(){return"#"+An(this.r)+An(this.g)+An(this.b)}function Mn(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function An(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function Nn(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new En(t,n,e,r)}function kn(t){if(t instanceof En)return new En(t.h,t.s,t.l,t.opacity);if(t instanceof Qt||(t=vn(t)),!t)return new En;if(t instanceof En)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),u=NaN,a=o-i,l=(o+i)/2;return a?(u=n===o?(e-r)/a+6*(e0&&l<1?0:u,new En(u,a,l,t.opacity)}function En(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Sn(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}function Cn(t,n,e,r,i){var o=t*t,u=o*t;return((1-3*t+3*o-u)*n+(4-6*o+3*u)*e+(1+3*t+3*o-3*u)*r+u*i)/6}Wt(Qt,vn,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:dn,formatHex:dn,formatHsl:function(){return kn(this).formatHsl()},formatRgb:gn,toString:gn}),Wt(bn,_n,Jt(Qt,{brighter:function(t){return t=null==t?nn:Math.pow(nn,t),new bn(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?tn:Math.pow(tn,t),new bn(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:xn,formatHex:xn,formatRgb:Mn,toString:Mn})),Wt(En,(function(t,n,e,r){return 1===arguments.length?kn(t):new En(t,n,e,null==r?1:r)}),Jt(Qt,{brighter:function(t){return t=null==t?nn:Math.pow(nn,t),new En(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?tn:Math.pow(tn,t),new En(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new bn(Sn(t>=240?t-240:t+120,i,r),Sn(t,i,r),Sn(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const jn=t=>()=>t;function qn(t,n){return function(e){return t+e*n}}function On(t){return 1==(t=+t)?Pn:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):jn(isNaN(n)?e:n)}}function Pn(t,n){var e=n-t;return e?qn(t,e):jn(isNaN(t)?n:t)}const Hn=function t(n){var e=On(n);function r(t,n){var r=e((t=_n(t)).r,(n=_n(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),u=Pn(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=u(n),t+""}}return r.gamma=t,r}(1);function Ln(t){return function(n){var e,r,i=n.length,o=new Array(i),u=new Array(i),a=new Array(i);for(e=0;e=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],u=r>0?t[r-1]:2*i-o,a=ro&&(i=n.slice(o,i),a[u]?a[u]+=i:a[++u]=i),(e=e[0])===(r=r[0])?a[u]?a[u]+=r:a[++u]=r:(a[++u]=null,l.push({i:u,x:Bn(e,r)})),o=Rn.lastIndex;return on&&(e=t,t=n,n=e),s=function(e){return Math.max(t,Math.min(n,e))}),r=l>2?Wn:Kn,i=o=null,h}function h(n){return null==n||isNaN(n=+n)?e:(i||(i=r(u.map(t),a,l)))(t(s(n)))}return h.invert=function(e){return s(n((o||(o=r(a,u.map(t),Bn)))(e)))},h.domain=function(t){return arguments.length?(u=Array.from(t,Fn),c()):u.slice()},h.range=function(t){return arguments.length?(a=Array.from(t),c()):a.slice()},h.rangeRound=function(t){return a=Array.from(t),l=Yn,c()},h.clamp=function(t){return arguments.length?(s=!!t||Zn,c()):s!==Zn},h.interpolate=function(t){return arguments.length?(l=t,c()):l},h.unknown=function(t){return arguments.length?(e=t,h):e},function(e,r){return t=e,n=r,c()}}function te(){return Qn()(Zn,Zn)}function ne(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}function ee(t,n,e,r){var i,o=function(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=$t?i*=10:o>=Vt?i*=5:o>=Yt&&(i*=2),n0)return[t];if((r=n0){let e=Math.round(t/u),r=Math.round(n/u);for(e*un&&--r,o=new Array(i=r-e+1);++an&&--r,o=new Array(i=r-e+1);++a0;){if((i=Ft(l,s,e))===r)return o[u]=l,o[a]=s,n(o);if(i>0)l=Math.floor(l/i)*i,s=Math.ceil(s/i)*i;else{if(!(i<0))break;l=Math.ceil(l*i)/i,s=Math.floor(s*i)/i}r=i}return t},t}function ie(){var t=te();return t.copy=function(){return Jn(t,ie())},ne.apply(t,arguments),re(t)}function oe(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}var ue={value:()=>{}};function ae(){for(var t,n=0,e=arguments.length,r={};n=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}}))}function ce(t,n){for(var e,r=0,i=t.length;r0)for(var e,r,i=new Array(e),o=0;o=0&&n._call.call(void 0,t),n=n._next;--ge}()}finally{ge=0,function(){var t,n,e=pe,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:pe=n);de=t,Ce(r)}(),we=0}}function Se(){var t=be.now(),n=t-me;n>1e3&&(_e-=n,me=t)}function Ce(t){ge||(ve&&(ve=clearTimeout(ve)),t-we>24?(t<1/0&&(ve=setTimeout(Ee,t-be.now()-_e)),ye&&(ye=clearInterval(ye))):(ye||(me=be.now(),ye=setInterval(Se,1e3)),ge=1,xe(Ee)))}function je(t,n,e){var r=new Ne;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}Ne.prototype=ke.prototype={constructor:Ne,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?Me():+e)+(null==n?0:+n),this._next||de===this||(de?de._next=this:pe=this,de=this),this._call=t,this._time=e,Ce()},stop:function(){this._call&&(this._call=null,this._time=1/0,Ce())}};var qe=fe("start","end","cancel","interrupt"),Oe=[];function Pe(t,n,e,r,i,o){var u=t.__transition;if(u){if(e in u)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(t){e.state=1,e.timer.restart(u,e.delay,e.time),e.delay<=t&&u(t-e.delay)}function u(o){var s,c,h,f;if(1!==e.state)return l();for(s in i)if((f=i[s]).name===e.name){if(3===f.state)return je(u);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[s]):+s0)throw new Error("too late; already scheduled");return e}function Le(t,n){var e=Te(t,n);if(e.state>3)throw new Error("too late; already running");return e}function Te(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}var De,Be=180/Math.PI,Xe={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function ze(t,n,e,r,i,o){var u,a,l;return(u=Math.sqrt(t*t+n*n))&&(t/=u,n/=u),(l=t*e+n*r)&&(e-=t*l,r-=n*l),(a=Math.sqrt(e*e+r*r))&&(e/=a,r/=a,l/=a),t*r180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Bn(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,u.rotate,a,l),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Bn(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,u.skewX,a,l),function(t,n,e,r,o,u){if(t!==e||n!==r){var a=o.push(i(o)+"scale(",null,",",null,")");u.push({i:a-4,x:Bn(t,e)},{i:a-2,x:Bn(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,u.scaleX,u.scaleY,a,l),o=u=null,function(t){for(var n,e=-1,r=l.length;++e=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?He:Le;return function(){var u=o(this,t),a=u.on;a!==r&&(i=(r=a).copy()).on(n,e),u.on=i}}var cr=vt.prototype.constructor;function hr(t){return function(){this.style.removeProperty(t)}}function fr(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}function pr(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&fr(t,o,e)),r}return o._value=n,o}function dr(t){return function(n){this.textContent=t.call(this,n)}}function gr(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&dr(r)),n}return r._value=t,r}var vr=0;function yr(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function mr(){return++vr}var wr=vt.prototype;yr.prototype=function(t){return vt().transition(t)}.prototype={constructor:yr,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=r(t));for(var i=this._groups,o=i.length,u=new Array(o),a=0;a{p&&(p.textContent="search: "+n+" of "+e+" total samples ( "+St(".3f")(n/e*100,3)+"%)")},d()};const E=k;let S=(t,n,e=!1)=>{if(!n)return!1;let r=b(t);e&&(n=n.toLowerCase(),r=r.toLowerCase());const i=new RegExp(n);return void 0!==r&&r&&r.match(i)};const C=S;let j=function(t){p&&(t?p.textContent=t:"function"==typeof d?d():p.textContent="")};const q=j;let O=function(t){return b(t)+" ("+St(".3f")(100*(t.x1-t.x0),3)+"%, "+x(t)+" samples)"},P=function(t){return t.highlight?"#E600E6":function(t,n){let e=_||"warm";_||void 0===n||""===n||(e="red",void 0!==t&&t&&t.match(/::/)&&(e="yellow"),"kernel"===n?e="orange":"jit"===n?e="green":"inlined"===n&&(e="aqua"));const r=function(t){let n=0;if(t){const e=t.split("`");e.length>1&&(t=e[e.length-1]),n=function(t){let n=0,e=0,r=1;if(t){for(let i=0;i6);i++)n+=r*(t.charCodeAt(i)%10),e+=9*r,r*=.7;e>0&&(n/=e)}return n}(t=t.split("(")[0])}return n}(t);return function(t,n){let e,r,i;return"red"===t?(e=200+Math.round(55*n),r=50+Math.round(80*n),i=r):"orange"===t?(e=190+Math.round(65*n),r=90+Math.round(65*n),i=0):"yellow"===t?(e=175+Math.round(55*n),r=e,i=50+Math.round(20*n)):"green"===t?(e=50+Math.round(60*n),r=200+Math.round(55*n),i=e):"pastelgreen"===t?(e=163+Math.round(75*n),r=195+Math.round(49*n),i=72+Math.round(149*n)):"blue"===t?(e=91+Math.round(126*n),r=156+Math.round(76*n),i=221+Math.round(26*n)):"aqua"===t?(e=50+Math.round(60*n),r=165+Math.round(55*n),i=r):"cold"===t?(e=0+Math.round(55*(1-n)),r=0+Math.round(230*(1-n)),i=200+Math.round(55*n)):(e=200+Math.round(55*n),r=0+Math.round(230*(1-n)),i=0+Math.round(55*(1-n))),"rgb("+e+","+r+","+i+")"}(e,r)}(b(t),A(t))};const H=P;function L(t){t.data.fade=!1,t.data.hide=!1,t.children&&t.children.forEach(L)}function T(t){t.parent&&(t.parent.data.fade=!0,T(t.parent))}function D(t){if(i&&i.hide(),function(t){let n,e,r,i=t,o=i.parent;for(;o;){for(n=o.children,e=n.length;e--;)r=n[e],r!==i&&(r.data.hide=!0);i=o,o=i.parent}}(t),L(t),T(t),I(),y){const n=yt(this).select("svg")._groups[0][0].parentNode.offsetTop,r=(window.innerHeight-n)/e,i=(t.height-r+10)*e;window.scrollTo({top:n+i,left:0,behavior:"smooth"})}"function"==typeof c&&c(t)}function B(t,n){if(t.id===n)return t;{const e=M(t);if(e)for(let t=0;t0){const r=t/(n.x1-n.x0);e=e.filter((function(t){return(t.x1-t.x0)*r>f}))}return e}(r),y=yt(this).select("svg");y.attr("width",t);let w=y.selectAll("g").data(g,(function(t){return t.id}));if(!n||v){const t=Math.max.apply(null,g.map((function(t){return t.depth})));n=(t+3)*e,n{D(n)})),w.exit().remove(),w.on("mouseover",(function(t,n){i&&i.show(n,this),j(O(n)),"function"==typeof h&&h(n)})).on("mouseout",(function(){i&&i.hide(),j(null)}))}))}function $(t,n){n.forEach((function(n){const e=t.find((function(t){return t.name===n.name}));e?(e.value+=n.value,n.children&&(e.children||(e.children=[]),$(e.children,n.children))):t.push(n)}))}function V(t){let n,e,r,i,o,u,a,l;const s=[],c=[],h=[],f=!g;let p=t.data;for(p.hide?(t.value=0,e=t.children,e&&h.push(e)):(t.value=p.fade?0:x(p),s.push(t));n=s.pop();)if(e=n.children,e&&(o=e.length)){for(i=0;o--;)a=e[o],p=a.data,p.hide?(a.value=0,r=a.children,r&&h.push(r)):(p.fade?a.value=0:(l=x(p),a.value=l,i+=l),s.push(a));f&&n.value&&(n.value-=i),c.push(e)}for(o=c.length;o--;){for(e=c[o],i=0,u=e.length;u--;)i+=e[u].value;e[0].parent.value+=i}for(;h.length;)for(e=h.pop(),u=e.length;u--;)a=e[u],a.value=0,r=a.children,r&&h.push(r)}function Y(){r.datum((t=>{if("Node"!==t.constructor.name){const n=Dt(t,M);return function(t){let n=0;!function(t,n){n(t);let e=t.children;if(e){const t=[e];let r,i,o;for(;t.length;)for(e=t.pop(),r=e.length;r--;)i=e[r],n(i),o=i.children,o&&t.push(o)}}(t,(function(t){t.id=n++}))}(n),V(n),n.originalValue=n.value,w&&n.eachAfter((t=>{let n=N(t);const e=t.children;let r=e&&e.length;for(;--r>=0;)n+=e[r].delta;t.delta=n})),n}}))}function F(e){if(!arguments.length)return F;r=e,Y(),r.each((function(e){if(0===yt(this).select("svg").size()){const e=yt(this).append("svg:svg").attr("width",t).attr("class","partition d3-flame-graph");n&&(n($([n.data],[t]),n.data))),Y(),I(),F):F},F.update=function(t){return r?(t&&(r.datum(t),Y()),I(),F):F},F.destroy=function(){return r?(i&&(i.hide(),"function"==typeof i.destroy&&i.destroy()),r.selectAll("svg").remove(),F):F},F.setColorMapper=function(t){return arguments.length?(P=n=>{const e=H(n);return t(n,e)},F):(P=H,F)},F.color=F.setColorMapper,F.setColorHue=function(t){return arguments.length?(_=t,F):(_=null,F)},F.minFrameSize=function(t){return arguments.length?(f=t,F):f},F.setDetailsElement=function(t){return arguments.length?(p=t,F):p},F.details=F.setDetailsElement,F.selfValue=function(t){return arguments.length?(g=t,F):g},F.resetHeightOnZoom=function(t){return arguments.length?(v=t,F):v},F.scrollOnZoom=function(t){return arguments.length?(y=t,F):y},F.getName=function(t){return arguments.length?(b=t,F):b},F.getValue=function(t){return arguments.length?(x=t,F):x},F.getChildren=function(t){return arguments.length?(M=t,F):M},F.getLibtype=function(t){return arguments.length?(A=t,F):A},F.getDelta=function(t){return arguments.length?(N=t,F):N},F.setSearchHandler=function(t){return arguments.length?(k=t,F):(k=E,F)},F.setDetailsHandler=function(t){return arguments.length?(j=t,F):(j=q,F)},F.setSearchMatch=function(t){return arguments.length?(S=t,F):(S=C,F)},F}return vt.prototype.interrupt=function(t){return this.each((function(){!function(t,n){var e,r,i,o=t.__transition,u=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>2&&e.state<5,e.state=6,e.timer.stop(),e.on.call(r?"interrupt":"cancel",t,t.__data__,e.index,e.group),delete o[i]):u=!1;u&&delete t.__transition}}(this,t)}))},vt.prototype.transition=function(t){var n,e;t instanceof yr?(n=t._id,t=t._name):(n=mr(),(e=_r).time=Me(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,o=0;on?1:t>=n?0:NaN}function e(t,n){return null==t||null==n?NaN:nt?1:n>=t?0:NaN}function r(t){let r,o,a;function u(t,n,e=0,i=t.length){if(e>>1;o(t[r],n)<0?e=r+1:i=r}while(en(t(e),r),a=(n,e)=>t(n)-e):(r=t===n||t===e?t:i,o=t,a=t),{left:u,center:function(t,n,e=0,r=t.length){const i=u(t,n,e,r-1);return i>e&&a(t[i-1],n)>-a(t[i],n)?i-1:i},right:function(t,n,e=0,i=t.length){if(e>>1;o(t[r],n)<=0?e=r+1:i=r}while(e{n(t,e,(r<<=2)+0,(i<<=2)+0,o<<=2),n(t,e,r+1,i+1,o),n(t,e,r+2,i+2,o),n(t,e,r+3,i+3,o)}}));function d(t){return function(n,e,r=e){if(!((e=+e)>=0))throw new RangeError("invalid rx");if(!((r=+r)>=0))throw new RangeError("invalid ry");let{data:i,width:o,height:a}=n;if(!((o=Math.floor(o))>=0))throw new RangeError("invalid width");if(!((a=Math.floor(void 0!==a?a:i.length/o))>=0))throw new RangeError("invalid height");if(!o||!a||!e&&!r)return n;const u=e&&t(e),c=r&&t(r),f=i.slice();return u&&c?(p(u,f,i,o,a),p(u,i,f,o,a),p(u,f,i,o,a),g(c,i,f,o,a),g(c,f,i,o,a),g(c,i,f,o,a)):u?(p(u,i,f,o,a),p(u,f,i,o,a),p(u,i,f,o,a)):c&&(g(c,i,f,o,a),g(c,f,i,o,a),g(c,i,f,o,a)),n}}function p(t,n,e,r,i){for(let o=0,a=r*i;o{if(!((o-=a)>=i))return;let u=t*r[i];const c=a*t;for(let t=i,n=i+c;t{if(!((a-=u)>=o))return;let c=n*i[o];const f=u*n,s=f+u;for(let t=o,n=o+f;t=n&&++e;else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(i=+i)>=i&&++e}return e}function _(t){return 0|t.length}function b(t){return!(t>0)}function m(t){return"object"!=typeof t||"length"in t?t:Array.from(t)}function x(t,n){let e,r=0,i=0,o=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(e=n-i,i+=e/++r,o+=e*(n-i));else{let a=-1;for(let u of t)null!=(u=n(u,++a,t))&&(u=+u)>=u&&(e=u-i,i+=e/++r,o+=e*(u-i))}if(r>1)return o/(r-1)}function w(t,n){const e=x(t,n);return e?Math.sqrt(e):e}function M(t,n){let e,r;if(void 0===n)for(const n of t)null!=n&&(void 0===e?n>=n&&(e=r=n):(e>n&&(e=n),r=o&&(e=r=o):(e>o&&(e=o),r0){for(o=t[--i];i>0&&(n=o,e=t[--i],o=n+e,r=e-(o-n),!r););i>0&&(r<0&&t[i-1]<0||r>0&&t[i-1]>0)&&(e=2*r,n=o+e,e==n-o&&(o=n))}return o}}class InternMap extends Map{constructor(t,n=N){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const[n,e]of t)this.set(n,e)}get(t){return super.get(A(this,t))}has(t){return super.has(A(this,t))}set(t,n){return super.set(S(this,t),n)}delete(t){return super.delete(E(this,t))}}class InternSet extends Set{constructor(t,n=N){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const n of t)this.add(n)}has(t){return super.has(A(this,t))}add(t){return super.add(S(this,t))}delete(t){return super.delete(E(this,t))}}function A({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):e}function S({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):(t.set(r,e),e)}function E({_intern:t,_key:n},e){const r=n(e);return t.has(r)&&(e=t.get(r),t.delete(r)),e}function N(t){return null!==t&&"object"==typeof t?t.valueOf():t}function k(t){return t}function C(t,...n){return F(t,k,k,n)}function P(t,...n){return F(t,Array.from,k,n)}function z(t,n){for(let e=1,r=n.length;et.pop().map((([n,e])=>[...t,n,e]))));return t}function $(t,n,...e){return F(t,k,n,e)}function D(t,n,...e){return F(t,Array.from,n,e)}function R(t){if(1!==t.length)throw new Error("duplicate key");return t[0]}function F(t,n,e,r){return function t(i,o){if(o>=r.length)return e(i);const a=new InternMap,u=r[o++];let c=-1;for(const t of i){const n=u(t,++c,i),e=a.get(n);e?e.push(t):a.set(n,[t])}for(const[n,e]of a)a.set(n,t(e,o));return n(a)}(t,0)}function q(t,n){return Array.from(n,(n=>t[n]))}function U(t,...n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");t=Array.from(t);let[e]=n;if(e&&2!==e.length||n.length>1){const r=Uint32Array.from(t,((t,n)=>n));return n.length>1?(n=n.map((n=>t.map(n))),r.sort(((t,e)=>{for(const r of n){const n=O(r[t],r[e]);if(n)return n}}))):(e=t.map(e),r.sort(((t,n)=>O(e[t],e[n])))),q(t,r)}return t.sort(I(e))}function I(t=n){if(t===n)return O;if("function"!=typeof t)throw new TypeError("compare is not a function");return(n,e)=>{const r=t(n,e);return r||0===r?r:(0===t(e,e))-(0===t(n,n))}}function O(t,n){return(null==t||!(t>=t))-(null==n||!(n>=n))||(tn?1:0)}var B=Array.prototype.slice;function Y(t){return()=>t}const L=Math.sqrt(50),j=Math.sqrt(10),H=Math.sqrt(2);function X(t,n,e){const r=(n-t)/Math.max(0,e),i=Math.floor(Math.log10(r)),o=r/Math.pow(10,i),a=o>=L?10:o>=j?5:o>=H?2:1;let u,c,f;return i<0?(f=Math.pow(10,-i)/a,u=Math.round(t*f),c=Math.round(n*f),u/fn&&--c,f=-f):(f=Math.pow(10,i)*a,u=Math.round(t/f),c=Math.round(n/f),u*fn&&--c),c0))return[];if((t=+t)===(n=+n))return[t];const r=n=i))return[];const u=o-i+1,c=new Array(u);if(r)if(a<0)for(let t=0;t0?(t=Math.floor(t/i)*i,n=Math.ceil(n/i)*i):i<0&&(t=Math.ceil(t*i)/i,n=Math.floor(n*i)/i),r=i}}function K(t){return Math.max(1,Math.ceil(Math.log(v(t))/Math.LN2)+1)}function Q(){var t=k,n=M,e=K;function r(r){Array.isArray(r)||(r=Array.from(r));var i,o,a,u=r.length,c=new Array(u);for(i=0;i=h)if(t>=h&&n===M){const t=V(l,h,e);isFinite(t)&&(t>0?h=(Math.floor(h/t)+1)*t:t<0&&(h=(Math.ceil(h*-t)+1)/-t))}else d.pop()}for(var p=d.length,g=0,y=p;d[g]<=l;)++g;for(;d[y-1]>h;)--y;(g||y0?d[i-1]:l,v.x1=i0)for(i=0;i=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e=i)&&(e=i)}return e}function tt(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e=o)&&(e=o,r=i);return r}function nt(t,n){let e;if(void 0===n)for(const n of t)null!=n&&(e>n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e>i||void 0===e&&i>=i)&&(e=i)}return e}function et(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e>n||void 0===e&&n>=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e>o||void 0===e&&o>=o)&&(e=o,r=i);return r}function rt(t,n,e=0,r=1/0,i){if(n=Math.floor(n),e=Math.floor(Math.max(0,e)),r=Math.floor(Math.min(t.length-1,r)),!(e<=n&&n<=r))return t;for(i=void 0===i?O:I(i);r>e;){if(r-e>600){const o=r-e+1,a=n-e+1,u=Math.log(o),c=.5*Math.exp(2*u/3),f=.5*Math.sqrt(u*c*(o-c)/o)*(a-o/2<0?-1:1);rt(t,n,Math.max(e,Math.floor(n-a*c/o+f)),Math.min(r,Math.floor(n+(o-a)*c/o+f)),i)}const o=t[n];let a=e,u=r;for(it(t,e,n),i(t[r],o)>0&&it(t,e,r);a0;)--u}0===i(t[e],o)?it(t,e,u):(++u,it(t,u,r)),u<=n&&(e=u+1),n<=u&&(r=u-1)}return t}function it(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function ot(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)>0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)>0:0===e(n,n))&&(r=n,i=!0);return r}function at(t,n,e){if(t=Float64Array.from(function*(t,n){if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(yield n);else{let e=-1;for(let r of t)null!=(r=n(r,++e,t))&&(r=+r)>=r&&(yield r)}}(t,e)),(r=t.length)&&!isNaN(n=+n)){if(n<=0||r<2)return nt(t);if(n>=1)return J(t);var r,i=(r-1)*n,o=Math.floor(i),a=J(rt(t,o).subarray(0,o+1));return a+(nt(t.subarray(o+1))-a)*(i-o)}}function ut(t,n,e=o){if((r=t.length)&&!isNaN(n=+n)){if(n<=0||r<2)return+e(t[0],0,t);if(n>=1)return+e(t[r-1],r-1,t);var r,i=(r-1)*n,a=Math.floor(i),u=+e(t[a],a,t);return u+(+e(t[a+1],a+1,t)-u)*(i-a)}}function ct(t,n,e=o){if(!isNaN(n=+n)){if(r=Float64Array.from(t,((n,r)=>o(e(t[r],r,t)))),n<=0)return et(r);if(n>=1)return tt(r);var r,i=Uint32Array.from(t,((t,n)=>n)),a=r.length-1,u=Math.floor(a*n);return rt(i,u,0,a,((t,n)=>O(r[t],r[n]))),(u=ot(i.subarray(0,u+1),(t=>r[t])))>=0?u:-1}}function ft(t){return Array.from(function*(t){for(const n of t)yield*n}(t))}function st(t,n){return[t,n]}function lt(t,n,e){t=+t,n=+n,e=(i=arguments.length)<2?(n=t,t=0,1):i<3?1:+e;for(var r=-1,i=0|Math.max(0,Math.ceil((n-t)/e)),o=new Array(i);++r+t(n)}function kt(t,n){return n=Math.max(0,t.bandwidth()-2*n)/2,t.round()&&(n=Math.round(n)),e=>+t(e)+n}function Ct(){return!this.__axis}function Pt(t,n){var e=[],r=null,i=null,o=6,a=6,u=3,c="undefined"!=typeof window&&window.devicePixelRatio>1?0:.5,f=t===xt||t===Tt?-1:1,s=t===Tt||t===wt?"x":"y",l=t===xt||t===Mt?St:Et;function h(h){var d=null==r?n.ticks?n.ticks.apply(n,e):n.domain():r,p=null==i?n.tickFormat?n.tickFormat.apply(n,e):mt:i,g=Math.max(o,0)+u,y=n.range(),v=+y[0]+c,_=+y[y.length-1]+c,b=(n.bandwidth?kt:Nt)(n.copy(),c),m=h.selection?h.selection():h,x=m.selectAll(".domain").data([null]),w=m.selectAll(".tick").data(d,n).order(),M=w.exit(),T=w.enter().append("g").attr("class","tick"),A=w.select("line"),S=w.select("text");x=x.merge(x.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),w=w.merge(T),A=A.merge(T.append("line").attr("stroke","currentColor").attr(s+"2",f*o)),S=S.merge(T.append("text").attr("fill","currentColor").attr(s,f*g).attr("dy",t===xt?"0em":t===Mt?"0.71em":"0.32em")),h!==m&&(x=x.transition(h),w=w.transition(h),A=A.transition(h),S=S.transition(h),M=M.transition(h).attr("opacity",At).attr("transform",(function(t){return isFinite(t=b(t))?l(t+c):this.getAttribute("transform")})),T.attr("opacity",At).attr("transform",(function(t){var n=this.parentNode.__axis;return l((n&&isFinite(n=n(t))?n:b(t))+c)}))),M.remove(),x.attr("d",t===Tt||t===wt?a?"M"+f*a+","+v+"H"+c+"V"+_+"H"+f*a:"M"+c+","+v+"V"+_:a?"M"+v+","+f*a+"V"+c+"H"+_+"V"+f*a:"M"+v+","+c+"H"+_),w.attr("opacity",1).attr("transform",(function(t){return l(b(t)+c)})),A.attr(s+"2",f*o),S.attr(s,f*g).text(p),m.filter(Ct).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===wt?"start":t===Tt?"end":"middle"),m.each((function(){this.__axis=b}))}return h.scale=function(t){return arguments.length?(n=t,h):n},h.ticks=function(){return e=Array.from(arguments),h},h.tickArguments=function(t){return arguments.length?(e=null==t?[]:Array.from(t),h):e.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:Array.from(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(o=a=+t,h):o},h.tickSizeInner=function(t){return arguments.length?(o=+t,h):o},h.tickSizeOuter=function(t){return arguments.length?(a=+t,h):a},h.tickPadding=function(t){return arguments.length?(u=+t,h):u},h.offset=function(t){return arguments.length?(c=+t,h):c},h}var zt={value:()=>{}};function $t(){for(var t,n=0,e=arguments.length,r={};n=0&&(n=t.slice(e+1),t=t.slice(0,e)),t&&!r.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))),a=-1,u=o.length;if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++a0)for(var e,r,i=new Array(e),o=0;o=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),Ut.hasOwnProperty(n)?{space:Ut[n],local:t}:t}function Ot(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===qt&&n.documentElement.namespaceURI===qt?n.createElement(t):n.createElementNS(e,t)}}function Bt(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Yt(t){var n=It(t);return(n.local?Bt:Ot)(n)}function Lt(){}function jt(t){return null==t?Lt:function(){return this.querySelector(t)}}function Ht(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function Xt(){return[]}function Gt(t){return null==t?Xt:function(){return this.querySelectorAll(t)}}function Vt(t){return function(){return this.matches(t)}}function Wt(t){return function(n){return n.matches(t)}}var Zt=Array.prototype.find;function Kt(){return this.firstElementChild}var Qt=Array.prototype.filter;function Jt(){return Array.from(this.children)}function tn(t){return new Array(t.length)}function nn(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function en(t,n,e,r,i,o){for(var a,u=0,c=n.length,f=o.length;un?1:t>=n?0:NaN}function cn(t){return function(){this.removeAttribute(t)}}function fn(t){return function(){this.removeAttributeNS(t.space,t.local)}}function sn(t,n){return function(){this.setAttribute(t,n)}}function ln(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function hn(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function dn(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function pn(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function gn(t){return function(){this.style.removeProperty(t)}}function yn(t,n,e){return function(){this.style.setProperty(t,n,e)}}function vn(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function _n(t,n){return t.style.getPropertyValue(n)||pn(t).getComputedStyle(t,null).getPropertyValue(n)}function bn(t){return function(){delete this[t]}}function mn(t,n){return function(){this[t]=n}}function xn(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function wn(t){return t.trim().split(/^|\s+/)}function Mn(t){return t.classList||new Tn(t)}function Tn(t){this._node=t,this._names=wn(t.getAttribute("class")||"")}function An(t,n){for(var e=Mn(t),r=-1,i=n.length;++r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var Gn=[null];function Vn(t,n){this._groups=t,this._parents=n}function Wn(){return new Vn([[document.documentElement]],Gn)}function Zn(t){return"string"==typeof t?new Vn([[document.querySelector(t)]],[document.documentElement]):new Vn([[t]],Gn)}Vn.prototype=Wn.prototype={constructor:Vn,select:function(t){"function"!=typeof t&&(t=jt(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=m&&(m=b+1);!(_=y[m])&&++m=0;)(r=i[o])&&(a&&4^r.compareDocumentPosition(a)&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=un);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?gn:"function"==typeof n?vn:yn)(t,n,null==e?"":e)):_n(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?bn:"function"==typeof n?xn:mn)(t,n)):this.node()[t]},classed:function(t,n){var e=wn(t+"");if(arguments.length<2){for(var r=Mn(this.node()),i=-1,o=e.length;++i=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}(t+""),a=o.length;if(!(arguments.length<2)){for(u=n?Ln:Yn,r=0;r()=>t;function fe(t,{sourceEvent:n,subject:e,target:r,identifier:i,active:o,x:a,y:u,dx:c,dy:f,dispatch:s}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},subject:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:a,enumerable:!0,configurable:!0},y:{value:u,enumerable:!0,configurable:!0},dx:{value:c,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:s}})}function se(t){return!t.ctrlKey&&!t.button}function le(){return this.parentNode}function he(t,n){return null==n?{x:t.x,y:t.y}:n}function de(){return navigator.maxTouchPoints||"ontouchstart"in this}function pe(t,n,e){t.prototype=n.prototype=e,e.constructor=t}function ge(t,n){var e=Object.create(t.prototype);for(var r in n)e[r]=n[r];return e}function ye(){}fe.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var ve=.7,_e=1/ve,be="\\s*([+-]?\\d+)\\s*",me="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*",xe="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*",we=/^#([0-9a-f]{3,8})$/,Me=new RegExp(`^rgb\\(${be},${be},${be}\\)$`),Te=new RegExp(`^rgb\\(${xe},${xe},${xe}\\)$`),Ae=new RegExp(`^rgba\\(${be},${be},${be},${me}\\)$`),Se=new RegExp(`^rgba\\(${xe},${xe},${xe},${me}\\)$`),Ee=new RegExp(`^hsl\\(${me},${xe},${xe}\\)$`),Ne=new RegExp(`^hsla\\(${me},${xe},${xe},${me}\\)$`),ke={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function Ce(){return this.rgb().formatHex()}function Pe(){return this.rgb().formatRgb()}function ze(t){var n,e;return t=(t+"").trim().toLowerCase(),(n=we.exec(t))?(e=n[1].length,n=parseInt(n[1],16),6===e?$e(n):3===e?new qe(n>>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?De(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?De(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=Me.exec(t))?new qe(n[1],n[2],n[3],1):(n=Te.exec(t))?new qe(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=Ae.exec(t))?De(n[1],n[2],n[3],n[4]):(n=Se.exec(t))?De(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=Ee.exec(t))?Le(n[1],n[2]/100,n[3]/100,1):(n=Ne.exec(t))?Le(n[1],n[2]/100,n[3]/100,n[4]):ke.hasOwnProperty(t)?$e(ke[t]):"transparent"===t?new qe(NaN,NaN,NaN,0):null}function $e(t){return new qe(t>>16&255,t>>8&255,255&t,1)}function De(t,n,e,r){return r<=0&&(t=n=e=NaN),new qe(t,n,e,r)}function Re(t){return t instanceof ye||(t=ze(t)),t?new qe((t=t.rgb()).r,t.g,t.b,t.opacity):new qe}function Fe(t,n,e,r){return 1===arguments.length?Re(t):new qe(t,n,e,null==r?1:r)}function qe(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Ue(){return`#${Ye(this.r)}${Ye(this.g)}${Ye(this.b)}`}function Ie(){const t=Oe(this.opacity);return`${1===t?"rgb(":"rgba("}${Be(this.r)}, ${Be(this.g)}, ${Be(this.b)}${1===t?")":`, ${t})`}`}function Oe(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function Be(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function Ye(t){return((t=Be(t))<16?"0":"")+t.toString(16)}function Le(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Xe(t,n,e,r)}function je(t){if(t instanceof Xe)return new Xe(t.h,t.s,t.l,t.opacity);if(t instanceof ye||(t=ze(t)),!t)return new Xe;if(t instanceof Xe)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),a=NaN,u=o-i,c=(o+i)/2;return u?(a=n===o?(e-r)/u+6*(e0&&c<1?0:a,new Xe(a,u,c,t.opacity)}function He(t,n,e,r){return 1===arguments.length?je(t):new Xe(t,n,e,null==r?1:r)}function Xe(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Ge(t){return(t=(t||0)%360)<0?t+360:t}function Ve(t){return Math.max(0,Math.min(1,t||0))}function We(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}pe(ye,ze,{copy(t){return Object.assign(new this.constructor,this,t)},displayable(){return this.rgb().displayable()},hex:Ce,formatHex:Ce,formatHex8:function(){return this.rgb().formatHex8()},formatHsl:function(){return je(this).formatHsl()},formatRgb:Pe,toString:Pe}),pe(qe,Fe,ge(ye,{brighter(t){return t=null==t?_e:Math.pow(_e,t),new qe(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=null==t?ve:Math.pow(ve,t),new qe(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new qe(Be(this.r),Be(this.g),Be(this.b),Oe(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Ue,formatHex:Ue,formatHex8:function(){return`#${Ye(this.r)}${Ye(this.g)}${Ye(this.b)}${Ye(255*(isNaN(this.opacity)?1:this.opacity))}`},formatRgb:Ie,toString:Ie})),pe(Xe,He,ge(ye,{brighter(t){return t=null==t?_e:Math.pow(_e,t),new Xe(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=null==t?ve:Math.pow(ve,t),new Xe(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new qe(We(t>=240?t-240:t+120,i,r),We(t,i,r),We(t<120?t+240:t-120,i,r),this.opacity)},clamp(){return new Xe(Ge(this.h),Ve(this.s),Ve(this.l),Oe(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const t=Oe(this.opacity);return`${1===t?"hsl(":"hsla("}${Ge(this.h)}, ${100*Ve(this.s)}%, ${100*Ve(this.l)}%${1===t?")":`, ${t})`}`}}));const Ze=Math.PI/180,Ke=180/Math.PI,Qe=.96422,Je=1,tr=.82521,nr=4/29,er=6/29,rr=3*er*er,ir=er*er*er;function or(t){if(t instanceof ur)return new ur(t.l,t.a,t.b,t.opacity);if(t instanceof pr)return gr(t);t instanceof qe||(t=Re(t));var n,e,r=lr(t.r),i=lr(t.g),o=lr(t.b),a=cr((.2225045*r+.7168786*i+.0606169*o)/Je);return r===i&&i===o?n=e=a:(n=cr((.4360747*r+.3850649*i+.1430804*o)/Qe),e=cr((.0139322*r+.0971045*i+.7141733*o)/tr)),new ur(116*a-16,500*(n-a),200*(a-e),t.opacity)}function ar(t,n,e,r){return 1===arguments.length?or(t):new ur(t,n,e,null==r?1:r)}function ur(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function cr(t){return t>ir?Math.pow(t,1/3):t/rr+nr}function fr(t){return t>er?t*t*t:rr*(t-nr)}function sr(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function lr(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function hr(t){if(t instanceof pr)return new pr(t.h,t.c,t.l,t.opacity);if(t instanceof ur||(t=or(t)),0===t.a&&0===t.b)return new pr(NaN,0=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],a=r>0?t[r-1]:2*i-o,u=r()=>t;function Cr(t,n){return function(e){return t+e*n}}function Pr(t,n){var e=n-t;return e?Cr(t,e>180||e<-180?e-360*Math.round(e/360):e):kr(isNaN(t)?n:t)}function zr(t){return 1==(t=+t)?$r:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):kr(isNaN(n)?e:n)}}function $r(t,n){var e=n-t;return e?Cr(t,e):kr(isNaN(t)?n:t)}var Dr=function t(n){var e=zr(n);function r(t,n){var r=e((t=Fe(t)).r,(n=Fe(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),a=$r(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=a(n),t+""}}return r.gamma=t,r}(1);function Rr(t){return function(n){var e,r,i=n.length,o=new Array(i),a=new Array(i),u=new Array(i);for(e=0;eo&&(i=n.slice(o,i),u[a]?u[a]+=i:u[++a]=i),(e=e[0])===(r=r[0])?u[a]?u[a]+=r:u[++a]=r:(u[++a]=null,c.push({i:a,x:Yr(e,r)})),o=Hr.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Yr(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,a.rotate,u,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Yr(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,a.skewX,u,c),function(t,n,e,r,o,a){if(t!==e||n!==r){var u=o.push(i(o)+"scale(",null,",",null,")");a.push({i:u-4,x:Yr(t,e)},{i:u-2,x:Yr(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,u,c),o=a=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(void 0,t),n=n._next;--yi}function Ci(){xi=(mi=Mi.now())+wi,yi=vi=0;try{ki()}finally{yi=0,function(){var t,n,e=pi,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:pi=n);gi=t,zi(r)}(),xi=0}}function Pi(){var t=Mi.now(),n=t-mi;n>bi&&(wi-=n,mi=t)}function zi(t){yi||(vi&&(vi=clearTimeout(vi)),t-xi>24?(t<1/0&&(vi=setTimeout(Ci,t-Mi.now()-wi)),_i&&(_i=clearInterval(_i))):(_i||(mi=Mi.now(),_i=setInterval(Pi,bi)),yi=1,Ti(Ci)))}function $i(t,n,e){var r=new Ei;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}Ei.prototype=Ni.prototype={constructor:Ei,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?Ai():+e)+(null==n?0:+n),this._next||gi===this||(gi?gi._next=this:pi=this,gi=this),this._call=t,this._time=e,zi()},stop:function(){this._call&&(this._call=null,this._time=1/0,zi())}};var Di=$t("start","end","cancel","interrupt"),Ri=[],Fi=0,qi=1,Ui=2,Ii=3,Oi=4,Bi=5,Yi=6;function Li(t,n,e,r,i,o){var a=t.__transition;if(a){if(e in a)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(t){e.state=qi,e.timer.restart(a,e.delay,e.time),e.delay<=t&&a(t-e.delay)}function a(o){var f,s,l,h;if(e.state!==qi)return c();for(f in i)if((h=i[f]).name===e.name){if(h.state===Ii)return $i(a);h.state===Oi?(h.state=Yi,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[f]):+fFi)throw new Error("too late; already scheduled");return e}function Hi(t,n){var e=Xi(t,n);if(e.state>Ii)throw new Error("too late; already running");return e}function Xi(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function Gi(t,n){var e,r,i,o=t.__transition,a=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>Ui&&e.state=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?ji:Hi;return function(){var a=o(this,t),u=a.on;u!==r&&(i=(r=u).copy()).on(n,e),a.on=i}}(e,t,n))},attr:function(t,n){var e=It(t),r="transform"===e?ni:Ki;return this.attrTween(t,"function"==typeof n?(e.local?ro:eo)(e,r,Zi(this,"attr."+t,n)):null==n?(e.local?Ji:Qi)(e):(e.local?no:to)(e,r,n))},attrTween:function(t,n){var e="attr."+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if("function"!=typeof n)throw new Error;var r=It(t);return this.tween(e,(r.local?io:oo)(r,n))},style:function(t,n,e){var r="transform"==(t+="")?ti:Ki;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var o=_n(this,t),a=(this.style.removeProperty(t),_n(this,t));return o===a?null:o===e&&a===r?i:i=n(e=o,r=a)}}(t,r)).on("end.style."+t,lo(t)):"function"==typeof n?this.styleTween(t,function(t,n,e){var r,i,o;return function(){var a=_n(this,t),u=e(this),c=u+"";return null==u&&(this.style.removeProperty(t),c=u=_n(this,t)),a===c?null:a===r&&c===i?o:(i=c,o=n(r=a,u))}}(t,r,Zi(this,"style."+t,n))).each(function(t,n){var e,r,i,o,a="style."+n,u="end."+a;return function(){var c=Hi(this,t),f=c.on,s=null==c.value[a]?o||(o=lo(n)):void 0;f===e&&i===s||(r=(e=f).copy()).on(u,i=s),c.on=r}}(this._id,t)):this.styleTween(t,function(t,n,e){var r,i,o=e+"";return function(){var a=_n(this,t);return a===o?null:a===r?i:i=n(r=a,e)}}(t,r,n),e).on("end.style."+t,null)},styleTween:function(t,n,e){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if("function"!=typeof n)throw new Error;return this.tween(r,function(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&function(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}(t,o,e)),r}return o._value=n,o}(t,n,null==e?"":e))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?"":n}}(Zi(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var n="text";if(arguments.length<1)return(n=this.tween(n))&&n._value;if(null==t)return this.tween(n,null);if("function"!=typeof t)throw new Error;return this.tween(n,function(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&function(t){return function(n){this.textContent=t.call(this,n)}}(r)),n}return r._value=t,r}(t))},remove:function(){return this.on("end.remove",function(t){return function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}}(this._id))},tween:function(t,n){var e=this._id;if(t+="",arguments.length<2){for(var r,i=Xi(this.node(),e).tween,o=0,a=i.length;o()=>t;function Qo(t,{sourceEvent:n,target:e,selection:r,mode:i,dispatch:o}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},selection:{value:r,enumerable:!0,configurable:!0},mode:{value:i,enumerable:!0,configurable:!0},_:{value:o}})}function Jo(t){t.preventDefault(),t.stopImmediatePropagation()}var ta={name:"drag"},na={name:"space"},ea={name:"handle"},ra={name:"center"};const{abs:ia,max:oa,min:aa}=Math;function ua(t){return[+t[0],+t[1]]}function ca(t){return[ua(t[0]),ua(t[1])]}var fa={name:"x",handles:["w","e"].map(va),input:function(t,n){return null==t?null:[[+t[0],n[0][1]],[+t[1],n[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},sa={name:"y",handles:["n","s"].map(va),input:function(t,n){return null==t?null:[[n[0][0],+t[0]],[n[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},la={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(va),input:function(t){return null==t?null:ca(t)},output:function(t){return t}},ha={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},da={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},pa={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},ga={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},ya={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function va(t){return{type:t}}function _a(t){return!t.ctrlKey&&!t.button}function ba(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function ma(){return navigator.maxTouchPoints||"ontouchstart"in this}function xa(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function wa(t){var n,e=ba,r=_a,i=ma,o=!0,a=$t("start","brush","end"),u=6;function c(n){var e=n.property("__brush",g).selectAll(".overlay").data([va("overlay")]);e.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",ha.overlay).merge(e).each((function(){var t=xa(this).extent;Zn(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),n.selectAll(".selection").data([va("selection")]).enter().append("rect").attr("class","selection").attr("cursor",ha.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=n.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return ha[t.type]})),n.each(f).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",h).filter(i).on("touchstart.brush",h).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function f(){var t=Zn(this),n=xa(this).selection;n?(t.selectAll(".selection").style("display",null).attr("x",n[0][0]).attr("y",n[0][1]).attr("width",n[1][0]-n[0][0]).attr("height",n[1][1]-n[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?n[1][0]-u/2:n[0][0]-u/2})).attr("y",(function(t){return"s"===t.type[0]?n[1][1]-u/2:n[0][1]-u/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?n[1][0]-n[0][0]+u:u})).attr("height",(function(t){return"e"===t.type||"w"===t.type?n[1][1]-n[0][1]+u:u}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function s(t,n,e){var r=t.__brush.emitter;return!r||e&&r.clean?new l(t,n,e):r}function l(t,n,e){this.that=t,this.args=n,this.state=t.__brush,this.active=0,this.clean=e}function h(e){if((!n||e.touches)&&r.apply(this,arguments)){var i,a,u,c,l,h,d,p,g,y,v,_=this,b=e.target.__data__.type,m="selection"===(o&&e.metaKey?b="overlay":b)?ta:o&&e.altKey?ra:ea,x=t===sa?null:ga[b],w=t===fa?null:ya[b],M=xa(_),T=M.extent,A=M.selection,S=T[0][0],E=T[0][1],N=T[1][0],k=T[1][1],C=0,P=0,z=x&&w&&o&&e.shiftKey,$=Array.from(e.touches||[e],(t=>{const n=t.identifier;return(t=ne(t,_)).point0=t.slice(),t.identifier=n,t}));Gi(_);var D=s(_,arguments,!0).beforestart();if("overlay"===b){A&&(g=!0);const n=[$[0],$[1]||$[0]];M.selection=A=[[i=t===sa?S:aa(n[0][0],n[1][0]),u=t===fa?E:aa(n[0][1],n[1][1])],[l=t===sa?N:oa(n[0][0],n[1][0]),d=t===fa?k:oa(n[0][1],n[1][1])]],$.length>1&&I(e)}else i=A[0][0],u=A[0][1],l=A[1][0],d=A[1][1];a=i,c=u,h=l,p=d;var R=Zn(_).attr("pointer-events","none"),F=R.selectAll(".overlay").attr("cursor",ha[b]);if(e.touches)D.moved=U,D.ended=O;else{var q=Zn(e.view).on("mousemove.brush",U,!0).on("mouseup.brush",O,!0);o&&q.on("keydown.brush",(function(t){switch(t.keyCode){case 16:z=x&&w;break;case 18:m===ea&&(x&&(l=h-C*x,i=a+C*x),w&&(d=p-P*w,u=c+P*w),m=ra,I(t));break;case 32:m!==ea&&m!==ra||(x<0?l=h-C:x>0&&(i=a-C),w<0?d=p-P:w>0&&(u=c-P),m=na,F.attr("cursor",ha.selection),I(t));break;default:return}Jo(t)}),!0).on("keyup.brush",(function(t){switch(t.keyCode){case 16:z&&(y=v=z=!1,I(t));break;case 18:m===ra&&(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=ea,I(t));break;case 32:m===na&&(t.altKey?(x&&(l=h-C*x,i=a+C*x),w&&(d=p-P*w,u=c+P*w),m=ra):(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=ea),F.attr("cursor",ha[b]),I(t));break;default:return}Jo(t)}),!0),ae(e.view)}f.call(_),D.start(e,m.name)}function U(t){for(const n of t.changedTouches||[t])for(const t of $)t.identifier===n.identifier&&(t.cur=ne(n,_));if(z&&!y&&!v&&1===$.length){const t=$[0];ia(t.cur[0]-t[0])>ia(t.cur[1]-t[1])?v=!0:y=!0}for(const t of $)t.cur&&(t[0]=t.cur[0],t[1]=t.cur[1]);g=!0,Jo(t),I(t)}function I(t){const n=$[0],e=n.point0;var r;switch(C=n[0]-e[0],P=n[1]-e[1],m){case na:case ta:x&&(C=oa(S-i,aa(N-l,C)),a=i+C,h=l+C),w&&(P=oa(E-u,aa(k-d,P)),c=u+P,p=d+P);break;case ea:$[1]?(x&&(a=oa(S,aa(N,$[0][0])),h=oa(S,aa(N,$[1][0])),x=1),w&&(c=oa(E,aa(k,$[0][1])),p=oa(E,aa(k,$[1][1])),w=1)):(x<0?(C=oa(S-i,aa(N-i,C)),a=i+C,h=l):x>0&&(C=oa(S-l,aa(N-l,C)),a=i,h=l+C),w<0?(P=oa(E-u,aa(k-u,P)),c=u+P,p=d):w>0&&(P=oa(E-d,aa(k-d,P)),c=u,p=d+P));break;case ra:x&&(a=oa(S,aa(N,i-C*x)),h=oa(S,aa(N,l+C*x))),w&&(c=oa(E,aa(k,u-P*w)),p=oa(E,aa(k,d+P*w)))}ht+e))}function za(t,n){var e=0,r=null,i=null,o=null;function a(a){var u,c=a.length,f=new Array(c),s=Pa(0,c),l=new Array(c*c),h=new Array(c),d=0;a=Float64Array.from({length:c*c},n?(t,n)=>a[n%c][n/c|0]:(t,n)=>a[n/c|0][n%c]);for(let n=0;nr(f[t],f[n])));for(const e of s){const r=n;if(t){const t=Pa(1+~c,c).filter((t=>t<0?a[~t*c+e]:a[e*c+t]));i&&t.sort(((t,n)=>i(t<0?-a[~t*c+e]:a[e*c+t],n<0?-a[~n*c+e]:a[e*c+n])));for(const r of t)if(r<0){(l[~r*c+e]||(l[~r*c+e]={source:null,target:null})).target={index:e,startAngle:n,endAngle:n+=a[~r*c+e]*d,value:a[~r*c+e]}}else{(l[e*c+r]||(l[e*c+r]={source:null,target:null})).source={index:e,startAngle:n,endAngle:n+=a[e*c+r]*d,value:a[e*c+r]}}h[e]={index:e,startAngle:r,endAngle:n,value:f[e]}}else{const t=Pa(0,c).filter((t=>a[e*c+t]||a[t*c+e]));i&&t.sort(((t,n)=>i(a[e*c+t],a[e*c+n])));for(const r of t){let t;if(e=0))throw new Error(`invalid digits: ${t}`);if(n>15)return qa;const e=10**n;return function(t){this._+=t[0];for(let n=1,r=t.length;nRa)if(Math.abs(s*u-c*f)>Ra&&i){let h=e-o,d=r-a,p=u*u+c*c,g=h*h+d*d,y=Math.sqrt(p),v=Math.sqrt(l),_=i*Math.tan(($a-Math.acos((p+l-g)/(2*y*v)))/2),b=_/v,m=_/y;Math.abs(b-1)>Ra&&this._append`L${t+b*f},${n+b*s}`,this._append`A${i},${i},0,0,${+(s*h>f*d)},${this._x1=t+m*u},${this._y1=n+m*c}`}else this._append`L${this._x1=t},${this._y1=n}`;else;}arc(t,n,e,r,i,o){if(t=+t,n=+n,o=!!o,(e=+e)<0)throw new Error(`negative radius: ${e}`);let a=e*Math.cos(r),u=e*Math.sin(r),c=t+a,f=n+u,s=1^o,l=o?r-i:i-r;null===this._x1?this._append`M${c},${f}`:(Math.abs(this._x1-c)>Ra||Math.abs(this._y1-f)>Ra)&&this._append`L${c},${f}`,e&&(l<0&&(l=l%Da+Da),l>Fa?this._append`A${e},${e},0,1,${s},${t-a},${n-u}A${e},${e},0,1,${s},${this._x1=c},${this._y1=f}`:l>Ra&&this._append`A${e},${e},0,${+(l>=$a)},${s},${this._x1=t+e*Math.cos(i)},${this._y1=n+e*Math.sin(i)}`)}rect(t,n,e,r){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${e=+e}v${+r}h${-e}Z`}toString(){return this._}};function Ia(){return new Ua}Ia.prototype=Ua.prototype;var Oa=Array.prototype.slice;function Ba(t){return function(){return t}}function Ya(t){return t.source}function La(t){return t.target}function ja(t){return t.radius}function Ha(t){return t.startAngle}function Xa(t){return t.endAngle}function Ga(){return 0}function Va(){return 10}function Wa(t){var n=Ya,e=La,r=ja,i=ja,o=Ha,a=Xa,u=Ga,c=null;function f(){var f,s=n.apply(this,arguments),l=e.apply(this,arguments),h=u.apply(this,arguments)/2,d=Oa.call(arguments),p=+r.apply(this,(d[0]=s,d)),g=o.apply(this,d)-Ea,y=a.apply(this,d)-Ea,v=+i.apply(this,(d[0]=l,d)),_=o.apply(this,d)-Ea,b=a.apply(this,d)-Ea;if(c||(c=f=Ia()),h>Ca&&(Ma(y-g)>2*h+Ca?y>g?(g+=h,y-=h):(g-=h,y+=h):g=y=(g+y)/2,Ma(b-_)>2*h+Ca?b>_?(_+=h,b-=h):(_-=h,b+=h):_=b=(_+b)/2),c.moveTo(p*Ta(g),p*Aa(g)),c.arc(0,0,p,g,y),g!==_||y!==b)if(t){var m=v-+t.apply(this,arguments),x=(_+b)/2;c.quadraticCurveTo(0,0,m*Ta(_),m*Aa(_)),c.lineTo(v*Ta(x),v*Aa(x)),c.lineTo(m*Ta(b),m*Aa(b))}else c.quadraticCurveTo(0,0,v*Ta(_),v*Aa(_)),c.arc(0,0,v,_,b);if(c.quadraticCurveTo(0,0,p*Ta(g),p*Aa(g)),c.closePath(),f)return c=null,f+""||null}return t&&(f.headRadius=function(n){return arguments.length?(t="function"==typeof n?n:Ba(+n),f):t}),f.radius=function(t){return arguments.length?(r=i="function"==typeof t?t:Ba(+t),f):r},f.sourceRadius=function(t){return arguments.length?(r="function"==typeof t?t:Ba(+t),f):r},f.targetRadius=function(t){return arguments.length?(i="function"==typeof t?t:Ba(+t),f):i},f.startAngle=function(t){return arguments.length?(o="function"==typeof t?t:Ba(+t),f):o},f.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:Ba(+t),f):a},f.padAngle=function(t){return arguments.length?(u="function"==typeof t?t:Ba(+t),f):u},f.source=function(t){return arguments.length?(n=t,f):n},f.target=function(t){return arguments.length?(e=t,f):e},f.context=function(t){return arguments.length?(c=null==t?null:t,f):c},f}var Za=Array.prototype.slice;function Ka(t,n){return t-n}var Qa=t=>()=>t;function Ja(t,n){for(var e,r=-1,i=n.length;++rr!=d>r&&e<(h-f)*(r-s)/(d-s)+f&&(i=-i)}return i}function nu(t,n,e){var r,i,o,a;return function(t,n,e){return(n[0]-t[0])*(e[1]-t[1])==(e[0]-t[0])*(n[1]-t[1])}(t,n,e)&&(i=t[r=+(t[0]===n[0])],o=e[r],a=n[r],i<=o&&o<=a||a<=o&&o<=i)}function eu(){}var ru=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function iu(){var t=1,n=1,e=K,r=u;function i(t){var n=e(t);if(Array.isArray(n))n=n.slice().sort(Ka);else{const e=M(t,ou);for(n=G(...Z(e[0],e[1],n),n);n[n.length-1]>=e[1];)n.pop();for(;n[1]o(t,n)))}function o(e,i){const o=null==i?NaN:+i;if(isNaN(o))throw new Error(`invalid value: ${i}`);var u=[],c=[];return function(e,r,i){var o,u,c,f,s,l,h=new Array,d=new Array;o=u=-1,f=au(e[0],r),ru[f<<1].forEach(p);for(;++o=r,ru[s<<2].forEach(p);for(;++o0?u.push([t]):c.push(t)})),c.forEach((function(t){for(var n,e=0,r=u.length;e0&&o0&&a=0&&o>=0))throw new Error("invalid size");return t=r,n=o,i},i.thresholds=function(t){return arguments.length?(e="function"==typeof t?t:Array.isArray(t)?Qa(Za.call(t)):Qa(t),i):e},i.smooth=function(t){return arguments.length?(r=t?u:eu,i):r===u},i}function ou(t){return isFinite(t)?t:NaN}function au(t,n){return null!=t&&+t>=n}function uu(t){return null==t||isNaN(t=+t)?-1/0:t}function cu(t,n,e,r){const i=r-n,o=e-n,a=isFinite(i)||isFinite(o)?i/o:Math.sign(i)/Math.sign(o);return isNaN(a)?t:t+a-.5}function fu(t){return t[0]}function su(t){return t[1]}function lu(){return 1}const hu=134217729,du=33306690738754706e-32;function pu(t,n,e,r,i){let o,a,u,c,f=n[0],s=r[0],l=0,h=0;s>f==s>-f?(o=f,f=n[++l]):(o=s,s=r[++h]);let d=0;if(lf==s>-f?(a=f+o,u=o-(a-f),f=n[++l]):(a=s+o,u=o-(a-s),s=r[++h]),o=a,0!==u&&(i[d++]=u);lf==s>-f?(a=o+f,c=a-o,u=o-(a-c)+(f-c),f=n[++l]):(a=o+s,c=a-o,u=o-(a-c)+(s-c),s=r[++h]),o=a,0!==u&&(i[d++]=u);for(;l=33306690738754716e-32*f?c:-function(t,n,e,r,i,o,a){let u,c,f,s,l,h,d,p,g,y,v,_,b,m,x,w,M,T;const A=t-i,S=e-i,E=n-o,N=r-o;m=A*N,h=hu*A,d=h-(h-A),p=A-d,h=hu*N,g=h-(h-N),y=N-g,x=p*y-(m-d*g-p*g-d*y),w=E*S,h=hu*E,d=h-(h-E),p=E-d,h=hu*S,g=h-(h-S),y=S-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,_u[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,_u[1]=b-(v+l)+(l-w),T=_+v,l=T-_,_u[2]=_-(T-l)+(v-l),_u[3]=T;let k=function(t,n){let e=n[0];for(let r=1;r=C||-k>=C)return k;if(l=t-A,u=t-(A+l)+(l-i),l=e-S,f=e-(S+l)+(l-i),l=n-E,c=n-(E+l)+(l-o),l=r-N,s=r-(N+l)+(l-o),0===u&&0===c&&0===f&&0===s)return k;if(C=vu*a+du*Math.abs(k),k+=A*s+N*u-(E*f+S*c),k>=C||-k>=C)return k;m=u*N,h=hu*u,d=h-(h-u),p=u-d,h=hu*N,g=h-(h-N),y=N-g,x=p*y-(m-d*g-p*g-d*y),w=c*S,h=hu*c,d=h-(h-c),p=c-d,h=hu*S,g=h-(h-S),y=S-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,wu[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,wu[1]=b-(v+l)+(l-w),T=_+v,l=T-_,wu[2]=_-(T-l)+(v-l),wu[3]=T;const P=pu(4,_u,4,wu,bu);m=A*s,h=hu*A,d=h-(h-A),p=A-d,h=hu*s,g=h-(h-s),y=s-g,x=p*y-(m-d*g-p*g-d*y),w=E*f,h=hu*E,d=h-(h-E),p=E-d,h=hu*f,g=h-(h-f),y=f-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,wu[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,wu[1]=b-(v+l)+(l-w),T=_+v,l=T-_,wu[2]=_-(T-l)+(v-l),wu[3]=T;const z=pu(P,bu,4,wu,mu);m=u*s,h=hu*u,d=h-(h-u),p=u-d,h=hu*s,g=h-(h-s),y=s-g,x=p*y-(m-d*g-p*g-d*y),w=c*f,h=hu*c,d=h-(h-c),p=c-d,h=hu*f,g=h-(h-f),y=f-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,wu[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,wu[1]=b-(v+l)+(l-w),T=_+v,l=T-_,wu[2]=_-(T-l)+(v-l),wu[3]=T;const $=pu(z,mu,4,wu,xu);return xu[$-1]}(t,n,e,r,i,o,f)}const Tu=Math.pow(2,-52),Au=new Uint32Array(512);class Su{static from(t,n=zu,e=$u){const r=t.length,i=new Float64Array(2*r);for(let o=0;o>1;if(n>0&&"number"!=typeof t[0])throw new Error("Expected coords to contain numbers.");this.coords=t;const e=Math.max(2*n-5,0);this._triangles=new Uint32Array(3*e),this._halfedges=new Int32Array(3*e),this._hashSize=Math.ceil(Math.sqrt(n)),this._hullPrev=new Uint32Array(n),this._hullNext=new Uint32Array(n),this._hullTri=new Uint32Array(n),this._hullHash=new Int32Array(this._hashSize).fill(-1),this._ids=new Uint32Array(n),this._dists=new Float64Array(n),this.update()}update(){const{coords:t,_hullPrev:n,_hullNext:e,_hullTri:r,_hullHash:i}=this,o=t.length>>1;let a=1/0,u=1/0,c=-1/0,f=-1/0;for(let n=0;nc&&(c=e),r>f&&(f=r),this._ids[n]=n}const s=(a+c)/2,l=(u+f)/2;let h,d,p,g=1/0;for(let n=0;n0&&(d=n,g=e)}let _=t[2*d],b=t[2*d+1],m=1/0;for(let n=0;nr&&(n[e++]=i,r=this._dists[i])}return this.hull=n.subarray(0,e),this.triangles=new Uint32Array(0),void(this.halfedges=new Uint32Array(0))}if(Mu(y,v,_,b,x,w)<0){const t=d,n=_,e=b;d=p,_=x,b=w,p=t,x=n,w=e}const M=function(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c),d=t+(f*s-u*l)*h,p=n+(a*l-c*s)*h;return{x:d,y:p}}(y,v,_,b,x,w);this._cx=M.x,this._cy=M.y;for(let n=0;n0&&Math.abs(f-o)<=Tu&&Math.abs(s-a)<=Tu)continue;if(o=f,a=s,c===h||c===d||c===p)continue;let l=0;for(let t=0,n=this._hashKey(f,s);t=0;)if(y=g,y===l){y=-1;break}if(-1===y)continue;let v=this._addTriangle(y,c,e[y],-1,-1,r[y]);r[c]=this._legalize(v+2),r[y]=v,T++;let _=e[y];for(;g=e[_],Mu(f,s,t[2*_],t[2*_+1],t[2*g],t[2*g+1])<0;)v=this._addTriangle(_,c,g,r[c],-1,r[_]),r[c]=this._legalize(v+2),e[_]=_,T--,_=g;if(y===l)for(;g=n[y],Mu(f,s,t[2*g],t[2*g+1],t[2*y],t[2*y+1])<0;)v=this._addTriangle(g,c,y,-1,r[y],r[g]),this._legalize(v+2),r[g]=v,e[y]=y,T--,y=g;this._hullStart=n[c]=y,e[y]=n[_]=c,e[c]=_,i[this._hashKey(f,s)]=c,i[this._hashKey(t[2*y],t[2*y+1])]=y}this.hull=new Uint32Array(T);for(let t=0,n=this._hullStart;t0?3-e:1+e)/4}(t-this._cx,n-this._cy)*this._hashSize)%this._hashSize}_legalize(t){const{_triangles:n,_halfedges:e,coords:r}=this;let i=0,o=0;for(;;){const a=e[t],u=t-t%3;if(o=u+(t+2)%3,-1===a){if(0===i)break;t=Au[--i];continue}const c=a-a%3,f=u+(t+1)%3,s=c+(a+2)%3,l=n[o],h=n[t],d=n[f],p=n[s];if(Nu(r[2*l],r[2*l+1],r[2*h],r[2*h+1],r[2*d],r[2*d+1],r[2*p],r[2*p+1])){n[t]=p,n[a]=l;const r=e[s];if(-1===r){let n=this._hullStart;do{if(this._hullTri[n]===s){this._hullTri[n]=t;break}n=this._hullPrev[n]}while(n!==this._hullStart)}this._link(t,r),this._link(a,e[o]),this._link(o,s);const u=c+(a+1)%3;i=e&&n[t[a]]>o;)t[a+1]=t[a--];t[a+1]=r}else{let i=e+1,o=r;Pu(t,e+r>>1,i),n[t[e]]>n[t[r]]&&Pu(t,e,r),n[t[i]]>n[t[r]]&&Pu(t,i,r),n[t[e]]>n[t[i]]&&Pu(t,e,i);const a=t[i],u=n[a];for(;;){do{i++}while(n[t[i]]u);if(o=o-e?(Cu(t,n,i,r),Cu(t,n,e,o-1)):(Cu(t,n,e,o-1),Cu(t,n,i,r))}}function Pu(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function zu(t){return t[0]}function $u(t){return t[1]}const Du=1e-6;class Ru{constructor(){this._x0=this._y0=this._x1=this._y1=null,this._=""}moveTo(t,n){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}`}closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")}lineTo(t,n){this._+=`L${this._x1=+t},${this._y1=+n}`}arc(t,n,e){const r=(t=+t)+(e=+e),i=n=+n;if(e<0)throw new Error("negative radius");null===this._x1?this._+=`M${r},${i}`:(Math.abs(this._x1-r)>Du||Math.abs(this._y1-i)>Du)&&(this._+="L"+r+","+i),e&&(this._+=`A${e},${e},0,1,1,${t-e},${n}A${e},${e},0,1,1,${this._x1=r},${this._y1=i}`)}rect(t,n,e,r){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${+e}v${+r}h${-e}Z`}value(){return this._||null}}class Fu{constructor(){this._=[]}moveTo(t,n){this._.push([t,n])}closePath(){this._.push(this._[0].slice())}lineTo(t,n){this._.push([t,n])}value(){return this._.length?this._:null}}class qu{constructor(t,[n,e,r,i]=[0,0,960,500]){if(!((r=+r)>=(n=+n)&&(i=+i)>=(e=+e)))throw new Error("invalid bounds");this.delaunay=t,this._circumcenters=new Float64Array(2*t.points.length),this.vectors=new Float64Array(2*t.points.length),this.xmax=r,this.xmin=n,this.ymax=i,this.ymin=e,this._init()}update(){return this.delaunay.update(),this._init(),this}_init(){const{delaunay:{points:t,hull:n,triangles:e},vectors:r}=this;let i,o;const a=this.circumcenters=this._circumcenters.subarray(0,e.length/3*2);for(let r,u,c=0,f=0,s=e.length;c1;)i-=2;for(let t=2;t0){if(n>=this.ymax)return null;(i=(this.ymax-n)/r)0){if(t>=this.xmax)return null;(i=(this.xmax-t)/e)this.xmax?2:0)|(nthis.ymax?8:0)}_simplify(t){if(t&&t.length>4){for(let n=0;n2&&function(t){const{triangles:n,coords:e}=t;for(let t=0;t1e-10)return!1}return!0}(t)){this.collinear=Int32Array.from({length:n.length/2},((t,n)=>n)).sort(((t,e)=>n[2*t]-n[2*e]||n[2*t+1]-n[2*e+1]));const t=this.collinear[0],e=this.collinear[this.collinear.length-1],r=[n[2*t],n[2*t+1],n[2*e],n[2*e+1]],i=1e-8*Math.hypot(r[3]-r[1],r[2]-r[0]);for(let t=0,e=n.length/2;t0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=r[0],o[r[0]]=1,2===r.length&&(o[r[1]]=0,this.triangles[1]=r[1],this.triangles[2]=r[1]))}voronoi(t){return new qu(this,t)}*neighbors(t){const{inedges:n,hull:e,_hullIndex:r,halfedges:i,triangles:o,collinear:a}=this;if(a){const n=a.indexOf(t);return n>0&&(yield a[n-1]),void(n=0&&i!==e&&i!==r;)e=i;return i}_step(t,n,e){const{inedges:r,hull:i,_hullIndex:o,halfedges:a,triangles:u,points:c}=this;if(-1===r[t]||!c.length)return(t+1)%(c.length>>1);let f=t,s=Iu(n-c[2*t],2)+Iu(e-c[2*t+1],2);const l=r[t];let h=l;do{let r=u[h];const l=Iu(n-c[2*r],2)+Iu(e-c[2*r+1],2);if(l9999?"+"+Ku(n,6):Ku(n,4))+"-"+Ku(t.getUTCMonth()+1,2)+"-"+Ku(t.getUTCDate(),2)+(o?"T"+Ku(e,2)+":"+Ku(r,2)+":"+Ku(i,2)+"."+Ku(o,3)+"Z":i?"T"+Ku(e,2)+":"+Ku(r,2)+":"+Ku(i,2)+"Z":r||e?"T"+Ku(e,2)+":"+Ku(r,2)+"Z":"")}function Ju(t){var n=new RegExp('["'+t+"\n\r]"),e=t.charCodeAt(0);function r(t,n){var r,i=[],o=t.length,a=0,u=0,c=o<=0,f=!1;function s(){if(c)return Hu;if(f)return f=!1,ju;var n,r,i=a;if(t.charCodeAt(i)===Xu){for(;a++=o?c=!0:(r=t.charCodeAt(a++))===Gu?f=!0:r===Vu&&(f=!0,t.charCodeAt(a)===Gu&&++a),t.slice(i+1,n-1).replace(/""/g,'"')}for(;amc(n,e).then((n=>(new DOMParser).parseFromString(n,t)))}var Sc=Ac("application/xml"),Ec=Ac("text/html"),Nc=Ac("image/svg+xml");function kc(t,n,e,r){if(isNaN(n)||isNaN(e))return t;var i,o,a,u,c,f,s,l,h,d=t._root,p={data:r},g=t._x0,y=t._y0,v=t._x1,_=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a,i=d,!(d=d[l=s<<1|f]))return i[l]=p,t;if(u=+t._x.call(null,d.data),c=+t._y.call(null,d.data),n===u&&e===c)return p.next=d,i?i[l]=p:t._root=p,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a}while((l=s<<1|f)==(h=(c>=a)<<1|u>=o));return i[h]=d,i[l]=p,t}function Cc(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function Pc(t){return t[0]}function zc(t){return t[1]}function $c(t,n,e){var r=new Dc(null==n?Pc:n,null==e?zc:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function Dc(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function Rc(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}var Fc=$c.prototype=Dc.prototype;function qc(t){return function(){return t}}function Uc(t){return 1e-6*(t()-.5)}function Ic(t){return t.x+t.vx}function Oc(t){return t.y+t.vy}function Bc(t){return t.index}function Yc(t,n){var e=t.get(n);if(!e)throw new Error("node not found: "+n);return e}Fc.copy=function(){var t,n,e=new Dc(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=Rc(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=Rc(n));return e},Fc.add=function(t){const n=+this._x.call(null,t),e=+this._y.call(null,t);return kc(this.cover(n,e),n,e,t)},Fc.addAll=function(t){var n,e,r,i,o=t.length,a=new Array(o),u=new Array(o),c=1/0,f=1/0,s=-1/0,l=-1/0;for(e=0;es&&(s=r),il&&(l=i));if(c>s||f>l)return this;for(this.cover(c,f).cover(s,l),e=0;et||t>=i||r>n||n>=o;)switch(u=(nh||(o=c.y0)>d||(a=c.x1)=v)<<1|t>=y)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-f],p[p.length-1-f]=c)}else{var _=t-+this._x.call(null,g.data),b=n-+this._y.call(null,g.data),m=_*_+b*b;if(m=(u=(p+y)/2))?p=u:y=u,(s=a>=(c=(g+v)/2))?g=c:v=c,n=d,!(d=d[l=s<<1|f]))return this;if(!d.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(d=n[0]||n[1]||n[2]||n[3])&&d===(n[3]||n[2]||n[1]||n[0])&&!d.length&&(e?e[h]=d:this._root=d),this):(this._root=i,this)},Fc.removeAll=function(t){for(var n=0,e=t.length;n1?r[0]+r.slice(2):r,+t.slice(e+1)]}function Zc(t){return(t=Wc(Math.abs(t)))?t[1]:NaN}var Kc,Qc=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Jc(t){if(!(n=Qc.exec(t)))throw new Error("invalid format: "+t);var n;return new tf({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function tf(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function nf(t,n){var e=Wc(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}Jc.prototype=tf.prototype,tf.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var ef={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>nf(100*t,n),r:nf,s:function(t,n){var e=Wc(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(Kc=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,a=r.length;return o===a?r:o>a?r+new Array(o-a+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+Wc(t,Math.max(0,n+o-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function rf(t){return t}var of,af=Array.prototype.map,uf=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function cf(t){var n,e,r=void 0===t.grouping||void 0===t.thousands?rf:(n=af.call(t.grouping,Number),e=t.thousands+"",function(t,r){for(var i=t.length,o=[],a=0,u=n[0],c=0;i>0&&u>0&&(c+u+1>r&&(u=Math.max(1,r-c)),o.push(t.substring(i-=u,i+u)),!((c+=u+1)>r));)u=n[a=(a+1)%n.length];return o.reverse().join(e)}),i=void 0===t.currency?"":t.currency[0]+"",o=void 0===t.currency?"":t.currency[1]+"",a=void 0===t.decimal?".":t.decimal+"",u=void 0===t.numerals?rf:function(t){return function(n){return n.replace(/[0-9]/g,(function(n){return t[+n]}))}}(af.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",f=void 0===t.minus?"−":t.minus+"",s=void 0===t.nan?"NaN":t.nan+"";function l(t){var n=(t=Jc(t)).fill,e=t.align,l=t.sign,h=t.symbol,d=t.zero,p=t.width,g=t.comma,y=t.precision,v=t.trim,_=t.type;"n"===_?(g=!0,_="g"):ef[_]||(void 0===y&&(y=12),v=!0,_="g"),(d||"0"===n&&"="===e)&&(d=!0,n="0",e="=");var b="$"===h?i:"#"===h&&/[boxX]/.test(_)?"0"+_.toLowerCase():"",m="$"===h?o:/[%p]/.test(_)?c:"",x=ef[_],w=/[defgprs%]/.test(_);function M(t){var i,o,c,h=b,M=m;if("c"===_)M=x(t)+M,t="";else{var T=(t=+t)<0||1/t<0;if(t=isNaN(t)?s:x(Math.abs(t),y),v&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),T&&0==+t&&"+"!==l&&(T=!1),h=(T?"("===l?l:f:"-"===l||"("===l?"":l)+h,M=("s"===_?uf[8+Kc/3]:"")+M+(T&&"("===l?")":""),w)for(i=-1,o=t.length;++i(c=t.charCodeAt(i))||c>57){M=(46===c?a+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}g&&!d&&(t=r(t,1/0));var A=h.length+t.length+M.length,S=A>1)+h+t+M+S.slice(A);break;default:t=S+h+t+M}return u(t)}return y=void 0===y?6:/[gprs]/.test(_)?Math.max(1,Math.min(21,y)):Math.max(0,Math.min(20,y)),M.toString=function(){return t+""},M}return{format:l,formatPrefix:function(t,n){var e=l(((t=Jc(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(Zc(n)/3))),i=Math.pow(10,-r),o=uf[8+r/3];return function(t){return e(i*t)+o}}}}function ff(n){return of=cf(n),t.format=of.format,t.formatPrefix=of.formatPrefix,of}function sf(t){return Math.max(0,-Zc(Math.abs(t)))}function lf(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(Zc(n)/3)))-Zc(Math.abs(t)))}function hf(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,Zc(n)-Zc(t))+1}t.format=void 0,t.formatPrefix=void 0,ff({thousands:",",grouping:[3],currency:["$",""]});var df=1e-6,pf=1e-12,gf=Math.PI,yf=gf/2,vf=gf/4,_f=2*gf,bf=180/gf,mf=gf/180,xf=Math.abs,wf=Math.atan,Mf=Math.atan2,Tf=Math.cos,Af=Math.ceil,Sf=Math.exp,Ef=Math.hypot,Nf=Math.log,kf=Math.pow,Cf=Math.sin,Pf=Math.sign||function(t){return t>0?1:t<0?-1:0},zf=Math.sqrt,$f=Math.tan;function Df(t){return t>1?0:t<-1?gf:Math.acos(t)}function Rf(t){return t>1?yf:t<-1?-yf:Math.asin(t)}function Ff(t){return(t=Cf(t/2))*t}function qf(){}function Uf(t,n){t&&Of.hasOwnProperty(t.type)&&Of[t.type](t,n)}var If={Feature:function(t,n){Uf(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r=0?1:-1,i=r*e,o=Tf(n=(n*=mf)/2+vf),a=Cf(n),u=Vf*a,c=Gf*o+u*Tf(i),f=u*r*Cf(i);as.add(Mf(f,c)),Xf=t,Gf=o,Vf=a}function ds(t){return[Mf(t[1],t[0]),Rf(t[2])]}function ps(t){var n=t[0],e=t[1],r=Tf(e);return[r*Tf(n),r*Cf(n),Cf(e)]}function gs(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function ys(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function vs(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function _s(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function bs(t){var n=zf(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}var ms,xs,ws,Ms,Ts,As,Ss,Es,Ns,ks,Cs,Ps,zs,$s,Ds,Rs,Fs={point:qs,lineStart:Is,lineEnd:Os,polygonStart:function(){Fs.point=Bs,Fs.lineStart=Ys,Fs.lineEnd=Ls,rs=new T,cs.polygonStart()},polygonEnd:function(){cs.polygonEnd(),Fs.point=qs,Fs.lineStart=Is,Fs.lineEnd=Os,as<0?(Wf=-(Kf=180),Zf=-(Qf=90)):rs>df?Qf=90:rs<-df&&(Zf=-90),os[0]=Wf,os[1]=Kf},sphere:function(){Wf=-(Kf=180),Zf=-(Qf=90)}};function qs(t,n){is.push(os=[Wf=t,Kf=t]),nQf&&(Qf=n)}function Us(t,n){var e=ps([t*mf,n*mf]);if(es){var r=ys(es,e),i=ys([r[1],-r[0],0],r);bs(i),i=ds(i);var o,a=t-Jf,u=a>0?1:-1,c=i[0]*bf*u,f=xf(a)>180;f^(u*JfQf&&(Qf=o):f^(u*Jf<(c=(c+360)%360-180)&&cQf&&(Qf=n)),f?tjs(Wf,Kf)&&(Kf=t):js(t,Kf)>js(Wf,Kf)&&(Wf=t):Kf>=Wf?(tKf&&(Kf=t)):t>Jf?js(Wf,t)>js(Wf,Kf)&&(Kf=t):js(t,Kf)>js(Wf,Kf)&&(Wf=t)}else is.push(os=[Wf=t,Kf=t]);nQf&&(Qf=n),es=e,Jf=t}function Is(){Fs.point=Us}function Os(){os[0]=Wf,os[1]=Kf,Fs.point=qs,es=null}function Bs(t,n){if(es){var e=t-Jf;rs.add(xf(e)>180?e+(e>0?360:-360):e)}else ts=t,ns=n;cs.point(t,n),Us(t,n)}function Ys(){cs.lineStart()}function Ls(){Bs(ts,ns),cs.lineEnd(),xf(rs)>df&&(Wf=-(Kf=180)),os[0]=Wf,os[1]=Kf,es=null}function js(t,n){return(n-=t)<0?n+360:n}function Hs(t,n){return t[0]-n[0]}function Xs(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:ngf&&(t-=Math.round(t/_f)*_f),[t,n]}function ul(t,n,e){return(t%=_f)?n||e?ol(fl(t),sl(n,e)):fl(t):n||e?sl(n,e):al}function cl(t){return function(n,e){return xf(n+=t)>gf&&(n-=Math.round(n/_f)*_f),[n,e]}}function fl(t){var n=cl(t);return n.invert=cl(-t),n}function sl(t,n){var e=Tf(t),r=Cf(t),i=Tf(n),o=Cf(n);function a(t,n){var a=Tf(n),u=Tf(t)*a,c=Cf(t)*a,f=Cf(n),s=f*e+u*r;return[Mf(c*i-s*o,u*e-f*r),Rf(s*i+c*o)]}return a.invert=function(t,n){var a=Tf(n),u=Tf(t)*a,c=Cf(t)*a,f=Cf(n),s=f*i-c*o;return[Mf(c*i+f*o,u*e+s*r),Rf(s*e-u*r)]},a}function ll(t){function n(n){return(n=t(n[0]*mf,n[1]*mf))[0]*=bf,n[1]*=bf,n}return t=ul(t[0]*mf,t[1]*mf,t.length>2?t[2]*mf:0),n.invert=function(n){return(n=t.invert(n[0]*mf,n[1]*mf))[0]*=bf,n[1]*=bf,n},n}function hl(t,n,e,r,i,o){if(e){var a=Tf(n),u=Cf(n),c=r*e;null==i?(i=n+r*_f,o=n-c/2):(i=dl(a,i),o=dl(a,o),(r>0?io)&&(i+=r*_f));for(var f,s=i;r>0?s>o:s1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function gl(t,n){return xf(t[0]-n[0])=0;--o)i.point((s=f[o])[0],s[1]);else r(h.x,h.p.x,-1,i);h=h.p}f=(h=h.o).z,d=!d}while(!h.v);i.lineEnd()}}}function _l(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r=0?1:-1,E=S*A,N=E>gf,k=y*w;if(c.add(Mf(k*S*Cf(E),v*M+k*Tf(E))),a+=N?A+S*_f:A,N^p>=e^m>=e){var C=ys(ps(d),ps(b));bs(C);var P=ys(o,C);bs(P);var z=(N^A>=0?-1:1)*Rf(P[2]);(r>z||r===z&&(C[0]||C[1]))&&(u+=N^A>=0?1:-1)}}return(a<-df||a0){for(l||(i.polygonStart(),l=!0),i.lineStart(),t=0;t1&&2&c&&h.push(h.pop().concat(h.shift())),a.push(h.filter(wl))}return h}}function wl(t){return t.length>1}function Ml(t,n){return((t=t.x)[0]<0?t[1]-yf-df:yf-t[1])-((n=n.x)[0]<0?n[1]-yf-df:yf-n[1])}al.invert=al;var Tl=xl((function(){return!0}),(function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,a){var u=o>0?gf:-gf,c=xf(o-e);xf(c-gf)0?yf:-yf),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),t.point(o,r),n=0):i!==u&&c>=gf&&(xf(e-i)df?wf((Cf(n)*(o=Tf(r))*Cf(e)-Cf(r)*(i=Tf(n))*Cf(t))/(i*o*a)):(n+r)/2}(e,r,o,a),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),n=0),t.point(e=o,r=a),i=u},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}}),(function(t,n,e,r){var i;if(null==t)i=e*yf,r.point(-gf,i),r.point(0,i),r.point(gf,i),r.point(gf,0),r.point(gf,-i),r.point(0,-i),r.point(-gf,-i),r.point(-gf,0),r.point(-gf,i);else if(xf(t[0]-n[0])>df){var o=t[0]0,i=xf(n)>df;function o(t,e){return Tf(t)*Tf(e)>n}function a(t,e,r){var i=[1,0,0],o=ys(ps(t),ps(e)),a=gs(o,o),u=o[0],c=a-u*u;if(!c)return!r&&t;var f=n*a/c,s=-n*u/c,l=ys(i,o),h=_s(i,f);vs(h,_s(o,s));var d=l,p=gs(h,d),g=gs(d,d),y=p*p-g*(gs(h,h)-1);if(!(y<0)){var v=zf(y),_=_s(d,(-p-v)/g);if(vs(_,h),_=ds(_),!r)return _;var b,m=t[0],x=e[0],w=t[1],M=e[1];x0^_[1]<(xf(_[0]-m)gf^(m<=_[0]&&_[0]<=x)){var S=_s(d,(-p+v)/g);return vs(S,h),[_,ds(S)]}}}function u(n,e){var i=r?t:gf-t,o=0;return n<-i?o|=1:n>i&&(o|=2),e<-i?o|=4:e>i&&(o|=8),o}return xl(o,(function(t){var n,e,c,f,s;return{lineStart:function(){f=c=!1,s=1},point:function(l,h){var d,p=[l,h],g=o(l,h),y=r?g?0:u(l,h):g?u(l+(l<0?gf:-gf),h):0;if(!n&&(f=c=g)&&t.lineStart(),g!==c&&(!(d=a(n,p))||gl(n,d)||gl(p,d))&&(p[2]=1),g!==c)s=0,g?(t.lineStart(),d=a(p,n),t.point(d[0],d[1])):(d=a(n,p),t.point(d[0],d[1],2),t.lineEnd()),n=d;else if(i&&n&&r^g){var v;y&e||!(v=a(p,n,!0))||(s=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1],3)))}!g||n&&gl(n,p)||t.point(p[0],p[1]),n=p,c=g,e=y},lineEnd:function(){c&&t.lineEnd(),n=null},clean:function(){return s|(f&&c)<<1}}}),(function(n,r,i,o){hl(o,t,e,i,n,r)}),r?[0,-t]:[-gf,t-gf])}var Sl,El,Nl,kl,Cl=1e9,Pl=-Cl;function zl(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,u,f){var s=0,l=0;if(null==i||(s=a(i,u))!==(l=a(o,u))||c(i,o)<0^u>0)do{f.point(0===s||3===s?t:e,s>1?r:n)}while((s=(s+u+4)%4)!==l);else f.point(o[0],o[1])}function a(r,i){return xf(r[0]-t)0?0:3:xf(r[0]-e)0?2:1:xf(r[1]-n)0?1:0:i>0?3:2}function u(t,n){return c(t.x,n.x)}function c(t,n){var e=a(t,1),r=a(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(a){var c,f,s,l,h,d,p,g,y,v,_,b=a,m=pl(),x={point:w,lineStart:function(){x.point=M,f&&f.push(s=[]);v=!0,y=!1,p=g=NaN},lineEnd:function(){c&&(M(l,h),d&&y&&m.rejoin(),c.push(m.result()));x.point=w,y&&b.lineEnd()},polygonStart:function(){b=m,c=[],f=[],_=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=f.length;er&&(h-o)*(r-a)>(d-a)*(t-o)&&++n:d<=r&&(h-o)*(r-a)<(d-a)*(t-o)&&--n;return n}(),e=_&&n,i=(c=ft(c)).length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),o(null,null,1,a),a.lineEnd()),i&&vl(c,u,n,o,a),a.polygonEnd());b=a,c=f=s=null}};function w(t,n){i(t,n)&&b.point(t,n)}function M(o,a){var u=i(o,a);if(f&&s.push([o,a]),v)l=o,h=a,d=u,v=!1,u&&(b.lineStart(),b.point(o,a));else if(u&&y)b.point(o,a);else{var c=[p=Math.max(Pl,Math.min(Cl,p)),g=Math.max(Pl,Math.min(Cl,g))],m=[o=Math.max(Pl,Math.min(Cl,o)),a=Math.max(Pl,Math.min(Cl,a))];!function(t,n,e,r,i,o){var a,u=t[0],c=t[1],f=0,s=1,l=n[0]-u,h=n[1]-c;if(a=e-u,l||!(a>0)){if(a/=l,l<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=i-u,l||!(a<0)){if(a/=l,l<0){if(a>s)return;a>f&&(f=a)}else if(l>0){if(a0)){if(a/=h,h<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=o-c,h||!(a<0)){if(a/=h,h<0){if(a>s)return;a>f&&(f=a)}else if(h>0){if(a0&&(t[0]=u+f*l,t[1]=c+f*h),s<1&&(n[0]=u+s*l,n[1]=c+s*h),!0}}}}}(c,m,t,n,e,r)?u&&(b.lineStart(),b.point(o,a),_=!1):(y||(b.lineStart(),b.point(c[0],c[1])),b.point(m[0],m[1]),u||b.lineEnd(),_=!1)}p=o,g=a,y=u}return x}}var $l={sphere:qf,point:qf,lineStart:function(){$l.point=Rl,$l.lineEnd=Dl},lineEnd:qf,polygonStart:qf,polygonEnd:qf};function Dl(){$l.point=$l.lineEnd=qf}function Rl(t,n){El=t*=mf,Nl=Cf(n*=mf),kl=Tf(n),$l.point=Fl}function Fl(t,n){t*=mf;var e=Cf(n*=mf),r=Tf(n),i=xf(t-El),o=Tf(i),a=r*Cf(i),u=kl*e-Nl*r*o,c=Nl*e+kl*r*o;Sl.add(Mf(zf(a*a+u*u),c)),El=t,Nl=e,kl=r}function ql(t){return Sl=new T,Lf(t,$l),+Sl}var Ul=[null,null],Il={type:"LineString",coordinates:Ul};function Ol(t,n){return Ul[0]=t,Ul[1]=n,ql(Il)}var Bl={Feature:function(t,n){return Ll(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r0&&(i=Ol(t[o],t[o-1]))>0&&e<=i&&r<=i&&(e+r-i)*(1-Math.pow((e-r)/i,2))df})).map(c)).concat(lt(Af(o/d)*d,i,d).filter((function(t){return xf(t%g)>df})).map(f))}return v.lines=function(){return _().map((function(t){return{type:"LineString",coordinates:t}}))},v.outline=function(){return{type:"Polygon",coordinates:[s(r).concat(l(a).slice(1),s(e).reverse().slice(1),l(u).reverse().slice(1))]}},v.extent=function(t){return arguments.length?v.extentMajor(t).extentMinor(t):v.extentMinor()},v.extentMajor=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],u=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),u>a&&(t=u,u=a,a=t),v.precision(y)):[[r,u],[e,a]]},v.extentMinor=function(e){return arguments.length?(n=+e[0][0],t=+e[1][0],o=+e[0][1],i=+e[1][1],n>t&&(e=n,n=t,t=e),o>i&&(e=o,o=i,i=e),v.precision(y)):[[n,o],[t,i]]},v.step=function(t){return arguments.length?v.stepMajor(t).stepMinor(t):v.stepMinor()},v.stepMajor=function(t){return arguments.length?(p=+t[0],g=+t[1],v):[p,g]},v.stepMinor=function(t){return arguments.length?(h=+t[0],d=+t[1],v):[h,d]},v.precision=function(h){return arguments.length?(y=+h,c=Wl(o,i,90),f=Zl(n,t,y),s=Wl(u,a,90),l=Zl(r,e,y),v):y},v.extentMajor([[-180,-90+df],[180,90-df]]).extentMinor([[-180,-80-df],[180,80+df]])}var Ql,Jl,th,nh,eh=t=>t,rh=new T,ih=new T,oh={point:qf,lineStart:qf,lineEnd:qf,polygonStart:function(){oh.lineStart=ah,oh.lineEnd=fh},polygonEnd:function(){oh.lineStart=oh.lineEnd=oh.point=qf,rh.add(xf(ih)),ih=new T},result:function(){var t=rh/2;return rh=new T,t}};function ah(){oh.point=uh}function uh(t,n){oh.point=ch,Ql=th=t,Jl=nh=n}function ch(t,n){ih.add(nh*t-th*n),th=t,nh=n}function fh(){ch(Ql,Jl)}var sh=oh,lh=1/0,hh=lh,dh=-lh,ph=dh,gh={point:function(t,n){tdh&&(dh=t);nph&&(ph=n)},lineStart:qf,lineEnd:qf,polygonStart:qf,polygonEnd:qf,result:function(){var t=[[lh,hh],[dh,ph]];return dh=ph=-(hh=lh=1/0),t}};var yh,vh,_h,bh,mh=gh,xh=0,wh=0,Mh=0,Th=0,Ah=0,Sh=0,Eh=0,Nh=0,kh=0,Ch={point:Ph,lineStart:zh,lineEnd:Rh,polygonStart:function(){Ch.lineStart=Fh,Ch.lineEnd=qh},polygonEnd:function(){Ch.point=Ph,Ch.lineStart=zh,Ch.lineEnd=Rh},result:function(){var t=kh?[Eh/kh,Nh/kh]:Sh?[Th/Sh,Ah/Sh]:Mh?[xh/Mh,wh/Mh]:[NaN,NaN];return xh=wh=Mh=Th=Ah=Sh=Eh=Nh=kh=0,t}};function Ph(t,n){xh+=t,wh+=n,++Mh}function zh(){Ch.point=$h}function $h(t,n){Ch.point=Dh,Ph(_h=t,bh=n)}function Dh(t,n){var e=t-_h,r=n-bh,i=zf(e*e+r*r);Th+=i*(_h+t)/2,Ah+=i*(bh+n)/2,Sh+=i,Ph(_h=t,bh=n)}function Rh(){Ch.point=Ph}function Fh(){Ch.point=Uh}function qh(){Ih(yh,vh)}function Uh(t,n){Ch.point=Ih,Ph(yh=_h=t,vh=bh=n)}function Ih(t,n){var e=t-_h,r=n-bh,i=zf(e*e+r*r);Th+=i*(_h+t)/2,Ah+=i*(bh+n)/2,Sh+=i,Eh+=(i=bh*t-_h*n)*(_h+t),Nh+=i*(bh+n),kh+=3*i,Ph(_h=t,bh=n)}var Oh=Ch;function Bh(t){this._context=t}Bh.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,_f)}},result:qf};var Yh,Lh,jh,Hh,Xh,Gh=new T,Vh={point:qf,lineStart:function(){Vh.point=Wh},lineEnd:function(){Yh&&Zh(Lh,jh),Vh.point=qf},polygonStart:function(){Yh=!0},polygonEnd:function(){Yh=null},result:function(){var t=+Gh;return Gh=new T,t}};function Wh(t,n){Vh.point=Zh,Lh=Hh=t,jh=Xh=n}function Zh(t,n){Hh-=t,Xh-=n,Gh.add(zf(Hh*Hh+Xh*Xh)),Hh=t,Xh=n}var Kh=Vh;let Qh,Jh,td,nd;class ed{constructor(t){this._append=null==t?rd:function(t){const n=Math.floor(t);if(!(n>=0))throw new RangeError(`invalid digits: ${t}`);if(n>15)return rd;if(n!==Qh){const t=10**n;Qh=n,Jh=function(n){let e=1;this._+=n[0];for(const r=n.length;e4*n&&g--){var m=a+h,x=u+d,w=c+p,M=zf(m*m+x*x+w*w),T=Rf(w/=M),A=xf(xf(w)-1)n||xf((v*k+_*C)/b-.5)>.3||a*h+u*d+c*p2?t[2]%360*mf:0,k()):[y*bf,v*bf,_*bf]},E.angle=function(t){return arguments.length?(b=t%360*mf,k()):b*bf},E.reflectX=function(t){return arguments.length?(m=t?-1:1,k()):m<0},E.reflectY=function(t){return arguments.length?(x=t?-1:1,k()):x<0},E.precision=function(t){return arguments.length?(a=dd(u,S=t*t),C()):zf(S)},E.fitExtent=function(t,n){return ud(E,t,n)},E.fitSize=function(t,n){return cd(E,t,n)},E.fitWidth=function(t,n){return fd(E,t,n)},E.fitHeight=function(t,n){return sd(E,t,n)},function(){return n=t.apply(this,arguments),E.invert=n.invert&&N,k()}}function _d(t){var n=0,e=gf/3,r=vd(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*mf,e=t[1]*mf):[n*bf,e*bf]},i}function bd(t,n){var e=Cf(t),r=(e+Cf(n))/2;if(xf(r)0?n<-yf+df&&(n=-yf+df):n>yf-df&&(n=yf-df);var e=i/kf(Nd(n),r);return[e*Cf(r*t),i-e*Tf(r*t)]}return o.invert=function(t,n){var e=i-n,o=Pf(r)*zf(t*t+e*e),a=Mf(t,xf(e))*Pf(e);return e*r<0&&(a-=gf*Pf(t)*Pf(e)),[a/r,2*wf(kf(i/o,1/r))-yf]},o}function Cd(t,n){return[t,n]}function Pd(t,n){var e=Tf(t),r=t===n?Cf(t):(e-Tf(n))/(n-t),i=e/r+t;if(xf(r)=0;)n+=e[r].value;else n=1;t.value=n}function Gd(t,n){t instanceof Map?(t=[void 0,t],void 0===n&&(n=Wd)):void 0===n&&(n=Vd);for(var e,r,i,o,a,u=new Qd(t),c=[u];e=c.pop();)if((i=n(e.data))&&(a=(i=Array.from(i)).length))for(e.children=i,o=a-1;o>=0;--o)c.push(r=i[o]=new Qd(i[o])),r.parent=e,r.depth=e.depth+1;return u.eachBefore(Kd)}function Vd(t){return t.children}function Wd(t){return Array.isArray(t)?t[1]:null}function Zd(t){void 0!==t.data.value&&(t.value=t.data.value),t.data=t.data.data}function Kd(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function Qd(t){this.data=t,this.depth=this.height=0,this.parent=null}function Jd(t){return null==t?null:tp(t)}function tp(t){if("function"!=typeof t)throw new Error;return t}function np(){return 0}function ep(t){return function(){return t}}qd.invert=function(t,n){for(var e,r=n,i=r*r,o=i*i*i,a=0;a<12&&(o=(i=(r-=e=(r*(zd+$d*i+o*(Dd+Rd*i))-n)/(zd+3*$d*i+o*(7*Dd+9*Rd*i)))*r)*i*i,!(xf(e)df&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},Od.invert=Md(Rf),Bd.invert=Md((function(t){return 2*wf(t)})),Yd.invert=function(t,n){return[-n,2*wf(Sf(t))-yf]},Qd.prototype=Gd.prototype={constructor:Qd,count:function(){return this.eachAfter(Xd)},each:function(t,n){let e=-1;for(const r of this)t.call(n,r,++e,this);return this},eachAfter:function(t,n){for(var e,r,i,o=this,a=[o],u=[],c=-1;o=a.pop();)if(u.push(o),e=o.children)for(r=0,i=e.length;r=0;--r)o.push(e[r]);return this},find:function(t,n){let e=-1;for(const r of this)if(t.call(n,r,++e,this))return r},sum:function(t){return this.eachAfter((function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e}))},sort:function(t){return this.eachBefore((function(n){n.children&&n.children.sort(t)}))},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;t=e.pop(),n=r.pop();for(;t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){return Array.from(this)},leaves:function(){var t=[];return this.eachBefore((function(n){n.children||t.push(n)})),t},links:function(){var t=this,n=[];return t.each((function(e){e!==t&&n.push({source:e.parent,target:e})})),n},copy:function(){return Gd(this).eachBefore(Zd)},[Symbol.iterator]:function*(){var t,n,e,r,i=this,o=[i];do{for(t=o.reverse(),o=[];i=t.pop();)if(yield i,n=i.children)for(e=0,r=n.length;e(t=(rp*t+ip)%op)/op}function up(t,n){for(var e,r,i=0,o=(t=function(t,n){let e,r,i=t.length;for(;i;)r=n()*i--|0,e=t[i],t[i]=t[r],t[r]=e;return t}(Array.from(t),n)).length,a=[];i0&&e*e>r*r+i*i}function lp(t,n){for(var e=0;e1e-6?(E+Math.sqrt(E*E-4*S*N))/(2*S):N/E);return{x:r+w+M*k,y:i+T+A*k,r:k}}function gp(t,n,e){var r,i,o,a,u=t.x-n.x,c=t.y-n.y,f=u*u+c*c;f?(i=n.r+e.r,i*=i,a=t.r+e.r,i>(a*=a)?(r=(f+a-i)/(2*f),o=Math.sqrt(Math.max(0,a/f-r*r)),e.x=t.x-r*u-o*c,e.y=t.y-r*c+o*u):(r=(f+i-a)/(2*f),o=Math.sqrt(Math.max(0,i/f-r*r)),e.x=n.x+r*u-o*c,e.y=n.y+r*c+o*u)):(e.x=n.x+e.r,e.y=n.y)}function yp(t,n){var e=t.r+n.r-1e-6,r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function vp(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function _p(t){this._=t,this.next=null,this.previous=null}function bp(t,n){if(!(o=(t=function(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}(t)).length))return 0;var e,r,i,o,a,u,c,f,s,l,h;if((e=t[0]).x=0,e.y=0,!(o>1))return e.r;if(r=t[1],e.x=-r.r,r.x=e.r,r.y=0,!(o>2))return e.r+r.r;gp(r,e,i=t[2]),e=new _p(e),r=new _p(r),i=new _p(i),e.next=i.previous=r,r.next=e.previous=i,i.next=r.previous=e;t:for(c=3;c1&&!zp(t,n););return t.slice(0,n)}function zp(t,n){if("/"===t[n]){let e=0;for(;n>0&&"\\"===t[--n];)++e;if(0==(1&e))return!0}return!1}function $p(t,n){return t.parent===n.parent?1:2}function Dp(t){var n=t.children;return n?n[0]:t.t}function Rp(t){var n=t.children;return n?n[n.length-1]:t.t}function Fp(t,n,e){var r=e/(n.i-t.i);n.c-=r,n.s+=e,t.c+=r,n.z+=e,n.m+=e}function qp(t,n,e){return t.a.parent===n.parent?t.a:e}function Up(t,n){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=n}function Ip(t,n,e,r,i){for(var o,a=t.children,u=-1,c=a.length,f=t.value&&(i-e)/t.value;++uh&&(h=u),y=s*s*g,(d=Math.max(h/y,y/l))>p){s-=u;break}p=d}v.push(a={value:s,dice:c1?n:1)},e}(Op);var Lp=function t(n){function e(t,e,r,i,o){if((a=t._squarify)&&a.ratio===n)for(var a,u,c,f,s,l=-1,h=a.length,d=t.value;++l1?n:1)},e}(Op);function jp(t,n,e){return(n[0]-t[0])*(e[1]-t[1])-(n[1]-t[1])*(e[0]-t[0])}function Hp(t,n){return t[0]-n[0]||t[1]-n[1]}function Xp(t){const n=t.length,e=[0,1];let r,i=2;for(r=2;r1&&jp(t[e[i-2]],t[e[i-1]],t[r])<=0;)--i;e[i++]=r}return e.slice(0,i)}var Gp=Math.random,Vp=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(Gp),Wp=function t(n){function e(t,e){return arguments.length<2&&(e=t,t=0),t=Math.floor(t),e=Math.floor(e)-t,function(){return Math.floor(n()*e+t)}}return e.source=t,e}(Gp),Zp=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(Gp),Kp=function t(n){var e=Zp.source(n);function r(){var t=e.apply(this,arguments);return function(){return Math.exp(t())}}return r.source=t,r}(Gp),Qp=function t(n){function e(t){return(t=+t)<=0?()=>0:function(){for(var e=0,r=t;r>1;--r)e+=n();return e+r*n()}}return e.source=t,e}(Gp),Jp=function t(n){var e=Qp.source(n);function r(t){if(0==(t=+t))return n;var r=e(t);return function(){return r()/t}}return r.source=t,r}(Gp),tg=function t(n){function e(t){return function(){return-Math.log1p(-n())/t}}return e.source=t,e}(Gp),ng=function t(n){function e(t){if((t=+t)<0)throw new RangeError("invalid alpha");return t=1/-t,function(){return Math.pow(1-n(),t)}}return e.source=t,e}(Gp),eg=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return function(){return Math.floor(n()+t)}}return e.source=t,e}(Gp),rg=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return 0===t?()=>1/0:1===t?()=>1:(t=Math.log1p(-t),function(){return 1+Math.floor(Math.log1p(-n())/t)})}return e.source=t,e}(Gp),ig=function t(n){var e=Zp.source(n)();function r(t,r){if((t=+t)<0)throw new RangeError("invalid k");if(0===t)return()=>0;if(r=null==r?1:+r,1===t)return()=>-Math.log1p(-n())*r;var i=(t<1?t+1:t)-1/3,o=1/(3*Math.sqrt(i)),a=t<1?()=>Math.pow(n(),1/t):()=>1;return function(){do{do{var t=e(),u=1+o*t}while(u<=0);u*=u*u;var c=1-n()}while(c>=1-.0331*t*t*t*t&&Math.log(c)>=.5*t*t+i*(1-u+Math.log(u)));return i*u*a()*r}}return r.source=t,r}(Gp),og=function t(n){var e=ig.source(n);function r(t,n){var r=e(t),i=e(n);return function(){var t=r();return 0===t?0:t/(t+i())}}return r.source=t,r}(Gp),ag=function t(n){var e=rg.source(n),r=og.source(n);function i(t,n){return t=+t,(n=+n)>=1?()=>t:n<=0?()=>0:function(){for(var i=0,o=t,a=n;o*a>16&&o*(1-a)>16;){var u=Math.floor((o+1)*a),c=r(u,o-u+1)();c<=a?(i+=u,o-=u,a=(a-c)/(1-c)):(o=u-1,a/=c)}for(var f=a<.5,s=e(f?a:1-a),l=s(),h=0;l<=o;++h)l+=s();return i+(f?h:o-h)}}return i.source=t,i}(Gp),ug=function t(n){function e(t,e,r){var i;return 0==(t=+t)?i=t=>-Math.log(t):(t=1/t,i=n=>Math.pow(n,t)),e=null==e?0:+e,r=null==r?1:+r,function(){return e+r*i(-Math.log1p(-n()))}}return e.source=t,e}(Gp),cg=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){return t+e*Math.tan(Math.PI*n())}}return e.source=t,e}(Gp),fg=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){var r=n();return t+e*Math.log(r/(1-r))}}return e.source=t,e}(Gp),sg=function t(n){var e=ig.source(n),r=ag.source(n);function i(t){return function(){for(var i=0,o=t;o>16;){var a=Math.floor(.875*o),u=e(a)();if(u>o)return i+r(a-1,o/u)();i+=a,o-=u}for(var c=-Math.log1p(-n()),f=0;c<=o;++f)c-=Math.log1p(-n());return i+f}}return i.source=t,i}(Gp);const lg=1/4294967296;function hg(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}function dg(t,n){switch(arguments.length){case 0:break;case 1:"function"==typeof t?this.interpolator(t):this.range(t);break;default:this.domain(t),"function"==typeof n?this.interpolator(n):this.range(n)}return this}const pg=Symbol("implicit");function gg(){var t=new InternMap,n=[],e=[],r=pg;function i(i){let o=t.get(i);if(void 0===o){if(r!==pg)return r;t.set(i,o=n.push(i)-1)}return e[o%e.length]}return i.domain=function(e){if(!arguments.length)return n.slice();n=[],t=new InternMap;for(const r of e)t.has(r)||t.set(r,n.push(r)-1);return i},i.range=function(t){return arguments.length?(e=Array.from(t),i):e.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return gg(n,e).unknown(r)},hg.apply(i,arguments),i}function yg(){var t,n,e=gg().unknown(void 0),r=e.domain,i=e.range,o=0,a=1,u=!1,c=0,f=0,s=.5;function l(){var e=r().length,l=an&&(e=t,t=n,n=e),function(e){return Math.max(t,Math.min(n,e))}}(a[0],a[t-1])),r=t>2?Mg:wg,i=o=null,l}function l(n){return null==n||isNaN(n=+n)?e:(i||(i=r(a.map(t),u,c)))(t(f(n)))}return l.invert=function(e){return f(n((o||(o=r(u,a.map(t),Yr)))(e)))},l.domain=function(t){return arguments.length?(a=Array.from(t,_g),s()):a.slice()},l.range=function(t){return arguments.length?(u=Array.from(t),s()):u.slice()},l.rangeRound=function(t){return u=Array.from(t),c=Vr,s()},l.clamp=function(t){return arguments.length?(f=!!t||mg,s()):f!==mg},l.interpolate=function(t){return arguments.length?(c=t,s()):c},l.unknown=function(t){return arguments.length?(e=t,l):e},function(e,r){return t=e,n=r,s()}}function Sg(){return Ag()(mg,mg)}function Eg(n,e,r,i){var o,a=W(n,e,r);switch((i=Jc(null==i?",f":i)).type){case"s":var u=Math.max(Math.abs(n),Math.abs(e));return null!=i.precision||isNaN(o=lf(a,u))||(i.precision=o),t.formatPrefix(i,u);case"":case"e":case"g":case"p":case"r":null!=i.precision||isNaN(o=hf(a,Math.max(Math.abs(n),Math.abs(e))))||(i.precision=o-("e"===i.type));break;case"f":case"%":null!=i.precision||isNaN(o=sf(a))||(i.precision=o-2*("%"===i.type))}return t.format(i)}function Ng(t){var n=t.domain;return t.ticks=function(t){var e=n();return G(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return Eg(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i,o=n(),a=0,u=o.length-1,c=o[a],f=o[u],s=10;for(f0;){if((i=V(c,f,e))===r)return o[a]=c,o[u]=f,n(o);if(i>0)c=Math.floor(c/i)*i,f=Math.ceil(f/i)*i;else{if(!(i<0))break;c=Math.ceil(c*i)/i,f=Math.floor(f*i)/i}r=i}return t},t}function kg(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],a=t[i];return a-t(-n,e)}function Fg(n){const e=n(Cg,Pg),r=e.domain;let i,o,a=10;function u(){return i=function(t){return t===Math.E?Math.log:10===t&&Math.log10||2===t&&Math.log2||(t=Math.log(t),n=>Math.log(n)/t)}(a),o=function(t){return 10===t?Dg:t===Math.E?Math.exp:n=>Math.pow(t,n)}(a),r()[0]<0?(i=Rg(i),o=Rg(o),n(zg,$g)):n(Cg,Pg),e}return e.base=function(t){return arguments.length?(a=+t,u()):a},e.domain=function(t){return arguments.length?(r(t),u()):r()},e.ticks=t=>{const n=r();let e=n[0],u=n[n.length-1];const c=u0){for(;l<=h;++l)for(f=1;fu)break;p.push(s)}}else for(;l<=h;++l)for(f=a-1;f>=1;--f)if(s=l>0?f/o(-l):f*o(l),!(su)break;p.push(s)}2*p.length{if(null==n&&(n=10),null==r&&(r=10===a?"s":","),"function"!=typeof r&&(a%1||null!=(r=Jc(r)).precision||(r.trim=!0),r=t.format(r)),n===1/0)return r;const u=Math.max(1,a*n/e.ticks().length);return t=>{let n=t/o(Math.round(i(t)));return n*ar(kg(r(),{floor:t=>o(Math.floor(i(t))),ceil:t=>o(Math.ceil(i(t)))})),e}function qg(t){return function(n){return Math.sign(n)*Math.log1p(Math.abs(n/t))}}function Ug(t){return function(n){return Math.sign(n)*Math.expm1(Math.abs(n))*t}}function Ig(t){var n=1,e=t(qg(n),Ug(n));return e.constant=function(e){return arguments.length?t(qg(n=+e),Ug(n)):n},Ng(e)}function Og(t){return function(n){return n<0?-Math.pow(-n,t):Math.pow(n,t)}}function Bg(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}function Yg(t){return t<0?-t*t:t*t}function Lg(t){var n=t(mg,mg),e=1;return n.exponent=function(n){return arguments.length?1===(e=+n)?t(mg,mg):.5===e?t(Bg,Yg):t(Og(e),Og(1/e)):e},Ng(n)}function jg(){var t=Lg(Ag());return t.copy=function(){return Tg(t,jg()).exponent(t.exponent())},hg.apply(t,arguments),t}function Hg(t){return Math.sign(t)*t*t}const Xg=new Date,Gg=new Date;function Vg(t,n,e,r){function i(n){return t(n=0===arguments.length?new Date:new Date(+n)),n}return i.floor=n=>(t(n=new Date(+n)),n),i.ceil=e=>(t(e=new Date(e-1)),n(e,1),t(e),e),i.round=t=>{const n=i(t),e=i.ceil(t);return t-n(n(t=new Date(+t),null==e?1:Math.floor(e)),t),i.range=(e,r,o)=>{const a=[];if(e=i.ceil(e),o=null==o?1:Math.floor(o),!(e0))return a;let u;do{a.push(u=new Date(+e)),n(e,o),t(e)}while(uVg((n=>{if(n>=n)for(;t(n),!e(n);)n.setTime(n-1)}),((t,r)=>{if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););})),e&&(i.count=(n,r)=>(Xg.setTime(+n),Gg.setTime(+r),t(Xg),t(Gg),Math.floor(e(Xg,Gg))),i.every=t=>(t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?n=>r(n)%t==0:n=>i.count(0,n)%t==0):i:null)),i}const Wg=Vg((()=>{}),((t,n)=>{t.setTime(+t+n)}),((t,n)=>n-t));Wg.every=t=>(t=Math.floor(t),isFinite(t)&&t>0?t>1?Vg((n=>{n.setTime(Math.floor(n/t)*t)}),((n,e)=>{n.setTime(+n+e*t)}),((n,e)=>(e-n)/t)):Wg:null);const Zg=Wg.range,Kg=1e3,Qg=6e4,Jg=36e5,ty=864e5,ny=6048e5,ey=2592e6,ry=31536e6,iy=Vg((t=>{t.setTime(t-t.getMilliseconds())}),((t,n)=>{t.setTime(+t+n*Kg)}),((t,n)=>(n-t)/Kg),(t=>t.getUTCSeconds())),oy=iy.range,ay=Vg((t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*Kg)}),((t,n)=>{t.setTime(+t+n*Qg)}),((t,n)=>(n-t)/Qg),(t=>t.getMinutes())),uy=ay.range,cy=Vg((t=>{t.setUTCSeconds(0,0)}),((t,n)=>{t.setTime(+t+n*Qg)}),((t,n)=>(n-t)/Qg),(t=>t.getUTCMinutes())),fy=cy.range,sy=Vg((t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*Kg-t.getMinutes()*Qg)}),((t,n)=>{t.setTime(+t+n*Jg)}),((t,n)=>(n-t)/Jg),(t=>t.getHours())),ly=sy.range,hy=Vg((t=>{t.setUTCMinutes(0,0,0)}),((t,n)=>{t.setTime(+t+n*Jg)}),((t,n)=>(n-t)/Jg),(t=>t.getUTCHours())),dy=hy.range,py=Vg((t=>t.setHours(0,0,0,0)),((t,n)=>t.setDate(t.getDate()+n)),((t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Qg)/ty),(t=>t.getDate()-1)),gy=py.range,yy=Vg((t=>{t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCDate(t.getUTCDate()+n)}),((t,n)=>(n-t)/ty),(t=>t.getUTCDate()-1)),vy=yy.range,_y=Vg((t=>{t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCDate(t.getUTCDate()+n)}),((t,n)=>(n-t)/ty),(t=>Math.floor(t/ty))),by=_y.range;function my(t){return Vg((n=>{n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)}),((t,n)=>{t.setDate(t.getDate()+7*n)}),((t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Qg)/ny))}const xy=my(0),wy=my(1),My=my(2),Ty=my(3),Ay=my(4),Sy=my(5),Ey=my(6),Ny=xy.range,ky=wy.range,Cy=My.range,Py=Ty.range,zy=Ay.range,$y=Sy.range,Dy=Ey.range;function Ry(t){return Vg((n=>{n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCDate(t.getUTCDate()+7*n)}),((t,n)=>(n-t)/ny))}const Fy=Ry(0),qy=Ry(1),Uy=Ry(2),Iy=Ry(3),Oy=Ry(4),By=Ry(5),Yy=Ry(6),Ly=Fy.range,jy=qy.range,Hy=Uy.range,Xy=Iy.range,Gy=Oy.range,Vy=By.range,Wy=Yy.range,Zy=Vg((t=>{t.setDate(1),t.setHours(0,0,0,0)}),((t,n)=>{t.setMonth(t.getMonth()+n)}),((t,n)=>n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())),(t=>t.getMonth())),Ky=Zy.range,Qy=Vg((t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCMonth(t.getUTCMonth()+n)}),((t,n)=>n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())),(t=>t.getUTCMonth())),Jy=Qy.range,tv=Vg((t=>{t.setMonth(0,1),t.setHours(0,0,0,0)}),((t,n)=>{t.setFullYear(t.getFullYear()+n)}),((t,n)=>n.getFullYear()-t.getFullYear()),(t=>t.getFullYear()));tv.every=t=>isFinite(t=Math.floor(t))&&t>0?Vg((n=>{n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)}),((n,e)=>{n.setFullYear(n.getFullYear()+e*t)})):null;const nv=tv.range,ev=Vg((t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),((t,n)=>{t.setUTCFullYear(t.getUTCFullYear()+n)}),((t,n)=>n.getUTCFullYear()-t.getUTCFullYear()),(t=>t.getUTCFullYear()));ev.every=t=>isFinite(t=Math.floor(t))&&t>0?Vg((n=>{n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)}),((n,e)=>{n.setUTCFullYear(n.getUTCFullYear()+e*t)})):null;const rv=ev.range;function iv(t,n,e,i,o,a){const u=[[iy,1,Kg],[iy,5,5e3],[iy,15,15e3],[iy,30,3e4],[a,1,Qg],[a,5,3e5],[a,15,9e5],[a,30,18e5],[o,1,Jg],[o,3,108e5],[o,6,216e5],[o,12,432e5],[i,1,ty],[i,2,1728e5],[e,1,ny],[n,1,ey],[n,3,7776e6],[t,1,ry]];function c(n,e,i){const o=Math.abs(e-n)/i,a=r((([,,t])=>t)).right(u,o);if(a===u.length)return t.every(W(n/ry,e/ry,i));if(0===a)return Wg.every(Math.max(W(n,e,i),1));const[c,f]=u[o/u[a-1][2]=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:k_,s:C_,S:Zv,u:Kv,U:Qv,V:t_,w:n_,W:e_,x:null,X:null,y:r_,Y:o_,Z:u_,"%":N_},m={a:function(t){return a[t.getUTCDay()]},A:function(t){return o[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return u[t.getUTCMonth()]},c:null,d:c_,e:c_,f:d_,g:T_,G:S_,H:f_,I:s_,j:l_,L:h_,m:p_,M:g_,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:k_,s:C_,S:y_,u:v_,U:__,V:m_,w:x_,W:w_,x:null,X:null,y:M_,Y:A_,Z:E_,"%":N_},x={a:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=p.get(r[0].toLowerCase()),e+r[0].length):-1},A:function(t,n,e){var r=l.exec(n.slice(e));return r?(t.w=h.get(r[0].toLowerCase()),e+r[0].length):-1},b:function(t,n,e){var r=v.exec(n.slice(e));return r?(t.m=_.get(r[0].toLowerCase()),e+r[0].length):-1},B:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.m=y.get(r[0].toLowerCase()),e+r[0].length):-1},c:function(t,e,r){return T(t,n,e,r)},d:zv,e:zv,f:Uv,g:Nv,G:Ev,H:Dv,I:Dv,j:$v,L:qv,m:Pv,M:Rv,p:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.p=s.get(r[0].toLowerCase()),e+r[0].length):-1},q:Cv,Q:Ov,s:Bv,S:Fv,u:Mv,U:Tv,V:Av,w:wv,W:Sv,x:function(t,n,r){return T(t,e,n,r)},X:function(t,n,e){return T(t,r,n,e)},y:Nv,Y:Ev,Z:kv,"%":Iv};function w(t,n){return function(e){var r,i,o,a=[],u=-1,c=0,f=t.length;for(e instanceof Date||(e=new Date(+e));++u53)return null;"w"in o||(o.w=1),"Z"in o?(i=(r=sv(lv(o.y,0,1))).getUTCDay(),r=i>4||0===i?qy.ceil(r):qy(r),r=yy.offset(r,7*(o.V-1)),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(i=(r=fv(lv(o.y,0,1))).getDay(),r=i>4||0===i?wy.ceil(r):wy(r),r=py.offset(r,7*(o.V-1)),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:"W"in o?1:0),i="Z"in o?sv(lv(o.y,0,1)).getUTCDay():fv(lv(o.y,0,1)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,sv(o)):fv(o)}}function T(t,n,e,r){for(var i,o,a=0,u=n.length,c=e.length;a=c)return-1;if(37===(i=n.charCodeAt(a++))){if(i=n.charAt(a++),!(o=x[i in pv?n.charAt(a++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}return b.x=w(e,b),b.X=w(r,b),b.c=w(n,b),m.x=w(e,m),m.X=w(r,m),m.c=w(n,m),{format:function(t){var n=w(t+="",b);return n.toString=function(){return t},n},parse:function(t){var n=M(t+="",!1);return n.toString=function(){return t},n},utcFormat:function(t){var n=w(t+="",m);return n.toString=function(){return t},n},utcParse:function(t){var n=M(t+="",!0);return n.toString=function(){return t},n}}}var dv,pv={"-":"",_:" ",0:"0"},gv=/^\s*\d+/,yv=/^%/,vv=/[\\^$*+?|[\]().{}]/g;function _v(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o[t.toLowerCase(),n])))}function wv(t,n,e){var r=gv.exec(n.slice(e,e+1));return r?(t.w=+r[0],e+r[0].length):-1}function Mv(t,n,e){var r=gv.exec(n.slice(e,e+1));return r?(t.u=+r[0],e+r[0].length):-1}function Tv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.U=+r[0],e+r[0].length):-1}function Av(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.V=+r[0],e+r[0].length):-1}function Sv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.W=+r[0],e+r[0].length):-1}function Ev(t,n,e){var r=gv.exec(n.slice(e,e+4));return r?(t.y=+r[0],e+r[0].length):-1}function Nv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),e+r[0].length):-1}function kv(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function Cv(t,n,e){var r=gv.exec(n.slice(e,e+1));return r?(t.q=3*r[0]-3,e+r[0].length):-1}function Pv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function zv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function $v(t,n,e){var r=gv.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Dv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Rv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Fv(t,n,e){var r=gv.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function qv(t,n,e){var r=gv.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function Uv(t,n,e){var r=gv.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function Iv(t,n,e){var r=yv.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function Ov(t,n,e){var r=gv.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function Bv(t,n,e){var r=gv.exec(n.slice(e));return r?(t.s=+r[0],e+r[0].length):-1}function Yv(t,n){return _v(t.getDate(),n,2)}function Lv(t,n){return _v(t.getHours(),n,2)}function jv(t,n){return _v(t.getHours()%12||12,n,2)}function Hv(t,n){return _v(1+py.count(tv(t),t),n,3)}function Xv(t,n){return _v(t.getMilliseconds(),n,3)}function Gv(t,n){return Xv(t,n)+"000"}function Vv(t,n){return _v(t.getMonth()+1,n,2)}function Wv(t,n){return _v(t.getMinutes(),n,2)}function Zv(t,n){return _v(t.getSeconds(),n,2)}function Kv(t){var n=t.getDay();return 0===n?7:n}function Qv(t,n){return _v(xy.count(tv(t)-1,t),n,2)}function Jv(t){var n=t.getDay();return n>=4||0===n?Ay(t):Ay.ceil(t)}function t_(t,n){return t=Jv(t),_v(Ay.count(tv(t),t)+(4===tv(t).getDay()),n,2)}function n_(t){return t.getDay()}function e_(t,n){return _v(wy.count(tv(t)-1,t),n,2)}function r_(t,n){return _v(t.getFullYear()%100,n,2)}function i_(t,n){return _v((t=Jv(t)).getFullYear()%100,n,2)}function o_(t,n){return _v(t.getFullYear()%1e4,n,4)}function a_(t,n){var e=t.getDay();return _v((t=e>=4||0===e?Ay(t):Ay.ceil(t)).getFullYear()%1e4,n,4)}function u_(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+_v(n/60|0,"0",2)+_v(n%60,"0",2)}function c_(t,n){return _v(t.getUTCDate(),n,2)}function f_(t,n){return _v(t.getUTCHours(),n,2)}function s_(t,n){return _v(t.getUTCHours()%12||12,n,2)}function l_(t,n){return _v(1+yy.count(ev(t),t),n,3)}function h_(t,n){return _v(t.getUTCMilliseconds(),n,3)}function d_(t,n){return h_(t,n)+"000"}function p_(t,n){return _v(t.getUTCMonth()+1,n,2)}function g_(t,n){return _v(t.getUTCMinutes(),n,2)}function y_(t,n){return _v(t.getUTCSeconds(),n,2)}function v_(t){var n=t.getUTCDay();return 0===n?7:n}function __(t,n){return _v(Fy.count(ev(t)-1,t),n,2)}function b_(t){var n=t.getUTCDay();return n>=4||0===n?Oy(t):Oy.ceil(t)}function m_(t,n){return t=b_(t),_v(Oy.count(ev(t),t)+(4===ev(t).getUTCDay()),n,2)}function x_(t){return t.getUTCDay()}function w_(t,n){return _v(qy.count(ev(t)-1,t),n,2)}function M_(t,n){return _v(t.getUTCFullYear()%100,n,2)}function T_(t,n){return _v((t=b_(t)).getUTCFullYear()%100,n,2)}function A_(t,n){return _v(t.getUTCFullYear()%1e4,n,4)}function S_(t,n){var e=t.getUTCDay();return _v((t=e>=4||0===e?Oy(t):Oy.ceil(t)).getUTCFullYear()%1e4,n,4)}function E_(){return"+0000"}function N_(){return"%"}function k_(t){return+t}function C_(t){return Math.floor(+t/1e3)}function P_(n){return dv=hv(n),t.timeFormat=dv.format,t.timeParse=dv.parse,t.utcFormat=dv.utcFormat,t.utcParse=dv.utcParse,dv}t.timeFormat=void 0,t.timeParse=void 0,t.utcFormat=void 0,t.utcParse=void 0,P_({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var z_="%Y-%m-%dT%H:%M:%S.%LZ";var $_=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat(z_),D_=$_;var R_=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse(z_),F_=R_;function q_(t){return new Date(t)}function U_(t){return t instanceof Date?+t:+new Date(+t)}function I_(t,n,e,r,i,o,a,u,c,f){var s=Sg(),l=s.invert,h=s.domain,d=f(".%L"),p=f(":%S"),g=f("%I:%M"),y=f("%I %p"),v=f("%a %d"),_=f("%b %d"),b=f("%B"),m=f("%Y");function x(t){return(c(t)Fr(t[t.length-1]),rb=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(H_),ib=eb(rb),ob=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(H_),ab=eb(ob),ub=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(H_),cb=eb(ub),fb=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(H_),sb=eb(fb),lb=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(H_),hb=eb(lb),db=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(H_),pb=eb(db),gb=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(H_),yb=eb(gb),vb=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(H_),_b=eb(vb),bb=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(H_),mb=eb(bb),xb=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(H_),wb=eb(xb),Mb=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(H_),Tb=eb(Mb),Ab=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(H_),Sb=eb(Ab),Eb=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(H_),Nb=eb(Eb),kb=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(H_),Cb=eb(kb),Pb=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(H_),zb=eb(Pb),$b=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(H_),Db=eb($b),Rb=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(H_),Fb=eb(Rb),qb=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(H_),Ub=eb(qb),Ib=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(H_),Ob=eb(Ib),Bb=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(H_),Yb=eb(Bb),Lb=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(H_),jb=eb(Lb),Hb=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(H_),Xb=eb(Hb),Gb=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(H_),Vb=eb(Gb),Wb=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(H_),Zb=eb(Wb),Kb=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(H_),Qb=eb(Kb),Jb=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(H_),tm=eb(Jb),nm=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(H_),em=eb(nm);var rm=hi(Tr(300,.5,0),Tr(-240,.5,1)),im=hi(Tr(-100,.75,.35),Tr(80,1.5,.8)),om=hi(Tr(260,.75,.35),Tr(80,1.5,.8)),am=Tr();var um=Fe(),cm=Math.PI/3,fm=2*Math.PI/3;function sm(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}}var lm=sm(H_("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),hm=sm(H_("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),dm=sm(H_("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),pm=sm(H_("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function gm(t){return function(){return t}}const ym=Math.abs,vm=Math.atan2,_m=Math.cos,bm=Math.max,mm=Math.min,xm=Math.sin,wm=Math.sqrt,Mm=1e-12,Tm=Math.PI,Am=Tm/2,Sm=2*Tm;function Em(t){return t>=1?Am:t<=-1?-Am:Math.asin(t)}function Nm(t){let n=3;return t.digits=function(e){if(!arguments.length)return n;if(null==e)n=null;else{const t=Math.floor(e);if(!(t>=0))throw new RangeError(`invalid digits: ${e}`);n=t}return t},()=>new Ua(n)}function km(t){return t.innerRadius}function Cm(t){return t.outerRadius}function Pm(t){return t.startAngle}function zm(t){return t.endAngle}function $m(t){return t&&t.padAngle}function Dm(t,n,e,r,i,o,a){var u=t-e,c=n-r,f=(a?o:-o)/wm(u*u+c*c),s=f*c,l=-f*u,h=t+s,d=n+l,p=e+s,g=r+l,y=(h+p)/2,v=(d+g)/2,_=p-h,b=g-d,m=_*_+b*b,x=i-o,w=h*g-p*d,M=(b<0?-1:1)*wm(bm(0,x*x*m-w*w)),T=(w*b-_*M)/m,A=(-w*_-b*M)/m,S=(w*b+_*M)/m,E=(-w*_+b*M)/m,N=T-y,k=A-v,C=S-y,P=E-v;return N*N+k*k>C*C+P*P&&(T=S,A=E),{cx:T,cy:A,x01:-s,y01:-l,x11:T*(i/x-1),y11:A*(i/x-1)}}var Rm=Array.prototype.slice;function Fm(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function qm(t){this._context=t}function Um(t){return new qm(t)}function Im(t){return t[0]}function Om(t){return t[1]}function Bm(t,n){var e=gm(!0),r=null,i=Um,o=null,a=Nm(u);function u(u){var c,f,s,l=(u=Fm(u)).length,h=!1;for(null==r&&(o=i(s=a())),c=0;c<=l;++c)!(c=l;--h)u.point(v[h],_[h]);u.lineEnd(),u.areaEnd()}y&&(v[s]=+t(d,s,f),_[s]=+n(d,s,f),u.point(r?+r(d,s,f):v[s],e?+e(d,s,f):_[s]))}if(p)return u=null,p+""||null}function s(){return Bm().defined(i).curve(a).context(o)}return t="function"==typeof t?t:void 0===t?Im:gm(+t),n="function"==typeof n?n:gm(void 0===n?0:+n),e="function"==typeof e?e:void 0===e?Om:gm(+e),f.x=function(n){return arguments.length?(t="function"==typeof n?n:gm(+n),r=null,f):t},f.x0=function(n){return arguments.length?(t="function"==typeof n?n:gm(+n),f):t},f.x1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:gm(+t),f):r},f.y=function(t){return arguments.length?(n="function"==typeof t?t:gm(+t),e=null,f):n},f.y0=function(t){return arguments.length?(n="function"==typeof t?t:gm(+t),f):n},f.y1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:gm(+t),f):e},f.lineX0=f.lineY0=function(){return s().x(t).y(n)},f.lineY1=function(){return s().x(t).y(e)},f.lineX1=function(){return s().x(r).y(n)},f.defined=function(t){return arguments.length?(i="function"==typeof t?t:gm(!!t),f):i},f.curve=function(t){return arguments.length?(a=t,null!=o&&(u=a(o)),f):a},f.context=function(t){return arguments.length?(null==t?o=u=null:u=a(o=t),f):o},f}function Lm(t,n){return nt?1:n>=t?0:NaN}function jm(t){return t}qm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var Hm=Gm(Um);function Xm(t){this._curve=t}function Gm(t){function n(n){return new Xm(t(n))}return n._curve=t,n}function Vm(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(Gm(t)):n()._curve},t}function Wm(){return Vm(Bm().curve(Hm))}function Zm(){var t=Ym().curve(Hm),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Vm(e())},delete t.lineX0,t.lineEndAngle=function(){return Vm(r())},delete t.lineX1,t.lineInnerRadius=function(){return Vm(i())},delete t.lineY0,t.lineOuterRadius=function(){return Vm(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(Gm(t)):n()._curve},t}function Km(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}Xm.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};class Qm{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n)}this._x0=t,this._y0=n}}class Jm{constructor(t){this._context=t}lineStart(){this._point=0}lineEnd(){}point(t,n){if(t=+t,n=+n,0===this._point)this._point=1;else{const e=Km(this._x0,this._y0),r=Km(this._x0,this._y0=(this._y0+n)/2),i=Km(t,this._y0),o=Km(t,n);this._context.moveTo(...e),this._context.bezierCurveTo(...r,...i,...o)}this._x0=t,this._y0=n}}function tx(t){return new Qm(t,!0)}function nx(t){return new Qm(t,!1)}function ex(t){return new Jm(t)}function rx(t){return t.source}function ix(t){return t.target}function ox(t){let n=rx,e=ix,r=Im,i=Om,o=null,a=null,u=Nm(c);function c(){let c;const f=Rm.call(arguments),s=n.apply(this,f),l=e.apply(this,f);if(null==o&&(a=t(c=u())),a.lineStart(),f[0]=s,a.point(+r.apply(this,f),+i.apply(this,f)),f[0]=l,a.point(+r.apply(this,f),+i.apply(this,f)),a.lineEnd(),c)return a=null,c+""||null}return c.source=function(t){return arguments.length?(n=t,c):n},c.target=function(t){return arguments.length?(e=t,c):e},c.x=function(t){return arguments.length?(r="function"==typeof t?t:gm(+t),c):r},c.y=function(t){return arguments.length?(i="function"==typeof t?t:gm(+t),c):i},c.context=function(n){return arguments.length?(null==n?o=a=null:a=t(o=n),c):o},c}const ax=wm(3);var ux={draw(t,n){const e=.59436*wm(n+mm(n/28,.75)),r=e/2,i=r*ax;t.moveTo(0,e),t.lineTo(0,-e),t.moveTo(-i,-r),t.lineTo(i,r),t.moveTo(-i,r),t.lineTo(i,-r)}},cx={draw(t,n){const e=wm(n/Tm);t.moveTo(e,0),t.arc(0,0,e,0,Sm)}},fx={draw(t,n){const e=wm(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}};const sx=wm(1/3),lx=2*sx;var hx={draw(t,n){const e=wm(n/lx),r=e*sx;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},dx={draw(t,n){const e=.62625*wm(n);t.moveTo(0,-e),t.lineTo(e,0),t.lineTo(0,e),t.lineTo(-e,0),t.closePath()}},px={draw(t,n){const e=.87559*wm(n-mm(n/7,2));t.moveTo(-e,0),t.lineTo(e,0),t.moveTo(0,e),t.lineTo(0,-e)}},gx={draw(t,n){const e=wm(n),r=-e/2;t.rect(r,r,e,e)}},yx={draw(t,n){const e=.4431*wm(n);t.moveTo(e,e),t.lineTo(e,-e),t.lineTo(-e,-e),t.lineTo(-e,e),t.closePath()}};const vx=xm(Tm/10)/xm(7*Tm/10),_x=xm(Sm/10)*vx,bx=-_m(Sm/10)*vx;var mx={draw(t,n){const e=wm(.8908130915292852*n),r=_x*e,i=bx*e;t.moveTo(0,-e),t.lineTo(r,i);for(let n=1;n<5;++n){const o=Sm*n/5,a=_m(o),u=xm(o);t.lineTo(u*e,-a*e),t.lineTo(a*r-u*i,u*r+a*i)}t.closePath()}};const xx=wm(3);var wx={draw(t,n){const e=-wm(n/(3*xx));t.moveTo(0,2*e),t.lineTo(-xx*e,-e),t.lineTo(xx*e,-e),t.closePath()}};const Mx=wm(3);var Tx={draw(t,n){const e=.6824*wm(n),r=e/2,i=e*Mx/2;t.moveTo(0,-e),t.lineTo(i,r),t.lineTo(-i,r),t.closePath()}};const Ax=-.5,Sx=wm(3)/2,Ex=1/wm(12),Nx=3*(Ex/2+1);var kx={draw(t,n){const e=wm(n/Nx),r=e/2,i=e*Ex,o=r,a=e*Ex+e,u=-o,c=a;t.moveTo(r,i),t.lineTo(o,a),t.lineTo(u,c),t.lineTo(Ax*r-Sx*i,Sx*r+Ax*i),t.lineTo(Ax*o-Sx*a,Sx*o+Ax*a),t.lineTo(Ax*u-Sx*c,Sx*u+Ax*c),t.lineTo(Ax*r+Sx*i,Ax*i-Sx*r),t.lineTo(Ax*o+Sx*a,Ax*a-Sx*o),t.lineTo(Ax*u+Sx*c,Ax*c-Sx*u),t.closePath()}},Cx={draw(t,n){const e=.6189*wm(n-mm(n/6,1.7));t.moveTo(-e,-e),t.lineTo(e,e),t.moveTo(-e,e),t.lineTo(e,-e)}};const Px=[cx,fx,hx,gx,mx,wx,kx],zx=[cx,px,Cx,Tx,ux,yx,dx];function $x(){}function Dx(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function Rx(t){this._context=t}function Fx(t){this._context=t}function qx(t){this._context=t}function Ux(t,n){this._basis=new Rx(t),this._beta=n}Rx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Dx(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Dx(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Fx.prototype={areaStart:$x,areaEnd:$x,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:Dx(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},qx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:Dx(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Ux.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],a=t[e]-i,u=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*a),this._beta*n[c]+(1-this._beta)*(o+r*u));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var Ix=function t(n){function e(t){return 1===n?new Rx(t):new Ux(t,n)}return e.beta=function(n){return t(+n)},e}(.85);function Ox(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function Bx(t,n){this._context=t,this._k=(1-n)/6}Bx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Ox(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:Ox(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Yx=function t(n){function e(t){return new Bx(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Lx(t,n){this._context=t,this._k=(1-n)/6}Lx.prototype={areaStart:$x,areaEnd:$x,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Ox(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var jx=function t(n){function e(t){return new Lx(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Hx(t,n){this._context=t,this._k=(1-n)/6}Hx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Ox(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Xx=function t(n){function e(t){return new Hx(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Gx(t,n,e){var r=t._x1,i=t._y1,o=t._x2,a=t._y2;if(t._l01_a>Mm){var u=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*u-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*u-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>Mm){var f=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,s=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*f+t._x1*t._l23_2a-n*t._l12_2a)/s,a=(a*f+t._y1*t._l23_2a-e*t._l12_2a)/s}t._context.bezierCurveTo(r,i,o,a,t._x2,t._y2)}function Vx(t,n){this._context=t,this._alpha=n}Vx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:Gx(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Wx=function t(n){function e(t){return n?new Vx(t,n):new Bx(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Zx(t,n){this._context=t,this._alpha=n}Zx.prototype={areaStart:$x,areaEnd:$x,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Gx(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Kx=function t(n){function e(t){return n?new Zx(t,n):new Lx(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Qx(t,n){this._context=t,this._alpha=n}Qx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Gx(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Jx=function t(n){function e(t){return n?new Qx(t,n):new Hx(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function tw(t){this._context=t}function nw(t){return t<0?-1:1}function ew(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),a=(e-t._y1)/(i||r<0&&-0),u=(o*i+a*r)/(r+i);return(nw(o)+nw(a))*Math.min(Math.abs(o),Math.abs(a),.5*Math.abs(u))||0}function rw(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function iw(t,n,e){var r=t._x0,i=t._y0,o=t._x1,a=t._y1,u=(o-r)/3;t._context.bezierCurveTo(r+u,i+u*n,o-u,a-u*e,o,a)}function ow(t){this._context=t}function aw(t){this._context=new uw(t)}function uw(t){this._context=t}function cw(t){this._context=t}function fw(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),a=new Array(r);for(i[0]=0,o[0]=2,a[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(a[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i,o=1,a=t[n[0]],u=a.length;o=0;)e[n]=n;return e}function dw(t,n){return t[n]}function pw(t){const n=[];return n.key=t,n}function gw(t){var n=t.map(yw);return hw(t).sort((function(t,e){return n[t]-n[e]}))}function yw(t){for(var n,e=-1,r=0,i=t.length,o=-1/0;++eo&&(o=n,r=e);return r}function vw(t){var n=t.map(_w);return hw(t).sort((function(t,e){return n[t]-n[e]}))}function _w(t){for(var n,e=0,r=-1,i=t.length;++r=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}};var bw=t=>()=>t;function mw(t,{sourceEvent:n,target:e,transform:r,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:i}})}function xw(t,n,e){this.k=t,this.x=n,this.y=e}xw.prototype={constructor:xw,scale:function(t){return 1===t?this:new xw(this.k*t,this.x,this.y)},translate:function(t,n){return 0===t&0===n?this:new xw(this.k,this.x+this.k*t,this.y+this.k*n)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var ww=new xw(1,0,0);function Mw(t){for(;!t.__zoom;)if(!(t=t.parentNode))return ww;return t.__zoom}function Tw(t){t.stopImmediatePropagation()}function Aw(t){t.preventDefault(),t.stopImmediatePropagation()}function Sw(t){return!(t.ctrlKey&&"wheel"!==t.type||t.button)}function Ew(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function Nw(){return this.__zoom||ww}function kw(t){return-t.deltaY*(1===t.deltaMode?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function Cw(){return navigator.maxTouchPoints||"ontouchstart"in this}function Pw(t,n,e){var r=t.invertX(n[0][0])-e[0][0],i=t.invertX(n[1][0])-e[1][0],o=t.invertY(n[0][1])-e[0][1],a=t.invertY(n[1][1])-e[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}Mw.prototype=xw.prototype,t.Adder=T,t.Delaunay=Lu,t.FormatSpecifier=tf,t.InternMap=InternMap,t.InternSet=InternSet,t.Node=Qd,t.Path=Ua,t.Voronoi=qu,t.ZoomTransform=xw,t.active=function(t,n){var e,r,i=t.__transition;if(i)for(r in n=null==n?null:n+"",i)if((e=i[r]).state>qi&&e.name===n)return new po([[t]],Zo,n,+r);return null},t.arc=function(){var t=km,n=Cm,e=gm(0),r=null,i=Pm,o=zm,a=$m,u=null,c=Nm(f);function f(){var f,s,l=+t.apply(this,arguments),h=+n.apply(this,arguments),d=i.apply(this,arguments)-Am,p=o.apply(this,arguments)-Am,g=ym(p-d),y=p>d;if(u||(u=f=c()),hMm)if(g>Sm-Mm)u.moveTo(h*_m(d),h*xm(d)),u.arc(0,0,h,d,p,!y),l>Mm&&(u.moveTo(l*_m(p),l*xm(p)),u.arc(0,0,l,p,d,y));else{var v,_,b=d,m=p,x=d,w=p,M=g,T=g,A=a.apply(this,arguments)/2,S=A>Mm&&(r?+r.apply(this,arguments):wm(l*l+h*h)),E=mm(ym(h-l)/2,+e.apply(this,arguments)),N=E,k=E;if(S>Mm){var C=Em(S/l*xm(A)),P=Em(S/h*xm(A));(M-=2*C)>Mm?(x+=C*=y?1:-1,w-=C):(M=0,x=w=(d+p)/2),(T-=2*P)>Mm?(b+=P*=y?1:-1,m-=P):(T=0,b=m=(d+p)/2)}var z=h*_m(b),$=h*xm(b),D=l*_m(w),R=l*xm(w);if(E>Mm){var F,q=h*_m(m),U=h*xm(m),I=l*_m(x),O=l*xm(x);if(g1?0:t<-1?Tm:Math.acos(t)}((B*L+Y*j)/(wm(B*B+Y*Y)*wm(L*L+j*j)))/2),X=wm(F[0]*F[0]+F[1]*F[1]);N=mm(E,(l-X)/(H-1)),k=mm(E,(h-X)/(H+1))}else N=k=0}T>Mm?k>Mm?(v=Dm(I,O,z,$,h,k,y),_=Dm(q,U,D,R,h,k,y),u.moveTo(v.cx+v.x01,v.cy+v.y01),kMm&&M>Mm?N>Mm?(v=Dm(D,R,q,U,l,-N,y),_=Dm(z,$,I,O,l,-N,y),u.lineTo(v.cx+v.x01,v.cy+v.y01),N=0))throw new RangeError("invalid r");let e=t.length;if(!((e=Math.floor(e))>=0))throw new RangeError("invalid length");if(!e||!n)return t;const r=y(n),i=t.slice();return r(t,i,0,e,1),r(i,t,0,e,1),r(t,i,0,e,1),t},t.blur2=l,t.blurImage=h,t.brush=function(){return wa(la)},t.brushSelection=function(t){var n=t.__brush;return n?n.dim.output(n.selection):null},t.brushX=function(){return wa(fa)},t.brushY=function(){return wa(sa)},t.buffer=function(t,n){return fetch(t,n).then(_c)},t.chord=function(){return za(!1,!1)},t.chordDirected=function(){return za(!0,!1)},t.chordTranspose=function(){return za(!1,!0)},t.cluster=function(){var t=Ld,n=1,e=1,r=!1;function i(i){var o,a=0;i.eachAfter((function(n){var e=n.children;e?(n.x=function(t){return t.reduce(jd,0)/t.length}(e),n.y=function(t){return 1+t.reduce(Hd,0)}(e)):(n.x=o?a+=t(n,o):0,n.y=0,o=n)}));var u=function(t){for(var n;n=t.children;)t=n[0];return t}(i),c=function(t){for(var n;n=t.children;)t=n[n.length-1];return t}(i),f=u.x-t(u,c)/2,s=c.x+t(c,u)/2;return i.eachAfter(r?function(t){t.x=(t.x-i.x)*n,t.y=(i.y-t.y)*e}:function(t){t.x=(t.x-f)/(s-f)*n,t.y=(1-(i.y?t.y/i.y:1))*e})}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.color=ze,t.contourDensity=function(){var t=fu,n=su,e=lu,r=960,i=500,o=20,a=2,u=3*o,c=r+2*u>>a,f=i+2*u>>a,s=Qa(20);function h(r){var i=new Float32Array(c*f),s=Math.pow(2,-a),h=-1;for(const o of r){var d=(t(o,++h,r)+u)*s,p=(n(o,h,r)+u)*s,g=+e(o,h,r);if(g&&d>=0&&d=0&&pt*r)))(n).map(((t,n)=>(t.value=+e[n],p(t))))}function p(t){return t.coordinates.forEach(g),t}function g(t){t.forEach(y)}function y(t){t.forEach(v)}function v(t){t[0]=t[0]*Math.pow(2,a)-u,t[1]=t[1]*Math.pow(2,a)-u}function _(){return c=r+2*(u=3*o)>>a,f=i+2*u>>a,d}return d.contours=function(t){var n=h(t),e=iu().size([c,f]),r=Math.pow(2,2*a),i=t=>{t=+t;var i=p(e.contour(n,t*r));return i.value=t,i};return Object.defineProperty(i,"max",{get:()=>J(n)/r}),i},d.x=function(n){return arguments.length?(t="function"==typeof n?n:Qa(+n),d):t},d.y=function(t){return arguments.length?(n="function"==typeof t?t:Qa(+t),d):n},d.weight=function(t){return arguments.length?(e="function"==typeof t?t:Qa(+t),d):e},d.size=function(t){if(!arguments.length)return[r,i];var n=+t[0],e=+t[1];if(!(n>=0&&e>=0))throw new Error("invalid size");return r=n,i=e,_()},d.cellSize=function(t){if(!arguments.length)return 1<=1))throw new Error("invalid cell size");return a=Math.floor(Math.log(t)/Math.LN2),_()},d.thresholds=function(t){return arguments.length?(s="function"==typeof t?t:Array.isArray(t)?Qa(Za.call(t)):Qa(t),d):s},d.bandwidth=function(t){if(!arguments.length)return Math.sqrt(o*(o+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return o=(Math.sqrt(4*t*t+1)-1)/2,_()},d},t.contours=iu,t.count=v,t.create=function(t){return Zn(Yt(t).call(document.documentElement))},t.creator=Yt,t.cross=function(...t){const n="function"==typeof t[t.length-1]&&function(t){return n=>t(...n)}(t.pop()),e=(t=t.map(m)).map(_),r=t.length-1,i=new Array(r+1).fill(0),o=[];if(r<0||e.some(b))return o;for(;;){o.push(i.map(((n,e)=>t[e][n])));let a=r;for(;++i[a]===e[a];){if(0===a)return n?o.map(n):o;i[a--]=0}}},t.csv=wc,t.csvFormat=rc,t.csvFormatBody=ic,t.csvFormatRow=ac,t.csvFormatRows=oc,t.csvFormatValue=uc,t.csvParse=nc,t.csvParseRows=ec,t.cubehelix=Tr,t.cumsum=function(t,n){var e=0,r=0;return Float64Array.from(t,void 0===n?t=>e+=+t||0:i=>e+=+n(i,r++,t)||0)},t.curveBasis=function(t){return new Rx(t)},t.curveBasisClosed=function(t){return new Fx(t)},t.curveBasisOpen=function(t){return new qx(t)},t.curveBumpX=tx,t.curveBumpY=nx,t.curveBundle=Ix,t.curveCardinal=Yx,t.curveCardinalClosed=jx,t.curveCardinalOpen=Xx,t.curveCatmullRom=Wx,t.curveCatmullRomClosed=Kx,t.curveCatmullRomOpen=Jx,t.curveLinear=Um,t.curveLinearClosed=function(t){return new tw(t)},t.curveMonotoneX=function(t){return new ow(t)},t.curveMonotoneY=function(t){return new aw(t)},t.curveNatural=function(t){return new cw(t)},t.curveStep=function(t){return new sw(t,.5)},t.curveStepAfter=function(t){return new sw(t,1)},t.curveStepBefore=function(t){return new sw(t,0)},t.descending=e,t.deviation=w,t.difference=function(t,...n){t=new InternSet(t);for(const e of n)for(const n of e)t.delete(n);return t},t.disjoint=function(t,n){const e=n[Symbol.iterator](),r=new InternSet;for(const n of t){if(r.has(n))return!1;let t,i;for(;({value:t,done:i}=e.next())&&!i;){if(Object.is(n,t))return!1;r.add(t)}}return!0},t.dispatch=$t,t.drag=function(){var t,n,e,r,i=se,o=le,a=he,u=de,c={},f=$t("start","drag","end"),s=0,l=0;function h(t){t.on("mousedown.drag",d).filter(u).on("touchstart.drag",y).on("touchmove.drag",v,ee).on("touchend.drag touchcancel.drag",_).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(a,u){if(!r&&i.call(this,a,u)){var c=b(this,o.call(this,a,u),a,u,"mouse");c&&(Zn(a.view).on("mousemove.drag",p,re).on("mouseup.drag",g,re),ae(a.view),ie(a),e=!1,t=a.clientX,n=a.clientY,c("start",a))}}function p(r){if(oe(r),!e){var i=r.clientX-t,o=r.clientY-n;e=i*i+o*o>l}c.mouse("drag",r)}function g(t){Zn(t.view).on("mousemove.drag mouseup.drag",null),ue(t.view,e),oe(t),c.mouse("end",t)}function y(t,n){if(i.call(this,t,n)){var e,r,a=t.changedTouches,u=o.call(this,t,n),c=a.length;for(e=0;e+t,t.easePoly=wo,t.easePolyIn=mo,t.easePolyInOut=wo,t.easePolyOut=xo,t.easeQuad=_o,t.easeQuadIn=function(t){return t*t},t.easeQuadInOut=_o,t.easeQuadOut=function(t){return t*(2-t)},t.easeSin=Ao,t.easeSinIn=function(t){return 1==+t?1:1-Math.cos(t*To)},t.easeSinInOut=Ao,t.easeSinOut=function(t){return Math.sin(t*To)},t.every=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(!n(r,++e,t))return!1;return!0},t.extent=M,t.fcumsum=function(t,n){const e=new T;let r=-1;return Float64Array.from(t,void 0===n?t=>e.add(+t||0):i=>e.add(+n(i,++r,t)||0))},t.filter=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");const e=[];let r=-1;for(const i of t)n(i,++r,t)&&e.push(i);return e},t.flatGroup=function(t,...n){return z(P(t,...n),n)},t.flatRollup=function(t,n,...e){return z(D(t,n,...e),e)},t.forceCenter=function(t,n){var e,r=1;function i(){var i,o,a=e.length,u=0,c=0;for(i=0;if+p||os+p||ac.index){var g=f-u.x-u.vx,y=s-u.y-u.vy,v=g*g+y*y;vt.r&&(t.r=t[n].r)}function c(){if(n){var r,i,o=n.length;for(e=new Array(o),r=0;r[u(t,n,r),t])));for(a=0,i=new Array(f);a=u)){(t.data!==n||t.next)&&(0===l&&(p+=(l=Uc(e))*l),0===h&&(p+=(h=Uc(e))*h),p(t=(Lc*t+jc)%Hc)/Hc}();function l(){h(),f.call("tick",n),e1?(null==e?u.delete(t):u.set(t,p(e)),n):u.get(t)},find:function(n,e,r){var i,o,a,u,c,f=0,s=t.length;for(null==r?r=1/0:r*=r,f=0;f1?(f.on(t,e),n):f.on(t)}}},t.forceX=function(t){var n,e,r,i=qc(.1);function o(t){for(var i,o=0,a=n.length;o=.12&&i<.234&&r>=-.425&&r<-.214?u:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:a).invert(t)},s.stream=function(e){return t&&n===e?t:(r=[a.stream(n=e),u.stream(e),c.stream(e)],i=r.length,t={point:function(t,n){for(var e=-1;++ejs(r[0],r[1])&&(r[1]=i[1]),js(i[0],r[1])>js(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(a=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(u=js(r[1],i[0]))>a&&(a=u,Wf=i[0],Kf=r[1])}return is=os=null,Wf===1/0||Zf===1/0?[[NaN,NaN],[NaN,NaN]]:[[Wf,Zf],[Kf,Qf]]},t.geoCentroid=function(t){ms=xs=ws=Ms=Ts=As=Ss=Es=0,Ns=new T,ks=new T,Cs=new T,Lf(t,Gs);var n=+Ns,e=+ks,r=+Cs,i=Ef(n,e,r);return i=0))throw new RangeError(`invalid digits: ${t}`);i=n}return null===n&&(r=new ed(i)),a},a.projection(t).digits(i).context(n)},t.geoProjection=yd,t.geoProjectionMutator=vd,t.geoRotation=ll,t.geoStereographic=function(){return yd(Bd).scale(250).clipAngle(142)},t.geoStereographicRaw=Bd,t.geoStream=Lf,t.geoTransform=function(t){return{stream:id(t)}},t.geoTransverseMercator=function(){var t=Ed(Yd),n=t.center,e=t.rotate;return t.center=function(t){return arguments.length?n([-t[1],t[0]]):[(t=n())[1],-t[0]]},t.rotate=function(t){return arguments.length?e([t[0],t[1],t.length>2?t[2]+90:90]):[(t=e())[0],t[1],t[2]-90]},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=Yd,t.gray=function(t,n){return new ur(t,0,0,null==n?1:n)},t.greatest=ot,t.greatestIndex=function(t,e=n){if(1===e.length)return tt(t,e);let r,i=-1,o=-1;for(const n of t)++o,(i<0?0===e(n,n):e(n,r)>0)&&(r=n,i=o);return i},t.group=C,t.groupSort=function(t,e,r){return(2!==e.length?U($(t,e,r),(([t,e],[r,i])=>n(e,i)||n(t,r))):U(C(t,r),(([t,r],[i,o])=>e(r,o)||n(t,i)))).map((([t])=>t))},t.groups=P,t.hcl=dr,t.hierarchy=Gd,t.histogram=Q,t.hsl=He,t.html=Ec,t.image=function(t,n){return new Promise((function(e,r){var i=new Image;for(var o in n)i[o]=n[o];i.onerror=r,i.onload=function(){e(i)},i.src=t}))},t.index=function(t,...n){return F(t,k,R,n)},t.indexes=function(t,...n){return F(t,Array.from,R,n)},t.interpolate=Gr,t.interpolateArray=function(t,n){return(Ir(n)?Ur:Or)(t,n)},t.interpolateBasis=Er,t.interpolateBasisClosed=Nr,t.interpolateBlues=Xb,t.interpolateBrBG=ib,t.interpolateBuGn=wb,t.interpolateBuPu=Tb,t.interpolateCividis=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"},t.interpolateCool=om,t.interpolateCubehelix=li,t.interpolateCubehelixDefault=rm,t.interpolateCubehelixLong=hi,t.interpolateDate=Br,t.interpolateDiscrete=function(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}},t.interpolateGnBu=Sb,t.interpolateGreens=Vb,t.interpolateGreys=Zb,t.interpolateHcl=ci,t.interpolateHclLong=fi,t.interpolateHsl=oi,t.interpolateHslLong=ai,t.interpolateHue=function(t,n){var e=Pr(+t,+n);return function(t){var n=e(t);return n-360*Math.floor(n/360)}},t.interpolateInferno=dm,t.interpolateLab=function(t,n){var e=$r((t=ar(t)).l,(n=ar(n)).l),r=$r(t.a,n.a),i=$r(t.b,n.b),o=$r(t.opacity,n.opacity);return function(n){return t.l=e(n),t.a=r(n),t.b=i(n),t.opacity=o(n),t+""}},t.interpolateMagma=hm,t.interpolateNumber=Yr,t.interpolateNumberArray=Ur,t.interpolateObject=Lr,t.interpolateOrRd=Nb,t.interpolateOranges=em,t.interpolatePRGn=ab,t.interpolatePiYG=cb,t.interpolatePlasma=pm,t.interpolatePuBu=zb,t.interpolatePuBuGn=Cb,t.interpolatePuOr=sb,t.interpolatePuRd=Db,t.interpolatePurples=Qb,t.interpolateRainbow=function(t){(t<0||t>1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return am.h=360*t-100,am.s=1.5-1.5*n,am.l=.8-.9*n,am+""},t.interpolateRdBu=hb,t.interpolateRdGy=pb,t.interpolateRdPu=Fb,t.interpolateRdYlBu=yb,t.interpolateRdYlGn=_b,t.interpolateReds=tm,t.interpolateRgb=Dr,t.interpolateRgbBasis=Fr,t.interpolateRgbBasisClosed=qr,t.interpolateRound=Vr,t.interpolateSinebow=function(t){var n;return t=(.5-t)*Math.PI,um.r=255*(n=Math.sin(t))*n,um.g=255*(n=Math.sin(t+cm))*n,um.b=255*(n=Math.sin(t+fm))*n,um+""},t.interpolateSpectral=mb,t.interpolateString=Xr,t.interpolateTransformCss=ti,t.interpolateTransformSvg=ni,t.interpolateTurbo=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"},t.interpolateViridis=lm,t.interpolateWarm=im,t.interpolateYlGn=Ob,t.interpolateYlGnBu=Ub,t.interpolateYlOrBr=Yb,t.interpolateYlOrRd=jb,t.interpolateZoom=ri,t.interrupt=Gi,t.intersection=function(t,...n){t=new InternSet(t),n=n.map(vt);t:for(const e of t)for(const r of n)if(!r.has(e)){t.delete(e);continue t}return t},t.interval=function(t,n,e){var r=new Ei,i=n;return null==n?(r.restart(t,n,e),r):(r._restart=r.restart,r.restart=function(t,n,e){n=+n,e=null==e?Ai():+e,r._restart((function o(a){a+=i,r._restart(o,i+=n,e),t(a)}),n,e)},r.restart(t,n,e),r)},t.isoFormat=D_,t.isoParse=F_,t.json=function(t,n){return fetch(t,n).then(Tc)},t.lab=ar,t.lch=function(t,n,e,r){return 1===arguments.length?hr(t):new pr(e,n,t,null==r?1:r)},t.least=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)<0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)<0:0===e(n,n))&&(r=n,i=!0);return r},t.leastIndex=ht,t.line=Bm,t.lineRadial=Wm,t.link=ox,t.linkHorizontal=function(){return ox(tx)},t.linkRadial=function(){const t=ox(ex);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.linkVertical=function(){return ox(nx)},t.local=Qn,t.map=function(t,n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");if("function"!=typeof n)throw new TypeError("mapper is not a function");return Array.from(t,((e,r)=>n(e,r,t)))},t.matcher=Vt,t.max=J,t.maxIndex=tt,t.mean=function(t,n){let e=0,r=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(++e,r+=n);else{let i=-1;for(let o of t)null!=(o=n(o,++i,t))&&(o=+o)>=o&&(++e,r+=o)}if(e)return r/e},t.median=function(t,n){return at(t,.5,n)},t.medianIndex=function(t,n){return ct(t,.5,n)},t.merge=ft,t.min=nt,t.minIndex=et,t.mode=function(t,n){const e=new InternMap;if(void 0===n)for(let n of t)null!=n&&n>=n&&e.set(n,(e.get(n)||0)+1);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&i>=i&&e.set(i,(e.get(i)||0)+1)}let r,i=0;for(const[t,n]of e)n>i&&(i=n,r=t);return r},t.namespace=It,t.namespaces=Ut,t.nice=Z,t.now=Ai,t.pack=function(){var t=null,n=1,e=1,r=np;function i(i){const o=ap();return i.x=n/2,i.y=e/2,t?i.eachBefore(xp(t)).eachAfter(wp(r,.5,o)).eachBefore(Mp(1)):i.eachBefore(xp(mp)).eachAfter(wp(np,1,o)).eachAfter(wp(r,i.r/Math.min(n,e),o)).eachBefore(Mp(Math.min(n,e)/(2*i.r))),i}return i.radius=function(n){return arguments.length?(t=Jd(n),i):t},i.size=function(t){return arguments.length?(n=+t[0],e=+t[1],i):[n,e]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:ep(+t),i):r},i},t.packEnclose=function(t){return up(t,ap())},t.packSiblings=function(t){return bp(t,ap()),t},t.pairs=function(t,n=st){const e=[];let r,i=!1;for(const o of t)i&&e.push(n(r,o)),r=o,i=!0;return e},t.partition=function(){var t=1,n=1,e=0,r=!1;function i(i){var o=i.height+1;return i.x0=i.y0=e,i.x1=t,i.y1=n/o,i.eachBefore(function(t,n){return function(r){r.children&&Ap(r,r.x0,t*(r.depth+1)/n,r.x1,t*(r.depth+2)/n);var i=r.x0,o=r.y0,a=r.x1-e,u=r.y1-e;a0&&(d+=l);for(null!=n?p.sort((function(t,e){return n(g[t],g[e])})):null!=e&&p.sort((function(t,n){return e(a[t],a[n])})),u=0,f=d?(v-h*b)/d:0;u0?l*f:0)+b,g[c]={data:a[c],index:u,value:l,startAngle:y,endAngle:s,padAngle:_};return g}return a.value=function(n){return arguments.length?(t="function"==typeof n?n:gm(+n),a):t},a.sortValues=function(t){return arguments.length?(n=t,e=null,a):n},a.sort=function(t){return arguments.length?(e=t,n=null,a):e},a.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:gm(+t),a):r},a.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:gm(+t),a):i},a.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:gm(+t),a):o},a},t.piecewise=di,t.pointRadial=Km,t.pointer=ne,t.pointers=function(t,n){return t.target&&(t=te(t),void 0===n&&(n=t.currentTarget),t=t.touches||[t]),Array.from(t,(t=>ne(t,n)))},t.polygonArea=function(t){for(var n,e=-1,r=t.length,i=t[r-1],o=0;++eu!=f>u&&a<(c-e)*(u-r)/(f-r)+e&&(s=!s),c=e,f=r;return s},t.polygonHull=function(t){if((e=t.length)<3)return null;var n,e,r=new Array(e),i=new Array(e);for(n=0;n=0;--n)f.push(t[r[o[n]][2]]);for(n=+u;n(n=1664525*n+1013904223|0,lg*(n>>>0))},t.randomLogNormal=Kp,t.randomLogistic=fg,t.randomNormal=Zp,t.randomPareto=ng,t.randomPoisson=sg,t.randomUniform=Vp,t.randomWeibull=ug,t.range=lt,t.rank=function(t,e=n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");let r=Array.from(t);const i=new Float64Array(r.length);2!==e.length&&(r=r.map(e),e=n);const o=(t,n)=>e(r[t],r[n]);let a,u;return(t=Uint32Array.from(r,((t,n)=>n))).sort(e===n?(t,n)=>O(r[t],r[n]):I(o)),t.forEach(((t,n)=>{const e=o(t,void 0===a?t:a);e>=0?((void 0===a||e>0)&&(a=t,u=n),i[t]=u):i[t]=NaN})),i},t.reduce=function(t,n,e){if("function"!=typeof n)throw new TypeError("reducer is not a function");const r=t[Symbol.iterator]();let i,o,a=-1;if(arguments.length<3){if(({done:i,value:e}=r.next()),i)return;++a}for(;({done:i,value:o}=r.next()),!i;)e=n(e,o,++a,t);return e},t.reverse=function(t){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");return Array.from(t).reverse()},t.rgb=Fe,t.ribbon=function(){return Wa()},t.ribbonArrow=function(){return Wa(Va)},t.rollup=$,t.rollups=D,t.scaleBand=yg,t.scaleDiverging=function t(){var n=Ng(L_()(mg));return n.copy=function(){return B_(n,t())},dg.apply(n,arguments)},t.scaleDivergingLog=function t(){var n=Fg(L_()).domain([.1,1,10]);return n.copy=function(){return B_(n,t()).base(n.base())},dg.apply(n,arguments)},t.scaleDivergingPow=j_,t.scaleDivergingSqrt=function(){return j_.apply(null,arguments).exponent(.5)},t.scaleDivergingSymlog=function t(){var n=Ig(L_());return n.copy=function(){return B_(n,t()).constant(n.constant())},dg.apply(n,arguments)},t.scaleIdentity=function t(n){var e;function r(t){return null==t||isNaN(t=+t)?e:t}return r.invert=r,r.domain=r.range=function(t){return arguments.length?(n=Array.from(t,_g),r):n.slice()},r.unknown=function(t){return arguments.length?(e=t,r):e},r.copy=function(){return t(n).unknown(e)},n=arguments.length?Array.from(n,_g):[0,1],Ng(r)},t.scaleImplicit=pg,t.scaleLinear=function t(){var n=Sg();return n.copy=function(){return Tg(n,t())},hg.apply(n,arguments),Ng(n)},t.scaleLog=function t(){const n=Fg(Ag()).domain([1,10]);return n.copy=()=>Tg(n,t()).base(n.base()),hg.apply(n,arguments),n},t.scaleOrdinal=gg,t.scalePoint=function(){return vg(yg.apply(null,arguments).paddingInner(1))},t.scalePow=jg,t.scaleQuantile=function t(){var e,r=[],i=[],o=[];function a(){var t=0,n=Math.max(1,i.length);for(o=new Array(n-1);++t0?o[n-1]:r[0],n=i?[o[i-1],r]:[o[n-1],o[n]]},u.unknown=function(t){return arguments.length?(n=t,u):u},u.thresholds=function(){return o.slice()},u.copy=function(){return t().domain([e,r]).range(a).unknown(n)},hg.apply(Ng(u),arguments)},t.scaleRadial=function t(){var n,e=Sg(),r=[0,1],i=!1;function o(t){var r=function(t){return Math.sign(t)*Math.sqrt(Math.abs(t))}(e(t));return isNaN(r)?n:i?Math.round(r):r}return o.invert=function(t){return e.invert(Hg(t))},o.domain=function(t){return arguments.length?(e.domain(t),o):e.domain()},o.range=function(t){return arguments.length?(e.range((r=Array.from(t,_g)).map(Hg)),o):r.slice()},o.rangeRound=function(t){return o.range(t).round(!0)},o.round=function(t){return arguments.length?(i=!!t,o):i},o.clamp=function(t){return arguments.length?(e.clamp(t),o):e.clamp()},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t(e.domain(),r).round(i).clamp(e.clamp()).unknown(n)},hg.apply(o,arguments),Ng(o)},t.scaleSequential=function t(){var n=Ng(O_()(mg));return n.copy=function(){return B_(n,t())},dg.apply(n,arguments)},t.scaleSequentialLog=function t(){var n=Fg(O_()).domain([1,10]);return n.copy=function(){return B_(n,t()).base(n.base())},dg.apply(n,arguments)},t.scaleSequentialPow=Y_,t.scaleSequentialQuantile=function t(){var e=[],r=mg;function i(t){if(null!=t&&!isNaN(t=+t))return r((s(e,t,1)-1)/(e.length-1))}return i.domain=function(t){if(!arguments.length)return e.slice();e=[];for(let n of t)null==n||isNaN(n=+n)||e.push(n);return e.sort(n),i},i.interpolator=function(t){return arguments.length?(r=t,i):r},i.range=function(){return e.map(((t,n)=>r(n/(e.length-1))))},i.quantiles=function(t){return Array.from({length:t+1},((n,r)=>at(e,r/t)))},i.copy=function(){return t(r).domain(e)},dg.apply(i,arguments)},t.scaleSequentialSqrt=function(){return Y_.apply(null,arguments).exponent(.5)},t.scaleSequentialSymlog=function t(){var n=Ig(O_());return n.copy=function(){return B_(n,t()).constant(n.constant())},dg.apply(n,arguments)},t.scaleSqrt=function(){return jg.apply(null,arguments).exponent(.5)},t.scaleSymlog=function t(){var n=Ig(Ag());return n.copy=function(){return Tg(n,t()).constant(n.constant())},hg.apply(n,arguments)},t.scaleThreshold=function t(){var n,e=[.5],r=[0,1],i=1;function o(t){return null!=t&&t<=t?r[s(e,t,0,i)]:n}return o.domain=function(t){return arguments.length?(e=Array.from(t),i=Math.min(e.length,r.length-1),o):e.slice()},o.range=function(t){return arguments.length?(r=Array.from(t),i=Math.min(e.length,r.length-1),o):r.slice()},o.invertExtent=function(t){var n=r.indexOf(t);return[e[n-1],e[n]]},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t().domain(e).range(r).unknown(n)},hg.apply(o,arguments)},t.scaleTime=function(){return hg.apply(I_(uv,cv,tv,Zy,xy,py,sy,ay,iy,t.timeFormat).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},t.scaleUtc=function(){return hg.apply(I_(ov,av,ev,Qy,Fy,yy,hy,cy,iy,t.utcFormat).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)},t.scan=function(t,n){const e=ht(t,n);return e<0?void 0:e},t.schemeAccent=G_,t.schemeBlues=Hb,t.schemeBrBG=rb,t.schemeBuGn=xb,t.schemeBuPu=Mb,t.schemeCategory10=X_,t.schemeDark2=V_,t.schemeGnBu=Ab,t.schemeGreens=Gb,t.schemeGreys=Wb,t.schemeOrRd=Eb,t.schemeOranges=nm,t.schemePRGn=ob,t.schemePaired=W_,t.schemePastel1=Z_,t.schemePastel2=K_,t.schemePiYG=ub,t.schemePuBu=Pb,t.schemePuBuGn=kb,t.schemePuOr=fb,t.schemePuRd=$b,t.schemePurples=Kb,t.schemeRdBu=lb,t.schemeRdGy=db,t.schemeRdPu=Rb,t.schemeRdYlBu=gb,t.schemeRdYlGn=vb,t.schemeReds=Jb,t.schemeSet1=Q_,t.schemeSet2=J_,t.schemeSet3=tb,t.schemeSpectral=bb,t.schemeTableau10=nb,t.schemeYlGn=Ib,t.schemeYlGnBu=qb,t.schemeYlOrBr=Bb,t.schemeYlOrRd=Lb,t.select=Zn,t.selectAll=function(t){return"string"==typeof t?new Vn([document.querySelectorAll(t)],[document.documentElement]):new Vn([Ht(t)],Gn)},t.selection=Wn,t.selector=jt,t.selectorAll=Gt,t.shuffle=dt,t.shuffler=pt,t.some=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(n(r,++e,t))return!0;return!1},t.sort=U,t.stack=function(){var t=gm([]),n=hw,e=lw,r=dw;function i(i){var o,a,u=Array.from(t.apply(this,arguments),pw),c=u.length,f=-1;for(const t of i)for(o=0,++f;o0)for(var e,r,i,o,a,u,c=0,f=t[n[0]].length;c0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=a,r[0]=a+=i):(r[0]=0,r[1]=i)},t.stackOffsetExpand=function(t,n){if((r=t.length)>0){for(var e,r,i,o=0,a=t[0].length;o0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,a=1;afunction(t){t=`${t}`;let n=t.length;zp(t,n-1)&&!zp(t,n-2)&&(t=t.slice(0,-1));return"/"===t[0]?t:`/${t}`}(t(n,e,r)))),e=n.map(Pp),i=new Set(n).add("");for(const t of e)i.has(t)||(i.add(t),n.push(t),e.push(Pp(t)),h.push(Np));d=(t,e)=>n[e],p=(t,n)=>e[n]}for(a=0,i=h.length;a=0&&(f=h[t]).data===Np;--t)f.data=null}if(u.parent=Sp,u.eachBefore((function(t){t.depth=t.parent.depth+1,--i})).eachBefore(Kd),u.parent=null,i>0)throw new Error("cycle");return u}return r.id=function(t){return arguments.length?(n=Jd(t),r):n},r.parentId=function(t){return arguments.length?(e=Jd(t),r):e},r.path=function(n){return arguments.length?(t=Jd(n),r):t},r},t.style=_n,t.subset=function(t,n){return _t(n,t)},t.sum=function(t,n){let e=0;if(void 0===n)for(let n of t)(n=+n)&&(e+=n);else{let r=-1;for(let i of t)(i=+n(i,++r,t))&&(e+=i)}return e},t.superset=_t,t.svg=Nc,t.symbol=function(t,n){let e=null,r=Nm(i);function i(){let i;if(e||(e=i=r()),t.apply(this,arguments).draw(e,+n.apply(this,arguments)),i)return e=null,i+""||null}return t="function"==typeof t?t:gm(t||cx),n="function"==typeof n?n:gm(void 0===n?64:+n),i.type=function(n){return arguments.length?(t="function"==typeof n?n:gm(n),i):t},i.size=function(t){return arguments.length?(n="function"==typeof t?t:gm(+t),i):n},i.context=function(t){return arguments.length?(e=null==t?null:t,i):e},i},t.symbolAsterisk=ux,t.symbolCircle=cx,t.symbolCross=fx,t.symbolDiamond=hx,t.symbolDiamond2=dx,t.symbolPlus=px,t.symbolSquare=gx,t.symbolSquare2=yx,t.symbolStar=mx,t.symbolTimes=Cx,t.symbolTriangle=wx,t.symbolTriangle2=Tx,t.symbolWye=kx,t.symbolX=Cx,t.symbols=Px,t.symbolsFill=Px,t.symbolsStroke=zx,t.text=mc,t.thresholdFreedmanDiaconis=function(t,n,e){const r=v(t),i=at(t,.75)-at(t,.25);return r&&i?Math.ceil((e-n)/(2*i*Math.pow(r,-1/3))):1},t.thresholdScott=function(t,n,e){const r=v(t),i=w(t);return r&&i?Math.ceil((e-n)*Math.cbrt(r)/(3.49*i)):1},t.thresholdSturges=K,t.tickFormat=Eg,t.tickIncrement=V,t.tickStep=W,t.ticks=G,t.timeDay=py,t.timeDays=gy,t.timeFormatDefaultLocale=P_,t.timeFormatLocale=hv,t.timeFriday=Sy,t.timeFridays=$y,t.timeHour=sy,t.timeHours=ly,t.timeInterval=Vg,t.timeMillisecond=Wg,t.timeMilliseconds=Zg,t.timeMinute=ay,t.timeMinutes=uy,t.timeMonday=wy,t.timeMondays=ky,t.timeMonth=Zy,t.timeMonths=Ky,t.timeSaturday=Ey,t.timeSaturdays=Dy,t.timeSecond=iy,t.timeSeconds=oy,t.timeSunday=xy,t.timeSundays=Ny,t.timeThursday=Ay,t.timeThursdays=zy,t.timeTickInterval=cv,t.timeTicks=uv,t.timeTuesday=My,t.timeTuesdays=Cy,t.timeWednesday=Ty,t.timeWednesdays=Py,t.timeWeek=xy,t.timeWeeks=Ny,t.timeYear=tv,t.timeYears=nv,t.timeout=$i,t.timer=Ni,t.timerFlush=ki,t.transition=go,t.transpose=gt,t.tree=function(){var t=$p,n=1,e=1,r=null;function i(i){var c=function(t){for(var n,e,r,i,o,a=new Up(t,0),u=[a];n=u.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)u.push(e=n.children[i]=new Up(r[i],i)),e.parent=n;return(a.parent=new Up(null,0)).children=[a],a}(i);if(c.eachAfter(o),c.parent.m=-c.z,c.eachBefore(a),r)i.eachBefore(u);else{var f=i,s=i,l=i;i.eachBefore((function(t){t.xs.x&&(s=t),t.depth>l.depth&&(l=t)}));var h=f===s?1:t(f,s)/2,d=h-f.x,p=n/(s.x+h+d),g=e/(l.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*g}))}return i}function o(n){var e=n.children,r=n.parent.children,i=n.i?r[n.i-1]:null;if(e){!function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)}(n);var o=(e[0].z+e[e.length-1].z)/2;i?(n.z=i.z+t(n._,i._),n.m=n.z-o):n.z=o}else i&&(n.z=i.z+t(n._,i._));n.parent.A=function(n,e,r){if(e){for(var i,o=n,a=n,u=e,c=o.parent.children[0],f=o.m,s=a.m,l=u.m,h=c.m;u=Rp(u),o=Dp(o),u&&o;)c=Dp(c),(a=Rp(a)).a=n,(i=u.z+l-o.z-f+t(u._,o._))>0&&(Fp(qp(u,n,r),n,i),f+=i,s+=i),l+=u.m,f+=o.m,h+=c.m,s+=a.m;u&&!Rp(a)&&(a.t=u,a.m+=l-s),o&&!Dp(c)&&(c.t=o,c.m+=f-h,r=n)}return r}(n,i,n.parent.A||r[0])}function a(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function u(t){t.x*=n,t.y=t.depth*e}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.treemap=function(){var t=Yp,n=!1,e=1,r=1,i=[0],o=np,a=np,u=np,c=np,f=np;function s(t){return t.x0=t.y0=0,t.x1=e,t.y1=r,t.eachBefore(l),i=[0],n&&t.eachBefore(Tp),t}function l(n){var e=i[n.depth],r=n.x0+e,s=n.y0+e,l=n.x1-e,h=n.y1-e;l=e-1){var s=u[n];return s.x0=i,s.y0=o,s.x1=a,void(s.y1=c)}var l=f[n],h=r/2+l,d=n+1,p=e-1;for(;d>>1;f[g]c-o){var _=r?(i*v+a*y)/r:a;t(n,d,y,i,o,_,c),t(d,e,v,_,o,a,c)}else{var b=r?(o*v+c*y)/r:c;t(n,d,y,i,o,a,b),t(d,e,v,i,b,a,c)}}(0,c,t.value,n,e,r,i)},t.treemapDice=Ap,t.treemapResquarify=Lp,t.treemapSlice=Ip,t.treemapSliceDice=function(t,n,e,r,i){(1&t.depth?Ip:Ap)(t,n,e,r,i)},t.treemapSquarify=Yp,t.tsv=Mc,t.tsvFormat=lc,t.tsvFormatBody=hc,t.tsvFormatRow=pc,t.tsvFormatRows=dc,t.tsvFormatValue=gc,t.tsvParse=fc,t.tsvParseRows=sc,t.union=function(...t){const n=new InternSet;for(const e of t)for(const t of e)n.add(t);return n},t.unixDay=_y,t.unixDays=by,t.utcDay=yy,t.utcDays=vy,t.utcFriday=By,t.utcFridays=Vy,t.utcHour=hy,t.utcHours=dy,t.utcMillisecond=Wg,t.utcMilliseconds=Zg,t.utcMinute=cy,t.utcMinutes=fy,t.utcMonday=qy,t.utcMondays=jy,t.utcMonth=Qy,t.utcMonths=Jy,t.utcSaturday=Yy,t.utcSaturdays=Wy,t.utcSecond=iy,t.utcSeconds=oy,t.utcSunday=Fy,t.utcSundays=Ly,t.utcThursday=Oy,t.utcThursdays=Gy,t.utcTickInterval=av,t.utcTicks=ov,t.utcTuesday=Uy,t.utcTuesdays=Hy,t.utcWednesday=Iy,t.utcWednesdays=Xy,t.utcWeek=Fy,t.utcWeeks=Ly,t.utcYear=ev,t.utcYears=rv,t.variance=x,t.version="7.8.5",t.window=pn,t.xml=Sc,t.zip=function(){return gt(arguments)},t.zoom=function(){var t,n,e,r=Sw,i=Ew,o=Pw,a=kw,u=Cw,c=[0,1/0],f=[[-1/0,-1/0],[1/0,1/0]],s=250,l=ri,h=$t("start","zoom","end"),d=500,p=150,g=0,y=10;function v(t){t.property("__zoom",Nw).on("wheel.zoom",T,{passive:!1}).on("mousedown.zoom",A).on("dblclick.zoom",S).filter(u).on("touchstart.zoom",E).on("touchmove.zoom",N).on("touchend.zoom touchcancel.zoom",k).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function _(t,n){return(n=Math.max(c[0],Math.min(c[1],n)))===t.k?t:new xw(n,t.x,t.y)}function b(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new xw(t.k,r,i)}function m(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function x(t,n,e,r){t.on("start.zoom",(function(){w(this,arguments).event(r).start()})).on("interrupt.zoom end.zoom",(function(){w(this,arguments).event(r).end()})).tween("zoom",(function(){var t=this,o=arguments,a=w(t,o).event(r),u=i.apply(t,o),c=null==e?m(u):"function"==typeof e?e.apply(t,o):e,f=Math.max(u[1][0]-u[0][0],u[1][1]-u[0][1]),s=t.__zoom,h="function"==typeof n?n.apply(t,o):n,d=l(s.invert(c).concat(f/s.k),h.invert(c).concat(f/h.k));return function(t){if(1===t)t=h;else{var n=d(t),e=f/n[2];t=new xw(e,c[0]-n[0]*e,c[1]-n[1]*e)}a.zoom(null,t)}}))}function w(t,n,e){return!e&&t.__zooming||new M(t,n)}function M(t,n){this.that=t,this.args=n,this.active=0,this.sourceEvent=null,this.extent=i.apply(t,n),this.taps=0}function T(t,...n){if(r.apply(this,arguments)){var e=w(this,n).event(t),i=this.__zoom,u=Math.max(c[0],Math.min(c[1],i.k*Math.pow(2,a.apply(this,arguments)))),s=ne(t);if(e.wheel)e.mouse[0][0]===s[0]&&e.mouse[0][1]===s[1]||(e.mouse[1]=i.invert(e.mouse[0]=s)),clearTimeout(e.wheel);else{if(i.k===u)return;e.mouse=[s,i.invert(s)],Gi(this),e.start()}Aw(t),e.wheel=setTimeout((function(){e.wheel=null,e.end()}),p),e.zoom("mouse",o(b(_(i,u),e.mouse[0],e.mouse[1]),e.extent,f))}}function A(t,...n){if(!e&&r.apply(this,arguments)){var i=t.currentTarget,a=w(this,n,!0).event(t),u=Zn(t.view).on("mousemove.zoom",(function(t){if(Aw(t),!a.moved){var n=t.clientX-s,e=t.clientY-l;a.moved=n*n+e*e>g}a.event(t).zoom("mouse",o(b(a.that.__zoom,a.mouse[0]=ne(t,i),a.mouse[1]),a.extent,f))}),!0).on("mouseup.zoom",(function(t){u.on("mousemove.zoom mouseup.zoom",null),ue(t.view,a.moved),Aw(t),a.event(t).end()}),!0),c=ne(t,i),s=t.clientX,l=t.clientY;ae(t.view),Tw(t),a.mouse=[c,this.__zoom.invert(c)],Gi(this),a.start()}}function S(t,...n){if(r.apply(this,arguments)){var e=this.__zoom,a=ne(t.changedTouches?t.changedTouches[0]:t,this),u=e.invert(a),c=e.k*(t.shiftKey?.5:2),l=o(b(_(e,c),a,u),i.apply(this,n),f);Aw(t),s>0?Zn(this).transition().duration(s).call(x,l,a,t):Zn(this).call(v.transform,l,a,t)}}function E(e,...i){if(r.apply(this,arguments)){var o,a,u,c,f=e.touches,s=f.length,l=w(this,i,e.changedTouches.length===s).event(e);for(Tw(e),a=0;a --help` for command-specific help.""" + + +# Constants for socket synchronization +_SYNC_TIMEOUT_SEC = 5.0 +_PROCESS_KILL_TIMEOUT_SEC = 2.0 +_READY_MESSAGE = b"ready" +_RECV_BUFFER_SIZE = 1024 +_BINARY_PROFILE_HEADER_SIZE = 64 +_BINARY_PROFILE_MAGICS = (b"HCAT", b"TACH") + +# Format configuration +FORMAT_EXTENSIONS = { + "pstats": "pstats", + "collapsed": "txt", + "flamegraph": "html", + "diff_flamegraph": "html", + "gecko": "json", + "heatmap": "html", + "jsonl": "jsonl", + "binary": "bin", +} + +COLLECTOR_MAP = { + "pstats": PstatsCollector, + "collapsed": CollapsedStackCollector, + "flamegraph": FlamegraphCollector, + "diff_flamegraph": DiffFlamegraphCollector, + "gecko": GeckoCollector, + "heatmap": HeatmapCollector, + "jsonl": JsonlCollector, + "binary": BinaryCollector, +} + +def _setup_child_monitor(args, parent_pid): + # Build CLI args for child profilers (excluding --subprocesses to avoid recursion) + child_cli_args = _build_child_profiler_args(args) + + # Build output pattern + output_pattern = _build_output_pattern(args) + + return ChildProcessMonitor( + pid=parent_pid, + cli_args=child_cli_args, + output_pattern=output_pattern, + ) + + +def _get_child_monitor_context(args, pid): + if getattr(args, 'subprocesses', False): + return _setup_child_monitor(args, pid) + return nullcontext() + + +def _build_child_profiler_args(args): + child_args = [] + + # Sampling options + hz = MICROSECONDS_PER_SECOND // args.sample_interval_usec + child_args.extend(["-r", str(hz)]) + if args.duration is not None: + child_args.extend(["-d", str(args.duration)]) + if args.all_threads: + child_args.append("-a") + if args.realtime_stats: + child_args.append("--realtime-stats") + if args.native: + child_args.append("--native") + if not args.gc: + child_args.append("--no-gc") + if args.opcodes: + child_args.append("--opcodes") + if args.async_aware: + child_args.append("--async-aware") + async_mode = getattr(args, 'async_mode', 'running') + if async_mode != "running": + child_args.extend(["--async-mode", async_mode]) + + # Mode options + mode = getattr(args, 'mode', 'wall') + if mode != "wall": + child_args.extend(["--mode", mode]) + + # Format options (skip pstats as it's the default) + if args.format == "diff_flamegraph": + child_args.extend(["--diff-flamegraph", args.diff_baseline]) + elif args.format != "pstats": + child_args.append(f"--{args.format}") + + return child_args + + +def _build_output_pattern(args): + """Build output filename pattern for child profilers. + + The pattern uses {pid} as a placeholder which will be replaced with the + actual child PID using str.replace(), so user filenames with braces are safe. + """ + if args.outfile: + # User specified output - add PID to filename + base, ext = os.path.splitext(args.outfile) + if ext: + return f"{base}_{{pid}}{ext}" + else: + return f"{args.outfile}_{{pid}}" + else: + # Use default pattern based on format (consistent _ separator) + extension = FORMAT_EXTENSIONS.get(args.format, "txt") + if args.format == "heatmap": + return "heatmap_{pid}" + if args.format == "pstats": + # pstats defaults to stdout, but for subprocesses we need files + return "profile_{pid}.pstats" + return f"{args.format}_{{pid}}.{extension}" + + +def _parse_mode(mode_string): + """Convert mode string to mode constant.""" + mode_map = { + "wall": PROFILING_MODE_WALL, + "cpu": PROFILING_MODE_CPU, + "gil": PROFILING_MODE_GIL, + "exception": PROFILING_MODE_EXCEPTION, + } + return mode_map[mode_string] + + +def _check_process_died(process): + """Check if process died and raise an error with stderr if available.""" + if process.poll() is None: + return + + # Process died - try to get stderr for error message + stderr_msg = "" + if process.stderr: + try: + stderr_msg = process.stderr.read().decode().strip() + except (OSError, UnicodeDecodeError): + pass + + if stderr_msg: + raise RuntimeError(stderr_msg) + raise RuntimeError(f"Process exited with code {process.returncode}") + + +def _wait_for_ready_signal(sync_sock, process, timeout): + """Wait for the ready signal from the subprocess, checking for early death.""" + deadline = time.monotonic() + timeout + sel = selectors.DefaultSelector() + sel.register(sync_sock, selectors.EVENT_READ) + + try: + while True: + _check_process_died(process) + + remaining = deadline - time.monotonic() + if remaining <= 0: + raise socket.timeout("timed out") + + if not sel.select(timeout=min(0.1, remaining)): + continue + + conn, _ = sync_sock.accept() + try: + ready_signal = conn.recv(_RECV_BUFFER_SIZE) + finally: + conn.close() + + if ready_signal != _READY_MESSAGE: + raise RuntimeError(f"Invalid ready signal received: {ready_signal!r}") + return + finally: + sel.close() + + +def _run_with_sync(original_cmd, suppress_output=False): + """Run a command with socket-based synchronization and return the process.""" + # Create a TCP socket for synchronization with better socket options + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sync_sock: + # Set SO_REUSEADDR to avoid "Address already in use" errors + sync_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sync_sock.bind(("127.0.0.1", 0)) # Let OS choose a free port + sync_port = sync_sock.getsockname()[1] + sync_sock.listen(1) + sync_sock.settimeout(_SYNC_TIMEOUT_SEC) + + # Get current working directory to preserve it + cwd = os.getcwd() + + # Build command using the sync coordinator + target_args = original_cmd[1:] # Remove python executable + cmd = ( + sys.executable, + "-m", + "profiling.sampling._sync_coordinator", + str(sync_port), + cwd, + ) + tuple(target_args) + + # Start the process with coordinator + # When suppress_output=True (live mode), capture stderr so we can + # report errors if the process dies before signaling ready. + # When suppress_output=False (normal mode), let stderr inherit so + # script errors print to the terminal. + popen_kwargs = {} + if suppress_output: + popen_kwargs["stdin"] = subprocess.DEVNULL + popen_kwargs["stdout"] = subprocess.DEVNULL + popen_kwargs["stderr"] = subprocess.PIPE + + process = subprocess.Popen(cmd, **popen_kwargs) + + try: + _wait_for_ready_signal(sync_sock, process, _SYNC_TIMEOUT_SEC) + except socket.timeout: + # If we timeout, kill the process and raise an error + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=_PROCESS_KILL_TIMEOUT_SEC) + except subprocess.TimeoutExpired: + process.kill() + process.wait() + raise RuntimeError( + "Process failed to signal readiness within timeout" + ) + + return process + + +_RATE_PATTERN = re.compile(r''' + ^ # Start of string + ( # Group 1: The numeric value + \d+ # One or more digits (integer part) + (?:\.\d+)? # Optional: decimal point followed by digits + ) # Examples: "10", "0.5", "100.25" + ( # Group 2: Optional unit suffix + hz # "hz" - hertz + | khz # "khz" - kilohertz + | k # "k" - shorthand for kilohertz + )? # Suffix is optional (bare number = Hz) + $ # End of string + ''', re.VERBOSE | re.IGNORECASE) + + +def _parse_sampling_rate(rate_str: str) -> int: + """Parse sampling rate string to microseconds.""" + rate_str = rate_str.strip().lower() + + match = _RATE_PATTERN.match(rate_str) + if not match: + raise argparse.ArgumentTypeError( + f"Invalid sampling rate format: {rate_str}. " + "Expected: number followed by optional suffix (hz, khz, k) with no spaces (e.g., 10khz)" + ) + + number_part = match.group(1) + suffix = match.group(2) or '' + + # Determine multiplier based on suffix + suffix_map = { + 'hz': 1, + 'khz': 1000, + 'k': 1000, + } + multiplier = suffix_map.get(suffix, 1) + hz = float(number_part) * multiplier + if hz <= 0: + raise argparse.ArgumentTypeError(f"Sampling rate must be positive: {rate_str}") + + interval_usec = int(MICROSECONDS_PER_SECOND / hz) + if interval_usec < 1: + raise argparse.ArgumentTypeError(f"Sampling rate too high: {rate_str}") + + return interval_usec + + +def _add_sampling_options(parser): + """Add sampling configuration options to a parser.""" + sampling_group = parser.add_argument_group("Sampling configuration") + sampling_group.add_argument( + "-r", + "--sampling-rate", + type=_parse_sampling_rate, + default="1khz", + metavar="RATE", + dest="sample_interval_usec", + help="sampling rate (e.g., 10000, 10khz, 10k)", + ) + sampling_group.add_argument( + "-d", + "--duration", + type=int, + default=None, + metavar="SECONDS", + help="Sampling duration (default: run to completion)", + ) + sampling_group.add_argument( + "-a", + "--all-threads", + action="store_true", + help="Sample all threads in the process instead of just the main thread", + ) + sampling_group.add_argument( + "--realtime-stats", + action="store_true", + help="Print real-time sampling statistics (Hz, mean, min, max) during profiling", + ) + sampling_group.add_argument( + "--native", + action="store_true", + help='Include artificial `` frames to denote calls to non-Python code', + ) + sampling_group.add_argument( + "--no-gc", + action="store_false", + dest="gc", + help='Don\'t include artificial `` frames to denote active garbage collection', + ) + sampling_group.add_argument( + "--opcodes", + action="store_true", + help="Gather bytecode opcode information for instruction-level profiling " + "(shows which bytecode instructions are executing, including specializations).", + ) + sampling_group.add_argument( + "--async-aware", + action="store_true", + help="Enable async-aware profiling (uses task-based stack reconstruction)", + ) + sampling_group.add_argument( + "--subprocesses", + action="store_true", + help="Also profile subprocesses. Each subprocess gets its own profiler and output file.", + ) + sampling_group.add_argument( + "--blocking", + action="store_true", + help="Stop all threads in target process before sampling to get consistent snapshots. " + "Uses thread_suspend on macOS and ptrace on Linux. Adds overhead but ensures memory " + "reads are from a frozen state.", + ) + + +def _add_mode_options(parser): + """Add mode options to a parser.""" + mode_group = parser.add_argument_group("Mode options") + mode_group.add_argument( + "--mode", + choices=["wall", "cpu", "gil", "exception"], + default="wall", + help="Sampling mode: wall (all samples), cpu (only samples when thread is on CPU), " + "gil (only samples when thread holds the GIL), " + "exception (only samples when thread has an active exception). " + "Incompatible with `--async-aware`", + ) + mode_group.add_argument( + "--async-mode", + choices=["running", "all"], + default="running", + help='Async profiling mode: "running" (only running task) ' + 'or "all" (all tasks including waiting). Requires `--async-aware`', + ) + + +def _add_format_options(parser, include_compression=True, include_binary=True): + """Add output format options to a parser.""" + output_group = parser.add_argument_group("Output options") + format_group = output_group.add_mutually_exclusive_group() + format_group.add_argument( + "--pstats", + action="store_const", + const="pstats", + dest="format", + help="Generate pstats output (default)", + ) + format_group.add_argument( + "--collapsed", + action="store_const", + const="collapsed", + dest="format", + help="Generate collapsed stack traces for flamegraphs", + ) + format_group.add_argument( + "--flamegraph", + action="store_const", + const="flamegraph", + dest="format", + help="Generate interactive HTML flamegraph visualization", + ) + format_group.add_argument( + "--gecko", + action="store_const", + const="gecko", + dest="format", + help="Generate Gecko format for Firefox Profiler", + ) + format_group.add_argument( + "--heatmap", + action="store_const", + const="heatmap", + dest="format", + help="Generate interactive HTML heatmap visualization with line-level sample counts", + ) + format_group.add_argument( + "--diff-flamegraph", + metavar="BASELINE", + action=DiffFlamegraphAction, + help="Generate differential flamegraph comparing current profile to `BASELINE` binary file", + ) + format_group.add_argument( + "--jsonl", + action="store_const", + const="jsonl", + dest="format", + help="Generate newline-delimited JSON (JSONL) for programmatic consumers", + ) + if include_binary: + format_group.add_argument( + "--binary", + action="store_const", + const="binary", + dest="format", + help="Generate high-performance binary format (use `replay` command to convert)", + ) + parser.set_defaults(format="pstats", diff_baseline=None) + + if include_compression: + output_group.add_argument( + "--compression", + choices=["auto", "zstd", "none"], + default="auto", + help="Compression for binary format: auto (use zstd if available), zstd, none", + ) + + output_group.add_argument( + "-o", + "--output", + dest="outfile", + help="Output path (default: `stdout` for `pstats` text; with `-o`, `pstats` is binary). " + "Auto-generated for other formats. For heatmap: directory name (default: `heatmap_PID`)", + ) + output_group.add_argument( + "--browser", + action="store_true", + help="Automatically open HTML output (flamegraph, heatmap) in browser. " + "When using `--subprocesses`, only the main process opens the browser", + ) + + +def _add_pstats_options(parser): + """Add pstats-specific display options to a parser.""" + pstats_group = parser.add_argument_group("pstats format options") + pstats_group.add_argument( + "--sort", + choices=[ + "nsamples", + "tottime", + "cumtime", + "sample-pct", + "cumul-pct", + "nsamples-cumul", + "name", + ], + default=None, + help="Sort order for pstats output (default: nsamples)", + ) + pstats_group.add_argument( + "-l", + "--limit", + type=int, + default=None, + help="Limit the number of rows in the output (default: 15)", + ) + pstats_group.add_argument( + "--no-summary", + action="store_true", + help="Disable the summary section in the pstats output", + ) + + +def _add_dump_options(parser): + """Add one-shot stack dump options to a parser.""" + dump_group = parser.add_argument_group("Dump options") + dump_group.add_argument( + "-a", + "--all-threads", + action="store_true", + help="Dump all threads in the process instead of just the main thread", + ) + dump_group.add_argument( + "--native", + action="store_true", + help='Include artificial `` frames to denote calls to non-Python code', + ) + dump_group.add_argument( + "--no-gc", + action="store_false", + dest="gc", + help='Don\'t include artificial `` frames to denote active garbage collection', + ) + dump_group.add_argument( + "--opcodes", + action="store_true", + help="Show bytecode opcode names when available.", + ) + dump_group.add_argument( + "--async-aware", + action="store_true", + help="Enable async-aware stack reconstruction", + ) + dump_group.add_argument( + "--async-mode", + choices=["running", "all"], + default=argparse.SUPPRESS, + help='Async stack mode: "running" (only running task) ' + 'or "all" (all tasks including waiting, default for dump). ' + "Requires `--async-aware`", + ) + dump_group.add_argument( + "--blocking", + action="store_true", + help="Stop all threads in target process before dumping the stack.", + ) + + +def _sort_to_mode(sort_choice): + """Convert sort choice string to SORT_MODE constant.""" + sort_map = { + "nsamples": SORT_MODE_NSAMPLES, + "tottime": SORT_MODE_TOTTIME, + "cumtime": SORT_MODE_CUMTIME, + "sample-pct": SORT_MODE_SAMPLE_PCT, + "cumul-pct": SORT_MODE_CUMUL_PCT, + "nsamples-cumul": SORT_MODE_NSAMPLES_CUMUL, + "name": -1, + } + return sort_map.get(sort_choice, SORT_MODE_NSAMPLES) + +def _create_collector(format_type, sample_interval_usec, skip_idle, opcodes=False, + mode=None, output_file=None, compression='auto', + diff_baseline=None): + """Create the appropriate collector based on format type. + + Args: + format_type: The output format ('pstats', 'collapsed', 'flamegraph', + 'gecko', 'heatmap', 'jsonl', 'binary', 'diff_flamegraph') + sample_interval_usec: Sampling interval in microseconds + skip_idle: Whether to skip idle samples + opcodes: Whether to collect opcode information (only used by gecko format + for creating interval markers in Firefox Profiler) + mode: Profiling mode for collectors that expose it in metadata + output_file: Output file path (required for binary format) + compression: Compression type for binary format ('auto', 'zstd', 'none') + diff_baseline: Path to baseline binary file for differential flamegraph + + Returns: + A collector instance of the appropriate type + """ + collector_class = COLLECTOR_MAP.get(format_type) + if collector_class is None: + raise ValueError(f"Unknown format: {format_type}") + + if format_type == "diff_flamegraph": + if diff_baseline is None: + raise ValueError("Differential flamegraph requires a baseline file") + if not os.path.exists(diff_baseline): + raise ValueError(f"Baseline file not found: {diff_baseline}") + return collector_class( + sample_interval_usec, + baseline_binary_path=diff_baseline, + skip_idle=skip_idle + ) + + # Binary format requires output file and compression + if format_type == "binary": + if output_file is None: + raise ValueError("Binary format requires an output file") + return collector_class(output_file, sample_interval_usec, skip_idle=skip_idle, + compression=compression) + + # Gecko format never skips idle (it needs both GIL and CPU data) + # and is the only format that uses opcodes for interval markers + if format_type == "gecko": + skip_idle = False + return collector_class(sample_interval_usec, skip_idle=skip_idle, opcodes=opcodes) + + if format_type == "jsonl": + return collector_class( + sample_interval_usec, skip_idle=skip_idle, mode=mode + ) + + return collector_class(sample_interval_usec, skip_idle=skip_idle) + + +def _generate_output_filename(format_type, pid): + """Generate output filename based on format and PID. + + Args: + format_type: The output format + pid: Process ID + + Returns: + Generated filename + """ + extension = FORMAT_EXTENSIONS.get(format_type, "txt") + # For heatmap, use cleaner directory name without extension + if format_type == "heatmap": + return f"heatmap_{pid}" + return f"{format_type}_{pid}.{extension}" + + +def _open_in_browser(path): + """Open a file or directory in the default web browser. + + Args: + path: File path or directory path to open + + For directories (heatmap), opens the index.html file inside. + """ + abs_path = os.path.abspath(path) + + # For heatmap directories, open the index.html file + if os.path.isdir(abs_path): + index_path = os.path.join(abs_path, 'index.html') + if os.path.exists(index_path): + abs_path = index_path + else: + print(f"Warning: Could not find index.html in {path}", file=sys.stderr) + return + + file_url = f"file://{abs_path}" + try: + webbrowser.open(file_url) + except Exception as e: + print(f"Warning: Could not open browser: {e}", file=sys.stderr) + + +def _validate_replay_input_file(filename): + """Validate that the replay input looks like a sampling binary profile.""" + try: + with open(filename, "rb") as file: + header = file.read(_BINARY_PROFILE_HEADER_SIZE) + except OSError as exc: + sys.exit(f"Error: Could not read input file {filename}: {exc}") + + if ( + len(header) < _BINARY_PROFILE_HEADER_SIZE + or header[:4] not in _BINARY_PROFILE_MAGICS + ): + sys.exit( + "Error: Input file is not a binary sampling profile. " + "The replay command only accepts files created with --binary" + ) + + +def _replay_with_reader(args, reader): + """Replay samples from an open binary reader.""" + info = reader.get_info() + interval = info['sample_interval_us'] + + print(f"Replaying {info['sample_count']} samples from {args.input_file}") + print(f" Sample interval: {interval} us") + print( + " Compression: " + f"{'zstd' if info.get('compression_type', 0) == 1 else 'none'}" + ) + + collector = _create_collector( + args.format, interval, skip_idle=False, + diff_baseline=args.diff_baseline + ) + + def progress_callback(current, total): + if total > 0: + pct = current / total + bar_width = 40 + filled = int(bar_width * pct) + bar = '█' * filled + '░' * (bar_width - filled) + print( + f"\r [{bar}] {pct*100:5.1f}% ({current:,}/{total:,})", + end="", + flush=True, + ) + + count = reader.replay_samples(collector, progress_callback) + print() + + if args.format == "pstats": + if args.outfile: + collector.export(args.outfile) + else: + sort_choice = ( + args.sort if args.sort is not None else "nsamples" + ) + limit = args.limit if args.limit is not None else 15 + sort_mode = _sort_to_mode(sort_choice) + collector.print_stats( + sort_mode, limit, not args.no_summary, + PROFILING_MODE_WALL + ) + else: + filename = ( + args.outfile + or _generate_output_filename(args.format, os.getpid()) + ) + collector.export(filename) + + # Auto-open browser for HTML output if --browser flag is set + if ( + args.format in ( + 'flamegraph', 'diff_flamegraph', 'heatmap' + ) + and getattr(args, 'browser', False) + ): + _open_in_browser(filename) + + print(f"Replayed {count} samples") + + +def _handle_output(collector, args, pid, mode): + """Handle output for the collector based on format and arguments. + + Args: + collector: The collector instance with profiling data + args: Parsed command-line arguments + pid: Process ID (for generating filenames) + mode: Profiling mode used + """ + if args.format == "binary": + # Binary format already wrote to file incrementally, just finalize + collector.export(None) + filename = collector.filename + print(f"Binary profile written to {filename} ({collector.total_samples} samples)") + elif args.format == "pstats": + if args.outfile: + # If outfile is a directory, generate filename inside it + if os.path.isdir(args.outfile): + filename = os.path.join(args.outfile, _generate_output_filename(args.format, pid)) + collector.export(filename) + else: + collector.export(args.outfile) + else: + # Print to stdout with defaults applied + sort_choice = args.sort if args.sort is not None else "nsamples" + limit = args.limit if args.limit is not None else 15 + sort_mode = _sort_to_mode(sort_choice) + collector.print_stats( + sort_mode, limit, not args.no_summary, mode + ) + else: + # Export to file + if args.outfile and os.path.isdir(args.outfile): + # If outfile is a directory, generate filename inside it + filename = os.path.join(args.outfile, _generate_output_filename(args.format, pid)) + else: + filename = args.outfile or _generate_output_filename(args.format, pid) + collector.export(filename) + + # Auto-open browser for HTML output if --browser flag is set + if args.format in ('flamegraph', 'diff_flamegraph', 'heatmap') and getattr(args, 'browser', False): + _open_in_browser(filename) + + +def _validate_args(args, parser): + """Validate format-specific options and live mode requirements. + + Args: + args: Parsed command-line arguments + parser: ArgumentParser instance for error reporting + """ + command = getattr(args, 'command', None) + + if command == "replay": + return + + # Check if live mode is available + if hasattr(args, 'live') and args.live and LiveStatsCollector is None: + parser.error( + "Live mode requires the curses module, which is not available." + ) + + # --subprocesses is incompatible with --live + if hasattr(args, 'subprocesses') and args.subprocesses: + if ChildProcessMonitor is None: + parser.error( + "--subprocesses is not available on this platform " + "(requires _remote_debugging module)." + ) + if hasattr(args, 'live') and args.live: + parser.error("--subprocesses is incompatible with --live mode.") + + # Async-aware mode is incompatible with --native, --no-gc, --mode, and --all-threads + if getattr(args, 'async_aware', False): + issues = [] + if getattr(args, 'native', False): + issues.append("--native") + if not getattr(args, 'gc', True): + issues.append("--no-gc") + if hasattr(args, 'mode') and args.mode != "wall": + issues.append(f"--mode={args.mode}") + if hasattr(args, 'all_threads') and args.all_threads: + issues.append("--all-threads") + if issues: + parser.error( + f"Options {', '.join(issues)} are incompatible with --async-aware. " + "Async-aware profiling uses task-based stack reconstruction." + ) + + # --async-mode requires --async-aware when explicitly set + if not getattr(args, 'async_aware', False): + if command == "dump": + # dump uses SUPPRESS default, so attr only exists if user passed it + if hasattr(args, 'async_mode'): + parser.error("--async-mode requires --async-aware to be enabled.") + elif hasattr(args, 'async_mode') and args.async_mode != "running": + parser.error("--async-mode requires --async-aware to be enabled.") + + if command == "dump": + return + + # Warn about blocking mode with aggressive sampling intervals + if args.blocking and args.sample_interval_usec < 100: + print( + f"Warning: --blocking with a {args.sample_interval_usec} µs interval will stop all threads " + f"{1_000_000 // args.sample_interval_usec} times per second. " + "Consider using --sampling-rate 1khz or lower to reduce overhead.", + file=sys.stderr + ) + + # Live mode is incompatible with format options + if hasattr(args, 'live') and args.live: + if args.format != "pstats": + format_flag = f"--{args.format}" + parser.error( + f"--live is incompatible with {format_flag}. Live mode uses a TUI interface." + ) + + # Live mode is also incompatible with pstats-specific options + issues = [] + if args.sort is not None: + issues.append("--sort") + if args.limit is not None: + issues.append("--limit") + if args.no_summary: + issues.append("--no-summary") + + if issues: + parser.error( + f"Options {', '.join(issues)} are incompatible with --live. " + "Live mode uses a TUI interface with its own controls." + ) + return + + # Validate gecko mode doesn't use non-wall mode + if args.format == "gecko" and getattr(args, 'mode', 'wall') != "wall": + parser.error( + "--mode option is incompatible with --gecko. " + "Gecko format automatically includes both GIL-holding and CPU status analysis." + ) + + # Validate --opcodes is only used with compatible formats + opcodes_compatible_formats = ("live", "gecko", "flamegraph", "diff_flamegraph", "heatmap", "binary") + if getattr(args, 'opcodes', False) and args.format not in opcodes_compatible_formats: + parser.error( + f"--opcodes is only compatible with {', '.join('--' + f for f in opcodes_compatible_formats)}." + ) + + # Validate pstats-specific options are only used with pstats format + if args.format != "pstats": + issues = [] + if args.sort is not None: + issues.append("--sort") + if args.limit is not None: + issues.append("--limit") + if args.no_summary: + issues.append("--no-summary") + + if issues: + format_flag = f"--{args.format}" + parser.error( + f"Options {', '.join(issues)} are only valid with --pstats, not {format_flag}" + ) + + +def main(): + """Main entry point for the CLI.""" + # Set locale for number formatting, restore on exit + old_locale = locale.setlocale(locale.LC_ALL, None) + locale.setlocale(locale.LC_ALL, "") + try: + _main() + finally: + locale.setlocale(locale.LC_ALL, old_locale) + + +def _main(): + # Create the main parser + parser = argparse.ArgumentParser( + description=_HELP_DESCRIPTION, + formatter_class=CustomFormatter, + ) + + # Create subparsers for commands + subparsers = parser.add_subparsers( + dest="command", required=True, help="Command to run" + ) + + # === RUN COMMAND === + run_parser = subparsers.add_parser( + "run", + help="Run and profile a script or module", + formatter_class=CustomFormatter, + description="""Run and profile a Python script or module + +Examples: + # Run and profile a module + `python -m profiling.sampling run -m mymodule arg1 arg2` + + # Generate flamegraph from a script + `python -m profiling.sampling run --flamegraph -o output.html script.py` + + # Profile with custom rate and duration + `python -m profiling.sampling run -r 5khz -d 30 script.py` + + # Save collapsed stacks to file + `python -m profiling.sampling run --collapsed -o stacks.txt script.py` + + # Live interactive mode for a script + `python -m profiling.sampling run --live script.py`""", + ) + run_parser.add_argument( + "-m", + "--module", + action="store_true", + help="Run target as a module (like `python -m`)", + ) + run_parser.add_argument( + "target", + help="Script file or module name to profile", + ) + run_parser.add_argument( + "args", + nargs=argparse.REMAINDER, + help="Arguments to pass to the script or module", + ) + run_parser.add_argument( + "--live", + action="store_true", + help="Interactive TUI profiler (top-like interface, press 'q' to quit, 's' to cycle sort)", + ) + _add_sampling_options(run_parser) + _add_mode_options(run_parser) + _add_format_options(run_parser) + _add_pstats_options(run_parser) + + # === ATTACH COMMAND === + attach_parser = subparsers.add_parser( + "attach", + help="Attach to and profile a running process", + formatter_class=CustomFormatter, + description="""Attach to a running process and profile it + +Examples: + # Profile all threads, sort by total time + `python -m profiling.sampling attach -a --sort tottime 1234` + + # Live interactive mode for a running process + `python -m profiling.sampling attach --live 1234`""", + ) + attach_parser.add_argument( + "pid", + type=int, + help="Process ID to attach to", + ) + attach_parser.add_argument( + "--live", + action="store_true", + help="Interactive TUI profiler (top-like interface, press 'q' to quit, 's' to cycle sort)", + ) + _add_sampling_options(attach_parser) + _add_mode_options(attach_parser) + _add_format_options(attach_parser) + _add_pstats_options(attach_parser) + + # === DUMP COMMAND === + dump_parser = subparsers.add_parser( + "dump", + help="Dump a running process's current stack", + formatter_class=CustomFormatter, + description="""Dump a running process's current Python stack + +Examples: + # Dump the main thread stack + `python -m profiling.sampling dump 1234` + + # Dump all thread stacks + `python -m profiling.sampling dump -a 1234`""", + ) + dump_parser.add_argument( + "pid", + type=int, + help="Process ID to dump", + ) + _add_dump_options(dump_parser) + + # === REPLAY COMMAND === + replay_parser = subparsers.add_parser( + "replay", + help="Replay a binary profile and convert to another format", + formatter_class=CustomFormatter, + description="""Replay a binary profile file and convert to another format + +Examples: + # Convert binary to flamegraph + `python -m profiling.sampling replay --flamegraph -o output.html profile.bin` + + # Convert binary to pstats and print to stdout + `python -m profiling.sampling replay profile.bin` + + # Convert binary to gecko format + `python -m profiling.sampling replay --gecko -o profile.json profile.bin`""", + ) + replay_parser.add_argument( + "input_file", + help="Binary profile file to replay", + ) + _add_format_options(replay_parser, include_compression=False, include_binary=False) + _add_pstats_options(replay_parser) + + # Parse arguments + args = parser.parse_args() + + # Validate arguments + _validate_args(args, parser) + + # Command dispatch table + command_handlers = { + "run": _handle_run, + "attach": _handle_attach, + "dump": _handle_dump, + "replay": _handle_replay, + } + + # Execute the appropriate command + handler = command_handlers.get(args.command) + if handler: + handler(args) + else: + parser.error(f"Unknown command: {args.command}") + + +def _handle_attach(args): + """Handle the 'attach' command.""" + if not _is_process_running(args.pid): + raise SamplingUnknownProcessError(args.pid) + # Check if live mode is requested + if args.live: + _handle_live_attach(args, args.pid) + return + + # Use PROFILING_MODE_ALL for gecko format + mode = ( + PROFILING_MODE_ALL + if args.format == "gecko" + else _parse_mode(args.mode) + ) + + # Determine skip_idle based on mode + skip_idle = ( + mode != PROFILING_MODE_WALL if mode != PROFILING_MODE_ALL else False + ) + + output_file = None + if args.format == "binary": + output_file = args.outfile or _generate_output_filename(args.format, args.pid) + + # Create the appropriate collector + collector = _create_collector( + args.format, args.sample_interval_usec, skip_idle, args.opcodes, mode, + output_file=output_file, + compression=getattr(args, 'compression', 'auto'), + diff_baseline=args.diff_baseline + ) + + with _get_child_monitor_context(args, args.pid): + collector = sample( + args.pid, + collector, + duration_sec=args.duration, + all_threads=args.all_threads, + realtime_stats=args.realtime_stats, + mode=mode, + async_aware=args.async_mode if args.async_aware else None, + native=args.native, + gc=args.gc, + opcodes=args.opcodes, + blocking=args.blocking, + ) + _handle_output(collector, args, args.pid, mode) + + +def _handle_dump(args): + if not _is_process_running(args.pid): + raise SamplingUnknownProcessError(args.pid) + + # Async-aware reconstruction requires wall mode so every thread is sampled, + # not just those holding the GIL or on CPU. + mode = PROFILING_MODE_WALL if args.async_aware else PROFILING_MODE_ALL + async_mode = getattr(args, "async_mode", "all") if args.async_aware else None + try: + stack_frames = dump_stack( + args.pid, + all_threads=args.all_threads, + mode=mode, + async_aware=async_mode, + native=args.native, + gc=args.gc, + opcodes=args.opcodes, + blocking=args.blocking, + ) + except ProcessLookupError: + sys.exit( + f"No stack dump collected - process {args.pid} exited before " + "its stack could be read." + ) + + print_stack_dump(stack_frames, pid=args.pid) + + +def _handle_run(args): + """Handle the 'run' command.""" + # Validate target exists before launching subprocess + if args.module: + # Temporarily add cwd to sys.path so we can find modules in the + # current directory, matching the coordinator's behavior + cwd = os.getcwd() + added_cwd = False + if cwd not in sys.path: + sys.path.insert(0, cwd) + added_cwd = True + try: + if importlib.util.find_spec(args.target) is None: + raise SamplingModuleNotFoundError(args.target) + finally: + if added_cwd: + sys.path.remove(cwd) + else: + if not os.path.exists(args.target): + raise SamplingScriptNotFoundError(args.target) + + # Check if live mode is requested + if args.live: + _handle_live_run(args) + return + + # Build the command to run + if args.module: + cmd = (sys.executable, "-m", args.target, *args.args) + else: + cmd = (sys.executable, args.target, *args.args) + + # Run with synchronization + try: + process = _run_with_sync(cmd, suppress_output=False) + except RuntimeError as e: + sys.exit(f"Error: {e}") + + # Use PROFILING_MODE_ALL for gecko format + mode = ( + PROFILING_MODE_ALL + if args.format == "gecko" + else _parse_mode(args.mode) + ) + + # Determine skip_idle based on mode + skip_idle = ( + mode != PROFILING_MODE_WALL if mode != PROFILING_MODE_ALL else False + ) + + output_file = None + if args.format == "binary": + output_file = args.outfile or _generate_output_filename(args.format, process.pid) + + # Create the appropriate collector + collector = _create_collector( + args.format, args.sample_interval_usec, skip_idle, args.opcodes, mode, + output_file=output_file, + compression=getattr(args, 'compression', 'auto'), + diff_baseline=args.diff_baseline + ) + + with _get_child_monitor_context(args, process.pid): + try: + collector = sample( + process.pid, + collector, + duration_sec=args.duration, + all_threads=args.all_threads, + realtime_stats=args.realtime_stats, + mode=mode, + async_aware=args.async_mode if args.async_aware else None, + native=args.native, + gc=args.gc, + opcodes=args.opcodes, + blocking=args.blocking, + ) + _handle_output(collector, args, process.pid, mode) + finally: + # Terminate the main subprocess - child profilers finish when their + # target processes exit + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=_PROCESS_KILL_TIMEOUT_SEC) + except subprocess.TimeoutExpired: + process.kill() + process.wait() + + +def _handle_live_attach(args, pid): + """Handle live mode for an existing process.""" + mode = _parse_mode(args.mode) + + # Determine skip_idle based on mode + skip_idle = mode != PROFILING_MODE_WALL + + # Create live collector with default settings + collector = LiveStatsCollector( + args.sample_interval_usec, + skip_idle=skip_idle, + sort_by="tottime", # Default initial sort + limit=20, # Default limit + pid=pid, + mode=mode, + opcodes=args.opcodes, + async_aware=args.async_mode if args.async_aware else None, + ) + + # Sample in live mode + sample_live( + pid, + collector, + duration_sec=args.duration, + all_threads=args.all_threads, + realtime_stats=args.realtime_stats, + mode=mode, + async_aware=args.async_mode if args.async_aware else None, + native=args.native, + gc=args.gc, + opcodes=args.opcodes, + blocking=args.blocking, + ) + + +def _handle_live_run(args): + """Handle live mode for running a script/module.""" + # Build the command to run + if args.module: + cmd = (sys.executable, "-m", args.target, *args.args) + else: + cmd = (sys.executable, args.target, *args.args) + + # Run with synchronization, suppressing output for live mode + try: + process = _run_with_sync(cmd, suppress_output=True) + except RuntimeError as e: + sys.exit(f"Error: {e}") + + mode = _parse_mode(args.mode) + + # Determine skip_idle based on mode + skip_idle = mode != PROFILING_MODE_WALL + + # Create live collector with default settings + collector = LiveStatsCollector( + args.sample_interval_usec, + skip_idle=skip_idle, + sort_by="tottime", # Default initial sort + limit=20, # Default limit + pid=process.pid, + mode=mode, + opcodes=args.opcodes, + async_aware=args.async_mode if args.async_aware else None, + ) + + # Profile the subprocess in live mode + try: + sample_live( + process.pid, + collector, + duration_sec=args.duration, + all_threads=args.all_threads, + realtime_stats=args.realtime_stats, + mode=mode, + async_aware=args.async_mode if args.async_aware else None, + native=args.native, + gc=args.gc, + opcodes=args.opcodes, + blocking=args.blocking, + ) + finally: + # Clean up the subprocess and get any error output + returncode = process.poll() + if returncode is None: + # Process still running - terminate it + process.terminate() + try: + process.wait(timeout=_PROCESS_KILL_TIMEOUT_SEC) + except subprocess.TimeoutExpired: + process.kill() + # Ensure process is fully terminated + process.wait() + # Read any stderr output (tracebacks, errors, etc.) + if process.stderr: + with process.stderr: + try: + stderr = process.stderr.read() + if stderr: + print(stderr.decode(), file=sys.stderr) + except (OSError, ValueError): + # Ignore errors if pipe is already closed + pass + + +def _handle_replay(args): + """Handle the 'replay' command - convert binary profile to another format.""" + if not os.path.exists(args.input_file): + sys.exit(f"Error: Input file not found: {args.input_file}") + + _validate_replay_input_file(args.input_file) + + try: + with BinaryReader(args.input_file) as reader: + _replay_with_reader(args, reader) + except (OSError, ValueError) as exc: + sys.exit(f"Error: {exc}") + + +if __name__ == "__main__": + main() diff --git a/Lib/profiling/sampling/collector.py b/Lib/profiling/sampling/collector.py new file mode 100644 index 00000000000000..8e0f0c44c4f8f3 --- /dev/null +++ b/Lib/profiling/sampling/collector.py @@ -0,0 +1,288 @@ +from abc import ABC, abstractmethod +from .constants import ( + DEFAULT_LOCATION, + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, + THREAD_STATUS_GIL_REQUESTED, + THREAD_STATUS_UNKNOWN, + THREAD_STATUS_HAS_EXCEPTION, + _INTERNAL_FRAME_SUFFIXES, +) + +try: + from _remote_debugging import FrameInfo +except ImportError: + # Fallback definition if _remote_debugging is not available + FrameInfo = None + + +def normalize_location(location): + """Normalize location to a 4-tuple format. + + Args: + location: tuple (lineno, end_lineno, col_offset, end_col_offset), + an integer line number, or None + + Returns: + tuple: (lineno, end_lineno, col_offset, end_col_offset) + """ + if location is None: + return DEFAULT_LOCATION + if isinstance(location, int): + return (location, location, -1, -1) + return location + + +def extract_lineno(location): + """Extract lineno from location. + + Args: + location: tuple (lineno, end_lineno, col_offset, end_col_offset), + an integer line number, or None + + Returns: + int: The line number (0 for synthetic frames) + """ + if location is None: + return 0 + if isinstance(location, int): + return location + return location[0] + +def _is_internal_frame(frame): + if isinstance(frame, tuple): + filename = frame[0] if frame else "" + else: + filename = getattr(frame, "filename", "") + + if not filename: + return False + + return filename.endswith(_INTERNAL_FRAME_SUFFIXES) + + +def filter_internal_frames(frames): + if not frames: + return frames + + return [f for f in frames if not _is_internal_frame(f)] + + +def _build_task_graph(awaited_info_list): + task_map = {} + child_to_parent = {} # child_id -> (selected_parent_id, parent_count) + all_task_ids = set() + all_parent_ids = set() + + for awaited_info in awaited_info_list: + thread_id = awaited_info.thread_id + for task_info in awaited_info.awaited_by: + task_id = task_info.task_id + task_map[task_id] = (task_info, thread_id) + all_task_ids.add(task_id) + + if task_info.awaited_by: + parent_ids = [p.task_name for p in task_info.awaited_by] + parent_count = len(parent_ids) + all_parent_ids.update(parent_ids) + selected_parent = min(parent_ids) if parent_count > 1 else parent_ids[0] + child_to_parent[task_id] = (selected_parent, parent_count) + + return task_map, child_to_parent, all_task_ids, all_parent_ids + + +def _find_leaf_tasks(all_task_ids, all_parent_ids): + return all_task_ids - all_parent_ids + + +def _build_linear_stacks(leaf_task_ids, task_map, child_to_parent): + for leaf_id in leaf_task_ids: + frames = [] + visited = set() + current_id = leaf_id + thread_id = None + + while current_id is not None: + if current_id in visited: + break + visited.add(current_id) + + if current_id not in task_map: + break + + task_info, tid = task_map[current_id] + + if thread_id is None: + thread_id = tid + + if task_info.coroutine_stack: + for coro_info in task_info.coroutine_stack: + for frame in coro_info.call_stack: + frames.append(frame) + + parent_info = child_to_parent.get(current_id) + task_name = task_info.task_name or "Task-" + str(task_info.task_id) + if parent_info: + selected_parent, parent_count = parent_info + if parent_count > 1: + task_name = f"{task_name} ({parent_count} parents)" + frames.append(FrameInfo(("", None, task_name, None))) + current_id = selected_parent + else: + frames.append(FrameInfo(("", None, task_name, None))) + current_id = None + + if frames and thread_id is not None: + yield frames, thread_id, leaf_id + + +def iter_async_frames(awaited_info_list): + task_map, child_to_parent, all_task_ids, all_parent_ids = _build_task_graph(awaited_info_list) + leaf_task_ids = _find_leaf_tasks(all_task_ids, all_parent_ids) + yield from _build_linear_stacks(leaf_task_ids, task_map, child_to_parent) + + +class Collector(ABC): + aggregating = False + + @abstractmethod + def collect(self, stack_frames, timestamps_us=None): + """Collect profiling data from stack frames. + + Args: + stack_frames: List of InterpreterInfo objects + timestamps_us: Optional list of timestamps in microseconds. If provided + (from binary replay with RLE batching), use these instead of current + time. If None, collectors should use time.monotonic() or similar. + The list may contain multiple timestamps when samples are batched + together (same stack, different times). + """ + + def collect_failed_sample(self): + """Collect data about a failed sample attempt.""" + + @abstractmethod + def export(self, filename): + """Export collected data to a file.""" + + @staticmethod + def _filter_internal_frames(frames): + return filter_internal_frames(frames) + + def _iter_all_frames(self, stack_frames, skip_idle=False): + for interpreter_info in stack_frames: + for thread_info in interpreter_info.threads: + # skip_idle now means: skip if thread is not actively running + # A thread is "active" if it has the GIL OR is on CPU + if skip_idle: + status_flags = thread_info.status + has_gil = bool(status_flags & THREAD_STATUS_HAS_GIL) + on_cpu = bool(status_flags & THREAD_STATUS_ON_CPU) + if not (has_gil or on_cpu): + continue + frames = thread_info.frame_info + if frames: + # Filter out internal profiler frames from the bottom of the stack + frames = self._filter_internal_frames(frames) + if frames: + yield frames, thread_info.thread_id + + def _iter_async_frames(self, awaited_info_list): + yield from iter_async_frames(awaited_info_list) + + def _iter_stacks(self, stack_frames, skip_idle=False): + """Yield (frames, thread_id) for all stacks, handling both sync and async modes.""" + if stack_frames and hasattr(stack_frames[0], "awaited_by"): + for frames, thread_id, _ in iter_async_frames(stack_frames): + if frames: + yield frames, thread_id + else: + for frames, thread_id in self._iter_all_frames(stack_frames, skip_idle=skip_idle): + if frames: + yield frames, thread_id + + def _is_gc_frame(self, frame): + if isinstance(frame, tuple): + funcname = frame[2] if len(frame) >= 3 else "" + else: + funcname = getattr(frame, "funcname", "") + + return "" in funcname or "gc_collect" in funcname + + def _collect_thread_status_stats(self, stack_frames): + """Collect aggregate and per-thread status statistics from a sample. + + Returns: + tuple: (aggregate_status_counts, has_gc_frame, per_thread_stats) + - aggregate_status_counts: dict with has_gil, on_cpu, has_exception, etc. + - has_gc_frame: bool indicating if any thread has GC frames + - per_thread_stats: dict mapping thread_id to per-thread counts + """ + status_counts = { + "has_gil": 0, + "on_cpu": 0, + "gil_requested": 0, + "unknown": 0, + "has_exception": 0, + "total": 0, + } + has_gc_frame = False + per_thread_stats = {} + + for interpreter_info in stack_frames: + threads = getattr(interpreter_info, "threads", []) + for thread_info in threads: + status_counts["total"] += 1 + + # Track thread status using bit flags + status_flags = getattr(thread_info, "status", 0) + + if status_flags & THREAD_STATUS_HAS_GIL: + status_counts["has_gil"] += 1 + if status_flags & THREAD_STATUS_ON_CPU: + status_counts["on_cpu"] += 1 + if status_flags & THREAD_STATUS_GIL_REQUESTED: + status_counts["gil_requested"] += 1 + if status_flags & THREAD_STATUS_UNKNOWN: + status_counts["unknown"] += 1 + if status_flags & THREAD_STATUS_HAS_EXCEPTION: + status_counts["has_exception"] += 1 + + # Track per-thread statistics + thread_id = getattr(thread_info, "thread_id", None) + if thread_id is not None: + if thread_id not in per_thread_stats: + per_thread_stats[thread_id] = { + "has_gil": 0, + "on_cpu": 0, + "gil_requested": 0, + "unknown": 0, + "has_exception": 0, + "total": 0, + "gc_samples": 0, + } + + thread_stats = per_thread_stats[thread_id] + thread_stats["total"] += 1 + + if status_flags & THREAD_STATUS_HAS_GIL: + thread_stats["has_gil"] += 1 + if status_flags & THREAD_STATUS_ON_CPU: + thread_stats["on_cpu"] += 1 + if status_flags & THREAD_STATUS_GIL_REQUESTED: + thread_stats["gil_requested"] += 1 + if status_flags & THREAD_STATUS_UNKNOWN: + thread_stats["unknown"] += 1 + if status_flags & THREAD_STATUS_HAS_EXCEPTION: + thread_stats["has_exception"] += 1 + + # Check for GC frames in this thread + frames = getattr(thread_info, "frame_info", None) + if frames: + for frame in frames: + if self._is_gc_frame(frame): + thread_stats["gc_samples"] += 1 + has_gc_frame = True + break + + return status_counts, has_gc_frame, per_thread_stats diff --git a/Lib/profiling/sampling/constants.py b/Lib/profiling/sampling/constants.py new file mode 100644 index 00000000000000..d7c710f943b1b7 --- /dev/null +++ b/Lib/profiling/sampling/constants.py @@ -0,0 +1,57 @@ +"""Constants for the sampling profiler.""" + +# Time unit conversion constants +MICROSECONDS_PER_SECOND = 1_000_000 +MILLISECONDS_PER_SECOND = 1_000 + +# Profiling mode constants +PROFILING_MODE_WALL = 0 +PROFILING_MODE_CPU = 1 +PROFILING_MODE_GIL = 2 +PROFILING_MODE_ALL = 3 # Combines GIL + CPU checks +PROFILING_MODE_EXCEPTION = 4 # Only samples when thread has an active exception + +PROFILING_MODE_NAMES = { + PROFILING_MODE_WALL: "wall", + PROFILING_MODE_CPU: "cpu", + PROFILING_MODE_GIL: "gil", + PROFILING_MODE_ALL: "all", + PROFILING_MODE_EXCEPTION: "exception", +} + +# Sort mode constants +SORT_MODE_NSAMPLES = 0 +SORT_MODE_TOTTIME = 1 +SORT_MODE_CUMTIME = 2 +SORT_MODE_SAMPLE_PCT = 3 +SORT_MODE_CUMUL_PCT = 4 +SORT_MODE_NSAMPLES_CUMUL = 5 + +# Default location for synthetic frames (native, GC) that have no source location +# Format: (lineno, end_lineno, col_offset, end_col_offset) +DEFAULT_LOCATION = (0, 0, -1, -1) + +# Internal frame path suffixes to filter from profiling output +# These are internal profiler modules that should not appear in user-facing output +_INTERNAL_FRAME_SUFFIXES = ( + "_sync_coordinator.py", +) + +# Thread status flags +try: + from _remote_debugging import ( + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, + THREAD_STATUS_UNKNOWN, + THREAD_STATUS_GIL_REQUESTED, + THREAD_STATUS_HAS_EXCEPTION, + THREAD_STATUS_MAIN_THREAD, + ) +except ImportError: + # Fallback for tests or when module is not available + THREAD_STATUS_HAS_GIL = (1 << 0) + THREAD_STATUS_ON_CPU = (1 << 1) + THREAD_STATUS_UNKNOWN = (1 << 2) + THREAD_STATUS_GIL_REQUESTED = (1 << 3) + THREAD_STATUS_HAS_EXCEPTION = (1 << 4) + THREAD_STATUS_MAIN_THREAD = (1 << 5) diff --git a/Lib/profiling/sampling/dump.py b/Lib/profiling/sampling/dump.py new file mode 100644 index 00000000000000..7cf43bd29ebffb --- /dev/null +++ b/Lib/profiling/sampling/dump.py @@ -0,0 +1,295 @@ +"""Pretty printing for one-shot sampling stack dumps.""" + +import contextlib +import linecache +import os +import sys +from traceback import _byte_offset_to_character_offset + +import _colorize + +from .collector import extract_lineno, filter_internal_frames, iter_async_frames +from .constants import ( + THREAD_STATUS_GIL_REQUESTED, + THREAD_STATUS_HAS_EXCEPTION, + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_MAIN_THREAD, + THREAD_STATUS_ON_CPU, + THREAD_STATUS_UNKNOWN, +) +from .opcode_utils import format_opcode + + +_STATUS_LABELS = ( + (THREAD_STATUS_MAIN_THREAD, "main thread"), + (THREAD_STATUS_HAS_GIL, "has GIL"), + (THREAD_STATUS_ON_CPU, "on CPU"), + (THREAD_STATUS_GIL_REQUESTED, "waiting for GIL"), + (THREAD_STATUS_HAS_EXCEPTION, "has exception"), +) + + +def _theme_for(file, colorize): + if colorize is True: + return _colorize.get_theme(force_color=True).profiler_dump + if colorize is False: + return _colorize.get_theme(force_no_color=True).profiler_dump + return _colorize.get_theme(tty_file=file).profiler_dump + + +def _color(text, color, theme): + if not color: + return text + return f"{color}{text}{theme.reset}" + + +def _frame_fields(frame): + if isinstance(frame, tuple): + filename = frame[0] if len(frame) > 0 else "" + location = frame[1] if len(frame) > 1 else None + qualname = frame[2] if len(frame) > 2 else "" + opcode = frame[3] if len(frame) > 3 else None + else: + filename = getattr(frame, "filename", "") + location = getattr(frame, "location", None) + qualname = getattr(frame, "qualname", None) + if qualname is None: + qualname = getattr(frame, "funcname", "") + opcode = getattr(frame, "opcode", None) + return filename, location, qualname, opcode + + +def _location_field(location, index, default=None): + if location is None: + return default + try: + value = location[index] + except (IndexError, TypeError): + return default + return default if value is None else value + + +def _status_text(status): + labels = [label for flag, label in _STATUS_LABELS if status & flag] + has_state = status & ( + THREAD_STATUS_HAS_GIL + | THREAD_STATUS_GIL_REQUESTED + | THREAD_STATUS_HAS_EXCEPTION + ) + if not has_state and not status & (THREAD_STATUS_UNKNOWN | THREAD_STATUS_ON_CPU): + labels.append("idle") + return ", ".join(labels) if labels else None + + +def _is_async_dump(stack_frames): + return bool(stack_frames) and hasattr(stack_frames[0], "awaited_by") + + +def _iter_dump_sections(stack_frames): + if not stack_frames: + return + + if _is_async_dump(stack_frames): + for frames, thread_id, leaf_id in iter_async_frames(stack_frames): + frames = filter_internal_frames(frames) + if frames: + yield None, thread_id, None, frames, f"task {leaf_id}" + return + + for interpreter_info in stack_frames: + interpreter_id = getattr(interpreter_info, "interpreter_id", None) + for thread_info in getattr(interpreter_info, "threads", ()): + frames = getattr(thread_info, "frame_info", None) or [] + frames = filter_internal_frames(frames) + yield ( + interpreter_id, + getattr(thread_info, "thread_id", None), + getattr(thread_info, "status", None), + frames, + None, + ) + + +def _display_filename(filename): + if not filename or filename == "~": + return filename + with contextlib.suppress(ValueError): + relpath = os.path.relpath(filename) + if not relpath.startswith(".." + os.sep) and relpath != "..": + return relpath + return filename + + +def _format_frame(frame, theme): + filename, location, qualname, opcode = _frame_fields(frame) + source_filename = filename + lineno = extract_lineno(location) + qualname_part = _color(qualname, theme.frame, theme) + + if filename == "~" and lineno == 0: + line = f" {qualname_part}" + else: + filename = _display_filename(filename) + if filename: + file_part = _color(f'"{filename}"', theme.filename, theme) + if lineno > 0: + line_part = _color(str(lineno), theme.line_no, theme) + line = f" File {file_part}, line {line_part}, in {qualname_part}" + else: + line = f" File {file_part}, in {qualname_part}" + else: + line = f" {qualname_part}" + + if opcode is not None: + line = f"{line} {_color(f'opcode={format_opcode(opcode)}', theme.opcode, theme)}" + + lines = [line] + source = _source_line(source_filename, location, lineno, theme) + if source: + lines.append(f" {source}") + return lines + + +def _source_offsets(line, location, lineno): + end_lineno = _location_field(location, 1, lineno) + col_offset = _location_field(location, 2, -1) + end_col_offset = _location_field(location, 3, -1) + + if col_offset < 0 or end_col_offset < 0 or end_lineno < lineno: + return None + + start = _byte_offset_to_character_offset(line, col_offset) + if end_lineno == lineno: + end = _byte_offset_to_character_offset(line, end_col_offset) + else: + end = len(line) + if start < 0 or end <= start: + return None + return start, end + + +def _trim_source_line(line, offsets): + stripped = line.lstrip() + leading = len(line) - len(stripped) + if offsets is None: + return stripped, None + + start, end = offsets + start = max(start - leading, 0) + end = max(end - leading, start + 1) + end = min(end, len(stripped)) + return stripped, (start, end) + + +def _highlight_source_line(line, offsets, theme): + if offsets is None or offsets[1] <= offsets[0]: + return _color(line, theme.source, theme) + + start, end = offsets + parts = [] + if line[:start]: + parts.append(_color(line[:start], theme.source, theme)) + parts.append(_color(line[start:end], theme.source_highlight, theme)) + if line[end:]: + parts.append(_color(line[end:], theme.source, theme)) + return "".join(parts) + + +def _source_line(filename, location, lineno, theme): + if not filename or filename == "~" or lineno <= 0: + return None + line = linecache.getline(filename, lineno).removesuffix("\n") + if not line: + return None + + offsets = _source_offsets(line, location, lineno) + line, offsets = _trim_source_line(line, offsets) + if not line: + return None + return _highlight_source_line(line, offsets, theme) + + +def _section_header( + *, + pid, + interpreter_id, + thread_id, + status, + label, + show_pid, + show_interpreter, + theme, +): + subject = "Stack dump" + if show_pid and pid is not None: + subject = f"{subject} for PID {pid}" + if thread_id is not None: + subject = f"{subject}, thread {thread_id}" + + details = [] + if show_interpreter and interpreter_id is not None: + details.append(f"interpreter {interpreter_id}") + if label: + details.append(label) + if status is not None: + status_text = _status_text(status) + if status_text: + details.append(status_text) + + suffix = "most recent call last" + if details: + suffix = f"{'; '.join(details)}; {suffix}" + return _color(f"{subject} ({suffix}):", theme.header, theme) + + +def format_stack_dump(stack_frames, *, pid=None, file=None, colorize=None): + """Return a formatted one-shot stack dump.""" + if file is None: + file = sys.stdout + + theme = _theme_for(file, colorize) + lines = [] + sections = list(_iter_dump_sections(stack_frames)) + if not sections: + if pid is None: + return f"{_color('No Python stacks found', theme.warning, theme)}\n" + return f"{_color(f'No Python stacks found for PID {pid}', theme.warning, theme)}\n" + + interpreter_ids = { + interpreter_id + for interpreter_id, _thread_id, _status, _frames, _label in sections + if interpreter_id is not None + } + show_interpreter = len(interpreter_ids) > 1 + + for section_index, (interpreter_id, thread_id, status, frames, label) in enumerate(sections): + if section_index: + lines.append("") + lines.append( + _section_header( + pid=pid, + interpreter_id=interpreter_id, + thread_id=thread_id, + status=status, + label=label, + show_pid=section_index == 0, + show_interpreter=show_interpreter, + theme=theme, + ) + ) + + if not frames: + lines.append(_color("No Python frames", theme.warning, theme)) + continue + + for frame in reversed(frames): + lines.extend(_format_frame(frame, theme)) + + return "\n".join(lines) + "\n" + + +def print_stack_dump(stack_frames, *, pid=None, file=None, colorize=None): + """Pretty-print a one-shot stack dump.""" + if file is None: + file = sys.stdout + file.write(format_stack_dump(stack_frames, pid=pid, file=file, colorize=colorize)) diff --git a/Lib/profiling/sampling/errors.py b/Lib/profiling/sampling/errors.py new file mode 100644 index 00000000000000..0832ad2d4381e0 --- /dev/null +++ b/Lib/profiling/sampling/errors.py @@ -0,0 +1,19 @@ +"""Custom exceptions for the sampling profiler.""" + +class SamplingProfilerError(Exception): + """Base exception for sampling profiler errors.""" + +class SamplingUnknownProcessError(SamplingProfilerError): + def __init__(self, pid): + self.pid = pid + super().__init__(f"Process with PID '{pid}' does not exist.") + +class SamplingScriptNotFoundError(SamplingProfilerError): + def __init__(self, script_path): + self.script_path = script_path + super().__init__(f"Script '{script_path}' not found.") + +class SamplingModuleNotFoundError(SamplingProfilerError): + def __init__(self, module_name): + self.module_name = module_name + super().__init__(f"Module '{module_name}' not found.") diff --git a/Lib/profiling/sampling/gecko_collector.py b/Lib/profiling/sampling/gecko_collector.py new file mode 100644 index 00000000000000..361f6037f216fd --- /dev/null +++ b/Lib/profiling/sampling/gecko_collector.py @@ -0,0 +1,980 @@ +import itertools +import io +import json +import os +import platform +import sys +import tempfile +import threading +import time + +from .collector import Collector, filter_internal_frames +from .opcode_utils import get_opcode_info, format_opcode +try: + from _remote_debugging import THREAD_STATUS_HAS_GIL, THREAD_STATUS_ON_CPU, THREAD_STATUS_UNKNOWN, THREAD_STATUS_GIL_REQUESTED, THREAD_STATUS_HAS_EXCEPTION, THREAD_STATUS_MAIN_THREAD +except ImportError: + # Fallback if module not available (shouldn't happen in normal use) + THREAD_STATUS_HAS_GIL = (1 << 0) + THREAD_STATUS_ON_CPU = (1 << 1) + THREAD_STATUS_UNKNOWN = (1 << 2) + THREAD_STATUS_GIL_REQUESTED = (1 << 3) + THREAD_STATUS_HAS_EXCEPTION = (1 << 4) + THREAD_STATUS_MAIN_THREAD = (1 << 5) + + +# Categories matching Firefox Profiler expectations +GECKO_CATEGORIES = [ + {"name": "Other", "color": "grey", "subcategories": ["Other"]}, + {"name": "Python", "color": "yellow", "subcategories": ["Other"]}, + {"name": "Native", "color": "blue", "subcategories": ["Other"]}, + {"name": "GC", "color": "orange", "subcategories": ["Other"]}, + {"name": "GIL", "color": "green", "subcategories": ["Other"]}, + {"name": "CPU", "color": "purple", "subcategories": ["Other"]}, + {"name": "Code Type", "color": "red", "subcategories": ["Other"]}, + {"name": "Opcodes", "color": "magenta", "subcategories": ["Other"]}, + {"name": "Exception", "color": "lightblue", "subcategories": ["Other"]}, +] + +# Category indices +CATEGORY_OTHER = 0 +CATEGORY_PYTHON = 1 +CATEGORY_NATIVE = 2 +CATEGORY_GC = 3 +CATEGORY_GIL = 4 +CATEGORY_CPU = 5 +CATEGORY_CODE_TYPE = 6 +CATEGORY_OPCODES = 7 +CATEGORY_EXCEPTION = 8 + +# Subcategory indices +DEFAULT_SUBCATEGORY = 0 + +GECKO_FORMAT_VERSION = 32 +GECKO_PREPROCESSED_VERSION = 57 + +# Resource type constants +RESOURCE_TYPE_LIBRARY = 1 + +# Frame constants +FRAME_ADDRESS_NONE = -1 +FRAME_INLINE_DEPTH_ROOT = 0 + +# Process constants +PROCESS_TYPE_MAIN = 0 +STACKWALK_DISABLED = 0 + +# In-memory buffer before spilling to disk +DEFAULT_SPILL_BUFFER_BYTES = 128 * 1024 +_JSON_SEPARATORS = (",", ":") +_JSON_ENCODER = json.JSONEncoder( + separators=_JSON_SEPARATORS, allow_nan=False +) + + +class SpillColumn: + def __init__(self, directory, basename, *, + buffer_bytes=None): + self.path = os.path.join(directory, basename) + self.buffer = bytearray() + self._buffer_bytes = ( + DEFAULT_SPILL_BUFFER_BYTES if buffer_bytes is None + else buffer_bytes + ) + + def append(self, value): + self.buffer += (_JSON_ENCODER.encode(value) + "\n").encode("utf-8") + if len(self.buffer) >= self._buffer_bytes: + self.flush() + + def flush(self): + with open(self.path, "ab") as file: + file.write(self.buffer) + self.buffer.clear() + + def iter_tokens(self): + with open(self.path, encoding="utf-8") as file: + for line in file: + yield line.rstrip("\n") + + +class GeckoThreadSpill: + _COLUMNS = ( + ("samples_stack", "samples-stack.json"), + ("samples_time", "samples-time.json"), + ("markers_name", "markers-name.json"), + ("markers_start_time", "markers-start-time.json"), + ("markers_end_time", "markers-end-time.json"), + ("markers_phase", "markers-phase.json"), + ("markers_category", "markers-category.json"), + ("markers_data", "markers-data.json"), + ) + + def __init__(self, directory, tid): + prefix = f"thread-{tid}-" + for attr, basename in self._COLUMNS: + setattr(self, attr, SpillColumn(directory, prefix + basename)) + self.sample_count = 0 + self.marker_count = 0 + + def append_sample(self, stack_index, time_ms): + self.samples_stack.append(stack_index) + self.samples_time.append(time_ms) + self.sample_count += 1 + + def append_marker(self, name_idx, start_time, end_time, phase, category, data): + self.markers_name.append(name_idx) + self.markers_start_time.append(start_time) + self.markers_end_time.append(end_time) + self.markers_phase.append(phase) + self.markers_category.append(category) + self.markers_data.append(data) + self.marker_count += 1 + + def prepare_read(self): + for attr, _basename in self._COLUMNS: + getattr(self, attr).flush() + + +class GeckoCollector(Collector): + aggregating = True + + def __init__(self, sample_interval_usec, *, skip_idle=False, opcodes=False): + self.sample_interval_usec = sample_interval_usec + self.skip_idle = skip_idle + self.opcodes_enabled = opcodes + self.start_time = time.monotonic() * 1000 # milliseconds since start + + # Global string table (shared across all threads) + self.global_strings = ["(root)"] # Start with root + self.global_string_map = {"(root)": 0} + + # Per-thread data structures + self.threads = {} # tid -> thread data + self.spill_dir = None + self.exported = False + + # Global tables + self.libs = [] + + # Sampling interval tracking + self.sample_count = 0 + self.last_sample_time = 0 + self.interval = 1.0 # Will be calculated from actual sampling + + # State tracking for interval markers (tid -> start_time) + self.has_gil_start = {} # Thread has the GIL + self.no_gil_start = {} # Thread doesn't have the GIL + self.on_cpu_start = {} # Thread is running on CPU + self.off_cpu_start = {} # Thread is off CPU + self.python_code_start = {} # Thread running Python code (has GIL) + self.native_code_start = {} # Thread running native code (on CPU without GIL) + self.gil_wait_start = {} # Thread waiting for GIL + self.exception_start = {} # Thread has an exception set + self.no_exception_start = {} # Thread has no exception set + + # GC event tracking: track GC start time per thread + self.gc_start_per_thread = {} # tid -> start_time + + # Track which threads have been initialized for state tracking + self.initialized_threads = set() + + # Opcode state tracking per thread: tid -> (opcode, lineno, col_offset, funcname, filename, start_time) + self.opcode_state = {} + + # For binary replay: track base timestamp (first sample's timestamp) + self._replay_base_timestamp_us = None + + def _track_state_transition(self, tid, condition, active_dict, inactive_dict, + active_name, inactive_name, category, current_time): + """Track binary state transitions and emit markers. + + Args: + tid: Thread ID + condition: Whether the active state is true + active_dict: Dict tracking start time of active state + inactive_dict: Dict tracking start time of inactive state + active_name: Name for active state marker + inactive_name: Name for inactive state marker + category: Gecko category for the markers + current_time: Current timestamp + """ + # On first observation of a thread, just record the current state + # without creating a marker (we don't know what the previous state was) + if tid not in self.initialized_threads: + if condition: + active_dict[tid] = current_time + else: + inactive_dict[tid] = current_time + return + + # For already-initialized threads, track transitions + if condition: + active_dict.setdefault(tid, current_time) + if tid in inactive_dict: + self._add_marker(tid, inactive_name, inactive_dict.pop(tid), + current_time, category) + else: + inactive_dict.setdefault(tid, current_time) + if tid in active_dict: + self._add_marker(tid, active_name, active_dict.pop(tid), + current_time, category) + + def collect(self, stack_frames, timestamps_us=None): + """Collect samples from stack frames. + + Args: + stack_frames: List of interpreter/thread frame info + timestamps_us: List of timestamps in microseconds (None for live sampling) + """ + if self.exported: + raise RuntimeError("cannot append to GeckoCollector after export") + + # Handle live sampling (no timestamps provided) + if timestamps_us is None: + current_time = (time.monotonic() * 1000) - self.start_time + times = [current_time] + else: + if not timestamps_us: + return + # Initialize base timestamp if needed + if self._replay_base_timestamp_us is None: + self._replay_base_timestamp_us = timestamps_us[0] + # Convert all timestamps to times (ms relative to first sample) + base = self._replay_base_timestamp_us + times = [(ts - base) / 1000 for ts in timestamps_us] + + first_time = times[0] + + # Update interval calculation + if self.sample_count > 0 and self.last_sample_time > 0: + self.interval = (times[-1] - self.last_sample_time) / self.sample_count + self.last_sample_time = times[-1] + + # Process threads + for interpreter_info in stack_frames: + for thread_info in interpreter_info.threads: + frames = filter_internal_frames(thread_info.frame_info) + tid = thread_info.thread_id + status_flags = thread_info.status + is_main_thread = bool(status_flags & THREAD_STATUS_MAIN_THREAD) + + # Initialize thread if needed + if tid not in self.threads: + self.threads[tid] = self._create_thread(tid, is_main_thread) + + thread_data = self.threads[tid] + + # Decode status flags + has_gil = bool(status_flags & THREAD_STATUS_HAS_GIL) + on_cpu = bool(status_flags & THREAD_STATUS_ON_CPU) + gil_requested = bool(status_flags & THREAD_STATUS_GIL_REQUESTED) + + # Track state transitions using first timestamp + self._track_state_transition( + tid, has_gil, self.has_gil_start, self.no_gil_start, + "Has GIL", "No GIL", CATEGORY_GIL, first_time + ) + self._track_state_transition( + tid, on_cpu, self.on_cpu_start, self.off_cpu_start, + "On CPU", "Off CPU", CATEGORY_CPU, first_time + ) + + # Track code type + if has_gil: + self._track_state_transition( + tid, True, self.python_code_start, self.native_code_start, + "Python Code", "Native Code", CATEGORY_CODE_TYPE, first_time + ) + elif on_cpu: + self._track_state_transition( + tid, True, self.native_code_start, self.python_code_start, + "Native Code", "Python Code", CATEGORY_CODE_TYPE, first_time + ) + else: + if tid in self.initialized_threads: + if tid in self.python_code_start: + self._add_marker(tid, "Python Code", self.python_code_start.pop(tid), + first_time, CATEGORY_CODE_TYPE) + if tid in self.native_code_start: + self._add_marker(tid, "Native Code", self.native_code_start.pop(tid), + first_time, CATEGORY_CODE_TYPE) + + # Track GIL wait + if gil_requested: + self.gil_wait_start.setdefault(tid, first_time) + elif tid in self.gil_wait_start: + self._add_marker(tid, "Waiting for GIL", self.gil_wait_start.pop(tid), + first_time, CATEGORY_GIL) + + # Track exception state + has_exception = bool(status_flags & THREAD_STATUS_HAS_EXCEPTION) + self._track_state_transition( + tid, has_exception, self.exception_start, self.no_exception_start, + "Has Exception", "No Exception", CATEGORY_EXCEPTION, first_time + ) + + # Track GC events + has_gc_frame = any(frame[2] == "" for frame in frames) + if has_gc_frame: + if tid not in self.gc_start_per_thread: + self.gc_start_per_thread[tid] = first_time + elif tid in self.gc_start_per_thread: + self._add_marker(tid, "GC Collecting", self.gc_start_per_thread.pop(tid), + first_time, CATEGORY_GC) + + # Mark thread as initialized + self.initialized_threads.add(tid) + + # Skip idle threads if requested + is_idle = not has_gil and not on_cpu + if self.skip_idle and is_idle: + continue + + if not frames: + continue + + # Process stack once to get stack_index + stack_index = self._process_stack(thread_data, frames) + + # Add samples with timestamps + thread_spill = thread_data["_spill"] + for t in times: + thread_spill.append_sample(stack_index, t) + + # Handle opcodes + if self.opcodes_enabled and frames: + leaf_frame = frames[0] + filename, location, funcname, opcode = leaf_frame + if isinstance(location, tuple): + lineno, _, col_offset, _ = location + else: + lineno = location + col_offset = -1 + + current_state = (opcode, lineno, col_offset, funcname, filename) + + if tid not in self.opcode_state: + self.opcode_state[tid] = (*current_state, first_time) + elif self.opcode_state[tid][:5] != current_state: + prev_opcode, prev_lineno, prev_col, prev_funcname, prev_filename, prev_start = self.opcode_state[tid] + self._add_opcode_interval_marker( + tid, prev_opcode, prev_lineno, prev_col, prev_funcname, prev_start, first_time + ) + self.opcode_state[tid] = (*current_state, first_time) + + self.sample_count += len(times) + + def _create_thread(self, tid, is_main_thread): + """Create a new thread structure with processed profile format.""" + if self.spill_dir is None: + self.spill_dir = tempfile.TemporaryDirectory() + + thread = { + "name": f"Thread-{tid}", + "isMainThread": is_main_thread, + "processStartupTime": 0, + "processShutdownTime": None, + "registerTime": 0, + "unregisterTime": None, + "pausedRanges": [], + "pid": str(os.getpid()), + "tid": tid, + "processType": "default", + "processName": "Python Process", + # Stack table - processed format + "stackTable": { + "frame": [], + "category": [], + "subcategory": [], + "prefix": [], + "length": 0, # Will be updated on export + }, + # Frame table - processed format + "frameTable": { + "address": [], + "category": [], + "subcategory": [], + "func": [], + "innerWindowID": [], + "implementation": [], + "optimizations": [], + "line": [], + "column": [], + "inlineDepth": [], + "nativeSymbol": [], + "length": 0, # Will be updated on export + }, + # Function table - processed format + "funcTable": { + "name": [], + "isJS": [], + "relevantForJS": [], + "resource": [], + "fileName": [], + "lineNumber": [], + "columnNumber": [], + "length": 0, # Will be updated on export + }, + # Resource table - processed format + "resourceTable": { + "lib": [], + "name": [], + "host": [], + "type": [], + "length": 0, # Will be updated on export + }, + # Native symbols table (empty for Python) + "nativeSymbols": { + "libIndex": [], + "address": [], + "name": [], + "functionSize": [], + "length": 0, + }, + # Caches for deduplication + "_stackCache": {}, + "_frameCache": {}, + "_funcCache": {}, + "_resourceCache": {}, + "_spill": GeckoThreadSpill(self.spill_dir.name, tid), + } + + return thread + + def _is_python(self, filename: str) -> bool: + return not filename.startswith("<") or filename in ["", ""] + + def _get_category(self, filename: str) -> int: + return CATEGORY_PYTHON if self._is_python(filename) else CATEGORY_NATIVE + + def _intern_string(self, s): + """Intern a string in the global string table.""" + if s in self.global_string_map: + return self.global_string_map[s] + idx = len(self.global_strings) + self.global_strings.append(s) + self.global_string_map[s] = idx + return idx + + def _add_marker(self, tid, name, start_time, end_time, category): + """Add an interval marker for a specific thread.""" + if tid not in self.threads: + return + + duration = end_time - start_time + + name_idx = self._intern_string(name) + self.threads[tid]["_spill"].append_marker( + name_idx, start_time, end_time, 1, category, { + "type": name.replace(" ", ""), + "duration": duration, + "tid": tid, + } + ) + + def _add_opcode_interval_marker(self, tid, opcode, lineno, col_offset, + funcname, start_time, end_time): + """Add an interval marker for opcode execution span.""" + if tid not in self.threads or opcode is None: + return + + opcode_info = get_opcode_info(opcode) + # Use formatted opcode name (with base opcode for specialized ones) + formatted_opname = format_opcode(opcode) + + name_idx = self._intern_string(formatted_opname) + + self.threads[tid]["_spill"].append_marker( + name_idx, start_time, end_time, 1, CATEGORY_OPCODES, { + "type": "Opcode", + "opcode": opcode, + "opname": formatted_opname, + "base_opname": opcode_info["base_opname"], + "is_specialized": opcode_info["is_specialized"], + "line": lineno, + "column": col_offset if col_offset >= 0 else None, + "function": funcname, + "duration": end_time - start_time, + } + ) + + def _process_stack(self, thread_data, frames): + """Process a stack and return the stack index.""" + if not frames: + return None + + # Cache references to avoid repeated dictionary lookups + stack_cache = thread_data["_stackCache"] + stack_table = thread_data["stackTable"] + stack_frames = stack_table["frame"] + stack_prefix = stack_table["prefix"] + stack_category = stack_table["category"] + stack_subcategory = stack_table["subcategory"] + + # Build stack bottom-up (from root to leaf) + prefix_stack_idx = None + + for frame_tuple in reversed(frames): + # frame_tuple is (filename, location, funcname, opcode) + # location is (lineno, end_lineno, col_offset, end_col_offset) or just lineno + filename, location, funcname, opcode = frame_tuple + if isinstance(location, tuple): + lineno, end_lineno, col_offset, end_col_offset = location + else: + # Legacy format: location is just lineno + lineno = location + col_offset = -1 + end_col_offset = -1 + + # Get or create function + func_idx = self._get_or_create_func( + thread_data, filename, funcname, lineno + ) + + # Get or create frame (include column for precise source location) + frame_idx = self._get_or_create_frame( + thread_data, func_idx, lineno, col_offset + ) + + # Check stack cache + stack_key = (frame_idx, prefix_stack_idx) + if stack_key in stack_cache: + prefix_stack_idx = stack_cache[stack_key] + else: + # Create new stack entry + stack_idx = len(stack_frames) + stack_frames.append(frame_idx) + stack_prefix.append(prefix_stack_idx) + + # Determine category + category = self._get_category(filename) + stack_category.append(category) + stack_subcategory.append(DEFAULT_SUBCATEGORY) + + stack_cache[stack_key] = stack_idx + prefix_stack_idx = stack_idx + + return prefix_stack_idx + + def _get_or_create_func(self, thread_data, filename, funcname, lineno): + """Get or create a function entry.""" + func_cache = thread_data["_funcCache"] + func_key = (filename, funcname) + + if func_key in func_cache: + return func_cache[func_key] + + # Cache references for func table + func_table = thread_data["funcTable"] + func_names = func_table["name"] + func_is_js = func_table["isJS"] + func_relevant = func_table["relevantForJS"] + func_resources = func_table["resource"] + func_filenames = func_table["fileName"] + func_line_numbers = func_table["lineNumber"] + func_column_numbers = func_table["columnNumber"] + + func_idx = len(func_names) + + # Intern strings in global table + name_idx = self._intern_string(funcname) + + # Determine if Python + is_python = self._is_python(filename) + + # Create resource + resource_idx = self._get_or_create_resource(thread_data, filename) + + # Add function + func_names.append(name_idx) + func_is_js.append(is_python) + func_relevant.append(is_python) + func_resources.append(resource_idx) + + if is_python: + filename_idx = self._intern_string(os.path.basename(filename)) + func_filenames.append(filename_idx) + func_line_numbers.append(lineno) + else: + func_filenames.append(None) + func_line_numbers.append(None) + func_column_numbers.append(None) + + func_cache[func_key] = func_idx + return func_idx + + def _get_or_create_resource(self, thread_data, filename): + """Get or create a resource entry.""" + resource_cache = thread_data["_resourceCache"] + + if filename in resource_cache: + return resource_cache[filename] + + # Cache references for resource table + resource_table = thread_data["resourceTable"] + resource_libs = resource_table["lib"] + resource_names = resource_table["name"] + resource_hosts = resource_table["host"] + resource_types = resource_table["type"] + + resource_idx = len(resource_names) + resource_name = ( + os.path.basename(filename) if "/" in filename else filename + ) + name_idx = self._intern_string(resource_name) + + resource_libs.append(None) + resource_names.append(name_idx) + resource_hosts.append(None) + resource_types.append(RESOURCE_TYPE_LIBRARY) + + resource_cache[filename] = resource_idx + return resource_idx + + def _get_or_create_frame(self, thread_data, func_idx, lineno, col_offset=-1): + """Get or create a frame entry.""" + frame_cache = thread_data["_frameCache"] + # Include column in cache key for precise frame identification + frame_key = (func_idx, lineno, col_offset if col_offset >= 0 else None) + + if frame_key in frame_cache: + return frame_cache[frame_key] + + # Cache references for frame table + frame_table = thread_data["frameTable"] + frame_addresses = frame_table["address"] + frame_inline_depths = frame_table["inlineDepth"] + frame_categories = frame_table["category"] + frame_subcategories = frame_table["subcategory"] + frame_funcs = frame_table["func"] + frame_native_symbols = frame_table["nativeSymbol"] + frame_inner_window_ids = frame_table["innerWindowID"] + frame_implementations = frame_table["implementation"] + frame_lines = frame_table["line"] + frame_columns = frame_table["column"] + frame_optimizations = frame_table["optimizations"] + + frame_idx = len(frame_funcs) + + # Determine category based on function - use cached func table reference + is_python = thread_data["funcTable"]["isJS"][func_idx] + category = CATEGORY_PYTHON if is_python else CATEGORY_NATIVE + + frame_addresses.append(FRAME_ADDRESS_NONE) + frame_inline_depths.append(FRAME_INLINE_DEPTH_ROOT) + frame_categories.append(category) + frame_subcategories.append(DEFAULT_SUBCATEGORY) + frame_funcs.append(func_idx) + frame_native_symbols.append(None) + frame_inner_window_ids.append(None) + frame_implementations.append(None) + frame_lines.append(lineno if lineno else None) + # Store column offset if available (>= 0), otherwise None + frame_columns.append(col_offset if col_offset >= 0 else None) + frame_optimizations.append(None) + + frame_cache[frame_key] = frame_idx + return frame_idx + + def _finalize_markers(self): + """Close any open markers at the end of profiling.""" + end_time = self.last_sample_time + + # Close all open markers for each thread using a generic approach + marker_states = [ + (self.has_gil_start, "Has GIL", CATEGORY_GIL), + (self.no_gil_start, "No GIL", CATEGORY_GIL), + (self.on_cpu_start, "On CPU", CATEGORY_CPU), + (self.off_cpu_start, "Off CPU", CATEGORY_CPU), + (self.python_code_start, "Python Code", CATEGORY_CODE_TYPE), + (self.native_code_start, "Native Code", CATEGORY_CODE_TYPE), + (self.gil_wait_start, "Waiting for GIL", CATEGORY_GIL), + (self.gc_start_per_thread, "GC Collecting", CATEGORY_GC), + (self.exception_start, "Has Exception", CATEGORY_EXCEPTION), + (self.no_exception_start, "No Exception", CATEGORY_EXCEPTION), + ] + + for state_dict, marker_name, category in marker_states: + for tid in list(state_dict.keys()): + self._add_marker(tid, marker_name, state_dict[tid], end_time, category) + del state_dict[tid] + + # Close any open opcode markers + for tid, state in list(self.opcode_state.items()): + opcode, lineno, col_offset, funcname, filename, start_time = state + self._add_opcode_interval_marker(tid, opcode, lineno, col_offset, funcname, start_time, end_time) + self.opcode_state.clear() + + def export(self, filename): + """Export the profile to a Gecko JSON file.""" + if self.sample_count > 0 and self.last_sample_time > 0: + self.interval = self.last_sample_time / self.sample_count + + # Spinner for progress indication + spinner = itertools.cycle(['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']) + stop_spinner = threading.Event() + + def spin(): + message = 'Building Gecko profile...' + while not stop_spinner.is_set(): + sys.stderr.write(f'\r{next(spinner)} {message}') + sys.stderr.flush() + time.sleep(0.1) + # Clear the spinner line + sys.stderr.write('\r' + ' ' * (len(message) + 3) + '\r') + sys.stderr.flush() + + spinner_thread = threading.Thread(target=spin, daemon=True) + spinner_thread.start() + + temp_path = None + replaced = False + try: + self._prepare_for_serialization() + output_dir = os.path.dirname(os.path.abspath(filename)) or "." + with tempfile.NamedTemporaryFile( + "w", dir=output_dir, delete=False + ) as file: + temp_path = file.name + self._stream_profile(file) + os.replace(temp_path, filename) + replaced = True + finally: + self.exported = True + stop_spinner.set() + spinner_thread.join(timeout=1.0) + # Small delay to ensure the clear happens + time.sleep(0.01) + if temp_path is not None and not replaced: + try: + os.unlink(temp_path) + except FileNotFoundError: + pass + self._cleanup_spills() + + print(f"Gecko profile written to {filename}") + print( + f"Open in Firefox Profiler: https://profiler.firefox.com/" + ) + + def _build_marker_schema(self): + """Build marker schema definitions for Firefox Profiler.""" + schema = [] + + # Opcode marker schema (only if opcodes enabled) + if self.opcodes_enabled: + schema.append({ + "name": "Opcode", + "display": ["marker-table", "marker-chart"], + "tooltipLabel": "{marker.data.opname}", + "tableLabel": "{marker.data.opname} at line {marker.data.line}", + "chartLabel": "{marker.data.opname}", + "fields": [ + {"key": "opname", "label": "Opcode", "format": "string", "searchable": True}, + {"key": "base_opname", "label": "Base Opcode", "format": "string"}, + {"key": "is_specialized", "label": "Specialized", "format": "string"}, + {"key": "line", "label": "Line", "format": "integer"}, + {"key": "column", "label": "Column", "format": "integer"}, + {"key": "function", "label": "Function", "format": "string"}, + {"key": "duration", "label": "Duration", "format": "duration"}, + ], + }) + + return schema + + def _build_profile(self): + """Build the complete profile structure in processed format.""" + try: + self._prepare_for_serialization() + file = io.StringIO() + self._stream_profile(file) + return json.loads(file.getvalue()) + finally: + self.exported = True + self._cleanup_spills() + + def _profile_head(self): + return { + "meta": { + "interval": self.interval, + "startTime": self.start_time, + "abi": platform.machine(), + "misc": "Python profiler", + "oscpu": platform.machine(), + "platform": platform.system(), + "processType": PROCESS_TYPE_MAIN, + "categories": GECKO_CATEGORIES, + "stackwalk": STACKWALK_DISABLED, + "toolkit": "", + "version": GECKO_FORMAT_VERSION, + "preprocessedProfileVersion": GECKO_PREPROCESSED_VERSION, + "appBuildID": "", + "physicalCPUs": os.cpu_count() or 0, + "logicalCPUs": os.cpu_count() or 0, + "CPUName": "", + "product": "Python", + "symbolicated": True, + "markerSchema": self._build_marker_schema(), + "importedFrom": "Tachyon Sampling Profiler", + "extensions": { + "id": [], + "name": [], + "baseURL": [], + "length": 0, + }, + }, + "libs": self.libs, + } + + def _profile_tail(self): + return { + "pages": [], + "shared": { + "stringArray": self.global_strings, + "sources": {"length": 0, "uuid": [], "filename": []}, + }, + } + + def _prepare_for_serialization(self): + if self.exported: + raise RuntimeError("GeckoCollector has already been exported") + self._finalize_markers() + for thread_data in self.threads.values(): + thread_data["_spill"].prepare_read() + thread_data["stackTable"]["length"] = len(thread_data["stackTable"]["frame"]) + thread_data["frameTable"]["length"] = len(thread_data["frameTable"]["func"]) + thread_data["funcTable"]["length"] = len(thread_data["funcTable"]["name"]) + thread_data["resourceTable"]["length"] = len(thread_data["resourceTable"]["name"]) + + def _cleanup_spills(self): + if self.spill_dir is not None: + self.spill_dir.cleanup() + self.spill_dir = None + + def _stream_profile(self, file): + file.write("{") + first = True + for key, value in self._profile_head().items(): + first = _write_json_member(file, key, value, first) + + first = _write_member_name(file, "threads", first) + file.write("[") + for index, (tid, thread_data) in enumerate(self.threads.items()): + if index: + file.write(",") + self._stream_thread(file, tid, thread_data) + file.write("]") + + for key, value in self._profile_tail().items(): + first = _write_json_member(file, key, value, first) + file.write("}") + + def _stream_thread(self, file, tid, thread_data): + spill = thread_data["_spill"] + metadata = { + "name": thread_data["name"], + "isMainThread": thread_data["isMainThread"], + "processStartupTime": thread_data["processStartupTime"], + "processShutdownTime": thread_data["processShutdownTime"], + "registerTime": thread_data["registerTime"], + "unregisterTime": thread_data["unregisterTime"], + "pausedRanges": thread_data["pausedRanges"], + "pid": thread_data["pid"], + "tid": thread_data["tid"], + "processType": thread_data["processType"], + "processName": thread_data["processName"], + } + file.write("{") + first = True + for key, value in metadata.items(): + first = _write_json_member(file, key, value, first) + + first = _write_member_name(file, "samples", first) + self._stream_samples(file, spill) + for key in ( + "stackTable", + "frameTable", + "funcTable", + "resourceTable", + "nativeSymbols", + ): + first = _write_json_member(file, key, thread_data[key], first) + first = _write_member_name(file, "markers", first) + self._stream_markers(file, spill) + file.write("}") + + def _stream_samples(self, file, spill): + _stream_column_table( + file, + ( + ("stack", spill.samples_stack.iter_tokens()), + ("time", spill.samples_time.iter_tokens()), + ("eventDelay", ("null" for _ in range(spill.sample_count))), + ), + spill.sample_count, + ( + ("weight", None), + ("weightType", "samples"), + ("length", spill.sample_count), + ), + ) + + def _stream_markers(self, file, spill): + _stream_column_table( + file, + ( + ("data", spill.markers_data.iter_tokens()), + ("name", spill.markers_name.iter_tokens()), + ("startTime", spill.markers_start_time.iter_tokens()), + ("endTime", spill.markers_end_time.iter_tokens()), + ("phase", spill.markers_phase.iter_tokens()), + ("category", spill.markers_category.iter_tokens()), + ), + spill.marker_count, + (("length", spill.marker_count),), + ) + + +def _write_json(file, value): + for chunk in _JSON_ENCODER.iterencode(value): + file.write(chunk) + + +def _write_member_name(file, name, first): + if not first: + file.write(",") + _write_json(file, name) + file.write(":") + return False + + +def _write_json_member(file, name, value, first): + first = _write_member_name(file, name, first) + _write_json(file, value) + return first + + +def _stream_column_table(file, columns, expected_count, trailing_members=()): + file.write("{") + first = True + for name, token_iter in columns: + first = _write_member_name(file, name, first) + _stream_array(file, token_iter, expected_count, name) + for name, value in trailing_members: + first = _write_json_member(file, name, value, first) + file.write("}") + + +def _stream_array(file, token_iter, expected_count, label="array"): + file.write("[") + count = 0 + for token in token_iter: + if count: + file.write(",") + file.write(token) + count += 1 + if count != expected_count: + raise RuntimeError( + f"streamed {count} {label} items, expected {expected_count}" + ) + file.write("]") diff --git a/Lib/profiling/sampling/heatmap_collector.py b/Lib/profiling/sampling/heatmap_collector.py new file mode 100644 index 00000000000000..6e650ec08f410b --- /dev/null +++ b/Lib/profiling/sampling/heatmap_collector.py @@ -0,0 +1,1144 @@ +"""Heatmap collector for Python profiling with line-level execution heat visualization.""" + +import base64 +import collections +import html +import importlib.resources +import json +import locale +import math +import os +import platform +import site +import sys +from dataclasses import dataclass, field +from pathlib import Path +from typing import Dict, List, Tuple + +from ._css_utils import get_combined_css +from ._format_utils import fmt +from .collector import normalize_location, extract_lineno +from .opcode_utils import get_opcode_info, format_opcode +from .stack_collector import StackTraceCollector +from .module_utils import extract_module_name, get_python_path_info + + +# ============================================================================ +# Data Classes +# ============================================================================ + +@dataclass +class FileStats: + """Statistics for a single profiled file.""" + filename: str + module_name: str + module_type: str + total_samples: int + total_self_samples: int + num_lines: int + max_samples: int + max_self_samples: int + percentage: float = 0.0 + + +@dataclass +class TreeNode: + """Node in the hierarchical file tree structure.""" + files: List[FileStats] = field(default_factory=list) + samples: int = 0 + count: int = 0 + children: Dict[str, 'TreeNode'] = field(default_factory=dict) + + +# ============================================================================ +# Helper Classes +# ============================================================================ + +class _TemplateLoader: + """Loads and caches HTML/CSS/JS templates for heatmap generation.""" + + def __init__(self): + """Load all templates and assets once.""" + self.index_template = None + self.file_template = None + self.index_css = None + self.index_js = None + self.file_css = None + self.file_js = None + self.logo_html = None + + self._load_templates() + + def _load_templates(self): + """Load all template files from _heatmap_assets.""" + try: + template_dir = importlib.resources.files(__package__) + assets_dir = template_dir / "_heatmap_assets" + + # Load HTML templates + self.index_template = (assets_dir / "heatmap_index_template.html").read_text(encoding="utf-8") + self.file_template = (assets_dir / "heatmap_pyfile_template.html").read_text(encoding="utf-8") + + # Load CSS (same file used for both index and file pages) + css_content = get_combined_css("heatmap") + self.index_css = css_content + self.file_css = css_content + + # Load JS + base_js = (template_dir / "_shared_assets" / "base.js").read_text(encoding="utf-8") + heatmap_shared_js = (assets_dir / "heatmap_shared.js").read_text(encoding="utf-8") + shared_js = f"{base_js}\n{heatmap_shared_js}" + self.index_js = f"{shared_js}\n{(assets_dir / 'heatmap_index.js').read_text(encoding='utf-8')}" + self.file_js = f"{shared_js}\n{(assets_dir / 'heatmap.js').read_text(encoding='utf-8')}" + + # Load Tachyon logo + logo_dir = template_dir / "_assets" + try: + png_path = logo_dir / "tachyon-logo.png" + b64_logo = base64.b64encode(png_path.read_bytes()).decode("ascii") + self.logo_html = f'' + except (FileNotFoundError, IOError) as e: + self.logo_html = '
    ' + print(f"Warning: Could not load Tachyon logo: {e}") + + except (FileNotFoundError, IOError) as e: + raise RuntimeError(f"Failed to load heatmap template files: {e}") from e + + +class _TreeBuilder: + """Builds hierarchical tree structure from file statistics.""" + + @staticmethod + def build_file_tree(file_stats: List[FileStats]) -> Dict[str, TreeNode]: + """Build hierarchical tree grouped by module type, then by module structure. + + Args: + file_stats: List of FileStats objects + + Returns: + Dictionary mapping module types to their tree roots + """ + # Group by module type first + type_groups = {'stdlib': [], 'site-packages': [], 'project': [], 'other': []} + for stat in file_stats: + type_groups[stat.module_type].append(stat) + + # Build tree for each type + trees = {} + for module_type, stats in type_groups.items(): + if not stats: + continue + + root_node = TreeNode() + + for stat in stats: + module_name = stat.module_name + parts = module_name.split('.') + + # Navigate/create tree structure + current_node = root_node + for i, part in enumerate(parts): + if i == len(parts) - 1: + # Last part - store the file + current_node.files.append(stat) + else: + # Intermediate part - create or navigate + if part not in current_node.children: + current_node.children[part] = TreeNode() + current_node = current_node.children[part] + + # Calculate aggregate stats for this type's tree + _TreeBuilder._calculate_node_stats(root_node) + trees[module_type] = root_node + + return trees + + @staticmethod + def _calculate_node_stats(node: TreeNode) -> Tuple[int, int]: + """Recursively calculate aggregate statistics for tree nodes. + + Args: + node: TreeNode to calculate stats for + + Returns: + Tuple of (total_samples, file_count) + """ + total_samples = 0 + file_count = 0 + + # Count files at this level + for file_stat in node.files: + total_samples += file_stat.total_samples + file_count += 1 + + # Recursively process children + for child in node.children.values(): + child_samples, child_count = _TreeBuilder._calculate_node_stats(child) + total_samples += child_samples + file_count += child_count + + node.samples = total_samples + node.count = file_count + return total_samples, file_count + + +class _HtmlRenderer: + """Renders hierarchical tree structures as HTML.""" + + def __init__(self, file_index: Dict[str, str]): + """Initialize renderer with file index. + + Args: + file_index: Mapping from filenames to HTML file names + """ + self.file_index = file_index + self.heatmap_bar_height = 16 + + def render_hierarchical_html(self, trees: Dict[str, TreeNode]) -> str: + """Build hierarchical HTML with type sections and collapsible module folders. + + Args: + trees: Dictionary mapping module types to tree roots + + Returns: + Complete HTML string for all sections + """ + type_names = { + 'stdlib': '📚 Standard Library', + 'site-packages': '📦 Site Packages', + 'project': '🏗️ Project Files', + 'other': '📄 Other Files' + } + + sections = [] + for module_type in ['project', 'stdlib', 'site-packages', 'other']: + if module_type not in trees: + continue + + tree = trees[module_type] + + # Project starts expanded, others start collapsed + is_collapsed = module_type in {'stdlib', 'site-packages', 'other'} + icon = '▶' if is_collapsed else '▼' + content_style = ' style="display: none;"' if is_collapsed else '' + + file_word = "file" if tree.count == 1 else "files" + sample_word = "sample" if tree.samples == 1 else "samples" + section_html = f''' +
    +
    + {icon} + {type_names[module_type]} + ({tree.count} {file_word}, {tree.samples:n} {sample_word}) +
    +
    +''' + + # Render root folders + root_folders = sorted(tree.children.items(), + key=lambda x: x[1].samples, reverse=True) + + for folder_name, folder_node in root_folders: + section_html += self._render_folder(folder_node, folder_name, level=1) + + # Render root files (files not in any module) + if tree.files: + sorted_files = sorted(tree.files, key=lambda x: x.total_samples, reverse=True) + section_html += '
    \n' + for stat in sorted_files: + section_html += self._render_file_item(stat, indent=' ') + section_html += '
    \n' + + section_html += '
    \n
    \n' + sections.append(section_html) + + return '\n'.join(sections) + + def _render_folder(self, node: TreeNode, name: str, level: int = 1) -> str: + """Render a single folder node recursively. + + Args: + node: TreeNode to render + name: Display name for the folder + level: Nesting level for indentation + + Returns: + HTML string for this folder and its contents + """ + indent = ' ' * level + parts = [] + + # Render folder header (collapsed by default) + file_word = "file" if node.count == 1 else "files" + sample_word = "sample" if node.samples == 1 else "samples" + parts.append(f'{indent}') + + return '\n'.join(parts) + + def _render_file_item(self, stat: FileStats, indent: str = '') -> str: + """Render a single file item with heatmap bar. + + Args: + stat: FileStats object + indent: Indentation string + + Returns: + HTML string for file item + """ + full_path = html.escape(stat.filename) + module_name = html.escape(stat.module_name) + + intensity = stat.percentage / 100.0 + bar_width = min(stat.percentage, 100) + + html_file = self.file_index[stat.filename] + s = "" if stat.total_samples == 1 else "s" + + return (f'{indent}
    \n' + f'{indent} 📄 {module_name}\n' + f'{indent} {stat.total_samples:n} sample{s}\n' + f'{indent}
    \n' + f'{indent}
    \n') + + +# ============================================================================ +# Main Collector Class +# ============================================================================ + +class HeatmapCollector(StackTraceCollector): + """Collector that generates coverage.py-style heatmap HTML output with line intensity. + + This collector creates detailed HTML reports showing which lines of code + were executed most frequently during profiling, similar to coverage.py + but showing execution "heat" rather than just coverage. + """ + + # File naming and formatting constants + FILE_INDEX_FORMAT = "file_{:04d}.html" + + def __init__(self, *args, **kwargs): + """Initialize the heatmap collector with data structures for analysis.""" + super().__init__(*args, **kwargs) + + # Sample counting data structures + self.line_samples = collections.Counter() + self.file_samples = collections.defaultdict(collections.Counter) + self.line_self_samples = collections.Counter() + self.file_self_samples = collections.defaultdict(collections.Counter) + + # Call graph data structures for navigation (sets for O(1) deduplication) + self.call_graph = collections.defaultdict(set) + self.callers_graph = collections.defaultdict(set) + self.function_definitions = {} + + # Map each sampled line to its function for proper caller lookup + # (filename, lineno) -> funcname + self.line_to_function = {} + + # Edge counting for call path analysis + self.edge_samples = collections.Counter() + + # Bytecode-level tracking data structures + # Track samples per (file, lineno) -> {opcode: {'count': N, 'locations': set()}} + # Locations are deduplicated via set to minimize memory usage + self.line_opcodes = collections.defaultdict(dict) + + # Statistics and metadata + self._total_samples = 0 + self._path_info = get_python_path_info() + self.stats = {} + + # Opcode collection flag + self.opcodes_enabled = False + + # Template loader (loads all templates once) + self._template_loader = _TemplateLoader() + + # File index (populated during export) + self.file_index = {} + + # Reusable set for deduplicating line locations within a single sample. + # This avoids over-counting recursive functions in cumulative stats. + self._seen_lines = set() + + def set_stats(self, sample_interval_usec, duration_sec, sample_rate, error_rate=None, missed_samples=None, **kwargs): + """Set profiling statistics to include in heatmap output. + + Args: + sample_interval_usec: Sampling interval in microseconds + duration_sec: Total profiling duration in seconds + sample_rate: Effective sampling rate + error_rate: Optional error rate during profiling + missed_samples: Optional percentage of missed samples + **kwargs: Additional statistics to include + """ + self.stats = { + "sample_interval_usec": sample_interval_usec, + "duration_sec": duration_sec, + "sample_rate": sample_rate, + "error_rate": error_rate, + "missed_samples": missed_samples, + "python_version": sys.version, + "python_implementation": platform.python_implementation(), + "platform": platform.platform(), + } + self.stats.update(kwargs) + + def process_frames(self, frames, thread_id, weight=1): + """Process stack frames and count samples per line. + + Args: + frames: List of (filename, location, funcname, opcode) tuples in + leaf-to-root order. location is (lineno, end_lineno, col_offset, end_col_offset). + opcode is None if not gathered. + thread_id: Thread ID for this stack trace + weight: Number of samples this stack represents (for batched RLE) + """ + self._total_samples += weight + self._seen_lines.clear() + + for i, (filename, location, funcname, opcode) in enumerate(frames): + # Normalize location to 4-tuple format + lineno, end_lineno, col_offset, end_col_offset = normalize_location(location) + + if not self._is_valid_frame(filename, lineno): + continue + + # frames[0] is the leaf - where execution is actually happening + is_leaf = (i == 0) + line_key = (filename, lineno) + count_cumulative = line_key not in self._seen_lines + if count_cumulative: + self._seen_lines.add(line_key) + + self._record_line_sample(filename, lineno, funcname, is_leaf=is_leaf, + count_cumulative=count_cumulative, weight=weight) + + if opcode is not None: + # Set opcodes_enabled flag when we first encounter opcode data + self.opcodes_enabled = True + self._record_bytecode_sample(filename, lineno, opcode, + end_lineno, col_offset, end_col_offset, + weight=weight) + + # Build call graph for adjacent frames (relationships are deduplicated anyway) + if i + 1 < len(frames): + next_frame = frames[i + 1] + next_lineno = extract_lineno(next_frame[1]) + self._record_call_relationship( + (filename, lineno, funcname), + (next_frame[0], next_lineno, next_frame[2]), + weight=weight, + ) + + def _is_valid_frame(self, filename, lineno): + """Check if a frame should be included in the heatmap.""" + # Skip internal or invalid files + if not filename or filename.startswith('<') or filename.startswith('['): + return False + + # Skip invalid frames with corrupted filename data + if filename == "__init__" and lineno == 0: + return False + + return True + + def _record_line_sample(self, filename, lineno, funcname, is_leaf=False, + count_cumulative=True, weight=1): + """Record a sample for a specific line.""" + # Track cumulative samples (all occurrences in stack) + if count_cumulative: + self.line_samples[(filename, lineno)] += weight + self.file_samples[filename][lineno] += weight + + # Track self/leaf samples (only when at top of stack) + if is_leaf: + self.line_self_samples[(filename, lineno)] += weight + self.file_self_samples[filename][lineno] += weight + + # Record function definition location + if funcname and (filename, funcname) not in self.function_definitions: + self.function_definitions[(filename, funcname)] = lineno + + # Map this line to its function for caller/callee navigation + if funcname: + self.line_to_function[(filename, lineno)] = funcname + + def _record_bytecode_sample(self, filename, lineno, opcode, + end_lineno=None, col_offset=None, end_col_offset=None, + weight=1): + """Record a sample for a specific bytecode instruction. + + Args: + filename: Source filename + lineno: Line number + opcode: Opcode number being executed + end_lineno: End line number (may be -1 if not available) + col_offset: Column offset in UTF-8 bytes (may be -1 if not available) + end_col_offset: End column offset in UTF-8 bytes (may be -1 if not available) + weight: Number of samples this represents (for batched RLE) + """ + key = (filename, lineno) + + # Initialize opcode entry if needed - use set for location deduplication + if opcode not in self.line_opcodes[key]: + self.line_opcodes[key][opcode] = {'count': 0, 'locations': set()} + + self.line_opcodes[key][opcode]['count'] += weight + + # Store unique location info if column offset is available (not -1) + if col_offset is not None and col_offset >= 0: + # Use tuple as set key for deduplication + loc_key = (end_lineno, col_offset, end_col_offset) + self.line_opcodes[key][opcode]['locations'].add(loc_key) + + def _get_bytecode_data_for_line(self, filename, lineno): + """Get bytecode disassembly data for instructions on a specific line. + + Args: + filename: Source filename + lineno: Line number + + Returns: + List of dicts with instruction info, sorted by samples descending + """ + key = (filename, lineno) + opcode_data = self.line_opcodes.get(key, {}) + + result = [] + for opcode, data in opcode_data.items(): + info = get_opcode_info(opcode) + # Handle both old format (int count) and new format (dict with count/locations) + if isinstance(data, dict): + count = data.get('count', 0) + raw_locations = data.get('locations', set()) + # Convert set of tuples to list of dicts for JSON serialization + if isinstance(raw_locations, set): + locations = [ + {'end_lineno': loc[0], 'col_offset': loc[1], 'end_col_offset': loc[2]} + for loc in raw_locations + ] + else: + locations = raw_locations + else: + count = data + locations = [] + + result.append({ + 'opcode': opcode, + 'opname': format_opcode(opcode), + 'base_opname': info['base_opname'], + 'is_specialized': info['is_specialized'], + 'samples': count, + 'locations': locations, + }) + + # Sort by samples descending, then by opcode number + result.sort(key=lambda x: (-x['samples'], x['opcode'])) + return result + + def _record_call_relationship(self, callee_frame, caller_frame, weight=1): + """Record caller/callee relationship between adjacent frames.""" + callee_filename, callee_lineno, callee_funcname = callee_frame + caller_filename, caller_lineno, caller_funcname = caller_frame + + # Skip internal files for call graph + if callee_filename.startswith('<') or callee_filename.startswith('['): + return + + # Get the callee's function definition line + callee_def_line = self.function_definitions.get( + (callee_filename, callee_funcname), callee_lineno + ) + + # Record caller -> callee relationship (set handles deduplication) + caller_key = (caller_filename, caller_lineno) + callee_info = (callee_filename, callee_def_line, callee_funcname) + self.call_graph[caller_key].add(callee_info) + + # Record callee <- caller relationship (set handles deduplication) + callee_key = (callee_filename, callee_def_line) + caller_info = (caller_filename, caller_lineno, caller_funcname) + self.callers_graph[callee_key].add(caller_info) + + # Count this call edge for path analysis + edge_key = (caller_key, callee_key) + self.edge_samples[edge_key] += weight + + def export(self, output_path): + """Export heatmap data as HTML files in a directory. + + Args: + output_path: Path where to create the heatmap output directory + """ + if not self.file_samples: + print("Warning: No heatmap data to export") + return + + try: + output_dir = self._prepare_output_directory(output_path) + file_stats = self._calculate_file_stats() + self._create_file_index(file_stats) + + # Generate individual file reports + self._generate_file_reports(output_dir, file_stats) + + # Generate index page + self._generate_index_html(output_dir / 'index.html', file_stats) + + self._print_export_summary(output_dir, file_stats) + + except Exception as e: + print(f"Error: Failed to export heatmap: {e}") + raise + + def _prepare_output_directory(self, output_path): + """Create output directory for heatmap files.""" + output_dir = Path(output_path) + if output_dir.suffix == '.html': + output_dir = output_dir.with_suffix('') + + try: + output_dir.mkdir(exist_ok=True, parents=True) + except (IOError, OSError) as e: + raise RuntimeError(f"Failed to create output directory {output_dir}: {e}") from e + + return output_dir + + def _create_file_index(self, file_stats: List[FileStats]): + """Create mapping from filenames to HTML file names.""" + self.file_index = { + stat.filename: self.FILE_INDEX_FORMAT.format(i) + for i, stat in enumerate(file_stats) + } + + def _generate_file_reports(self, output_dir, file_stats: List[FileStats]): + """Generate HTML report for each source file.""" + for stat in file_stats: + file_path = output_dir / self.file_index[stat.filename] + line_counts = self.file_samples[stat.filename] + valid_line_counts = {line: count for line, count in line_counts.items() if line >= 0} + + self_counts = self.file_self_samples.get(stat.filename, {}) + valid_self_counts = {line: count for line, count in self_counts.items() if line >= 0} + + self._generate_file_html( + file_path, + stat.filename, + valid_line_counts, + valid_self_counts, + stat + ) + + def _print_export_summary(self, output_dir, file_stats: List[FileStats]): + """Print summary of exported heatmap.""" + print(f"Heatmap output written to {output_dir}/") + print(f" - Index: {output_dir / 'index.html'}") + s = "" if len(file_stats) == 1 else "s" + print(f" - {len(file_stats)} source file{s} analyzed") + + def _calculate_file_stats(self) -> List[FileStats]: + """Calculate statistics for each file. + + Returns: + List of FileStats objects sorted by total samples + """ + file_stats = [] + for filename, line_counts in self.file_samples.items(): + # Skip special frames + if filename in ('~', '...', '.') or filename.startswith('<') or filename.startswith('['): + continue + + # Filter out lines with -1 (special frames) + valid_line_counts = {line: count for line, count in line_counts.items() if line >= 0} + if not valid_line_counts: + continue + + # Get self samples for this file + self_line_counts = self.file_self_samples.get(filename, {}) + valid_self_counts = {line: count for line, count in self_line_counts.items() if line >= 0} + + total_samples = sum(valid_line_counts.values()) + total_self_samples = sum(valid_self_counts.values()) + num_lines = len(valid_line_counts) + max_samples = max(valid_line_counts.values()) + max_self_samples = max(valid_self_counts.values()) if valid_self_counts else 0 + module_name, module_type = extract_module_name(filename, self._path_info) + + file_stats.append(FileStats( + filename=filename, + module_name=module_name, + module_type=module_type, + total_samples=total_samples, + total_self_samples=total_self_samples, + num_lines=num_lines, + max_samples=max_samples, + max_self_samples=max_self_samples, + percentage=0.0 + )) + + # Sort by total samples and calculate percentages + file_stats.sort(key=lambda x: x.total_samples, reverse=True) + if file_stats: + max_total = file_stats[0].total_samples + for stat in file_stats: + stat.percentage = (stat.total_samples / max_total * 100) if max_total > 0 else 0 + + return file_stats + + def _generate_index_html(self, index_path: Path, file_stats: List[FileStats]): + """Generate index.html with list of all profiled files.""" + # Build hierarchical tree + tree = _TreeBuilder.build_file_tree(file_stats) + + # Render tree as HTML + renderer = _HtmlRenderer(self.file_index) + sections_html = renderer.render_hierarchical_html(tree) + + # Format error rate and missed samples with bar classes + error_rate = self.stats.get('error_rate') + if error_rate is not None: + error_rate_str = f"{fmt(error_rate)}%" + error_rate_width = min(error_rate, 100) + # Determine bar color class based on rate + if error_rate < 5: + error_rate_class = "good" + elif error_rate < 15: + error_rate_class = "warning" + else: + error_rate_class = "error" + else: + error_rate_str = "N/A" + error_rate_width = 0 + error_rate_class = "good" + + missed_samples = self.stats.get('missed_samples') + if missed_samples is not None: + missed_samples_str = f"{fmt(missed_samples)}%" + missed_samples_width = min(missed_samples, 100) + if missed_samples < 5: + missed_samples_class = "good" + elif missed_samples < 15: + missed_samples_class = "warning" + else: + missed_samples_class = "error" + else: + missed_samples_str = "N/A" + missed_samples_width = 0 + missed_samples_class = "good" + + # Populate template + replacements = { + "": f"", + "": f"", + "": self._template_loader.logo_html, + "": f"{sys.version_info.major}.{sys.version_info.minor}", + "": f"{len(file_stats):n}", + "": f"{self._total_samples:n}", + "": fmt(self.stats.get('duration_sec', 0)), + "": fmt(self.stats.get('sample_rate', 0)), + "": error_rate_str, + "": str(error_rate_width), + "": error_rate_class, + "": missed_samples_str, + "": str(missed_samples_width), + "": missed_samples_class, + "": sections_html, + } + + html_content = self._template_loader.index_template + for placeholder, value in replacements.items(): + html_content = html_content.replace(placeholder, value) + + try: + index_path.write_text(html_content, encoding='utf-8') + except (IOError, OSError) as e: + raise RuntimeError(f"Failed to write index file {index_path}: {e}") from e + + def _generate_file_html(self, output_path: Path, filename: str, + line_counts: Dict[int, int], self_counts: Dict[int, int], + file_stat: FileStats): + """Generate HTML for a single source file with heatmap coloring.""" + # Read source file + try: + source_lines = Path(filename).read_text(encoding='utf-8', errors='replace').splitlines() + except (IOError, OSError) as e: + if not (filename.startswith('<') or filename.startswith('[') or + filename in ('~', '...', '.') or len(filename) < 2): + print(f"Warning: Could not read source file {filename}: {e}") + source_lines = [f"# Source file not available: {filename}"] + + # Generate HTML for each line + max_samples = max(line_counts.values()) if line_counts else 1 + max_self_samples = max(self_counts.values()) if self_counts else 1 + code_lines_html = [ + self._build_line_html(line_num, line_content, line_counts, self_counts, + max_samples, max_self_samples, filename) + for line_num, line_content in enumerate(source_lines, start=1) + ] + + # Populate template + replacements = { + "": html.escape(filename), + "": f"{file_stat.total_samples:n}", + "": f"{file_stat.total_self_samples:n}", + "": f"{file_stat.num_lines:n}", + "": fmt(file_stat.percentage, 2), + "": f"{file_stat.max_samples:n}", + "": f"{file_stat.max_self_samples:n}", + "": ''.join(code_lines_html), + "": f"", + "": f"", + "": self._template_loader.logo_html, + "": f"{sys.version_info.major}.{sys.version_info.minor}", + } + + html_content = self._template_loader.file_template + for placeholder, value in replacements.items(): + html_content = html_content.replace(placeholder, value) + + try: + output_path.write_text(html_content, encoding='utf-8') + except (IOError, OSError) as e: + raise RuntimeError(f"Failed to write file {output_path}: {e}") from e + + def _build_line_html(self, line_num: int, line_content: str, + line_counts: Dict[int, int], self_counts: Dict[int, int], + max_samples: int, max_self_samples: int, filename: str) -> str: + """Build HTML for a single line of source code.""" + cumulative_samples = line_counts.get(line_num, 0) + self_samples = self_counts.get(line_num, 0) + + # Calculate colors for both self and cumulative modes + if cumulative_samples > 0: + log_cumulative = math.log(cumulative_samples + 1) + log_max = math.log(max_samples + 1) + cumulative_intensity = log_cumulative / log_max if log_max > 0 else 0 + + if self_samples > 0 and max_self_samples > 0: + log_self = math.log(self_samples + 1) + log_max_self = math.log(max_self_samples + 1) + self_intensity = log_self / log_max_self if log_max_self > 0 else 0 + else: + self_intensity = 0 + + self_display = f"{self_samples:n}" if self_samples > 0 else "" + cumulative_display = f"{cumulative_samples:n}" + tooltip = f"Self: {self_samples:n}, Total: {cumulative_samples:n}" + else: + cumulative_intensity = 0 + self_intensity = 0 + self_display = "" + cumulative_display = "" + tooltip = "" + + # Get bytecode data for this line (if any) + bytecode_data = self._get_bytecode_data_for_line(filename, line_num) + has_bytecode = len(bytecode_data) > 0 and cumulative_samples > 0 + + # Build bytecode toggle button if data is available + bytecode_btn_html = '' + bytecode_panel_html = '' + if has_bytecode: + bytecode_json = html.escape(json.dumps(bytecode_data)) + + # Calculate specialization percentage + total_samples = sum(d['samples'] for d in bytecode_data) + specialized_samples = sum(d['samples'] for d in bytecode_data if d['is_specialized']) + spec_pct = int(100 * specialized_samples / total_samples) if total_samples > 0 else 0 + + bytecode_btn_html = ( + f'' + ) + # Wrapper contains columns + content panel + bytecode_panel_html = ( + f'
    \n' + f'
    ' + f'
    ' + f'
    ' + f'
    ' + f'
    \n' + f'
    \n' + f'
    \n' + ) + elif self.opcodes_enabled: + # Add invisible spacer to maintain consistent indentation when opcodes are enabled + bytecode_btn_html = '
    ' + + # Get navigation buttons + nav_buttons_html = self._build_navigation_buttons(filename, line_num) + + # Build line HTML with instruction highlights if available + line_html = self._render_source_with_highlights(line_content, line_num, + filename, bytecode_data) + title_attr = f' title="{html.escape(tooltip)}"' if tooltip else "" + + # Specialization color for toggle mode (green gradient based on spec %) + spec_color_attr = '' + if has_bytecode: + spec_color = self._format_specialization_color(spec_pct) + spec_color_attr = f'data-spec-color="{spec_color}" ' + + return ( + f'
    \n' + f'
    {line_num}
    \n' + f'
    {self_display}
    \n' + f'
    {cumulative_display}
    \n' + f' {bytecode_btn_html}\n' + f'
    {line_html}
    \n' + f' {nav_buttons_html}\n' + f'
    \n' + f'{bytecode_panel_html}' + ) + + def _render_source_with_highlights(self, line_content: str, line_num: int, + filename: str, bytecode_data: list) -> str: + """Render source line with instruction highlight spans. + + Simple: collect ranges with sample counts, assign each byte position to + smallest covering range, then emit spans for contiguous runs with sample data. + """ + content = line_content.rstrip('\n') + if not content: + return '' + + # Collect all (start, end) -> {samples, opcodes} mapping from instructions + # Multiple instructions may share the same range, so we sum samples and collect opcodes + range_data = {} + for instr in bytecode_data: + samples = instr.get('samples', 0) + opname = instr.get('opname', '') + for loc in instr.get('locations', []): + if loc.get('end_lineno', line_num) == line_num: + start, end = loc.get('col_offset', -1), loc.get('end_col_offset', -1) + if start >= 0 and end >= 0: + key = (start, end) + if key not in range_data: + range_data[key] = {'samples': 0, 'opcodes': []} + range_data[key]['samples'] += samples + if opname and opname not in range_data[key]['opcodes']: + range_data[key]['opcodes'].append(opname) + + if not range_data: + return html.escape(content) + + # For each byte position, find the smallest covering range + byte_to_range = {} + for (start, end) in range_data.keys(): + for pos in range(start, end): + if pos not in byte_to_range: + byte_to_range[pos] = (start, end) + else: + # Keep smaller range + old_start, old_end = byte_to_range[pos] + if (end - start) < (old_end - old_start): + byte_to_range[pos] = (start, end) + + # Calculate totals for percentage and intensity + total_line_samples = sum(d['samples'] for d in range_data.values()) + max_range_samples = max(d['samples'] for d in range_data.values()) if range_data else 1 + + # Render character by character + result = [] + byte_offset = 0 + char_idx = 0 + current_range = None + span_chars = [] + + def flush_span(): + nonlocal span_chars, current_range + if span_chars: + text = html.escape(''.join(span_chars)) + if current_range: + data = range_data.get(current_range, {'samples': 0, 'opcodes': []}) + samples = data['samples'] + opcodes = ', '.join(data['opcodes'][:3]) # Top 3 opcodes + if len(data['opcodes']) > 3: + opcodes += f" +{len(data['opcodes']) - 3} more" + pct = int(100 * samples / total_line_samples) if total_line_samples > 0 else 0 + result.append(f'{text}') + else: + result.append(text) + span_chars = [] + + while char_idx < len(content): + char = content[char_idx] + char_bytes = len(char.encode('utf-8')) + char_range = byte_to_range.get(byte_offset) + + if char_range != current_range: + flush_span() + current_range = char_range + + span_chars.append(char) + byte_offset += char_bytes + char_idx += 1 + + flush_span() + return ''.join(result) + + def _format_specialization_color(self, spec_pct: int) -> str: + """Format specialization color based on percentage. + + Uses a gradient from gray (0%) through orange (50%) to green (100%). + """ + # Normalize to 0-1 + ratio = spec_pct / 100.0 + + if ratio >= 0.5: + # Orange to green (50-100%) + t = (ratio - 0.5) * 2 # 0 to 1 + r = int(255 * (1 - t)) # 255 -> 0 + g = int(180 + 75 * t) # 180 -> 255 + b = int(50 * (1 - t)) # 50 -> 0 + else: + # Gray to orange (0-50%) + t = ratio * 2 # 0 to 1 + r = int(158 + 97 * t) # 158 -> 255 + g = int(158 + 22 * t) # 158 -> 180 + b = int(158 - 108 * t) # 158 -> 50 + + alpha = 0.15 + 0.25 * ratio # 0.15 to 0.4 + return f"rgba({r}, {g}, {b}, {alpha})" + + def _build_navigation_buttons(self, filename: str, line_num: int) -> str: + """Build navigation buttons for callers/callees. + + - Callers: All lines in a function show who calls this function + - Callees: Only actual call site lines show what they call + """ + line_key = (filename, line_num) + + funcname = self.line_to_function.get(line_key) + + # Get callers: look up by function definition line, not current line + # This ensures all lines in a function show who calls this function + if funcname: + func_def_line = self.function_definitions.get((filename, funcname), line_num) + func_def_key = (filename, func_def_line) + caller_list = self._deduplicate_by_function(self.callers_graph.get(func_def_key, set())) + else: + caller_list = self._deduplicate_by_function(self.callers_graph.get(line_key, set())) + + # Get callees: only show for actual call site lines (not every line in function) + callee_list = self._deduplicate_by_function(self.call_graph.get(line_key, set())) + + # Get edge counts for each caller/callee + # For callers, use the function definition key for edge lookup + if funcname: + func_def_line = self.function_definitions.get((filename, funcname), line_num) + caller_edge_key = (filename, func_def_line) + else: + caller_edge_key = line_key + callers_with_counts = self._get_edge_counts(caller_edge_key, caller_list, is_caller=True) + # For callees, use the actual line key since that's where the call happens + callees_with_counts = self._get_edge_counts(line_key, callee_list, is_caller=False) + + # Build navigation buttons with counts + caller_btn = self._create_navigation_button(callers_with_counts, 'caller', '▲') + callee_btn = self._create_navigation_button(callees_with_counts, 'callee', '▼') + + if caller_btn or callee_btn: + return f'
    {caller_btn}{callee_btn}
    ' + return '' + + def _get_edge_counts(self, line_key: Tuple[str, int], + items: List[Tuple[str, int, str]], + is_caller: bool) -> List[Tuple[str, int, str, int]]: + """Get sample counts for each caller/callee edge.""" + result = [] + for file, line, func in items: + edge_line_key = (file, line) + if is_caller: + edge_key = (edge_line_key, line_key) + else: + edge_key = (line_key, edge_line_key) + + count = self.edge_samples.get(edge_key, 0) + result.append((file, line, func, count)) + + result.sort(key=lambda x: x[3], reverse=True) + return result + + def _deduplicate_by_function(self, items) -> List[Tuple[str, int, str]]: + """Remove duplicate entries based on (file, function) key. + + Args: + items: Iterable of (file, line, func) tuples (set or list) + """ + seen = {} + result = [] + for file, line, func in items: + key = (file, func) + if key not in seen: + seen[key] = True + result.append((file, line, func)) + return result + + def _create_navigation_button(self, items_with_counts: List[Tuple[str, int, str, int]], + btn_class: str, arrow: str) -> str: + """Create HTML for a navigation button with sample counts.""" + # Filter valid items + valid_items = [(f, l, fn, cnt) for f, l, fn, cnt in items_with_counts + if f in self.file_index and l > 0] + if not valid_items: + return "" + + if len(valid_items) == 1: + file, line, func, count = valid_items[0] + target_html = self.file_index[file] + nav_data = json.dumps({'link': f"{target_html}#line-{line}", 'func': func}) + title = f"Go to {btn_class}: {html.escape(func)} ({count:n} samples)" + return f'' + + # Multiple items - create menu + total_samples = sum(cnt for _, _, _, cnt in valid_items) + items_data = [ + { + 'file': os.path.basename(file), + 'func': func, + 'count': count, + 'link': f"{self.file_index[file]}#line-{line}" + } + for file, line, func, count in valid_items + ] + items_json = html.escape(json.dumps(items_data)) + title = f"{len(items_data)} {btn_class}s ({total_samples:n} samples)" + return f'' diff --git a/Lib/profiling/sampling/jsonl_collector.py b/Lib/profiling/sampling/jsonl_collector.py new file mode 100644 index 00000000000000..5aa42ef09024dc --- /dev/null +++ b/Lib/profiling/sampling/jsonl_collector.py @@ -0,0 +1,267 @@ +"""JSON Lines (JSONL) collector for the sampling profiler. + +Emits a normalized newline-delimited JSON record stream suitable for +programmatic consumption by external tools, scripts, and agents. Each line +is one JSON object; consumers can parse the file incrementally line by +line, but the producer writes the whole file at the end of the run (it is +not a live/streaming producer). + +Record schema +============= + +Every record is a JSON object with at least ``"type"``, ``"v"`` (record +schema version), and ``"run_id"`` (UUID4 hex tagging the run; allows +demultiplexing concatenated streams). Records appear in this fixed order: + +1. ``meta`` (exactly one, first line):: + + {"type":"meta","v":0,"run_id":"", + "sample_interval_usec":,"mode":"wall|cpu|gil|all|exception"} + + ``mode`` is omitted when not provided. + +2. ``string_table`` (zero or more):: + + {"type":"string_table","v":0,"run_id":"", + "strings":[{"str_id":,"value":""}, ...]} + + Strings (filenames, function names) are interned to keep repeated values + compact. IDs are zero-based. Each chunk holds up to ``_CHUNK_SIZE`` + entries, and each entry carries its explicit ``str_id`` so consumers do + not need to infer offsets across chunks. + +3. ``frame_table`` (zero or more):: + + {"type":"frame_table","v":0,"run_id":"", + "frames":[{"frame_id":,"path_str_id":,"func_str_id":, + "line":,"end_line":,"col":, + "end_col":}, ...]} + + ``end_line``/``col``/``end_col`` are *omitted* when source location data + is unavailable (a missing key means "not available", not zero or null). + ``line`` is ``0`` for synthetic frames (for example, internal marker + frames whose source location is None). Frame IDs are zero-based. + +4. ``agg`` (zero or more):: + + {"type":"agg","v":0,"run_id":"","kind":"frame","scope":"final", + "samples_total":, + "entries":[{"frame_id":,"self":,"cumulative":}, ...]} + + ``self`` counts samples where the frame was the leaf (currently + executing); ``cumulative`` counts samples where the frame appeared + anywhere in the stack (deduped per sample so recursion does not + double-count). ``samples_total`` is the run-wide total, repeated on + each chunk so a streaming consumer always knows the denominator. + +5. ``end`` (exactly one, last line):: + + {"type":"end","v":0,"run_id":"","samples_total":} + + Presence of ``end`` is the consumer's signal that the file is complete. + +Forward compatibility +===================== + +Consumers MUST ignore unknown record ``"type"`` values and unknown object +fields. New fields will be added by adding optional keys; an incompatible +schema change will bump the per-record ``"v"``. +""" + +from collections import Counter +import json +import uuid +from itertools import batched + +from .constants import PROFILING_MODE_NAMES +from .collector import normalize_location +from .stack_collector import StackTraceCollector + + +_CHUNK_SIZE = 256 +_SCHEMA_VERSION = 0 + + +class JsonlCollector(StackTraceCollector): + """Collector that exports finalized profiling data as JSONL. + + See the module docstring for the full record schema. The collector + accumulates samples in memory and writes the complete file at + ``export()`` time. + """ + + def __init__(self, sample_interval_usec, *, skip_idle=False, mode=None): + super().__init__(sample_interval_usec, skip_idle=skip_idle) + self.run_id = uuid.uuid4().hex + + self._string_to_id = {} + self._strings = [] + + self._frame_to_id = {} + self._frames = [] + + self._frame_self = Counter() + self._frame_cumulative = Counter() + self._samples_total = 0 + self._seen_frame_ids = set() + + self._mode = mode + + def process_frames(self, frames, _thread_id, weight=1): + self._samples_total += weight + self._seen_frame_ids.clear() + + for i, (filename, location, funcname, _opcode) in enumerate(frames): + frame_id = self._get_or_create_frame_id( + filename, location, funcname + ) + is_leaf = i == 0 + count_cumulative = frame_id not in self._seen_frame_ids + + if count_cumulative: + self._seen_frame_ids.add(frame_id) + + if is_leaf: + self._frame_self[frame_id] += weight + + if count_cumulative: + self._frame_cumulative[frame_id] += weight + + def export(self, filename): + with open(filename, "w", encoding="utf-8") as output: + self._write_message(output, self._build_meta_record()) + self._write_chunked_records( + output, + { + "type": "string_table", + "v": _SCHEMA_VERSION, + "run_id": self.run_id, + }, + "strings", + self._strings, + ) + self._write_chunked_records( + output, + { + "type": "frame_table", + "v": _SCHEMA_VERSION, + "run_id": self.run_id, + }, + "frames", + self._frames, + ) + self._write_chunked_records( + output, + { + "type": "agg", + "v": _SCHEMA_VERSION, + "run_id": self.run_id, + "kind": "frame", + "scope": "final", + "samples_total": self._samples_total, + }, + "entries", + self._iter_final_agg_entries(), + ) + self._write_message(output, self._build_end_record()) + print(f"JSONL profile written to {filename}") + + def _build_meta_record(self): + record = { + "type": "meta", + "v": _SCHEMA_VERSION, + "run_id": self.run_id, + "sample_interval_usec": self.sample_interval_usec, + } + + if self._mode is not None: + record["mode"] = PROFILING_MODE_NAMES.get( + self._mode, str(self._mode) + ) + + return record + + def _build_end_record(self): + record = { + "type": "end", + "v": _SCHEMA_VERSION, + "run_id": self.run_id, + "samples_total": self._samples_total, + } + + return record + + def _iter_final_agg_entries(self): + for frame_record in self._frames: + frame_id = frame_record["frame_id"] + yield { + "frame_id": frame_id, + "self": self._frame_self[frame_id], + "cumulative": self._frame_cumulative[frame_id], + } + + def _get_or_create_frame_id(self, filename, location, funcname): + location_fields = self._location_to_export_fields(location) + func_str_id = self._intern_string(funcname) + path_str_id = self._intern_string(filename) + + frame_key = ( + path_str_id, + func_str_id, + location_fields["line"], + location_fields.get("end_line"), + location_fields.get("col"), + location_fields.get("end_col"), + ) + + if (frame_id := self._frame_to_id.get(frame_key)) is not None: + return frame_id + + frame_id = len(self._frames) + frame_record = { + "frame_id": frame_id, + "path_str_id": path_str_id, + "func_str_id": func_str_id, + **location_fields, + } + + self._frame_to_id[frame_key] = frame_id + self._frames.append(frame_record) + return frame_id + + def _intern_string(self, value): + value = str(value) + + if (string_id := self._string_to_id.get(value)) is not None: + return string_id + + string_id = len(self._strings) + self._string_to_id[value] = string_id + self._strings.append({"str_id": string_id, "value": value}) + return string_id + + @staticmethod + def _location_to_export_fields(location): + lineno, end_lineno, col_offset, end_col_offset = normalize_location( + location + ) + + fields = {"line": lineno} + if end_lineno > 0: + fields["end_line"] = end_lineno + if col_offset >= 0: + fields["col"] = col_offset + if end_col_offset >= 0: + fields["end_col"] = end_col_offset + return fields + + def _write_chunked_records( + self, output, base_record, chunk_field, entries + ): + for chunk in batched(entries, _CHUNK_SIZE): + self._write_message(output, {**base_record, chunk_field: chunk}) + + @staticmethod + def _write_message(output, record): + output.write(json.dumps(record, separators=(",", ":"))) + output.write("\n") diff --git a/Lib/profiling/sampling/live_collector/__init__.py b/Lib/profiling/sampling/live_collector/__init__.py new file mode 100644 index 00000000000000..59d50955e52959 --- /dev/null +++ b/Lib/profiling/sampling/live_collector/__init__.py @@ -0,0 +1,200 @@ +"""Live profiling collector that displays top-like statistics using curses. + + ┌─────────────────────────────┐ + │ Target Python Process │ + │ (being profiled) │ + └──────────────┬──────────────┘ + │ Stack sampling at + │ configured interval + │ (e.g., 10000µs) + ▼ + ┌─────────────────────────────┐ + │ LiveStatsCollector │ + │ ┌───────────────────────┐ │ + │ │ collect() │ │ Aggregates samples + │ │ - Iterates frames │ │ into statistics + │ │ - Updates counters │ │ + │ └───────────┬───────────┘ │ + │ │ │ + │ ▼ │ + │ ┌───────────────────────┐ │ + │ │ Data Storage │ │ + │ │ - result dict │ │ Tracks per-function: + │ │ - direct_calls │ │ • Direct samples + │ │ - cumulative_calls │ │ • Cumulative samples + │ └───────────┬───────────┘ │ • Derived time stats + │ │ │ + │ ▼ │ + │ ┌───────────────────────┐ │ + │ │ Display Update │ │ + │ │ (10Hz by default) │ │ Rate-limited refresh + │ └───────────┬───────────┘ │ + └──────────────┼──────────────┘ + │ + ▼ + ┌─────────────────────────────┐ + │ DisplayInterface │ + │ (Abstract layer) │ + └──────────────┬──────────────┘ + ┌───────┴────────┐ + │ │ + ┌──────────▼────────┐ ┌───▼──────────┐ + │ CursesDisplay │ │ MockDisplay │ + │ - Real terminal │ │ - Testing │ + │ - ncurses backend │ │ - No UI │ + └─────────┬─────────┘ └──────────────┘ + │ + ▼ + ┌─────────────────────────────────────┐ + │ Widget-Based Rendering │ + │ ┌─────────────────────────────────┐ │ + │ │ HeaderWidget │ │ + │ │ • PID, uptime, time, interval │ │ + │ │ • Sample stats & progress bar │ │ + │ │ • Efficiency bar │ │ + │ │ • Thread status & GC stats │ │ + │ │ • Function summary │ │ + │ │ • Top 3 hottest functions │ │ + │ ├─────────────────────────────────┤ │ + │ │ TableWidget │ │ + │ │ • Column headers (sortable) │ │ Interactive display + │ │ • Stats rows (scrolling) │ │ with keyboard controls: + │ │ - nsamples % time │ │ s: sort, p: pause + │ │ - function file:line │ │ r: reset, /: filter + │ ├─────────────────────────────────┤ │ q: quit, h: help + │ │ FooterWidget │ │ + │ │ • Legend and status │ │ + │ │ • Filter input prompt │ │ + │ └─────────────────────────────────┘ │ + └─────────────────────────────────────┘ + +Architecture: + +The live collector is organized into four layers. The data collection layer +(LiveStatsCollector) aggregates stack samples into per-function statistics without +any knowledge of how they will be presented. The display abstraction layer +(DisplayInterface) defines rendering operations without coupling to curses or any +specific UI framework. The widget layer (Widget, HeaderWidget, TableWidget, +FooterWidget, HelpWidget, ProgressBarWidget) encapsulates individual UI components +with their own rendering logic, promoting modularity and reusability. The +presentation layer (CursesDisplay/MockDisplay) implements the actual rendering for +terminal output and testing. + +The system runs two independent update loops. The sampling loop is driven by the +profiler at the configured interval (e.g., 10000µs) and continuously collects +stack frames and updates statistics. The display loop runs at a fixed refresh rate +(default 10Hz) and updates the terminal independently of sampling frequency. This +separation allows high-frequency sampling without overwhelming the terminal with +constant redraws. + +Statistics are computed incrementally as samples arrive. The collector maintains +running counters (direct calls and cumulative calls) in a dictionary keyed by +function location. Derived metrics like time estimates and percentages are computed +on-demand during display updates rather than being stored, which minimizes memory +overhead as the number of tracked functions grows. + +User input is processed asynchronously during display updates using non-blocking I/O. +This allows interactive controls (sorting, filtering, pausing) without interrupting +the data collection pipeline. The collector maintains mode flags (paused, +filter_input_mode) that affect what gets displayed but not what gets collected. + +""" + +# Re-export all public classes and constants for backward compatibility +from .collector import LiveStatsCollector +from .display import DisplayInterface, CursesDisplay, MockDisplay +from .widgets import ( + Widget, + ProgressBarWidget, + HeaderWidget, + TableWidget, + FooterWidget, + HelpWidget, +) +from .constants import ( + MICROSECONDS_PER_SECOND, + DISPLAY_UPDATE_HZ, + DISPLAY_UPDATE_INTERVAL_SEC, + MIN_TERMINAL_WIDTH, + MIN_TERMINAL_HEIGHT, + WIDTH_THRESHOLD_SAMPLE_PCT, + WIDTH_THRESHOLD_TOTTIME, + WIDTH_THRESHOLD_CUMUL_PCT, + WIDTH_THRESHOLD_CUMTIME, + HEADER_LINES, + FOOTER_LINES, + SAFETY_MARGIN, + TOP_FUNCTIONS_DISPLAY_COUNT, + COL_WIDTH_NSAMPLES, + COL_SPACING, + COL_WIDTH_SAMPLE_PCT, + COL_WIDTH_TIME, + MIN_FUNC_NAME_WIDTH, + MAX_FUNC_NAME_WIDTH, + MIN_AVAILABLE_SPACE, + MIN_BAR_WIDTH, + MAX_SAMPLE_RATE_BAR_WIDTH, + MAX_EFFICIENCY_BAR_WIDTH, + MIN_SAMPLE_RATE_FOR_SCALING, + FINISHED_BANNER_EXTRA_LINES, + COLOR_PAIR_HEADER_BG, + COLOR_PAIR_CYAN, + COLOR_PAIR_YELLOW, + COLOR_PAIR_GREEN, + COLOR_PAIR_MAGENTA, + COLOR_PAIR_RED, + COLOR_PAIR_SORTED_HEADER, + DEFAULT_SORT_BY, + DEFAULT_DISPLAY_LIMIT, +) + +__all__ = [ + # Main collector + "LiveStatsCollector", + # Display interfaces + "DisplayInterface", + "CursesDisplay", + "MockDisplay", + # Widgets + "Widget", + "ProgressBarWidget", + "HeaderWidget", + "TableWidget", + "FooterWidget", + "HelpWidget", + # Constants + "MICROSECONDS_PER_SECOND", + "DISPLAY_UPDATE_HZ", + "DISPLAY_UPDATE_INTERVAL_SEC", + "MIN_TERMINAL_WIDTH", + "MIN_TERMINAL_HEIGHT", + "WIDTH_THRESHOLD_SAMPLE_PCT", + "WIDTH_THRESHOLD_TOTTIME", + "WIDTH_THRESHOLD_CUMUL_PCT", + "WIDTH_THRESHOLD_CUMTIME", + "HEADER_LINES", + "FOOTER_LINES", + "SAFETY_MARGIN", + "TOP_FUNCTIONS_DISPLAY_COUNT", + "COL_WIDTH_NSAMPLES", + "COL_SPACING", + "COL_WIDTH_SAMPLE_PCT", + "COL_WIDTH_TIME", + "MIN_FUNC_NAME_WIDTH", + "MAX_FUNC_NAME_WIDTH", + "MIN_AVAILABLE_SPACE", + "MIN_BAR_WIDTH", + "MAX_SAMPLE_RATE_BAR_WIDTH", + "MAX_EFFICIENCY_BAR_WIDTH", + "MIN_SAMPLE_RATE_FOR_SCALING", + "FINISHED_BANNER_EXTRA_LINES", + "COLOR_PAIR_HEADER_BG", + "COLOR_PAIR_CYAN", + "COLOR_PAIR_YELLOW", + "COLOR_PAIR_GREEN", + "COLOR_PAIR_MAGENTA", + "COLOR_PAIR_RED", + "COLOR_PAIR_SORTED_HEADER", + "DEFAULT_SORT_BY", + "DEFAULT_DISPLAY_LIMIT", +] diff --git a/Lib/profiling/sampling/live_collector/collector.py b/Lib/profiling/sampling/live_collector/collector.py new file mode 100644 index 00000000000000..a53cfc6b719a10 --- /dev/null +++ b/Lib/profiling/sampling/live_collector/collector.py @@ -0,0 +1,1111 @@ +"""LiveStatsCollector - Main collector class for live profiling.""" + +import collections +import contextlib +import curses +from dataclasses import dataclass, field +import os +import site +import sys +import sysconfig +import time +lazy import _colorize + +from ..collector import Collector, extract_lineno +from ..constants import ( + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, + THREAD_STATUS_UNKNOWN, + THREAD_STATUS_GIL_REQUESTED, + THREAD_STATUS_HAS_EXCEPTION, + PROFILING_MODE_CPU, + PROFILING_MODE_GIL, + PROFILING_MODE_WALL, +) +from .constants import ( + MICROSECONDS_PER_SECOND, + DISPLAY_UPDATE_INTERVAL_SEC, + MIN_TERMINAL_WIDTH, + MIN_TERMINAL_HEIGHT, + HEADER_LINES, + FOOTER_LINES, + SAFETY_MARGIN, + FINISHED_BANNER_EXTRA_LINES, + DEFAULT_SORT_BY, + DEFAULT_DISPLAY_LIMIT, + COLOR_PAIR_SAMPLES, + COLOR_PAIR_FILE, + COLOR_PAIR_FUNC, + COLOR_PAIR_HEADER_BG, + COLOR_PAIR_CYAN, + COLOR_PAIR_YELLOW, + COLOR_PAIR_GREEN, + COLOR_PAIR_MAGENTA, + COLOR_PAIR_RED, + COLOR_PAIR_SORTED_HEADER, +) +from .display import CursesDisplay +from .widgets import HeaderWidget, TableWidget, FooterWidget, HelpWidget, OpcodePanel +from .trend_tracker import TrendTracker + + +@dataclass +class ThreadData: + """Encapsulates all profiling data for a single thread.""" + + thread_id: int + + # Function call statistics: {location: {direct_calls: int, cumulative_calls: int}} + result: dict = field(default_factory=lambda: collections.defaultdict( + lambda: dict(direct_calls=0, cumulative_calls=0) + )) + + # Thread status statistics + has_gil: int = 0 + on_cpu: int = 0 + gil_requested: int = 0 + unknown: int = 0 + has_exception: int = 0 + total: int = 0 # Total status samples for this thread + + # Sample counts + sample_count: int = 0 + gc_frame_samples: int = 0 + + # Opcode statistics: {location: {opcode: count}} + opcode_stats: dict = field(default_factory=lambda: collections.defaultdict( + lambda: collections.defaultdict(int) + )) + + def increment_status_flag(self, status_flags): + """Update status counts based on status bit flags.""" + if status_flags & THREAD_STATUS_HAS_GIL: + self.has_gil += 1 + if status_flags & THREAD_STATUS_ON_CPU: + self.on_cpu += 1 + if status_flags & THREAD_STATUS_GIL_REQUESTED: + self.gil_requested += 1 + if status_flags & THREAD_STATUS_UNKNOWN: + self.unknown += 1 + if status_flags & THREAD_STATUS_HAS_EXCEPTION: + self.has_exception += 1 + self.total += 1 + + def as_status_dict(self): + """Return status counts as a dict for compatibility.""" + return { + "has_gil": self.has_gil, + "on_cpu": self.on_cpu, + "gil_requested": self.gil_requested, + "unknown": self.unknown, + "has_exception": self.has_exception, + "total": self.total, + } + + +class LiveStatsCollector(Collector): + """Collector that displays live top-like statistics using ncurses.""" + + def __init__( + self, + sample_interval_usec, + *, + skip_idle=False, + sort_by=DEFAULT_SORT_BY, + limit=DEFAULT_DISPLAY_LIMIT, + pid=None, + display=None, + mode=None, + opcodes=False, + async_aware=None, + ): + """ + Initialize the live stats collector. + + Args: + sample_interval_usec: Sampling interval in microseconds + skip_idle: Whether to skip idle threads + sort_by: Sort key ('tottime', 'nsamples', 'cumtime', 'sample_pct', 'cumul_pct') + limit: Maximum number of functions to display + pid: Process ID being profiled + display: DisplayInterface implementation (None means curses will be used) + mode: Profiling mode ('cpu', 'gil', etc.) - affects what stats are shown + opcodes: Whether to show opcode panel (requires --opcodes flag) + async_aware: Async tracing mode - None (sync only), "all" or "running" + """ + self.result = collections.defaultdict( + lambda: dict(total_rec_calls=0, direct_calls=0, cumulative_calls=0) + ) + self.sample_interval_usec = sample_interval_usec + self.sample_interval_sec = ( + sample_interval_usec / MICROSECONDS_PER_SECOND + ) + self.skip_idle = skip_idle + self.sort_by = sort_by + self.limit = limit + self.total_samples = 0 + self.start_time = None + self.stdscr = None + self.display = display # DisplayInterface implementation + self.running = True + self.pid = pid + self.mode = mode # Profiling mode + self.async_aware = async_aware # Async tracing mode + # Pre-select frame iterator method to avoid per-call dispatch overhead + self._get_frame_iterator = self._get_async_frame_iterator if async_aware else self._get_sync_frame_iterator + self._saved_stdout = None + self._saved_stderr = None + self._devnull = None + self._last_display_update = None + self.max_sample_rate = 0 # Track maximum sample rate seen + self.successful_samples = 0 # Track samples that captured frames + self.failed_samples = 0 # Track samples that failed to capture frames + self.display_update_interval_sec = DISPLAY_UPDATE_INTERVAL_SEC # Instance variable for display refresh rate + + # Thread status statistics (bit flags) + self.thread_status_counts = { + "has_gil": 0, + "on_cpu": 0, + "gil_requested": 0, + "unknown": 0, + "has_exception": 0, + "total": 0, # Total thread count across all samples + } + self.gc_frame_samples = 0 # Track samples with GC frames + + # Opcode statistics: {location: {opcode: count}} + self.opcode_stats = collections.defaultdict(lambda: collections.defaultdict(int)) + self.show_opcodes = opcodes # Show opcode panel when --opcodes flag is passed + self.selected_row = 0 # Currently selected row in table for opcode view + self.scroll_offset = 0 # Scroll offset for table when in opcode mode + + # Interactive controls state + self.paused = False # Pause UI updates (profiling continues) + self.show_help = False # Show help screen + self.filter_pattern = None # Glob pattern to filter functions + self.filter_input_mode = False # Currently entering filter text + self.filter_input_buffer = "" # Buffer for filter input + self.finished = False # Program has finished, showing final state + self.finish_timestamp = None # When profiling finished (for time freezing) + self.finish_wall_time = None # Wall clock time when profiling finished + + # Thread tracking state + self.thread_ids = [] # List of thread IDs seen + self.view_mode = "ALL" # "ALL" or "PER_THREAD" + self.current_thread_index = ( + 0 # Index into thread_ids when in PER_THREAD mode + ) + self.per_thread_data = {} # {thread_id: ThreadData} + + # Calculate common path prefixes to strip + self._path_prefixes = self._get_common_path_prefixes() + + # Widgets (initialized when display is available) + self.header_widget = None + self.table_widget = None + self.footer_widget = None + self.help_widget = None + self.opcode_panel = None + + # Color mode + self._can_colorize = _colorize.can_colorize() + + # Trend tracking (initialized after colors are set up) + self._trend_tracker = None + + self._seen_locations = set() + + @property + def elapsed_time(self): + """Get the elapsed time, frozen when finished.""" + if self.finished and self.finish_timestamp is not None: + # Handle case where process exited before any samples were collected + if self.start_time is None: + return 0 + return self.finish_timestamp - self.start_time + return time.perf_counter() - self.start_time if self.start_time else 0 + + @property + def current_time_display(self): + """Get the current time for display, frozen when finished.""" + if self.finished and self.finish_wall_time is not None: + return time.strftime("%H:%M:%S", time.localtime(self.finish_wall_time)) + return time.strftime("%H:%M:%S") + + def _get_or_create_thread_data(self, thread_id): + """Get or create ThreadData for a thread ID.""" + if thread_id not in self.per_thread_data: + self.per_thread_data[thread_id] = ThreadData(thread_id=thread_id) + return self.per_thread_data[thread_id] + + def _get_current_thread_data(self): + """Get ThreadData for currently selected thread in PER_THREAD mode.""" + if self.view_mode == "PER_THREAD" and self.current_thread_index < len(self.thread_ids): + thread_id = self.thread_ids[self.current_thread_index] + return self.per_thread_data.get(thread_id) + return None + + def _get_current_result_source(self): + """Get result dict for current view mode (aggregated or per-thread).""" + if self.view_mode == "ALL": + return self.result + thread_data = self._get_current_thread_data() + return thread_data.result if thread_data else {} + + def _get_common_path_prefixes(self): + """Get common path prefixes to strip from file paths.""" + prefixes = [] + + # Get the actual stdlib location from the os module + # This works for both installed Python and development builds + os_module_file = os.__file__ + if os_module_file: + # os.__file__ points to os.py, get its directory + stdlib_dir = os.path.dirname(os.path.abspath(os_module_file)) + prefixes.append(stdlib_dir) + + # Get stdlib location from sysconfig (may be different or same) + stdlib_path = sysconfig.get_path("stdlib") + if stdlib_path: + prefixes.append(stdlib_path) + + # Get platstdlib location (platform-specific stdlib) + platstdlib_path = sysconfig.get_path("platstdlib") + if platstdlib_path: + prefixes.append(platstdlib_path) + + # Get site-packages locations + for site_path in site.getsitepackages(): + prefixes.append(site_path) + + # Also check user site-packages + user_site = site.getusersitepackages() + if user_site: + prefixes.append(user_site) + + # Remove duplicates and sort by length (longest first) to match most specific paths first + prefixes = list(set(prefixes)) + prefixes.sort(key=lambda x: len(x), reverse=True) + + return prefixes + + def simplify_path(self, filepath): + """Simplify a file path by removing common prefixes.""" + # Try to match against known prefixes + for prefix_path in self._path_prefixes: + if filepath.startswith(prefix_path): + # Remove the prefix completely + relative = filepath[len(prefix_path) :].lstrip(os.sep) + return relative + + # If no match, return the original path + return filepath + + def process_frames(self, frames, thread_id=None): + """Process a single thread's frame stack. + + Args: + frames: List of frame information + thread_id: Thread ID for per-thread tracking (optional) + """ + if not frames: + return + + # Get per-thread data if tracking per-thread + thread_data = self._get_or_create_thread_data(thread_id) if thread_id is not None else None + self._seen_locations.clear() + + # Process each frame in the stack to track cumulative calls + # frame.location is (lineno, end_lineno, col_offset, end_col_offset), int, or None + for frame in frames: + lineno = extract_lineno(frame.location) + location = (frame.filename, lineno, frame.funcname) + if location not in self._seen_locations: + self._seen_locations.add(location) + self.result[location]["cumulative_calls"] += 1 + if thread_data: + thread_data.result[location]["cumulative_calls"] += 1 + + # The top frame gets counted as an inline call (directly executing) + top_frame = frames[0] + top_lineno = extract_lineno(top_frame.location) + top_location = (top_frame.filename, top_lineno, top_frame.funcname) + self.result[top_location]["direct_calls"] += 1 + if thread_data: + thread_data.result[top_location]["direct_calls"] += 1 + + # Track opcode for top frame (the actively executing instruction) + opcode = getattr(top_frame, 'opcode', None) + if opcode is not None: + self.opcode_stats[top_location][opcode] += 1 + if thread_data: + thread_data.opcode_stats[top_location][opcode] += 1 + + def _get_sync_frame_iterator(self, stack_frames): + """Iterator for sync frames.""" + return self._iter_all_frames(stack_frames, skip_idle=self.skip_idle) + + def _get_async_frame_iterator(self, stack_frames): + """Iterator for async frames, yielding (frames, thread_id) tuples.""" + for frames, thread_id, task_id in self._iter_async_frames(stack_frames): + yield frames, thread_id + + def collect_failed_sample(self): + self.failed_samples += 1 + self.total_samples += 1 + + def collect(self, stack_frames, timestamp_us=None): + """Collect and display profiling data.""" + if self.start_time is None: + self.start_time = time.perf_counter() + self._last_display_update = self.start_time + + has_gc_frame = False + + # Collect thread status stats (only available in sync mode) + if not self.async_aware: + status_counts, sample_has_gc, per_thread_stats = self._collect_thread_status_stats(stack_frames) + for key, count in status_counts.items(): + self.thread_status_counts[key] += count + if sample_has_gc: + has_gc_frame = True + + for thread_id, stats in per_thread_stats.items(): + thread_data = self._get_or_create_thread_data(thread_id) + thread_data.has_gil += stats.get("has_gil", 0) + thread_data.on_cpu += stats.get("on_cpu", 0) + thread_data.gil_requested += stats.get("gil_requested", 0) + thread_data.unknown += stats.get("unknown", 0) + thread_data.has_exception += stats.get("has_exception", 0) + thread_data.total += stats.get("total", 0) + if stats.get("gc_samples", 0): + thread_data.gc_frame_samples += stats["gc_samples"] + + # Process frames using pre-selected iterator + frames_processed = False + for frames, thread_id in self._get_frame_iterator(stack_frames): + if not frames: + continue + + self.process_frames(frames, thread_id=thread_id) + frames_processed = True + + # Track thread IDs + if thread_id is not None and thread_id not in self.thread_ids: + self.thread_ids.append(thread_id) + + if thread_id is not None: + thread_data = self._get_or_create_thread_data(thread_id) + thread_data.sample_count += 1 + + if has_gc_frame: + self.gc_frame_samples += 1 + + # Count as successful - the sample worked even if no frames matched the filter + # (e.g., in --mode exception when no thread has an active exception) + self.successful_samples += 1 + self.total_samples += 1 + + # Handle input on every sample for instant responsiveness + if self.display is not None: + self._handle_input() + + # Update display at configured rate if display is initialized and not paused + if self.display is not None and not self.paused: + current_time = time.perf_counter() + if ( + self._last_display_update is None + or (current_time - self._last_display_update) + >= self.display_update_interval_sec + ): + self._update_display() + self._last_display_update = current_time + + def _prepare_display_data(self, height): + """Prepare data for display rendering.""" + elapsed = self.elapsed_time + stats_list = self.build_stats_list() + + # Calculate available space for stats + # Add extra lines for finished banner when in finished state + extra_header_lines = ( + FINISHED_BANNER_EXTRA_LINES if self.finished else 0 + ) + max_stats_lines = max( + 0, + height + - HEADER_LINES + - extra_header_lines + - FOOTER_LINES + - SAFETY_MARGIN, + ) + stats_list = stats_list[:max_stats_lines] + + return elapsed, stats_list + + def _initialize_widgets(self, colors): + """Initialize widgets with display and colors.""" + if self.header_widget is None: + # Initialize trend tracker with colors + if self._trend_tracker is None: + self._trend_tracker = TrendTracker(colors, enabled=True) + + self.header_widget = HeaderWidget(self.display, colors, self) + self.table_widget = TableWidget(self.display, colors, self) + self.footer_widget = FooterWidget(self.display, colors, self) + self.help_widget = HelpWidget(self.display, colors) + self.opcode_panel = OpcodePanel(self.display, colors, self) + + def _render_display_sections( + self, height, width, elapsed, stats_list, colors + ): + """Render all display sections to the screen.""" + line = 0 + try: + # Initialize widgets if not already done + self._initialize_widgets(colors) + + # Render header + line = self.header_widget.render( + line, width, elapsed=elapsed, stats_list=stats_list + ) + + # Render table + line = self.table_widget.render( + line, width, height=height, stats_list=stats_list + ) + + # Render opcode panel if enabled + if self.show_opcodes: + line = self.opcode_panel.render( + line, width, height=height, stats_list=stats_list + ) + + except curses.error: + pass + + def _update_display(self): + """Update the display with current stats.""" + try: + # Clear screen and get dimensions + self.display.clear() + height, width = self.display.get_dimensions() + + # Check terminal size + if width < MIN_TERMINAL_WIDTH or height < MIN_TERMINAL_HEIGHT: + self._show_terminal_too_small(height, width) + self.display.refresh() + return + + # Setup colors and initialize widgets (needed for both help and normal display) + colors = self._setup_colors() + self._initialize_widgets(colors) + + # Show help screen if requested + if self.show_help: + self.help_widget.render(0, width, height=height) + self.display.refresh() + return + + # Prepare data + elapsed, stats_list = self._prepare_display_data(height) + + # Render all sections + self._render_display_sections( + height, width, elapsed, stats_list, colors + ) + + # Footer + self.footer_widget.render(height - 2, width) + + # Show filter input prompt if in filter input mode + if self.filter_input_mode: + self.footer_widget.render_filter_input_prompt( + height - 1, width + ) + + # Refresh display + self.display.redraw() + self.display.refresh() + + except Exception: + pass + + def _cycle_sort(self, reverse=False): + """Cycle through different sort modes in column order. + + Args: + reverse: If True, cycle backwards (right to left), otherwise forward (left to right) + """ + sort_modes = [ + "nsamples", + "sample_pct", + "tottime", + "cumul_pct", + "cumtime", + ] + try: + current_idx = sort_modes.index(self.sort_by) + if reverse: + self.sort_by = sort_modes[(current_idx - 1) % len(sort_modes)] + else: + self.sort_by = sort_modes[(current_idx + 1) % len(sort_modes)] + except ValueError: + self.sort_by = "nsamples" + + def _setup_colors(self): + """Set up color pairs and return color attributes.""" + A_BOLD = self.display.get_attr("A_BOLD") + A_REVERSE = self.display.get_attr("A_REVERSE") + A_UNDERLINE = self.display.get_attr("A_UNDERLINE") + A_NORMAL = self.display.get_attr("A_NORMAL") + + if self.display.has_colors() and self._can_colorize: + with contextlib.suppress(Exception): + theme = _colorize.get_theme(force_color=True).live_profiler + default_bg = -1 + + self.display.init_color_pair(COLOR_PAIR_SAMPLES, theme.samples_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_FILE, theme.file_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_FUNC, theme.func_fg, default_bg) + + # Normal header background color pair + self.display.init_color_pair( + COLOR_PAIR_HEADER_BG, + theme.normal_header_fg, + theme.normal_header_bg, + ) + + self.display.init_color_pair(COLOR_PAIR_CYAN, theme.pid_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_YELLOW, theme.time_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_GREEN, theme.uptime_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_MAGENTA, theme.interval_fg, default_bg) + self.display.init_color_pair(COLOR_PAIR_RED, theme.off_gil_fg, default_bg) + self.display.init_color_pair( + COLOR_PAIR_SORTED_HEADER, + theme.sorted_header_fg, + theme.sorted_header_bg, + ) + + TREND_UP_PAIR = 11 + TREND_DOWN_PAIR = 12 + self.display.init_color_pair(TREND_UP_PAIR, theme.trend_up_fg, default_bg) + self.display.init_color_pair(TREND_DOWN_PAIR, theme.trend_down_fg, default_bg) + + return { + "header": self.display.get_color_pair(COLOR_PAIR_HEADER_BG) | A_BOLD, + "cyan": self.display.get_color_pair(COLOR_PAIR_CYAN) | A_BOLD, + "yellow": self.display.get_color_pair(COLOR_PAIR_YELLOW) | A_BOLD, + "green": self.display.get_color_pair(COLOR_PAIR_GREEN) | A_BOLD, + "magenta": self.display.get_color_pair(COLOR_PAIR_MAGENTA) | A_BOLD, + "red": self.display.get_color_pair(COLOR_PAIR_RED) | A_BOLD, + "sorted_header": self.display.get_color_pair(COLOR_PAIR_SORTED_HEADER) | A_BOLD, + "normal_header": self.display.get_color_pair(COLOR_PAIR_HEADER_BG) | A_BOLD, + "color_samples": self.display.get_color_pair(COLOR_PAIR_SAMPLES), + "color_file": self.display.get_color_pair(COLOR_PAIR_FILE), + "color_func": self.display.get_color_pair(COLOR_PAIR_FUNC), + "trend_up": self.display.get_color_pair(TREND_UP_PAIR) | A_BOLD, + "trend_down": self.display.get_color_pair(TREND_DOWN_PAIR) | A_BOLD, + "trend_stable": A_NORMAL, + } + + # Fallback for no-color mode + return { + "header": A_REVERSE | A_BOLD, + "cyan": A_BOLD, + "yellow": A_BOLD, + "green": A_BOLD, + "magenta": A_BOLD, + "red": A_BOLD, + "sorted_header": A_REVERSE | A_BOLD | A_UNDERLINE, + "normal_header": A_REVERSE | A_BOLD, + "color_samples": A_NORMAL, + "color_file": A_NORMAL, + "color_func": A_NORMAL, + "trend_up": A_BOLD, + "trend_down": A_BOLD, + "trend_stable": A_NORMAL, + } + + def build_stats_list(self): + """Build and sort the statistics list.""" + stats_list = [] + result_source = self._get_current_result_source() + + for func, call_counts in result_source.items(): + # Apply filter if set (using substring matching) + if self.filter_pattern: + filename, lineno, funcname = func + # Simple substring match (case-insensitive) + pattern_lower = self.filter_pattern.lower() + filename_lower = filename.lower() + funcname_lower = funcname.lower() + + # Match if pattern is substring of filename, funcname, or combined + matched = ( + pattern_lower in filename_lower + or pattern_lower in funcname_lower + or pattern_lower in f"{filename_lower}:{funcname_lower}" + ) + if not matched: + continue + + direct_calls = call_counts.get("direct_calls", 0) + cumulative_calls = call_counts.get("cumulative_calls", 0) + total_time = direct_calls * self.sample_interval_sec + cumulative_time = cumulative_calls * self.sample_interval_sec + + # Calculate sample percentages using successful_samples as denominator + # This ensures percentages are relative to samples that actually had data, + # not all sampling attempts (important for filtered modes like --mode exception) + sample_pct = (direct_calls / self.successful_samples * 100) if self.successful_samples > 0 else 0 + cumul_pct = (cumulative_calls / self.successful_samples * 100) if self.successful_samples > 0 else 0 + + # Calculate trends for all columns using TrendTracker + trends = {} + if self._trend_tracker is not None: + trends = self._trend_tracker.update_metrics( + func, + { + 'nsamples': direct_calls, + 'tottime': total_time, + 'cumtime': cumulative_time, + 'sample_pct': sample_pct, + 'cumul_pct': cumul_pct, + } + ) + + stats_list.append( + { + "func": func, + "direct_calls": direct_calls, + "cumulative_calls": cumulative_calls, + "total_time": total_time, + "cumulative_time": cumulative_time, + "sample_pct": sample_pct, + "cumul_pct": cumul_pct, + "trends": trends, + } + ) + + # Sort the stats + if self.sort_by == "nsamples": + stats_list.sort(key=lambda x: x["direct_calls"], reverse=True) + elif self.sort_by == "tottime": + stats_list.sort(key=lambda x: x["total_time"], reverse=True) + elif self.sort_by == "cumtime": + stats_list.sort(key=lambda x: x["cumulative_time"], reverse=True) + elif self.sort_by == "sample_pct": + stats_list.sort(key=lambda x: x["sample_pct"], reverse=True) + elif self.sort_by == "cumul_pct": + stats_list.sort(key=lambda x: x["cumul_pct"], reverse=True) + + return stats_list + + def reset_stats(self): + """Reset all collected statistics.""" + self.result.clear() + self.per_thread_data.clear() + self.thread_ids.clear() + self.view_mode = "ALL" + self.current_thread_index = 0 + self.total_samples = 0 + self.successful_samples = 0 + self.failed_samples = 0 + self.max_sample_rate = 0 + self.thread_status_counts = { + "has_gil": 0, + "on_cpu": 0, + "gil_requested": 0, + "unknown": 0, + "has_exception": 0, + "total": 0, + } + self.gc_frame_samples = 0 + # Clear trend tracking + if self._trend_tracker is not None: + self._trend_tracker.clear() + # Reset finished state and finish timestamp + self.finished = False + self.finish_timestamp = None + self.finish_wall_time = None + self.start_time = time.perf_counter() + self._last_display_update = self.start_time + + def mark_finished(self): + """Mark the profiling session as finished.""" + self.finished = True + # Capture the finish timestamp to freeze all timing displays + self.finish_timestamp = time.perf_counter() + self.finish_wall_time = time.time() # Wall clock time for display + # Force a final display update to show the finished message + if self.display is not None: + self._update_display() + + def _handle_finished_input_update(self, had_input): + """Update display after input when program is finished.""" + if self.finished and had_input and self.display is not None: + self._update_display() + + def _get_visible_rows_info(self): + """Calculate visible rows and stats list for opcode navigation.""" + stats_list = self.build_stats_list() + if self.display: + height, _ = self.display.get_dimensions() + extra_header = FINISHED_BANNER_EXTRA_LINES if self.finished else 0 + max_stats = max(0, height - HEADER_LINES - extra_header - FOOTER_LINES - SAFETY_MARGIN) + stats_list = stats_list[:max_stats] + visible_rows = max(1, height - 8 - 2 - 12) + else: + visible_rows = self.limit + total_rows = len(stats_list) + return stats_list, visible_rows, total_rows + + def _move_selection_down(self): + """Move selection down in opcode mode with scrolling.""" + if not self.show_opcodes: + return + + stats_list, visible_rows, total_rows = self._get_visible_rows_info() + if total_rows == 0: + return + + # Max scroll is when last item is at bottom + max_scroll = max(0, total_rows - visible_rows) + # Current absolute position + abs_pos = self.scroll_offset + self.selected_row + + # Only move if not at the last item + if abs_pos < total_rows - 1: + # Try to move selection within visible area first + if self.selected_row < visible_rows - 1: + self.selected_row += 1 + elif self.scroll_offset < max_scroll: + # Scroll down + self.scroll_offset += 1 + + # Clamp to valid range + self.scroll_offset = min(self.scroll_offset, max_scroll) + max_selected = min(visible_rows - 1, total_rows - self.scroll_offset - 1) + self.selected_row = min(self.selected_row, max(0, max_selected)) + + def _move_selection_up(self): + """Move selection up in opcode mode with scrolling.""" + if not self.show_opcodes: + return + + if self.selected_row > 0: + self.selected_row -= 1 + elif self.scroll_offset > 0: + self.scroll_offset -= 1 + + # Clamp to valid range based on actual stats_list + stats_list, visible_rows, total_rows = self._get_visible_rows_info() + if total_rows > 0: + max_scroll = max(0, total_rows - visible_rows) + self.scroll_offset = min(self.scroll_offset, max_scroll) + max_selected = min(visible_rows - 1, total_rows - self.scroll_offset - 1) + self.selected_row = min(self.selected_row, max(0, max_selected)) + + def _navigate_to_previous_thread(self): + """Navigate to previous thread in PER_THREAD mode, or switch from ALL to PER_THREAD.""" + if len(self.thread_ids) > 0: + if self.view_mode == "ALL": + self.view_mode = "PER_THREAD" + self.current_thread_index = len(self.thread_ids) - 1 + else: + self.current_thread_index = ( + self.current_thread_index - 1 + ) % len(self.thread_ids) + + def _navigate_to_next_thread(self): + """Navigate to next thread in PER_THREAD mode, or switch from ALL to PER_THREAD.""" + if len(self.thread_ids) > 0: + if self.view_mode == "ALL": + self.view_mode = "PER_THREAD" + self.current_thread_index = 0 + else: + self.current_thread_index = ( + self.current_thread_index + 1 + ) % len(self.thread_ids) + + def _show_terminal_too_small(self, height, width): + """Display a message when terminal is too small.""" + A_BOLD = self.display.get_attr("A_BOLD") + msg1 = "Terminal too small!" + msg2 = f"Need: {MIN_TERMINAL_WIDTH}x{MIN_TERMINAL_HEIGHT}" + msg3 = f"Have: {width}x{height}" + msg4 = "Please resize" + + # Center the messages + if height >= 4: + self.display.add_str( + height // 2 - 2, + max(0, (width - len(msg1)) // 2), + msg1[: width - 1], + A_BOLD, + ) + self.display.add_str( + height // 2 - 1, + max(0, (width - len(msg2)) // 2), + msg2[: width - 1], + ) + self.display.add_str( + height // 2, + max(0, (width - len(msg3)) // 2), + msg3[: width - 1], + ) + self.display.add_str( + height // 2 + 1, + max(0, (width - len(msg4)) // 2), + msg4[: width - 1], + ) + elif height >= 1: + self.display.add_str(0, 0, msg1[: width - 1], A_BOLD) + + def _show_terminal_size_warning_and_wait(self, height, width): + """Show terminal size warning during initialization and wait for user acknowledgment.""" + A_BOLD = self.display.get_attr("A_BOLD") + A_DIM = self.display.get_attr("A_DIM") + + self.display.clear() + msg1 = "WARNING: Terminal too small!" + msg2 = f"Required: {MIN_TERMINAL_WIDTH}x{MIN_TERMINAL_HEIGHT}" + msg3 = f"Current: {width}x{height}" + msg4 = "Please resize your terminal for best experience" + msg5 = "Press any key to continue..." + + # Center the messages + if height >= 5: + self.display.add_str( + height // 2 - 2, + max(0, (width - len(msg1)) // 2), + msg1[: width - 1], + A_BOLD, + ) + self.display.add_str( + height // 2 - 1, + max(0, (width - len(msg2)) // 2), + msg2[: width - 1], + ) + self.display.add_str( + height // 2, + max(0, (width - len(msg3)) // 2), + msg3[: width - 1], + ) + self.display.add_str( + height // 2 + 1, + max(0, (width - len(msg4)) // 2), + msg4[: width - 1], + ) + self.display.add_str( + height // 2 + 3, + max(0, (width - len(msg5)) // 2), + msg5[: width - 1], + A_DIM, + ) + elif height >= 1: + self.display.add_str(0, 0, msg1[: width - 1], A_BOLD) + + self.display.refresh() + # Wait for user acknowledgment (2 seconds timeout) + self.display.set_nodelay(False) + # Note: timeout is curses-specific, skipping for now + self.display.get_input() + self.display.set_nodelay(True) + + def _handle_input(self): + """Handle keyboard input (non-blocking).""" + self.display.set_nodelay(True) + ch = self.display.get_input() + + # Handle filter input mode FIRST - takes precedence over all commands + if self.filter_input_mode: + if ch == 27: # ESC key + self.filter_input_mode = False + self.filter_input_buffer = "" + elif ch == 10 or ch == 13: # Enter key + self.filter_pattern = ( + self.filter_input_buffer + if self.filter_input_buffer + else None + ) + self.filter_input_mode = False + self.filter_input_buffer = "" + elif ch == 127 or ch == 263: # Backspace + if self.filter_input_buffer: + self.filter_input_buffer = self.filter_input_buffer[:-1] + elif ch >= 32 and ch < 127: # Printable characters + self.filter_input_buffer += chr(ch) + + # Update display if input was processed while finished + self._handle_finished_input_update(ch != -1) + return + + # Handle help toggle keys + if ch == ord("h") or ch == ord("H") or ch == ord("?"): + self.show_help = not self.show_help + return + + # If showing help, any other key closes it + if self.show_help and ch != -1: + self.show_help = False + return + + # Handle regular commands + if ch == ord("q") or ch == ord("Q"): + self.running = False + + elif ch == ord("s"): + self._cycle_sort(reverse=False) + + elif ch == ord("S"): + self._cycle_sort(reverse=True) + + elif ch == ord("p") or ch == ord("P"): + self.paused = not self.paused + + elif ch == ord("r") or ch == ord("R"): + # Don't allow reset when profiling is finished + if not self.finished: + self.reset_stats() + + elif ch == ord("+") or ch == ord("="): + # Decrease update interval (faster refresh) + self.display_update_interval_sec = max( + 0.05, self.display_update_interval_sec - 0.05 + ) # Min 20Hz + + elif ch == ord("-") or ch == ord("_"): + # Increase update interval (slower refresh) + self.display_update_interval_sec = min( + 1.0, self.display_update_interval_sec + 0.05 + ) # Max 1Hz + + elif ch == ord("c") or ch == ord("C"): + if self.filter_pattern: + self.filter_pattern = None + + elif ch == ord("/"): + self.filter_input_mode = True + self.filter_input_buffer = self.filter_pattern or "" + + elif ch == ord("t") or ch == ord("T"): + # Toggle between ALL and PER_THREAD modes + if self.view_mode == "ALL": + if len(self.thread_ids) > 0: + self.view_mode = "PER_THREAD" + self.current_thread_index = 0 + else: + self.view_mode = "ALL" + + elif ch == ord("x") or ch == ord("X"): + # Toggle trend colors on/off + if self._trend_tracker is not None: + self._trend_tracker.toggle() + + elif ch == ord("j") or ch == ord("J"): + # Move selection down in opcode mode (with scrolling) + self._move_selection_down() + + elif ch == ord("k") or ch == ord("K"): + # Move selection up in opcode mode (with scrolling) + self._move_selection_up() + + elif ch == curses.KEY_UP: + # Move selection up (same as 'k') when in opcode mode + if self.show_opcodes: + self._move_selection_up() + else: + # Navigate to previous thread (same as KEY_LEFT) + self._navigate_to_previous_thread() + + elif ch == curses.KEY_DOWN: + # Move selection down (same as 'j') when in opcode mode + if self.show_opcodes: + self._move_selection_down() + else: + # Navigate to next thread (same as KEY_RIGHT) + self._navigate_to_next_thread() + + elif ch == curses.KEY_LEFT: + # Navigate to previous thread + self._navigate_to_previous_thread() + + elif ch == curses.KEY_RIGHT: + # Navigate to next thread + self._navigate_to_next_thread() + + # Update display if input was processed while finished + self._handle_finished_input_update(ch != -1) + + def init_curses(self, stdscr): + """Initialize curses display and suppress stdout/stderr.""" + self.stdscr = stdscr + self.display = CursesDisplay(stdscr) + + # Check terminal size upfront and warn if too small + height, width = self.display.get_dimensions() + + if width < MIN_TERMINAL_WIDTH or height < MIN_TERMINAL_HEIGHT: + # Show warning and wait briefly for user to see it + self._show_terminal_size_warning_and_wait(height, width) + + curses.curs_set(0) # Hide cursor + stdscr.nodelay(True) # Non-blocking input + stdscr.scrollok(False) # Disable scrolling + stdscr.idlok(False) # Disable hardware insert/delete + stdscr.leaveok(True) # Don't care about cursor position + + if curses.has_colors(): + curses.start_color() + curses.use_default_colors() + + # Suppress stdout and stderr to prevent interfering with curses display + # Use contextlib.redirect_stdout/stderr for better resource management + self._saved_stdout = sys.stdout + self._saved_stderr = sys.stderr + # Open devnull and ensure it's cleaned up even if an exception occurs + try: + self._devnull = open(os.devnull, "w") + sys.stdout = self._devnull + sys.stderr = self._devnull + except Exception: + # If redirection fails, restore original streams + sys.stdout = self._saved_stdout + sys.stderr = self._saved_stderr + raise + + # Initial clear + self.display.clear() + self.display.refresh() + + def cleanup_curses(self): + """Clean up curses display and restore stdout/stderr.""" + # Restore stdout and stderr in reverse order + # Use try-finally to ensure cleanup even if restoration fails + try: + if self._saved_stdout is not None: + sys.stdout = self._saved_stdout + self._saved_stdout = None + if self._saved_stderr is not None: + sys.stderr = self._saved_stderr + self._saved_stderr = None + finally: + # Always close devnull, even if stdout/stderr restoration fails + if self._devnull is not None: + with contextlib.suppress(Exception): + self._devnull.close() + self._devnull = None + + if self.display is not None and self.stdscr is not None: + with contextlib.suppress(Exception): + curses.curs_set(1) # Show cursor + self.display.set_nodelay(False) + + def export(self, filename): + """Export is not supported in live mode.""" + raise NotImplementedError( + "Export to file is not supported in live mode. " + "Use the live TUI to view statistics in real-time." + ) diff --git a/Lib/profiling/sampling/live_collector/constants.py b/Lib/profiling/sampling/live_collector/constants.py new file mode 100644 index 00000000000000..bb45006553a67b --- /dev/null +++ b/Lib/profiling/sampling/live_collector/constants.py @@ -0,0 +1,65 @@ +"""Constants for the live profiling collector.""" + +# Time conversion constants +MICROSECONDS_PER_SECOND = 1_000_000 + +# Display update constants +DISPLAY_UPDATE_HZ = 10 +DISPLAY_UPDATE_INTERVAL_SEC = 1.0 / DISPLAY_UPDATE_HZ # 0.1 seconds + +# Terminal size constraints +MIN_TERMINAL_WIDTH = 60 +MIN_TERMINAL_HEIGHT = 12 + +# Column width thresholds +WIDTH_THRESHOLD_SAMPLE_PCT = 80 +WIDTH_THRESHOLD_TOTTIME = 100 +WIDTH_THRESHOLD_CUMUL_PCT = 120 +WIDTH_THRESHOLD_CUMTIME = 140 + +# Display layout constants +HEADER_LINES = 10 # Increased to include thread status line +FOOTER_LINES = 2 +SAFETY_MARGIN = 1 +TOP_FUNCTIONS_DISPLAY_COUNT = 3 + +# Column widths for data display +COL_WIDTH_NSAMPLES = 13 +COL_SPACING = 2 +COL_WIDTH_SAMPLE_PCT = 5 +COL_WIDTH_TIME = 10 + +# Function name display +MIN_FUNC_NAME_WIDTH = 10 +MAX_FUNC_NAME_WIDTH = 40 +MIN_AVAILABLE_SPACE = 10 + +# Progress bar display +MIN_BAR_WIDTH = 10 +MAX_SAMPLE_RATE_BAR_WIDTH = 30 +MAX_EFFICIENCY_BAR_WIDTH = 60 + +# Sample rate scaling +MIN_SAMPLE_RATE_FOR_SCALING = 100 + +# Finished banner display +FINISHED_BANNER_EXTRA_LINES = 3 # Blank line + banner + blank line + +# Opcode panel display +OPCODE_PANEL_HEIGHT = 12 # Height reserved for opcode statistics panel + +# Color pair IDs +COLOR_PAIR_SAMPLES = 1 +COLOR_PAIR_FILE = 2 +COLOR_PAIR_FUNC = 3 +COLOR_PAIR_HEADER_BG = 4 +COLOR_PAIR_CYAN = 5 +COLOR_PAIR_YELLOW = 6 +COLOR_PAIR_GREEN = 7 +COLOR_PAIR_MAGENTA = 8 +COLOR_PAIR_RED = 9 +COLOR_PAIR_SORTED_HEADER = 10 + +# Default display settings +DEFAULT_SORT_BY = "nsamples" # Number of samples in leaf (self time) +DEFAULT_DISPLAY_LIMIT = 20 diff --git a/Lib/profiling/sampling/live_collector/display.py b/Lib/profiling/sampling/live_collector/display.py new file mode 100644 index 00000000000000..f5324421b10211 --- /dev/null +++ b/Lib/profiling/sampling/live_collector/display.py @@ -0,0 +1,240 @@ +"""Display interface abstractions for the live profiling collector.""" + +import contextlib +import curses +from abc import ABC, abstractmethod + + +class DisplayInterface(ABC): + """Abstract interface for display operations to enable testing.""" + + @abstractmethod + def get_dimensions(self): + """Get terminal dimensions as (height, width).""" + pass + + @abstractmethod + def clear(self): + """Clear the screen.""" + pass + + @abstractmethod + def refresh(self): + """Refresh the screen to show changes.""" + pass + + @abstractmethod + def redraw(self): + """Redraw the entire window.""" + pass + + @abstractmethod + def add_str(self, line, col, text, attr=0): + """Add a string at the specified position.""" + pass + + @abstractmethod + def get_input(self): + """Get a character from input (non-blocking). Returns -1 if no input.""" + pass + + @abstractmethod + def set_nodelay(self, flag): + """Set non-blocking mode for input.""" + pass + + @abstractmethod + def has_colors(self): + """Check if terminal supports colors.""" + pass + + @abstractmethod + def init_color_pair(self, pair_id, fg, bg): + """Initialize a color pair.""" + pass + + @abstractmethod + def get_color_pair(self, pair_id): + """Get a color pair attribute.""" + pass + + @abstractmethod + def get_attr(self, name): + """Get a display attribute by name (e.g., 'A_BOLD', 'A_REVERSE').""" + pass + + +class CursesDisplay(DisplayInterface): + """Real curses display implementation.""" + + def __init__(self, stdscr): + self.stdscr = stdscr + + def get_dimensions(self): + return self.stdscr.getmaxyx() + + def clear(self): + # Use erase() instead of clear() to avoid flickering + # clear() forces a complete screen redraw, erase() just clears the buffer + self.stdscr.erase() + + def refresh(self): + self.stdscr.refresh() + + def redraw(self): + # Use noutrefresh + doupdate for smoother updates + self.stdscr.noutrefresh() + curses.doupdate() + + def add_str(self, line, col, text, attr=0): + try: + height, width = self.get_dimensions() + if 0 <= line < height and 0 <= col < width: + max_len = width - col - 1 + if len(text) > max_len: + text = text[:max_len] + self.stdscr.addstr(line, col, text, attr) + except curses.error: + pass + + def get_input(self): + try: + return self.stdscr.getch() + except (KeyError, curses.error): + return -1 + + def set_nodelay(self, flag): + self.stdscr.nodelay(flag) + + def has_colors(self): + return curses.has_colors() + + def init_color_pair(self, pair_id, fg, bg): + try: + curses.init_pair(pair_id, fg, bg) + except curses.error: + pass + + def get_color_pair(self, pair_id): + return curses.color_pair(pair_id) + + def get_attr(self, name): + return getattr(curses, name, 0) + + +class MockDisplay(DisplayInterface): + """Mock display for testing.""" + + def __init__(self, height=40, width=160): + self.height = height + self.width = width + self.buffer = {} + self.cleared = False + self.refreshed = False + self.redrawn = False + self.input_queue = [] + self.nodelay_flag = True + self.colors_supported = True + self.color_pairs = {} + + def get_dimensions(self): + return (self.height, self.width) + + def clear(self): + self.buffer.clear() + self.cleared = True + + def refresh(self): + self.refreshed = True + + def redraw(self): + self.redrawn = True + + def add_str(self, line, col, text, attr=0): + if 0 <= line < self.height and 0 <= col < self.width: + max_len = self.width - col - 1 + if len(text) > max_len: + text = text[:max_len] + self.buffer[(line, col)] = (text, attr) + + def get_input(self): + if self.input_queue: + return self.input_queue.pop(0) + return -1 + + def set_nodelay(self, flag): + self.nodelay_flag = flag + + def has_colors(self): + return self.colors_supported + + def init_color_pair(self, pair_id, fg, bg): + self.color_pairs[pair_id] = (fg, bg) + + def get_color_pair(self, pair_id): + return pair_id << 8 + + def get_attr(self, name): + attrs = { + "A_NORMAL": 0, + "A_BOLD": 1 << 16, + "A_REVERSE": 1 << 17, + "A_UNDERLINE": 1 << 18, + "A_DIM": 1 << 19, + } + return attrs.get(name, 0) + + def simulate_input(self, char): + """Helper method for tests to simulate keyboard input.""" + self.input_queue.append(char) + + def get_text_at(self, line, col): + """Helper method for tests to inspect buffer content.""" + if (line, col) in self.buffer: + return self.buffer[(line, col)][0] + return None + + def get_all_lines(self): + """Get all display content as a list of lines (for testing).""" + if not self.buffer: + return [] + + max_line = max(pos[0] for pos in self.buffer.keys()) + lines = [] + for line_num in range(max_line + 1): + line_parts = [] + for col in range(self.width): + if (line_num, col) in self.buffer: + text, _ = self.buffer[(line_num, col)] + line_parts.append((col, text)) + + # Reconstruct line from parts + if line_parts: + line_parts.sort(key=lambda x: x[0]) + line = "" + last_col = 0 + for col, text in line_parts: + if col > last_col: + line += " " * (col - last_col) + line += text + last_col = col + len(text) + lines.append(line.rstrip()) + else: + lines.append("") + + # Remove trailing empty lines + while lines and not lines[-1]: + lines.pop() + + return lines + + def find_text(self, pattern): + """Find text matching pattern in buffer (for testing). Returns (line, col) or None.""" + for (line, col), (text, _) in self.buffer.items(): + if pattern in text: + return (line, col) + return None + + def contains_text(self, text): + """Check if display contains the given text anywhere (for testing).""" + return self.find_text(text) is not None diff --git a/Lib/profiling/sampling/live_collector/trend_tracker.py b/Lib/profiling/sampling/live_collector/trend_tracker.py new file mode 100644 index 00000000000000..c025b83a13423f --- /dev/null +++ b/Lib/profiling/sampling/live_collector/trend_tracker.py @@ -0,0 +1,157 @@ +"""TrendTracker - Encapsulated trend tracking for live profiling metrics. + +This module provides trend tracking functionality for profiling metrics, +calculating direction indicators (up/down/stable) and managing associated +visual attributes like colors. +""" + +import curses +from typing import Dict, Literal, Any + +TrendDirection = Literal["up", "down", "stable"] + + +class TrendTracker: + """ + Tracks metric trends over time and provides visual indicators. + + This class encapsulates all logic for: + - Tracking previous values of metrics + - Calculating trend directions (up/down/stable) + - Determining visual attributes (colors) for trends + - Managing enable/disable state + + Example: + tracker = TrendTracker(colors_dict) + tracker.update("func1", "nsamples", 10) + trend = tracker.get_trend("func1", "nsamples") + color = tracker.get_color("func1", "nsamples") + """ + + # Threshold for determining if a value has changed significantly + CHANGE_THRESHOLD = 0.001 + + def __init__(self, colors: Dict[str, int], enabled: bool = True): + """ + Initialize the trend tracker. + + Args: + colors: Dictionary containing color attributes including + 'trend_up', 'trend_down', 'trend_stable' + enabled: Whether trend tracking is initially enabled + """ + self._previous_values: Dict[Any, Dict[str, float]] = {} + self._enabled = enabled + self._colors = colors + + @property + def enabled(self) -> bool: + """Whether trend tracking is enabled.""" + return self._enabled + + def toggle(self) -> bool: + """ + Toggle trend tracking on/off. + + Returns: + New enabled state + """ + self._enabled = not self._enabled + return self._enabled + + def set_enabled(self, enabled: bool) -> None: + """Set trend tracking enabled state.""" + self._enabled = enabled + + def update(self, key: Any, metric: str, value: float) -> TrendDirection: + """ + Update a metric value and calculate its trend. + + Args: + key: Identifier for the entity (e.g., function) + metric: Name of the metric (e.g., 'nsamples', 'tottime') + value: Current value of the metric + + Returns: + Trend direction: 'up', 'down', or 'stable' + """ + # Initialize storage for this key if needed + if key not in self._previous_values: + self._previous_values[key] = {} + + # Get previous value, defaulting to current if not tracked yet + prev_value = self._previous_values[key].get(metric, value) + + # Calculate trend + if value > prev_value + self.CHANGE_THRESHOLD: + trend = "up" + elif value < prev_value - self.CHANGE_THRESHOLD: + trend = "down" + else: + trend = "stable" + + # Update previous value for next iteration + self._previous_values[key][metric] = value + + return trend + + def get_trend(self, key: Any, metric: str) -> TrendDirection: + """ + Get the current trend for a metric without updating. + + Args: + key: Identifier for the entity + metric: Name of the metric + + Returns: + Trend direction, or 'stable' if not tracked + """ + # This would require storing trends separately, which we don't do + # For now, return stable if not found + return "stable" + + def get_color(self, trend: TrendDirection) -> int: + """ + Get the color attribute for a trend direction. + + Args: + trend: The trend direction + + Returns: + Curses color attribute (or A_NORMAL if disabled) + """ + if not self._enabled: + return curses.A_NORMAL + + if trend == "up": + return self._colors.get("trend_up", curses.A_BOLD) + elif trend == "down": + return self._colors.get("trend_down", curses.A_BOLD) + else: # stable + return self._colors.get("trend_stable", curses.A_NORMAL) + + def update_metrics(self, key: Any, metrics: Dict[str, float]) -> Dict[str, TrendDirection]: + """ + Update multiple metrics at once and get their trends. + + Args: + key: Identifier for the entity + metrics: Dictionary of metric_name -> value + + Returns: + Dictionary of metric_name -> trend_direction + """ + trends = {} + for metric, value in metrics.items(): + trends[metric] = self.update(key, metric, value) + return trends + + def clear(self) -> None: + """Clear all tracked values (useful on stats reset).""" + self._previous_values.clear() + + def __repr__(self) -> str: + """String representation for debugging.""" + status = "enabled" if self._enabled else "disabled" + tracked = len(self._previous_values) + return f"TrendTracker({status}, tracking {tracked} entities)" diff --git a/Lib/profiling/sampling/live_collector/widgets.py b/Lib/profiling/sampling/live_collector/widgets.py new file mode 100644 index 00000000000000..86d2649f875e62 --- /dev/null +++ b/Lib/profiling/sampling/live_collector/widgets.py @@ -0,0 +1,1090 @@ +"""Widget classes for the live profiling collector UI.""" + +import curses +import time +from abc import ABC, abstractmethod + +from .constants import ( + TOP_FUNCTIONS_DISPLAY_COUNT, + MIN_FUNC_NAME_WIDTH, + MAX_FUNC_NAME_WIDTH, + WIDTH_THRESHOLD_SAMPLE_PCT, + WIDTH_THRESHOLD_TOTTIME, + WIDTH_THRESHOLD_CUMUL_PCT, + WIDTH_THRESHOLD_CUMTIME, + MICROSECONDS_PER_SECOND, + DISPLAY_UPDATE_INTERVAL_SEC, + MIN_BAR_WIDTH, + MAX_SAMPLE_RATE_BAR_WIDTH, + MAX_EFFICIENCY_BAR_WIDTH, + MIN_SAMPLE_RATE_FOR_SCALING, + FOOTER_LINES, + FINISHED_BANNER_EXTRA_LINES, + OPCODE_PANEL_HEIGHT, +) +from ..constants import ( + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, + THREAD_STATUS_UNKNOWN, + THREAD_STATUS_GIL_REQUESTED, + PROFILING_MODE_CPU, + PROFILING_MODE_GIL, + PROFILING_MODE_WALL, +) +from ..opcode_utils import get_opcode_info, format_opcode + + +class Widget(ABC): + """Base class for UI widgets.""" + + def __init__(self, display, colors): + """ + Initialize widget. + + Args: + display: DisplayInterface implementation + colors: Dictionary of color attributes + """ + self.display = display + self.colors = colors + + @abstractmethod + def render(self, line, width, **kwargs): + """ + Render the widget starting at the given line. + + Args: + line: Starting line number + width: Available width + **kwargs: Additional rendering parameters + + Returns: + Next available line number after rendering + """ + pass + + def add_str(self, line, col, text, attr=0): + """Add a string to the display at the specified position.""" + self.display.add_str(line, col, text, attr) + + +class ProgressBarWidget(Widget): + """Reusable progress bar widget.""" + + def render(self, line, width, **kwargs): + """Render is not used for progress bars - use render_bar instead.""" + raise NotImplementedError("Use render_bar method instead") + + def render_bar( + self, filled, total, max_width, fill_char="█", empty_char="░" + ): + """ + Render a progress bar and return the bar string and its length. + + Args: + filled: Current filled amount + total: Total amount (max value) + max_width: Maximum width for the bar + fill_char: Character to use for filled portion + empty_char: Character to use for empty portion + + Returns: + Tuple of (bar_string, bar_length) + """ + bar_width = min(max_width, max_width) + normalized = min(filled / max(total, 1), 1.0) + bar_fill = int(normalized * bar_width) + + bar = "[" + for i in range(bar_width): + if i < bar_fill: + bar += fill_char + else: + bar += empty_char + bar += "]" + return bar, len(bar) + + +class HeaderWidget(Widget): + """Widget for rendering the header section (lines 0-8).""" + + def __init__(self, display, colors, collector): + """ + Initialize header widget. + + Args: + display: DisplayInterface implementation + colors: Dictionary of color attributes + collector: Reference to LiveStatsCollector for accessing stats + """ + super().__init__(display, colors) + self.collector = collector + self.progress_bar = ProgressBarWidget(display, colors) + + def render(self, line, width, **kwargs): + """ + Render the complete header section. + + Args: + line: Starting line number + width: Available width + kwargs: Must contain 'elapsed' key + + Returns: + Next available line number + """ + elapsed = kwargs["elapsed"] + + line = self.draw_header_info(line, width, elapsed) + line = self.draw_sample_stats(line, width, elapsed) + line = self.draw_efficiency_bar(line, width) + line = self.draw_thread_status(line, width) + line = self.draw_function_stats( + line, width, kwargs.get("stats_list", []) + ) + line = self.draw_top_functions( + line, width, kwargs.get("stats_list", []) + ) + + # Show prominent finished banner if profiling is complete + if self.collector.finished: + line = self.draw_finished_banner(line, width) + + # Separator + A_DIM = self.display.get_attr("A_DIM") + separator = "─" * (width - 1) + self.add_str(line, 0, separator[: width - 1], A_DIM) + line += 1 + + return line + + def format_uptime(self, elapsed): + """Format elapsed time as uptime string.""" + uptime_sec = int(elapsed) + hours = uptime_sec // 3600 + minutes = (uptime_sec % 3600) // 60 + seconds = uptime_sec % 60 + if hours > 0: + return f"{hours}h{minutes:02d}m{seconds:02d}s" + else: + return f"{minutes}m{seconds:02d}s" + + def draw_header_info(self, line, width, elapsed): + """Draw the header information line with PID, uptime, time, and interval.""" + # Draw title + A_BOLD = self.display.get_attr("A_BOLD") + title = "Tachyon Profiler" + self.add_str(line, 0, title, A_BOLD | self.colors["cyan"]) + line += 1 + + current_time = self.collector.current_time_display + uptime = self.format_uptime(elapsed) + + # Calculate display refresh rate + refresh_hz = ( + 1.0 / self.collector.display_update_interval_sec if self.collector.display_update_interval_sec > 0 else 0 + ) + + # Get current view mode and thread display + if self.collector.view_mode == "ALL": + thread_name = "ALL" + thread_color = self.colors["green"] + else: + # PER_THREAD mode + if self.collector.current_thread_index < len( + self.collector.thread_ids + ): + thread_id = self.collector.thread_ids[ + self.collector.current_thread_index + ] + num_threads = len(self.collector.thread_ids) + thread_name = f"{thread_id} ({self.collector.current_thread_index + 1}/{num_threads})" + thread_color = self.colors["magenta"] + else: + thread_name = "ALL" + thread_color = self.colors["green"] + + header_parts = [ + ("PID: ", curses.A_BOLD), + (f"{self.collector.pid}", self.colors["cyan"]), + (" │ ", curses.A_DIM), + ("Thread: ", curses.A_BOLD), + (thread_name, thread_color), + (" │ ", curses.A_DIM), + ("Uptime: ", curses.A_BOLD), + (uptime, self.colors["green"]), + (" │ ", curses.A_DIM), + ("Time: ", curses.A_BOLD), + (current_time, self.colors["yellow"]), + (" │ ", curses.A_DIM), + ("Interval: ", curses.A_BOLD), + ( + f"{self.collector.sample_interval_usec}µs", + self.colors["magenta"], + ), + (" │ ", curses.A_DIM), + ("Display: ", curses.A_BOLD), + (f"{refresh_hz:.1f}Hz", self.colors["cyan"]), + ] + + col = 0 + for text, attr in header_parts: + if col < width - 1: + self.add_str(line, col, text, attr) + col += len(text) + return line + 1 + + def format_rate_with_units(self, rate_hz): + """Format a rate in Hz with appropriate units (Hz, KHz, MHz).""" + if rate_hz >= MICROSECONDS_PER_SECOND: + return f"{rate_hz / MICROSECONDS_PER_SECOND:.1f}MHz" + elif rate_hz >= 1_000: + return f"{rate_hz / 1_000:.1f}KHz" + else: + return f"{rate_hz:.1f}Hz" + + def draw_sample_stats(self, line, width, elapsed): + """Draw sample statistics with visual progress bar.""" + sample_rate = ( + self.collector.total_samples / elapsed if elapsed > 0 else 0 + ) + + # Update max sample rate + if sample_rate > self.collector.max_sample_rate: + self.collector.max_sample_rate = sample_rate + + col = 0 + self.add_str(line, col, "Samples: ", curses.A_BOLD) + col += 9 + self.add_str( + line, + col, + f"{self.collector.total_samples:>8}", + self.colors["cyan"], + ) + col += 8 + self.add_str( + line, col, f" total ({sample_rate:>7.1f}/s) ", curses.A_NORMAL + ) + col += 23 + + # Draw sample rate bar + target_rate = ( + MICROSECONDS_PER_SECOND / self.collector.sample_interval_usec + ) + + # Show current/target ratio with percentage + if sample_rate > 0 and target_rate > 0: + percentage = min((sample_rate / target_rate) * 100, 100) + current_formatted = self.format_rate_with_units(sample_rate) + target_formatted = self.format_rate_with_units(target_rate) + + if percentage >= 99.5: # Show 100% when very close + rate_label = f" {current_formatted}/{target_formatted} (100%)" + else: + rate_label = f" {current_formatted}/{target_formatted} ({percentage:>4.1f}%)" + else: + target_formatted = self.format_rate_with_units(target_rate) + rate_label = f" target: {target_formatted}" + + available_width = width - col - len(rate_label) - 3 + + if available_width >= MIN_BAR_WIDTH: + bar_width = min(MAX_SAMPLE_RATE_BAR_WIDTH, available_width) + # Use target rate as the reference, with a minimum for scaling + reference_rate = max(target_rate, MIN_SAMPLE_RATE_FOR_SCALING) + normalized_rate = min(sample_rate / reference_rate, 1.0) + bar_fill = int(normalized_rate * bar_width) + + bar = "[" + for i in range(bar_width): + bar += "█" if i < bar_fill else "░" + bar += "]" + self.add_str(line, col, bar, self.colors["green"]) + col += len(bar) + + if col + len(rate_label) < width - 1: + self.add_str(line, col + 1, rate_label, curses.A_DIM) + return line + 1 + + def draw_efficiency_bar(self, line, width): + """Draw sample efficiency bar showing success/failure rates.""" + # total_samples = successful_samples + failed_samples, so percentages add to 100% + total = max(1, self.collector.total_samples) + success_pct = (self.collector.successful_samples / total) * 100 + failed_pct = (self.collector.failed_samples / total) * 100 + + col = 0 + self.add_str(line, col, "Efficiency:", curses.A_BOLD) + col += 11 + + label = f" {success_pct:>5.2f}% good, {failed_pct:>5.2f}% failed" + available_width = width - col - len(label) - 3 + + if available_width >= MIN_BAR_WIDTH: + bar_width = min(MAX_EFFICIENCY_BAR_WIDTH, available_width) + success_fill = int((self.collector.successful_samples / total) * bar_width) + failed_fill = bar_width - success_fill + + self.add_str(line, col, "[", curses.A_NORMAL) + col += 1 + if success_fill > 0: + self.add_str( + line, col, "█" * success_fill, self.colors["green"] + ) + col += success_fill + if failed_fill > 0: + self.add_str(line, col, "█" * failed_fill, self.colors["red"]) + col += failed_fill + self.add_str(line, col, "]", curses.A_NORMAL) + col += 1 + + self.add_str(line, col + 1, label, curses.A_NORMAL) + return line + 1 + + def _add_percentage_stat( + self, line, col, value, label, color, add_separator=False + ): + """Add a percentage stat to the display. + + Args: + line: Line number + col: Starting column + value: Percentage value + label: Label text + color: Color attribute + add_separator: Whether to add separator before the stat + + Returns: + Updated column position + """ + if add_separator: + self.add_str(line, col, " │ ", curses.A_DIM) + col += 3 + + self.add_str(line, col, f"{value:>4.1f}", color) + col += 4 + self.add_str(line, col, f"% {label}", curses.A_NORMAL) + col += len(label) + 2 + + return col + + def draw_thread_status(self, line, width): + """Draw thread status statistics and GC information.""" + # Get status counts for current view mode + thread_data = self.collector._get_current_thread_data() + status_counts = thread_data.as_status_dict() if thread_data else self.collector.thread_status_counts + + # Calculate percentages + total_threads = max(1, status_counts["total"]) + pct_on_gil = (status_counts["has_gil"] / total_threads) * 100 + pct_off_gil = 100.0 - pct_on_gil + pct_gil_requested = (status_counts["gil_requested"] / total_threads) * 100 + pct_exception = (status_counts.get("has_exception", 0) / total_threads) * 100 + + # Get GC percentage based on view mode + if thread_data: + total_samples = max(1, thread_data.sample_count) + pct_gc = (thread_data.gc_frame_samples / total_samples) * 100 + else: + # Use total_samples for GC percentage since gc_frame_samples is tracked + # across ALL samples (via thread status), not just successful ones + total_samples = max(1, self.collector.total_samples) + pct_gc = (self.collector.gc_frame_samples / total_samples) * 100 + + col = 0 + self.add_str(line, col, "Threads: ", curses.A_BOLD) + col += 11 + + # Show GIL stats only if mode is not GIL (GIL mode filters to only GIL holders) + if self.collector.mode != PROFILING_MODE_GIL: + col = self._add_percentage_stat( + line, col, pct_on_gil, "on gil", self.colors["green"] + ) + col = self._add_percentage_stat( + line, + col, + pct_off_gil, + "off gil", + self.colors["red"], + add_separator=True, + ) + + # Show "waiting for gil" only if mode is not GIL + if self.collector.mode != PROFILING_MODE_GIL and col < width - 30: + col = self._add_percentage_stat( + line, + col, + pct_gil_requested, + "waiting for gil", + self.colors["yellow"], + add_separator=True, + ) + + # Show exception stats + if col < width - 15: + col = self._add_percentage_stat( + line, + col, + pct_exception, + "exc", + self.colors["red"], + add_separator=(col > 11), + ) + + # Always show GC stats + if col < width - 15: + col = self._add_percentage_stat( + line, + col, + pct_gc, + "GC", + self.colors["magenta"], + add_separator=(col > 11), + ) + + return line + 1 + + def draw_function_stats(self, line, width, stats_list): + """Draw function statistics summary.""" + result_set = self.collector._get_current_result_source() + total_funcs = len(result_set) + funcs_shown = len(stats_list) + executing_funcs = sum( + 1 for f in result_set.values() if f.get("direct_calls", 0) > 0 + ) + stack_only = total_funcs - executing_funcs + + col = 0 + self.add_str(line, col, "Functions: ", curses.A_BOLD) + col += 11 + self.add_str(line, col, f"{total_funcs:>5}", self.colors["cyan"]) + col += 5 + self.add_str(line, col, " total", curses.A_NORMAL) + col += 6 + + if col < width - 25: + self.add_str(line, col, " │ ", curses.A_DIM) + col += 3 + self.add_str( + line, col, f"{executing_funcs:>5}", self.colors["green"] + ) + col += 5 + self.add_str(line, col, " exec", curses.A_NORMAL) + col += 5 + + if col < width - 25: + self.add_str(line, col, " │ ", curses.A_DIM) + col += 3 + self.add_str(line, col, f"{stack_only:>5}", self.colors["yellow"]) + col += 5 + self.add_str(line, col, " stack", curses.A_NORMAL) + col += 6 + + if col < width - 20: + self.add_str(line, col, " │ ", curses.A_DIM) + col += 3 + self.add_str( + line, col, f"{funcs_shown:>5}", self.colors["magenta"] + ) + col += 5 + self.add_str(line, col, " shown", curses.A_NORMAL) + return line + 1 + + def draw_top_functions(self, line, width, stats_list): + """Draw top N hottest functions.""" + col = 0 + self.add_str( + line, + col, + f"Top {TOP_FUNCTIONS_DISPLAY_COUNT}: ", + curses.A_BOLD, + ) + col += 11 + + top_by_samples = sorted( + stats_list, key=lambda x: x["direct_calls"], reverse=True + ) + emojis = ["🥇", "🥈", "🥉"] + medal_colors = [ + self.colors["red"], + self.colors["yellow"], + self.colors["green"], + ] + + displayed = 0 + for func_data in top_by_samples: + if displayed >= TOP_FUNCTIONS_DISPLAY_COUNT: + break + if col >= width - 20: + break + if func_data["direct_calls"] == 0: + continue + + func_name = func_data["func"][2] + func_pct = func_data["sample_pct"] + + # Medal emoji + if col + 3 < width - 15: + self.add_str( + line, col, emojis[displayed] + " ", medal_colors[displayed] + ) + col += 3 + + # Function name (truncate to fit) + available_for_name = width - col - 15 + max_name_len = min(25, max(5, available_for_name)) + if len(func_name) > max_name_len: + func_name = func_name[: max_name_len - 3] + "..." + + if col + len(func_name) < width - 10: + self.add_str(line, col, func_name, medal_colors[displayed]) + col += len(func_name) + + pct_str = ( + f" ({func_pct:.1f}%)" + if func_pct >= 0.1 + else f" ({func_data['direct_calls']})" + ) + self.add_str(line, col, pct_str, curses.A_DIM) + col += len(pct_str) + + displayed += 1 + + if displayed < 3 and col < width - 30: + self.add_str(line, col, " │ ", curses.A_DIM) + col += 3 + + if displayed == 0 and col < width - 25: + self.add_str(line, col, "(collecting samples...)", curses.A_DIM) + + return line + 1 + + def draw_finished_banner(self, line, width): + """Draw a prominent banner when profiling is finished.""" + A_REVERSE = self.display.get_attr("A_REVERSE") + A_BOLD = self.display.get_attr("A_BOLD") + + # Add blank line for separation + line += 1 + + # Create the banner message + message = " ✓ PROFILING COMPLETE - Final Results Below - Press 'q' to Quit " + + # Center the message and fill the width with reverse video + if len(message) < width - 1: + padding_total = width - len(message) - 1 + padding_left = padding_total // 2 + padding_right = padding_total - padding_left + full_message = " " * padding_left + message + " " * padding_right + else: + full_message = message[: width - 1] + + # Draw the banner with reverse video and bold + self.add_str( + line, 0, full_message, A_REVERSE | A_BOLD | self.colors["green"] + ) + line += 1 + + # Add blank line for separation + line += 1 + + return line + + +class TableWidget(Widget): + """Widget for rendering column headers and data rows.""" + + def __init__(self, display, colors, collector): + """ + Initialize table widget. + + Args: + display: DisplayInterface implementation + colors: Dictionary of color attributes + collector: Reference to LiveStatsCollector for accessing stats + """ + super().__init__(display, colors) + self.collector = collector + + def render(self, line, width, **kwargs): + """ + Render column headers and data rows. + + Args: + line: Starting line number + width: Available width + kwargs: Must contain 'height' and 'stats_list' keys + + Returns: + Next available line number + """ + height = kwargs["height"] + stats_list = kwargs["stats_list"] + + # Draw column headers + line, show_sample_pct, show_tottime, show_cumul_pct, show_cumtime = ( + self.draw_column_headers(line, width) + ) + column_flags = ( + show_sample_pct, + show_tottime, + show_cumul_pct, + show_cumtime, + ) + + # Draw data rows + line = self.draw_stats_rows( + line, height, width, stats_list, column_flags + ) + + return line + + def draw_column_headers(self, line, width): + """Draw column headers with sort indicators.""" + # Determine which columns to show based on width + show_sample_pct = width >= WIDTH_THRESHOLD_SAMPLE_PCT + show_tottime = width >= WIDTH_THRESHOLD_TOTTIME + show_cumul_pct = width >= WIDTH_THRESHOLD_CUMUL_PCT + show_cumtime = width >= WIDTH_THRESHOLD_CUMTIME + + sorted_header = self.colors["sorted_header"] + normal_header = self.colors["normal_header"] + + # Determine which column is sorted + sort_col = { + "nsamples": 0, + "sample_pct": 1, + "tottime": 2, + "cumul_pct": 3, + "cumtime": 4, + }.get(self.collector.sort_by, -1) + + # Build the full header line first, then draw it + # This avoids gaps between columns when using reverse video + header_parts = [] + col = 0 + + # Column 0: nsamples + text = f"{'▼nsamples' if sort_col == 0 else 'nsamples':>13} " + header_parts.append((col, text, sorted_header if sort_col == 0 else normal_header)) + col += 15 + + # Column 1: sample % + if show_sample_pct: + text = f"{'▼%' if sort_col == 1 else '%':>5} " + header_parts.append((col, text, sorted_header if sort_col == 1 else normal_header)) + col += 7 + + # Column 2: tottime + if show_tottime: + text = f"{'▼tottime' if sort_col == 2 else 'tottime':>10} " + header_parts.append((col, text, sorted_header if sort_col == 2 else normal_header)) + col += 12 + + # Column 3: cumul % + if show_cumul_pct: + text = f"{'▼%' if sort_col == 3 else '%':>5} " + header_parts.append((col, text, sorted_header if sort_col == 3 else normal_header)) + col += 7 + + # Column 4: cumtime + if show_cumtime: + text = f"{'▼cumtime' if sort_col == 4 else 'cumtime':>10} " + header_parts.append((col, text, sorted_header if sort_col == 4 else normal_header)) + col += 12 + + # Remaining headers + if col < width - 15: + remaining_space = width - col - 1 + func_width = min( + MAX_FUNC_NAME_WIDTH, + max(MIN_FUNC_NAME_WIDTH, remaining_space // 2), + ) + text = f"{'function':<{func_width}} " + header_parts.append((col, text, normal_header)) + col += func_width + 2 + + if col < width - 10: + file_text = "file:line" + padding = width - col - len(file_text) + text = file_text + " " * max(0, padding) + header_parts.append((col, text, normal_header)) + + # Draw full-width background first + self.add_str(line, 0, " " * (width - 1), normal_header) + + # Draw each header part on top + for col_pos, text, attr in header_parts: + self.add_str(line, col_pos, text.rstrip(), attr) + + return ( + line + 1, + show_sample_pct, + show_tottime, + show_cumul_pct, + show_cumtime, + ) + + def draw_stats_rows(self, line, height, width, stats_list, column_flags): + """Draw the statistics data rows.""" + show_sample_pct, show_tottime, show_cumul_pct, show_cumtime = ( + column_flags + ) + + # Get color attributes + color_file = self.colors.get("color_file", curses.A_NORMAL) + color_func = self.colors.get("color_func", curses.A_NORMAL) + + # Get trend tracker for color decisions + trend_tracker = self.collector._trend_tracker + + # Check if opcode mode is enabled for row selection highlighting + show_opcodes = getattr(self.collector, 'show_opcodes', False) + selected_row = getattr(self.collector, 'selected_row', 0) + scroll_offset = getattr(self.collector, 'scroll_offset', 0) if show_opcodes else 0 + A_REVERSE = self.display.get_attr("A_REVERSE") + A_BOLD = self.display.get_attr("A_BOLD") + + # Reserve space for opcode panel when enabled + opcode_panel_height = OPCODE_PANEL_HEIGHT if show_opcodes else 0 + + # Apply scroll offset when in opcode mode + display_stats = stats_list[scroll_offset:] if show_opcodes else stats_list + + for row_idx, stat in enumerate(display_stats): + if line >= height - FOOTER_LINES - opcode_panel_height: + break + + func = stat["func"] + direct_calls = stat["direct_calls"] + cumulative_calls = stat["cumulative_calls"] + total_time = stat["total_time"] + cumulative_time = stat["cumulative_time"] + sample_pct = stat["sample_pct"] + cum_pct = stat["cumul_pct"] + trends = stat.get("trends", {}) + + # Check if this row is selected + is_selected = show_opcodes and row_idx == selected_row + + # Helper function to get trend color + def get_trend_color(column_name): + if is_selected: + return A_REVERSE | A_BOLD + trend = trends.get(column_name, "stable") + if trend_tracker is not None and trend_tracker.enabled: + return trend_tracker.get_color(trend) + return curses.A_NORMAL + + filename, lineno, funcname = func[0], func[1], func[2] + samples_str = f"{direct_calls}/{cumulative_calls}" + col = 0 + + # Fill entire row with reverse video background for selected row + if is_selected: + self.add_str(line, 0, " " * (width - 1), A_REVERSE | A_BOLD) + + # Show selection indicator when opcode panel is enabled + if show_opcodes: + if is_selected: + self.add_str(line, col, "►", A_REVERSE | A_BOLD) + else: + self.add_str(line, col, " ", curses.A_NORMAL) + col += 2 + + # Samples column - apply trend color based on nsamples trend + nsamples_color = get_trend_color("nsamples") + self.add_str(line, col, f"{samples_str:>13} ", nsamples_color) + col += 15 + + # Sample % column + if show_sample_pct: + sample_pct_color = get_trend_color("sample_pct") + self.add_str(line, col, f"{sample_pct:>5.1f} ", sample_pct_color) + col += 7 + + # Total time column + if show_tottime: + tottime_color = get_trend_color("tottime") + self.add_str(line, col, f"{total_time:>10.3f} ", tottime_color) + col += 12 + + # Cumul % column + if show_cumul_pct: + cumul_pct_color = get_trend_color("cumul_pct") + self.add_str(line, col, f"{cum_pct:>5.1f} ", cumul_pct_color) + col += 7 + + # Cumul time column + if show_cumtime: + cumtime_color = get_trend_color("cumtime") + self.add_str(line, col, f"{cumulative_time:>10.3f} ", cumtime_color) + col += 12 + + # Function name column + if col < width - 15: + remaining_space = width - col - 1 + func_width = min( + MAX_FUNC_NAME_WIDTH, + max(MIN_FUNC_NAME_WIDTH, remaining_space // 2), + ) + + func_display = funcname + if len(funcname) > func_width: + func_display = funcname[: func_width - 3] + "..." + func_display = f"{func_display:<{func_width}}" + func_color = A_REVERSE | A_BOLD if is_selected else color_func + self.add_str(line, col, func_display, func_color) + col += func_width + 2 + + # File:line column + if col < width - 10: + simplified_path = self.collector.simplify_path(filename) + file_line = f"{simplified_path}:{lineno}" + remaining_width = width - col - 1 + file_color = A_REVERSE | A_BOLD if is_selected else color_file + self.add_str( + line, col, file_line[:remaining_width], file_color + ) + + line += 1 + + return line + + +class FooterWidget(Widget): + """Widget for rendering the footer section (legend and controls).""" + + def __init__(self, display, colors, collector): + """ + Initialize footer widget. + + Args: + display: DisplayInterface implementation + colors: Dictionary of color attributes + collector: Reference to LiveStatsCollector for accessing state + """ + super().__init__(display, colors) + self.collector = collector + + def render(self, line, width, **kwargs): + """ + Render the footer at the specified position. + + Args: + line: Starting line number (should be height - 2) + width: Available width + + Returns: + Next available line number + """ + A_DIM = self.display.get_attr("A_DIM") + A_BOLD = self.display.get_attr("A_BOLD") + + # Legend line + legend = "nsamples: direct/cumulative (direct=executing, cumulative=on stack)" + self.add_str(line, 0, legend[: width - 1], A_DIM) + line += 1 + + # Controls line with status + sort_names = { + "tottime": "Total Time", + "nsamples": "Direct Samples", + "cumtime": "Cumulative Time", + "sample_pct": "Sample %", + "cumul_pct": "Cumulative %", + } + sort_display = sort_names.get( + self.collector.sort_by, self.collector.sort_by + ) + + # Build status indicators + status = [] + if self.collector.finished: + status.append("[PROFILING FINISHED - Press 'q' to quit]") + elif self.collector.paused: + status.append("[PAUSED]") + if self.collector.filter_pattern: + status.append( + f"[Filter: {self.collector.filter_pattern} (c to clear)]" + ) + # Show trend colors status if disabled + if self.collector._trend_tracker is not None and not self.collector._trend_tracker.enabled: + status.append("[Trend colors: OFF]") + status_str = " ".join(status) + " " if status else "" + + if self.collector.finished: + footer = f"{status_str}" + else: + footer = f"{status_str}Sort: {sort_display} | 't':mode 'x':trends ←→:thread 'h':help 'q':quit" + self.add_str( + line, + 0, + footer[: width - 1], + A_BOLD + if (self.collector.paused or self.collector.finished) + else A_DIM, + ) + + return line + 1 + + def render_filter_input_prompt(self, line, width): + """Draw the filter input prompt at the bottom of the screen.""" + A_BOLD = self.display.get_attr("A_BOLD") + A_REVERSE = self.display.get_attr("A_REVERSE") + + # Draw prompt on last line + prompt = f"Function filter: {self.collector.filter_input_buffer}_" + self.add_str(line, 0, prompt[: width - 1], A_REVERSE | A_BOLD) + + +class HelpWidget(Widget): + """Widget for rendering the help screen overlay.""" + + def render(self, line, width, **kwargs): + """ + Render the help screen. + + Args: + line: Starting line number (ignored, help is centered) + width: Available width + kwargs: Must contain 'height' key + + Returns: + Next available line number (not used for overlays) + """ + height = kwargs["height"] + A_BOLD = self.display.get_attr("A_BOLD") + A_NORMAL = self.display.get_attr("A_NORMAL") + + help_lines = [ + ("Tachyon Profiler - Interactive Commands", A_BOLD), + ("", A_NORMAL), + ("Navigation & Display:", A_BOLD), + (" s - Cycle through sort modes (forward)", A_NORMAL), + (" S - Cycle through sort modes (backward)", A_NORMAL), + (" t - Toggle view mode (ALL / per-thread)", A_NORMAL), + (" x - Toggle trend colors (on/off)", A_NORMAL), + (" j/k or ↑/↓ - Select next/previous function (--opcodes)", A_NORMAL), + (" ← / → - Cycle through threads", A_NORMAL), + (" + - Faster display refresh rate", A_NORMAL), + (" - - Slower display refresh rate", A_NORMAL), + ("", A_NORMAL), + ("Control:", A_BOLD), + (" p - Freeze display (snapshot)", A_NORMAL), + (" r - Reset all statistics", A_NORMAL), + ("", A_NORMAL), + ("Filtering:", A_BOLD), + (" / - Enter function filter (substring)", A_NORMAL), + (" c - Clear filter", A_NORMAL), + (" ESC - Cancel filter input", A_NORMAL), + ("", A_NORMAL), + ("Other:", A_BOLD), + (" h or ? - Show/hide this help", A_NORMAL), + (" q - Quit profiler", A_NORMAL), + ("", A_NORMAL), + ("Press any key to close this help screen", A_BOLD), + ] + + start_line = (height - len(help_lines)) // 2 + for i, (text, attr) in enumerate(help_lines): + if start_line + i < height - 1: + col = 2 # Left-align with small margin + self.add_str(start_line + i, col, text[: width - 3], attr) + + return line # Not used for overlays + + +class OpcodePanel(Widget): + """Widget for displaying opcode statistics for a selected function.""" + + def __init__(self, display, colors, collector): + super().__init__(display, colors) + self.collector = collector + + def render(self, line, width, **kwargs): + """Render opcode statistics panel. + + Args: + line: Starting line number + width: Available width + kwargs: Must contain 'stats_list', 'height' + + Returns: + Next available line number + """ + stats_list = kwargs.get("stats_list", []) + height = kwargs.get("height", 24) + selected_row = self.collector.selected_row + scroll_offset = getattr(self.collector, 'scroll_offset', 0) + + A_BOLD = self.display.get_attr("A_BOLD") + A_NORMAL = self.display.get_attr("A_NORMAL") + color_cyan = self.colors.get("color_cyan", A_NORMAL) + color_yellow = self.colors.get("color_yellow", A_NORMAL) + color_magenta = self.colors.get("color_magenta", A_NORMAL) + + # Get the selected function from stats_list (accounting for scroll) + actual_index = scroll_offset + selected_row + if not stats_list or actual_index >= len(stats_list): + self.add_str(line, 0, "No function selected (use j/k to select)", A_NORMAL) + return line + 1 + + selected_stat = stats_list[actual_index] + func = selected_stat["func"] + filename, lineno, funcname = func + + # Get opcode stats for this function + opcode_stats = self.collector.opcode_stats.get(func, {}) + + if not opcode_stats: + self.add_str(line, 0, f"No opcode data for {funcname}() (requires --opcodes)", A_NORMAL) + return line + 1 + + # Sort opcodes by count + sorted_opcodes = sorted(opcode_stats.items(), key=lambda x: -x[1]) + total_opcode_samples = sum(opcode_stats.values()) + + # Draw header + header = f"─── Opcodes for {funcname}() " + header += "─" * max(0, width - len(header) - 1) + self.add_str(line, 0, header[:width-1], color_cyan | A_BOLD) + line += 1 + + # Calculate max samples for bar scaling + max_count = sorted_opcodes[0][1] if sorted_opcodes else 1 + + # Draw opcode rows (limit to available space) + max_rows = min(8, height - line - 3) # Leave room for footer + bar_width = 20 + + for i, (opcode_num, count) in enumerate(sorted_opcodes[:max_rows]): + if line >= height - 3: + break + + opcode_info = get_opcode_info(opcode_num) + is_specialized = opcode_info["is_specialized"] + name_display = format_opcode(opcode_num) + + pct = (count / total_opcode_samples * 100) if total_opcode_samples > 0 else 0 + + # Draw bar + bar_fill = int((count / max_count) * bar_width) if max_count > 0 else 0 + bar = "█" * bar_fill + "░" * (bar_width - bar_fill) + + # Format: [████████░░░░] LOAD_ATTR 45.2% (1234) + # Specialized opcodes shown in magenta, base opcodes in yellow + name_color = color_magenta if is_specialized else color_yellow + + row_text = f"[{bar}] {name_display:<35} {pct:>5.1f}% ({count:>6})" + self.add_str(line, 2, row_text[:width-3], name_color) + line += 1 + + # Show "..." if more opcodes exist + if len(sorted_opcodes) > max_rows: + remaining = len(sorted_opcodes) - max_rows + self.add_str(line, 2, f"... and {remaining} more opcodes", A_NORMAL) + line += 1 + + return line diff --git a/Lib/profiling/sampling/module_utils.py b/Lib/profiling/sampling/module_utils.py new file mode 100644 index 00000000000000..dfde2b28ab29a4 --- /dev/null +++ b/Lib/profiling/sampling/module_utils.py @@ -0,0 +1,102 @@ +"""Utilities for extracting module names from file paths.""" + +import os +import site +import sys +from pathlib import Path + + +def get_python_path_info(): + """Get information about Python's search paths. + + Returns: + dict: Dictionary containing stdlib path, site-packages paths, and sys.path entries. + """ + info = { + 'stdlib': None, + 'site_packages': [], + 'sys_path': [] + } + + # Get standard library path from os module location + try: + if hasattr(os, '__file__') and os.__file__: + info['stdlib'] = Path(os.__file__).parent + except (AttributeError, OSError): + pass # Silently continue if we can't determine stdlib path + + # Get site-packages directories + site_packages = [] + try: + site_packages.extend(Path(p) for p in site.getsitepackages()) + except (AttributeError, OSError): + pass # Continue without site packages if unavailable + + # Get user site-packages + try: + user_site = site.getusersitepackages() + if user_site and Path(user_site).exists(): + site_packages.append(Path(user_site)) + except (AttributeError, OSError): + pass # Continue without user site packages + + info['site_packages'] = site_packages + info['sys_path'] = [Path(p) for p in sys.path if p] + + return info + + +def extract_module_name(filename, path_info): + """Extract Python module name and type from file path. + + Args: + filename: Path to the Python file + path_info: Dictionary from get_python_path_info() + + Returns: + tuple: (module_name, module_type) where module_type is one of: + 'stdlib', 'site-packages', 'project', or 'other' + """ + if not filename: + return ('unknown', 'other') + + try: + file_path = Path(filename) + except (ValueError, OSError): + return (str(filename), 'other') + + # Check if it's in stdlib + if path_info['stdlib'] and file_path.is_relative_to(path_info['stdlib']): + return (_path_to_module(file_path.relative_to(path_info['stdlib'])), 'stdlib') + + # Check site-packages + for site_pkg in path_info['site_packages']: + if file_path.is_relative_to(site_pkg): + return (_path_to_module(file_path.relative_to(site_pkg)), 'site-packages') + + # Check other sys.path entries (project files) + if not str(file_path).startswith(('<', '[')): # Skip special files + for path_entry in path_info['sys_path']: + if file_path.is_relative_to(path_entry): + return (_path_to_module(file_path.relative_to(path_entry)), 'project') + + # Fallback: just use the filename + return (_path_to_module(file_path), 'other') + + +def _path_to_module(path): + if isinstance(path, str): + path = Path(path) + + # Remove .py extension + if path.suffix == '.py': + path = path.with_suffix('') + + # Convert path separators to dots, stripping root/drive (e.g. "/" or "C:\") + parts = [p for p in path.parts if p != path.root and p != path.drive] + + # Handle __init__ files - they represent the package itself + if parts and parts[-1] == '__init__': + parts = parts[:-1] + + return '.'.join(parts) if parts else path.stem diff --git a/Lib/profiling/sampling/opcode_utils.py b/Lib/profiling/sampling/opcode_utils.py new file mode 100644 index 00000000000000..71b35383da153d --- /dev/null +++ b/Lib/profiling/sampling/opcode_utils.py @@ -0,0 +1,94 @@ +"""Opcode utilities for bytecode-level profiler visualization. + +This module provides utilities to get opcode names and detect specialization +status using the opcode module's metadata. Used by heatmap and flamegraph +collectors to display which bytecode instructions are executing at each +source line, including Python's adaptive specialization optimizations. +""" + +import opcode + +# Build opcode name mapping: opcode number -> opcode name +# This includes both standard opcodes and specialized variants (Python 3.11+) +_OPCODE_NAMES = dict(enumerate(opcode.opname)) +if hasattr(opcode, "_specialized_opmap"): + for name, op in opcode._specialized_opmap.items(): + _OPCODE_NAMES[op] = name + +# Build deopt mapping: specialized opcode number -> base opcode number +# Python 3.11+ uses adaptive specialization where generic opcodes like +# LOAD_ATTR can be replaced at runtime with specialized variants like +# LOAD_ATTR_INSTANCE_VALUE. This mapping lets us show both forms. +_DEOPT_MAP = {} +if hasattr(opcode, "_specializations") and hasattr( + opcode, "_specialized_opmap" +): + for base_name, variant_names in opcode._specializations.items(): + base_opcode = opcode.opmap.get(base_name) + if base_opcode is not None: + for variant_name in variant_names: + variant_opcode = opcode._specialized_opmap.get(variant_name) + if variant_opcode is not None: + _DEOPT_MAP[variant_opcode] = base_opcode + + +def get_opcode_info(opcode_num): + """Get opcode name and specialization info from an opcode number. + + Args: + opcode_num: The opcode number (0-255 or higher for specialized) + + Returns: + A dict with keys: + - 'opname': The opcode name (e.g., 'LOAD_ATTR_INSTANCE_VALUE') + - 'base_opname': The base opcode name (e.g., 'LOAD_ATTR') + - 'is_specialized': True if this is a specialized instruction + """ + opname = _OPCODE_NAMES.get(opcode_num) + if opname is None: + return { + "opname": f"<{opcode_num}>", + "base_opname": f"<{opcode_num}>", + "is_specialized": False, + } + + base_opcode = _DEOPT_MAP.get(opcode_num) + if base_opcode is not None: + base_opname = _OPCODE_NAMES.get(base_opcode, f"<{base_opcode}>") + return { + "opname": opname, + "base_opname": base_opname, + "is_specialized": True, + } + + return { + "opname": opname, + "base_opname": opname, + "is_specialized": False, + } + + +def format_opcode(opcode_num): + """Format an opcode for display, showing base opcode for specialized ones. + + Args: + opcode_num: The opcode number (0-255 or higher for specialized) + + Returns: + A formatted string like 'LOAD_ATTR' or 'LOAD_ATTR_INSTANCE_VALUE (LOAD_ATTR)' + """ + info = get_opcode_info(opcode_num) + if info["is_specialized"]: + return f"{info['opname']} ({info['base_opname']})" + return info["opname"] + + +def get_opcode_mapping(): + """Get opcode name and deopt mappings for JavaScript consumption. + + Returns: + A dict with keys: + - 'names': Dict mapping opcode numbers to opcode names + - 'deopt': Dict mapping specialized opcode numbers to base opcode numbers + """ + return {"names": _OPCODE_NAMES, "deopt": _DEOPT_MAP} diff --git a/Lib/profiling/sampling/pstats_collector.py b/Lib/profiling/sampling/pstats_collector.py new file mode 100644 index 00000000000000..43b1daf2a119d4 --- /dev/null +++ b/Lib/profiling/sampling/pstats_collector.py @@ -0,0 +1,424 @@ +import collections +import marshal +import pstats +lazy from _colorize import ANSIColors + +from .collector import Collector, extract_lineno +from .constants import MICROSECONDS_PER_SECOND, PROFILING_MODE_CPU + + +class PstatsCollector(Collector): + aggregating = True + + def __init__(self, sample_interval_usec, *, skip_idle=False): + self.result = collections.defaultdict( + lambda: dict(total_rec_calls=0, direct_calls=0, cumulative_calls=0) + ) + self.stats = {} + self.sample_interval_usec = sample_interval_usec + self.callers = collections.defaultdict( + lambda: collections.defaultdict(int) + ) + self.skip_idle = skip_idle + self._seen_locations = set() + + def _process_frames(self, frames, weight=1): + """Process a single thread's frame stack.""" + if not frames: + return + + self._seen_locations.clear() + + # Process each frame in the stack to track cumulative calls + # frame.location is int, tuple (lineno, end_lineno, col_offset, end_col_offset), or None + for frame in frames: + lineno = extract_lineno(frame.location) + location = (frame.filename, lineno, frame.funcname) + if location not in self._seen_locations: + self._seen_locations.add(location) + self.result[location]["cumulative_calls"] += weight + + # The top frame gets counted as an inline call (directly executing) + top_lineno = extract_lineno(frames[0].location) + top_location = (frames[0].filename, top_lineno, frames[0].funcname) + self.result[top_location]["direct_calls"] += weight + + # Track caller-callee relationships for call graph + for i in range(1, len(frames)): + callee_frame = frames[i - 1] + caller_frame = frames[i] + + callee_lineno = extract_lineno(callee_frame.location) + caller_lineno = extract_lineno(caller_frame.location) + callee = (callee_frame.filename, callee_lineno, callee_frame.funcname) + caller = (caller_frame.filename, caller_lineno, caller_frame.funcname) + + self.callers[callee][caller] += weight + + def collect(self, stack_frames, timestamps_us=None): + weight = len(timestamps_us) if timestamps_us else 1 + for frames, _ in self._iter_stacks(stack_frames, skip_idle=self.skip_idle): + self._process_frames(frames, weight=weight) + + def export(self, filename): + self.create_stats() + self._dump_stats(filename) + + def _dump_stats(self, file): + stats_with_marker = dict(self.stats) + stats_with_marker[("__sampled__",)] = True + with open(file, "wb") as f: + marshal.dump(stats_with_marker, f) + + # Needed for compatibility with pstats.Stats + def create_stats(self): + sample_interval_sec = self.sample_interval_usec / MICROSECONDS_PER_SECOND + callers = {} + for fname, call_counts in self.result.items(): + total = call_counts["direct_calls"] * sample_interval_sec + cumulative_calls = call_counts["cumulative_calls"] + cumulative = cumulative_calls * sample_interval_sec + callers = dict(self.callers.get(fname, {})) + self.stats[fname] = ( + call_counts["direct_calls"], # cc = direct calls for sample percentage + cumulative_calls, # nc = cumulative calls for cumulative percentage + total, + cumulative, + callers, + ) + + def print_stats(self, sort=-1, limit=None, show_summary=True, mode=None): + """Print formatted statistics to stdout.""" + # Create stats object + stats = pstats.SampledStats(self).strip_dirs() + if not stats.stats: + print("No samples were collected.") + if mode == PROFILING_MODE_CPU: + print("This can happen in CPU mode when all threads are idle.") + return + + # Get the stats data + stats_list = [] + for func, ( + direct_calls, + cumulative_calls, + total_time, + cumulative_time, + callers, + ) in stats.stats.items(): + stats_list.append( + ( + func, + direct_calls, + cumulative_calls, + total_time, + cumulative_time, + callers, + ) + ) + + # Calculate total samples for percentage calculations (using direct_calls) + total_samples = sum( + direct_calls for _, direct_calls, _, _, _, _ in stats_list + ) + + # Sort based on the requested field + sort_field = sort + if sort_field == -1: # stdname + stats_list.sort(key=lambda x: str(x[0])) + elif sort_field == 0: # nsamples (direct samples) + stats_list.sort(key=lambda x: x[1], reverse=True) # direct_calls + elif sort_field == 1: # tottime + stats_list.sort(key=lambda x: x[3], reverse=True) # total_time + elif sort_field == 2: # cumtime + stats_list.sort(key=lambda x: x[4], reverse=True) # cumulative_time + elif sort_field == 3: # sample% + stats_list.sort( + key=lambda x: (x[1] / total_samples * 100) + if total_samples > 0 + else 0, + reverse=True, # direct_calls percentage + ) + elif sort_field == 4: # cumul% + stats_list.sort( + key=lambda x: (x[2] / total_samples * 100) + if total_samples > 0 + else 0, + reverse=True, # cumulative_calls percentage + ) + elif sort_field == 5: # nsamples (cumulative samples) + stats_list.sort(key=lambda x: x[2], reverse=True) # cumulative_calls + + # Apply limit if specified + if limit is not None: + stats_list = stats_list[:limit] + + # Determine the best unit for time columns based on maximum values + max_total_time = max( + (total_time for _, _, _, total_time, _, _ in stats_list), default=0 + ) + max_cumulative_time = max( + (cumulative_time for _, _, _, _, cumulative_time, _ in stats_list), + default=0, + ) + + total_time_unit, total_time_scale = self._determine_best_unit(max_total_time) + cumulative_time_unit, cumulative_time_scale = self._determine_best_unit( + max_cumulative_time + ) + + # Define column widths for consistent alignment + col_widths = { + "nsamples": 15, # "nsamples" column (inline/cumulative format) + "sample_pct": 8, # "sample%" column + "tottime": max(12, len(f"tottime ({total_time_unit})")), + "cum_pct": 8, # "cumul%" column + "cumtime": max(12, len(f"cumtime ({cumulative_time_unit})")), + } + + # Print header with colors and proper alignment + print(f"{ANSIColors.BOLD_BLUE}Profile Stats:{ANSIColors.RESET}") + + header_nsamples = f"{ANSIColors.BOLD_BLUE}{'nsamples':>{col_widths['nsamples']}}{ANSIColors.RESET}" + header_sample_pct = f"{ANSIColors.BOLD_BLUE}{'sample%':>{col_widths['sample_pct']}}{ANSIColors.RESET}" + header_tottime = f"{ANSIColors.BOLD_BLUE}{f'tottime ({total_time_unit})':>{col_widths['tottime']}}{ANSIColors.RESET}" + header_cum_pct = f"{ANSIColors.BOLD_BLUE}{'cumul%':>{col_widths['cum_pct']}}{ANSIColors.RESET}" + header_cumtime = f"{ANSIColors.BOLD_BLUE}{f'cumtime ({cumulative_time_unit})':>{col_widths['cumtime']}}{ANSIColors.RESET}" + header_filename = ( + f"{ANSIColors.BOLD_BLUE}filename:lineno(function){ANSIColors.RESET}" + ) + + print( + f"{header_nsamples} {header_sample_pct} {header_tottime} {header_cum_pct} {header_cumtime} {header_filename}" + ) + + # Print each line with proper alignment + for ( + func, + direct_calls, + cumulative_calls, + total_time, + cumulative_time, + callers, + ) in stats_list: + # Calculate percentages + sample_pct = ( + (direct_calls / total_samples * 100) if total_samples > 0 else 0 + ) + cum_pct = ( + (cumulative_calls / total_samples * 100) + if total_samples > 0 + else 0 + ) + + # Format values with proper alignment - always use A/B format + nsamples_str = f"{direct_calls}/{cumulative_calls}" + nsamples_str = f"{nsamples_str:>{col_widths['nsamples']}}" + sample_pct_str = f"{sample_pct:{col_widths['sample_pct']}.1f}" + tottime = f"{total_time * total_time_scale:{col_widths['tottime']}.3f}" + cum_pct_str = f"{cum_pct:{col_widths['cum_pct']}.1f}" + cumtime = f"{cumulative_time * cumulative_time_scale:{col_widths['cumtime']}.3f}" + + # Format the function name with colors + func_name = ( + f"{ANSIColors.GREEN}{func[0]}{ANSIColors.RESET}:" + f"{ANSIColors.YELLOW}{func[1]}{ANSIColors.RESET}(" + f"{ANSIColors.CYAN}{func[2]}{ANSIColors.RESET})" + ) + + # Print the formatted line with consistent spacing + print( + f"{nsamples_str} {sample_pct_str} {tottime} {cum_pct_str} {cumtime} {func_name}" + ) + + # Print legend + print(f"\n{ANSIColors.BOLD_BLUE}Legend:{ANSIColors.RESET}") + print( + f" {ANSIColors.YELLOW}nsamples{ANSIColors.RESET}: Direct/Cumulative samples (direct executing / on call stack)" + ) + print( + f" {ANSIColors.YELLOW}sample%{ANSIColors.RESET}: Percentage of total samples this function was directly executing" + ) + print( + f" {ANSIColors.YELLOW}tottime{ANSIColors.RESET}: Estimated total time spent directly in this function" + ) + print( + f" {ANSIColors.YELLOW}cumul%{ANSIColors.RESET}: Percentage of total samples when this function was on the call stack" + ) + print( + f" {ANSIColors.YELLOW}cumtime{ANSIColors.RESET}: Estimated cumulative time (including time in called functions)" + ) + print( + f" {ANSIColors.YELLOW}filename:lineno(function){ANSIColors.RESET}: Function location and name" + ) + + # Print summary of interesting functions if enabled + if show_summary and stats_list: + self._print_summary(stats_list, total_samples) + + @staticmethod + def _determine_best_unit(max_value): + """Determine the best unit (s, ms, μs) and scale factor for a maximum value.""" + if max_value >= 1.0: + return "s", 1.0 + elif max_value >= 0.001: + return "ms", 1000.0 + else: + return "μs", float(MICROSECONDS_PER_SECOND) + + def _print_summary(self, stats_list, total_samples): + """Print summary of interesting functions.""" + print( + f"\n{ANSIColors.BOLD_BLUE}Summary of Interesting Functions:{ANSIColors.RESET}" + ) + + # Aggregate stats by fully qualified function name (ignoring line numbers) + func_aggregated = {} + for ( + func, + direct_calls, + cumulative_calls, + total_time, + cumulative_time, + callers, + ) in stats_list: + # Use filename:function_name as the key to get fully qualified name + qualified_name = f"{func[0]}:{func[2]}" + if qualified_name not in func_aggregated: + func_aggregated[qualified_name] = [ + 0, + 0, + 0, + 0, + ] # direct_calls, cumulative_calls, total_time, cumulative_time + func_aggregated[qualified_name][0] += direct_calls + func_aggregated[qualified_name][1] += cumulative_calls + func_aggregated[qualified_name][2] += total_time + func_aggregated[qualified_name][3] += cumulative_time + + # Convert aggregated data back to list format for processing + aggregated_stats = [] + for qualified_name, ( + prim_calls, + total_calls, + total_time, + cumulative_time, + ) in func_aggregated.items(): + # Parse the qualified name back to filename and function name + if ":" in qualified_name: + filename, func_name = qualified_name.rsplit(":", 1) + else: + filename, func_name = "", qualified_name + # Create a dummy func tuple with filename and function name for display + dummy_func = (filename, "", func_name) + aggregated_stats.append( + ( + dummy_func, + prim_calls, + total_calls, + total_time, + cumulative_time, + {}, + ) + ) + + # Determine best units for summary metrics + max_total_time = max( + (total_time for _, _, _, total_time, _, _ in aggregated_stats), + default=0, + ) + max_cumulative_time = max( + ( + cumulative_time + for _, _, _, _, cumulative_time, _ in aggregated_stats + ), + default=0, + ) + + total_unit, total_scale = self._determine_best_unit(max_total_time) + cumulative_unit, cumulative_scale = self._determine_best_unit( + max_cumulative_time + ) + + def _format_func_name(func): + """Format function name with colors.""" + return ( + f"{ANSIColors.GREEN}{func[0]}{ANSIColors.RESET}:" + f"{ANSIColors.YELLOW}{func[1]}{ANSIColors.RESET}(" + f"{ANSIColors.CYAN}{func[2]}{ANSIColors.RESET})" + ) + + def _print_top_functions(stats_list, title, key_func, format_line, n=3): + """Print top N functions sorted by key_func with formatted output.""" + print(f"\n{ANSIColors.BOLD_BLUE}{title}:{ANSIColors.RESET}") + sorted_stats = sorted(stats_list, key=key_func, reverse=True) + for stat in sorted_stats[:n]: + if line := format_line(stat): + print(f" {line}") + + # Functions with highest direct/cumulative ratio (hot spots) + def format_hotspots(stat): + func, direct_calls, cumulative_calls, total_time, _, _ = stat + if direct_calls > 0 and cumulative_calls > 0: + ratio = direct_calls / cumulative_calls + direct_pct = ( + (direct_calls / total_samples * 100) + if total_samples > 0 + else 0 + ) + return ( + f"{ratio:.3f} direct/cumulative ratio, " + f"{direct_pct:.1f}% direct samples: {_format_func_name(func)}" + ) + return None + + _print_top_functions( + aggregated_stats, + "Functions with Highest Direct/Cumulative Ratio (Hot Spots)", + key_func=lambda x: (x[1] / x[2]) if x[2] > 0 else 0, + format_line=format_hotspots, + ) + + # Functions with highest call frequency (cumulative/direct difference) + def format_call_frequency(stat): + func, direct_calls, cumulative_calls, total_time, _, _ = stat + if cumulative_calls > direct_calls: + call_frequency = cumulative_calls - direct_calls + cum_pct = ( + (cumulative_calls / total_samples * 100) + if total_samples > 0 + else 0 + ) + return ( + f"{call_frequency:d} indirect calls, " + f"{cum_pct:.1f}% total stack presence: {_format_func_name(func)}" + ) + return None + + _print_top_functions( + aggregated_stats, + "Functions with Highest Call Frequency (Indirect Calls)", + key_func=lambda x: x[2] - x[1], # Sort by (cumulative - direct) + format_line=format_call_frequency, + ) + + # Functions with highest cumulative-to-direct multiplier (call magnification) + def format_call_magnification(stat): + func, direct_calls, cumulative_calls, total_time, _, _ = stat + if direct_calls > 0 and cumulative_calls > direct_calls: + multiplier = cumulative_calls / direct_calls + indirect_calls = cumulative_calls - direct_calls + return ( + f"{multiplier:.1f}x call magnification, " + f"{indirect_calls:d} indirect calls from {direct_calls:d} direct: {_format_func_name(func)}" + ) + return None + + _print_top_functions( + aggregated_stats, + "Functions with Highest Call Magnification (Cumulative/Direct)", + key_func=lambda x: (x[2] / x[1]) + if x[1] > 0 + else 0, # Sort by cumulative/direct ratio + format_line=format_call_magnification, + ) diff --git a/Lib/profiling/sampling/sample.py b/Lib/profiling/sampling/sample.py new file mode 100644 index 00000000000000..50ccc57566d70d --- /dev/null +++ b/Lib/profiling/sampling/sample.py @@ -0,0 +1,659 @@ +import _remote_debugging +import contextlib +import os +import statistics +import sys +import sysconfig +import time +from collections import deque +lazy from _colorize import ANSIColors + +from .pstats_collector import PstatsCollector +from .stack_collector import CollapsedStackCollector, FlamegraphCollector +from .heatmap_collector import HeatmapCollector +from .gecko_collector import GeckoCollector +from .binary_collector import BinaryCollector + + +@contextlib.contextmanager +def _pause_threads(unwinder, blocking): + """Context manager to pause/resume threads around sampling if blocking is True.""" + if blocking: + unwinder.pause_threads() + try: + yield + finally: + unwinder.resume_threads() + else: + yield + + +from .constants import ( + PROFILING_MODE_WALL, + PROFILING_MODE_CPU, + PROFILING_MODE_GIL, + PROFILING_MODE_ALL, + PROFILING_MODE_EXCEPTION, +) +from ._format_utils import fmt +try: + from .live_collector import LiveStatsCollector +except ImportError: + LiveStatsCollector = None + +_FREE_THREADED_BUILD = sysconfig.get_config_var("Py_GIL_DISABLED") is not None + +# Minimum number of samples required before showing the TUI +# If fewer samples are collected, we skip the TUI and just print a message +MIN_SAMPLES_FOR_TUI = 200 + +# Maximum number of consecutive identical samples to keep before flushing. +MAX_PENDING_SAMPLES = 8192 + + +def _resolve_python_pid(pid): + """On Windows, if pid is a venvlauncher process, return the child Python PID. + + The venvlauncher (used as python.exe in venvs) spawns the real Python + interpreter as a child process via CreateProcessW. The RemoteUnwinder + needs the child's PID, not the launcher's. + + Returns the original pid if not on Windows, not a venv launcher, + or no child process is found. + """ + if os.name != "nt" or sys.prefix == sys.base_prefix: + return pid + try: + children = _remote_debugging.get_child_pids(pid, recursive=False) + python_children = [ + child for child in children + if _remote_debugging.is_python_process(child) + ] + if len(python_children) == 1: + return python_children[0] + except (OSError, RuntimeError) as err: + raise SystemExit( + f"Failed to initialize profiler from virtualenv: {err}\n" + f"Try running with the base interpreter: {sys._base_executable}" + ) from err + return pid + + +class SampleProfiler: + def __init__(self, pid, sample_interval_usec, all_threads, *, mode=PROFILING_MODE_WALL, native=False, gc=True, opcodes=False, skip_non_matching_threads=True, collect_stats=False, blocking=False): + self.pid = _resolve_python_pid(pid) + self.sample_interval_usec = sample_interval_usec + self.all_threads = all_threads + self.mode = mode # Store mode for later use + self.collect_stats = collect_stats + self.blocking = blocking + try: + self.unwinder = self._new_unwinder(native, gc, opcodes, skip_non_matching_threads) + except RuntimeError as err: + raise SystemExit(err) from err + # Track sample intervals and total sample count + self.sample_intervals = deque(maxlen=100) + self.total_samples = 0 + self.realtime_stats = False + + def _new_unwinder(self, native, gc, opcodes, skip_non_matching_threads): + kwargs = {} + if _FREE_THREADED_BUILD or self.all_threads: + kwargs['all_threads'] = self.all_threads + else: + kwargs['only_active_thread'] = bool(self.all_threads) + + return _remote_debugging.RemoteUnwinder( + self.pid, + mode=self.mode, + native=native, + gc=gc, + opcodes=opcodes, + skip_non_matching_threads=skip_non_matching_threads, + cache_frames=True, + stats=self.collect_stats, + **kwargs + ) + + def _get_stack_trace(self, async_aware=None): + with _pause_threads(self.unwinder, self.blocking): + if async_aware == "all": + return self.unwinder.get_all_awaited_by() + if async_aware == "running": + return self.unwinder.get_async_stack_trace() + return self.unwinder.get_stack_trace() + + def dump_stack(self, *, async_aware=None): + """Return a single stack snapshot from the target process.""" + return self._get_stack_trace(async_aware=async_aware) + + def sample(self, collector, duration_sec=None, *, async_aware=False): + sample_interval_sec = self.sample_interval_usec / 1_000_000 + num_samples = 0 + errors = 0 + interrupted = False + running_time_sec = 0 + start_time = next_time = time.perf_counter() + last_sample_time = start_time + realtime_update_interval = 1.0 # Update every second + last_realtime_update = start_time + aggregating = getattr(collector, 'aggregating', False) is True + prev_stack = None + pending_count = 0 + pending_timestamps = [] if aggregating else None + + def flush_pending(): + nonlocal pending_count, pending_timestamps + if pending_count == 0: + return + pending_count = 0 + ts = pending_timestamps + pending_timestamps = [] + collector.collect(prev_stack, timestamps_us=ts) + + try: + while duration_sec is None or running_time_sec < duration_sec: + # Check if live collector wants to stop + if hasattr(collector, 'running') and not collector.running: + break + + current_time = time.perf_counter() + current_time_us = int(current_time * 1_000_000) + if next_time > current_time: + sleep_time = (next_time - current_time) * 0.9 + if sleep_time > 0.0001: + time.sleep(sleep_time) + elif next_time < current_time: + try: + stack_frames = self._get_stack_trace( + async_aware=async_aware + ) + if aggregating: + if stack_frames != prev_stack: + flush_pending() + prev_stack = stack_frames + pending_count += 1 + pending_timestamps.append(current_time_us) + if pending_count >= MAX_PENDING_SAMPLES: + flush_pending() + else: + collector.collect(stack_frames) + except ProcessLookupError as e: + running_time_sec = current_time - start_time + break + except (RuntimeError, UnicodeDecodeError, MemoryError, OSError): + flush_pending() + collector.collect_failed_sample() + errors += 1 + prev_stack = None + except Exception as e: + if not _is_process_running(self.pid): + break + raise e from None + + # Track actual sampling intervals for real-time stats + if num_samples > 0: + actual_interval = current_time - last_sample_time + self.sample_intervals.append( + 1.0 / actual_interval + ) # Convert to Hz + self.total_samples += 1 + + # Print real-time statistics if enabled + if ( + self.realtime_stats + and (current_time - last_realtime_update) + >= realtime_update_interval + ): + self._print_realtime_stats() + last_realtime_update = current_time + + last_sample_time = current_time + num_samples += 1 + next_time += sample_interval_sec + + running_time_sec = time.perf_counter() - start_time + except KeyboardInterrupt: + interrupted = True + running_time_sec = time.perf_counter() - start_time + print("Interrupted by user.") + finally: + flush_pending() + + # Clear real-time stats line if it was being displayed + if self.realtime_stats and len(self.sample_intervals) > 0: + print() # Add newline after real-time stats + + sample_rate = num_samples / running_time_sec if running_time_sec > 0 else 0 + error_rate = (errors / num_samples) * 100 if num_samples > 0 else 0 + expected_samples = int(running_time_sec / sample_interval_sec) + missed_samples = (expected_samples - num_samples) / expected_samples * 100 if expected_samples > 0 else 0 + + # Don't print stats for live mode (curses is handling display) + is_live_mode = LiveStatsCollector is not None and isinstance(collector, LiveStatsCollector) + if not is_live_mode: + s = "" if num_samples == 1 else "s" + print(f"Captured {num_samples:n} sample{s} in {fmt(running_time_sec, 2)} seconds") + print(f"Sample rate: {fmt(sample_rate, 2)} samples/sec") + print(f"Error rate: {fmt(error_rate, 2)}") + + # Print unwinder stats if stats collection is enabled + if self.collect_stats: + self._print_unwinder_stats() + + if isinstance(collector, BinaryCollector): + self._print_binary_stats(collector) + + # Pass stats to flamegraph collector if it's the right type + if hasattr(collector, 'set_stats'): + collector.set_stats(self.sample_interval_usec, running_time_sec, sample_rate, error_rate, missed_samples, mode=self.mode) + + if num_samples < expected_samples and not is_live_mode and not interrupted: + print( + f"Warning: missed {expected_samples - num_samples} samples " + f"from the expected total of {expected_samples} " + f"({fmt((expected_samples - num_samples) / expected_samples * 100, 2)}%)" + ) + + def _print_realtime_stats(self): + """Print real-time sampling statistics.""" + if len(self.sample_intervals) < 2: + return + + # Calculate statistics on the Hz values (deque automatically maintains rolling window) + hz_values = list(self.sample_intervals) + mean_hz = statistics.mean(hz_values) + min_hz = min(hz_values) + max_hz = max(hz_values) + + # Calculate microseconds per sample for all metrics (1/Hz * 1,000,000) + mean_us_per_sample = (1.0 / mean_hz) * 1_000_000 if mean_hz > 0 else 0 + min_us_per_sample = ( + (1.0 / max_hz) * 1_000_000 if max_hz > 0 else 0 + ) # Min time = Max Hz + max_us_per_sample = ( + (1.0 / min_hz) * 1_000_000 if min_hz > 0 else 0 + ) # Max time = Min Hz + + # Build cache stats string if stats collection is enabled + cache_stats_str = "" + if self.collect_stats: + try: + stats = self.unwinder.get_stats() + hits = stats.get('frame_cache_hits', 0) + partial = stats.get('frame_cache_partial_hits', 0) + misses = stats.get('frame_cache_misses', 0) + total = hits + partial + misses + if total > 0: + hit_pct = (hits + partial) / total * 100 + cache_stats_str = f" {ANSIColors.MAGENTA}Cache: {fmt(hit_pct)}% ({hits}+{partial}/{misses}){ANSIColors.RESET}" + except RuntimeError: + pass + + # Clear line and print stats + print( + f"\r\033[K{ANSIColors.BOLD_BLUE}Stats:{ANSIColors.RESET} " + f"{ANSIColors.YELLOW}{fmt(mean_hz)}Hz ({fmt(mean_us_per_sample)}µs){ANSIColors.RESET} " + f"{ANSIColors.GREEN}Min: {fmt(min_hz)}Hz{ANSIColors.RESET} " + f"{ANSIColors.RED}Max: {fmt(max_hz)}Hz{ANSIColors.RESET} " + f"{ANSIColors.CYAN}N={self.total_samples}{ANSIColors.RESET}" + f"{cache_stats_str}", + end="", + flush=True, + ) + + def _print_unwinder_stats(self): + """Print unwinder statistics including cache performance.""" + try: + stats = self.unwinder.get_stats() + except RuntimeError: + return # Stats not enabled + + print(f"\n{ANSIColors.BOLD_BLUE}{'='*50}{ANSIColors.RESET}") + print(f"{ANSIColors.BOLD_BLUE}Unwinder Statistics:{ANSIColors.RESET}") + + # Frame cache stats + total_samples = stats.get('total_samples', 0) + frame_cache_hits = stats.get('frame_cache_hits', 0) + frame_cache_partial_hits = stats.get('frame_cache_partial_hits', 0) + frame_cache_misses = stats.get('frame_cache_misses', 0) + total_lookups = frame_cache_hits + frame_cache_partial_hits + frame_cache_misses + + # Calculate percentages + hits_pct = (frame_cache_hits / total_lookups * 100) if total_lookups > 0 else 0 + partial_pct = (frame_cache_partial_hits / total_lookups * 100) if total_lookups > 0 else 0 + misses_pct = (frame_cache_misses / total_lookups * 100) if total_lookups > 0 else 0 + + print(f" {ANSIColors.CYAN}Frame Cache:{ANSIColors.RESET}") + print(f" Total samples: {total_samples:n}") + print(f" Full hits: {frame_cache_hits:n} ({ANSIColors.GREEN}{fmt(hits_pct)}%{ANSIColors.RESET})") + print(f" Partial hits: {frame_cache_partial_hits:n} ({ANSIColors.YELLOW}{fmt(partial_pct)}%{ANSIColors.RESET})") + print(f" Misses: {frame_cache_misses:n} ({ANSIColors.RED}{fmt(misses_pct)}%{ANSIColors.RESET})") + + # Frame read stats + frames_from_cache = stats.get('frames_read_from_cache', 0) + frames_from_memory = stats.get('frames_read_from_memory', 0) + total_frames = frames_from_cache + frames_from_memory + cache_frame_pct = (frames_from_cache / total_frames * 100) if total_frames > 0 else 0 + memory_frame_pct = (frames_from_memory / total_frames * 100) if total_frames > 0 else 0 + + print(f" {ANSIColors.CYAN}Frame Reads:{ANSIColors.RESET}") + print(f" From cache: {frames_from_cache:n} ({ANSIColors.GREEN}{fmt(cache_frame_pct)}%{ANSIColors.RESET})") + print(f" From memory: {frames_from_memory:n} ({ANSIColors.RED}{fmt(memory_frame_pct)}%{ANSIColors.RESET})") + + # Code object cache stats + code_hits = stats.get('code_object_cache_hits', 0) + code_misses = stats.get('code_object_cache_misses', 0) + total_code = code_hits + code_misses + code_hits_pct = (code_hits / total_code * 100) if total_code > 0 else 0 + code_misses_pct = (code_misses / total_code * 100) if total_code > 0 else 0 + + print(f" {ANSIColors.CYAN}Code Object Cache:{ANSIColors.RESET}") + print(f" Hits: {code_hits:n} ({ANSIColors.GREEN}{fmt(code_hits_pct)}%{ANSIColors.RESET})") + print(f" Misses: {code_misses:n} ({ANSIColors.RED}{fmt(code_misses_pct)}%{ANSIColors.RESET})") + + batched_attempts = stats.get('batched_read_attempts', 0) + batched_successes = stats.get('batched_read_successes', 0) + batched_misses = stats.get('batched_read_misses', 0) + segments_requested = stats.get('batched_read_segments_requested', 0) + segments_completed = stats.get('batched_read_segments_completed', 0) + if batched_attempts > 0: + batched_success_rate = stats.get('batched_read_success_rate', 0.0) + batched_miss_rate = 100.0 - batched_success_rate + segment_completion_rate = stats.get( + 'batched_read_segment_completion_rate', 0.0 + ) + + print(f" {ANSIColors.CYAN}Batched Reads:{ANSIColors.RESET}") + print(f" Attempts: {batched_attempts:n}") + print( + f" Successes: {batched_successes:n} " + f"({ANSIColors.GREEN}{fmt(batched_success_rate)}%{ANSIColors.RESET})" + ) + print( + f" Misses: {batched_misses:n} " + f"({ANSIColors.RED}{fmt(batched_miss_rate)}%{ANSIColors.RESET})" + ) + print( + f" Segments read: {segments_completed:n}/{segments_requested:n} " + f"({ANSIColors.GREEN}{fmt(segment_completion_rate)}%{ANSIColors.RESET})" + ) + + # Memory operations + memory_reads = stats.get('memory_reads', 0) + memory_bytes = stats.get('memory_bytes_read', 0) + if memory_bytes >= 1024 * 1024: + memory_str = f"{fmt(memory_bytes / (1024 * 1024))} MB" + elif memory_bytes >= 1024: + memory_str = f"{fmt(memory_bytes / 1024)} KB" + else: + memory_str = f"{memory_bytes} B" + print(f" {ANSIColors.CYAN}Memory:{ANSIColors.RESET}") + print(f" Read operations: {memory_reads:n} ({memory_str})") + + # Stale invalidations + stale_invalidations = stats.get('stale_cache_invalidations', 0) + if stale_invalidations > 0: + print(f" {ANSIColors.YELLOW}Stale cache invalidations: {stale_invalidations}{ANSIColors.RESET}") + + def _print_binary_stats(self, collector): + """Print binary I/O encoding statistics.""" + try: + stats = collector.get_stats() + except (ValueError, RuntimeError): + return # Collector closed or stats unavailable + + print(f" {ANSIColors.CYAN}Binary Encoding:{ANSIColors.RESET}") + + repeat_records = stats.get('repeat_records', 0) + repeat_samples = stats.get('repeat_samples', 0) + full_records = stats.get('full_records', 0) + suffix_records = stats.get('suffix_records', 0) + pop_push_records = stats.get('pop_push_records', 0) + total_records = stats.get('total_records', 0) + + if total_records > 0: + repeat_pct = repeat_records / total_records * 100 + full_pct = full_records / total_records * 100 + suffix_pct = suffix_records / total_records * 100 + pop_push_pct = pop_push_records / total_records * 100 + else: + repeat_pct = full_pct = suffix_pct = pop_push_pct = 0 + + print(f" Records: {total_records:,}") + print(f" RLE repeat: {repeat_records:,} ({ANSIColors.GREEN}{repeat_pct:.1f}%{ANSIColors.RESET}) [{repeat_samples:,} samples]") + print(f" Full stack: {full_records:,} ({full_pct:.1f}%)") + print(f" Suffix match: {suffix_records:,} ({suffix_pct:.1f}%)") + print(f" Pop-push: {pop_push_records:,} ({pop_push_pct:.1f}%)") + + frames_written = stats.get('total_frames_written', 0) + frames_saved = stats.get('frames_saved', 0) + compression_pct = stats.get('frame_compression_pct', 0) + + print(f" {ANSIColors.CYAN}Frame Efficiency:{ANSIColors.RESET}") + print(f" Frames written: {frames_written:,}") + print(f" Frames saved: {frames_saved:,} ({ANSIColors.GREEN}{compression_pct:.1f}%{ANSIColors.RESET})") + + bytes_written = stats.get('bytes_written', 0) + if bytes_written >= 1024 * 1024: + bytes_str = f"{bytes_written / (1024 * 1024):.1f} MB" + elif bytes_written >= 1024: + bytes_str = f"{bytes_written / 1024:.1f} KB" + else: + bytes_str = f"{bytes_written} B" + print(f" Bytes (pre-zstd): {bytes_str}") + + +def _is_process_running(pid): + if pid <= 0: + return False + if os.name == "posix": + try: + os.kill(pid, 0) + return True + except ProcessLookupError: + return False + except PermissionError: + # EPERM means process exists but we can't signal it + return True + elif sys.platform == "win32": + try: + _remote_debugging.RemoteUnwinder(pid) + except Exception: + return False + return True + else: + raise ValueError(f"Unsupported platform: {sys.platform}") + + +def sample( + pid, + collector, + *, + duration_sec=None, + all_threads=False, + realtime_stats=False, + mode=PROFILING_MODE_WALL, + async_aware=None, + native=False, + gc=True, + opcodes=False, + blocking=False, +): + """Sample a process using the provided collector. + + Args: + pid: Process ID to sample + collector: Collector instance to use for gathering samples + duration_sec: How long to sample for (seconds), or None to run until + the process exits or interrupted + all_threads: Whether to sample all threads + realtime_stats: Whether to print real-time sampling statistics + mode: Profiling mode - WALL (all samples), CPU (only when on CPU), + GIL (only when holding GIL), ALL (includes GIL and CPU status), + EXCEPTION (only when thread has an active exception) + native: Whether to include native frames + gc: Whether to include GC frames + opcodes: Whether to include opcode information + blocking: Whether to stop all threads before sampling for consistent snapshots + + Returns: + The collector with collected samples + """ + # Get sample interval from collector + sample_interval_usec = collector.sample_interval_usec + + # PROFILING_MODE_ALL implies no skipping at all + if mode == PROFILING_MODE_ALL: + skip_non_matching_threads = False + else: + # For most modes, skip non-matching threads + # Gecko collector overrides this by setting skip_idle=False + skip_non_matching_threads = True + + profiler = SampleProfiler( + pid, + sample_interval_usec, + all_threads=all_threads, + mode=mode, + native=native, + gc=gc, + opcodes=opcodes, + skip_non_matching_threads=skip_non_matching_threads, + collect_stats=realtime_stats, + blocking=blocking, + ) + profiler.realtime_stats = realtime_stats + + # Run the sampling + profiler.sample(collector, duration_sec, async_aware=async_aware) + + return collector + + +def dump_stack( + pid, + *, + all_threads=False, + mode=PROFILING_MODE_ALL, + async_aware=None, + native=False, + gc=True, + opcodes=False, + blocking=False, +): + """Return a single stack snapshot from a process.""" + if mode == PROFILING_MODE_ALL: + skip_non_matching_threads = False + else: + skip_non_matching_threads = True + + profiler = SampleProfiler( + pid, + sample_interval_usec=1, + all_threads=all_threads, + mode=mode, + native=native, + gc=gc, + opcodes=opcodes, + skip_non_matching_threads=skip_non_matching_threads, + blocking=blocking, + ) + return profiler.dump_stack(async_aware=async_aware) + + +def sample_live( + pid, + collector, + *, + duration_sec=None, + all_threads=False, + realtime_stats=False, + mode=PROFILING_MODE_WALL, + async_aware=None, + native=False, + gc=True, + opcodes=False, + blocking=False, +): + """Sample a process in live/interactive mode with curses TUI. + + Args: + pid: Process ID to sample + collector: LiveStatsCollector instance + duration_sec: How long to sample for (seconds) + all_threads: Whether to sample all threads + realtime_stats: Whether to print real-time sampling statistics + mode: Profiling mode - WALL (all samples), CPU (only when on CPU), + GIL (only when holding GIL), ALL (includes GIL and CPU status), + EXCEPTION (only when thread has an active exception) + native: Whether to include native frames + gc: Whether to include GC frames + opcodes: Whether to include opcode information + blocking: Whether to stop all threads before sampling for consistent snapshots + + Returns: + The collector with collected samples + """ + import curses + + # Check if process is alive before doing any heavy initialization + if not _is_process_running(pid): + print(f"No samples collected - process {pid} exited before profiling could begin.", file=sys.stderr) + return collector + + # Get sample interval from collector + sample_interval_usec = collector.sample_interval_usec + + # PROFILING_MODE_ALL implies no skipping at all + if mode == PROFILING_MODE_ALL: + skip_non_matching_threads = False + else: + skip_non_matching_threads = True + + profiler = SampleProfiler( + pid, + sample_interval_usec, + all_threads=all_threads, + mode=mode, + native=native, + gc=gc, + opcodes=opcodes, + skip_non_matching_threads=skip_non_matching_threads, + collect_stats=realtime_stats, + blocking=blocking, + ) + profiler.realtime_stats = realtime_stats + + def curses_wrapper_func(stdscr): + collector.init_curses(stdscr) + try: + profiler.sample(collector, duration_sec, async_aware=async_aware) + # If too few samples were collected, exit cleanly without showing TUI + if collector.successful_samples < MIN_SAMPLES_FOR_TUI: + # Clear screen before exiting to avoid visual artifacts + stdscr.clear() + stdscr.refresh() + return + # Mark as finished and keep the TUI running until user presses 'q' + collector.mark_finished() + # Keep processing input until user quits + while collector.running: + collector._handle_input() + time.sleep(0.05) # Small sleep to avoid busy waiting + finally: + collector.cleanup_curses() + + try: + curses.wrapper(curses_wrapper_func) + except KeyboardInterrupt: + pass + + # If too few samples were collected, print a message + if collector.successful_samples < MIN_SAMPLES_FOR_TUI: + if collector.successful_samples == 0: + print(f"No samples collected - process {pid} exited before profiling could begin.", file=sys.stderr) + else: + print(f"Only {collector.successful_samples} sample(s) collected (minimum {MIN_SAMPLES_FOR_TUI} required for TUI) - process {pid} exited too quickly.", file=sys.stderr) + + return collector diff --git a/Lib/profiling/sampling/stack_collector.py b/Lib/profiling/sampling/stack_collector.py new file mode 100644 index 00000000000000..42281dc6454c83 --- /dev/null +++ b/Lib/profiling/sampling/stack_collector.py @@ -0,0 +1,742 @@ +import base64 +import collections +import functools +import importlib.resources +import json +import linecache +import os +import sys +import sysconfig + +from ._css_utils import get_combined_css +from .collector import Collector, extract_lineno +from .opcode_utils import get_opcode_mapping +from .string_table import StringTable +from .module_utils import extract_module_name, get_python_path_info + + +class StackTraceCollector(Collector): + aggregating = True + + def __init__(self, sample_interval_usec, *, skip_idle=False): + self.sample_interval_usec = sample_interval_usec + self.skip_idle = skip_idle + + def collect(self, stack_frames, timestamps_us=None): + weight = len(timestamps_us) if timestamps_us else 1 + for frames, thread_id in self._iter_stacks(stack_frames, skip_idle=self.skip_idle): + self.process_frames(frames, thread_id, weight=weight) + + def process_frames(self, frames, thread_id, weight=1): + pass + + +class CollapsedStackCollector(StackTraceCollector): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.stack_counter = collections.Counter() + + def process_frames(self, frames, thread_id, weight=1): + # Extract only (filename, lineno, funcname) - opcode not needed for collapsed stacks + # frame is (filename, location, funcname, opcode) + call_tree = tuple( + (f[0], extract_lineno(f[1]), f[2]) for f in reversed(frames) + ) + self.stack_counter[(call_tree, thread_id)] += weight + + def export(self, filename): + lines = [] + for (call_tree, thread_id), count in self.stack_counter.items(): + parts = [f"tid:{thread_id}"] + for file, line, func in call_tree: + # This is what pstats does for "special" frames: + if file == "~" and line == 0: + part = func + else: + part = f"{os.path.basename(file)}:{func}:{line}" + parts.append(part) + stack_str = ";".join(parts) + lines.append((stack_str, count)) + + lines.sort(key=lambda x: (-x[1], x[0])) + + with open(filename, "w") as f: + for stack, count in lines: + f.write(f"{stack} {count}\n") + print(f"Collapsed stack output written to {filename}") + + +class FlamegraphCollector(StackTraceCollector): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.stats = {} + self._root = {"samples": 0, "children": {}, "threads": set()} + self._total_samples = 0 + self._sample_count = 0 # Track actual number of samples (not thread traces) + self._func_intern = {} + self._string_table = StringTable() + self._module_cache = {} + self._all_threads = set() + + # Thread status statistics (similar to LiveStatsCollector) + self.thread_status_counts = { + "has_gil": 0, + "on_cpu": 0, + "gil_requested": 0, + "unknown": 0, + "has_exception": 0, + "total": 0, + } + self.samples_with_gc_frames = 0 + + # Per-thread statistics + self.per_thread_stats = {} # {thread_id: {has_gil, on_cpu, gil_requested, unknown, has_exception, total, gc_samples}} + + def collect(self, stack_frames, timestamps_us=None): + """Override to track thread status statistics before processing frames.""" + # Weight is number of timestamps (samples with identical stack) + weight = len(timestamps_us) if timestamps_us else 1 + + # Increment sample count by weight + self._sample_count += weight + + # Collect both aggregate and per-thread statistics using base method + status_counts, has_gc_frame, per_thread_stats = self._collect_thread_status_stats(stack_frames) + + # Merge aggregate status counts (multiply by weight) + for key in status_counts: + self.thread_status_counts[key] += status_counts[key] * weight + + # Update aggregate GC frame count + if has_gc_frame: + self.samples_with_gc_frames += weight + + # Merge per-thread statistics (multiply by weight) + for thread_id, stats in per_thread_stats.items(): + if thread_id not in self.per_thread_stats: + self.per_thread_stats[thread_id] = { + "has_gil": 0, + "on_cpu": 0, + "gil_requested": 0, + "unknown": 0, + "has_exception": 0, + "total": 0, + "gc_samples": 0, + } + for key, value in stats.items(): + self.per_thread_stats[thread_id][key] += value * weight + + # Call parent collect to process frames + super().collect(stack_frames, timestamps_us) + + def set_stats(self, sample_interval_usec, duration_sec, sample_rate, + error_rate=None, missed_samples=None, mode=None): + """Set profiling statistics to include in flamegraph data.""" + self.stats = { + "sample_interval_usec": sample_interval_usec, + "duration_sec": duration_sec, + "sample_rate": sample_rate, + "error_rate": error_rate, + "missed_samples": missed_samples, + "mode": mode + } + + def export(self, filename): + flamegraph_data = self._convert_to_flamegraph_format() + + # Debug output with string table statistics + num_functions = len(flamegraph_data.get("children", [])) + total_time = flamegraph_data.get("value", 0) + string_count = len(self._string_table) + s1 = "" if num_functions == 1 else "s" + s2 = "" if total_time == 1 else "s" + s3 = "" if string_count == 1 else "s" + print( + f"Flamegraph data: {num_functions} root function{s1}, " + f"{total_time} total sample{s2}, " + f"{string_count} unique string{s3}" + ) + + if num_functions == 0: + print( + "Warning: No functions found in profiling data. Check if sampling captured any data." + ) + return + + html_content = self._create_flamegraph_html(flamegraph_data) + + with open(filename, "w", encoding="utf-8") as f: + f.write(html_content) + + print(f"Flamegraph saved to: {filename}") + + @staticmethod + @functools.lru_cache(maxsize=None) + def _format_function_name(func): + filename, lineno, funcname = func + + # Special frames like and should not show file:line + if filename == "~" and lineno == 0: + return funcname + + if len(filename) > 50: + parts = filename.split("/") + if len(parts) > 2: + filename = f".../{'/'.join(parts[-2:])}" + + return f"{funcname} ({filename}:{lineno})" + + @staticmethod + @functools.lru_cache(maxsize=None) + def _format_module_name(func, module_name): + filename, lineno, funcname = func + + # Special frames like and should not show file:line + if filename == "~" and lineno == 0: + return funcname + + return f"{funcname} ({module_name}:{lineno})" + + def _get_module_name(self, filename, path_info): + module_name = self._module_cache.get(filename) + if module_name is None: + module_name, _ = extract_module_name(filename, path_info) + self._module_cache[filename] = module_name + return module_name + + def _convert_to_flamegraph_format(self): + if self._total_samples == 0: + return { + "name": self._string_table.intern("No Data"), + "value": 0, + "children": [], + "threads": [], + "strings": self._string_table.get_strings() + } + + def convert_children(children, min_samples, path_info): + out = [] + for func, node in children.items(): + samples = node["samples"] + if samples < min_samples: + continue + + # Intern all string components for maximum efficiency + filename_idx = self._string_table.intern(func[0]) + funcname_idx = self._string_table.intern(func[2]) + module_name = self._get_module_name(func[0], path_info) + + module_idx = self._string_table.intern(module_name) + name_idx = self._string_table.intern(self._format_function_name(func)) + label_idx = self._string_table.intern(self._format_module_name(func, module_name)) + + child_entry = { + "name": name_idx, + "label": label_idx, + "value": samples, + "self": node.get("self", 0), + "children": [], + "filename": filename_idx, + "module": module_idx, + "lineno": func[1], + "funcname": funcname_idx, + "threads": sorted(list(node.get("threads", set()))), + } + + source = self._get_source_lines(func) + if source: + # Intern source lines for memory efficiency + source_indices = [self._string_table.intern(line) for line in source] + child_entry["source"] = source_indices + + # Include opcode data if available + opcodes = node.get("opcodes", {}) + if opcodes: + child_entry["opcodes"] = dict(opcodes) + + # Recurse + child_entry["children"] = convert_children( + node["children"], min_samples, path_info + ) + out.append(child_entry) + + # Sort by value (descending) then by name index for consistent ordering + out.sort(key=lambda x: (-x["value"], x["name"])) + return out + + # Filter out very small functions (less than 0.1% of total samples) + total_samples = self._total_samples + min_samples = max(1, int(total_samples * 0.001)) + path_info = get_python_path_info() + + root_children = convert_children(self._root["children"], min_samples, path_info) + if not root_children: + return { + "name": self._string_table.intern("No significant data"), + "value": 0, + "children": [], + "strings": self._string_table.get_strings() + } + + # Calculate thread status percentages for display + is_free_threaded = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) + total_threads = max(1, self.thread_status_counts["total"]) + thread_stats = { + "has_gil_pct": (self.thread_status_counts["has_gil"] / total_threads) * 100, + "on_cpu_pct": (self.thread_status_counts["on_cpu"] / total_threads) * 100, + "gil_requested_pct": (self.thread_status_counts["gil_requested"] / total_threads) * 100, + "has_exception_pct": (self.thread_status_counts["has_exception"] / total_threads) * 100, + "gc_pct": (self.samples_with_gc_frames / max(1, self._sample_count)) * 100, + "free_threaded": is_free_threaded, + **self.thread_status_counts + } + + # Calculate per-thread statistics with percentages + per_thread_stats_with_pct = {} + total_samples_denominator = max(1, self._sample_count) + for thread_id, stats in self.per_thread_stats.items(): + total = max(1, stats["total"]) + per_thread_stats_with_pct[thread_id] = { + "has_gil_pct": (stats["has_gil"] / total) * 100, + "on_cpu_pct": (stats["on_cpu"] / total) * 100, + "gil_requested_pct": (stats["gil_requested"] / total) * 100, + "has_exception_pct": (stats["has_exception"] / total) * 100, + "gc_pct": (stats["gc_samples"] / total_samples_denominator) * 100, + **stats + } + + # Build opcode mapping for JS + opcode_mapping = get_opcode_mapping() + + # If we only have one root child, make it the root to avoid redundant level + if len(root_children) == 1: + main_child = root_children[0] + # Update name and label to indicate it's the program root + old_name = self._string_table.get_string(main_child["name"]) + main_child["name"] = self._string_table.intern(f"Program Root: {old_name}") + old_label = self._string_table.get_string(main_child["label"]) + main_child["label"] = self._string_table.intern(f"Program Root: {old_label}") + main_child["stats"] = { + **self.stats, + "thread_stats": thread_stats, + "per_thread_stats": per_thread_stats_with_pct + } + main_child["threads"] = sorted(list(self._all_threads)) + main_child["strings"] = self._string_table.get_strings() + main_child["opcode_mapping"] = opcode_mapping + return main_child + + program_root_idx = self._string_table.intern("Program Root") + return { + "name": program_root_idx, + "label": program_root_idx, + "value": total_samples, + "children": root_children, + "stats": { + **self.stats, + "thread_stats": thread_stats, + "per_thread_stats": per_thread_stats_with_pct + }, + "threads": sorted(list(self._all_threads)), + "strings": self._string_table.get_strings(), + "opcode_mapping": opcode_mapping + } + + def process_frames(self, frames, thread_id, weight=1): + """Process stack frames into flamegraph tree structure. + + Args: + frames: List of (filename, location, funcname, opcode) tuples in + leaf-to-root order. location is (lineno, end_lineno, col_offset, end_col_offset). + opcode is None if not gathered. + thread_id: Thread ID for this stack trace + weight: Number of samples this stack represents (for batched RLE) + """ + # Reverse to root->leaf order for tree building + self._root["samples"] += weight + self._total_samples += weight + self._root["threads"].add(thread_id) + self._all_threads.add(thread_id) + + current = self._root + for filename, location, funcname, opcode in reversed(frames): + lineno = extract_lineno(location) + func = (filename, lineno, funcname) + func = self._func_intern.setdefault(func, func) + + node = current["children"].get(func) + if node is None: + node = {"samples": 0, "children": {}, "threads": set(), "opcodes": collections.Counter(), "self": 0} + current["children"][func] = node + node["samples"] += weight + node["threads"].add(thread_id) + + if opcode is not None: + node["opcodes"][opcode] += weight + + current = node + + if current is not self._root: + current["self"] += weight + + def _get_source_lines(self, func): + filename, lineno, _ = func + + try: + lines = [] + start_line = max(1, lineno - 2) + end_line = lineno + 3 + + for line_num in range(start_line, end_line): + line = linecache.getline(filename, line_num) + if line.strip(): + marker = "→ " if line_num == lineno else " " + lines.append(f"{marker}{line_num}: {line.rstrip()}") + + return lines if lines else None + + except Exception: + return None + + def _create_flamegraph_html(self, data): + data_json = json.dumps(data) + + template_dir = importlib.resources.files(__package__) + vendor_dir = template_dir / "_vendor" + assets_dir = template_dir / "_assets" + + d3_path = vendor_dir / "d3" / "7.8.5" / "d3.min.js" + d3_flame_graph_dir = vendor_dir / "d3-flame-graph" / "4.1.3" + fg_css_path = d3_flame_graph_dir / "d3-flamegraph.css" + fg_js_path = d3_flame_graph_dir / "d3-flamegraph.min.js" + fg_tooltip_js_path = d3_flame_graph_dir / "d3-flamegraph-tooltip.min.js" + + html_template = (template_dir / "_flamegraph_assets" / "flamegraph_template.html").read_text(encoding="utf-8") + css_content = get_combined_css("flamegraph") + base_js = (template_dir / "_shared_assets" / "base.js").read_text(encoding="utf-8") + component_js = (template_dir / "_flamegraph_assets" / "flamegraph.js").read_text(encoding="utf-8") + js_content = f"{base_js}\n{component_js}" + + # Set title and subtitle based on whether this is a differential flamegraph + is_differential = data.get("stats", {}).get("is_differential", False) + if is_differential: + title = "Tachyon Profiler - Differential Flamegraph Report" + subtitle = "Differential Flamegraph Report" + else: + title = "Tachyon Profiler - Flamegraph Report" + subtitle = "Flamegraph Report" + + html_template = html_template.replace("{{TITLE}}", title) + html_template = html_template.replace("{{SUBTITLE}}", subtitle) + + # Inline first-party CSS/JS + html_template = html_template.replace( + "", f"" + ) + html_template = html_template.replace( + "", f"" + ) + + png_path = assets_dir / "tachyon-logo.png" + b64_logo = base64.b64encode(png_path.read_bytes()).decode("ascii") + + # Let CSS control size; keep markup simple + logo_html = f'Tachyon logo' + html_template = html_template.replace("", logo_html) + html_template = html_template.replace( + "", f"{sys.version_info.major}.{sys.version_info.minor}" + ) + + d3_js = d3_path.read_text(encoding="utf-8") + fg_css = fg_css_path.read_text(encoding="utf-8") + fg_js = fg_js_path.read_text(encoding="utf-8") + fg_tooltip_js = fg_tooltip_js_path.read_text(encoding="utf-8") + + html_template = html_template.replace( + "", + f"", + ) + html_template = html_template.replace( + "", + f"", + ) + html_template = html_template.replace( + "", + f"", + ) + html_template = html_template.replace( + "", + f"", + ) + + # Replace the placeholder with actual data + html_content = html_template.replace( + "{{FLAMEGRAPH_DATA}}", data_json + ) + + return html_content + + +class DiffFlamegraphCollector(FlamegraphCollector): + """Differential flamegraph collector that compares against a baseline binary profile.""" + + def __init__(self, sample_interval_usec, *, baseline_binary_path, skip_idle=False): + super().__init__(sample_interval_usec, skip_idle=skip_idle) + if not os.path.exists(baseline_binary_path): + raise ValueError(f"Baseline file not found: {baseline_binary_path}") + self.baseline_binary_path = baseline_binary_path + self._baseline_collector = None + self._elided_paths = set() + + def _load_baseline(self): + """Load baseline profile from binary file.""" + from .binary_reader import BinaryReader + + with BinaryReader(self.baseline_binary_path) as reader: + info = reader.get_info() + + baseline_collector = FlamegraphCollector( + sample_interval_usec=info['sample_interval_us'], + skip_idle=self.skip_idle + ) + + reader.replay_samples(baseline_collector) + + self._baseline_collector = baseline_collector + + def _aggregate_path_samples(self, root_node, path=None): + """Aggregate samples by stack path, excluding line numbers for cross-profile matching.""" + if path is None: + path = () + + stats = {} + + for func, node in root_node["children"].items(): + filename, _lineno, funcname = func + func_key = (filename, funcname) + path_key = path + (func_key,) + + total_samples = node.get("samples", 0) + self_samples = node.get("self", 0) + + if path_key in stats: + stats[path_key]["total"] += total_samples + stats[path_key]["self"] += self_samples + else: + stats[path_key] = { + "total": total_samples, + "self": self_samples + } + + child_stats = self._aggregate_path_samples(node, path_key) + for key, data in child_stats.items(): + if key in stats: + stats[key]["total"] += data["total"] + stats[key]["self"] += data["self"] + else: + stats[key] = data + + return stats + + def _convert_to_flamegraph_format(self): + """Convert to flamegraph format with differential annotations.""" + if self._baseline_collector is None: + self._load_baseline() + + current_flamegraph = super()._convert_to_flamegraph_format() + + current_stats = self._aggregate_path_samples(self._root) + baseline_stats = self._aggregate_path_samples(self._baseline_collector._root) + + # Scale baseline values to make them comparable, accounting for both + # sample count differences and sample interval differences. + baseline_total = self._baseline_collector._total_samples + if baseline_total > 0 and self._total_samples > 0: + current_time = self._total_samples * self.sample_interval_usec + baseline_time = baseline_total * self._baseline_collector.sample_interval_usec + scale = current_time / baseline_time + elif baseline_total > 0: + # Current profile is empty - use interval-based scale for elided display + scale = self.sample_interval_usec / self._baseline_collector.sample_interval_usec + else: + scale = 1.0 + + self._annotate_nodes_with_diff(current_flamegraph, current_stats, baseline_stats, scale) + self._add_elided_flamegraph(current_flamegraph, current_stats, baseline_stats, scale) + + return current_flamegraph + + def _annotate_nodes_with_diff(self, current_flamegraph, current_stats, baseline_stats, scale): + """Annotate each node in the tree with diff metadata.""" + if "stats" not in current_flamegraph: + current_flamegraph["stats"] = {} + + current_flamegraph["stats"]["baseline_samples"] = self._baseline_collector._total_samples + current_flamegraph["stats"]["current_samples"] = self._total_samples + current_flamegraph["stats"]["baseline_scale"] = scale + current_flamegraph["stats"]["is_differential"] = True + + if self._is_promoted_root(current_flamegraph): + self._add_diff_data_to_node(current_flamegraph, (), current_stats, baseline_stats, scale) + else: + for child in current_flamegraph["children"]: + self._add_diff_data_to_node(child, (), current_stats, baseline_stats, scale) + + def _add_diff_data_to_node(self, node, path, current_stats, baseline_stats, scale): + """Recursively add diff metadata to nodes.""" + func_key = self._extract_func_key(node, self._string_table) + path_key = path + (func_key,) if func_key else path + + current_data = current_stats.get(path_key, {"total": 0, "self": 0}) + baseline_data = baseline_stats.get(path_key, {"total": 0, "self": 0}) + + current_self = current_data["self"] + baseline_self = baseline_data["self"] * scale + baseline_total = baseline_data["total"] * scale + + diff = current_self - baseline_self + if baseline_self > 0: + diff_pct = (diff / baseline_self) * 100.0 + elif current_self > 0: + diff_pct = 100.0 + else: + diff_pct = 0.0 + + node["baseline"] = baseline_self + node["baseline_total"] = baseline_total + node["self_time"] = current_self + node["diff"] = diff + node["diff_pct"] = diff_pct + + if "children" in node and node["children"]: + for child in node["children"]: + self._add_diff_data_to_node(child, path_key, current_stats, baseline_stats, scale) + + def _is_promoted_root(self, data): + """Check if the data represents a promoted root node.""" + return "filename" in data and "funcname" in data + + def _add_elided_flamegraph(self, current_flamegraph, current_stats, baseline_stats, scale): + """Calculate elided paths and add elided flamegraph to stats.""" + self._elided_paths = baseline_stats.keys() - current_stats.keys() + + current_flamegraph["stats"]["elided_count"] = len(self._elided_paths) + + if self._elided_paths: + elided_flamegraph = self._build_elided_flamegraph(baseline_stats, scale) + if elided_flamegraph: + current_flamegraph["stats"]["elided_flamegraph"] = elided_flamegraph + + def _build_elided_flamegraph(self, baseline_stats, scale): + """Build flamegraph containing only elided paths from baseline. + + This re-runs the base conversion pipeline on the baseline collector + to produce a complete formatted flamegraph, then prunes it to keep + only elided paths. + """ + if not self._baseline_collector or not self._elided_paths: + return None + + # Suppress source line collection for elided nodes - these functions + # no longer exist in the current profile, so source lines from the + # current machine's filesystem would be misleading or unavailable. + orig_get_source = self._baseline_collector._get_source_lines + self._baseline_collector._get_source_lines = lambda func: None + try: + baseline_data = self._baseline_collector._convert_to_flamegraph_format() + finally: + self._baseline_collector._get_source_lines = orig_get_source + + # Remove non-elided nodes and recalculate values + if not self._extract_elided_nodes(baseline_data, path=()): + return None + + self._add_elided_metadata(baseline_data, baseline_stats, scale, path=()) + + # Merge only profiling metadata, not thread-level stats + for key in ("sample_interval_usec", "duration_sec", "sample_rate", + "error_rate", "missed_samples", "mode"): + if key in self.stats: + baseline_data["stats"][key] = self.stats[key] + baseline_data["stats"]["is_differential"] = True + baseline_data["stats"]["baseline_samples"] = self._baseline_collector._total_samples + baseline_data["stats"]["current_samples"] = self._total_samples + + return baseline_data + + def _extract_elided_nodes(self, node, path): + """Remove non-elided nodes and recalculate values bottom-up.""" + if not node: + return False + + func_key = self._extract_func_key(node, self._baseline_collector._string_table) + current_path = path + (func_key,) if func_key else path + + is_elided = current_path in self._elided_paths if func_key else False + + if "children" in node: + # Filter children, keeping only those with elided descendants + elided_children = [] + total_value = 0 + for child in node["children"]: + if self._extract_elided_nodes(child, current_path): + elided_children.append(child) + total_value += child.get("value", 0) + node["children"] = elided_children + + # Recalculate value for structural (non-elided) ancestor nodes; + # elided nodes keep their original value to preserve self-samples + if elided_children and not is_elided: + node["value"] = total_value + + # Keep this node if it's elided or has elided descendants + return is_elided or bool(node.get("children")) + + def _add_elided_metadata(self, node, baseline_stats, scale, path): + """Add differential metadata showing this path disappeared.""" + if not node: + return + + func_key = self._extract_func_key(node, self._baseline_collector._string_table) + current_path = path + (func_key,) if func_key else path + + baseline_self = 0 + baseline_total = 0 + if func_key and current_path in baseline_stats: + baseline_data = baseline_stats[current_path] + baseline_self = baseline_data["self"] * scale + baseline_total = baseline_data["total"] * scale + + node["baseline"] = baseline_self + node["baseline_total"] = baseline_total + node["diff"] = -baseline_self + else: + node["baseline"] = 0 + node["baseline_total"] = 0 + node["diff"] = 0 + + node["self_time"] = 0 + # Elided paths have zero current self-time, so the change is always + # -100% when there was actual baseline self-time to lose. + # For internal nodes with no baseline self-time, use 0% to avoid + # misleading tooltips. + if baseline_self > 0: + node["diff_pct"] = -100.0 + else: + node["diff_pct"] = 0.0 + + if "children" in node and node["children"]: + for child in node["children"]: + self._add_elided_metadata(child, baseline_stats, scale, current_path) + + def _extract_func_key(self, node, string_table): + """Extract (filename, funcname) key from node, excluding line numbers. + + Line numbers are excluded to match functions even if they moved. + Returns None for root nodes that don't have function information. + """ + if "filename" not in node or "funcname" not in node: + return None + filename = string_table.get_string(node["filename"]) + funcname = string_table.get_string(node["funcname"]) + return (filename, funcname) diff --git a/Lib/profiling/sampling/string_table.py b/Lib/profiling/sampling/string_table.py new file mode 100644 index 00000000000000..25c347f7ff12ad --- /dev/null +++ b/Lib/profiling/sampling/string_table.py @@ -0,0 +1,53 @@ +"""String table implementation for memory-efficient string storage in profiling data.""" + +class StringTable: + """A string table for interning strings and reducing memory usage.""" + + def __init__(self): + self._strings = [] + self._string_to_index = {} + + def intern(self, string): + """Intern a string and return its index. + + Args: + string: The string to intern + + Returns: + int: The index of the string in the table + """ + if not isinstance(string, str): + string = str(string) + + if string in self._string_to_index: + return self._string_to_index[string] + + index = len(self._strings) + self._strings.append(string) + self._string_to_index[string] = index + return index + + def get_string(self, index): + """Get a string by its index. + + Args: + index: The index of the string + + Returns: + str: The string at the given index, or empty string if invalid + """ + if 0 <= index < len(self._strings): + return self._strings[index] + return "" + + def get_strings(self): + """Get the list of all strings in the table. + + Returns: + list: A copy of the strings list + """ + return self._strings.copy() + + def __len__(self): + """Return the number of strings in the table.""" + return len(self._strings) diff --git a/Lib/profiling/tracing/__init__.py b/Lib/profiling/tracing/__init__.py new file mode 100644 index 00000000000000..bd3cbf299aab3b --- /dev/null +++ b/Lib/profiling/tracing/__init__.py @@ -0,0 +1,218 @@ +"""Tracing profiler for Python. + +This module provides deterministic profiling of Python programs by tracing +every function call and return. +""" + +__all__ = ("run", "runctx", "Profile") + +import _lsprof +import importlib.machinery +import importlib.util +import io +from profiling.tracing._utils import _Utils + +# ____________________________________________________________ +# Simple interface + +def run(statement, filename=None, sort=-1): + """Run statement under profiler optionally saving results in filename + + This function takes a single argument that can be passed to the + "exec" statement, and an optional file name. In all cases this + routine attempts to "exec" its first argument and gather profiling + statistics from the execution. If no file name is present, then this + function automatically prints a simple profiling report, sorted by the + standard name string (file/line/function-name) that is presented in + each line. + """ + return _Utils(Profile).run(statement, filename, sort) + +def runctx(statement, globals, locals, filename=None, sort=-1): + """Run statement under profiler, supplying your own globals and locals, + optionally saving results in filename. + + statement and filename have the same semantics as profile.run + """ + return _Utils(Profile).runctx(statement, globals, locals, + filename, sort) + +# ____________________________________________________________ + +class Profile(_lsprof.Profiler): + """Profile(timer=None, timeunit=None, subcalls=True, builtins=True) + + Builds a profiler object using the specified timer function. + The default timer is a fast built-in one based on real time. + For custom timer functions returning integers, timeunit can + be a float specifying a scale (i.e. how long each integer unit + is, in seconds). + """ + + # Most of the functionality is in the base class. + # This subclass only adds convenient and backward-compatible methods. + + def print_stats(self, sort=-1): + import pstats + if not isinstance(sort, tuple): + sort = (sort,) + pstats.Stats(self).strip_dirs().sort_stats(*sort).print_stats() + + def dump_stats(self, file): + import marshal + with open(file, 'wb') as f: + self.create_stats() + marshal.dump(self.stats, f) + + def create_stats(self): + self.disable() + self.snapshot_stats() + + def snapshot_stats(self): + entries = self.getstats() + self.stats = {} + callersdicts = {} + # call information + for entry in entries: + func = label(entry.code) + nc = entry.callcount # ncalls column of pstats (before '/') + cc = nc - entry.reccallcount # ncalls column of pstats (after '/') + tt = entry.inlinetime # tottime column of pstats + ct = entry.totaltime # cumtime column of pstats + callers = {} + callersdicts[id(entry.code)] = callers + self.stats[func] = cc, nc, tt, ct, callers + # subcall information + for entry in entries: + if entry.calls: + func = label(entry.code) + for subentry in entry.calls: + try: + callers = callersdicts[id(subentry.code)] + except KeyError: + continue + nc = subentry.callcount + cc = nc - subentry.reccallcount + tt = subentry.inlinetime + ct = subentry.totaltime + if func in callers: + prev = callers[func] + nc += prev[0] + cc += prev[1] + tt += prev[2] + ct += prev[3] + callers[func] = nc, cc, tt, ct + + # The following two methods can be called by clients to use + # a profiler to profile a statement, given as a string. + + def run(self, cmd): + import __main__ + dict = __main__.__dict__ + return self.runctx(cmd, dict, dict) + + def runctx(self, cmd, globals, locals): + self.enable() + try: + exec(cmd, globals, locals) + finally: + self.disable() + return self + + # This method is more useful to profile a single function call. + def runcall(self, func, /, *args, **kw): + self.enable() + try: + return func(*args, **kw) + finally: + self.disable() + + def __enter__(self): + self.enable() + return self + + def __exit__(self, *exc_info): + self.disable() + +# ____________________________________________________________ + +def label(code): + if isinstance(code, str): + return ('~', 0, code) # built-in functions ('~' sorts at the end) + else: + return (code.co_filename, code.co_firstlineno, code.co_name) + +# ____________________________________________________________ + +def main(): + import os + import sys + import runpy + import pstats + from optparse import OptionParser + usage = "cProfile.py [-o output_file_path] [-s sort] [-m module | scriptfile] [arg] ..." + parser = OptionParser(usage=usage) + parser.allow_interspersed_args = False + parser.add_option('-o', '--outfile', dest="outfile", + help="Save stats to ", default=None) + parser.add_option('-s', '--sort', dest="sort", + help="Sort order when printing to stdout, based on pstats.Stats class", + default=2, + choices=sorted(pstats.Stats.sort_arg_dict_default)) + parser.add_option('-m', dest="module", action="store_true", + help="Profile a library module", default=False) + + if not sys.argv[1:]: + parser.print_usage() + sys.exit(2) + + (options, args) = parser.parse_args() + sys.argv[:] = args + + # The script that we're profiling may chdir, so capture the absolute path + # to the output file at startup. + if options.outfile is not None: + options.outfile = os.path.abspath(options.outfile) + + if len(args) > 0: + if options.module: + code = "run_module(modname, run_name='__main__')" + globs = { + 'run_module': runpy.run_module, + 'modname': args[0] + } + else: + progname = args[0] + sys.path.insert(0, os.path.dirname(progname)) + with io.open_code(progname) as fp: + code = compile(fp.read(), progname, 'exec', module='__main__') + spec = importlib.machinery.ModuleSpec(name='__main__', loader=None, + origin=progname) + module = importlib.util.module_from_spec(spec) + # Set __main__ so that importing __main__ in the profiled code will + # return the same namespace that the code is executing under. + sys.modules['__main__'] = module + # Ensure that we're using the same __dict__ instance as the module + # for the global variables so that updates to globals are reflected + # in the module's namespace. + globs = module.__dict__ + globs.update({ + '__spec__': spec, + '__file__': spec.origin, + '__name__': spec.name, + '__package__': None, + }) + + try: + runctx(code, globs, None, options.outfile, options.sort) + except BrokenPipeError as exc: + # Prevent "Exception ignored" during interpreter shutdown. + sys.stdout = None + sys.exit(exc.errno) + else: + parser.print_usage() + return parser + +# When invoked as main program, invoke the profiler on a script +if __name__ == '__main__': + main() diff --git a/Lib/profiling/tracing/__main__.py b/Lib/profiling/tracing/__main__.py new file mode 100644 index 00000000000000..95041d1368b165 --- /dev/null +++ b/Lib/profiling/tracing/__main__.py @@ -0,0 +1,6 @@ +"""Run the tracing profiler from the command line.""" + +from profiling.tracing import main + +if __name__ == '__main__': + main() diff --git a/Lib/profiling/tracing/_utils.py b/Lib/profiling/tracing/_utils.py new file mode 100644 index 00000000000000..8c4b65889c707f --- /dev/null +++ b/Lib/profiling/tracing/_utils.py @@ -0,0 +1,32 @@ +class _Utils: + """Support class for utility functions which are shared by + profile.py and cProfile.py modules. + Not supposed to be used directly. + """ + + def __init__(self, profiler): + self.profiler = profiler + + def run(self, statement, filename, sort): + prof = self.profiler() + try: + prof.run(statement) + except SystemExit: + pass + finally: + self._show(prof, filename, sort) + + def runctx(self, statement, globals, locals, filename, sort): + prof = self.profiler() + try: + prof.runctx(statement, globals, locals) + except SystemExit: + pass + finally: + self._show(prof, filename, sort) + + def _show(self, prof, filename, sort): + if filename is not None: + prof.dump_stats(filename) + else: + prof.print_stats(sort) diff --git a/Lib/pstats.py b/Lib/pstats.py index 079abd2c1b81df..07ecda07796e44 100644 --- a/Lib/pstats.py +++ b/Lib/pstats.py @@ -154,6 +154,7 @@ def load_stats(self, arg): arg.create_stats() self.stats = arg.stats arg.stats = {} + return if not self.stats: raise TypeError("Cannot create or construct a %r object from %r" % (self.__class__, arg)) diff --git a/Lib/py_compile.py b/Lib/py_compile.py index 43d8ec90ffb6b1..7ca479141e01e4 100644 --- a/Lib/py_compile.py +++ b/Lib/py_compile.py @@ -177,7 +177,7 @@ def main(): import argparse description = 'A simple command-line interface for py_compile module.' - parser = argparse.ArgumentParser(description=description, color=True) + parser = argparse.ArgumentParser(description=description) parser.add_argument( '-q', '--quiet', action='store_true', @@ -194,8 +194,10 @@ def main(): else: filenames = args.filenames for filename in filenames: + cfilename = (None if sys.implementation.cache_tag + else f"{filename.rpartition('.')[0]}.pyc") try: - compile(filename, doraise=True) + compile(filename, cfilename, doraise=True) except PyCompileError as error: if args.quiet: parser.exit(1) diff --git a/Lib/pyclbr.py b/Lib/pyclbr.py index 37f86995d6ce00..35772cc01d0517 100644 --- a/Lib/pyclbr.py +++ b/Lib/pyclbr.py @@ -158,7 +158,6 @@ def _readmodule(module, path, inpackage=None): return _readmodule(submodule, parent['__path__'], package) # Search the path for the module. - f = None if inpackage is not None: search_path = path else: diff --git a/Lib/pydoc.py b/Lib/pydoc.py index d508fb70ea429e..fe42592530c2cf 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -78,20 +78,41 @@ class or function within a module or module in a package. If the from reprlib import Repr from traceback import format_exception_only -from _pyrepl.pager import (get_pager, pipe_pager, - plain_pager, tempfile_pager, tty_pager) +try: + from _pyrepl.pager import (get_pager, pipe_pager, + plain_pager, tempfile_pager, tty_pager) -# Expose plain() as pydoc.plain() -from _pyrepl.pager import plain # noqa: F401 + # Expose plain() as pydoc.plain() + from _pyrepl.pager import plain # noqa: F401 + # --------------------------------------------------------- old names + getpager = get_pager + pipepager = pipe_pager + plainpager = plain_pager + tempfilepager = tempfile_pager + ttypager = tty_pager -# --------------------------------------------------------- old names +except ModuleNotFoundError: + # Minimal alternatives for cases where _pyrepl is absent. -getpager = get_pager -pipepager = pipe_pager -plainpager = plain_pager -tempfilepager = tempfile_pager -ttypager = tty_pager + def plain(text: str) -> str: + """Remove boldface formatting from text.""" + return re.sub('.\b', '', text) + + def plain_pager(text: str, title: str = '') -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + encoding = getattr(sys.stdout, 'encoding', None) or 'utf-8' + text = text.encode(encoding, 'backslashreplace').decode(encoding) + text = plain(text) + sys.stdout.write(text) + + def get_pager(): + """Unconditionally return the plain pager, since _pyrepl is absent.""" + return plain_pager + + # --------------------------------------------------------- old names + getpager = get_pager + plainpager = plain_pager # --------------------------------------------------------- common routines @@ -108,96 +129,10 @@ def pathdirs(): normdirs.append(normdir) return dirs -def _findclass(func): - cls = sys.modules.get(func.__module__) - if cls is None: - return None - for name in func.__qualname__.split('.')[:-1]: - cls = getattr(cls, name) - if not inspect.isclass(cls): - return None - return cls - -def _finddoc(obj): - if inspect.ismethod(obj): - name = obj.__func__.__name__ - self = obj.__self__ - if (inspect.isclass(self) and - getattr(getattr(self, name, None), '__func__') is obj.__func__): - # classmethod - cls = self - else: - cls = self.__class__ - elif inspect.isfunction(obj): - name = obj.__name__ - cls = _findclass(obj) - if cls is None or getattr(cls, name) is not obj: - return None - elif inspect.isbuiltin(obj): - name = obj.__name__ - self = obj.__self__ - if (inspect.isclass(self) and - self.__qualname__ + '.' + name == obj.__qualname__): - # classmethod - cls = self - else: - cls = self.__class__ - # Should be tested before isdatadescriptor(). - elif isinstance(obj, property): - name = obj.__name__ - cls = _findclass(obj.fget) - if cls is None or getattr(cls, name) is not obj: - return None - elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj): - name = obj.__name__ - cls = obj.__objclass__ - if getattr(cls, name) is not obj: - return None - if inspect.ismemberdescriptor(obj): - slots = getattr(cls, '__slots__', None) - if isinstance(slots, dict) and name in slots: - return slots[name] - else: - return None - for base in cls.__mro__: - try: - doc = _getowndoc(getattr(base, name)) - except AttributeError: - continue - if doc is not None: - return doc - return None - -def _getowndoc(obj): - """Get the documentation string for an object if it is not - inherited from its class.""" - try: - doc = object.__getattribute__(obj, '__doc__') - if doc is None: - return None - if obj is not type: - typedoc = type(obj).__doc__ - if isinstance(typedoc, str) and typedoc == doc: - return None - return doc - except AttributeError: - return None - def _getdoc(object): - """Get the documentation string for an object. - - All tabs are expanded to spaces. To clean up docstrings that are - indented to line up with blocks of code, any whitespace than can be - uniformly removed from the second line onwards is removed.""" - doc = _getowndoc(object) - if doc is None: - try: - doc = _finddoc(object) - except (AttributeError, TypeError): - return None - if not isinstance(doc, str): - return None - return inspect.cleandoc(doc) + return inspect.getdoc(object, + fallback_to_class_doc=False, + inherit_class_doc=False) def getdoc(object): """Get the doc string or comments for an object.""" @@ -327,12 +262,12 @@ def visiblename(name, all=None, obj=None): """Decide whether to show documentation on a variable.""" # Certain special names are redundant or internal. # XXX Remove __initializing__? - if name in {'__author__', '__builtins__', '__cached__', '__credits__', - '__date__', '__doc__', '__file__', '__spec__', - '__loader__', '__module__', '__name__', '__package__', - '__path__', '__qualname__', '__slots__', '__version__', - '__static_attributes__', '__firstlineno__', - '__annotate_func__', '__annotations_cache__'}: + if name in {'__author__', '__builtins__', '__credits__', '__date__', + '__doc__', '__file__', '__spec__', '__loader__', '__module__', + '__name__', '__package__', '__path__', '__qualname__', + '__slots__', '__version__', '__static_attributes__', + '__firstlineno__', '__annotate_func__', + '__annotations_cache__'}: return 0 # Private names are hidden, but special names are displayed. if name.startswith('__') and name.endswith('__'): return 1 @@ -536,6 +471,7 @@ class Doc: PYTHONDOCS = os.environ.get("PYTHONDOCS", "https://docs.python.org/%d.%d/library" % sys.version_info[:2]) + STDLIB_DIR = sysconfig.get_path('stdlib') def document(self, object, name=None, *args): """Generate documentation for an object.""" @@ -561,31 +497,60 @@ def fail(self, object, name=None, *args): docmodule = docclass = docroutine = docother = docproperty = docdata = fail - def getdocloc(self, object, basedir=sysconfig.get_path('stdlib')): + def getdocloc(self, object, basedir=None): """Return the location of module docs or None""" + basedir = self.STDLIB_DIR if basedir is None else basedir + docloc = os.environ.get("PYTHONDOCS", self.PYTHONDOCS) + + if (self._is_stdlib_module(object, basedir) and + object.__name__ not in ('xml.etree', 'test.test_pydoc.pydoc_mod')): + + try: + from pydoc_data import module_docs + except ImportError: + module_docs = None + + if module_docs and object.__name__ in module_docs.module_docs: + doc_name = module_docs.module_docs[object.__name__] + if docloc.startswith(("http://", "https://")): + docloc = "{}/{}".format(docloc.rstrip("/"), doc_name) + else: + docloc = os.path.join(docloc, doc_name) + else: + docloc = None + else: + docloc = None + return docloc + + def _get_version(self, object): + if self._is_stdlib_module(object): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + version = getattr(object, '__version__', None) + else: + version = getattr(object, '__version__', None) + return '' if version is None else str(version) + + def _is_stdlib_module(self, object, basedir=None): + basedir = self.STDLIB_DIR if basedir is None else basedir try: file = inspect.getabsfile(object) except TypeError: file = '(built-in)' - docloc = os.environ.get("PYTHONDOCS", self.PYTHONDOCS) + if sysconfig.is_python_build(): + srcdir = sysconfig.get_config_var('srcdir') + if srcdir: + basedir = os.path.join(srcdir, 'Lib') basedir = os.path.normcase(basedir) - if (isinstance(object, type(os)) and - (object.__name__ in ('errno', 'exceptions', 'gc', - 'marshal', 'posix', 'signal', 'sys', - '_thread', 'zipimport') or - (file.startswith(basedir) and - not file.startswith(os.path.join(basedir, 'site-packages')))) and - object.__name__ not in ('xml.etree', 'test.test_pydoc.pydoc_mod')): - if docloc.startswith(("http://", "https://")): - docloc = "{}/{}.html".format(docloc.rstrip("/"), object.__name__.lower()) - else: - docloc = os.path.join(docloc, object.__name__.lower() + ".html") - else: - docloc = None - return docloc + return (isinstance(object, type(os)) and + (object.__name__ in ('errno', 'exceptions', 'gc', + 'marshal', 'posix', 'signal', 'sys', + '_thread', 'zipimport') + or (file.startswith(basedir) and + not file.startswith(os.path.join(basedir, 'site-packages'))))) # -------------------------------------------- HTML documentation generator @@ -846,8 +811,8 @@ def docmodule(self, object, name=None, mod=None, *ignored): except TypeError: filelink = '(built-in)' info = [] - if hasattr(object, '__version__'): - version = str(object.__version__) + + if version := self._get_version(object): if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': version = version[11:-1].strip() info.append('version %s' % self.escape(version)) @@ -884,6 +849,7 @@ def docmodule(self, object, name=None, mod=None, *ignored): for key, value in inspect.getmembers(object, inspect.isroutine): # if __all__ exists, believe it. Otherwise use a heuristic. if (all is not None + or inspect.isbuiltin(value) or (inspect.getmodule(value) or object) is object): if visiblename(key, all, object): funcs.append((key, value)) @@ -1274,6 +1240,17 @@ def indent(self, text, prefix=' '): lines = [(prefix + line).rstrip() for line in text.split('\n')] return '\n'.join(lines) + def _format_doc(self, text, width=68): + """Wraps the single-line summary if it is too long.""" + if not text: return '' + lines = text.split('\n', 2) + if len(lines) > 1 and lines[1]: + return text + lines[:1] = textwrap.wrap(lines[0], width, + break_long_words=False, + break_on_hyphens=False) + return '\n'.join(lines) + def section(self, title, contents): """Format a section with a given heading.""" clean_contents = self.indent(contents).rstrip() @@ -1328,6 +1305,7 @@ def docmodule(self, object, name=None, mod=None, *ignored): for key, value in inspect.getmembers(object, inspect.isroutine): # if __all__ exists, believe it. Otherwise use a heuristic. if (all is not None + or inspect.isbuiltin(value) or (inspect.getmodule(value) or object) is object): if visiblename(key, all, object): funcs.append((key, value)) @@ -1377,11 +1355,10 @@ def docmodule(self, object, name=None, mod=None, *ignored): if data: contents = [] for key, value in data: - contents.append(self.docother(value, key, name, maxlen=70)) + contents.append(self.docother(value, key, name, maxlen=76)) result = result + self.section('DATA', '\n'.join(contents)) - if hasattr(object, '__version__'): - version = str(object.__version__) + if version := self._get_version(object): if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': version = version[11:-1].strip() result = result + self.section('VERSION', version) @@ -1424,6 +1401,7 @@ def makename(c, m=object.__module__): doc = getdoc(object) if doc: + doc = self._format_doc(doc) push(doc + '\n') # List the mro, if non-trivial. @@ -1500,7 +1478,7 @@ def spilldata(msg, attrs, predicate): obj = getattr(object, name) except AttributeError: obj = homecls.__dict__[name] - push(self.docother(obj, name, mod, maxlen=70, doc=doc) + + push(self.docother(obj, name, mod, maxlen=72, doc=doc) + '\n') return attrs @@ -1624,6 +1602,7 @@ def docroutine(self, object, name=None, mod=None, cl=None, homecls=None): return decl + '\n' else: doc = getdoc(object) or '' + doc = self._format_doc(doc) return decl + '\n' + (doc and self.indent(doc).rstrip() + '\n') def docdata(self, object, name=None, mod=None, cl=None, *ignored): @@ -1636,6 +1615,7 @@ def docdata(self, object, name=None, mod=None, cl=None, *ignored): push('\n') doc = getdoc(object) or '' if doc: + doc = self._format_doc(doc) push(self.indent(doc)) push('\n') return ''.join(results) @@ -1649,12 +1629,13 @@ def docother(self, object, name=None, mod=None, parent=None, *ignored, if maxlen: line = (name and name + ' = ' or '') + repr chop = maxlen - len(line) - if chop < 0: repr = repr[:chop] + '...' + if chop < 0: repr = repr[:chop-3] + '...' line = (name and self.bold(name) + ' = ' or '') + repr if not doc: doc = getdoc(object) if doc: - line += '\n' + self.indent(str(doc)) + '\n' + doc = self._format_doc(str(doc)) + line += '\n' + self.indent(doc) + '\n' return line class _PlainTextDoc(TextDoc): @@ -1879,6 +1860,7 @@ class Helper: 'in': ('in', 'SEQUENCEMETHODS'), 'is': 'COMPARISON', 'lambda': ('lambda', 'FUNCTIONS'), + 'lazy': ('lazy', 'MODULES'), 'nonlocal': ('nonlocal', 'global NAMESPACES'), 'not': 'BOOLEAN', 'or': 'BOOLEAN', @@ -2059,10 +2041,11 @@ def interact(self): while True: try: request = self.getline('help> ') - if not request: break except (KeyboardInterrupt, EOFError): break request = request.strip() + if not request: + continue # back to the prompt # Make sure significant trailing quoting marks of literals don't # get deleted while cleaning input diff --git a/Lib/pydoc_data/module_docs.py b/Lib/pydoc_data/module_docs.py new file mode 100644 index 00000000000000..1a3126d3db9590 --- /dev/null +++ b/Lib/pydoc_data/module_docs.py @@ -0,0 +1,323 @@ +# Autogenerated by Sphinx on Thu May 7 16:26:23 2026 +# as part of the release process. + +module_docs = { + '__future__': '__future__#module-__future__', + '__main__': '__main__#module-__main__', + '_thread': '_thread#module-_thread', + '_tkinter': 'tkinter#module-_tkinter', + 'abc': 'abc#module-abc', + 'aifc': 'aifc#module-aifc', + 'annotationlib': 'annotationlib#module-annotationlib', + 'argparse': 'argparse#module-argparse', + 'array': 'array#module-array', + 'ast': 'ast#module-ast', + 'asynchat': 'asynchat#module-asynchat', + 'asyncio': 'asyncio#module-asyncio', + 'asyncore': 'asyncore#module-asyncore', + 'atexit': 'atexit#module-atexit', + 'audioop': 'audioop#module-audioop', + 'base64': 'base64#module-base64', + 'bdb': 'bdb#module-bdb', + 'binascii': 'binascii#module-binascii', + 'bisect': 'bisect#module-bisect', + 'builtins': 'builtins#module-builtins', + 'bz2': 'bz2#module-bz2', + 'calendar': 'calendar#module-calendar', + 'cgi': 'cgi#module-cgi', + 'cgitb': 'cgitb#module-cgitb', + 'chunk': 'chunk#module-chunk', + 'cmath': 'cmath#module-cmath', + 'cmd': 'cmd#module-cmd', + 'code': 'code#module-code', + 'codecs': 'codecs#module-codecs', + 'codeop': 'codeop#module-codeop', + 'collections': 'collections#module-collections', + 'collections.abc': 'collections.abc#module-collections.abc', + 'colorsys': 'colorsys#module-colorsys', + 'compileall': 'compileall#module-compileall', + 'compression': 'compression#module-compression', + 'compression.zstd': 'compression.zstd#module-compression.zstd', + 'concurrent.futures': 'concurrent.futures#module-concurrent.futures', + 'concurrent.interpreters': 'concurrent.interpreters#module-concurrent.interpreters', + 'configparser': 'configparser#module-configparser', + 'contextlib': 'contextlib#module-contextlib', + 'contextvars': 'contextvars#module-contextvars', + 'copy': 'copy#module-copy', + 'copyreg': 'copyreg#module-copyreg', + 'crypt': 'crypt#module-crypt', + 'csv': 'csv#module-csv', + 'ctypes': 'ctypes#module-ctypes', + 'curses': 'curses#module-curses', + 'curses.ascii': 'curses.ascii#module-curses.ascii', + 'curses.panel': 'curses.panel#module-curses.panel', + 'curses.textpad': 'curses#module-curses.textpad', + 'dataclasses': 'dataclasses#module-dataclasses', + 'datetime': 'datetime#module-datetime', + 'dbm': 'dbm#module-dbm', + 'dbm.dumb': 'dbm#module-dbm.dumb', + 'dbm.gnu': 'dbm#module-dbm.gnu', + 'dbm.ndbm': 'dbm#module-dbm.ndbm', + 'dbm.sqlite3': 'dbm#module-dbm.sqlite3', + 'decimal': 'decimal#module-decimal', + 'difflib': 'difflib#module-difflib', + 'dis': 'dis#module-dis', + 'distutils': 'distutils#module-distutils', + 'doctest': 'doctest#module-doctest', + 'email': 'email#module-email', + 'email.charset': 'email.charset#module-email.charset', + 'email.contentmanager': 'email.contentmanager#module-email.contentmanager', + 'email.encoders': 'email.encoders#module-email.encoders', + 'email.errors': 'email.errors#module-email.errors', + 'email.generator': 'email.generator#module-email.generator', + 'email.header': 'email.header#module-email.header', + 'email.headerregistry': 'email.headerregistry#module-email.headerregistry', + 'email.iterators': 'email.iterators#module-email.iterators', + 'email.message': 'email.message#module-email.message', + 'email.mime': 'email.mime#module-email.mime', + 'email.mime.application': 'email.mime#module-email.mime.application', + 'email.mime.audio': 'email.mime#module-email.mime.audio', + 'email.mime.base': 'email.mime#module-email.mime.base', + 'email.mime.image': 'email.mime#module-email.mime.image', + 'email.mime.message': 'email.mime#module-email.mime.message', + 'email.mime.multipart': 'email.mime#module-email.mime.multipart', + 'email.mime.nonmultipart': 'email.mime#module-email.mime.nonmultipart', + 'email.mime.text': 'email.mime#module-email.mime.text', + 'email.parser': 'email.parser#module-email.parser', + 'email.policy': 'email.policy#module-email.policy', + 'email.utils': 'email.utils#module-email.utils', + 'encodings': 'codecs#module-encodings', + 'encodings.idna': 'codecs#module-encodings.idna', + 'encodings.mbcs': 'codecs#module-encodings.mbcs', + 'encodings.utf_8_sig': 'codecs#module-encodings.utf_8_sig', + 'ensurepip': 'ensurepip#module-ensurepip', + 'enum': 'enum#module-enum', + 'errno': 'errno#module-errno', + 'faulthandler': 'faulthandler#module-faulthandler', + 'fcntl': 'fcntl#module-fcntl', + 'filecmp': 'filecmp#module-filecmp', + 'fileinput': 'fileinput#module-fileinput', + 'fnmatch': 'fnmatch#module-fnmatch', + 'fractions': 'fractions#module-fractions', + 'ftplib': 'ftplib#module-ftplib', + 'functools': 'functools#module-functools', + 'gc': 'gc#module-gc', + 'getopt': 'getopt#module-getopt', + 'getpass': 'getpass#module-getpass', + 'gettext': 'gettext#module-gettext', + 'glob': 'glob#module-glob', + 'graphlib': 'graphlib#module-graphlib', + 'grp': 'grp#module-grp', + 'gzip': 'gzip#module-gzip', + 'hashlib': 'hashlib#module-hashlib', + 'heapq': 'heapq#module-heapq', + 'hmac': 'hmac#module-hmac', + 'html': 'html#module-html', + 'html.entities': 'html.entities#module-html.entities', + 'html.parser': 'html.parser#module-html.parser', + 'http': 'http#module-http', + 'http.client': 'http.client#module-http.client', + 'http.cookiejar': 'http.cookiejar#module-http.cookiejar', + 'http.cookies': 'http.cookies#module-http.cookies', + 'http.server': 'http.server#module-http.server', + 'idlelib': 'idle#module-idlelib', + 'imaplib': 'imaplib#module-imaplib', + 'imghdr': 'imghdr#module-imghdr', + 'imp': 'imp#module-imp', + 'importlib': 'importlib#module-importlib', + 'importlib.abc': 'importlib#module-importlib.abc', + 'importlib.machinery': 'importlib#module-importlib.machinery', + 'importlib.metadata': 'importlib.metadata#module-importlib.metadata', + 'importlib.resources': 'importlib.resources#module-importlib.resources', + 'importlib.resources.abc': 'importlib.resources.abc#module-importlib.resources.abc', + 'importlib.util': 'importlib#module-importlib.util', + 'inspect': 'inspect#module-inspect', + 'io': 'io#module-io', + 'ipaddress': 'ipaddress#module-ipaddress', + 'itertools': 'itertools#module-itertools', + 'json': 'json#module-json', + 'json.tool': 'json#module-json.tool', + 'keyword': 'keyword#module-keyword', + 'linecache': 'linecache#module-linecache', + 'locale': 'locale#module-locale', + 'logging': 'logging#module-logging', + 'logging.config': 'logging.config#module-logging.config', + 'logging.handlers': 'logging.handlers#module-logging.handlers', + 'lzma': 'lzma#module-lzma', + 'mailbox': 'mailbox#module-mailbox', + 'mailcap': 'mailcap#module-mailcap', + 'marshal': 'marshal#module-marshal', + 'math': 'math#module-math', + 'math.integer': 'math.integer#module-math.integer', + 'mimetypes': 'mimetypes#module-mimetypes', + 'mmap': 'mmap#module-mmap', + 'modulefinder': 'modulefinder#module-modulefinder', + 'msilib': 'msilib#module-msilib', + 'msvcrt': 'msvcrt#module-msvcrt', + 'multiprocessing': 'multiprocessing#module-multiprocessing', + 'multiprocessing.connection': 'multiprocessing#module-multiprocessing.connection', + 'multiprocessing.dummy': 'multiprocessing#module-multiprocessing.dummy', + 'multiprocessing.managers': 'multiprocessing#module-multiprocessing.managers', + 'multiprocessing.pool': 'multiprocessing#module-multiprocessing.pool', + 'multiprocessing.shared_memory': 'multiprocessing.shared_memory#module-multiprocessing.shared_memory', + 'multiprocessing.sharedctypes': 'multiprocessing#module-multiprocessing.sharedctypes', + 'netrc': 'netrc#module-netrc', + 'nis': 'nis#module-nis', + 'nntplib': 'nntplib#module-nntplib', + 'numbers': 'numbers#module-numbers', + 'operator': 'operator#module-operator', + 'optparse': 'optparse#module-optparse', + 'os': 'os#module-os', + 'os.path': 'os.path#module-os.path', + 'ossaudiodev': 'ossaudiodev#module-ossaudiodev', + 'pathlib': 'pathlib#module-pathlib', + 'pathlib.types': 'pathlib#module-pathlib.types', + 'pdb': 'pdb#module-pdb', + 'pickle': 'pickle#module-pickle', + 'pickletools': 'pickletools#module-pickletools', + 'pipes': 'pipes#module-pipes', + 'pkgutil': 'pkgutil#module-pkgutil', + 'platform': 'platform#module-platform', + 'plistlib': 'plistlib#module-plistlib', + 'poplib': 'poplib#module-poplib', + 'posix': 'posix#module-posix', + 'pprint': 'pprint#module-pprint', + 'profile': 'profile#module-profile', + 'profiling': 'profiling#module-profiling', + 'profiling.sampling': 'profiling.sampling#module-profiling.sampling', + 'profiling.tracing': 'profiling.tracing#module-profiling.tracing', + 'pstats': 'pstats#module-pstats', + 'pty': 'pty#module-pty', + 'pwd': 'pwd#module-pwd', + 'py_compile': 'py_compile#module-py_compile', + 'pyclbr': 'pyclbr#module-pyclbr', + 'pydoc': 'pydoc#module-pydoc', + 'queue': 'queue#module-queue', + 'quopri': 'quopri#module-quopri', + 'random': 'random#module-random', + 're': 're#module-re', + 'readline': 'readline#module-readline', + 'reprlib': 'reprlib#module-reprlib', + 'resource': 'resource#module-resource', + 'rlcompleter': 'rlcompleter#module-rlcompleter', + 'runpy': 'runpy#module-runpy', + 'sched': 'sched#module-sched', + 'secrets': 'secrets#module-secrets', + 'select': 'select#module-select', + 'selectors': 'selectors#module-selectors', + 'shelve': 'shelve#module-shelve', + 'shlex': 'shlex#module-shlex', + 'shutil': 'shutil#module-shutil', + 'signal': 'signal#module-signal', + 'site': 'site#module-site', + 'sitecustomize': 'site#module-sitecustomize', + 'smtpd': 'smtpd#module-smtpd', + 'smtplib': 'smtplib#module-smtplib', + 'sndhdr': 'sndhdr#module-sndhdr', + 'socket': 'socket#module-socket', + 'socketserver': 'socketserver#module-socketserver', + 'spwd': 'spwd#module-spwd', + 'sqlite3': 'sqlite3#module-sqlite3', + 'ssl': 'ssl#module-ssl', + 'stat': 'stat#module-stat', + 'statistics': 'statistics#module-statistics', + 'string': 'string#module-string', + 'string.templatelib': 'string.templatelib#module-string.templatelib', + 'stringprep': 'stringprep#module-stringprep', + 'struct': 'struct#module-struct', + 'subprocess': 'subprocess#module-subprocess', + 'sunau': 'sunau#module-sunau', + 'symtable': 'symtable#module-symtable', + 'sys': 'sys#module-sys', + 'sys.monitoring': 'sys.monitoring#module-sys.monitoring', + 'sysconfig': 'sysconfig#module-sysconfig', + 'syslog': 'syslog#module-syslog', + 'tabnanny': 'tabnanny#module-tabnanny', + 'tarfile': 'tarfile#module-tarfile', + 'telnetlib': 'telnetlib#module-telnetlib', + 'tempfile': 'tempfile#module-tempfile', + 'termios': 'termios#module-termios', + 'test': 'test#module-test', + 'test.regrtest': 'test#module-test.regrtest', + 'test.support': 'test#module-test.support', + 'test.support.bytecode_helper': 'test#module-test.support.bytecode_helper', + 'test.support.import_helper': 'test#module-test.support.import_helper', + 'test.support.os_helper': 'test#module-test.support.os_helper', + 'test.support.script_helper': 'test#module-test.support.script_helper', + 'test.support.socket_helper': 'test#module-test.support.socket_helper', + 'test.support.threading_helper': 'test#module-test.support.threading_helper', + 'test.support.warnings_helper': 'test#module-test.support.warnings_helper', + 'textwrap': 'textwrap#module-textwrap', + 'threading': 'threading#module-threading', + 'time': 'time#module-time', + 'timeit': 'timeit#module-timeit', + 'tkinter': 'tkinter#module-tkinter', + 'tkinter.colorchooser': 'tkinter.colorchooser#module-tkinter.colorchooser', + 'tkinter.commondialog': 'dialog#module-tkinter.commondialog', + 'tkinter.dnd': 'tkinter.dnd#module-tkinter.dnd', + 'tkinter.filedialog': 'dialog#module-tkinter.filedialog', + 'tkinter.font': 'tkinter.font#module-tkinter.font', + 'tkinter.messagebox': 'tkinter.messagebox#module-tkinter.messagebox', + 'tkinter.scrolledtext': 'tkinter.scrolledtext#module-tkinter.scrolledtext', + 'tkinter.simpledialog': 'dialog#module-tkinter.simpledialog', + 'tkinter.ttk': 'tkinter.ttk#module-tkinter.ttk', + 'token': 'token#module-token', + 'tokenize': 'tokenize#module-tokenize', + 'tomllib': 'tomllib#module-tomllib', + 'trace': 'trace#module-trace', + 'traceback': 'traceback#module-traceback', + 'tracemalloc': 'tracemalloc#module-tracemalloc', + 'tty': 'tty#module-tty', + 'turtle': 'turtle#module-turtle', + 'turtledemo': 'turtle#module-turtledemo', + 'types': 'types#module-types', + 'typing': 'typing#module-typing', + 'unicodedata': 'unicodedata#module-unicodedata', + 'unittest': 'unittest#module-unittest', + 'unittest.mock': 'unittest.mock#module-unittest.mock', + 'urllib': 'urllib#module-urllib', + 'urllib.error': 'urllib.error#module-urllib.error', + 'urllib.parse': 'urllib.parse#module-urllib.parse', + 'urllib.request': 'urllib.request#module-urllib.request', + 'urllib.response': 'urllib.request#module-urllib.response', + 'urllib.robotparser': 'urllib.robotparser#module-urllib.robotparser', + 'usercustomize': 'site#module-usercustomize', + 'uu': 'uu#module-uu', + 'uuid': 'uuid#module-uuid', + 'venv': 'venv#module-venv', + 'warnings': 'warnings#module-warnings', + 'wave': 'wave#module-wave', + 'weakref': 'weakref#module-weakref', + 'webbrowser': 'webbrowser#module-webbrowser', + 'winreg': 'winreg#module-winreg', + 'winsound': 'winsound#module-winsound', + 'wsgiref': 'wsgiref#module-wsgiref', + 'wsgiref.handlers': 'wsgiref#module-wsgiref.handlers', + 'wsgiref.headers': 'wsgiref#module-wsgiref.headers', + 'wsgiref.simple_server': 'wsgiref#module-wsgiref.simple_server', + 'wsgiref.types': 'wsgiref#module-wsgiref.types', + 'wsgiref.util': 'wsgiref#module-wsgiref.util', + 'wsgiref.validate': 'wsgiref#module-wsgiref.validate', + 'xdrlib': 'xdrlib#module-xdrlib', + 'xml': 'xml#module-xml', + 'xml.dom': 'xml.dom#module-xml.dom', + 'xml.dom.minidom': 'xml.dom.minidom#module-xml.dom.minidom', + 'xml.dom.pulldom': 'xml.dom.pulldom#module-xml.dom.pulldom', + 'xml.etree.ElementInclude': 'xml.etree.elementtree#module-xml.etree.ElementInclude', + 'xml.etree.ElementTree': 'xml.etree.elementtree#module-xml.etree.ElementTree', + 'xml.parsers.expat': 'pyexpat#module-xml.parsers.expat', + 'xml.parsers.expat.errors': 'pyexpat#module-xml.parsers.expat.errors', + 'xml.parsers.expat.model': 'pyexpat#module-xml.parsers.expat.model', + 'xml.sax': 'xml.sax#module-xml.sax', + 'xml.sax.handler': 'xml.sax.handler#module-xml.sax.handler', + 'xml.sax.saxutils': 'xml.sax.utils#module-xml.sax.saxutils', + 'xml.sax.xmlreader': 'xml.sax.reader#module-xml.sax.xmlreader', + 'xmlrpc': 'xmlrpc#module-xmlrpc', + 'xmlrpc.client': 'xmlrpc.client#module-xmlrpc.client', + 'xmlrpc.server': 'xmlrpc.server#module-xmlrpc.server', + 'zipapp': 'zipapp#module-zipapp', + 'zipfile': 'zipfile#module-zipfile', + 'zipimport': 'zipimport#module-zipimport', + 'zlib': 'zlib#module-zlib', + 'zoneinfo': 'zoneinfo#module-zoneinfo', +} diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 5f7e14a79d3356..5f61001c46b79c 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,4 +1,4 @@ -# Autogenerated by Sphinx on Tue May 6 18:33:44 2025 +# Autogenerated by Sphinx on Thu May 7 16:26:23 2026 # as part of the release process. topics = { @@ -46,11 +46,10 @@ | "[" [target_list] "]" | attributeref | subscription - | slicing | "*" target -(See section Primaries for the syntax definitions for *attributeref*, -*subscription*, and *slicing*.) +(See section Primaries for the syntax definitions for *attributeref* +and *subscription*.) An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter @@ -59,12 +58,11 @@ Assignment is defined recursively depending on the form of the target (list). When a target is part of a mutable object (an attribute -reference, subscription or slicing), the mutable object must -ultimately perform the assignment and decide about its validity, and -may raise an exception if the assignment is unacceptable. The rules -observed by various types and the exceptions raised are given with the -definition of the object types (see section The standard type -hierarchy). +reference or subscription), the mutable object must ultimately perform +the assignment and decide about its validity, and may raise an +exception if the assignment is unacceptable. The rules observed by +various types and the exceptions raised are given with the definition +of the object types (see section The standard type hierarchy). Assignment of an object to a target list, optionally enclosed in parentheses or square brackets, is recursively defined as follows. @@ -130,9 +128,13 @@ class Cls: attributes, such as properties created with "property()". * If the target is a subscription: The primary expression in the - reference is evaluated. It should yield either a mutable sequence - object (such as a list) or a mapping object (such as a dictionary). - Next, the subscript expression is evaluated. + reference is evaluated. Next, the subscript expression is evaluated. + Then, the primary’s "__setitem__()" method is called with two + arguments: the subscript and the assigned object. + + Typically, "__setitem__()" is defined on mutable sequence objects + (such as lists) and mapping objects (such as dictionaries), and + behaves as follows. If the primary is a mutable sequence object (such as a list), the subscript must yield an integer. If it is negative, the sequence’s @@ -149,27 +151,17 @@ class Cls: existing key/value pair with the same key value, or insert a new key/value pair (if no key with the same value existed). - For user-defined objects, the "__setitem__()" method is called with - appropriate arguments. - -* If the target is a slicing: The primary expression in the reference - is evaluated. It should yield a mutable sequence object (such as a - list). The assigned object should be a sequence object of the same - type. Next, the lower and upper bound expressions are evaluated, - insofar they are present; defaults are zero and the sequence’s - length. The bounds should evaluate to integers. If either bound is - negative, the sequence’s length is added to it. The resulting - bounds are clipped to lie between zero and the sequence’s length, - inclusive. Finally, the sequence object is asked to replace the - slice with the items of the assigned sequence. The length of the - slice may be different from the length of the assigned sequence, - thus changing the length of the target sequence, if the target - sequence allows it. - -**CPython implementation detail:** In the current implementation, the -syntax for targets is taken to be the same as for expressions, and -invalid syntax is rejected during the code generation phase, causing -less detailed error messages. + If the target is a slicing: The primary expression should evaluate + to a mutable sequence object (such as a list). The assigned object + should be *iterable*. The slicing’s lower and upper bounds should be + integers; if they are "None" (or not present), the defaults are zero + and the sequence’s length. If either bound is negative, the + sequence’s length is added to it. The resulting bounds are clipped + to lie between zero and the sequence’s length, inclusive. Finally, + the sequence object is asked to replace the slice with the items of + the assigned sequence. The length of the slice may be different + from the length of the assigned sequence, thus changing the length + of the target sequence, if the target sequence allows it. Although the definition of assignment implies that overlaps between the left-hand side and the right-hand side are ‘simultaneous’ (for @@ -196,7 +188,7 @@ class Cls: binary operation and an assignment statement: augmented_assignment_stmt: augtarget augop (expression_list | yield_expression) - augtarget: identifier | attributeref | subscription | slicing + augtarget: identifier | attributeref | subscription augop: "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**=" | ">>=" | "<<=" | "&=" | "^=" | "|=" @@ -369,13 +361,12 @@ async def func(param1, param2): Is semantically equivalent to: - iter = (ITER) - iter = type(iter).__aiter__(iter) + iter = (ITER).__aiter__() running = True while running: try: - TARGET = await type(iter).__anext__(iter) + TARGET = await iter.__anext__() except StopAsyncIteration: running = False else: @@ -383,7 +374,8 @@ async def func(param1, param2): else: SUITE2 -See also "__aiter__()" and "__anext__()" for details. +except that implicit special method lookup is used for "__aiter__()" +and "__anext__()". It is a "SyntaxError" to use an "async for" statement outside the body of a coroutine function. @@ -405,9 +397,9 @@ async def func(param1, param2): is semantically equivalent to: manager = (EXPRESSION) - aenter = type(manager).__aenter__ - aexit = type(manager).__aexit__ - value = await aenter(manager) + aenter = manager.__aenter__ + aexit = manager.__aexit__ + value = await aenter() hit_except = False try: @@ -415,13 +407,14 @@ async def func(param1, param2): SUITE except: hit_except = True - if not await aexit(manager, *sys.exc_info()): + if not await aexit(*sys.exc_info()): raise finally: if not hit_except: - await aexit(manager, None, None, None) + await aexit(None, None, None) -See also "__aenter__()" and "__aexit__()" for details. +except that implicit special method lookup is used for "__aenter__()" +and "__aexit__()". It is a "SyntaxError" to use an "async with" statement outside the body of a coroutine function. @@ -435,9 +428,9 @@ async def func(param1, param2): 'atom-identifiers': r'''Identifiers (Names) ******************* -An identifier occurring as an atom is a name. See section Identifiers -and keywords for lexical definition and section Naming and binding for -documentation of naming and binding. +An identifier occurring as an atom is a name. See section Names +(identifiers and keywords) for lexical definition and section Naming +and binding for documentation of naming and binding. When the name is bound to an object, evaluation of the atom yields that object. When a name is not bound, an attempt to evaluate it @@ -489,22 +482,122 @@ async def func(param1, param2): 'atom-literals': r'''Literals ******** -Python supports string and bytes literals and various numeric -literals: +A *literal* is a textual representation of a value. Python supports +numeric, string and bytes literals. Format strings and template +strings are treated as string literals. + +Numeric literals consist of a single "NUMBER" token, which names an +integer, floating-point number, or an imaginary number. See the +Numeric literals section in Lexical analysis documentation for +details. + +String and bytes literals may consist of several tokens. See section +String literal concatenation for details. + +Note that negative and complex numbers, like "-3" or "3+4.2j", are +syntactically not literals, but unary or binary arithmetic operations +involving the "-" or "+" operator. - literal: stringliteral | bytesliteral - | integer | floatnumber | imagnumber +Evaluation of a literal yields an object of the given type ("int", +"float", "complex", "str", "bytes", or "Template") with the given +value. The value may be approximated in the case of floating-point and +imaginary literals. -Evaluation of a literal yields an object of the given type (string, -bytes, integer, floating-point number, complex number) with the given -value. The value may be approximated in the case of floating-point -and imaginary (complex) literals. See section Literals for details. +The formal grammar for literals is: + + literal: strings | NUMBER + + +Literals and object identity +============================ All literals correspond to immutable data types, and hence the object’s identity is less important than its value. Multiple evaluations of literals with the same value (either the same occurrence in the program text or a different occurrence) may obtain the same object or a different object with the same value. + +CPython implementation detail: For example, in CPython, *small* +integers with the same value evaluate to the same object: + + >>> x = 7 + >>> y = 7 + >>> x is y + True + +However, large integers evaluate to different objects: + + >>> x = 123456789 + >>> y = 123456789 + >>> x is y + False + +This behavior may change in future versions of CPython. In particular, +the boundary between “small” and “large” integers has already changed +in the past.CPython will emit a "SyntaxWarning" when you compare +literals using "is": + + >>> x = 7 + >>> x is 7 + :1: SyntaxWarning: "is" with 'int' literal. Did you mean "=="? + True + +See When can I rely on identity tests with the is operator? for more +information. + +Template strings are immutable but may reference mutable objects as +"Interpolation" values. For the purposes of this section, two +t-strings have the “same value” if both their structure and the +*identity* of the values match. + +**CPython implementation detail:** Currently, each evaluation of a +template string results in a different object. + + +String literal concatenation +============================ + +Multiple adjacent string or bytes literals, possibly using different +quoting conventions, are allowed, and their meaning is the same as +their concatenation: + + >>> "hello" 'world' + "helloworld" + +This feature is defined at the syntactical level, so it only works +with literals. To concatenate string expressions at run time, the ‘+’ +operator may be used: + + >>> greeting = "Hello" + >>> space = " " + >>> name = "Blaise" + >>> print(greeting + space + name) # not: print(greeting space name) + Hello Blaise + +Literal concatenation can freely mix raw strings, triple-quoted +strings, and formatted string literals. For example: + + >>> "Hello" r', ' f"{name}!" + "Hello, Blaise!" + +This feature can be used to reduce the number of backslashes needed, +to split long strings conveniently across long lines, or even to add +comments to parts of strings. For example: + + re.compile("[A-Za-z_]" # letter or underscore + "[A-Za-z0-9_]*" # letter, digit or underscore + ) + +However, bytes literals may only be combined with other byte literals; +not with string literals of any kind. Also, template string literals +may only be combined with other template string literals: + + >>> t"Hello" t"{name}!" + Template(strings=('Hello', '!'), interpolations=(...)) + +Formally: + + strings: (STRING | fstring)+ | tstring+ ''', 'attribute-access': r'''Customizing attribute access **************************** @@ -588,6 +681,9 @@ class instances. Customizing module attribute access =================================== +module.__getattr__() +module.__dir__() + Special names "__getattr__" and "__dir__" can be also used to customize access to module attributes. The "__getattr__" function at the module level should accept one argument which is the name of an @@ -603,6 +699,8 @@ class instances. present, this function overrides the standard "dir()" search on a module. +module.__class__ + For a more fine grained customization of the module behavior (setting attributes, properties, etc.), one can set the "__class__" attribute of a module object to a subclass of "types.ModuleType". For example: @@ -814,9 +912,10 @@ class to a new value, *value*. renders the meaning of the program undefined. In the future, a check may be added to prevent this. -* "TypeError" will be raised if nonempty *__slots__* are defined for a - class derived from a ""variable-length" built-in type" such as - "int", "bytes", and "tuple". +* "TypeError" will be raised if *__slots__* other than *__dict__* and + *__weakref__* are defined for a class derived from a ""variable- + length" built-in type" such as "int", "bytes", and "type", except + "tuple". * Any non-string *iterable* may be assigned to *__slots__*. @@ -836,6 +935,10 @@ class derived from a ""variable-length" built-in type" such as * If an *iterator* is used for *__slots__* then a *descriptor* is created for each of the iterator’s values. However, the *__slots__* attribute will be an empty iterator. + +Changed in version 3.15: Allowed defining the *__dict__* and +*__weakref__* *__slots__* for any class. Allowed defining any +*__slots__* for a class derived from "tuple". ''', 'attribute-references': r'''Attribute references ******************** @@ -865,7 +968,7 @@ class derived from a ""variable-length" built-in type" such as binary operation and an assignment statement: augmented_assignment_stmt: augtarget augop (expression_list | yield_expression) - augtarget: identifier | attributeref | subscription | slicing + augtarget: identifier | attributeref | subscription augop: "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**=" | ">>=" | "<<=" | "&=" | "^=" | "|=" @@ -959,7 +1062,7 @@ class and instance attributes applies as for regular assignments. The "%" (modulo) operator yields the remainder from the division of the first argument by the second. The numeric arguments are first -converted to a common type. A zero right argument raises the +converted to a common type. A zero right argument raises the "ZeroDivisionError" exception. The arguments may be floating-point numbers, e.g., "3.14%0.7" equals "0.34" (since "3.14" equals "4*0.7 + 0.34".) The modulo operator always yields a result with the same sign @@ -1047,12 +1150,33 @@ class and instance attributes applies as for regular assignments. 'bltin-ellipsis-object': r'''The Ellipsis Object ******************* -This object is commonly used by slicing (see Slicings). It supports -no special operations. There is exactly one ellipsis object, named -"Ellipsis" (a built-in name). "type(Ellipsis)()" produces the +This object is commonly used to indicate that something is omitted. It +supports no special operations. There is exactly one ellipsis object, +named "Ellipsis" (a built-in name). "type(Ellipsis)()" produces the "Ellipsis" singleton. It is written as "Ellipsis" or "...". + +In typical use, "..." as the "Ellipsis" object appears in a few +different places, for instance: + +* In type annotations, such as callable arguments or tuple elements. + +* As the body of a function instead of a pass statement. + +* In third-party libraries, such as Numpy’s slicing and striding. + +Python also uses three dots in ways that are not "Ellipsis" objects, +for instance: + +* Doctest’s "ELLIPSIS", as a pattern for missing content. + +* The default Python prompt of the *interactive* shell when partial + input is incomplete. + +Lastly, the Python documentation often uses three dots in conventional +English usage to mean omitted content, even in code examples that also +use them as the "Ellipsis". ''', 'bltin-null-object': r'''The Null Object *************** @@ -1314,6 +1438,9 @@ class Foo: class Foo(object): pass +There may be one or more base classes; see Multiple inheritance below +for more information. + The class’s suite is then executed in a new execution frame (see Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly @@ -1377,6 +1504,115 @@ class attributes; they are shared by instances. Instance attributes **PEP 3129** - Class Decorators The proposal that added class decorators. Function and method decorators were introduced in **PEP 318**. + + +Multiple inheritance +==================== + +Python classes may have multiple base classes, a technique known as +*multiple inheritance*. The base classes are specified in the class +definition by listing them in parentheses after the class name, +separated by commas. For example, the following class definition: + + >>> class A: pass + >>> class B: pass + >>> class C(A, B): pass + +defines a class "C" that inherits from classes "A" and "B". + +The *method resolution order* (MRO) is the order in which base classes +are searched when looking up an attribute on a class. See The Python +2.3 Method Resolution Order for a description of how Python determines +the MRO for a class. + +Multiple inheritance is not always allowed. Attempting to define a +class with multiple inheritance will raise an error if one of the +bases does not allow subclassing, if a consistent MRO cannot be +created, if no valid metaclass can be determined, or if there is an +instance layout conflict. We’ll discuss each of these in turn. + +First, all base classes must allow subclassing. While most classes +allow subclassing, some built-in classes do not, such as "bool": + + >>> class SubBool(bool): # TypeError + ... pass + Traceback (most recent call last): + ... + TypeError: type 'bool' is not an acceptable base type + +In the resolved MRO of a class, the class’s bases appear in the order +they were specified in the class’s bases list. Additionally, the MRO +always lists a child class before any of its bases. A class definition +will fail if it is impossible to resolve a consistent MRO that +satisfies these rules from the list of bases provided: + + >>> class Base: pass + >>> class Child(Base): pass + >>> class Grandchild(Base, Child): pass # TypeError + Traceback (most recent call last): + ... + TypeError: Cannot create a consistent method resolution order (MRO) for bases Base, Child + +In the MRO of "Grandchild", "Base" must appear before "Child" because +it is first in the base class list, but it must also appear after +"Child" because it is a parent of "Child". This is a contradiction, so +the class cannot be defined. + +If some of the bases have a custom *metaclass*, the metaclass of the +resulting class is chosen among the metaclasses of the bases and the +explicitly specified metaclass of the child class. It must be a +metaclass that is a subclass of all other candidate metaclasses. If no +such metaclass exists among the candidates, the class cannot be +created, as explained in Determining the appropriate metaclass. + +Finally, the instance layouts of the bases must be compatible. This +means that it must be possible to compute a *solid base* for the +class. Exactly which classes are solid bases depends on the Python +implementation. + +**CPython implementation detail:** In CPython, a class is a solid base +if it has a nonempty "__slots__" definition. Many but not all classes +defined in C are also solid bases, including most builtins (such as +"int" or "BaseException") but excluding most concrete "Exception" +classes. Generally, a C class is a solid base if its underlying struct +is different in size from its base class. + +Every class has a solid base. "object", the base class, has itself as +its solid base. If there is a single base, the child class’s solid +base is that class if it is a solid base, or else the base class’s +solid base. If there are multiple bases, we first find the solid base +for each base class to produce a list of candidate solid bases. If +there is a unique solid base that is a subclass of all others, then +that class is the solid base. Otherwise, class creation fails. + +Example: + + >>> class Solid1: + ... __slots__ = ("solid1",) + >>> + >>> class Solid2: + ... __slots__ = ("solid2",) + >>> + >>> class SolidChild(Solid1): + ... __slots__ = ("solid_child",) + >>> + >>> class C1: # solid base is `object` + ... pass + >>> + >>> # OK: solid bases are `Solid1` and `object`, and `Solid1` is a subclass of `object`. + >>> class C2(Solid1, C1): # solid base is `Solid1` + ... pass + >>> + >>> # OK: solid bases are `SolidChild` and `Solid1`, and `SolidChild` is a subclass of `Solid1`. + >>> class C3(SolidChild, Solid1): # solid base is `SolidChild` + ... pass + >>> + >>> # Error: solid bases are `Solid1` and `Solid2`, but neither is a subclass of the other. + >>> class C4(Solid1, Solid2): # error: no single solid base + ... pass + Traceback (most recent call last): + ... + TypeError: multiple bases have instance lay-out conflict ''', 'comparisons': r'''Comparisons *********** @@ -1724,16 +1960,16 @@ class attributes; they are shared by instances. Instance attributes The "for" statement is used to iterate over the elements of a sequence (such as a string, tuple or list) or other iterable object: - for_stmt: "for" target_list "in" starred_list ":" suite + for_stmt: "for" target_list "in" starred_expression_list ":" suite ["else" ":" suite] -The "starred_list" expression is evaluated once; it should yield an -*iterable* object. An *iterator* is created for that iterable. The -first item provided by the iterator is then assigned to the target -list using the standard rules for assignments (see Assignment -statements), and the suite is executed. This repeats for each item -provided by the iterator. When the iterator is exhausted, the suite -in the "else" clause, if present, is executed, and the loop +The "starred_expression_list" expression is evaluated once; it should +yield an *iterable* object. An *iterator* is created for that +iterable. The first item provided by the iterator is then assigned to +the target list using the standard rules for assignments (see +Assignment statements), and the suite is executed. This repeats for +each item provided by the iterator. When the iterator is exhausted, +the suite in the "else" clause, if present, is executed, and the loop terminates. A "break" statement executed in the first suite terminates the loop @@ -1874,15 +2110,29 @@ class attributes; they are shared by instances. Instance attributes "except*" clause ---------------- -The "except*" clause(s) are used for handling "ExceptionGroup"s. The -exception type for matching is interpreted as in the case of "except", -but in the case of exception groups we can have partial matches when -the type matches some of the exceptions in the group. This means that -multiple "except*" clauses can execute, each handling part of the -exception group. Each clause executes at most once and handles an -exception group of all matching exceptions. Each exception in the -group is handled by at most one "except*" clause, the first that -matches it. +The "except*" clause(s) specify one or more handlers for groups of +exceptions ("BaseExceptionGroup" instances). A "try" statement can +have either "except" or "except*" clauses, but not both. The exception +type for matching is mandatory in the case of "except*", so "except*:" +is a syntax error. The type is interpreted as in the case of "except", +but matching is performed on the exceptions contained in the group +that is being handled. An "TypeError" is raised if a matching type is +a subclass of "BaseExceptionGroup", because that would have ambiguous +semantics. + +When an exception group is raised in the try block, each "except*" +clause splits (see "split()") it into the subgroups of matching and +non-matching exceptions. If the matching subgroup is not empty, it +becomes the handled exception (the value returned from +"sys.exception()") and assigned to the target of the "except*" clause +(if there is one). Then, the body of the "except*" clause executes. If +the non-matching subgroup is not empty, it is processed by the next +"except*" in the same manner. This continues until all exceptions in +the group have been matched, or the last "except*" clause has run. + +After all "except*" clauses execute, the group of unhandled exceptions +is merged with any exceptions that were raised or re-raised from +within "except*" clauses. This merged exception group propagates on.: >>> try: ... raise ExceptionGroup("eg", @@ -1895,33 +2145,27 @@ class attributes; they are shared by instances. Instance attributes caught with nested (TypeError(2),) caught with nested (OSError(3), OSError(4)) + Exception Group Traceback (most recent call last): - | File "", line 2, in - | ExceptionGroup: eg + | File "", line 2, in + | raise ExceptionGroup("eg", + | [ValueError(1), TypeError(2), OSError(3), OSError(4)]) + | ExceptionGroup: eg (1 sub-exception) +-+---------------- 1 ---------------- | ValueError: 1 +------------------------------------ -Any remaining exceptions that were not handled by any "except*" clause -are re-raised at the end, along with all exceptions that were raised -from within the "except*" clauses. If this list contains more than one -exception to reraise, they are combined into an exception group. - -If the raised exception is not an exception group and its type matches -one of the "except*" clauses, it is caught and wrapped by an exception -group with an empty message string. +If the exception raised from the "try" block is not an exception group +and its type matches one of the "except*" clauses, it is caught and +wrapped by an exception group with an empty message string. This +ensures that the type of the target "e" is consistently +"BaseExceptionGroup": >>> try: ... raise BlockingIOError ... except* BlockingIOError as e: ... print(repr(e)) ... - ExceptionGroup('', (BlockingIOError())) - -An "except*" clause must have a matching expression; it cannot be -"except*:". Furthermore, this expression cannot contain exception -group types, because that would have ambiguous semantics. + ExceptionGroup('', (BlockingIOError(),)) -It is not possible to mix "except" and "except*" in the same "try". "break", "continue" and "return" cannot appear in an "except*" clause. @@ -1938,11 +2182,11 @@ class attributes; they are shared by instances. Instance attributes ---------------- If "finally" is present, it specifies a ‘cleanup’ handler. The "try" -clause is executed, including any "except" and "else" clauses. If an +clause is executed, including any "except" and "else" clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The "finally" clause is executed. If there is a saved exception it is re-raised at the end of the "finally" -clause. If the "finally" clause raises another exception, the saved +clause. If the "finally" clause raises another exception, the saved exception is set as the context of the new exception. If the "finally" clause executes a "return", "break" or "continue" statement, the saved exception is discarded. For example, this function returns 42. @@ -2040,9 +2284,9 @@ def foo(): is semantically equivalent to: manager = (EXPRESSION) - enter = type(manager).__enter__ - exit = type(manager).__exit__ - value = enter(manager) + enter = manager.__enter__ + exit = manager.__exit__ + value = enter() hit_except = False try: @@ -2050,11 +2294,14 @@ def foo(): SUITE except: hit_except = True - if not exit(manager, *sys.exc_info()): + if not exit(*sys.exc_info()): raise finally: if not hit_except: - exit(manager, None, None, None) + exit(None, None, None) + +except that implicit special method lookup is used for "__enter__()" +and "__exit__()". With more than one item, the context managers are processed as if multiple "with" statements were nested: @@ -2097,9 +2344,9 @@ def foo(): The match statement is used for pattern matching. Syntax: match_stmt: 'match' subject_expr ":" NEWLINE INDENT case_block+ DEDENT - subject_expr: star_named_expression "," star_named_expressions? - | named_expression - case_block: 'case' patterns [guard] ":" block + subject_expr: `!star_named_expression` "," `!star_named_expressions`? + | `!named_expression` + case_block: 'case' patterns [guard] ":" `!block` Note: @@ -2190,7 +2437,7 @@ def foo(): Guards ------ - guard: "if" named_expression + guard: "if" `!named_expression` A "guard" (which is part of the "case") must succeed for code inside the "case" block to execute. It takes the form: "if" followed by an @@ -2325,11 +2572,12 @@ def foo(): | "None" | "True" | "False" - signed_number: ["-"] NUMBER + signed_number: ["+" | "-"] NUMBER The rule "strings" and the token "NUMBER" are defined in the standard Python grammar. Triple-quoted strings are supported. Raw strings and -byte strings are supported. f-strings are not supported. +byte strings are supported. f-strings and t-strings are not +supported. The forms "signed_number '+' NUMBER" and "signed_number '-' NUMBER" are for expressing complex numbers; they require a real number on the @@ -2483,7 +2731,7 @@ def foo(): Note: The length of the subject sequence is obtained via "len()" (i.e. - via the "__len__()" protocol). This length may be cached by the + via the "__len__()" protocol). This length may be cached by the interpreter in a similar manner as value patterns. In simple terms "[P1, P2, P3," … ", P]" matches only if all the @@ -2591,7 +2839,7 @@ def foo(): If only keyword patterns are present, they are processed as follows, one by one: - I. The keyword is looked up as an attribute on the subject. + 1. The keyword is looked up as an attribute on the subject. * If this raises an exception other than "AttributeError", the exception bubbles up. @@ -2603,14 +2851,14 @@ def foo(): the class pattern fails; if this succeeds, the match proceeds to the next keyword. - II. If all keyword patterns succeed, the class pattern succeeds. + 2. If all keyword patterns succeed, the class pattern succeeds. If any positional patterns are present, they are converted to keyword patterns using the "__match_args__" attribute on the class "name_or_attr" before matching: - I. The equivalent of "getattr(cls, "__match_args__", ())" is - called. + 1. The equivalent of "getattr(cls, "__match_args__", ())" is + called. * If this raises an exception, the exception bubbles up. @@ -2631,9 +2879,9 @@ def foo(): Customizing positional arguments in class pattern matching - II. Once all positional patterns have been converted to keyword - patterns, - the match proceeds as if there were only keyword patterns. + 2. Once all positional patterns have been converted to keyword + patterns, the match proceeds as if there were only keyword + patterns. For the following built-in types the handling of positional subpatterns is different: @@ -2866,6 +3114,9 @@ class Foo: class Foo(object): pass +There may be one or more base classes; see Multiple inheritance below +for more information. + The class’s suite is then executed in a new execution frame (see Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly @@ -2931,6 +3182,115 @@ class attributes; they are shared by instances. Instance attributes decorators were introduced in **PEP 318**. +Multiple inheritance +-------------------- + +Python classes may have multiple base classes, a technique known as +*multiple inheritance*. The base classes are specified in the class +definition by listing them in parentheses after the class name, +separated by commas. For example, the following class definition: + + >>> class A: pass + >>> class B: pass + >>> class C(A, B): pass + +defines a class "C" that inherits from classes "A" and "B". + +The *method resolution order* (MRO) is the order in which base classes +are searched when looking up an attribute on a class. See The Python +2.3 Method Resolution Order for a description of how Python determines +the MRO for a class. + +Multiple inheritance is not always allowed. Attempting to define a +class with multiple inheritance will raise an error if one of the +bases does not allow subclassing, if a consistent MRO cannot be +created, if no valid metaclass can be determined, or if there is an +instance layout conflict. We’ll discuss each of these in turn. + +First, all base classes must allow subclassing. While most classes +allow subclassing, some built-in classes do not, such as "bool": + + >>> class SubBool(bool): # TypeError + ... pass + Traceback (most recent call last): + ... + TypeError: type 'bool' is not an acceptable base type + +In the resolved MRO of a class, the class’s bases appear in the order +they were specified in the class’s bases list. Additionally, the MRO +always lists a child class before any of its bases. A class definition +will fail if it is impossible to resolve a consistent MRO that +satisfies these rules from the list of bases provided: + + >>> class Base: pass + >>> class Child(Base): pass + >>> class Grandchild(Base, Child): pass # TypeError + Traceback (most recent call last): + ... + TypeError: Cannot create a consistent method resolution order (MRO) for bases Base, Child + +In the MRO of "Grandchild", "Base" must appear before "Child" because +it is first in the base class list, but it must also appear after +"Child" because it is a parent of "Child". This is a contradiction, so +the class cannot be defined. + +If some of the bases have a custom *metaclass*, the metaclass of the +resulting class is chosen among the metaclasses of the bases and the +explicitly specified metaclass of the child class. It must be a +metaclass that is a subclass of all other candidate metaclasses. If no +such metaclass exists among the candidates, the class cannot be +created, as explained in Determining the appropriate metaclass. + +Finally, the instance layouts of the bases must be compatible. This +means that it must be possible to compute a *solid base* for the +class. Exactly which classes are solid bases depends on the Python +implementation. + +**CPython implementation detail:** In CPython, a class is a solid base +if it has a nonempty "__slots__" definition. Many but not all classes +defined in C are also solid bases, including most builtins (such as +"int" or "BaseException") but excluding most concrete "Exception" +classes. Generally, a C class is a solid base if its underlying struct +is different in size from its base class. + +Every class has a solid base. "object", the base class, has itself as +its solid base. If there is a single base, the child class’s solid +base is that class if it is a solid base, or else the base class’s +solid base. If there are multiple bases, we first find the solid base +for each base class to produce a list of candidate solid bases. If +there is a unique solid base that is a subclass of all others, then +that class is the solid base. Otherwise, class creation fails. + +Example: + + >>> class Solid1: + ... __slots__ = ("solid1",) + >>> + >>> class Solid2: + ... __slots__ = ("solid2",) + >>> + >>> class SolidChild(Solid1): + ... __slots__ = ("solid_child",) + >>> + >>> class C1: # solid base is `object` + ... pass + >>> + >>> # OK: solid bases are `Solid1` and `object`, and `Solid1` is a subclass of `object`. + >>> class C2(Solid1, C1): # solid base is `Solid1` + ... pass + >>> + >>> # OK: solid bases are `SolidChild` and `Solid1`, and `SolidChild` is a subclass of `Solid1`. + >>> class C3(SolidChild, Solid1): # solid base is `SolidChild` + ... pass + >>> + >>> # Error: solid bases are `Solid1` and `Solid2`, but neither is a subclass of the other. + >>> class C4(Solid1, Solid2): # error: no single solid base + ... pass + Traceback (most recent call last): + ... + TypeError: multiple bases have instance lay-out conflict + + Coroutines ========== @@ -2985,13 +3345,12 @@ async def func(param1, param2): Is semantically equivalent to: - iter = (ITER) - iter = type(iter).__aiter__(iter) + iter = (ITER).__aiter__() running = True while running: try: - TARGET = await type(iter).__anext__(iter) + TARGET = await iter.__anext__() except StopAsyncIteration: running = False else: @@ -2999,7 +3358,8 @@ async def func(param1, param2): else: SUITE2 -See also "__aiter__()" and "__anext__()" for details. +except that implicit special method lookup is used for "__aiter__()" +and "__anext__()". It is a "SyntaxError" to use an "async for" statement outside the body of a coroutine function. @@ -3021,9 +3381,9 @@ async def func(param1, param2): is semantically equivalent to: manager = (EXPRESSION) - aenter = type(manager).__aenter__ - aexit = type(manager).__aexit__ - value = await aenter(manager) + aenter = manager.__aenter__ + aexit = manager.__aexit__ + value = await aenter() hit_except = False try: @@ -3031,13 +3391,14 @@ async def func(param1, param2): SUITE except: hit_except = True - if not await aexit(manager, *sys.exc_info()): + if not await aexit(*sys.exc_info()): raise finally: if not hit_except: - await aexit(manager, None, None, None) + await aexit(None, None, None) -See also "__aenter__()" and "__aexit__()" for details. +except that implicit special method lookup is used for "__aenter__()" +and "__aexit__()". It is a "SyntaxError" to use an "async with" statement outside the body of a coroutine function. @@ -3304,7 +3665,7 @@ def f() -> annotation: ... introspects and uses the annotations (such as "dataclasses" or "functools.singledispatch()"). -By default, annotations are lazily evaluated in a annotation scope. +By default, annotations are lazily evaluated in an annotation scope. This means that they are not evaluated when the code containing the annotation is evaluated. Instead, the interpreter saves information that can be used to evaluate the annotation later if requested. The @@ -3318,6 +3679,12 @@ def f() -> annotation: ... >>> f.__annotations__ {'param': 'annotation'} +This future statement will be deprecated and removed in a future +version of Python, but not before Python 3.13 reaches its end of life +(see **PEP 749**). When it is used, introspection tools like +"annotationlib.get_annotations()" and "typing.get_type_hints()" are +less likely to be able to resolve annotations at runtime. + -[ Footnotes ]- [1] The exception is propagated to the invocation stack unless there @@ -3442,19 +3809,13 @@ def f() -> annotation: ... When a description of an arithmetic operator below uses the phrase “the numeric arguments are converted to a common real type”, this -means that the operator implementation for built-in types works as -follows: - -* If both arguments are complex numbers, no conversion is performed; +means that the operator implementation for built-in numeric types +works as described in the Numeric Types section of the standard +library documentation. -* if either argument is a complex or a floating-point number, the - other is converted to a floating-point number; - -* otherwise, both must be integers and no conversion is necessary. - -Some additional rules apply for certain operators (e.g., a string as a -left argument to the ‘%’ operator). Extensions must define their own -conversion behavior. +Some additional rules apply for certain operators and non-numeric +operands (for example, a string as a left argument to the "%" +operator). Extensions must define their own conversion behavior. ''', 'customization': r'''Basic customization ******************* @@ -3611,7 +3972,7 @@ def f() -> annotation: ... formatting to one of the built-in types, or use a similar formatting option syntax. - See Format Specification Mini-Language for a description of the + See Format specification mini-language for a description of the standard formatting syntax. The return value must be a string object. @@ -3748,7 +4109,7 @@ def __hash__(self): intended to provide protection against a denial-of-service caused by carefully chosen inputs that exploit the worst case performance of a dict insertion, *O*(*n*^2) complexity. See - http://ocert.org/advisories/ocert-2011-003.html for + https://ocert.org/advisories/ocert-2011-003.html for details.Changing hash values affects the iteration order of sets. Python has never made guarantees about this ordering (and it typically varies between 32-bit and 64-bit builds).See also @@ -3829,10 +4190,14 @@ def double(x): available for commands and command arguments, e.g. the current global and local names are offered as arguments of the "p" command. + +Command-line interface +====================== + You can also invoke "pdb" from the command line to debug other scripts. For example: - python -m pdb [-c command] (-m module | pyfile) [args ...] + python -m pdb [-c command] (-m module | -p pid | pyfile) [args ...] When invoked as a module, pdb will automatically enter post-mortem debugging if the program being debugged exits abnormally. After post- @@ -3844,7 +4209,7 @@ def double(x): -c, --command To execute commands as if given in a ".pdbrc" file; see Debugger - Commands. + commands. Changed in version 3.2: Added the "-c" option. @@ -3856,6 +4221,23 @@ def double(x): Changed in version 3.7: Added the "-m" option. +-p, --pid + + Attach to the process with the specified PID. + + Added in version 3.14. + +To attach to a running Python process for remote debugging, use the +"-p" or "--pid" option with the target process’s PID: + + python -m pdb -p 1234 + +Note: + + Attaching to a process that is blocked in a system call or waiting + for I/O will only work once the next bytecode instruction is + executed or when the process receives a signal. + Typical usage to execute a statement under control of the debugger is: >>> import pdb @@ -4048,7 +4430,7 @@ class pdb.Pdb(completekey='tab', stdin=None, stdout=None, skip=None, nosigint=Fa See the documentation for the functions explained above. -Debugger Commands +Debugger commands ================= The commands recognized by the debugger are listed below. Most @@ -4228,8 +4610,8 @@ class pdb.Pdb(completekey='tab', stdin=None, stdout=None, skip=None, nosigint=Fa To remove all commands from a breakpoint, type "commands" and follow it immediately with "end"; that is, give no commands. - With no *bpnumber* argument, "commands" refers to the last - breakpoint set. + With no *bpnumber* argument, "commands" refers to the most recently + set breakpoint that still exists. You can use breakpoint commands to start your program up again. Simply use the "continue" command, or "step", or any other command @@ -4574,11 +4956,11 @@ def inner(x): Deletion of a name removes the binding of that name from the local or global namespace, depending on whether the name occurs in a "global" -statement in the same code block. If the name is unbound, a -"NameError" exception will be raised. +statement in the same code block. Trying to delete an unbound name +raises a "NameError" exception. -Deletion of attribute references, subscriptions and slicings is passed -to the primary object involved; deletion of a slicing is in general +Deletion of attribute references and subscriptions is passed to the +primary object involved; deletion of a slicing is in general equivalent to assignment of an empty slice of the right type (but even this is determined by the sliced object). @@ -4594,8 +4976,8 @@ def inner(x): dict_display: "{" [dict_item_list | dict_comprehension] "}" dict_item_list: dict_item ("," dict_item)* [","] + dict_comprehension: dict_item comp_for dict_item: expression ":" expression | "**" or_expr - dict_comprehension: expression ":" expression comp_for A dictionary display yields a new dictionary object. @@ -4614,11 +4996,22 @@ def inner(x): Added in version 3.5: Unpacking into dictionary displays, originally proposed by **PEP 448**. -A dict comprehension, in contrast to list and set comprehensions, -needs two expressions separated with a colon followed by the usual -“for” and “if” clauses. When the comprehension is run, the resulting -key and value elements are inserted in the new dictionary in the order -they are produced. +A dict comprehension may take one of two forms: + +* The first form uses two expressions separated with a colon followed + by the usual “for” and “if” clauses. When the comprehension is run, + the resulting key and value elements are inserted in the new + dictionary in the order they are produced. + +* The second form uses a single expression prefixed by the "**" + dictionary unpacking operator followed by the usual “for” and “if” + clauses. When the comprehension is evaluated, the expression is + evaluated and then unpacked, inserting zero or more key/value pairs + into the new dictionary. + +Both forms of dictionary comprehension retain the property that if the +same key is specified multiple times, the associated value in the +resulting dictionary will be the last one specified. Restrictions on the types of the key values are listed earlier in section The standard type hierarchy. (To summarize, the key type @@ -4630,6 +5023,9 @@ def inner(x): the evaluation order of key and value was not well-defined. In CPython, the value was evaluated before the key. Starting with 3.8, the key is evaluated before the value, as proposed by **PEP 572**. + +Changed in version 3.15: Unpacking with the "**" operator is now +allowed in dictionary comprehensions. ''', 'dynamic-features': r'''Interaction with dynamic features ********************************* @@ -4711,11 +5107,6 @@ class of the instance or a *non-virtual base class* thereof. The See also the description of the "try" statement in section The try statement and "raise" statement in section The raise statement. - --[ Footnotes ]- - -[1] This limitation occurs because the code that is executed by these - operations is not available at the time the module is compiled. ''', 'execmodel': r'''Execution model *************** @@ -5069,6 +5460,181 @@ class of the instance or a *non-virtual base class* thereof. The See also the description of the "try" statement in section The try statement and "raise" statement in section The raise statement. + +Runtime Components +================== + + +General Computing Model +----------------------- + +Python’s execution model does not operate in a vacuum. It runs on a +host machine and through that host’s runtime environment, including +its operating system (OS), if there is one. When a program runs, the +conceptual layers of how it runs on the host look something like this: + + **host machine** + **process** (global resources) + **thread** (runs machine code) + +Each process represents a program running on the host. Think of each +process itself as the data part of its program. Think of the process’ +threads as the execution part of the program. This distinction will +be important to understand the conceptual Python runtime. + +The process, as the data part, is the execution context in which the +program runs. It mostly consists of the set of resources assigned to +the program by the host, including memory, signals, file handles, +sockets, and environment variables. + +Processes are isolated and independent from one another. (The same is +true for hosts.) The host manages the process’ access to its assigned +resources, in addition to coordinating between processes. + +Each thread represents the actual execution of the program’s machine +code, running relative to the resources assigned to the program’s +process. It’s strictly up to the host how and when that execution +takes place. + +From the point of view of Python, a program always starts with exactly +one thread. However, the program may grow to run in multiple +simultaneous threads. Not all hosts support multiple threads per +process, but most do. Unlike processes, threads in a process are not +isolated and independent from one another. Specifically, all threads +in a process share all of the process’ resources. + +The fundamental point of threads is that each one does *run* +independently, at the same time as the others. That may be only +conceptually at the same time (“concurrently”) or physically (“in +parallel”). Either way, the threads effectively run at a non- +synchronized rate. + +Note: + + That non-synchronized rate means none of the process’ memory is + guaranteed to stay consistent for the code running in any given + thread. Thus multi-threaded programs must take care to coordinate + access to intentionally shared resources. Likewise, they must take + care to be absolutely diligent about not accessing any *other* + resources in multiple threads; otherwise two threads running at the + same time might accidentally interfere with each other’s use of some + shared data. All this is true for both Python programs and the + Python runtime.The cost of this broad, unstructured requirement is + the tradeoff for the kind of raw concurrency that threads provide. + The alternative to the required discipline generally means dealing + with non-deterministic bugs and data corruption. + + +Python Runtime Model +-------------------- + +The same conceptual layers apply to each Python program, with some +extra data layers specific to Python: + + **host machine** + **process** (global resources) + Python global runtime (*state*) + Python interpreter (*state*) + **thread** (runs Python bytecode and “C-API”) + Python thread *state* + +At the conceptual level: when a Python program starts, it looks +exactly like that diagram, with one of each. The runtime may grow to +include multiple interpreters, and each interpreter may grow to +include multiple thread states. + +Note: + + A Python implementation won’t necessarily implement the runtime + layers distinctly or even concretely. The only exception is places + where distinct layers are directly specified or exposed to users, + like through the "threading" module. + +Note: + + The initial interpreter is typically called the “main” interpreter. + Some Python implementations, like CPython, assign special roles to + the main interpreter.Likewise, the host thread where the runtime was + initialized is known as the “main” thread. It may be different from + the process’ initial thread, though they are often the same. In + some cases “main thread” may be even more specific and refer to the + initial thread state. A Python runtime might assign specific + responsibilities to the main thread, such as handling signals. + +As a whole, the Python runtime consists of the global runtime state, +interpreters, and thread states. The runtime ensures all that state +stays consistent over its lifetime, particularly when used with +multiple host threads. + +The global runtime, at the conceptual level, is just a set of +interpreters. While those interpreters are otherwise isolated and +independent from one another, they may share some data or other +resources. The runtime is responsible for managing these global +resources safely. The actual nature and management of these resources +is implementation-specific. Ultimately, the external utility of the +global runtime is limited to managing interpreters. + +In contrast, an “interpreter” is conceptually what we would normally +think of as the (full-featured) “Python runtime”. When machine code +executing in a host thread interacts with the Python runtime, it calls +into Python in the context of a specific interpreter. + +Note: + + The term “interpreter” here is not the same as the “bytecode + interpreter”, which is what regularly runs in threads, executing + compiled Python code.In an ideal world, “Python runtime” would refer + to what we currently call “interpreter”. However, it’s been called + “interpreter” at least since introduced in 1997 (CPython:a027efa5b). + +Each interpreter completely encapsulates all of the non-process- +global, non-thread-specific state needed for the Python runtime to +work. Notably, the interpreter’s state persists between uses. It +includes fundamental data like "sys.modules". The runtime ensures +multiple threads using the same interpreter will safely share it +between them. + +A Python implementation may support using multiple interpreters at the +same time in the same process. They are independent and isolated from +one another. For example, each interpreter has its own "sys.modules". + +For thread-specific runtime state, each interpreter has a set of +thread states, which it manages, in the same way the global runtime +contains a set of interpreters. It can have thread states for as many +host threads as it needs. It may even have multiple thread states for +the same host thread, though that isn’t as common. + +Each thread state, conceptually, has all the thread-specific runtime +data an interpreter needs to operate in one host thread. The thread +state includes the current raised exception and the thread’s Python +call stack. It may include other thread-specific resources. + +Note: + + The term “Python thread” can sometimes refer to a thread state, but + normally it means a thread created using the "threading" module. + +Each thread state, over its lifetime, is always tied to exactly one +interpreter and exactly one host thread. It will only ever be used in +that thread and with that interpreter. + +Multiple thread states may be tied to the same host thread, whether +for different interpreters or even the same interpreter. However, for +any given host thread, only one of the thread states tied to it can be +used by the thread at a time. + +Thread states are isolated and independent from one another and don’t +share any data, except for possibly sharing an interpreter and objects +or other resources belonging to that interpreter. + +Once a program is running, new Python threads can be created using the +"threading" module (on platforms and Python implementations that +support threads). Additional processes can be created using the "os", +"subprocess", and "multiprocessing" modules. Interpreters can be +created and used with the "interpreters" module. Coroutines (async) +can be run using "asyncio" in each interpreter, typically only in a +single thread (often the main thread). + -[ Footnotes ]- [1] This limitation occurs because the code that is executed by these @@ -5077,7 +5643,7 @@ class of the instance or a *non-virtual base class* thereof. The 'exprlists': r'''Expression lists **************** - starred_expression: ["*"] or_expr + starred_expression: "*" or_expr | expression flexible_expression: assignment_expression | starred_expression flexible_expression_list: flexible_expression ("," flexible_expression)* [","] starred_expression_list: starred_expression ("," starred_expression)* [","] @@ -5109,25 +5675,53 @@ class of the instance or a *non-virtual base class* thereof. The 'floating': r'''Floating-point literals *********************** -Floating-point literals are described by the following lexical -definitions: +Floating-point (float) literals, such as "3.14" or "1.5", denote +approximations of real numbers. + +They consist of *integer* and *fraction* parts, each composed of +decimal digits. The parts are separated by a decimal point, ".": + + 2.71828 + 4.0 + +Unlike in integer literals, leading zeros are allowed. For example, +"077.010" is legal, and denotes the same number as "77.01". + +As in integer literals, single underscores may occur between digits to +help readability: + + 96_485.332_123 + 3.14_15_93 + +Either of these parts, but not both, can be empty. For example: + + 10. # (equivalent to 10.0) + .001 # (equivalent to 0.001) + +Optionally, the integer and fraction may be followed by an *exponent*: +the letter "e" or "E", followed by an optional sign, "+" or "-", and a +number in the same format as the integer and fraction parts. The "e" +or "E" represents “times ten raised to the power of”: - floatnumber: pointfloat | exponentfloat - pointfloat: [digitpart] fraction | digitpart "." - exponentfloat: (digitpart | pointfloat) exponent - digitpart: digit (["_"] digit)* - fraction: "." digitpart - exponent: ("e" | "E") ["+" | "-"] digitpart + 1.0e3 # (represents 1.0×10³, or 1000.0) + 1.166e-5 # (represents 1.166×10⁻⁵, or 0.00001166) + 6.02214076e+23 # (represents 6.02214076×10²³, or 602214076000000000000000.) -Note that the integer and exponent parts are always interpreted using -radix 10. For example, "077e010" is legal, and denotes the same number -as "77e10". The allowed range of floating-point literals is -implementation-dependent. As in integer literals, underscores are -supported for digit grouping. +In floats with only integer and exponent parts, the decimal point may +be omitted: -Some examples of floating-point literals: + 1e3 # (equivalent to 1.e3 and 1.0e3) + 0e0 # (equivalent to 0.) - 3.14 10. .001 1e100 3.14e-10 0e0 3.14_15_93 +Formally, floating-point literals are described by the following +lexical definitions: + + floatnumber: + | digitpart "." [digitpart] [exponent] + | "." digitpart [exponent] + | digitpart exponent + digitpart: digit (["_"] digit)* + exponent: ("e" | "E") ["+" | "-"] digitpart Changed in version 3.6: Underscores are now allowed for grouping purposes in literals. @@ -5138,16 +5732,16 @@ class of the instance or a *non-virtual base class* thereof. The The "for" statement is used to iterate over the elements of a sequence (such as a string, tuple or list) or other iterable object: - for_stmt: "for" target_list "in" starred_list ":" suite + for_stmt: "for" target_list "in" starred_expression_list ":" suite ["else" ":" suite] -The "starred_list" expression is evaluated once; it should yield an -*iterable* object. An *iterator* is created for that iterable. The -first item provided by the iterator is then assigned to the target -list using the standard rules for assignments (see Assignment -statements), and the suite is executed. This repeats for each item -provided by the iterator. When the iterator is exhausted, the suite -in the "else" clause, if present, is executed, and the loop +The "starred_expression_list" expression is evaluated once; it should +yield an *iterable* object. An *iterator* is created for that +iterable. The first item provided by the iterator is then assigned to +the target list using the standard rules for assignments (see +Assignment statements), and the suite is executed. This repeats for +each item provided by the iterator. When the iterator is exhausted, +the suite in the "else" clause, if present, is executed, and the loop terminates. A "break" statement executed in the first suite terminates the loop @@ -5175,15 +5769,15 @@ class of the instance or a *non-virtual base class* thereof. The Changed in version 3.11: Starred elements are now allowed in the expression list. ''', - 'formatstrings': r'''Format String Syntax + 'formatstrings': r'''Format string syntax ******************** The "str.format()" method and the "Formatter" class share the same syntax for format strings (although in the case of "Formatter", subclasses can define their own format string syntax). The syntax is -related to that of formatted string literals, but it is less -sophisticated and, in particular, does not support arbitrary -expressions. +related to that of formatted string literals and template string +literals, but it is less sophisticated and, in particular, does not +support arbitrary expressions in interpolations. Format strings contain “replacement fields” surrounded by curly braces "{}". Anything that is not contained in braces is considered literal @@ -5210,7 +5804,7 @@ class of the instance or a *non-virtual base class* thereof. The preceded by a colon "':'". These specify a non-default format for the replacement value. -See also the Format Specification Mini-Language section. +See also the Format specification mini-language section. The *field_name* itself begins with an *arg_name* that is either a number or a keyword. If it’s a number, it refers to a positional @@ -5278,14 +5872,14 @@ class of the instance or a *non-virtual base class* thereof. The See the Format examples section for some examples. -Format Specification Mini-Language +Format specification mini-language ================================== “Format specifications” are used within replacement fields contained within a format string to define how individual values are presented -(see Format String Syntax and f-strings). They can also be passed -directly to the built-in "format()" function. Each formattable type -may define how the format specification is to be interpreted. +(see Format string syntax, f-strings, and t-strings). They can also be +passed directly to the built-in "format()" function. Each formattable +type may define how the format specification is to be interpreted. Most built-in types implement the following options for format specifications, although some of the formatting options are only @@ -5304,7 +5898,7 @@ class of the instance or a *non-virtual base class* thereof. The sign: "+" | "-" | " " width_and_precision: [width_with_grouping][precision_with_grouping] width_with_grouping: [width][grouping] - precision_with_grouping: "." [precision][grouping] + precision_with_grouping: "." [precision][grouping] | "." grouping width: digit+ precision: digit+ grouping: "," | "_" @@ -5490,7 +6084,9 @@ class of the instance or a *non-virtual base class* thereof. The | | With no precision given, uses a precision of "6" digits | | | after the decimal point for "float", and shows all | | | coefficient digits for "Decimal". If "p=0", the decimal | - | | point is omitted unless the "#" option is used. | + | | point is omitted unless the "#" option is used. For | + | | "float", the exponent always contains at least two digits, | + | | and is zero if the value is zero. | +-----------+------------------------------------------------------------+ | "'E'" | Scientific notation. Same as "'e'" except it uses an upper | | | case ‘E’ as the separator character. | @@ -5688,8 +6284,8 @@ class of the instance or a *non-virtual base class* thereof. The Using type-specific formatting: - >>> import datetime - >>> d = datetime.datetime(2010, 7, 4, 12, 15, 58) + >>> import datetime as dt + >>> d = dt.datetime(2010, 7, 4, 12, 15, 58) >>> '{:%Y-%m-%d %H:%M:%S}'.format(d) '2010-07-04 12:15:58' @@ -5886,9 +6482,15 @@ def whats_on_the_telly(penguin=None): without "global", although free variables may refer to globals without being declared global. -The "global" statement applies to the entire scope of a function or -class body. A "SyntaxError" is raised if a variable is used or -assigned to prior to its global declaration in the scope. +The "global" statement applies to the entire current scope (module, +function body or class definition). A "SyntaxError" is raised if a +variable is used or assigned to prior to its global declaration in the +scope. + +At the module level, all variables are global, so a "global" statement +has no effect. However, variables must still not be used or assigned +to prior to their "global" declaration. This requirement is relaxed in +the interactive prompt (*REPL*). **Programmer’s note:** "global" is a directive to the parser. It applies only to code parsed at the same time as the "global" @@ -5942,73 +6544,45 @@ class body. A "SyntaxError" is raised if a variable is used or to help avoid name clashes between “private” attributes of base and derived classes. See section Identifiers (Names). ''', - 'identifiers': r'''Identifiers and keywords -************************ - -Identifiers (also referred to as *names*) are described by the -following lexical definitions. - -The syntax of identifiers in Python is based on the Unicode standard -annex UAX-31, with elaboration and changes as defined below; see also -**PEP 3131** for further details. - -Within the ASCII range (U+0001..U+007F), the valid characters for -identifiers include the uppercase and lowercase letters "A" through -"Z", the underscore "_" and, except for the first character, the -digits "0" through "9". Python 3.0 introduced additional characters -from outside the ASCII range (see **PEP 3131**). For these -characters, the classification uses the version of the Unicode -Character Database as included in the "unicodedata" module. + 'identifiers': r'''Names (identifiers and keywords) +******************************** -Identifiers are unlimited in length. Case is significant. +"NAME" tokens represent *identifiers*, *keywords*, and *soft +keywords*. - identifier: xid_start xid_continue* - id_start: - id_continue: - xid_start: - xid_continue: +Names are composed of the following characters: -The Unicode category codes mentioned above stand for: +* uppercase and lowercase letters ("A-Z" and "a-z"), -* *Lu* - uppercase letters +* the underscore ("_"), -* *Ll* - lowercase letters +* digits ("0" through "9"), which cannot appear as the first + character, and -* *Lt* - titlecase letters +* non-ASCII characters. Valid names may only contain “letter-like” and + “digit-like” characters; see Non-ASCII characters in names for + details. -* *Lm* - modifier letters +Names must contain at least one character, but have no upper length +limit. Case is significant. -* *Lo* - other letters +Formally, names are described by the following lexical definitions: -* *Nl* - letter numbers + NAME: name_start name_continue* + name_start: "a"..."z" | "A"..."Z" | "_" | + name_continue: name_start | "0"..."9" + identifier: -* *Mn* - nonspacing marks - -* *Mc* - spacing combining marks - -* *Nd* - decimal numbers - -* *Pc* - connector punctuations - -* *Other_ID_Start* - explicit list of characters in PropList.txt to - support backwards compatibility - -* *Other_ID_Continue* - likewise - -All identifiers are converted into the normal form NFKC while parsing; -comparison of identifiers is based on NFKC. - -A non-normative HTML file listing all valid identifier characters for -Unicode 16.0.0 can be found at -https://www.unicode.org/Public/16.0.0/ucd/DerivedCoreProperties.txt +Note that not all names matched by this grammar are valid; see Non- +ASCII characters in names for details. Keywords ======== -The following identifiers are used as reserved words, or *keywords* of -the language, and cannot be used as ordinary identifiers. They must -be spelled exactly as written here: +The following names are used as reserved words, or *keywords* of the +language, and cannot be used as ordinary identifiers. They must be +spelled exactly as written here: False await else import pass None break except in raise @@ -6024,20 +6598,26 @@ class body. A "SyntaxError" is raised if a variable is used or Added in version 3.10. -Some identifiers are only reserved under specific contexts. These are -known as *soft keywords*. The identifiers "match", "case", "type" and -"_" can syntactically act as keywords in certain contexts, but this -distinction is done at the parser level, not when tokenizing. +Some names are only reserved under specific contexts. These are known +as *soft keywords*: + +* "match", "case", and "_", when used in the "match" statement. + +* "type", when used in the "type" statement. + +* "lazy", when used before an "import" statement. + +These syntactically act as keywords in their specific contexts, but +this distinction is done at the parser level, not when tokenizing. As soft keywords, their use in the grammar is possible while still preserving compatibility with existing code that uses these names as identifier names. -"match", "case", and "_" are used in the "match" statement. "type" is -used in the "type" statement. - Changed in version 3.12: "type" is now a soft keyword. +Changed in version 3.15: "lazy" is now a soft keyword. + Reserved classes of identifiers =============================== @@ -6081,6 +6661,101 @@ class body. A "SyntaxError" is raised if a variable is used or context of a class definition, are re-written to use a mangled form to help avoid name clashes between “private” attributes of base and derived classes. See section Identifiers (Names). + + +Non-ASCII characters in names +============================= + +Names that contain non-ASCII characters need additional normalization +and validation beyond the rules and grammar explained above. For +example, "ř_1", "蛇", or "साँप" are valid names, but "r〰2", "€", or +"🐍" are not. + +This section explains the exact rules. + +All names are converted into the normalization form NFKC while +parsing. This means that, for example, some typographic variants of +characters are converted to their “basic” form. For example, +"fiⁿₐˡᵢᶻₐᵗᵢᵒₙ" normalizes to "finalization", so Python treats them as +the same name: + + >>> fiⁿₐˡᵢᶻₐᵗᵢᵒₙ = 3 + >>> finalization + 3 + +Note: + + Normalization is done at the lexical level only. Run-time functions + that take names as *strings* generally do not normalize their + arguments. For example, the variable defined above is accessible at + run time in the "globals()" dictionary as + "globals()["finalization"]" but not "globals()["fiⁿₐˡᵢᶻₐᵗᵢᵒₙ"]". + +Similarly to how ASCII-only names must contain only letters, digits +and the underscore, and cannot start with a digit, a valid name must +start with a character in the “letter-like” set "xid_start", and the +remaining characters must be in the “letter- and digit-like” set +"xid_continue". + +These sets are based on the *XID_Start* and *XID_Continue* sets as +defined by the Unicode standard annex UAX-31. Python’s "xid_start" +additionally includes the underscore ("_"). Note that Python does not +necessarily conform to UAX-31. + +A non-normative listing of characters in the *XID_Start* and +*XID_Continue* sets as defined by Unicode is available in the +DerivedCoreProperties.txt file in the Unicode Character Database. For +reference, the construction rules for the "xid_*" sets are given +below. + +The set "id_start" is defined as the union of: + +* Unicode category "" - uppercase letters (includes "A" to "Z") + +* Unicode category "" - lowercase letters (includes "a" to "z") + +* Unicode category "" - titlecase letters + +* Unicode category "" - modifier letters + +* Unicode category "" - other letters + +* Unicode category "" - letter numbers + +* {""_""} - the underscore + +* "" - an explicit set of characters in PropList.txt + to support backwards compatibility + +The set "xid_start" then closes this set under NFKC normalization, by +removing all characters whose normalization is not of the form +"id_start id_continue*". + +The set "id_continue" is defined as the union of: + +* "id_start" (see above) + +* Unicode category "" - decimal numbers (includes "0" to "9") + +* Unicode category "" - connector punctuations + +* Unicode category "" - nonspacing marks + +* Unicode category "" - spacing combining marks + +* "" - another explicit set of characters in + PropList.txt to support backwards compatibility + +Again, "xid_continue" closes this set under NFKC normalization. + +Unicode categories use the version of the Unicode Character Database +as included in the "unicodedata" module. + +See also: + + * **PEP 3131** – Supporting Non-ASCII Identifiers + + * **PEP 672** – Unicode-related Security Considerations for Python ''', 'if': r'''The "if" statement ****************** @@ -6101,25 +6776,61 @@ class body. A "SyntaxError" is raised if a variable is used or 'imaginary': r'''Imaginary literals ****************** -Imaginary literals are described by the following lexical definitions: +Python has complex number objects, but no complex literals. Instead, +*imaginary literals* denote complex numbers with a zero real part. - imagnumber: (floatnumber | digitpart) ("j" | "J") +For example, in math, the complex number 3+4.2*i* is written as the +real number 3 added to the imaginary number 4.2*i*. Python uses a +similar syntax, except the imaginary unit is written as "j" rather +than *i*: + + 3+4.2j + +This is an expression composed of the integer literal "3", the +operator ‘"+"’, and the imaginary literal "4.2j". Since these are +three separate tokens, whitespace is allowed between them: + + 3 + 4.2j + +No whitespace is allowed *within* each token. In particular, the "j" +suffix, may not be separated from the number before it. + +The number before the "j" has the same syntax as a floating-point +literal. Thus, the following are valid imaginary literals: -An imaginary literal yields a complex number with a real part of 0.0. -Complex numbers are represented as a pair of floating-point numbers -and have the same restrictions on their range. To create a complex -number with a nonzero real part, add a floating-point number to it, -e.g., "(3+4j)". Some examples of imaginary literals: + 4.2j + 3.14j + 10.j + .001j + 1e100j + 3.14e-10j + 3.14_15_93j - 3.14j 10.j 10j .001j 1e100j 3.14e-10j 3.14_15_93j +Unlike in a floating-point literal the decimal point can be omitted if +the imaginary number only has an integer part. The number is still +evaluated as a floating-point number, not an integer: + + 10j + 0j + 1000000000000000000000000j # equivalent to 1e+24j + +The "j" suffix is case-insensitive. That means you can use "J" +instead: + + 3.14J # equivalent to 3.14j + +Formally, imaginary literals are described by the following lexical +definition: + + imagnumber: (floatnumber | digitpart) ("j" | "J") ''', 'import': r'''The "import" statement ********************** - import_stmt: "import" module ["as" identifier] ("," module ["as" identifier])* - | "from" relative_module "import" identifier ["as" identifier] + import_stmt: ["lazy"] "import" module ["as" identifier] ("," module ["as" identifier])* + | ["lazy"] "from" relative_module "import" identifier ["as" identifier] ("," identifier ["as" identifier])* - | "from" relative_module "import" "(" identifier ["as" identifier] + | ["lazy"] "from" relative_module "import" "(" identifier ["as" identifier] ("," identifier ["as" identifier])* [","] ")" | "from" relative_module "import" "*" module: (identifier ".")* identifier @@ -6130,8 +6841,9 @@ class body. A "SyntaxError" is raised if a variable is used or 1. find a module, loading and initializing it if necessary -2. define a name or names in the local namespace for the scope where - the "import" statement occurs. +2. define a name or names in the current namespace for the scope where + the "import" statement occurs, just as an assignment statement + would (including "global" and "nonlocal" semantics). When the statement contains multiple clauses (separated by commas) the two steps are carried out separately for each clause, just as though @@ -6176,7 +6888,7 @@ class body. A "SyntaxError" is raised if a variable is used or 3. if the attribute is not found, "ImportError" is raised. - 4. otherwise, a reference to that value is stored in the local + 4. otherwise, a reference to that value is stored in the current namespace, using the name in the "as" clause if it is present, otherwise using the attribute name @@ -6195,7 +6907,9 @@ class body. A "SyntaxError" is raised if a variable is used or The *public names* defined by a module are determined by checking the module’s namespace for a variable named "__all__"; if defined, it must be a sequence of strings which are names defined or imported by that -module. The names given in "__all__" are all considered public and +module. Names containing non-ASCII characters must be in the +normalization form NFKC; see Non-ASCII characters in names for +details. The names given in "__all__" are all considered public and are required to exist. If "__all__" is not defined, the set of public names includes all names found in the module’s namespace which do not begin with an underscore character ("'_'"). "__all__" should contain @@ -6229,18 +6943,107 @@ class body. A "SyntaxError" is raised if a variable is used or "sys.path", "sys.meta_path", "sys.path_hooks". -Future statements -================= +Lazy imports +============ -A *future statement* is a directive to the compiler that a particular -module should be compiled using syntax or semantics that will be -available in a specified future release of Python where the feature -becomes standard. +The "lazy" keyword is a soft keyword that only has special meaning +when it appears immediately before an "import" or "from" statement. +When an import statement is preceded by the "lazy" keyword, the import +becomes *lazy*: the module is not loaded immediately at the import +statement. Instead, a lazy proxy object is created and bound to the +name. The actual module is loaded on first use of that name. -The future statement is intended to ease migration to future versions -of Python that introduce incompatible changes to the language. It -allows use of the new features on a per-module basis before the -release in which the feature becomes standard. +Lazy imports are only permitted at module scope. Using "lazy" inside a +function, class body, or "try"/"except"/"finally" block raises a +"SyntaxError". Star imports cannot be lazy ("lazy from module import +*" is a syntax error), and future statements cannot be lazy. + +When using "lazy from ... import", each imported name is bound to a +lazy proxy object. The first access to any of these names triggers +loading of the entire module and resolves only that specific name to +its actual value. Other names remain as lazy proxies until they are +accessed. + +Example: + + lazy import json + import sys + + print('json' in sys.modules) # False - json module not yet loaded + + # First use triggers loading + result = json.dumps({"hello": "world"}) + + print('json' in sys.modules) # True - now loaded + +If an error occurs during module loading (such as "ImportError" or +"SyntaxError"), it is raised at the point where the lazy import is +first used, not at the import statement itself. + +See **PEP 810** for the full specification of lazy imports. + +Added in version 3.15. + + +Compatibility via "__lazy_modules__" +------------------------------------ + +As an alternative to using the "lazy" keyword, a module can opt into +lazy loading for specific imports by defining a module-level +"__lazy_modules__" variable. When present, it must be a container of +fully qualified module name strings. Any regular (non-"lazy") +"import" statement at module scope whose target appears in +"__lazy_modules__" is treated as a lazy import, exactly as if the +"lazy" keyword had been used. + +This provides a way to enable lazy loading for specific dependencies +without changing individual "import" statements. This is useful when +supporting Python versions older than 3.15 while using lazy imports in +3.15+: + + __lazy_modules__ = ["json", "pathlib"] + + import json # loaded lazily (name is in __lazy_modules__) + import os # loaded eagerly (name not in __lazy_modules__) + + import pathlib # loaded lazily + +Relative imports are resolved to their absolute name before the +lookup, so "__lazy_modules__" must always contain fully qualified +module names. + +For "from"-style imports, the relevant name is the module following +"from", not the names of its members: + + # In mypackage/mymodule.py + __lazy_modules__ = ["mypackage", "mypackage.sub.utils"] + + from . import helper # loaded lazily: . resolves to mypackage + from .sub.utils import func # loaded lazily: .sub.utils resolves to mypackage.sub.utils + import json # loaded eagerly (not in __lazy_modules__) + +Imports inside functions, class bodies, or "try"/"except"/"finally" +blocks are always eager, regardless of "__lazy_modules__". + +Setting "-X lazy_imports=none" (or the "PYTHON_LAZY_IMPORTS" +environment variable to "none") overrides "__lazy_modules__" and +forces all imports to be eager. + +Added in version 3.15. + + +Future statements +================= + +A *future statement* is a directive to the compiler that a particular +module should be compiled using syntax or semantics that will be +available in a specified future release of Python where the feature +becomes standard. + +The future statement is intended to ease migration to future versions +of Python that introduce incompatible changes to the language. It +allows use of the new features on a per-module basis before the +release in which the feature becomes standard. future_stmt: "from" "__future__" "import" feature ["as" identifier] ("," feature ["as" identifier])* @@ -6353,37 +7156,62 @@ class body. A "SyntaxError" is raised if a variable is used or 'integers': r'''Integer literals **************** -Integer literals are described by the following lexical definitions: +Integer literals denote whole numbers. For example: + + 7 + 3 + 2147483647 + +There is no limit for the length of integer literals apart from what +can be stored in available memory: + + 7922816251426433759354395033679228162514264337593543950336 + +Underscores can be used to group digits for enhanced readability, and +are ignored for determining the numeric value of the literal. For +example, the following literals are equivalent: + + 100_000_000_000 + 100000000000 + 1_00_00_00_00_000 + +Underscores can only occur between digits. For example, "_123", +"321_", and "123__321" are *not* valid literals. - integer: decinteger | bininteger | octinteger | hexinteger - decinteger: nonzerodigit (["_"] digit)* | "0"+ (["_"] "0")* +Integers can be specified in binary (base 2), octal (base 8), or +hexadecimal (base 16) using the prefixes "0b", "0o" and "0x", +respectively. Hexadecimal digits 10 through 15 are represented by +letters "A"-"F", case-insensitive. For example: + + 0b100110111 + 0b_1110_0101 + 0o177 + 0o377 + 0xdeadbeef + 0xDead_Beef + +An underscore can follow the base specifier. For example, "0x_1f" is a +valid literal, but "0_x1f" and "0x__1f" are not. + +Leading zeros in a non-zero decimal number are not allowed. For +example, "0123" is not a valid literal. This is for disambiguation +with C-style octal literals, which Python used before version 3.0. + +Formally, integer literals are described by the following lexical +definitions: + + integer: decinteger | bininteger | octinteger | hexinteger | zerointeger + decinteger: nonzerodigit (["_"] digit)* bininteger: "0" ("b" | "B") (["_"] bindigit)+ octinteger: "0" ("o" | "O") (["_"] octdigit)+ hexinteger: "0" ("x" | "X") (["_"] hexdigit)+ + zerointeger: "0"+ (["_"] "0")* nonzerodigit: "1"..."9" digit: "0"..."9" bindigit: "0" | "1" octdigit: "0"..."7" hexdigit: digit | "a"..."f" | "A"..."F" -There is no limit for the length of integer literals apart from what -can be stored in available memory. - -Underscores are ignored for determining the numeric value of the -literal. They can be used to group digits for enhanced readability. -One underscore can occur between digits, and after base specifiers -like "0x". - -Note that leading zeros in a non-zero decimal number are not allowed. -This is for disambiguation with C-style octal literals, which Python -used before version 3.0. - -Some examples of integer literals: - - 7 2147483647 0o177 0b100110111 - 3 79228162514264337593543950336 0o377 0xdeadbeef - 100_000_000_000 0b_1110_0101 - Changed in version 3.6: Underscores are now allowed for grouping purposes in literals. ''', @@ -6730,14 +7558,189 @@ class body. A "SyntaxError" is raised if a variable is used or 'numbers': r'''Numeric literals **************** -There are three types of numeric literals: integers, floating-point -numbers, and imaginary numbers. There are no complex literals -(complex numbers can be formed by adding a real number and an -imaginary number). +"NUMBER" tokens represent numeric literals, of which there are three +types: integers, floating-point numbers, and imaginary numbers. + + NUMBER: integer | floatnumber | imagnumber + +The numeric value of a numeric literal is the same as if it were +passed as a string to the "int", "float" or "complex" class +constructor, respectively. Note that not all valid inputs for those +constructors are also valid literals. + +Numeric literals do not include a sign; a phrase like "-1" is actually +an expression composed of the unary operator ‘"-"’ and the literal +"1". + + +Integer literals +================ + +Integer literals denote whole numbers. For example: + + 7 + 3 + 2147483647 + +There is no limit for the length of integer literals apart from what +can be stored in available memory: + + 7922816251426433759354395033679228162514264337593543950336 + +Underscores can be used to group digits for enhanced readability, and +are ignored for determining the numeric value of the literal. For +example, the following literals are equivalent: + + 100_000_000_000 + 100000000000 + 1_00_00_00_00_000 + +Underscores can only occur between digits. For example, "_123", +"321_", and "123__321" are *not* valid literals. + +Integers can be specified in binary (base 2), octal (base 8), or +hexadecimal (base 16) using the prefixes "0b", "0o" and "0x", +respectively. Hexadecimal digits 10 through 15 are represented by +letters "A"-"F", case-insensitive. For example: + + 0b100110111 + 0b_1110_0101 + 0o177 + 0o377 + 0xdeadbeef + 0xDead_Beef + +An underscore can follow the base specifier. For example, "0x_1f" is a +valid literal, but "0_x1f" and "0x__1f" are not. + +Leading zeros in a non-zero decimal number are not allowed. For +example, "0123" is not a valid literal. This is for disambiguation +with C-style octal literals, which Python used before version 3.0. + +Formally, integer literals are described by the following lexical +definitions: + + integer: decinteger | bininteger | octinteger | hexinteger | zerointeger + decinteger: nonzerodigit (["_"] digit)* + bininteger: "0" ("b" | "B") (["_"] bindigit)+ + octinteger: "0" ("o" | "O") (["_"] octdigit)+ + hexinteger: "0" ("x" | "X") (["_"] hexdigit)+ + zerointeger: "0"+ (["_"] "0")* + nonzerodigit: "1"..."9" + digit: "0"..."9" + bindigit: "0" | "1" + octdigit: "0"..."7" + hexdigit: digit | "a"..."f" | "A"..."F" + +Changed in version 3.6: Underscores are now allowed for grouping +purposes in literals. + + +Floating-point literals +======================= + +Floating-point (float) literals, such as "3.14" or "1.5", denote +approximations of real numbers. + +They consist of *integer* and *fraction* parts, each composed of +decimal digits. The parts are separated by a decimal point, ".": + + 2.71828 + 4.0 + +Unlike in integer literals, leading zeros are allowed. For example, +"077.010" is legal, and denotes the same number as "77.01". + +As in integer literals, single underscores may occur between digits to +help readability: + + 96_485.332_123 + 3.14_15_93 + +Either of these parts, but not both, can be empty. For example: + + 10. # (equivalent to 10.0) + .001 # (equivalent to 0.001) + +Optionally, the integer and fraction may be followed by an *exponent*: +the letter "e" or "E", followed by an optional sign, "+" or "-", and a +number in the same format as the integer and fraction parts. The "e" +or "E" represents “times ten raised to the power of”: + + 1.0e3 # (represents 1.0×10³, or 1000.0) + 1.166e-5 # (represents 1.166×10⁻⁵, or 0.00001166) + 6.02214076e+23 # (represents 6.02214076×10²³, or 602214076000000000000000.) + +In floats with only integer and exponent parts, the decimal point may +be omitted: + + 1e3 # (equivalent to 1.e3 and 1.0e3) + 0e0 # (equivalent to 0.) + +Formally, floating-point literals are described by the following +lexical definitions: + + floatnumber: + | digitpart "." [digitpart] [exponent] + | "." digitpart [exponent] + | digitpart exponent + digitpart: digit (["_"] digit)* + exponent: ("e" | "E") ["+" | "-"] digitpart + +Changed in version 3.6: Underscores are now allowed for grouping +purposes in literals. + + +Imaginary literals +================== + +Python has complex number objects, but no complex literals. Instead, +*imaginary literals* denote complex numbers with a zero real part. + +For example, in math, the complex number 3+4.2*i* is written as the +real number 3 added to the imaginary number 4.2*i*. Python uses a +similar syntax, except the imaginary unit is written as "j" rather +than *i*: + + 3+4.2j + +This is an expression composed of the integer literal "3", the +operator ‘"+"’, and the imaginary literal "4.2j". Since these are +three separate tokens, whitespace is allowed between them: + + 3 + 4.2j + +No whitespace is allowed *within* each token. In particular, the "j" +suffix, may not be separated from the number before it. + +The number before the "j" has the same syntax as a floating-point +literal. Thus, the following are valid imaginary literals: + + 4.2j + 3.14j + 10.j + .001j + 1e100j + 3.14e-10j + 3.14_15_93j + +Unlike in a floating-point literal the decimal point can be omitted if +the imaginary number only has an integer part. The number is still +evaluated as a floating-point number, not an integer: + + 10j + 0j + 1000000000000000000000000j # equivalent to 1e+24j + +The "j" suffix is case-insensitive. That means you can use "J" +instead: + + 3.14J # equivalent to 3.14j -Note that numeric literals do not include a sign; a phrase like "-1" -is actually an expression composed of the unary operator ‘"-"’ and the -literal "1". +Formally, imaginary literals are described by the following lexical +definition: + + imagnumber: (floatnumber | digitpart) ("j" | "J") ''', 'numeric-types': r'''Emulating numeric types *********************** @@ -6807,9 +7810,9 @@ class that has an "__rsub__()" method, "type(y).__rsub__(y, x)" is third argument if the three-argument version of the built-in "pow()" function is to be supported. - Changed in version 3.14.0a7 (unreleased): Three-argument "pow()" - now try calling "__rpow__()" if necessary. Previously it was only - called in two-argument "pow()" and the binary power operator. + Changed in version 3.14: Three-argument "pow()" now try calling + "__rpow__()" if necessary. Previously it was only called in two- + argument "pow()" and the binary power operator. Note: @@ -6894,9 +7897,8 @@ class that has an "__rsub__()" method, "type(y).__rsub__(y, x)" is ************************* *Objects* are Python’s abstraction for data. All data in a Python -program is represented by objects or by relations between objects. (In -a sense, and in conformance to Von Neumann’s model of a “stored -program computer”, code is also represented by objects.) +program is represented by objects or by relations between objects. +Even code is represented by objects. Every object has an identity, a type and a value. An object’s *identity* never changes once it has been created; you may think of it @@ -7002,8 +8004,8 @@ class that has an "__rsub__()" method, "type(y).__rsub__(y, x)" is | value...}", "{expressions...}" | list display, dictionary display, set | | | display | +-------------------------------------------------+---------------------------------------+ -| "x[index]", "x[index:index]", | Subscription, slicing, call, | -| "x(arguments...)", "x.attribute" | attribute reference | +| "x[index]", "x[index:index]" "x(arguments...)", | Subscription (including slicing), | +| "x.attribute" | call, attribute reference | +-------------------------------------------------+---------------------------------------+ | "await x" | Await expression | +-------------------------------------------------+---------------------------------------+ @@ -7120,8 +8122,8 @@ class C: pass # a class with no methods (yet) The power operator has the same semantics as the built-in "pow()" function, when called with two arguments: it yields its left argument -raised to the power of its right argument. The numeric arguments are -first converted to a common type, and the result is of that type. +raised to the power of its right argument. Numeric arguments are first +converted to a common type, and the result is of that type. For int operands, the result has the same type as the operands unless the second argument is negative; in that case, all arguments are @@ -7283,21 +8285,24 @@ class C: pass # a class with no methods (yet) The "collections.abc" module provides a "MutableMapping" *abstract base class* to help create those methods from a base set of "__getitem__()", "__setitem__()", "__delitem__()", and "keys()". -Mutable sequences should provide methods "append()", "count()", -"index()", "extend()", "insert()", "pop()", "remove()", "reverse()" -and "sort()", like Python standard "list" objects. Finally, sequence + +Mutable sequences should provide methods "append()", "clear()", +"count()", "extend()", "index()", "insert()", "pop()", "remove()", and +"reverse()", like Python standard "list" objects. Finally, sequence types should implement addition (meaning concatenation) and multiplication (meaning repetition) by defining the methods "__add__()", "__radd__()", "__iadd__()", "__mul__()", "__rmul__()" and "__imul__()" described below; they should not define other numerical -operators. It is recommended that both mappings and sequences -implement the "__contains__()" method to allow efficient use of the -"in" operator; for mappings, "in" should search the mapping’s keys; -for sequences, it should search through the values. It is further -recommended that both mappings and sequences implement the -"__iter__()" method to allow efficient iteration through the -container; for mappings, "__iter__()" should iterate through the -object’s keys; for sequences, it should iterate through the values. +operators. + +It is recommended that both mappings and sequences implement the +"__contains__()" method to allow efficient use of the "in" operator; +for mappings, "in" should search the mapping’s keys; for sequences, it +should search through the values. It is further recommended that both +mappings and sequences implement the "__iter__()" method to allow +efficient iteration through the container; for mappings, "__iter__()" +should iterate through the object’s keys; for sequences, it should +iterate through the values. object.__len__(self) @@ -7324,35 +8329,46 @@ class C: pass # a class with no methods (yet) Added in version 3.4. -Note: +object.__getitem__(self, subscript) + + Called to implement *subscription*, that is, "self[subscript]". See + Subscriptions and slicings for details on the syntax. + + There are two types of built-in objects that support subscription + via "__getitem__()": + + * **sequences**, where *subscript* (also called *index*) should be + an integer or a "slice" object. See the sequence documentation + for the expected behavior, including handling "slice" objects and + negative indices. - Slicing is done exclusively with the following three methods. A - call like + * **mappings**, where *subscript* is also called the *key*. See + mapping documentation for the expected behavior. - a[1:2] = b + If *subscript* is of an inappropriate type, "__getitem__()" should + raise "TypeError". If *subscript* has an inappropriate value, + "__getitem__()" should raise an "LookupError" or one of its + subclasses ("IndexError" for sequences; "KeyError" for mappings). - is translated to + Note: + + Slicing is handled by "__getitem__()", "__setitem__()", and + "__delitem__()". A call like - a[slice(1, 2, None)] = b + a[1:2] = b - and so forth. Missing slice items are always filled in with "None". + is translated to -object.__getitem__(self, key) + a[slice(1, 2, None)] = b - Called to implement evaluation of "self[key]". For *sequence* - types, the accepted keys should be integers. Optionally, they may - support "slice" objects as well. Negative index support is also - optional. If *key* is of an inappropriate type, "TypeError" may be - raised; if *key* is a value outside the set of indexes for the - sequence (after any special interpretation of negative values), - "IndexError" should be raised. For *mapping* types, if *key* is - missing (not in the container), "KeyError" should be raised. + and so forth. Missing slice items are always filled in with + "None". Note: - "for" loops expect that an "IndexError" will be raised for - illegal indexes to allow proper detection of the end of the - sequence. + The sequence iteration protocol (used, for example, in "for" + loops), expects that an "IndexError" will be raised for illegal + indexes to allow proper detection of the end of a sequence. Note: @@ -7442,37 +8458,40 @@ class C: pass # a class with no methods (yet) 'slicings': r'''Slicings ******** -A slicing selects a range of items in a sequence object (e.g., a -string, tuple or list). Slicings may be used as expressions or as -targets in assignment or "del" statements. The syntax for a slicing: - - slicing: primary "[" slice_list "]" - slice_list: slice_item ("," slice_item)* [","] - slice_item: expression | proper_slice - proper_slice: [lower_bound] ":" [upper_bound] [ ":" [stride] ] - lower_bound: expression - upper_bound: expression - stride: expression - -There is ambiguity in the formal syntax here: anything that looks like -an expression list also looks like a slice list, so any subscription -can be interpreted as a slicing. Rather than further complicating the -syntax, this is disambiguated by defining that in this case the -interpretation as a subscription takes priority over the -interpretation as a slicing (this is the case if the slice list -contains no proper slice). - -The semantics for a slicing are as follows. The primary is indexed -(using the same "__getitem__()" method as normal subscription) with a -key that is constructed from the slice list, as follows. If the slice -list contains at least one comma, the key is a tuple containing the -conversion of the slice items; otherwise, the conversion of the lone -slice item is the key. The conversion of a slice item that is an -expression is that expression. The conversion of a proper slice is a -slice object (see section The standard type hierarchy) whose "start", -"stop" and "step" attributes are the values of the expressions given -as lower bound, upper bound and stride, respectively, substituting -"None" for missing expressions. +A more advanced form of subscription, *slicing*, is commonly used to +extract a portion of a sequence. In this form, the subscript is a +*slice*: up to three expressions separated by colons. Any of the +expressions may be omitted, but a slice must contain at least one +colon: + + >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five'] + >>> number_names[1:3] + ['one', 'two'] + >>> number_names[1:] + ['one', 'two', 'three', 'four', 'five'] + >>> number_names[:3] + ['zero', 'one', 'two'] + >>> number_names[:] + ['zero', 'one', 'two', 'three', 'four', 'five'] + >>> number_names[::2] + ['zero', 'two', 'four'] + >>> number_names[:-3] + ['zero', 'one', 'two'] + >>> del number_names[4:] + >>> number_names + ['zero', 'one', 'two', 'three'] + +When a slice is evaluated, the interpreter constructs a "slice" object +whose "start", "stop" and "step" attributes, respectively, are the +results of the expressions between the colons. Any missing expression +evaluates to "None". This "slice" object is then passed to the +"__getitem__()" or "__class_getitem__()" *special method*, as above. + + # continuing with the SubscriptionDemo instance defined above: + >>> demo[2:3] + subscripted with: slice(2, 3, None) + >>> demo[::'spam'] + subscripted with: slice(None, None, 'spam') ''', 'specialattrs': r'''Special Attributes ****************** @@ -7534,8 +8553,8 @@ class C: pass # a class with no methods (yet) important that the emulation only be implemented to the degree that it makes sense for the object being modelled. For example, some sequences may work well with retrieval of individual elements, but -extracting a slice may not make sense. (One example of this is the -"NodeList" interface in the W3C’s Document Object Model.) +extracting a slice may not make sense. (One example of this is the +NodeList interface in the W3C’s Document Object Model.) Basic customization @@ -7693,7 +8712,7 @@ class C: pass # a class with no methods (yet) formatting to one of the built-in types, or use a similar formatting option syntax. - See Format Specification Mini-Language for a description of the + See Format specification mini-language for a description of the standard formatting syntax. The return value must be a string object. @@ -7830,7 +8849,7 @@ def __hash__(self): intended to provide protection against a denial-of-service caused by carefully chosen inputs that exploit the worst case performance of a dict insertion, *O*(*n*^2) complexity. See - http://ocert.org/advisories/ocert-2011-003.html for + https://ocert.org/advisories/ocert-2011-003.html for details.Changing hash values affects the iteration order of sets. Python has never made guarantees about this ordering (and it typically varies between 32-bit and 64-bit builds).See also @@ -7930,6 +8949,9 @@ class instances. Customizing module attribute access ----------------------------------- +module.__getattr__() +module.__dir__() + Special names "__getattr__" and "__dir__" can be also used to customize access to module attributes. The "__getattr__" function at the module level should accept one argument which is the name of an @@ -7945,6 +8967,8 @@ class instances. present, this function overrides the standard "dir()" search on a module. +module.__class__ + For a more fine grained customization of the module behavior (setting attributes, properties, etc.), one can set the "__class__" attribute of a module object to a subclass of "types.ModuleType". For example: @@ -8156,9 +9180,10 @@ class to a new value, *value*. renders the meaning of the program undefined. In the future, a check may be added to prevent this. -* "TypeError" will be raised if nonempty *__slots__* are defined for a - class derived from a ""variable-length" built-in type" such as - "int", "bytes", and "tuple". +* "TypeError" will be raised if *__slots__* other than *__dict__* and + *__weakref__* are defined for a class derived from a ""variable- + length" built-in type" such as "int", "bytes", and "type", except + "tuple". * Any non-string *iterable* may be assigned to *__slots__*. @@ -8179,6 +9204,10 @@ class derived from a ""variable-length" built-in type" such as created for each of the iterator’s values. However, the *__slots__* attribute will be an empty iterator. +Changed in version 3.15: Allowed defining the *__dict__* and +*__weakref__* *__slots__* for any class. Allowed defining any +*__slots__* for a class derived from "tuple". + Customizing class creation ========================== @@ -8640,21 +9669,24 @@ class of a class is known as that class’s *metaclass*, and most The "collections.abc" module provides a "MutableMapping" *abstract base class* to help create those methods from a base set of "__getitem__()", "__setitem__()", "__delitem__()", and "keys()". -Mutable sequences should provide methods "append()", "count()", -"index()", "extend()", "insert()", "pop()", "remove()", "reverse()" -and "sort()", like Python standard "list" objects. Finally, sequence + +Mutable sequences should provide methods "append()", "clear()", +"count()", "extend()", "index()", "insert()", "pop()", "remove()", and +"reverse()", like Python standard "list" objects. Finally, sequence types should implement addition (meaning concatenation) and multiplication (meaning repetition) by defining the methods "__add__()", "__radd__()", "__iadd__()", "__mul__()", "__rmul__()" and "__imul__()" described below; they should not define other numerical -operators. It is recommended that both mappings and sequences -implement the "__contains__()" method to allow efficient use of the -"in" operator; for mappings, "in" should search the mapping’s keys; -for sequences, it should search through the values. It is further -recommended that both mappings and sequences implement the -"__iter__()" method to allow efficient iteration through the -container; for mappings, "__iter__()" should iterate through the -object’s keys; for sequences, it should iterate through the values. +operators. + +It is recommended that both mappings and sequences implement the +"__contains__()" method to allow efficient use of the "in" operator; +for mappings, "in" should search the mapping’s keys; for sequences, it +should search through the values. It is further recommended that both +mappings and sequences implement the "__iter__()" method to allow +efficient iteration through the container; for mappings, "__iter__()" +should iterate through the object’s keys; for sequences, it should +iterate through the values. object.__len__(self) @@ -8681,35 +9713,46 @@ class of a class is known as that class’s *metaclass*, and most Added in version 3.4. -Note: +object.__getitem__(self, subscript) - Slicing is done exclusively with the following three methods. A - call like + Called to implement *subscription*, that is, "self[subscript]". See + Subscriptions and slicings for details on the syntax. - a[1:2] = b + There are two types of built-in objects that support subscription + via "__getitem__()": - is translated to + * **sequences**, where *subscript* (also called *index*) should be + an integer or a "slice" object. See the sequence documentation + for the expected behavior, including handling "slice" objects and + negative indices. - a[slice(1, 2, None)] = b + * **mappings**, where *subscript* is also called the *key*. See + mapping documentation for the expected behavior. - and so forth. Missing slice items are always filled in with "None". + If *subscript* is of an inappropriate type, "__getitem__()" should + raise "TypeError". If *subscript* has an inappropriate value, + "__getitem__()" should raise an "LookupError" or one of its + subclasses ("IndexError" for sequences; "KeyError" for mappings). -object.__getitem__(self, key) + Note: + + Slicing is handled by "__getitem__()", "__setitem__()", and + "__delitem__()". A call like + + a[1:2] = b + + is translated to - Called to implement evaluation of "self[key]". For *sequence* - types, the accepted keys should be integers. Optionally, they may - support "slice" objects as well. Negative index support is also - optional. If *key* is of an inappropriate type, "TypeError" may be - raised; if *key* is a value outside the set of indexes for the - sequence (after any special interpretation of negative values), - "IndexError" should be raised. For *mapping* types, if *key* is - missing (not in the container), "KeyError" should be raised. + a[slice(1, 2, None)] = b + + and so forth. Missing slice items are always filled in with + "None". Note: - "for" loops expect that an "IndexError" will be raised for - illegal indexes to allow proper detection of the end of the - sequence. + The sequence iteration protocol (used, for example, in "for" + loops), expects that an "IndexError" will be raised for illegal + indexes to allow proper detection of the end of a sequence. Note: @@ -8845,9 +9888,9 @@ class that has an "__rsub__()" method, "type(y).__rsub__(y, x)" is third argument if the three-argument version of the built-in "pow()" function is to be supported. - Changed in version 3.14.0a7 (unreleased): Three-argument "pow()" - now try calling "__rpow__()" if necessary. Previously it was only - called in two-argument "pow()" and the binary power operator. + Changed in version 3.14: Three-argument "pow()" now try calling + "__rpow__()" if necessary. Previously it was only called in two- + argument "pow()" and the binary power operator. Note: @@ -9027,14 +10070,27 @@ class is used in a class pattern with positional arguments, each "inspect.BufferFlags" provides a convenient way to interpret the flags. The method must return a "memoryview" object. + **Thread safety:** In *free-threaded* Python, implementations must + manage any internal export counter using atomic operations. The + method must be safe to call concurrently from multiple threads, and + the returned buffer’s underlying data must remain valid until the + corresponding "__release_buffer__()" call completes. See Thread + safety for memoryview objects for details. + object.__release_buffer__(self, buffer) Called when a buffer is no longer needed. The *buffer* argument is a "memoryview" object that was previously returned by "__buffer__()". The method must release any resources associated - with the buffer. This method should return "None". Buffer objects - that do not need to perform any cleanup are not required to - implement this method. + with the buffer. This method should return "None". + + **Thread safety:** In *free-threaded* Python, any export counter + decrement must use atomic operations. Resource cleanup must be + thread-safe, as the final release may race with concurrent releases + from other threads. + + Buffer objects that do not need to perform any cleanup are not + required to implement this method. Added in version 3.12. @@ -9175,7 +10231,7 @@ class is used in a class pattern with positional arguments, each Strings also support two styles of string formatting, one providing a large degree of flexibility and customization (see "str.format()", -Format String Syntax and Custom String Formatting) and the other based +Format string syntax and Custom string formatting) and the other based on C "printf" style formatting that handles a narrower range of types and is slightly harder to use correctly, but is often faster for the cases it can handle (printf-style String Formatting). @@ -9203,19 +10259,31 @@ class is used in a class pattern with positional arguments, each it is intended to remove all case distinctions in a string. For example, the German lowercase letter "'ß'" is equivalent to ""ss"". Since it is already lowercase, "lower()" would do nothing to "'ß'"; - "casefold()" converts it to ""ss"". + "casefold()" converts it to ""ss"". For example: - The casefolding algorithm is described in section 3.13 ‘Default + >>> 'straße'.lower() + 'straße' + >>> 'straße'.casefold() + 'strasse' + + The casefolding algorithm is described in section 3.13.3 ‘Default Case Folding’ of the Unicode Standard. Added in version 3.3. -str.center(width[, fillchar]) +str.center(width, fillchar=' ', /) Return centered in a string of length *width*. Padding is done using the specified *fillchar* (default is an ASCII space). The original string is returned if *width* is less than or equal to - "len(s)". + "len(s)". For example: + + >>> 'Python'.center(10) + ' Python ' + >>> 'Python'.center(10, '-') + '--Python--' + >>> 'Python'.center(4) + 'Python' str.count(sub[, start[, end]]) @@ -9224,7 +10292,18 @@ class is used in a class pattern with positional arguments, each *end* are interpreted as in slice notation. If *sub* is empty, returns the number of empty strings between - characters which is the length of the string plus one. + characters which is the length of the string plus one. For example: + + >>> 'spam, spam, spam'.count('spam') + 3 + >>> 'spam, spam, spam'.count('spam', 5) + 2 + >>> 'spam, spam, spam'.count('spam', 5, 10) + 1 + >>> 'spam, spam, spam'.count('eggs') + 0 + >>> 'spam, spam, spam'.count('') + 17 str.encode(encoding='utf-8', errors='strict') @@ -9241,7 +10320,13 @@ class is used in a class pattern with positional arguments, each For performance reasons, the value of *errors* is not checked for validity unless an encoding error actually occurs, Python - Development Mode is enabled or a debug build is used. + Development Mode is enabled or a debug build is used. For example: + + >>> encoded_str_to_bytes = 'Python'.encode() + >>> type(encoded_str_to_bytes) + + >>> encoded_str_to_bytes + b'Python' Changed in version 3.1: Added support for keyword arguments. @@ -9254,6 +10339,19 @@ class is used in a class pattern with positional arguments, each otherwise return "False". *suffix* can also be a tuple of suffixes to look for. With optional *start*, test beginning at that position. With optional *end*, stop comparing at that position. + Using *start* and *end* is equivalent to + "str[start:end].endswith(suffix)". For example: + + >>> 'Python'.endswith('on') + True + >>> 'a tuple of suffixes'.endswith(('at', 'in')) + False + >>> 'a tuple of suffixes'.endswith(('at', 'es')) + True + >>> 'Python is amazing'.endswith('is', 0, 9) + True + + See also "startswith()" and "removesuffix()". str.expandtabs(tabsize=8) @@ -9269,19 +10367,29 @@ class is used in a class pattern with positional arguments, each ("\n") or return ("\r"), it is copied and the current column is reset to zero. Any other character is copied unchanged and the current column is incremented by one regardless of how the - character is represented when printed. + character is represented when printed. For example: - >>> '01\t012\t0123\t01234'.expandtabs() - '01 012 0123 01234' - >>> '01\t012\t0123\t01234'.expandtabs(4) - '01 012 0123 01234' + >>> '01\t012\t0123\t01234'.expandtabs() + '01 012 0123 01234' + >>> '01\t012\t0123\t01234'.expandtabs(4) + '01 012 0123 01234' + >>> print('01\t012\n0123\t01234'.expandtabs(4)) + 01 012 + 0123 01234 str.find(sub[, start[, end]]) Return the lowest index in the string where substring *sub* is found within the slice "s[start:end]". Optional arguments *start* and *end* are interpreted as in slice notation. Return "-1" if - *sub* is not found. + *sub* is not found. For example: + + >>> 'spam, spam, spam'.find('sp') + 0 + >>> 'spam, spam, spam'.find('sp', 5) + 6 + + See also "rfind()" and "index()". Note: @@ -9300,12 +10408,16 @@ class is used in a class pattern with positional arguments, each the numeric index of a positional argument, or the name of a keyword argument. Returns a copy of the string where each replacement field is replaced with the string value of the - corresponding argument. + corresponding argument. For example: - >>> "The sum of 1 + 2 is {0}".format(1+2) - 'The sum of 1 + 2 is 3' + >>> "The sum of 1 + 2 is {0}".format(1+2) + 'The sum of 1 + 2 is 3' + >>> "The sum of {a} + {b} is {answer}".format(answer=1+2, a=1, b=2) + 'The sum of 1 + 2 is 3' + >>> "{1} expects the {0} Inquisition!".format("Spanish", "Nobody") + 'Nobody expects the Spanish Inquisition!' - See Format String Syntax for a description of the various + See Format string syntax for a description of the various formatting options that can be specified in format strings. Note: @@ -9341,7 +10453,18 @@ class is used in a class pattern with positional arguments, each str.index(sub[, start[, end]]) Like "find()", but raise "ValueError" when the substring is not - found. + found. For example: + + >>> 'spam, spam, spam'.index('spam') + 0 + >>> 'spam, spam, spam'.index('eggs') + Traceback (most recent call last): + File "", line 1, in + 'spam, spam, spam'.index('eggs') + ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^ + ValueError: substring not found + + See also "rindex()". str.isalnum() @@ -9349,6 +10472,16 @@ class is used in a class pattern with positional arguments, each there is at least one character, "False" otherwise. A character "c" is alphanumeric if one of the following returns "True": "c.isalpha()", "c.isdecimal()", "c.isdigit()", or "c.isnumeric()". + For example: + + >>> 'abc123'.isalnum() + True + >>> 'abc123!@#'.isalnum() + False + >>> ''.isalnum() + False + >>> ' '.isalnum() + False str.isalpha() @@ -9358,13 +10491,28 @@ class is used in a class pattern with positional arguments, each database as “Letter”, i.e., those with general category property being one of “Lm”, “Lt”, “Lu”, “Ll”, or “Lo”. Note that this is different from the Alphabetic property defined in the section 4.10 - ‘Letters, Alphabetic, and Ideographic’ of the Unicode Standard. + ‘Letters, Alphabetic, and Ideographic’ of the Unicode Standard. For + example: + + >>> 'Letters and spaces'.isalpha() + False + >>> 'LettersOnly'.isalpha() + True + >>> 'µ'.isalpha() # non-ASCII characters can be considered alphabetical too + True + + See Unicode Properties. str.isascii() Return "True" if the string is empty or all characters in the string are ASCII, "False" otherwise. ASCII characters have code - points in the range U+0000-U+007F. + points in the range U+0000-U+007F. For example: + + >>> 'ASCII characters'.isascii() + True + >>> 'µ'.isascii() + False Added in version 3.7. @@ -9373,8 +10521,16 @@ class is used in a class pattern with positional arguments, each Return "True" if all characters in the string are decimal characters and there is at least one character, "False" otherwise. Decimal characters are those that can be used to form numbers in - base 10, e.g. U+0660, ARABIC-INDIC DIGIT ZERO. Formally a decimal - character is a character in the Unicode General Category “Nd”. + base 10, such as U+0660, ARABIC-INDIC DIGIT ZERO. Formally a + decimal character is a character in the Unicode General Category + “Nd”. For example: + + >>> '0123456789'.isdecimal() + True + >>> '٠١٢٣٤٥٦٧٨٩'.isdecimal() # Arabic-Indic digits zero to nine + True + >>> 'alphabetic'.isdecimal() + False str.isdigit() @@ -9389,7 +10545,7 @@ class is used in a class pattern with positional arguments, each str.isidentifier() Return "True" if the string is a valid identifier according to the - language definition, section Identifiers and keywords. + language definition, section Names (identifiers and keywords). "keyword.iskeyword()" can be used to test whether string "s" is a reserved identifier, such as "def" and "class". @@ -9417,12 +10573,24 @@ class is used in a class pattern with positional arguments, each that have the Unicode numeric value property, e.g. U+2155, VULGAR FRACTION ONE FIFTH. Formally, numeric characters are those with the property value Numeric_Type=Digit, Numeric_Type=Decimal or - Numeric_Type=Numeric. + Numeric_Type=Numeric. For example: + + >>> '0123456789'.isnumeric() + True + >>> '٠١٢٣٤٥٦٧٨٩'.isnumeric() # Arabic-indic digit zero to nine + True + >>> '⅕'.isnumeric() # Vulgar fraction one fifth + True + >>> '²'.isdecimal(), '²'.isdigit(), '²'.isnumeric() + (False, True, True) + + See also "isdecimal()" and "isdigit()". Numeric characters are a + superset of decimal numbers. str.isprintable() - Return true if all characters in the string are printable, false if - it contains at least one non-printable character. + Return "True" if all characters in the string are printable, + "False" if it contains at least one non-printable character. Here “printable” means the character is suitable for "repr()" to use in its output; “non-printable” means that "repr()" on built-in @@ -9435,16 +10603,38 @@ class is used in a class pattern with positional arguments, each plus the ASCII space 0x20. Nonprintable characters are those in group Separator or Other (Z or C), except the ASCII space. + For example: + + >>> ''.isprintable(), ' '.isprintable() + (True, True) + >>> '\t'.isprintable(), '\n'.isprintable() + (False, False) + + See also "isspace()". + str.isspace() Return "True" if there are only whitespace characters in the string and there is at least one character, "False" otherwise. + For example: + + >>> ''.isspace() + False + >>> ' '.isspace() + True + >>> '\t\n'.isspace() # TAB and BREAK LINE + True + >>> '\u3000'.isspace() # IDEOGRAPHIC SPACE + True + A character is *whitespace* if in the Unicode character database (see "unicodedata"), either its general category is "Zs" (“Separator, space”), or its bidirectional class is one of "WS", "B", or "S". + See also "isprintable()". + str.istitle() Return "True" if the string is a titlecased string and there is at @@ -9452,6 +10642,17 @@ class is used in a class pattern with positional arguments, each follow uncased characters and lowercase characters only cased ones. Return "False" otherwise. + For example: + + >>> 'Spam, Spam, Spam'.istitle() + True + >>> 'spam, spam, spam'.istitle() + False + >>> 'SPAM, SPAM, SPAM'.istitle() + False + + See also "title()". + str.isupper() Return "True" if all cased characters [4] in the string are @@ -9467,29 +10668,51 @@ class is used in a class pattern with positional arguments, each >>> ' '.isupper() False -str.join(iterable) +str.join(iterable, /) Return a string which is the concatenation of the strings in *iterable*. A "TypeError" will be raised if there are any non- string values in *iterable*, including "bytes" objects. The - separator between elements is the string providing this method. + separator between elements is the string providing this method. For + example: + + >>> ', '.join(['spam', 'spam', 'spam']) + 'spam, spam, spam' + >>> '-'.join('Python') + 'P-y-t-h-o-n' + + See also "split()". -str.ljust(width[, fillchar]) +str.ljust(width, fillchar=' ', /) Return the string left justified in a string of length *width*. Padding is done using the specified *fillchar* (default is an ASCII space). The original string is returned if *width* is less than or equal to "len(s)". + For example: + + >>> 'Python'.ljust(10) + 'Python ' + >>> 'Python'.ljust(10, '.') + 'Python....' + >>> 'Monty Python'.ljust(10, '.') + 'Monty Python' + + See also "rjust()". + str.lower() Return a copy of the string with all the cased characters [4] - converted to lowercase. + converted to lowercase. For example: - The lowercasing algorithm used is described in section 3.13 - ‘Default Case Folding’ of the Unicode Standard. + >>> 'Lower Method Example'.lower() + 'lower method example' -str.lstrip([chars]) + The lowercasing algorithm used is described in section 3.13.2 + ‘Default Case Conversion’ of the Unicode Standard. + +str.lstrip(chars=None, /) Return a copy of the string with leading characters removed. The *chars* argument is a string specifying the set of characters to be @@ -9510,7 +10733,8 @@ class is used in a class pattern with positional arguments, each >>> 'Arthur: three!'.removeprefix('Arthur: ') 'three!' -static str.maketrans(x[, y[, z]]) +static str.maketrans(dict, /) +static str.maketrans(from, to, remove='', /) This static method returns a translation table usable for "str.translate()". @@ -9521,12 +10745,14 @@ class is used in a class pattern with positional arguments, each Character keys will then be converted to ordinals. If there are two arguments, they must be strings of equal length, - and in the resulting dictionary, each character in x will be mapped - to the character at the same position in y. If there is a third - argument, it must be a string, whose characters will be mapped to - "None" in the result. + and in the resulting dictionary, each character in *from* will be + mapped to the character at the same position in *to*. If there is + a third argument, it must be a string, whose characters will be + mapped to "None" in the result. + + Changed in version 3.15: *dict* can now be a "frozendict". -str.partition(sep) +str.partition(sep, /) Split the string at the first occurrence of *sep*, and return a 3-tuple containing the part before the separator, the separator @@ -9534,6 +10760,17 @@ class is used in a class pattern with positional arguments, each found, return a 3-tuple containing the string itself, followed by two empty strings. + For example: + + >>> 'Monty Python'.partition(' ') + ('Monty', ' ', 'Python') + >>> "Monty Python's Flying Circus".partition(' ') + ('Monty', ' ', "Python's Flying Circus") + >>> 'Monty Python'.partition('-') + ('Monty Python', '', '') + + See also "rpartition()". + str.removeprefix(prefix, /) If the string starts with the *prefix* string, return @@ -9547,6 +10784,8 @@ class is used in a class pattern with positional arguments, each Added in version 3.9. + See also "removesuffix()" and "startswith()". + str.removesuffix(suffix, /) If the string ends with the *suffix* string and that *suffix* is @@ -9560,12 +10799,19 @@ class is used in a class pattern with positional arguments, each Added in version 3.9. -str.replace(old, new, count=-1) + See also "removeprefix()" and "endswith()". + +str.replace(old, new, /, count=-1) Return a copy of the string with all occurrences of substring *old* replaced by *new*. If *count* is given, only the first *count* occurrences are replaced. If *count* is not specified or "-1", then - all occurrences are replaced. + all occurrences are replaced. For example: + + >>> 'spam, spam, spam'.replace('spam', 'eggs') + 'eggs, eggs, eggs' + >>> 'spam, spam, spam'.replace('spam', 'eggs', 1) + 'eggs, spam, spam' Changed in version 3.13: *count* is now supported as a keyword argument. @@ -9575,21 +10821,50 @@ class is used in a class pattern with positional arguments, each Return the highest index in the string where substring *sub* is found, such that *sub* is contained within "s[start:end]". Optional arguments *start* and *end* are interpreted as in slice - notation. Return "-1" on failure. + notation. Return "-1" on failure. For example: + + >>> 'spam, spam, spam'.rfind('sp') + 12 + >>> 'spam, spam, spam'.rfind('sp', 0, 10) + 6 + + See also "find()" and "rindex()". str.rindex(sub[, start[, end]]) Like "rfind()" but raises "ValueError" when the substring *sub* is - not found. + not found. For example: + + >>> 'spam, spam, spam'.rindex('spam') + 12 + >>> 'spam, spam, spam'.rindex('eggs') + Traceback (most recent call last): + File "", line 1, in + 'spam, spam, spam'.rindex('eggs') + ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^ + ValueError: substring not found -str.rjust(width[, fillchar]) + See also "index()" and "find()". + +str.rjust(width, fillchar=' ', /) Return the string right justified in a string of length *width*. Padding is done using the specified *fillchar* (default is an ASCII space). The original string is returned if *width* is less than or equal to "len(s)". -str.rpartition(sep) + For example: + + >>> 'Python'.rjust(10) + ' Python' + >>> 'Python'.rjust(10, '.') + '....Python' + >>> 'Monty Python'.rjust(10, '.') + 'Monty Python' + + See also "ljust()" and "zfill()". + +str.rpartition(sep, /) Split the string at the last occurrence of *sep*, and return a 3-tuple containing the part before the separator, the separator @@ -9597,6 +10872,17 @@ class is used in a class pattern with positional arguments, each found, return a 3-tuple containing two empty strings, followed by the string itself. + For example: + + >>> 'Monty Python'.rpartition(' ') + ('Monty', ' ', 'Python') + >>> "Monty Python's Flying Circus".rpartition(' ') + ("Monty Python's Flying", ' ', 'Circus') + >>> 'Monty Python'.rpartition('-') + ('', '', 'Monty Python') + + See also "partition()". + str.rsplit(sep=None, maxsplit=-1) Return a list of the words in the string, using *sep* as the @@ -9606,27 +10892,29 @@ class is used in a class pattern with positional arguments, each from the right, "rsplit()" behaves like "split()" which is described in detail below. -str.rstrip([chars]) +str.rstrip(chars=None, /) Return a copy of the string with trailing characters removed. The *chars* argument is a string specifying the set of characters to be removed. If omitted or "None", the *chars* argument defaults to removing whitespace. The *chars* argument is not a suffix; rather, - all combinations of its values are stripped: + all combinations of its values are stripped. For example: >>> ' spacious '.rstrip() ' spacious' >>> 'mississippi'.rstrip('ipz') 'mississ' - See "str.removesuffix()" for a method that will remove a single - suffix string rather than all of a set of characters. For example: + See "removesuffix()" for a method that will remove a single suffix + string rather than all of a set of characters. For example: >>> 'Monty Python'.rstrip(' Python') 'M' >>> 'Monty Python'.removesuffix(' Python') 'Monty' + See also "strip()". + str.split(sep=None, maxsplit=-1) Return a list of the words in the string, using *sep* as the @@ -9669,6 +10957,20 @@ class is used in a class pattern with positional arguments, each >>> ' 1 2 3 '.split() ['1', '2', '3'] + If *sep* is not specified or is "None" and *maxsplit* is "0", only + leading runs of consecutive whitespace are considered. + + For example: + + >>> "".split(None, 0) + [] + >>> " ".split(None, 0) + [] + >>> " foo ".split(maxsplit=0) + ['foo '] + + See also "join()". + str.splitlines(keepends=False) Return a list of the lines in the string, breaking at line @@ -9737,14 +11039,27 @@ class is used in a class pattern with positional arguments, each With optional *start*, test string beginning at that position. With optional *end*, stop comparing string at that position. -str.strip([chars]) + For example: + + >>> 'Python'.startswith('Py') + True + >>> 'a tuple of prefixes'.startswith(('at', 'a')) + True + >>> 'Python is amazing'.startswith('is', 7) + True + + See also "endswith()" and "removeprefix()". + +str.strip(chars=None, /) Return a copy of the string with the leading and trailing characters removed. The *chars* argument is a string specifying the set of characters to be removed. If omitted or "None", the *chars* argument defaults to removing whitespace. The *chars* argument is not a prefix or suffix; rather, all combinations of its values are - stripped: + stripped. + + For example: >>> ' spacious '.strip() 'spacious' @@ -9755,17 +11070,31 @@ class is used in a class pattern with positional arguments, each stripped from the string. Characters are removed from the leading end until reaching a string character that is not contained in the set of characters in *chars*. A similar action takes place on the - trailing end. For example: + trailing end. + + For example: >>> comment_string = '#....... Section 3.2.1 Issue #32 .......' >>> comment_string.strip('.#! ') 'Section 3.2.1 Issue #32' + See also "rstrip()". + str.swapcase() Return a copy of the string with uppercase characters converted to - lowercase and vice versa. Note that it is not necessarily true that - "s.swapcase().swapcase() == s". + lowercase and vice versa. For example: + + >>> 'Hello World'.swapcase() + 'hELLO wORLD' + + Note that it is not necessarily true that "s.swapcase().swapcase() + == s". For example: + + >>> 'straße'.swapcase().swapcase() + 'strasse' + + See also "str.lower()" and "str.upper()". str.title() @@ -9801,7 +11130,9 @@ class is used in a class pattern with positional arguments, each >>> titlecase("they're bill's friends.") "They're Bill's Friends." -str.translate(table) + See also "istitle()". + +str.translate(table, /) Return a copy of the string in which each character has been mapped through the given translation table. The table must be an object @@ -9815,6 +11146,12 @@ class is used in a class pattern with positional arguments, each You can use "str.maketrans()" to create a translation map from character-to-character mappings in different formats. + The following example uses a mapping to replace "'a'" with "'X'", + "'b'" with "'Y'", and delete "'c'": + + >>> 'abc123'.translate({ord('a'): 'X', ord('b'): 'Y', ord('c'): None}) + 'XY123' + See also the "codecs" module for a more flexible approach to custom character mappings. @@ -9826,10 +11163,10 @@ class is used in a class pattern with positional arguments, each category of the resulting character(s) is not “Lu” (Letter, uppercase), but e.g. “Lt” (Letter, titlecase). - The uppercasing algorithm used is described in section 3.13 - ‘Default Case Folding’ of the Unicode Standard. + The uppercasing algorithm used is described in section 3.13.2 + ‘Default Case Conversion’ of the Unicode Standard. -str.zfill(width) +str.zfill(width, /) Return a copy of the string left filled with ASCII "'0'" digits to make a string of length *width*. A leading sign prefix @@ -9843,178 +11180,319 @@ class is used in a class pattern with positional arguments, each '00042' >>> "-42".zfill(5) '-0042' + + See also "rjust()". ''', 'strings': '''String and Bytes literals ************************* -String literals are described by the following lexical definitions: - - stringliteral: [stringprefix](shortstring | longstring) - stringprefix: "r" | "u" | "R" | "U" | "f" | "F" - | "fr" | "Fr" | "fR" | "FR" | "rf" | "rF" | "Rf" | "RF" - shortstring: "'" shortstringitem* "'" | '"' shortstringitem* '"' - longstring: "\'\'\'" longstringitem* "\'\'\'" | '"""' longstringitem* '"""' - shortstringitem: shortstringchar | stringescapeseq - longstringitem: longstringchar | stringescapeseq - shortstringchar: - longstringchar: - stringescapeseq: "\\" - - bytesliteral: bytesprefix(shortbytes | longbytes) - bytesprefix: "b" | "B" | "br" | "Br" | "bR" | "BR" | "rb" | "rB" | "Rb" | "RB" - shortbytes: "'" shortbytesitem* "'" | '"' shortbytesitem* '"' - longbytes: "\'\'\'" longbytesitem* "\'\'\'" | '"""' longbytesitem* '"""' - shortbytesitem: shortbyteschar | bytesescapeseq - longbytesitem: longbyteschar | bytesescapeseq - shortbyteschar: - longbyteschar: - bytesescapeseq: "\\" - -One syntactic restriction not indicated by these productions is that -whitespace is not allowed between the "stringprefix" or "bytesprefix" -and the rest of the literal. The source character set is defined by -the encoding declaration; it is UTF-8 if no encoding declaration is -given in the source file; see section Encoding declarations. - -In plain English: Both types of literals can be enclosed in matching -single quotes ("'") or double quotes ("""). They can also be enclosed -in matching groups of three single or double quotes (these are -generally referred to as *triple-quoted strings*). The backslash ("\\") -character is used to give special meaning to otherwise ordinary -characters like "n", which means ‘newline’ when escaped ("\\n"). It can -also be used to escape characters that otherwise have a special -meaning, such as newline, backslash itself, or the quote character. -See escape sequences below for examples. - -Bytes literals are always prefixed with "'b'" or "'B'"; they produce -an instance of the "bytes" type instead of the "str" type. They may -only contain ASCII characters; bytes with a numeric value of 128 or -greater must be expressed with escapes. +String literals are text enclosed in single quotes ("'") or double +quotes ("""). For example: -Both string and bytes literals may optionally be prefixed with a -letter "'r'" or "'R'"; such constructs are called *raw string -literals* and *raw bytes literals* respectively and treat backslashes -as literal characters. As a result, in raw string literals, "'\\U'" -and "'\\u'" escapes are not treated specially. + "spam" + 'eggs' + +The quote used to start the literal also terminates it, so a string +literal can only contain the other quote (except with escape +sequences, see below). For example: + + 'Say "Hello", please.' + "Don't do that!" + +Except for this limitation, the choice of quote character ("'" or """) +does not affect how the literal is parsed. + +Inside a string literal, the backslash ("\\") character introduces an +*escape sequence*, which has special meaning depending on the +character after the backslash. For example, "\\"" denotes the double +quote character, and does *not* end the string: + + >>> print("Say \\"Hello\\" to everyone!") + Say "Hello" to everyone! + +See escape sequences below for a full list of such sequences, and more +details. + + +Triple-quoted strings +===================== + +Strings can also be enclosed in matching groups of three single or +double quotes. These are generally referred to as *triple-quoted +strings*: + + """This is a triple-quoted string.""" + +In triple-quoted literals, unescaped quotes are allowed (and are +retained), except that three unescaped quotes in a row terminate the +literal, if they are of the same kind ("'" or """) used at the start: + + """This string has "quotes" inside.""" + +Unescaped newlines are also allowed and retained: + + \'\'\'This triple-quoted string + continues on the next line.\'\'\' + + +String prefixes +=============== + +String literals can have an optional *prefix* that influences how the +content of the literal is parsed, for example: + + b"data" + f'{result=}' + +The allowed prefixes are: + +* "b": Bytes literal + +* "r": Raw string + +* "f": Formatted string literal (“f-string”) + +* "t": Template string literal (“t-string”) + +* "u": No effect (allowed for backwards compatibility) + +See the linked sections for details on each type. + +Prefixes are case-insensitive (for example, ‘"B"’ works the same as +‘"b"’). The ‘"r"’ prefix can be combined with ‘"f"’, ‘"t"’ or ‘"b"’, +so ‘"fr"’, ‘"rf"’, ‘"tr"’, ‘"rt"’, ‘"br"’, and ‘"rb"’ are also valid +prefixes. Added in version 3.3: The "'rb'" prefix of raw bytes literals has been added as a synonym of "'br'".Support for the unicode legacy literal ("u'value'") was reintroduced to simplify the maintenance of dual Python 2.x and 3.x codebases. See **PEP 414** for more information. -A string literal with "'f'" or "'F'" in its prefix is a *formatted -string literal*; see f-strings. The "'f'" may be combined with "'r'", -but not with "'b'" or "'u'", therefore raw formatted strings are -possible, but formatted bytes literals are not. -In triple-quoted literals, unescaped newlines and quotes are allowed -(and are retained), except that three unescaped quotes in a row -terminate the literal. (A “quote” is the character used to open the -literal, i.e. either "'" or """.) +Formal grammar +============== + +String literals, except “f-strings” and “t-strings”, are described by +the following lexical definitions. + +These definitions use negative lookaheads ("!") to indicate that an +ending quote ends the literal. + + STRING: [stringprefix] (stringcontent) + stringprefix: <("r" | "u" | "b" | "br" | "rb"), case-insensitive> + stringcontent: + | "\'\'\'" ( !"\'\'\'" longstringitem)* "\'\'\'" + | '"""' ( !'"""' longstringitem)* '"""' + | "'" ( !"'" stringitem)* "'" + | '"' ( !'"' stringitem)* '"' + stringitem: stringchar | stringescapeseq + stringchar: + longstringitem: stringitem | newline + stringescapeseq: "\\" + +Note that as in all lexical definitions, whitespace is significant. In +particular, the prefix (if any) must be immediately followed by the +starting quote. Escape sequences ================ -Unless an "'r'" or "'R'" prefix is present, escape sequences in string +Unless an ‘"r"’ or ‘"R"’ prefix is present, escape sequences in string and bytes literals are interpreted according to rules similar to those used by Standard C. The recognized escape sequences are: -+---------------------------+-----------------------------------+---------+ -| Escape Sequence | Meaning | Notes | -|===========================|===================================|=========| -| "\\" | Backslash and newline ignored | (1) | -+---------------------------+-----------------------------------+---------+ -| "\\\\" | Backslash ("\\") | | -+---------------------------+-----------------------------------+---------+ -| "\\'" | Single quote ("'") | | -+---------------------------+-----------------------------------+---------+ -| "\\"" | Double quote (""") | | -+---------------------------+-----------------------------------+---------+ -| "\\a" | ASCII Bell (BEL) | | -+---------------------------+-----------------------------------+---------+ -| "\\b" | ASCII Backspace (BS) | | -+---------------------------+-----------------------------------+---------+ -| "\\f" | ASCII Formfeed (FF) | | -+---------------------------+-----------------------------------+---------+ -| "\\n" | ASCII Linefeed (LF) | | -+---------------------------+-----------------------------------+---------+ -| "\\r" | ASCII Carriage Return (CR) | | -+---------------------------+-----------------------------------+---------+ -| "\\t" | ASCII Horizontal Tab (TAB) | | -+---------------------------+-----------------------------------+---------+ -| "\\v" | ASCII Vertical Tab (VT) | | -+---------------------------+-----------------------------------+---------+ -| "\\*ooo*" | Character with octal value *ooo* | (2,4) | -+---------------------------+-----------------------------------+---------+ -| "\\x*hh*" | Character with hex value *hh* | (3,4) | -+---------------------------+-----------------------------------+---------+ - -Escape sequences only recognized in string literals are: - -+---------------------------+-----------------------------------+---------+ -| Escape Sequence | Meaning | Notes | -|===========================|===================================|=========| -| "\\N{*name*}" | Character named *name* in the | (5) | -| | Unicode database | | -+---------------------------+-----------------------------------+---------+ -| "\\u*xxxx*" | Character with 16-bit hex value | (6) | -| | *xxxx* | | -+---------------------------+-----------------------------------+---------+ -| "\\U*xxxxxxxx*" | Character with 32-bit hex value | (7) | -| | *xxxxxxxx* | | -+---------------------------+-----------------------------------+---------+ ++----------------------------------------------------+----------------------------------------------------+ +| Escape Sequence | Meaning | +|====================================================|====================================================| +| "\\" | Ignored end of line | ++----------------------------------------------------+----------------------------------------------------+ +| "\\\\" | Backslash | ++----------------------------------------------------+----------------------------------------------------+ +| "\\'" | Single quote | ++----------------------------------------------------+----------------------------------------------------+ +| "\\"" | Double quote | ++----------------------------------------------------+----------------------------------------------------+ +| "\\a" | ASCII Bell (BEL) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\b" | ASCII Backspace (BS) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\f" | ASCII Formfeed (FF) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\n" | ASCII Linefeed (LF) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\r" | ASCII Carriage Return (CR) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\t" | ASCII Horizontal Tab (TAB) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\v" | ASCII Vertical Tab (VT) | ++----------------------------------------------------+----------------------------------------------------+ +| "\\*ooo*" | Octal character | ++----------------------------------------------------+----------------------------------------------------+ +| "\\x*hh*" | Hexadecimal character | ++----------------------------------------------------+----------------------------------------------------+ +| "\\N{*name*}" | Named Unicode character | ++----------------------------------------------------+----------------------------------------------------+ +| "\\u*xxxx*" | Hexadecimal Unicode character | ++----------------------------------------------------+----------------------------------------------------+ +| "\\U*xxxxxxxx*" | Hexadecimal Unicode character | ++----------------------------------------------------+----------------------------------------------------+ + -Notes: +Ignored end of line +------------------- + +A backslash can be added at the end of a line to ignore the newline: + + >>> 'This string will not include \\ + ... backslashes or newline characters.' + 'This string will not include backslashes or newline characters.' + +The same result can be achieved using triple-quoted strings, or +parentheses and string literal concatenation. + + +Escaped characters +------------------ + +To include a backslash in a non-raw Python string literal, it must be +doubled. The "\\\\" escape sequence denotes a single backslash +character: + + >>> print('C:\\\\Program Files') + C:\\Program Files + +Similarly, the "\\'" and "\\"" sequences denote the single and double +quote character, respectively: + + >>> print('\\' and \\"') + ' and " + + +Octal character +--------------- + +The sequence "\\*ooo*" denotes a *character* with the octal (base 8) +value *ooo*: + + >>> '\\120' + 'P' + +Up to three octal digits (0 through 7) are accepted. + +In a bytes literal, *character* means a *byte* with the given value. +In a string literal, it means a Unicode character with the given +value. -1. A backslash can be added at the end of a line to ignore the - newline: +Changed in version 3.11: Octal escapes with value larger than "0o377" +(255) produce a "DeprecationWarning". - >>> 'This string will not include \\ - ... backslashes or newline characters.' - 'This string will not include backslashes or newline characters.' +Changed in version 3.12: Octal escapes with value larger than "0o377" +(255) produce a "SyntaxWarning". In a future Python version they will +raise a "SyntaxError". - The same result can be achieved using triple-quoted strings, or - parentheses and string literal concatenation. -2. As in Standard C, up to three octal digits are accepted. +Hexadecimal character +--------------------- + +The sequence "\\x*hh*" denotes a *character* with the hex (base 16) +value *hh*: + + >>> '\\x50' + 'P' + +Unlike in Standard C, exactly two hex digits are required. + +In a bytes literal, *character* means a *byte* with the given value. +In a string literal, it means a Unicode character with the given +value. + + +Named Unicode character +----------------------- + +The sequence "\\N{*name*}" denotes a Unicode character with the given +*name*: + + >>> '\\N{LATIN CAPITAL LETTER P}' + 'P' + >>> '\\N{SNAKE}' + '🐍' + +This sequence cannot appear in bytes literals. + +Changed in version 3.3: Support for name aliases has been added. - Changed in version 3.11: Octal escapes with value larger than - "0o377" produce a "DeprecationWarning". - Changed in version 3.12: Octal escapes with value larger than - "0o377" produce a "SyntaxWarning". In a future Python version they - will be eventually a "SyntaxError". +Hexadecimal Unicode characters +------------------------------ -3. Unlike in Standard C, exactly two hex digits are required. +These sequences "\\u*xxxx*" and "\\U*xxxxxxxx*" denote the Unicode +character with the given hex (base 16) value. Exactly four digits are +required for "\\u"; exactly eight digits are required for "\\U". The +latter can encode any Unicode character. -4. In a bytes literal, hexadecimal and octal escapes denote the byte - with the given value. In a string literal, these escapes denote a - Unicode character with the given value. + >>> '\\u1234' + 'ሴ' + >>> '\\U0001f40d' + '🐍' -5. Changed in version 3.3: Support for name aliases [1] has been - added. +These sequences cannot appear in bytes literals. -6. Exactly four hex digits are required. -7. Any Unicode character can be encoded this way. Exactly eight hex - digits are required. +Unrecognized escape sequences +----------------------------- + +Unlike in Standard C, all unrecognized escape sequences are left in +the string unchanged, that is, *the backslash is left in the result*: + + >>> print('\\q') + \\q + >>> list('\\q') + ['\\\\', 'q'] -Unlike Standard C, all unrecognized escape sequences are left in the -string unchanged, i.e., *the backslash is left in the result*. (This -behavior is useful when debugging: if an escape sequence is mistyped, -the resulting output is more easily recognized as broken.) It is also -important to note that the escape sequences only recognized in string -literals fall into the category of unrecognized escapes for bytes -literals. +Note that for bytes literals, the escape sequences only recognized in +string literals ("\\N...", "\\u...", "\\U...") fall into the category of +unrecognized escapes. Changed in version 3.6: Unrecognized escape sequences produce a "DeprecationWarning". Changed in version 3.12: Unrecognized escape sequences produce a -"SyntaxWarning". In a future Python version they will be eventually a +"SyntaxWarning". In a future Python version they will raise a "SyntaxError". + +Bytes literals +============== + +*Bytes literals* are always prefixed with ‘"b"’ or ‘"B"’; they produce +an instance of the "bytes" type instead of the "str" type. They may +only contain ASCII characters; bytes with a numeric value of 128 or +greater must be expressed with escape sequences (typically Hexadecimal +character or Octal character): + + >>> b'\\x89PNG\\r\\n\\x1a\\n' + b'\\x89PNG\\r\\n\\x1a\\n' + >>> list(b'\\x89PNG\\r\\n\\x1a\\n') + [137, 80, 78, 71, 13, 10, 26, 10] + +Similarly, a zero byte must be expressed using an escape sequence +(typically "\\0" or "\\x00"). + + +Raw string literals +=================== + +Both string and bytes literals may optionally be prefixed with a +letter ‘"r"’ or ‘"R"’; such constructs are called *raw string +literals* and *raw bytes literals* respectively and treat backslashes +as literal characters. As a result, in raw string literals, escape +sequences are not treated specially: + + >>> r'\\d{4}-\\d{2}-\\d{2}' + '\\\\d{4}-\\\\d{2}-\\\\d{2}' + Even in a raw literal, quotes can be escaped with a backslash, but the backslash remains in the result; for example, "r"\\""" is a valid string literal consisting of two characters: a backslash and a double @@ -10024,63 +11502,414 @@ class is used in a class pattern with positional arguments, each the following quote character). Note also that a single backslash followed by a newline is interpreted as those two characters as part of the literal, *not* as a line continuation. + + +f-strings +========= + +Added in version 3.6. + +Changed in version 3.7: The "await" and "async for" can be used in +expressions within f-strings. + +Changed in version 3.8: Added the debug specifier ("=") + +Changed in version 3.12: Many restrictions on expressions within +f-strings have been removed. Notably, nested strings, comments, and +backslashes are now permitted. + +A *formatted string literal* or *f-string* is a string literal that is +prefixed with ‘"f"’ or ‘"F"’. Unlike other string literals, f-strings +do not have a constant value. They may contain *replacement fields* +delimited by curly braces "{}". Replacement fields contain expressions +which are evaluated at run time. For example: + + >>> who = 'nobody' + >>> nationality = 'Spanish' + >>> f'{who.title()} expects the {nationality} Inquisition!' + 'Nobody expects the Spanish Inquisition!' + +Any doubled curly braces ("{{" or "}}") outside replacement fields are +replaced with the corresponding single curly brace: + + >>> print(f'{{...}}') + {...} + +Other characters outside replacement fields are treated like in +ordinary string literals. This means that escape sequences are decoded +(except when a literal is also marked as a raw string), and newlines +are possible in triple-quoted f-strings: + + >>> name = 'Galahad' + >>> favorite_color = 'blue' + >>> print(f'{name}:\\t{favorite_color}') + Galahad: blue + >>> print(rf"C:\\Users\\{name}") + C:\\Users\\Galahad + >>> print(f\'\'\'Three shall be the number of the counting + ... and the number of the counting shall be three.\'\'\') + Three shall be the number of the counting + and the number of the counting shall be three. + +Expressions in formatted string literals are treated like regular +Python expressions. Each expression is evaluated in the context where +the formatted string literal appears, in order from left to right. An +empty expression is not allowed, and both "lambda" and assignment +expressions ":=" must be surrounded by explicit parentheses: + + >>> f'{(half := 1/2)}, {half * 42}' + '0.5, 21.0' + +Reusing the outer f-string quoting type inside a replacement field is +permitted: + + >>> a = dict(x=2) + >>> f"abc {a["x"]} def" + 'abc 2 def' + +Backslashes are also allowed in replacement fields and are evaluated +the same way as in any other context: + + >>> a = ["a", "b", "c"] + >>> print(f"List a contains:\\n{"\\n".join(a)}") + List a contains: + a + b + c + +It is possible to nest f-strings: + + >>> name = 'world' + >>> f'Repeated:{f' hello {name}' * 3}' + 'Repeated: hello world hello world hello world' + +Portable Python programs should not use more than 5 levels of nesting. + +**CPython implementation detail:** CPython does not limit nesting of +f-strings. + +Replacement expressions can contain newlines in both single-quoted and +triple-quoted f-strings and they can contain comments. Everything that +comes after a "#" inside a replacement field is a comment (even +closing braces and quotes). This means that replacement fields with +comments must be closed in a different line: + + >>> a = 2 + >>> f"abc{a # This comment }" continues until the end of the line + ... + 3}" + 'abc5' + +After the expression, replacement fields may optionally contain: + +* a *debug specifier* – an equal sign ("="), optionally surrounded by + whitespace on one or both sides; + +* a *conversion specifier* – "!s", "!r" or "!a"; and/or + +* a *format specifier* prefixed with a colon (":"). + +See the Standard Library section on f-strings for details on how these +fields are evaluated. + +As that section explains, *format specifiers* are passed as the second +argument to the "format()" function to format a replacement field +value. For example, they can be used to specify a field width and +padding characters using the Format Specification Mini-Language: + + >>> number = 14.3 + >>> f'{number:20.7f}' + ' 14.3000000' + +Top-level format specifiers may include nested replacement fields: + + >>> field_size = 20 + >>> precision = 7 + >>> f'{number:{field_size}.{precision}f}' + ' 14.3000000' + +These nested fields may include their own conversion fields and format +specifiers: + + >>> number = 3 + >>> f'{number:{field_size}}' + ' 3' + >>> f'{number:{field_size:05}}' + '00000000000000000003' + +However, these nested fields may not include more deeply nested +replacement fields. + +Formatted string literals cannot be used as *docstrings*, even if they +do not include expressions: + + >>> def foo(): + ... f"Not a docstring" + ... + >>> print(foo.__doc__) + None + +See also: + + * **PEP 498** – Literal String Interpolation + + * **PEP 701** – Syntactic formalization of f-strings + + * "str.format()", which uses a related format string mechanism. + + +t-strings +========= + +Added in version 3.14. + +A *template string literal* or *t-string* is a string literal that is +prefixed with ‘"t"’ or ‘"T"’. These strings follow the same syntax +rules as formatted string literals. For differences in evaluation +rules, see the Standard Library section on t-strings + + +Formal grammar for f-strings +============================ + +F-strings are handled partly by the *lexical analyzer*, which produces +the tokens "FSTRING_START", "FSTRING_MIDDLE" and "FSTRING_END", and +partly by the parser, which handles expressions in the replacement +field. The exact way the work is split is a CPython implementation +detail. + +Correspondingly, the f-string grammar is a mix of lexical and +syntactic definitions. + +Whitespace is significant in these situations: + +* There may be no whitespace in "FSTRING_START" (between the prefix + and quote). + +* Whitespace in "FSTRING_MIDDLE" is part of the literal string + contents. + +* In "fstring_replacement_field", if "f_debug_specifier" is present, + all whitespace after the opening brace until the + "f_debug_specifier", as well as whitespace immediately following + "f_debug_specifier", is retained as part of the expression. + + **CPython implementation detail:** The expression is not handled in + the tokenization phase; it is retrieved from the source code using + locations of the "{" token and the token after "=". + +The "FSTRING_MIDDLE" definition uses negative lookaheads ("!") to +indicate special characters (backslash, newline, "{", "}") and +sequences ("f_quote"). + + fstring: FSTRING_START fstring_middle* FSTRING_END + + FSTRING_START: fstringprefix ("'" | '"' | "\'\'\'" | '"""') + FSTRING_END: f_quote + fstringprefix: <("f" | "fr" | "rf"), case-insensitive> + f_debug_specifier: '=' + f_quote: + + fstring_middle: + | fstring_replacement_field + | FSTRING_MIDDLE + FSTRING_MIDDLE: + | (!"\\" !newline !'{' !'}' !f_quote) source_character + | stringescapeseq + | "{{" + | "}}" + | + fstring_replacement_field: + | '{' f_expression [f_debug_specifier] [fstring_conversion] + [fstring_full_format_spec] '}' + fstring_conversion: + | "!" ("s" | "r" | "a") + fstring_full_format_spec: + | ':' fstring_format_spec* + fstring_format_spec: + | FSTRING_MIDDLE + | fstring_replacement_field + f_expression: + | ','.(conditional_expression | "*" or_expr)+ [","] + | yield_expression + +Note: + + In the above grammar snippet, the "f_quote" and "FSTRING_MIDDLE" + rules are context-sensitive – they depend on the contents of + "FSTRING_START" of the nearest enclosing "fstring".Constructing a + more traditional formal grammar from this template is left as an + exercise for the reader. + +The grammar for t-strings is identical to the one for f-strings, with +*t* instead of *f* at the beginning of rule and token names and in the +prefix. + + tstring: TSTRING_START tstring_middle* TSTRING_END + + ''', - 'subscriptions': r'''Subscriptions -************* + 'subscriptions': r'''Subscriptions and slicings +************************** + +The *subscription* syntax is usually used for selecting an element +from a container – for example, to get a value from a "dict": -The subscription of an instance of a container class will generally -select an element from the container. The subscription of a *generic -class* will generally return a GenericAlias object. + >>> digits_by_name = {'one': 1, 'two': 2} + >>> digits_by_name['two'] # Subscripting a dictionary using the key 'two' + 2 - subscription: primary "[" flexible_expression_list "]" +In the subscription syntax, the object being subscribed – a primary – +is followed by a *subscript* in square brackets. In the simplest case, +the subscript is a single expression. -When an object is subscripted, the interpreter will evaluate the -primary and the expression list. +Depending on the type of the object being subscribed, the subscript is +sometimes called a *key* (for mappings), *index* (for sequences), or +*type argument* (for *generic types*). Syntactically, these are all +equivalent: -The primary must evaluate to an object that supports subscription. An -object may support subscription through defining one or both of -"__getitem__()" and "__class_getitem__()". When the primary is -subscripted, the evaluated result of the expression list will be -passed to one of these methods. For more details on when -"__class_getitem__" is called instead of "__getitem__", see + >>> colors = ['red', 'blue', 'green', 'black'] + >>> colors[3] # Subscripting a list using the index 3 + 'black' + + >>> list[str] # Parameterizing the list type using the type argument str + list[str] + +At runtime, the interpreter will evaluate the primary and the +subscript, and call the primary’s "__getitem__()" or +"__class_getitem__()" *special method* with the subscript as argument. +For more details on which of these methods is called, see __class_getitem__ versus __getitem__. -If the expression list contains at least one comma, or if any of the -expressions are starred, the expression list will evaluate to a -"tuple" containing the items of the expression list. Otherwise, the -expression list will evaluate to the value of the list’s sole member. - -Changed in version 3.11: Expressions in an expression list may be -starred. See **PEP 646**. - -For built-in objects, there are two types of objects that support -subscription via "__getitem__()": - -1. Mappings. If the primary is a *mapping*, the expression list must - evaluate to an object whose value is one of the keys of the - mapping, and the subscription selects the value in the mapping that - corresponds to that key. An example of a builtin mapping class is - the "dict" class. - -2. Sequences. If the primary is a *sequence*, the expression list must - evaluate to an "int" or a "slice" (as discussed in the following - section). Examples of builtin sequence classes include the "str", - "list" and "tuple" classes. - -The formal syntax makes no special provision for negative indices in -*sequences*. However, built-in sequences all provide a "__getitem__()" -method that interprets negative indices by adding the length of the -sequence to the index so that, for example, "x[-1]" selects the last -item of "x". The resulting value must be a nonnegative integer less -than the number of items in the sequence, and the subscription selects -the item whose index is that value (counting from zero). Since the -support for negative indices and slicing occurs in the object’s -"__getitem__()" method, subclasses overriding this method will need to -explicitly add that support. - -A "string" is a special kind of sequence whose items are *characters*. -A character is not a separate data type but a string of exactly one -character. +To show how subscription works, we can define a custom object that +implements "__getitem__()" and prints out the value of the subscript: + + >>> class SubscriptionDemo: + ... def __getitem__(self, key): + ... print(f'subscripted with: {key!r}') + ... + >>> demo = SubscriptionDemo() + >>> demo[1] + subscripted with: 1 + >>> demo['a' * 3] + subscripted with: 'aaa' + +See "__getitem__()" documentation for how built-in types handle +subscription. + +Subscriptions may also be used as targets in assignment or deletion +statements. In these cases, the interpreter will call the subscripted +object’s "__setitem__()" or "__delitem__()" *special method*, +respectively, instead of "__getitem__()". + + >>> colors = ['red', 'blue', 'green', 'black'] + >>> colors[3] = 'white' # Setting item at index + >>> colors + ['red', 'blue', 'green', 'white'] + >>> del colors[3] # Deleting item at index 3 + >>> colors + ['red', 'blue', 'green'] + +All advanced forms of *subscript* documented in the following sections +are also usable for assignment and deletion. + + +Slicings +======== + +A more advanced form of subscription, *slicing*, is commonly used to +extract a portion of a sequence. In this form, the subscript is a +*slice*: up to three expressions separated by colons. Any of the +expressions may be omitted, but a slice must contain at least one +colon: + + >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five'] + >>> number_names[1:3] + ['one', 'two'] + >>> number_names[1:] + ['one', 'two', 'three', 'four', 'five'] + >>> number_names[:3] + ['zero', 'one', 'two'] + >>> number_names[:] + ['zero', 'one', 'two', 'three', 'four', 'five'] + >>> number_names[::2] + ['zero', 'two', 'four'] + >>> number_names[:-3] + ['zero', 'one', 'two'] + >>> del number_names[4:] + >>> number_names + ['zero', 'one', 'two', 'three'] + +When a slice is evaluated, the interpreter constructs a "slice" object +whose "start", "stop" and "step" attributes, respectively, are the +results of the expressions between the colons. Any missing expression +evaluates to "None". This "slice" object is then passed to the +"__getitem__()" or "__class_getitem__()" *special method*, as above. + + # continuing with the SubscriptionDemo instance defined above: + >>> demo[2:3] + subscripted with: slice(2, 3, None) + >>> demo[::'spam'] + subscripted with: slice(None, None, 'spam') + + +Comma-separated subscripts +========================== + +The subscript can also be given as two or more comma-separated +expressions or slices: + + # continuing with the SubscriptionDemo instance defined above: + >>> demo[1, 2, 3] + subscripted with: (1, 2, 3) + >>> demo[1:2, 3] + subscripted with: (slice(1, 2, None), 3) + +This form is commonly used with numerical libraries for slicing multi- +dimensional data. In this case, the interpreter constructs a "tuple" +of the results of the expressions or slices, and passes this tuple to +the "__getitem__()" or "__class_getitem__()" *special method*, as +above. + +The subscript may also be given as a single expression or slice +followed by a comma, to specify a one-element tuple: + + >>> demo['spam',] + subscripted with: ('spam',) + + +“Starred” subscriptions +======================= + +Added in version 3.11: Expressions in *tuple_slices* may be starred. +See **PEP 646**. + +The subscript can also contain a starred expression. In this case, the +interpreter unpacks the result into a tuple, and passes this tuple to +"__getitem__()" or "__class_getitem__()": + + # continuing with the SubscriptionDemo instance defined above: + >>> demo[*range(10)] + subscripted with: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + +Starred expressions may be combined with comma-separated expressions +and slices: + + >>> demo['a', 'b', *range(3), 'c'] + subscripted with: ('a', 'b', 0, 1, 2, 'c') + + +Formal subscription grammar +=========================== + + subscription: primary '[' subscript ']' + subscript: single_subscript | tuple_subscript + single_subscript: proper_slice | assignment_expression + proper_slice: [expression] ":" [expression] [ ":" [expression] ] + tuple_subscript: ','.(single_subscript | starred_expression)+ [','] + +Recall that the "|" operator denotes ordered choice. Specifically, in +"subscript", if both alternatives would match, the first +("single_subscript") has priority. ''', 'truth': r'''Truth Value Testing ******************* @@ -10090,8 +11919,11 @@ class is used in a class pattern with positional arguments, each By default, an object is considered true unless its class defines either a "__bool__()" method that returns "False" or a "__len__()" -method that returns zero, when called with the object. [1] Here are -most of the built-in objects considered false: +method that returns zero, when called with the object. [1] If one of +the methods raises an exception when called, the exception is +propagated and the object does not have a truth value (for example, +"NotImplemented"). Here are most of the built-in objects considered +false: * constants defined to be false: "None" and "False" @@ -10218,15 +12050,29 @@ class is used in a class pattern with positional arguments, each "except*" clause ================ -The "except*" clause(s) are used for handling "ExceptionGroup"s. The -exception type for matching is interpreted as in the case of "except", -but in the case of exception groups we can have partial matches when -the type matches some of the exceptions in the group. This means that -multiple "except*" clauses can execute, each handling part of the -exception group. Each clause executes at most once and handles an -exception group of all matching exceptions. Each exception in the -group is handled by at most one "except*" clause, the first that -matches it. +The "except*" clause(s) specify one or more handlers for groups of +exceptions ("BaseExceptionGroup" instances). A "try" statement can +have either "except" or "except*" clauses, but not both. The exception +type for matching is mandatory in the case of "except*", so "except*:" +is a syntax error. The type is interpreted as in the case of "except", +but matching is performed on the exceptions contained in the group +that is being handled. An "TypeError" is raised if a matching type is +a subclass of "BaseExceptionGroup", because that would have ambiguous +semantics. + +When an exception group is raised in the try block, each "except*" +clause splits (see "split()") it into the subgroups of matching and +non-matching exceptions. If the matching subgroup is not empty, it +becomes the handled exception (the value returned from +"sys.exception()") and assigned to the target of the "except*" clause +(if there is one). Then, the body of the "except*" clause executes. If +the non-matching subgroup is not empty, it is processed by the next +"except*" in the same manner. This continues until all exceptions in +the group have been matched, or the last "except*" clause has run. + +After all "except*" clauses execute, the group of unhandled exceptions +is merged with any exceptions that were raised or re-raised from +within "except*" clauses. This merged exception group propagates on.: >>> try: ... raise ExceptionGroup("eg", @@ -10239,33 +12085,27 @@ class is used in a class pattern with positional arguments, each caught with nested (TypeError(2),) caught with nested (OSError(3), OSError(4)) + Exception Group Traceback (most recent call last): - | File "", line 2, in - | ExceptionGroup: eg + | File "", line 2, in + | raise ExceptionGroup("eg", + | [ValueError(1), TypeError(2), OSError(3), OSError(4)]) + | ExceptionGroup: eg (1 sub-exception) +-+---------------- 1 ---------------- | ValueError: 1 +------------------------------------ -Any remaining exceptions that were not handled by any "except*" clause -are re-raised at the end, along with all exceptions that were raised -from within the "except*" clauses. If this list contains more than one -exception to reraise, they are combined into an exception group. - -If the raised exception is not an exception group and its type matches -one of the "except*" clauses, it is caught and wrapped by an exception -group with an empty message string. +If the exception raised from the "try" block is not an exception group +and its type matches one of the "except*" clauses, it is caught and +wrapped by an exception group with an empty message string. This +ensures that the type of the target "e" is consistently +"BaseExceptionGroup": >>> try: ... raise BlockingIOError ... except* BlockingIOError as e: ... print(repr(e)) ... - ExceptionGroup('', (BlockingIOError())) + ExceptionGroup('', (BlockingIOError(),)) -An "except*" clause must have a matching expression; it cannot be -"except*:". Furthermore, this expression cannot contain exception -group types, because that would have ambiguous semantics. - -It is not possible to mix "except" and "except*" in the same "try". "break", "continue" and "return" cannot appear in an "except*" clause. @@ -10282,11 +12122,11 @@ class is used in a class pattern with positional arguments, each ================ If "finally" is present, it specifies a ‘cleanup’ handler. The "try" -clause is executed, including any "except" and "else" clauses. If an +clause is executed, including any "except" and "else" clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The "finally" clause is executed. If there is a saved exception it is re-raised at the end of the "finally" -clause. If the "finally" clause raises another exception, the saved +clause. If the "finally" clause raises another exception, the saved exception is set as the context of the new exception. If the "finally" clause executes a "return", "break" or "continue" statement, the saved exception is discarded. For example, this function returns 42. @@ -10475,10 +12315,19 @@ def foo(): "a[-2]" equals "a[n-2]", the second to last item of sequence a with length "n". -Sequences also support slicing: "a[i:j]" selects all items with index -*k* such that *i* "<=" *k* "<" *j*. When used as an expression, a -slice is a sequence of the same type. The comment above about negative -indexes also applies to negative slice positions. +The resulting value must be a nonnegative integer less than the number +of items in the sequence. If it is not, an "IndexError" is raised. + +Sequences also support slicing: "a[start:stop]" selects all items with +index *k* such that *start* "<=" *k* "<" *stop*. When used as an +expression, a slice is a sequence of the same type. The comment above +about negative subscripts also applies to negative slice positions. +Note that no error is raised if a slice position is less than zero or +larger than the length of the sequence. + +If *start* is missing or "None", slicing behaves as if *start* was +zero. If *stop* is missing or "None", slicing behaves as if *stop* was +equal to the length of the sequence. Some sequences also support “extended slicing” with a third “step” parameter: "a[i:j:k]" selects all items of *a* with index *x* where "x @@ -10499,27 +12348,33 @@ def foo(): The following types are immutable sequences: Strings - A string is a sequence of values that represent Unicode code - points. All the code points in the range "U+0000 - U+10FFFF" can be - represented in a string. Python doesn’t have a char type; instead, - every code point in the string is represented as a string object - with length "1". The built-in function "ord()" converts a code - point from its string form to an integer in the range "0 - 10FFFF"; - "chr()" converts an integer in the range "0 - 10FFFF" to the - corresponding length "1" string object. "str.encode()" can be used - to convert a "str" to "bytes" using the given text encoding, and + A string ("str") is a sequence of values that represent + *characters*, or more formally, *Unicode code points*. All the code + points in the range "0" to "0x10FFFF" can be represented in a + string. + + Python doesn’t have a dedicated *character* type. Instead, every + code point in the string is represented as a string object with + length "1". + + The built-in function "ord()" converts a code point from its string + form to an integer in the range "0" to "0x10FFFF"; "chr()" converts + an integer in the range "0" to "0x10FFFF" to the corresponding + length "1" string object. "str.encode()" can be used to convert a + "str" to "bytes" using the given text encoding, and "bytes.decode()" can be used to achieve the opposite. Tuples - The items of a tuple are arbitrary Python objects. Tuples of two or - more items are formed by comma-separated lists of expressions. A - tuple of one item (a ‘singleton’) can be formed by affixing a comma - to an expression (an expression by itself does not create a tuple, - since parentheses must be usable for grouping of expressions). An - empty tuple can be formed by an empty pair of parentheses. + The items of a "tuple" are arbitrary Python objects. Tuples of two + or more items are formed by comma-separated lists of expressions. + A tuple of one item (a ‘singleton’) can be formed by affixing a + comma to an expression (an expression by itself does not create a + tuple, since parentheses must be usable for grouping of + expressions). An empty tuple can be formed by an empty pair of + parentheses. Bytes - A bytes object is an immutable array. The items are 8-bit bytes, + A "bytes" object is an immutable array. The items are 8-bit bytes, represented by integers in the range 0 <= x < 256. Bytes literals (like "b'abc'") and the built-in "bytes()" constructor can be used to create bytes objects. Also, bytes objects can be decoded to @@ -10648,6 +12503,10 @@ def foo(): +----------------------------------------------------+----------------------------------------------------+ | Attribute | Meaning | |====================================================|====================================================| +| function.__builtins__ | A reference to the "dictionary" that holds the | +| | function’s builtins namespace. Added in version | +| | 3.10. | ++----------------------------------------------------+----------------------------------------------------+ | function.__globals__ | A reference to the "dictionary" that holds the | | | function’s global variables – the global namespace | | | of the module in which the function was defined. | @@ -10979,7 +12838,7 @@ def foo(): "ImportWarning" when falling back to "__package__" during import resolution. - Deprecated since version 3.13, will be removed in version 3.15: + Deprecated since version 3.13, removed in version 3.15: "__package__" will cease to be set or taken into consideration by the import system or standard library. @@ -11023,41 +12882,24 @@ def foo(): module.__file__ -module.__cached__ - - "__file__" and "__cached__" are both optional attributes that may - or may not be set. Both attributes should be a "str" when they are - available. - - "__file__" indicates the pathname of the file from which the module - was loaded (if loaded from a file), or the pathname of the shared - library file for extension modules loaded dynamically from a shared - library. It might be missing for certain types of modules, such as - C modules that are statically linked into the interpreter, and the - import system may opt to leave it unset if it has no semantic - meaning (for example, a module loaded from a database). - - If "__file__" is set then the "__cached__" attribute might also be - set, which is the path to any compiled version of the code (for - example, a byte-compiled file). The file does not need to exist to - set this attribute; the path can simply point to where the compiled - file *would* exist (see **PEP 3147**). - - Note that "__cached__" may be set even if "__file__" is not set. - However, that scenario is quite atypical. Ultimately, the *loader* - is what makes use of the module spec provided by the *finder* (from - which "__file__" and "__cached__" are derived). So if a loader can - load from a cached module but otherwise does not load from a file, - that atypical scenario may be appropriate. + "__file__" is an optional attribute that may or may not be set. + Both attributes should be a "str" when they are available. - It is **strongly** recommended that you use - "module.__spec__.cached" instead of "module.__cached__". + An optional attribute, "__file__" indicates the pathname of the + file from which the module was loaded (if loaded from a file), or + the pathname of the shared library file for extension modules + loaded dynamically from a shared library. It might be missing for + certain types of modules, such as C modules that are statically + linked into the interpreter, and the import system may opt to leave + it unset if it has no semantic meaning (for example, a module + loaded from a database). - Deprecated since version 3.13, will be removed in version 3.15: - Setting "__cached__" on a module while failing to set - "__spec__.cached" is deprecated. In Python 3.15, "__cached__" will - cease to be set or taken into consideration by the import system or - standard library. + Deprecated since version 3.13, removed in version 3.15: Setting + "__cached__" on a module while failing to set "__spec__.cached" is + deprecated. In Python 3.15, "__cached__" will cease to be set or + taken into consideration by the import system or standard library. + + Changed in version 3.15: "__cached__" is no longer set. Other writable attributes on module objects @@ -11087,6 +12929,19 @@ def foo(): Added in version 3.14. +module.__lazy_modules__ + + A container (an object implementing "__contains__()") of fully + qualified module name strings. When defined at module scope, any + regular "import" statement in that module whose target module name + appears in this container is treated as a lazy import, as if the + "lazy" keyword had been used. Imports inside functions, class + bodies, or "try"/"except"/"finally" blocks are unaffected. + + See Compatibility via __lazy_modules__ for details and examples. + + Added in version 3.15. + Module dictionaries ------------------- @@ -11161,6 +13016,11 @@ class method object, it is transformed into an instance method object | | "X.__bases__" will be exactly equal to "(A, B, | | | C)". | +----------------------------------------------------+----------------------------------------------------+ +| type.__base__ | **CPython implementation detail:** The single base | +| | class in the inheritance chain that is responsible | +| | for the memory layout of instances. This attribute | +| | corresponds to "tp_base" at the C level. | ++----------------------------------------------------+----------------------------------------------------+ | type.__doc__ | The class’s documentation string, or "None" if | | | undefined. Not inherited by subclasses. | +----------------------------------------------------+----------------------------------------------------+ @@ -11168,11 +13028,20 @@ class method object, it is transformed into an instance method object | | collected during class body execution. See also: | | | "__annotations__ attributes". For best practices | | | on working with "__annotations__", please see | -| | "annotationlib". Where possible, use | +| | "annotationlib". Use | | | "annotationlib.get_annotations()" instead of | -| | accessing this attribute directly. Changed in | -| | version 3.14: Annotations are now lazily | -| | evaluated. See **PEP 649**. | +| | accessing this attribute directly. Warning: | +| | Accessing the "__annotations__" attribute directly | +| | on a class object may return annotations for the | +| | wrong class, specifically in certain cases where | +| | the class, its base class, or a metaclass is | +| | defined under "from __future__ import | +| | annotations". See **749** for details.This | +| | attribute does not exist on certain builtin | +| | classes. On user-defined classes without | +| | "__annotations__", it is an empty dictionary. | +| | Changed in version 3.14: Annotations are now | +| | lazily evaluated. See **PEP 649**. | +----------------------------------------------------+----------------------------------------------------+ | type.__annotate__() | The *annotate function* for this class, or "None" | | | if the class has no annotations. See also: | @@ -11271,11 +13140,28 @@ class instance has a namespace implemented as a dictionary which is socket objects (and perhaps by other functions or methods provided by extension modules). +File objects implement common methods, listed below, to simplify usage +in generic code. They are expected to be With Statement Context +Managers. + The objects "sys.stdin", "sys.stdout" and "sys.stderr" are initialized to file objects corresponding to the interpreter’s standard input, output and error streams; they are all open in text mode and therefore follow the interface defined by the "io.TextIOBase" abstract class. +file.read(size=-1, /) + + Retrieve up to *size* data from the file. As a convenience if + *size* is unspecified or -1 retrieve all data available. + +file.write(data, /) + + Store *data* to the file. + +file.close() + + Flush any buffers and close the underlying file. + Internal types ============== @@ -11352,12 +13238,6 @@ class instance has a namespace implemented as a dictionary which is +----------------------------------------------------+----------------------------------------------------+ | codeobject.co_firstlineno | The line number of the first line of the function | +----------------------------------------------------+----------------------------------------------------+ -| codeobject.co_lnotab | A string encoding the mapping from *bytecode* | -| | offsets to line numbers. For details, see the | -| | source code of the interpreter. Deprecated since | -| | version 3.12: This attribute of code objects is | -| | deprecated, and may be removed in Python 3.15. | -+----------------------------------------------------+----------------------------------------------------+ | codeobject.co_stacksize | The required stack size of the code object | +----------------------------------------------------+----------------------------------------------------+ | codeobject.co_flags | An "integer" encoding a number of flags for the | @@ -11510,6 +13390,10 @@ class instance has a namespace implemented as a dictionary which is | | (this is an index into the *bytecode* string of | | | the code object) | +----------------------------------------------------+----------------------------------------------------+ +| frame.f_generator | The *generator* or *coroutine* object that owns | +| | this frame, or "None" if the frame is a normal | +| | function. Added in version 3.14. | ++----------------------------------------------------+----------------------------------------------------+ Special writable attributes @@ -11621,6 +13505,10 @@ class instance has a namespace implemented as a dictionary which is Slice objects are used to represent slices for "__getitem__()" methods. They are also created by the built-in "slice()" function. +Added in version 3.15: The "slice()" type now supports subscription. +For example, "slice[float]" may be used in type annotations to +indicate a slice containing "float" objects. + Special read-only attributes: "start" is the lower bound; "stop" is the upper bound; "step" is the step value; each is "None" if omitted. These attributes can have any type. @@ -11674,13 +13562,13 @@ class instance has a namespace implemented as a dictionary which is See Function definitions for more information. ''', - 'typesmapping': r'''Mapping Types — "dict" -********************** + 'typesmapping': r'''Mapping types — "dict", "frozendict" +************************************ -A *mapping* object maps *hashable* values to arbitrary objects. -Mappings are mutable objects. There is currently only one standard -mapping type, the *dictionary*. (For other containers see the built- -in "list", "set", and "tuple" classes, and the "collections" module.) +A *mapping* object maps *hashable* values to arbitrary objects. There +are currently two standard mapping types, the *dictionary* and +"frozendict". (For other containers see the built-in "list", "set", +and "tuple" classes, and the "collections" module.) A dictionary’s keys are *almost* arbitrary values. Values that are not *hashable*, that is, values containing lists, dictionaries or @@ -11690,8 +13578,8 @@ class instance has a namespace implemented as a dictionary which is dictionary entry. class dict(**kwargs) -class dict(mapping, **kwargs) -class dict(iterable, **kwargs) +class dict(mapping, /, **kwargs) +class dict(iterable, /, **kwargs) Return a new dictionary initialized from an optional positional argument and a possibly empty set of keyword arguments. @@ -11724,8 +13612,11 @@ class dict(iterable, **kwargs) the keyword argument replaces the value from the positional argument. - To illustrate, the following examples all return a dictionary equal - to "{"one": 1, "two": 2, "three": 3}": + Dictionaries compare equal if and only if they have the same "(key, + value)" pairs (regardless of ordering). Order comparisons (‘<’, + ‘<=’, ‘>=’, ‘>’) raise "TypeError". To illustrate dictionary + creation and equality, the following examples all return a + dictionary equal to "{"one": 1, "two": 2, "three": 3}": >>> a = dict(one=1, two=2, three=3) >>> b = {'one': 1, 'two': 2, 'three': 3} @@ -11740,6 +13631,29 @@ class dict(iterable, **kwargs) keys that are valid Python identifiers. Otherwise, any valid keys can be used. + Dictionaries preserve insertion order. Note that updating a key + does not affect the order. Keys added after deletion are inserted + at the end. + + >>> d = {"one": 1, "two": 2, "three": 3, "four": 4} + >>> d + {'one': 1, 'two': 2, 'three': 3, 'four': 4} + >>> list(d) + ['one', 'two', 'three', 'four'] + >>> list(d.values()) + [1, 2, 3, 4] + >>> d["one"] = 42 + >>> d + {'one': 42, 'two': 2, 'three': 3, 'four': 4} + >>> del d["two"] + >>> d["two"] = None + >>> d + {'one': 42, 'three': 3, 'four': 4, 'two': None} + + Changed in version 3.7: Dictionary order is guaranteed to be + insertion order. This behavior was an implementation detail of + CPython from 3.6. + These are the operations that dictionaries support (and therefore, custom mapping types should support too): @@ -11777,8 +13691,8 @@ class dict(iterable, **kwargs) 1 The example above shows part of the implementation of - "collections.Counter". A different "__missing__" method is used - by "collections.defaultdict". + "collections.Counter". A different "__missing__()" method is + used by "collections.defaultdict". d[key] = value @@ -11837,7 +13751,8 @@ class dict(iterable, **kwargs) Return a new view of the dictionary’s keys. See the documentation of view objects. - pop(key[, default]) + pop(key, /) + pop(key, default, /) If *key* is in the dictionary, remove it and return its value, else return *default*. If *default* is not given and *key* is @@ -11868,10 +13783,13 @@ class dict(iterable, **kwargs) *key* with a value of *default* and return *default*. *default* defaults to "None". - update([other]) + update(**kwargs) + update(mapping, /, **kwargs) + update(iterable, /, **kwargs) - Update the dictionary with the key/value pairs from *other*, - overwriting existing keys. Return "None". + Update the dictionary with the key/value pairs from *mapping* or + *iterable* and *kwargs*, overwriting existing keys. Return + "None". "update()" accepts either another object with a "keys()" method (in which case "__getitem__()" is called with every key returned @@ -11910,33 +13828,6 @@ class dict(iterable, **kwargs) Added in version 3.9. - Dictionaries compare equal if and only if they have the same "(key, - value)" pairs (regardless of ordering). Order comparisons (‘<’, - ‘<=’, ‘>=’, ‘>’) raise "TypeError". - - Dictionaries preserve insertion order. Note that updating a key - does not affect the order. Keys added after deletion are inserted - at the end. - - >>> d = {"one": 1, "two": 2, "three": 3, "four": 4} - >>> d - {'one': 1, 'two': 2, 'three': 3, 'four': 4} - >>> list(d) - ['one', 'two', 'three', 'four'] - >>> list(d.values()) - [1, 2, 3, 4] - >>> d["one"] = 42 - >>> d - {'one': 42, 'two': 2, 'three': 3, 'four': 4} - >>> del d["two"] - >>> d["two"] = None - >>> d - {'one': 42, 'three': 3, 'four': 4, 'two': None} - - Changed in version 3.7: Dictionary order is guaranteed to be - insertion order. This behavior was an implementation detail of - CPython from 3.6. - Dictionaries and dictionary views are reversible. >>> d = {"one": 1, "two": 2, "three": 3, "four": 4} @@ -11951,10 +13842,15 @@ class dict(iterable, **kwargs) Changed in version 3.8: Dictionaries are now reversible. + See also: + + "frozendict" and "types.MappingProxyType" can be used to create a + read-only view of a "dict". + See also: - "types.MappingProxyType" can be used to create a read-only view of a - "dict". + For detailed information on thread-safety guarantees for "dict" + objects, see Thread safety for dict objects. Dictionary view objects @@ -12059,6 +13955,47 @@ class dict(iterable, **kwargs) mappingproxy({'bacon': 1, 'spam': 500}) >>> values.mapping['spam'] 500 + + +Frozen dictionaries +=================== + +class frozendict(**kwargs) +class frozendict(mapping, /, **kwargs) +class frozendict(iterable, /, **kwargs) + + Return a new frozen dictionary initialized from an optional + positional argument and a possibly empty set of keyword arguments. + + A "frozendict" has a similar API to the "dict" API, with the + following differences: + + * "dict" has more methods than "frozendict": + + * "__delitem__()" + + * "__setitem__()" + + * "clear()" + + * "pop()" + + * "popitem()" + + * "setdefault()" + + * "update()" + + * A "frozendict" can be hashed with "hash(frozendict)" if all keys + and values can be hashed. + + * "frozendict |= other" does not modify the "frozendict" in-place + but creates a new frozen dictionary. + + "frozendict" is not a "dict" subclass but inherits directly from + "object". + + Added in version 3.15. ''', 'typesmethods': r'''Methods ******* @@ -12163,7 +14100,7 @@ class dict(iterable, **kwargs) | "s * n" or "n * s" | equivalent to adding *s* to | (2)(7) | | | itself *n* times | | +----------------------------+----------------------------------+------------+ -| "s[i]" | *i*th item of *s*, origin 0 | (3) | +| "s[i]" | *i*th item of *s*, origin 0 | (3)(8) | +----------------------------+----------------------------------+------------+ | "s[i:j]" | slice of *s* from *i* to *j* | (3)(4) | +----------------------------+----------------------------------+------------+ @@ -12176,13 +14113,6 @@ class dict(iterable, **kwargs) +----------------------------+----------------------------------+------------+ | "max(s)" | largest item of *s* | | +----------------------------+----------------------------------+------------+ -| "s.index(x[, i[, j]])" | index of the first occurrence of | (8) | -| | *x* in *s* (at or after index | | -| | *i* and before index *j*) | | -+----------------------------+----------------------------------+------------+ -| "s.count(x)" | total number of occurrences of | | -| | *x* in *s* | | -+----------------------------+----------------------------------+------------+ Sequences of the same type also support comparisons. In particular, tuples and lists are compared lexicographically by comparing @@ -12240,10 +14170,17 @@ class dict(iterable, **kwargs) note that "-0" is still "0". 4. The slice of *s* from *i* to *j* is defined as the sequence of - items with index *k* such that "i <= k < j". If *i* or *j* is - greater than "len(s)", use "len(s)". If *i* is omitted or "None", - use "0". If *j* is omitted or "None", use "len(s)". If *i* is - greater than or equal to *j*, the slice is empty. + items with index *k* such that "i <= k < j". + + * If *i* is omitted or "None", use "0". + + * If *j* is omitted or "None", use "len(s)". + + * If *i* or *j* is less than "-len(s)", use "0". + + * If *i* or *j* is greater than "len(s)", use "len(s)". + + * If *i* is greater than or equal to *j*, the slice is empty. 5. The slice of *s* from *i* to *j* with step *k* is defined as the sequence of items with index "x = i + n*k" such that "0 <= n < @@ -12279,13 +14216,31 @@ class dict(iterable, **kwargs) that follow specific patterns, and hence don’t support sequence concatenation or repetition. -8. "index" raises "ValueError" when *x* is not found in *s*. Not all - implementations support passing the additional arguments *i* and - *j*. These arguments allow efficient searching of subsections of - the sequence. Passing the extra arguments is roughly equivalent to - using "s[i:j].index(x)", only without copying any data and with the - returned index being relative to the start of the sequence rather - than the start of the slice. +8. An "IndexError" is raised if *i* is outside the sequence range. + +-[ Sequence Methods ]- + +Sequence types also support the following methods: + +sequence.count(value, /) + + Return the total number of occurrences of *value* in *sequence*. + +sequence.index(value[, start[, stop]]) + + Return the index of the first occurrence of *value* in *sequence*. + + Raises "ValueError" if *value* is not found in *sequence*. + + The *start* or *stop* arguments allow for efficient searching of + subsections of the sequence, beginning at *start* and ending at + *stop*. This is roughly equivalent to "start + + sequence[start:stop].index(value)", only without copying any data. + + Caution: + + Not all sequence types support passing the *start* and *stop* + arguments. Immutable Sequence Types @@ -12321,11 +14276,15 @@ class dict(iterable, **kwargs) | "s[i] = x" | item *i* of *s* is replaced by | | | | *x* | | +--------------------------------+----------------------------------+-----------------------+ +| "del s[i]" | removes item *i* of *s* | | ++--------------------------------+----------------------------------+-----------------------+ | "s[i:j] = t" | slice of *s* from *i* to *j* is | | | | replaced by the contents of the | | | | iterable *t* | | +--------------------------------+----------------------------------+-----------------------+ -| "del s[i:j]" | same as "s[i:j] = []" | | +| "del s[i:j]" | removes the elements of "s[i:j]" | | +| | from the list (same as "s[i:j] = | | +| | []") | | +--------------------------------+----------------------------------+-----------------------+ | "s[i:j:k] = t" | the elements of "s[i:j:k]" are | (1) | | | replaced by those of *t* | | @@ -12333,63 +14292,79 @@ class dict(iterable, **kwargs) | "del s[i:j:k]" | removes the elements of | | | | "s[i:j:k]" from the list | | +--------------------------------+----------------------------------+-----------------------+ -| "s.append(x)" | appends *x* to the end of the | | -| | sequence (same as | | -| | "s[len(s):len(s)] = [x]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.clear()" | removes all items from *s* (same | (5) | -| | as "del s[:]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.copy()" | creates a shallow copy of *s* | (5) | -| | (same as "s[:]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.extend(t)" or "s += t" | extends *s* with the contents of | | +| "s += t" | extends *s* with the contents of | | | | *t* (for the most part the same | | | | as "s[len(s):len(s)] = t") | | +--------------------------------+----------------------------------+-----------------------+ -| "s *= n" | updates *s* with its contents | (6) | +| "s *= n" | updates *s* with its contents | (2) | | | repeated *n* times | | +--------------------------------+----------------------------------+-----------------------+ -| "s.insert(i, x)" | inserts *x* into *s* at the | | -| | index given by *i* (same as | | -| | "s[i:i] = [x]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.pop()" or "s.pop(i)" | retrieves the item at *i* and | (2) | -| | also removes it from *s* | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.remove(x)" | removes the first item from *s* | (3) | -| | where "s[i]" is equal to *x* | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.reverse()" | reverses the items of *s* in | (4) | -| | place | | -+--------------------------------+----------------------------------+-----------------------+ Notes: 1. If *k* is not equal to "1", *t* must have the same length as the slice it is replacing. -2. The optional argument *i* defaults to "-1", so that by default the - last item is removed and returned. +2. The value *n* is an integer, or an object implementing + "__index__()". Zero and negative values of *n* clear the sequence. + Items in the sequence are not copied; they are referenced multiple + times, as explained for "s * n" under Common Sequence Operations. -3. "remove()" raises "ValueError" when *x* is not found in *s*. +-[ Mutable Sequence Methods ]- -4. The "reverse()" method modifies the sequence in place for economy - of space when reversing a large sequence. To remind users that it - operates by side effect, it does not return the reversed sequence. +Mutable sequence types also support the following methods: -5. "clear()" and "copy()" are included for consistency with the - interfaces of mutable containers that don’t support slicing - operations (such as "dict" and "set"). "copy()" is not part of the - "collections.abc.MutableSequence" ABC, but most concrete mutable - sequence classes provide it. +sequence.append(value, /) - Added in version 3.3: "clear()" and "copy()" methods. + Append *value* to the end of the sequence. This is equivalent to + writing "seq[len(seq):len(seq)] = [value]". -6. The value *n* is an integer, or an object implementing - "__index__()". Zero and negative values of *n* clear the sequence. - Items in the sequence are not copied; they are referenced multiple - times, as explained for "s * n" under Common Sequence Operations. +sequence.clear() + + Added in version 3.3. + + Remove all items from *sequence*. This is equivalent to writing + "del sequence[:]". + +sequence.copy() + + Added in version 3.3. + + Create a shallow copy of *sequence*. This is equivalent to writing + "sequence[:]". + + Hint: + + The "copy()" method is not part of the "MutableSequence" "ABC", + but most concrete mutable sequence types provide it. + +sequence.extend(iterable, /) + + Extend *sequence* with the contents of *iterable*. For the most + part, this is the same as writing "seq[len(seq):len(seq)] = + iterable". + +sequence.insert(index, value, /) + + Insert *value* into *sequence* at the given *index*. This is + equivalent to writing "sequence[index:index] = [value]". + +sequence.pop(index=-1, /) + + Retrieve the item at *index* and also removes it from *sequence*. + By default, the last item in *sequence* is removed and returned. + +sequence.remove(value, /) + + Remove the first item from *sequence* where "sequence[i] == value". + + Raises "ValueError" if *value* is not found in *sequence*. + +sequence.reverse() + + Reverse the items of *sequence* in place. This method maintains + economy of space when reversing a large sequence. To remind users + that it operates by side-effect, it returns "None". Lists @@ -12399,7 +14374,7 @@ class dict(iterable, **kwargs) homogeneous items (where the precise degree of similarity will vary by application). -class list([iterable]) +class list(iterable=(), /) Lists may be constructed in several ways: @@ -12470,6 +14445,11 @@ class list([iterable]) empty for the duration, and raises "ValueError" if it can detect that the list has been mutated during a sort. +See also: + + For detailed information on thread-safety guarantees for "list" + objects, see Thread safety for list objects. + Tuples ====== @@ -12480,7 +14460,7 @@ class list([iterable]) of homogeneous data is needed (such as allowing storage in a "set" or "dict" instance). -class tuple([iterable]) +class tuple(iterable=(), /) Tuples may be constructed in a number of ways: @@ -12520,8 +14500,8 @@ class tuple([iterable]) The "range" type represents an immutable sequence of numbers and is commonly used for looping a specific number of times in "for" loops. -class range(stop) -class range(start, stop[, step]) +class range(stop, /) +class range(start, stop, step=1, /) The arguments to the range constructor must be integers (either built-in "int" or any object that implements the "__index__()" @@ -12649,11 +14629,15 @@ class range(start, stop[, step]) | "s[i] = x" | item *i* of *s* is replaced by | | | | *x* | | +--------------------------------+----------------------------------+-----------------------+ +| "del s[i]" | removes item *i* of *s* | | ++--------------------------------+----------------------------------+-----------------------+ | "s[i:j] = t" | slice of *s* from *i* to *j* is | | | | replaced by the contents of the | | | | iterable *t* | | +--------------------------------+----------------------------------+-----------------------+ -| "del s[i:j]" | same as "s[i:j] = []" | | +| "del s[i:j]" | removes the elements of "s[i:j]" | | +| | from the list (same as "s[i:j] = | | +| | []") | | +--------------------------------+----------------------------------+-----------------------+ | "s[i:j:k] = t" | the elements of "s[i:j:k]" are | (1) | | | replaced by those of *t* | | @@ -12661,63 +14645,79 @@ class range(start, stop[, step]) | "del s[i:j:k]" | removes the elements of | | | | "s[i:j:k]" from the list | | +--------------------------------+----------------------------------+-----------------------+ -| "s.append(x)" | appends *x* to the end of the | | -| | sequence (same as | | -| | "s[len(s):len(s)] = [x]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.clear()" | removes all items from *s* (same | (5) | -| | as "del s[:]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.copy()" | creates a shallow copy of *s* | (5) | -| | (same as "s[:]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.extend(t)" or "s += t" | extends *s* with the contents of | | +| "s += t" | extends *s* with the contents of | | | | *t* (for the most part the same | | | | as "s[len(s):len(s)] = t") | | +--------------------------------+----------------------------------+-----------------------+ -| "s *= n" | updates *s* with its contents | (6) | +| "s *= n" | updates *s* with its contents | (2) | | | repeated *n* times | | +--------------------------------+----------------------------------+-----------------------+ -| "s.insert(i, x)" | inserts *x* into *s* at the | | -| | index given by *i* (same as | | -| | "s[i:i] = [x]") | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.pop()" or "s.pop(i)" | retrieves the item at *i* and | (2) | -| | also removes it from *s* | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.remove(x)" | removes the first item from *s* | (3) | -| | where "s[i]" is equal to *x* | | -+--------------------------------+----------------------------------+-----------------------+ -| "s.reverse()" | reverses the items of *s* in | (4) | -| | place | | -+--------------------------------+----------------------------------+-----------------------+ Notes: 1. If *k* is not equal to "1", *t* must have the same length as the slice it is replacing. -2. The optional argument *i* defaults to "-1", so that by default the - last item is removed and returned. +2. The value *n* is an integer, or an object implementing + "__index__()". Zero and negative values of *n* clear the sequence. + Items in the sequence are not copied; they are referenced multiple + times, as explained for "s * n" under Common Sequence Operations. -3. "remove()" raises "ValueError" when *x* is not found in *s*. +-[ Mutable Sequence Methods ]- -4. The "reverse()" method modifies the sequence in place for economy - of space when reversing a large sequence. To remind users that it - operates by side effect, it does not return the reversed sequence. +Mutable sequence types also support the following methods: -5. "clear()" and "copy()" are included for consistency with the - interfaces of mutable containers that don’t support slicing - operations (such as "dict" and "set"). "copy()" is not part of the - "collections.abc.MutableSequence" ABC, but most concrete mutable - sequence classes provide it. +sequence.append(value, /) - Added in version 3.3: "clear()" and "copy()" methods. + Append *value* to the end of the sequence. This is equivalent to + writing "seq[len(seq):len(seq)] = [value]". -6. The value *n* is an integer, or an object implementing - "__index__()". Zero and negative values of *n* clear the sequence. - Items in the sequence are not copied; they are referenced multiple - times, as explained for "s * n" under Common Sequence Operations. +sequence.clear() + + Added in version 3.3. + + Remove all items from *sequence*. This is equivalent to writing + "del sequence[:]". + +sequence.copy() + + Added in version 3.3. + + Create a shallow copy of *sequence*. This is equivalent to writing + "sequence[:]". + + Hint: + + The "copy()" method is not part of the "MutableSequence" "ABC", + but most concrete mutable sequence types provide it. + +sequence.extend(iterable, /) + + Extend *sequence* with the contents of *iterable*. For the most + part, this is the same as writing "seq[len(seq):len(seq)] = + iterable". + +sequence.insert(index, value, /) + + Insert *value* into *sequence* at the given *index*. This is + equivalent to writing "sequence[index:index] = [value]". + +sequence.pop(index=-1, /) + + Retrieve the item at *index* and also removes it from *sequence*. + By default, the last item in *sequence* is removed and returned. + +sequence.remove(value, /) + + Remove the first item from *sequence* where "sequence[i] == value". + + Raises "ValueError" if *value* is not found in *sequence*. + +sequence.reverse() + + Reverse the items of *sequence* in place. This method maintains + economy of space when reversing a large sequence. To remind users + that it operates by side-effect, it returns "None". ''', 'unary': r'''Unary arithmetic and bitwise operations *************************************** @@ -12820,9 +14820,9 @@ class range(start, stop[, step]) is semantically equivalent to: manager = (EXPRESSION) - enter = type(manager).__enter__ - exit = type(manager).__exit__ - value = enter(manager) + enter = manager.__enter__ + exit = manager.__exit__ + value = enter() hit_except = False try: @@ -12830,11 +14830,14 @@ class range(start, stop[, step]) SUITE except: hit_except = True - if not exit(manager, *sys.exc_info()): + if not exit(*sys.exc_info()): raise finally: if not hit_except: - exit(manager, None, None, None) + exit(None, None, None) + +except that implicit special method lookup is used for "__enter__()" +and "__exit__()". With more than one item, the context managers are processed as if multiple "with" statements were nested: diff --git a/Lib/queue.py b/Lib/queue.py index c90de8edc76c34..c0b359876543f7 100644 --- a/Lib/queue.py +++ b/Lib/queue.py @@ -236,9 +236,11 @@ def shutdown(self, immediate=False): By default, gets will only raise once the queue is empty. Set 'immediate' to True to make gets raise immediately instead. - All blocked callers of put() and get() will be unblocked. If - 'immediate', callers of join() are unblocked regardless of - the number of unfinished tasks. + All blocked callers of put() and get() will be unblocked. + + If 'immediate', the queue is drained and unfinished tasks + is reduced by the number of drained tasks. If unfinished tasks + is reduced to zero, callers of Queue.join are unblocked. ''' with self.mutex: self.is_shutdown = True diff --git a/Lib/random.py b/Lib/random.py index c89cbb755abac8..4541267bab866a 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -836,7 +836,11 @@ def binomialvariate(self, n=1, p=0.5): if not c: return x while True: - y += _floor(_log2(random()) / c) + 1 + try: + y += _floor(_log2(random()) / c) + 1 + except ValueError: + # Reject case where random() returned 0.0 + continue if y > n: return x x += 1 @@ -1012,26 +1016,26 @@ def _test(N=10_000): def _parse_args(arg_list: list[str] | None): import argparse parser = argparse.ArgumentParser( - formatter_class=argparse.RawTextHelpFormatter, color=True) + formatter_class=argparse.RawTextHelpFormatter) group = parser.add_mutually_exclusive_group() group.add_argument( "-c", "--choice", nargs="+", help="print a random choice") group.add_argument( "-i", "--integer", type=int, metavar="N", - help="print a random integer between 1 and N inclusive") + help="print a random integer between 1 and `N` inclusive") group.add_argument( "-f", "--float", type=float, metavar="N", - help="print a random floating-point number between 0 and N inclusive") + help="print a random floating-point number between 0 and `N` inclusive") group.add_argument( "--test", type=int, const=10_000, nargs="?", help=argparse.SUPPRESS) parser.add_argument("input", nargs="*", help="""\ if no options given, output depends on the input - string or multiple: same as --choice - integer: same as --integer - float: same as --float""") + string or multiple: same as `--choice` + integer: same as `--integer` + float: same as `--float`""") args = parser.parse_args(arg_list) return args, parser.format_help() diff --git a/Lib/re/__init__.py b/Lib/re/__init__.py index af2808a77da691..e6d29fd7a403b6 100644 --- a/Lib/re/__init__.py +++ b/Lib/re/__init__.py @@ -85,17 +85,18 @@ \\ Matches a literal backslash. This module exports the following functions: - match Match a regular expression pattern to the beginning of a string. - fullmatch Match a regular expression pattern to all of a string. - search Search a string for the presence of a pattern. - sub Substitute occurrences of a pattern found in a string. - subn Same as sub, but also return the number of substitutions made. - split Split a string by the occurrences of a pattern. - findall Find all occurrences of a pattern in a string. - finditer Return an iterator yielding a Match object for each match. - compile Compile a pattern into a Pattern object. - purge Clear the regular expression cache. - escape Backslash all non-alphanumerics in a string. + prefixmatch Match a regular expression pattern to the beginning of a str. + match The original name of prefixmatch prior to 3.15. + fullmatch Match a regular expression pattern to all of a string. + search Search a string for the presence of a pattern. + sub Substitute occurrences of a pattern found in a string. + subn Same as sub, but also return the number of substitutions made. + split Split a string by the occurrences of a pattern. + findall Find all occurrences of a pattern in a string. + finditer Return an iterator yielding a Match object for each match. + compile Compile a pattern into a Pattern object. + purge Clear the regular expression cache. + escape Backslash all non-alphanumerics in a string. Each function other than purge and escape can take an optional 'flags' argument consisting of one or more of the following module constants, joined by "|". @@ -130,15 +131,13 @@ # public symbols __all__ = [ - "match", "fullmatch", "search", "sub", "subn", "split", + "prefixmatch", "match", "fullmatch", "search", "sub", "subn", "split", "findall", "finditer", "compile", "purge", "escape", "error", "Pattern", "Match", "A", "I", "L", "M", "S", "X", "U", "ASCII", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE", "UNICODE", "NOFLAG", "RegexFlag", "PatternError" ] -__version__ = "2.2.1" - @enum.global_enum @enum._simple_enum(enum.IntFlag, boundary=enum.KEEP) class RegexFlag: @@ -161,10 +160,13 @@ class RegexFlag: # -------------------------------------------------------------------- # public interface -def match(pattern, string, flags=0): +def prefixmatch(pattern, string, flags=0): """Try to apply the pattern at the start of the string, returning a Match object, or None if no match was found.""" - return _compile(pattern, flags).match(string) + return _compile(pattern, flags).prefixmatch(string) + +# Our original name which was less explicitly clear about the behavior for prefixmatch. +match = prefixmatch def fullmatch(pattern, string, flags=0): """Try to apply the pattern to all of the string, returning @@ -313,7 +315,7 @@ def escape(pattern): return pattern.translate(_special_chars_map).encode('latin1') Pattern = type(_compiler.compile('', 0)) -Match = type(_compiler.compile('', 0).match('')) +Match = type(_compiler.compile('', 0).prefixmatch('')) # -------------------------------------------------------------------- # internals @@ -399,9 +401,12 @@ def __init__(self, lexicon, flags=0): s = _parser.State() s.flags = flags for phrase, action in lexicon: + sub_pattern = _parser.parse(phrase, flags) + if sub_pattern.state.groups != 1: + raise ValueError("Cannot use capturing groups in re.Scanner") gid = s.opengroup() p.append(_parser.SubPattern(s, [ - (SUBPATTERN, (gid, 0, 0, _parser.parse(phrase, flags))), + (SUBPATTERN, (gid, 0, 0, sub_pattern)), ])) s.closegroup(gid, p[-1]) p = _parser.SubPattern(s, [(BRANCH, (None, p))]) @@ -409,10 +414,10 @@ def __init__(self, lexicon, flags=0): def scan(self, string): result = [] append = result.append - match = self.scanner.scanner(string).match + _match = self.scanner.scanner(string).prefixmatch i = 0 while True: - m = match() + m = _match() if not m: break j = m.end() @@ -426,3 +431,12 @@ def scan(self, string): append(action) i = j return result, string[i:] + + +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "2.2.1" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/re/_compiler.py b/Lib/re/_compiler.py index 20dd561d1c1520..c2ca8e25abe34d 100644 --- a/Lib/re/_compiler.py +++ b/Lib/re/_compiler.py @@ -375,7 +375,7 @@ def _optimize_charset(charset, iscased=None, fixup=None, fixes=None): # less significant byte is a bit index in the chunk (just like the # CHARSET matching). - charmap = bytes(charmap) # should be hashable + charmap = charmap.take_bytes() # should be hashable comps = {} mapping = bytearray(256) block = 0 diff --git a/Lib/re/_parser.py b/Lib/re/_parser.py index 35ab7ede2a75a9..bd189fe0695f80 100644 --- a/Lib/re/_parser.py +++ b/Lib/re/_parser.py @@ -455,7 +455,6 @@ def _parse_sub(source, state, verbose, nested): items = [] itemsappend = items.append sourcematch = source.match - start = source.tell() while True: itemsappend(_parse(source, state, verbose, nested + 1, not nested and not items)) diff --git a/Lib/rlcompleter.py b/Lib/rlcompleter.py index 23eb0020f42e8a..26fcda612567ea 100644 --- a/Lib/rlcompleter.py +++ b/Lib/rlcompleter.py @@ -36,9 +36,13 @@ import re import __main__ import warnings +import types __all__ = ["Completer"] +# Sentinel object to distinguish "missing" from "present but None" +_MISSING = sentinel("MISSING") + class Completer: def __init__(self, namespace = None): """Create a new completer for the command line. @@ -178,19 +182,29 @@ def attr_matches(self, text): if (word[:n] == attr and not (noprefix and word[:n+1] == noprefix)): match = "%s.%s" % (expr, word) - if isinstance(getattr(type(thisobject), word, None), - property): - # bpo-44752: thisobject.word is a method decorated by - # `@property`. What follows applies a postfix if - # thisobject.word is callable, but know we know that - # this is not callable (because it is a property). - # Also, getattr(thisobject, word) will evaluate the - # property method, which is not desirable. + + class_attr = getattr(type(thisobject), word, None) + if isinstance( + class_attr, + (property, types.GetSetDescriptorType, types.MemberDescriptorType) + ) or (hasattr(class_attr, '__get__') and not callable(class_attr)): + # Avoid evaluating descriptors, which could run + # arbitrary code or raise exceptions. matches.append(match) continue - if (value := getattr(thisobject, word, None)) is not None: - matches.append(self._callable_postfix(value, match)) + + if (isinstance(thisobject, types.ModuleType) + and + isinstance(thisobject.__dict__.get(word), + types.LazyImportType) + ): + value = thisobject.__dict__.get(word) else: + value = getattr(thisobject, word, _MISSING) + + if value is not _MISSING: + matches.append(self._callable_postfix(value, match)) + elif word in getattr(type(thisobject), '__slots__', ()): matches.append(match) if matches or not noprefix: break diff --git a/Lib/runpy.py b/Lib/runpy.py index ef54d3282eee06..a535b4f651a5ae 100644 --- a/Lib/runpy.py +++ b/Lib/runpy.py @@ -80,7 +80,6 @@ def _run_code(code, run_globals, init_globals=None, pkg_name = mod_spec.parent run_globals.update(__name__ = mod_name, __file__ = fname, - __cached__ = cached, __doc__ = None, __loader__ = loader, __package__ = pkg_name, @@ -103,8 +102,10 @@ def _run_module_code(code, init_globals=None, # Helper to get the full name, spec and code for a module def _get_module_details(mod_name, error=ImportError): + # name= is only accepted by ImportError and its subclasses. + kwargs = {"name": mod_name} if issubclass(error, ImportError) else {} if mod_name.startswith("."): - raise error("Relative module names not supported") + raise error("Relative module names not supported", **kwargs) pkg_name, _, _ = mod_name.rpartition(".") if pkg_name: # Try importing the parent to avoid catching initialization errors @@ -137,12 +138,13 @@ def _get_module_details(mod_name, error=ImportError): if mod_name.endswith(".py"): msg += (f". Try using '{mod_name[:-3]}' instead of " f"'{mod_name}' as the module name.") - raise error(msg.format(mod_name, type(ex).__name__, ex)) from ex + raise error(msg.format(mod_name, type(ex).__name__, ex), + **kwargs) from ex if spec is None: - raise error("No module named %s" % mod_name) + raise error("No module named %s" % mod_name, **kwargs) if spec.submodule_search_locations is not None: if mod_name == "__main__" or mod_name.endswith(".__main__"): - raise error("Cannot use package as __main__ module") + raise error("Cannot use package as __main__ module", **kwargs) try: pkg_main_name = mod_name + ".__main__" return _get_module_details(pkg_main_name, error) @@ -150,17 +152,19 @@ def _get_module_details(mod_name, error=ImportError): if mod_name not in sys.modules: raise # No module loaded; being a package is irrelevant raise error(("%s; %r is a package and cannot " + - "be directly executed") %(e, mod_name)) + "be directly executed") %(e, mod_name), + **kwargs) loader = spec.loader if loader is None: raise error("%r is a namespace package and cannot be executed" - % mod_name) + % mod_name, + **kwargs) try: code = loader.get_code(mod_name) except ImportError as e: - raise error(format(e)) from e + raise error(format(e), **kwargs) from e if code is None: - raise error("No code object available for %s" % mod_name) + raise error("No code object available for %s" % mod_name, **kwargs) return mod_name, spec, code class _Error(Exception): @@ -180,7 +184,6 @@ def _run_module_as_main(mod_name, alter_argv=True): At the very least, these variables in __main__ will be overwritten: __name__ __file__ - __cached__ __loader__ __package__ """ @@ -234,6 +237,7 @@ def _get_main_module_details(error=ImportError): # Also moves the standard __main__ out of the way so that the # preexisting __loader__ entry doesn't cause issues main_name = "__main__" + kwargs = {"name": main_name} if issubclass(error, ImportError) else {} saved_main = sys.modules[main_name] del sys.modules[main_name] try: @@ -241,13 +245,14 @@ def _get_main_module_details(error=ImportError): except ImportError as exc: if main_name in str(exc): raise error("can't find %r module in %r" % - (main_name, sys.path[0])) from exc + (main_name, sys.path[0]), + **kwargs) from exc raise finally: sys.modules[main_name] = saved_main -def _get_code_from_file(fname): +def _get_code_from_file(fname, module): # Check for a compiled file first from pkgutil import read_code code_path = os.path.abspath(fname) @@ -256,7 +261,7 @@ def _get_code_from_file(fname): if code is None: # That didn't work, so try it as normal source code with io.open_code(code_path) as f: - code = compile(f.read(), fname, 'exec') + code = compile(f.read(), fname, 'exec', module=module) return code def run_path(path_name, init_globals=None, run_name=None): @@ -283,7 +288,7 @@ def run_path(path_name, init_globals=None, run_name=None): if isinstance(importer, type(None)): # Not a valid sys.path entry, so run the code directly # execfile() doesn't help as we want to allow compiled files - code = _get_code_from_file(path_name) + code = _get_code_from_file(path_name, run_name) return _run_module_code(code, init_globals, run_name, pkg_name=pkg_name, script_name=path_name) else: diff --git a/Lib/shelve.py b/Lib/shelve.py index 1010be1e09d702..9f6296667fdb6b 100644 --- a/Lib/shelve.py +++ b/Lib/shelve.py @@ -57,7 +57,6 @@ """ from pickle import DEFAULT_PROTOCOL, dumps, loads -from io import BytesIO import collections.abc diff --git a/Lib/shlex.py b/Lib/shlex.py index 5bf6e0d70e0012..c7ffc918d53961 100644 --- a/Lib/shlex.py +++ b/Lib/shlex.py @@ -317,17 +317,26 @@ def join(split_command): return ' '.join(quote(arg) for arg in split_command) -def quote(s): - """Return a shell-escaped version of the string *s*.""" +def quote(s, *, force=False): + """Return a shell-escaped version of the string *s*. + + If *force* is *True*, then *s* is unconditionally quoted, + even if it is already safe for a shell without being quoted. + """ if not s: return "''" + if not isinstance(s, str): + raise TypeError(f"expected string object, got {type(s).__name__!r}") + # Use bytes.translate() for performance safe_chars = (b'%+,-./0123456789:=@' b'ABCDEFGHIJKLMNOPQRSTUVWXYZ_' b'abcdefghijklmnopqrstuvwxyz') - # No quoting is needed if `s` is an ASCII string consisting only of `safe_chars` - if s.isascii() and not s.encode().translate(None, delete=safe_chars): + # No quoting is needed if we are not forcing quoting + # and `s` is an ASCII string consisting only of `safe_chars`. + if (not force + and s.isascii() and not s.encode().translate(None, delete=safe_chars)): return s # use single quotes, and put single quotes into double quotes diff --git a/Lib/shutil.py b/Lib/shutil.py index 8d8fe145567822..5095318da23314 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -885,10 +885,14 @@ def move(src, dst, copy_function=copy2): If dst already exists but is not a directory, it may be overwritten depending on os.rename() semantics. - If the destination is on our current filesystem, then rename() is used. - Otherwise, src is copied to the destination and then removed. Symlinks are - recreated under the new name if os.rename() fails because of cross - filesystem renames. + os.rename() is preferably used if the source and destination are on the + same filesystem. In case os.rename() fails due to OSError (e.g. the user + has write permission to *dst* file but not to its parent directory), + this method falls back to using *copy_function* silently. + Symlinks are also recreated under the new name if os.rename() fails + because of cross filesystem renames. + + It's recommended to use os.rename() if atomic move is strictly required. The optional `copy_function` argument is a callable that will be used to copy the source or it will be delegated to `copytree`. @@ -940,8 +944,8 @@ def move(src, dst, copy_function=copy2): return real_dst def _destinsrc(src, dst): - src = os.path.abspath(src) - dst = os.path.abspath(dst) + src = os.path.realpath(src) + dst = os.path.realpath(dst) if not src.endswith(os.path.sep): src += os.path.sep if not dst.endswith(os.path.sep): @@ -1212,19 +1216,22 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, for arg, val in format_info[1]: kwargs[arg] = val + base_name = os.fspath(base_name) + if base_dir is None: base_dir = os.curdir + else: + base_dir = os.fspath(base_dir) supports_root_dir = getattr(func, 'supports_root_dir', False) save_cwd = None if root_dir is not None: + root_dir = os.fspath(root_dir) stmd = os.stat(root_dir).st_mode if not stat.S_ISDIR(stmd): raise NotADirectoryError(errno.ENOTDIR, 'Not a directory', root_dir) if supports_root_dir: - # Support path-like base_name here for backwards-compatibility. - base_name = os.fspath(base_name) kwargs['root_dir'] = root_dir else: save_cwd = os.getcwd() @@ -1300,12 +1307,6 @@ def unregister_unpack_format(name): """Removes the pack format from the registry.""" del _UNPACK_FORMATS[name] -def _ensure_directory(path): - """Ensure that the parent directory of `path` exists""" - dirname = os.path.dirname(path) - if not os.path.isdir(dirname): - os.makedirs(dirname) - def _unpack_zipfile(filename, extract_dir): """Unpack zip `filename` to `extract_dir` """ @@ -1314,27 +1315,9 @@ def _unpack_zipfile(filename, extract_dir): if not zipfile.is_zipfile(filename): raise ReadError("%s is not a zip file" % filename) - zip = zipfile.ZipFile(filename) - try: - for info in zip.infolist(): - name = info.filename - - # don't extract absolute paths or ones with .. in them - if name.startswith('/') or '..' in name: - continue - - targetpath = os.path.join(extract_dir, *name.split('/')) - if not targetpath: - continue - - _ensure_directory(targetpath) - if not name.endswith('/'): - # file - with zip.open(name, 'r') as source, \ - open(targetpath, 'wb') as target: - copyfileobj(source, target) - finally: - zip.close() + with zipfile.ZipFile(filename) as zip: + zip._ignore_invalid_names = True + zip.extractall(extract_dir) def _unpack_tarfile(filename, extract_dir, *, filter=None): """Unpack tar/tar.gz/tar.bz2/tar.xz/tar.zst `filename` to `extract_dir` @@ -1653,15 +1636,3 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None): if _access_check(name, mode): return name return None - -def __getattr__(name): - if name == "ExecError": - import warnings - warnings._deprecated( - "shutil.ExecError", - f"{warnings._DEPRECATED_MSG}; it " - "isn't raised by any shutil function.", - remove=(3, 16) - ) - return RuntimeError - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/signal.py b/Lib/signal.py index c8cd3d4f597ca5..6387e5c35dbaba 100644 --- a/Lib/signal.py +++ b/Lib/signal.py @@ -15,7 +15,7 @@ 'Handlers', __name__, lambda name: name in ('SIG_DFL', 'SIG_IGN')) -if 'pthread_sigmask' in _globals: +if 'SIG_BLOCK' in _globals: _IntEnum._convert_( 'Sigmasks', __name__, lambda name: name in ('SIG_BLOCK', 'SIG_UNBLOCK', 'SIG_SETMASK')) diff --git a/Lib/site.py b/Lib/site.py index f93271971594d8..b7f5c7f0246bc1 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -18,55 +18,26 @@ it is also checked for site-packages (sys.base_prefix and sys.base_exec_prefix will always be the "real" prefixes of the Python installation). If "pyvenv.cfg" (a bootstrap configuration file) contains -the key "include-system-site-packages" set to anything other than "false" -(case-insensitive), the system-level prefixes will still also be -searched for site-packages; otherwise they won't. - -All of the resulting site-specific directories, if they exist, are -appended to sys.path, and also inspected for path configuration -files. - -A path configuration file is a file whose name has the form -.pth; its contents are additional directories (one per line) -to be added to sys.path. Non-existing directories (or -non-directories) are never added to sys.path; no directory is added to -sys.path more than once. Blank lines and lines beginning with -'#' are skipped. Lines starting with 'import' are executed. - -For example, suppose sys.prefix and sys.exec_prefix are set to -/usr/local and there is a directory /usr/local/lib/python2.5/site-packages -with three subdirectories, foo, bar and spam, and two path -configuration files, foo.pth and bar.pth. Assume foo.pth contains the -following: - - # foo package configuration - foo - bar - bletch - -and bar.pth contains: - - # bar package configuration - bar - -Then the following directories are added to sys.path, in this order: - - /usr/local/lib/python2.5/site-packages/bar - /usr/local/lib/python2.5/site-packages/foo - -Note that bletch is omitted because it doesn't exist; bar precedes foo -because bar.pth comes alphabetically before foo.pth; and spam is -omitted because it is not mentioned in either path configuration file. - -The readline module is also automatically configured to enable -completion for systems that support it. This can be overridden in -sitecustomize, usercustomize or PYTHONSTARTUP. Starting Python in -isolated mode (-I) disables automatic readline configuration. - -After these operations, an attempt is made to import a module -named sitecustomize, which can perform arbitrary additional -site-specific customizations. If this import fails with an -ImportError exception, it is silently ignored. +the key "include-system-site-packages" set to "true" (case-insensitive), +the system-level prefixes will still also be searched for site-packages; +otherwise they won't. + +Two kinds of configuration files are processed in each site-packages +directory: + +- .pth files extend sys.path with additional directories (one per + line). Lines starting with "import" are deprecated (see PEP 829). + +- .start files specify startup entry points using the pkg.mod:callable + syntax. These are resolved via pkgutil.resolve_name() and called with no + arguments. + +When called from main(), all .pth path extensions are applied before any +.start entry points are executed, ensuring that paths are available before +startup code runs. + +See the documentation for the site module for full details: +https://docs.python.org/3/library/site.html """ import sys @@ -77,6 +48,11 @@ import stat import errno +lazy import locale +lazy import pkgutil +lazy import traceback +lazy import warnings + # Prefixes for site-packages; add additional prefixes like /usr/local here PREFIXES = [sys.prefix, sys.exec_prefix] # Enable per user site-packages directory @@ -90,17 +66,34 @@ USER_BASE = None -def _trace(message): +def _trace(message, exc=None): if sys.flags.verbose: - print(message, file=sys.stderr) + _print_error(message, exc) -def _warn(*args, **kwargs): - import warnings +def _print_error(message, exc=None): + """Print an error message to stderr, optionally with a formatted traceback.""" + print(message, file=sys.stderr) + if exc is not None: + for record in traceback.format_exception(exc): + for line in record.splitlines(): + print(' ' + line, file=sys.stderr) + +def _warn(*args, **kwargs): warnings.warn(*args, **kwargs) +def _warn_future_us(message, remove): + # Don't call warnings._deprecated() directly because we're lazily importing warnings and don't + # want to have to trigger an eager import if it's not necessary. Startup time matters a lot + # here and warnings isn't cheap! This inlines the check from + # warnings._py_warnings._deprecated(). + _version = sys.version_info + if (_version[:2] > remove) or (_version[:2] == remove and _version[3] != "alpha"): + warnings._deprecated(message, remove=remove) + + def makepath(*paths): dir = os.path.join(*paths) try: @@ -111,7 +104,7 @@ def makepath(*paths): def abs_paths(): - """Set all module __file__ and __cached__ attributes to an absolute path""" + """Set __file__ to an absolute path.""" for m in set(sys.modules.values()): loader_module = None try: @@ -127,10 +120,6 @@ def abs_paths(): m.__file__ = os.path.abspath(m.__file__) except (AttributeError, OSError, TypeError): pass - try: - m.__cached__ = os.path.abspath(m.__cached__) - except (AttributeError, OSError, TypeError): - pass def removeduppaths(): @@ -165,95 +154,450 @@ def _init_pathinfo(): return d -def addpackage(sitedir, name, known_paths): - """Process a .pth file within the site-packages directory: - For each line in the file, either combine it with sitedir to a path - and add that to known_paths, or execute it if it starts with 'import '. +# PEP 829 implementation notes. +# +# Startup information (.pth and .start file information) can be processed in +# implicit or explicit batches. Implicit batches are self-contained +# site.addsitedir() calls: they create a per-call StartupState, populate it +# from the site directory's .pth and .start files, run process() on it, and +# then throw the state away. +# +# main() needs different semantics: it accumulates state across multiple +# StartupState.addsitedir() calls (user-site plus all global site-packages) so +# that every sys.path extension is visible *before* any startup code (.start +# entry points and .pth import lines) runs. Callers can opt into the same +# behavior by creating a StartupState directly and calling its addsitedir(), +# addusersitepackages(), and addsitepackages() methods, then invoking +# process() once at the end of the batch. +# +# Here's the CRITICAL reentrancy invariant: recursive site.addsitedir() calls +# reached from a .start entry point or an exec'd .pth import line must not +# mutate the StartupState currently being processed. Reentrant calls reach +# the module-level site.addsitedir() shim, which always builds a fresh +# per-call state. + + +def _read_pthstart_file(sitedir, name, suffix): + """Parse a .start or .pth file and return (lines, filename). + + On success, ``lines`` is a (possibly empty) list of the file's lines. + On failure (file missing, hidden, unreadable, or .start with bad + encoding), ``lines`` is ``None`` so callers can distinguish a + successfully-read empty file from one that could not be read. """ - if known_paths is None: - known_paths = _init_pathinfo() - reset = True - else: - reset = False - fullname = os.path.join(sitedir, name) + filename = os.path.join(sitedir, name) + _trace(f"Reading startup configuration file: {filename}") + try: - st = os.lstat(fullname) - except OSError: - return + st = os.lstat(filename) + except OSError as exc: + _trace(f"Cannot stat {filename!r}", exc) + return None, filename + if ((getattr(st, 'st_flags', 0) & stat.UF_HIDDEN) or (getattr(st, 'st_file_attributes', 0) & stat.FILE_ATTRIBUTE_HIDDEN)): - _trace(f"Skipping hidden .pth file: {fullname!r}") - return - _trace(f"Processing .pth file: {fullname!r}") + _trace(f"Skipping hidden {suffix} file: {filename!r}") + return None, filename + + _trace(f"Processing {suffix} file: {filename!r}") try: - with io.open_code(fullname) as f: - pth_content = f.read() - except OSError: - return + with io.open_code(filename) as f: + raw_content = f.read() + except OSError as exc: + _trace(f"Cannot read {filename!r}", exc) + return None, filename try: - # Accept BOM markers in .pth files as we do in source files - # (Windows PowerShell 5.1 makes it hard to emit UTF-8 files without a BOM) - pth_content = pth_content.decode("utf-8-sig") + # Accept BOM markers in .start and .pth files as we do in source files + # (Windows PowerShell 5.1 makes it hard to emit UTF-8 files without a BOM). + content = raw_content.decode("utf-8-sig") except UnicodeDecodeError: - # Fallback to locale encoding for backward compatibility. - # We will deprecate this fallback in the future. - import locale - pth_content = pth_content.decode(locale.getencoding()) - _trace(f"Cannot read {fullname!r} as UTF-8. " - f"Using fallback encoding {locale.getencoding()!r}") - - for n, line in enumerate(pth_content.splitlines(), 1): - if line.startswith("#"): - continue - if line.strip() == "": - continue + _trace(f"Cannot read {filename!r} as UTF-8.") + # For .pth files only, and then only until Python 3.20, fall back to + # locale encoding for backward compatibility. + _warn_future_us( + ".pth files decoded to locale encoding as a fallback", + remove=(3, 20) + ) + if suffix == ".pth": + content = raw_content.decode(locale.getencoding()) + _trace(f"Using fallback encoding {locale.getencoding()!r}") + else: + return None, filename + + return content.splitlines(), filename + + +class StartupState: + """Per-batch accumulator for .pth and .start file processing. + + A StartupState collects sys.path extensions, deprecated .pth import lines, + and .start entry points read from one or more site-packages directories. + Calling process() applies them in PEP 829 order: paths are added to + sys.path first, then import lines from .pth files (skipping any with a + matching .start), then entry points from .start files. + + State lives entirely on the instance; there is no module-level pending + state. This is what makes the module reentrancy-safe: a site.addsitedir() + call reached recursively from an exec'd import line or a .start entry + point operates on a different StartupState than the one being processed by + the outer call. + + The internal data is intentionally private. The lower-level write + methods (_record_sitedir(), _read_pth_file(), _read_start_file()) are + private to the site module; the public surface is addsitedir(), + addusersitepackages(), addsitepackages(), and process(). + """ + __slots__ = ( + '_known_paths', + '_processed_sitedirs', + '_path_entries', + '_importexecs', + '_entrypoints', + ) + + def __init__(self, known_paths=None): + """Create an independent startup state. + + *known_paths* is a set of case-normalized paths already present + on sys.path, used to avoid duplicate path entries. When None + (the default), it is initialized from the current sys.path. + + A caller-supplied set is stored by reference and mutated in place + as new paths are recorded; pass a fresh set per StartupState if + isolation across instances is required. + """ + self._known_paths = ( + _init_pathinfo() + if known_paths is None + else known_paths) + self._processed_sitedirs = set() + # The sys.path append ledger. This is a list of 2-tuples of the form + # (pthfile, path) where `pthfile` is the .pth file which is extending + # the path, and `path` is the directory to add to sys.path. Note that + # to preserve the interleaving semantics (i.e. .pth file paths are + # added after the sitedir in which the .pth file is found), `path` + # could be a sitedir, in which case `pthfile` will always be None. + self._path_entries = [] + # Both dicts map "" -> list + # of items collected from that file. Mapping by filename lets us + # cross-reference a .pth and its matching .start (PEP 829 import + # suppression rule) and lets _print_error report the source file + # when an entry fails. + self._importexecs = {} + self._entrypoints = {} + + def addsitedir(self, sitedir): + """Add a site directory and accumulate its .pth and .start startup data. + + Read the .pth and .start files in *sitedir* and record their + sys.path extensions, deprecated .pth import lines, and .start entry + points on this state. The recorded data is not applied until + process() is called. + + Typically used to batch multiple site directories before a single + process() call, so that every sys.path extension is visible before + any startup code runs. Reentrant calls reached from a .start entry + point or an exec'd .pth import line must not mutate the state + currently being processed; for those cases, use site.addsitedir() + instead, which always creates a fresh per-call state. + """ + self._addsitedir(sitedir, process_known_sitedirs=True) + + def addusersitepackages(self): + """Add the per-user site-packages directory, if enabled. + + The user site directory is added only when user site-packages are + enabled and the directory exists. Its startup data is accumulated + for later processing by process(). + """ + _trace("Processing user site-packages") + user_site = getusersitepackages() + if ENABLE_USER_SITE and os.path.isdir(user_site): + self.addsitedir(user_site) + + def addsitepackages(self, prefixes=None): + """Add global site-packages directories, if they exist. + + Site-packages directories are computed from *prefixes*, or from the + global PREFIXES when *prefixes* is None. Each directory's startup + data is accumulated for later processing by process(). + """ + _trace("Processing global site-packages") + for sitedir in getsitepackages(prefixes): + if os.path.isdir(sitedir): + self.addsitedir(sitedir) + + def _addsitedir(self, sitedir, *, process_known_sitedirs): + """Internal addsitedir() implementation with full dedup control. + + The public addsitedir() always uses process_known_sitedirs=True + (gh-149819 semantics). The module-level legacy known_paths shim + uses process_known_sitedirs=False to preserve 3.14 idempotency + (gh-75723). + """ + sitedir = self._record_sitedir( + sitedir, process_known_sitedirs=process_known_sitedirs) + if sitedir is None: + return try: + names = os.listdir(sitedir) + except OSError: + return + + # The following phases are defined by PEP 829. + # Phases 1-3: Read .pth files, accumulating paths and import lines. + pth_names = sorted( + name for name in names + if name.endswith(".pth") and not name.startswith(".") + ) + for name in pth_names: + self._read_pth_file(sitedir, name) + + # Phases 6-7: Discover .start files and accumulate their entry points. + # Import lines from .pth files with a matching .start file are + # discarded at flush time by _exec_imports(). + start_names = sorted( + name for name in names + if name.endswith(".start") and not name.startswith(".") + ) + for name in start_names: + self._read_start_file(sitedir, name) + + def _record_sitedir(self, sitedir, *, process_known_sitedirs=True): + sitedir, sitedircase = makepath(sitedir) + # Have we already processed this sitedir? + if sitedircase in self._processed_sitedirs: + return None + # In legacy known_paths mode, a known sitedir means its startup files + # were already processed by an earlier addsitedir() call, so skip it + # to preserve idempotency (gh-75723). In explicit StartupState mode, + # known_paths only tracks sys.path entries; a sitedir may already be + # on sys.path (for example from $PYTHONPATH, gh-149819) but still need + # its .pth and .start files processed once. The separate + # _processed_sitedirs set is what lets explicit batches distinguish + # "already on sys.path" from "startup files already read". + if not process_known_sitedirs and sitedircase in self._known_paths: + return None + # Record that we've processed this sitedir. + self._processed_sitedirs.add(sitedircase) + if sitedircase not in self._known_paths: + self._known_paths.add(sitedircase) + # Add the sitedir to the sys.path extension ledger. There is no + # .pth file to record. + self._path_entries.append((None, sitedir)) + return sitedir + + def _read_pth_file(self, sitedir, name): + """Parse a .pth file, accumulating sys.path extensions and import lines. + + Errors on individual lines do not abort processing of the rest of + the file (PEP 829). Per-batch deduplication is done against + self._known_paths: any path already in it is skipped, and newly + accepted paths are added to it so that subsequent .pth files in + the same batch don't add them more than once. + """ + lines, filename = _read_pthstart_file(sitedir, name, ".pth") + if lines is None: + return + + for n, line in enumerate(lines, 1): + line = line.strip() + if not line or line.startswith("#"): + continue + + # In Python 3.18 and 3.19, `import` lines are silently + # ignored. In Python 3.20 and beyond, issue a warning when + # `import` lines in .pth files are detected. if line.startswith(("import ", "import\t")): - exec(line) + _warn_future_us( + "import lines in .pth files are silently ignored", + remove=(3, 18), + ) + _warn_future_us( + "import lines in .pth files are noisily ignored", + remove=(3, 20), + ) + self._importexecs.setdefault(filename, []).append(line) continue - line = line.rstrip() - dir, dircase = makepath(sitedir, line) - if dircase not in known_paths and os.path.exists(dir): - sys.path.append(dir) - known_paths.add(dircase) - except Exception as exc: - print(f"Error processing line {n:d} of {fullname}:\n", - file=sys.stderr) - import traceback - for record in traceback.format_exception(exc): - for line in record.splitlines(): - print(' '+line, file=sys.stderr) - print("\nRemainder of file ignored", file=sys.stderr) - break - if reset: - known_paths = None - return known_paths + try: + dir_, dircase = makepath(sitedir, line) + except Exception as exc: + _trace(f"Error in {filename!r}, line {n:d}: {line!r}", exc) + continue -def addsitedir(sitedir, known_paths=None): - """Add 'sitedir' argument to sys.path if missing and handle .pth files in - 'sitedir'""" - _trace(f"Adding directory: {sitedir!r}") + # PEP 829 dedup: skip paths already seen in this batch. + if dircase in self._known_paths: + _trace( + f"In {filename!r}, line {n:d}: " + f"skipping duplicate sys.path entry: {dir_}" + ) + else: + # Add this directory to the sys.path extension ledger, while + # also recording the .pth file it was found in. + self._path_entries.append((filename, dir_)) + self._known_paths.add(dircase) + + def _read_start_file(self, sitedir, name): + """Parse a .start file for a list of entry point strings.""" + lines, filename = _read_pthstart_file(sitedir, name, ".start") + if lines is None: + return + + # PEP 829: the *presence* of a matching .start file disables `import` + # line processing in the matched .pth file, regardless of whether this + # .start file contains any entry points. Register the filename as a + # key now so an empty (or comment-only) .start file still suppresses. + entrypoints = self._entrypoints.setdefault(filename, []) + + for n, line in enumerate(lines, 1): + line = line.strip() + if not line or line.startswith("#"): + continue + # Syntax validation is deferred to entry point execution + # time, where pkgutil.resolve_name(strict=True) enforces the + # pkg.mod:callable form. + entrypoints.append(line) + + def process(self): + """Apply accumulated state in PEP 829 order. + + Phase order matters: all .pth path extensions are applied to + sys.path *before* any import line or .start entry point runs, so + that an entry point may live in a module reachable only via a + .pth-extended path. + """ + self._extend_syspath() + self._exec_imports() + self._execute_start_entrypoints() + + def _extend_syspath(self): + # Duplicate path-extension specifications have already been filtered + # out upstream across .pth files within this batch (via known_paths), + # and ledger entries are already abspath/normpath'd. .pth-derived + # entries (filename is not None) are existence-checked and skipped + # with an error if missing. Sitedir entries (filename is None) are + # appended unconditionally: legacy addsitedir() added the sitedir to + # sys.path before attempting to list it, so an unreadable or + # non-existent sitedir still landed on sys.path. Deferring the + # append to here preserves that contract. + for filename, dir_ in self._path_entries: + # As a backstop, known_paths may not have been seeded from sys.path + # (callers can pass an empty set), and multiple StartupState + # instances against the same sys.path don't share state, so always + # do a final anti-duplication check. + if dir_ in sys.path: + continue + if filename is None or os.path.exists(dir_): + if filename is not None: + _trace(f"Extending sys.path with {dir_} from {filename}") + sys.path.append(dir_) + else: + _print_error( + f"In {filename}: {dir_} does not exist; " + f"skipping sys.path append" + ) + + def _exec_imports(self): + # For each `import` line we've seen in a .pth file, exec() it in + # order, unless the .pth has a matching .start file in this same + # batch. In that case, PEP 829 says the import lines are + # suppressed in favor of the .start's entry points. + for filename, imports in self._importexecs.items(): + # Given "/path/to/foo.pth", check whether "/path/to/foo.start" was + # registered in this same batch. + name, dot, pth = filename.rpartition(".") + assert dot == "." and pth == "pth", ( + f"Bad startup filename: {filename}" + ) + if f"{name}.start" in self._entrypoints: + _trace( + f"import lines in {filename} are suppressed " + f"due to matching {name}.start file." + ) + continue + + _trace( + f"import lines in {filename} are deprecated, " + f"use entry points in a {name}.start file instead." + ) + for line in imports: + try: + _trace(f"Exec'ing from {filename}: {line}") + exec(line) + except Exception as exc: + _print_error( + f"Error in import line from {filename}: {line}", + exc, + ) + + def _execute_start_entrypoints(self): + # Resolve each entry point string to a callable via + # pkgutil.resolve_name(strict=True), which both validates the + # required pkg.mod:callable form and performs the import in one + # step, then call it with no arguments. + for filename, entrypoints in self._entrypoints.items(): + for entrypoint in entrypoints: + try: + _trace( + f"Executing entry point: {entrypoint} from {filename}" + ) + callable_ = pkgutil.resolve_name(entrypoint, strict=True) + except ValueError as exc: + _print_error( + f"Invalid entry point syntax in {filename}: " + f"{entrypoint!r}", + exc, + ) + except Exception as exc: + _print_error( + f"Error resolving entry point {entrypoint} " + f"from {filename}", + exc, + ) + else: + try: + callable_() + except Exception as exc: + _print_error( + f"Error in entry point {entrypoint} from {filename}", + exc, + ) + + +def addpackage(sitedir, name, known_paths): + """Process a .pth file within the site-packages directory.""" if known_paths is None: known_paths = _init_pathinfo() reset = True else: reset = False - sitedir, sitedircase = makepath(sitedir) - if not sitedircase in known_paths: - sys.path.append(sitedir) # Add path component - known_paths.add(sitedircase) - try: - names = os.listdir(sitedir) - except OSError: - return - names = [name for name in names - if name.endswith(".pth") and not name.startswith(".")] - for name in sorted(names): - addpackage(sitedir, name, known_paths) - if reset: - known_paths = None + + state = StartupState(known_paths) + state._read_pth_file(sitedir, name) + state.process() + + return None if reset else known_paths + + +def addsitedir(sitedir, known_paths=None): + """Add a site directory and process its startup files. + + For batched processing across multiple site directories, build a + StartupState explicitly and call StartupState.addsitedir() on it; that + defers .pth/.start processing until a single StartupState.process() call. + """ + _trace(f"Adding directory: {sitedir!r}") + if known_paths is None: + state = StartupState(_init_pathinfo()) + state.addsitedir(sitedir) + else: + # Preserve gh-75723 idempotency for legacy known_paths mode: a + # sitedir already present in known_paths is skipped, not reprocessed. + state = StartupState(known_paths) + state._addsitedir(sitedir, process_known_sitedirs=False) + state.process() return known_paths @@ -333,7 +677,7 @@ def _get_path(userbase): if sys.platform == 'darwin' and sys._framework: return f'{userbase}/lib/{implementation_lower}/site-packages' - return f'{userbase}/lib/python{version[0]}.{version[1]}{abi_thread}/site-packages' + return f'{userbase}/lib/{implementation_lower}{version[0]}.{version[1]}{abi_thread}/site-packages' def getuserbase(): @@ -366,21 +710,20 @@ def getusersitepackages(): return USER_SITE + def addusersitepackages(known_paths): - """Add a per user site-package to sys.path + """Add the per-user site-packages directory, if enabled. - Each user has its own python directory with site-packages in the - home directory. + The user site directory is added only when user site-packages are enabled + and the directory exists. Return *known_paths*, updated with any paths + added by addsitedir(). """ - # get the per user site-package path - # this call will also make sure USER_BASE and USER_SITE are set - _trace("Processing user site-packages") - user_site = getusersitepackages() - - if ENABLE_USER_SITE and os.path.isdir(user_site): - addsitedir(user_site, known_paths) + state = StartupState(known_paths) + state.addusersitepackages() + state.process() return known_paths + def getsitepackages(prefixes=None): """Returns a list containing all global site-packages directories. @@ -420,15 +763,20 @@ def getsitepackages(prefixes=None): sitepackages.append(os.path.join(prefix, "Lib", "site-packages")) return sitepackages + def addsitepackages(known_paths, prefixes=None): - """Add site-packages to sys.path""" - _trace("Processing global site-packages") - for sitedir in getsitepackages(prefixes): - if os.path.isdir(sitedir): - addsitedir(sitedir, known_paths) + """Add global site-packages directories, if they exist. + Site-packages directories are computed from *prefixes*, or from the global + prefixes when *prefixes* is None. Return *known_paths*, updated with any + paths added by addsitedir(). + """ + state = StartupState(known_paths) + state.addsitepackages(prefixes) + state.process() return known_paths + def setquit(): """Define new builtins 'quit' and 'exit'. @@ -449,9 +797,9 @@ def setcopyright(): """Set 'copyright' and 'credits' in builtins""" builtins.copyright = _sitebuiltins._Printer("copyright", sys.copyright) builtins.credits = _sitebuiltins._Printer("credits", """\ - Thanks to CWI, CNRI, BeOpen, Zope Corporation, the Python Software - Foundation, and a cast of thousands for supporting Python - development. See www.python.org for more information.""") +Thanks to CWI, CNRI, BeOpen, Zope Corporation, the Python Software +Foundation, and a cast of thousands for supporting Python +development. See www.python.org for more information.""") files, dirs = [], [] # Not all modules are required to have a __file__ attribute. See # PEP 420 for more details. @@ -531,6 +879,8 @@ def register_readline(): import _pyrepl.unix_console console_errors = _pyrepl.unix_console._error from _pyrepl.main import CAN_USE_PYREPL + except ModuleNotFoundError: + CAN_USE_PYREPL = False finally: sys.path = original_path except ImportError: @@ -593,6 +943,15 @@ def write_history(): def venv(known_paths): + """Process pyvenv.cfg and add the venv site-packages, if applicable.""" + state = StartupState(known_paths) + _venv(state) + state.process() + return known_paths + + +def _venv(state): + """State-driven implementation of venv(); used by main() for batching.""" global PREFIXES, ENABLE_USER_SITE env = os.environ @@ -632,20 +991,22 @@ def venv(known_paths): sys._home = value if sys.prefix != site_prefix: - _warn(f'Unexpected value in sys.prefix, expected {site_prefix}, got {sys.prefix}', RuntimeWarning) + _warn( + f'Unexpected value in sys.prefix, expected {site_prefix}, got {sys.prefix}', + RuntimeWarning) if sys.exec_prefix != site_prefix: - _warn(f'Unexpected value in sys.exec_prefix, expected {site_prefix}, got {sys.exec_prefix}', RuntimeWarning) + _warn( + f'Unexpected value in sys.exec_prefix, expected {site_prefix}, got {sys.exec_prefix}', + RuntimeWarning) - # Doing this here ensures venv takes precedence over user-site - addsitepackages(known_paths, [sys.prefix]) + # Doing this here ensures venv takes precedence over user-site. + state.addsitepackages([sys.prefix]) if system_site == "true": PREFIXES += [sys.base_prefix, sys.base_exec_prefix] else: ENABLE_USER_SITE = False - return known_paths - def execsitecustomize(): """Run custom site specific code, if available.""" @@ -696,17 +1057,25 @@ def main(): global ENABLE_USER_SITE orig_path = sys.path[:] - known_paths = removeduppaths() + removeduppaths() if orig_path != sys.path: # removeduppaths() might make sys.path absolute. - # fix __file__ and __cached__ of already imported modules too. + # Fix __file__ of already imported modules too. abs_paths() - known_paths = venv(known_paths) + state = StartupState(set()) + _venv(state) + if ENABLE_USER_SITE is None: ENABLE_USER_SITE = check_enableusersite() - known_paths = addusersitepackages(known_paths) - known_paths = addsitepackages(known_paths) + + state.addusersitepackages() + state.addsitepackages() + # PEP 829: flush accumulated data from all .pth and .start files. + # Paths are extended first, then deprecated import lines are exec'd, + # and finally .start entry points are executed — ensuring sys.path is + # fully populated before any startup code runs. + state.process() setquit() setcopyright() sethelper() diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 84d6d858e7dec1..4cfc2338d99c67 100644 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -177,6 +177,15 @@ def _quote_periods(bindata): def _fix_eols(data): return re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data) + +try: + hmac.digest(b'', b'', 'md5') +except ValueError: + _have_cram_md5_support = False +else: + _have_cram_md5_support = True + + try: import ssl except ImportError: @@ -242,7 +251,6 @@ def __init__(self, host='', port=0, local_hostname=None, will be used. """ - self._host = host self.timeout = timeout self.esmtp_features = {} self.command_encoding = 'ascii' @@ -333,6 +341,7 @@ def connect(self, host='localhost', port=0, source_address=None): port = int(port) except ValueError: raise OSError("nonnumeric port") + self._host = host if not port: port = self.default_port sys.audit("smtplib.connect", self, host, port) @@ -665,8 +674,11 @@ def auth_cram_md5(self, challenge=None): # CRAM-MD5 does not support initial-response. if challenge is None: return None - return self.user + " " + hmac.HMAC( - self.password.encode('ascii'), challenge, 'md5').hexdigest() + if not _have_cram_md5_support: + raise SMTPException("CRAM-MD5 is not supported") + password = self.password.encode('ascii') + authcode = hmac.HMAC(password, challenge, 'md5') + return f"{self.user} {authcode.hexdigest()}" def auth_plain(self, challenge=None): """ Authobject to use with PLAIN authentication. Requires self.user and @@ -718,8 +730,10 @@ def login(self, user, password, *, initial_response_ok=True): advertised_authlist = self.esmtp_features["auth"].split() # Authentication methods we can handle in our preferred order: - preferred_auths = ['CRAM-MD5', 'PLAIN', 'LOGIN'] - + if _have_cram_md5_support: + preferred_auths = ['CRAM-MD5', 'PLAIN', 'LOGIN'] + else: + preferred_auths = ['PLAIN', 'LOGIN'] # We try the supported authentications in our preferred order, if # the server supports them. authlist = [auth for auth in preferred_auths @@ -903,7 +917,7 @@ def send_message(self, msg, from_addr=None, to_addrs=None, The arguments are as for sendmail, except that msg is an email.message.Message object. If from_addr is None or to_addrs is None, these arguments are taken from the headers of the Message as - described in RFC 2822 (a ValueError is raised if there is more than + described in RFC 5322 (a ValueError is raised if there is more than one set of 'Resent-' headers). Regardless of the values of from_addr and to_addr, any Bcc field (or Resent-Bcc field, when the Message is a resent) of the Message object won't be transmitted. The Message @@ -917,7 +931,7 @@ def send_message(self, msg, from_addr=None, to_addrs=None, policy. """ - # 'Resent-Date' is a mandatory field if the Message is resent (RFC 2822 + # 'Resent-Date' is a mandatory field if the Message is resent (RFC 5322 # Section 3.6.6). In such a case, we use the 'Resent-*' fields. However, # if there is more than one 'Resent-' block there's no way to # unambiguously determine which one is the most recent in all cases, @@ -936,7 +950,7 @@ def send_message(self, msg, from_addr=None, to_addrs=None, else: raise ValueError("message has more than one 'Resent-' header block") if from_addr is None: - # Prefer the sender field per RFC 2822:3.6.2. + # Prefer the sender field per RFC 5322 section 3.6.2. from_addr = (msg[header_prefix + 'Sender'] if (header_prefix + 'Sender') in msg else msg[header_prefix + 'From']) diff --git a/Lib/socket.py b/Lib/socket.py index 3073c012b19877..03c3fe88f15cfe 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -123,7 +123,7 @@ def _intenum_converter(value, enum_klass): 10004: "The operation was interrupted.", 10009: "A bad file handle was passed.", 10013: "Permission denied.", - 10014: "A fault occurred on the network??", + 10014: "An invalid pointer was passed.", 10022: "An invalid operation was attempted.", 10024: "Too many open files.", 10035: "The socket operation would block.", @@ -181,7 +181,7 @@ def _intenum_converter(value, enum_klass): 11001: "Host not found.", 11002: "Nonauthoritative host not found.", 11003: "This is a nonrecoverable error.", - 11004: "Valid name, no data record requested type.", + 11004: "Valid name, no data record of requested type.", 11005: "QoS receivers.", 11006: "QoS senders.", 11007: "No QoS senders.", @@ -197,7 +197,7 @@ def _intenum_converter(value, enum_klass): 11017: "QoS flowspec error.", 11018: "Invalid QoS provider buffer.", 11019: "Invalid QoS filter style.", - 11020: "Invalid QoS filter style.", + 11020: "Invalid QoS filter type.", 11021: "Incorrect QoS filter count.", 11022: "Invalid QoS object length.", 11023: "Incorrect QoS flow count.", @@ -649,18 +649,22 @@ def _fallback_socketpair(family=AF_INET, type=SOCK_STREAM, proto=0): # Authenticating avoids using a connection from something else # able to connect to {host}:{port} instead of us. # We expect only AF_INET and AF_INET6 families. - try: - if ( - ssock.getsockname() != csock.getpeername() - or csock.getsockname() != ssock.getpeername() - ): - raise ConnectionError("Unexpected peer connection") - except: - # getsockname() and getpeername() can fail - # if either socket isn't connected. - ssock.close() - csock.close() - raise + # + # Note that we skip this on WASI because on that platorm the client socket + # may not have finished connecting by the time we've reached this point (gh-146139). + if sys.platform != "wasi": + try: + if ( + ssock.getsockname() != csock.getpeername() + or csock.getsockname() != ssock.getpeername() + ): + raise ConnectionError("Unexpected peer connection") + except: + # getsockname() and getpeername() can fail + # if either socket isn't connected. + ssock.close() + csock.close() + raise return (ssock, csock) diff --git a/Lib/socketserver.py b/Lib/socketserver.py index 93b0a23be27f68..ec389457ef5b1c 100644 --- a/Lib/socketserver.py +++ b/Lib/socketserver.py @@ -120,9 +120,6 @@ class will essentially render the service "deaf" while one request is # Author of the BaseServer patch: Luke Kenneth Casson Leighton -__version__ = "0.4" - - import socket import selectors import os @@ -861,3 +858,12 @@ def setup(self): def finish(self): self.socket.sendto(self.wfile.getvalue(), self.client_address) + + +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "0.4" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/sqlite3/__main__.py b/Lib/sqlite3/__main__.py index 35344ecceff526..ec72c694390717 100644 --- a/Lib/sqlite3/__main__.py +++ b/Lib/sqlite3/__main__.py @@ -60,6 +60,7 @@ def runsource(self, source, filename="", symbol="single"): if not source or source.isspace(): return False + # Remember to update CLI_COMMANDS in _completer.py if source[0] == ".": match source[1:].strip(): case "version": @@ -86,14 +87,11 @@ def runsource(self, source, filename="", symbol="single"): def main(*args): - parser = ArgumentParser( - description="Python sqlite3 CLI", - color=True, - ) + parser = ArgumentParser(description="Python sqlite3 CLI") parser.add_argument( "filename", type=str, default=":memory:", nargs="?", help=( - "SQLite database to open (defaults to ':memory:'). " + "SQLite database to open (defaults to `:memory:`). " "A new database is created if the file does not previously exist." ), ) @@ -101,7 +99,7 @@ def main(*args): "sql", type=str, nargs="?", help=( "An SQL query to execute. " - "Any returned rows are printed to stdout." + "Any returned rows are printed to `stdout`." ), ) parser.add_argument( @@ -132,8 +130,11 @@ def main(*args): theme = get_theme() s = theme.syntax - sys.ps1 = f"{s.prompt}sqlite> {s.reset}" - sys.ps2 = f"{s.prompt} ... {s.reset}" + # Use RL_PROMPT_START_IGNORE (\001) and RL_PROMPT_END_IGNORE (\002) to + # bracket non-printing characters. This tells readline to ignore them + # when calculating screen space for redisplay during history scrolling. + sys.ps1 = f"\001{s.prompt}\002sqlite> \001{s.reset}\002" + sys.ps2 = f"\001{s.prompt}\002 ... \001{s.reset}\002" con = sqlite3.connect(args.filename, isolation_level=None) try: @@ -142,7 +143,7 @@ def main(*args): execute(con, args.sql, suppress_errors=False, theme=theme) else: # No SQL provided; start the REPL. - with completer(): + with completer(con): console = SqliteInteractiveConsole(con, use_color=True) console.interact(banner, exitmsg="") finally: diff --git a/Lib/sqlite3/_completer.py b/Lib/sqlite3/_completer.py index f21ef69cad6439..ba580f968bf92d 100644 --- a/Lib/sqlite3/_completer.py +++ b/Lib/sqlite3/_completer.py @@ -1,3 +1,4 @@ +from _sqlite3 import OperationalError from contextlib import contextmanager try: @@ -5,23 +6,89 @@ except ImportError: SQLITE_KEYWORDS = () +CLI_COMMANDS = ('.quit', '.help', '.version') + _completion_matches = [] -def _complete(text, state): +def _complete(con, text, state): global _completion_matches if state == 0: - text_upper = text.upper() - _completion_matches = [c for c in SQLITE_KEYWORDS if c.startswith(text_upper)] + if text.startswith("."): + _completion_matches = [ + c + " " for c in CLI_COMMANDS if c.startswith(text) + ] + else: + text_upper = text.upper() + _completion_matches = [ + c + " " for c in SQLITE_KEYWORDS if c.startswith(text_upper) + ] + + cursor = con.cursor() + schemata = tuple(row[1] for row + in cursor.execute("PRAGMA database_list")) + # tables, indexes, triggers, and views + # escape '_' which can appear in attached database names + select_clauses = ( + f"""\ + SELECT name || ' ' FROM \"{schema}\".sqlite_master + WHERE name LIKE REPLACE(:text, '_', '^_') || '%' ESCAPE '^'""" + for schema in schemata + ) + _completion_matches.extend( + row[0] + for row in cursor.execute( + " UNION ".join(select_clauses), {"text": text} + ) + ) + # columns + try: + select_clauses = ( + f"""\ + SELECT pti.name || ' ' FROM "{schema}".sqlite_master AS sm + JOIN pragma_table_xinfo(sm.name,'{schema}') AS pti + WHERE sm.type='table' AND + pti.name LIKE REPLACE(:text, '_', '^_') || '%' ESCAPE '^'""" + for schema in schemata + ) + _completion_matches.extend( + row[0] + for row in cursor.execute( + " UNION ".join(select_clauses), {"text": text} + ) + ) + except OperationalError: + # skip on SQLite<3.16.0 where pragma table-valued function is + # not supported yet + pass + # functions + try: + _completion_matches.extend( + row[0] for row in cursor.execute("""\ + SELECT DISTINCT UPPER(name) || '(' + FROM pragma_function_list() + WHERE name NOT IN ('->', '->>') AND + name LIKE REPLACE(:text, '_', '^_') || '%' ESCAPE '^'""", + {"text": text}, + ) + ) + except OperationalError: + # skip on SQLite<3.30.0 where function_list is not supported yet + pass + # schemata + text_lower = text.lower() + _completion_matches.extend(c for c in schemata + if c.lower().startswith(text_lower)) + _completion_matches = sorted(set(_completion_matches)) try: - return _completion_matches[state] + " " + return _completion_matches[state] except IndexError: return None @contextmanager -def completer(): +def completer(con): try: import readline except ImportError: @@ -29,8 +96,10 @@ def completer(): return old_completer = readline.get_completer() + def complete(text, state): + return _complete(con, text, state) try: - readline.set_completer(_complete) + readline.set_completer(complete) if readline.backend == "editline": # libedit uses "^I" instead of "tab" command_string = "bind ^I rl_complete" diff --git a/Lib/ssl.py b/Lib/ssl.py index 86fb8990636692..3c0361330d7e95 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -13,6 +13,9 @@ Functions: + get_sigalgs -- return a list of all available TLS signature + algorithms (requires OpenSSL 3.4 or later) + cert_time_to_seconds -- convert time string used for certificate notBefore and notAfter functions to integer seconds past the Epoch (the time values @@ -107,11 +110,7 @@ ) from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj from _ssl import RAND_status, RAND_add, RAND_bytes -try: - from _ssl import RAND_egd -except ImportError: - # RAND_egd is not supported on some platforms - pass +from _ssl import get_sigalgs from _ssl import ( @@ -151,7 +150,8 @@ source=_ssl) PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_TLS -_PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()} +_PROTOCOL_NAMES = frozendict({ + value: name for name, value in _SSLMethod.__members__.items()}) _SSLv2_IF_EXISTS = getattr(_SSLMethod, 'PROTOCOL_SSLv2', None) @@ -186,7 +186,7 @@ class _TLSContentType: class _TLSAlertType: """Alert types for TLSContentType.ALERT messages - See RFC 8466, section B.2 + See RFC 8446, section B.2 """ CLOSE_NOTIFY = 0 UNEXPECTED_MESSAGE = 10 @@ -721,10 +721,9 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None, # root CA certificates for the given purpose. This may fail silently. context.load_default_certs(purpose) # OpenSSL 1.1.1 keylog file - if hasattr(context, 'keylog_filename'): - keylogfile = os.environ.get('SSLKEYLOGFILE') - if keylogfile and not sys.flags.ignore_environment: - context.keylog_filename = keylogfile + keylogfile = os.environ.get('SSLKEYLOGFILE') + if keylogfile and not sys.flags.ignore_environment: + context.keylog_filename = keylogfile return context def _create_unverified_context(protocol=None, *, cert_reqs=CERT_NONE, @@ -775,10 +774,9 @@ def _create_unverified_context(protocol=None, *, cert_reqs=CERT_NONE, # root CA certificates for the given purpose. This may fail silently. context.load_default_certs(purpose) # OpenSSL 1.1.1 keylog file - if hasattr(context, 'keylog_filename'): - keylogfile = os.environ.get('SSLKEYLOGFILE') - if keylogfile and not sys.flags.ignore_environment: - context.keylog_filename = keylogfile + keylogfile = os.environ.get('SSLKEYLOGFILE') + if keylogfile and not sys.flags.ignore_environment: + context.keylog_filename = keylogfile return context # Used by http.client if no context is explicitly passed. @@ -931,6 +929,18 @@ def cipher(self): ssl_version, secret_bits)``.""" return self._sslobj.cipher() + def group(self): + """Return the currently selected key agreement group name.""" + return self._sslobj.group() + + def client_sigalg(self): + """Return the selected client authentication signature algorithm.""" + return self._sslobj.client_sigalg() + + def server_sigalg(self): + """Return the selected server handshake signature algorithm.""" + return self._sslobj.server_sigalg() + def shared_ciphers(self): """Return a list of ciphers shared by the client during the handshake or None if this is not a valid server connection. @@ -1043,7 +1053,12 @@ def _create(cls, sock, server_side=False, do_handshake_on_connect=True, notconn_pre_handshake_data = self.recv(1) except OSError as e: # EINVAL occurs for recv(1) on non-connected on unix sockets. - if e.errno not in (errno.ENOTCONN, errno.EINVAL): + if e.errno in (errno.ENOTCONN, errno.EINVAL): + pass + elif sys.platform == 'cygwin' and e.errno == errno.EAGAIN: + # EAGAIN occurs on Cygwin. + pass + else: raise notconn_pre_handshake_data = b'' self.setblocking(blocking) @@ -1210,6 +1225,30 @@ def cipher(self): else: return self._sslobj.cipher() + @_sslcopydoc + def group(self): + self._checkClosed() + if self._sslobj is None: + return None + else: + return self._sslobj.group() + + @_sslcopydoc + def client_sigalg(self): + self._checkClosed() + if self._sslobj is None: + return None + else: + return self._sslobj.client_sigalg() + + @_sslcopydoc + def server_sigalg(self): + self._checkClosed() + if self._sslobj is None: + return None + else: + return self._sslobj.server_sigalg() + @_sslcopydoc def shared_ciphers(self): self._checkClosed() @@ -1499,11 +1538,8 @@ def DER_cert_to_PEM_cert(der_cert_bytes): """Takes a certificate in binary DER format and returns the PEM version of it as a string.""" - f = str(base64.standard_b64encode(der_cert_bytes), 'ASCII', 'strict') - ss = [PEM_HEADER] - ss += [f[i:i+64] for i in range(0, len(f), 64)] - ss.append(PEM_FOOTER + '\n') - return '\n'.join(ss) + f = str(base64.b64encode(der_cert_bytes, wrapcol=64), 'ASCII') + return f'{PEM_HEADER}\n{f}\n{PEM_FOOTER}\n' def PEM_cert_to_DER_cert(pem_cert_string): """Takes a certificate in ASCII PEM format and returns the diff --git a/Lib/stat.py b/Lib/stat.py index 1b4ed1ebc940ef..214c7917b5e048 100644 --- a/Lib/stat.py +++ b/Lib/stat.py @@ -166,9 +166,14 @@ def filemode(mode): perm = [] for index, table in enumerate(_filemode_table): for bit, char in table: - if mode & bit == bit: - perm.append(char) - break + if index == 0: + if S_IFMT(mode) == bit: + perm.append(char) + break + else: + if mode & bit == bit: + perm.append(char) + break else: if index == 0: # Unknown filetype @@ -200,6 +205,21 @@ def filemode(mode): FILE_ATTRIBUTE_VIRTUAL = 65536 +# Linux STATX_ATTR constants for interpreting os.statx()'s +# "stx_attributes" and "stx_attributes_mask" members + +STATX_ATTR_COMPRESSED = 0x00000004 +STATX_ATTR_IMMUTABLE = 0x00000010 +STATX_ATTR_APPEND = 0x00000020 +STATX_ATTR_NODUMP = 0x00000040 +STATX_ATTR_ENCRYPTED = 0x00000800 +STATX_ATTR_AUTOMOUNT = 0x00001000 +STATX_ATTR_MOUNT_ROOT = 0x00002000 +STATX_ATTR_VERITY = 0x00100000 +STATX_ATTR_DAX = 0x00200000 +STATX_ATTR_WRITE_ATOMIC = 0x00400000 + + # If available, use C implementation try: from _stat import * diff --git a/Lib/statistics.py b/Lib/statistics.py index 3d805cb073987d..01ca6c51dafcaf 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -136,7 +136,7 @@ from fractions import Fraction from decimal import Decimal -from itertools import count, groupby, repeat +from itertools import compress, count, groupby, repeat from bisect import bisect_left, bisect_right from math import hypot, sqrt, fabs, exp, erfc, tau, log, fsum, sumprod from math import isfinite, isinf, pi, cos, sin, tan, cosh, asin, atan, acos @@ -145,6 +145,7 @@ from collections import Counter, namedtuple, defaultdict _SQRT2 = sqrt(2.0) +_SQRT2PI = sqrt(tau) _random = random ## Exceptions ############################################################## @@ -194,9 +195,9 @@ def fmean(data, weights=None): n = len(data) except TypeError: # Handle iterators that do not define __len__(). - counter = count() - total = fsum(map(itemgetter(0), zip(data, counter))) - n = next(counter) + counter = count(1) + total = fsum(compress(data, counter)) + n = next(counter) - 1 else: total = fsum(data) @@ -247,7 +248,7 @@ def count_positive(iterable): elif x == 0.0: found_zero = True else: - raise StatisticsError('No negative inputs allowed', x) + raise StatisticsError(f'No negative inputs allowed: {x!r}') total = fsum(map(log, count_positive(data))) @@ -619,9 +620,14 @@ def stdev(data, xbar=None): if n < 2: raise StatisticsError('stdev requires at least two data points') mss = ss / (n - 1) + try: + mss_numerator = mss.numerator + mss_denominator = mss.denominator + except AttributeError: + raise ValueError('inf or nan encountered in data') if issubclass(T, Decimal): - return _decimal_sqrt_of_frac(mss.numerator, mss.denominator) - return _float_sqrt_of_frac(mss.numerator, mss.denominator) + return _decimal_sqrt_of_frac(mss_numerator, mss_denominator) + return _float_sqrt_of_frac(mss_numerator, mss_denominator) def pstdev(data, mu=None): @@ -637,9 +643,14 @@ def pstdev(data, mu=None): if n < 1: raise StatisticsError('pstdev requires at least one data point') mss = ss / n + try: + mss_numerator = mss.numerator + mss_denominator = mss.denominator + except AttributeError: + raise ValueError('inf or nan encountered in data') if issubclass(T, Decimal): - return _decimal_sqrt_of_frac(mss.numerator, mss.denominator) - return _float_sqrt_of_frac(mss.numerator, mss.denominator) + return _decimal_sqrt_of_frac(mss_numerator, mss_denominator) + return _float_sqrt_of_frac(mss_numerator, mss_denominator) ## Statistics for relations between two inputs ############################# @@ -1247,11 +1258,11 @@ def samples(self, n, *, seed=None): def pdf(self, x): "Probability density function. P(x <= X < x+dx) / dx" - variance = self._sigma * self._sigma - if not variance: + sigma = self._sigma + if not sigma: raise StatisticsError('pdf() not defined when sigma is zero') - diff = x - self._mu - return exp(diff * diff / (-2.0 * variance)) / sqrt(tau * variance) + z = (x - self._mu) / sigma + return exp(-0.5 * z * z) / (_SQRT2PI * sigma) def cdf(self, x): "Cumulative distribution function. P(X <= x)" diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 79251bd5310223..38b655f2f7b9d2 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -250,6 +250,82 @@ def __repr__(self): else: _PopenSelector = selectors.SelectSelector + def _communicate_io_posix(selector, stdin, input_view, input_offset, + output_buffers, endtime, *, close_on_eof=False): + """ + Low-level POSIX I/O multiplexing loop used by Popen._communicate. + + Handles the select loop for reading/writing but does not manage + stream lifecycle or raise timeout exceptions. + + Args: + selector: A _PopenSelector with streams already registered + stdin: Writable file object for input, or None + input_view: memoryview of input bytes, or None + input_offset: Starting offset into input_view (for resume support) + output_buffers: Dict {file_object: list} to append read chunks to + endtime: Deadline timestamp, or None for no timeout + close_on_eof: If True, close output streams immediately when they + EOF rather than leaving them open for the caller to close. + Used by Popen._communicate() to match its historical behavior + of releasing fds as soon as the child closes the corresponding + pipe. + + Returns: + (new_input_offset, completed) + - new_input_offset: How many bytes of input were written + - completed: True if all I/O finished, False if timed out + + Note: + - Closes output streams on EOF only if close_on_eof=True + - Does NOT raise TimeoutExpired (caller handles) + - Appends to output_buffers lists in place + """ + stdin_fd = stdin.fileno() if stdin else None + + while selector.get_map(): + remaining = _deadline_remaining(endtime) + if remaining is not None and remaining <= 0: + return (input_offset, False) # Timed out + + ready = selector.select(remaining) + + # Check timeout after select (may have woken spuriously) + if endtime is not None and _time() > endtime: + return (input_offset, False) # Timed out + + for key, events in ready: + if key.fd == stdin_fd: + chunk = input_view[input_offset:input_offset + _PIPE_BUF] + try: + input_offset += os.write(key.fd, chunk) + except BrokenPipeError: + selector.unregister(key.fd) + try: + stdin.close() + except BrokenPipeError: + pass + else: + if input_offset >= len(input_view): + selector.unregister(key.fd) + try: + stdin.close() + except BrokenPipeError: + pass + elif key.fileobj in output_buffers: + data = os.read(key.fd, 32768) + if not data: + selector.unregister(key.fileobj) + if close_on_eof: + try: + key.fileobj.close() + except OSError: + pass + else: + output_buffers[key.fileobj].append(data) + + return (input_offset, True) # Completed + if _mswindows: # On Windows we just need to close `Popen._handle` when we no longer need @@ -289,6 +365,45 @@ def _cleanup(): DEVNULL = -3 +def _deadline_remaining(endtime): + """Calculate remaining time until deadline.""" + if endtime is None: + return None + return endtime - _time() + + +def _flush_stdin(stdin): + """Flush stdin, ignoring BrokenPipeError and closed file ValueError.""" + try: + stdin.flush() + except BrokenPipeError: + pass # communicate() must ignore BrokenPipeError. + except ValueError: + # Ignore ValueError: I/O operation on closed file. + if not stdin.closed: + raise + + +def _make_input_view(input_data): + """Convert input data to a byte memoryview for writing. + + Handles the case where input_data is already a memoryview with + non-byte elements (e.g., int32 array) by casting to a byte view. + This ensures len(view) returns the byte count, not element count. + """ + if not input_data: + return None + if isinstance(input_data, memoryview): + return input_data.cast("b") # ensure byte view for correct len() + return memoryview(input_data) + + +def _translate_newlines(data, encoding, errors): + """Decode bytes to str and translate newlines to \n.""" + data = data.decode(encoding, errors) + return data.replace("\r\n", "\n").replace("\r", "\n") + + # XXX This function is only used by multiprocessing and the test suite, # but it's here so that it can be imported when Python is compiled without # threads. @@ -351,15 +466,16 @@ def _args_from_interpreter_flags(): # -X options if dev_mode: args.extend(('-X', 'dev')) - for opt in ('faulthandler', 'tracemalloc', 'importtime', - 'frozen_modules', 'showrefcount', 'utf8', 'gil'): - if opt in xoptions: - value = xoptions[opt] - if value is True: - arg = opt - else: - arg = '%s=%s' % (opt, value) - args.extend(('-X', arg)) + for opt in sorted(xoptions): + if opt == 'dev': + # handled above via sys.flags.dev_mode + continue + value = xoptions[opt] + if value is True: + arg = opt + else: + arg = '%s=%s' % (opt, value) + args.extend(('-X', arg)) return args @@ -748,6 +864,60 @@ def _use_posix_spawn(): return False +def _can_use_pidfd_open(): + # Availability: Linux >= 5.3 + if not hasattr(os, "pidfd_open"): + return False + try: + pidfd = os.pidfd_open(os.getpid(), 0) + except OSError as err: + if err.errno in {errno.EMFILE, errno.ENFILE}: + # transitory 'too many open files' + return True + # likely blocked by security policy like SECCOMP (EPERM, + # EACCES, ENOSYS) + return False + else: + os.close(pidfd) + return True + + +def _can_use_kqueue(): + # Availability: macOS, BSD + names = ( + "kqueue", + "KQ_EV_ADD", + "KQ_EV_ONESHOT", + "KQ_FILTER_PROC", + "KQ_NOTE_EXIT", + ) + if not all(hasattr(select, x) for x in names): + return False + kq = None + try: + kq = select.kqueue() + kev = select.kevent( + os.getpid(), + filter=select.KQ_FILTER_PROC, + flags=select.KQ_EV_ADD | select.KQ_EV_ONESHOT, + fflags=select.KQ_NOTE_EXIT, + ) + kq.control([kev], 1, 0) + return True + except OSError as err: + if err.errno in {errno.EMFILE, errno.ENFILE}: + # transitory 'too many open files' + return True + return False + finally: + if kq is not None: + kq.close() + + +_CAN_USE_PIDFD_OPEN = not _mswindows and _can_use_pidfd_open() +_CAN_USE_KQUEUE = not _mswindows and _can_use_kqueue() + + # These are primarily fail-safe knobs for negatives. A True value does not # guarantee the given libc/syscall API will be used. _USE_POSIX_SPAWN = _use_posix_spawn() @@ -1094,8 +1264,8 @@ def universal_newlines(self, universal_newlines): self.text_mode = bool(universal_newlines) def _translate_newlines(self, data, encoding, errors): - data = data.decode(encoding, errors) - return data.replace("\r\n", "\n").replace("\r", "\n") + # Subclass-overridable hook; defers to the module-level helper. + return _translate_newlines(data, encoding, errors) def __enter__(self): return self @@ -1222,7 +1392,7 @@ def communicate(self, input=None, timeout=None): # See the detailed comment in .wait(). if timeout is not None: sigint_timeout = min(self._sigint_wait_secs, - self._remaining_time(endtime)) + _deadline_remaining(endtime)) else: sigint_timeout = self._sigint_wait_secs self._sigint_wait_secs = 0 # nothing else should wait. @@ -1235,7 +1405,7 @@ def communicate(self, input=None, timeout=None): finally: self._communication_started = True try: - sts = self.wait(timeout=self._remaining_time(endtime)) + self.wait(timeout=_deadline_remaining(endtime)) except TimeoutExpired as exc: exc.timeout = timeout raise @@ -1249,14 +1419,6 @@ def poll(self): return self._internal_poll() - def _remaining_time(self, endtime): - """Convenience for _communicate when computing timeouts.""" - if endtime is None: - return None - else: - return endtime - _time() - - def _check_timeout(self, endtime, orig_timeout, stdout_seq, stderr_seq, skip_check_and_raise=False): """Convenience for checking if a timeout has expired.""" @@ -1282,7 +1444,7 @@ def wait(self, timeout=None): # generated SIGINT and will exit rapidly. if timeout is not None: sigint_timeout = min(self._sigint_wait_secs, - self._remaining_time(endtime)) + _deadline_remaining(endtime)) else: sigint_timeout = self._sigint_wait_secs self._sigint_wait_secs = 0 # nothing else should wait. @@ -1613,6 +1775,10 @@ def _readerthread(self, fh, buffer): fh.close() + def _writerthread(self, input): + self._stdin_write(input) + + def _communicate(self, input, endtime, orig_timeout): # Start reader threads feeding into a list hanging off of this # object, unless they've already been started. @@ -1631,18 +1797,33 @@ def _communicate(self, input, endtime, orig_timeout): self.stderr_thread.daemon = True self.stderr_thread.start() - if self.stdin: - self._stdin_write(input) + # Start writer thread to send input to stdin, unless already + # started. The thread writes input and closes stdin when done, + # or continues in the background on timeout. + if self.stdin and not hasattr(self, "_stdin_thread"): + self._stdin_thread = \ + threading.Thread(target=self._writerthread, + args=(input,)) + self._stdin_thread.daemon = True + self._stdin_thread.start() + + # Wait for the writer thread, or time out. If we time out, the + # thread remains writing and the fd left open in case the user + # calls communicate again. + if hasattr(self, "_stdin_thread"): + self._stdin_thread.join(_deadline_remaining(endtime)) + if self._stdin_thread.is_alive(): + raise TimeoutExpired(self.args, orig_timeout) # Wait for the reader threads, or time out. If we time out, the # threads remain reading and the fds left open in case the user # calls communicate again. if self.stdout is not None: - self.stdout_thread.join(self._remaining_time(endtime)) + self.stdout_thread.join(_deadline_remaining(endtime)) if self.stdout_thread.is_alive(): raise TimeoutExpired(self.args, orig_timeout) if self.stderr is not None: - self.stderr_thread.join(self._remaining_time(endtime)) + self.stderr_thread.join(_deadline_remaining(endtime)) if self.stderr_thread.is_alive(): raise TimeoutExpired(self.args, orig_timeout) @@ -2027,14 +2208,100 @@ def _try_wait(self, wait_flags): sts = 0 return (pid, sts) + def _wait_pidfd(self, timeout): + """Wait for PID to terminate using pidfd_open() + poll(). + Linux >= 5.3 only. + """ + if not _CAN_USE_PIDFD_OPEN: + return False + try: + pidfd = os.pidfd_open(self.pid, 0) + except OSError: + # May be: + # - ESRCH: no such process + # - EMFILE, ENFILE: too many open files (usually 1024) + # - ENODEV: anonymous inode filesystem not supported + # - EPERM, EACCES, ENOSYS: undocumented; may happen if + # blocked by security policy like SECCOMP + return False + + try: + poller = select.poll() + poller.register(pidfd, select.POLLIN) + events = poller.poll(timeout * 1000) + if not events: + raise TimeoutExpired(self.args, timeout) + return True + finally: + os.close(pidfd) + + def _wait_kqueue(self, timeout): + """Wait for PID to terminate using kqueue(). macOS and BSD only.""" + if not _CAN_USE_KQUEUE: + return False + try: + kq = select.kqueue() + except OSError: + # likely EMFILE / ENFILE (too many open files) + return False + + try: + kev = select.kevent( + self.pid, + filter=select.KQ_FILTER_PROC, + flags=select.KQ_EV_ADD | select.KQ_EV_ONESHOT, + fflags=select.KQ_NOTE_EXIT, + ) + try: + events = kq.control([kev], 1, timeout) # wait + except OSError: + return False + else: + if not events: + raise TimeoutExpired(self.args, timeout) + return True + finally: + kq.close() def _wait(self, timeout): - """Internal implementation of wait() on POSIX.""" + """Internal implementation of wait() on POSIX. + + Uses efficient pidfd_open() + poll() on Linux or kqueue() + on macOS/BSD when available. Falls back to polling + waitpid(WNOHANG) otherwise. + """ if self.returncode is not None: return self.returncode if timeout is not None: - endtime = _time() + timeout + if timeout < 0: + raise TimeoutExpired(self.args, timeout) + started = _time() + endtime = started + timeout + + # Try efficient wait first. + if self._wait_pidfd(timeout) or self._wait_kqueue(timeout): + # Process is gone. At this point os.waitpid(pid, 0) + # will return immediately, but in very rare races + # the PID may have been reused. + # os.waitpid(pid, WNOHANG) ensures we attempt a + # non-blocking reap without blocking indefinitely. + with self._waitpid_lock: + if self.returncode is not None: + return self.returncode # Another thread waited. + (pid, sts) = self._try_wait(os.WNOHANG) + assert pid == self.pid or pid == 0 + if pid == self.pid: + self._handle_exitstatus(sts) + return self.returncode + # os.waitpid(pid, WNOHANG) returned 0 instead + # of our PID, meaning PID has not yet exited, + # even though poll() / kqueue() said so. Very + # rare and mostly theoretical. Fallback to busy + # polling. + elapsed = _time() - started + endtime -= elapsed + # Enter a busy loop if we have a timeout. This busy loop was # cribbed from Lib/threading.py in Thread.wait() at r71065. delay = 0.0005 # 500 us -> initial delay of 1 ms @@ -2050,7 +2317,7 @@ def _wait(self, timeout): break finally: self._waitpid_lock.release() - remaining = self._remaining_time(endtime) + remaining = _deadline_remaining(endtime) if remaining <= 0: raise TimeoutExpired(self.args, timeout) delay = min(delay * 2, remaining, .05) @@ -2066,6 +2333,7 @@ def _wait(self, timeout): # http://bugs.python.org/issue14396. if pid == self.pid: self._handle_exitstatus(sts) + return self.returncode @@ -2073,10 +2341,7 @@ def _communicate(self, input, endtime, orig_timeout): if self.stdin and not self._communication_started: # Flush stdio buffer. This might block, if the user has # been writing to .stdin in an uncontrolled fashion. - try: - self.stdin.flush() - except BrokenPipeError: - pass # communicate() must ignore BrokenPipeError. + _flush_stdin(self.stdin) if not input: try: self.stdin.close() @@ -2101,54 +2366,42 @@ def _communicate(self, input, endtime, orig_timeout): self._save_input(input) - if self._input: - input_view = memoryview(self._input) + input_view = _make_input_view(self._input) + input_offset = self._input_offset if self._input else 0 with _PopenSelector() as selector: - if self.stdin and input: + if self.stdin and not self.stdin.closed and self._input: selector.register(self.stdin, selectors.EVENT_WRITE) if self.stdout and not self.stdout.closed: selector.register(self.stdout, selectors.EVENT_READ) if self.stderr and not self.stderr.closed: selector.register(self.stderr, selectors.EVENT_READ) - while selector.get_map(): - timeout = self._remaining_time(endtime) - if timeout is not None and timeout < 0: - self._check_timeout(endtime, orig_timeout, - stdout, stderr, - skip_check_and_raise=True) - raise RuntimeError( # Impossible :) - '_check_timeout(..., skip_check_and_raise=True) ' - 'failed to raise TimeoutExpired.') - - ready = selector.select(timeout) - self._check_timeout(endtime, orig_timeout, stdout, stderr) - - # XXX Rewrite these to use non-blocking I/O on the file - # objects; they are no longer using C stdio! - - for key, events in ready: - if key.fileobj is self.stdin: - chunk = input_view[self._input_offset : - self._input_offset + _PIPE_BUF] - try: - self._input_offset += os.write(key.fd, chunk) - except BrokenPipeError: - selector.unregister(key.fileobj) - key.fileobj.close() - else: - if self._input_offset >= len(self._input): - selector.unregister(key.fileobj) - key.fileobj.close() - elif key.fileobj in (self.stdout, self.stderr): - data = os.read(key.fd, 32768) - if not data: - selector.unregister(key.fileobj) - key.fileobj.close() - self._fileobj2output[key.fileobj].append(data) + stdin_to_write = (self.stdin if self.stdin and self._input + and not self.stdin.closed else None) + # Persist the returned offset on self so a subsequent + # communicate() after a TimeoutExpired resumes mid-input + # rather than re-sending bytes the child already consumed. + new_offset, completed = _communicate_io_posix( + selector, + stdin_to_write, + input_view, + input_offset, + self._fileobj2output, + endtime, + close_on_eof=True) + if self._input: + self._input_offset = new_offset + + if not completed: + self._check_timeout(endtime, orig_timeout, stdout, stderr, + skip_check_and_raise=True) + raise RuntimeError( # Impossible :) + '_check_timeout(..., skip_check_and_raise=True) ' + 'failed to raise TimeoutExpired.') + try: - self.wait(timeout=self._remaining_time(endtime)) + self.wait(timeout=_deadline_remaining(endtime)) except TimeoutExpired as exc: exc.timeout = orig_timeout raise diff --git a/Lib/symtable.py b/Lib/symtable.py index 77475c3ffd9224..9238437191c00f 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -17,13 +17,13 @@ __all__ = ["symtable", "SymbolTableType", "SymbolTable", "Class", "Function", "Symbol"] -def symtable(code, filename, compile_type): +def symtable(code, filename, compile_type, *, module=None): """ Return the toplevel *SymbolTable* for the source code. *filename* is the name of the file with the code and *compile_type* is the *compile()* mode argument. """ - top = _symtable.symtable(code, filename, compile_type) + top = _symtable.symtable(code, filename, compile_type, module=module) return _newSymbolTable(top, filename) class SymbolTableFactory: @@ -184,6 +184,7 @@ class Function(SymbolTable): __frees = None __globals = None __nonlocals = None + __cells = None def __idents_matching(self, test_func): return tuple(ident for ident in self.get_identifiers() @@ -229,43 +230,17 @@ def get_frees(self): self.__frees = self.__idents_matching(is_free) return self.__frees + def get_cells(self): + """Return a tuple of cell variables in the function. + """ + if self.__cells is None: + is_cell = lambda x: _get_scope(x) == CELL + self.__cells = self.__idents_matching(is_cell) + return self.__cells -class Class(SymbolTable): - - __methods = None - def get_methods(self): - """Return a tuple of methods declared in the class. - """ - import warnings - typename = f'{self.__class__.__module__}.{self.__class__.__name__}' - warnings.warn(f'{typename}.get_methods() is deprecated ' - f'and will be removed in Python 3.16.', - DeprecationWarning, stacklevel=2) - - if self.__methods is None: - d = {} - - def is_local_symbol(ident): - flags = self._table.symbols.get(ident, 0) - return ((flags >> SCOPE_OFF) & SCOPE_MASK) == LOCAL - - for st in self._table.children: - # pick the function-like symbols that are local identifiers - if is_local_symbol(st.name): - match st.type: - case _symtable.TYPE_FUNCTION: - d[st.name] = 1 - case _symtable.TYPE_TYPE_PARAMETERS: - # Get the function-def block in the annotation - # scope 'st' with the same identifier, if any. - scope_name = st.name - for c in st.children: - if c.name == scope_name and c.type == _symtable.TYPE_FUNCTION: - d[scope_name] = 1 - break - self.__methods = tuple(d) - return self.__methods +class Class(SymbolTable): + pass class Symbol: @@ -342,6 +317,10 @@ def is_free(self): """ return bool(self.__scope == FREE) + def is_cell(self): + """Return *True* if the symbol is a cell variable.""" + return bool(self.__scope == CELL) + def is_free_class(self): """Return *True* if a class-scoped symbol is free from the perspective of a method.""" @@ -401,7 +380,7 @@ def get_namespace(self): _flags = [('USE', USE)] _flags.extend(kv for kv in globals().items() if kv[0].startswith('DEF_')) _scopes_names = ('FREE', 'LOCAL', 'GLOBAL_IMPLICIT', 'GLOBAL_EXPLICIT', 'CELL') -_scopes_value_to_name = {globals()[n]: n for n in _scopes_names} +_scopes_value_to_name = frozendict({globals()[n]: n for n in _scopes_names}) def main(args): diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 49e0986517ce97..719b306b02b6e3 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -412,7 +412,13 @@ def _init_non_posix(vars): vars['EXE'] = '.exe' vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) - vars['TZPATH'] = '' + # No standard path exists on Windows for this, but we'll check + # whether someone is imitating a POSIX-like layout + check_tzpath = os.path.join(vars['prefix'], 'share', 'zoneinfo') + if os.path.exists(check_tzpath): + vars['TZPATH'] = check_tzpath + else: + vars['TZPATH'] = '' # # public APIs @@ -431,6 +437,7 @@ def parse_config_h(fp, vars=None): import re define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + quoted_re = re.compile('^"(.*)"$') while True: line = fp.readline() @@ -439,6 +446,8 @@ def parse_config_h(fp, vars=None): m = define_rx.match(line) if m: n, v = m.group(1, 2) + if mq := quoted_re.match(v): + v = mq.group(1) try: if n in _ALWAYS_STR: raise ValueError @@ -639,25 +648,30 @@ def get_platform(): isn't particularly important. Examples of returned values: - linux-i586 - linux-alpha (?) + linux-x86_64 + linux-aarch64 solaris-2.6-sun4u - Windows will return one of: - win-amd64 (64-bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) - win-arm64 (64-bit Windows on ARM64 (aka AArch64) - win32 (all others - specifically, sys.platform is returned) - For other non-POSIX platforms, currently just returns 'sys.platform'. + Windows: - """ + - win-amd64 (64-bit Windows on AMD64, aka x86_64, Intel64, and EM64T) + - win-arm64 (64-bit Windows on ARM64, aka AArch64) + - win32 (all others - specifically, sys.platform is returned) + + POSIX based OS: + + - linux-x86_64 + - macosx-15.5-arm64 + - macosx-26.0-universal2 (macOS on Apple Silicon or Intel) + - android-24-arm64_v8a + + For other non-POSIX platforms, currently just returns :data:`sys.platform`.""" if os.name == 'nt': - if 'amd64' in sys.version.lower(): - return 'win-amd64' - if '(arm)' in sys.version.lower(): - return 'win-arm32' - if '(arm64)' in sys.version.lower(): - return 'win-arm64' + import _sysconfig + platform = _sysconfig.get_platform() + if platform: + return platform return sys.platform if os.name != "posix" or not hasattr(os, 'uname'): @@ -683,11 +697,19 @@ def get_platform(): release = get_config_var("ANDROID_API_LEVEL") # Wheel tags use the ABI names from Android's own tools. + # When Python is running on 32-bit ARM Android on a 64-bit ARM kernel, + # 'os.uname().machine' is 'armv8l'. Such devices run the same userspace + # code as 'armv7l' devices. + # During the build process of the Android testbed when targeting 32-bit ARM, + # '_PYTHON_HOST_PLATFORM' is 'arm-linux-androideabi', so 'machine' becomes + # 'arm'. machine = { - "x86_64": "x86_64", - "i686": "x86", "aarch64": "arm64_v8a", + "arm": "armeabi_v7a", "armv7l": "armeabi_v7a", + "armv8l": "armeabi_v7a", + "i686": "x86", + "x86_64": "x86_64", }[machine] elif osname == "linux": # At least on Linux/Intel, 'machine' is the processor -- @@ -734,41 +756,3 @@ def get_python_version(): def _get_python_version_abi(): return _PY_VERSION_SHORT + get_config_var("abi_thread") - - -def expand_makefile_vars(s, vars): - """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in - 'string' according to 'vars' (a dictionary mapping variable names to - values). Variables not present in 'vars' are silently expanded to the - empty string. The variable values in 'vars' should not contain further - variable expansions; if 'vars' is the output of 'parse_makefile()', - you're fine. Returns a variable-expanded version of 's'. - """ - - import warnings - warnings.warn( - 'sysconfig.expand_makefile_vars is deprecated and will be removed in ' - 'Python 3.16. Use sysconfig.get_paths(vars=...) instead.', - DeprecationWarning, - stacklevel=2, - ) - - import re - - _findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)" - _findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}" - - # This algorithm does multiple expansion, so if vars['foo'] contains - # "${bar}", it will expand ${foo} to ${bar}, and then expand - # ${bar}... and so forth. This is fine as long as 'vars' comes from - # 'parse_makefile()', which takes care of such expansions eagerly, - # according to make's variable expansion semantics. - - while True: - m = re.search(_findvar1_rx, s) or re.search(_findvar2_rx, s) - if m: - (beg, end) = m.span() - s = s[0:beg] + vars.get(m.group(1)) + s[end:] - else: - break - return s diff --git a/Lib/sysconfig/__main__.py b/Lib/sysconfig/__main__.py index bc2197cfe79402..0cf0cf4dbb9ec7 100644 --- a/Lib/sysconfig/__main__.py +++ b/Lib/sysconfig/__main__.py @@ -21,8 +21,9 @@ # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). _variable_rx = r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)" -_findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)" -_findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}" +_findvar_rx = (r"\$(\([A-Za-z][A-Za-z0-9_]*\)" + r"|\{[A-Za-z][A-Za-z0-9_]*\}" + r"|\$?)") def _parse_makefile(filename, vars=None, keep_unresolved=True): @@ -49,26 +50,7 @@ def _parse_makefile(filename, vars=None, keep_unresolved=True): m = re.match(_variable_rx, line) if m: n, v = m.group(1, 2) - v = v.strip() - # `$$' is a literal `$' in make - tmpv = v.replace('$$', '') - - if "$" in tmpv: - notdone[n] = v - else: - try: - if n in _ALWAYS_STR: - raise ValueError - - v = int(v) - except ValueError: - # insert literal `$' - done[n] = v.replace('$$', '$') - else: - done[n] = v - - # do variable interpolation here - variables = list(notdone.keys()) + notdone[n] = v.strip() # Variables with a 'PY_' prefix in the makefile. These need to # be made available without that prefix through sysconfig. @@ -76,72 +58,64 @@ def _parse_makefile(filename, vars=None, keep_unresolved=True): # if the expansion uses the name without a prefix. renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') - while len(variables) > 0: - for name in tuple(variables): - value = notdone[name] - m1 = re.search(_findvar1_rx, value) - m2 = re.search(_findvar2_rx, value) - if m1 and m2: - m = m1 if m1.start() < m2.start() else m2 - else: - m = m1 if m1 else m2 - if m is not None: - n = m.group(1) - found = True - if n in done: - item = str(done[n]) - elif n in notdone: - # get it on a subsequent round - found = False - elif n in os.environ: - # do it like make: fall back to environment - item = os.environ[n] - - elif n in renamed_variables: - if (name.startswith('PY_') and - name[3:] in renamed_variables): - item = "" - - elif 'PY_' + n in notdone: - found = False - - else: - item = str(done['PY_' + n]) - + def resolve_var(name): + def repl(m): + n = m[1] + if n == '$': + return '$' + elif n == '': + # bogus variable reference (e.g. "prefix=$/opt/python") + if keep_unresolved: + return m[0] + raise ValueError + elif n[0] == '(' and n[-1] == ')': + n = n[1:-1] + elif n[0] == '{' and n[-1] == '}': + n = n[1:-1] + + if n in done: + return str(done[n]) + elif n in notdone: + return str(resolve_var(n)) + elif n in os.environ: + # do it like make: fall back to environment + return os.environ[n] + elif n in renamed_variables: + if name.startswith('PY_') and name[3:] in renamed_variables: + return "" + n = 'PY_' + n + if n in notdone: + return str(resolve_var(n)) else: - done[n] = item = "" - - if found: - after = value[m.end():] - value = value[:m.start()] + item + after - if "$" in after: - notdone[name] = value - else: - try: - if name in _ALWAYS_STR: - raise ValueError - value = int(value) - except ValueError: - done[name] = value.strip() - else: - done[name] = value - variables.remove(name) - - if name.startswith('PY_') \ - and name[3:] in renamed_variables: - - name = name[3:] - if name not in done: - done[name] = value - + assert n not in done + return "" else: - # Adds unresolved variables to the done dict. - # This is disabled when called from distutils.sysconfig - if keep_unresolved: - done[name] = value - # bogus variable reference (e.g. "prefix=$/opt/python"); - # just drop it since we can't deal - variables.remove(name) + done[n] = "" + return "" + + assert name not in done + done[name] = "" + try: + value = re.sub(_findvar_rx, repl, notdone[name]) + except ValueError: + del done[name] + return "" + value = value.strip() + if name not in _ALWAYS_STR: + try: + value = int(value) + except ValueError: + pass + done[name] = value + if name.startswith('PY_') and name[3:] in renamed_variables: + name = name[3:] + if name not in done: + done[name] = value + return value + + for n in notdone: + if n not in done: + resolve_var(n) # strip spurious spaces for k, v in done.items(): diff --git a/Lib/tabnanny.py b/Lib/tabnanny.py index c0097351b269f2..272e8e33e0c46a 100644 --- a/Lib/tabnanny.py +++ b/Lib/tabnanny.py @@ -16,8 +16,6 @@ # XXX The API needs to undergo changes however; the current code is too # XXX script-like. This will be addressed later. -__version__ = "6" - import os import sys import tokenize @@ -334,5 +332,14 @@ def _process_tokens(tokens): raise NannyNag(start[0], msg, line) +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "6" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + if __name__ == '__main__': main() diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 80d8644af86f74..a293a049247274 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -28,7 +28,6 @@ """Read from and write to tar format archives. """ -version = "0.9.0" __author__ = "Lars Gust\u00e4bel (lars@gustaebel.de)" __credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend." @@ -201,7 +200,6 @@ def itn(n, digits=8, format=DEFAULT_FORMAT): # base-256 representation. This allows values up to (256**(digits-1))-1. # A 0o200 byte indicates a positive number, a 0o377 byte a negative # number. - original_n = n n = int(n) if 0 <= n < 8 ** (digits - 1): s = bytes("%0*o" % (digits - 1, n), "ascii") + NUL @@ -339,7 +337,7 @@ class _Stream: """ def __init__(self, name, mode, comptype, fileobj, bufsize, - compresslevel, preset): + compresslevel, preset, mtime): """Construct a _Stream object. """ self._extfileobj = True @@ -353,7 +351,7 @@ def __init__(self, name, mode, comptype, fileobj, bufsize, fileobj = _StreamProxy(fileobj) comptype = fileobj.getcomptype() - self.name = name or "" + self.name = os.fspath(name) if name is not None else "" self.mode = mode self.comptype = comptype self.fileobj = fileobj @@ -374,7 +372,7 @@ def __init__(self, name, mode, comptype, fileobj, bufsize, self.exception = zlib.error self._init_read_gz() else: - self._init_write_gz(compresslevel) + self._init_write_gz(compresslevel, mtime) elif comptype == "bz2": try: @@ -382,7 +380,6 @@ def __init__(self, name, mode, comptype, fileobj, bufsize, except ImportError: raise CompressionError("bz2 module is not available") from None if mode == "r": - self.dbuf = b"" self.cmp = bz2.BZ2Decompressor() self.exception = OSError else: @@ -394,7 +391,6 @@ def __init__(self, name, mode, comptype, fileobj, bufsize, except ImportError: raise CompressionError("lzma module is not available") from None if mode == "r": - self.dbuf = b"" self.cmp = lzma.LZMADecompressor() self.exception = lzma.LZMAError else: @@ -405,7 +401,6 @@ def __init__(self, name, mode, comptype, fileobj, bufsize, except ImportError: raise CompressionError("compression.zstd module is not available") from None if mode == "r": - self.dbuf = b"" self.cmp = zstd.ZstdDecompressor() self.exception = zstd.ZstdError else: @@ -423,7 +418,7 @@ def __del__(self): if hasattr(self, "closed") and not self.closed: self.close() - def _init_write_gz(self, compresslevel): + def _init_write_gz(self, compresslevel, mtime): """Initialize for writing with gzip compression. """ self.cmp = self.zlib.compressobj(compresslevel, @@ -431,7 +426,9 @@ def _init_write_gz(self, compresslevel): -self.zlib.MAX_WBITS, self.zlib.DEF_MEM_LEVEL, 0) - timestamp = struct.pack(" 1024. """ + # Only non-negative offsets are allowed + if count < 0: + raise InvalidHeaderError("invalid offset") blocks, remainder = divmod(count, BLOCKSIZE) if remainder: blocks += 1 @@ -1723,7 +1752,7 @@ class TarFile(object): def __init__(self, name=None, mode="r", fileobj=None, format=None, tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, errors="surrogateescape", pax_headers=None, debug=None, - errorlevel=None, copybufsize=None, stream=False): + errorlevel=None, copybufsize=None, stream=False, mtime=None): """Open an (uncompressed) tar archive 'name'. 'mode' is either 'r' to read from an existing archive, 'a' to append data to an existing file or 'w' to create a new file overwriting an existing one. 'mode' @@ -1929,8 +1958,9 @@ def not_compressed(comptype): compresslevel = kwargs.pop("compresslevel", 6) preset = kwargs.pop("preset", None) + mtime = kwargs.pop("mtime", None) stream = _Stream(name, filemode, comptype, fileobj, bufsize, - compresslevel, preset) + compresslevel, preset, mtime) try: t = cls(name, filemode, stream, **kwargs) except: @@ -1966,7 +1996,8 @@ def gzopen(cls, name, mode="r", fileobj=None, compresslevel=6, **kwargs): raise CompressionError("gzip module is not available") from None try: - fileobj = GzipFile(name, mode + "b", compresslevel, fileobj) + mtime = kwargs.pop("mtime", None) + fileobj = GzipFile(name, mode + "b", compresslevel, fileobj, mtime=mtime) except OSError as e: if fileobj is not None and mode == 'r': raise ReadError("not a gzip file") from e @@ -2164,7 +2195,6 @@ def gettarinfo(self, name=None, arcname=None, fileobj=None): # Now, fill the TarInfo object with # information specific for the file. tarinfo = self.tarinfo() - tarinfo._tarfile = self # To be removed in 3.16. # Use os.stat or os.lstat, depending on if symlinks shall be resolved. if fileobj is None: @@ -2243,10 +2273,11 @@ def gettarinfo(self, name=None, arcname=None, fileobj=None): return tarinfo def list(self, verbose=True, *, members=None): - """Print a table of contents to sys.stdout. If 'verbose' is False, only - the names of the members are printed. If it is True, an 'ls -l'-like - output is produced. 'members' is optional and must be a subset of the - list returned by getmembers(). + """Print a table of contents to sys.stdout. + + If 'verbose' is False, only the names of the members are printed. + If it is True, an 'ls -l'-like output is produced. 'members' is + optional and must be a subset of the list returned by getmembers(). """ # Convert tarinfo type to stat type. type2mode = {REGTYPE: stat.S_IFREG, SYMTYPE: stat.S_IFLNK, @@ -2337,10 +2368,12 @@ def add(self, name, arcname=None, recursive=True, *, filter=None): self.addfile(tarinfo) def addfile(self, tarinfo, fileobj=None): - """Add the TarInfo object 'tarinfo' to the archive. If 'tarinfo' represents - a non zero-size regular file, the 'fileobj' argument should be a binary file, - and tarinfo.size bytes are read from it and added to the archive. - You can create TarInfo objects directly, or by using gettarinfo(). + """Add the TarInfo object 'tarinfo' to the archive. + + If 'tarinfo' represents a non zero-size regular file, the 'fileobj' + argument should be a binary file, and tarinfo.size bytes are read + from it and added to the archive. You can create TarInfo objects + directly, or by using gettarinfo(). """ self._check("awx") @@ -2716,10 +2749,19 @@ def makelink_with_filter(self, tarinfo, targetpath, if os.path.lexists(targetpath): # Avoid FileExistsError on following os.symlink. os.unlink(targetpath) - os.symlink(tarinfo.linkname, targetpath) + link_target = tarinfo.linkname + if os.name == "nt": + # gh-57911: Posix-flavoured forward-slash path separators in + # symlink targets aren't acknowledged by Windows, resulting + # in corrupted links. + link_target = link_target.replace("/", os.path.sep) + os.symlink(link_target, targetpath) return else: if os.path.exists(tarinfo._link_target): + if os.path.lexists(targetpath): + # Avoid FileExistsError on following os.link. + os.unlink(targetpath) os.link(tarinfo._link_target, targetpath) return except symlink_exception: @@ -3032,7 +3074,7 @@ def main(): import argparse description = 'A simple command-line interface for tarfile module.' - parser = argparse.ArgumentParser(description=description, color=True) + parser = argparse.ArgumentParser(description=description) parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Verbose output') parser.add_argument('--filter', metavar='', @@ -3126,5 +3168,15 @@ def main(): if args.verbose: print('{!r} file created.'.format(tar_name)) + +def __getattr__(name): + if name == "version": + from warnings import _deprecated + + _deprecated("version", remove=(3, 20)) + return "0.9.0" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + if __name__ == '__main__': main() diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 53d14ff5c67131..6dac9ab3c41717 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -57,10 +57,11 @@ if hasattr(_os, 'O_BINARY'): _bin_openflags |= _os.O_BINARY -if hasattr(_os, 'TMP_MAX'): - TMP_MAX = _os.TMP_MAX -else: - TMP_MAX = 10000 +# This is more than enough. +# Each name contains over 40 random bits. Even with a million temporary +# files, the chance of a conflict is less than 1 in a million, and with +# 20 attempts, it is less than 1e-120. +TMP_MAX = 20 # This variable _was_ unused for legacy reasons, see issue 10354. # But as of 3.5 we actually use it at runtime so changing it would @@ -196,8 +197,7 @@ def _get_default_tempdir(dirlist=None): for dir in dirlist: if dir != _os.curdir: dir = _os.path.abspath(dir) - # Try only a few names per directory. - for seq in range(100): + for seq in range(TMP_MAX): name = next(namer) filename = _os.path.join(dir, name) try: @@ -213,10 +213,8 @@ def _get_default_tempdir(dirlist=None): except FileExistsError: pass except PermissionError: - # This exception is thrown when a directory with the chosen name - # already exists on windows. - if (_os.name == 'nt' and _os.path.isdir(dir) and - _os.access(dir, _os.W_OK)): + # See the comment in mkdtemp(). + if _os.name == 'nt' and _os.path.isdir(dir): continue break # no point trying more names in this directory except OSError: @@ -258,10 +256,8 @@ def _mkstemp_inner(dir, pre, suf, flags, output_type): except FileExistsError: continue # try again except PermissionError: - # This exception is thrown when a directory with the chosen name - # already exists on windows. - if (_os.name == 'nt' and _os.path.isdir(dir) and - _os.access(dir, _os.W_OK)): + # See the comment in mkdtemp(). + if _os.name == 'nt' and _os.path.isdir(dir) and seq < TMP_MAX - 1: continue else: raise @@ -386,10 +382,14 @@ def mkdtemp(suffix=None, prefix=None, dir=None): except FileExistsError: continue # try again except PermissionError: - # This exception is thrown when a directory with the chosen name - # already exists on windows. - if (_os.name == 'nt' and _os.path.isdir(dir) and - _os.access(dir, _os.W_OK)): + # On Posix, this exception is raised when the user has no + # write access to the parent directory. + # On Windows, it is also raised when a directory with + # the chosen name already exists, or if the parent directory + # is not a directory. + # We cannot distinguish between "directory-exists-error" and + # "access-denied-error". + if _os.name == 'nt' and _os.path.isdir(dir) and seq < TMP_MAX - 1: continue else: raise @@ -691,7 +691,7 @@ def opener(*args): fd, name = _mkstemp_inner(dir, prefix, suffix, flags, output_type) try: _os.unlink(name) - except BaseException as e: + except BaseException: _os.close(fd) raise return fd diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml index f1a967203ce4ba..dca74eb6e14bbd 100644 --- a/Lib/test/.ruff.toml +++ b/Lib/test/.ruff.toml @@ -1,5 +1,8 @@ extend = "../../.ruff.toml" # Inherit the project-wide settings +# Unlike Tools/, tests can use newer syntax than PYTHON_FOR_REGEN +target-version = "py315" + extend-exclude = [ # Excluded (run with the other AC files in its own separate ruff job in pre-commit) "test_clinic.py", @@ -8,13 +11,15 @@ extend-exclude = [ # Non UTF-8 files "encoded_modules/module_iso_8859_1.py", "encoded_modules/module_koi8_r.py", - # SyntaxError because of t-strings - "test_annotationlib.py", - "test_string/test_templatelib.py", - "test_tstring.py", # New grammar constructions may not yet be recognized by Ruff, # and tests re-use the same names as only the grammar is being checked. "test_grammar.py", + # Lazy import syntax (PEP 810) is not yet supported by Ruff + "test_lazy_import/__init__.py", + "test_lazy_import/data/*.py", + "test_lazy_import/data/**/*.py", + # Unary plus literal pattern is not yet supported by Ruff (GH-145239) + "test_patma.py", ] [lint] diff --git a/Lib/test/NormalizationTest-3.2.0.txt b/Lib/test/NormalizationTest-3.2.0.txt new file mode 100644 index 00000000000000..d1c7b5165e3618 --- /dev/null +++ b/Lib/test/NormalizationTest-3.2.0.txt @@ -0,0 +1,17035 @@ +# NormalizationTest-3.2.0.txt +# Date: 2002-03-19,23:31:18 GMT [MD] +# +# Normalization Test Suite +# Format: +# +# Columns (c1, c2,...) are separated by semicolons +# Comments are indicated with hash marks +# +# CONFORMANCE: +# 1. The following invariants must be true for all conformant implementations +# +# NFC +# c2 == NFC(c1) == NFC(c2) == NFC(c3) +# c4 == NFC(c4) == NFC(c5) +# +# NFD +# c3 == NFD(c1) == NFD(c2) == NFD(c3) +# c5 == NFD(c4) == NFD(c5) +# +# NFKC +# c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5) +# +# NFKD +# c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5) +# +# 2. For every assigned Unicode 3.1.0 code point X that is not specifically +# listed in Part 1, the following invariants must be true for all conformant +# implementations: +# +# X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X) +# +@Part0 # Specific cases +# +1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE +1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW +1E0A 0323;1E0C 0307;0044 0323 0307;1E0C 0307;0044 0323 0307; # (Ḋ◌̣; Ḍ◌̇; D◌̣◌̇; Ḍ◌̇; D◌̣◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING DOT BELOW +1E0C 0307;1E0C 0307;0044 0323 0307;1E0C 0307;0044 0323 0307; # (Ḍ◌̇; Ḍ◌̇; D◌̣◌̇; Ḍ◌̇; D◌̣◌̇; ) LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING DOT ABOVE +0044 0307 0323;1E0C 0307;0044 0323 0307;1E0C 0307;0044 0323 0307; # (D◌̇◌̣; Ḍ◌̇; D◌̣◌̇; Ḍ◌̇; D◌̣◌̇; ) LATIN CAPITAL LETTER D, COMBINING DOT ABOVE, COMBINING DOT BELOW +0044 0323 0307;1E0C 0307;0044 0323 0307;1E0C 0307;0044 0323 0307; # (D◌̣◌̇; Ḍ◌̇; D◌̣◌̇; Ḍ◌̇; D◌̣◌̇; ) LATIN CAPITAL LETTER D, COMBINING DOT BELOW, COMBINING DOT ABOVE +1E0A 031B;1E0A 031B;0044 031B 0307;1E0A 031B;0044 031B 0307; # (Ḋ◌̛; Ḋ◌̛; D◌̛◌̇; Ḋ◌̛; D◌̛◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING HORN +1E0C 031B;1E0C 031B;0044 031B 0323;1E0C 031B;0044 031B 0323; # (Ḍ◌̛; Ḍ◌̛; D◌̛◌̣; Ḍ◌̛; D◌̛◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING HORN +1E0A 031B 0323;1E0C 031B 0307;0044 031B 0323 0307;1E0C 031B 0307;0044 031B 0323 0307; # (Ḋ◌̛◌̣; Ḍ◌̛◌̇; D◌̛◌̣◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING HORN, COMBINING DOT BELOW +1E0C 031B 0307;1E0C 031B 0307;0044 031B 0323 0307;1E0C 031B 0307;0044 031B 0323 0307; # (Ḍ◌̛◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; ) LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING HORN, COMBINING DOT ABOVE +0044 031B 0307 0323;1E0C 031B 0307;0044 031B 0323 0307;1E0C 031B 0307;0044 031B 0323 0307; # (D◌̛◌̇◌̣; Ḍ◌̛◌̇; D◌̛◌̣◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; ) LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT ABOVE, COMBINING DOT BELOW +0044 031B 0323 0307;1E0C 031B 0307;0044 031B 0323 0307;1E0C 031B 0307;0044 031B 0323 0307; # (D◌̛◌̣◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; ) LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT BELOW, COMBINING DOT ABOVE +00C8;00C8;0045 0300;00C8;0045 0300; # (È; È; E◌̀; È; E◌̀; ) LATIN CAPITAL LETTER E WITH GRAVE +0112;0112;0045 0304;0112;0045 0304; # (Ē; Ē; E◌̄; Ē; E◌̄; ) LATIN CAPITAL LETTER E WITH MACRON +0045 0300;00C8;0045 0300;00C8;0045 0300; # (E◌̀; È; E◌̀; È; E◌̀; ) LATIN CAPITAL LETTER E, COMBINING GRAVE ACCENT +0045 0304;0112;0045 0304;0112;0045 0304; # (E◌̄; Ē; E◌̄; Ē; E◌̄; ) LATIN CAPITAL LETTER E, COMBINING MACRON +1E14;1E14;0045 0304 0300;1E14;0045 0304 0300; # (Ḕ; Ḕ; E◌̄◌̀; Ḕ; E◌̄◌̀; ) LATIN CAPITAL LETTER E WITH MACRON AND GRAVE +0112 0300;1E14;0045 0304 0300;1E14;0045 0304 0300; # (Ē◌̀; Ḕ; E◌̄◌̀; Ḕ; E◌̄◌̀; ) LATIN CAPITAL LETTER E WITH MACRON, COMBINING GRAVE ACCENT +1E14 0304;1E14 0304;0045 0304 0300 0304;1E14 0304;0045 0304 0300 0304; # (Ḕ◌̄; Ḕ◌̄; E◌̄◌̀◌̄; Ḕ◌̄; E◌̄◌̀◌̄; ) LATIN CAPITAL LETTER E WITH MACRON AND GRAVE, COMBINING MACRON +0045 0304 0300;1E14;0045 0304 0300;1E14;0045 0304 0300; # (E◌̄◌̀; Ḕ; E◌̄◌̀; Ḕ; E◌̄◌̀; ) LATIN CAPITAL LETTER E, COMBINING MACRON, COMBINING GRAVE ACCENT +0045 0300 0304;00C8 0304;0045 0300 0304;00C8 0304;0045 0300 0304; # (E◌̀◌̄; È◌̄; E◌̀◌̄; È◌̄; E◌̀◌̄; ) LATIN CAPITAL LETTER E, COMBINING GRAVE ACCENT, COMBINING MACRON +05B8 05B9 05B1 0591 05C3 05B0 05AC 059F;05B1 05B8 05B9 0591 05C3 05B0 05AC 059F;05B1 05B8 05B9 0591 05C3 05B0 05AC 059F;05B1 05B8 05B9 0591 05C3 05B0 05AC 059F;05B1 05B8 05B9 0591 05C3 05B0 05AC 059F; # (◌ָ◌ֹ◌ֱ◌֑׃◌ְ◌֬◌֟; ◌ֱ◌ָ◌ֹ◌֑׃◌ְ◌֬◌֟; ◌ֱ◌ָ◌ֹ◌֑׃◌ְ◌֬◌֟; ◌ֱ◌ָ◌ֹ◌֑׃◌ְ◌֬◌֟; ◌ֱ◌ָ◌ֹ◌֑׃◌ְ◌֬◌֟; ) HEBREW POINT QAMATS, HEBREW POINT HOLAM, HEBREW POINT HATAF SEGOL, HEBREW ACCENT ETNAHTA, HEBREW PUNCTUATION SOF PASUQ, HEBREW POINT SHEVA, HEBREW ACCENT ILUY, HEBREW ACCENT QARNEY PARA +0592 05B7 05BC 05A5 05B0 05C0 05C4 05AD;05B0 05B7 05BC 05A5 0592 05C0 05AD 05C4;05B0 05B7 05BC 05A5 0592 05C0 05AD 05C4;05B0 05B7 05BC 05A5 0592 05C0 05AD 05C4;05B0 05B7 05BC 05A5 0592 05C0 05AD 05C4; # (◌֒◌ַ◌ּ◌֥◌ְ׀◌ׄ◌֭; ◌ְ◌ַ◌ּ◌֥◌֒׀◌֭◌ׄ; ◌ְ◌ַ◌ּ◌֥◌֒׀◌֭◌ׄ; ◌ְ◌ַ◌ּ◌֥◌֒׀◌֭◌ׄ; ◌ְ◌ַ◌ּ◌֥◌֒׀◌֭◌ׄ; ) HEBREW ACCENT SEGOL, HEBREW POINT PATAH, HEBREW POINT DAGESH OR MAPIQ, HEBREW ACCENT MERKHA, HEBREW POINT SHEVA, HEBREW PUNCTUATION PASEQ, HEBREW MARK UPPER DOT, HEBREW ACCENT DEHI +# +@Part1 # Character by character test +# All characters not explicitly occurring in c1 of Part 1 have identical NFC, D, KC, KD forms. +# +00A0;00A0;00A0;0020;0020; # ( ;  ;  ; ; ; ) NO-BREAK SPACE +00A8;00A8;00A8;0020 0308;0020 0308; # (¨; ¨; ¨; ◌̈; ◌̈; ) DIAERESIS +00AA;00AA;00AA;0061;0061; # (ª; ª; ª; a; a; ) FEMININE ORDINAL INDICATOR +00AF;00AF;00AF;0020 0304;0020 0304; # (¯; ¯; ¯; ◌̄; ◌̄; ) MACRON +00B2;00B2;00B2;0032;0032; # (²; ²; ²; 2; 2; ) SUPERSCRIPT TWO +00B3;00B3;00B3;0033;0033; # (³; ³; ³; 3; 3; ) SUPERSCRIPT THREE +00B4;00B4;00B4;0020 0301;0020 0301; # (´; ´; ´; ◌́; ◌́; ) ACUTE ACCENT +00B5;00B5;00B5;03BC;03BC; # (µ; µ; µ; μ; μ; ) MICRO SIGN +00B8;00B8;00B8;0020 0327;0020 0327; # (¸; ¸; ¸; ◌̧; ◌̧; ) CEDILLA +00B9;00B9;00B9;0031;0031; # (¹; ¹; ¹; 1; 1; ) SUPERSCRIPT ONE +00BA;00BA;00BA;006F;006F; # (º; º; º; o; o; ) MASCULINE ORDINAL INDICATOR +00BC;00BC;00BC;0031 2044 0034;0031 2044 0034; # (¼; ¼; ¼; 1⁄4; 1⁄4; ) VULGAR FRACTION ONE QUARTER +00BD;00BD;00BD;0031 2044 0032;0031 2044 0032; # (½; ½; ½; 1⁄2; 1⁄2; ) VULGAR FRACTION ONE HALF +00BE;00BE;00BE;0033 2044 0034;0033 2044 0034; # (¾; ¾; ¾; 3⁄4; 3⁄4; ) VULGAR FRACTION THREE QUARTERS +00C0;00C0;0041 0300;00C0;0041 0300; # (À; À; A◌̀; À; A◌̀; ) LATIN CAPITAL LETTER A WITH GRAVE +00C1;00C1;0041 0301;00C1;0041 0301; # (Á; Á; A◌́; Á; A◌́; ) LATIN CAPITAL LETTER A WITH ACUTE +00C2;00C2;0041 0302;00C2;0041 0302; # (Â; Â; A◌̂; Â; A◌̂; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX +00C3;00C3;0041 0303;00C3;0041 0303; # (Ã; Ã; A◌̃; Ã; A◌̃; ) LATIN CAPITAL LETTER A WITH TILDE +00C4;00C4;0041 0308;00C4;0041 0308; # (Ä; Ä; A◌̈; Ä; A◌̈; ) LATIN CAPITAL LETTER A WITH DIAERESIS +00C5;00C5;0041 030A;00C5;0041 030A; # (Å; Å; A◌̊; Å; A◌̊; ) LATIN CAPITAL LETTER A WITH RING ABOVE +00C7;00C7;0043 0327;00C7;0043 0327; # (Ç; Ç; C◌̧; Ç; C◌̧; ) LATIN CAPITAL LETTER C WITH CEDILLA +00C8;00C8;0045 0300;00C8;0045 0300; # (È; È; E◌̀; È; E◌̀; ) LATIN CAPITAL LETTER E WITH GRAVE +00C9;00C9;0045 0301;00C9;0045 0301; # (É; É; E◌́; É; E◌́; ) LATIN CAPITAL LETTER E WITH ACUTE +00CA;00CA;0045 0302;00CA;0045 0302; # (Ê; Ê; E◌̂; Ê; E◌̂; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX +00CB;00CB;0045 0308;00CB;0045 0308; # (Ë; Ë; E◌̈; Ë; E◌̈; ) LATIN CAPITAL LETTER E WITH DIAERESIS +00CC;00CC;0049 0300;00CC;0049 0300; # (Ì; Ì; I◌̀; Ì; I◌̀; ) LATIN CAPITAL LETTER I WITH GRAVE +00CD;00CD;0049 0301;00CD;0049 0301; # (Í; Í; I◌́; Í; I◌́; ) LATIN CAPITAL LETTER I WITH ACUTE +00CE;00CE;0049 0302;00CE;0049 0302; # (Î; Î; I◌̂; Î; I◌̂; ) LATIN CAPITAL LETTER I WITH CIRCUMFLEX +00CF;00CF;0049 0308;00CF;0049 0308; # (Ï; Ï; I◌̈; Ï; I◌̈; ) LATIN CAPITAL LETTER I WITH DIAERESIS +00D1;00D1;004E 0303;00D1;004E 0303; # (Ñ; Ñ; N◌̃; Ñ; N◌̃; ) LATIN CAPITAL LETTER N WITH TILDE +00D2;00D2;004F 0300;00D2;004F 0300; # (Ò; Ò; O◌̀; Ò; O◌̀; ) LATIN CAPITAL LETTER O WITH GRAVE +00D3;00D3;004F 0301;00D3;004F 0301; # (Ó; Ó; O◌́; Ó; O◌́; ) LATIN CAPITAL LETTER O WITH ACUTE +00D4;00D4;004F 0302;00D4;004F 0302; # (Ô; Ô; O◌̂; Ô; O◌̂; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX +00D5;00D5;004F 0303;00D5;004F 0303; # (Õ; Õ; O◌̃; Õ; O◌̃; ) LATIN CAPITAL LETTER O WITH TILDE +00D6;00D6;004F 0308;00D6;004F 0308; # (Ö; Ö; O◌̈; Ö; O◌̈; ) LATIN CAPITAL LETTER O WITH DIAERESIS +00D9;00D9;0055 0300;00D9;0055 0300; # (Ù; Ù; U◌̀; Ù; U◌̀; ) LATIN CAPITAL LETTER U WITH GRAVE +00DA;00DA;0055 0301;00DA;0055 0301; # (Ú; Ú; U◌́; Ú; U◌́; ) LATIN CAPITAL LETTER U WITH ACUTE +00DB;00DB;0055 0302;00DB;0055 0302; # (Û; Û; U◌̂; Û; U◌̂; ) LATIN CAPITAL LETTER U WITH CIRCUMFLEX +00DC;00DC;0055 0308;00DC;0055 0308; # (Ü; Ü; U◌̈; Ü; U◌̈; ) LATIN CAPITAL LETTER U WITH DIAERESIS +00DD;00DD;0059 0301;00DD;0059 0301; # (Ý; Ý; Y◌́; Ý; Y◌́; ) LATIN CAPITAL LETTER Y WITH ACUTE +00E0;00E0;0061 0300;00E0;0061 0300; # (à; à; a◌̀; à; a◌̀; ) LATIN SMALL LETTER A WITH GRAVE +00E1;00E1;0061 0301;00E1;0061 0301; # (á; á; a◌́; á; a◌́; ) LATIN SMALL LETTER A WITH ACUTE +00E2;00E2;0061 0302;00E2;0061 0302; # (â; â; a◌̂; â; a◌̂; ) LATIN SMALL LETTER A WITH CIRCUMFLEX +00E3;00E3;0061 0303;00E3;0061 0303; # (ã; ã; a◌̃; ã; a◌̃; ) LATIN SMALL LETTER A WITH TILDE +00E4;00E4;0061 0308;00E4;0061 0308; # (ä; ä; a◌̈; ä; a◌̈; ) LATIN SMALL LETTER A WITH DIAERESIS +00E5;00E5;0061 030A;00E5;0061 030A; # (å; å; a◌̊; å; a◌̊; ) LATIN SMALL LETTER A WITH RING ABOVE +00E7;00E7;0063 0327;00E7;0063 0327; # (ç; ç; c◌̧; ç; c◌̧; ) LATIN SMALL LETTER C WITH CEDILLA +00E8;00E8;0065 0300;00E8;0065 0300; # (è; è; e◌̀; è; e◌̀; ) LATIN SMALL LETTER E WITH GRAVE +00E9;00E9;0065 0301;00E9;0065 0301; # (é; é; e◌́; é; e◌́; ) LATIN SMALL LETTER E WITH ACUTE +00EA;00EA;0065 0302;00EA;0065 0302; # (ê; ê; e◌̂; ê; e◌̂; ) LATIN SMALL LETTER E WITH CIRCUMFLEX +00EB;00EB;0065 0308;00EB;0065 0308; # (ë; ë; e◌̈; ë; e◌̈; ) LATIN SMALL LETTER E WITH DIAERESIS +00EC;00EC;0069 0300;00EC;0069 0300; # (ì; ì; i◌̀; ì; i◌̀; ) LATIN SMALL LETTER I WITH GRAVE +00ED;00ED;0069 0301;00ED;0069 0301; # (í; í; i◌́; í; i◌́; ) LATIN SMALL LETTER I WITH ACUTE +00EE;00EE;0069 0302;00EE;0069 0302; # (î; î; i◌̂; î; i◌̂; ) LATIN SMALL LETTER I WITH CIRCUMFLEX +00EF;00EF;0069 0308;00EF;0069 0308; # (ï; ï; i◌̈; ï; i◌̈; ) LATIN SMALL LETTER I WITH DIAERESIS +00F1;00F1;006E 0303;00F1;006E 0303; # (ñ; ñ; n◌̃; ñ; n◌̃; ) LATIN SMALL LETTER N WITH TILDE +00F2;00F2;006F 0300;00F2;006F 0300; # (ò; ò; o◌̀; ò; o◌̀; ) LATIN SMALL LETTER O WITH GRAVE +00F3;00F3;006F 0301;00F3;006F 0301; # (ó; ó; o◌́; ó; o◌́; ) LATIN SMALL LETTER O WITH ACUTE +00F4;00F4;006F 0302;00F4;006F 0302; # (ô; ô; o◌̂; ô; o◌̂; ) LATIN SMALL LETTER O WITH CIRCUMFLEX +00F5;00F5;006F 0303;00F5;006F 0303; # (õ; õ; o◌̃; õ; o◌̃; ) LATIN SMALL LETTER O WITH TILDE +00F6;00F6;006F 0308;00F6;006F 0308; # (ö; ö; o◌̈; ö; o◌̈; ) LATIN SMALL LETTER O WITH DIAERESIS +00F9;00F9;0075 0300;00F9;0075 0300; # (ù; ù; u◌̀; ù; u◌̀; ) LATIN SMALL LETTER U WITH GRAVE +00FA;00FA;0075 0301;00FA;0075 0301; # (ú; ú; u◌́; ú; u◌́; ) LATIN SMALL LETTER U WITH ACUTE +00FB;00FB;0075 0302;00FB;0075 0302; # (û; û; u◌̂; û; u◌̂; ) LATIN SMALL LETTER U WITH CIRCUMFLEX +00FC;00FC;0075 0308;00FC;0075 0308; # (ü; ü; u◌̈; ü; u◌̈; ) LATIN SMALL LETTER U WITH DIAERESIS +00FD;00FD;0079 0301;00FD;0079 0301; # (ý; ý; y◌́; ý; y◌́; ) LATIN SMALL LETTER Y WITH ACUTE +00FF;00FF;0079 0308;00FF;0079 0308; # (ÿ; ÿ; y◌̈; ÿ; y◌̈; ) LATIN SMALL LETTER Y WITH DIAERESIS +0100;0100;0041 0304;0100;0041 0304; # (Ā; Ā; A◌̄; Ā; A◌̄; ) LATIN CAPITAL LETTER A WITH MACRON +0101;0101;0061 0304;0101;0061 0304; # (ā; ā; a◌̄; ā; a◌̄; ) LATIN SMALL LETTER A WITH MACRON +0102;0102;0041 0306;0102;0041 0306; # (Ă; Ă; A◌̆; Ă; A◌̆; ) LATIN CAPITAL LETTER A WITH BREVE +0103;0103;0061 0306;0103;0061 0306; # (ă; ă; a◌̆; ă; a◌̆; ) LATIN SMALL LETTER A WITH BREVE +0104;0104;0041 0328;0104;0041 0328; # (Ą; Ą; A◌̨; Ą; A◌̨; ) LATIN CAPITAL LETTER A WITH OGONEK +0105;0105;0061 0328;0105;0061 0328; # (ą; ą; a◌̨; ą; a◌̨; ) LATIN SMALL LETTER A WITH OGONEK +0106;0106;0043 0301;0106;0043 0301; # (Ć; Ć; C◌́; Ć; C◌́; ) LATIN CAPITAL LETTER C WITH ACUTE +0107;0107;0063 0301;0107;0063 0301; # (ć; ć; c◌́; ć; c◌́; ) LATIN SMALL LETTER C WITH ACUTE +0108;0108;0043 0302;0108;0043 0302; # (Ĉ; Ĉ; C◌̂; Ĉ; C◌̂; ) LATIN CAPITAL LETTER C WITH CIRCUMFLEX +0109;0109;0063 0302;0109;0063 0302; # (ĉ; ĉ; c◌̂; ĉ; c◌̂; ) LATIN SMALL LETTER C WITH CIRCUMFLEX +010A;010A;0043 0307;010A;0043 0307; # (Ċ; Ċ; C◌̇; Ċ; C◌̇; ) LATIN CAPITAL LETTER C WITH DOT ABOVE +010B;010B;0063 0307;010B;0063 0307; # (ċ; ċ; c◌̇; ċ; c◌̇; ) LATIN SMALL LETTER C WITH DOT ABOVE +010C;010C;0043 030C;010C;0043 030C; # (Č; Č; C◌̌; Č; C◌̌; ) LATIN CAPITAL LETTER C WITH CARON +010D;010D;0063 030C;010D;0063 030C; # (č; č; c◌̌; č; c◌̌; ) LATIN SMALL LETTER C WITH CARON +010E;010E;0044 030C;010E;0044 030C; # (Ď; Ď; D◌̌; Ď; D◌̌; ) LATIN CAPITAL LETTER D WITH CARON +010F;010F;0064 030C;010F;0064 030C; # (ď; ď; d◌̌; ď; d◌̌; ) LATIN SMALL LETTER D WITH CARON +0112;0112;0045 0304;0112;0045 0304; # (Ē; Ē; E◌̄; Ē; E◌̄; ) LATIN CAPITAL LETTER E WITH MACRON +0113;0113;0065 0304;0113;0065 0304; # (ē; ē; e◌̄; ē; e◌̄; ) LATIN SMALL LETTER E WITH MACRON +0114;0114;0045 0306;0114;0045 0306; # (Ĕ; Ĕ; E◌̆; Ĕ; E◌̆; ) LATIN CAPITAL LETTER E WITH BREVE +0115;0115;0065 0306;0115;0065 0306; # (ĕ; ĕ; e◌̆; ĕ; e◌̆; ) LATIN SMALL LETTER E WITH BREVE +0116;0116;0045 0307;0116;0045 0307; # (Ė; Ė; E◌̇; Ė; E◌̇; ) LATIN CAPITAL LETTER E WITH DOT ABOVE +0117;0117;0065 0307;0117;0065 0307; # (ė; ė; e◌̇; ė; e◌̇; ) LATIN SMALL LETTER E WITH DOT ABOVE +0118;0118;0045 0328;0118;0045 0328; # (Ę; Ę; E◌̨; Ę; E◌̨; ) LATIN CAPITAL LETTER E WITH OGONEK +0119;0119;0065 0328;0119;0065 0328; # (ę; ę; e◌̨; ę; e◌̨; ) LATIN SMALL LETTER E WITH OGONEK +011A;011A;0045 030C;011A;0045 030C; # (Ě; Ě; E◌̌; Ě; E◌̌; ) LATIN CAPITAL LETTER E WITH CARON +011B;011B;0065 030C;011B;0065 030C; # (ě; ě; e◌̌; ě; e◌̌; ) LATIN SMALL LETTER E WITH CARON +011C;011C;0047 0302;011C;0047 0302; # (Ĝ; Ĝ; G◌̂; Ĝ; G◌̂; ) LATIN CAPITAL LETTER G WITH CIRCUMFLEX +011D;011D;0067 0302;011D;0067 0302; # (ĝ; ĝ; g◌̂; ĝ; g◌̂; ) LATIN SMALL LETTER G WITH CIRCUMFLEX +011E;011E;0047 0306;011E;0047 0306; # (Ğ; Ğ; G◌̆; Ğ; G◌̆; ) LATIN CAPITAL LETTER G WITH BREVE +011F;011F;0067 0306;011F;0067 0306; # (ğ; ğ; g◌̆; ğ; g◌̆; ) LATIN SMALL LETTER G WITH BREVE +0120;0120;0047 0307;0120;0047 0307; # (Ġ; Ġ; G◌̇; Ġ; G◌̇; ) LATIN CAPITAL LETTER G WITH DOT ABOVE +0121;0121;0067 0307;0121;0067 0307; # (ġ; ġ; g◌̇; ġ; g◌̇; ) LATIN SMALL LETTER G WITH DOT ABOVE +0122;0122;0047 0327;0122;0047 0327; # (Ģ; Ģ; G◌̧; Ģ; G◌̧; ) LATIN CAPITAL LETTER G WITH CEDILLA +0123;0123;0067 0327;0123;0067 0327; # (ģ; ģ; g◌̧; ģ; g◌̧; ) LATIN SMALL LETTER G WITH CEDILLA +0124;0124;0048 0302;0124;0048 0302; # (Ĥ; Ĥ; H◌̂; Ĥ; H◌̂; ) LATIN CAPITAL LETTER H WITH CIRCUMFLEX +0125;0125;0068 0302;0125;0068 0302; # (ĥ; ĥ; h◌̂; ĥ; h◌̂; ) LATIN SMALL LETTER H WITH CIRCUMFLEX +0128;0128;0049 0303;0128;0049 0303; # (Ĩ; Ĩ; I◌̃; Ĩ; I◌̃; ) LATIN CAPITAL LETTER I WITH TILDE +0129;0129;0069 0303;0129;0069 0303; # (ĩ; ĩ; i◌̃; ĩ; i◌̃; ) LATIN SMALL LETTER I WITH TILDE +012A;012A;0049 0304;012A;0049 0304; # (Ī; Ī; I◌̄; Ī; I◌̄; ) LATIN CAPITAL LETTER I WITH MACRON +012B;012B;0069 0304;012B;0069 0304; # (ī; ī; i◌̄; ī; i◌̄; ) LATIN SMALL LETTER I WITH MACRON +012C;012C;0049 0306;012C;0049 0306; # (Ĭ; Ĭ; I◌̆; Ĭ; I◌̆; ) LATIN CAPITAL LETTER I WITH BREVE +012D;012D;0069 0306;012D;0069 0306; # (ĭ; ĭ; i◌̆; ĭ; i◌̆; ) LATIN SMALL LETTER I WITH BREVE +012E;012E;0049 0328;012E;0049 0328; # (Į; Į; I◌̨; Į; I◌̨; ) LATIN CAPITAL LETTER I WITH OGONEK +012F;012F;0069 0328;012F;0069 0328; # (į; į; i◌̨; į; i◌̨; ) LATIN SMALL LETTER I WITH OGONEK +0130;0130;0049 0307;0130;0049 0307; # (İ; İ; I◌̇; İ; I◌̇; ) LATIN CAPITAL LETTER I WITH DOT ABOVE +0132;0132;0132;0049 004A;0049 004A; # (IJ; IJ; IJ; IJ; IJ; ) LATIN CAPITAL LIGATURE IJ +0133;0133;0133;0069 006A;0069 006A; # (ij; ij; ij; ij; ij; ) LATIN SMALL LIGATURE IJ +0134;0134;004A 0302;0134;004A 0302; # (Ĵ; Ĵ; J◌̂; Ĵ; J◌̂; ) LATIN CAPITAL LETTER J WITH CIRCUMFLEX +0135;0135;006A 0302;0135;006A 0302; # (ĵ; ĵ; j◌̂; ĵ; j◌̂; ) LATIN SMALL LETTER J WITH CIRCUMFLEX +0136;0136;004B 0327;0136;004B 0327; # (Ķ; Ķ; K◌̧; Ķ; K◌̧; ) LATIN CAPITAL LETTER K WITH CEDILLA +0137;0137;006B 0327;0137;006B 0327; # (ķ; ķ; k◌̧; ķ; k◌̧; ) LATIN SMALL LETTER K WITH CEDILLA +0139;0139;004C 0301;0139;004C 0301; # (Ĺ; Ĺ; L◌́; Ĺ; L◌́; ) LATIN CAPITAL LETTER L WITH ACUTE +013A;013A;006C 0301;013A;006C 0301; # (ĺ; ĺ; l◌́; ĺ; l◌́; ) LATIN SMALL LETTER L WITH ACUTE +013B;013B;004C 0327;013B;004C 0327; # (Ļ; Ļ; L◌̧; Ļ; L◌̧; ) LATIN CAPITAL LETTER L WITH CEDILLA +013C;013C;006C 0327;013C;006C 0327; # (ļ; ļ; l◌̧; ļ; l◌̧; ) LATIN SMALL LETTER L WITH CEDILLA +013D;013D;004C 030C;013D;004C 030C; # (Ľ; Ľ; L◌̌; Ľ; L◌̌; ) LATIN CAPITAL LETTER L WITH CARON +013E;013E;006C 030C;013E;006C 030C; # (ľ; ľ; l◌̌; ľ; l◌̌; ) LATIN SMALL LETTER L WITH CARON +013F;013F;013F;004C 00B7;004C 00B7; # (Ŀ; Ŀ; Ŀ; L·; L·; ) LATIN CAPITAL LETTER L WITH MIDDLE DOT +0140;0140;0140;006C 00B7;006C 00B7; # (ŀ; ŀ; ŀ; l·; l·; ) LATIN SMALL LETTER L WITH MIDDLE DOT +0143;0143;004E 0301;0143;004E 0301; # (Ń; Ń; N◌́; Ń; N◌́; ) LATIN CAPITAL LETTER N WITH ACUTE +0144;0144;006E 0301;0144;006E 0301; # (ń; ń; n◌́; ń; n◌́; ) LATIN SMALL LETTER N WITH ACUTE +0145;0145;004E 0327;0145;004E 0327; # (Ņ; Ņ; N◌̧; Ņ; N◌̧; ) LATIN CAPITAL LETTER N WITH CEDILLA +0146;0146;006E 0327;0146;006E 0327; # (ņ; ņ; n◌̧; ņ; n◌̧; ) LATIN SMALL LETTER N WITH CEDILLA +0147;0147;004E 030C;0147;004E 030C; # (Ň; Ň; N◌̌; Ň; N◌̌; ) LATIN CAPITAL LETTER N WITH CARON +0148;0148;006E 030C;0148;006E 030C; # (ň; ň; n◌̌; ň; n◌̌; ) LATIN SMALL LETTER N WITH CARON +0149;0149;0149;02BC 006E;02BC 006E; # (ʼn; ʼn; ʼn; ʼn; ʼn; ) LATIN SMALL LETTER N PRECEDED BY APOSTROPHE +014C;014C;004F 0304;014C;004F 0304; # (Ō; Ō; O◌̄; Ō; O◌̄; ) LATIN CAPITAL LETTER O WITH MACRON +014D;014D;006F 0304;014D;006F 0304; # (ō; ō; o◌̄; ō; o◌̄; ) LATIN SMALL LETTER O WITH MACRON +014E;014E;004F 0306;014E;004F 0306; # (Ŏ; Ŏ; O◌̆; Ŏ; O◌̆; ) LATIN CAPITAL LETTER O WITH BREVE +014F;014F;006F 0306;014F;006F 0306; # (ŏ; ŏ; o◌̆; ŏ; o◌̆; ) LATIN SMALL LETTER O WITH BREVE +0150;0150;004F 030B;0150;004F 030B; # (Ő; Ő; O◌̋; Ő; O◌̋; ) LATIN CAPITAL LETTER O WITH DOUBLE ACUTE +0151;0151;006F 030B;0151;006F 030B; # (ő; ő; o◌̋; ő; o◌̋; ) LATIN SMALL LETTER O WITH DOUBLE ACUTE +0154;0154;0052 0301;0154;0052 0301; # (Ŕ; Ŕ; R◌́; Ŕ; R◌́; ) LATIN CAPITAL LETTER R WITH ACUTE +0155;0155;0072 0301;0155;0072 0301; # (ŕ; ŕ; r◌́; ŕ; r◌́; ) LATIN SMALL LETTER R WITH ACUTE +0156;0156;0052 0327;0156;0052 0327; # (Ŗ; Ŗ; R◌̧; Ŗ; R◌̧; ) LATIN CAPITAL LETTER R WITH CEDILLA +0157;0157;0072 0327;0157;0072 0327; # (ŗ; ŗ; r◌̧; ŗ; r◌̧; ) LATIN SMALL LETTER R WITH CEDILLA +0158;0158;0052 030C;0158;0052 030C; # (Ř; Ř; R◌̌; Ř; R◌̌; ) LATIN CAPITAL LETTER R WITH CARON +0159;0159;0072 030C;0159;0072 030C; # (ř; ř; r◌̌; ř; r◌̌; ) LATIN SMALL LETTER R WITH CARON +015A;015A;0053 0301;015A;0053 0301; # (Ś; Ś; S◌́; Ś; S◌́; ) LATIN CAPITAL LETTER S WITH ACUTE +015B;015B;0073 0301;015B;0073 0301; # (ś; ś; s◌́; ś; s◌́; ) LATIN SMALL LETTER S WITH ACUTE +015C;015C;0053 0302;015C;0053 0302; # (Ŝ; Ŝ; S◌̂; Ŝ; S◌̂; ) LATIN CAPITAL LETTER S WITH CIRCUMFLEX +015D;015D;0073 0302;015D;0073 0302; # (ŝ; ŝ; s◌̂; ŝ; s◌̂; ) LATIN SMALL LETTER S WITH CIRCUMFLEX +015E;015E;0053 0327;015E;0053 0327; # (Ş; Ş; S◌̧; Ş; S◌̧; ) LATIN CAPITAL LETTER S WITH CEDILLA +015F;015F;0073 0327;015F;0073 0327; # (ş; ş; s◌̧; ş; s◌̧; ) LATIN SMALL LETTER S WITH CEDILLA +0160;0160;0053 030C;0160;0053 030C; # (Š; Š; S◌̌; Š; S◌̌; ) LATIN CAPITAL LETTER S WITH CARON +0161;0161;0073 030C;0161;0073 030C; # (š; š; s◌̌; š; s◌̌; ) LATIN SMALL LETTER S WITH CARON +0162;0162;0054 0327;0162;0054 0327; # (Ţ; Ţ; T◌̧; Ţ; T◌̧; ) LATIN CAPITAL LETTER T WITH CEDILLA +0163;0163;0074 0327;0163;0074 0327; # (ţ; ţ; t◌̧; ţ; t◌̧; ) LATIN SMALL LETTER T WITH CEDILLA +0164;0164;0054 030C;0164;0054 030C; # (Ť; Ť; T◌̌; Ť; T◌̌; ) LATIN CAPITAL LETTER T WITH CARON +0165;0165;0074 030C;0165;0074 030C; # (ť; ť; t◌̌; ť; t◌̌; ) LATIN SMALL LETTER T WITH CARON +0168;0168;0055 0303;0168;0055 0303; # (Ũ; Ũ; U◌̃; Ũ; U◌̃; ) LATIN CAPITAL LETTER U WITH TILDE +0169;0169;0075 0303;0169;0075 0303; # (ũ; ũ; u◌̃; ũ; u◌̃; ) LATIN SMALL LETTER U WITH TILDE +016A;016A;0055 0304;016A;0055 0304; # (Ū; Ū; U◌̄; Ū; U◌̄; ) LATIN CAPITAL LETTER U WITH MACRON +016B;016B;0075 0304;016B;0075 0304; # (ū; ū; u◌̄; ū; u◌̄; ) LATIN SMALL LETTER U WITH MACRON +016C;016C;0055 0306;016C;0055 0306; # (Ŭ; Ŭ; U◌̆; Ŭ; U◌̆; ) LATIN CAPITAL LETTER U WITH BREVE +016D;016D;0075 0306;016D;0075 0306; # (ŭ; ŭ; u◌̆; ŭ; u◌̆; ) LATIN SMALL LETTER U WITH BREVE +016E;016E;0055 030A;016E;0055 030A; # (Ů; Ů; U◌̊; Ů; U◌̊; ) LATIN CAPITAL LETTER U WITH RING ABOVE +016F;016F;0075 030A;016F;0075 030A; # (ů; ů; u◌̊; ů; u◌̊; ) LATIN SMALL LETTER U WITH RING ABOVE +0170;0170;0055 030B;0170;0055 030B; # (Ű; Ű; U◌̋; Ű; U◌̋; ) LATIN CAPITAL LETTER U WITH DOUBLE ACUTE +0171;0171;0075 030B;0171;0075 030B; # (ű; ű; u◌̋; ű; u◌̋; ) LATIN SMALL LETTER U WITH DOUBLE ACUTE +0172;0172;0055 0328;0172;0055 0328; # (Ų; Ų; U◌̨; Ų; U◌̨; ) LATIN CAPITAL LETTER U WITH OGONEK +0173;0173;0075 0328;0173;0075 0328; # (ų; ų; u◌̨; ų; u◌̨; ) LATIN SMALL LETTER U WITH OGONEK +0174;0174;0057 0302;0174;0057 0302; # (Ŵ; Ŵ; W◌̂; Ŵ; W◌̂; ) LATIN CAPITAL LETTER W WITH CIRCUMFLEX +0175;0175;0077 0302;0175;0077 0302; # (ŵ; ŵ; w◌̂; ŵ; w◌̂; ) LATIN SMALL LETTER W WITH CIRCUMFLEX +0176;0176;0059 0302;0176;0059 0302; # (Ŷ; Ŷ; Y◌̂; Ŷ; Y◌̂; ) LATIN CAPITAL LETTER Y WITH CIRCUMFLEX +0177;0177;0079 0302;0177;0079 0302; # (ŷ; ŷ; y◌̂; ŷ; y◌̂; ) LATIN SMALL LETTER Y WITH CIRCUMFLEX +0178;0178;0059 0308;0178;0059 0308; # (Ÿ; Ÿ; Y◌̈; Ÿ; Y◌̈; ) LATIN CAPITAL LETTER Y WITH DIAERESIS +0179;0179;005A 0301;0179;005A 0301; # (Ź; Ź; Z◌́; Ź; Z◌́; ) LATIN CAPITAL LETTER Z WITH ACUTE +017A;017A;007A 0301;017A;007A 0301; # (ź; ź; z◌́; ź; z◌́; ) LATIN SMALL LETTER Z WITH ACUTE +017B;017B;005A 0307;017B;005A 0307; # (Ż; Ż; Z◌̇; Ż; Z◌̇; ) LATIN CAPITAL LETTER Z WITH DOT ABOVE +017C;017C;007A 0307;017C;007A 0307; # (ż; ż; z◌̇; ż; z◌̇; ) LATIN SMALL LETTER Z WITH DOT ABOVE +017D;017D;005A 030C;017D;005A 030C; # (Ž; Ž; Z◌̌; Ž; Z◌̌; ) LATIN CAPITAL LETTER Z WITH CARON +017E;017E;007A 030C;017E;007A 030C; # (ž; ž; z◌̌; ž; z◌̌; ) LATIN SMALL LETTER Z WITH CARON +017F;017F;017F;0073;0073; # (ſ; ſ; ſ; s; s; ) LATIN SMALL LETTER LONG S +01A0;01A0;004F 031B;01A0;004F 031B; # (Ơ; Ơ; O◌̛; Ơ; O◌̛; ) LATIN CAPITAL LETTER O WITH HORN +01A1;01A1;006F 031B;01A1;006F 031B; # (ơ; ơ; o◌̛; ơ; o◌̛; ) LATIN SMALL LETTER O WITH HORN +01AF;01AF;0055 031B;01AF;0055 031B; # (Ư; Ư; U◌̛; Ư; U◌̛; ) LATIN CAPITAL LETTER U WITH HORN +01B0;01B0;0075 031B;01B0;0075 031B; # (ư; ư; u◌̛; ư; u◌̛; ) LATIN SMALL LETTER U WITH HORN +01C4;01C4;01C4;0044 017D;0044 005A 030C; # (DŽ; DŽ; DŽ; DŽ; DZ◌̌; ) LATIN CAPITAL LETTER DZ WITH CARON +01C5;01C5;01C5;0044 017E;0044 007A 030C; # (Dž; Dž; Dž; Dž; Dz◌̌; ) LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON +01C6;01C6;01C6;0064 017E;0064 007A 030C; # (dž; dž; dž; dž; dz◌̌; ) LATIN SMALL LETTER DZ WITH CARON +01C7;01C7;01C7;004C 004A;004C 004A; # (LJ; LJ; LJ; LJ; LJ; ) LATIN CAPITAL LETTER LJ +01C8;01C8;01C8;004C 006A;004C 006A; # (Lj; Lj; Lj; Lj; Lj; ) LATIN CAPITAL LETTER L WITH SMALL LETTER J +01C9;01C9;01C9;006C 006A;006C 006A; # (lj; lj; lj; lj; lj; ) LATIN SMALL LETTER LJ +01CA;01CA;01CA;004E 004A;004E 004A; # (NJ; NJ; NJ; NJ; NJ; ) LATIN CAPITAL LETTER NJ +01CB;01CB;01CB;004E 006A;004E 006A; # (Nj; Nj; Nj; Nj; Nj; ) LATIN CAPITAL LETTER N WITH SMALL LETTER J +01CC;01CC;01CC;006E 006A;006E 006A; # (nj; nj; nj; nj; nj; ) LATIN SMALL LETTER NJ +01CD;01CD;0041 030C;01CD;0041 030C; # (Ǎ; Ǎ; A◌̌; Ǎ; A◌̌; ) LATIN CAPITAL LETTER A WITH CARON +01CE;01CE;0061 030C;01CE;0061 030C; # (ǎ; ǎ; a◌̌; ǎ; a◌̌; ) LATIN SMALL LETTER A WITH CARON +01CF;01CF;0049 030C;01CF;0049 030C; # (Ǐ; Ǐ; I◌̌; Ǐ; I◌̌; ) LATIN CAPITAL LETTER I WITH CARON +01D0;01D0;0069 030C;01D0;0069 030C; # (ǐ; ǐ; i◌̌; ǐ; i◌̌; ) LATIN SMALL LETTER I WITH CARON +01D1;01D1;004F 030C;01D1;004F 030C; # (Ǒ; Ǒ; O◌̌; Ǒ; O◌̌; ) LATIN CAPITAL LETTER O WITH CARON +01D2;01D2;006F 030C;01D2;006F 030C; # (ǒ; ǒ; o◌̌; ǒ; o◌̌; ) LATIN SMALL LETTER O WITH CARON +01D3;01D3;0055 030C;01D3;0055 030C; # (Ǔ; Ǔ; U◌̌; Ǔ; U◌̌; ) LATIN CAPITAL LETTER U WITH CARON +01D4;01D4;0075 030C;01D4;0075 030C; # (ǔ; ǔ; u◌̌; ǔ; u◌̌; ) LATIN SMALL LETTER U WITH CARON +01D5;01D5;0055 0308 0304;01D5;0055 0308 0304; # (Ǖ; Ǖ; U◌̈◌̄; Ǖ; U◌̈◌̄; ) LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON +01D6;01D6;0075 0308 0304;01D6;0075 0308 0304; # (ǖ; ǖ; u◌̈◌̄; ǖ; u◌̈◌̄; ) LATIN SMALL LETTER U WITH DIAERESIS AND MACRON +01D7;01D7;0055 0308 0301;01D7;0055 0308 0301; # (Ǘ; Ǘ; U◌̈◌́; Ǘ; U◌̈◌́; ) LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE +01D8;01D8;0075 0308 0301;01D8;0075 0308 0301; # (ǘ; ǘ; u◌̈◌́; ǘ; u◌̈◌́; ) LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE +01D9;01D9;0055 0308 030C;01D9;0055 0308 030C; # (Ǚ; Ǚ; U◌̈◌̌; Ǚ; U◌̈◌̌; ) LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON +01DA;01DA;0075 0308 030C;01DA;0075 0308 030C; # (ǚ; ǚ; u◌̈◌̌; ǚ; u◌̈◌̌; ) LATIN SMALL LETTER U WITH DIAERESIS AND CARON +01DB;01DB;0055 0308 0300;01DB;0055 0308 0300; # (Ǜ; Ǜ; U◌̈◌̀; Ǜ; U◌̈◌̀; ) LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE +01DC;01DC;0075 0308 0300;01DC;0075 0308 0300; # (ǜ; ǜ; u◌̈◌̀; ǜ; u◌̈◌̀; ) LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE +01DE;01DE;0041 0308 0304;01DE;0041 0308 0304; # (Ǟ; Ǟ; A◌̈◌̄; Ǟ; A◌̈◌̄; ) LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON +01DF;01DF;0061 0308 0304;01DF;0061 0308 0304; # (ǟ; ǟ; a◌̈◌̄; ǟ; a◌̈◌̄; ) LATIN SMALL LETTER A WITH DIAERESIS AND MACRON +01E0;01E0;0041 0307 0304;01E0;0041 0307 0304; # (Ǡ; Ǡ; A◌̇◌̄; Ǡ; A◌̇◌̄; ) LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON +01E1;01E1;0061 0307 0304;01E1;0061 0307 0304; # (ǡ; ǡ; a◌̇◌̄; ǡ; a◌̇◌̄; ) LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON +01E2;01E2;00C6 0304;01E2;00C6 0304; # (Ǣ; Ǣ; Æ◌̄; Ǣ; Æ◌̄; ) LATIN CAPITAL LETTER AE WITH MACRON +01E3;01E3;00E6 0304;01E3;00E6 0304; # (ǣ; ǣ; æ◌̄; ǣ; æ◌̄; ) LATIN SMALL LETTER AE WITH MACRON +01E6;01E6;0047 030C;01E6;0047 030C; # (Ǧ; Ǧ; G◌̌; Ǧ; G◌̌; ) LATIN CAPITAL LETTER G WITH CARON +01E7;01E7;0067 030C;01E7;0067 030C; # (ǧ; ǧ; g◌̌; ǧ; g◌̌; ) LATIN SMALL LETTER G WITH CARON +01E8;01E8;004B 030C;01E8;004B 030C; # (Ǩ; Ǩ; K◌̌; Ǩ; K◌̌; ) LATIN CAPITAL LETTER K WITH CARON +01E9;01E9;006B 030C;01E9;006B 030C; # (ǩ; ǩ; k◌̌; ǩ; k◌̌; ) LATIN SMALL LETTER K WITH CARON +01EA;01EA;004F 0328;01EA;004F 0328; # (Ǫ; Ǫ; O◌̨; Ǫ; O◌̨; ) LATIN CAPITAL LETTER O WITH OGONEK +01EB;01EB;006F 0328;01EB;006F 0328; # (ǫ; ǫ; o◌̨; ǫ; o◌̨; ) LATIN SMALL LETTER O WITH OGONEK +01EC;01EC;004F 0328 0304;01EC;004F 0328 0304; # (Ǭ; Ǭ; O◌̨◌̄; Ǭ; O◌̨◌̄; ) LATIN CAPITAL LETTER O WITH OGONEK AND MACRON +01ED;01ED;006F 0328 0304;01ED;006F 0328 0304; # (ǭ; ǭ; o◌̨◌̄; ǭ; o◌̨◌̄; ) LATIN SMALL LETTER O WITH OGONEK AND MACRON +01EE;01EE;01B7 030C;01EE;01B7 030C; # (Ǯ; Ǯ; Ʒ◌̌; Ǯ; Ʒ◌̌; ) LATIN CAPITAL LETTER EZH WITH CARON +01EF;01EF;0292 030C;01EF;0292 030C; # (ǯ; ǯ; ʒ◌̌; ǯ; ʒ◌̌; ) LATIN SMALL LETTER EZH WITH CARON +01F0;01F0;006A 030C;01F0;006A 030C; # (ǰ; ǰ; j◌̌; ǰ; j◌̌; ) LATIN SMALL LETTER J WITH CARON +01F1;01F1;01F1;0044 005A;0044 005A; # (DZ; DZ; DZ; DZ; DZ; ) LATIN CAPITAL LETTER DZ +01F2;01F2;01F2;0044 007A;0044 007A; # (Dz; Dz; Dz; Dz; Dz; ) LATIN CAPITAL LETTER D WITH SMALL LETTER Z +01F3;01F3;01F3;0064 007A;0064 007A; # (dz; dz; dz; dz; dz; ) LATIN SMALL LETTER DZ +01F4;01F4;0047 0301;01F4;0047 0301; # (Ǵ; Ǵ; G◌́; Ǵ; G◌́; ) LATIN CAPITAL LETTER G WITH ACUTE +01F5;01F5;0067 0301;01F5;0067 0301; # (ǵ; ǵ; g◌́; ǵ; g◌́; ) LATIN SMALL LETTER G WITH ACUTE +01F8;01F8;004E 0300;01F8;004E 0300; # (Ǹ; Ǹ; N◌̀; Ǹ; N◌̀; ) LATIN CAPITAL LETTER N WITH GRAVE +01F9;01F9;006E 0300;01F9;006E 0300; # (ǹ; ǹ; n◌̀; ǹ; n◌̀; ) LATIN SMALL LETTER N WITH GRAVE +01FA;01FA;0041 030A 0301;01FA;0041 030A 0301; # (Ǻ; Ǻ; A◌̊◌́; Ǻ; A◌̊◌́; ) LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE +01FB;01FB;0061 030A 0301;01FB;0061 030A 0301; # (ǻ; ǻ; a◌̊◌́; ǻ; a◌̊◌́; ) LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE +01FC;01FC;00C6 0301;01FC;00C6 0301; # (Ǽ; Ǽ; Æ◌́; Ǽ; Æ◌́; ) LATIN CAPITAL LETTER AE WITH ACUTE +01FD;01FD;00E6 0301;01FD;00E6 0301; # (ǽ; ǽ; æ◌́; ǽ; æ◌́; ) LATIN SMALL LETTER AE WITH ACUTE +01FE;01FE;00D8 0301;01FE;00D8 0301; # (Ǿ; Ǿ; Ø◌́; Ǿ; Ø◌́; ) LATIN CAPITAL LETTER O WITH STROKE AND ACUTE +01FF;01FF;00F8 0301;01FF;00F8 0301; # (ǿ; ǿ; ø◌́; ǿ; ø◌́; ) LATIN SMALL LETTER O WITH STROKE AND ACUTE +0200;0200;0041 030F;0200;0041 030F; # (Ȁ; Ȁ; A◌̏; Ȁ; A◌̏; ) LATIN CAPITAL LETTER A WITH DOUBLE GRAVE +0201;0201;0061 030F;0201;0061 030F; # (ȁ; ȁ; a◌̏; ȁ; a◌̏; ) LATIN SMALL LETTER A WITH DOUBLE GRAVE +0202;0202;0041 0311;0202;0041 0311; # (Ȃ; Ȃ; A◌̑; Ȃ; A◌̑; ) LATIN CAPITAL LETTER A WITH INVERTED BREVE +0203;0203;0061 0311;0203;0061 0311; # (ȃ; ȃ; a◌̑; ȃ; a◌̑; ) LATIN SMALL LETTER A WITH INVERTED BREVE +0204;0204;0045 030F;0204;0045 030F; # (Ȅ; Ȅ; E◌̏; Ȅ; E◌̏; ) LATIN CAPITAL LETTER E WITH DOUBLE GRAVE +0205;0205;0065 030F;0205;0065 030F; # (ȅ; ȅ; e◌̏; ȅ; e◌̏; ) LATIN SMALL LETTER E WITH DOUBLE GRAVE +0206;0206;0045 0311;0206;0045 0311; # (Ȇ; Ȇ; E◌̑; Ȇ; E◌̑; ) LATIN CAPITAL LETTER E WITH INVERTED BREVE +0207;0207;0065 0311;0207;0065 0311; # (ȇ; ȇ; e◌̑; ȇ; e◌̑; ) LATIN SMALL LETTER E WITH INVERTED BREVE +0208;0208;0049 030F;0208;0049 030F; # (Ȉ; Ȉ; I◌̏; Ȉ; I◌̏; ) LATIN CAPITAL LETTER I WITH DOUBLE GRAVE +0209;0209;0069 030F;0209;0069 030F; # (ȉ; ȉ; i◌̏; ȉ; i◌̏; ) LATIN SMALL LETTER I WITH DOUBLE GRAVE +020A;020A;0049 0311;020A;0049 0311; # (Ȋ; Ȋ; I◌̑; Ȋ; I◌̑; ) LATIN CAPITAL LETTER I WITH INVERTED BREVE +020B;020B;0069 0311;020B;0069 0311; # (ȋ; ȋ; i◌̑; ȋ; i◌̑; ) LATIN SMALL LETTER I WITH INVERTED BREVE +020C;020C;004F 030F;020C;004F 030F; # (Ȍ; Ȍ; O◌̏; Ȍ; O◌̏; ) LATIN CAPITAL LETTER O WITH DOUBLE GRAVE +020D;020D;006F 030F;020D;006F 030F; # (ȍ; ȍ; o◌̏; ȍ; o◌̏; ) LATIN SMALL LETTER O WITH DOUBLE GRAVE +020E;020E;004F 0311;020E;004F 0311; # (Ȏ; Ȏ; O◌̑; Ȏ; O◌̑; ) LATIN CAPITAL LETTER O WITH INVERTED BREVE +020F;020F;006F 0311;020F;006F 0311; # (ȏ; ȏ; o◌̑; ȏ; o◌̑; ) LATIN SMALL LETTER O WITH INVERTED BREVE +0210;0210;0052 030F;0210;0052 030F; # (Ȑ; Ȑ; R◌̏; Ȑ; R◌̏; ) LATIN CAPITAL LETTER R WITH DOUBLE GRAVE +0211;0211;0072 030F;0211;0072 030F; # (ȑ; ȑ; r◌̏; ȑ; r◌̏; ) LATIN SMALL LETTER R WITH DOUBLE GRAVE +0212;0212;0052 0311;0212;0052 0311; # (Ȓ; Ȓ; R◌̑; Ȓ; R◌̑; ) LATIN CAPITAL LETTER R WITH INVERTED BREVE +0213;0213;0072 0311;0213;0072 0311; # (ȓ; ȓ; r◌̑; ȓ; r◌̑; ) LATIN SMALL LETTER R WITH INVERTED BREVE +0214;0214;0055 030F;0214;0055 030F; # (Ȕ; Ȕ; U◌̏; Ȕ; U◌̏; ) LATIN CAPITAL LETTER U WITH DOUBLE GRAVE +0215;0215;0075 030F;0215;0075 030F; # (ȕ; ȕ; u◌̏; ȕ; u◌̏; ) LATIN SMALL LETTER U WITH DOUBLE GRAVE +0216;0216;0055 0311;0216;0055 0311; # (Ȗ; Ȗ; U◌̑; Ȗ; U◌̑; ) LATIN CAPITAL LETTER U WITH INVERTED BREVE +0217;0217;0075 0311;0217;0075 0311; # (ȗ; ȗ; u◌̑; ȗ; u◌̑; ) LATIN SMALL LETTER U WITH INVERTED BREVE +0218;0218;0053 0326;0218;0053 0326; # (Ș; Ș; S◌̦; Ș; S◌̦; ) LATIN CAPITAL LETTER S WITH COMMA BELOW +0219;0219;0073 0326;0219;0073 0326; # (ș; ș; s◌̦; ș; s◌̦; ) LATIN SMALL LETTER S WITH COMMA BELOW +021A;021A;0054 0326;021A;0054 0326; # (Ț; Ț; T◌̦; Ț; T◌̦; ) LATIN CAPITAL LETTER T WITH COMMA BELOW +021B;021B;0074 0326;021B;0074 0326; # (ț; ț; t◌̦; ț; t◌̦; ) LATIN SMALL LETTER T WITH COMMA BELOW +021E;021E;0048 030C;021E;0048 030C; # (Ȟ; Ȟ; H◌̌; Ȟ; H◌̌; ) LATIN CAPITAL LETTER H WITH CARON +021F;021F;0068 030C;021F;0068 030C; # (ȟ; ȟ; h◌̌; ȟ; h◌̌; ) LATIN SMALL LETTER H WITH CARON +0226;0226;0041 0307;0226;0041 0307; # (Ȧ; Ȧ; A◌̇; Ȧ; A◌̇; ) LATIN CAPITAL LETTER A WITH DOT ABOVE +0227;0227;0061 0307;0227;0061 0307; # (ȧ; ȧ; a◌̇; ȧ; a◌̇; ) LATIN SMALL LETTER A WITH DOT ABOVE +0228;0228;0045 0327;0228;0045 0327; # (Ȩ; Ȩ; E◌̧; Ȩ; E◌̧; ) LATIN CAPITAL LETTER E WITH CEDILLA +0229;0229;0065 0327;0229;0065 0327; # (ȩ; ȩ; e◌̧; ȩ; e◌̧; ) LATIN SMALL LETTER E WITH CEDILLA +022A;022A;004F 0308 0304;022A;004F 0308 0304; # (Ȫ; Ȫ; O◌̈◌̄; Ȫ; O◌̈◌̄; ) LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON +022B;022B;006F 0308 0304;022B;006F 0308 0304; # (ȫ; ȫ; o◌̈◌̄; ȫ; o◌̈◌̄; ) LATIN SMALL LETTER O WITH DIAERESIS AND MACRON +022C;022C;004F 0303 0304;022C;004F 0303 0304; # (Ȭ; Ȭ; O◌̃◌̄; Ȭ; O◌̃◌̄; ) LATIN CAPITAL LETTER O WITH TILDE AND MACRON +022D;022D;006F 0303 0304;022D;006F 0303 0304; # (ȭ; ȭ; o◌̃◌̄; ȭ; o◌̃◌̄; ) LATIN SMALL LETTER O WITH TILDE AND MACRON +022E;022E;004F 0307;022E;004F 0307; # (Ȯ; Ȯ; O◌̇; Ȯ; O◌̇; ) LATIN CAPITAL LETTER O WITH DOT ABOVE +022F;022F;006F 0307;022F;006F 0307; # (ȯ; ȯ; o◌̇; ȯ; o◌̇; ) LATIN SMALL LETTER O WITH DOT ABOVE +0230;0230;004F 0307 0304;0230;004F 0307 0304; # (Ȱ; Ȱ; O◌̇◌̄; Ȱ; O◌̇◌̄; ) LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON +0231;0231;006F 0307 0304;0231;006F 0307 0304; # (ȱ; ȱ; o◌̇◌̄; ȱ; o◌̇◌̄; ) LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON +0232;0232;0059 0304;0232;0059 0304; # (Ȳ; Ȳ; Y◌̄; Ȳ; Y◌̄; ) LATIN CAPITAL LETTER Y WITH MACRON +0233;0233;0079 0304;0233;0079 0304; # (ȳ; ȳ; y◌̄; ȳ; y◌̄; ) LATIN SMALL LETTER Y WITH MACRON +02B0;02B0;02B0;0068;0068; # (ʰ; ʰ; ʰ; h; h; ) MODIFIER LETTER SMALL H +02B1;02B1;02B1;0266;0266; # (ʱ; ʱ; ʱ; ɦ; ɦ; ) MODIFIER LETTER SMALL H WITH HOOK +02B2;02B2;02B2;006A;006A; # (ʲ; ʲ; ʲ; j; j; ) MODIFIER LETTER SMALL J +02B3;02B3;02B3;0072;0072; # (ʳ; ʳ; ʳ; r; r; ) MODIFIER LETTER SMALL R +02B4;02B4;02B4;0279;0279; # (ʴ; ʴ; ʴ; ɹ; ɹ; ) MODIFIER LETTER SMALL TURNED R +02B5;02B5;02B5;027B;027B; # (ʵ; ʵ; ʵ; ɻ; ɻ; ) MODIFIER LETTER SMALL TURNED R WITH HOOK +02B6;02B6;02B6;0281;0281; # (ʶ; ʶ; ʶ; ʁ; ʁ; ) MODIFIER LETTER SMALL CAPITAL INVERTED R +02B7;02B7;02B7;0077;0077; # (ʷ; ʷ; ʷ; w; w; ) MODIFIER LETTER SMALL W +02B8;02B8;02B8;0079;0079; # (ʸ; ʸ; ʸ; y; y; ) MODIFIER LETTER SMALL Y +02D8;02D8;02D8;0020 0306;0020 0306; # (˘; ˘; ˘; ◌̆; ◌̆; ) BREVE +02D9;02D9;02D9;0020 0307;0020 0307; # (˙; ˙; ˙; ◌̇; ◌̇; ) DOT ABOVE +02DA;02DA;02DA;0020 030A;0020 030A; # (˚; ˚; ˚; ◌̊; ◌̊; ) RING ABOVE +02DB;02DB;02DB;0020 0328;0020 0328; # (˛; ˛; ˛; ◌̨; ◌̨; ) OGONEK +02DC;02DC;02DC;0020 0303;0020 0303; # (˜; ˜; ˜; ◌̃; ◌̃; ) SMALL TILDE +02DD;02DD;02DD;0020 030B;0020 030B; # (˝; ˝; ˝; ◌̋; ◌̋; ) DOUBLE ACUTE ACCENT +02E0;02E0;02E0;0263;0263; # (ˠ; ˠ; ˠ; ɣ; ɣ; ) MODIFIER LETTER SMALL GAMMA +02E1;02E1;02E1;006C;006C; # (ˡ; ˡ; ˡ; l; l; ) MODIFIER LETTER SMALL L +02E2;02E2;02E2;0073;0073; # (ˢ; ˢ; ˢ; s; s; ) MODIFIER LETTER SMALL S +02E3;02E3;02E3;0078;0078; # (ˣ; ˣ; ˣ; x; x; ) MODIFIER LETTER SMALL X +02E4;02E4;02E4;0295;0295; # (ˤ; ˤ; ˤ; ʕ; ʕ; ) MODIFIER LETTER SMALL REVERSED GLOTTAL STOP +0340;0300;0300;0300;0300; # (◌̀; ◌̀; ◌̀; ◌̀; ◌̀; ) COMBINING GRAVE TONE MARK +0341;0301;0301;0301;0301; # (◌́; ◌́; ◌́; ◌́; ◌́; ) COMBINING ACUTE TONE MARK +0343;0313;0313;0313;0313; # (◌̓; ◌̓; ◌̓; ◌̓; ◌̓; ) COMBINING GREEK KORONIS +0344;0308 0301;0308 0301;0308 0301;0308 0301; # (◌̈́; ◌̈◌́; ◌̈◌́; ◌̈◌́; ◌̈◌́; ) COMBINING GREEK DIALYTIKA TONOS +0374;02B9;02B9;02B9;02B9; # (ʹ; ʹ; ʹ; ʹ; ʹ; ) GREEK NUMERAL SIGN +037A;037A;037A;0020 0345;0020 0345; # (ͺ; ͺ; ͺ; ◌ͅ; ◌ͅ; ) GREEK YPOGEGRAMMENI +037E;003B;003B;003B;003B; # (;; ;; ;; ;; ;; ) GREEK QUESTION MARK +0384;0384;0384;0020 0301;0020 0301; # (΄; ΄; ΄; ◌́; ◌́; ) GREEK TONOS +0385;0385;00A8 0301;0020 0308 0301;0020 0308 0301; # (΅; ΅; ¨◌́; ◌̈◌́; ◌̈◌́; ) GREEK DIALYTIKA TONOS +0386;0386;0391 0301;0386;0391 0301; # (Ά; Ά; Α◌́; Ά; Α◌́; ) GREEK CAPITAL LETTER ALPHA WITH TONOS +0387;00B7;00B7;00B7;00B7; # (·; ·; ·; ·; ·; ) GREEK ANO TELEIA +0388;0388;0395 0301;0388;0395 0301; # (Έ; Έ; Ε◌́; Έ; Ε◌́; ) GREEK CAPITAL LETTER EPSILON WITH TONOS +0389;0389;0397 0301;0389;0397 0301; # (Ή; Ή; Η◌́; Ή; Η◌́; ) GREEK CAPITAL LETTER ETA WITH TONOS +038A;038A;0399 0301;038A;0399 0301; # (Ί; Ί; Ι◌́; Ί; Ι◌́; ) GREEK CAPITAL LETTER IOTA WITH TONOS +038C;038C;039F 0301;038C;039F 0301; # (Ό; Ό; Ο◌́; Ό; Ο◌́; ) GREEK CAPITAL LETTER OMICRON WITH TONOS +038E;038E;03A5 0301;038E;03A5 0301; # (Ύ; Ύ; Υ◌́; Ύ; Υ◌́; ) GREEK CAPITAL LETTER UPSILON WITH TONOS +038F;038F;03A9 0301;038F;03A9 0301; # (Ώ; Ώ; Ω◌́; Ώ; Ω◌́; ) GREEK CAPITAL LETTER OMEGA WITH TONOS +0390;0390;03B9 0308 0301;0390;03B9 0308 0301; # (ΐ; ΐ; ι◌̈◌́; ΐ; ι◌̈◌́; ) GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS +03AA;03AA;0399 0308;03AA;0399 0308; # (Ϊ; Ϊ; Ι◌̈; Ϊ; Ι◌̈; ) GREEK CAPITAL LETTER IOTA WITH DIALYTIKA +03AB;03AB;03A5 0308;03AB;03A5 0308; # (Ϋ; Ϋ; Υ◌̈; Ϋ; Υ◌̈; ) GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA +03AC;03AC;03B1 0301;03AC;03B1 0301; # (ά; ά; α◌́; ά; α◌́; ) GREEK SMALL LETTER ALPHA WITH TONOS +03AD;03AD;03B5 0301;03AD;03B5 0301; # (έ; έ; ε◌́; έ; ε◌́; ) GREEK SMALL LETTER EPSILON WITH TONOS +03AE;03AE;03B7 0301;03AE;03B7 0301; # (ή; ή; η◌́; ή; η◌́; ) GREEK SMALL LETTER ETA WITH TONOS +03AF;03AF;03B9 0301;03AF;03B9 0301; # (ί; ί; ι◌́; ί; ι◌́; ) GREEK SMALL LETTER IOTA WITH TONOS +03B0;03B0;03C5 0308 0301;03B0;03C5 0308 0301; # (ΰ; ΰ; υ◌̈◌́; ΰ; υ◌̈◌́; ) GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS +03CA;03CA;03B9 0308;03CA;03B9 0308; # (ϊ; ϊ; ι◌̈; ϊ; ι◌̈; ) GREEK SMALL LETTER IOTA WITH DIALYTIKA +03CB;03CB;03C5 0308;03CB;03C5 0308; # (ϋ; ϋ; υ◌̈; ϋ; υ◌̈; ) GREEK SMALL LETTER UPSILON WITH DIALYTIKA +03CC;03CC;03BF 0301;03CC;03BF 0301; # (ό; ό; ο◌́; ό; ο◌́; ) GREEK SMALL LETTER OMICRON WITH TONOS +03CD;03CD;03C5 0301;03CD;03C5 0301; # (ύ; ύ; υ◌́; ύ; υ◌́; ) GREEK SMALL LETTER UPSILON WITH TONOS +03CE;03CE;03C9 0301;03CE;03C9 0301; # (ώ; ώ; ω◌́; ώ; ω◌́; ) GREEK SMALL LETTER OMEGA WITH TONOS +03D0;03D0;03D0;03B2;03B2; # (ϐ; ϐ; ϐ; β; β; ) GREEK BETA SYMBOL +03D1;03D1;03D1;03B8;03B8; # (ϑ; ϑ; ϑ; θ; θ; ) GREEK THETA SYMBOL +03D2;03D2;03D2;03A5;03A5; # (ϒ; ϒ; ϒ; Υ; Υ; ) GREEK UPSILON WITH HOOK SYMBOL +03D3;03D3;03D2 0301;038E;03A5 0301; # (ϓ; ϓ; ϒ◌́; Ύ; Υ◌́; ) GREEK UPSILON WITH ACUTE AND HOOK SYMBOL +03D4;03D4;03D2 0308;03AB;03A5 0308; # (ϔ; ϔ; ϒ◌̈; Ϋ; Υ◌̈; ) GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL +03D5;03D5;03D5;03C6;03C6; # (ϕ; ϕ; ϕ; φ; φ; ) GREEK PHI SYMBOL +03D6;03D6;03D6;03C0;03C0; # (ϖ; ϖ; ϖ; π; π; ) GREEK PI SYMBOL +03F0;03F0;03F0;03BA;03BA; # (ϰ; ϰ; ϰ; κ; κ; ) GREEK KAPPA SYMBOL +03F1;03F1;03F1;03C1;03C1; # (ϱ; ϱ; ϱ; ρ; ρ; ) GREEK RHO SYMBOL +03F2;03F2;03F2;03C2;03C2; # (ϲ; ϲ; ϲ; ς; ς; ) GREEK LUNATE SIGMA SYMBOL +03F4;03F4;03F4;0398;0398; # (ϴ; ϴ; ϴ; Θ; Θ; ) GREEK CAPITAL THETA SYMBOL +03F5;03F5;03F5;03B5;03B5; # (ϵ; ϵ; ϵ; ε; ε; ) GREEK LUNATE EPSILON SYMBOL +0400;0400;0415 0300;0400;0415 0300; # (Ѐ; Ѐ; Е◌̀; Ѐ; Е◌̀; ) CYRILLIC CAPITAL LETTER IE WITH GRAVE +0401;0401;0415 0308;0401;0415 0308; # (Ё; Ё; Е◌̈; Ё; Е◌̈; ) CYRILLIC CAPITAL LETTER IO +0403;0403;0413 0301;0403;0413 0301; # (Ѓ; Ѓ; Г◌́; Ѓ; Г◌́; ) CYRILLIC CAPITAL LETTER GJE +0407;0407;0406 0308;0407;0406 0308; # (Ї; Ї; І◌̈; Ї; І◌̈; ) CYRILLIC CAPITAL LETTER YI +040C;040C;041A 0301;040C;041A 0301; # (Ќ; Ќ; К◌́; Ќ; К◌́; ) CYRILLIC CAPITAL LETTER KJE +040D;040D;0418 0300;040D;0418 0300; # (Ѝ; Ѝ; И◌̀; Ѝ; И◌̀; ) CYRILLIC CAPITAL LETTER I WITH GRAVE +040E;040E;0423 0306;040E;0423 0306; # (Ў; Ў; У◌̆; Ў; У◌̆; ) CYRILLIC CAPITAL LETTER SHORT U +0419;0419;0418 0306;0419;0418 0306; # (Й; Й; И◌̆; Й; И◌̆; ) CYRILLIC CAPITAL LETTER SHORT I +0439;0439;0438 0306;0439;0438 0306; # (й; й; и◌̆; й; и◌̆; ) CYRILLIC SMALL LETTER SHORT I +0450;0450;0435 0300;0450;0435 0300; # (ѐ; ѐ; е◌̀; ѐ; е◌̀; ) CYRILLIC SMALL LETTER IE WITH GRAVE +0451;0451;0435 0308;0451;0435 0308; # (ё; ё; е◌̈; ё; е◌̈; ) CYRILLIC SMALL LETTER IO +0453;0453;0433 0301;0453;0433 0301; # (ѓ; ѓ; г◌́; ѓ; г◌́; ) CYRILLIC SMALL LETTER GJE +0457;0457;0456 0308;0457;0456 0308; # (ї; ї; і◌̈; ї; і◌̈; ) CYRILLIC SMALL LETTER YI +045C;045C;043A 0301;045C;043A 0301; # (ќ; ќ; к◌́; ќ; к◌́; ) CYRILLIC SMALL LETTER KJE +045D;045D;0438 0300;045D;0438 0300; # (ѝ; ѝ; и◌̀; ѝ; и◌̀; ) CYRILLIC SMALL LETTER I WITH GRAVE +045E;045E;0443 0306;045E;0443 0306; # (ў; ў; у◌̆; ў; у◌̆; ) CYRILLIC SMALL LETTER SHORT U +0476;0476;0474 030F;0476;0474 030F; # (Ѷ; Ѷ; Ѵ◌̏; Ѷ; Ѵ◌̏; ) CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT +0477;0477;0475 030F;0477;0475 030F; # (ѷ; ѷ; ѵ◌̏; ѷ; ѵ◌̏; ) CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT +04C1;04C1;0416 0306;04C1;0416 0306; # (Ӂ; Ӂ; Ж◌̆; Ӂ; Ж◌̆; ) CYRILLIC CAPITAL LETTER ZHE WITH BREVE +04C2;04C2;0436 0306;04C2;0436 0306; # (ӂ; ӂ; ж◌̆; ӂ; ж◌̆; ) CYRILLIC SMALL LETTER ZHE WITH BREVE +04D0;04D0;0410 0306;04D0;0410 0306; # (Ӑ; Ӑ; А◌̆; Ӑ; А◌̆; ) CYRILLIC CAPITAL LETTER A WITH BREVE +04D1;04D1;0430 0306;04D1;0430 0306; # (ӑ; ӑ; а◌̆; ӑ; а◌̆; ) CYRILLIC SMALL LETTER A WITH BREVE +04D2;04D2;0410 0308;04D2;0410 0308; # (Ӓ; Ӓ; А◌̈; Ӓ; А◌̈; ) CYRILLIC CAPITAL LETTER A WITH DIAERESIS +04D3;04D3;0430 0308;04D3;0430 0308; # (ӓ; ӓ; а◌̈; ӓ; а◌̈; ) CYRILLIC SMALL LETTER A WITH DIAERESIS +04D6;04D6;0415 0306;04D6;0415 0306; # (Ӗ; Ӗ; Е◌̆; Ӗ; Е◌̆; ) CYRILLIC CAPITAL LETTER IE WITH BREVE +04D7;04D7;0435 0306;04D7;0435 0306; # (ӗ; ӗ; е◌̆; ӗ; е◌̆; ) CYRILLIC SMALL LETTER IE WITH BREVE +04DA;04DA;04D8 0308;04DA;04D8 0308; # (Ӛ; Ӛ; Ә◌̈; Ӛ; Ә◌̈; ) CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS +04DB;04DB;04D9 0308;04DB;04D9 0308; # (ӛ; ӛ; ә◌̈; ӛ; ә◌̈; ) CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS +04DC;04DC;0416 0308;04DC;0416 0308; # (Ӝ; Ӝ; Ж◌̈; Ӝ; Ж◌̈; ) CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS +04DD;04DD;0436 0308;04DD;0436 0308; # (ӝ; ӝ; ж◌̈; ӝ; ж◌̈; ) CYRILLIC SMALL LETTER ZHE WITH DIAERESIS +04DE;04DE;0417 0308;04DE;0417 0308; # (Ӟ; Ӟ; З◌̈; Ӟ; З◌̈; ) CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS +04DF;04DF;0437 0308;04DF;0437 0308; # (ӟ; ӟ; з◌̈; ӟ; з◌̈; ) CYRILLIC SMALL LETTER ZE WITH DIAERESIS +04E2;04E2;0418 0304;04E2;0418 0304; # (Ӣ; Ӣ; И◌̄; Ӣ; И◌̄; ) CYRILLIC CAPITAL LETTER I WITH MACRON +04E3;04E3;0438 0304;04E3;0438 0304; # (ӣ; ӣ; и◌̄; ӣ; и◌̄; ) CYRILLIC SMALL LETTER I WITH MACRON +04E4;04E4;0418 0308;04E4;0418 0308; # (Ӥ; Ӥ; И◌̈; Ӥ; И◌̈; ) CYRILLIC CAPITAL LETTER I WITH DIAERESIS +04E5;04E5;0438 0308;04E5;0438 0308; # (ӥ; ӥ; и◌̈; ӥ; и◌̈; ) CYRILLIC SMALL LETTER I WITH DIAERESIS +04E6;04E6;041E 0308;04E6;041E 0308; # (Ӧ; Ӧ; О◌̈; Ӧ; О◌̈; ) CYRILLIC CAPITAL LETTER O WITH DIAERESIS +04E7;04E7;043E 0308;04E7;043E 0308; # (ӧ; ӧ; о◌̈; ӧ; о◌̈; ) CYRILLIC SMALL LETTER O WITH DIAERESIS +04EA;04EA;04E8 0308;04EA;04E8 0308; # (Ӫ; Ӫ; Ө◌̈; Ӫ; Ө◌̈; ) CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS +04EB;04EB;04E9 0308;04EB;04E9 0308; # (ӫ; ӫ; ө◌̈; ӫ; ө◌̈; ) CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS +04EC;04EC;042D 0308;04EC;042D 0308; # (Ӭ; Ӭ; Э◌̈; Ӭ; Э◌̈; ) CYRILLIC CAPITAL LETTER E WITH DIAERESIS +04ED;04ED;044D 0308;04ED;044D 0308; # (ӭ; ӭ; э◌̈; ӭ; э◌̈; ) CYRILLIC SMALL LETTER E WITH DIAERESIS +04EE;04EE;0423 0304;04EE;0423 0304; # (Ӯ; Ӯ; У◌̄; Ӯ; У◌̄; ) CYRILLIC CAPITAL LETTER U WITH MACRON +04EF;04EF;0443 0304;04EF;0443 0304; # (ӯ; ӯ; у◌̄; ӯ; у◌̄; ) CYRILLIC SMALL LETTER U WITH MACRON +04F0;04F0;0423 0308;04F0;0423 0308; # (Ӱ; Ӱ; У◌̈; Ӱ; У◌̈; ) CYRILLIC CAPITAL LETTER U WITH DIAERESIS +04F1;04F1;0443 0308;04F1;0443 0308; # (ӱ; ӱ; у◌̈; ӱ; у◌̈; ) CYRILLIC SMALL LETTER U WITH DIAERESIS +04F2;04F2;0423 030B;04F2;0423 030B; # (Ӳ; Ӳ; У◌̋; Ӳ; У◌̋; ) CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE +04F3;04F3;0443 030B;04F3;0443 030B; # (ӳ; ӳ; у◌̋; ӳ; у◌̋; ) CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE +04F4;04F4;0427 0308;04F4;0427 0308; # (Ӵ; Ӵ; Ч◌̈; Ӵ; Ч◌̈; ) CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS +04F5;04F5;0447 0308;04F5;0447 0308; # (ӵ; ӵ; ч◌̈; ӵ; ч◌̈; ) CYRILLIC SMALL LETTER CHE WITH DIAERESIS +04F8;04F8;042B 0308;04F8;042B 0308; # (Ӹ; Ӹ; Ы◌̈; Ӹ; Ы◌̈; ) CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS +04F9;04F9;044B 0308;04F9;044B 0308; # (ӹ; ӹ; ы◌̈; ӹ; ы◌̈; ) CYRILLIC SMALL LETTER YERU WITH DIAERESIS +0587;0587;0587;0565 0582;0565 0582; # (և; և; և; եւ; եւ; ) ARMENIAN SMALL LIGATURE ECH YIWN +0622;0622;0627 0653;0622;0627 0653; # (آ; آ; ا◌ٓ; آ; ا◌ٓ; ) ARABIC LETTER ALEF WITH MADDA ABOVE +0623;0623;0627 0654;0623;0627 0654; # (أ; أ; ا◌ٔ; أ; ا◌ٔ; ) ARABIC LETTER ALEF WITH HAMZA ABOVE +0624;0624;0648 0654;0624;0648 0654; # (ؤ; ؤ; و◌ٔ; ؤ; و◌ٔ; ) ARABIC LETTER WAW WITH HAMZA ABOVE +0625;0625;0627 0655;0625;0627 0655; # (إ; إ; ا◌ٕ; إ; ا◌ٕ; ) ARABIC LETTER ALEF WITH HAMZA BELOW +0626;0626;064A 0654;0626;064A 0654; # (ئ; ئ; ي◌ٔ; ئ; ي◌ٔ; ) ARABIC LETTER YEH WITH HAMZA ABOVE +0675;0675;0675;0627 0674;0627 0674; # (ٵ; ٵ; ٵ; اٴ; اٴ; ) ARABIC LETTER HIGH HAMZA ALEF +0676;0676;0676;0648 0674;0648 0674; # (ٶ; ٶ; ٶ; وٴ; وٴ; ) ARABIC LETTER HIGH HAMZA WAW +0677;0677;0677;06C7 0674;06C7 0674; # (ٷ; ٷ; ٷ; ۇٴ; ۇٴ; ) ARABIC LETTER U WITH HAMZA ABOVE +0678;0678;0678;064A 0674;064A 0674; # (ٸ; ٸ; ٸ; يٴ; يٴ; ) ARABIC LETTER HIGH HAMZA YEH +06C0;06C0;06D5 0654;06C0;06D5 0654; # (ۀ; ۀ; ە◌ٔ; ۀ; ە◌ٔ; ) ARABIC LETTER HEH WITH YEH ABOVE +06C2;06C2;06C1 0654;06C2;06C1 0654; # (ۂ; ۂ; ہ◌ٔ; ۂ; ہ◌ٔ; ) ARABIC LETTER HEH GOAL WITH HAMZA ABOVE +06D3;06D3;06D2 0654;06D3;06D2 0654; # (ۓ; ۓ; ے◌ٔ; ۓ; ے◌ٔ; ) ARABIC LETTER YEH BARREE WITH HAMZA ABOVE +0929;0929;0928 093C;0929;0928 093C; # (ऩ; ऩ; न◌़; ऩ; न◌़; ) DEVANAGARI LETTER NNNA +0931;0931;0930 093C;0931;0930 093C; # (ऱ; ऱ; र◌़; ऱ; र◌़; ) DEVANAGARI LETTER RRA +0934;0934;0933 093C;0934;0933 093C; # (ऴ; ऴ; ळ◌़; ऴ; ळ◌़; ) DEVANAGARI LETTER LLLA +0958;0915 093C;0915 093C;0915 093C;0915 093C; # (क़; क◌़; क◌़; क◌़; क◌़; ) DEVANAGARI LETTER QA +0959;0916 093C;0916 093C;0916 093C;0916 093C; # (ख़; ख◌़; ख◌़; ख◌़; ख◌़; ) DEVANAGARI LETTER KHHA +095A;0917 093C;0917 093C;0917 093C;0917 093C; # (ग़; ग◌़; ग◌़; ग◌़; ग◌़; ) DEVANAGARI LETTER GHHA +095B;091C 093C;091C 093C;091C 093C;091C 093C; # (ज़; ज◌़; ज◌़; ज◌़; ज◌़; ) DEVANAGARI LETTER ZA +095C;0921 093C;0921 093C;0921 093C;0921 093C; # (ड़; ड◌़; ड◌़; ड◌़; ड◌़; ) DEVANAGARI LETTER DDDHA +095D;0922 093C;0922 093C;0922 093C;0922 093C; # (ढ़; ढ◌़; ढ◌़; ढ◌़; ढ◌़; ) DEVANAGARI LETTER RHA +095E;092B 093C;092B 093C;092B 093C;092B 093C; # (फ़; फ◌़; फ◌़; फ◌़; फ◌़; ) DEVANAGARI LETTER FA +095F;092F 093C;092F 093C;092F 093C;092F 093C; # (य़; य◌़; य◌़; य◌़; य◌़; ) DEVANAGARI LETTER YYA +09CB;09CB;09C7 09BE;09CB;09C7 09BE; # (ো; ো; ো; ো; ো; ) BENGALI VOWEL SIGN O +09CC;09CC;09C7 09D7;09CC;09C7 09D7; # (ৌ; ৌ; ৌ; ৌ; ৌ; ) BENGALI VOWEL SIGN AU +09DC;09A1 09BC;09A1 09BC;09A1 09BC;09A1 09BC; # (ড়; ড◌়; ড◌়; ড◌়; ড◌়; ) BENGALI LETTER RRA +09DD;09A2 09BC;09A2 09BC;09A2 09BC;09A2 09BC; # (ঢ়; ঢ◌়; ঢ◌়; ঢ◌়; ঢ◌়; ) BENGALI LETTER RHA +09DF;09AF 09BC;09AF 09BC;09AF 09BC;09AF 09BC; # (য়; য◌়; য◌়; য◌়; য◌়; ) BENGALI LETTER YYA +0A33;0A32 0A3C;0A32 0A3C;0A32 0A3C;0A32 0A3C; # (ਲ਼; ਲ◌਼; ਲ◌਼; ਲ◌਼; ਲ◌਼; ) GURMUKHI LETTER LLA +0A36;0A38 0A3C;0A38 0A3C;0A38 0A3C;0A38 0A3C; # (ਸ਼; ਸ◌਼; ਸ◌਼; ਸ◌਼; ਸ◌਼; ) GURMUKHI LETTER SHA +0A59;0A16 0A3C;0A16 0A3C;0A16 0A3C;0A16 0A3C; # (ਖ਼; ਖ◌਼; ਖ◌਼; ਖ◌਼; ਖ◌਼; ) GURMUKHI LETTER KHHA +0A5A;0A17 0A3C;0A17 0A3C;0A17 0A3C;0A17 0A3C; # (ਗ਼; ਗ◌਼; ਗ◌਼; ਗ◌਼; ਗ◌਼; ) GURMUKHI LETTER GHHA +0A5B;0A1C 0A3C;0A1C 0A3C;0A1C 0A3C;0A1C 0A3C; # (ਜ਼; ਜ◌਼; ਜ◌਼; ਜ◌਼; ਜ◌਼; ) GURMUKHI LETTER ZA +0A5E;0A2B 0A3C;0A2B 0A3C;0A2B 0A3C;0A2B 0A3C; # (ਫ਼; ਫ◌਼; ਫ◌਼; ਫ◌਼; ਫ◌਼; ) GURMUKHI LETTER FA +0B48;0B48;0B47 0B56;0B48;0B47 0B56; # (ୈ; ୈ; େ◌ୖ; ୈ; େ◌ୖ; ) ORIYA VOWEL SIGN AI +0B4B;0B4B;0B47 0B3E;0B4B;0B47 0B3E; # (ୋ; ୋ; ୋ; ୋ; ୋ; ) ORIYA VOWEL SIGN O +0B4C;0B4C;0B47 0B57;0B4C;0B47 0B57; # (ୌ; ୌ; ୌ; ୌ; ୌ; ) ORIYA VOWEL SIGN AU +0B5C;0B21 0B3C;0B21 0B3C;0B21 0B3C;0B21 0B3C; # (ଡ଼; ଡ◌଼; ଡ◌଼; ଡ◌଼; ଡ◌଼; ) ORIYA LETTER RRA +0B5D;0B22 0B3C;0B22 0B3C;0B22 0B3C;0B22 0B3C; # (ଢ଼; ଢ◌଼; ଢ◌଼; ଢ◌଼; ଢ◌଼; ) ORIYA LETTER RHA +0B94;0B94;0B92 0BD7;0B94;0B92 0BD7; # (ஔ; ஔ; ஔ; ஔ; ஔ; ) TAMIL LETTER AU +0BCA;0BCA;0BC6 0BBE;0BCA;0BC6 0BBE; # (ொ; ொ; ொ; ொ; ொ; ) TAMIL VOWEL SIGN O +0BCB;0BCB;0BC7 0BBE;0BCB;0BC7 0BBE; # (ோ; ோ; ோ; ோ; ோ; ) TAMIL VOWEL SIGN OO +0BCC;0BCC;0BC6 0BD7;0BCC;0BC6 0BD7; # (ௌ; ௌ; ௌ; ௌ; ௌ; ) TAMIL VOWEL SIGN AU +0C48;0C48;0C46 0C56;0C48;0C46 0C56; # (◌ై; ◌ై; ◌ె◌ౖ; ◌ై; ◌ె◌ౖ; ) TELUGU VOWEL SIGN AI +0CC0;0CC0;0CBF 0CD5;0CC0;0CBF 0CD5; # (ೀ; ೀ; ◌ೀ; ೀ; ◌ೀ; ) KANNADA VOWEL SIGN II +0CC7;0CC7;0CC6 0CD5;0CC7;0CC6 0CD5; # (ೇ; ೇ; ◌ೇ; ೇ; ◌ೇ; ) KANNADA VOWEL SIGN EE +0CC8;0CC8;0CC6 0CD6;0CC8;0CC6 0CD6; # (ೈ; ೈ; ◌ೈ; ೈ; ◌ೈ; ) KANNADA VOWEL SIGN AI +0CCA;0CCA;0CC6 0CC2;0CCA;0CC6 0CC2; # (ೊ; ೊ; ◌ೊ; ೊ; ◌ೊ; ) KANNADA VOWEL SIGN O +0CCB;0CCB;0CC6 0CC2 0CD5;0CCB;0CC6 0CC2 0CD5; # (ೋ; ೋ; ◌ೋ; ೋ; ◌ೋ; ) KANNADA VOWEL SIGN OO +0D4A;0D4A;0D46 0D3E;0D4A;0D46 0D3E; # (ൊ; ൊ; ൊ; ൊ; ൊ; ) MALAYALAM VOWEL SIGN O +0D4B;0D4B;0D47 0D3E;0D4B;0D47 0D3E; # (ോ; ോ; ോ; ോ; ോ; ) MALAYALAM VOWEL SIGN OO +0D4C;0D4C;0D46 0D57;0D4C;0D46 0D57; # (ൌ; ൌ; ൌ; ൌ; ൌ; ) MALAYALAM VOWEL SIGN AU +0DDA;0DDA;0DD9 0DCA;0DDA;0DD9 0DCA; # (ේ; ේ; ෙ◌්; ේ; ෙ◌්; ) SINHALA VOWEL SIGN DIGA KOMBUVA +0DDC;0DDC;0DD9 0DCF;0DDC;0DD9 0DCF; # (ො; ො; ො; ො; ො; ) SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA +0DDD;0DDD;0DD9 0DCF 0DCA;0DDD;0DD9 0DCF 0DCA; # (ෝ; ෝ; ො◌්; ෝ; ො◌්; ) SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA +0DDE;0DDE;0DD9 0DDF;0DDE;0DD9 0DDF; # (ෞ; ෞ; ෞ; ෞ; ෞ; ) SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA +0E33;0E33;0E33;0E4D 0E32;0E4D 0E32; # (ำ; ำ; ำ; ◌ํา; ◌ํา; ) THAI CHARACTER SARA AM +0EB3;0EB3;0EB3;0ECD 0EB2;0ECD 0EB2; # (ຳ; ຳ; ຳ; ◌ໍາ; ◌ໍາ; ) LAO VOWEL SIGN AM +0EDC;0EDC;0EDC;0EAB 0E99;0EAB 0E99; # (ໜ; ໜ; ໜ; ຫນ; ຫນ; ) LAO HO NO +0EDD;0EDD;0EDD;0EAB 0EA1;0EAB 0EA1; # (ໝ; ໝ; ໝ; ຫມ; ຫມ; ) LAO HO MO +0F0C;0F0C;0F0C;0F0B;0F0B; # (༌; ༌; ༌; ་; ་; ) TIBETAN MARK DELIMITER TSHEG BSTAR +0F43;0F42 0FB7;0F42 0FB7;0F42 0FB7;0F42 0FB7; # (གྷ; ག◌ྷ; ག◌ྷ; ག◌ྷ; ག◌ྷ; ) TIBETAN LETTER GHA +0F4D;0F4C 0FB7;0F4C 0FB7;0F4C 0FB7;0F4C 0FB7; # (ཌྷ; ཌ◌ྷ; ཌ◌ྷ; ཌ◌ྷ; ཌ◌ྷ; ) TIBETAN LETTER DDHA +0F52;0F51 0FB7;0F51 0FB7;0F51 0FB7;0F51 0FB7; # (དྷ; ད◌ྷ; ད◌ྷ; ད◌ྷ; ད◌ྷ; ) TIBETAN LETTER DHA +0F57;0F56 0FB7;0F56 0FB7;0F56 0FB7;0F56 0FB7; # (བྷ; བ◌ྷ; བ◌ྷ; བ◌ྷ; བ◌ྷ; ) TIBETAN LETTER BHA +0F5C;0F5B 0FB7;0F5B 0FB7;0F5B 0FB7;0F5B 0FB7; # (ཛྷ; ཛ◌ྷ; ཛ◌ྷ; ཛ◌ྷ; ཛ◌ྷ; ) TIBETAN LETTER DZHA +0F69;0F40 0FB5;0F40 0FB5;0F40 0FB5;0F40 0FB5; # (ཀྵ; ཀ◌ྵ; ཀ◌ྵ; ཀ◌ྵ; ཀ◌ྵ; ) TIBETAN LETTER KSSA +0F73;0F71 0F72;0F71 0F72;0F71 0F72;0F71 0F72; # (◌ཱི; ◌ཱ◌ི; ◌ཱ◌ི; ◌ཱ◌ི; ◌ཱ◌ི; ) TIBETAN VOWEL SIGN II +0F75;0F71 0F74;0F71 0F74;0F71 0F74;0F71 0F74; # (◌ཱུ; ◌ཱ◌ུ; ◌ཱ◌ུ; ◌ཱ◌ུ; ◌ཱ◌ུ; ) TIBETAN VOWEL SIGN UU +0F76;0FB2 0F80;0FB2 0F80;0FB2 0F80;0FB2 0F80; # (◌ྲྀ; ◌ྲ◌ྀ; ◌ྲ◌ྀ; ◌ྲ◌ྀ; ◌ྲ◌ྀ; ) TIBETAN VOWEL SIGN VOCALIC R +0F77;0F77;0F77;0FB2 0F71 0F80;0FB2 0F71 0F80; # (◌ཷ; ◌ཷ; ◌ཷ; ◌ྲ◌ཱ◌ྀ; ◌ྲ◌ཱ◌ྀ; ) TIBETAN VOWEL SIGN VOCALIC RR +0F78;0FB3 0F80;0FB3 0F80;0FB3 0F80;0FB3 0F80; # (◌ླྀ; ◌ླ◌ྀ; ◌ླ◌ྀ; ◌ླ◌ྀ; ◌ླ◌ྀ; ) TIBETAN VOWEL SIGN VOCALIC L +0F79;0F79;0F79;0FB3 0F71 0F80;0FB3 0F71 0F80; # (◌ཹ; ◌ཹ; ◌ཹ; ◌ླ◌ཱ◌ྀ; ◌ླ◌ཱ◌ྀ; ) TIBETAN VOWEL SIGN VOCALIC LL +0F81;0F71 0F80;0F71 0F80;0F71 0F80;0F71 0F80; # (◌ཱྀ; ◌ཱ◌ྀ; ◌ཱ◌ྀ; ◌ཱ◌ྀ; ◌ཱ◌ྀ; ) TIBETAN VOWEL SIGN REVERSED II +0F93;0F92 0FB7;0F92 0FB7;0F92 0FB7;0F92 0FB7; # (◌ྒྷ; ◌ྒ◌ྷ; ◌ྒ◌ྷ; ◌ྒ◌ྷ; ◌ྒ◌ྷ; ) TIBETAN SUBJOINED LETTER GHA +0F9D;0F9C 0FB7;0F9C 0FB7;0F9C 0FB7;0F9C 0FB7; # (◌ྜྷ; ◌ྜ◌ྷ; ◌ྜ◌ྷ; ◌ྜ◌ྷ; ◌ྜ◌ྷ; ) TIBETAN SUBJOINED LETTER DDHA +0FA2;0FA1 0FB7;0FA1 0FB7;0FA1 0FB7;0FA1 0FB7; # (◌ྡྷ; ◌ྡ◌ྷ; ◌ྡ◌ྷ; ◌ྡ◌ྷ; ◌ྡ◌ྷ; ) TIBETAN SUBJOINED LETTER DHA +0FA7;0FA6 0FB7;0FA6 0FB7;0FA6 0FB7;0FA6 0FB7; # (◌ྦྷ; ◌ྦ◌ྷ; ◌ྦ◌ྷ; ◌ྦ◌ྷ; ◌ྦ◌ྷ; ) TIBETAN SUBJOINED LETTER BHA +0FAC;0FAB 0FB7;0FAB 0FB7;0FAB 0FB7;0FAB 0FB7; # (◌ྫྷ; ◌ྫ◌ྷ; ◌ྫ◌ྷ; ◌ྫ◌ྷ; ◌ྫ◌ྷ; ) TIBETAN SUBJOINED LETTER DZHA +0FB9;0F90 0FB5;0F90 0FB5;0F90 0FB5;0F90 0FB5; # (◌ྐྵ; ◌ྐ◌ྵ; ◌ྐ◌ྵ; ◌ྐ◌ྵ; ◌ྐ◌ྵ; ) TIBETAN SUBJOINED LETTER KSSA +1026;1026;1025 102E;1026;1025 102E; # (ဦ; ဦ; ဥ◌ီ; ဦ; ဥ◌ီ; ) MYANMAR LETTER UU +1E00;1E00;0041 0325;1E00;0041 0325; # (Ḁ; Ḁ; A◌̥; Ḁ; A◌̥; ) LATIN CAPITAL LETTER A WITH RING BELOW +1E01;1E01;0061 0325;1E01;0061 0325; # (ḁ; ḁ; a◌̥; ḁ; a◌̥; ) LATIN SMALL LETTER A WITH RING BELOW +1E02;1E02;0042 0307;1E02;0042 0307; # (Ḃ; Ḃ; B◌̇; Ḃ; B◌̇; ) LATIN CAPITAL LETTER B WITH DOT ABOVE +1E03;1E03;0062 0307;1E03;0062 0307; # (ḃ; ḃ; b◌̇; ḃ; b◌̇; ) LATIN SMALL LETTER B WITH DOT ABOVE +1E04;1E04;0042 0323;1E04;0042 0323; # (Ḅ; Ḅ; B◌̣; Ḅ; B◌̣; ) LATIN CAPITAL LETTER B WITH DOT BELOW +1E05;1E05;0062 0323;1E05;0062 0323; # (ḅ; ḅ; b◌̣; ḅ; b◌̣; ) LATIN SMALL LETTER B WITH DOT BELOW +1E06;1E06;0042 0331;1E06;0042 0331; # (Ḇ; Ḇ; B◌̱; Ḇ; B◌̱; ) LATIN CAPITAL LETTER B WITH LINE BELOW +1E07;1E07;0062 0331;1E07;0062 0331; # (ḇ; ḇ; b◌̱; ḇ; b◌̱; ) LATIN SMALL LETTER B WITH LINE BELOW +1E08;1E08;0043 0327 0301;1E08;0043 0327 0301; # (Ḉ; Ḉ; C◌̧◌́; Ḉ; C◌̧◌́; ) LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE +1E09;1E09;0063 0327 0301;1E09;0063 0327 0301; # (ḉ; ḉ; c◌̧◌́; ḉ; c◌̧◌́; ) LATIN SMALL LETTER C WITH CEDILLA AND ACUTE +1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE +1E0B;1E0B;0064 0307;1E0B;0064 0307; # (ḋ; ḋ; d◌̇; ḋ; d◌̇; ) LATIN SMALL LETTER D WITH DOT ABOVE +1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW +1E0D;1E0D;0064 0323;1E0D;0064 0323; # (ḍ; ḍ; d◌̣; ḍ; d◌̣; ) LATIN SMALL LETTER D WITH DOT BELOW +1E0E;1E0E;0044 0331;1E0E;0044 0331; # (Ḏ; Ḏ; D◌̱; Ḏ; D◌̱; ) LATIN CAPITAL LETTER D WITH LINE BELOW +1E0F;1E0F;0064 0331;1E0F;0064 0331; # (ḏ; ḏ; d◌̱; ḏ; d◌̱; ) LATIN SMALL LETTER D WITH LINE BELOW +1E10;1E10;0044 0327;1E10;0044 0327; # (Ḑ; Ḑ; D◌̧; Ḑ; D◌̧; ) LATIN CAPITAL LETTER D WITH CEDILLA +1E11;1E11;0064 0327;1E11;0064 0327; # (ḑ; ḑ; d◌̧; ḑ; d◌̧; ) LATIN SMALL LETTER D WITH CEDILLA +1E12;1E12;0044 032D;1E12;0044 032D; # (Ḓ; Ḓ; D◌̭; Ḓ; D◌̭; ) LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW +1E13;1E13;0064 032D;1E13;0064 032D; # (ḓ; ḓ; d◌̭; ḓ; d◌̭; ) LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW +1E14;1E14;0045 0304 0300;1E14;0045 0304 0300; # (Ḕ; Ḕ; E◌̄◌̀; Ḕ; E◌̄◌̀; ) LATIN CAPITAL LETTER E WITH MACRON AND GRAVE +1E15;1E15;0065 0304 0300;1E15;0065 0304 0300; # (ḕ; ḕ; e◌̄◌̀; ḕ; e◌̄◌̀; ) LATIN SMALL LETTER E WITH MACRON AND GRAVE +1E16;1E16;0045 0304 0301;1E16;0045 0304 0301; # (Ḗ; Ḗ; E◌̄◌́; Ḗ; E◌̄◌́; ) LATIN CAPITAL LETTER E WITH MACRON AND ACUTE +1E17;1E17;0065 0304 0301;1E17;0065 0304 0301; # (ḗ; ḗ; e◌̄◌́; ḗ; e◌̄◌́; ) LATIN SMALL LETTER E WITH MACRON AND ACUTE +1E18;1E18;0045 032D;1E18;0045 032D; # (Ḙ; Ḙ; E◌̭; Ḙ; E◌̭; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW +1E19;1E19;0065 032D;1E19;0065 032D; # (ḙ; ḙ; e◌̭; ḙ; e◌̭; ) LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW +1E1A;1E1A;0045 0330;1E1A;0045 0330; # (Ḛ; Ḛ; E◌̰; Ḛ; E◌̰; ) LATIN CAPITAL LETTER E WITH TILDE BELOW +1E1B;1E1B;0065 0330;1E1B;0065 0330; # (ḛ; ḛ; e◌̰; ḛ; e◌̰; ) LATIN SMALL LETTER E WITH TILDE BELOW +1E1C;1E1C;0045 0327 0306;1E1C;0045 0327 0306; # (Ḝ; Ḝ; E◌̧◌̆; Ḝ; E◌̧◌̆; ) LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE +1E1D;1E1D;0065 0327 0306;1E1D;0065 0327 0306; # (ḝ; ḝ; e◌̧◌̆; ḝ; e◌̧◌̆; ) LATIN SMALL LETTER E WITH CEDILLA AND BREVE +1E1E;1E1E;0046 0307;1E1E;0046 0307; # (Ḟ; Ḟ; F◌̇; Ḟ; F◌̇; ) LATIN CAPITAL LETTER F WITH DOT ABOVE +1E1F;1E1F;0066 0307;1E1F;0066 0307; # (ḟ; ḟ; f◌̇; ḟ; f◌̇; ) LATIN SMALL LETTER F WITH DOT ABOVE +1E20;1E20;0047 0304;1E20;0047 0304; # (Ḡ; Ḡ; G◌̄; Ḡ; G◌̄; ) LATIN CAPITAL LETTER G WITH MACRON +1E21;1E21;0067 0304;1E21;0067 0304; # (ḡ; ḡ; g◌̄; ḡ; g◌̄; ) LATIN SMALL LETTER G WITH MACRON +1E22;1E22;0048 0307;1E22;0048 0307; # (Ḣ; Ḣ; H◌̇; Ḣ; H◌̇; ) LATIN CAPITAL LETTER H WITH DOT ABOVE +1E23;1E23;0068 0307;1E23;0068 0307; # (ḣ; ḣ; h◌̇; ḣ; h◌̇; ) LATIN SMALL LETTER H WITH DOT ABOVE +1E24;1E24;0048 0323;1E24;0048 0323; # (Ḥ; Ḥ; H◌̣; Ḥ; H◌̣; ) LATIN CAPITAL LETTER H WITH DOT BELOW +1E25;1E25;0068 0323;1E25;0068 0323; # (ḥ; ḥ; h◌̣; ḥ; h◌̣; ) LATIN SMALL LETTER H WITH DOT BELOW +1E26;1E26;0048 0308;1E26;0048 0308; # (Ḧ; Ḧ; H◌̈; Ḧ; H◌̈; ) LATIN CAPITAL LETTER H WITH DIAERESIS +1E27;1E27;0068 0308;1E27;0068 0308; # (ḧ; ḧ; h◌̈; ḧ; h◌̈; ) LATIN SMALL LETTER H WITH DIAERESIS +1E28;1E28;0048 0327;1E28;0048 0327; # (Ḩ; Ḩ; H◌̧; Ḩ; H◌̧; ) LATIN CAPITAL LETTER H WITH CEDILLA +1E29;1E29;0068 0327;1E29;0068 0327; # (ḩ; ḩ; h◌̧; ḩ; h◌̧; ) LATIN SMALL LETTER H WITH CEDILLA +1E2A;1E2A;0048 032E;1E2A;0048 032E; # (Ḫ; Ḫ; H◌̮; Ḫ; H◌̮; ) LATIN CAPITAL LETTER H WITH BREVE BELOW +1E2B;1E2B;0068 032E;1E2B;0068 032E; # (ḫ; ḫ; h◌̮; ḫ; h◌̮; ) LATIN SMALL LETTER H WITH BREVE BELOW +1E2C;1E2C;0049 0330;1E2C;0049 0330; # (Ḭ; Ḭ; I◌̰; Ḭ; I◌̰; ) LATIN CAPITAL LETTER I WITH TILDE BELOW +1E2D;1E2D;0069 0330;1E2D;0069 0330; # (ḭ; ḭ; i◌̰; ḭ; i◌̰; ) LATIN SMALL LETTER I WITH TILDE BELOW +1E2E;1E2E;0049 0308 0301;1E2E;0049 0308 0301; # (Ḯ; Ḯ; I◌̈◌́; Ḯ; I◌̈◌́; ) LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE +1E2F;1E2F;0069 0308 0301;1E2F;0069 0308 0301; # (ḯ; ḯ; i◌̈◌́; ḯ; i◌̈◌́; ) LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE +1E30;1E30;004B 0301;1E30;004B 0301; # (Ḱ; Ḱ; K◌́; Ḱ; K◌́; ) LATIN CAPITAL LETTER K WITH ACUTE +1E31;1E31;006B 0301;1E31;006B 0301; # (ḱ; ḱ; k◌́; ḱ; k◌́; ) LATIN SMALL LETTER K WITH ACUTE +1E32;1E32;004B 0323;1E32;004B 0323; # (Ḳ; Ḳ; K◌̣; Ḳ; K◌̣; ) LATIN CAPITAL LETTER K WITH DOT BELOW +1E33;1E33;006B 0323;1E33;006B 0323; # (ḳ; ḳ; k◌̣; ḳ; k◌̣; ) LATIN SMALL LETTER K WITH DOT BELOW +1E34;1E34;004B 0331;1E34;004B 0331; # (Ḵ; Ḵ; K◌̱; Ḵ; K◌̱; ) LATIN CAPITAL LETTER K WITH LINE BELOW +1E35;1E35;006B 0331;1E35;006B 0331; # (ḵ; ḵ; k◌̱; ḵ; k◌̱; ) LATIN SMALL LETTER K WITH LINE BELOW +1E36;1E36;004C 0323;1E36;004C 0323; # (Ḷ; Ḷ; L◌̣; Ḷ; L◌̣; ) LATIN CAPITAL LETTER L WITH DOT BELOW +1E37;1E37;006C 0323;1E37;006C 0323; # (ḷ; ḷ; l◌̣; ḷ; l◌̣; ) LATIN SMALL LETTER L WITH DOT BELOW +1E38;1E38;004C 0323 0304;1E38;004C 0323 0304; # (Ḹ; Ḹ; L◌̣◌̄; Ḹ; L◌̣◌̄; ) LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON +1E39;1E39;006C 0323 0304;1E39;006C 0323 0304; # (ḹ; ḹ; l◌̣◌̄; ḹ; l◌̣◌̄; ) LATIN SMALL LETTER L WITH DOT BELOW AND MACRON +1E3A;1E3A;004C 0331;1E3A;004C 0331; # (Ḻ; Ḻ; L◌̱; Ḻ; L◌̱; ) LATIN CAPITAL LETTER L WITH LINE BELOW +1E3B;1E3B;006C 0331;1E3B;006C 0331; # (ḻ; ḻ; l◌̱; ḻ; l◌̱; ) LATIN SMALL LETTER L WITH LINE BELOW +1E3C;1E3C;004C 032D;1E3C;004C 032D; # (Ḽ; Ḽ; L◌̭; Ḽ; L◌̭; ) LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW +1E3D;1E3D;006C 032D;1E3D;006C 032D; # (ḽ; ḽ; l◌̭; ḽ; l◌̭; ) LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW +1E3E;1E3E;004D 0301;1E3E;004D 0301; # (Ḿ; Ḿ; M◌́; Ḿ; M◌́; ) LATIN CAPITAL LETTER M WITH ACUTE +1E3F;1E3F;006D 0301;1E3F;006D 0301; # (ḿ; ḿ; m◌́; ḿ; m◌́; ) LATIN SMALL LETTER M WITH ACUTE +1E40;1E40;004D 0307;1E40;004D 0307; # (Ṁ; Ṁ; M◌̇; Ṁ; M◌̇; ) LATIN CAPITAL LETTER M WITH DOT ABOVE +1E41;1E41;006D 0307;1E41;006D 0307; # (ṁ; ṁ; m◌̇; ṁ; m◌̇; ) LATIN SMALL LETTER M WITH DOT ABOVE +1E42;1E42;004D 0323;1E42;004D 0323; # (Ṃ; Ṃ; M◌̣; Ṃ; M◌̣; ) LATIN CAPITAL LETTER M WITH DOT BELOW +1E43;1E43;006D 0323;1E43;006D 0323; # (ṃ; ṃ; m◌̣; ṃ; m◌̣; ) LATIN SMALL LETTER M WITH DOT BELOW +1E44;1E44;004E 0307;1E44;004E 0307; # (Ṅ; Ṅ; N◌̇; Ṅ; N◌̇; ) LATIN CAPITAL LETTER N WITH DOT ABOVE +1E45;1E45;006E 0307;1E45;006E 0307; # (ṅ; ṅ; n◌̇; ṅ; n◌̇; ) LATIN SMALL LETTER N WITH DOT ABOVE +1E46;1E46;004E 0323;1E46;004E 0323; # (Ṇ; Ṇ; N◌̣; Ṇ; N◌̣; ) LATIN CAPITAL LETTER N WITH DOT BELOW +1E47;1E47;006E 0323;1E47;006E 0323; # (ṇ; ṇ; n◌̣; ṇ; n◌̣; ) LATIN SMALL LETTER N WITH DOT BELOW +1E48;1E48;004E 0331;1E48;004E 0331; # (Ṉ; Ṉ; N◌̱; Ṉ; N◌̱; ) LATIN CAPITAL LETTER N WITH LINE BELOW +1E49;1E49;006E 0331;1E49;006E 0331; # (ṉ; ṉ; n◌̱; ṉ; n◌̱; ) LATIN SMALL LETTER N WITH LINE BELOW +1E4A;1E4A;004E 032D;1E4A;004E 032D; # (Ṋ; Ṋ; N◌̭; Ṋ; N◌̭; ) LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW +1E4B;1E4B;006E 032D;1E4B;006E 032D; # (ṋ; ṋ; n◌̭; ṋ; n◌̭; ) LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW +1E4C;1E4C;004F 0303 0301;1E4C;004F 0303 0301; # (Ṍ; Ṍ; O◌̃◌́; Ṍ; O◌̃◌́; ) LATIN CAPITAL LETTER O WITH TILDE AND ACUTE +1E4D;1E4D;006F 0303 0301;1E4D;006F 0303 0301; # (ṍ; ṍ; o◌̃◌́; ṍ; o◌̃◌́; ) LATIN SMALL LETTER O WITH TILDE AND ACUTE +1E4E;1E4E;004F 0303 0308;1E4E;004F 0303 0308; # (Ṏ; Ṏ; O◌̃◌̈; Ṏ; O◌̃◌̈; ) LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS +1E4F;1E4F;006F 0303 0308;1E4F;006F 0303 0308; # (ṏ; ṏ; o◌̃◌̈; ṏ; o◌̃◌̈; ) LATIN SMALL LETTER O WITH TILDE AND DIAERESIS +1E50;1E50;004F 0304 0300;1E50;004F 0304 0300; # (Ṑ; Ṑ; O◌̄◌̀; Ṑ; O◌̄◌̀; ) LATIN CAPITAL LETTER O WITH MACRON AND GRAVE +1E51;1E51;006F 0304 0300;1E51;006F 0304 0300; # (ṑ; ṑ; o◌̄◌̀; ṑ; o◌̄◌̀; ) LATIN SMALL LETTER O WITH MACRON AND GRAVE +1E52;1E52;004F 0304 0301;1E52;004F 0304 0301; # (Ṓ; Ṓ; O◌̄◌́; Ṓ; O◌̄◌́; ) LATIN CAPITAL LETTER O WITH MACRON AND ACUTE +1E53;1E53;006F 0304 0301;1E53;006F 0304 0301; # (ṓ; ṓ; o◌̄◌́; ṓ; o◌̄◌́; ) LATIN SMALL LETTER O WITH MACRON AND ACUTE +1E54;1E54;0050 0301;1E54;0050 0301; # (Ṕ; Ṕ; P◌́; Ṕ; P◌́; ) LATIN CAPITAL LETTER P WITH ACUTE +1E55;1E55;0070 0301;1E55;0070 0301; # (ṕ; ṕ; p◌́; ṕ; p◌́; ) LATIN SMALL LETTER P WITH ACUTE +1E56;1E56;0050 0307;1E56;0050 0307; # (Ṗ; Ṗ; P◌̇; Ṗ; P◌̇; ) LATIN CAPITAL LETTER P WITH DOT ABOVE +1E57;1E57;0070 0307;1E57;0070 0307; # (ṗ; ṗ; p◌̇; ṗ; p◌̇; ) LATIN SMALL LETTER P WITH DOT ABOVE +1E58;1E58;0052 0307;1E58;0052 0307; # (Ṙ; Ṙ; R◌̇; Ṙ; R◌̇; ) LATIN CAPITAL LETTER R WITH DOT ABOVE +1E59;1E59;0072 0307;1E59;0072 0307; # (ṙ; ṙ; r◌̇; ṙ; r◌̇; ) LATIN SMALL LETTER R WITH DOT ABOVE +1E5A;1E5A;0052 0323;1E5A;0052 0323; # (Ṛ; Ṛ; R◌̣; Ṛ; R◌̣; ) LATIN CAPITAL LETTER R WITH DOT BELOW +1E5B;1E5B;0072 0323;1E5B;0072 0323; # (ṛ; ṛ; r◌̣; ṛ; r◌̣; ) LATIN SMALL LETTER R WITH DOT BELOW +1E5C;1E5C;0052 0323 0304;1E5C;0052 0323 0304; # (Ṝ; Ṝ; R◌̣◌̄; Ṝ; R◌̣◌̄; ) LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON +1E5D;1E5D;0072 0323 0304;1E5D;0072 0323 0304; # (ṝ; ṝ; r◌̣◌̄; ṝ; r◌̣◌̄; ) LATIN SMALL LETTER R WITH DOT BELOW AND MACRON +1E5E;1E5E;0052 0331;1E5E;0052 0331; # (Ṟ; Ṟ; R◌̱; Ṟ; R◌̱; ) LATIN CAPITAL LETTER R WITH LINE BELOW +1E5F;1E5F;0072 0331;1E5F;0072 0331; # (ṟ; ṟ; r◌̱; ṟ; r◌̱; ) LATIN SMALL LETTER R WITH LINE BELOW +1E60;1E60;0053 0307;1E60;0053 0307; # (Ṡ; Ṡ; S◌̇; Ṡ; S◌̇; ) LATIN CAPITAL LETTER S WITH DOT ABOVE +1E61;1E61;0073 0307;1E61;0073 0307; # (ṡ; ṡ; s◌̇; ṡ; s◌̇; ) LATIN SMALL LETTER S WITH DOT ABOVE +1E62;1E62;0053 0323;1E62;0053 0323; # (Ṣ; Ṣ; S◌̣; Ṣ; S◌̣; ) LATIN CAPITAL LETTER S WITH DOT BELOW +1E63;1E63;0073 0323;1E63;0073 0323; # (ṣ; ṣ; s◌̣; ṣ; s◌̣; ) LATIN SMALL LETTER S WITH DOT BELOW +1E64;1E64;0053 0301 0307;1E64;0053 0301 0307; # (Ṥ; Ṥ; S◌́◌̇; Ṥ; S◌́◌̇; ) LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE +1E65;1E65;0073 0301 0307;1E65;0073 0301 0307; # (ṥ; ṥ; s◌́◌̇; ṥ; s◌́◌̇; ) LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE +1E66;1E66;0053 030C 0307;1E66;0053 030C 0307; # (Ṧ; Ṧ; S◌̌◌̇; Ṧ; S◌̌◌̇; ) LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE +1E67;1E67;0073 030C 0307;1E67;0073 030C 0307; # (ṧ; ṧ; s◌̌◌̇; ṧ; s◌̌◌̇; ) LATIN SMALL LETTER S WITH CARON AND DOT ABOVE +1E68;1E68;0053 0323 0307;1E68;0053 0323 0307; # (Ṩ; Ṩ; S◌̣◌̇; Ṩ; S◌̣◌̇; ) LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE +1E69;1E69;0073 0323 0307;1E69;0073 0323 0307; # (ṩ; ṩ; s◌̣◌̇; ṩ; s◌̣◌̇; ) LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE +1E6A;1E6A;0054 0307;1E6A;0054 0307; # (Ṫ; Ṫ; T◌̇; Ṫ; T◌̇; ) LATIN CAPITAL LETTER T WITH DOT ABOVE +1E6B;1E6B;0074 0307;1E6B;0074 0307; # (ṫ; ṫ; t◌̇; ṫ; t◌̇; ) LATIN SMALL LETTER T WITH DOT ABOVE +1E6C;1E6C;0054 0323;1E6C;0054 0323; # (Ṭ; Ṭ; T◌̣; Ṭ; T◌̣; ) LATIN CAPITAL LETTER T WITH DOT BELOW +1E6D;1E6D;0074 0323;1E6D;0074 0323; # (ṭ; ṭ; t◌̣; ṭ; t◌̣; ) LATIN SMALL LETTER T WITH DOT BELOW +1E6E;1E6E;0054 0331;1E6E;0054 0331; # (Ṯ; Ṯ; T◌̱; Ṯ; T◌̱; ) LATIN CAPITAL LETTER T WITH LINE BELOW +1E6F;1E6F;0074 0331;1E6F;0074 0331; # (ṯ; ṯ; t◌̱; ṯ; t◌̱; ) LATIN SMALL LETTER T WITH LINE BELOW +1E70;1E70;0054 032D;1E70;0054 032D; # (Ṱ; Ṱ; T◌̭; Ṱ; T◌̭; ) LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW +1E71;1E71;0074 032D;1E71;0074 032D; # (ṱ; ṱ; t◌̭; ṱ; t◌̭; ) LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW +1E72;1E72;0055 0324;1E72;0055 0324; # (Ṳ; Ṳ; U◌̤; Ṳ; U◌̤; ) LATIN CAPITAL LETTER U WITH DIAERESIS BELOW +1E73;1E73;0075 0324;1E73;0075 0324; # (ṳ; ṳ; u◌̤; ṳ; u◌̤; ) LATIN SMALL LETTER U WITH DIAERESIS BELOW +1E74;1E74;0055 0330;1E74;0055 0330; # (Ṵ; Ṵ; U◌̰; Ṵ; U◌̰; ) LATIN CAPITAL LETTER U WITH TILDE BELOW +1E75;1E75;0075 0330;1E75;0075 0330; # (ṵ; ṵ; u◌̰; ṵ; u◌̰; ) LATIN SMALL LETTER U WITH TILDE BELOW +1E76;1E76;0055 032D;1E76;0055 032D; # (Ṷ; Ṷ; U◌̭; Ṷ; U◌̭; ) LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW +1E77;1E77;0075 032D;1E77;0075 032D; # (ṷ; ṷ; u◌̭; ṷ; u◌̭; ) LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW +1E78;1E78;0055 0303 0301;1E78;0055 0303 0301; # (Ṹ; Ṹ; U◌̃◌́; Ṹ; U◌̃◌́; ) LATIN CAPITAL LETTER U WITH TILDE AND ACUTE +1E79;1E79;0075 0303 0301;1E79;0075 0303 0301; # (ṹ; ṹ; u◌̃◌́; ṹ; u◌̃◌́; ) LATIN SMALL LETTER U WITH TILDE AND ACUTE +1E7A;1E7A;0055 0304 0308;1E7A;0055 0304 0308; # (Ṻ; Ṻ; U◌̄◌̈; Ṻ; U◌̄◌̈; ) LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS +1E7B;1E7B;0075 0304 0308;1E7B;0075 0304 0308; # (ṻ; ṻ; u◌̄◌̈; ṻ; u◌̄◌̈; ) LATIN SMALL LETTER U WITH MACRON AND DIAERESIS +1E7C;1E7C;0056 0303;1E7C;0056 0303; # (Ṽ; Ṽ; V◌̃; Ṽ; V◌̃; ) LATIN CAPITAL LETTER V WITH TILDE +1E7D;1E7D;0076 0303;1E7D;0076 0303; # (ṽ; ṽ; v◌̃; ṽ; v◌̃; ) LATIN SMALL LETTER V WITH TILDE +1E7E;1E7E;0056 0323;1E7E;0056 0323; # (Ṿ; Ṿ; V◌̣; Ṿ; V◌̣; ) LATIN CAPITAL LETTER V WITH DOT BELOW +1E7F;1E7F;0076 0323;1E7F;0076 0323; # (ṿ; ṿ; v◌̣; ṿ; v◌̣; ) LATIN SMALL LETTER V WITH DOT BELOW +1E80;1E80;0057 0300;1E80;0057 0300; # (Ẁ; Ẁ; W◌̀; Ẁ; W◌̀; ) LATIN CAPITAL LETTER W WITH GRAVE +1E81;1E81;0077 0300;1E81;0077 0300; # (ẁ; ẁ; w◌̀; ẁ; w◌̀; ) LATIN SMALL LETTER W WITH GRAVE +1E82;1E82;0057 0301;1E82;0057 0301; # (Ẃ; Ẃ; W◌́; Ẃ; W◌́; ) LATIN CAPITAL LETTER W WITH ACUTE +1E83;1E83;0077 0301;1E83;0077 0301; # (ẃ; ẃ; w◌́; ẃ; w◌́; ) LATIN SMALL LETTER W WITH ACUTE +1E84;1E84;0057 0308;1E84;0057 0308; # (Ẅ; Ẅ; W◌̈; Ẅ; W◌̈; ) LATIN CAPITAL LETTER W WITH DIAERESIS +1E85;1E85;0077 0308;1E85;0077 0308; # (ẅ; ẅ; w◌̈; ẅ; w◌̈; ) LATIN SMALL LETTER W WITH DIAERESIS +1E86;1E86;0057 0307;1E86;0057 0307; # (Ẇ; Ẇ; W◌̇; Ẇ; W◌̇; ) LATIN CAPITAL LETTER W WITH DOT ABOVE +1E87;1E87;0077 0307;1E87;0077 0307; # (ẇ; ẇ; w◌̇; ẇ; w◌̇; ) LATIN SMALL LETTER W WITH DOT ABOVE +1E88;1E88;0057 0323;1E88;0057 0323; # (Ẉ; Ẉ; W◌̣; Ẉ; W◌̣; ) LATIN CAPITAL LETTER W WITH DOT BELOW +1E89;1E89;0077 0323;1E89;0077 0323; # (ẉ; ẉ; w◌̣; ẉ; w◌̣; ) LATIN SMALL LETTER W WITH DOT BELOW +1E8A;1E8A;0058 0307;1E8A;0058 0307; # (Ẋ; Ẋ; X◌̇; Ẋ; X◌̇; ) LATIN CAPITAL LETTER X WITH DOT ABOVE +1E8B;1E8B;0078 0307;1E8B;0078 0307; # (ẋ; ẋ; x◌̇; ẋ; x◌̇; ) LATIN SMALL LETTER X WITH DOT ABOVE +1E8C;1E8C;0058 0308;1E8C;0058 0308; # (Ẍ; Ẍ; X◌̈; Ẍ; X◌̈; ) LATIN CAPITAL LETTER X WITH DIAERESIS +1E8D;1E8D;0078 0308;1E8D;0078 0308; # (ẍ; ẍ; x◌̈; ẍ; x◌̈; ) LATIN SMALL LETTER X WITH DIAERESIS +1E8E;1E8E;0059 0307;1E8E;0059 0307; # (Ẏ; Ẏ; Y◌̇; Ẏ; Y◌̇; ) LATIN CAPITAL LETTER Y WITH DOT ABOVE +1E8F;1E8F;0079 0307;1E8F;0079 0307; # (ẏ; ẏ; y◌̇; ẏ; y◌̇; ) LATIN SMALL LETTER Y WITH DOT ABOVE +1E90;1E90;005A 0302;1E90;005A 0302; # (Ẑ; Ẑ; Z◌̂; Ẑ; Z◌̂; ) LATIN CAPITAL LETTER Z WITH CIRCUMFLEX +1E91;1E91;007A 0302;1E91;007A 0302; # (ẑ; ẑ; z◌̂; ẑ; z◌̂; ) LATIN SMALL LETTER Z WITH CIRCUMFLEX +1E92;1E92;005A 0323;1E92;005A 0323; # (Ẓ; Ẓ; Z◌̣; Ẓ; Z◌̣; ) LATIN CAPITAL LETTER Z WITH DOT BELOW +1E93;1E93;007A 0323;1E93;007A 0323; # (ẓ; ẓ; z◌̣; ẓ; z◌̣; ) LATIN SMALL LETTER Z WITH DOT BELOW +1E94;1E94;005A 0331;1E94;005A 0331; # (Ẕ; Ẕ; Z◌̱; Ẕ; Z◌̱; ) LATIN CAPITAL LETTER Z WITH LINE BELOW +1E95;1E95;007A 0331;1E95;007A 0331; # (ẕ; ẕ; z◌̱; ẕ; z◌̱; ) LATIN SMALL LETTER Z WITH LINE BELOW +1E96;1E96;0068 0331;1E96;0068 0331; # (ẖ; ẖ; h◌̱; ẖ; h◌̱; ) LATIN SMALL LETTER H WITH LINE BELOW +1E97;1E97;0074 0308;1E97;0074 0308; # (ẗ; ẗ; t◌̈; ẗ; t◌̈; ) LATIN SMALL LETTER T WITH DIAERESIS +1E98;1E98;0077 030A;1E98;0077 030A; # (ẘ; ẘ; w◌̊; ẘ; w◌̊; ) LATIN SMALL LETTER W WITH RING ABOVE +1E99;1E99;0079 030A;1E99;0079 030A; # (ẙ; ẙ; y◌̊; ẙ; y◌̊; ) LATIN SMALL LETTER Y WITH RING ABOVE +1E9A;1E9A;1E9A;0061 02BE;0061 02BE; # (ẚ; ẚ; ẚ; aʾ; aʾ; ) LATIN SMALL LETTER A WITH RIGHT HALF RING +1E9B;1E9B;017F 0307;1E61;0073 0307; # (ẛ; ẛ; ſ◌̇; ṡ; s◌̇; ) LATIN SMALL LETTER LONG S WITH DOT ABOVE +1EA0;1EA0;0041 0323;1EA0;0041 0323; # (Ạ; Ạ; A◌̣; Ạ; A◌̣; ) LATIN CAPITAL LETTER A WITH DOT BELOW +1EA1;1EA1;0061 0323;1EA1;0061 0323; # (ạ; ạ; a◌̣; ạ; a◌̣; ) LATIN SMALL LETTER A WITH DOT BELOW +1EA2;1EA2;0041 0309;1EA2;0041 0309; # (Ả; Ả; A◌̉; Ả; A◌̉; ) LATIN CAPITAL LETTER A WITH HOOK ABOVE +1EA3;1EA3;0061 0309;1EA3;0061 0309; # (ả; ả; a◌̉; ả; a◌̉; ) LATIN SMALL LETTER A WITH HOOK ABOVE +1EA4;1EA4;0041 0302 0301;1EA4;0041 0302 0301; # (Ấ; Ấ; A◌̂◌́; Ấ; A◌̂◌́; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE +1EA5;1EA5;0061 0302 0301;1EA5;0061 0302 0301; # (ấ; ấ; a◌̂◌́; ấ; a◌̂◌́; ) LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE +1EA6;1EA6;0041 0302 0300;1EA6;0041 0302 0300; # (Ầ; Ầ; A◌̂◌̀; Ầ; A◌̂◌̀; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE +1EA7;1EA7;0061 0302 0300;1EA7;0061 0302 0300; # (ầ; ầ; a◌̂◌̀; ầ; a◌̂◌̀; ) LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE +1EA8;1EA8;0041 0302 0309;1EA8;0041 0302 0309; # (Ẩ; Ẩ; A◌̂◌̉; Ẩ; A◌̂◌̉; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE +1EA9;1EA9;0061 0302 0309;1EA9;0061 0302 0309; # (ẩ; ẩ; a◌̂◌̉; ẩ; a◌̂◌̉; ) LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE +1EAA;1EAA;0041 0302 0303;1EAA;0041 0302 0303; # (Ẫ; Ẫ; A◌̂◌̃; Ẫ; A◌̂◌̃; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE +1EAB;1EAB;0061 0302 0303;1EAB;0061 0302 0303; # (ẫ; ẫ; a◌̂◌̃; ẫ; a◌̂◌̃; ) LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE +1EAC;1EAC;0041 0323 0302;1EAC;0041 0323 0302; # (Ậ; Ậ; A◌̣◌̂; Ậ; A◌̣◌̂; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW +1EAD;1EAD;0061 0323 0302;1EAD;0061 0323 0302; # (ậ; ậ; a◌̣◌̂; ậ; a◌̣◌̂; ) LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW +1EAE;1EAE;0041 0306 0301;1EAE;0041 0306 0301; # (Ắ; Ắ; A◌̆◌́; Ắ; A◌̆◌́; ) LATIN CAPITAL LETTER A WITH BREVE AND ACUTE +1EAF;1EAF;0061 0306 0301;1EAF;0061 0306 0301; # (ắ; ắ; a◌̆◌́; ắ; a◌̆◌́; ) LATIN SMALL LETTER A WITH BREVE AND ACUTE +1EB0;1EB0;0041 0306 0300;1EB0;0041 0306 0300; # (Ằ; Ằ; A◌̆◌̀; Ằ; A◌̆◌̀; ) LATIN CAPITAL LETTER A WITH BREVE AND GRAVE +1EB1;1EB1;0061 0306 0300;1EB1;0061 0306 0300; # (ằ; ằ; a◌̆◌̀; ằ; a◌̆◌̀; ) LATIN SMALL LETTER A WITH BREVE AND GRAVE +1EB2;1EB2;0041 0306 0309;1EB2;0041 0306 0309; # (Ẳ; Ẳ; A◌̆◌̉; Ẳ; A◌̆◌̉; ) LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE +1EB3;1EB3;0061 0306 0309;1EB3;0061 0306 0309; # (ẳ; ẳ; a◌̆◌̉; ẳ; a◌̆◌̉; ) LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE +1EB4;1EB4;0041 0306 0303;1EB4;0041 0306 0303; # (Ẵ; Ẵ; A◌̆◌̃; Ẵ; A◌̆◌̃; ) LATIN CAPITAL LETTER A WITH BREVE AND TILDE +1EB5;1EB5;0061 0306 0303;1EB5;0061 0306 0303; # (ẵ; ẵ; a◌̆◌̃; ẵ; a◌̆◌̃; ) LATIN SMALL LETTER A WITH BREVE AND TILDE +1EB6;1EB6;0041 0323 0306;1EB6;0041 0323 0306; # (Ặ; Ặ; A◌̣◌̆; Ặ; A◌̣◌̆; ) LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW +1EB7;1EB7;0061 0323 0306;1EB7;0061 0323 0306; # (ặ; ặ; a◌̣◌̆; ặ; a◌̣◌̆; ) LATIN SMALL LETTER A WITH BREVE AND DOT BELOW +1EB8;1EB8;0045 0323;1EB8;0045 0323; # (Ẹ; Ẹ; E◌̣; Ẹ; E◌̣; ) LATIN CAPITAL LETTER E WITH DOT BELOW +1EB9;1EB9;0065 0323;1EB9;0065 0323; # (ẹ; ẹ; e◌̣; ẹ; e◌̣; ) LATIN SMALL LETTER E WITH DOT BELOW +1EBA;1EBA;0045 0309;1EBA;0045 0309; # (Ẻ; Ẻ; E◌̉; Ẻ; E◌̉; ) LATIN CAPITAL LETTER E WITH HOOK ABOVE +1EBB;1EBB;0065 0309;1EBB;0065 0309; # (ẻ; ẻ; e◌̉; ẻ; e◌̉; ) LATIN SMALL LETTER E WITH HOOK ABOVE +1EBC;1EBC;0045 0303;1EBC;0045 0303; # (Ẽ; Ẽ; E◌̃; Ẽ; E◌̃; ) LATIN CAPITAL LETTER E WITH TILDE +1EBD;1EBD;0065 0303;1EBD;0065 0303; # (ẽ; ẽ; e◌̃; ẽ; e◌̃; ) LATIN SMALL LETTER E WITH TILDE +1EBE;1EBE;0045 0302 0301;1EBE;0045 0302 0301; # (Ế; Ế; E◌̂◌́; Ế; E◌̂◌́; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE +1EBF;1EBF;0065 0302 0301;1EBF;0065 0302 0301; # (ế; ế; e◌̂◌́; ế; e◌̂◌́; ) LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE +1EC0;1EC0;0045 0302 0300;1EC0;0045 0302 0300; # (Ề; Ề; E◌̂◌̀; Ề; E◌̂◌̀; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE +1EC1;1EC1;0065 0302 0300;1EC1;0065 0302 0300; # (ề; ề; e◌̂◌̀; ề; e◌̂◌̀; ) LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE +1EC2;1EC2;0045 0302 0309;1EC2;0045 0302 0309; # (Ể; Ể; E◌̂◌̉; Ể; E◌̂◌̉; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE +1EC3;1EC3;0065 0302 0309;1EC3;0065 0302 0309; # (ể; ể; e◌̂◌̉; ể; e◌̂◌̉; ) LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE +1EC4;1EC4;0045 0302 0303;1EC4;0045 0302 0303; # (Ễ; Ễ; E◌̂◌̃; Ễ; E◌̂◌̃; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE +1EC5;1EC5;0065 0302 0303;1EC5;0065 0302 0303; # (ễ; ễ; e◌̂◌̃; ễ; e◌̂◌̃; ) LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE +1EC6;1EC6;0045 0323 0302;1EC6;0045 0323 0302; # (Ệ; Ệ; E◌̣◌̂; Ệ; E◌̣◌̂; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW +1EC7;1EC7;0065 0323 0302;1EC7;0065 0323 0302; # (ệ; ệ; e◌̣◌̂; ệ; e◌̣◌̂; ) LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW +1EC8;1EC8;0049 0309;1EC8;0049 0309; # (Ỉ; Ỉ; I◌̉; Ỉ; I◌̉; ) LATIN CAPITAL LETTER I WITH HOOK ABOVE +1EC9;1EC9;0069 0309;1EC9;0069 0309; # (ỉ; ỉ; i◌̉; ỉ; i◌̉; ) LATIN SMALL LETTER I WITH HOOK ABOVE +1ECA;1ECA;0049 0323;1ECA;0049 0323; # (Ị; Ị; I◌̣; Ị; I◌̣; ) LATIN CAPITAL LETTER I WITH DOT BELOW +1ECB;1ECB;0069 0323;1ECB;0069 0323; # (ị; ị; i◌̣; ị; i◌̣; ) LATIN SMALL LETTER I WITH DOT BELOW +1ECC;1ECC;004F 0323;1ECC;004F 0323; # (Ọ; Ọ; O◌̣; Ọ; O◌̣; ) LATIN CAPITAL LETTER O WITH DOT BELOW +1ECD;1ECD;006F 0323;1ECD;006F 0323; # (ọ; ọ; o◌̣; ọ; o◌̣; ) LATIN SMALL LETTER O WITH DOT BELOW +1ECE;1ECE;004F 0309;1ECE;004F 0309; # (Ỏ; Ỏ; O◌̉; Ỏ; O◌̉; ) LATIN CAPITAL LETTER O WITH HOOK ABOVE +1ECF;1ECF;006F 0309;1ECF;006F 0309; # (ỏ; ỏ; o◌̉; ỏ; o◌̉; ) LATIN SMALL LETTER O WITH HOOK ABOVE +1ED0;1ED0;004F 0302 0301;1ED0;004F 0302 0301; # (Ố; Ố; O◌̂◌́; Ố; O◌̂◌́; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE +1ED1;1ED1;006F 0302 0301;1ED1;006F 0302 0301; # (ố; ố; o◌̂◌́; ố; o◌̂◌́; ) LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE +1ED2;1ED2;004F 0302 0300;1ED2;004F 0302 0300; # (Ồ; Ồ; O◌̂◌̀; Ồ; O◌̂◌̀; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE +1ED3;1ED3;006F 0302 0300;1ED3;006F 0302 0300; # (ồ; ồ; o◌̂◌̀; ồ; o◌̂◌̀; ) LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE +1ED4;1ED4;004F 0302 0309;1ED4;004F 0302 0309; # (Ổ; Ổ; O◌̂◌̉; Ổ; O◌̂◌̉; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE +1ED5;1ED5;006F 0302 0309;1ED5;006F 0302 0309; # (ổ; ổ; o◌̂◌̉; ổ; o◌̂◌̉; ) LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE +1ED6;1ED6;004F 0302 0303;1ED6;004F 0302 0303; # (Ỗ; Ỗ; O◌̂◌̃; Ỗ; O◌̂◌̃; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE +1ED7;1ED7;006F 0302 0303;1ED7;006F 0302 0303; # (ỗ; ỗ; o◌̂◌̃; ỗ; o◌̂◌̃; ) LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE +1ED8;1ED8;004F 0323 0302;1ED8;004F 0323 0302; # (Ộ; Ộ; O◌̣◌̂; Ộ; O◌̣◌̂; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW +1ED9;1ED9;006F 0323 0302;1ED9;006F 0323 0302; # (ộ; ộ; o◌̣◌̂; ộ; o◌̣◌̂; ) LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW +1EDA;1EDA;004F 031B 0301;1EDA;004F 031B 0301; # (Ớ; Ớ; O◌̛◌́; Ớ; O◌̛◌́; ) LATIN CAPITAL LETTER O WITH HORN AND ACUTE +1EDB;1EDB;006F 031B 0301;1EDB;006F 031B 0301; # (ớ; ớ; o◌̛◌́; ớ; o◌̛◌́; ) LATIN SMALL LETTER O WITH HORN AND ACUTE +1EDC;1EDC;004F 031B 0300;1EDC;004F 031B 0300; # (Ờ; Ờ; O◌̛◌̀; Ờ; O◌̛◌̀; ) LATIN CAPITAL LETTER O WITH HORN AND GRAVE +1EDD;1EDD;006F 031B 0300;1EDD;006F 031B 0300; # (ờ; ờ; o◌̛◌̀; ờ; o◌̛◌̀; ) LATIN SMALL LETTER O WITH HORN AND GRAVE +1EDE;1EDE;004F 031B 0309;1EDE;004F 031B 0309; # (Ở; Ở; O◌̛◌̉; Ở; O◌̛◌̉; ) LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE +1EDF;1EDF;006F 031B 0309;1EDF;006F 031B 0309; # (ở; ở; o◌̛◌̉; ở; o◌̛◌̉; ) LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE +1EE0;1EE0;004F 031B 0303;1EE0;004F 031B 0303; # (Ỡ; Ỡ; O◌̛◌̃; Ỡ; O◌̛◌̃; ) LATIN CAPITAL LETTER O WITH HORN AND TILDE +1EE1;1EE1;006F 031B 0303;1EE1;006F 031B 0303; # (ỡ; ỡ; o◌̛◌̃; ỡ; o◌̛◌̃; ) LATIN SMALL LETTER O WITH HORN AND TILDE +1EE2;1EE2;004F 031B 0323;1EE2;004F 031B 0323; # (Ợ; Ợ; O◌̛◌̣; Ợ; O◌̛◌̣; ) LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW +1EE3;1EE3;006F 031B 0323;1EE3;006F 031B 0323; # (ợ; ợ; o◌̛◌̣; ợ; o◌̛◌̣; ) LATIN SMALL LETTER O WITH HORN AND DOT BELOW +1EE4;1EE4;0055 0323;1EE4;0055 0323; # (Ụ; Ụ; U◌̣; Ụ; U◌̣; ) LATIN CAPITAL LETTER U WITH DOT BELOW +1EE5;1EE5;0075 0323;1EE5;0075 0323; # (ụ; ụ; u◌̣; ụ; u◌̣; ) LATIN SMALL LETTER U WITH DOT BELOW +1EE6;1EE6;0055 0309;1EE6;0055 0309; # (Ủ; Ủ; U◌̉; Ủ; U◌̉; ) LATIN CAPITAL LETTER U WITH HOOK ABOVE +1EE7;1EE7;0075 0309;1EE7;0075 0309; # (ủ; ủ; u◌̉; ủ; u◌̉; ) LATIN SMALL LETTER U WITH HOOK ABOVE +1EE8;1EE8;0055 031B 0301;1EE8;0055 031B 0301; # (Ứ; Ứ; U◌̛◌́; Ứ; U◌̛◌́; ) LATIN CAPITAL LETTER U WITH HORN AND ACUTE +1EE9;1EE9;0075 031B 0301;1EE9;0075 031B 0301; # (ứ; ứ; u◌̛◌́; ứ; u◌̛◌́; ) LATIN SMALL LETTER U WITH HORN AND ACUTE +1EEA;1EEA;0055 031B 0300;1EEA;0055 031B 0300; # (Ừ; Ừ; U◌̛◌̀; Ừ; U◌̛◌̀; ) LATIN CAPITAL LETTER U WITH HORN AND GRAVE +1EEB;1EEB;0075 031B 0300;1EEB;0075 031B 0300; # (ừ; ừ; u◌̛◌̀; ừ; u◌̛◌̀; ) LATIN SMALL LETTER U WITH HORN AND GRAVE +1EEC;1EEC;0055 031B 0309;1EEC;0055 031B 0309; # (Ử; Ử; U◌̛◌̉; Ử; U◌̛◌̉; ) LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE +1EED;1EED;0075 031B 0309;1EED;0075 031B 0309; # (ử; ử; u◌̛◌̉; ử; u◌̛◌̉; ) LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE +1EEE;1EEE;0055 031B 0303;1EEE;0055 031B 0303; # (Ữ; Ữ; U◌̛◌̃; Ữ; U◌̛◌̃; ) LATIN CAPITAL LETTER U WITH HORN AND TILDE +1EEF;1EEF;0075 031B 0303;1EEF;0075 031B 0303; # (ữ; ữ; u◌̛◌̃; ữ; u◌̛◌̃; ) LATIN SMALL LETTER U WITH HORN AND TILDE +1EF0;1EF0;0055 031B 0323;1EF0;0055 031B 0323; # (Ự; Ự; U◌̛◌̣; Ự; U◌̛◌̣; ) LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW +1EF1;1EF1;0075 031B 0323;1EF1;0075 031B 0323; # (ự; ự; u◌̛◌̣; ự; u◌̛◌̣; ) LATIN SMALL LETTER U WITH HORN AND DOT BELOW +1EF2;1EF2;0059 0300;1EF2;0059 0300; # (Ỳ; Ỳ; Y◌̀; Ỳ; Y◌̀; ) LATIN CAPITAL LETTER Y WITH GRAVE +1EF3;1EF3;0079 0300;1EF3;0079 0300; # (ỳ; ỳ; y◌̀; ỳ; y◌̀; ) LATIN SMALL LETTER Y WITH GRAVE +1EF4;1EF4;0059 0323;1EF4;0059 0323; # (Ỵ; Ỵ; Y◌̣; Ỵ; Y◌̣; ) LATIN CAPITAL LETTER Y WITH DOT BELOW +1EF5;1EF5;0079 0323;1EF5;0079 0323; # (ỵ; ỵ; y◌̣; ỵ; y◌̣; ) LATIN SMALL LETTER Y WITH DOT BELOW +1EF6;1EF6;0059 0309;1EF6;0059 0309; # (Ỷ; Ỷ; Y◌̉; Ỷ; Y◌̉; ) LATIN CAPITAL LETTER Y WITH HOOK ABOVE +1EF7;1EF7;0079 0309;1EF7;0079 0309; # (ỷ; ỷ; y◌̉; ỷ; y◌̉; ) LATIN SMALL LETTER Y WITH HOOK ABOVE +1EF8;1EF8;0059 0303;1EF8;0059 0303; # (Ỹ; Ỹ; Y◌̃; Ỹ; Y◌̃; ) LATIN CAPITAL LETTER Y WITH TILDE +1EF9;1EF9;0079 0303;1EF9;0079 0303; # (ỹ; ỹ; y◌̃; ỹ; y◌̃; ) LATIN SMALL LETTER Y WITH TILDE +1F00;1F00;03B1 0313;1F00;03B1 0313; # (ἀ; ἀ; α◌̓; ἀ; α◌̓; ) GREEK SMALL LETTER ALPHA WITH PSILI +1F01;1F01;03B1 0314;1F01;03B1 0314; # (ἁ; ἁ; α◌̔; ἁ; α◌̔; ) GREEK SMALL LETTER ALPHA WITH DASIA +1F02;1F02;03B1 0313 0300;1F02;03B1 0313 0300; # (ἂ; ἂ; α◌̓◌̀; ἂ; α◌̓◌̀; ) GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA +1F03;1F03;03B1 0314 0300;1F03;03B1 0314 0300; # (ἃ; ἃ; α◌̔◌̀; ἃ; α◌̔◌̀; ) GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA +1F04;1F04;03B1 0313 0301;1F04;03B1 0313 0301; # (ἄ; ἄ; α◌̓◌́; ἄ; α◌̓◌́; ) GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA +1F05;1F05;03B1 0314 0301;1F05;03B1 0314 0301; # (ἅ; ἅ; α◌̔◌́; ἅ; α◌̔◌́; ) GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA +1F06;1F06;03B1 0313 0342;1F06;03B1 0313 0342; # (ἆ; ἆ; α◌̓◌͂; ἆ; α◌̓◌͂; ) GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI +1F07;1F07;03B1 0314 0342;1F07;03B1 0314 0342; # (ἇ; ἇ; α◌̔◌͂; ἇ; α◌̔◌͂; ) GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI +1F08;1F08;0391 0313;1F08;0391 0313; # (Ἀ; Ἀ; Α◌̓; Ἀ; Α◌̓; ) GREEK CAPITAL LETTER ALPHA WITH PSILI +1F09;1F09;0391 0314;1F09;0391 0314; # (Ἁ; Ἁ; Α◌̔; Ἁ; Α◌̔; ) GREEK CAPITAL LETTER ALPHA WITH DASIA +1F0A;1F0A;0391 0313 0300;1F0A;0391 0313 0300; # (Ἂ; Ἂ; Α◌̓◌̀; Ἂ; Α◌̓◌̀; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA +1F0B;1F0B;0391 0314 0300;1F0B;0391 0314 0300; # (Ἃ; Ἃ; Α◌̔◌̀; Ἃ; Α◌̔◌̀; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA +1F0C;1F0C;0391 0313 0301;1F0C;0391 0313 0301; # (Ἄ; Ἄ; Α◌̓◌́; Ἄ; Α◌̓◌́; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA +1F0D;1F0D;0391 0314 0301;1F0D;0391 0314 0301; # (Ἅ; Ἅ; Α◌̔◌́; Ἅ; Α◌̔◌́; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA +1F0E;1F0E;0391 0313 0342;1F0E;0391 0313 0342; # (Ἆ; Ἆ; Α◌̓◌͂; Ἆ; Α◌̓◌͂; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI +1F0F;1F0F;0391 0314 0342;1F0F;0391 0314 0342; # (Ἇ; Ἇ; Α◌̔◌͂; Ἇ; Α◌̔◌͂; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI +1F10;1F10;03B5 0313;1F10;03B5 0313; # (ἐ; ἐ; ε◌̓; ἐ; ε◌̓; ) GREEK SMALL LETTER EPSILON WITH PSILI +1F11;1F11;03B5 0314;1F11;03B5 0314; # (ἑ; ἑ; ε◌̔; ἑ; ε◌̔; ) GREEK SMALL LETTER EPSILON WITH DASIA +1F12;1F12;03B5 0313 0300;1F12;03B5 0313 0300; # (ἒ; ἒ; ε◌̓◌̀; ἒ; ε◌̓◌̀; ) GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA +1F13;1F13;03B5 0314 0300;1F13;03B5 0314 0300; # (ἓ; ἓ; ε◌̔◌̀; ἓ; ε◌̔◌̀; ) GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA +1F14;1F14;03B5 0313 0301;1F14;03B5 0313 0301; # (ἔ; ἔ; ε◌̓◌́; ἔ; ε◌̓◌́; ) GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA +1F15;1F15;03B5 0314 0301;1F15;03B5 0314 0301; # (ἕ; ἕ; ε◌̔◌́; ἕ; ε◌̔◌́; ) GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA +1F18;1F18;0395 0313;1F18;0395 0313; # (Ἐ; Ἐ; Ε◌̓; Ἐ; Ε◌̓; ) GREEK CAPITAL LETTER EPSILON WITH PSILI +1F19;1F19;0395 0314;1F19;0395 0314; # (Ἑ; Ἑ; Ε◌̔; Ἑ; Ε◌̔; ) GREEK CAPITAL LETTER EPSILON WITH DASIA +1F1A;1F1A;0395 0313 0300;1F1A;0395 0313 0300; # (Ἒ; Ἒ; Ε◌̓◌̀; Ἒ; Ε◌̓◌̀; ) GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA +1F1B;1F1B;0395 0314 0300;1F1B;0395 0314 0300; # (Ἓ; Ἓ; Ε◌̔◌̀; Ἓ; Ε◌̔◌̀; ) GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA +1F1C;1F1C;0395 0313 0301;1F1C;0395 0313 0301; # (Ἔ; Ἔ; Ε◌̓◌́; Ἔ; Ε◌̓◌́; ) GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA +1F1D;1F1D;0395 0314 0301;1F1D;0395 0314 0301; # (Ἕ; Ἕ; Ε◌̔◌́; Ἕ; Ε◌̔◌́; ) GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA +1F20;1F20;03B7 0313;1F20;03B7 0313; # (ἠ; ἠ; η◌̓; ἠ; η◌̓; ) GREEK SMALL LETTER ETA WITH PSILI +1F21;1F21;03B7 0314;1F21;03B7 0314; # (ἡ; ἡ; η◌̔; ἡ; η◌̔; ) GREEK SMALL LETTER ETA WITH DASIA +1F22;1F22;03B7 0313 0300;1F22;03B7 0313 0300; # (ἢ; ἢ; η◌̓◌̀; ἢ; η◌̓◌̀; ) GREEK SMALL LETTER ETA WITH PSILI AND VARIA +1F23;1F23;03B7 0314 0300;1F23;03B7 0314 0300; # (ἣ; ἣ; η◌̔◌̀; ἣ; η◌̔◌̀; ) GREEK SMALL LETTER ETA WITH DASIA AND VARIA +1F24;1F24;03B7 0313 0301;1F24;03B7 0313 0301; # (ἤ; ἤ; η◌̓◌́; ἤ; η◌̓◌́; ) GREEK SMALL LETTER ETA WITH PSILI AND OXIA +1F25;1F25;03B7 0314 0301;1F25;03B7 0314 0301; # (ἥ; ἥ; η◌̔◌́; ἥ; η◌̔◌́; ) GREEK SMALL LETTER ETA WITH DASIA AND OXIA +1F26;1F26;03B7 0313 0342;1F26;03B7 0313 0342; # (ἦ; ἦ; η◌̓◌͂; ἦ; η◌̓◌͂; ) GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI +1F27;1F27;03B7 0314 0342;1F27;03B7 0314 0342; # (ἧ; ἧ; η◌̔◌͂; ἧ; η◌̔◌͂; ) GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI +1F28;1F28;0397 0313;1F28;0397 0313; # (Ἠ; Ἠ; Η◌̓; Ἠ; Η◌̓; ) GREEK CAPITAL LETTER ETA WITH PSILI +1F29;1F29;0397 0314;1F29;0397 0314; # (Ἡ; Ἡ; Η◌̔; Ἡ; Η◌̔; ) GREEK CAPITAL LETTER ETA WITH DASIA +1F2A;1F2A;0397 0313 0300;1F2A;0397 0313 0300; # (Ἢ; Ἢ; Η◌̓◌̀; Ἢ; Η◌̓◌̀; ) GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA +1F2B;1F2B;0397 0314 0300;1F2B;0397 0314 0300; # (Ἣ; Ἣ; Η◌̔◌̀; Ἣ; Η◌̔◌̀; ) GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA +1F2C;1F2C;0397 0313 0301;1F2C;0397 0313 0301; # (Ἤ; Ἤ; Η◌̓◌́; Ἤ; Η◌̓◌́; ) GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA +1F2D;1F2D;0397 0314 0301;1F2D;0397 0314 0301; # (Ἥ; Ἥ; Η◌̔◌́; Ἥ; Η◌̔◌́; ) GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA +1F2E;1F2E;0397 0313 0342;1F2E;0397 0313 0342; # (Ἦ; Ἦ; Η◌̓◌͂; Ἦ; Η◌̓◌͂; ) GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI +1F2F;1F2F;0397 0314 0342;1F2F;0397 0314 0342; # (Ἧ; Ἧ; Η◌̔◌͂; Ἧ; Η◌̔◌͂; ) GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI +1F30;1F30;03B9 0313;1F30;03B9 0313; # (ἰ; ἰ; ι◌̓; ἰ; ι◌̓; ) GREEK SMALL LETTER IOTA WITH PSILI +1F31;1F31;03B9 0314;1F31;03B9 0314; # (ἱ; ἱ; ι◌̔; ἱ; ι◌̔; ) GREEK SMALL LETTER IOTA WITH DASIA +1F32;1F32;03B9 0313 0300;1F32;03B9 0313 0300; # (ἲ; ἲ; ι◌̓◌̀; ἲ; ι◌̓◌̀; ) GREEK SMALL LETTER IOTA WITH PSILI AND VARIA +1F33;1F33;03B9 0314 0300;1F33;03B9 0314 0300; # (ἳ; ἳ; ι◌̔◌̀; ἳ; ι◌̔◌̀; ) GREEK SMALL LETTER IOTA WITH DASIA AND VARIA +1F34;1F34;03B9 0313 0301;1F34;03B9 0313 0301; # (ἴ; ἴ; ι◌̓◌́; ἴ; ι◌̓◌́; ) GREEK SMALL LETTER IOTA WITH PSILI AND OXIA +1F35;1F35;03B9 0314 0301;1F35;03B9 0314 0301; # (ἵ; ἵ; ι◌̔◌́; ἵ; ι◌̔◌́; ) GREEK SMALL LETTER IOTA WITH DASIA AND OXIA +1F36;1F36;03B9 0313 0342;1F36;03B9 0313 0342; # (ἶ; ἶ; ι◌̓◌͂; ἶ; ι◌̓◌͂; ) GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI +1F37;1F37;03B9 0314 0342;1F37;03B9 0314 0342; # (ἷ; ἷ; ι◌̔◌͂; ἷ; ι◌̔◌͂; ) GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI +1F38;1F38;0399 0313;1F38;0399 0313; # (Ἰ; Ἰ; Ι◌̓; Ἰ; Ι◌̓; ) GREEK CAPITAL LETTER IOTA WITH PSILI +1F39;1F39;0399 0314;1F39;0399 0314; # (Ἱ; Ἱ; Ι◌̔; Ἱ; Ι◌̔; ) GREEK CAPITAL LETTER IOTA WITH DASIA +1F3A;1F3A;0399 0313 0300;1F3A;0399 0313 0300; # (Ἲ; Ἲ; Ι◌̓◌̀; Ἲ; Ι◌̓◌̀; ) GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA +1F3B;1F3B;0399 0314 0300;1F3B;0399 0314 0300; # (Ἳ; Ἳ; Ι◌̔◌̀; Ἳ; Ι◌̔◌̀; ) GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA +1F3C;1F3C;0399 0313 0301;1F3C;0399 0313 0301; # (Ἴ; Ἴ; Ι◌̓◌́; Ἴ; Ι◌̓◌́; ) GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA +1F3D;1F3D;0399 0314 0301;1F3D;0399 0314 0301; # (Ἵ; Ἵ; Ι◌̔◌́; Ἵ; Ι◌̔◌́; ) GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA +1F3E;1F3E;0399 0313 0342;1F3E;0399 0313 0342; # (Ἶ; Ἶ; Ι◌̓◌͂; Ἶ; Ι◌̓◌͂; ) GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI +1F3F;1F3F;0399 0314 0342;1F3F;0399 0314 0342; # (Ἷ; Ἷ; Ι◌̔◌͂; Ἷ; Ι◌̔◌͂; ) GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI +1F40;1F40;03BF 0313;1F40;03BF 0313; # (ὀ; ὀ; ο◌̓; ὀ; ο◌̓; ) GREEK SMALL LETTER OMICRON WITH PSILI +1F41;1F41;03BF 0314;1F41;03BF 0314; # (ὁ; ὁ; ο◌̔; ὁ; ο◌̔; ) GREEK SMALL LETTER OMICRON WITH DASIA +1F42;1F42;03BF 0313 0300;1F42;03BF 0313 0300; # (ὂ; ὂ; ο◌̓◌̀; ὂ; ο◌̓◌̀; ) GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA +1F43;1F43;03BF 0314 0300;1F43;03BF 0314 0300; # (ὃ; ὃ; ο◌̔◌̀; ὃ; ο◌̔◌̀; ) GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA +1F44;1F44;03BF 0313 0301;1F44;03BF 0313 0301; # (ὄ; ὄ; ο◌̓◌́; ὄ; ο◌̓◌́; ) GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA +1F45;1F45;03BF 0314 0301;1F45;03BF 0314 0301; # (ὅ; ὅ; ο◌̔◌́; ὅ; ο◌̔◌́; ) GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA +1F48;1F48;039F 0313;1F48;039F 0313; # (Ὀ; Ὀ; Ο◌̓; Ὀ; Ο◌̓; ) GREEK CAPITAL LETTER OMICRON WITH PSILI +1F49;1F49;039F 0314;1F49;039F 0314; # (Ὁ; Ὁ; Ο◌̔; Ὁ; Ο◌̔; ) GREEK CAPITAL LETTER OMICRON WITH DASIA +1F4A;1F4A;039F 0313 0300;1F4A;039F 0313 0300; # (Ὂ; Ὂ; Ο◌̓◌̀; Ὂ; Ο◌̓◌̀; ) GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA +1F4B;1F4B;039F 0314 0300;1F4B;039F 0314 0300; # (Ὃ; Ὃ; Ο◌̔◌̀; Ὃ; Ο◌̔◌̀; ) GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA +1F4C;1F4C;039F 0313 0301;1F4C;039F 0313 0301; # (Ὄ; Ὄ; Ο◌̓◌́; Ὄ; Ο◌̓◌́; ) GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA +1F4D;1F4D;039F 0314 0301;1F4D;039F 0314 0301; # (Ὅ; Ὅ; Ο◌̔◌́; Ὅ; Ο◌̔◌́; ) GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA +1F50;1F50;03C5 0313;1F50;03C5 0313; # (ὐ; ὐ; υ◌̓; ὐ; υ◌̓; ) GREEK SMALL LETTER UPSILON WITH PSILI +1F51;1F51;03C5 0314;1F51;03C5 0314; # (ὑ; ὑ; υ◌̔; ὑ; υ◌̔; ) GREEK SMALL LETTER UPSILON WITH DASIA +1F52;1F52;03C5 0313 0300;1F52;03C5 0313 0300; # (ὒ; ὒ; υ◌̓◌̀; ὒ; υ◌̓◌̀; ) GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA +1F53;1F53;03C5 0314 0300;1F53;03C5 0314 0300; # (ὓ; ὓ; υ◌̔◌̀; ὓ; υ◌̔◌̀; ) GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA +1F54;1F54;03C5 0313 0301;1F54;03C5 0313 0301; # (ὔ; ὔ; υ◌̓◌́; ὔ; υ◌̓◌́; ) GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA +1F55;1F55;03C5 0314 0301;1F55;03C5 0314 0301; # (ὕ; ὕ; υ◌̔◌́; ὕ; υ◌̔◌́; ) GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA +1F56;1F56;03C5 0313 0342;1F56;03C5 0313 0342; # (ὖ; ὖ; υ◌̓◌͂; ὖ; υ◌̓◌͂; ) GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI +1F57;1F57;03C5 0314 0342;1F57;03C5 0314 0342; # (ὗ; ὗ; υ◌̔◌͂; ὗ; υ◌̔◌͂; ) GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI +1F59;1F59;03A5 0314;1F59;03A5 0314; # (Ὑ; Ὑ; Υ◌̔; Ὑ; Υ◌̔; ) GREEK CAPITAL LETTER UPSILON WITH DASIA +1F5B;1F5B;03A5 0314 0300;1F5B;03A5 0314 0300; # (Ὓ; Ὓ; Υ◌̔◌̀; Ὓ; Υ◌̔◌̀; ) GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA +1F5D;1F5D;03A5 0314 0301;1F5D;03A5 0314 0301; # (Ὕ; Ὕ; Υ◌̔◌́; Ὕ; Υ◌̔◌́; ) GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA +1F5F;1F5F;03A5 0314 0342;1F5F;03A5 0314 0342; # (Ὗ; Ὗ; Υ◌̔◌͂; Ὗ; Υ◌̔◌͂; ) GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI +1F60;1F60;03C9 0313;1F60;03C9 0313; # (ὠ; ὠ; ω◌̓; ὠ; ω◌̓; ) GREEK SMALL LETTER OMEGA WITH PSILI +1F61;1F61;03C9 0314;1F61;03C9 0314; # (ὡ; ὡ; ω◌̔; ὡ; ω◌̔; ) GREEK SMALL LETTER OMEGA WITH DASIA +1F62;1F62;03C9 0313 0300;1F62;03C9 0313 0300; # (ὢ; ὢ; ω◌̓◌̀; ὢ; ω◌̓◌̀; ) GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA +1F63;1F63;03C9 0314 0300;1F63;03C9 0314 0300; # (ὣ; ὣ; ω◌̔◌̀; ὣ; ω◌̔◌̀; ) GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA +1F64;1F64;03C9 0313 0301;1F64;03C9 0313 0301; # (ὤ; ὤ; ω◌̓◌́; ὤ; ω◌̓◌́; ) GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA +1F65;1F65;03C9 0314 0301;1F65;03C9 0314 0301; # (ὥ; ὥ; ω◌̔◌́; ὥ; ω◌̔◌́; ) GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA +1F66;1F66;03C9 0313 0342;1F66;03C9 0313 0342; # (ὦ; ὦ; ω◌̓◌͂; ὦ; ω◌̓◌͂; ) GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI +1F67;1F67;03C9 0314 0342;1F67;03C9 0314 0342; # (ὧ; ὧ; ω◌̔◌͂; ὧ; ω◌̔◌͂; ) GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI +1F68;1F68;03A9 0313;1F68;03A9 0313; # (Ὠ; Ὠ; Ω◌̓; Ὠ; Ω◌̓; ) GREEK CAPITAL LETTER OMEGA WITH PSILI +1F69;1F69;03A9 0314;1F69;03A9 0314; # (Ὡ; Ὡ; Ω◌̔; Ὡ; Ω◌̔; ) GREEK CAPITAL LETTER OMEGA WITH DASIA +1F6A;1F6A;03A9 0313 0300;1F6A;03A9 0313 0300; # (Ὢ; Ὢ; Ω◌̓◌̀; Ὢ; Ω◌̓◌̀; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA +1F6B;1F6B;03A9 0314 0300;1F6B;03A9 0314 0300; # (Ὣ; Ὣ; Ω◌̔◌̀; Ὣ; Ω◌̔◌̀; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA +1F6C;1F6C;03A9 0313 0301;1F6C;03A9 0313 0301; # (Ὤ; Ὤ; Ω◌̓◌́; Ὤ; Ω◌̓◌́; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA +1F6D;1F6D;03A9 0314 0301;1F6D;03A9 0314 0301; # (Ὥ; Ὥ; Ω◌̔◌́; Ὥ; Ω◌̔◌́; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA +1F6E;1F6E;03A9 0313 0342;1F6E;03A9 0313 0342; # (Ὦ; Ὦ; Ω◌̓◌͂; Ὦ; Ω◌̓◌͂; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI +1F6F;1F6F;03A9 0314 0342;1F6F;03A9 0314 0342; # (Ὧ; Ὧ; Ω◌̔◌͂; Ὧ; Ω◌̔◌͂; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI +1F70;1F70;03B1 0300;1F70;03B1 0300; # (ὰ; ὰ; α◌̀; ὰ; α◌̀; ) GREEK SMALL LETTER ALPHA WITH VARIA +1F71;03AC;03B1 0301;03AC;03B1 0301; # (ά; ά; α◌́; ά; α◌́; ) GREEK SMALL LETTER ALPHA WITH OXIA +1F72;1F72;03B5 0300;1F72;03B5 0300; # (ὲ; ὲ; ε◌̀; ὲ; ε◌̀; ) GREEK SMALL LETTER EPSILON WITH VARIA +1F73;03AD;03B5 0301;03AD;03B5 0301; # (έ; έ; ε◌́; έ; ε◌́; ) GREEK SMALL LETTER EPSILON WITH OXIA +1F74;1F74;03B7 0300;1F74;03B7 0300; # (ὴ; ὴ; η◌̀; ὴ; η◌̀; ) GREEK SMALL LETTER ETA WITH VARIA +1F75;03AE;03B7 0301;03AE;03B7 0301; # (ή; ή; η◌́; ή; η◌́; ) GREEK SMALL LETTER ETA WITH OXIA +1F76;1F76;03B9 0300;1F76;03B9 0300; # (ὶ; ὶ; ι◌̀; ὶ; ι◌̀; ) GREEK SMALL LETTER IOTA WITH VARIA +1F77;03AF;03B9 0301;03AF;03B9 0301; # (ί; ί; ι◌́; ί; ι◌́; ) GREEK SMALL LETTER IOTA WITH OXIA +1F78;1F78;03BF 0300;1F78;03BF 0300; # (ὸ; ὸ; ο◌̀; ὸ; ο◌̀; ) GREEK SMALL LETTER OMICRON WITH VARIA +1F79;03CC;03BF 0301;03CC;03BF 0301; # (ό; ό; ο◌́; ό; ο◌́; ) GREEK SMALL LETTER OMICRON WITH OXIA +1F7A;1F7A;03C5 0300;1F7A;03C5 0300; # (ὺ; ὺ; υ◌̀; ὺ; υ◌̀; ) GREEK SMALL LETTER UPSILON WITH VARIA +1F7B;03CD;03C5 0301;03CD;03C5 0301; # (ύ; ύ; υ◌́; ύ; υ◌́; ) GREEK SMALL LETTER UPSILON WITH OXIA +1F7C;1F7C;03C9 0300;1F7C;03C9 0300; # (ὼ; ὼ; ω◌̀; ὼ; ω◌̀; ) GREEK SMALL LETTER OMEGA WITH VARIA +1F7D;03CE;03C9 0301;03CE;03C9 0301; # (ώ; ώ; ω◌́; ώ; ω◌́; ) GREEK SMALL LETTER OMEGA WITH OXIA +1F80;1F80;03B1 0313 0345;1F80;03B1 0313 0345; # (ᾀ; ᾀ; α◌̓◌ͅ; ᾀ; α◌̓◌ͅ; ) GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI +1F81;1F81;03B1 0314 0345;1F81;03B1 0314 0345; # (ᾁ; ᾁ; α◌̔◌ͅ; ᾁ; α◌̔◌ͅ; ) GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI +1F82;1F82;03B1 0313 0300 0345;1F82;03B1 0313 0300 0345; # (ᾂ; ᾂ; α◌̓◌̀◌ͅ; ᾂ; α◌̓◌̀◌ͅ; ) GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI +1F83;1F83;03B1 0314 0300 0345;1F83;03B1 0314 0300 0345; # (ᾃ; ᾃ; α◌̔◌̀◌ͅ; ᾃ; α◌̔◌̀◌ͅ; ) GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI +1F84;1F84;03B1 0313 0301 0345;1F84;03B1 0313 0301 0345; # (ᾄ; ᾄ; α◌̓◌́◌ͅ; ᾄ; α◌̓◌́◌ͅ; ) GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI +1F85;1F85;03B1 0314 0301 0345;1F85;03B1 0314 0301 0345; # (ᾅ; ᾅ; α◌̔◌́◌ͅ; ᾅ; α◌̔◌́◌ͅ; ) GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI +1F86;1F86;03B1 0313 0342 0345;1F86;03B1 0313 0342 0345; # (ᾆ; ᾆ; α◌̓◌͂◌ͅ; ᾆ; α◌̓◌͂◌ͅ; ) GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI +1F87;1F87;03B1 0314 0342 0345;1F87;03B1 0314 0342 0345; # (ᾇ; ᾇ; α◌̔◌͂◌ͅ; ᾇ; α◌̔◌͂◌ͅ; ) GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI +1F88;1F88;0391 0313 0345;1F88;0391 0313 0345; # (ᾈ; ᾈ; Α◌̓◌ͅ; ᾈ; Α◌̓◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI +1F89;1F89;0391 0314 0345;1F89;0391 0314 0345; # (ᾉ; ᾉ; Α◌̔◌ͅ; ᾉ; Α◌̔◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI +1F8A;1F8A;0391 0313 0300 0345;1F8A;0391 0313 0300 0345; # (ᾊ; ᾊ; Α◌̓◌̀◌ͅ; ᾊ; Α◌̓◌̀◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI +1F8B;1F8B;0391 0314 0300 0345;1F8B;0391 0314 0300 0345; # (ᾋ; ᾋ; Α◌̔◌̀◌ͅ; ᾋ; Α◌̔◌̀◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI +1F8C;1F8C;0391 0313 0301 0345;1F8C;0391 0313 0301 0345; # (ᾌ; ᾌ; Α◌̓◌́◌ͅ; ᾌ; Α◌̓◌́◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI +1F8D;1F8D;0391 0314 0301 0345;1F8D;0391 0314 0301 0345; # (ᾍ; ᾍ; Α◌̔◌́◌ͅ; ᾍ; Α◌̔◌́◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI +1F8E;1F8E;0391 0313 0342 0345;1F8E;0391 0313 0342 0345; # (ᾎ; ᾎ; Α◌̓◌͂◌ͅ; ᾎ; Α◌̓◌͂◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +1F8F;1F8F;0391 0314 0342 0345;1F8F;0391 0314 0342 0345; # (ᾏ; ᾏ; Α◌̔◌͂◌ͅ; ᾏ; Α◌̔◌͂◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +1F90;1F90;03B7 0313 0345;1F90;03B7 0313 0345; # (ᾐ; ᾐ; η◌̓◌ͅ; ᾐ; η◌̓◌ͅ; ) GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI +1F91;1F91;03B7 0314 0345;1F91;03B7 0314 0345; # (ᾑ; ᾑ; η◌̔◌ͅ; ᾑ; η◌̔◌ͅ; ) GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI +1F92;1F92;03B7 0313 0300 0345;1F92;03B7 0313 0300 0345; # (ᾒ; ᾒ; η◌̓◌̀◌ͅ; ᾒ; η◌̓◌̀◌ͅ; ) GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI +1F93;1F93;03B7 0314 0300 0345;1F93;03B7 0314 0300 0345; # (ᾓ; ᾓ; η◌̔◌̀◌ͅ; ᾓ; η◌̔◌̀◌ͅ; ) GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI +1F94;1F94;03B7 0313 0301 0345;1F94;03B7 0313 0301 0345; # (ᾔ; ᾔ; η◌̓◌́◌ͅ; ᾔ; η◌̓◌́◌ͅ; ) GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI +1F95;1F95;03B7 0314 0301 0345;1F95;03B7 0314 0301 0345; # (ᾕ; ᾕ; η◌̔◌́◌ͅ; ᾕ; η◌̔◌́◌ͅ; ) GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI +1F96;1F96;03B7 0313 0342 0345;1F96;03B7 0313 0342 0345; # (ᾖ; ᾖ; η◌̓◌͂◌ͅ; ᾖ; η◌̓◌͂◌ͅ; ) GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI +1F97;1F97;03B7 0314 0342 0345;1F97;03B7 0314 0342 0345; # (ᾗ; ᾗ; η◌̔◌͂◌ͅ; ᾗ; η◌̔◌͂◌ͅ; ) GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI +1F98;1F98;0397 0313 0345;1F98;0397 0313 0345; # (ᾘ; ᾘ; Η◌̓◌ͅ; ᾘ; Η◌̓◌ͅ; ) GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI +1F99;1F99;0397 0314 0345;1F99;0397 0314 0345; # (ᾙ; ᾙ; Η◌̔◌ͅ; ᾙ; Η◌̔◌ͅ; ) GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI +1F9A;1F9A;0397 0313 0300 0345;1F9A;0397 0313 0300 0345; # (ᾚ; ᾚ; Η◌̓◌̀◌ͅ; ᾚ; Η◌̓◌̀◌ͅ; ) GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI +1F9B;1F9B;0397 0314 0300 0345;1F9B;0397 0314 0300 0345; # (ᾛ; ᾛ; Η◌̔◌̀◌ͅ; ᾛ; Η◌̔◌̀◌ͅ; ) GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI +1F9C;1F9C;0397 0313 0301 0345;1F9C;0397 0313 0301 0345; # (ᾜ; ᾜ; Η◌̓◌́◌ͅ; ᾜ; Η◌̓◌́◌ͅ; ) GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI +1F9D;1F9D;0397 0314 0301 0345;1F9D;0397 0314 0301 0345; # (ᾝ; ᾝ; Η◌̔◌́◌ͅ; ᾝ; Η◌̔◌́◌ͅ; ) GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI +1F9E;1F9E;0397 0313 0342 0345;1F9E;0397 0313 0342 0345; # (ᾞ; ᾞ; Η◌̓◌͂◌ͅ; ᾞ; Η◌̓◌͂◌ͅ; ) GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +1F9F;1F9F;0397 0314 0342 0345;1F9F;0397 0314 0342 0345; # (ᾟ; ᾟ; Η◌̔◌͂◌ͅ; ᾟ; Η◌̔◌͂◌ͅ; ) GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +1FA0;1FA0;03C9 0313 0345;1FA0;03C9 0313 0345; # (ᾠ; ᾠ; ω◌̓◌ͅ; ᾠ; ω◌̓◌ͅ; ) GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI +1FA1;1FA1;03C9 0314 0345;1FA1;03C9 0314 0345; # (ᾡ; ᾡ; ω◌̔◌ͅ; ᾡ; ω◌̔◌ͅ; ) GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI +1FA2;1FA2;03C9 0313 0300 0345;1FA2;03C9 0313 0300 0345; # (ᾢ; ᾢ; ω◌̓◌̀◌ͅ; ᾢ; ω◌̓◌̀◌ͅ; ) GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI +1FA3;1FA3;03C9 0314 0300 0345;1FA3;03C9 0314 0300 0345; # (ᾣ; ᾣ; ω◌̔◌̀◌ͅ; ᾣ; ω◌̔◌̀◌ͅ; ) GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI +1FA4;1FA4;03C9 0313 0301 0345;1FA4;03C9 0313 0301 0345; # (ᾤ; ᾤ; ω◌̓◌́◌ͅ; ᾤ; ω◌̓◌́◌ͅ; ) GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI +1FA5;1FA5;03C9 0314 0301 0345;1FA5;03C9 0314 0301 0345; # (ᾥ; ᾥ; ω◌̔◌́◌ͅ; ᾥ; ω◌̔◌́◌ͅ; ) GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI +1FA6;1FA6;03C9 0313 0342 0345;1FA6;03C9 0313 0342 0345; # (ᾦ; ᾦ; ω◌̓◌͂◌ͅ; ᾦ; ω◌̓◌͂◌ͅ; ) GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI +1FA7;1FA7;03C9 0314 0342 0345;1FA7;03C9 0314 0342 0345; # (ᾧ; ᾧ; ω◌̔◌͂◌ͅ; ᾧ; ω◌̔◌͂◌ͅ; ) GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI +1FA8;1FA8;03A9 0313 0345;1FA8;03A9 0313 0345; # (ᾨ; ᾨ; Ω◌̓◌ͅ; ᾨ; Ω◌̓◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI +1FA9;1FA9;03A9 0314 0345;1FA9;03A9 0314 0345; # (ᾩ; ᾩ; Ω◌̔◌ͅ; ᾩ; Ω◌̔◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI +1FAA;1FAA;03A9 0313 0300 0345;1FAA;03A9 0313 0300 0345; # (ᾪ; ᾪ; Ω◌̓◌̀◌ͅ; ᾪ; Ω◌̓◌̀◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI +1FAB;1FAB;03A9 0314 0300 0345;1FAB;03A9 0314 0300 0345; # (ᾫ; ᾫ; Ω◌̔◌̀◌ͅ; ᾫ; Ω◌̔◌̀◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI +1FAC;1FAC;03A9 0313 0301 0345;1FAC;03A9 0313 0301 0345; # (ᾬ; ᾬ; Ω◌̓◌́◌ͅ; ᾬ; Ω◌̓◌́◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI +1FAD;1FAD;03A9 0314 0301 0345;1FAD;03A9 0314 0301 0345; # (ᾭ; ᾭ; Ω◌̔◌́◌ͅ; ᾭ; Ω◌̔◌́◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI +1FAE;1FAE;03A9 0313 0342 0345;1FAE;03A9 0313 0342 0345; # (ᾮ; ᾮ; Ω◌̓◌͂◌ͅ; ᾮ; Ω◌̓◌͂◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +1FAF;1FAF;03A9 0314 0342 0345;1FAF;03A9 0314 0342 0345; # (ᾯ; ᾯ; Ω◌̔◌͂◌ͅ; ᾯ; Ω◌̔◌͂◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +1FB0;1FB0;03B1 0306;1FB0;03B1 0306; # (ᾰ; ᾰ; α◌̆; ᾰ; α◌̆; ) GREEK SMALL LETTER ALPHA WITH VRACHY +1FB1;1FB1;03B1 0304;1FB1;03B1 0304; # (ᾱ; ᾱ; α◌̄; ᾱ; α◌̄; ) GREEK SMALL LETTER ALPHA WITH MACRON +1FB2;1FB2;03B1 0300 0345;1FB2;03B1 0300 0345; # (ᾲ; ᾲ; α◌̀◌ͅ; ᾲ; α◌̀◌ͅ; ) GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI +1FB3;1FB3;03B1 0345;1FB3;03B1 0345; # (ᾳ; ᾳ; α◌ͅ; ᾳ; α◌ͅ; ) GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI +1FB4;1FB4;03B1 0301 0345;1FB4;03B1 0301 0345; # (ᾴ; ᾴ; α◌́◌ͅ; ᾴ; α◌́◌ͅ; ) GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI +1FB6;1FB6;03B1 0342;1FB6;03B1 0342; # (ᾶ; ᾶ; α◌͂; ᾶ; α◌͂; ) GREEK SMALL LETTER ALPHA WITH PERISPOMENI +1FB7;1FB7;03B1 0342 0345;1FB7;03B1 0342 0345; # (ᾷ; ᾷ; α◌͂◌ͅ; ᾷ; α◌͂◌ͅ; ) GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI +1FB8;1FB8;0391 0306;1FB8;0391 0306; # (Ᾰ; Ᾰ; Α◌̆; Ᾰ; Α◌̆; ) GREEK CAPITAL LETTER ALPHA WITH VRACHY +1FB9;1FB9;0391 0304;1FB9;0391 0304; # (Ᾱ; Ᾱ; Α◌̄; Ᾱ; Α◌̄; ) GREEK CAPITAL LETTER ALPHA WITH MACRON +1FBA;1FBA;0391 0300;1FBA;0391 0300; # (Ὰ; Ὰ; Α◌̀; Ὰ; Α◌̀; ) GREEK CAPITAL LETTER ALPHA WITH VARIA +1FBB;0386;0391 0301;0386;0391 0301; # (Ά; Ά; Α◌́; Ά; Α◌́; ) GREEK CAPITAL LETTER ALPHA WITH OXIA +1FBC;1FBC;0391 0345;1FBC;0391 0345; # (ᾼ; ᾼ; Α◌ͅ; ᾼ; Α◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI +1FBD;1FBD;1FBD;0020 0313;0020 0313; # (᾽; ᾽; ᾽; ◌̓; ◌̓; ) GREEK KORONIS +1FBE;03B9;03B9;03B9;03B9; # (ι; ι; ι; ι; ι; ) GREEK PROSGEGRAMMENI +1FBF;1FBF;1FBF;0020 0313;0020 0313; # (᾿; ᾿; ᾿; ◌̓; ◌̓; ) GREEK PSILI +1FC0;1FC0;1FC0;0020 0342;0020 0342; # (῀; ῀; ῀; ◌͂; ◌͂; ) GREEK PERISPOMENI +1FC1;1FC1;00A8 0342;0020 0308 0342;0020 0308 0342; # (῁; ῁; ¨◌͂; ◌̈◌͂; ◌̈◌͂; ) GREEK DIALYTIKA AND PERISPOMENI +1FC2;1FC2;03B7 0300 0345;1FC2;03B7 0300 0345; # (ῂ; ῂ; η◌̀◌ͅ; ῂ; η◌̀◌ͅ; ) GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI +1FC3;1FC3;03B7 0345;1FC3;03B7 0345; # (ῃ; ῃ; η◌ͅ; ῃ; η◌ͅ; ) GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI +1FC4;1FC4;03B7 0301 0345;1FC4;03B7 0301 0345; # (ῄ; ῄ; η◌́◌ͅ; ῄ; η◌́◌ͅ; ) GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI +1FC6;1FC6;03B7 0342;1FC6;03B7 0342; # (ῆ; ῆ; η◌͂; ῆ; η◌͂; ) GREEK SMALL LETTER ETA WITH PERISPOMENI +1FC7;1FC7;03B7 0342 0345;1FC7;03B7 0342 0345; # (ῇ; ῇ; η◌͂◌ͅ; ῇ; η◌͂◌ͅ; ) GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI +1FC8;1FC8;0395 0300;1FC8;0395 0300; # (Ὲ; Ὲ; Ε◌̀; Ὲ; Ε◌̀; ) GREEK CAPITAL LETTER EPSILON WITH VARIA +1FC9;0388;0395 0301;0388;0395 0301; # (Έ; Έ; Ε◌́; Έ; Ε◌́; ) GREEK CAPITAL LETTER EPSILON WITH OXIA +1FCA;1FCA;0397 0300;1FCA;0397 0300; # (Ὴ; Ὴ; Η◌̀; Ὴ; Η◌̀; ) GREEK CAPITAL LETTER ETA WITH VARIA +1FCB;0389;0397 0301;0389;0397 0301; # (Ή; Ή; Η◌́; Ή; Η◌́; ) GREEK CAPITAL LETTER ETA WITH OXIA +1FCC;1FCC;0397 0345;1FCC;0397 0345; # (ῌ; ῌ; Η◌ͅ; ῌ; Η◌ͅ; ) GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI +1FCD;1FCD;1FBF 0300;0020 0313 0300;0020 0313 0300; # (῍; ῍; ᾿◌̀; ◌̓◌̀; ◌̓◌̀; ) GREEK PSILI AND VARIA +1FCE;1FCE;1FBF 0301;0020 0313 0301;0020 0313 0301; # (῎; ῎; ᾿◌́; ◌̓◌́; ◌̓◌́; ) GREEK PSILI AND OXIA +1FCF;1FCF;1FBF 0342;0020 0313 0342;0020 0313 0342; # (῏; ῏; ᾿◌͂; ◌̓◌͂; ◌̓◌͂; ) GREEK PSILI AND PERISPOMENI +1FD0;1FD0;03B9 0306;1FD0;03B9 0306; # (ῐ; ῐ; ι◌̆; ῐ; ι◌̆; ) GREEK SMALL LETTER IOTA WITH VRACHY +1FD1;1FD1;03B9 0304;1FD1;03B9 0304; # (ῑ; ῑ; ι◌̄; ῑ; ι◌̄; ) GREEK SMALL LETTER IOTA WITH MACRON +1FD2;1FD2;03B9 0308 0300;1FD2;03B9 0308 0300; # (ῒ; ῒ; ι◌̈◌̀; ῒ; ι◌̈◌̀; ) GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA +1FD3;0390;03B9 0308 0301;0390;03B9 0308 0301; # (ΐ; ΐ; ι◌̈◌́; ΐ; ι◌̈◌́; ) GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA +1FD6;1FD6;03B9 0342;1FD6;03B9 0342; # (ῖ; ῖ; ι◌͂; ῖ; ι◌͂; ) GREEK SMALL LETTER IOTA WITH PERISPOMENI +1FD7;1FD7;03B9 0308 0342;1FD7;03B9 0308 0342; # (ῗ; ῗ; ι◌̈◌͂; ῗ; ι◌̈◌͂; ) GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI +1FD8;1FD8;0399 0306;1FD8;0399 0306; # (Ῐ; Ῐ; Ι◌̆; Ῐ; Ι◌̆; ) GREEK CAPITAL LETTER IOTA WITH VRACHY +1FD9;1FD9;0399 0304;1FD9;0399 0304; # (Ῑ; Ῑ; Ι◌̄; Ῑ; Ι◌̄; ) GREEK CAPITAL LETTER IOTA WITH MACRON +1FDA;1FDA;0399 0300;1FDA;0399 0300; # (Ὶ; Ὶ; Ι◌̀; Ὶ; Ι◌̀; ) GREEK CAPITAL LETTER IOTA WITH VARIA +1FDB;038A;0399 0301;038A;0399 0301; # (Ί; Ί; Ι◌́; Ί; Ι◌́; ) GREEK CAPITAL LETTER IOTA WITH OXIA +1FDD;1FDD;1FFE 0300;0020 0314 0300;0020 0314 0300; # (῝; ῝; ῾◌̀; ◌̔◌̀; ◌̔◌̀; ) GREEK DASIA AND VARIA +1FDE;1FDE;1FFE 0301;0020 0314 0301;0020 0314 0301; # (῞; ῞; ῾◌́; ◌̔◌́; ◌̔◌́; ) GREEK DASIA AND OXIA +1FDF;1FDF;1FFE 0342;0020 0314 0342;0020 0314 0342; # (῟; ῟; ῾◌͂; ◌̔◌͂; ◌̔◌͂; ) GREEK DASIA AND PERISPOMENI +1FE0;1FE0;03C5 0306;1FE0;03C5 0306; # (ῠ; ῠ; υ◌̆; ῠ; υ◌̆; ) GREEK SMALL LETTER UPSILON WITH VRACHY +1FE1;1FE1;03C5 0304;1FE1;03C5 0304; # (ῡ; ῡ; υ◌̄; ῡ; υ◌̄; ) GREEK SMALL LETTER UPSILON WITH MACRON +1FE2;1FE2;03C5 0308 0300;1FE2;03C5 0308 0300; # (ῢ; ῢ; υ◌̈◌̀; ῢ; υ◌̈◌̀; ) GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA +1FE3;03B0;03C5 0308 0301;03B0;03C5 0308 0301; # (ΰ; ΰ; υ◌̈◌́; ΰ; υ◌̈◌́; ) GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA +1FE4;1FE4;03C1 0313;1FE4;03C1 0313; # (ῤ; ῤ; ρ◌̓; ῤ; ρ◌̓; ) GREEK SMALL LETTER RHO WITH PSILI +1FE5;1FE5;03C1 0314;1FE5;03C1 0314; # (ῥ; ῥ; ρ◌̔; ῥ; ρ◌̔; ) GREEK SMALL LETTER RHO WITH DASIA +1FE6;1FE6;03C5 0342;1FE6;03C5 0342; # (ῦ; ῦ; υ◌͂; ῦ; υ◌͂; ) GREEK SMALL LETTER UPSILON WITH PERISPOMENI +1FE7;1FE7;03C5 0308 0342;1FE7;03C5 0308 0342; # (ῧ; ῧ; υ◌̈◌͂; ῧ; υ◌̈◌͂; ) GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI +1FE8;1FE8;03A5 0306;1FE8;03A5 0306; # (Ῠ; Ῠ; Υ◌̆; Ῠ; Υ◌̆; ) GREEK CAPITAL LETTER UPSILON WITH VRACHY +1FE9;1FE9;03A5 0304;1FE9;03A5 0304; # (Ῡ; Ῡ; Υ◌̄; Ῡ; Υ◌̄; ) GREEK CAPITAL LETTER UPSILON WITH MACRON +1FEA;1FEA;03A5 0300;1FEA;03A5 0300; # (Ὺ; Ὺ; Υ◌̀; Ὺ; Υ◌̀; ) GREEK CAPITAL LETTER UPSILON WITH VARIA +1FEB;038E;03A5 0301;038E;03A5 0301; # (Ύ; Ύ; Υ◌́; Ύ; Υ◌́; ) GREEK CAPITAL LETTER UPSILON WITH OXIA +1FEC;1FEC;03A1 0314;1FEC;03A1 0314; # (Ῥ; Ῥ; Ρ◌̔; Ῥ; Ρ◌̔; ) GREEK CAPITAL LETTER RHO WITH DASIA +1FED;1FED;00A8 0300;0020 0308 0300;0020 0308 0300; # (῭; ῭; ¨◌̀; ◌̈◌̀; ◌̈◌̀; ) GREEK DIALYTIKA AND VARIA +1FEE;0385;00A8 0301;0020 0308 0301;0020 0308 0301; # (΅; ΅; ¨◌́; ◌̈◌́; ◌̈◌́; ) GREEK DIALYTIKA AND OXIA +1FEF;0060;0060;0060;0060; # (`; `; `; `; `; ) GREEK VARIA +1FF2;1FF2;03C9 0300 0345;1FF2;03C9 0300 0345; # (ῲ; ῲ; ω◌̀◌ͅ; ῲ; ω◌̀◌ͅ; ) GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI +1FF3;1FF3;03C9 0345;1FF3;03C9 0345; # (ῳ; ῳ; ω◌ͅ; ῳ; ω◌ͅ; ) GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI +1FF4;1FF4;03C9 0301 0345;1FF4;03C9 0301 0345; # (ῴ; ῴ; ω◌́◌ͅ; ῴ; ω◌́◌ͅ; ) GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI +1FF6;1FF6;03C9 0342;1FF6;03C9 0342; # (ῶ; ῶ; ω◌͂; ῶ; ω◌͂; ) GREEK SMALL LETTER OMEGA WITH PERISPOMENI +1FF7;1FF7;03C9 0342 0345;1FF7;03C9 0342 0345; # (ῷ; ῷ; ω◌͂◌ͅ; ῷ; ω◌͂◌ͅ; ) GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI +1FF8;1FF8;039F 0300;1FF8;039F 0300; # (Ὸ; Ὸ; Ο◌̀; Ὸ; Ο◌̀; ) GREEK CAPITAL LETTER OMICRON WITH VARIA +1FF9;038C;039F 0301;038C;039F 0301; # (Ό; Ό; Ο◌́; Ό; Ο◌́; ) GREEK CAPITAL LETTER OMICRON WITH OXIA +1FFA;1FFA;03A9 0300;1FFA;03A9 0300; # (Ὼ; Ὼ; Ω◌̀; Ὼ; Ω◌̀; ) GREEK CAPITAL LETTER OMEGA WITH VARIA +1FFB;038F;03A9 0301;038F;03A9 0301; # (Ώ; Ώ; Ω◌́; Ώ; Ω◌́; ) GREEK CAPITAL LETTER OMEGA WITH OXIA +1FFC;1FFC;03A9 0345;1FFC;03A9 0345; # (ῼ; ῼ; Ω◌ͅ; ῼ; Ω◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI +1FFD;00B4;00B4;0020 0301;0020 0301; # (´; ´; ´; ◌́; ◌́; ) GREEK OXIA +1FFE;1FFE;1FFE;0020 0314;0020 0314; # (῾; ῾; ῾; ◌̔; ◌̔; ) GREEK DASIA +2000;2002;2002;0020;0020; # ( ;  ;  ; ; ; ) EN QUAD +2001;2003;2003;0020;0020; # ( ;  ;  ; ; ; ) EM QUAD +2002;2002;2002;0020;0020; # ( ;  ;  ; ; ; ) EN SPACE +2003;2003;2003;0020;0020; # ( ;  ;  ; ; ; ) EM SPACE +2004;2004;2004;0020;0020; # ( ;  ;  ; ; ; ) THREE-PER-EM SPACE +2005;2005;2005;0020;0020; # ( ;  ;  ; ; ; ) FOUR-PER-EM SPACE +2006;2006;2006;0020;0020; # ( ;  ;  ; ; ; ) SIX-PER-EM SPACE +2007;2007;2007;0020;0020; # ( ;  ;  ; ; ; ) FIGURE SPACE +2008;2008;2008;0020;0020; # ( ;  ;  ; ; ; ) PUNCTUATION SPACE +2009;2009;2009;0020;0020; # ( ;  ;  ; ; ; ) THIN SPACE +200A;200A;200A;0020;0020; # ( ;  ;  ; ; ; ) HAIR SPACE +2011;2011;2011;2010;2010; # (‑; ‑; ‑; ‐; ‐; ) NON-BREAKING HYPHEN +2017;2017;2017;0020 0333;0020 0333; # (‗; ‗; ‗; ◌̳; ◌̳; ) DOUBLE LOW LINE +2024;2024;2024;002E;002E; # (․; ․; ․; .; .; ) ONE DOT LEADER +2025;2025;2025;002E 002E;002E 002E; # (‥; ‥; ‥; ..; ..; ) TWO DOT LEADER +2026;2026;2026;002E 002E 002E;002E 002E 002E; # (…; …; …; ...; ...; ) HORIZONTAL ELLIPSIS +202F;202F;202F;0020;0020; # ( ;  ;  ; ; ; ) NARROW NO-BREAK SPACE +2033;2033;2033;2032 2032;2032 2032; # (″; ″; ″; ′′; ′′; ) DOUBLE PRIME +2034;2034;2034;2032 2032 2032;2032 2032 2032; # (‴; ‴; ‴; ′′′; ′′′; ) TRIPLE PRIME +2036;2036;2036;2035 2035;2035 2035; # (‶; ‶; ‶; ‵‵; ‵‵; ) REVERSED DOUBLE PRIME +2037;2037;2037;2035 2035 2035;2035 2035 2035; # (‷; ‷; ‷; ‵‵‵; ‵‵‵; ) REVERSED TRIPLE PRIME +203C;203C;203C;0021 0021;0021 0021; # (‼; ‼; ‼; !!; !!; ) DOUBLE EXCLAMATION MARK +203E;203E;203E;0020 0305;0020 0305; # (‾; ‾; ‾; ◌̅; ◌̅; ) OVERLINE +2047;2047;2047;003F 003F;003F 003F; # (⁇; ⁇; ⁇; ??; ??; ) DOUBLE QUESTION MARK +2048;2048;2048;003F 0021;003F 0021; # (⁈; ⁈; ⁈; ?!; ?!; ) QUESTION EXCLAMATION MARK +2049;2049;2049;0021 003F;0021 003F; # (⁉; ⁉; ⁉; !?; !?; ) EXCLAMATION QUESTION MARK +2057;2057;2057;2032 2032 2032 2032;2032 2032 2032 2032; # (⁗; ⁗; ⁗; ′′′′; ′′′′; ) QUADRUPLE PRIME +205F;205F;205F;0020;0020; # ( ;  ;  ; ; ; ) MEDIUM MATHEMATICAL SPACE +2070;2070;2070;0030;0030; # (⁰; ⁰; ⁰; 0; 0; ) SUPERSCRIPT ZERO +2071;2071;2071;0069;0069; # (ⁱ; ⁱ; ⁱ; i; i; ) SUPERSCRIPT LATIN SMALL LETTER I +2074;2074;2074;0034;0034; # (⁴; ⁴; ⁴; 4; 4; ) SUPERSCRIPT FOUR +2075;2075;2075;0035;0035; # (⁵; ⁵; ⁵; 5; 5; ) SUPERSCRIPT FIVE +2076;2076;2076;0036;0036; # (⁶; ⁶; ⁶; 6; 6; ) SUPERSCRIPT SIX +2077;2077;2077;0037;0037; # (⁷; ⁷; ⁷; 7; 7; ) SUPERSCRIPT SEVEN +2078;2078;2078;0038;0038; # (⁸; ⁸; ⁸; 8; 8; ) SUPERSCRIPT EIGHT +2079;2079;2079;0039;0039; # (⁹; ⁹; ⁹; 9; 9; ) SUPERSCRIPT NINE +207A;207A;207A;002B;002B; # (⁺; ⁺; ⁺; +; +; ) SUPERSCRIPT PLUS SIGN +207B;207B;207B;2212;2212; # (⁻; ⁻; ⁻; −; −; ) SUPERSCRIPT MINUS +207C;207C;207C;003D;003D; # (⁼; ⁼; ⁼; =; =; ) SUPERSCRIPT EQUALS SIGN +207D;207D;207D;0028;0028; # (⁽; ⁽; ⁽; (; (; ) SUPERSCRIPT LEFT PARENTHESIS +207E;207E;207E;0029;0029; # (⁾; ⁾; ⁾; ); ); ) SUPERSCRIPT RIGHT PARENTHESIS +207F;207F;207F;006E;006E; # (ⁿ; ⁿ; ⁿ; n; n; ) SUPERSCRIPT LATIN SMALL LETTER N +2080;2080;2080;0030;0030; # (₀; ₀; ₀; 0; 0; ) SUBSCRIPT ZERO +2081;2081;2081;0031;0031; # (₁; ₁; ₁; 1; 1; ) SUBSCRIPT ONE +2082;2082;2082;0032;0032; # (₂; ₂; ₂; 2; 2; ) SUBSCRIPT TWO +2083;2083;2083;0033;0033; # (₃; ₃; ₃; 3; 3; ) SUBSCRIPT THREE +2084;2084;2084;0034;0034; # (₄; ₄; ₄; 4; 4; ) SUBSCRIPT FOUR +2085;2085;2085;0035;0035; # (₅; ₅; ₅; 5; 5; ) SUBSCRIPT FIVE +2086;2086;2086;0036;0036; # (₆; ₆; ₆; 6; 6; ) SUBSCRIPT SIX +2087;2087;2087;0037;0037; # (₇; ₇; ₇; 7; 7; ) SUBSCRIPT SEVEN +2088;2088;2088;0038;0038; # (₈; ₈; ₈; 8; 8; ) SUBSCRIPT EIGHT +2089;2089;2089;0039;0039; # (₉; ₉; ₉; 9; 9; ) SUBSCRIPT NINE +208A;208A;208A;002B;002B; # (₊; ₊; ₊; +; +; ) SUBSCRIPT PLUS SIGN +208B;208B;208B;2212;2212; # (₋; ₋; ₋; −; −; ) SUBSCRIPT MINUS +208C;208C;208C;003D;003D; # (₌; ₌; ₌; =; =; ) SUBSCRIPT EQUALS SIGN +208D;208D;208D;0028;0028; # (₍; ₍; ₍; (; (; ) SUBSCRIPT LEFT PARENTHESIS +208E;208E;208E;0029;0029; # (₎; ₎; ₎; ); ); ) SUBSCRIPT RIGHT PARENTHESIS +20A8;20A8;20A8;0052 0073;0052 0073; # (₨; ₨; ₨; Rs; Rs; ) RUPEE SIGN +2100;2100;2100;0061 002F 0063;0061 002F 0063; # (℀; ℀; ℀; a/c; a/c; ) ACCOUNT OF +2101;2101;2101;0061 002F 0073;0061 002F 0073; # (℁; ℁; ℁; a/s; a/s; ) ADDRESSED TO THE SUBJECT +2102;2102;2102;0043;0043; # (ℂ; ℂ; ℂ; C; C; ) DOUBLE-STRUCK CAPITAL C +2103;2103;2103;00B0 0043;00B0 0043; # (℃; ℃; ℃; °C; °C; ) DEGREE CELSIUS +2105;2105;2105;0063 002F 006F;0063 002F 006F; # (℅; ℅; ℅; c/o; c/o; ) CARE OF +2106;2106;2106;0063 002F 0075;0063 002F 0075; # (℆; ℆; ℆; c/u; c/u; ) CADA UNA +2107;2107;2107;0190;0190; # (ℇ; ℇ; ℇ; Ɛ; Ɛ; ) EULER CONSTANT +2109;2109;2109;00B0 0046;00B0 0046; # (℉; ℉; ℉; °F; °F; ) DEGREE FAHRENHEIT +210A;210A;210A;0067;0067; # (ℊ; ℊ; ℊ; g; g; ) SCRIPT SMALL G +210B;210B;210B;0048;0048; # (ℋ; ℋ; ℋ; H; H; ) SCRIPT CAPITAL H +210C;210C;210C;0048;0048; # (ℌ; ℌ; ℌ; H; H; ) BLACK-LETTER CAPITAL H +210D;210D;210D;0048;0048; # (ℍ; ℍ; ℍ; H; H; ) DOUBLE-STRUCK CAPITAL H +210E;210E;210E;0068;0068; # (ℎ; ℎ; ℎ; h; h; ) PLANCK CONSTANT +210F;210F;210F;0127;0127; # (ℏ; ℏ; ℏ; ħ; ħ; ) PLANCK CONSTANT OVER TWO PI +2110;2110;2110;0049;0049; # (ℐ; ℐ; ℐ; I; I; ) SCRIPT CAPITAL I +2111;2111;2111;0049;0049; # (ℑ; ℑ; ℑ; I; I; ) BLACK-LETTER CAPITAL I +2112;2112;2112;004C;004C; # (ℒ; ℒ; ℒ; L; L; ) SCRIPT CAPITAL L +2113;2113;2113;006C;006C; # (ℓ; ℓ; ℓ; l; l; ) SCRIPT SMALL L +2115;2115;2115;004E;004E; # (ℕ; ℕ; ℕ; N; N; ) DOUBLE-STRUCK CAPITAL N +2116;2116;2116;004E 006F;004E 006F; # (№; №; №; No; No; ) NUMERO SIGN +2119;2119;2119;0050;0050; # (ℙ; ℙ; ℙ; P; P; ) DOUBLE-STRUCK CAPITAL P +211A;211A;211A;0051;0051; # (ℚ; ℚ; ℚ; Q; Q; ) DOUBLE-STRUCK CAPITAL Q +211B;211B;211B;0052;0052; # (ℛ; ℛ; ℛ; R; R; ) SCRIPT CAPITAL R +211C;211C;211C;0052;0052; # (ℜ; ℜ; ℜ; R; R; ) BLACK-LETTER CAPITAL R +211D;211D;211D;0052;0052; # (ℝ; ℝ; ℝ; R; R; ) DOUBLE-STRUCK CAPITAL R +2120;2120;2120;0053 004D;0053 004D; # (℠; ℠; ℠; SM; SM; ) SERVICE MARK +2121;2121;2121;0054 0045 004C;0054 0045 004C; # (℡; ℡; ℡; TEL; TEL; ) TELEPHONE SIGN +2122;2122;2122;0054 004D;0054 004D; # (™; ™; ™; TM; TM; ) TRADE MARK SIGN +2124;2124;2124;005A;005A; # (ℤ; ℤ; ℤ; Z; Z; ) DOUBLE-STRUCK CAPITAL Z +2126;03A9;03A9;03A9;03A9; # (Ω; Ω; Ω; Ω; Ω; ) OHM SIGN +2128;2128;2128;005A;005A; # (ℨ; ℨ; ℨ; Z; Z; ) BLACK-LETTER CAPITAL Z +212A;004B;004B;004B;004B; # (K; K; K; K; K; ) KELVIN SIGN +212B;00C5;0041 030A;00C5;0041 030A; # (Å; Å; A◌̊; Å; A◌̊; ) ANGSTROM SIGN +212C;212C;212C;0042;0042; # (ℬ; ℬ; ℬ; B; B; ) SCRIPT CAPITAL B +212D;212D;212D;0043;0043; # (ℭ; ℭ; ℭ; C; C; ) BLACK-LETTER CAPITAL C +212F;212F;212F;0065;0065; # (ℯ; ℯ; ℯ; e; e; ) SCRIPT SMALL E +2130;2130;2130;0045;0045; # (ℰ; ℰ; ℰ; E; E; ) SCRIPT CAPITAL E +2131;2131;2131;0046;0046; # (ℱ; ℱ; ℱ; F; F; ) SCRIPT CAPITAL F +2133;2133;2133;004D;004D; # (ℳ; ℳ; ℳ; M; M; ) SCRIPT CAPITAL M +2134;2134;2134;006F;006F; # (ℴ; ℴ; ℴ; o; o; ) SCRIPT SMALL O +2135;2135;2135;05D0;05D0; # (ℵ; ℵ; ℵ; א; א; ) ALEF SYMBOL +2136;2136;2136;05D1;05D1; # (ℶ; ℶ; ℶ; ב; ב; ) BET SYMBOL +2137;2137;2137;05D2;05D2; # (ℷ; ℷ; ℷ; ג; ג; ) GIMEL SYMBOL +2138;2138;2138;05D3;05D3; # (ℸ; ℸ; ℸ; ד; ד; ) DALET SYMBOL +2139;2139;2139;0069;0069; # (ℹ; ℹ; ℹ; i; i; ) INFORMATION SOURCE +213D;213D;213D;03B3;03B3; # (ℽ; ℽ; ℽ; γ; γ; ) DOUBLE-STRUCK SMALL GAMMA +213E;213E;213E;0393;0393; # (ℾ; ℾ; ℾ; Γ; Γ; ) DOUBLE-STRUCK CAPITAL GAMMA +213F;213F;213F;03A0;03A0; # (ℿ; ℿ; ℿ; Π; Π; ) DOUBLE-STRUCK CAPITAL PI +2140;2140;2140;2211;2211; # (⅀; ⅀; ⅀; ∑; ∑; ) DOUBLE-STRUCK N-ARY SUMMATION +2145;2145;2145;0044;0044; # (ⅅ; ⅅ; ⅅ; D; D; ) DOUBLE-STRUCK ITALIC CAPITAL D +2146;2146;2146;0064;0064; # (ⅆ; ⅆ; ⅆ; d; d; ) DOUBLE-STRUCK ITALIC SMALL D +2147;2147;2147;0065;0065; # (ⅇ; ⅇ; ⅇ; e; e; ) DOUBLE-STRUCK ITALIC SMALL E +2148;2148;2148;0069;0069; # (ⅈ; ⅈ; ⅈ; i; i; ) DOUBLE-STRUCK ITALIC SMALL I +2149;2149;2149;006A;006A; # (ⅉ; ⅉ; ⅉ; j; j; ) DOUBLE-STRUCK ITALIC SMALL J +2153;2153;2153;0031 2044 0033;0031 2044 0033; # (⅓; ⅓; ⅓; 1⁄3; 1⁄3; ) VULGAR FRACTION ONE THIRD +2154;2154;2154;0032 2044 0033;0032 2044 0033; # (⅔; ⅔; ⅔; 2⁄3; 2⁄3; ) VULGAR FRACTION TWO THIRDS +2155;2155;2155;0031 2044 0035;0031 2044 0035; # (⅕; ⅕; ⅕; 1⁄5; 1⁄5; ) VULGAR FRACTION ONE FIFTH +2156;2156;2156;0032 2044 0035;0032 2044 0035; # (⅖; ⅖; ⅖; 2⁄5; 2⁄5; ) VULGAR FRACTION TWO FIFTHS +2157;2157;2157;0033 2044 0035;0033 2044 0035; # (⅗; ⅗; ⅗; 3⁄5; 3⁄5; ) VULGAR FRACTION THREE FIFTHS +2158;2158;2158;0034 2044 0035;0034 2044 0035; # (⅘; ⅘; ⅘; 4⁄5; 4⁄5; ) VULGAR FRACTION FOUR FIFTHS +2159;2159;2159;0031 2044 0036;0031 2044 0036; # (⅙; ⅙; ⅙; 1⁄6; 1⁄6; ) VULGAR FRACTION ONE SIXTH +215A;215A;215A;0035 2044 0036;0035 2044 0036; # (⅚; ⅚; ⅚; 5⁄6; 5⁄6; ) VULGAR FRACTION FIVE SIXTHS +215B;215B;215B;0031 2044 0038;0031 2044 0038; # (⅛; ⅛; ⅛; 1⁄8; 1⁄8; ) VULGAR FRACTION ONE EIGHTH +215C;215C;215C;0033 2044 0038;0033 2044 0038; # (⅜; ⅜; ⅜; 3⁄8; 3⁄8; ) VULGAR FRACTION THREE EIGHTHS +215D;215D;215D;0035 2044 0038;0035 2044 0038; # (⅝; ⅝; ⅝; 5⁄8; 5⁄8; ) VULGAR FRACTION FIVE EIGHTHS +215E;215E;215E;0037 2044 0038;0037 2044 0038; # (⅞; ⅞; ⅞; 7⁄8; 7⁄8; ) VULGAR FRACTION SEVEN EIGHTHS +215F;215F;215F;0031 2044;0031 2044; # (⅟; ⅟; ⅟; 1⁄; 1⁄; ) FRACTION NUMERATOR ONE +2160;2160;2160;0049;0049; # (Ⅰ; Ⅰ; Ⅰ; I; I; ) ROMAN NUMERAL ONE +2161;2161;2161;0049 0049;0049 0049; # (Ⅱ; Ⅱ; Ⅱ; II; II; ) ROMAN NUMERAL TWO +2162;2162;2162;0049 0049 0049;0049 0049 0049; # (Ⅲ; Ⅲ; Ⅲ; III; III; ) ROMAN NUMERAL THREE +2163;2163;2163;0049 0056;0049 0056; # (Ⅳ; Ⅳ; Ⅳ; IV; IV; ) ROMAN NUMERAL FOUR +2164;2164;2164;0056;0056; # (Ⅴ; Ⅴ; Ⅴ; V; V; ) ROMAN NUMERAL FIVE +2165;2165;2165;0056 0049;0056 0049; # (Ⅵ; Ⅵ; Ⅵ; VI; VI; ) ROMAN NUMERAL SIX +2166;2166;2166;0056 0049 0049;0056 0049 0049; # (Ⅶ; Ⅶ; Ⅶ; VII; VII; ) ROMAN NUMERAL SEVEN +2167;2167;2167;0056 0049 0049 0049;0056 0049 0049 0049; # (Ⅷ; Ⅷ; Ⅷ; VIII; VIII; ) ROMAN NUMERAL EIGHT +2168;2168;2168;0049 0058;0049 0058; # (Ⅸ; Ⅸ; Ⅸ; IX; IX; ) ROMAN NUMERAL NINE +2169;2169;2169;0058;0058; # (Ⅹ; Ⅹ; Ⅹ; X; X; ) ROMAN NUMERAL TEN +216A;216A;216A;0058 0049;0058 0049; # (Ⅺ; Ⅺ; Ⅺ; XI; XI; ) ROMAN NUMERAL ELEVEN +216B;216B;216B;0058 0049 0049;0058 0049 0049; # (Ⅻ; Ⅻ; Ⅻ; XII; XII; ) ROMAN NUMERAL TWELVE +216C;216C;216C;004C;004C; # (Ⅼ; Ⅼ; Ⅼ; L; L; ) ROMAN NUMERAL FIFTY +216D;216D;216D;0043;0043; # (Ⅽ; Ⅽ; Ⅽ; C; C; ) ROMAN NUMERAL ONE HUNDRED +216E;216E;216E;0044;0044; # (Ⅾ; Ⅾ; Ⅾ; D; D; ) ROMAN NUMERAL FIVE HUNDRED +216F;216F;216F;004D;004D; # (Ⅿ; Ⅿ; Ⅿ; M; M; ) ROMAN NUMERAL ONE THOUSAND +2170;2170;2170;0069;0069; # (ⅰ; ⅰ; ⅰ; i; i; ) SMALL ROMAN NUMERAL ONE +2171;2171;2171;0069 0069;0069 0069; # (ⅱ; ⅱ; ⅱ; ii; ii; ) SMALL ROMAN NUMERAL TWO +2172;2172;2172;0069 0069 0069;0069 0069 0069; # (ⅲ; ⅲ; ⅲ; iii; iii; ) SMALL ROMAN NUMERAL THREE +2173;2173;2173;0069 0076;0069 0076; # (ⅳ; ⅳ; ⅳ; iv; iv; ) SMALL ROMAN NUMERAL FOUR +2174;2174;2174;0076;0076; # (ⅴ; ⅴ; ⅴ; v; v; ) SMALL ROMAN NUMERAL FIVE +2175;2175;2175;0076 0069;0076 0069; # (ⅵ; ⅵ; ⅵ; vi; vi; ) SMALL ROMAN NUMERAL SIX +2176;2176;2176;0076 0069 0069;0076 0069 0069; # (ⅶ; ⅶ; ⅶ; vii; vii; ) SMALL ROMAN NUMERAL SEVEN +2177;2177;2177;0076 0069 0069 0069;0076 0069 0069 0069; # (ⅷ; ⅷ; ⅷ; viii; viii; ) SMALL ROMAN NUMERAL EIGHT +2178;2178;2178;0069 0078;0069 0078; # (ⅸ; ⅸ; ⅸ; ix; ix; ) SMALL ROMAN NUMERAL NINE +2179;2179;2179;0078;0078; # (ⅹ; ⅹ; ⅹ; x; x; ) SMALL ROMAN NUMERAL TEN +217A;217A;217A;0078 0069;0078 0069; # (ⅺ; ⅺ; ⅺ; xi; xi; ) SMALL ROMAN NUMERAL ELEVEN +217B;217B;217B;0078 0069 0069;0078 0069 0069; # (ⅻ; ⅻ; ⅻ; xii; xii; ) SMALL ROMAN NUMERAL TWELVE +217C;217C;217C;006C;006C; # (ⅼ; ⅼ; ⅼ; l; l; ) SMALL ROMAN NUMERAL FIFTY +217D;217D;217D;0063;0063; # (ⅽ; ⅽ; ⅽ; c; c; ) SMALL ROMAN NUMERAL ONE HUNDRED +217E;217E;217E;0064;0064; # (ⅾ; ⅾ; ⅾ; d; d; ) SMALL ROMAN NUMERAL FIVE HUNDRED +217F;217F;217F;006D;006D; # (ⅿ; ⅿ; ⅿ; m; m; ) SMALL ROMAN NUMERAL ONE THOUSAND +219A;219A;2190 0338;219A;2190 0338; # (↚; ↚; ←◌̸; ↚; ←◌̸; ) LEFTWARDS ARROW WITH STROKE +219B;219B;2192 0338;219B;2192 0338; # (↛; ↛; →◌̸; ↛; →◌̸; ) RIGHTWARDS ARROW WITH STROKE +21AE;21AE;2194 0338;21AE;2194 0338; # (↮; ↮; ↔◌̸; ↮; ↔◌̸; ) LEFT RIGHT ARROW WITH STROKE +21CD;21CD;21D0 0338;21CD;21D0 0338; # (⇍; ⇍; ⇐◌̸; ⇍; ⇐◌̸; ) LEFTWARDS DOUBLE ARROW WITH STROKE +21CE;21CE;21D4 0338;21CE;21D4 0338; # (⇎; ⇎; ⇔◌̸; ⇎; ⇔◌̸; ) LEFT RIGHT DOUBLE ARROW WITH STROKE +21CF;21CF;21D2 0338;21CF;21D2 0338; # (⇏; ⇏; ⇒◌̸; ⇏; ⇒◌̸; ) RIGHTWARDS DOUBLE ARROW WITH STROKE +2204;2204;2203 0338;2204;2203 0338; # (∄; ∄; ∃◌̸; ∄; ∃◌̸; ) THERE DOES NOT EXIST +2209;2209;2208 0338;2209;2208 0338; # (∉; ∉; ∈◌̸; ∉; ∈◌̸; ) NOT AN ELEMENT OF +220C;220C;220B 0338;220C;220B 0338; # (∌; ∌; ∋◌̸; ∌; ∋◌̸; ) DOES NOT CONTAIN AS MEMBER +2224;2224;2223 0338;2224;2223 0338; # (∤; ∤; ∣◌̸; ∤; ∣◌̸; ) DOES NOT DIVIDE +2226;2226;2225 0338;2226;2225 0338; # (∦; ∦; ∥◌̸; ∦; ∥◌̸; ) NOT PARALLEL TO +222C;222C;222C;222B 222B;222B 222B; # (∬; ∬; ∬; ∫∫; ∫∫; ) DOUBLE INTEGRAL +222D;222D;222D;222B 222B 222B;222B 222B 222B; # (∭; ∭; ∭; ∫∫∫; ∫∫∫; ) TRIPLE INTEGRAL +222F;222F;222F;222E 222E;222E 222E; # (∯; ∯; ∯; ∮∮; ∮∮; ) SURFACE INTEGRAL +2230;2230;2230;222E 222E 222E;222E 222E 222E; # (∰; ∰; ∰; ∮∮∮; ∮∮∮; ) VOLUME INTEGRAL +2241;2241;223C 0338;2241;223C 0338; # (≁; ≁; ∼◌̸; ≁; ∼◌̸; ) NOT TILDE +2244;2244;2243 0338;2244;2243 0338; # (≄; ≄; ≃◌̸; ≄; ≃◌̸; ) NOT ASYMPTOTICALLY EQUAL TO +2247;2247;2245 0338;2247;2245 0338; # (≇; ≇; ≅◌̸; ≇; ≅◌̸; ) NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO +2249;2249;2248 0338;2249;2248 0338; # (≉; ≉; ≈◌̸; ≉; ≈◌̸; ) NOT ALMOST EQUAL TO +2260;2260;003D 0338;2260;003D 0338; # (≠; ≠; =◌̸; ≠; =◌̸; ) NOT EQUAL TO +2262;2262;2261 0338;2262;2261 0338; # (≢; ≢; ≡◌̸; ≢; ≡◌̸; ) NOT IDENTICAL TO +226D;226D;224D 0338;226D;224D 0338; # (≭; ≭; ≍◌̸; ≭; ≍◌̸; ) NOT EQUIVALENT TO +226E;226E;003C 0338;226E;003C 0338; # (≮; ≮; <◌̸; ≮; <◌̸; ) NOT LESS-THAN +226F;226F;003E 0338;226F;003E 0338; # (≯; ≯; >◌̸; ≯; >◌̸; ) NOT GREATER-THAN +2270;2270;2264 0338;2270;2264 0338; # (≰; ≰; ≤◌̸; ≰; ≤◌̸; ) NEITHER LESS-THAN NOR EQUAL TO +2271;2271;2265 0338;2271;2265 0338; # (≱; ≱; ≥◌̸; ≱; ≥◌̸; ) NEITHER GREATER-THAN NOR EQUAL TO +2274;2274;2272 0338;2274;2272 0338; # (≴; ≴; ≲◌̸; ≴; ≲◌̸; ) NEITHER LESS-THAN NOR EQUIVALENT TO +2275;2275;2273 0338;2275;2273 0338; # (≵; ≵; ≳◌̸; ≵; ≳◌̸; ) NEITHER GREATER-THAN NOR EQUIVALENT TO +2278;2278;2276 0338;2278;2276 0338; # (≸; ≸; ≶◌̸; ≸; ≶◌̸; ) NEITHER LESS-THAN NOR GREATER-THAN +2279;2279;2277 0338;2279;2277 0338; # (≹; ≹; ≷◌̸; ≹; ≷◌̸; ) NEITHER GREATER-THAN NOR LESS-THAN +2280;2280;227A 0338;2280;227A 0338; # (⊀; ⊀; ≺◌̸; ⊀; ≺◌̸; ) DOES NOT PRECEDE +2281;2281;227B 0338;2281;227B 0338; # (⊁; ⊁; ≻◌̸; ⊁; ≻◌̸; ) DOES NOT SUCCEED +2284;2284;2282 0338;2284;2282 0338; # (⊄; ⊄; ⊂◌̸; ⊄; ⊂◌̸; ) NOT A SUBSET OF +2285;2285;2283 0338;2285;2283 0338; # (⊅; ⊅; ⊃◌̸; ⊅; ⊃◌̸; ) NOT A SUPERSET OF +2288;2288;2286 0338;2288;2286 0338; # (⊈; ⊈; ⊆◌̸; ⊈; ⊆◌̸; ) NEITHER A SUBSET OF NOR EQUAL TO +2289;2289;2287 0338;2289;2287 0338; # (⊉; ⊉; ⊇◌̸; ⊉; ⊇◌̸; ) NEITHER A SUPERSET OF NOR EQUAL TO +22AC;22AC;22A2 0338;22AC;22A2 0338; # (⊬; ⊬; ⊢◌̸; ⊬; ⊢◌̸; ) DOES NOT PROVE +22AD;22AD;22A8 0338;22AD;22A8 0338; # (⊭; ⊭; ⊨◌̸; ⊭; ⊨◌̸; ) NOT TRUE +22AE;22AE;22A9 0338;22AE;22A9 0338; # (⊮; ⊮; ⊩◌̸; ⊮; ⊩◌̸; ) DOES NOT FORCE +22AF;22AF;22AB 0338;22AF;22AB 0338; # (⊯; ⊯; ⊫◌̸; ⊯; ⊫◌̸; ) NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE +22E0;22E0;227C 0338;22E0;227C 0338; # (⋠; ⋠; ≼◌̸; ⋠; ≼◌̸; ) DOES NOT PRECEDE OR EQUAL +22E1;22E1;227D 0338;22E1;227D 0338; # (⋡; ⋡; ≽◌̸; ⋡; ≽◌̸; ) DOES NOT SUCCEED OR EQUAL +22E2;22E2;2291 0338;22E2;2291 0338; # (⋢; ⋢; ⊑◌̸; ⋢; ⊑◌̸; ) NOT SQUARE IMAGE OF OR EQUAL TO +22E3;22E3;2292 0338;22E3;2292 0338; # (⋣; ⋣; ⊒◌̸; ⋣; ⊒◌̸; ) NOT SQUARE ORIGINAL OF OR EQUAL TO +22EA;22EA;22B2 0338;22EA;22B2 0338; # (⋪; ⋪; ⊲◌̸; ⋪; ⊲◌̸; ) NOT NORMAL SUBGROUP OF +22EB;22EB;22B3 0338;22EB;22B3 0338; # (⋫; ⋫; ⊳◌̸; ⋫; ⊳◌̸; ) DOES NOT CONTAIN AS NORMAL SUBGROUP +22EC;22EC;22B4 0338;22EC;22B4 0338; # (⋬; ⋬; ⊴◌̸; ⋬; ⊴◌̸; ) NOT NORMAL SUBGROUP OF OR EQUAL TO +22ED;22ED;22B5 0338;22ED;22B5 0338; # (⋭; ⋭; ⊵◌̸; ⋭; ⊵◌̸; ) DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL +2329;3008;3008;3008;3008; # (〈; 〈; 〈; 〈; 〈; ) LEFT-POINTING ANGLE BRACKET +232A;3009;3009;3009;3009; # (〉; 〉; 〉; 〉; 〉; ) RIGHT-POINTING ANGLE BRACKET +2460;2460;2460;0031;0031; # (①; ①; ①; 1; 1; ) CIRCLED DIGIT ONE +2461;2461;2461;0032;0032; # (②; ②; ②; 2; 2; ) CIRCLED DIGIT TWO +2462;2462;2462;0033;0033; # (③; ③; ③; 3; 3; ) CIRCLED DIGIT THREE +2463;2463;2463;0034;0034; # (④; ④; ④; 4; 4; ) CIRCLED DIGIT FOUR +2464;2464;2464;0035;0035; # (⑤; ⑤; ⑤; 5; 5; ) CIRCLED DIGIT FIVE +2465;2465;2465;0036;0036; # (⑥; ⑥; ⑥; 6; 6; ) CIRCLED DIGIT SIX +2466;2466;2466;0037;0037; # (⑦; ⑦; ⑦; 7; 7; ) CIRCLED DIGIT SEVEN +2467;2467;2467;0038;0038; # (⑧; ⑧; ⑧; 8; 8; ) CIRCLED DIGIT EIGHT +2468;2468;2468;0039;0039; # (⑨; ⑨; ⑨; 9; 9; ) CIRCLED DIGIT NINE +2469;2469;2469;0031 0030;0031 0030; # (⑩; ⑩; ⑩; 10; 10; ) CIRCLED NUMBER TEN +246A;246A;246A;0031 0031;0031 0031; # (⑪; ⑪; ⑪; 11; 11; ) CIRCLED NUMBER ELEVEN +246B;246B;246B;0031 0032;0031 0032; # (⑫; ⑫; ⑫; 12; 12; ) CIRCLED NUMBER TWELVE +246C;246C;246C;0031 0033;0031 0033; # (⑬; ⑬; ⑬; 13; 13; ) CIRCLED NUMBER THIRTEEN +246D;246D;246D;0031 0034;0031 0034; # (⑭; ⑭; ⑭; 14; 14; ) CIRCLED NUMBER FOURTEEN +246E;246E;246E;0031 0035;0031 0035; # (⑮; ⑮; ⑮; 15; 15; ) CIRCLED NUMBER FIFTEEN +246F;246F;246F;0031 0036;0031 0036; # (⑯; ⑯; ⑯; 16; 16; ) CIRCLED NUMBER SIXTEEN +2470;2470;2470;0031 0037;0031 0037; # (⑰; ⑰; ⑰; 17; 17; ) CIRCLED NUMBER SEVENTEEN +2471;2471;2471;0031 0038;0031 0038; # (⑱; ⑱; ⑱; 18; 18; ) CIRCLED NUMBER EIGHTEEN +2472;2472;2472;0031 0039;0031 0039; # (⑲; ⑲; ⑲; 19; 19; ) CIRCLED NUMBER NINETEEN +2473;2473;2473;0032 0030;0032 0030; # (⑳; ⑳; ⑳; 20; 20; ) CIRCLED NUMBER TWENTY +2474;2474;2474;0028 0031 0029;0028 0031 0029; # (⑴; ⑴; ⑴; (1); (1); ) PARENTHESIZED DIGIT ONE +2475;2475;2475;0028 0032 0029;0028 0032 0029; # (⑵; ⑵; ⑵; (2); (2); ) PARENTHESIZED DIGIT TWO +2476;2476;2476;0028 0033 0029;0028 0033 0029; # (⑶; ⑶; ⑶; (3); (3); ) PARENTHESIZED DIGIT THREE +2477;2477;2477;0028 0034 0029;0028 0034 0029; # (⑷; ⑷; ⑷; (4); (4); ) PARENTHESIZED DIGIT FOUR +2478;2478;2478;0028 0035 0029;0028 0035 0029; # (⑸; ⑸; ⑸; (5); (5); ) PARENTHESIZED DIGIT FIVE +2479;2479;2479;0028 0036 0029;0028 0036 0029; # (⑹; ⑹; ⑹; (6); (6); ) PARENTHESIZED DIGIT SIX +247A;247A;247A;0028 0037 0029;0028 0037 0029; # (⑺; ⑺; ⑺; (7); (7); ) PARENTHESIZED DIGIT SEVEN +247B;247B;247B;0028 0038 0029;0028 0038 0029; # (⑻; ⑻; ⑻; (8); (8); ) PARENTHESIZED DIGIT EIGHT +247C;247C;247C;0028 0039 0029;0028 0039 0029; # (⑼; ⑼; ⑼; (9); (9); ) PARENTHESIZED DIGIT NINE +247D;247D;247D;0028 0031 0030 0029;0028 0031 0030 0029; # (⑽; ⑽; ⑽; (10); (10); ) PARENTHESIZED NUMBER TEN +247E;247E;247E;0028 0031 0031 0029;0028 0031 0031 0029; # (⑾; ⑾; ⑾; (11); (11); ) PARENTHESIZED NUMBER ELEVEN +247F;247F;247F;0028 0031 0032 0029;0028 0031 0032 0029; # (⑿; ⑿; ⑿; (12); (12); ) PARENTHESIZED NUMBER TWELVE +2480;2480;2480;0028 0031 0033 0029;0028 0031 0033 0029; # (⒀; ⒀; ⒀; (13); (13); ) PARENTHESIZED NUMBER THIRTEEN +2481;2481;2481;0028 0031 0034 0029;0028 0031 0034 0029; # (⒁; ⒁; ⒁; (14); (14); ) PARENTHESIZED NUMBER FOURTEEN +2482;2482;2482;0028 0031 0035 0029;0028 0031 0035 0029; # (⒂; ⒂; ⒂; (15); (15); ) PARENTHESIZED NUMBER FIFTEEN +2483;2483;2483;0028 0031 0036 0029;0028 0031 0036 0029; # (⒃; ⒃; ⒃; (16); (16); ) PARENTHESIZED NUMBER SIXTEEN +2484;2484;2484;0028 0031 0037 0029;0028 0031 0037 0029; # (⒄; ⒄; ⒄; (17); (17); ) PARENTHESIZED NUMBER SEVENTEEN +2485;2485;2485;0028 0031 0038 0029;0028 0031 0038 0029; # (⒅; ⒅; ⒅; (18); (18); ) PARENTHESIZED NUMBER EIGHTEEN +2486;2486;2486;0028 0031 0039 0029;0028 0031 0039 0029; # (⒆; ⒆; ⒆; (19); (19); ) PARENTHESIZED NUMBER NINETEEN +2487;2487;2487;0028 0032 0030 0029;0028 0032 0030 0029; # (⒇; ⒇; ⒇; (20); (20); ) PARENTHESIZED NUMBER TWENTY +2488;2488;2488;0031 002E;0031 002E; # (⒈; ⒈; ⒈; 1.; 1.; ) DIGIT ONE FULL STOP +2489;2489;2489;0032 002E;0032 002E; # (⒉; ⒉; ⒉; 2.; 2.; ) DIGIT TWO FULL STOP +248A;248A;248A;0033 002E;0033 002E; # (⒊; ⒊; ⒊; 3.; 3.; ) DIGIT THREE FULL STOP +248B;248B;248B;0034 002E;0034 002E; # (⒋; ⒋; ⒋; 4.; 4.; ) DIGIT FOUR FULL STOP +248C;248C;248C;0035 002E;0035 002E; # (⒌; ⒌; ⒌; 5.; 5.; ) DIGIT FIVE FULL STOP +248D;248D;248D;0036 002E;0036 002E; # (⒍; ⒍; ⒍; 6.; 6.; ) DIGIT SIX FULL STOP +248E;248E;248E;0037 002E;0037 002E; # (⒎; ⒎; ⒎; 7.; 7.; ) DIGIT SEVEN FULL STOP +248F;248F;248F;0038 002E;0038 002E; # (⒏; ⒏; ⒏; 8.; 8.; ) DIGIT EIGHT FULL STOP +2490;2490;2490;0039 002E;0039 002E; # (⒐; ⒐; ⒐; 9.; 9.; ) DIGIT NINE FULL STOP +2491;2491;2491;0031 0030 002E;0031 0030 002E; # (⒑; ⒑; ⒑; 10.; 10.; ) NUMBER TEN FULL STOP +2492;2492;2492;0031 0031 002E;0031 0031 002E; # (⒒; ⒒; ⒒; 11.; 11.; ) NUMBER ELEVEN FULL STOP +2493;2493;2493;0031 0032 002E;0031 0032 002E; # (⒓; ⒓; ⒓; 12.; 12.; ) NUMBER TWELVE FULL STOP +2494;2494;2494;0031 0033 002E;0031 0033 002E; # (⒔; ⒔; ⒔; 13.; 13.; ) NUMBER THIRTEEN FULL STOP +2495;2495;2495;0031 0034 002E;0031 0034 002E; # (⒕; ⒕; ⒕; 14.; 14.; ) NUMBER FOURTEEN FULL STOP +2496;2496;2496;0031 0035 002E;0031 0035 002E; # (⒖; ⒖; ⒖; 15.; 15.; ) NUMBER FIFTEEN FULL STOP +2497;2497;2497;0031 0036 002E;0031 0036 002E; # (⒗; ⒗; ⒗; 16.; 16.; ) NUMBER SIXTEEN FULL STOP +2498;2498;2498;0031 0037 002E;0031 0037 002E; # (⒘; ⒘; ⒘; 17.; 17.; ) NUMBER SEVENTEEN FULL STOP +2499;2499;2499;0031 0038 002E;0031 0038 002E; # (⒙; ⒙; ⒙; 18.; 18.; ) NUMBER EIGHTEEN FULL STOP +249A;249A;249A;0031 0039 002E;0031 0039 002E; # (⒚; ⒚; ⒚; 19.; 19.; ) NUMBER NINETEEN FULL STOP +249B;249B;249B;0032 0030 002E;0032 0030 002E; # (⒛; ⒛; ⒛; 20.; 20.; ) NUMBER TWENTY FULL STOP +249C;249C;249C;0028 0061 0029;0028 0061 0029; # (⒜; ⒜; ⒜; (a); (a); ) PARENTHESIZED LATIN SMALL LETTER A +249D;249D;249D;0028 0062 0029;0028 0062 0029; # (⒝; ⒝; ⒝; (b); (b); ) PARENTHESIZED LATIN SMALL LETTER B +249E;249E;249E;0028 0063 0029;0028 0063 0029; # (⒞; ⒞; ⒞; (c); (c); ) PARENTHESIZED LATIN SMALL LETTER C +249F;249F;249F;0028 0064 0029;0028 0064 0029; # (⒟; ⒟; ⒟; (d); (d); ) PARENTHESIZED LATIN SMALL LETTER D +24A0;24A0;24A0;0028 0065 0029;0028 0065 0029; # (⒠; ⒠; ⒠; (e); (e); ) PARENTHESIZED LATIN SMALL LETTER E +24A1;24A1;24A1;0028 0066 0029;0028 0066 0029; # (⒡; ⒡; ⒡; (f); (f); ) PARENTHESIZED LATIN SMALL LETTER F +24A2;24A2;24A2;0028 0067 0029;0028 0067 0029; # (⒢; ⒢; ⒢; (g); (g); ) PARENTHESIZED LATIN SMALL LETTER G +24A3;24A3;24A3;0028 0068 0029;0028 0068 0029; # (⒣; ⒣; ⒣; (h); (h); ) PARENTHESIZED LATIN SMALL LETTER H +24A4;24A4;24A4;0028 0069 0029;0028 0069 0029; # (⒤; ⒤; ⒤; (i); (i); ) PARENTHESIZED LATIN SMALL LETTER I +24A5;24A5;24A5;0028 006A 0029;0028 006A 0029; # (⒥; ⒥; ⒥; (j); (j); ) PARENTHESIZED LATIN SMALL LETTER J +24A6;24A6;24A6;0028 006B 0029;0028 006B 0029; # (⒦; ⒦; ⒦; (k); (k); ) PARENTHESIZED LATIN SMALL LETTER K +24A7;24A7;24A7;0028 006C 0029;0028 006C 0029; # (⒧; ⒧; ⒧; (l); (l); ) PARENTHESIZED LATIN SMALL LETTER L +24A8;24A8;24A8;0028 006D 0029;0028 006D 0029; # (⒨; ⒨; ⒨; (m); (m); ) PARENTHESIZED LATIN SMALL LETTER M +24A9;24A9;24A9;0028 006E 0029;0028 006E 0029; # (⒩; ⒩; ⒩; (n); (n); ) PARENTHESIZED LATIN SMALL LETTER N +24AA;24AA;24AA;0028 006F 0029;0028 006F 0029; # (⒪; ⒪; ⒪; (o); (o); ) PARENTHESIZED LATIN SMALL LETTER O +24AB;24AB;24AB;0028 0070 0029;0028 0070 0029; # (⒫; ⒫; ⒫; (p); (p); ) PARENTHESIZED LATIN SMALL LETTER P +24AC;24AC;24AC;0028 0071 0029;0028 0071 0029; # (⒬; ⒬; ⒬; (q); (q); ) PARENTHESIZED LATIN SMALL LETTER Q +24AD;24AD;24AD;0028 0072 0029;0028 0072 0029; # (⒭; ⒭; ⒭; (r); (r); ) PARENTHESIZED LATIN SMALL LETTER R +24AE;24AE;24AE;0028 0073 0029;0028 0073 0029; # (⒮; ⒮; ⒮; (s); (s); ) PARENTHESIZED LATIN SMALL LETTER S +24AF;24AF;24AF;0028 0074 0029;0028 0074 0029; # (⒯; ⒯; ⒯; (t); (t); ) PARENTHESIZED LATIN SMALL LETTER T +24B0;24B0;24B0;0028 0075 0029;0028 0075 0029; # (⒰; ⒰; ⒰; (u); (u); ) PARENTHESIZED LATIN SMALL LETTER U +24B1;24B1;24B1;0028 0076 0029;0028 0076 0029; # (⒱; ⒱; ⒱; (v); (v); ) PARENTHESIZED LATIN SMALL LETTER V +24B2;24B2;24B2;0028 0077 0029;0028 0077 0029; # (⒲; ⒲; ⒲; (w); (w); ) PARENTHESIZED LATIN SMALL LETTER W +24B3;24B3;24B3;0028 0078 0029;0028 0078 0029; # (⒳; ⒳; ⒳; (x); (x); ) PARENTHESIZED LATIN SMALL LETTER X +24B4;24B4;24B4;0028 0079 0029;0028 0079 0029; # (⒴; ⒴; ⒴; (y); (y); ) PARENTHESIZED LATIN SMALL LETTER Y +24B5;24B5;24B5;0028 007A 0029;0028 007A 0029; # (⒵; ⒵; ⒵; (z); (z); ) PARENTHESIZED LATIN SMALL LETTER Z +24B6;24B6;24B6;0041;0041; # (Ⓐ; Ⓐ; Ⓐ; A; A; ) CIRCLED LATIN CAPITAL LETTER A +24B7;24B7;24B7;0042;0042; # (Ⓑ; Ⓑ; Ⓑ; B; B; ) CIRCLED LATIN CAPITAL LETTER B +24B8;24B8;24B8;0043;0043; # (Ⓒ; Ⓒ; Ⓒ; C; C; ) CIRCLED LATIN CAPITAL LETTER C +24B9;24B9;24B9;0044;0044; # (Ⓓ; Ⓓ; Ⓓ; D; D; ) CIRCLED LATIN CAPITAL LETTER D +24BA;24BA;24BA;0045;0045; # (Ⓔ; Ⓔ; Ⓔ; E; E; ) CIRCLED LATIN CAPITAL LETTER E +24BB;24BB;24BB;0046;0046; # (Ⓕ; Ⓕ; Ⓕ; F; F; ) CIRCLED LATIN CAPITAL LETTER F +24BC;24BC;24BC;0047;0047; # (Ⓖ; Ⓖ; Ⓖ; G; G; ) CIRCLED LATIN CAPITAL LETTER G +24BD;24BD;24BD;0048;0048; # (Ⓗ; Ⓗ; Ⓗ; H; H; ) CIRCLED LATIN CAPITAL LETTER H +24BE;24BE;24BE;0049;0049; # (Ⓘ; Ⓘ; Ⓘ; I; I; ) CIRCLED LATIN CAPITAL LETTER I +24BF;24BF;24BF;004A;004A; # (Ⓙ; Ⓙ; Ⓙ; J; J; ) CIRCLED LATIN CAPITAL LETTER J +24C0;24C0;24C0;004B;004B; # (Ⓚ; Ⓚ; Ⓚ; K; K; ) CIRCLED LATIN CAPITAL LETTER K +24C1;24C1;24C1;004C;004C; # (Ⓛ; Ⓛ; Ⓛ; L; L; ) CIRCLED LATIN CAPITAL LETTER L +24C2;24C2;24C2;004D;004D; # (Ⓜ; Ⓜ; Ⓜ; M; M; ) CIRCLED LATIN CAPITAL LETTER M +24C3;24C3;24C3;004E;004E; # (Ⓝ; Ⓝ; Ⓝ; N; N; ) CIRCLED LATIN CAPITAL LETTER N +24C4;24C4;24C4;004F;004F; # (Ⓞ; Ⓞ; Ⓞ; O; O; ) CIRCLED LATIN CAPITAL LETTER O +24C5;24C5;24C5;0050;0050; # (Ⓟ; Ⓟ; Ⓟ; P; P; ) CIRCLED LATIN CAPITAL LETTER P +24C6;24C6;24C6;0051;0051; # (Ⓠ; Ⓠ; Ⓠ; Q; Q; ) CIRCLED LATIN CAPITAL LETTER Q +24C7;24C7;24C7;0052;0052; # (Ⓡ; Ⓡ; Ⓡ; R; R; ) CIRCLED LATIN CAPITAL LETTER R +24C8;24C8;24C8;0053;0053; # (Ⓢ; Ⓢ; Ⓢ; S; S; ) CIRCLED LATIN CAPITAL LETTER S +24C9;24C9;24C9;0054;0054; # (Ⓣ; Ⓣ; Ⓣ; T; T; ) CIRCLED LATIN CAPITAL LETTER T +24CA;24CA;24CA;0055;0055; # (Ⓤ; Ⓤ; Ⓤ; U; U; ) CIRCLED LATIN CAPITAL LETTER U +24CB;24CB;24CB;0056;0056; # (Ⓥ; Ⓥ; Ⓥ; V; V; ) CIRCLED LATIN CAPITAL LETTER V +24CC;24CC;24CC;0057;0057; # (Ⓦ; Ⓦ; Ⓦ; W; W; ) CIRCLED LATIN CAPITAL LETTER W +24CD;24CD;24CD;0058;0058; # (Ⓧ; Ⓧ; Ⓧ; X; X; ) CIRCLED LATIN CAPITAL LETTER X +24CE;24CE;24CE;0059;0059; # (Ⓨ; Ⓨ; Ⓨ; Y; Y; ) CIRCLED LATIN CAPITAL LETTER Y +24CF;24CF;24CF;005A;005A; # (Ⓩ; Ⓩ; Ⓩ; Z; Z; ) CIRCLED LATIN CAPITAL LETTER Z +24D0;24D0;24D0;0061;0061; # (ⓐ; ⓐ; ⓐ; a; a; ) CIRCLED LATIN SMALL LETTER A +24D1;24D1;24D1;0062;0062; # (ⓑ; ⓑ; ⓑ; b; b; ) CIRCLED LATIN SMALL LETTER B +24D2;24D2;24D2;0063;0063; # (ⓒ; ⓒ; ⓒ; c; c; ) CIRCLED LATIN SMALL LETTER C +24D3;24D3;24D3;0064;0064; # (ⓓ; ⓓ; ⓓ; d; d; ) CIRCLED LATIN SMALL LETTER D +24D4;24D4;24D4;0065;0065; # (ⓔ; ⓔ; ⓔ; e; e; ) CIRCLED LATIN SMALL LETTER E +24D5;24D5;24D5;0066;0066; # (ⓕ; ⓕ; ⓕ; f; f; ) CIRCLED LATIN SMALL LETTER F +24D6;24D6;24D6;0067;0067; # (ⓖ; ⓖ; ⓖ; g; g; ) CIRCLED LATIN SMALL LETTER G +24D7;24D7;24D7;0068;0068; # (ⓗ; ⓗ; ⓗ; h; h; ) CIRCLED LATIN SMALL LETTER H +24D8;24D8;24D8;0069;0069; # (ⓘ; ⓘ; ⓘ; i; i; ) CIRCLED LATIN SMALL LETTER I +24D9;24D9;24D9;006A;006A; # (ⓙ; ⓙ; ⓙ; j; j; ) CIRCLED LATIN SMALL LETTER J +24DA;24DA;24DA;006B;006B; # (ⓚ; ⓚ; ⓚ; k; k; ) CIRCLED LATIN SMALL LETTER K +24DB;24DB;24DB;006C;006C; # (ⓛ; ⓛ; ⓛ; l; l; ) CIRCLED LATIN SMALL LETTER L +24DC;24DC;24DC;006D;006D; # (ⓜ; ⓜ; ⓜ; m; m; ) CIRCLED LATIN SMALL LETTER M +24DD;24DD;24DD;006E;006E; # (ⓝ; ⓝ; ⓝ; n; n; ) CIRCLED LATIN SMALL LETTER N +24DE;24DE;24DE;006F;006F; # (ⓞ; ⓞ; ⓞ; o; o; ) CIRCLED LATIN SMALL LETTER O +24DF;24DF;24DF;0070;0070; # (ⓟ; ⓟ; ⓟ; p; p; ) CIRCLED LATIN SMALL LETTER P +24E0;24E0;24E0;0071;0071; # (ⓠ; ⓠ; ⓠ; q; q; ) CIRCLED LATIN SMALL LETTER Q +24E1;24E1;24E1;0072;0072; # (ⓡ; ⓡ; ⓡ; r; r; ) CIRCLED LATIN SMALL LETTER R +24E2;24E2;24E2;0073;0073; # (ⓢ; ⓢ; ⓢ; s; s; ) CIRCLED LATIN SMALL LETTER S +24E3;24E3;24E3;0074;0074; # (ⓣ; ⓣ; ⓣ; t; t; ) CIRCLED LATIN SMALL LETTER T +24E4;24E4;24E4;0075;0075; # (ⓤ; ⓤ; ⓤ; u; u; ) CIRCLED LATIN SMALL LETTER U +24E5;24E5;24E5;0076;0076; # (ⓥ; ⓥ; ⓥ; v; v; ) CIRCLED LATIN SMALL LETTER V +24E6;24E6;24E6;0077;0077; # (ⓦ; ⓦ; ⓦ; w; w; ) CIRCLED LATIN SMALL LETTER W +24E7;24E7;24E7;0078;0078; # (ⓧ; ⓧ; ⓧ; x; x; ) CIRCLED LATIN SMALL LETTER X +24E8;24E8;24E8;0079;0079; # (ⓨ; ⓨ; ⓨ; y; y; ) CIRCLED LATIN SMALL LETTER Y +24E9;24E9;24E9;007A;007A; # (ⓩ; ⓩ; ⓩ; z; z; ) CIRCLED LATIN SMALL LETTER Z +24EA;24EA;24EA;0030;0030; # (⓪; ⓪; ⓪; 0; 0; ) CIRCLED DIGIT ZERO +2A0C;2A0C;2A0C;222B 222B 222B 222B;222B 222B 222B 222B; # (⨌; ⨌; ⨌; ∫∫∫∫; ∫∫∫∫; ) QUADRUPLE INTEGRAL OPERATOR +2A74;2A74;2A74;003A 003A 003D;003A 003A 003D; # (⩴; ⩴; ⩴; ::=; ::=; ) DOUBLE COLON EQUAL +2A75;2A75;2A75;003D 003D;003D 003D; # (⩵; ⩵; ⩵; ==; ==; ) TWO CONSECUTIVE EQUALS SIGNS +2A76;2A76;2A76;003D 003D 003D;003D 003D 003D; # (⩶; ⩶; ⩶; ===; ===; ) THREE CONSECUTIVE EQUALS SIGNS +2ADC;2ADD 0338;2ADD 0338;2ADD 0338;2ADD 0338; # (⫝̸; ⫝◌̸; ⫝◌̸; ⫝◌̸; ⫝◌̸; ) FORKING +2E9F;2E9F;2E9F;6BCD;6BCD; # (⺟; ⺟; ⺟; 母; 母; ) CJK RADICAL MOTHER +2EF3;2EF3;2EF3;9F9F;9F9F; # (⻳; ⻳; ⻳; 龟; 龟; ) CJK RADICAL C-SIMPLIFIED TURTLE +2F00;2F00;2F00;4E00;4E00; # (⼀; ⼀; ⼀; 一; 一; ) KANGXI RADICAL ONE +2F01;2F01;2F01;4E28;4E28; # (⼁; ⼁; ⼁; 丨; 丨; ) KANGXI RADICAL LINE +2F02;2F02;2F02;4E36;4E36; # (⼂; ⼂; ⼂; 丶; 丶; ) KANGXI RADICAL DOT +2F03;2F03;2F03;4E3F;4E3F; # (⼃; ⼃; ⼃; 丿; 丿; ) KANGXI RADICAL SLASH +2F04;2F04;2F04;4E59;4E59; # (⼄; ⼄; ⼄; 乙; 乙; ) KANGXI RADICAL SECOND +2F05;2F05;2F05;4E85;4E85; # (⼅; ⼅; ⼅; 亅; 亅; ) KANGXI RADICAL HOOK +2F06;2F06;2F06;4E8C;4E8C; # (⼆; ⼆; ⼆; 二; 二; ) KANGXI RADICAL TWO +2F07;2F07;2F07;4EA0;4EA0; # (⼇; ⼇; ⼇; 亠; 亠; ) KANGXI RADICAL LID +2F08;2F08;2F08;4EBA;4EBA; # (⼈; ⼈; ⼈; 人; 人; ) KANGXI RADICAL MAN +2F09;2F09;2F09;513F;513F; # (⼉; ⼉; ⼉; 儿; 儿; ) KANGXI RADICAL LEGS +2F0A;2F0A;2F0A;5165;5165; # (⼊; ⼊; ⼊; 入; 入; ) KANGXI RADICAL ENTER +2F0B;2F0B;2F0B;516B;516B; # (⼋; ⼋; ⼋; 八; 八; ) KANGXI RADICAL EIGHT +2F0C;2F0C;2F0C;5182;5182; # (⼌; ⼌; ⼌; 冂; 冂; ) KANGXI RADICAL DOWN BOX +2F0D;2F0D;2F0D;5196;5196; # (⼍; ⼍; ⼍; 冖; 冖; ) KANGXI RADICAL COVER +2F0E;2F0E;2F0E;51AB;51AB; # (⼎; ⼎; ⼎; 冫; 冫; ) KANGXI RADICAL ICE +2F0F;2F0F;2F0F;51E0;51E0; # (⼏; ⼏; ⼏; 几; 几; ) KANGXI RADICAL TABLE +2F10;2F10;2F10;51F5;51F5; # (⼐; ⼐; ⼐; 凵; 凵; ) KANGXI RADICAL OPEN BOX +2F11;2F11;2F11;5200;5200; # (⼑; ⼑; ⼑; 刀; 刀; ) KANGXI RADICAL KNIFE +2F12;2F12;2F12;529B;529B; # (⼒; ⼒; ⼒; 力; 力; ) KANGXI RADICAL POWER +2F13;2F13;2F13;52F9;52F9; # (⼓; ⼓; ⼓; 勹; 勹; ) KANGXI RADICAL WRAP +2F14;2F14;2F14;5315;5315; # (⼔; ⼔; ⼔; 匕; 匕; ) KANGXI RADICAL SPOON +2F15;2F15;2F15;531A;531A; # (⼕; ⼕; ⼕; 匚; 匚; ) KANGXI RADICAL RIGHT OPEN BOX +2F16;2F16;2F16;5338;5338; # (⼖; ⼖; ⼖; 匸; 匸; ) KANGXI RADICAL HIDING ENCLOSURE +2F17;2F17;2F17;5341;5341; # (⼗; ⼗; ⼗; 十; 十; ) KANGXI RADICAL TEN +2F18;2F18;2F18;535C;535C; # (⼘; ⼘; ⼘; 卜; 卜; ) KANGXI RADICAL DIVINATION +2F19;2F19;2F19;5369;5369; # (⼙; ⼙; ⼙; 卩; 卩; ) KANGXI RADICAL SEAL +2F1A;2F1A;2F1A;5382;5382; # (⼚; ⼚; ⼚; 厂; 厂; ) KANGXI RADICAL CLIFF +2F1B;2F1B;2F1B;53B6;53B6; # (⼛; ⼛; ⼛; 厶; 厶; ) KANGXI RADICAL PRIVATE +2F1C;2F1C;2F1C;53C8;53C8; # (⼜; ⼜; ⼜; 又; 又; ) KANGXI RADICAL AGAIN +2F1D;2F1D;2F1D;53E3;53E3; # (⼝; ⼝; ⼝; 口; 口; ) KANGXI RADICAL MOUTH +2F1E;2F1E;2F1E;56D7;56D7; # (⼞; ⼞; ⼞; 囗; 囗; ) KANGXI RADICAL ENCLOSURE +2F1F;2F1F;2F1F;571F;571F; # (⼟; ⼟; ⼟; 土; 土; ) KANGXI RADICAL EARTH +2F20;2F20;2F20;58EB;58EB; # (⼠; ⼠; ⼠; 士; 士; ) KANGXI RADICAL SCHOLAR +2F21;2F21;2F21;5902;5902; # (⼡; ⼡; ⼡; 夂; 夂; ) KANGXI RADICAL GO +2F22;2F22;2F22;590A;590A; # (⼢; ⼢; ⼢; 夊; 夊; ) KANGXI RADICAL GO SLOWLY +2F23;2F23;2F23;5915;5915; # (⼣; ⼣; ⼣; 夕; 夕; ) KANGXI RADICAL EVENING +2F24;2F24;2F24;5927;5927; # (⼤; ⼤; ⼤; 大; 大; ) KANGXI RADICAL BIG +2F25;2F25;2F25;5973;5973; # (⼥; ⼥; ⼥; 女; 女; ) KANGXI RADICAL WOMAN +2F26;2F26;2F26;5B50;5B50; # (⼦; ⼦; ⼦; 子; 子; ) KANGXI RADICAL CHILD +2F27;2F27;2F27;5B80;5B80; # (⼧; ⼧; ⼧; 宀; 宀; ) KANGXI RADICAL ROOF +2F28;2F28;2F28;5BF8;5BF8; # (⼨; ⼨; ⼨; 寸; 寸; ) KANGXI RADICAL INCH +2F29;2F29;2F29;5C0F;5C0F; # (⼩; ⼩; ⼩; 小; 小; ) KANGXI RADICAL SMALL +2F2A;2F2A;2F2A;5C22;5C22; # (⼪; ⼪; ⼪; 尢; 尢; ) KANGXI RADICAL LAME +2F2B;2F2B;2F2B;5C38;5C38; # (⼫; ⼫; ⼫; 尸; 尸; ) KANGXI RADICAL CORPSE +2F2C;2F2C;2F2C;5C6E;5C6E; # (⼬; ⼬; ⼬; 屮; 屮; ) KANGXI RADICAL SPROUT +2F2D;2F2D;2F2D;5C71;5C71; # (⼭; ⼭; ⼭; 山; 山; ) KANGXI RADICAL MOUNTAIN +2F2E;2F2E;2F2E;5DDB;5DDB; # (⼮; ⼮; ⼮; 巛; 巛; ) KANGXI RADICAL RIVER +2F2F;2F2F;2F2F;5DE5;5DE5; # (⼯; ⼯; ⼯; 工; 工; ) KANGXI RADICAL WORK +2F30;2F30;2F30;5DF1;5DF1; # (⼰; ⼰; ⼰; 己; 己; ) KANGXI RADICAL ONESELF +2F31;2F31;2F31;5DFE;5DFE; # (⼱; ⼱; ⼱; 巾; 巾; ) KANGXI RADICAL TURBAN +2F32;2F32;2F32;5E72;5E72; # (⼲; ⼲; ⼲; 干; 干; ) KANGXI RADICAL DRY +2F33;2F33;2F33;5E7A;5E7A; # (⼳; ⼳; ⼳; 幺; 幺; ) KANGXI RADICAL SHORT THREAD +2F34;2F34;2F34;5E7F;5E7F; # (⼴; ⼴; ⼴; 广; 广; ) KANGXI RADICAL DOTTED CLIFF +2F35;2F35;2F35;5EF4;5EF4; # (⼵; ⼵; ⼵; 廴; 廴; ) KANGXI RADICAL LONG STRIDE +2F36;2F36;2F36;5EFE;5EFE; # (⼶; ⼶; ⼶; 廾; 廾; ) KANGXI RADICAL TWO HANDS +2F37;2F37;2F37;5F0B;5F0B; # (⼷; ⼷; ⼷; 弋; 弋; ) KANGXI RADICAL SHOOT +2F38;2F38;2F38;5F13;5F13; # (⼸; ⼸; ⼸; 弓; 弓; ) KANGXI RADICAL BOW +2F39;2F39;2F39;5F50;5F50; # (⼹; ⼹; ⼹; 彐; 彐; ) KANGXI RADICAL SNOUT +2F3A;2F3A;2F3A;5F61;5F61; # (⼺; ⼺; ⼺; 彡; 彡; ) KANGXI RADICAL BRISTLE +2F3B;2F3B;2F3B;5F73;5F73; # (⼻; ⼻; ⼻; 彳; 彳; ) KANGXI RADICAL STEP +2F3C;2F3C;2F3C;5FC3;5FC3; # (⼼; ⼼; ⼼; 心; 心; ) KANGXI RADICAL HEART +2F3D;2F3D;2F3D;6208;6208; # (⼽; ⼽; ⼽; 戈; 戈; ) KANGXI RADICAL HALBERD +2F3E;2F3E;2F3E;6236;6236; # (⼾; ⼾; ⼾; 戶; 戶; ) KANGXI RADICAL DOOR +2F3F;2F3F;2F3F;624B;624B; # (⼿; ⼿; ⼿; 手; 手; ) KANGXI RADICAL HAND +2F40;2F40;2F40;652F;652F; # (⽀; ⽀; ⽀; 支; 支; ) KANGXI RADICAL BRANCH +2F41;2F41;2F41;6534;6534; # (⽁; ⽁; ⽁; 攴; 攴; ) KANGXI RADICAL RAP +2F42;2F42;2F42;6587;6587; # (⽂; ⽂; ⽂; 文; 文; ) KANGXI RADICAL SCRIPT +2F43;2F43;2F43;6597;6597; # (⽃; ⽃; ⽃; 斗; 斗; ) KANGXI RADICAL DIPPER +2F44;2F44;2F44;65A4;65A4; # (⽄; ⽄; ⽄; 斤; 斤; ) KANGXI RADICAL AXE +2F45;2F45;2F45;65B9;65B9; # (⽅; ⽅; ⽅; 方; 方; ) KANGXI RADICAL SQUARE +2F46;2F46;2F46;65E0;65E0; # (⽆; ⽆; ⽆; 无; 无; ) KANGXI RADICAL NOT +2F47;2F47;2F47;65E5;65E5; # (⽇; ⽇; ⽇; 日; 日; ) KANGXI RADICAL SUN +2F48;2F48;2F48;66F0;66F0; # (⽈; ⽈; ⽈; 曰; 曰; ) KANGXI RADICAL SAY +2F49;2F49;2F49;6708;6708; # (⽉; ⽉; ⽉; 月; 月; ) KANGXI RADICAL MOON +2F4A;2F4A;2F4A;6728;6728; # (⽊; ⽊; ⽊; 木; 木; ) KANGXI RADICAL TREE +2F4B;2F4B;2F4B;6B20;6B20; # (⽋; ⽋; ⽋; 欠; 欠; ) KANGXI RADICAL LACK +2F4C;2F4C;2F4C;6B62;6B62; # (⽌; ⽌; ⽌; 止; 止; ) KANGXI RADICAL STOP +2F4D;2F4D;2F4D;6B79;6B79; # (⽍; ⽍; ⽍; 歹; 歹; ) KANGXI RADICAL DEATH +2F4E;2F4E;2F4E;6BB3;6BB3; # (⽎; ⽎; ⽎; 殳; 殳; ) KANGXI RADICAL WEAPON +2F4F;2F4F;2F4F;6BCB;6BCB; # (⽏; ⽏; ⽏; 毋; 毋; ) KANGXI RADICAL DO NOT +2F50;2F50;2F50;6BD4;6BD4; # (⽐; ⽐; ⽐; 比; 比; ) KANGXI RADICAL COMPARE +2F51;2F51;2F51;6BDB;6BDB; # (⽑; ⽑; ⽑; 毛; 毛; ) KANGXI RADICAL FUR +2F52;2F52;2F52;6C0F;6C0F; # (⽒; ⽒; ⽒; 氏; 氏; ) KANGXI RADICAL CLAN +2F53;2F53;2F53;6C14;6C14; # (⽓; ⽓; ⽓; 气; 气; ) KANGXI RADICAL STEAM +2F54;2F54;2F54;6C34;6C34; # (⽔; ⽔; ⽔; 水; 水; ) KANGXI RADICAL WATER +2F55;2F55;2F55;706B;706B; # (⽕; ⽕; ⽕; 火; 火; ) KANGXI RADICAL FIRE +2F56;2F56;2F56;722A;722A; # (⽖; ⽖; ⽖; 爪; 爪; ) KANGXI RADICAL CLAW +2F57;2F57;2F57;7236;7236; # (⽗; ⽗; ⽗; 父; 父; ) KANGXI RADICAL FATHER +2F58;2F58;2F58;723B;723B; # (⽘; ⽘; ⽘; 爻; 爻; ) KANGXI RADICAL DOUBLE X +2F59;2F59;2F59;723F;723F; # (⽙; ⽙; ⽙; 爿; 爿; ) KANGXI RADICAL HALF TREE TRUNK +2F5A;2F5A;2F5A;7247;7247; # (⽚; ⽚; ⽚; 片; 片; ) KANGXI RADICAL SLICE +2F5B;2F5B;2F5B;7259;7259; # (⽛; ⽛; ⽛; 牙; 牙; ) KANGXI RADICAL FANG +2F5C;2F5C;2F5C;725B;725B; # (⽜; ⽜; ⽜; 牛; 牛; ) KANGXI RADICAL COW +2F5D;2F5D;2F5D;72AC;72AC; # (⽝; ⽝; ⽝; 犬; 犬; ) KANGXI RADICAL DOG +2F5E;2F5E;2F5E;7384;7384; # (⽞; ⽞; ⽞; 玄; 玄; ) KANGXI RADICAL PROFOUND +2F5F;2F5F;2F5F;7389;7389; # (⽟; ⽟; ⽟; 玉; 玉; ) KANGXI RADICAL JADE +2F60;2F60;2F60;74DC;74DC; # (⽠; ⽠; ⽠; 瓜; 瓜; ) KANGXI RADICAL MELON +2F61;2F61;2F61;74E6;74E6; # (⽡; ⽡; ⽡; 瓦; 瓦; ) KANGXI RADICAL TILE +2F62;2F62;2F62;7518;7518; # (⽢; ⽢; ⽢; 甘; 甘; ) KANGXI RADICAL SWEET +2F63;2F63;2F63;751F;751F; # (⽣; ⽣; ⽣; 生; 生; ) KANGXI RADICAL LIFE +2F64;2F64;2F64;7528;7528; # (⽤; ⽤; ⽤; 用; 用; ) KANGXI RADICAL USE +2F65;2F65;2F65;7530;7530; # (⽥; ⽥; ⽥; 田; 田; ) KANGXI RADICAL FIELD +2F66;2F66;2F66;758B;758B; # (⽦; ⽦; ⽦; 疋; 疋; ) KANGXI RADICAL BOLT OF CLOTH +2F67;2F67;2F67;7592;7592; # (⽧; ⽧; ⽧; 疒; 疒; ) KANGXI RADICAL SICKNESS +2F68;2F68;2F68;7676;7676; # (⽨; ⽨; ⽨; 癶; 癶; ) KANGXI RADICAL DOTTED TENT +2F69;2F69;2F69;767D;767D; # (⽩; ⽩; ⽩; 白; 白; ) KANGXI RADICAL WHITE +2F6A;2F6A;2F6A;76AE;76AE; # (⽪; ⽪; ⽪; 皮; 皮; ) KANGXI RADICAL SKIN +2F6B;2F6B;2F6B;76BF;76BF; # (⽫; ⽫; ⽫; 皿; 皿; ) KANGXI RADICAL DISH +2F6C;2F6C;2F6C;76EE;76EE; # (⽬; ⽬; ⽬; 目; 目; ) KANGXI RADICAL EYE +2F6D;2F6D;2F6D;77DB;77DB; # (⽭; ⽭; ⽭; 矛; 矛; ) KANGXI RADICAL SPEAR +2F6E;2F6E;2F6E;77E2;77E2; # (⽮; ⽮; ⽮; 矢; 矢; ) KANGXI RADICAL ARROW +2F6F;2F6F;2F6F;77F3;77F3; # (⽯; ⽯; ⽯; 石; 石; ) KANGXI RADICAL STONE +2F70;2F70;2F70;793A;793A; # (⽰; ⽰; ⽰; 示; 示; ) KANGXI RADICAL SPIRIT +2F71;2F71;2F71;79B8;79B8; # (⽱; ⽱; ⽱; 禸; 禸; ) KANGXI RADICAL TRACK +2F72;2F72;2F72;79BE;79BE; # (⽲; ⽲; ⽲; 禾; 禾; ) KANGXI RADICAL GRAIN +2F73;2F73;2F73;7A74;7A74; # (⽳; ⽳; ⽳; 穴; 穴; ) KANGXI RADICAL CAVE +2F74;2F74;2F74;7ACB;7ACB; # (⽴; ⽴; ⽴; 立; 立; ) KANGXI RADICAL STAND +2F75;2F75;2F75;7AF9;7AF9; # (⽵; ⽵; ⽵; 竹; 竹; ) KANGXI RADICAL BAMBOO +2F76;2F76;2F76;7C73;7C73; # (⽶; ⽶; ⽶; 米; 米; ) KANGXI RADICAL RICE +2F77;2F77;2F77;7CF8;7CF8; # (⽷; ⽷; ⽷; 糸; 糸; ) KANGXI RADICAL SILK +2F78;2F78;2F78;7F36;7F36; # (⽸; ⽸; ⽸; 缶; 缶; ) KANGXI RADICAL JAR +2F79;2F79;2F79;7F51;7F51; # (⽹; ⽹; ⽹; 网; 网; ) KANGXI RADICAL NET +2F7A;2F7A;2F7A;7F8A;7F8A; # (⽺; ⽺; ⽺; 羊; 羊; ) KANGXI RADICAL SHEEP +2F7B;2F7B;2F7B;7FBD;7FBD; # (⽻; ⽻; ⽻; 羽; 羽; ) KANGXI RADICAL FEATHER +2F7C;2F7C;2F7C;8001;8001; # (⽼; ⽼; ⽼; 老; 老; ) KANGXI RADICAL OLD +2F7D;2F7D;2F7D;800C;800C; # (⽽; ⽽; ⽽; 而; 而; ) KANGXI RADICAL AND +2F7E;2F7E;2F7E;8012;8012; # (⽾; ⽾; ⽾; 耒; 耒; ) KANGXI RADICAL PLOW +2F7F;2F7F;2F7F;8033;8033; # (⽿; ⽿; ⽿; 耳; 耳; ) KANGXI RADICAL EAR +2F80;2F80;2F80;807F;807F; # (⾀; ⾀; ⾀; 聿; 聿; ) KANGXI RADICAL BRUSH +2F81;2F81;2F81;8089;8089; # (⾁; ⾁; ⾁; 肉; 肉; ) KANGXI RADICAL MEAT +2F82;2F82;2F82;81E3;81E3; # (⾂; ⾂; ⾂; 臣; 臣; ) KANGXI RADICAL MINISTER +2F83;2F83;2F83;81EA;81EA; # (⾃; ⾃; ⾃; 自; 自; ) KANGXI RADICAL SELF +2F84;2F84;2F84;81F3;81F3; # (⾄; ⾄; ⾄; 至; 至; ) KANGXI RADICAL ARRIVE +2F85;2F85;2F85;81FC;81FC; # (⾅; ⾅; ⾅; 臼; 臼; ) KANGXI RADICAL MORTAR +2F86;2F86;2F86;820C;820C; # (⾆; ⾆; ⾆; 舌; 舌; ) KANGXI RADICAL TONGUE +2F87;2F87;2F87;821B;821B; # (⾇; ⾇; ⾇; 舛; 舛; ) KANGXI RADICAL OPPOSE +2F88;2F88;2F88;821F;821F; # (⾈; ⾈; ⾈; 舟; 舟; ) KANGXI RADICAL BOAT +2F89;2F89;2F89;826E;826E; # (⾉; ⾉; ⾉; 艮; 艮; ) KANGXI RADICAL STOPPING +2F8A;2F8A;2F8A;8272;8272; # (⾊; ⾊; ⾊; 色; 色; ) KANGXI RADICAL COLOR +2F8B;2F8B;2F8B;8278;8278; # (⾋; ⾋; ⾋; 艸; 艸; ) KANGXI RADICAL GRASS +2F8C;2F8C;2F8C;864D;864D; # (⾌; ⾌; ⾌; 虍; 虍; ) KANGXI RADICAL TIGER +2F8D;2F8D;2F8D;866B;866B; # (⾍; ⾍; ⾍; 虫; 虫; ) KANGXI RADICAL INSECT +2F8E;2F8E;2F8E;8840;8840; # (⾎; ⾎; ⾎; 血; 血; ) KANGXI RADICAL BLOOD +2F8F;2F8F;2F8F;884C;884C; # (⾏; ⾏; ⾏; 行; 行; ) KANGXI RADICAL WALK ENCLOSURE +2F90;2F90;2F90;8863;8863; # (⾐; ⾐; ⾐; 衣; 衣; ) KANGXI RADICAL CLOTHES +2F91;2F91;2F91;897E;897E; # (⾑; ⾑; ⾑; 襾; 襾; ) KANGXI RADICAL WEST +2F92;2F92;2F92;898B;898B; # (⾒; ⾒; ⾒; 見; 見; ) KANGXI RADICAL SEE +2F93;2F93;2F93;89D2;89D2; # (⾓; ⾓; ⾓; 角; 角; ) KANGXI RADICAL HORN +2F94;2F94;2F94;8A00;8A00; # (⾔; ⾔; ⾔; 言; 言; ) KANGXI RADICAL SPEECH +2F95;2F95;2F95;8C37;8C37; # (⾕; ⾕; ⾕; 谷; 谷; ) KANGXI RADICAL VALLEY +2F96;2F96;2F96;8C46;8C46; # (⾖; ⾖; ⾖; 豆; 豆; ) KANGXI RADICAL BEAN +2F97;2F97;2F97;8C55;8C55; # (⾗; ⾗; ⾗; 豕; 豕; ) KANGXI RADICAL PIG +2F98;2F98;2F98;8C78;8C78; # (⾘; ⾘; ⾘; 豸; 豸; ) KANGXI RADICAL BADGER +2F99;2F99;2F99;8C9D;8C9D; # (⾙; ⾙; ⾙; 貝; 貝; ) KANGXI RADICAL SHELL +2F9A;2F9A;2F9A;8D64;8D64; # (⾚; ⾚; ⾚; 赤; 赤; ) KANGXI RADICAL RED +2F9B;2F9B;2F9B;8D70;8D70; # (⾛; ⾛; ⾛; 走; 走; ) KANGXI RADICAL RUN +2F9C;2F9C;2F9C;8DB3;8DB3; # (⾜; ⾜; ⾜; 足; 足; ) KANGXI RADICAL FOOT +2F9D;2F9D;2F9D;8EAB;8EAB; # (⾝; ⾝; ⾝; 身; 身; ) KANGXI RADICAL BODY +2F9E;2F9E;2F9E;8ECA;8ECA; # (⾞; ⾞; ⾞; 車; 車; ) KANGXI RADICAL CART +2F9F;2F9F;2F9F;8F9B;8F9B; # (⾟; ⾟; ⾟; 辛; 辛; ) KANGXI RADICAL BITTER +2FA0;2FA0;2FA0;8FB0;8FB0; # (⾠; ⾠; ⾠; 辰; 辰; ) KANGXI RADICAL MORNING +2FA1;2FA1;2FA1;8FB5;8FB5; # (⾡; ⾡; ⾡; 辵; 辵; ) KANGXI RADICAL WALK +2FA2;2FA2;2FA2;9091;9091; # (⾢; ⾢; ⾢; 邑; 邑; ) KANGXI RADICAL CITY +2FA3;2FA3;2FA3;9149;9149; # (⾣; ⾣; ⾣; 酉; 酉; ) KANGXI RADICAL WINE +2FA4;2FA4;2FA4;91C6;91C6; # (⾤; ⾤; ⾤; 釆; 釆; ) KANGXI RADICAL DISTINGUISH +2FA5;2FA5;2FA5;91CC;91CC; # (⾥; ⾥; ⾥; 里; 里; ) KANGXI RADICAL VILLAGE +2FA6;2FA6;2FA6;91D1;91D1; # (⾦; ⾦; ⾦; 金; 金; ) KANGXI RADICAL GOLD +2FA7;2FA7;2FA7;9577;9577; # (⾧; ⾧; ⾧; 長; 長; ) KANGXI RADICAL LONG +2FA8;2FA8;2FA8;9580;9580; # (⾨; ⾨; ⾨; 門; 門; ) KANGXI RADICAL GATE +2FA9;2FA9;2FA9;961C;961C; # (⾩; ⾩; ⾩; 阜; 阜; ) KANGXI RADICAL MOUND +2FAA;2FAA;2FAA;96B6;96B6; # (⾪; ⾪; ⾪; 隶; 隶; ) KANGXI RADICAL SLAVE +2FAB;2FAB;2FAB;96B9;96B9; # (⾫; ⾫; ⾫; 隹; 隹; ) KANGXI RADICAL SHORT TAILED BIRD +2FAC;2FAC;2FAC;96E8;96E8; # (⾬; ⾬; ⾬; 雨; 雨; ) KANGXI RADICAL RAIN +2FAD;2FAD;2FAD;9751;9751; # (⾭; ⾭; ⾭; 靑; 靑; ) KANGXI RADICAL BLUE +2FAE;2FAE;2FAE;975E;975E; # (⾮; ⾮; ⾮; 非; 非; ) KANGXI RADICAL WRONG +2FAF;2FAF;2FAF;9762;9762; # (⾯; ⾯; ⾯; 面; 面; ) KANGXI RADICAL FACE +2FB0;2FB0;2FB0;9769;9769; # (⾰; ⾰; ⾰; 革; 革; ) KANGXI RADICAL LEATHER +2FB1;2FB1;2FB1;97CB;97CB; # (⾱; ⾱; ⾱; 韋; 韋; ) KANGXI RADICAL TANNED LEATHER +2FB2;2FB2;2FB2;97ED;97ED; # (⾲; ⾲; ⾲; 韭; 韭; ) KANGXI RADICAL LEEK +2FB3;2FB3;2FB3;97F3;97F3; # (⾳; ⾳; ⾳; 音; 音; ) KANGXI RADICAL SOUND +2FB4;2FB4;2FB4;9801;9801; # (⾴; ⾴; ⾴; 頁; 頁; ) KANGXI RADICAL LEAF +2FB5;2FB5;2FB5;98A8;98A8; # (⾵; ⾵; ⾵; 風; 風; ) KANGXI RADICAL WIND +2FB6;2FB6;2FB6;98DB;98DB; # (⾶; ⾶; ⾶; 飛; 飛; ) KANGXI RADICAL FLY +2FB7;2FB7;2FB7;98DF;98DF; # (⾷; ⾷; ⾷; 食; 食; ) KANGXI RADICAL EAT +2FB8;2FB8;2FB8;9996;9996; # (⾸; ⾸; ⾸; 首; 首; ) KANGXI RADICAL HEAD +2FB9;2FB9;2FB9;9999;9999; # (⾹; ⾹; ⾹; 香; 香; ) KANGXI RADICAL FRAGRANT +2FBA;2FBA;2FBA;99AC;99AC; # (⾺; ⾺; ⾺; 馬; 馬; ) KANGXI RADICAL HORSE +2FBB;2FBB;2FBB;9AA8;9AA8; # (⾻; ⾻; ⾻; 骨; 骨; ) KANGXI RADICAL BONE +2FBC;2FBC;2FBC;9AD8;9AD8; # (⾼; ⾼; ⾼; 高; 高; ) KANGXI RADICAL TALL +2FBD;2FBD;2FBD;9ADF;9ADF; # (⾽; ⾽; ⾽; 髟; 髟; ) KANGXI RADICAL HAIR +2FBE;2FBE;2FBE;9B25;9B25; # (⾾; ⾾; ⾾; 鬥; 鬥; ) KANGXI RADICAL FIGHT +2FBF;2FBF;2FBF;9B2F;9B2F; # (⾿; ⾿; ⾿; 鬯; 鬯; ) KANGXI RADICAL SACRIFICIAL WINE +2FC0;2FC0;2FC0;9B32;9B32; # (⿀; ⿀; ⿀; 鬲; 鬲; ) KANGXI RADICAL CAULDRON +2FC1;2FC1;2FC1;9B3C;9B3C; # (⿁; ⿁; ⿁; 鬼; 鬼; ) KANGXI RADICAL GHOST +2FC2;2FC2;2FC2;9B5A;9B5A; # (⿂; ⿂; ⿂; 魚; 魚; ) KANGXI RADICAL FISH +2FC3;2FC3;2FC3;9CE5;9CE5; # (⿃; ⿃; ⿃; 鳥; 鳥; ) KANGXI RADICAL BIRD +2FC4;2FC4;2FC4;9E75;9E75; # (⿄; ⿄; ⿄; 鹵; 鹵; ) KANGXI RADICAL SALT +2FC5;2FC5;2FC5;9E7F;9E7F; # (⿅; ⿅; ⿅; 鹿; 鹿; ) KANGXI RADICAL DEER +2FC6;2FC6;2FC6;9EA5;9EA5; # (⿆; ⿆; ⿆; 麥; 麥; ) KANGXI RADICAL WHEAT +2FC7;2FC7;2FC7;9EBB;9EBB; # (⿇; ⿇; ⿇; 麻; 麻; ) KANGXI RADICAL HEMP +2FC8;2FC8;2FC8;9EC3;9EC3; # (⿈; ⿈; ⿈; 黃; 黃; ) KANGXI RADICAL YELLOW +2FC9;2FC9;2FC9;9ECD;9ECD; # (⿉; ⿉; ⿉; 黍; 黍; ) KANGXI RADICAL MILLET +2FCA;2FCA;2FCA;9ED1;9ED1; # (⿊; ⿊; ⿊; 黑; 黑; ) KANGXI RADICAL BLACK +2FCB;2FCB;2FCB;9EF9;9EF9; # (⿋; ⿋; ⿋; 黹; 黹; ) KANGXI RADICAL EMBROIDERY +2FCC;2FCC;2FCC;9EFD;9EFD; # (⿌; ⿌; ⿌; 黽; 黽; ) KANGXI RADICAL FROG +2FCD;2FCD;2FCD;9F0E;9F0E; # (⿍; ⿍; ⿍; 鼎; 鼎; ) KANGXI RADICAL TRIPOD +2FCE;2FCE;2FCE;9F13;9F13; # (⿎; ⿎; ⿎; 鼓; 鼓; ) KANGXI RADICAL DRUM +2FCF;2FCF;2FCF;9F20;9F20; # (⿏; ⿏; ⿏; 鼠; 鼠; ) KANGXI RADICAL RAT +2FD0;2FD0;2FD0;9F3B;9F3B; # (⿐; ⿐; ⿐; 鼻; 鼻; ) KANGXI RADICAL NOSE +2FD1;2FD1;2FD1;9F4A;9F4A; # (⿑; ⿑; ⿑; 齊; 齊; ) KANGXI RADICAL EVEN +2FD2;2FD2;2FD2;9F52;9F52; # (⿒; ⿒; ⿒; 齒; 齒; ) KANGXI RADICAL TOOTH +2FD3;2FD3;2FD3;9F8D;9F8D; # (⿓; ⿓; ⿓; 龍; 龍; ) KANGXI RADICAL DRAGON +2FD4;2FD4;2FD4;9F9C;9F9C; # (⿔; ⿔; ⿔; 龜; 龜; ) KANGXI RADICAL TURTLE +2FD5;2FD5;2FD5;9FA0;9FA0; # (⿕; ⿕; ⿕; 龠; 龠; ) KANGXI RADICAL FLUTE +3000;3000;3000;0020;0020; # ( ;  ;  ; ; ; ) IDEOGRAPHIC SPACE +3036;3036;3036;3012;3012; # (〶; 〶; 〶; 〒; 〒; ) CIRCLED POSTAL MARK +3038;3038;3038;5341;5341; # (〸; 〸; 〸; 十; 十; ) HANGZHOU NUMERAL TEN +3039;3039;3039;5344;5344; # (〹; 〹; 〹; 卄; 卄; ) HANGZHOU NUMERAL TWENTY +303A;303A;303A;5345;5345; # (〺; 〺; 〺; 卅; 卅; ) HANGZHOU NUMERAL THIRTY +304C;304C;304B 3099;304C;304B 3099; # (が; が; か◌゙; が; か◌゙; ) HIRAGANA LETTER GA +304E;304E;304D 3099;304E;304D 3099; # (ぎ; ぎ; き◌゙; ぎ; き◌゙; ) HIRAGANA LETTER GI +3050;3050;304F 3099;3050;304F 3099; # (ぐ; ぐ; く◌゙; ぐ; く◌゙; ) HIRAGANA LETTER GU +3052;3052;3051 3099;3052;3051 3099; # (げ; げ; け◌゙; げ; け◌゙; ) HIRAGANA LETTER GE +3054;3054;3053 3099;3054;3053 3099; # (ご; ご; こ◌゙; ご; こ◌゙; ) HIRAGANA LETTER GO +3056;3056;3055 3099;3056;3055 3099; # (ざ; ざ; さ◌゙; ざ; さ◌゙; ) HIRAGANA LETTER ZA +3058;3058;3057 3099;3058;3057 3099; # (じ; じ; し◌゙; じ; し◌゙; ) HIRAGANA LETTER ZI +305A;305A;3059 3099;305A;3059 3099; # (ず; ず; す◌゙; ず; す◌゙; ) HIRAGANA LETTER ZU +305C;305C;305B 3099;305C;305B 3099; # (ぜ; ぜ; せ◌゙; ぜ; せ◌゙; ) HIRAGANA LETTER ZE +305E;305E;305D 3099;305E;305D 3099; # (ぞ; ぞ; そ◌゙; ぞ; そ◌゙; ) HIRAGANA LETTER ZO +3060;3060;305F 3099;3060;305F 3099; # (だ; だ; た◌゙; だ; た◌゙; ) HIRAGANA LETTER DA +3062;3062;3061 3099;3062;3061 3099; # (ぢ; ぢ; ち◌゙; ぢ; ち◌゙; ) HIRAGANA LETTER DI +3065;3065;3064 3099;3065;3064 3099; # (づ; づ; つ◌゙; づ; つ◌゙; ) HIRAGANA LETTER DU +3067;3067;3066 3099;3067;3066 3099; # (で; で; て◌゙; で; て◌゙; ) HIRAGANA LETTER DE +3069;3069;3068 3099;3069;3068 3099; # (ど; ど; と◌゙; ど; と◌゙; ) HIRAGANA LETTER DO +3070;3070;306F 3099;3070;306F 3099; # (ば; ば; は◌゙; ば; は◌゙; ) HIRAGANA LETTER BA +3071;3071;306F 309A;3071;306F 309A; # (ぱ; ぱ; は◌゚; ぱ; は◌゚; ) HIRAGANA LETTER PA +3073;3073;3072 3099;3073;3072 3099; # (び; び; ひ◌゙; び; ひ◌゙; ) HIRAGANA LETTER BI +3074;3074;3072 309A;3074;3072 309A; # (ぴ; ぴ; ひ◌゚; ぴ; ひ◌゚; ) HIRAGANA LETTER PI +3076;3076;3075 3099;3076;3075 3099; # (ぶ; ぶ; ふ◌゙; ぶ; ふ◌゙; ) HIRAGANA LETTER BU +3077;3077;3075 309A;3077;3075 309A; # (ぷ; ぷ; ふ◌゚; ぷ; ふ◌゚; ) HIRAGANA LETTER PU +3079;3079;3078 3099;3079;3078 3099; # (べ; べ; へ◌゙; べ; へ◌゙; ) HIRAGANA LETTER BE +307A;307A;3078 309A;307A;3078 309A; # (ぺ; ぺ; へ◌゚; ぺ; へ◌゚; ) HIRAGANA LETTER PE +307C;307C;307B 3099;307C;307B 3099; # (ぼ; ぼ; ほ◌゙; ぼ; ほ◌゙; ) HIRAGANA LETTER BO +307D;307D;307B 309A;307D;307B 309A; # (ぽ; ぽ; ほ◌゚; ぽ; ほ◌゚; ) HIRAGANA LETTER PO +3094;3094;3046 3099;3094;3046 3099; # (ゔ; ゔ; う◌゙; ゔ; う◌゙; ) HIRAGANA LETTER VU +309B;309B;309B;0020 3099;0020 3099; # (゛; ゛; ゛; ◌゙; ◌゙; ) KATAKANA-HIRAGANA VOICED SOUND MARK +309C;309C;309C;0020 309A;0020 309A; # (゜; ゜; ゜; ◌゚; ◌゚; ) KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK +309E;309E;309D 3099;309E;309D 3099; # (ゞ; ゞ; ゝ◌゙; ゞ; ゝ◌゙; ) HIRAGANA VOICED ITERATION MARK +309F;309F;309F;3088 308A;3088 308A; # (ゟ; ゟ; ゟ; より; より; ) HIRAGANA DIGRAPH YORI +30AC;30AC;30AB 3099;30AC;30AB 3099; # (ガ; ガ; カ◌゙; ガ; カ◌゙; ) KATAKANA LETTER GA +30AE;30AE;30AD 3099;30AE;30AD 3099; # (ギ; ギ; キ◌゙; ギ; キ◌゙; ) KATAKANA LETTER GI +30B0;30B0;30AF 3099;30B0;30AF 3099; # (グ; グ; ク◌゙; グ; ク◌゙; ) KATAKANA LETTER GU +30B2;30B2;30B1 3099;30B2;30B1 3099; # (ゲ; ゲ; ケ◌゙; ゲ; ケ◌゙; ) KATAKANA LETTER GE +30B4;30B4;30B3 3099;30B4;30B3 3099; # (ゴ; ゴ; コ◌゙; ゴ; コ◌゙; ) KATAKANA LETTER GO +30B6;30B6;30B5 3099;30B6;30B5 3099; # (ザ; ザ; サ◌゙; ザ; サ◌゙; ) KATAKANA LETTER ZA +30B8;30B8;30B7 3099;30B8;30B7 3099; # (ジ; ジ; シ◌゙; ジ; シ◌゙; ) KATAKANA LETTER ZI +30BA;30BA;30B9 3099;30BA;30B9 3099; # (ズ; ズ; ス◌゙; ズ; ス◌゙; ) KATAKANA LETTER ZU +30BC;30BC;30BB 3099;30BC;30BB 3099; # (ゼ; ゼ; セ◌゙; ゼ; セ◌゙; ) KATAKANA LETTER ZE +30BE;30BE;30BD 3099;30BE;30BD 3099; # (ゾ; ゾ; ソ◌゙; ゾ; ソ◌゙; ) KATAKANA LETTER ZO +30C0;30C0;30BF 3099;30C0;30BF 3099; # (ダ; ダ; タ◌゙; ダ; タ◌゙; ) KATAKANA LETTER DA +30C2;30C2;30C1 3099;30C2;30C1 3099; # (ヂ; ヂ; チ◌゙; ヂ; チ◌゙; ) KATAKANA LETTER DI +30C5;30C5;30C4 3099;30C5;30C4 3099; # (ヅ; ヅ; ツ◌゙; ヅ; ツ◌゙; ) KATAKANA LETTER DU +30C7;30C7;30C6 3099;30C7;30C6 3099; # (デ; デ; テ◌゙; デ; テ◌゙; ) KATAKANA LETTER DE +30C9;30C9;30C8 3099;30C9;30C8 3099; # (ド; ド; ト◌゙; ド; ト◌゙; ) KATAKANA LETTER DO +30D0;30D0;30CF 3099;30D0;30CF 3099; # (バ; バ; ハ◌゙; バ; ハ◌゙; ) KATAKANA LETTER BA +30D1;30D1;30CF 309A;30D1;30CF 309A; # (パ; パ; ハ◌゚; パ; ハ◌゚; ) KATAKANA LETTER PA +30D3;30D3;30D2 3099;30D3;30D2 3099; # (ビ; ビ; ヒ◌゙; ビ; ヒ◌゙; ) KATAKANA LETTER BI +30D4;30D4;30D2 309A;30D4;30D2 309A; # (ピ; ピ; ヒ◌゚; ピ; ヒ◌゚; ) KATAKANA LETTER PI +30D6;30D6;30D5 3099;30D6;30D5 3099; # (ブ; ブ; フ◌゙; ブ; フ◌゙; ) KATAKANA LETTER BU +30D7;30D7;30D5 309A;30D7;30D5 309A; # (プ; プ; フ◌゚; プ; フ◌゚; ) KATAKANA LETTER PU +30D9;30D9;30D8 3099;30D9;30D8 3099; # (ベ; ベ; ヘ◌゙; ベ; ヘ◌゙; ) KATAKANA LETTER BE +30DA;30DA;30D8 309A;30DA;30D8 309A; # (ペ; ペ; ヘ◌゚; ペ; ヘ◌゚; ) KATAKANA LETTER PE +30DC;30DC;30DB 3099;30DC;30DB 3099; # (ボ; ボ; ホ◌゙; ボ; ホ◌゙; ) KATAKANA LETTER BO +30DD;30DD;30DB 309A;30DD;30DB 309A; # (ポ; ポ; ホ◌゚; ポ; ホ◌゚; ) KATAKANA LETTER PO +30F4;30F4;30A6 3099;30F4;30A6 3099; # (ヴ; ヴ; ウ◌゙; ヴ; ウ◌゙; ) KATAKANA LETTER VU +30F7;30F7;30EF 3099;30F7;30EF 3099; # (ヷ; ヷ; ワ◌゙; ヷ; ワ◌゙; ) KATAKANA LETTER VA +30F8;30F8;30F0 3099;30F8;30F0 3099; # (ヸ; ヸ; ヰ◌゙; ヸ; ヰ◌゙; ) KATAKANA LETTER VI +30F9;30F9;30F1 3099;30F9;30F1 3099; # (ヹ; ヹ; ヱ◌゙; ヹ; ヱ◌゙; ) KATAKANA LETTER VE +30FA;30FA;30F2 3099;30FA;30F2 3099; # (ヺ; ヺ; ヲ◌゙; ヺ; ヲ◌゙; ) KATAKANA LETTER VO +30FE;30FE;30FD 3099;30FE;30FD 3099; # (ヾ; ヾ; ヽ◌゙; ヾ; ヽ◌゙; ) KATAKANA VOICED ITERATION MARK +30FF;30FF;30FF;30B3 30C8;30B3 30C8; # (ヿ; ヿ; ヿ; コト; コト; ) KATAKANA DIGRAPH KOTO +3131;3131;3131;1100;1100; # (ㄱ; ㄱ; ㄱ; ᄀ; ᄀ; ) HANGUL LETTER KIYEOK +3132;3132;3132;1101;1101; # (ㄲ; ㄲ; ㄲ; ᄁ; ᄁ; ) HANGUL LETTER SSANGKIYEOK +3133;3133;3133;11AA;11AA; # (ㄳ; ㄳ; ㄳ; ᆪ; ᆪ; ) HANGUL LETTER KIYEOK-SIOS +3134;3134;3134;1102;1102; # (ㄴ; ㄴ; ㄴ; ᄂ; ᄂ; ) HANGUL LETTER NIEUN +3135;3135;3135;11AC;11AC; # (ㄵ; ㄵ; ㄵ; ᆬ; ᆬ; ) HANGUL LETTER NIEUN-CIEUC +3136;3136;3136;11AD;11AD; # (ㄶ; ㄶ; ㄶ; ᆭ; ᆭ; ) HANGUL LETTER NIEUN-HIEUH +3137;3137;3137;1103;1103; # (ㄷ; ㄷ; ㄷ; ᄃ; ᄃ; ) HANGUL LETTER TIKEUT +3138;3138;3138;1104;1104; # (ㄸ; ㄸ; ㄸ; ᄄ; ᄄ; ) HANGUL LETTER SSANGTIKEUT +3139;3139;3139;1105;1105; # (ㄹ; ㄹ; ㄹ; ᄅ; ᄅ; ) HANGUL LETTER RIEUL +313A;313A;313A;11B0;11B0; # (ㄺ; ㄺ; ㄺ; ᆰ; ᆰ; ) HANGUL LETTER RIEUL-KIYEOK +313B;313B;313B;11B1;11B1; # (ㄻ; ㄻ; ㄻ; ᆱ; ᆱ; ) HANGUL LETTER RIEUL-MIEUM +313C;313C;313C;11B2;11B2; # (ㄼ; ㄼ; ㄼ; ᆲ; ᆲ; ) HANGUL LETTER RIEUL-PIEUP +313D;313D;313D;11B3;11B3; # (ㄽ; ㄽ; ㄽ; ᆳ; ᆳ; ) HANGUL LETTER RIEUL-SIOS +313E;313E;313E;11B4;11B4; # (ㄾ; ㄾ; ㄾ; ᆴ; ᆴ; ) HANGUL LETTER RIEUL-THIEUTH +313F;313F;313F;11B5;11B5; # (ㄿ; ㄿ; ㄿ; ᆵ; ᆵ; ) HANGUL LETTER RIEUL-PHIEUPH +3140;3140;3140;111A;111A; # (ㅀ; ㅀ; ㅀ; ᄚ; ᄚ; ) HANGUL LETTER RIEUL-HIEUH +3141;3141;3141;1106;1106; # (ㅁ; ㅁ; ㅁ; ᄆ; ᄆ; ) HANGUL LETTER MIEUM +3142;3142;3142;1107;1107; # (ㅂ; ㅂ; ㅂ; ᄇ; ᄇ; ) HANGUL LETTER PIEUP +3143;3143;3143;1108;1108; # (ㅃ; ㅃ; ㅃ; ᄈ; ᄈ; ) HANGUL LETTER SSANGPIEUP +3144;3144;3144;1121;1121; # (ㅄ; ㅄ; ㅄ; ᄡ; ᄡ; ) HANGUL LETTER PIEUP-SIOS +3145;3145;3145;1109;1109; # (ㅅ; ㅅ; ㅅ; ᄉ; ᄉ; ) HANGUL LETTER SIOS +3146;3146;3146;110A;110A; # (ㅆ; ㅆ; ㅆ; ᄊ; ᄊ; ) HANGUL LETTER SSANGSIOS +3147;3147;3147;110B;110B; # (ㅇ; ㅇ; ㅇ; ᄋ; ᄋ; ) HANGUL LETTER IEUNG +3148;3148;3148;110C;110C; # (ㅈ; ㅈ; ㅈ; ᄌ; ᄌ; ) HANGUL LETTER CIEUC +3149;3149;3149;110D;110D; # (ㅉ; ㅉ; ㅉ; ᄍ; ᄍ; ) HANGUL LETTER SSANGCIEUC +314A;314A;314A;110E;110E; # (ㅊ; ㅊ; ㅊ; ᄎ; ᄎ; ) HANGUL LETTER CHIEUCH +314B;314B;314B;110F;110F; # (ㅋ; ㅋ; ㅋ; ᄏ; ᄏ; ) HANGUL LETTER KHIEUKH +314C;314C;314C;1110;1110; # (ㅌ; ㅌ; ㅌ; ᄐ; ᄐ; ) HANGUL LETTER THIEUTH +314D;314D;314D;1111;1111; # (ㅍ; ㅍ; ㅍ; ᄑ; ᄑ; ) HANGUL LETTER PHIEUPH +314E;314E;314E;1112;1112; # (ㅎ; ㅎ; ㅎ; ᄒ; ᄒ; ) HANGUL LETTER HIEUH +314F;314F;314F;1161;1161; # (ㅏ; ㅏ; ㅏ; ᅡ; ᅡ; ) HANGUL LETTER A +3150;3150;3150;1162;1162; # (ㅐ; ㅐ; ㅐ; ᅢ; ᅢ; ) HANGUL LETTER AE +3151;3151;3151;1163;1163; # (ㅑ; ㅑ; ㅑ; ᅣ; ᅣ; ) HANGUL LETTER YA +3152;3152;3152;1164;1164; # (ㅒ; ㅒ; ㅒ; ᅤ; ᅤ; ) HANGUL LETTER YAE +3153;3153;3153;1165;1165; # (ㅓ; ㅓ; ㅓ; ᅥ; ᅥ; ) HANGUL LETTER EO +3154;3154;3154;1166;1166; # (ㅔ; ㅔ; ㅔ; ᅦ; ᅦ; ) HANGUL LETTER E +3155;3155;3155;1167;1167; # (ㅕ; ㅕ; ㅕ; ᅧ; ᅧ; ) HANGUL LETTER YEO +3156;3156;3156;1168;1168; # (ㅖ; ㅖ; ㅖ; ᅨ; ᅨ; ) HANGUL LETTER YE +3157;3157;3157;1169;1169; # (ㅗ; ㅗ; ㅗ; ᅩ; ᅩ; ) HANGUL LETTER O +3158;3158;3158;116A;116A; # (ㅘ; ㅘ; ㅘ; ᅪ; ᅪ; ) HANGUL LETTER WA +3159;3159;3159;116B;116B; # (ㅙ; ㅙ; ㅙ; ᅫ; ᅫ; ) HANGUL LETTER WAE +315A;315A;315A;116C;116C; # (ㅚ; ㅚ; ㅚ; ᅬ; ᅬ; ) HANGUL LETTER OE +315B;315B;315B;116D;116D; # (ㅛ; ㅛ; ㅛ; ᅭ; ᅭ; ) HANGUL LETTER YO +315C;315C;315C;116E;116E; # (ㅜ; ㅜ; ㅜ; ᅮ; ᅮ; ) HANGUL LETTER U +315D;315D;315D;116F;116F; # (ㅝ; ㅝ; ㅝ; ᅯ; ᅯ; ) HANGUL LETTER WEO +315E;315E;315E;1170;1170; # (ㅞ; ㅞ; ㅞ; ᅰ; ᅰ; ) HANGUL LETTER WE +315F;315F;315F;1171;1171; # (ㅟ; ㅟ; ㅟ; ᅱ; ᅱ; ) HANGUL LETTER WI +3160;3160;3160;1172;1172; # (ㅠ; ㅠ; ㅠ; ᅲ; ᅲ; ) HANGUL LETTER YU +3161;3161;3161;1173;1173; # (ㅡ; ㅡ; ㅡ; ᅳ; ᅳ; ) HANGUL LETTER EU +3162;3162;3162;1174;1174; # (ㅢ; ㅢ; ㅢ; ᅴ; ᅴ; ) HANGUL LETTER YI +3163;3163;3163;1175;1175; # (ㅣ; ㅣ; ㅣ; ᅵ; ᅵ; ) HANGUL LETTER I +3164;3164;3164;1160;1160; # (ㅤ; ㅤ; ㅤ; ᅠ; ᅠ; ) HANGUL FILLER +3165;3165;3165;1114;1114; # (ㅥ; ㅥ; ㅥ; ᄔ; ᄔ; ) HANGUL LETTER SSANGNIEUN +3166;3166;3166;1115;1115; # (ㅦ; ㅦ; ㅦ; ᄕ; ᄕ; ) HANGUL LETTER NIEUN-TIKEUT +3167;3167;3167;11C7;11C7; # (ㅧ; ㅧ; ㅧ; ᇇ; ᇇ; ) HANGUL LETTER NIEUN-SIOS +3168;3168;3168;11C8;11C8; # (ㅨ; ㅨ; ㅨ; ᇈ; ᇈ; ) HANGUL LETTER NIEUN-PANSIOS +3169;3169;3169;11CC;11CC; # (ㅩ; ㅩ; ㅩ; ᇌ; ᇌ; ) HANGUL LETTER RIEUL-KIYEOK-SIOS +316A;316A;316A;11CE;11CE; # (ㅪ; ㅪ; ㅪ; ᇎ; ᇎ; ) HANGUL LETTER RIEUL-TIKEUT +316B;316B;316B;11D3;11D3; # (ㅫ; ㅫ; ㅫ; ᇓ; ᇓ; ) HANGUL LETTER RIEUL-PIEUP-SIOS +316C;316C;316C;11D7;11D7; # (ㅬ; ㅬ; ㅬ; ᇗ; ᇗ; ) HANGUL LETTER RIEUL-PANSIOS +316D;316D;316D;11D9;11D9; # (ㅭ; ㅭ; ㅭ; ᇙ; ᇙ; ) HANGUL LETTER RIEUL-YEORINHIEUH +316E;316E;316E;111C;111C; # (ㅮ; ㅮ; ㅮ; ᄜ; ᄜ; ) HANGUL LETTER MIEUM-PIEUP +316F;316F;316F;11DD;11DD; # (ㅯ; ㅯ; ㅯ; ᇝ; ᇝ; ) HANGUL LETTER MIEUM-SIOS +3170;3170;3170;11DF;11DF; # (ㅰ; ㅰ; ㅰ; ᇟ; ᇟ; ) HANGUL LETTER MIEUM-PANSIOS +3171;3171;3171;111D;111D; # (ㅱ; ㅱ; ㅱ; ᄝ; ᄝ; ) HANGUL LETTER KAPYEOUNMIEUM +3172;3172;3172;111E;111E; # (ㅲ; ㅲ; ㅲ; ᄞ; ᄞ; ) HANGUL LETTER PIEUP-KIYEOK +3173;3173;3173;1120;1120; # (ㅳ; ㅳ; ㅳ; ᄠ; ᄠ; ) HANGUL LETTER PIEUP-TIKEUT +3174;3174;3174;1122;1122; # (ㅴ; ㅴ; ㅴ; ᄢ; ᄢ; ) HANGUL LETTER PIEUP-SIOS-KIYEOK +3175;3175;3175;1123;1123; # (ㅵ; ㅵ; ㅵ; ᄣ; ᄣ; ) HANGUL LETTER PIEUP-SIOS-TIKEUT +3176;3176;3176;1127;1127; # (ㅶ; ㅶ; ㅶ; ᄧ; ᄧ; ) HANGUL LETTER PIEUP-CIEUC +3177;3177;3177;1129;1129; # (ㅷ; ㅷ; ㅷ; ᄩ; ᄩ; ) HANGUL LETTER PIEUP-THIEUTH +3178;3178;3178;112B;112B; # (ㅸ; ㅸ; ㅸ; ᄫ; ᄫ; ) HANGUL LETTER KAPYEOUNPIEUP +3179;3179;3179;112C;112C; # (ㅹ; ㅹ; ㅹ; ᄬ; ᄬ; ) HANGUL LETTER KAPYEOUNSSANGPIEUP +317A;317A;317A;112D;112D; # (ㅺ; ㅺ; ㅺ; ᄭ; ᄭ; ) HANGUL LETTER SIOS-KIYEOK +317B;317B;317B;112E;112E; # (ㅻ; ㅻ; ㅻ; ᄮ; ᄮ; ) HANGUL LETTER SIOS-NIEUN +317C;317C;317C;112F;112F; # (ㅼ; ㅼ; ㅼ; ᄯ; ᄯ; ) HANGUL LETTER SIOS-TIKEUT +317D;317D;317D;1132;1132; # (ㅽ; ㅽ; ㅽ; ᄲ; ᄲ; ) HANGUL LETTER SIOS-PIEUP +317E;317E;317E;1136;1136; # (ㅾ; ㅾ; ㅾ; ᄶ; ᄶ; ) HANGUL LETTER SIOS-CIEUC +317F;317F;317F;1140;1140; # (ㅿ; ㅿ; ㅿ; ᅀ; ᅀ; ) HANGUL LETTER PANSIOS +3180;3180;3180;1147;1147; # (ㆀ; ㆀ; ㆀ; ᅇ; ᅇ; ) HANGUL LETTER SSANGIEUNG +3181;3181;3181;114C;114C; # (ㆁ; ㆁ; ㆁ; ᅌ; ᅌ; ) HANGUL LETTER YESIEUNG +3182;3182;3182;11F1;11F1; # (ㆂ; ㆂ; ㆂ; ᇱ; ᇱ; ) HANGUL LETTER YESIEUNG-SIOS +3183;3183;3183;11F2;11F2; # (ㆃ; ㆃ; ㆃ; ᇲ; ᇲ; ) HANGUL LETTER YESIEUNG-PANSIOS +3184;3184;3184;1157;1157; # (ㆄ; ㆄ; ㆄ; ᅗ; ᅗ; ) HANGUL LETTER KAPYEOUNPHIEUPH +3185;3185;3185;1158;1158; # (ㆅ; ㆅ; ㆅ; ᅘ; ᅘ; ) HANGUL LETTER SSANGHIEUH +3186;3186;3186;1159;1159; # (ㆆ; ㆆ; ㆆ; ᅙ; ᅙ; ) HANGUL LETTER YEORINHIEUH +3187;3187;3187;1184;1184; # (ㆇ; ㆇ; ㆇ; ᆄ; ᆄ; ) HANGUL LETTER YO-YA +3188;3188;3188;1185;1185; # (ㆈ; ㆈ; ㆈ; ᆅ; ᆅ; ) HANGUL LETTER YO-YAE +3189;3189;3189;1188;1188; # (ㆉ; ㆉ; ㆉ; ᆈ; ᆈ; ) HANGUL LETTER YO-I +318A;318A;318A;1191;1191; # (ㆊ; ㆊ; ㆊ; ᆑ; ᆑ; ) HANGUL LETTER YU-YEO +318B;318B;318B;1192;1192; # (ㆋ; ㆋ; ㆋ; ᆒ; ᆒ; ) HANGUL LETTER YU-YE +318C;318C;318C;1194;1194; # (ㆌ; ㆌ; ㆌ; ᆔ; ᆔ; ) HANGUL LETTER YU-I +318D;318D;318D;119E;119E; # (ㆍ; ㆍ; ㆍ; ᆞ; ᆞ; ) HANGUL LETTER ARAEA +318E;318E;318E;11A1;11A1; # (ㆎ; ㆎ; ㆎ; ᆡ; ᆡ; ) HANGUL LETTER ARAEAE +3192;3192;3192;4E00;4E00; # (㆒; ㆒; ㆒; 一; 一; ) IDEOGRAPHIC ANNOTATION ONE MARK +3193;3193;3193;4E8C;4E8C; # (㆓; ㆓; ㆓; 二; 二; ) IDEOGRAPHIC ANNOTATION TWO MARK +3194;3194;3194;4E09;4E09; # (㆔; ㆔; ㆔; 三; 三; ) IDEOGRAPHIC ANNOTATION THREE MARK +3195;3195;3195;56DB;56DB; # (㆕; ㆕; ㆕; 四; 四; ) IDEOGRAPHIC ANNOTATION FOUR MARK +3196;3196;3196;4E0A;4E0A; # (㆖; ㆖; ㆖; 上; 上; ) IDEOGRAPHIC ANNOTATION TOP MARK +3197;3197;3197;4E2D;4E2D; # (㆗; ㆗; ㆗; 中; 中; ) IDEOGRAPHIC ANNOTATION MIDDLE MARK +3198;3198;3198;4E0B;4E0B; # (㆘; ㆘; ㆘; 下; 下; ) IDEOGRAPHIC ANNOTATION BOTTOM MARK +3199;3199;3199;7532;7532; # (㆙; ㆙; ㆙; 甲; 甲; ) IDEOGRAPHIC ANNOTATION FIRST MARK +319A;319A;319A;4E59;4E59; # (㆚; ㆚; ㆚; 乙; 乙; ) IDEOGRAPHIC ANNOTATION SECOND MARK +319B;319B;319B;4E19;4E19; # (㆛; ㆛; ㆛; 丙; 丙; ) IDEOGRAPHIC ANNOTATION THIRD MARK +319C;319C;319C;4E01;4E01; # (㆜; ㆜; ㆜; 丁; 丁; ) IDEOGRAPHIC ANNOTATION FOURTH MARK +319D;319D;319D;5929;5929; # (㆝; ㆝; ㆝; 天; 天; ) IDEOGRAPHIC ANNOTATION HEAVEN MARK +319E;319E;319E;5730;5730; # (㆞; ㆞; ㆞; 地; 地; ) IDEOGRAPHIC ANNOTATION EARTH MARK +319F;319F;319F;4EBA;4EBA; # (㆟; ㆟; ㆟; 人; 人; ) IDEOGRAPHIC ANNOTATION MAN MARK +3200;3200;3200;0028 1100 0029;0028 1100 0029; # (㈀; ㈀; ㈀; (ᄀ); (ᄀ); ) PARENTHESIZED HANGUL KIYEOK +3201;3201;3201;0028 1102 0029;0028 1102 0029; # (㈁; ㈁; ㈁; (ᄂ); (ᄂ); ) PARENTHESIZED HANGUL NIEUN +3202;3202;3202;0028 1103 0029;0028 1103 0029; # (㈂; ㈂; ㈂; (ᄃ); (ᄃ); ) PARENTHESIZED HANGUL TIKEUT +3203;3203;3203;0028 1105 0029;0028 1105 0029; # (㈃; ㈃; ㈃; (ᄅ); (ᄅ); ) PARENTHESIZED HANGUL RIEUL +3204;3204;3204;0028 1106 0029;0028 1106 0029; # (㈄; ㈄; ㈄; (ᄆ); (ᄆ); ) PARENTHESIZED HANGUL MIEUM +3205;3205;3205;0028 1107 0029;0028 1107 0029; # (㈅; ㈅; ㈅; (ᄇ); (ᄇ); ) PARENTHESIZED HANGUL PIEUP +3206;3206;3206;0028 1109 0029;0028 1109 0029; # (㈆; ㈆; ㈆; (ᄉ); (ᄉ); ) PARENTHESIZED HANGUL SIOS +3207;3207;3207;0028 110B 0029;0028 110B 0029; # (㈇; ㈇; ㈇; (ᄋ); (ᄋ); ) PARENTHESIZED HANGUL IEUNG +3208;3208;3208;0028 110C 0029;0028 110C 0029; # (㈈; ㈈; ㈈; (ᄌ); (ᄌ); ) PARENTHESIZED HANGUL CIEUC +3209;3209;3209;0028 110E 0029;0028 110E 0029; # (㈉; ㈉; ㈉; (ᄎ); (ᄎ); ) PARENTHESIZED HANGUL CHIEUCH +320A;320A;320A;0028 110F 0029;0028 110F 0029; # (㈊; ㈊; ㈊; (ᄏ); (ᄏ); ) PARENTHESIZED HANGUL KHIEUKH +320B;320B;320B;0028 1110 0029;0028 1110 0029; # (㈋; ㈋; ㈋; (ᄐ); (ᄐ); ) PARENTHESIZED HANGUL THIEUTH +320C;320C;320C;0028 1111 0029;0028 1111 0029; # (㈌; ㈌; ㈌; (ᄑ); (ᄑ); ) PARENTHESIZED HANGUL PHIEUPH +320D;320D;320D;0028 1112 0029;0028 1112 0029; # (㈍; ㈍; ㈍; (ᄒ); (ᄒ); ) PARENTHESIZED HANGUL HIEUH +320E;320E;320E;0028 AC00 0029;0028 1100 1161 0029; # (㈎; ㈎; ㈎; (가); (가); ) PARENTHESIZED HANGUL KIYEOK A +320F;320F;320F;0028 B098 0029;0028 1102 1161 0029; # (㈏; ㈏; ㈏; (나); (나); ) PARENTHESIZED HANGUL NIEUN A +3210;3210;3210;0028 B2E4 0029;0028 1103 1161 0029; # (㈐; ㈐; ㈐; (다); (다); ) PARENTHESIZED HANGUL TIKEUT A +3211;3211;3211;0028 B77C 0029;0028 1105 1161 0029; # (㈑; ㈑; ㈑; (라); (라); ) PARENTHESIZED HANGUL RIEUL A +3212;3212;3212;0028 B9C8 0029;0028 1106 1161 0029; # (㈒; ㈒; ㈒; (마); (마); ) PARENTHESIZED HANGUL MIEUM A +3213;3213;3213;0028 BC14 0029;0028 1107 1161 0029; # (㈓; ㈓; ㈓; (바); (바); ) PARENTHESIZED HANGUL PIEUP A +3214;3214;3214;0028 C0AC 0029;0028 1109 1161 0029; # (㈔; ㈔; ㈔; (사); (사); ) PARENTHESIZED HANGUL SIOS A +3215;3215;3215;0028 C544 0029;0028 110B 1161 0029; # (㈕; ㈕; ㈕; (아); (아); ) PARENTHESIZED HANGUL IEUNG A +3216;3216;3216;0028 C790 0029;0028 110C 1161 0029; # (㈖; ㈖; ㈖; (자); (자); ) PARENTHESIZED HANGUL CIEUC A +3217;3217;3217;0028 CC28 0029;0028 110E 1161 0029; # (㈗; ㈗; ㈗; (차); (차); ) PARENTHESIZED HANGUL CHIEUCH A +3218;3218;3218;0028 CE74 0029;0028 110F 1161 0029; # (㈘; ㈘; ㈘; (카); (카); ) PARENTHESIZED HANGUL KHIEUKH A +3219;3219;3219;0028 D0C0 0029;0028 1110 1161 0029; # (㈙; ㈙; ㈙; (타); (타); ) PARENTHESIZED HANGUL THIEUTH A +321A;321A;321A;0028 D30C 0029;0028 1111 1161 0029; # (㈚; ㈚; ㈚; (파); (파); ) PARENTHESIZED HANGUL PHIEUPH A +321B;321B;321B;0028 D558 0029;0028 1112 1161 0029; # (㈛; ㈛; ㈛; (하); (하); ) PARENTHESIZED HANGUL HIEUH A +321C;321C;321C;0028 C8FC 0029;0028 110C 116E 0029; # (㈜; ㈜; ㈜; (주); (주); ) PARENTHESIZED HANGUL CIEUC U +3220;3220;3220;0028 4E00 0029;0028 4E00 0029; # (㈠; ㈠; ㈠; (一); (一); ) PARENTHESIZED IDEOGRAPH ONE +3221;3221;3221;0028 4E8C 0029;0028 4E8C 0029; # (㈡; ㈡; ㈡; (二); (二); ) PARENTHESIZED IDEOGRAPH TWO +3222;3222;3222;0028 4E09 0029;0028 4E09 0029; # (㈢; ㈢; ㈢; (三); (三); ) PARENTHESIZED IDEOGRAPH THREE +3223;3223;3223;0028 56DB 0029;0028 56DB 0029; # (㈣; ㈣; ㈣; (四); (四); ) PARENTHESIZED IDEOGRAPH FOUR +3224;3224;3224;0028 4E94 0029;0028 4E94 0029; # (㈤; ㈤; ㈤; (五); (五); ) PARENTHESIZED IDEOGRAPH FIVE +3225;3225;3225;0028 516D 0029;0028 516D 0029; # (㈥; ㈥; ㈥; (六); (六); ) PARENTHESIZED IDEOGRAPH SIX +3226;3226;3226;0028 4E03 0029;0028 4E03 0029; # (㈦; ㈦; ㈦; (七); (七); ) PARENTHESIZED IDEOGRAPH SEVEN +3227;3227;3227;0028 516B 0029;0028 516B 0029; # (㈧; ㈧; ㈧; (八); (八); ) PARENTHESIZED IDEOGRAPH EIGHT +3228;3228;3228;0028 4E5D 0029;0028 4E5D 0029; # (㈨; ㈨; ㈨; (九); (九); ) PARENTHESIZED IDEOGRAPH NINE +3229;3229;3229;0028 5341 0029;0028 5341 0029; # (㈩; ㈩; ㈩; (十); (十); ) PARENTHESIZED IDEOGRAPH TEN +322A;322A;322A;0028 6708 0029;0028 6708 0029; # (㈪; ㈪; ㈪; (月); (月); ) PARENTHESIZED IDEOGRAPH MOON +322B;322B;322B;0028 706B 0029;0028 706B 0029; # (㈫; ㈫; ㈫; (火); (火); ) PARENTHESIZED IDEOGRAPH FIRE +322C;322C;322C;0028 6C34 0029;0028 6C34 0029; # (㈬; ㈬; ㈬; (水); (水); ) PARENTHESIZED IDEOGRAPH WATER +322D;322D;322D;0028 6728 0029;0028 6728 0029; # (㈭; ㈭; ㈭; (木); (木); ) PARENTHESIZED IDEOGRAPH WOOD +322E;322E;322E;0028 91D1 0029;0028 91D1 0029; # (㈮; ㈮; ㈮; (金); (金); ) PARENTHESIZED IDEOGRAPH METAL +322F;322F;322F;0028 571F 0029;0028 571F 0029; # (㈯; ㈯; ㈯; (土); (土); ) PARENTHESIZED IDEOGRAPH EARTH +3230;3230;3230;0028 65E5 0029;0028 65E5 0029; # (㈰; ㈰; ㈰; (日); (日); ) PARENTHESIZED IDEOGRAPH SUN +3231;3231;3231;0028 682A 0029;0028 682A 0029; # (㈱; ㈱; ㈱; (株); (株); ) PARENTHESIZED IDEOGRAPH STOCK +3232;3232;3232;0028 6709 0029;0028 6709 0029; # (㈲; ㈲; ㈲; (有); (有); ) PARENTHESIZED IDEOGRAPH HAVE +3233;3233;3233;0028 793E 0029;0028 793E 0029; # (㈳; ㈳; ㈳; (社); (社); ) PARENTHESIZED IDEOGRAPH SOCIETY +3234;3234;3234;0028 540D 0029;0028 540D 0029; # (㈴; ㈴; ㈴; (名); (名); ) PARENTHESIZED IDEOGRAPH NAME +3235;3235;3235;0028 7279 0029;0028 7279 0029; # (㈵; ㈵; ㈵; (特); (特); ) PARENTHESIZED IDEOGRAPH SPECIAL +3236;3236;3236;0028 8CA1 0029;0028 8CA1 0029; # (㈶; ㈶; ㈶; (財); (財); ) PARENTHESIZED IDEOGRAPH FINANCIAL +3237;3237;3237;0028 795D 0029;0028 795D 0029; # (㈷; ㈷; ㈷; (祝); (祝); ) PARENTHESIZED IDEOGRAPH CONGRATULATION +3238;3238;3238;0028 52B4 0029;0028 52B4 0029; # (㈸; ㈸; ㈸; (労); (労); ) PARENTHESIZED IDEOGRAPH LABOR +3239;3239;3239;0028 4EE3 0029;0028 4EE3 0029; # (㈹; ㈹; ㈹; (代); (代); ) PARENTHESIZED IDEOGRAPH REPRESENT +323A;323A;323A;0028 547C 0029;0028 547C 0029; # (㈺; ㈺; ㈺; (呼); (呼); ) PARENTHESIZED IDEOGRAPH CALL +323B;323B;323B;0028 5B66 0029;0028 5B66 0029; # (㈻; ㈻; ㈻; (学); (学); ) PARENTHESIZED IDEOGRAPH STUDY +323C;323C;323C;0028 76E3 0029;0028 76E3 0029; # (㈼; ㈼; ㈼; (監); (監); ) PARENTHESIZED IDEOGRAPH SUPERVISE +323D;323D;323D;0028 4F01 0029;0028 4F01 0029; # (㈽; ㈽; ㈽; (企); (企); ) PARENTHESIZED IDEOGRAPH ENTERPRISE +323E;323E;323E;0028 8CC7 0029;0028 8CC7 0029; # (㈾; ㈾; ㈾; (資); (資); ) PARENTHESIZED IDEOGRAPH RESOURCE +323F;323F;323F;0028 5354 0029;0028 5354 0029; # (㈿; ㈿; ㈿; (協); (協); ) PARENTHESIZED IDEOGRAPH ALLIANCE +3240;3240;3240;0028 796D 0029;0028 796D 0029; # (㉀; ㉀; ㉀; (祭); (祭); ) PARENTHESIZED IDEOGRAPH FESTIVAL +3241;3241;3241;0028 4F11 0029;0028 4F11 0029; # (㉁; ㉁; ㉁; (休); (休); ) PARENTHESIZED IDEOGRAPH REST +3242;3242;3242;0028 81EA 0029;0028 81EA 0029; # (㉂; ㉂; ㉂; (自); (自); ) PARENTHESIZED IDEOGRAPH SELF +3243;3243;3243;0028 81F3 0029;0028 81F3 0029; # (㉃; ㉃; ㉃; (至); (至); ) PARENTHESIZED IDEOGRAPH REACH +3251;3251;3251;0032 0031;0032 0031; # (㉑; ㉑; ㉑; 21; 21; ) CIRCLED NUMBER TWENTY ONE +3252;3252;3252;0032 0032;0032 0032; # (㉒; ㉒; ㉒; 22; 22; ) CIRCLED NUMBER TWENTY TWO +3253;3253;3253;0032 0033;0032 0033; # (㉓; ㉓; ㉓; 23; 23; ) CIRCLED NUMBER TWENTY THREE +3254;3254;3254;0032 0034;0032 0034; # (㉔; ㉔; ㉔; 24; 24; ) CIRCLED NUMBER TWENTY FOUR +3255;3255;3255;0032 0035;0032 0035; # (㉕; ㉕; ㉕; 25; 25; ) CIRCLED NUMBER TWENTY FIVE +3256;3256;3256;0032 0036;0032 0036; # (㉖; ㉖; ㉖; 26; 26; ) CIRCLED NUMBER TWENTY SIX +3257;3257;3257;0032 0037;0032 0037; # (㉗; ㉗; ㉗; 27; 27; ) CIRCLED NUMBER TWENTY SEVEN +3258;3258;3258;0032 0038;0032 0038; # (㉘; ㉘; ㉘; 28; 28; ) CIRCLED NUMBER TWENTY EIGHT +3259;3259;3259;0032 0039;0032 0039; # (㉙; ㉙; ㉙; 29; 29; ) CIRCLED NUMBER TWENTY NINE +325A;325A;325A;0033 0030;0033 0030; # (㉚; ㉚; ㉚; 30; 30; ) CIRCLED NUMBER THIRTY +325B;325B;325B;0033 0031;0033 0031; # (㉛; ㉛; ㉛; 31; 31; ) CIRCLED NUMBER THIRTY ONE +325C;325C;325C;0033 0032;0033 0032; # (㉜; ㉜; ㉜; 32; 32; ) CIRCLED NUMBER THIRTY TWO +325D;325D;325D;0033 0033;0033 0033; # (㉝; ㉝; ㉝; 33; 33; ) CIRCLED NUMBER THIRTY THREE +325E;325E;325E;0033 0034;0033 0034; # (㉞; ㉞; ㉞; 34; 34; ) CIRCLED NUMBER THIRTY FOUR +325F;325F;325F;0033 0035;0033 0035; # (㉟; ㉟; ㉟; 35; 35; ) CIRCLED NUMBER THIRTY FIVE +3260;3260;3260;1100;1100; # (㉠; ㉠; ㉠; ᄀ; ᄀ; ) CIRCLED HANGUL KIYEOK +3261;3261;3261;1102;1102; # (㉡; ㉡; ㉡; ᄂ; ᄂ; ) CIRCLED HANGUL NIEUN +3262;3262;3262;1103;1103; # (㉢; ㉢; ㉢; ᄃ; ᄃ; ) CIRCLED HANGUL TIKEUT +3263;3263;3263;1105;1105; # (㉣; ㉣; ㉣; ᄅ; ᄅ; ) CIRCLED HANGUL RIEUL +3264;3264;3264;1106;1106; # (㉤; ㉤; ㉤; ᄆ; ᄆ; ) CIRCLED HANGUL MIEUM +3265;3265;3265;1107;1107; # (㉥; ㉥; ㉥; ᄇ; ᄇ; ) CIRCLED HANGUL PIEUP +3266;3266;3266;1109;1109; # (㉦; ㉦; ㉦; ᄉ; ᄉ; ) CIRCLED HANGUL SIOS +3267;3267;3267;110B;110B; # (㉧; ㉧; ㉧; ᄋ; ᄋ; ) CIRCLED HANGUL IEUNG +3268;3268;3268;110C;110C; # (㉨; ㉨; ㉨; ᄌ; ᄌ; ) CIRCLED HANGUL CIEUC +3269;3269;3269;110E;110E; # (㉩; ㉩; ㉩; ᄎ; ᄎ; ) CIRCLED HANGUL CHIEUCH +326A;326A;326A;110F;110F; # (㉪; ㉪; ㉪; ᄏ; ᄏ; ) CIRCLED HANGUL KHIEUKH +326B;326B;326B;1110;1110; # (㉫; ㉫; ㉫; ᄐ; ᄐ; ) CIRCLED HANGUL THIEUTH +326C;326C;326C;1111;1111; # (㉬; ㉬; ㉬; ᄑ; ᄑ; ) CIRCLED HANGUL PHIEUPH +326D;326D;326D;1112;1112; # (㉭; ㉭; ㉭; ᄒ; ᄒ; ) CIRCLED HANGUL HIEUH +326E;326E;326E;AC00;1100 1161; # (㉮; ㉮; ㉮; 가; 가; ) CIRCLED HANGUL KIYEOK A +326F;326F;326F;B098;1102 1161; # (㉯; ㉯; ㉯; 나; 나; ) CIRCLED HANGUL NIEUN A +3270;3270;3270;B2E4;1103 1161; # (㉰; ㉰; ㉰; 다; 다; ) CIRCLED HANGUL TIKEUT A +3271;3271;3271;B77C;1105 1161; # (㉱; ㉱; ㉱; 라; 라; ) CIRCLED HANGUL RIEUL A +3272;3272;3272;B9C8;1106 1161; # (㉲; ㉲; ㉲; 마; 마; ) CIRCLED HANGUL MIEUM A +3273;3273;3273;BC14;1107 1161; # (㉳; ㉳; ㉳; 바; 바; ) CIRCLED HANGUL PIEUP A +3274;3274;3274;C0AC;1109 1161; # (㉴; ㉴; ㉴; 사; 사; ) CIRCLED HANGUL SIOS A +3275;3275;3275;C544;110B 1161; # (㉵; ㉵; ㉵; 아; 아; ) CIRCLED HANGUL IEUNG A +3276;3276;3276;C790;110C 1161; # (㉶; ㉶; ㉶; 자; 자; ) CIRCLED HANGUL CIEUC A +3277;3277;3277;CC28;110E 1161; # (㉷; ㉷; ㉷; 차; 차; ) CIRCLED HANGUL CHIEUCH A +3278;3278;3278;CE74;110F 1161; # (㉸; ㉸; ㉸; 카; 카; ) CIRCLED HANGUL KHIEUKH A +3279;3279;3279;D0C0;1110 1161; # (㉹; ㉹; ㉹; 타; 타; ) CIRCLED HANGUL THIEUTH A +327A;327A;327A;D30C;1111 1161; # (㉺; ㉺; ㉺; 파; 파; ) CIRCLED HANGUL PHIEUPH A +327B;327B;327B;D558;1112 1161; # (㉻; ㉻; ㉻; 하; 하; ) CIRCLED HANGUL HIEUH A +3280;3280;3280;4E00;4E00; # (㊀; ㊀; ㊀; 一; 一; ) CIRCLED IDEOGRAPH ONE +3281;3281;3281;4E8C;4E8C; # (㊁; ㊁; ㊁; 二; 二; ) CIRCLED IDEOGRAPH TWO +3282;3282;3282;4E09;4E09; # (㊂; ㊂; ㊂; 三; 三; ) CIRCLED IDEOGRAPH THREE +3283;3283;3283;56DB;56DB; # (㊃; ㊃; ㊃; 四; 四; ) CIRCLED IDEOGRAPH FOUR +3284;3284;3284;4E94;4E94; # (㊄; ㊄; ㊄; 五; 五; ) CIRCLED IDEOGRAPH FIVE +3285;3285;3285;516D;516D; # (㊅; ㊅; ㊅; 六; 六; ) CIRCLED IDEOGRAPH SIX +3286;3286;3286;4E03;4E03; # (㊆; ㊆; ㊆; 七; 七; ) CIRCLED IDEOGRAPH SEVEN +3287;3287;3287;516B;516B; # (㊇; ㊇; ㊇; 八; 八; ) CIRCLED IDEOGRAPH EIGHT +3288;3288;3288;4E5D;4E5D; # (㊈; ㊈; ㊈; 九; 九; ) CIRCLED IDEOGRAPH NINE +3289;3289;3289;5341;5341; # (㊉; ㊉; ㊉; 十; 十; ) CIRCLED IDEOGRAPH TEN +328A;328A;328A;6708;6708; # (㊊; ㊊; ㊊; 月; 月; ) CIRCLED IDEOGRAPH MOON +328B;328B;328B;706B;706B; # (㊋; ㊋; ㊋; 火; 火; ) CIRCLED IDEOGRAPH FIRE +328C;328C;328C;6C34;6C34; # (㊌; ㊌; ㊌; 水; 水; ) CIRCLED IDEOGRAPH WATER +328D;328D;328D;6728;6728; # (㊍; ㊍; ㊍; 木; 木; ) CIRCLED IDEOGRAPH WOOD +328E;328E;328E;91D1;91D1; # (㊎; ㊎; ㊎; 金; 金; ) CIRCLED IDEOGRAPH METAL +328F;328F;328F;571F;571F; # (㊏; ㊏; ㊏; 土; 土; ) CIRCLED IDEOGRAPH EARTH +3290;3290;3290;65E5;65E5; # (㊐; ㊐; ㊐; 日; 日; ) CIRCLED IDEOGRAPH SUN +3291;3291;3291;682A;682A; # (㊑; ㊑; ㊑; 株; 株; ) CIRCLED IDEOGRAPH STOCK +3292;3292;3292;6709;6709; # (㊒; ㊒; ㊒; 有; 有; ) CIRCLED IDEOGRAPH HAVE +3293;3293;3293;793E;793E; # (㊓; ㊓; ㊓; 社; 社; ) CIRCLED IDEOGRAPH SOCIETY +3294;3294;3294;540D;540D; # (㊔; ㊔; ㊔; 名; 名; ) CIRCLED IDEOGRAPH NAME +3295;3295;3295;7279;7279; # (㊕; ㊕; ㊕; 特; 特; ) CIRCLED IDEOGRAPH SPECIAL +3296;3296;3296;8CA1;8CA1; # (㊖; ㊖; ㊖; 財; 財; ) CIRCLED IDEOGRAPH FINANCIAL +3297;3297;3297;795D;795D; # (㊗; ㊗; ㊗; 祝; 祝; ) CIRCLED IDEOGRAPH CONGRATULATION +3298;3298;3298;52B4;52B4; # (㊘; ㊘; ㊘; 労; 労; ) CIRCLED IDEOGRAPH LABOR +3299;3299;3299;79D8;79D8; # (㊙; ㊙; ㊙; 秘; 秘; ) CIRCLED IDEOGRAPH SECRET +329A;329A;329A;7537;7537; # (㊚; ㊚; ㊚; 男; 男; ) CIRCLED IDEOGRAPH MALE +329B;329B;329B;5973;5973; # (㊛; ㊛; ㊛; 女; 女; ) CIRCLED IDEOGRAPH FEMALE +329C;329C;329C;9069;9069; # (㊜; ㊜; ㊜; 適; 適; ) CIRCLED IDEOGRAPH SUITABLE +329D;329D;329D;512A;512A; # (㊝; ㊝; ㊝; 優; 優; ) CIRCLED IDEOGRAPH EXCELLENT +329E;329E;329E;5370;5370; # (㊞; ㊞; ㊞; 印; 印; ) CIRCLED IDEOGRAPH PRINT +329F;329F;329F;6CE8;6CE8; # (㊟; ㊟; ㊟; 注; 注; ) CIRCLED IDEOGRAPH ATTENTION +32A0;32A0;32A0;9805;9805; # (㊠; ㊠; ㊠; 項; 項; ) CIRCLED IDEOGRAPH ITEM +32A1;32A1;32A1;4F11;4F11; # (㊡; ㊡; ㊡; 休; 休; ) CIRCLED IDEOGRAPH REST +32A2;32A2;32A2;5199;5199; # (㊢; ㊢; ㊢; 写; 写; ) CIRCLED IDEOGRAPH COPY +32A3;32A3;32A3;6B63;6B63; # (㊣; ㊣; ㊣; 正; 正; ) CIRCLED IDEOGRAPH CORRECT +32A4;32A4;32A4;4E0A;4E0A; # (㊤; ㊤; ㊤; 上; 上; ) CIRCLED IDEOGRAPH HIGH +32A5;32A5;32A5;4E2D;4E2D; # (㊥; ㊥; ㊥; 中; 中; ) CIRCLED IDEOGRAPH CENTRE +32A6;32A6;32A6;4E0B;4E0B; # (㊦; ㊦; ㊦; 下; 下; ) CIRCLED IDEOGRAPH LOW +32A7;32A7;32A7;5DE6;5DE6; # (㊧; ㊧; ㊧; 左; 左; ) CIRCLED IDEOGRAPH LEFT +32A8;32A8;32A8;53F3;53F3; # (㊨; ㊨; ㊨; 右; 右; ) CIRCLED IDEOGRAPH RIGHT +32A9;32A9;32A9;533B;533B; # (㊩; ㊩; ㊩; 医; 医; ) CIRCLED IDEOGRAPH MEDICINE +32AA;32AA;32AA;5B97;5B97; # (㊪; ㊪; ㊪; 宗; 宗; ) CIRCLED IDEOGRAPH RELIGION +32AB;32AB;32AB;5B66;5B66; # (㊫; ㊫; ㊫; 学; 学; ) CIRCLED IDEOGRAPH STUDY +32AC;32AC;32AC;76E3;76E3; # (㊬; ㊬; ㊬; 監; 監; ) CIRCLED IDEOGRAPH SUPERVISE +32AD;32AD;32AD;4F01;4F01; # (㊭; ㊭; ㊭; 企; 企; ) CIRCLED IDEOGRAPH ENTERPRISE +32AE;32AE;32AE;8CC7;8CC7; # (㊮; ㊮; ㊮; 資; 資; ) CIRCLED IDEOGRAPH RESOURCE +32AF;32AF;32AF;5354;5354; # (㊯; ㊯; ㊯; 協; 協; ) CIRCLED IDEOGRAPH ALLIANCE +32B0;32B0;32B0;591C;591C; # (㊰; ㊰; ㊰; 夜; 夜; ) CIRCLED IDEOGRAPH NIGHT +32B1;32B1;32B1;0033 0036;0033 0036; # (㊱; ㊱; ㊱; 36; 36; ) CIRCLED NUMBER THIRTY SIX +32B2;32B2;32B2;0033 0037;0033 0037; # (㊲; ㊲; ㊲; 37; 37; ) CIRCLED NUMBER THIRTY SEVEN +32B3;32B3;32B3;0033 0038;0033 0038; # (㊳; ㊳; ㊳; 38; 38; ) CIRCLED NUMBER THIRTY EIGHT +32B4;32B4;32B4;0033 0039;0033 0039; # (㊴; ㊴; ㊴; 39; 39; ) CIRCLED NUMBER THIRTY NINE +32B5;32B5;32B5;0034 0030;0034 0030; # (㊵; ㊵; ㊵; 40; 40; ) CIRCLED NUMBER FORTY +32B6;32B6;32B6;0034 0031;0034 0031; # (㊶; ㊶; ㊶; 41; 41; ) CIRCLED NUMBER FORTY ONE +32B7;32B7;32B7;0034 0032;0034 0032; # (㊷; ㊷; ㊷; 42; 42; ) CIRCLED NUMBER FORTY TWO +32B8;32B8;32B8;0034 0033;0034 0033; # (㊸; ㊸; ㊸; 43; 43; ) CIRCLED NUMBER FORTY THREE +32B9;32B9;32B9;0034 0034;0034 0034; # (㊹; ㊹; ㊹; 44; 44; ) CIRCLED NUMBER FORTY FOUR +32BA;32BA;32BA;0034 0035;0034 0035; # (㊺; ㊺; ㊺; 45; 45; ) CIRCLED NUMBER FORTY FIVE +32BB;32BB;32BB;0034 0036;0034 0036; # (㊻; ㊻; ㊻; 46; 46; ) CIRCLED NUMBER FORTY SIX +32BC;32BC;32BC;0034 0037;0034 0037; # (㊼; ㊼; ㊼; 47; 47; ) CIRCLED NUMBER FORTY SEVEN +32BD;32BD;32BD;0034 0038;0034 0038; # (㊽; ㊽; ㊽; 48; 48; ) CIRCLED NUMBER FORTY EIGHT +32BE;32BE;32BE;0034 0039;0034 0039; # (㊾; ㊾; ㊾; 49; 49; ) CIRCLED NUMBER FORTY NINE +32BF;32BF;32BF;0035 0030;0035 0030; # (㊿; ㊿; ㊿; 50; 50; ) CIRCLED NUMBER FIFTY +32C0;32C0;32C0;0031 6708;0031 6708; # (㋀; ㋀; ㋀; 1月; 1月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY +32C1;32C1;32C1;0032 6708;0032 6708; # (㋁; ㋁; ㋁; 2月; 2月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY +32C2;32C2;32C2;0033 6708;0033 6708; # (㋂; ㋂; ㋂; 3月; 3月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH +32C3;32C3;32C3;0034 6708;0034 6708; # (㋃; ㋃; ㋃; 4月; 4月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL +32C4;32C4;32C4;0035 6708;0035 6708; # (㋄; ㋄; ㋄; 5月; 5月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY +32C5;32C5;32C5;0036 6708;0036 6708; # (㋅; ㋅; ㋅; 6月; 6月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE +32C6;32C6;32C6;0037 6708;0037 6708; # (㋆; ㋆; ㋆; 7月; 7月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY +32C7;32C7;32C7;0038 6708;0038 6708; # (㋇; ㋇; ㋇; 8月; 8月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST +32C8;32C8;32C8;0039 6708;0039 6708; # (㋈; ㋈; ㋈; 9月; 9月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER +32C9;32C9;32C9;0031 0030 6708;0031 0030 6708; # (㋉; ㋉; ㋉; 10月; 10月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER +32CA;32CA;32CA;0031 0031 6708;0031 0031 6708; # (㋊; ㋊; ㋊; 11月; 11月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER +32CB;32CB;32CB;0031 0032 6708;0031 0032 6708; # (㋋; ㋋; ㋋; 12月; 12月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER +32D0;32D0;32D0;30A2;30A2; # (㋐; ㋐; ㋐; ア; ア; ) CIRCLED KATAKANA A +32D1;32D1;32D1;30A4;30A4; # (㋑; ㋑; ㋑; イ; イ; ) CIRCLED KATAKANA I +32D2;32D2;32D2;30A6;30A6; # (㋒; ㋒; ㋒; ウ; ウ; ) CIRCLED KATAKANA U +32D3;32D3;32D3;30A8;30A8; # (㋓; ㋓; ㋓; エ; エ; ) CIRCLED KATAKANA E +32D4;32D4;32D4;30AA;30AA; # (㋔; ㋔; ㋔; オ; オ; ) CIRCLED KATAKANA O +32D5;32D5;32D5;30AB;30AB; # (㋕; ㋕; ㋕; カ; カ; ) CIRCLED KATAKANA KA +32D6;32D6;32D6;30AD;30AD; # (㋖; ㋖; ㋖; キ; キ; ) CIRCLED KATAKANA KI +32D7;32D7;32D7;30AF;30AF; # (㋗; ㋗; ㋗; ク; ク; ) CIRCLED KATAKANA KU +32D8;32D8;32D8;30B1;30B1; # (㋘; ㋘; ㋘; ケ; ケ; ) CIRCLED KATAKANA KE +32D9;32D9;32D9;30B3;30B3; # (㋙; ㋙; ㋙; コ; コ; ) CIRCLED KATAKANA KO +32DA;32DA;32DA;30B5;30B5; # (㋚; ㋚; ㋚; サ; サ; ) CIRCLED KATAKANA SA +32DB;32DB;32DB;30B7;30B7; # (㋛; ㋛; ㋛; シ; シ; ) CIRCLED KATAKANA SI +32DC;32DC;32DC;30B9;30B9; # (㋜; ㋜; ㋜; ス; ス; ) CIRCLED KATAKANA SU +32DD;32DD;32DD;30BB;30BB; # (㋝; ㋝; ㋝; セ; セ; ) CIRCLED KATAKANA SE +32DE;32DE;32DE;30BD;30BD; # (㋞; ㋞; ㋞; ソ; ソ; ) CIRCLED KATAKANA SO +32DF;32DF;32DF;30BF;30BF; # (㋟; ㋟; ㋟; タ; タ; ) CIRCLED KATAKANA TA +32E0;32E0;32E0;30C1;30C1; # (㋠; ㋠; ㋠; チ; チ; ) CIRCLED KATAKANA TI +32E1;32E1;32E1;30C4;30C4; # (㋡; ㋡; ㋡; ツ; ツ; ) CIRCLED KATAKANA TU +32E2;32E2;32E2;30C6;30C6; # (㋢; ㋢; ㋢; テ; テ; ) CIRCLED KATAKANA TE +32E3;32E3;32E3;30C8;30C8; # (㋣; ㋣; ㋣; ト; ト; ) CIRCLED KATAKANA TO +32E4;32E4;32E4;30CA;30CA; # (㋤; ㋤; ㋤; ナ; ナ; ) CIRCLED KATAKANA NA +32E5;32E5;32E5;30CB;30CB; # (㋥; ㋥; ㋥; ニ; ニ; ) CIRCLED KATAKANA NI +32E6;32E6;32E6;30CC;30CC; # (㋦; ㋦; ㋦; ヌ; ヌ; ) CIRCLED KATAKANA NU +32E7;32E7;32E7;30CD;30CD; # (㋧; ㋧; ㋧; ネ; ネ; ) CIRCLED KATAKANA NE +32E8;32E8;32E8;30CE;30CE; # (㋨; ㋨; ㋨; ノ; ノ; ) CIRCLED KATAKANA NO +32E9;32E9;32E9;30CF;30CF; # (㋩; ㋩; ㋩; ハ; ハ; ) CIRCLED KATAKANA HA +32EA;32EA;32EA;30D2;30D2; # (㋪; ㋪; ㋪; ヒ; ヒ; ) CIRCLED KATAKANA HI +32EB;32EB;32EB;30D5;30D5; # (㋫; ㋫; ㋫; フ; フ; ) CIRCLED KATAKANA HU +32EC;32EC;32EC;30D8;30D8; # (㋬; ㋬; ㋬; ヘ; ヘ; ) CIRCLED KATAKANA HE +32ED;32ED;32ED;30DB;30DB; # (㋭; ㋭; ㋭; ホ; ホ; ) CIRCLED KATAKANA HO +32EE;32EE;32EE;30DE;30DE; # (㋮; ㋮; ㋮; マ; マ; ) CIRCLED KATAKANA MA +32EF;32EF;32EF;30DF;30DF; # (㋯; ㋯; ㋯; ミ; ミ; ) CIRCLED KATAKANA MI +32F0;32F0;32F0;30E0;30E0; # (㋰; ㋰; ㋰; ム; ム; ) CIRCLED KATAKANA MU +32F1;32F1;32F1;30E1;30E1; # (㋱; ㋱; ㋱; メ; メ; ) CIRCLED KATAKANA ME +32F2;32F2;32F2;30E2;30E2; # (㋲; ㋲; ㋲; モ; モ; ) CIRCLED KATAKANA MO +32F3;32F3;32F3;30E4;30E4; # (㋳; ㋳; ㋳; ヤ; ヤ; ) CIRCLED KATAKANA YA +32F4;32F4;32F4;30E6;30E6; # (㋴; ㋴; ㋴; ユ; ユ; ) CIRCLED KATAKANA YU +32F5;32F5;32F5;30E8;30E8; # (㋵; ㋵; ㋵; ヨ; ヨ; ) CIRCLED KATAKANA YO +32F6;32F6;32F6;30E9;30E9; # (㋶; ㋶; ㋶; ラ; ラ; ) CIRCLED KATAKANA RA +32F7;32F7;32F7;30EA;30EA; # (㋷; ㋷; ㋷; リ; リ; ) CIRCLED KATAKANA RI +32F8;32F8;32F8;30EB;30EB; # (㋸; ㋸; ㋸; ル; ル; ) CIRCLED KATAKANA RU +32F9;32F9;32F9;30EC;30EC; # (㋹; ㋹; ㋹; レ; レ; ) CIRCLED KATAKANA RE +32FA;32FA;32FA;30ED;30ED; # (㋺; ㋺; ㋺; ロ; ロ; ) CIRCLED KATAKANA RO +32FB;32FB;32FB;30EF;30EF; # (㋻; ㋻; ㋻; ワ; ワ; ) CIRCLED KATAKANA WA +32FC;32FC;32FC;30F0;30F0; # (㋼; ㋼; ㋼; ヰ; ヰ; ) CIRCLED KATAKANA WI +32FD;32FD;32FD;30F1;30F1; # (㋽; ㋽; ㋽; ヱ; ヱ; ) CIRCLED KATAKANA WE +32FE;32FE;32FE;30F2;30F2; # (㋾; ㋾; ㋾; ヲ; ヲ; ) CIRCLED KATAKANA WO +3300;3300;3300;30A2 30D1 30FC 30C8;30A2 30CF 309A 30FC 30C8; # (㌀; ㌀; ㌀; アパート; アハ◌゚ート; ) SQUARE APAATO +3301;3301;3301;30A2 30EB 30D5 30A1;30A2 30EB 30D5 30A1; # (㌁; ㌁; ㌁; アルファ; アルファ; ) SQUARE ARUHUA +3302;3302;3302;30A2 30F3 30DA 30A2;30A2 30F3 30D8 309A 30A2; # (㌂; ㌂; ㌂; アンペア; アンヘ◌゚ア; ) SQUARE ANPEA +3303;3303;3303;30A2 30FC 30EB;30A2 30FC 30EB; # (㌃; ㌃; ㌃; アール; アール; ) SQUARE AARU +3304;3304;3304;30A4 30CB 30F3 30B0;30A4 30CB 30F3 30AF 3099; # (㌄; ㌄; ㌄; イニング; イニンク◌゙; ) SQUARE ININGU +3305;3305;3305;30A4 30F3 30C1;30A4 30F3 30C1; # (㌅; ㌅; ㌅; インチ; インチ; ) SQUARE INTI +3306;3306;3306;30A6 30A9 30F3;30A6 30A9 30F3; # (㌆; ㌆; ㌆; ウォン; ウォン; ) SQUARE UON +3307;3307;3307;30A8 30B9 30AF 30FC 30C9;30A8 30B9 30AF 30FC 30C8 3099; # (㌇; ㌇; ㌇; エスクード; エスクート◌゙; ) SQUARE ESUKUUDO +3308;3308;3308;30A8 30FC 30AB 30FC;30A8 30FC 30AB 30FC; # (㌈; ㌈; ㌈; エーカー; エーカー; ) SQUARE EEKAA +3309;3309;3309;30AA 30F3 30B9;30AA 30F3 30B9; # (㌉; ㌉; ㌉; オンス; オンス; ) SQUARE ONSU +330A;330A;330A;30AA 30FC 30E0;30AA 30FC 30E0; # (㌊; ㌊; ㌊; オーム; オーム; ) SQUARE OOMU +330B;330B;330B;30AB 30A4 30EA;30AB 30A4 30EA; # (㌋; ㌋; ㌋; カイリ; カイリ; ) SQUARE KAIRI +330C;330C;330C;30AB 30E9 30C3 30C8;30AB 30E9 30C3 30C8; # (㌌; ㌌; ㌌; カラット; カラット; ) SQUARE KARATTO +330D;330D;330D;30AB 30ED 30EA 30FC;30AB 30ED 30EA 30FC; # (㌍; ㌍; ㌍; カロリー; カロリー; ) SQUARE KARORII +330E;330E;330E;30AC 30ED 30F3;30AB 3099 30ED 30F3; # (㌎; ㌎; ㌎; ガロン; カ◌゙ロン; ) SQUARE GARON +330F;330F;330F;30AC 30F3 30DE;30AB 3099 30F3 30DE; # (㌏; ㌏; ㌏; ガンマ; カ◌゙ンマ; ) SQUARE GANMA +3310;3310;3310;30AE 30AC;30AD 3099 30AB 3099; # (㌐; ㌐; ㌐; ギガ; キ◌゙カ◌゙; ) SQUARE GIGA +3311;3311;3311;30AE 30CB 30FC;30AD 3099 30CB 30FC; # (㌑; ㌑; ㌑; ギニー; キ◌゙ニー; ) SQUARE GINII +3312;3312;3312;30AD 30E5 30EA 30FC;30AD 30E5 30EA 30FC; # (㌒; ㌒; ㌒; キュリー; キュリー; ) SQUARE KYURII +3313;3313;3313;30AE 30EB 30C0 30FC;30AD 3099 30EB 30BF 3099 30FC; # (㌓; ㌓; ㌓; ギルダー; キ◌゙ルタ◌゙ー; ) SQUARE GIRUDAA +3314;3314;3314;30AD 30ED;30AD 30ED; # (㌔; ㌔; ㌔; キロ; キロ; ) SQUARE KIRO +3315;3315;3315;30AD 30ED 30B0 30E9 30E0;30AD 30ED 30AF 3099 30E9 30E0; # (㌕; ㌕; ㌕; キログラム; キロク◌゙ラム; ) SQUARE KIROGURAMU +3316;3316;3316;30AD 30ED 30E1 30FC 30C8 30EB;30AD 30ED 30E1 30FC 30C8 30EB; # (㌖; ㌖; ㌖; キロメートル; キロメートル; ) SQUARE KIROMEETORU +3317;3317;3317;30AD 30ED 30EF 30C3 30C8;30AD 30ED 30EF 30C3 30C8; # (㌗; ㌗; ㌗; キロワット; キロワット; ) SQUARE KIROWATTO +3318;3318;3318;30B0 30E9 30E0;30AF 3099 30E9 30E0; # (㌘; ㌘; ㌘; グラム; ク◌゙ラム; ) SQUARE GURAMU +3319;3319;3319;30B0 30E9 30E0 30C8 30F3;30AF 3099 30E9 30E0 30C8 30F3; # (㌙; ㌙; ㌙; グラムトン; ク◌゙ラムトン; ) SQUARE GURAMUTON +331A;331A;331A;30AF 30EB 30BC 30A4 30ED;30AF 30EB 30BB 3099 30A4 30ED; # (㌚; ㌚; ㌚; クルゼイロ; クルセ◌゙イロ; ) SQUARE KURUZEIRO +331B;331B;331B;30AF 30ED 30FC 30CD;30AF 30ED 30FC 30CD; # (㌛; ㌛; ㌛; クローネ; クローネ; ) SQUARE KUROONE +331C;331C;331C;30B1 30FC 30B9;30B1 30FC 30B9; # (㌜; ㌜; ㌜; ケース; ケース; ) SQUARE KEESU +331D;331D;331D;30B3 30EB 30CA;30B3 30EB 30CA; # (㌝; ㌝; ㌝; コルナ; コルナ; ) SQUARE KORUNA +331E;331E;331E;30B3 30FC 30DD;30B3 30FC 30DB 309A; # (㌞; ㌞; ㌞; コーポ; コーホ◌゚; ) SQUARE KOOPO +331F;331F;331F;30B5 30A4 30AF 30EB;30B5 30A4 30AF 30EB; # (㌟; ㌟; ㌟; サイクル; サイクル; ) SQUARE SAIKURU +3320;3320;3320;30B5 30F3 30C1 30FC 30E0;30B5 30F3 30C1 30FC 30E0; # (㌠; ㌠; ㌠; サンチーム; サンチーム; ) SQUARE SANTIIMU +3321;3321;3321;30B7 30EA 30F3 30B0;30B7 30EA 30F3 30AF 3099; # (㌡; ㌡; ㌡; シリング; シリンク◌゙; ) SQUARE SIRINGU +3322;3322;3322;30BB 30F3 30C1;30BB 30F3 30C1; # (㌢; ㌢; ㌢; センチ; センチ; ) SQUARE SENTI +3323;3323;3323;30BB 30F3 30C8;30BB 30F3 30C8; # (㌣; ㌣; ㌣; セント; セント; ) SQUARE SENTO +3324;3324;3324;30C0 30FC 30B9;30BF 3099 30FC 30B9; # (㌤; ㌤; ㌤; ダース; タ◌゙ース; ) SQUARE DAASU +3325;3325;3325;30C7 30B7;30C6 3099 30B7; # (㌥; ㌥; ㌥; デシ; テ◌゙シ; ) SQUARE DESI +3326;3326;3326;30C9 30EB;30C8 3099 30EB; # (㌦; ㌦; ㌦; ドル; ト◌゙ル; ) SQUARE DORU +3327;3327;3327;30C8 30F3;30C8 30F3; # (㌧; ㌧; ㌧; トン; トン; ) SQUARE TON +3328;3328;3328;30CA 30CE;30CA 30CE; # (㌨; ㌨; ㌨; ナノ; ナノ; ) SQUARE NANO +3329;3329;3329;30CE 30C3 30C8;30CE 30C3 30C8; # (㌩; ㌩; ㌩; ノット; ノット; ) SQUARE NOTTO +332A;332A;332A;30CF 30A4 30C4;30CF 30A4 30C4; # (㌪; ㌪; ㌪; ハイツ; ハイツ; ) SQUARE HAITU +332B;332B;332B;30D1 30FC 30BB 30F3 30C8;30CF 309A 30FC 30BB 30F3 30C8; # (㌫; ㌫; ㌫; パーセント; ハ◌゚ーセント; ) SQUARE PAASENTO +332C;332C;332C;30D1 30FC 30C4;30CF 309A 30FC 30C4; # (㌬; ㌬; ㌬; パーツ; ハ◌゚ーツ; ) SQUARE PAATU +332D;332D;332D;30D0 30FC 30EC 30EB;30CF 3099 30FC 30EC 30EB; # (㌭; ㌭; ㌭; バーレル; ハ◌゙ーレル; ) SQUARE BAARERU +332E;332E;332E;30D4 30A2 30B9 30C8 30EB;30D2 309A 30A2 30B9 30C8 30EB; # (㌮; ㌮; ㌮; ピアストル; ヒ◌゚アストル; ) SQUARE PIASUTORU +332F;332F;332F;30D4 30AF 30EB;30D2 309A 30AF 30EB; # (㌯; ㌯; ㌯; ピクル; ヒ◌゚クル; ) SQUARE PIKURU +3330;3330;3330;30D4 30B3;30D2 309A 30B3; # (㌰; ㌰; ㌰; ピコ; ヒ◌゚コ; ) SQUARE PIKO +3331;3331;3331;30D3 30EB;30D2 3099 30EB; # (㌱; ㌱; ㌱; ビル; ヒ◌゙ル; ) SQUARE BIRU +3332;3332;3332;30D5 30A1 30E9 30C3 30C9;30D5 30A1 30E9 30C3 30C8 3099; # (㌲; ㌲; ㌲; ファラッド; ファラット◌゙; ) SQUARE HUARADDO +3333;3333;3333;30D5 30A3 30FC 30C8;30D5 30A3 30FC 30C8; # (㌳; ㌳; ㌳; フィート; フィート; ) SQUARE HUIITO +3334;3334;3334;30D6 30C3 30B7 30A7 30EB;30D5 3099 30C3 30B7 30A7 30EB; # (㌴; ㌴; ㌴; ブッシェル; フ◌゙ッシェル; ) SQUARE BUSSYERU +3335;3335;3335;30D5 30E9 30F3;30D5 30E9 30F3; # (㌵; ㌵; ㌵; フラン; フラン; ) SQUARE HURAN +3336;3336;3336;30D8 30AF 30BF 30FC 30EB;30D8 30AF 30BF 30FC 30EB; # (㌶; ㌶; ㌶; ヘクタール; ヘクタール; ) SQUARE HEKUTAARU +3337;3337;3337;30DA 30BD;30D8 309A 30BD; # (㌷; ㌷; ㌷; ペソ; ヘ◌゚ソ; ) SQUARE PESO +3338;3338;3338;30DA 30CB 30D2;30D8 309A 30CB 30D2; # (㌸; ㌸; ㌸; ペニヒ; ヘ◌゚ニヒ; ) SQUARE PENIHI +3339;3339;3339;30D8 30EB 30C4;30D8 30EB 30C4; # (㌹; ㌹; ㌹; ヘルツ; ヘルツ; ) SQUARE HERUTU +333A;333A;333A;30DA 30F3 30B9;30D8 309A 30F3 30B9; # (㌺; ㌺; ㌺; ペンス; ヘ◌゚ンス; ) SQUARE PENSU +333B;333B;333B;30DA 30FC 30B8;30D8 309A 30FC 30B7 3099; # (㌻; ㌻; ㌻; ページ; ヘ◌゚ーシ◌゙; ) SQUARE PEEZI +333C;333C;333C;30D9 30FC 30BF;30D8 3099 30FC 30BF; # (㌼; ㌼; ㌼; ベータ; ヘ◌゙ータ; ) SQUARE BEETA +333D;333D;333D;30DD 30A4 30F3 30C8;30DB 309A 30A4 30F3 30C8; # (㌽; ㌽; ㌽; ポイント; ホ◌゚イント; ) SQUARE POINTO +333E;333E;333E;30DC 30EB 30C8;30DB 3099 30EB 30C8; # (㌾; ㌾; ㌾; ボルト; ホ◌゙ルト; ) SQUARE BORUTO +333F;333F;333F;30DB 30F3;30DB 30F3; # (㌿; ㌿; ㌿; ホン; ホン; ) SQUARE HON +3340;3340;3340;30DD 30F3 30C9;30DB 309A 30F3 30C8 3099; # (㍀; ㍀; ㍀; ポンド; ホ◌゚ント◌゙; ) SQUARE PONDO +3341;3341;3341;30DB 30FC 30EB;30DB 30FC 30EB; # (㍁; ㍁; ㍁; ホール; ホール; ) SQUARE HOORU +3342;3342;3342;30DB 30FC 30F3;30DB 30FC 30F3; # (㍂; ㍂; ㍂; ホーン; ホーン; ) SQUARE HOON +3343;3343;3343;30DE 30A4 30AF 30ED;30DE 30A4 30AF 30ED; # (㍃; ㍃; ㍃; マイクロ; マイクロ; ) SQUARE MAIKURO +3344;3344;3344;30DE 30A4 30EB;30DE 30A4 30EB; # (㍄; ㍄; ㍄; マイル; マイル; ) SQUARE MAIRU +3345;3345;3345;30DE 30C3 30CF;30DE 30C3 30CF; # (㍅; ㍅; ㍅; マッハ; マッハ; ) SQUARE MAHHA +3346;3346;3346;30DE 30EB 30AF;30DE 30EB 30AF; # (㍆; ㍆; ㍆; マルク; マルク; ) SQUARE MARUKU +3347;3347;3347;30DE 30F3 30B7 30E7 30F3;30DE 30F3 30B7 30E7 30F3; # (㍇; ㍇; ㍇; マンション; マンション; ) SQUARE MANSYON +3348;3348;3348;30DF 30AF 30ED 30F3;30DF 30AF 30ED 30F3; # (㍈; ㍈; ㍈; ミクロン; ミクロン; ) SQUARE MIKURON +3349;3349;3349;30DF 30EA;30DF 30EA; # (㍉; ㍉; ㍉; ミリ; ミリ; ) SQUARE MIRI +334A;334A;334A;30DF 30EA 30D0 30FC 30EB;30DF 30EA 30CF 3099 30FC 30EB; # (㍊; ㍊; ㍊; ミリバール; ミリハ◌゙ール; ) SQUARE MIRIBAARU +334B;334B;334B;30E1 30AC;30E1 30AB 3099; # (㍋; ㍋; ㍋; メガ; メカ◌゙; ) SQUARE MEGA +334C;334C;334C;30E1 30AC 30C8 30F3;30E1 30AB 3099 30C8 30F3; # (㍌; ㍌; ㍌; メガトン; メカ◌゙トン; ) SQUARE MEGATON +334D;334D;334D;30E1 30FC 30C8 30EB;30E1 30FC 30C8 30EB; # (㍍; ㍍; ㍍; メートル; メートル; ) SQUARE MEETORU +334E;334E;334E;30E4 30FC 30C9;30E4 30FC 30C8 3099; # (㍎; ㍎; ㍎; ヤード; ヤート◌゙; ) SQUARE YAADO +334F;334F;334F;30E4 30FC 30EB;30E4 30FC 30EB; # (㍏; ㍏; ㍏; ヤール; ヤール; ) SQUARE YAARU +3350;3350;3350;30E6 30A2 30F3;30E6 30A2 30F3; # (㍐; ㍐; ㍐; ユアン; ユアン; ) SQUARE YUAN +3351;3351;3351;30EA 30C3 30C8 30EB;30EA 30C3 30C8 30EB; # (㍑; ㍑; ㍑; リットル; リットル; ) SQUARE RITTORU +3352;3352;3352;30EA 30E9;30EA 30E9; # (㍒; ㍒; ㍒; リラ; リラ; ) SQUARE RIRA +3353;3353;3353;30EB 30D4 30FC;30EB 30D2 309A 30FC; # (㍓; ㍓; ㍓; ルピー; ルヒ◌゚ー; ) SQUARE RUPII +3354;3354;3354;30EB 30FC 30D6 30EB;30EB 30FC 30D5 3099 30EB; # (㍔; ㍔; ㍔; ルーブル; ルーフ◌゙ル; ) SQUARE RUUBURU +3355;3355;3355;30EC 30E0;30EC 30E0; # (㍕; ㍕; ㍕; レム; レム; ) SQUARE REMU +3356;3356;3356;30EC 30F3 30C8 30B2 30F3;30EC 30F3 30C8 30B1 3099 30F3; # (㍖; ㍖; ㍖; レントゲン; レントケ◌゙ン; ) SQUARE RENTOGEN +3357;3357;3357;30EF 30C3 30C8;30EF 30C3 30C8; # (㍗; ㍗; ㍗; ワット; ワット; ) SQUARE WATTO +3358;3358;3358;0030 70B9;0030 70B9; # (㍘; ㍘; ㍘; 0点; 0点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO +3359;3359;3359;0031 70B9;0031 70B9; # (㍙; ㍙; ㍙; 1点; 1点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE +335A;335A;335A;0032 70B9;0032 70B9; # (㍚; ㍚; ㍚; 2点; 2点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO +335B;335B;335B;0033 70B9;0033 70B9; # (㍛; ㍛; ㍛; 3点; 3点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE +335C;335C;335C;0034 70B9;0034 70B9; # (㍜; ㍜; ㍜; 4点; 4点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR +335D;335D;335D;0035 70B9;0035 70B9; # (㍝; ㍝; ㍝; 5点; 5点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE +335E;335E;335E;0036 70B9;0036 70B9; # (㍞; ㍞; ㍞; 6点; 6点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX +335F;335F;335F;0037 70B9;0037 70B9; # (㍟; ㍟; ㍟; 7点; 7点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN +3360;3360;3360;0038 70B9;0038 70B9; # (㍠; ㍠; ㍠; 8点; 8点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT +3361;3361;3361;0039 70B9;0039 70B9; # (㍡; ㍡; ㍡; 9点; 9点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE +3362;3362;3362;0031 0030 70B9;0031 0030 70B9; # (㍢; ㍢; ㍢; 10点; 10点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN +3363;3363;3363;0031 0031 70B9;0031 0031 70B9; # (㍣; ㍣; ㍣; 11点; 11点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN +3364;3364;3364;0031 0032 70B9;0031 0032 70B9; # (㍤; ㍤; ㍤; 12点; 12点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE +3365;3365;3365;0031 0033 70B9;0031 0033 70B9; # (㍥; ㍥; ㍥; 13点; 13点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN +3366;3366;3366;0031 0034 70B9;0031 0034 70B9; # (㍦; ㍦; ㍦; 14点; 14点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN +3367;3367;3367;0031 0035 70B9;0031 0035 70B9; # (㍧; ㍧; ㍧; 15点; 15点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN +3368;3368;3368;0031 0036 70B9;0031 0036 70B9; # (㍨; ㍨; ㍨; 16点; 16点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN +3369;3369;3369;0031 0037 70B9;0031 0037 70B9; # (㍩; ㍩; ㍩; 17点; 17点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN +336A;336A;336A;0031 0038 70B9;0031 0038 70B9; # (㍪; ㍪; ㍪; 18点; 18点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN +336B;336B;336B;0031 0039 70B9;0031 0039 70B9; # (㍫; ㍫; ㍫; 19点; 19点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN +336C;336C;336C;0032 0030 70B9;0032 0030 70B9; # (㍬; ㍬; ㍬; 20点; 20点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY +336D;336D;336D;0032 0031 70B9;0032 0031 70B9; # (㍭; ㍭; ㍭; 21点; 21点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE +336E;336E;336E;0032 0032 70B9;0032 0032 70B9; # (㍮; ㍮; ㍮; 22点; 22点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO +336F;336F;336F;0032 0033 70B9;0032 0033 70B9; # (㍯; ㍯; ㍯; 23点; 23点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE +3370;3370;3370;0032 0034 70B9;0032 0034 70B9; # (㍰; ㍰; ㍰; 24点; 24点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR +3371;3371;3371;0068 0050 0061;0068 0050 0061; # (㍱; ㍱; ㍱; hPa; hPa; ) SQUARE HPA +3372;3372;3372;0064 0061;0064 0061; # (㍲; ㍲; ㍲; da; da; ) SQUARE DA +3373;3373;3373;0041 0055;0041 0055; # (㍳; ㍳; ㍳; AU; AU; ) SQUARE AU +3374;3374;3374;0062 0061 0072;0062 0061 0072; # (㍴; ㍴; ㍴; bar; bar; ) SQUARE BAR +3375;3375;3375;006F 0056;006F 0056; # (㍵; ㍵; ㍵; oV; oV; ) SQUARE OV +3376;3376;3376;0070 0063;0070 0063; # (㍶; ㍶; ㍶; pc; pc; ) SQUARE PC +337B;337B;337B;5E73 6210;5E73 6210; # (㍻; ㍻; ㍻; 平成; 平成; ) SQUARE ERA NAME HEISEI +337C;337C;337C;662D 548C;662D 548C; # (㍼; ㍼; ㍼; 昭和; 昭和; ) SQUARE ERA NAME SYOUWA +337D;337D;337D;5927 6B63;5927 6B63; # (㍽; ㍽; ㍽; 大正; 大正; ) SQUARE ERA NAME TAISYOU +337E;337E;337E;660E 6CBB;660E 6CBB; # (㍾; ㍾; ㍾; 明治; 明治; ) SQUARE ERA NAME MEIZI +337F;337F;337F;682A 5F0F 4F1A 793E;682A 5F0F 4F1A 793E; # (㍿; ㍿; ㍿; 株式会社; 株式会社; ) SQUARE CORPORATION +3380;3380;3380;0070 0041;0070 0041; # (㎀; ㎀; ㎀; pA; pA; ) SQUARE PA AMPS +3381;3381;3381;006E 0041;006E 0041; # (㎁; ㎁; ㎁; nA; nA; ) SQUARE NA +3382;3382;3382;03BC 0041;03BC 0041; # (㎂; ㎂; ㎂; μA; μA; ) SQUARE MU A +3383;3383;3383;006D 0041;006D 0041; # (㎃; ㎃; ㎃; mA; mA; ) SQUARE MA +3384;3384;3384;006B 0041;006B 0041; # (㎄; ㎄; ㎄; kA; kA; ) SQUARE KA +3385;3385;3385;004B 0042;004B 0042; # (㎅; ㎅; ㎅; KB; KB; ) SQUARE KB +3386;3386;3386;004D 0042;004D 0042; # (㎆; ㎆; ㎆; MB; MB; ) SQUARE MB +3387;3387;3387;0047 0042;0047 0042; # (㎇; ㎇; ㎇; GB; GB; ) SQUARE GB +3388;3388;3388;0063 0061 006C;0063 0061 006C; # (㎈; ㎈; ㎈; cal; cal; ) SQUARE CAL +3389;3389;3389;006B 0063 0061 006C;006B 0063 0061 006C; # (㎉; ㎉; ㎉; kcal; kcal; ) SQUARE KCAL +338A;338A;338A;0070 0046;0070 0046; # (㎊; ㎊; ㎊; pF; pF; ) SQUARE PF +338B;338B;338B;006E 0046;006E 0046; # (㎋; ㎋; ㎋; nF; nF; ) SQUARE NF +338C;338C;338C;03BC 0046;03BC 0046; # (㎌; ㎌; ㎌; μF; μF; ) SQUARE MU F +338D;338D;338D;03BC 0067;03BC 0067; # (㎍; ㎍; ㎍; μg; μg; ) SQUARE MU G +338E;338E;338E;006D 0067;006D 0067; # (㎎; ㎎; ㎎; mg; mg; ) SQUARE MG +338F;338F;338F;006B 0067;006B 0067; # (㎏; ㎏; ㎏; kg; kg; ) SQUARE KG +3390;3390;3390;0048 007A;0048 007A; # (㎐; ㎐; ㎐; Hz; Hz; ) SQUARE HZ +3391;3391;3391;006B 0048 007A;006B 0048 007A; # (㎑; ㎑; ㎑; kHz; kHz; ) SQUARE KHZ +3392;3392;3392;004D 0048 007A;004D 0048 007A; # (㎒; ㎒; ㎒; MHz; MHz; ) SQUARE MHZ +3393;3393;3393;0047 0048 007A;0047 0048 007A; # (㎓; ㎓; ㎓; GHz; GHz; ) SQUARE GHZ +3394;3394;3394;0054 0048 007A;0054 0048 007A; # (㎔; ㎔; ㎔; THz; THz; ) SQUARE THZ +3395;3395;3395;03BC 006C;03BC 006C; # (㎕; ㎕; ㎕; μl; μl; ) SQUARE MU L +3396;3396;3396;006D 006C;006D 006C; # (㎖; ㎖; ㎖; ml; ml; ) SQUARE ML +3397;3397;3397;0064 006C;0064 006C; # (㎗; ㎗; ㎗; dl; dl; ) SQUARE DL +3398;3398;3398;006B 006C;006B 006C; # (㎘; ㎘; ㎘; kl; kl; ) SQUARE KL +3399;3399;3399;0066 006D;0066 006D; # (㎙; ㎙; ㎙; fm; fm; ) SQUARE FM +339A;339A;339A;006E 006D;006E 006D; # (㎚; ㎚; ㎚; nm; nm; ) SQUARE NM +339B;339B;339B;03BC 006D;03BC 006D; # (㎛; ㎛; ㎛; μm; μm; ) SQUARE MU M +339C;339C;339C;006D 006D;006D 006D; # (㎜; ㎜; ㎜; mm; mm; ) SQUARE MM +339D;339D;339D;0063 006D;0063 006D; # (㎝; ㎝; ㎝; cm; cm; ) SQUARE CM +339E;339E;339E;006B 006D;006B 006D; # (㎞; ㎞; ㎞; km; km; ) SQUARE KM +339F;339F;339F;006D 006D 0032;006D 006D 0032; # (㎟; ㎟; ㎟; mm2; mm2; ) SQUARE MM SQUARED +33A0;33A0;33A0;0063 006D 0032;0063 006D 0032; # (㎠; ㎠; ㎠; cm2; cm2; ) SQUARE CM SQUARED +33A1;33A1;33A1;006D 0032;006D 0032; # (㎡; ㎡; ㎡; m2; m2; ) SQUARE M SQUARED +33A2;33A2;33A2;006B 006D 0032;006B 006D 0032; # (㎢; ㎢; ㎢; km2; km2; ) SQUARE KM SQUARED +33A3;33A3;33A3;006D 006D 0033;006D 006D 0033; # (㎣; ㎣; ㎣; mm3; mm3; ) SQUARE MM CUBED +33A4;33A4;33A4;0063 006D 0033;0063 006D 0033; # (㎤; ㎤; ㎤; cm3; cm3; ) SQUARE CM CUBED +33A5;33A5;33A5;006D 0033;006D 0033; # (㎥; ㎥; ㎥; m3; m3; ) SQUARE M CUBED +33A6;33A6;33A6;006B 006D 0033;006B 006D 0033; # (㎦; ㎦; ㎦; km3; km3; ) SQUARE KM CUBED +33A7;33A7;33A7;006D 2215 0073;006D 2215 0073; # (㎧; ㎧; ㎧; m∕s; m∕s; ) SQUARE M OVER S +33A8;33A8;33A8;006D 2215 0073 0032;006D 2215 0073 0032; # (㎨; ㎨; ㎨; m∕s2; m∕s2; ) SQUARE M OVER S SQUARED +33A9;33A9;33A9;0050 0061;0050 0061; # (㎩; ㎩; ㎩; Pa; Pa; ) SQUARE PA +33AA;33AA;33AA;006B 0050 0061;006B 0050 0061; # (㎪; ㎪; ㎪; kPa; kPa; ) SQUARE KPA +33AB;33AB;33AB;004D 0050 0061;004D 0050 0061; # (㎫; ㎫; ㎫; MPa; MPa; ) SQUARE MPA +33AC;33AC;33AC;0047 0050 0061;0047 0050 0061; # (㎬; ㎬; ㎬; GPa; GPa; ) SQUARE GPA +33AD;33AD;33AD;0072 0061 0064;0072 0061 0064; # (㎭; ㎭; ㎭; rad; rad; ) SQUARE RAD +33AE;33AE;33AE;0072 0061 0064 2215 0073;0072 0061 0064 2215 0073; # (㎮; ㎮; ㎮; rad∕s; rad∕s; ) SQUARE RAD OVER S +33AF;33AF;33AF;0072 0061 0064 2215 0073 0032;0072 0061 0064 2215 0073 0032; # (㎯; ㎯; ㎯; rad∕s2; rad∕s2; ) SQUARE RAD OVER S SQUARED +33B0;33B0;33B0;0070 0073;0070 0073; # (㎰; ㎰; ㎰; ps; ps; ) SQUARE PS +33B1;33B1;33B1;006E 0073;006E 0073; # (㎱; ㎱; ㎱; ns; ns; ) SQUARE NS +33B2;33B2;33B2;03BC 0073;03BC 0073; # (㎲; ㎲; ㎲; μs; μs; ) SQUARE MU S +33B3;33B3;33B3;006D 0073;006D 0073; # (㎳; ㎳; ㎳; ms; ms; ) SQUARE MS +33B4;33B4;33B4;0070 0056;0070 0056; # (㎴; ㎴; ㎴; pV; pV; ) SQUARE PV +33B5;33B5;33B5;006E 0056;006E 0056; # (㎵; ㎵; ㎵; nV; nV; ) SQUARE NV +33B6;33B6;33B6;03BC 0056;03BC 0056; # (㎶; ㎶; ㎶; μV; μV; ) SQUARE MU V +33B7;33B7;33B7;006D 0056;006D 0056; # (㎷; ㎷; ㎷; mV; mV; ) SQUARE MV +33B8;33B8;33B8;006B 0056;006B 0056; # (㎸; ㎸; ㎸; kV; kV; ) SQUARE KV +33B9;33B9;33B9;004D 0056;004D 0056; # (㎹; ㎹; ㎹; MV; MV; ) SQUARE MV MEGA +33BA;33BA;33BA;0070 0057;0070 0057; # (㎺; ㎺; ㎺; pW; pW; ) SQUARE PW +33BB;33BB;33BB;006E 0057;006E 0057; # (㎻; ㎻; ㎻; nW; nW; ) SQUARE NW +33BC;33BC;33BC;03BC 0057;03BC 0057; # (㎼; ㎼; ㎼; μW; μW; ) SQUARE MU W +33BD;33BD;33BD;006D 0057;006D 0057; # (㎽; ㎽; ㎽; mW; mW; ) SQUARE MW +33BE;33BE;33BE;006B 0057;006B 0057; # (㎾; ㎾; ㎾; kW; kW; ) SQUARE KW +33BF;33BF;33BF;004D 0057;004D 0057; # (㎿; ㎿; ㎿; MW; MW; ) SQUARE MW MEGA +33C0;33C0;33C0;006B 03A9;006B 03A9; # (㏀; ㏀; ㏀; kΩ; kΩ; ) SQUARE K OHM +33C1;33C1;33C1;004D 03A9;004D 03A9; # (㏁; ㏁; ㏁; MΩ; MΩ; ) SQUARE M OHM +33C2;33C2;33C2;0061 002E 006D 002E;0061 002E 006D 002E; # (㏂; ㏂; ㏂; a.m.; a.m.; ) SQUARE AM +33C3;33C3;33C3;0042 0071;0042 0071; # (㏃; ㏃; ㏃; Bq; Bq; ) SQUARE BQ +33C4;33C4;33C4;0063 0063;0063 0063; # (㏄; ㏄; ㏄; cc; cc; ) SQUARE CC +33C5;33C5;33C5;0063 0064;0063 0064; # (㏅; ㏅; ㏅; cd; cd; ) SQUARE CD +33C6;33C6;33C6;0043 2215 006B 0067;0043 2215 006B 0067; # (㏆; ㏆; ㏆; C∕kg; C∕kg; ) SQUARE C OVER KG +33C7;33C7;33C7;0043 006F 002E;0043 006F 002E; # (㏇; ㏇; ㏇; Co.; Co.; ) SQUARE CO +33C8;33C8;33C8;0064 0042;0064 0042; # (㏈; ㏈; ㏈; dB; dB; ) SQUARE DB +33C9;33C9;33C9;0047 0079;0047 0079; # (㏉; ㏉; ㏉; Gy; Gy; ) SQUARE GY +33CA;33CA;33CA;0068 0061;0068 0061; # (㏊; ㏊; ㏊; ha; ha; ) SQUARE HA +33CB;33CB;33CB;0048 0050;0048 0050; # (㏋; ㏋; ㏋; HP; HP; ) SQUARE HP +33CC;33CC;33CC;0069 006E;0069 006E; # (㏌; ㏌; ㏌; in; in; ) SQUARE IN +33CD;33CD;33CD;004B 004B;004B 004B; # (㏍; ㏍; ㏍; KK; KK; ) SQUARE KK +33CE;33CE;33CE;004B 004D;004B 004D; # (㏎; ㏎; ㏎; KM; KM; ) SQUARE KM CAPITAL +33CF;33CF;33CF;006B 0074;006B 0074; # (㏏; ㏏; ㏏; kt; kt; ) SQUARE KT +33D0;33D0;33D0;006C 006D;006C 006D; # (㏐; ㏐; ㏐; lm; lm; ) SQUARE LM +33D1;33D1;33D1;006C 006E;006C 006E; # (㏑; ㏑; ㏑; ln; ln; ) SQUARE LN +33D2;33D2;33D2;006C 006F 0067;006C 006F 0067; # (㏒; ㏒; ㏒; log; log; ) SQUARE LOG +33D3;33D3;33D3;006C 0078;006C 0078; # (㏓; ㏓; ㏓; lx; lx; ) SQUARE LX +33D4;33D4;33D4;006D 0062;006D 0062; # (㏔; ㏔; ㏔; mb; mb; ) SQUARE MB SMALL +33D5;33D5;33D5;006D 0069 006C;006D 0069 006C; # (㏕; ㏕; ㏕; mil; mil; ) SQUARE MIL +33D6;33D6;33D6;006D 006F 006C;006D 006F 006C; # (㏖; ㏖; ㏖; mol; mol; ) SQUARE MOL +33D7;33D7;33D7;0050 0048;0050 0048; # (㏗; ㏗; ㏗; PH; PH; ) SQUARE PH +33D8;33D8;33D8;0070 002E 006D 002E;0070 002E 006D 002E; # (㏘; ㏘; ㏘; p.m.; p.m.; ) SQUARE PM +33D9;33D9;33D9;0050 0050 004D;0050 0050 004D; # (㏙; ㏙; ㏙; PPM; PPM; ) SQUARE PPM +33DA;33DA;33DA;0050 0052;0050 0052; # (㏚; ㏚; ㏚; PR; PR; ) SQUARE PR +33DB;33DB;33DB;0073 0072;0073 0072; # (㏛; ㏛; ㏛; sr; sr; ) SQUARE SR +33DC;33DC;33DC;0053 0076;0053 0076; # (㏜; ㏜; ㏜; Sv; Sv; ) SQUARE SV +33DD;33DD;33DD;0057 0062;0057 0062; # (㏝; ㏝; ㏝; Wb; Wb; ) SQUARE WB +33E0;33E0;33E0;0031 65E5;0031 65E5; # (㏠; ㏠; ㏠; 1日; 1日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE +33E1;33E1;33E1;0032 65E5;0032 65E5; # (㏡; ㏡; ㏡; 2日; 2日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO +33E2;33E2;33E2;0033 65E5;0033 65E5; # (㏢; ㏢; ㏢; 3日; 3日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE +33E3;33E3;33E3;0034 65E5;0034 65E5; # (㏣; ㏣; ㏣; 4日; 4日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR +33E4;33E4;33E4;0035 65E5;0035 65E5; # (㏤; ㏤; ㏤; 5日; 5日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE +33E5;33E5;33E5;0036 65E5;0036 65E5; # (㏥; ㏥; ㏥; 6日; 6日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX +33E6;33E6;33E6;0037 65E5;0037 65E5; # (㏦; ㏦; ㏦; 7日; 7日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN +33E7;33E7;33E7;0038 65E5;0038 65E5; # (㏧; ㏧; ㏧; 8日; 8日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT +33E8;33E8;33E8;0039 65E5;0039 65E5; # (㏨; ㏨; ㏨; 9日; 9日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE +33E9;33E9;33E9;0031 0030 65E5;0031 0030 65E5; # (㏩; ㏩; ㏩; 10日; 10日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN +33EA;33EA;33EA;0031 0031 65E5;0031 0031 65E5; # (㏪; ㏪; ㏪; 11日; 11日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN +33EB;33EB;33EB;0031 0032 65E5;0031 0032 65E5; # (㏫; ㏫; ㏫; 12日; 12日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE +33EC;33EC;33EC;0031 0033 65E5;0031 0033 65E5; # (㏬; ㏬; ㏬; 13日; 13日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN +33ED;33ED;33ED;0031 0034 65E5;0031 0034 65E5; # (㏭; ㏭; ㏭; 14日; 14日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN +33EE;33EE;33EE;0031 0035 65E5;0031 0035 65E5; # (㏮; ㏮; ㏮; 15日; 15日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN +33EF;33EF;33EF;0031 0036 65E5;0031 0036 65E5; # (㏯; ㏯; ㏯; 16日; 16日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN +33F0;33F0;33F0;0031 0037 65E5;0031 0037 65E5; # (㏰; ㏰; ㏰; 17日; 17日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN +33F1;33F1;33F1;0031 0038 65E5;0031 0038 65E5; # (㏱; ㏱; ㏱; 18日; 18日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN +33F2;33F2;33F2;0031 0039 65E5;0031 0039 65E5; # (㏲; ㏲; ㏲; 19日; 19日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN +33F3;33F3;33F3;0032 0030 65E5;0032 0030 65E5; # (㏳; ㏳; ㏳; 20日; 20日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY +33F4;33F4;33F4;0032 0031 65E5;0032 0031 65E5; # (㏴; ㏴; ㏴; 21日; 21日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE +33F5;33F5;33F5;0032 0032 65E5;0032 0032 65E5; # (㏵; ㏵; ㏵; 22日; 22日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO +33F6;33F6;33F6;0032 0033 65E5;0032 0033 65E5; # (㏶; ㏶; ㏶; 23日; 23日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE +33F7;33F7;33F7;0032 0034 65E5;0032 0034 65E5; # (㏷; ㏷; ㏷; 24日; 24日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR +33F8;33F8;33F8;0032 0035 65E5;0032 0035 65E5; # (㏸; ㏸; ㏸; 25日; 25日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE +33F9;33F9;33F9;0032 0036 65E5;0032 0036 65E5; # (㏹; ㏹; ㏹; 26日; 26日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX +33FA;33FA;33FA;0032 0037 65E5;0032 0037 65E5; # (㏺; ㏺; ㏺; 27日; 27日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN +33FB;33FB;33FB;0032 0038 65E5;0032 0038 65E5; # (㏻; ㏻; ㏻; 28日; 28日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT +33FC;33FC;33FC;0032 0039 65E5;0032 0039 65E5; # (㏼; ㏼; ㏼; 29日; 29日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE +33FD;33FD;33FD;0033 0030 65E5;0033 0030 65E5; # (㏽; ㏽; ㏽; 30日; 30日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY +33FE;33FE;33FE;0033 0031 65E5;0033 0031 65E5; # (㏾; ㏾; ㏾; 31日; 31日; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE +AC00;AC00;1100 1161;AC00;1100 1161; # (가; 가; 가; 가; 가; ) HANGUL SYLLABLE GA +AC01;AC01;1100 1161 11A8;AC01;1100 1161 11A8; # (각; 각; 각; 각; 각; ) HANGUL SYLLABLE GAG +AC02;AC02;1100 1161 11A9;AC02;1100 1161 11A9; # (갂; 갂; 갂; 갂; 갂; ) HANGUL SYLLABLE GAGG +AC03;AC03;1100 1161 11AA;AC03;1100 1161 11AA; # (갃; 갃; 갃; 갃; 갃; ) HANGUL SYLLABLE GAGS +AC04;AC04;1100 1161 11AB;AC04;1100 1161 11AB; # (간; 간; 간; 간; 간; ) HANGUL SYLLABLE GAN +AC05;AC05;1100 1161 11AC;AC05;1100 1161 11AC; # (갅; 갅; 갅; 갅; 갅; ) HANGUL SYLLABLE GANJ +AC06;AC06;1100 1161 11AD;AC06;1100 1161 11AD; # (갆; 갆; 갆; 갆; 갆; ) HANGUL SYLLABLE GANH +AC07;AC07;1100 1161 11AE;AC07;1100 1161 11AE; # (갇; 갇; 갇; 갇; 갇; ) HANGUL SYLLABLE GAD +AC08;AC08;1100 1161 11AF;AC08;1100 1161 11AF; # (갈; 갈; 갈; 갈; 갈; ) HANGUL SYLLABLE GAL +AC09;AC09;1100 1161 11B0;AC09;1100 1161 11B0; # (갉; 갉; 갉; 갉; 갉; ) HANGUL SYLLABLE GALG +AC0A;AC0A;1100 1161 11B1;AC0A;1100 1161 11B1; # (갊; 갊; 갊; 갊; 갊; ) HANGUL SYLLABLE GALM +AC0B;AC0B;1100 1161 11B2;AC0B;1100 1161 11B2; # (갋; 갋; 갋; 갋; 갋; ) HANGUL SYLLABLE GALB +AC0C;AC0C;1100 1161 11B3;AC0C;1100 1161 11B3; # (갌; 갌; 갌; 갌; 갌; ) HANGUL SYLLABLE GALS +AC0D;AC0D;1100 1161 11B4;AC0D;1100 1161 11B4; # (갍; 갍; 갍; 갍; 갍; ) HANGUL SYLLABLE GALT +AC0E;AC0E;1100 1161 11B5;AC0E;1100 1161 11B5; # (갎; 갎; 갎; 갎; 갎; ) HANGUL SYLLABLE GALP +AC0F;AC0F;1100 1161 11B6;AC0F;1100 1161 11B6; # (갏; 갏; 갏; 갏; 갏; ) HANGUL SYLLABLE GALH +AC10;AC10;1100 1161 11B7;AC10;1100 1161 11B7; # (감; 감; 감; 감; 감; ) HANGUL SYLLABLE GAM +AC11;AC11;1100 1161 11B8;AC11;1100 1161 11B8; # (갑; 갑; 갑; 갑; 갑; ) HANGUL SYLLABLE GAB +AC12;AC12;1100 1161 11B9;AC12;1100 1161 11B9; # (값; 값; 값; 값; 값; ) HANGUL SYLLABLE GABS +AC13;AC13;1100 1161 11BA;AC13;1100 1161 11BA; # (갓; 갓; 갓; 갓; 갓; ) HANGUL SYLLABLE GAS +AC14;AC14;1100 1161 11BB;AC14;1100 1161 11BB; # (갔; 갔; 갔; 갔; 갔; ) HANGUL SYLLABLE GASS +AC15;AC15;1100 1161 11BC;AC15;1100 1161 11BC; # (강; 강; 강; 강; 강; ) HANGUL SYLLABLE GANG +AC16;AC16;1100 1161 11BD;AC16;1100 1161 11BD; # (갖; 갖; 갖; 갖; 갖; ) HANGUL SYLLABLE GAJ +AC17;AC17;1100 1161 11BE;AC17;1100 1161 11BE; # (갗; 갗; 갗; 갗; 갗; ) HANGUL SYLLABLE GAC +AC18;AC18;1100 1161 11BF;AC18;1100 1161 11BF; # (갘; 갘; 갘; 갘; 갘; ) HANGUL SYLLABLE GAK +AC19;AC19;1100 1161 11C0;AC19;1100 1161 11C0; # (같; 같; 같; 같; 같; ) HANGUL SYLLABLE GAT +AC1A;AC1A;1100 1161 11C1;AC1A;1100 1161 11C1; # (갚; 갚; 갚; 갚; 갚; ) HANGUL SYLLABLE GAP +AC1B;AC1B;1100 1161 11C2;AC1B;1100 1161 11C2; # (갛; 갛; 갛; 갛; 갛; ) HANGUL SYLLABLE GAH +AC1C;AC1C;1100 1162;AC1C;1100 1162; # (개; 개; 개; 개; 개; ) HANGUL SYLLABLE GAE +AC1D;AC1D;1100 1162 11A8;AC1D;1100 1162 11A8; # (객; 객; 객; 객; 객; ) HANGUL SYLLABLE GAEG +AC1E;AC1E;1100 1162 11A9;AC1E;1100 1162 11A9; # (갞; 갞; 갞; 갞; 갞; ) HANGUL SYLLABLE GAEGG +AC1F;AC1F;1100 1162 11AA;AC1F;1100 1162 11AA; # (갟; 갟; 갟; 갟; 갟; ) HANGUL SYLLABLE GAEGS +AC20;AC20;1100 1162 11AB;AC20;1100 1162 11AB; # (갠; 갠; 갠; 갠; 갠; ) HANGUL SYLLABLE GAEN +AC21;AC21;1100 1162 11AC;AC21;1100 1162 11AC; # (갡; 갡; 갡; 갡; 갡; ) HANGUL SYLLABLE GAENJ +AC22;AC22;1100 1162 11AD;AC22;1100 1162 11AD; # (갢; 갢; 갢; 갢; 갢; ) HANGUL SYLLABLE GAENH +AC23;AC23;1100 1162 11AE;AC23;1100 1162 11AE; # (갣; 갣; 갣; 갣; 갣; ) HANGUL SYLLABLE GAED +AC24;AC24;1100 1162 11AF;AC24;1100 1162 11AF; # (갤; 갤; 갤; 갤; 갤; ) HANGUL SYLLABLE GAEL +AC25;AC25;1100 1162 11B0;AC25;1100 1162 11B0; # (갥; 갥; 갥; 갥; 갥; ) HANGUL SYLLABLE GAELG +AC26;AC26;1100 1162 11B1;AC26;1100 1162 11B1; # (갦; 갦; 갦; 갦; 갦; ) HANGUL SYLLABLE GAELM +AC27;AC27;1100 1162 11B2;AC27;1100 1162 11B2; # (갧; 갧; 갧; 갧; 갧; ) HANGUL SYLLABLE GAELB +AC28;AC28;1100 1162 11B3;AC28;1100 1162 11B3; # (갨; 갨; 갨; 갨; 갨; ) HANGUL SYLLABLE GAELS +AC29;AC29;1100 1162 11B4;AC29;1100 1162 11B4; # (갩; 갩; 갩; 갩; 갩; ) HANGUL SYLLABLE GAELT +AC2A;AC2A;1100 1162 11B5;AC2A;1100 1162 11B5; # (갪; 갪; 갪; 갪; 갪; ) HANGUL SYLLABLE GAELP +AC2B;AC2B;1100 1162 11B6;AC2B;1100 1162 11B6; # (갫; 갫; 갫; 갫; 갫; ) HANGUL SYLLABLE GAELH +AC2C;AC2C;1100 1162 11B7;AC2C;1100 1162 11B7; # (갬; 갬; 갬; 갬; 갬; ) HANGUL SYLLABLE GAEM +AC2D;AC2D;1100 1162 11B8;AC2D;1100 1162 11B8; # (갭; 갭; 갭; 갭; 갭; ) HANGUL SYLLABLE GAEB +AC2E;AC2E;1100 1162 11B9;AC2E;1100 1162 11B9; # (갮; 갮; 갮; 갮; 갮; ) HANGUL SYLLABLE GAEBS +AC2F;AC2F;1100 1162 11BA;AC2F;1100 1162 11BA; # (갯; 갯; 갯; 갯; 갯; ) HANGUL SYLLABLE GAES +AC30;AC30;1100 1162 11BB;AC30;1100 1162 11BB; # (갰; 갰; 갰; 갰; 갰; ) HANGUL SYLLABLE GAESS +AC31;AC31;1100 1162 11BC;AC31;1100 1162 11BC; # (갱; 갱; 갱; 갱; 갱; ) HANGUL SYLLABLE GAENG +AC32;AC32;1100 1162 11BD;AC32;1100 1162 11BD; # (갲; 갲; 갲; 갲; 갲; ) HANGUL SYLLABLE GAEJ +AC33;AC33;1100 1162 11BE;AC33;1100 1162 11BE; # (갳; 갳; 갳; 갳; 갳; ) HANGUL SYLLABLE GAEC +AC34;AC34;1100 1162 11BF;AC34;1100 1162 11BF; # (갴; 갴; 갴; 갴; 갴; ) HANGUL SYLLABLE GAEK +AC35;AC35;1100 1162 11C0;AC35;1100 1162 11C0; # (갵; 갵; 갵; 갵; 갵; ) HANGUL SYLLABLE GAET +AC36;AC36;1100 1162 11C1;AC36;1100 1162 11C1; # (갶; 갶; 갶; 갶; 갶; ) HANGUL SYLLABLE GAEP +AC37;AC37;1100 1162 11C2;AC37;1100 1162 11C2; # (갷; 갷; 갷; 갷; 갷; ) HANGUL SYLLABLE GAEH +AC38;AC38;1100 1163;AC38;1100 1163; # (갸; 갸; 갸; 갸; 갸; ) HANGUL SYLLABLE GYA +AC39;AC39;1100 1163 11A8;AC39;1100 1163 11A8; # (갹; 갹; 갹; 갹; 갹; ) HANGUL SYLLABLE GYAG +AC3A;AC3A;1100 1163 11A9;AC3A;1100 1163 11A9; # (갺; 갺; 갺; 갺; 갺; ) HANGUL SYLLABLE GYAGG +AC3B;AC3B;1100 1163 11AA;AC3B;1100 1163 11AA; # (갻; 갻; 갻; 갻; 갻; ) HANGUL SYLLABLE GYAGS +AC3C;AC3C;1100 1163 11AB;AC3C;1100 1163 11AB; # (갼; 갼; 갼; 갼; 갼; ) HANGUL SYLLABLE GYAN +AC3D;AC3D;1100 1163 11AC;AC3D;1100 1163 11AC; # (갽; 갽; 갽; 갽; 갽; ) HANGUL SYLLABLE GYANJ +AC3E;AC3E;1100 1163 11AD;AC3E;1100 1163 11AD; # (갾; 갾; 갾; 갾; 갾; ) HANGUL SYLLABLE GYANH +AC3F;AC3F;1100 1163 11AE;AC3F;1100 1163 11AE; # (갿; 갿; 갿; 갿; 갿; ) HANGUL SYLLABLE GYAD +AC40;AC40;1100 1163 11AF;AC40;1100 1163 11AF; # (걀; 걀; 걀; 걀; 걀; ) HANGUL SYLLABLE GYAL +AC41;AC41;1100 1163 11B0;AC41;1100 1163 11B0; # (걁; 걁; 걁; 걁; 걁; ) HANGUL SYLLABLE GYALG +AC42;AC42;1100 1163 11B1;AC42;1100 1163 11B1; # (걂; 걂; 걂; 걂; 걂; ) HANGUL SYLLABLE GYALM +AC43;AC43;1100 1163 11B2;AC43;1100 1163 11B2; # (걃; 걃; 걃; 걃; 걃; ) HANGUL SYLLABLE GYALB +AC44;AC44;1100 1163 11B3;AC44;1100 1163 11B3; # (걄; 걄; 걄; 걄; 걄; ) HANGUL SYLLABLE GYALS +AC45;AC45;1100 1163 11B4;AC45;1100 1163 11B4; # (걅; 걅; 걅; 걅; 걅; ) HANGUL SYLLABLE GYALT +AC46;AC46;1100 1163 11B5;AC46;1100 1163 11B5; # (걆; 걆; 걆; 걆; 걆; ) HANGUL SYLLABLE GYALP +AC47;AC47;1100 1163 11B6;AC47;1100 1163 11B6; # (걇; 걇; 걇; 걇; 걇; ) HANGUL SYLLABLE GYALH +AC48;AC48;1100 1163 11B7;AC48;1100 1163 11B7; # (걈; 걈; 걈; 걈; 걈; ) HANGUL SYLLABLE GYAM +AC49;AC49;1100 1163 11B8;AC49;1100 1163 11B8; # (걉; 걉; 걉; 걉; 걉; ) HANGUL SYLLABLE GYAB +AC4A;AC4A;1100 1163 11B9;AC4A;1100 1163 11B9; # (걊; 걊; 걊; 걊; 걊; ) HANGUL SYLLABLE GYABS +AC4B;AC4B;1100 1163 11BA;AC4B;1100 1163 11BA; # (걋; 걋; 걋; 걋; 걋; ) HANGUL SYLLABLE GYAS +AC4C;AC4C;1100 1163 11BB;AC4C;1100 1163 11BB; # (걌; 걌; 걌; 걌; 걌; ) HANGUL SYLLABLE GYASS +AC4D;AC4D;1100 1163 11BC;AC4D;1100 1163 11BC; # (걍; 걍; 걍; 걍; 걍; ) HANGUL SYLLABLE GYANG +AC4E;AC4E;1100 1163 11BD;AC4E;1100 1163 11BD; # (걎; 걎; 걎; 걎; 걎; ) HANGUL SYLLABLE GYAJ +AC4F;AC4F;1100 1163 11BE;AC4F;1100 1163 11BE; # (걏; 걏; 걏; 걏; 걏; ) HANGUL SYLLABLE GYAC +AC50;AC50;1100 1163 11BF;AC50;1100 1163 11BF; # (걐; 걐; 걐; 걐; 걐; ) HANGUL SYLLABLE GYAK +AC51;AC51;1100 1163 11C0;AC51;1100 1163 11C0; # (걑; 걑; 걑; 걑; 걑; ) HANGUL SYLLABLE GYAT +AC52;AC52;1100 1163 11C1;AC52;1100 1163 11C1; # (걒; 걒; 걒; 걒; 걒; ) HANGUL SYLLABLE GYAP +AC53;AC53;1100 1163 11C2;AC53;1100 1163 11C2; # (걓; 걓; 걓; 걓; 걓; ) HANGUL SYLLABLE GYAH +AC54;AC54;1100 1164;AC54;1100 1164; # (걔; 걔; 걔; 걔; 걔; ) HANGUL SYLLABLE GYAE +AC55;AC55;1100 1164 11A8;AC55;1100 1164 11A8; # (걕; 걕; 걕; 걕; 걕; ) HANGUL SYLLABLE GYAEG +AC56;AC56;1100 1164 11A9;AC56;1100 1164 11A9; # (걖; 걖; 걖; 걖; 걖; ) HANGUL SYLLABLE GYAEGG +AC57;AC57;1100 1164 11AA;AC57;1100 1164 11AA; # (걗; 걗; 걗; 걗; 걗; ) HANGUL SYLLABLE GYAEGS +AC58;AC58;1100 1164 11AB;AC58;1100 1164 11AB; # (걘; 걘; 걘; 걘; 걘; ) HANGUL SYLLABLE GYAEN +AC59;AC59;1100 1164 11AC;AC59;1100 1164 11AC; # (걙; 걙; 걙; 걙; 걙; ) HANGUL SYLLABLE GYAENJ +AC5A;AC5A;1100 1164 11AD;AC5A;1100 1164 11AD; # (걚; 걚; 걚; 걚; 걚; ) HANGUL SYLLABLE GYAENH +AC5B;AC5B;1100 1164 11AE;AC5B;1100 1164 11AE; # (걛; 걛; 걛; 걛; 걛; ) HANGUL SYLLABLE GYAED +AC5C;AC5C;1100 1164 11AF;AC5C;1100 1164 11AF; # (걜; 걜; 걜; 걜; 걜; ) HANGUL SYLLABLE GYAEL +AC5D;AC5D;1100 1164 11B0;AC5D;1100 1164 11B0; # (걝; 걝; 걝; 걝; 걝; ) HANGUL SYLLABLE GYAELG +AC5E;AC5E;1100 1164 11B1;AC5E;1100 1164 11B1; # (걞; 걞; 걞; 걞; 걞; ) HANGUL SYLLABLE GYAELM +AC5F;AC5F;1100 1164 11B2;AC5F;1100 1164 11B2; # (걟; 걟; 걟; 걟; 걟; ) HANGUL SYLLABLE GYAELB +AC60;AC60;1100 1164 11B3;AC60;1100 1164 11B3; # (걠; 걠; 걠; 걠; 걠; ) HANGUL SYLLABLE GYAELS +AC61;AC61;1100 1164 11B4;AC61;1100 1164 11B4; # (걡; 걡; 걡; 걡; 걡; ) HANGUL SYLLABLE GYAELT +AC62;AC62;1100 1164 11B5;AC62;1100 1164 11B5; # (걢; 걢; 걢; 걢; 걢; ) HANGUL SYLLABLE GYAELP +AC63;AC63;1100 1164 11B6;AC63;1100 1164 11B6; # (걣; 걣; 걣; 걣; 걣; ) HANGUL SYLLABLE GYAELH +AC64;AC64;1100 1164 11B7;AC64;1100 1164 11B7; # (걤; 걤; 걤; 걤; 걤; ) HANGUL SYLLABLE GYAEM +AC65;AC65;1100 1164 11B8;AC65;1100 1164 11B8; # (걥; 걥; 걥; 걥; 걥; ) HANGUL SYLLABLE GYAEB +AC66;AC66;1100 1164 11B9;AC66;1100 1164 11B9; # (걦; 걦; 걦; 걦; 걦; ) HANGUL SYLLABLE GYAEBS +AC67;AC67;1100 1164 11BA;AC67;1100 1164 11BA; # (걧; 걧; 걧; 걧; 걧; ) HANGUL SYLLABLE GYAES +AC68;AC68;1100 1164 11BB;AC68;1100 1164 11BB; # (걨; 걨; 걨; 걨; 걨; ) HANGUL SYLLABLE GYAESS +AC69;AC69;1100 1164 11BC;AC69;1100 1164 11BC; # (걩; 걩; 걩; 걩; 걩; ) HANGUL SYLLABLE GYAENG +AC6A;AC6A;1100 1164 11BD;AC6A;1100 1164 11BD; # (걪; 걪; 걪; 걪; 걪; ) HANGUL SYLLABLE GYAEJ +AC6B;AC6B;1100 1164 11BE;AC6B;1100 1164 11BE; # (걫; 걫; 걫; 걫; 걫; ) HANGUL SYLLABLE GYAEC +AC6C;AC6C;1100 1164 11BF;AC6C;1100 1164 11BF; # (걬; 걬; 걬; 걬; 걬; ) HANGUL SYLLABLE GYAEK +AC6D;AC6D;1100 1164 11C0;AC6D;1100 1164 11C0; # (걭; 걭; 걭; 걭; 걭; ) HANGUL SYLLABLE GYAET +AC6E;AC6E;1100 1164 11C1;AC6E;1100 1164 11C1; # (걮; 걮; 걮; 걮; 걮; ) HANGUL SYLLABLE GYAEP +AC6F;AC6F;1100 1164 11C2;AC6F;1100 1164 11C2; # (걯; 걯; 걯; 걯; 걯; ) HANGUL SYLLABLE GYAEH +AC70;AC70;1100 1165;AC70;1100 1165; # (거; 거; 거; 거; 거; ) HANGUL SYLLABLE GEO +AC71;AC71;1100 1165 11A8;AC71;1100 1165 11A8; # (걱; 걱; 걱; 걱; 걱; ) HANGUL SYLLABLE GEOG +AC72;AC72;1100 1165 11A9;AC72;1100 1165 11A9; # (걲; 걲; 걲; 걲; 걲; ) HANGUL SYLLABLE GEOGG +AC73;AC73;1100 1165 11AA;AC73;1100 1165 11AA; # (걳; 걳; 걳; 걳; 걳; ) HANGUL SYLLABLE GEOGS +AC74;AC74;1100 1165 11AB;AC74;1100 1165 11AB; # (건; 건; 건; 건; 건; ) HANGUL SYLLABLE GEON +AC75;AC75;1100 1165 11AC;AC75;1100 1165 11AC; # (걵; 걵; 걵; 걵; 걵; ) HANGUL SYLLABLE GEONJ +AC76;AC76;1100 1165 11AD;AC76;1100 1165 11AD; # (걶; 걶; 걶; 걶; 걶; ) HANGUL SYLLABLE GEONH +AC77;AC77;1100 1165 11AE;AC77;1100 1165 11AE; # (걷; 걷; 걷; 걷; 걷; ) HANGUL SYLLABLE GEOD +AC78;AC78;1100 1165 11AF;AC78;1100 1165 11AF; # (걸; 걸; 걸; 걸; 걸; ) HANGUL SYLLABLE GEOL +AC79;AC79;1100 1165 11B0;AC79;1100 1165 11B0; # (걹; 걹; 걹; 걹; 걹; ) HANGUL SYLLABLE GEOLG +AC7A;AC7A;1100 1165 11B1;AC7A;1100 1165 11B1; # (걺; 걺; 걺; 걺; 걺; ) HANGUL SYLLABLE GEOLM +AC7B;AC7B;1100 1165 11B2;AC7B;1100 1165 11B2; # (걻; 걻; 걻; 걻; 걻; ) HANGUL SYLLABLE GEOLB +AC7C;AC7C;1100 1165 11B3;AC7C;1100 1165 11B3; # (걼; 걼; 걼; 걼; 걼; ) HANGUL SYLLABLE GEOLS +AC7D;AC7D;1100 1165 11B4;AC7D;1100 1165 11B4; # (걽; 걽; 걽; 걽; 걽; ) HANGUL SYLLABLE GEOLT +AC7E;AC7E;1100 1165 11B5;AC7E;1100 1165 11B5; # (걾; 걾; 걾; 걾; 걾; ) HANGUL SYLLABLE GEOLP +AC7F;AC7F;1100 1165 11B6;AC7F;1100 1165 11B6; # (걿; 걿; 걿; 걿; 걿; ) HANGUL SYLLABLE GEOLH +AC80;AC80;1100 1165 11B7;AC80;1100 1165 11B7; # (검; 검; 검; 검; 검; ) HANGUL SYLLABLE GEOM +AC81;AC81;1100 1165 11B8;AC81;1100 1165 11B8; # (겁; 겁; 겁; 겁; 겁; ) HANGUL SYLLABLE GEOB +AC82;AC82;1100 1165 11B9;AC82;1100 1165 11B9; # (겂; 겂; 겂; 겂; 겂; ) HANGUL SYLLABLE GEOBS +AC83;AC83;1100 1165 11BA;AC83;1100 1165 11BA; # (것; 것; 것; 것; 것; ) HANGUL SYLLABLE GEOS +AC84;AC84;1100 1165 11BB;AC84;1100 1165 11BB; # (겄; 겄; 겄; 겄; 겄; ) HANGUL SYLLABLE GEOSS +AC85;AC85;1100 1165 11BC;AC85;1100 1165 11BC; # (겅; 겅; 겅; 겅; 겅; ) HANGUL SYLLABLE GEONG +AC86;AC86;1100 1165 11BD;AC86;1100 1165 11BD; # (겆; 겆; 겆; 겆; 겆; ) HANGUL SYLLABLE GEOJ +AC87;AC87;1100 1165 11BE;AC87;1100 1165 11BE; # (겇; 겇; 겇; 겇; 겇; ) HANGUL SYLLABLE GEOC +AC88;AC88;1100 1165 11BF;AC88;1100 1165 11BF; # (겈; 겈; 겈; 겈; 겈; ) HANGUL SYLLABLE GEOK +AC89;AC89;1100 1165 11C0;AC89;1100 1165 11C0; # (겉; 겉; 겉; 겉; 겉; ) HANGUL SYLLABLE GEOT +AC8A;AC8A;1100 1165 11C1;AC8A;1100 1165 11C1; # (겊; 겊; 겊; 겊; 겊; ) HANGUL SYLLABLE GEOP +AC8B;AC8B;1100 1165 11C2;AC8B;1100 1165 11C2; # (겋; 겋; 겋; 겋; 겋; ) HANGUL SYLLABLE GEOH +AC8C;AC8C;1100 1166;AC8C;1100 1166; # (게; 게; 게; 게; 게; ) HANGUL SYLLABLE GE +AC8D;AC8D;1100 1166 11A8;AC8D;1100 1166 11A8; # (겍; 겍; 겍; 겍; 겍; ) HANGUL SYLLABLE GEG +AC8E;AC8E;1100 1166 11A9;AC8E;1100 1166 11A9; # (겎; 겎; 겎; 겎; 겎; ) HANGUL SYLLABLE GEGG +AC8F;AC8F;1100 1166 11AA;AC8F;1100 1166 11AA; # (겏; 겏; 겏; 겏; 겏; ) HANGUL SYLLABLE GEGS +AC90;AC90;1100 1166 11AB;AC90;1100 1166 11AB; # (겐; 겐; 겐; 겐; 겐; ) HANGUL SYLLABLE GEN +AC91;AC91;1100 1166 11AC;AC91;1100 1166 11AC; # (겑; 겑; 겑; 겑; 겑; ) HANGUL SYLLABLE GENJ +AC92;AC92;1100 1166 11AD;AC92;1100 1166 11AD; # (겒; 겒; 겒; 겒; 겒; ) HANGUL SYLLABLE GENH +AC93;AC93;1100 1166 11AE;AC93;1100 1166 11AE; # (겓; 겓; 겓; 겓; 겓; ) HANGUL SYLLABLE GED +AC94;AC94;1100 1166 11AF;AC94;1100 1166 11AF; # (겔; 겔; 겔; 겔; 겔; ) HANGUL SYLLABLE GEL +AC95;AC95;1100 1166 11B0;AC95;1100 1166 11B0; # (겕; 겕; 겕; 겕; 겕; ) HANGUL SYLLABLE GELG +AC96;AC96;1100 1166 11B1;AC96;1100 1166 11B1; # (겖; 겖; 겖; 겖; 겖; ) HANGUL SYLLABLE GELM +AC97;AC97;1100 1166 11B2;AC97;1100 1166 11B2; # (겗; 겗; 겗; 겗; 겗; ) HANGUL SYLLABLE GELB +AC98;AC98;1100 1166 11B3;AC98;1100 1166 11B3; # (겘; 겘; 겘; 겘; 겘; ) HANGUL SYLLABLE GELS +AC99;AC99;1100 1166 11B4;AC99;1100 1166 11B4; # (겙; 겙; 겙; 겙; 겙; ) HANGUL SYLLABLE GELT +AC9A;AC9A;1100 1166 11B5;AC9A;1100 1166 11B5; # (겚; 겚; 겚; 겚; 겚; ) HANGUL SYLLABLE GELP +AC9B;AC9B;1100 1166 11B6;AC9B;1100 1166 11B6; # (겛; 겛; 겛; 겛; 겛; ) HANGUL SYLLABLE GELH +AC9C;AC9C;1100 1166 11B7;AC9C;1100 1166 11B7; # (겜; 겜; 겜; 겜; 겜; ) HANGUL SYLLABLE GEM +AC9D;AC9D;1100 1166 11B8;AC9D;1100 1166 11B8; # (겝; 겝; 겝; 겝; 겝; ) HANGUL SYLLABLE GEB +AC9E;AC9E;1100 1166 11B9;AC9E;1100 1166 11B9; # (겞; 겞; 겞; 겞; 겞; ) HANGUL SYLLABLE GEBS +AC9F;AC9F;1100 1166 11BA;AC9F;1100 1166 11BA; # (겟; 겟; 겟; 겟; 겟; ) HANGUL SYLLABLE GES +ACA0;ACA0;1100 1166 11BB;ACA0;1100 1166 11BB; # (겠; 겠; 겠; 겠; 겠; ) HANGUL SYLLABLE GESS +ACA1;ACA1;1100 1166 11BC;ACA1;1100 1166 11BC; # (겡; 겡; 겡; 겡; 겡; ) HANGUL SYLLABLE GENG +ACA2;ACA2;1100 1166 11BD;ACA2;1100 1166 11BD; # (겢; 겢; 겢; 겢; 겢; ) HANGUL SYLLABLE GEJ +ACA3;ACA3;1100 1166 11BE;ACA3;1100 1166 11BE; # (겣; 겣; 겣; 겣; 겣; ) HANGUL SYLLABLE GEC +ACA4;ACA4;1100 1166 11BF;ACA4;1100 1166 11BF; # (겤; 겤; 겤; 겤; 겤; ) HANGUL SYLLABLE GEK +ACA5;ACA5;1100 1166 11C0;ACA5;1100 1166 11C0; # (겥; 겥; 겥; 겥; 겥; ) HANGUL SYLLABLE GET +ACA6;ACA6;1100 1166 11C1;ACA6;1100 1166 11C1; # (겦; 겦; 겦; 겦; 겦; ) HANGUL SYLLABLE GEP +ACA7;ACA7;1100 1166 11C2;ACA7;1100 1166 11C2; # (겧; 겧; 겧; 겧; 겧; ) HANGUL SYLLABLE GEH +ACA8;ACA8;1100 1167;ACA8;1100 1167; # (겨; 겨; 겨; 겨; 겨; ) HANGUL SYLLABLE GYEO +ACA9;ACA9;1100 1167 11A8;ACA9;1100 1167 11A8; # (격; 격; 격; 격; 격; ) HANGUL SYLLABLE GYEOG +ACAA;ACAA;1100 1167 11A9;ACAA;1100 1167 11A9; # (겪; 겪; 겪; 겪; 겪; ) HANGUL SYLLABLE GYEOGG +ACAB;ACAB;1100 1167 11AA;ACAB;1100 1167 11AA; # (겫; 겫; 겫; 겫; 겫; ) HANGUL SYLLABLE GYEOGS +ACAC;ACAC;1100 1167 11AB;ACAC;1100 1167 11AB; # (견; 견; 견; 견; 견; ) HANGUL SYLLABLE GYEON +ACAD;ACAD;1100 1167 11AC;ACAD;1100 1167 11AC; # (겭; 겭; 겭; 겭; 겭; ) HANGUL SYLLABLE GYEONJ +ACAE;ACAE;1100 1167 11AD;ACAE;1100 1167 11AD; # (겮; 겮; 겮; 겮; 겮; ) HANGUL SYLLABLE GYEONH +ACAF;ACAF;1100 1167 11AE;ACAF;1100 1167 11AE; # (겯; 겯; 겯; 겯; 겯; ) HANGUL SYLLABLE GYEOD +ACB0;ACB0;1100 1167 11AF;ACB0;1100 1167 11AF; # (결; 결; 결; 결; 결; ) HANGUL SYLLABLE GYEOL +ACB1;ACB1;1100 1167 11B0;ACB1;1100 1167 11B0; # (겱; 겱; 겱; 겱; 겱; ) HANGUL SYLLABLE GYEOLG +ACB2;ACB2;1100 1167 11B1;ACB2;1100 1167 11B1; # (겲; 겲; 겲; 겲; 겲; ) HANGUL SYLLABLE GYEOLM +ACB3;ACB3;1100 1167 11B2;ACB3;1100 1167 11B2; # (겳; 겳; 겳; 겳; 겳; ) HANGUL SYLLABLE GYEOLB +ACB4;ACB4;1100 1167 11B3;ACB4;1100 1167 11B3; # (겴; 겴; 겴; 겴; 겴; ) HANGUL SYLLABLE GYEOLS +ACB5;ACB5;1100 1167 11B4;ACB5;1100 1167 11B4; # (겵; 겵; 겵; 겵; 겵; ) HANGUL SYLLABLE GYEOLT +ACB6;ACB6;1100 1167 11B5;ACB6;1100 1167 11B5; # (겶; 겶; 겶; 겶; 겶; ) HANGUL SYLLABLE GYEOLP +ACB7;ACB7;1100 1167 11B6;ACB7;1100 1167 11B6; # (겷; 겷; 겷; 겷; 겷; ) HANGUL SYLLABLE GYEOLH +ACB8;ACB8;1100 1167 11B7;ACB8;1100 1167 11B7; # (겸; 겸; 겸; 겸; 겸; ) HANGUL SYLLABLE GYEOM +ACB9;ACB9;1100 1167 11B8;ACB9;1100 1167 11B8; # (겹; 겹; 겹; 겹; 겹; ) HANGUL SYLLABLE GYEOB +ACBA;ACBA;1100 1167 11B9;ACBA;1100 1167 11B9; # (겺; 겺; 겺; 겺; 겺; ) HANGUL SYLLABLE GYEOBS +ACBB;ACBB;1100 1167 11BA;ACBB;1100 1167 11BA; # (겻; 겻; 겻; 겻; 겻; ) HANGUL SYLLABLE GYEOS +ACBC;ACBC;1100 1167 11BB;ACBC;1100 1167 11BB; # (겼; 겼; 겼; 겼; 겼; ) HANGUL SYLLABLE GYEOSS +ACBD;ACBD;1100 1167 11BC;ACBD;1100 1167 11BC; # (경; 경; 경; 경; 경; ) HANGUL SYLLABLE GYEONG +ACBE;ACBE;1100 1167 11BD;ACBE;1100 1167 11BD; # (겾; 겾; 겾; 겾; 겾; ) HANGUL SYLLABLE GYEOJ +ACBF;ACBF;1100 1167 11BE;ACBF;1100 1167 11BE; # (겿; 겿; 겿; 겿; 겿; ) HANGUL SYLLABLE GYEOC +ACC0;ACC0;1100 1167 11BF;ACC0;1100 1167 11BF; # (곀; 곀; 곀; 곀; 곀; ) HANGUL SYLLABLE GYEOK +ACC1;ACC1;1100 1167 11C0;ACC1;1100 1167 11C0; # (곁; 곁; 곁; 곁; 곁; ) HANGUL SYLLABLE GYEOT +ACC2;ACC2;1100 1167 11C1;ACC2;1100 1167 11C1; # (곂; 곂; 곂; 곂; 곂; ) HANGUL SYLLABLE GYEOP +ACC3;ACC3;1100 1167 11C2;ACC3;1100 1167 11C2; # (곃; 곃; 곃; 곃; 곃; ) HANGUL SYLLABLE GYEOH +ACC4;ACC4;1100 1168;ACC4;1100 1168; # (계; 계; 계; 계; 계; ) HANGUL SYLLABLE GYE +ACC5;ACC5;1100 1168 11A8;ACC5;1100 1168 11A8; # (곅; 곅; 곅; 곅; 곅; ) HANGUL SYLLABLE GYEG +ACC6;ACC6;1100 1168 11A9;ACC6;1100 1168 11A9; # (곆; 곆; 곆; 곆; 곆; ) HANGUL SYLLABLE GYEGG +ACC7;ACC7;1100 1168 11AA;ACC7;1100 1168 11AA; # (곇; 곇; 곇; 곇; 곇; ) HANGUL SYLLABLE GYEGS +ACC8;ACC8;1100 1168 11AB;ACC8;1100 1168 11AB; # (곈; 곈; 곈; 곈; 곈; ) HANGUL SYLLABLE GYEN +ACC9;ACC9;1100 1168 11AC;ACC9;1100 1168 11AC; # (곉; 곉; 곉; 곉; 곉; ) HANGUL SYLLABLE GYENJ +ACCA;ACCA;1100 1168 11AD;ACCA;1100 1168 11AD; # (곊; 곊; 곊; 곊; 곊; ) HANGUL SYLLABLE GYENH +ACCB;ACCB;1100 1168 11AE;ACCB;1100 1168 11AE; # (곋; 곋; 곋; 곋; 곋; ) HANGUL SYLLABLE GYED +ACCC;ACCC;1100 1168 11AF;ACCC;1100 1168 11AF; # (곌; 곌; 곌; 곌; 곌; ) HANGUL SYLLABLE GYEL +ACCD;ACCD;1100 1168 11B0;ACCD;1100 1168 11B0; # (곍; 곍; 곍; 곍; 곍; ) HANGUL SYLLABLE GYELG +ACCE;ACCE;1100 1168 11B1;ACCE;1100 1168 11B1; # (곎; 곎; 곎; 곎; 곎; ) HANGUL SYLLABLE GYELM +ACCF;ACCF;1100 1168 11B2;ACCF;1100 1168 11B2; # (곏; 곏; 곏; 곏; 곏; ) HANGUL SYLLABLE GYELB +ACD0;ACD0;1100 1168 11B3;ACD0;1100 1168 11B3; # (곐; 곐; 곐; 곐; 곐; ) HANGUL SYLLABLE GYELS +ACD1;ACD1;1100 1168 11B4;ACD1;1100 1168 11B4; # (곑; 곑; 곑; 곑; 곑; ) HANGUL SYLLABLE GYELT +ACD2;ACD2;1100 1168 11B5;ACD2;1100 1168 11B5; # (곒; 곒; 곒; 곒; 곒; ) HANGUL SYLLABLE GYELP +ACD3;ACD3;1100 1168 11B6;ACD3;1100 1168 11B6; # (곓; 곓; 곓; 곓; 곓; ) HANGUL SYLLABLE GYELH +ACD4;ACD4;1100 1168 11B7;ACD4;1100 1168 11B7; # (곔; 곔; 곔; 곔; 곔; ) HANGUL SYLLABLE GYEM +ACD5;ACD5;1100 1168 11B8;ACD5;1100 1168 11B8; # (곕; 곕; 곕; 곕; 곕; ) HANGUL SYLLABLE GYEB +ACD6;ACD6;1100 1168 11B9;ACD6;1100 1168 11B9; # (곖; 곖; 곖; 곖; 곖; ) HANGUL SYLLABLE GYEBS +ACD7;ACD7;1100 1168 11BA;ACD7;1100 1168 11BA; # (곗; 곗; 곗; 곗; 곗; ) HANGUL SYLLABLE GYES +ACD8;ACD8;1100 1168 11BB;ACD8;1100 1168 11BB; # (곘; 곘; 곘; 곘; 곘; ) HANGUL SYLLABLE GYESS +ACD9;ACD9;1100 1168 11BC;ACD9;1100 1168 11BC; # (곙; 곙; 곙; 곙; 곙; ) HANGUL SYLLABLE GYENG +ACDA;ACDA;1100 1168 11BD;ACDA;1100 1168 11BD; # (곚; 곚; 곚; 곚; 곚; ) HANGUL SYLLABLE GYEJ +ACDB;ACDB;1100 1168 11BE;ACDB;1100 1168 11BE; # (곛; 곛; 곛; 곛; 곛; ) HANGUL SYLLABLE GYEC +ACDC;ACDC;1100 1168 11BF;ACDC;1100 1168 11BF; # (곜; 곜; 곜; 곜; 곜; ) HANGUL SYLLABLE GYEK +ACDD;ACDD;1100 1168 11C0;ACDD;1100 1168 11C0; # (곝; 곝; 곝; 곝; 곝; ) HANGUL SYLLABLE GYET +ACDE;ACDE;1100 1168 11C1;ACDE;1100 1168 11C1; # (곞; 곞; 곞; 곞; 곞; ) HANGUL SYLLABLE GYEP +ACDF;ACDF;1100 1168 11C2;ACDF;1100 1168 11C2; # (곟; 곟; 곟; 곟; 곟; ) HANGUL SYLLABLE GYEH +ACE0;ACE0;1100 1169;ACE0;1100 1169; # (고; 고; 고; 고; 고; ) HANGUL SYLLABLE GO +ACE1;ACE1;1100 1169 11A8;ACE1;1100 1169 11A8; # (곡; 곡; 곡; 곡; 곡; ) HANGUL SYLLABLE GOG +ACE2;ACE2;1100 1169 11A9;ACE2;1100 1169 11A9; # (곢; 곢; 곢; 곢; 곢; ) HANGUL SYLLABLE GOGG +ACE3;ACE3;1100 1169 11AA;ACE3;1100 1169 11AA; # (곣; 곣; 곣; 곣; 곣; ) HANGUL SYLLABLE GOGS +ACE4;ACE4;1100 1169 11AB;ACE4;1100 1169 11AB; # (곤; 곤; 곤; 곤; 곤; ) HANGUL SYLLABLE GON +ACE5;ACE5;1100 1169 11AC;ACE5;1100 1169 11AC; # (곥; 곥; 곥; 곥; 곥; ) HANGUL SYLLABLE GONJ +ACE6;ACE6;1100 1169 11AD;ACE6;1100 1169 11AD; # (곦; 곦; 곦; 곦; 곦; ) HANGUL SYLLABLE GONH +ACE7;ACE7;1100 1169 11AE;ACE7;1100 1169 11AE; # (곧; 곧; 곧; 곧; 곧; ) HANGUL SYLLABLE GOD +ACE8;ACE8;1100 1169 11AF;ACE8;1100 1169 11AF; # (골; 골; 골; 골; 골; ) HANGUL SYLLABLE GOL +ACE9;ACE9;1100 1169 11B0;ACE9;1100 1169 11B0; # (곩; 곩; 곩; 곩; 곩; ) HANGUL SYLLABLE GOLG +ACEA;ACEA;1100 1169 11B1;ACEA;1100 1169 11B1; # (곪; 곪; 곪; 곪; 곪; ) HANGUL SYLLABLE GOLM +ACEB;ACEB;1100 1169 11B2;ACEB;1100 1169 11B2; # (곫; 곫; 곫; 곫; 곫; ) HANGUL SYLLABLE GOLB +ACEC;ACEC;1100 1169 11B3;ACEC;1100 1169 11B3; # (곬; 곬; 곬; 곬; 곬; ) HANGUL SYLLABLE GOLS +ACED;ACED;1100 1169 11B4;ACED;1100 1169 11B4; # (곭; 곭; 곭; 곭; 곭; ) HANGUL SYLLABLE GOLT +ACEE;ACEE;1100 1169 11B5;ACEE;1100 1169 11B5; # (곮; 곮; 곮; 곮; 곮; ) HANGUL SYLLABLE GOLP +ACEF;ACEF;1100 1169 11B6;ACEF;1100 1169 11B6; # (곯; 곯; 곯; 곯; 곯; ) HANGUL SYLLABLE GOLH +ACF0;ACF0;1100 1169 11B7;ACF0;1100 1169 11B7; # (곰; 곰; 곰; 곰; 곰; ) HANGUL SYLLABLE GOM +ACF1;ACF1;1100 1169 11B8;ACF1;1100 1169 11B8; # (곱; 곱; 곱; 곱; 곱; ) HANGUL SYLLABLE GOB +ACF2;ACF2;1100 1169 11B9;ACF2;1100 1169 11B9; # (곲; 곲; 곲; 곲; 곲; ) HANGUL SYLLABLE GOBS +ACF3;ACF3;1100 1169 11BA;ACF3;1100 1169 11BA; # (곳; 곳; 곳; 곳; 곳; ) HANGUL SYLLABLE GOS +ACF4;ACF4;1100 1169 11BB;ACF4;1100 1169 11BB; # (곴; 곴; 곴; 곴; 곴; ) HANGUL SYLLABLE GOSS +ACF5;ACF5;1100 1169 11BC;ACF5;1100 1169 11BC; # (공; 공; 공; 공; 공; ) HANGUL SYLLABLE GONG +ACF6;ACF6;1100 1169 11BD;ACF6;1100 1169 11BD; # (곶; 곶; 곶; 곶; 곶; ) HANGUL SYLLABLE GOJ +ACF7;ACF7;1100 1169 11BE;ACF7;1100 1169 11BE; # (곷; 곷; 곷; 곷; 곷; ) HANGUL SYLLABLE GOC +ACF8;ACF8;1100 1169 11BF;ACF8;1100 1169 11BF; # (곸; 곸; 곸; 곸; 곸; ) HANGUL SYLLABLE GOK +ACF9;ACF9;1100 1169 11C0;ACF9;1100 1169 11C0; # (곹; 곹; 곹; 곹; 곹; ) HANGUL SYLLABLE GOT +ACFA;ACFA;1100 1169 11C1;ACFA;1100 1169 11C1; # (곺; 곺; 곺; 곺; 곺; ) HANGUL SYLLABLE GOP +ACFB;ACFB;1100 1169 11C2;ACFB;1100 1169 11C2; # (곻; 곻; 곻; 곻; 곻; ) HANGUL SYLLABLE GOH +ACFC;ACFC;1100 116A;ACFC;1100 116A; # (과; 과; 과; 과; 과; ) HANGUL SYLLABLE GWA +ACFD;ACFD;1100 116A 11A8;ACFD;1100 116A 11A8; # (곽; 곽; 곽; 곽; 곽; ) HANGUL SYLLABLE GWAG +ACFE;ACFE;1100 116A 11A9;ACFE;1100 116A 11A9; # (곾; 곾; 곾; 곾; 곾; ) HANGUL SYLLABLE GWAGG +ACFF;ACFF;1100 116A 11AA;ACFF;1100 116A 11AA; # (곿; 곿; 곿; 곿; 곿; ) HANGUL SYLLABLE GWAGS +AD00;AD00;1100 116A 11AB;AD00;1100 116A 11AB; # (관; 관; 관; 관; 관; ) HANGUL SYLLABLE GWAN +AD01;AD01;1100 116A 11AC;AD01;1100 116A 11AC; # (괁; 괁; 괁; 괁; 괁; ) HANGUL SYLLABLE GWANJ +AD02;AD02;1100 116A 11AD;AD02;1100 116A 11AD; # (괂; 괂; 괂; 괂; 괂; ) HANGUL SYLLABLE GWANH +AD03;AD03;1100 116A 11AE;AD03;1100 116A 11AE; # (괃; 괃; 괃; 괃; 괃; ) HANGUL SYLLABLE GWAD +AD04;AD04;1100 116A 11AF;AD04;1100 116A 11AF; # (괄; 괄; 괄; 괄; 괄; ) HANGUL SYLLABLE GWAL +AD05;AD05;1100 116A 11B0;AD05;1100 116A 11B0; # (괅; 괅; 괅; 괅; 괅; ) HANGUL SYLLABLE GWALG +AD06;AD06;1100 116A 11B1;AD06;1100 116A 11B1; # (괆; 괆; 괆; 괆; 괆; ) HANGUL SYLLABLE GWALM +AD07;AD07;1100 116A 11B2;AD07;1100 116A 11B2; # (괇; 괇; 괇; 괇; 괇; ) HANGUL SYLLABLE GWALB +AD08;AD08;1100 116A 11B3;AD08;1100 116A 11B3; # (괈; 괈; 괈; 괈; 괈; ) HANGUL SYLLABLE GWALS +AD09;AD09;1100 116A 11B4;AD09;1100 116A 11B4; # (괉; 괉; 괉; 괉; 괉; ) HANGUL SYLLABLE GWALT +AD0A;AD0A;1100 116A 11B5;AD0A;1100 116A 11B5; # (괊; 괊; 괊; 괊; 괊; ) HANGUL SYLLABLE GWALP +AD0B;AD0B;1100 116A 11B6;AD0B;1100 116A 11B6; # (괋; 괋; 괋; 괋; 괋; ) HANGUL SYLLABLE GWALH +AD0C;AD0C;1100 116A 11B7;AD0C;1100 116A 11B7; # (괌; 괌; 괌; 괌; 괌; ) HANGUL SYLLABLE GWAM +AD0D;AD0D;1100 116A 11B8;AD0D;1100 116A 11B8; # (괍; 괍; 괍; 괍; 괍; ) HANGUL SYLLABLE GWAB +AD0E;AD0E;1100 116A 11B9;AD0E;1100 116A 11B9; # (괎; 괎; 괎; 괎; 괎; ) HANGUL SYLLABLE GWABS +AD0F;AD0F;1100 116A 11BA;AD0F;1100 116A 11BA; # (괏; 괏; 괏; 괏; 괏; ) HANGUL SYLLABLE GWAS +AD10;AD10;1100 116A 11BB;AD10;1100 116A 11BB; # (괐; 괐; 괐; 괐; 괐; ) HANGUL SYLLABLE GWASS +AD11;AD11;1100 116A 11BC;AD11;1100 116A 11BC; # (광; 광; 광; 광; 광; ) HANGUL SYLLABLE GWANG +AD12;AD12;1100 116A 11BD;AD12;1100 116A 11BD; # (괒; 괒; 괒; 괒; 괒; ) HANGUL SYLLABLE GWAJ +AD13;AD13;1100 116A 11BE;AD13;1100 116A 11BE; # (괓; 괓; 괓; 괓; 괓; ) HANGUL SYLLABLE GWAC +AD14;AD14;1100 116A 11BF;AD14;1100 116A 11BF; # (괔; 괔; 괔; 괔; 괔; ) HANGUL SYLLABLE GWAK +AD15;AD15;1100 116A 11C0;AD15;1100 116A 11C0; # (괕; 괕; 괕; 괕; 괕; ) HANGUL SYLLABLE GWAT +AD16;AD16;1100 116A 11C1;AD16;1100 116A 11C1; # (괖; 괖; 괖; 괖; 괖; ) HANGUL SYLLABLE GWAP +AD17;AD17;1100 116A 11C2;AD17;1100 116A 11C2; # (괗; 괗; 괗; 괗; 괗; ) HANGUL SYLLABLE GWAH +AD18;AD18;1100 116B;AD18;1100 116B; # (괘; 괘; 괘; 괘; 괘; ) HANGUL SYLLABLE GWAE +AD19;AD19;1100 116B 11A8;AD19;1100 116B 11A8; # (괙; 괙; 괙; 괙; 괙; ) HANGUL SYLLABLE GWAEG +AD1A;AD1A;1100 116B 11A9;AD1A;1100 116B 11A9; # (괚; 괚; 괚; 괚; 괚; ) HANGUL SYLLABLE GWAEGG +AD1B;AD1B;1100 116B 11AA;AD1B;1100 116B 11AA; # (괛; 괛; 괛; 괛; 괛; ) HANGUL SYLLABLE GWAEGS +AD1C;AD1C;1100 116B 11AB;AD1C;1100 116B 11AB; # (괜; 괜; 괜; 괜; 괜; ) HANGUL SYLLABLE GWAEN +AD1D;AD1D;1100 116B 11AC;AD1D;1100 116B 11AC; # (괝; 괝; 괝; 괝; 괝; ) HANGUL SYLLABLE GWAENJ +AD1E;AD1E;1100 116B 11AD;AD1E;1100 116B 11AD; # (괞; 괞; 괞; 괞; 괞; ) HANGUL SYLLABLE GWAENH +AD1F;AD1F;1100 116B 11AE;AD1F;1100 116B 11AE; # (괟; 괟; 괟; 괟; 괟; ) HANGUL SYLLABLE GWAED +AD20;AD20;1100 116B 11AF;AD20;1100 116B 11AF; # (괠; 괠; 괠; 괠; 괠; ) HANGUL SYLLABLE GWAEL +AD21;AD21;1100 116B 11B0;AD21;1100 116B 11B0; # (괡; 괡; 괡; 괡; 괡; ) HANGUL SYLLABLE GWAELG +AD22;AD22;1100 116B 11B1;AD22;1100 116B 11B1; # (괢; 괢; 괢; 괢; 괢; ) HANGUL SYLLABLE GWAELM +AD23;AD23;1100 116B 11B2;AD23;1100 116B 11B2; # (괣; 괣; 괣; 괣; 괣; ) HANGUL SYLLABLE GWAELB +AD24;AD24;1100 116B 11B3;AD24;1100 116B 11B3; # (괤; 괤; 괤; 괤; 괤; ) HANGUL SYLLABLE GWAELS +AD25;AD25;1100 116B 11B4;AD25;1100 116B 11B4; # (괥; 괥; 괥; 괥; 괥; ) HANGUL SYLLABLE GWAELT +AD26;AD26;1100 116B 11B5;AD26;1100 116B 11B5; # (괦; 괦; 괦; 괦; 괦; ) HANGUL SYLLABLE GWAELP +AD27;AD27;1100 116B 11B6;AD27;1100 116B 11B6; # (괧; 괧; 괧; 괧; 괧; ) HANGUL SYLLABLE GWAELH +AD28;AD28;1100 116B 11B7;AD28;1100 116B 11B7; # (괨; 괨; 괨; 괨; 괨; ) HANGUL SYLLABLE GWAEM +AD29;AD29;1100 116B 11B8;AD29;1100 116B 11B8; # (괩; 괩; 괩; 괩; 괩; ) HANGUL SYLLABLE GWAEB +AD2A;AD2A;1100 116B 11B9;AD2A;1100 116B 11B9; # (괪; 괪; 괪; 괪; 괪; ) HANGUL SYLLABLE GWAEBS +AD2B;AD2B;1100 116B 11BA;AD2B;1100 116B 11BA; # (괫; 괫; 괫; 괫; 괫; ) HANGUL SYLLABLE GWAES +AD2C;AD2C;1100 116B 11BB;AD2C;1100 116B 11BB; # (괬; 괬; 괬; 괬; 괬; ) HANGUL SYLLABLE GWAESS +AD2D;AD2D;1100 116B 11BC;AD2D;1100 116B 11BC; # (괭; 괭; 괭; 괭; 괭; ) HANGUL SYLLABLE GWAENG +AD2E;AD2E;1100 116B 11BD;AD2E;1100 116B 11BD; # (괮; 괮; 괮; 괮; 괮; ) HANGUL SYLLABLE GWAEJ +AD2F;AD2F;1100 116B 11BE;AD2F;1100 116B 11BE; # (괯; 괯; 괯; 괯; 괯; ) HANGUL SYLLABLE GWAEC +AD30;AD30;1100 116B 11BF;AD30;1100 116B 11BF; # (괰; 괰; 괰; 괰; 괰; ) HANGUL SYLLABLE GWAEK +AD31;AD31;1100 116B 11C0;AD31;1100 116B 11C0; # (괱; 괱; 괱; 괱; 괱; ) HANGUL SYLLABLE GWAET +AD32;AD32;1100 116B 11C1;AD32;1100 116B 11C1; # (괲; 괲; 괲; 괲; 괲; ) HANGUL SYLLABLE GWAEP +AD33;AD33;1100 116B 11C2;AD33;1100 116B 11C2; # (괳; 괳; 괳; 괳; 괳; ) HANGUL SYLLABLE GWAEH +AD34;AD34;1100 116C;AD34;1100 116C; # (괴; 괴; 괴; 괴; 괴; ) HANGUL SYLLABLE GOE +AD35;AD35;1100 116C 11A8;AD35;1100 116C 11A8; # (괵; 괵; 괵; 괵; 괵; ) HANGUL SYLLABLE GOEG +AD36;AD36;1100 116C 11A9;AD36;1100 116C 11A9; # (괶; 괶; 괶; 괶; 괶; ) HANGUL SYLLABLE GOEGG +AD37;AD37;1100 116C 11AA;AD37;1100 116C 11AA; # (괷; 괷; 괷; 괷; 괷; ) HANGUL SYLLABLE GOEGS +AD38;AD38;1100 116C 11AB;AD38;1100 116C 11AB; # (괸; 괸; 괸; 괸; 괸; ) HANGUL SYLLABLE GOEN +AD39;AD39;1100 116C 11AC;AD39;1100 116C 11AC; # (괹; 괹; 괹; 괹; 괹; ) HANGUL SYLLABLE GOENJ +AD3A;AD3A;1100 116C 11AD;AD3A;1100 116C 11AD; # (괺; 괺; 괺; 괺; 괺; ) HANGUL SYLLABLE GOENH +AD3B;AD3B;1100 116C 11AE;AD3B;1100 116C 11AE; # (괻; 괻; 괻; 괻; 괻; ) HANGUL SYLLABLE GOED +AD3C;AD3C;1100 116C 11AF;AD3C;1100 116C 11AF; # (괼; 괼; 괼; 괼; 괼; ) HANGUL SYLLABLE GOEL +AD3D;AD3D;1100 116C 11B0;AD3D;1100 116C 11B0; # (괽; 괽; 괽; 괽; 괽; ) HANGUL SYLLABLE GOELG +AD3E;AD3E;1100 116C 11B1;AD3E;1100 116C 11B1; # (괾; 괾; 괾; 괾; 괾; ) HANGUL SYLLABLE GOELM +AD3F;AD3F;1100 116C 11B2;AD3F;1100 116C 11B2; # (괿; 괿; 괿; 괿; 괿; ) HANGUL SYLLABLE GOELB +AD40;AD40;1100 116C 11B3;AD40;1100 116C 11B3; # (굀; 굀; 굀; 굀; 굀; ) HANGUL SYLLABLE GOELS +AD41;AD41;1100 116C 11B4;AD41;1100 116C 11B4; # (굁; 굁; 굁; 굁; 굁; ) HANGUL SYLLABLE GOELT +AD42;AD42;1100 116C 11B5;AD42;1100 116C 11B5; # (굂; 굂; 굂; 굂; 굂; ) HANGUL SYLLABLE GOELP +AD43;AD43;1100 116C 11B6;AD43;1100 116C 11B6; # (굃; 굃; 굃; 굃; 굃; ) HANGUL SYLLABLE GOELH +AD44;AD44;1100 116C 11B7;AD44;1100 116C 11B7; # (굄; 굄; 굄; 굄; 굄; ) HANGUL SYLLABLE GOEM +AD45;AD45;1100 116C 11B8;AD45;1100 116C 11B8; # (굅; 굅; 굅; 굅; 굅; ) HANGUL SYLLABLE GOEB +AD46;AD46;1100 116C 11B9;AD46;1100 116C 11B9; # (굆; 굆; 굆; 굆; 굆; ) HANGUL SYLLABLE GOEBS +AD47;AD47;1100 116C 11BA;AD47;1100 116C 11BA; # (굇; 굇; 굇; 굇; 굇; ) HANGUL SYLLABLE GOES +AD48;AD48;1100 116C 11BB;AD48;1100 116C 11BB; # (굈; 굈; 굈; 굈; 굈; ) HANGUL SYLLABLE GOESS +AD49;AD49;1100 116C 11BC;AD49;1100 116C 11BC; # (굉; 굉; 굉; 굉; 굉; ) HANGUL SYLLABLE GOENG +AD4A;AD4A;1100 116C 11BD;AD4A;1100 116C 11BD; # (굊; 굊; 굊; 굊; 굊; ) HANGUL SYLLABLE GOEJ +AD4B;AD4B;1100 116C 11BE;AD4B;1100 116C 11BE; # (굋; 굋; 굋; 굋; 굋; ) HANGUL SYLLABLE GOEC +AD4C;AD4C;1100 116C 11BF;AD4C;1100 116C 11BF; # (굌; 굌; 굌; 굌; 굌; ) HANGUL SYLLABLE GOEK +AD4D;AD4D;1100 116C 11C0;AD4D;1100 116C 11C0; # (굍; 굍; 굍; 굍; 굍; ) HANGUL SYLLABLE GOET +AD4E;AD4E;1100 116C 11C1;AD4E;1100 116C 11C1; # (굎; 굎; 굎; 굎; 굎; ) HANGUL SYLLABLE GOEP +AD4F;AD4F;1100 116C 11C2;AD4F;1100 116C 11C2; # (굏; 굏; 굏; 굏; 굏; ) HANGUL SYLLABLE GOEH +AD50;AD50;1100 116D;AD50;1100 116D; # (교; 교; 교; 교; 교; ) HANGUL SYLLABLE GYO +AD51;AD51;1100 116D 11A8;AD51;1100 116D 11A8; # (굑; 굑; 굑; 굑; 굑; ) HANGUL SYLLABLE GYOG +AD52;AD52;1100 116D 11A9;AD52;1100 116D 11A9; # (굒; 굒; 굒; 굒; 굒; ) HANGUL SYLLABLE GYOGG +AD53;AD53;1100 116D 11AA;AD53;1100 116D 11AA; # (굓; 굓; 굓; 굓; 굓; ) HANGUL SYLLABLE GYOGS +AD54;AD54;1100 116D 11AB;AD54;1100 116D 11AB; # (굔; 굔; 굔; 굔; 굔; ) HANGUL SYLLABLE GYON +AD55;AD55;1100 116D 11AC;AD55;1100 116D 11AC; # (굕; 굕; 굕; 굕; 굕; ) HANGUL SYLLABLE GYONJ +AD56;AD56;1100 116D 11AD;AD56;1100 116D 11AD; # (굖; 굖; 굖; 굖; 굖; ) HANGUL SYLLABLE GYONH +AD57;AD57;1100 116D 11AE;AD57;1100 116D 11AE; # (굗; 굗; 굗; 굗; 굗; ) HANGUL SYLLABLE GYOD +AD58;AD58;1100 116D 11AF;AD58;1100 116D 11AF; # (굘; 굘; 굘; 굘; 굘; ) HANGUL SYLLABLE GYOL +AD59;AD59;1100 116D 11B0;AD59;1100 116D 11B0; # (굙; 굙; 굙; 굙; 굙; ) HANGUL SYLLABLE GYOLG +AD5A;AD5A;1100 116D 11B1;AD5A;1100 116D 11B1; # (굚; 굚; 굚; 굚; 굚; ) HANGUL SYLLABLE GYOLM +AD5B;AD5B;1100 116D 11B2;AD5B;1100 116D 11B2; # (굛; 굛; 굛; 굛; 굛; ) HANGUL SYLLABLE GYOLB +AD5C;AD5C;1100 116D 11B3;AD5C;1100 116D 11B3; # (굜; 굜; 굜; 굜; 굜; ) HANGUL SYLLABLE GYOLS +AD5D;AD5D;1100 116D 11B4;AD5D;1100 116D 11B4; # (굝; 굝; 굝; 굝; 굝; ) HANGUL SYLLABLE GYOLT +AD5E;AD5E;1100 116D 11B5;AD5E;1100 116D 11B5; # (굞; 굞; 굞; 굞; 굞; ) HANGUL SYLLABLE GYOLP +AD5F;AD5F;1100 116D 11B6;AD5F;1100 116D 11B6; # (굟; 굟; 굟; 굟; 굟; ) HANGUL SYLLABLE GYOLH +AD60;AD60;1100 116D 11B7;AD60;1100 116D 11B7; # (굠; 굠; 굠; 굠; 굠; ) HANGUL SYLLABLE GYOM +AD61;AD61;1100 116D 11B8;AD61;1100 116D 11B8; # (굡; 굡; 굡; 굡; 굡; ) HANGUL SYLLABLE GYOB +AD62;AD62;1100 116D 11B9;AD62;1100 116D 11B9; # (굢; 굢; 굢; 굢; 굢; ) HANGUL SYLLABLE GYOBS +AD63;AD63;1100 116D 11BA;AD63;1100 116D 11BA; # (굣; 굣; 굣; 굣; 굣; ) HANGUL SYLLABLE GYOS +AD64;AD64;1100 116D 11BB;AD64;1100 116D 11BB; # (굤; 굤; 굤; 굤; 굤; ) HANGUL SYLLABLE GYOSS +AD65;AD65;1100 116D 11BC;AD65;1100 116D 11BC; # (굥; 굥; 굥; 굥; 굥; ) HANGUL SYLLABLE GYONG +AD66;AD66;1100 116D 11BD;AD66;1100 116D 11BD; # (굦; 굦; 굦; 굦; 굦; ) HANGUL SYLLABLE GYOJ +AD67;AD67;1100 116D 11BE;AD67;1100 116D 11BE; # (굧; 굧; 굧; 굧; 굧; ) HANGUL SYLLABLE GYOC +AD68;AD68;1100 116D 11BF;AD68;1100 116D 11BF; # (굨; 굨; 굨; 굨; 굨; ) HANGUL SYLLABLE GYOK +AD69;AD69;1100 116D 11C0;AD69;1100 116D 11C0; # (굩; 굩; 굩; 굩; 굩; ) HANGUL SYLLABLE GYOT +AD6A;AD6A;1100 116D 11C1;AD6A;1100 116D 11C1; # (굪; 굪; 굪; 굪; 굪; ) HANGUL SYLLABLE GYOP +AD6B;AD6B;1100 116D 11C2;AD6B;1100 116D 11C2; # (굫; 굫; 굫; 굫; 굫; ) HANGUL SYLLABLE GYOH +AD6C;AD6C;1100 116E;AD6C;1100 116E; # (구; 구; 구; 구; 구; ) HANGUL SYLLABLE GU +AD6D;AD6D;1100 116E 11A8;AD6D;1100 116E 11A8; # (국; 국; 국; 국; 국; ) HANGUL SYLLABLE GUG +AD6E;AD6E;1100 116E 11A9;AD6E;1100 116E 11A9; # (굮; 굮; 굮; 굮; 굮; ) HANGUL SYLLABLE GUGG +AD6F;AD6F;1100 116E 11AA;AD6F;1100 116E 11AA; # (굯; 굯; 굯; 굯; 굯; ) HANGUL SYLLABLE GUGS +AD70;AD70;1100 116E 11AB;AD70;1100 116E 11AB; # (군; 군; 군; 군; 군; ) HANGUL SYLLABLE GUN +AD71;AD71;1100 116E 11AC;AD71;1100 116E 11AC; # (굱; 굱; 굱; 굱; 굱; ) HANGUL SYLLABLE GUNJ +AD72;AD72;1100 116E 11AD;AD72;1100 116E 11AD; # (굲; 굲; 굲; 굲; 굲; ) HANGUL SYLLABLE GUNH +AD73;AD73;1100 116E 11AE;AD73;1100 116E 11AE; # (굳; 굳; 굳; 굳; 굳; ) HANGUL SYLLABLE GUD +AD74;AD74;1100 116E 11AF;AD74;1100 116E 11AF; # (굴; 굴; 굴; 굴; 굴; ) HANGUL SYLLABLE GUL +AD75;AD75;1100 116E 11B0;AD75;1100 116E 11B0; # (굵; 굵; 굵; 굵; 굵; ) HANGUL SYLLABLE GULG +AD76;AD76;1100 116E 11B1;AD76;1100 116E 11B1; # (굶; 굶; 굶; 굶; 굶; ) HANGUL SYLLABLE GULM +AD77;AD77;1100 116E 11B2;AD77;1100 116E 11B2; # (굷; 굷; 굷; 굷; 굷; ) HANGUL SYLLABLE GULB +AD78;AD78;1100 116E 11B3;AD78;1100 116E 11B3; # (굸; 굸; 굸; 굸; 굸; ) HANGUL SYLLABLE GULS +AD79;AD79;1100 116E 11B4;AD79;1100 116E 11B4; # (굹; 굹; 굹; 굹; 굹; ) HANGUL SYLLABLE GULT +AD7A;AD7A;1100 116E 11B5;AD7A;1100 116E 11B5; # (굺; 굺; 굺; 굺; 굺; ) HANGUL SYLLABLE GULP +AD7B;AD7B;1100 116E 11B6;AD7B;1100 116E 11B6; # (굻; 굻; 굻; 굻; 굻; ) HANGUL SYLLABLE GULH +AD7C;AD7C;1100 116E 11B7;AD7C;1100 116E 11B7; # (굼; 굼; 굼; 굼; 굼; ) HANGUL SYLLABLE GUM +AD7D;AD7D;1100 116E 11B8;AD7D;1100 116E 11B8; # (굽; 굽; 굽; 굽; 굽; ) HANGUL SYLLABLE GUB +AD7E;AD7E;1100 116E 11B9;AD7E;1100 116E 11B9; # (굾; 굾; 굾; 굾; 굾; ) HANGUL SYLLABLE GUBS +AD7F;AD7F;1100 116E 11BA;AD7F;1100 116E 11BA; # (굿; 굿; 굿; 굿; 굿; ) HANGUL SYLLABLE GUS +AD80;AD80;1100 116E 11BB;AD80;1100 116E 11BB; # (궀; 궀; 궀; 궀; 궀; ) HANGUL SYLLABLE GUSS +AD81;AD81;1100 116E 11BC;AD81;1100 116E 11BC; # (궁; 궁; 궁; 궁; 궁; ) HANGUL SYLLABLE GUNG +AD82;AD82;1100 116E 11BD;AD82;1100 116E 11BD; # (궂; 궂; 궂; 궂; 궂; ) HANGUL SYLLABLE GUJ +AD83;AD83;1100 116E 11BE;AD83;1100 116E 11BE; # (궃; 궃; 궃; 궃; 궃; ) HANGUL SYLLABLE GUC +AD84;AD84;1100 116E 11BF;AD84;1100 116E 11BF; # (궄; 궄; 궄; 궄; 궄; ) HANGUL SYLLABLE GUK +AD85;AD85;1100 116E 11C0;AD85;1100 116E 11C0; # (궅; 궅; 궅; 궅; 궅; ) HANGUL SYLLABLE GUT +AD86;AD86;1100 116E 11C1;AD86;1100 116E 11C1; # (궆; 궆; 궆; 궆; 궆; ) HANGUL SYLLABLE GUP +AD87;AD87;1100 116E 11C2;AD87;1100 116E 11C2; # (궇; 궇; 궇; 궇; 궇; ) HANGUL SYLLABLE GUH +AD88;AD88;1100 116F;AD88;1100 116F; # (궈; 궈; 궈; 궈; 궈; ) HANGUL SYLLABLE GWEO +AD89;AD89;1100 116F 11A8;AD89;1100 116F 11A8; # (궉; 궉; 궉; 궉; 궉; ) HANGUL SYLLABLE GWEOG +AD8A;AD8A;1100 116F 11A9;AD8A;1100 116F 11A9; # (궊; 궊; 궊; 궊; 궊; ) HANGUL SYLLABLE GWEOGG +AD8B;AD8B;1100 116F 11AA;AD8B;1100 116F 11AA; # (궋; 궋; 궋; 궋; 궋; ) HANGUL SYLLABLE GWEOGS +AD8C;AD8C;1100 116F 11AB;AD8C;1100 116F 11AB; # (권; 권; 권; 권; 권; ) HANGUL SYLLABLE GWEON +AD8D;AD8D;1100 116F 11AC;AD8D;1100 116F 11AC; # (궍; 궍; 궍; 궍; 궍; ) HANGUL SYLLABLE GWEONJ +AD8E;AD8E;1100 116F 11AD;AD8E;1100 116F 11AD; # (궎; 궎; 궎; 궎; 궎; ) HANGUL SYLLABLE GWEONH +AD8F;AD8F;1100 116F 11AE;AD8F;1100 116F 11AE; # (궏; 궏; 궏; 궏; 궏; ) HANGUL SYLLABLE GWEOD +AD90;AD90;1100 116F 11AF;AD90;1100 116F 11AF; # (궐; 궐; 궐; 궐; 궐; ) HANGUL SYLLABLE GWEOL +AD91;AD91;1100 116F 11B0;AD91;1100 116F 11B0; # (궑; 궑; 궑; 궑; 궑; ) HANGUL SYLLABLE GWEOLG +AD92;AD92;1100 116F 11B1;AD92;1100 116F 11B1; # (궒; 궒; 궒; 궒; 궒; ) HANGUL SYLLABLE GWEOLM +AD93;AD93;1100 116F 11B2;AD93;1100 116F 11B2; # (궓; 궓; 궓; 궓; 궓; ) HANGUL SYLLABLE GWEOLB +AD94;AD94;1100 116F 11B3;AD94;1100 116F 11B3; # (궔; 궔; 궔; 궔; 궔; ) HANGUL SYLLABLE GWEOLS +AD95;AD95;1100 116F 11B4;AD95;1100 116F 11B4; # (궕; 궕; 궕; 궕; 궕; ) HANGUL SYLLABLE GWEOLT +AD96;AD96;1100 116F 11B5;AD96;1100 116F 11B5; # (궖; 궖; 궖; 궖; 궖; ) HANGUL SYLLABLE GWEOLP +AD97;AD97;1100 116F 11B6;AD97;1100 116F 11B6; # (궗; 궗; 궗; 궗; 궗; ) HANGUL SYLLABLE GWEOLH +AD98;AD98;1100 116F 11B7;AD98;1100 116F 11B7; # (궘; 궘; 궘; 궘; 궘; ) HANGUL SYLLABLE GWEOM +AD99;AD99;1100 116F 11B8;AD99;1100 116F 11B8; # (궙; 궙; 궙; 궙; 궙; ) HANGUL SYLLABLE GWEOB +AD9A;AD9A;1100 116F 11B9;AD9A;1100 116F 11B9; # (궚; 궚; 궚; 궚; 궚; ) HANGUL SYLLABLE GWEOBS +AD9B;AD9B;1100 116F 11BA;AD9B;1100 116F 11BA; # (궛; 궛; 궛; 궛; 궛; ) HANGUL SYLLABLE GWEOS +AD9C;AD9C;1100 116F 11BB;AD9C;1100 116F 11BB; # (궜; 궜; 궜; 궜; 궜; ) HANGUL SYLLABLE GWEOSS +AD9D;AD9D;1100 116F 11BC;AD9D;1100 116F 11BC; # (궝; 궝; 궝; 궝; 궝; ) HANGUL SYLLABLE GWEONG +AD9E;AD9E;1100 116F 11BD;AD9E;1100 116F 11BD; # (궞; 궞; 궞; 궞; 궞; ) HANGUL SYLLABLE GWEOJ +AD9F;AD9F;1100 116F 11BE;AD9F;1100 116F 11BE; # (궟; 궟; 궟; 궟; 궟; ) HANGUL SYLLABLE GWEOC +ADA0;ADA0;1100 116F 11BF;ADA0;1100 116F 11BF; # (궠; 궠; 궠; 궠; 궠; ) HANGUL SYLLABLE GWEOK +ADA1;ADA1;1100 116F 11C0;ADA1;1100 116F 11C0; # (궡; 궡; 궡; 궡; 궡; ) HANGUL SYLLABLE GWEOT +ADA2;ADA2;1100 116F 11C1;ADA2;1100 116F 11C1; # (궢; 궢; 궢; 궢; 궢; ) HANGUL SYLLABLE GWEOP +ADA3;ADA3;1100 116F 11C2;ADA3;1100 116F 11C2; # (궣; 궣; 궣; 궣; 궣; ) HANGUL SYLLABLE GWEOH +ADA4;ADA4;1100 1170;ADA4;1100 1170; # (궤; 궤; 궤; 궤; 궤; ) HANGUL SYLLABLE GWE +ADA5;ADA5;1100 1170 11A8;ADA5;1100 1170 11A8; # (궥; 궥; 궥; 궥; 궥; ) HANGUL SYLLABLE GWEG +ADA6;ADA6;1100 1170 11A9;ADA6;1100 1170 11A9; # (궦; 궦; 궦; 궦; 궦; ) HANGUL SYLLABLE GWEGG +ADA7;ADA7;1100 1170 11AA;ADA7;1100 1170 11AA; # (궧; 궧; 궧; 궧; 궧; ) HANGUL SYLLABLE GWEGS +ADA8;ADA8;1100 1170 11AB;ADA8;1100 1170 11AB; # (궨; 궨; 궨; 궨; 궨; ) HANGUL SYLLABLE GWEN +ADA9;ADA9;1100 1170 11AC;ADA9;1100 1170 11AC; # (궩; 궩; 궩; 궩; 궩; ) HANGUL SYLLABLE GWENJ +ADAA;ADAA;1100 1170 11AD;ADAA;1100 1170 11AD; # (궪; 궪; 궪; 궪; 궪; ) HANGUL SYLLABLE GWENH +ADAB;ADAB;1100 1170 11AE;ADAB;1100 1170 11AE; # (궫; 궫; 궫; 궫; 궫; ) HANGUL SYLLABLE GWED +ADAC;ADAC;1100 1170 11AF;ADAC;1100 1170 11AF; # (궬; 궬; 궬; 궬; 궬; ) HANGUL SYLLABLE GWEL +ADAD;ADAD;1100 1170 11B0;ADAD;1100 1170 11B0; # (궭; 궭; 궭; 궭; 궭; ) HANGUL SYLLABLE GWELG +ADAE;ADAE;1100 1170 11B1;ADAE;1100 1170 11B1; # (궮; 궮; 궮; 궮; 궮; ) HANGUL SYLLABLE GWELM +ADAF;ADAF;1100 1170 11B2;ADAF;1100 1170 11B2; # (궯; 궯; 궯; 궯; 궯; ) HANGUL SYLLABLE GWELB +ADB0;ADB0;1100 1170 11B3;ADB0;1100 1170 11B3; # (궰; 궰; 궰; 궰; 궰; ) HANGUL SYLLABLE GWELS +ADB1;ADB1;1100 1170 11B4;ADB1;1100 1170 11B4; # (궱; 궱; 궱; 궱; 궱; ) HANGUL SYLLABLE GWELT +ADB2;ADB2;1100 1170 11B5;ADB2;1100 1170 11B5; # (궲; 궲; 궲; 궲; 궲; ) HANGUL SYLLABLE GWELP +ADB3;ADB3;1100 1170 11B6;ADB3;1100 1170 11B6; # (궳; 궳; 궳; 궳; 궳; ) HANGUL SYLLABLE GWELH +ADB4;ADB4;1100 1170 11B7;ADB4;1100 1170 11B7; # (궴; 궴; 궴; 궴; 궴; ) HANGUL SYLLABLE GWEM +ADB5;ADB5;1100 1170 11B8;ADB5;1100 1170 11B8; # (궵; 궵; 궵; 궵; 궵; ) HANGUL SYLLABLE GWEB +ADB6;ADB6;1100 1170 11B9;ADB6;1100 1170 11B9; # (궶; 궶; 궶; 궶; 궶; ) HANGUL SYLLABLE GWEBS +ADB7;ADB7;1100 1170 11BA;ADB7;1100 1170 11BA; # (궷; 궷; 궷; 궷; 궷; ) HANGUL SYLLABLE GWES +ADB8;ADB8;1100 1170 11BB;ADB8;1100 1170 11BB; # (궸; 궸; 궸; 궸; 궸; ) HANGUL SYLLABLE GWESS +ADB9;ADB9;1100 1170 11BC;ADB9;1100 1170 11BC; # (궹; 궹; 궹; 궹; 궹; ) HANGUL SYLLABLE GWENG +ADBA;ADBA;1100 1170 11BD;ADBA;1100 1170 11BD; # (궺; 궺; 궺; 궺; 궺; ) HANGUL SYLLABLE GWEJ +ADBB;ADBB;1100 1170 11BE;ADBB;1100 1170 11BE; # (궻; 궻; 궻; 궻; 궻; ) HANGUL SYLLABLE GWEC +ADBC;ADBC;1100 1170 11BF;ADBC;1100 1170 11BF; # (궼; 궼; 궼; 궼; 궼; ) HANGUL SYLLABLE GWEK +ADBD;ADBD;1100 1170 11C0;ADBD;1100 1170 11C0; # (궽; 궽; 궽; 궽; 궽; ) HANGUL SYLLABLE GWET +ADBE;ADBE;1100 1170 11C1;ADBE;1100 1170 11C1; # (궾; 궾; 궾; 궾; 궾; ) HANGUL SYLLABLE GWEP +ADBF;ADBF;1100 1170 11C2;ADBF;1100 1170 11C2; # (궿; 궿; 궿; 궿; 궿; ) HANGUL SYLLABLE GWEH +ADC0;ADC0;1100 1171;ADC0;1100 1171; # (귀; 귀; 귀; 귀; 귀; ) HANGUL SYLLABLE GWI +ADC1;ADC1;1100 1171 11A8;ADC1;1100 1171 11A8; # (귁; 귁; 귁; 귁; 귁; ) HANGUL SYLLABLE GWIG +ADC2;ADC2;1100 1171 11A9;ADC2;1100 1171 11A9; # (귂; 귂; 귂; 귂; 귂; ) HANGUL SYLLABLE GWIGG +ADC3;ADC3;1100 1171 11AA;ADC3;1100 1171 11AA; # (귃; 귃; 귃; 귃; 귃; ) HANGUL SYLLABLE GWIGS +ADC4;ADC4;1100 1171 11AB;ADC4;1100 1171 11AB; # (귄; 귄; 귄; 귄; 귄; ) HANGUL SYLLABLE GWIN +ADC5;ADC5;1100 1171 11AC;ADC5;1100 1171 11AC; # (귅; 귅; 귅; 귅; 귅; ) HANGUL SYLLABLE GWINJ +ADC6;ADC6;1100 1171 11AD;ADC6;1100 1171 11AD; # (귆; 귆; 귆; 귆; 귆; ) HANGUL SYLLABLE GWINH +ADC7;ADC7;1100 1171 11AE;ADC7;1100 1171 11AE; # (귇; 귇; 귇; 귇; 귇; ) HANGUL SYLLABLE GWID +ADC8;ADC8;1100 1171 11AF;ADC8;1100 1171 11AF; # (귈; 귈; 귈; 귈; 귈; ) HANGUL SYLLABLE GWIL +ADC9;ADC9;1100 1171 11B0;ADC9;1100 1171 11B0; # (귉; 귉; 귉; 귉; 귉; ) HANGUL SYLLABLE GWILG +ADCA;ADCA;1100 1171 11B1;ADCA;1100 1171 11B1; # (귊; 귊; 귊; 귊; 귊; ) HANGUL SYLLABLE GWILM +ADCB;ADCB;1100 1171 11B2;ADCB;1100 1171 11B2; # (귋; 귋; 귋; 귋; 귋; ) HANGUL SYLLABLE GWILB +ADCC;ADCC;1100 1171 11B3;ADCC;1100 1171 11B3; # (귌; 귌; 귌; 귌; 귌; ) HANGUL SYLLABLE GWILS +ADCD;ADCD;1100 1171 11B4;ADCD;1100 1171 11B4; # (귍; 귍; 귍; 귍; 귍; ) HANGUL SYLLABLE GWILT +ADCE;ADCE;1100 1171 11B5;ADCE;1100 1171 11B5; # (귎; 귎; 귎; 귎; 귎; ) HANGUL SYLLABLE GWILP +ADCF;ADCF;1100 1171 11B6;ADCF;1100 1171 11B6; # (귏; 귏; 귏; 귏; 귏; ) HANGUL SYLLABLE GWILH +ADD0;ADD0;1100 1171 11B7;ADD0;1100 1171 11B7; # (귐; 귐; 귐; 귐; 귐; ) HANGUL SYLLABLE GWIM +ADD1;ADD1;1100 1171 11B8;ADD1;1100 1171 11B8; # (귑; 귑; 귑; 귑; 귑; ) HANGUL SYLLABLE GWIB +ADD2;ADD2;1100 1171 11B9;ADD2;1100 1171 11B9; # (귒; 귒; 귒; 귒; 귒; ) HANGUL SYLLABLE GWIBS +ADD3;ADD3;1100 1171 11BA;ADD3;1100 1171 11BA; # (귓; 귓; 귓; 귓; 귓; ) HANGUL SYLLABLE GWIS +ADD4;ADD4;1100 1171 11BB;ADD4;1100 1171 11BB; # (귔; 귔; 귔; 귔; 귔; ) HANGUL SYLLABLE GWISS +ADD5;ADD5;1100 1171 11BC;ADD5;1100 1171 11BC; # (귕; 귕; 귕; 귕; 귕; ) HANGUL SYLLABLE GWING +ADD6;ADD6;1100 1171 11BD;ADD6;1100 1171 11BD; # (귖; 귖; 귖; 귖; 귖; ) HANGUL SYLLABLE GWIJ +ADD7;ADD7;1100 1171 11BE;ADD7;1100 1171 11BE; # (귗; 귗; 귗; 귗; 귗; ) HANGUL SYLLABLE GWIC +ADD8;ADD8;1100 1171 11BF;ADD8;1100 1171 11BF; # (귘; 귘; 귘; 귘; 귘; ) HANGUL SYLLABLE GWIK +ADD9;ADD9;1100 1171 11C0;ADD9;1100 1171 11C0; # (귙; 귙; 귙; 귙; 귙; ) HANGUL SYLLABLE GWIT +ADDA;ADDA;1100 1171 11C1;ADDA;1100 1171 11C1; # (귚; 귚; 귚; 귚; 귚; ) HANGUL SYLLABLE GWIP +ADDB;ADDB;1100 1171 11C2;ADDB;1100 1171 11C2; # (귛; 귛; 귛; 귛; 귛; ) HANGUL SYLLABLE GWIH +ADDC;ADDC;1100 1172;ADDC;1100 1172; # (규; 규; 규; 규; 규; ) HANGUL SYLLABLE GYU +ADDD;ADDD;1100 1172 11A8;ADDD;1100 1172 11A8; # (귝; 귝; 귝; 귝; 귝; ) HANGUL SYLLABLE GYUG +ADDE;ADDE;1100 1172 11A9;ADDE;1100 1172 11A9; # (귞; 귞; 귞; 귞; 귞; ) HANGUL SYLLABLE GYUGG +ADDF;ADDF;1100 1172 11AA;ADDF;1100 1172 11AA; # (귟; 귟; 귟; 귟; 귟; ) HANGUL SYLLABLE GYUGS +ADE0;ADE0;1100 1172 11AB;ADE0;1100 1172 11AB; # (균; 균; 균; 균; 균; ) HANGUL SYLLABLE GYUN +ADE1;ADE1;1100 1172 11AC;ADE1;1100 1172 11AC; # (귡; 귡; 귡; 귡; 귡; ) HANGUL SYLLABLE GYUNJ +ADE2;ADE2;1100 1172 11AD;ADE2;1100 1172 11AD; # (귢; 귢; 귢; 귢; 귢; ) HANGUL SYLLABLE GYUNH +ADE3;ADE3;1100 1172 11AE;ADE3;1100 1172 11AE; # (귣; 귣; 귣; 귣; 귣; ) HANGUL SYLLABLE GYUD +ADE4;ADE4;1100 1172 11AF;ADE4;1100 1172 11AF; # (귤; 귤; 귤; 귤; 귤; ) HANGUL SYLLABLE GYUL +ADE5;ADE5;1100 1172 11B0;ADE5;1100 1172 11B0; # (귥; 귥; 귥; 귥; 귥; ) HANGUL SYLLABLE GYULG +ADE6;ADE6;1100 1172 11B1;ADE6;1100 1172 11B1; # (귦; 귦; 귦; 귦; 귦; ) HANGUL SYLLABLE GYULM +ADE7;ADE7;1100 1172 11B2;ADE7;1100 1172 11B2; # (귧; 귧; 귧; 귧; 귧; ) HANGUL SYLLABLE GYULB +ADE8;ADE8;1100 1172 11B3;ADE8;1100 1172 11B3; # (귨; 귨; 귨; 귨; 귨; ) HANGUL SYLLABLE GYULS +ADE9;ADE9;1100 1172 11B4;ADE9;1100 1172 11B4; # (귩; 귩; 귩; 귩; 귩; ) HANGUL SYLLABLE GYULT +ADEA;ADEA;1100 1172 11B5;ADEA;1100 1172 11B5; # (귪; 귪; 귪; 귪; 귪; ) HANGUL SYLLABLE GYULP +ADEB;ADEB;1100 1172 11B6;ADEB;1100 1172 11B6; # (귫; 귫; 귫; 귫; 귫; ) HANGUL SYLLABLE GYULH +ADEC;ADEC;1100 1172 11B7;ADEC;1100 1172 11B7; # (귬; 귬; 귬; 귬; 귬; ) HANGUL SYLLABLE GYUM +ADED;ADED;1100 1172 11B8;ADED;1100 1172 11B8; # (귭; 귭; 귭; 귭; 귭; ) HANGUL SYLLABLE GYUB +ADEE;ADEE;1100 1172 11B9;ADEE;1100 1172 11B9; # (귮; 귮; 귮; 귮; 귮; ) HANGUL SYLLABLE GYUBS +ADEF;ADEF;1100 1172 11BA;ADEF;1100 1172 11BA; # (귯; 귯; 귯; 귯; 귯; ) HANGUL SYLLABLE GYUS +ADF0;ADF0;1100 1172 11BB;ADF0;1100 1172 11BB; # (귰; 귰; 귰; 귰; 귰; ) HANGUL SYLLABLE GYUSS +ADF1;ADF1;1100 1172 11BC;ADF1;1100 1172 11BC; # (귱; 귱; 귱; 귱; 귱; ) HANGUL SYLLABLE GYUNG +ADF2;ADF2;1100 1172 11BD;ADF2;1100 1172 11BD; # (귲; 귲; 귲; 귲; 귲; ) HANGUL SYLLABLE GYUJ +ADF3;ADF3;1100 1172 11BE;ADF3;1100 1172 11BE; # (귳; 귳; 귳; 귳; 귳; ) HANGUL SYLLABLE GYUC +ADF4;ADF4;1100 1172 11BF;ADF4;1100 1172 11BF; # (귴; 귴; 귴; 귴; 귴; ) HANGUL SYLLABLE GYUK +ADF5;ADF5;1100 1172 11C0;ADF5;1100 1172 11C0; # (귵; 귵; 귵; 귵; 귵; ) HANGUL SYLLABLE GYUT +ADF6;ADF6;1100 1172 11C1;ADF6;1100 1172 11C1; # (귶; 귶; 귶; 귶; 귶; ) HANGUL SYLLABLE GYUP +ADF7;ADF7;1100 1172 11C2;ADF7;1100 1172 11C2; # (귷; 귷; 귷; 귷; 귷; ) HANGUL SYLLABLE GYUH +ADF8;ADF8;1100 1173;ADF8;1100 1173; # (그; 그; 그; 그; 그; ) HANGUL SYLLABLE GEU +ADF9;ADF9;1100 1173 11A8;ADF9;1100 1173 11A8; # (극; 극; 극; 극; 극; ) HANGUL SYLLABLE GEUG +ADFA;ADFA;1100 1173 11A9;ADFA;1100 1173 11A9; # (귺; 귺; 귺; 귺; 귺; ) HANGUL SYLLABLE GEUGG +ADFB;ADFB;1100 1173 11AA;ADFB;1100 1173 11AA; # (귻; 귻; 귻; 귻; 귻; ) HANGUL SYLLABLE GEUGS +ADFC;ADFC;1100 1173 11AB;ADFC;1100 1173 11AB; # (근; 근; 근; 근; 근; ) HANGUL SYLLABLE GEUN +ADFD;ADFD;1100 1173 11AC;ADFD;1100 1173 11AC; # (귽; 귽; 귽; 귽; 귽; ) HANGUL SYLLABLE GEUNJ +ADFE;ADFE;1100 1173 11AD;ADFE;1100 1173 11AD; # (귾; 귾; 귾; 귾; 귾; ) HANGUL SYLLABLE GEUNH +ADFF;ADFF;1100 1173 11AE;ADFF;1100 1173 11AE; # (귿; 귿; 귿; 귿; 귿; ) HANGUL SYLLABLE GEUD +AE00;AE00;1100 1173 11AF;AE00;1100 1173 11AF; # (글; 글; 글; 글; 글; ) HANGUL SYLLABLE GEUL +AE01;AE01;1100 1173 11B0;AE01;1100 1173 11B0; # (긁; 긁; 긁; 긁; 긁; ) HANGUL SYLLABLE GEULG +AE02;AE02;1100 1173 11B1;AE02;1100 1173 11B1; # (긂; 긂; 긂; 긂; 긂; ) HANGUL SYLLABLE GEULM +AE03;AE03;1100 1173 11B2;AE03;1100 1173 11B2; # (긃; 긃; 긃; 긃; 긃; ) HANGUL SYLLABLE GEULB +AE04;AE04;1100 1173 11B3;AE04;1100 1173 11B3; # (긄; 긄; 긄; 긄; 긄; ) HANGUL SYLLABLE GEULS +AE05;AE05;1100 1173 11B4;AE05;1100 1173 11B4; # (긅; 긅; 긅; 긅; 긅; ) HANGUL SYLLABLE GEULT +AE06;AE06;1100 1173 11B5;AE06;1100 1173 11B5; # (긆; 긆; 긆; 긆; 긆; ) HANGUL SYLLABLE GEULP +AE07;AE07;1100 1173 11B6;AE07;1100 1173 11B6; # (긇; 긇; 긇; 긇; 긇; ) HANGUL SYLLABLE GEULH +AE08;AE08;1100 1173 11B7;AE08;1100 1173 11B7; # (금; 금; 금; 금; 금; ) HANGUL SYLLABLE GEUM +AE09;AE09;1100 1173 11B8;AE09;1100 1173 11B8; # (급; 급; 급; 급; 급; ) HANGUL SYLLABLE GEUB +AE0A;AE0A;1100 1173 11B9;AE0A;1100 1173 11B9; # (긊; 긊; 긊; 긊; 긊; ) HANGUL SYLLABLE GEUBS +AE0B;AE0B;1100 1173 11BA;AE0B;1100 1173 11BA; # (긋; 긋; 긋; 긋; 긋; ) HANGUL SYLLABLE GEUS +AE0C;AE0C;1100 1173 11BB;AE0C;1100 1173 11BB; # (긌; 긌; 긌; 긌; 긌; ) HANGUL SYLLABLE GEUSS +AE0D;AE0D;1100 1173 11BC;AE0D;1100 1173 11BC; # (긍; 긍; 긍; 긍; 긍; ) HANGUL SYLLABLE GEUNG +AE0E;AE0E;1100 1173 11BD;AE0E;1100 1173 11BD; # (긎; 긎; 긎; 긎; 긎; ) HANGUL SYLLABLE GEUJ +AE0F;AE0F;1100 1173 11BE;AE0F;1100 1173 11BE; # (긏; 긏; 긏; 긏; 긏; ) HANGUL SYLLABLE GEUC +AE10;AE10;1100 1173 11BF;AE10;1100 1173 11BF; # (긐; 긐; 긐; 긐; 긐; ) HANGUL SYLLABLE GEUK +AE11;AE11;1100 1173 11C0;AE11;1100 1173 11C0; # (긑; 긑; 긑; 긑; 긑; ) HANGUL SYLLABLE GEUT +AE12;AE12;1100 1173 11C1;AE12;1100 1173 11C1; # (긒; 긒; 긒; 긒; 긒; ) HANGUL SYLLABLE GEUP +AE13;AE13;1100 1173 11C2;AE13;1100 1173 11C2; # (긓; 긓; 긓; 긓; 긓; ) HANGUL SYLLABLE GEUH +AE14;AE14;1100 1174;AE14;1100 1174; # (긔; 긔; 긔; 긔; 긔; ) HANGUL SYLLABLE GYI +AE15;AE15;1100 1174 11A8;AE15;1100 1174 11A8; # (긕; 긕; 긕; 긕; 긕; ) HANGUL SYLLABLE GYIG +AE16;AE16;1100 1174 11A9;AE16;1100 1174 11A9; # (긖; 긖; 긖; 긖; 긖; ) HANGUL SYLLABLE GYIGG +AE17;AE17;1100 1174 11AA;AE17;1100 1174 11AA; # (긗; 긗; 긗; 긗; 긗; ) HANGUL SYLLABLE GYIGS +AE18;AE18;1100 1174 11AB;AE18;1100 1174 11AB; # (긘; 긘; 긘; 긘; 긘; ) HANGUL SYLLABLE GYIN +AE19;AE19;1100 1174 11AC;AE19;1100 1174 11AC; # (긙; 긙; 긙; 긙; 긙; ) HANGUL SYLLABLE GYINJ +AE1A;AE1A;1100 1174 11AD;AE1A;1100 1174 11AD; # (긚; 긚; 긚; 긚; 긚; ) HANGUL SYLLABLE GYINH +AE1B;AE1B;1100 1174 11AE;AE1B;1100 1174 11AE; # (긛; 긛; 긛; 긛; 긛; ) HANGUL SYLLABLE GYID +AE1C;AE1C;1100 1174 11AF;AE1C;1100 1174 11AF; # (긜; 긜; 긜; 긜; 긜; ) HANGUL SYLLABLE GYIL +AE1D;AE1D;1100 1174 11B0;AE1D;1100 1174 11B0; # (긝; 긝; 긝; 긝; 긝; ) HANGUL SYLLABLE GYILG +AE1E;AE1E;1100 1174 11B1;AE1E;1100 1174 11B1; # (긞; 긞; 긞; 긞; 긞; ) HANGUL SYLLABLE GYILM +AE1F;AE1F;1100 1174 11B2;AE1F;1100 1174 11B2; # (긟; 긟; 긟; 긟; 긟; ) HANGUL SYLLABLE GYILB +AE20;AE20;1100 1174 11B3;AE20;1100 1174 11B3; # (긠; 긠; 긠; 긠; 긠; ) HANGUL SYLLABLE GYILS +AE21;AE21;1100 1174 11B4;AE21;1100 1174 11B4; # (긡; 긡; 긡; 긡; 긡; ) HANGUL SYLLABLE GYILT +AE22;AE22;1100 1174 11B5;AE22;1100 1174 11B5; # (긢; 긢; 긢; 긢; 긢; ) HANGUL SYLLABLE GYILP +AE23;AE23;1100 1174 11B6;AE23;1100 1174 11B6; # (긣; 긣; 긣; 긣; 긣; ) HANGUL SYLLABLE GYILH +AE24;AE24;1100 1174 11B7;AE24;1100 1174 11B7; # (긤; 긤; 긤; 긤; 긤; ) HANGUL SYLLABLE GYIM +AE25;AE25;1100 1174 11B8;AE25;1100 1174 11B8; # (긥; 긥; 긥; 긥; 긥; ) HANGUL SYLLABLE GYIB +AE26;AE26;1100 1174 11B9;AE26;1100 1174 11B9; # (긦; 긦; 긦; 긦; 긦; ) HANGUL SYLLABLE GYIBS +AE27;AE27;1100 1174 11BA;AE27;1100 1174 11BA; # (긧; 긧; 긧; 긧; 긧; ) HANGUL SYLLABLE GYIS +AE28;AE28;1100 1174 11BB;AE28;1100 1174 11BB; # (긨; 긨; 긨; 긨; 긨; ) HANGUL SYLLABLE GYISS +AE29;AE29;1100 1174 11BC;AE29;1100 1174 11BC; # (긩; 긩; 긩; 긩; 긩; ) HANGUL SYLLABLE GYING +AE2A;AE2A;1100 1174 11BD;AE2A;1100 1174 11BD; # (긪; 긪; 긪; 긪; 긪; ) HANGUL SYLLABLE GYIJ +AE2B;AE2B;1100 1174 11BE;AE2B;1100 1174 11BE; # (긫; 긫; 긫; 긫; 긫; ) HANGUL SYLLABLE GYIC +AE2C;AE2C;1100 1174 11BF;AE2C;1100 1174 11BF; # (긬; 긬; 긬; 긬; 긬; ) HANGUL SYLLABLE GYIK +AE2D;AE2D;1100 1174 11C0;AE2D;1100 1174 11C0; # (긭; 긭; 긭; 긭; 긭; ) HANGUL SYLLABLE GYIT +AE2E;AE2E;1100 1174 11C1;AE2E;1100 1174 11C1; # (긮; 긮; 긮; 긮; 긮; ) HANGUL SYLLABLE GYIP +AE2F;AE2F;1100 1174 11C2;AE2F;1100 1174 11C2; # (긯; 긯; 긯; 긯; 긯; ) HANGUL SYLLABLE GYIH +AE30;AE30;1100 1175;AE30;1100 1175; # (기; 기; 기; 기; 기; ) HANGUL SYLLABLE GI +AE31;AE31;1100 1175 11A8;AE31;1100 1175 11A8; # (긱; 긱; 긱; 긱; 긱; ) HANGUL SYLLABLE GIG +AE32;AE32;1100 1175 11A9;AE32;1100 1175 11A9; # (긲; 긲; 긲; 긲; 긲; ) HANGUL SYLLABLE GIGG +AE33;AE33;1100 1175 11AA;AE33;1100 1175 11AA; # (긳; 긳; 긳; 긳; 긳; ) HANGUL SYLLABLE GIGS +AE34;AE34;1100 1175 11AB;AE34;1100 1175 11AB; # (긴; 긴; 긴; 긴; 긴; ) HANGUL SYLLABLE GIN +AE35;AE35;1100 1175 11AC;AE35;1100 1175 11AC; # (긵; 긵; 긵; 긵; 긵; ) HANGUL SYLLABLE GINJ +AE36;AE36;1100 1175 11AD;AE36;1100 1175 11AD; # (긶; 긶; 긶; 긶; 긶; ) HANGUL SYLLABLE GINH +AE37;AE37;1100 1175 11AE;AE37;1100 1175 11AE; # (긷; 긷; 긷; 긷; 긷; ) HANGUL SYLLABLE GID +AE38;AE38;1100 1175 11AF;AE38;1100 1175 11AF; # (길; 길; 길; 길; 길; ) HANGUL SYLLABLE GIL +AE39;AE39;1100 1175 11B0;AE39;1100 1175 11B0; # (긹; 긹; 긹; 긹; 긹; ) HANGUL SYLLABLE GILG +AE3A;AE3A;1100 1175 11B1;AE3A;1100 1175 11B1; # (긺; 긺; 긺; 긺; 긺; ) HANGUL SYLLABLE GILM +AE3B;AE3B;1100 1175 11B2;AE3B;1100 1175 11B2; # (긻; 긻; 긻; 긻; 긻; ) HANGUL SYLLABLE GILB +AE3C;AE3C;1100 1175 11B3;AE3C;1100 1175 11B3; # (긼; 긼; 긼; 긼; 긼; ) HANGUL SYLLABLE GILS +AE3D;AE3D;1100 1175 11B4;AE3D;1100 1175 11B4; # (긽; 긽; 긽; 긽; 긽; ) HANGUL SYLLABLE GILT +AE3E;AE3E;1100 1175 11B5;AE3E;1100 1175 11B5; # (긾; 긾; 긾; 긾; 긾; ) HANGUL SYLLABLE GILP +AE3F;AE3F;1100 1175 11B6;AE3F;1100 1175 11B6; # (긿; 긿; 긿; 긿; 긿; ) HANGUL SYLLABLE GILH +AE40;AE40;1100 1175 11B7;AE40;1100 1175 11B7; # (김; 김; 김; 김; 김; ) HANGUL SYLLABLE GIM +AE41;AE41;1100 1175 11B8;AE41;1100 1175 11B8; # (깁; 깁; 깁; 깁; 깁; ) HANGUL SYLLABLE GIB +AE42;AE42;1100 1175 11B9;AE42;1100 1175 11B9; # (깂; 깂; 깂; 깂; 깂; ) HANGUL SYLLABLE GIBS +AE43;AE43;1100 1175 11BA;AE43;1100 1175 11BA; # (깃; 깃; 깃; 깃; 깃; ) HANGUL SYLLABLE GIS +AE44;AE44;1100 1175 11BB;AE44;1100 1175 11BB; # (깄; 깄; 깄; 깄; 깄; ) HANGUL SYLLABLE GISS +AE45;AE45;1100 1175 11BC;AE45;1100 1175 11BC; # (깅; 깅; 깅; 깅; 깅; ) HANGUL SYLLABLE GING +AE46;AE46;1100 1175 11BD;AE46;1100 1175 11BD; # (깆; 깆; 깆; 깆; 깆; ) HANGUL SYLLABLE GIJ +AE47;AE47;1100 1175 11BE;AE47;1100 1175 11BE; # (깇; 깇; 깇; 깇; 깇; ) HANGUL SYLLABLE GIC +AE48;AE48;1100 1175 11BF;AE48;1100 1175 11BF; # (깈; 깈; 깈; 깈; 깈; ) HANGUL SYLLABLE GIK +AE49;AE49;1100 1175 11C0;AE49;1100 1175 11C0; # (깉; 깉; 깉; 깉; 깉; ) HANGUL SYLLABLE GIT +AE4A;AE4A;1100 1175 11C1;AE4A;1100 1175 11C1; # (깊; 깊; 깊; 깊; 깊; ) HANGUL SYLLABLE GIP +AE4B;AE4B;1100 1175 11C2;AE4B;1100 1175 11C2; # (깋; 깋; 깋; 깋; 깋; ) HANGUL SYLLABLE GIH +AE4C;AE4C;1101 1161;AE4C;1101 1161; # (까; 까; 까; 까; 까; ) HANGUL SYLLABLE GGA +AE4D;AE4D;1101 1161 11A8;AE4D;1101 1161 11A8; # (깍; 깍; 깍; 깍; 깍; ) HANGUL SYLLABLE GGAG +AE4E;AE4E;1101 1161 11A9;AE4E;1101 1161 11A9; # (깎; 깎; 깎; 깎; 깎; ) HANGUL SYLLABLE GGAGG +AE4F;AE4F;1101 1161 11AA;AE4F;1101 1161 11AA; # (깏; 깏; 깏; 깏; 깏; ) HANGUL SYLLABLE GGAGS +AE50;AE50;1101 1161 11AB;AE50;1101 1161 11AB; # (깐; 깐; 깐; 깐; 깐; ) HANGUL SYLLABLE GGAN +AE51;AE51;1101 1161 11AC;AE51;1101 1161 11AC; # (깑; 깑; 깑; 깑; 깑; ) HANGUL SYLLABLE GGANJ +AE52;AE52;1101 1161 11AD;AE52;1101 1161 11AD; # (깒; 깒; 깒; 깒; 깒; ) HANGUL SYLLABLE GGANH +AE53;AE53;1101 1161 11AE;AE53;1101 1161 11AE; # (깓; 깓; 깓; 깓; 깓; ) HANGUL SYLLABLE GGAD +AE54;AE54;1101 1161 11AF;AE54;1101 1161 11AF; # (깔; 깔; 깔; 깔; 깔; ) HANGUL SYLLABLE GGAL +AE55;AE55;1101 1161 11B0;AE55;1101 1161 11B0; # (깕; 깕; 깕; 깕; 깕; ) HANGUL SYLLABLE GGALG +AE56;AE56;1101 1161 11B1;AE56;1101 1161 11B1; # (깖; 깖; 깖; 깖; 깖; ) HANGUL SYLLABLE GGALM +AE57;AE57;1101 1161 11B2;AE57;1101 1161 11B2; # (깗; 깗; 깗; 깗; 깗; ) HANGUL SYLLABLE GGALB +AE58;AE58;1101 1161 11B3;AE58;1101 1161 11B3; # (깘; 깘; 깘; 깘; 깘; ) HANGUL SYLLABLE GGALS +AE59;AE59;1101 1161 11B4;AE59;1101 1161 11B4; # (깙; 깙; 깙; 깙; 깙; ) HANGUL SYLLABLE GGALT +AE5A;AE5A;1101 1161 11B5;AE5A;1101 1161 11B5; # (깚; 깚; 깚; 깚; 깚; ) HANGUL SYLLABLE GGALP +AE5B;AE5B;1101 1161 11B6;AE5B;1101 1161 11B6; # (깛; 깛; 깛; 깛; 깛; ) HANGUL SYLLABLE GGALH +AE5C;AE5C;1101 1161 11B7;AE5C;1101 1161 11B7; # (깜; 깜; 깜; 깜; 깜; ) HANGUL SYLLABLE GGAM +AE5D;AE5D;1101 1161 11B8;AE5D;1101 1161 11B8; # (깝; 깝; 깝; 깝; 깝; ) HANGUL SYLLABLE GGAB +AE5E;AE5E;1101 1161 11B9;AE5E;1101 1161 11B9; # (깞; 깞; 깞; 깞; 깞; ) HANGUL SYLLABLE GGABS +AE5F;AE5F;1101 1161 11BA;AE5F;1101 1161 11BA; # (깟; 깟; 깟; 깟; 깟; ) HANGUL SYLLABLE GGAS +AE60;AE60;1101 1161 11BB;AE60;1101 1161 11BB; # (깠; 깠; 깠; 깠; 깠; ) HANGUL SYLLABLE GGASS +AE61;AE61;1101 1161 11BC;AE61;1101 1161 11BC; # (깡; 깡; 깡; 깡; 깡; ) HANGUL SYLLABLE GGANG +AE62;AE62;1101 1161 11BD;AE62;1101 1161 11BD; # (깢; 깢; 깢; 깢; 깢; ) HANGUL SYLLABLE GGAJ +AE63;AE63;1101 1161 11BE;AE63;1101 1161 11BE; # (깣; 깣; 깣; 깣; 깣; ) HANGUL SYLLABLE GGAC +AE64;AE64;1101 1161 11BF;AE64;1101 1161 11BF; # (깤; 깤; 깤; 깤; 깤; ) HANGUL SYLLABLE GGAK +AE65;AE65;1101 1161 11C0;AE65;1101 1161 11C0; # (깥; 깥; 깥; 깥; 깥; ) HANGUL SYLLABLE GGAT +AE66;AE66;1101 1161 11C1;AE66;1101 1161 11C1; # (깦; 깦; 깦; 깦; 깦; ) HANGUL SYLLABLE GGAP +AE67;AE67;1101 1161 11C2;AE67;1101 1161 11C2; # (깧; 깧; 깧; 깧; 깧; ) HANGUL SYLLABLE GGAH +AE68;AE68;1101 1162;AE68;1101 1162; # (깨; 깨; 깨; 깨; 깨; ) HANGUL SYLLABLE GGAE +AE69;AE69;1101 1162 11A8;AE69;1101 1162 11A8; # (깩; 깩; 깩; 깩; 깩; ) HANGUL SYLLABLE GGAEG +AE6A;AE6A;1101 1162 11A9;AE6A;1101 1162 11A9; # (깪; 깪; 깪; 깪; 깪; ) HANGUL SYLLABLE GGAEGG +AE6B;AE6B;1101 1162 11AA;AE6B;1101 1162 11AA; # (깫; 깫; 깫; 깫; 깫; ) HANGUL SYLLABLE GGAEGS +AE6C;AE6C;1101 1162 11AB;AE6C;1101 1162 11AB; # (깬; 깬; 깬; 깬; 깬; ) HANGUL SYLLABLE GGAEN +AE6D;AE6D;1101 1162 11AC;AE6D;1101 1162 11AC; # (깭; 깭; 깭; 깭; 깭; ) HANGUL SYLLABLE GGAENJ +AE6E;AE6E;1101 1162 11AD;AE6E;1101 1162 11AD; # (깮; 깮; 깮; 깮; 깮; ) HANGUL SYLLABLE GGAENH +AE6F;AE6F;1101 1162 11AE;AE6F;1101 1162 11AE; # (깯; 깯; 깯; 깯; 깯; ) HANGUL SYLLABLE GGAED +AE70;AE70;1101 1162 11AF;AE70;1101 1162 11AF; # (깰; 깰; 깰; 깰; 깰; ) HANGUL SYLLABLE GGAEL +AE71;AE71;1101 1162 11B0;AE71;1101 1162 11B0; # (깱; 깱; 깱; 깱; 깱; ) HANGUL SYLLABLE GGAELG +AE72;AE72;1101 1162 11B1;AE72;1101 1162 11B1; # (깲; 깲; 깲; 깲; 깲; ) HANGUL SYLLABLE GGAELM +AE73;AE73;1101 1162 11B2;AE73;1101 1162 11B2; # (깳; 깳; 깳; 깳; 깳; ) HANGUL SYLLABLE GGAELB +AE74;AE74;1101 1162 11B3;AE74;1101 1162 11B3; # (깴; 깴; 깴; 깴; 깴; ) HANGUL SYLLABLE GGAELS +AE75;AE75;1101 1162 11B4;AE75;1101 1162 11B4; # (깵; 깵; 깵; 깵; 깵; ) HANGUL SYLLABLE GGAELT +AE76;AE76;1101 1162 11B5;AE76;1101 1162 11B5; # (깶; 깶; 깶; 깶; 깶; ) HANGUL SYLLABLE GGAELP +AE77;AE77;1101 1162 11B6;AE77;1101 1162 11B6; # (깷; 깷; 깷; 깷; 깷; ) HANGUL SYLLABLE GGAELH +AE78;AE78;1101 1162 11B7;AE78;1101 1162 11B7; # (깸; 깸; 깸; 깸; 깸; ) HANGUL SYLLABLE GGAEM +AE79;AE79;1101 1162 11B8;AE79;1101 1162 11B8; # (깹; 깹; 깹; 깹; 깹; ) HANGUL SYLLABLE GGAEB +AE7A;AE7A;1101 1162 11B9;AE7A;1101 1162 11B9; # (깺; 깺; 깺; 깺; 깺; ) HANGUL SYLLABLE GGAEBS +AE7B;AE7B;1101 1162 11BA;AE7B;1101 1162 11BA; # (깻; 깻; 깻; 깻; 깻; ) HANGUL SYLLABLE GGAES +AE7C;AE7C;1101 1162 11BB;AE7C;1101 1162 11BB; # (깼; 깼; 깼; 깼; 깼; ) HANGUL SYLLABLE GGAESS +AE7D;AE7D;1101 1162 11BC;AE7D;1101 1162 11BC; # (깽; 깽; 깽; 깽; 깽; ) HANGUL SYLLABLE GGAENG +AE7E;AE7E;1101 1162 11BD;AE7E;1101 1162 11BD; # (깾; 깾; 깾; 깾; 깾; ) HANGUL SYLLABLE GGAEJ +AE7F;AE7F;1101 1162 11BE;AE7F;1101 1162 11BE; # (깿; 깿; 깿; 깿; 깿; ) HANGUL SYLLABLE GGAEC +AE80;AE80;1101 1162 11BF;AE80;1101 1162 11BF; # (꺀; 꺀; 꺀; 꺀; 꺀; ) HANGUL SYLLABLE GGAEK +AE81;AE81;1101 1162 11C0;AE81;1101 1162 11C0; # (꺁; 꺁; 꺁; 꺁; 꺁; ) HANGUL SYLLABLE GGAET +AE82;AE82;1101 1162 11C1;AE82;1101 1162 11C1; # (꺂; 꺂; 꺂; 꺂; 꺂; ) HANGUL SYLLABLE GGAEP +AE83;AE83;1101 1162 11C2;AE83;1101 1162 11C2; # (꺃; 꺃; 꺃; 꺃; 꺃; ) HANGUL SYLLABLE GGAEH +AE84;AE84;1101 1163;AE84;1101 1163; # (꺄; 꺄; 꺄; 꺄; 꺄; ) HANGUL SYLLABLE GGYA +AE85;AE85;1101 1163 11A8;AE85;1101 1163 11A8; # (꺅; 꺅; 꺅; 꺅; 꺅; ) HANGUL SYLLABLE GGYAG +AE86;AE86;1101 1163 11A9;AE86;1101 1163 11A9; # (꺆; 꺆; 꺆; 꺆; 꺆; ) HANGUL SYLLABLE GGYAGG +AE87;AE87;1101 1163 11AA;AE87;1101 1163 11AA; # (꺇; 꺇; 꺇; 꺇; 꺇; ) HANGUL SYLLABLE GGYAGS +AE88;AE88;1101 1163 11AB;AE88;1101 1163 11AB; # (꺈; 꺈; 꺈; 꺈; 꺈; ) HANGUL SYLLABLE GGYAN +AE89;AE89;1101 1163 11AC;AE89;1101 1163 11AC; # (꺉; 꺉; 꺉; 꺉; 꺉; ) HANGUL SYLLABLE GGYANJ +AE8A;AE8A;1101 1163 11AD;AE8A;1101 1163 11AD; # (꺊; 꺊; 꺊; 꺊; 꺊; ) HANGUL SYLLABLE GGYANH +AE8B;AE8B;1101 1163 11AE;AE8B;1101 1163 11AE; # (꺋; 꺋; 꺋; 꺋; 꺋; ) HANGUL SYLLABLE GGYAD +AE8C;AE8C;1101 1163 11AF;AE8C;1101 1163 11AF; # (꺌; 꺌; 꺌; 꺌; 꺌; ) HANGUL SYLLABLE GGYAL +AE8D;AE8D;1101 1163 11B0;AE8D;1101 1163 11B0; # (꺍; 꺍; 꺍; 꺍; 꺍; ) HANGUL SYLLABLE GGYALG +AE8E;AE8E;1101 1163 11B1;AE8E;1101 1163 11B1; # (꺎; 꺎; 꺎; 꺎; 꺎; ) HANGUL SYLLABLE GGYALM +AE8F;AE8F;1101 1163 11B2;AE8F;1101 1163 11B2; # (꺏; 꺏; 꺏; 꺏; 꺏; ) HANGUL SYLLABLE GGYALB +AE90;AE90;1101 1163 11B3;AE90;1101 1163 11B3; # (꺐; 꺐; 꺐; 꺐; 꺐; ) HANGUL SYLLABLE GGYALS +AE91;AE91;1101 1163 11B4;AE91;1101 1163 11B4; # (꺑; 꺑; 꺑; 꺑; 꺑; ) HANGUL SYLLABLE GGYALT +AE92;AE92;1101 1163 11B5;AE92;1101 1163 11B5; # (꺒; 꺒; 꺒; 꺒; 꺒; ) HANGUL SYLLABLE GGYALP +AE93;AE93;1101 1163 11B6;AE93;1101 1163 11B6; # (꺓; 꺓; 꺓; 꺓; 꺓; ) HANGUL SYLLABLE GGYALH +AE94;AE94;1101 1163 11B7;AE94;1101 1163 11B7; # (꺔; 꺔; 꺔; 꺔; 꺔; ) HANGUL SYLLABLE GGYAM +AE95;AE95;1101 1163 11B8;AE95;1101 1163 11B8; # (꺕; 꺕; 꺕; 꺕; 꺕; ) HANGUL SYLLABLE GGYAB +AE96;AE96;1101 1163 11B9;AE96;1101 1163 11B9; # (꺖; 꺖; 꺖; 꺖; 꺖; ) HANGUL SYLLABLE GGYABS +AE97;AE97;1101 1163 11BA;AE97;1101 1163 11BA; # (꺗; 꺗; 꺗; 꺗; 꺗; ) HANGUL SYLLABLE GGYAS +AE98;AE98;1101 1163 11BB;AE98;1101 1163 11BB; # (꺘; 꺘; 꺘; 꺘; 꺘; ) HANGUL SYLLABLE GGYASS +AE99;AE99;1101 1163 11BC;AE99;1101 1163 11BC; # (꺙; 꺙; 꺙; 꺙; 꺙; ) HANGUL SYLLABLE GGYANG +AE9A;AE9A;1101 1163 11BD;AE9A;1101 1163 11BD; # (꺚; 꺚; 꺚; 꺚; 꺚; ) HANGUL SYLLABLE GGYAJ +AE9B;AE9B;1101 1163 11BE;AE9B;1101 1163 11BE; # (꺛; 꺛; 꺛; 꺛; 꺛; ) HANGUL SYLLABLE GGYAC +AE9C;AE9C;1101 1163 11BF;AE9C;1101 1163 11BF; # (꺜; 꺜; 꺜; 꺜; 꺜; ) HANGUL SYLLABLE GGYAK +AE9D;AE9D;1101 1163 11C0;AE9D;1101 1163 11C0; # (꺝; 꺝; 꺝; 꺝; 꺝; ) HANGUL SYLLABLE GGYAT +AE9E;AE9E;1101 1163 11C1;AE9E;1101 1163 11C1; # (꺞; 꺞; 꺞; 꺞; 꺞; ) HANGUL SYLLABLE GGYAP +AE9F;AE9F;1101 1163 11C2;AE9F;1101 1163 11C2; # (꺟; 꺟; 꺟; 꺟; 꺟; ) HANGUL SYLLABLE GGYAH +AEA0;AEA0;1101 1164;AEA0;1101 1164; # (꺠; 꺠; 꺠; 꺠; 꺠; ) HANGUL SYLLABLE GGYAE +AEA1;AEA1;1101 1164 11A8;AEA1;1101 1164 11A8; # (꺡; 꺡; 꺡; 꺡; 꺡; ) HANGUL SYLLABLE GGYAEG +AEA2;AEA2;1101 1164 11A9;AEA2;1101 1164 11A9; # (꺢; 꺢; 꺢; 꺢; 꺢; ) HANGUL SYLLABLE GGYAEGG +AEA3;AEA3;1101 1164 11AA;AEA3;1101 1164 11AA; # (꺣; 꺣; 꺣; 꺣; 꺣; ) HANGUL SYLLABLE GGYAEGS +AEA4;AEA4;1101 1164 11AB;AEA4;1101 1164 11AB; # (꺤; 꺤; 꺤; 꺤; 꺤; ) HANGUL SYLLABLE GGYAEN +AEA5;AEA5;1101 1164 11AC;AEA5;1101 1164 11AC; # (꺥; 꺥; 꺥; 꺥; 꺥; ) HANGUL SYLLABLE GGYAENJ +AEA6;AEA6;1101 1164 11AD;AEA6;1101 1164 11AD; # (꺦; 꺦; 꺦; 꺦; 꺦; ) HANGUL SYLLABLE GGYAENH +AEA7;AEA7;1101 1164 11AE;AEA7;1101 1164 11AE; # (꺧; 꺧; 꺧; 꺧; 꺧; ) HANGUL SYLLABLE GGYAED +AEA8;AEA8;1101 1164 11AF;AEA8;1101 1164 11AF; # (꺨; 꺨; 꺨; 꺨; 꺨; ) HANGUL SYLLABLE GGYAEL +AEA9;AEA9;1101 1164 11B0;AEA9;1101 1164 11B0; # (꺩; 꺩; 꺩; 꺩; 꺩; ) HANGUL SYLLABLE GGYAELG +AEAA;AEAA;1101 1164 11B1;AEAA;1101 1164 11B1; # (꺪; 꺪; 꺪; 꺪; 꺪; ) HANGUL SYLLABLE GGYAELM +AEAB;AEAB;1101 1164 11B2;AEAB;1101 1164 11B2; # (꺫; 꺫; 꺫; 꺫; 꺫; ) HANGUL SYLLABLE GGYAELB +AEAC;AEAC;1101 1164 11B3;AEAC;1101 1164 11B3; # (꺬; 꺬; 꺬; 꺬; 꺬; ) HANGUL SYLLABLE GGYAELS +AEAD;AEAD;1101 1164 11B4;AEAD;1101 1164 11B4; # (꺭; 꺭; 꺭; 꺭; 꺭; ) HANGUL SYLLABLE GGYAELT +AEAE;AEAE;1101 1164 11B5;AEAE;1101 1164 11B5; # (꺮; 꺮; 꺮; 꺮; 꺮; ) HANGUL SYLLABLE GGYAELP +AEAF;AEAF;1101 1164 11B6;AEAF;1101 1164 11B6; # (꺯; 꺯; 꺯; 꺯; 꺯; ) HANGUL SYLLABLE GGYAELH +AEB0;AEB0;1101 1164 11B7;AEB0;1101 1164 11B7; # (꺰; 꺰; 꺰; 꺰; 꺰; ) HANGUL SYLLABLE GGYAEM +AEB1;AEB1;1101 1164 11B8;AEB1;1101 1164 11B8; # (꺱; 꺱; 꺱; 꺱; 꺱; ) HANGUL SYLLABLE GGYAEB +AEB2;AEB2;1101 1164 11B9;AEB2;1101 1164 11B9; # (꺲; 꺲; 꺲; 꺲; 꺲; ) HANGUL SYLLABLE GGYAEBS +AEB3;AEB3;1101 1164 11BA;AEB3;1101 1164 11BA; # (꺳; 꺳; 꺳; 꺳; 꺳; ) HANGUL SYLLABLE GGYAES +AEB4;AEB4;1101 1164 11BB;AEB4;1101 1164 11BB; # (꺴; 꺴; 꺴; 꺴; 꺴; ) HANGUL SYLLABLE GGYAESS +AEB5;AEB5;1101 1164 11BC;AEB5;1101 1164 11BC; # (꺵; 꺵; 꺵; 꺵; 꺵; ) HANGUL SYLLABLE GGYAENG +AEB6;AEB6;1101 1164 11BD;AEB6;1101 1164 11BD; # (꺶; 꺶; 꺶; 꺶; 꺶; ) HANGUL SYLLABLE GGYAEJ +AEB7;AEB7;1101 1164 11BE;AEB7;1101 1164 11BE; # (꺷; 꺷; 꺷; 꺷; 꺷; ) HANGUL SYLLABLE GGYAEC +AEB8;AEB8;1101 1164 11BF;AEB8;1101 1164 11BF; # (꺸; 꺸; 꺸; 꺸; 꺸; ) HANGUL SYLLABLE GGYAEK +AEB9;AEB9;1101 1164 11C0;AEB9;1101 1164 11C0; # (꺹; 꺹; 꺹; 꺹; 꺹; ) HANGUL SYLLABLE GGYAET +AEBA;AEBA;1101 1164 11C1;AEBA;1101 1164 11C1; # (꺺; 꺺; 꺺; 꺺; 꺺; ) HANGUL SYLLABLE GGYAEP +AEBB;AEBB;1101 1164 11C2;AEBB;1101 1164 11C2; # (꺻; 꺻; 꺻; 꺻; 꺻; ) HANGUL SYLLABLE GGYAEH +AEBC;AEBC;1101 1165;AEBC;1101 1165; # (꺼; 꺼; 꺼; 꺼; 꺼; ) HANGUL SYLLABLE GGEO +AEBD;AEBD;1101 1165 11A8;AEBD;1101 1165 11A8; # (꺽; 꺽; 꺽; 꺽; 꺽; ) HANGUL SYLLABLE GGEOG +AEBE;AEBE;1101 1165 11A9;AEBE;1101 1165 11A9; # (꺾; 꺾; 꺾; 꺾; 꺾; ) HANGUL SYLLABLE GGEOGG +AEBF;AEBF;1101 1165 11AA;AEBF;1101 1165 11AA; # (꺿; 꺿; 꺿; 꺿; 꺿; ) HANGUL SYLLABLE GGEOGS +AEC0;AEC0;1101 1165 11AB;AEC0;1101 1165 11AB; # (껀; 껀; 껀; 껀; 껀; ) HANGUL SYLLABLE GGEON +AEC1;AEC1;1101 1165 11AC;AEC1;1101 1165 11AC; # (껁; 껁; 껁; 껁; 껁; ) HANGUL SYLLABLE GGEONJ +AEC2;AEC2;1101 1165 11AD;AEC2;1101 1165 11AD; # (껂; 껂; 껂; 껂; 껂; ) HANGUL SYLLABLE GGEONH +AEC3;AEC3;1101 1165 11AE;AEC3;1101 1165 11AE; # (껃; 껃; 껃; 껃; 껃; ) HANGUL SYLLABLE GGEOD +AEC4;AEC4;1101 1165 11AF;AEC4;1101 1165 11AF; # (껄; 껄; 껄; 껄; 껄; ) HANGUL SYLLABLE GGEOL +AEC5;AEC5;1101 1165 11B0;AEC5;1101 1165 11B0; # (껅; 껅; 껅; 껅; 껅; ) HANGUL SYLLABLE GGEOLG +AEC6;AEC6;1101 1165 11B1;AEC6;1101 1165 11B1; # (껆; 껆; 껆; 껆; 껆; ) HANGUL SYLLABLE GGEOLM +AEC7;AEC7;1101 1165 11B2;AEC7;1101 1165 11B2; # (껇; 껇; 껇; 껇; 껇; ) HANGUL SYLLABLE GGEOLB +AEC8;AEC8;1101 1165 11B3;AEC8;1101 1165 11B3; # (껈; 껈; 껈; 껈; 껈; ) HANGUL SYLLABLE GGEOLS +AEC9;AEC9;1101 1165 11B4;AEC9;1101 1165 11B4; # (껉; 껉; 껉; 껉; 껉; ) HANGUL SYLLABLE GGEOLT +AECA;AECA;1101 1165 11B5;AECA;1101 1165 11B5; # (껊; 껊; 껊; 껊; 껊; ) HANGUL SYLLABLE GGEOLP +AECB;AECB;1101 1165 11B6;AECB;1101 1165 11B6; # (껋; 껋; 껋; 껋; 껋; ) HANGUL SYLLABLE GGEOLH +AECC;AECC;1101 1165 11B7;AECC;1101 1165 11B7; # (껌; 껌; 껌; 껌; 껌; ) HANGUL SYLLABLE GGEOM +AECD;AECD;1101 1165 11B8;AECD;1101 1165 11B8; # (껍; 껍; 껍; 껍; 껍; ) HANGUL SYLLABLE GGEOB +AECE;AECE;1101 1165 11B9;AECE;1101 1165 11B9; # (껎; 껎; 껎; 껎; 껎; ) HANGUL SYLLABLE GGEOBS +AECF;AECF;1101 1165 11BA;AECF;1101 1165 11BA; # (껏; 껏; 껏; 껏; 껏; ) HANGUL SYLLABLE GGEOS +AED0;AED0;1101 1165 11BB;AED0;1101 1165 11BB; # (껐; 껐; 껐; 껐; 껐; ) HANGUL SYLLABLE GGEOSS +AED1;AED1;1101 1165 11BC;AED1;1101 1165 11BC; # (껑; 껑; 껑; 껑; 껑; ) HANGUL SYLLABLE GGEONG +AED2;AED2;1101 1165 11BD;AED2;1101 1165 11BD; # (껒; 껒; 껒; 껒; 껒; ) HANGUL SYLLABLE GGEOJ +AED3;AED3;1101 1165 11BE;AED3;1101 1165 11BE; # (껓; 껓; 껓; 껓; 껓; ) HANGUL SYLLABLE GGEOC +AED4;AED4;1101 1165 11BF;AED4;1101 1165 11BF; # (껔; 껔; 껔; 껔; 껔; ) HANGUL SYLLABLE GGEOK +AED5;AED5;1101 1165 11C0;AED5;1101 1165 11C0; # (껕; 껕; 껕; 껕; 껕; ) HANGUL SYLLABLE GGEOT +AED6;AED6;1101 1165 11C1;AED6;1101 1165 11C1; # (껖; 껖; 껖; 껖; 껖; ) HANGUL SYLLABLE GGEOP +AED7;AED7;1101 1165 11C2;AED7;1101 1165 11C2; # (껗; 껗; 껗; 껗; 껗; ) HANGUL SYLLABLE GGEOH +AED8;AED8;1101 1166;AED8;1101 1166; # (께; 께; 께; 께; 께; ) HANGUL SYLLABLE GGE +AED9;AED9;1101 1166 11A8;AED9;1101 1166 11A8; # (껙; 껙; 껙; 껙; 껙; ) HANGUL SYLLABLE GGEG +AEDA;AEDA;1101 1166 11A9;AEDA;1101 1166 11A9; # (껚; 껚; 껚; 껚; 껚; ) HANGUL SYLLABLE GGEGG +AEDB;AEDB;1101 1166 11AA;AEDB;1101 1166 11AA; # (껛; 껛; 껛; 껛; 껛; ) HANGUL SYLLABLE GGEGS +AEDC;AEDC;1101 1166 11AB;AEDC;1101 1166 11AB; # (껜; 껜; 껜; 껜; 껜; ) HANGUL SYLLABLE GGEN +AEDD;AEDD;1101 1166 11AC;AEDD;1101 1166 11AC; # (껝; 껝; 껝; 껝; 껝; ) HANGUL SYLLABLE GGENJ +AEDE;AEDE;1101 1166 11AD;AEDE;1101 1166 11AD; # (껞; 껞; 껞; 껞; 껞; ) HANGUL SYLLABLE GGENH +AEDF;AEDF;1101 1166 11AE;AEDF;1101 1166 11AE; # (껟; 껟; 껟; 껟; 껟; ) HANGUL SYLLABLE GGED +AEE0;AEE0;1101 1166 11AF;AEE0;1101 1166 11AF; # (껠; 껠; 껠; 껠; 껠; ) HANGUL SYLLABLE GGEL +AEE1;AEE1;1101 1166 11B0;AEE1;1101 1166 11B0; # (껡; 껡; 껡; 껡; 껡; ) HANGUL SYLLABLE GGELG +AEE2;AEE2;1101 1166 11B1;AEE2;1101 1166 11B1; # (껢; 껢; 껢; 껢; 껢; ) HANGUL SYLLABLE GGELM +AEE3;AEE3;1101 1166 11B2;AEE3;1101 1166 11B2; # (껣; 껣; 껣; 껣; 껣; ) HANGUL SYLLABLE GGELB +AEE4;AEE4;1101 1166 11B3;AEE4;1101 1166 11B3; # (껤; 껤; 껤; 껤; 껤; ) HANGUL SYLLABLE GGELS +AEE5;AEE5;1101 1166 11B4;AEE5;1101 1166 11B4; # (껥; 껥; 껥; 껥; 껥; ) HANGUL SYLLABLE GGELT +AEE6;AEE6;1101 1166 11B5;AEE6;1101 1166 11B5; # (껦; 껦; 껦; 껦; 껦; ) HANGUL SYLLABLE GGELP +AEE7;AEE7;1101 1166 11B6;AEE7;1101 1166 11B6; # (껧; 껧; 껧; 껧; 껧; ) HANGUL SYLLABLE GGELH +AEE8;AEE8;1101 1166 11B7;AEE8;1101 1166 11B7; # (껨; 껨; 껨; 껨; 껨; ) HANGUL SYLLABLE GGEM +AEE9;AEE9;1101 1166 11B8;AEE9;1101 1166 11B8; # (껩; 껩; 껩; 껩; 껩; ) HANGUL SYLLABLE GGEB +AEEA;AEEA;1101 1166 11B9;AEEA;1101 1166 11B9; # (껪; 껪; 껪; 껪; 껪; ) HANGUL SYLLABLE GGEBS +AEEB;AEEB;1101 1166 11BA;AEEB;1101 1166 11BA; # (껫; 껫; 껫; 껫; 껫; ) HANGUL SYLLABLE GGES +AEEC;AEEC;1101 1166 11BB;AEEC;1101 1166 11BB; # (껬; 껬; 껬; 껬; 껬; ) HANGUL SYLLABLE GGESS +AEED;AEED;1101 1166 11BC;AEED;1101 1166 11BC; # (껭; 껭; 껭; 껭; 껭; ) HANGUL SYLLABLE GGENG +AEEE;AEEE;1101 1166 11BD;AEEE;1101 1166 11BD; # (껮; 껮; 껮; 껮; 껮; ) HANGUL SYLLABLE GGEJ +AEEF;AEEF;1101 1166 11BE;AEEF;1101 1166 11BE; # (껯; 껯; 껯; 껯; 껯; ) HANGUL SYLLABLE GGEC +AEF0;AEF0;1101 1166 11BF;AEF0;1101 1166 11BF; # (껰; 껰; 껰; 껰; 껰; ) HANGUL SYLLABLE GGEK +AEF1;AEF1;1101 1166 11C0;AEF1;1101 1166 11C0; # (껱; 껱; 껱; 껱; 껱; ) HANGUL SYLLABLE GGET +AEF2;AEF2;1101 1166 11C1;AEF2;1101 1166 11C1; # (껲; 껲; 껲; 껲; 껲; ) HANGUL SYLLABLE GGEP +AEF3;AEF3;1101 1166 11C2;AEF3;1101 1166 11C2; # (껳; 껳; 껳; 껳; 껳; ) HANGUL SYLLABLE GGEH +AEF4;AEF4;1101 1167;AEF4;1101 1167; # (껴; 껴; 껴; 껴; 껴; ) HANGUL SYLLABLE GGYEO +AEF5;AEF5;1101 1167 11A8;AEF5;1101 1167 11A8; # (껵; 껵; 껵; 껵; 껵; ) HANGUL SYLLABLE GGYEOG +AEF6;AEF6;1101 1167 11A9;AEF6;1101 1167 11A9; # (껶; 껶; 껶; 껶; 껶; ) HANGUL SYLLABLE GGYEOGG +AEF7;AEF7;1101 1167 11AA;AEF7;1101 1167 11AA; # (껷; 껷; 껷; 껷; 껷; ) HANGUL SYLLABLE GGYEOGS +AEF8;AEF8;1101 1167 11AB;AEF8;1101 1167 11AB; # (껸; 껸; 껸; 껸; 껸; ) HANGUL SYLLABLE GGYEON +AEF9;AEF9;1101 1167 11AC;AEF9;1101 1167 11AC; # (껹; 껹; 껹; 껹; 껹; ) HANGUL SYLLABLE GGYEONJ +AEFA;AEFA;1101 1167 11AD;AEFA;1101 1167 11AD; # (껺; 껺; 껺; 껺; 껺; ) HANGUL SYLLABLE GGYEONH +AEFB;AEFB;1101 1167 11AE;AEFB;1101 1167 11AE; # (껻; 껻; 껻; 껻; 껻; ) HANGUL SYLLABLE GGYEOD +AEFC;AEFC;1101 1167 11AF;AEFC;1101 1167 11AF; # (껼; 껼; 껼; 껼; 껼; ) HANGUL SYLLABLE GGYEOL +AEFD;AEFD;1101 1167 11B0;AEFD;1101 1167 11B0; # (껽; 껽; 껽; 껽; 껽; ) HANGUL SYLLABLE GGYEOLG +AEFE;AEFE;1101 1167 11B1;AEFE;1101 1167 11B1; # (껾; 껾; 껾; 껾; 껾; ) HANGUL SYLLABLE GGYEOLM +AEFF;AEFF;1101 1167 11B2;AEFF;1101 1167 11B2; # (껿; 껿; 껿; 껿; 껿; ) HANGUL SYLLABLE GGYEOLB +AF00;AF00;1101 1167 11B3;AF00;1101 1167 11B3; # (꼀; 꼀; 꼀; 꼀; 꼀; ) HANGUL SYLLABLE GGYEOLS +AF01;AF01;1101 1167 11B4;AF01;1101 1167 11B4; # (꼁; 꼁; 꼁; 꼁; 꼁; ) HANGUL SYLLABLE GGYEOLT +AF02;AF02;1101 1167 11B5;AF02;1101 1167 11B5; # (꼂; 꼂; 꼂; 꼂; 꼂; ) HANGUL SYLLABLE GGYEOLP +AF03;AF03;1101 1167 11B6;AF03;1101 1167 11B6; # (꼃; 꼃; 꼃; 꼃; 꼃; ) HANGUL SYLLABLE GGYEOLH +AF04;AF04;1101 1167 11B7;AF04;1101 1167 11B7; # (꼄; 꼄; 꼄; 꼄; 꼄; ) HANGUL SYLLABLE GGYEOM +AF05;AF05;1101 1167 11B8;AF05;1101 1167 11B8; # (꼅; 꼅; 꼅; 꼅; 꼅; ) HANGUL SYLLABLE GGYEOB +AF06;AF06;1101 1167 11B9;AF06;1101 1167 11B9; # (꼆; 꼆; 꼆; 꼆; 꼆; ) HANGUL SYLLABLE GGYEOBS +AF07;AF07;1101 1167 11BA;AF07;1101 1167 11BA; # (꼇; 꼇; 꼇; 꼇; 꼇; ) HANGUL SYLLABLE GGYEOS +AF08;AF08;1101 1167 11BB;AF08;1101 1167 11BB; # (꼈; 꼈; 꼈; 꼈; 꼈; ) HANGUL SYLLABLE GGYEOSS +AF09;AF09;1101 1167 11BC;AF09;1101 1167 11BC; # (꼉; 꼉; 꼉; 꼉; 꼉; ) HANGUL SYLLABLE GGYEONG +AF0A;AF0A;1101 1167 11BD;AF0A;1101 1167 11BD; # (꼊; 꼊; 꼊; 꼊; 꼊; ) HANGUL SYLLABLE GGYEOJ +AF0B;AF0B;1101 1167 11BE;AF0B;1101 1167 11BE; # (꼋; 꼋; 꼋; 꼋; 꼋; ) HANGUL SYLLABLE GGYEOC +AF0C;AF0C;1101 1167 11BF;AF0C;1101 1167 11BF; # (꼌; 꼌; 꼌; 꼌; 꼌; ) HANGUL SYLLABLE GGYEOK +AF0D;AF0D;1101 1167 11C0;AF0D;1101 1167 11C0; # (꼍; 꼍; 꼍; 꼍; 꼍; ) HANGUL SYLLABLE GGYEOT +AF0E;AF0E;1101 1167 11C1;AF0E;1101 1167 11C1; # (꼎; 꼎; 꼎; 꼎; 꼎; ) HANGUL SYLLABLE GGYEOP +AF0F;AF0F;1101 1167 11C2;AF0F;1101 1167 11C2; # (꼏; 꼏; 꼏; 꼏; 꼏; ) HANGUL SYLLABLE GGYEOH +AF10;AF10;1101 1168;AF10;1101 1168; # (꼐; 꼐; 꼐; 꼐; 꼐; ) HANGUL SYLLABLE GGYE +AF11;AF11;1101 1168 11A8;AF11;1101 1168 11A8; # (꼑; 꼑; 꼑; 꼑; 꼑; ) HANGUL SYLLABLE GGYEG +AF12;AF12;1101 1168 11A9;AF12;1101 1168 11A9; # (꼒; 꼒; 꼒; 꼒; 꼒; ) HANGUL SYLLABLE GGYEGG +AF13;AF13;1101 1168 11AA;AF13;1101 1168 11AA; # (꼓; 꼓; 꼓; 꼓; 꼓; ) HANGUL SYLLABLE GGYEGS +AF14;AF14;1101 1168 11AB;AF14;1101 1168 11AB; # (꼔; 꼔; 꼔; 꼔; 꼔; ) HANGUL SYLLABLE GGYEN +AF15;AF15;1101 1168 11AC;AF15;1101 1168 11AC; # (꼕; 꼕; 꼕; 꼕; 꼕; ) HANGUL SYLLABLE GGYENJ +AF16;AF16;1101 1168 11AD;AF16;1101 1168 11AD; # (꼖; 꼖; 꼖; 꼖; 꼖; ) HANGUL SYLLABLE GGYENH +AF17;AF17;1101 1168 11AE;AF17;1101 1168 11AE; # (꼗; 꼗; 꼗; 꼗; 꼗; ) HANGUL SYLLABLE GGYED +AF18;AF18;1101 1168 11AF;AF18;1101 1168 11AF; # (꼘; 꼘; 꼘; 꼘; 꼘; ) HANGUL SYLLABLE GGYEL +AF19;AF19;1101 1168 11B0;AF19;1101 1168 11B0; # (꼙; 꼙; 꼙; 꼙; 꼙; ) HANGUL SYLLABLE GGYELG +AF1A;AF1A;1101 1168 11B1;AF1A;1101 1168 11B1; # (꼚; 꼚; 꼚; 꼚; 꼚; ) HANGUL SYLLABLE GGYELM +AF1B;AF1B;1101 1168 11B2;AF1B;1101 1168 11B2; # (꼛; 꼛; 꼛; 꼛; 꼛; ) HANGUL SYLLABLE GGYELB +AF1C;AF1C;1101 1168 11B3;AF1C;1101 1168 11B3; # (꼜; 꼜; 꼜; 꼜; 꼜; ) HANGUL SYLLABLE GGYELS +AF1D;AF1D;1101 1168 11B4;AF1D;1101 1168 11B4; # (꼝; 꼝; 꼝; 꼝; 꼝; ) HANGUL SYLLABLE GGYELT +AF1E;AF1E;1101 1168 11B5;AF1E;1101 1168 11B5; # (꼞; 꼞; 꼞; 꼞; 꼞; ) HANGUL SYLLABLE GGYELP +AF1F;AF1F;1101 1168 11B6;AF1F;1101 1168 11B6; # (꼟; 꼟; 꼟; 꼟; 꼟; ) HANGUL SYLLABLE GGYELH +AF20;AF20;1101 1168 11B7;AF20;1101 1168 11B7; # (꼠; 꼠; 꼠; 꼠; 꼠; ) HANGUL SYLLABLE GGYEM +AF21;AF21;1101 1168 11B8;AF21;1101 1168 11B8; # (꼡; 꼡; 꼡; 꼡; 꼡; ) HANGUL SYLLABLE GGYEB +AF22;AF22;1101 1168 11B9;AF22;1101 1168 11B9; # (꼢; 꼢; 꼢; 꼢; 꼢; ) HANGUL SYLLABLE GGYEBS +AF23;AF23;1101 1168 11BA;AF23;1101 1168 11BA; # (꼣; 꼣; 꼣; 꼣; 꼣; ) HANGUL SYLLABLE GGYES +AF24;AF24;1101 1168 11BB;AF24;1101 1168 11BB; # (꼤; 꼤; 꼤; 꼤; 꼤; ) HANGUL SYLLABLE GGYESS +AF25;AF25;1101 1168 11BC;AF25;1101 1168 11BC; # (꼥; 꼥; 꼥; 꼥; 꼥; ) HANGUL SYLLABLE GGYENG +AF26;AF26;1101 1168 11BD;AF26;1101 1168 11BD; # (꼦; 꼦; 꼦; 꼦; 꼦; ) HANGUL SYLLABLE GGYEJ +AF27;AF27;1101 1168 11BE;AF27;1101 1168 11BE; # (꼧; 꼧; 꼧; 꼧; 꼧; ) HANGUL SYLLABLE GGYEC +AF28;AF28;1101 1168 11BF;AF28;1101 1168 11BF; # (꼨; 꼨; 꼨; 꼨; 꼨; ) HANGUL SYLLABLE GGYEK +AF29;AF29;1101 1168 11C0;AF29;1101 1168 11C0; # (꼩; 꼩; 꼩; 꼩; 꼩; ) HANGUL SYLLABLE GGYET +AF2A;AF2A;1101 1168 11C1;AF2A;1101 1168 11C1; # (꼪; 꼪; 꼪; 꼪; 꼪; ) HANGUL SYLLABLE GGYEP +AF2B;AF2B;1101 1168 11C2;AF2B;1101 1168 11C2; # (꼫; 꼫; 꼫; 꼫; 꼫; ) HANGUL SYLLABLE GGYEH +AF2C;AF2C;1101 1169;AF2C;1101 1169; # (꼬; 꼬; 꼬; 꼬; 꼬; ) HANGUL SYLLABLE GGO +AF2D;AF2D;1101 1169 11A8;AF2D;1101 1169 11A8; # (꼭; 꼭; 꼭; 꼭; 꼭; ) HANGUL SYLLABLE GGOG +AF2E;AF2E;1101 1169 11A9;AF2E;1101 1169 11A9; # (꼮; 꼮; 꼮; 꼮; 꼮; ) HANGUL SYLLABLE GGOGG +AF2F;AF2F;1101 1169 11AA;AF2F;1101 1169 11AA; # (꼯; 꼯; 꼯; 꼯; 꼯; ) HANGUL SYLLABLE GGOGS +AF30;AF30;1101 1169 11AB;AF30;1101 1169 11AB; # (꼰; 꼰; 꼰; 꼰; 꼰; ) HANGUL SYLLABLE GGON +AF31;AF31;1101 1169 11AC;AF31;1101 1169 11AC; # (꼱; 꼱; 꼱; 꼱; 꼱; ) HANGUL SYLLABLE GGONJ +AF32;AF32;1101 1169 11AD;AF32;1101 1169 11AD; # (꼲; 꼲; 꼲; 꼲; 꼲; ) HANGUL SYLLABLE GGONH +AF33;AF33;1101 1169 11AE;AF33;1101 1169 11AE; # (꼳; 꼳; 꼳; 꼳; 꼳; ) HANGUL SYLLABLE GGOD +AF34;AF34;1101 1169 11AF;AF34;1101 1169 11AF; # (꼴; 꼴; 꼴; 꼴; 꼴; ) HANGUL SYLLABLE GGOL +AF35;AF35;1101 1169 11B0;AF35;1101 1169 11B0; # (꼵; 꼵; 꼵; 꼵; 꼵; ) HANGUL SYLLABLE GGOLG +AF36;AF36;1101 1169 11B1;AF36;1101 1169 11B1; # (꼶; 꼶; 꼶; 꼶; 꼶; ) HANGUL SYLLABLE GGOLM +AF37;AF37;1101 1169 11B2;AF37;1101 1169 11B2; # (꼷; 꼷; 꼷; 꼷; 꼷; ) HANGUL SYLLABLE GGOLB +AF38;AF38;1101 1169 11B3;AF38;1101 1169 11B3; # (꼸; 꼸; 꼸; 꼸; 꼸; ) HANGUL SYLLABLE GGOLS +AF39;AF39;1101 1169 11B4;AF39;1101 1169 11B4; # (꼹; 꼹; 꼹; 꼹; 꼹; ) HANGUL SYLLABLE GGOLT +AF3A;AF3A;1101 1169 11B5;AF3A;1101 1169 11B5; # (꼺; 꼺; 꼺; 꼺; 꼺; ) HANGUL SYLLABLE GGOLP +AF3B;AF3B;1101 1169 11B6;AF3B;1101 1169 11B6; # (꼻; 꼻; 꼻; 꼻; 꼻; ) HANGUL SYLLABLE GGOLH +AF3C;AF3C;1101 1169 11B7;AF3C;1101 1169 11B7; # (꼼; 꼼; 꼼; 꼼; 꼼; ) HANGUL SYLLABLE GGOM +AF3D;AF3D;1101 1169 11B8;AF3D;1101 1169 11B8; # (꼽; 꼽; 꼽; 꼽; 꼽; ) HANGUL SYLLABLE GGOB +AF3E;AF3E;1101 1169 11B9;AF3E;1101 1169 11B9; # (꼾; 꼾; 꼾; 꼾; 꼾; ) HANGUL SYLLABLE GGOBS +AF3F;AF3F;1101 1169 11BA;AF3F;1101 1169 11BA; # (꼿; 꼿; 꼿; 꼿; 꼿; ) HANGUL SYLLABLE GGOS +AF40;AF40;1101 1169 11BB;AF40;1101 1169 11BB; # (꽀; 꽀; 꽀; 꽀; 꽀; ) HANGUL SYLLABLE GGOSS +AF41;AF41;1101 1169 11BC;AF41;1101 1169 11BC; # (꽁; 꽁; 꽁; 꽁; 꽁; ) HANGUL SYLLABLE GGONG +AF42;AF42;1101 1169 11BD;AF42;1101 1169 11BD; # (꽂; 꽂; 꽂; 꽂; 꽂; ) HANGUL SYLLABLE GGOJ +AF43;AF43;1101 1169 11BE;AF43;1101 1169 11BE; # (꽃; 꽃; 꽃; 꽃; 꽃; ) HANGUL SYLLABLE GGOC +AF44;AF44;1101 1169 11BF;AF44;1101 1169 11BF; # (꽄; 꽄; 꽄; 꽄; 꽄; ) HANGUL SYLLABLE GGOK +AF45;AF45;1101 1169 11C0;AF45;1101 1169 11C0; # (꽅; 꽅; 꽅; 꽅; 꽅; ) HANGUL SYLLABLE GGOT +AF46;AF46;1101 1169 11C1;AF46;1101 1169 11C1; # (꽆; 꽆; 꽆; 꽆; 꽆; ) HANGUL SYLLABLE GGOP +AF47;AF47;1101 1169 11C2;AF47;1101 1169 11C2; # (꽇; 꽇; 꽇; 꽇; 꽇; ) HANGUL SYLLABLE GGOH +AF48;AF48;1101 116A;AF48;1101 116A; # (꽈; 꽈; 꽈; 꽈; 꽈; ) HANGUL SYLLABLE GGWA +AF49;AF49;1101 116A 11A8;AF49;1101 116A 11A8; # (꽉; 꽉; 꽉; 꽉; 꽉; ) HANGUL SYLLABLE GGWAG +AF4A;AF4A;1101 116A 11A9;AF4A;1101 116A 11A9; # (꽊; 꽊; 꽊; 꽊; 꽊; ) HANGUL SYLLABLE GGWAGG +AF4B;AF4B;1101 116A 11AA;AF4B;1101 116A 11AA; # (꽋; 꽋; 꽋; 꽋; 꽋; ) HANGUL SYLLABLE GGWAGS +AF4C;AF4C;1101 116A 11AB;AF4C;1101 116A 11AB; # (꽌; 꽌; 꽌; 꽌; 꽌; ) HANGUL SYLLABLE GGWAN +AF4D;AF4D;1101 116A 11AC;AF4D;1101 116A 11AC; # (꽍; 꽍; 꽍; 꽍; 꽍; ) HANGUL SYLLABLE GGWANJ +AF4E;AF4E;1101 116A 11AD;AF4E;1101 116A 11AD; # (꽎; 꽎; 꽎; 꽎; 꽎; ) HANGUL SYLLABLE GGWANH +AF4F;AF4F;1101 116A 11AE;AF4F;1101 116A 11AE; # (꽏; 꽏; 꽏; 꽏; 꽏; ) HANGUL SYLLABLE GGWAD +AF50;AF50;1101 116A 11AF;AF50;1101 116A 11AF; # (꽐; 꽐; 꽐; 꽐; 꽐; ) HANGUL SYLLABLE GGWAL +AF51;AF51;1101 116A 11B0;AF51;1101 116A 11B0; # (꽑; 꽑; 꽑; 꽑; 꽑; ) HANGUL SYLLABLE GGWALG +AF52;AF52;1101 116A 11B1;AF52;1101 116A 11B1; # (꽒; 꽒; 꽒; 꽒; 꽒; ) HANGUL SYLLABLE GGWALM +AF53;AF53;1101 116A 11B2;AF53;1101 116A 11B2; # (꽓; 꽓; 꽓; 꽓; 꽓; ) HANGUL SYLLABLE GGWALB +AF54;AF54;1101 116A 11B3;AF54;1101 116A 11B3; # (꽔; 꽔; 꽔; 꽔; 꽔; ) HANGUL SYLLABLE GGWALS +AF55;AF55;1101 116A 11B4;AF55;1101 116A 11B4; # (꽕; 꽕; 꽕; 꽕; 꽕; ) HANGUL SYLLABLE GGWALT +AF56;AF56;1101 116A 11B5;AF56;1101 116A 11B5; # (꽖; 꽖; 꽖; 꽖; 꽖; ) HANGUL SYLLABLE GGWALP +AF57;AF57;1101 116A 11B6;AF57;1101 116A 11B6; # (꽗; 꽗; 꽗; 꽗; 꽗; ) HANGUL SYLLABLE GGWALH +AF58;AF58;1101 116A 11B7;AF58;1101 116A 11B7; # (꽘; 꽘; 꽘; 꽘; 꽘; ) HANGUL SYLLABLE GGWAM +AF59;AF59;1101 116A 11B8;AF59;1101 116A 11B8; # (꽙; 꽙; 꽙; 꽙; 꽙; ) HANGUL SYLLABLE GGWAB +AF5A;AF5A;1101 116A 11B9;AF5A;1101 116A 11B9; # (꽚; 꽚; 꽚; 꽚; 꽚; ) HANGUL SYLLABLE GGWABS +AF5B;AF5B;1101 116A 11BA;AF5B;1101 116A 11BA; # (꽛; 꽛; 꽛; 꽛; 꽛; ) HANGUL SYLLABLE GGWAS +AF5C;AF5C;1101 116A 11BB;AF5C;1101 116A 11BB; # (꽜; 꽜; 꽜; 꽜; 꽜; ) HANGUL SYLLABLE GGWASS +AF5D;AF5D;1101 116A 11BC;AF5D;1101 116A 11BC; # (꽝; 꽝; 꽝; 꽝; 꽝; ) HANGUL SYLLABLE GGWANG +AF5E;AF5E;1101 116A 11BD;AF5E;1101 116A 11BD; # (꽞; 꽞; 꽞; 꽞; 꽞; ) HANGUL SYLLABLE GGWAJ +AF5F;AF5F;1101 116A 11BE;AF5F;1101 116A 11BE; # (꽟; 꽟; 꽟; 꽟; 꽟; ) HANGUL SYLLABLE GGWAC +AF60;AF60;1101 116A 11BF;AF60;1101 116A 11BF; # (꽠; 꽠; 꽠; 꽠; 꽠; ) HANGUL SYLLABLE GGWAK +AF61;AF61;1101 116A 11C0;AF61;1101 116A 11C0; # (꽡; 꽡; 꽡; 꽡; 꽡; ) HANGUL SYLLABLE GGWAT +AF62;AF62;1101 116A 11C1;AF62;1101 116A 11C1; # (꽢; 꽢; 꽢; 꽢; 꽢; ) HANGUL SYLLABLE GGWAP +AF63;AF63;1101 116A 11C2;AF63;1101 116A 11C2; # (꽣; 꽣; 꽣; 꽣; 꽣; ) HANGUL SYLLABLE GGWAH +AF64;AF64;1101 116B;AF64;1101 116B; # (꽤; 꽤; 꽤; 꽤; 꽤; ) HANGUL SYLLABLE GGWAE +AF65;AF65;1101 116B 11A8;AF65;1101 116B 11A8; # (꽥; 꽥; 꽥; 꽥; 꽥; ) HANGUL SYLLABLE GGWAEG +AF66;AF66;1101 116B 11A9;AF66;1101 116B 11A9; # (꽦; 꽦; 꽦; 꽦; 꽦; ) HANGUL SYLLABLE GGWAEGG +AF67;AF67;1101 116B 11AA;AF67;1101 116B 11AA; # (꽧; 꽧; 꽧; 꽧; 꽧; ) HANGUL SYLLABLE GGWAEGS +AF68;AF68;1101 116B 11AB;AF68;1101 116B 11AB; # (꽨; 꽨; 꽨; 꽨; 꽨; ) HANGUL SYLLABLE GGWAEN +AF69;AF69;1101 116B 11AC;AF69;1101 116B 11AC; # (꽩; 꽩; 꽩; 꽩; 꽩; ) HANGUL SYLLABLE GGWAENJ +AF6A;AF6A;1101 116B 11AD;AF6A;1101 116B 11AD; # (꽪; 꽪; 꽪; 꽪; 꽪; ) HANGUL SYLLABLE GGWAENH +AF6B;AF6B;1101 116B 11AE;AF6B;1101 116B 11AE; # (꽫; 꽫; 꽫; 꽫; 꽫; ) HANGUL SYLLABLE GGWAED +AF6C;AF6C;1101 116B 11AF;AF6C;1101 116B 11AF; # (꽬; 꽬; 꽬; 꽬; 꽬; ) HANGUL SYLLABLE GGWAEL +AF6D;AF6D;1101 116B 11B0;AF6D;1101 116B 11B0; # (꽭; 꽭; 꽭; 꽭; 꽭; ) HANGUL SYLLABLE GGWAELG +AF6E;AF6E;1101 116B 11B1;AF6E;1101 116B 11B1; # (꽮; 꽮; 꽮; 꽮; 꽮; ) HANGUL SYLLABLE GGWAELM +AF6F;AF6F;1101 116B 11B2;AF6F;1101 116B 11B2; # (꽯; 꽯; 꽯; 꽯; 꽯; ) HANGUL SYLLABLE GGWAELB +AF70;AF70;1101 116B 11B3;AF70;1101 116B 11B3; # (꽰; 꽰; 꽰; 꽰; 꽰; ) HANGUL SYLLABLE GGWAELS +AF71;AF71;1101 116B 11B4;AF71;1101 116B 11B4; # (꽱; 꽱; 꽱; 꽱; 꽱; ) HANGUL SYLLABLE GGWAELT +AF72;AF72;1101 116B 11B5;AF72;1101 116B 11B5; # (꽲; 꽲; 꽲; 꽲; 꽲; ) HANGUL SYLLABLE GGWAELP +AF73;AF73;1101 116B 11B6;AF73;1101 116B 11B6; # (꽳; 꽳; 꽳; 꽳; 꽳; ) HANGUL SYLLABLE GGWAELH +AF74;AF74;1101 116B 11B7;AF74;1101 116B 11B7; # (꽴; 꽴; 꽴; 꽴; 꽴; ) HANGUL SYLLABLE GGWAEM +AF75;AF75;1101 116B 11B8;AF75;1101 116B 11B8; # (꽵; 꽵; 꽵; 꽵; 꽵; ) HANGUL SYLLABLE GGWAEB +AF76;AF76;1101 116B 11B9;AF76;1101 116B 11B9; # (꽶; 꽶; 꽶; 꽶; 꽶; ) HANGUL SYLLABLE GGWAEBS +AF77;AF77;1101 116B 11BA;AF77;1101 116B 11BA; # (꽷; 꽷; 꽷; 꽷; 꽷; ) HANGUL SYLLABLE GGWAES +AF78;AF78;1101 116B 11BB;AF78;1101 116B 11BB; # (꽸; 꽸; 꽸; 꽸; 꽸; ) HANGUL SYLLABLE GGWAESS +AF79;AF79;1101 116B 11BC;AF79;1101 116B 11BC; # (꽹; 꽹; 꽹; 꽹; 꽹; ) HANGUL SYLLABLE GGWAENG +AF7A;AF7A;1101 116B 11BD;AF7A;1101 116B 11BD; # (꽺; 꽺; 꽺; 꽺; 꽺; ) HANGUL SYLLABLE GGWAEJ +AF7B;AF7B;1101 116B 11BE;AF7B;1101 116B 11BE; # (꽻; 꽻; 꽻; 꽻; 꽻; ) HANGUL SYLLABLE GGWAEC +AF7C;AF7C;1101 116B 11BF;AF7C;1101 116B 11BF; # (꽼; 꽼; 꽼; 꽼; 꽼; ) HANGUL SYLLABLE GGWAEK +AF7D;AF7D;1101 116B 11C0;AF7D;1101 116B 11C0; # (꽽; 꽽; 꽽; 꽽; 꽽; ) HANGUL SYLLABLE GGWAET +AF7E;AF7E;1101 116B 11C1;AF7E;1101 116B 11C1; # (꽾; 꽾; 꽾; 꽾; 꽾; ) HANGUL SYLLABLE GGWAEP +AF7F;AF7F;1101 116B 11C2;AF7F;1101 116B 11C2; # (꽿; 꽿; 꽿; 꽿; 꽿; ) HANGUL SYLLABLE GGWAEH +AF80;AF80;1101 116C;AF80;1101 116C; # (꾀; 꾀; 꾀; 꾀; 꾀; ) HANGUL SYLLABLE GGOE +AF81;AF81;1101 116C 11A8;AF81;1101 116C 11A8; # (꾁; 꾁; 꾁; 꾁; 꾁; ) HANGUL SYLLABLE GGOEG +AF82;AF82;1101 116C 11A9;AF82;1101 116C 11A9; # (꾂; 꾂; 꾂; 꾂; 꾂; ) HANGUL SYLLABLE GGOEGG +AF83;AF83;1101 116C 11AA;AF83;1101 116C 11AA; # (꾃; 꾃; 꾃; 꾃; 꾃; ) HANGUL SYLLABLE GGOEGS +AF84;AF84;1101 116C 11AB;AF84;1101 116C 11AB; # (꾄; 꾄; 꾄; 꾄; 꾄; ) HANGUL SYLLABLE GGOEN +AF85;AF85;1101 116C 11AC;AF85;1101 116C 11AC; # (꾅; 꾅; 꾅; 꾅; 꾅; ) HANGUL SYLLABLE GGOENJ +AF86;AF86;1101 116C 11AD;AF86;1101 116C 11AD; # (꾆; 꾆; 꾆; 꾆; 꾆; ) HANGUL SYLLABLE GGOENH +AF87;AF87;1101 116C 11AE;AF87;1101 116C 11AE; # (꾇; 꾇; 꾇; 꾇; 꾇; ) HANGUL SYLLABLE GGOED +AF88;AF88;1101 116C 11AF;AF88;1101 116C 11AF; # (꾈; 꾈; 꾈; 꾈; 꾈; ) HANGUL SYLLABLE GGOEL +AF89;AF89;1101 116C 11B0;AF89;1101 116C 11B0; # (꾉; 꾉; 꾉; 꾉; 꾉; ) HANGUL SYLLABLE GGOELG +AF8A;AF8A;1101 116C 11B1;AF8A;1101 116C 11B1; # (꾊; 꾊; 꾊; 꾊; 꾊; ) HANGUL SYLLABLE GGOELM +AF8B;AF8B;1101 116C 11B2;AF8B;1101 116C 11B2; # (꾋; 꾋; 꾋; 꾋; 꾋; ) HANGUL SYLLABLE GGOELB +AF8C;AF8C;1101 116C 11B3;AF8C;1101 116C 11B3; # (꾌; 꾌; 꾌; 꾌; 꾌; ) HANGUL SYLLABLE GGOELS +AF8D;AF8D;1101 116C 11B4;AF8D;1101 116C 11B4; # (꾍; 꾍; 꾍; 꾍; 꾍; ) HANGUL SYLLABLE GGOELT +AF8E;AF8E;1101 116C 11B5;AF8E;1101 116C 11B5; # (꾎; 꾎; 꾎; 꾎; 꾎; ) HANGUL SYLLABLE GGOELP +AF8F;AF8F;1101 116C 11B6;AF8F;1101 116C 11B6; # (꾏; 꾏; 꾏; 꾏; 꾏; ) HANGUL SYLLABLE GGOELH +AF90;AF90;1101 116C 11B7;AF90;1101 116C 11B7; # (꾐; 꾐; 꾐; 꾐; 꾐; ) HANGUL SYLLABLE GGOEM +AF91;AF91;1101 116C 11B8;AF91;1101 116C 11B8; # (꾑; 꾑; 꾑; 꾑; 꾑; ) HANGUL SYLLABLE GGOEB +AF92;AF92;1101 116C 11B9;AF92;1101 116C 11B9; # (꾒; 꾒; 꾒; 꾒; 꾒; ) HANGUL SYLLABLE GGOEBS +AF93;AF93;1101 116C 11BA;AF93;1101 116C 11BA; # (꾓; 꾓; 꾓; 꾓; 꾓; ) HANGUL SYLLABLE GGOES +AF94;AF94;1101 116C 11BB;AF94;1101 116C 11BB; # (꾔; 꾔; 꾔; 꾔; 꾔; ) HANGUL SYLLABLE GGOESS +AF95;AF95;1101 116C 11BC;AF95;1101 116C 11BC; # (꾕; 꾕; 꾕; 꾕; 꾕; ) HANGUL SYLLABLE GGOENG +AF96;AF96;1101 116C 11BD;AF96;1101 116C 11BD; # (꾖; 꾖; 꾖; 꾖; 꾖; ) HANGUL SYLLABLE GGOEJ +AF97;AF97;1101 116C 11BE;AF97;1101 116C 11BE; # (꾗; 꾗; 꾗; 꾗; 꾗; ) HANGUL SYLLABLE GGOEC +AF98;AF98;1101 116C 11BF;AF98;1101 116C 11BF; # (꾘; 꾘; 꾘; 꾘; 꾘; ) HANGUL SYLLABLE GGOEK +AF99;AF99;1101 116C 11C0;AF99;1101 116C 11C0; # (꾙; 꾙; 꾙; 꾙; 꾙; ) HANGUL SYLLABLE GGOET +AF9A;AF9A;1101 116C 11C1;AF9A;1101 116C 11C1; # (꾚; 꾚; 꾚; 꾚; 꾚; ) HANGUL SYLLABLE GGOEP +AF9B;AF9B;1101 116C 11C2;AF9B;1101 116C 11C2; # (꾛; 꾛; 꾛; 꾛; 꾛; ) HANGUL SYLLABLE GGOEH +AF9C;AF9C;1101 116D;AF9C;1101 116D; # (꾜; 꾜; 꾜; 꾜; 꾜; ) HANGUL SYLLABLE GGYO +AF9D;AF9D;1101 116D 11A8;AF9D;1101 116D 11A8; # (꾝; 꾝; 꾝; 꾝; 꾝; ) HANGUL SYLLABLE GGYOG +AF9E;AF9E;1101 116D 11A9;AF9E;1101 116D 11A9; # (꾞; 꾞; 꾞; 꾞; 꾞; ) HANGUL SYLLABLE GGYOGG +AF9F;AF9F;1101 116D 11AA;AF9F;1101 116D 11AA; # (꾟; 꾟; 꾟; 꾟; 꾟; ) HANGUL SYLLABLE GGYOGS +AFA0;AFA0;1101 116D 11AB;AFA0;1101 116D 11AB; # (꾠; 꾠; 꾠; 꾠; 꾠; ) HANGUL SYLLABLE GGYON +AFA1;AFA1;1101 116D 11AC;AFA1;1101 116D 11AC; # (꾡; 꾡; 꾡; 꾡; 꾡; ) HANGUL SYLLABLE GGYONJ +AFA2;AFA2;1101 116D 11AD;AFA2;1101 116D 11AD; # (꾢; 꾢; 꾢; 꾢; 꾢; ) HANGUL SYLLABLE GGYONH +AFA3;AFA3;1101 116D 11AE;AFA3;1101 116D 11AE; # (꾣; 꾣; 꾣; 꾣; 꾣; ) HANGUL SYLLABLE GGYOD +AFA4;AFA4;1101 116D 11AF;AFA4;1101 116D 11AF; # (꾤; 꾤; 꾤; 꾤; 꾤; ) HANGUL SYLLABLE GGYOL +AFA5;AFA5;1101 116D 11B0;AFA5;1101 116D 11B0; # (꾥; 꾥; 꾥; 꾥; 꾥; ) HANGUL SYLLABLE GGYOLG +AFA6;AFA6;1101 116D 11B1;AFA6;1101 116D 11B1; # (꾦; 꾦; 꾦; 꾦; 꾦; ) HANGUL SYLLABLE GGYOLM +AFA7;AFA7;1101 116D 11B2;AFA7;1101 116D 11B2; # (꾧; 꾧; 꾧; 꾧; 꾧; ) HANGUL SYLLABLE GGYOLB +AFA8;AFA8;1101 116D 11B3;AFA8;1101 116D 11B3; # (꾨; 꾨; 꾨; 꾨; 꾨; ) HANGUL SYLLABLE GGYOLS +AFA9;AFA9;1101 116D 11B4;AFA9;1101 116D 11B4; # (꾩; 꾩; 꾩; 꾩; 꾩; ) HANGUL SYLLABLE GGYOLT +AFAA;AFAA;1101 116D 11B5;AFAA;1101 116D 11B5; # (꾪; 꾪; 꾪; 꾪; 꾪; ) HANGUL SYLLABLE GGYOLP +AFAB;AFAB;1101 116D 11B6;AFAB;1101 116D 11B6; # (꾫; 꾫; 꾫; 꾫; 꾫; ) HANGUL SYLLABLE GGYOLH +AFAC;AFAC;1101 116D 11B7;AFAC;1101 116D 11B7; # (꾬; 꾬; 꾬; 꾬; 꾬; ) HANGUL SYLLABLE GGYOM +AFAD;AFAD;1101 116D 11B8;AFAD;1101 116D 11B8; # (꾭; 꾭; 꾭; 꾭; 꾭; ) HANGUL SYLLABLE GGYOB +AFAE;AFAE;1101 116D 11B9;AFAE;1101 116D 11B9; # (꾮; 꾮; 꾮; 꾮; 꾮; ) HANGUL SYLLABLE GGYOBS +AFAF;AFAF;1101 116D 11BA;AFAF;1101 116D 11BA; # (꾯; 꾯; 꾯; 꾯; 꾯; ) HANGUL SYLLABLE GGYOS +AFB0;AFB0;1101 116D 11BB;AFB0;1101 116D 11BB; # (꾰; 꾰; 꾰; 꾰; 꾰; ) HANGUL SYLLABLE GGYOSS +AFB1;AFB1;1101 116D 11BC;AFB1;1101 116D 11BC; # (꾱; 꾱; 꾱; 꾱; 꾱; ) HANGUL SYLLABLE GGYONG +AFB2;AFB2;1101 116D 11BD;AFB2;1101 116D 11BD; # (꾲; 꾲; 꾲; 꾲; 꾲; ) HANGUL SYLLABLE GGYOJ +AFB3;AFB3;1101 116D 11BE;AFB3;1101 116D 11BE; # (꾳; 꾳; 꾳; 꾳; 꾳; ) HANGUL SYLLABLE GGYOC +AFB4;AFB4;1101 116D 11BF;AFB4;1101 116D 11BF; # (꾴; 꾴; 꾴; 꾴; 꾴; ) HANGUL SYLLABLE GGYOK +AFB5;AFB5;1101 116D 11C0;AFB5;1101 116D 11C0; # (꾵; 꾵; 꾵; 꾵; 꾵; ) HANGUL SYLLABLE GGYOT +AFB6;AFB6;1101 116D 11C1;AFB6;1101 116D 11C1; # (꾶; 꾶; 꾶; 꾶; 꾶; ) HANGUL SYLLABLE GGYOP +AFB7;AFB7;1101 116D 11C2;AFB7;1101 116D 11C2; # (꾷; 꾷; 꾷; 꾷; 꾷; ) HANGUL SYLLABLE GGYOH +AFB8;AFB8;1101 116E;AFB8;1101 116E; # (꾸; 꾸; 꾸; 꾸; 꾸; ) HANGUL SYLLABLE GGU +AFB9;AFB9;1101 116E 11A8;AFB9;1101 116E 11A8; # (꾹; 꾹; 꾹; 꾹; 꾹; ) HANGUL SYLLABLE GGUG +AFBA;AFBA;1101 116E 11A9;AFBA;1101 116E 11A9; # (꾺; 꾺; 꾺; 꾺; 꾺; ) HANGUL SYLLABLE GGUGG +AFBB;AFBB;1101 116E 11AA;AFBB;1101 116E 11AA; # (꾻; 꾻; 꾻; 꾻; 꾻; ) HANGUL SYLLABLE GGUGS +AFBC;AFBC;1101 116E 11AB;AFBC;1101 116E 11AB; # (꾼; 꾼; 꾼; 꾼; 꾼; ) HANGUL SYLLABLE GGUN +AFBD;AFBD;1101 116E 11AC;AFBD;1101 116E 11AC; # (꾽; 꾽; 꾽; 꾽; 꾽; ) HANGUL SYLLABLE GGUNJ +AFBE;AFBE;1101 116E 11AD;AFBE;1101 116E 11AD; # (꾾; 꾾; 꾾; 꾾; 꾾; ) HANGUL SYLLABLE GGUNH +AFBF;AFBF;1101 116E 11AE;AFBF;1101 116E 11AE; # (꾿; 꾿; 꾿; 꾿; 꾿; ) HANGUL SYLLABLE GGUD +AFC0;AFC0;1101 116E 11AF;AFC0;1101 116E 11AF; # (꿀; 꿀; 꿀; 꿀; 꿀; ) HANGUL SYLLABLE GGUL +AFC1;AFC1;1101 116E 11B0;AFC1;1101 116E 11B0; # (꿁; 꿁; 꿁; 꿁; 꿁; ) HANGUL SYLLABLE GGULG +AFC2;AFC2;1101 116E 11B1;AFC2;1101 116E 11B1; # (꿂; 꿂; 꿂; 꿂; 꿂; ) HANGUL SYLLABLE GGULM +AFC3;AFC3;1101 116E 11B2;AFC3;1101 116E 11B2; # (꿃; 꿃; 꿃; 꿃; 꿃; ) HANGUL SYLLABLE GGULB +AFC4;AFC4;1101 116E 11B3;AFC4;1101 116E 11B3; # (꿄; 꿄; 꿄; 꿄; 꿄; ) HANGUL SYLLABLE GGULS +AFC5;AFC5;1101 116E 11B4;AFC5;1101 116E 11B4; # (꿅; 꿅; 꿅; 꿅; 꿅; ) HANGUL SYLLABLE GGULT +AFC6;AFC6;1101 116E 11B5;AFC6;1101 116E 11B5; # (꿆; 꿆; 꿆; 꿆; 꿆; ) HANGUL SYLLABLE GGULP +AFC7;AFC7;1101 116E 11B6;AFC7;1101 116E 11B6; # (꿇; 꿇; 꿇; 꿇; 꿇; ) HANGUL SYLLABLE GGULH +AFC8;AFC8;1101 116E 11B7;AFC8;1101 116E 11B7; # (꿈; 꿈; 꿈; 꿈; 꿈; ) HANGUL SYLLABLE GGUM +AFC9;AFC9;1101 116E 11B8;AFC9;1101 116E 11B8; # (꿉; 꿉; 꿉; 꿉; 꿉; ) HANGUL SYLLABLE GGUB +AFCA;AFCA;1101 116E 11B9;AFCA;1101 116E 11B9; # (꿊; 꿊; 꿊; 꿊; 꿊; ) HANGUL SYLLABLE GGUBS +AFCB;AFCB;1101 116E 11BA;AFCB;1101 116E 11BA; # (꿋; 꿋; 꿋; 꿋; 꿋; ) HANGUL SYLLABLE GGUS +AFCC;AFCC;1101 116E 11BB;AFCC;1101 116E 11BB; # (꿌; 꿌; 꿌; 꿌; 꿌; ) HANGUL SYLLABLE GGUSS +AFCD;AFCD;1101 116E 11BC;AFCD;1101 116E 11BC; # (꿍; 꿍; 꿍; 꿍; 꿍; ) HANGUL SYLLABLE GGUNG +AFCE;AFCE;1101 116E 11BD;AFCE;1101 116E 11BD; # (꿎; 꿎; 꿎; 꿎; 꿎; ) HANGUL SYLLABLE GGUJ +AFCF;AFCF;1101 116E 11BE;AFCF;1101 116E 11BE; # (꿏; 꿏; 꿏; 꿏; 꿏; ) HANGUL SYLLABLE GGUC +AFD0;AFD0;1101 116E 11BF;AFD0;1101 116E 11BF; # (꿐; 꿐; 꿐; 꿐; 꿐; ) HANGUL SYLLABLE GGUK +AFD1;AFD1;1101 116E 11C0;AFD1;1101 116E 11C0; # (꿑; 꿑; 꿑; 꿑; 꿑; ) HANGUL SYLLABLE GGUT +AFD2;AFD2;1101 116E 11C1;AFD2;1101 116E 11C1; # (꿒; 꿒; 꿒; 꿒; 꿒; ) HANGUL SYLLABLE GGUP +AFD3;AFD3;1101 116E 11C2;AFD3;1101 116E 11C2; # (꿓; 꿓; 꿓; 꿓; 꿓; ) HANGUL SYLLABLE GGUH +AFD4;AFD4;1101 116F;AFD4;1101 116F; # (꿔; 꿔; 꿔; 꿔; 꿔; ) HANGUL SYLLABLE GGWEO +AFD5;AFD5;1101 116F 11A8;AFD5;1101 116F 11A8; # (꿕; 꿕; 꿕; 꿕; 꿕; ) HANGUL SYLLABLE GGWEOG +AFD6;AFD6;1101 116F 11A9;AFD6;1101 116F 11A9; # (꿖; 꿖; 꿖; 꿖; 꿖; ) HANGUL SYLLABLE GGWEOGG +AFD7;AFD7;1101 116F 11AA;AFD7;1101 116F 11AA; # (꿗; 꿗; 꿗; 꿗; 꿗; ) HANGUL SYLLABLE GGWEOGS +AFD8;AFD8;1101 116F 11AB;AFD8;1101 116F 11AB; # (꿘; 꿘; 꿘; 꿘; 꿘; ) HANGUL SYLLABLE GGWEON +AFD9;AFD9;1101 116F 11AC;AFD9;1101 116F 11AC; # (꿙; 꿙; 꿙; 꿙; 꿙; ) HANGUL SYLLABLE GGWEONJ +AFDA;AFDA;1101 116F 11AD;AFDA;1101 116F 11AD; # (꿚; 꿚; 꿚; 꿚; 꿚; ) HANGUL SYLLABLE GGWEONH +AFDB;AFDB;1101 116F 11AE;AFDB;1101 116F 11AE; # (꿛; 꿛; 꿛; 꿛; 꿛; ) HANGUL SYLLABLE GGWEOD +AFDC;AFDC;1101 116F 11AF;AFDC;1101 116F 11AF; # (꿜; 꿜; 꿜; 꿜; 꿜; ) HANGUL SYLLABLE GGWEOL +AFDD;AFDD;1101 116F 11B0;AFDD;1101 116F 11B0; # (꿝; 꿝; 꿝; 꿝; 꿝; ) HANGUL SYLLABLE GGWEOLG +AFDE;AFDE;1101 116F 11B1;AFDE;1101 116F 11B1; # (꿞; 꿞; 꿞; 꿞; 꿞; ) HANGUL SYLLABLE GGWEOLM +AFDF;AFDF;1101 116F 11B2;AFDF;1101 116F 11B2; # (꿟; 꿟; 꿟; 꿟; 꿟; ) HANGUL SYLLABLE GGWEOLB +AFE0;AFE0;1101 116F 11B3;AFE0;1101 116F 11B3; # (꿠; 꿠; 꿠; 꿠; 꿠; ) HANGUL SYLLABLE GGWEOLS +AFE1;AFE1;1101 116F 11B4;AFE1;1101 116F 11B4; # (꿡; 꿡; 꿡; 꿡; 꿡; ) HANGUL SYLLABLE GGWEOLT +AFE2;AFE2;1101 116F 11B5;AFE2;1101 116F 11B5; # (꿢; 꿢; 꿢; 꿢; 꿢; ) HANGUL SYLLABLE GGWEOLP +AFE3;AFE3;1101 116F 11B6;AFE3;1101 116F 11B6; # (꿣; 꿣; 꿣; 꿣; 꿣; ) HANGUL SYLLABLE GGWEOLH +AFE4;AFE4;1101 116F 11B7;AFE4;1101 116F 11B7; # (꿤; 꿤; 꿤; 꿤; 꿤; ) HANGUL SYLLABLE GGWEOM +AFE5;AFE5;1101 116F 11B8;AFE5;1101 116F 11B8; # (꿥; 꿥; 꿥; 꿥; 꿥; ) HANGUL SYLLABLE GGWEOB +AFE6;AFE6;1101 116F 11B9;AFE6;1101 116F 11B9; # (꿦; 꿦; 꿦; 꿦; 꿦; ) HANGUL SYLLABLE GGWEOBS +AFE7;AFE7;1101 116F 11BA;AFE7;1101 116F 11BA; # (꿧; 꿧; 꿧; 꿧; 꿧; ) HANGUL SYLLABLE GGWEOS +AFE8;AFE8;1101 116F 11BB;AFE8;1101 116F 11BB; # (꿨; 꿨; 꿨; 꿨; 꿨; ) HANGUL SYLLABLE GGWEOSS +AFE9;AFE9;1101 116F 11BC;AFE9;1101 116F 11BC; # (꿩; 꿩; 꿩; 꿩; 꿩; ) HANGUL SYLLABLE GGWEONG +AFEA;AFEA;1101 116F 11BD;AFEA;1101 116F 11BD; # (꿪; 꿪; 꿪; 꿪; 꿪; ) HANGUL SYLLABLE GGWEOJ +AFEB;AFEB;1101 116F 11BE;AFEB;1101 116F 11BE; # (꿫; 꿫; 꿫; 꿫; 꿫; ) HANGUL SYLLABLE GGWEOC +AFEC;AFEC;1101 116F 11BF;AFEC;1101 116F 11BF; # (꿬; 꿬; 꿬; 꿬; 꿬; ) HANGUL SYLLABLE GGWEOK +AFED;AFED;1101 116F 11C0;AFED;1101 116F 11C0; # (꿭; 꿭; 꿭; 꿭; 꿭; ) HANGUL SYLLABLE GGWEOT +AFEE;AFEE;1101 116F 11C1;AFEE;1101 116F 11C1; # (꿮; 꿮; 꿮; 꿮; 꿮; ) HANGUL SYLLABLE GGWEOP +AFEF;AFEF;1101 116F 11C2;AFEF;1101 116F 11C2; # (꿯; 꿯; 꿯; 꿯; 꿯; ) HANGUL SYLLABLE GGWEOH +AFF0;AFF0;1101 1170;AFF0;1101 1170; # (꿰; 꿰; 꿰; 꿰; 꿰; ) HANGUL SYLLABLE GGWE +AFF1;AFF1;1101 1170 11A8;AFF1;1101 1170 11A8; # (꿱; 꿱; 꿱; 꿱; 꿱; ) HANGUL SYLLABLE GGWEG +AFF2;AFF2;1101 1170 11A9;AFF2;1101 1170 11A9; # (꿲; 꿲; 꿲; 꿲; 꿲; ) HANGUL SYLLABLE GGWEGG +AFF3;AFF3;1101 1170 11AA;AFF3;1101 1170 11AA; # (꿳; 꿳; 꿳; 꿳; 꿳; ) HANGUL SYLLABLE GGWEGS +AFF4;AFF4;1101 1170 11AB;AFF4;1101 1170 11AB; # (꿴; 꿴; 꿴; 꿴; 꿴; ) HANGUL SYLLABLE GGWEN +AFF5;AFF5;1101 1170 11AC;AFF5;1101 1170 11AC; # (꿵; 꿵; 꿵; 꿵; 꿵; ) HANGUL SYLLABLE GGWENJ +AFF6;AFF6;1101 1170 11AD;AFF6;1101 1170 11AD; # (꿶; 꿶; 꿶; 꿶; 꿶; ) HANGUL SYLLABLE GGWENH +AFF7;AFF7;1101 1170 11AE;AFF7;1101 1170 11AE; # (꿷; 꿷; 꿷; 꿷; 꿷; ) HANGUL SYLLABLE GGWED +AFF8;AFF8;1101 1170 11AF;AFF8;1101 1170 11AF; # (꿸; 꿸; 꿸; 꿸; 꿸; ) HANGUL SYLLABLE GGWEL +AFF9;AFF9;1101 1170 11B0;AFF9;1101 1170 11B0; # (꿹; 꿹; 꿹; 꿹; 꿹; ) HANGUL SYLLABLE GGWELG +AFFA;AFFA;1101 1170 11B1;AFFA;1101 1170 11B1; # (꿺; 꿺; 꿺; 꿺; 꿺; ) HANGUL SYLLABLE GGWELM +AFFB;AFFB;1101 1170 11B2;AFFB;1101 1170 11B2; # (꿻; 꿻; 꿻; 꿻; 꿻; ) HANGUL SYLLABLE GGWELB +AFFC;AFFC;1101 1170 11B3;AFFC;1101 1170 11B3; # (꿼; 꿼; 꿼; 꿼; 꿼; ) HANGUL SYLLABLE GGWELS +AFFD;AFFD;1101 1170 11B4;AFFD;1101 1170 11B4; # (꿽; 꿽; 꿽; 꿽; 꿽; ) HANGUL SYLLABLE GGWELT +AFFE;AFFE;1101 1170 11B5;AFFE;1101 1170 11B5; # (꿾; 꿾; 꿾; 꿾; 꿾; ) HANGUL SYLLABLE GGWELP +AFFF;AFFF;1101 1170 11B6;AFFF;1101 1170 11B6; # (꿿; 꿿; 꿿; 꿿; 꿿; ) HANGUL SYLLABLE GGWELH +B000;B000;1101 1170 11B7;B000;1101 1170 11B7; # (뀀; 뀀; 뀀; 뀀; 뀀; ) HANGUL SYLLABLE GGWEM +B001;B001;1101 1170 11B8;B001;1101 1170 11B8; # (뀁; 뀁; 뀁; 뀁; 뀁; ) HANGUL SYLLABLE GGWEB +B002;B002;1101 1170 11B9;B002;1101 1170 11B9; # (뀂; 뀂; 뀂; 뀂; 뀂; ) HANGUL SYLLABLE GGWEBS +B003;B003;1101 1170 11BA;B003;1101 1170 11BA; # (뀃; 뀃; 뀃; 뀃; 뀃; ) HANGUL SYLLABLE GGWES +B004;B004;1101 1170 11BB;B004;1101 1170 11BB; # (뀄; 뀄; 뀄; 뀄; 뀄; ) HANGUL SYLLABLE GGWESS +B005;B005;1101 1170 11BC;B005;1101 1170 11BC; # (뀅; 뀅; 뀅; 뀅; 뀅; ) HANGUL SYLLABLE GGWENG +B006;B006;1101 1170 11BD;B006;1101 1170 11BD; # (뀆; 뀆; 뀆; 뀆; 뀆; ) HANGUL SYLLABLE GGWEJ +B007;B007;1101 1170 11BE;B007;1101 1170 11BE; # (뀇; 뀇; 뀇; 뀇; 뀇; ) HANGUL SYLLABLE GGWEC +B008;B008;1101 1170 11BF;B008;1101 1170 11BF; # (뀈; 뀈; 뀈; 뀈; 뀈; ) HANGUL SYLLABLE GGWEK +B009;B009;1101 1170 11C0;B009;1101 1170 11C0; # (뀉; 뀉; 뀉; 뀉; 뀉; ) HANGUL SYLLABLE GGWET +B00A;B00A;1101 1170 11C1;B00A;1101 1170 11C1; # (뀊; 뀊; 뀊; 뀊; 뀊; ) HANGUL SYLLABLE GGWEP +B00B;B00B;1101 1170 11C2;B00B;1101 1170 11C2; # (뀋; 뀋; 뀋; 뀋; 뀋; ) HANGUL SYLLABLE GGWEH +B00C;B00C;1101 1171;B00C;1101 1171; # (뀌; 뀌; 뀌; 뀌; 뀌; ) HANGUL SYLLABLE GGWI +B00D;B00D;1101 1171 11A8;B00D;1101 1171 11A8; # (뀍; 뀍; 뀍; 뀍; 뀍; ) HANGUL SYLLABLE GGWIG +B00E;B00E;1101 1171 11A9;B00E;1101 1171 11A9; # (뀎; 뀎; 뀎; 뀎; 뀎; ) HANGUL SYLLABLE GGWIGG +B00F;B00F;1101 1171 11AA;B00F;1101 1171 11AA; # (뀏; 뀏; 뀏; 뀏; 뀏; ) HANGUL SYLLABLE GGWIGS +B010;B010;1101 1171 11AB;B010;1101 1171 11AB; # (뀐; 뀐; 뀐; 뀐; 뀐; ) HANGUL SYLLABLE GGWIN +B011;B011;1101 1171 11AC;B011;1101 1171 11AC; # (뀑; 뀑; 뀑; 뀑; 뀑; ) HANGUL SYLLABLE GGWINJ +B012;B012;1101 1171 11AD;B012;1101 1171 11AD; # (뀒; 뀒; 뀒; 뀒; 뀒; ) HANGUL SYLLABLE GGWINH +B013;B013;1101 1171 11AE;B013;1101 1171 11AE; # (뀓; 뀓; 뀓; 뀓; 뀓; ) HANGUL SYLLABLE GGWID +B014;B014;1101 1171 11AF;B014;1101 1171 11AF; # (뀔; 뀔; 뀔; 뀔; 뀔; ) HANGUL SYLLABLE GGWIL +B015;B015;1101 1171 11B0;B015;1101 1171 11B0; # (뀕; 뀕; 뀕; 뀕; 뀕; ) HANGUL SYLLABLE GGWILG +B016;B016;1101 1171 11B1;B016;1101 1171 11B1; # (뀖; 뀖; 뀖; 뀖; 뀖; ) HANGUL SYLLABLE GGWILM +B017;B017;1101 1171 11B2;B017;1101 1171 11B2; # (뀗; 뀗; 뀗; 뀗; 뀗; ) HANGUL SYLLABLE GGWILB +B018;B018;1101 1171 11B3;B018;1101 1171 11B3; # (뀘; 뀘; 뀘; 뀘; 뀘; ) HANGUL SYLLABLE GGWILS +B019;B019;1101 1171 11B4;B019;1101 1171 11B4; # (뀙; 뀙; 뀙; 뀙; 뀙; ) HANGUL SYLLABLE GGWILT +B01A;B01A;1101 1171 11B5;B01A;1101 1171 11B5; # (뀚; 뀚; 뀚; 뀚; 뀚; ) HANGUL SYLLABLE GGWILP +B01B;B01B;1101 1171 11B6;B01B;1101 1171 11B6; # (뀛; 뀛; 뀛; 뀛; 뀛; ) HANGUL SYLLABLE GGWILH +B01C;B01C;1101 1171 11B7;B01C;1101 1171 11B7; # (뀜; 뀜; 뀜; 뀜; 뀜; ) HANGUL SYLLABLE GGWIM +B01D;B01D;1101 1171 11B8;B01D;1101 1171 11B8; # (뀝; 뀝; 뀝; 뀝; 뀝; ) HANGUL SYLLABLE GGWIB +B01E;B01E;1101 1171 11B9;B01E;1101 1171 11B9; # (뀞; 뀞; 뀞; 뀞; 뀞; ) HANGUL SYLLABLE GGWIBS +B01F;B01F;1101 1171 11BA;B01F;1101 1171 11BA; # (뀟; 뀟; 뀟; 뀟; 뀟; ) HANGUL SYLLABLE GGWIS +B020;B020;1101 1171 11BB;B020;1101 1171 11BB; # (뀠; 뀠; 뀠; 뀠; 뀠; ) HANGUL SYLLABLE GGWISS +B021;B021;1101 1171 11BC;B021;1101 1171 11BC; # (뀡; 뀡; 뀡; 뀡; 뀡; ) HANGUL SYLLABLE GGWING +B022;B022;1101 1171 11BD;B022;1101 1171 11BD; # (뀢; 뀢; 뀢; 뀢; 뀢; ) HANGUL SYLLABLE GGWIJ +B023;B023;1101 1171 11BE;B023;1101 1171 11BE; # (뀣; 뀣; 뀣; 뀣; 뀣; ) HANGUL SYLLABLE GGWIC +B024;B024;1101 1171 11BF;B024;1101 1171 11BF; # (뀤; 뀤; 뀤; 뀤; 뀤; ) HANGUL SYLLABLE GGWIK +B025;B025;1101 1171 11C0;B025;1101 1171 11C0; # (뀥; 뀥; 뀥; 뀥; 뀥; ) HANGUL SYLLABLE GGWIT +B026;B026;1101 1171 11C1;B026;1101 1171 11C1; # (뀦; 뀦; 뀦; 뀦; 뀦; ) HANGUL SYLLABLE GGWIP +B027;B027;1101 1171 11C2;B027;1101 1171 11C2; # (뀧; 뀧; 뀧; 뀧; 뀧; ) HANGUL SYLLABLE GGWIH +B028;B028;1101 1172;B028;1101 1172; # (뀨; 뀨; 뀨; 뀨; 뀨; ) HANGUL SYLLABLE GGYU +B029;B029;1101 1172 11A8;B029;1101 1172 11A8; # (뀩; 뀩; 뀩; 뀩; 뀩; ) HANGUL SYLLABLE GGYUG +B02A;B02A;1101 1172 11A9;B02A;1101 1172 11A9; # (뀪; 뀪; 뀪; 뀪; 뀪; ) HANGUL SYLLABLE GGYUGG +B02B;B02B;1101 1172 11AA;B02B;1101 1172 11AA; # (뀫; 뀫; 뀫; 뀫; 뀫; ) HANGUL SYLLABLE GGYUGS +B02C;B02C;1101 1172 11AB;B02C;1101 1172 11AB; # (뀬; 뀬; 뀬; 뀬; 뀬; ) HANGUL SYLLABLE GGYUN +B02D;B02D;1101 1172 11AC;B02D;1101 1172 11AC; # (뀭; 뀭; 뀭; 뀭; 뀭; ) HANGUL SYLLABLE GGYUNJ +B02E;B02E;1101 1172 11AD;B02E;1101 1172 11AD; # (뀮; 뀮; 뀮; 뀮; 뀮; ) HANGUL SYLLABLE GGYUNH +B02F;B02F;1101 1172 11AE;B02F;1101 1172 11AE; # (뀯; 뀯; 뀯; 뀯; 뀯; ) HANGUL SYLLABLE GGYUD +B030;B030;1101 1172 11AF;B030;1101 1172 11AF; # (뀰; 뀰; 뀰; 뀰; 뀰; ) HANGUL SYLLABLE GGYUL +B031;B031;1101 1172 11B0;B031;1101 1172 11B0; # (뀱; 뀱; 뀱; 뀱; 뀱; ) HANGUL SYLLABLE GGYULG +B032;B032;1101 1172 11B1;B032;1101 1172 11B1; # (뀲; 뀲; 뀲; 뀲; 뀲; ) HANGUL SYLLABLE GGYULM +B033;B033;1101 1172 11B2;B033;1101 1172 11B2; # (뀳; 뀳; 뀳; 뀳; 뀳; ) HANGUL SYLLABLE GGYULB +B034;B034;1101 1172 11B3;B034;1101 1172 11B3; # (뀴; 뀴; 뀴; 뀴; 뀴; ) HANGUL SYLLABLE GGYULS +B035;B035;1101 1172 11B4;B035;1101 1172 11B4; # (뀵; 뀵; 뀵; 뀵; 뀵; ) HANGUL SYLLABLE GGYULT +B036;B036;1101 1172 11B5;B036;1101 1172 11B5; # (뀶; 뀶; 뀶; 뀶; 뀶; ) HANGUL SYLLABLE GGYULP +B037;B037;1101 1172 11B6;B037;1101 1172 11B6; # (뀷; 뀷; 뀷; 뀷; 뀷; ) HANGUL SYLLABLE GGYULH +B038;B038;1101 1172 11B7;B038;1101 1172 11B7; # (뀸; 뀸; 뀸; 뀸; 뀸; ) HANGUL SYLLABLE GGYUM +B039;B039;1101 1172 11B8;B039;1101 1172 11B8; # (뀹; 뀹; 뀹; 뀹; 뀹; ) HANGUL SYLLABLE GGYUB +B03A;B03A;1101 1172 11B9;B03A;1101 1172 11B9; # (뀺; 뀺; 뀺; 뀺; 뀺; ) HANGUL SYLLABLE GGYUBS +B03B;B03B;1101 1172 11BA;B03B;1101 1172 11BA; # (뀻; 뀻; 뀻; 뀻; 뀻; ) HANGUL SYLLABLE GGYUS +B03C;B03C;1101 1172 11BB;B03C;1101 1172 11BB; # (뀼; 뀼; 뀼; 뀼; 뀼; ) HANGUL SYLLABLE GGYUSS +B03D;B03D;1101 1172 11BC;B03D;1101 1172 11BC; # (뀽; 뀽; 뀽; 뀽; 뀽; ) HANGUL SYLLABLE GGYUNG +B03E;B03E;1101 1172 11BD;B03E;1101 1172 11BD; # (뀾; 뀾; 뀾; 뀾; 뀾; ) HANGUL SYLLABLE GGYUJ +B03F;B03F;1101 1172 11BE;B03F;1101 1172 11BE; # (뀿; 뀿; 뀿; 뀿; 뀿; ) HANGUL SYLLABLE GGYUC +B040;B040;1101 1172 11BF;B040;1101 1172 11BF; # (끀; 끀; 끀; 끀; 끀; ) HANGUL SYLLABLE GGYUK +B041;B041;1101 1172 11C0;B041;1101 1172 11C0; # (끁; 끁; 끁; 끁; 끁; ) HANGUL SYLLABLE GGYUT +B042;B042;1101 1172 11C1;B042;1101 1172 11C1; # (끂; 끂; 끂; 끂; 끂; ) HANGUL SYLLABLE GGYUP +B043;B043;1101 1172 11C2;B043;1101 1172 11C2; # (끃; 끃; 끃; 끃; 끃; ) HANGUL SYLLABLE GGYUH +B044;B044;1101 1173;B044;1101 1173; # (끄; 끄; 끄; 끄; 끄; ) HANGUL SYLLABLE GGEU +B045;B045;1101 1173 11A8;B045;1101 1173 11A8; # (끅; 끅; 끅; 끅; 끅; ) HANGUL SYLLABLE GGEUG +B046;B046;1101 1173 11A9;B046;1101 1173 11A9; # (끆; 끆; 끆; 끆; 끆; ) HANGUL SYLLABLE GGEUGG +B047;B047;1101 1173 11AA;B047;1101 1173 11AA; # (끇; 끇; 끇; 끇; 끇; ) HANGUL SYLLABLE GGEUGS +B048;B048;1101 1173 11AB;B048;1101 1173 11AB; # (끈; 끈; 끈; 끈; 끈; ) HANGUL SYLLABLE GGEUN +B049;B049;1101 1173 11AC;B049;1101 1173 11AC; # (끉; 끉; 끉; 끉; 끉; ) HANGUL SYLLABLE GGEUNJ +B04A;B04A;1101 1173 11AD;B04A;1101 1173 11AD; # (끊; 끊; 끊; 끊; 끊; ) HANGUL SYLLABLE GGEUNH +B04B;B04B;1101 1173 11AE;B04B;1101 1173 11AE; # (끋; 끋; 끋; 끋; 끋; ) HANGUL SYLLABLE GGEUD +B04C;B04C;1101 1173 11AF;B04C;1101 1173 11AF; # (끌; 끌; 끌; 끌; 끌; ) HANGUL SYLLABLE GGEUL +B04D;B04D;1101 1173 11B0;B04D;1101 1173 11B0; # (끍; 끍; 끍; 끍; 끍; ) HANGUL SYLLABLE GGEULG +B04E;B04E;1101 1173 11B1;B04E;1101 1173 11B1; # (끎; 끎; 끎; 끎; 끎; ) HANGUL SYLLABLE GGEULM +B04F;B04F;1101 1173 11B2;B04F;1101 1173 11B2; # (끏; 끏; 끏; 끏; 끏; ) HANGUL SYLLABLE GGEULB +B050;B050;1101 1173 11B3;B050;1101 1173 11B3; # (끐; 끐; 끐; 끐; 끐; ) HANGUL SYLLABLE GGEULS +B051;B051;1101 1173 11B4;B051;1101 1173 11B4; # (끑; 끑; 끑; 끑; 끑; ) HANGUL SYLLABLE GGEULT +B052;B052;1101 1173 11B5;B052;1101 1173 11B5; # (끒; 끒; 끒; 끒; 끒; ) HANGUL SYLLABLE GGEULP +B053;B053;1101 1173 11B6;B053;1101 1173 11B6; # (끓; 끓; 끓; 끓; 끓; ) HANGUL SYLLABLE GGEULH +B054;B054;1101 1173 11B7;B054;1101 1173 11B7; # (끔; 끔; 끔; 끔; 끔; ) HANGUL SYLLABLE GGEUM +B055;B055;1101 1173 11B8;B055;1101 1173 11B8; # (끕; 끕; 끕; 끕; 끕; ) HANGUL SYLLABLE GGEUB +B056;B056;1101 1173 11B9;B056;1101 1173 11B9; # (끖; 끖; 끖; 끖; 끖; ) HANGUL SYLLABLE GGEUBS +B057;B057;1101 1173 11BA;B057;1101 1173 11BA; # (끗; 끗; 끗; 끗; 끗; ) HANGUL SYLLABLE GGEUS +B058;B058;1101 1173 11BB;B058;1101 1173 11BB; # (끘; 끘; 끘; 끘; 끘; ) HANGUL SYLLABLE GGEUSS +B059;B059;1101 1173 11BC;B059;1101 1173 11BC; # (끙; 끙; 끙; 끙; 끙; ) HANGUL SYLLABLE GGEUNG +B05A;B05A;1101 1173 11BD;B05A;1101 1173 11BD; # (끚; 끚; 끚; 끚; 끚; ) HANGUL SYLLABLE GGEUJ +B05B;B05B;1101 1173 11BE;B05B;1101 1173 11BE; # (끛; 끛; 끛; 끛; 끛; ) HANGUL SYLLABLE GGEUC +B05C;B05C;1101 1173 11BF;B05C;1101 1173 11BF; # (끜; 끜; 끜; 끜; 끜; ) HANGUL SYLLABLE GGEUK +B05D;B05D;1101 1173 11C0;B05D;1101 1173 11C0; # (끝; 끝; 끝; 끝; 끝; ) HANGUL SYLLABLE GGEUT +B05E;B05E;1101 1173 11C1;B05E;1101 1173 11C1; # (끞; 끞; 끞; 끞; 끞; ) HANGUL SYLLABLE GGEUP +B05F;B05F;1101 1173 11C2;B05F;1101 1173 11C2; # (끟; 끟; 끟; 끟; 끟; ) HANGUL SYLLABLE GGEUH +B060;B060;1101 1174;B060;1101 1174; # (끠; 끠; 끠; 끠; 끠; ) HANGUL SYLLABLE GGYI +B061;B061;1101 1174 11A8;B061;1101 1174 11A8; # (끡; 끡; 끡; 끡; 끡; ) HANGUL SYLLABLE GGYIG +B062;B062;1101 1174 11A9;B062;1101 1174 11A9; # (끢; 끢; 끢; 끢; 끢; ) HANGUL SYLLABLE GGYIGG +B063;B063;1101 1174 11AA;B063;1101 1174 11AA; # (끣; 끣; 끣; 끣; 끣; ) HANGUL SYLLABLE GGYIGS +B064;B064;1101 1174 11AB;B064;1101 1174 11AB; # (끤; 끤; 끤; 끤; 끤; ) HANGUL SYLLABLE GGYIN +B065;B065;1101 1174 11AC;B065;1101 1174 11AC; # (끥; 끥; 끥; 끥; 끥; ) HANGUL SYLLABLE GGYINJ +B066;B066;1101 1174 11AD;B066;1101 1174 11AD; # (끦; 끦; 끦; 끦; 끦; ) HANGUL SYLLABLE GGYINH +B067;B067;1101 1174 11AE;B067;1101 1174 11AE; # (끧; 끧; 끧; 끧; 끧; ) HANGUL SYLLABLE GGYID +B068;B068;1101 1174 11AF;B068;1101 1174 11AF; # (끨; 끨; 끨; 끨; 끨; ) HANGUL SYLLABLE GGYIL +B069;B069;1101 1174 11B0;B069;1101 1174 11B0; # (끩; 끩; 끩; 끩; 끩; ) HANGUL SYLLABLE GGYILG +B06A;B06A;1101 1174 11B1;B06A;1101 1174 11B1; # (끪; 끪; 끪; 끪; 끪; ) HANGUL SYLLABLE GGYILM +B06B;B06B;1101 1174 11B2;B06B;1101 1174 11B2; # (끫; 끫; 끫; 끫; 끫; ) HANGUL SYLLABLE GGYILB +B06C;B06C;1101 1174 11B3;B06C;1101 1174 11B3; # (끬; 끬; 끬; 끬; 끬; ) HANGUL SYLLABLE GGYILS +B06D;B06D;1101 1174 11B4;B06D;1101 1174 11B4; # (끭; 끭; 끭; 끭; 끭; ) HANGUL SYLLABLE GGYILT +B06E;B06E;1101 1174 11B5;B06E;1101 1174 11B5; # (끮; 끮; 끮; 끮; 끮; ) HANGUL SYLLABLE GGYILP +B06F;B06F;1101 1174 11B6;B06F;1101 1174 11B6; # (끯; 끯; 끯; 끯; 끯; ) HANGUL SYLLABLE GGYILH +B070;B070;1101 1174 11B7;B070;1101 1174 11B7; # (끰; 끰; 끰; 끰; 끰; ) HANGUL SYLLABLE GGYIM +B071;B071;1101 1174 11B8;B071;1101 1174 11B8; # (끱; 끱; 끱; 끱; 끱; ) HANGUL SYLLABLE GGYIB +B072;B072;1101 1174 11B9;B072;1101 1174 11B9; # (끲; 끲; 끲; 끲; 끲; ) HANGUL SYLLABLE GGYIBS +B073;B073;1101 1174 11BA;B073;1101 1174 11BA; # (끳; 끳; 끳; 끳; 끳; ) HANGUL SYLLABLE GGYIS +B074;B074;1101 1174 11BB;B074;1101 1174 11BB; # (끴; 끴; 끴; 끴; 끴; ) HANGUL SYLLABLE GGYISS +B075;B075;1101 1174 11BC;B075;1101 1174 11BC; # (끵; 끵; 끵; 끵; 끵; ) HANGUL SYLLABLE GGYING +B076;B076;1101 1174 11BD;B076;1101 1174 11BD; # (끶; 끶; 끶; 끶; 끶; ) HANGUL SYLLABLE GGYIJ +B077;B077;1101 1174 11BE;B077;1101 1174 11BE; # (끷; 끷; 끷; 끷; 끷; ) HANGUL SYLLABLE GGYIC +B078;B078;1101 1174 11BF;B078;1101 1174 11BF; # (끸; 끸; 끸; 끸; 끸; ) HANGUL SYLLABLE GGYIK +B079;B079;1101 1174 11C0;B079;1101 1174 11C0; # (끹; 끹; 끹; 끹; 끹; ) HANGUL SYLLABLE GGYIT +B07A;B07A;1101 1174 11C1;B07A;1101 1174 11C1; # (끺; 끺; 끺; 끺; 끺; ) HANGUL SYLLABLE GGYIP +B07B;B07B;1101 1174 11C2;B07B;1101 1174 11C2; # (끻; 끻; 끻; 끻; 끻; ) HANGUL SYLLABLE GGYIH +B07C;B07C;1101 1175;B07C;1101 1175; # (끼; 끼; 끼; 끼; 끼; ) HANGUL SYLLABLE GGI +B07D;B07D;1101 1175 11A8;B07D;1101 1175 11A8; # (끽; 끽; 끽; 끽; 끽; ) HANGUL SYLLABLE GGIG +B07E;B07E;1101 1175 11A9;B07E;1101 1175 11A9; # (끾; 끾; 끾; 끾; 끾; ) HANGUL SYLLABLE GGIGG +B07F;B07F;1101 1175 11AA;B07F;1101 1175 11AA; # (끿; 끿; 끿; 끿; 끿; ) HANGUL SYLLABLE GGIGS +B080;B080;1101 1175 11AB;B080;1101 1175 11AB; # (낀; 낀; 낀; 낀; 낀; ) HANGUL SYLLABLE GGIN +B081;B081;1101 1175 11AC;B081;1101 1175 11AC; # (낁; 낁; 낁; 낁; 낁; ) HANGUL SYLLABLE GGINJ +B082;B082;1101 1175 11AD;B082;1101 1175 11AD; # (낂; 낂; 낂; 낂; 낂; ) HANGUL SYLLABLE GGINH +B083;B083;1101 1175 11AE;B083;1101 1175 11AE; # (낃; 낃; 낃; 낃; 낃; ) HANGUL SYLLABLE GGID +B084;B084;1101 1175 11AF;B084;1101 1175 11AF; # (낄; 낄; 낄; 낄; 낄; ) HANGUL SYLLABLE GGIL +B085;B085;1101 1175 11B0;B085;1101 1175 11B0; # (낅; 낅; 낅; 낅; 낅; ) HANGUL SYLLABLE GGILG +B086;B086;1101 1175 11B1;B086;1101 1175 11B1; # (낆; 낆; 낆; 낆; 낆; ) HANGUL SYLLABLE GGILM +B087;B087;1101 1175 11B2;B087;1101 1175 11B2; # (낇; 낇; 낇; 낇; 낇; ) HANGUL SYLLABLE GGILB +B088;B088;1101 1175 11B3;B088;1101 1175 11B3; # (낈; 낈; 낈; 낈; 낈; ) HANGUL SYLLABLE GGILS +B089;B089;1101 1175 11B4;B089;1101 1175 11B4; # (낉; 낉; 낉; 낉; 낉; ) HANGUL SYLLABLE GGILT +B08A;B08A;1101 1175 11B5;B08A;1101 1175 11B5; # (낊; 낊; 낊; 낊; 낊; ) HANGUL SYLLABLE GGILP +B08B;B08B;1101 1175 11B6;B08B;1101 1175 11B6; # (낋; 낋; 낋; 낋; 낋; ) HANGUL SYLLABLE GGILH +B08C;B08C;1101 1175 11B7;B08C;1101 1175 11B7; # (낌; 낌; 낌; 낌; 낌; ) HANGUL SYLLABLE GGIM +B08D;B08D;1101 1175 11B8;B08D;1101 1175 11B8; # (낍; 낍; 낍; 낍; 낍; ) HANGUL SYLLABLE GGIB +B08E;B08E;1101 1175 11B9;B08E;1101 1175 11B9; # (낎; 낎; 낎; 낎; 낎; ) HANGUL SYLLABLE GGIBS +B08F;B08F;1101 1175 11BA;B08F;1101 1175 11BA; # (낏; 낏; 낏; 낏; 낏; ) HANGUL SYLLABLE GGIS +B090;B090;1101 1175 11BB;B090;1101 1175 11BB; # (낐; 낐; 낐; 낐; 낐; ) HANGUL SYLLABLE GGISS +B091;B091;1101 1175 11BC;B091;1101 1175 11BC; # (낑; 낑; 낑; 낑; 낑; ) HANGUL SYLLABLE GGING +B092;B092;1101 1175 11BD;B092;1101 1175 11BD; # (낒; 낒; 낒; 낒; 낒; ) HANGUL SYLLABLE GGIJ +B093;B093;1101 1175 11BE;B093;1101 1175 11BE; # (낓; 낓; 낓; 낓; 낓; ) HANGUL SYLLABLE GGIC +B094;B094;1101 1175 11BF;B094;1101 1175 11BF; # (낔; 낔; 낔; 낔; 낔; ) HANGUL SYLLABLE GGIK +B095;B095;1101 1175 11C0;B095;1101 1175 11C0; # (낕; 낕; 낕; 낕; 낕; ) HANGUL SYLLABLE GGIT +B096;B096;1101 1175 11C1;B096;1101 1175 11C1; # (낖; 낖; 낖; 낖; 낖; ) HANGUL SYLLABLE GGIP +B097;B097;1101 1175 11C2;B097;1101 1175 11C2; # (낗; 낗; 낗; 낗; 낗; ) HANGUL SYLLABLE GGIH +B098;B098;1102 1161;B098;1102 1161; # (나; 나; 나; 나; 나; ) HANGUL SYLLABLE NA +B099;B099;1102 1161 11A8;B099;1102 1161 11A8; # (낙; 낙; 낙; 낙; 낙; ) HANGUL SYLLABLE NAG +B09A;B09A;1102 1161 11A9;B09A;1102 1161 11A9; # (낚; 낚; 낚; 낚; 낚; ) HANGUL SYLLABLE NAGG +B09B;B09B;1102 1161 11AA;B09B;1102 1161 11AA; # (낛; 낛; 낛; 낛; 낛; ) HANGUL SYLLABLE NAGS +B09C;B09C;1102 1161 11AB;B09C;1102 1161 11AB; # (난; 난; 난; 난; 난; ) HANGUL SYLLABLE NAN +B09D;B09D;1102 1161 11AC;B09D;1102 1161 11AC; # (낝; 낝; 낝; 낝; 낝; ) HANGUL SYLLABLE NANJ +B09E;B09E;1102 1161 11AD;B09E;1102 1161 11AD; # (낞; 낞; 낞; 낞; 낞; ) HANGUL SYLLABLE NANH +B09F;B09F;1102 1161 11AE;B09F;1102 1161 11AE; # (낟; 낟; 낟; 낟; 낟; ) HANGUL SYLLABLE NAD +B0A0;B0A0;1102 1161 11AF;B0A0;1102 1161 11AF; # (날; 날; 날; 날; 날; ) HANGUL SYLLABLE NAL +B0A1;B0A1;1102 1161 11B0;B0A1;1102 1161 11B0; # (낡; 낡; 낡; 낡; 낡; ) HANGUL SYLLABLE NALG +B0A2;B0A2;1102 1161 11B1;B0A2;1102 1161 11B1; # (낢; 낢; 낢; 낢; 낢; ) HANGUL SYLLABLE NALM +B0A3;B0A3;1102 1161 11B2;B0A3;1102 1161 11B2; # (낣; 낣; 낣; 낣; 낣; ) HANGUL SYLLABLE NALB +B0A4;B0A4;1102 1161 11B3;B0A4;1102 1161 11B3; # (낤; 낤; 낤; 낤; 낤; ) HANGUL SYLLABLE NALS +B0A5;B0A5;1102 1161 11B4;B0A5;1102 1161 11B4; # (낥; 낥; 낥; 낥; 낥; ) HANGUL SYLLABLE NALT +B0A6;B0A6;1102 1161 11B5;B0A6;1102 1161 11B5; # (낦; 낦; 낦; 낦; 낦; ) HANGUL SYLLABLE NALP +B0A7;B0A7;1102 1161 11B6;B0A7;1102 1161 11B6; # (낧; 낧; 낧; 낧; 낧; ) HANGUL SYLLABLE NALH +B0A8;B0A8;1102 1161 11B7;B0A8;1102 1161 11B7; # (남; 남; 남; 남; 남; ) HANGUL SYLLABLE NAM +B0A9;B0A9;1102 1161 11B8;B0A9;1102 1161 11B8; # (납; 납; 납; 납; 납; ) HANGUL SYLLABLE NAB +B0AA;B0AA;1102 1161 11B9;B0AA;1102 1161 11B9; # (낪; 낪; 낪; 낪; 낪; ) HANGUL SYLLABLE NABS +B0AB;B0AB;1102 1161 11BA;B0AB;1102 1161 11BA; # (낫; 낫; 낫; 낫; 낫; ) HANGUL SYLLABLE NAS +B0AC;B0AC;1102 1161 11BB;B0AC;1102 1161 11BB; # (났; 났; 났; 났; 났; ) HANGUL SYLLABLE NASS +B0AD;B0AD;1102 1161 11BC;B0AD;1102 1161 11BC; # (낭; 낭; 낭; 낭; 낭; ) HANGUL SYLLABLE NANG +B0AE;B0AE;1102 1161 11BD;B0AE;1102 1161 11BD; # (낮; 낮; 낮; 낮; 낮; ) HANGUL SYLLABLE NAJ +B0AF;B0AF;1102 1161 11BE;B0AF;1102 1161 11BE; # (낯; 낯; 낯; 낯; 낯; ) HANGUL SYLLABLE NAC +B0B0;B0B0;1102 1161 11BF;B0B0;1102 1161 11BF; # (낰; 낰; 낰; 낰; 낰; ) HANGUL SYLLABLE NAK +B0B1;B0B1;1102 1161 11C0;B0B1;1102 1161 11C0; # (낱; 낱; 낱; 낱; 낱; ) HANGUL SYLLABLE NAT +B0B2;B0B2;1102 1161 11C1;B0B2;1102 1161 11C1; # (낲; 낲; 낲; 낲; 낲; ) HANGUL SYLLABLE NAP +B0B3;B0B3;1102 1161 11C2;B0B3;1102 1161 11C2; # (낳; 낳; 낳; 낳; 낳; ) HANGUL SYLLABLE NAH +B0B4;B0B4;1102 1162;B0B4;1102 1162; # (내; 내; 내; 내; 내; ) HANGUL SYLLABLE NAE +B0B5;B0B5;1102 1162 11A8;B0B5;1102 1162 11A8; # (낵; 낵; 낵; 낵; 낵; ) HANGUL SYLLABLE NAEG +B0B6;B0B6;1102 1162 11A9;B0B6;1102 1162 11A9; # (낶; 낶; 낶; 낶; 낶; ) HANGUL SYLLABLE NAEGG +B0B7;B0B7;1102 1162 11AA;B0B7;1102 1162 11AA; # (낷; 낷; 낷; 낷; 낷; ) HANGUL SYLLABLE NAEGS +B0B8;B0B8;1102 1162 11AB;B0B8;1102 1162 11AB; # (낸; 낸; 낸; 낸; 낸; ) HANGUL SYLLABLE NAEN +B0B9;B0B9;1102 1162 11AC;B0B9;1102 1162 11AC; # (낹; 낹; 낹; 낹; 낹; ) HANGUL SYLLABLE NAENJ +B0BA;B0BA;1102 1162 11AD;B0BA;1102 1162 11AD; # (낺; 낺; 낺; 낺; 낺; ) HANGUL SYLLABLE NAENH +B0BB;B0BB;1102 1162 11AE;B0BB;1102 1162 11AE; # (낻; 낻; 낻; 낻; 낻; ) HANGUL SYLLABLE NAED +B0BC;B0BC;1102 1162 11AF;B0BC;1102 1162 11AF; # (낼; 낼; 낼; 낼; 낼; ) HANGUL SYLLABLE NAEL +B0BD;B0BD;1102 1162 11B0;B0BD;1102 1162 11B0; # (낽; 낽; 낽; 낽; 낽; ) HANGUL SYLLABLE NAELG +B0BE;B0BE;1102 1162 11B1;B0BE;1102 1162 11B1; # (낾; 낾; 낾; 낾; 낾; ) HANGUL SYLLABLE NAELM +B0BF;B0BF;1102 1162 11B2;B0BF;1102 1162 11B2; # (낿; 낿; 낿; 낿; 낿; ) HANGUL SYLLABLE NAELB +B0C0;B0C0;1102 1162 11B3;B0C0;1102 1162 11B3; # (냀; 냀; 냀; 냀; 냀; ) HANGUL SYLLABLE NAELS +B0C1;B0C1;1102 1162 11B4;B0C1;1102 1162 11B4; # (냁; 냁; 냁; 냁; 냁; ) HANGUL SYLLABLE NAELT +B0C2;B0C2;1102 1162 11B5;B0C2;1102 1162 11B5; # (냂; 냂; 냂; 냂; 냂; ) HANGUL SYLLABLE NAELP +B0C3;B0C3;1102 1162 11B6;B0C3;1102 1162 11B6; # (냃; 냃; 냃; 냃; 냃; ) HANGUL SYLLABLE NAELH +B0C4;B0C4;1102 1162 11B7;B0C4;1102 1162 11B7; # (냄; 냄; 냄; 냄; 냄; ) HANGUL SYLLABLE NAEM +B0C5;B0C5;1102 1162 11B8;B0C5;1102 1162 11B8; # (냅; 냅; 냅; 냅; 냅; ) HANGUL SYLLABLE NAEB +B0C6;B0C6;1102 1162 11B9;B0C6;1102 1162 11B9; # (냆; 냆; 냆; 냆; 냆; ) HANGUL SYLLABLE NAEBS +B0C7;B0C7;1102 1162 11BA;B0C7;1102 1162 11BA; # (냇; 냇; 냇; 냇; 냇; ) HANGUL SYLLABLE NAES +B0C8;B0C8;1102 1162 11BB;B0C8;1102 1162 11BB; # (냈; 냈; 냈; 냈; 냈; ) HANGUL SYLLABLE NAESS +B0C9;B0C9;1102 1162 11BC;B0C9;1102 1162 11BC; # (냉; 냉; 냉; 냉; 냉; ) HANGUL SYLLABLE NAENG +B0CA;B0CA;1102 1162 11BD;B0CA;1102 1162 11BD; # (냊; 냊; 냊; 냊; 냊; ) HANGUL SYLLABLE NAEJ +B0CB;B0CB;1102 1162 11BE;B0CB;1102 1162 11BE; # (냋; 냋; 냋; 냋; 냋; ) HANGUL SYLLABLE NAEC +B0CC;B0CC;1102 1162 11BF;B0CC;1102 1162 11BF; # (냌; 냌; 냌; 냌; 냌; ) HANGUL SYLLABLE NAEK +B0CD;B0CD;1102 1162 11C0;B0CD;1102 1162 11C0; # (냍; 냍; 냍; 냍; 냍; ) HANGUL SYLLABLE NAET +B0CE;B0CE;1102 1162 11C1;B0CE;1102 1162 11C1; # (냎; 냎; 냎; 냎; 냎; ) HANGUL SYLLABLE NAEP +B0CF;B0CF;1102 1162 11C2;B0CF;1102 1162 11C2; # (냏; 냏; 냏; 냏; 냏; ) HANGUL SYLLABLE NAEH +B0D0;B0D0;1102 1163;B0D0;1102 1163; # (냐; 냐; 냐; 냐; 냐; ) HANGUL SYLLABLE NYA +B0D1;B0D1;1102 1163 11A8;B0D1;1102 1163 11A8; # (냑; 냑; 냑; 냑; 냑; ) HANGUL SYLLABLE NYAG +B0D2;B0D2;1102 1163 11A9;B0D2;1102 1163 11A9; # (냒; 냒; 냒; 냒; 냒; ) HANGUL SYLLABLE NYAGG +B0D3;B0D3;1102 1163 11AA;B0D3;1102 1163 11AA; # (냓; 냓; 냓; 냓; 냓; ) HANGUL SYLLABLE NYAGS +B0D4;B0D4;1102 1163 11AB;B0D4;1102 1163 11AB; # (냔; 냔; 냔; 냔; 냔; ) HANGUL SYLLABLE NYAN +B0D5;B0D5;1102 1163 11AC;B0D5;1102 1163 11AC; # (냕; 냕; 냕; 냕; 냕; ) HANGUL SYLLABLE NYANJ +B0D6;B0D6;1102 1163 11AD;B0D6;1102 1163 11AD; # (냖; 냖; 냖; 냖; 냖; ) HANGUL SYLLABLE NYANH +B0D7;B0D7;1102 1163 11AE;B0D7;1102 1163 11AE; # (냗; 냗; 냗; 냗; 냗; ) HANGUL SYLLABLE NYAD +B0D8;B0D8;1102 1163 11AF;B0D8;1102 1163 11AF; # (냘; 냘; 냘; 냘; 냘; ) HANGUL SYLLABLE NYAL +B0D9;B0D9;1102 1163 11B0;B0D9;1102 1163 11B0; # (냙; 냙; 냙; 냙; 냙; ) HANGUL SYLLABLE NYALG +B0DA;B0DA;1102 1163 11B1;B0DA;1102 1163 11B1; # (냚; 냚; 냚; 냚; 냚; ) HANGUL SYLLABLE NYALM +B0DB;B0DB;1102 1163 11B2;B0DB;1102 1163 11B2; # (냛; 냛; 냛; 냛; 냛; ) HANGUL SYLLABLE NYALB +B0DC;B0DC;1102 1163 11B3;B0DC;1102 1163 11B3; # (냜; 냜; 냜; 냜; 냜; ) HANGUL SYLLABLE NYALS +B0DD;B0DD;1102 1163 11B4;B0DD;1102 1163 11B4; # (냝; 냝; 냝; 냝; 냝; ) HANGUL SYLLABLE NYALT +B0DE;B0DE;1102 1163 11B5;B0DE;1102 1163 11B5; # (냞; 냞; 냞; 냞; 냞; ) HANGUL SYLLABLE NYALP +B0DF;B0DF;1102 1163 11B6;B0DF;1102 1163 11B6; # (냟; 냟; 냟; 냟; 냟; ) HANGUL SYLLABLE NYALH +B0E0;B0E0;1102 1163 11B7;B0E0;1102 1163 11B7; # (냠; 냠; 냠; 냠; 냠; ) HANGUL SYLLABLE NYAM +B0E1;B0E1;1102 1163 11B8;B0E1;1102 1163 11B8; # (냡; 냡; 냡; 냡; 냡; ) HANGUL SYLLABLE NYAB +B0E2;B0E2;1102 1163 11B9;B0E2;1102 1163 11B9; # (냢; 냢; 냢; 냢; 냢; ) HANGUL SYLLABLE NYABS +B0E3;B0E3;1102 1163 11BA;B0E3;1102 1163 11BA; # (냣; 냣; 냣; 냣; 냣; ) HANGUL SYLLABLE NYAS +B0E4;B0E4;1102 1163 11BB;B0E4;1102 1163 11BB; # (냤; 냤; 냤; 냤; 냤; ) HANGUL SYLLABLE NYASS +B0E5;B0E5;1102 1163 11BC;B0E5;1102 1163 11BC; # (냥; 냥; 냥; 냥; 냥; ) HANGUL SYLLABLE NYANG +B0E6;B0E6;1102 1163 11BD;B0E6;1102 1163 11BD; # (냦; 냦; 냦; 냦; 냦; ) HANGUL SYLLABLE NYAJ +B0E7;B0E7;1102 1163 11BE;B0E7;1102 1163 11BE; # (냧; 냧; 냧; 냧; 냧; ) HANGUL SYLLABLE NYAC +B0E8;B0E8;1102 1163 11BF;B0E8;1102 1163 11BF; # (냨; 냨; 냨; 냨; 냨; ) HANGUL SYLLABLE NYAK +B0E9;B0E9;1102 1163 11C0;B0E9;1102 1163 11C0; # (냩; 냩; 냩; 냩; 냩; ) HANGUL SYLLABLE NYAT +B0EA;B0EA;1102 1163 11C1;B0EA;1102 1163 11C1; # (냪; 냪; 냪; 냪; 냪; ) HANGUL SYLLABLE NYAP +B0EB;B0EB;1102 1163 11C2;B0EB;1102 1163 11C2; # (냫; 냫; 냫; 냫; 냫; ) HANGUL SYLLABLE NYAH +B0EC;B0EC;1102 1164;B0EC;1102 1164; # (냬; 냬; 냬; 냬; 냬; ) HANGUL SYLLABLE NYAE +B0ED;B0ED;1102 1164 11A8;B0ED;1102 1164 11A8; # (냭; 냭; 냭; 냭; 냭; ) HANGUL SYLLABLE NYAEG +B0EE;B0EE;1102 1164 11A9;B0EE;1102 1164 11A9; # (냮; 냮; 냮; 냮; 냮; ) HANGUL SYLLABLE NYAEGG +B0EF;B0EF;1102 1164 11AA;B0EF;1102 1164 11AA; # (냯; 냯; 냯; 냯; 냯; ) HANGUL SYLLABLE NYAEGS +B0F0;B0F0;1102 1164 11AB;B0F0;1102 1164 11AB; # (냰; 냰; 냰; 냰; 냰; ) HANGUL SYLLABLE NYAEN +B0F1;B0F1;1102 1164 11AC;B0F1;1102 1164 11AC; # (냱; 냱; 냱; 냱; 냱; ) HANGUL SYLLABLE NYAENJ +B0F2;B0F2;1102 1164 11AD;B0F2;1102 1164 11AD; # (냲; 냲; 냲; 냲; 냲; ) HANGUL SYLLABLE NYAENH +B0F3;B0F3;1102 1164 11AE;B0F3;1102 1164 11AE; # (냳; 냳; 냳; 냳; 냳; ) HANGUL SYLLABLE NYAED +B0F4;B0F4;1102 1164 11AF;B0F4;1102 1164 11AF; # (냴; 냴; 냴; 냴; 냴; ) HANGUL SYLLABLE NYAEL +B0F5;B0F5;1102 1164 11B0;B0F5;1102 1164 11B0; # (냵; 냵; 냵; 냵; 냵; ) HANGUL SYLLABLE NYAELG +B0F6;B0F6;1102 1164 11B1;B0F6;1102 1164 11B1; # (냶; 냶; 냶; 냶; 냶; ) HANGUL SYLLABLE NYAELM +B0F7;B0F7;1102 1164 11B2;B0F7;1102 1164 11B2; # (냷; 냷; 냷; 냷; 냷; ) HANGUL SYLLABLE NYAELB +B0F8;B0F8;1102 1164 11B3;B0F8;1102 1164 11B3; # (냸; 냸; 냸; 냸; 냸; ) HANGUL SYLLABLE NYAELS +B0F9;B0F9;1102 1164 11B4;B0F9;1102 1164 11B4; # (냹; 냹; 냹; 냹; 냹; ) HANGUL SYLLABLE NYAELT +B0FA;B0FA;1102 1164 11B5;B0FA;1102 1164 11B5; # (냺; 냺; 냺; 냺; 냺; ) HANGUL SYLLABLE NYAELP +B0FB;B0FB;1102 1164 11B6;B0FB;1102 1164 11B6; # (냻; 냻; 냻; 냻; 냻; ) HANGUL SYLLABLE NYAELH +B0FC;B0FC;1102 1164 11B7;B0FC;1102 1164 11B7; # (냼; 냼; 냼; 냼; 냼; ) HANGUL SYLLABLE NYAEM +B0FD;B0FD;1102 1164 11B8;B0FD;1102 1164 11B8; # (냽; 냽; 냽; 냽; 냽; ) HANGUL SYLLABLE NYAEB +B0FE;B0FE;1102 1164 11B9;B0FE;1102 1164 11B9; # (냾; 냾; 냾; 냾; 냾; ) HANGUL SYLLABLE NYAEBS +B0FF;B0FF;1102 1164 11BA;B0FF;1102 1164 11BA; # (냿; 냿; 냿; 냿; 냿; ) HANGUL SYLLABLE NYAES +B100;B100;1102 1164 11BB;B100;1102 1164 11BB; # (넀; 넀; 넀; 넀; 넀; ) HANGUL SYLLABLE NYAESS +B101;B101;1102 1164 11BC;B101;1102 1164 11BC; # (넁; 넁; 넁; 넁; 넁; ) HANGUL SYLLABLE NYAENG +B102;B102;1102 1164 11BD;B102;1102 1164 11BD; # (넂; 넂; 넂; 넂; 넂; ) HANGUL SYLLABLE NYAEJ +B103;B103;1102 1164 11BE;B103;1102 1164 11BE; # (넃; 넃; 넃; 넃; 넃; ) HANGUL SYLLABLE NYAEC +B104;B104;1102 1164 11BF;B104;1102 1164 11BF; # (넄; 넄; 넄; 넄; 넄; ) HANGUL SYLLABLE NYAEK +B105;B105;1102 1164 11C0;B105;1102 1164 11C0; # (넅; 넅; 넅; 넅; 넅; ) HANGUL SYLLABLE NYAET +B106;B106;1102 1164 11C1;B106;1102 1164 11C1; # (넆; 넆; 넆; 넆; 넆; ) HANGUL SYLLABLE NYAEP +B107;B107;1102 1164 11C2;B107;1102 1164 11C2; # (넇; 넇; 넇; 넇; 넇; ) HANGUL SYLLABLE NYAEH +B108;B108;1102 1165;B108;1102 1165; # (너; 너; 너; 너; 너; ) HANGUL SYLLABLE NEO +B109;B109;1102 1165 11A8;B109;1102 1165 11A8; # (넉; 넉; 넉; 넉; 넉; ) HANGUL SYLLABLE NEOG +B10A;B10A;1102 1165 11A9;B10A;1102 1165 11A9; # (넊; 넊; 넊; 넊; 넊; ) HANGUL SYLLABLE NEOGG +B10B;B10B;1102 1165 11AA;B10B;1102 1165 11AA; # (넋; 넋; 넋; 넋; 넋; ) HANGUL SYLLABLE NEOGS +B10C;B10C;1102 1165 11AB;B10C;1102 1165 11AB; # (넌; 넌; 넌; 넌; 넌; ) HANGUL SYLLABLE NEON +B10D;B10D;1102 1165 11AC;B10D;1102 1165 11AC; # (넍; 넍; 넍; 넍; 넍; ) HANGUL SYLLABLE NEONJ +B10E;B10E;1102 1165 11AD;B10E;1102 1165 11AD; # (넎; 넎; 넎; 넎; 넎; ) HANGUL SYLLABLE NEONH +B10F;B10F;1102 1165 11AE;B10F;1102 1165 11AE; # (넏; 넏; 넏; 넏; 넏; ) HANGUL SYLLABLE NEOD +B110;B110;1102 1165 11AF;B110;1102 1165 11AF; # (널; 널; 널; 널; 널; ) HANGUL SYLLABLE NEOL +B111;B111;1102 1165 11B0;B111;1102 1165 11B0; # (넑; 넑; 넑; 넑; 넑; ) HANGUL SYLLABLE NEOLG +B112;B112;1102 1165 11B1;B112;1102 1165 11B1; # (넒; 넒; 넒; 넒; 넒; ) HANGUL SYLLABLE NEOLM +B113;B113;1102 1165 11B2;B113;1102 1165 11B2; # (넓; 넓; 넓; 넓; 넓; ) HANGUL SYLLABLE NEOLB +B114;B114;1102 1165 11B3;B114;1102 1165 11B3; # (넔; 넔; 넔; 넔; 넔; ) HANGUL SYLLABLE NEOLS +B115;B115;1102 1165 11B4;B115;1102 1165 11B4; # (넕; 넕; 넕; 넕; 넕; ) HANGUL SYLLABLE NEOLT +B116;B116;1102 1165 11B5;B116;1102 1165 11B5; # (넖; 넖; 넖; 넖; 넖; ) HANGUL SYLLABLE NEOLP +B117;B117;1102 1165 11B6;B117;1102 1165 11B6; # (넗; 넗; 넗; 넗; 넗; ) HANGUL SYLLABLE NEOLH +B118;B118;1102 1165 11B7;B118;1102 1165 11B7; # (넘; 넘; 넘; 넘; 넘; ) HANGUL SYLLABLE NEOM +B119;B119;1102 1165 11B8;B119;1102 1165 11B8; # (넙; 넙; 넙; 넙; 넙; ) HANGUL SYLLABLE NEOB +B11A;B11A;1102 1165 11B9;B11A;1102 1165 11B9; # (넚; 넚; 넚; 넚; 넚; ) HANGUL SYLLABLE NEOBS +B11B;B11B;1102 1165 11BA;B11B;1102 1165 11BA; # (넛; 넛; 넛; 넛; 넛; ) HANGUL SYLLABLE NEOS +B11C;B11C;1102 1165 11BB;B11C;1102 1165 11BB; # (넜; 넜; 넜; 넜; 넜; ) HANGUL SYLLABLE NEOSS +B11D;B11D;1102 1165 11BC;B11D;1102 1165 11BC; # (넝; 넝; 넝; 넝; 넝; ) HANGUL SYLLABLE NEONG +B11E;B11E;1102 1165 11BD;B11E;1102 1165 11BD; # (넞; 넞; 넞; 넞; 넞; ) HANGUL SYLLABLE NEOJ +B11F;B11F;1102 1165 11BE;B11F;1102 1165 11BE; # (넟; 넟; 넟; 넟; 넟; ) HANGUL SYLLABLE NEOC +B120;B120;1102 1165 11BF;B120;1102 1165 11BF; # (넠; 넠; 넠; 넠; 넠; ) HANGUL SYLLABLE NEOK +B121;B121;1102 1165 11C0;B121;1102 1165 11C0; # (넡; 넡; 넡; 넡; 넡; ) HANGUL SYLLABLE NEOT +B122;B122;1102 1165 11C1;B122;1102 1165 11C1; # (넢; 넢; 넢; 넢; 넢; ) HANGUL SYLLABLE NEOP +B123;B123;1102 1165 11C2;B123;1102 1165 11C2; # (넣; 넣; 넣; 넣; 넣; ) HANGUL SYLLABLE NEOH +B124;B124;1102 1166;B124;1102 1166; # (네; 네; 네; 네; 네; ) HANGUL SYLLABLE NE +B125;B125;1102 1166 11A8;B125;1102 1166 11A8; # (넥; 넥; 넥; 넥; 넥; ) HANGUL SYLLABLE NEG +B126;B126;1102 1166 11A9;B126;1102 1166 11A9; # (넦; 넦; 넦; 넦; 넦; ) HANGUL SYLLABLE NEGG +B127;B127;1102 1166 11AA;B127;1102 1166 11AA; # (넧; 넧; 넧; 넧; 넧; ) HANGUL SYLLABLE NEGS +B128;B128;1102 1166 11AB;B128;1102 1166 11AB; # (넨; 넨; 넨; 넨; 넨; ) HANGUL SYLLABLE NEN +B129;B129;1102 1166 11AC;B129;1102 1166 11AC; # (넩; 넩; 넩; 넩; 넩; ) HANGUL SYLLABLE NENJ +B12A;B12A;1102 1166 11AD;B12A;1102 1166 11AD; # (넪; 넪; 넪; 넪; 넪; ) HANGUL SYLLABLE NENH +B12B;B12B;1102 1166 11AE;B12B;1102 1166 11AE; # (넫; 넫; 넫; 넫; 넫; ) HANGUL SYLLABLE NED +B12C;B12C;1102 1166 11AF;B12C;1102 1166 11AF; # (넬; 넬; 넬; 넬; 넬; ) HANGUL SYLLABLE NEL +B12D;B12D;1102 1166 11B0;B12D;1102 1166 11B0; # (넭; 넭; 넭; 넭; 넭; ) HANGUL SYLLABLE NELG +B12E;B12E;1102 1166 11B1;B12E;1102 1166 11B1; # (넮; 넮; 넮; 넮; 넮; ) HANGUL SYLLABLE NELM +B12F;B12F;1102 1166 11B2;B12F;1102 1166 11B2; # (넯; 넯; 넯; 넯; 넯; ) HANGUL SYLLABLE NELB +B130;B130;1102 1166 11B3;B130;1102 1166 11B3; # (넰; 넰; 넰; 넰; 넰; ) HANGUL SYLLABLE NELS +B131;B131;1102 1166 11B4;B131;1102 1166 11B4; # (넱; 넱; 넱; 넱; 넱; ) HANGUL SYLLABLE NELT +B132;B132;1102 1166 11B5;B132;1102 1166 11B5; # (넲; 넲; 넲; 넲; 넲; ) HANGUL SYLLABLE NELP +B133;B133;1102 1166 11B6;B133;1102 1166 11B6; # (넳; 넳; 넳; 넳; 넳; ) HANGUL SYLLABLE NELH +B134;B134;1102 1166 11B7;B134;1102 1166 11B7; # (넴; 넴; 넴; 넴; 넴; ) HANGUL SYLLABLE NEM +B135;B135;1102 1166 11B8;B135;1102 1166 11B8; # (넵; 넵; 넵; 넵; 넵; ) HANGUL SYLLABLE NEB +B136;B136;1102 1166 11B9;B136;1102 1166 11B9; # (넶; 넶; 넶; 넶; 넶; ) HANGUL SYLLABLE NEBS +B137;B137;1102 1166 11BA;B137;1102 1166 11BA; # (넷; 넷; 넷; 넷; 넷; ) HANGUL SYLLABLE NES +B138;B138;1102 1166 11BB;B138;1102 1166 11BB; # (넸; 넸; 넸; 넸; 넸; ) HANGUL SYLLABLE NESS +B139;B139;1102 1166 11BC;B139;1102 1166 11BC; # (넹; 넹; 넹; 넹; 넹; ) HANGUL SYLLABLE NENG +B13A;B13A;1102 1166 11BD;B13A;1102 1166 11BD; # (넺; 넺; 넺; 넺; 넺; ) HANGUL SYLLABLE NEJ +B13B;B13B;1102 1166 11BE;B13B;1102 1166 11BE; # (넻; 넻; 넻; 넻; 넻; ) HANGUL SYLLABLE NEC +B13C;B13C;1102 1166 11BF;B13C;1102 1166 11BF; # (넼; 넼; 넼; 넼; 넼; ) HANGUL SYLLABLE NEK +B13D;B13D;1102 1166 11C0;B13D;1102 1166 11C0; # (넽; 넽; 넽; 넽; 넽; ) HANGUL SYLLABLE NET +B13E;B13E;1102 1166 11C1;B13E;1102 1166 11C1; # (넾; 넾; 넾; 넾; 넾; ) HANGUL SYLLABLE NEP +B13F;B13F;1102 1166 11C2;B13F;1102 1166 11C2; # (넿; 넿; 넿; 넿; 넿; ) HANGUL SYLLABLE NEH +B140;B140;1102 1167;B140;1102 1167; # (녀; 녀; 녀; 녀; 녀; ) HANGUL SYLLABLE NYEO +B141;B141;1102 1167 11A8;B141;1102 1167 11A8; # (녁; 녁; 녁; 녁; 녁; ) HANGUL SYLLABLE NYEOG +B142;B142;1102 1167 11A9;B142;1102 1167 11A9; # (녂; 녂; 녂; 녂; 녂; ) HANGUL SYLLABLE NYEOGG +B143;B143;1102 1167 11AA;B143;1102 1167 11AA; # (녃; 녃; 녃; 녃; 녃; ) HANGUL SYLLABLE NYEOGS +B144;B144;1102 1167 11AB;B144;1102 1167 11AB; # (년; 년; 년; 년; 년; ) HANGUL SYLLABLE NYEON +B145;B145;1102 1167 11AC;B145;1102 1167 11AC; # (녅; 녅; 녅; 녅; 녅; ) HANGUL SYLLABLE NYEONJ +B146;B146;1102 1167 11AD;B146;1102 1167 11AD; # (녆; 녆; 녆; 녆; 녆; ) HANGUL SYLLABLE NYEONH +B147;B147;1102 1167 11AE;B147;1102 1167 11AE; # (녇; 녇; 녇; 녇; 녇; ) HANGUL SYLLABLE NYEOD +B148;B148;1102 1167 11AF;B148;1102 1167 11AF; # (녈; 녈; 녈; 녈; 녈; ) HANGUL SYLLABLE NYEOL +B149;B149;1102 1167 11B0;B149;1102 1167 11B0; # (녉; 녉; 녉; 녉; 녉; ) HANGUL SYLLABLE NYEOLG +B14A;B14A;1102 1167 11B1;B14A;1102 1167 11B1; # (녊; 녊; 녊; 녊; 녊; ) HANGUL SYLLABLE NYEOLM +B14B;B14B;1102 1167 11B2;B14B;1102 1167 11B2; # (녋; 녋; 녋; 녋; 녋; ) HANGUL SYLLABLE NYEOLB +B14C;B14C;1102 1167 11B3;B14C;1102 1167 11B3; # (녌; 녌; 녌; 녌; 녌; ) HANGUL SYLLABLE NYEOLS +B14D;B14D;1102 1167 11B4;B14D;1102 1167 11B4; # (녍; 녍; 녍; 녍; 녍; ) HANGUL SYLLABLE NYEOLT +B14E;B14E;1102 1167 11B5;B14E;1102 1167 11B5; # (녎; 녎; 녎; 녎; 녎; ) HANGUL SYLLABLE NYEOLP +B14F;B14F;1102 1167 11B6;B14F;1102 1167 11B6; # (녏; 녏; 녏; 녏; 녏; ) HANGUL SYLLABLE NYEOLH +B150;B150;1102 1167 11B7;B150;1102 1167 11B7; # (념; 념; 념; 념; 념; ) HANGUL SYLLABLE NYEOM +B151;B151;1102 1167 11B8;B151;1102 1167 11B8; # (녑; 녑; 녑; 녑; 녑; ) HANGUL SYLLABLE NYEOB +B152;B152;1102 1167 11B9;B152;1102 1167 11B9; # (녒; 녒; 녒; 녒; 녒; ) HANGUL SYLLABLE NYEOBS +B153;B153;1102 1167 11BA;B153;1102 1167 11BA; # (녓; 녓; 녓; 녓; 녓; ) HANGUL SYLLABLE NYEOS +B154;B154;1102 1167 11BB;B154;1102 1167 11BB; # (녔; 녔; 녔; 녔; 녔; ) HANGUL SYLLABLE NYEOSS +B155;B155;1102 1167 11BC;B155;1102 1167 11BC; # (녕; 녕; 녕; 녕; 녕; ) HANGUL SYLLABLE NYEONG +B156;B156;1102 1167 11BD;B156;1102 1167 11BD; # (녖; 녖; 녖; 녖; 녖; ) HANGUL SYLLABLE NYEOJ +B157;B157;1102 1167 11BE;B157;1102 1167 11BE; # (녗; 녗; 녗; 녗; 녗; ) HANGUL SYLLABLE NYEOC +B158;B158;1102 1167 11BF;B158;1102 1167 11BF; # (녘; 녘; 녘; 녘; 녘; ) HANGUL SYLLABLE NYEOK +B159;B159;1102 1167 11C0;B159;1102 1167 11C0; # (녙; 녙; 녙; 녙; 녙; ) HANGUL SYLLABLE NYEOT +B15A;B15A;1102 1167 11C1;B15A;1102 1167 11C1; # (녚; 녚; 녚; 녚; 녚; ) HANGUL SYLLABLE NYEOP +B15B;B15B;1102 1167 11C2;B15B;1102 1167 11C2; # (녛; 녛; 녛; 녛; 녛; ) HANGUL SYLLABLE NYEOH +B15C;B15C;1102 1168;B15C;1102 1168; # (녜; 녜; 녜; 녜; 녜; ) HANGUL SYLLABLE NYE +B15D;B15D;1102 1168 11A8;B15D;1102 1168 11A8; # (녝; 녝; 녝; 녝; 녝; ) HANGUL SYLLABLE NYEG +B15E;B15E;1102 1168 11A9;B15E;1102 1168 11A9; # (녞; 녞; 녞; 녞; 녞; ) HANGUL SYLLABLE NYEGG +B15F;B15F;1102 1168 11AA;B15F;1102 1168 11AA; # (녟; 녟; 녟; 녟; 녟; ) HANGUL SYLLABLE NYEGS +B160;B160;1102 1168 11AB;B160;1102 1168 11AB; # (녠; 녠; 녠; 녠; 녠; ) HANGUL SYLLABLE NYEN +B161;B161;1102 1168 11AC;B161;1102 1168 11AC; # (녡; 녡; 녡; 녡; 녡; ) HANGUL SYLLABLE NYENJ +B162;B162;1102 1168 11AD;B162;1102 1168 11AD; # (녢; 녢; 녢; 녢; 녢; ) HANGUL SYLLABLE NYENH +B163;B163;1102 1168 11AE;B163;1102 1168 11AE; # (녣; 녣; 녣; 녣; 녣; ) HANGUL SYLLABLE NYED +B164;B164;1102 1168 11AF;B164;1102 1168 11AF; # (녤; 녤; 녤; 녤; 녤; ) HANGUL SYLLABLE NYEL +B165;B165;1102 1168 11B0;B165;1102 1168 11B0; # (녥; 녥; 녥; 녥; 녥; ) HANGUL SYLLABLE NYELG +B166;B166;1102 1168 11B1;B166;1102 1168 11B1; # (녦; 녦; 녦; 녦; 녦; ) HANGUL SYLLABLE NYELM +B167;B167;1102 1168 11B2;B167;1102 1168 11B2; # (녧; 녧; 녧; 녧; 녧; ) HANGUL SYLLABLE NYELB +B168;B168;1102 1168 11B3;B168;1102 1168 11B3; # (녨; 녨; 녨; 녨; 녨; ) HANGUL SYLLABLE NYELS +B169;B169;1102 1168 11B4;B169;1102 1168 11B4; # (녩; 녩; 녩; 녩; 녩; ) HANGUL SYLLABLE NYELT +B16A;B16A;1102 1168 11B5;B16A;1102 1168 11B5; # (녪; 녪; 녪; 녪; 녪; ) HANGUL SYLLABLE NYELP +B16B;B16B;1102 1168 11B6;B16B;1102 1168 11B6; # (녫; 녫; 녫; 녫; 녫; ) HANGUL SYLLABLE NYELH +B16C;B16C;1102 1168 11B7;B16C;1102 1168 11B7; # (녬; 녬; 녬; 녬; 녬; ) HANGUL SYLLABLE NYEM +B16D;B16D;1102 1168 11B8;B16D;1102 1168 11B8; # (녭; 녭; 녭; 녭; 녭; ) HANGUL SYLLABLE NYEB +B16E;B16E;1102 1168 11B9;B16E;1102 1168 11B9; # (녮; 녮; 녮; 녮; 녮; ) HANGUL SYLLABLE NYEBS +B16F;B16F;1102 1168 11BA;B16F;1102 1168 11BA; # (녯; 녯; 녯; 녯; 녯; ) HANGUL SYLLABLE NYES +B170;B170;1102 1168 11BB;B170;1102 1168 11BB; # (녰; 녰; 녰; 녰; 녰; ) HANGUL SYLLABLE NYESS +B171;B171;1102 1168 11BC;B171;1102 1168 11BC; # (녱; 녱; 녱; 녱; 녱; ) HANGUL SYLLABLE NYENG +B172;B172;1102 1168 11BD;B172;1102 1168 11BD; # (녲; 녲; 녲; 녲; 녲; ) HANGUL SYLLABLE NYEJ +B173;B173;1102 1168 11BE;B173;1102 1168 11BE; # (녳; 녳; 녳; 녳; 녳; ) HANGUL SYLLABLE NYEC +B174;B174;1102 1168 11BF;B174;1102 1168 11BF; # (녴; 녴; 녴; 녴; 녴; ) HANGUL SYLLABLE NYEK +B175;B175;1102 1168 11C0;B175;1102 1168 11C0; # (녵; 녵; 녵; 녵; 녵; ) HANGUL SYLLABLE NYET +B176;B176;1102 1168 11C1;B176;1102 1168 11C1; # (녶; 녶; 녶; 녶; 녶; ) HANGUL SYLLABLE NYEP +B177;B177;1102 1168 11C2;B177;1102 1168 11C2; # (녷; 녷; 녷; 녷; 녷; ) HANGUL SYLLABLE NYEH +B178;B178;1102 1169;B178;1102 1169; # (노; 노; 노; 노; 노; ) HANGUL SYLLABLE NO +B179;B179;1102 1169 11A8;B179;1102 1169 11A8; # (녹; 녹; 녹; 녹; 녹; ) HANGUL SYLLABLE NOG +B17A;B17A;1102 1169 11A9;B17A;1102 1169 11A9; # (녺; 녺; 녺; 녺; 녺; ) HANGUL SYLLABLE NOGG +B17B;B17B;1102 1169 11AA;B17B;1102 1169 11AA; # (녻; 녻; 녻; 녻; 녻; ) HANGUL SYLLABLE NOGS +B17C;B17C;1102 1169 11AB;B17C;1102 1169 11AB; # (논; 논; 논; 논; 논; ) HANGUL SYLLABLE NON +B17D;B17D;1102 1169 11AC;B17D;1102 1169 11AC; # (녽; 녽; 녽; 녽; 녽; ) HANGUL SYLLABLE NONJ +B17E;B17E;1102 1169 11AD;B17E;1102 1169 11AD; # (녾; 녾; 녾; 녾; 녾; ) HANGUL SYLLABLE NONH +B17F;B17F;1102 1169 11AE;B17F;1102 1169 11AE; # (녿; 녿; 녿; 녿; 녿; ) HANGUL SYLLABLE NOD +B180;B180;1102 1169 11AF;B180;1102 1169 11AF; # (놀; 놀; 놀; 놀; 놀; ) HANGUL SYLLABLE NOL +B181;B181;1102 1169 11B0;B181;1102 1169 11B0; # (놁; 놁; 놁; 놁; 놁; ) HANGUL SYLLABLE NOLG +B182;B182;1102 1169 11B1;B182;1102 1169 11B1; # (놂; 놂; 놂; 놂; 놂; ) HANGUL SYLLABLE NOLM +B183;B183;1102 1169 11B2;B183;1102 1169 11B2; # (놃; 놃; 놃; 놃; 놃; ) HANGUL SYLLABLE NOLB +B184;B184;1102 1169 11B3;B184;1102 1169 11B3; # (놄; 놄; 놄; 놄; 놄; ) HANGUL SYLLABLE NOLS +B185;B185;1102 1169 11B4;B185;1102 1169 11B4; # (놅; 놅; 놅; 놅; 놅; ) HANGUL SYLLABLE NOLT +B186;B186;1102 1169 11B5;B186;1102 1169 11B5; # (놆; 놆; 놆; 놆; 놆; ) HANGUL SYLLABLE NOLP +B187;B187;1102 1169 11B6;B187;1102 1169 11B6; # (놇; 놇; 놇; 놇; 놇; ) HANGUL SYLLABLE NOLH +B188;B188;1102 1169 11B7;B188;1102 1169 11B7; # (놈; 놈; 놈; 놈; 놈; ) HANGUL SYLLABLE NOM +B189;B189;1102 1169 11B8;B189;1102 1169 11B8; # (놉; 놉; 놉; 놉; 놉; ) HANGUL SYLLABLE NOB +B18A;B18A;1102 1169 11B9;B18A;1102 1169 11B9; # (놊; 놊; 놊; 놊; 놊; ) HANGUL SYLLABLE NOBS +B18B;B18B;1102 1169 11BA;B18B;1102 1169 11BA; # (놋; 놋; 놋; 놋; 놋; ) HANGUL SYLLABLE NOS +B18C;B18C;1102 1169 11BB;B18C;1102 1169 11BB; # (놌; 놌; 놌; 놌; 놌; ) HANGUL SYLLABLE NOSS +B18D;B18D;1102 1169 11BC;B18D;1102 1169 11BC; # (농; 농; 농; 농; 농; ) HANGUL SYLLABLE NONG +B18E;B18E;1102 1169 11BD;B18E;1102 1169 11BD; # (놎; 놎; 놎; 놎; 놎; ) HANGUL SYLLABLE NOJ +B18F;B18F;1102 1169 11BE;B18F;1102 1169 11BE; # (놏; 놏; 놏; 놏; 놏; ) HANGUL SYLLABLE NOC +B190;B190;1102 1169 11BF;B190;1102 1169 11BF; # (놐; 놐; 놐; 놐; 놐; ) HANGUL SYLLABLE NOK +B191;B191;1102 1169 11C0;B191;1102 1169 11C0; # (놑; 놑; 놑; 놑; 놑; ) HANGUL SYLLABLE NOT +B192;B192;1102 1169 11C1;B192;1102 1169 11C1; # (높; 높; 높; 높; 높; ) HANGUL SYLLABLE NOP +B193;B193;1102 1169 11C2;B193;1102 1169 11C2; # (놓; 놓; 놓; 놓; 놓; ) HANGUL SYLLABLE NOH +B194;B194;1102 116A;B194;1102 116A; # (놔; 놔; 놔; 놔; 놔; ) HANGUL SYLLABLE NWA +B195;B195;1102 116A 11A8;B195;1102 116A 11A8; # (놕; 놕; 놕; 놕; 놕; ) HANGUL SYLLABLE NWAG +B196;B196;1102 116A 11A9;B196;1102 116A 11A9; # (놖; 놖; 놖; 놖; 놖; ) HANGUL SYLLABLE NWAGG +B197;B197;1102 116A 11AA;B197;1102 116A 11AA; # (놗; 놗; 놗; 놗; 놗; ) HANGUL SYLLABLE NWAGS +B198;B198;1102 116A 11AB;B198;1102 116A 11AB; # (놘; 놘; 놘; 놘; 놘; ) HANGUL SYLLABLE NWAN +B199;B199;1102 116A 11AC;B199;1102 116A 11AC; # (놙; 놙; 놙; 놙; 놙; ) HANGUL SYLLABLE NWANJ +B19A;B19A;1102 116A 11AD;B19A;1102 116A 11AD; # (놚; 놚; 놚; 놚; 놚; ) HANGUL SYLLABLE NWANH +B19B;B19B;1102 116A 11AE;B19B;1102 116A 11AE; # (놛; 놛; 놛; 놛; 놛; ) HANGUL SYLLABLE NWAD +B19C;B19C;1102 116A 11AF;B19C;1102 116A 11AF; # (놜; 놜; 놜; 놜; 놜; ) HANGUL SYLLABLE NWAL +B19D;B19D;1102 116A 11B0;B19D;1102 116A 11B0; # (놝; 놝; 놝; 놝; 놝; ) HANGUL SYLLABLE NWALG +B19E;B19E;1102 116A 11B1;B19E;1102 116A 11B1; # (놞; 놞; 놞; 놞; 놞; ) HANGUL SYLLABLE NWALM +B19F;B19F;1102 116A 11B2;B19F;1102 116A 11B2; # (놟; 놟; 놟; 놟; 놟; ) HANGUL SYLLABLE NWALB +B1A0;B1A0;1102 116A 11B3;B1A0;1102 116A 11B3; # (놠; 놠; 놠; 놠; 놠; ) HANGUL SYLLABLE NWALS +B1A1;B1A1;1102 116A 11B4;B1A1;1102 116A 11B4; # (놡; 놡; 놡; 놡; 놡; ) HANGUL SYLLABLE NWALT +B1A2;B1A2;1102 116A 11B5;B1A2;1102 116A 11B5; # (놢; 놢; 놢; 놢; 놢; ) HANGUL SYLLABLE NWALP +B1A3;B1A3;1102 116A 11B6;B1A3;1102 116A 11B6; # (놣; 놣; 놣; 놣; 놣; ) HANGUL SYLLABLE NWALH +B1A4;B1A4;1102 116A 11B7;B1A4;1102 116A 11B7; # (놤; 놤; 놤; 놤; 놤; ) HANGUL SYLLABLE NWAM +B1A5;B1A5;1102 116A 11B8;B1A5;1102 116A 11B8; # (놥; 놥; 놥; 놥; 놥; ) HANGUL SYLLABLE NWAB +B1A6;B1A6;1102 116A 11B9;B1A6;1102 116A 11B9; # (놦; 놦; 놦; 놦; 놦; ) HANGUL SYLLABLE NWABS +B1A7;B1A7;1102 116A 11BA;B1A7;1102 116A 11BA; # (놧; 놧; 놧; 놧; 놧; ) HANGUL SYLLABLE NWAS +B1A8;B1A8;1102 116A 11BB;B1A8;1102 116A 11BB; # (놨; 놨; 놨; 놨; 놨; ) HANGUL SYLLABLE NWASS +B1A9;B1A9;1102 116A 11BC;B1A9;1102 116A 11BC; # (놩; 놩; 놩; 놩; 놩; ) HANGUL SYLLABLE NWANG +B1AA;B1AA;1102 116A 11BD;B1AA;1102 116A 11BD; # (놪; 놪; 놪; 놪; 놪; ) HANGUL SYLLABLE NWAJ +B1AB;B1AB;1102 116A 11BE;B1AB;1102 116A 11BE; # (놫; 놫; 놫; 놫; 놫; ) HANGUL SYLLABLE NWAC +B1AC;B1AC;1102 116A 11BF;B1AC;1102 116A 11BF; # (놬; 놬; 놬; 놬; 놬; ) HANGUL SYLLABLE NWAK +B1AD;B1AD;1102 116A 11C0;B1AD;1102 116A 11C0; # (놭; 놭; 놭; 놭; 놭; ) HANGUL SYLLABLE NWAT +B1AE;B1AE;1102 116A 11C1;B1AE;1102 116A 11C1; # (놮; 놮; 놮; 놮; 놮; ) HANGUL SYLLABLE NWAP +B1AF;B1AF;1102 116A 11C2;B1AF;1102 116A 11C2; # (놯; 놯; 놯; 놯; 놯; ) HANGUL SYLLABLE NWAH +B1B0;B1B0;1102 116B;B1B0;1102 116B; # (놰; 놰; 놰; 놰; 놰; ) HANGUL SYLLABLE NWAE +B1B1;B1B1;1102 116B 11A8;B1B1;1102 116B 11A8; # (놱; 놱; 놱; 놱; 놱; ) HANGUL SYLLABLE NWAEG +B1B2;B1B2;1102 116B 11A9;B1B2;1102 116B 11A9; # (놲; 놲; 놲; 놲; 놲; ) HANGUL SYLLABLE NWAEGG +B1B3;B1B3;1102 116B 11AA;B1B3;1102 116B 11AA; # (놳; 놳; 놳; 놳; 놳; ) HANGUL SYLLABLE NWAEGS +B1B4;B1B4;1102 116B 11AB;B1B4;1102 116B 11AB; # (놴; 놴; 놴; 놴; 놴; ) HANGUL SYLLABLE NWAEN +B1B5;B1B5;1102 116B 11AC;B1B5;1102 116B 11AC; # (놵; 놵; 놵; 놵; 놵; ) HANGUL SYLLABLE NWAENJ +B1B6;B1B6;1102 116B 11AD;B1B6;1102 116B 11AD; # (놶; 놶; 놶; 놶; 놶; ) HANGUL SYLLABLE NWAENH +B1B7;B1B7;1102 116B 11AE;B1B7;1102 116B 11AE; # (놷; 놷; 놷; 놷; 놷; ) HANGUL SYLLABLE NWAED +B1B8;B1B8;1102 116B 11AF;B1B8;1102 116B 11AF; # (놸; 놸; 놸; 놸; 놸; ) HANGUL SYLLABLE NWAEL +B1B9;B1B9;1102 116B 11B0;B1B9;1102 116B 11B0; # (놹; 놹; 놹; 놹; 놹; ) HANGUL SYLLABLE NWAELG +B1BA;B1BA;1102 116B 11B1;B1BA;1102 116B 11B1; # (놺; 놺; 놺; 놺; 놺; ) HANGUL SYLLABLE NWAELM +B1BB;B1BB;1102 116B 11B2;B1BB;1102 116B 11B2; # (놻; 놻; 놻; 놻; 놻; ) HANGUL SYLLABLE NWAELB +B1BC;B1BC;1102 116B 11B3;B1BC;1102 116B 11B3; # (놼; 놼; 놼; 놼; 놼; ) HANGUL SYLLABLE NWAELS +B1BD;B1BD;1102 116B 11B4;B1BD;1102 116B 11B4; # (놽; 놽; 놽; 놽; 놽; ) HANGUL SYLLABLE NWAELT +B1BE;B1BE;1102 116B 11B5;B1BE;1102 116B 11B5; # (놾; 놾; 놾; 놾; 놾; ) HANGUL SYLLABLE NWAELP +B1BF;B1BF;1102 116B 11B6;B1BF;1102 116B 11B6; # (놿; 놿; 놿; 놿; 놿; ) HANGUL SYLLABLE NWAELH +B1C0;B1C0;1102 116B 11B7;B1C0;1102 116B 11B7; # (뇀; 뇀; 뇀; 뇀; 뇀; ) HANGUL SYLLABLE NWAEM +B1C1;B1C1;1102 116B 11B8;B1C1;1102 116B 11B8; # (뇁; 뇁; 뇁; 뇁; 뇁; ) HANGUL SYLLABLE NWAEB +B1C2;B1C2;1102 116B 11B9;B1C2;1102 116B 11B9; # (뇂; 뇂; 뇂; 뇂; 뇂; ) HANGUL SYLLABLE NWAEBS +B1C3;B1C3;1102 116B 11BA;B1C3;1102 116B 11BA; # (뇃; 뇃; 뇃; 뇃; 뇃; ) HANGUL SYLLABLE NWAES +B1C4;B1C4;1102 116B 11BB;B1C4;1102 116B 11BB; # (뇄; 뇄; 뇄; 뇄; 뇄; ) HANGUL SYLLABLE NWAESS +B1C5;B1C5;1102 116B 11BC;B1C5;1102 116B 11BC; # (뇅; 뇅; 뇅; 뇅; 뇅; ) HANGUL SYLLABLE NWAENG +B1C6;B1C6;1102 116B 11BD;B1C6;1102 116B 11BD; # (뇆; 뇆; 뇆; 뇆; 뇆; ) HANGUL SYLLABLE NWAEJ +B1C7;B1C7;1102 116B 11BE;B1C7;1102 116B 11BE; # (뇇; 뇇; 뇇; 뇇; 뇇; ) HANGUL SYLLABLE NWAEC +B1C8;B1C8;1102 116B 11BF;B1C8;1102 116B 11BF; # (뇈; 뇈; 뇈; 뇈; 뇈; ) HANGUL SYLLABLE NWAEK +B1C9;B1C9;1102 116B 11C0;B1C9;1102 116B 11C0; # (뇉; 뇉; 뇉; 뇉; 뇉; ) HANGUL SYLLABLE NWAET +B1CA;B1CA;1102 116B 11C1;B1CA;1102 116B 11C1; # (뇊; 뇊; 뇊; 뇊; 뇊; ) HANGUL SYLLABLE NWAEP +B1CB;B1CB;1102 116B 11C2;B1CB;1102 116B 11C2; # (뇋; 뇋; 뇋; 뇋; 뇋; ) HANGUL SYLLABLE NWAEH +B1CC;B1CC;1102 116C;B1CC;1102 116C; # (뇌; 뇌; 뇌; 뇌; 뇌; ) HANGUL SYLLABLE NOE +B1CD;B1CD;1102 116C 11A8;B1CD;1102 116C 11A8; # (뇍; 뇍; 뇍; 뇍; 뇍; ) HANGUL SYLLABLE NOEG +B1CE;B1CE;1102 116C 11A9;B1CE;1102 116C 11A9; # (뇎; 뇎; 뇎; 뇎; 뇎; ) HANGUL SYLLABLE NOEGG +B1CF;B1CF;1102 116C 11AA;B1CF;1102 116C 11AA; # (뇏; 뇏; 뇏; 뇏; 뇏; ) HANGUL SYLLABLE NOEGS +B1D0;B1D0;1102 116C 11AB;B1D0;1102 116C 11AB; # (뇐; 뇐; 뇐; 뇐; 뇐; ) HANGUL SYLLABLE NOEN +B1D1;B1D1;1102 116C 11AC;B1D1;1102 116C 11AC; # (뇑; 뇑; 뇑; 뇑; 뇑; ) HANGUL SYLLABLE NOENJ +B1D2;B1D2;1102 116C 11AD;B1D2;1102 116C 11AD; # (뇒; 뇒; 뇒; 뇒; 뇒; ) HANGUL SYLLABLE NOENH +B1D3;B1D3;1102 116C 11AE;B1D3;1102 116C 11AE; # (뇓; 뇓; 뇓; 뇓; 뇓; ) HANGUL SYLLABLE NOED +B1D4;B1D4;1102 116C 11AF;B1D4;1102 116C 11AF; # (뇔; 뇔; 뇔; 뇔; 뇔; ) HANGUL SYLLABLE NOEL +B1D5;B1D5;1102 116C 11B0;B1D5;1102 116C 11B0; # (뇕; 뇕; 뇕; 뇕; 뇕; ) HANGUL SYLLABLE NOELG +B1D6;B1D6;1102 116C 11B1;B1D6;1102 116C 11B1; # (뇖; 뇖; 뇖; 뇖; 뇖; ) HANGUL SYLLABLE NOELM +B1D7;B1D7;1102 116C 11B2;B1D7;1102 116C 11B2; # (뇗; 뇗; 뇗; 뇗; 뇗; ) HANGUL SYLLABLE NOELB +B1D8;B1D8;1102 116C 11B3;B1D8;1102 116C 11B3; # (뇘; 뇘; 뇘; 뇘; 뇘; ) HANGUL SYLLABLE NOELS +B1D9;B1D9;1102 116C 11B4;B1D9;1102 116C 11B4; # (뇙; 뇙; 뇙; 뇙; 뇙; ) HANGUL SYLLABLE NOELT +B1DA;B1DA;1102 116C 11B5;B1DA;1102 116C 11B5; # (뇚; 뇚; 뇚; 뇚; 뇚; ) HANGUL SYLLABLE NOELP +B1DB;B1DB;1102 116C 11B6;B1DB;1102 116C 11B6; # (뇛; 뇛; 뇛; 뇛; 뇛; ) HANGUL SYLLABLE NOELH +B1DC;B1DC;1102 116C 11B7;B1DC;1102 116C 11B7; # (뇜; 뇜; 뇜; 뇜; 뇜; ) HANGUL SYLLABLE NOEM +B1DD;B1DD;1102 116C 11B8;B1DD;1102 116C 11B8; # (뇝; 뇝; 뇝; 뇝; 뇝; ) HANGUL SYLLABLE NOEB +B1DE;B1DE;1102 116C 11B9;B1DE;1102 116C 11B9; # (뇞; 뇞; 뇞; 뇞; 뇞; ) HANGUL SYLLABLE NOEBS +B1DF;B1DF;1102 116C 11BA;B1DF;1102 116C 11BA; # (뇟; 뇟; 뇟; 뇟; 뇟; ) HANGUL SYLLABLE NOES +B1E0;B1E0;1102 116C 11BB;B1E0;1102 116C 11BB; # (뇠; 뇠; 뇠; 뇠; 뇠; ) HANGUL SYLLABLE NOESS +B1E1;B1E1;1102 116C 11BC;B1E1;1102 116C 11BC; # (뇡; 뇡; 뇡; 뇡; 뇡; ) HANGUL SYLLABLE NOENG +B1E2;B1E2;1102 116C 11BD;B1E2;1102 116C 11BD; # (뇢; 뇢; 뇢; 뇢; 뇢; ) HANGUL SYLLABLE NOEJ +B1E3;B1E3;1102 116C 11BE;B1E3;1102 116C 11BE; # (뇣; 뇣; 뇣; 뇣; 뇣; ) HANGUL SYLLABLE NOEC +B1E4;B1E4;1102 116C 11BF;B1E4;1102 116C 11BF; # (뇤; 뇤; 뇤; 뇤; 뇤; ) HANGUL SYLLABLE NOEK +B1E5;B1E5;1102 116C 11C0;B1E5;1102 116C 11C0; # (뇥; 뇥; 뇥; 뇥; 뇥; ) HANGUL SYLLABLE NOET +B1E6;B1E6;1102 116C 11C1;B1E6;1102 116C 11C1; # (뇦; 뇦; 뇦; 뇦; 뇦; ) HANGUL SYLLABLE NOEP +B1E7;B1E7;1102 116C 11C2;B1E7;1102 116C 11C2; # (뇧; 뇧; 뇧; 뇧; 뇧; ) HANGUL SYLLABLE NOEH +B1E8;B1E8;1102 116D;B1E8;1102 116D; # (뇨; 뇨; 뇨; 뇨; 뇨; ) HANGUL SYLLABLE NYO +B1E9;B1E9;1102 116D 11A8;B1E9;1102 116D 11A8; # (뇩; 뇩; 뇩; 뇩; 뇩; ) HANGUL SYLLABLE NYOG +B1EA;B1EA;1102 116D 11A9;B1EA;1102 116D 11A9; # (뇪; 뇪; 뇪; 뇪; 뇪; ) HANGUL SYLLABLE NYOGG +B1EB;B1EB;1102 116D 11AA;B1EB;1102 116D 11AA; # (뇫; 뇫; 뇫; 뇫; 뇫; ) HANGUL SYLLABLE NYOGS +B1EC;B1EC;1102 116D 11AB;B1EC;1102 116D 11AB; # (뇬; 뇬; 뇬; 뇬; 뇬; ) HANGUL SYLLABLE NYON +B1ED;B1ED;1102 116D 11AC;B1ED;1102 116D 11AC; # (뇭; 뇭; 뇭; 뇭; 뇭; ) HANGUL SYLLABLE NYONJ +B1EE;B1EE;1102 116D 11AD;B1EE;1102 116D 11AD; # (뇮; 뇮; 뇮; 뇮; 뇮; ) HANGUL SYLLABLE NYONH +B1EF;B1EF;1102 116D 11AE;B1EF;1102 116D 11AE; # (뇯; 뇯; 뇯; 뇯; 뇯; ) HANGUL SYLLABLE NYOD +B1F0;B1F0;1102 116D 11AF;B1F0;1102 116D 11AF; # (뇰; 뇰; 뇰; 뇰; 뇰; ) HANGUL SYLLABLE NYOL +B1F1;B1F1;1102 116D 11B0;B1F1;1102 116D 11B0; # (뇱; 뇱; 뇱; 뇱; 뇱; ) HANGUL SYLLABLE NYOLG +B1F2;B1F2;1102 116D 11B1;B1F2;1102 116D 11B1; # (뇲; 뇲; 뇲; 뇲; 뇲; ) HANGUL SYLLABLE NYOLM +B1F3;B1F3;1102 116D 11B2;B1F3;1102 116D 11B2; # (뇳; 뇳; 뇳; 뇳; 뇳; ) HANGUL SYLLABLE NYOLB +B1F4;B1F4;1102 116D 11B3;B1F4;1102 116D 11B3; # (뇴; 뇴; 뇴; 뇴; 뇴; ) HANGUL SYLLABLE NYOLS +B1F5;B1F5;1102 116D 11B4;B1F5;1102 116D 11B4; # (뇵; 뇵; 뇵; 뇵; 뇵; ) HANGUL SYLLABLE NYOLT +B1F6;B1F6;1102 116D 11B5;B1F6;1102 116D 11B5; # (뇶; 뇶; 뇶; 뇶; 뇶; ) HANGUL SYLLABLE NYOLP +B1F7;B1F7;1102 116D 11B6;B1F7;1102 116D 11B6; # (뇷; 뇷; 뇷; 뇷; 뇷; ) HANGUL SYLLABLE NYOLH +B1F8;B1F8;1102 116D 11B7;B1F8;1102 116D 11B7; # (뇸; 뇸; 뇸; 뇸; 뇸; ) HANGUL SYLLABLE NYOM +B1F9;B1F9;1102 116D 11B8;B1F9;1102 116D 11B8; # (뇹; 뇹; 뇹; 뇹; 뇹; ) HANGUL SYLLABLE NYOB +B1FA;B1FA;1102 116D 11B9;B1FA;1102 116D 11B9; # (뇺; 뇺; 뇺; 뇺; 뇺; ) HANGUL SYLLABLE NYOBS +B1FB;B1FB;1102 116D 11BA;B1FB;1102 116D 11BA; # (뇻; 뇻; 뇻; 뇻; 뇻; ) HANGUL SYLLABLE NYOS +B1FC;B1FC;1102 116D 11BB;B1FC;1102 116D 11BB; # (뇼; 뇼; 뇼; 뇼; 뇼; ) HANGUL SYLLABLE NYOSS +B1FD;B1FD;1102 116D 11BC;B1FD;1102 116D 11BC; # (뇽; 뇽; 뇽; 뇽; 뇽; ) HANGUL SYLLABLE NYONG +B1FE;B1FE;1102 116D 11BD;B1FE;1102 116D 11BD; # (뇾; 뇾; 뇾; 뇾; 뇾; ) HANGUL SYLLABLE NYOJ +B1FF;B1FF;1102 116D 11BE;B1FF;1102 116D 11BE; # (뇿; 뇿; 뇿; 뇿; 뇿; ) HANGUL SYLLABLE NYOC +B200;B200;1102 116D 11BF;B200;1102 116D 11BF; # (눀; 눀; 눀; 눀; 눀; ) HANGUL SYLLABLE NYOK +B201;B201;1102 116D 11C0;B201;1102 116D 11C0; # (눁; 눁; 눁; 눁; 눁; ) HANGUL SYLLABLE NYOT +B202;B202;1102 116D 11C1;B202;1102 116D 11C1; # (눂; 눂; 눂; 눂; 눂; ) HANGUL SYLLABLE NYOP +B203;B203;1102 116D 11C2;B203;1102 116D 11C2; # (눃; 눃; 눃; 눃; 눃; ) HANGUL SYLLABLE NYOH +B204;B204;1102 116E;B204;1102 116E; # (누; 누; 누; 누; 누; ) HANGUL SYLLABLE NU +B205;B205;1102 116E 11A8;B205;1102 116E 11A8; # (눅; 눅; 눅; 눅; 눅; ) HANGUL SYLLABLE NUG +B206;B206;1102 116E 11A9;B206;1102 116E 11A9; # (눆; 눆; 눆; 눆; 눆; ) HANGUL SYLLABLE NUGG +B207;B207;1102 116E 11AA;B207;1102 116E 11AA; # (눇; 눇; 눇; 눇; 눇; ) HANGUL SYLLABLE NUGS +B208;B208;1102 116E 11AB;B208;1102 116E 11AB; # (눈; 눈; 눈; 눈; 눈; ) HANGUL SYLLABLE NUN +B209;B209;1102 116E 11AC;B209;1102 116E 11AC; # (눉; 눉; 눉; 눉; 눉; ) HANGUL SYLLABLE NUNJ +B20A;B20A;1102 116E 11AD;B20A;1102 116E 11AD; # (눊; 눊; 눊; 눊; 눊; ) HANGUL SYLLABLE NUNH +B20B;B20B;1102 116E 11AE;B20B;1102 116E 11AE; # (눋; 눋; 눋; 눋; 눋; ) HANGUL SYLLABLE NUD +B20C;B20C;1102 116E 11AF;B20C;1102 116E 11AF; # (눌; 눌; 눌; 눌; 눌; ) HANGUL SYLLABLE NUL +B20D;B20D;1102 116E 11B0;B20D;1102 116E 11B0; # (눍; 눍; 눍; 눍; 눍; ) HANGUL SYLLABLE NULG +B20E;B20E;1102 116E 11B1;B20E;1102 116E 11B1; # (눎; 눎; 눎; 눎; 눎; ) HANGUL SYLLABLE NULM +B20F;B20F;1102 116E 11B2;B20F;1102 116E 11B2; # (눏; 눏; 눏; 눏; 눏; ) HANGUL SYLLABLE NULB +B210;B210;1102 116E 11B3;B210;1102 116E 11B3; # (눐; 눐; 눐; 눐; 눐; ) HANGUL SYLLABLE NULS +B211;B211;1102 116E 11B4;B211;1102 116E 11B4; # (눑; 눑; 눑; 눑; 눑; ) HANGUL SYLLABLE NULT +B212;B212;1102 116E 11B5;B212;1102 116E 11B5; # (눒; 눒; 눒; 눒; 눒; ) HANGUL SYLLABLE NULP +B213;B213;1102 116E 11B6;B213;1102 116E 11B6; # (눓; 눓; 눓; 눓; 눓; ) HANGUL SYLLABLE NULH +B214;B214;1102 116E 11B7;B214;1102 116E 11B7; # (눔; 눔; 눔; 눔; 눔; ) HANGUL SYLLABLE NUM +B215;B215;1102 116E 11B8;B215;1102 116E 11B8; # (눕; 눕; 눕; 눕; 눕; ) HANGUL SYLLABLE NUB +B216;B216;1102 116E 11B9;B216;1102 116E 11B9; # (눖; 눖; 눖; 눖; 눖; ) HANGUL SYLLABLE NUBS +B217;B217;1102 116E 11BA;B217;1102 116E 11BA; # (눗; 눗; 눗; 눗; 눗; ) HANGUL SYLLABLE NUS +B218;B218;1102 116E 11BB;B218;1102 116E 11BB; # (눘; 눘; 눘; 눘; 눘; ) HANGUL SYLLABLE NUSS +B219;B219;1102 116E 11BC;B219;1102 116E 11BC; # (눙; 눙; 눙; 눙; 눙; ) HANGUL SYLLABLE NUNG +B21A;B21A;1102 116E 11BD;B21A;1102 116E 11BD; # (눚; 눚; 눚; 눚; 눚; ) HANGUL SYLLABLE NUJ +B21B;B21B;1102 116E 11BE;B21B;1102 116E 11BE; # (눛; 눛; 눛; 눛; 눛; ) HANGUL SYLLABLE NUC +B21C;B21C;1102 116E 11BF;B21C;1102 116E 11BF; # (눜; 눜; 눜; 눜; 눜; ) HANGUL SYLLABLE NUK +B21D;B21D;1102 116E 11C0;B21D;1102 116E 11C0; # (눝; 눝; 눝; 눝; 눝; ) HANGUL SYLLABLE NUT +B21E;B21E;1102 116E 11C1;B21E;1102 116E 11C1; # (눞; 눞; 눞; 눞; 눞; ) HANGUL SYLLABLE NUP +B21F;B21F;1102 116E 11C2;B21F;1102 116E 11C2; # (눟; 눟; 눟; 눟; 눟; ) HANGUL SYLLABLE NUH +B220;B220;1102 116F;B220;1102 116F; # (눠; 눠; 눠; 눠; 눠; ) HANGUL SYLLABLE NWEO +B221;B221;1102 116F 11A8;B221;1102 116F 11A8; # (눡; 눡; 눡; 눡; 눡; ) HANGUL SYLLABLE NWEOG +B222;B222;1102 116F 11A9;B222;1102 116F 11A9; # (눢; 눢; 눢; 눢; 눢; ) HANGUL SYLLABLE NWEOGG +B223;B223;1102 116F 11AA;B223;1102 116F 11AA; # (눣; 눣; 눣; 눣; 눣; ) HANGUL SYLLABLE NWEOGS +B224;B224;1102 116F 11AB;B224;1102 116F 11AB; # (눤; 눤; 눤; 눤; 눤; ) HANGUL SYLLABLE NWEON +B225;B225;1102 116F 11AC;B225;1102 116F 11AC; # (눥; 눥; 눥; 눥; 눥; ) HANGUL SYLLABLE NWEONJ +B226;B226;1102 116F 11AD;B226;1102 116F 11AD; # (눦; 눦; 눦; 눦; 눦; ) HANGUL SYLLABLE NWEONH +B227;B227;1102 116F 11AE;B227;1102 116F 11AE; # (눧; 눧; 눧; 눧; 눧; ) HANGUL SYLLABLE NWEOD +B228;B228;1102 116F 11AF;B228;1102 116F 11AF; # (눨; 눨; 눨; 눨; 눨; ) HANGUL SYLLABLE NWEOL +B229;B229;1102 116F 11B0;B229;1102 116F 11B0; # (눩; 눩; 눩; 눩; 눩; ) HANGUL SYLLABLE NWEOLG +B22A;B22A;1102 116F 11B1;B22A;1102 116F 11B1; # (눪; 눪; 눪; 눪; 눪; ) HANGUL SYLLABLE NWEOLM +B22B;B22B;1102 116F 11B2;B22B;1102 116F 11B2; # (눫; 눫; 눫; 눫; 눫; ) HANGUL SYLLABLE NWEOLB +B22C;B22C;1102 116F 11B3;B22C;1102 116F 11B3; # (눬; 눬; 눬; 눬; 눬; ) HANGUL SYLLABLE NWEOLS +B22D;B22D;1102 116F 11B4;B22D;1102 116F 11B4; # (눭; 눭; 눭; 눭; 눭; ) HANGUL SYLLABLE NWEOLT +B22E;B22E;1102 116F 11B5;B22E;1102 116F 11B5; # (눮; 눮; 눮; 눮; 눮; ) HANGUL SYLLABLE NWEOLP +B22F;B22F;1102 116F 11B6;B22F;1102 116F 11B6; # (눯; 눯; 눯; 눯; 눯; ) HANGUL SYLLABLE NWEOLH +B230;B230;1102 116F 11B7;B230;1102 116F 11B7; # (눰; 눰; 눰; 눰; 눰; ) HANGUL SYLLABLE NWEOM +B231;B231;1102 116F 11B8;B231;1102 116F 11B8; # (눱; 눱; 눱; 눱; 눱; ) HANGUL SYLLABLE NWEOB +B232;B232;1102 116F 11B9;B232;1102 116F 11B9; # (눲; 눲; 눲; 눲; 눲; ) HANGUL SYLLABLE NWEOBS +B233;B233;1102 116F 11BA;B233;1102 116F 11BA; # (눳; 눳; 눳; 눳; 눳; ) HANGUL SYLLABLE NWEOS +B234;B234;1102 116F 11BB;B234;1102 116F 11BB; # (눴; 눴; 눴; 눴; 눴; ) HANGUL SYLLABLE NWEOSS +B235;B235;1102 116F 11BC;B235;1102 116F 11BC; # (눵; 눵; 눵; 눵; 눵; ) HANGUL SYLLABLE NWEONG +B236;B236;1102 116F 11BD;B236;1102 116F 11BD; # (눶; 눶; 눶; 눶; 눶; ) HANGUL SYLLABLE NWEOJ +B237;B237;1102 116F 11BE;B237;1102 116F 11BE; # (눷; 눷; 눷; 눷; 눷; ) HANGUL SYLLABLE NWEOC +B238;B238;1102 116F 11BF;B238;1102 116F 11BF; # (눸; 눸; 눸; 눸; 눸; ) HANGUL SYLLABLE NWEOK +B239;B239;1102 116F 11C0;B239;1102 116F 11C0; # (눹; 눹; 눹; 눹; 눹; ) HANGUL SYLLABLE NWEOT +B23A;B23A;1102 116F 11C1;B23A;1102 116F 11C1; # (눺; 눺; 눺; 눺; 눺; ) HANGUL SYLLABLE NWEOP +B23B;B23B;1102 116F 11C2;B23B;1102 116F 11C2; # (눻; 눻; 눻; 눻; 눻; ) HANGUL SYLLABLE NWEOH +B23C;B23C;1102 1170;B23C;1102 1170; # (눼; 눼; 눼; 눼; 눼; ) HANGUL SYLLABLE NWE +B23D;B23D;1102 1170 11A8;B23D;1102 1170 11A8; # (눽; 눽; 눽; 눽; 눽; ) HANGUL SYLLABLE NWEG +B23E;B23E;1102 1170 11A9;B23E;1102 1170 11A9; # (눾; 눾; 눾; 눾; 눾; ) HANGUL SYLLABLE NWEGG +B23F;B23F;1102 1170 11AA;B23F;1102 1170 11AA; # (눿; 눿; 눿; 눿; 눿; ) HANGUL SYLLABLE NWEGS +B240;B240;1102 1170 11AB;B240;1102 1170 11AB; # (뉀; 뉀; 뉀; 뉀; 뉀; ) HANGUL SYLLABLE NWEN +B241;B241;1102 1170 11AC;B241;1102 1170 11AC; # (뉁; 뉁; 뉁; 뉁; 뉁; ) HANGUL SYLLABLE NWENJ +B242;B242;1102 1170 11AD;B242;1102 1170 11AD; # (뉂; 뉂; 뉂; 뉂; 뉂; ) HANGUL SYLLABLE NWENH +B243;B243;1102 1170 11AE;B243;1102 1170 11AE; # (뉃; 뉃; 뉃; 뉃; 뉃; ) HANGUL SYLLABLE NWED +B244;B244;1102 1170 11AF;B244;1102 1170 11AF; # (뉄; 뉄; 뉄; 뉄; 뉄; ) HANGUL SYLLABLE NWEL +B245;B245;1102 1170 11B0;B245;1102 1170 11B0; # (뉅; 뉅; 뉅; 뉅; 뉅; ) HANGUL SYLLABLE NWELG +B246;B246;1102 1170 11B1;B246;1102 1170 11B1; # (뉆; 뉆; 뉆; 뉆; 뉆; ) HANGUL SYLLABLE NWELM +B247;B247;1102 1170 11B2;B247;1102 1170 11B2; # (뉇; 뉇; 뉇; 뉇; 뉇; ) HANGUL SYLLABLE NWELB +B248;B248;1102 1170 11B3;B248;1102 1170 11B3; # (뉈; 뉈; 뉈; 뉈; 뉈; ) HANGUL SYLLABLE NWELS +B249;B249;1102 1170 11B4;B249;1102 1170 11B4; # (뉉; 뉉; 뉉; 뉉; 뉉; ) HANGUL SYLLABLE NWELT +B24A;B24A;1102 1170 11B5;B24A;1102 1170 11B5; # (뉊; 뉊; 뉊; 뉊; 뉊; ) HANGUL SYLLABLE NWELP +B24B;B24B;1102 1170 11B6;B24B;1102 1170 11B6; # (뉋; 뉋; 뉋; 뉋; 뉋; ) HANGUL SYLLABLE NWELH +B24C;B24C;1102 1170 11B7;B24C;1102 1170 11B7; # (뉌; 뉌; 뉌; 뉌; 뉌; ) HANGUL SYLLABLE NWEM +B24D;B24D;1102 1170 11B8;B24D;1102 1170 11B8; # (뉍; 뉍; 뉍; 뉍; 뉍; ) HANGUL SYLLABLE NWEB +B24E;B24E;1102 1170 11B9;B24E;1102 1170 11B9; # (뉎; 뉎; 뉎; 뉎; 뉎; ) HANGUL SYLLABLE NWEBS +B24F;B24F;1102 1170 11BA;B24F;1102 1170 11BA; # (뉏; 뉏; 뉏; 뉏; 뉏; ) HANGUL SYLLABLE NWES +B250;B250;1102 1170 11BB;B250;1102 1170 11BB; # (뉐; 뉐; 뉐; 뉐; 뉐; ) HANGUL SYLLABLE NWESS +B251;B251;1102 1170 11BC;B251;1102 1170 11BC; # (뉑; 뉑; 뉑; 뉑; 뉑; ) HANGUL SYLLABLE NWENG +B252;B252;1102 1170 11BD;B252;1102 1170 11BD; # (뉒; 뉒; 뉒; 뉒; 뉒; ) HANGUL SYLLABLE NWEJ +B253;B253;1102 1170 11BE;B253;1102 1170 11BE; # (뉓; 뉓; 뉓; 뉓; 뉓; ) HANGUL SYLLABLE NWEC +B254;B254;1102 1170 11BF;B254;1102 1170 11BF; # (뉔; 뉔; 뉔; 뉔; 뉔; ) HANGUL SYLLABLE NWEK +B255;B255;1102 1170 11C0;B255;1102 1170 11C0; # (뉕; 뉕; 뉕; 뉕; 뉕; ) HANGUL SYLLABLE NWET +B256;B256;1102 1170 11C1;B256;1102 1170 11C1; # (뉖; 뉖; 뉖; 뉖; 뉖; ) HANGUL SYLLABLE NWEP +B257;B257;1102 1170 11C2;B257;1102 1170 11C2; # (뉗; 뉗; 뉗; 뉗; 뉗; ) HANGUL SYLLABLE NWEH +B258;B258;1102 1171;B258;1102 1171; # (뉘; 뉘; 뉘; 뉘; 뉘; ) HANGUL SYLLABLE NWI +B259;B259;1102 1171 11A8;B259;1102 1171 11A8; # (뉙; 뉙; 뉙; 뉙; 뉙; ) HANGUL SYLLABLE NWIG +B25A;B25A;1102 1171 11A9;B25A;1102 1171 11A9; # (뉚; 뉚; 뉚; 뉚; 뉚; ) HANGUL SYLLABLE NWIGG +B25B;B25B;1102 1171 11AA;B25B;1102 1171 11AA; # (뉛; 뉛; 뉛; 뉛; 뉛; ) HANGUL SYLLABLE NWIGS +B25C;B25C;1102 1171 11AB;B25C;1102 1171 11AB; # (뉜; 뉜; 뉜; 뉜; 뉜; ) HANGUL SYLLABLE NWIN +B25D;B25D;1102 1171 11AC;B25D;1102 1171 11AC; # (뉝; 뉝; 뉝; 뉝; 뉝; ) HANGUL SYLLABLE NWINJ +B25E;B25E;1102 1171 11AD;B25E;1102 1171 11AD; # (뉞; 뉞; 뉞; 뉞; 뉞; ) HANGUL SYLLABLE NWINH +B25F;B25F;1102 1171 11AE;B25F;1102 1171 11AE; # (뉟; 뉟; 뉟; 뉟; 뉟; ) HANGUL SYLLABLE NWID +B260;B260;1102 1171 11AF;B260;1102 1171 11AF; # (뉠; 뉠; 뉠; 뉠; 뉠; ) HANGUL SYLLABLE NWIL +B261;B261;1102 1171 11B0;B261;1102 1171 11B0; # (뉡; 뉡; 뉡; 뉡; 뉡; ) HANGUL SYLLABLE NWILG +B262;B262;1102 1171 11B1;B262;1102 1171 11B1; # (뉢; 뉢; 뉢; 뉢; 뉢; ) HANGUL SYLLABLE NWILM +B263;B263;1102 1171 11B2;B263;1102 1171 11B2; # (뉣; 뉣; 뉣; 뉣; 뉣; ) HANGUL SYLLABLE NWILB +B264;B264;1102 1171 11B3;B264;1102 1171 11B3; # (뉤; 뉤; 뉤; 뉤; 뉤; ) HANGUL SYLLABLE NWILS +B265;B265;1102 1171 11B4;B265;1102 1171 11B4; # (뉥; 뉥; 뉥; 뉥; 뉥; ) HANGUL SYLLABLE NWILT +B266;B266;1102 1171 11B5;B266;1102 1171 11B5; # (뉦; 뉦; 뉦; 뉦; 뉦; ) HANGUL SYLLABLE NWILP +B267;B267;1102 1171 11B6;B267;1102 1171 11B6; # (뉧; 뉧; 뉧; 뉧; 뉧; ) HANGUL SYLLABLE NWILH +B268;B268;1102 1171 11B7;B268;1102 1171 11B7; # (뉨; 뉨; 뉨; 뉨; 뉨; ) HANGUL SYLLABLE NWIM +B269;B269;1102 1171 11B8;B269;1102 1171 11B8; # (뉩; 뉩; 뉩; 뉩; 뉩; ) HANGUL SYLLABLE NWIB +B26A;B26A;1102 1171 11B9;B26A;1102 1171 11B9; # (뉪; 뉪; 뉪; 뉪; 뉪; ) HANGUL SYLLABLE NWIBS +B26B;B26B;1102 1171 11BA;B26B;1102 1171 11BA; # (뉫; 뉫; 뉫; 뉫; 뉫; ) HANGUL SYLLABLE NWIS +B26C;B26C;1102 1171 11BB;B26C;1102 1171 11BB; # (뉬; 뉬; 뉬; 뉬; 뉬; ) HANGUL SYLLABLE NWISS +B26D;B26D;1102 1171 11BC;B26D;1102 1171 11BC; # (뉭; 뉭; 뉭; 뉭; 뉭; ) HANGUL SYLLABLE NWING +B26E;B26E;1102 1171 11BD;B26E;1102 1171 11BD; # (뉮; 뉮; 뉮; 뉮; 뉮; ) HANGUL SYLLABLE NWIJ +B26F;B26F;1102 1171 11BE;B26F;1102 1171 11BE; # (뉯; 뉯; 뉯; 뉯; 뉯; ) HANGUL SYLLABLE NWIC +B270;B270;1102 1171 11BF;B270;1102 1171 11BF; # (뉰; 뉰; 뉰; 뉰; 뉰; ) HANGUL SYLLABLE NWIK +B271;B271;1102 1171 11C0;B271;1102 1171 11C0; # (뉱; 뉱; 뉱; 뉱; 뉱; ) HANGUL SYLLABLE NWIT +B272;B272;1102 1171 11C1;B272;1102 1171 11C1; # (뉲; 뉲; 뉲; 뉲; 뉲; ) HANGUL SYLLABLE NWIP +B273;B273;1102 1171 11C2;B273;1102 1171 11C2; # (뉳; 뉳; 뉳; 뉳; 뉳; ) HANGUL SYLLABLE NWIH +B274;B274;1102 1172;B274;1102 1172; # (뉴; 뉴; 뉴; 뉴; 뉴; ) HANGUL SYLLABLE NYU +B275;B275;1102 1172 11A8;B275;1102 1172 11A8; # (뉵; 뉵; 뉵; 뉵; 뉵; ) HANGUL SYLLABLE NYUG +B276;B276;1102 1172 11A9;B276;1102 1172 11A9; # (뉶; 뉶; 뉶; 뉶; 뉶; ) HANGUL SYLLABLE NYUGG +B277;B277;1102 1172 11AA;B277;1102 1172 11AA; # (뉷; 뉷; 뉷; 뉷; 뉷; ) HANGUL SYLLABLE NYUGS +B278;B278;1102 1172 11AB;B278;1102 1172 11AB; # (뉸; 뉸; 뉸; 뉸; 뉸; ) HANGUL SYLLABLE NYUN +B279;B279;1102 1172 11AC;B279;1102 1172 11AC; # (뉹; 뉹; 뉹; 뉹; 뉹; ) HANGUL SYLLABLE NYUNJ +B27A;B27A;1102 1172 11AD;B27A;1102 1172 11AD; # (뉺; 뉺; 뉺; 뉺; 뉺; ) HANGUL SYLLABLE NYUNH +B27B;B27B;1102 1172 11AE;B27B;1102 1172 11AE; # (뉻; 뉻; 뉻; 뉻; 뉻; ) HANGUL SYLLABLE NYUD +B27C;B27C;1102 1172 11AF;B27C;1102 1172 11AF; # (뉼; 뉼; 뉼; 뉼; 뉼; ) HANGUL SYLLABLE NYUL +B27D;B27D;1102 1172 11B0;B27D;1102 1172 11B0; # (뉽; 뉽; 뉽; 뉽; 뉽; ) HANGUL SYLLABLE NYULG +B27E;B27E;1102 1172 11B1;B27E;1102 1172 11B1; # (뉾; 뉾; 뉾; 뉾; 뉾; ) HANGUL SYLLABLE NYULM +B27F;B27F;1102 1172 11B2;B27F;1102 1172 11B2; # (뉿; 뉿; 뉿; 뉿; 뉿; ) HANGUL SYLLABLE NYULB +B280;B280;1102 1172 11B3;B280;1102 1172 11B3; # (늀; 늀; 늀; 늀; 늀; ) HANGUL SYLLABLE NYULS +B281;B281;1102 1172 11B4;B281;1102 1172 11B4; # (늁; 늁; 늁; 늁; 늁; ) HANGUL SYLLABLE NYULT +B282;B282;1102 1172 11B5;B282;1102 1172 11B5; # (늂; 늂; 늂; 늂; 늂; ) HANGUL SYLLABLE NYULP +B283;B283;1102 1172 11B6;B283;1102 1172 11B6; # (늃; 늃; 늃; 늃; 늃; ) HANGUL SYLLABLE NYULH +B284;B284;1102 1172 11B7;B284;1102 1172 11B7; # (늄; 늄; 늄; 늄; 늄; ) HANGUL SYLLABLE NYUM +B285;B285;1102 1172 11B8;B285;1102 1172 11B8; # (늅; 늅; 늅; 늅; 늅; ) HANGUL SYLLABLE NYUB +B286;B286;1102 1172 11B9;B286;1102 1172 11B9; # (늆; 늆; 늆; 늆; 늆; ) HANGUL SYLLABLE NYUBS +B287;B287;1102 1172 11BA;B287;1102 1172 11BA; # (늇; 늇; 늇; 늇; 늇; ) HANGUL SYLLABLE NYUS +B288;B288;1102 1172 11BB;B288;1102 1172 11BB; # (늈; 늈; 늈; 늈; 늈; ) HANGUL SYLLABLE NYUSS +B289;B289;1102 1172 11BC;B289;1102 1172 11BC; # (늉; 늉; 늉; 늉; 늉; ) HANGUL SYLLABLE NYUNG +B28A;B28A;1102 1172 11BD;B28A;1102 1172 11BD; # (늊; 늊; 늊; 늊; 늊; ) HANGUL SYLLABLE NYUJ +B28B;B28B;1102 1172 11BE;B28B;1102 1172 11BE; # (늋; 늋; 늋; 늋; 늋; ) HANGUL SYLLABLE NYUC +B28C;B28C;1102 1172 11BF;B28C;1102 1172 11BF; # (늌; 늌; 늌; 늌; 늌; ) HANGUL SYLLABLE NYUK +B28D;B28D;1102 1172 11C0;B28D;1102 1172 11C0; # (늍; 늍; 늍; 늍; 늍; ) HANGUL SYLLABLE NYUT +B28E;B28E;1102 1172 11C1;B28E;1102 1172 11C1; # (늎; 늎; 늎; 늎; 늎; ) HANGUL SYLLABLE NYUP +B28F;B28F;1102 1172 11C2;B28F;1102 1172 11C2; # (늏; 늏; 늏; 늏; 늏; ) HANGUL SYLLABLE NYUH +B290;B290;1102 1173;B290;1102 1173; # (느; 느; 느; 느; 느; ) HANGUL SYLLABLE NEU +B291;B291;1102 1173 11A8;B291;1102 1173 11A8; # (늑; 늑; 늑; 늑; 늑; ) HANGUL SYLLABLE NEUG +B292;B292;1102 1173 11A9;B292;1102 1173 11A9; # (늒; 늒; 늒; 늒; 늒; ) HANGUL SYLLABLE NEUGG +B293;B293;1102 1173 11AA;B293;1102 1173 11AA; # (늓; 늓; 늓; 늓; 늓; ) HANGUL SYLLABLE NEUGS +B294;B294;1102 1173 11AB;B294;1102 1173 11AB; # (는; 는; 는; 는; 는; ) HANGUL SYLLABLE NEUN +B295;B295;1102 1173 11AC;B295;1102 1173 11AC; # (늕; 늕; 늕; 늕; 늕; ) HANGUL SYLLABLE NEUNJ +B296;B296;1102 1173 11AD;B296;1102 1173 11AD; # (늖; 늖; 늖; 늖; 늖; ) HANGUL SYLLABLE NEUNH +B297;B297;1102 1173 11AE;B297;1102 1173 11AE; # (늗; 늗; 늗; 늗; 늗; ) HANGUL SYLLABLE NEUD +B298;B298;1102 1173 11AF;B298;1102 1173 11AF; # (늘; 늘; 늘; 늘; 늘; ) HANGUL SYLLABLE NEUL +B299;B299;1102 1173 11B0;B299;1102 1173 11B0; # (늙; 늙; 늙; 늙; 늙; ) HANGUL SYLLABLE NEULG +B29A;B29A;1102 1173 11B1;B29A;1102 1173 11B1; # (늚; 늚; 늚; 늚; 늚; ) HANGUL SYLLABLE NEULM +B29B;B29B;1102 1173 11B2;B29B;1102 1173 11B2; # (늛; 늛; 늛; 늛; 늛; ) HANGUL SYLLABLE NEULB +B29C;B29C;1102 1173 11B3;B29C;1102 1173 11B3; # (늜; 늜; 늜; 늜; 늜; ) HANGUL SYLLABLE NEULS +B29D;B29D;1102 1173 11B4;B29D;1102 1173 11B4; # (늝; 늝; 늝; 늝; 늝; ) HANGUL SYLLABLE NEULT +B29E;B29E;1102 1173 11B5;B29E;1102 1173 11B5; # (늞; 늞; 늞; 늞; 늞; ) HANGUL SYLLABLE NEULP +B29F;B29F;1102 1173 11B6;B29F;1102 1173 11B6; # (늟; 늟; 늟; 늟; 늟; ) HANGUL SYLLABLE NEULH +B2A0;B2A0;1102 1173 11B7;B2A0;1102 1173 11B7; # (늠; 늠; 늠; 늠; 늠; ) HANGUL SYLLABLE NEUM +B2A1;B2A1;1102 1173 11B8;B2A1;1102 1173 11B8; # (늡; 늡; 늡; 늡; 늡; ) HANGUL SYLLABLE NEUB +B2A2;B2A2;1102 1173 11B9;B2A2;1102 1173 11B9; # (늢; 늢; 늢; 늢; 늢; ) HANGUL SYLLABLE NEUBS +B2A3;B2A3;1102 1173 11BA;B2A3;1102 1173 11BA; # (늣; 늣; 늣; 늣; 늣; ) HANGUL SYLLABLE NEUS +B2A4;B2A4;1102 1173 11BB;B2A4;1102 1173 11BB; # (늤; 늤; 늤; 늤; 늤; ) HANGUL SYLLABLE NEUSS +B2A5;B2A5;1102 1173 11BC;B2A5;1102 1173 11BC; # (능; 능; 능; 능; 능; ) HANGUL SYLLABLE NEUNG +B2A6;B2A6;1102 1173 11BD;B2A6;1102 1173 11BD; # (늦; 늦; 늦; 늦; 늦; ) HANGUL SYLLABLE NEUJ +B2A7;B2A7;1102 1173 11BE;B2A7;1102 1173 11BE; # (늧; 늧; 늧; 늧; 늧; ) HANGUL SYLLABLE NEUC +B2A8;B2A8;1102 1173 11BF;B2A8;1102 1173 11BF; # (늨; 늨; 늨; 늨; 늨; ) HANGUL SYLLABLE NEUK +B2A9;B2A9;1102 1173 11C0;B2A9;1102 1173 11C0; # (늩; 늩; 늩; 늩; 늩; ) HANGUL SYLLABLE NEUT +B2AA;B2AA;1102 1173 11C1;B2AA;1102 1173 11C1; # (늪; 늪; 늪; 늪; 늪; ) HANGUL SYLLABLE NEUP +B2AB;B2AB;1102 1173 11C2;B2AB;1102 1173 11C2; # (늫; 늫; 늫; 늫; 늫; ) HANGUL SYLLABLE NEUH +B2AC;B2AC;1102 1174;B2AC;1102 1174; # (늬; 늬; 늬; 늬; 늬; ) HANGUL SYLLABLE NYI +B2AD;B2AD;1102 1174 11A8;B2AD;1102 1174 11A8; # (늭; 늭; 늭; 늭; 늭; ) HANGUL SYLLABLE NYIG +B2AE;B2AE;1102 1174 11A9;B2AE;1102 1174 11A9; # (늮; 늮; 늮; 늮; 늮; ) HANGUL SYLLABLE NYIGG +B2AF;B2AF;1102 1174 11AA;B2AF;1102 1174 11AA; # (늯; 늯; 늯; 늯; 늯; ) HANGUL SYLLABLE NYIGS +B2B0;B2B0;1102 1174 11AB;B2B0;1102 1174 11AB; # (늰; 늰; 늰; 늰; 늰; ) HANGUL SYLLABLE NYIN +B2B1;B2B1;1102 1174 11AC;B2B1;1102 1174 11AC; # (늱; 늱; 늱; 늱; 늱; ) HANGUL SYLLABLE NYINJ +B2B2;B2B2;1102 1174 11AD;B2B2;1102 1174 11AD; # (늲; 늲; 늲; 늲; 늲; ) HANGUL SYLLABLE NYINH +B2B3;B2B3;1102 1174 11AE;B2B3;1102 1174 11AE; # (늳; 늳; 늳; 늳; 늳; ) HANGUL SYLLABLE NYID +B2B4;B2B4;1102 1174 11AF;B2B4;1102 1174 11AF; # (늴; 늴; 늴; 늴; 늴; ) HANGUL SYLLABLE NYIL +B2B5;B2B5;1102 1174 11B0;B2B5;1102 1174 11B0; # (늵; 늵; 늵; 늵; 늵; ) HANGUL SYLLABLE NYILG +B2B6;B2B6;1102 1174 11B1;B2B6;1102 1174 11B1; # (늶; 늶; 늶; 늶; 늶; ) HANGUL SYLLABLE NYILM +B2B7;B2B7;1102 1174 11B2;B2B7;1102 1174 11B2; # (늷; 늷; 늷; 늷; 늷; ) HANGUL SYLLABLE NYILB +B2B8;B2B8;1102 1174 11B3;B2B8;1102 1174 11B3; # (늸; 늸; 늸; 늸; 늸; ) HANGUL SYLLABLE NYILS +B2B9;B2B9;1102 1174 11B4;B2B9;1102 1174 11B4; # (늹; 늹; 늹; 늹; 늹; ) HANGUL SYLLABLE NYILT +B2BA;B2BA;1102 1174 11B5;B2BA;1102 1174 11B5; # (늺; 늺; 늺; 늺; 늺; ) HANGUL SYLLABLE NYILP +B2BB;B2BB;1102 1174 11B6;B2BB;1102 1174 11B6; # (늻; 늻; 늻; 늻; 늻; ) HANGUL SYLLABLE NYILH +B2BC;B2BC;1102 1174 11B7;B2BC;1102 1174 11B7; # (늼; 늼; 늼; 늼; 늼; ) HANGUL SYLLABLE NYIM +B2BD;B2BD;1102 1174 11B8;B2BD;1102 1174 11B8; # (늽; 늽; 늽; 늽; 늽; ) HANGUL SYLLABLE NYIB +B2BE;B2BE;1102 1174 11B9;B2BE;1102 1174 11B9; # (늾; 늾; 늾; 늾; 늾; ) HANGUL SYLLABLE NYIBS +B2BF;B2BF;1102 1174 11BA;B2BF;1102 1174 11BA; # (늿; 늿; 늿; 늿; 늿; ) HANGUL SYLLABLE NYIS +B2C0;B2C0;1102 1174 11BB;B2C0;1102 1174 11BB; # (닀; 닀; 닀; 닀; 닀; ) HANGUL SYLLABLE NYISS +B2C1;B2C1;1102 1174 11BC;B2C1;1102 1174 11BC; # (닁; 닁; 닁; 닁; 닁; ) HANGUL SYLLABLE NYING +B2C2;B2C2;1102 1174 11BD;B2C2;1102 1174 11BD; # (닂; 닂; 닂; 닂; 닂; ) HANGUL SYLLABLE NYIJ +B2C3;B2C3;1102 1174 11BE;B2C3;1102 1174 11BE; # (닃; 닃; 닃; 닃; 닃; ) HANGUL SYLLABLE NYIC +B2C4;B2C4;1102 1174 11BF;B2C4;1102 1174 11BF; # (닄; 닄; 닄; 닄; 닄; ) HANGUL SYLLABLE NYIK +B2C5;B2C5;1102 1174 11C0;B2C5;1102 1174 11C0; # (닅; 닅; 닅; 닅; 닅; ) HANGUL SYLLABLE NYIT +B2C6;B2C6;1102 1174 11C1;B2C6;1102 1174 11C1; # (닆; 닆; 닆; 닆; 닆; ) HANGUL SYLLABLE NYIP +B2C7;B2C7;1102 1174 11C2;B2C7;1102 1174 11C2; # (닇; 닇; 닇; 닇; 닇; ) HANGUL SYLLABLE NYIH +B2C8;B2C8;1102 1175;B2C8;1102 1175; # (니; 니; 니; 니; 니; ) HANGUL SYLLABLE NI +B2C9;B2C9;1102 1175 11A8;B2C9;1102 1175 11A8; # (닉; 닉; 닉; 닉; 닉; ) HANGUL SYLLABLE NIG +B2CA;B2CA;1102 1175 11A9;B2CA;1102 1175 11A9; # (닊; 닊; 닊; 닊; 닊; ) HANGUL SYLLABLE NIGG +B2CB;B2CB;1102 1175 11AA;B2CB;1102 1175 11AA; # (닋; 닋; 닋; 닋; 닋; ) HANGUL SYLLABLE NIGS +B2CC;B2CC;1102 1175 11AB;B2CC;1102 1175 11AB; # (닌; 닌; 닌; 닌; 닌; ) HANGUL SYLLABLE NIN +B2CD;B2CD;1102 1175 11AC;B2CD;1102 1175 11AC; # (닍; 닍; 닍; 닍; 닍; ) HANGUL SYLLABLE NINJ +B2CE;B2CE;1102 1175 11AD;B2CE;1102 1175 11AD; # (닎; 닎; 닎; 닎; 닎; ) HANGUL SYLLABLE NINH +B2CF;B2CF;1102 1175 11AE;B2CF;1102 1175 11AE; # (닏; 닏; 닏; 닏; 닏; ) HANGUL SYLLABLE NID +B2D0;B2D0;1102 1175 11AF;B2D0;1102 1175 11AF; # (닐; 닐; 닐; 닐; 닐; ) HANGUL SYLLABLE NIL +B2D1;B2D1;1102 1175 11B0;B2D1;1102 1175 11B0; # (닑; 닑; 닑; 닑; 닑; ) HANGUL SYLLABLE NILG +B2D2;B2D2;1102 1175 11B1;B2D2;1102 1175 11B1; # (닒; 닒; 닒; 닒; 닒; ) HANGUL SYLLABLE NILM +B2D3;B2D3;1102 1175 11B2;B2D3;1102 1175 11B2; # (닓; 닓; 닓; 닓; 닓; ) HANGUL SYLLABLE NILB +B2D4;B2D4;1102 1175 11B3;B2D4;1102 1175 11B3; # (닔; 닔; 닔; 닔; 닔; ) HANGUL SYLLABLE NILS +B2D5;B2D5;1102 1175 11B4;B2D5;1102 1175 11B4; # (닕; 닕; 닕; 닕; 닕; ) HANGUL SYLLABLE NILT +B2D6;B2D6;1102 1175 11B5;B2D6;1102 1175 11B5; # (닖; 닖; 닖; 닖; 닖; ) HANGUL SYLLABLE NILP +B2D7;B2D7;1102 1175 11B6;B2D7;1102 1175 11B6; # (닗; 닗; 닗; 닗; 닗; ) HANGUL SYLLABLE NILH +B2D8;B2D8;1102 1175 11B7;B2D8;1102 1175 11B7; # (님; 님; 님; 님; 님; ) HANGUL SYLLABLE NIM +B2D9;B2D9;1102 1175 11B8;B2D9;1102 1175 11B8; # (닙; 닙; 닙; 닙; 닙; ) HANGUL SYLLABLE NIB +B2DA;B2DA;1102 1175 11B9;B2DA;1102 1175 11B9; # (닚; 닚; 닚; 닚; 닚; ) HANGUL SYLLABLE NIBS +B2DB;B2DB;1102 1175 11BA;B2DB;1102 1175 11BA; # (닛; 닛; 닛; 닛; 닛; ) HANGUL SYLLABLE NIS +B2DC;B2DC;1102 1175 11BB;B2DC;1102 1175 11BB; # (닜; 닜; 닜; 닜; 닜; ) HANGUL SYLLABLE NISS +B2DD;B2DD;1102 1175 11BC;B2DD;1102 1175 11BC; # (닝; 닝; 닝; 닝; 닝; ) HANGUL SYLLABLE NING +B2DE;B2DE;1102 1175 11BD;B2DE;1102 1175 11BD; # (닞; 닞; 닞; 닞; 닞; ) HANGUL SYLLABLE NIJ +B2DF;B2DF;1102 1175 11BE;B2DF;1102 1175 11BE; # (닟; 닟; 닟; 닟; 닟; ) HANGUL SYLLABLE NIC +B2E0;B2E0;1102 1175 11BF;B2E0;1102 1175 11BF; # (닠; 닠; 닠; 닠; 닠; ) HANGUL SYLLABLE NIK +B2E1;B2E1;1102 1175 11C0;B2E1;1102 1175 11C0; # (닡; 닡; 닡; 닡; 닡; ) HANGUL SYLLABLE NIT +B2E2;B2E2;1102 1175 11C1;B2E2;1102 1175 11C1; # (닢; 닢; 닢; 닢; 닢; ) HANGUL SYLLABLE NIP +B2E3;B2E3;1102 1175 11C2;B2E3;1102 1175 11C2; # (닣; 닣; 닣; 닣; 닣; ) HANGUL SYLLABLE NIH +B2E4;B2E4;1103 1161;B2E4;1103 1161; # (다; 다; 다; 다; 다; ) HANGUL SYLLABLE DA +B2E5;B2E5;1103 1161 11A8;B2E5;1103 1161 11A8; # (닥; 닥; 닥; 닥; 닥; ) HANGUL SYLLABLE DAG +B2E6;B2E6;1103 1161 11A9;B2E6;1103 1161 11A9; # (닦; 닦; 닦; 닦; 닦; ) HANGUL SYLLABLE DAGG +B2E7;B2E7;1103 1161 11AA;B2E7;1103 1161 11AA; # (닧; 닧; 닧; 닧; 닧; ) HANGUL SYLLABLE DAGS +B2E8;B2E8;1103 1161 11AB;B2E8;1103 1161 11AB; # (단; 단; 단; 단; 단; ) HANGUL SYLLABLE DAN +B2E9;B2E9;1103 1161 11AC;B2E9;1103 1161 11AC; # (닩; 닩; 닩; 닩; 닩; ) HANGUL SYLLABLE DANJ +B2EA;B2EA;1103 1161 11AD;B2EA;1103 1161 11AD; # (닪; 닪; 닪; 닪; 닪; ) HANGUL SYLLABLE DANH +B2EB;B2EB;1103 1161 11AE;B2EB;1103 1161 11AE; # (닫; 닫; 닫; 닫; 닫; ) HANGUL SYLLABLE DAD +B2EC;B2EC;1103 1161 11AF;B2EC;1103 1161 11AF; # (달; 달; 달; 달; 달; ) HANGUL SYLLABLE DAL +B2ED;B2ED;1103 1161 11B0;B2ED;1103 1161 11B0; # (닭; 닭; 닭; 닭; 닭; ) HANGUL SYLLABLE DALG +B2EE;B2EE;1103 1161 11B1;B2EE;1103 1161 11B1; # (닮; 닮; 닮; 닮; 닮; ) HANGUL SYLLABLE DALM +B2EF;B2EF;1103 1161 11B2;B2EF;1103 1161 11B2; # (닯; 닯; 닯; 닯; 닯; ) HANGUL SYLLABLE DALB +B2F0;B2F0;1103 1161 11B3;B2F0;1103 1161 11B3; # (닰; 닰; 닰; 닰; 닰; ) HANGUL SYLLABLE DALS +B2F1;B2F1;1103 1161 11B4;B2F1;1103 1161 11B4; # (닱; 닱; 닱; 닱; 닱; ) HANGUL SYLLABLE DALT +B2F2;B2F2;1103 1161 11B5;B2F2;1103 1161 11B5; # (닲; 닲; 닲; 닲; 닲; ) HANGUL SYLLABLE DALP +B2F3;B2F3;1103 1161 11B6;B2F3;1103 1161 11B6; # (닳; 닳; 닳; 닳; 닳; ) HANGUL SYLLABLE DALH +B2F4;B2F4;1103 1161 11B7;B2F4;1103 1161 11B7; # (담; 담; 담; 담; 담; ) HANGUL SYLLABLE DAM +B2F5;B2F5;1103 1161 11B8;B2F5;1103 1161 11B8; # (답; 답; 답; 답; 답; ) HANGUL SYLLABLE DAB +B2F6;B2F6;1103 1161 11B9;B2F6;1103 1161 11B9; # (닶; 닶; 닶; 닶; 닶; ) HANGUL SYLLABLE DABS +B2F7;B2F7;1103 1161 11BA;B2F7;1103 1161 11BA; # (닷; 닷; 닷; 닷; 닷; ) HANGUL SYLLABLE DAS +B2F8;B2F8;1103 1161 11BB;B2F8;1103 1161 11BB; # (닸; 닸; 닸; 닸; 닸; ) HANGUL SYLLABLE DASS +B2F9;B2F9;1103 1161 11BC;B2F9;1103 1161 11BC; # (당; 당; 당; 당; 당; ) HANGUL SYLLABLE DANG +B2FA;B2FA;1103 1161 11BD;B2FA;1103 1161 11BD; # (닺; 닺; 닺; 닺; 닺; ) HANGUL SYLLABLE DAJ +B2FB;B2FB;1103 1161 11BE;B2FB;1103 1161 11BE; # (닻; 닻; 닻; 닻; 닻; ) HANGUL SYLLABLE DAC +B2FC;B2FC;1103 1161 11BF;B2FC;1103 1161 11BF; # (닼; 닼; 닼; 닼; 닼; ) HANGUL SYLLABLE DAK +B2FD;B2FD;1103 1161 11C0;B2FD;1103 1161 11C0; # (닽; 닽; 닽; 닽; 닽; ) HANGUL SYLLABLE DAT +B2FE;B2FE;1103 1161 11C1;B2FE;1103 1161 11C1; # (닾; 닾; 닾; 닾; 닾; ) HANGUL SYLLABLE DAP +B2FF;B2FF;1103 1161 11C2;B2FF;1103 1161 11C2; # (닿; 닿; 닿; 닿; 닿; ) HANGUL SYLLABLE DAH +B300;B300;1103 1162;B300;1103 1162; # (대; 대; 대; 대; 대; ) HANGUL SYLLABLE DAE +B301;B301;1103 1162 11A8;B301;1103 1162 11A8; # (댁; 댁; 댁; 댁; 댁; ) HANGUL SYLLABLE DAEG +B302;B302;1103 1162 11A9;B302;1103 1162 11A9; # (댂; 댂; 댂; 댂; 댂; ) HANGUL SYLLABLE DAEGG +B303;B303;1103 1162 11AA;B303;1103 1162 11AA; # (댃; 댃; 댃; 댃; 댃; ) HANGUL SYLLABLE DAEGS +B304;B304;1103 1162 11AB;B304;1103 1162 11AB; # (댄; 댄; 댄; 댄; 댄; ) HANGUL SYLLABLE DAEN +B305;B305;1103 1162 11AC;B305;1103 1162 11AC; # (댅; 댅; 댅; 댅; 댅; ) HANGUL SYLLABLE DAENJ +B306;B306;1103 1162 11AD;B306;1103 1162 11AD; # (댆; 댆; 댆; 댆; 댆; ) HANGUL SYLLABLE DAENH +B307;B307;1103 1162 11AE;B307;1103 1162 11AE; # (댇; 댇; 댇; 댇; 댇; ) HANGUL SYLLABLE DAED +B308;B308;1103 1162 11AF;B308;1103 1162 11AF; # (댈; 댈; 댈; 댈; 댈; ) HANGUL SYLLABLE DAEL +B309;B309;1103 1162 11B0;B309;1103 1162 11B0; # (댉; 댉; 댉; 댉; 댉; ) HANGUL SYLLABLE DAELG +B30A;B30A;1103 1162 11B1;B30A;1103 1162 11B1; # (댊; 댊; 댊; 댊; 댊; ) HANGUL SYLLABLE DAELM +B30B;B30B;1103 1162 11B2;B30B;1103 1162 11B2; # (댋; 댋; 댋; 댋; 댋; ) HANGUL SYLLABLE DAELB +B30C;B30C;1103 1162 11B3;B30C;1103 1162 11B3; # (댌; 댌; 댌; 댌; 댌; ) HANGUL SYLLABLE DAELS +B30D;B30D;1103 1162 11B4;B30D;1103 1162 11B4; # (댍; 댍; 댍; 댍; 댍; ) HANGUL SYLLABLE DAELT +B30E;B30E;1103 1162 11B5;B30E;1103 1162 11B5; # (댎; 댎; 댎; 댎; 댎; ) HANGUL SYLLABLE DAELP +B30F;B30F;1103 1162 11B6;B30F;1103 1162 11B6; # (댏; 댏; 댏; 댏; 댏; ) HANGUL SYLLABLE DAELH +B310;B310;1103 1162 11B7;B310;1103 1162 11B7; # (댐; 댐; 댐; 댐; 댐; ) HANGUL SYLLABLE DAEM +B311;B311;1103 1162 11B8;B311;1103 1162 11B8; # (댑; 댑; 댑; 댑; 댑; ) HANGUL SYLLABLE DAEB +B312;B312;1103 1162 11B9;B312;1103 1162 11B9; # (댒; 댒; 댒; 댒; 댒; ) HANGUL SYLLABLE DAEBS +B313;B313;1103 1162 11BA;B313;1103 1162 11BA; # (댓; 댓; 댓; 댓; 댓; ) HANGUL SYLLABLE DAES +B314;B314;1103 1162 11BB;B314;1103 1162 11BB; # (댔; 댔; 댔; 댔; 댔; ) HANGUL SYLLABLE DAESS +B315;B315;1103 1162 11BC;B315;1103 1162 11BC; # (댕; 댕; 댕; 댕; 댕; ) HANGUL SYLLABLE DAENG +B316;B316;1103 1162 11BD;B316;1103 1162 11BD; # (댖; 댖; 댖; 댖; 댖; ) HANGUL SYLLABLE DAEJ +B317;B317;1103 1162 11BE;B317;1103 1162 11BE; # (댗; 댗; 댗; 댗; 댗; ) HANGUL SYLLABLE DAEC +B318;B318;1103 1162 11BF;B318;1103 1162 11BF; # (댘; 댘; 댘; 댘; 댘; ) HANGUL SYLLABLE DAEK +B319;B319;1103 1162 11C0;B319;1103 1162 11C0; # (댙; 댙; 댙; 댙; 댙; ) HANGUL SYLLABLE DAET +B31A;B31A;1103 1162 11C1;B31A;1103 1162 11C1; # (댚; 댚; 댚; 댚; 댚; ) HANGUL SYLLABLE DAEP +B31B;B31B;1103 1162 11C2;B31B;1103 1162 11C2; # (댛; 댛; 댛; 댛; 댛; ) HANGUL SYLLABLE DAEH +B31C;B31C;1103 1163;B31C;1103 1163; # (댜; 댜; 댜; 댜; 댜; ) HANGUL SYLLABLE DYA +B31D;B31D;1103 1163 11A8;B31D;1103 1163 11A8; # (댝; 댝; 댝; 댝; 댝; ) HANGUL SYLLABLE DYAG +B31E;B31E;1103 1163 11A9;B31E;1103 1163 11A9; # (댞; 댞; 댞; 댞; 댞; ) HANGUL SYLLABLE DYAGG +B31F;B31F;1103 1163 11AA;B31F;1103 1163 11AA; # (댟; 댟; 댟; 댟; 댟; ) HANGUL SYLLABLE DYAGS +B320;B320;1103 1163 11AB;B320;1103 1163 11AB; # (댠; 댠; 댠; 댠; 댠; ) HANGUL SYLLABLE DYAN +B321;B321;1103 1163 11AC;B321;1103 1163 11AC; # (댡; 댡; 댡; 댡; 댡; ) HANGUL SYLLABLE DYANJ +B322;B322;1103 1163 11AD;B322;1103 1163 11AD; # (댢; 댢; 댢; 댢; 댢; ) HANGUL SYLLABLE DYANH +B323;B323;1103 1163 11AE;B323;1103 1163 11AE; # (댣; 댣; 댣; 댣; 댣; ) HANGUL SYLLABLE DYAD +B324;B324;1103 1163 11AF;B324;1103 1163 11AF; # (댤; 댤; 댤; 댤; 댤; ) HANGUL SYLLABLE DYAL +B325;B325;1103 1163 11B0;B325;1103 1163 11B0; # (댥; 댥; 댥; 댥; 댥; ) HANGUL SYLLABLE DYALG +B326;B326;1103 1163 11B1;B326;1103 1163 11B1; # (댦; 댦; 댦; 댦; 댦; ) HANGUL SYLLABLE DYALM +B327;B327;1103 1163 11B2;B327;1103 1163 11B2; # (댧; 댧; 댧; 댧; 댧; ) HANGUL SYLLABLE DYALB +B328;B328;1103 1163 11B3;B328;1103 1163 11B3; # (댨; 댨; 댨; 댨; 댨; ) HANGUL SYLLABLE DYALS +B329;B329;1103 1163 11B4;B329;1103 1163 11B4; # (댩; 댩; 댩; 댩; 댩; ) HANGUL SYLLABLE DYALT +B32A;B32A;1103 1163 11B5;B32A;1103 1163 11B5; # (댪; 댪; 댪; 댪; 댪; ) HANGUL SYLLABLE DYALP +B32B;B32B;1103 1163 11B6;B32B;1103 1163 11B6; # (댫; 댫; 댫; 댫; 댫; ) HANGUL SYLLABLE DYALH +B32C;B32C;1103 1163 11B7;B32C;1103 1163 11B7; # (댬; 댬; 댬; 댬; 댬; ) HANGUL SYLLABLE DYAM +B32D;B32D;1103 1163 11B8;B32D;1103 1163 11B8; # (댭; 댭; 댭; 댭; 댭; ) HANGUL SYLLABLE DYAB +B32E;B32E;1103 1163 11B9;B32E;1103 1163 11B9; # (댮; 댮; 댮; 댮; 댮; ) HANGUL SYLLABLE DYABS +B32F;B32F;1103 1163 11BA;B32F;1103 1163 11BA; # (댯; 댯; 댯; 댯; 댯; ) HANGUL SYLLABLE DYAS +B330;B330;1103 1163 11BB;B330;1103 1163 11BB; # (댰; 댰; 댰; 댰; 댰; ) HANGUL SYLLABLE DYASS +B331;B331;1103 1163 11BC;B331;1103 1163 11BC; # (댱; 댱; 댱; 댱; 댱; ) HANGUL SYLLABLE DYANG +B332;B332;1103 1163 11BD;B332;1103 1163 11BD; # (댲; 댲; 댲; 댲; 댲; ) HANGUL SYLLABLE DYAJ +B333;B333;1103 1163 11BE;B333;1103 1163 11BE; # (댳; 댳; 댳; 댳; 댳; ) HANGUL SYLLABLE DYAC +B334;B334;1103 1163 11BF;B334;1103 1163 11BF; # (댴; 댴; 댴; 댴; 댴; ) HANGUL SYLLABLE DYAK +B335;B335;1103 1163 11C0;B335;1103 1163 11C0; # (댵; 댵; 댵; 댵; 댵; ) HANGUL SYLLABLE DYAT +B336;B336;1103 1163 11C1;B336;1103 1163 11C1; # (댶; 댶; 댶; 댶; 댶; ) HANGUL SYLLABLE DYAP +B337;B337;1103 1163 11C2;B337;1103 1163 11C2; # (댷; 댷; 댷; 댷; 댷; ) HANGUL SYLLABLE DYAH +B338;B338;1103 1164;B338;1103 1164; # (댸; 댸; 댸; 댸; 댸; ) HANGUL SYLLABLE DYAE +B339;B339;1103 1164 11A8;B339;1103 1164 11A8; # (댹; 댹; 댹; 댹; 댹; ) HANGUL SYLLABLE DYAEG +B33A;B33A;1103 1164 11A9;B33A;1103 1164 11A9; # (댺; 댺; 댺; 댺; 댺; ) HANGUL SYLLABLE DYAEGG +B33B;B33B;1103 1164 11AA;B33B;1103 1164 11AA; # (댻; 댻; 댻; 댻; 댻; ) HANGUL SYLLABLE DYAEGS +B33C;B33C;1103 1164 11AB;B33C;1103 1164 11AB; # (댼; 댼; 댼; 댼; 댼; ) HANGUL SYLLABLE DYAEN +B33D;B33D;1103 1164 11AC;B33D;1103 1164 11AC; # (댽; 댽; 댽; 댽; 댽; ) HANGUL SYLLABLE DYAENJ +B33E;B33E;1103 1164 11AD;B33E;1103 1164 11AD; # (댾; 댾; 댾; 댾; 댾; ) HANGUL SYLLABLE DYAENH +B33F;B33F;1103 1164 11AE;B33F;1103 1164 11AE; # (댿; 댿; 댿; 댿; 댿; ) HANGUL SYLLABLE DYAED +B340;B340;1103 1164 11AF;B340;1103 1164 11AF; # (덀; 덀; 덀; 덀; 덀; ) HANGUL SYLLABLE DYAEL +B341;B341;1103 1164 11B0;B341;1103 1164 11B0; # (덁; 덁; 덁; 덁; 덁; ) HANGUL SYLLABLE DYAELG +B342;B342;1103 1164 11B1;B342;1103 1164 11B1; # (덂; 덂; 덂; 덂; 덂; ) HANGUL SYLLABLE DYAELM +B343;B343;1103 1164 11B2;B343;1103 1164 11B2; # (덃; 덃; 덃; 덃; 덃; ) HANGUL SYLLABLE DYAELB +B344;B344;1103 1164 11B3;B344;1103 1164 11B3; # (덄; 덄; 덄; 덄; 덄; ) HANGUL SYLLABLE DYAELS +B345;B345;1103 1164 11B4;B345;1103 1164 11B4; # (덅; 덅; 덅; 덅; 덅; ) HANGUL SYLLABLE DYAELT +B346;B346;1103 1164 11B5;B346;1103 1164 11B5; # (덆; 덆; 덆; 덆; 덆; ) HANGUL SYLLABLE DYAELP +B347;B347;1103 1164 11B6;B347;1103 1164 11B6; # (덇; 덇; 덇; 덇; 덇; ) HANGUL SYLLABLE DYAELH +B348;B348;1103 1164 11B7;B348;1103 1164 11B7; # (덈; 덈; 덈; 덈; 덈; ) HANGUL SYLLABLE DYAEM +B349;B349;1103 1164 11B8;B349;1103 1164 11B8; # (덉; 덉; 덉; 덉; 덉; ) HANGUL SYLLABLE DYAEB +B34A;B34A;1103 1164 11B9;B34A;1103 1164 11B9; # (덊; 덊; 덊; 덊; 덊; ) HANGUL SYLLABLE DYAEBS +B34B;B34B;1103 1164 11BA;B34B;1103 1164 11BA; # (덋; 덋; 덋; 덋; 덋; ) HANGUL SYLLABLE DYAES +B34C;B34C;1103 1164 11BB;B34C;1103 1164 11BB; # (덌; 덌; 덌; 덌; 덌; ) HANGUL SYLLABLE DYAESS +B34D;B34D;1103 1164 11BC;B34D;1103 1164 11BC; # (덍; 덍; 덍; 덍; 덍; ) HANGUL SYLLABLE DYAENG +B34E;B34E;1103 1164 11BD;B34E;1103 1164 11BD; # (덎; 덎; 덎; 덎; 덎; ) HANGUL SYLLABLE DYAEJ +B34F;B34F;1103 1164 11BE;B34F;1103 1164 11BE; # (덏; 덏; 덏; 덏; 덏; ) HANGUL SYLLABLE DYAEC +B350;B350;1103 1164 11BF;B350;1103 1164 11BF; # (덐; 덐; 덐; 덐; 덐; ) HANGUL SYLLABLE DYAEK +B351;B351;1103 1164 11C0;B351;1103 1164 11C0; # (덑; 덑; 덑; 덑; 덑; ) HANGUL SYLLABLE DYAET +B352;B352;1103 1164 11C1;B352;1103 1164 11C1; # (덒; 덒; 덒; 덒; 덒; ) HANGUL SYLLABLE DYAEP +B353;B353;1103 1164 11C2;B353;1103 1164 11C2; # (덓; 덓; 덓; 덓; 덓; ) HANGUL SYLLABLE DYAEH +B354;B354;1103 1165;B354;1103 1165; # (더; 더; 더; 더; 더; ) HANGUL SYLLABLE DEO +B355;B355;1103 1165 11A8;B355;1103 1165 11A8; # (덕; 덕; 덕; 덕; 덕; ) HANGUL SYLLABLE DEOG +B356;B356;1103 1165 11A9;B356;1103 1165 11A9; # (덖; 덖; 덖; 덖; 덖; ) HANGUL SYLLABLE DEOGG +B357;B357;1103 1165 11AA;B357;1103 1165 11AA; # (덗; 덗; 덗; 덗; 덗; ) HANGUL SYLLABLE DEOGS +B358;B358;1103 1165 11AB;B358;1103 1165 11AB; # (던; 던; 던; 던; 던; ) HANGUL SYLLABLE DEON +B359;B359;1103 1165 11AC;B359;1103 1165 11AC; # (덙; 덙; 덙; 덙; 덙; ) HANGUL SYLLABLE DEONJ +B35A;B35A;1103 1165 11AD;B35A;1103 1165 11AD; # (덚; 덚; 덚; 덚; 덚; ) HANGUL SYLLABLE DEONH +B35B;B35B;1103 1165 11AE;B35B;1103 1165 11AE; # (덛; 덛; 덛; 덛; 덛; ) HANGUL SYLLABLE DEOD +B35C;B35C;1103 1165 11AF;B35C;1103 1165 11AF; # (덜; 덜; 덜; 덜; 덜; ) HANGUL SYLLABLE DEOL +B35D;B35D;1103 1165 11B0;B35D;1103 1165 11B0; # (덝; 덝; 덝; 덝; 덝; ) HANGUL SYLLABLE DEOLG +B35E;B35E;1103 1165 11B1;B35E;1103 1165 11B1; # (덞; 덞; 덞; 덞; 덞; ) HANGUL SYLLABLE DEOLM +B35F;B35F;1103 1165 11B2;B35F;1103 1165 11B2; # (덟; 덟; 덟; 덟; 덟; ) HANGUL SYLLABLE DEOLB +B360;B360;1103 1165 11B3;B360;1103 1165 11B3; # (덠; 덠; 덠; 덠; 덠; ) HANGUL SYLLABLE DEOLS +B361;B361;1103 1165 11B4;B361;1103 1165 11B4; # (덡; 덡; 덡; 덡; 덡; ) HANGUL SYLLABLE DEOLT +B362;B362;1103 1165 11B5;B362;1103 1165 11B5; # (덢; 덢; 덢; 덢; 덢; ) HANGUL SYLLABLE DEOLP +B363;B363;1103 1165 11B6;B363;1103 1165 11B6; # (덣; 덣; 덣; 덣; 덣; ) HANGUL SYLLABLE DEOLH +B364;B364;1103 1165 11B7;B364;1103 1165 11B7; # (덤; 덤; 덤; 덤; 덤; ) HANGUL SYLLABLE DEOM +B365;B365;1103 1165 11B8;B365;1103 1165 11B8; # (덥; 덥; 덥; 덥; 덥; ) HANGUL SYLLABLE DEOB +B366;B366;1103 1165 11B9;B366;1103 1165 11B9; # (덦; 덦; 덦; 덦; 덦; ) HANGUL SYLLABLE DEOBS +B367;B367;1103 1165 11BA;B367;1103 1165 11BA; # (덧; 덧; 덧; 덧; 덧; ) HANGUL SYLLABLE DEOS +B368;B368;1103 1165 11BB;B368;1103 1165 11BB; # (덨; 덨; 덨; 덨; 덨; ) HANGUL SYLLABLE DEOSS +B369;B369;1103 1165 11BC;B369;1103 1165 11BC; # (덩; 덩; 덩; 덩; 덩; ) HANGUL SYLLABLE DEONG +B36A;B36A;1103 1165 11BD;B36A;1103 1165 11BD; # (덪; 덪; 덪; 덪; 덪; ) HANGUL SYLLABLE DEOJ +B36B;B36B;1103 1165 11BE;B36B;1103 1165 11BE; # (덫; 덫; 덫; 덫; 덫; ) HANGUL SYLLABLE DEOC +B36C;B36C;1103 1165 11BF;B36C;1103 1165 11BF; # (덬; 덬; 덬; 덬; 덬; ) HANGUL SYLLABLE DEOK +B36D;B36D;1103 1165 11C0;B36D;1103 1165 11C0; # (덭; 덭; 덭; 덭; 덭; ) HANGUL SYLLABLE DEOT +B36E;B36E;1103 1165 11C1;B36E;1103 1165 11C1; # (덮; 덮; 덮; 덮; 덮; ) HANGUL SYLLABLE DEOP +B36F;B36F;1103 1165 11C2;B36F;1103 1165 11C2; # (덯; 덯; 덯; 덯; 덯; ) HANGUL SYLLABLE DEOH +B370;B370;1103 1166;B370;1103 1166; # (데; 데; 데; 데; 데; ) HANGUL SYLLABLE DE +B371;B371;1103 1166 11A8;B371;1103 1166 11A8; # (덱; 덱; 덱; 덱; 덱; ) HANGUL SYLLABLE DEG +B372;B372;1103 1166 11A9;B372;1103 1166 11A9; # (덲; 덲; 덲; 덲; 덲; ) HANGUL SYLLABLE DEGG +B373;B373;1103 1166 11AA;B373;1103 1166 11AA; # (덳; 덳; 덳; 덳; 덳; ) HANGUL SYLLABLE DEGS +B374;B374;1103 1166 11AB;B374;1103 1166 11AB; # (덴; 덴; 덴; 덴; 덴; ) HANGUL SYLLABLE DEN +B375;B375;1103 1166 11AC;B375;1103 1166 11AC; # (덵; 덵; 덵; 덵; 덵; ) HANGUL SYLLABLE DENJ +B376;B376;1103 1166 11AD;B376;1103 1166 11AD; # (덶; 덶; 덶; 덶; 덶; ) HANGUL SYLLABLE DENH +B377;B377;1103 1166 11AE;B377;1103 1166 11AE; # (덷; 덷; 덷; 덷; 덷; ) HANGUL SYLLABLE DED +B378;B378;1103 1166 11AF;B378;1103 1166 11AF; # (델; 델; 델; 델; 델; ) HANGUL SYLLABLE DEL +B379;B379;1103 1166 11B0;B379;1103 1166 11B0; # (덹; 덹; 덹; 덹; 덹; ) HANGUL SYLLABLE DELG +B37A;B37A;1103 1166 11B1;B37A;1103 1166 11B1; # (덺; 덺; 덺; 덺; 덺; ) HANGUL SYLLABLE DELM +B37B;B37B;1103 1166 11B2;B37B;1103 1166 11B2; # (덻; 덻; 덻; 덻; 덻; ) HANGUL SYLLABLE DELB +B37C;B37C;1103 1166 11B3;B37C;1103 1166 11B3; # (덼; 덼; 덼; 덼; 덼; ) HANGUL SYLLABLE DELS +B37D;B37D;1103 1166 11B4;B37D;1103 1166 11B4; # (덽; 덽; 덽; 덽; 덽; ) HANGUL SYLLABLE DELT +B37E;B37E;1103 1166 11B5;B37E;1103 1166 11B5; # (덾; 덾; 덾; 덾; 덾; ) HANGUL SYLLABLE DELP +B37F;B37F;1103 1166 11B6;B37F;1103 1166 11B6; # (덿; 덿; 덿; 덿; 덿; ) HANGUL SYLLABLE DELH +B380;B380;1103 1166 11B7;B380;1103 1166 11B7; # (뎀; 뎀; 뎀; 뎀; 뎀; ) HANGUL SYLLABLE DEM +B381;B381;1103 1166 11B8;B381;1103 1166 11B8; # (뎁; 뎁; 뎁; 뎁; 뎁; ) HANGUL SYLLABLE DEB +B382;B382;1103 1166 11B9;B382;1103 1166 11B9; # (뎂; 뎂; 뎂; 뎂; 뎂; ) HANGUL SYLLABLE DEBS +B383;B383;1103 1166 11BA;B383;1103 1166 11BA; # (뎃; 뎃; 뎃; 뎃; 뎃; ) HANGUL SYLLABLE DES +B384;B384;1103 1166 11BB;B384;1103 1166 11BB; # (뎄; 뎄; 뎄; 뎄; 뎄; ) HANGUL SYLLABLE DESS +B385;B385;1103 1166 11BC;B385;1103 1166 11BC; # (뎅; 뎅; 뎅; 뎅; 뎅; ) HANGUL SYLLABLE DENG +B386;B386;1103 1166 11BD;B386;1103 1166 11BD; # (뎆; 뎆; 뎆; 뎆; 뎆; ) HANGUL SYLLABLE DEJ +B387;B387;1103 1166 11BE;B387;1103 1166 11BE; # (뎇; 뎇; 뎇; 뎇; 뎇; ) HANGUL SYLLABLE DEC +B388;B388;1103 1166 11BF;B388;1103 1166 11BF; # (뎈; 뎈; 뎈; 뎈; 뎈; ) HANGUL SYLLABLE DEK +B389;B389;1103 1166 11C0;B389;1103 1166 11C0; # (뎉; 뎉; 뎉; 뎉; 뎉; ) HANGUL SYLLABLE DET +B38A;B38A;1103 1166 11C1;B38A;1103 1166 11C1; # (뎊; 뎊; 뎊; 뎊; 뎊; ) HANGUL SYLLABLE DEP +B38B;B38B;1103 1166 11C2;B38B;1103 1166 11C2; # (뎋; 뎋; 뎋; 뎋; 뎋; ) HANGUL SYLLABLE DEH +B38C;B38C;1103 1167;B38C;1103 1167; # (뎌; 뎌; 뎌; 뎌; 뎌; ) HANGUL SYLLABLE DYEO +B38D;B38D;1103 1167 11A8;B38D;1103 1167 11A8; # (뎍; 뎍; 뎍; 뎍; 뎍; ) HANGUL SYLLABLE DYEOG +B38E;B38E;1103 1167 11A9;B38E;1103 1167 11A9; # (뎎; 뎎; 뎎; 뎎; 뎎; ) HANGUL SYLLABLE DYEOGG +B38F;B38F;1103 1167 11AA;B38F;1103 1167 11AA; # (뎏; 뎏; 뎏; 뎏; 뎏; ) HANGUL SYLLABLE DYEOGS +B390;B390;1103 1167 11AB;B390;1103 1167 11AB; # (뎐; 뎐; 뎐; 뎐; 뎐; ) HANGUL SYLLABLE DYEON +B391;B391;1103 1167 11AC;B391;1103 1167 11AC; # (뎑; 뎑; 뎑; 뎑; 뎑; ) HANGUL SYLLABLE DYEONJ +B392;B392;1103 1167 11AD;B392;1103 1167 11AD; # (뎒; 뎒; 뎒; 뎒; 뎒; ) HANGUL SYLLABLE DYEONH +B393;B393;1103 1167 11AE;B393;1103 1167 11AE; # (뎓; 뎓; 뎓; 뎓; 뎓; ) HANGUL SYLLABLE DYEOD +B394;B394;1103 1167 11AF;B394;1103 1167 11AF; # (뎔; 뎔; 뎔; 뎔; 뎔; ) HANGUL SYLLABLE DYEOL +B395;B395;1103 1167 11B0;B395;1103 1167 11B0; # (뎕; 뎕; 뎕; 뎕; 뎕; ) HANGUL SYLLABLE DYEOLG +B396;B396;1103 1167 11B1;B396;1103 1167 11B1; # (뎖; 뎖; 뎖; 뎖; 뎖; ) HANGUL SYLLABLE DYEOLM +B397;B397;1103 1167 11B2;B397;1103 1167 11B2; # (뎗; 뎗; 뎗; 뎗; 뎗; ) HANGUL SYLLABLE DYEOLB +B398;B398;1103 1167 11B3;B398;1103 1167 11B3; # (뎘; 뎘; 뎘; 뎘; 뎘; ) HANGUL SYLLABLE DYEOLS +B399;B399;1103 1167 11B4;B399;1103 1167 11B4; # (뎙; 뎙; 뎙; 뎙; 뎙; ) HANGUL SYLLABLE DYEOLT +B39A;B39A;1103 1167 11B5;B39A;1103 1167 11B5; # (뎚; 뎚; 뎚; 뎚; 뎚; ) HANGUL SYLLABLE DYEOLP +B39B;B39B;1103 1167 11B6;B39B;1103 1167 11B6; # (뎛; 뎛; 뎛; 뎛; 뎛; ) HANGUL SYLLABLE DYEOLH +B39C;B39C;1103 1167 11B7;B39C;1103 1167 11B7; # (뎜; 뎜; 뎜; 뎜; 뎜; ) HANGUL SYLLABLE DYEOM +B39D;B39D;1103 1167 11B8;B39D;1103 1167 11B8; # (뎝; 뎝; 뎝; 뎝; 뎝; ) HANGUL SYLLABLE DYEOB +B39E;B39E;1103 1167 11B9;B39E;1103 1167 11B9; # (뎞; 뎞; 뎞; 뎞; 뎞; ) HANGUL SYLLABLE DYEOBS +B39F;B39F;1103 1167 11BA;B39F;1103 1167 11BA; # (뎟; 뎟; 뎟; 뎟; 뎟; ) HANGUL SYLLABLE DYEOS +B3A0;B3A0;1103 1167 11BB;B3A0;1103 1167 11BB; # (뎠; 뎠; 뎠; 뎠; 뎠; ) HANGUL SYLLABLE DYEOSS +B3A1;B3A1;1103 1167 11BC;B3A1;1103 1167 11BC; # (뎡; 뎡; 뎡; 뎡; 뎡; ) HANGUL SYLLABLE DYEONG +B3A2;B3A2;1103 1167 11BD;B3A2;1103 1167 11BD; # (뎢; 뎢; 뎢; 뎢; 뎢; ) HANGUL SYLLABLE DYEOJ +B3A3;B3A3;1103 1167 11BE;B3A3;1103 1167 11BE; # (뎣; 뎣; 뎣; 뎣; 뎣; ) HANGUL SYLLABLE DYEOC +B3A4;B3A4;1103 1167 11BF;B3A4;1103 1167 11BF; # (뎤; 뎤; 뎤; 뎤; 뎤; ) HANGUL SYLLABLE DYEOK +B3A5;B3A5;1103 1167 11C0;B3A5;1103 1167 11C0; # (뎥; 뎥; 뎥; 뎥; 뎥; ) HANGUL SYLLABLE DYEOT +B3A6;B3A6;1103 1167 11C1;B3A6;1103 1167 11C1; # (뎦; 뎦; 뎦; 뎦; 뎦; ) HANGUL SYLLABLE DYEOP +B3A7;B3A7;1103 1167 11C2;B3A7;1103 1167 11C2; # (뎧; 뎧; 뎧; 뎧; 뎧; ) HANGUL SYLLABLE DYEOH +B3A8;B3A8;1103 1168;B3A8;1103 1168; # (뎨; 뎨; 뎨; 뎨; 뎨; ) HANGUL SYLLABLE DYE +B3A9;B3A9;1103 1168 11A8;B3A9;1103 1168 11A8; # (뎩; 뎩; 뎩; 뎩; 뎩; ) HANGUL SYLLABLE DYEG +B3AA;B3AA;1103 1168 11A9;B3AA;1103 1168 11A9; # (뎪; 뎪; 뎪; 뎪; 뎪; ) HANGUL SYLLABLE DYEGG +B3AB;B3AB;1103 1168 11AA;B3AB;1103 1168 11AA; # (뎫; 뎫; 뎫; 뎫; 뎫; ) HANGUL SYLLABLE DYEGS +B3AC;B3AC;1103 1168 11AB;B3AC;1103 1168 11AB; # (뎬; 뎬; 뎬; 뎬; 뎬; ) HANGUL SYLLABLE DYEN +B3AD;B3AD;1103 1168 11AC;B3AD;1103 1168 11AC; # (뎭; 뎭; 뎭; 뎭; 뎭; ) HANGUL SYLLABLE DYENJ +B3AE;B3AE;1103 1168 11AD;B3AE;1103 1168 11AD; # (뎮; 뎮; 뎮; 뎮; 뎮; ) HANGUL SYLLABLE DYENH +B3AF;B3AF;1103 1168 11AE;B3AF;1103 1168 11AE; # (뎯; 뎯; 뎯; 뎯; 뎯; ) HANGUL SYLLABLE DYED +B3B0;B3B0;1103 1168 11AF;B3B0;1103 1168 11AF; # (뎰; 뎰; 뎰; 뎰; 뎰; ) HANGUL SYLLABLE DYEL +B3B1;B3B1;1103 1168 11B0;B3B1;1103 1168 11B0; # (뎱; 뎱; 뎱; 뎱; 뎱; ) HANGUL SYLLABLE DYELG +B3B2;B3B2;1103 1168 11B1;B3B2;1103 1168 11B1; # (뎲; 뎲; 뎲; 뎲; 뎲; ) HANGUL SYLLABLE DYELM +B3B3;B3B3;1103 1168 11B2;B3B3;1103 1168 11B2; # (뎳; 뎳; 뎳; 뎳; 뎳; ) HANGUL SYLLABLE DYELB +B3B4;B3B4;1103 1168 11B3;B3B4;1103 1168 11B3; # (뎴; 뎴; 뎴; 뎴; 뎴; ) HANGUL SYLLABLE DYELS +B3B5;B3B5;1103 1168 11B4;B3B5;1103 1168 11B4; # (뎵; 뎵; 뎵; 뎵; 뎵; ) HANGUL SYLLABLE DYELT +B3B6;B3B6;1103 1168 11B5;B3B6;1103 1168 11B5; # (뎶; 뎶; 뎶; 뎶; 뎶; ) HANGUL SYLLABLE DYELP +B3B7;B3B7;1103 1168 11B6;B3B7;1103 1168 11B6; # (뎷; 뎷; 뎷; 뎷; 뎷; ) HANGUL SYLLABLE DYELH +B3B8;B3B8;1103 1168 11B7;B3B8;1103 1168 11B7; # (뎸; 뎸; 뎸; 뎸; 뎸; ) HANGUL SYLLABLE DYEM +B3B9;B3B9;1103 1168 11B8;B3B9;1103 1168 11B8; # (뎹; 뎹; 뎹; 뎹; 뎹; ) HANGUL SYLLABLE DYEB +B3BA;B3BA;1103 1168 11B9;B3BA;1103 1168 11B9; # (뎺; 뎺; 뎺; 뎺; 뎺; ) HANGUL SYLLABLE DYEBS +B3BB;B3BB;1103 1168 11BA;B3BB;1103 1168 11BA; # (뎻; 뎻; 뎻; 뎻; 뎻; ) HANGUL SYLLABLE DYES +B3BC;B3BC;1103 1168 11BB;B3BC;1103 1168 11BB; # (뎼; 뎼; 뎼; 뎼; 뎼; ) HANGUL SYLLABLE DYESS +B3BD;B3BD;1103 1168 11BC;B3BD;1103 1168 11BC; # (뎽; 뎽; 뎽; 뎽; 뎽; ) HANGUL SYLLABLE DYENG +B3BE;B3BE;1103 1168 11BD;B3BE;1103 1168 11BD; # (뎾; 뎾; 뎾; 뎾; 뎾; ) HANGUL SYLLABLE DYEJ +B3BF;B3BF;1103 1168 11BE;B3BF;1103 1168 11BE; # (뎿; 뎿; 뎿; 뎿; 뎿; ) HANGUL SYLLABLE DYEC +B3C0;B3C0;1103 1168 11BF;B3C0;1103 1168 11BF; # (돀; 돀; 돀; 돀; 돀; ) HANGUL SYLLABLE DYEK +B3C1;B3C1;1103 1168 11C0;B3C1;1103 1168 11C0; # (돁; 돁; 돁; 돁; 돁; ) HANGUL SYLLABLE DYET +B3C2;B3C2;1103 1168 11C1;B3C2;1103 1168 11C1; # (돂; 돂; 돂; 돂; 돂; ) HANGUL SYLLABLE DYEP +B3C3;B3C3;1103 1168 11C2;B3C3;1103 1168 11C2; # (돃; 돃; 돃; 돃; 돃; ) HANGUL SYLLABLE DYEH +B3C4;B3C4;1103 1169;B3C4;1103 1169; # (도; 도; 도; 도; 도; ) HANGUL SYLLABLE DO +B3C5;B3C5;1103 1169 11A8;B3C5;1103 1169 11A8; # (독; 독; 독; 독; 독; ) HANGUL SYLLABLE DOG +B3C6;B3C6;1103 1169 11A9;B3C6;1103 1169 11A9; # (돆; 돆; 돆; 돆; 돆; ) HANGUL SYLLABLE DOGG +B3C7;B3C7;1103 1169 11AA;B3C7;1103 1169 11AA; # (돇; 돇; 돇; 돇; 돇; ) HANGUL SYLLABLE DOGS +B3C8;B3C8;1103 1169 11AB;B3C8;1103 1169 11AB; # (돈; 돈; 돈; 돈; 돈; ) HANGUL SYLLABLE DON +B3C9;B3C9;1103 1169 11AC;B3C9;1103 1169 11AC; # (돉; 돉; 돉; 돉; 돉; ) HANGUL SYLLABLE DONJ +B3CA;B3CA;1103 1169 11AD;B3CA;1103 1169 11AD; # (돊; 돊; 돊; 돊; 돊; ) HANGUL SYLLABLE DONH +B3CB;B3CB;1103 1169 11AE;B3CB;1103 1169 11AE; # (돋; 돋; 돋; 돋; 돋; ) HANGUL SYLLABLE DOD +B3CC;B3CC;1103 1169 11AF;B3CC;1103 1169 11AF; # (돌; 돌; 돌; 돌; 돌; ) HANGUL SYLLABLE DOL +B3CD;B3CD;1103 1169 11B0;B3CD;1103 1169 11B0; # (돍; 돍; 돍; 돍; 돍; ) HANGUL SYLLABLE DOLG +B3CE;B3CE;1103 1169 11B1;B3CE;1103 1169 11B1; # (돎; 돎; 돎; 돎; 돎; ) HANGUL SYLLABLE DOLM +B3CF;B3CF;1103 1169 11B2;B3CF;1103 1169 11B2; # (돏; 돏; 돏; 돏; 돏; ) HANGUL SYLLABLE DOLB +B3D0;B3D0;1103 1169 11B3;B3D0;1103 1169 11B3; # (돐; 돐; 돐; 돐; 돐; ) HANGUL SYLLABLE DOLS +B3D1;B3D1;1103 1169 11B4;B3D1;1103 1169 11B4; # (돑; 돑; 돑; 돑; 돑; ) HANGUL SYLLABLE DOLT +B3D2;B3D2;1103 1169 11B5;B3D2;1103 1169 11B5; # (돒; 돒; 돒; 돒; 돒; ) HANGUL SYLLABLE DOLP +B3D3;B3D3;1103 1169 11B6;B3D3;1103 1169 11B6; # (돓; 돓; 돓; 돓; 돓; ) HANGUL SYLLABLE DOLH +B3D4;B3D4;1103 1169 11B7;B3D4;1103 1169 11B7; # (돔; 돔; 돔; 돔; 돔; ) HANGUL SYLLABLE DOM +B3D5;B3D5;1103 1169 11B8;B3D5;1103 1169 11B8; # (돕; 돕; 돕; 돕; 돕; ) HANGUL SYLLABLE DOB +B3D6;B3D6;1103 1169 11B9;B3D6;1103 1169 11B9; # (돖; 돖; 돖; 돖; 돖; ) HANGUL SYLLABLE DOBS +B3D7;B3D7;1103 1169 11BA;B3D7;1103 1169 11BA; # (돗; 돗; 돗; 돗; 돗; ) HANGUL SYLLABLE DOS +B3D8;B3D8;1103 1169 11BB;B3D8;1103 1169 11BB; # (돘; 돘; 돘; 돘; 돘; ) HANGUL SYLLABLE DOSS +B3D9;B3D9;1103 1169 11BC;B3D9;1103 1169 11BC; # (동; 동; 동; 동; 동; ) HANGUL SYLLABLE DONG +B3DA;B3DA;1103 1169 11BD;B3DA;1103 1169 11BD; # (돚; 돚; 돚; 돚; 돚; ) HANGUL SYLLABLE DOJ +B3DB;B3DB;1103 1169 11BE;B3DB;1103 1169 11BE; # (돛; 돛; 돛; 돛; 돛; ) HANGUL SYLLABLE DOC +B3DC;B3DC;1103 1169 11BF;B3DC;1103 1169 11BF; # (돜; 돜; 돜; 돜; 돜; ) HANGUL SYLLABLE DOK +B3DD;B3DD;1103 1169 11C0;B3DD;1103 1169 11C0; # (돝; 돝; 돝; 돝; 돝; ) HANGUL SYLLABLE DOT +B3DE;B3DE;1103 1169 11C1;B3DE;1103 1169 11C1; # (돞; 돞; 돞; 돞; 돞; ) HANGUL SYLLABLE DOP +B3DF;B3DF;1103 1169 11C2;B3DF;1103 1169 11C2; # (돟; 돟; 돟; 돟; 돟; ) HANGUL SYLLABLE DOH +B3E0;B3E0;1103 116A;B3E0;1103 116A; # (돠; 돠; 돠; 돠; 돠; ) HANGUL SYLLABLE DWA +B3E1;B3E1;1103 116A 11A8;B3E1;1103 116A 11A8; # (돡; 돡; 돡; 돡; 돡; ) HANGUL SYLLABLE DWAG +B3E2;B3E2;1103 116A 11A9;B3E2;1103 116A 11A9; # (돢; 돢; 돢; 돢; 돢; ) HANGUL SYLLABLE DWAGG +B3E3;B3E3;1103 116A 11AA;B3E3;1103 116A 11AA; # (돣; 돣; 돣; 돣; 돣; ) HANGUL SYLLABLE DWAGS +B3E4;B3E4;1103 116A 11AB;B3E4;1103 116A 11AB; # (돤; 돤; 돤; 돤; 돤; ) HANGUL SYLLABLE DWAN +B3E5;B3E5;1103 116A 11AC;B3E5;1103 116A 11AC; # (돥; 돥; 돥; 돥; 돥; ) HANGUL SYLLABLE DWANJ +B3E6;B3E6;1103 116A 11AD;B3E6;1103 116A 11AD; # (돦; 돦; 돦; 돦; 돦; ) HANGUL SYLLABLE DWANH +B3E7;B3E7;1103 116A 11AE;B3E7;1103 116A 11AE; # (돧; 돧; 돧; 돧; 돧; ) HANGUL SYLLABLE DWAD +B3E8;B3E8;1103 116A 11AF;B3E8;1103 116A 11AF; # (돨; 돨; 돨; 돨; 돨; ) HANGUL SYLLABLE DWAL +B3E9;B3E9;1103 116A 11B0;B3E9;1103 116A 11B0; # (돩; 돩; 돩; 돩; 돩; ) HANGUL SYLLABLE DWALG +B3EA;B3EA;1103 116A 11B1;B3EA;1103 116A 11B1; # (돪; 돪; 돪; 돪; 돪; ) HANGUL SYLLABLE DWALM +B3EB;B3EB;1103 116A 11B2;B3EB;1103 116A 11B2; # (돫; 돫; 돫; 돫; 돫; ) HANGUL SYLLABLE DWALB +B3EC;B3EC;1103 116A 11B3;B3EC;1103 116A 11B3; # (돬; 돬; 돬; 돬; 돬; ) HANGUL SYLLABLE DWALS +B3ED;B3ED;1103 116A 11B4;B3ED;1103 116A 11B4; # (돭; 돭; 돭; 돭; 돭; ) HANGUL SYLLABLE DWALT +B3EE;B3EE;1103 116A 11B5;B3EE;1103 116A 11B5; # (돮; 돮; 돮; 돮; 돮; ) HANGUL SYLLABLE DWALP +B3EF;B3EF;1103 116A 11B6;B3EF;1103 116A 11B6; # (돯; 돯; 돯; 돯; 돯; ) HANGUL SYLLABLE DWALH +B3F0;B3F0;1103 116A 11B7;B3F0;1103 116A 11B7; # (돰; 돰; 돰; 돰; 돰; ) HANGUL SYLLABLE DWAM +B3F1;B3F1;1103 116A 11B8;B3F1;1103 116A 11B8; # (돱; 돱; 돱; 돱; 돱; ) HANGUL SYLLABLE DWAB +B3F2;B3F2;1103 116A 11B9;B3F2;1103 116A 11B9; # (돲; 돲; 돲; 돲; 돲; ) HANGUL SYLLABLE DWABS +B3F3;B3F3;1103 116A 11BA;B3F3;1103 116A 11BA; # (돳; 돳; 돳; 돳; 돳; ) HANGUL SYLLABLE DWAS +B3F4;B3F4;1103 116A 11BB;B3F4;1103 116A 11BB; # (돴; 돴; 돴; 돴; 돴; ) HANGUL SYLLABLE DWASS +B3F5;B3F5;1103 116A 11BC;B3F5;1103 116A 11BC; # (돵; 돵; 돵; 돵; 돵; ) HANGUL SYLLABLE DWANG +B3F6;B3F6;1103 116A 11BD;B3F6;1103 116A 11BD; # (돶; 돶; 돶; 돶; 돶; ) HANGUL SYLLABLE DWAJ +B3F7;B3F7;1103 116A 11BE;B3F7;1103 116A 11BE; # (돷; 돷; 돷; 돷; 돷; ) HANGUL SYLLABLE DWAC +B3F8;B3F8;1103 116A 11BF;B3F8;1103 116A 11BF; # (돸; 돸; 돸; 돸; 돸; ) HANGUL SYLLABLE DWAK +B3F9;B3F9;1103 116A 11C0;B3F9;1103 116A 11C0; # (돹; 돹; 돹; 돹; 돹; ) HANGUL SYLLABLE DWAT +B3FA;B3FA;1103 116A 11C1;B3FA;1103 116A 11C1; # (돺; 돺; 돺; 돺; 돺; ) HANGUL SYLLABLE DWAP +B3FB;B3FB;1103 116A 11C2;B3FB;1103 116A 11C2; # (돻; 돻; 돻; 돻; 돻; ) HANGUL SYLLABLE DWAH +B3FC;B3FC;1103 116B;B3FC;1103 116B; # (돼; 돼; 돼; 돼; 돼; ) HANGUL SYLLABLE DWAE +B3FD;B3FD;1103 116B 11A8;B3FD;1103 116B 11A8; # (돽; 돽; 돽; 돽; 돽; ) HANGUL SYLLABLE DWAEG +B3FE;B3FE;1103 116B 11A9;B3FE;1103 116B 11A9; # (돾; 돾; 돾; 돾; 돾; ) HANGUL SYLLABLE DWAEGG +B3FF;B3FF;1103 116B 11AA;B3FF;1103 116B 11AA; # (돿; 돿; 돿; 돿; 돿; ) HANGUL SYLLABLE DWAEGS +B400;B400;1103 116B 11AB;B400;1103 116B 11AB; # (됀; 됀; 됀; 됀; 됀; ) HANGUL SYLLABLE DWAEN +B401;B401;1103 116B 11AC;B401;1103 116B 11AC; # (됁; 됁; 됁; 됁; 됁; ) HANGUL SYLLABLE DWAENJ +B402;B402;1103 116B 11AD;B402;1103 116B 11AD; # (됂; 됂; 됂; 됂; 됂; ) HANGUL SYLLABLE DWAENH +B403;B403;1103 116B 11AE;B403;1103 116B 11AE; # (됃; 됃; 됃; 됃; 됃; ) HANGUL SYLLABLE DWAED +B404;B404;1103 116B 11AF;B404;1103 116B 11AF; # (됄; 됄; 됄; 됄; 됄; ) HANGUL SYLLABLE DWAEL +B405;B405;1103 116B 11B0;B405;1103 116B 11B0; # (됅; 됅; 됅; 됅; 됅; ) HANGUL SYLLABLE DWAELG +B406;B406;1103 116B 11B1;B406;1103 116B 11B1; # (됆; 됆; 됆; 됆; 됆; ) HANGUL SYLLABLE DWAELM +B407;B407;1103 116B 11B2;B407;1103 116B 11B2; # (됇; 됇; 됇; 됇; 됇; ) HANGUL SYLLABLE DWAELB +B408;B408;1103 116B 11B3;B408;1103 116B 11B3; # (됈; 됈; 됈; 됈; 됈; ) HANGUL SYLLABLE DWAELS +B409;B409;1103 116B 11B4;B409;1103 116B 11B4; # (됉; 됉; 됉; 됉; 됉; ) HANGUL SYLLABLE DWAELT +B40A;B40A;1103 116B 11B5;B40A;1103 116B 11B5; # (됊; 됊; 됊; 됊; 됊; ) HANGUL SYLLABLE DWAELP +B40B;B40B;1103 116B 11B6;B40B;1103 116B 11B6; # (됋; 됋; 됋; 됋; 됋; ) HANGUL SYLLABLE DWAELH +B40C;B40C;1103 116B 11B7;B40C;1103 116B 11B7; # (됌; 됌; 됌; 됌; 됌; ) HANGUL SYLLABLE DWAEM +B40D;B40D;1103 116B 11B8;B40D;1103 116B 11B8; # (됍; 됍; 됍; 됍; 됍; ) HANGUL SYLLABLE DWAEB +B40E;B40E;1103 116B 11B9;B40E;1103 116B 11B9; # (됎; 됎; 됎; 됎; 됎; ) HANGUL SYLLABLE DWAEBS +B40F;B40F;1103 116B 11BA;B40F;1103 116B 11BA; # (됏; 됏; 됏; 됏; 됏; ) HANGUL SYLLABLE DWAES +B410;B410;1103 116B 11BB;B410;1103 116B 11BB; # (됐; 됐; 됐; 됐; 됐; ) HANGUL SYLLABLE DWAESS +B411;B411;1103 116B 11BC;B411;1103 116B 11BC; # (됑; 됑; 됑; 됑; 됑; ) HANGUL SYLLABLE DWAENG +B412;B412;1103 116B 11BD;B412;1103 116B 11BD; # (됒; 됒; 됒; 됒; 됒; ) HANGUL SYLLABLE DWAEJ +B413;B413;1103 116B 11BE;B413;1103 116B 11BE; # (됓; 됓; 됓; 됓; 됓; ) HANGUL SYLLABLE DWAEC +B414;B414;1103 116B 11BF;B414;1103 116B 11BF; # (됔; 됔; 됔; 됔; 됔; ) HANGUL SYLLABLE DWAEK +B415;B415;1103 116B 11C0;B415;1103 116B 11C0; # (됕; 됕; 됕; 됕; 됕; ) HANGUL SYLLABLE DWAET +B416;B416;1103 116B 11C1;B416;1103 116B 11C1; # (됖; 됖; 됖; 됖; 됖; ) HANGUL SYLLABLE DWAEP +B417;B417;1103 116B 11C2;B417;1103 116B 11C2; # (됗; 됗; 됗; 됗; 됗; ) HANGUL SYLLABLE DWAEH +B418;B418;1103 116C;B418;1103 116C; # (되; 되; 되; 되; 되; ) HANGUL SYLLABLE DOE +B419;B419;1103 116C 11A8;B419;1103 116C 11A8; # (됙; 됙; 됙; 됙; 됙; ) HANGUL SYLLABLE DOEG +B41A;B41A;1103 116C 11A9;B41A;1103 116C 11A9; # (됚; 됚; 됚; 됚; 됚; ) HANGUL SYLLABLE DOEGG +B41B;B41B;1103 116C 11AA;B41B;1103 116C 11AA; # (됛; 됛; 됛; 됛; 됛; ) HANGUL SYLLABLE DOEGS +B41C;B41C;1103 116C 11AB;B41C;1103 116C 11AB; # (된; 된; 된; 된; 된; ) HANGUL SYLLABLE DOEN +B41D;B41D;1103 116C 11AC;B41D;1103 116C 11AC; # (됝; 됝; 됝; 됝; 됝; ) HANGUL SYLLABLE DOENJ +B41E;B41E;1103 116C 11AD;B41E;1103 116C 11AD; # (됞; 됞; 됞; 됞; 됞; ) HANGUL SYLLABLE DOENH +B41F;B41F;1103 116C 11AE;B41F;1103 116C 11AE; # (됟; 됟; 됟; 됟; 됟; ) HANGUL SYLLABLE DOED +B420;B420;1103 116C 11AF;B420;1103 116C 11AF; # (될; 될; 될; 될; 될; ) HANGUL SYLLABLE DOEL +B421;B421;1103 116C 11B0;B421;1103 116C 11B0; # (됡; 됡; 됡; 됡; 됡; ) HANGUL SYLLABLE DOELG +B422;B422;1103 116C 11B1;B422;1103 116C 11B1; # (됢; 됢; 됢; 됢; 됢; ) HANGUL SYLLABLE DOELM +B423;B423;1103 116C 11B2;B423;1103 116C 11B2; # (됣; 됣; 됣; 됣; 됣; ) HANGUL SYLLABLE DOELB +B424;B424;1103 116C 11B3;B424;1103 116C 11B3; # (됤; 됤; 됤; 됤; 됤; ) HANGUL SYLLABLE DOELS +B425;B425;1103 116C 11B4;B425;1103 116C 11B4; # (됥; 됥; 됥; 됥; 됥; ) HANGUL SYLLABLE DOELT +B426;B426;1103 116C 11B5;B426;1103 116C 11B5; # (됦; 됦; 됦; 됦; 됦; ) HANGUL SYLLABLE DOELP +B427;B427;1103 116C 11B6;B427;1103 116C 11B6; # (됧; 됧; 됧; 됧; 됧; ) HANGUL SYLLABLE DOELH +B428;B428;1103 116C 11B7;B428;1103 116C 11B7; # (됨; 됨; 됨; 됨; 됨; ) HANGUL SYLLABLE DOEM +B429;B429;1103 116C 11B8;B429;1103 116C 11B8; # (됩; 됩; 됩; 됩; 됩; ) HANGUL SYLLABLE DOEB +B42A;B42A;1103 116C 11B9;B42A;1103 116C 11B9; # (됪; 됪; 됪; 됪; 됪; ) HANGUL SYLLABLE DOEBS +B42B;B42B;1103 116C 11BA;B42B;1103 116C 11BA; # (됫; 됫; 됫; 됫; 됫; ) HANGUL SYLLABLE DOES +B42C;B42C;1103 116C 11BB;B42C;1103 116C 11BB; # (됬; 됬; 됬; 됬; 됬; ) HANGUL SYLLABLE DOESS +B42D;B42D;1103 116C 11BC;B42D;1103 116C 11BC; # (됭; 됭; 됭; 됭; 됭; ) HANGUL SYLLABLE DOENG +B42E;B42E;1103 116C 11BD;B42E;1103 116C 11BD; # (됮; 됮; 됮; 됮; 됮; ) HANGUL SYLLABLE DOEJ +B42F;B42F;1103 116C 11BE;B42F;1103 116C 11BE; # (됯; 됯; 됯; 됯; 됯; ) HANGUL SYLLABLE DOEC +B430;B430;1103 116C 11BF;B430;1103 116C 11BF; # (됰; 됰; 됰; 됰; 됰; ) HANGUL SYLLABLE DOEK +B431;B431;1103 116C 11C0;B431;1103 116C 11C0; # (됱; 됱; 됱; 됱; 됱; ) HANGUL SYLLABLE DOET +B432;B432;1103 116C 11C1;B432;1103 116C 11C1; # (됲; 됲; 됲; 됲; 됲; ) HANGUL SYLLABLE DOEP +B433;B433;1103 116C 11C2;B433;1103 116C 11C2; # (됳; 됳; 됳; 됳; 됳; ) HANGUL SYLLABLE DOEH +B434;B434;1103 116D;B434;1103 116D; # (됴; 됴; 됴; 됴; 됴; ) HANGUL SYLLABLE DYO +B435;B435;1103 116D 11A8;B435;1103 116D 11A8; # (됵; 됵; 됵; 됵; 됵; ) HANGUL SYLLABLE DYOG +B436;B436;1103 116D 11A9;B436;1103 116D 11A9; # (됶; 됶; 됶; 됶; 됶; ) HANGUL SYLLABLE DYOGG +B437;B437;1103 116D 11AA;B437;1103 116D 11AA; # (됷; 됷; 됷; 됷; 됷; ) HANGUL SYLLABLE DYOGS +B438;B438;1103 116D 11AB;B438;1103 116D 11AB; # (됸; 됸; 됸; 됸; 됸; ) HANGUL SYLLABLE DYON +B439;B439;1103 116D 11AC;B439;1103 116D 11AC; # (됹; 됹; 됹; 됹; 됹; ) HANGUL SYLLABLE DYONJ +B43A;B43A;1103 116D 11AD;B43A;1103 116D 11AD; # (됺; 됺; 됺; 됺; 됺; ) HANGUL SYLLABLE DYONH +B43B;B43B;1103 116D 11AE;B43B;1103 116D 11AE; # (됻; 됻; 됻; 됻; 됻; ) HANGUL SYLLABLE DYOD +B43C;B43C;1103 116D 11AF;B43C;1103 116D 11AF; # (됼; 됼; 됼; 됼; 됼; ) HANGUL SYLLABLE DYOL +B43D;B43D;1103 116D 11B0;B43D;1103 116D 11B0; # (됽; 됽; 됽; 됽; 됽; ) HANGUL SYLLABLE DYOLG +B43E;B43E;1103 116D 11B1;B43E;1103 116D 11B1; # (됾; 됾; 됾; 됾; 됾; ) HANGUL SYLLABLE DYOLM +B43F;B43F;1103 116D 11B2;B43F;1103 116D 11B2; # (됿; 됿; 됿; 됿; 됿; ) HANGUL SYLLABLE DYOLB +B440;B440;1103 116D 11B3;B440;1103 116D 11B3; # (둀; 둀; 둀; 둀; 둀; ) HANGUL SYLLABLE DYOLS +B441;B441;1103 116D 11B4;B441;1103 116D 11B4; # (둁; 둁; 둁; 둁; 둁; ) HANGUL SYLLABLE DYOLT +B442;B442;1103 116D 11B5;B442;1103 116D 11B5; # (둂; 둂; 둂; 둂; 둂; ) HANGUL SYLLABLE DYOLP +B443;B443;1103 116D 11B6;B443;1103 116D 11B6; # (둃; 둃; 둃; 둃; 둃; ) HANGUL SYLLABLE DYOLH +B444;B444;1103 116D 11B7;B444;1103 116D 11B7; # (둄; 둄; 둄; 둄; 둄; ) HANGUL SYLLABLE DYOM +B445;B445;1103 116D 11B8;B445;1103 116D 11B8; # (둅; 둅; 둅; 둅; 둅; ) HANGUL SYLLABLE DYOB +B446;B446;1103 116D 11B9;B446;1103 116D 11B9; # (둆; 둆; 둆; 둆; 둆; ) HANGUL SYLLABLE DYOBS +B447;B447;1103 116D 11BA;B447;1103 116D 11BA; # (둇; 둇; 둇; 둇; 둇; ) HANGUL SYLLABLE DYOS +B448;B448;1103 116D 11BB;B448;1103 116D 11BB; # (둈; 둈; 둈; 둈; 둈; ) HANGUL SYLLABLE DYOSS +B449;B449;1103 116D 11BC;B449;1103 116D 11BC; # (둉; 둉; 둉; 둉; 둉; ) HANGUL SYLLABLE DYONG +B44A;B44A;1103 116D 11BD;B44A;1103 116D 11BD; # (둊; 둊; 둊; 둊; 둊; ) HANGUL SYLLABLE DYOJ +B44B;B44B;1103 116D 11BE;B44B;1103 116D 11BE; # (둋; 둋; 둋; 둋; 둋; ) HANGUL SYLLABLE DYOC +B44C;B44C;1103 116D 11BF;B44C;1103 116D 11BF; # (둌; 둌; 둌; 둌; 둌; ) HANGUL SYLLABLE DYOK +B44D;B44D;1103 116D 11C0;B44D;1103 116D 11C0; # (둍; 둍; 둍; 둍; 둍; ) HANGUL SYLLABLE DYOT +B44E;B44E;1103 116D 11C1;B44E;1103 116D 11C1; # (둎; 둎; 둎; 둎; 둎; ) HANGUL SYLLABLE DYOP +B44F;B44F;1103 116D 11C2;B44F;1103 116D 11C2; # (둏; 둏; 둏; 둏; 둏; ) HANGUL SYLLABLE DYOH +B450;B450;1103 116E;B450;1103 116E; # (두; 두; 두; 두; 두; ) HANGUL SYLLABLE DU +B451;B451;1103 116E 11A8;B451;1103 116E 11A8; # (둑; 둑; 둑; 둑; 둑; ) HANGUL SYLLABLE DUG +B452;B452;1103 116E 11A9;B452;1103 116E 11A9; # (둒; 둒; 둒; 둒; 둒; ) HANGUL SYLLABLE DUGG +B453;B453;1103 116E 11AA;B453;1103 116E 11AA; # (둓; 둓; 둓; 둓; 둓; ) HANGUL SYLLABLE DUGS +B454;B454;1103 116E 11AB;B454;1103 116E 11AB; # (둔; 둔; 둔; 둔; 둔; ) HANGUL SYLLABLE DUN +B455;B455;1103 116E 11AC;B455;1103 116E 11AC; # (둕; 둕; 둕; 둕; 둕; ) HANGUL SYLLABLE DUNJ +B456;B456;1103 116E 11AD;B456;1103 116E 11AD; # (둖; 둖; 둖; 둖; 둖; ) HANGUL SYLLABLE DUNH +B457;B457;1103 116E 11AE;B457;1103 116E 11AE; # (둗; 둗; 둗; 둗; 둗; ) HANGUL SYLLABLE DUD +B458;B458;1103 116E 11AF;B458;1103 116E 11AF; # (둘; 둘; 둘; 둘; 둘; ) HANGUL SYLLABLE DUL +B459;B459;1103 116E 11B0;B459;1103 116E 11B0; # (둙; 둙; 둙; 둙; 둙; ) HANGUL SYLLABLE DULG +B45A;B45A;1103 116E 11B1;B45A;1103 116E 11B1; # (둚; 둚; 둚; 둚; 둚; ) HANGUL SYLLABLE DULM +B45B;B45B;1103 116E 11B2;B45B;1103 116E 11B2; # (둛; 둛; 둛; 둛; 둛; ) HANGUL SYLLABLE DULB +B45C;B45C;1103 116E 11B3;B45C;1103 116E 11B3; # (둜; 둜; 둜; 둜; 둜; ) HANGUL SYLLABLE DULS +B45D;B45D;1103 116E 11B4;B45D;1103 116E 11B4; # (둝; 둝; 둝; 둝; 둝; ) HANGUL SYLLABLE DULT +B45E;B45E;1103 116E 11B5;B45E;1103 116E 11B5; # (둞; 둞; 둞; 둞; 둞; ) HANGUL SYLLABLE DULP +B45F;B45F;1103 116E 11B6;B45F;1103 116E 11B6; # (둟; 둟; 둟; 둟; 둟; ) HANGUL SYLLABLE DULH +B460;B460;1103 116E 11B7;B460;1103 116E 11B7; # (둠; 둠; 둠; 둠; 둠; ) HANGUL SYLLABLE DUM +B461;B461;1103 116E 11B8;B461;1103 116E 11B8; # (둡; 둡; 둡; 둡; 둡; ) HANGUL SYLLABLE DUB +B462;B462;1103 116E 11B9;B462;1103 116E 11B9; # (둢; 둢; 둢; 둢; 둢; ) HANGUL SYLLABLE DUBS +B463;B463;1103 116E 11BA;B463;1103 116E 11BA; # (둣; 둣; 둣; 둣; 둣; ) HANGUL SYLLABLE DUS +B464;B464;1103 116E 11BB;B464;1103 116E 11BB; # (둤; 둤; 둤; 둤; 둤; ) HANGUL SYLLABLE DUSS +B465;B465;1103 116E 11BC;B465;1103 116E 11BC; # (둥; 둥; 둥; 둥; 둥; ) HANGUL SYLLABLE DUNG +B466;B466;1103 116E 11BD;B466;1103 116E 11BD; # (둦; 둦; 둦; 둦; 둦; ) HANGUL SYLLABLE DUJ +B467;B467;1103 116E 11BE;B467;1103 116E 11BE; # (둧; 둧; 둧; 둧; 둧; ) HANGUL SYLLABLE DUC +B468;B468;1103 116E 11BF;B468;1103 116E 11BF; # (둨; 둨; 둨; 둨; 둨; ) HANGUL SYLLABLE DUK +B469;B469;1103 116E 11C0;B469;1103 116E 11C0; # (둩; 둩; 둩; 둩; 둩; ) HANGUL SYLLABLE DUT +B46A;B46A;1103 116E 11C1;B46A;1103 116E 11C1; # (둪; 둪; 둪; 둪; 둪; ) HANGUL SYLLABLE DUP +B46B;B46B;1103 116E 11C2;B46B;1103 116E 11C2; # (둫; 둫; 둫; 둫; 둫; ) HANGUL SYLLABLE DUH +B46C;B46C;1103 116F;B46C;1103 116F; # (둬; 둬; 둬; 둬; 둬; ) HANGUL SYLLABLE DWEO +B46D;B46D;1103 116F 11A8;B46D;1103 116F 11A8; # (둭; 둭; 둭; 둭; 둭; ) HANGUL SYLLABLE DWEOG +B46E;B46E;1103 116F 11A9;B46E;1103 116F 11A9; # (둮; 둮; 둮; 둮; 둮; ) HANGUL SYLLABLE DWEOGG +B46F;B46F;1103 116F 11AA;B46F;1103 116F 11AA; # (둯; 둯; 둯; 둯; 둯; ) HANGUL SYLLABLE DWEOGS +B470;B470;1103 116F 11AB;B470;1103 116F 11AB; # (둰; 둰; 둰; 둰; 둰; ) HANGUL SYLLABLE DWEON +B471;B471;1103 116F 11AC;B471;1103 116F 11AC; # (둱; 둱; 둱; 둱; 둱; ) HANGUL SYLLABLE DWEONJ +B472;B472;1103 116F 11AD;B472;1103 116F 11AD; # (둲; 둲; 둲; 둲; 둲; ) HANGUL SYLLABLE DWEONH +B473;B473;1103 116F 11AE;B473;1103 116F 11AE; # (둳; 둳; 둳; 둳; 둳; ) HANGUL SYLLABLE DWEOD +B474;B474;1103 116F 11AF;B474;1103 116F 11AF; # (둴; 둴; 둴; 둴; 둴; ) HANGUL SYLLABLE DWEOL +B475;B475;1103 116F 11B0;B475;1103 116F 11B0; # (둵; 둵; 둵; 둵; 둵; ) HANGUL SYLLABLE DWEOLG +B476;B476;1103 116F 11B1;B476;1103 116F 11B1; # (둶; 둶; 둶; 둶; 둶; ) HANGUL SYLLABLE DWEOLM +B477;B477;1103 116F 11B2;B477;1103 116F 11B2; # (둷; 둷; 둷; 둷; 둷; ) HANGUL SYLLABLE DWEOLB +B478;B478;1103 116F 11B3;B478;1103 116F 11B3; # (둸; 둸; 둸; 둸; 둸; ) HANGUL SYLLABLE DWEOLS +B479;B479;1103 116F 11B4;B479;1103 116F 11B4; # (둹; 둹; 둹; 둹; 둹; ) HANGUL SYLLABLE DWEOLT +B47A;B47A;1103 116F 11B5;B47A;1103 116F 11B5; # (둺; 둺; 둺; 둺; 둺; ) HANGUL SYLLABLE DWEOLP +B47B;B47B;1103 116F 11B6;B47B;1103 116F 11B6; # (둻; 둻; 둻; 둻; 둻; ) HANGUL SYLLABLE DWEOLH +B47C;B47C;1103 116F 11B7;B47C;1103 116F 11B7; # (둼; 둼; 둼; 둼; 둼; ) HANGUL SYLLABLE DWEOM +B47D;B47D;1103 116F 11B8;B47D;1103 116F 11B8; # (둽; 둽; 둽; 둽; 둽; ) HANGUL SYLLABLE DWEOB +B47E;B47E;1103 116F 11B9;B47E;1103 116F 11B9; # (둾; 둾; 둾; 둾; 둾; ) HANGUL SYLLABLE DWEOBS +B47F;B47F;1103 116F 11BA;B47F;1103 116F 11BA; # (둿; 둿; 둿; 둿; 둿; ) HANGUL SYLLABLE DWEOS +B480;B480;1103 116F 11BB;B480;1103 116F 11BB; # (뒀; 뒀; 뒀; 뒀; 뒀; ) HANGUL SYLLABLE DWEOSS +B481;B481;1103 116F 11BC;B481;1103 116F 11BC; # (뒁; 뒁; 뒁; 뒁; 뒁; ) HANGUL SYLLABLE DWEONG +B482;B482;1103 116F 11BD;B482;1103 116F 11BD; # (뒂; 뒂; 뒂; 뒂; 뒂; ) HANGUL SYLLABLE DWEOJ +B483;B483;1103 116F 11BE;B483;1103 116F 11BE; # (뒃; 뒃; 뒃; 뒃; 뒃; ) HANGUL SYLLABLE DWEOC +B484;B484;1103 116F 11BF;B484;1103 116F 11BF; # (뒄; 뒄; 뒄; 뒄; 뒄; ) HANGUL SYLLABLE DWEOK +B485;B485;1103 116F 11C0;B485;1103 116F 11C0; # (뒅; 뒅; 뒅; 뒅; 뒅; ) HANGUL SYLLABLE DWEOT +B486;B486;1103 116F 11C1;B486;1103 116F 11C1; # (뒆; 뒆; 뒆; 뒆; 뒆; ) HANGUL SYLLABLE DWEOP +B487;B487;1103 116F 11C2;B487;1103 116F 11C2; # (뒇; 뒇; 뒇; 뒇; 뒇; ) HANGUL SYLLABLE DWEOH +B488;B488;1103 1170;B488;1103 1170; # (뒈; 뒈; 뒈; 뒈; 뒈; ) HANGUL SYLLABLE DWE +B489;B489;1103 1170 11A8;B489;1103 1170 11A8; # (뒉; 뒉; 뒉; 뒉; 뒉; ) HANGUL SYLLABLE DWEG +B48A;B48A;1103 1170 11A9;B48A;1103 1170 11A9; # (뒊; 뒊; 뒊; 뒊; 뒊; ) HANGUL SYLLABLE DWEGG +B48B;B48B;1103 1170 11AA;B48B;1103 1170 11AA; # (뒋; 뒋; 뒋; 뒋; 뒋; ) HANGUL SYLLABLE DWEGS +B48C;B48C;1103 1170 11AB;B48C;1103 1170 11AB; # (뒌; 뒌; 뒌; 뒌; 뒌; ) HANGUL SYLLABLE DWEN +B48D;B48D;1103 1170 11AC;B48D;1103 1170 11AC; # (뒍; 뒍; 뒍; 뒍; 뒍; ) HANGUL SYLLABLE DWENJ +B48E;B48E;1103 1170 11AD;B48E;1103 1170 11AD; # (뒎; 뒎; 뒎; 뒎; 뒎; ) HANGUL SYLLABLE DWENH +B48F;B48F;1103 1170 11AE;B48F;1103 1170 11AE; # (뒏; 뒏; 뒏; 뒏; 뒏; ) HANGUL SYLLABLE DWED +B490;B490;1103 1170 11AF;B490;1103 1170 11AF; # (뒐; 뒐; 뒐; 뒐; 뒐; ) HANGUL SYLLABLE DWEL +B491;B491;1103 1170 11B0;B491;1103 1170 11B0; # (뒑; 뒑; 뒑; 뒑; 뒑; ) HANGUL SYLLABLE DWELG +B492;B492;1103 1170 11B1;B492;1103 1170 11B1; # (뒒; 뒒; 뒒; 뒒; 뒒; ) HANGUL SYLLABLE DWELM +B493;B493;1103 1170 11B2;B493;1103 1170 11B2; # (뒓; 뒓; 뒓; 뒓; 뒓; ) HANGUL SYLLABLE DWELB +B494;B494;1103 1170 11B3;B494;1103 1170 11B3; # (뒔; 뒔; 뒔; 뒔; 뒔; ) HANGUL SYLLABLE DWELS +B495;B495;1103 1170 11B4;B495;1103 1170 11B4; # (뒕; 뒕; 뒕; 뒕; 뒕; ) HANGUL SYLLABLE DWELT +B496;B496;1103 1170 11B5;B496;1103 1170 11B5; # (뒖; 뒖; 뒖; 뒖; 뒖; ) HANGUL SYLLABLE DWELP +B497;B497;1103 1170 11B6;B497;1103 1170 11B6; # (뒗; 뒗; 뒗; 뒗; 뒗; ) HANGUL SYLLABLE DWELH +B498;B498;1103 1170 11B7;B498;1103 1170 11B7; # (뒘; 뒘; 뒘; 뒘; 뒘; ) HANGUL SYLLABLE DWEM +B499;B499;1103 1170 11B8;B499;1103 1170 11B8; # (뒙; 뒙; 뒙; 뒙; 뒙; ) HANGUL SYLLABLE DWEB +B49A;B49A;1103 1170 11B9;B49A;1103 1170 11B9; # (뒚; 뒚; 뒚; 뒚; 뒚; ) HANGUL SYLLABLE DWEBS +B49B;B49B;1103 1170 11BA;B49B;1103 1170 11BA; # (뒛; 뒛; 뒛; 뒛; 뒛; ) HANGUL SYLLABLE DWES +B49C;B49C;1103 1170 11BB;B49C;1103 1170 11BB; # (뒜; 뒜; 뒜; 뒜; 뒜; ) HANGUL SYLLABLE DWESS +B49D;B49D;1103 1170 11BC;B49D;1103 1170 11BC; # (뒝; 뒝; 뒝; 뒝; 뒝; ) HANGUL SYLLABLE DWENG +B49E;B49E;1103 1170 11BD;B49E;1103 1170 11BD; # (뒞; 뒞; 뒞; 뒞; 뒞; ) HANGUL SYLLABLE DWEJ +B49F;B49F;1103 1170 11BE;B49F;1103 1170 11BE; # (뒟; 뒟; 뒟; 뒟; 뒟; ) HANGUL SYLLABLE DWEC +B4A0;B4A0;1103 1170 11BF;B4A0;1103 1170 11BF; # (뒠; 뒠; 뒠; 뒠; 뒠; ) HANGUL SYLLABLE DWEK +B4A1;B4A1;1103 1170 11C0;B4A1;1103 1170 11C0; # (뒡; 뒡; 뒡; 뒡; 뒡; ) HANGUL SYLLABLE DWET +B4A2;B4A2;1103 1170 11C1;B4A2;1103 1170 11C1; # (뒢; 뒢; 뒢; 뒢; 뒢; ) HANGUL SYLLABLE DWEP +B4A3;B4A3;1103 1170 11C2;B4A3;1103 1170 11C2; # (뒣; 뒣; 뒣; 뒣; 뒣; ) HANGUL SYLLABLE DWEH +B4A4;B4A4;1103 1171;B4A4;1103 1171; # (뒤; 뒤; 뒤; 뒤; 뒤; ) HANGUL SYLLABLE DWI +B4A5;B4A5;1103 1171 11A8;B4A5;1103 1171 11A8; # (뒥; 뒥; 뒥; 뒥; 뒥; ) HANGUL SYLLABLE DWIG +B4A6;B4A6;1103 1171 11A9;B4A6;1103 1171 11A9; # (뒦; 뒦; 뒦; 뒦; 뒦; ) HANGUL SYLLABLE DWIGG +B4A7;B4A7;1103 1171 11AA;B4A7;1103 1171 11AA; # (뒧; 뒧; 뒧; 뒧; 뒧; ) HANGUL SYLLABLE DWIGS +B4A8;B4A8;1103 1171 11AB;B4A8;1103 1171 11AB; # (뒨; 뒨; 뒨; 뒨; 뒨; ) HANGUL SYLLABLE DWIN +B4A9;B4A9;1103 1171 11AC;B4A9;1103 1171 11AC; # (뒩; 뒩; 뒩; 뒩; 뒩; ) HANGUL SYLLABLE DWINJ +B4AA;B4AA;1103 1171 11AD;B4AA;1103 1171 11AD; # (뒪; 뒪; 뒪; 뒪; 뒪; ) HANGUL SYLLABLE DWINH +B4AB;B4AB;1103 1171 11AE;B4AB;1103 1171 11AE; # (뒫; 뒫; 뒫; 뒫; 뒫; ) HANGUL SYLLABLE DWID +B4AC;B4AC;1103 1171 11AF;B4AC;1103 1171 11AF; # (뒬; 뒬; 뒬; 뒬; 뒬; ) HANGUL SYLLABLE DWIL +B4AD;B4AD;1103 1171 11B0;B4AD;1103 1171 11B0; # (뒭; 뒭; 뒭; 뒭; 뒭; ) HANGUL SYLLABLE DWILG +B4AE;B4AE;1103 1171 11B1;B4AE;1103 1171 11B1; # (뒮; 뒮; 뒮; 뒮; 뒮; ) HANGUL SYLLABLE DWILM +B4AF;B4AF;1103 1171 11B2;B4AF;1103 1171 11B2; # (뒯; 뒯; 뒯; 뒯; 뒯; ) HANGUL SYLLABLE DWILB +B4B0;B4B0;1103 1171 11B3;B4B0;1103 1171 11B3; # (뒰; 뒰; 뒰; 뒰; 뒰; ) HANGUL SYLLABLE DWILS +B4B1;B4B1;1103 1171 11B4;B4B1;1103 1171 11B4; # (뒱; 뒱; 뒱; 뒱; 뒱; ) HANGUL SYLLABLE DWILT +B4B2;B4B2;1103 1171 11B5;B4B2;1103 1171 11B5; # (뒲; 뒲; 뒲; 뒲; 뒲; ) HANGUL SYLLABLE DWILP +B4B3;B4B3;1103 1171 11B6;B4B3;1103 1171 11B6; # (뒳; 뒳; 뒳; 뒳; 뒳; ) HANGUL SYLLABLE DWILH +B4B4;B4B4;1103 1171 11B7;B4B4;1103 1171 11B7; # (뒴; 뒴; 뒴; 뒴; 뒴; ) HANGUL SYLLABLE DWIM +B4B5;B4B5;1103 1171 11B8;B4B5;1103 1171 11B8; # (뒵; 뒵; 뒵; 뒵; 뒵; ) HANGUL SYLLABLE DWIB +B4B6;B4B6;1103 1171 11B9;B4B6;1103 1171 11B9; # (뒶; 뒶; 뒶; 뒶; 뒶; ) HANGUL SYLLABLE DWIBS +B4B7;B4B7;1103 1171 11BA;B4B7;1103 1171 11BA; # (뒷; 뒷; 뒷; 뒷; 뒷; ) HANGUL SYLLABLE DWIS +B4B8;B4B8;1103 1171 11BB;B4B8;1103 1171 11BB; # (뒸; 뒸; 뒸; 뒸; 뒸; ) HANGUL SYLLABLE DWISS +B4B9;B4B9;1103 1171 11BC;B4B9;1103 1171 11BC; # (뒹; 뒹; 뒹; 뒹; 뒹; ) HANGUL SYLLABLE DWING +B4BA;B4BA;1103 1171 11BD;B4BA;1103 1171 11BD; # (뒺; 뒺; 뒺; 뒺; 뒺; ) HANGUL SYLLABLE DWIJ +B4BB;B4BB;1103 1171 11BE;B4BB;1103 1171 11BE; # (뒻; 뒻; 뒻; 뒻; 뒻; ) HANGUL SYLLABLE DWIC +B4BC;B4BC;1103 1171 11BF;B4BC;1103 1171 11BF; # (뒼; 뒼; 뒼; 뒼; 뒼; ) HANGUL SYLLABLE DWIK +B4BD;B4BD;1103 1171 11C0;B4BD;1103 1171 11C0; # (뒽; 뒽; 뒽; 뒽; 뒽; ) HANGUL SYLLABLE DWIT +B4BE;B4BE;1103 1171 11C1;B4BE;1103 1171 11C1; # (뒾; 뒾; 뒾; 뒾; 뒾; ) HANGUL SYLLABLE DWIP +B4BF;B4BF;1103 1171 11C2;B4BF;1103 1171 11C2; # (뒿; 뒿; 뒿; 뒿; 뒿; ) HANGUL SYLLABLE DWIH +B4C0;B4C0;1103 1172;B4C0;1103 1172; # (듀; 듀; 듀; 듀; 듀; ) HANGUL SYLLABLE DYU +B4C1;B4C1;1103 1172 11A8;B4C1;1103 1172 11A8; # (듁; 듁; 듁; 듁; 듁; ) HANGUL SYLLABLE DYUG +B4C2;B4C2;1103 1172 11A9;B4C2;1103 1172 11A9; # (듂; 듂; 듂; 듂; 듂; ) HANGUL SYLLABLE DYUGG +B4C3;B4C3;1103 1172 11AA;B4C3;1103 1172 11AA; # (듃; 듃; 듃; 듃; 듃; ) HANGUL SYLLABLE DYUGS +B4C4;B4C4;1103 1172 11AB;B4C4;1103 1172 11AB; # (듄; 듄; 듄; 듄; 듄; ) HANGUL SYLLABLE DYUN +B4C5;B4C5;1103 1172 11AC;B4C5;1103 1172 11AC; # (듅; 듅; 듅; 듅; 듅; ) HANGUL SYLLABLE DYUNJ +B4C6;B4C6;1103 1172 11AD;B4C6;1103 1172 11AD; # (듆; 듆; 듆; 듆; 듆; ) HANGUL SYLLABLE DYUNH +B4C7;B4C7;1103 1172 11AE;B4C7;1103 1172 11AE; # (듇; 듇; 듇; 듇; 듇; ) HANGUL SYLLABLE DYUD +B4C8;B4C8;1103 1172 11AF;B4C8;1103 1172 11AF; # (듈; 듈; 듈; 듈; 듈; ) HANGUL SYLLABLE DYUL +B4C9;B4C9;1103 1172 11B0;B4C9;1103 1172 11B0; # (듉; 듉; 듉; 듉; 듉; ) HANGUL SYLLABLE DYULG +B4CA;B4CA;1103 1172 11B1;B4CA;1103 1172 11B1; # (듊; 듊; 듊; 듊; 듊; ) HANGUL SYLLABLE DYULM +B4CB;B4CB;1103 1172 11B2;B4CB;1103 1172 11B2; # (듋; 듋; 듋; 듋; 듋; ) HANGUL SYLLABLE DYULB +B4CC;B4CC;1103 1172 11B3;B4CC;1103 1172 11B3; # (듌; 듌; 듌; 듌; 듌; ) HANGUL SYLLABLE DYULS +B4CD;B4CD;1103 1172 11B4;B4CD;1103 1172 11B4; # (듍; 듍; 듍; 듍; 듍; ) HANGUL SYLLABLE DYULT +B4CE;B4CE;1103 1172 11B5;B4CE;1103 1172 11B5; # (듎; 듎; 듎; 듎; 듎; ) HANGUL SYLLABLE DYULP +B4CF;B4CF;1103 1172 11B6;B4CF;1103 1172 11B6; # (듏; 듏; 듏; 듏; 듏; ) HANGUL SYLLABLE DYULH +B4D0;B4D0;1103 1172 11B7;B4D0;1103 1172 11B7; # (듐; 듐; 듐; 듐; 듐; ) HANGUL SYLLABLE DYUM +B4D1;B4D1;1103 1172 11B8;B4D1;1103 1172 11B8; # (듑; 듑; 듑; 듑; 듑; ) HANGUL SYLLABLE DYUB +B4D2;B4D2;1103 1172 11B9;B4D2;1103 1172 11B9; # (듒; 듒; 듒; 듒; 듒; ) HANGUL SYLLABLE DYUBS +B4D3;B4D3;1103 1172 11BA;B4D3;1103 1172 11BA; # (듓; 듓; 듓; 듓; 듓; ) HANGUL SYLLABLE DYUS +B4D4;B4D4;1103 1172 11BB;B4D4;1103 1172 11BB; # (듔; 듔; 듔; 듔; 듔; ) HANGUL SYLLABLE DYUSS +B4D5;B4D5;1103 1172 11BC;B4D5;1103 1172 11BC; # (듕; 듕; 듕; 듕; 듕; ) HANGUL SYLLABLE DYUNG +B4D6;B4D6;1103 1172 11BD;B4D6;1103 1172 11BD; # (듖; 듖; 듖; 듖; 듖; ) HANGUL SYLLABLE DYUJ +B4D7;B4D7;1103 1172 11BE;B4D7;1103 1172 11BE; # (듗; 듗; 듗; 듗; 듗; ) HANGUL SYLLABLE DYUC +B4D8;B4D8;1103 1172 11BF;B4D8;1103 1172 11BF; # (듘; 듘; 듘; 듘; 듘; ) HANGUL SYLLABLE DYUK +B4D9;B4D9;1103 1172 11C0;B4D9;1103 1172 11C0; # (듙; 듙; 듙; 듙; 듙; ) HANGUL SYLLABLE DYUT +B4DA;B4DA;1103 1172 11C1;B4DA;1103 1172 11C1; # (듚; 듚; 듚; 듚; 듚; ) HANGUL SYLLABLE DYUP +B4DB;B4DB;1103 1172 11C2;B4DB;1103 1172 11C2; # (듛; 듛; 듛; 듛; 듛; ) HANGUL SYLLABLE DYUH +B4DC;B4DC;1103 1173;B4DC;1103 1173; # (드; 드; 드; 드; 드; ) HANGUL SYLLABLE DEU +B4DD;B4DD;1103 1173 11A8;B4DD;1103 1173 11A8; # (득; 득; 득; 득; 득; ) HANGUL SYLLABLE DEUG +B4DE;B4DE;1103 1173 11A9;B4DE;1103 1173 11A9; # (듞; 듞; 듞; 듞; 듞; ) HANGUL SYLLABLE DEUGG +B4DF;B4DF;1103 1173 11AA;B4DF;1103 1173 11AA; # (듟; 듟; 듟; 듟; 듟; ) HANGUL SYLLABLE DEUGS +B4E0;B4E0;1103 1173 11AB;B4E0;1103 1173 11AB; # (든; 든; 든; 든; 든; ) HANGUL SYLLABLE DEUN +B4E1;B4E1;1103 1173 11AC;B4E1;1103 1173 11AC; # (듡; 듡; 듡; 듡; 듡; ) HANGUL SYLLABLE DEUNJ +B4E2;B4E2;1103 1173 11AD;B4E2;1103 1173 11AD; # (듢; 듢; 듢; 듢; 듢; ) HANGUL SYLLABLE DEUNH +B4E3;B4E3;1103 1173 11AE;B4E3;1103 1173 11AE; # (듣; 듣; 듣; 듣; 듣; ) HANGUL SYLLABLE DEUD +B4E4;B4E4;1103 1173 11AF;B4E4;1103 1173 11AF; # (들; 들; 들; 들; 들; ) HANGUL SYLLABLE DEUL +B4E5;B4E5;1103 1173 11B0;B4E5;1103 1173 11B0; # (듥; 듥; 듥; 듥; 듥; ) HANGUL SYLLABLE DEULG +B4E6;B4E6;1103 1173 11B1;B4E6;1103 1173 11B1; # (듦; 듦; 듦; 듦; 듦; ) HANGUL SYLLABLE DEULM +B4E7;B4E7;1103 1173 11B2;B4E7;1103 1173 11B2; # (듧; 듧; 듧; 듧; 듧; ) HANGUL SYLLABLE DEULB +B4E8;B4E8;1103 1173 11B3;B4E8;1103 1173 11B3; # (듨; 듨; 듨; 듨; 듨; ) HANGUL SYLLABLE DEULS +B4E9;B4E9;1103 1173 11B4;B4E9;1103 1173 11B4; # (듩; 듩; 듩; 듩; 듩; ) HANGUL SYLLABLE DEULT +B4EA;B4EA;1103 1173 11B5;B4EA;1103 1173 11B5; # (듪; 듪; 듪; 듪; 듪; ) HANGUL SYLLABLE DEULP +B4EB;B4EB;1103 1173 11B6;B4EB;1103 1173 11B6; # (듫; 듫; 듫; 듫; 듫; ) HANGUL SYLLABLE DEULH +B4EC;B4EC;1103 1173 11B7;B4EC;1103 1173 11B7; # (듬; 듬; 듬; 듬; 듬; ) HANGUL SYLLABLE DEUM +B4ED;B4ED;1103 1173 11B8;B4ED;1103 1173 11B8; # (듭; 듭; 듭; 듭; 듭; ) HANGUL SYLLABLE DEUB +B4EE;B4EE;1103 1173 11B9;B4EE;1103 1173 11B9; # (듮; 듮; 듮; 듮; 듮; ) HANGUL SYLLABLE DEUBS +B4EF;B4EF;1103 1173 11BA;B4EF;1103 1173 11BA; # (듯; 듯; 듯; 듯; 듯; ) HANGUL SYLLABLE DEUS +B4F0;B4F0;1103 1173 11BB;B4F0;1103 1173 11BB; # (듰; 듰; 듰; 듰; 듰; ) HANGUL SYLLABLE DEUSS +B4F1;B4F1;1103 1173 11BC;B4F1;1103 1173 11BC; # (등; 등; 등; 등; 등; ) HANGUL SYLLABLE DEUNG +B4F2;B4F2;1103 1173 11BD;B4F2;1103 1173 11BD; # (듲; 듲; 듲; 듲; 듲; ) HANGUL SYLLABLE DEUJ +B4F3;B4F3;1103 1173 11BE;B4F3;1103 1173 11BE; # (듳; 듳; 듳; 듳; 듳; ) HANGUL SYLLABLE DEUC +B4F4;B4F4;1103 1173 11BF;B4F4;1103 1173 11BF; # (듴; 듴; 듴; 듴; 듴; ) HANGUL SYLLABLE DEUK +B4F5;B4F5;1103 1173 11C0;B4F5;1103 1173 11C0; # (듵; 듵; 듵; 듵; 듵; ) HANGUL SYLLABLE DEUT +B4F6;B4F6;1103 1173 11C1;B4F6;1103 1173 11C1; # (듶; 듶; 듶; 듶; 듶; ) HANGUL SYLLABLE DEUP +B4F7;B4F7;1103 1173 11C2;B4F7;1103 1173 11C2; # (듷; 듷; 듷; 듷; 듷; ) HANGUL SYLLABLE DEUH +B4F8;B4F8;1103 1174;B4F8;1103 1174; # (듸; 듸; 듸; 듸; 듸; ) HANGUL SYLLABLE DYI +B4F9;B4F9;1103 1174 11A8;B4F9;1103 1174 11A8; # (듹; 듹; 듹; 듹; 듹; ) HANGUL SYLLABLE DYIG +B4FA;B4FA;1103 1174 11A9;B4FA;1103 1174 11A9; # (듺; 듺; 듺; 듺; 듺; ) HANGUL SYLLABLE DYIGG +B4FB;B4FB;1103 1174 11AA;B4FB;1103 1174 11AA; # (듻; 듻; 듻; 듻; 듻; ) HANGUL SYLLABLE DYIGS +B4FC;B4FC;1103 1174 11AB;B4FC;1103 1174 11AB; # (듼; 듼; 듼; 듼; 듼; ) HANGUL SYLLABLE DYIN +B4FD;B4FD;1103 1174 11AC;B4FD;1103 1174 11AC; # (듽; 듽; 듽; 듽; 듽; ) HANGUL SYLLABLE DYINJ +B4FE;B4FE;1103 1174 11AD;B4FE;1103 1174 11AD; # (듾; 듾; 듾; 듾; 듾; ) HANGUL SYLLABLE DYINH +B4FF;B4FF;1103 1174 11AE;B4FF;1103 1174 11AE; # (듿; 듿; 듿; 듿; 듿; ) HANGUL SYLLABLE DYID +B500;B500;1103 1174 11AF;B500;1103 1174 11AF; # (딀; 딀; 딀; 딀; 딀; ) HANGUL SYLLABLE DYIL +B501;B501;1103 1174 11B0;B501;1103 1174 11B0; # (딁; 딁; 딁; 딁; 딁; ) HANGUL SYLLABLE DYILG +B502;B502;1103 1174 11B1;B502;1103 1174 11B1; # (딂; 딂; 딂; 딂; 딂; ) HANGUL SYLLABLE DYILM +B503;B503;1103 1174 11B2;B503;1103 1174 11B2; # (딃; 딃; 딃; 딃; 딃; ) HANGUL SYLLABLE DYILB +B504;B504;1103 1174 11B3;B504;1103 1174 11B3; # (딄; 딄; 딄; 딄; 딄; ) HANGUL SYLLABLE DYILS +B505;B505;1103 1174 11B4;B505;1103 1174 11B4; # (딅; 딅; 딅; 딅; 딅; ) HANGUL SYLLABLE DYILT +B506;B506;1103 1174 11B5;B506;1103 1174 11B5; # (딆; 딆; 딆; 딆; 딆; ) HANGUL SYLLABLE DYILP +B507;B507;1103 1174 11B6;B507;1103 1174 11B6; # (딇; 딇; 딇; 딇; 딇; ) HANGUL SYLLABLE DYILH +B508;B508;1103 1174 11B7;B508;1103 1174 11B7; # (딈; 딈; 딈; 딈; 딈; ) HANGUL SYLLABLE DYIM +B509;B509;1103 1174 11B8;B509;1103 1174 11B8; # (딉; 딉; 딉; 딉; 딉; ) HANGUL SYLLABLE DYIB +B50A;B50A;1103 1174 11B9;B50A;1103 1174 11B9; # (딊; 딊; 딊; 딊; 딊; ) HANGUL SYLLABLE DYIBS +B50B;B50B;1103 1174 11BA;B50B;1103 1174 11BA; # (딋; 딋; 딋; 딋; 딋; ) HANGUL SYLLABLE DYIS +B50C;B50C;1103 1174 11BB;B50C;1103 1174 11BB; # (딌; 딌; 딌; 딌; 딌; ) HANGUL SYLLABLE DYISS +B50D;B50D;1103 1174 11BC;B50D;1103 1174 11BC; # (딍; 딍; 딍; 딍; 딍; ) HANGUL SYLLABLE DYING +B50E;B50E;1103 1174 11BD;B50E;1103 1174 11BD; # (딎; 딎; 딎; 딎; 딎; ) HANGUL SYLLABLE DYIJ +B50F;B50F;1103 1174 11BE;B50F;1103 1174 11BE; # (딏; 딏; 딏; 딏; 딏; ) HANGUL SYLLABLE DYIC +B510;B510;1103 1174 11BF;B510;1103 1174 11BF; # (딐; 딐; 딐; 딐; 딐; ) HANGUL SYLLABLE DYIK +B511;B511;1103 1174 11C0;B511;1103 1174 11C0; # (딑; 딑; 딑; 딑; 딑; ) HANGUL SYLLABLE DYIT +B512;B512;1103 1174 11C1;B512;1103 1174 11C1; # (딒; 딒; 딒; 딒; 딒; ) HANGUL SYLLABLE DYIP +B513;B513;1103 1174 11C2;B513;1103 1174 11C2; # (딓; 딓; 딓; 딓; 딓; ) HANGUL SYLLABLE DYIH +B514;B514;1103 1175;B514;1103 1175; # (디; 디; 디; 디; 디; ) HANGUL SYLLABLE DI +B515;B515;1103 1175 11A8;B515;1103 1175 11A8; # (딕; 딕; 딕; 딕; 딕; ) HANGUL SYLLABLE DIG +B516;B516;1103 1175 11A9;B516;1103 1175 11A9; # (딖; 딖; 딖; 딖; 딖; ) HANGUL SYLLABLE DIGG +B517;B517;1103 1175 11AA;B517;1103 1175 11AA; # (딗; 딗; 딗; 딗; 딗; ) HANGUL SYLLABLE DIGS +B518;B518;1103 1175 11AB;B518;1103 1175 11AB; # (딘; 딘; 딘; 딘; 딘; ) HANGUL SYLLABLE DIN +B519;B519;1103 1175 11AC;B519;1103 1175 11AC; # (딙; 딙; 딙; 딙; 딙; ) HANGUL SYLLABLE DINJ +B51A;B51A;1103 1175 11AD;B51A;1103 1175 11AD; # (딚; 딚; 딚; 딚; 딚; ) HANGUL SYLLABLE DINH +B51B;B51B;1103 1175 11AE;B51B;1103 1175 11AE; # (딛; 딛; 딛; 딛; 딛; ) HANGUL SYLLABLE DID +B51C;B51C;1103 1175 11AF;B51C;1103 1175 11AF; # (딜; 딜; 딜; 딜; 딜; ) HANGUL SYLLABLE DIL +B51D;B51D;1103 1175 11B0;B51D;1103 1175 11B0; # (딝; 딝; 딝; 딝; 딝; ) HANGUL SYLLABLE DILG +B51E;B51E;1103 1175 11B1;B51E;1103 1175 11B1; # (딞; 딞; 딞; 딞; 딞; ) HANGUL SYLLABLE DILM +B51F;B51F;1103 1175 11B2;B51F;1103 1175 11B2; # (딟; 딟; 딟; 딟; 딟; ) HANGUL SYLLABLE DILB +B520;B520;1103 1175 11B3;B520;1103 1175 11B3; # (딠; 딠; 딠; 딠; 딠; ) HANGUL SYLLABLE DILS +B521;B521;1103 1175 11B4;B521;1103 1175 11B4; # (딡; 딡; 딡; 딡; 딡; ) HANGUL SYLLABLE DILT +B522;B522;1103 1175 11B5;B522;1103 1175 11B5; # (딢; 딢; 딢; 딢; 딢; ) HANGUL SYLLABLE DILP +B523;B523;1103 1175 11B6;B523;1103 1175 11B6; # (딣; 딣; 딣; 딣; 딣; ) HANGUL SYLLABLE DILH +B524;B524;1103 1175 11B7;B524;1103 1175 11B7; # (딤; 딤; 딤; 딤; 딤; ) HANGUL SYLLABLE DIM +B525;B525;1103 1175 11B8;B525;1103 1175 11B8; # (딥; 딥; 딥; 딥; 딥; ) HANGUL SYLLABLE DIB +B526;B526;1103 1175 11B9;B526;1103 1175 11B9; # (딦; 딦; 딦; 딦; 딦; ) HANGUL SYLLABLE DIBS +B527;B527;1103 1175 11BA;B527;1103 1175 11BA; # (딧; 딧; 딧; 딧; 딧; ) HANGUL SYLLABLE DIS +B528;B528;1103 1175 11BB;B528;1103 1175 11BB; # (딨; 딨; 딨; 딨; 딨; ) HANGUL SYLLABLE DISS +B529;B529;1103 1175 11BC;B529;1103 1175 11BC; # (딩; 딩; 딩; 딩; 딩; ) HANGUL SYLLABLE DING +B52A;B52A;1103 1175 11BD;B52A;1103 1175 11BD; # (딪; 딪; 딪; 딪; 딪; ) HANGUL SYLLABLE DIJ +B52B;B52B;1103 1175 11BE;B52B;1103 1175 11BE; # (딫; 딫; 딫; 딫; 딫; ) HANGUL SYLLABLE DIC +B52C;B52C;1103 1175 11BF;B52C;1103 1175 11BF; # (딬; 딬; 딬; 딬; 딬; ) HANGUL SYLLABLE DIK +B52D;B52D;1103 1175 11C0;B52D;1103 1175 11C0; # (딭; 딭; 딭; 딭; 딭; ) HANGUL SYLLABLE DIT +B52E;B52E;1103 1175 11C1;B52E;1103 1175 11C1; # (딮; 딮; 딮; 딮; 딮; ) HANGUL SYLLABLE DIP +B52F;B52F;1103 1175 11C2;B52F;1103 1175 11C2; # (딯; 딯; 딯; 딯; 딯; ) HANGUL SYLLABLE DIH +B530;B530;1104 1161;B530;1104 1161; # (따; 따; 따; 따; 따; ) HANGUL SYLLABLE DDA +B531;B531;1104 1161 11A8;B531;1104 1161 11A8; # (딱; 딱; 딱; 딱; 딱; ) HANGUL SYLLABLE DDAG +B532;B532;1104 1161 11A9;B532;1104 1161 11A9; # (딲; 딲; 딲; 딲; 딲; ) HANGUL SYLLABLE DDAGG +B533;B533;1104 1161 11AA;B533;1104 1161 11AA; # (딳; 딳; 딳; 딳; 딳; ) HANGUL SYLLABLE DDAGS +B534;B534;1104 1161 11AB;B534;1104 1161 11AB; # (딴; 딴; 딴; 딴; 딴; ) HANGUL SYLLABLE DDAN +B535;B535;1104 1161 11AC;B535;1104 1161 11AC; # (딵; 딵; 딵; 딵; 딵; ) HANGUL SYLLABLE DDANJ +B536;B536;1104 1161 11AD;B536;1104 1161 11AD; # (딶; 딶; 딶; 딶; 딶; ) HANGUL SYLLABLE DDANH +B537;B537;1104 1161 11AE;B537;1104 1161 11AE; # (딷; 딷; 딷; 딷; 딷; ) HANGUL SYLLABLE DDAD +B538;B538;1104 1161 11AF;B538;1104 1161 11AF; # (딸; 딸; 딸; 딸; 딸; ) HANGUL SYLLABLE DDAL +B539;B539;1104 1161 11B0;B539;1104 1161 11B0; # (딹; 딹; 딹; 딹; 딹; ) HANGUL SYLLABLE DDALG +B53A;B53A;1104 1161 11B1;B53A;1104 1161 11B1; # (딺; 딺; 딺; 딺; 딺; ) HANGUL SYLLABLE DDALM +B53B;B53B;1104 1161 11B2;B53B;1104 1161 11B2; # (딻; 딻; 딻; 딻; 딻; ) HANGUL SYLLABLE DDALB +B53C;B53C;1104 1161 11B3;B53C;1104 1161 11B3; # (딼; 딼; 딼; 딼; 딼; ) HANGUL SYLLABLE DDALS +B53D;B53D;1104 1161 11B4;B53D;1104 1161 11B4; # (딽; 딽; 딽; 딽; 딽; ) HANGUL SYLLABLE DDALT +B53E;B53E;1104 1161 11B5;B53E;1104 1161 11B5; # (딾; 딾; 딾; 딾; 딾; ) HANGUL SYLLABLE DDALP +B53F;B53F;1104 1161 11B6;B53F;1104 1161 11B6; # (딿; 딿; 딿; 딿; 딿; ) HANGUL SYLLABLE DDALH +B540;B540;1104 1161 11B7;B540;1104 1161 11B7; # (땀; 땀; 땀; 땀; 땀; ) HANGUL SYLLABLE DDAM +B541;B541;1104 1161 11B8;B541;1104 1161 11B8; # (땁; 땁; 땁; 땁; 땁; ) HANGUL SYLLABLE DDAB +B542;B542;1104 1161 11B9;B542;1104 1161 11B9; # (땂; 땂; 땂; 땂; 땂; ) HANGUL SYLLABLE DDABS +B543;B543;1104 1161 11BA;B543;1104 1161 11BA; # (땃; 땃; 땃; 땃; 땃; ) HANGUL SYLLABLE DDAS +B544;B544;1104 1161 11BB;B544;1104 1161 11BB; # (땄; 땄; 땄; 땄; 땄; ) HANGUL SYLLABLE DDASS +B545;B545;1104 1161 11BC;B545;1104 1161 11BC; # (땅; 땅; 땅; 땅; 땅; ) HANGUL SYLLABLE DDANG +B546;B546;1104 1161 11BD;B546;1104 1161 11BD; # (땆; 땆; 땆; 땆; 땆; ) HANGUL SYLLABLE DDAJ +B547;B547;1104 1161 11BE;B547;1104 1161 11BE; # (땇; 땇; 땇; 땇; 땇; ) HANGUL SYLLABLE DDAC +B548;B548;1104 1161 11BF;B548;1104 1161 11BF; # (땈; 땈; 땈; 땈; 땈; ) HANGUL SYLLABLE DDAK +B549;B549;1104 1161 11C0;B549;1104 1161 11C0; # (땉; 땉; 땉; 땉; 땉; ) HANGUL SYLLABLE DDAT +B54A;B54A;1104 1161 11C1;B54A;1104 1161 11C1; # (땊; 땊; 땊; 땊; 땊; ) HANGUL SYLLABLE DDAP +B54B;B54B;1104 1161 11C2;B54B;1104 1161 11C2; # (땋; 땋; 땋; 땋; 땋; ) HANGUL SYLLABLE DDAH +B54C;B54C;1104 1162;B54C;1104 1162; # (때; 때; 때; 때; 때; ) HANGUL SYLLABLE DDAE +B54D;B54D;1104 1162 11A8;B54D;1104 1162 11A8; # (땍; 땍; 땍; 땍; 땍; ) HANGUL SYLLABLE DDAEG +B54E;B54E;1104 1162 11A9;B54E;1104 1162 11A9; # (땎; 땎; 땎; 땎; 땎; ) HANGUL SYLLABLE DDAEGG +B54F;B54F;1104 1162 11AA;B54F;1104 1162 11AA; # (땏; 땏; 땏; 땏; 땏; ) HANGUL SYLLABLE DDAEGS +B550;B550;1104 1162 11AB;B550;1104 1162 11AB; # (땐; 땐; 땐; 땐; 땐; ) HANGUL SYLLABLE DDAEN +B551;B551;1104 1162 11AC;B551;1104 1162 11AC; # (땑; 땑; 땑; 땑; 땑; ) HANGUL SYLLABLE DDAENJ +B552;B552;1104 1162 11AD;B552;1104 1162 11AD; # (땒; 땒; 땒; 땒; 땒; ) HANGUL SYLLABLE DDAENH +B553;B553;1104 1162 11AE;B553;1104 1162 11AE; # (땓; 땓; 땓; 땓; 땓; ) HANGUL SYLLABLE DDAED +B554;B554;1104 1162 11AF;B554;1104 1162 11AF; # (땔; 땔; 땔; 땔; 땔; ) HANGUL SYLLABLE DDAEL +B555;B555;1104 1162 11B0;B555;1104 1162 11B0; # (땕; 땕; 땕; 땕; 땕; ) HANGUL SYLLABLE DDAELG +B556;B556;1104 1162 11B1;B556;1104 1162 11B1; # (땖; 땖; 땖; 땖; 땖; ) HANGUL SYLLABLE DDAELM +B557;B557;1104 1162 11B2;B557;1104 1162 11B2; # (땗; 땗; 땗; 땗; 땗; ) HANGUL SYLLABLE DDAELB +B558;B558;1104 1162 11B3;B558;1104 1162 11B3; # (땘; 땘; 땘; 땘; 땘; ) HANGUL SYLLABLE DDAELS +B559;B559;1104 1162 11B4;B559;1104 1162 11B4; # (땙; 땙; 땙; 땙; 땙; ) HANGUL SYLLABLE DDAELT +B55A;B55A;1104 1162 11B5;B55A;1104 1162 11B5; # (땚; 땚; 땚; 땚; 땚; ) HANGUL SYLLABLE DDAELP +B55B;B55B;1104 1162 11B6;B55B;1104 1162 11B6; # (땛; 땛; 땛; 땛; 땛; ) HANGUL SYLLABLE DDAELH +B55C;B55C;1104 1162 11B7;B55C;1104 1162 11B7; # (땜; 땜; 땜; 땜; 땜; ) HANGUL SYLLABLE DDAEM +B55D;B55D;1104 1162 11B8;B55D;1104 1162 11B8; # (땝; 땝; 땝; 땝; 땝; ) HANGUL SYLLABLE DDAEB +B55E;B55E;1104 1162 11B9;B55E;1104 1162 11B9; # (땞; 땞; 땞; 땞; 땞; ) HANGUL SYLLABLE DDAEBS +B55F;B55F;1104 1162 11BA;B55F;1104 1162 11BA; # (땟; 땟; 땟; 땟; 땟; ) HANGUL SYLLABLE DDAES +B560;B560;1104 1162 11BB;B560;1104 1162 11BB; # (땠; 땠; 땠; 땠; 땠; ) HANGUL SYLLABLE DDAESS +B561;B561;1104 1162 11BC;B561;1104 1162 11BC; # (땡; 땡; 땡; 땡; 땡; ) HANGUL SYLLABLE DDAENG +B562;B562;1104 1162 11BD;B562;1104 1162 11BD; # (땢; 땢; 땢; 땢; 땢; ) HANGUL SYLLABLE DDAEJ +B563;B563;1104 1162 11BE;B563;1104 1162 11BE; # (땣; 땣; 땣; 땣; 땣; ) HANGUL SYLLABLE DDAEC +B564;B564;1104 1162 11BF;B564;1104 1162 11BF; # (땤; 땤; 땤; 땤; 땤; ) HANGUL SYLLABLE DDAEK +B565;B565;1104 1162 11C0;B565;1104 1162 11C0; # (땥; 땥; 땥; 땥; 땥; ) HANGUL SYLLABLE DDAET +B566;B566;1104 1162 11C1;B566;1104 1162 11C1; # (땦; 땦; 땦; 땦; 땦; ) HANGUL SYLLABLE DDAEP +B567;B567;1104 1162 11C2;B567;1104 1162 11C2; # (땧; 땧; 땧; 땧; 땧; ) HANGUL SYLLABLE DDAEH +B568;B568;1104 1163;B568;1104 1163; # (땨; 땨; 땨; 땨; 땨; ) HANGUL SYLLABLE DDYA +B569;B569;1104 1163 11A8;B569;1104 1163 11A8; # (땩; 땩; 땩; 땩; 땩; ) HANGUL SYLLABLE DDYAG +B56A;B56A;1104 1163 11A9;B56A;1104 1163 11A9; # (땪; 땪; 땪; 땪; 땪; ) HANGUL SYLLABLE DDYAGG +B56B;B56B;1104 1163 11AA;B56B;1104 1163 11AA; # (땫; 땫; 땫; 땫; 땫; ) HANGUL SYLLABLE DDYAGS +B56C;B56C;1104 1163 11AB;B56C;1104 1163 11AB; # (땬; 땬; 땬; 땬; 땬; ) HANGUL SYLLABLE DDYAN +B56D;B56D;1104 1163 11AC;B56D;1104 1163 11AC; # (땭; 땭; 땭; 땭; 땭; ) HANGUL SYLLABLE DDYANJ +B56E;B56E;1104 1163 11AD;B56E;1104 1163 11AD; # (땮; 땮; 땮; 땮; 땮; ) HANGUL SYLLABLE DDYANH +B56F;B56F;1104 1163 11AE;B56F;1104 1163 11AE; # (땯; 땯; 땯; 땯; 땯; ) HANGUL SYLLABLE DDYAD +B570;B570;1104 1163 11AF;B570;1104 1163 11AF; # (땰; 땰; 땰; 땰; 땰; ) HANGUL SYLLABLE DDYAL +B571;B571;1104 1163 11B0;B571;1104 1163 11B0; # (땱; 땱; 땱; 땱; 땱; ) HANGUL SYLLABLE DDYALG +B572;B572;1104 1163 11B1;B572;1104 1163 11B1; # (땲; 땲; 땲; 땲; 땲; ) HANGUL SYLLABLE DDYALM +B573;B573;1104 1163 11B2;B573;1104 1163 11B2; # (땳; 땳; 땳; 땳; 땳; ) HANGUL SYLLABLE DDYALB +B574;B574;1104 1163 11B3;B574;1104 1163 11B3; # (땴; 땴; 땴; 땴; 땴; ) HANGUL SYLLABLE DDYALS +B575;B575;1104 1163 11B4;B575;1104 1163 11B4; # (땵; 땵; 땵; 땵; 땵; ) HANGUL SYLLABLE DDYALT +B576;B576;1104 1163 11B5;B576;1104 1163 11B5; # (땶; 땶; 땶; 땶; 땶; ) HANGUL SYLLABLE DDYALP +B577;B577;1104 1163 11B6;B577;1104 1163 11B6; # (땷; 땷; 땷; 땷; 땷; ) HANGUL SYLLABLE DDYALH +B578;B578;1104 1163 11B7;B578;1104 1163 11B7; # (땸; 땸; 땸; 땸; 땸; ) HANGUL SYLLABLE DDYAM +B579;B579;1104 1163 11B8;B579;1104 1163 11B8; # (땹; 땹; 땹; 땹; 땹; ) HANGUL SYLLABLE DDYAB +B57A;B57A;1104 1163 11B9;B57A;1104 1163 11B9; # (땺; 땺; 땺; 땺; 땺; ) HANGUL SYLLABLE DDYABS +B57B;B57B;1104 1163 11BA;B57B;1104 1163 11BA; # (땻; 땻; 땻; 땻; 땻; ) HANGUL SYLLABLE DDYAS +B57C;B57C;1104 1163 11BB;B57C;1104 1163 11BB; # (땼; 땼; 땼; 땼; 땼; ) HANGUL SYLLABLE DDYASS +B57D;B57D;1104 1163 11BC;B57D;1104 1163 11BC; # (땽; 땽; 땽; 땽; 땽; ) HANGUL SYLLABLE DDYANG +B57E;B57E;1104 1163 11BD;B57E;1104 1163 11BD; # (땾; 땾; 땾; 땾; 땾; ) HANGUL SYLLABLE DDYAJ +B57F;B57F;1104 1163 11BE;B57F;1104 1163 11BE; # (땿; 땿; 땿; 땿; 땿; ) HANGUL SYLLABLE DDYAC +B580;B580;1104 1163 11BF;B580;1104 1163 11BF; # (떀; 떀; 떀; 떀; 떀; ) HANGUL SYLLABLE DDYAK +B581;B581;1104 1163 11C0;B581;1104 1163 11C0; # (떁; 떁; 떁; 떁; 떁; ) HANGUL SYLLABLE DDYAT +B582;B582;1104 1163 11C1;B582;1104 1163 11C1; # (떂; 떂; 떂; 떂; 떂; ) HANGUL SYLLABLE DDYAP +B583;B583;1104 1163 11C2;B583;1104 1163 11C2; # (떃; 떃; 떃; 떃; 떃; ) HANGUL SYLLABLE DDYAH +B584;B584;1104 1164;B584;1104 1164; # (떄; 떄; 떄; 떄; 떄; ) HANGUL SYLLABLE DDYAE +B585;B585;1104 1164 11A8;B585;1104 1164 11A8; # (떅; 떅; 떅; 떅; 떅; ) HANGUL SYLLABLE DDYAEG +B586;B586;1104 1164 11A9;B586;1104 1164 11A9; # (떆; 떆; 떆; 떆; 떆; ) HANGUL SYLLABLE DDYAEGG +B587;B587;1104 1164 11AA;B587;1104 1164 11AA; # (떇; 떇; 떇; 떇; 떇; ) HANGUL SYLLABLE DDYAEGS +B588;B588;1104 1164 11AB;B588;1104 1164 11AB; # (떈; 떈; 떈; 떈; 떈; ) HANGUL SYLLABLE DDYAEN +B589;B589;1104 1164 11AC;B589;1104 1164 11AC; # (떉; 떉; 떉; 떉; 떉; ) HANGUL SYLLABLE DDYAENJ +B58A;B58A;1104 1164 11AD;B58A;1104 1164 11AD; # (떊; 떊; 떊; 떊; 떊; ) HANGUL SYLLABLE DDYAENH +B58B;B58B;1104 1164 11AE;B58B;1104 1164 11AE; # (떋; 떋; 떋; 떋; 떋; ) HANGUL SYLLABLE DDYAED +B58C;B58C;1104 1164 11AF;B58C;1104 1164 11AF; # (떌; 떌; 떌; 떌; 떌; ) HANGUL SYLLABLE DDYAEL +B58D;B58D;1104 1164 11B0;B58D;1104 1164 11B0; # (떍; 떍; 떍; 떍; 떍; ) HANGUL SYLLABLE DDYAELG +B58E;B58E;1104 1164 11B1;B58E;1104 1164 11B1; # (떎; 떎; 떎; 떎; 떎; ) HANGUL SYLLABLE DDYAELM +B58F;B58F;1104 1164 11B2;B58F;1104 1164 11B2; # (떏; 떏; 떏; 떏; 떏; ) HANGUL SYLLABLE DDYAELB +B590;B590;1104 1164 11B3;B590;1104 1164 11B3; # (떐; 떐; 떐; 떐; 떐; ) HANGUL SYLLABLE DDYAELS +B591;B591;1104 1164 11B4;B591;1104 1164 11B4; # (떑; 떑; 떑; 떑; 떑; ) HANGUL SYLLABLE DDYAELT +B592;B592;1104 1164 11B5;B592;1104 1164 11B5; # (떒; 떒; 떒; 떒; 떒; ) HANGUL SYLLABLE DDYAELP +B593;B593;1104 1164 11B6;B593;1104 1164 11B6; # (떓; 떓; 떓; 떓; 떓; ) HANGUL SYLLABLE DDYAELH +B594;B594;1104 1164 11B7;B594;1104 1164 11B7; # (떔; 떔; 떔; 떔; 떔; ) HANGUL SYLLABLE DDYAEM +B595;B595;1104 1164 11B8;B595;1104 1164 11B8; # (떕; 떕; 떕; 떕; 떕; ) HANGUL SYLLABLE DDYAEB +B596;B596;1104 1164 11B9;B596;1104 1164 11B9; # (떖; 떖; 떖; 떖; 떖; ) HANGUL SYLLABLE DDYAEBS +B597;B597;1104 1164 11BA;B597;1104 1164 11BA; # (떗; 떗; 떗; 떗; 떗; ) HANGUL SYLLABLE DDYAES +B598;B598;1104 1164 11BB;B598;1104 1164 11BB; # (떘; 떘; 떘; 떘; 떘; ) HANGUL SYLLABLE DDYAESS +B599;B599;1104 1164 11BC;B599;1104 1164 11BC; # (떙; 떙; 떙; 떙; 떙; ) HANGUL SYLLABLE DDYAENG +B59A;B59A;1104 1164 11BD;B59A;1104 1164 11BD; # (떚; 떚; 떚; 떚; 떚; ) HANGUL SYLLABLE DDYAEJ +B59B;B59B;1104 1164 11BE;B59B;1104 1164 11BE; # (떛; 떛; 떛; 떛; 떛; ) HANGUL SYLLABLE DDYAEC +B59C;B59C;1104 1164 11BF;B59C;1104 1164 11BF; # (떜; 떜; 떜; 떜; 떜; ) HANGUL SYLLABLE DDYAEK +B59D;B59D;1104 1164 11C0;B59D;1104 1164 11C0; # (떝; 떝; 떝; 떝; 떝; ) HANGUL SYLLABLE DDYAET +B59E;B59E;1104 1164 11C1;B59E;1104 1164 11C1; # (떞; 떞; 떞; 떞; 떞; ) HANGUL SYLLABLE DDYAEP +B59F;B59F;1104 1164 11C2;B59F;1104 1164 11C2; # (떟; 떟; 떟; 떟; 떟; ) HANGUL SYLLABLE DDYAEH +B5A0;B5A0;1104 1165;B5A0;1104 1165; # (떠; 떠; 떠; 떠; 떠; ) HANGUL SYLLABLE DDEO +B5A1;B5A1;1104 1165 11A8;B5A1;1104 1165 11A8; # (떡; 떡; 떡; 떡; 떡; ) HANGUL SYLLABLE DDEOG +B5A2;B5A2;1104 1165 11A9;B5A2;1104 1165 11A9; # (떢; 떢; 떢; 떢; 떢; ) HANGUL SYLLABLE DDEOGG +B5A3;B5A3;1104 1165 11AA;B5A3;1104 1165 11AA; # (떣; 떣; 떣; 떣; 떣; ) HANGUL SYLLABLE DDEOGS +B5A4;B5A4;1104 1165 11AB;B5A4;1104 1165 11AB; # (떤; 떤; 떤; 떤; 떤; ) HANGUL SYLLABLE DDEON +B5A5;B5A5;1104 1165 11AC;B5A5;1104 1165 11AC; # (떥; 떥; 떥; 떥; 떥; ) HANGUL SYLLABLE DDEONJ +B5A6;B5A6;1104 1165 11AD;B5A6;1104 1165 11AD; # (떦; 떦; 떦; 떦; 떦; ) HANGUL SYLLABLE DDEONH +B5A7;B5A7;1104 1165 11AE;B5A7;1104 1165 11AE; # (떧; 떧; 떧; 떧; 떧; ) HANGUL SYLLABLE DDEOD +B5A8;B5A8;1104 1165 11AF;B5A8;1104 1165 11AF; # (떨; 떨; 떨; 떨; 떨; ) HANGUL SYLLABLE DDEOL +B5A9;B5A9;1104 1165 11B0;B5A9;1104 1165 11B0; # (떩; 떩; 떩; 떩; 떩; ) HANGUL SYLLABLE DDEOLG +B5AA;B5AA;1104 1165 11B1;B5AA;1104 1165 11B1; # (떪; 떪; 떪; 떪; 떪; ) HANGUL SYLLABLE DDEOLM +B5AB;B5AB;1104 1165 11B2;B5AB;1104 1165 11B2; # (떫; 떫; 떫; 떫; 떫; ) HANGUL SYLLABLE DDEOLB +B5AC;B5AC;1104 1165 11B3;B5AC;1104 1165 11B3; # (떬; 떬; 떬; 떬; 떬; ) HANGUL SYLLABLE DDEOLS +B5AD;B5AD;1104 1165 11B4;B5AD;1104 1165 11B4; # (떭; 떭; 떭; 떭; 떭; ) HANGUL SYLLABLE DDEOLT +B5AE;B5AE;1104 1165 11B5;B5AE;1104 1165 11B5; # (떮; 떮; 떮; 떮; 떮; ) HANGUL SYLLABLE DDEOLP +B5AF;B5AF;1104 1165 11B6;B5AF;1104 1165 11B6; # (떯; 떯; 떯; 떯; 떯; ) HANGUL SYLLABLE DDEOLH +B5B0;B5B0;1104 1165 11B7;B5B0;1104 1165 11B7; # (떰; 떰; 떰; 떰; 떰; ) HANGUL SYLLABLE DDEOM +B5B1;B5B1;1104 1165 11B8;B5B1;1104 1165 11B8; # (떱; 떱; 떱; 떱; 떱; ) HANGUL SYLLABLE DDEOB +B5B2;B5B2;1104 1165 11B9;B5B2;1104 1165 11B9; # (떲; 떲; 떲; 떲; 떲; ) HANGUL SYLLABLE DDEOBS +B5B3;B5B3;1104 1165 11BA;B5B3;1104 1165 11BA; # (떳; 떳; 떳; 떳; 떳; ) HANGUL SYLLABLE DDEOS +B5B4;B5B4;1104 1165 11BB;B5B4;1104 1165 11BB; # (떴; 떴; 떴; 떴; 떴; ) HANGUL SYLLABLE DDEOSS +B5B5;B5B5;1104 1165 11BC;B5B5;1104 1165 11BC; # (떵; 떵; 떵; 떵; 떵; ) HANGUL SYLLABLE DDEONG +B5B6;B5B6;1104 1165 11BD;B5B6;1104 1165 11BD; # (떶; 떶; 떶; 떶; 떶; ) HANGUL SYLLABLE DDEOJ +B5B7;B5B7;1104 1165 11BE;B5B7;1104 1165 11BE; # (떷; 떷; 떷; 떷; 떷; ) HANGUL SYLLABLE DDEOC +B5B8;B5B8;1104 1165 11BF;B5B8;1104 1165 11BF; # (떸; 떸; 떸; 떸; 떸; ) HANGUL SYLLABLE DDEOK +B5B9;B5B9;1104 1165 11C0;B5B9;1104 1165 11C0; # (떹; 떹; 떹; 떹; 떹; ) HANGUL SYLLABLE DDEOT +B5BA;B5BA;1104 1165 11C1;B5BA;1104 1165 11C1; # (떺; 떺; 떺; 떺; 떺; ) HANGUL SYLLABLE DDEOP +B5BB;B5BB;1104 1165 11C2;B5BB;1104 1165 11C2; # (떻; 떻; 떻; 떻; 떻; ) HANGUL SYLLABLE DDEOH +B5BC;B5BC;1104 1166;B5BC;1104 1166; # (떼; 떼; 떼; 떼; 떼; ) HANGUL SYLLABLE DDE +B5BD;B5BD;1104 1166 11A8;B5BD;1104 1166 11A8; # (떽; 떽; 떽; 떽; 떽; ) HANGUL SYLLABLE DDEG +B5BE;B5BE;1104 1166 11A9;B5BE;1104 1166 11A9; # (떾; 떾; 떾; 떾; 떾; ) HANGUL SYLLABLE DDEGG +B5BF;B5BF;1104 1166 11AA;B5BF;1104 1166 11AA; # (떿; 떿; 떿; 떿; 떿; ) HANGUL SYLLABLE DDEGS +B5C0;B5C0;1104 1166 11AB;B5C0;1104 1166 11AB; # (뗀; 뗀; 뗀; 뗀; 뗀; ) HANGUL SYLLABLE DDEN +B5C1;B5C1;1104 1166 11AC;B5C1;1104 1166 11AC; # (뗁; 뗁; 뗁; 뗁; 뗁; ) HANGUL SYLLABLE DDENJ +B5C2;B5C2;1104 1166 11AD;B5C2;1104 1166 11AD; # (뗂; 뗂; 뗂; 뗂; 뗂; ) HANGUL SYLLABLE DDENH +B5C3;B5C3;1104 1166 11AE;B5C3;1104 1166 11AE; # (뗃; 뗃; 뗃; 뗃; 뗃; ) HANGUL SYLLABLE DDED +B5C4;B5C4;1104 1166 11AF;B5C4;1104 1166 11AF; # (뗄; 뗄; 뗄; 뗄; 뗄; ) HANGUL SYLLABLE DDEL +B5C5;B5C5;1104 1166 11B0;B5C5;1104 1166 11B0; # (뗅; 뗅; 뗅; 뗅; 뗅; ) HANGUL SYLLABLE DDELG +B5C6;B5C6;1104 1166 11B1;B5C6;1104 1166 11B1; # (뗆; 뗆; 뗆; 뗆; 뗆; ) HANGUL SYLLABLE DDELM +B5C7;B5C7;1104 1166 11B2;B5C7;1104 1166 11B2; # (뗇; 뗇; 뗇; 뗇; 뗇; ) HANGUL SYLLABLE DDELB +B5C8;B5C8;1104 1166 11B3;B5C8;1104 1166 11B3; # (뗈; 뗈; 뗈; 뗈; 뗈; ) HANGUL SYLLABLE DDELS +B5C9;B5C9;1104 1166 11B4;B5C9;1104 1166 11B4; # (뗉; 뗉; 뗉; 뗉; 뗉; ) HANGUL SYLLABLE DDELT +B5CA;B5CA;1104 1166 11B5;B5CA;1104 1166 11B5; # (뗊; 뗊; 뗊; 뗊; 뗊; ) HANGUL SYLLABLE DDELP +B5CB;B5CB;1104 1166 11B6;B5CB;1104 1166 11B6; # (뗋; 뗋; 뗋; 뗋; 뗋; ) HANGUL SYLLABLE DDELH +B5CC;B5CC;1104 1166 11B7;B5CC;1104 1166 11B7; # (뗌; 뗌; 뗌; 뗌; 뗌; ) HANGUL SYLLABLE DDEM +B5CD;B5CD;1104 1166 11B8;B5CD;1104 1166 11B8; # (뗍; 뗍; 뗍; 뗍; 뗍; ) HANGUL SYLLABLE DDEB +B5CE;B5CE;1104 1166 11B9;B5CE;1104 1166 11B9; # (뗎; 뗎; 뗎; 뗎; 뗎; ) HANGUL SYLLABLE DDEBS +B5CF;B5CF;1104 1166 11BA;B5CF;1104 1166 11BA; # (뗏; 뗏; 뗏; 뗏; 뗏; ) HANGUL SYLLABLE DDES +B5D0;B5D0;1104 1166 11BB;B5D0;1104 1166 11BB; # (뗐; 뗐; 뗐; 뗐; 뗐; ) HANGUL SYLLABLE DDESS +B5D1;B5D1;1104 1166 11BC;B5D1;1104 1166 11BC; # (뗑; 뗑; 뗑; 뗑; 뗑; ) HANGUL SYLLABLE DDENG +B5D2;B5D2;1104 1166 11BD;B5D2;1104 1166 11BD; # (뗒; 뗒; 뗒; 뗒; 뗒; ) HANGUL SYLLABLE DDEJ +B5D3;B5D3;1104 1166 11BE;B5D3;1104 1166 11BE; # (뗓; 뗓; 뗓; 뗓; 뗓; ) HANGUL SYLLABLE DDEC +B5D4;B5D4;1104 1166 11BF;B5D4;1104 1166 11BF; # (뗔; 뗔; 뗔; 뗔; 뗔; ) HANGUL SYLLABLE DDEK +B5D5;B5D5;1104 1166 11C0;B5D5;1104 1166 11C0; # (뗕; 뗕; 뗕; 뗕; 뗕; ) HANGUL SYLLABLE DDET +B5D6;B5D6;1104 1166 11C1;B5D6;1104 1166 11C1; # (뗖; 뗖; 뗖; 뗖; 뗖; ) HANGUL SYLLABLE DDEP +B5D7;B5D7;1104 1166 11C2;B5D7;1104 1166 11C2; # (뗗; 뗗; 뗗; 뗗; 뗗; ) HANGUL SYLLABLE DDEH +B5D8;B5D8;1104 1167;B5D8;1104 1167; # (뗘; 뗘; 뗘; 뗘; 뗘; ) HANGUL SYLLABLE DDYEO +B5D9;B5D9;1104 1167 11A8;B5D9;1104 1167 11A8; # (뗙; 뗙; 뗙; 뗙; 뗙; ) HANGUL SYLLABLE DDYEOG +B5DA;B5DA;1104 1167 11A9;B5DA;1104 1167 11A9; # (뗚; 뗚; 뗚; 뗚; 뗚; ) HANGUL SYLLABLE DDYEOGG +B5DB;B5DB;1104 1167 11AA;B5DB;1104 1167 11AA; # (뗛; 뗛; 뗛; 뗛; 뗛; ) HANGUL SYLLABLE DDYEOGS +B5DC;B5DC;1104 1167 11AB;B5DC;1104 1167 11AB; # (뗜; 뗜; 뗜; 뗜; 뗜; ) HANGUL SYLLABLE DDYEON +B5DD;B5DD;1104 1167 11AC;B5DD;1104 1167 11AC; # (뗝; 뗝; 뗝; 뗝; 뗝; ) HANGUL SYLLABLE DDYEONJ +B5DE;B5DE;1104 1167 11AD;B5DE;1104 1167 11AD; # (뗞; 뗞; 뗞; 뗞; 뗞; ) HANGUL SYLLABLE DDYEONH +B5DF;B5DF;1104 1167 11AE;B5DF;1104 1167 11AE; # (뗟; 뗟; 뗟; 뗟; 뗟; ) HANGUL SYLLABLE DDYEOD +B5E0;B5E0;1104 1167 11AF;B5E0;1104 1167 11AF; # (뗠; 뗠; 뗠; 뗠; 뗠; ) HANGUL SYLLABLE DDYEOL +B5E1;B5E1;1104 1167 11B0;B5E1;1104 1167 11B0; # (뗡; 뗡; 뗡; 뗡; 뗡; ) HANGUL SYLLABLE DDYEOLG +B5E2;B5E2;1104 1167 11B1;B5E2;1104 1167 11B1; # (뗢; 뗢; 뗢; 뗢; 뗢; ) HANGUL SYLLABLE DDYEOLM +B5E3;B5E3;1104 1167 11B2;B5E3;1104 1167 11B2; # (뗣; 뗣; 뗣; 뗣; 뗣; ) HANGUL SYLLABLE DDYEOLB +B5E4;B5E4;1104 1167 11B3;B5E4;1104 1167 11B3; # (뗤; 뗤; 뗤; 뗤; 뗤; ) HANGUL SYLLABLE DDYEOLS +B5E5;B5E5;1104 1167 11B4;B5E5;1104 1167 11B4; # (뗥; 뗥; 뗥; 뗥; 뗥; ) HANGUL SYLLABLE DDYEOLT +B5E6;B5E6;1104 1167 11B5;B5E6;1104 1167 11B5; # (뗦; 뗦; 뗦; 뗦; 뗦; ) HANGUL SYLLABLE DDYEOLP +B5E7;B5E7;1104 1167 11B6;B5E7;1104 1167 11B6; # (뗧; 뗧; 뗧; 뗧; 뗧; ) HANGUL SYLLABLE DDYEOLH +B5E8;B5E8;1104 1167 11B7;B5E8;1104 1167 11B7; # (뗨; 뗨; 뗨; 뗨; 뗨; ) HANGUL SYLLABLE DDYEOM +B5E9;B5E9;1104 1167 11B8;B5E9;1104 1167 11B8; # (뗩; 뗩; 뗩; 뗩; 뗩; ) HANGUL SYLLABLE DDYEOB +B5EA;B5EA;1104 1167 11B9;B5EA;1104 1167 11B9; # (뗪; 뗪; 뗪; 뗪; 뗪; ) HANGUL SYLLABLE DDYEOBS +B5EB;B5EB;1104 1167 11BA;B5EB;1104 1167 11BA; # (뗫; 뗫; 뗫; 뗫; 뗫; ) HANGUL SYLLABLE DDYEOS +B5EC;B5EC;1104 1167 11BB;B5EC;1104 1167 11BB; # (뗬; 뗬; 뗬; 뗬; 뗬; ) HANGUL SYLLABLE DDYEOSS +B5ED;B5ED;1104 1167 11BC;B5ED;1104 1167 11BC; # (뗭; 뗭; 뗭; 뗭; 뗭; ) HANGUL SYLLABLE DDYEONG +B5EE;B5EE;1104 1167 11BD;B5EE;1104 1167 11BD; # (뗮; 뗮; 뗮; 뗮; 뗮; ) HANGUL SYLLABLE DDYEOJ +B5EF;B5EF;1104 1167 11BE;B5EF;1104 1167 11BE; # (뗯; 뗯; 뗯; 뗯; 뗯; ) HANGUL SYLLABLE DDYEOC +B5F0;B5F0;1104 1167 11BF;B5F0;1104 1167 11BF; # (뗰; 뗰; 뗰; 뗰; 뗰; ) HANGUL SYLLABLE DDYEOK +B5F1;B5F1;1104 1167 11C0;B5F1;1104 1167 11C0; # (뗱; 뗱; 뗱; 뗱; 뗱; ) HANGUL SYLLABLE DDYEOT +B5F2;B5F2;1104 1167 11C1;B5F2;1104 1167 11C1; # (뗲; 뗲; 뗲; 뗲; 뗲; ) HANGUL SYLLABLE DDYEOP +B5F3;B5F3;1104 1167 11C2;B5F3;1104 1167 11C2; # (뗳; 뗳; 뗳; 뗳; 뗳; ) HANGUL SYLLABLE DDYEOH +B5F4;B5F4;1104 1168;B5F4;1104 1168; # (뗴; 뗴; 뗴; 뗴; 뗴; ) HANGUL SYLLABLE DDYE +B5F5;B5F5;1104 1168 11A8;B5F5;1104 1168 11A8; # (뗵; 뗵; 뗵; 뗵; 뗵; ) HANGUL SYLLABLE DDYEG +B5F6;B5F6;1104 1168 11A9;B5F6;1104 1168 11A9; # (뗶; 뗶; 뗶; 뗶; 뗶; ) HANGUL SYLLABLE DDYEGG +B5F7;B5F7;1104 1168 11AA;B5F7;1104 1168 11AA; # (뗷; 뗷; 뗷; 뗷; 뗷; ) HANGUL SYLLABLE DDYEGS +B5F8;B5F8;1104 1168 11AB;B5F8;1104 1168 11AB; # (뗸; 뗸; 뗸; 뗸; 뗸; ) HANGUL SYLLABLE DDYEN +B5F9;B5F9;1104 1168 11AC;B5F9;1104 1168 11AC; # (뗹; 뗹; 뗹; 뗹; 뗹; ) HANGUL SYLLABLE DDYENJ +B5FA;B5FA;1104 1168 11AD;B5FA;1104 1168 11AD; # (뗺; 뗺; 뗺; 뗺; 뗺; ) HANGUL SYLLABLE DDYENH +B5FB;B5FB;1104 1168 11AE;B5FB;1104 1168 11AE; # (뗻; 뗻; 뗻; 뗻; 뗻; ) HANGUL SYLLABLE DDYED +B5FC;B5FC;1104 1168 11AF;B5FC;1104 1168 11AF; # (뗼; 뗼; 뗼; 뗼; 뗼; ) HANGUL SYLLABLE DDYEL +B5FD;B5FD;1104 1168 11B0;B5FD;1104 1168 11B0; # (뗽; 뗽; 뗽; 뗽; 뗽; ) HANGUL SYLLABLE DDYELG +B5FE;B5FE;1104 1168 11B1;B5FE;1104 1168 11B1; # (뗾; 뗾; 뗾; 뗾; 뗾; ) HANGUL SYLLABLE DDYELM +B5FF;B5FF;1104 1168 11B2;B5FF;1104 1168 11B2; # (뗿; 뗿; 뗿; 뗿; 뗿; ) HANGUL SYLLABLE DDYELB +B600;B600;1104 1168 11B3;B600;1104 1168 11B3; # (똀; 똀; 똀; 똀; 똀; ) HANGUL SYLLABLE DDYELS +B601;B601;1104 1168 11B4;B601;1104 1168 11B4; # (똁; 똁; 똁; 똁; 똁; ) HANGUL SYLLABLE DDYELT +B602;B602;1104 1168 11B5;B602;1104 1168 11B5; # (똂; 똂; 똂; 똂; 똂; ) HANGUL SYLLABLE DDYELP +B603;B603;1104 1168 11B6;B603;1104 1168 11B6; # (똃; 똃; 똃; 똃; 똃; ) HANGUL SYLLABLE DDYELH +B604;B604;1104 1168 11B7;B604;1104 1168 11B7; # (똄; 똄; 똄; 똄; 똄; ) HANGUL SYLLABLE DDYEM +B605;B605;1104 1168 11B8;B605;1104 1168 11B8; # (똅; 똅; 똅; 똅; 똅; ) HANGUL SYLLABLE DDYEB +B606;B606;1104 1168 11B9;B606;1104 1168 11B9; # (똆; 똆; 똆; 똆; 똆; ) HANGUL SYLLABLE DDYEBS +B607;B607;1104 1168 11BA;B607;1104 1168 11BA; # (똇; 똇; 똇; 똇; 똇; ) HANGUL SYLLABLE DDYES +B608;B608;1104 1168 11BB;B608;1104 1168 11BB; # (똈; 똈; 똈; 똈; 똈; ) HANGUL SYLLABLE DDYESS +B609;B609;1104 1168 11BC;B609;1104 1168 11BC; # (똉; 똉; 똉; 똉; 똉; ) HANGUL SYLLABLE DDYENG +B60A;B60A;1104 1168 11BD;B60A;1104 1168 11BD; # (똊; 똊; 똊; 똊; 똊; ) HANGUL SYLLABLE DDYEJ +B60B;B60B;1104 1168 11BE;B60B;1104 1168 11BE; # (똋; 똋; 똋; 똋; 똋; ) HANGUL SYLLABLE DDYEC +B60C;B60C;1104 1168 11BF;B60C;1104 1168 11BF; # (똌; 똌; 똌; 똌; 똌; ) HANGUL SYLLABLE DDYEK +B60D;B60D;1104 1168 11C0;B60D;1104 1168 11C0; # (똍; 똍; 똍; 똍; 똍; ) HANGUL SYLLABLE DDYET +B60E;B60E;1104 1168 11C1;B60E;1104 1168 11C1; # (똎; 똎; 똎; 똎; 똎; ) HANGUL SYLLABLE DDYEP +B60F;B60F;1104 1168 11C2;B60F;1104 1168 11C2; # (똏; 똏; 똏; 똏; 똏; ) HANGUL SYLLABLE DDYEH +B610;B610;1104 1169;B610;1104 1169; # (또; 또; 또; 또; 또; ) HANGUL SYLLABLE DDO +B611;B611;1104 1169 11A8;B611;1104 1169 11A8; # (똑; 똑; 똑; 똑; 똑; ) HANGUL SYLLABLE DDOG +B612;B612;1104 1169 11A9;B612;1104 1169 11A9; # (똒; 똒; 똒; 똒; 똒; ) HANGUL SYLLABLE DDOGG +B613;B613;1104 1169 11AA;B613;1104 1169 11AA; # (똓; 똓; 똓; 똓; 똓; ) HANGUL SYLLABLE DDOGS +B614;B614;1104 1169 11AB;B614;1104 1169 11AB; # (똔; 똔; 똔; 똔; 똔; ) HANGUL SYLLABLE DDON +B615;B615;1104 1169 11AC;B615;1104 1169 11AC; # (똕; 똕; 똕; 똕; 똕; ) HANGUL SYLLABLE DDONJ +B616;B616;1104 1169 11AD;B616;1104 1169 11AD; # (똖; 똖; 똖; 똖; 똖; ) HANGUL SYLLABLE DDONH +B617;B617;1104 1169 11AE;B617;1104 1169 11AE; # (똗; 똗; 똗; 똗; 똗; ) HANGUL SYLLABLE DDOD +B618;B618;1104 1169 11AF;B618;1104 1169 11AF; # (똘; 똘; 똘; 똘; 똘; ) HANGUL SYLLABLE DDOL +B619;B619;1104 1169 11B0;B619;1104 1169 11B0; # (똙; 똙; 똙; 똙; 똙; ) HANGUL SYLLABLE DDOLG +B61A;B61A;1104 1169 11B1;B61A;1104 1169 11B1; # (똚; 똚; 똚; 똚; 똚; ) HANGUL SYLLABLE DDOLM +B61B;B61B;1104 1169 11B2;B61B;1104 1169 11B2; # (똛; 똛; 똛; 똛; 똛; ) HANGUL SYLLABLE DDOLB +B61C;B61C;1104 1169 11B3;B61C;1104 1169 11B3; # (똜; 똜; 똜; 똜; 똜; ) HANGUL SYLLABLE DDOLS +B61D;B61D;1104 1169 11B4;B61D;1104 1169 11B4; # (똝; 똝; 똝; 똝; 똝; ) HANGUL SYLLABLE DDOLT +B61E;B61E;1104 1169 11B5;B61E;1104 1169 11B5; # (똞; 똞; 똞; 똞; 똞; ) HANGUL SYLLABLE DDOLP +B61F;B61F;1104 1169 11B6;B61F;1104 1169 11B6; # (똟; 똟; 똟; 똟; 똟; ) HANGUL SYLLABLE DDOLH +B620;B620;1104 1169 11B7;B620;1104 1169 11B7; # (똠; 똠; 똠; 똠; 똠; ) HANGUL SYLLABLE DDOM +B621;B621;1104 1169 11B8;B621;1104 1169 11B8; # (똡; 똡; 똡; 똡; 똡; ) HANGUL SYLLABLE DDOB +B622;B622;1104 1169 11B9;B622;1104 1169 11B9; # (똢; 똢; 똢; 똢; 똢; ) HANGUL SYLLABLE DDOBS +B623;B623;1104 1169 11BA;B623;1104 1169 11BA; # (똣; 똣; 똣; 똣; 똣; ) HANGUL SYLLABLE DDOS +B624;B624;1104 1169 11BB;B624;1104 1169 11BB; # (똤; 똤; 똤; 똤; 똤; ) HANGUL SYLLABLE DDOSS +B625;B625;1104 1169 11BC;B625;1104 1169 11BC; # (똥; 똥; 똥; 똥; 똥; ) HANGUL SYLLABLE DDONG +B626;B626;1104 1169 11BD;B626;1104 1169 11BD; # (똦; 똦; 똦; 똦; 똦; ) HANGUL SYLLABLE DDOJ +B627;B627;1104 1169 11BE;B627;1104 1169 11BE; # (똧; 똧; 똧; 똧; 똧; ) HANGUL SYLLABLE DDOC +B628;B628;1104 1169 11BF;B628;1104 1169 11BF; # (똨; 똨; 똨; 똨; 똨; ) HANGUL SYLLABLE DDOK +B629;B629;1104 1169 11C0;B629;1104 1169 11C0; # (똩; 똩; 똩; 똩; 똩; ) HANGUL SYLLABLE DDOT +B62A;B62A;1104 1169 11C1;B62A;1104 1169 11C1; # (똪; 똪; 똪; 똪; 똪; ) HANGUL SYLLABLE DDOP +B62B;B62B;1104 1169 11C2;B62B;1104 1169 11C2; # (똫; 똫; 똫; 똫; 똫; ) HANGUL SYLLABLE DDOH +B62C;B62C;1104 116A;B62C;1104 116A; # (똬; 똬; 똬; 똬; 똬; ) HANGUL SYLLABLE DDWA +B62D;B62D;1104 116A 11A8;B62D;1104 116A 11A8; # (똭; 똭; 똭; 똭; 똭; ) HANGUL SYLLABLE DDWAG +B62E;B62E;1104 116A 11A9;B62E;1104 116A 11A9; # (똮; 똮; 똮; 똮; 똮; ) HANGUL SYLLABLE DDWAGG +B62F;B62F;1104 116A 11AA;B62F;1104 116A 11AA; # (똯; 똯; 똯; 똯; 똯; ) HANGUL SYLLABLE DDWAGS +B630;B630;1104 116A 11AB;B630;1104 116A 11AB; # (똰; 똰; 똰; 똰; 똰; ) HANGUL SYLLABLE DDWAN +B631;B631;1104 116A 11AC;B631;1104 116A 11AC; # (똱; 똱; 똱; 똱; 똱; ) HANGUL SYLLABLE DDWANJ +B632;B632;1104 116A 11AD;B632;1104 116A 11AD; # (똲; 똲; 똲; 똲; 똲; ) HANGUL SYLLABLE DDWANH +B633;B633;1104 116A 11AE;B633;1104 116A 11AE; # (똳; 똳; 똳; 똳; 똳; ) HANGUL SYLLABLE DDWAD +B634;B634;1104 116A 11AF;B634;1104 116A 11AF; # (똴; 똴; 똴; 똴; 똴; ) HANGUL SYLLABLE DDWAL +B635;B635;1104 116A 11B0;B635;1104 116A 11B0; # (똵; 똵; 똵; 똵; 똵; ) HANGUL SYLLABLE DDWALG +B636;B636;1104 116A 11B1;B636;1104 116A 11B1; # (똶; 똶; 똶; 똶; 똶; ) HANGUL SYLLABLE DDWALM +B637;B637;1104 116A 11B2;B637;1104 116A 11B2; # (똷; 똷; 똷; 똷; 똷; ) HANGUL SYLLABLE DDWALB +B638;B638;1104 116A 11B3;B638;1104 116A 11B3; # (똸; 똸; 똸; 똸; 똸; ) HANGUL SYLLABLE DDWALS +B639;B639;1104 116A 11B4;B639;1104 116A 11B4; # (똹; 똹; 똹; 똹; 똹; ) HANGUL SYLLABLE DDWALT +B63A;B63A;1104 116A 11B5;B63A;1104 116A 11B5; # (똺; 똺; 똺; 똺; 똺; ) HANGUL SYLLABLE DDWALP +B63B;B63B;1104 116A 11B6;B63B;1104 116A 11B6; # (똻; 똻; 똻; 똻; 똻; ) HANGUL SYLLABLE DDWALH +B63C;B63C;1104 116A 11B7;B63C;1104 116A 11B7; # (똼; 똼; 똼; 똼; 똼; ) HANGUL SYLLABLE DDWAM +B63D;B63D;1104 116A 11B8;B63D;1104 116A 11B8; # (똽; 똽; 똽; 똽; 똽; ) HANGUL SYLLABLE DDWAB +B63E;B63E;1104 116A 11B9;B63E;1104 116A 11B9; # (똾; 똾; 똾; 똾; 똾; ) HANGUL SYLLABLE DDWABS +B63F;B63F;1104 116A 11BA;B63F;1104 116A 11BA; # (똿; 똿; 똿; 똿; 똿; ) HANGUL SYLLABLE DDWAS +B640;B640;1104 116A 11BB;B640;1104 116A 11BB; # (뙀; 뙀; 뙀; 뙀; 뙀; ) HANGUL SYLLABLE DDWASS +B641;B641;1104 116A 11BC;B641;1104 116A 11BC; # (뙁; 뙁; 뙁; 뙁; 뙁; ) HANGUL SYLLABLE DDWANG +B642;B642;1104 116A 11BD;B642;1104 116A 11BD; # (뙂; 뙂; 뙂; 뙂; 뙂; ) HANGUL SYLLABLE DDWAJ +B643;B643;1104 116A 11BE;B643;1104 116A 11BE; # (뙃; 뙃; 뙃; 뙃; 뙃; ) HANGUL SYLLABLE DDWAC +B644;B644;1104 116A 11BF;B644;1104 116A 11BF; # (뙄; 뙄; 뙄; 뙄; 뙄; ) HANGUL SYLLABLE DDWAK +B645;B645;1104 116A 11C0;B645;1104 116A 11C0; # (뙅; 뙅; 뙅; 뙅; 뙅; ) HANGUL SYLLABLE DDWAT +B646;B646;1104 116A 11C1;B646;1104 116A 11C1; # (뙆; 뙆; 뙆; 뙆; 뙆; ) HANGUL SYLLABLE DDWAP +B647;B647;1104 116A 11C2;B647;1104 116A 11C2; # (뙇; 뙇; 뙇; 뙇; 뙇; ) HANGUL SYLLABLE DDWAH +B648;B648;1104 116B;B648;1104 116B; # (뙈; 뙈; 뙈; 뙈; 뙈; ) HANGUL SYLLABLE DDWAE +B649;B649;1104 116B 11A8;B649;1104 116B 11A8; # (뙉; 뙉; 뙉; 뙉; 뙉; ) HANGUL SYLLABLE DDWAEG +B64A;B64A;1104 116B 11A9;B64A;1104 116B 11A9; # (뙊; 뙊; 뙊; 뙊; 뙊; ) HANGUL SYLLABLE DDWAEGG +B64B;B64B;1104 116B 11AA;B64B;1104 116B 11AA; # (뙋; 뙋; 뙋; 뙋; 뙋; ) HANGUL SYLLABLE DDWAEGS +B64C;B64C;1104 116B 11AB;B64C;1104 116B 11AB; # (뙌; 뙌; 뙌; 뙌; 뙌; ) HANGUL SYLLABLE DDWAEN +B64D;B64D;1104 116B 11AC;B64D;1104 116B 11AC; # (뙍; 뙍; 뙍; 뙍; 뙍; ) HANGUL SYLLABLE DDWAENJ +B64E;B64E;1104 116B 11AD;B64E;1104 116B 11AD; # (뙎; 뙎; 뙎; 뙎; 뙎; ) HANGUL SYLLABLE DDWAENH +B64F;B64F;1104 116B 11AE;B64F;1104 116B 11AE; # (뙏; 뙏; 뙏; 뙏; 뙏; ) HANGUL SYLLABLE DDWAED +B650;B650;1104 116B 11AF;B650;1104 116B 11AF; # (뙐; 뙐; 뙐; 뙐; 뙐; ) HANGUL SYLLABLE DDWAEL +B651;B651;1104 116B 11B0;B651;1104 116B 11B0; # (뙑; 뙑; 뙑; 뙑; 뙑; ) HANGUL SYLLABLE DDWAELG +B652;B652;1104 116B 11B1;B652;1104 116B 11B1; # (뙒; 뙒; 뙒; 뙒; 뙒; ) HANGUL SYLLABLE DDWAELM +B653;B653;1104 116B 11B2;B653;1104 116B 11B2; # (뙓; 뙓; 뙓; 뙓; 뙓; ) HANGUL SYLLABLE DDWAELB +B654;B654;1104 116B 11B3;B654;1104 116B 11B3; # (뙔; 뙔; 뙔; 뙔; 뙔; ) HANGUL SYLLABLE DDWAELS +B655;B655;1104 116B 11B4;B655;1104 116B 11B4; # (뙕; 뙕; 뙕; 뙕; 뙕; ) HANGUL SYLLABLE DDWAELT +B656;B656;1104 116B 11B5;B656;1104 116B 11B5; # (뙖; 뙖; 뙖; 뙖; 뙖; ) HANGUL SYLLABLE DDWAELP +B657;B657;1104 116B 11B6;B657;1104 116B 11B6; # (뙗; 뙗; 뙗; 뙗; 뙗; ) HANGUL SYLLABLE DDWAELH +B658;B658;1104 116B 11B7;B658;1104 116B 11B7; # (뙘; 뙘; 뙘; 뙘; 뙘; ) HANGUL SYLLABLE DDWAEM +B659;B659;1104 116B 11B8;B659;1104 116B 11B8; # (뙙; 뙙; 뙙; 뙙; 뙙; ) HANGUL SYLLABLE DDWAEB +B65A;B65A;1104 116B 11B9;B65A;1104 116B 11B9; # (뙚; 뙚; 뙚; 뙚; 뙚; ) HANGUL SYLLABLE DDWAEBS +B65B;B65B;1104 116B 11BA;B65B;1104 116B 11BA; # (뙛; 뙛; 뙛; 뙛; 뙛; ) HANGUL SYLLABLE DDWAES +B65C;B65C;1104 116B 11BB;B65C;1104 116B 11BB; # (뙜; 뙜; 뙜; 뙜; 뙜; ) HANGUL SYLLABLE DDWAESS +B65D;B65D;1104 116B 11BC;B65D;1104 116B 11BC; # (뙝; 뙝; 뙝; 뙝; 뙝; ) HANGUL SYLLABLE DDWAENG +B65E;B65E;1104 116B 11BD;B65E;1104 116B 11BD; # (뙞; 뙞; 뙞; 뙞; 뙞; ) HANGUL SYLLABLE DDWAEJ +B65F;B65F;1104 116B 11BE;B65F;1104 116B 11BE; # (뙟; 뙟; 뙟; 뙟; 뙟; ) HANGUL SYLLABLE DDWAEC +B660;B660;1104 116B 11BF;B660;1104 116B 11BF; # (뙠; 뙠; 뙠; 뙠; 뙠; ) HANGUL SYLLABLE DDWAEK +B661;B661;1104 116B 11C0;B661;1104 116B 11C0; # (뙡; 뙡; 뙡; 뙡; 뙡; ) HANGUL SYLLABLE DDWAET +B662;B662;1104 116B 11C1;B662;1104 116B 11C1; # (뙢; 뙢; 뙢; 뙢; 뙢; ) HANGUL SYLLABLE DDWAEP +B663;B663;1104 116B 11C2;B663;1104 116B 11C2; # (뙣; 뙣; 뙣; 뙣; 뙣; ) HANGUL SYLLABLE DDWAEH +B664;B664;1104 116C;B664;1104 116C; # (뙤; 뙤; 뙤; 뙤; 뙤; ) HANGUL SYLLABLE DDOE +B665;B665;1104 116C 11A8;B665;1104 116C 11A8; # (뙥; 뙥; 뙥; 뙥; 뙥; ) HANGUL SYLLABLE DDOEG +B666;B666;1104 116C 11A9;B666;1104 116C 11A9; # (뙦; 뙦; 뙦; 뙦; 뙦; ) HANGUL SYLLABLE DDOEGG +B667;B667;1104 116C 11AA;B667;1104 116C 11AA; # (뙧; 뙧; 뙧; 뙧; 뙧; ) HANGUL SYLLABLE DDOEGS +B668;B668;1104 116C 11AB;B668;1104 116C 11AB; # (뙨; 뙨; 뙨; 뙨; 뙨; ) HANGUL SYLLABLE DDOEN +B669;B669;1104 116C 11AC;B669;1104 116C 11AC; # (뙩; 뙩; 뙩; 뙩; 뙩; ) HANGUL SYLLABLE DDOENJ +B66A;B66A;1104 116C 11AD;B66A;1104 116C 11AD; # (뙪; 뙪; 뙪; 뙪; 뙪; ) HANGUL SYLLABLE DDOENH +B66B;B66B;1104 116C 11AE;B66B;1104 116C 11AE; # (뙫; 뙫; 뙫; 뙫; 뙫; ) HANGUL SYLLABLE DDOED +B66C;B66C;1104 116C 11AF;B66C;1104 116C 11AF; # (뙬; 뙬; 뙬; 뙬; 뙬; ) HANGUL SYLLABLE DDOEL +B66D;B66D;1104 116C 11B0;B66D;1104 116C 11B0; # (뙭; 뙭; 뙭; 뙭; 뙭; ) HANGUL SYLLABLE DDOELG +B66E;B66E;1104 116C 11B1;B66E;1104 116C 11B1; # (뙮; 뙮; 뙮; 뙮; 뙮; ) HANGUL SYLLABLE DDOELM +B66F;B66F;1104 116C 11B2;B66F;1104 116C 11B2; # (뙯; 뙯; 뙯; 뙯; 뙯; ) HANGUL SYLLABLE DDOELB +B670;B670;1104 116C 11B3;B670;1104 116C 11B3; # (뙰; 뙰; 뙰; 뙰; 뙰; ) HANGUL SYLLABLE DDOELS +B671;B671;1104 116C 11B4;B671;1104 116C 11B4; # (뙱; 뙱; 뙱; 뙱; 뙱; ) HANGUL SYLLABLE DDOELT +B672;B672;1104 116C 11B5;B672;1104 116C 11B5; # (뙲; 뙲; 뙲; 뙲; 뙲; ) HANGUL SYLLABLE DDOELP +B673;B673;1104 116C 11B6;B673;1104 116C 11B6; # (뙳; 뙳; 뙳; 뙳; 뙳; ) HANGUL SYLLABLE DDOELH +B674;B674;1104 116C 11B7;B674;1104 116C 11B7; # (뙴; 뙴; 뙴; 뙴; 뙴; ) HANGUL SYLLABLE DDOEM +B675;B675;1104 116C 11B8;B675;1104 116C 11B8; # (뙵; 뙵; 뙵; 뙵; 뙵; ) HANGUL SYLLABLE DDOEB +B676;B676;1104 116C 11B9;B676;1104 116C 11B9; # (뙶; 뙶; 뙶; 뙶; 뙶; ) HANGUL SYLLABLE DDOEBS +B677;B677;1104 116C 11BA;B677;1104 116C 11BA; # (뙷; 뙷; 뙷; 뙷; 뙷; ) HANGUL SYLLABLE DDOES +B678;B678;1104 116C 11BB;B678;1104 116C 11BB; # (뙸; 뙸; 뙸; 뙸; 뙸; ) HANGUL SYLLABLE DDOESS +B679;B679;1104 116C 11BC;B679;1104 116C 11BC; # (뙹; 뙹; 뙹; 뙹; 뙹; ) HANGUL SYLLABLE DDOENG +B67A;B67A;1104 116C 11BD;B67A;1104 116C 11BD; # (뙺; 뙺; 뙺; 뙺; 뙺; ) HANGUL SYLLABLE DDOEJ +B67B;B67B;1104 116C 11BE;B67B;1104 116C 11BE; # (뙻; 뙻; 뙻; 뙻; 뙻; ) HANGUL SYLLABLE DDOEC +B67C;B67C;1104 116C 11BF;B67C;1104 116C 11BF; # (뙼; 뙼; 뙼; 뙼; 뙼; ) HANGUL SYLLABLE DDOEK +B67D;B67D;1104 116C 11C0;B67D;1104 116C 11C0; # (뙽; 뙽; 뙽; 뙽; 뙽; ) HANGUL SYLLABLE DDOET +B67E;B67E;1104 116C 11C1;B67E;1104 116C 11C1; # (뙾; 뙾; 뙾; 뙾; 뙾; ) HANGUL SYLLABLE DDOEP +B67F;B67F;1104 116C 11C2;B67F;1104 116C 11C2; # (뙿; 뙿; 뙿; 뙿; 뙿; ) HANGUL SYLLABLE DDOEH +B680;B680;1104 116D;B680;1104 116D; # (뚀; 뚀; 뚀; 뚀; 뚀; ) HANGUL SYLLABLE DDYO +B681;B681;1104 116D 11A8;B681;1104 116D 11A8; # (뚁; 뚁; 뚁; 뚁; 뚁; ) HANGUL SYLLABLE DDYOG +B682;B682;1104 116D 11A9;B682;1104 116D 11A9; # (뚂; 뚂; 뚂; 뚂; 뚂; ) HANGUL SYLLABLE DDYOGG +B683;B683;1104 116D 11AA;B683;1104 116D 11AA; # (뚃; 뚃; 뚃; 뚃; 뚃; ) HANGUL SYLLABLE DDYOGS +B684;B684;1104 116D 11AB;B684;1104 116D 11AB; # (뚄; 뚄; 뚄; 뚄; 뚄; ) HANGUL SYLLABLE DDYON +B685;B685;1104 116D 11AC;B685;1104 116D 11AC; # (뚅; 뚅; 뚅; 뚅; 뚅; ) HANGUL SYLLABLE DDYONJ +B686;B686;1104 116D 11AD;B686;1104 116D 11AD; # (뚆; 뚆; 뚆; 뚆; 뚆; ) HANGUL SYLLABLE DDYONH +B687;B687;1104 116D 11AE;B687;1104 116D 11AE; # (뚇; 뚇; 뚇; 뚇; 뚇; ) HANGUL SYLLABLE DDYOD +B688;B688;1104 116D 11AF;B688;1104 116D 11AF; # (뚈; 뚈; 뚈; 뚈; 뚈; ) HANGUL SYLLABLE DDYOL +B689;B689;1104 116D 11B0;B689;1104 116D 11B0; # (뚉; 뚉; 뚉; 뚉; 뚉; ) HANGUL SYLLABLE DDYOLG +B68A;B68A;1104 116D 11B1;B68A;1104 116D 11B1; # (뚊; 뚊; 뚊; 뚊; 뚊; ) HANGUL SYLLABLE DDYOLM +B68B;B68B;1104 116D 11B2;B68B;1104 116D 11B2; # (뚋; 뚋; 뚋; 뚋; 뚋; ) HANGUL SYLLABLE DDYOLB +B68C;B68C;1104 116D 11B3;B68C;1104 116D 11B3; # (뚌; 뚌; 뚌; 뚌; 뚌; ) HANGUL SYLLABLE DDYOLS +B68D;B68D;1104 116D 11B4;B68D;1104 116D 11B4; # (뚍; 뚍; 뚍; 뚍; 뚍; ) HANGUL SYLLABLE DDYOLT +B68E;B68E;1104 116D 11B5;B68E;1104 116D 11B5; # (뚎; 뚎; 뚎; 뚎; 뚎; ) HANGUL SYLLABLE DDYOLP +B68F;B68F;1104 116D 11B6;B68F;1104 116D 11B6; # (뚏; 뚏; 뚏; 뚏; 뚏; ) HANGUL SYLLABLE DDYOLH +B690;B690;1104 116D 11B7;B690;1104 116D 11B7; # (뚐; 뚐; 뚐; 뚐; 뚐; ) HANGUL SYLLABLE DDYOM +B691;B691;1104 116D 11B8;B691;1104 116D 11B8; # (뚑; 뚑; 뚑; 뚑; 뚑; ) HANGUL SYLLABLE DDYOB +B692;B692;1104 116D 11B9;B692;1104 116D 11B9; # (뚒; 뚒; 뚒; 뚒; 뚒; ) HANGUL SYLLABLE DDYOBS +B693;B693;1104 116D 11BA;B693;1104 116D 11BA; # (뚓; 뚓; 뚓; 뚓; 뚓; ) HANGUL SYLLABLE DDYOS +B694;B694;1104 116D 11BB;B694;1104 116D 11BB; # (뚔; 뚔; 뚔; 뚔; 뚔; ) HANGUL SYLLABLE DDYOSS +B695;B695;1104 116D 11BC;B695;1104 116D 11BC; # (뚕; 뚕; 뚕; 뚕; 뚕; ) HANGUL SYLLABLE DDYONG +B696;B696;1104 116D 11BD;B696;1104 116D 11BD; # (뚖; 뚖; 뚖; 뚖; 뚖; ) HANGUL SYLLABLE DDYOJ +B697;B697;1104 116D 11BE;B697;1104 116D 11BE; # (뚗; 뚗; 뚗; 뚗; 뚗; ) HANGUL SYLLABLE DDYOC +B698;B698;1104 116D 11BF;B698;1104 116D 11BF; # (뚘; 뚘; 뚘; 뚘; 뚘; ) HANGUL SYLLABLE DDYOK +B699;B699;1104 116D 11C0;B699;1104 116D 11C0; # (뚙; 뚙; 뚙; 뚙; 뚙; ) HANGUL SYLLABLE DDYOT +B69A;B69A;1104 116D 11C1;B69A;1104 116D 11C1; # (뚚; 뚚; 뚚; 뚚; 뚚; ) HANGUL SYLLABLE DDYOP +B69B;B69B;1104 116D 11C2;B69B;1104 116D 11C2; # (뚛; 뚛; 뚛; 뚛; 뚛; ) HANGUL SYLLABLE DDYOH +B69C;B69C;1104 116E;B69C;1104 116E; # (뚜; 뚜; 뚜; 뚜; 뚜; ) HANGUL SYLLABLE DDU +B69D;B69D;1104 116E 11A8;B69D;1104 116E 11A8; # (뚝; 뚝; 뚝; 뚝; 뚝; ) HANGUL SYLLABLE DDUG +B69E;B69E;1104 116E 11A9;B69E;1104 116E 11A9; # (뚞; 뚞; 뚞; 뚞; 뚞; ) HANGUL SYLLABLE DDUGG +B69F;B69F;1104 116E 11AA;B69F;1104 116E 11AA; # (뚟; 뚟; 뚟; 뚟; 뚟; ) HANGUL SYLLABLE DDUGS +B6A0;B6A0;1104 116E 11AB;B6A0;1104 116E 11AB; # (뚠; 뚠; 뚠; 뚠; 뚠; ) HANGUL SYLLABLE DDUN +B6A1;B6A1;1104 116E 11AC;B6A1;1104 116E 11AC; # (뚡; 뚡; 뚡; 뚡; 뚡; ) HANGUL SYLLABLE DDUNJ +B6A2;B6A2;1104 116E 11AD;B6A2;1104 116E 11AD; # (뚢; 뚢; 뚢; 뚢; 뚢; ) HANGUL SYLLABLE DDUNH +B6A3;B6A3;1104 116E 11AE;B6A3;1104 116E 11AE; # (뚣; 뚣; 뚣; 뚣; 뚣; ) HANGUL SYLLABLE DDUD +B6A4;B6A4;1104 116E 11AF;B6A4;1104 116E 11AF; # (뚤; 뚤; 뚤; 뚤; 뚤; ) HANGUL SYLLABLE DDUL +B6A5;B6A5;1104 116E 11B0;B6A5;1104 116E 11B0; # (뚥; 뚥; 뚥; 뚥; 뚥; ) HANGUL SYLLABLE DDULG +B6A6;B6A6;1104 116E 11B1;B6A6;1104 116E 11B1; # (뚦; 뚦; 뚦; 뚦; 뚦; ) HANGUL SYLLABLE DDULM +B6A7;B6A7;1104 116E 11B2;B6A7;1104 116E 11B2; # (뚧; 뚧; 뚧; 뚧; 뚧; ) HANGUL SYLLABLE DDULB +B6A8;B6A8;1104 116E 11B3;B6A8;1104 116E 11B3; # (뚨; 뚨; 뚨; 뚨; 뚨; ) HANGUL SYLLABLE DDULS +B6A9;B6A9;1104 116E 11B4;B6A9;1104 116E 11B4; # (뚩; 뚩; 뚩; 뚩; 뚩; ) HANGUL SYLLABLE DDULT +B6AA;B6AA;1104 116E 11B5;B6AA;1104 116E 11B5; # (뚪; 뚪; 뚪; 뚪; 뚪; ) HANGUL SYLLABLE DDULP +B6AB;B6AB;1104 116E 11B6;B6AB;1104 116E 11B6; # (뚫; 뚫; 뚫; 뚫; 뚫; ) HANGUL SYLLABLE DDULH +B6AC;B6AC;1104 116E 11B7;B6AC;1104 116E 11B7; # (뚬; 뚬; 뚬; 뚬; 뚬; ) HANGUL SYLLABLE DDUM +B6AD;B6AD;1104 116E 11B8;B6AD;1104 116E 11B8; # (뚭; 뚭; 뚭; 뚭; 뚭; ) HANGUL SYLLABLE DDUB +B6AE;B6AE;1104 116E 11B9;B6AE;1104 116E 11B9; # (뚮; 뚮; 뚮; 뚮; 뚮; ) HANGUL SYLLABLE DDUBS +B6AF;B6AF;1104 116E 11BA;B6AF;1104 116E 11BA; # (뚯; 뚯; 뚯; 뚯; 뚯; ) HANGUL SYLLABLE DDUS +B6B0;B6B0;1104 116E 11BB;B6B0;1104 116E 11BB; # (뚰; 뚰; 뚰; 뚰; 뚰; ) HANGUL SYLLABLE DDUSS +B6B1;B6B1;1104 116E 11BC;B6B1;1104 116E 11BC; # (뚱; 뚱; 뚱; 뚱; 뚱; ) HANGUL SYLLABLE DDUNG +B6B2;B6B2;1104 116E 11BD;B6B2;1104 116E 11BD; # (뚲; 뚲; 뚲; 뚲; 뚲; ) HANGUL SYLLABLE DDUJ +B6B3;B6B3;1104 116E 11BE;B6B3;1104 116E 11BE; # (뚳; 뚳; 뚳; 뚳; 뚳; ) HANGUL SYLLABLE DDUC +B6B4;B6B4;1104 116E 11BF;B6B4;1104 116E 11BF; # (뚴; 뚴; 뚴; 뚴; 뚴; ) HANGUL SYLLABLE DDUK +B6B5;B6B5;1104 116E 11C0;B6B5;1104 116E 11C0; # (뚵; 뚵; 뚵; 뚵; 뚵; ) HANGUL SYLLABLE DDUT +B6B6;B6B6;1104 116E 11C1;B6B6;1104 116E 11C1; # (뚶; 뚶; 뚶; 뚶; 뚶; ) HANGUL SYLLABLE DDUP +B6B7;B6B7;1104 116E 11C2;B6B7;1104 116E 11C2; # (뚷; 뚷; 뚷; 뚷; 뚷; ) HANGUL SYLLABLE DDUH +B6B8;B6B8;1104 116F;B6B8;1104 116F; # (뚸; 뚸; 뚸; 뚸; 뚸; ) HANGUL SYLLABLE DDWEO +B6B9;B6B9;1104 116F 11A8;B6B9;1104 116F 11A8; # (뚹; 뚹; 뚹; 뚹; 뚹; ) HANGUL SYLLABLE DDWEOG +B6BA;B6BA;1104 116F 11A9;B6BA;1104 116F 11A9; # (뚺; 뚺; 뚺; 뚺; 뚺; ) HANGUL SYLLABLE DDWEOGG +B6BB;B6BB;1104 116F 11AA;B6BB;1104 116F 11AA; # (뚻; 뚻; 뚻; 뚻; 뚻; ) HANGUL SYLLABLE DDWEOGS +B6BC;B6BC;1104 116F 11AB;B6BC;1104 116F 11AB; # (뚼; 뚼; 뚼; 뚼; 뚼; ) HANGUL SYLLABLE DDWEON +B6BD;B6BD;1104 116F 11AC;B6BD;1104 116F 11AC; # (뚽; 뚽; 뚽; 뚽; 뚽; ) HANGUL SYLLABLE DDWEONJ +B6BE;B6BE;1104 116F 11AD;B6BE;1104 116F 11AD; # (뚾; 뚾; 뚾; 뚾; 뚾; ) HANGUL SYLLABLE DDWEONH +B6BF;B6BF;1104 116F 11AE;B6BF;1104 116F 11AE; # (뚿; 뚿; 뚿; 뚿; 뚿; ) HANGUL SYLLABLE DDWEOD +B6C0;B6C0;1104 116F 11AF;B6C0;1104 116F 11AF; # (뛀; 뛀; 뛀; 뛀; 뛀; ) HANGUL SYLLABLE DDWEOL +B6C1;B6C1;1104 116F 11B0;B6C1;1104 116F 11B0; # (뛁; 뛁; 뛁; 뛁; 뛁; ) HANGUL SYLLABLE DDWEOLG +B6C2;B6C2;1104 116F 11B1;B6C2;1104 116F 11B1; # (뛂; 뛂; 뛂; 뛂; 뛂; ) HANGUL SYLLABLE DDWEOLM +B6C3;B6C3;1104 116F 11B2;B6C3;1104 116F 11B2; # (뛃; 뛃; 뛃; 뛃; 뛃; ) HANGUL SYLLABLE DDWEOLB +B6C4;B6C4;1104 116F 11B3;B6C4;1104 116F 11B3; # (뛄; 뛄; 뛄; 뛄; 뛄; ) HANGUL SYLLABLE DDWEOLS +B6C5;B6C5;1104 116F 11B4;B6C5;1104 116F 11B4; # (뛅; 뛅; 뛅; 뛅; 뛅; ) HANGUL SYLLABLE DDWEOLT +B6C6;B6C6;1104 116F 11B5;B6C6;1104 116F 11B5; # (뛆; 뛆; 뛆; 뛆; 뛆; ) HANGUL SYLLABLE DDWEOLP +B6C7;B6C7;1104 116F 11B6;B6C7;1104 116F 11B6; # (뛇; 뛇; 뛇; 뛇; 뛇; ) HANGUL SYLLABLE DDWEOLH +B6C8;B6C8;1104 116F 11B7;B6C8;1104 116F 11B7; # (뛈; 뛈; 뛈; 뛈; 뛈; ) HANGUL SYLLABLE DDWEOM +B6C9;B6C9;1104 116F 11B8;B6C9;1104 116F 11B8; # (뛉; 뛉; 뛉; 뛉; 뛉; ) HANGUL SYLLABLE DDWEOB +B6CA;B6CA;1104 116F 11B9;B6CA;1104 116F 11B9; # (뛊; 뛊; 뛊; 뛊; 뛊; ) HANGUL SYLLABLE DDWEOBS +B6CB;B6CB;1104 116F 11BA;B6CB;1104 116F 11BA; # (뛋; 뛋; 뛋; 뛋; 뛋; ) HANGUL SYLLABLE DDWEOS +B6CC;B6CC;1104 116F 11BB;B6CC;1104 116F 11BB; # (뛌; 뛌; 뛌; 뛌; 뛌; ) HANGUL SYLLABLE DDWEOSS +B6CD;B6CD;1104 116F 11BC;B6CD;1104 116F 11BC; # (뛍; 뛍; 뛍; 뛍; 뛍; ) HANGUL SYLLABLE DDWEONG +B6CE;B6CE;1104 116F 11BD;B6CE;1104 116F 11BD; # (뛎; 뛎; 뛎; 뛎; 뛎; ) HANGUL SYLLABLE DDWEOJ +B6CF;B6CF;1104 116F 11BE;B6CF;1104 116F 11BE; # (뛏; 뛏; 뛏; 뛏; 뛏; ) HANGUL SYLLABLE DDWEOC +B6D0;B6D0;1104 116F 11BF;B6D0;1104 116F 11BF; # (뛐; 뛐; 뛐; 뛐; 뛐; ) HANGUL SYLLABLE DDWEOK +B6D1;B6D1;1104 116F 11C0;B6D1;1104 116F 11C0; # (뛑; 뛑; 뛑; 뛑; 뛑; ) HANGUL SYLLABLE DDWEOT +B6D2;B6D2;1104 116F 11C1;B6D2;1104 116F 11C1; # (뛒; 뛒; 뛒; 뛒; 뛒; ) HANGUL SYLLABLE DDWEOP +B6D3;B6D3;1104 116F 11C2;B6D3;1104 116F 11C2; # (뛓; 뛓; 뛓; 뛓; 뛓; ) HANGUL SYLLABLE DDWEOH +B6D4;B6D4;1104 1170;B6D4;1104 1170; # (뛔; 뛔; 뛔; 뛔; 뛔; ) HANGUL SYLLABLE DDWE +B6D5;B6D5;1104 1170 11A8;B6D5;1104 1170 11A8; # (뛕; 뛕; 뛕; 뛕; 뛕; ) HANGUL SYLLABLE DDWEG +B6D6;B6D6;1104 1170 11A9;B6D6;1104 1170 11A9; # (뛖; 뛖; 뛖; 뛖; 뛖; ) HANGUL SYLLABLE DDWEGG +B6D7;B6D7;1104 1170 11AA;B6D7;1104 1170 11AA; # (뛗; 뛗; 뛗; 뛗; 뛗; ) HANGUL SYLLABLE DDWEGS +B6D8;B6D8;1104 1170 11AB;B6D8;1104 1170 11AB; # (뛘; 뛘; 뛘; 뛘; 뛘; ) HANGUL SYLLABLE DDWEN +B6D9;B6D9;1104 1170 11AC;B6D9;1104 1170 11AC; # (뛙; 뛙; 뛙; 뛙; 뛙; ) HANGUL SYLLABLE DDWENJ +B6DA;B6DA;1104 1170 11AD;B6DA;1104 1170 11AD; # (뛚; 뛚; 뛚; 뛚; 뛚; ) HANGUL SYLLABLE DDWENH +B6DB;B6DB;1104 1170 11AE;B6DB;1104 1170 11AE; # (뛛; 뛛; 뛛; 뛛; 뛛; ) HANGUL SYLLABLE DDWED +B6DC;B6DC;1104 1170 11AF;B6DC;1104 1170 11AF; # (뛜; 뛜; 뛜; 뛜; 뛜; ) HANGUL SYLLABLE DDWEL +B6DD;B6DD;1104 1170 11B0;B6DD;1104 1170 11B0; # (뛝; 뛝; 뛝; 뛝; 뛝; ) HANGUL SYLLABLE DDWELG +B6DE;B6DE;1104 1170 11B1;B6DE;1104 1170 11B1; # (뛞; 뛞; 뛞; 뛞; 뛞; ) HANGUL SYLLABLE DDWELM +B6DF;B6DF;1104 1170 11B2;B6DF;1104 1170 11B2; # (뛟; 뛟; 뛟; 뛟; 뛟; ) HANGUL SYLLABLE DDWELB +B6E0;B6E0;1104 1170 11B3;B6E0;1104 1170 11B3; # (뛠; 뛠; 뛠; 뛠; 뛠; ) HANGUL SYLLABLE DDWELS +B6E1;B6E1;1104 1170 11B4;B6E1;1104 1170 11B4; # (뛡; 뛡; 뛡; 뛡; 뛡; ) HANGUL SYLLABLE DDWELT +B6E2;B6E2;1104 1170 11B5;B6E2;1104 1170 11B5; # (뛢; 뛢; 뛢; 뛢; 뛢; ) HANGUL SYLLABLE DDWELP +B6E3;B6E3;1104 1170 11B6;B6E3;1104 1170 11B6; # (뛣; 뛣; 뛣; 뛣; 뛣; ) HANGUL SYLLABLE DDWELH +B6E4;B6E4;1104 1170 11B7;B6E4;1104 1170 11B7; # (뛤; 뛤; 뛤; 뛤; 뛤; ) HANGUL SYLLABLE DDWEM +B6E5;B6E5;1104 1170 11B8;B6E5;1104 1170 11B8; # (뛥; 뛥; 뛥; 뛥; 뛥; ) HANGUL SYLLABLE DDWEB +B6E6;B6E6;1104 1170 11B9;B6E6;1104 1170 11B9; # (뛦; 뛦; 뛦; 뛦; 뛦; ) HANGUL SYLLABLE DDWEBS +B6E7;B6E7;1104 1170 11BA;B6E7;1104 1170 11BA; # (뛧; 뛧; 뛧; 뛧; 뛧; ) HANGUL SYLLABLE DDWES +B6E8;B6E8;1104 1170 11BB;B6E8;1104 1170 11BB; # (뛨; 뛨; 뛨; 뛨; 뛨; ) HANGUL SYLLABLE DDWESS +B6E9;B6E9;1104 1170 11BC;B6E9;1104 1170 11BC; # (뛩; 뛩; 뛩; 뛩; 뛩; ) HANGUL SYLLABLE DDWENG +B6EA;B6EA;1104 1170 11BD;B6EA;1104 1170 11BD; # (뛪; 뛪; 뛪; 뛪; 뛪; ) HANGUL SYLLABLE DDWEJ +B6EB;B6EB;1104 1170 11BE;B6EB;1104 1170 11BE; # (뛫; 뛫; 뛫; 뛫; 뛫; ) HANGUL SYLLABLE DDWEC +B6EC;B6EC;1104 1170 11BF;B6EC;1104 1170 11BF; # (뛬; 뛬; 뛬; 뛬; 뛬; ) HANGUL SYLLABLE DDWEK +B6ED;B6ED;1104 1170 11C0;B6ED;1104 1170 11C0; # (뛭; 뛭; 뛭; 뛭; 뛭; ) HANGUL SYLLABLE DDWET +B6EE;B6EE;1104 1170 11C1;B6EE;1104 1170 11C1; # (뛮; 뛮; 뛮; 뛮; 뛮; ) HANGUL SYLLABLE DDWEP +B6EF;B6EF;1104 1170 11C2;B6EF;1104 1170 11C2; # (뛯; 뛯; 뛯; 뛯; 뛯; ) HANGUL SYLLABLE DDWEH +B6F0;B6F0;1104 1171;B6F0;1104 1171; # (뛰; 뛰; 뛰; 뛰; 뛰; ) HANGUL SYLLABLE DDWI +B6F1;B6F1;1104 1171 11A8;B6F1;1104 1171 11A8; # (뛱; 뛱; 뛱; 뛱; 뛱; ) HANGUL SYLLABLE DDWIG +B6F2;B6F2;1104 1171 11A9;B6F2;1104 1171 11A9; # (뛲; 뛲; 뛲; 뛲; 뛲; ) HANGUL SYLLABLE DDWIGG +B6F3;B6F3;1104 1171 11AA;B6F3;1104 1171 11AA; # (뛳; 뛳; 뛳; 뛳; 뛳; ) HANGUL SYLLABLE DDWIGS +B6F4;B6F4;1104 1171 11AB;B6F4;1104 1171 11AB; # (뛴; 뛴; 뛴; 뛴; 뛴; ) HANGUL SYLLABLE DDWIN +B6F5;B6F5;1104 1171 11AC;B6F5;1104 1171 11AC; # (뛵; 뛵; 뛵; 뛵; 뛵; ) HANGUL SYLLABLE DDWINJ +B6F6;B6F6;1104 1171 11AD;B6F6;1104 1171 11AD; # (뛶; 뛶; 뛶; 뛶; 뛶; ) HANGUL SYLLABLE DDWINH +B6F7;B6F7;1104 1171 11AE;B6F7;1104 1171 11AE; # (뛷; 뛷; 뛷; 뛷; 뛷; ) HANGUL SYLLABLE DDWID +B6F8;B6F8;1104 1171 11AF;B6F8;1104 1171 11AF; # (뛸; 뛸; 뛸; 뛸; 뛸; ) HANGUL SYLLABLE DDWIL +B6F9;B6F9;1104 1171 11B0;B6F9;1104 1171 11B0; # (뛹; 뛹; 뛹; 뛹; 뛹; ) HANGUL SYLLABLE DDWILG +B6FA;B6FA;1104 1171 11B1;B6FA;1104 1171 11B1; # (뛺; 뛺; 뛺; 뛺; 뛺; ) HANGUL SYLLABLE DDWILM +B6FB;B6FB;1104 1171 11B2;B6FB;1104 1171 11B2; # (뛻; 뛻; 뛻; 뛻; 뛻; ) HANGUL SYLLABLE DDWILB +B6FC;B6FC;1104 1171 11B3;B6FC;1104 1171 11B3; # (뛼; 뛼; 뛼; 뛼; 뛼; ) HANGUL SYLLABLE DDWILS +B6FD;B6FD;1104 1171 11B4;B6FD;1104 1171 11B4; # (뛽; 뛽; 뛽; 뛽; 뛽; ) HANGUL SYLLABLE DDWILT +B6FE;B6FE;1104 1171 11B5;B6FE;1104 1171 11B5; # (뛾; 뛾; 뛾; 뛾; 뛾; ) HANGUL SYLLABLE DDWILP +B6FF;B6FF;1104 1171 11B6;B6FF;1104 1171 11B6; # (뛿; 뛿; 뛿; 뛿; 뛿; ) HANGUL SYLLABLE DDWILH +B700;B700;1104 1171 11B7;B700;1104 1171 11B7; # (뜀; 뜀; 뜀; 뜀; 뜀; ) HANGUL SYLLABLE DDWIM +B701;B701;1104 1171 11B8;B701;1104 1171 11B8; # (뜁; 뜁; 뜁; 뜁; 뜁; ) HANGUL SYLLABLE DDWIB +B702;B702;1104 1171 11B9;B702;1104 1171 11B9; # (뜂; 뜂; 뜂; 뜂; 뜂; ) HANGUL SYLLABLE DDWIBS +B703;B703;1104 1171 11BA;B703;1104 1171 11BA; # (뜃; 뜃; 뜃; 뜃; 뜃; ) HANGUL SYLLABLE DDWIS +B704;B704;1104 1171 11BB;B704;1104 1171 11BB; # (뜄; 뜄; 뜄; 뜄; 뜄; ) HANGUL SYLLABLE DDWISS +B705;B705;1104 1171 11BC;B705;1104 1171 11BC; # (뜅; 뜅; 뜅; 뜅; 뜅; ) HANGUL SYLLABLE DDWING +B706;B706;1104 1171 11BD;B706;1104 1171 11BD; # (뜆; 뜆; 뜆; 뜆; 뜆; ) HANGUL SYLLABLE DDWIJ +B707;B707;1104 1171 11BE;B707;1104 1171 11BE; # (뜇; 뜇; 뜇; 뜇; 뜇; ) HANGUL SYLLABLE DDWIC +B708;B708;1104 1171 11BF;B708;1104 1171 11BF; # (뜈; 뜈; 뜈; 뜈; 뜈; ) HANGUL SYLLABLE DDWIK +B709;B709;1104 1171 11C0;B709;1104 1171 11C0; # (뜉; 뜉; 뜉; 뜉; 뜉; ) HANGUL SYLLABLE DDWIT +B70A;B70A;1104 1171 11C1;B70A;1104 1171 11C1; # (뜊; 뜊; 뜊; 뜊; 뜊; ) HANGUL SYLLABLE DDWIP +B70B;B70B;1104 1171 11C2;B70B;1104 1171 11C2; # (뜋; 뜋; 뜋; 뜋; 뜋; ) HANGUL SYLLABLE DDWIH +B70C;B70C;1104 1172;B70C;1104 1172; # (뜌; 뜌; 뜌; 뜌; 뜌; ) HANGUL SYLLABLE DDYU +B70D;B70D;1104 1172 11A8;B70D;1104 1172 11A8; # (뜍; 뜍; 뜍; 뜍; 뜍; ) HANGUL SYLLABLE DDYUG +B70E;B70E;1104 1172 11A9;B70E;1104 1172 11A9; # (뜎; 뜎; 뜎; 뜎; 뜎; ) HANGUL SYLLABLE DDYUGG +B70F;B70F;1104 1172 11AA;B70F;1104 1172 11AA; # (뜏; 뜏; 뜏; 뜏; 뜏; ) HANGUL SYLLABLE DDYUGS +B710;B710;1104 1172 11AB;B710;1104 1172 11AB; # (뜐; 뜐; 뜐; 뜐; 뜐; ) HANGUL SYLLABLE DDYUN +B711;B711;1104 1172 11AC;B711;1104 1172 11AC; # (뜑; 뜑; 뜑; 뜑; 뜑; ) HANGUL SYLLABLE DDYUNJ +B712;B712;1104 1172 11AD;B712;1104 1172 11AD; # (뜒; 뜒; 뜒; 뜒; 뜒; ) HANGUL SYLLABLE DDYUNH +B713;B713;1104 1172 11AE;B713;1104 1172 11AE; # (뜓; 뜓; 뜓; 뜓; 뜓; ) HANGUL SYLLABLE DDYUD +B714;B714;1104 1172 11AF;B714;1104 1172 11AF; # (뜔; 뜔; 뜔; 뜔; 뜔; ) HANGUL SYLLABLE DDYUL +B715;B715;1104 1172 11B0;B715;1104 1172 11B0; # (뜕; 뜕; 뜕; 뜕; 뜕; ) HANGUL SYLLABLE DDYULG +B716;B716;1104 1172 11B1;B716;1104 1172 11B1; # (뜖; 뜖; 뜖; 뜖; 뜖; ) HANGUL SYLLABLE DDYULM +B717;B717;1104 1172 11B2;B717;1104 1172 11B2; # (뜗; 뜗; 뜗; 뜗; 뜗; ) HANGUL SYLLABLE DDYULB +B718;B718;1104 1172 11B3;B718;1104 1172 11B3; # (뜘; 뜘; 뜘; 뜘; 뜘; ) HANGUL SYLLABLE DDYULS +B719;B719;1104 1172 11B4;B719;1104 1172 11B4; # (뜙; 뜙; 뜙; 뜙; 뜙; ) HANGUL SYLLABLE DDYULT +B71A;B71A;1104 1172 11B5;B71A;1104 1172 11B5; # (뜚; 뜚; 뜚; 뜚; 뜚; ) HANGUL SYLLABLE DDYULP +B71B;B71B;1104 1172 11B6;B71B;1104 1172 11B6; # (뜛; 뜛; 뜛; 뜛; 뜛; ) HANGUL SYLLABLE DDYULH +B71C;B71C;1104 1172 11B7;B71C;1104 1172 11B7; # (뜜; 뜜; 뜜; 뜜; 뜜; ) HANGUL SYLLABLE DDYUM +B71D;B71D;1104 1172 11B8;B71D;1104 1172 11B8; # (뜝; 뜝; 뜝; 뜝; 뜝; ) HANGUL SYLLABLE DDYUB +B71E;B71E;1104 1172 11B9;B71E;1104 1172 11B9; # (뜞; 뜞; 뜞; 뜞; 뜞; ) HANGUL SYLLABLE DDYUBS +B71F;B71F;1104 1172 11BA;B71F;1104 1172 11BA; # (뜟; 뜟; 뜟; 뜟; 뜟; ) HANGUL SYLLABLE DDYUS +B720;B720;1104 1172 11BB;B720;1104 1172 11BB; # (뜠; 뜠; 뜠; 뜠; 뜠; ) HANGUL SYLLABLE DDYUSS +B721;B721;1104 1172 11BC;B721;1104 1172 11BC; # (뜡; 뜡; 뜡; 뜡; 뜡; ) HANGUL SYLLABLE DDYUNG +B722;B722;1104 1172 11BD;B722;1104 1172 11BD; # (뜢; 뜢; 뜢; 뜢; 뜢; ) HANGUL SYLLABLE DDYUJ +B723;B723;1104 1172 11BE;B723;1104 1172 11BE; # (뜣; 뜣; 뜣; 뜣; 뜣; ) HANGUL SYLLABLE DDYUC +B724;B724;1104 1172 11BF;B724;1104 1172 11BF; # (뜤; 뜤; 뜤; 뜤; 뜤; ) HANGUL SYLLABLE DDYUK +B725;B725;1104 1172 11C0;B725;1104 1172 11C0; # (뜥; 뜥; 뜥; 뜥; 뜥; ) HANGUL SYLLABLE DDYUT +B726;B726;1104 1172 11C1;B726;1104 1172 11C1; # (뜦; 뜦; 뜦; 뜦; 뜦; ) HANGUL SYLLABLE DDYUP +B727;B727;1104 1172 11C2;B727;1104 1172 11C2; # (뜧; 뜧; 뜧; 뜧; 뜧; ) HANGUL SYLLABLE DDYUH +B728;B728;1104 1173;B728;1104 1173; # (뜨; 뜨; 뜨; 뜨; 뜨; ) HANGUL SYLLABLE DDEU +B729;B729;1104 1173 11A8;B729;1104 1173 11A8; # (뜩; 뜩; 뜩; 뜩; 뜩; ) HANGUL SYLLABLE DDEUG +B72A;B72A;1104 1173 11A9;B72A;1104 1173 11A9; # (뜪; 뜪; 뜪; 뜪; 뜪; ) HANGUL SYLLABLE DDEUGG +B72B;B72B;1104 1173 11AA;B72B;1104 1173 11AA; # (뜫; 뜫; 뜫; 뜫; 뜫; ) HANGUL SYLLABLE DDEUGS +B72C;B72C;1104 1173 11AB;B72C;1104 1173 11AB; # (뜬; 뜬; 뜬; 뜬; 뜬; ) HANGUL SYLLABLE DDEUN +B72D;B72D;1104 1173 11AC;B72D;1104 1173 11AC; # (뜭; 뜭; 뜭; 뜭; 뜭; ) HANGUL SYLLABLE DDEUNJ +B72E;B72E;1104 1173 11AD;B72E;1104 1173 11AD; # (뜮; 뜮; 뜮; 뜮; 뜮; ) HANGUL SYLLABLE DDEUNH +B72F;B72F;1104 1173 11AE;B72F;1104 1173 11AE; # (뜯; 뜯; 뜯; 뜯; 뜯; ) HANGUL SYLLABLE DDEUD +B730;B730;1104 1173 11AF;B730;1104 1173 11AF; # (뜰; 뜰; 뜰; 뜰; 뜰; ) HANGUL SYLLABLE DDEUL +B731;B731;1104 1173 11B0;B731;1104 1173 11B0; # (뜱; 뜱; 뜱; 뜱; 뜱; ) HANGUL SYLLABLE DDEULG +B732;B732;1104 1173 11B1;B732;1104 1173 11B1; # (뜲; 뜲; 뜲; 뜲; 뜲; ) HANGUL SYLLABLE DDEULM +B733;B733;1104 1173 11B2;B733;1104 1173 11B2; # (뜳; 뜳; 뜳; 뜳; 뜳; ) HANGUL SYLLABLE DDEULB +B734;B734;1104 1173 11B3;B734;1104 1173 11B3; # (뜴; 뜴; 뜴; 뜴; 뜴; ) HANGUL SYLLABLE DDEULS +B735;B735;1104 1173 11B4;B735;1104 1173 11B4; # (뜵; 뜵; 뜵; 뜵; 뜵; ) HANGUL SYLLABLE DDEULT +B736;B736;1104 1173 11B5;B736;1104 1173 11B5; # (뜶; 뜶; 뜶; 뜶; 뜶; ) HANGUL SYLLABLE DDEULP +B737;B737;1104 1173 11B6;B737;1104 1173 11B6; # (뜷; 뜷; 뜷; 뜷; 뜷; ) HANGUL SYLLABLE DDEULH +B738;B738;1104 1173 11B7;B738;1104 1173 11B7; # (뜸; 뜸; 뜸; 뜸; 뜸; ) HANGUL SYLLABLE DDEUM +B739;B739;1104 1173 11B8;B739;1104 1173 11B8; # (뜹; 뜹; 뜹; 뜹; 뜹; ) HANGUL SYLLABLE DDEUB +B73A;B73A;1104 1173 11B9;B73A;1104 1173 11B9; # (뜺; 뜺; 뜺; 뜺; 뜺; ) HANGUL SYLLABLE DDEUBS +B73B;B73B;1104 1173 11BA;B73B;1104 1173 11BA; # (뜻; 뜻; 뜻; 뜻; 뜻; ) HANGUL SYLLABLE DDEUS +B73C;B73C;1104 1173 11BB;B73C;1104 1173 11BB; # (뜼; 뜼; 뜼; 뜼; 뜼; ) HANGUL SYLLABLE DDEUSS +B73D;B73D;1104 1173 11BC;B73D;1104 1173 11BC; # (뜽; 뜽; 뜽; 뜽; 뜽; ) HANGUL SYLLABLE DDEUNG +B73E;B73E;1104 1173 11BD;B73E;1104 1173 11BD; # (뜾; 뜾; 뜾; 뜾; 뜾; ) HANGUL SYLLABLE DDEUJ +B73F;B73F;1104 1173 11BE;B73F;1104 1173 11BE; # (뜿; 뜿; 뜿; 뜿; 뜿; ) HANGUL SYLLABLE DDEUC +B740;B740;1104 1173 11BF;B740;1104 1173 11BF; # (띀; 띀; 띀; 띀; 띀; ) HANGUL SYLLABLE DDEUK +B741;B741;1104 1173 11C0;B741;1104 1173 11C0; # (띁; 띁; 띁; 띁; 띁; ) HANGUL SYLLABLE DDEUT +B742;B742;1104 1173 11C1;B742;1104 1173 11C1; # (띂; 띂; 띂; 띂; 띂; ) HANGUL SYLLABLE DDEUP +B743;B743;1104 1173 11C2;B743;1104 1173 11C2; # (띃; 띃; 띃; 띃; 띃; ) HANGUL SYLLABLE DDEUH +B744;B744;1104 1174;B744;1104 1174; # (띄; 띄; 띄; 띄; 띄; ) HANGUL SYLLABLE DDYI +B745;B745;1104 1174 11A8;B745;1104 1174 11A8; # (띅; 띅; 띅; 띅; 띅; ) HANGUL SYLLABLE DDYIG +B746;B746;1104 1174 11A9;B746;1104 1174 11A9; # (띆; 띆; 띆; 띆; 띆; ) HANGUL SYLLABLE DDYIGG +B747;B747;1104 1174 11AA;B747;1104 1174 11AA; # (띇; 띇; 띇; 띇; 띇; ) HANGUL SYLLABLE DDYIGS +B748;B748;1104 1174 11AB;B748;1104 1174 11AB; # (띈; 띈; 띈; 띈; 띈; ) HANGUL SYLLABLE DDYIN +B749;B749;1104 1174 11AC;B749;1104 1174 11AC; # (띉; 띉; 띉; 띉; 띉; ) HANGUL SYLLABLE DDYINJ +B74A;B74A;1104 1174 11AD;B74A;1104 1174 11AD; # (띊; 띊; 띊; 띊; 띊; ) HANGUL SYLLABLE DDYINH +B74B;B74B;1104 1174 11AE;B74B;1104 1174 11AE; # (띋; 띋; 띋; 띋; 띋; ) HANGUL SYLLABLE DDYID +B74C;B74C;1104 1174 11AF;B74C;1104 1174 11AF; # (띌; 띌; 띌; 띌; 띌; ) HANGUL SYLLABLE DDYIL +B74D;B74D;1104 1174 11B0;B74D;1104 1174 11B0; # (띍; 띍; 띍; 띍; 띍; ) HANGUL SYLLABLE DDYILG +B74E;B74E;1104 1174 11B1;B74E;1104 1174 11B1; # (띎; 띎; 띎; 띎; 띎; ) HANGUL SYLLABLE DDYILM +B74F;B74F;1104 1174 11B2;B74F;1104 1174 11B2; # (띏; 띏; 띏; 띏; 띏; ) HANGUL SYLLABLE DDYILB +B750;B750;1104 1174 11B3;B750;1104 1174 11B3; # (띐; 띐; 띐; 띐; 띐; ) HANGUL SYLLABLE DDYILS +B751;B751;1104 1174 11B4;B751;1104 1174 11B4; # (띑; 띑; 띑; 띑; 띑; ) HANGUL SYLLABLE DDYILT +B752;B752;1104 1174 11B5;B752;1104 1174 11B5; # (띒; 띒; 띒; 띒; 띒; ) HANGUL SYLLABLE DDYILP +B753;B753;1104 1174 11B6;B753;1104 1174 11B6; # (띓; 띓; 띓; 띓; 띓; ) HANGUL SYLLABLE DDYILH +B754;B754;1104 1174 11B7;B754;1104 1174 11B7; # (띔; 띔; 띔; 띔; 띔; ) HANGUL SYLLABLE DDYIM +B755;B755;1104 1174 11B8;B755;1104 1174 11B8; # (띕; 띕; 띕; 띕; 띕; ) HANGUL SYLLABLE DDYIB +B756;B756;1104 1174 11B9;B756;1104 1174 11B9; # (띖; 띖; 띖; 띖; 띖; ) HANGUL SYLLABLE DDYIBS +B757;B757;1104 1174 11BA;B757;1104 1174 11BA; # (띗; 띗; 띗; 띗; 띗; ) HANGUL SYLLABLE DDYIS +B758;B758;1104 1174 11BB;B758;1104 1174 11BB; # (띘; 띘; 띘; 띘; 띘; ) HANGUL SYLLABLE DDYISS +B759;B759;1104 1174 11BC;B759;1104 1174 11BC; # (띙; 띙; 띙; 띙; 띙; ) HANGUL SYLLABLE DDYING +B75A;B75A;1104 1174 11BD;B75A;1104 1174 11BD; # (띚; 띚; 띚; 띚; 띚; ) HANGUL SYLLABLE DDYIJ +B75B;B75B;1104 1174 11BE;B75B;1104 1174 11BE; # (띛; 띛; 띛; 띛; 띛; ) HANGUL SYLLABLE DDYIC +B75C;B75C;1104 1174 11BF;B75C;1104 1174 11BF; # (띜; 띜; 띜; 띜; 띜; ) HANGUL SYLLABLE DDYIK +B75D;B75D;1104 1174 11C0;B75D;1104 1174 11C0; # (띝; 띝; 띝; 띝; 띝; ) HANGUL SYLLABLE DDYIT +B75E;B75E;1104 1174 11C1;B75E;1104 1174 11C1; # (띞; 띞; 띞; 띞; 띞; ) HANGUL SYLLABLE DDYIP +B75F;B75F;1104 1174 11C2;B75F;1104 1174 11C2; # (띟; 띟; 띟; 띟; 띟; ) HANGUL SYLLABLE DDYIH +B760;B760;1104 1175;B760;1104 1175; # (띠; 띠; 띠; 띠; 띠; ) HANGUL SYLLABLE DDI +B761;B761;1104 1175 11A8;B761;1104 1175 11A8; # (띡; 띡; 띡; 띡; 띡; ) HANGUL SYLLABLE DDIG +B762;B762;1104 1175 11A9;B762;1104 1175 11A9; # (띢; 띢; 띢; 띢; 띢; ) HANGUL SYLLABLE DDIGG +B763;B763;1104 1175 11AA;B763;1104 1175 11AA; # (띣; 띣; 띣; 띣; 띣; ) HANGUL SYLLABLE DDIGS +B764;B764;1104 1175 11AB;B764;1104 1175 11AB; # (띤; 띤; 띤; 띤; 띤; ) HANGUL SYLLABLE DDIN +B765;B765;1104 1175 11AC;B765;1104 1175 11AC; # (띥; 띥; 띥; 띥; 띥; ) HANGUL SYLLABLE DDINJ +B766;B766;1104 1175 11AD;B766;1104 1175 11AD; # (띦; 띦; 띦; 띦; 띦; ) HANGUL SYLLABLE DDINH +B767;B767;1104 1175 11AE;B767;1104 1175 11AE; # (띧; 띧; 띧; 띧; 띧; ) HANGUL SYLLABLE DDID +B768;B768;1104 1175 11AF;B768;1104 1175 11AF; # (띨; 띨; 띨; 띨; 띨; ) HANGUL SYLLABLE DDIL +B769;B769;1104 1175 11B0;B769;1104 1175 11B0; # (띩; 띩; 띩; 띩; 띩; ) HANGUL SYLLABLE DDILG +B76A;B76A;1104 1175 11B1;B76A;1104 1175 11B1; # (띪; 띪; 띪; 띪; 띪; ) HANGUL SYLLABLE DDILM +B76B;B76B;1104 1175 11B2;B76B;1104 1175 11B2; # (띫; 띫; 띫; 띫; 띫; ) HANGUL SYLLABLE DDILB +B76C;B76C;1104 1175 11B3;B76C;1104 1175 11B3; # (띬; 띬; 띬; 띬; 띬; ) HANGUL SYLLABLE DDILS +B76D;B76D;1104 1175 11B4;B76D;1104 1175 11B4; # (띭; 띭; 띭; 띭; 띭; ) HANGUL SYLLABLE DDILT +B76E;B76E;1104 1175 11B5;B76E;1104 1175 11B5; # (띮; 띮; 띮; 띮; 띮; ) HANGUL SYLLABLE DDILP +B76F;B76F;1104 1175 11B6;B76F;1104 1175 11B6; # (띯; 띯; 띯; 띯; 띯; ) HANGUL SYLLABLE DDILH +B770;B770;1104 1175 11B7;B770;1104 1175 11B7; # (띰; 띰; 띰; 띰; 띰; ) HANGUL SYLLABLE DDIM +B771;B771;1104 1175 11B8;B771;1104 1175 11B8; # (띱; 띱; 띱; 띱; 띱; ) HANGUL SYLLABLE DDIB +B772;B772;1104 1175 11B9;B772;1104 1175 11B9; # (띲; 띲; 띲; 띲; 띲; ) HANGUL SYLLABLE DDIBS +B773;B773;1104 1175 11BA;B773;1104 1175 11BA; # (띳; 띳; 띳; 띳; 띳; ) HANGUL SYLLABLE DDIS +B774;B774;1104 1175 11BB;B774;1104 1175 11BB; # (띴; 띴; 띴; 띴; 띴; ) HANGUL SYLLABLE DDISS +B775;B775;1104 1175 11BC;B775;1104 1175 11BC; # (띵; 띵; 띵; 띵; 띵; ) HANGUL SYLLABLE DDING +B776;B776;1104 1175 11BD;B776;1104 1175 11BD; # (띶; 띶; 띶; 띶; 띶; ) HANGUL SYLLABLE DDIJ +B777;B777;1104 1175 11BE;B777;1104 1175 11BE; # (띷; 띷; 띷; 띷; 띷; ) HANGUL SYLLABLE DDIC +B778;B778;1104 1175 11BF;B778;1104 1175 11BF; # (띸; 띸; 띸; 띸; 띸; ) HANGUL SYLLABLE DDIK +B779;B779;1104 1175 11C0;B779;1104 1175 11C0; # (띹; 띹; 띹; 띹; 띹; ) HANGUL SYLLABLE DDIT +B77A;B77A;1104 1175 11C1;B77A;1104 1175 11C1; # (띺; 띺; 띺; 띺; 띺; ) HANGUL SYLLABLE DDIP +B77B;B77B;1104 1175 11C2;B77B;1104 1175 11C2; # (띻; 띻; 띻; 띻; 띻; ) HANGUL SYLLABLE DDIH +B77C;B77C;1105 1161;B77C;1105 1161; # (라; 라; 라; 라; 라; ) HANGUL SYLLABLE RA +B77D;B77D;1105 1161 11A8;B77D;1105 1161 11A8; # (락; 락; 락; 락; 락; ) HANGUL SYLLABLE RAG +B77E;B77E;1105 1161 11A9;B77E;1105 1161 11A9; # (띾; 띾; 띾; 띾; 띾; ) HANGUL SYLLABLE RAGG +B77F;B77F;1105 1161 11AA;B77F;1105 1161 11AA; # (띿; 띿; 띿; 띿; 띿; ) HANGUL SYLLABLE RAGS +B780;B780;1105 1161 11AB;B780;1105 1161 11AB; # (란; 란; 란; 란; 란; ) HANGUL SYLLABLE RAN +B781;B781;1105 1161 11AC;B781;1105 1161 11AC; # (랁; 랁; 랁; 랁; 랁; ) HANGUL SYLLABLE RANJ +B782;B782;1105 1161 11AD;B782;1105 1161 11AD; # (랂; 랂; 랂; 랂; 랂; ) HANGUL SYLLABLE RANH +B783;B783;1105 1161 11AE;B783;1105 1161 11AE; # (랃; 랃; 랃; 랃; 랃; ) HANGUL SYLLABLE RAD +B784;B784;1105 1161 11AF;B784;1105 1161 11AF; # (랄; 랄; 랄; 랄; 랄; ) HANGUL SYLLABLE RAL +B785;B785;1105 1161 11B0;B785;1105 1161 11B0; # (랅; 랅; 랅; 랅; 랅; ) HANGUL SYLLABLE RALG +B786;B786;1105 1161 11B1;B786;1105 1161 11B1; # (랆; 랆; 랆; 랆; 랆; ) HANGUL SYLLABLE RALM +B787;B787;1105 1161 11B2;B787;1105 1161 11B2; # (랇; 랇; 랇; 랇; 랇; ) HANGUL SYLLABLE RALB +B788;B788;1105 1161 11B3;B788;1105 1161 11B3; # (랈; 랈; 랈; 랈; 랈; ) HANGUL SYLLABLE RALS +B789;B789;1105 1161 11B4;B789;1105 1161 11B4; # (랉; 랉; 랉; 랉; 랉; ) HANGUL SYLLABLE RALT +B78A;B78A;1105 1161 11B5;B78A;1105 1161 11B5; # (랊; 랊; 랊; 랊; 랊; ) HANGUL SYLLABLE RALP +B78B;B78B;1105 1161 11B6;B78B;1105 1161 11B6; # (랋; 랋; 랋; 랋; 랋; ) HANGUL SYLLABLE RALH +B78C;B78C;1105 1161 11B7;B78C;1105 1161 11B7; # (람; 람; 람; 람; 람; ) HANGUL SYLLABLE RAM +B78D;B78D;1105 1161 11B8;B78D;1105 1161 11B8; # (랍; 랍; 랍; 랍; 랍; ) HANGUL SYLLABLE RAB +B78E;B78E;1105 1161 11B9;B78E;1105 1161 11B9; # (랎; 랎; 랎; 랎; 랎; ) HANGUL SYLLABLE RABS +B78F;B78F;1105 1161 11BA;B78F;1105 1161 11BA; # (랏; 랏; 랏; 랏; 랏; ) HANGUL SYLLABLE RAS +B790;B790;1105 1161 11BB;B790;1105 1161 11BB; # (랐; 랐; 랐; 랐; 랐; ) HANGUL SYLLABLE RASS +B791;B791;1105 1161 11BC;B791;1105 1161 11BC; # (랑; 랑; 랑; 랑; 랑; ) HANGUL SYLLABLE RANG +B792;B792;1105 1161 11BD;B792;1105 1161 11BD; # (랒; 랒; 랒; 랒; 랒; ) HANGUL SYLLABLE RAJ +B793;B793;1105 1161 11BE;B793;1105 1161 11BE; # (랓; 랓; 랓; 랓; 랓; ) HANGUL SYLLABLE RAC +B794;B794;1105 1161 11BF;B794;1105 1161 11BF; # (랔; 랔; 랔; 랔; 랔; ) HANGUL SYLLABLE RAK +B795;B795;1105 1161 11C0;B795;1105 1161 11C0; # (랕; 랕; 랕; 랕; 랕; ) HANGUL SYLLABLE RAT +B796;B796;1105 1161 11C1;B796;1105 1161 11C1; # (랖; 랖; 랖; 랖; 랖; ) HANGUL SYLLABLE RAP +B797;B797;1105 1161 11C2;B797;1105 1161 11C2; # (랗; 랗; 랗; 랗; 랗; ) HANGUL SYLLABLE RAH +B798;B798;1105 1162;B798;1105 1162; # (래; 래; 래; 래; 래; ) HANGUL SYLLABLE RAE +B799;B799;1105 1162 11A8;B799;1105 1162 11A8; # (랙; 랙; 랙; 랙; 랙; ) HANGUL SYLLABLE RAEG +B79A;B79A;1105 1162 11A9;B79A;1105 1162 11A9; # (랚; 랚; 랚; 랚; 랚; ) HANGUL SYLLABLE RAEGG +B79B;B79B;1105 1162 11AA;B79B;1105 1162 11AA; # (랛; 랛; 랛; 랛; 랛; ) HANGUL SYLLABLE RAEGS +B79C;B79C;1105 1162 11AB;B79C;1105 1162 11AB; # (랜; 랜; 랜; 랜; 랜; ) HANGUL SYLLABLE RAEN +B79D;B79D;1105 1162 11AC;B79D;1105 1162 11AC; # (랝; 랝; 랝; 랝; 랝; ) HANGUL SYLLABLE RAENJ +B79E;B79E;1105 1162 11AD;B79E;1105 1162 11AD; # (랞; 랞; 랞; 랞; 랞; ) HANGUL SYLLABLE RAENH +B79F;B79F;1105 1162 11AE;B79F;1105 1162 11AE; # (랟; 랟; 랟; 랟; 랟; ) HANGUL SYLLABLE RAED +B7A0;B7A0;1105 1162 11AF;B7A0;1105 1162 11AF; # (랠; 랠; 랠; 랠; 랠; ) HANGUL SYLLABLE RAEL +B7A1;B7A1;1105 1162 11B0;B7A1;1105 1162 11B0; # (랡; 랡; 랡; 랡; 랡; ) HANGUL SYLLABLE RAELG +B7A2;B7A2;1105 1162 11B1;B7A2;1105 1162 11B1; # (랢; 랢; 랢; 랢; 랢; ) HANGUL SYLLABLE RAELM +B7A3;B7A3;1105 1162 11B2;B7A3;1105 1162 11B2; # (랣; 랣; 랣; 랣; 랣; ) HANGUL SYLLABLE RAELB +B7A4;B7A4;1105 1162 11B3;B7A4;1105 1162 11B3; # (랤; 랤; 랤; 랤; 랤; ) HANGUL SYLLABLE RAELS +B7A5;B7A5;1105 1162 11B4;B7A5;1105 1162 11B4; # (랥; 랥; 랥; 랥; 랥; ) HANGUL SYLLABLE RAELT +B7A6;B7A6;1105 1162 11B5;B7A6;1105 1162 11B5; # (랦; 랦; 랦; 랦; 랦; ) HANGUL SYLLABLE RAELP +B7A7;B7A7;1105 1162 11B6;B7A7;1105 1162 11B6; # (랧; 랧; 랧; 랧; 랧; ) HANGUL SYLLABLE RAELH +B7A8;B7A8;1105 1162 11B7;B7A8;1105 1162 11B7; # (램; 램; 램; 램; 램; ) HANGUL SYLLABLE RAEM +B7A9;B7A9;1105 1162 11B8;B7A9;1105 1162 11B8; # (랩; 랩; 랩; 랩; 랩; ) HANGUL SYLLABLE RAEB +B7AA;B7AA;1105 1162 11B9;B7AA;1105 1162 11B9; # (랪; 랪; 랪; 랪; 랪; ) HANGUL SYLLABLE RAEBS +B7AB;B7AB;1105 1162 11BA;B7AB;1105 1162 11BA; # (랫; 랫; 랫; 랫; 랫; ) HANGUL SYLLABLE RAES +B7AC;B7AC;1105 1162 11BB;B7AC;1105 1162 11BB; # (랬; 랬; 랬; 랬; 랬; ) HANGUL SYLLABLE RAESS +B7AD;B7AD;1105 1162 11BC;B7AD;1105 1162 11BC; # (랭; 랭; 랭; 랭; 랭; ) HANGUL SYLLABLE RAENG +B7AE;B7AE;1105 1162 11BD;B7AE;1105 1162 11BD; # (랮; 랮; 랮; 랮; 랮; ) HANGUL SYLLABLE RAEJ +B7AF;B7AF;1105 1162 11BE;B7AF;1105 1162 11BE; # (랯; 랯; 랯; 랯; 랯; ) HANGUL SYLLABLE RAEC +B7B0;B7B0;1105 1162 11BF;B7B0;1105 1162 11BF; # (랰; 랰; 랰; 랰; 랰; ) HANGUL SYLLABLE RAEK +B7B1;B7B1;1105 1162 11C0;B7B1;1105 1162 11C0; # (랱; 랱; 랱; 랱; 랱; ) HANGUL SYLLABLE RAET +B7B2;B7B2;1105 1162 11C1;B7B2;1105 1162 11C1; # (랲; 랲; 랲; 랲; 랲; ) HANGUL SYLLABLE RAEP +B7B3;B7B3;1105 1162 11C2;B7B3;1105 1162 11C2; # (랳; 랳; 랳; 랳; 랳; ) HANGUL SYLLABLE RAEH +B7B4;B7B4;1105 1163;B7B4;1105 1163; # (랴; 랴; 랴; 랴; 랴; ) HANGUL SYLLABLE RYA +B7B5;B7B5;1105 1163 11A8;B7B5;1105 1163 11A8; # (략; 략; 략; 략; 략; ) HANGUL SYLLABLE RYAG +B7B6;B7B6;1105 1163 11A9;B7B6;1105 1163 11A9; # (랶; 랶; 랶; 랶; 랶; ) HANGUL SYLLABLE RYAGG +B7B7;B7B7;1105 1163 11AA;B7B7;1105 1163 11AA; # (랷; 랷; 랷; 랷; 랷; ) HANGUL SYLLABLE RYAGS +B7B8;B7B8;1105 1163 11AB;B7B8;1105 1163 11AB; # (랸; 랸; 랸; 랸; 랸; ) HANGUL SYLLABLE RYAN +B7B9;B7B9;1105 1163 11AC;B7B9;1105 1163 11AC; # (랹; 랹; 랹; 랹; 랹; ) HANGUL SYLLABLE RYANJ +B7BA;B7BA;1105 1163 11AD;B7BA;1105 1163 11AD; # (랺; 랺; 랺; 랺; 랺; ) HANGUL SYLLABLE RYANH +B7BB;B7BB;1105 1163 11AE;B7BB;1105 1163 11AE; # (랻; 랻; 랻; 랻; 랻; ) HANGUL SYLLABLE RYAD +B7BC;B7BC;1105 1163 11AF;B7BC;1105 1163 11AF; # (랼; 랼; 랼; 랼; 랼; ) HANGUL SYLLABLE RYAL +B7BD;B7BD;1105 1163 11B0;B7BD;1105 1163 11B0; # (랽; 랽; 랽; 랽; 랽; ) HANGUL SYLLABLE RYALG +B7BE;B7BE;1105 1163 11B1;B7BE;1105 1163 11B1; # (랾; 랾; 랾; 랾; 랾; ) HANGUL SYLLABLE RYALM +B7BF;B7BF;1105 1163 11B2;B7BF;1105 1163 11B2; # (랿; 랿; 랿; 랿; 랿; ) HANGUL SYLLABLE RYALB +B7C0;B7C0;1105 1163 11B3;B7C0;1105 1163 11B3; # (럀; 럀; 럀; 럀; 럀; ) HANGUL SYLLABLE RYALS +B7C1;B7C1;1105 1163 11B4;B7C1;1105 1163 11B4; # (럁; 럁; 럁; 럁; 럁; ) HANGUL SYLLABLE RYALT +B7C2;B7C2;1105 1163 11B5;B7C2;1105 1163 11B5; # (럂; 럂; 럂; 럂; 럂; ) HANGUL SYLLABLE RYALP +B7C3;B7C3;1105 1163 11B6;B7C3;1105 1163 11B6; # (럃; 럃; 럃; 럃; 럃; ) HANGUL SYLLABLE RYALH +B7C4;B7C4;1105 1163 11B7;B7C4;1105 1163 11B7; # (럄; 럄; 럄; 럄; 럄; ) HANGUL SYLLABLE RYAM +B7C5;B7C5;1105 1163 11B8;B7C5;1105 1163 11B8; # (럅; 럅; 럅; 럅; 럅; ) HANGUL SYLLABLE RYAB +B7C6;B7C6;1105 1163 11B9;B7C6;1105 1163 11B9; # (럆; 럆; 럆; 럆; 럆; ) HANGUL SYLLABLE RYABS +B7C7;B7C7;1105 1163 11BA;B7C7;1105 1163 11BA; # (럇; 럇; 럇; 럇; 럇; ) HANGUL SYLLABLE RYAS +B7C8;B7C8;1105 1163 11BB;B7C8;1105 1163 11BB; # (럈; 럈; 럈; 럈; 럈; ) HANGUL SYLLABLE RYASS +B7C9;B7C9;1105 1163 11BC;B7C9;1105 1163 11BC; # (량; 량; 량; 량; 량; ) HANGUL SYLLABLE RYANG +B7CA;B7CA;1105 1163 11BD;B7CA;1105 1163 11BD; # (럊; 럊; 럊; 럊; 럊; ) HANGUL SYLLABLE RYAJ +B7CB;B7CB;1105 1163 11BE;B7CB;1105 1163 11BE; # (럋; 럋; 럋; 럋; 럋; ) HANGUL SYLLABLE RYAC +B7CC;B7CC;1105 1163 11BF;B7CC;1105 1163 11BF; # (럌; 럌; 럌; 럌; 럌; ) HANGUL SYLLABLE RYAK +B7CD;B7CD;1105 1163 11C0;B7CD;1105 1163 11C0; # (럍; 럍; 럍; 럍; 럍; ) HANGUL SYLLABLE RYAT +B7CE;B7CE;1105 1163 11C1;B7CE;1105 1163 11C1; # (럎; 럎; 럎; 럎; 럎; ) HANGUL SYLLABLE RYAP +B7CF;B7CF;1105 1163 11C2;B7CF;1105 1163 11C2; # (럏; 럏; 럏; 럏; 럏; ) HANGUL SYLLABLE RYAH +B7D0;B7D0;1105 1164;B7D0;1105 1164; # (럐; 럐; 럐; 럐; 럐; ) HANGUL SYLLABLE RYAE +B7D1;B7D1;1105 1164 11A8;B7D1;1105 1164 11A8; # (럑; 럑; 럑; 럑; 럑; ) HANGUL SYLLABLE RYAEG +B7D2;B7D2;1105 1164 11A9;B7D2;1105 1164 11A9; # (럒; 럒; 럒; 럒; 럒; ) HANGUL SYLLABLE RYAEGG +B7D3;B7D3;1105 1164 11AA;B7D3;1105 1164 11AA; # (럓; 럓; 럓; 럓; 럓; ) HANGUL SYLLABLE RYAEGS +B7D4;B7D4;1105 1164 11AB;B7D4;1105 1164 11AB; # (럔; 럔; 럔; 럔; 럔; ) HANGUL SYLLABLE RYAEN +B7D5;B7D5;1105 1164 11AC;B7D5;1105 1164 11AC; # (럕; 럕; 럕; 럕; 럕; ) HANGUL SYLLABLE RYAENJ +B7D6;B7D6;1105 1164 11AD;B7D6;1105 1164 11AD; # (럖; 럖; 럖; 럖; 럖; ) HANGUL SYLLABLE RYAENH +B7D7;B7D7;1105 1164 11AE;B7D7;1105 1164 11AE; # (럗; 럗; 럗; 럗; 럗; ) HANGUL SYLLABLE RYAED +B7D8;B7D8;1105 1164 11AF;B7D8;1105 1164 11AF; # (럘; 럘; 럘; 럘; 럘; ) HANGUL SYLLABLE RYAEL +B7D9;B7D9;1105 1164 11B0;B7D9;1105 1164 11B0; # (럙; 럙; 럙; 럙; 럙; ) HANGUL SYLLABLE RYAELG +B7DA;B7DA;1105 1164 11B1;B7DA;1105 1164 11B1; # (럚; 럚; 럚; 럚; 럚; ) HANGUL SYLLABLE RYAELM +B7DB;B7DB;1105 1164 11B2;B7DB;1105 1164 11B2; # (럛; 럛; 럛; 럛; 럛; ) HANGUL SYLLABLE RYAELB +B7DC;B7DC;1105 1164 11B3;B7DC;1105 1164 11B3; # (럜; 럜; 럜; 럜; 럜; ) HANGUL SYLLABLE RYAELS +B7DD;B7DD;1105 1164 11B4;B7DD;1105 1164 11B4; # (럝; 럝; 럝; 럝; 럝; ) HANGUL SYLLABLE RYAELT +B7DE;B7DE;1105 1164 11B5;B7DE;1105 1164 11B5; # (럞; 럞; 럞; 럞; 럞; ) HANGUL SYLLABLE RYAELP +B7DF;B7DF;1105 1164 11B6;B7DF;1105 1164 11B6; # (럟; 럟; 럟; 럟; 럟; ) HANGUL SYLLABLE RYAELH +B7E0;B7E0;1105 1164 11B7;B7E0;1105 1164 11B7; # (럠; 럠; 럠; 럠; 럠; ) HANGUL SYLLABLE RYAEM +B7E1;B7E1;1105 1164 11B8;B7E1;1105 1164 11B8; # (럡; 럡; 럡; 럡; 럡; ) HANGUL SYLLABLE RYAEB +B7E2;B7E2;1105 1164 11B9;B7E2;1105 1164 11B9; # (럢; 럢; 럢; 럢; 럢; ) HANGUL SYLLABLE RYAEBS +B7E3;B7E3;1105 1164 11BA;B7E3;1105 1164 11BA; # (럣; 럣; 럣; 럣; 럣; ) HANGUL SYLLABLE RYAES +B7E4;B7E4;1105 1164 11BB;B7E4;1105 1164 11BB; # (럤; 럤; 럤; 럤; 럤; ) HANGUL SYLLABLE RYAESS +B7E5;B7E5;1105 1164 11BC;B7E5;1105 1164 11BC; # (럥; 럥; 럥; 럥; 럥; ) HANGUL SYLLABLE RYAENG +B7E6;B7E6;1105 1164 11BD;B7E6;1105 1164 11BD; # (럦; 럦; 럦; 럦; 럦; ) HANGUL SYLLABLE RYAEJ +B7E7;B7E7;1105 1164 11BE;B7E7;1105 1164 11BE; # (럧; 럧; 럧; 럧; 럧; ) HANGUL SYLLABLE RYAEC +B7E8;B7E8;1105 1164 11BF;B7E8;1105 1164 11BF; # (럨; 럨; 럨; 럨; 럨; ) HANGUL SYLLABLE RYAEK +B7E9;B7E9;1105 1164 11C0;B7E9;1105 1164 11C0; # (럩; 럩; 럩; 럩; 럩; ) HANGUL SYLLABLE RYAET +B7EA;B7EA;1105 1164 11C1;B7EA;1105 1164 11C1; # (럪; 럪; 럪; 럪; 럪; ) HANGUL SYLLABLE RYAEP +B7EB;B7EB;1105 1164 11C2;B7EB;1105 1164 11C2; # (럫; 럫; 럫; 럫; 럫; ) HANGUL SYLLABLE RYAEH +B7EC;B7EC;1105 1165;B7EC;1105 1165; # (러; 러; 러; 러; 러; ) HANGUL SYLLABLE REO +B7ED;B7ED;1105 1165 11A8;B7ED;1105 1165 11A8; # (럭; 럭; 럭; 럭; 럭; ) HANGUL SYLLABLE REOG +B7EE;B7EE;1105 1165 11A9;B7EE;1105 1165 11A9; # (럮; 럮; 럮; 럮; 럮; ) HANGUL SYLLABLE REOGG +B7EF;B7EF;1105 1165 11AA;B7EF;1105 1165 11AA; # (럯; 럯; 럯; 럯; 럯; ) HANGUL SYLLABLE REOGS +B7F0;B7F0;1105 1165 11AB;B7F0;1105 1165 11AB; # (런; 런; 런; 런; 런; ) HANGUL SYLLABLE REON +B7F1;B7F1;1105 1165 11AC;B7F1;1105 1165 11AC; # (럱; 럱; 럱; 럱; 럱; ) HANGUL SYLLABLE REONJ +B7F2;B7F2;1105 1165 11AD;B7F2;1105 1165 11AD; # (럲; 럲; 럲; 럲; 럲; ) HANGUL SYLLABLE REONH +B7F3;B7F3;1105 1165 11AE;B7F3;1105 1165 11AE; # (럳; 럳; 럳; 럳; 럳; ) HANGUL SYLLABLE REOD +B7F4;B7F4;1105 1165 11AF;B7F4;1105 1165 11AF; # (럴; 럴; 럴; 럴; 럴; ) HANGUL SYLLABLE REOL +B7F5;B7F5;1105 1165 11B0;B7F5;1105 1165 11B0; # (럵; 럵; 럵; 럵; 럵; ) HANGUL SYLLABLE REOLG +B7F6;B7F6;1105 1165 11B1;B7F6;1105 1165 11B1; # (럶; 럶; 럶; 럶; 럶; ) HANGUL SYLLABLE REOLM +B7F7;B7F7;1105 1165 11B2;B7F7;1105 1165 11B2; # (럷; 럷; 럷; 럷; 럷; ) HANGUL SYLLABLE REOLB +B7F8;B7F8;1105 1165 11B3;B7F8;1105 1165 11B3; # (럸; 럸; 럸; 럸; 럸; ) HANGUL SYLLABLE REOLS +B7F9;B7F9;1105 1165 11B4;B7F9;1105 1165 11B4; # (럹; 럹; 럹; 럹; 럹; ) HANGUL SYLLABLE REOLT +B7FA;B7FA;1105 1165 11B5;B7FA;1105 1165 11B5; # (럺; 럺; 럺; 럺; 럺; ) HANGUL SYLLABLE REOLP +B7FB;B7FB;1105 1165 11B6;B7FB;1105 1165 11B6; # (럻; 럻; 럻; 럻; 럻; ) HANGUL SYLLABLE REOLH +B7FC;B7FC;1105 1165 11B7;B7FC;1105 1165 11B7; # (럼; 럼; 럼; 럼; 럼; ) HANGUL SYLLABLE REOM +B7FD;B7FD;1105 1165 11B8;B7FD;1105 1165 11B8; # (럽; 럽; 럽; 럽; 럽; ) HANGUL SYLLABLE REOB +B7FE;B7FE;1105 1165 11B9;B7FE;1105 1165 11B9; # (럾; 럾; 럾; 럾; 럾; ) HANGUL SYLLABLE REOBS +B7FF;B7FF;1105 1165 11BA;B7FF;1105 1165 11BA; # (럿; 럿; 럿; 럿; 럿; ) HANGUL SYLLABLE REOS +B800;B800;1105 1165 11BB;B800;1105 1165 11BB; # (렀; 렀; 렀; 렀; 렀; ) HANGUL SYLLABLE REOSS +B801;B801;1105 1165 11BC;B801;1105 1165 11BC; # (렁; 렁; 렁; 렁; 렁; ) HANGUL SYLLABLE REONG +B802;B802;1105 1165 11BD;B802;1105 1165 11BD; # (렂; 렂; 렂; 렂; 렂; ) HANGUL SYLLABLE REOJ +B803;B803;1105 1165 11BE;B803;1105 1165 11BE; # (렃; 렃; 렃; 렃; 렃; ) HANGUL SYLLABLE REOC +B804;B804;1105 1165 11BF;B804;1105 1165 11BF; # (렄; 렄; 렄; 렄; 렄; ) HANGUL SYLLABLE REOK +B805;B805;1105 1165 11C0;B805;1105 1165 11C0; # (렅; 렅; 렅; 렅; 렅; ) HANGUL SYLLABLE REOT +B806;B806;1105 1165 11C1;B806;1105 1165 11C1; # (렆; 렆; 렆; 렆; 렆; ) HANGUL SYLLABLE REOP +B807;B807;1105 1165 11C2;B807;1105 1165 11C2; # (렇; 렇; 렇; 렇; 렇; ) HANGUL SYLLABLE REOH +B808;B808;1105 1166;B808;1105 1166; # (레; 레; 레; 레; 레; ) HANGUL SYLLABLE RE +B809;B809;1105 1166 11A8;B809;1105 1166 11A8; # (렉; 렉; 렉; 렉; 렉; ) HANGUL SYLLABLE REG +B80A;B80A;1105 1166 11A9;B80A;1105 1166 11A9; # (렊; 렊; 렊; 렊; 렊; ) HANGUL SYLLABLE REGG +B80B;B80B;1105 1166 11AA;B80B;1105 1166 11AA; # (렋; 렋; 렋; 렋; 렋; ) HANGUL SYLLABLE REGS +B80C;B80C;1105 1166 11AB;B80C;1105 1166 11AB; # (렌; 렌; 렌; 렌; 렌; ) HANGUL SYLLABLE REN +B80D;B80D;1105 1166 11AC;B80D;1105 1166 11AC; # (렍; 렍; 렍; 렍; 렍; ) HANGUL SYLLABLE RENJ +B80E;B80E;1105 1166 11AD;B80E;1105 1166 11AD; # (렎; 렎; 렎; 렎; 렎; ) HANGUL SYLLABLE RENH +B80F;B80F;1105 1166 11AE;B80F;1105 1166 11AE; # (렏; 렏; 렏; 렏; 렏; ) HANGUL SYLLABLE RED +B810;B810;1105 1166 11AF;B810;1105 1166 11AF; # (렐; 렐; 렐; 렐; 렐; ) HANGUL SYLLABLE REL +B811;B811;1105 1166 11B0;B811;1105 1166 11B0; # (렑; 렑; 렑; 렑; 렑; ) HANGUL SYLLABLE RELG +B812;B812;1105 1166 11B1;B812;1105 1166 11B1; # (렒; 렒; 렒; 렒; 렒; ) HANGUL SYLLABLE RELM +B813;B813;1105 1166 11B2;B813;1105 1166 11B2; # (렓; 렓; 렓; 렓; 렓; ) HANGUL SYLLABLE RELB +B814;B814;1105 1166 11B3;B814;1105 1166 11B3; # (렔; 렔; 렔; 렔; 렔; ) HANGUL SYLLABLE RELS +B815;B815;1105 1166 11B4;B815;1105 1166 11B4; # (렕; 렕; 렕; 렕; 렕; ) HANGUL SYLLABLE RELT +B816;B816;1105 1166 11B5;B816;1105 1166 11B5; # (렖; 렖; 렖; 렖; 렖; ) HANGUL SYLLABLE RELP +B817;B817;1105 1166 11B6;B817;1105 1166 11B6; # (렗; 렗; 렗; 렗; 렗; ) HANGUL SYLLABLE RELH +B818;B818;1105 1166 11B7;B818;1105 1166 11B7; # (렘; 렘; 렘; 렘; 렘; ) HANGUL SYLLABLE REM +B819;B819;1105 1166 11B8;B819;1105 1166 11B8; # (렙; 렙; 렙; 렙; 렙; ) HANGUL SYLLABLE REB +B81A;B81A;1105 1166 11B9;B81A;1105 1166 11B9; # (렚; 렚; 렚; 렚; 렚; ) HANGUL SYLLABLE REBS +B81B;B81B;1105 1166 11BA;B81B;1105 1166 11BA; # (렛; 렛; 렛; 렛; 렛; ) HANGUL SYLLABLE RES +B81C;B81C;1105 1166 11BB;B81C;1105 1166 11BB; # (렜; 렜; 렜; 렜; 렜; ) HANGUL SYLLABLE RESS +B81D;B81D;1105 1166 11BC;B81D;1105 1166 11BC; # (렝; 렝; 렝; 렝; 렝; ) HANGUL SYLLABLE RENG +B81E;B81E;1105 1166 11BD;B81E;1105 1166 11BD; # (렞; 렞; 렞; 렞; 렞; ) HANGUL SYLLABLE REJ +B81F;B81F;1105 1166 11BE;B81F;1105 1166 11BE; # (렟; 렟; 렟; 렟; 렟; ) HANGUL SYLLABLE REC +B820;B820;1105 1166 11BF;B820;1105 1166 11BF; # (렠; 렠; 렠; 렠; 렠; ) HANGUL SYLLABLE REK +B821;B821;1105 1166 11C0;B821;1105 1166 11C0; # (렡; 렡; 렡; 렡; 렡; ) HANGUL SYLLABLE RET +B822;B822;1105 1166 11C1;B822;1105 1166 11C1; # (렢; 렢; 렢; 렢; 렢; ) HANGUL SYLLABLE REP +B823;B823;1105 1166 11C2;B823;1105 1166 11C2; # (렣; 렣; 렣; 렣; 렣; ) HANGUL SYLLABLE REH +B824;B824;1105 1167;B824;1105 1167; # (려; 려; 려; 려; 려; ) HANGUL SYLLABLE RYEO +B825;B825;1105 1167 11A8;B825;1105 1167 11A8; # (력; 력; 력; 력; 력; ) HANGUL SYLLABLE RYEOG +B826;B826;1105 1167 11A9;B826;1105 1167 11A9; # (렦; 렦; 렦; 렦; 렦; ) HANGUL SYLLABLE RYEOGG +B827;B827;1105 1167 11AA;B827;1105 1167 11AA; # (렧; 렧; 렧; 렧; 렧; ) HANGUL SYLLABLE RYEOGS +B828;B828;1105 1167 11AB;B828;1105 1167 11AB; # (련; 련; 련; 련; 련; ) HANGUL SYLLABLE RYEON +B829;B829;1105 1167 11AC;B829;1105 1167 11AC; # (렩; 렩; 렩; 렩; 렩; ) HANGUL SYLLABLE RYEONJ +B82A;B82A;1105 1167 11AD;B82A;1105 1167 11AD; # (렪; 렪; 렪; 렪; 렪; ) HANGUL SYLLABLE RYEONH +B82B;B82B;1105 1167 11AE;B82B;1105 1167 11AE; # (렫; 렫; 렫; 렫; 렫; ) HANGUL SYLLABLE RYEOD +B82C;B82C;1105 1167 11AF;B82C;1105 1167 11AF; # (렬; 렬; 렬; 렬; 렬; ) HANGUL SYLLABLE RYEOL +B82D;B82D;1105 1167 11B0;B82D;1105 1167 11B0; # (렭; 렭; 렭; 렭; 렭; ) HANGUL SYLLABLE RYEOLG +B82E;B82E;1105 1167 11B1;B82E;1105 1167 11B1; # (렮; 렮; 렮; 렮; 렮; ) HANGUL SYLLABLE RYEOLM +B82F;B82F;1105 1167 11B2;B82F;1105 1167 11B2; # (렯; 렯; 렯; 렯; 렯; ) HANGUL SYLLABLE RYEOLB +B830;B830;1105 1167 11B3;B830;1105 1167 11B3; # (렰; 렰; 렰; 렰; 렰; ) HANGUL SYLLABLE RYEOLS +B831;B831;1105 1167 11B4;B831;1105 1167 11B4; # (렱; 렱; 렱; 렱; 렱; ) HANGUL SYLLABLE RYEOLT +B832;B832;1105 1167 11B5;B832;1105 1167 11B5; # (렲; 렲; 렲; 렲; 렲; ) HANGUL SYLLABLE RYEOLP +B833;B833;1105 1167 11B6;B833;1105 1167 11B6; # (렳; 렳; 렳; 렳; 렳; ) HANGUL SYLLABLE RYEOLH +B834;B834;1105 1167 11B7;B834;1105 1167 11B7; # (렴; 렴; 렴; 렴; 렴; ) HANGUL SYLLABLE RYEOM +B835;B835;1105 1167 11B8;B835;1105 1167 11B8; # (렵; 렵; 렵; 렵; 렵; ) HANGUL SYLLABLE RYEOB +B836;B836;1105 1167 11B9;B836;1105 1167 11B9; # (렶; 렶; 렶; 렶; 렶; ) HANGUL SYLLABLE RYEOBS +B837;B837;1105 1167 11BA;B837;1105 1167 11BA; # (렷; 렷; 렷; 렷; 렷; ) HANGUL SYLLABLE RYEOS +B838;B838;1105 1167 11BB;B838;1105 1167 11BB; # (렸; 렸; 렸; 렸; 렸; ) HANGUL SYLLABLE RYEOSS +B839;B839;1105 1167 11BC;B839;1105 1167 11BC; # (령; 령; 령; 령; 령; ) HANGUL SYLLABLE RYEONG +B83A;B83A;1105 1167 11BD;B83A;1105 1167 11BD; # (렺; 렺; 렺; 렺; 렺; ) HANGUL SYLLABLE RYEOJ +B83B;B83B;1105 1167 11BE;B83B;1105 1167 11BE; # (렻; 렻; 렻; 렻; 렻; ) HANGUL SYLLABLE RYEOC +B83C;B83C;1105 1167 11BF;B83C;1105 1167 11BF; # (렼; 렼; 렼; 렼; 렼; ) HANGUL SYLLABLE RYEOK +B83D;B83D;1105 1167 11C0;B83D;1105 1167 11C0; # (렽; 렽; 렽; 렽; 렽; ) HANGUL SYLLABLE RYEOT +B83E;B83E;1105 1167 11C1;B83E;1105 1167 11C1; # (렾; 렾; 렾; 렾; 렾; ) HANGUL SYLLABLE RYEOP +B83F;B83F;1105 1167 11C2;B83F;1105 1167 11C2; # (렿; 렿; 렿; 렿; 렿; ) HANGUL SYLLABLE RYEOH +B840;B840;1105 1168;B840;1105 1168; # (례; 례; 례; 례; 례; ) HANGUL SYLLABLE RYE +B841;B841;1105 1168 11A8;B841;1105 1168 11A8; # (롁; 롁; 롁; 롁; 롁; ) HANGUL SYLLABLE RYEG +B842;B842;1105 1168 11A9;B842;1105 1168 11A9; # (롂; 롂; 롂; 롂; 롂; ) HANGUL SYLLABLE RYEGG +B843;B843;1105 1168 11AA;B843;1105 1168 11AA; # (롃; 롃; 롃; 롃; 롃; ) HANGUL SYLLABLE RYEGS +B844;B844;1105 1168 11AB;B844;1105 1168 11AB; # (롄; 롄; 롄; 롄; 롄; ) HANGUL SYLLABLE RYEN +B845;B845;1105 1168 11AC;B845;1105 1168 11AC; # (롅; 롅; 롅; 롅; 롅; ) HANGUL SYLLABLE RYENJ +B846;B846;1105 1168 11AD;B846;1105 1168 11AD; # (롆; 롆; 롆; 롆; 롆; ) HANGUL SYLLABLE RYENH +B847;B847;1105 1168 11AE;B847;1105 1168 11AE; # (롇; 롇; 롇; 롇; 롇; ) HANGUL SYLLABLE RYED +B848;B848;1105 1168 11AF;B848;1105 1168 11AF; # (롈; 롈; 롈; 롈; 롈; ) HANGUL SYLLABLE RYEL +B849;B849;1105 1168 11B0;B849;1105 1168 11B0; # (롉; 롉; 롉; 롉; 롉; ) HANGUL SYLLABLE RYELG +B84A;B84A;1105 1168 11B1;B84A;1105 1168 11B1; # (롊; 롊; 롊; 롊; 롊; ) HANGUL SYLLABLE RYELM +B84B;B84B;1105 1168 11B2;B84B;1105 1168 11B2; # (롋; 롋; 롋; 롋; 롋; ) HANGUL SYLLABLE RYELB +B84C;B84C;1105 1168 11B3;B84C;1105 1168 11B3; # (롌; 롌; 롌; 롌; 롌; ) HANGUL SYLLABLE RYELS +B84D;B84D;1105 1168 11B4;B84D;1105 1168 11B4; # (롍; 롍; 롍; 롍; 롍; ) HANGUL SYLLABLE RYELT +B84E;B84E;1105 1168 11B5;B84E;1105 1168 11B5; # (롎; 롎; 롎; 롎; 롎; ) HANGUL SYLLABLE RYELP +B84F;B84F;1105 1168 11B6;B84F;1105 1168 11B6; # (롏; 롏; 롏; 롏; 롏; ) HANGUL SYLLABLE RYELH +B850;B850;1105 1168 11B7;B850;1105 1168 11B7; # (롐; 롐; 롐; 롐; 롐; ) HANGUL SYLLABLE RYEM +B851;B851;1105 1168 11B8;B851;1105 1168 11B8; # (롑; 롑; 롑; 롑; 롑; ) HANGUL SYLLABLE RYEB +B852;B852;1105 1168 11B9;B852;1105 1168 11B9; # (롒; 롒; 롒; 롒; 롒; ) HANGUL SYLLABLE RYEBS +B853;B853;1105 1168 11BA;B853;1105 1168 11BA; # (롓; 롓; 롓; 롓; 롓; ) HANGUL SYLLABLE RYES +B854;B854;1105 1168 11BB;B854;1105 1168 11BB; # (롔; 롔; 롔; 롔; 롔; ) HANGUL SYLLABLE RYESS +B855;B855;1105 1168 11BC;B855;1105 1168 11BC; # (롕; 롕; 롕; 롕; 롕; ) HANGUL SYLLABLE RYENG +B856;B856;1105 1168 11BD;B856;1105 1168 11BD; # (롖; 롖; 롖; 롖; 롖; ) HANGUL SYLLABLE RYEJ +B857;B857;1105 1168 11BE;B857;1105 1168 11BE; # (롗; 롗; 롗; 롗; 롗; ) HANGUL SYLLABLE RYEC +B858;B858;1105 1168 11BF;B858;1105 1168 11BF; # (롘; 롘; 롘; 롘; 롘; ) HANGUL SYLLABLE RYEK +B859;B859;1105 1168 11C0;B859;1105 1168 11C0; # (롙; 롙; 롙; 롙; 롙; ) HANGUL SYLLABLE RYET +B85A;B85A;1105 1168 11C1;B85A;1105 1168 11C1; # (롚; 롚; 롚; 롚; 롚; ) HANGUL SYLLABLE RYEP +B85B;B85B;1105 1168 11C2;B85B;1105 1168 11C2; # (롛; 롛; 롛; 롛; 롛; ) HANGUL SYLLABLE RYEH +B85C;B85C;1105 1169;B85C;1105 1169; # (로; 로; 로; 로; 로; ) HANGUL SYLLABLE RO +B85D;B85D;1105 1169 11A8;B85D;1105 1169 11A8; # (록; 록; 록; 록; 록; ) HANGUL SYLLABLE ROG +B85E;B85E;1105 1169 11A9;B85E;1105 1169 11A9; # (롞; 롞; 롞; 롞; 롞; ) HANGUL SYLLABLE ROGG +B85F;B85F;1105 1169 11AA;B85F;1105 1169 11AA; # (롟; 롟; 롟; 롟; 롟; ) HANGUL SYLLABLE ROGS +B860;B860;1105 1169 11AB;B860;1105 1169 11AB; # (론; 론; 론; 론; 론; ) HANGUL SYLLABLE RON +B861;B861;1105 1169 11AC;B861;1105 1169 11AC; # (롡; 롡; 롡; 롡; 롡; ) HANGUL SYLLABLE RONJ +B862;B862;1105 1169 11AD;B862;1105 1169 11AD; # (롢; 롢; 롢; 롢; 롢; ) HANGUL SYLLABLE RONH +B863;B863;1105 1169 11AE;B863;1105 1169 11AE; # (롣; 롣; 롣; 롣; 롣; ) HANGUL SYLLABLE ROD +B864;B864;1105 1169 11AF;B864;1105 1169 11AF; # (롤; 롤; 롤; 롤; 롤; ) HANGUL SYLLABLE ROL +B865;B865;1105 1169 11B0;B865;1105 1169 11B0; # (롥; 롥; 롥; 롥; 롥; ) HANGUL SYLLABLE ROLG +B866;B866;1105 1169 11B1;B866;1105 1169 11B1; # (롦; 롦; 롦; 롦; 롦; ) HANGUL SYLLABLE ROLM +B867;B867;1105 1169 11B2;B867;1105 1169 11B2; # (롧; 롧; 롧; 롧; 롧; ) HANGUL SYLLABLE ROLB +B868;B868;1105 1169 11B3;B868;1105 1169 11B3; # (롨; 롨; 롨; 롨; 롨; ) HANGUL SYLLABLE ROLS +B869;B869;1105 1169 11B4;B869;1105 1169 11B4; # (롩; 롩; 롩; 롩; 롩; ) HANGUL SYLLABLE ROLT +B86A;B86A;1105 1169 11B5;B86A;1105 1169 11B5; # (롪; 롪; 롪; 롪; 롪; ) HANGUL SYLLABLE ROLP +B86B;B86B;1105 1169 11B6;B86B;1105 1169 11B6; # (롫; 롫; 롫; 롫; 롫; ) HANGUL SYLLABLE ROLH +B86C;B86C;1105 1169 11B7;B86C;1105 1169 11B7; # (롬; 롬; 롬; 롬; 롬; ) HANGUL SYLLABLE ROM +B86D;B86D;1105 1169 11B8;B86D;1105 1169 11B8; # (롭; 롭; 롭; 롭; 롭; ) HANGUL SYLLABLE ROB +B86E;B86E;1105 1169 11B9;B86E;1105 1169 11B9; # (롮; 롮; 롮; 롮; 롮; ) HANGUL SYLLABLE ROBS +B86F;B86F;1105 1169 11BA;B86F;1105 1169 11BA; # (롯; 롯; 롯; 롯; 롯; ) HANGUL SYLLABLE ROS +B870;B870;1105 1169 11BB;B870;1105 1169 11BB; # (롰; 롰; 롰; 롰; 롰; ) HANGUL SYLLABLE ROSS +B871;B871;1105 1169 11BC;B871;1105 1169 11BC; # (롱; 롱; 롱; 롱; 롱; ) HANGUL SYLLABLE RONG +B872;B872;1105 1169 11BD;B872;1105 1169 11BD; # (롲; 롲; 롲; 롲; 롲; ) HANGUL SYLLABLE ROJ +B873;B873;1105 1169 11BE;B873;1105 1169 11BE; # (롳; 롳; 롳; 롳; 롳; ) HANGUL SYLLABLE ROC +B874;B874;1105 1169 11BF;B874;1105 1169 11BF; # (롴; 롴; 롴; 롴; 롴; ) HANGUL SYLLABLE ROK +B875;B875;1105 1169 11C0;B875;1105 1169 11C0; # (롵; 롵; 롵; 롵; 롵; ) HANGUL SYLLABLE ROT +B876;B876;1105 1169 11C1;B876;1105 1169 11C1; # (롶; 롶; 롶; 롶; 롶; ) HANGUL SYLLABLE ROP +B877;B877;1105 1169 11C2;B877;1105 1169 11C2; # (롷; 롷; 롷; 롷; 롷; ) HANGUL SYLLABLE ROH +B878;B878;1105 116A;B878;1105 116A; # (롸; 롸; 롸; 롸; 롸; ) HANGUL SYLLABLE RWA +B879;B879;1105 116A 11A8;B879;1105 116A 11A8; # (롹; 롹; 롹; 롹; 롹; ) HANGUL SYLLABLE RWAG +B87A;B87A;1105 116A 11A9;B87A;1105 116A 11A9; # (롺; 롺; 롺; 롺; 롺; ) HANGUL SYLLABLE RWAGG +B87B;B87B;1105 116A 11AA;B87B;1105 116A 11AA; # (롻; 롻; 롻; 롻; 롻; ) HANGUL SYLLABLE RWAGS +B87C;B87C;1105 116A 11AB;B87C;1105 116A 11AB; # (롼; 롼; 롼; 롼; 롼; ) HANGUL SYLLABLE RWAN +B87D;B87D;1105 116A 11AC;B87D;1105 116A 11AC; # (롽; 롽; 롽; 롽; 롽; ) HANGUL SYLLABLE RWANJ +B87E;B87E;1105 116A 11AD;B87E;1105 116A 11AD; # (롾; 롾; 롾; 롾; 롾; ) HANGUL SYLLABLE RWANH +B87F;B87F;1105 116A 11AE;B87F;1105 116A 11AE; # (롿; 롿; 롿; 롿; 롿; ) HANGUL SYLLABLE RWAD +B880;B880;1105 116A 11AF;B880;1105 116A 11AF; # (뢀; 뢀; 뢀; 뢀; 뢀; ) HANGUL SYLLABLE RWAL +B881;B881;1105 116A 11B0;B881;1105 116A 11B0; # (뢁; 뢁; 뢁; 뢁; 뢁; ) HANGUL SYLLABLE RWALG +B882;B882;1105 116A 11B1;B882;1105 116A 11B1; # (뢂; 뢂; 뢂; 뢂; 뢂; ) HANGUL SYLLABLE RWALM +B883;B883;1105 116A 11B2;B883;1105 116A 11B2; # (뢃; 뢃; 뢃; 뢃; 뢃; ) HANGUL SYLLABLE RWALB +B884;B884;1105 116A 11B3;B884;1105 116A 11B3; # (뢄; 뢄; 뢄; 뢄; 뢄; ) HANGUL SYLLABLE RWALS +B885;B885;1105 116A 11B4;B885;1105 116A 11B4; # (뢅; 뢅; 뢅; 뢅; 뢅; ) HANGUL SYLLABLE RWALT +B886;B886;1105 116A 11B5;B886;1105 116A 11B5; # (뢆; 뢆; 뢆; 뢆; 뢆; ) HANGUL SYLLABLE RWALP +B887;B887;1105 116A 11B6;B887;1105 116A 11B6; # (뢇; 뢇; 뢇; 뢇; 뢇; ) HANGUL SYLLABLE RWALH +B888;B888;1105 116A 11B7;B888;1105 116A 11B7; # (뢈; 뢈; 뢈; 뢈; 뢈; ) HANGUL SYLLABLE RWAM +B889;B889;1105 116A 11B8;B889;1105 116A 11B8; # (뢉; 뢉; 뢉; 뢉; 뢉; ) HANGUL SYLLABLE RWAB +B88A;B88A;1105 116A 11B9;B88A;1105 116A 11B9; # (뢊; 뢊; 뢊; 뢊; 뢊; ) HANGUL SYLLABLE RWABS +B88B;B88B;1105 116A 11BA;B88B;1105 116A 11BA; # (뢋; 뢋; 뢋; 뢋; 뢋; ) HANGUL SYLLABLE RWAS +B88C;B88C;1105 116A 11BB;B88C;1105 116A 11BB; # (뢌; 뢌; 뢌; 뢌; 뢌; ) HANGUL SYLLABLE RWASS +B88D;B88D;1105 116A 11BC;B88D;1105 116A 11BC; # (뢍; 뢍; 뢍; 뢍; 뢍; ) HANGUL SYLLABLE RWANG +B88E;B88E;1105 116A 11BD;B88E;1105 116A 11BD; # (뢎; 뢎; 뢎; 뢎; 뢎; ) HANGUL SYLLABLE RWAJ +B88F;B88F;1105 116A 11BE;B88F;1105 116A 11BE; # (뢏; 뢏; 뢏; 뢏; 뢏; ) HANGUL SYLLABLE RWAC +B890;B890;1105 116A 11BF;B890;1105 116A 11BF; # (뢐; 뢐; 뢐; 뢐; 뢐; ) HANGUL SYLLABLE RWAK +B891;B891;1105 116A 11C0;B891;1105 116A 11C0; # (뢑; 뢑; 뢑; 뢑; 뢑; ) HANGUL SYLLABLE RWAT +B892;B892;1105 116A 11C1;B892;1105 116A 11C1; # (뢒; 뢒; 뢒; 뢒; 뢒; ) HANGUL SYLLABLE RWAP +B893;B893;1105 116A 11C2;B893;1105 116A 11C2; # (뢓; 뢓; 뢓; 뢓; 뢓; ) HANGUL SYLLABLE RWAH +B894;B894;1105 116B;B894;1105 116B; # (뢔; 뢔; 뢔; 뢔; 뢔; ) HANGUL SYLLABLE RWAE +B895;B895;1105 116B 11A8;B895;1105 116B 11A8; # (뢕; 뢕; 뢕; 뢕; 뢕; ) HANGUL SYLLABLE RWAEG +B896;B896;1105 116B 11A9;B896;1105 116B 11A9; # (뢖; 뢖; 뢖; 뢖; 뢖; ) HANGUL SYLLABLE RWAEGG +B897;B897;1105 116B 11AA;B897;1105 116B 11AA; # (뢗; 뢗; 뢗; 뢗; 뢗; ) HANGUL SYLLABLE RWAEGS +B898;B898;1105 116B 11AB;B898;1105 116B 11AB; # (뢘; 뢘; 뢘; 뢘; 뢘; ) HANGUL SYLLABLE RWAEN +B899;B899;1105 116B 11AC;B899;1105 116B 11AC; # (뢙; 뢙; 뢙; 뢙; 뢙; ) HANGUL SYLLABLE RWAENJ +B89A;B89A;1105 116B 11AD;B89A;1105 116B 11AD; # (뢚; 뢚; 뢚; 뢚; 뢚; ) HANGUL SYLLABLE RWAENH +B89B;B89B;1105 116B 11AE;B89B;1105 116B 11AE; # (뢛; 뢛; 뢛; 뢛; 뢛; ) HANGUL SYLLABLE RWAED +B89C;B89C;1105 116B 11AF;B89C;1105 116B 11AF; # (뢜; 뢜; 뢜; 뢜; 뢜; ) HANGUL SYLLABLE RWAEL +B89D;B89D;1105 116B 11B0;B89D;1105 116B 11B0; # (뢝; 뢝; 뢝; 뢝; 뢝; ) HANGUL SYLLABLE RWAELG +B89E;B89E;1105 116B 11B1;B89E;1105 116B 11B1; # (뢞; 뢞; 뢞; 뢞; 뢞; ) HANGUL SYLLABLE RWAELM +B89F;B89F;1105 116B 11B2;B89F;1105 116B 11B2; # (뢟; 뢟; 뢟; 뢟; 뢟; ) HANGUL SYLLABLE RWAELB +B8A0;B8A0;1105 116B 11B3;B8A0;1105 116B 11B3; # (뢠; 뢠; 뢠; 뢠; 뢠; ) HANGUL SYLLABLE RWAELS +B8A1;B8A1;1105 116B 11B4;B8A1;1105 116B 11B4; # (뢡; 뢡; 뢡; 뢡; 뢡; ) HANGUL SYLLABLE RWAELT +B8A2;B8A2;1105 116B 11B5;B8A2;1105 116B 11B5; # (뢢; 뢢; 뢢; 뢢; 뢢; ) HANGUL SYLLABLE RWAELP +B8A3;B8A3;1105 116B 11B6;B8A3;1105 116B 11B6; # (뢣; 뢣; 뢣; 뢣; 뢣; ) HANGUL SYLLABLE RWAELH +B8A4;B8A4;1105 116B 11B7;B8A4;1105 116B 11B7; # (뢤; 뢤; 뢤; 뢤; 뢤; ) HANGUL SYLLABLE RWAEM +B8A5;B8A5;1105 116B 11B8;B8A5;1105 116B 11B8; # (뢥; 뢥; 뢥; 뢥; 뢥; ) HANGUL SYLLABLE RWAEB +B8A6;B8A6;1105 116B 11B9;B8A6;1105 116B 11B9; # (뢦; 뢦; 뢦; 뢦; 뢦; ) HANGUL SYLLABLE RWAEBS +B8A7;B8A7;1105 116B 11BA;B8A7;1105 116B 11BA; # (뢧; 뢧; 뢧; 뢧; 뢧; ) HANGUL SYLLABLE RWAES +B8A8;B8A8;1105 116B 11BB;B8A8;1105 116B 11BB; # (뢨; 뢨; 뢨; 뢨; 뢨; ) HANGUL SYLLABLE RWAESS +B8A9;B8A9;1105 116B 11BC;B8A9;1105 116B 11BC; # (뢩; 뢩; 뢩; 뢩; 뢩; ) HANGUL SYLLABLE RWAENG +B8AA;B8AA;1105 116B 11BD;B8AA;1105 116B 11BD; # (뢪; 뢪; 뢪; 뢪; 뢪; ) HANGUL SYLLABLE RWAEJ +B8AB;B8AB;1105 116B 11BE;B8AB;1105 116B 11BE; # (뢫; 뢫; 뢫; 뢫; 뢫; ) HANGUL SYLLABLE RWAEC +B8AC;B8AC;1105 116B 11BF;B8AC;1105 116B 11BF; # (뢬; 뢬; 뢬; 뢬; 뢬; ) HANGUL SYLLABLE RWAEK +B8AD;B8AD;1105 116B 11C0;B8AD;1105 116B 11C0; # (뢭; 뢭; 뢭; 뢭; 뢭; ) HANGUL SYLLABLE RWAET +B8AE;B8AE;1105 116B 11C1;B8AE;1105 116B 11C1; # (뢮; 뢮; 뢮; 뢮; 뢮; ) HANGUL SYLLABLE RWAEP +B8AF;B8AF;1105 116B 11C2;B8AF;1105 116B 11C2; # (뢯; 뢯; 뢯; 뢯; 뢯; ) HANGUL SYLLABLE RWAEH +B8B0;B8B0;1105 116C;B8B0;1105 116C; # (뢰; 뢰; 뢰; 뢰; 뢰; ) HANGUL SYLLABLE ROE +B8B1;B8B1;1105 116C 11A8;B8B1;1105 116C 11A8; # (뢱; 뢱; 뢱; 뢱; 뢱; ) HANGUL SYLLABLE ROEG +B8B2;B8B2;1105 116C 11A9;B8B2;1105 116C 11A9; # (뢲; 뢲; 뢲; 뢲; 뢲; ) HANGUL SYLLABLE ROEGG +B8B3;B8B3;1105 116C 11AA;B8B3;1105 116C 11AA; # (뢳; 뢳; 뢳; 뢳; 뢳; ) HANGUL SYLLABLE ROEGS +B8B4;B8B4;1105 116C 11AB;B8B4;1105 116C 11AB; # (뢴; 뢴; 뢴; 뢴; 뢴; ) HANGUL SYLLABLE ROEN +B8B5;B8B5;1105 116C 11AC;B8B5;1105 116C 11AC; # (뢵; 뢵; 뢵; 뢵; 뢵; ) HANGUL SYLLABLE ROENJ +B8B6;B8B6;1105 116C 11AD;B8B6;1105 116C 11AD; # (뢶; 뢶; 뢶; 뢶; 뢶; ) HANGUL SYLLABLE ROENH +B8B7;B8B7;1105 116C 11AE;B8B7;1105 116C 11AE; # (뢷; 뢷; 뢷; 뢷; 뢷; ) HANGUL SYLLABLE ROED +B8B8;B8B8;1105 116C 11AF;B8B8;1105 116C 11AF; # (뢸; 뢸; 뢸; 뢸; 뢸; ) HANGUL SYLLABLE ROEL +B8B9;B8B9;1105 116C 11B0;B8B9;1105 116C 11B0; # (뢹; 뢹; 뢹; 뢹; 뢹; ) HANGUL SYLLABLE ROELG +B8BA;B8BA;1105 116C 11B1;B8BA;1105 116C 11B1; # (뢺; 뢺; 뢺; 뢺; 뢺; ) HANGUL SYLLABLE ROELM +B8BB;B8BB;1105 116C 11B2;B8BB;1105 116C 11B2; # (뢻; 뢻; 뢻; 뢻; 뢻; ) HANGUL SYLLABLE ROELB +B8BC;B8BC;1105 116C 11B3;B8BC;1105 116C 11B3; # (뢼; 뢼; 뢼; 뢼; 뢼; ) HANGUL SYLLABLE ROELS +B8BD;B8BD;1105 116C 11B4;B8BD;1105 116C 11B4; # (뢽; 뢽; 뢽; 뢽; 뢽; ) HANGUL SYLLABLE ROELT +B8BE;B8BE;1105 116C 11B5;B8BE;1105 116C 11B5; # (뢾; 뢾; 뢾; 뢾; 뢾; ) HANGUL SYLLABLE ROELP +B8BF;B8BF;1105 116C 11B6;B8BF;1105 116C 11B6; # (뢿; 뢿; 뢿; 뢿; 뢿; ) HANGUL SYLLABLE ROELH +B8C0;B8C0;1105 116C 11B7;B8C0;1105 116C 11B7; # (룀; 룀; 룀; 룀; 룀; ) HANGUL SYLLABLE ROEM +B8C1;B8C1;1105 116C 11B8;B8C1;1105 116C 11B8; # (룁; 룁; 룁; 룁; 룁; ) HANGUL SYLLABLE ROEB +B8C2;B8C2;1105 116C 11B9;B8C2;1105 116C 11B9; # (룂; 룂; 룂; 룂; 룂; ) HANGUL SYLLABLE ROEBS +B8C3;B8C3;1105 116C 11BA;B8C3;1105 116C 11BA; # (룃; 룃; 룃; 룃; 룃; ) HANGUL SYLLABLE ROES +B8C4;B8C4;1105 116C 11BB;B8C4;1105 116C 11BB; # (룄; 룄; 룄; 룄; 룄; ) HANGUL SYLLABLE ROESS +B8C5;B8C5;1105 116C 11BC;B8C5;1105 116C 11BC; # (룅; 룅; 룅; 룅; 룅; ) HANGUL SYLLABLE ROENG +B8C6;B8C6;1105 116C 11BD;B8C6;1105 116C 11BD; # (룆; 룆; 룆; 룆; 룆; ) HANGUL SYLLABLE ROEJ +B8C7;B8C7;1105 116C 11BE;B8C7;1105 116C 11BE; # (룇; 룇; 룇; 룇; 룇; ) HANGUL SYLLABLE ROEC +B8C8;B8C8;1105 116C 11BF;B8C8;1105 116C 11BF; # (룈; 룈; 룈; 룈; 룈; ) HANGUL SYLLABLE ROEK +B8C9;B8C9;1105 116C 11C0;B8C9;1105 116C 11C0; # (룉; 룉; 룉; 룉; 룉; ) HANGUL SYLLABLE ROET +B8CA;B8CA;1105 116C 11C1;B8CA;1105 116C 11C1; # (룊; 룊; 룊; 룊; 룊; ) HANGUL SYLLABLE ROEP +B8CB;B8CB;1105 116C 11C2;B8CB;1105 116C 11C2; # (룋; 룋; 룋; 룋; 룋; ) HANGUL SYLLABLE ROEH +B8CC;B8CC;1105 116D;B8CC;1105 116D; # (료; 료; 료; 료; 료; ) HANGUL SYLLABLE RYO +B8CD;B8CD;1105 116D 11A8;B8CD;1105 116D 11A8; # (룍; 룍; 룍; 룍; 룍; ) HANGUL SYLLABLE RYOG +B8CE;B8CE;1105 116D 11A9;B8CE;1105 116D 11A9; # (룎; 룎; 룎; 룎; 룎; ) HANGUL SYLLABLE RYOGG +B8CF;B8CF;1105 116D 11AA;B8CF;1105 116D 11AA; # (룏; 룏; 룏; 룏; 룏; ) HANGUL SYLLABLE RYOGS +B8D0;B8D0;1105 116D 11AB;B8D0;1105 116D 11AB; # (룐; 룐; 룐; 룐; 룐; ) HANGUL SYLLABLE RYON +B8D1;B8D1;1105 116D 11AC;B8D1;1105 116D 11AC; # (룑; 룑; 룑; 룑; 룑; ) HANGUL SYLLABLE RYONJ +B8D2;B8D2;1105 116D 11AD;B8D2;1105 116D 11AD; # (룒; 룒; 룒; 룒; 룒; ) HANGUL SYLLABLE RYONH +B8D3;B8D3;1105 116D 11AE;B8D3;1105 116D 11AE; # (룓; 룓; 룓; 룓; 룓; ) HANGUL SYLLABLE RYOD +B8D4;B8D4;1105 116D 11AF;B8D4;1105 116D 11AF; # (룔; 룔; 룔; 룔; 룔; ) HANGUL SYLLABLE RYOL +B8D5;B8D5;1105 116D 11B0;B8D5;1105 116D 11B0; # (룕; 룕; 룕; 룕; 룕; ) HANGUL SYLLABLE RYOLG +B8D6;B8D6;1105 116D 11B1;B8D6;1105 116D 11B1; # (룖; 룖; 룖; 룖; 룖; ) HANGUL SYLLABLE RYOLM +B8D7;B8D7;1105 116D 11B2;B8D7;1105 116D 11B2; # (룗; 룗; 룗; 룗; 룗; ) HANGUL SYLLABLE RYOLB +B8D8;B8D8;1105 116D 11B3;B8D8;1105 116D 11B3; # (룘; 룘; 룘; 룘; 룘; ) HANGUL SYLLABLE RYOLS +B8D9;B8D9;1105 116D 11B4;B8D9;1105 116D 11B4; # (룙; 룙; 룙; 룙; 룙; ) HANGUL SYLLABLE RYOLT +B8DA;B8DA;1105 116D 11B5;B8DA;1105 116D 11B5; # (룚; 룚; 룚; 룚; 룚; ) HANGUL SYLLABLE RYOLP +B8DB;B8DB;1105 116D 11B6;B8DB;1105 116D 11B6; # (룛; 룛; 룛; 룛; 룛; ) HANGUL SYLLABLE RYOLH +B8DC;B8DC;1105 116D 11B7;B8DC;1105 116D 11B7; # (룜; 룜; 룜; 룜; 룜; ) HANGUL SYLLABLE RYOM +B8DD;B8DD;1105 116D 11B8;B8DD;1105 116D 11B8; # (룝; 룝; 룝; 룝; 룝; ) HANGUL SYLLABLE RYOB +B8DE;B8DE;1105 116D 11B9;B8DE;1105 116D 11B9; # (룞; 룞; 룞; 룞; 룞; ) HANGUL SYLLABLE RYOBS +B8DF;B8DF;1105 116D 11BA;B8DF;1105 116D 11BA; # (룟; 룟; 룟; 룟; 룟; ) HANGUL SYLLABLE RYOS +B8E0;B8E0;1105 116D 11BB;B8E0;1105 116D 11BB; # (룠; 룠; 룠; 룠; 룠; ) HANGUL SYLLABLE RYOSS +B8E1;B8E1;1105 116D 11BC;B8E1;1105 116D 11BC; # (룡; 룡; 룡; 룡; 룡; ) HANGUL SYLLABLE RYONG +B8E2;B8E2;1105 116D 11BD;B8E2;1105 116D 11BD; # (룢; 룢; 룢; 룢; 룢; ) HANGUL SYLLABLE RYOJ +B8E3;B8E3;1105 116D 11BE;B8E3;1105 116D 11BE; # (룣; 룣; 룣; 룣; 룣; ) HANGUL SYLLABLE RYOC +B8E4;B8E4;1105 116D 11BF;B8E4;1105 116D 11BF; # (룤; 룤; 룤; 룤; 룤; ) HANGUL SYLLABLE RYOK +B8E5;B8E5;1105 116D 11C0;B8E5;1105 116D 11C0; # (룥; 룥; 룥; 룥; 룥; ) HANGUL SYLLABLE RYOT +B8E6;B8E6;1105 116D 11C1;B8E6;1105 116D 11C1; # (룦; 룦; 룦; 룦; 룦; ) HANGUL SYLLABLE RYOP +B8E7;B8E7;1105 116D 11C2;B8E7;1105 116D 11C2; # (룧; 룧; 룧; 룧; 룧; ) HANGUL SYLLABLE RYOH +B8E8;B8E8;1105 116E;B8E8;1105 116E; # (루; 루; 루; 루; 루; ) HANGUL SYLLABLE RU +B8E9;B8E9;1105 116E 11A8;B8E9;1105 116E 11A8; # (룩; 룩; 룩; 룩; 룩; ) HANGUL SYLLABLE RUG +B8EA;B8EA;1105 116E 11A9;B8EA;1105 116E 11A9; # (룪; 룪; 룪; 룪; 룪; ) HANGUL SYLLABLE RUGG +B8EB;B8EB;1105 116E 11AA;B8EB;1105 116E 11AA; # (룫; 룫; 룫; 룫; 룫; ) HANGUL SYLLABLE RUGS +B8EC;B8EC;1105 116E 11AB;B8EC;1105 116E 11AB; # (룬; 룬; 룬; 룬; 룬; ) HANGUL SYLLABLE RUN +B8ED;B8ED;1105 116E 11AC;B8ED;1105 116E 11AC; # (룭; 룭; 룭; 룭; 룭; ) HANGUL SYLLABLE RUNJ +B8EE;B8EE;1105 116E 11AD;B8EE;1105 116E 11AD; # (룮; 룮; 룮; 룮; 룮; ) HANGUL SYLLABLE RUNH +B8EF;B8EF;1105 116E 11AE;B8EF;1105 116E 11AE; # (룯; 룯; 룯; 룯; 룯; ) HANGUL SYLLABLE RUD +B8F0;B8F0;1105 116E 11AF;B8F0;1105 116E 11AF; # (룰; 룰; 룰; 룰; 룰; ) HANGUL SYLLABLE RUL +B8F1;B8F1;1105 116E 11B0;B8F1;1105 116E 11B0; # (룱; 룱; 룱; 룱; 룱; ) HANGUL SYLLABLE RULG +B8F2;B8F2;1105 116E 11B1;B8F2;1105 116E 11B1; # (룲; 룲; 룲; 룲; 룲; ) HANGUL SYLLABLE RULM +B8F3;B8F3;1105 116E 11B2;B8F3;1105 116E 11B2; # (룳; 룳; 룳; 룳; 룳; ) HANGUL SYLLABLE RULB +B8F4;B8F4;1105 116E 11B3;B8F4;1105 116E 11B3; # (룴; 룴; 룴; 룴; 룴; ) HANGUL SYLLABLE RULS +B8F5;B8F5;1105 116E 11B4;B8F5;1105 116E 11B4; # (룵; 룵; 룵; 룵; 룵; ) HANGUL SYLLABLE RULT +B8F6;B8F6;1105 116E 11B5;B8F6;1105 116E 11B5; # (룶; 룶; 룶; 룶; 룶; ) HANGUL SYLLABLE RULP +B8F7;B8F7;1105 116E 11B6;B8F7;1105 116E 11B6; # (룷; 룷; 룷; 룷; 룷; ) HANGUL SYLLABLE RULH +B8F8;B8F8;1105 116E 11B7;B8F8;1105 116E 11B7; # (룸; 룸; 룸; 룸; 룸; ) HANGUL SYLLABLE RUM +B8F9;B8F9;1105 116E 11B8;B8F9;1105 116E 11B8; # (룹; 룹; 룹; 룹; 룹; ) HANGUL SYLLABLE RUB +B8FA;B8FA;1105 116E 11B9;B8FA;1105 116E 11B9; # (룺; 룺; 룺; 룺; 룺; ) HANGUL SYLLABLE RUBS +B8FB;B8FB;1105 116E 11BA;B8FB;1105 116E 11BA; # (룻; 룻; 룻; 룻; 룻; ) HANGUL SYLLABLE RUS +B8FC;B8FC;1105 116E 11BB;B8FC;1105 116E 11BB; # (룼; 룼; 룼; 룼; 룼; ) HANGUL SYLLABLE RUSS +B8FD;B8FD;1105 116E 11BC;B8FD;1105 116E 11BC; # (룽; 룽; 룽; 룽; 룽; ) HANGUL SYLLABLE RUNG +B8FE;B8FE;1105 116E 11BD;B8FE;1105 116E 11BD; # (룾; 룾; 룾; 룾; 룾; ) HANGUL SYLLABLE RUJ +B8FF;B8FF;1105 116E 11BE;B8FF;1105 116E 11BE; # (룿; 룿; 룿; 룿; 룿; ) HANGUL SYLLABLE RUC +B900;B900;1105 116E 11BF;B900;1105 116E 11BF; # (뤀; 뤀; 뤀; 뤀; 뤀; ) HANGUL SYLLABLE RUK +B901;B901;1105 116E 11C0;B901;1105 116E 11C0; # (뤁; 뤁; 뤁; 뤁; 뤁; ) HANGUL SYLLABLE RUT +B902;B902;1105 116E 11C1;B902;1105 116E 11C1; # (뤂; 뤂; 뤂; 뤂; 뤂; ) HANGUL SYLLABLE RUP +B903;B903;1105 116E 11C2;B903;1105 116E 11C2; # (뤃; 뤃; 뤃; 뤃; 뤃; ) HANGUL SYLLABLE RUH +B904;B904;1105 116F;B904;1105 116F; # (뤄; 뤄; 뤄; 뤄; 뤄; ) HANGUL SYLLABLE RWEO +B905;B905;1105 116F 11A8;B905;1105 116F 11A8; # (뤅; 뤅; 뤅; 뤅; 뤅; ) HANGUL SYLLABLE RWEOG +B906;B906;1105 116F 11A9;B906;1105 116F 11A9; # (뤆; 뤆; 뤆; 뤆; 뤆; ) HANGUL SYLLABLE RWEOGG +B907;B907;1105 116F 11AA;B907;1105 116F 11AA; # (뤇; 뤇; 뤇; 뤇; 뤇; ) HANGUL SYLLABLE RWEOGS +B908;B908;1105 116F 11AB;B908;1105 116F 11AB; # (뤈; 뤈; 뤈; 뤈; 뤈; ) HANGUL SYLLABLE RWEON +B909;B909;1105 116F 11AC;B909;1105 116F 11AC; # (뤉; 뤉; 뤉; 뤉; 뤉; ) HANGUL SYLLABLE RWEONJ +B90A;B90A;1105 116F 11AD;B90A;1105 116F 11AD; # (뤊; 뤊; 뤊; 뤊; 뤊; ) HANGUL SYLLABLE RWEONH +B90B;B90B;1105 116F 11AE;B90B;1105 116F 11AE; # (뤋; 뤋; 뤋; 뤋; 뤋; ) HANGUL SYLLABLE RWEOD +B90C;B90C;1105 116F 11AF;B90C;1105 116F 11AF; # (뤌; 뤌; 뤌; 뤌; 뤌; ) HANGUL SYLLABLE RWEOL +B90D;B90D;1105 116F 11B0;B90D;1105 116F 11B0; # (뤍; 뤍; 뤍; 뤍; 뤍; ) HANGUL SYLLABLE RWEOLG +B90E;B90E;1105 116F 11B1;B90E;1105 116F 11B1; # (뤎; 뤎; 뤎; 뤎; 뤎; ) HANGUL SYLLABLE RWEOLM +B90F;B90F;1105 116F 11B2;B90F;1105 116F 11B2; # (뤏; 뤏; 뤏; 뤏; 뤏; ) HANGUL SYLLABLE RWEOLB +B910;B910;1105 116F 11B3;B910;1105 116F 11B3; # (뤐; 뤐; 뤐; 뤐; 뤐; ) HANGUL SYLLABLE RWEOLS +B911;B911;1105 116F 11B4;B911;1105 116F 11B4; # (뤑; 뤑; 뤑; 뤑; 뤑; ) HANGUL SYLLABLE RWEOLT +B912;B912;1105 116F 11B5;B912;1105 116F 11B5; # (뤒; 뤒; 뤒; 뤒; 뤒; ) HANGUL SYLLABLE RWEOLP +B913;B913;1105 116F 11B6;B913;1105 116F 11B6; # (뤓; 뤓; 뤓; 뤓; 뤓; ) HANGUL SYLLABLE RWEOLH +B914;B914;1105 116F 11B7;B914;1105 116F 11B7; # (뤔; 뤔; 뤔; 뤔; 뤔; ) HANGUL SYLLABLE RWEOM +B915;B915;1105 116F 11B8;B915;1105 116F 11B8; # (뤕; 뤕; 뤕; 뤕; 뤕; ) HANGUL SYLLABLE RWEOB +B916;B916;1105 116F 11B9;B916;1105 116F 11B9; # (뤖; 뤖; 뤖; 뤖; 뤖; ) HANGUL SYLLABLE RWEOBS +B917;B917;1105 116F 11BA;B917;1105 116F 11BA; # (뤗; 뤗; 뤗; 뤗; 뤗; ) HANGUL SYLLABLE RWEOS +B918;B918;1105 116F 11BB;B918;1105 116F 11BB; # (뤘; 뤘; 뤘; 뤘; 뤘; ) HANGUL SYLLABLE RWEOSS +B919;B919;1105 116F 11BC;B919;1105 116F 11BC; # (뤙; 뤙; 뤙; 뤙; 뤙; ) HANGUL SYLLABLE RWEONG +B91A;B91A;1105 116F 11BD;B91A;1105 116F 11BD; # (뤚; 뤚; 뤚; 뤚; 뤚; ) HANGUL SYLLABLE RWEOJ +B91B;B91B;1105 116F 11BE;B91B;1105 116F 11BE; # (뤛; 뤛; 뤛; 뤛; 뤛; ) HANGUL SYLLABLE RWEOC +B91C;B91C;1105 116F 11BF;B91C;1105 116F 11BF; # (뤜; 뤜; 뤜; 뤜; 뤜; ) HANGUL SYLLABLE RWEOK +B91D;B91D;1105 116F 11C0;B91D;1105 116F 11C0; # (뤝; 뤝; 뤝; 뤝; 뤝; ) HANGUL SYLLABLE RWEOT +B91E;B91E;1105 116F 11C1;B91E;1105 116F 11C1; # (뤞; 뤞; 뤞; 뤞; 뤞; ) HANGUL SYLLABLE RWEOP +B91F;B91F;1105 116F 11C2;B91F;1105 116F 11C2; # (뤟; 뤟; 뤟; 뤟; 뤟; ) HANGUL SYLLABLE RWEOH +B920;B920;1105 1170;B920;1105 1170; # (뤠; 뤠; 뤠; 뤠; 뤠; ) HANGUL SYLLABLE RWE +B921;B921;1105 1170 11A8;B921;1105 1170 11A8; # (뤡; 뤡; 뤡; 뤡; 뤡; ) HANGUL SYLLABLE RWEG +B922;B922;1105 1170 11A9;B922;1105 1170 11A9; # (뤢; 뤢; 뤢; 뤢; 뤢; ) HANGUL SYLLABLE RWEGG +B923;B923;1105 1170 11AA;B923;1105 1170 11AA; # (뤣; 뤣; 뤣; 뤣; 뤣; ) HANGUL SYLLABLE RWEGS +B924;B924;1105 1170 11AB;B924;1105 1170 11AB; # (뤤; 뤤; 뤤; 뤤; 뤤; ) HANGUL SYLLABLE RWEN +B925;B925;1105 1170 11AC;B925;1105 1170 11AC; # (뤥; 뤥; 뤥; 뤥; 뤥; ) HANGUL SYLLABLE RWENJ +B926;B926;1105 1170 11AD;B926;1105 1170 11AD; # (뤦; 뤦; 뤦; 뤦; 뤦; ) HANGUL SYLLABLE RWENH +B927;B927;1105 1170 11AE;B927;1105 1170 11AE; # (뤧; 뤧; 뤧; 뤧; 뤧; ) HANGUL SYLLABLE RWED +B928;B928;1105 1170 11AF;B928;1105 1170 11AF; # (뤨; 뤨; 뤨; 뤨; 뤨; ) HANGUL SYLLABLE RWEL +B929;B929;1105 1170 11B0;B929;1105 1170 11B0; # (뤩; 뤩; 뤩; 뤩; 뤩; ) HANGUL SYLLABLE RWELG +B92A;B92A;1105 1170 11B1;B92A;1105 1170 11B1; # (뤪; 뤪; 뤪; 뤪; 뤪; ) HANGUL SYLLABLE RWELM +B92B;B92B;1105 1170 11B2;B92B;1105 1170 11B2; # (뤫; 뤫; 뤫; 뤫; 뤫; ) HANGUL SYLLABLE RWELB +B92C;B92C;1105 1170 11B3;B92C;1105 1170 11B3; # (뤬; 뤬; 뤬; 뤬; 뤬; ) HANGUL SYLLABLE RWELS +B92D;B92D;1105 1170 11B4;B92D;1105 1170 11B4; # (뤭; 뤭; 뤭; 뤭; 뤭; ) HANGUL SYLLABLE RWELT +B92E;B92E;1105 1170 11B5;B92E;1105 1170 11B5; # (뤮; 뤮; 뤮; 뤮; 뤮; ) HANGUL SYLLABLE RWELP +B92F;B92F;1105 1170 11B6;B92F;1105 1170 11B6; # (뤯; 뤯; 뤯; 뤯; 뤯; ) HANGUL SYLLABLE RWELH +B930;B930;1105 1170 11B7;B930;1105 1170 11B7; # (뤰; 뤰; 뤰; 뤰; 뤰; ) HANGUL SYLLABLE RWEM +B931;B931;1105 1170 11B8;B931;1105 1170 11B8; # (뤱; 뤱; 뤱; 뤱; 뤱; ) HANGUL SYLLABLE RWEB +B932;B932;1105 1170 11B9;B932;1105 1170 11B9; # (뤲; 뤲; 뤲; 뤲; 뤲; ) HANGUL SYLLABLE RWEBS +B933;B933;1105 1170 11BA;B933;1105 1170 11BA; # (뤳; 뤳; 뤳; 뤳; 뤳; ) HANGUL SYLLABLE RWES +B934;B934;1105 1170 11BB;B934;1105 1170 11BB; # (뤴; 뤴; 뤴; 뤴; 뤴; ) HANGUL SYLLABLE RWESS +B935;B935;1105 1170 11BC;B935;1105 1170 11BC; # (뤵; 뤵; 뤵; 뤵; 뤵; ) HANGUL SYLLABLE RWENG +B936;B936;1105 1170 11BD;B936;1105 1170 11BD; # (뤶; 뤶; 뤶; 뤶; 뤶; ) HANGUL SYLLABLE RWEJ +B937;B937;1105 1170 11BE;B937;1105 1170 11BE; # (뤷; 뤷; 뤷; 뤷; 뤷; ) HANGUL SYLLABLE RWEC +B938;B938;1105 1170 11BF;B938;1105 1170 11BF; # (뤸; 뤸; 뤸; 뤸; 뤸; ) HANGUL SYLLABLE RWEK +B939;B939;1105 1170 11C0;B939;1105 1170 11C0; # (뤹; 뤹; 뤹; 뤹; 뤹; ) HANGUL SYLLABLE RWET +B93A;B93A;1105 1170 11C1;B93A;1105 1170 11C1; # (뤺; 뤺; 뤺; 뤺; 뤺; ) HANGUL SYLLABLE RWEP +B93B;B93B;1105 1170 11C2;B93B;1105 1170 11C2; # (뤻; 뤻; 뤻; 뤻; 뤻; ) HANGUL SYLLABLE RWEH +B93C;B93C;1105 1171;B93C;1105 1171; # (뤼; 뤼; 뤼; 뤼; 뤼; ) HANGUL SYLLABLE RWI +B93D;B93D;1105 1171 11A8;B93D;1105 1171 11A8; # (뤽; 뤽; 뤽; 뤽; 뤽; ) HANGUL SYLLABLE RWIG +B93E;B93E;1105 1171 11A9;B93E;1105 1171 11A9; # (뤾; 뤾; 뤾; 뤾; 뤾; ) HANGUL SYLLABLE RWIGG +B93F;B93F;1105 1171 11AA;B93F;1105 1171 11AA; # (뤿; 뤿; 뤿; 뤿; 뤿; ) HANGUL SYLLABLE RWIGS +B940;B940;1105 1171 11AB;B940;1105 1171 11AB; # (륀; 륀; 륀; 륀; 륀; ) HANGUL SYLLABLE RWIN +B941;B941;1105 1171 11AC;B941;1105 1171 11AC; # (륁; 륁; 륁; 륁; 륁; ) HANGUL SYLLABLE RWINJ +B942;B942;1105 1171 11AD;B942;1105 1171 11AD; # (륂; 륂; 륂; 륂; 륂; ) HANGUL SYLLABLE RWINH +B943;B943;1105 1171 11AE;B943;1105 1171 11AE; # (륃; 륃; 륃; 륃; 륃; ) HANGUL SYLLABLE RWID +B944;B944;1105 1171 11AF;B944;1105 1171 11AF; # (륄; 륄; 륄; 륄; 륄; ) HANGUL SYLLABLE RWIL +B945;B945;1105 1171 11B0;B945;1105 1171 11B0; # (륅; 륅; 륅; 륅; 륅; ) HANGUL SYLLABLE RWILG +B946;B946;1105 1171 11B1;B946;1105 1171 11B1; # (륆; 륆; 륆; 륆; 륆; ) HANGUL SYLLABLE RWILM +B947;B947;1105 1171 11B2;B947;1105 1171 11B2; # (륇; 륇; 륇; 륇; 륇; ) HANGUL SYLLABLE RWILB +B948;B948;1105 1171 11B3;B948;1105 1171 11B3; # (륈; 륈; 륈; 륈; 륈; ) HANGUL SYLLABLE RWILS +B949;B949;1105 1171 11B4;B949;1105 1171 11B4; # (륉; 륉; 륉; 륉; 륉; ) HANGUL SYLLABLE RWILT +B94A;B94A;1105 1171 11B5;B94A;1105 1171 11B5; # (륊; 륊; 륊; 륊; 륊; ) HANGUL SYLLABLE RWILP +B94B;B94B;1105 1171 11B6;B94B;1105 1171 11B6; # (륋; 륋; 륋; 륋; 륋; ) HANGUL SYLLABLE RWILH +B94C;B94C;1105 1171 11B7;B94C;1105 1171 11B7; # (륌; 륌; 륌; 륌; 륌; ) HANGUL SYLLABLE RWIM +B94D;B94D;1105 1171 11B8;B94D;1105 1171 11B8; # (륍; 륍; 륍; 륍; 륍; ) HANGUL SYLLABLE RWIB +B94E;B94E;1105 1171 11B9;B94E;1105 1171 11B9; # (륎; 륎; 륎; 륎; 륎; ) HANGUL SYLLABLE RWIBS +B94F;B94F;1105 1171 11BA;B94F;1105 1171 11BA; # (륏; 륏; 륏; 륏; 륏; ) HANGUL SYLLABLE RWIS +B950;B950;1105 1171 11BB;B950;1105 1171 11BB; # (륐; 륐; 륐; 륐; 륐; ) HANGUL SYLLABLE RWISS +B951;B951;1105 1171 11BC;B951;1105 1171 11BC; # (륑; 륑; 륑; 륑; 륑; ) HANGUL SYLLABLE RWING +B952;B952;1105 1171 11BD;B952;1105 1171 11BD; # (륒; 륒; 륒; 륒; 륒; ) HANGUL SYLLABLE RWIJ +B953;B953;1105 1171 11BE;B953;1105 1171 11BE; # (륓; 륓; 륓; 륓; 륓; ) HANGUL SYLLABLE RWIC +B954;B954;1105 1171 11BF;B954;1105 1171 11BF; # (륔; 륔; 륔; 륔; 륔; ) HANGUL SYLLABLE RWIK +B955;B955;1105 1171 11C0;B955;1105 1171 11C0; # (륕; 륕; 륕; 륕; 륕; ) HANGUL SYLLABLE RWIT +B956;B956;1105 1171 11C1;B956;1105 1171 11C1; # (륖; 륖; 륖; 륖; 륖; ) HANGUL SYLLABLE RWIP +B957;B957;1105 1171 11C2;B957;1105 1171 11C2; # (륗; 륗; 륗; 륗; 륗; ) HANGUL SYLLABLE RWIH +B958;B958;1105 1172;B958;1105 1172; # (류; 류; 류; 류; 류; ) HANGUL SYLLABLE RYU +B959;B959;1105 1172 11A8;B959;1105 1172 11A8; # (륙; 륙; 륙; 륙; 륙; ) HANGUL SYLLABLE RYUG +B95A;B95A;1105 1172 11A9;B95A;1105 1172 11A9; # (륚; 륚; 륚; 륚; 륚; ) HANGUL SYLLABLE RYUGG +B95B;B95B;1105 1172 11AA;B95B;1105 1172 11AA; # (륛; 륛; 륛; 륛; 륛; ) HANGUL SYLLABLE RYUGS +B95C;B95C;1105 1172 11AB;B95C;1105 1172 11AB; # (륜; 륜; 륜; 륜; 륜; ) HANGUL SYLLABLE RYUN +B95D;B95D;1105 1172 11AC;B95D;1105 1172 11AC; # (륝; 륝; 륝; 륝; 륝; ) HANGUL SYLLABLE RYUNJ +B95E;B95E;1105 1172 11AD;B95E;1105 1172 11AD; # (륞; 륞; 륞; 륞; 륞; ) HANGUL SYLLABLE RYUNH +B95F;B95F;1105 1172 11AE;B95F;1105 1172 11AE; # (륟; 륟; 륟; 륟; 륟; ) HANGUL SYLLABLE RYUD +B960;B960;1105 1172 11AF;B960;1105 1172 11AF; # (률; 률; 률; 률; 률; ) HANGUL SYLLABLE RYUL +B961;B961;1105 1172 11B0;B961;1105 1172 11B0; # (륡; 륡; 륡; 륡; 륡; ) HANGUL SYLLABLE RYULG +B962;B962;1105 1172 11B1;B962;1105 1172 11B1; # (륢; 륢; 륢; 륢; 륢; ) HANGUL SYLLABLE RYULM +B963;B963;1105 1172 11B2;B963;1105 1172 11B2; # (륣; 륣; 륣; 륣; 륣; ) HANGUL SYLLABLE RYULB +B964;B964;1105 1172 11B3;B964;1105 1172 11B3; # (륤; 륤; 륤; 륤; 륤; ) HANGUL SYLLABLE RYULS +B965;B965;1105 1172 11B4;B965;1105 1172 11B4; # (륥; 륥; 륥; 륥; 륥; ) HANGUL SYLLABLE RYULT +B966;B966;1105 1172 11B5;B966;1105 1172 11B5; # (륦; 륦; 륦; 륦; 륦; ) HANGUL SYLLABLE RYULP +B967;B967;1105 1172 11B6;B967;1105 1172 11B6; # (륧; 륧; 륧; 륧; 륧; ) HANGUL SYLLABLE RYULH +B968;B968;1105 1172 11B7;B968;1105 1172 11B7; # (륨; 륨; 륨; 륨; 륨; ) HANGUL SYLLABLE RYUM +B969;B969;1105 1172 11B8;B969;1105 1172 11B8; # (륩; 륩; 륩; 륩; 륩; ) HANGUL SYLLABLE RYUB +B96A;B96A;1105 1172 11B9;B96A;1105 1172 11B9; # (륪; 륪; 륪; 륪; 륪; ) HANGUL SYLLABLE RYUBS +B96B;B96B;1105 1172 11BA;B96B;1105 1172 11BA; # (륫; 륫; 륫; 륫; 륫; ) HANGUL SYLLABLE RYUS +B96C;B96C;1105 1172 11BB;B96C;1105 1172 11BB; # (륬; 륬; 륬; 륬; 륬; ) HANGUL SYLLABLE RYUSS +B96D;B96D;1105 1172 11BC;B96D;1105 1172 11BC; # (륭; 륭; 륭; 륭; 륭; ) HANGUL SYLLABLE RYUNG +B96E;B96E;1105 1172 11BD;B96E;1105 1172 11BD; # (륮; 륮; 륮; 륮; 륮; ) HANGUL SYLLABLE RYUJ +B96F;B96F;1105 1172 11BE;B96F;1105 1172 11BE; # (륯; 륯; 륯; 륯; 륯; ) HANGUL SYLLABLE RYUC +B970;B970;1105 1172 11BF;B970;1105 1172 11BF; # (륰; 륰; 륰; 륰; 륰; ) HANGUL SYLLABLE RYUK +B971;B971;1105 1172 11C0;B971;1105 1172 11C0; # (륱; 륱; 륱; 륱; 륱; ) HANGUL SYLLABLE RYUT +B972;B972;1105 1172 11C1;B972;1105 1172 11C1; # (륲; 륲; 륲; 륲; 륲; ) HANGUL SYLLABLE RYUP +B973;B973;1105 1172 11C2;B973;1105 1172 11C2; # (륳; 륳; 륳; 륳; 륳; ) HANGUL SYLLABLE RYUH +B974;B974;1105 1173;B974;1105 1173; # (르; 르; 르; 르; 르; ) HANGUL SYLLABLE REU +B975;B975;1105 1173 11A8;B975;1105 1173 11A8; # (륵; 륵; 륵; 륵; 륵; ) HANGUL SYLLABLE REUG +B976;B976;1105 1173 11A9;B976;1105 1173 11A9; # (륶; 륶; 륶; 륶; 륶; ) HANGUL SYLLABLE REUGG +B977;B977;1105 1173 11AA;B977;1105 1173 11AA; # (륷; 륷; 륷; 륷; 륷; ) HANGUL SYLLABLE REUGS +B978;B978;1105 1173 11AB;B978;1105 1173 11AB; # (른; 른; 른; 른; 른; ) HANGUL SYLLABLE REUN +B979;B979;1105 1173 11AC;B979;1105 1173 11AC; # (륹; 륹; 륹; 륹; 륹; ) HANGUL SYLLABLE REUNJ +B97A;B97A;1105 1173 11AD;B97A;1105 1173 11AD; # (륺; 륺; 륺; 륺; 륺; ) HANGUL SYLLABLE REUNH +B97B;B97B;1105 1173 11AE;B97B;1105 1173 11AE; # (륻; 륻; 륻; 륻; 륻; ) HANGUL SYLLABLE REUD +B97C;B97C;1105 1173 11AF;B97C;1105 1173 11AF; # (를; 를; 를; 를; 를; ) HANGUL SYLLABLE REUL +B97D;B97D;1105 1173 11B0;B97D;1105 1173 11B0; # (륽; 륽; 륽; 륽; 륽; ) HANGUL SYLLABLE REULG +B97E;B97E;1105 1173 11B1;B97E;1105 1173 11B1; # (륾; 륾; 륾; 륾; 륾; ) HANGUL SYLLABLE REULM +B97F;B97F;1105 1173 11B2;B97F;1105 1173 11B2; # (륿; 륿; 륿; 륿; 륿; ) HANGUL SYLLABLE REULB +B980;B980;1105 1173 11B3;B980;1105 1173 11B3; # (릀; 릀; 릀; 릀; 릀; ) HANGUL SYLLABLE REULS +B981;B981;1105 1173 11B4;B981;1105 1173 11B4; # (릁; 릁; 릁; 릁; 릁; ) HANGUL SYLLABLE REULT +B982;B982;1105 1173 11B5;B982;1105 1173 11B5; # (릂; 릂; 릂; 릂; 릂; ) HANGUL SYLLABLE REULP +B983;B983;1105 1173 11B6;B983;1105 1173 11B6; # (릃; 릃; 릃; 릃; 릃; ) HANGUL SYLLABLE REULH +B984;B984;1105 1173 11B7;B984;1105 1173 11B7; # (름; 름; 름; 름; 름; ) HANGUL SYLLABLE REUM +B985;B985;1105 1173 11B8;B985;1105 1173 11B8; # (릅; 릅; 릅; 릅; 릅; ) HANGUL SYLLABLE REUB +B986;B986;1105 1173 11B9;B986;1105 1173 11B9; # (릆; 릆; 릆; 릆; 릆; ) HANGUL SYLLABLE REUBS +B987;B987;1105 1173 11BA;B987;1105 1173 11BA; # (릇; 릇; 릇; 릇; 릇; ) HANGUL SYLLABLE REUS +B988;B988;1105 1173 11BB;B988;1105 1173 11BB; # (릈; 릈; 릈; 릈; 릈; ) HANGUL SYLLABLE REUSS +B989;B989;1105 1173 11BC;B989;1105 1173 11BC; # (릉; 릉; 릉; 릉; 릉; ) HANGUL SYLLABLE REUNG +B98A;B98A;1105 1173 11BD;B98A;1105 1173 11BD; # (릊; 릊; 릊; 릊; 릊; ) HANGUL SYLLABLE REUJ +B98B;B98B;1105 1173 11BE;B98B;1105 1173 11BE; # (릋; 릋; 릋; 릋; 릋; ) HANGUL SYLLABLE REUC +B98C;B98C;1105 1173 11BF;B98C;1105 1173 11BF; # (릌; 릌; 릌; 릌; 릌; ) HANGUL SYLLABLE REUK +B98D;B98D;1105 1173 11C0;B98D;1105 1173 11C0; # (릍; 릍; 릍; 릍; 릍; ) HANGUL SYLLABLE REUT +B98E;B98E;1105 1173 11C1;B98E;1105 1173 11C1; # (릎; 릎; 릎; 릎; 릎; ) HANGUL SYLLABLE REUP +B98F;B98F;1105 1173 11C2;B98F;1105 1173 11C2; # (릏; 릏; 릏; 릏; 릏; ) HANGUL SYLLABLE REUH +B990;B990;1105 1174;B990;1105 1174; # (릐; 릐; 릐; 릐; 릐; ) HANGUL SYLLABLE RYI +B991;B991;1105 1174 11A8;B991;1105 1174 11A8; # (릑; 릑; 릑; 릑; 릑; ) HANGUL SYLLABLE RYIG +B992;B992;1105 1174 11A9;B992;1105 1174 11A9; # (릒; 릒; 릒; 릒; 릒; ) HANGUL SYLLABLE RYIGG +B993;B993;1105 1174 11AA;B993;1105 1174 11AA; # (릓; 릓; 릓; 릓; 릓; ) HANGUL SYLLABLE RYIGS +B994;B994;1105 1174 11AB;B994;1105 1174 11AB; # (릔; 릔; 릔; 릔; 릔; ) HANGUL SYLLABLE RYIN +B995;B995;1105 1174 11AC;B995;1105 1174 11AC; # (릕; 릕; 릕; 릕; 릕; ) HANGUL SYLLABLE RYINJ +B996;B996;1105 1174 11AD;B996;1105 1174 11AD; # (릖; 릖; 릖; 릖; 릖; ) HANGUL SYLLABLE RYINH +B997;B997;1105 1174 11AE;B997;1105 1174 11AE; # (릗; 릗; 릗; 릗; 릗; ) HANGUL SYLLABLE RYID +B998;B998;1105 1174 11AF;B998;1105 1174 11AF; # (릘; 릘; 릘; 릘; 릘; ) HANGUL SYLLABLE RYIL +B999;B999;1105 1174 11B0;B999;1105 1174 11B0; # (릙; 릙; 릙; 릙; 릙; ) HANGUL SYLLABLE RYILG +B99A;B99A;1105 1174 11B1;B99A;1105 1174 11B1; # (릚; 릚; 릚; 릚; 릚; ) HANGUL SYLLABLE RYILM +B99B;B99B;1105 1174 11B2;B99B;1105 1174 11B2; # (릛; 릛; 릛; 릛; 릛; ) HANGUL SYLLABLE RYILB +B99C;B99C;1105 1174 11B3;B99C;1105 1174 11B3; # (릜; 릜; 릜; 릜; 릜; ) HANGUL SYLLABLE RYILS +B99D;B99D;1105 1174 11B4;B99D;1105 1174 11B4; # (릝; 릝; 릝; 릝; 릝; ) HANGUL SYLLABLE RYILT +B99E;B99E;1105 1174 11B5;B99E;1105 1174 11B5; # (릞; 릞; 릞; 릞; 릞; ) HANGUL SYLLABLE RYILP +B99F;B99F;1105 1174 11B6;B99F;1105 1174 11B6; # (릟; 릟; 릟; 릟; 릟; ) HANGUL SYLLABLE RYILH +B9A0;B9A0;1105 1174 11B7;B9A0;1105 1174 11B7; # (릠; 릠; 릠; 릠; 릠; ) HANGUL SYLLABLE RYIM +B9A1;B9A1;1105 1174 11B8;B9A1;1105 1174 11B8; # (릡; 릡; 릡; 릡; 릡; ) HANGUL SYLLABLE RYIB +B9A2;B9A2;1105 1174 11B9;B9A2;1105 1174 11B9; # (릢; 릢; 릢; 릢; 릢; ) HANGUL SYLLABLE RYIBS +B9A3;B9A3;1105 1174 11BA;B9A3;1105 1174 11BA; # (릣; 릣; 릣; 릣; 릣; ) HANGUL SYLLABLE RYIS +B9A4;B9A4;1105 1174 11BB;B9A4;1105 1174 11BB; # (릤; 릤; 릤; 릤; 릤; ) HANGUL SYLLABLE RYISS +B9A5;B9A5;1105 1174 11BC;B9A5;1105 1174 11BC; # (릥; 릥; 릥; 릥; 릥; ) HANGUL SYLLABLE RYING +B9A6;B9A6;1105 1174 11BD;B9A6;1105 1174 11BD; # (릦; 릦; 릦; 릦; 릦; ) HANGUL SYLLABLE RYIJ +B9A7;B9A7;1105 1174 11BE;B9A7;1105 1174 11BE; # (릧; 릧; 릧; 릧; 릧; ) HANGUL SYLLABLE RYIC +B9A8;B9A8;1105 1174 11BF;B9A8;1105 1174 11BF; # (릨; 릨; 릨; 릨; 릨; ) HANGUL SYLLABLE RYIK +B9A9;B9A9;1105 1174 11C0;B9A9;1105 1174 11C0; # (릩; 릩; 릩; 릩; 릩; ) HANGUL SYLLABLE RYIT +B9AA;B9AA;1105 1174 11C1;B9AA;1105 1174 11C1; # (릪; 릪; 릪; 릪; 릪; ) HANGUL SYLLABLE RYIP +B9AB;B9AB;1105 1174 11C2;B9AB;1105 1174 11C2; # (릫; 릫; 릫; 릫; 릫; ) HANGUL SYLLABLE RYIH +B9AC;B9AC;1105 1175;B9AC;1105 1175; # (리; 리; 리; 리; 리; ) HANGUL SYLLABLE RI +B9AD;B9AD;1105 1175 11A8;B9AD;1105 1175 11A8; # (릭; 릭; 릭; 릭; 릭; ) HANGUL SYLLABLE RIG +B9AE;B9AE;1105 1175 11A9;B9AE;1105 1175 11A9; # (릮; 릮; 릮; 릮; 릮; ) HANGUL SYLLABLE RIGG +B9AF;B9AF;1105 1175 11AA;B9AF;1105 1175 11AA; # (릯; 릯; 릯; 릯; 릯; ) HANGUL SYLLABLE RIGS +B9B0;B9B0;1105 1175 11AB;B9B0;1105 1175 11AB; # (린; 린; 린; 린; 린; ) HANGUL SYLLABLE RIN +B9B1;B9B1;1105 1175 11AC;B9B1;1105 1175 11AC; # (릱; 릱; 릱; 릱; 릱; ) HANGUL SYLLABLE RINJ +B9B2;B9B2;1105 1175 11AD;B9B2;1105 1175 11AD; # (릲; 릲; 릲; 릲; 릲; ) HANGUL SYLLABLE RINH +B9B3;B9B3;1105 1175 11AE;B9B3;1105 1175 11AE; # (릳; 릳; 릳; 릳; 릳; ) HANGUL SYLLABLE RID +B9B4;B9B4;1105 1175 11AF;B9B4;1105 1175 11AF; # (릴; 릴; 릴; 릴; 릴; ) HANGUL SYLLABLE RIL +B9B5;B9B5;1105 1175 11B0;B9B5;1105 1175 11B0; # (릵; 릵; 릵; 릵; 릵; ) HANGUL SYLLABLE RILG +B9B6;B9B6;1105 1175 11B1;B9B6;1105 1175 11B1; # (릶; 릶; 릶; 릶; 릶; ) HANGUL SYLLABLE RILM +B9B7;B9B7;1105 1175 11B2;B9B7;1105 1175 11B2; # (릷; 릷; 릷; 릷; 릷; ) HANGUL SYLLABLE RILB +B9B8;B9B8;1105 1175 11B3;B9B8;1105 1175 11B3; # (릸; 릸; 릸; 릸; 릸; ) HANGUL SYLLABLE RILS +B9B9;B9B9;1105 1175 11B4;B9B9;1105 1175 11B4; # (릹; 릹; 릹; 릹; 릹; ) HANGUL SYLLABLE RILT +B9BA;B9BA;1105 1175 11B5;B9BA;1105 1175 11B5; # (릺; 릺; 릺; 릺; 릺; ) HANGUL SYLLABLE RILP +B9BB;B9BB;1105 1175 11B6;B9BB;1105 1175 11B6; # (릻; 릻; 릻; 릻; 릻; ) HANGUL SYLLABLE RILH +B9BC;B9BC;1105 1175 11B7;B9BC;1105 1175 11B7; # (림; 림; 림; 림; 림; ) HANGUL SYLLABLE RIM +B9BD;B9BD;1105 1175 11B8;B9BD;1105 1175 11B8; # (립; 립; 립; 립; 립; ) HANGUL SYLLABLE RIB +B9BE;B9BE;1105 1175 11B9;B9BE;1105 1175 11B9; # (릾; 릾; 릾; 릾; 릾; ) HANGUL SYLLABLE RIBS +B9BF;B9BF;1105 1175 11BA;B9BF;1105 1175 11BA; # (릿; 릿; 릿; 릿; 릿; ) HANGUL SYLLABLE RIS +B9C0;B9C0;1105 1175 11BB;B9C0;1105 1175 11BB; # (맀; 맀; 맀; 맀; 맀; ) HANGUL SYLLABLE RISS +B9C1;B9C1;1105 1175 11BC;B9C1;1105 1175 11BC; # (링; 링; 링; 링; 링; ) HANGUL SYLLABLE RING +B9C2;B9C2;1105 1175 11BD;B9C2;1105 1175 11BD; # (맂; 맂; 맂; 맂; 맂; ) HANGUL SYLLABLE RIJ +B9C3;B9C3;1105 1175 11BE;B9C3;1105 1175 11BE; # (맃; 맃; 맃; 맃; 맃; ) HANGUL SYLLABLE RIC +B9C4;B9C4;1105 1175 11BF;B9C4;1105 1175 11BF; # (맄; 맄; 맄; 맄; 맄; ) HANGUL SYLLABLE RIK +B9C5;B9C5;1105 1175 11C0;B9C5;1105 1175 11C0; # (맅; 맅; 맅; 맅; 맅; ) HANGUL SYLLABLE RIT +B9C6;B9C6;1105 1175 11C1;B9C6;1105 1175 11C1; # (맆; 맆; 맆; 맆; 맆; ) HANGUL SYLLABLE RIP +B9C7;B9C7;1105 1175 11C2;B9C7;1105 1175 11C2; # (맇; 맇; 맇; 맇; 맇; ) HANGUL SYLLABLE RIH +B9C8;B9C8;1106 1161;B9C8;1106 1161; # (마; 마; 마; 마; 마; ) HANGUL SYLLABLE MA +B9C9;B9C9;1106 1161 11A8;B9C9;1106 1161 11A8; # (막; 막; 막; 막; 막; ) HANGUL SYLLABLE MAG +B9CA;B9CA;1106 1161 11A9;B9CA;1106 1161 11A9; # (맊; 맊; 맊; 맊; 맊; ) HANGUL SYLLABLE MAGG +B9CB;B9CB;1106 1161 11AA;B9CB;1106 1161 11AA; # (맋; 맋; 맋; 맋; 맋; ) HANGUL SYLLABLE MAGS +B9CC;B9CC;1106 1161 11AB;B9CC;1106 1161 11AB; # (만; 만; 만; 만; 만; ) HANGUL SYLLABLE MAN +B9CD;B9CD;1106 1161 11AC;B9CD;1106 1161 11AC; # (맍; 맍; 맍; 맍; 맍; ) HANGUL SYLLABLE MANJ +B9CE;B9CE;1106 1161 11AD;B9CE;1106 1161 11AD; # (많; 많; 많; 많; 많; ) HANGUL SYLLABLE MANH +B9CF;B9CF;1106 1161 11AE;B9CF;1106 1161 11AE; # (맏; 맏; 맏; 맏; 맏; ) HANGUL SYLLABLE MAD +B9D0;B9D0;1106 1161 11AF;B9D0;1106 1161 11AF; # (말; 말; 말; 말; 말; ) HANGUL SYLLABLE MAL +B9D1;B9D1;1106 1161 11B0;B9D1;1106 1161 11B0; # (맑; 맑; 맑; 맑; 맑; ) HANGUL SYLLABLE MALG +B9D2;B9D2;1106 1161 11B1;B9D2;1106 1161 11B1; # (맒; 맒; 맒; 맒; 맒; ) HANGUL SYLLABLE MALM +B9D3;B9D3;1106 1161 11B2;B9D3;1106 1161 11B2; # (맓; 맓; 맓; 맓; 맓; ) HANGUL SYLLABLE MALB +B9D4;B9D4;1106 1161 11B3;B9D4;1106 1161 11B3; # (맔; 맔; 맔; 맔; 맔; ) HANGUL SYLLABLE MALS +B9D5;B9D5;1106 1161 11B4;B9D5;1106 1161 11B4; # (맕; 맕; 맕; 맕; 맕; ) HANGUL SYLLABLE MALT +B9D6;B9D6;1106 1161 11B5;B9D6;1106 1161 11B5; # (맖; 맖; 맖; 맖; 맖; ) HANGUL SYLLABLE MALP +B9D7;B9D7;1106 1161 11B6;B9D7;1106 1161 11B6; # (맗; 맗; 맗; 맗; 맗; ) HANGUL SYLLABLE MALH +B9D8;B9D8;1106 1161 11B7;B9D8;1106 1161 11B7; # (맘; 맘; 맘; 맘; 맘; ) HANGUL SYLLABLE MAM +B9D9;B9D9;1106 1161 11B8;B9D9;1106 1161 11B8; # (맙; 맙; 맙; 맙; 맙; ) HANGUL SYLLABLE MAB +B9DA;B9DA;1106 1161 11B9;B9DA;1106 1161 11B9; # (맚; 맚; 맚; 맚; 맚; ) HANGUL SYLLABLE MABS +B9DB;B9DB;1106 1161 11BA;B9DB;1106 1161 11BA; # (맛; 맛; 맛; 맛; 맛; ) HANGUL SYLLABLE MAS +B9DC;B9DC;1106 1161 11BB;B9DC;1106 1161 11BB; # (맜; 맜; 맜; 맜; 맜; ) HANGUL SYLLABLE MASS +B9DD;B9DD;1106 1161 11BC;B9DD;1106 1161 11BC; # (망; 망; 망; 망; 망; ) HANGUL SYLLABLE MANG +B9DE;B9DE;1106 1161 11BD;B9DE;1106 1161 11BD; # (맞; 맞; 맞; 맞; 맞; ) HANGUL SYLLABLE MAJ +B9DF;B9DF;1106 1161 11BE;B9DF;1106 1161 11BE; # (맟; 맟; 맟; 맟; 맟; ) HANGUL SYLLABLE MAC +B9E0;B9E0;1106 1161 11BF;B9E0;1106 1161 11BF; # (맠; 맠; 맠; 맠; 맠; ) HANGUL SYLLABLE MAK +B9E1;B9E1;1106 1161 11C0;B9E1;1106 1161 11C0; # (맡; 맡; 맡; 맡; 맡; ) HANGUL SYLLABLE MAT +B9E2;B9E2;1106 1161 11C1;B9E2;1106 1161 11C1; # (맢; 맢; 맢; 맢; 맢; ) HANGUL SYLLABLE MAP +B9E3;B9E3;1106 1161 11C2;B9E3;1106 1161 11C2; # (맣; 맣; 맣; 맣; 맣; ) HANGUL SYLLABLE MAH +B9E4;B9E4;1106 1162;B9E4;1106 1162; # (매; 매; 매; 매; 매; ) HANGUL SYLLABLE MAE +B9E5;B9E5;1106 1162 11A8;B9E5;1106 1162 11A8; # (맥; 맥; 맥; 맥; 맥; ) HANGUL SYLLABLE MAEG +B9E6;B9E6;1106 1162 11A9;B9E6;1106 1162 11A9; # (맦; 맦; 맦; 맦; 맦; ) HANGUL SYLLABLE MAEGG +B9E7;B9E7;1106 1162 11AA;B9E7;1106 1162 11AA; # (맧; 맧; 맧; 맧; 맧; ) HANGUL SYLLABLE MAEGS +B9E8;B9E8;1106 1162 11AB;B9E8;1106 1162 11AB; # (맨; 맨; 맨; 맨; 맨; ) HANGUL SYLLABLE MAEN +B9E9;B9E9;1106 1162 11AC;B9E9;1106 1162 11AC; # (맩; 맩; 맩; 맩; 맩; ) HANGUL SYLLABLE MAENJ +B9EA;B9EA;1106 1162 11AD;B9EA;1106 1162 11AD; # (맪; 맪; 맪; 맪; 맪; ) HANGUL SYLLABLE MAENH +B9EB;B9EB;1106 1162 11AE;B9EB;1106 1162 11AE; # (맫; 맫; 맫; 맫; 맫; ) HANGUL SYLLABLE MAED +B9EC;B9EC;1106 1162 11AF;B9EC;1106 1162 11AF; # (맬; 맬; 맬; 맬; 맬; ) HANGUL SYLLABLE MAEL +B9ED;B9ED;1106 1162 11B0;B9ED;1106 1162 11B0; # (맭; 맭; 맭; 맭; 맭; ) HANGUL SYLLABLE MAELG +B9EE;B9EE;1106 1162 11B1;B9EE;1106 1162 11B1; # (맮; 맮; 맮; 맮; 맮; ) HANGUL SYLLABLE MAELM +B9EF;B9EF;1106 1162 11B2;B9EF;1106 1162 11B2; # (맯; 맯; 맯; 맯; 맯; ) HANGUL SYLLABLE MAELB +B9F0;B9F0;1106 1162 11B3;B9F0;1106 1162 11B3; # (맰; 맰; 맰; 맰; 맰; ) HANGUL SYLLABLE MAELS +B9F1;B9F1;1106 1162 11B4;B9F1;1106 1162 11B4; # (맱; 맱; 맱; 맱; 맱; ) HANGUL SYLLABLE MAELT +B9F2;B9F2;1106 1162 11B5;B9F2;1106 1162 11B5; # (맲; 맲; 맲; 맲; 맲; ) HANGUL SYLLABLE MAELP +B9F3;B9F3;1106 1162 11B6;B9F3;1106 1162 11B6; # (맳; 맳; 맳; 맳; 맳; ) HANGUL SYLLABLE MAELH +B9F4;B9F4;1106 1162 11B7;B9F4;1106 1162 11B7; # (맴; 맴; 맴; 맴; 맴; ) HANGUL SYLLABLE MAEM +B9F5;B9F5;1106 1162 11B8;B9F5;1106 1162 11B8; # (맵; 맵; 맵; 맵; 맵; ) HANGUL SYLLABLE MAEB +B9F6;B9F6;1106 1162 11B9;B9F6;1106 1162 11B9; # (맶; 맶; 맶; 맶; 맶; ) HANGUL SYLLABLE MAEBS +B9F7;B9F7;1106 1162 11BA;B9F7;1106 1162 11BA; # (맷; 맷; 맷; 맷; 맷; ) HANGUL SYLLABLE MAES +B9F8;B9F8;1106 1162 11BB;B9F8;1106 1162 11BB; # (맸; 맸; 맸; 맸; 맸; ) HANGUL SYLLABLE MAESS +B9F9;B9F9;1106 1162 11BC;B9F9;1106 1162 11BC; # (맹; 맹; 맹; 맹; 맹; ) HANGUL SYLLABLE MAENG +B9FA;B9FA;1106 1162 11BD;B9FA;1106 1162 11BD; # (맺; 맺; 맺; 맺; 맺; ) HANGUL SYLLABLE MAEJ +B9FB;B9FB;1106 1162 11BE;B9FB;1106 1162 11BE; # (맻; 맻; 맻; 맻; 맻; ) HANGUL SYLLABLE MAEC +B9FC;B9FC;1106 1162 11BF;B9FC;1106 1162 11BF; # (맼; 맼; 맼; 맼; 맼; ) HANGUL SYLLABLE MAEK +B9FD;B9FD;1106 1162 11C0;B9FD;1106 1162 11C0; # (맽; 맽; 맽; 맽; 맽; ) HANGUL SYLLABLE MAET +B9FE;B9FE;1106 1162 11C1;B9FE;1106 1162 11C1; # (맾; 맾; 맾; 맾; 맾; ) HANGUL SYLLABLE MAEP +B9FF;B9FF;1106 1162 11C2;B9FF;1106 1162 11C2; # (맿; 맿; 맿; 맿; 맿; ) HANGUL SYLLABLE MAEH +BA00;BA00;1106 1163;BA00;1106 1163; # (먀; 먀; 먀; 먀; 먀; ) HANGUL SYLLABLE MYA +BA01;BA01;1106 1163 11A8;BA01;1106 1163 11A8; # (먁; 먁; 먁; 먁; 먁; ) HANGUL SYLLABLE MYAG +BA02;BA02;1106 1163 11A9;BA02;1106 1163 11A9; # (먂; 먂; 먂; 먂; 먂; ) HANGUL SYLLABLE MYAGG +BA03;BA03;1106 1163 11AA;BA03;1106 1163 11AA; # (먃; 먃; 먃; 먃; 먃; ) HANGUL SYLLABLE MYAGS +BA04;BA04;1106 1163 11AB;BA04;1106 1163 11AB; # (먄; 먄; 먄; 먄; 먄; ) HANGUL SYLLABLE MYAN +BA05;BA05;1106 1163 11AC;BA05;1106 1163 11AC; # (먅; 먅; 먅; 먅; 먅; ) HANGUL SYLLABLE MYANJ +BA06;BA06;1106 1163 11AD;BA06;1106 1163 11AD; # (먆; 먆; 먆; 먆; 먆; ) HANGUL SYLLABLE MYANH +BA07;BA07;1106 1163 11AE;BA07;1106 1163 11AE; # (먇; 먇; 먇; 먇; 먇; ) HANGUL SYLLABLE MYAD +BA08;BA08;1106 1163 11AF;BA08;1106 1163 11AF; # (먈; 먈; 먈; 먈; 먈; ) HANGUL SYLLABLE MYAL +BA09;BA09;1106 1163 11B0;BA09;1106 1163 11B0; # (먉; 먉; 먉; 먉; 먉; ) HANGUL SYLLABLE MYALG +BA0A;BA0A;1106 1163 11B1;BA0A;1106 1163 11B1; # (먊; 먊; 먊; 먊; 먊; ) HANGUL SYLLABLE MYALM +BA0B;BA0B;1106 1163 11B2;BA0B;1106 1163 11B2; # (먋; 먋; 먋; 먋; 먋; ) HANGUL SYLLABLE MYALB +BA0C;BA0C;1106 1163 11B3;BA0C;1106 1163 11B3; # (먌; 먌; 먌; 먌; 먌; ) HANGUL SYLLABLE MYALS +BA0D;BA0D;1106 1163 11B4;BA0D;1106 1163 11B4; # (먍; 먍; 먍; 먍; 먍; ) HANGUL SYLLABLE MYALT +BA0E;BA0E;1106 1163 11B5;BA0E;1106 1163 11B5; # (먎; 먎; 먎; 먎; 먎; ) HANGUL SYLLABLE MYALP +BA0F;BA0F;1106 1163 11B6;BA0F;1106 1163 11B6; # (먏; 먏; 먏; 먏; 먏; ) HANGUL SYLLABLE MYALH +BA10;BA10;1106 1163 11B7;BA10;1106 1163 11B7; # (먐; 먐; 먐; 먐; 먐; ) HANGUL SYLLABLE MYAM +BA11;BA11;1106 1163 11B8;BA11;1106 1163 11B8; # (먑; 먑; 먑; 먑; 먑; ) HANGUL SYLLABLE MYAB +BA12;BA12;1106 1163 11B9;BA12;1106 1163 11B9; # (먒; 먒; 먒; 먒; 먒; ) HANGUL SYLLABLE MYABS +BA13;BA13;1106 1163 11BA;BA13;1106 1163 11BA; # (먓; 먓; 먓; 먓; 먓; ) HANGUL SYLLABLE MYAS +BA14;BA14;1106 1163 11BB;BA14;1106 1163 11BB; # (먔; 먔; 먔; 먔; 먔; ) HANGUL SYLLABLE MYASS +BA15;BA15;1106 1163 11BC;BA15;1106 1163 11BC; # (먕; 먕; 먕; 먕; 먕; ) HANGUL SYLLABLE MYANG +BA16;BA16;1106 1163 11BD;BA16;1106 1163 11BD; # (먖; 먖; 먖; 먖; 먖; ) HANGUL SYLLABLE MYAJ +BA17;BA17;1106 1163 11BE;BA17;1106 1163 11BE; # (먗; 먗; 먗; 먗; 먗; ) HANGUL SYLLABLE MYAC +BA18;BA18;1106 1163 11BF;BA18;1106 1163 11BF; # (먘; 먘; 먘; 먘; 먘; ) HANGUL SYLLABLE MYAK +BA19;BA19;1106 1163 11C0;BA19;1106 1163 11C0; # (먙; 먙; 먙; 먙; 먙; ) HANGUL SYLLABLE MYAT +BA1A;BA1A;1106 1163 11C1;BA1A;1106 1163 11C1; # (먚; 먚; 먚; 먚; 먚; ) HANGUL SYLLABLE MYAP +BA1B;BA1B;1106 1163 11C2;BA1B;1106 1163 11C2; # (먛; 먛; 먛; 먛; 먛; ) HANGUL SYLLABLE MYAH +BA1C;BA1C;1106 1164;BA1C;1106 1164; # (먜; 먜; 먜; 먜; 먜; ) HANGUL SYLLABLE MYAE +BA1D;BA1D;1106 1164 11A8;BA1D;1106 1164 11A8; # (먝; 먝; 먝; 먝; 먝; ) HANGUL SYLLABLE MYAEG +BA1E;BA1E;1106 1164 11A9;BA1E;1106 1164 11A9; # (먞; 먞; 먞; 먞; 먞; ) HANGUL SYLLABLE MYAEGG +BA1F;BA1F;1106 1164 11AA;BA1F;1106 1164 11AA; # (먟; 먟; 먟; 먟; 먟; ) HANGUL SYLLABLE MYAEGS +BA20;BA20;1106 1164 11AB;BA20;1106 1164 11AB; # (먠; 먠; 먠; 먠; 먠; ) HANGUL SYLLABLE MYAEN +BA21;BA21;1106 1164 11AC;BA21;1106 1164 11AC; # (먡; 먡; 먡; 먡; 먡; ) HANGUL SYLLABLE MYAENJ +BA22;BA22;1106 1164 11AD;BA22;1106 1164 11AD; # (먢; 먢; 먢; 먢; 먢; ) HANGUL SYLLABLE MYAENH +BA23;BA23;1106 1164 11AE;BA23;1106 1164 11AE; # (먣; 먣; 먣; 먣; 먣; ) HANGUL SYLLABLE MYAED +BA24;BA24;1106 1164 11AF;BA24;1106 1164 11AF; # (먤; 먤; 먤; 먤; 먤; ) HANGUL SYLLABLE MYAEL +BA25;BA25;1106 1164 11B0;BA25;1106 1164 11B0; # (먥; 먥; 먥; 먥; 먥; ) HANGUL SYLLABLE MYAELG +BA26;BA26;1106 1164 11B1;BA26;1106 1164 11B1; # (먦; 먦; 먦; 먦; 먦; ) HANGUL SYLLABLE MYAELM +BA27;BA27;1106 1164 11B2;BA27;1106 1164 11B2; # (먧; 먧; 먧; 먧; 먧; ) HANGUL SYLLABLE MYAELB +BA28;BA28;1106 1164 11B3;BA28;1106 1164 11B3; # (먨; 먨; 먨; 먨; 먨; ) HANGUL SYLLABLE MYAELS +BA29;BA29;1106 1164 11B4;BA29;1106 1164 11B4; # (먩; 먩; 먩; 먩; 먩; ) HANGUL SYLLABLE MYAELT +BA2A;BA2A;1106 1164 11B5;BA2A;1106 1164 11B5; # (먪; 먪; 먪; 먪; 먪; ) HANGUL SYLLABLE MYAELP +BA2B;BA2B;1106 1164 11B6;BA2B;1106 1164 11B6; # (먫; 먫; 먫; 먫; 먫; ) HANGUL SYLLABLE MYAELH +BA2C;BA2C;1106 1164 11B7;BA2C;1106 1164 11B7; # (먬; 먬; 먬; 먬; 먬; ) HANGUL SYLLABLE MYAEM +BA2D;BA2D;1106 1164 11B8;BA2D;1106 1164 11B8; # (먭; 먭; 먭; 먭; 먭; ) HANGUL SYLLABLE MYAEB +BA2E;BA2E;1106 1164 11B9;BA2E;1106 1164 11B9; # (먮; 먮; 먮; 먮; 먮; ) HANGUL SYLLABLE MYAEBS +BA2F;BA2F;1106 1164 11BA;BA2F;1106 1164 11BA; # (먯; 먯; 먯; 먯; 먯; ) HANGUL SYLLABLE MYAES +BA30;BA30;1106 1164 11BB;BA30;1106 1164 11BB; # (먰; 먰; 먰; 먰; 먰; ) HANGUL SYLLABLE MYAESS +BA31;BA31;1106 1164 11BC;BA31;1106 1164 11BC; # (먱; 먱; 먱; 먱; 먱; ) HANGUL SYLLABLE MYAENG +BA32;BA32;1106 1164 11BD;BA32;1106 1164 11BD; # (먲; 먲; 먲; 먲; 먲; ) HANGUL SYLLABLE MYAEJ +BA33;BA33;1106 1164 11BE;BA33;1106 1164 11BE; # (먳; 먳; 먳; 먳; 먳; ) HANGUL SYLLABLE MYAEC +BA34;BA34;1106 1164 11BF;BA34;1106 1164 11BF; # (먴; 먴; 먴; 먴; 먴; ) HANGUL SYLLABLE MYAEK +BA35;BA35;1106 1164 11C0;BA35;1106 1164 11C0; # (먵; 먵; 먵; 먵; 먵; ) HANGUL SYLLABLE MYAET +BA36;BA36;1106 1164 11C1;BA36;1106 1164 11C1; # (먶; 먶; 먶; 먶; 먶; ) HANGUL SYLLABLE MYAEP +BA37;BA37;1106 1164 11C2;BA37;1106 1164 11C2; # (먷; 먷; 먷; 먷; 먷; ) HANGUL SYLLABLE MYAEH +BA38;BA38;1106 1165;BA38;1106 1165; # (머; 머; 머; 머; 머; ) HANGUL SYLLABLE MEO +BA39;BA39;1106 1165 11A8;BA39;1106 1165 11A8; # (먹; 먹; 먹; 먹; 먹; ) HANGUL SYLLABLE MEOG +BA3A;BA3A;1106 1165 11A9;BA3A;1106 1165 11A9; # (먺; 먺; 먺; 먺; 먺; ) HANGUL SYLLABLE MEOGG +BA3B;BA3B;1106 1165 11AA;BA3B;1106 1165 11AA; # (먻; 먻; 먻; 먻; 먻; ) HANGUL SYLLABLE MEOGS +BA3C;BA3C;1106 1165 11AB;BA3C;1106 1165 11AB; # (먼; 먼; 먼; 먼; 먼; ) HANGUL SYLLABLE MEON +BA3D;BA3D;1106 1165 11AC;BA3D;1106 1165 11AC; # (먽; 먽; 먽; 먽; 먽; ) HANGUL SYLLABLE MEONJ +BA3E;BA3E;1106 1165 11AD;BA3E;1106 1165 11AD; # (먾; 먾; 먾; 먾; 먾; ) HANGUL SYLLABLE MEONH +BA3F;BA3F;1106 1165 11AE;BA3F;1106 1165 11AE; # (먿; 먿; 먿; 먿; 먿; ) HANGUL SYLLABLE MEOD +BA40;BA40;1106 1165 11AF;BA40;1106 1165 11AF; # (멀; 멀; 멀; 멀; 멀; ) HANGUL SYLLABLE MEOL +BA41;BA41;1106 1165 11B0;BA41;1106 1165 11B0; # (멁; 멁; 멁; 멁; 멁; ) HANGUL SYLLABLE MEOLG +BA42;BA42;1106 1165 11B1;BA42;1106 1165 11B1; # (멂; 멂; 멂; 멂; 멂; ) HANGUL SYLLABLE MEOLM +BA43;BA43;1106 1165 11B2;BA43;1106 1165 11B2; # (멃; 멃; 멃; 멃; 멃; ) HANGUL SYLLABLE MEOLB +BA44;BA44;1106 1165 11B3;BA44;1106 1165 11B3; # (멄; 멄; 멄; 멄; 멄; ) HANGUL SYLLABLE MEOLS +BA45;BA45;1106 1165 11B4;BA45;1106 1165 11B4; # (멅; 멅; 멅; 멅; 멅; ) HANGUL SYLLABLE MEOLT +BA46;BA46;1106 1165 11B5;BA46;1106 1165 11B5; # (멆; 멆; 멆; 멆; 멆; ) HANGUL SYLLABLE MEOLP +BA47;BA47;1106 1165 11B6;BA47;1106 1165 11B6; # (멇; 멇; 멇; 멇; 멇; ) HANGUL SYLLABLE MEOLH +BA48;BA48;1106 1165 11B7;BA48;1106 1165 11B7; # (멈; 멈; 멈; 멈; 멈; ) HANGUL SYLLABLE MEOM +BA49;BA49;1106 1165 11B8;BA49;1106 1165 11B8; # (멉; 멉; 멉; 멉; 멉; ) HANGUL SYLLABLE MEOB +BA4A;BA4A;1106 1165 11B9;BA4A;1106 1165 11B9; # (멊; 멊; 멊; 멊; 멊; ) HANGUL SYLLABLE MEOBS +BA4B;BA4B;1106 1165 11BA;BA4B;1106 1165 11BA; # (멋; 멋; 멋; 멋; 멋; ) HANGUL SYLLABLE MEOS +BA4C;BA4C;1106 1165 11BB;BA4C;1106 1165 11BB; # (멌; 멌; 멌; 멌; 멌; ) HANGUL SYLLABLE MEOSS +BA4D;BA4D;1106 1165 11BC;BA4D;1106 1165 11BC; # (멍; 멍; 멍; 멍; 멍; ) HANGUL SYLLABLE MEONG +BA4E;BA4E;1106 1165 11BD;BA4E;1106 1165 11BD; # (멎; 멎; 멎; 멎; 멎; ) HANGUL SYLLABLE MEOJ +BA4F;BA4F;1106 1165 11BE;BA4F;1106 1165 11BE; # (멏; 멏; 멏; 멏; 멏; ) HANGUL SYLLABLE MEOC +BA50;BA50;1106 1165 11BF;BA50;1106 1165 11BF; # (멐; 멐; 멐; 멐; 멐; ) HANGUL SYLLABLE MEOK +BA51;BA51;1106 1165 11C0;BA51;1106 1165 11C0; # (멑; 멑; 멑; 멑; 멑; ) HANGUL SYLLABLE MEOT +BA52;BA52;1106 1165 11C1;BA52;1106 1165 11C1; # (멒; 멒; 멒; 멒; 멒; ) HANGUL SYLLABLE MEOP +BA53;BA53;1106 1165 11C2;BA53;1106 1165 11C2; # (멓; 멓; 멓; 멓; 멓; ) HANGUL SYLLABLE MEOH +BA54;BA54;1106 1166;BA54;1106 1166; # (메; 메; 메; 메; 메; ) HANGUL SYLLABLE ME +BA55;BA55;1106 1166 11A8;BA55;1106 1166 11A8; # (멕; 멕; 멕; 멕; 멕; ) HANGUL SYLLABLE MEG +BA56;BA56;1106 1166 11A9;BA56;1106 1166 11A9; # (멖; 멖; 멖; 멖; 멖; ) HANGUL SYLLABLE MEGG +BA57;BA57;1106 1166 11AA;BA57;1106 1166 11AA; # (멗; 멗; 멗; 멗; 멗; ) HANGUL SYLLABLE MEGS +BA58;BA58;1106 1166 11AB;BA58;1106 1166 11AB; # (멘; 멘; 멘; 멘; 멘; ) HANGUL SYLLABLE MEN +BA59;BA59;1106 1166 11AC;BA59;1106 1166 11AC; # (멙; 멙; 멙; 멙; 멙; ) HANGUL SYLLABLE MENJ +BA5A;BA5A;1106 1166 11AD;BA5A;1106 1166 11AD; # (멚; 멚; 멚; 멚; 멚; ) HANGUL SYLLABLE MENH +BA5B;BA5B;1106 1166 11AE;BA5B;1106 1166 11AE; # (멛; 멛; 멛; 멛; 멛; ) HANGUL SYLLABLE MED +BA5C;BA5C;1106 1166 11AF;BA5C;1106 1166 11AF; # (멜; 멜; 멜; 멜; 멜; ) HANGUL SYLLABLE MEL +BA5D;BA5D;1106 1166 11B0;BA5D;1106 1166 11B0; # (멝; 멝; 멝; 멝; 멝; ) HANGUL SYLLABLE MELG +BA5E;BA5E;1106 1166 11B1;BA5E;1106 1166 11B1; # (멞; 멞; 멞; 멞; 멞; ) HANGUL SYLLABLE MELM +BA5F;BA5F;1106 1166 11B2;BA5F;1106 1166 11B2; # (멟; 멟; 멟; 멟; 멟; ) HANGUL SYLLABLE MELB +BA60;BA60;1106 1166 11B3;BA60;1106 1166 11B3; # (멠; 멠; 멠; 멠; 멠; ) HANGUL SYLLABLE MELS +BA61;BA61;1106 1166 11B4;BA61;1106 1166 11B4; # (멡; 멡; 멡; 멡; 멡; ) HANGUL SYLLABLE MELT +BA62;BA62;1106 1166 11B5;BA62;1106 1166 11B5; # (멢; 멢; 멢; 멢; 멢; ) HANGUL SYLLABLE MELP +BA63;BA63;1106 1166 11B6;BA63;1106 1166 11B6; # (멣; 멣; 멣; 멣; 멣; ) HANGUL SYLLABLE MELH +BA64;BA64;1106 1166 11B7;BA64;1106 1166 11B7; # (멤; 멤; 멤; 멤; 멤; ) HANGUL SYLLABLE MEM +BA65;BA65;1106 1166 11B8;BA65;1106 1166 11B8; # (멥; 멥; 멥; 멥; 멥; ) HANGUL SYLLABLE MEB +BA66;BA66;1106 1166 11B9;BA66;1106 1166 11B9; # (멦; 멦; 멦; 멦; 멦; ) HANGUL SYLLABLE MEBS +BA67;BA67;1106 1166 11BA;BA67;1106 1166 11BA; # (멧; 멧; 멧; 멧; 멧; ) HANGUL SYLLABLE MES +BA68;BA68;1106 1166 11BB;BA68;1106 1166 11BB; # (멨; 멨; 멨; 멨; 멨; ) HANGUL SYLLABLE MESS +BA69;BA69;1106 1166 11BC;BA69;1106 1166 11BC; # (멩; 멩; 멩; 멩; 멩; ) HANGUL SYLLABLE MENG +BA6A;BA6A;1106 1166 11BD;BA6A;1106 1166 11BD; # (멪; 멪; 멪; 멪; 멪; ) HANGUL SYLLABLE MEJ +BA6B;BA6B;1106 1166 11BE;BA6B;1106 1166 11BE; # (멫; 멫; 멫; 멫; 멫; ) HANGUL SYLLABLE MEC +BA6C;BA6C;1106 1166 11BF;BA6C;1106 1166 11BF; # (멬; 멬; 멬; 멬; 멬; ) HANGUL SYLLABLE MEK +BA6D;BA6D;1106 1166 11C0;BA6D;1106 1166 11C0; # (멭; 멭; 멭; 멭; 멭; ) HANGUL SYLLABLE MET +BA6E;BA6E;1106 1166 11C1;BA6E;1106 1166 11C1; # (멮; 멮; 멮; 멮; 멮; ) HANGUL SYLLABLE MEP +BA6F;BA6F;1106 1166 11C2;BA6F;1106 1166 11C2; # (멯; 멯; 멯; 멯; 멯; ) HANGUL SYLLABLE MEH +BA70;BA70;1106 1167;BA70;1106 1167; # (며; 며; 며; 며; 며; ) HANGUL SYLLABLE MYEO +BA71;BA71;1106 1167 11A8;BA71;1106 1167 11A8; # (멱; 멱; 멱; 멱; 멱; ) HANGUL SYLLABLE MYEOG +BA72;BA72;1106 1167 11A9;BA72;1106 1167 11A9; # (멲; 멲; 멲; 멲; 멲; ) HANGUL SYLLABLE MYEOGG +BA73;BA73;1106 1167 11AA;BA73;1106 1167 11AA; # (멳; 멳; 멳; 멳; 멳; ) HANGUL SYLLABLE MYEOGS +BA74;BA74;1106 1167 11AB;BA74;1106 1167 11AB; # (면; 면; 면; 면; 면; ) HANGUL SYLLABLE MYEON +BA75;BA75;1106 1167 11AC;BA75;1106 1167 11AC; # (멵; 멵; 멵; 멵; 멵; ) HANGUL SYLLABLE MYEONJ +BA76;BA76;1106 1167 11AD;BA76;1106 1167 11AD; # (멶; 멶; 멶; 멶; 멶; ) HANGUL SYLLABLE MYEONH +BA77;BA77;1106 1167 11AE;BA77;1106 1167 11AE; # (멷; 멷; 멷; 멷; 멷; ) HANGUL SYLLABLE MYEOD +BA78;BA78;1106 1167 11AF;BA78;1106 1167 11AF; # (멸; 멸; 멸; 멸; 멸; ) HANGUL SYLLABLE MYEOL +BA79;BA79;1106 1167 11B0;BA79;1106 1167 11B0; # (멹; 멹; 멹; 멹; 멹; ) HANGUL SYLLABLE MYEOLG +BA7A;BA7A;1106 1167 11B1;BA7A;1106 1167 11B1; # (멺; 멺; 멺; 멺; 멺; ) HANGUL SYLLABLE MYEOLM +BA7B;BA7B;1106 1167 11B2;BA7B;1106 1167 11B2; # (멻; 멻; 멻; 멻; 멻; ) HANGUL SYLLABLE MYEOLB +BA7C;BA7C;1106 1167 11B3;BA7C;1106 1167 11B3; # (멼; 멼; 멼; 멼; 멼; ) HANGUL SYLLABLE MYEOLS +BA7D;BA7D;1106 1167 11B4;BA7D;1106 1167 11B4; # (멽; 멽; 멽; 멽; 멽; ) HANGUL SYLLABLE MYEOLT +BA7E;BA7E;1106 1167 11B5;BA7E;1106 1167 11B5; # (멾; 멾; 멾; 멾; 멾; ) HANGUL SYLLABLE MYEOLP +BA7F;BA7F;1106 1167 11B6;BA7F;1106 1167 11B6; # (멿; 멿; 멿; 멿; 멿; ) HANGUL SYLLABLE MYEOLH +BA80;BA80;1106 1167 11B7;BA80;1106 1167 11B7; # (몀; 몀; 몀; 몀; 몀; ) HANGUL SYLLABLE MYEOM +BA81;BA81;1106 1167 11B8;BA81;1106 1167 11B8; # (몁; 몁; 몁; 몁; 몁; ) HANGUL SYLLABLE MYEOB +BA82;BA82;1106 1167 11B9;BA82;1106 1167 11B9; # (몂; 몂; 몂; 몂; 몂; ) HANGUL SYLLABLE MYEOBS +BA83;BA83;1106 1167 11BA;BA83;1106 1167 11BA; # (몃; 몃; 몃; 몃; 몃; ) HANGUL SYLLABLE MYEOS +BA84;BA84;1106 1167 11BB;BA84;1106 1167 11BB; # (몄; 몄; 몄; 몄; 몄; ) HANGUL SYLLABLE MYEOSS +BA85;BA85;1106 1167 11BC;BA85;1106 1167 11BC; # (명; 명; 명; 명; 명; ) HANGUL SYLLABLE MYEONG +BA86;BA86;1106 1167 11BD;BA86;1106 1167 11BD; # (몆; 몆; 몆; 몆; 몆; ) HANGUL SYLLABLE MYEOJ +BA87;BA87;1106 1167 11BE;BA87;1106 1167 11BE; # (몇; 몇; 몇; 몇; 몇; ) HANGUL SYLLABLE MYEOC +BA88;BA88;1106 1167 11BF;BA88;1106 1167 11BF; # (몈; 몈; 몈; 몈; 몈; ) HANGUL SYLLABLE MYEOK +BA89;BA89;1106 1167 11C0;BA89;1106 1167 11C0; # (몉; 몉; 몉; 몉; 몉; ) HANGUL SYLLABLE MYEOT +BA8A;BA8A;1106 1167 11C1;BA8A;1106 1167 11C1; # (몊; 몊; 몊; 몊; 몊; ) HANGUL SYLLABLE MYEOP +BA8B;BA8B;1106 1167 11C2;BA8B;1106 1167 11C2; # (몋; 몋; 몋; 몋; 몋; ) HANGUL SYLLABLE MYEOH +BA8C;BA8C;1106 1168;BA8C;1106 1168; # (몌; 몌; 몌; 몌; 몌; ) HANGUL SYLLABLE MYE +BA8D;BA8D;1106 1168 11A8;BA8D;1106 1168 11A8; # (몍; 몍; 몍; 몍; 몍; ) HANGUL SYLLABLE MYEG +BA8E;BA8E;1106 1168 11A9;BA8E;1106 1168 11A9; # (몎; 몎; 몎; 몎; 몎; ) HANGUL SYLLABLE MYEGG +BA8F;BA8F;1106 1168 11AA;BA8F;1106 1168 11AA; # (몏; 몏; 몏; 몏; 몏; ) HANGUL SYLLABLE MYEGS +BA90;BA90;1106 1168 11AB;BA90;1106 1168 11AB; # (몐; 몐; 몐; 몐; 몐; ) HANGUL SYLLABLE MYEN +BA91;BA91;1106 1168 11AC;BA91;1106 1168 11AC; # (몑; 몑; 몑; 몑; 몑; ) HANGUL SYLLABLE MYENJ +BA92;BA92;1106 1168 11AD;BA92;1106 1168 11AD; # (몒; 몒; 몒; 몒; 몒; ) HANGUL SYLLABLE MYENH +BA93;BA93;1106 1168 11AE;BA93;1106 1168 11AE; # (몓; 몓; 몓; 몓; 몓; ) HANGUL SYLLABLE MYED +BA94;BA94;1106 1168 11AF;BA94;1106 1168 11AF; # (몔; 몔; 몔; 몔; 몔; ) HANGUL SYLLABLE MYEL +BA95;BA95;1106 1168 11B0;BA95;1106 1168 11B0; # (몕; 몕; 몕; 몕; 몕; ) HANGUL SYLLABLE MYELG +BA96;BA96;1106 1168 11B1;BA96;1106 1168 11B1; # (몖; 몖; 몖; 몖; 몖; ) HANGUL SYLLABLE MYELM +BA97;BA97;1106 1168 11B2;BA97;1106 1168 11B2; # (몗; 몗; 몗; 몗; 몗; ) HANGUL SYLLABLE MYELB +BA98;BA98;1106 1168 11B3;BA98;1106 1168 11B3; # (몘; 몘; 몘; 몘; 몘; ) HANGUL SYLLABLE MYELS +BA99;BA99;1106 1168 11B4;BA99;1106 1168 11B4; # (몙; 몙; 몙; 몙; 몙; ) HANGUL SYLLABLE MYELT +BA9A;BA9A;1106 1168 11B5;BA9A;1106 1168 11B5; # (몚; 몚; 몚; 몚; 몚; ) HANGUL SYLLABLE MYELP +BA9B;BA9B;1106 1168 11B6;BA9B;1106 1168 11B6; # (몛; 몛; 몛; 몛; 몛; ) HANGUL SYLLABLE MYELH +BA9C;BA9C;1106 1168 11B7;BA9C;1106 1168 11B7; # (몜; 몜; 몜; 몜; 몜; ) HANGUL SYLLABLE MYEM +BA9D;BA9D;1106 1168 11B8;BA9D;1106 1168 11B8; # (몝; 몝; 몝; 몝; 몝; ) HANGUL SYLLABLE MYEB +BA9E;BA9E;1106 1168 11B9;BA9E;1106 1168 11B9; # (몞; 몞; 몞; 몞; 몞; ) HANGUL SYLLABLE MYEBS +BA9F;BA9F;1106 1168 11BA;BA9F;1106 1168 11BA; # (몟; 몟; 몟; 몟; 몟; ) HANGUL SYLLABLE MYES +BAA0;BAA0;1106 1168 11BB;BAA0;1106 1168 11BB; # (몠; 몠; 몠; 몠; 몠; ) HANGUL SYLLABLE MYESS +BAA1;BAA1;1106 1168 11BC;BAA1;1106 1168 11BC; # (몡; 몡; 몡; 몡; 몡; ) HANGUL SYLLABLE MYENG +BAA2;BAA2;1106 1168 11BD;BAA2;1106 1168 11BD; # (몢; 몢; 몢; 몢; 몢; ) HANGUL SYLLABLE MYEJ +BAA3;BAA3;1106 1168 11BE;BAA3;1106 1168 11BE; # (몣; 몣; 몣; 몣; 몣; ) HANGUL SYLLABLE MYEC +BAA4;BAA4;1106 1168 11BF;BAA4;1106 1168 11BF; # (몤; 몤; 몤; 몤; 몤; ) HANGUL SYLLABLE MYEK +BAA5;BAA5;1106 1168 11C0;BAA5;1106 1168 11C0; # (몥; 몥; 몥; 몥; 몥; ) HANGUL SYLLABLE MYET +BAA6;BAA6;1106 1168 11C1;BAA6;1106 1168 11C1; # (몦; 몦; 몦; 몦; 몦; ) HANGUL SYLLABLE MYEP +BAA7;BAA7;1106 1168 11C2;BAA7;1106 1168 11C2; # (몧; 몧; 몧; 몧; 몧; ) HANGUL SYLLABLE MYEH +BAA8;BAA8;1106 1169;BAA8;1106 1169; # (모; 모; 모; 모; 모; ) HANGUL SYLLABLE MO +BAA9;BAA9;1106 1169 11A8;BAA9;1106 1169 11A8; # (목; 목; 목; 목; 목; ) HANGUL SYLLABLE MOG +BAAA;BAAA;1106 1169 11A9;BAAA;1106 1169 11A9; # (몪; 몪; 몪; 몪; 몪; ) HANGUL SYLLABLE MOGG +BAAB;BAAB;1106 1169 11AA;BAAB;1106 1169 11AA; # (몫; 몫; 몫; 몫; 몫; ) HANGUL SYLLABLE MOGS +BAAC;BAAC;1106 1169 11AB;BAAC;1106 1169 11AB; # (몬; 몬; 몬; 몬; 몬; ) HANGUL SYLLABLE MON +BAAD;BAAD;1106 1169 11AC;BAAD;1106 1169 11AC; # (몭; 몭; 몭; 몭; 몭; ) HANGUL SYLLABLE MONJ +BAAE;BAAE;1106 1169 11AD;BAAE;1106 1169 11AD; # (몮; 몮; 몮; 몮; 몮; ) HANGUL SYLLABLE MONH +BAAF;BAAF;1106 1169 11AE;BAAF;1106 1169 11AE; # (몯; 몯; 몯; 몯; 몯; ) HANGUL SYLLABLE MOD +BAB0;BAB0;1106 1169 11AF;BAB0;1106 1169 11AF; # (몰; 몰; 몰; 몰; 몰; ) HANGUL SYLLABLE MOL +BAB1;BAB1;1106 1169 11B0;BAB1;1106 1169 11B0; # (몱; 몱; 몱; 몱; 몱; ) HANGUL SYLLABLE MOLG +BAB2;BAB2;1106 1169 11B1;BAB2;1106 1169 11B1; # (몲; 몲; 몲; 몲; 몲; ) HANGUL SYLLABLE MOLM +BAB3;BAB3;1106 1169 11B2;BAB3;1106 1169 11B2; # (몳; 몳; 몳; 몳; 몳; ) HANGUL SYLLABLE MOLB +BAB4;BAB4;1106 1169 11B3;BAB4;1106 1169 11B3; # (몴; 몴; 몴; 몴; 몴; ) HANGUL SYLLABLE MOLS +BAB5;BAB5;1106 1169 11B4;BAB5;1106 1169 11B4; # (몵; 몵; 몵; 몵; 몵; ) HANGUL SYLLABLE MOLT +BAB6;BAB6;1106 1169 11B5;BAB6;1106 1169 11B5; # (몶; 몶; 몶; 몶; 몶; ) HANGUL SYLLABLE MOLP +BAB7;BAB7;1106 1169 11B6;BAB7;1106 1169 11B6; # (몷; 몷; 몷; 몷; 몷; ) HANGUL SYLLABLE MOLH +BAB8;BAB8;1106 1169 11B7;BAB8;1106 1169 11B7; # (몸; 몸; 몸; 몸; 몸; ) HANGUL SYLLABLE MOM +BAB9;BAB9;1106 1169 11B8;BAB9;1106 1169 11B8; # (몹; 몹; 몹; 몹; 몹; ) HANGUL SYLLABLE MOB +BABA;BABA;1106 1169 11B9;BABA;1106 1169 11B9; # (몺; 몺; 몺; 몺; 몺; ) HANGUL SYLLABLE MOBS +BABB;BABB;1106 1169 11BA;BABB;1106 1169 11BA; # (못; 못; 못; 못; 못; ) HANGUL SYLLABLE MOS +BABC;BABC;1106 1169 11BB;BABC;1106 1169 11BB; # (몼; 몼; 몼; 몼; 몼; ) HANGUL SYLLABLE MOSS +BABD;BABD;1106 1169 11BC;BABD;1106 1169 11BC; # (몽; 몽; 몽; 몽; 몽; ) HANGUL SYLLABLE MONG +BABE;BABE;1106 1169 11BD;BABE;1106 1169 11BD; # (몾; 몾; 몾; 몾; 몾; ) HANGUL SYLLABLE MOJ +BABF;BABF;1106 1169 11BE;BABF;1106 1169 11BE; # (몿; 몿; 몿; 몿; 몿; ) HANGUL SYLLABLE MOC +BAC0;BAC0;1106 1169 11BF;BAC0;1106 1169 11BF; # (뫀; 뫀; 뫀; 뫀; 뫀; ) HANGUL SYLLABLE MOK +BAC1;BAC1;1106 1169 11C0;BAC1;1106 1169 11C0; # (뫁; 뫁; 뫁; 뫁; 뫁; ) HANGUL SYLLABLE MOT +BAC2;BAC2;1106 1169 11C1;BAC2;1106 1169 11C1; # (뫂; 뫂; 뫂; 뫂; 뫂; ) HANGUL SYLLABLE MOP +BAC3;BAC3;1106 1169 11C2;BAC3;1106 1169 11C2; # (뫃; 뫃; 뫃; 뫃; 뫃; ) HANGUL SYLLABLE MOH +BAC4;BAC4;1106 116A;BAC4;1106 116A; # (뫄; 뫄; 뫄; 뫄; 뫄; ) HANGUL SYLLABLE MWA +BAC5;BAC5;1106 116A 11A8;BAC5;1106 116A 11A8; # (뫅; 뫅; 뫅; 뫅; 뫅; ) HANGUL SYLLABLE MWAG +BAC6;BAC6;1106 116A 11A9;BAC6;1106 116A 11A9; # (뫆; 뫆; 뫆; 뫆; 뫆; ) HANGUL SYLLABLE MWAGG +BAC7;BAC7;1106 116A 11AA;BAC7;1106 116A 11AA; # (뫇; 뫇; 뫇; 뫇; 뫇; ) HANGUL SYLLABLE MWAGS +BAC8;BAC8;1106 116A 11AB;BAC8;1106 116A 11AB; # (뫈; 뫈; 뫈; 뫈; 뫈; ) HANGUL SYLLABLE MWAN +BAC9;BAC9;1106 116A 11AC;BAC9;1106 116A 11AC; # (뫉; 뫉; 뫉; 뫉; 뫉; ) HANGUL SYLLABLE MWANJ +BACA;BACA;1106 116A 11AD;BACA;1106 116A 11AD; # (뫊; 뫊; 뫊; 뫊; 뫊; ) HANGUL SYLLABLE MWANH +BACB;BACB;1106 116A 11AE;BACB;1106 116A 11AE; # (뫋; 뫋; 뫋; 뫋; 뫋; ) HANGUL SYLLABLE MWAD +BACC;BACC;1106 116A 11AF;BACC;1106 116A 11AF; # (뫌; 뫌; 뫌; 뫌; 뫌; ) HANGUL SYLLABLE MWAL +BACD;BACD;1106 116A 11B0;BACD;1106 116A 11B0; # (뫍; 뫍; 뫍; 뫍; 뫍; ) HANGUL SYLLABLE MWALG +BACE;BACE;1106 116A 11B1;BACE;1106 116A 11B1; # (뫎; 뫎; 뫎; 뫎; 뫎; ) HANGUL SYLLABLE MWALM +BACF;BACF;1106 116A 11B2;BACF;1106 116A 11B2; # (뫏; 뫏; 뫏; 뫏; 뫏; ) HANGUL SYLLABLE MWALB +BAD0;BAD0;1106 116A 11B3;BAD0;1106 116A 11B3; # (뫐; 뫐; 뫐; 뫐; 뫐; ) HANGUL SYLLABLE MWALS +BAD1;BAD1;1106 116A 11B4;BAD1;1106 116A 11B4; # (뫑; 뫑; 뫑; 뫑; 뫑; ) HANGUL SYLLABLE MWALT +BAD2;BAD2;1106 116A 11B5;BAD2;1106 116A 11B5; # (뫒; 뫒; 뫒; 뫒; 뫒; ) HANGUL SYLLABLE MWALP +BAD3;BAD3;1106 116A 11B6;BAD3;1106 116A 11B6; # (뫓; 뫓; 뫓; 뫓; 뫓; ) HANGUL SYLLABLE MWALH +BAD4;BAD4;1106 116A 11B7;BAD4;1106 116A 11B7; # (뫔; 뫔; 뫔; 뫔; 뫔; ) HANGUL SYLLABLE MWAM +BAD5;BAD5;1106 116A 11B8;BAD5;1106 116A 11B8; # (뫕; 뫕; 뫕; 뫕; 뫕; ) HANGUL SYLLABLE MWAB +BAD6;BAD6;1106 116A 11B9;BAD6;1106 116A 11B9; # (뫖; 뫖; 뫖; 뫖; 뫖; ) HANGUL SYLLABLE MWABS +BAD7;BAD7;1106 116A 11BA;BAD7;1106 116A 11BA; # (뫗; 뫗; 뫗; 뫗; 뫗; ) HANGUL SYLLABLE MWAS +BAD8;BAD8;1106 116A 11BB;BAD8;1106 116A 11BB; # (뫘; 뫘; 뫘; 뫘; 뫘; ) HANGUL SYLLABLE MWASS +BAD9;BAD9;1106 116A 11BC;BAD9;1106 116A 11BC; # (뫙; 뫙; 뫙; 뫙; 뫙; ) HANGUL SYLLABLE MWANG +BADA;BADA;1106 116A 11BD;BADA;1106 116A 11BD; # (뫚; 뫚; 뫚; 뫚; 뫚; ) HANGUL SYLLABLE MWAJ +BADB;BADB;1106 116A 11BE;BADB;1106 116A 11BE; # (뫛; 뫛; 뫛; 뫛; 뫛; ) HANGUL SYLLABLE MWAC +BADC;BADC;1106 116A 11BF;BADC;1106 116A 11BF; # (뫜; 뫜; 뫜; 뫜; 뫜; ) HANGUL SYLLABLE MWAK +BADD;BADD;1106 116A 11C0;BADD;1106 116A 11C0; # (뫝; 뫝; 뫝; 뫝; 뫝; ) HANGUL SYLLABLE MWAT +BADE;BADE;1106 116A 11C1;BADE;1106 116A 11C1; # (뫞; 뫞; 뫞; 뫞; 뫞; ) HANGUL SYLLABLE MWAP +BADF;BADF;1106 116A 11C2;BADF;1106 116A 11C2; # (뫟; 뫟; 뫟; 뫟; 뫟; ) HANGUL SYLLABLE MWAH +BAE0;BAE0;1106 116B;BAE0;1106 116B; # (뫠; 뫠; 뫠; 뫠; 뫠; ) HANGUL SYLLABLE MWAE +BAE1;BAE1;1106 116B 11A8;BAE1;1106 116B 11A8; # (뫡; 뫡; 뫡; 뫡; 뫡; ) HANGUL SYLLABLE MWAEG +BAE2;BAE2;1106 116B 11A9;BAE2;1106 116B 11A9; # (뫢; 뫢; 뫢; 뫢; 뫢; ) HANGUL SYLLABLE MWAEGG +BAE3;BAE3;1106 116B 11AA;BAE3;1106 116B 11AA; # (뫣; 뫣; 뫣; 뫣; 뫣; ) HANGUL SYLLABLE MWAEGS +BAE4;BAE4;1106 116B 11AB;BAE4;1106 116B 11AB; # (뫤; 뫤; 뫤; 뫤; 뫤; ) HANGUL SYLLABLE MWAEN +BAE5;BAE5;1106 116B 11AC;BAE5;1106 116B 11AC; # (뫥; 뫥; 뫥; 뫥; 뫥; ) HANGUL SYLLABLE MWAENJ +BAE6;BAE6;1106 116B 11AD;BAE6;1106 116B 11AD; # (뫦; 뫦; 뫦; 뫦; 뫦; ) HANGUL SYLLABLE MWAENH +BAE7;BAE7;1106 116B 11AE;BAE7;1106 116B 11AE; # (뫧; 뫧; 뫧; 뫧; 뫧; ) HANGUL SYLLABLE MWAED +BAE8;BAE8;1106 116B 11AF;BAE8;1106 116B 11AF; # (뫨; 뫨; 뫨; 뫨; 뫨; ) HANGUL SYLLABLE MWAEL +BAE9;BAE9;1106 116B 11B0;BAE9;1106 116B 11B0; # (뫩; 뫩; 뫩; 뫩; 뫩; ) HANGUL SYLLABLE MWAELG +BAEA;BAEA;1106 116B 11B1;BAEA;1106 116B 11B1; # (뫪; 뫪; 뫪; 뫪; 뫪; ) HANGUL SYLLABLE MWAELM +BAEB;BAEB;1106 116B 11B2;BAEB;1106 116B 11B2; # (뫫; 뫫; 뫫; 뫫; 뫫; ) HANGUL SYLLABLE MWAELB +BAEC;BAEC;1106 116B 11B3;BAEC;1106 116B 11B3; # (뫬; 뫬; 뫬; 뫬; 뫬; ) HANGUL SYLLABLE MWAELS +BAED;BAED;1106 116B 11B4;BAED;1106 116B 11B4; # (뫭; 뫭; 뫭; 뫭; 뫭; ) HANGUL SYLLABLE MWAELT +BAEE;BAEE;1106 116B 11B5;BAEE;1106 116B 11B5; # (뫮; 뫮; 뫮; 뫮; 뫮; ) HANGUL SYLLABLE MWAELP +BAEF;BAEF;1106 116B 11B6;BAEF;1106 116B 11B6; # (뫯; 뫯; 뫯; 뫯; 뫯; ) HANGUL SYLLABLE MWAELH +BAF0;BAF0;1106 116B 11B7;BAF0;1106 116B 11B7; # (뫰; 뫰; 뫰; 뫰; 뫰; ) HANGUL SYLLABLE MWAEM +BAF1;BAF1;1106 116B 11B8;BAF1;1106 116B 11B8; # (뫱; 뫱; 뫱; 뫱; 뫱; ) HANGUL SYLLABLE MWAEB +BAF2;BAF2;1106 116B 11B9;BAF2;1106 116B 11B9; # (뫲; 뫲; 뫲; 뫲; 뫲; ) HANGUL SYLLABLE MWAEBS +BAF3;BAF3;1106 116B 11BA;BAF3;1106 116B 11BA; # (뫳; 뫳; 뫳; 뫳; 뫳; ) HANGUL SYLLABLE MWAES +BAF4;BAF4;1106 116B 11BB;BAF4;1106 116B 11BB; # (뫴; 뫴; 뫴; 뫴; 뫴; ) HANGUL SYLLABLE MWAESS +BAF5;BAF5;1106 116B 11BC;BAF5;1106 116B 11BC; # (뫵; 뫵; 뫵; 뫵; 뫵; ) HANGUL SYLLABLE MWAENG +BAF6;BAF6;1106 116B 11BD;BAF6;1106 116B 11BD; # (뫶; 뫶; 뫶; 뫶; 뫶; ) HANGUL SYLLABLE MWAEJ +BAF7;BAF7;1106 116B 11BE;BAF7;1106 116B 11BE; # (뫷; 뫷; 뫷; 뫷; 뫷; ) HANGUL SYLLABLE MWAEC +BAF8;BAF8;1106 116B 11BF;BAF8;1106 116B 11BF; # (뫸; 뫸; 뫸; 뫸; 뫸; ) HANGUL SYLLABLE MWAEK +BAF9;BAF9;1106 116B 11C0;BAF9;1106 116B 11C0; # (뫹; 뫹; 뫹; 뫹; 뫹; ) HANGUL SYLLABLE MWAET +BAFA;BAFA;1106 116B 11C1;BAFA;1106 116B 11C1; # (뫺; 뫺; 뫺; 뫺; 뫺; ) HANGUL SYLLABLE MWAEP +BAFB;BAFB;1106 116B 11C2;BAFB;1106 116B 11C2; # (뫻; 뫻; 뫻; 뫻; 뫻; ) HANGUL SYLLABLE MWAEH +BAFC;BAFC;1106 116C;BAFC;1106 116C; # (뫼; 뫼; 뫼; 뫼; 뫼; ) HANGUL SYLLABLE MOE +BAFD;BAFD;1106 116C 11A8;BAFD;1106 116C 11A8; # (뫽; 뫽; 뫽; 뫽; 뫽; ) HANGUL SYLLABLE MOEG +BAFE;BAFE;1106 116C 11A9;BAFE;1106 116C 11A9; # (뫾; 뫾; 뫾; 뫾; 뫾; ) HANGUL SYLLABLE MOEGG +BAFF;BAFF;1106 116C 11AA;BAFF;1106 116C 11AA; # (뫿; 뫿; 뫿; 뫿; 뫿; ) HANGUL SYLLABLE MOEGS +BB00;BB00;1106 116C 11AB;BB00;1106 116C 11AB; # (묀; 묀; 묀; 묀; 묀; ) HANGUL SYLLABLE MOEN +BB01;BB01;1106 116C 11AC;BB01;1106 116C 11AC; # (묁; 묁; 묁; 묁; 묁; ) HANGUL SYLLABLE MOENJ +BB02;BB02;1106 116C 11AD;BB02;1106 116C 11AD; # (묂; 묂; 묂; 묂; 묂; ) HANGUL SYLLABLE MOENH +BB03;BB03;1106 116C 11AE;BB03;1106 116C 11AE; # (묃; 묃; 묃; 묃; 묃; ) HANGUL SYLLABLE MOED +BB04;BB04;1106 116C 11AF;BB04;1106 116C 11AF; # (묄; 묄; 묄; 묄; 묄; ) HANGUL SYLLABLE MOEL +BB05;BB05;1106 116C 11B0;BB05;1106 116C 11B0; # (묅; 묅; 묅; 묅; 묅; ) HANGUL SYLLABLE MOELG +BB06;BB06;1106 116C 11B1;BB06;1106 116C 11B1; # (묆; 묆; 묆; 묆; 묆; ) HANGUL SYLLABLE MOELM +BB07;BB07;1106 116C 11B2;BB07;1106 116C 11B2; # (묇; 묇; 묇; 묇; 묇; ) HANGUL SYLLABLE MOELB +BB08;BB08;1106 116C 11B3;BB08;1106 116C 11B3; # (묈; 묈; 묈; 묈; 묈; ) HANGUL SYLLABLE MOELS +BB09;BB09;1106 116C 11B4;BB09;1106 116C 11B4; # (묉; 묉; 묉; 묉; 묉; ) HANGUL SYLLABLE MOELT +BB0A;BB0A;1106 116C 11B5;BB0A;1106 116C 11B5; # (묊; 묊; 묊; 묊; 묊; ) HANGUL SYLLABLE MOELP +BB0B;BB0B;1106 116C 11B6;BB0B;1106 116C 11B6; # (묋; 묋; 묋; 묋; 묋; ) HANGUL SYLLABLE MOELH +BB0C;BB0C;1106 116C 11B7;BB0C;1106 116C 11B7; # (묌; 묌; 묌; 묌; 묌; ) HANGUL SYLLABLE MOEM +BB0D;BB0D;1106 116C 11B8;BB0D;1106 116C 11B8; # (묍; 묍; 묍; 묍; 묍; ) HANGUL SYLLABLE MOEB +BB0E;BB0E;1106 116C 11B9;BB0E;1106 116C 11B9; # (묎; 묎; 묎; 묎; 묎; ) HANGUL SYLLABLE MOEBS +BB0F;BB0F;1106 116C 11BA;BB0F;1106 116C 11BA; # (묏; 묏; 묏; 묏; 묏; ) HANGUL SYLLABLE MOES +BB10;BB10;1106 116C 11BB;BB10;1106 116C 11BB; # (묐; 묐; 묐; 묐; 묐; ) HANGUL SYLLABLE MOESS +BB11;BB11;1106 116C 11BC;BB11;1106 116C 11BC; # (묑; 묑; 묑; 묑; 묑; ) HANGUL SYLLABLE MOENG +BB12;BB12;1106 116C 11BD;BB12;1106 116C 11BD; # (묒; 묒; 묒; 묒; 묒; ) HANGUL SYLLABLE MOEJ +BB13;BB13;1106 116C 11BE;BB13;1106 116C 11BE; # (묓; 묓; 묓; 묓; 묓; ) HANGUL SYLLABLE MOEC +BB14;BB14;1106 116C 11BF;BB14;1106 116C 11BF; # (묔; 묔; 묔; 묔; 묔; ) HANGUL SYLLABLE MOEK +BB15;BB15;1106 116C 11C0;BB15;1106 116C 11C0; # (묕; 묕; 묕; 묕; 묕; ) HANGUL SYLLABLE MOET +BB16;BB16;1106 116C 11C1;BB16;1106 116C 11C1; # (묖; 묖; 묖; 묖; 묖; ) HANGUL SYLLABLE MOEP +BB17;BB17;1106 116C 11C2;BB17;1106 116C 11C2; # (묗; 묗; 묗; 묗; 묗; ) HANGUL SYLLABLE MOEH +BB18;BB18;1106 116D;BB18;1106 116D; # (묘; 묘; 묘; 묘; 묘; ) HANGUL SYLLABLE MYO +BB19;BB19;1106 116D 11A8;BB19;1106 116D 11A8; # (묙; 묙; 묙; 묙; 묙; ) HANGUL SYLLABLE MYOG +BB1A;BB1A;1106 116D 11A9;BB1A;1106 116D 11A9; # (묚; 묚; 묚; 묚; 묚; ) HANGUL SYLLABLE MYOGG +BB1B;BB1B;1106 116D 11AA;BB1B;1106 116D 11AA; # (묛; 묛; 묛; 묛; 묛; ) HANGUL SYLLABLE MYOGS +BB1C;BB1C;1106 116D 11AB;BB1C;1106 116D 11AB; # (묜; 묜; 묜; 묜; 묜; ) HANGUL SYLLABLE MYON +BB1D;BB1D;1106 116D 11AC;BB1D;1106 116D 11AC; # (묝; 묝; 묝; 묝; 묝; ) HANGUL SYLLABLE MYONJ +BB1E;BB1E;1106 116D 11AD;BB1E;1106 116D 11AD; # (묞; 묞; 묞; 묞; 묞; ) HANGUL SYLLABLE MYONH +BB1F;BB1F;1106 116D 11AE;BB1F;1106 116D 11AE; # (묟; 묟; 묟; 묟; 묟; ) HANGUL SYLLABLE MYOD +BB20;BB20;1106 116D 11AF;BB20;1106 116D 11AF; # (묠; 묠; 묠; 묠; 묠; ) HANGUL SYLLABLE MYOL +BB21;BB21;1106 116D 11B0;BB21;1106 116D 11B0; # (묡; 묡; 묡; 묡; 묡; ) HANGUL SYLLABLE MYOLG +BB22;BB22;1106 116D 11B1;BB22;1106 116D 11B1; # (묢; 묢; 묢; 묢; 묢; ) HANGUL SYLLABLE MYOLM +BB23;BB23;1106 116D 11B2;BB23;1106 116D 11B2; # (묣; 묣; 묣; 묣; 묣; ) HANGUL SYLLABLE MYOLB +BB24;BB24;1106 116D 11B3;BB24;1106 116D 11B3; # (묤; 묤; 묤; 묤; 묤; ) HANGUL SYLLABLE MYOLS +BB25;BB25;1106 116D 11B4;BB25;1106 116D 11B4; # (묥; 묥; 묥; 묥; 묥; ) HANGUL SYLLABLE MYOLT +BB26;BB26;1106 116D 11B5;BB26;1106 116D 11B5; # (묦; 묦; 묦; 묦; 묦; ) HANGUL SYLLABLE MYOLP +BB27;BB27;1106 116D 11B6;BB27;1106 116D 11B6; # (묧; 묧; 묧; 묧; 묧; ) HANGUL SYLLABLE MYOLH +BB28;BB28;1106 116D 11B7;BB28;1106 116D 11B7; # (묨; 묨; 묨; 묨; 묨; ) HANGUL SYLLABLE MYOM +BB29;BB29;1106 116D 11B8;BB29;1106 116D 11B8; # (묩; 묩; 묩; 묩; 묩; ) HANGUL SYLLABLE MYOB +BB2A;BB2A;1106 116D 11B9;BB2A;1106 116D 11B9; # (묪; 묪; 묪; 묪; 묪; ) HANGUL SYLLABLE MYOBS +BB2B;BB2B;1106 116D 11BA;BB2B;1106 116D 11BA; # (묫; 묫; 묫; 묫; 묫; ) HANGUL SYLLABLE MYOS +BB2C;BB2C;1106 116D 11BB;BB2C;1106 116D 11BB; # (묬; 묬; 묬; 묬; 묬; ) HANGUL SYLLABLE MYOSS +BB2D;BB2D;1106 116D 11BC;BB2D;1106 116D 11BC; # (묭; 묭; 묭; 묭; 묭; ) HANGUL SYLLABLE MYONG +BB2E;BB2E;1106 116D 11BD;BB2E;1106 116D 11BD; # (묮; 묮; 묮; 묮; 묮; ) HANGUL SYLLABLE MYOJ +BB2F;BB2F;1106 116D 11BE;BB2F;1106 116D 11BE; # (묯; 묯; 묯; 묯; 묯; ) HANGUL SYLLABLE MYOC +BB30;BB30;1106 116D 11BF;BB30;1106 116D 11BF; # (묰; 묰; 묰; 묰; 묰; ) HANGUL SYLLABLE MYOK +BB31;BB31;1106 116D 11C0;BB31;1106 116D 11C0; # (묱; 묱; 묱; 묱; 묱; ) HANGUL SYLLABLE MYOT +BB32;BB32;1106 116D 11C1;BB32;1106 116D 11C1; # (묲; 묲; 묲; 묲; 묲; ) HANGUL SYLLABLE MYOP +BB33;BB33;1106 116D 11C2;BB33;1106 116D 11C2; # (묳; 묳; 묳; 묳; 묳; ) HANGUL SYLLABLE MYOH +BB34;BB34;1106 116E;BB34;1106 116E; # (무; 무; 무; 무; 무; ) HANGUL SYLLABLE MU +BB35;BB35;1106 116E 11A8;BB35;1106 116E 11A8; # (묵; 묵; 묵; 묵; 묵; ) HANGUL SYLLABLE MUG +BB36;BB36;1106 116E 11A9;BB36;1106 116E 11A9; # (묶; 묶; 묶; 묶; 묶; ) HANGUL SYLLABLE MUGG +BB37;BB37;1106 116E 11AA;BB37;1106 116E 11AA; # (묷; 묷; 묷; 묷; 묷; ) HANGUL SYLLABLE MUGS +BB38;BB38;1106 116E 11AB;BB38;1106 116E 11AB; # (문; 문; 문; 문; 문; ) HANGUL SYLLABLE MUN +BB39;BB39;1106 116E 11AC;BB39;1106 116E 11AC; # (묹; 묹; 묹; 묹; 묹; ) HANGUL SYLLABLE MUNJ +BB3A;BB3A;1106 116E 11AD;BB3A;1106 116E 11AD; # (묺; 묺; 묺; 묺; 묺; ) HANGUL SYLLABLE MUNH +BB3B;BB3B;1106 116E 11AE;BB3B;1106 116E 11AE; # (묻; 묻; 묻; 묻; 묻; ) HANGUL SYLLABLE MUD +BB3C;BB3C;1106 116E 11AF;BB3C;1106 116E 11AF; # (물; 물; 물; 물; 물; ) HANGUL SYLLABLE MUL +BB3D;BB3D;1106 116E 11B0;BB3D;1106 116E 11B0; # (묽; 묽; 묽; 묽; 묽; ) HANGUL SYLLABLE MULG +BB3E;BB3E;1106 116E 11B1;BB3E;1106 116E 11B1; # (묾; 묾; 묾; 묾; 묾; ) HANGUL SYLLABLE MULM +BB3F;BB3F;1106 116E 11B2;BB3F;1106 116E 11B2; # (묿; 묿; 묿; 묿; 묿; ) HANGUL SYLLABLE MULB +BB40;BB40;1106 116E 11B3;BB40;1106 116E 11B3; # (뭀; 뭀; 뭀; 뭀; 뭀; ) HANGUL SYLLABLE MULS +BB41;BB41;1106 116E 11B4;BB41;1106 116E 11B4; # (뭁; 뭁; 뭁; 뭁; 뭁; ) HANGUL SYLLABLE MULT +BB42;BB42;1106 116E 11B5;BB42;1106 116E 11B5; # (뭂; 뭂; 뭂; 뭂; 뭂; ) HANGUL SYLLABLE MULP +BB43;BB43;1106 116E 11B6;BB43;1106 116E 11B6; # (뭃; 뭃; 뭃; 뭃; 뭃; ) HANGUL SYLLABLE MULH +BB44;BB44;1106 116E 11B7;BB44;1106 116E 11B7; # (뭄; 뭄; 뭄; 뭄; 뭄; ) HANGUL SYLLABLE MUM +BB45;BB45;1106 116E 11B8;BB45;1106 116E 11B8; # (뭅; 뭅; 뭅; 뭅; 뭅; ) HANGUL SYLLABLE MUB +BB46;BB46;1106 116E 11B9;BB46;1106 116E 11B9; # (뭆; 뭆; 뭆; 뭆; 뭆; ) HANGUL SYLLABLE MUBS +BB47;BB47;1106 116E 11BA;BB47;1106 116E 11BA; # (뭇; 뭇; 뭇; 뭇; 뭇; ) HANGUL SYLLABLE MUS +BB48;BB48;1106 116E 11BB;BB48;1106 116E 11BB; # (뭈; 뭈; 뭈; 뭈; 뭈; ) HANGUL SYLLABLE MUSS +BB49;BB49;1106 116E 11BC;BB49;1106 116E 11BC; # (뭉; 뭉; 뭉; 뭉; 뭉; ) HANGUL SYLLABLE MUNG +BB4A;BB4A;1106 116E 11BD;BB4A;1106 116E 11BD; # (뭊; 뭊; 뭊; 뭊; 뭊; ) HANGUL SYLLABLE MUJ +BB4B;BB4B;1106 116E 11BE;BB4B;1106 116E 11BE; # (뭋; 뭋; 뭋; 뭋; 뭋; ) HANGUL SYLLABLE MUC +BB4C;BB4C;1106 116E 11BF;BB4C;1106 116E 11BF; # (뭌; 뭌; 뭌; 뭌; 뭌; ) HANGUL SYLLABLE MUK +BB4D;BB4D;1106 116E 11C0;BB4D;1106 116E 11C0; # (뭍; 뭍; 뭍; 뭍; 뭍; ) HANGUL SYLLABLE MUT +BB4E;BB4E;1106 116E 11C1;BB4E;1106 116E 11C1; # (뭎; 뭎; 뭎; 뭎; 뭎; ) HANGUL SYLLABLE MUP +BB4F;BB4F;1106 116E 11C2;BB4F;1106 116E 11C2; # (뭏; 뭏; 뭏; 뭏; 뭏; ) HANGUL SYLLABLE MUH +BB50;BB50;1106 116F;BB50;1106 116F; # (뭐; 뭐; 뭐; 뭐; 뭐; ) HANGUL SYLLABLE MWEO +BB51;BB51;1106 116F 11A8;BB51;1106 116F 11A8; # (뭑; 뭑; 뭑; 뭑; 뭑; ) HANGUL SYLLABLE MWEOG +BB52;BB52;1106 116F 11A9;BB52;1106 116F 11A9; # (뭒; 뭒; 뭒; 뭒; 뭒; ) HANGUL SYLLABLE MWEOGG +BB53;BB53;1106 116F 11AA;BB53;1106 116F 11AA; # (뭓; 뭓; 뭓; 뭓; 뭓; ) HANGUL SYLLABLE MWEOGS +BB54;BB54;1106 116F 11AB;BB54;1106 116F 11AB; # (뭔; 뭔; 뭔; 뭔; 뭔; ) HANGUL SYLLABLE MWEON +BB55;BB55;1106 116F 11AC;BB55;1106 116F 11AC; # (뭕; 뭕; 뭕; 뭕; 뭕; ) HANGUL SYLLABLE MWEONJ +BB56;BB56;1106 116F 11AD;BB56;1106 116F 11AD; # (뭖; 뭖; 뭖; 뭖; 뭖; ) HANGUL SYLLABLE MWEONH +BB57;BB57;1106 116F 11AE;BB57;1106 116F 11AE; # (뭗; 뭗; 뭗; 뭗; 뭗; ) HANGUL SYLLABLE MWEOD +BB58;BB58;1106 116F 11AF;BB58;1106 116F 11AF; # (뭘; 뭘; 뭘; 뭘; 뭘; ) HANGUL SYLLABLE MWEOL +BB59;BB59;1106 116F 11B0;BB59;1106 116F 11B0; # (뭙; 뭙; 뭙; 뭙; 뭙; ) HANGUL SYLLABLE MWEOLG +BB5A;BB5A;1106 116F 11B1;BB5A;1106 116F 11B1; # (뭚; 뭚; 뭚; 뭚; 뭚; ) HANGUL SYLLABLE MWEOLM +BB5B;BB5B;1106 116F 11B2;BB5B;1106 116F 11B2; # (뭛; 뭛; 뭛; 뭛; 뭛; ) HANGUL SYLLABLE MWEOLB +BB5C;BB5C;1106 116F 11B3;BB5C;1106 116F 11B3; # (뭜; 뭜; 뭜; 뭜; 뭜; ) HANGUL SYLLABLE MWEOLS +BB5D;BB5D;1106 116F 11B4;BB5D;1106 116F 11B4; # (뭝; 뭝; 뭝; 뭝; 뭝; ) HANGUL SYLLABLE MWEOLT +BB5E;BB5E;1106 116F 11B5;BB5E;1106 116F 11B5; # (뭞; 뭞; 뭞; 뭞; 뭞; ) HANGUL SYLLABLE MWEOLP +BB5F;BB5F;1106 116F 11B6;BB5F;1106 116F 11B6; # (뭟; 뭟; 뭟; 뭟; 뭟; ) HANGUL SYLLABLE MWEOLH +BB60;BB60;1106 116F 11B7;BB60;1106 116F 11B7; # (뭠; 뭠; 뭠; 뭠; 뭠; ) HANGUL SYLLABLE MWEOM +BB61;BB61;1106 116F 11B8;BB61;1106 116F 11B8; # (뭡; 뭡; 뭡; 뭡; 뭡; ) HANGUL SYLLABLE MWEOB +BB62;BB62;1106 116F 11B9;BB62;1106 116F 11B9; # (뭢; 뭢; 뭢; 뭢; 뭢; ) HANGUL SYLLABLE MWEOBS +BB63;BB63;1106 116F 11BA;BB63;1106 116F 11BA; # (뭣; 뭣; 뭣; 뭣; 뭣; ) HANGUL SYLLABLE MWEOS +BB64;BB64;1106 116F 11BB;BB64;1106 116F 11BB; # (뭤; 뭤; 뭤; 뭤; 뭤; ) HANGUL SYLLABLE MWEOSS +BB65;BB65;1106 116F 11BC;BB65;1106 116F 11BC; # (뭥; 뭥; 뭥; 뭥; 뭥; ) HANGUL SYLLABLE MWEONG +BB66;BB66;1106 116F 11BD;BB66;1106 116F 11BD; # (뭦; 뭦; 뭦; 뭦; 뭦; ) HANGUL SYLLABLE MWEOJ +BB67;BB67;1106 116F 11BE;BB67;1106 116F 11BE; # (뭧; 뭧; 뭧; 뭧; 뭧; ) HANGUL SYLLABLE MWEOC +BB68;BB68;1106 116F 11BF;BB68;1106 116F 11BF; # (뭨; 뭨; 뭨; 뭨; 뭨; ) HANGUL SYLLABLE MWEOK +BB69;BB69;1106 116F 11C0;BB69;1106 116F 11C0; # (뭩; 뭩; 뭩; 뭩; 뭩; ) HANGUL SYLLABLE MWEOT +BB6A;BB6A;1106 116F 11C1;BB6A;1106 116F 11C1; # (뭪; 뭪; 뭪; 뭪; 뭪; ) HANGUL SYLLABLE MWEOP +BB6B;BB6B;1106 116F 11C2;BB6B;1106 116F 11C2; # (뭫; 뭫; 뭫; 뭫; 뭫; ) HANGUL SYLLABLE MWEOH +BB6C;BB6C;1106 1170;BB6C;1106 1170; # (뭬; 뭬; 뭬; 뭬; 뭬; ) HANGUL SYLLABLE MWE +BB6D;BB6D;1106 1170 11A8;BB6D;1106 1170 11A8; # (뭭; 뭭; 뭭; 뭭; 뭭; ) HANGUL SYLLABLE MWEG +BB6E;BB6E;1106 1170 11A9;BB6E;1106 1170 11A9; # (뭮; 뭮; 뭮; 뭮; 뭮; ) HANGUL SYLLABLE MWEGG +BB6F;BB6F;1106 1170 11AA;BB6F;1106 1170 11AA; # (뭯; 뭯; 뭯; 뭯; 뭯; ) HANGUL SYLLABLE MWEGS +BB70;BB70;1106 1170 11AB;BB70;1106 1170 11AB; # (뭰; 뭰; 뭰; 뭰; 뭰; ) HANGUL SYLLABLE MWEN +BB71;BB71;1106 1170 11AC;BB71;1106 1170 11AC; # (뭱; 뭱; 뭱; 뭱; 뭱; ) HANGUL SYLLABLE MWENJ +BB72;BB72;1106 1170 11AD;BB72;1106 1170 11AD; # (뭲; 뭲; 뭲; 뭲; 뭲; ) HANGUL SYLLABLE MWENH +BB73;BB73;1106 1170 11AE;BB73;1106 1170 11AE; # (뭳; 뭳; 뭳; 뭳; 뭳; ) HANGUL SYLLABLE MWED +BB74;BB74;1106 1170 11AF;BB74;1106 1170 11AF; # (뭴; 뭴; 뭴; 뭴; 뭴; ) HANGUL SYLLABLE MWEL +BB75;BB75;1106 1170 11B0;BB75;1106 1170 11B0; # (뭵; 뭵; 뭵; 뭵; 뭵; ) HANGUL SYLLABLE MWELG +BB76;BB76;1106 1170 11B1;BB76;1106 1170 11B1; # (뭶; 뭶; 뭶; 뭶; 뭶; ) HANGUL SYLLABLE MWELM +BB77;BB77;1106 1170 11B2;BB77;1106 1170 11B2; # (뭷; 뭷; 뭷; 뭷; 뭷; ) HANGUL SYLLABLE MWELB +BB78;BB78;1106 1170 11B3;BB78;1106 1170 11B3; # (뭸; 뭸; 뭸; 뭸; 뭸; ) HANGUL SYLLABLE MWELS +BB79;BB79;1106 1170 11B4;BB79;1106 1170 11B4; # (뭹; 뭹; 뭹; 뭹; 뭹; ) HANGUL SYLLABLE MWELT +BB7A;BB7A;1106 1170 11B5;BB7A;1106 1170 11B5; # (뭺; 뭺; 뭺; 뭺; 뭺; ) HANGUL SYLLABLE MWELP +BB7B;BB7B;1106 1170 11B6;BB7B;1106 1170 11B6; # (뭻; 뭻; 뭻; 뭻; 뭻; ) HANGUL SYLLABLE MWELH +BB7C;BB7C;1106 1170 11B7;BB7C;1106 1170 11B7; # (뭼; 뭼; 뭼; 뭼; 뭼; ) HANGUL SYLLABLE MWEM +BB7D;BB7D;1106 1170 11B8;BB7D;1106 1170 11B8; # (뭽; 뭽; 뭽; 뭽; 뭽; ) HANGUL SYLLABLE MWEB +BB7E;BB7E;1106 1170 11B9;BB7E;1106 1170 11B9; # (뭾; 뭾; 뭾; 뭾; 뭾; ) HANGUL SYLLABLE MWEBS +BB7F;BB7F;1106 1170 11BA;BB7F;1106 1170 11BA; # (뭿; 뭿; 뭿; 뭿; 뭿; ) HANGUL SYLLABLE MWES +BB80;BB80;1106 1170 11BB;BB80;1106 1170 11BB; # (뮀; 뮀; 뮀; 뮀; 뮀; ) HANGUL SYLLABLE MWESS +BB81;BB81;1106 1170 11BC;BB81;1106 1170 11BC; # (뮁; 뮁; 뮁; 뮁; 뮁; ) HANGUL SYLLABLE MWENG +BB82;BB82;1106 1170 11BD;BB82;1106 1170 11BD; # (뮂; 뮂; 뮂; 뮂; 뮂; ) HANGUL SYLLABLE MWEJ +BB83;BB83;1106 1170 11BE;BB83;1106 1170 11BE; # (뮃; 뮃; 뮃; 뮃; 뮃; ) HANGUL SYLLABLE MWEC +BB84;BB84;1106 1170 11BF;BB84;1106 1170 11BF; # (뮄; 뮄; 뮄; 뮄; 뮄; ) HANGUL SYLLABLE MWEK +BB85;BB85;1106 1170 11C0;BB85;1106 1170 11C0; # (뮅; 뮅; 뮅; 뮅; 뮅; ) HANGUL SYLLABLE MWET +BB86;BB86;1106 1170 11C1;BB86;1106 1170 11C1; # (뮆; 뮆; 뮆; 뮆; 뮆; ) HANGUL SYLLABLE MWEP +BB87;BB87;1106 1170 11C2;BB87;1106 1170 11C2; # (뮇; 뮇; 뮇; 뮇; 뮇; ) HANGUL SYLLABLE MWEH +BB88;BB88;1106 1171;BB88;1106 1171; # (뮈; 뮈; 뮈; 뮈; 뮈; ) HANGUL SYLLABLE MWI +BB89;BB89;1106 1171 11A8;BB89;1106 1171 11A8; # (뮉; 뮉; 뮉; 뮉; 뮉; ) HANGUL SYLLABLE MWIG +BB8A;BB8A;1106 1171 11A9;BB8A;1106 1171 11A9; # (뮊; 뮊; 뮊; 뮊; 뮊; ) HANGUL SYLLABLE MWIGG +BB8B;BB8B;1106 1171 11AA;BB8B;1106 1171 11AA; # (뮋; 뮋; 뮋; 뮋; 뮋; ) HANGUL SYLLABLE MWIGS +BB8C;BB8C;1106 1171 11AB;BB8C;1106 1171 11AB; # (뮌; 뮌; 뮌; 뮌; 뮌; ) HANGUL SYLLABLE MWIN +BB8D;BB8D;1106 1171 11AC;BB8D;1106 1171 11AC; # (뮍; 뮍; 뮍; 뮍; 뮍; ) HANGUL SYLLABLE MWINJ +BB8E;BB8E;1106 1171 11AD;BB8E;1106 1171 11AD; # (뮎; 뮎; 뮎; 뮎; 뮎; ) HANGUL SYLLABLE MWINH +BB8F;BB8F;1106 1171 11AE;BB8F;1106 1171 11AE; # (뮏; 뮏; 뮏; 뮏; 뮏; ) HANGUL SYLLABLE MWID +BB90;BB90;1106 1171 11AF;BB90;1106 1171 11AF; # (뮐; 뮐; 뮐; 뮐; 뮐; ) HANGUL SYLLABLE MWIL +BB91;BB91;1106 1171 11B0;BB91;1106 1171 11B0; # (뮑; 뮑; 뮑; 뮑; 뮑; ) HANGUL SYLLABLE MWILG +BB92;BB92;1106 1171 11B1;BB92;1106 1171 11B1; # (뮒; 뮒; 뮒; 뮒; 뮒; ) HANGUL SYLLABLE MWILM +BB93;BB93;1106 1171 11B2;BB93;1106 1171 11B2; # (뮓; 뮓; 뮓; 뮓; 뮓; ) HANGUL SYLLABLE MWILB +BB94;BB94;1106 1171 11B3;BB94;1106 1171 11B3; # (뮔; 뮔; 뮔; 뮔; 뮔; ) HANGUL SYLLABLE MWILS +BB95;BB95;1106 1171 11B4;BB95;1106 1171 11B4; # (뮕; 뮕; 뮕; 뮕; 뮕; ) HANGUL SYLLABLE MWILT +BB96;BB96;1106 1171 11B5;BB96;1106 1171 11B5; # (뮖; 뮖; 뮖; 뮖; 뮖; ) HANGUL SYLLABLE MWILP +BB97;BB97;1106 1171 11B6;BB97;1106 1171 11B6; # (뮗; 뮗; 뮗; 뮗; 뮗; ) HANGUL SYLLABLE MWILH +BB98;BB98;1106 1171 11B7;BB98;1106 1171 11B7; # (뮘; 뮘; 뮘; 뮘; 뮘; ) HANGUL SYLLABLE MWIM +BB99;BB99;1106 1171 11B8;BB99;1106 1171 11B8; # (뮙; 뮙; 뮙; 뮙; 뮙; ) HANGUL SYLLABLE MWIB +BB9A;BB9A;1106 1171 11B9;BB9A;1106 1171 11B9; # (뮚; 뮚; 뮚; 뮚; 뮚; ) HANGUL SYLLABLE MWIBS +BB9B;BB9B;1106 1171 11BA;BB9B;1106 1171 11BA; # (뮛; 뮛; 뮛; 뮛; 뮛; ) HANGUL SYLLABLE MWIS +BB9C;BB9C;1106 1171 11BB;BB9C;1106 1171 11BB; # (뮜; 뮜; 뮜; 뮜; 뮜; ) HANGUL SYLLABLE MWISS +BB9D;BB9D;1106 1171 11BC;BB9D;1106 1171 11BC; # (뮝; 뮝; 뮝; 뮝; 뮝; ) HANGUL SYLLABLE MWING +BB9E;BB9E;1106 1171 11BD;BB9E;1106 1171 11BD; # (뮞; 뮞; 뮞; 뮞; 뮞; ) HANGUL SYLLABLE MWIJ +BB9F;BB9F;1106 1171 11BE;BB9F;1106 1171 11BE; # (뮟; 뮟; 뮟; 뮟; 뮟; ) HANGUL SYLLABLE MWIC +BBA0;BBA0;1106 1171 11BF;BBA0;1106 1171 11BF; # (뮠; 뮠; 뮠; 뮠; 뮠; ) HANGUL SYLLABLE MWIK +BBA1;BBA1;1106 1171 11C0;BBA1;1106 1171 11C0; # (뮡; 뮡; 뮡; 뮡; 뮡; ) HANGUL SYLLABLE MWIT +BBA2;BBA2;1106 1171 11C1;BBA2;1106 1171 11C1; # (뮢; 뮢; 뮢; 뮢; 뮢; ) HANGUL SYLLABLE MWIP +BBA3;BBA3;1106 1171 11C2;BBA3;1106 1171 11C2; # (뮣; 뮣; 뮣; 뮣; 뮣; ) HANGUL SYLLABLE MWIH +BBA4;BBA4;1106 1172;BBA4;1106 1172; # (뮤; 뮤; 뮤; 뮤; 뮤; ) HANGUL SYLLABLE MYU +BBA5;BBA5;1106 1172 11A8;BBA5;1106 1172 11A8; # (뮥; 뮥; 뮥; 뮥; 뮥; ) HANGUL SYLLABLE MYUG +BBA6;BBA6;1106 1172 11A9;BBA6;1106 1172 11A9; # (뮦; 뮦; 뮦; 뮦; 뮦; ) HANGUL SYLLABLE MYUGG +BBA7;BBA7;1106 1172 11AA;BBA7;1106 1172 11AA; # (뮧; 뮧; 뮧; 뮧; 뮧; ) HANGUL SYLLABLE MYUGS +BBA8;BBA8;1106 1172 11AB;BBA8;1106 1172 11AB; # (뮨; 뮨; 뮨; 뮨; 뮨; ) HANGUL SYLLABLE MYUN +BBA9;BBA9;1106 1172 11AC;BBA9;1106 1172 11AC; # (뮩; 뮩; 뮩; 뮩; 뮩; ) HANGUL SYLLABLE MYUNJ +BBAA;BBAA;1106 1172 11AD;BBAA;1106 1172 11AD; # (뮪; 뮪; 뮪; 뮪; 뮪; ) HANGUL SYLLABLE MYUNH +BBAB;BBAB;1106 1172 11AE;BBAB;1106 1172 11AE; # (뮫; 뮫; 뮫; 뮫; 뮫; ) HANGUL SYLLABLE MYUD +BBAC;BBAC;1106 1172 11AF;BBAC;1106 1172 11AF; # (뮬; 뮬; 뮬; 뮬; 뮬; ) HANGUL SYLLABLE MYUL +BBAD;BBAD;1106 1172 11B0;BBAD;1106 1172 11B0; # (뮭; 뮭; 뮭; 뮭; 뮭; ) HANGUL SYLLABLE MYULG +BBAE;BBAE;1106 1172 11B1;BBAE;1106 1172 11B1; # (뮮; 뮮; 뮮; 뮮; 뮮; ) HANGUL SYLLABLE MYULM +BBAF;BBAF;1106 1172 11B2;BBAF;1106 1172 11B2; # (뮯; 뮯; 뮯; 뮯; 뮯; ) HANGUL SYLLABLE MYULB +BBB0;BBB0;1106 1172 11B3;BBB0;1106 1172 11B3; # (뮰; 뮰; 뮰; 뮰; 뮰; ) HANGUL SYLLABLE MYULS +BBB1;BBB1;1106 1172 11B4;BBB1;1106 1172 11B4; # (뮱; 뮱; 뮱; 뮱; 뮱; ) HANGUL SYLLABLE MYULT +BBB2;BBB2;1106 1172 11B5;BBB2;1106 1172 11B5; # (뮲; 뮲; 뮲; 뮲; 뮲; ) HANGUL SYLLABLE MYULP +BBB3;BBB3;1106 1172 11B6;BBB3;1106 1172 11B6; # (뮳; 뮳; 뮳; 뮳; 뮳; ) HANGUL SYLLABLE MYULH +BBB4;BBB4;1106 1172 11B7;BBB4;1106 1172 11B7; # (뮴; 뮴; 뮴; 뮴; 뮴; ) HANGUL SYLLABLE MYUM +BBB5;BBB5;1106 1172 11B8;BBB5;1106 1172 11B8; # (뮵; 뮵; 뮵; 뮵; 뮵; ) HANGUL SYLLABLE MYUB +BBB6;BBB6;1106 1172 11B9;BBB6;1106 1172 11B9; # (뮶; 뮶; 뮶; 뮶; 뮶; ) HANGUL SYLLABLE MYUBS +BBB7;BBB7;1106 1172 11BA;BBB7;1106 1172 11BA; # (뮷; 뮷; 뮷; 뮷; 뮷; ) HANGUL SYLLABLE MYUS +BBB8;BBB8;1106 1172 11BB;BBB8;1106 1172 11BB; # (뮸; 뮸; 뮸; 뮸; 뮸; ) HANGUL SYLLABLE MYUSS +BBB9;BBB9;1106 1172 11BC;BBB9;1106 1172 11BC; # (뮹; 뮹; 뮹; 뮹; 뮹; ) HANGUL SYLLABLE MYUNG +BBBA;BBBA;1106 1172 11BD;BBBA;1106 1172 11BD; # (뮺; 뮺; 뮺; 뮺; 뮺; ) HANGUL SYLLABLE MYUJ +BBBB;BBBB;1106 1172 11BE;BBBB;1106 1172 11BE; # (뮻; 뮻; 뮻; 뮻; 뮻; ) HANGUL SYLLABLE MYUC +BBBC;BBBC;1106 1172 11BF;BBBC;1106 1172 11BF; # (뮼; 뮼; 뮼; 뮼; 뮼; ) HANGUL SYLLABLE MYUK +BBBD;BBBD;1106 1172 11C0;BBBD;1106 1172 11C0; # (뮽; 뮽; 뮽; 뮽; 뮽; ) HANGUL SYLLABLE MYUT +BBBE;BBBE;1106 1172 11C1;BBBE;1106 1172 11C1; # (뮾; 뮾; 뮾; 뮾; 뮾; ) HANGUL SYLLABLE MYUP +BBBF;BBBF;1106 1172 11C2;BBBF;1106 1172 11C2; # (뮿; 뮿; 뮿; 뮿; 뮿; ) HANGUL SYLLABLE MYUH +BBC0;BBC0;1106 1173;BBC0;1106 1173; # (므; 므; 므; 므; 므; ) HANGUL SYLLABLE MEU +BBC1;BBC1;1106 1173 11A8;BBC1;1106 1173 11A8; # (믁; 믁; 믁; 믁; 믁; ) HANGUL SYLLABLE MEUG +BBC2;BBC2;1106 1173 11A9;BBC2;1106 1173 11A9; # (믂; 믂; 믂; 믂; 믂; ) HANGUL SYLLABLE MEUGG +BBC3;BBC3;1106 1173 11AA;BBC3;1106 1173 11AA; # (믃; 믃; 믃; 믃; 믃; ) HANGUL SYLLABLE MEUGS +BBC4;BBC4;1106 1173 11AB;BBC4;1106 1173 11AB; # (믄; 믄; 믄; 믄; 믄; ) HANGUL SYLLABLE MEUN +BBC5;BBC5;1106 1173 11AC;BBC5;1106 1173 11AC; # (믅; 믅; 믅; 믅; 믅; ) HANGUL SYLLABLE MEUNJ +BBC6;BBC6;1106 1173 11AD;BBC6;1106 1173 11AD; # (믆; 믆; 믆; 믆; 믆; ) HANGUL SYLLABLE MEUNH +BBC7;BBC7;1106 1173 11AE;BBC7;1106 1173 11AE; # (믇; 믇; 믇; 믇; 믇; ) HANGUL SYLLABLE MEUD +BBC8;BBC8;1106 1173 11AF;BBC8;1106 1173 11AF; # (믈; 믈; 믈; 믈; 믈; ) HANGUL SYLLABLE MEUL +BBC9;BBC9;1106 1173 11B0;BBC9;1106 1173 11B0; # (믉; 믉; 믉; 믉; 믉; ) HANGUL SYLLABLE MEULG +BBCA;BBCA;1106 1173 11B1;BBCA;1106 1173 11B1; # (믊; 믊; 믊; 믊; 믊; ) HANGUL SYLLABLE MEULM +BBCB;BBCB;1106 1173 11B2;BBCB;1106 1173 11B2; # (믋; 믋; 믋; 믋; 믋; ) HANGUL SYLLABLE MEULB +BBCC;BBCC;1106 1173 11B3;BBCC;1106 1173 11B3; # (믌; 믌; 믌; 믌; 믌; ) HANGUL SYLLABLE MEULS +BBCD;BBCD;1106 1173 11B4;BBCD;1106 1173 11B4; # (믍; 믍; 믍; 믍; 믍; ) HANGUL SYLLABLE MEULT +BBCE;BBCE;1106 1173 11B5;BBCE;1106 1173 11B5; # (믎; 믎; 믎; 믎; 믎; ) HANGUL SYLLABLE MEULP +BBCF;BBCF;1106 1173 11B6;BBCF;1106 1173 11B6; # (믏; 믏; 믏; 믏; 믏; ) HANGUL SYLLABLE MEULH +BBD0;BBD0;1106 1173 11B7;BBD0;1106 1173 11B7; # (믐; 믐; 믐; 믐; 믐; ) HANGUL SYLLABLE MEUM +BBD1;BBD1;1106 1173 11B8;BBD1;1106 1173 11B8; # (믑; 믑; 믑; 믑; 믑; ) HANGUL SYLLABLE MEUB +BBD2;BBD2;1106 1173 11B9;BBD2;1106 1173 11B9; # (믒; 믒; 믒; 믒; 믒; ) HANGUL SYLLABLE MEUBS +BBD3;BBD3;1106 1173 11BA;BBD3;1106 1173 11BA; # (믓; 믓; 믓; 믓; 믓; ) HANGUL SYLLABLE MEUS +BBD4;BBD4;1106 1173 11BB;BBD4;1106 1173 11BB; # (믔; 믔; 믔; 믔; 믔; ) HANGUL SYLLABLE MEUSS +BBD5;BBD5;1106 1173 11BC;BBD5;1106 1173 11BC; # (믕; 믕; 믕; 믕; 믕; ) HANGUL SYLLABLE MEUNG +BBD6;BBD6;1106 1173 11BD;BBD6;1106 1173 11BD; # (믖; 믖; 믖; 믖; 믖; ) HANGUL SYLLABLE MEUJ +BBD7;BBD7;1106 1173 11BE;BBD7;1106 1173 11BE; # (믗; 믗; 믗; 믗; 믗; ) HANGUL SYLLABLE MEUC +BBD8;BBD8;1106 1173 11BF;BBD8;1106 1173 11BF; # (믘; 믘; 믘; 믘; 믘; ) HANGUL SYLLABLE MEUK +BBD9;BBD9;1106 1173 11C0;BBD9;1106 1173 11C0; # (믙; 믙; 믙; 믙; 믙; ) HANGUL SYLLABLE MEUT +BBDA;BBDA;1106 1173 11C1;BBDA;1106 1173 11C1; # (믚; 믚; 믚; 믚; 믚; ) HANGUL SYLLABLE MEUP +BBDB;BBDB;1106 1173 11C2;BBDB;1106 1173 11C2; # (믛; 믛; 믛; 믛; 믛; ) HANGUL SYLLABLE MEUH +BBDC;BBDC;1106 1174;BBDC;1106 1174; # (믜; 믜; 믜; 믜; 믜; ) HANGUL SYLLABLE MYI +BBDD;BBDD;1106 1174 11A8;BBDD;1106 1174 11A8; # (믝; 믝; 믝; 믝; 믝; ) HANGUL SYLLABLE MYIG +BBDE;BBDE;1106 1174 11A9;BBDE;1106 1174 11A9; # (믞; 믞; 믞; 믞; 믞; ) HANGUL SYLLABLE MYIGG +BBDF;BBDF;1106 1174 11AA;BBDF;1106 1174 11AA; # (믟; 믟; 믟; 믟; 믟; ) HANGUL SYLLABLE MYIGS +BBE0;BBE0;1106 1174 11AB;BBE0;1106 1174 11AB; # (믠; 믠; 믠; 믠; 믠; ) HANGUL SYLLABLE MYIN +BBE1;BBE1;1106 1174 11AC;BBE1;1106 1174 11AC; # (믡; 믡; 믡; 믡; 믡; ) HANGUL SYLLABLE MYINJ +BBE2;BBE2;1106 1174 11AD;BBE2;1106 1174 11AD; # (믢; 믢; 믢; 믢; 믢; ) HANGUL SYLLABLE MYINH +BBE3;BBE3;1106 1174 11AE;BBE3;1106 1174 11AE; # (믣; 믣; 믣; 믣; 믣; ) HANGUL SYLLABLE MYID +BBE4;BBE4;1106 1174 11AF;BBE4;1106 1174 11AF; # (믤; 믤; 믤; 믤; 믤; ) HANGUL SYLLABLE MYIL +BBE5;BBE5;1106 1174 11B0;BBE5;1106 1174 11B0; # (믥; 믥; 믥; 믥; 믥; ) HANGUL SYLLABLE MYILG +BBE6;BBE6;1106 1174 11B1;BBE6;1106 1174 11B1; # (믦; 믦; 믦; 믦; 믦; ) HANGUL SYLLABLE MYILM +BBE7;BBE7;1106 1174 11B2;BBE7;1106 1174 11B2; # (믧; 믧; 믧; 믧; 믧; ) HANGUL SYLLABLE MYILB +BBE8;BBE8;1106 1174 11B3;BBE8;1106 1174 11B3; # (믨; 믨; 믨; 믨; 믨; ) HANGUL SYLLABLE MYILS +BBE9;BBE9;1106 1174 11B4;BBE9;1106 1174 11B4; # (믩; 믩; 믩; 믩; 믩; ) HANGUL SYLLABLE MYILT +BBEA;BBEA;1106 1174 11B5;BBEA;1106 1174 11B5; # (믪; 믪; 믪; 믪; 믪; ) HANGUL SYLLABLE MYILP +BBEB;BBEB;1106 1174 11B6;BBEB;1106 1174 11B6; # (믫; 믫; 믫; 믫; 믫; ) HANGUL SYLLABLE MYILH +BBEC;BBEC;1106 1174 11B7;BBEC;1106 1174 11B7; # (믬; 믬; 믬; 믬; 믬; ) HANGUL SYLLABLE MYIM +BBED;BBED;1106 1174 11B8;BBED;1106 1174 11B8; # (믭; 믭; 믭; 믭; 믭; ) HANGUL SYLLABLE MYIB +BBEE;BBEE;1106 1174 11B9;BBEE;1106 1174 11B9; # (믮; 믮; 믮; 믮; 믮; ) HANGUL SYLLABLE MYIBS +BBEF;BBEF;1106 1174 11BA;BBEF;1106 1174 11BA; # (믯; 믯; 믯; 믯; 믯; ) HANGUL SYLLABLE MYIS +BBF0;BBF0;1106 1174 11BB;BBF0;1106 1174 11BB; # (믰; 믰; 믰; 믰; 믰; ) HANGUL SYLLABLE MYISS +BBF1;BBF1;1106 1174 11BC;BBF1;1106 1174 11BC; # (믱; 믱; 믱; 믱; 믱; ) HANGUL SYLLABLE MYING +BBF2;BBF2;1106 1174 11BD;BBF2;1106 1174 11BD; # (믲; 믲; 믲; 믲; 믲; ) HANGUL SYLLABLE MYIJ +BBF3;BBF3;1106 1174 11BE;BBF3;1106 1174 11BE; # (믳; 믳; 믳; 믳; 믳; ) HANGUL SYLLABLE MYIC +BBF4;BBF4;1106 1174 11BF;BBF4;1106 1174 11BF; # (믴; 믴; 믴; 믴; 믴; ) HANGUL SYLLABLE MYIK +BBF5;BBF5;1106 1174 11C0;BBF5;1106 1174 11C0; # (믵; 믵; 믵; 믵; 믵; ) HANGUL SYLLABLE MYIT +BBF6;BBF6;1106 1174 11C1;BBF6;1106 1174 11C1; # (믶; 믶; 믶; 믶; 믶; ) HANGUL SYLLABLE MYIP +BBF7;BBF7;1106 1174 11C2;BBF7;1106 1174 11C2; # (믷; 믷; 믷; 믷; 믷; ) HANGUL SYLLABLE MYIH +BBF8;BBF8;1106 1175;BBF8;1106 1175; # (미; 미; 미; 미; 미; ) HANGUL SYLLABLE MI +BBF9;BBF9;1106 1175 11A8;BBF9;1106 1175 11A8; # (믹; 믹; 믹; 믹; 믹; ) HANGUL SYLLABLE MIG +BBFA;BBFA;1106 1175 11A9;BBFA;1106 1175 11A9; # (믺; 믺; 믺; 믺; 믺; ) HANGUL SYLLABLE MIGG +BBFB;BBFB;1106 1175 11AA;BBFB;1106 1175 11AA; # (믻; 믻; 믻; 믻; 믻; ) HANGUL SYLLABLE MIGS +BBFC;BBFC;1106 1175 11AB;BBFC;1106 1175 11AB; # (민; 민; 민; 민; 민; ) HANGUL SYLLABLE MIN +BBFD;BBFD;1106 1175 11AC;BBFD;1106 1175 11AC; # (믽; 믽; 믽; 믽; 믽; ) HANGUL SYLLABLE MINJ +BBFE;BBFE;1106 1175 11AD;BBFE;1106 1175 11AD; # (믾; 믾; 믾; 믾; 믾; ) HANGUL SYLLABLE MINH +BBFF;BBFF;1106 1175 11AE;BBFF;1106 1175 11AE; # (믿; 믿; 믿; 믿; 믿; ) HANGUL SYLLABLE MID +BC00;BC00;1106 1175 11AF;BC00;1106 1175 11AF; # (밀; 밀; 밀; 밀; 밀; ) HANGUL SYLLABLE MIL +BC01;BC01;1106 1175 11B0;BC01;1106 1175 11B0; # (밁; 밁; 밁; 밁; 밁; ) HANGUL SYLLABLE MILG +BC02;BC02;1106 1175 11B1;BC02;1106 1175 11B1; # (밂; 밂; 밂; 밂; 밂; ) HANGUL SYLLABLE MILM +BC03;BC03;1106 1175 11B2;BC03;1106 1175 11B2; # (밃; 밃; 밃; 밃; 밃; ) HANGUL SYLLABLE MILB +BC04;BC04;1106 1175 11B3;BC04;1106 1175 11B3; # (밄; 밄; 밄; 밄; 밄; ) HANGUL SYLLABLE MILS +BC05;BC05;1106 1175 11B4;BC05;1106 1175 11B4; # (밅; 밅; 밅; 밅; 밅; ) HANGUL SYLLABLE MILT +BC06;BC06;1106 1175 11B5;BC06;1106 1175 11B5; # (밆; 밆; 밆; 밆; 밆; ) HANGUL SYLLABLE MILP +BC07;BC07;1106 1175 11B6;BC07;1106 1175 11B6; # (밇; 밇; 밇; 밇; 밇; ) HANGUL SYLLABLE MILH +BC08;BC08;1106 1175 11B7;BC08;1106 1175 11B7; # (밈; 밈; 밈; 밈; 밈; ) HANGUL SYLLABLE MIM +BC09;BC09;1106 1175 11B8;BC09;1106 1175 11B8; # (밉; 밉; 밉; 밉; 밉; ) HANGUL SYLLABLE MIB +BC0A;BC0A;1106 1175 11B9;BC0A;1106 1175 11B9; # (밊; 밊; 밊; 밊; 밊; ) HANGUL SYLLABLE MIBS +BC0B;BC0B;1106 1175 11BA;BC0B;1106 1175 11BA; # (밋; 밋; 밋; 밋; 밋; ) HANGUL SYLLABLE MIS +BC0C;BC0C;1106 1175 11BB;BC0C;1106 1175 11BB; # (밌; 밌; 밌; 밌; 밌; ) HANGUL SYLLABLE MISS +BC0D;BC0D;1106 1175 11BC;BC0D;1106 1175 11BC; # (밍; 밍; 밍; 밍; 밍; ) HANGUL SYLLABLE MING +BC0E;BC0E;1106 1175 11BD;BC0E;1106 1175 11BD; # (밎; 밎; 밎; 밎; 밎; ) HANGUL SYLLABLE MIJ +BC0F;BC0F;1106 1175 11BE;BC0F;1106 1175 11BE; # (및; 및; 및; 및; 및; ) HANGUL SYLLABLE MIC +BC10;BC10;1106 1175 11BF;BC10;1106 1175 11BF; # (밐; 밐; 밐; 밐; 밐; ) HANGUL SYLLABLE MIK +BC11;BC11;1106 1175 11C0;BC11;1106 1175 11C0; # (밑; 밑; 밑; 밑; 밑; ) HANGUL SYLLABLE MIT +BC12;BC12;1106 1175 11C1;BC12;1106 1175 11C1; # (밒; 밒; 밒; 밒; 밒; ) HANGUL SYLLABLE MIP +BC13;BC13;1106 1175 11C2;BC13;1106 1175 11C2; # (밓; 밓; 밓; 밓; 밓; ) HANGUL SYLLABLE MIH +BC14;BC14;1107 1161;BC14;1107 1161; # (바; 바; 바; 바; 바; ) HANGUL SYLLABLE BA +BC15;BC15;1107 1161 11A8;BC15;1107 1161 11A8; # (박; 박; 박; 박; 박; ) HANGUL SYLLABLE BAG +BC16;BC16;1107 1161 11A9;BC16;1107 1161 11A9; # (밖; 밖; 밖; 밖; 밖; ) HANGUL SYLLABLE BAGG +BC17;BC17;1107 1161 11AA;BC17;1107 1161 11AA; # (밗; 밗; 밗; 밗; 밗; ) HANGUL SYLLABLE BAGS +BC18;BC18;1107 1161 11AB;BC18;1107 1161 11AB; # (반; 반; 반; 반; 반; ) HANGUL SYLLABLE BAN +BC19;BC19;1107 1161 11AC;BC19;1107 1161 11AC; # (밙; 밙; 밙; 밙; 밙; ) HANGUL SYLLABLE BANJ +BC1A;BC1A;1107 1161 11AD;BC1A;1107 1161 11AD; # (밚; 밚; 밚; 밚; 밚; ) HANGUL SYLLABLE BANH +BC1B;BC1B;1107 1161 11AE;BC1B;1107 1161 11AE; # (받; 받; 받; 받; 받; ) HANGUL SYLLABLE BAD +BC1C;BC1C;1107 1161 11AF;BC1C;1107 1161 11AF; # (발; 발; 발; 발; 발; ) HANGUL SYLLABLE BAL +BC1D;BC1D;1107 1161 11B0;BC1D;1107 1161 11B0; # (밝; 밝; 밝; 밝; 밝; ) HANGUL SYLLABLE BALG +BC1E;BC1E;1107 1161 11B1;BC1E;1107 1161 11B1; # (밞; 밞; 밞; 밞; 밞; ) HANGUL SYLLABLE BALM +BC1F;BC1F;1107 1161 11B2;BC1F;1107 1161 11B2; # (밟; 밟; 밟; 밟; 밟; ) HANGUL SYLLABLE BALB +BC20;BC20;1107 1161 11B3;BC20;1107 1161 11B3; # (밠; 밠; 밠; 밠; 밠; ) HANGUL SYLLABLE BALS +BC21;BC21;1107 1161 11B4;BC21;1107 1161 11B4; # (밡; 밡; 밡; 밡; 밡; ) HANGUL SYLLABLE BALT +BC22;BC22;1107 1161 11B5;BC22;1107 1161 11B5; # (밢; 밢; 밢; 밢; 밢; ) HANGUL SYLLABLE BALP +BC23;BC23;1107 1161 11B6;BC23;1107 1161 11B6; # (밣; 밣; 밣; 밣; 밣; ) HANGUL SYLLABLE BALH +BC24;BC24;1107 1161 11B7;BC24;1107 1161 11B7; # (밤; 밤; 밤; 밤; 밤; ) HANGUL SYLLABLE BAM +BC25;BC25;1107 1161 11B8;BC25;1107 1161 11B8; # (밥; 밥; 밥; 밥; 밥; ) HANGUL SYLLABLE BAB +BC26;BC26;1107 1161 11B9;BC26;1107 1161 11B9; # (밦; 밦; 밦; 밦; 밦; ) HANGUL SYLLABLE BABS +BC27;BC27;1107 1161 11BA;BC27;1107 1161 11BA; # (밧; 밧; 밧; 밧; 밧; ) HANGUL SYLLABLE BAS +BC28;BC28;1107 1161 11BB;BC28;1107 1161 11BB; # (밨; 밨; 밨; 밨; 밨; ) HANGUL SYLLABLE BASS +BC29;BC29;1107 1161 11BC;BC29;1107 1161 11BC; # (방; 방; 방; 방; 방; ) HANGUL SYLLABLE BANG +BC2A;BC2A;1107 1161 11BD;BC2A;1107 1161 11BD; # (밪; 밪; 밪; 밪; 밪; ) HANGUL SYLLABLE BAJ +BC2B;BC2B;1107 1161 11BE;BC2B;1107 1161 11BE; # (밫; 밫; 밫; 밫; 밫; ) HANGUL SYLLABLE BAC +BC2C;BC2C;1107 1161 11BF;BC2C;1107 1161 11BF; # (밬; 밬; 밬; 밬; 밬; ) HANGUL SYLLABLE BAK +BC2D;BC2D;1107 1161 11C0;BC2D;1107 1161 11C0; # (밭; 밭; 밭; 밭; 밭; ) HANGUL SYLLABLE BAT +BC2E;BC2E;1107 1161 11C1;BC2E;1107 1161 11C1; # (밮; 밮; 밮; 밮; 밮; ) HANGUL SYLLABLE BAP +BC2F;BC2F;1107 1161 11C2;BC2F;1107 1161 11C2; # (밯; 밯; 밯; 밯; 밯; ) HANGUL SYLLABLE BAH +BC30;BC30;1107 1162;BC30;1107 1162; # (배; 배; 배; 배; 배; ) HANGUL SYLLABLE BAE +BC31;BC31;1107 1162 11A8;BC31;1107 1162 11A8; # (백; 백; 백; 백; 백; ) HANGUL SYLLABLE BAEG +BC32;BC32;1107 1162 11A9;BC32;1107 1162 11A9; # (밲; 밲; 밲; 밲; 밲; ) HANGUL SYLLABLE BAEGG +BC33;BC33;1107 1162 11AA;BC33;1107 1162 11AA; # (밳; 밳; 밳; 밳; 밳; ) HANGUL SYLLABLE BAEGS +BC34;BC34;1107 1162 11AB;BC34;1107 1162 11AB; # (밴; 밴; 밴; 밴; 밴; ) HANGUL SYLLABLE BAEN +BC35;BC35;1107 1162 11AC;BC35;1107 1162 11AC; # (밵; 밵; 밵; 밵; 밵; ) HANGUL SYLLABLE BAENJ +BC36;BC36;1107 1162 11AD;BC36;1107 1162 11AD; # (밶; 밶; 밶; 밶; 밶; ) HANGUL SYLLABLE BAENH +BC37;BC37;1107 1162 11AE;BC37;1107 1162 11AE; # (밷; 밷; 밷; 밷; 밷; ) HANGUL SYLLABLE BAED +BC38;BC38;1107 1162 11AF;BC38;1107 1162 11AF; # (밸; 밸; 밸; 밸; 밸; ) HANGUL SYLLABLE BAEL +BC39;BC39;1107 1162 11B0;BC39;1107 1162 11B0; # (밹; 밹; 밹; 밹; 밹; ) HANGUL SYLLABLE BAELG +BC3A;BC3A;1107 1162 11B1;BC3A;1107 1162 11B1; # (밺; 밺; 밺; 밺; 밺; ) HANGUL SYLLABLE BAELM +BC3B;BC3B;1107 1162 11B2;BC3B;1107 1162 11B2; # (밻; 밻; 밻; 밻; 밻; ) HANGUL SYLLABLE BAELB +BC3C;BC3C;1107 1162 11B3;BC3C;1107 1162 11B3; # (밼; 밼; 밼; 밼; 밼; ) HANGUL SYLLABLE BAELS +BC3D;BC3D;1107 1162 11B4;BC3D;1107 1162 11B4; # (밽; 밽; 밽; 밽; 밽; ) HANGUL SYLLABLE BAELT +BC3E;BC3E;1107 1162 11B5;BC3E;1107 1162 11B5; # (밾; 밾; 밾; 밾; 밾; ) HANGUL SYLLABLE BAELP +BC3F;BC3F;1107 1162 11B6;BC3F;1107 1162 11B6; # (밿; 밿; 밿; 밿; 밿; ) HANGUL SYLLABLE BAELH +BC40;BC40;1107 1162 11B7;BC40;1107 1162 11B7; # (뱀; 뱀; 뱀; 뱀; 뱀; ) HANGUL SYLLABLE BAEM +BC41;BC41;1107 1162 11B8;BC41;1107 1162 11B8; # (뱁; 뱁; 뱁; 뱁; 뱁; ) HANGUL SYLLABLE BAEB +BC42;BC42;1107 1162 11B9;BC42;1107 1162 11B9; # (뱂; 뱂; 뱂; 뱂; 뱂; ) HANGUL SYLLABLE BAEBS +BC43;BC43;1107 1162 11BA;BC43;1107 1162 11BA; # (뱃; 뱃; 뱃; 뱃; 뱃; ) HANGUL SYLLABLE BAES +BC44;BC44;1107 1162 11BB;BC44;1107 1162 11BB; # (뱄; 뱄; 뱄; 뱄; 뱄; ) HANGUL SYLLABLE BAESS +BC45;BC45;1107 1162 11BC;BC45;1107 1162 11BC; # (뱅; 뱅; 뱅; 뱅; 뱅; ) HANGUL SYLLABLE BAENG +BC46;BC46;1107 1162 11BD;BC46;1107 1162 11BD; # (뱆; 뱆; 뱆; 뱆; 뱆; ) HANGUL SYLLABLE BAEJ +BC47;BC47;1107 1162 11BE;BC47;1107 1162 11BE; # (뱇; 뱇; 뱇; 뱇; 뱇; ) HANGUL SYLLABLE BAEC +BC48;BC48;1107 1162 11BF;BC48;1107 1162 11BF; # (뱈; 뱈; 뱈; 뱈; 뱈; ) HANGUL SYLLABLE BAEK +BC49;BC49;1107 1162 11C0;BC49;1107 1162 11C0; # (뱉; 뱉; 뱉; 뱉; 뱉; ) HANGUL SYLLABLE BAET +BC4A;BC4A;1107 1162 11C1;BC4A;1107 1162 11C1; # (뱊; 뱊; 뱊; 뱊; 뱊; ) HANGUL SYLLABLE BAEP +BC4B;BC4B;1107 1162 11C2;BC4B;1107 1162 11C2; # (뱋; 뱋; 뱋; 뱋; 뱋; ) HANGUL SYLLABLE BAEH +BC4C;BC4C;1107 1163;BC4C;1107 1163; # (뱌; 뱌; 뱌; 뱌; 뱌; ) HANGUL SYLLABLE BYA +BC4D;BC4D;1107 1163 11A8;BC4D;1107 1163 11A8; # (뱍; 뱍; 뱍; 뱍; 뱍; ) HANGUL SYLLABLE BYAG +BC4E;BC4E;1107 1163 11A9;BC4E;1107 1163 11A9; # (뱎; 뱎; 뱎; 뱎; 뱎; ) HANGUL SYLLABLE BYAGG +BC4F;BC4F;1107 1163 11AA;BC4F;1107 1163 11AA; # (뱏; 뱏; 뱏; 뱏; 뱏; ) HANGUL SYLLABLE BYAGS +BC50;BC50;1107 1163 11AB;BC50;1107 1163 11AB; # (뱐; 뱐; 뱐; 뱐; 뱐; ) HANGUL SYLLABLE BYAN +BC51;BC51;1107 1163 11AC;BC51;1107 1163 11AC; # (뱑; 뱑; 뱑; 뱑; 뱑; ) HANGUL SYLLABLE BYANJ +BC52;BC52;1107 1163 11AD;BC52;1107 1163 11AD; # (뱒; 뱒; 뱒; 뱒; 뱒; ) HANGUL SYLLABLE BYANH +BC53;BC53;1107 1163 11AE;BC53;1107 1163 11AE; # (뱓; 뱓; 뱓; 뱓; 뱓; ) HANGUL SYLLABLE BYAD +BC54;BC54;1107 1163 11AF;BC54;1107 1163 11AF; # (뱔; 뱔; 뱔; 뱔; 뱔; ) HANGUL SYLLABLE BYAL +BC55;BC55;1107 1163 11B0;BC55;1107 1163 11B0; # (뱕; 뱕; 뱕; 뱕; 뱕; ) HANGUL SYLLABLE BYALG +BC56;BC56;1107 1163 11B1;BC56;1107 1163 11B1; # (뱖; 뱖; 뱖; 뱖; 뱖; ) HANGUL SYLLABLE BYALM +BC57;BC57;1107 1163 11B2;BC57;1107 1163 11B2; # (뱗; 뱗; 뱗; 뱗; 뱗; ) HANGUL SYLLABLE BYALB +BC58;BC58;1107 1163 11B3;BC58;1107 1163 11B3; # (뱘; 뱘; 뱘; 뱘; 뱘; ) HANGUL SYLLABLE BYALS +BC59;BC59;1107 1163 11B4;BC59;1107 1163 11B4; # (뱙; 뱙; 뱙; 뱙; 뱙; ) HANGUL SYLLABLE BYALT +BC5A;BC5A;1107 1163 11B5;BC5A;1107 1163 11B5; # (뱚; 뱚; 뱚; 뱚; 뱚; ) HANGUL SYLLABLE BYALP +BC5B;BC5B;1107 1163 11B6;BC5B;1107 1163 11B6; # (뱛; 뱛; 뱛; 뱛; 뱛; ) HANGUL SYLLABLE BYALH +BC5C;BC5C;1107 1163 11B7;BC5C;1107 1163 11B7; # (뱜; 뱜; 뱜; 뱜; 뱜; ) HANGUL SYLLABLE BYAM +BC5D;BC5D;1107 1163 11B8;BC5D;1107 1163 11B8; # (뱝; 뱝; 뱝; 뱝; 뱝; ) HANGUL SYLLABLE BYAB +BC5E;BC5E;1107 1163 11B9;BC5E;1107 1163 11B9; # (뱞; 뱞; 뱞; 뱞; 뱞; ) HANGUL SYLLABLE BYABS +BC5F;BC5F;1107 1163 11BA;BC5F;1107 1163 11BA; # (뱟; 뱟; 뱟; 뱟; 뱟; ) HANGUL SYLLABLE BYAS +BC60;BC60;1107 1163 11BB;BC60;1107 1163 11BB; # (뱠; 뱠; 뱠; 뱠; 뱠; ) HANGUL SYLLABLE BYASS +BC61;BC61;1107 1163 11BC;BC61;1107 1163 11BC; # (뱡; 뱡; 뱡; 뱡; 뱡; ) HANGUL SYLLABLE BYANG +BC62;BC62;1107 1163 11BD;BC62;1107 1163 11BD; # (뱢; 뱢; 뱢; 뱢; 뱢; ) HANGUL SYLLABLE BYAJ +BC63;BC63;1107 1163 11BE;BC63;1107 1163 11BE; # (뱣; 뱣; 뱣; 뱣; 뱣; ) HANGUL SYLLABLE BYAC +BC64;BC64;1107 1163 11BF;BC64;1107 1163 11BF; # (뱤; 뱤; 뱤; 뱤; 뱤; ) HANGUL SYLLABLE BYAK +BC65;BC65;1107 1163 11C0;BC65;1107 1163 11C0; # (뱥; 뱥; 뱥; 뱥; 뱥; ) HANGUL SYLLABLE BYAT +BC66;BC66;1107 1163 11C1;BC66;1107 1163 11C1; # (뱦; 뱦; 뱦; 뱦; 뱦; ) HANGUL SYLLABLE BYAP +BC67;BC67;1107 1163 11C2;BC67;1107 1163 11C2; # (뱧; 뱧; 뱧; 뱧; 뱧; ) HANGUL SYLLABLE BYAH +BC68;BC68;1107 1164;BC68;1107 1164; # (뱨; 뱨; 뱨; 뱨; 뱨; ) HANGUL SYLLABLE BYAE +BC69;BC69;1107 1164 11A8;BC69;1107 1164 11A8; # (뱩; 뱩; 뱩; 뱩; 뱩; ) HANGUL SYLLABLE BYAEG +BC6A;BC6A;1107 1164 11A9;BC6A;1107 1164 11A9; # (뱪; 뱪; 뱪; 뱪; 뱪; ) HANGUL SYLLABLE BYAEGG +BC6B;BC6B;1107 1164 11AA;BC6B;1107 1164 11AA; # (뱫; 뱫; 뱫; 뱫; 뱫; ) HANGUL SYLLABLE BYAEGS +BC6C;BC6C;1107 1164 11AB;BC6C;1107 1164 11AB; # (뱬; 뱬; 뱬; 뱬; 뱬; ) HANGUL SYLLABLE BYAEN +BC6D;BC6D;1107 1164 11AC;BC6D;1107 1164 11AC; # (뱭; 뱭; 뱭; 뱭; 뱭; ) HANGUL SYLLABLE BYAENJ +BC6E;BC6E;1107 1164 11AD;BC6E;1107 1164 11AD; # (뱮; 뱮; 뱮; 뱮; 뱮; ) HANGUL SYLLABLE BYAENH +BC6F;BC6F;1107 1164 11AE;BC6F;1107 1164 11AE; # (뱯; 뱯; 뱯; 뱯; 뱯; ) HANGUL SYLLABLE BYAED +BC70;BC70;1107 1164 11AF;BC70;1107 1164 11AF; # (뱰; 뱰; 뱰; 뱰; 뱰; ) HANGUL SYLLABLE BYAEL +BC71;BC71;1107 1164 11B0;BC71;1107 1164 11B0; # (뱱; 뱱; 뱱; 뱱; 뱱; ) HANGUL SYLLABLE BYAELG +BC72;BC72;1107 1164 11B1;BC72;1107 1164 11B1; # (뱲; 뱲; 뱲; 뱲; 뱲; ) HANGUL SYLLABLE BYAELM +BC73;BC73;1107 1164 11B2;BC73;1107 1164 11B2; # (뱳; 뱳; 뱳; 뱳; 뱳; ) HANGUL SYLLABLE BYAELB +BC74;BC74;1107 1164 11B3;BC74;1107 1164 11B3; # (뱴; 뱴; 뱴; 뱴; 뱴; ) HANGUL SYLLABLE BYAELS +BC75;BC75;1107 1164 11B4;BC75;1107 1164 11B4; # (뱵; 뱵; 뱵; 뱵; 뱵; ) HANGUL SYLLABLE BYAELT +BC76;BC76;1107 1164 11B5;BC76;1107 1164 11B5; # (뱶; 뱶; 뱶; 뱶; 뱶; ) HANGUL SYLLABLE BYAELP +BC77;BC77;1107 1164 11B6;BC77;1107 1164 11B6; # (뱷; 뱷; 뱷; 뱷; 뱷; ) HANGUL SYLLABLE BYAELH +BC78;BC78;1107 1164 11B7;BC78;1107 1164 11B7; # (뱸; 뱸; 뱸; 뱸; 뱸; ) HANGUL SYLLABLE BYAEM +BC79;BC79;1107 1164 11B8;BC79;1107 1164 11B8; # (뱹; 뱹; 뱹; 뱹; 뱹; ) HANGUL SYLLABLE BYAEB +BC7A;BC7A;1107 1164 11B9;BC7A;1107 1164 11B9; # (뱺; 뱺; 뱺; 뱺; 뱺; ) HANGUL SYLLABLE BYAEBS +BC7B;BC7B;1107 1164 11BA;BC7B;1107 1164 11BA; # (뱻; 뱻; 뱻; 뱻; 뱻; ) HANGUL SYLLABLE BYAES +BC7C;BC7C;1107 1164 11BB;BC7C;1107 1164 11BB; # (뱼; 뱼; 뱼; 뱼; 뱼; ) HANGUL SYLLABLE BYAESS +BC7D;BC7D;1107 1164 11BC;BC7D;1107 1164 11BC; # (뱽; 뱽; 뱽; 뱽; 뱽; ) HANGUL SYLLABLE BYAENG +BC7E;BC7E;1107 1164 11BD;BC7E;1107 1164 11BD; # (뱾; 뱾; 뱾; 뱾; 뱾; ) HANGUL SYLLABLE BYAEJ +BC7F;BC7F;1107 1164 11BE;BC7F;1107 1164 11BE; # (뱿; 뱿; 뱿; 뱿; 뱿; ) HANGUL SYLLABLE BYAEC +BC80;BC80;1107 1164 11BF;BC80;1107 1164 11BF; # (벀; 벀; 벀; 벀; 벀; ) HANGUL SYLLABLE BYAEK +BC81;BC81;1107 1164 11C0;BC81;1107 1164 11C0; # (벁; 벁; 벁; 벁; 벁; ) HANGUL SYLLABLE BYAET +BC82;BC82;1107 1164 11C1;BC82;1107 1164 11C1; # (벂; 벂; 벂; 벂; 벂; ) HANGUL SYLLABLE BYAEP +BC83;BC83;1107 1164 11C2;BC83;1107 1164 11C2; # (벃; 벃; 벃; 벃; 벃; ) HANGUL SYLLABLE BYAEH +BC84;BC84;1107 1165;BC84;1107 1165; # (버; 버; 버; 버; 버; ) HANGUL SYLLABLE BEO +BC85;BC85;1107 1165 11A8;BC85;1107 1165 11A8; # (벅; 벅; 벅; 벅; 벅; ) HANGUL SYLLABLE BEOG +BC86;BC86;1107 1165 11A9;BC86;1107 1165 11A9; # (벆; 벆; 벆; 벆; 벆; ) HANGUL SYLLABLE BEOGG +BC87;BC87;1107 1165 11AA;BC87;1107 1165 11AA; # (벇; 벇; 벇; 벇; 벇; ) HANGUL SYLLABLE BEOGS +BC88;BC88;1107 1165 11AB;BC88;1107 1165 11AB; # (번; 번; 번; 번; 번; ) HANGUL SYLLABLE BEON +BC89;BC89;1107 1165 11AC;BC89;1107 1165 11AC; # (벉; 벉; 벉; 벉; 벉; ) HANGUL SYLLABLE BEONJ +BC8A;BC8A;1107 1165 11AD;BC8A;1107 1165 11AD; # (벊; 벊; 벊; 벊; 벊; ) HANGUL SYLLABLE BEONH +BC8B;BC8B;1107 1165 11AE;BC8B;1107 1165 11AE; # (벋; 벋; 벋; 벋; 벋; ) HANGUL SYLLABLE BEOD +BC8C;BC8C;1107 1165 11AF;BC8C;1107 1165 11AF; # (벌; 벌; 벌; 벌; 벌; ) HANGUL SYLLABLE BEOL +BC8D;BC8D;1107 1165 11B0;BC8D;1107 1165 11B0; # (벍; 벍; 벍; 벍; 벍; ) HANGUL SYLLABLE BEOLG +BC8E;BC8E;1107 1165 11B1;BC8E;1107 1165 11B1; # (벎; 벎; 벎; 벎; 벎; ) HANGUL SYLLABLE BEOLM +BC8F;BC8F;1107 1165 11B2;BC8F;1107 1165 11B2; # (벏; 벏; 벏; 벏; 벏; ) HANGUL SYLLABLE BEOLB +BC90;BC90;1107 1165 11B3;BC90;1107 1165 11B3; # (벐; 벐; 벐; 벐; 벐; ) HANGUL SYLLABLE BEOLS +BC91;BC91;1107 1165 11B4;BC91;1107 1165 11B4; # (벑; 벑; 벑; 벑; 벑; ) HANGUL SYLLABLE BEOLT +BC92;BC92;1107 1165 11B5;BC92;1107 1165 11B5; # (벒; 벒; 벒; 벒; 벒; ) HANGUL SYLLABLE BEOLP +BC93;BC93;1107 1165 11B6;BC93;1107 1165 11B6; # (벓; 벓; 벓; 벓; 벓; ) HANGUL SYLLABLE BEOLH +BC94;BC94;1107 1165 11B7;BC94;1107 1165 11B7; # (범; 범; 범; 범; 범; ) HANGUL SYLLABLE BEOM +BC95;BC95;1107 1165 11B8;BC95;1107 1165 11B8; # (법; 법; 법; 법; 법; ) HANGUL SYLLABLE BEOB +BC96;BC96;1107 1165 11B9;BC96;1107 1165 11B9; # (벖; 벖; 벖; 벖; 벖; ) HANGUL SYLLABLE BEOBS +BC97;BC97;1107 1165 11BA;BC97;1107 1165 11BA; # (벗; 벗; 벗; 벗; 벗; ) HANGUL SYLLABLE BEOS +BC98;BC98;1107 1165 11BB;BC98;1107 1165 11BB; # (벘; 벘; 벘; 벘; 벘; ) HANGUL SYLLABLE BEOSS +BC99;BC99;1107 1165 11BC;BC99;1107 1165 11BC; # (벙; 벙; 벙; 벙; 벙; ) HANGUL SYLLABLE BEONG +BC9A;BC9A;1107 1165 11BD;BC9A;1107 1165 11BD; # (벚; 벚; 벚; 벚; 벚; ) HANGUL SYLLABLE BEOJ +BC9B;BC9B;1107 1165 11BE;BC9B;1107 1165 11BE; # (벛; 벛; 벛; 벛; 벛; ) HANGUL SYLLABLE BEOC +BC9C;BC9C;1107 1165 11BF;BC9C;1107 1165 11BF; # (벜; 벜; 벜; 벜; 벜; ) HANGUL SYLLABLE BEOK +BC9D;BC9D;1107 1165 11C0;BC9D;1107 1165 11C0; # (벝; 벝; 벝; 벝; 벝; ) HANGUL SYLLABLE BEOT +BC9E;BC9E;1107 1165 11C1;BC9E;1107 1165 11C1; # (벞; 벞; 벞; 벞; 벞; ) HANGUL SYLLABLE BEOP +BC9F;BC9F;1107 1165 11C2;BC9F;1107 1165 11C2; # (벟; 벟; 벟; 벟; 벟; ) HANGUL SYLLABLE BEOH +BCA0;BCA0;1107 1166;BCA0;1107 1166; # (베; 베; 베; 베; 베; ) HANGUL SYLLABLE BE +BCA1;BCA1;1107 1166 11A8;BCA1;1107 1166 11A8; # (벡; 벡; 벡; 벡; 벡; ) HANGUL SYLLABLE BEG +BCA2;BCA2;1107 1166 11A9;BCA2;1107 1166 11A9; # (벢; 벢; 벢; 벢; 벢; ) HANGUL SYLLABLE BEGG +BCA3;BCA3;1107 1166 11AA;BCA3;1107 1166 11AA; # (벣; 벣; 벣; 벣; 벣; ) HANGUL SYLLABLE BEGS +BCA4;BCA4;1107 1166 11AB;BCA4;1107 1166 11AB; # (벤; 벤; 벤; 벤; 벤; ) HANGUL SYLLABLE BEN +BCA5;BCA5;1107 1166 11AC;BCA5;1107 1166 11AC; # (벥; 벥; 벥; 벥; 벥; ) HANGUL SYLLABLE BENJ +BCA6;BCA6;1107 1166 11AD;BCA6;1107 1166 11AD; # (벦; 벦; 벦; 벦; 벦; ) HANGUL SYLLABLE BENH +BCA7;BCA7;1107 1166 11AE;BCA7;1107 1166 11AE; # (벧; 벧; 벧; 벧; 벧; ) HANGUL SYLLABLE BED +BCA8;BCA8;1107 1166 11AF;BCA8;1107 1166 11AF; # (벨; 벨; 벨; 벨; 벨; ) HANGUL SYLLABLE BEL +BCA9;BCA9;1107 1166 11B0;BCA9;1107 1166 11B0; # (벩; 벩; 벩; 벩; 벩; ) HANGUL SYLLABLE BELG +BCAA;BCAA;1107 1166 11B1;BCAA;1107 1166 11B1; # (벪; 벪; 벪; 벪; 벪; ) HANGUL SYLLABLE BELM +BCAB;BCAB;1107 1166 11B2;BCAB;1107 1166 11B2; # (벫; 벫; 벫; 벫; 벫; ) HANGUL SYLLABLE BELB +BCAC;BCAC;1107 1166 11B3;BCAC;1107 1166 11B3; # (벬; 벬; 벬; 벬; 벬; ) HANGUL SYLLABLE BELS +BCAD;BCAD;1107 1166 11B4;BCAD;1107 1166 11B4; # (벭; 벭; 벭; 벭; 벭; ) HANGUL SYLLABLE BELT +BCAE;BCAE;1107 1166 11B5;BCAE;1107 1166 11B5; # (벮; 벮; 벮; 벮; 벮; ) HANGUL SYLLABLE BELP +BCAF;BCAF;1107 1166 11B6;BCAF;1107 1166 11B6; # (벯; 벯; 벯; 벯; 벯; ) HANGUL SYLLABLE BELH +BCB0;BCB0;1107 1166 11B7;BCB0;1107 1166 11B7; # (벰; 벰; 벰; 벰; 벰; ) HANGUL SYLLABLE BEM +BCB1;BCB1;1107 1166 11B8;BCB1;1107 1166 11B8; # (벱; 벱; 벱; 벱; 벱; ) HANGUL SYLLABLE BEB +BCB2;BCB2;1107 1166 11B9;BCB2;1107 1166 11B9; # (벲; 벲; 벲; 벲; 벲; ) HANGUL SYLLABLE BEBS +BCB3;BCB3;1107 1166 11BA;BCB3;1107 1166 11BA; # (벳; 벳; 벳; 벳; 벳; ) HANGUL SYLLABLE BES +BCB4;BCB4;1107 1166 11BB;BCB4;1107 1166 11BB; # (벴; 벴; 벴; 벴; 벴; ) HANGUL SYLLABLE BESS +BCB5;BCB5;1107 1166 11BC;BCB5;1107 1166 11BC; # (벵; 벵; 벵; 벵; 벵; ) HANGUL SYLLABLE BENG +BCB6;BCB6;1107 1166 11BD;BCB6;1107 1166 11BD; # (벶; 벶; 벶; 벶; 벶; ) HANGUL SYLLABLE BEJ +BCB7;BCB7;1107 1166 11BE;BCB7;1107 1166 11BE; # (벷; 벷; 벷; 벷; 벷; ) HANGUL SYLLABLE BEC +BCB8;BCB8;1107 1166 11BF;BCB8;1107 1166 11BF; # (벸; 벸; 벸; 벸; 벸; ) HANGUL SYLLABLE BEK +BCB9;BCB9;1107 1166 11C0;BCB9;1107 1166 11C0; # (벹; 벹; 벹; 벹; 벹; ) HANGUL SYLLABLE BET +BCBA;BCBA;1107 1166 11C1;BCBA;1107 1166 11C1; # (벺; 벺; 벺; 벺; 벺; ) HANGUL SYLLABLE BEP +BCBB;BCBB;1107 1166 11C2;BCBB;1107 1166 11C2; # (벻; 벻; 벻; 벻; 벻; ) HANGUL SYLLABLE BEH +BCBC;BCBC;1107 1167;BCBC;1107 1167; # (벼; 벼; 벼; 벼; 벼; ) HANGUL SYLLABLE BYEO +BCBD;BCBD;1107 1167 11A8;BCBD;1107 1167 11A8; # (벽; 벽; 벽; 벽; 벽; ) HANGUL SYLLABLE BYEOG +BCBE;BCBE;1107 1167 11A9;BCBE;1107 1167 11A9; # (벾; 벾; 벾; 벾; 벾; ) HANGUL SYLLABLE BYEOGG +BCBF;BCBF;1107 1167 11AA;BCBF;1107 1167 11AA; # (벿; 벿; 벿; 벿; 벿; ) HANGUL SYLLABLE BYEOGS +BCC0;BCC0;1107 1167 11AB;BCC0;1107 1167 11AB; # (변; 변; 변; 변; 변; ) HANGUL SYLLABLE BYEON +BCC1;BCC1;1107 1167 11AC;BCC1;1107 1167 11AC; # (볁; 볁; 볁; 볁; 볁; ) HANGUL SYLLABLE BYEONJ +BCC2;BCC2;1107 1167 11AD;BCC2;1107 1167 11AD; # (볂; 볂; 볂; 볂; 볂; ) HANGUL SYLLABLE BYEONH +BCC3;BCC3;1107 1167 11AE;BCC3;1107 1167 11AE; # (볃; 볃; 볃; 볃; 볃; ) HANGUL SYLLABLE BYEOD +BCC4;BCC4;1107 1167 11AF;BCC4;1107 1167 11AF; # (별; 별; 별; 별; 별; ) HANGUL SYLLABLE BYEOL +BCC5;BCC5;1107 1167 11B0;BCC5;1107 1167 11B0; # (볅; 볅; 볅; 볅; 볅; ) HANGUL SYLLABLE BYEOLG +BCC6;BCC6;1107 1167 11B1;BCC6;1107 1167 11B1; # (볆; 볆; 볆; 볆; 볆; ) HANGUL SYLLABLE BYEOLM +BCC7;BCC7;1107 1167 11B2;BCC7;1107 1167 11B2; # (볇; 볇; 볇; 볇; 볇; ) HANGUL SYLLABLE BYEOLB +BCC8;BCC8;1107 1167 11B3;BCC8;1107 1167 11B3; # (볈; 볈; 볈; 볈; 볈; ) HANGUL SYLLABLE BYEOLS +BCC9;BCC9;1107 1167 11B4;BCC9;1107 1167 11B4; # (볉; 볉; 볉; 볉; 볉; ) HANGUL SYLLABLE BYEOLT +BCCA;BCCA;1107 1167 11B5;BCCA;1107 1167 11B5; # (볊; 볊; 볊; 볊; 볊; ) HANGUL SYLLABLE BYEOLP +BCCB;BCCB;1107 1167 11B6;BCCB;1107 1167 11B6; # (볋; 볋; 볋; 볋; 볋; ) HANGUL SYLLABLE BYEOLH +BCCC;BCCC;1107 1167 11B7;BCCC;1107 1167 11B7; # (볌; 볌; 볌; 볌; 볌; ) HANGUL SYLLABLE BYEOM +BCCD;BCCD;1107 1167 11B8;BCCD;1107 1167 11B8; # (볍; 볍; 볍; 볍; 볍; ) HANGUL SYLLABLE BYEOB +BCCE;BCCE;1107 1167 11B9;BCCE;1107 1167 11B9; # (볎; 볎; 볎; 볎; 볎; ) HANGUL SYLLABLE BYEOBS +BCCF;BCCF;1107 1167 11BA;BCCF;1107 1167 11BA; # (볏; 볏; 볏; 볏; 볏; ) HANGUL SYLLABLE BYEOS +BCD0;BCD0;1107 1167 11BB;BCD0;1107 1167 11BB; # (볐; 볐; 볐; 볐; 볐; ) HANGUL SYLLABLE BYEOSS +BCD1;BCD1;1107 1167 11BC;BCD1;1107 1167 11BC; # (병; 병; 병; 병; 병; ) HANGUL SYLLABLE BYEONG +BCD2;BCD2;1107 1167 11BD;BCD2;1107 1167 11BD; # (볒; 볒; 볒; 볒; 볒; ) HANGUL SYLLABLE BYEOJ +BCD3;BCD3;1107 1167 11BE;BCD3;1107 1167 11BE; # (볓; 볓; 볓; 볓; 볓; ) HANGUL SYLLABLE BYEOC +BCD4;BCD4;1107 1167 11BF;BCD4;1107 1167 11BF; # (볔; 볔; 볔; 볔; 볔; ) HANGUL SYLLABLE BYEOK +BCD5;BCD5;1107 1167 11C0;BCD5;1107 1167 11C0; # (볕; 볕; 볕; 볕; 볕; ) HANGUL SYLLABLE BYEOT +BCD6;BCD6;1107 1167 11C1;BCD6;1107 1167 11C1; # (볖; 볖; 볖; 볖; 볖; ) HANGUL SYLLABLE BYEOP +BCD7;BCD7;1107 1167 11C2;BCD7;1107 1167 11C2; # (볗; 볗; 볗; 볗; 볗; ) HANGUL SYLLABLE BYEOH +BCD8;BCD8;1107 1168;BCD8;1107 1168; # (볘; 볘; 볘; 볘; 볘; ) HANGUL SYLLABLE BYE +BCD9;BCD9;1107 1168 11A8;BCD9;1107 1168 11A8; # (볙; 볙; 볙; 볙; 볙; ) HANGUL SYLLABLE BYEG +BCDA;BCDA;1107 1168 11A9;BCDA;1107 1168 11A9; # (볚; 볚; 볚; 볚; 볚; ) HANGUL SYLLABLE BYEGG +BCDB;BCDB;1107 1168 11AA;BCDB;1107 1168 11AA; # (볛; 볛; 볛; 볛; 볛; ) HANGUL SYLLABLE BYEGS +BCDC;BCDC;1107 1168 11AB;BCDC;1107 1168 11AB; # (볜; 볜; 볜; 볜; 볜; ) HANGUL SYLLABLE BYEN +BCDD;BCDD;1107 1168 11AC;BCDD;1107 1168 11AC; # (볝; 볝; 볝; 볝; 볝; ) HANGUL SYLLABLE BYENJ +BCDE;BCDE;1107 1168 11AD;BCDE;1107 1168 11AD; # (볞; 볞; 볞; 볞; 볞; ) HANGUL SYLLABLE BYENH +BCDF;BCDF;1107 1168 11AE;BCDF;1107 1168 11AE; # (볟; 볟; 볟; 볟; 볟; ) HANGUL SYLLABLE BYED +BCE0;BCE0;1107 1168 11AF;BCE0;1107 1168 11AF; # (볠; 볠; 볠; 볠; 볠; ) HANGUL SYLLABLE BYEL +BCE1;BCE1;1107 1168 11B0;BCE1;1107 1168 11B0; # (볡; 볡; 볡; 볡; 볡; ) HANGUL SYLLABLE BYELG +BCE2;BCE2;1107 1168 11B1;BCE2;1107 1168 11B1; # (볢; 볢; 볢; 볢; 볢; ) HANGUL SYLLABLE BYELM +BCE3;BCE3;1107 1168 11B2;BCE3;1107 1168 11B2; # (볣; 볣; 볣; 볣; 볣; ) HANGUL SYLLABLE BYELB +BCE4;BCE4;1107 1168 11B3;BCE4;1107 1168 11B3; # (볤; 볤; 볤; 볤; 볤; ) HANGUL SYLLABLE BYELS +BCE5;BCE5;1107 1168 11B4;BCE5;1107 1168 11B4; # (볥; 볥; 볥; 볥; 볥; ) HANGUL SYLLABLE BYELT +BCE6;BCE6;1107 1168 11B5;BCE6;1107 1168 11B5; # (볦; 볦; 볦; 볦; 볦; ) HANGUL SYLLABLE BYELP +BCE7;BCE7;1107 1168 11B6;BCE7;1107 1168 11B6; # (볧; 볧; 볧; 볧; 볧; ) HANGUL SYLLABLE BYELH +BCE8;BCE8;1107 1168 11B7;BCE8;1107 1168 11B7; # (볨; 볨; 볨; 볨; 볨; ) HANGUL SYLLABLE BYEM +BCE9;BCE9;1107 1168 11B8;BCE9;1107 1168 11B8; # (볩; 볩; 볩; 볩; 볩; ) HANGUL SYLLABLE BYEB +BCEA;BCEA;1107 1168 11B9;BCEA;1107 1168 11B9; # (볪; 볪; 볪; 볪; 볪; ) HANGUL SYLLABLE BYEBS +BCEB;BCEB;1107 1168 11BA;BCEB;1107 1168 11BA; # (볫; 볫; 볫; 볫; 볫; ) HANGUL SYLLABLE BYES +BCEC;BCEC;1107 1168 11BB;BCEC;1107 1168 11BB; # (볬; 볬; 볬; 볬; 볬; ) HANGUL SYLLABLE BYESS +BCED;BCED;1107 1168 11BC;BCED;1107 1168 11BC; # (볭; 볭; 볭; 볭; 볭; ) HANGUL SYLLABLE BYENG +BCEE;BCEE;1107 1168 11BD;BCEE;1107 1168 11BD; # (볮; 볮; 볮; 볮; 볮; ) HANGUL SYLLABLE BYEJ +BCEF;BCEF;1107 1168 11BE;BCEF;1107 1168 11BE; # (볯; 볯; 볯; 볯; 볯; ) HANGUL SYLLABLE BYEC +BCF0;BCF0;1107 1168 11BF;BCF0;1107 1168 11BF; # (볰; 볰; 볰; 볰; 볰; ) HANGUL SYLLABLE BYEK +BCF1;BCF1;1107 1168 11C0;BCF1;1107 1168 11C0; # (볱; 볱; 볱; 볱; 볱; ) HANGUL SYLLABLE BYET +BCF2;BCF2;1107 1168 11C1;BCF2;1107 1168 11C1; # (볲; 볲; 볲; 볲; 볲; ) HANGUL SYLLABLE BYEP +BCF3;BCF3;1107 1168 11C2;BCF3;1107 1168 11C2; # (볳; 볳; 볳; 볳; 볳; ) HANGUL SYLLABLE BYEH +BCF4;BCF4;1107 1169;BCF4;1107 1169; # (보; 보; 보; 보; 보; ) HANGUL SYLLABLE BO +BCF5;BCF5;1107 1169 11A8;BCF5;1107 1169 11A8; # (복; 복; 복; 복; 복; ) HANGUL SYLLABLE BOG +BCF6;BCF6;1107 1169 11A9;BCF6;1107 1169 11A9; # (볶; 볶; 볶; 볶; 볶; ) HANGUL SYLLABLE BOGG +BCF7;BCF7;1107 1169 11AA;BCF7;1107 1169 11AA; # (볷; 볷; 볷; 볷; 볷; ) HANGUL SYLLABLE BOGS +BCF8;BCF8;1107 1169 11AB;BCF8;1107 1169 11AB; # (본; 본; 본; 본; 본; ) HANGUL SYLLABLE BON +BCF9;BCF9;1107 1169 11AC;BCF9;1107 1169 11AC; # (볹; 볹; 볹; 볹; 볹; ) HANGUL SYLLABLE BONJ +BCFA;BCFA;1107 1169 11AD;BCFA;1107 1169 11AD; # (볺; 볺; 볺; 볺; 볺; ) HANGUL SYLLABLE BONH +BCFB;BCFB;1107 1169 11AE;BCFB;1107 1169 11AE; # (볻; 볻; 볻; 볻; 볻; ) HANGUL SYLLABLE BOD +BCFC;BCFC;1107 1169 11AF;BCFC;1107 1169 11AF; # (볼; 볼; 볼; 볼; 볼; ) HANGUL SYLLABLE BOL +BCFD;BCFD;1107 1169 11B0;BCFD;1107 1169 11B0; # (볽; 볽; 볽; 볽; 볽; ) HANGUL SYLLABLE BOLG +BCFE;BCFE;1107 1169 11B1;BCFE;1107 1169 11B1; # (볾; 볾; 볾; 볾; 볾; ) HANGUL SYLLABLE BOLM +BCFF;BCFF;1107 1169 11B2;BCFF;1107 1169 11B2; # (볿; 볿; 볿; 볿; 볿; ) HANGUL SYLLABLE BOLB +BD00;BD00;1107 1169 11B3;BD00;1107 1169 11B3; # (봀; 봀; 봀; 봀; 봀; ) HANGUL SYLLABLE BOLS +BD01;BD01;1107 1169 11B4;BD01;1107 1169 11B4; # (봁; 봁; 봁; 봁; 봁; ) HANGUL SYLLABLE BOLT +BD02;BD02;1107 1169 11B5;BD02;1107 1169 11B5; # (봂; 봂; 봂; 봂; 봂; ) HANGUL SYLLABLE BOLP +BD03;BD03;1107 1169 11B6;BD03;1107 1169 11B6; # (봃; 봃; 봃; 봃; 봃; ) HANGUL SYLLABLE BOLH +BD04;BD04;1107 1169 11B7;BD04;1107 1169 11B7; # (봄; 봄; 봄; 봄; 봄; ) HANGUL SYLLABLE BOM +BD05;BD05;1107 1169 11B8;BD05;1107 1169 11B8; # (봅; 봅; 봅; 봅; 봅; ) HANGUL SYLLABLE BOB +BD06;BD06;1107 1169 11B9;BD06;1107 1169 11B9; # (봆; 봆; 봆; 봆; 봆; ) HANGUL SYLLABLE BOBS +BD07;BD07;1107 1169 11BA;BD07;1107 1169 11BA; # (봇; 봇; 봇; 봇; 봇; ) HANGUL SYLLABLE BOS +BD08;BD08;1107 1169 11BB;BD08;1107 1169 11BB; # (봈; 봈; 봈; 봈; 봈; ) HANGUL SYLLABLE BOSS +BD09;BD09;1107 1169 11BC;BD09;1107 1169 11BC; # (봉; 봉; 봉; 봉; 봉; ) HANGUL SYLLABLE BONG +BD0A;BD0A;1107 1169 11BD;BD0A;1107 1169 11BD; # (봊; 봊; 봊; 봊; 봊; ) HANGUL SYLLABLE BOJ +BD0B;BD0B;1107 1169 11BE;BD0B;1107 1169 11BE; # (봋; 봋; 봋; 봋; 봋; ) HANGUL SYLLABLE BOC +BD0C;BD0C;1107 1169 11BF;BD0C;1107 1169 11BF; # (봌; 봌; 봌; 봌; 봌; ) HANGUL SYLLABLE BOK +BD0D;BD0D;1107 1169 11C0;BD0D;1107 1169 11C0; # (봍; 봍; 봍; 봍; 봍; ) HANGUL SYLLABLE BOT +BD0E;BD0E;1107 1169 11C1;BD0E;1107 1169 11C1; # (봎; 봎; 봎; 봎; 봎; ) HANGUL SYLLABLE BOP +BD0F;BD0F;1107 1169 11C2;BD0F;1107 1169 11C2; # (봏; 봏; 봏; 봏; 봏; ) HANGUL SYLLABLE BOH +BD10;BD10;1107 116A;BD10;1107 116A; # (봐; 봐; 봐; 봐; 봐; ) HANGUL SYLLABLE BWA +BD11;BD11;1107 116A 11A8;BD11;1107 116A 11A8; # (봑; 봑; 봑; 봑; 봑; ) HANGUL SYLLABLE BWAG +BD12;BD12;1107 116A 11A9;BD12;1107 116A 11A9; # (봒; 봒; 봒; 봒; 봒; ) HANGUL SYLLABLE BWAGG +BD13;BD13;1107 116A 11AA;BD13;1107 116A 11AA; # (봓; 봓; 봓; 봓; 봓; ) HANGUL SYLLABLE BWAGS +BD14;BD14;1107 116A 11AB;BD14;1107 116A 11AB; # (봔; 봔; 봔; 봔; 봔; ) HANGUL SYLLABLE BWAN +BD15;BD15;1107 116A 11AC;BD15;1107 116A 11AC; # (봕; 봕; 봕; 봕; 봕; ) HANGUL SYLLABLE BWANJ +BD16;BD16;1107 116A 11AD;BD16;1107 116A 11AD; # (봖; 봖; 봖; 봖; 봖; ) HANGUL SYLLABLE BWANH +BD17;BD17;1107 116A 11AE;BD17;1107 116A 11AE; # (봗; 봗; 봗; 봗; 봗; ) HANGUL SYLLABLE BWAD +BD18;BD18;1107 116A 11AF;BD18;1107 116A 11AF; # (봘; 봘; 봘; 봘; 봘; ) HANGUL SYLLABLE BWAL +BD19;BD19;1107 116A 11B0;BD19;1107 116A 11B0; # (봙; 봙; 봙; 봙; 봙; ) HANGUL SYLLABLE BWALG +BD1A;BD1A;1107 116A 11B1;BD1A;1107 116A 11B1; # (봚; 봚; 봚; 봚; 봚; ) HANGUL SYLLABLE BWALM +BD1B;BD1B;1107 116A 11B2;BD1B;1107 116A 11B2; # (봛; 봛; 봛; 봛; 봛; ) HANGUL SYLLABLE BWALB +BD1C;BD1C;1107 116A 11B3;BD1C;1107 116A 11B3; # (봜; 봜; 봜; 봜; 봜; ) HANGUL SYLLABLE BWALS +BD1D;BD1D;1107 116A 11B4;BD1D;1107 116A 11B4; # (봝; 봝; 봝; 봝; 봝; ) HANGUL SYLLABLE BWALT +BD1E;BD1E;1107 116A 11B5;BD1E;1107 116A 11B5; # (봞; 봞; 봞; 봞; 봞; ) HANGUL SYLLABLE BWALP +BD1F;BD1F;1107 116A 11B6;BD1F;1107 116A 11B6; # (봟; 봟; 봟; 봟; 봟; ) HANGUL SYLLABLE BWALH +BD20;BD20;1107 116A 11B7;BD20;1107 116A 11B7; # (봠; 봠; 봠; 봠; 봠; ) HANGUL SYLLABLE BWAM +BD21;BD21;1107 116A 11B8;BD21;1107 116A 11B8; # (봡; 봡; 봡; 봡; 봡; ) HANGUL SYLLABLE BWAB +BD22;BD22;1107 116A 11B9;BD22;1107 116A 11B9; # (봢; 봢; 봢; 봢; 봢; ) HANGUL SYLLABLE BWABS +BD23;BD23;1107 116A 11BA;BD23;1107 116A 11BA; # (봣; 봣; 봣; 봣; 봣; ) HANGUL SYLLABLE BWAS +BD24;BD24;1107 116A 11BB;BD24;1107 116A 11BB; # (봤; 봤; 봤; 봤; 봤; ) HANGUL SYLLABLE BWASS +BD25;BD25;1107 116A 11BC;BD25;1107 116A 11BC; # (봥; 봥; 봥; 봥; 봥; ) HANGUL SYLLABLE BWANG +BD26;BD26;1107 116A 11BD;BD26;1107 116A 11BD; # (봦; 봦; 봦; 봦; 봦; ) HANGUL SYLLABLE BWAJ +BD27;BD27;1107 116A 11BE;BD27;1107 116A 11BE; # (봧; 봧; 봧; 봧; 봧; ) HANGUL SYLLABLE BWAC +BD28;BD28;1107 116A 11BF;BD28;1107 116A 11BF; # (봨; 봨; 봨; 봨; 봨; ) HANGUL SYLLABLE BWAK +BD29;BD29;1107 116A 11C0;BD29;1107 116A 11C0; # (봩; 봩; 봩; 봩; 봩; ) HANGUL SYLLABLE BWAT +BD2A;BD2A;1107 116A 11C1;BD2A;1107 116A 11C1; # (봪; 봪; 봪; 봪; 봪; ) HANGUL SYLLABLE BWAP +BD2B;BD2B;1107 116A 11C2;BD2B;1107 116A 11C2; # (봫; 봫; 봫; 봫; 봫; ) HANGUL SYLLABLE BWAH +BD2C;BD2C;1107 116B;BD2C;1107 116B; # (봬; 봬; 봬; 봬; 봬; ) HANGUL SYLLABLE BWAE +BD2D;BD2D;1107 116B 11A8;BD2D;1107 116B 11A8; # (봭; 봭; 봭; 봭; 봭; ) HANGUL SYLLABLE BWAEG +BD2E;BD2E;1107 116B 11A9;BD2E;1107 116B 11A9; # (봮; 봮; 봮; 봮; 봮; ) HANGUL SYLLABLE BWAEGG +BD2F;BD2F;1107 116B 11AA;BD2F;1107 116B 11AA; # (봯; 봯; 봯; 봯; 봯; ) HANGUL SYLLABLE BWAEGS +BD30;BD30;1107 116B 11AB;BD30;1107 116B 11AB; # (봰; 봰; 봰; 봰; 봰; ) HANGUL SYLLABLE BWAEN +BD31;BD31;1107 116B 11AC;BD31;1107 116B 11AC; # (봱; 봱; 봱; 봱; 봱; ) HANGUL SYLLABLE BWAENJ +BD32;BD32;1107 116B 11AD;BD32;1107 116B 11AD; # (봲; 봲; 봲; 봲; 봲; ) HANGUL SYLLABLE BWAENH +BD33;BD33;1107 116B 11AE;BD33;1107 116B 11AE; # (봳; 봳; 봳; 봳; 봳; ) HANGUL SYLLABLE BWAED +BD34;BD34;1107 116B 11AF;BD34;1107 116B 11AF; # (봴; 봴; 봴; 봴; 봴; ) HANGUL SYLLABLE BWAEL +BD35;BD35;1107 116B 11B0;BD35;1107 116B 11B0; # (봵; 봵; 봵; 봵; 봵; ) HANGUL SYLLABLE BWAELG +BD36;BD36;1107 116B 11B1;BD36;1107 116B 11B1; # (봶; 봶; 봶; 봶; 봶; ) HANGUL SYLLABLE BWAELM +BD37;BD37;1107 116B 11B2;BD37;1107 116B 11B2; # (봷; 봷; 봷; 봷; 봷; ) HANGUL SYLLABLE BWAELB +BD38;BD38;1107 116B 11B3;BD38;1107 116B 11B3; # (봸; 봸; 봸; 봸; 봸; ) HANGUL SYLLABLE BWAELS +BD39;BD39;1107 116B 11B4;BD39;1107 116B 11B4; # (봹; 봹; 봹; 봹; 봹; ) HANGUL SYLLABLE BWAELT +BD3A;BD3A;1107 116B 11B5;BD3A;1107 116B 11B5; # (봺; 봺; 봺; 봺; 봺; ) HANGUL SYLLABLE BWAELP +BD3B;BD3B;1107 116B 11B6;BD3B;1107 116B 11B6; # (봻; 봻; 봻; 봻; 봻; ) HANGUL SYLLABLE BWAELH +BD3C;BD3C;1107 116B 11B7;BD3C;1107 116B 11B7; # (봼; 봼; 봼; 봼; 봼; ) HANGUL SYLLABLE BWAEM +BD3D;BD3D;1107 116B 11B8;BD3D;1107 116B 11B8; # (봽; 봽; 봽; 봽; 봽; ) HANGUL SYLLABLE BWAEB +BD3E;BD3E;1107 116B 11B9;BD3E;1107 116B 11B9; # (봾; 봾; 봾; 봾; 봾; ) HANGUL SYLLABLE BWAEBS +BD3F;BD3F;1107 116B 11BA;BD3F;1107 116B 11BA; # (봿; 봿; 봿; 봿; 봿; ) HANGUL SYLLABLE BWAES +BD40;BD40;1107 116B 11BB;BD40;1107 116B 11BB; # (뵀; 뵀; 뵀; 뵀; 뵀; ) HANGUL SYLLABLE BWAESS +BD41;BD41;1107 116B 11BC;BD41;1107 116B 11BC; # (뵁; 뵁; 뵁; 뵁; 뵁; ) HANGUL SYLLABLE BWAENG +BD42;BD42;1107 116B 11BD;BD42;1107 116B 11BD; # (뵂; 뵂; 뵂; 뵂; 뵂; ) HANGUL SYLLABLE BWAEJ +BD43;BD43;1107 116B 11BE;BD43;1107 116B 11BE; # (뵃; 뵃; 뵃; 뵃; 뵃; ) HANGUL SYLLABLE BWAEC +BD44;BD44;1107 116B 11BF;BD44;1107 116B 11BF; # (뵄; 뵄; 뵄; 뵄; 뵄; ) HANGUL SYLLABLE BWAEK +BD45;BD45;1107 116B 11C0;BD45;1107 116B 11C0; # (뵅; 뵅; 뵅; 뵅; 뵅; ) HANGUL SYLLABLE BWAET +BD46;BD46;1107 116B 11C1;BD46;1107 116B 11C1; # (뵆; 뵆; 뵆; 뵆; 뵆; ) HANGUL SYLLABLE BWAEP +BD47;BD47;1107 116B 11C2;BD47;1107 116B 11C2; # (뵇; 뵇; 뵇; 뵇; 뵇; ) HANGUL SYLLABLE BWAEH +BD48;BD48;1107 116C;BD48;1107 116C; # (뵈; 뵈; 뵈; 뵈; 뵈; ) HANGUL SYLLABLE BOE +BD49;BD49;1107 116C 11A8;BD49;1107 116C 11A8; # (뵉; 뵉; 뵉; 뵉; 뵉; ) HANGUL SYLLABLE BOEG +BD4A;BD4A;1107 116C 11A9;BD4A;1107 116C 11A9; # (뵊; 뵊; 뵊; 뵊; 뵊; ) HANGUL SYLLABLE BOEGG +BD4B;BD4B;1107 116C 11AA;BD4B;1107 116C 11AA; # (뵋; 뵋; 뵋; 뵋; 뵋; ) HANGUL SYLLABLE BOEGS +BD4C;BD4C;1107 116C 11AB;BD4C;1107 116C 11AB; # (뵌; 뵌; 뵌; 뵌; 뵌; ) HANGUL SYLLABLE BOEN +BD4D;BD4D;1107 116C 11AC;BD4D;1107 116C 11AC; # (뵍; 뵍; 뵍; 뵍; 뵍; ) HANGUL SYLLABLE BOENJ +BD4E;BD4E;1107 116C 11AD;BD4E;1107 116C 11AD; # (뵎; 뵎; 뵎; 뵎; 뵎; ) HANGUL SYLLABLE BOENH +BD4F;BD4F;1107 116C 11AE;BD4F;1107 116C 11AE; # (뵏; 뵏; 뵏; 뵏; 뵏; ) HANGUL SYLLABLE BOED +BD50;BD50;1107 116C 11AF;BD50;1107 116C 11AF; # (뵐; 뵐; 뵐; 뵐; 뵐; ) HANGUL SYLLABLE BOEL +BD51;BD51;1107 116C 11B0;BD51;1107 116C 11B0; # (뵑; 뵑; 뵑; 뵑; 뵑; ) HANGUL SYLLABLE BOELG +BD52;BD52;1107 116C 11B1;BD52;1107 116C 11B1; # (뵒; 뵒; 뵒; 뵒; 뵒; ) HANGUL SYLLABLE BOELM +BD53;BD53;1107 116C 11B2;BD53;1107 116C 11B2; # (뵓; 뵓; 뵓; 뵓; 뵓; ) HANGUL SYLLABLE BOELB +BD54;BD54;1107 116C 11B3;BD54;1107 116C 11B3; # (뵔; 뵔; 뵔; 뵔; 뵔; ) HANGUL SYLLABLE BOELS +BD55;BD55;1107 116C 11B4;BD55;1107 116C 11B4; # (뵕; 뵕; 뵕; 뵕; 뵕; ) HANGUL SYLLABLE BOELT +BD56;BD56;1107 116C 11B5;BD56;1107 116C 11B5; # (뵖; 뵖; 뵖; 뵖; 뵖; ) HANGUL SYLLABLE BOELP +BD57;BD57;1107 116C 11B6;BD57;1107 116C 11B6; # (뵗; 뵗; 뵗; 뵗; 뵗; ) HANGUL SYLLABLE BOELH +BD58;BD58;1107 116C 11B7;BD58;1107 116C 11B7; # (뵘; 뵘; 뵘; 뵘; 뵘; ) HANGUL SYLLABLE BOEM +BD59;BD59;1107 116C 11B8;BD59;1107 116C 11B8; # (뵙; 뵙; 뵙; 뵙; 뵙; ) HANGUL SYLLABLE BOEB +BD5A;BD5A;1107 116C 11B9;BD5A;1107 116C 11B9; # (뵚; 뵚; 뵚; 뵚; 뵚; ) HANGUL SYLLABLE BOEBS +BD5B;BD5B;1107 116C 11BA;BD5B;1107 116C 11BA; # (뵛; 뵛; 뵛; 뵛; 뵛; ) HANGUL SYLLABLE BOES +BD5C;BD5C;1107 116C 11BB;BD5C;1107 116C 11BB; # (뵜; 뵜; 뵜; 뵜; 뵜; ) HANGUL SYLLABLE BOESS +BD5D;BD5D;1107 116C 11BC;BD5D;1107 116C 11BC; # (뵝; 뵝; 뵝; 뵝; 뵝; ) HANGUL SYLLABLE BOENG +BD5E;BD5E;1107 116C 11BD;BD5E;1107 116C 11BD; # (뵞; 뵞; 뵞; 뵞; 뵞; ) HANGUL SYLLABLE BOEJ +BD5F;BD5F;1107 116C 11BE;BD5F;1107 116C 11BE; # (뵟; 뵟; 뵟; 뵟; 뵟; ) HANGUL SYLLABLE BOEC +BD60;BD60;1107 116C 11BF;BD60;1107 116C 11BF; # (뵠; 뵠; 뵠; 뵠; 뵠; ) HANGUL SYLLABLE BOEK +BD61;BD61;1107 116C 11C0;BD61;1107 116C 11C0; # (뵡; 뵡; 뵡; 뵡; 뵡; ) HANGUL SYLLABLE BOET +BD62;BD62;1107 116C 11C1;BD62;1107 116C 11C1; # (뵢; 뵢; 뵢; 뵢; 뵢; ) HANGUL SYLLABLE BOEP +BD63;BD63;1107 116C 11C2;BD63;1107 116C 11C2; # (뵣; 뵣; 뵣; 뵣; 뵣; ) HANGUL SYLLABLE BOEH +BD64;BD64;1107 116D;BD64;1107 116D; # (뵤; 뵤; 뵤; 뵤; 뵤; ) HANGUL SYLLABLE BYO +BD65;BD65;1107 116D 11A8;BD65;1107 116D 11A8; # (뵥; 뵥; 뵥; 뵥; 뵥; ) HANGUL SYLLABLE BYOG +BD66;BD66;1107 116D 11A9;BD66;1107 116D 11A9; # (뵦; 뵦; 뵦; 뵦; 뵦; ) HANGUL SYLLABLE BYOGG +BD67;BD67;1107 116D 11AA;BD67;1107 116D 11AA; # (뵧; 뵧; 뵧; 뵧; 뵧; ) HANGUL SYLLABLE BYOGS +BD68;BD68;1107 116D 11AB;BD68;1107 116D 11AB; # (뵨; 뵨; 뵨; 뵨; 뵨; ) HANGUL SYLLABLE BYON +BD69;BD69;1107 116D 11AC;BD69;1107 116D 11AC; # (뵩; 뵩; 뵩; 뵩; 뵩; ) HANGUL SYLLABLE BYONJ +BD6A;BD6A;1107 116D 11AD;BD6A;1107 116D 11AD; # (뵪; 뵪; 뵪; 뵪; 뵪; ) HANGUL SYLLABLE BYONH +BD6B;BD6B;1107 116D 11AE;BD6B;1107 116D 11AE; # (뵫; 뵫; 뵫; 뵫; 뵫; ) HANGUL SYLLABLE BYOD +BD6C;BD6C;1107 116D 11AF;BD6C;1107 116D 11AF; # (뵬; 뵬; 뵬; 뵬; 뵬; ) HANGUL SYLLABLE BYOL +BD6D;BD6D;1107 116D 11B0;BD6D;1107 116D 11B0; # (뵭; 뵭; 뵭; 뵭; 뵭; ) HANGUL SYLLABLE BYOLG +BD6E;BD6E;1107 116D 11B1;BD6E;1107 116D 11B1; # (뵮; 뵮; 뵮; 뵮; 뵮; ) HANGUL SYLLABLE BYOLM +BD6F;BD6F;1107 116D 11B2;BD6F;1107 116D 11B2; # (뵯; 뵯; 뵯; 뵯; 뵯; ) HANGUL SYLLABLE BYOLB +BD70;BD70;1107 116D 11B3;BD70;1107 116D 11B3; # (뵰; 뵰; 뵰; 뵰; 뵰; ) HANGUL SYLLABLE BYOLS +BD71;BD71;1107 116D 11B4;BD71;1107 116D 11B4; # (뵱; 뵱; 뵱; 뵱; 뵱; ) HANGUL SYLLABLE BYOLT +BD72;BD72;1107 116D 11B5;BD72;1107 116D 11B5; # (뵲; 뵲; 뵲; 뵲; 뵲; ) HANGUL SYLLABLE BYOLP +BD73;BD73;1107 116D 11B6;BD73;1107 116D 11B6; # (뵳; 뵳; 뵳; 뵳; 뵳; ) HANGUL SYLLABLE BYOLH +BD74;BD74;1107 116D 11B7;BD74;1107 116D 11B7; # (뵴; 뵴; 뵴; 뵴; 뵴; ) HANGUL SYLLABLE BYOM +BD75;BD75;1107 116D 11B8;BD75;1107 116D 11B8; # (뵵; 뵵; 뵵; 뵵; 뵵; ) HANGUL SYLLABLE BYOB +BD76;BD76;1107 116D 11B9;BD76;1107 116D 11B9; # (뵶; 뵶; 뵶; 뵶; 뵶; ) HANGUL SYLLABLE BYOBS +BD77;BD77;1107 116D 11BA;BD77;1107 116D 11BA; # (뵷; 뵷; 뵷; 뵷; 뵷; ) HANGUL SYLLABLE BYOS +BD78;BD78;1107 116D 11BB;BD78;1107 116D 11BB; # (뵸; 뵸; 뵸; 뵸; 뵸; ) HANGUL SYLLABLE BYOSS +BD79;BD79;1107 116D 11BC;BD79;1107 116D 11BC; # (뵹; 뵹; 뵹; 뵹; 뵹; ) HANGUL SYLLABLE BYONG +BD7A;BD7A;1107 116D 11BD;BD7A;1107 116D 11BD; # (뵺; 뵺; 뵺; 뵺; 뵺; ) HANGUL SYLLABLE BYOJ +BD7B;BD7B;1107 116D 11BE;BD7B;1107 116D 11BE; # (뵻; 뵻; 뵻; 뵻; 뵻; ) HANGUL SYLLABLE BYOC +BD7C;BD7C;1107 116D 11BF;BD7C;1107 116D 11BF; # (뵼; 뵼; 뵼; 뵼; 뵼; ) HANGUL SYLLABLE BYOK +BD7D;BD7D;1107 116D 11C0;BD7D;1107 116D 11C0; # (뵽; 뵽; 뵽; 뵽; 뵽; ) HANGUL SYLLABLE BYOT +BD7E;BD7E;1107 116D 11C1;BD7E;1107 116D 11C1; # (뵾; 뵾; 뵾; 뵾; 뵾; ) HANGUL SYLLABLE BYOP +BD7F;BD7F;1107 116D 11C2;BD7F;1107 116D 11C2; # (뵿; 뵿; 뵿; 뵿; 뵿; ) HANGUL SYLLABLE BYOH +BD80;BD80;1107 116E;BD80;1107 116E; # (부; 부; 부; 부; 부; ) HANGUL SYLLABLE BU +BD81;BD81;1107 116E 11A8;BD81;1107 116E 11A8; # (북; 북; 북; 북; 북; ) HANGUL SYLLABLE BUG +BD82;BD82;1107 116E 11A9;BD82;1107 116E 11A9; # (붂; 붂; 붂; 붂; 붂; ) HANGUL SYLLABLE BUGG +BD83;BD83;1107 116E 11AA;BD83;1107 116E 11AA; # (붃; 붃; 붃; 붃; 붃; ) HANGUL SYLLABLE BUGS +BD84;BD84;1107 116E 11AB;BD84;1107 116E 11AB; # (분; 분; 분; 분; 분; ) HANGUL SYLLABLE BUN +BD85;BD85;1107 116E 11AC;BD85;1107 116E 11AC; # (붅; 붅; 붅; 붅; 붅; ) HANGUL SYLLABLE BUNJ +BD86;BD86;1107 116E 11AD;BD86;1107 116E 11AD; # (붆; 붆; 붆; 붆; 붆; ) HANGUL SYLLABLE BUNH +BD87;BD87;1107 116E 11AE;BD87;1107 116E 11AE; # (붇; 붇; 붇; 붇; 붇; ) HANGUL SYLLABLE BUD +BD88;BD88;1107 116E 11AF;BD88;1107 116E 11AF; # (불; 불; 불; 불; 불; ) HANGUL SYLLABLE BUL +BD89;BD89;1107 116E 11B0;BD89;1107 116E 11B0; # (붉; 붉; 붉; 붉; 붉; ) HANGUL SYLLABLE BULG +BD8A;BD8A;1107 116E 11B1;BD8A;1107 116E 11B1; # (붊; 붊; 붊; 붊; 붊; ) HANGUL SYLLABLE BULM +BD8B;BD8B;1107 116E 11B2;BD8B;1107 116E 11B2; # (붋; 붋; 붋; 붋; 붋; ) HANGUL SYLLABLE BULB +BD8C;BD8C;1107 116E 11B3;BD8C;1107 116E 11B3; # (붌; 붌; 붌; 붌; 붌; ) HANGUL SYLLABLE BULS +BD8D;BD8D;1107 116E 11B4;BD8D;1107 116E 11B4; # (붍; 붍; 붍; 붍; 붍; ) HANGUL SYLLABLE BULT +BD8E;BD8E;1107 116E 11B5;BD8E;1107 116E 11B5; # (붎; 붎; 붎; 붎; 붎; ) HANGUL SYLLABLE BULP +BD8F;BD8F;1107 116E 11B6;BD8F;1107 116E 11B6; # (붏; 붏; 붏; 붏; 붏; ) HANGUL SYLLABLE BULH +BD90;BD90;1107 116E 11B7;BD90;1107 116E 11B7; # (붐; 붐; 붐; 붐; 붐; ) HANGUL SYLLABLE BUM +BD91;BD91;1107 116E 11B8;BD91;1107 116E 11B8; # (붑; 붑; 붑; 붑; 붑; ) HANGUL SYLLABLE BUB +BD92;BD92;1107 116E 11B9;BD92;1107 116E 11B9; # (붒; 붒; 붒; 붒; 붒; ) HANGUL SYLLABLE BUBS +BD93;BD93;1107 116E 11BA;BD93;1107 116E 11BA; # (붓; 붓; 붓; 붓; 붓; ) HANGUL SYLLABLE BUS +BD94;BD94;1107 116E 11BB;BD94;1107 116E 11BB; # (붔; 붔; 붔; 붔; 붔; ) HANGUL SYLLABLE BUSS +BD95;BD95;1107 116E 11BC;BD95;1107 116E 11BC; # (붕; 붕; 붕; 붕; 붕; ) HANGUL SYLLABLE BUNG +BD96;BD96;1107 116E 11BD;BD96;1107 116E 11BD; # (붖; 붖; 붖; 붖; 붖; ) HANGUL SYLLABLE BUJ +BD97;BD97;1107 116E 11BE;BD97;1107 116E 11BE; # (붗; 붗; 붗; 붗; 붗; ) HANGUL SYLLABLE BUC +BD98;BD98;1107 116E 11BF;BD98;1107 116E 11BF; # (붘; 붘; 붘; 붘; 붘; ) HANGUL SYLLABLE BUK +BD99;BD99;1107 116E 11C0;BD99;1107 116E 11C0; # (붙; 붙; 붙; 붙; 붙; ) HANGUL SYLLABLE BUT +BD9A;BD9A;1107 116E 11C1;BD9A;1107 116E 11C1; # (붚; 붚; 붚; 붚; 붚; ) HANGUL SYLLABLE BUP +BD9B;BD9B;1107 116E 11C2;BD9B;1107 116E 11C2; # (붛; 붛; 붛; 붛; 붛; ) HANGUL SYLLABLE BUH +BD9C;BD9C;1107 116F;BD9C;1107 116F; # (붜; 붜; 붜; 붜; 붜; ) HANGUL SYLLABLE BWEO +BD9D;BD9D;1107 116F 11A8;BD9D;1107 116F 11A8; # (붝; 붝; 붝; 붝; 붝; ) HANGUL SYLLABLE BWEOG +BD9E;BD9E;1107 116F 11A9;BD9E;1107 116F 11A9; # (붞; 붞; 붞; 붞; 붞; ) HANGUL SYLLABLE BWEOGG +BD9F;BD9F;1107 116F 11AA;BD9F;1107 116F 11AA; # (붟; 붟; 붟; 붟; 붟; ) HANGUL SYLLABLE BWEOGS +BDA0;BDA0;1107 116F 11AB;BDA0;1107 116F 11AB; # (붠; 붠; 붠; 붠; 붠; ) HANGUL SYLLABLE BWEON +BDA1;BDA1;1107 116F 11AC;BDA1;1107 116F 11AC; # (붡; 붡; 붡; 붡; 붡; ) HANGUL SYLLABLE BWEONJ +BDA2;BDA2;1107 116F 11AD;BDA2;1107 116F 11AD; # (붢; 붢; 붢; 붢; 붢; ) HANGUL SYLLABLE BWEONH +BDA3;BDA3;1107 116F 11AE;BDA3;1107 116F 11AE; # (붣; 붣; 붣; 붣; 붣; ) HANGUL SYLLABLE BWEOD +BDA4;BDA4;1107 116F 11AF;BDA4;1107 116F 11AF; # (붤; 붤; 붤; 붤; 붤; ) HANGUL SYLLABLE BWEOL +BDA5;BDA5;1107 116F 11B0;BDA5;1107 116F 11B0; # (붥; 붥; 붥; 붥; 붥; ) HANGUL SYLLABLE BWEOLG +BDA6;BDA6;1107 116F 11B1;BDA6;1107 116F 11B1; # (붦; 붦; 붦; 붦; 붦; ) HANGUL SYLLABLE BWEOLM +BDA7;BDA7;1107 116F 11B2;BDA7;1107 116F 11B2; # (붧; 붧; 붧; 붧; 붧; ) HANGUL SYLLABLE BWEOLB +BDA8;BDA8;1107 116F 11B3;BDA8;1107 116F 11B3; # (붨; 붨; 붨; 붨; 붨; ) HANGUL SYLLABLE BWEOLS +BDA9;BDA9;1107 116F 11B4;BDA9;1107 116F 11B4; # (붩; 붩; 붩; 붩; 붩; ) HANGUL SYLLABLE BWEOLT +BDAA;BDAA;1107 116F 11B5;BDAA;1107 116F 11B5; # (붪; 붪; 붪; 붪; 붪; ) HANGUL SYLLABLE BWEOLP +BDAB;BDAB;1107 116F 11B6;BDAB;1107 116F 11B6; # (붫; 붫; 붫; 붫; 붫; ) HANGUL SYLLABLE BWEOLH +BDAC;BDAC;1107 116F 11B7;BDAC;1107 116F 11B7; # (붬; 붬; 붬; 붬; 붬; ) HANGUL SYLLABLE BWEOM +BDAD;BDAD;1107 116F 11B8;BDAD;1107 116F 11B8; # (붭; 붭; 붭; 붭; 붭; ) HANGUL SYLLABLE BWEOB +BDAE;BDAE;1107 116F 11B9;BDAE;1107 116F 11B9; # (붮; 붮; 붮; 붮; 붮; ) HANGUL SYLLABLE BWEOBS +BDAF;BDAF;1107 116F 11BA;BDAF;1107 116F 11BA; # (붯; 붯; 붯; 붯; 붯; ) HANGUL SYLLABLE BWEOS +BDB0;BDB0;1107 116F 11BB;BDB0;1107 116F 11BB; # (붰; 붰; 붰; 붰; 붰; ) HANGUL SYLLABLE BWEOSS +BDB1;BDB1;1107 116F 11BC;BDB1;1107 116F 11BC; # (붱; 붱; 붱; 붱; 붱; ) HANGUL SYLLABLE BWEONG +BDB2;BDB2;1107 116F 11BD;BDB2;1107 116F 11BD; # (붲; 붲; 붲; 붲; 붲; ) HANGUL SYLLABLE BWEOJ +BDB3;BDB3;1107 116F 11BE;BDB3;1107 116F 11BE; # (붳; 붳; 붳; 붳; 붳; ) HANGUL SYLLABLE BWEOC +BDB4;BDB4;1107 116F 11BF;BDB4;1107 116F 11BF; # (붴; 붴; 붴; 붴; 붴; ) HANGUL SYLLABLE BWEOK +BDB5;BDB5;1107 116F 11C0;BDB5;1107 116F 11C0; # (붵; 붵; 붵; 붵; 붵; ) HANGUL SYLLABLE BWEOT +BDB6;BDB6;1107 116F 11C1;BDB6;1107 116F 11C1; # (붶; 붶; 붶; 붶; 붶; ) HANGUL SYLLABLE BWEOP +BDB7;BDB7;1107 116F 11C2;BDB7;1107 116F 11C2; # (붷; 붷; 붷; 붷; 붷; ) HANGUL SYLLABLE BWEOH +BDB8;BDB8;1107 1170;BDB8;1107 1170; # (붸; 붸; 붸; 붸; 붸; ) HANGUL SYLLABLE BWE +BDB9;BDB9;1107 1170 11A8;BDB9;1107 1170 11A8; # (붹; 붹; 붹; 붹; 붹; ) HANGUL SYLLABLE BWEG +BDBA;BDBA;1107 1170 11A9;BDBA;1107 1170 11A9; # (붺; 붺; 붺; 붺; 붺; ) HANGUL SYLLABLE BWEGG +BDBB;BDBB;1107 1170 11AA;BDBB;1107 1170 11AA; # (붻; 붻; 붻; 붻; 붻; ) HANGUL SYLLABLE BWEGS +BDBC;BDBC;1107 1170 11AB;BDBC;1107 1170 11AB; # (붼; 붼; 붼; 붼; 붼; ) HANGUL SYLLABLE BWEN +BDBD;BDBD;1107 1170 11AC;BDBD;1107 1170 11AC; # (붽; 붽; 붽; 붽; 붽; ) HANGUL SYLLABLE BWENJ +BDBE;BDBE;1107 1170 11AD;BDBE;1107 1170 11AD; # (붾; 붾; 붾; 붾; 붾; ) HANGUL SYLLABLE BWENH +BDBF;BDBF;1107 1170 11AE;BDBF;1107 1170 11AE; # (붿; 붿; 붿; 붿; 붿; ) HANGUL SYLLABLE BWED +BDC0;BDC0;1107 1170 11AF;BDC0;1107 1170 11AF; # (뷀; 뷀; 뷀; 뷀; 뷀; ) HANGUL SYLLABLE BWEL +BDC1;BDC1;1107 1170 11B0;BDC1;1107 1170 11B0; # (뷁; 뷁; 뷁; 뷁; 뷁; ) HANGUL SYLLABLE BWELG +BDC2;BDC2;1107 1170 11B1;BDC2;1107 1170 11B1; # (뷂; 뷂; 뷂; 뷂; 뷂; ) HANGUL SYLLABLE BWELM +BDC3;BDC3;1107 1170 11B2;BDC3;1107 1170 11B2; # (뷃; 뷃; 뷃; 뷃; 뷃; ) HANGUL SYLLABLE BWELB +BDC4;BDC4;1107 1170 11B3;BDC4;1107 1170 11B3; # (뷄; 뷄; 뷄; 뷄; 뷄; ) HANGUL SYLLABLE BWELS +BDC5;BDC5;1107 1170 11B4;BDC5;1107 1170 11B4; # (뷅; 뷅; 뷅; 뷅; 뷅; ) HANGUL SYLLABLE BWELT +BDC6;BDC6;1107 1170 11B5;BDC6;1107 1170 11B5; # (뷆; 뷆; 뷆; 뷆; 뷆; ) HANGUL SYLLABLE BWELP +BDC7;BDC7;1107 1170 11B6;BDC7;1107 1170 11B6; # (뷇; 뷇; 뷇; 뷇; 뷇; ) HANGUL SYLLABLE BWELH +BDC8;BDC8;1107 1170 11B7;BDC8;1107 1170 11B7; # (뷈; 뷈; 뷈; 뷈; 뷈; ) HANGUL SYLLABLE BWEM +BDC9;BDC9;1107 1170 11B8;BDC9;1107 1170 11B8; # (뷉; 뷉; 뷉; 뷉; 뷉; ) HANGUL SYLLABLE BWEB +BDCA;BDCA;1107 1170 11B9;BDCA;1107 1170 11B9; # (뷊; 뷊; 뷊; 뷊; 뷊; ) HANGUL SYLLABLE BWEBS +BDCB;BDCB;1107 1170 11BA;BDCB;1107 1170 11BA; # (뷋; 뷋; 뷋; 뷋; 뷋; ) HANGUL SYLLABLE BWES +BDCC;BDCC;1107 1170 11BB;BDCC;1107 1170 11BB; # (뷌; 뷌; 뷌; 뷌; 뷌; ) HANGUL SYLLABLE BWESS +BDCD;BDCD;1107 1170 11BC;BDCD;1107 1170 11BC; # (뷍; 뷍; 뷍; 뷍; 뷍; ) HANGUL SYLLABLE BWENG +BDCE;BDCE;1107 1170 11BD;BDCE;1107 1170 11BD; # (뷎; 뷎; 뷎; 뷎; 뷎; ) HANGUL SYLLABLE BWEJ +BDCF;BDCF;1107 1170 11BE;BDCF;1107 1170 11BE; # (뷏; 뷏; 뷏; 뷏; 뷏; ) HANGUL SYLLABLE BWEC +BDD0;BDD0;1107 1170 11BF;BDD0;1107 1170 11BF; # (뷐; 뷐; 뷐; 뷐; 뷐; ) HANGUL SYLLABLE BWEK +BDD1;BDD1;1107 1170 11C0;BDD1;1107 1170 11C0; # (뷑; 뷑; 뷑; 뷑; 뷑; ) HANGUL SYLLABLE BWET +BDD2;BDD2;1107 1170 11C1;BDD2;1107 1170 11C1; # (뷒; 뷒; 뷒; 뷒; 뷒; ) HANGUL SYLLABLE BWEP +BDD3;BDD3;1107 1170 11C2;BDD3;1107 1170 11C2; # (뷓; 뷓; 뷓; 뷓; 뷓; ) HANGUL SYLLABLE BWEH +BDD4;BDD4;1107 1171;BDD4;1107 1171; # (뷔; 뷔; 뷔; 뷔; 뷔; ) HANGUL SYLLABLE BWI +BDD5;BDD5;1107 1171 11A8;BDD5;1107 1171 11A8; # (뷕; 뷕; 뷕; 뷕; 뷕; ) HANGUL SYLLABLE BWIG +BDD6;BDD6;1107 1171 11A9;BDD6;1107 1171 11A9; # (뷖; 뷖; 뷖; 뷖; 뷖; ) HANGUL SYLLABLE BWIGG +BDD7;BDD7;1107 1171 11AA;BDD7;1107 1171 11AA; # (뷗; 뷗; 뷗; 뷗; 뷗; ) HANGUL SYLLABLE BWIGS +BDD8;BDD8;1107 1171 11AB;BDD8;1107 1171 11AB; # (뷘; 뷘; 뷘; 뷘; 뷘; ) HANGUL SYLLABLE BWIN +BDD9;BDD9;1107 1171 11AC;BDD9;1107 1171 11AC; # (뷙; 뷙; 뷙; 뷙; 뷙; ) HANGUL SYLLABLE BWINJ +BDDA;BDDA;1107 1171 11AD;BDDA;1107 1171 11AD; # (뷚; 뷚; 뷚; 뷚; 뷚; ) HANGUL SYLLABLE BWINH +BDDB;BDDB;1107 1171 11AE;BDDB;1107 1171 11AE; # (뷛; 뷛; 뷛; 뷛; 뷛; ) HANGUL SYLLABLE BWID +BDDC;BDDC;1107 1171 11AF;BDDC;1107 1171 11AF; # (뷜; 뷜; 뷜; 뷜; 뷜; ) HANGUL SYLLABLE BWIL +BDDD;BDDD;1107 1171 11B0;BDDD;1107 1171 11B0; # (뷝; 뷝; 뷝; 뷝; 뷝; ) HANGUL SYLLABLE BWILG +BDDE;BDDE;1107 1171 11B1;BDDE;1107 1171 11B1; # (뷞; 뷞; 뷞; 뷞; 뷞; ) HANGUL SYLLABLE BWILM +BDDF;BDDF;1107 1171 11B2;BDDF;1107 1171 11B2; # (뷟; 뷟; 뷟; 뷟; 뷟; ) HANGUL SYLLABLE BWILB +BDE0;BDE0;1107 1171 11B3;BDE0;1107 1171 11B3; # (뷠; 뷠; 뷠; 뷠; 뷠; ) HANGUL SYLLABLE BWILS +BDE1;BDE1;1107 1171 11B4;BDE1;1107 1171 11B4; # (뷡; 뷡; 뷡; 뷡; 뷡; ) HANGUL SYLLABLE BWILT +BDE2;BDE2;1107 1171 11B5;BDE2;1107 1171 11B5; # (뷢; 뷢; 뷢; 뷢; 뷢; ) HANGUL SYLLABLE BWILP +BDE3;BDE3;1107 1171 11B6;BDE3;1107 1171 11B6; # (뷣; 뷣; 뷣; 뷣; 뷣; ) HANGUL SYLLABLE BWILH +BDE4;BDE4;1107 1171 11B7;BDE4;1107 1171 11B7; # (뷤; 뷤; 뷤; 뷤; 뷤; ) HANGUL SYLLABLE BWIM +BDE5;BDE5;1107 1171 11B8;BDE5;1107 1171 11B8; # (뷥; 뷥; 뷥; 뷥; 뷥; ) HANGUL SYLLABLE BWIB +BDE6;BDE6;1107 1171 11B9;BDE6;1107 1171 11B9; # (뷦; 뷦; 뷦; 뷦; 뷦; ) HANGUL SYLLABLE BWIBS +BDE7;BDE7;1107 1171 11BA;BDE7;1107 1171 11BA; # (뷧; 뷧; 뷧; 뷧; 뷧; ) HANGUL SYLLABLE BWIS +BDE8;BDE8;1107 1171 11BB;BDE8;1107 1171 11BB; # (뷨; 뷨; 뷨; 뷨; 뷨; ) HANGUL SYLLABLE BWISS +BDE9;BDE9;1107 1171 11BC;BDE9;1107 1171 11BC; # (뷩; 뷩; 뷩; 뷩; 뷩; ) HANGUL SYLLABLE BWING +BDEA;BDEA;1107 1171 11BD;BDEA;1107 1171 11BD; # (뷪; 뷪; 뷪; 뷪; 뷪; ) HANGUL SYLLABLE BWIJ +BDEB;BDEB;1107 1171 11BE;BDEB;1107 1171 11BE; # (뷫; 뷫; 뷫; 뷫; 뷫; ) HANGUL SYLLABLE BWIC +BDEC;BDEC;1107 1171 11BF;BDEC;1107 1171 11BF; # (뷬; 뷬; 뷬; 뷬; 뷬; ) HANGUL SYLLABLE BWIK +BDED;BDED;1107 1171 11C0;BDED;1107 1171 11C0; # (뷭; 뷭; 뷭; 뷭; 뷭; ) HANGUL SYLLABLE BWIT +BDEE;BDEE;1107 1171 11C1;BDEE;1107 1171 11C1; # (뷮; 뷮; 뷮; 뷮; 뷮; ) HANGUL SYLLABLE BWIP +BDEF;BDEF;1107 1171 11C2;BDEF;1107 1171 11C2; # (뷯; 뷯; 뷯; 뷯; 뷯; ) HANGUL SYLLABLE BWIH +BDF0;BDF0;1107 1172;BDF0;1107 1172; # (뷰; 뷰; 뷰; 뷰; 뷰; ) HANGUL SYLLABLE BYU +BDF1;BDF1;1107 1172 11A8;BDF1;1107 1172 11A8; # (뷱; 뷱; 뷱; 뷱; 뷱; ) HANGUL SYLLABLE BYUG +BDF2;BDF2;1107 1172 11A9;BDF2;1107 1172 11A9; # (뷲; 뷲; 뷲; 뷲; 뷲; ) HANGUL SYLLABLE BYUGG +BDF3;BDF3;1107 1172 11AA;BDF3;1107 1172 11AA; # (뷳; 뷳; 뷳; 뷳; 뷳; ) HANGUL SYLLABLE BYUGS +BDF4;BDF4;1107 1172 11AB;BDF4;1107 1172 11AB; # (뷴; 뷴; 뷴; 뷴; 뷴; ) HANGUL SYLLABLE BYUN +BDF5;BDF5;1107 1172 11AC;BDF5;1107 1172 11AC; # (뷵; 뷵; 뷵; 뷵; 뷵; ) HANGUL SYLLABLE BYUNJ +BDF6;BDF6;1107 1172 11AD;BDF6;1107 1172 11AD; # (뷶; 뷶; 뷶; 뷶; 뷶; ) HANGUL SYLLABLE BYUNH +BDF7;BDF7;1107 1172 11AE;BDF7;1107 1172 11AE; # (뷷; 뷷; 뷷; 뷷; 뷷; ) HANGUL SYLLABLE BYUD +BDF8;BDF8;1107 1172 11AF;BDF8;1107 1172 11AF; # (뷸; 뷸; 뷸; 뷸; 뷸; ) HANGUL SYLLABLE BYUL +BDF9;BDF9;1107 1172 11B0;BDF9;1107 1172 11B0; # (뷹; 뷹; 뷹; 뷹; 뷹; ) HANGUL SYLLABLE BYULG +BDFA;BDFA;1107 1172 11B1;BDFA;1107 1172 11B1; # (뷺; 뷺; 뷺; 뷺; 뷺; ) HANGUL SYLLABLE BYULM +BDFB;BDFB;1107 1172 11B2;BDFB;1107 1172 11B2; # (뷻; 뷻; 뷻; 뷻; 뷻; ) HANGUL SYLLABLE BYULB +BDFC;BDFC;1107 1172 11B3;BDFC;1107 1172 11B3; # (뷼; 뷼; 뷼; 뷼; 뷼; ) HANGUL SYLLABLE BYULS +BDFD;BDFD;1107 1172 11B4;BDFD;1107 1172 11B4; # (뷽; 뷽; 뷽; 뷽; 뷽; ) HANGUL SYLLABLE BYULT +BDFE;BDFE;1107 1172 11B5;BDFE;1107 1172 11B5; # (뷾; 뷾; 뷾; 뷾; 뷾; ) HANGUL SYLLABLE BYULP +BDFF;BDFF;1107 1172 11B6;BDFF;1107 1172 11B6; # (뷿; 뷿; 뷿; 뷿; 뷿; ) HANGUL SYLLABLE BYULH +BE00;BE00;1107 1172 11B7;BE00;1107 1172 11B7; # (븀; 븀; 븀; 븀; 븀; ) HANGUL SYLLABLE BYUM +BE01;BE01;1107 1172 11B8;BE01;1107 1172 11B8; # (븁; 븁; 븁; 븁; 븁; ) HANGUL SYLLABLE BYUB +BE02;BE02;1107 1172 11B9;BE02;1107 1172 11B9; # (븂; 븂; 븂; 븂; 븂; ) HANGUL SYLLABLE BYUBS +BE03;BE03;1107 1172 11BA;BE03;1107 1172 11BA; # (븃; 븃; 븃; 븃; 븃; ) HANGUL SYLLABLE BYUS +BE04;BE04;1107 1172 11BB;BE04;1107 1172 11BB; # (븄; 븄; 븄; 븄; 븄; ) HANGUL SYLLABLE BYUSS +BE05;BE05;1107 1172 11BC;BE05;1107 1172 11BC; # (븅; 븅; 븅; 븅; 븅; ) HANGUL SYLLABLE BYUNG +BE06;BE06;1107 1172 11BD;BE06;1107 1172 11BD; # (븆; 븆; 븆; 븆; 븆; ) HANGUL SYLLABLE BYUJ +BE07;BE07;1107 1172 11BE;BE07;1107 1172 11BE; # (븇; 븇; 븇; 븇; 븇; ) HANGUL SYLLABLE BYUC +BE08;BE08;1107 1172 11BF;BE08;1107 1172 11BF; # (븈; 븈; 븈; 븈; 븈; ) HANGUL SYLLABLE BYUK +BE09;BE09;1107 1172 11C0;BE09;1107 1172 11C0; # (븉; 븉; 븉; 븉; 븉; ) HANGUL SYLLABLE BYUT +BE0A;BE0A;1107 1172 11C1;BE0A;1107 1172 11C1; # (븊; 븊; 븊; 븊; 븊; ) HANGUL SYLLABLE BYUP +BE0B;BE0B;1107 1172 11C2;BE0B;1107 1172 11C2; # (븋; 븋; 븋; 븋; 븋; ) HANGUL SYLLABLE BYUH +BE0C;BE0C;1107 1173;BE0C;1107 1173; # (브; 브; 브; 브; 브; ) HANGUL SYLLABLE BEU +BE0D;BE0D;1107 1173 11A8;BE0D;1107 1173 11A8; # (븍; 븍; 븍; 븍; 븍; ) HANGUL SYLLABLE BEUG +BE0E;BE0E;1107 1173 11A9;BE0E;1107 1173 11A9; # (븎; 븎; 븎; 븎; 븎; ) HANGUL SYLLABLE BEUGG +BE0F;BE0F;1107 1173 11AA;BE0F;1107 1173 11AA; # (븏; 븏; 븏; 븏; 븏; ) HANGUL SYLLABLE BEUGS +BE10;BE10;1107 1173 11AB;BE10;1107 1173 11AB; # (븐; 븐; 븐; 븐; 븐; ) HANGUL SYLLABLE BEUN +BE11;BE11;1107 1173 11AC;BE11;1107 1173 11AC; # (븑; 븑; 븑; 븑; 븑; ) HANGUL SYLLABLE BEUNJ +BE12;BE12;1107 1173 11AD;BE12;1107 1173 11AD; # (븒; 븒; 븒; 븒; 븒; ) HANGUL SYLLABLE BEUNH +BE13;BE13;1107 1173 11AE;BE13;1107 1173 11AE; # (븓; 븓; 븓; 븓; 븓; ) HANGUL SYLLABLE BEUD +BE14;BE14;1107 1173 11AF;BE14;1107 1173 11AF; # (블; 블; 블; 블; 블; ) HANGUL SYLLABLE BEUL +BE15;BE15;1107 1173 11B0;BE15;1107 1173 11B0; # (븕; 븕; 븕; 븕; 븕; ) HANGUL SYLLABLE BEULG +BE16;BE16;1107 1173 11B1;BE16;1107 1173 11B1; # (븖; 븖; 븖; 븖; 븖; ) HANGUL SYLLABLE BEULM +BE17;BE17;1107 1173 11B2;BE17;1107 1173 11B2; # (븗; 븗; 븗; 븗; 븗; ) HANGUL SYLLABLE BEULB +BE18;BE18;1107 1173 11B3;BE18;1107 1173 11B3; # (븘; 븘; 븘; 븘; 븘; ) HANGUL SYLLABLE BEULS +BE19;BE19;1107 1173 11B4;BE19;1107 1173 11B4; # (븙; 븙; 븙; 븙; 븙; ) HANGUL SYLLABLE BEULT +BE1A;BE1A;1107 1173 11B5;BE1A;1107 1173 11B5; # (븚; 븚; 븚; 븚; 븚; ) HANGUL SYLLABLE BEULP +BE1B;BE1B;1107 1173 11B6;BE1B;1107 1173 11B6; # (븛; 븛; 븛; 븛; 븛; ) HANGUL SYLLABLE BEULH +BE1C;BE1C;1107 1173 11B7;BE1C;1107 1173 11B7; # (븜; 븜; 븜; 븜; 븜; ) HANGUL SYLLABLE BEUM +BE1D;BE1D;1107 1173 11B8;BE1D;1107 1173 11B8; # (븝; 븝; 븝; 븝; 븝; ) HANGUL SYLLABLE BEUB +BE1E;BE1E;1107 1173 11B9;BE1E;1107 1173 11B9; # (븞; 븞; 븞; 븞; 븞; ) HANGUL SYLLABLE BEUBS +BE1F;BE1F;1107 1173 11BA;BE1F;1107 1173 11BA; # (븟; 븟; 븟; 븟; 븟; ) HANGUL SYLLABLE BEUS +BE20;BE20;1107 1173 11BB;BE20;1107 1173 11BB; # (븠; 븠; 븠; 븠; 븠; ) HANGUL SYLLABLE BEUSS +BE21;BE21;1107 1173 11BC;BE21;1107 1173 11BC; # (븡; 븡; 븡; 븡; 븡; ) HANGUL SYLLABLE BEUNG +BE22;BE22;1107 1173 11BD;BE22;1107 1173 11BD; # (븢; 븢; 븢; 븢; 븢; ) HANGUL SYLLABLE BEUJ +BE23;BE23;1107 1173 11BE;BE23;1107 1173 11BE; # (븣; 븣; 븣; 븣; 븣; ) HANGUL SYLLABLE BEUC +BE24;BE24;1107 1173 11BF;BE24;1107 1173 11BF; # (븤; 븤; 븤; 븤; 븤; ) HANGUL SYLLABLE BEUK +BE25;BE25;1107 1173 11C0;BE25;1107 1173 11C0; # (븥; 븥; 븥; 븥; 븥; ) HANGUL SYLLABLE BEUT +BE26;BE26;1107 1173 11C1;BE26;1107 1173 11C1; # (븦; 븦; 븦; 븦; 븦; ) HANGUL SYLLABLE BEUP +BE27;BE27;1107 1173 11C2;BE27;1107 1173 11C2; # (븧; 븧; 븧; 븧; 븧; ) HANGUL SYLLABLE BEUH +BE28;BE28;1107 1174;BE28;1107 1174; # (븨; 븨; 븨; 븨; 븨; ) HANGUL SYLLABLE BYI +BE29;BE29;1107 1174 11A8;BE29;1107 1174 11A8; # (븩; 븩; 븩; 븩; 븩; ) HANGUL SYLLABLE BYIG +BE2A;BE2A;1107 1174 11A9;BE2A;1107 1174 11A9; # (븪; 븪; 븪; 븪; 븪; ) HANGUL SYLLABLE BYIGG +BE2B;BE2B;1107 1174 11AA;BE2B;1107 1174 11AA; # (븫; 븫; 븫; 븫; 븫; ) HANGUL SYLLABLE BYIGS +BE2C;BE2C;1107 1174 11AB;BE2C;1107 1174 11AB; # (븬; 븬; 븬; 븬; 븬; ) HANGUL SYLLABLE BYIN +BE2D;BE2D;1107 1174 11AC;BE2D;1107 1174 11AC; # (븭; 븭; 븭; 븭; 븭; ) HANGUL SYLLABLE BYINJ +BE2E;BE2E;1107 1174 11AD;BE2E;1107 1174 11AD; # (븮; 븮; 븮; 븮; 븮; ) HANGUL SYLLABLE BYINH +BE2F;BE2F;1107 1174 11AE;BE2F;1107 1174 11AE; # (븯; 븯; 븯; 븯; 븯; ) HANGUL SYLLABLE BYID +BE30;BE30;1107 1174 11AF;BE30;1107 1174 11AF; # (븰; 븰; 븰; 븰; 븰; ) HANGUL SYLLABLE BYIL +BE31;BE31;1107 1174 11B0;BE31;1107 1174 11B0; # (븱; 븱; 븱; 븱; 븱; ) HANGUL SYLLABLE BYILG +BE32;BE32;1107 1174 11B1;BE32;1107 1174 11B1; # (븲; 븲; 븲; 븲; 븲; ) HANGUL SYLLABLE BYILM +BE33;BE33;1107 1174 11B2;BE33;1107 1174 11B2; # (븳; 븳; 븳; 븳; 븳; ) HANGUL SYLLABLE BYILB +BE34;BE34;1107 1174 11B3;BE34;1107 1174 11B3; # (븴; 븴; 븴; 븴; 븴; ) HANGUL SYLLABLE BYILS +BE35;BE35;1107 1174 11B4;BE35;1107 1174 11B4; # (븵; 븵; 븵; 븵; 븵; ) HANGUL SYLLABLE BYILT +BE36;BE36;1107 1174 11B5;BE36;1107 1174 11B5; # (븶; 븶; 븶; 븶; 븶; ) HANGUL SYLLABLE BYILP +BE37;BE37;1107 1174 11B6;BE37;1107 1174 11B6; # (븷; 븷; 븷; 븷; 븷; ) HANGUL SYLLABLE BYILH +BE38;BE38;1107 1174 11B7;BE38;1107 1174 11B7; # (븸; 븸; 븸; 븸; 븸; ) HANGUL SYLLABLE BYIM +BE39;BE39;1107 1174 11B8;BE39;1107 1174 11B8; # (븹; 븹; 븹; 븹; 븹; ) HANGUL SYLLABLE BYIB +BE3A;BE3A;1107 1174 11B9;BE3A;1107 1174 11B9; # (븺; 븺; 븺; 븺; 븺; ) HANGUL SYLLABLE BYIBS +BE3B;BE3B;1107 1174 11BA;BE3B;1107 1174 11BA; # (븻; 븻; 븻; 븻; 븻; ) HANGUL SYLLABLE BYIS +BE3C;BE3C;1107 1174 11BB;BE3C;1107 1174 11BB; # (븼; 븼; 븼; 븼; 븼; ) HANGUL SYLLABLE BYISS +BE3D;BE3D;1107 1174 11BC;BE3D;1107 1174 11BC; # (븽; 븽; 븽; 븽; 븽; ) HANGUL SYLLABLE BYING +BE3E;BE3E;1107 1174 11BD;BE3E;1107 1174 11BD; # (븾; 븾; 븾; 븾; 븾; ) HANGUL SYLLABLE BYIJ +BE3F;BE3F;1107 1174 11BE;BE3F;1107 1174 11BE; # (븿; 븿; 븿; 븿; 븿; ) HANGUL SYLLABLE BYIC +BE40;BE40;1107 1174 11BF;BE40;1107 1174 11BF; # (빀; 빀; 빀; 빀; 빀; ) HANGUL SYLLABLE BYIK +BE41;BE41;1107 1174 11C0;BE41;1107 1174 11C0; # (빁; 빁; 빁; 빁; 빁; ) HANGUL SYLLABLE BYIT +BE42;BE42;1107 1174 11C1;BE42;1107 1174 11C1; # (빂; 빂; 빂; 빂; 빂; ) HANGUL SYLLABLE BYIP +BE43;BE43;1107 1174 11C2;BE43;1107 1174 11C2; # (빃; 빃; 빃; 빃; 빃; ) HANGUL SYLLABLE BYIH +BE44;BE44;1107 1175;BE44;1107 1175; # (비; 비; 비; 비; 비; ) HANGUL SYLLABLE BI +BE45;BE45;1107 1175 11A8;BE45;1107 1175 11A8; # (빅; 빅; 빅; 빅; 빅; ) HANGUL SYLLABLE BIG +BE46;BE46;1107 1175 11A9;BE46;1107 1175 11A9; # (빆; 빆; 빆; 빆; 빆; ) HANGUL SYLLABLE BIGG +BE47;BE47;1107 1175 11AA;BE47;1107 1175 11AA; # (빇; 빇; 빇; 빇; 빇; ) HANGUL SYLLABLE BIGS +BE48;BE48;1107 1175 11AB;BE48;1107 1175 11AB; # (빈; 빈; 빈; 빈; 빈; ) HANGUL SYLLABLE BIN +BE49;BE49;1107 1175 11AC;BE49;1107 1175 11AC; # (빉; 빉; 빉; 빉; 빉; ) HANGUL SYLLABLE BINJ +BE4A;BE4A;1107 1175 11AD;BE4A;1107 1175 11AD; # (빊; 빊; 빊; 빊; 빊; ) HANGUL SYLLABLE BINH +BE4B;BE4B;1107 1175 11AE;BE4B;1107 1175 11AE; # (빋; 빋; 빋; 빋; 빋; ) HANGUL SYLLABLE BID +BE4C;BE4C;1107 1175 11AF;BE4C;1107 1175 11AF; # (빌; 빌; 빌; 빌; 빌; ) HANGUL SYLLABLE BIL +BE4D;BE4D;1107 1175 11B0;BE4D;1107 1175 11B0; # (빍; 빍; 빍; 빍; 빍; ) HANGUL SYLLABLE BILG +BE4E;BE4E;1107 1175 11B1;BE4E;1107 1175 11B1; # (빎; 빎; 빎; 빎; 빎; ) HANGUL SYLLABLE BILM +BE4F;BE4F;1107 1175 11B2;BE4F;1107 1175 11B2; # (빏; 빏; 빏; 빏; 빏; ) HANGUL SYLLABLE BILB +BE50;BE50;1107 1175 11B3;BE50;1107 1175 11B3; # (빐; 빐; 빐; 빐; 빐; ) HANGUL SYLLABLE BILS +BE51;BE51;1107 1175 11B4;BE51;1107 1175 11B4; # (빑; 빑; 빑; 빑; 빑; ) HANGUL SYLLABLE BILT +BE52;BE52;1107 1175 11B5;BE52;1107 1175 11B5; # (빒; 빒; 빒; 빒; 빒; ) HANGUL SYLLABLE BILP +BE53;BE53;1107 1175 11B6;BE53;1107 1175 11B6; # (빓; 빓; 빓; 빓; 빓; ) HANGUL SYLLABLE BILH +BE54;BE54;1107 1175 11B7;BE54;1107 1175 11B7; # (빔; 빔; 빔; 빔; 빔; ) HANGUL SYLLABLE BIM +BE55;BE55;1107 1175 11B8;BE55;1107 1175 11B8; # (빕; 빕; 빕; 빕; 빕; ) HANGUL SYLLABLE BIB +BE56;BE56;1107 1175 11B9;BE56;1107 1175 11B9; # (빖; 빖; 빖; 빖; 빖; ) HANGUL SYLLABLE BIBS +BE57;BE57;1107 1175 11BA;BE57;1107 1175 11BA; # (빗; 빗; 빗; 빗; 빗; ) HANGUL SYLLABLE BIS +BE58;BE58;1107 1175 11BB;BE58;1107 1175 11BB; # (빘; 빘; 빘; 빘; 빘; ) HANGUL SYLLABLE BISS +BE59;BE59;1107 1175 11BC;BE59;1107 1175 11BC; # (빙; 빙; 빙; 빙; 빙; ) HANGUL SYLLABLE BING +BE5A;BE5A;1107 1175 11BD;BE5A;1107 1175 11BD; # (빚; 빚; 빚; 빚; 빚; ) HANGUL SYLLABLE BIJ +BE5B;BE5B;1107 1175 11BE;BE5B;1107 1175 11BE; # (빛; 빛; 빛; 빛; 빛; ) HANGUL SYLLABLE BIC +BE5C;BE5C;1107 1175 11BF;BE5C;1107 1175 11BF; # (빜; 빜; 빜; 빜; 빜; ) HANGUL SYLLABLE BIK +BE5D;BE5D;1107 1175 11C0;BE5D;1107 1175 11C0; # (빝; 빝; 빝; 빝; 빝; ) HANGUL SYLLABLE BIT +BE5E;BE5E;1107 1175 11C1;BE5E;1107 1175 11C1; # (빞; 빞; 빞; 빞; 빞; ) HANGUL SYLLABLE BIP +BE5F;BE5F;1107 1175 11C2;BE5F;1107 1175 11C2; # (빟; 빟; 빟; 빟; 빟; ) HANGUL SYLLABLE BIH +BE60;BE60;1108 1161;BE60;1108 1161; # (빠; 빠; 빠; 빠; 빠; ) HANGUL SYLLABLE BBA +BE61;BE61;1108 1161 11A8;BE61;1108 1161 11A8; # (빡; 빡; 빡; 빡; 빡; ) HANGUL SYLLABLE BBAG +BE62;BE62;1108 1161 11A9;BE62;1108 1161 11A9; # (빢; 빢; 빢; 빢; 빢; ) HANGUL SYLLABLE BBAGG +BE63;BE63;1108 1161 11AA;BE63;1108 1161 11AA; # (빣; 빣; 빣; 빣; 빣; ) HANGUL SYLLABLE BBAGS +BE64;BE64;1108 1161 11AB;BE64;1108 1161 11AB; # (빤; 빤; 빤; 빤; 빤; ) HANGUL SYLLABLE BBAN +BE65;BE65;1108 1161 11AC;BE65;1108 1161 11AC; # (빥; 빥; 빥; 빥; 빥; ) HANGUL SYLLABLE BBANJ +BE66;BE66;1108 1161 11AD;BE66;1108 1161 11AD; # (빦; 빦; 빦; 빦; 빦; ) HANGUL SYLLABLE BBANH +BE67;BE67;1108 1161 11AE;BE67;1108 1161 11AE; # (빧; 빧; 빧; 빧; 빧; ) HANGUL SYLLABLE BBAD +BE68;BE68;1108 1161 11AF;BE68;1108 1161 11AF; # (빨; 빨; 빨; 빨; 빨; ) HANGUL SYLLABLE BBAL +BE69;BE69;1108 1161 11B0;BE69;1108 1161 11B0; # (빩; 빩; 빩; 빩; 빩; ) HANGUL SYLLABLE BBALG +BE6A;BE6A;1108 1161 11B1;BE6A;1108 1161 11B1; # (빪; 빪; 빪; 빪; 빪; ) HANGUL SYLLABLE BBALM +BE6B;BE6B;1108 1161 11B2;BE6B;1108 1161 11B2; # (빫; 빫; 빫; 빫; 빫; ) HANGUL SYLLABLE BBALB +BE6C;BE6C;1108 1161 11B3;BE6C;1108 1161 11B3; # (빬; 빬; 빬; 빬; 빬; ) HANGUL SYLLABLE BBALS +BE6D;BE6D;1108 1161 11B4;BE6D;1108 1161 11B4; # (빭; 빭; 빭; 빭; 빭; ) HANGUL SYLLABLE BBALT +BE6E;BE6E;1108 1161 11B5;BE6E;1108 1161 11B5; # (빮; 빮; 빮; 빮; 빮; ) HANGUL SYLLABLE BBALP +BE6F;BE6F;1108 1161 11B6;BE6F;1108 1161 11B6; # (빯; 빯; 빯; 빯; 빯; ) HANGUL SYLLABLE BBALH +BE70;BE70;1108 1161 11B7;BE70;1108 1161 11B7; # (빰; 빰; 빰; 빰; 빰; ) HANGUL SYLLABLE BBAM +BE71;BE71;1108 1161 11B8;BE71;1108 1161 11B8; # (빱; 빱; 빱; 빱; 빱; ) HANGUL SYLLABLE BBAB +BE72;BE72;1108 1161 11B9;BE72;1108 1161 11B9; # (빲; 빲; 빲; 빲; 빲; ) HANGUL SYLLABLE BBABS +BE73;BE73;1108 1161 11BA;BE73;1108 1161 11BA; # (빳; 빳; 빳; 빳; 빳; ) HANGUL SYLLABLE BBAS +BE74;BE74;1108 1161 11BB;BE74;1108 1161 11BB; # (빴; 빴; 빴; 빴; 빴; ) HANGUL SYLLABLE BBASS +BE75;BE75;1108 1161 11BC;BE75;1108 1161 11BC; # (빵; 빵; 빵; 빵; 빵; ) HANGUL SYLLABLE BBANG +BE76;BE76;1108 1161 11BD;BE76;1108 1161 11BD; # (빶; 빶; 빶; 빶; 빶; ) HANGUL SYLLABLE BBAJ +BE77;BE77;1108 1161 11BE;BE77;1108 1161 11BE; # (빷; 빷; 빷; 빷; 빷; ) HANGUL SYLLABLE BBAC +BE78;BE78;1108 1161 11BF;BE78;1108 1161 11BF; # (빸; 빸; 빸; 빸; 빸; ) HANGUL SYLLABLE BBAK +BE79;BE79;1108 1161 11C0;BE79;1108 1161 11C0; # (빹; 빹; 빹; 빹; 빹; ) HANGUL SYLLABLE BBAT +BE7A;BE7A;1108 1161 11C1;BE7A;1108 1161 11C1; # (빺; 빺; 빺; 빺; 빺; ) HANGUL SYLLABLE BBAP +BE7B;BE7B;1108 1161 11C2;BE7B;1108 1161 11C2; # (빻; 빻; 빻; 빻; 빻; ) HANGUL SYLLABLE BBAH +BE7C;BE7C;1108 1162;BE7C;1108 1162; # (빼; 빼; 빼; 빼; 빼; ) HANGUL SYLLABLE BBAE +BE7D;BE7D;1108 1162 11A8;BE7D;1108 1162 11A8; # (빽; 빽; 빽; 빽; 빽; ) HANGUL SYLLABLE BBAEG +BE7E;BE7E;1108 1162 11A9;BE7E;1108 1162 11A9; # (빾; 빾; 빾; 빾; 빾; ) HANGUL SYLLABLE BBAEGG +BE7F;BE7F;1108 1162 11AA;BE7F;1108 1162 11AA; # (빿; 빿; 빿; 빿; 빿; ) HANGUL SYLLABLE BBAEGS +BE80;BE80;1108 1162 11AB;BE80;1108 1162 11AB; # (뺀; 뺀; 뺀; 뺀; 뺀; ) HANGUL SYLLABLE BBAEN +BE81;BE81;1108 1162 11AC;BE81;1108 1162 11AC; # (뺁; 뺁; 뺁; 뺁; 뺁; ) HANGUL SYLLABLE BBAENJ +BE82;BE82;1108 1162 11AD;BE82;1108 1162 11AD; # (뺂; 뺂; 뺂; 뺂; 뺂; ) HANGUL SYLLABLE BBAENH +BE83;BE83;1108 1162 11AE;BE83;1108 1162 11AE; # (뺃; 뺃; 뺃; 뺃; 뺃; ) HANGUL SYLLABLE BBAED +BE84;BE84;1108 1162 11AF;BE84;1108 1162 11AF; # (뺄; 뺄; 뺄; 뺄; 뺄; ) HANGUL SYLLABLE BBAEL +BE85;BE85;1108 1162 11B0;BE85;1108 1162 11B0; # (뺅; 뺅; 뺅; 뺅; 뺅; ) HANGUL SYLLABLE BBAELG +BE86;BE86;1108 1162 11B1;BE86;1108 1162 11B1; # (뺆; 뺆; 뺆; 뺆; 뺆; ) HANGUL SYLLABLE BBAELM +BE87;BE87;1108 1162 11B2;BE87;1108 1162 11B2; # (뺇; 뺇; 뺇; 뺇; 뺇; ) HANGUL SYLLABLE BBAELB +BE88;BE88;1108 1162 11B3;BE88;1108 1162 11B3; # (뺈; 뺈; 뺈; 뺈; 뺈; ) HANGUL SYLLABLE BBAELS +BE89;BE89;1108 1162 11B4;BE89;1108 1162 11B4; # (뺉; 뺉; 뺉; 뺉; 뺉; ) HANGUL SYLLABLE BBAELT +BE8A;BE8A;1108 1162 11B5;BE8A;1108 1162 11B5; # (뺊; 뺊; 뺊; 뺊; 뺊; ) HANGUL SYLLABLE BBAELP +BE8B;BE8B;1108 1162 11B6;BE8B;1108 1162 11B6; # (뺋; 뺋; 뺋; 뺋; 뺋; ) HANGUL SYLLABLE BBAELH +BE8C;BE8C;1108 1162 11B7;BE8C;1108 1162 11B7; # (뺌; 뺌; 뺌; 뺌; 뺌; ) HANGUL SYLLABLE BBAEM +BE8D;BE8D;1108 1162 11B8;BE8D;1108 1162 11B8; # (뺍; 뺍; 뺍; 뺍; 뺍; ) HANGUL SYLLABLE BBAEB +BE8E;BE8E;1108 1162 11B9;BE8E;1108 1162 11B9; # (뺎; 뺎; 뺎; 뺎; 뺎; ) HANGUL SYLLABLE BBAEBS +BE8F;BE8F;1108 1162 11BA;BE8F;1108 1162 11BA; # (뺏; 뺏; 뺏; 뺏; 뺏; ) HANGUL SYLLABLE BBAES +BE90;BE90;1108 1162 11BB;BE90;1108 1162 11BB; # (뺐; 뺐; 뺐; 뺐; 뺐; ) HANGUL SYLLABLE BBAESS +BE91;BE91;1108 1162 11BC;BE91;1108 1162 11BC; # (뺑; 뺑; 뺑; 뺑; 뺑; ) HANGUL SYLLABLE BBAENG +BE92;BE92;1108 1162 11BD;BE92;1108 1162 11BD; # (뺒; 뺒; 뺒; 뺒; 뺒; ) HANGUL SYLLABLE BBAEJ +BE93;BE93;1108 1162 11BE;BE93;1108 1162 11BE; # (뺓; 뺓; 뺓; 뺓; 뺓; ) HANGUL SYLLABLE BBAEC +BE94;BE94;1108 1162 11BF;BE94;1108 1162 11BF; # (뺔; 뺔; 뺔; 뺔; 뺔; ) HANGUL SYLLABLE BBAEK +BE95;BE95;1108 1162 11C0;BE95;1108 1162 11C0; # (뺕; 뺕; 뺕; 뺕; 뺕; ) HANGUL SYLLABLE BBAET +BE96;BE96;1108 1162 11C1;BE96;1108 1162 11C1; # (뺖; 뺖; 뺖; 뺖; 뺖; ) HANGUL SYLLABLE BBAEP +BE97;BE97;1108 1162 11C2;BE97;1108 1162 11C2; # (뺗; 뺗; 뺗; 뺗; 뺗; ) HANGUL SYLLABLE BBAEH +BE98;BE98;1108 1163;BE98;1108 1163; # (뺘; 뺘; 뺘; 뺘; 뺘; ) HANGUL SYLLABLE BBYA +BE99;BE99;1108 1163 11A8;BE99;1108 1163 11A8; # (뺙; 뺙; 뺙; 뺙; 뺙; ) HANGUL SYLLABLE BBYAG +BE9A;BE9A;1108 1163 11A9;BE9A;1108 1163 11A9; # (뺚; 뺚; 뺚; 뺚; 뺚; ) HANGUL SYLLABLE BBYAGG +BE9B;BE9B;1108 1163 11AA;BE9B;1108 1163 11AA; # (뺛; 뺛; 뺛; 뺛; 뺛; ) HANGUL SYLLABLE BBYAGS +BE9C;BE9C;1108 1163 11AB;BE9C;1108 1163 11AB; # (뺜; 뺜; 뺜; 뺜; 뺜; ) HANGUL SYLLABLE BBYAN +BE9D;BE9D;1108 1163 11AC;BE9D;1108 1163 11AC; # (뺝; 뺝; 뺝; 뺝; 뺝; ) HANGUL SYLLABLE BBYANJ +BE9E;BE9E;1108 1163 11AD;BE9E;1108 1163 11AD; # (뺞; 뺞; 뺞; 뺞; 뺞; ) HANGUL SYLLABLE BBYANH +BE9F;BE9F;1108 1163 11AE;BE9F;1108 1163 11AE; # (뺟; 뺟; 뺟; 뺟; 뺟; ) HANGUL SYLLABLE BBYAD +BEA0;BEA0;1108 1163 11AF;BEA0;1108 1163 11AF; # (뺠; 뺠; 뺠; 뺠; 뺠; ) HANGUL SYLLABLE BBYAL +BEA1;BEA1;1108 1163 11B0;BEA1;1108 1163 11B0; # (뺡; 뺡; 뺡; 뺡; 뺡; ) HANGUL SYLLABLE BBYALG +BEA2;BEA2;1108 1163 11B1;BEA2;1108 1163 11B1; # (뺢; 뺢; 뺢; 뺢; 뺢; ) HANGUL SYLLABLE BBYALM +BEA3;BEA3;1108 1163 11B2;BEA3;1108 1163 11B2; # (뺣; 뺣; 뺣; 뺣; 뺣; ) HANGUL SYLLABLE BBYALB +BEA4;BEA4;1108 1163 11B3;BEA4;1108 1163 11B3; # (뺤; 뺤; 뺤; 뺤; 뺤; ) HANGUL SYLLABLE BBYALS +BEA5;BEA5;1108 1163 11B4;BEA5;1108 1163 11B4; # (뺥; 뺥; 뺥; 뺥; 뺥; ) HANGUL SYLLABLE BBYALT +BEA6;BEA6;1108 1163 11B5;BEA6;1108 1163 11B5; # (뺦; 뺦; 뺦; 뺦; 뺦; ) HANGUL SYLLABLE BBYALP +BEA7;BEA7;1108 1163 11B6;BEA7;1108 1163 11B6; # (뺧; 뺧; 뺧; 뺧; 뺧; ) HANGUL SYLLABLE BBYALH +BEA8;BEA8;1108 1163 11B7;BEA8;1108 1163 11B7; # (뺨; 뺨; 뺨; 뺨; 뺨; ) HANGUL SYLLABLE BBYAM +BEA9;BEA9;1108 1163 11B8;BEA9;1108 1163 11B8; # (뺩; 뺩; 뺩; 뺩; 뺩; ) HANGUL SYLLABLE BBYAB +BEAA;BEAA;1108 1163 11B9;BEAA;1108 1163 11B9; # (뺪; 뺪; 뺪; 뺪; 뺪; ) HANGUL SYLLABLE BBYABS +BEAB;BEAB;1108 1163 11BA;BEAB;1108 1163 11BA; # (뺫; 뺫; 뺫; 뺫; 뺫; ) HANGUL SYLLABLE BBYAS +BEAC;BEAC;1108 1163 11BB;BEAC;1108 1163 11BB; # (뺬; 뺬; 뺬; 뺬; 뺬; ) HANGUL SYLLABLE BBYASS +BEAD;BEAD;1108 1163 11BC;BEAD;1108 1163 11BC; # (뺭; 뺭; 뺭; 뺭; 뺭; ) HANGUL SYLLABLE BBYANG +BEAE;BEAE;1108 1163 11BD;BEAE;1108 1163 11BD; # (뺮; 뺮; 뺮; 뺮; 뺮; ) HANGUL SYLLABLE BBYAJ +BEAF;BEAF;1108 1163 11BE;BEAF;1108 1163 11BE; # (뺯; 뺯; 뺯; 뺯; 뺯; ) HANGUL SYLLABLE BBYAC +BEB0;BEB0;1108 1163 11BF;BEB0;1108 1163 11BF; # (뺰; 뺰; 뺰; 뺰; 뺰; ) HANGUL SYLLABLE BBYAK +BEB1;BEB1;1108 1163 11C0;BEB1;1108 1163 11C0; # (뺱; 뺱; 뺱; 뺱; 뺱; ) HANGUL SYLLABLE BBYAT +BEB2;BEB2;1108 1163 11C1;BEB2;1108 1163 11C1; # (뺲; 뺲; 뺲; 뺲; 뺲; ) HANGUL SYLLABLE BBYAP +BEB3;BEB3;1108 1163 11C2;BEB3;1108 1163 11C2; # (뺳; 뺳; 뺳; 뺳; 뺳; ) HANGUL SYLLABLE BBYAH +BEB4;BEB4;1108 1164;BEB4;1108 1164; # (뺴; 뺴; 뺴; 뺴; 뺴; ) HANGUL SYLLABLE BBYAE +BEB5;BEB5;1108 1164 11A8;BEB5;1108 1164 11A8; # (뺵; 뺵; 뺵; 뺵; 뺵; ) HANGUL SYLLABLE BBYAEG +BEB6;BEB6;1108 1164 11A9;BEB6;1108 1164 11A9; # (뺶; 뺶; 뺶; 뺶; 뺶; ) HANGUL SYLLABLE BBYAEGG +BEB7;BEB7;1108 1164 11AA;BEB7;1108 1164 11AA; # (뺷; 뺷; 뺷; 뺷; 뺷; ) HANGUL SYLLABLE BBYAEGS +BEB8;BEB8;1108 1164 11AB;BEB8;1108 1164 11AB; # (뺸; 뺸; 뺸; 뺸; 뺸; ) HANGUL SYLLABLE BBYAEN +BEB9;BEB9;1108 1164 11AC;BEB9;1108 1164 11AC; # (뺹; 뺹; 뺹; 뺹; 뺹; ) HANGUL SYLLABLE BBYAENJ +BEBA;BEBA;1108 1164 11AD;BEBA;1108 1164 11AD; # (뺺; 뺺; 뺺; 뺺; 뺺; ) HANGUL SYLLABLE BBYAENH +BEBB;BEBB;1108 1164 11AE;BEBB;1108 1164 11AE; # (뺻; 뺻; 뺻; 뺻; 뺻; ) HANGUL SYLLABLE BBYAED +BEBC;BEBC;1108 1164 11AF;BEBC;1108 1164 11AF; # (뺼; 뺼; 뺼; 뺼; 뺼; ) HANGUL SYLLABLE BBYAEL +BEBD;BEBD;1108 1164 11B0;BEBD;1108 1164 11B0; # (뺽; 뺽; 뺽; 뺽; 뺽; ) HANGUL SYLLABLE BBYAELG +BEBE;BEBE;1108 1164 11B1;BEBE;1108 1164 11B1; # (뺾; 뺾; 뺾; 뺾; 뺾; ) HANGUL SYLLABLE BBYAELM +BEBF;BEBF;1108 1164 11B2;BEBF;1108 1164 11B2; # (뺿; 뺿; 뺿; 뺿; 뺿; ) HANGUL SYLLABLE BBYAELB +BEC0;BEC0;1108 1164 11B3;BEC0;1108 1164 11B3; # (뻀; 뻀; 뻀; 뻀; 뻀; ) HANGUL SYLLABLE BBYAELS +BEC1;BEC1;1108 1164 11B4;BEC1;1108 1164 11B4; # (뻁; 뻁; 뻁; 뻁; 뻁; ) HANGUL SYLLABLE BBYAELT +BEC2;BEC2;1108 1164 11B5;BEC2;1108 1164 11B5; # (뻂; 뻂; 뻂; 뻂; 뻂; ) HANGUL SYLLABLE BBYAELP +BEC3;BEC3;1108 1164 11B6;BEC3;1108 1164 11B6; # (뻃; 뻃; 뻃; 뻃; 뻃; ) HANGUL SYLLABLE BBYAELH +BEC4;BEC4;1108 1164 11B7;BEC4;1108 1164 11B7; # (뻄; 뻄; 뻄; 뻄; 뻄; ) HANGUL SYLLABLE BBYAEM +BEC5;BEC5;1108 1164 11B8;BEC5;1108 1164 11B8; # (뻅; 뻅; 뻅; 뻅; 뻅; ) HANGUL SYLLABLE BBYAEB +BEC6;BEC6;1108 1164 11B9;BEC6;1108 1164 11B9; # (뻆; 뻆; 뻆; 뻆; 뻆; ) HANGUL SYLLABLE BBYAEBS +BEC7;BEC7;1108 1164 11BA;BEC7;1108 1164 11BA; # (뻇; 뻇; 뻇; 뻇; 뻇; ) HANGUL SYLLABLE BBYAES +BEC8;BEC8;1108 1164 11BB;BEC8;1108 1164 11BB; # (뻈; 뻈; 뻈; 뻈; 뻈; ) HANGUL SYLLABLE BBYAESS +BEC9;BEC9;1108 1164 11BC;BEC9;1108 1164 11BC; # (뻉; 뻉; 뻉; 뻉; 뻉; ) HANGUL SYLLABLE BBYAENG +BECA;BECA;1108 1164 11BD;BECA;1108 1164 11BD; # (뻊; 뻊; 뻊; 뻊; 뻊; ) HANGUL SYLLABLE BBYAEJ +BECB;BECB;1108 1164 11BE;BECB;1108 1164 11BE; # (뻋; 뻋; 뻋; 뻋; 뻋; ) HANGUL SYLLABLE BBYAEC +BECC;BECC;1108 1164 11BF;BECC;1108 1164 11BF; # (뻌; 뻌; 뻌; 뻌; 뻌; ) HANGUL SYLLABLE BBYAEK +BECD;BECD;1108 1164 11C0;BECD;1108 1164 11C0; # (뻍; 뻍; 뻍; 뻍; 뻍; ) HANGUL SYLLABLE BBYAET +BECE;BECE;1108 1164 11C1;BECE;1108 1164 11C1; # (뻎; 뻎; 뻎; 뻎; 뻎; ) HANGUL SYLLABLE BBYAEP +BECF;BECF;1108 1164 11C2;BECF;1108 1164 11C2; # (뻏; 뻏; 뻏; 뻏; 뻏; ) HANGUL SYLLABLE BBYAEH +BED0;BED0;1108 1165;BED0;1108 1165; # (뻐; 뻐; 뻐; 뻐; 뻐; ) HANGUL SYLLABLE BBEO +BED1;BED1;1108 1165 11A8;BED1;1108 1165 11A8; # (뻑; 뻑; 뻑; 뻑; 뻑; ) HANGUL SYLLABLE BBEOG +BED2;BED2;1108 1165 11A9;BED2;1108 1165 11A9; # (뻒; 뻒; 뻒; 뻒; 뻒; ) HANGUL SYLLABLE BBEOGG +BED3;BED3;1108 1165 11AA;BED3;1108 1165 11AA; # (뻓; 뻓; 뻓; 뻓; 뻓; ) HANGUL SYLLABLE BBEOGS +BED4;BED4;1108 1165 11AB;BED4;1108 1165 11AB; # (뻔; 뻔; 뻔; 뻔; 뻔; ) HANGUL SYLLABLE BBEON +BED5;BED5;1108 1165 11AC;BED5;1108 1165 11AC; # (뻕; 뻕; 뻕; 뻕; 뻕; ) HANGUL SYLLABLE BBEONJ +BED6;BED6;1108 1165 11AD;BED6;1108 1165 11AD; # (뻖; 뻖; 뻖; 뻖; 뻖; ) HANGUL SYLLABLE BBEONH +BED7;BED7;1108 1165 11AE;BED7;1108 1165 11AE; # (뻗; 뻗; 뻗; 뻗; 뻗; ) HANGUL SYLLABLE BBEOD +BED8;BED8;1108 1165 11AF;BED8;1108 1165 11AF; # (뻘; 뻘; 뻘; 뻘; 뻘; ) HANGUL SYLLABLE BBEOL +BED9;BED9;1108 1165 11B0;BED9;1108 1165 11B0; # (뻙; 뻙; 뻙; 뻙; 뻙; ) HANGUL SYLLABLE BBEOLG +BEDA;BEDA;1108 1165 11B1;BEDA;1108 1165 11B1; # (뻚; 뻚; 뻚; 뻚; 뻚; ) HANGUL SYLLABLE BBEOLM +BEDB;BEDB;1108 1165 11B2;BEDB;1108 1165 11B2; # (뻛; 뻛; 뻛; 뻛; 뻛; ) HANGUL SYLLABLE BBEOLB +BEDC;BEDC;1108 1165 11B3;BEDC;1108 1165 11B3; # (뻜; 뻜; 뻜; 뻜; 뻜; ) HANGUL SYLLABLE BBEOLS +BEDD;BEDD;1108 1165 11B4;BEDD;1108 1165 11B4; # (뻝; 뻝; 뻝; 뻝; 뻝; ) HANGUL SYLLABLE BBEOLT +BEDE;BEDE;1108 1165 11B5;BEDE;1108 1165 11B5; # (뻞; 뻞; 뻞; 뻞; 뻞; ) HANGUL SYLLABLE BBEOLP +BEDF;BEDF;1108 1165 11B6;BEDF;1108 1165 11B6; # (뻟; 뻟; 뻟; 뻟; 뻟; ) HANGUL SYLLABLE BBEOLH +BEE0;BEE0;1108 1165 11B7;BEE0;1108 1165 11B7; # (뻠; 뻠; 뻠; 뻠; 뻠; ) HANGUL SYLLABLE BBEOM +BEE1;BEE1;1108 1165 11B8;BEE1;1108 1165 11B8; # (뻡; 뻡; 뻡; 뻡; 뻡; ) HANGUL SYLLABLE BBEOB +BEE2;BEE2;1108 1165 11B9;BEE2;1108 1165 11B9; # (뻢; 뻢; 뻢; 뻢; 뻢; ) HANGUL SYLLABLE BBEOBS +BEE3;BEE3;1108 1165 11BA;BEE3;1108 1165 11BA; # (뻣; 뻣; 뻣; 뻣; 뻣; ) HANGUL SYLLABLE BBEOS +BEE4;BEE4;1108 1165 11BB;BEE4;1108 1165 11BB; # (뻤; 뻤; 뻤; 뻤; 뻤; ) HANGUL SYLLABLE BBEOSS +BEE5;BEE5;1108 1165 11BC;BEE5;1108 1165 11BC; # (뻥; 뻥; 뻥; 뻥; 뻥; ) HANGUL SYLLABLE BBEONG +BEE6;BEE6;1108 1165 11BD;BEE6;1108 1165 11BD; # (뻦; 뻦; 뻦; 뻦; 뻦; ) HANGUL SYLLABLE BBEOJ +BEE7;BEE7;1108 1165 11BE;BEE7;1108 1165 11BE; # (뻧; 뻧; 뻧; 뻧; 뻧; ) HANGUL SYLLABLE BBEOC +BEE8;BEE8;1108 1165 11BF;BEE8;1108 1165 11BF; # (뻨; 뻨; 뻨; 뻨; 뻨; ) HANGUL SYLLABLE BBEOK +BEE9;BEE9;1108 1165 11C0;BEE9;1108 1165 11C0; # (뻩; 뻩; 뻩; 뻩; 뻩; ) HANGUL SYLLABLE BBEOT +BEEA;BEEA;1108 1165 11C1;BEEA;1108 1165 11C1; # (뻪; 뻪; 뻪; 뻪; 뻪; ) HANGUL SYLLABLE BBEOP +BEEB;BEEB;1108 1165 11C2;BEEB;1108 1165 11C2; # (뻫; 뻫; 뻫; 뻫; 뻫; ) HANGUL SYLLABLE BBEOH +BEEC;BEEC;1108 1166;BEEC;1108 1166; # (뻬; 뻬; 뻬; 뻬; 뻬; ) HANGUL SYLLABLE BBE +BEED;BEED;1108 1166 11A8;BEED;1108 1166 11A8; # (뻭; 뻭; 뻭; 뻭; 뻭; ) HANGUL SYLLABLE BBEG +BEEE;BEEE;1108 1166 11A9;BEEE;1108 1166 11A9; # (뻮; 뻮; 뻮; 뻮; 뻮; ) HANGUL SYLLABLE BBEGG +BEEF;BEEF;1108 1166 11AA;BEEF;1108 1166 11AA; # (뻯; 뻯; 뻯; 뻯; 뻯; ) HANGUL SYLLABLE BBEGS +BEF0;BEF0;1108 1166 11AB;BEF0;1108 1166 11AB; # (뻰; 뻰; 뻰; 뻰; 뻰; ) HANGUL SYLLABLE BBEN +BEF1;BEF1;1108 1166 11AC;BEF1;1108 1166 11AC; # (뻱; 뻱; 뻱; 뻱; 뻱; ) HANGUL SYLLABLE BBENJ +BEF2;BEF2;1108 1166 11AD;BEF2;1108 1166 11AD; # (뻲; 뻲; 뻲; 뻲; 뻲; ) HANGUL SYLLABLE BBENH +BEF3;BEF3;1108 1166 11AE;BEF3;1108 1166 11AE; # (뻳; 뻳; 뻳; 뻳; 뻳; ) HANGUL SYLLABLE BBED +BEF4;BEF4;1108 1166 11AF;BEF4;1108 1166 11AF; # (뻴; 뻴; 뻴; 뻴; 뻴; ) HANGUL SYLLABLE BBEL +BEF5;BEF5;1108 1166 11B0;BEF5;1108 1166 11B0; # (뻵; 뻵; 뻵; 뻵; 뻵; ) HANGUL SYLLABLE BBELG +BEF6;BEF6;1108 1166 11B1;BEF6;1108 1166 11B1; # (뻶; 뻶; 뻶; 뻶; 뻶; ) HANGUL SYLLABLE BBELM +BEF7;BEF7;1108 1166 11B2;BEF7;1108 1166 11B2; # (뻷; 뻷; 뻷; 뻷; 뻷; ) HANGUL SYLLABLE BBELB +BEF8;BEF8;1108 1166 11B3;BEF8;1108 1166 11B3; # (뻸; 뻸; 뻸; 뻸; 뻸; ) HANGUL SYLLABLE BBELS +BEF9;BEF9;1108 1166 11B4;BEF9;1108 1166 11B4; # (뻹; 뻹; 뻹; 뻹; 뻹; ) HANGUL SYLLABLE BBELT +BEFA;BEFA;1108 1166 11B5;BEFA;1108 1166 11B5; # (뻺; 뻺; 뻺; 뻺; 뻺; ) HANGUL SYLLABLE BBELP +BEFB;BEFB;1108 1166 11B6;BEFB;1108 1166 11B6; # (뻻; 뻻; 뻻; 뻻; 뻻; ) HANGUL SYLLABLE BBELH +BEFC;BEFC;1108 1166 11B7;BEFC;1108 1166 11B7; # (뻼; 뻼; 뻼; 뻼; 뻼; ) HANGUL SYLLABLE BBEM +BEFD;BEFD;1108 1166 11B8;BEFD;1108 1166 11B8; # (뻽; 뻽; 뻽; 뻽; 뻽; ) HANGUL SYLLABLE BBEB +BEFE;BEFE;1108 1166 11B9;BEFE;1108 1166 11B9; # (뻾; 뻾; 뻾; 뻾; 뻾; ) HANGUL SYLLABLE BBEBS +BEFF;BEFF;1108 1166 11BA;BEFF;1108 1166 11BA; # (뻿; 뻿; 뻿; 뻿; 뻿; ) HANGUL SYLLABLE BBES +BF00;BF00;1108 1166 11BB;BF00;1108 1166 11BB; # (뼀; 뼀; 뼀; 뼀; 뼀; ) HANGUL SYLLABLE BBESS +BF01;BF01;1108 1166 11BC;BF01;1108 1166 11BC; # (뼁; 뼁; 뼁; 뼁; 뼁; ) HANGUL SYLLABLE BBENG +BF02;BF02;1108 1166 11BD;BF02;1108 1166 11BD; # (뼂; 뼂; 뼂; 뼂; 뼂; ) HANGUL SYLLABLE BBEJ +BF03;BF03;1108 1166 11BE;BF03;1108 1166 11BE; # (뼃; 뼃; 뼃; 뼃; 뼃; ) HANGUL SYLLABLE BBEC +BF04;BF04;1108 1166 11BF;BF04;1108 1166 11BF; # (뼄; 뼄; 뼄; 뼄; 뼄; ) HANGUL SYLLABLE BBEK +BF05;BF05;1108 1166 11C0;BF05;1108 1166 11C0; # (뼅; 뼅; 뼅; 뼅; 뼅; ) HANGUL SYLLABLE BBET +BF06;BF06;1108 1166 11C1;BF06;1108 1166 11C1; # (뼆; 뼆; 뼆; 뼆; 뼆; ) HANGUL SYLLABLE BBEP +BF07;BF07;1108 1166 11C2;BF07;1108 1166 11C2; # (뼇; 뼇; 뼇; 뼇; 뼇; ) HANGUL SYLLABLE BBEH +BF08;BF08;1108 1167;BF08;1108 1167; # (뼈; 뼈; 뼈; 뼈; 뼈; ) HANGUL SYLLABLE BBYEO +BF09;BF09;1108 1167 11A8;BF09;1108 1167 11A8; # (뼉; 뼉; 뼉; 뼉; 뼉; ) HANGUL SYLLABLE BBYEOG +BF0A;BF0A;1108 1167 11A9;BF0A;1108 1167 11A9; # (뼊; 뼊; 뼊; 뼊; 뼊; ) HANGUL SYLLABLE BBYEOGG +BF0B;BF0B;1108 1167 11AA;BF0B;1108 1167 11AA; # (뼋; 뼋; 뼋; 뼋; 뼋; ) HANGUL SYLLABLE BBYEOGS +BF0C;BF0C;1108 1167 11AB;BF0C;1108 1167 11AB; # (뼌; 뼌; 뼌; 뼌; 뼌; ) HANGUL SYLLABLE BBYEON +BF0D;BF0D;1108 1167 11AC;BF0D;1108 1167 11AC; # (뼍; 뼍; 뼍; 뼍; 뼍; ) HANGUL SYLLABLE BBYEONJ +BF0E;BF0E;1108 1167 11AD;BF0E;1108 1167 11AD; # (뼎; 뼎; 뼎; 뼎; 뼎; ) HANGUL SYLLABLE BBYEONH +BF0F;BF0F;1108 1167 11AE;BF0F;1108 1167 11AE; # (뼏; 뼏; 뼏; 뼏; 뼏; ) HANGUL SYLLABLE BBYEOD +BF10;BF10;1108 1167 11AF;BF10;1108 1167 11AF; # (뼐; 뼐; 뼐; 뼐; 뼐; ) HANGUL SYLLABLE BBYEOL +BF11;BF11;1108 1167 11B0;BF11;1108 1167 11B0; # (뼑; 뼑; 뼑; 뼑; 뼑; ) HANGUL SYLLABLE BBYEOLG +BF12;BF12;1108 1167 11B1;BF12;1108 1167 11B1; # (뼒; 뼒; 뼒; 뼒; 뼒; ) HANGUL SYLLABLE BBYEOLM +BF13;BF13;1108 1167 11B2;BF13;1108 1167 11B2; # (뼓; 뼓; 뼓; 뼓; 뼓; ) HANGUL SYLLABLE BBYEOLB +BF14;BF14;1108 1167 11B3;BF14;1108 1167 11B3; # (뼔; 뼔; 뼔; 뼔; 뼔; ) HANGUL SYLLABLE BBYEOLS +BF15;BF15;1108 1167 11B4;BF15;1108 1167 11B4; # (뼕; 뼕; 뼕; 뼕; 뼕; ) HANGUL SYLLABLE BBYEOLT +BF16;BF16;1108 1167 11B5;BF16;1108 1167 11B5; # (뼖; 뼖; 뼖; 뼖; 뼖; ) HANGUL SYLLABLE BBYEOLP +BF17;BF17;1108 1167 11B6;BF17;1108 1167 11B6; # (뼗; 뼗; 뼗; 뼗; 뼗; ) HANGUL SYLLABLE BBYEOLH +BF18;BF18;1108 1167 11B7;BF18;1108 1167 11B7; # (뼘; 뼘; 뼘; 뼘; 뼘; ) HANGUL SYLLABLE BBYEOM +BF19;BF19;1108 1167 11B8;BF19;1108 1167 11B8; # (뼙; 뼙; 뼙; 뼙; 뼙; ) HANGUL SYLLABLE BBYEOB +BF1A;BF1A;1108 1167 11B9;BF1A;1108 1167 11B9; # (뼚; 뼚; 뼚; 뼚; 뼚; ) HANGUL SYLLABLE BBYEOBS +BF1B;BF1B;1108 1167 11BA;BF1B;1108 1167 11BA; # (뼛; 뼛; 뼛; 뼛; 뼛; ) HANGUL SYLLABLE BBYEOS +BF1C;BF1C;1108 1167 11BB;BF1C;1108 1167 11BB; # (뼜; 뼜; 뼜; 뼜; 뼜; ) HANGUL SYLLABLE BBYEOSS +BF1D;BF1D;1108 1167 11BC;BF1D;1108 1167 11BC; # (뼝; 뼝; 뼝; 뼝; 뼝; ) HANGUL SYLLABLE BBYEONG +BF1E;BF1E;1108 1167 11BD;BF1E;1108 1167 11BD; # (뼞; 뼞; 뼞; 뼞; 뼞; ) HANGUL SYLLABLE BBYEOJ +BF1F;BF1F;1108 1167 11BE;BF1F;1108 1167 11BE; # (뼟; 뼟; 뼟; 뼟; 뼟; ) HANGUL SYLLABLE BBYEOC +BF20;BF20;1108 1167 11BF;BF20;1108 1167 11BF; # (뼠; 뼠; 뼠; 뼠; 뼠; ) HANGUL SYLLABLE BBYEOK +BF21;BF21;1108 1167 11C0;BF21;1108 1167 11C0; # (뼡; 뼡; 뼡; 뼡; 뼡; ) HANGUL SYLLABLE BBYEOT +BF22;BF22;1108 1167 11C1;BF22;1108 1167 11C1; # (뼢; 뼢; 뼢; 뼢; 뼢; ) HANGUL SYLLABLE BBYEOP +BF23;BF23;1108 1167 11C2;BF23;1108 1167 11C2; # (뼣; 뼣; 뼣; 뼣; 뼣; ) HANGUL SYLLABLE BBYEOH +BF24;BF24;1108 1168;BF24;1108 1168; # (뼤; 뼤; 뼤; 뼤; 뼤; ) HANGUL SYLLABLE BBYE +BF25;BF25;1108 1168 11A8;BF25;1108 1168 11A8; # (뼥; 뼥; 뼥; 뼥; 뼥; ) HANGUL SYLLABLE BBYEG +BF26;BF26;1108 1168 11A9;BF26;1108 1168 11A9; # (뼦; 뼦; 뼦; 뼦; 뼦; ) HANGUL SYLLABLE BBYEGG +BF27;BF27;1108 1168 11AA;BF27;1108 1168 11AA; # (뼧; 뼧; 뼧; 뼧; 뼧; ) HANGUL SYLLABLE BBYEGS +BF28;BF28;1108 1168 11AB;BF28;1108 1168 11AB; # (뼨; 뼨; 뼨; 뼨; 뼨; ) HANGUL SYLLABLE BBYEN +BF29;BF29;1108 1168 11AC;BF29;1108 1168 11AC; # (뼩; 뼩; 뼩; 뼩; 뼩; ) HANGUL SYLLABLE BBYENJ +BF2A;BF2A;1108 1168 11AD;BF2A;1108 1168 11AD; # (뼪; 뼪; 뼪; 뼪; 뼪; ) HANGUL SYLLABLE BBYENH +BF2B;BF2B;1108 1168 11AE;BF2B;1108 1168 11AE; # (뼫; 뼫; 뼫; 뼫; 뼫; ) HANGUL SYLLABLE BBYED +BF2C;BF2C;1108 1168 11AF;BF2C;1108 1168 11AF; # (뼬; 뼬; 뼬; 뼬; 뼬; ) HANGUL SYLLABLE BBYEL +BF2D;BF2D;1108 1168 11B0;BF2D;1108 1168 11B0; # (뼭; 뼭; 뼭; 뼭; 뼭; ) HANGUL SYLLABLE BBYELG +BF2E;BF2E;1108 1168 11B1;BF2E;1108 1168 11B1; # (뼮; 뼮; 뼮; 뼮; 뼮; ) HANGUL SYLLABLE BBYELM +BF2F;BF2F;1108 1168 11B2;BF2F;1108 1168 11B2; # (뼯; 뼯; 뼯; 뼯; 뼯; ) HANGUL SYLLABLE BBYELB +BF30;BF30;1108 1168 11B3;BF30;1108 1168 11B3; # (뼰; 뼰; 뼰; 뼰; 뼰; ) HANGUL SYLLABLE BBYELS +BF31;BF31;1108 1168 11B4;BF31;1108 1168 11B4; # (뼱; 뼱; 뼱; 뼱; 뼱; ) HANGUL SYLLABLE BBYELT +BF32;BF32;1108 1168 11B5;BF32;1108 1168 11B5; # (뼲; 뼲; 뼲; 뼲; 뼲; ) HANGUL SYLLABLE BBYELP +BF33;BF33;1108 1168 11B6;BF33;1108 1168 11B6; # (뼳; 뼳; 뼳; 뼳; 뼳; ) HANGUL SYLLABLE BBYELH +BF34;BF34;1108 1168 11B7;BF34;1108 1168 11B7; # (뼴; 뼴; 뼴; 뼴; 뼴; ) HANGUL SYLLABLE BBYEM +BF35;BF35;1108 1168 11B8;BF35;1108 1168 11B8; # (뼵; 뼵; 뼵; 뼵; 뼵; ) HANGUL SYLLABLE BBYEB +BF36;BF36;1108 1168 11B9;BF36;1108 1168 11B9; # (뼶; 뼶; 뼶; 뼶; 뼶; ) HANGUL SYLLABLE BBYEBS +BF37;BF37;1108 1168 11BA;BF37;1108 1168 11BA; # (뼷; 뼷; 뼷; 뼷; 뼷; ) HANGUL SYLLABLE BBYES +BF38;BF38;1108 1168 11BB;BF38;1108 1168 11BB; # (뼸; 뼸; 뼸; 뼸; 뼸; ) HANGUL SYLLABLE BBYESS +BF39;BF39;1108 1168 11BC;BF39;1108 1168 11BC; # (뼹; 뼹; 뼹; 뼹; 뼹; ) HANGUL SYLLABLE BBYENG +BF3A;BF3A;1108 1168 11BD;BF3A;1108 1168 11BD; # (뼺; 뼺; 뼺; 뼺; 뼺; ) HANGUL SYLLABLE BBYEJ +BF3B;BF3B;1108 1168 11BE;BF3B;1108 1168 11BE; # (뼻; 뼻; 뼻; 뼻; 뼻; ) HANGUL SYLLABLE BBYEC +BF3C;BF3C;1108 1168 11BF;BF3C;1108 1168 11BF; # (뼼; 뼼; 뼼; 뼼; 뼼; ) HANGUL SYLLABLE BBYEK +BF3D;BF3D;1108 1168 11C0;BF3D;1108 1168 11C0; # (뼽; 뼽; 뼽; 뼽; 뼽; ) HANGUL SYLLABLE BBYET +BF3E;BF3E;1108 1168 11C1;BF3E;1108 1168 11C1; # (뼾; 뼾; 뼾; 뼾; 뼾; ) HANGUL SYLLABLE BBYEP +BF3F;BF3F;1108 1168 11C2;BF3F;1108 1168 11C2; # (뼿; 뼿; 뼿; 뼿; 뼿; ) HANGUL SYLLABLE BBYEH +BF40;BF40;1108 1169;BF40;1108 1169; # (뽀; 뽀; 뽀; 뽀; 뽀; ) HANGUL SYLLABLE BBO +BF41;BF41;1108 1169 11A8;BF41;1108 1169 11A8; # (뽁; 뽁; 뽁; 뽁; 뽁; ) HANGUL SYLLABLE BBOG +BF42;BF42;1108 1169 11A9;BF42;1108 1169 11A9; # (뽂; 뽂; 뽂; 뽂; 뽂; ) HANGUL SYLLABLE BBOGG +BF43;BF43;1108 1169 11AA;BF43;1108 1169 11AA; # (뽃; 뽃; 뽃; 뽃; 뽃; ) HANGUL SYLLABLE BBOGS +BF44;BF44;1108 1169 11AB;BF44;1108 1169 11AB; # (뽄; 뽄; 뽄; 뽄; 뽄; ) HANGUL SYLLABLE BBON +BF45;BF45;1108 1169 11AC;BF45;1108 1169 11AC; # (뽅; 뽅; 뽅; 뽅; 뽅; ) HANGUL SYLLABLE BBONJ +BF46;BF46;1108 1169 11AD;BF46;1108 1169 11AD; # (뽆; 뽆; 뽆; 뽆; 뽆; ) HANGUL SYLLABLE BBONH +BF47;BF47;1108 1169 11AE;BF47;1108 1169 11AE; # (뽇; 뽇; 뽇; 뽇; 뽇; ) HANGUL SYLLABLE BBOD +BF48;BF48;1108 1169 11AF;BF48;1108 1169 11AF; # (뽈; 뽈; 뽈; 뽈; 뽈; ) HANGUL SYLLABLE BBOL +BF49;BF49;1108 1169 11B0;BF49;1108 1169 11B0; # (뽉; 뽉; 뽉; 뽉; 뽉; ) HANGUL SYLLABLE BBOLG +BF4A;BF4A;1108 1169 11B1;BF4A;1108 1169 11B1; # (뽊; 뽊; 뽊; 뽊; 뽊; ) HANGUL SYLLABLE BBOLM +BF4B;BF4B;1108 1169 11B2;BF4B;1108 1169 11B2; # (뽋; 뽋; 뽋; 뽋; 뽋; ) HANGUL SYLLABLE BBOLB +BF4C;BF4C;1108 1169 11B3;BF4C;1108 1169 11B3; # (뽌; 뽌; 뽌; 뽌; 뽌; ) HANGUL SYLLABLE BBOLS +BF4D;BF4D;1108 1169 11B4;BF4D;1108 1169 11B4; # (뽍; 뽍; 뽍; 뽍; 뽍; ) HANGUL SYLLABLE BBOLT +BF4E;BF4E;1108 1169 11B5;BF4E;1108 1169 11B5; # (뽎; 뽎; 뽎; 뽎; 뽎; ) HANGUL SYLLABLE BBOLP +BF4F;BF4F;1108 1169 11B6;BF4F;1108 1169 11B6; # (뽏; 뽏; 뽏; 뽏; 뽏; ) HANGUL SYLLABLE BBOLH +BF50;BF50;1108 1169 11B7;BF50;1108 1169 11B7; # (뽐; 뽐; 뽐; 뽐; 뽐; ) HANGUL SYLLABLE BBOM +BF51;BF51;1108 1169 11B8;BF51;1108 1169 11B8; # (뽑; 뽑; 뽑; 뽑; 뽑; ) HANGUL SYLLABLE BBOB +BF52;BF52;1108 1169 11B9;BF52;1108 1169 11B9; # (뽒; 뽒; 뽒; 뽒; 뽒; ) HANGUL SYLLABLE BBOBS +BF53;BF53;1108 1169 11BA;BF53;1108 1169 11BA; # (뽓; 뽓; 뽓; 뽓; 뽓; ) HANGUL SYLLABLE BBOS +BF54;BF54;1108 1169 11BB;BF54;1108 1169 11BB; # (뽔; 뽔; 뽔; 뽔; 뽔; ) HANGUL SYLLABLE BBOSS +BF55;BF55;1108 1169 11BC;BF55;1108 1169 11BC; # (뽕; 뽕; 뽕; 뽕; 뽕; ) HANGUL SYLLABLE BBONG +BF56;BF56;1108 1169 11BD;BF56;1108 1169 11BD; # (뽖; 뽖; 뽖; 뽖; 뽖; ) HANGUL SYLLABLE BBOJ +BF57;BF57;1108 1169 11BE;BF57;1108 1169 11BE; # (뽗; 뽗; 뽗; 뽗; 뽗; ) HANGUL SYLLABLE BBOC +BF58;BF58;1108 1169 11BF;BF58;1108 1169 11BF; # (뽘; 뽘; 뽘; 뽘; 뽘; ) HANGUL SYLLABLE BBOK +BF59;BF59;1108 1169 11C0;BF59;1108 1169 11C0; # (뽙; 뽙; 뽙; 뽙; 뽙; ) HANGUL SYLLABLE BBOT +BF5A;BF5A;1108 1169 11C1;BF5A;1108 1169 11C1; # (뽚; 뽚; 뽚; 뽚; 뽚; ) HANGUL SYLLABLE BBOP +BF5B;BF5B;1108 1169 11C2;BF5B;1108 1169 11C2; # (뽛; 뽛; 뽛; 뽛; 뽛; ) HANGUL SYLLABLE BBOH +BF5C;BF5C;1108 116A;BF5C;1108 116A; # (뽜; 뽜; 뽜; 뽜; 뽜; ) HANGUL SYLLABLE BBWA +BF5D;BF5D;1108 116A 11A8;BF5D;1108 116A 11A8; # (뽝; 뽝; 뽝; 뽝; 뽝; ) HANGUL SYLLABLE BBWAG +BF5E;BF5E;1108 116A 11A9;BF5E;1108 116A 11A9; # (뽞; 뽞; 뽞; 뽞; 뽞; ) HANGUL SYLLABLE BBWAGG +BF5F;BF5F;1108 116A 11AA;BF5F;1108 116A 11AA; # (뽟; 뽟; 뽟; 뽟; 뽟; ) HANGUL SYLLABLE BBWAGS +BF60;BF60;1108 116A 11AB;BF60;1108 116A 11AB; # (뽠; 뽠; 뽠; 뽠; 뽠; ) HANGUL SYLLABLE BBWAN +BF61;BF61;1108 116A 11AC;BF61;1108 116A 11AC; # (뽡; 뽡; 뽡; 뽡; 뽡; ) HANGUL SYLLABLE BBWANJ +BF62;BF62;1108 116A 11AD;BF62;1108 116A 11AD; # (뽢; 뽢; 뽢; 뽢; 뽢; ) HANGUL SYLLABLE BBWANH +BF63;BF63;1108 116A 11AE;BF63;1108 116A 11AE; # (뽣; 뽣; 뽣; 뽣; 뽣; ) HANGUL SYLLABLE BBWAD +BF64;BF64;1108 116A 11AF;BF64;1108 116A 11AF; # (뽤; 뽤; 뽤; 뽤; 뽤; ) HANGUL SYLLABLE BBWAL +BF65;BF65;1108 116A 11B0;BF65;1108 116A 11B0; # (뽥; 뽥; 뽥; 뽥; 뽥; ) HANGUL SYLLABLE BBWALG +BF66;BF66;1108 116A 11B1;BF66;1108 116A 11B1; # (뽦; 뽦; 뽦; 뽦; 뽦; ) HANGUL SYLLABLE BBWALM +BF67;BF67;1108 116A 11B2;BF67;1108 116A 11B2; # (뽧; 뽧; 뽧; 뽧; 뽧; ) HANGUL SYLLABLE BBWALB +BF68;BF68;1108 116A 11B3;BF68;1108 116A 11B3; # (뽨; 뽨; 뽨; 뽨; 뽨; ) HANGUL SYLLABLE BBWALS +BF69;BF69;1108 116A 11B4;BF69;1108 116A 11B4; # (뽩; 뽩; 뽩; 뽩; 뽩; ) HANGUL SYLLABLE BBWALT +BF6A;BF6A;1108 116A 11B5;BF6A;1108 116A 11B5; # (뽪; 뽪; 뽪; 뽪; 뽪; ) HANGUL SYLLABLE BBWALP +BF6B;BF6B;1108 116A 11B6;BF6B;1108 116A 11B6; # (뽫; 뽫; 뽫; 뽫; 뽫; ) HANGUL SYLLABLE BBWALH +BF6C;BF6C;1108 116A 11B7;BF6C;1108 116A 11B7; # (뽬; 뽬; 뽬; 뽬; 뽬; ) HANGUL SYLLABLE BBWAM +BF6D;BF6D;1108 116A 11B8;BF6D;1108 116A 11B8; # (뽭; 뽭; 뽭; 뽭; 뽭; ) HANGUL SYLLABLE BBWAB +BF6E;BF6E;1108 116A 11B9;BF6E;1108 116A 11B9; # (뽮; 뽮; 뽮; 뽮; 뽮; ) HANGUL SYLLABLE BBWABS +BF6F;BF6F;1108 116A 11BA;BF6F;1108 116A 11BA; # (뽯; 뽯; 뽯; 뽯; 뽯; ) HANGUL SYLLABLE BBWAS +BF70;BF70;1108 116A 11BB;BF70;1108 116A 11BB; # (뽰; 뽰; 뽰; 뽰; 뽰; ) HANGUL SYLLABLE BBWASS +BF71;BF71;1108 116A 11BC;BF71;1108 116A 11BC; # (뽱; 뽱; 뽱; 뽱; 뽱; ) HANGUL SYLLABLE BBWANG +BF72;BF72;1108 116A 11BD;BF72;1108 116A 11BD; # (뽲; 뽲; 뽲; 뽲; 뽲; ) HANGUL SYLLABLE BBWAJ +BF73;BF73;1108 116A 11BE;BF73;1108 116A 11BE; # (뽳; 뽳; 뽳; 뽳; 뽳; ) HANGUL SYLLABLE BBWAC +BF74;BF74;1108 116A 11BF;BF74;1108 116A 11BF; # (뽴; 뽴; 뽴; 뽴; 뽴; ) HANGUL SYLLABLE BBWAK +BF75;BF75;1108 116A 11C0;BF75;1108 116A 11C0; # (뽵; 뽵; 뽵; 뽵; 뽵; ) HANGUL SYLLABLE BBWAT +BF76;BF76;1108 116A 11C1;BF76;1108 116A 11C1; # (뽶; 뽶; 뽶; 뽶; 뽶; ) HANGUL SYLLABLE BBWAP +BF77;BF77;1108 116A 11C2;BF77;1108 116A 11C2; # (뽷; 뽷; 뽷; 뽷; 뽷; ) HANGUL SYLLABLE BBWAH +BF78;BF78;1108 116B;BF78;1108 116B; # (뽸; 뽸; 뽸; 뽸; 뽸; ) HANGUL SYLLABLE BBWAE +BF79;BF79;1108 116B 11A8;BF79;1108 116B 11A8; # (뽹; 뽹; 뽹; 뽹; 뽹; ) HANGUL SYLLABLE BBWAEG +BF7A;BF7A;1108 116B 11A9;BF7A;1108 116B 11A9; # (뽺; 뽺; 뽺; 뽺; 뽺; ) HANGUL SYLLABLE BBWAEGG +BF7B;BF7B;1108 116B 11AA;BF7B;1108 116B 11AA; # (뽻; 뽻; 뽻; 뽻; 뽻; ) HANGUL SYLLABLE BBWAEGS +BF7C;BF7C;1108 116B 11AB;BF7C;1108 116B 11AB; # (뽼; 뽼; 뽼; 뽼; 뽼; ) HANGUL SYLLABLE BBWAEN +BF7D;BF7D;1108 116B 11AC;BF7D;1108 116B 11AC; # (뽽; 뽽; 뽽; 뽽; 뽽; ) HANGUL SYLLABLE BBWAENJ +BF7E;BF7E;1108 116B 11AD;BF7E;1108 116B 11AD; # (뽾; 뽾; 뽾; 뽾; 뽾; ) HANGUL SYLLABLE BBWAENH +BF7F;BF7F;1108 116B 11AE;BF7F;1108 116B 11AE; # (뽿; 뽿; 뽿; 뽿; 뽿; ) HANGUL SYLLABLE BBWAED +BF80;BF80;1108 116B 11AF;BF80;1108 116B 11AF; # (뾀; 뾀; 뾀; 뾀; 뾀; ) HANGUL SYLLABLE BBWAEL +BF81;BF81;1108 116B 11B0;BF81;1108 116B 11B0; # (뾁; 뾁; 뾁; 뾁; 뾁; ) HANGUL SYLLABLE BBWAELG +BF82;BF82;1108 116B 11B1;BF82;1108 116B 11B1; # (뾂; 뾂; 뾂; 뾂; 뾂; ) HANGUL SYLLABLE BBWAELM +BF83;BF83;1108 116B 11B2;BF83;1108 116B 11B2; # (뾃; 뾃; 뾃; 뾃; 뾃; ) HANGUL SYLLABLE BBWAELB +BF84;BF84;1108 116B 11B3;BF84;1108 116B 11B3; # (뾄; 뾄; 뾄; 뾄; 뾄; ) HANGUL SYLLABLE BBWAELS +BF85;BF85;1108 116B 11B4;BF85;1108 116B 11B4; # (뾅; 뾅; 뾅; 뾅; 뾅; ) HANGUL SYLLABLE BBWAELT +BF86;BF86;1108 116B 11B5;BF86;1108 116B 11B5; # (뾆; 뾆; 뾆; 뾆; 뾆; ) HANGUL SYLLABLE BBWAELP +BF87;BF87;1108 116B 11B6;BF87;1108 116B 11B6; # (뾇; 뾇; 뾇; 뾇; 뾇; ) HANGUL SYLLABLE BBWAELH +BF88;BF88;1108 116B 11B7;BF88;1108 116B 11B7; # (뾈; 뾈; 뾈; 뾈; 뾈; ) HANGUL SYLLABLE BBWAEM +BF89;BF89;1108 116B 11B8;BF89;1108 116B 11B8; # (뾉; 뾉; 뾉; 뾉; 뾉; ) HANGUL SYLLABLE BBWAEB +BF8A;BF8A;1108 116B 11B9;BF8A;1108 116B 11B9; # (뾊; 뾊; 뾊; 뾊; 뾊; ) HANGUL SYLLABLE BBWAEBS +BF8B;BF8B;1108 116B 11BA;BF8B;1108 116B 11BA; # (뾋; 뾋; 뾋; 뾋; 뾋; ) HANGUL SYLLABLE BBWAES +BF8C;BF8C;1108 116B 11BB;BF8C;1108 116B 11BB; # (뾌; 뾌; 뾌; 뾌; 뾌; ) HANGUL SYLLABLE BBWAESS +BF8D;BF8D;1108 116B 11BC;BF8D;1108 116B 11BC; # (뾍; 뾍; 뾍; 뾍; 뾍; ) HANGUL SYLLABLE BBWAENG +BF8E;BF8E;1108 116B 11BD;BF8E;1108 116B 11BD; # (뾎; 뾎; 뾎; 뾎; 뾎; ) HANGUL SYLLABLE BBWAEJ +BF8F;BF8F;1108 116B 11BE;BF8F;1108 116B 11BE; # (뾏; 뾏; 뾏; 뾏; 뾏; ) HANGUL SYLLABLE BBWAEC +BF90;BF90;1108 116B 11BF;BF90;1108 116B 11BF; # (뾐; 뾐; 뾐; 뾐; 뾐; ) HANGUL SYLLABLE BBWAEK +BF91;BF91;1108 116B 11C0;BF91;1108 116B 11C0; # (뾑; 뾑; 뾑; 뾑; 뾑; ) HANGUL SYLLABLE BBWAET +BF92;BF92;1108 116B 11C1;BF92;1108 116B 11C1; # (뾒; 뾒; 뾒; 뾒; 뾒; ) HANGUL SYLLABLE BBWAEP +BF93;BF93;1108 116B 11C2;BF93;1108 116B 11C2; # (뾓; 뾓; 뾓; 뾓; 뾓; ) HANGUL SYLLABLE BBWAEH +BF94;BF94;1108 116C;BF94;1108 116C; # (뾔; 뾔; 뾔; 뾔; 뾔; ) HANGUL SYLLABLE BBOE +BF95;BF95;1108 116C 11A8;BF95;1108 116C 11A8; # (뾕; 뾕; 뾕; 뾕; 뾕; ) HANGUL SYLLABLE BBOEG +BF96;BF96;1108 116C 11A9;BF96;1108 116C 11A9; # (뾖; 뾖; 뾖; 뾖; 뾖; ) HANGUL SYLLABLE BBOEGG +BF97;BF97;1108 116C 11AA;BF97;1108 116C 11AA; # (뾗; 뾗; 뾗; 뾗; 뾗; ) HANGUL SYLLABLE BBOEGS +BF98;BF98;1108 116C 11AB;BF98;1108 116C 11AB; # (뾘; 뾘; 뾘; 뾘; 뾘; ) HANGUL SYLLABLE BBOEN +BF99;BF99;1108 116C 11AC;BF99;1108 116C 11AC; # (뾙; 뾙; 뾙; 뾙; 뾙; ) HANGUL SYLLABLE BBOENJ +BF9A;BF9A;1108 116C 11AD;BF9A;1108 116C 11AD; # (뾚; 뾚; 뾚; 뾚; 뾚; ) HANGUL SYLLABLE BBOENH +BF9B;BF9B;1108 116C 11AE;BF9B;1108 116C 11AE; # (뾛; 뾛; 뾛; 뾛; 뾛; ) HANGUL SYLLABLE BBOED +BF9C;BF9C;1108 116C 11AF;BF9C;1108 116C 11AF; # (뾜; 뾜; 뾜; 뾜; 뾜; ) HANGUL SYLLABLE BBOEL +BF9D;BF9D;1108 116C 11B0;BF9D;1108 116C 11B0; # (뾝; 뾝; 뾝; 뾝; 뾝; ) HANGUL SYLLABLE BBOELG +BF9E;BF9E;1108 116C 11B1;BF9E;1108 116C 11B1; # (뾞; 뾞; 뾞; 뾞; 뾞; ) HANGUL SYLLABLE BBOELM +BF9F;BF9F;1108 116C 11B2;BF9F;1108 116C 11B2; # (뾟; 뾟; 뾟; 뾟; 뾟; ) HANGUL SYLLABLE BBOELB +BFA0;BFA0;1108 116C 11B3;BFA0;1108 116C 11B3; # (뾠; 뾠; 뾠; 뾠; 뾠; ) HANGUL SYLLABLE BBOELS +BFA1;BFA1;1108 116C 11B4;BFA1;1108 116C 11B4; # (뾡; 뾡; 뾡; 뾡; 뾡; ) HANGUL SYLLABLE BBOELT +BFA2;BFA2;1108 116C 11B5;BFA2;1108 116C 11B5; # (뾢; 뾢; 뾢; 뾢; 뾢; ) HANGUL SYLLABLE BBOELP +BFA3;BFA3;1108 116C 11B6;BFA3;1108 116C 11B6; # (뾣; 뾣; 뾣; 뾣; 뾣; ) HANGUL SYLLABLE BBOELH +BFA4;BFA4;1108 116C 11B7;BFA4;1108 116C 11B7; # (뾤; 뾤; 뾤; 뾤; 뾤; ) HANGUL SYLLABLE BBOEM +BFA5;BFA5;1108 116C 11B8;BFA5;1108 116C 11B8; # (뾥; 뾥; 뾥; 뾥; 뾥; ) HANGUL SYLLABLE BBOEB +BFA6;BFA6;1108 116C 11B9;BFA6;1108 116C 11B9; # (뾦; 뾦; 뾦; 뾦; 뾦; ) HANGUL SYLLABLE BBOEBS +BFA7;BFA7;1108 116C 11BA;BFA7;1108 116C 11BA; # (뾧; 뾧; 뾧; 뾧; 뾧; ) HANGUL SYLLABLE BBOES +BFA8;BFA8;1108 116C 11BB;BFA8;1108 116C 11BB; # (뾨; 뾨; 뾨; 뾨; 뾨; ) HANGUL SYLLABLE BBOESS +BFA9;BFA9;1108 116C 11BC;BFA9;1108 116C 11BC; # (뾩; 뾩; 뾩; 뾩; 뾩; ) HANGUL SYLLABLE BBOENG +BFAA;BFAA;1108 116C 11BD;BFAA;1108 116C 11BD; # (뾪; 뾪; 뾪; 뾪; 뾪; ) HANGUL SYLLABLE BBOEJ +BFAB;BFAB;1108 116C 11BE;BFAB;1108 116C 11BE; # (뾫; 뾫; 뾫; 뾫; 뾫; ) HANGUL SYLLABLE BBOEC +BFAC;BFAC;1108 116C 11BF;BFAC;1108 116C 11BF; # (뾬; 뾬; 뾬; 뾬; 뾬; ) HANGUL SYLLABLE BBOEK +BFAD;BFAD;1108 116C 11C0;BFAD;1108 116C 11C0; # (뾭; 뾭; 뾭; 뾭; 뾭; ) HANGUL SYLLABLE BBOET +BFAE;BFAE;1108 116C 11C1;BFAE;1108 116C 11C1; # (뾮; 뾮; 뾮; 뾮; 뾮; ) HANGUL SYLLABLE BBOEP +BFAF;BFAF;1108 116C 11C2;BFAF;1108 116C 11C2; # (뾯; 뾯; 뾯; 뾯; 뾯; ) HANGUL SYLLABLE BBOEH +BFB0;BFB0;1108 116D;BFB0;1108 116D; # (뾰; 뾰; 뾰; 뾰; 뾰; ) HANGUL SYLLABLE BBYO +BFB1;BFB1;1108 116D 11A8;BFB1;1108 116D 11A8; # (뾱; 뾱; 뾱; 뾱; 뾱; ) HANGUL SYLLABLE BBYOG +BFB2;BFB2;1108 116D 11A9;BFB2;1108 116D 11A9; # (뾲; 뾲; 뾲; 뾲; 뾲; ) HANGUL SYLLABLE BBYOGG +BFB3;BFB3;1108 116D 11AA;BFB3;1108 116D 11AA; # (뾳; 뾳; 뾳; 뾳; 뾳; ) HANGUL SYLLABLE BBYOGS +BFB4;BFB4;1108 116D 11AB;BFB4;1108 116D 11AB; # (뾴; 뾴; 뾴; 뾴; 뾴; ) HANGUL SYLLABLE BBYON +BFB5;BFB5;1108 116D 11AC;BFB5;1108 116D 11AC; # (뾵; 뾵; 뾵; 뾵; 뾵; ) HANGUL SYLLABLE BBYONJ +BFB6;BFB6;1108 116D 11AD;BFB6;1108 116D 11AD; # (뾶; 뾶; 뾶; 뾶; 뾶; ) HANGUL SYLLABLE BBYONH +BFB7;BFB7;1108 116D 11AE;BFB7;1108 116D 11AE; # (뾷; 뾷; 뾷; 뾷; 뾷; ) HANGUL SYLLABLE BBYOD +BFB8;BFB8;1108 116D 11AF;BFB8;1108 116D 11AF; # (뾸; 뾸; 뾸; 뾸; 뾸; ) HANGUL SYLLABLE BBYOL +BFB9;BFB9;1108 116D 11B0;BFB9;1108 116D 11B0; # (뾹; 뾹; 뾹; 뾹; 뾹; ) HANGUL SYLLABLE BBYOLG +BFBA;BFBA;1108 116D 11B1;BFBA;1108 116D 11B1; # (뾺; 뾺; 뾺; 뾺; 뾺; ) HANGUL SYLLABLE BBYOLM +BFBB;BFBB;1108 116D 11B2;BFBB;1108 116D 11B2; # (뾻; 뾻; 뾻; 뾻; 뾻; ) HANGUL SYLLABLE BBYOLB +BFBC;BFBC;1108 116D 11B3;BFBC;1108 116D 11B3; # (뾼; 뾼; 뾼; 뾼; 뾼; ) HANGUL SYLLABLE BBYOLS +BFBD;BFBD;1108 116D 11B4;BFBD;1108 116D 11B4; # (뾽; 뾽; 뾽; 뾽; 뾽; ) HANGUL SYLLABLE BBYOLT +BFBE;BFBE;1108 116D 11B5;BFBE;1108 116D 11B5; # (뾾; 뾾; 뾾; 뾾; 뾾; ) HANGUL SYLLABLE BBYOLP +BFBF;BFBF;1108 116D 11B6;BFBF;1108 116D 11B6; # (뾿; 뾿; 뾿; 뾿; 뾿; ) HANGUL SYLLABLE BBYOLH +BFC0;BFC0;1108 116D 11B7;BFC0;1108 116D 11B7; # (뿀; 뿀; 뿀; 뿀; 뿀; ) HANGUL SYLLABLE BBYOM +BFC1;BFC1;1108 116D 11B8;BFC1;1108 116D 11B8; # (뿁; 뿁; 뿁; 뿁; 뿁; ) HANGUL SYLLABLE BBYOB +BFC2;BFC2;1108 116D 11B9;BFC2;1108 116D 11B9; # (뿂; 뿂; 뿂; 뿂; 뿂; ) HANGUL SYLLABLE BBYOBS +BFC3;BFC3;1108 116D 11BA;BFC3;1108 116D 11BA; # (뿃; 뿃; 뿃; 뿃; 뿃; ) HANGUL SYLLABLE BBYOS +BFC4;BFC4;1108 116D 11BB;BFC4;1108 116D 11BB; # (뿄; 뿄; 뿄; 뿄; 뿄; ) HANGUL SYLLABLE BBYOSS +BFC5;BFC5;1108 116D 11BC;BFC5;1108 116D 11BC; # (뿅; 뿅; 뿅; 뿅; 뿅; ) HANGUL SYLLABLE BBYONG +BFC6;BFC6;1108 116D 11BD;BFC6;1108 116D 11BD; # (뿆; 뿆; 뿆; 뿆; 뿆; ) HANGUL SYLLABLE BBYOJ +BFC7;BFC7;1108 116D 11BE;BFC7;1108 116D 11BE; # (뿇; 뿇; 뿇; 뿇; 뿇; ) HANGUL SYLLABLE BBYOC +BFC8;BFC8;1108 116D 11BF;BFC8;1108 116D 11BF; # (뿈; 뿈; 뿈; 뿈; 뿈; ) HANGUL SYLLABLE BBYOK +BFC9;BFC9;1108 116D 11C0;BFC9;1108 116D 11C0; # (뿉; 뿉; 뿉; 뿉; 뿉; ) HANGUL SYLLABLE BBYOT +BFCA;BFCA;1108 116D 11C1;BFCA;1108 116D 11C1; # (뿊; 뿊; 뿊; 뿊; 뿊; ) HANGUL SYLLABLE BBYOP +BFCB;BFCB;1108 116D 11C2;BFCB;1108 116D 11C2; # (뿋; 뿋; 뿋; 뿋; 뿋; ) HANGUL SYLLABLE BBYOH +BFCC;BFCC;1108 116E;BFCC;1108 116E; # (뿌; 뿌; 뿌; 뿌; 뿌; ) HANGUL SYLLABLE BBU +BFCD;BFCD;1108 116E 11A8;BFCD;1108 116E 11A8; # (뿍; 뿍; 뿍; 뿍; 뿍; ) HANGUL SYLLABLE BBUG +BFCE;BFCE;1108 116E 11A9;BFCE;1108 116E 11A9; # (뿎; 뿎; 뿎; 뿎; 뿎; ) HANGUL SYLLABLE BBUGG +BFCF;BFCF;1108 116E 11AA;BFCF;1108 116E 11AA; # (뿏; 뿏; 뿏; 뿏; 뿏; ) HANGUL SYLLABLE BBUGS +BFD0;BFD0;1108 116E 11AB;BFD0;1108 116E 11AB; # (뿐; 뿐; 뿐; 뿐; 뿐; ) HANGUL SYLLABLE BBUN +BFD1;BFD1;1108 116E 11AC;BFD1;1108 116E 11AC; # (뿑; 뿑; 뿑; 뿑; 뿑; ) HANGUL SYLLABLE BBUNJ +BFD2;BFD2;1108 116E 11AD;BFD2;1108 116E 11AD; # (뿒; 뿒; 뿒; 뿒; 뿒; ) HANGUL SYLLABLE BBUNH +BFD3;BFD3;1108 116E 11AE;BFD3;1108 116E 11AE; # (뿓; 뿓; 뿓; 뿓; 뿓; ) HANGUL SYLLABLE BBUD +BFD4;BFD4;1108 116E 11AF;BFD4;1108 116E 11AF; # (뿔; 뿔; 뿔; 뿔; 뿔; ) HANGUL SYLLABLE BBUL +BFD5;BFD5;1108 116E 11B0;BFD5;1108 116E 11B0; # (뿕; 뿕; 뿕; 뿕; 뿕; ) HANGUL SYLLABLE BBULG +BFD6;BFD6;1108 116E 11B1;BFD6;1108 116E 11B1; # (뿖; 뿖; 뿖; 뿖; 뿖; ) HANGUL SYLLABLE BBULM +BFD7;BFD7;1108 116E 11B2;BFD7;1108 116E 11B2; # (뿗; 뿗; 뿗; 뿗; 뿗; ) HANGUL SYLLABLE BBULB +BFD8;BFD8;1108 116E 11B3;BFD8;1108 116E 11B3; # (뿘; 뿘; 뿘; 뿘; 뿘; ) HANGUL SYLLABLE BBULS +BFD9;BFD9;1108 116E 11B4;BFD9;1108 116E 11B4; # (뿙; 뿙; 뿙; 뿙; 뿙; ) HANGUL SYLLABLE BBULT +BFDA;BFDA;1108 116E 11B5;BFDA;1108 116E 11B5; # (뿚; 뿚; 뿚; 뿚; 뿚; ) HANGUL SYLLABLE BBULP +BFDB;BFDB;1108 116E 11B6;BFDB;1108 116E 11B6; # (뿛; 뿛; 뿛; 뿛; 뿛; ) HANGUL SYLLABLE BBULH +BFDC;BFDC;1108 116E 11B7;BFDC;1108 116E 11B7; # (뿜; 뿜; 뿜; 뿜; 뿜; ) HANGUL SYLLABLE BBUM +BFDD;BFDD;1108 116E 11B8;BFDD;1108 116E 11B8; # (뿝; 뿝; 뿝; 뿝; 뿝; ) HANGUL SYLLABLE BBUB +BFDE;BFDE;1108 116E 11B9;BFDE;1108 116E 11B9; # (뿞; 뿞; 뿞; 뿞; 뿞; ) HANGUL SYLLABLE BBUBS +BFDF;BFDF;1108 116E 11BA;BFDF;1108 116E 11BA; # (뿟; 뿟; 뿟; 뿟; 뿟; ) HANGUL SYLLABLE BBUS +BFE0;BFE0;1108 116E 11BB;BFE0;1108 116E 11BB; # (뿠; 뿠; 뿠; 뿠; 뿠; ) HANGUL SYLLABLE BBUSS +BFE1;BFE1;1108 116E 11BC;BFE1;1108 116E 11BC; # (뿡; 뿡; 뿡; 뿡; 뿡; ) HANGUL SYLLABLE BBUNG +BFE2;BFE2;1108 116E 11BD;BFE2;1108 116E 11BD; # (뿢; 뿢; 뿢; 뿢; 뿢; ) HANGUL SYLLABLE BBUJ +BFE3;BFE3;1108 116E 11BE;BFE3;1108 116E 11BE; # (뿣; 뿣; 뿣; 뿣; 뿣; ) HANGUL SYLLABLE BBUC +BFE4;BFE4;1108 116E 11BF;BFE4;1108 116E 11BF; # (뿤; 뿤; 뿤; 뿤; 뿤; ) HANGUL SYLLABLE BBUK +BFE5;BFE5;1108 116E 11C0;BFE5;1108 116E 11C0; # (뿥; 뿥; 뿥; 뿥; 뿥; ) HANGUL SYLLABLE BBUT +BFE6;BFE6;1108 116E 11C1;BFE6;1108 116E 11C1; # (뿦; 뿦; 뿦; 뿦; 뿦; ) HANGUL SYLLABLE BBUP +BFE7;BFE7;1108 116E 11C2;BFE7;1108 116E 11C2; # (뿧; 뿧; 뿧; 뿧; 뿧; ) HANGUL SYLLABLE BBUH +BFE8;BFE8;1108 116F;BFE8;1108 116F; # (뿨; 뿨; 뿨; 뿨; 뿨; ) HANGUL SYLLABLE BBWEO +BFE9;BFE9;1108 116F 11A8;BFE9;1108 116F 11A8; # (뿩; 뿩; 뿩; 뿩; 뿩; ) HANGUL SYLLABLE BBWEOG +BFEA;BFEA;1108 116F 11A9;BFEA;1108 116F 11A9; # (뿪; 뿪; 뿪; 뿪; 뿪; ) HANGUL SYLLABLE BBWEOGG +BFEB;BFEB;1108 116F 11AA;BFEB;1108 116F 11AA; # (뿫; 뿫; 뿫; 뿫; 뿫; ) HANGUL SYLLABLE BBWEOGS +BFEC;BFEC;1108 116F 11AB;BFEC;1108 116F 11AB; # (뿬; 뿬; 뿬; 뿬; 뿬; ) HANGUL SYLLABLE BBWEON +BFED;BFED;1108 116F 11AC;BFED;1108 116F 11AC; # (뿭; 뿭; 뿭; 뿭; 뿭; ) HANGUL SYLLABLE BBWEONJ +BFEE;BFEE;1108 116F 11AD;BFEE;1108 116F 11AD; # (뿮; 뿮; 뿮; 뿮; 뿮; ) HANGUL SYLLABLE BBWEONH +BFEF;BFEF;1108 116F 11AE;BFEF;1108 116F 11AE; # (뿯; 뿯; 뿯; 뿯; 뿯; ) HANGUL SYLLABLE BBWEOD +BFF0;BFF0;1108 116F 11AF;BFF0;1108 116F 11AF; # (뿰; 뿰; 뿰; 뿰; 뿰; ) HANGUL SYLLABLE BBWEOL +BFF1;BFF1;1108 116F 11B0;BFF1;1108 116F 11B0; # (뿱; 뿱; 뿱; 뿱; 뿱; ) HANGUL SYLLABLE BBWEOLG +BFF2;BFF2;1108 116F 11B1;BFF2;1108 116F 11B1; # (뿲; 뿲; 뿲; 뿲; 뿲; ) HANGUL SYLLABLE BBWEOLM +BFF3;BFF3;1108 116F 11B2;BFF3;1108 116F 11B2; # (뿳; 뿳; 뿳; 뿳; 뿳; ) HANGUL SYLLABLE BBWEOLB +BFF4;BFF4;1108 116F 11B3;BFF4;1108 116F 11B3; # (뿴; 뿴; 뿴; 뿴; 뿴; ) HANGUL SYLLABLE BBWEOLS +BFF5;BFF5;1108 116F 11B4;BFF5;1108 116F 11B4; # (뿵; 뿵; 뿵; 뿵; 뿵; ) HANGUL SYLLABLE BBWEOLT +BFF6;BFF6;1108 116F 11B5;BFF6;1108 116F 11B5; # (뿶; 뿶; 뿶; 뿶; 뿶; ) HANGUL SYLLABLE BBWEOLP +BFF7;BFF7;1108 116F 11B6;BFF7;1108 116F 11B6; # (뿷; 뿷; 뿷; 뿷; 뿷; ) HANGUL SYLLABLE BBWEOLH +BFF8;BFF8;1108 116F 11B7;BFF8;1108 116F 11B7; # (뿸; 뿸; 뿸; 뿸; 뿸; ) HANGUL SYLLABLE BBWEOM +BFF9;BFF9;1108 116F 11B8;BFF9;1108 116F 11B8; # (뿹; 뿹; 뿹; 뿹; 뿹; ) HANGUL SYLLABLE BBWEOB +BFFA;BFFA;1108 116F 11B9;BFFA;1108 116F 11B9; # (뿺; 뿺; 뿺; 뿺; 뿺; ) HANGUL SYLLABLE BBWEOBS +BFFB;BFFB;1108 116F 11BA;BFFB;1108 116F 11BA; # (뿻; 뿻; 뿻; 뿻; 뿻; ) HANGUL SYLLABLE BBWEOS +BFFC;BFFC;1108 116F 11BB;BFFC;1108 116F 11BB; # (뿼; 뿼; 뿼; 뿼; 뿼; ) HANGUL SYLLABLE BBWEOSS +BFFD;BFFD;1108 116F 11BC;BFFD;1108 116F 11BC; # (뿽; 뿽; 뿽; 뿽; 뿽; ) HANGUL SYLLABLE BBWEONG +BFFE;BFFE;1108 116F 11BD;BFFE;1108 116F 11BD; # (뿾; 뿾; 뿾; 뿾; 뿾; ) HANGUL SYLLABLE BBWEOJ +BFFF;BFFF;1108 116F 11BE;BFFF;1108 116F 11BE; # (뿿; 뿿; 뿿; 뿿; 뿿; ) HANGUL SYLLABLE BBWEOC +C000;C000;1108 116F 11BF;C000;1108 116F 11BF; # (쀀; 쀀; 쀀; 쀀; 쀀; ) HANGUL SYLLABLE BBWEOK +C001;C001;1108 116F 11C0;C001;1108 116F 11C0; # (쀁; 쀁; 쀁; 쀁; 쀁; ) HANGUL SYLLABLE BBWEOT +C002;C002;1108 116F 11C1;C002;1108 116F 11C1; # (쀂; 쀂; 쀂; 쀂; 쀂; ) HANGUL SYLLABLE BBWEOP +C003;C003;1108 116F 11C2;C003;1108 116F 11C2; # (쀃; 쀃; 쀃; 쀃; 쀃; ) HANGUL SYLLABLE BBWEOH +C004;C004;1108 1170;C004;1108 1170; # (쀄; 쀄; 쀄; 쀄; 쀄; ) HANGUL SYLLABLE BBWE +C005;C005;1108 1170 11A8;C005;1108 1170 11A8; # (쀅; 쀅; 쀅; 쀅; 쀅; ) HANGUL SYLLABLE BBWEG +C006;C006;1108 1170 11A9;C006;1108 1170 11A9; # (쀆; 쀆; 쀆; 쀆; 쀆; ) HANGUL SYLLABLE BBWEGG +C007;C007;1108 1170 11AA;C007;1108 1170 11AA; # (쀇; 쀇; 쀇; 쀇; 쀇; ) HANGUL SYLLABLE BBWEGS +C008;C008;1108 1170 11AB;C008;1108 1170 11AB; # (쀈; 쀈; 쀈; 쀈; 쀈; ) HANGUL SYLLABLE BBWEN +C009;C009;1108 1170 11AC;C009;1108 1170 11AC; # (쀉; 쀉; 쀉; 쀉; 쀉; ) HANGUL SYLLABLE BBWENJ +C00A;C00A;1108 1170 11AD;C00A;1108 1170 11AD; # (쀊; 쀊; 쀊; 쀊; 쀊; ) HANGUL SYLLABLE BBWENH +C00B;C00B;1108 1170 11AE;C00B;1108 1170 11AE; # (쀋; 쀋; 쀋; 쀋; 쀋; ) HANGUL SYLLABLE BBWED +C00C;C00C;1108 1170 11AF;C00C;1108 1170 11AF; # (쀌; 쀌; 쀌; 쀌; 쀌; ) HANGUL SYLLABLE BBWEL +C00D;C00D;1108 1170 11B0;C00D;1108 1170 11B0; # (쀍; 쀍; 쀍; 쀍; 쀍; ) HANGUL SYLLABLE BBWELG +C00E;C00E;1108 1170 11B1;C00E;1108 1170 11B1; # (쀎; 쀎; 쀎; 쀎; 쀎; ) HANGUL SYLLABLE BBWELM +C00F;C00F;1108 1170 11B2;C00F;1108 1170 11B2; # (쀏; 쀏; 쀏; 쀏; 쀏; ) HANGUL SYLLABLE BBWELB +C010;C010;1108 1170 11B3;C010;1108 1170 11B3; # (쀐; 쀐; 쀐; 쀐; 쀐; ) HANGUL SYLLABLE BBWELS +C011;C011;1108 1170 11B4;C011;1108 1170 11B4; # (쀑; 쀑; 쀑; 쀑; 쀑; ) HANGUL SYLLABLE BBWELT +C012;C012;1108 1170 11B5;C012;1108 1170 11B5; # (쀒; 쀒; 쀒; 쀒; 쀒; ) HANGUL SYLLABLE BBWELP +C013;C013;1108 1170 11B6;C013;1108 1170 11B6; # (쀓; 쀓; 쀓; 쀓; 쀓; ) HANGUL SYLLABLE BBWELH +C014;C014;1108 1170 11B7;C014;1108 1170 11B7; # (쀔; 쀔; 쀔; 쀔; 쀔; ) HANGUL SYLLABLE BBWEM +C015;C015;1108 1170 11B8;C015;1108 1170 11B8; # (쀕; 쀕; 쀕; 쀕; 쀕; ) HANGUL SYLLABLE BBWEB +C016;C016;1108 1170 11B9;C016;1108 1170 11B9; # (쀖; 쀖; 쀖; 쀖; 쀖; ) HANGUL SYLLABLE BBWEBS +C017;C017;1108 1170 11BA;C017;1108 1170 11BA; # (쀗; 쀗; 쀗; 쀗; 쀗; ) HANGUL SYLLABLE BBWES +C018;C018;1108 1170 11BB;C018;1108 1170 11BB; # (쀘; 쀘; 쀘; 쀘; 쀘; ) HANGUL SYLLABLE BBWESS +C019;C019;1108 1170 11BC;C019;1108 1170 11BC; # (쀙; 쀙; 쀙; 쀙; 쀙; ) HANGUL SYLLABLE BBWENG +C01A;C01A;1108 1170 11BD;C01A;1108 1170 11BD; # (쀚; 쀚; 쀚; 쀚; 쀚; ) HANGUL SYLLABLE BBWEJ +C01B;C01B;1108 1170 11BE;C01B;1108 1170 11BE; # (쀛; 쀛; 쀛; 쀛; 쀛; ) HANGUL SYLLABLE BBWEC +C01C;C01C;1108 1170 11BF;C01C;1108 1170 11BF; # (쀜; 쀜; 쀜; 쀜; 쀜; ) HANGUL SYLLABLE BBWEK +C01D;C01D;1108 1170 11C0;C01D;1108 1170 11C0; # (쀝; 쀝; 쀝; 쀝; 쀝; ) HANGUL SYLLABLE BBWET +C01E;C01E;1108 1170 11C1;C01E;1108 1170 11C1; # (쀞; 쀞; 쀞; 쀞; 쀞; ) HANGUL SYLLABLE BBWEP +C01F;C01F;1108 1170 11C2;C01F;1108 1170 11C2; # (쀟; 쀟; 쀟; 쀟; 쀟; ) HANGUL SYLLABLE BBWEH +C020;C020;1108 1171;C020;1108 1171; # (쀠; 쀠; 쀠; 쀠; 쀠; ) HANGUL SYLLABLE BBWI +C021;C021;1108 1171 11A8;C021;1108 1171 11A8; # (쀡; 쀡; 쀡; 쀡; 쀡; ) HANGUL SYLLABLE BBWIG +C022;C022;1108 1171 11A9;C022;1108 1171 11A9; # (쀢; 쀢; 쀢; 쀢; 쀢; ) HANGUL SYLLABLE BBWIGG +C023;C023;1108 1171 11AA;C023;1108 1171 11AA; # (쀣; 쀣; 쀣; 쀣; 쀣; ) HANGUL SYLLABLE BBWIGS +C024;C024;1108 1171 11AB;C024;1108 1171 11AB; # (쀤; 쀤; 쀤; 쀤; 쀤; ) HANGUL SYLLABLE BBWIN +C025;C025;1108 1171 11AC;C025;1108 1171 11AC; # (쀥; 쀥; 쀥; 쀥; 쀥; ) HANGUL SYLLABLE BBWINJ +C026;C026;1108 1171 11AD;C026;1108 1171 11AD; # (쀦; 쀦; 쀦; 쀦; 쀦; ) HANGUL SYLLABLE BBWINH +C027;C027;1108 1171 11AE;C027;1108 1171 11AE; # (쀧; 쀧; 쀧; 쀧; 쀧; ) HANGUL SYLLABLE BBWID +C028;C028;1108 1171 11AF;C028;1108 1171 11AF; # (쀨; 쀨; 쀨; 쀨; 쀨; ) HANGUL SYLLABLE BBWIL +C029;C029;1108 1171 11B0;C029;1108 1171 11B0; # (쀩; 쀩; 쀩; 쀩; 쀩; ) HANGUL SYLLABLE BBWILG +C02A;C02A;1108 1171 11B1;C02A;1108 1171 11B1; # (쀪; 쀪; 쀪; 쀪; 쀪; ) HANGUL SYLLABLE BBWILM +C02B;C02B;1108 1171 11B2;C02B;1108 1171 11B2; # (쀫; 쀫; 쀫; 쀫; 쀫; ) HANGUL SYLLABLE BBWILB +C02C;C02C;1108 1171 11B3;C02C;1108 1171 11B3; # (쀬; 쀬; 쀬; 쀬; 쀬; ) HANGUL SYLLABLE BBWILS +C02D;C02D;1108 1171 11B4;C02D;1108 1171 11B4; # (쀭; 쀭; 쀭; 쀭; 쀭; ) HANGUL SYLLABLE BBWILT +C02E;C02E;1108 1171 11B5;C02E;1108 1171 11B5; # (쀮; 쀮; 쀮; 쀮; 쀮; ) HANGUL SYLLABLE BBWILP +C02F;C02F;1108 1171 11B6;C02F;1108 1171 11B6; # (쀯; 쀯; 쀯; 쀯; 쀯; ) HANGUL SYLLABLE BBWILH +C030;C030;1108 1171 11B7;C030;1108 1171 11B7; # (쀰; 쀰; 쀰; 쀰; 쀰; ) HANGUL SYLLABLE BBWIM +C031;C031;1108 1171 11B8;C031;1108 1171 11B8; # (쀱; 쀱; 쀱; 쀱; 쀱; ) HANGUL SYLLABLE BBWIB +C032;C032;1108 1171 11B9;C032;1108 1171 11B9; # (쀲; 쀲; 쀲; 쀲; 쀲; ) HANGUL SYLLABLE BBWIBS +C033;C033;1108 1171 11BA;C033;1108 1171 11BA; # (쀳; 쀳; 쀳; 쀳; 쀳; ) HANGUL SYLLABLE BBWIS +C034;C034;1108 1171 11BB;C034;1108 1171 11BB; # (쀴; 쀴; 쀴; 쀴; 쀴; ) HANGUL SYLLABLE BBWISS +C035;C035;1108 1171 11BC;C035;1108 1171 11BC; # (쀵; 쀵; 쀵; 쀵; 쀵; ) HANGUL SYLLABLE BBWING +C036;C036;1108 1171 11BD;C036;1108 1171 11BD; # (쀶; 쀶; 쀶; 쀶; 쀶; ) HANGUL SYLLABLE BBWIJ +C037;C037;1108 1171 11BE;C037;1108 1171 11BE; # (쀷; 쀷; 쀷; 쀷; 쀷; ) HANGUL SYLLABLE BBWIC +C038;C038;1108 1171 11BF;C038;1108 1171 11BF; # (쀸; 쀸; 쀸; 쀸; 쀸; ) HANGUL SYLLABLE BBWIK +C039;C039;1108 1171 11C0;C039;1108 1171 11C0; # (쀹; 쀹; 쀹; 쀹; 쀹; ) HANGUL SYLLABLE BBWIT +C03A;C03A;1108 1171 11C1;C03A;1108 1171 11C1; # (쀺; 쀺; 쀺; 쀺; 쀺; ) HANGUL SYLLABLE BBWIP +C03B;C03B;1108 1171 11C2;C03B;1108 1171 11C2; # (쀻; 쀻; 쀻; 쀻; 쀻; ) HANGUL SYLLABLE BBWIH +C03C;C03C;1108 1172;C03C;1108 1172; # (쀼; 쀼; 쀼; 쀼; 쀼; ) HANGUL SYLLABLE BBYU +C03D;C03D;1108 1172 11A8;C03D;1108 1172 11A8; # (쀽; 쀽; 쀽; 쀽; 쀽; ) HANGUL SYLLABLE BBYUG +C03E;C03E;1108 1172 11A9;C03E;1108 1172 11A9; # (쀾; 쀾; 쀾; 쀾; 쀾; ) HANGUL SYLLABLE BBYUGG +C03F;C03F;1108 1172 11AA;C03F;1108 1172 11AA; # (쀿; 쀿; 쀿; 쀿; 쀿; ) HANGUL SYLLABLE BBYUGS +C040;C040;1108 1172 11AB;C040;1108 1172 11AB; # (쁀; 쁀; 쁀; 쁀; 쁀; ) HANGUL SYLLABLE BBYUN +C041;C041;1108 1172 11AC;C041;1108 1172 11AC; # (쁁; 쁁; 쁁; 쁁; 쁁; ) HANGUL SYLLABLE BBYUNJ +C042;C042;1108 1172 11AD;C042;1108 1172 11AD; # (쁂; 쁂; 쁂; 쁂; 쁂; ) HANGUL SYLLABLE BBYUNH +C043;C043;1108 1172 11AE;C043;1108 1172 11AE; # (쁃; 쁃; 쁃; 쁃; 쁃; ) HANGUL SYLLABLE BBYUD +C044;C044;1108 1172 11AF;C044;1108 1172 11AF; # (쁄; 쁄; 쁄; 쁄; 쁄; ) HANGUL SYLLABLE BBYUL +C045;C045;1108 1172 11B0;C045;1108 1172 11B0; # (쁅; 쁅; 쁅; 쁅; 쁅; ) HANGUL SYLLABLE BBYULG +C046;C046;1108 1172 11B1;C046;1108 1172 11B1; # (쁆; 쁆; 쁆; 쁆; 쁆; ) HANGUL SYLLABLE BBYULM +C047;C047;1108 1172 11B2;C047;1108 1172 11B2; # (쁇; 쁇; 쁇; 쁇; 쁇; ) HANGUL SYLLABLE BBYULB +C048;C048;1108 1172 11B3;C048;1108 1172 11B3; # (쁈; 쁈; 쁈; 쁈; 쁈; ) HANGUL SYLLABLE BBYULS +C049;C049;1108 1172 11B4;C049;1108 1172 11B4; # (쁉; 쁉; 쁉; 쁉; 쁉; ) HANGUL SYLLABLE BBYULT +C04A;C04A;1108 1172 11B5;C04A;1108 1172 11B5; # (쁊; 쁊; 쁊; 쁊; 쁊; ) HANGUL SYLLABLE BBYULP +C04B;C04B;1108 1172 11B6;C04B;1108 1172 11B6; # (쁋; 쁋; 쁋; 쁋; 쁋; ) HANGUL SYLLABLE BBYULH +C04C;C04C;1108 1172 11B7;C04C;1108 1172 11B7; # (쁌; 쁌; 쁌; 쁌; 쁌; ) HANGUL SYLLABLE BBYUM +C04D;C04D;1108 1172 11B8;C04D;1108 1172 11B8; # (쁍; 쁍; 쁍; 쁍; 쁍; ) HANGUL SYLLABLE BBYUB +C04E;C04E;1108 1172 11B9;C04E;1108 1172 11B9; # (쁎; 쁎; 쁎; 쁎; 쁎; ) HANGUL SYLLABLE BBYUBS +C04F;C04F;1108 1172 11BA;C04F;1108 1172 11BA; # (쁏; 쁏; 쁏; 쁏; 쁏; ) HANGUL SYLLABLE BBYUS +C050;C050;1108 1172 11BB;C050;1108 1172 11BB; # (쁐; 쁐; 쁐; 쁐; 쁐; ) HANGUL SYLLABLE BBYUSS +C051;C051;1108 1172 11BC;C051;1108 1172 11BC; # (쁑; 쁑; 쁑; 쁑; 쁑; ) HANGUL SYLLABLE BBYUNG +C052;C052;1108 1172 11BD;C052;1108 1172 11BD; # (쁒; 쁒; 쁒; 쁒; 쁒; ) HANGUL SYLLABLE BBYUJ +C053;C053;1108 1172 11BE;C053;1108 1172 11BE; # (쁓; 쁓; 쁓; 쁓; 쁓; ) HANGUL SYLLABLE BBYUC +C054;C054;1108 1172 11BF;C054;1108 1172 11BF; # (쁔; 쁔; 쁔; 쁔; 쁔; ) HANGUL SYLLABLE BBYUK +C055;C055;1108 1172 11C0;C055;1108 1172 11C0; # (쁕; 쁕; 쁕; 쁕; 쁕; ) HANGUL SYLLABLE BBYUT +C056;C056;1108 1172 11C1;C056;1108 1172 11C1; # (쁖; 쁖; 쁖; 쁖; 쁖; ) HANGUL SYLLABLE BBYUP +C057;C057;1108 1172 11C2;C057;1108 1172 11C2; # (쁗; 쁗; 쁗; 쁗; 쁗; ) HANGUL SYLLABLE BBYUH +C058;C058;1108 1173;C058;1108 1173; # (쁘; 쁘; 쁘; 쁘; 쁘; ) HANGUL SYLLABLE BBEU +C059;C059;1108 1173 11A8;C059;1108 1173 11A8; # (쁙; 쁙; 쁙; 쁙; 쁙; ) HANGUL SYLLABLE BBEUG +C05A;C05A;1108 1173 11A9;C05A;1108 1173 11A9; # (쁚; 쁚; 쁚; 쁚; 쁚; ) HANGUL SYLLABLE BBEUGG +C05B;C05B;1108 1173 11AA;C05B;1108 1173 11AA; # (쁛; 쁛; 쁛; 쁛; 쁛; ) HANGUL SYLLABLE BBEUGS +C05C;C05C;1108 1173 11AB;C05C;1108 1173 11AB; # (쁜; 쁜; 쁜; 쁜; 쁜; ) HANGUL SYLLABLE BBEUN +C05D;C05D;1108 1173 11AC;C05D;1108 1173 11AC; # (쁝; 쁝; 쁝; 쁝; 쁝; ) HANGUL SYLLABLE BBEUNJ +C05E;C05E;1108 1173 11AD;C05E;1108 1173 11AD; # (쁞; 쁞; 쁞; 쁞; 쁞; ) HANGUL SYLLABLE BBEUNH +C05F;C05F;1108 1173 11AE;C05F;1108 1173 11AE; # (쁟; 쁟; 쁟; 쁟; 쁟; ) HANGUL SYLLABLE BBEUD +C060;C060;1108 1173 11AF;C060;1108 1173 11AF; # (쁠; 쁠; 쁠; 쁠; 쁠; ) HANGUL SYLLABLE BBEUL +C061;C061;1108 1173 11B0;C061;1108 1173 11B0; # (쁡; 쁡; 쁡; 쁡; 쁡; ) HANGUL SYLLABLE BBEULG +C062;C062;1108 1173 11B1;C062;1108 1173 11B1; # (쁢; 쁢; 쁢; 쁢; 쁢; ) HANGUL SYLLABLE BBEULM +C063;C063;1108 1173 11B2;C063;1108 1173 11B2; # (쁣; 쁣; 쁣; 쁣; 쁣; ) HANGUL SYLLABLE BBEULB +C064;C064;1108 1173 11B3;C064;1108 1173 11B3; # (쁤; 쁤; 쁤; 쁤; 쁤; ) HANGUL SYLLABLE BBEULS +C065;C065;1108 1173 11B4;C065;1108 1173 11B4; # (쁥; 쁥; 쁥; 쁥; 쁥; ) HANGUL SYLLABLE BBEULT +C066;C066;1108 1173 11B5;C066;1108 1173 11B5; # (쁦; 쁦; 쁦; 쁦; 쁦; ) HANGUL SYLLABLE BBEULP +C067;C067;1108 1173 11B6;C067;1108 1173 11B6; # (쁧; 쁧; 쁧; 쁧; 쁧; ) HANGUL SYLLABLE BBEULH +C068;C068;1108 1173 11B7;C068;1108 1173 11B7; # (쁨; 쁨; 쁨; 쁨; 쁨; ) HANGUL SYLLABLE BBEUM +C069;C069;1108 1173 11B8;C069;1108 1173 11B8; # (쁩; 쁩; 쁩; 쁩; 쁩; ) HANGUL SYLLABLE BBEUB +C06A;C06A;1108 1173 11B9;C06A;1108 1173 11B9; # (쁪; 쁪; 쁪; 쁪; 쁪; ) HANGUL SYLLABLE BBEUBS +C06B;C06B;1108 1173 11BA;C06B;1108 1173 11BA; # (쁫; 쁫; 쁫; 쁫; 쁫; ) HANGUL SYLLABLE BBEUS +C06C;C06C;1108 1173 11BB;C06C;1108 1173 11BB; # (쁬; 쁬; 쁬; 쁬; 쁬; ) HANGUL SYLLABLE BBEUSS +C06D;C06D;1108 1173 11BC;C06D;1108 1173 11BC; # (쁭; 쁭; 쁭; 쁭; 쁭; ) HANGUL SYLLABLE BBEUNG +C06E;C06E;1108 1173 11BD;C06E;1108 1173 11BD; # (쁮; 쁮; 쁮; 쁮; 쁮; ) HANGUL SYLLABLE BBEUJ +C06F;C06F;1108 1173 11BE;C06F;1108 1173 11BE; # (쁯; 쁯; 쁯; 쁯; 쁯; ) HANGUL SYLLABLE BBEUC +C070;C070;1108 1173 11BF;C070;1108 1173 11BF; # (쁰; 쁰; 쁰; 쁰; 쁰; ) HANGUL SYLLABLE BBEUK +C071;C071;1108 1173 11C0;C071;1108 1173 11C0; # (쁱; 쁱; 쁱; 쁱; 쁱; ) HANGUL SYLLABLE BBEUT +C072;C072;1108 1173 11C1;C072;1108 1173 11C1; # (쁲; 쁲; 쁲; 쁲; 쁲; ) HANGUL SYLLABLE BBEUP +C073;C073;1108 1173 11C2;C073;1108 1173 11C2; # (쁳; 쁳; 쁳; 쁳; 쁳; ) HANGUL SYLLABLE BBEUH +C074;C074;1108 1174;C074;1108 1174; # (쁴; 쁴; 쁴; 쁴; 쁴; ) HANGUL SYLLABLE BBYI +C075;C075;1108 1174 11A8;C075;1108 1174 11A8; # (쁵; 쁵; 쁵; 쁵; 쁵; ) HANGUL SYLLABLE BBYIG +C076;C076;1108 1174 11A9;C076;1108 1174 11A9; # (쁶; 쁶; 쁶; 쁶; 쁶; ) HANGUL SYLLABLE BBYIGG +C077;C077;1108 1174 11AA;C077;1108 1174 11AA; # (쁷; 쁷; 쁷; 쁷; 쁷; ) HANGUL SYLLABLE BBYIGS +C078;C078;1108 1174 11AB;C078;1108 1174 11AB; # (쁸; 쁸; 쁸; 쁸; 쁸; ) HANGUL SYLLABLE BBYIN +C079;C079;1108 1174 11AC;C079;1108 1174 11AC; # (쁹; 쁹; 쁹; 쁹; 쁹; ) HANGUL SYLLABLE BBYINJ +C07A;C07A;1108 1174 11AD;C07A;1108 1174 11AD; # (쁺; 쁺; 쁺; 쁺; 쁺; ) HANGUL SYLLABLE BBYINH +C07B;C07B;1108 1174 11AE;C07B;1108 1174 11AE; # (쁻; 쁻; 쁻; 쁻; 쁻; ) HANGUL SYLLABLE BBYID +C07C;C07C;1108 1174 11AF;C07C;1108 1174 11AF; # (쁼; 쁼; 쁼; 쁼; 쁼; ) HANGUL SYLLABLE BBYIL +C07D;C07D;1108 1174 11B0;C07D;1108 1174 11B0; # (쁽; 쁽; 쁽; 쁽; 쁽; ) HANGUL SYLLABLE BBYILG +C07E;C07E;1108 1174 11B1;C07E;1108 1174 11B1; # (쁾; 쁾; 쁾; 쁾; 쁾; ) HANGUL SYLLABLE BBYILM +C07F;C07F;1108 1174 11B2;C07F;1108 1174 11B2; # (쁿; 쁿; 쁿; 쁿; 쁿; ) HANGUL SYLLABLE BBYILB +C080;C080;1108 1174 11B3;C080;1108 1174 11B3; # (삀; 삀; 삀; 삀; 삀; ) HANGUL SYLLABLE BBYILS +C081;C081;1108 1174 11B4;C081;1108 1174 11B4; # (삁; 삁; 삁; 삁; 삁; ) HANGUL SYLLABLE BBYILT +C082;C082;1108 1174 11B5;C082;1108 1174 11B5; # (삂; 삂; 삂; 삂; 삂; ) HANGUL SYLLABLE BBYILP +C083;C083;1108 1174 11B6;C083;1108 1174 11B6; # (삃; 삃; 삃; 삃; 삃; ) HANGUL SYLLABLE BBYILH +C084;C084;1108 1174 11B7;C084;1108 1174 11B7; # (삄; 삄; 삄; 삄; 삄; ) HANGUL SYLLABLE BBYIM +C085;C085;1108 1174 11B8;C085;1108 1174 11B8; # (삅; 삅; 삅; 삅; 삅; ) HANGUL SYLLABLE BBYIB +C086;C086;1108 1174 11B9;C086;1108 1174 11B9; # (삆; 삆; 삆; 삆; 삆; ) HANGUL SYLLABLE BBYIBS +C087;C087;1108 1174 11BA;C087;1108 1174 11BA; # (삇; 삇; 삇; 삇; 삇; ) HANGUL SYLLABLE BBYIS +C088;C088;1108 1174 11BB;C088;1108 1174 11BB; # (삈; 삈; 삈; 삈; 삈; ) HANGUL SYLLABLE BBYISS +C089;C089;1108 1174 11BC;C089;1108 1174 11BC; # (삉; 삉; 삉; 삉; 삉; ) HANGUL SYLLABLE BBYING +C08A;C08A;1108 1174 11BD;C08A;1108 1174 11BD; # (삊; 삊; 삊; 삊; 삊; ) HANGUL SYLLABLE BBYIJ +C08B;C08B;1108 1174 11BE;C08B;1108 1174 11BE; # (삋; 삋; 삋; 삋; 삋; ) HANGUL SYLLABLE BBYIC +C08C;C08C;1108 1174 11BF;C08C;1108 1174 11BF; # (삌; 삌; 삌; 삌; 삌; ) HANGUL SYLLABLE BBYIK +C08D;C08D;1108 1174 11C0;C08D;1108 1174 11C0; # (삍; 삍; 삍; 삍; 삍; ) HANGUL SYLLABLE BBYIT +C08E;C08E;1108 1174 11C1;C08E;1108 1174 11C1; # (삎; 삎; 삎; 삎; 삎; ) HANGUL SYLLABLE BBYIP +C08F;C08F;1108 1174 11C2;C08F;1108 1174 11C2; # (삏; 삏; 삏; 삏; 삏; ) HANGUL SYLLABLE BBYIH +C090;C090;1108 1175;C090;1108 1175; # (삐; 삐; 삐; 삐; 삐; ) HANGUL SYLLABLE BBI +C091;C091;1108 1175 11A8;C091;1108 1175 11A8; # (삑; 삑; 삑; 삑; 삑; ) HANGUL SYLLABLE BBIG +C092;C092;1108 1175 11A9;C092;1108 1175 11A9; # (삒; 삒; 삒; 삒; 삒; ) HANGUL SYLLABLE BBIGG +C093;C093;1108 1175 11AA;C093;1108 1175 11AA; # (삓; 삓; 삓; 삓; 삓; ) HANGUL SYLLABLE BBIGS +C094;C094;1108 1175 11AB;C094;1108 1175 11AB; # (삔; 삔; 삔; 삔; 삔; ) HANGUL SYLLABLE BBIN +C095;C095;1108 1175 11AC;C095;1108 1175 11AC; # (삕; 삕; 삕; 삕; 삕; ) HANGUL SYLLABLE BBINJ +C096;C096;1108 1175 11AD;C096;1108 1175 11AD; # (삖; 삖; 삖; 삖; 삖; ) HANGUL SYLLABLE BBINH +C097;C097;1108 1175 11AE;C097;1108 1175 11AE; # (삗; 삗; 삗; 삗; 삗; ) HANGUL SYLLABLE BBID +C098;C098;1108 1175 11AF;C098;1108 1175 11AF; # (삘; 삘; 삘; 삘; 삘; ) HANGUL SYLLABLE BBIL +C099;C099;1108 1175 11B0;C099;1108 1175 11B0; # (삙; 삙; 삙; 삙; 삙; ) HANGUL SYLLABLE BBILG +C09A;C09A;1108 1175 11B1;C09A;1108 1175 11B1; # (삚; 삚; 삚; 삚; 삚; ) HANGUL SYLLABLE BBILM +C09B;C09B;1108 1175 11B2;C09B;1108 1175 11B2; # (삛; 삛; 삛; 삛; 삛; ) HANGUL SYLLABLE BBILB +C09C;C09C;1108 1175 11B3;C09C;1108 1175 11B3; # (삜; 삜; 삜; 삜; 삜; ) HANGUL SYLLABLE BBILS +C09D;C09D;1108 1175 11B4;C09D;1108 1175 11B4; # (삝; 삝; 삝; 삝; 삝; ) HANGUL SYLLABLE BBILT +C09E;C09E;1108 1175 11B5;C09E;1108 1175 11B5; # (삞; 삞; 삞; 삞; 삞; ) HANGUL SYLLABLE BBILP +C09F;C09F;1108 1175 11B6;C09F;1108 1175 11B6; # (삟; 삟; 삟; 삟; 삟; ) HANGUL SYLLABLE BBILH +C0A0;C0A0;1108 1175 11B7;C0A0;1108 1175 11B7; # (삠; 삠; 삠; 삠; 삠; ) HANGUL SYLLABLE BBIM +C0A1;C0A1;1108 1175 11B8;C0A1;1108 1175 11B8; # (삡; 삡; 삡; 삡; 삡; ) HANGUL SYLLABLE BBIB +C0A2;C0A2;1108 1175 11B9;C0A2;1108 1175 11B9; # (삢; 삢; 삢; 삢; 삢; ) HANGUL SYLLABLE BBIBS +C0A3;C0A3;1108 1175 11BA;C0A3;1108 1175 11BA; # (삣; 삣; 삣; 삣; 삣; ) HANGUL SYLLABLE BBIS +C0A4;C0A4;1108 1175 11BB;C0A4;1108 1175 11BB; # (삤; 삤; 삤; 삤; 삤; ) HANGUL SYLLABLE BBISS +C0A5;C0A5;1108 1175 11BC;C0A5;1108 1175 11BC; # (삥; 삥; 삥; 삥; 삥; ) HANGUL SYLLABLE BBING +C0A6;C0A6;1108 1175 11BD;C0A6;1108 1175 11BD; # (삦; 삦; 삦; 삦; 삦; ) HANGUL SYLLABLE BBIJ +C0A7;C0A7;1108 1175 11BE;C0A7;1108 1175 11BE; # (삧; 삧; 삧; 삧; 삧; ) HANGUL SYLLABLE BBIC +C0A8;C0A8;1108 1175 11BF;C0A8;1108 1175 11BF; # (삨; 삨; 삨; 삨; 삨; ) HANGUL SYLLABLE BBIK +C0A9;C0A9;1108 1175 11C0;C0A9;1108 1175 11C0; # (삩; 삩; 삩; 삩; 삩; ) HANGUL SYLLABLE BBIT +C0AA;C0AA;1108 1175 11C1;C0AA;1108 1175 11C1; # (삪; 삪; 삪; 삪; 삪; ) HANGUL SYLLABLE BBIP +C0AB;C0AB;1108 1175 11C2;C0AB;1108 1175 11C2; # (삫; 삫; 삫; 삫; 삫; ) HANGUL SYLLABLE BBIH +C0AC;C0AC;1109 1161;C0AC;1109 1161; # (사; 사; 사; 사; 사; ) HANGUL SYLLABLE SA +C0AD;C0AD;1109 1161 11A8;C0AD;1109 1161 11A8; # (삭; 삭; 삭; 삭; 삭; ) HANGUL SYLLABLE SAG +C0AE;C0AE;1109 1161 11A9;C0AE;1109 1161 11A9; # (삮; 삮; 삮; 삮; 삮; ) HANGUL SYLLABLE SAGG +C0AF;C0AF;1109 1161 11AA;C0AF;1109 1161 11AA; # (삯; 삯; 삯; 삯; 삯; ) HANGUL SYLLABLE SAGS +C0B0;C0B0;1109 1161 11AB;C0B0;1109 1161 11AB; # (산; 산; 산; 산; 산; ) HANGUL SYLLABLE SAN +C0B1;C0B1;1109 1161 11AC;C0B1;1109 1161 11AC; # (삱; 삱; 삱; 삱; 삱; ) HANGUL SYLLABLE SANJ +C0B2;C0B2;1109 1161 11AD;C0B2;1109 1161 11AD; # (삲; 삲; 삲; 삲; 삲; ) HANGUL SYLLABLE SANH +C0B3;C0B3;1109 1161 11AE;C0B3;1109 1161 11AE; # (삳; 삳; 삳; 삳; 삳; ) HANGUL SYLLABLE SAD +C0B4;C0B4;1109 1161 11AF;C0B4;1109 1161 11AF; # (살; 살; 살; 살; 살; ) HANGUL SYLLABLE SAL +C0B5;C0B5;1109 1161 11B0;C0B5;1109 1161 11B0; # (삵; 삵; 삵; 삵; 삵; ) HANGUL SYLLABLE SALG +C0B6;C0B6;1109 1161 11B1;C0B6;1109 1161 11B1; # (삶; 삶; 삶; 삶; 삶; ) HANGUL SYLLABLE SALM +C0B7;C0B7;1109 1161 11B2;C0B7;1109 1161 11B2; # (삷; 삷; 삷; 삷; 삷; ) HANGUL SYLLABLE SALB +C0B8;C0B8;1109 1161 11B3;C0B8;1109 1161 11B3; # (삸; 삸; 삸; 삸; 삸; ) HANGUL SYLLABLE SALS +C0B9;C0B9;1109 1161 11B4;C0B9;1109 1161 11B4; # (삹; 삹; 삹; 삹; 삹; ) HANGUL SYLLABLE SALT +C0BA;C0BA;1109 1161 11B5;C0BA;1109 1161 11B5; # (삺; 삺; 삺; 삺; 삺; ) HANGUL SYLLABLE SALP +C0BB;C0BB;1109 1161 11B6;C0BB;1109 1161 11B6; # (삻; 삻; 삻; 삻; 삻; ) HANGUL SYLLABLE SALH +C0BC;C0BC;1109 1161 11B7;C0BC;1109 1161 11B7; # (삼; 삼; 삼; 삼; 삼; ) HANGUL SYLLABLE SAM +C0BD;C0BD;1109 1161 11B8;C0BD;1109 1161 11B8; # (삽; 삽; 삽; 삽; 삽; ) HANGUL SYLLABLE SAB +C0BE;C0BE;1109 1161 11B9;C0BE;1109 1161 11B9; # (삾; 삾; 삾; 삾; 삾; ) HANGUL SYLLABLE SABS +C0BF;C0BF;1109 1161 11BA;C0BF;1109 1161 11BA; # (삿; 삿; 삿; 삿; 삿; ) HANGUL SYLLABLE SAS +C0C0;C0C0;1109 1161 11BB;C0C0;1109 1161 11BB; # (샀; 샀; 샀; 샀; 샀; ) HANGUL SYLLABLE SASS +C0C1;C0C1;1109 1161 11BC;C0C1;1109 1161 11BC; # (상; 상; 상; 상; 상; ) HANGUL SYLLABLE SANG +C0C2;C0C2;1109 1161 11BD;C0C2;1109 1161 11BD; # (샂; 샂; 샂; 샂; 샂; ) HANGUL SYLLABLE SAJ +C0C3;C0C3;1109 1161 11BE;C0C3;1109 1161 11BE; # (샃; 샃; 샃; 샃; 샃; ) HANGUL SYLLABLE SAC +C0C4;C0C4;1109 1161 11BF;C0C4;1109 1161 11BF; # (샄; 샄; 샄; 샄; 샄; ) HANGUL SYLLABLE SAK +C0C5;C0C5;1109 1161 11C0;C0C5;1109 1161 11C0; # (샅; 샅; 샅; 샅; 샅; ) HANGUL SYLLABLE SAT +C0C6;C0C6;1109 1161 11C1;C0C6;1109 1161 11C1; # (샆; 샆; 샆; 샆; 샆; ) HANGUL SYLLABLE SAP +C0C7;C0C7;1109 1161 11C2;C0C7;1109 1161 11C2; # (샇; 샇; 샇; 샇; 샇; ) HANGUL SYLLABLE SAH +C0C8;C0C8;1109 1162;C0C8;1109 1162; # (새; 새; 새; 새; 새; ) HANGUL SYLLABLE SAE +C0C9;C0C9;1109 1162 11A8;C0C9;1109 1162 11A8; # (색; 색; 색; 색; 색; ) HANGUL SYLLABLE SAEG +C0CA;C0CA;1109 1162 11A9;C0CA;1109 1162 11A9; # (샊; 샊; 샊; 샊; 샊; ) HANGUL SYLLABLE SAEGG +C0CB;C0CB;1109 1162 11AA;C0CB;1109 1162 11AA; # (샋; 샋; 샋; 샋; 샋; ) HANGUL SYLLABLE SAEGS +C0CC;C0CC;1109 1162 11AB;C0CC;1109 1162 11AB; # (샌; 샌; 샌; 샌; 샌; ) HANGUL SYLLABLE SAEN +C0CD;C0CD;1109 1162 11AC;C0CD;1109 1162 11AC; # (샍; 샍; 샍; 샍; 샍; ) HANGUL SYLLABLE SAENJ +C0CE;C0CE;1109 1162 11AD;C0CE;1109 1162 11AD; # (샎; 샎; 샎; 샎; 샎; ) HANGUL SYLLABLE SAENH +C0CF;C0CF;1109 1162 11AE;C0CF;1109 1162 11AE; # (샏; 샏; 샏; 샏; 샏; ) HANGUL SYLLABLE SAED +C0D0;C0D0;1109 1162 11AF;C0D0;1109 1162 11AF; # (샐; 샐; 샐; 샐; 샐; ) HANGUL SYLLABLE SAEL +C0D1;C0D1;1109 1162 11B0;C0D1;1109 1162 11B0; # (샑; 샑; 샑; 샑; 샑; ) HANGUL SYLLABLE SAELG +C0D2;C0D2;1109 1162 11B1;C0D2;1109 1162 11B1; # (샒; 샒; 샒; 샒; 샒; ) HANGUL SYLLABLE SAELM +C0D3;C0D3;1109 1162 11B2;C0D3;1109 1162 11B2; # (샓; 샓; 샓; 샓; 샓; ) HANGUL SYLLABLE SAELB +C0D4;C0D4;1109 1162 11B3;C0D4;1109 1162 11B3; # (샔; 샔; 샔; 샔; 샔; ) HANGUL SYLLABLE SAELS +C0D5;C0D5;1109 1162 11B4;C0D5;1109 1162 11B4; # (샕; 샕; 샕; 샕; 샕; ) HANGUL SYLLABLE SAELT +C0D6;C0D6;1109 1162 11B5;C0D6;1109 1162 11B5; # (샖; 샖; 샖; 샖; 샖; ) HANGUL SYLLABLE SAELP +C0D7;C0D7;1109 1162 11B6;C0D7;1109 1162 11B6; # (샗; 샗; 샗; 샗; 샗; ) HANGUL SYLLABLE SAELH +C0D8;C0D8;1109 1162 11B7;C0D8;1109 1162 11B7; # (샘; 샘; 샘; 샘; 샘; ) HANGUL SYLLABLE SAEM +C0D9;C0D9;1109 1162 11B8;C0D9;1109 1162 11B8; # (샙; 샙; 샙; 샙; 샙; ) HANGUL SYLLABLE SAEB +C0DA;C0DA;1109 1162 11B9;C0DA;1109 1162 11B9; # (샚; 샚; 샚; 샚; 샚; ) HANGUL SYLLABLE SAEBS +C0DB;C0DB;1109 1162 11BA;C0DB;1109 1162 11BA; # (샛; 샛; 샛; 샛; 샛; ) HANGUL SYLLABLE SAES +C0DC;C0DC;1109 1162 11BB;C0DC;1109 1162 11BB; # (샜; 샜; 샜; 샜; 샜; ) HANGUL SYLLABLE SAESS +C0DD;C0DD;1109 1162 11BC;C0DD;1109 1162 11BC; # (생; 생; 생; 생; 생; ) HANGUL SYLLABLE SAENG +C0DE;C0DE;1109 1162 11BD;C0DE;1109 1162 11BD; # (샞; 샞; 샞; 샞; 샞; ) HANGUL SYLLABLE SAEJ +C0DF;C0DF;1109 1162 11BE;C0DF;1109 1162 11BE; # (샟; 샟; 샟; 샟; 샟; ) HANGUL SYLLABLE SAEC +C0E0;C0E0;1109 1162 11BF;C0E0;1109 1162 11BF; # (샠; 샠; 샠; 샠; 샠; ) HANGUL SYLLABLE SAEK +C0E1;C0E1;1109 1162 11C0;C0E1;1109 1162 11C0; # (샡; 샡; 샡; 샡; 샡; ) HANGUL SYLLABLE SAET +C0E2;C0E2;1109 1162 11C1;C0E2;1109 1162 11C1; # (샢; 샢; 샢; 샢; 샢; ) HANGUL SYLLABLE SAEP +C0E3;C0E3;1109 1162 11C2;C0E3;1109 1162 11C2; # (샣; 샣; 샣; 샣; 샣; ) HANGUL SYLLABLE SAEH +C0E4;C0E4;1109 1163;C0E4;1109 1163; # (샤; 샤; 샤; 샤; 샤; ) HANGUL SYLLABLE SYA +C0E5;C0E5;1109 1163 11A8;C0E5;1109 1163 11A8; # (샥; 샥; 샥; 샥; 샥; ) HANGUL SYLLABLE SYAG +C0E6;C0E6;1109 1163 11A9;C0E6;1109 1163 11A9; # (샦; 샦; 샦; 샦; 샦; ) HANGUL SYLLABLE SYAGG +C0E7;C0E7;1109 1163 11AA;C0E7;1109 1163 11AA; # (샧; 샧; 샧; 샧; 샧; ) HANGUL SYLLABLE SYAGS +C0E8;C0E8;1109 1163 11AB;C0E8;1109 1163 11AB; # (샨; 샨; 샨; 샨; 샨; ) HANGUL SYLLABLE SYAN +C0E9;C0E9;1109 1163 11AC;C0E9;1109 1163 11AC; # (샩; 샩; 샩; 샩; 샩; ) HANGUL SYLLABLE SYANJ +C0EA;C0EA;1109 1163 11AD;C0EA;1109 1163 11AD; # (샪; 샪; 샪; 샪; 샪; ) HANGUL SYLLABLE SYANH +C0EB;C0EB;1109 1163 11AE;C0EB;1109 1163 11AE; # (샫; 샫; 샫; 샫; 샫; ) HANGUL SYLLABLE SYAD +C0EC;C0EC;1109 1163 11AF;C0EC;1109 1163 11AF; # (샬; 샬; 샬; 샬; 샬; ) HANGUL SYLLABLE SYAL +C0ED;C0ED;1109 1163 11B0;C0ED;1109 1163 11B0; # (샭; 샭; 샭; 샭; 샭; ) HANGUL SYLLABLE SYALG +C0EE;C0EE;1109 1163 11B1;C0EE;1109 1163 11B1; # (샮; 샮; 샮; 샮; 샮; ) HANGUL SYLLABLE SYALM +C0EF;C0EF;1109 1163 11B2;C0EF;1109 1163 11B2; # (샯; 샯; 샯; 샯; 샯; ) HANGUL SYLLABLE SYALB +C0F0;C0F0;1109 1163 11B3;C0F0;1109 1163 11B3; # (샰; 샰; 샰; 샰; 샰; ) HANGUL SYLLABLE SYALS +C0F1;C0F1;1109 1163 11B4;C0F1;1109 1163 11B4; # (샱; 샱; 샱; 샱; 샱; ) HANGUL SYLLABLE SYALT +C0F2;C0F2;1109 1163 11B5;C0F2;1109 1163 11B5; # (샲; 샲; 샲; 샲; 샲; ) HANGUL SYLLABLE SYALP +C0F3;C0F3;1109 1163 11B6;C0F3;1109 1163 11B6; # (샳; 샳; 샳; 샳; 샳; ) HANGUL SYLLABLE SYALH +C0F4;C0F4;1109 1163 11B7;C0F4;1109 1163 11B7; # (샴; 샴; 샴; 샴; 샴; ) HANGUL SYLLABLE SYAM +C0F5;C0F5;1109 1163 11B8;C0F5;1109 1163 11B8; # (샵; 샵; 샵; 샵; 샵; ) HANGUL SYLLABLE SYAB +C0F6;C0F6;1109 1163 11B9;C0F6;1109 1163 11B9; # (샶; 샶; 샶; 샶; 샶; ) HANGUL SYLLABLE SYABS +C0F7;C0F7;1109 1163 11BA;C0F7;1109 1163 11BA; # (샷; 샷; 샷; 샷; 샷; ) HANGUL SYLLABLE SYAS +C0F8;C0F8;1109 1163 11BB;C0F8;1109 1163 11BB; # (샸; 샸; 샸; 샸; 샸; ) HANGUL SYLLABLE SYASS +C0F9;C0F9;1109 1163 11BC;C0F9;1109 1163 11BC; # (샹; 샹; 샹; 샹; 샹; ) HANGUL SYLLABLE SYANG +C0FA;C0FA;1109 1163 11BD;C0FA;1109 1163 11BD; # (샺; 샺; 샺; 샺; 샺; ) HANGUL SYLLABLE SYAJ +C0FB;C0FB;1109 1163 11BE;C0FB;1109 1163 11BE; # (샻; 샻; 샻; 샻; 샻; ) HANGUL SYLLABLE SYAC +C0FC;C0FC;1109 1163 11BF;C0FC;1109 1163 11BF; # (샼; 샼; 샼; 샼; 샼; ) HANGUL SYLLABLE SYAK +C0FD;C0FD;1109 1163 11C0;C0FD;1109 1163 11C0; # (샽; 샽; 샽; 샽; 샽; ) HANGUL SYLLABLE SYAT +C0FE;C0FE;1109 1163 11C1;C0FE;1109 1163 11C1; # (샾; 샾; 샾; 샾; 샾; ) HANGUL SYLLABLE SYAP +C0FF;C0FF;1109 1163 11C2;C0FF;1109 1163 11C2; # (샿; 샿; 샿; 샿; 샿; ) HANGUL SYLLABLE SYAH +C100;C100;1109 1164;C100;1109 1164; # (섀; 섀; 섀; 섀; 섀; ) HANGUL SYLLABLE SYAE +C101;C101;1109 1164 11A8;C101;1109 1164 11A8; # (섁; 섁; 섁; 섁; 섁; ) HANGUL SYLLABLE SYAEG +C102;C102;1109 1164 11A9;C102;1109 1164 11A9; # (섂; 섂; 섂; 섂; 섂; ) HANGUL SYLLABLE SYAEGG +C103;C103;1109 1164 11AA;C103;1109 1164 11AA; # (섃; 섃; 섃; 섃; 섃; ) HANGUL SYLLABLE SYAEGS +C104;C104;1109 1164 11AB;C104;1109 1164 11AB; # (섄; 섄; 섄; 섄; 섄; ) HANGUL SYLLABLE SYAEN +C105;C105;1109 1164 11AC;C105;1109 1164 11AC; # (섅; 섅; 섅; 섅; 섅; ) HANGUL SYLLABLE SYAENJ +C106;C106;1109 1164 11AD;C106;1109 1164 11AD; # (섆; 섆; 섆; 섆; 섆; ) HANGUL SYLLABLE SYAENH +C107;C107;1109 1164 11AE;C107;1109 1164 11AE; # (섇; 섇; 섇; 섇; 섇; ) HANGUL SYLLABLE SYAED +C108;C108;1109 1164 11AF;C108;1109 1164 11AF; # (섈; 섈; 섈; 섈; 섈; ) HANGUL SYLLABLE SYAEL +C109;C109;1109 1164 11B0;C109;1109 1164 11B0; # (섉; 섉; 섉; 섉; 섉; ) HANGUL SYLLABLE SYAELG +C10A;C10A;1109 1164 11B1;C10A;1109 1164 11B1; # (섊; 섊; 섊; 섊; 섊; ) HANGUL SYLLABLE SYAELM +C10B;C10B;1109 1164 11B2;C10B;1109 1164 11B2; # (섋; 섋; 섋; 섋; 섋; ) HANGUL SYLLABLE SYAELB +C10C;C10C;1109 1164 11B3;C10C;1109 1164 11B3; # (섌; 섌; 섌; 섌; 섌; ) HANGUL SYLLABLE SYAELS +C10D;C10D;1109 1164 11B4;C10D;1109 1164 11B4; # (섍; 섍; 섍; 섍; 섍; ) HANGUL SYLLABLE SYAELT +C10E;C10E;1109 1164 11B5;C10E;1109 1164 11B5; # (섎; 섎; 섎; 섎; 섎; ) HANGUL SYLLABLE SYAELP +C10F;C10F;1109 1164 11B6;C10F;1109 1164 11B6; # (섏; 섏; 섏; 섏; 섏; ) HANGUL SYLLABLE SYAELH +C110;C110;1109 1164 11B7;C110;1109 1164 11B7; # (섐; 섐; 섐; 섐; 섐; ) HANGUL SYLLABLE SYAEM +C111;C111;1109 1164 11B8;C111;1109 1164 11B8; # (섑; 섑; 섑; 섑; 섑; ) HANGUL SYLLABLE SYAEB +C112;C112;1109 1164 11B9;C112;1109 1164 11B9; # (섒; 섒; 섒; 섒; 섒; ) HANGUL SYLLABLE SYAEBS +C113;C113;1109 1164 11BA;C113;1109 1164 11BA; # (섓; 섓; 섓; 섓; 섓; ) HANGUL SYLLABLE SYAES +C114;C114;1109 1164 11BB;C114;1109 1164 11BB; # (섔; 섔; 섔; 섔; 섔; ) HANGUL SYLLABLE SYAESS +C115;C115;1109 1164 11BC;C115;1109 1164 11BC; # (섕; 섕; 섕; 섕; 섕; ) HANGUL SYLLABLE SYAENG +C116;C116;1109 1164 11BD;C116;1109 1164 11BD; # (섖; 섖; 섖; 섖; 섖; ) HANGUL SYLLABLE SYAEJ +C117;C117;1109 1164 11BE;C117;1109 1164 11BE; # (섗; 섗; 섗; 섗; 섗; ) HANGUL SYLLABLE SYAEC +C118;C118;1109 1164 11BF;C118;1109 1164 11BF; # (섘; 섘; 섘; 섘; 섘; ) HANGUL SYLLABLE SYAEK +C119;C119;1109 1164 11C0;C119;1109 1164 11C0; # (섙; 섙; 섙; 섙; 섙; ) HANGUL SYLLABLE SYAET +C11A;C11A;1109 1164 11C1;C11A;1109 1164 11C1; # (섚; 섚; 섚; 섚; 섚; ) HANGUL SYLLABLE SYAEP +C11B;C11B;1109 1164 11C2;C11B;1109 1164 11C2; # (섛; 섛; 섛; 섛; 섛; ) HANGUL SYLLABLE SYAEH +C11C;C11C;1109 1165;C11C;1109 1165; # (서; 서; 서; 서; 서; ) HANGUL SYLLABLE SEO +C11D;C11D;1109 1165 11A8;C11D;1109 1165 11A8; # (석; 석; 석; 석; 석; ) HANGUL SYLLABLE SEOG +C11E;C11E;1109 1165 11A9;C11E;1109 1165 11A9; # (섞; 섞; 섞; 섞; 섞; ) HANGUL SYLLABLE SEOGG +C11F;C11F;1109 1165 11AA;C11F;1109 1165 11AA; # (섟; 섟; 섟; 섟; 섟; ) HANGUL SYLLABLE SEOGS +C120;C120;1109 1165 11AB;C120;1109 1165 11AB; # (선; 선; 선; 선; 선; ) HANGUL SYLLABLE SEON +C121;C121;1109 1165 11AC;C121;1109 1165 11AC; # (섡; 섡; 섡; 섡; 섡; ) HANGUL SYLLABLE SEONJ +C122;C122;1109 1165 11AD;C122;1109 1165 11AD; # (섢; 섢; 섢; 섢; 섢; ) HANGUL SYLLABLE SEONH +C123;C123;1109 1165 11AE;C123;1109 1165 11AE; # (섣; 섣; 섣; 섣; 섣; ) HANGUL SYLLABLE SEOD +C124;C124;1109 1165 11AF;C124;1109 1165 11AF; # (설; 설; 설; 설; 설; ) HANGUL SYLLABLE SEOL +C125;C125;1109 1165 11B0;C125;1109 1165 11B0; # (섥; 섥; 섥; 섥; 섥; ) HANGUL SYLLABLE SEOLG +C126;C126;1109 1165 11B1;C126;1109 1165 11B1; # (섦; 섦; 섦; 섦; 섦; ) HANGUL SYLLABLE SEOLM +C127;C127;1109 1165 11B2;C127;1109 1165 11B2; # (섧; 섧; 섧; 섧; 섧; ) HANGUL SYLLABLE SEOLB +C128;C128;1109 1165 11B3;C128;1109 1165 11B3; # (섨; 섨; 섨; 섨; 섨; ) HANGUL SYLLABLE SEOLS +C129;C129;1109 1165 11B4;C129;1109 1165 11B4; # (섩; 섩; 섩; 섩; 섩; ) HANGUL SYLLABLE SEOLT +C12A;C12A;1109 1165 11B5;C12A;1109 1165 11B5; # (섪; 섪; 섪; 섪; 섪; ) HANGUL SYLLABLE SEOLP +C12B;C12B;1109 1165 11B6;C12B;1109 1165 11B6; # (섫; 섫; 섫; 섫; 섫; ) HANGUL SYLLABLE SEOLH +C12C;C12C;1109 1165 11B7;C12C;1109 1165 11B7; # (섬; 섬; 섬; 섬; 섬; ) HANGUL SYLLABLE SEOM +C12D;C12D;1109 1165 11B8;C12D;1109 1165 11B8; # (섭; 섭; 섭; 섭; 섭; ) HANGUL SYLLABLE SEOB +C12E;C12E;1109 1165 11B9;C12E;1109 1165 11B9; # (섮; 섮; 섮; 섮; 섮; ) HANGUL SYLLABLE SEOBS +C12F;C12F;1109 1165 11BA;C12F;1109 1165 11BA; # (섯; 섯; 섯; 섯; 섯; ) HANGUL SYLLABLE SEOS +C130;C130;1109 1165 11BB;C130;1109 1165 11BB; # (섰; 섰; 섰; 섰; 섰; ) HANGUL SYLLABLE SEOSS +C131;C131;1109 1165 11BC;C131;1109 1165 11BC; # (성; 성; 성; 성; 성; ) HANGUL SYLLABLE SEONG +C132;C132;1109 1165 11BD;C132;1109 1165 11BD; # (섲; 섲; 섲; 섲; 섲; ) HANGUL SYLLABLE SEOJ +C133;C133;1109 1165 11BE;C133;1109 1165 11BE; # (섳; 섳; 섳; 섳; 섳; ) HANGUL SYLLABLE SEOC +C134;C134;1109 1165 11BF;C134;1109 1165 11BF; # (섴; 섴; 섴; 섴; 섴; ) HANGUL SYLLABLE SEOK +C135;C135;1109 1165 11C0;C135;1109 1165 11C0; # (섵; 섵; 섵; 섵; 섵; ) HANGUL SYLLABLE SEOT +C136;C136;1109 1165 11C1;C136;1109 1165 11C1; # (섶; 섶; 섶; 섶; 섶; ) HANGUL SYLLABLE SEOP +C137;C137;1109 1165 11C2;C137;1109 1165 11C2; # (섷; 섷; 섷; 섷; 섷; ) HANGUL SYLLABLE SEOH +C138;C138;1109 1166;C138;1109 1166; # (세; 세; 세; 세; 세; ) HANGUL SYLLABLE SE +C139;C139;1109 1166 11A8;C139;1109 1166 11A8; # (섹; 섹; 섹; 섹; 섹; ) HANGUL SYLLABLE SEG +C13A;C13A;1109 1166 11A9;C13A;1109 1166 11A9; # (섺; 섺; 섺; 섺; 섺; ) HANGUL SYLLABLE SEGG +C13B;C13B;1109 1166 11AA;C13B;1109 1166 11AA; # (섻; 섻; 섻; 섻; 섻; ) HANGUL SYLLABLE SEGS +C13C;C13C;1109 1166 11AB;C13C;1109 1166 11AB; # (센; 센; 센; 센; 센; ) HANGUL SYLLABLE SEN +C13D;C13D;1109 1166 11AC;C13D;1109 1166 11AC; # (섽; 섽; 섽; 섽; 섽; ) HANGUL SYLLABLE SENJ +C13E;C13E;1109 1166 11AD;C13E;1109 1166 11AD; # (섾; 섾; 섾; 섾; 섾; ) HANGUL SYLLABLE SENH +C13F;C13F;1109 1166 11AE;C13F;1109 1166 11AE; # (섿; 섿; 섿; 섿; 섿; ) HANGUL SYLLABLE SED +C140;C140;1109 1166 11AF;C140;1109 1166 11AF; # (셀; 셀; 셀; 셀; 셀; ) HANGUL SYLLABLE SEL +C141;C141;1109 1166 11B0;C141;1109 1166 11B0; # (셁; 셁; 셁; 셁; 셁; ) HANGUL SYLLABLE SELG +C142;C142;1109 1166 11B1;C142;1109 1166 11B1; # (셂; 셂; 셂; 셂; 셂; ) HANGUL SYLLABLE SELM +C143;C143;1109 1166 11B2;C143;1109 1166 11B2; # (셃; 셃; 셃; 셃; 셃; ) HANGUL SYLLABLE SELB +C144;C144;1109 1166 11B3;C144;1109 1166 11B3; # (셄; 셄; 셄; 셄; 셄; ) HANGUL SYLLABLE SELS +C145;C145;1109 1166 11B4;C145;1109 1166 11B4; # (셅; 셅; 셅; 셅; 셅; ) HANGUL SYLLABLE SELT +C146;C146;1109 1166 11B5;C146;1109 1166 11B5; # (셆; 셆; 셆; 셆; 셆; ) HANGUL SYLLABLE SELP +C147;C147;1109 1166 11B6;C147;1109 1166 11B6; # (셇; 셇; 셇; 셇; 셇; ) HANGUL SYLLABLE SELH +C148;C148;1109 1166 11B7;C148;1109 1166 11B7; # (셈; 셈; 셈; 셈; 셈; ) HANGUL SYLLABLE SEM +C149;C149;1109 1166 11B8;C149;1109 1166 11B8; # (셉; 셉; 셉; 셉; 셉; ) HANGUL SYLLABLE SEB +C14A;C14A;1109 1166 11B9;C14A;1109 1166 11B9; # (셊; 셊; 셊; 셊; 셊; ) HANGUL SYLLABLE SEBS +C14B;C14B;1109 1166 11BA;C14B;1109 1166 11BA; # (셋; 셋; 셋; 셋; 셋; ) HANGUL SYLLABLE SES +C14C;C14C;1109 1166 11BB;C14C;1109 1166 11BB; # (셌; 셌; 셌; 셌; 셌; ) HANGUL SYLLABLE SESS +C14D;C14D;1109 1166 11BC;C14D;1109 1166 11BC; # (셍; 셍; 셍; 셍; 셍; ) HANGUL SYLLABLE SENG +C14E;C14E;1109 1166 11BD;C14E;1109 1166 11BD; # (셎; 셎; 셎; 셎; 셎; ) HANGUL SYLLABLE SEJ +C14F;C14F;1109 1166 11BE;C14F;1109 1166 11BE; # (셏; 셏; 셏; 셏; 셏; ) HANGUL SYLLABLE SEC +C150;C150;1109 1166 11BF;C150;1109 1166 11BF; # (셐; 셐; 셐; 셐; 셐; ) HANGUL SYLLABLE SEK +C151;C151;1109 1166 11C0;C151;1109 1166 11C0; # (셑; 셑; 셑; 셑; 셑; ) HANGUL SYLLABLE SET +C152;C152;1109 1166 11C1;C152;1109 1166 11C1; # (셒; 셒; 셒; 셒; 셒; ) HANGUL SYLLABLE SEP +C153;C153;1109 1166 11C2;C153;1109 1166 11C2; # (셓; 셓; 셓; 셓; 셓; ) HANGUL SYLLABLE SEH +C154;C154;1109 1167;C154;1109 1167; # (셔; 셔; 셔; 셔; 셔; ) HANGUL SYLLABLE SYEO +C155;C155;1109 1167 11A8;C155;1109 1167 11A8; # (셕; 셕; 셕; 셕; 셕; ) HANGUL SYLLABLE SYEOG +C156;C156;1109 1167 11A9;C156;1109 1167 11A9; # (셖; 셖; 셖; 셖; 셖; ) HANGUL SYLLABLE SYEOGG +C157;C157;1109 1167 11AA;C157;1109 1167 11AA; # (셗; 셗; 셗; 셗; 셗; ) HANGUL SYLLABLE SYEOGS +C158;C158;1109 1167 11AB;C158;1109 1167 11AB; # (션; 션; 션; 션; 션; ) HANGUL SYLLABLE SYEON +C159;C159;1109 1167 11AC;C159;1109 1167 11AC; # (셙; 셙; 셙; 셙; 셙; ) HANGUL SYLLABLE SYEONJ +C15A;C15A;1109 1167 11AD;C15A;1109 1167 11AD; # (셚; 셚; 셚; 셚; 셚; ) HANGUL SYLLABLE SYEONH +C15B;C15B;1109 1167 11AE;C15B;1109 1167 11AE; # (셛; 셛; 셛; 셛; 셛; ) HANGUL SYLLABLE SYEOD +C15C;C15C;1109 1167 11AF;C15C;1109 1167 11AF; # (셜; 셜; 셜; 셜; 셜; ) HANGUL SYLLABLE SYEOL +C15D;C15D;1109 1167 11B0;C15D;1109 1167 11B0; # (셝; 셝; 셝; 셝; 셝; ) HANGUL SYLLABLE SYEOLG +C15E;C15E;1109 1167 11B1;C15E;1109 1167 11B1; # (셞; 셞; 셞; 셞; 셞; ) HANGUL SYLLABLE SYEOLM +C15F;C15F;1109 1167 11B2;C15F;1109 1167 11B2; # (셟; 셟; 셟; 셟; 셟; ) HANGUL SYLLABLE SYEOLB +C160;C160;1109 1167 11B3;C160;1109 1167 11B3; # (셠; 셠; 셠; 셠; 셠; ) HANGUL SYLLABLE SYEOLS +C161;C161;1109 1167 11B4;C161;1109 1167 11B4; # (셡; 셡; 셡; 셡; 셡; ) HANGUL SYLLABLE SYEOLT +C162;C162;1109 1167 11B5;C162;1109 1167 11B5; # (셢; 셢; 셢; 셢; 셢; ) HANGUL SYLLABLE SYEOLP +C163;C163;1109 1167 11B6;C163;1109 1167 11B6; # (셣; 셣; 셣; 셣; 셣; ) HANGUL SYLLABLE SYEOLH +C164;C164;1109 1167 11B7;C164;1109 1167 11B7; # (셤; 셤; 셤; 셤; 셤; ) HANGUL SYLLABLE SYEOM +C165;C165;1109 1167 11B8;C165;1109 1167 11B8; # (셥; 셥; 셥; 셥; 셥; ) HANGUL SYLLABLE SYEOB +C166;C166;1109 1167 11B9;C166;1109 1167 11B9; # (셦; 셦; 셦; 셦; 셦; ) HANGUL SYLLABLE SYEOBS +C167;C167;1109 1167 11BA;C167;1109 1167 11BA; # (셧; 셧; 셧; 셧; 셧; ) HANGUL SYLLABLE SYEOS +C168;C168;1109 1167 11BB;C168;1109 1167 11BB; # (셨; 셨; 셨; 셨; 셨; ) HANGUL SYLLABLE SYEOSS +C169;C169;1109 1167 11BC;C169;1109 1167 11BC; # (셩; 셩; 셩; 셩; 셩; ) HANGUL SYLLABLE SYEONG +C16A;C16A;1109 1167 11BD;C16A;1109 1167 11BD; # (셪; 셪; 셪; 셪; 셪; ) HANGUL SYLLABLE SYEOJ +C16B;C16B;1109 1167 11BE;C16B;1109 1167 11BE; # (셫; 셫; 셫; 셫; 셫; ) HANGUL SYLLABLE SYEOC +C16C;C16C;1109 1167 11BF;C16C;1109 1167 11BF; # (셬; 셬; 셬; 셬; 셬; ) HANGUL SYLLABLE SYEOK +C16D;C16D;1109 1167 11C0;C16D;1109 1167 11C0; # (셭; 셭; 셭; 셭; 셭; ) HANGUL SYLLABLE SYEOT +C16E;C16E;1109 1167 11C1;C16E;1109 1167 11C1; # (셮; 셮; 셮; 셮; 셮; ) HANGUL SYLLABLE SYEOP +C16F;C16F;1109 1167 11C2;C16F;1109 1167 11C2; # (셯; 셯; 셯; 셯; 셯; ) HANGUL SYLLABLE SYEOH +C170;C170;1109 1168;C170;1109 1168; # (셰; 셰; 셰; 셰; 셰; ) HANGUL SYLLABLE SYE +C171;C171;1109 1168 11A8;C171;1109 1168 11A8; # (셱; 셱; 셱; 셱; 셱; ) HANGUL SYLLABLE SYEG +C172;C172;1109 1168 11A9;C172;1109 1168 11A9; # (셲; 셲; 셲; 셲; 셲; ) HANGUL SYLLABLE SYEGG +C173;C173;1109 1168 11AA;C173;1109 1168 11AA; # (셳; 셳; 셳; 셳; 셳; ) HANGUL SYLLABLE SYEGS +C174;C174;1109 1168 11AB;C174;1109 1168 11AB; # (셴; 셴; 셴; 셴; 셴; ) HANGUL SYLLABLE SYEN +C175;C175;1109 1168 11AC;C175;1109 1168 11AC; # (셵; 셵; 셵; 셵; 셵; ) HANGUL SYLLABLE SYENJ +C176;C176;1109 1168 11AD;C176;1109 1168 11AD; # (셶; 셶; 셶; 셶; 셶; ) HANGUL SYLLABLE SYENH +C177;C177;1109 1168 11AE;C177;1109 1168 11AE; # (셷; 셷; 셷; 셷; 셷; ) HANGUL SYLLABLE SYED +C178;C178;1109 1168 11AF;C178;1109 1168 11AF; # (셸; 셸; 셸; 셸; 셸; ) HANGUL SYLLABLE SYEL +C179;C179;1109 1168 11B0;C179;1109 1168 11B0; # (셹; 셹; 셹; 셹; 셹; ) HANGUL SYLLABLE SYELG +C17A;C17A;1109 1168 11B1;C17A;1109 1168 11B1; # (셺; 셺; 셺; 셺; 셺; ) HANGUL SYLLABLE SYELM +C17B;C17B;1109 1168 11B2;C17B;1109 1168 11B2; # (셻; 셻; 셻; 셻; 셻; ) HANGUL SYLLABLE SYELB +C17C;C17C;1109 1168 11B3;C17C;1109 1168 11B3; # (셼; 셼; 셼; 셼; 셼; ) HANGUL SYLLABLE SYELS +C17D;C17D;1109 1168 11B4;C17D;1109 1168 11B4; # (셽; 셽; 셽; 셽; 셽; ) HANGUL SYLLABLE SYELT +C17E;C17E;1109 1168 11B5;C17E;1109 1168 11B5; # (셾; 셾; 셾; 셾; 셾; ) HANGUL SYLLABLE SYELP +C17F;C17F;1109 1168 11B6;C17F;1109 1168 11B6; # (셿; 셿; 셿; 셿; 셿; ) HANGUL SYLLABLE SYELH +C180;C180;1109 1168 11B7;C180;1109 1168 11B7; # (솀; 솀; 솀; 솀; 솀; ) HANGUL SYLLABLE SYEM +C181;C181;1109 1168 11B8;C181;1109 1168 11B8; # (솁; 솁; 솁; 솁; 솁; ) HANGUL SYLLABLE SYEB +C182;C182;1109 1168 11B9;C182;1109 1168 11B9; # (솂; 솂; 솂; 솂; 솂; ) HANGUL SYLLABLE SYEBS +C183;C183;1109 1168 11BA;C183;1109 1168 11BA; # (솃; 솃; 솃; 솃; 솃; ) HANGUL SYLLABLE SYES +C184;C184;1109 1168 11BB;C184;1109 1168 11BB; # (솄; 솄; 솄; 솄; 솄; ) HANGUL SYLLABLE SYESS +C185;C185;1109 1168 11BC;C185;1109 1168 11BC; # (솅; 솅; 솅; 솅; 솅; ) HANGUL SYLLABLE SYENG +C186;C186;1109 1168 11BD;C186;1109 1168 11BD; # (솆; 솆; 솆; 솆; 솆; ) HANGUL SYLLABLE SYEJ +C187;C187;1109 1168 11BE;C187;1109 1168 11BE; # (솇; 솇; 솇; 솇; 솇; ) HANGUL SYLLABLE SYEC +C188;C188;1109 1168 11BF;C188;1109 1168 11BF; # (솈; 솈; 솈; 솈; 솈; ) HANGUL SYLLABLE SYEK +C189;C189;1109 1168 11C0;C189;1109 1168 11C0; # (솉; 솉; 솉; 솉; 솉; ) HANGUL SYLLABLE SYET +C18A;C18A;1109 1168 11C1;C18A;1109 1168 11C1; # (솊; 솊; 솊; 솊; 솊; ) HANGUL SYLLABLE SYEP +C18B;C18B;1109 1168 11C2;C18B;1109 1168 11C2; # (솋; 솋; 솋; 솋; 솋; ) HANGUL SYLLABLE SYEH +C18C;C18C;1109 1169;C18C;1109 1169; # (소; 소; 소; 소; 소; ) HANGUL SYLLABLE SO +C18D;C18D;1109 1169 11A8;C18D;1109 1169 11A8; # (속; 속; 속; 속; 속; ) HANGUL SYLLABLE SOG +C18E;C18E;1109 1169 11A9;C18E;1109 1169 11A9; # (솎; 솎; 솎; 솎; 솎; ) HANGUL SYLLABLE SOGG +C18F;C18F;1109 1169 11AA;C18F;1109 1169 11AA; # (솏; 솏; 솏; 솏; 솏; ) HANGUL SYLLABLE SOGS +C190;C190;1109 1169 11AB;C190;1109 1169 11AB; # (손; 손; 손; 손; 손; ) HANGUL SYLLABLE SON +C191;C191;1109 1169 11AC;C191;1109 1169 11AC; # (솑; 솑; 솑; 솑; 솑; ) HANGUL SYLLABLE SONJ +C192;C192;1109 1169 11AD;C192;1109 1169 11AD; # (솒; 솒; 솒; 솒; 솒; ) HANGUL SYLLABLE SONH +C193;C193;1109 1169 11AE;C193;1109 1169 11AE; # (솓; 솓; 솓; 솓; 솓; ) HANGUL SYLLABLE SOD +C194;C194;1109 1169 11AF;C194;1109 1169 11AF; # (솔; 솔; 솔; 솔; 솔; ) HANGUL SYLLABLE SOL +C195;C195;1109 1169 11B0;C195;1109 1169 11B0; # (솕; 솕; 솕; 솕; 솕; ) HANGUL SYLLABLE SOLG +C196;C196;1109 1169 11B1;C196;1109 1169 11B1; # (솖; 솖; 솖; 솖; 솖; ) HANGUL SYLLABLE SOLM +C197;C197;1109 1169 11B2;C197;1109 1169 11B2; # (솗; 솗; 솗; 솗; 솗; ) HANGUL SYLLABLE SOLB +C198;C198;1109 1169 11B3;C198;1109 1169 11B3; # (솘; 솘; 솘; 솘; 솘; ) HANGUL SYLLABLE SOLS +C199;C199;1109 1169 11B4;C199;1109 1169 11B4; # (솙; 솙; 솙; 솙; 솙; ) HANGUL SYLLABLE SOLT +C19A;C19A;1109 1169 11B5;C19A;1109 1169 11B5; # (솚; 솚; 솚; 솚; 솚; ) HANGUL SYLLABLE SOLP +C19B;C19B;1109 1169 11B6;C19B;1109 1169 11B6; # (솛; 솛; 솛; 솛; 솛; ) HANGUL SYLLABLE SOLH +C19C;C19C;1109 1169 11B7;C19C;1109 1169 11B7; # (솜; 솜; 솜; 솜; 솜; ) HANGUL SYLLABLE SOM +C19D;C19D;1109 1169 11B8;C19D;1109 1169 11B8; # (솝; 솝; 솝; 솝; 솝; ) HANGUL SYLLABLE SOB +C19E;C19E;1109 1169 11B9;C19E;1109 1169 11B9; # (솞; 솞; 솞; 솞; 솞; ) HANGUL SYLLABLE SOBS +C19F;C19F;1109 1169 11BA;C19F;1109 1169 11BA; # (솟; 솟; 솟; 솟; 솟; ) HANGUL SYLLABLE SOS +C1A0;C1A0;1109 1169 11BB;C1A0;1109 1169 11BB; # (솠; 솠; 솠; 솠; 솠; ) HANGUL SYLLABLE SOSS +C1A1;C1A1;1109 1169 11BC;C1A1;1109 1169 11BC; # (송; 송; 송; 송; 송; ) HANGUL SYLLABLE SONG +C1A2;C1A2;1109 1169 11BD;C1A2;1109 1169 11BD; # (솢; 솢; 솢; 솢; 솢; ) HANGUL SYLLABLE SOJ +C1A3;C1A3;1109 1169 11BE;C1A3;1109 1169 11BE; # (솣; 솣; 솣; 솣; 솣; ) HANGUL SYLLABLE SOC +C1A4;C1A4;1109 1169 11BF;C1A4;1109 1169 11BF; # (솤; 솤; 솤; 솤; 솤; ) HANGUL SYLLABLE SOK +C1A5;C1A5;1109 1169 11C0;C1A5;1109 1169 11C0; # (솥; 솥; 솥; 솥; 솥; ) HANGUL SYLLABLE SOT +C1A6;C1A6;1109 1169 11C1;C1A6;1109 1169 11C1; # (솦; 솦; 솦; 솦; 솦; ) HANGUL SYLLABLE SOP +C1A7;C1A7;1109 1169 11C2;C1A7;1109 1169 11C2; # (솧; 솧; 솧; 솧; 솧; ) HANGUL SYLLABLE SOH +C1A8;C1A8;1109 116A;C1A8;1109 116A; # (솨; 솨; 솨; 솨; 솨; ) HANGUL SYLLABLE SWA +C1A9;C1A9;1109 116A 11A8;C1A9;1109 116A 11A8; # (솩; 솩; 솩; 솩; 솩; ) HANGUL SYLLABLE SWAG +C1AA;C1AA;1109 116A 11A9;C1AA;1109 116A 11A9; # (솪; 솪; 솪; 솪; 솪; ) HANGUL SYLLABLE SWAGG +C1AB;C1AB;1109 116A 11AA;C1AB;1109 116A 11AA; # (솫; 솫; 솫; 솫; 솫; ) HANGUL SYLLABLE SWAGS +C1AC;C1AC;1109 116A 11AB;C1AC;1109 116A 11AB; # (솬; 솬; 솬; 솬; 솬; ) HANGUL SYLLABLE SWAN +C1AD;C1AD;1109 116A 11AC;C1AD;1109 116A 11AC; # (솭; 솭; 솭; 솭; 솭; ) HANGUL SYLLABLE SWANJ +C1AE;C1AE;1109 116A 11AD;C1AE;1109 116A 11AD; # (솮; 솮; 솮; 솮; 솮; ) HANGUL SYLLABLE SWANH +C1AF;C1AF;1109 116A 11AE;C1AF;1109 116A 11AE; # (솯; 솯; 솯; 솯; 솯; ) HANGUL SYLLABLE SWAD +C1B0;C1B0;1109 116A 11AF;C1B0;1109 116A 11AF; # (솰; 솰; 솰; 솰; 솰; ) HANGUL SYLLABLE SWAL +C1B1;C1B1;1109 116A 11B0;C1B1;1109 116A 11B0; # (솱; 솱; 솱; 솱; 솱; ) HANGUL SYLLABLE SWALG +C1B2;C1B2;1109 116A 11B1;C1B2;1109 116A 11B1; # (솲; 솲; 솲; 솲; 솲; ) HANGUL SYLLABLE SWALM +C1B3;C1B3;1109 116A 11B2;C1B3;1109 116A 11B2; # (솳; 솳; 솳; 솳; 솳; ) HANGUL SYLLABLE SWALB +C1B4;C1B4;1109 116A 11B3;C1B4;1109 116A 11B3; # (솴; 솴; 솴; 솴; 솴; ) HANGUL SYLLABLE SWALS +C1B5;C1B5;1109 116A 11B4;C1B5;1109 116A 11B4; # (솵; 솵; 솵; 솵; 솵; ) HANGUL SYLLABLE SWALT +C1B6;C1B6;1109 116A 11B5;C1B6;1109 116A 11B5; # (솶; 솶; 솶; 솶; 솶; ) HANGUL SYLLABLE SWALP +C1B7;C1B7;1109 116A 11B6;C1B7;1109 116A 11B6; # (솷; 솷; 솷; 솷; 솷; ) HANGUL SYLLABLE SWALH +C1B8;C1B8;1109 116A 11B7;C1B8;1109 116A 11B7; # (솸; 솸; 솸; 솸; 솸; ) HANGUL SYLLABLE SWAM +C1B9;C1B9;1109 116A 11B8;C1B9;1109 116A 11B8; # (솹; 솹; 솹; 솹; 솹; ) HANGUL SYLLABLE SWAB +C1BA;C1BA;1109 116A 11B9;C1BA;1109 116A 11B9; # (솺; 솺; 솺; 솺; 솺; ) HANGUL SYLLABLE SWABS +C1BB;C1BB;1109 116A 11BA;C1BB;1109 116A 11BA; # (솻; 솻; 솻; 솻; 솻; ) HANGUL SYLLABLE SWAS +C1BC;C1BC;1109 116A 11BB;C1BC;1109 116A 11BB; # (솼; 솼; 솼; 솼; 솼; ) HANGUL SYLLABLE SWASS +C1BD;C1BD;1109 116A 11BC;C1BD;1109 116A 11BC; # (솽; 솽; 솽; 솽; 솽; ) HANGUL SYLLABLE SWANG +C1BE;C1BE;1109 116A 11BD;C1BE;1109 116A 11BD; # (솾; 솾; 솾; 솾; 솾; ) HANGUL SYLLABLE SWAJ +C1BF;C1BF;1109 116A 11BE;C1BF;1109 116A 11BE; # (솿; 솿; 솿; 솿; 솿; ) HANGUL SYLLABLE SWAC +C1C0;C1C0;1109 116A 11BF;C1C0;1109 116A 11BF; # (쇀; 쇀; 쇀; 쇀; 쇀; ) HANGUL SYLLABLE SWAK +C1C1;C1C1;1109 116A 11C0;C1C1;1109 116A 11C0; # (쇁; 쇁; 쇁; 쇁; 쇁; ) HANGUL SYLLABLE SWAT +C1C2;C1C2;1109 116A 11C1;C1C2;1109 116A 11C1; # (쇂; 쇂; 쇂; 쇂; 쇂; ) HANGUL SYLLABLE SWAP +C1C3;C1C3;1109 116A 11C2;C1C3;1109 116A 11C2; # (쇃; 쇃; 쇃; 쇃; 쇃; ) HANGUL SYLLABLE SWAH +C1C4;C1C4;1109 116B;C1C4;1109 116B; # (쇄; 쇄; 쇄; 쇄; 쇄; ) HANGUL SYLLABLE SWAE +C1C5;C1C5;1109 116B 11A8;C1C5;1109 116B 11A8; # (쇅; 쇅; 쇅; 쇅; 쇅; ) HANGUL SYLLABLE SWAEG +C1C6;C1C6;1109 116B 11A9;C1C6;1109 116B 11A9; # (쇆; 쇆; 쇆; 쇆; 쇆; ) HANGUL SYLLABLE SWAEGG +C1C7;C1C7;1109 116B 11AA;C1C7;1109 116B 11AA; # (쇇; 쇇; 쇇; 쇇; 쇇; ) HANGUL SYLLABLE SWAEGS +C1C8;C1C8;1109 116B 11AB;C1C8;1109 116B 11AB; # (쇈; 쇈; 쇈; 쇈; 쇈; ) HANGUL SYLLABLE SWAEN +C1C9;C1C9;1109 116B 11AC;C1C9;1109 116B 11AC; # (쇉; 쇉; 쇉; 쇉; 쇉; ) HANGUL SYLLABLE SWAENJ +C1CA;C1CA;1109 116B 11AD;C1CA;1109 116B 11AD; # (쇊; 쇊; 쇊; 쇊; 쇊; ) HANGUL SYLLABLE SWAENH +C1CB;C1CB;1109 116B 11AE;C1CB;1109 116B 11AE; # (쇋; 쇋; 쇋; 쇋; 쇋; ) HANGUL SYLLABLE SWAED +C1CC;C1CC;1109 116B 11AF;C1CC;1109 116B 11AF; # (쇌; 쇌; 쇌; 쇌; 쇌; ) HANGUL SYLLABLE SWAEL +C1CD;C1CD;1109 116B 11B0;C1CD;1109 116B 11B0; # (쇍; 쇍; 쇍; 쇍; 쇍; ) HANGUL SYLLABLE SWAELG +C1CE;C1CE;1109 116B 11B1;C1CE;1109 116B 11B1; # (쇎; 쇎; 쇎; 쇎; 쇎; ) HANGUL SYLLABLE SWAELM +C1CF;C1CF;1109 116B 11B2;C1CF;1109 116B 11B2; # (쇏; 쇏; 쇏; 쇏; 쇏; ) HANGUL SYLLABLE SWAELB +C1D0;C1D0;1109 116B 11B3;C1D0;1109 116B 11B3; # (쇐; 쇐; 쇐; 쇐; 쇐; ) HANGUL SYLLABLE SWAELS +C1D1;C1D1;1109 116B 11B4;C1D1;1109 116B 11B4; # (쇑; 쇑; 쇑; 쇑; 쇑; ) HANGUL SYLLABLE SWAELT +C1D2;C1D2;1109 116B 11B5;C1D2;1109 116B 11B5; # (쇒; 쇒; 쇒; 쇒; 쇒; ) HANGUL SYLLABLE SWAELP +C1D3;C1D3;1109 116B 11B6;C1D3;1109 116B 11B6; # (쇓; 쇓; 쇓; 쇓; 쇓; ) HANGUL SYLLABLE SWAELH +C1D4;C1D4;1109 116B 11B7;C1D4;1109 116B 11B7; # (쇔; 쇔; 쇔; 쇔; 쇔; ) HANGUL SYLLABLE SWAEM +C1D5;C1D5;1109 116B 11B8;C1D5;1109 116B 11B8; # (쇕; 쇕; 쇕; 쇕; 쇕; ) HANGUL SYLLABLE SWAEB +C1D6;C1D6;1109 116B 11B9;C1D6;1109 116B 11B9; # (쇖; 쇖; 쇖; 쇖; 쇖; ) HANGUL SYLLABLE SWAEBS +C1D7;C1D7;1109 116B 11BA;C1D7;1109 116B 11BA; # (쇗; 쇗; 쇗; 쇗; 쇗; ) HANGUL SYLLABLE SWAES +C1D8;C1D8;1109 116B 11BB;C1D8;1109 116B 11BB; # (쇘; 쇘; 쇘; 쇘; 쇘; ) HANGUL SYLLABLE SWAESS +C1D9;C1D9;1109 116B 11BC;C1D9;1109 116B 11BC; # (쇙; 쇙; 쇙; 쇙; 쇙; ) HANGUL SYLLABLE SWAENG +C1DA;C1DA;1109 116B 11BD;C1DA;1109 116B 11BD; # (쇚; 쇚; 쇚; 쇚; 쇚; ) HANGUL SYLLABLE SWAEJ +C1DB;C1DB;1109 116B 11BE;C1DB;1109 116B 11BE; # (쇛; 쇛; 쇛; 쇛; 쇛; ) HANGUL SYLLABLE SWAEC +C1DC;C1DC;1109 116B 11BF;C1DC;1109 116B 11BF; # (쇜; 쇜; 쇜; 쇜; 쇜; ) HANGUL SYLLABLE SWAEK +C1DD;C1DD;1109 116B 11C0;C1DD;1109 116B 11C0; # (쇝; 쇝; 쇝; 쇝; 쇝; ) HANGUL SYLLABLE SWAET +C1DE;C1DE;1109 116B 11C1;C1DE;1109 116B 11C1; # (쇞; 쇞; 쇞; 쇞; 쇞; ) HANGUL SYLLABLE SWAEP +C1DF;C1DF;1109 116B 11C2;C1DF;1109 116B 11C2; # (쇟; 쇟; 쇟; 쇟; 쇟; ) HANGUL SYLLABLE SWAEH +C1E0;C1E0;1109 116C;C1E0;1109 116C; # (쇠; 쇠; 쇠; 쇠; 쇠; ) HANGUL SYLLABLE SOE +C1E1;C1E1;1109 116C 11A8;C1E1;1109 116C 11A8; # (쇡; 쇡; 쇡; 쇡; 쇡; ) HANGUL SYLLABLE SOEG +C1E2;C1E2;1109 116C 11A9;C1E2;1109 116C 11A9; # (쇢; 쇢; 쇢; 쇢; 쇢; ) HANGUL SYLLABLE SOEGG +C1E3;C1E3;1109 116C 11AA;C1E3;1109 116C 11AA; # (쇣; 쇣; 쇣; 쇣; 쇣; ) HANGUL SYLLABLE SOEGS +C1E4;C1E4;1109 116C 11AB;C1E4;1109 116C 11AB; # (쇤; 쇤; 쇤; 쇤; 쇤; ) HANGUL SYLLABLE SOEN +C1E5;C1E5;1109 116C 11AC;C1E5;1109 116C 11AC; # (쇥; 쇥; 쇥; 쇥; 쇥; ) HANGUL SYLLABLE SOENJ +C1E6;C1E6;1109 116C 11AD;C1E6;1109 116C 11AD; # (쇦; 쇦; 쇦; 쇦; 쇦; ) HANGUL SYLLABLE SOENH +C1E7;C1E7;1109 116C 11AE;C1E7;1109 116C 11AE; # (쇧; 쇧; 쇧; 쇧; 쇧; ) HANGUL SYLLABLE SOED +C1E8;C1E8;1109 116C 11AF;C1E8;1109 116C 11AF; # (쇨; 쇨; 쇨; 쇨; 쇨; ) HANGUL SYLLABLE SOEL +C1E9;C1E9;1109 116C 11B0;C1E9;1109 116C 11B0; # (쇩; 쇩; 쇩; 쇩; 쇩; ) HANGUL SYLLABLE SOELG +C1EA;C1EA;1109 116C 11B1;C1EA;1109 116C 11B1; # (쇪; 쇪; 쇪; 쇪; 쇪; ) HANGUL SYLLABLE SOELM +C1EB;C1EB;1109 116C 11B2;C1EB;1109 116C 11B2; # (쇫; 쇫; 쇫; 쇫; 쇫; ) HANGUL SYLLABLE SOELB +C1EC;C1EC;1109 116C 11B3;C1EC;1109 116C 11B3; # (쇬; 쇬; 쇬; 쇬; 쇬; ) HANGUL SYLLABLE SOELS +C1ED;C1ED;1109 116C 11B4;C1ED;1109 116C 11B4; # (쇭; 쇭; 쇭; 쇭; 쇭; ) HANGUL SYLLABLE SOELT +C1EE;C1EE;1109 116C 11B5;C1EE;1109 116C 11B5; # (쇮; 쇮; 쇮; 쇮; 쇮; ) HANGUL SYLLABLE SOELP +C1EF;C1EF;1109 116C 11B6;C1EF;1109 116C 11B6; # (쇯; 쇯; 쇯; 쇯; 쇯; ) HANGUL SYLLABLE SOELH +C1F0;C1F0;1109 116C 11B7;C1F0;1109 116C 11B7; # (쇰; 쇰; 쇰; 쇰; 쇰; ) HANGUL SYLLABLE SOEM +C1F1;C1F1;1109 116C 11B8;C1F1;1109 116C 11B8; # (쇱; 쇱; 쇱; 쇱; 쇱; ) HANGUL SYLLABLE SOEB +C1F2;C1F2;1109 116C 11B9;C1F2;1109 116C 11B9; # (쇲; 쇲; 쇲; 쇲; 쇲; ) HANGUL SYLLABLE SOEBS +C1F3;C1F3;1109 116C 11BA;C1F3;1109 116C 11BA; # (쇳; 쇳; 쇳; 쇳; 쇳; ) HANGUL SYLLABLE SOES +C1F4;C1F4;1109 116C 11BB;C1F4;1109 116C 11BB; # (쇴; 쇴; 쇴; 쇴; 쇴; ) HANGUL SYLLABLE SOESS +C1F5;C1F5;1109 116C 11BC;C1F5;1109 116C 11BC; # (쇵; 쇵; 쇵; 쇵; 쇵; ) HANGUL SYLLABLE SOENG +C1F6;C1F6;1109 116C 11BD;C1F6;1109 116C 11BD; # (쇶; 쇶; 쇶; 쇶; 쇶; ) HANGUL SYLLABLE SOEJ +C1F7;C1F7;1109 116C 11BE;C1F7;1109 116C 11BE; # (쇷; 쇷; 쇷; 쇷; 쇷; ) HANGUL SYLLABLE SOEC +C1F8;C1F8;1109 116C 11BF;C1F8;1109 116C 11BF; # (쇸; 쇸; 쇸; 쇸; 쇸; ) HANGUL SYLLABLE SOEK +C1F9;C1F9;1109 116C 11C0;C1F9;1109 116C 11C0; # (쇹; 쇹; 쇹; 쇹; 쇹; ) HANGUL SYLLABLE SOET +C1FA;C1FA;1109 116C 11C1;C1FA;1109 116C 11C1; # (쇺; 쇺; 쇺; 쇺; 쇺; ) HANGUL SYLLABLE SOEP +C1FB;C1FB;1109 116C 11C2;C1FB;1109 116C 11C2; # (쇻; 쇻; 쇻; 쇻; 쇻; ) HANGUL SYLLABLE SOEH +C1FC;C1FC;1109 116D;C1FC;1109 116D; # (쇼; 쇼; 쇼; 쇼; 쇼; ) HANGUL SYLLABLE SYO +C1FD;C1FD;1109 116D 11A8;C1FD;1109 116D 11A8; # (쇽; 쇽; 쇽; 쇽; 쇽; ) HANGUL SYLLABLE SYOG +C1FE;C1FE;1109 116D 11A9;C1FE;1109 116D 11A9; # (쇾; 쇾; 쇾; 쇾; 쇾; ) HANGUL SYLLABLE SYOGG +C1FF;C1FF;1109 116D 11AA;C1FF;1109 116D 11AA; # (쇿; 쇿; 쇿; 쇿; 쇿; ) HANGUL SYLLABLE SYOGS +C200;C200;1109 116D 11AB;C200;1109 116D 11AB; # (숀; 숀; 숀; 숀; 숀; ) HANGUL SYLLABLE SYON +C201;C201;1109 116D 11AC;C201;1109 116D 11AC; # (숁; 숁; 숁; 숁; 숁; ) HANGUL SYLLABLE SYONJ +C202;C202;1109 116D 11AD;C202;1109 116D 11AD; # (숂; 숂; 숂; 숂; 숂; ) HANGUL SYLLABLE SYONH +C203;C203;1109 116D 11AE;C203;1109 116D 11AE; # (숃; 숃; 숃; 숃; 숃; ) HANGUL SYLLABLE SYOD +C204;C204;1109 116D 11AF;C204;1109 116D 11AF; # (숄; 숄; 숄; 숄; 숄; ) HANGUL SYLLABLE SYOL +C205;C205;1109 116D 11B0;C205;1109 116D 11B0; # (숅; 숅; 숅; 숅; 숅; ) HANGUL SYLLABLE SYOLG +C206;C206;1109 116D 11B1;C206;1109 116D 11B1; # (숆; 숆; 숆; 숆; 숆; ) HANGUL SYLLABLE SYOLM +C207;C207;1109 116D 11B2;C207;1109 116D 11B2; # (숇; 숇; 숇; 숇; 숇; ) HANGUL SYLLABLE SYOLB +C208;C208;1109 116D 11B3;C208;1109 116D 11B3; # (숈; 숈; 숈; 숈; 숈; ) HANGUL SYLLABLE SYOLS +C209;C209;1109 116D 11B4;C209;1109 116D 11B4; # (숉; 숉; 숉; 숉; 숉; ) HANGUL SYLLABLE SYOLT +C20A;C20A;1109 116D 11B5;C20A;1109 116D 11B5; # (숊; 숊; 숊; 숊; 숊; ) HANGUL SYLLABLE SYOLP +C20B;C20B;1109 116D 11B6;C20B;1109 116D 11B6; # (숋; 숋; 숋; 숋; 숋; ) HANGUL SYLLABLE SYOLH +C20C;C20C;1109 116D 11B7;C20C;1109 116D 11B7; # (숌; 숌; 숌; 숌; 숌; ) HANGUL SYLLABLE SYOM +C20D;C20D;1109 116D 11B8;C20D;1109 116D 11B8; # (숍; 숍; 숍; 숍; 숍; ) HANGUL SYLLABLE SYOB +C20E;C20E;1109 116D 11B9;C20E;1109 116D 11B9; # (숎; 숎; 숎; 숎; 숎; ) HANGUL SYLLABLE SYOBS +C20F;C20F;1109 116D 11BA;C20F;1109 116D 11BA; # (숏; 숏; 숏; 숏; 숏; ) HANGUL SYLLABLE SYOS +C210;C210;1109 116D 11BB;C210;1109 116D 11BB; # (숐; 숐; 숐; 숐; 숐; ) HANGUL SYLLABLE SYOSS +C211;C211;1109 116D 11BC;C211;1109 116D 11BC; # (숑; 숑; 숑; 숑; 숑; ) HANGUL SYLLABLE SYONG +C212;C212;1109 116D 11BD;C212;1109 116D 11BD; # (숒; 숒; 숒; 숒; 숒; ) HANGUL SYLLABLE SYOJ +C213;C213;1109 116D 11BE;C213;1109 116D 11BE; # (숓; 숓; 숓; 숓; 숓; ) HANGUL SYLLABLE SYOC +C214;C214;1109 116D 11BF;C214;1109 116D 11BF; # (숔; 숔; 숔; 숔; 숔; ) HANGUL SYLLABLE SYOK +C215;C215;1109 116D 11C0;C215;1109 116D 11C0; # (숕; 숕; 숕; 숕; 숕; ) HANGUL SYLLABLE SYOT +C216;C216;1109 116D 11C1;C216;1109 116D 11C1; # (숖; 숖; 숖; 숖; 숖; ) HANGUL SYLLABLE SYOP +C217;C217;1109 116D 11C2;C217;1109 116D 11C2; # (숗; 숗; 숗; 숗; 숗; ) HANGUL SYLLABLE SYOH +C218;C218;1109 116E;C218;1109 116E; # (수; 수; 수; 수; 수; ) HANGUL SYLLABLE SU +C219;C219;1109 116E 11A8;C219;1109 116E 11A8; # (숙; 숙; 숙; 숙; 숙; ) HANGUL SYLLABLE SUG +C21A;C21A;1109 116E 11A9;C21A;1109 116E 11A9; # (숚; 숚; 숚; 숚; 숚; ) HANGUL SYLLABLE SUGG +C21B;C21B;1109 116E 11AA;C21B;1109 116E 11AA; # (숛; 숛; 숛; 숛; 숛; ) HANGUL SYLLABLE SUGS +C21C;C21C;1109 116E 11AB;C21C;1109 116E 11AB; # (순; 순; 순; 순; 순; ) HANGUL SYLLABLE SUN +C21D;C21D;1109 116E 11AC;C21D;1109 116E 11AC; # (숝; 숝; 숝; 숝; 숝; ) HANGUL SYLLABLE SUNJ +C21E;C21E;1109 116E 11AD;C21E;1109 116E 11AD; # (숞; 숞; 숞; 숞; 숞; ) HANGUL SYLLABLE SUNH +C21F;C21F;1109 116E 11AE;C21F;1109 116E 11AE; # (숟; 숟; 숟; 숟; 숟; ) HANGUL SYLLABLE SUD +C220;C220;1109 116E 11AF;C220;1109 116E 11AF; # (술; 술; 술; 술; 술; ) HANGUL SYLLABLE SUL +C221;C221;1109 116E 11B0;C221;1109 116E 11B0; # (숡; 숡; 숡; 숡; 숡; ) HANGUL SYLLABLE SULG +C222;C222;1109 116E 11B1;C222;1109 116E 11B1; # (숢; 숢; 숢; 숢; 숢; ) HANGUL SYLLABLE SULM +C223;C223;1109 116E 11B2;C223;1109 116E 11B2; # (숣; 숣; 숣; 숣; 숣; ) HANGUL SYLLABLE SULB +C224;C224;1109 116E 11B3;C224;1109 116E 11B3; # (숤; 숤; 숤; 숤; 숤; ) HANGUL SYLLABLE SULS +C225;C225;1109 116E 11B4;C225;1109 116E 11B4; # (숥; 숥; 숥; 숥; 숥; ) HANGUL SYLLABLE SULT +C226;C226;1109 116E 11B5;C226;1109 116E 11B5; # (숦; 숦; 숦; 숦; 숦; ) HANGUL SYLLABLE SULP +C227;C227;1109 116E 11B6;C227;1109 116E 11B6; # (숧; 숧; 숧; 숧; 숧; ) HANGUL SYLLABLE SULH +C228;C228;1109 116E 11B7;C228;1109 116E 11B7; # (숨; 숨; 숨; 숨; 숨; ) HANGUL SYLLABLE SUM +C229;C229;1109 116E 11B8;C229;1109 116E 11B8; # (숩; 숩; 숩; 숩; 숩; ) HANGUL SYLLABLE SUB +C22A;C22A;1109 116E 11B9;C22A;1109 116E 11B9; # (숪; 숪; 숪; 숪; 숪; ) HANGUL SYLLABLE SUBS +C22B;C22B;1109 116E 11BA;C22B;1109 116E 11BA; # (숫; 숫; 숫; 숫; 숫; ) HANGUL SYLLABLE SUS +C22C;C22C;1109 116E 11BB;C22C;1109 116E 11BB; # (숬; 숬; 숬; 숬; 숬; ) HANGUL SYLLABLE SUSS +C22D;C22D;1109 116E 11BC;C22D;1109 116E 11BC; # (숭; 숭; 숭; 숭; 숭; ) HANGUL SYLLABLE SUNG +C22E;C22E;1109 116E 11BD;C22E;1109 116E 11BD; # (숮; 숮; 숮; 숮; 숮; ) HANGUL SYLLABLE SUJ +C22F;C22F;1109 116E 11BE;C22F;1109 116E 11BE; # (숯; 숯; 숯; 숯; 숯; ) HANGUL SYLLABLE SUC +C230;C230;1109 116E 11BF;C230;1109 116E 11BF; # (숰; 숰; 숰; 숰; 숰; ) HANGUL SYLLABLE SUK +C231;C231;1109 116E 11C0;C231;1109 116E 11C0; # (숱; 숱; 숱; 숱; 숱; ) HANGUL SYLLABLE SUT +C232;C232;1109 116E 11C1;C232;1109 116E 11C1; # (숲; 숲; 숲; 숲; 숲; ) HANGUL SYLLABLE SUP +C233;C233;1109 116E 11C2;C233;1109 116E 11C2; # (숳; 숳; 숳; 숳; 숳; ) HANGUL SYLLABLE SUH +C234;C234;1109 116F;C234;1109 116F; # (숴; 숴; 숴; 숴; 숴; ) HANGUL SYLLABLE SWEO +C235;C235;1109 116F 11A8;C235;1109 116F 11A8; # (숵; 숵; 숵; 숵; 숵; ) HANGUL SYLLABLE SWEOG +C236;C236;1109 116F 11A9;C236;1109 116F 11A9; # (숶; 숶; 숶; 숶; 숶; ) HANGUL SYLLABLE SWEOGG +C237;C237;1109 116F 11AA;C237;1109 116F 11AA; # (숷; 숷; 숷; 숷; 숷; ) HANGUL SYLLABLE SWEOGS +C238;C238;1109 116F 11AB;C238;1109 116F 11AB; # (숸; 숸; 숸; 숸; 숸; ) HANGUL SYLLABLE SWEON +C239;C239;1109 116F 11AC;C239;1109 116F 11AC; # (숹; 숹; 숹; 숹; 숹; ) HANGUL SYLLABLE SWEONJ +C23A;C23A;1109 116F 11AD;C23A;1109 116F 11AD; # (숺; 숺; 숺; 숺; 숺; ) HANGUL SYLLABLE SWEONH +C23B;C23B;1109 116F 11AE;C23B;1109 116F 11AE; # (숻; 숻; 숻; 숻; 숻; ) HANGUL SYLLABLE SWEOD +C23C;C23C;1109 116F 11AF;C23C;1109 116F 11AF; # (숼; 숼; 숼; 숼; 숼; ) HANGUL SYLLABLE SWEOL +C23D;C23D;1109 116F 11B0;C23D;1109 116F 11B0; # (숽; 숽; 숽; 숽; 숽; ) HANGUL SYLLABLE SWEOLG +C23E;C23E;1109 116F 11B1;C23E;1109 116F 11B1; # (숾; 숾; 숾; 숾; 숾; ) HANGUL SYLLABLE SWEOLM +C23F;C23F;1109 116F 11B2;C23F;1109 116F 11B2; # (숿; 숿; 숿; 숿; 숿; ) HANGUL SYLLABLE SWEOLB +C240;C240;1109 116F 11B3;C240;1109 116F 11B3; # (쉀; 쉀; 쉀; 쉀; 쉀; ) HANGUL SYLLABLE SWEOLS +C241;C241;1109 116F 11B4;C241;1109 116F 11B4; # (쉁; 쉁; 쉁; 쉁; 쉁; ) HANGUL SYLLABLE SWEOLT +C242;C242;1109 116F 11B5;C242;1109 116F 11B5; # (쉂; 쉂; 쉂; 쉂; 쉂; ) HANGUL SYLLABLE SWEOLP +C243;C243;1109 116F 11B6;C243;1109 116F 11B6; # (쉃; 쉃; 쉃; 쉃; 쉃; ) HANGUL SYLLABLE SWEOLH +C244;C244;1109 116F 11B7;C244;1109 116F 11B7; # (쉄; 쉄; 쉄; 쉄; 쉄; ) HANGUL SYLLABLE SWEOM +C245;C245;1109 116F 11B8;C245;1109 116F 11B8; # (쉅; 쉅; 쉅; 쉅; 쉅; ) HANGUL SYLLABLE SWEOB +C246;C246;1109 116F 11B9;C246;1109 116F 11B9; # (쉆; 쉆; 쉆; 쉆; 쉆; ) HANGUL SYLLABLE SWEOBS +C247;C247;1109 116F 11BA;C247;1109 116F 11BA; # (쉇; 쉇; 쉇; 쉇; 쉇; ) HANGUL SYLLABLE SWEOS +C248;C248;1109 116F 11BB;C248;1109 116F 11BB; # (쉈; 쉈; 쉈; 쉈; 쉈; ) HANGUL SYLLABLE SWEOSS +C249;C249;1109 116F 11BC;C249;1109 116F 11BC; # (쉉; 쉉; 쉉; 쉉; 쉉; ) HANGUL SYLLABLE SWEONG +C24A;C24A;1109 116F 11BD;C24A;1109 116F 11BD; # (쉊; 쉊; 쉊; 쉊; 쉊; ) HANGUL SYLLABLE SWEOJ +C24B;C24B;1109 116F 11BE;C24B;1109 116F 11BE; # (쉋; 쉋; 쉋; 쉋; 쉋; ) HANGUL SYLLABLE SWEOC +C24C;C24C;1109 116F 11BF;C24C;1109 116F 11BF; # (쉌; 쉌; 쉌; 쉌; 쉌; ) HANGUL SYLLABLE SWEOK +C24D;C24D;1109 116F 11C0;C24D;1109 116F 11C0; # (쉍; 쉍; 쉍; 쉍; 쉍; ) HANGUL SYLLABLE SWEOT +C24E;C24E;1109 116F 11C1;C24E;1109 116F 11C1; # (쉎; 쉎; 쉎; 쉎; 쉎; ) HANGUL SYLLABLE SWEOP +C24F;C24F;1109 116F 11C2;C24F;1109 116F 11C2; # (쉏; 쉏; 쉏; 쉏; 쉏; ) HANGUL SYLLABLE SWEOH +C250;C250;1109 1170;C250;1109 1170; # (쉐; 쉐; 쉐; 쉐; 쉐; ) HANGUL SYLLABLE SWE +C251;C251;1109 1170 11A8;C251;1109 1170 11A8; # (쉑; 쉑; 쉑; 쉑; 쉑; ) HANGUL SYLLABLE SWEG +C252;C252;1109 1170 11A9;C252;1109 1170 11A9; # (쉒; 쉒; 쉒; 쉒; 쉒; ) HANGUL SYLLABLE SWEGG +C253;C253;1109 1170 11AA;C253;1109 1170 11AA; # (쉓; 쉓; 쉓; 쉓; 쉓; ) HANGUL SYLLABLE SWEGS +C254;C254;1109 1170 11AB;C254;1109 1170 11AB; # (쉔; 쉔; 쉔; 쉔; 쉔; ) HANGUL SYLLABLE SWEN +C255;C255;1109 1170 11AC;C255;1109 1170 11AC; # (쉕; 쉕; 쉕; 쉕; 쉕; ) HANGUL SYLLABLE SWENJ +C256;C256;1109 1170 11AD;C256;1109 1170 11AD; # (쉖; 쉖; 쉖; 쉖; 쉖; ) HANGUL SYLLABLE SWENH +C257;C257;1109 1170 11AE;C257;1109 1170 11AE; # (쉗; 쉗; 쉗; 쉗; 쉗; ) HANGUL SYLLABLE SWED +C258;C258;1109 1170 11AF;C258;1109 1170 11AF; # (쉘; 쉘; 쉘; 쉘; 쉘; ) HANGUL SYLLABLE SWEL +C259;C259;1109 1170 11B0;C259;1109 1170 11B0; # (쉙; 쉙; 쉙; 쉙; 쉙; ) HANGUL SYLLABLE SWELG +C25A;C25A;1109 1170 11B1;C25A;1109 1170 11B1; # (쉚; 쉚; 쉚; 쉚; 쉚; ) HANGUL SYLLABLE SWELM +C25B;C25B;1109 1170 11B2;C25B;1109 1170 11B2; # (쉛; 쉛; 쉛; 쉛; 쉛; ) HANGUL SYLLABLE SWELB +C25C;C25C;1109 1170 11B3;C25C;1109 1170 11B3; # (쉜; 쉜; 쉜; 쉜; 쉜; ) HANGUL SYLLABLE SWELS +C25D;C25D;1109 1170 11B4;C25D;1109 1170 11B4; # (쉝; 쉝; 쉝; 쉝; 쉝; ) HANGUL SYLLABLE SWELT +C25E;C25E;1109 1170 11B5;C25E;1109 1170 11B5; # (쉞; 쉞; 쉞; 쉞; 쉞; ) HANGUL SYLLABLE SWELP +C25F;C25F;1109 1170 11B6;C25F;1109 1170 11B6; # (쉟; 쉟; 쉟; 쉟; 쉟; ) HANGUL SYLLABLE SWELH +C260;C260;1109 1170 11B7;C260;1109 1170 11B7; # (쉠; 쉠; 쉠; 쉠; 쉠; ) HANGUL SYLLABLE SWEM +C261;C261;1109 1170 11B8;C261;1109 1170 11B8; # (쉡; 쉡; 쉡; 쉡; 쉡; ) HANGUL SYLLABLE SWEB +C262;C262;1109 1170 11B9;C262;1109 1170 11B9; # (쉢; 쉢; 쉢; 쉢; 쉢; ) HANGUL SYLLABLE SWEBS +C263;C263;1109 1170 11BA;C263;1109 1170 11BA; # (쉣; 쉣; 쉣; 쉣; 쉣; ) HANGUL SYLLABLE SWES +C264;C264;1109 1170 11BB;C264;1109 1170 11BB; # (쉤; 쉤; 쉤; 쉤; 쉤; ) HANGUL SYLLABLE SWESS +C265;C265;1109 1170 11BC;C265;1109 1170 11BC; # (쉥; 쉥; 쉥; 쉥; 쉥; ) HANGUL SYLLABLE SWENG +C266;C266;1109 1170 11BD;C266;1109 1170 11BD; # (쉦; 쉦; 쉦; 쉦; 쉦; ) HANGUL SYLLABLE SWEJ +C267;C267;1109 1170 11BE;C267;1109 1170 11BE; # (쉧; 쉧; 쉧; 쉧; 쉧; ) HANGUL SYLLABLE SWEC +C268;C268;1109 1170 11BF;C268;1109 1170 11BF; # (쉨; 쉨; 쉨; 쉨; 쉨; ) HANGUL SYLLABLE SWEK +C269;C269;1109 1170 11C0;C269;1109 1170 11C0; # (쉩; 쉩; 쉩; 쉩; 쉩; ) HANGUL SYLLABLE SWET +C26A;C26A;1109 1170 11C1;C26A;1109 1170 11C1; # (쉪; 쉪; 쉪; 쉪; 쉪; ) HANGUL SYLLABLE SWEP +C26B;C26B;1109 1170 11C2;C26B;1109 1170 11C2; # (쉫; 쉫; 쉫; 쉫; 쉫; ) HANGUL SYLLABLE SWEH +C26C;C26C;1109 1171;C26C;1109 1171; # (쉬; 쉬; 쉬; 쉬; 쉬; ) HANGUL SYLLABLE SWI +C26D;C26D;1109 1171 11A8;C26D;1109 1171 11A8; # (쉭; 쉭; 쉭; 쉭; 쉭; ) HANGUL SYLLABLE SWIG +C26E;C26E;1109 1171 11A9;C26E;1109 1171 11A9; # (쉮; 쉮; 쉮; 쉮; 쉮; ) HANGUL SYLLABLE SWIGG +C26F;C26F;1109 1171 11AA;C26F;1109 1171 11AA; # (쉯; 쉯; 쉯; 쉯; 쉯; ) HANGUL SYLLABLE SWIGS +C270;C270;1109 1171 11AB;C270;1109 1171 11AB; # (쉰; 쉰; 쉰; 쉰; 쉰; ) HANGUL SYLLABLE SWIN +C271;C271;1109 1171 11AC;C271;1109 1171 11AC; # (쉱; 쉱; 쉱; 쉱; 쉱; ) HANGUL SYLLABLE SWINJ +C272;C272;1109 1171 11AD;C272;1109 1171 11AD; # (쉲; 쉲; 쉲; 쉲; 쉲; ) HANGUL SYLLABLE SWINH +C273;C273;1109 1171 11AE;C273;1109 1171 11AE; # (쉳; 쉳; 쉳; 쉳; 쉳; ) HANGUL SYLLABLE SWID +C274;C274;1109 1171 11AF;C274;1109 1171 11AF; # (쉴; 쉴; 쉴; 쉴; 쉴; ) HANGUL SYLLABLE SWIL +C275;C275;1109 1171 11B0;C275;1109 1171 11B0; # (쉵; 쉵; 쉵; 쉵; 쉵; ) HANGUL SYLLABLE SWILG +C276;C276;1109 1171 11B1;C276;1109 1171 11B1; # (쉶; 쉶; 쉶; 쉶; 쉶; ) HANGUL SYLLABLE SWILM +C277;C277;1109 1171 11B2;C277;1109 1171 11B2; # (쉷; 쉷; 쉷; 쉷; 쉷; ) HANGUL SYLLABLE SWILB +C278;C278;1109 1171 11B3;C278;1109 1171 11B3; # (쉸; 쉸; 쉸; 쉸; 쉸; ) HANGUL SYLLABLE SWILS +C279;C279;1109 1171 11B4;C279;1109 1171 11B4; # (쉹; 쉹; 쉹; 쉹; 쉹; ) HANGUL SYLLABLE SWILT +C27A;C27A;1109 1171 11B5;C27A;1109 1171 11B5; # (쉺; 쉺; 쉺; 쉺; 쉺; ) HANGUL SYLLABLE SWILP +C27B;C27B;1109 1171 11B6;C27B;1109 1171 11B6; # (쉻; 쉻; 쉻; 쉻; 쉻; ) HANGUL SYLLABLE SWILH +C27C;C27C;1109 1171 11B7;C27C;1109 1171 11B7; # (쉼; 쉼; 쉼; 쉼; 쉼; ) HANGUL SYLLABLE SWIM +C27D;C27D;1109 1171 11B8;C27D;1109 1171 11B8; # (쉽; 쉽; 쉽; 쉽; 쉽; ) HANGUL SYLLABLE SWIB +C27E;C27E;1109 1171 11B9;C27E;1109 1171 11B9; # (쉾; 쉾; 쉾; 쉾; 쉾; ) HANGUL SYLLABLE SWIBS +C27F;C27F;1109 1171 11BA;C27F;1109 1171 11BA; # (쉿; 쉿; 쉿; 쉿; 쉿; ) HANGUL SYLLABLE SWIS +C280;C280;1109 1171 11BB;C280;1109 1171 11BB; # (슀; 슀; 슀; 슀; 슀; ) HANGUL SYLLABLE SWISS +C281;C281;1109 1171 11BC;C281;1109 1171 11BC; # (슁; 슁; 슁; 슁; 슁; ) HANGUL SYLLABLE SWING +C282;C282;1109 1171 11BD;C282;1109 1171 11BD; # (슂; 슂; 슂; 슂; 슂; ) HANGUL SYLLABLE SWIJ +C283;C283;1109 1171 11BE;C283;1109 1171 11BE; # (슃; 슃; 슃; 슃; 슃; ) HANGUL SYLLABLE SWIC +C284;C284;1109 1171 11BF;C284;1109 1171 11BF; # (슄; 슄; 슄; 슄; 슄; ) HANGUL SYLLABLE SWIK +C285;C285;1109 1171 11C0;C285;1109 1171 11C0; # (슅; 슅; 슅; 슅; 슅; ) HANGUL SYLLABLE SWIT +C286;C286;1109 1171 11C1;C286;1109 1171 11C1; # (슆; 슆; 슆; 슆; 슆; ) HANGUL SYLLABLE SWIP +C287;C287;1109 1171 11C2;C287;1109 1171 11C2; # (슇; 슇; 슇; 슇; 슇; ) HANGUL SYLLABLE SWIH +C288;C288;1109 1172;C288;1109 1172; # (슈; 슈; 슈; 슈; 슈; ) HANGUL SYLLABLE SYU +C289;C289;1109 1172 11A8;C289;1109 1172 11A8; # (슉; 슉; 슉; 슉; 슉; ) HANGUL SYLLABLE SYUG +C28A;C28A;1109 1172 11A9;C28A;1109 1172 11A9; # (슊; 슊; 슊; 슊; 슊; ) HANGUL SYLLABLE SYUGG +C28B;C28B;1109 1172 11AA;C28B;1109 1172 11AA; # (슋; 슋; 슋; 슋; 슋; ) HANGUL SYLLABLE SYUGS +C28C;C28C;1109 1172 11AB;C28C;1109 1172 11AB; # (슌; 슌; 슌; 슌; 슌; ) HANGUL SYLLABLE SYUN +C28D;C28D;1109 1172 11AC;C28D;1109 1172 11AC; # (슍; 슍; 슍; 슍; 슍; ) HANGUL SYLLABLE SYUNJ +C28E;C28E;1109 1172 11AD;C28E;1109 1172 11AD; # (슎; 슎; 슎; 슎; 슎; ) HANGUL SYLLABLE SYUNH +C28F;C28F;1109 1172 11AE;C28F;1109 1172 11AE; # (슏; 슏; 슏; 슏; 슏; ) HANGUL SYLLABLE SYUD +C290;C290;1109 1172 11AF;C290;1109 1172 11AF; # (슐; 슐; 슐; 슐; 슐; ) HANGUL SYLLABLE SYUL +C291;C291;1109 1172 11B0;C291;1109 1172 11B0; # (슑; 슑; 슑; 슑; 슑; ) HANGUL SYLLABLE SYULG +C292;C292;1109 1172 11B1;C292;1109 1172 11B1; # (슒; 슒; 슒; 슒; 슒; ) HANGUL SYLLABLE SYULM +C293;C293;1109 1172 11B2;C293;1109 1172 11B2; # (슓; 슓; 슓; 슓; 슓; ) HANGUL SYLLABLE SYULB +C294;C294;1109 1172 11B3;C294;1109 1172 11B3; # (슔; 슔; 슔; 슔; 슔; ) HANGUL SYLLABLE SYULS +C295;C295;1109 1172 11B4;C295;1109 1172 11B4; # (슕; 슕; 슕; 슕; 슕; ) HANGUL SYLLABLE SYULT +C296;C296;1109 1172 11B5;C296;1109 1172 11B5; # (슖; 슖; 슖; 슖; 슖; ) HANGUL SYLLABLE SYULP +C297;C297;1109 1172 11B6;C297;1109 1172 11B6; # (슗; 슗; 슗; 슗; 슗; ) HANGUL SYLLABLE SYULH +C298;C298;1109 1172 11B7;C298;1109 1172 11B7; # (슘; 슘; 슘; 슘; 슘; ) HANGUL SYLLABLE SYUM +C299;C299;1109 1172 11B8;C299;1109 1172 11B8; # (슙; 슙; 슙; 슙; 슙; ) HANGUL SYLLABLE SYUB +C29A;C29A;1109 1172 11B9;C29A;1109 1172 11B9; # (슚; 슚; 슚; 슚; 슚; ) HANGUL SYLLABLE SYUBS +C29B;C29B;1109 1172 11BA;C29B;1109 1172 11BA; # (슛; 슛; 슛; 슛; 슛; ) HANGUL SYLLABLE SYUS +C29C;C29C;1109 1172 11BB;C29C;1109 1172 11BB; # (슜; 슜; 슜; 슜; 슜; ) HANGUL SYLLABLE SYUSS +C29D;C29D;1109 1172 11BC;C29D;1109 1172 11BC; # (슝; 슝; 슝; 슝; 슝; ) HANGUL SYLLABLE SYUNG +C29E;C29E;1109 1172 11BD;C29E;1109 1172 11BD; # (슞; 슞; 슞; 슞; 슞; ) HANGUL SYLLABLE SYUJ +C29F;C29F;1109 1172 11BE;C29F;1109 1172 11BE; # (슟; 슟; 슟; 슟; 슟; ) HANGUL SYLLABLE SYUC +C2A0;C2A0;1109 1172 11BF;C2A0;1109 1172 11BF; # (슠; 슠; 슠; 슠; 슠; ) HANGUL SYLLABLE SYUK +C2A1;C2A1;1109 1172 11C0;C2A1;1109 1172 11C0; # (슡; 슡; 슡; 슡; 슡; ) HANGUL SYLLABLE SYUT +C2A2;C2A2;1109 1172 11C1;C2A2;1109 1172 11C1; # (슢; 슢; 슢; 슢; 슢; ) HANGUL SYLLABLE SYUP +C2A3;C2A3;1109 1172 11C2;C2A3;1109 1172 11C2; # (슣; 슣; 슣; 슣; 슣; ) HANGUL SYLLABLE SYUH +C2A4;C2A4;1109 1173;C2A4;1109 1173; # (스; 스; 스; 스; 스; ) HANGUL SYLLABLE SEU +C2A5;C2A5;1109 1173 11A8;C2A5;1109 1173 11A8; # (슥; 슥; 슥; 슥; 슥; ) HANGUL SYLLABLE SEUG +C2A6;C2A6;1109 1173 11A9;C2A6;1109 1173 11A9; # (슦; 슦; 슦; 슦; 슦; ) HANGUL SYLLABLE SEUGG +C2A7;C2A7;1109 1173 11AA;C2A7;1109 1173 11AA; # (슧; 슧; 슧; 슧; 슧; ) HANGUL SYLLABLE SEUGS +C2A8;C2A8;1109 1173 11AB;C2A8;1109 1173 11AB; # (슨; 슨; 슨; 슨; 슨; ) HANGUL SYLLABLE SEUN +C2A9;C2A9;1109 1173 11AC;C2A9;1109 1173 11AC; # (슩; 슩; 슩; 슩; 슩; ) HANGUL SYLLABLE SEUNJ +C2AA;C2AA;1109 1173 11AD;C2AA;1109 1173 11AD; # (슪; 슪; 슪; 슪; 슪; ) HANGUL SYLLABLE SEUNH +C2AB;C2AB;1109 1173 11AE;C2AB;1109 1173 11AE; # (슫; 슫; 슫; 슫; 슫; ) HANGUL SYLLABLE SEUD +C2AC;C2AC;1109 1173 11AF;C2AC;1109 1173 11AF; # (슬; 슬; 슬; 슬; 슬; ) HANGUL SYLLABLE SEUL +C2AD;C2AD;1109 1173 11B0;C2AD;1109 1173 11B0; # (슭; 슭; 슭; 슭; 슭; ) HANGUL SYLLABLE SEULG +C2AE;C2AE;1109 1173 11B1;C2AE;1109 1173 11B1; # (슮; 슮; 슮; 슮; 슮; ) HANGUL SYLLABLE SEULM +C2AF;C2AF;1109 1173 11B2;C2AF;1109 1173 11B2; # (슯; 슯; 슯; 슯; 슯; ) HANGUL SYLLABLE SEULB +C2B0;C2B0;1109 1173 11B3;C2B0;1109 1173 11B3; # (슰; 슰; 슰; 슰; 슰; ) HANGUL SYLLABLE SEULS +C2B1;C2B1;1109 1173 11B4;C2B1;1109 1173 11B4; # (슱; 슱; 슱; 슱; 슱; ) HANGUL SYLLABLE SEULT +C2B2;C2B2;1109 1173 11B5;C2B2;1109 1173 11B5; # (슲; 슲; 슲; 슲; 슲; ) HANGUL SYLLABLE SEULP +C2B3;C2B3;1109 1173 11B6;C2B3;1109 1173 11B6; # (슳; 슳; 슳; 슳; 슳; ) HANGUL SYLLABLE SEULH +C2B4;C2B4;1109 1173 11B7;C2B4;1109 1173 11B7; # (슴; 슴; 슴; 슴; 슴; ) HANGUL SYLLABLE SEUM +C2B5;C2B5;1109 1173 11B8;C2B5;1109 1173 11B8; # (습; 습; 습; 습; 습; ) HANGUL SYLLABLE SEUB +C2B6;C2B6;1109 1173 11B9;C2B6;1109 1173 11B9; # (슶; 슶; 슶; 슶; 슶; ) HANGUL SYLLABLE SEUBS +C2B7;C2B7;1109 1173 11BA;C2B7;1109 1173 11BA; # (슷; 슷; 슷; 슷; 슷; ) HANGUL SYLLABLE SEUS +C2B8;C2B8;1109 1173 11BB;C2B8;1109 1173 11BB; # (슸; 슸; 슸; 슸; 슸; ) HANGUL SYLLABLE SEUSS +C2B9;C2B9;1109 1173 11BC;C2B9;1109 1173 11BC; # (승; 승; 승; 승; 승; ) HANGUL SYLLABLE SEUNG +C2BA;C2BA;1109 1173 11BD;C2BA;1109 1173 11BD; # (슺; 슺; 슺; 슺; 슺; ) HANGUL SYLLABLE SEUJ +C2BB;C2BB;1109 1173 11BE;C2BB;1109 1173 11BE; # (슻; 슻; 슻; 슻; 슻; ) HANGUL SYLLABLE SEUC +C2BC;C2BC;1109 1173 11BF;C2BC;1109 1173 11BF; # (슼; 슼; 슼; 슼; 슼; ) HANGUL SYLLABLE SEUK +C2BD;C2BD;1109 1173 11C0;C2BD;1109 1173 11C0; # (슽; 슽; 슽; 슽; 슽; ) HANGUL SYLLABLE SEUT +C2BE;C2BE;1109 1173 11C1;C2BE;1109 1173 11C1; # (슾; 슾; 슾; 슾; 슾; ) HANGUL SYLLABLE SEUP +C2BF;C2BF;1109 1173 11C2;C2BF;1109 1173 11C2; # (슿; 슿; 슿; 슿; 슿; ) HANGUL SYLLABLE SEUH +C2C0;C2C0;1109 1174;C2C0;1109 1174; # (싀; 싀; 싀; 싀; 싀; ) HANGUL SYLLABLE SYI +C2C1;C2C1;1109 1174 11A8;C2C1;1109 1174 11A8; # (싁; 싁; 싁; 싁; 싁; ) HANGUL SYLLABLE SYIG +C2C2;C2C2;1109 1174 11A9;C2C2;1109 1174 11A9; # (싂; 싂; 싂; 싂; 싂; ) HANGUL SYLLABLE SYIGG +C2C3;C2C3;1109 1174 11AA;C2C3;1109 1174 11AA; # (싃; 싃; 싃; 싃; 싃; ) HANGUL SYLLABLE SYIGS +C2C4;C2C4;1109 1174 11AB;C2C4;1109 1174 11AB; # (싄; 싄; 싄; 싄; 싄; ) HANGUL SYLLABLE SYIN +C2C5;C2C5;1109 1174 11AC;C2C5;1109 1174 11AC; # (싅; 싅; 싅; 싅; 싅; ) HANGUL SYLLABLE SYINJ +C2C6;C2C6;1109 1174 11AD;C2C6;1109 1174 11AD; # (싆; 싆; 싆; 싆; 싆; ) HANGUL SYLLABLE SYINH +C2C7;C2C7;1109 1174 11AE;C2C7;1109 1174 11AE; # (싇; 싇; 싇; 싇; 싇; ) HANGUL SYLLABLE SYID +C2C8;C2C8;1109 1174 11AF;C2C8;1109 1174 11AF; # (싈; 싈; 싈; 싈; 싈; ) HANGUL SYLLABLE SYIL +C2C9;C2C9;1109 1174 11B0;C2C9;1109 1174 11B0; # (싉; 싉; 싉; 싉; 싉; ) HANGUL SYLLABLE SYILG +C2CA;C2CA;1109 1174 11B1;C2CA;1109 1174 11B1; # (싊; 싊; 싊; 싊; 싊; ) HANGUL SYLLABLE SYILM +C2CB;C2CB;1109 1174 11B2;C2CB;1109 1174 11B2; # (싋; 싋; 싋; 싋; 싋; ) HANGUL SYLLABLE SYILB +C2CC;C2CC;1109 1174 11B3;C2CC;1109 1174 11B3; # (싌; 싌; 싌; 싌; 싌; ) HANGUL SYLLABLE SYILS +C2CD;C2CD;1109 1174 11B4;C2CD;1109 1174 11B4; # (싍; 싍; 싍; 싍; 싍; ) HANGUL SYLLABLE SYILT +C2CE;C2CE;1109 1174 11B5;C2CE;1109 1174 11B5; # (싎; 싎; 싎; 싎; 싎; ) HANGUL SYLLABLE SYILP +C2CF;C2CF;1109 1174 11B6;C2CF;1109 1174 11B6; # (싏; 싏; 싏; 싏; 싏; ) HANGUL SYLLABLE SYILH +C2D0;C2D0;1109 1174 11B7;C2D0;1109 1174 11B7; # (싐; 싐; 싐; 싐; 싐; ) HANGUL SYLLABLE SYIM +C2D1;C2D1;1109 1174 11B8;C2D1;1109 1174 11B8; # (싑; 싑; 싑; 싑; 싑; ) HANGUL SYLLABLE SYIB +C2D2;C2D2;1109 1174 11B9;C2D2;1109 1174 11B9; # (싒; 싒; 싒; 싒; 싒; ) HANGUL SYLLABLE SYIBS +C2D3;C2D3;1109 1174 11BA;C2D3;1109 1174 11BA; # (싓; 싓; 싓; 싓; 싓; ) HANGUL SYLLABLE SYIS +C2D4;C2D4;1109 1174 11BB;C2D4;1109 1174 11BB; # (싔; 싔; 싔; 싔; 싔; ) HANGUL SYLLABLE SYISS +C2D5;C2D5;1109 1174 11BC;C2D5;1109 1174 11BC; # (싕; 싕; 싕; 싕; 싕; ) HANGUL SYLLABLE SYING +C2D6;C2D6;1109 1174 11BD;C2D6;1109 1174 11BD; # (싖; 싖; 싖; 싖; 싖; ) HANGUL SYLLABLE SYIJ +C2D7;C2D7;1109 1174 11BE;C2D7;1109 1174 11BE; # (싗; 싗; 싗; 싗; 싗; ) HANGUL SYLLABLE SYIC +C2D8;C2D8;1109 1174 11BF;C2D8;1109 1174 11BF; # (싘; 싘; 싘; 싘; 싘; ) HANGUL SYLLABLE SYIK +C2D9;C2D9;1109 1174 11C0;C2D9;1109 1174 11C0; # (싙; 싙; 싙; 싙; 싙; ) HANGUL SYLLABLE SYIT +C2DA;C2DA;1109 1174 11C1;C2DA;1109 1174 11C1; # (싚; 싚; 싚; 싚; 싚; ) HANGUL SYLLABLE SYIP +C2DB;C2DB;1109 1174 11C2;C2DB;1109 1174 11C2; # (싛; 싛; 싛; 싛; 싛; ) HANGUL SYLLABLE SYIH +C2DC;C2DC;1109 1175;C2DC;1109 1175; # (시; 시; 시; 시; 시; ) HANGUL SYLLABLE SI +C2DD;C2DD;1109 1175 11A8;C2DD;1109 1175 11A8; # (식; 식; 식; 식; 식; ) HANGUL SYLLABLE SIG +C2DE;C2DE;1109 1175 11A9;C2DE;1109 1175 11A9; # (싞; 싞; 싞; 싞; 싞; ) HANGUL SYLLABLE SIGG +C2DF;C2DF;1109 1175 11AA;C2DF;1109 1175 11AA; # (싟; 싟; 싟; 싟; 싟; ) HANGUL SYLLABLE SIGS +C2E0;C2E0;1109 1175 11AB;C2E0;1109 1175 11AB; # (신; 신; 신; 신; 신; ) HANGUL SYLLABLE SIN +C2E1;C2E1;1109 1175 11AC;C2E1;1109 1175 11AC; # (싡; 싡; 싡; 싡; 싡; ) HANGUL SYLLABLE SINJ +C2E2;C2E2;1109 1175 11AD;C2E2;1109 1175 11AD; # (싢; 싢; 싢; 싢; 싢; ) HANGUL SYLLABLE SINH +C2E3;C2E3;1109 1175 11AE;C2E3;1109 1175 11AE; # (싣; 싣; 싣; 싣; 싣; ) HANGUL SYLLABLE SID +C2E4;C2E4;1109 1175 11AF;C2E4;1109 1175 11AF; # (실; 실; 실; 실; 실; ) HANGUL SYLLABLE SIL +C2E5;C2E5;1109 1175 11B0;C2E5;1109 1175 11B0; # (싥; 싥; 싥; 싥; 싥; ) HANGUL SYLLABLE SILG +C2E6;C2E6;1109 1175 11B1;C2E6;1109 1175 11B1; # (싦; 싦; 싦; 싦; 싦; ) HANGUL SYLLABLE SILM +C2E7;C2E7;1109 1175 11B2;C2E7;1109 1175 11B2; # (싧; 싧; 싧; 싧; 싧; ) HANGUL SYLLABLE SILB +C2E8;C2E8;1109 1175 11B3;C2E8;1109 1175 11B3; # (싨; 싨; 싨; 싨; 싨; ) HANGUL SYLLABLE SILS +C2E9;C2E9;1109 1175 11B4;C2E9;1109 1175 11B4; # (싩; 싩; 싩; 싩; 싩; ) HANGUL SYLLABLE SILT +C2EA;C2EA;1109 1175 11B5;C2EA;1109 1175 11B5; # (싪; 싪; 싪; 싪; 싪; ) HANGUL SYLLABLE SILP +C2EB;C2EB;1109 1175 11B6;C2EB;1109 1175 11B6; # (싫; 싫; 싫; 싫; 싫; ) HANGUL SYLLABLE SILH +C2EC;C2EC;1109 1175 11B7;C2EC;1109 1175 11B7; # (심; 심; 심; 심; 심; ) HANGUL SYLLABLE SIM +C2ED;C2ED;1109 1175 11B8;C2ED;1109 1175 11B8; # (십; 십; 십; 십; 십; ) HANGUL SYLLABLE SIB +C2EE;C2EE;1109 1175 11B9;C2EE;1109 1175 11B9; # (싮; 싮; 싮; 싮; 싮; ) HANGUL SYLLABLE SIBS +C2EF;C2EF;1109 1175 11BA;C2EF;1109 1175 11BA; # (싯; 싯; 싯; 싯; 싯; ) HANGUL SYLLABLE SIS +C2F0;C2F0;1109 1175 11BB;C2F0;1109 1175 11BB; # (싰; 싰; 싰; 싰; 싰; ) HANGUL SYLLABLE SISS +C2F1;C2F1;1109 1175 11BC;C2F1;1109 1175 11BC; # (싱; 싱; 싱; 싱; 싱; ) HANGUL SYLLABLE SING +C2F2;C2F2;1109 1175 11BD;C2F2;1109 1175 11BD; # (싲; 싲; 싲; 싲; 싲; ) HANGUL SYLLABLE SIJ +C2F3;C2F3;1109 1175 11BE;C2F3;1109 1175 11BE; # (싳; 싳; 싳; 싳; 싳; ) HANGUL SYLLABLE SIC +C2F4;C2F4;1109 1175 11BF;C2F4;1109 1175 11BF; # (싴; 싴; 싴; 싴; 싴; ) HANGUL SYLLABLE SIK +C2F5;C2F5;1109 1175 11C0;C2F5;1109 1175 11C0; # (싵; 싵; 싵; 싵; 싵; ) HANGUL SYLLABLE SIT +C2F6;C2F6;1109 1175 11C1;C2F6;1109 1175 11C1; # (싶; 싶; 싶; 싶; 싶; ) HANGUL SYLLABLE SIP +C2F7;C2F7;1109 1175 11C2;C2F7;1109 1175 11C2; # (싷; 싷; 싷; 싷; 싷; ) HANGUL SYLLABLE SIH +C2F8;C2F8;110A 1161;C2F8;110A 1161; # (싸; 싸; 싸; 싸; 싸; ) HANGUL SYLLABLE SSA +C2F9;C2F9;110A 1161 11A8;C2F9;110A 1161 11A8; # (싹; 싹; 싹; 싹; 싹; ) HANGUL SYLLABLE SSAG +C2FA;C2FA;110A 1161 11A9;C2FA;110A 1161 11A9; # (싺; 싺; 싺; 싺; 싺; ) HANGUL SYLLABLE SSAGG +C2FB;C2FB;110A 1161 11AA;C2FB;110A 1161 11AA; # (싻; 싻; 싻; 싻; 싻; ) HANGUL SYLLABLE SSAGS +C2FC;C2FC;110A 1161 11AB;C2FC;110A 1161 11AB; # (싼; 싼; 싼; 싼; 싼; ) HANGUL SYLLABLE SSAN +C2FD;C2FD;110A 1161 11AC;C2FD;110A 1161 11AC; # (싽; 싽; 싽; 싽; 싽; ) HANGUL SYLLABLE SSANJ +C2FE;C2FE;110A 1161 11AD;C2FE;110A 1161 11AD; # (싾; 싾; 싾; 싾; 싾; ) HANGUL SYLLABLE SSANH +C2FF;C2FF;110A 1161 11AE;C2FF;110A 1161 11AE; # (싿; 싿; 싿; 싿; 싿; ) HANGUL SYLLABLE SSAD +C300;C300;110A 1161 11AF;C300;110A 1161 11AF; # (쌀; 쌀; 쌀; 쌀; 쌀; ) HANGUL SYLLABLE SSAL +C301;C301;110A 1161 11B0;C301;110A 1161 11B0; # (쌁; 쌁; 쌁; 쌁; 쌁; ) HANGUL SYLLABLE SSALG +C302;C302;110A 1161 11B1;C302;110A 1161 11B1; # (쌂; 쌂; 쌂; 쌂; 쌂; ) HANGUL SYLLABLE SSALM +C303;C303;110A 1161 11B2;C303;110A 1161 11B2; # (쌃; 쌃; 쌃; 쌃; 쌃; ) HANGUL SYLLABLE SSALB +C304;C304;110A 1161 11B3;C304;110A 1161 11B3; # (쌄; 쌄; 쌄; 쌄; 쌄; ) HANGUL SYLLABLE SSALS +C305;C305;110A 1161 11B4;C305;110A 1161 11B4; # (쌅; 쌅; 쌅; 쌅; 쌅; ) HANGUL SYLLABLE SSALT +C306;C306;110A 1161 11B5;C306;110A 1161 11B5; # (쌆; 쌆; 쌆; 쌆; 쌆; ) HANGUL SYLLABLE SSALP +C307;C307;110A 1161 11B6;C307;110A 1161 11B6; # (쌇; 쌇; 쌇; 쌇; 쌇; ) HANGUL SYLLABLE SSALH +C308;C308;110A 1161 11B7;C308;110A 1161 11B7; # (쌈; 쌈; 쌈; 쌈; 쌈; ) HANGUL SYLLABLE SSAM +C309;C309;110A 1161 11B8;C309;110A 1161 11B8; # (쌉; 쌉; 쌉; 쌉; 쌉; ) HANGUL SYLLABLE SSAB +C30A;C30A;110A 1161 11B9;C30A;110A 1161 11B9; # (쌊; 쌊; 쌊; 쌊; 쌊; ) HANGUL SYLLABLE SSABS +C30B;C30B;110A 1161 11BA;C30B;110A 1161 11BA; # (쌋; 쌋; 쌋; 쌋; 쌋; ) HANGUL SYLLABLE SSAS +C30C;C30C;110A 1161 11BB;C30C;110A 1161 11BB; # (쌌; 쌌; 쌌; 쌌; 쌌; ) HANGUL SYLLABLE SSASS +C30D;C30D;110A 1161 11BC;C30D;110A 1161 11BC; # (쌍; 쌍; 쌍; 쌍; 쌍; ) HANGUL SYLLABLE SSANG +C30E;C30E;110A 1161 11BD;C30E;110A 1161 11BD; # (쌎; 쌎; 쌎; 쌎; 쌎; ) HANGUL SYLLABLE SSAJ +C30F;C30F;110A 1161 11BE;C30F;110A 1161 11BE; # (쌏; 쌏; 쌏; 쌏; 쌏; ) HANGUL SYLLABLE SSAC +C310;C310;110A 1161 11BF;C310;110A 1161 11BF; # (쌐; 쌐; 쌐; 쌐; 쌐; ) HANGUL SYLLABLE SSAK +C311;C311;110A 1161 11C0;C311;110A 1161 11C0; # (쌑; 쌑; 쌑; 쌑; 쌑; ) HANGUL SYLLABLE SSAT +C312;C312;110A 1161 11C1;C312;110A 1161 11C1; # (쌒; 쌒; 쌒; 쌒; 쌒; ) HANGUL SYLLABLE SSAP +C313;C313;110A 1161 11C2;C313;110A 1161 11C2; # (쌓; 쌓; 쌓; 쌓; 쌓; ) HANGUL SYLLABLE SSAH +C314;C314;110A 1162;C314;110A 1162; # (쌔; 쌔; 쌔; 쌔; 쌔; ) HANGUL SYLLABLE SSAE +C315;C315;110A 1162 11A8;C315;110A 1162 11A8; # (쌕; 쌕; 쌕; 쌕; 쌕; ) HANGUL SYLLABLE SSAEG +C316;C316;110A 1162 11A9;C316;110A 1162 11A9; # (쌖; 쌖; 쌖; 쌖; 쌖; ) HANGUL SYLLABLE SSAEGG +C317;C317;110A 1162 11AA;C317;110A 1162 11AA; # (쌗; 쌗; 쌗; 쌗; 쌗; ) HANGUL SYLLABLE SSAEGS +C318;C318;110A 1162 11AB;C318;110A 1162 11AB; # (쌘; 쌘; 쌘; 쌘; 쌘; ) HANGUL SYLLABLE SSAEN +C319;C319;110A 1162 11AC;C319;110A 1162 11AC; # (쌙; 쌙; 쌙; 쌙; 쌙; ) HANGUL SYLLABLE SSAENJ +C31A;C31A;110A 1162 11AD;C31A;110A 1162 11AD; # (쌚; 쌚; 쌚; 쌚; 쌚; ) HANGUL SYLLABLE SSAENH +C31B;C31B;110A 1162 11AE;C31B;110A 1162 11AE; # (쌛; 쌛; 쌛; 쌛; 쌛; ) HANGUL SYLLABLE SSAED +C31C;C31C;110A 1162 11AF;C31C;110A 1162 11AF; # (쌜; 쌜; 쌜; 쌜; 쌜; ) HANGUL SYLLABLE SSAEL +C31D;C31D;110A 1162 11B0;C31D;110A 1162 11B0; # (쌝; 쌝; 쌝; 쌝; 쌝; ) HANGUL SYLLABLE SSAELG +C31E;C31E;110A 1162 11B1;C31E;110A 1162 11B1; # (쌞; 쌞; 쌞; 쌞; 쌞; ) HANGUL SYLLABLE SSAELM +C31F;C31F;110A 1162 11B2;C31F;110A 1162 11B2; # (쌟; 쌟; 쌟; 쌟; 쌟; ) HANGUL SYLLABLE SSAELB +C320;C320;110A 1162 11B3;C320;110A 1162 11B3; # (쌠; 쌠; 쌠; 쌠; 쌠; ) HANGUL SYLLABLE SSAELS +C321;C321;110A 1162 11B4;C321;110A 1162 11B4; # (쌡; 쌡; 쌡; 쌡; 쌡; ) HANGUL SYLLABLE SSAELT +C322;C322;110A 1162 11B5;C322;110A 1162 11B5; # (쌢; 쌢; 쌢; 쌢; 쌢; ) HANGUL SYLLABLE SSAELP +C323;C323;110A 1162 11B6;C323;110A 1162 11B6; # (쌣; 쌣; 쌣; 쌣; 쌣; ) HANGUL SYLLABLE SSAELH +C324;C324;110A 1162 11B7;C324;110A 1162 11B7; # (쌤; 쌤; 쌤; 쌤; 쌤; ) HANGUL SYLLABLE SSAEM +C325;C325;110A 1162 11B8;C325;110A 1162 11B8; # (쌥; 쌥; 쌥; 쌥; 쌥; ) HANGUL SYLLABLE SSAEB +C326;C326;110A 1162 11B9;C326;110A 1162 11B9; # (쌦; 쌦; 쌦; 쌦; 쌦; ) HANGUL SYLLABLE SSAEBS +C327;C327;110A 1162 11BA;C327;110A 1162 11BA; # (쌧; 쌧; 쌧; 쌧; 쌧; ) HANGUL SYLLABLE SSAES +C328;C328;110A 1162 11BB;C328;110A 1162 11BB; # (쌨; 쌨; 쌨; 쌨; 쌨; ) HANGUL SYLLABLE SSAESS +C329;C329;110A 1162 11BC;C329;110A 1162 11BC; # (쌩; 쌩; 쌩; 쌩; 쌩; ) HANGUL SYLLABLE SSAENG +C32A;C32A;110A 1162 11BD;C32A;110A 1162 11BD; # (쌪; 쌪; 쌪; 쌪; 쌪; ) HANGUL SYLLABLE SSAEJ +C32B;C32B;110A 1162 11BE;C32B;110A 1162 11BE; # (쌫; 쌫; 쌫; 쌫; 쌫; ) HANGUL SYLLABLE SSAEC +C32C;C32C;110A 1162 11BF;C32C;110A 1162 11BF; # (쌬; 쌬; 쌬; 쌬; 쌬; ) HANGUL SYLLABLE SSAEK +C32D;C32D;110A 1162 11C0;C32D;110A 1162 11C0; # (쌭; 쌭; 쌭; 쌭; 쌭; ) HANGUL SYLLABLE SSAET +C32E;C32E;110A 1162 11C1;C32E;110A 1162 11C1; # (쌮; 쌮; 쌮; 쌮; 쌮; ) HANGUL SYLLABLE SSAEP +C32F;C32F;110A 1162 11C2;C32F;110A 1162 11C2; # (쌯; 쌯; 쌯; 쌯; 쌯; ) HANGUL SYLLABLE SSAEH +C330;C330;110A 1163;C330;110A 1163; # (쌰; 쌰; 쌰; 쌰; 쌰; ) HANGUL SYLLABLE SSYA +C331;C331;110A 1163 11A8;C331;110A 1163 11A8; # (쌱; 쌱; 쌱; 쌱; 쌱; ) HANGUL SYLLABLE SSYAG +C332;C332;110A 1163 11A9;C332;110A 1163 11A9; # (쌲; 쌲; 쌲; 쌲; 쌲; ) HANGUL SYLLABLE SSYAGG +C333;C333;110A 1163 11AA;C333;110A 1163 11AA; # (쌳; 쌳; 쌳; 쌳; 쌳; ) HANGUL SYLLABLE SSYAGS +C334;C334;110A 1163 11AB;C334;110A 1163 11AB; # (쌴; 쌴; 쌴; 쌴; 쌴; ) HANGUL SYLLABLE SSYAN +C335;C335;110A 1163 11AC;C335;110A 1163 11AC; # (쌵; 쌵; 쌵; 쌵; 쌵; ) HANGUL SYLLABLE SSYANJ +C336;C336;110A 1163 11AD;C336;110A 1163 11AD; # (쌶; 쌶; 쌶; 쌶; 쌶; ) HANGUL SYLLABLE SSYANH +C337;C337;110A 1163 11AE;C337;110A 1163 11AE; # (쌷; 쌷; 쌷; 쌷; 쌷; ) HANGUL SYLLABLE SSYAD +C338;C338;110A 1163 11AF;C338;110A 1163 11AF; # (쌸; 쌸; 쌸; 쌸; 쌸; ) HANGUL SYLLABLE SSYAL +C339;C339;110A 1163 11B0;C339;110A 1163 11B0; # (쌹; 쌹; 쌹; 쌹; 쌹; ) HANGUL SYLLABLE SSYALG +C33A;C33A;110A 1163 11B1;C33A;110A 1163 11B1; # (쌺; 쌺; 쌺; 쌺; 쌺; ) HANGUL SYLLABLE SSYALM +C33B;C33B;110A 1163 11B2;C33B;110A 1163 11B2; # (쌻; 쌻; 쌻; 쌻; 쌻; ) HANGUL SYLLABLE SSYALB +C33C;C33C;110A 1163 11B3;C33C;110A 1163 11B3; # (쌼; 쌼; 쌼; 쌼; 쌼; ) HANGUL SYLLABLE SSYALS +C33D;C33D;110A 1163 11B4;C33D;110A 1163 11B4; # (쌽; 쌽; 쌽; 쌽; 쌽; ) HANGUL SYLLABLE SSYALT +C33E;C33E;110A 1163 11B5;C33E;110A 1163 11B5; # (쌾; 쌾; 쌾; 쌾; 쌾; ) HANGUL SYLLABLE SSYALP +C33F;C33F;110A 1163 11B6;C33F;110A 1163 11B6; # (쌿; 쌿; 쌿; 쌿; 쌿; ) HANGUL SYLLABLE SSYALH +C340;C340;110A 1163 11B7;C340;110A 1163 11B7; # (썀; 썀; 썀; 썀; 썀; ) HANGUL SYLLABLE SSYAM +C341;C341;110A 1163 11B8;C341;110A 1163 11B8; # (썁; 썁; 썁; 썁; 썁; ) HANGUL SYLLABLE SSYAB +C342;C342;110A 1163 11B9;C342;110A 1163 11B9; # (썂; 썂; 썂; 썂; 썂; ) HANGUL SYLLABLE SSYABS +C343;C343;110A 1163 11BA;C343;110A 1163 11BA; # (썃; 썃; 썃; 썃; 썃; ) HANGUL SYLLABLE SSYAS +C344;C344;110A 1163 11BB;C344;110A 1163 11BB; # (썄; 썄; 썄; 썄; 썄; ) HANGUL SYLLABLE SSYASS +C345;C345;110A 1163 11BC;C345;110A 1163 11BC; # (썅; 썅; 썅; 썅; 썅; ) HANGUL SYLLABLE SSYANG +C346;C346;110A 1163 11BD;C346;110A 1163 11BD; # (썆; 썆; 썆; 썆; 썆; ) HANGUL SYLLABLE SSYAJ +C347;C347;110A 1163 11BE;C347;110A 1163 11BE; # (썇; 썇; 썇; 썇; 썇; ) HANGUL SYLLABLE SSYAC +C348;C348;110A 1163 11BF;C348;110A 1163 11BF; # (썈; 썈; 썈; 썈; 썈; ) HANGUL SYLLABLE SSYAK +C349;C349;110A 1163 11C0;C349;110A 1163 11C0; # (썉; 썉; 썉; 썉; 썉; ) HANGUL SYLLABLE SSYAT +C34A;C34A;110A 1163 11C1;C34A;110A 1163 11C1; # (썊; 썊; 썊; 썊; 썊; ) HANGUL SYLLABLE SSYAP +C34B;C34B;110A 1163 11C2;C34B;110A 1163 11C2; # (썋; 썋; 썋; 썋; 썋; ) HANGUL SYLLABLE SSYAH +C34C;C34C;110A 1164;C34C;110A 1164; # (썌; 썌; 썌; 썌; 썌; ) HANGUL SYLLABLE SSYAE +C34D;C34D;110A 1164 11A8;C34D;110A 1164 11A8; # (썍; 썍; 썍; 썍; 썍; ) HANGUL SYLLABLE SSYAEG +C34E;C34E;110A 1164 11A9;C34E;110A 1164 11A9; # (썎; 썎; 썎; 썎; 썎; ) HANGUL SYLLABLE SSYAEGG +C34F;C34F;110A 1164 11AA;C34F;110A 1164 11AA; # (썏; 썏; 썏; 썏; 썏; ) HANGUL SYLLABLE SSYAEGS +C350;C350;110A 1164 11AB;C350;110A 1164 11AB; # (썐; 썐; 썐; 썐; 썐; ) HANGUL SYLLABLE SSYAEN +C351;C351;110A 1164 11AC;C351;110A 1164 11AC; # (썑; 썑; 썑; 썑; 썑; ) HANGUL SYLLABLE SSYAENJ +C352;C352;110A 1164 11AD;C352;110A 1164 11AD; # (썒; 썒; 썒; 썒; 썒; ) HANGUL SYLLABLE SSYAENH +C353;C353;110A 1164 11AE;C353;110A 1164 11AE; # (썓; 썓; 썓; 썓; 썓; ) HANGUL SYLLABLE SSYAED +C354;C354;110A 1164 11AF;C354;110A 1164 11AF; # (썔; 썔; 썔; 썔; 썔; ) HANGUL SYLLABLE SSYAEL +C355;C355;110A 1164 11B0;C355;110A 1164 11B0; # (썕; 썕; 썕; 썕; 썕; ) HANGUL SYLLABLE SSYAELG +C356;C356;110A 1164 11B1;C356;110A 1164 11B1; # (썖; 썖; 썖; 썖; 썖; ) HANGUL SYLLABLE SSYAELM +C357;C357;110A 1164 11B2;C357;110A 1164 11B2; # (썗; 썗; 썗; 썗; 썗; ) HANGUL SYLLABLE SSYAELB +C358;C358;110A 1164 11B3;C358;110A 1164 11B3; # (썘; 썘; 썘; 썘; 썘; ) HANGUL SYLLABLE SSYAELS +C359;C359;110A 1164 11B4;C359;110A 1164 11B4; # (썙; 썙; 썙; 썙; 썙; ) HANGUL SYLLABLE SSYAELT +C35A;C35A;110A 1164 11B5;C35A;110A 1164 11B5; # (썚; 썚; 썚; 썚; 썚; ) HANGUL SYLLABLE SSYAELP +C35B;C35B;110A 1164 11B6;C35B;110A 1164 11B6; # (썛; 썛; 썛; 썛; 썛; ) HANGUL SYLLABLE SSYAELH +C35C;C35C;110A 1164 11B7;C35C;110A 1164 11B7; # (썜; 썜; 썜; 썜; 썜; ) HANGUL SYLLABLE SSYAEM +C35D;C35D;110A 1164 11B8;C35D;110A 1164 11B8; # (썝; 썝; 썝; 썝; 썝; ) HANGUL SYLLABLE SSYAEB +C35E;C35E;110A 1164 11B9;C35E;110A 1164 11B9; # (썞; 썞; 썞; 썞; 썞; ) HANGUL SYLLABLE SSYAEBS +C35F;C35F;110A 1164 11BA;C35F;110A 1164 11BA; # (썟; 썟; 썟; 썟; 썟; ) HANGUL SYLLABLE SSYAES +C360;C360;110A 1164 11BB;C360;110A 1164 11BB; # (썠; 썠; 썠; 썠; 썠; ) HANGUL SYLLABLE SSYAESS +C361;C361;110A 1164 11BC;C361;110A 1164 11BC; # (썡; 썡; 썡; 썡; 썡; ) HANGUL SYLLABLE SSYAENG +C362;C362;110A 1164 11BD;C362;110A 1164 11BD; # (썢; 썢; 썢; 썢; 썢; ) HANGUL SYLLABLE SSYAEJ +C363;C363;110A 1164 11BE;C363;110A 1164 11BE; # (썣; 썣; 썣; 썣; 썣; ) HANGUL SYLLABLE SSYAEC +C364;C364;110A 1164 11BF;C364;110A 1164 11BF; # (썤; 썤; 썤; 썤; 썤; ) HANGUL SYLLABLE SSYAEK +C365;C365;110A 1164 11C0;C365;110A 1164 11C0; # (썥; 썥; 썥; 썥; 썥; ) HANGUL SYLLABLE SSYAET +C366;C366;110A 1164 11C1;C366;110A 1164 11C1; # (썦; 썦; 썦; 썦; 썦; ) HANGUL SYLLABLE SSYAEP +C367;C367;110A 1164 11C2;C367;110A 1164 11C2; # (썧; 썧; 썧; 썧; 썧; ) HANGUL SYLLABLE SSYAEH +C368;C368;110A 1165;C368;110A 1165; # (써; 써; 써; 써; 써; ) HANGUL SYLLABLE SSEO +C369;C369;110A 1165 11A8;C369;110A 1165 11A8; # (썩; 썩; 썩; 썩; 썩; ) HANGUL SYLLABLE SSEOG +C36A;C36A;110A 1165 11A9;C36A;110A 1165 11A9; # (썪; 썪; 썪; 썪; 썪; ) HANGUL SYLLABLE SSEOGG +C36B;C36B;110A 1165 11AA;C36B;110A 1165 11AA; # (썫; 썫; 썫; 썫; 썫; ) HANGUL SYLLABLE SSEOGS +C36C;C36C;110A 1165 11AB;C36C;110A 1165 11AB; # (썬; 썬; 썬; 썬; 썬; ) HANGUL SYLLABLE SSEON +C36D;C36D;110A 1165 11AC;C36D;110A 1165 11AC; # (썭; 썭; 썭; 썭; 썭; ) HANGUL SYLLABLE SSEONJ +C36E;C36E;110A 1165 11AD;C36E;110A 1165 11AD; # (썮; 썮; 썮; 썮; 썮; ) HANGUL SYLLABLE SSEONH +C36F;C36F;110A 1165 11AE;C36F;110A 1165 11AE; # (썯; 썯; 썯; 썯; 썯; ) HANGUL SYLLABLE SSEOD +C370;C370;110A 1165 11AF;C370;110A 1165 11AF; # (썰; 썰; 썰; 썰; 썰; ) HANGUL SYLLABLE SSEOL +C371;C371;110A 1165 11B0;C371;110A 1165 11B0; # (썱; 썱; 썱; 썱; 썱; ) HANGUL SYLLABLE SSEOLG +C372;C372;110A 1165 11B1;C372;110A 1165 11B1; # (썲; 썲; 썲; 썲; 썲; ) HANGUL SYLLABLE SSEOLM +C373;C373;110A 1165 11B2;C373;110A 1165 11B2; # (썳; 썳; 썳; 썳; 썳; ) HANGUL SYLLABLE SSEOLB +C374;C374;110A 1165 11B3;C374;110A 1165 11B3; # (썴; 썴; 썴; 썴; 썴; ) HANGUL SYLLABLE SSEOLS +C375;C375;110A 1165 11B4;C375;110A 1165 11B4; # (썵; 썵; 썵; 썵; 썵; ) HANGUL SYLLABLE SSEOLT +C376;C376;110A 1165 11B5;C376;110A 1165 11B5; # (썶; 썶; 썶; 썶; 썶; ) HANGUL SYLLABLE SSEOLP +C377;C377;110A 1165 11B6;C377;110A 1165 11B6; # (썷; 썷; 썷; 썷; 썷; ) HANGUL SYLLABLE SSEOLH +C378;C378;110A 1165 11B7;C378;110A 1165 11B7; # (썸; 썸; 썸; 썸; 썸; ) HANGUL SYLLABLE SSEOM +C379;C379;110A 1165 11B8;C379;110A 1165 11B8; # (썹; 썹; 썹; 썹; 썹; ) HANGUL SYLLABLE SSEOB +C37A;C37A;110A 1165 11B9;C37A;110A 1165 11B9; # (썺; 썺; 썺; 썺; 썺; ) HANGUL SYLLABLE SSEOBS +C37B;C37B;110A 1165 11BA;C37B;110A 1165 11BA; # (썻; 썻; 썻; 썻; 썻; ) HANGUL SYLLABLE SSEOS +C37C;C37C;110A 1165 11BB;C37C;110A 1165 11BB; # (썼; 썼; 썼; 썼; 썼; ) HANGUL SYLLABLE SSEOSS +C37D;C37D;110A 1165 11BC;C37D;110A 1165 11BC; # (썽; 썽; 썽; 썽; 썽; ) HANGUL SYLLABLE SSEONG +C37E;C37E;110A 1165 11BD;C37E;110A 1165 11BD; # (썾; 썾; 썾; 썾; 썾; ) HANGUL SYLLABLE SSEOJ +C37F;C37F;110A 1165 11BE;C37F;110A 1165 11BE; # (썿; 썿; 썿; 썿; 썿; ) HANGUL SYLLABLE SSEOC +C380;C380;110A 1165 11BF;C380;110A 1165 11BF; # (쎀; 쎀; 쎀; 쎀; 쎀; ) HANGUL SYLLABLE SSEOK +C381;C381;110A 1165 11C0;C381;110A 1165 11C0; # (쎁; 쎁; 쎁; 쎁; 쎁; ) HANGUL SYLLABLE SSEOT +C382;C382;110A 1165 11C1;C382;110A 1165 11C1; # (쎂; 쎂; 쎂; 쎂; 쎂; ) HANGUL SYLLABLE SSEOP +C383;C383;110A 1165 11C2;C383;110A 1165 11C2; # (쎃; 쎃; 쎃; 쎃; 쎃; ) HANGUL SYLLABLE SSEOH +C384;C384;110A 1166;C384;110A 1166; # (쎄; 쎄; 쎄; 쎄; 쎄; ) HANGUL SYLLABLE SSE +C385;C385;110A 1166 11A8;C385;110A 1166 11A8; # (쎅; 쎅; 쎅; 쎅; 쎅; ) HANGUL SYLLABLE SSEG +C386;C386;110A 1166 11A9;C386;110A 1166 11A9; # (쎆; 쎆; 쎆; 쎆; 쎆; ) HANGUL SYLLABLE SSEGG +C387;C387;110A 1166 11AA;C387;110A 1166 11AA; # (쎇; 쎇; 쎇; 쎇; 쎇; ) HANGUL SYLLABLE SSEGS +C388;C388;110A 1166 11AB;C388;110A 1166 11AB; # (쎈; 쎈; 쎈; 쎈; 쎈; ) HANGUL SYLLABLE SSEN +C389;C389;110A 1166 11AC;C389;110A 1166 11AC; # (쎉; 쎉; 쎉; 쎉; 쎉; ) HANGUL SYLLABLE SSENJ +C38A;C38A;110A 1166 11AD;C38A;110A 1166 11AD; # (쎊; 쎊; 쎊; 쎊; 쎊; ) HANGUL SYLLABLE SSENH +C38B;C38B;110A 1166 11AE;C38B;110A 1166 11AE; # (쎋; 쎋; 쎋; 쎋; 쎋; ) HANGUL SYLLABLE SSED +C38C;C38C;110A 1166 11AF;C38C;110A 1166 11AF; # (쎌; 쎌; 쎌; 쎌; 쎌; ) HANGUL SYLLABLE SSEL +C38D;C38D;110A 1166 11B0;C38D;110A 1166 11B0; # (쎍; 쎍; 쎍; 쎍; 쎍; ) HANGUL SYLLABLE SSELG +C38E;C38E;110A 1166 11B1;C38E;110A 1166 11B1; # (쎎; 쎎; 쎎; 쎎; 쎎; ) HANGUL SYLLABLE SSELM +C38F;C38F;110A 1166 11B2;C38F;110A 1166 11B2; # (쎏; 쎏; 쎏; 쎏; 쎏; ) HANGUL SYLLABLE SSELB +C390;C390;110A 1166 11B3;C390;110A 1166 11B3; # (쎐; 쎐; 쎐; 쎐; 쎐; ) HANGUL SYLLABLE SSELS +C391;C391;110A 1166 11B4;C391;110A 1166 11B4; # (쎑; 쎑; 쎑; 쎑; 쎑; ) HANGUL SYLLABLE SSELT +C392;C392;110A 1166 11B5;C392;110A 1166 11B5; # (쎒; 쎒; 쎒; 쎒; 쎒; ) HANGUL SYLLABLE SSELP +C393;C393;110A 1166 11B6;C393;110A 1166 11B6; # (쎓; 쎓; 쎓; 쎓; 쎓; ) HANGUL SYLLABLE SSELH +C394;C394;110A 1166 11B7;C394;110A 1166 11B7; # (쎔; 쎔; 쎔; 쎔; 쎔; ) HANGUL SYLLABLE SSEM +C395;C395;110A 1166 11B8;C395;110A 1166 11B8; # (쎕; 쎕; 쎕; 쎕; 쎕; ) HANGUL SYLLABLE SSEB +C396;C396;110A 1166 11B9;C396;110A 1166 11B9; # (쎖; 쎖; 쎖; 쎖; 쎖; ) HANGUL SYLLABLE SSEBS +C397;C397;110A 1166 11BA;C397;110A 1166 11BA; # (쎗; 쎗; 쎗; 쎗; 쎗; ) HANGUL SYLLABLE SSES +C398;C398;110A 1166 11BB;C398;110A 1166 11BB; # (쎘; 쎘; 쎘; 쎘; 쎘; ) HANGUL SYLLABLE SSESS +C399;C399;110A 1166 11BC;C399;110A 1166 11BC; # (쎙; 쎙; 쎙; 쎙; 쎙; ) HANGUL SYLLABLE SSENG +C39A;C39A;110A 1166 11BD;C39A;110A 1166 11BD; # (쎚; 쎚; 쎚; 쎚; 쎚; ) HANGUL SYLLABLE SSEJ +C39B;C39B;110A 1166 11BE;C39B;110A 1166 11BE; # (쎛; 쎛; 쎛; 쎛; 쎛; ) HANGUL SYLLABLE SSEC +C39C;C39C;110A 1166 11BF;C39C;110A 1166 11BF; # (쎜; 쎜; 쎜; 쎜; 쎜; ) HANGUL SYLLABLE SSEK +C39D;C39D;110A 1166 11C0;C39D;110A 1166 11C0; # (쎝; 쎝; 쎝; 쎝; 쎝; ) HANGUL SYLLABLE SSET +C39E;C39E;110A 1166 11C1;C39E;110A 1166 11C1; # (쎞; 쎞; 쎞; 쎞; 쎞; ) HANGUL SYLLABLE SSEP +C39F;C39F;110A 1166 11C2;C39F;110A 1166 11C2; # (쎟; 쎟; 쎟; 쎟; 쎟; ) HANGUL SYLLABLE SSEH +C3A0;C3A0;110A 1167;C3A0;110A 1167; # (쎠; 쎠; 쎠; 쎠; 쎠; ) HANGUL SYLLABLE SSYEO +C3A1;C3A1;110A 1167 11A8;C3A1;110A 1167 11A8; # (쎡; 쎡; 쎡; 쎡; 쎡; ) HANGUL SYLLABLE SSYEOG +C3A2;C3A2;110A 1167 11A9;C3A2;110A 1167 11A9; # (쎢; 쎢; 쎢; 쎢; 쎢; ) HANGUL SYLLABLE SSYEOGG +C3A3;C3A3;110A 1167 11AA;C3A3;110A 1167 11AA; # (쎣; 쎣; 쎣; 쎣; 쎣; ) HANGUL SYLLABLE SSYEOGS +C3A4;C3A4;110A 1167 11AB;C3A4;110A 1167 11AB; # (쎤; 쎤; 쎤; 쎤; 쎤; ) HANGUL SYLLABLE SSYEON +C3A5;C3A5;110A 1167 11AC;C3A5;110A 1167 11AC; # (쎥; 쎥; 쎥; 쎥; 쎥; ) HANGUL SYLLABLE SSYEONJ +C3A6;C3A6;110A 1167 11AD;C3A6;110A 1167 11AD; # (쎦; 쎦; 쎦; 쎦; 쎦; ) HANGUL SYLLABLE SSYEONH +C3A7;C3A7;110A 1167 11AE;C3A7;110A 1167 11AE; # (쎧; 쎧; 쎧; 쎧; 쎧; ) HANGUL SYLLABLE SSYEOD +C3A8;C3A8;110A 1167 11AF;C3A8;110A 1167 11AF; # (쎨; 쎨; 쎨; 쎨; 쎨; ) HANGUL SYLLABLE SSYEOL +C3A9;C3A9;110A 1167 11B0;C3A9;110A 1167 11B0; # (쎩; 쎩; 쎩; 쎩; 쎩; ) HANGUL SYLLABLE SSYEOLG +C3AA;C3AA;110A 1167 11B1;C3AA;110A 1167 11B1; # (쎪; 쎪; 쎪; 쎪; 쎪; ) HANGUL SYLLABLE SSYEOLM +C3AB;C3AB;110A 1167 11B2;C3AB;110A 1167 11B2; # (쎫; 쎫; 쎫; 쎫; 쎫; ) HANGUL SYLLABLE SSYEOLB +C3AC;C3AC;110A 1167 11B3;C3AC;110A 1167 11B3; # (쎬; 쎬; 쎬; 쎬; 쎬; ) HANGUL SYLLABLE SSYEOLS +C3AD;C3AD;110A 1167 11B4;C3AD;110A 1167 11B4; # (쎭; 쎭; 쎭; 쎭; 쎭; ) HANGUL SYLLABLE SSYEOLT +C3AE;C3AE;110A 1167 11B5;C3AE;110A 1167 11B5; # (쎮; 쎮; 쎮; 쎮; 쎮; ) HANGUL SYLLABLE SSYEOLP +C3AF;C3AF;110A 1167 11B6;C3AF;110A 1167 11B6; # (쎯; 쎯; 쎯; 쎯; 쎯; ) HANGUL SYLLABLE SSYEOLH +C3B0;C3B0;110A 1167 11B7;C3B0;110A 1167 11B7; # (쎰; 쎰; 쎰; 쎰; 쎰; ) HANGUL SYLLABLE SSYEOM +C3B1;C3B1;110A 1167 11B8;C3B1;110A 1167 11B8; # (쎱; 쎱; 쎱; 쎱; 쎱; ) HANGUL SYLLABLE SSYEOB +C3B2;C3B2;110A 1167 11B9;C3B2;110A 1167 11B9; # (쎲; 쎲; 쎲; 쎲; 쎲; ) HANGUL SYLLABLE SSYEOBS +C3B3;C3B3;110A 1167 11BA;C3B3;110A 1167 11BA; # (쎳; 쎳; 쎳; 쎳; 쎳; ) HANGUL SYLLABLE SSYEOS +C3B4;C3B4;110A 1167 11BB;C3B4;110A 1167 11BB; # (쎴; 쎴; 쎴; 쎴; 쎴; ) HANGUL SYLLABLE SSYEOSS +C3B5;C3B5;110A 1167 11BC;C3B5;110A 1167 11BC; # (쎵; 쎵; 쎵; 쎵; 쎵; ) HANGUL SYLLABLE SSYEONG +C3B6;C3B6;110A 1167 11BD;C3B6;110A 1167 11BD; # (쎶; 쎶; 쎶; 쎶; 쎶; ) HANGUL SYLLABLE SSYEOJ +C3B7;C3B7;110A 1167 11BE;C3B7;110A 1167 11BE; # (쎷; 쎷; 쎷; 쎷; 쎷; ) HANGUL SYLLABLE SSYEOC +C3B8;C3B8;110A 1167 11BF;C3B8;110A 1167 11BF; # (쎸; 쎸; 쎸; 쎸; 쎸; ) HANGUL SYLLABLE SSYEOK +C3B9;C3B9;110A 1167 11C0;C3B9;110A 1167 11C0; # (쎹; 쎹; 쎹; 쎹; 쎹; ) HANGUL SYLLABLE SSYEOT +C3BA;C3BA;110A 1167 11C1;C3BA;110A 1167 11C1; # (쎺; 쎺; 쎺; 쎺; 쎺; ) HANGUL SYLLABLE SSYEOP +C3BB;C3BB;110A 1167 11C2;C3BB;110A 1167 11C2; # (쎻; 쎻; 쎻; 쎻; 쎻; ) HANGUL SYLLABLE SSYEOH +C3BC;C3BC;110A 1168;C3BC;110A 1168; # (쎼; 쎼; 쎼; 쎼; 쎼; ) HANGUL SYLLABLE SSYE +C3BD;C3BD;110A 1168 11A8;C3BD;110A 1168 11A8; # (쎽; 쎽; 쎽; 쎽; 쎽; ) HANGUL SYLLABLE SSYEG +C3BE;C3BE;110A 1168 11A9;C3BE;110A 1168 11A9; # (쎾; 쎾; 쎾; 쎾; 쎾; ) HANGUL SYLLABLE SSYEGG +C3BF;C3BF;110A 1168 11AA;C3BF;110A 1168 11AA; # (쎿; 쎿; 쎿; 쎿; 쎿; ) HANGUL SYLLABLE SSYEGS +C3C0;C3C0;110A 1168 11AB;C3C0;110A 1168 11AB; # (쏀; 쏀; 쏀; 쏀; 쏀; ) HANGUL SYLLABLE SSYEN +C3C1;C3C1;110A 1168 11AC;C3C1;110A 1168 11AC; # (쏁; 쏁; 쏁; 쏁; 쏁; ) HANGUL SYLLABLE SSYENJ +C3C2;C3C2;110A 1168 11AD;C3C2;110A 1168 11AD; # (쏂; 쏂; 쏂; 쏂; 쏂; ) HANGUL SYLLABLE SSYENH +C3C3;C3C3;110A 1168 11AE;C3C3;110A 1168 11AE; # (쏃; 쏃; 쏃; 쏃; 쏃; ) HANGUL SYLLABLE SSYED +C3C4;C3C4;110A 1168 11AF;C3C4;110A 1168 11AF; # (쏄; 쏄; 쏄; 쏄; 쏄; ) HANGUL SYLLABLE SSYEL +C3C5;C3C5;110A 1168 11B0;C3C5;110A 1168 11B0; # (쏅; 쏅; 쏅; 쏅; 쏅; ) HANGUL SYLLABLE SSYELG +C3C6;C3C6;110A 1168 11B1;C3C6;110A 1168 11B1; # (쏆; 쏆; 쏆; 쏆; 쏆; ) HANGUL SYLLABLE SSYELM +C3C7;C3C7;110A 1168 11B2;C3C7;110A 1168 11B2; # (쏇; 쏇; 쏇; 쏇; 쏇; ) HANGUL SYLLABLE SSYELB +C3C8;C3C8;110A 1168 11B3;C3C8;110A 1168 11B3; # (쏈; 쏈; 쏈; 쏈; 쏈; ) HANGUL SYLLABLE SSYELS +C3C9;C3C9;110A 1168 11B4;C3C9;110A 1168 11B4; # (쏉; 쏉; 쏉; 쏉; 쏉; ) HANGUL SYLLABLE SSYELT +C3CA;C3CA;110A 1168 11B5;C3CA;110A 1168 11B5; # (쏊; 쏊; 쏊; 쏊; 쏊; ) HANGUL SYLLABLE SSYELP +C3CB;C3CB;110A 1168 11B6;C3CB;110A 1168 11B6; # (쏋; 쏋; 쏋; 쏋; 쏋; ) HANGUL SYLLABLE SSYELH +C3CC;C3CC;110A 1168 11B7;C3CC;110A 1168 11B7; # (쏌; 쏌; 쏌; 쏌; 쏌; ) HANGUL SYLLABLE SSYEM +C3CD;C3CD;110A 1168 11B8;C3CD;110A 1168 11B8; # (쏍; 쏍; 쏍; 쏍; 쏍; ) HANGUL SYLLABLE SSYEB +C3CE;C3CE;110A 1168 11B9;C3CE;110A 1168 11B9; # (쏎; 쏎; 쏎; 쏎; 쏎; ) HANGUL SYLLABLE SSYEBS +C3CF;C3CF;110A 1168 11BA;C3CF;110A 1168 11BA; # (쏏; 쏏; 쏏; 쏏; 쏏; ) HANGUL SYLLABLE SSYES +C3D0;C3D0;110A 1168 11BB;C3D0;110A 1168 11BB; # (쏐; 쏐; 쏐; 쏐; 쏐; ) HANGUL SYLLABLE SSYESS +C3D1;C3D1;110A 1168 11BC;C3D1;110A 1168 11BC; # (쏑; 쏑; 쏑; 쏑; 쏑; ) HANGUL SYLLABLE SSYENG +C3D2;C3D2;110A 1168 11BD;C3D2;110A 1168 11BD; # (쏒; 쏒; 쏒; 쏒; 쏒; ) HANGUL SYLLABLE SSYEJ +C3D3;C3D3;110A 1168 11BE;C3D3;110A 1168 11BE; # (쏓; 쏓; 쏓; 쏓; 쏓; ) HANGUL SYLLABLE SSYEC +C3D4;C3D4;110A 1168 11BF;C3D4;110A 1168 11BF; # (쏔; 쏔; 쏔; 쏔; 쏔; ) HANGUL SYLLABLE SSYEK +C3D5;C3D5;110A 1168 11C0;C3D5;110A 1168 11C0; # (쏕; 쏕; 쏕; 쏕; 쏕; ) HANGUL SYLLABLE SSYET +C3D6;C3D6;110A 1168 11C1;C3D6;110A 1168 11C1; # (쏖; 쏖; 쏖; 쏖; 쏖; ) HANGUL SYLLABLE SSYEP +C3D7;C3D7;110A 1168 11C2;C3D7;110A 1168 11C2; # (쏗; 쏗; 쏗; 쏗; 쏗; ) HANGUL SYLLABLE SSYEH +C3D8;C3D8;110A 1169;C3D8;110A 1169; # (쏘; 쏘; 쏘; 쏘; 쏘; ) HANGUL SYLLABLE SSO +C3D9;C3D9;110A 1169 11A8;C3D9;110A 1169 11A8; # (쏙; 쏙; 쏙; 쏙; 쏙; ) HANGUL SYLLABLE SSOG +C3DA;C3DA;110A 1169 11A9;C3DA;110A 1169 11A9; # (쏚; 쏚; 쏚; 쏚; 쏚; ) HANGUL SYLLABLE SSOGG +C3DB;C3DB;110A 1169 11AA;C3DB;110A 1169 11AA; # (쏛; 쏛; 쏛; 쏛; 쏛; ) HANGUL SYLLABLE SSOGS +C3DC;C3DC;110A 1169 11AB;C3DC;110A 1169 11AB; # (쏜; 쏜; 쏜; 쏜; 쏜; ) HANGUL SYLLABLE SSON +C3DD;C3DD;110A 1169 11AC;C3DD;110A 1169 11AC; # (쏝; 쏝; 쏝; 쏝; 쏝; ) HANGUL SYLLABLE SSONJ +C3DE;C3DE;110A 1169 11AD;C3DE;110A 1169 11AD; # (쏞; 쏞; 쏞; 쏞; 쏞; ) HANGUL SYLLABLE SSONH +C3DF;C3DF;110A 1169 11AE;C3DF;110A 1169 11AE; # (쏟; 쏟; 쏟; 쏟; 쏟; ) HANGUL SYLLABLE SSOD +C3E0;C3E0;110A 1169 11AF;C3E0;110A 1169 11AF; # (쏠; 쏠; 쏠; 쏠; 쏠; ) HANGUL SYLLABLE SSOL +C3E1;C3E1;110A 1169 11B0;C3E1;110A 1169 11B0; # (쏡; 쏡; 쏡; 쏡; 쏡; ) HANGUL SYLLABLE SSOLG +C3E2;C3E2;110A 1169 11B1;C3E2;110A 1169 11B1; # (쏢; 쏢; 쏢; 쏢; 쏢; ) HANGUL SYLLABLE SSOLM +C3E3;C3E3;110A 1169 11B2;C3E3;110A 1169 11B2; # (쏣; 쏣; 쏣; 쏣; 쏣; ) HANGUL SYLLABLE SSOLB +C3E4;C3E4;110A 1169 11B3;C3E4;110A 1169 11B3; # (쏤; 쏤; 쏤; 쏤; 쏤; ) HANGUL SYLLABLE SSOLS +C3E5;C3E5;110A 1169 11B4;C3E5;110A 1169 11B4; # (쏥; 쏥; 쏥; 쏥; 쏥; ) HANGUL SYLLABLE SSOLT +C3E6;C3E6;110A 1169 11B5;C3E6;110A 1169 11B5; # (쏦; 쏦; 쏦; 쏦; 쏦; ) HANGUL SYLLABLE SSOLP +C3E7;C3E7;110A 1169 11B6;C3E7;110A 1169 11B6; # (쏧; 쏧; 쏧; 쏧; 쏧; ) HANGUL SYLLABLE SSOLH +C3E8;C3E8;110A 1169 11B7;C3E8;110A 1169 11B7; # (쏨; 쏨; 쏨; 쏨; 쏨; ) HANGUL SYLLABLE SSOM +C3E9;C3E9;110A 1169 11B8;C3E9;110A 1169 11B8; # (쏩; 쏩; 쏩; 쏩; 쏩; ) HANGUL SYLLABLE SSOB +C3EA;C3EA;110A 1169 11B9;C3EA;110A 1169 11B9; # (쏪; 쏪; 쏪; 쏪; 쏪; ) HANGUL SYLLABLE SSOBS +C3EB;C3EB;110A 1169 11BA;C3EB;110A 1169 11BA; # (쏫; 쏫; 쏫; 쏫; 쏫; ) HANGUL SYLLABLE SSOS +C3EC;C3EC;110A 1169 11BB;C3EC;110A 1169 11BB; # (쏬; 쏬; 쏬; 쏬; 쏬; ) HANGUL SYLLABLE SSOSS +C3ED;C3ED;110A 1169 11BC;C3ED;110A 1169 11BC; # (쏭; 쏭; 쏭; 쏭; 쏭; ) HANGUL SYLLABLE SSONG +C3EE;C3EE;110A 1169 11BD;C3EE;110A 1169 11BD; # (쏮; 쏮; 쏮; 쏮; 쏮; ) HANGUL SYLLABLE SSOJ +C3EF;C3EF;110A 1169 11BE;C3EF;110A 1169 11BE; # (쏯; 쏯; 쏯; 쏯; 쏯; ) HANGUL SYLLABLE SSOC +C3F0;C3F0;110A 1169 11BF;C3F0;110A 1169 11BF; # (쏰; 쏰; 쏰; 쏰; 쏰; ) HANGUL SYLLABLE SSOK +C3F1;C3F1;110A 1169 11C0;C3F1;110A 1169 11C0; # (쏱; 쏱; 쏱; 쏱; 쏱; ) HANGUL SYLLABLE SSOT +C3F2;C3F2;110A 1169 11C1;C3F2;110A 1169 11C1; # (쏲; 쏲; 쏲; 쏲; 쏲; ) HANGUL SYLLABLE SSOP +C3F3;C3F3;110A 1169 11C2;C3F3;110A 1169 11C2; # (쏳; 쏳; 쏳; 쏳; 쏳; ) HANGUL SYLLABLE SSOH +C3F4;C3F4;110A 116A;C3F4;110A 116A; # (쏴; 쏴; 쏴; 쏴; 쏴; ) HANGUL SYLLABLE SSWA +C3F5;C3F5;110A 116A 11A8;C3F5;110A 116A 11A8; # (쏵; 쏵; 쏵; 쏵; 쏵; ) HANGUL SYLLABLE SSWAG +C3F6;C3F6;110A 116A 11A9;C3F6;110A 116A 11A9; # (쏶; 쏶; 쏶; 쏶; 쏶; ) HANGUL SYLLABLE SSWAGG +C3F7;C3F7;110A 116A 11AA;C3F7;110A 116A 11AA; # (쏷; 쏷; 쏷; 쏷; 쏷; ) HANGUL SYLLABLE SSWAGS +C3F8;C3F8;110A 116A 11AB;C3F8;110A 116A 11AB; # (쏸; 쏸; 쏸; 쏸; 쏸; ) HANGUL SYLLABLE SSWAN +C3F9;C3F9;110A 116A 11AC;C3F9;110A 116A 11AC; # (쏹; 쏹; 쏹; 쏹; 쏹; ) HANGUL SYLLABLE SSWANJ +C3FA;C3FA;110A 116A 11AD;C3FA;110A 116A 11AD; # (쏺; 쏺; 쏺; 쏺; 쏺; ) HANGUL SYLLABLE SSWANH +C3FB;C3FB;110A 116A 11AE;C3FB;110A 116A 11AE; # (쏻; 쏻; 쏻; 쏻; 쏻; ) HANGUL SYLLABLE SSWAD +C3FC;C3FC;110A 116A 11AF;C3FC;110A 116A 11AF; # (쏼; 쏼; 쏼; 쏼; 쏼; ) HANGUL SYLLABLE SSWAL +C3FD;C3FD;110A 116A 11B0;C3FD;110A 116A 11B0; # (쏽; 쏽; 쏽; 쏽; 쏽; ) HANGUL SYLLABLE SSWALG +C3FE;C3FE;110A 116A 11B1;C3FE;110A 116A 11B1; # (쏾; 쏾; 쏾; 쏾; 쏾; ) HANGUL SYLLABLE SSWALM +C3FF;C3FF;110A 116A 11B2;C3FF;110A 116A 11B2; # (쏿; 쏿; 쏿; 쏿; 쏿; ) HANGUL SYLLABLE SSWALB +C400;C400;110A 116A 11B3;C400;110A 116A 11B3; # (쐀; 쐀; 쐀; 쐀; 쐀; ) HANGUL SYLLABLE SSWALS +C401;C401;110A 116A 11B4;C401;110A 116A 11B4; # (쐁; 쐁; 쐁; 쐁; 쐁; ) HANGUL SYLLABLE SSWALT +C402;C402;110A 116A 11B5;C402;110A 116A 11B5; # (쐂; 쐂; 쐂; 쐂; 쐂; ) HANGUL SYLLABLE SSWALP +C403;C403;110A 116A 11B6;C403;110A 116A 11B6; # (쐃; 쐃; 쐃; 쐃; 쐃; ) HANGUL SYLLABLE SSWALH +C404;C404;110A 116A 11B7;C404;110A 116A 11B7; # (쐄; 쐄; 쐄; 쐄; 쐄; ) HANGUL SYLLABLE SSWAM +C405;C405;110A 116A 11B8;C405;110A 116A 11B8; # (쐅; 쐅; 쐅; 쐅; 쐅; ) HANGUL SYLLABLE SSWAB +C406;C406;110A 116A 11B9;C406;110A 116A 11B9; # (쐆; 쐆; 쐆; 쐆; 쐆; ) HANGUL SYLLABLE SSWABS +C407;C407;110A 116A 11BA;C407;110A 116A 11BA; # (쐇; 쐇; 쐇; 쐇; 쐇; ) HANGUL SYLLABLE SSWAS +C408;C408;110A 116A 11BB;C408;110A 116A 11BB; # (쐈; 쐈; 쐈; 쐈; 쐈; ) HANGUL SYLLABLE SSWASS +C409;C409;110A 116A 11BC;C409;110A 116A 11BC; # (쐉; 쐉; 쐉; 쐉; 쐉; ) HANGUL SYLLABLE SSWANG +C40A;C40A;110A 116A 11BD;C40A;110A 116A 11BD; # (쐊; 쐊; 쐊; 쐊; 쐊; ) HANGUL SYLLABLE SSWAJ +C40B;C40B;110A 116A 11BE;C40B;110A 116A 11BE; # (쐋; 쐋; 쐋; 쐋; 쐋; ) HANGUL SYLLABLE SSWAC +C40C;C40C;110A 116A 11BF;C40C;110A 116A 11BF; # (쐌; 쐌; 쐌; 쐌; 쐌; ) HANGUL SYLLABLE SSWAK +C40D;C40D;110A 116A 11C0;C40D;110A 116A 11C0; # (쐍; 쐍; 쐍; 쐍; 쐍; ) HANGUL SYLLABLE SSWAT +C40E;C40E;110A 116A 11C1;C40E;110A 116A 11C1; # (쐎; 쐎; 쐎; 쐎; 쐎; ) HANGUL SYLLABLE SSWAP +C40F;C40F;110A 116A 11C2;C40F;110A 116A 11C2; # (쐏; 쐏; 쐏; 쐏; 쐏; ) HANGUL SYLLABLE SSWAH +C410;C410;110A 116B;C410;110A 116B; # (쐐; 쐐; 쐐; 쐐; 쐐; ) HANGUL SYLLABLE SSWAE +C411;C411;110A 116B 11A8;C411;110A 116B 11A8; # (쐑; 쐑; 쐑; 쐑; 쐑; ) HANGUL SYLLABLE SSWAEG +C412;C412;110A 116B 11A9;C412;110A 116B 11A9; # (쐒; 쐒; 쐒; 쐒; 쐒; ) HANGUL SYLLABLE SSWAEGG +C413;C413;110A 116B 11AA;C413;110A 116B 11AA; # (쐓; 쐓; 쐓; 쐓; 쐓; ) HANGUL SYLLABLE SSWAEGS +C414;C414;110A 116B 11AB;C414;110A 116B 11AB; # (쐔; 쐔; 쐔; 쐔; 쐔; ) HANGUL SYLLABLE SSWAEN +C415;C415;110A 116B 11AC;C415;110A 116B 11AC; # (쐕; 쐕; 쐕; 쐕; 쐕; ) HANGUL SYLLABLE SSWAENJ +C416;C416;110A 116B 11AD;C416;110A 116B 11AD; # (쐖; 쐖; 쐖; 쐖; 쐖; ) HANGUL SYLLABLE SSWAENH +C417;C417;110A 116B 11AE;C417;110A 116B 11AE; # (쐗; 쐗; 쐗; 쐗; 쐗; ) HANGUL SYLLABLE SSWAED +C418;C418;110A 116B 11AF;C418;110A 116B 11AF; # (쐘; 쐘; 쐘; 쐘; 쐘; ) HANGUL SYLLABLE SSWAEL +C419;C419;110A 116B 11B0;C419;110A 116B 11B0; # (쐙; 쐙; 쐙; 쐙; 쐙; ) HANGUL SYLLABLE SSWAELG +C41A;C41A;110A 116B 11B1;C41A;110A 116B 11B1; # (쐚; 쐚; 쐚; 쐚; 쐚; ) HANGUL SYLLABLE SSWAELM +C41B;C41B;110A 116B 11B2;C41B;110A 116B 11B2; # (쐛; 쐛; 쐛; 쐛; 쐛; ) HANGUL SYLLABLE SSWAELB +C41C;C41C;110A 116B 11B3;C41C;110A 116B 11B3; # (쐜; 쐜; 쐜; 쐜; 쐜; ) HANGUL SYLLABLE SSWAELS +C41D;C41D;110A 116B 11B4;C41D;110A 116B 11B4; # (쐝; 쐝; 쐝; 쐝; 쐝; ) HANGUL SYLLABLE SSWAELT +C41E;C41E;110A 116B 11B5;C41E;110A 116B 11B5; # (쐞; 쐞; 쐞; 쐞; 쐞; ) HANGUL SYLLABLE SSWAELP +C41F;C41F;110A 116B 11B6;C41F;110A 116B 11B6; # (쐟; 쐟; 쐟; 쐟; 쐟; ) HANGUL SYLLABLE SSWAELH +C420;C420;110A 116B 11B7;C420;110A 116B 11B7; # (쐠; 쐠; 쐠; 쐠; 쐠; ) HANGUL SYLLABLE SSWAEM +C421;C421;110A 116B 11B8;C421;110A 116B 11B8; # (쐡; 쐡; 쐡; 쐡; 쐡; ) HANGUL SYLLABLE SSWAEB +C422;C422;110A 116B 11B9;C422;110A 116B 11B9; # (쐢; 쐢; 쐢; 쐢; 쐢; ) HANGUL SYLLABLE SSWAEBS +C423;C423;110A 116B 11BA;C423;110A 116B 11BA; # (쐣; 쐣; 쐣; 쐣; 쐣; ) HANGUL SYLLABLE SSWAES +C424;C424;110A 116B 11BB;C424;110A 116B 11BB; # (쐤; 쐤; 쐤; 쐤; 쐤; ) HANGUL SYLLABLE SSWAESS +C425;C425;110A 116B 11BC;C425;110A 116B 11BC; # (쐥; 쐥; 쐥; 쐥; 쐥; ) HANGUL SYLLABLE SSWAENG +C426;C426;110A 116B 11BD;C426;110A 116B 11BD; # (쐦; 쐦; 쐦; 쐦; 쐦; ) HANGUL SYLLABLE SSWAEJ +C427;C427;110A 116B 11BE;C427;110A 116B 11BE; # (쐧; 쐧; 쐧; 쐧; 쐧; ) HANGUL SYLLABLE SSWAEC +C428;C428;110A 116B 11BF;C428;110A 116B 11BF; # (쐨; 쐨; 쐨; 쐨; 쐨; ) HANGUL SYLLABLE SSWAEK +C429;C429;110A 116B 11C0;C429;110A 116B 11C0; # (쐩; 쐩; 쐩; 쐩; 쐩; ) HANGUL SYLLABLE SSWAET +C42A;C42A;110A 116B 11C1;C42A;110A 116B 11C1; # (쐪; 쐪; 쐪; 쐪; 쐪; ) HANGUL SYLLABLE SSWAEP +C42B;C42B;110A 116B 11C2;C42B;110A 116B 11C2; # (쐫; 쐫; 쐫; 쐫; 쐫; ) HANGUL SYLLABLE SSWAEH +C42C;C42C;110A 116C;C42C;110A 116C; # (쐬; 쐬; 쐬; 쐬; 쐬; ) HANGUL SYLLABLE SSOE +C42D;C42D;110A 116C 11A8;C42D;110A 116C 11A8; # (쐭; 쐭; 쐭; 쐭; 쐭; ) HANGUL SYLLABLE SSOEG +C42E;C42E;110A 116C 11A9;C42E;110A 116C 11A9; # (쐮; 쐮; 쐮; 쐮; 쐮; ) HANGUL SYLLABLE SSOEGG +C42F;C42F;110A 116C 11AA;C42F;110A 116C 11AA; # (쐯; 쐯; 쐯; 쐯; 쐯; ) HANGUL SYLLABLE SSOEGS +C430;C430;110A 116C 11AB;C430;110A 116C 11AB; # (쐰; 쐰; 쐰; 쐰; 쐰; ) HANGUL SYLLABLE SSOEN +C431;C431;110A 116C 11AC;C431;110A 116C 11AC; # (쐱; 쐱; 쐱; 쐱; 쐱; ) HANGUL SYLLABLE SSOENJ +C432;C432;110A 116C 11AD;C432;110A 116C 11AD; # (쐲; 쐲; 쐲; 쐲; 쐲; ) HANGUL SYLLABLE SSOENH +C433;C433;110A 116C 11AE;C433;110A 116C 11AE; # (쐳; 쐳; 쐳; 쐳; 쐳; ) HANGUL SYLLABLE SSOED +C434;C434;110A 116C 11AF;C434;110A 116C 11AF; # (쐴; 쐴; 쐴; 쐴; 쐴; ) HANGUL SYLLABLE SSOEL +C435;C435;110A 116C 11B0;C435;110A 116C 11B0; # (쐵; 쐵; 쐵; 쐵; 쐵; ) HANGUL SYLLABLE SSOELG +C436;C436;110A 116C 11B1;C436;110A 116C 11B1; # (쐶; 쐶; 쐶; 쐶; 쐶; ) HANGUL SYLLABLE SSOELM +C437;C437;110A 116C 11B2;C437;110A 116C 11B2; # (쐷; 쐷; 쐷; 쐷; 쐷; ) HANGUL SYLLABLE SSOELB +C438;C438;110A 116C 11B3;C438;110A 116C 11B3; # (쐸; 쐸; 쐸; 쐸; 쐸; ) HANGUL SYLLABLE SSOELS +C439;C439;110A 116C 11B4;C439;110A 116C 11B4; # (쐹; 쐹; 쐹; 쐹; 쐹; ) HANGUL SYLLABLE SSOELT +C43A;C43A;110A 116C 11B5;C43A;110A 116C 11B5; # (쐺; 쐺; 쐺; 쐺; 쐺; ) HANGUL SYLLABLE SSOELP +C43B;C43B;110A 116C 11B6;C43B;110A 116C 11B6; # (쐻; 쐻; 쐻; 쐻; 쐻; ) HANGUL SYLLABLE SSOELH +C43C;C43C;110A 116C 11B7;C43C;110A 116C 11B7; # (쐼; 쐼; 쐼; 쐼; 쐼; ) HANGUL SYLLABLE SSOEM +C43D;C43D;110A 116C 11B8;C43D;110A 116C 11B8; # (쐽; 쐽; 쐽; 쐽; 쐽; ) HANGUL SYLLABLE SSOEB +C43E;C43E;110A 116C 11B9;C43E;110A 116C 11B9; # (쐾; 쐾; 쐾; 쐾; 쐾; ) HANGUL SYLLABLE SSOEBS +C43F;C43F;110A 116C 11BA;C43F;110A 116C 11BA; # (쐿; 쐿; 쐿; 쐿; 쐿; ) HANGUL SYLLABLE SSOES +C440;C440;110A 116C 11BB;C440;110A 116C 11BB; # (쑀; 쑀; 쑀; 쑀; 쑀; ) HANGUL SYLLABLE SSOESS +C441;C441;110A 116C 11BC;C441;110A 116C 11BC; # (쑁; 쑁; 쑁; 쑁; 쑁; ) HANGUL SYLLABLE SSOENG +C442;C442;110A 116C 11BD;C442;110A 116C 11BD; # (쑂; 쑂; 쑂; 쑂; 쑂; ) HANGUL SYLLABLE SSOEJ +C443;C443;110A 116C 11BE;C443;110A 116C 11BE; # (쑃; 쑃; 쑃; 쑃; 쑃; ) HANGUL SYLLABLE SSOEC +C444;C444;110A 116C 11BF;C444;110A 116C 11BF; # (쑄; 쑄; 쑄; 쑄; 쑄; ) HANGUL SYLLABLE SSOEK +C445;C445;110A 116C 11C0;C445;110A 116C 11C0; # (쑅; 쑅; 쑅; 쑅; 쑅; ) HANGUL SYLLABLE SSOET +C446;C446;110A 116C 11C1;C446;110A 116C 11C1; # (쑆; 쑆; 쑆; 쑆; 쑆; ) HANGUL SYLLABLE SSOEP +C447;C447;110A 116C 11C2;C447;110A 116C 11C2; # (쑇; 쑇; 쑇; 쑇; 쑇; ) HANGUL SYLLABLE SSOEH +C448;C448;110A 116D;C448;110A 116D; # (쑈; 쑈; 쑈; 쑈; 쑈; ) HANGUL SYLLABLE SSYO +C449;C449;110A 116D 11A8;C449;110A 116D 11A8; # (쑉; 쑉; 쑉; 쑉; 쑉; ) HANGUL SYLLABLE SSYOG +C44A;C44A;110A 116D 11A9;C44A;110A 116D 11A9; # (쑊; 쑊; 쑊; 쑊; 쑊; ) HANGUL SYLLABLE SSYOGG +C44B;C44B;110A 116D 11AA;C44B;110A 116D 11AA; # (쑋; 쑋; 쑋; 쑋; 쑋; ) HANGUL SYLLABLE SSYOGS +C44C;C44C;110A 116D 11AB;C44C;110A 116D 11AB; # (쑌; 쑌; 쑌; 쑌; 쑌; ) HANGUL SYLLABLE SSYON +C44D;C44D;110A 116D 11AC;C44D;110A 116D 11AC; # (쑍; 쑍; 쑍; 쑍; 쑍; ) HANGUL SYLLABLE SSYONJ +C44E;C44E;110A 116D 11AD;C44E;110A 116D 11AD; # (쑎; 쑎; 쑎; 쑎; 쑎; ) HANGUL SYLLABLE SSYONH +C44F;C44F;110A 116D 11AE;C44F;110A 116D 11AE; # (쑏; 쑏; 쑏; 쑏; 쑏; ) HANGUL SYLLABLE SSYOD +C450;C450;110A 116D 11AF;C450;110A 116D 11AF; # (쑐; 쑐; 쑐; 쑐; 쑐; ) HANGUL SYLLABLE SSYOL +C451;C451;110A 116D 11B0;C451;110A 116D 11B0; # (쑑; 쑑; 쑑; 쑑; 쑑; ) HANGUL SYLLABLE SSYOLG +C452;C452;110A 116D 11B1;C452;110A 116D 11B1; # (쑒; 쑒; 쑒; 쑒; 쑒; ) HANGUL SYLLABLE SSYOLM +C453;C453;110A 116D 11B2;C453;110A 116D 11B2; # (쑓; 쑓; 쑓; 쑓; 쑓; ) HANGUL SYLLABLE SSYOLB +C454;C454;110A 116D 11B3;C454;110A 116D 11B3; # (쑔; 쑔; 쑔; 쑔; 쑔; ) HANGUL SYLLABLE SSYOLS +C455;C455;110A 116D 11B4;C455;110A 116D 11B4; # (쑕; 쑕; 쑕; 쑕; 쑕; ) HANGUL SYLLABLE SSYOLT +C456;C456;110A 116D 11B5;C456;110A 116D 11B5; # (쑖; 쑖; 쑖; 쑖; 쑖; ) HANGUL SYLLABLE SSYOLP +C457;C457;110A 116D 11B6;C457;110A 116D 11B6; # (쑗; 쑗; 쑗; 쑗; 쑗; ) HANGUL SYLLABLE SSYOLH +C458;C458;110A 116D 11B7;C458;110A 116D 11B7; # (쑘; 쑘; 쑘; 쑘; 쑘; ) HANGUL SYLLABLE SSYOM +C459;C459;110A 116D 11B8;C459;110A 116D 11B8; # (쑙; 쑙; 쑙; 쑙; 쑙; ) HANGUL SYLLABLE SSYOB +C45A;C45A;110A 116D 11B9;C45A;110A 116D 11B9; # (쑚; 쑚; 쑚; 쑚; 쑚; ) HANGUL SYLLABLE SSYOBS +C45B;C45B;110A 116D 11BA;C45B;110A 116D 11BA; # (쑛; 쑛; 쑛; 쑛; 쑛; ) HANGUL SYLLABLE SSYOS +C45C;C45C;110A 116D 11BB;C45C;110A 116D 11BB; # (쑜; 쑜; 쑜; 쑜; 쑜; ) HANGUL SYLLABLE SSYOSS +C45D;C45D;110A 116D 11BC;C45D;110A 116D 11BC; # (쑝; 쑝; 쑝; 쑝; 쑝; ) HANGUL SYLLABLE SSYONG +C45E;C45E;110A 116D 11BD;C45E;110A 116D 11BD; # (쑞; 쑞; 쑞; 쑞; 쑞; ) HANGUL SYLLABLE SSYOJ +C45F;C45F;110A 116D 11BE;C45F;110A 116D 11BE; # (쑟; 쑟; 쑟; 쑟; 쑟; ) HANGUL SYLLABLE SSYOC +C460;C460;110A 116D 11BF;C460;110A 116D 11BF; # (쑠; 쑠; 쑠; 쑠; 쑠; ) HANGUL SYLLABLE SSYOK +C461;C461;110A 116D 11C0;C461;110A 116D 11C0; # (쑡; 쑡; 쑡; 쑡; 쑡; ) HANGUL SYLLABLE SSYOT +C462;C462;110A 116D 11C1;C462;110A 116D 11C1; # (쑢; 쑢; 쑢; 쑢; 쑢; ) HANGUL SYLLABLE SSYOP +C463;C463;110A 116D 11C2;C463;110A 116D 11C2; # (쑣; 쑣; 쑣; 쑣; 쑣; ) HANGUL SYLLABLE SSYOH +C464;C464;110A 116E;C464;110A 116E; # (쑤; 쑤; 쑤; 쑤; 쑤; ) HANGUL SYLLABLE SSU +C465;C465;110A 116E 11A8;C465;110A 116E 11A8; # (쑥; 쑥; 쑥; 쑥; 쑥; ) HANGUL SYLLABLE SSUG +C466;C466;110A 116E 11A9;C466;110A 116E 11A9; # (쑦; 쑦; 쑦; 쑦; 쑦; ) HANGUL SYLLABLE SSUGG +C467;C467;110A 116E 11AA;C467;110A 116E 11AA; # (쑧; 쑧; 쑧; 쑧; 쑧; ) HANGUL SYLLABLE SSUGS +C468;C468;110A 116E 11AB;C468;110A 116E 11AB; # (쑨; 쑨; 쑨; 쑨; 쑨; ) HANGUL SYLLABLE SSUN +C469;C469;110A 116E 11AC;C469;110A 116E 11AC; # (쑩; 쑩; 쑩; 쑩; 쑩; ) HANGUL SYLLABLE SSUNJ +C46A;C46A;110A 116E 11AD;C46A;110A 116E 11AD; # (쑪; 쑪; 쑪; 쑪; 쑪; ) HANGUL SYLLABLE SSUNH +C46B;C46B;110A 116E 11AE;C46B;110A 116E 11AE; # (쑫; 쑫; 쑫; 쑫; 쑫; ) HANGUL SYLLABLE SSUD +C46C;C46C;110A 116E 11AF;C46C;110A 116E 11AF; # (쑬; 쑬; 쑬; 쑬; 쑬; ) HANGUL SYLLABLE SSUL +C46D;C46D;110A 116E 11B0;C46D;110A 116E 11B0; # (쑭; 쑭; 쑭; 쑭; 쑭; ) HANGUL SYLLABLE SSULG +C46E;C46E;110A 116E 11B1;C46E;110A 116E 11B1; # (쑮; 쑮; 쑮; 쑮; 쑮; ) HANGUL SYLLABLE SSULM +C46F;C46F;110A 116E 11B2;C46F;110A 116E 11B2; # (쑯; 쑯; 쑯; 쑯; 쑯; ) HANGUL SYLLABLE SSULB +C470;C470;110A 116E 11B3;C470;110A 116E 11B3; # (쑰; 쑰; 쑰; 쑰; 쑰; ) HANGUL SYLLABLE SSULS +C471;C471;110A 116E 11B4;C471;110A 116E 11B4; # (쑱; 쑱; 쑱; 쑱; 쑱; ) HANGUL SYLLABLE SSULT +C472;C472;110A 116E 11B5;C472;110A 116E 11B5; # (쑲; 쑲; 쑲; 쑲; 쑲; ) HANGUL SYLLABLE SSULP +C473;C473;110A 116E 11B6;C473;110A 116E 11B6; # (쑳; 쑳; 쑳; 쑳; 쑳; ) HANGUL SYLLABLE SSULH +C474;C474;110A 116E 11B7;C474;110A 116E 11B7; # (쑴; 쑴; 쑴; 쑴; 쑴; ) HANGUL SYLLABLE SSUM +C475;C475;110A 116E 11B8;C475;110A 116E 11B8; # (쑵; 쑵; 쑵; 쑵; 쑵; ) HANGUL SYLLABLE SSUB +C476;C476;110A 116E 11B9;C476;110A 116E 11B9; # (쑶; 쑶; 쑶; 쑶; 쑶; ) HANGUL SYLLABLE SSUBS +C477;C477;110A 116E 11BA;C477;110A 116E 11BA; # (쑷; 쑷; 쑷; 쑷; 쑷; ) HANGUL SYLLABLE SSUS +C478;C478;110A 116E 11BB;C478;110A 116E 11BB; # (쑸; 쑸; 쑸; 쑸; 쑸; ) HANGUL SYLLABLE SSUSS +C479;C479;110A 116E 11BC;C479;110A 116E 11BC; # (쑹; 쑹; 쑹; 쑹; 쑹; ) HANGUL SYLLABLE SSUNG +C47A;C47A;110A 116E 11BD;C47A;110A 116E 11BD; # (쑺; 쑺; 쑺; 쑺; 쑺; ) HANGUL SYLLABLE SSUJ +C47B;C47B;110A 116E 11BE;C47B;110A 116E 11BE; # (쑻; 쑻; 쑻; 쑻; 쑻; ) HANGUL SYLLABLE SSUC +C47C;C47C;110A 116E 11BF;C47C;110A 116E 11BF; # (쑼; 쑼; 쑼; 쑼; 쑼; ) HANGUL SYLLABLE SSUK +C47D;C47D;110A 116E 11C0;C47D;110A 116E 11C0; # (쑽; 쑽; 쑽; 쑽; 쑽; ) HANGUL SYLLABLE SSUT +C47E;C47E;110A 116E 11C1;C47E;110A 116E 11C1; # (쑾; 쑾; 쑾; 쑾; 쑾; ) HANGUL SYLLABLE SSUP +C47F;C47F;110A 116E 11C2;C47F;110A 116E 11C2; # (쑿; 쑿; 쑿; 쑿; 쑿; ) HANGUL SYLLABLE SSUH +C480;C480;110A 116F;C480;110A 116F; # (쒀; 쒀; 쒀; 쒀; 쒀; ) HANGUL SYLLABLE SSWEO +C481;C481;110A 116F 11A8;C481;110A 116F 11A8; # (쒁; 쒁; 쒁; 쒁; 쒁; ) HANGUL SYLLABLE SSWEOG +C482;C482;110A 116F 11A9;C482;110A 116F 11A9; # (쒂; 쒂; 쒂; 쒂; 쒂; ) HANGUL SYLLABLE SSWEOGG +C483;C483;110A 116F 11AA;C483;110A 116F 11AA; # (쒃; 쒃; 쒃; 쒃; 쒃; ) HANGUL SYLLABLE SSWEOGS +C484;C484;110A 116F 11AB;C484;110A 116F 11AB; # (쒄; 쒄; 쒄; 쒄; 쒄; ) HANGUL SYLLABLE SSWEON +C485;C485;110A 116F 11AC;C485;110A 116F 11AC; # (쒅; 쒅; 쒅; 쒅; 쒅; ) HANGUL SYLLABLE SSWEONJ +C486;C486;110A 116F 11AD;C486;110A 116F 11AD; # (쒆; 쒆; 쒆; 쒆; 쒆; ) HANGUL SYLLABLE SSWEONH +C487;C487;110A 116F 11AE;C487;110A 116F 11AE; # (쒇; 쒇; 쒇; 쒇; 쒇; ) HANGUL SYLLABLE SSWEOD +C488;C488;110A 116F 11AF;C488;110A 116F 11AF; # (쒈; 쒈; 쒈; 쒈; 쒈; ) HANGUL SYLLABLE SSWEOL +C489;C489;110A 116F 11B0;C489;110A 116F 11B0; # (쒉; 쒉; 쒉; 쒉; 쒉; ) HANGUL SYLLABLE SSWEOLG +C48A;C48A;110A 116F 11B1;C48A;110A 116F 11B1; # (쒊; 쒊; 쒊; 쒊; 쒊; ) HANGUL SYLLABLE SSWEOLM +C48B;C48B;110A 116F 11B2;C48B;110A 116F 11B2; # (쒋; 쒋; 쒋; 쒋; 쒋; ) HANGUL SYLLABLE SSWEOLB +C48C;C48C;110A 116F 11B3;C48C;110A 116F 11B3; # (쒌; 쒌; 쒌; 쒌; 쒌; ) HANGUL SYLLABLE SSWEOLS +C48D;C48D;110A 116F 11B4;C48D;110A 116F 11B4; # (쒍; 쒍; 쒍; 쒍; 쒍; ) HANGUL SYLLABLE SSWEOLT +C48E;C48E;110A 116F 11B5;C48E;110A 116F 11B5; # (쒎; 쒎; 쒎; 쒎; 쒎; ) HANGUL SYLLABLE SSWEOLP +C48F;C48F;110A 116F 11B6;C48F;110A 116F 11B6; # (쒏; 쒏; 쒏; 쒏; 쒏; ) HANGUL SYLLABLE SSWEOLH +C490;C490;110A 116F 11B7;C490;110A 116F 11B7; # (쒐; 쒐; 쒐; 쒐; 쒐; ) HANGUL SYLLABLE SSWEOM +C491;C491;110A 116F 11B8;C491;110A 116F 11B8; # (쒑; 쒑; 쒑; 쒑; 쒑; ) HANGUL SYLLABLE SSWEOB +C492;C492;110A 116F 11B9;C492;110A 116F 11B9; # (쒒; 쒒; 쒒; 쒒; 쒒; ) HANGUL SYLLABLE SSWEOBS +C493;C493;110A 116F 11BA;C493;110A 116F 11BA; # (쒓; 쒓; 쒓; 쒓; 쒓; ) HANGUL SYLLABLE SSWEOS +C494;C494;110A 116F 11BB;C494;110A 116F 11BB; # (쒔; 쒔; 쒔; 쒔; 쒔; ) HANGUL SYLLABLE SSWEOSS +C495;C495;110A 116F 11BC;C495;110A 116F 11BC; # (쒕; 쒕; 쒕; 쒕; 쒕; ) HANGUL SYLLABLE SSWEONG +C496;C496;110A 116F 11BD;C496;110A 116F 11BD; # (쒖; 쒖; 쒖; 쒖; 쒖; ) HANGUL SYLLABLE SSWEOJ +C497;C497;110A 116F 11BE;C497;110A 116F 11BE; # (쒗; 쒗; 쒗; 쒗; 쒗; ) HANGUL SYLLABLE SSWEOC +C498;C498;110A 116F 11BF;C498;110A 116F 11BF; # (쒘; 쒘; 쒘; 쒘; 쒘; ) HANGUL SYLLABLE SSWEOK +C499;C499;110A 116F 11C0;C499;110A 116F 11C0; # (쒙; 쒙; 쒙; 쒙; 쒙; ) HANGUL SYLLABLE SSWEOT +C49A;C49A;110A 116F 11C1;C49A;110A 116F 11C1; # (쒚; 쒚; 쒚; 쒚; 쒚; ) HANGUL SYLLABLE SSWEOP +C49B;C49B;110A 116F 11C2;C49B;110A 116F 11C2; # (쒛; 쒛; 쒛; 쒛; 쒛; ) HANGUL SYLLABLE SSWEOH +C49C;C49C;110A 1170;C49C;110A 1170; # (쒜; 쒜; 쒜; 쒜; 쒜; ) HANGUL SYLLABLE SSWE +C49D;C49D;110A 1170 11A8;C49D;110A 1170 11A8; # (쒝; 쒝; 쒝; 쒝; 쒝; ) HANGUL SYLLABLE SSWEG +C49E;C49E;110A 1170 11A9;C49E;110A 1170 11A9; # (쒞; 쒞; 쒞; 쒞; 쒞; ) HANGUL SYLLABLE SSWEGG +C49F;C49F;110A 1170 11AA;C49F;110A 1170 11AA; # (쒟; 쒟; 쒟; 쒟; 쒟; ) HANGUL SYLLABLE SSWEGS +C4A0;C4A0;110A 1170 11AB;C4A0;110A 1170 11AB; # (쒠; 쒠; 쒠; 쒠; 쒠; ) HANGUL SYLLABLE SSWEN +C4A1;C4A1;110A 1170 11AC;C4A1;110A 1170 11AC; # (쒡; 쒡; 쒡; 쒡; 쒡; ) HANGUL SYLLABLE SSWENJ +C4A2;C4A2;110A 1170 11AD;C4A2;110A 1170 11AD; # (쒢; 쒢; 쒢; 쒢; 쒢; ) HANGUL SYLLABLE SSWENH +C4A3;C4A3;110A 1170 11AE;C4A3;110A 1170 11AE; # (쒣; 쒣; 쒣; 쒣; 쒣; ) HANGUL SYLLABLE SSWED +C4A4;C4A4;110A 1170 11AF;C4A4;110A 1170 11AF; # (쒤; 쒤; 쒤; 쒤; 쒤; ) HANGUL SYLLABLE SSWEL +C4A5;C4A5;110A 1170 11B0;C4A5;110A 1170 11B0; # (쒥; 쒥; 쒥; 쒥; 쒥; ) HANGUL SYLLABLE SSWELG +C4A6;C4A6;110A 1170 11B1;C4A6;110A 1170 11B1; # (쒦; 쒦; 쒦; 쒦; 쒦; ) HANGUL SYLLABLE SSWELM +C4A7;C4A7;110A 1170 11B2;C4A7;110A 1170 11B2; # (쒧; 쒧; 쒧; 쒧; 쒧; ) HANGUL SYLLABLE SSWELB +C4A8;C4A8;110A 1170 11B3;C4A8;110A 1170 11B3; # (쒨; 쒨; 쒨; 쒨; 쒨; ) HANGUL SYLLABLE SSWELS +C4A9;C4A9;110A 1170 11B4;C4A9;110A 1170 11B4; # (쒩; 쒩; 쒩; 쒩; 쒩; ) HANGUL SYLLABLE SSWELT +C4AA;C4AA;110A 1170 11B5;C4AA;110A 1170 11B5; # (쒪; 쒪; 쒪; 쒪; 쒪; ) HANGUL SYLLABLE SSWELP +C4AB;C4AB;110A 1170 11B6;C4AB;110A 1170 11B6; # (쒫; 쒫; 쒫; 쒫; 쒫; ) HANGUL SYLLABLE SSWELH +C4AC;C4AC;110A 1170 11B7;C4AC;110A 1170 11B7; # (쒬; 쒬; 쒬; 쒬; 쒬; ) HANGUL SYLLABLE SSWEM +C4AD;C4AD;110A 1170 11B8;C4AD;110A 1170 11B8; # (쒭; 쒭; 쒭; 쒭; 쒭; ) HANGUL SYLLABLE SSWEB +C4AE;C4AE;110A 1170 11B9;C4AE;110A 1170 11B9; # (쒮; 쒮; 쒮; 쒮; 쒮; ) HANGUL SYLLABLE SSWEBS +C4AF;C4AF;110A 1170 11BA;C4AF;110A 1170 11BA; # (쒯; 쒯; 쒯; 쒯; 쒯; ) HANGUL SYLLABLE SSWES +C4B0;C4B0;110A 1170 11BB;C4B0;110A 1170 11BB; # (쒰; 쒰; 쒰; 쒰; 쒰; ) HANGUL SYLLABLE SSWESS +C4B1;C4B1;110A 1170 11BC;C4B1;110A 1170 11BC; # (쒱; 쒱; 쒱; 쒱; 쒱; ) HANGUL SYLLABLE SSWENG +C4B2;C4B2;110A 1170 11BD;C4B2;110A 1170 11BD; # (쒲; 쒲; 쒲; 쒲; 쒲; ) HANGUL SYLLABLE SSWEJ +C4B3;C4B3;110A 1170 11BE;C4B3;110A 1170 11BE; # (쒳; 쒳; 쒳; 쒳; 쒳; ) HANGUL SYLLABLE SSWEC +C4B4;C4B4;110A 1170 11BF;C4B4;110A 1170 11BF; # (쒴; 쒴; 쒴; 쒴; 쒴; ) HANGUL SYLLABLE SSWEK +C4B5;C4B5;110A 1170 11C0;C4B5;110A 1170 11C0; # (쒵; 쒵; 쒵; 쒵; 쒵; ) HANGUL SYLLABLE SSWET +C4B6;C4B6;110A 1170 11C1;C4B6;110A 1170 11C1; # (쒶; 쒶; 쒶; 쒶; 쒶; ) HANGUL SYLLABLE SSWEP +C4B7;C4B7;110A 1170 11C2;C4B7;110A 1170 11C2; # (쒷; 쒷; 쒷; 쒷; 쒷; ) HANGUL SYLLABLE SSWEH +C4B8;C4B8;110A 1171;C4B8;110A 1171; # (쒸; 쒸; 쒸; 쒸; 쒸; ) HANGUL SYLLABLE SSWI +C4B9;C4B9;110A 1171 11A8;C4B9;110A 1171 11A8; # (쒹; 쒹; 쒹; 쒹; 쒹; ) HANGUL SYLLABLE SSWIG +C4BA;C4BA;110A 1171 11A9;C4BA;110A 1171 11A9; # (쒺; 쒺; 쒺; 쒺; 쒺; ) HANGUL SYLLABLE SSWIGG +C4BB;C4BB;110A 1171 11AA;C4BB;110A 1171 11AA; # (쒻; 쒻; 쒻; 쒻; 쒻; ) HANGUL SYLLABLE SSWIGS +C4BC;C4BC;110A 1171 11AB;C4BC;110A 1171 11AB; # (쒼; 쒼; 쒼; 쒼; 쒼; ) HANGUL SYLLABLE SSWIN +C4BD;C4BD;110A 1171 11AC;C4BD;110A 1171 11AC; # (쒽; 쒽; 쒽; 쒽; 쒽; ) HANGUL SYLLABLE SSWINJ +C4BE;C4BE;110A 1171 11AD;C4BE;110A 1171 11AD; # (쒾; 쒾; 쒾; 쒾; 쒾; ) HANGUL SYLLABLE SSWINH +C4BF;C4BF;110A 1171 11AE;C4BF;110A 1171 11AE; # (쒿; 쒿; 쒿; 쒿; 쒿; ) HANGUL SYLLABLE SSWID +C4C0;C4C0;110A 1171 11AF;C4C0;110A 1171 11AF; # (쓀; 쓀; 쓀; 쓀; 쓀; ) HANGUL SYLLABLE SSWIL +C4C1;C4C1;110A 1171 11B0;C4C1;110A 1171 11B0; # (쓁; 쓁; 쓁; 쓁; 쓁; ) HANGUL SYLLABLE SSWILG +C4C2;C4C2;110A 1171 11B1;C4C2;110A 1171 11B1; # (쓂; 쓂; 쓂; 쓂; 쓂; ) HANGUL SYLLABLE SSWILM +C4C3;C4C3;110A 1171 11B2;C4C3;110A 1171 11B2; # (쓃; 쓃; 쓃; 쓃; 쓃; ) HANGUL SYLLABLE SSWILB +C4C4;C4C4;110A 1171 11B3;C4C4;110A 1171 11B3; # (쓄; 쓄; 쓄; 쓄; 쓄; ) HANGUL SYLLABLE SSWILS +C4C5;C4C5;110A 1171 11B4;C4C5;110A 1171 11B4; # (쓅; 쓅; 쓅; 쓅; 쓅; ) HANGUL SYLLABLE SSWILT +C4C6;C4C6;110A 1171 11B5;C4C6;110A 1171 11B5; # (쓆; 쓆; 쓆; 쓆; 쓆; ) HANGUL SYLLABLE SSWILP +C4C7;C4C7;110A 1171 11B6;C4C7;110A 1171 11B6; # (쓇; 쓇; 쓇; 쓇; 쓇; ) HANGUL SYLLABLE SSWILH +C4C8;C4C8;110A 1171 11B7;C4C8;110A 1171 11B7; # (쓈; 쓈; 쓈; 쓈; 쓈; ) HANGUL SYLLABLE SSWIM +C4C9;C4C9;110A 1171 11B8;C4C9;110A 1171 11B8; # (쓉; 쓉; 쓉; 쓉; 쓉; ) HANGUL SYLLABLE SSWIB +C4CA;C4CA;110A 1171 11B9;C4CA;110A 1171 11B9; # (쓊; 쓊; 쓊; 쓊; 쓊; ) HANGUL SYLLABLE SSWIBS +C4CB;C4CB;110A 1171 11BA;C4CB;110A 1171 11BA; # (쓋; 쓋; 쓋; 쓋; 쓋; ) HANGUL SYLLABLE SSWIS +C4CC;C4CC;110A 1171 11BB;C4CC;110A 1171 11BB; # (쓌; 쓌; 쓌; 쓌; 쓌; ) HANGUL SYLLABLE SSWISS +C4CD;C4CD;110A 1171 11BC;C4CD;110A 1171 11BC; # (쓍; 쓍; 쓍; 쓍; 쓍; ) HANGUL SYLLABLE SSWING +C4CE;C4CE;110A 1171 11BD;C4CE;110A 1171 11BD; # (쓎; 쓎; 쓎; 쓎; 쓎; ) HANGUL SYLLABLE SSWIJ +C4CF;C4CF;110A 1171 11BE;C4CF;110A 1171 11BE; # (쓏; 쓏; 쓏; 쓏; 쓏; ) HANGUL SYLLABLE SSWIC +C4D0;C4D0;110A 1171 11BF;C4D0;110A 1171 11BF; # (쓐; 쓐; 쓐; 쓐; 쓐; ) HANGUL SYLLABLE SSWIK +C4D1;C4D1;110A 1171 11C0;C4D1;110A 1171 11C0; # (쓑; 쓑; 쓑; 쓑; 쓑; ) HANGUL SYLLABLE SSWIT +C4D2;C4D2;110A 1171 11C1;C4D2;110A 1171 11C1; # (쓒; 쓒; 쓒; 쓒; 쓒; ) HANGUL SYLLABLE SSWIP +C4D3;C4D3;110A 1171 11C2;C4D3;110A 1171 11C2; # (쓓; 쓓; 쓓; 쓓; 쓓; ) HANGUL SYLLABLE SSWIH +C4D4;C4D4;110A 1172;C4D4;110A 1172; # (쓔; 쓔; 쓔; 쓔; 쓔; ) HANGUL SYLLABLE SSYU +C4D5;C4D5;110A 1172 11A8;C4D5;110A 1172 11A8; # (쓕; 쓕; 쓕; 쓕; 쓕; ) HANGUL SYLLABLE SSYUG +C4D6;C4D6;110A 1172 11A9;C4D6;110A 1172 11A9; # (쓖; 쓖; 쓖; 쓖; 쓖; ) HANGUL SYLLABLE SSYUGG +C4D7;C4D7;110A 1172 11AA;C4D7;110A 1172 11AA; # (쓗; 쓗; 쓗; 쓗; 쓗; ) HANGUL SYLLABLE SSYUGS +C4D8;C4D8;110A 1172 11AB;C4D8;110A 1172 11AB; # (쓘; 쓘; 쓘; 쓘; 쓘; ) HANGUL SYLLABLE SSYUN +C4D9;C4D9;110A 1172 11AC;C4D9;110A 1172 11AC; # (쓙; 쓙; 쓙; 쓙; 쓙; ) HANGUL SYLLABLE SSYUNJ +C4DA;C4DA;110A 1172 11AD;C4DA;110A 1172 11AD; # (쓚; 쓚; 쓚; 쓚; 쓚; ) HANGUL SYLLABLE SSYUNH +C4DB;C4DB;110A 1172 11AE;C4DB;110A 1172 11AE; # (쓛; 쓛; 쓛; 쓛; 쓛; ) HANGUL SYLLABLE SSYUD +C4DC;C4DC;110A 1172 11AF;C4DC;110A 1172 11AF; # (쓜; 쓜; 쓜; 쓜; 쓜; ) HANGUL SYLLABLE SSYUL +C4DD;C4DD;110A 1172 11B0;C4DD;110A 1172 11B0; # (쓝; 쓝; 쓝; 쓝; 쓝; ) HANGUL SYLLABLE SSYULG +C4DE;C4DE;110A 1172 11B1;C4DE;110A 1172 11B1; # (쓞; 쓞; 쓞; 쓞; 쓞; ) HANGUL SYLLABLE SSYULM +C4DF;C4DF;110A 1172 11B2;C4DF;110A 1172 11B2; # (쓟; 쓟; 쓟; 쓟; 쓟; ) HANGUL SYLLABLE SSYULB +C4E0;C4E0;110A 1172 11B3;C4E0;110A 1172 11B3; # (쓠; 쓠; 쓠; 쓠; 쓠; ) HANGUL SYLLABLE SSYULS +C4E1;C4E1;110A 1172 11B4;C4E1;110A 1172 11B4; # (쓡; 쓡; 쓡; 쓡; 쓡; ) HANGUL SYLLABLE SSYULT +C4E2;C4E2;110A 1172 11B5;C4E2;110A 1172 11B5; # (쓢; 쓢; 쓢; 쓢; 쓢; ) HANGUL SYLLABLE SSYULP +C4E3;C4E3;110A 1172 11B6;C4E3;110A 1172 11B6; # (쓣; 쓣; 쓣; 쓣; 쓣; ) HANGUL SYLLABLE SSYULH +C4E4;C4E4;110A 1172 11B7;C4E4;110A 1172 11B7; # (쓤; 쓤; 쓤; 쓤; 쓤; ) HANGUL SYLLABLE SSYUM +C4E5;C4E5;110A 1172 11B8;C4E5;110A 1172 11B8; # (쓥; 쓥; 쓥; 쓥; 쓥; ) HANGUL SYLLABLE SSYUB +C4E6;C4E6;110A 1172 11B9;C4E6;110A 1172 11B9; # (쓦; 쓦; 쓦; 쓦; 쓦; ) HANGUL SYLLABLE SSYUBS +C4E7;C4E7;110A 1172 11BA;C4E7;110A 1172 11BA; # (쓧; 쓧; 쓧; 쓧; 쓧; ) HANGUL SYLLABLE SSYUS +C4E8;C4E8;110A 1172 11BB;C4E8;110A 1172 11BB; # (쓨; 쓨; 쓨; 쓨; 쓨; ) HANGUL SYLLABLE SSYUSS +C4E9;C4E9;110A 1172 11BC;C4E9;110A 1172 11BC; # (쓩; 쓩; 쓩; 쓩; 쓩; ) HANGUL SYLLABLE SSYUNG +C4EA;C4EA;110A 1172 11BD;C4EA;110A 1172 11BD; # (쓪; 쓪; 쓪; 쓪; 쓪; ) HANGUL SYLLABLE SSYUJ +C4EB;C4EB;110A 1172 11BE;C4EB;110A 1172 11BE; # (쓫; 쓫; 쓫; 쓫; 쓫; ) HANGUL SYLLABLE SSYUC +C4EC;C4EC;110A 1172 11BF;C4EC;110A 1172 11BF; # (쓬; 쓬; 쓬; 쓬; 쓬; ) HANGUL SYLLABLE SSYUK +C4ED;C4ED;110A 1172 11C0;C4ED;110A 1172 11C0; # (쓭; 쓭; 쓭; 쓭; 쓭; ) HANGUL SYLLABLE SSYUT +C4EE;C4EE;110A 1172 11C1;C4EE;110A 1172 11C1; # (쓮; 쓮; 쓮; 쓮; 쓮; ) HANGUL SYLLABLE SSYUP +C4EF;C4EF;110A 1172 11C2;C4EF;110A 1172 11C2; # (쓯; 쓯; 쓯; 쓯; 쓯; ) HANGUL SYLLABLE SSYUH +C4F0;C4F0;110A 1173;C4F0;110A 1173; # (쓰; 쓰; 쓰; 쓰; 쓰; ) HANGUL SYLLABLE SSEU +C4F1;C4F1;110A 1173 11A8;C4F1;110A 1173 11A8; # (쓱; 쓱; 쓱; 쓱; 쓱; ) HANGUL SYLLABLE SSEUG +C4F2;C4F2;110A 1173 11A9;C4F2;110A 1173 11A9; # (쓲; 쓲; 쓲; 쓲; 쓲; ) HANGUL SYLLABLE SSEUGG +C4F3;C4F3;110A 1173 11AA;C4F3;110A 1173 11AA; # (쓳; 쓳; 쓳; 쓳; 쓳; ) HANGUL SYLLABLE SSEUGS +C4F4;C4F4;110A 1173 11AB;C4F4;110A 1173 11AB; # (쓴; 쓴; 쓴; 쓴; 쓴; ) HANGUL SYLLABLE SSEUN +C4F5;C4F5;110A 1173 11AC;C4F5;110A 1173 11AC; # (쓵; 쓵; 쓵; 쓵; 쓵; ) HANGUL SYLLABLE SSEUNJ +C4F6;C4F6;110A 1173 11AD;C4F6;110A 1173 11AD; # (쓶; 쓶; 쓶; 쓶; 쓶; ) HANGUL SYLLABLE SSEUNH +C4F7;C4F7;110A 1173 11AE;C4F7;110A 1173 11AE; # (쓷; 쓷; 쓷; 쓷; 쓷; ) HANGUL SYLLABLE SSEUD +C4F8;C4F8;110A 1173 11AF;C4F8;110A 1173 11AF; # (쓸; 쓸; 쓸; 쓸; 쓸; ) HANGUL SYLLABLE SSEUL +C4F9;C4F9;110A 1173 11B0;C4F9;110A 1173 11B0; # (쓹; 쓹; 쓹; 쓹; 쓹; ) HANGUL SYLLABLE SSEULG +C4FA;C4FA;110A 1173 11B1;C4FA;110A 1173 11B1; # (쓺; 쓺; 쓺; 쓺; 쓺; ) HANGUL SYLLABLE SSEULM +C4FB;C4FB;110A 1173 11B2;C4FB;110A 1173 11B2; # (쓻; 쓻; 쓻; 쓻; 쓻; ) HANGUL SYLLABLE SSEULB +C4FC;C4FC;110A 1173 11B3;C4FC;110A 1173 11B3; # (쓼; 쓼; 쓼; 쓼; 쓼; ) HANGUL SYLLABLE SSEULS +C4FD;C4FD;110A 1173 11B4;C4FD;110A 1173 11B4; # (쓽; 쓽; 쓽; 쓽; 쓽; ) HANGUL SYLLABLE SSEULT +C4FE;C4FE;110A 1173 11B5;C4FE;110A 1173 11B5; # (쓾; 쓾; 쓾; 쓾; 쓾; ) HANGUL SYLLABLE SSEULP +C4FF;C4FF;110A 1173 11B6;C4FF;110A 1173 11B6; # (쓿; 쓿; 쓿; 쓿; 쓿; ) HANGUL SYLLABLE SSEULH +C500;C500;110A 1173 11B7;C500;110A 1173 11B7; # (씀; 씀; 씀; 씀; 씀; ) HANGUL SYLLABLE SSEUM +C501;C501;110A 1173 11B8;C501;110A 1173 11B8; # (씁; 씁; 씁; 씁; 씁; ) HANGUL SYLLABLE SSEUB +C502;C502;110A 1173 11B9;C502;110A 1173 11B9; # (씂; 씂; 씂; 씂; 씂; ) HANGUL SYLLABLE SSEUBS +C503;C503;110A 1173 11BA;C503;110A 1173 11BA; # (씃; 씃; 씃; 씃; 씃; ) HANGUL SYLLABLE SSEUS +C504;C504;110A 1173 11BB;C504;110A 1173 11BB; # (씄; 씄; 씄; 씄; 씄; ) HANGUL SYLLABLE SSEUSS +C505;C505;110A 1173 11BC;C505;110A 1173 11BC; # (씅; 씅; 씅; 씅; 씅; ) HANGUL SYLLABLE SSEUNG +C506;C506;110A 1173 11BD;C506;110A 1173 11BD; # (씆; 씆; 씆; 씆; 씆; ) HANGUL SYLLABLE SSEUJ +C507;C507;110A 1173 11BE;C507;110A 1173 11BE; # (씇; 씇; 씇; 씇; 씇; ) HANGUL SYLLABLE SSEUC +C508;C508;110A 1173 11BF;C508;110A 1173 11BF; # (씈; 씈; 씈; 씈; 씈; ) HANGUL SYLLABLE SSEUK +C509;C509;110A 1173 11C0;C509;110A 1173 11C0; # (씉; 씉; 씉; 씉; 씉; ) HANGUL SYLLABLE SSEUT +C50A;C50A;110A 1173 11C1;C50A;110A 1173 11C1; # (씊; 씊; 씊; 씊; 씊; ) HANGUL SYLLABLE SSEUP +C50B;C50B;110A 1173 11C2;C50B;110A 1173 11C2; # (씋; 씋; 씋; 씋; 씋; ) HANGUL SYLLABLE SSEUH +C50C;C50C;110A 1174;C50C;110A 1174; # (씌; 씌; 씌; 씌; 씌; ) HANGUL SYLLABLE SSYI +C50D;C50D;110A 1174 11A8;C50D;110A 1174 11A8; # (씍; 씍; 씍; 씍; 씍; ) HANGUL SYLLABLE SSYIG +C50E;C50E;110A 1174 11A9;C50E;110A 1174 11A9; # (씎; 씎; 씎; 씎; 씎; ) HANGUL SYLLABLE SSYIGG +C50F;C50F;110A 1174 11AA;C50F;110A 1174 11AA; # (씏; 씏; 씏; 씏; 씏; ) HANGUL SYLLABLE SSYIGS +C510;C510;110A 1174 11AB;C510;110A 1174 11AB; # (씐; 씐; 씐; 씐; 씐; ) HANGUL SYLLABLE SSYIN +C511;C511;110A 1174 11AC;C511;110A 1174 11AC; # (씑; 씑; 씑; 씑; 씑; ) HANGUL SYLLABLE SSYINJ +C512;C512;110A 1174 11AD;C512;110A 1174 11AD; # (씒; 씒; 씒; 씒; 씒; ) HANGUL SYLLABLE SSYINH +C513;C513;110A 1174 11AE;C513;110A 1174 11AE; # (씓; 씓; 씓; 씓; 씓; ) HANGUL SYLLABLE SSYID +C514;C514;110A 1174 11AF;C514;110A 1174 11AF; # (씔; 씔; 씔; 씔; 씔; ) HANGUL SYLLABLE SSYIL +C515;C515;110A 1174 11B0;C515;110A 1174 11B0; # (씕; 씕; 씕; 씕; 씕; ) HANGUL SYLLABLE SSYILG +C516;C516;110A 1174 11B1;C516;110A 1174 11B1; # (씖; 씖; 씖; 씖; 씖; ) HANGUL SYLLABLE SSYILM +C517;C517;110A 1174 11B2;C517;110A 1174 11B2; # (씗; 씗; 씗; 씗; 씗; ) HANGUL SYLLABLE SSYILB +C518;C518;110A 1174 11B3;C518;110A 1174 11B3; # (씘; 씘; 씘; 씘; 씘; ) HANGUL SYLLABLE SSYILS +C519;C519;110A 1174 11B4;C519;110A 1174 11B4; # (씙; 씙; 씙; 씙; 씙; ) HANGUL SYLLABLE SSYILT +C51A;C51A;110A 1174 11B5;C51A;110A 1174 11B5; # (씚; 씚; 씚; 씚; 씚; ) HANGUL SYLLABLE SSYILP +C51B;C51B;110A 1174 11B6;C51B;110A 1174 11B6; # (씛; 씛; 씛; 씛; 씛; ) HANGUL SYLLABLE SSYILH +C51C;C51C;110A 1174 11B7;C51C;110A 1174 11B7; # (씜; 씜; 씜; 씜; 씜; ) HANGUL SYLLABLE SSYIM +C51D;C51D;110A 1174 11B8;C51D;110A 1174 11B8; # (씝; 씝; 씝; 씝; 씝; ) HANGUL SYLLABLE SSYIB +C51E;C51E;110A 1174 11B9;C51E;110A 1174 11B9; # (씞; 씞; 씞; 씞; 씞; ) HANGUL SYLLABLE SSYIBS +C51F;C51F;110A 1174 11BA;C51F;110A 1174 11BA; # (씟; 씟; 씟; 씟; 씟; ) HANGUL SYLLABLE SSYIS +C520;C520;110A 1174 11BB;C520;110A 1174 11BB; # (씠; 씠; 씠; 씠; 씠; ) HANGUL SYLLABLE SSYISS +C521;C521;110A 1174 11BC;C521;110A 1174 11BC; # (씡; 씡; 씡; 씡; 씡; ) HANGUL SYLLABLE SSYING +C522;C522;110A 1174 11BD;C522;110A 1174 11BD; # (씢; 씢; 씢; 씢; 씢; ) HANGUL SYLLABLE SSYIJ +C523;C523;110A 1174 11BE;C523;110A 1174 11BE; # (씣; 씣; 씣; 씣; 씣; ) HANGUL SYLLABLE SSYIC +C524;C524;110A 1174 11BF;C524;110A 1174 11BF; # (씤; 씤; 씤; 씤; 씤; ) HANGUL SYLLABLE SSYIK +C525;C525;110A 1174 11C0;C525;110A 1174 11C0; # (씥; 씥; 씥; 씥; 씥; ) HANGUL SYLLABLE SSYIT +C526;C526;110A 1174 11C1;C526;110A 1174 11C1; # (씦; 씦; 씦; 씦; 씦; ) HANGUL SYLLABLE SSYIP +C527;C527;110A 1174 11C2;C527;110A 1174 11C2; # (씧; 씧; 씧; 씧; 씧; ) HANGUL SYLLABLE SSYIH +C528;C528;110A 1175;C528;110A 1175; # (씨; 씨; 씨; 씨; 씨; ) HANGUL SYLLABLE SSI +C529;C529;110A 1175 11A8;C529;110A 1175 11A8; # (씩; 씩; 씩; 씩; 씩; ) HANGUL SYLLABLE SSIG +C52A;C52A;110A 1175 11A9;C52A;110A 1175 11A9; # (씪; 씪; 씪; 씪; 씪; ) HANGUL SYLLABLE SSIGG +C52B;C52B;110A 1175 11AA;C52B;110A 1175 11AA; # (씫; 씫; 씫; 씫; 씫; ) HANGUL SYLLABLE SSIGS +C52C;C52C;110A 1175 11AB;C52C;110A 1175 11AB; # (씬; 씬; 씬; 씬; 씬; ) HANGUL SYLLABLE SSIN +C52D;C52D;110A 1175 11AC;C52D;110A 1175 11AC; # (씭; 씭; 씭; 씭; 씭; ) HANGUL SYLLABLE SSINJ +C52E;C52E;110A 1175 11AD;C52E;110A 1175 11AD; # (씮; 씮; 씮; 씮; 씮; ) HANGUL SYLLABLE SSINH +C52F;C52F;110A 1175 11AE;C52F;110A 1175 11AE; # (씯; 씯; 씯; 씯; 씯; ) HANGUL SYLLABLE SSID +C530;C530;110A 1175 11AF;C530;110A 1175 11AF; # (씰; 씰; 씰; 씰; 씰; ) HANGUL SYLLABLE SSIL +C531;C531;110A 1175 11B0;C531;110A 1175 11B0; # (씱; 씱; 씱; 씱; 씱; ) HANGUL SYLLABLE SSILG +C532;C532;110A 1175 11B1;C532;110A 1175 11B1; # (씲; 씲; 씲; 씲; 씲; ) HANGUL SYLLABLE SSILM +C533;C533;110A 1175 11B2;C533;110A 1175 11B2; # (씳; 씳; 씳; 씳; 씳; ) HANGUL SYLLABLE SSILB +C534;C534;110A 1175 11B3;C534;110A 1175 11B3; # (씴; 씴; 씴; 씴; 씴; ) HANGUL SYLLABLE SSILS +C535;C535;110A 1175 11B4;C535;110A 1175 11B4; # (씵; 씵; 씵; 씵; 씵; ) HANGUL SYLLABLE SSILT +C536;C536;110A 1175 11B5;C536;110A 1175 11B5; # (씶; 씶; 씶; 씶; 씶; ) HANGUL SYLLABLE SSILP +C537;C537;110A 1175 11B6;C537;110A 1175 11B6; # (씷; 씷; 씷; 씷; 씷; ) HANGUL SYLLABLE SSILH +C538;C538;110A 1175 11B7;C538;110A 1175 11B7; # (씸; 씸; 씸; 씸; 씸; ) HANGUL SYLLABLE SSIM +C539;C539;110A 1175 11B8;C539;110A 1175 11B8; # (씹; 씹; 씹; 씹; 씹; ) HANGUL SYLLABLE SSIB +C53A;C53A;110A 1175 11B9;C53A;110A 1175 11B9; # (씺; 씺; 씺; 씺; 씺; ) HANGUL SYLLABLE SSIBS +C53B;C53B;110A 1175 11BA;C53B;110A 1175 11BA; # (씻; 씻; 씻; 씻; 씻; ) HANGUL SYLLABLE SSIS +C53C;C53C;110A 1175 11BB;C53C;110A 1175 11BB; # (씼; 씼; 씼; 씼; 씼; ) HANGUL SYLLABLE SSISS +C53D;C53D;110A 1175 11BC;C53D;110A 1175 11BC; # (씽; 씽; 씽; 씽; 씽; ) HANGUL SYLLABLE SSING +C53E;C53E;110A 1175 11BD;C53E;110A 1175 11BD; # (씾; 씾; 씾; 씾; 씾; ) HANGUL SYLLABLE SSIJ +C53F;C53F;110A 1175 11BE;C53F;110A 1175 11BE; # (씿; 씿; 씿; 씿; 씿; ) HANGUL SYLLABLE SSIC +C540;C540;110A 1175 11BF;C540;110A 1175 11BF; # (앀; 앀; 앀; 앀; 앀; ) HANGUL SYLLABLE SSIK +C541;C541;110A 1175 11C0;C541;110A 1175 11C0; # (앁; 앁; 앁; 앁; 앁; ) HANGUL SYLLABLE SSIT +C542;C542;110A 1175 11C1;C542;110A 1175 11C1; # (앂; 앂; 앂; 앂; 앂; ) HANGUL SYLLABLE SSIP +C543;C543;110A 1175 11C2;C543;110A 1175 11C2; # (앃; 앃; 앃; 앃; 앃; ) HANGUL SYLLABLE SSIH +C544;C544;110B 1161;C544;110B 1161; # (아; 아; 아; 아; 아; ) HANGUL SYLLABLE A +C545;C545;110B 1161 11A8;C545;110B 1161 11A8; # (악; 악; 악; 악; 악; ) HANGUL SYLLABLE AG +C546;C546;110B 1161 11A9;C546;110B 1161 11A9; # (앆; 앆; 앆; 앆; 앆; ) HANGUL SYLLABLE AGG +C547;C547;110B 1161 11AA;C547;110B 1161 11AA; # (앇; 앇; 앇; 앇; 앇; ) HANGUL SYLLABLE AGS +C548;C548;110B 1161 11AB;C548;110B 1161 11AB; # (안; 안; 안; 안; 안; ) HANGUL SYLLABLE AN +C549;C549;110B 1161 11AC;C549;110B 1161 11AC; # (앉; 앉; 앉; 앉; 앉; ) HANGUL SYLLABLE ANJ +C54A;C54A;110B 1161 11AD;C54A;110B 1161 11AD; # (않; 않; 않; 않; 않; ) HANGUL SYLLABLE ANH +C54B;C54B;110B 1161 11AE;C54B;110B 1161 11AE; # (앋; 앋; 앋; 앋; 앋; ) HANGUL SYLLABLE AD +C54C;C54C;110B 1161 11AF;C54C;110B 1161 11AF; # (알; 알; 알; 알; 알; ) HANGUL SYLLABLE AL +C54D;C54D;110B 1161 11B0;C54D;110B 1161 11B0; # (앍; 앍; 앍; 앍; 앍; ) HANGUL SYLLABLE ALG +C54E;C54E;110B 1161 11B1;C54E;110B 1161 11B1; # (앎; 앎; 앎; 앎; 앎; ) HANGUL SYLLABLE ALM +C54F;C54F;110B 1161 11B2;C54F;110B 1161 11B2; # (앏; 앏; 앏; 앏; 앏; ) HANGUL SYLLABLE ALB +C550;C550;110B 1161 11B3;C550;110B 1161 11B3; # (앐; 앐; 앐; 앐; 앐; ) HANGUL SYLLABLE ALS +C551;C551;110B 1161 11B4;C551;110B 1161 11B4; # (앑; 앑; 앑; 앑; 앑; ) HANGUL SYLLABLE ALT +C552;C552;110B 1161 11B5;C552;110B 1161 11B5; # (앒; 앒; 앒; 앒; 앒; ) HANGUL SYLLABLE ALP +C553;C553;110B 1161 11B6;C553;110B 1161 11B6; # (앓; 앓; 앓; 앓; 앓; ) HANGUL SYLLABLE ALH +C554;C554;110B 1161 11B7;C554;110B 1161 11B7; # (암; 암; 암; 암; 암; ) HANGUL SYLLABLE AM +C555;C555;110B 1161 11B8;C555;110B 1161 11B8; # (압; 압; 압; 압; 압; ) HANGUL SYLLABLE AB +C556;C556;110B 1161 11B9;C556;110B 1161 11B9; # (앖; 앖; 앖; 앖; 앖; ) HANGUL SYLLABLE ABS +C557;C557;110B 1161 11BA;C557;110B 1161 11BA; # (앗; 앗; 앗; 앗; 앗; ) HANGUL SYLLABLE AS +C558;C558;110B 1161 11BB;C558;110B 1161 11BB; # (았; 았; 았; 았; 았; ) HANGUL SYLLABLE ASS +C559;C559;110B 1161 11BC;C559;110B 1161 11BC; # (앙; 앙; 앙; 앙; 앙; ) HANGUL SYLLABLE ANG +C55A;C55A;110B 1161 11BD;C55A;110B 1161 11BD; # (앚; 앚; 앚; 앚; 앚; ) HANGUL SYLLABLE AJ +C55B;C55B;110B 1161 11BE;C55B;110B 1161 11BE; # (앛; 앛; 앛; 앛; 앛; ) HANGUL SYLLABLE AC +C55C;C55C;110B 1161 11BF;C55C;110B 1161 11BF; # (앜; 앜; 앜; 앜; 앜; ) HANGUL SYLLABLE AK +C55D;C55D;110B 1161 11C0;C55D;110B 1161 11C0; # (앝; 앝; 앝; 앝; 앝; ) HANGUL SYLLABLE AT +C55E;C55E;110B 1161 11C1;C55E;110B 1161 11C1; # (앞; 앞; 앞; 앞; 앞; ) HANGUL SYLLABLE AP +C55F;C55F;110B 1161 11C2;C55F;110B 1161 11C2; # (앟; 앟; 앟; 앟; 앟; ) HANGUL SYLLABLE AH +C560;C560;110B 1162;C560;110B 1162; # (애; 애; 애; 애; 애; ) HANGUL SYLLABLE AE +C561;C561;110B 1162 11A8;C561;110B 1162 11A8; # (액; 액; 액; 액; 액; ) HANGUL SYLLABLE AEG +C562;C562;110B 1162 11A9;C562;110B 1162 11A9; # (앢; 앢; 앢; 앢; 앢; ) HANGUL SYLLABLE AEGG +C563;C563;110B 1162 11AA;C563;110B 1162 11AA; # (앣; 앣; 앣; 앣; 앣; ) HANGUL SYLLABLE AEGS +C564;C564;110B 1162 11AB;C564;110B 1162 11AB; # (앤; 앤; 앤; 앤; 앤; ) HANGUL SYLLABLE AEN +C565;C565;110B 1162 11AC;C565;110B 1162 11AC; # (앥; 앥; 앥; 앥; 앥; ) HANGUL SYLLABLE AENJ +C566;C566;110B 1162 11AD;C566;110B 1162 11AD; # (앦; 앦; 앦; 앦; 앦; ) HANGUL SYLLABLE AENH +C567;C567;110B 1162 11AE;C567;110B 1162 11AE; # (앧; 앧; 앧; 앧; 앧; ) HANGUL SYLLABLE AED +C568;C568;110B 1162 11AF;C568;110B 1162 11AF; # (앨; 앨; 앨; 앨; 앨; ) HANGUL SYLLABLE AEL +C569;C569;110B 1162 11B0;C569;110B 1162 11B0; # (앩; 앩; 앩; 앩; 앩; ) HANGUL SYLLABLE AELG +C56A;C56A;110B 1162 11B1;C56A;110B 1162 11B1; # (앪; 앪; 앪; 앪; 앪; ) HANGUL SYLLABLE AELM +C56B;C56B;110B 1162 11B2;C56B;110B 1162 11B2; # (앫; 앫; 앫; 앫; 앫; ) HANGUL SYLLABLE AELB +C56C;C56C;110B 1162 11B3;C56C;110B 1162 11B3; # (앬; 앬; 앬; 앬; 앬; ) HANGUL SYLLABLE AELS +C56D;C56D;110B 1162 11B4;C56D;110B 1162 11B4; # (앭; 앭; 앭; 앭; 앭; ) HANGUL SYLLABLE AELT +C56E;C56E;110B 1162 11B5;C56E;110B 1162 11B5; # (앮; 앮; 앮; 앮; 앮; ) HANGUL SYLLABLE AELP +C56F;C56F;110B 1162 11B6;C56F;110B 1162 11B6; # (앯; 앯; 앯; 앯; 앯; ) HANGUL SYLLABLE AELH +C570;C570;110B 1162 11B7;C570;110B 1162 11B7; # (앰; 앰; 앰; 앰; 앰; ) HANGUL SYLLABLE AEM +C571;C571;110B 1162 11B8;C571;110B 1162 11B8; # (앱; 앱; 앱; 앱; 앱; ) HANGUL SYLLABLE AEB +C572;C572;110B 1162 11B9;C572;110B 1162 11B9; # (앲; 앲; 앲; 앲; 앲; ) HANGUL SYLLABLE AEBS +C573;C573;110B 1162 11BA;C573;110B 1162 11BA; # (앳; 앳; 앳; 앳; 앳; ) HANGUL SYLLABLE AES +C574;C574;110B 1162 11BB;C574;110B 1162 11BB; # (앴; 앴; 앴; 앴; 앴; ) HANGUL SYLLABLE AESS +C575;C575;110B 1162 11BC;C575;110B 1162 11BC; # (앵; 앵; 앵; 앵; 앵; ) HANGUL SYLLABLE AENG +C576;C576;110B 1162 11BD;C576;110B 1162 11BD; # (앶; 앶; 앶; 앶; 앶; ) HANGUL SYLLABLE AEJ +C577;C577;110B 1162 11BE;C577;110B 1162 11BE; # (앷; 앷; 앷; 앷; 앷; ) HANGUL SYLLABLE AEC +C578;C578;110B 1162 11BF;C578;110B 1162 11BF; # (앸; 앸; 앸; 앸; 앸; ) HANGUL SYLLABLE AEK +C579;C579;110B 1162 11C0;C579;110B 1162 11C0; # (앹; 앹; 앹; 앹; 앹; ) HANGUL SYLLABLE AET +C57A;C57A;110B 1162 11C1;C57A;110B 1162 11C1; # (앺; 앺; 앺; 앺; 앺; ) HANGUL SYLLABLE AEP +C57B;C57B;110B 1162 11C2;C57B;110B 1162 11C2; # (앻; 앻; 앻; 앻; 앻; ) HANGUL SYLLABLE AEH +C57C;C57C;110B 1163;C57C;110B 1163; # (야; 야; 야; 야; 야; ) HANGUL SYLLABLE YA +C57D;C57D;110B 1163 11A8;C57D;110B 1163 11A8; # (약; 약; 약; 약; 약; ) HANGUL SYLLABLE YAG +C57E;C57E;110B 1163 11A9;C57E;110B 1163 11A9; # (앾; 앾; 앾; 앾; 앾; ) HANGUL SYLLABLE YAGG +C57F;C57F;110B 1163 11AA;C57F;110B 1163 11AA; # (앿; 앿; 앿; 앿; 앿; ) HANGUL SYLLABLE YAGS +C580;C580;110B 1163 11AB;C580;110B 1163 11AB; # (얀; 얀; 얀; 얀; 얀; ) HANGUL SYLLABLE YAN +C581;C581;110B 1163 11AC;C581;110B 1163 11AC; # (얁; 얁; 얁; 얁; 얁; ) HANGUL SYLLABLE YANJ +C582;C582;110B 1163 11AD;C582;110B 1163 11AD; # (얂; 얂; 얂; 얂; 얂; ) HANGUL SYLLABLE YANH +C583;C583;110B 1163 11AE;C583;110B 1163 11AE; # (얃; 얃; 얃; 얃; 얃; ) HANGUL SYLLABLE YAD +C584;C584;110B 1163 11AF;C584;110B 1163 11AF; # (얄; 얄; 얄; 얄; 얄; ) HANGUL SYLLABLE YAL +C585;C585;110B 1163 11B0;C585;110B 1163 11B0; # (얅; 얅; 얅; 얅; 얅; ) HANGUL SYLLABLE YALG +C586;C586;110B 1163 11B1;C586;110B 1163 11B1; # (얆; 얆; 얆; 얆; 얆; ) HANGUL SYLLABLE YALM +C587;C587;110B 1163 11B2;C587;110B 1163 11B2; # (얇; 얇; 얇; 얇; 얇; ) HANGUL SYLLABLE YALB +C588;C588;110B 1163 11B3;C588;110B 1163 11B3; # (얈; 얈; 얈; 얈; 얈; ) HANGUL SYLLABLE YALS +C589;C589;110B 1163 11B4;C589;110B 1163 11B4; # (얉; 얉; 얉; 얉; 얉; ) HANGUL SYLLABLE YALT +C58A;C58A;110B 1163 11B5;C58A;110B 1163 11B5; # (얊; 얊; 얊; 얊; 얊; ) HANGUL SYLLABLE YALP +C58B;C58B;110B 1163 11B6;C58B;110B 1163 11B6; # (얋; 얋; 얋; 얋; 얋; ) HANGUL SYLLABLE YALH +C58C;C58C;110B 1163 11B7;C58C;110B 1163 11B7; # (얌; 얌; 얌; 얌; 얌; ) HANGUL SYLLABLE YAM +C58D;C58D;110B 1163 11B8;C58D;110B 1163 11B8; # (얍; 얍; 얍; 얍; 얍; ) HANGUL SYLLABLE YAB +C58E;C58E;110B 1163 11B9;C58E;110B 1163 11B9; # (얎; 얎; 얎; 얎; 얎; ) HANGUL SYLLABLE YABS +C58F;C58F;110B 1163 11BA;C58F;110B 1163 11BA; # (얏; 얏; 얏; 얏; 얏; ) HANGUL SYLLABLE YAS +C590;C590;110B 1163 11BB;C590;110B 1163 11BB; # (얐; 얐; 얐; 얐; 얐; ) HANGUL SYLLABLE YASS +C591;C591;110B 1163 11BC;C591;110B 1163 11BC; # (양; 양; 양; 양; 양; ) HANGUL SYLLABLE YANG +C592;C592;110B 1163 11BD;C592;110B 1163 11BD; # (얒; 얒; 얒; 얒; 얒; ) HANGUL SYLLABLE YAJ +C593;C593;110B 1163 11BE;C593;110B 1163 11BE; # (얓; 얓; 얓; 얓; 얓; ) HANGUL SYLLABLE YAC +C594;C594;110B 1163 11BF;C594;110B 1163 11BF; # (얔; 얔; 얔; 얔; 얔; ) HANGUL SYLLABLE YAK +C595;C595;110B 1163 11C0;C595;110B 1163 11C0; # (얕; 얕; 얕; 얕; 얕; ) HANGUL SYLLABLE YAT +C596;C596;110B 1163 11C1;C596;110B 1163 11C1; # (얖; 얖; 얖; 얖; 얖; ) HANGUL SYLLABLE YAP +C597;C597;110B 1163 11C2;C597;110B 1163 11C2; # (얗; 얗; 얗; 얗; 얗; ) HANGUL SYLLABLE YAH +C598;C598;110B 1164;C598;110B 1164; # (얘; 얘; 얘; 얘; 얘; ) HANGUL SYLLABLE YAE +C599;C599;110B 1164 11A8;C599;110B 1164 11A8; # (얙; 얙; 얙; 얙; 얙; ) HANGUL SYLLABLE YAEG +C59A;C59A;110B 1164 11A9;C59A;110B 1164 11A9; # (얚; 얚; 얚; 얚; 얚; ) HANGUL SYLLABLE YAEGG +C59B;C59B;110B 1164 11AA;C59B;110B 1164 11AA; # (얛; 얛; 얛; 얛; 얛; ) HANGUL SYLLABLE YAEGS +C59C;C59C;110B 1164 11AB;C59C;110B 1164 11AB; # (얜; 얜; 얜; 얜; 얜; ) HANGUL SYLLABLE YAEN +C59D;C59D;110B 1164 11AC;C59D;110B 1164 11AC; # (얝; 얝; 얝; 얝; 얝; ) HANGUL SYLLABLE YAENJ +C59E;C59E;110B 1164 11AD;C59E;110B 1164 11AD; # (얞; 얞; 얞; 얞; 얞; ) HANGUL SYLLABLE YAENH +C59F;C59F;110B 1164 11AE;C59F;110B 1164 11AE; # (얟; 얟; 얟; 얟; 얟; ) HANGUL SYLLABLE YAED +C5A0;C5A0;110B 1164 11AF;C5A0;110B 1164 11AF; # (얠; 얠; 얠; 얠; 얠; ) HANGUL SYLLABLE YAEL +C5A1;C5A1;110B 1164 11B0;C5A1;110B 1164 11B0; # (얡; 얡; 얡; 얡; 얡; ) HANGUL SYLLABLE YAELG +C5A2;C5A2;110B 1164 11B1;C5A2;110B 1164 11B1; # (얢; 얢; 얢; 얢; 얢; ) HANGUL SYLLABLE YAELM +C5A3;C5A3;110B 1164 11B2;C5A3;110B 1164 11B2; # (얣; 얣; 얣; 얣; 얣; ) HANGUL SYLLABLE YAELB +C5A4;C5A4;110B 1164 11B3;C5A4;110B 1164 11B3; # (얤; 얤; 얤; 얤; 얤; ) HANGUL SYLLABLE YAELS +C5A5;C5A5;110B 1164 11B4;C5A5;110B 1164 11B4; # (얥; 얥; 얥; 얥; 얥; ) HANGUL SYLLABLE YAELT +C5A6;C5A6;110B 1164 11B5;C5A6;110B 1164 11B5; # (얦; 얦; 얦; 얦; 얦; ) HANGUL SYLLABLE YAELP +C5A7;C5A7;110B 1164 11B6;C5A7;110B 1164 11B6; # (얧; 얧; 얧; 얧; 얧; ) HANGUL SYLLABLE YAELH +C5A8;C5A8;110B 1164 11B7;C5A8;110B 1164 11B7; # (얨; 얨; 얨; 얨; 얨; ) HANGUL SYLLABLE YAEM +C5A9;C5A9;110B 1164 11B8;C5A9;110B 1164 11B8; # (얩; 얩; 얩; 얩; 얩; ) HANGUL SYLLABLE YAEB +C5AA;C5AA;110B 1164 11B9;C5AA;110B 1164 11B9; # (얪; 얪; 얪; 얪; 얪; ) HANGUL SYLLABLE YAEBS +C5AB;C5AB;110B 1164 11BA;C5AB;110B 1164 11BA; # (얫; 얫; 얫; 얫; 얫; ) HANGUL SYLLABLE YAES +C5AC;C5AC;110B 1164 11BB;C5AC;110B 1164 11BB; # (얬; 얬; 얬; 얬; 얬; ) HANGUL SYLLABLE YAESS +C5AD;C5AD;110B 1164 11BC;C5AD;110B 1164 11BC; # (얭; 얭; 얭; 얭; 얭; ) HANGUL SYLLABLE YAENG +C5AE;C5AE;110B 1164 11BD;C5AE;110B 1164 11BD; # (얮; 얮; 얮; 얮; 얮; ) HANGUL SYLLABLE YAEJ +C5AF;C5AF;110B 1164 11BE;C5AF;110B 1164 11BE; # (얯; 얯; 얯; 얯; 얯; ) HANGUL SYLLABLE YAEC +C5B0;C5B0;110B 1164 11BF;C5B0;110B 1164 11BF; # (얰; 얰; 얰; 얰; 얰; ) HANGUL SYLLABLE YAEK +C5B1;C5B1;110B 1164 11C0;C5B1;110B 1164 11C0; # (얱; 얱; 얱; 얱; 얱; ) HANGUL SYLLABLE YAET +C5B2;C5B2;110B 1164 11C1;C5B2;110B 1164 11C1; # (얲; 얲; 얲; 얲; 얲; ) HANGUL SYLLABLE YAEP +C5B3;C5B3;110B 1164 11C2;C5B3;110B 1164 11C2; # (얳; 얳; 얳; 얳; 얳; ) HANGUL SYLLABLE YAEH +C5B4;C5B4;110B 1165;C5B4;110B 1165; # (어; 어; 어; 어; 어; ) HANGUL SYLLABLE EO +C5B5;C5B5;110B 1165 11A8;C5B5;110B 1165 11A8; # (억; 억; 억; 억; 억; ) HANGUL SYLLABLE EOG +C5B6;C5B6;110B 1165 11A9;C5B6;110B 1165 11A9; # (얶; 얶; 얶; 얶; 얶; ) HANGUL SYLLABLE EOGG +C5B7;C5B7;110B 1165 11AA;C5B7;110B 1165 11AA; # (얷; 얷; 얷; 얷; 얷; ) HANGUL SYLLABLE EOGS +C5B8;C5B8;110B 1165 11AB;C5B8;110B 1165 11AB; # (언; 언; 언; 언; 언; ) HANGUL SYLLABLE EON +C5B9;C5B9;110B 1165 11AC;C5B9;110B 1165 11AC; # (얹; 얹; 얹; 얹; 얹; ) HANGUL SYLLABLE EONJ +C5BA;C5BA;110B 1165 11AD;C5BA;110B 1165 11AD; # (얺; 얺; 얺; 얺; 얺; ) HANGUL SYLLABLE EONH +C5BB;C5BB;110B 1165 11AE;C5BB;110B 1165 11AE; # (얻; 얻; 얻; 얻; 얻; ) HANGUL SYLLABLE EOD +C5BC;C5BC;110B 1165 11AF;C5BC;110B 1165 11AF; # (얼; 얼; 얼; 얼; 얼; ) HANGUL SYLLABLE EOL +C5BD;C5BD;110B 1165 11B0;C5BD;110B 1165 11B0; # (얽; 얽; 얽; 얽; 얽; ) HANGUL SYLLABLE EOLG +C5BE;C5BE;110B 1165 11B1;C5BE;110B 1165 11B1; # (얾; 얾; 얾; 얾; 얾; ) HANGUL SYLLABLE EOLM +C5BF;C5BF;110B 1165 11B2;C5BF;110B 1165 11B2; # (얿; 얿; 얿; 얿; 얿; ) HANGUL SYLLABLE EOLB +C5C0;C5C0;110B 1165 11B3;C5C0;110B 1165 11B3; # (엀; 엀; 엀; 엀; 엀; ) HANGUL SYLLABLE EOLS +C5C1;C5C1;110B 1165 11B4;C5C1;110B 1165 11B4; # (엁; 엁; 엁; 엁; 엁; ) HANGUL SYLLABLE EOLT +C5C2;C5C2;110B 1165 11B5;C5C2;110B 1165 11B5; # (엂; 엂; 엂; 엂; 엂; ) HANGUL SYLLABLE EOLP +C5C3;C5C3;110B 1165 11B6;C5C3;110B 1165 11B6; # (엃; 엃; 엃; 엃; 엃; ) HANGUL SYLLABLE EOLH +C5C4;C5C4;110B 1165 11B7;C5C4;110B 1165 11B7; # (엄; 엄; 엄; 엄; 엄; ) HANGUL SYLLABLE EOM +C5C5;C5C5;110B 1165 11B8;C5C5;110B 1165 11B8; # (업; 업; 업; 업; 업; ) HANGUL SYLLABLE EOB +C5C6;C5C6;110B 1165 11B9;C5C6;110B 1165 11B9; # (없; 없; 없; 없; 없; ) HANGUL SYLLABLE EOBS +C5C7;C5C7;110B 1165 11BA;C5C7;110B 1165 11BA; # (엇; 엇; 엇; 엇; 엇; ) HANGUL SYLLABLE EOS +C5C8;C5C8;110B 1165 11BB;C5C8;110B 1165 11BB; # (었; 었; 었; 었; 었; ) HANGUL SYLLABLE EOSS +C5C9;C5C9;110B 1165 11BC;C5C9;110B 1165 11BC; # (엉; 엉; 엉; 엉; 엉; ) HANGUL SYLLABLE EONG +C5CA;C5CA;110B 1165 11BD;C5CA;110B 1165 11BD; # (엊; 엊; 엊; 엊; 엊; ) HANGUL SYLLABLE EOJ +C5CB;C5CB;110B 1165 11BE;C5CB;110B 1165 11BE; # (엋; 엋; 엋; 엋; 엋; ) HANGUL SYLLABLE EOC +C5CC;C5CC;110B 1165 11BF;C5CC;110B 1165 11BF; # (엌; 엌; 엌; 엌; 엌; ) HANGUL SYLLABLE EOK +C5CD;C5CD;110B 1165 11C0;C5CD;110B 1165 11C0; # (엍; 엍; 엍; 엍; 엍; ) HANGUL SYLLABLE EOT +C5CE;C5CE;110B 1165 11C1;C5CE;110B 1165 11C1; # (엎; 엎; 엎; 엎; 엎; ) HANGUL SYLLABLE EOP +C5CF;C5CF;110B 1165 11C2;C5CF;110B 1165 11C2; # (엏; 엏; 엏; 엏; 엏; ) HANGUL SYLLABLE EOH +C5D0;C5D0;110B 1166;C5D0;110B 1166; # (에; 에; 에; 에; 에; ) HANGUL SYLLABLE E +C5D1;C5D1;110B 1166 11A8;C5D1;110B 1166 11A8; # (엑; 엑; 엑; 엑; 엑; ) HANGUL SYLLABLE EG +C5D2;C5D2;110B 1166 11A9;C5D2;110B 1166 11A9; # (엒; 엒; 엒; 엒; 엒; ) HANGUL SYLLABLE EGG +C5D3;C5D3;110B 1166 11AA;C5D3;110B 1166 11AA; # (엓; 엓; 엓; 엓; 엓; ) HANGUL SYLLABLE EGS +C5D4;C5D4;110B 1166 11AB;C5D4;110B 1166 11AB; # (엔; 엔; 엔; 엔; 엔; ) HANGUL SYLLABLE EN +C5D5;C5D5;110B 1166 11AC;C5D5;110B 1166 11AC; # (엕; 엕; 엕; 엕; 엕; ) HANGUL SYLLABLE ENJ +C5D6;C5D6;110B 1166 11AD;C5D6;110B 1166 11AD; # (엖; 엖; 엖; 엖; 엖; ) HANGUL SYLLABLE ENH +C5D7;C5D7;110B 1166 11AE;C5D7;110B 1166 11AE; # (엗; 엗; 엗; 엗; 엗; ) HANGUL SYLLABLE ED +C5D8;C5D8;110B 1166 11AF;C5D8;110B 1166 11AF; # (엘; 엘; 엘; 엘; 엘; ) HANGUL SYLLABLE EL +C5D9;C5D9;110B 1166 11B0;C5D9;110B 1166 11B0; # (엙; 엙; 엙; 엙; 엙; ) HANGUL SYLLABLE ELG +C5DA;C5DA;110B 1166 11B1;C5DA;110B 1166 11B1; # (엚; 엚; 엚; 엚; 엚; ) HANGUL SYLLABLE ELM +C5DB;C5DB;110B 1166 11B2;C5DB;110B 1166 11B2; # (엛; 엛; 엛; 엛; 엛; ) HANGUL SYLLABLE ELB +C5DC;C5DC;110B 1166 11B3;C5DC;110B 1166 11B3; # (엜; 엜; 엜; 엜; 엜; ) HANGUL SYLLABLE ELS +C5DD;C5DD;110B 1166 11B4;C5DD;110B 1166 11B4; # (엝; 엝; 엝; 엝; 엝; ) HANGUL SYLLABLE ELT +C5DE;C5DE;110B 1166 11B5;C5DE;110B 1166 11B5; # (엞; 엞; 엞; 엞; 엞; ) HANGUL SYLLABLE ELP +C5DF;C5DF;110B 1166 11B6;C5DF;110B 1166 11B6; # (엟; 엟; 엟; 엟; 엟; ) HANGUL SYLLABLE ELH +C5E0;C5E0;110B 1166 11B7;C5E0;110B 1166 11B7; # (엠; 엠; 엠; 엠; 엠; ) HANGUL SYLLABLE EM +C5E1;C5E1;110B 1166 11B8;C5E1;110B 1166 11B8; # (엡; 엡; 엡; 엡; 엡; ) HANGUL SYLLABLE EB +C5E2;C5E2;110B 1166 11B9;C5E2;110B 1166 11B9; # (엢; 엢; 엢; 엢; 엢; ) HANGUL SYLLABLE EBS +C5E3;C5E3;110B 1166 11BA;C5E3;110B 1166 11BA; # (엣; 엣; 엣; 엣; 엣; ) HANGUL SYLLABLE ES +C5E4;C5E4;110B 1166 11BB;C5E4;110B 1166 11BB; # (엤; 엤; 엤; 엤; 엤; ) HANGUL SYLLABLE ESS +C5E5;C5E5;110B 1166 11BC;C5E5;110B 1166 11BC; # (엥; 엥; 엥; 엥; 엥; ) HANGUL SYLLABLE ENG +C5E6;C5E6;110B 1166 11BD;C5E6;110B 1166 11BD; # (엦; 엦; 엦; 엦; 엦; ) HANGUL SYLLABLE EJ +C5E7;C5E7;110B 1166 11BE;C5E7;110B 1166 11BE; # (엧; 엧; 엧; 엧; 엧; ) HANGUL SYLLABLE EC +C5E8;C5E8;110B 1166 11BF;C5E8;110B 1166 11BF; # (엨; 엨; 엨; 엨; 엨; ) HANGUL SYLLABLE EK +C5E9;C5E9;110B 1166 11C0;C5E9;110B 1166 11C0; # (엩; 엩; 엩; 엩; 엩; ) HANGUL SYLLABLE ET +C5EA;C5EA;110B 1166 11C1;C5EA;110B 1166 11C1; # (엪; 엪; 엪; 엪; 엪; ) HANGUL SYLLABLE EP +C5EB;C5EB;110B 1166 11C2;C5EB;110B 1166 11C2; # (엫; 엫; 엫; 엫; 엫; ) HANGUL SYLLABLE EH +C5EC;C5EC;110B 1167;C5EC;110B 1167; # (여; 여; 여; 여; 여; ) HANGUL SYLLABLE YEO +C5ED;C5ED;110B 1167 11A8;C5ED;110B 1167 11A8; # (역; 역; 역; 역; 역; ) HANGUL SYLLABLE YEOG +C5EE;C5EE;110B 1167 11A9;C5EE;110B 1167 11A9; # (엮; 엮; 엮; 엮; 엮; ) HANGUL SYLLABLE YEOGG +C5EF;C5EF;110B 1167 11AA;C5EF;110B 1167 11AA; # (엯; 엯; 엯; 엯; 엯; ) HANGUL SYLLABLE YEOGS +C5F0;C5F0;110B 1167 11AB;C5F0;110B 1167 11AB; # (연; 연; 연; 연; 연; ) HANGUL SYLLABLE YEON +C5F1;C5F1;110B 1167 11AC;C5F1;110B 1167 11AC; # (엱; 엱; 엱; 엱; 엱; ) HANGUL SYLLABLE YEONJ +C5F2;C5F2;110B 1167 11AD;C5F2;110B 1167 11AD; # (엲; 엲; 엲; 엲; 엲; ) HANGUL SYLLABLE YEONH +C5F3;C5F3;110B 1167 11AE;C5F3;110B 1167 11AE; # (엳; 엳; 엳; 엳; 엳; ) HANGUL SYLLABLE YEOD +C5F4;C5F4;110B 1167 11AF;C5F4;110B 1167 11AF; # (열; 열; 열; 열; 열; ) HANGUL SYLLABLE YEOL +C5F5;C5F5;110B 1167 11B0;C5F5;110B 1167 11B0; # (엵; 엵; 엵; 엵; 엵; ) HANGUL SYLLABLE YEOLG +C5F6;C5F6;110B 1167 11B1;C5F6;110B 1167 11B1; # (엶; 엶; 엶; 엶; 엶; ) HANGUL SYLLABLE YEOLM +C5F7;C5F7;110B 1167 11B2;C5F7;110B 1167 11B2; # (엷; 엷; 엷; 엷; 엷; ) HANGUL SYLLABLE YEOLB +C5F8;C5F8;110B 1167 11B3;C5F8;110B 1167 11B3; # (엸; 엸; 엸; 엸; 엸; ) HANGUL SYLLABLE YEOLS +C5F9;C5F9;110B 1167 11B4;C5F9;110B 1167 11B4; # (엹; 엹; 엹; 엹; 엹; ) HANGUL SYLLABLE YEOLT +C5FA;C5FA;110B 1167 11B5;C5FA;110B 1167 11B5; # (엺; 엺; 엺; 엺; 엺; ) HANGUL SYLLABLE YEOLP +C5FB;C5FB;110B 1167 11B6;C5FB;110B 1167 11B6; # (엻; 엻; 엻; 엻; 엻; ) HANGUL SYLLABLE YEOLH +C5FC;C5FC;110B 1167 11B7;C5FC;110B 1167 11B7; # (염; 염; 염; 염; 염; ) HANGUL SYLLABLE YEOM +C5FD;C5FD;110B 1167 11B8;C5FD;110B 1167 11B8; # (엽; 엽; 엽; 엽; 엽; ) HANGUL SYLLABLE YEOB +C5FE;C5FE;110B 1167 11B9;C5FE;110B 1167 11B9; # (엾; 엾; 엾; 엾; 엾; ) HANGUL SYLLABLE YEOBS +C5FF;C5FF;110B 1167 11BA;C5FF;110B 1167 11BA; # (엿; 엿; 엿; 엿; 엿; ) HANGUL SYLLABLE YEOS +C600;C600;110B 1167 11BB;C600;110B 1167 11BB; # (였; 였; 였; 였; 였; ) HANGUL SYLLABLE YEOSS +C601;C601;110B 1167 11BC;C601;110B 1167 11BC; # (영; 영; 영; 영; 영; ) HANGUL SYLLABLE YEONG +C602;C602;110B 1167 11BD;C602;110B 1167 11BD; # (옂; 옂; 옂; 옂; 옂; ) HANGUL SYLLABLE YEOJ +C603;C603;110B 1167 11BE;C603;110B 1167 11BE; # (옃; 옃; 옃; 옃; 옃; ) HANGUL SYLLABLE YEOC +C604;C604;110B 1167 11BF;C604;110B 1167 11BF; # (옄; 옄; 옄; 옄; 옄; ) HANGUL SYLLABLE YEOK +C605;C605;110B 1167 11C0;C605;110B 1167 11C0; # (옅; 옅; 옅; 옅; 옅; ) HANGUL SYLLABLE YEOT +C606;C606;110B 1167 11C1;C606;110B 1167 11C1; # (옆; 옆; 옆; 옆; 옆; ) HANGUL SYLLABLE YEOP +C607;C607;110B 1167 11C2;C607;110B 1167 11C2; # (옇; 옇; 옇; 옇; 옇; ) HANGUL SYLLABLE YEOH +C608;C608;110B 1168;C608;110B 1168; # (예; 예; 예; 예; 예; ) HANGUL SYLLABLE YE +C609;C609;110B 1168 11A8;C609;110B 1168 11A8; # (옉; 옉; 옉; 옉; 옉; ) HANGUL SYLLABLE YEG +C60A;C60A;110B 1168 11A9;C60A;110B 1168 11A9; # (옊; 옊; 옊; 옊; 옊; ) HANGUL SYLLABLE YEGG +C60B;C60B;110B 1168 11AA;C60B;110B 1168 11AA; # (옋; 옋; 옋; 옋; 옋; ) HANGUL SYLLABLE YEGS +C60C;C60C;110B 1168 11AB;C60C;110B 1168 11AB; # (옌; 옌; 옌; 옌; 옌; ) HANGUL SYLLABLE YEN +C60D;C60D;110B 1168 11AC;C60D;110B 1168 11AC; # (옍; 옍; 옍; 옍; 옍; ) HANGUL SYLLABLE YENJ +C60E;C60E;110B 1168 11AD;C60E;110B 1168 11AD; # (옎; 옎; 옎; 옎; 옎; ) HANGUL SYLLABLE YENH +C60F;C60F;110B 1168 11AE;C60F;110B 1168 11AE; # (옏; 옏; 옏; 옏; 옏; ) HANGUL SYLLABLE YED +C610;C610;110B 1168 11AF;C610;110B 1168 11AF; # (옐; 옐; 옐; 옐; 옐; ) HANGUL SYLLABLE YEL +C611;C611;110B 1168 11B0;C611;110B 1168 11B0; # (옑; 옑; 옑; 옑; 옑; ) HANGUL SYLLABLE YELG +C612;C612;110B 1168 11B1;C612;110B 1168 11B1; # (옒; 옒; 옒; 옒; 옒; ) HANGUL SYLLABLE YELM +C613;C613;110B 1168 11B2;C613;110B 1168 11B2; # (옓; 옓; 옓; 옓; 옓; ) HANGUL SYLLABLE YELB +C614;C614;110B 1168 11B3;C614;110B 1168 11B3; # (옔; 옔; 옔; 옔; 옔; ) HANGUL SYLLABLE YELS +C615;C615;110B 1168 11B4;C615;110B 1168 11B4; # (옕; 옕; 옕; 옕; 옕; ) HANGUL SYLLABLE YELT +C616;C616;110B 1168 11B5;C616;110B 1168 11B5; # (옖; 옖; 옖; 옖; 옖; ) HANGUL SYLLABLE YELP +C617;C617;110B 1168 11B6;C617;110B 1168 11B6; # (옗; 옗; 옗; 옗; 옗; ) HANGUL SYLLABLE YELH +C618;C618;110B 1168 11B7;C618;110B 1168 11B7; # (옘; 옘; 옘; 옘; 옘; ) HANGUL SYLLABLE YEM +C619;C619;110B 1168 11B8;C619;110B 1168 11B8; # (옙; 옙; 옙; 옙; 옙; ) HANGUL SYLLABLE YEB +C61A;C61A;110B 1168 11B9;C61A;110B 1168 11B9; # (옚; 옚; 옚; 옚; 옚; ) HANGUL SYLLABLE YEBS +C61B;C61B;110B 1168 11BA;C61B;110B 1168 11BA; # (옛; 옛; 옛; 옛; 옛; ) HANGUL SYLLABLE YES +C61C;C61C;110B 1168 11BB;C61C;110B 1168 11BB; # (옜; 옜; 옜; 옜; 옜; ) HANGUL SYLLABLE YESS +C61D;C61D;110B 1168 11BC;C61D;110B 1168 11BC; # (옝; 옝; 옝; 옝; 옝; ) HANGUL SYLLABLE YENG +C61E;C61E;110B 1168 11BD;C61E;110B 1168 11BD; # (옞; 옞; 옞; 옞; 옞; ) HANGUL SYLLABLE YEJ +C61F;C61F;110B 1168 11BE;C61F;110B 1168 11BE; # (옟; 옟; 옟; 옟; 옟; ) HANGUL SYLLABLE YEC +C620;C620;110B 1168 11BF;C620;110B 1168 11BF; # (옠; 옠; 옠; 옠; 옠; ) HANGUL SYLLABLE YEK +C621;C621;110B 1168 11C0;C621;110B 1168 11C0; # (옡; 옡; 옡; 옡; 옡; ) HANGUL SYLLABLE YET +C622;C622;110B 1168 11C1;C622;110B 1168 11C1; # (옢; 옢; 옢; 옢; 옢; ) HANGUL SYLLABLE YEP +C623;C623;110B 1168 11C2;C623;110B 1168 11C2; # (옣; 옣; 옣; 옣; 옣; ) HANGUL SYLLABLE YEH +C624;C624;110B 1169;C624;110B 1169; # (오; 오; 오; 오; 오; ) HANGUL SYLLABLE O +C625;C625;110B 1169 11A8;C625;110B 1169 11A8; # (옥; 옥; 옥; 옥; 옥; ) HANGUL SYLLABLE OG +C626;C626;110B 1169 11A9;C626;110B 1169 11A9; # (옦; 옦; 옦; 옦; 옦; ) HANGUL SYLLABLE OGG +C627;C627;110B 1169 11AA;C627;110B 1169 11AA; # (옧; 옧; 옧; 옧; 옧; ) HANGUL SYLLABLE OGS +C628;C628;110B 1169 11AB;C628;110B 1169 11AB; # (온; 온; 온; 온; 온; ) HANGUL SYLLABLE ON +C629;C629;110B 1169 11AC;C629;110B 1169 11AC; # (옩; 옩; 옩; 옩; 옩; ) HANGUL SYLLABLE ONJ +C62A;C62A;110B 1169 11AD;C62A;110B 1169 11AD; # (옪; 옪; 옪; 옪; 옪; ) HANGUL SYLLABLE ONH +C62B;C62B;110B 1169 11AE;C62B;110B 1169 11AE; # (옫; 옫; 옫; 옫; 옫; ) HANGUL SYLLABLE OD +C62C;C62C;110B 1169 11AF;C62C;110B 1169 11AF; # (올; 올; 올; 올; 올; ) HANGUL SYLLABLE OL +C62D;C62D;110B 1169 11B0;C62D;110B 1169 11B0; # (옭; 옭; 옭; 옭; 옭; ) HANGUL SYLLABLE OLG +C62E;C62E;110B 1169 11B1;C62E;110B 1169 11B1; # (옮; 옮; 옮; 옮; 옮; ) HANGUL SYLLABLE OLM +C62F;C62F;110B 1169 11B2;C62F;110B 1169 11B2; # (옯; 옯; 옯; 옯; 옯; ) HANGUL SYLLABLE OLB +C630;C630;110B 1169 11B3;C630;110B 1169 11B3; # (옰; 옰; 옰; 옰; 옰; ) HANGUL SYLLABLE OLS +C631;C631;110B 1169 11B4;C631;110B 1169 11B4; # (옱; 옱; 옱; 옱; 옱; ) HANGUL SYLLABLE OLT +C632;C632;110B 1169 11B5;C632;110B 1169 11B5; # (옲; 옲; 옲; 옲; 옲; ) HANGUL SYLLABLE OLP +C633;C633;110B 1169 11B6;C633;110B 1169 11B6; # (옳; 옳; 옳; 옳; 옳; ) HANGUL SYLLABLE OLH +C634;C634;110B 1169 11B7;C634;110B 1169 11B7; # (옴; 옴; 옴; 옴; 옴; ) HANGUL SYLLABLE OM +C635;C635;110B 1169 11B8;C635;110B 1169 11B8; # (옵; 옵; 옵; 옵; 옵; ) HANGUL SYLLABLE OB +C636;C636;110B 1169 11B9;C636;110B 1169 11B9; # (옶; 옶; 옶; 옶; 옶; ) HANGUL SYLLABLE OBS +C637;C637;110B 1169 11BA;C637;110B 1169 11BA; # (옷; 옷; 옷; 옷; 옷; ) HANGUL SYLLABLE OS +C638;C638;110B 1169 11BB;C638;110B 1169 11BB; # (옸; 옸; 옸; 옸; 옸; ) HANGUL SYLLABLE OSS +C639;C639;110B 1169 11BC;C639;110B 1169 11BC; # (옹; 옹; 옹; 옹; 옹; ) HANGUL SYLLABLE ONG +C63A;C63A;110B 1169 11BD;C63A;110B 1169 11BD; # (옺; 옺; 옺; 옺; 옺; ) HANGUL SYLLABLE OJ +C63B;C63B;110B 1169 11BE;C63B;110B 1169 11BE; # (옻; 옻; 옻; 옻; 옻; ) HANGUL SYLLABLE OC +C63C;C63C;110B 1169 11BF;C63C;110B 1169 11BF; # (옼; 옼; 옼; 옼; 옼; ) HANGUL SYLLABLE OK +C63D;C63D;110B 1169 11C0;C63D;110B 1169 11C0; # (옽; 옽; 옽; 옽; 옽; ) HANGUL SYLLABLE OT +C63E;C63E;110B 1169 11C1;C63E;110B 1169 11C1; # (옾; 옾; 옾; 옾; 옾; ) HANGUL SYLLABLE OP +C63F;C63F;110B 1169 11C2;C63F;110B 1169 11C2; # (옿; 옿; 옿; 옿; 옿; ) HANGUL SYLLABLE OH +C640;C640;110B 116A;C640;110B 116A; # (와; 와; 와; 와; 와; ) HANGUL SYLLABLE WA +C641;C641;110B 116A 11A8;C641;110B 116A 11A8; # (왁; 왁; 왁; 왁; 왁; ) HANGUL SYLLABLE WAG +C642;C642;110B 116A 11A9;C642;110B 116A 11A9; # (왂; 왂; 왂; 왂; 왂; ) HANGUL SYLLABLE WAGG +C643;C643;110B 116A 11AA;C643;110B 116A 11AA; # (왃; 왃; 왃; 왃; 왃; ) HANGUL SYLLABLE WAGS +C644;C644;110B 116A 11AB;C644;110B 116A 11AB; # (완; 완; 완; 완; 완; ) HANGUL SYLLABLE WAN +C645;C645;110B 116A 11AC;C645;110B 116A 11AC; # (왅; 왅; 왅; 왅; 왅; ) HANGUL SYLLABLE WANJ +C646;C646;110B 116A 11AD;C646;110B 116A 11AD; # (왆; 왆; 왆; 왆; 왆; ) HANGUL SYLLABLE WANH +C647;C647;110B 116A 11AE;C647;110B 116A 11AE; # (왇; 왇; 왇; 왇; 왇; ) HANGUL SYLLABLE WAD +C648;C648;110B 116A 11AF;C648;110B 116A 11AF; # (왈; 왈; 왈; 왈; 왈; ) HANGUL SYLLABLE WAL +C649;C649;110B 116A 11B0;C649;110B 116A 11B0; # (왉; 왉; 왉; 왉; 왉; ) HANGUL SYLLABLE WALG +C64A;C64A;110B 116A 11B1;C64A;110B 116A 11B1; # (왊; 왊; 왊; 왊; 왊; ) HANGUL SYLLABLE WALM +C64B;C64B;110B 116A 11B2;C64B;110B 116A 11B2; # (왋; 왋; 왋; 왋; 왋; ) HANGUL SYLLABLE WALB +C64C;C64C;110B 116A 11B3;C64C;110B 116A 11B3; # (왌; 왌; 왌; 왌; 왌; ) HANGUL SYLLABLE WALS +C64D;C64D;110B 116A 11B4;C64D;110B 116A 11B4; # (왍; 왍; 왍; 왍; 왍; ) HANGUL SYLLABLE WALT +C64E;C64E;110B 116A 11B5;C64E;110B 116A 11B5; # (왎; 왎; 왎; 왎; 왎; ) HANGUL SYLLABLE WALP +C64F;C64F;110B 116A 11B6;C64F;110B 116A 11B6; # (왏; 왏; 왏; 왏; 왏; ) HANGUL SYLLABLE WALH +C650;C650;110B 116A 11B7;C650;110B 116A 11B7; # (왐; 왐; 왐; 왐; 왐; ) HANGUL SYLLABLE WAM +C651;C651;110B 116A 11B8;C651;110B 116A 11B8; # (왑; 왑; 왑; 왑; 왑; ) HANGUL SYLLABLE WAB +C652;C652;110B 116A 11B9;C652;110B 116A 11B9; # (왒; 왒; 왒; 왒; 왒; ) HANGUL SYLLABLE WABS +C653;C653;110B 116A 11BA;C653;110B 116A 11BA; # (왓; 왓; 왓; 왓; 왓; ) HANGUL SYLLABLE WAS +C654;C654;110B 116A 11BB;C654;110B 116A 11BB; # (왔; 왔; 왔; 왔; 왔; ) HANGUL SYLLABLE WASS +C655;C655;110B 116A 11BC;C655;110B 116A 11BC; # (왕; 왕; 왕; 왕; 왕; ) HANGUL SYLLABLE WANG +C656;C656;110B 116A 11BD;C656;110B 116A 11BD; # (왖; 왖; 왖; 왖; 왖; ) HANGUL SYLLABLE WAJ +C657;C657;110B 116A 11BE;C657;110B 116A 11BE; # (왗; 왗; 왗; 왗; 왗; ) HANGUL SYLLABLE WAC +C658;C658;110B 116A 11BF;C658;110B 116A 11BF; # (왘; 왘; 왘; 왘; 왘; ) HANGUL SYLLABLE WAK +C659;C659;110B 116A 11C0;C659;110B 116A 11C0; # (왙; 왙; 왙; 왙; 왙; ) HANGUL SYLLABLE WAT +C65A;C65A;110B 116A 11C1;C65A;110B 116A 11C1; # (왚; 왚; 왚; 왚; 왚; ) HANGUL SYLLABLE WAP +C65B;C65B;110B 116A 11C2;C65B;110B 116A 11C2; # (왛; 왛; 왛; 왛; 왛; ) HANGUL SYLLABLE WAH +C65C;C65C;110B 116B;C65C;110B 116B; # (왜; 왜; 왜; 왜; 왜; ) HANGUL SYLLABLE WAE +C65D;C65D;110B 116B 11A8;C65D;110B 116B 11A8; # (왝; 왝; 왝; 왝; 왝; ) HANGUL SYLLABLE WAEG +C65E;C65E;110B 116B 11A9;C65E;110B 116B 11A9; # (왞; 왞; 왞; 왞; 왞; ) HANGUL SYLLABLE WAEGG +C65F;C65F;110B 116B 11AA;C65F;110B 116B 11AA; # (왟; 왟; 왟; 왟; 왟; ) HANGUL SYLLABLE WAEGS +C660;C660;110B 116B 11AB;C660;110B 116B 11AB; # (왠; 왠; 왠; 왠; 왠; ) HANGUL SYLLABLE WAEN +C661;C661;110B 116B 11AC;C661;110B 116B 11AC; # (왡; 왡; 왡; 왡; 왡; ) HANGUL SYLLABLE WAENJ +C662;C662;110B 116B 11AD;C662;110B 116B 11AD; # (왢; 왢; 왢; 왢; 왢; ) HANGUL SYLLABLE WAENH +C663;C663;110B 116B 11AE;C663;110B 116B 11AE; # (왣; 왣; 왣; 왣; 왣; ) HANGUL SYLLABLE WAED +C664;C664;110B 116B 11AF;C664;110B 116B 11AF; # (왤; 왤; 왤; 왤; 왤; ) HANGUL SYLLABLE WAEL +C665;C665;110B 116B 11B0;C665;110B 116B 11B0; # (왥; 왥; 왥; 왥; 왥; ) HANGUL SYLLABLE WAELG +C666;C666;110B 116B 11B1;C666;110B 116B 11B1; # (왦; 왦; 왦; 왦; 왦; ) HANGUL SYLLABLE WAELM +C667;C667;110B 116B 11B2;C667;110B 116B 11B2; # (왧; 왧; 왧; 왧; 왧; ) HANGUL SYLLABLE WAELB +C668;C668;110B 116B 11B3;C668;110B 116B 11B3; # (왨; 왨; 왨; 왨; 왨; ) HANGUL SYLLABLE WAELS +C669;C669;110B 116B 11B4;C669;110B 116B 11B4; # (왩; 왩; 왩; 왩; 왩; ) HANGUL SYLLABLE WAELT +C66A;C66A;110B 116B 11B5;C66A;110B 116B 11B5; # (왪; 왪; 왪; 왪; 왪; ) HANGUL SYLLABLE WAELP +C66B;C66B;110B 116B 11B6;C66B;110B 116B 11B6; # (왫; 왫; 왫; 왫; 왫; ) HANGUL SYLLABLE WAELH +C66C;C66C;110B 116B 11B7;C66C;110B 116B 11B7; # (왬; 왬; 왬; 왬; 왬; ) HANGUL SYLLABLE WAEM +C66D;C66D;110B 116B 11B8;C66D;110B 116B 11B8; # (왭; 왭; 왭; 왭; 왭; ) HANGUL SYLLABLE WAEB +C66E;C66E;110B 116B 11B9;C66E;110B 116B 11B9; # (왮; 왮; 왮; 왮; 왮; ) HANGUL SYLLABLE WAEBS +C66F;C66F;110B 116B 11BA;C66F;110B 116B 11BA; # (왯; 왯; 왯; 왯; 왯; ) HANGUL SYLLABLE WAES +C670;C670;110B 116B 11BB;C670;110B 116B 11BB; # (왰; 왰; 왰; 왰; 왰; ) HANGUL SYLLABLE WAESS +C671;C671;110B 116B 11BC;C671;110B 116B 11BC; # (왱; 왱; 왱; 왱; 왱; ) HANGUL SYLLABLE WAENG +C672;C672;110B 116B 11BD;C672;110B 116B 11BD; # (왲; 왲; 왲; 왲; 왲; ) HANGUL SYLLABLE WAEJ +C673;C673;110B 116B 11BE;C673;110B 116B 11BE; # (왳; 왳; 왳; 왳; 왳; ) HANGUL SYLLABLE WAEC +C674;C674;110B 116B 11BF;C674;110B 116B 11BF; # (왴; 왴; 왴; 왴; 왴; ) HANGUL SYLLABLE WAEK +C675;C675;110B 116B 11C0;C675;110B 116B 11C0; # (왵; 왵; 왵; 왵; 왵; ) HANGUL SYLLABLE WAET +C676;C676;110B 116B 11C1;C676;110B 116B 11C1; # (왶; 왶; 왶; 왶; 왶; ) HANGUL SYLLABLE WAEP +C677;C677;110B 116B 11C2;C677;110B 116B 11C2; # (왷; 왷; 왷; 왷; 왷; ) HANGUL SYLLABLE WAEH +C678;C678;110B 116C;C678;110B 116C; # (외; 외; 외; 외; 외; ) HANGUL SYLLABLE OE +C679;C679;110B 116C 11A8;C679;110B 116C 11A8; # (왹; 왹; 왹; 왹; 왹; ) HANGUL SYLLABLE OEG +C67A;C67A;110B 116C 11A9;C67A;110B 116C 11A9; # (왺; 왺; 왺; 왺; 왺; ) HANGUL SYLLABLE OEGG +C67B;C67B;110B 116C 11AA;C67B;110B 116C 11AA; # (왻; 왻; 왻; 왻; 왻; ) HANGUL SYLLABLE OEGS +C67C;C67C;110B 116C 11AB;C67C;110B 116C 11AB; # (왼; 왼; 왼; 왼; 왼; ) HANGUL SYLLABLE OEN +C67D;C67D;110B 116C 11AC;C67D;110B 116C 11AC; # (왽; 왽; 왽; 왽; 왽; ) HANGUL SYLLABLE OENJ +C67E;C67E;110B 116C 11AD;C67E;110B 116C 11AD; # (왾; 왾; 왾; 왾; 왾; ) HANGUL SYLLABLE OENH +C67F;C67F;110B 116C 11AE;C67F;110B 116C 11AE; # (왿; 왿; 왿; 왿; 왿; ) HANGUL SYLLABLE OED +C680;C680;110B 116C 11AF;C680;110B 116C 11AF; # (욀; 욀; 욀; 욀; 욀; ) HANGUL SYLLABLE OEL +C681;C681;110B 116C 11B0;C681;110B 116C 11B0; # (욁; 욁; 욁; 욁; 욁; ) HANGUL SYLLABLE OELG +C682;C682;110B 116C 11B1;C682;110B 116C 11B1; # (욂; 욂; 욂; 욂; 욂; ) HANGUL SYLLABLE OELM +C683;C683;110B 116C 11B2;C683;110B 116C 11B2; # (욃; 욃; 욃; 욃; 욃; ) HANGUL SYLLABLE OELB +C684;C684;110B 116C 11B3;C684;110B 116C 11B3; # (욄; 욄; 욄; 욄; 욄; ) HANGUL SYLLABLE OELS +C685;C685;110B 116C 11B4;C685;110B 116C 11B4; # (욅; 욅; 욅; 욅; 욅; ) HANGUL SYLLABLE OELT +C686;C686;110B 116C 11B5;C686;110B 116C 11B5; # (욆; 욆; 욆; 욆; 욆; ) HANGUL SYLLABLE OELP +C687;C687;110B 116C 11B6;C687;110B 116C 11B6; # (욇; 욇; 욇; 욇; 욇; ) HANGUL SYLLABLE OELH +C688;C688;110B 116C 11B7;C688;110B 116C 11B7; # (욈; 욈; 욈; 욈; 욈; ) HANGUL SYLLABLE OEM +C689;C689;110B 116C 11B8;C689;110B 116C 11B8; # (욉; 욉; 욉; 욉; 욉; ) HANGUL SYLLABLE OEB +C68A;C68A;110B 116C 11B9;C68A;110B 116C 11B9; # (욊; 욊; 욊; 욊; 욊; ) HANGUL SYLLABLE OEBS +C68B;C68B;110B 116C 11BA;C68B;110B 116C 11BA; # (욋; 욋; 욋; 욋; 욋; ) HANGUL SYLLABLE OES +C68C;C68C;110B 116C 11BB;C68C;110B 116C 11BB; # (욌; 욌; 욌; 욌; 욌; ) HANGUL SYLLABLE OESS +C68D;C68D;110B 116C 11BC;C68D;110B 116C 11BC; # (욍; 욍; 욍; 욍; 욍; ) HANGUL SYLLABLE OENG +C68E;C68E;110B 116C 11BD;C68E;110B 116C 11BD; # (욎; 욎; 욎; 욎; 욎; ) HANGUL SYLLABLE OEJ +C68F;C68F;110B 116C 11BE;C68F;110B 116C 11BE; # (욏; 욏; 욏; 욏; 욏; ) HANGUL SYLLABLE OEC +C690;C690;110B 116C 11BF;C690;110B 116C 11BF; # (욐; 욐; 욐; 욐; 욐; ) HANGUL SYLLABLE OEK +C691;C691;110B 116C 11C0;C691;110B 116C 11C0; # (욑; 욑; 욑; 욑; 욑; ) HANGUL SYLLABLE OET +C692;C692;110B 116C 11C1;C692;110B 116C 11C1; # (욒; 욒; 욒; 욒; 욒; ) HANGUL SYLLABLE OEP +C693;C693;110B 116C 11C2;C693;110B 116C 11C2; # (욓; 욓; 욓; 욓; 욓; ) HANGUL SYLLABLE OEH +C694;C694;110B 116D;C694;110B 116D; # (요; 요; 요; 요; 요; ) HANGUL SYLLABLE YO +C695;C695;110B 116D 11A8;C695;110B 116D 11A8; # (욕; 욕; 욕; 욕; 욕; ) HANGUL SYLLABLE YOG +C696;C696;110B 116D 11A9;C696;110B 116D 11A9; # (욖; 욖; 욖; 욖; 욖; ) HANGUL SYLLABLE YOGG +C697;C697;110B 116D 11AA;C697;110B 116D 11AA; # (욗; 욗; 욗; 욗; 욗; ) HANGUL SYLLABLE YOGS +C698;C698;110B 116D 11AB;C698;110B 116D 11AB; # (욘; 욘; 욘; 욘; 욘; ) HANGUL SYLLABLE YON +C699;C699;110B 116D 11AC;C699;110B 116D 11AC; # (욙; 욙; 욙; 욙; 욙; ) HANGUL SYLLABLE YONJ +C69A;C69A;110B 116D 11AD;C69A;110B 116D 11AD; # (욚; 욚; 욚; 욚; 욚; ) HANGUL SYLLABLE YONH +C69B;C69B;110B 116D 11AE;C69B;110B 116D 11AE; # (욛; 욛; 욛; 욛; 욛; ) HANGUL SYLLABLE YOD +C69C;C69C;110B 116D 11AF;C69C;110B 116D 11AF; # (욜; 욜; 욜; 욜; 욜; ) HANGUL SYLLABLE YOL +C69D;C69D;110B 116D 11B0;C69D;110B 116D 11B0; # (욝; 욝; 욝; 욝; 욝; ) HANGUL SYLLABLE YOLG +C69E;C69E;110B 116D 11B1;C69E;110B 116D 11B1; # (욞; 욞; 욞; 욞; 욞; ) HANGUL SYLLABLE YOLM +C69F;C69F;110B 116D 11B2;C69F;110B 116D 11B2; # (욟; 욟; 욟; 욟; 욟; ) HANGUL SYLLABLE YOLB +C6A0;C6A0;110B 116D 11B3;C6A0;110B 116D 11B3; # (욠; 욠; 욠; 욠; 욠; ) HANGUL SYLLABLE YOLS +C6A1;C6A1;110B 116D 11B4;C6A1;110B 116D 11B4; # (욡; 욡; 욡; 욡; 욡; ) HANGUL SYLLABLE YOLT +C6A2;C6A2;110B 116D 11B5;C6A2;110B 116D 11B5; # (욢; 욢; 욢; 욢; 욢; ) HANGUL SYLLABLE YOLP +C6A3;C6A3;110B 116D 11B6;C6A3;110B 116D 11B6; # (욣; 욣; 욣; 욣; 욣; ) HANGUL SYLLABLE YOLH +C6A4;C6A4;110B 116D 11B7;C6A4;110B 116D 11B7; # (욤; 욤; 욤; 욤; 욤; ) HANGUL SYLLABLE YOM +C6A5;C6A5;110B 116D 11B8;C6A5;110B 116D 11B8; # (욥; 욥; 욥; 욥; 욥; ) HANGUL SYLLABLE YOB +C6A6;C6A6;110B 116D 11B9;C6A6;110B 116D 11B9; # (욦; 욦; 욦; 욦; 욦; ) HANGUL SYLLABLE YOBS +C6A7;C6A7;110B 116D 11BA;C6A7;110B 116D 11BA; # (욧; 욧; 욧; 욧; 욧; ) HANGUL SYLLABLE YOS +C6A8;C6A8;110B 116D 11BB;C6A8;110B 116D 11BB; # (욨; 욨; 욨; 욨; 욨; ) HANGUL SYLLABLE YOSS +C6A9;C6A9;110B 116D 11BC;C6A9;110B 116D 11BC; # (용; 용; 용; 용; 용; ) HANGUL SYLLABLE YONG +C6AA;C6AA;110B 116D 11BD;C6AA;110B 116D 11BD; # (욪; 욪; 욪; 욪; 욪; ) HANGUL SYLLABLE YOJ +C6AB;C6AB;110B 116D 11BE;C6AB;110B 116D 11BE; # (욫; 욫; 욫; 욫; 욫; ) HANGUL SYLLABLE YOC +C6AC;C6AC;110B 116D 11BF;C6AC;110B 116D 11BF; # (욬; 욬; 욬; 욬; 욬; ) HANGUL SYLLABLE YOK +C6AD;C6AD;110B 116D 11C0;C6AD;110B 116D 11C0; # (욭; 욭; 욭; 욭; 욭; ) HANGUL SYLLABLE YOT +C6AE;C6AE;110B 116D 11C1;C6AE;110B 116D 11C1; # (욮; 욮; 욮; 욮; 욮; ) HANGUL SYLLABLE YOP +C6AF;C6AF;110B 116D 11C2;C6AF;110B 116D 11C2; # (욯; 욯; 욯; 욯; 욯; ) HANGUL SYLLABLE YOH +C6B0;C6B0;110B 116E;C6B0;110B 116E; # (우; 우; 우; 우; 우; ) HANGUL SYLLABLE U +C6B1;C6B1;110B 116E 11A8;C6B1;110B 116E 11A8; # (욱; 욱; 욱; 욱; 욱; ) HANGUL SYLLABLE UG +C6B2;C6B2;110B 116E 11A9;C6B2;110B 116E 11A9; # (욲; 욲; 욲; 욲; 욲; ) HANGUL SYLLABLE UGG +C6B3;C6B3;110B 116E 11AA;C6B3;110B 116E 11AA; # (욳; 욳; 욳; 욳; 욳; ) HANGUL SYLLABLE UGS +C6B4;C6B4;110B 116E 11AB;C6B4;110B 116E 11AB; # (운; 운; 운; 운; 운; ) HANGUL SYLLABLE UN +C6B5;C6B5;110B 116E 11AC;C6B5;110B 116E 11AC; # (욵; 욵; 욵; 욵; 욵; ) HANGUL SYLLABLE UNJ +C6B6;C6B6;110B 116E 11AD;C6B6;110B 116E 11AD; # (욶; 욶; 욶; 욶; 욶; ) HANGUL SYLLABLE UNH +C6B7;C6B7;110B 116E 11AE;C6B7;110B 116E 11AE; # (욷; 욷; 욷; 욷; 욷; ) HANGUL SYLLABLE UD +C6B8;C6B8;110B 116E 11AF;C6B8;110B 116E 11AF; # (울; 울; 울; 울; 울; ) HANGUL SYLLABLE UL +C6B9;C6B9;110B 116E 11B0;C6B9;110B 116E 11B0; # (욹; 욹; 욹; 욹; 욹; ) HANGUL SYLLABLE ULG +C6BA;C6BA;110B 116E 11B1;C6BA;110B 116E 11B1; # (욺; 욺; 욺; 욺; 욺; ) HANGUL SYLLABLE ULM +C6BB;C6BB;110B 116E 11B2;C6BB;110B 116E 11B2; # (욻; 욻; 욻; 욻; 욻; ) HANGUL SYLLABLE ULB +C6BC;C6BC;110B 116E 11B3;C6BC;110B 116E 11B3; # (욼; 욼; 욼; 욼; 욼; ) HANGUL SYLLABLE ULS +C6BD;C6BD;110B 116E 11B4;C6BD;110B 116E 11B4; # (욽; 욽; 욽; 욽; 욽; ) HANGUL SYLLABLE ULT +C6BE;C6BE;110B 116E 11B5;C6BE;110B 116E 11B5; # (욾; 욾; 욾; 욾; 욾; ) HANGUL SYLLABLE ULP +C6BF;C6BF;110B 116E 11B6;C6BF;110B 116E 11B6; # (욿; 욿; 욿; 욿; 욿; ) HANGUL SYLLABLE ULH +C6C0;C6C0;110B 116E 11B7;C6C0;110B 116E 11B7; # (움; 움; 움; 움; 움; ) HANGUL SYLLABLE UM +C6C1;C6C1;110B 116E 11B8;C6C1;110B 116E 11B8; # (웁; 웁; 웁; 웁; 웁; ) HANGUL SYLLABLE UB +C6C2;C6C2;110B 116E 11B9;C6C2;110B 116E 11B9; # (웂; 웂; 웂; 웂; 웂; ) HANGUL SYLLABLE UBS +C6C3;C6C3;110B 116E 11BA;C6C3;110B 116E 11BA; # (웃; 웃; 웃; 웃; 웃; ) HANGUL SYLLABLE US +C6C4;C6C4;110B 116E 11BB;C6C4;110B 116E 11BB; # (웄; 웄; 웄; 웄; 웄; ) HANGUL SYLLABLE USS +C6C5;C6C5;110B 116E 11BC;C6C5;110B 116E 11BC; # (웅; 웅; 웅; 웅; 웅; ) HANGUL SYLLABLE UNG +C6C6;C6C6;110B 116E 11BD;C6C6;110B 116E 11BD; # (웆; 웆; 웆; 웆; 웆; ) HANGUL SYLLABLE UJ +C6C7;C6C7;110B 116E 11BE;C6C7;110B 116E 11BE; # (웇; 웇; 웇; 웇; 웇; ) HANGUL SYLLABLE UC +C6C8;C6C8;110B 116E 11BF;C6C8;110B 116E 11BF; # (웈; 웈; 웈; 웈; 웈; ) HANGUL SYLLABLE UK +C6C9;C6C9;110B 116E 11C0;C6C9;110B 116E 11C0; # (웉; 웉; 웉; 웉; 웉; ) HANGUL SYLLABLE UT +C6CA;C6CA;110B 116E 11C1;C6CA;110B 116E 11C1; # (웊; 웊; 웊; 웊; 웊; ) HANGUL SYLLABLE UP +C6CB;C6CB;110B 116E 11C2;C6CB;110B 116E 11C2; # (웋; 웋; 웋; 웋; 웋; ) HANGUL SYLLABLE UH +C6CC;C6CC;110B 116F;C6CC;110B 116F; # (워; 워; 워; 워; 워; ) HANGUL SYLLABLE WEO +C6CD;C6CD;110B 116F 11A8;C6CD;110B 116F 11A8; # (웍; 웍; 웍; 웍; 웍; ) HANGUL SYLLABLE WEOG +C6CE;C6CE;110B 116F 11A9;C6CE;110B 116F 11A9; # (웎; 웎; 웎; 웎; 웎; ) HANGUL SYLLABLE WEOGG +C6CF;C6CF;110B 116F 11AA;C6CF;110B 116F 11AA; # (웏; 웏; 웏; 웏; 웏; ) HANGUL SYLLABLE WEOGS +C6D0;C6D0;110B 116F 11AB;C6D0;110B 116F 11AB; # (원; 원; 원; 원; 원; ) HANGUL SYLLABLE WEON +C6D1;C6D1;110B 116F 11AC;C6D1;110B 116F 11AC; # (웑; 웑; 웑; 웑; 웑; ) HANGUL SYLLABLE WEONJ +C6D2;C6D2;110B 116F 11AD;C6D2;110B 116F 11AD; # (웒; 웒; 웒; 웒; 웒; ) HANGUL SYLLABLE WEONH +C6D3;C6D3;110B 116F 11AE;C6D3;110B 116F 11AE; # (웓; 웓; 웓; 웓; 웓; ) HANGUL SYLLABLE WEOD +C6D4;C6D4;110B 116F 11AF;C6D4;110B 116F 11AF; # (월; 월; 월; 월; 월; ) HANGUL SYLLABLE WEOL +C6D5;C6D5;110B 116F 11B0;C6D5;110B 116F 11B0; # (웕; 웕; 웕; 웕; 웕; ) HANGUL SYLLABLE WEOLG +C6D6;C6D6;110B 116F 11B1;C6D6;110B 116F 11B1; # (웖; 웖; 웖; 웖; 웖; ) HANGUL SYLLABLE WEOLM +C6D7;C6D7;110B 116F 11B2;C6D7;110B 116F 11B2; # (웗; 웗; 웗; 웗; 웗; ) HANGUL SYLLABLE WEOLB +C6D8;C6D8;110B 116F 11B3;C6D8;110B 116F 11B3; # (웘; 웘; 웘; 웘; 웘; ) HANGUL SYLLABLE WEOLS +C6D9;C6D9;110B 116F 11B4;C6D9;110B 116F 11B4; # (웙; 웙; 웙; 웙; 웙; ) HANGUL SYLLABLE WEOLT +C6DA;C6DA;110B 116F 11B5;C6DA;110B 116F 11B5; # (웚; 웚; 웚; 웚; 웚; ) HANGUL SYLLABLE WEOLP +C6DB;C6DB;110B 116F 11B6;C6DB;110B 116F 11B6; # (웛; 웛; 웛; 웛; 웛; ) HANGUL SYLLABLE WEOLH +C6DC;C6DC;110B 116F 11B7;C6DC;110B 116F 11B7; # (웜; 웜; 웜; 웜; 웜; ) HANGUL SYLLABLE WEOM +C6DD;C6DD;110B 116F 11B8;C6DD;110B 116F 11B8; # (웝; 웝; 웝; 웝; 웝; ) HANGUL SYLLABLE WEOB +C6DE;C6DE;110B 116F 11B9;C6DE;110B 116F 11B9; # (웞; 웞; 웞; 웞; 웞; ) HANGUL SYLLABLE WEOBS +C6DF;C6DF;110B 116F 11BA;C6DF;110B 116F 11BA; # (웟; 웟; 웟; 웟; 웟; ) HANGUL SYLLABLE WEOS +C6E0;C6E0;110B 116F 11BB;C6E0;110B 116F 11BB; # (웠; 웠; 웠; 웠; 웠; ) HANGUL SYLLABLE WEOSS +C6E1;C6E1;110B 116F 11BC;C6E1;110B 116F 11BC; # (웡; 웡; 웡; 웡; 웡; ) HANGUL SYLLABLE WEONG +C6E2;C6E2;110B 116F 11BD;C6E2;110B 116F 11BD; # (웢; 웢; 웢; 웢; 웢; ) HANGUL SYLLABLE WEOJ +C6E3;C6E3;110B 116F 11BE;C6E3;110B 116F 11BE; # (웣; 웣; 웣; 웣; 웣; ) HANGUL SYLLABLE WEOC +C6E4;C6E4;110B 116F 11BF;C6E4;110B 116F 11BF; # (웤; 웤; 웤; 웤; 웤; ) HANGUL SYLLABLE WEOK +C6E5;C6E5;110B 116F 11C0;C6E5;110B 116F 11C0; # (웥; 웥; 웥; 웥; 웥; ) HANGUL SYLLABLE WEOT +C6E6;C6E6;110B 116F 11C1;C6E6;110B 116F 11C1; # (웦; 웦; 웦; 웦; 웦; ) HANGUL SYLLABLE WEOP +C6E7;C6E7;110B 116F 11C2;C6E7;110B 116F 11C2; # (웧; 웧; 웧; 웧; 웧; ) HANGUL SYLLABLE WEOH +C6E8;C6E8;110B 1170;C6E8;110B 1170; # (웨; 웨; 웨; 웨; 웨; ) HANGUL SYLLABLE WE +C6E9;C6E9;110B 1170 11A8;C6E9;110B 1170 11A8; # (웩; 웩; 웩; 웩; 웩; ) HANGUL SYLLABLE WEG +C6EA;C6EA;110B 1170 11A9;C6EA;110B 1170 11A9; # (웪; 웪; 웪; 웪; 웪; ) HANGUL SYLLABLE WEGG +C6EB;C6EB;110B 1170 11AA;C6EB;110B 1170 11AA; # (웫; 웫; 웫; 웫; 웫; ) HANGUL SYLLABLE WEGS +C6EC;C6EC;110B 1170 11AB;C6EC;110B 1170 11AB; # (웬; 웬; 웬; 웬; 웬; ) HANGUL SYLLABLE WEN +C6ED;C6ED;110B 1170 11AC;C6ED;110B 1170 11AC; # (웭; 웭; 웭; 웭; 웭; ) HANGUL SYLLABLE WENJ +C6EE;C6EE;110B 1170 11AD;C6EE;110B 1170 11AD; # (웮; 웮; 웮; 웮; 웮; ) HANGUL SYLLABLE WENH +C6EF;C6EF;110B 1170 11AE;C6EF;110B 1170 11AE; # (웯; 웯; 웯; 웯; 웯; ) HANGUL SYLLABLE WED +C6F0;C6F0;110B 1170 11AF;C6F0;110B 1170 11AF; # (웰; 웰; 웰; 웰; 웰; ) HANGUL SYLLABLE WEL +C6F1;C6F1;110B 1170 11B0;C6F1;110B 1170 11B0; # (웱; 웱; 웱; 웱; 웱; ) HANGUL SYLLABLE WELG +C6F2;C6F2;110B 1170 11B1;C6F2;110B 1170 11B1; # (웲; 웲; 웲; 웲; 웲; ) HANGUL SYLLABLE WELM +C6F3;C6F3;110B 1170 11B2;C6F3;110B 1170 11B2; # (웳; 웳; 웳; 웳; 웳; ) HANGUL SYLLABLE WELB +C6F4;C6F4;110B 1170 11B3;C6F4;110B 1170 11B3; # (웴; 웴; 웴; 웴; 웴; ) HANGUL SYLLABLE WELS +C6F5;C6F5;110B 1170 11B4;C6F5;110B 1170 11B4; # (웵; 웵; 웵; 웵; 웵; ) HANGUL SYLLABLE WELT +C6F6;C6F6;110B 1170 11B5;C6F6;110B 1170 11B5; # (웶; 웶; 웶; 웶; 웶; ) HANGUL SYLLABLE WELP +C6F7;C6F7;110B 1170 11B6;C6F7;110B 1170 11B6; # (웷; 웷; 웷; 웷; 웷; ) HANGUL SYLLABLE WELH +C6F8;C6F8;110B 1170 11B7;C6F8;110B 1170 11B7; # (웸; 웸; 웸; 웸; 웸; ) HANGUL SYLLABLE WEM +C6F9;C6F9;110B 1170 11B8;C6F9;110B 1170 11B8; # (웹; 웹; 웹; 웹; 웹; ) HANGUL SYLLABLE WEB +C6FA;C6FA;110B 1170 11B9;C6FA;110B 1170 11B9; # (웺; 웺; 웺; 웺; 웺; ) HANGUL SYLLABLE WEBS +C6FB;C6FB;110B 1170 11BA;C6FB;110B 1170 11BA; # (웻; 웻; 웻; 웻; 웻; ) HANGUL SYLLABLE WES +C6FC;C6FC;110B 1170 11BB;C6FC;110B 1170 11BB; # (웼; 웼; 웼; 웼; 웼; ) HANGUL SYLLABLE WESS +C6FD;C6FD;110B 1170 11BC;C6FD;110B 1170 11BC; # (웽; 웽; 웽; 웽; 웽; ) HANGUL SYLLABLE WENG +C6FE;C6FE;110B 1170 11BD;C6FE;110B 1170 11BD; # (웾; 웾; 웾; 웾; 웾; ) HANGUL SYLLABLE WEJ +C6FF;C6FF;110B 1170 11BE;C6FF;110B 1170 11BE; # (웿; 웿; 웿; 웿; 웿; ) HANGUL SYLLABLE WEC +C700;C700;110B 1170 11BF;C700;110B 1170 11BF; # (윀; 윀; 윀; 윀; 윀; ) HANGUL SYLLABLE WEK +C701;C701;110B 1170 11C0;C701;110B 1170 11C0; # (윁; 윁; 윁; 윁; 윁; ) HANGUL SYLLABLE WET +C702;C702;110B 1170 11C1;C702;110B 1170 11C1; # (윂; 윂; 윂; 윂; 윂; ) HANGUL SYLLABLE WEP +C703;C703;110B 1170 11C2;C703;110B 1170 11C2; # (윃; 윃; 윃; 윃; 윃; ) HANGUL SYLLABLE WEH +C704;C704;110B 1171;C704;110B 1171; # (위; 위; 위; 위; 위; ) HANGUL SYLLABLE WI +C705;C705;110B 1171 11A8;C705;110B 1171 11A8; # (윅; 윅; 윅; 윅; 윅; ) HANGUL SYLLABLE WIG +C706;C706;110B 1171 11A9;C706;110B 1171 11A9; # (윆; 윆; 윆; 윆; 윆; ) HANGUL SYLLABLE WIGG +C707;C707;110B 1171 11AA;C707;110B 1171 11AA; # (윇; 윇; 윇; 윇; 윇; ) HANGUL SYLLABLE WIGS +C708;C708;110B 1171 11AB;C708;110B 1171 11AB; # (윈; 윈; 윈; 윈; 윈; ) HANGUL SYLLABLE WIN +C709;C709;110B 1171 11AC;C709;110B 1171 11AC; # (윉; 윉; 윉; 윉; 윉; ) HANGUL SYLLABLE WINJ +C70A;C70A;110B 1171 11AD;C70A;110B 1171 11AD; # (윊; 윊; 윊; 윊; 윊; ) HANGUL SYLLABLE WINH +C70B;C70B;110B 1171 11AE;C70B;110B 1171 11AE; # (윋; 윋; 윋; 윋; 윋; ) HANGUL SYLLABLE WID +C70C;C70C;110B 1171 11AF;C70C;110B 1171 11AF; # (윌; 윌; 윌; 윌; 윌; ) HANGUL SYLLABLE WIL +C70D;C70D;110B 1171 11B0;C70D;110B 1171 11B0; # (윍; 윍; 윍; 윍; 윍; ) HANGUL SYLLABLE WILG +C70E;C70E;110B 1171 11B1;C70E;110B 1171 11B1; # (윎; 윎; 윎; 윎; 윎; ) HANGUL SYLLABLE WILM +C70F;C70F;110B 1171 11B2;C70F;110B 1171 11B2; # (윏; 윏; 윏; 윏; 윏; ) HANGUL SYLLABLE WILB +C710;C710;110B 1171 11B3;C710;110B 1171 11B3; # (윐; 윐; 윐; 윐; 윐; ) HANGUL SYLLABLE WILS +C711;C711;110B 1171 11B4;C711;110B 1171 11B4; # (윑; 윑; 윑; 윑; 윑; ) HANGUL SYLLABLE WILT +C712;C712;110B 1171 11B5;C712;110B 1171 11B5; # (윒; 윒; 윒; 윒; 윒; ) HANGUL SYLLABLE WILP +C713;C713;110B 1171 11B6;C713;110B 1171 11B6; # (윓; 윓; 윓; 윓; 윓; ) HANGUL SYLLABLE WILH +C714;C714;110B 1171 11B7;C714;110B 1171 11B7; # (윔; 윔; 윔; 윔; 윔; ) HANGUL SYLLABLE WIM +C715;C715;110B 1171 11B8;C715;110B 1171 11B8; # (윕; 윕; 윕; 윕; 윕; ) HANGUL SYLLABLE WIB +C716;C716;110B 1171 11B9;C716;110B 1171 11B9; # (윖; 윖; 윖; 윖; 윖; ) HANGUL SYLLABLE WIBS +C717;C717;110B 1171 11BA;C717;110B 1171 11BA; # (윗; 윗; 윗; 윗; 윗; ) HANGUL SYLLABLE WIS +C718;C718;110B 1171 11BB;C718;110B 1171 11BB; # (윘; 윘; 윘; 윘; 윘; ) HANGUL SYLLABLE WISS +C719;C719;110B 1171 11BC;C719;110B 1171 11BC; # (윙; 윙; 윙; 윙; 윙; ) HANGUL SYLLABLE WING +C71A;C71A;110B 1171 11BD;C71A;110B 1171 11BD; # (윚; 윚; 윚; 윚; 윚; ) HANGUL SYLLABLE WIJ +C71B;C71B;110B 1171 11BE;C71B;110B 1171 11BE; # (윛; 윛; 윛; 윛; 윛; ) HANGUL SYLLABLE WIC +C71C;C71C;110B 1171 11BF;C71C;110B 1171 11BF; # (윜; 윜; 윜; 윜; 윜; ) HANGUL SYLLABLE WIK +C71D;C71D;110B 1171 11C0;C71D;110B 1171 11C0; # (윝; 윝; 윝; 윝; 윝; ) HANGUL SYLLABLE WIT +C71E;C71E;110B 1171 11C1;C71E;110B 1171 11C1; # (윞; 윞; 윞; 윞; 윞; ) HANGUL SYLLABLE WIP +C71F;C71F;110B 1171 11C2;C71F;110B 1171 11C2; # (윟; 윟; 윟; 윟; 윟; ) HANGUL SYLLABLE WIH +C720;C720;110B 1172;C720;110B 1172; # (유; 유; 유; 유; 유; ) HANGUL SYLLABLE YU +C721;C721;110B 1172 11A8;C721;110B 1172 11A8; # (육; 육; 육; 육; 육; ) HANGUL SYLLABLE YUG +C722;C722;110B 1172 11A9;C722;110B 1172 11A9; # (윢; 윢; 윢; 윢; 윢; ) HANGUL SYLLABLE YUGG +C723;C723;110B 1172 11AA;C723;110B 1172 11AA; # (윣; 윣; 윣; 윣; 윣; ) HANGUL SYLLABLE YUGS +C724;C724;110B 1172 11AB;C724;110B 1172 11AB; # (윤; 윤; 윤; 윤; 윤; ) HANGUL SYLLABLE YUN +C725;C725;110B 1172 11AC;C725;110B 1172 11AC; # (윥; 윥; 윥; 윥; 윥; ) HANGUL SYLLABLE YUNJ +C726;C726;110B 1172 11AD;C726;110B 1172 11AD; # (윦; 윦; 윦; 윦; 윦; ) HANGUL SYLLABLE YUNH +C727;C727;110B 1172 11AE;C727;110B 1172 11AE; # (윧; 윧; 윧; 윧; 윧; ) HANGUL SYLLABLE YUD +C728;C728;110B 1172 11AF;C728;110B 1172 11AF; # (율; 율; 율; 율; 율; ) HANGUL SYLLABLE YUL +C729;C729;110B 1172 11B0;C729;110B 1172 11B0; # (윩; 윩; 윩; 윩; 윩; ) HANGUL SYLLABLE YULG +C72A;C72A;110B 1172 11B1;C72A;110B 1172 11B1; # (윪; 윪; 윪; 윪; 윪; ) HANGUL SYLLABLE YULM +C72B;C72B;110B 1172 11B2;C72B;110B 1172 11B2; # (윫; 윫; 윫; 윫; 윫; ) HANGUL SYLLABLE YULB +C72C;C72C;110B 1172 11B3;C72C;110B 1172 11B3; # (윬; 윬; 윬; 윬; 윬; ) HANGUL SYLLABLE YULS +C72D;C72D;110B 1172 11B4;C72D;110B 1172 11B4; # (윭; 윭; 윭; 윭; 윭; ) HANGUL SYLLABLE YULT +C72E;C72E;110B 1172 11B5;C72E;110B 1172 11B5; # (윮; 윮; 윮; 윮; 윮; ) HANGUL SYLLABLE YULP +C72F;C72F;110B 1172 11B6;C72F;110B 1172 11B6; # (윯; 윯; 윯; 윯; 윯; ) HANGUL SYLLABLE YULH +C730;C730;110B 1172 11B7;C730;110B 1172 11B7; # (윰; 윰; 윰; 윰; 윰; ) HANGUL SYLLABLE YUM +C731;C731;110B 1172 11B8;C731;110B 1172 11B8; # (윱; 윱; 윱; 윱; 윱; ) HANGUL SYLLABLE YUB +C732;C732;110B 1172 11B9;C732;110B 1172 11B9; # (윲; 윲; 윲; 윲; 윲; ) HANGUL SYLLABLE YUBS +C733;C733;110B 1172 11BA;C733;110B 1172 11BA; # (윳; 윳; 윳; 윳; 윳; ) HANGUL SYLLABLE YUS +C734;C734;110B 1172 11BB;C734;110B 1172 11BB; # (윴; 윴; 윴; 윴; 윴; ) HANGUL SYLLABLE YUSS +C735;C735;110B 1172 11BC;C735;110B 1172 11BC; # (융; 융; 융; 융; 융; ) HANGUL SYLLABLE YUNG +C736;C736;110B 1172 11BD;C736;110B 1172 11BD; # (윶; 윶; 윶; 윶; 윶; ) HANGUL SYLLABLE YUJ +C737;C737;110B 1172 11BE;C737;110B 1172 11BE; # (윷; 윷; 윷; 윷; 윷; ) HANGUL SYLLABLE YUC +C738;C738;110B 1172 11BF;C738;110B 1172 11BF; # (윸; 윸; 윸; 윸; 윸; ) HANGUL SYLLABLE YUK +C739;C739;110B 1172 11C0;C739;110B 1172 11C0; # (윹; 윹; 윹; 윹; 윹; ) HANGUL SYLLABLE YUT +C73A;C73A;110B 1172 11C1;C73A;110B 1172 11C1; # (윺; 윺; 윺; 윺; 윺; ) HANGUL SYLLABLE YUP +C73B;C73B;110B 1172 11C2;C73B;110B 1172 11C2; # (윻; 윻; 윻; 윻; 윻; ) HANGUL SYLLABLE YUH +C73C;C73C;110B 1173;C73C;110B 1173; # (으; 으; 으; 으; 으; ) HANGUL SYLLABLE EU +C73D;C73D;110B 1173 11A8;C73D;110B 1173 11A8; # (윽; 윽; 윽; 윽; 윽; ) HANGUL SYLLABLE EUG +C73E;C73E;110B 1173 11A9;C73E;110B 1173 11A9; # (윾; 윾; 윾; 윾; 윾; ) HANGUL SYLLABLE EUGG +C73F;C73F;110B 1173 11AA;C73F;110B 1173 11AA; # (윿; 윿; 윿; 윿; 윿; ) HANGUL SYLLABLE EUGS +C740;C740;110B 1173 11AB;C740;110B 1173 11AB; # (은; 은; 은; 은; 은; ) HANGUL SYLLABLE EUN +C741;C741;110B 1173 11AC;C741;110B 1173 11AC; # (읁; 읁; 읁; 읁; 읁; ) HANGUL SYLLABLE EUNJ +C742;C742;110B 1173 11AD;C742;110B 1173 11AD; # (읂; 읂; 읂; 읂; 읂; ) HANGUL SYLLABLE EUNH +C743;C743;110B 1173 11AE;C743;110B 1173 11AE; # (읃; 읃; 읃; 읃; 읃; ) HANGUL SYLLABLE EUD +C744;C744;110B 1173 11AF;C744;110B 1173 11AF; # (을; 을; 을; 을; 을; ) HANGUL SYLLABLE EUL +C745;C745;110B 1173 11B0;C745;110B 1173 11B0; # (읅; 읅; 읅; 읅; 읅; ) HANGUL SYLLABLE EULG +C746;C746;110B 1173 11B1;C746;110B 1173 11B1; # (읆; 읆; 읆; 읆; 읆; ) HANGUL SYLLABLE EULM +C747;C747;110B 1173 11B2;C747;110B 1173 11B2; # (읇; 읇; 읇; 읇; 읇; ) HANGUL SYLLABLE EULB +C748;C748;110B 1173 11B3;C748;110B 1173 11B3; # (읈; 읈; 읈; 읈; 읈; ) HANGUL SYLLABLE EULS +C749;C749;110B 1173 11B4;C749;110B 1173 11B4; # (읉; 읉; 읉; 읉; 읉; ) HANGUL SYLLABLE EULT +C74A;C74A;110B 1173 11B5;C74A;110B 1173 11B5; # (읊; 읊; 읊; 읊; 읊; ) HANGUL SYLLABLE EULP +C74B;C74B;110B 1173 11B6;C74B;110B 1173 11B6; # (읋; 읋; 읋; 읋; 읋; ) HANGUL SYLLABLE EULH +C74C;C74C;110B 1173 11B7;C74C;110B 1173 11B7; # (음; 음; 음; 음; 음; ) HANGUL SYLLABLE EUM +C74D;C74D;110B 1173 11B8;C74D;110B 1173 11B8; # (읍; 읍; 읍; 읍; 읍; ) HANGUL SYLLABLE EUB +C74E;C74E;110B 1173 11B9;C74E;110B 1173 11B9; # (읎; 읎; 읎; 읎; 읎; ) HANGUL SYLLABLE EUBS +C74F;C74F;110B 1173 11BA;C74F;110B 1173 11BA; # (읏; 읏; 읏; 읏; 읏; ) HANGUL SYLLABLE EUS +C750;C750;110B 1173 11BB;C750;110B 1173 11BB; # (읐; 읐; 읐; 읐; 읐; ) HANGUL SYLLABLE EUSS +C751;C751;110B 1173 11BC;C751;110B 1173 11BC; # (응; 응; 응; 응; 응; ) HANGUL SYLLABLE EUNG +C752;C752;110B 1173 11BD;C752;110B 1173 11BD; # (읒; 읒; 읒; 읒; 읒; ) HANGUL SYLLABLE EUJ +C753;C753;110B 1173 11BE;C753;110B 1173 11BE; # (읓; 읓; 읓; 읓; 읓; ) HANGUL SYLLABLE EUC +C754;C754;110B 1173 11BF;C754;110B 1173 11BF; # (읔; 읔; 읔; 읔; 읔; ) HANGUL SYLLABLE EUK +C755;C755;110B 1173 11C0;C755;110B 1173 11C0; # (읕; 읕; 읕; 읕; 읕; ) HANGUL SYLLABLE EUT +C756;C756;110B 1173 11C1;C756;110B 1173 11C1; # (읖; 읖; 읖; 읖; 읖; ) HANGUL SYLLABLE EUP +C757;C757;110B 1173 11C2;C757;110B 1173 11C2; # (읗; 읗; 읗; 읗; 읗; ) HANGUL SYLLABLE EUH +C758;C758;110B 1174;C758;110B 1174; # (의; 의; 의; 의; 의; ) HANGUL SYLLABLE YI +C759;C759;110B 1174 11A8;C759;110B 1174 11A8; # (읙; 읙; 읙; 읙; 읙; ) HANGUL SYLLABLE YIG +C75A;C75A;110B 1174 11A9;C75A;110B 1174 11A9; # (읚; 읚; 읚; 읚; 읚; ) HANGUL SYLLABLE YIGG +C75B;C75B;110B 1174 11AA;C75B;110B 1174 11AA; # (읛; 읛; 읛; 읛; 읛; ) HANGUL SYLLABLE YIGS +C75C;C75C;110B 1174 11AB;C75C;110B 1174 11AB; # (읜; 읜; 읜; 읜; 읜; ) HANGUL SYLLABLE YIN +C75D;C75D;110B 1174 11AC;C75D;110B 1174 11AC; # (읝; 읝; 읝; 읝; 읝; ) HANGUL SYLLABLE YINJ +C75E;C75E;110B 1174 11AD;C75E;110B 1174 11AD; # (읞; 읞; 읞; 읞; 읞; ) HANGUL SYLLABLE YINH +C75F;C75F;110B 1174 11AE;C75F;110B 1174 11AE; # (읟; 읟; 읟; 읟; 읟; ) HANGUL SYLLABLE YID +C760;C760;110B 1174 11AF;C760;110B 1174 11AF; # (읠; 읠; 읠; 읠; 읠; ) HANGUL SYLLABLE YIL +C761;C761;110B 1174 11B0;C761;110B 1174 11B0; # (읡; 읡; 읡; 읡; 읡; ) HANGUL SYLLABLE YILG +C762;C762;110B 1174 11B1;C762;110B 1174 11B1; # (읢; 읢; 읢; 읢; 읢; ) HANGUL SYLLABLE YILM +C763;C763;110B 1174 11B2;C763;110B 1174 11B2; # (읣; 읣; 읣; 읣; 읣; ) HANGUL SYLLABLE YILB +C764;C764;110B 1174 11B3;C764;110B 1174 11B3; # (읤; 읤; 읤; 읤; 읤; ) HANGUL SYLLABLE YILS +C765;C765;110B 1174 11B4;C765;110B 1174 11B4; # (읥; 읥; 읥; 읥; 읥; ) HANGUL SYLLABLE YILT +C766;C766;110B 1174 11B5;C766;110B 1174 11B5; # (읦; 읦; 읦; 읦; 읦; ) HANGUL SYLLABLE YILP +C767;C767;110B 1174 11B6;C767;110B 1174 11B6; # (읧; 읧; 읧; 읧; 읧; ) HANGUL SYLLABLE YILH +C768;C768;110B 1174 11B7;C768;110B 1174 11B7; # (읨; 읨; 읨; 읨; 읨; ) HANGUL SYLLABLE YIM +C769;C769;110B 1174 11B8;C769;110B 1174 11B8; # (읩; 읩; 읩; 읩; 읩; ) HANGUL SYLLABLE YIB +C76A;C76A;110B 1174 11B9;C76A;110B 1174 11B9; # (읪; 읪; 읪; 읪; 읪; ) HANGUL SYLLABLE YIBS +C76B;C76B;110B 1174 11BA;C76B;110B 1174 11BA; # (읫; 읫; 읫; 읫; 읫; ) HANGUL SYLLABLE YIS +C76C;C76C;110B 1174 11BB;C76C;110B 1174 11BB; # (읬; 읬; 읬; 읬; 읬; ) HANGUL SYLLABLE YISS +C76D;C76D;110B 1174 11BC;C76D;110B 1174 11BC; # (읭; 읭; 읭; 읭; 읭; ) HANGUL SYLLABLE YING +C76E;C76E;110B 1174 11BD;C76E;110B 1174 11BD; # (읮; 읮; 읮; 읮; 읮; ) HANGUL SYLLABLE YIJ +C76F;C76F;110B 1174 11BE;C76F;110B 1174 11BE; # (읯; 읯; 읯; 읯; 읯; ) HANGUL SYLLABLE YIC +C770;C770;110B 1174 11BF;C770;110B 1174 11BF; # (읰; 읰; 읰; 읰; 읰; ) HANGUL SYLLABLE YIK +C771;C771;110B 1174 11C0;C771;110B 1174 11C0; # (읱; 읱; 읱; 읱; 읱; ) HANGUL SYLLABLE YIT +C772;C772;110B 1174 11C1;C772;110B 1174 11C1; # (읲; 읲; 읲; 읲; 읲; ) HANGUL SYLLABLE YIP +C773;C773;110B 1174 11C2;C773;110B 1174 11C2; # (읳; 읳; 읳; 읳; 읳; ) HANGUL SYLLABLE YIH +C774;C774;110B 1175;C774;110B 1175; # (이; 이; 이; 이; 이; ) HANGUL SYLLABLE I +C775;C775;110B 1175 11A8;C775;110B 1175 11A8; # (익; 익; 익; 익; 익; ) HANGUL SYLLABLE IG +C776;C776;110B 1175 11A9;C776;110B 1175 11A9; # (읶; 읶; 읶; 읶; 읶; ) HANGUL SYLLABLE IGG +C777;C777;110B 1175 11AA;C777;110B 1175 11AA; # (읷; 읷; 읷; 읷; 읷; ) HANGUL SYLLABLE IGS +C778;C778;110B 1175 11AB;C778;110B 1175 11AB; # (인; 인; 인; 인; 인; ) HANGUL SYLLABLE IN +C779;C779;110B 1175 11AC;C779;110B 1175 11AC; # (읹; 읹; 읹; 읹; 읹; ) HANGUL SYLLABLE INJ +C77A;C77A;110B 1175 11AD;C77A;110B 1175 11AD; # (읺; 읺; 읺; 읺; 읺; ) HANGUL SYLLABLE INH +C77B;C77B;110B 1175 11AE;C77B;110B 1175 11AE; # (읻; 읻; 읻; 읻; 읻; ) HANGUL SYLLABLE ID +C77C;C77C;110B 1175 11AF;C77C;110B 1175 11AF; # (일; 일; 일; 일; 일; ) HANGUL SYLLABLE IL +C77D;C77D;110B 1175 11B0;C77D;110B 1175 11B0; # (읽; 읽; 읽; 읽; 읽; ) HANGUL SYLLABLE ILG +C77E;C77E;110B 1175 11B1;C77E;110B 1175 11B1; # (읾; 읾; 읾; 읾; 읾; ) HANGUL SYLLABLE ILM +C77F;C77F;110B 1175 11B2;C77F;110B 1175 11B2; # (읿; 읿; 읿; 읿; 읿; ) HANGUL SYLLABLE ILB +C780;C780;110B 1175 11B3;C780;110B 1175 11B3; # (잀; 잀; 잀; 잀; 잀; ) HANGUL SYLLABLE ILS +C781;C781;110B 1175 11B4;C781;110B 1175 11B4; # (잁; 잁; 잁; 잁; 잁; ) HANGUL SYLLABLE ILT +C782;C782;110B 1175 11B5;C782;110B 1175 11B5; # (잂; 잂; 잂; 잂; 잂; ) HANGUL SYLLABLE ILP +C783;C783;110B 1175 11B6;C783;110B 1175 11B6; # (잃; 잃; 잃; 잃; 잃; ) HANGUL SYLLABLE ILH +C784;C784;110B 1175 11B7;C784;110B 1175 11B7; # (임; 임; 임; 임; 임; ) HANGUL SYLLABLE IM +C785;C785;110B 1175 11B8;C785;110B 1175 11B8; # (입; 입; 입; 입; 입; ) HANGUL SYLLABLE IB +C786;C786;110B 1175 11B9;C786;110B 1175 11B9; # (잆; 잆; 잆; 잆; 잆; ) HANGUL SYLLABLE IBS +C787;C787;110B 1175 11BA;C787;110B 1175 11BA; # (잇; 잇; 잇; 잇; 잇; ) HANGUL SYLLABLE IS +C788;C788;110B 1175 11BB;C788;110B 1175 11BB; # (있; 있; 있; 있; 있; ) HANGUL SYLLABLE ISS +C789;C789;110B 1175 11BC;C789;110B 1175 11BC; # (잉; 잉; 잉; 잉; 잉; ) HANGUL SYLLABLE ING +C78A;C78A;110B 1175 11BD;C78A;110B 1175 11BD; # (잊; 잊; 잊; 잊; 잊; ) HANGUL SYLLABLE IJ +C78B;C78B;110B 1175 11BE;C78B;110B 1175 11BE; # (잋; 잋; 잋; 잋; 잋; ) HANGUL SYLLABLE IC +C78C;C78C;110B 1175 11BF;C78C;110B 1175 11BF; # (잌; 잌; 잌; 잌; 잌; ) HANGUL SYLLABLE IK +C78D;C78D;110B 1175 11C0;C78D;110B 1175 11C0; # (잍; 잍; 잍; 잍; 잍; ) HANGUL SYLLABLE IT +C78E;C78E;110B 1175 11C1;C78E;110B 1175 11C1; # (잎; 잎; 잎; 잎; 잎; ) HANGUL SYLLABLE IP +C78F;C78F;110B 1175 11C2;C78F;110B 1175 11C2; # (잏; 잏; 잏; 잏; 잏; ) HANGUL SYLLABLE IH +C790;C790;110C 1161;C790;110C 1161; # (자; 자; 자; 자; 자; ) HANGUL SYLLABLE JA +C791;C791;110C 1161 11A8;C791;110C 1161 11A8; # (작; 작; 작; 작; 작; ) HANGUL SYLLABLE JAG +C792;C792;110C 1161 11A9;C792;110C 1161 11A9; # (잒; 잒; 잒; 잒; 잒; ) HANGUL SYLLABLE JAGG +C793;C793;110C 1161 11AA;C793;110C 1161 11AA; # (잓; 잓; 잓; 잓; 잓; ) HANGUL SYLLABLE JAGS +C794;C794;110C 1161 11AB;C794;110C 1161 11AB; # (잔; 잔; 잔; 잔; 잔; ) HANGUL SYLLABLE JAN +C795;C795;110C 1161 11AC;C795;110C 1161 11AC; # (잕; 잕; 잕; 잕; 잕; ) HANGUL SYLLABLE JANJ +C796;C796;110C 1161 11AD;C796;110C 1161 11AD; # (잖; 잖; 잖; 잖; 잖; ) HANGUL SYLLABLE JANH +C797;C797;110C 1161 11AE;C797;110C 1161 11AE; # (잗; 잗; 잗; 잗; 잗; ) HANGUL SYLLABLE JAD +C798;C798;110C 1161 11AF;C798;110C 1161 11AF; # (잘; 잘; 잘; 잘; 잘; ) HANGUL SYLLABLE JAL +C799;C799;110C 1161 11B0;C799;110C 1161 11B0; # (잙; 잙; 잙; 잙; 잙; ) HANGUL SYLLABLE JALG +C79A;C79A;110C 1161 11B1;C79A;110C 1161 11B1; # (잚; 잚; 잚; 잚; 잚; ) HANGUL SYLLABLE JALM +C79B;C79B;110C 1161 11B2;C79B;110C 1161 11B2; # (잛; 잛; 잛; 잛; 잛; ) HANGUL SYLLABLE JALB +C79C;C79C;110C 1161 11B3;C79C;110C 1161 11B3; # (잜; 잜; 잜; 잜; 잜; ) HANGUL SYLLABLE JALS +C79D;C79D;110C 1161 11B4;C79D;110C 1161 11B4; # (잝; 잝; 잝; 잝; 잝; ) HANGUL SYLLABLE JALT +C79E;C79E;110C 1161 11B5;C79E;110C 1161 11B5; # (잞; 잞; 잞; 잞; 잞; ) HANGUL SYLLABLE JALP +C79F;C79F;110C 1161 11B6;C79F;110C 1161 11B6; # (잟; 잟; 잟; 잟; 잟; ) HANGUL SYLLABLE JALH +C7A0;C7A0;110C 1161 11B7;C7A0;110C 1161 11B7; # (잠; 잠; 잠; 잠; 잠; ) HANGUL SYLLABLE JAM +C7A1;C7A1;110C 1161 11B8;C7A1;110C 1161 11B8; # (잡; 잡; 잡; 잡; 잡; ) HANGUL SYLLABLE JAB +C7A2;C7A2;110C 1161 11B9;C7A2;110C 1161 11B9; # (잢; 잢; 잢; 잢; 잢; ) HANGUL SYLLABLE JABS +C7A3;C7A3;110C 1161 11BA;C7A3;110C 1161 11BA; # (잣; 잣; 잣; 잣; 잣; ) HANGUL SYLLABLE JAS +C7A4;C7A4;110C 1161 11BB;C7A4;110C 1161 11BB; # (잤; 잤; 잤; 잤; 잤; ) HANGUL SYLLABLE JASS +C7A5;C7A5;110C 1161 11BC;C7A5;110C 1161 11BC; # (장; 장; 장; 장; 장; ) HANGUL SYLLABLE JANG +C7A6;C7A6;110C 1161 11BD;C7A6;110C 1161 11BD; # (잦; 잦; 잦; 잦; 잦; ) HANGUL SYLLABLE JAJ +C7A7;C7A7;110C 1161 11BE;C7A7;110C 1161 11BE; # (잧; 잧; 잧; 잧; 잧; ) HANGUL SYLLABLE JAC +C7A8;C7A8;110C 1161 11BF;C7A8;110C 1161 11BF; # (잨; 잨; 잨; 잨; 잨; ) HANGUL SYLLABLE JAK +C7A9;C7A9;110C 1161 11C0;C7A9;110C 1161 11C0; # (잩; 잩; 잩; 잩; 잩; ) HANGUL SYLLABLE JAT +C7AA;C7AA;110C 1161 11C1;C7AA;110C 1161 11C1; # (잪; 잪; 잪; 잪; 잪; ) HANGUL SYLLABLE JAP +C7AB;C7AB;110C 1161 11C2;C7AB;110C 1161 11C2; # (잫; 잫; 잫; 잫; 잫; ) HANGUL SYLLABLE JAH +C7AC;C7AC;110C 1162;C7AC;110C 1162; # (재; 재; 재; 재; 재; ) HANGUL SYLLABLE JAE +C7AD;C7AD;110C 1162 11A8;C7AD;110C 1162 11A8; # (잭; 잭; 잭; 잭; 잭; ) HANGUL SYLLABLE JAEG +C7AE;C7AE;110C 1162 11A9;C7AE;110C 1162 11A9; # (잮; 잮; 잮; 잮; 잮; ) HANGUL SYLLABLE JAEGG +C7AF;C7AF;110C 1162 11AA;C7AF;110C 1162 11AA; # (잯; 잯; 잯; 잯; 잯; ) HANGUL SYLLABLE JAEGS +C7B0;C7B0;110C 1162 11AB;C7B0;110C 1162 11AB; # (잰; 잰; 잰; 잰; 잰; ) HANGUL SYLLABLE JAEN +C7B1;C7B1;110C 1162 11AC;C7B1;110C 1162 11AC; # (잱; 잱; 잱; 잱; 잱; ) HANGUL SYLLABLE JAENJ +C7B2;C7B2;110C 1162 11AD;C7B2;110C 1162 11AD; # (잲; 잲; 잲; 잲; 잲; ) HANGUL SYLLABLE JAENH +C7B3;C7B3;110C 1162 11AE;C7B3;110C 1162 11AE; # (잳; 잳; 잳; 잳; 잳; ) HANGUL SYLLABLE JAED +C7B4;C7B4;110C 1162 11AF;C7B4;110C 1162 11AF; # (잴; 잴; 잴; 잴; 잴; ) HANGUL SYLLABLE JAEL +C7B5;C7B5;110C 1162 11B0;C7B5;110C 1162 11B0; # (잵; 잵; 잵; 잵; 잵; ) HANGUL SYLLABLE JAELG +C7B6;C7B6;110C 1162 11B1;C7B6;110C 1162 11B1; # (잶; 잶; 잶; 잶; 잶; ) HANGUL SYLLABLE JAELM +C7B7;C7B7;110C 1162 11B2;C7B7;110C 1162 11B2; # (잷; 잷; 잷; 잷; 잷; ) HANGUL SYLLABLE JAELB +C7B8;C7B8;110C 1162 11B3;C7B8;110C 1162 11B3; # (잸; 잸; 잸; 잸; 잸; ) HANGUL SYLLABLE JAELS +C7B9;C7B9;110C 1162 11B4;C7B9;110C 1162 11B4; # (잹; 잹; 잹; 잹; 잹; ) HANGUL SYLLABLE JAELT +C7BA;C7BA;110C 1162 11B5;C7BA;110C 1162 11B5; # (잺; 잺; 잺; 잺; 잺; ) HANGUL SYLLABLE JAELP +C7BB;C7BB;110C 1162 11B6;C7BB;110C 1162 11B6; # (잻; 잻; 잻; 잻; 잻; ) HANGUL SYLLABLE JAELH +C7BC;C7BC;110C 1162 11B7;C7BC;110C 1162 11B7; # (잼; 잼; 잼; 잼; 잼; ) HANGUL SYLLABLE JAEM +C7BD;C7BD;110C 1162 11B8;C7BD;110C 1162 11B8; # (잽; 잽; 잽; 잽; 잽; ) HANGUL SYLLABLE JAEB +C7BE;C7BE;110C 1162 11B9;C7BE;110C 1162 11B9; # (잾; 잾; 잾; 잾; 잾; ) HANGUL SYLLABLE JAEBS +C7BF;C7BF;110C 1162 11BA;C7BF;110C 1162 11BA; # (잿; 잿; 잿; 잿; 잿; ) HANGUL SYLLABLE JAES +C7C0;C7C0;110C 1162 11BB;C7C0;110C 1162 11BB; # (쟀; 쟀; 쟀; 쟀; 쟀; ) HANGUL SYLLABLE JAESS +C7C1;C7C1;110C 1162 11BC;C7C1;110C 1162 11BC; # (쟁; 쟁; 쟁; 쟁; 쟁; ) HANGUL SYLLABLE JAENG +C7C2;C7C2;110C 1162 11BD;C7C2;110C 1162 11BD; # (쟂; 쟂; 쟂; 쟂; 쟂; ) HANGUL SYLLABLE JAEJ +C7C3;C7C3;110C 1162 11BE;C7C3;110C 1162 11BE; # (쟃; 쟃; 쟃; 쟃; 쟃; ) HANGUL SYLLABLE JAEC +C7C4;C7C4;110C 1162 11BF;C7C4;110C 1162 11BF; # (쟄; 쟄; 쟄; 쟄; 쟄; ) HANGUL SYLLABLE JAEK +C7C5;C7C5;110C 1162 11C0;C7C5;110C 1162 11C0; # (쟅; 쟅; 쟅; 쟅; 쟅; ) HANGUL SYLLABLE JAET +C7C6;C7C6;110C 1162 11C1;C7C6;110C 1162 11C1; # (쟆; 쟆; 쟆; 쟆; 쟆; ) HANGUL SYLLABLE JAEP +C7C7;C7C7;110C 1162 11C2;C7C7;110C 1162 11C2; # (쟇; 쟇; 쟇; 쟇; 쟇; ) HANGUL SYLLABLE JAEH +C7C8;C7C8;110C 1163;C7C8;110C 1163; # (쟈; 쟈; 쟈; 쟈; 쟈; ) HANGUL SYLLABLE JYA +C7C9;C7C9;110C 1163 11A8;C7C9;110C 1163 11A8; # (쟉; 쟉; 쟉; 쟉; 쟉; ) HANGUL SYLLABLE JYAG +C7CA;C7CA;110C 1163 11A9;C7CA;110C 1163 11A9; # (쟊; 쟊; 쟊; 쟊; 쟊; ) HANGUL SYLLABLE JYAGG +C7CB;C7CB;110C 1163 11AA;C7CB;110C 1163 11AA; # (쟋; 쟋; 쟋; 쟋; 쟋; ) HANGUL SYLLABLE JYAGS +C7CC;C7CC;110C 1163 11AB;C7CC;110C 1163 11AB; # (쟌; 쟌; 쟌; 쟌; 쟌; ) HANGUL SYLLABLE JYAN +C7CD;C7CD;110C 1163 11AC;C7CD;110C 1163 11AC; # (쟍; 쟍; 쟍; 쟍; 쟍; ) HANGUL SYLLABLE JYANJ +C7CE;C7CE;110C 1163 11AD;C7CE;110C 1163 11AD; # (쟎; 쟎; 쟎; 쟎; 쟎; ) HANGUL SYLLABLE JYANH +C7CF;C7CF;110C 1163 11AE;C7CF;110C 1163 11AE; # (쟏; 쟏; 쟏; 쟏; 쟏; ) HANGUL SYLLABLE JYAD +C7D0;C7D0;110C 1163 11AF;C7D0;110C 1163 11AF; # (쟐; 쟐; 쟐; 쟐; 쟐; ) HANGUL SYLLABLE JYAL +C7D1;C7D1;110C 1163 11B0;C7D1;110C 1163 11B0; # (쟑; 쟑; 쟑; 쟑; 쟑; ) HANGUL SYLLABLE JYALG +C7D2;C7D2;110C 1163 11B1;C7D2;110C 1163 11B1; # (쟒; 쟒; 쟒; 쟒; 쟒; ) HANGUL SYLLABLE JYALM +C7D3;C7D3;110C 1163 11B2;C7D3;110C 1163 11B2; # (쟓; 쟓; 쟓; 쟓; 쟓; ) HANGUL SYLLABLE JYALB +C7D4;C7D4;110C 1163 11B3;C7D4;110C 1163 11B3; # (쟔; 쟔; 쟔; 쟔; 쟔; ) HANGUL SYLLABLE JYALS +C7D5;C7D5;110C 1163 11B4;C7D5;110C 1163 11B4; # (쟕; 쟕; 쟕; 쟕; 쟕; ) HANGUL SYLLABLE JYALT +C7D6;C7D6;110C 1163 11B5;C7D6;110C 1163 11B5; # (쟖; 쟖; 쟖; 쟖; 쟖; ) HANGUL SYLLABLE JYALP +C7D7;C7D7;110C 1163 11B6;C7D7;110C 1163 11B6; # (쟗; 쟗; 쟗; 쟗; 쟗; ) HANGUL SYLLABLE JYALH +C7D8;C7D8;110C 1163 11B7;C7D8;110C 1163 11B7; # (쟘; 쟘; 쟘; 쟘; 쟘; ) HANGUL SYLLABLE JYAM +C7D9;C7D9;110C 1163 11B8;C7D9;110C 1163 11B8; # (쟙; 쟙; 쟙; 쟙; 쟙; ) HANGUL SYLLABLE JYAB +C7DA;C7DA;110C 1163 11B9;C7DA;110C 1163 11B9; # (쟚; 쟚; 쟚; 쟚; 쟚; ) HANGUL SYLLABLE JYABS +C7DB;C7DB;110C 1163 11BA;C7DB;110C 1163 11BA; # (쟛; 쟛; 쟛; 쟛; 쟛; ) HANGUL SYLLABLE JYAS +C7DC;C7DC;110C 1163 11BB;C7DC;110C 1163 11BB; # (쟜; 쟜; 쟜; 쟜; 쟜; ) HANGUL SYLLABLE JYASS +C7DD;C7DD;110C 1163 11BC;C7DD;110C 1163 11BC; # (쟝; 쟝; 쟝; 쟝; 쟝; ) HANGUL SYLLABLE JYANG +C7DE;C7DE;110C 1163 11BD;C7DE;110C 1163 11BD; # (쟞; 쟞; 쟞; 쟞; 쟞; ) HANGUL SYLLABLE JYAJ +C7DF;C7DF;110C 1163 11BE;C7DF;110C 1163 11BE; # (쟟; 쟟; 쟟; 쟟; 쟟; ) HANGUL SYLLABLE JYAC +C7E0;C7E0;110C 1163 11BF;C7E0;110C 1163 11BF; # (쟠; 쟠; 쟠; 쟠; 쟠; ) HANGUL SYLLABLE JYAK +C7E1;C7E1;110C 1163 11C0;C7E1;110C 1163 11C0; # (쟡; 쟡; 쟡; 쟡; 쟡; ) HANGUL SYLLABLE JYAT +C7E2;C7E2;110C 1163 11C1;C7E2;110C 1163 11C1; # (쟢; 쟢; 쟢; 쟢; 쟢; ) HANGUL SYLLABLE JYAP +C7E3;C7E3;110C 1163 11C2;C7E3;110C 1163 11C2; # (쟣; 쟣; 쟣; 쟣; 쟣; ) HANGUL SYLLABLE JYAH +C7E4;C7E4;110C 1164;C7E4;110C 1164; # (쟤; 쟤; 쟤; 쟤; 쟤; ) HANGUL SYLLABLE JYAE +C7E5;C7E5;110C 1164 11A8;C7E5;110C 1164 11A8; # (쟥; 쟥; 쟥; 쟥; 쟥; ) HANGUL SYLLABLE JYAEG +C7E6;C7E6;110C 1164 11A9;C7E6;110C 1164 11A9; # (쟦; 쟦; 쟦; 쟦; 쟦; ) HANGUL SYLLABLE JYAEGG +C7E7;C7E7;110C 1164 11AA;C7E7;110C 1164 11AA; # (쟧; 쟧; 쟧; 쟧; 쟧; ) HANGUL SYLLABLE JYAEGS +C7E8;C7E8;110C 1164 11AB;C7E8;110C 1164 11AB; # (쟨; 쟨; 쟨; 쟨; 쟨; ) HANGUL SYLLABLE JYAEN +C7E9;C7E9;110C 1164 11AC;C7E9;110C 1164 11AC; # (쟩; 쟩; 쟩; 쟩; 쟩; ) HANGUL SYLLABLE JYAENJ +C7EA;C7EA;110C 1164 11AD;C7EA;110C 1164 11AD; # (쟪; 쟪; 쟪; 쟪; 쟪; ) HANGUL SYLLABLE JYAENH +C7EB;C7EB;110C 1164 11AE;C7EB;110C 1164 11AE; # (쟫; 쟫; 쟫; 쟫; 쟫; ) HANGUL SYLLABLE JYAED +C7EC;C7EC;110C 1164 11AF;C7EC;110C 1164 11AF; # (쟬; 쟬; 쟬; 쟬; 쟬; ) HANGUL SYLLABLE JYAEL +C7ED;C7ED;110C 1164 11B0;C7ED;110C 1164 11B0; # (쟭; 쟭; 쟭; 쟭; 쟭; ) HANGUL SYLLABLE JYAELG +C7EE;C7EE;110C 1164 11B1;C7EE;110C 1164 11B1; # (쟮; 쟮; 쟮; 쟮; 쟮; ) HANGUL SYLLABLE JYAELM +C7EF;C7EF;110C 1164 11B2;C7EF;110C 1164 11B2; # (쟯; 쟯; 쟯; 쟯; 쟯; ) HANGUL SYLLABLE JYAELB +C7F0;C7F0;110C 1164 11B3;C7F0;110C 1164 11B3; # (쟰; 쟰; 쟰; 쟰; 쟰; ) HANGUL SYLLABLE JYAELS +C7F1;C7F1;110C 1164 11B4;C7F1;110C 1164 11B4; # (쟱; 쟱; 쟱; 쟱; 쟱; ) HANGUL SYLLABLE JYAELT +C7F2;C7F2;110C 1164 11B5;C7F2;110C 1164 11B5; # (쟲; 쟲; 쟲; 쟲; 쟲; ) HANGUL SYLLABLE JYAELP +C7F3;C7F3;110C 1164 11B6;C7F3;110C 1164 11B6; # (쟳; 쟳; 쟳; 쟳; 쟳; ) HANGUL SYLLABLE JYAELH +C7F4;C7F4;110C 1164 11B7;C7F4;110C 1164 11B7; # (쟴; 쟴; 쟴; 쟴; 쟴; ) HANGUL SYLLABLE JYAEM +C7F5;C7F5;110C 1164 11B8;C7F5;110C 1164 11B8; # (쟵; 쟵; 쟵; 쟵; 쟵; ) HANGUL SYLLABLE JYAEB +C7F6;C7F6;110C 1164 11B9;C7F6;110C 1164 11B9; # (쟶; 쟶; 쟶; 쟶; 쟶; ) HANGUL SYLLABLE JYAEBS +C7F7;C7F7;110C 1164 11BA;C7F7;110C 1164 11BA; # (쟷; 쟷; 쟷; 쟷; 쟷; ) HANGUL SYLLABLE JYAES +C7F8;C7F8;110C 1164 11BB;C7F8;110C 1164 11BB; # (쟸; 쟸; 쟸; 쟸; 쟸; ) HANGUL SYLLABLE JYAESS +C7F9;C7F9;110C 1164 11BC;C7F9;110C 1164 11BC; # (쟹; 쟹; 쟹; 쟹; 쟹; ) HANGUL SYLLABLE JYAENG +C7FA;C7FA;110C 1164 11BD;C7FA;110C 1164 11BD; # (쟺; 쟺; 쟺; 쟺; 쟺; ) HANGUL SYLLABLE JYAEJ +C7FB;C7FB;110C 1164 11BE;C7FB;110C 1164 11BE; # (쟻; 쟻; 쟻; 쟻; 쟻; ) HANGUL SYLLABLE JYAEC +C7FC;C7FC;110C 1164 11BF;C7FC;110C 1164 11BF; # (쟼; 쟼; 쟼; 쟼; 쟼; ) HANGUL SYLLABLE JYAEK +C7FD;C7FD;110C 1164 11C0;C7FD;110C 1164 11C0; # (쟽; 쟽; 쟽; 쟽; 쟽; ) HANGUL SYLLABLE JYAET +C7FE;C7FE;110C 1164 11C1;C7FE;110C 1164 11C1; # (쟾; 쟾; 쟾; 쟾; 쟾; ) HANGUL SYLLABLE JYAEP +C7FF;C7FF;110C 1164 11C2;C7FF;110C 1164 11C2; # (쟿; 쟿; 쟿; 쟿; 쟿; ) HANGUL SYLLABLE JYAEH +C800;C800;110C 1165;C800;110C 1165; # (저; 저; 저; 저; 저; ) HANGUL SYLLABLE JEO +C801;C801;110C 1165 11A8;C801;110C 1165 11A8; # (적; 적; 적; 적; 적; ) HANGUL SYLLABLE JEOG +C802;C802;110C 1165 11A9;C802;110C 1165 11A9; # (젂; 젂; 젂; 젂; 젂; ) HANGUL SYLLABLE JEOGG +C803;C803;110C 1165 11AA;C803;110C 1165 11AA; # (젃; 젃; 젃; 젃; 젃; ) HANGUL SYLLABLE JEOGS +C804;C804;110C 1165 11AB;C804;110C 1165 11AB; # (전; 전; 전; 전; 전; ) HANGUL SYLLABLE JEON +C805;C805;110C 1165 11AC;C805;110C 1165 11AC; # (젅; 젅; 젅; 젅; 젅; ) HANGUL SYLLABLE JEONJ +C806;C806;110C 1165 11AD;C806;110C 1165 11AD; # (젆; 젆; 젆; 젆; 젆; ) HANGUL SYLLABLE JEONH +C807;C807;110C 1165 11AE;C807;110C 1165 11AE; # (젇; 젇; 젇; 젇; 젇; ) HANGUL SYLLABLE JEOD +C808;C808;110C 1165 11AF;C808;110C 1165 11AF; # (절; 절; 절; 절; 절; ) HANGUL SYLLABLE JEOL +C809;C809;110C 1165 11B0;C809;110C 1165 11B0; # (젉; 젉; 젉; 젉; 젉; ) HANGUL SYLLABLE JEOLG +C80A;C80A;110C 1165 11B1;C80A;110C 1165 11B1; # (젊; 젊; 젊; 젊; 젊; ) HANGUL SYLLABLE JEOLM +C80B;C80B;110C 1165 11B2;C80B;110C 1165 11B2; # (젋; 젋; 젋; 젋; 젋; ) HANGUL SYLLABLE JEOLB +C80C;C80C;110C 1165 11B3;C80C;110C 1165 11B3; # (젌; 젌; 젌; 젌; 젌; ) HANGUL SYLLABLE JEOLS +C80D;C80D;110C 1165 11B4;C80D;110C 1165 11B4; # (젍; 젍; 젍; 젍; 젍; ) HANGUL SYLLABLE JEOLT +C80E;C80E;110C 1165 11B5;C80E;110C 1165 11B5; # (젎; 젎; 젎; 젎; 젎; ) HANGUL SYLLABLE JEOLP +C80F;C80F;110C 1165 11B6;C80F;110C 1165 11B6; # (젏; 젏; 젏; 젏; 젏; ) HANGUL SYLLABLE JEOLH +C810;C810;110C 1165 11B7;C810;110C 1165 11B7; # (점; 점; 점; 점; 점; ) HANGUL SYLLABLE JEOM +C811;C811;110C 1165 11B8;C811;110C 1165 11B8; # (접; 접; 접; 접; 접; ) HANGUL SYLLABLE JEOB +C812;C812;110C 1165 11B9;C812;110C 1165 11B9; # (젒; 젒; 젒; 젒; 젒; ) HANGUL SYLLABLE JEOBS +C813;C813;110C 1165 11BA;C813;110C 1165 11BA; # (젓; 젓; 젓; 젓; 젓; ) HANGUL SYLLABLE JEOS +C814;C814;110C 1165 11BB;C814;110C 1165 11BB; # (젔; 젔; 젔; 젔; 젔; ) HANGUL SYLLABLE JEOSS +C815;C815;110C 1165 11BC;C815;110C 1165 11BC; # (정; 정; 정; 정; 정; ) HANGUL SYLLABLE JEONG +C816;C816;110C 1165 11BD;C816;110C 1165 11BD; # (젖; 젖; 젖; 젖; 젖; ) HANGUL SYLLABLE JEOJ +C817;C817;110C 1165 11BE;C817;110C 1165 11BE; # (젗; 젗; 젗; 젗; 젗; ) HANGUL SYLLABLE JEOC +C818;C818;110C 1165 11BF;C818;110C 1165 11BF; # (젘; 젘; 젘; 젘; 젘; ) HANGUL SYLLABLE JEOK +C819;C819;110C 1165 11C0;C819;110C 1165 11C0; # (젙; 젙; 젙; 젙; 젙; ) HANGUL SYLLABLE JEOT +C81A;C81A;110C 1165 11C1;C81A;110C 1165 11C1; # (젚; 젚; 젚; 젚; 젚; ) HANGUL SYLLABLE JEOP +C81B;C81B;110C 1165 11C2;C81B;110C 1165 11C2; # (젛; 젛; 젛; 젛; 젛; ) HANGUL SYLLABLE JEOH +C81C;C81C;110C 1166;C81C;110C 1166; # (제; 제; 제; 제; 제; ) HANGUL SYLLABLE JE +C81D;C81D;110C 1166 11A8;C81D;110C 1166 11A8; # (젝; 젝; 젝; 젝; 젝; ) HANGUL SYLLABLE JEG +C81E;C81E;110C 1166 11A9;C81E;110C 1166 11A9; # (젞; 젞; 젞; 젞; 젞; ) HANGUL SYLLABLE JEGG +C81F;C81F;110C 1166 11AA;C81F;110C 1166 11AA; # (젟; 젟; 젟; 젟; 젟; ) HANGUL SYLLABLE JEGS +C820;C820;110C 1166 11AB;C820;110C 1166 11AB; # (젠; 젠; 젠; 젠; 젠; ) HANGUL SYLLABLE JEN +C821;C821;110C 1166 11AC;C821;110C 1166 11AC; # (젡; 젡; 젡; 젡; 젡; ) HANGUL SYLLABLE JENJ +C822;C822;110C 1166 11AD;C822;110C 1166 11AD; # (젢; 젢; 젢; 젢; 젢; ) HANGUL SYLLABLE JENH +C823;C823;110C 1166 11AE;C823;110C 1166 11AE; # (젣; 젣; 젣; 젣; 젣; ) HANGUL SYLLABLE JED +C824;C824;110C 1166 11AF;C824;110C 1166 11AF; # (젤; 젤; 젤; 젤; 젤; ) HANGUL SYLLABLE JEL +C825;C825;110C 1166 11B0;C825;110C 1166 11B0; # (젥; 젥; 젥; 젥; 젥; ) HANGUL SYLLABLE JELG +C826;C826;110C 1166 11B1;C826;110C 1166 11B1; # (젦; 젦; 젦; 젦; 젦; ) HANGUL SYLLABLE JELM +C827;C827;110C 1166 11B2;C827;110C 1166 11B2; # (젧; 젧; 젧; 젧; 젧; ) HANGUL SYLLABLE JELB +C828;C828;110C 1166 11B3;C828;110C 1166 11B3; # (젨; 젨; 젨; 젨; 젨; ) HANGUL SYLLABLE JELS +C829;C829;110C 1166 11B4;C829;110C 1166 11B4; # (젩; 젩; 젩; 젩; 젩; ) HANGUL SYLLABLE JELT +C82A;C82A;110C 1166 11B5;C82A;110C 1166 11B5; # (젪; 젪; 젪; 젪; 젪; ) HANGUL SYLLABLE JELP +C82B;C82B;110C 1166 11B6;C82B;110C 1166 11B6; # (젫; 젫; 젫; 젫; 젫; ) HANGUL SYLLABLE JELH +C82C;C82C;110C 1166 11B7;C82C;110C 1166 11B7; # (젬; 젬; 젬; 젬; 젬; ) HANGUL SYLLABLE JEM +C82D;C82D;110C 1166 11B8;C82D;110C 1166 11B8; # (젭; 젭; 젭; 젭; 젭; ) HANGUL SYLLABLE JEB +C82E;C82E;110C 1166 11B9;C82E;110C 1166 11B9; # (젮; 젮; 젮; 젮; 젮; ) HANGUL SYLLABLE JEBS +C82F;C82F;110C 1166 11BA;C82F;110C 1166 11BA; # (젯; 젯; 젯; 젯; 젯; ) HANGUL SYLLABLE JES +C830;C830;110C 1166 11BB;C830;110C 1166 11BB; # (젰; 젰; 젰; 젰; 젰; ) HANGUL SYLLABLE JESS +C831;C831;110C 1166 11BC;C831;110C 1166 11BC; # (젱; 젱; 젱; 젱; 젱; ) HANGUL SYLLABLE JENG +C832;C832;110C 1166 11BD;C832;110C 1166 11BD; # (젲; 젲; 젲; 젲; 젲; ) HANGUL SYLLABLE JEJ +C833;C833;110C 1166 11BE;C833;110C 1166 11BE; # (젳; 젳; 젳; 젳; 젳; ) HANGUL SYLLABLE JEC +C834;C834;110C 1166 11BF;C834;110C 1166 11BF; # (젴; 젴; 젴; 젴; 젴; ) HANGUL SYLLABLE JEK +C835;C835;110C 1166 11C0;C835;110C 1166 11C0; # (젵; 젵; 젵; 젵; 젵; ) HANGUL SYLLABLE JET +C836;C836;110C 1166 11C1;C836;110C 1166 11C1; # (젶; 젶; 젶; 젶; 젶; ) HANGUL SYLLABLE JEP +C837;C837;110C 1166 11C2;C837;110C 1166 11C2; # (젷; 젷; 젷; 젷; 젷; ) HANGUL SYLLABLE JEH +C838;C838;110C 1167;C838;110C 1167; # (져; 져; 져; 져; 져; ) HANGUL SYLLABLE JYEO +C839;C839;110C 1167 11A8;C839;110C 1167 11A8; # (젹; 젹; 젹; 젹; 젹; ) HANGUL SYLLABLE JYEOG +C83A;C83A;110C 1167 11A9;C83A;110C 1167 11A9; # (젺; 젺; 젺; 젺; 젺; ) HANGUL SYLLABLE JYEOGG +C83B;C83B;110C 1167 11AA;C83B;110C 1167 11AA; # (젻; 젻; 젻; 젻; 젻; ) HANGUL SYLLABLE JYEOGS +C83C;C83C;110C 1167 11AB;C83C;110C 1167 11AB; # (젼; 젼; 젼; 젼; 젼; ) HANGUL SYLLABLE JYEON +C83D;C83D;110C 1167 11AC;C83D;110C 1167 11AC; # (젽; 젽; 젽; 젽; 젽; ) HANGUL SYLLABLE JYEONJ +C83E;C83E;110C 1167 11AD;C83E;110C 1167 11AD; # (젾; 젾; 젾; 젾; 젾; ) HANGUL SYLLABLE JYEONH +C83F;C83F;110C 1167 11AE;C83F;110C 1167 11AE; # (젿; 젿; 젿; 젿; 젿; ) HANGUL SYLLABLE JYEOD +C840;C840;110C 1167 11AF;C840;110C 1167 11AF; # (졀; 졀; 졀; 졀; 졀; ) HANGUL SYLLABLE JYEOL +C841;C841;110C 1167 11B0;C841;110C 1167 11B0; # (졁; 졁; 졁; 졁; 졁; ) HANGUL SYLLABLE JYEOLG +C842;C842;110C 1167 11B1;C842;110C 1167 11B1; # (졂; 졂; 졂; 졂; 졂; ) HANGUL SYLLABLE JYEOLM +C843;C843;110C 1167 11B2;C843;110C 1167 11B2; # (졃; 졃; 졃; 졃; 졃; ) HANGUL SYLLABLE JYEOLB +C844;C844;110C 1167 11B3;C844;110C 1167 11B3; # (졄; 졄; 졄; 졄; 졄; ) HANGUL SYLLABLE JYEOLS +C845;C845;110C 1167 11B4;C845;110C 1167 11B4; # (졅; 졅; 졅; 졅; 졅; ) HANGUL SYLLABLE JYEOLT +C846;C846;110C 1167 11B5;C846;110C 1167 11B5; # (졆; 졆; 졆; 졆; 졆; ) HANGUL SYLLABLE JYEOLP +C847;C847;110C 1167 11B6;C847;110C 1167 11B6; # (졇; 졇; 졇; 졇; 졇; ) HANGUL SYLLABLE JYEOLH +C848;C848;110C 1167 11B7;C848;110C 1167 11B7; # (졈; 졈; 졈; 졈; 졈; ) HANGUL SYLLABLE JYEOM +C849;C849;110C 1167 11B8;C849;110C 1167 11B8; # (졉; 졉; 졉; 졉; 졉; ) HANGUL SYLLABLE JYEOB +C84A;C84A;110C 1167 11B9;C84A;110C 1167 11B9; # (졊; 졊; 졊; 졊; 졊; ) HANGUL SYLLABLE JYEOBS +C84B;C84B;110C 1167 11BA;C84B;110C 1167 11BA; # (졋; 졋; 졋; 졋; 졋; ) HANGUL SYLLABLE JYEOS +C84C;C84C;110C 1167 11BB;C84C;110C 1167 11BB; # (졌; 졌; 졌; 졌; 졌; ) HANGUL SYLLABLE JYEOSS +C84D;C84D;110C 1167 11BC;C84D;110C 1167 11BC; # (졍; 졍; 졍; 졍; 졍; ) HANGUL SYLLABLE JYEONG +C84E;C84E;110C 1167 11BD;C84E;110C 1167 11BD; # (졎; 졎; 졎; 졎; 졎; ) HANGUL SYLLABLE JYEOJ +C84F;C84F;110C 1167 11BE;C84F;110C 1167 11BE; # (졏; 졏; 졏; 졏; 졏; ) HANGUL SYLLABLE JYEOC +C850;C850;110C 1167 11BF;C850;110C 1167 11BF; # (졐; 졐; 졐; 졐; 졐; ) HANGUL SYLLABLE JYEOK +C851;C851;110C 1167 11C0;C851;110C 1167 11C0; # (졑; 졑; 졑; 졑; 졑; ) HANGUL SYLLABLE JYEOT +C852;C852;110C 1167 11C1;C852;110C 1167 11C1; # (졒; 졒; 졒; 졒; 졒; ) HANGUL SYLLABLE JYEOP +C853;C853;110C 1167 11C2;C853;110C 1167 11C2; # (졓; 졓; 졓; 졓; 졓; ) HANGUL SYLLABLE JYEOH +C854;C854;110C 1168;C854;110C 1168; # (졔; 졔; 졔; 졔; 졔; ) HANGUL SYLLABLE JYE +C855;C855;110C 1168 11A8;C855;110C 1168 11A8; # (졕; 졕; 졕; 졕; 졕; ) HANGUL SYLLABLE JYEG +C856;C856;110C 1168 11A9;C856;110C 1168 11A9; # (졖; 졖; 졖; 졖; 졖; ) HANGUL SYLLABLE JYEGG +C857;C857;110C 1168 11AA;C857;110C 1168 11AA; # (졗; 졗; 졗; 졗; 졗; ) HANGUL SYLLABLE JYEGS +C858;C858;110C 1168 11AB;C858;110C 1168 11AB; # (졘; 졘; 졘; 졘; 졘; ) HANGUL SYLLABLE JYEN +C859;C859;110C 1168 11AC;C859;110C 1168 11AC; # (졙; 졙; 졙; 졙; 졙; ) HANGUL SYLLABLE JYENJ +C85A;C85A;110C 1168 11AD;C85A;110C 1168 11AD; # (졚; 졚; 졚; 졚; 졚; ) HANGUL SYLLABLE JYENH +C85B;C85B;110C 1168 11AE;C85B;110C 1168 11AE; # (졛; 졛; 졛; 졛; 졛; ) HANGUL SYLLABLE JYED +C85C;C85C;110C 1168 11AF;C85C;110C 1168 11AF; # (졜; 졜; 졜; 졜; 졜; ) HANGUL SYLLABLE JYEL +C85D;C85D;110C 1168 11B0;C85D;110C 1168 11B0; # (졝; 졝; 졝; 졝; 졝; ) HANGUL SYLLABLE JYELG +C85E;C85E;110C 1168 11B1;C85E;110C 1168 11B1; # (졞; 졞; 졞; 졞; 졞; ) HANGUL SYLLABLE JYELM +C85F;C85F;110C 1168 11B2;C85F;110C 1168 11B2; # (졟; 졟; 졟; 졟; 졟; ) HANGUL SYLLABLE JYELB +C860;C860;110C 1168 11B3;C860;110C 1168 11B3; # (졠; 졠; 졠; 졠; 졠; ) HANGUL SYLLABLE JYELS +C861;C861;110C 1168 11B4;C861;110C 1168 11B4; # (졡; 졡; 졡; 졡; 졡; ) HANGUL SYLLABLE JYELT +C862;C862;110C 1168 11B5;C862;110C 1168 11B5; # (졢; 졢; 졢; 졢; 졢; ) HANGUL SYLLABLE JYELP +C863;C863;110C 1168 11B6;C863;110C 1168 11B6; # (졣; 졣; 졣; 졣; 졣; ) HANGUL SYLLABLE JYELH +C864;C864;110C 1168 11B7;C864;110C 1168 11B7; # (졤; 졤; 졤; 졤; 졤; ) HANGUL SYLLABLE JYEM +C865;C865;110C 1168 11B8;C865;110C 1168 11B8; # (졥; 졥; 졥; 졥; 졥; ) HANGUL SYLLABLE JYEB +C866;C866;110C 1168 11B9;C866;110C 1168 11B9; # (졦; 졦; 졦; 졦; 졦; ) HANGUL SYLLABLE JYEBS +C867;C867;110C 1168 11BA;C867;110C 1168 11BA; # (졧; 졧; 졧; 졧; 졧; ) HANGUL SYLLABLE JYES +C868;C868;110C 1168 11BB;C868;110C 1168 11BB; # (졨; 졨; 졨; 졨; 졨; ) HANGUL SYLLABLE JYESS +C869;C869;110C 1168 11BC;C869;110C 1168 11BC; # (졩; 졩; 졩; 졩; 졩; ) HANGUL SYLLABLE JYENG +C86A;C86A;110C 1168 11BD;C86A;110C 1168 11BD; # (졪; 졪; 졪; 졪; 졪; ) HANGUL SYLLABLE JYEJ +C86B;C86B;110C 1168 11BE;C86B;110C 1168 11BE; # (졫; 졫; 졫; 졫; 졫; ) HANGUL SYLLABLE JYEC +C86C;C86C;110C 1168 11BF;C86C;110C 1168 11BF; # (졬; 졬; 졬; 졬; 졬; ) HANGUL SYLLABLE JYEK +C86D;C86D;110C 1168 11C0;C86D;110C 1168 11C0; # (졭; 졭; 졭; 졭; 졭; ) HANGUL SYLLABLE JYET +C86E;C86E;110C 1168 11C1;C86E;110C 1168 11C1; # (졮; 졮; 졮; 졮; 졮; ) HANGUL SYLLABLE JYEP +C86F;C86F;110C 1168 11C2;C86F;110C 1168 11C2; # (졯; 졯; 졯; 졯; 졯; ) HANGUL SYLLABLE JYEH +C870;C870;110C 1169;C870;110C 1169; # (조; 조; 조; 조; 조; ) HANGUL SYLLABLE JO +C871;C871;110C 1169 11A8;C871;110C 1169 11A8; # (족; 족; 족; 족; 족; ) HANGUL SYLLABLE JOG +C872;C872;110C 1169 11A9;C872;110C 1169 11A9; # (졲; 졲; 졲; 졲; 졲; ) HANGUL SYLLABLE JOGG +C873;C873;110C 1169 11AA;C873;110C 1169 11AA; # (졳; 졳; 졳; 졳; 졳; ) HANGUL SYLLABLE JOGS +C874;C874;110C 1169 11AB;C874;110C 1169 11AB; # (존; 존; 존; 존; 존; ) HANGUL SYLLABLE JON +C875;C875;110C 1169 11AC;C875;110C 1169 11AC; # (졵; 졵; 졵; 졵; 졵; ) HANGUL SYLLABLE JONJ +C876;C876;110C 1169 11AD;C876;110C 1169 11AD; # (졶; 졶; 졶; 졶; 졶; ) HANGUL SYLLABLE JONH +C877;C877;110C 1169 11AE;C877;110C 1169 11AE; # (졷; 졷; 졷; 졷; 졷; ) HANGUL SYLLABLE JOD +C878;C878;110C 1169 11AF;C878;110C 1169 11AF; # (졸; 졸; 졸; 졸; 졸; ) HANGUL SYLLABLE JOL +C879;C879;110C 1169 11B0;C879;110C 1169 11B0; # (졹; 졹; 졹; 졹; 졹; ) HANGUL SYLLABLE JOLG +C87A;C87A;110C 1169 11B1;C87A;110C 1169 11B1; # (졺; 졺; 졺; 졺; 졺; ) HANGUL SYLLABLE JOLM +C87B;C87B;110C 1169 11B2;C87B;110C 1169 11B2; # (졻; 졻; 졻; 졻; 졻; ) HANGUL SYLLABLE JOLB +C87C;C87C;110C 1169 11B3;C87C;110C 1169 11B3; # (졼; 졼; 졼; 졼; 졼; ) HANGUL SYLLABLE JOLS +C87D;C87D;110C 1169 11B4;C87D;110C 1169 11B4; # (졽; 졽; 졽; 졽; 졽; ) HANGUL SYLLABLE JOLT +C87E;C87E;110C 1169 11B5;C87E;110C 1169 11B5; # (졾; 졾; 졾; 졾; 졾; ) HANGUL SYLLABLE JOLP +C87F;C87F;110C 1169 11B6;C87F;110C 1169 11B6; # (졿; 졿; 졿; 졿; 졿; ) HANGUL SYLLABLE JOLH +C880;C880;110C 1169 11B7;C880;110C 1169 11B7; # (좀; 좀; 좀; 좀; 좀; ) HANGUL SYLLABLE JOM +C881;C881;110C 1169 11B8;C881;110C 1169 11B8; # (좁; 좁; 좁; 좁; 좁; ) HANGUL SYLLABLE JOB +C882;C882;110C 1169 11B9;C882;110C 1169 11B9; # (좂; 좂; 좂; 좂; 좂; ) HANGUL SYLLABLE JOBS +C883;C883;110C 1169 11BA;C883;110C 1169 11BA; # (좃; 좃; 좃; 좃; 좃; ) HANGUL SYLLABLE JOS +C884;C884;110C 1169 11BB;C884;110C 1169 11BB; # (좄; 좄; 좄; 좄; 좄; ) HANGUL SYLLABLE JOSS +C885;C885;110C 1169 11BC;C885;110C 1169 11BC; # (종; 종; 종; 종; 종; ) HANGUL SYLLABLE JONG +C886;C886;110C 1169 11BD;C886;110C 1169 11BD; # (좆; 좆; 좆; 좆; 좆; ) HANGUL SYLLABLE JOJ +C887;C887;110C 1169 11BE;C887;110C 1169 11BE; # (좇; 좇; 좇; 좇; 좇; ) HANGUL SYLLABLE JOC +C888;C888;110C 1169 11BF;C888;110C 1169 11BF; # (좈; 좈; 좈; 좈; 좈; ) HANGUL SYLLABLE JOK +C889;C889;110C 1169 11C0;C889;110C 1169 11C0; # (좉; 좉; 좉; 좉; 좉; ) HANGUL SYLLABLE JOT +C88A;C88A;110C 1169 11C1;C88A;110C 1169 11C1; # (좊; 좊; 좊; 좊; 좊; ) HANGUL SYLLABLE JOP +C88B;C88B;110C 1169 11C2;C88B;110C 1169 11C2; # (좋; 좋; 좋; 좋; 좋; ) HANGUL SYLLABLE JOH +C88C;C88C;110C 116A;C88C;110C 116A; # (좌; 좌; 좌; 좌; 좌; ) HANGUL SYLLABLE JWA +C88D;C88D;110C 116A 11A8;C88D;110C 116A 11A8; # (좍; 좍; 좍; 좍; 좍; ) HANGUL SYLLABLE JWAG +C88E;C88E;110C 116A 11A9;C88E;110C 116A 11A9; # (좎; 좎; 좎; 좎; 좎; ) HANGUL SYLLABLE JWAGG +C88F;C88F;110C 116A 11AA;C88F;110C 116A 11AA; # (좏; 좏; 좏; 좏; 좏; ) HANGUL SYLLABLE JWAGS +C890;C890;110C 116A 11AB;C890;110C 116A 11AB; # (좐; 좐; 좐; 좐; 좐; ) HANGUL SYLLABLE JWAN +C891;C891;110C 116A 11AC;C891;110C 116A 11AC; # (좑; 좑; 좑; 좑; 좑; ) HANGUL SYLLABLE JWANJ +C892;C892;110C 116A 11AD;C892;110C 116A 11AD; # (좒; 좒; 좒; 좒; 좒; ) HANGUL SYLLABLE JWANH +C893;C893;110C 116A 11AE;C893;110C 116A 11AE; # (좓; 좓; 좓; 좓; 좓; ) HANGUL SYLLABLE JWAD +C894;C894;110C 116A 11AF;C894;110C 116A 11AF; # (좔; 좔; 좔; 좔; 좔; ) HANGUL SYLLABLE JWAL +C895;C895;110C 116A 11B0;C895;110C 116A 11B0; # (좕; 좕; 좕; 좕; 좕; ) HANGUL SYLLABLE JWALG +C896;C896;110C 116A 11B1;C896;110C 116A 11B1; # (좖; 좖; 좖; 좖; 좖; ) HANGUL SYLLABLE JWALM +C897;C897;110C 116A 11B2;C897;110C 116A 11B2; # (좗; 좗; 좗; 좗; 좗; ) HANGUL SYLLABLE JWALB +C898;C898;110C 116A 11B3;C898;110C 116A 11B3; # (좘; 좘; 좘; 좘; 좘; ) HANGUL SYLLABLE JWALS +C899;C899;110C 116A 11B4;C899;110C 116A 11B4; # (좙; 좙; 좙; 좙; 좙; ) HANGUL SYLLABLE JWALT +C89A;C89A;110C 116A 11B5;C89A;110C 116A 11B5; # (좚; 좚; 좚; 좚; 좚; ) HANGUL SYLLABLE JWALP +C89B;C89B;110C 116A 11B6;C89B;110C 116A 11B6; # (좛; 좛; 좛; 좛; 좛; ) HANGUL SYLLABLE JWALH +C89C;C89C;110C 116A 11B7;C89C;110C 116A 11B7; # (좜; 좜; 좜; 좜; 좜; ) HANGUL SYLLABLE JWAM +C89D;C89D;110C 116A 11B8;C89D;110C 116A 11B8; # (좝; 좝; 좝; 좝; 좝; ) HANGUL SYLLABLE JWAB +C89E;C89E;110C 116A 11B9;C89E;110C 116A 11B9; # (좞; 좞; 좞; 좞; 좞; ) HANGUL SYLLABLE JWABS +C89F;C89F;110C 116A 11BA;C89F;110C 116A 11BA; # (좟; 좟; 좟; 좟; 좟; ) HANGUL SYLLABLE JWAS +C8A0;C8A0;110C 116A 11BB;C8A0;110C 116A 11BB; # (좠; 좠; 좠; 좠; 좠; ) HANGUL SYLLABLE JWASS +C8A1;C8A1;110C 116A 11BC;C8A1;110C 116A 11BC; # (좡; 좡; 좡; 좡; 좡; ) HANGUL SYLLABLE JWANG +C8A2;C8A2;110C 116A 11BD;C8A2;110C 116A 11BD; # (좢; 좢; 좢; 좢; 좢; ) HANGUL SYLLABLE JWAJ +C8A3;C8A3;110C 116A 11BE;C8A3;110C 116A 11BE; # (좣; 좣; 좣; 좣; 좣; ) HANGUL SYLLABLE JWAC +C8A4;C8A4;110C 116A 11BF;C8A4;110C 116A 11BF; # (좤; 좤; 좤; 좤; 좤; ) HANGUL SYLLABLE JWAK +C8A5;C8A5;110C 116A 11C0;C8A5;110C 116A 11C0; # (좥; 좥; 좥; 좥; 좥; ) HANGUL SYLLABLE JWAT +C8A6;C8A6;110C 116A 11C1;C8A6;110C 116A 11C1; # (좦; 좦; 좦; 좦; 좦; ) HANGUL SYLLABLE JWAP +C8A7;C8A7;110C 116A 11C2;C8A7;110C 116A 11C2; # (좧; 좧; 좧; 좧; 좧; ) HANGUL SYLLABLE JWAH +C8A8;C8A8;110C 116B;C8A8;110C 116B; # (좨; 좨; 좨; 좨; 좨; ) HANGUL SYLLABLE JWAE +C8A9;C8A9;110C 116B 11A8;C8A9;110C 116B 11A8; # (좩; 좩; 좩; 좩; 좩; ) HANGUL SYLLABLE JWAEG +C8AA;C8AA;110C 116B 11A9;C8AA;110C 116B 11A9; # (좪; 좪; 좪; 좪; 좪; ) HANGUL SYLLABLE JWAEGG +C8AB;C8AB;110C 116B 11AA;C8AB;110C 116B 11AA; # (좫; 좫; 좫; 좫; 좫; ) HANGUL SYLLABLE JWAEGS +C8AC;C8AC;110C 116B 11AB;C8AC;110C 116B 11AB; # (좬; 좬; 좬; 좬; 좬; ) HANGUL SYLLABLE JWAEN +C8AD;C8AD;110C 116B 11AC;C8AD;110C 116B 11AC; # (좭; 좭; 좭; 좭; 좭; ) HANGUL SYLLABLE JWAENJ +C8AE;C8AE;110C 116B 11AD;C8AE;110C 116B 11AD; # (좮; 좮; 좮; 좮; 좮; ) HANGUL SYLLABLE JWAENH +C8AF;C8AF;110C 116B 11AE;C8AF;110C 116B 11AE; # (좯; 좯; 좯; 좯; 좯; ) HANGUL SYLLABLE JWAED +C8B0;C8B0;110C 116B 11AF;C8B0;110C 116B 11AF; # (좰; 좰; 좰; 좰; 좰; ) HANGUL SYLLABLE JWAEL +C8B1;C8B1;110C 116B 11B0;C8B1;110C 116B 11B0; # (좱; 좱; 좱; 좱; 좱; ) HANGUL SYLLABLE JWAELG +C8B2;C8B2;110C 116B 11B1;C8B2;110C 116B 11B1; # (좲; 좲; 좲; 좲; 좲; ) HANGUL SYLLABLE JWAELM +C8B3;C8B3;110C 116B 11B2;C8B3;110C 116B 11B2; # (좳; 좳; 좳; 좳; 좳; ) HANGUL SYLLABLE JWAELB +C8B4;C8B4;110C 116B 11B3;C8B4;110C 116B 11B3; # (좴; 좴; 좴; 좴; 좴; ) HANGUL SYLLABLE JWAELS +C8B5;C8B5;110C 116B 11B4;C8B5;110C 116B 11B4; # (좵; 좵; 좵; 좵; 좵; ) HANGUL SYLLABLE JWAELT +C8B6;C8B6;110C 116B 11B5;C8B6;110C 116B 11B5; # (좶; 좶; 좶; 좶; 좶; ) HANGUL SYLLABLE JWAELP +C8B7;C8B7;110C 116B 11B6;C8B7;110C 116B 11B6; # (좷; 좷; 좷; 좷; 좷; ) HANGUL SYLLABLE JWAELH +C8B8;C8B8;110C 116B 11B7;C8B8;110C 116B 11B7; # (좸; 좸; 좸; 좸; 좸; ) HANGUL SYLLABLE JWAEM +C8B9;C8B9;110C 116B 11B8;C8B9;110C 116B 11B8; # (좹; 좹; 좹; 좹; 좹; ) HANGUL SYLLABLE JWAEB +C8BA;C8BA;110C 116B 11B9;C8BA;110C 116B 11B9; # (좺; 좺; 좺; 좺; 좺; ) HANGUL SYLLABLE JWAEBS +C8BB;C8BB;110C 116B 11BA;C8BB;110C 116B 11BA; # (좻; 좻; 좻; 좻; 좻; ) HANGUL SYLLABLE JWAES +C8BC;C8BC;110C 116B 11BB;C8BC;110C 116B 11BB; # (좼; 좼; 좼; 좼; 좼; ) HANGUL SYLLABLE JWAESS +C8BD;C8BD;110C 116B 11BC;C8BD;110C 116B 11BC; # (좽; 좽; 좽; 좽; 좽; ) HANGUL SYLLABLE JWAENG +C8BE;C8BE;110C 116B 11BD;C8BE;110C 116B 11BD; # (좾; 좾; 좾; 좾; 좾; ) HANGUL SYLLABLE JWAEJ +C8BF;C8BF;110C 116B 11BE;C8BF;110C 116B 11BE; # (좿; 좿; 좿; 좿; 좿; ) HANGUL SYLLABLE JWAEC +C8C0;C8C0;110C 116B 11BF;C8C0;110C 116B 11BF; # (죀; 죀; 죀; 죀; 죀; ) HANGUL SYLLABLE JWAEK +C8C1;C8C1;110C 116B 11C0;C8C1;110C 116B 11C0; # (죁; 죁; 죁; 죁; 죁; ) HANGUL SYLLABLE JWAET +C8C2;C8C2;110C 116B 11C1;C8C2;110C 116B 11C1; # (죂; 죂; 죂; 죂; 죂; ) HANGUL SYLLABLE JWAEP +C8C3;C8C3;110C 116B 11C2;C8C3;110C 116B 11C2; # (죃; 죃; 죃; 죃; 죃; ) HANGUL SYLLABLE JWAEH +C8C4;C8C4;110C 116C;C8C4;110C 116C; # (죄; 죄; 죄; 죄; 죄; ) HANGUL SYLLABLE JOE +C8C5;C8C5;110C 116C 11A8;C8C5;110C 116C 11A8; # (죅; 죅; 죅; 죅; 죅; ) HANGUL SYLLABLE JOEG +C8C6;C8C6;110C 116C 11A9;C8C6;110C 116C 11A9; # (죆; 죆; 죆; 죆; 죆; ) HANGUL SYLLABLE JOEGG +C8C7;C8C7;110C 116C 11AA;C8C7;110C 116C 11AA; # (죇; 죇; 죇; 죇; 죇; ) HANGUL SYLLABLE JOEGS +C8C8;C8C8;110C 116C 11AB;C8C8;110C 116C 11AB; # (죈; 죈; 죈; 죈; 죈; ) HANGUL SYLLABLE JOEN +C8C9;C8C9;110C 116C 11AC;C8C9;110C 116C 11AC; # (죉; 죉; 죉; 죉; 죉; ) HANGUL SYLLABLE JOENJ +C8CA;C8CA;110C 116C 11AD;C8CA;110C 116C 11AD; # (죊; 죊; 죊; 죊; 죊; ) HANGUL SYLLABLE JOENH +C8CB;C8CB;110C 116C 11AE;C8CB;110C 116C 11AE; # (죋; 죋; 죋; 죋; 죋; ) HANGUL SYLLABLE JOED +C8CC;C8CC;110C 116C 11AF;C8CC;110C 116C 11AF; # (죌; 죌; 죌; 죌; 죌; ) HANGUL SYLLABLE JOEL +C8CD;C8CD;110C 116C 11B0;C8CD;110C 116C 11B0; # (죍; 죍; 죍; 죍; 죍; ) HANGUL SYLLABLE JOELG +C8CE;C8CE;110C 116C 11B1;C8CE;110C 116C 11B1; # (죎; 죎; 죎; 죎; 죎; ) HANGUL SYLLABLE JOELM +C8CF;C8CF;110C 116C 11B2;C8CF;110C 116C 11B2; # (죏; 죏; 죏; 죏; 죏; ) HANGUL SYLLABLE JOELB +C8D0;C8D0;110C 116C 11B3;C8D0;110C 116C 11B3; # (죐; 죐; 죐; 죐; 죐; ) HANGUL SYLLABLE JOELS +C8D1;C8D1;110C 116C 11B4;C8D1;110C 116C 11B4; # (죑; 죑; 죑; 죑; 죑; ) HANGUL SYLLABLE JOELT +C8D2;C8D2;110C 116C 11B5;C8D2;110C 116C 11B5; # (죒; 죒; 죒; 죒; 죒; ) HANGUL SYLLABLE JOELP +C8D3;C8D3;110C 116C 11B6;C8D3;110C 116C 11B6; # (죓; 죓; 죓; 죓; 죓; ) HANGUL SYLLABLE JOELH +C8D4;C8D4;110C 116C 11B7;C8D4;110C 116C 11B7; # (죔; 죔; 죔; 죔; 죔; ) HANGUL SYLLABLE JOEM +C8D5;C8D5;110C 116C 11B8;C8D5;110C 116C 11B8; # (죕; 죕; 죕; 죕; 죕; ) HANGUL SYLLABLE JOEB +C8D6;C8D6;110C 116C 11B9;C8D6;110C 116C 11B9; # (죖; 죖; 죖; 죖; 죖; ) HANGUL SYLLABLE JOEBS +C8D7;C8D7;110C 116C 11BA;C8D7;110C 116C 11BA; # (죗; 죗; 죗; 죗; 죗; ) HANGUL SYLLABLE JOES +C8D8;C8D8;110C 116C 11BB;C8D8;110C 116C 11BB; # (죘; 죘; 죘; 죘; 죘; ) HANGUL SYLLABLE JOESS +C8D9;C8D9;110C 116C 11BC;C8D9;110C 116C 11BC; # (죙; 죙; 죙; 죙; 죙; ) HANGUL SYLLABLE JOENG +C8DA;C8DA;110C 116C 11BD;C8DA;110C 116C 11BD; # (죚; 죚; 죚; 죚; 죚; ) HANGUL SYLLABLE JOEJ +C8DB;C8DB;110C 116C 11BE;C8DB;110C 116C 11BE; # (죛; 죛; 죛; 죛; 죛; ) HANGUL SYLLABLE JOEC +C8DC;C8DC;110C 116C 11BF;C8DC;110C 116C 11BF; # (죜; 죜; 죜; 죜; 죜; ) HANGUL SYLLABLE JOEK +C8DD;C8DD;110C 116C 11C0;C8DD;110C 116C 11C0; # (죝; 죝; 죝; 죝; 죝; ) HANGUL SYLLABLE JOET +C8DE;C8DE;110C 116C 11C1;C8DE;110C 116C 11C1; # (죞; 죞; 죞; 죞; 죞; ) HANGUL SYLLABLE JOEP +C8DF;C8DF;110C 116C 11C2;C8DF;110C 116C 11C2; # (죟; 죟; 죟; 죟; 죟; ) HANGUL SYLLABLE JOEH +C8E0;C8E0;110C 116D;C8E0;110C 116D; # (죠; 죠; 죠; 죠; 죠; ) HANGUL SYLLABLE JYO +C8E1;C8E1;110C 116D 11A8;C8E1;110C 116D 11A8; # (죡; 죡; 죡; 죡; 죡; ) HANGUL SYLLABLE JYOG +C8E2;C8E2;110C 116D 11A9;C8E2;110C 116D 11A9; # (죢; 죢; 죢; 죢; 죢; ) HANGUL SYLLABLE JYOGG +C8E3;C8E3;110C 116D 11AA;C8E3;110C 116D 11AA; # (죣; 죣; 죣; 죣; 죣; ) HANGUL SYLLABLE JYOGS +C8E4;C8E4;110C 116D 11AB;C8E4;110C 116D 11AB; # (죤; 죤; 죤; 죤; 죤; ) HANGUL SYLLABLE JYON +C8E5;C8E5;110C 116D 11AC;C8E5;110C 116D 11AC; # (죥; 죥; 죥; 죥; 죥; ) HANGUL SYLLABLE JYONJ +C8E6;C8E6;110C 116D 11AD;C8E6;110C 116D 11AD; # (죦; 죦; 죦; 죦; 죦; ) HANGUL SYLLABLE JYONH +C8E7;C8E7;110C 116D 11AE;C8E7;110C 116D 11AE; # (죧; 죧; 죧; 죧; 죧; ) HANGUL SYLLABLE JYOD +C8E8;C8E8;110C 116D 11AF;C8E8;110C 116D 11AF; # (죨; 죨; 죨; 죨; 죨; ) HANGUL SYLLABLE JYOL +C8E9;C8E9;110C 116D 11B0;C8E9;110C 116D 11B0; # (죩; 죩; 죩; 죩; 죩; ) HANGUL SYLLABLE JYOLG +C8EA;C8EA;110C 116D 11B1;C8EA;110C 116D 11B1; # (죪; 죪; 죪; 죪; 죪; ) HANGUL SYLLABLE JYOLM +C8EB;C8EB;110C 116D 11B2;C8EB;110C 116D 11B2; # (죫; 죫; 죫; 죫; 죫; ) HANGUL SYLLABLE JYOLB +C8EC;C8EC;110C 116D 11B3;C8EC;110C 116D 11B3; # (죬; 죬; 죬; 죬; 죬; ) HANGUL SYLLABLE JYOLS +C8ED;C8ED;110C 116D 11B4;C8ED;110C 116D 11B4; # (죭; 죭; 죭; 죭; 죭; ) HANGUL SYLLABLE JYOLT +C8EE;C8EE;110C 116D 11B5;C8EE;110C 116D 11B5; # (죮; 죮; 죮; 죮; 죮; ) HANGUL SYLLABLE JYOLP +C8EF;C8EF;110C 116D 11B6;C8EF;110C 116D 11B6; # (죯; 죯; 죯; 죯; 죯; ) HANGUL SYLLABLE JYOLH +C8F0;C8F0;110C 116D 11B7;C8F0;110C 116D 11B7; # (죰; 죰; 죰; 죰; 죰; ) HANGUL SYLLABLE JYOM +C8F1;C8F1;110C 116D 11B8;C8F1;110C 116D 11B8; # (죱; 죱; 죱; 죱; 죱; ) HANGUL SYLLABLE JYOB +C8F2;C8F2;110C 116D 11B9;C8F2;110C 116D 11B9; # (죲; 죲; 죲; 죲; 죲; ) HANGUL SYLLABLE JYOBS +C8F3;C8F3;110C 116D 11BA;C8F3;110C 116D 11BA; # (죳; 죳; 죳; 죳; 죳; ) HANGUL SYLLABLE JYOS +C8F4;C8F4;110C 116D 11BB;C8F4;110C 116D 11BB; # (죴; 죴; 죴; 죴; 죴; ) HANGUL SYLLABLE JYOSS +C8F5;C8F5;110C 116D 11BC;C8F5;110C 116D 11BC; # (죵; 죵; 죵; 죵; 죵; ) HANGUL SYLLABLE JYONG +C8F6;C8F6;110C 116D 11BD;C8F6;110C 116D 11BD; # (죶; 죶; 죶; 죶; 죶; ) HANGUL SYLLABLE JYOJ +C8F7;C8F7;110C 116D 11BE;C8F7;110C 116D 11BE; # (죷; 죷; 죷; 죷; 죷; ) HANGUL SYLLABLE JYOC +C8F8;C8F8;110C 116D 11BF;C8F8;110C 116D 11BF; # (죸; 죸; 죸; 죸; 죸; ) HANGUL SYLLABLE JYOK +C8F9;C8F9;110C 116D 11C0;C8F9;110C 116D 11C0; # (죹; 죹; 죹; 죹; 죹; ) HANGUL SYLLABLE JYOT +C8FA;C8FA;110C 116D 11C1;C8FA;110C 116D 11C1; # (죺; 죺; 죺; 죺; 죺; ) HANGUL SYLLABLE JYOP +C8FB;C8FB;110C 116D 11C2;C8FB;110C 116D 11C2; # (죻; 죻; 죻; 죻; 죻; ) HANGUL SYLLABLE JYOH +C8FC;C8FC;110C 116E;C8FC;110C 116E; # (주; 주; 주; 주; 주; ) HANGUL SYLLABLE JU +C8FD;C8FD;110C 116E 11A8;C8FD;110C 116E 11A8; # (죽; 죽; 죽; 죽; 죽; ) HANGUL SYLLABLE JUG +C8FE;C8FE;110C 116E 11A9;C8FE;110C 116E 11A9; # (죾; 죾; 죾; 죾; 죾; ) HANGUL SYLLABLE JUGG +C8FF;C8FF;110C 116E 11AA;C8FF;110C 116E 11AA; # (죿; 죿; 죿; 죿; 죿; ) HANGUL SYLLABLE JUGS +C900;C900;110C 116E 11AB;C900;110C 116E 11AB; # (준; 준; 준; 준; 준; ) HANGUL SYLLABLE JUN +C901;C901;110C 116E 11AC;C901;110C 116E 11AC; # (줁; 줁; 줁; 줁; 줁; ) HANGUL SYLLABLE JUNJ +C902;C902;110C 116E 11AD;C902;110C 116E 11AD; # (줂; 줂; 줂; 줂; 줂; ) HANGUL SYLLABLE JUNH +C903;C903;110C 116E 11AE;C903;110C 116E 11AE; # (줃; 줃; 줃; 줃; 줃; ) HANGUL SYLLABLE JUD +C904;C904;110C 116E 11AF;C904;110C 116E 11AF; # (줄; 줄; 줄; 줄; 줄; ) HANGUL SYLLABLE JUL +C905;C905;110C 116E 11B0;C905;110C 116E 11B0; # (줅; 줅; 줅; 줅; 줅; ) HANGUL SYLLABLE JULG +C906;C906;110C 116E 11B1;C906;110C 116E 11B1; # (줆; 줆; 줆; 줆; 줆; ) HANGUL SYLLABLE JULM +C907;C907;110C 116E 11B2;C907;110C 116E 11B2; # (줇; 줇; 줇; 줇; 줇; ) HANGUL SYLLABLE JULB +C908;C908;110C 116E 11B3;C908;110C 116E 11B3; # (줈; 줈; 줈; 줈; 줈; ) HANGUL SYLLABLE JULS +C909;C909;110C 116E 11B4;C909;110C 116E 11B4; # (줉; 줉; 줉; 줉; 줉; ) HANGUL SYLLABLE JULT +C90A;C90A;110C 116E 11B5;C90A;110C 116E 11B5; # (줊; 줊; 줊; 줊; 줊; ) HANGUL SYLLABLE JULP +C90B;C90B;110C 116E 11B6;C90B;110C 116E 11B6; # (줋; 줋; 줋; 줋; 줋; ) HANGUL SYLLABLE JULH +C90C;C90C;110C 116E 11B7;C90C;110C 116E 11B7; # (줌; 줌; 줌; 줌; 줌; ) HANGUL SYLLABLE JUM +C90D;C90D;110C 116E 11B8;C90D;110C 116E 11B8; # (줍; 줍; 줍; 줍; 줍; ) HANGUL SYLLABLE JUB +C90E;C90E;110C 116E 11B9;C90E;110C 116E 11B9; # (줎; 줎; 줎; 줎; 줎; ) HANGUL SYLLABLE JUBS +C90F;C90F;110C 116E 11BA;C90F;110C 116E 11BA; # (줏; 줏; 줏; 줏; 줏; ) HANGUL SYLLABLE JUS +C910;C910;110C 116E 11BB;C910;110C 116E 11BB; # (줐; 줐; 줐; 줐; 줐; ) HANGUL SYLLABLE JUSS +C911;C911;110C 116E 11BC;C911;110C 116E 11BC; # (중; 중; 중; 중; 중; ) HANGUL SYLLABLE JUNG +C912;C912;110C 116E 11BD;C912;110C 116E 11BD; # (줒; 줒; 줒; 줒; 줒; ) HANGUL SYLLABLE JUJ +C913;C913;110C 116E 11BE;C913;110C 116E 11BE; # (줓; 줓; 줓; 줓; 줓; ) HANGUL SYLLABLE JUC +C914;C914;110C 116E 11BF;C914;110C 116E 11BF; # (줔; 줔; 줔; 줔; 줔; ) HANGUL SYLLABLE JUK +C915;C915;110C 116E 11C0;C915;110C 116E 11C0; # (줕; 줕; 줕; 줕; 줕; ) HANGUL SYLLABLE JUT +C916;C916;110C 116E 11C1;C916;110C 116E 11C1; # (줖; 줖; 줖; 줖; 줖; ) HANGUL SYLLABLE JUP +C917;C917;110C 116E 11C2;C917;110C 116E 11C2; # (줗; 줗; 줗; 줗; 줗; ) HANGUL SYLLABLE JUH +C918;C918;110C 116F;C918;110C 116F; # (줘; 줘; 줘; 줘; 줘; ) HANGUL SYLLABLE JWEO +C919;C919;110C 116F 11A8;C919;110C 116F 11A8; # (줙; 줙; 줙; 줙; 줙; ) HANGUL SYLLABLE JWEOG +C91A;C91A;110C 116F 11A9;C91A;110C 116F 11A9; # (줚; 줚; 줚; 줚; 줚; ) HANGUL SYLLABLE JWEOGG +C91B;C91B;110C 116F 11AA;C91B;110C 116F 11AA; # (줛; 줛; 줛; 줛; 줛; ) HANGUL SYLLABLE JWEOGS +C91C;C91C;110C 116F 11AB;C91C;110C 116F 11AB; # (줜; 줜; 줜; 줜; 줜; ) HANGUL SYLLABLE JWEON +C91D;C91D;110C 116F 11AC;C91D;110C 116F 11AC; # (줝; 줝; 줝; 줝; 줝; ) HANGUL SYLLABLE JWEONJ +C91E;C91E;110C 116F 11AD;C91E;110C 116F 11AD; # (줞; 줞; 줞; 줞; 줞; ) HANGUL SYLLABLE JWEONH +C91F;C91F;110C 116F 11AE;C91F;110C 116F 11AE; # (줟; 줟; 줟; 줟; 줟; ) HANGUL SYLLABLE JWEOD +C920;C920;110C 116F 11AF;C920;110C 116F 11AF; # (줠; 줠; 줠; 줠; 줠; ) HANGUL SYLLABLE JWEOL +C921;C921;110C 116F 11B0;C921;110C 116F 11B0; # (줡; 줡; 줡; 줡; 줡; ) HANGUL SYLLABLE JWEOLG +C922;C922;110C 116F 11B1;C922;110C 116F 11B1; # (줢; 줢; 줢; 줢; 줢; ) HANGUL SYLLABLE JWEOLM +C923;C923;110C 116F 11B2;C923;110C 116F 11B2; # (줣; 줣; 줣; 줣; 줣; ) HANGUL SYLLABLE JWEOLB +C924;C924;110C 116F 11B3;C924;110C 116F 11B3; # (줤; 줤; 줤; 줤; 줤; ) HANGUL SYLLABLE JWEOLS +C925;C925;110C 116F 11B4;C925;110C 116F 11B4; # (줥; 줥; 줥; 줥; 줥; ) HANGUL SYLLABLE JWEOLT +C926;C926;110C 116F 11B5;C926;110C 116F 11B5; # (줦; 줦; 줦; 줦; 줦; ) HANGUL SYLLABLE JWEOLP +C927;C927;110C 116F 11B6;C927;110C 116F 11B6; # (줧; 줧; 줧; 줧; 줧; ) HANGUL SYLLABLE JWEOLH +C928;C928;110C 116F 11B7;C928;110C 116F 11B7; # (줨; 줨; 줨; 줨; 줨; ) HANGUL SYLLABLE JWEOM +C929;C929;110C 116F 11B8;C929;110C 116F 11B8; # (줩; 줩; 줩; 줩; 줩; ) HANGUL SYLLABLE JWEOB +C92A;C92A;110C 116F 11B9;C92A;110C 116F 11B9; # (줪; 줪; 줪; 줪; 줪; ) HANGUL SYLLABLE JWEOBS +C92B;C92B;110C 116F 11BA;C92B;110C 116F 11BA; # (줫; 줫; 줫; 줫; 줫; ) HANGUL SYLLABLE JWEOS +C92C;C92C;110C 116F 11BB;C92C;110C 116F 11BB; # (줬; 줬; 줬; 줬; 줬; ) HANGUL SYLLABLE JWEOSS +C92D;C92D;110C 116F 11BC;C92D;110C 116F 11BC; # (줭; 줭; 줭; 줭; 줭; ) HANGUL SYLLABLE JWEONG +C92E;C92E;110C 116F 11BD;C92E;110C 116F 11BD; # (줮; 줮; 줮; 줮; 줮; ) HANGUL SYLLABLE JWEOJ +C92F;C92F;110C 116F 11BE;C92F;110C 116F 11BE; # (줯; 줯; 줯; 줯; 줯; ) HANGUL SYLLABLE JWEOC +C930;C930;110C 116F 11BF;C930;110C 116F 11BF; # (줰; 줰; 줰; 줰; 줰; ) HANGUL SYLLABLE JWEOK +C931;C931;110C 116F 11C0;C931;110C 116F 11C0; # (줱; 줱; 줱; 줱; 줱; ) HANGUL SYLLABLE JWEOT +C932;C932;110C 116F 11C1;C932;110C 116F 11C1; # (줲; 줲; 줲; 줲; 줲; ) HANGUL SYLLABLE JWEOP +C933;C933;110C 116F 11C2;C933;110C 116F 11C2; # (줳; 줳; 줳; 줳; 줳; ) HANGUL SYLLABLE JWEOH +C934;C934;110C 1170;C934;110C 1170; # (줴; 줴; 줴; 줴; 줴; ) HANGUL SYLLABLE JWE +C935;C935;110C 1170 11A8;C935;110C 1170 11A8; # (줵; 줵; 줵; 줵; 줵; ) HANGUL SYLLABLE JWEG +C936;C936;110C 1170 11A9;C936;110C 1170 11A9; # (줶; 줶; 줶; 줶; 줶; ) HANGUL SYLLABLE JWEGG +C937;C937;110C 1170 11AA;C937;110C 1170 11AA; # (줷; 줷; 줷; 줷; 줷; ) HANGUL SYLLABLE JWEGS +C938;C938;110C 1170 11AB;C938;110C 1170 11AB; # (줸; 줸; 줸; 줸; 줸; ) HANGUL SYLLABLE JWEN +C939;C939;110C 1170 11AC;C939;110C 1170 11AC; # (줹; 줹; 줹; 줹; 줹; ) HANGUL SYLLABLE JWENJ +C93A;C93A;110C 1170 11AD;C93A;110C 1170 11AD; # (줺; 줺; 줺; 줺; 줺; ) HANGUL SYLLABLE JWENH +C93B;C93B;110C 1170 11AE;C93B;110C 1170 11AE; # (줻; 줻; 줻; 줻; 줻; ) HANGUL SYLLABLE JWED +C93C;C93C;110C 1170 11AF;C93C;110C 1170 11AF; # (줼; 줼; 줼; 줼; 줼; ) HANGUL SYLLABLE JWEL +C93D;C93D;110C 1170 11B0;C93D;110C 1170 11B0; # (줽; 줽; 줽; 줽; 줽; ) HANGUL SYLLABLE JWELG +C93E;C93E;110C 1170 11B1;C93E;110C 1170 11B1; # (줾; 줾; 줾; 줾; 줾; ) HANGUL SYLLABLE JWELM +C93F;C93F;110C 1170 11B2;C93F;110C 1170 11B2; # (줿; 줿; 줿; 줿; 줿; ) HANGUL SYLLABLE JWELB +C940;C940;110C 1170 11B3;C940;110C 1170 11B3; # (쥀; 쥀; 쥀; 쥀; 쥀; ) HANGUL SYLLABLE JWELS +C941;C941;110C 1170 11B4;C941;110C 1170 11B4; # (쥁; 쥁; 쥁; 쥁; 쥁; ) HANGUL SYLLABLE JWELT +C942;C942;110C 1170 11B5;C942;110C 1170 11B5; # (쥂; 쥂; 쥂; 쥂; 쥂; ) HANGUL SYLLABLE JWELP +C943;C943;110C 1170 11B6;C943;110C 1170 11B6; # (쥃; 쥃; 쥃; 쥃; 쥃; ) HANGUL SYLLABLE JWELH +C944;C944;110C 1170 11B7;C944;110C 1170 11B7; # (쥄; 쥄; 쥄; 쥄; 쥄; ) HANGUL SYLLABLE JWEM +C945;C945;110C 1170 11B8;C945;110C 1170 11B8; # (쥅; 쥅; 쥅; 쥅; 쥅; ) HANGUL SYLLABLE JWEB +C946;C946;110C 1170 11B9;C946;110C 1170 11B9; # (쥆; 쥆; 쥆; 쥆; 쥆; ) HANGUL SYLLABLE JWEBS +C947;C947;110C 1170 11BA;C947;110C 1170 11BA; # (쥇; 쥇; 쥇; 쥇; 쥇; ) HANGUL SYLLABLE JWES +C948;C948;110C 1170 11BB;C948;110C 1170 11BB; # (쥈; 쥈; 쥈; 쥈; 쥈; ) HANGUL SYLLABLE JWESS +C949;C949;110C 1170 11BC;C949;110C 1170 11BC; # (쥉; 쥉; 쥉; 쥉; 쥉; ) HANGUL SYLLABLE JWENG +C94A;C94A;110C 1170 11BD;C94A;110C 1170 11BD; # (쥊; 쥊; 쥊; 쥊; 쥊; ) HANGUL SYLLABLE JWEJ +C94B;C94B;110C 1170 11BE;C94B;110C 1170 11BE; # (쥋; 쥋; 쥋; 쥋; 쥋; ) HANGUL SYLLABLE JWEC +C94C;C94C;110C 1170 11BF;C94C;110C 1170 11BF; # (쥌; 쥌; 쥌; 쥌; 쥌; ) HANGUL SYLLABLE JWEK +C94D;C94D;110C 1170 11C0;C94D;110C 1170 11C0; # (쥍; 쥍; 쥍; 쥍; 쥍; ) HANGUL SYLLABLE JWET +C94E;C94E;110C 1170 11C1;C94E;110C 1170 11C1; # (쥎; 쥎; 쥎; 쥎; 쥎; ) HANGUL SYLLABLE JWEP +C94F;C94F;110C 1170 11C2;C94F;110C 1170 11C2; # (쥏; 쥏; 쥏; 쥏; 쥏; ) HANGUL SYLLABLE JWEH +C950;C950;110C 1171;C950;110C 1171; # (쥐; 쥐; 쥐; 쥐; 쥐; ) HANGUL SYLLABLE JWI +C951;C951;110C 1171 11A8;C951;110C 1171 11A8; # (쥑; 쥑; 쥑; 쥑; 쥑; ) HANGUL SYLLABLE JWIG +C952;C952;110C 1171 11A9;C952;110C 1171 11A9; # (쥒; 쥒; 쥒; 쥒; 쥒; ) HANGUL SYLLABLE JWIGG +C953;C953;110C 1171 11AA;C953;110C 1171 11AA; # (쥓; 쥓; 쥓; 쥓; 쥓; ) HANGUL SYLLABLE JWIGS +C954;C954;110C 1171 11AB;C954;110C 1171 11AB; # (쥔; 쥔; 쥔; 쥔; 쥔; ) HANGUL SYLLABLE JWIN +C955;C955;110C 1171 11AC;C955;110C 1171 11AC; # (쥕; 쥕; 쥕; 쥕; 쥕; ) HANGUL SYLLABLE JWINJ +C956;C956;110C 1171 11AD;C956;110C 1171 11AD; # (쥖; 쥖; 쥖; 쥖; 쥖; ) HANGUL SYLLABLE JWINH +C957;C957;110C 1171 11AE;C957;110C 1171 11AE; # (쥗; 쥗; 쥗; 쥗; 쥗; ) HANGUL SYLLABLE JWID +C958;C958;110C 1171 11AF;C958;110C 1171 11AF; # (쥘; 쥘; 쥘; 쥘; 쥘; ) HANGUL SYLLABLE JWIL +C959;C959;110C 1171 11B0;C959;110C 1171 11B0; # (쥙; 쥙; 쥙; 쥙; 쥙; ) HANGUL SYLLABLE JWILG +C95A;C95A;110C 1171 11B1;C95A;110C 1171 11B1; # (쥚; 쥚; 쥚; 쥚; 쥚; ) HANGUL SYLLABLE JWILM +C95B;C95B;110C 1171 11B2;C95B;110C 1171 11B2; # (쥛; 쥛; 쥛; 쥛; 쥛; ) HANGUL SYLLABLE JWILB +C95C;C95C;110C 1171 11B3;C95C;110C 1171 11B3; # (쥜; 쥜; 쥜; 쥜; 쥜; ) HANGUL SYLLABLE JWILS +C95D;C95D;110C 1171 11B4;C95D;110C 1171 11B4; # (쥝; 쥝; 쥝; 쥝; 쥝; ) HANGUL SYLLABLE JWILT +C95E;C95E;110C 1171 11B5;C95E;110C 1171 11B5; # (쥞; 쥞; 쥞; 쥞; 쥞; ) HANGUL SYLLABLE JWILP +C95F;C95F;110C 1171 11B6;C95F;110C 1171 11B6; # (쥟; 쥟; 쥟; 쥟; 쥟; ) HANGUL SYLLABLE JWILH +C960;C960;110C 1171 11B7;C960;110C 1171 11B7; # (쥠; 쥠; 쥠; 쥠; 쥠; ) HANGUL SYLLABLE JWIM +C961;C961;110C 1171 11B8;C961;110C 1171 11B8; # (쥡; 쥡; 쥡; 쥡; 쥡; ) HANGUL SYLLABLE JWIB +C962;C962;110C 1171 11B9;C962;110C 1171 11B9; # (쥢; 쥢; 쥢; 쥢; 쥢; ) HANGUL SYLLABLE JWIBS +C963;C963;110C 1171 11BA;C963;110C 1171 11BA; # (쥣; 쥣; 쥣; 쥣; 쥣; ) HANGUL SYLLABLE JWIS +C964;C964;110C 1171 11BB;C964;110C 1171 11BB; # (쥤; 쥤; 쥤; 쥤; 쥤; ) HANGUL SYLLABLE JWISS +C965;C965;110C 1171 11BC;C965;110C 1171 11BC; # (쥥; 쥥; 쥥; 쥥; 쥥; ) HANGUL SYLLABLE JWING +C966;C966;110C 1171 11BD;C966;110C 1171 11BD; # (쥦; 쥦; 쥦; 쥦; 쥦; ) HANGUL SYLLABLE JWIJ +C967;C967;110C 1171 11BE;C967;110C 1171 11BE; # (쥧; 쥧; 쥧; 쥧; 쥧; ) HANGUL SYLLABLE JWIC +C968;C968;110C 1171 11BF;C968;110C 1171 11BF; # (쥨; 쥨; 쥨; 쥨; 쥨; ) HANGUL SYLLABLE JWIK +C969;C969;110C 1171 11C0;C969;110C 1171 11C0; # (쥩; 쥩; 쥩; 쥩; 쥩; ) HANGUL SYLLABLE JWIT +C96A;C96A;110C 1171 11C1;C96A;110C 1171 11C1; # (쥪; 쥪; 쥪; 쥪; 쥪; ) HANGUL SYLLABLE JWIP +C96B;C96B;110C 1171 11C2;C96B;110C 1171 11C2; # (쥫; 쥫; 쥫; 쥫; 쥫; ) HANGUL SYLLABLE JWIH +C96C;C96C;110C 1172;C96C;110C 1172; # (쥬; 쥬; 쥬; 쥬; 쥬; ) HANGUL SYLLABLE JYU +C96D;C96D;110C 1172 11A8;C96D;110C 1172 11A8; # (쥭; 쥭; 쥭; 쥭; 쥭; ) HANGUL SYLLABLE JYUG +C96E;C96E;110C 1172 11A9;C96E;110C 1172 11A9; # (쥮; 쥮; 쥮; 쥮; 쥮; ) HANGUL SYLLABLE JYUGG +C96F;C96F;110C 1172 11AA;C96F;110C 1172 11AA; # (쥯; 쥯; 쥯; 쥯; 쥯; ) HANGUL SYLLABLE JYUGS +C970;C970;110C 1172 11AB;C970;110C 1172 11AB; # (쥰; 쥰; 쥰; 쥰; 쥰; ) HANGUL SYLLABLE JYUN +C971;C971;110C 1172 11AC;C971;110C 1172 11AC; # (쥱; 쥱; 쥱; 쥱; 쥱; ) HANGUL SYLLABLE JYUNJ +C972;C972;110C 1172 11AD;C972;110C 1172 11AD; # (쥲; 쥲; 쥲; 쥲; 쥲; ) HANGUL SYLLABLE JYUNH +C973;C973;110C 1172 11AE;C973;110C 1172 11AE; # (쥳; 쥳; 쥳; 쥳; 쥳; ) HANGUL SYLLABLE JYUD +C974;C974;110C 1172 11AF;C974;110C 1172 11AF; # (쥴; 쥴; 쥴; 쥴; 쥴; ) HANGUL SYLLABLE JYUL +C975;C975;110C 1172 11B0;C975;110C 1172 11B0; # (쥵; 쥵; 쥵; 쥵; 쥵; ) HANGUL SYLLABLE JYULG +C976;C976;110C 1172 11B1;C976;110C 1172 11B1; # (쥶; 쥶; 쥶; 쥶; 쥶; ) HANGUL SYLLABLE JYULM +C977;C977;110C 1172 11B2;C977;110C 1172 11B2; # (쥷; 쥷; 쥷; 쥷; 쥷; ) HANGUL SYLLABLE JYULB +C978;C978;110C 1172 11B3;C978;110C 1172 11B3; # (쥸; 쥸; 쥸; 쥸; 쥸; ) HANGUL SYLLABLE JYULS +C979;C979;110C 1172 11B4;C979;110C 1172 11B4; # (쥹; 쥹; 쥹; 쥹; 쥹; ) HANGUL SYLLABLE JYULT +C97A;C97A;110C 1172 11B5;C97A;110C 1172 11B5; # (쥺; 쥺; 쥺; 쥺; 쥺; ) HANGUL SYLLABLE JYULP +C97B;C97B;110C 1172 11B6;C97B;110C 1172 11B6; # (쥻; 쥻; 쥻; 쥻; 쥻; ) HANGUL SYLLABLE JYULH +C97C;C97C;110C 1172 11B7;C97C;110C 1172 11B7; # (쥼; 쥼; 쥼; 쥼; 쥼; ) HANGUL SYLLABLE JYUM +C97D;C97D;110C 1172 11B8;C97D;110C 1172 11B8; # (쥽; 쥽; 쥽; 쥽; 쥽; ) HANGUL SYLLABLE JYUB +C97E;C97E;110C 1172 11B9;C97E;110C 1172 11B9; # (쥾; 쥾; 쥾; 쥾; 쥾; ) HANGUL SYLLABLE JYUBS +C97F;C97F;110C 1172 11BA;C97F;110C 1172 11BA; # (쥿; 쥿; 쥿; 쥿; 쥿; ) HANGUL SYLLABLE JYUS +C980;C980;110C 1172 11BB;C980;110C 1172 11BB; # (즀; 즀; 즀; 즀; 즀; ) HANGUL SYLLABLE JYUSS +C981;C981;110C 1172 11BC;C981;110C 1172 11BC; # (즁; 즁; 즁; 즁; 즁; ) HANGUL SYLLABLE JYUNG +C982;C982;110C 1172 11BD;C982;110C 1172 11BD; # (즂; 즂; 즂; 즂; 즂; ) HANGUL SYLLABLE JYUJ +C983;C983;110C 1172 11BE;C983;110C 1172 11BE; # (즃; 즃; 즃; 즃; 즃; ) HANGUL SYLLABLE JYUC +C984;C984;110C 1172 11BF;C984;110C 1172 11BF; # (즄; 즄; 즄; 즄; 즄; ) HANGUL SYLLABLE JYUK +C985;C985;110C 1172 11C0;C985;110C 1172 11C0; # (즅; 즅; 즅; 즅; 즅; ) HANGUL SYLLABLE JYUT +C986;C986;110C 1172 11C1;C986;110C 1172 11C1; # (즆; 즆; 즆; 즆; 즆; ) HANGUL SYLLABLE JYUP +C987;C987;110C 1172 11C2;C987;110C 1172 11C2; # (즇; 즇; 즇; 즇; 즇; ) HANGUL SYLLABLE JYUH +C988;C988;110C 1173;C988;110C 1173; # (즈; 즈; 즈; 즈; 즈; ) HANGUL SYLLABLE JEU +C989;C989;110C 1173 11A8;C989;110C 1173 11A8; # (즉; 즉; 즉; 즉; 즉; ) HANGUL SYLLABLE JEUG +C98A;C98A;110C 1173 11A9;C98A;110C 1173 11A9; # (즊; 즊; 즊; 즊; 즊; ) HANGUL SYLLABLE JEUGG +C98B;C98B;110C 1173 11AA;C98B;110C 1173 11AA; # (즋; 즋; 즋; 즋; 즋; ) HANGUL SYLLABLE JEUGS +C98C;C98C;110C 1173 11AB;C98C;110C 1173 11AB; # (즌; 즌; 즌; 즌; 즌; ) HANGUL SYLLABLE JEUN +C98D;C98D;110C 1173 11AC;C98D;110C 1173 11AC; # (즍; 즍; 즍; 즍; 즍; ) HANGUL SYLLABLE JEUNJ +C98E;C98E;110C 1173 11AD;C98E;110C 1173 11AD; # (즎; 즎; 즎; 즎; 즎; ) HANGUL SYLLABLE JEUNH +C98F;C98F;110C 1173 11AE;C98F;110C 1173 11AE; # (즏; 즏; 즏; 즏; 즏; ) HANGUL SYLLABLE JEUD +C990;C990;110C 1173 11AF;C990;110C 1173 11AF; # (즐; 즐; 즐; 즐; 즐; ) HANGUL SYLLABLE JEUL +C991;C991;110C 1173 11B0;C991;110C 1173 11B0; # (즑; 즑; 즑; 즑; 즑; ) HANGUL SYLLABLE JEULG +C992;C992;110C 1173 11B1;C992;110C 1173 11B1; # (즒; 즒; 즒; 즒; 즒; ) HANGUL SYLLABLE JEULM +C993;C993;110C 1173 11B2;C993;110C 1173 11B2; # (즓; 즓; 즓; 즓; 즓; ) HANGUL SYLLABLE JEULB +C994;C994;110C 1173 11B3;C994;110C 1173 11B3; # (즔; 즔; 즔; 즔; 즔; ) HANGUL SYLLABLE JEULS +C995;C995;110C 1173 11B4;C995;110C 1173 11B4; # (즕; 즕; 즕; 즕; 즕; ) HANGUL SYLLABLE JEULT +C996;C996;110C 1173 11B5;C996;110C 1173 11B5; # (즖; 즖; 즖; 즖; 즖; ) HANGUL SYLLABLE JEULP +C997;C997;110C 1173 11B6;C997;110C 1173 11B6; # (즗; 즗; 즗; 즗; 즗; ) HANGUL SYLLABLE JEULH +C998;C998;110C 1173 11B7;C998;110C 1173 11B7; # (즘; 즘; 즘; 즘; 즘; ) HANGUL SYLLABLE JEUM +C999;C999;110C 1173 11B8;C999;110C 1173 11B8; # (즙; 즙; 즙; 즙; 즙; ) HANGUL SYLLABLE JEUB +C99A;C99A;110C 1173 11B9;C99A;110C 1173 11B9; # (즚; 즚; 즚; 즚; 즚; ) HANGUL SYLLABLE JEUBS +C99B;C99B;110C 1173 11BA;C99B;110C 1173 11BA; # (즛; 즛; 즛; 즛; 즛; ) HANGUL SYLLABLE JEUS +C99C;C99C;110C 1173 11BB;C99C;110C 1173 11BB; # (즜; 즜; 즜; 즜; 즜; ) HANGUL SYLLABLE JEUSS +C99D;C99D;110C 1173 11BC;C99D;110C 1173 11BC; # (증; 증; 증; 증; 증; ) HANGUL SYLLABLE JEUNG +C99E;C99E;110C 1173 11BD;C99E;110C 1173 11BD; # (즞; 즞; 즞; 즞; 즞; ) HANGUL SYLLABLE JEUJ +C99F;C99F;110C 1173 11BE;C99F;110C 1173 11BE; # (즟; 즟; 즟; 즟; 즟; ) HANGUL SYLLABLE JEUC +C9A0;C9A0;110C 1173 11BF;C9A0;110C 1173 11BF; # (즠; 즠; 즠; 즠; 즠; ) HANGUL SYLLABLE JEUK +C9A1;C9A1;110C 1173 11C0;C9A1;110C 1173 11C0; # (즡; 즡; 즡; 즡; 즡; ) HANGUL SYLLABLE JEUT +C9A2;C9A2;110C 1173 11C1;C9A2;110C 1173 11C1; # (즢; 즢; 즢; 즢; 즢; ) HANGUL SYLLABLE JEUP +C9A3;C9A3;110C 1173 11C2;C9A3;110C 1173 11C2; # (즣; 즣; 즣; 즣; 즣; ) HANGUL SYLLABLE JEUH +C9A4;C9A4;110C 1174;C9A4;110C 1174; # (즤; 즤; 즤; 즤; 즤; ) HANGUL SYLLABLE JYI +C9A5;C9A5;110C 1174 11A8;C9A5;110C 1174 11A8; # (즥; 즥; 즥; 즥; 즥; ) HANGUL SYLLABLE JYIG +C9A6;C9A6;110C 1174 11A9;C9A6;110C 1174 11A9; # (즦; 즦; 즦; 즦; 즦; ) HANGUL SYLLABLE JYIGG +C9A7;C9A7;110C 1174 11AA;C9A7;110C 1174 11AA; # (즧; 즧; 즧; 즧; 즧; ) HANGUL SYLLABLE JYIGS +C9A8;C9A8;110C 1174 11AB;C9A8;110C 1174 11AB; # (즨; 즨; 즨; 즨; 즨; ) HANGUL SYLLABLE JYIN +C9A9;C9A9;110C 1174 11AC;C9A9;110C 1174 11AC; # (즩; 즩; 즩; 즩; 즩; ) HANGUL SYLLABLE JYINJ +C9AA;C9AA;110C 1174 11AD;C9AA;110C 1174 11AD; # (즪; 즪; 즪; 즪; 즪; ) HANGUL SYLLABLE JYINH +C9AB;C9AB;110C 1174 11AE;C9AB;110C 1174 11AE; # (즫; 즫; 즫; 즫; 즫; ) HANGUL SYLLABLE JYID +C9AC;C9AC;110C 1174 11AF;C9AC;110C 1174 11AF; # (즬; 즬; 즬; 즬; 즬; ) HANGUL SYLLABLE JYIL +C9AD;C9AD;110C 1174 11B0;C9AD;110C 1174 11B0; # (즭; 즭; 즭; 즭; 즭; ) HANGUL SYLLABLE JYILG +C9AE;C9AE;110C 1174 11B1;C9AE;110C 1174 11B1; # (즮; 즮; 즮; 즮; 즮; ) HANGUL SYLLABLE JYILM +C9AF;C9AF;110C 1174 11B2;C9AF;110C 1174 11B2; # (즯; 즯; 즯; 즯; 즯; ) HANGUL SYLLABLE JYILB +C9B0;C9B0;110C 1174 11B3;C9B0;110C 1174 11B3; # (즰; 즰; 즰; 즰; 즰; ) HANGUL SYLLABLE JYILS +C9B1;C9B1;110C 1174 11B4;C9B1;110C 1174 11B4; # (즱; 즱; 즱; 즱; 즱; ) HANGUL SYLLABLE JYILT +C9B2;C9B2;110C 1174 11B5;C9B2;110C 1174 11B5; # (즲; 즲; 즲; 즲; 즲; ) HANGUL SYLLABLE JYILP +C9B3;C9B3;110C 1174 11B6;C9B3;110C 1174 11B6; # (즳; 즳; 즳; 즳; 즳; ) HANGUL SYLLABLE JYILH +C9B4;C9B4;110C 1174 11B7;C9B4;110C 1174 11B7; # (즴; 즴; 즴; 즴; 즴; ) HANGUL SYLLABLE JYIM +C9B5;C9B5;110C 1174 11B8;C9B5;110C 1174 11B8; # (즵; 즵; 즵; 즵; 즵; ) HANGUL SYLLABLE JYIB +C9B6;C9B6;110C 1174 11B9;C9B6;110C 1174 11B9; # (즶; 즶; 즶; 즶; 즶; ) HANGUL SYLLABLE JYIBS +C9B7;C9B7;110C 1174 11BA;C9B7;110C 1174 11BA; # (즷; 즷; 즷; 즷; 즷; ) HANGUL SYLLABLE JYIS +C9B8;C9B8;110C 1174 11BB;C9B8;110C 1174 11BB; # (즸; 즸; 즸; 즸; 즸; ) HANGUL SYLLABLE JYISS +C9B9;C9B9;110C 1174 11BC;C9B9;110C 1174 11BC; # (즹; 즹; 즹; 즹; 즹; ) HANGUL SYLLABLE JYING +C9BA;C9BA;110C 1174 11BD;C9BA;110C 1174 11BD; # (즺; 즺; 즺; 즺; 즺; ) HANGUL SYLLABLE JYIJ +C9BB;C9BB;110C 1174 11BE;C9BB;110C 1174 11BE; # (즻; 즻; 즻; 즻; 즻; ) HANGUL SYLLABLE JYIC +C9BC;C9BC;110C 1174 11BF;C9BC;110C 1174 11BF; # (즼; 즼; 즼; 즼; 즼; ) HANGUL SYLLABLE JYIK +C9BD;C9BD;110C 1174 11C0;C9BD;110C 1174 11C0; # (즽; 즽; 즽; 즽; 즽; ) HANGUL SYLLABLE JYIT +C9BE;C9BE;110C 1174 11C1;C9BE;110C 1174 11C1; # (즾; 즾; 즾; 즾; 즾; ) HANGUL SYLLABLE JYIP +C9BF;C9BF;110C 1174 11C2;C9BF;110C 1174 11C2; # (즿; 즿; 즿; 즿; 즿; ) HANGUL SYLLABLE JYIH +C9C0;C9C0;110C 1175;C9C0;110C 1175; # (지; 지; 지; 지; 지; ) HANGUL SYLLABLE JI +C9C1;C9C1;110C 1175 11A8;C9C1;110C 1175 11A8; # (직; 직; 직; 직; 직; ) HANGUL SYLLABLE JIG +C9C2;C9C2;110C 1175 11A9;C9C2;110C 1175 11A9; # (짂; 짂; 짂; 짂; 짂; ) HANGUL SYLLABLE JIGG +C9C3;C9C3;110C 1175 11AA;C9C3;110C 1175 11AA; # (짃; 짃; 짃; 짃; 짃; ) HANGUL SYLLABLE JIGS +C9C4;C9C4;110C 1175 11AB;C9C4;110C 1175 11AB; # (진; 진; 진; 진; 진; ) HANGUL SYLLABLE JIN +C9C5;C9C5;110C 1175 11AC;C9C5;110C 1175 11AC; # (짅; 짅; 짅; 짅; 짅; ) HANGUL SYLLABLE JINJ +C9C6;C9C6;110C 1175 11AD;C9C6;110C 1175 11AD; # (짆; 짆; 짆; 짆; 짆; ) HANGUL SYLLABLE JINH +C9C7;C9C7;110C 1175 11AE;C9C7;110C 1175 11AE; # (짇; 짇; 짇; 짇; 짇; ) HANGUL SYLLABLE JID +C9C8;C9C8;110C 1175 11AF;C9C8;110C 1175 11AF; # (질; 질; 질; 질; 질; ) HANGUL SYLLABLE JIL +C9C9;C9C9;110C 1175 11B0;C9C9;110C 1175 11B0; # (짉; 짉; 짉; 짉; 짉; ) HANGUL SYLLABLE JILG +C9CA;C9CA;110C 1175 11B1;C9CA;110C 1175 11B1; # (짊; 짊; 짊; 짊; 짊; ) HANGUL SYLLABLE JILM +C9CB;C9CB;110C 1175 11B2;C9CB;110C 1175 11B2; # (짋; 짋; 짋; 짋; 짋; ) HANGUL SYLLABLE JILB +C9CC;C9CC;110C 1175 11B3;C9CC;110C 1175 11B3; # (짌; 짌; 짌; 짌; 짌; ) HANGUL SYLLABLE JILS +C9CD;C9CD;110C 1175 11B4;C9CD;110C 1175 11B4; # (짍; 짍; 짍; 짍; 짍; ) HANGUL SYLLABLE JILT +C9CE;C9CE;110C 1175 11B5;C9CE;110C 1175 11B5; # (짎; 짎; 짎; 짎; 짎; ) HANGUL SYLLABLE JILP +C9CF;C9CF;110C 1175 11B6;C9CF;110C 1175 11B6; # (짏; 짏; 짏; 짏; 짏; ) HANGUL SYLLABLE JILH +C9D0;C9D0;110C 1175 11B7;C9D0;110C 1175 11B7; # (짐; 짐; 짐; 짐; 짐; ) HANGUL SYLLABLE JIM +C9D1;C9D1;110C 1175 11B8;C9D1;110C 1175 11B8; # (집; 집; 집; 집; 집; ) HANGUL SYLLABLE JIB +C9D2;C9D2;110C 1175 11B9;C9D2;110C 1175 11B9; # (짒; 짒; 짒; 짒; 짒; ) HANGUL SYLLABLE JIBS +C9D3;C9D3;110C 1175 11BA;C9D3;110C 1175 11BA; # (짓; 짓; 짓; 짓; 짓; ) HANGUL SYLLABLE JIS +C9D4;C9D4;110C 1175 11BB;C9D4;110C 1175 11BB; # (짔; 짔; 짔; 짔; 짔; ) HANGUL SYLLABLE JISS +C9D5;C9D5;110C 1175 11BC;C9D5;110C 1175 11BC; # (징; 징; 징; 징; 징; ) HANGUL SYLLABLE JING +C9D6;C9D6;110C 1175 11BD;C9D6;110C 1175 11BD; # (짖; 짖; 짖; 짖; 짖; ) HANGUL SYLLABLE JIJ +C9D7;C9D7;110C 1175 11BE;C9D7;110C 1175 11BE; # (짗; 짗; 짗; 짗; 짗; ) HANGUL SYLLABLE JIC +C9D8;C9D8;110C 1175 11BF;C9D8;110C 1175 11BF; # (짘; 짘; 짘; 짘; 짘; ) HANGUL SYLLABLE JIK +C9D9;C9D9;110C 1175 11C0;C9D9;110C 1175 11C0; # (짙; 짙; 짙; 짙; 짙; ) HANGUL SYLLABLE JIT +C9DA;C9DA;110C 1175 11C1;C9DA;110C 1175 11C1; # (짚; 짚; 짚; 짚; 짚; ) HANGUL SYLLABLE JIP +C9DB;C9DB;110C 1175 11C2;C9DB;110C 1175 11C2; # (짛; 짛; 짛; 짛; 짛; ) HANGUL SYLLABLE JIH +C9DC;C9DC;110D 1161;C9DC;110D 1161; # (짜; 짜; 짜; 짜; 짜; ) HANGUL SYLLABLE JJA +C9DD;C9DD;110D 1161 11A8;C9DD;110D 1161 11A8; # (짝; 짝; 짝; 짝; 짝; ) HANGUL SYLLABLE JJAG +C9DE;C9DE;110D 1161 11A9;C9DE;110D 1161 11A9; # (짞; 짞; 짞; 짞; 짞; ) HANGUL SYLLABLE JJAGG +C9DF;C9DF;110D 1161 11AA;C9DF;110D 1161 11AA; # (짟; 짟; 짟; 짟; 짟; ) HANGUL SYLLABLE JJAGS +C9E0;C9E0;110D 1161 11AB;C9E0;110D 1161 11AB; # (짠; 짠; 짠; 짠; 짠; ) HANGUL SYLLABLE JJAN +C9E1;C9E1;110D 1161 11AC;C9E1;110D 1161 11AC; # (짡; 짡; 짡; 짡; 짡; ) HANGUL SYLLABLE JJANJ +C9E2;C9E2;110D 1161 11AD;C9E2;110D 1161 11AD; # (짢; 짢; 짢; 짢; 짢; ) HANGUL SYLLABLE JJANH +C9E3;C9E3;110D 1161 11AE;C9E3;110D 1161 11AE; # (짣; 짣; 짣; 짣; 짣; ) HANGUL SYLLABLE JJAD +C9E4;C9E4;110D 1161 11AF;C9E4;110D 1161 11AF; # (짤; 짤; 짤; 짤; 짤; ) HANGUL SYLLABLE JJAL +C9E5;C9E5;110D 1161 11B0;C9E5;110D 1161 11B0; # (짥; 짥; 짥; 짥; 짥; ) HANGUL SYLLABLE JJALG +C9E6;C9E6;110D 1161 11B1;C9E6;110D 1161 11B1; # (짦; 짦; 짦; 짦; 짦; ) HANGUL SYLLABLE JJALM +C9E7;C9E7;110D 1161 11B2;C9E7;110D 1161 11B2; # (짧; 짧; 짧; 짧; 짧; ) HANGUL SYLLABLE JJALB +C9E8;C9E8;110D 1161 11B3;C9E8;110D 1161 11B3; # (짨; 짨; 짨; 짨; 짨; ) HANGUL SYLLABLE JJALS +C9E9;C9E9;110D 1161 11B4;C9E9;110D 1161 11B4; # (짩; 짩; 짩; 짩; 짩; ) HANGUL SYLLABLE JJALT +C9EA;C9EA;110D 1161 11B5;C9EA;110D 1161 11B5; # (짪; 짪; 짪; 짪; 짪; ) HANGUL SYLLABLE JJALP +C9EB;C9EB;110D 1161 11B6;C9EB;110D 1161 11B6; # (짫; 짫; 짫; 짫; 짫; ) HANGUL SYLLABLE JJALH +C9EC;C9EC;110D 1161 11B7;C9EC;110D 1161 11B7; # (짬; 짬; 짬; 짬; 짬; ) HANGUL SYLLABLE JJAM +C9ED;C9ED;110D 1161 11B8;C9ED;110D 1161 11B8; # (짭; 짭; 짭; 짭; 짭; ) HANGUL SYLLABLE JJAB +C9EE;C9EE;110D 1161 11B9;C9EE;110D 1161 11B9; # (짮; 짮; 짮; 짮; 짮; ) HANGUL SYLLABLE JJABS +C9EF;C9EF;110D 1161 11BA;C9EF;110D 1161 11BA; # (짯; 짯; 짯; 짯; 짯; ) HANGUL SYLLABLE JJAS +C9F0;C9F0;110D 1161 11BB;C9F0;110D 1161 11BB; # (짰; 짰; 짰; 짰; 짰; ) HANGUL SYLLABLE JJASS +C9F1;C9F1;110D 1161 11BC;C9F1;110D 1161 11BC; # (짱; 짱; 짱; 짱; 짱; ) HANGUL SYLLABLE JJANG +C9F2;C9F2;110D 1161 11BD;C9F2;110D 1161 11BD; # (짲; 짲; 짲; 짲; 짲; ) HANGUL SYLLABLE JJAJ +C9F3;C9F3;110D 1161 11BE;C9F3;110D 1161 11BE; # (짳; 짳; 짳; 짳; 짳; ) HANGUL SYLLABLE JJAC +C9F4;C9F4;110D 1161 11BF;C9F4;110D 1161 11BF; # (짴; 짴; 짴; 짴; 짴; ) HANGUL SYLLABLE JJAK +C9F5;C9F5;110D 1161 11C0;C9F5;110D 1161 11C0; # (짵; 짵; 짵; 짵; 짵; ) HANGUL SYLLABLE JJAT +C9F6;C9F6;110D 1161 11C1;C9F6;110D 1161 11C1; # (짶; 짶; 짶; 짶; 짶; ) HANGUL SYLLABLE JJAP +C9F7;C9F7;110D 1161 11C2;C9F7;110D 1161 11C2; # (짷; 짷; 짷; 짷; 짷; ) HANGUL SYLLABLE JJAH +C9F8;C9F8;110D 1162;C9F8;110D 1162; # (째; 째; 째; 째; 째; ) HANGUL SYLLABLE JJAE +C9F9;C9F9;110D 1162 11A8;C9F9;110D 1162 11A8; # (짹; 짹; 짹; 짹; 짹; ) HANGUL SYLLABLE JJAEG +C9FA;C9FA;110D 1162 11A9;C9FA;110D 1162 11A9; # (짺; 짺; 짺; 짺; 짺; ) HANGUL SYLLABLE JJAEGG +C9FB;C9FB;110D 1162 11AA;C9FB;110D 1162 11AA; # (짻; 짻; 짻; 짻; 짻; ) HANGUL SYLLABLE JJAEGS +C9FC;C9FC;110D 1162 11AB;C9FC;110D 1162 11AB; # (짼; 짼; 짼; 짼; 짼; ) HANGUL SYLLABLE JJAEN +C9FD;C9FD;110D 1162 11AC;C9FD;110D 1162 11AC; # (짽; 짽; 짽; 짽; 짽; ) HANGUL SYLLABLE JJAENJ +C9FE;C9FE;110D 1162 11AD;C9FE;110D 1162 11AD; # (짾; 짾; 짾; 짾; 짾; ) HANGUL SYLLABLE JJAENH +C9FF;C9FF;110D 1162 11AE;C9FF;110D 1162 11AE; # (짿; 짿; 짿; 짿; 짿; ) HANGUL SYLLABLE JJAED +CA00;CA00;110D 1162 11AF;CA00;110D 1162 11AF; # (쨀; 쨀; 쨀; 쨀; 쨀; ) HANGUL SYLLABLE JJAEL +CA01;CA01;110D 1162 11B0;CA01;110D 1162 11B0; # (쨁; 쨁; 쨁; 쨁; 쨁; ) HANGUL SYLLABLE JJAELG +CA02;CA02;110D 1162 11B1;CA02;110D 1162 11B1; # (쨂; 쨂; 쨂; 쨂; 쨂; ) HANGUL SYLLABLE JJAELM +CA03;CA03;110D 1162 11B2;CA03;110D 1162 11B2; # (쨃; 쨃; 쨃; 쨃; 쨃; ) HANGUL SYLLABLE JJAELB +CA04;CA04;110D 1162 11B3;CA04;110D 1162 11B3; # (쨄; 쨄; 쨄; 쨄; 쨄; ) HANGUL SYLLABLE JJAELS +CA05;CA05;110D 1162 11B4;CA05;110D 1162 11B4; # (쨅; 쨅; 쨅; 쨅; 쨅; ) HANGUL SYLLABLE JJAELT +CA06;CA06;110D 1162 11B5;CA06;110D 1162 11B5; # (쨆; 쨆; 쨆; 쨆; 쨆; ) HANGUL SYLLABLE JJAELP +CA07;CA07;110D 1162 11B6;CA07;110D 1162 11B6; # (쨇; 쨇; 쨇; 쨇; 쨇; ) HANGUL SYLLABLE JJAELH +CA08;CA08;110D 1162 11B7;CA08;110D 1162 11B7; # (쨈; 쨈; 쨈; 쨈; 쨈; ) HANGUL SYLLABLE JJAEM +CA09;CA09;110D 1162 11B8;CA09;110D 1162 11B8; # (쨉; 쨉; 쨉; 쨉; 쨉; ) HANGUL SYLLABLE JJAEB +CA0A;CA0A;110D 1162 11B9;CA0A;110D 1162 11B9; # (쨊; 쨊; 쨊; 쨊; 쨊; ) HANGUL SYLLABLE JJAEBS +CA0B;CA0B;110D 1162 11BA;CA0B;110D 1162 11BA; # (쨋; 쨋; 쨋; 쨋; 쨋; ) HANGUL SYLLABLE JJAES +CA0C;CA0C;110D 1162 11BB;CA0C;110D 1162 11BB; # (쨌; 쨌; 쨌; 쨌; 쨌; ) HANGUL SYLLABLE JJAESS +CA0D;CA0D;110D 1162 11BC;CA0D;110D 1162 11BC; # (쨍; 쨍; 쨍; 쨍; 쨍; ) HANGUL SYLLABLE JJAENG +CA0E;CA0E;110D 1162 11BD;CA0E;110D 1162 11BD; # (쨎; 쨎; 쨎; 쨎; 쨎; ) HANGUL SYLLABLE JJAEJ +CA0F;CA0F;110D 1162 11BE;CA0F;110D 1162 11BE; # (쨏; 쨏; 쨏; 쨏; 쨏; ) HANGUL SYLLABLE JJAEC +CA10;CA10;110D 1162 11BF;CA10;110D 1162 11BF; # (쨐; 쨐; 쨐; 쨐; 쨐; ) HANGUL SYLLABLE JJAEK +CA11;CA11;110D 1162 11C0;CA11;110D 1162 11C0; # (쨑; 쨑; 쨑; 쨑; 쨑; ) HANGUL SYLLABLE JJAET +CA12;CA12;110D 1162 11C1;CA12;110D 1162 11C1; # (쨒; 쨒; 쨒; 쨒; 쨒; ) HANGUL SYLLABLE JJAEP +CA13;CA13;110D 1162 11C2;CA13;110D 1162 11C2; # (쨓; 쨓; 쨓; 쨓; 쨓; ) HANGUL SYLLABLE JJAEH +CA14;CA14;110D 1163;CA14;110D 1163; # (쨔; 쨔; 쨔; 쨔; 쨔; ) HANGUL SYLLABLE JJYA +CA15;CA15;110D 1163 11A8;CA15;110D 1163 11A8; # (쨕; 쨕; 쨕; 쨕; 쨕; ) HANGUL SYLLABLE JJYAG +CA16;CA16;110D 1163 11A9;CA16;110D 1163 11A9; # (쨖; 쨖; 쨖; 쨖; 쨖; ) HANGUL SYLLABLE JJYAGG +CA17;CA17;110D 1163 11AA;CA17;110D 1163 11AA; # (쨗; 쨗; 쨗; 쨗; 쨗; ) HANGUL SYLLABLE JJYAGS +CA18;CA18;110D 1163 11AB;CA18;110D 1163 11AB; # (쨘; 쨘; 쨘; 쨘; 쨘; ) HANGUL SYLLABLE JJYAN +CA19;CA19;110D 1163 11AC;CA19;110D 1163 11AC; # (쨙; 쨙; 쨙; 쨙; 쨙; ) HANGUL SYLLABLE JJYANJ +CA1A;CA1A;110D 1163 11AD;CA1A;110D 1163 11AD; # (쨚; 쨚; 쨚; 쨚; 쨚; ) HANGUL SYLLABLE JJYANH +CA1B;CA1B;110D 1163 11AE;CA1B;110D 1163 11AE; # (쨛; 쨛; 쨛; 쨛; 쨛; ) HANGUL SYLLABLE JJYAD +CA1C;CA1C;110D 1163 11AF;CA1C;110D 1163 11AF; # (쨜; 쨜; 쨜; 쨜; 쨜; ) HANGUL SYLLABLE JJYAL +CA1D;CA1D;110D 1163 11B0;CA1D;110D 1163 11B0; # (쨝; 쨝; 쨝; 쨝; 쨝; ) HANGUL SYLLABLE JJYALG +CA1E;CA1E;110D 1163 11B1;CA1E;110D 1163 11B1; # (쨞; 쨞; 쨞; 쨞; 쨞; ) HANGUL SYLLABLE JJYALM +CA1F;CA1F;110D 1163 11B2;CA1F;110D 1163 11B2; # (쨟; 쨟; 쨟; 쨟; 쨟; ) HANGUL SYLLABLE JJYALB +CA20;CA20;110D 1163 11B3;CA20;110D 1163 11B3; # (쨠; 쨠; 쨠; 쨠; 쨠; ) HANGUL SYLLABLE JJYALS +CA21;CA21;110D 1163 11B4;CA21;110D 1163 11B4; # (쨡; 쨡; 쨡; 쨡; 쨡; ) HANGUL SYLLABLE JJYALT +CA22;CA22;110D 1163 11B5;CA22;110D 1163 11B5; # (쨢; 쨢; 쨢; 쨢; 쨢; ) HANGUL SYLLABLE JJYALP +CA23;CA23;110D 1163 11B6;CA23;110D 1163 11B6; # (쨣; 쨣; 쨣; 쨣; 쨣; ) HANGUL SYLLABLE JJYALH +CA24;CA24;110D 1163 11B7;CA24;110D 1163 11B7; # (쨤; 쨤; 쨤; 쨤; 쨤; ) HANGUL SYLLABLE JJYAM +CA25;CA25;110D 1163 11B8;CA25;110D 1163 11B8; # (쨥; 쨥; 쨥; 쨥; 쨥; ) HANGUL SYLLABLE JJYAB +CA26;CA26;110D 1163 11B9;CA26;110D 1163 11B9; # (쨦; 쨦; 쨦; 쨦; 쨦; ) HANGUL SYLLABLE JJYABS +CA27;CA27;110D 1163 11BA;CA27;110D 1163 11BA; # (쨧; 쨧; 쨧; 쨧; 쨧; ) HANGUL SYLLABLE JJYAS +CA28;CA28;110D 1163 11BB;CA28;110D 1163 11BB; # (쨨; 쨨; 쨨; 쨨; 쨨; ) HANGUL SYLLABLE JJYASS +CA29;CA29;110D 1163 11BC;CA29;110D 1163 11BC; # (쨩; 쨩; 쨩; 쨩; 쨩; ) HANGUL SYLLABLE JJYANG +CA2A;CA2A;110D 1163 11BD;CA2A;110D 1163 11BD; # (쨪; 쨪; 쨪; 쨪; 쨪; ) HANGUL SYLLABLE JJYAJ +CA2B;CA2B;110D 1163 11BE;CA2B;110D 1163 11BE; # (쨫; 쨫; 쨫; 쨫; 쨫; ) HANGUL SYLLABLE JJYAC +CA2C;CA2C;110D 1163 11BF;CA2C;110D 1163 11BF; # (쨬; 쨬; 쨬; 쨬; 쨬; ) HANGUL SYLLABLE JJYAK +CA2D;CA2D;110D 1163 11C0;CA2D;110D 1163 11C0; # (쨭; 쨭; 쨭; 쨭; 쨭; ) HANGUL SYLLABLE JJYAT +CA2E;CA2E;110D 1163 11C1;CA2E;110D 1163 11C1; # (쨮; 쨮; 쨮; 쨮; 쨮; ) HANGUL SYLLABLE JJYAP +CA2F;CA2F;110D 1163 11C2;CA2F;110D 1163 11C2; # (쨯; 쨯; 쨯; 쨯; 쨯; ) HANGUL SYLLABLE JJYAH +CA30;CA30;110D 1164;CA30;110D 1164; # (쨰; 쨰; 쨰; 쨰; 쨰; ) HANGUL SYLLABLE JJYAE +CA31;CA31;110D 1164 11A8;CA31;110D 1164 11A8; # (쨱; 쨱; 쨱; 쨱; 쨱; ) HANGUL SYLLABLE JJYAEG +CA32;CA32;110D 1164 11A9;CA32;110D 1164 11A9; # (쨲; 쨲; 쨲; 쨲; 쨲; ) HANGUL SYLLABLE JJYAEGG +CA33;CA33;110D 1164 11AA;CA33;110D 1164 11AA; # (쨳; 쨳; 쨳; 쨳; 쨳; ) HANGUL SYLLABLE JJYAEGS +CA34;CA34;110D 1164 11AB;CA34;110D 1164 11AB; # (쨴; 쨴; 쨴; 쨴; 쨴; ) HANGUL SYLLABLE JJYAEN +CA35;CA35;110D 1164 11AC;CA35;110D 1164 11AC; # (쨵; 쨵; 쨵; 쨵; 쨵; ) HANGUL SYLLABLE JJYAENJ +CA36;CA36;110D 1164 11AD;CA36;110D 1164 11AD; # (쨶; 쨶; 쨶; 쨶; 쨶; ) HANGUL SYLLABLE JJYAENH +CA37;CA37;110D 1164 11AE;CA37;110D 1164 11AE; # (쨷; 쨷; 쨷; 쨷; 쨷; ) HANGUL SYLLABLE JJYAED +CA38;CA38;110D 1164 11AF;CA38;110D 1164 11AF; # (쨸; 쨸; 쨸; 쨸; 쨸; ) HANGUL SYLLABLE JJYAEL +CA39;CA39;110D 1164 11B0;CA39;110D 1164 11B0; # (쨹; 쨹; 쨹; 쨹; 쨹; ) HANGUL SYLLABLE JJYAELG +CA3A;CA3A;110D 1164 11B1;CA3A;110D 1164 11B1; # (쨺; 쨺; 쨺; 쨺; 쨺; ) HANGUL SYLLABLE JJYAELM +CA3B;CA3B;110D 1164 11B2;CA3B;110D 1164 11B2; # (쨻; 쨻; 쨻; 쨻; 쨻; ) HANGUL SYLLABLE JJYAELB +CA3C;CA3C;110D 1164 11B3;CA3C;110D 1164 11B3; # (쨼; 쨼; 쨼; 쨼; 쨼; ) HANGUL SYLLABLE JJYAELS +CA3D;CA3D;110D 1164 11B4;CA3D;110D 1164 11B4; # (쨽; 쨽; 쨽; 쨽; 쨽; ) HANGUL SYLLABLE JJYAELT +CA3E;CA3E;110D 1164 11B5;CA3E;110D 1164 11B5; # (쨾; 쨾; 쨾; 쨾; 쨾; ) HANGUL SYLLABLE JJYAELP +CA3F;CA3F;110D 1164 11B6;CA3F;110D 1164 11B6; # (쨿; 쨿; 쨿; 쨿; 쨿; ) HANGUL SYLLABLE JJYAELH +CA40;CA40;110D 1164 11B7;CA40;110D 1164 11B7; # (쩀; 쩀; 쩀; 쩀; 쩀; ) HANGUL SYLLABLE JJYAEM +CA41;CA41;110D 1164 11B8;CA41;110D 1164 11B8; # (쩁; 쩁; 쩁; 쩁; 쩁; ) HANGUL SYLLABLE JJYAEB +CA42;CA42;110D 1164 11B9;CA42;110D 1164 11B9; # (쩂; 쩂; 쩂; 쩂; 쩂; ) HANGUL SYLLABLE JJYAEBS +CA43;CA43;110D 1164 11BA;CA43;110D 1164 11BA; # (쩃; 쩃; 쩃; 쩃; 쩃; ) HANGUL SYLLABLE JJYAES +CA44;CA44;110D 1164 11BB;CA44;110D 1164 11BB; # (쩄; 쩄; 쩄; 쩄; 쩄; ) HANGUL SYLLABLE JJYAESS +CA45;CA45;110D 1164 11BC;CA45;110D 1164 11BC; # (쩅; 쩅; 쩅; 쩅; 쩅; ) HANGUL SYLLABLE JJYAENG +CA46;CA46;110D 1164 11BD;CA46;110D 1164 11BD; # (쩆; 쩆; 쩆; 쩆; 쩆; ) HANGUL SYLLABLE JJYAEJ +CA47;CA47;110D 1164 11BE;CA47;110D 1164 11BE; # (쩇; 쩇; 쩇; 쩇; 쩇; ) HANGUL SYLLABLE JJYAEC +CA48;CA48;110D 1164 11BF;CA48;110D 1164 11BF; # (쩈; 쩈; 쩈; 쩈; 쩈; ) HANGUL SYLLABLE JJYAEK +CA49;CA49;110D 1164 11C0;CA49;110D 1164 11C0; # (쩉; 쩉; 쩉; 쩉; 쩉; ) HANGUL SYLLABLE JJYAET +CA4A;CA4A;110D 1164 11C1;CA4A;110D 1164 11C1; # (쩊; 쩊; 쩊; 쩊; 쩊; ) HANGUL SYLLABLE JJYAEP +CA4B;CA4B;110D 1164 11C2;CA4B;110D 1164 11C2; # (쩋; 쩋; 쩋; 쩋; 쩋; ) HANGUL SYLLABLE JJYAEH +CA4C;CA4C;110D 1165;CA4C;110D 1165; # (쩌; 쩌; 쩌; 쩌; 쩌; ) HANGUL SYLLABLE JJEO +CA4D;CA4D;110D 1165 11A8;CA4D;110D 1165 11A8; # (쩍; 쩍; 쩍; 쩍; 쩍; ) HANGUL SYLLABLE JJEOG +CA4E;CA4E;110D 1165 11A9;CA4E;110D 1165 11A9; # (쩎; 쩎; 쩎; 쩎; 쩎; ) HANGUL SYLLABLE JJEOGG +CA4F;CA4F;110D 1165 11AA;CA4F;110D 1165 11AA; # (쩏; 쩏; 쩏; 쩏; 쩏; ) HANGUL SYLLABLE JJEOGS +CA50;CA50;110D 1165 11AB;CA50;110D 1165 11AB; # (쩐; 쩐; 쩐; 쩐; 쩐; ) HANGUL SYLLABLE JJEON +CA51;CA51;110D 1165 11AC;CA51;110D 1165 11AC; # (쩑; 쩑; 쩑; 쩑; 쩑; ) HANGUL SYLLABLE JJEONJ +CA52;CA52;110D 1165 11AD;CA52;110D 1165 11AD; # (쩒; 쩒; 쩒; 쩒; 쩒; ) HANGUL SYLLABLE JJEONH +CA53;CA53;110D 1165 11AE;CA53;110D 1165 11AE; # (쩓; 쩓; 쩓; 쩓; 쩓; ) HANGUL SYLLABLE JJEOD +CA54;CA54;110D 1165 11AF;CA54;110D 1165 11AF; # (쩔; 쩔; 쩔; 쩔; 쩔; ) HANGUL SYLLABLE JJEOL +CA55;CA55;110D 1165 11B0;CA55;110D 1165 11B0; # (쩕; 쩕; 쩕; 쩕; 쩕; ) HANGUL SYLLABLE JJEOLG +CA56;CA56;110D 1165 11B1;CA56;110D 1165 11B1; # (쩖; 쩖; 쩖; 쩖; 쩖; ) HANGUL SYLLABLE JJEOLM +CA57;CA57;110D 1165 11B2;CA57;110D 1165 11B2; # (쩗; 쩗; 쩗; 쩗; 쩗; ) HANGUL SYLLABLE JJEOLB +CA58;CA58;110D 1165 11B3;CA58;110D 1165 11B3; # (쩘; 쩘; 쩘; 쩘; 쩘; ) HANGUL SYLLABLE JJEOLS +CA59;CA59;110D 1165 11B4;CA59;110D 1165 11B4; # (쩙; 쩙; 쩙; 쩙; 쩙; ) HANGUL SYLLABLE JJEOLT +CA5A;CA5A;110D 1165 11B5;CA5A;110D 1165 11B5; # (쩚; 쩚; 쩚; 쩚; 쩚; ) HANGUL SYLLABLE JJEOLP +CA5B;CA5B;110D 1165 11B6;CA5B;110D 1165 11B6; # (쩛; 쩛; 쩛; 쩛; 쩛; ) HANGUL SYLLABLE JJEOLH +CA5C;CA5C;110D 1165 11B7;CA5C;110D 1165 11B7; # (쩜; 쩜; 쩜; 쩜; 쩜; ) HANGUL SYLLABLE JJEOM +CA5D;CA5D;110D 1165 11B8;CA5D;110D 1165 11B8; # (쩝; 쩝; 쩝; 쩝; 쩝; ) HANGUL SYLLABLE JJEOB +CA5E;CA5E;110D 1165 11B9;CA5E;110D 1165 11B9; # (쩞; 쩞; 쩞; 쩞; 쩞; ) HANGUL SYLLABLE JJEOBS +CA5F;CA5F;110D 1165 11BA;CA5F;110D 1165 11BA; # (쩟; 쩟; 쩟; 쩟; 쩟; ) HANGUL SYLLABLE JJEOS +CA60;CA60;110D 1165 11BB;CA60;110D 1165 11BB; # (쩠; 쩠; 쩠; 쩠; 쩠; ) HANGUL SYLLABLE JJEOSS +CA61;CA61;110D 1165 11BC;CA61;110D 1165 11BC; # (쩡; 쩡; 쩡; 쩡; 쩡; ) HANGUL SYLLABLE JJEONG +CA62;CA62;110D 1165 11BD;CA62;110D 1165 11BD; # (쩢; 쩢; 쩢; 쩢; 쩢; ) HANGUL SYLLABLE JJEOJ +CA63;CA63;110D 1165 11BE;CA63;110D 1165 11BE; # (쩣; 쩣; 쩣; 쩣; 쩣; ) HANGUL SYLLABLE JJEOC +CA64;CA64;110D 1165 11BF;CA64;110D 1165 11BF; # (쩤; 쩤; 쩤; 쩤; 쩤; ) HANGUL SYLLABLE JJEOK +CA65;CA65;110D 1165 11C0;CA65;110D 1165 11C0; # (쩥; 쩥; 쩥; 쩥; 쩥; ) HANGUL SYLLABLE JJEOT +CA66;CA66;110D 1165 11C1;CA66;110D 1165 11C1; # (쩦; 쩦; 쩦; 쩦; 쩦; ) HANGUL SYLLABLE JJEOP +CA67;CA67;110D 1165 11C2;CA67;110D 1165 11C2; # (쩧; 쩧; 쩧; 쩧; 쩧; ) HANGUL SYLLABLE JJEOH +CA68;CA68;110D 1166;CA68;110D 1166; # (쩨; 쩨; 쩨; 쩨; 쩨; ) HANGUL SYLLABLE JJE +CA69;CA69;110D 1166 11A8;CA69;110D 1166 11A8; # (쩩; 쩩; 쩩; 쩩; 쩩; ) HANGUL SYLLABLE JJEG +CA6A;CA6A;110D 1166 11A9;CA6A;110D 1166 11A9; # (쩪; 쩪; 쩪; 쩪; 쩪; ) HANGUL SYLLABLE JJEGG +CA6B;CA6B;110D 1166 11AA;CA6B;110D 1166 11AA; # (쩫; 쩫; 쩫; 쩫; 쩫; ) HANGUL SYLLABLE JJEGS +CA6C;CA6C;110D 1166 11AB;CA6C;110D 1166 11AB; # (쩬; 쩬; 쩬; 쩬; 쩬; ) HANGUL SYLLABLE JJEN +CA6D;CA6D;110D 1166 11AC;CA6D;110D 1166 11AC; # (쩭; 쩭; 쩭; 쩭; 쩭; ) HANGUL SYLLABLE JJENJ +CA6E;CA6E;110D 1166 11AD;CA6E;110D 1166 11AD; # (쩮; 쩮; 쩮; 쩮; 쩮; ) HANGUL SYLLABLE JJENH +CA6F;CA6F;110D 1166 11AE;CA6F;110D 1166 11AE; # (쩯; 쩯; 쩯; 쩯; 쩯; ) HANGUL SYLLABLE JJED +CA70;CA70;110D 1166 11AF;CA70;110D 1166 11AF; # (쩰; 쩰; 쩰; 쩰; 쩰; ) HANGUL SYLLABLE JJEL +CA71;CA71;110D 1166 11B0;CA71;110D 1166 11B0; # (쩱; 쩱; 쩱; 쩱; 쩱; ) HANGUL SYLLABLE JJELG +CA72;CA72;110D 1166 11B1;CA72;110D 1166 11B1; # (쩲; 쩲; 쩲; 쩲; 쩲; ) HANGUL SYLLABLE JJELM +CA73;CA73;110D 1166 11B2;CA73;110D 1166 11B2; # (쩳; 쩳; 쩳; 쩳; 쩳; ) HANGUL SYLLABLE JJELB +CA74;CA74;110D 1166 11B3;CA74;110D 1166 11B3; # (쩴; 쩴; 쩴; 쩴; 쩴; ) HANGUL SYLLABLE JJELS +CA75;CA75;110D 1166 11B4;CA75;110D 1166 11B4; # (쩵; 쩵; 쩵; 쩵; 쩵; ) HANGUL SYLLABLE JJELT +CA76;CA76;110D 1166 11B5;CA76;110D 1166 11B5; # (쩶; 쩶; 쩶; 쩶; 쩶; ) HANGUL SYLLABLE JJELP +CA77;CA77;110D 1166 11B6;CA77;110D 1166 11B6; # (쩷; 쩷; 쩷; 쩷; 쩷; ) HANGUL SYLLABLE JJELH +CA78;CA78;110D 1166 11B7;CA78;110D 1166 11B7; # (쩸; 쩸; 쩸; 쩸; 쩸; ) HANGUL SYLLABLE JJEM +CA79;CA79;110D 1166 11B8;CA79;110D 1166 11B8; # (쩹; 쩹; 쩹; 쩹; 쩹; ) HANGUL SYLLABLE JJEB +CA7A;CA7A;110D 1166 11B9;CA7A;110D 1166 11B9; # (쩺; 쩺; 쩺; 쩺; 쩺; ) HANGUL SYLLABLE JJEBS +CA7B;CA7B;110D 1166 11BA;CA7B;110D 1166 11BA; # (쩻; 쩻; 쩻; 쩻; 쩻; ) HANGUL SYLLABLE JJES +CA7C;CA7C;110D 1166 11BB;CA7C;110D 1166 11BB; # (쩼; 쩼; 쩼; 쩼; 쩼; ) HANGUL SYLLABLE JJESS +CA7D;CA7D;110D 1166 11BC;CA7D;110D 1166 11BC; # (쩽; 쩽; 쩽; 쩽; 쩽; ) HANGUL SYLLABLE JJENG +CA7E;CA7E;110D 1166 11BD;CA7E;110D 1166 11BD; # (쩾; 쩾; 쩾; 쩾; 쩾; ) HANGUL SYLLABLE JJEJ +CA7F;CA7F;110D 1166 11BE;CA7F;110D 1166 11BE; # (쩿; 쩿; 쩿; 쩿; 쩿; ) HANGUL SYLLABLE JJEC +CA80;CA80;110D 1166 11BF;CA80;110D 1166 11BF; # (쪀; 쪀; 쪀; 쪀; 쪀; ) HANGUL SYLLABLE JJEK +CA81;CA81;110D 1166 11C0;CA81;110D 1166 11C0; # (쪁; 쪁; 쪁; 쪁; 쪁; ) HANGUL SYLLABLE JJET +CA82;CA82;110D 1166 11C1;CA82;110D 1166 11C1; # (쪂; 쪂; 쪂; 쪂; 쪂; ) HANGUL SYLLABLE JJEP +CA83;CA83;110D 1166 11C2;CA83;110D 1166 11C2; # (쪃; 쪃; 쪃; 쪃; 쪃; ) HANGUL SYLLABLE JJEH +CA84;CA84;110D 1167;CA84;110D 1167; # (쪄; 쪄; 쪄; 쪄; 쪄; ) HANGUL SYLLABLE JJYEO +CA85;CA85;110D 1167 11A8;CA85;110D 1167 11A8; # (쪅; 쪅; 쪅; 쪅; 쪅; ) HANGUL SYLLABLE JJYEOG +CA86;CA86;110D 1167 11A9;CA86;110D 1167 11A9; # (쪆; 쪆; 쪆; 쪆; 쪆; ) HANGUL SYLLABLE JJYEOGG +CA87;CA87;110D 1167 11AA;CA87;110D 1167 11AA; # (쪇; 쪇; 쪇; 쪇; 쪇; ) HANGUL SYLLABLE JJYEOGS +CA88;CA88;110D 1167 11AB;CA88;110D 1167 11AB; # (쪈; 쪈; 쪈; 쪈; 쪈; ) HANGUL SYLLABLE JJYEON +CA89;CA89;110D 1167 11AC;CA89;110D 1167 11AC; # (쪉; 쪉; 쪉; 쪉; 쪉; ) HANGUL SYLLABLE JJYEONJ +CA8A;CA8A;110D 1167 11AD;CA8A;110D 1167 11AD; # (쪊; 쪊; 쪊; 쪊; 쪊; ) HANGUL SYLLABLE JJYEONH +CA8B;CA8B;110D 1167 11AE;CA8B;110D 1167 11AE; # (쪋; 쪋; 쪋; 쪋; 쪋; ) HANGUL SYLLABLE JJYEOD +CA8C;CA8C;110D 1167 11AF;CA8C;110D 1167 11AF; # (쪌; 쪌; 쪌; 쪌; 쪌; ) HANGUL SYLLABLE JJYEOL +CA8D;CA8D;110D 1167 11B0;CA8D;110D 1167 11B0; # (쪍; 쪍; 쪍; 쪍; 쪍; ) HANGUL SYLLABLE JJYEOLG +CA8E;CA8E;110D 1167 11B1;CA8E;110D 1167 11B1; # (쪎; 쪎; 쪎; 쪎; 쪎; ) HANGUL SYLLABLE JJYEOLM +CA8F;CA8F;110D 1167 11B2;CA8F;110D 1167 11B2; # (쪏; 쪏; 쪏; 쪏; 쪏; ) HANGUL SYLLABLE JJYEOLB +CA90;CA90;110D 1167 11B3;CA90;110D 1167 11B3; # (쪐; 쪐; 쪐; 쪐; 쪐; ) HANGUL SYLLABLE JJYEOLS +CA91;CA91;110D 1167 11B4;CA91;110D 1167 11B4; # (쪑; 쪑; 쪑; 쪑; 쪑; ) HANGUL SYLLABLE JJYEOLT +CA92;CA92;110D 1167 11B5;CA92;110D 1167 11B5; # (쪒; 쪒; 쪒; 쪒; 쪒; ) HANGUL SYLLABLE JJYEOLP +CA93;CA93;110D 1167 11B6;CA93;110D 1167 11B6; # (쪓; 쪓; 쪓; 쪓; 쪓; ) HANGUL SYLLABLE JJYEOLH +CA94;CA94;110D 1167 11B7;CA94;110D 1167 11B7; # (쪔; 쪔; 쪔; 쪔; 쪔; ) HANGUL SYLLABLE JJYEOM +CA95;CA95;110D 1167 11B8;CA95;110D 1167 11B8; # (쪕; 쪕; 쪕; 쪕; 쪕; ) HANGUL SYLLABLE JJYEOB +CA96;CA96;110D 1167 11B9;CA96;110D 1167 11B9; # (쪖; 쪖; 쪖; 쪖; 쪖; ) HANGUL SYLLABLE JJYEOBS +CA97;CA97;110D 1167 11BA;CA97;110D 1167 11BA; # (쪗; 쪗; 쪗; 쪗; 쪗; ) HANGUL SYLLABLE JJYEOS +CA98;CA98;110D 1167 11BB;CA98;110D 1167 11BB; # (쪘; 쪘; 쪘; 쪘; 쪘; ) HANGUL SYLLABLE JJYEOSS +CA99;CA99;110D 1167 11BC;CA99;110D 1167 11BC; # (쪙; 쪙; 쪙; 쪙; 쪙; ) HANGUL SYLLABLE JJYEONG +CA9A;CA9A;110D 1167 11BD;CA9A;110D 1167 11BD; # (쪚; 쪚; 쪚; 쪚; 쪚; ) HANGUL SYLLABLE JJYEOJ +CA9B;CA9B;110D 1167 11BE;CA9B;110D 1167 11BE; # (쪛; 쪛; 쪛; 쪛; 쪛; ) HANGUL SYLLABLE JJYEOC +CA9C;CA9C;110D 1167 11BF;CA9C;110D 1167 11BF; # (쪜; 쪜; 쪜; 쪜; 쪜; ) HANGUL SYLLABLE JJYEOK +CA9D;CA9D;110D 1167 11C0;CA9D;110D 1167 11C0; # (쪝; 쪝; 쪝; 쪝; 쪝; ) HANGUL SYLLABLE JJYEOT +CA9E;CA9E;110D 1167 11C1;CA9E;110D 1167 11C1; # (쪞; 쪞; 쪞; 쪞; 쪞; ) HANGUL SYLLABLE JJYEOP +CA9F;CA9F;110D 1167 11C2;CA9F;110D 1167 11C2; # (쪟; 쪟; 쪟; 쪟; 쪟; ) HANGUL SYLLABLE JJYEOH +CAA0;CAA0;110D 1168;CAA0;110D 1168; # (쪠; 쪠; 쪠; 쪠; 쪠; ) HANGUL SYLLABLE JJYE +CAA1;CAA1;110D 1168 11A8;CAA1;110D 1168 11A8; # (쪡; 쪡; 쪡; 쪡; 쪡; ) HANGUL SYLLABLE JJYEG +CAA2;CAA2;110D 1168 11A9;CAA2;110D 1168 11A9; # (쪢; 쪢; 쪢; 쪢; 쪢; ) HANGUL SYLLABLE JJYEGG +CAA3;CAA3;110D 1168 11AA;CAA3;110D 1168 11AA; # (쪣; 쪣; 쪣; 쪣; 쪣; ) HANGUL SYLLABLE JJYEGS +CAA4;CAA4;110D 1168 11AB;CAA4;110D 1168 11AB; # (쪤; 쪤; 쪤; 쪤; 쪤; ) HANGUL SYLLABLE JJYEN +CAA5;CAA5;110D 1168 11AC;CAA5;110D 1168 11AC; # (쪥; 쪥; 쪥; 쪥; 쪥; ) HANGUL SYLLABLE JJYENJ +CAA6;CAA6;110D 1168 11AD;CAA6;110D 1168 11AD; # (쪦; 쪦; 쪦; 쪦; 쪦; ) HANGUL SYLLABLE JJYENH +CAA7;CAA7;110D 1168 11AE;CAA7;110D 1168 11AE; # (쪧; 쪧; 쪧; 쪧; 쪧; ) HANGUL SYLLABLE JJYED +CAA8;CAA8;110D 1168 11AF;CAA8;110D 1168 11AF; # (쪨; 쪨; 쪨; 쪨; 쪨; ) HANGUL SYLLABLE JJYEL +CAA9;CAA9;110D 1168 11B0;CAA9;110D 1168 11B0; # (쪩; 쪩; 쪩; 쪩; 쪩; ) HANGUL SYLLABLE JJYELG +CAAA;CAAA;110D 1168 11B1;CAAA;110D 1168 11B1; # (쪪; 쪪; 쪪; 쪪; 쪪; ) HANGUL SYLLABLE JJYELM +CAAB;CAAB;110D 1168 11B2;CAAB;110D 1168 11B2; # (쪫; 쪫; 쪫; 쪫; 쪫; ) HANGUL SYLLABLE JJYELB +CAAC;CAAC;110D 1168 11B3;CAAC;110D 1168 11B3; # (쪬; 쪬; 쪬; 쪬; 쪬; ) HANGUL SYLLABLE JJYELS +CAAD;CAAD;110D 1168 11B4;CAAD;110D 1168 11B4; # (쪭; 쪭; 쪭; 쪭; 쪭; ) HANGUL SYLLABLE JJYELT +CAAE;CAAE;110D 1168 11B5;CAAE;110D 1168 11B5; # (쪮; 쪮; 쪮; 쪮; 쪮; ) HANGUL SYLLABLE JJYELP +CAAF;CAAF;110D 1168 11B6;CAAF;110D 1168 11B6; # (쪯; 쪯; 쪯; 쪯; 쪯; ) HANGUL SYLLABLE JJYELH +CAB0;CAB0;110D 1168 11B7;CAB0;110D 1168 11B7; # (쪰; 쪰; 쪰; 쪰; 쪰; ) HANGUL SYLLABLE JJYEM +CAB1;CAB1;110D 1168 11B8;CAB1;110D 1168 11B8; # (쪱; 쪱; 쪱; 쪱; 쪱; ) HANGUL SYLLABLE JJYEB +CAB2;CAB2;110D 1168 11B9;CAB2;110D 1168 11B9; # (쪲; 쪲; 쪲; 쪲; 쪲; ) HANGUL SYLLABLE JJYEBS +CAB3;CAB3;110D 1168 11BA;CAB3;110D 1168 11BA; # (쪳; 쪳; 쪳; 쪳; 쪳; ) HANGUL SYLLABLE JJYES +CAB4;CAB4;110D 1168 11BB;CAB4;110D 1168 11BB; # (쪴; 쪴; 쪴; 쪴; 쪴; ) HANGUL SYLLABLE JJYESS +CAB5;CAB5;110D 1168 11BC;CAB5;110D 1168 11BC; # (쪵; 쪵; 쪵; 쪵; 쪵; ) HANGUL SYLLABLE JJYENG +CAB6;CAB6;110D 1168 11BD;CAB6;110D 1168 11BD; # (쪶; 쪶; 쪶; 쪶; 쪶; ) HANGUL SYLLABLE JJYEJ +CAB7;CAB7;110D 1168 11BE;CAB7;110D 1168 11BE; # (쪷; 쪷; 쪷; 쪷; 쪷; ) HANGUL SYLLABLE JJYEC +CAB8;CAB8;110D 1168 11BF;CAB8;110D 1168 11BF; # (쪸; 쪸; 쪸; 쪸; 쪸; ) HANGUL SYLLABLE JJYEK +CAB9;CAB9;110D 1168 11C0;CAB9;110D 1168 11C0; # (쪹; 쪹; 쪹; 쪹; 쪹; ) HANGUL SYLLABLE JJYET +CABA;CABA;110D 1168 11C1;CABA;110D 1168 11C1; # (쪺; 쪺; 쪺; 쪺; 쪺; ) HANGUL SYLLABLE JJYEP +CABB;CABB;110D 1168 11C2;CABB;110D 1168 11C2; # (쪻; 쪻; 쪻; 쪻; 쪻; ) HANGUL SYLLABLE JJYEH +CABC;CABC;110D 1169;CABC;110D 1169; # (쪼; 쪼; 쪼; 쪼; 쪼; ) HANGUL SYLLABLE JJO +CABD;CABD;110D 1169 11A8;CABD;110D 1169 11A8; # (쪽; 쪽; 쪽; 쪽; 쪽; ) HANGUL SYLLABLE JJOG +CABE;CABE;110D 1169 11A9;CABE;110D 1169 11A9; # (쪾; 쪾; 쪾; 쪾; 쪾; ) HANGUL SYLLABLE JJOGG +CABF;CABF;110D 1169 11AA;CABF;110D 1169 11AA; # (쪿; 쪿; 쪿; 쪿; 쪿; ) HANGUL SYLLABLE JJOGS +CAC0;CAC0;110D 1169 11AB;CAC0;110D 1169 11AB; # (쫀; 쫀; 쫀; 쫀; 쫀; ) HANGUL SYLLABLE JJON +CAC1;CAC1;110D 1169 11AC;CAC1;110D 1169 11AC; # (쫁; 쫁; 쫁; 쫁; 쫁; ) HANGUL SYLLABLE JJONJ +CAC2;CAC2;110D 1169 11AD;CAC2;110D 1169 11AD; # (쫂; 쫂; 쫂; 쫂; 쫂; ) HANGUL SYLLABLE JJONH +CAC3;CAC3;110D 1169 11AE;CAC3;110D 1169 11AE; # (쫃; 쫃; 쫃; 쫃; 쫃; ) HANGUL SYLLABLE JJOD +CAC4;CAC4;110D 1169 11AF;CAC4;110D 1169 11AF; # (쫄; 쫄; 쫄; 쫄; 쫄; ) HANGUL SYLLABLE JJOL +CAC5;CAC5;110D 1169 11B0;CAC5;110D 1169 11B0; # (쫅; 쫅; 쫅; 쫅; 쫅; ) HANGUL SYLLABLE JJOLG +CAC6;CAC6;110D 1169 11B1;CAC6;110D 1169 11B1; # (쫆; 쫆; 쫆; 쫆; 쫆; ) HANGUL SYLLABLE JJOLM +CAC7;CAC7;110D 1169 11B2;CAC7;110D 1169 11B2; # (쫇; 쫇; 쫇; 쫇; 쫇; ) HANGUL SYLLABLE JJOLB +CAC8;CAC8;110D 1169 11B3;CAC8;110D 1169 11B3; # (쫈; 쫈; 쫈; 쫈; 쫈; ) HANGUL SYLLABLE JJOLS +CAC9;CAC9;110D 1169 11B4;CAC9;110D 1169 11B4; # (쫉; 쫉; 쫉; 쫉; 쫉; ) HANGUL SYLLABLE JJOLT +CACA;CACA;110D 1169 11B5;CACA;110D 1169 11B5; # (쫊; 쫊; 쫊; 쫊; 쫊; ) HANGUL SYLLABLE JJOLP +CACB;CACB;110D 1169 11B6;CACB;110D 1169 11B6; # (쫋; 쫋; 쫋; 쫋; 쫋; ) HANGUL SYLLABLE JJOLH +CACC;CACC;110D 1169 11B7;CACC;110D 1169 11B7; # (쫌; 쫌; 쫌; 쫌; 쫌; ) HANGUL SYLLABLE JJOM +CACD;CACD;110D 1169 11B8;CACD;110D 1169 11B8; # (쫍; 쫍; 쫍; 쫍; 쫍; ) HANGUL SYLLABLE JJOB +CACE;CACE;110D 1169 11B9;CACE;110D 1169 11B9; # (쫎; 쫎; 쫎; 쫎; 쫎; ) HANGUL SYLLABLE JJOBS +CACF;CACF;110D 1169 11BA;CACF;110D 1169 11BA; # (쫏; 쫏; 쫏; 쫏; 쫏; ) HANGUL SYLLABLE JJOS +CAD0;CAD0;110D 1169 11BB;CAD0;110D 1169 11BB; # (쫐; 쫐; 쫐; 쫐; 쫐; ) HANGUL SYLLABLE JJOSS +CAD1;CAD1;110D 1169 11BC;CAD1;110D 1169 11BC; # (쫑; 쫑; 쫑; 쫑; 쫑; ) HANGUL SYLLABLE JJONG +CAD2;CAD2;110D 1169 11BD;CAD2;110D 1169 11BD; # (쫒; 쫒; 쫒; 쫒; 쫒; ) HANGUL SYLLABLE JJOJ +CAD3;CAD3;110D 1169 11BE;CAD3;110D 1169 11BE; # (쫓; 쫓; 쫓; 쫓; 쫓; ) HANGUL SYLLABLE JJOC +CAD4;CAD4;110D 1169 11BF;CAD4;110D 1169 11BF; # (쫔; 쫔; 쫔; 쫔; 쫔; ) HANGUL SYLLABLE JJOK +CAD5;CAD5;110D 1169 11C0;CAD5;110D 1169 11C0; # (쫕; 쫕; 쫕; 쫕; 쫕; ) HANGUL SYLLABLE JJOT +CAD6;CAD6;110D 1169 11C1;CAD6;110D 1169 11C1; # (쫖; 쫖; 쫖; 쫖; 쫖; ) HANGUL SYLLABLE JJOP +CAD7;CAD7;110D 1169 11C2;CAD7;110D 1169 11C2; # (쫗; 쫗; 쫗; 쫗; 쫗; ) HANGUL SYLLABLE JJOH +CAD8;CAD8;110D 116A;CAD8;110D 116A; # (쫘; 쫘; 쫘; 쫘; 쫘; ) HANGUL SYLLABLE JJWA +CAD9;CAD9;110D 116A 11A8;CAD9;110D 116A 11A8; # (쫙; 쫙; 쫙; 쫙; 쫙; ) HANGUL SYLLABLE JJWAG +CADA;CADA;110D 116A 11A9;CADA;110D 116A 11A9; # (쫚; 쫚; 쫚; 쫚; 쫚; ) HANGUL SYLLABLE JJWAGG +CADB;CADB;110D 116A 11AA;CADB;110D 116A 11AA; # (쫛; 쫛; 쫛; 쫛; 쫛; ) HANGUL SYLLABLE JJWAGS +CADC;CADC;110D 116A 11AB;CADC;110D 116A 11AB; # (쫜; 쫜; 쫜; 쫜; 쫜; ) HANGUL SYLLABLE JJWAN +CADD;CADD;110D 116A 11AC;CADD;110D 116A 11AC; # (쫝; 쫝; 쫝; 쫝; 쫝; ) HANGUL SYLLABLE JJWANJ +CADE;CADE;110D 116A 11AD;CADE;110D 116A 11AD; # (쫞; 쫞; 쫞; 쫞; 쫞; ) HANGUL SYLLABLE JJWANH +CADF;CADF;110D 116A 11AE;CADF;110D 116A 11AE; # (쫟; 쫟; 쫟; 쫟; 쫟; ) HANGUL SYLLABLE JJWAD +CAE0;CAE0;110D 116A 11AF;CAE0;110D 116A 11AF; # (쫠; 쫠; 쫠; 쫠; 쫠; ) HANGUL SYLLABLE JJWAL +CAE1;CAE1;110D 116A 11B0;CAE1;110D 116A 11B0; # (쫡; 쫡; 쫡; 쫡; 쫡; ) HANGUL SYLLABLE JJWALG +CAE2;CAE2;110D 116A 11B1;CAE2;110D 116A 11B1; # (쫢; 쫢; 쫢; 쫢; 쫢; ) HANGUL SYLLABLE JJWALM +CAE3;CAE3;110D 116A 11B2;CAE3;110D 116A 11B2; # (쫣; 쫣; 쫣; 쫣; 쫣; ) HANGUL SYLLABLE JJWALB +CAE4;CAE4;110D 116A 11B3;CAE4;110D 116A 11B3; # (쫤; 쫤; 쫤; 쫤; 쫤; ) HANGUL SYLLABLE JJWALS +CAE5;CAE5;110D 116A 11B4;CAE5;110D 116A 11B4; # (쫥; 쫥; 쫥; 쫥; 쫥; ) HANGUL SYLLABLE JJWALT +CAE6;CAE6;110D 116A 11B5;CAE6;110D 116A 11B5; # (쫦; 쫦; 쫦; 쫦; 쫦; ) HANGUL SYLLABLE JJWALP +CAE7;CAE7;110D 116A 11B6;CAE7;110D 116A 11B6; # (쫧; 쫧; 쫧; 쫧; 쫧; ) HANGUL SYLLABLE JJWALH +CAE8;CAE8;110D 116A 11B7;CAE8;110D 116A 11B7; # (쫨; 쫨; 쫨; 쫨; 쫨; ) HANGUL SYLLABLE JJWAM +CAE9;CAE9;110D 116A 11B8;CAE9;110D 116A 11B8; # (쫩; 쫩; 쫩; 쫩; 쫩; ) HANGUL SYLLABLE JJWAB +CAEA;CAEA;110D 116A 11B9;CAEA;110D 116A 11B9; # (쫪; 쫪; 쫪; 쫪; 쫪; ) HANGUL SYLLABLE JJWABS +CAEB;CAEB;110D 116A 11BA;CAEB;110D 116A 11BA; # (쫫; 쫫; 쫫; 쫫; 쫫; ) HANGUL SYLLABLE JJWAS +CAEC;CAEC;110D 116A 11BB;CAEC;110D 116A 11BB; # (쫬; 쫬; 쫬; 쫬; 쫬; ) HANGUL SYLLABLE JJWASS +CAED;CAED;110D 116A 11BC;CAED;110D 116A 11BC; # (쫭; 쫭; 쫭; 쫭; 쫭; ) HANGUL SYLLABLE JJWANG +CAEE;CAEE;110D 116A 11BD;CAEE;110D 116A 11BD; # (쫮; 쫮; 쫮; 쫮; 쫮; ) HANGUL SYLLABLE JJWAJ +CAEF;CAEF;110D 116A 11BE;CAEF;110D 116A 11BE; # (쫯; 쫯; 쫯; 쫯; 쫯; ) HANGUL SYLLABLE JJWAC +CAF0;CAF0;110D 116A 11BF;CAF0;110D 116A 11BF; # (쫰; 쫰; 쫰; 쫰; 쫰; ) HANGUL SYLLABLE JJWAK +CAF1;CAF1;110D 116A 11C0;CAF1;110D 116A 11C0; # (쫱; 쫱; 쫱; 쫱; 쫱; ) HANGUL SYLLABLE JJWAT +CAF2;CAF2;110D 116A 11C1;CAF2;110D 116A 11C1; # (쫲; 쫲; 쫲; 쫲; 쫲; ) HANGUL SYLLABLE JJWAP +CAF3;CAF3;110D 116A 11C2;CAF3;110D 116A 11C2; # (쫳; 쫳; 쫳; 쫳; 쫳; ) HANGUL SYLLABLE JJWAH +CAF4;CAF4;110D 116B;CAF4;110D 116B; # (쫴; 쫴; 쫴; 쫴; 쫴; ) HANGUL SYLLABLE JJWAE +CAF5;CAF5;110D 116B 11A8;CAF5;110D 116B 11A8; # (쫵; 쫵; 쫵; 쫵; 쫵; ) HANGUL SYLLABLE JJWAEG +CAF6;CAF6;110D 116B 11A9;CAF6;110D 116B 11A9; # (쫶; 쫶; 쫶; 쫶; 쫶; ) HANGUL SYLLABLE JJWAEGG +CAF7;CAF7;110D 116B 11AA;CAF7;110D 116B 11AA; # (쫷; 쫷; 쫷; 쫷; 쫷; ) HANGUL SYLLABLE JJWAEGS +CAF8;CAF8;110D 116B 11AB;CAF8;110D 116B 11AB; # (쫸; 쫸; 쫸; 쫸; 쫸; ) HANGUL SYLLABLE JJWAEN +CAF9;CAF9;110D 116B 11AC;CAF9;110D 116B 11AC; # (쫹; 쫹; 쫹; 쫹; 쫹; ) HANGUL SYLLABLE JJWAENJ +CAFA;CAFA;110D 116B 11AD;CAFA;110D 116B 11AD; # (쫺; 쫺; 쫺; 쫺; 쫺; ) HANGUL SYLLABLE JJWAENH +CAFB;CAFB;110D 116B 11AE;CAFB;110D 116B 11AE; # (쫻; 쫻; 쫻; 쫻; 쫻; ) HANGUL SYLLABLE JJWAED +CAFC;CAFC;110D 116B 11AF;CAFC;110D 116B 11AF; # (쫼; 쫼; 쫼; 쫼; 쫼; ) HANGUL SYLLABLE JJWAEL +CAFD;CAFD;110D 116B 11B0;CAFD;110D 116B 11B0; # (쫽; 쫽; 쫽; 쫽; 쫽; ) HANGUL SYLLABLE JJWAELG +CAFE;CAFE;110D 116B 11B1;CAFE;110D 116B 11B1; # (쫾; 쫾; 쫾; 쫾; 쫾; ) HANGUL SYLLABLE JJWAELM +CAFF;CAFF;110D 116B 11B2;CAFF;110D 116B 11B2; # (쫿; 쫿; 쫿; 쫿; 쫿; ) HANGUL SYLLABLE JJWAELB +CB00;CB00;110D 116B 11B3;CB00;110D 116B 11B3; # (쬀; 쬀; 쬀; 쬀; 쬀; ) HANGUL SYLLABLE JJWAELS +CB01;CB01;110D 116B 11B4;CB01;110D 116B 11B4; # (쬁; 쬁; 쬁; 쬁; 쬁; ) HANGUL SYLLABLE JJWAELT +CB02;CB02;110D 116B 11B5;CB02;110D 116B 11B5; # (쬂; 쬂; 쬂; 쬂; 쬂; ) HANGUL SYLLABLE JJWAELP +CB03;CB03;110D 116B 11B6;CB03;110D 116B 11B6; # (쬃; 쬃; 쬃; 쬃; 쬃; ) HANGUL SYLLABLE JJWAELH +CB04;CB04;110D 116B 11B7;CB04;110D 116B 11B7; # (쬄; 쬄; 쬄; 쬄; 쬄; ) HANGUL SYLLABLE JJWAEM +CB05;CB05;110D 116B 11B8;CB05;110D 116B 11B8; # (쬅; 쬅; 쬅; 쬅; 쬅; ) HANGUL SYLLABLE JJWAEB +CB06;CB06;110D 116B 11B9;CB06;110D 116B 11B9; # (쬆; 쬆; 쬆; 쬆; 쬆; ) HANGUL SYLLABLE JJWAEBS +CB07;CB07;110D 116B 11BA;CB07;110D 116B 11BA; # (쬇; 쬇; 쬇; 쬇; 쬇; ) HANGUL SYLLABLE JJWAES +CB08;CB08;110D 116B 11BB;CB08;110D 116B 11BB; # (쬈; 쬈; 쬈; 쬈; 쬈; ) HANGUL SYLLABLE JJWAESS +CB09;CB09;110D 116B 11BC;CB09;110D 116B 11BC; # (쬉; 쬉; 쬉; 쬉; 쬉; ) HANGUL SYLLABLE JJWAENG +CB0A;CB0A;110D 116B 11BD;CB0A;110D 116B 11BD; # (쬊; 쬊; 쬊; 쬊; 쬊; ) HANGUL SYLLABLE JJWAEJ +CB0B;CB0B;110D 116B 11BE;CB0B;110D 116B 11BE; # (쬋; 쬋; 쬋; 쬋; 쬋; ) HANGUL SYLLABLE JJWAEC +CB0C;CB0C;110D 116B 11BF;CB0C;110D 116B 11BF; # (쬌; 쬌; 쬌; 쬌; 쬌; ) HANGUL SYLLABLE JJWAEK +CB0D;CB0D;110D 116B 11C0;CB0D;110D 116B 11C0; # (쬍; 쬍; 쬍; 쬍; 쬍; ) HANGUL SYLLABLE JJWAET +CB0E;CB0E;110D 116B 11C1;CB0E;110D 116B 11C1; # (쬎; 쬎; 쬎; 쬎; 쬎; ) HANGUL SYLLABLE JJWAEP +CB0F;CB0F;110D 116B 11C2;CB0F;110D 116B 11C2; # (쬏; 쬏; 쬏; 쬏; 쬏; ) HANGUL SYLLABLE JJWAEH +CB10;CB10;110D 116C;CB10;110D 116C; # (쬐; 쬐; 쬐; 쬐; 쬐; ) HANGUL SYLLABLE JJOE +CB11;CB11;110D 116C 11A8;CB11;110D 116C 11A8; # (쬑; 쬑; 쬑; 쬑; 쬑; ) HANGUL SYLLABLE JJOEG +CB12;CB12;110D 116C 11A9;CB12;110D 116C 11A9; # (쬒; 쬒; 쬒; 쬒; 쬒; ) HANGUL SYLLABLE JJOEGG +CB13;CB13;110D 116C 11AA;CB13;110D 116C 11AA; # (쬓; 쬓; 쬓; 쬓; 쬓; ) HANGUL SYLLABLE JJOEGS +CB14;CB14;110D 116C 11AB;CB14;110D 116C 11AB; # (쬔; 쬔; 쬔; 쬔; 쬔; ) HANGUL SYLLABLE JJOEN +CB15;CB15;110D 116C 11AC;CB15;110D 116C 11AC; # (쬕; 쬕; 쬕; 쬕; 쬕; ) HANGUL SYLLABLE JJOENJ +CB16;CB16;110D 116C 11AD;CB16;110D 116C 11AD; # (쬖; 쬖; 쬖; 쬖; 쬖; ) HANGUL SYLLABLE JJOENH +CB17;CB17;110D 116C 11AE;CB17;110D 116C 11AE; # (쬗; 쬗; 쬗; 쬗; 쬗; ) HANGUL SYLLABLE JJOED +CB18;CB18;110D 116C 11AF;CB18;110D 116C 11AF; # (쬘; 쬘; 쬘; 쬘; 쬘; ) HANGUL SYLLABLE JJOEL +CB19;CB19;110D 116C 11B0;CB19;110D 116C 11B0; # (쬙; 쬙; 쬙; 쬙; 쬙; ) HANGUL SYLLABLE JJOELG +CB1A;CB1A;110D 116C 11B1;CB1A;110D 116C 11B1; # (쬚; 쬚; 쬚; 쬚; 쬚; ) HANGUL SYLLABLE JJOELM +CB1B;CB1B;110D 116C 11B2;CB1B;110D 116C 11B2; # (쬛; 쬛; 쬛; 쬛; 쬛; ) HANGUL SYLLABLE JJOELB +CB1C;CB1C;110D 116C 11B3;CB1C;110D 116C 11B3; # (쬜; 쬜; 쬜; 쬜; 쬜; ) HANGUL SYLLABLE JJOELS +CB1D;CB1D;110D 116C 11B4;CB1D;110D 116C 11B4; # (쬝; 쬝; 쬝; 쬝; 쬝; ) HANGUL SYLLABLE JJOELT +CB1E;CB1E;110D 116C 11B5;CB1E;110D 116C 11B5; # (쬞; 쬞; 쬞; 쬞; 쬞; ) HANGUL SYLLABLE JJOELP +CB1F;CB1F;110D 116C 11B6;CB1F;110D 116C 11B6; # (쬟; 쬟; 쬟; 쬟; 쬟; ) HANGUL SYLLABLE JJOELH +CB20;CB20;110D 116C 11B7;CB20;110D 116C 11B7; # (쬠; 쬠; 쬠; 쬠; 쬠; ) HANGUL SYLLABLE JJOEM +CB21;CB21;110D 116C 11B8;CB21;110D 116C 11B8; # (쬡; 쬡; 쬡; 쬡; 쬡; ) HANGUL SYLLABLE JJOEB +CB22;CB22;110D 116C 11B9;CB22;110D 116C 11B9; # (쬢; 쬢; 쬢; 쬢; 쬢; ) HANGUL SYLLABLE JJOEBS +CB23;CB23;110D 116C 11BA;CB23;110D 116C 11BA; # (쬣; 쬣; 쬣; 쬣; 쬣; ) HANGUL SYLLABLE JJOES +CB24;CB24;110D 116C 11BB;CB24;110D 116C 11BB; # (쬤; 쬤; 쬤; 쬤; 쬤; ) HANGUL SYLLABLE JJOESS +CB25;CB25;110D 116C 11BC;CB25;110D 116C 11BC; # (쬥; 쬥; 쬥; 쬥; 쬥; ) HANGUL SYLLABLE JJOENG +CB26;CB26;110D 116C 11BD;CB26;110D 116C 11BD; # (쬦; 쬦; 쬦; 쬦; 쬦; ) HANGUL SYLLABLE JJOEJ +CB27;CB27;110D 116C 11BE;CB27;110D 116C 11BE; # (쬧; 쬧; 쬧; 쬧; 쬧; ) HANGUL SYLLABLE JJOEC +CB28;CB28;110D 116C 11BF;CB28;110D 116C 11BF; # (쬨; 쬨; 쬨; 쬨; 쬨; ) HANGUL SYLLABLE JJOEK +CB29;CB29;110D 116C 11C0;CB29;110D 116C 11C0; # (쬩; 쬩; 쬩; 쬩; 쬩; ) HANGUL SYLLABLE JJOET +CB2A;CB2A;110D 116C 11C1;CB2A;110D 116C 11C1; # (쬪; 쬪; 쬪; 쬪; 쬪; ) HANGUL SYLLABLE JJOEP +CB2B;CB2B;110D 116C 11C2;CB2B;110D 116C 11C2; # (쬫; 쬫; 쬫; 쬫; 쬫; ) HANGUL SYLLABLE JJOEH +CB2C;CB2C;110D 116D;CB2C;110D 116D; # (쬬; 쬬; 쬬; 쬬; 쬬; ) HANGUL SYLLABLE JJYO +CB2D;CB2D;110D 116D 11A8;CB2D;110D 116D 11A8; # (쬭; 쬭; 쬭; 쬭; 쬭; ) HANGUL SYLLABLE JJYOG +CB2E;CB2E;110D 116D 11A9;CB2E;110D 116D 11A9; # (쬮; 쬮; 쬮; 쬮; 쬮; ) HANGUL SYLLABLE JJYOGG +CB2F;CB2F;110D 116D 11AA;CB2F;110D 116D 11AA; # (쬯; 쬯; 쬯; 쬯; 쬯; ) HANGUL SYLLABLE JJYOGS +CB30;CB30;110D 116D 11AB;CB30;110D 116D 11AB; # (쬰; 쬰; 쬰; 쬰; 쬰; ) HANGUL SYLLABLE JJYON +CB31;CB31;110D 116D 11AC;CB31;110D 116D 11AC; # (쬱; 쬱; 쬱; 쬱; 쬱; ) HANGUL SYLLABLE JJYONJ +CB32;CB32;110D 116D 11AD;CB32;110D 116D 11AD; # (쬲; 쬲; 쬲; 쬲; 쬲; ) HANGUL SYLLABLE JJYONH +CB33;CB33;110D 116D 11AE;CB33;110D 116D 11AE; # (쬳; 쬳; 쬳; 쬳; 쬳; ) HANGUL SYLLABLE JJYOD +CB34;CB34;110D 116D 11AF;CB34;110D 116D 11AF; # (쬴; 쬴; 쬴; 쬴; 쬴; ) HANGUL SYLLABLE JJYOL +CB35;CB35;110D 116D 11B0;CB35;110D 116D 11B0; # (쬵; 쬵; 쬵; 쬵; 쬵; ) HANGUL SYLLABLE JJYOLG +CB36;CB36;110D 116D 11B1;CB36;110D 116D 11B1; # (쬶; 쬶; 쬶; 쬶; 쬶; ) HANGUL SYLLABLE JJYOLM +CB37;CB37;110D 116D 11B2;CB37;110D 116D 11B2; # (쬷; 쬷; 쬷; 쬷; 쬷; ) HANGUL SYLLABLE JJYOLB +CB38;CB38;110D 116D 11B3;CB38;110D 116D 11B3; # (쬸; 쬸; 쬸; 쬸; 쬸; ) HANGUL SYLLABLE JJYOLS +CB39;CB39;110D 116D 11B4;CB39;110D 116D 11B4; # (쬹; 쬹; 쬹; 쬹; 쬹; ) HANGUL SYLLABLE JJYOLT +CB3A;CB3A;110D 116D 11B5;CB3A;110D 116D 11B5; # (쬺; 쬺; 쬺; 쬺; 쬺; ) HANGUL SYLLABLE JJYOLP +CB3B;CB3B;110D 116D 11B6;CB3B;110D 116D 11B6; # (쬻; 쬻; 쬻; 쬻; 쬻; ) HANGUL SYLLABLE JJYOLH +CB3C;CB3C;110D 116D 11B7;CB3C;110D 116D 11B7; # (쬼; 쬼; 쬼; 쬼; 쬼; ) HANGUL SYLLABLE JJYOM +CB3D;CB3D;110D 116D 11B8;CB3D;110D 116D 11B8; # (쬽; 쬽; 쬽; 쬽; 쬽; ) HANGUL SYLLABLE JJYOB +CB3E;CB3E;110D 116D 11B9;CB3E;110D 116D 11B9; # (쬾; 쬾; 쬾; 쬾; 쬾; ) HANGUL SYLLABLE JJYOBS +CB3F;CB3F;110D 116D 11BA;CB3F;110D 116D 11BA; # (쬿; 쬿; 쬿; 쬿; 쬿; ) HANGUL SYLLABLE JJYOS +CB40;CB40;110D 116D 11BB;CB40;110D 116D 11BB; # (쭀; 쭀; 쭀; 쭀; 쭀; ) HANGUL SYLLABLE JJYOSS +CB41;CB41;110D 116D 11BC;CB41;110D 116D 11BC; # (쭁; 쭁; 쭁; 쭁; 쭁; ) HANGUL SYLLABLE JJYONG +CB42;CB42;110D 116D 11BD;CB42;110D 116D 11BD; # (쭂; 쭂; 쭂; 쭂; 쭂; ) HANGUL SYLLABLE JJYOJ +CB43;CB43;110D 116D 11BE;CB43;110D 116D 11BE; # (쭃; 쭃; 쭃; 쭃; 쭃; ) HANGUL SYLLABLE JJYOC +CB44;CB44;110D 116D 11BF;CB44;110D 116D 11BF; # (쭄; 쭄; 쭄; 쭄; 쭄; ) HANGUL SYLLABLE JJYOK +CB45;CB45;110D 116D 11C0;CB45;110D 116D 11C0; # (쭅; 쭅; 쭅; 쭅; 쭅; ) HANGUL SYLLABLE JJYOT +CB46;CB46;110D 116D 11C1;CB46;110D 116D 11C1; # (쭆; 쭆; 쭆; 쭆; 쭆; ) HANGUL SYLLABLE JJYOP +CB47;CB47;110D 116D 11C2;CB47;110D 116D 11C2; # (쭇; 쭇; 쭇; 쭇; 쭇; ) HANGUL SYLLABLE JJYOH +CB48;CB48;110D 116E;CB48;110D 116E; # (쭈; 쭈; 쭈; 쭈; 쭈; ) HANGUL SYLLABLE JJU +CB49;CB49;110D 116E 11A8;CB49;110D 116E 11A8; # (쭉; 쭉; 쭉; 쭉; 쭉; ) HANGUL SYLLABLE JJUG +CB4A;CB4A;110D 116E 11A9;CB4A;110D 116E 11A9; # (쭊; 쭊; 쭊; 쭊; 쭊; ) HANGUL SYLLABLE JJUGG +CB4B;CB4B;110D 116E 11AA;CB4B;110D 116E 11AA; # (쭋; 쭋; 쭋; 쭋; 쭋; ) HANGUL SYLLABLE JJUGS +CB4C;CB4C;110D 116E 11AB;CB4C;110D 116E 11AB; # (쭌; 쭌; 쭌; 쭌; 쭌; ) HANGUL SYLLABLE JJUN +CB4D;CB4D;110D 116E 11AC;CB4D;110D 116E 11AC; # (쭍; 쭍; 쭍; 쭍; 쭍; ) HANGUL SYLLABLE JJUNJ +CB4E;CB4E;110D 116E 11AD;CB4E;110D 116E 11AD; # (쭎; 쭎; 쭎; 쭎; 쭎; ) HANGUL SYLLABLE JJUNH +CB4F;CB4F;110D 116E 11AE;CB4F;110D 116E 11AE; # (쭏; 쭏; 쭏; 쭏; 쭏; ) HANGUL SYLLABLE JJUD +CB50;CB50;110D 116E 11AF;CB50;110D 116E 11AF; # (쭐; 쭐; 쭐; 쭐; 쭐; ) HANGUL SYLLABLE JJUL +CB51;CB51;110D 116E 11B0;CB51;110D 116E 11B0; # (쭑; 쭑; 쭑; 쭑; 쭑; ) HANGUL SYLLABLE JJULG +CB52;CB52;110D 116E 11B1;CB52;110D 116E 11B1; # (쭒; 쭒; 쭒; 쭒; 쭒; ) HANGUL SYLLABLE JJULM +CB53;CB53;110D 116E 11B2;CB53;110D 116E 11B2; # (쭓; 쭓; 쭓; 쭓; 쭓; ) HANGUL SYLLABLE JJULB +CB54;CB54;110D 116E 11B3;CB54;110D 116E 11B3; # (쭔; 쭔; 쭔; 쭔; 쭔; ) HANGUL SYLLABLE JJULS +CB55;CB55;110D 116E 11B4;CB55;110D 116E 11B4; # (쭕; 쭕; 쭕; 쭕; 쭕; ) HANGUL SYLLABLE JJULT +CB56;CB56;110D 116E 11B5;CB56;110D 116E 11B5; # (쭖; 쭖; 쭖; 쭖; 쭖; ) HANGUL SYLLABLE JJULP +CB57;CB57;110D 116E 11B6;CB57;110D 116E 11B6; # (쭗; 쭗; 쭗; 쭗; 쭗; ) HANGUL SYLLABLE JJULH +CB58;CB58;110D 116E 11B7;CB58;110D 116E 11B7; # (쭘; 쭘; 쭘; 쭘; 쭘; ) HANGUL SYLLABLE JJUM +CB59;CB59;110D 116E 11B8;CB59;110D 116E 11B8; # (쭙; 쭙; 쭙; 쭙; 쭙; ) HANGUL SYLLABLE JJUB +CB5A;CB5A;110D 116E 11B9;CB5A;110D 116E 11B9; # (쭚; 쭚; 쭚; 쭚; 쭚; ) HANGUL SYLLABLE JJUBS +CB5B;CB5B;110D 116E 11BA;CB5B;110D 116E 11BA; # (쭛; 쭛; 쭛; 쭛; 쭛; ) HANGUL SYLLABLE JJUS +CB5C;CB5C;110D 116E 11BB;CB5C;110D 116E 11BB; # (쭜; 쭜; 쭜; 쭜; 쭜; ) HANGUL SYLLABLE JJUSS +CB5D;CB5D;110D 116E 11BC;CB5D;110D 116E 11BC; # (쭝; 쭝; 쭝; 쭝; 쭝; ) HANGUL SYLLABLE JJUNG +CB5E;CB5E;110D 116E 11BD;CB5E;110D 116E 11BD; # (쭞; 쭞; 쭞; 쭞; 쭞; ) HANGUL SYLLABLE JJUJ +CB5F;CB5F;110D 116E 11BE;CB5F;110D 116E 11BE; # (쭟; 쭟; 쭟; 쭟; 쭟; ) HANGUL SYLLABLE JJUC +CB60;CB60;110D 116E 11BF;CB60;110D 116E 11BF; # (쭠; 쭠; 쭠; 쭠; 쭠; ) HANGUL SYLLABLE JJUK +CB61;CB61;110D 116E 11C0;CB61;110D 116E 11C0; # (쭡; 쭡; 쭡; 쭡; 쭡; ) HANGUL SYLLABLE JJUT +CB62;CB62;110D 116E 11C1;CB62;110D 116E 11C1; # (쭢; 쭢; 쭢; 쭢; 쭢; ) HANGUL SYLLABLE JJUP +CB63;CB63;110D 116E 11C2;CB63;110D 116E 11C2; # (쭣; 쭣; 쭣; 쭣; 쭣; ) HANGUL SYLLABLE JJUH +CB64;CB64;110D 116F;CB64;110D 116F; # (쭤; 쭤; 쭤; 쭤; 쭤; ) HANGUL SYLLABLE JJWEO +CB65;CB65;110D 116F 11A8;CB65;110D 116F 11A8; # (쭥; 쭥; 쭥; 쭥; 쭥; ) HANGUL SYLLABLE JJWEOG +CB66;CB66;110D 116F 11A9;CB66;110D 116F 11A9; # (쭦; 쭦; 쭦; 쭦; 쭦; ) HANGUL SYLLABLE JJWEOGG +CB67;CB67;110D 116F 11AA;CB67;110D 116F 11AA; # (쭧; 쭧; 쭧; 쭧; 쭧; ) HANGUL SYLLABLE JJWEOGS +CB68;CB68;110D 116F 11AB;CB68;110D 116F 11AB; # (쭨; 쭨; 쭨; 쭨; 쭨; ) HANGUL SYLLABLE JJWEON +CB69;CB69;110D 116F 11AC;CB69;110D 116F 11AC; # (쭩; 쭩; 쭩; 쭩; 쭩; ) HANGUL SYLLABLE JJWEONJ +CB6A;CB6A;110D 116F 11AD;CB6A;110D 116F 11AD; # (쭪; 쭪; 쭪; 쭪; 쭪; ) HANGUL SYLLABLE JJWEONH +CB6B;CB6B;110D 116F 11AE;CB6B;110D 116F 11AE; # (쭫; 쭫; 쭫; 쭫; 쭫; ) HANGUL SYLLABLE JJWEOD +CB6C;CB6C;110D 116F 11AF;CB6C;110D 116F 11AF; # (쭬; 쭬; 쭬; 쭬; 쭬; ) HANGUL SYLLABLE JJWEOL +CB6D;CB6D;110D 116F 11B0;CB6D;110D 116F 11B0; # (쭭; 쭭; 쭭; 쭭; 쭭; ) HANGUL SYLLABLE JJWEOLG +CB6E;CB6E;110D 116F 11B1;CB6E;110D 116F 11B1; # (쭮; 쭮; 쭮; 쭮; 쭮; ) HANGUL SYLLABLE JJWEOLM +CB6F;CB6F;110D 116F 11B2;CB6F;110D 116F 11B2; # (쭯; 쭯; 쭯; 쭯; 쭯; ) HANGUL SYLLABLE JJWEOLB +CB70;CB70;110D 116F 11B3;CB70;110D 116F 11B3; # (쭰; 쭰; 쭰; 쭰; 쭰; ) HANGUL SYLLABLE JJWEOLS +CB71;CB71;110D 116F 11B4;CB71;110D 116F 11B4; # (쭱; 쭱; 쭱; 쭱; 쭱; ) HANGUL SYLLABLE JJWEOLT +CB72;CB72;110D 116F 11B5;CB72;110D 116F 11B5; # (쭲; 쭲; 쭲; 쭲; 쭲; ) HANGUL SYLLABLE JJWEOLP +CB73;CB73;110D 116F 11B6;CB73;110D 116F 11B6; # (쭳; 쭳; 쭳; 쭳; 쭳; ) HANGUL SYLLABLE JJWEOLH +CB74;CB74;110D 116F 11B7;CB74;110D 116F 11B7; # (쭴; 쭴; 쭴; 쭴; 쭴; ) HANGUL SYLLABLE JJWEOM +CB75;CB75;110D 116F 11B8;CB75;110D 116F 11B8; # (쭵; 쭵; 쭵; 쭵; 쭵; ) HANGUL SYLLABLE JJWEOB +CB76;CB76;110D 116F 11B9;CB76;110D 116F 11B9; # (쭶; 쭶; 쭶; 쭶; 쭶; ) HANGUL SYLLABLE JJWEOBS +CB77;CB77;110D 116F 11BA;CB77;110D 116F 11BA; # (쭷; 쭷; 쭷; 쭷; 쭷; ) HANGUL SYLLABLE JJWEOS +CB78;CB78;110D 116F 11BB;CB78;110D 116F 11BB; # (쭸; 쭸; 쭸; 쭸; 쭸; ) HANGUL SYLLABLE JJWEOSS +CB79;CB79;110D 116F 11BC;CB79;110D 116F 11BC; # (쭹; 쭹; 쭹; 쭹; 쭹; ) HANGUL SYLLABLE JJWEONG +CB7A;CB7A;110D 116F 11BD;CB7A;110D 116F 11BD; # (쭺; 쭺; 쭺; 쭺; 쭺; ) HANGUL SYLLABLE JJWEOJ +CB7B;CB7B;110D 116F 11BE;CB7B;110D 116F 11BE; # (쭻; 쭻; 쭻; 쭻; 쭻; ) HANGUL SYLLABLE JJWEOC +CB7C;CB7C;110D 116F 11BF;CB7C;110D 116F 11BF; # (쭼; 쭼; 쭼; 쭼; 쭼; ) HANGUL SYLLABLE JJWEOK +CB7D;CB7D;110D 116F 11C0;CB7D;110D 116F 11C0; # (쭽; 쭽; 쭽; 쭽; 쭽; ) HANGUL SYLLABLE JJWEOT +CB7E;CB7E;110D 116F 11C1;CB7E;110D 116F 11C1; # (쭾; 쭾; 쭾; 쭾; 쭾; ) HANGUL SYLLABLE JJWEOP +CB7F;CB7F;110D 116F 11C2;CB7F;110D 116F 11C2; # (쭿; 쭿; 쭿; 쭿; 쭿; ) HANGUL SYLLABLE JJWEOH +CB80;CB80;110D 1170;CB80;110D 1170; # (쮀; 쮀; 쮀; 쮀; 쮀; ) HANGUL SYLLABLE JJWE +CB81;CB81;110D 1170 11A8;CB81;110D 1170 11A8; # (쮁; 쮁; 쮁; 쮁; 쮁; ) HANGUL SYLLABLE JJWEG +CB82;CB82;110D 1170 11A9;CB82;110D 1170 11A9; # (쮂; 쮂; 쮂; 쮂; 쮂; ) HANGUL SYLLABLE JJWEGG +CB83;CB83;110D 1170 11AA;CB83;110D 1170 11AA; # (쮃; 쮃; 쮃; 쮃; 쮃; ) HANGUL SYLLABLE JJWEGS +CB84;CB84;110D 1170 11AB;CB84;110D 1170 11AB; # (쮄; 쮄; 쮄; 쮄; 쮄; ) HANGUL SYLLABLE JJWEN +CB85;CB85;110D 1170 11AC;CB85;110D 1170 11AC; # (쮅; 쮅; 쮅; 쮅; 쮅; ) HANGUL SYLLABLE JJWENJ +CB86;CB86;110D 1170 11AD;CB86;110D 1170 11AD; # (쮆; 쮆; 쮆; 쮆; 쮆; ) HANGUL SYLLABLE JJWENH +CB87;CB87;110D 1170 11AE;CB87;110D 1170 11AE; # (쮇; 쮇; 쮇; 쮇; 쮇; ) HANGUL SYLLABLE JJWED +CB88;CB88;110D 1170 11AF;CB88;110D 1170 11AF; # (쮈; 쮈; 쮈; 쮈; 쮈; ) HANGUL SYLLABLE JJWEL +CB89;CB89;110D 1170 11B0;CB89;110D 1170 11B0; # (쮉; 쮉; 쮉; 쮉; 쮉; ) HANGUL SYLLABLE JJWELG +CB8A;CB8A;110D 1170 11B1;CB8A;110D 1170 11B1; # (쮊; 쮊; 쮊; 쮊; 쮊; ) HANGUL SYLLABLE JJWELM +CB8B;CB8B;110D 1170 11B2;CB8B;110D 1170 11B2; # (쮋; 쮋; 쮋; 쮋; 쮋; ) HANGUL SYLLABLE JJWELB +CB8C;CB8C;110D 1170 11B3;CB8C;110D 1170 11B3; # (쮌; 쮌; 쮌; 쮌; 쮌; ) HANGUL SYLLABLE JJWELS +CB8D;CB8D;110D 1170 11B4;CB8D;110D 1170 11B4; # (쮍; 쮍; 쮍; 쮍; 쮍; ) HANGUL SYLLABLE JJWELT +CB8E;CB8E;110D 1170 11B5;CB8E;110D 1170 11B5; # (쮎; 쮎; 쮎; 쮎; 쮎; ) HANGUL SYLLABLE JJWELP +CB8F;CB8F;110D 1170 11B6;CB8F;110D 1170 11B6; # (쮏; 쮏; 쮏; 쮏; 쮏; ) HANGUL SYLLABLE JJWELH +CB90;CB90;110D 1170 11B7;CB90;110D 1170 11B7; # (쮐; 쮐; 쮐; 쮐; 쮐; ) HANGUL SYLLABLE JJWEM +CB91;CB91;110D 1170 11B8;CB91;110D 1170 11B8; # (쮑; 쮑; 쮑; 쮑; 쮑; ) HANGUL SYLLABLE JJWEB +CB92;CB92;110D 1170 11B9;CB92;110D 1170 11B9; # (쮒; 쮒; 쮒; 쮒; 쮒; ) HANGUL SYLLABLE JJWEBS +CB93;CB93;110D 1170 11BA;CB93;110D 1170 11BA; # (쮓; 쮓; 쮓; 쮓; 쮓; ) HANGUL SYLLABLE JJWES +CB94;CB94;110D 1170 11BB;CB94;110D 1170 11BB; # (쮔; 쮔; 쮔; 쮔; 쮔; ) HANGUL SYLLABLE JJWESS +CB95;CB95;110D 1170 11BC;CB95;110D 1170 11BC; # (쮕; 쮕; 쮕; 쮕; 쮕; ) HANGUL SYLLABLE JJWENG +CB96;CB96;110D 1170 11BD;CB96;110D 1170 11BD; # (쮖; 쮖; 쮖; 쮖; 쮖; ) HANGUL SYLLABLE JJWEJ +CB97;CB97;110D 1170 11BE;CB97;110D 1170 11BE; # (쮗; 쮗; 쮗; 쮗; 쮗; ) HANGUL SYLLABLE JJWEC +CB98;CB98;110D 1170 11BF;CB98;110D 1170 11BF; # (쮘; 쮘; 쮘; 쮘; 쮘; ) HANGUL SYLLABLE JJWEK +CB99;CB99;110D 1170 11C0;CB99;110D 1170 11C0; # (쮙; 쮙; 쮙; 쮙; 쮙; ) HANGUL SYLLABLE JJWET +CB9A;CB9A;110D 1170 11C1;CB9A;110D 1170 11C1; # (쮚; 쮚; 쮚; 쮚; 쮚; ) HANGUL SYLLABLE JJWEP +CB9B;CB9B;110D 1170 11C2;CB9B;110D 1170 11C2; # (쮛; 쮛; 쮛; 쮛; 쮛; ) HANGUL SYLLABLE JJWEH +CB9C;CB9C;110D 1171;CB9C;110D 1171; # (쮜; 쮜; 쮜; 쮜; 쮜; ) HANGUL SYLLABLE JJWI +CB9D;CB9D;110D 1171 11A8;CB9D;110D 1171 11A8; # (쮝; 쮝; 쮝; 쮝; 쮝; ) HANGUL SYLLABLE JJWIG +CB9E;CB9E;110D 1171 11A9;CB9E;110D 1171 11A9; # (쮞; 쮞; 쮞; 쮞; 쮞; ) HANGUL SYLLABLE JJWIGG +CB9F;CB9F;110D 1171 11AA;CB9F;110D 1171 11AA; # (쮟; 쮟; 쮟; 쮟; 쮟; ) HANGUL SYLLABLE JJWIGS +CBA0;CBA0;110D 1171 11AB;CBA0;110D 1171 11AB; # (쮠; 쮠; 쮠; 쮠; 쮠; ) HANGUL SYLLABLE JJWIN +CBA1;CBA1;110D 1171 11AC;CBA1;110D 1171 11AC; # (쮡; 쮡; 쮡; 쮡; 쮡; ) HANGUL SYLLABLE JJWINJ +CBA2;CBA2;110D 1171 11AD;CBA2;110D 1171 11AD; # (쮢; 쮢; 쮢; 쮢; 쮢; ) HANGUL SYLLABLE JJWINH +CBA3;CBA3;110D 1171 11AE;CBA3;110D 1171 11AE; # (쮣; 쮣; 쮣; 쮣; 쮣; ) HANGUL SYLLABLE JJWID +CBA4;CBA4;110D 1171 11AF;CBA4;110D 1171 11AF; # (쮤; 쮤; 쮤; 쮤; 쮤; ) HANGUL SYLLABLE JJWIL +CBA5;CBA5;110D 1171 11B0;CBA5;110D 1171 11B0; # (쮥; 쮥; 쮥; 쮥; 쮥; ) HANGUL SYLLABLE JJWILG +CBA6;CBA6;110D 1171 11B1;CBA6;110D 1171 11B1; # (쮦; 쮦; 쮦; 쮦; 쮦; ) HANGUL SYLLABLE JJWILM +CBA7;CBA7;110D 1171 11B2;CBA7;110D 1171 11B2; # (쮧; 쮧; 쮧; 쮧; 쮧; ) HANGUL SYLLABLE JJWILB +CBA8;CBA8;110D 1171 11B3;CBA8;110D 1171 11B3; # (쮨; 쮨; 쮨; 쮨; 쮨; ) HANGUL SYLLABLE JJWILS +CBA9;CBA9;110D 1171 11B4;CBA9;110D 1171 11B4; # (쮩; 쮩; 쮩; 쮩; 쮩; ) HANGUL SYLLABLE JJWILT +CBAA;CBAA;110D 1171 11B5;CBAA;110D 1171 11B5; # (쮪; 쮪; 쮪; 쮪; 쮪; ) HANGUL SYLLABLE JJWILP +CBAB;CBAB;110D 1171 11B6;CBAB;110D 1171 11B6; # (쮫; 쮫; 쮫; 쮫; 쮫; ) HANGUL SYLLABLE JJWILH +CBAC;CBAC;110D 1171 11B7;CBAC;110D 1171 11B7; # (쮬; 쮬; 쮬; 쮬; 쮬; ) HANGUL SYLLABLE JJWIM +CBAD;CBAD;110D 1171 11B8;CBAD;110D 1171 11B8; # (쮭; 쮭; 쮭; 쮭; 쮭; ) HANGUL SYLLABLE JJWIB +CBAE;CBAE;110D 1171 11B9;CBAE;110D 1171 11B9; # (쮮; 쮮; 쮮; 쮮; 쮮; ) HANGUL SYLLABLE JJWIBS +CBAF;CBAF;110D 1171 11BA;CBAF;110D 1171 11BA; # (쮯; 쮯; 쮯; 쮯; 쮯; ) HANGUL SYLLABLE JJWIS +CBB0;CBB0;110D 1171 11BB;CBB0;110D 1171 11BB; # (쮰; 쮰; 쮰; 쮰; 쮰; ) HANGUL SYLLABLE JJWISS +CBB1;CBB1;110D 1171 11BC;CBB1;110D 1171 11BC; # (쮱; 쮱; 쮱; 쮱; 쮱; ) HANGUL SYLLABLE JJWING +CBB2;CBB2;110D 1171 11BD;CBB2;110D 1171 11BD; # (쮲; 쮲; 쮲; 쮲; 쮲; ) HANGUL SYLLABLE JJWIJ +CBB3;CBB3;110D 1171 11BE;CBB3;110D 1171 11BE; # (쮳; 쮳; 쮳; 쮳; 쮳; ) HANGUL SYLLABLE JJWIC +CBB4;CBB4;110D 1171 11BF;CBB4;110D 1171 11BF; # (쮴; 쮴; 쮴; 쮴; 쮴; ) HANGUL SYLLABLE JJWIK +CBB5;CBB5;110D 1171 11C0;CBB5;110D 1171 11C0; # (쮵; 쮵; 쮵; 쮵; 쮵; ) HANGUL SYLLABLE JJWIT +CBB6;CBB6;110D 1171 11C1;CBB6;110D 1171 11C1; # (쮶; 쮶; 쮶; 쮶; 쮶; ) HANGUL SYLLABLE JJWIP +CBB7;CBB7;110D 1171 11C2;CBB7;110D 1171 11C2; # (쮷; 쮷; 쮷; 쮷; 쮷; ) HANGUL SYLLABLE JJWIH +CBB8;CBB8;110D 1172;CBB8;110D 1172; # (쮸; 쮸; 쮸; 쮸; 쮸; ) HANGUL SYLLABLE JJYU +CBB9;CBB9;110D 1172 11A8;CBB9;110D 1172 11A8; # (쮹; 쮹; 쮹; 쮹; 쮹; ) HANGUL SYLLABLE JJYUG +CBBA;CBBA;110D 1172 11A9;CBBA;110D 1172 11A9; # (쮺; 쮺; 쮺; 쮺; 쮺; ) HANGUL SYLLABLE JJYUGG +CBBB;CBBB;110D 1172 11AA;CBBB;110D 1172 11AA; # (쮻; 쮻; 쮻; 쮻; 쮻; ) HANGUL SYLLABLE JJYUGS +CBBC;CBBC;110D 1172 11AB;CBBC;110D 1172 11AB; # (쮼; 쮼; 쮼; 쮼; 쮼; ) HANGUL SYLLABLE JJYUN +CBBD;CBBD;110D 1172 11AC;CBBD;110D 1172 11AC; # (쮽; 쮽; 쮽; 쮽; 쮽; ) HANGUL SYLLABLE JJYUNJ +CBBE;CBBE;110D 1172 11AD;CBBE;110D 1172 11AD; # (쮾; 쮾; 쮾; 쮾; 쮾; ) HANGUL SYLLABLE JJYUNH +CBBF;CBBF;110D 1172 11AE;CBBF;110D 1172 11AE; # (쮿; 쮿; 쮿; 쮿; 쮿; ) HANGUL SYLLABLE JJYUD +CBC0;CBC0;110D 1172 11AF;CBC0;110D 1172 11AF; # (쯀; 쯀; 쯀; 쯀; 쯀; ) HANGUL SYLLABLE JJYUL +CBC1;CBC1;110D 1172 11B0;CBC1;110D 1172 11B0; # (쯁; 쯁; 쯁; 쯁; 쯁; ) HANGUL SYLLABLE JJYULG +CBC2;CBC2;110D 1172 11B1;CBC2;110D 1172 11B1; # (쯂; 쯂; 쯂; 쯂; 쯂; ) HANGUL SYLLABLE JJYULM +CBC3;CBC3;110D 1172 11B2;CBC3;110D 1172 11B2; # (쯃; 쯃; 쯃; 쯃; 쯃; ) HANGUL SYLLABLE JJYULB +CBC4;CBC4;110D 1172 11B3;CBC4;110D 1172 11B3; # (쯄; 쯄; 쯄; 쯄; 쯄; ) HANGUL SYLLABLE JJYULS +CBC5;CBC5;110D 1172 11B4;CBC5;110D 1172 11B4; # (쯅; 쯅; 쯅; 쯅; 쯅; ) HANGUL SYLLABLE JJYULT +CBC6;CBC6;110D 1172 11B5;CBC6;110D 1172 11B5; # (쯆; 쯆; 쯆; 쯆; 쯆; ) HANGUL SYLLABLE JJYULP +CBC7;CBC7;110D 1172 11B6;CBC7;110D 1172 11B6; # (쯇; 쯇; 쯇; 쯇; 쯇; ) HANGUL SYLLABLE JJYULH +CBC8;CBC8;110D 1172 11B7;CBC8;110D 1172 11B7; # (쯈; 쯈; 쯈; 쯈; 쯈; ) HANGUL SYLLABLE JJYUM +CBC9;CBC9;110D 1172 11B8;CBC9;110D 1172 11B8; # (쯉; 쯉; 쯉; 쯉; 쯉; ) HANGUL SYLLABLE JJYUB +CBCA;CBCA;110D 1172 11B9;CBCA;110D 1172 11B9; # (쯊; 쯊; 쯊; 쯊; 쯊; ) HANGUL SYLLABLE JJYUBS +CBCB;CBCB;110D 1172 11BA;CBCB;110D 1172 11BA; # (쯋; 쯋; 쯋; 쯋; 쯋; ) HANGUL SYLLABLE JJYUS +CBCC;CBCC;110D 1172 11BB;CBCC;110D 1172 11BB; # (쯌; 쯌; 쯌; 쯌; 쯌; ) HANGUL SYLLABLE JJYUSS +CBCD;CBCD;110D 1172 11BC;CBCD;110D 1172 11BC; # (쯍; 쯍; 쯍; 쯍; 쯍; ) HANGUL SYLLABLE JJYUNG +CBCE;CBCE;110D 1172 11BD;CBCE;110D 1172 11BD; # (쯎; 쯎; 쯎; 쯎; 쯎; ) HANGUL SYLLABLE JJYUJ +CBCF;CBCF;110D 1172 11BE;CBCF;110D 1172 11BE; # (쯏; 쯏; 쯏; 쯏; 쯏; ) HANGUL SYLLABLE JJYUC +CBD0;CBD0;110D 1172 11BF;CBD0;110D 1172 11BF; # (쯐; 쯐; 쯐; 쯐; 쯐; ) HANGUL SYLLABLE JJYUK +CBD1;CBD1;110D 1172 11C0;CBD1;110D 1172 11C0; # (쯑; 쯑; 쯑; 쯑; 쯑; ) HANGUL SYLLABLE JJYUT +CBD2;CBD2;110D 1172 11C1;CBD2;110D 1172 11C1; # (쯒; 쯒; 쯒; 쯒; 쯒; ) HANGUL SYLLABLE JJYUP +CBD3;CBD3;110D 1172 11C2;CBD3;110D 1172 11C2; # (쯓; 쯓; 쯓; 쯓; 쯓; ) HANGUL SYLLABLE JJYUH +CBD4;CBD4;110D 1173;CBD4;110D 1173; # (쯔; 쯔; 쯔; 쯔; 쯔; ) HANGUL SYLLABLE JJEU +CBD5;CBD5;110D 1173 11A8;CBD5;110D 1173 11A8; # (쯕; 쯕; 쯕; 쯕; 쯕; ) HANGUL SYLLABLE JJEUG +CBD6;CBD6;110D 1173 11A9;CBD6;110D 1173 11A9; # (쯖; 쯖; 쯖; 쯖; 쯖; ) HANGUL SYLLABLE JJEUGG +CBD7;CBD7;110D 1173 11AA;CBD7;110D 1173 11AA; # (쯗; 쯗; 쯗; 쯗; 쯗; ) HANGUL SYLLABLE JJEUGS +CBD8;CBD8;110D 1173 11AB;CBD8;110D 1173 11AB; # (쯘; 쯘; 쯘; 쯘; 쯘; ) HANGUL SYLLABLE JJEUN +CBD9;CBD9;110D 1173 11AC;CBD9;110D 1173 11AC; # (쯙; 쯙; 쯙; 쯙; 쯙; ) HANGUL SYLLABLE JJEUNJ +CBDA;CBDA;110D 1173 11AD;CBDA;110D 1173 11AD; # (쯚; 쯚; 쯚; 쯚; 쯚; ) HANGUL SYLLABLE JJEUNH +CBDB;CBDB;110D 1173 11AE;CBDB;110D 1173 11AE; # (쯛; 쯛; 쯛; 쯛; 쯛; ) HANGUL SYLLABLE JJEUD +CBDC;CBDC;110D 1173 11AF;CBDC;110D 1173 11AF; # (쯜; 쯜; 쯜; 쯜; 쯜; ) HANGUL SYLLABLE JJEUL +CBDD;CBDD;110D 1173 11B0;CBDD;110D 1173 11B0; # (쯝; 쯝; 쯝; 쯝; 쯝; ) HANGUL SYLLABLE JJEULG +CBDE;CBDE;110D 1173 11B1;CBDE;110D 1173 11B1; # (쯞; 쯞; 쯞; 쯞; 쯞; ) HANGUL SYLLABLE JJEULM +CBDF;CBDF;110D 1173 11B2;CBDF;110D 1173 11B2; # (쯟; 쯟; 쯟; 쯟; 쯟; ) HANGUL SYLLABLE JJEULB +CBE0;CBE0;110D 1173 11B3;CBE0;110D 1173 11B3; # (쯠; 쯠; 쯠; 쯠; 쯠; ) HANGUL SYLLABLE JJEULS +CBE1;CBE1;110D 1173 11B4;CBE1;110D 1173 11B4; # (쯡; 쯡; 쯡; 쯡; 쯡; ) HANGUL SYLLABLE JJEULT +CBE2;CBE2;110D 1173 11B5;CBE2;110D 1173 11B5; # (쯢; 쯢; 쯢; 쯢; 쯢; ) HANGUL SYLLABLE JJEULP +CBE3;CBE3;110D 1173 11B6;CBE3;110D 1173 11B6; # (쯣; 쯣; 쯣; 쯣; 쯣; ) HANGUL SYLLABLE JJEULH +CBE4;CBE4;110D 1173 11B7;CBE4;110D 1173 11B7; # (쯤; 쯤; 쯤; 쯤; 쯤; ) HANGUL SYLLABLE JJEUM +CBE5;CBE5;110D 1173 11B8;CBE5;110D 1173 11B8; # (쯥; 쯥; 쯥; 쯥; 쯥; ) HANGUL SYLLABLE JJEUB +CBE6;CBE6;110D 1173 11B9;CBE6;110D 1173 11B9; # (쯦; 쯦; 쯦; 쯦; 쯦; ) HANGUL SYLLABLE JJEUBS +CBE7;CBE7;110D 1173 11BA;CBE7;110D 1173 11BA; # (쯧; 쯧; 쯧; 쯧; 쯧; ) HANGUL SYLLABLE JJEUS +CBE8;CBE8;110D 1173 11BB;CBE8;110D 1173 11BB; # (쯨; 쯨; 쯨; 쯨; 쯨; ) HANGUL SYLLABLE JJEUSS +CBE9;CBE9;110D 1173 11BC;CBE9;110D 1173 11BC; # (쯩; 쯩; 쯩; 쯩; 쯩; ) HANGUL SYLLABLE JJEUNG +CBEA;CBEA;110D 1173 11BD;CBEA;110D 1173 11BD; # (쯪; 쯪; 쯪; 쯪; 쯪; ) HANGUL SYLLABLE JJEUJ +CBEB;CBEB;110D 1173 11BE;CBEB;110D 1173 11BE; # (쯫; 쯫; 쯫; 쯫; 쯫; ) HANGUL SYLLABLE JJEUC +CBEC;CBEC;110D 1173 11BF;CBEC;110D 1173 11BF; # (쯬; 쯬; 쯬; 쯬; 쯬; ) HANGUL SYLLABLE JJEUK +CBED;CBED;110D 1173 11C0;CBED;110D 1173 11C0; # (쯭; 쯭; 쯭; 쯭; 쯭; ) HANGUL SYLLABLE JJEUT +CBEE;CBEE;110D 1173 11C1;CBEE;110D 1173 11C1; # (쯮; 쯮; 쯮; 쯮; 쯮; ) HANGUL SYLLABLE JJEUP +CBEF;CBEF;110D 1173 11C2;CBEF;110D 1173 11C2; # (쯯; 쯯; 쯯; 쯯; 쯯; ) HANGUL SYLLABLE JJEUH +CBF0;CBF0;110D 1174;CBF0;110D 1174; # (쯰; 쯰; 쯰; 쯰; 쯰; ) HANGUL SYLLABLE JJYI +CBF1;CBF1;110D 1174 11A8;CBF1;110D 1174 11A8; # (쯱; 쯱; 쯱; 쯱; 쯱; ) HANGUL SYLLABLE JJYIG +CBF2;CBF2;110D 1174 11A9;CBF2;110D 1174 11A9; # (쯲; 쯲; 쯲; 쯲; 쯲; ) HANGUL SYLLABLE JJYIGG +CBF3;CBF3;110D 1174 11AA;CBF3;110D 1174 11AA; # (쯳; 쯳; 쯳; 쯳; 쯳; ) HANGUL SYLLABLE JJYIGS +CBF4;CBF4;110D 1174 11AB;CBF4;110D 1174 11AB; # (쯴; 쯴; 쯴; 쯴; 쯴; ) HANGUL SYLLABLE JJYIN +CBF5;CBF5;110D 1174 11AC;CBF5;110D 1174 11AC; # (쯵; 쯵; 쯵; 쯵; 쯵; ) HANGUL SYLLABLE JJYINJ +CBF6;CBF6;110D 1174 11AD;CBF6;110D 1174 11AD; # (쯶; 쯶; 쯶; 쯶; 쯶; ) HANGUL SYLLABLE JJYINH +CBF7;CBF7;110D 1174 11AE;CBF7;110D 1174 11AE; # (쯷; 쯷; 쯷; 쯷; 쯷; ) HANGUL SYLLABLE JJYID +CBF8;CBF8;110D 1174 11AF;CBF8;110D 1174 11AF; # (쯸; 쯸; 쯸; 쯸; 쯸; ) HANGUL SYLLABLE JJYIL +CBF9;CBF9;110D 1174 11B0;CBF9;110D 1174 11B0; # (쯹; 쯹; 쯹; 쯹; 쯹; ) HANGUL SYLLABLE JJYILG +CBFA;CBFA;110D 1174 11B1;CBFA;110D 1174 11B1; # (쯺; 쯺; 쯺; 쯺; 쯺; ) HANGUL SYLLABLE JJYILM +CBFB;CBFB;110D 1174 11B2;CBFB;110D 1174 11B2; # (쯻; 쯻; 쯻; 쯻; 쯻; ) HANGUL SYLLABLE JJYILB +CBFC;CBFC;110D 1174 11B3;CBFC;110D 1174 11B3; # (쯼; 쯼; 쯼; 쯼; 쯼; ) HANGUL SYLLABLE JJYILS +CBFD;CBFD;110D 1174 11B4;CBFD;110D 1174 11B4; # (쯽; 쯽; 쯽; 쯽; 쯽; ) HANGUL SYLLABLE JJYILT +CBFE;CBFE;110D 1174 11B5;CBFE;110D 1174 11B5; # (쯾; 쯾; 쯾; 쯾; 쯾; ) HANGUL SYLLABLE JJYILP +CBFF;CBFF;110D 1174 11B6;CBFF;110D 1174 11B6; # (쯿; 쯿; 쯿; 쯿; 쯿; ) HANGUL SYLLABLE JJYILH +CC00;CC00;110D 1174 11B7;CC00;110D 1174 11B7; # (찀; 찀; 찀; 찀; 찀; ) HANGUL SYLLABLE JJYIM +CC01;CC01;110D 1174 11B8;CC01;110D 1174 11B8; # (찁; 찁; 찁; 찁; 찁; ) HANGUL SYLLABLE JJYIB +CC02;CC02;110D 1174 11B9;CC02;110D 1174 11B9; # (찂; 찂; 찂; 찂; 찂; ) HANGUL SYLLABLE JJYIBS +CC03;CC03;110D 1174 11BA;CC03;110D 1174 11BA; # (찃; 찃; 찃; 찃; 찃; ) HANGUL SYLLABLE JJYIS +CC04;CC04;110D 1174 11BB;CC04;110D 1174 11BB; # (찄; 찄; 찄; 찄; 찄; ) HANGUL SYLLABLE JJYISS +CC05;CC05;110D 1174 11BC;CC05;110D 1174 11BC; # (찅; 찅; 찅; 찅; 찅; ) HANGUL SYLLABLE JJYING +CC06;CC06;110D 1174 11BD;CC06;110D 1174 11BD; # (찆; 찆; 찆; 찆; 찆; ) HANGUL SYLLABLE JJYIJ +CC07;CC07;110D 1174 11BE;CC07;110D 1174 11BE; # (찇; 찇; 찇; 찇; 찇; ) HANGUL SYLLABLE JJYIC +CC08;CC08;110D 1174 11BF;CC08;110D 1174 11BF; # (찈; 찈; 찈; 찈; 찈; ) HANGUL SYLLABLE JJYIK +CC09;CC09;110D 1174 11C0;CC09;110D 1174 11C0; # (찉; 찉; 찉; 찉; 찉; ) HANGUL SYLLABLE JJYIT +CC0A;CC0A;110D 1174 11C1;CC0A;110D 1174 11C1; # (찊; 찊; 찊; 찊; 찊; ) HANGUL SYLLABLE JJYIP +CC0B;CC0B;110D 1174 11C2;CC0B;110D 1174 11C2; # (찋; 찋; 찋; 찋; 찋; ) HANGUL SYLLABLE JJYIH +CC0C;CC0C;110D 1175;CC0C;110D 1175; # (찌; 찌; 찌; 찌; 찌; ) HANGUL SYLLABLE JJI +CC0D;CC0D;110D 1175 11A8;CC0D;110D 1175 11A8; # (찍; 찍; 찍; 찍; 찍; ) HANGUL SYLLABLE JJIG +CC0E;CC0E;110D 1175 11A9;CC0E;110D 1175 11A9; # (찎; 찎; 찎; 찎; 찎; ) HANGUL SYLLABLE JJIGG +CC0F;CC0F;110D 1175 11AA;CC0F;110D 1175 11AA; # (찏; 찏; 찏; 찏; 찏; ) HANGUL SYLLABLE JJIGS +CC10;CC10;110D 1175 11AB;CC10;110D 1175 11AB; # (찐; 찐; 찐; 찐; 찐; ) HANGUL SYLLABLE JJIN +CC11;CC11;110D 1175 11AC;CC11;110D 1175 11AC; # (찑; 찑; 찑; 찑; 찑; ) HANGUL SYLLABLE JJINJ +CC12;CC12;110D 1175 11AD;CC12;110D 1175 11AD; # (찒; 찒; 찒; 찒; 찒; ) HANGUL SYLLABLE JJINH +CC13;CC13;110D 1175 11AE;CC13;110D 1175 11AE; # (찓; 찓; 찓; 찓; 찓; ) HANGUL SYLLABLE JJID +CC14;CC14;110D 1175 11AF;CC14;110D 1175 11AF; # (찔; 찔; 찔; 찔; 찔; ) HANGUL SYLLABLE JJIL +CC15;CC15;110D 1175 11B0;CC15;110D 1175 11B0; # (찕; 찕; 찕; 찕; 찕; ) HANGUL SYLLABLE JJILG +CC16;CC16;110D 1175 11B1;CC16;110D 1175 11B1; # (찖; 찖; 찖; 찖; 찖; ) HANGUL SYLLABLE JJILM +CC17;CC17;110D 1175 11B2;CC17;110D 1175 11B2; # (찗; 찗; 찗; 찗; 찗; ) HANGUL SYLLABLE JJILB +CC18;CC18;110D 1175 11B3;CC18;110D 1175 11B3; # (찘; 찘; 찘; 찘; 찘; ) HANGUL SYLLABLE JJILS +CC19;CC19;110D 1175 11B4;CC19;110D 1175 11B4; # (찙; 찙; 찙; 찙; 찙; ) HANGUL SYLLABLE JJILT +CC1A;CC1A;110D 1175 11B5;CC1A;110D 1175 11B5; # (찚; 찚; 찚; 찚; 찚; ) HANGUL SYLLABLE JJILP +CC1B;CC1B;110D 1175 11B6;CC1B;110D 1175 11B6; # (찛; 찛; 찛; 찛; 찛; ) HANGUL SYLLABLE JJILH +CC1C;CC1C;110D 1175 11B7;CC1C;110D 1175 11B7; # (찜; 찜; 찜; 찜; 찜; ) HANGUL SYLLABLE JJIM +CC1D;CC1D;110D 1175 11B8;CC1D;110D 1175 11B8; # (찝; 찝; 찝; 찝; 찝; ) HANGUL SYLLABLE JJIB +CC1E;CC1E;110D 1175 11B9;CC1E;110D 1175 11B9; # (찞; 찞; 찞; 찞; 찞; ) HANGUL SYLLABLE JJIBS +CC1F;CC1F;110D 1175 11BA;CC1F;110D 1175 11BA; # (찟; 찟; 찟; 찟; 찟; ) HANGUL SYLLABLE JJIS +CC20;CC20;110D 1175 11BB;CC20;110D 1175 11BB; # (찠; 찠; 찠; 찠; 찠; ) HANGUL SYLLABLE JJISS +CC21;CC21;110D 1175 11BC;CC21;110D 1175 11BC; # (찡; 찡; 찡; 찡; 찡; ) HANGUL SYLLABLE JJING +CC22;CC22;110D 1175 11BD;CC22;110D 1175 11BD; # (찢; 찢; 찢; 찢; 찢; ) HANGUL SYLLABLE JJIJ +CC23;CC23;110D 1175 11BE;CC23;110D 1175 11BE; # (찣; 찣; 찣; 찣; 찣; ) HANGUL SYLLABLE JJIC +CC24;CC24;110D 1175 11BF;CC24;110D 1175 11BF; # (찤; 찤; 찤; 찤; 찤; ) HANGUL SYLLABLE JJIK +CC25;CC25;110D 1175 11C0;CC25;110D 1175 11C0; # (찥; 찥; 찥; 찥; 찥; ) HANGUL SYLLABLE JJIT +CC26;CC26;110D 1175 11C1;CC26;110D 1175 11C1; # (찦; 찦; 찦; 찦; 찦; ) HANGUL SYLLABLE JJIP +CC27;CC27;110D 1175 11C2;CC27;110D 1175 11C2; # (찧; 찧; 찧; 찧; 찧; ) HANGUL SYLLABLE JJIH +CC28;CC28;110E 1161;CC28;110E 1161; # (차; 차; 차; 차; 차; ) HANGUL SYLLABLE CA +CC29;CC29;110E 1161 11A8;CC29;110E 1161 11A8; # (착; 착; 착; 착; 착; ) HANGUL SYLLABLE CAG +CC2A;CC2A;110E 1161 11A9;CC2A;110E 1161 11A9; # (찪; 찪; 찪; 찪; 찪; ) HANGUL SYLLABLE CAGG +CC2B;CC2B;110E 1161 11AA;CC2B;110E 1161 11AA; # (찫; 찫; 찫; 찫; 찫; ) HANGUL SYLLABLE CAGS +CC2C;CC2C;110E 1161 11AB;CC2C;110E 1161 11AB; # (찬; 찬; 찬; 찬; 찬; ) HANGUL SYLLABLE CAN +CC2D;CC2D;110E 1161 11AC;CC2D;110E 1161 11AC; # (찭; 찭; 찭; 찭; 찭; ) HANGUL SYLLABLE CANJ +CC2E;CC2E;110E 1161 11AD;CC2E;110E 1161 11AD; # (찮; 찮; 찮; 찮; 찮; ) HANGUL SYLLABLE CANH +CC2F;CC2F;110E 1161 11AE;CC2F;110E 1161 11AE; # (찯; 찯; 찯; 찯; 찯; ) HANGUL SYLLABLE CAD +CC30;CC30;110E 1161 11AF;CC30;110E 1161 11AF; # (찰; 찰; 찰; 찰; 찰; ) HANGUL SYLLABLE CAL +CC31;CC31;110E 1161 11B0;CC31;110E 1161 11B0; # (찱; 찱; 찱; 찱; 찱; ) HANGUL SYLLABLE CALG +CC32;CC32;110E 1161 11B1;CC32;110E 1161 11B1; # (찲; 찲; 찲; 찲; 찲; ) HANGUL SYLLABLE CALM +CC33;CC33;110E 1161 11B2;CC33;110E 1161 11B2; # (찳; 찳; 찳; 찳; 찳; ) HANGUL SYLLABLE CALB +CC34;CC34;110E 1161 11B3;CC34;110E 1161 11B3; # (찴; 찴; 찴; 찴; 찴; ) HANGUL SYLLABLE CALS +CC35;CC35;110E 1161 11B4;CC35;110E 1161 11B4; # (찵; 찵; 찵; 찵; 찵; ) HANGUL SYLLABLE CALT +CC36;CC36;110E 1161 11B5;CC36;110E 1161 11B5; # (찶; 찶; 찶; 찶; 찶; ) HANGUL SYLLABLE CALP +CC37;CC37;110E 1161 11B6;CC37;110E 1161 11B6; # (찷; 찷; 찷; 찷; 찷; ) HANGUL SYLLABLE CALH +CC38;CC38;110E 1161 11B7;CC38;110E 1161 11B7; # (참; 참; 참; 참; 참; ) HANGUL SYLLABLE CAM +CC39;CC39;110E 1161 11B8;CC39;110E 1161 11B8; # (찹; 찹; 찹; 찹; 찹; ) HANGUL SYLLABLE CAB +CC3A;CC3A;110E 1161 11B9;CC3A;110E 1161 11B9; # (찺; 찺; 찺; 찺; 찺; ) HANGUL SYLLABLE CABS +CC3B;CC3B;110E 1161 11BA;CC3B;110E 1161 11BA; # (찻; 찻; 찻; 찻; 찻; ) HANGUL SYLLABLE CAS +CC3C;CC3C;110E 1161 11BB;CC3C;110E 1161 11BB; # (찼; 찼; 찼; 찼; 찼; ) HANGUL SYLLABLE CASS +CC3D;CC3D;110E 1161 11BC;CC3D;110E 1161 11BC; # (창; 창; 창; 창; 창; ) HANGUL SYLLABLE CANG +CC3E;CC3E;110E 1161 11BD;CC3E;110E 1161 11BD; # (찾; 찾; 찾; 찾; 찾; ) HANGUL SYLLABLE CAJ +CC3F;CC3F;110E 1161 11BE;CC3F;110E 1161 11BE; # (찿; 찿; 찿; 찿; 찿; ) HANGUL SYLLABLE CAC +CC40;CC40;110E 1161 11BF;CC40;110E 1161 11BF; # (챀; 챀; 챀; 챀; 챀; ) HANGUL SYLLABLE CAK +CC41;CC41;110E 1161 11C0;CC41;110E 1161 11C0; # (챁; 챁; 챁; 챁; 챁; ) HANGUL SYLLABLE CAT +CC42;CC42;110E 1161 11C1;CC42;110E 1161 11C1; # (챂; 챂; 챂; 챂; 챂; ) HANGUL SYLLABLE CAP +CC43;CC43;110E 1161 11C2;CC43;110E 1161 11C2; # (챃; 챃; 챃; 챃; 챃; ) HANGUL SYLLABLE CAH +CC44;CC44;110E 1162;CC44;110E 1162; # (채; 채; 채; 채; 채; ) HANGUL SYLLABLE CAE +CC45;CC45;110E 1162 11A8;CC45;110E 1162 11A8; # (책; 책; 책; 책; 책; ) HANGUL SYLLABLE CAEG +CC46;CC46;110E 1162 11A9;CC46;110E 1162 11A9; # (챆; 챆; 챆; 챆; 챆; ) HANGUL SYLLABLE CAEGG +CC47;CC47;110E 1162 11AA;CC47;110E 1162 11AA; # (챇; 챇; 챇; 챇; 챇; ) HANGUL SYLLABLE CAEGS +CC48;CC48;110E 1162 11AB;CC48;110E 1162 11AB; # (챈; 챈; 챈; 챈; 챈; ) HANGUL SYLLABLE CAEN +CC49;CC49;110E 1162 11AC;CC49;110E 1162 11AC; # (챉; 챉; 챉; 챉; 챉; ) HANGUL SYLLABLE CAENJ +CC4A;CC4A;110E 1162 11AD;CC4A;110E 1162 11AD; # (챊; 챊; 챊; 챊; 챊; ) HANGUL SYLLABLE CAENH +CC4B;CC4B;110E 1162 11AE;CC4B;110E 1162 11AE; # (챋; 챋; 챋; 챋; 챋; ) HANGUL SYLLABLE CAED +CC4C;CC4C;110E 1162 11AF;CC4C;110E 1162 11AF; # (챌; 챌; 챌; 챌; 챌; ) HANGUL SYLLABLE CAEL +CC4D;CC4D;110E 1162 11B0;CC4D;110E 1162 11B0; # (챍; 챍; 챍; 챍; 챍; ) HANGUL SYLLABLE CAELG +CC4E;CC4E;110E 1162 11B1;CC4E;110E 1162 11B1; # (챎; 챎; 챎; 챎; 챎; ) HANGUL SYLLABLE CAELM +CC4F;CC4F;110E 1162 11B2;CC4F;110E 1162 11B2; # (챏; 챏; 챏; 챏; 챏; ) HANGUL SYLLABLE CAELB +CC50;CC50;110E 1162 11B3;CC50;110E 1162 11B3; # (챐; 챐; 챐; 챐; 챐; ) HANGUL SYLLABLE CAELS +CC51;CC51;110E 1162 11B4;CC51;110E 1162 11B4; # (챑; 챑; 챑; 챑; 챑; ) HANGUL SYLLABLE CAELT +CC52;CC52;110E 1162 11B5;CC52;110E 1162 11B5; # (챒; 챒; 챒; 챒; 챒; ) HANGUL SYLLABLE CAELP +CC53;CC53;110E 1162 11B6;CC53;110E 1162 11B6; # (챓; 챓; 챓; 챓; 챓; ) HANGUL SYLLABLE CAELH +CC54;CC54;110E 1162 11B7;CC54;110E 1162 11B7; # (챔; 챔; 챔; 챔; 챔; ) HANGUL SYLLABLE CAEM +CC55;CC55;110E 1162 11B8;CC55;110E 1162 11B8; # (챕; 챕; 챕; 챕; 챕; ) HANGUL SYLLABLE CAEB +CC56;CC56;110E 1162 11B9;CC56;110E 1162 11B9; # (챖; 챖; 챖; 챖; 챖; ) HANGUL SYLLABLE CAEBS +CC57;CC57;110E 1162 11BA;CC57;110E 1162 11BA; # (챗; 챗; 챗; 챗; 챗; ) HANGUL SYLLABLE CAES +CC58;CC58;110E 1162 11BB;CC58;110E 1162 11BB; # (챘; 챘; 챘; 챘; 챘; ) HANGUL SYLLABLE CAESS +CC59;CC59;110E 1162 11BC;CC59;110E 1162 11BC; # (챙; 챙; 챙; 챙; 챙; ) HANGUL SYLLABLE CAENG +CC5A;CC5A;110E 1162 11BD;CC5A;110E 1162 11BD; # (챚; 챚; 챚; 챚; 챚; ) HANGUL SYLLABLE CAEJ +CC5B;CC5B;110E 1162 11BE;CC5B;110E 1162 11BE; # (챛; 챛; 챛; 챛; 챛; ) HANGUL SYLLABLE CAEC +CC5C;CC5C;110E 1162 11BF;CC5C;110E 1162 11BF; # (챜; 챜; 챜; 챜; 챜; ) HANGUL SYLLABLE CAEK +CC5D;CC5D;110E 1162 11C0;CC5D;110E 1162 11C0; # (챝; 챝; 챝; 챝; 챝; ) HANGUL SYLLABLE CAET +CC5E;CC5E;110E 1162 11C1;CC5E;110E 1162 11C1; # (챞; 챞; 챞; 챞; 챞; ) HANGUL SYLLABLE CAEP +CC5F;CC5F;110E 1162 11C2;CC5F;110E 1162 11C2; # (챟; 챟; 챟; 챟; 챟; ) HANGUL SYLLABLE CAEH +CC60;CC60;110E 1163;CC60;110E 1163; # (챠; 챠; 챠; 챠; 챠; ) HANGUL SYLLABLE CYA +CC61;CC61;110E 1163 11A8;CC61;110E 1163 11A8; # (챡; 챡; 챡; 챡; 챡; ) HANGUL SYLLABLE CYAG +CC62;CC62;110E 1163 11A9;CC62;110E 1163 11A9; # (챢; 챢; 챢; 챢; 챢; ) HANGUL SYLLABLE CYAGG +CC63;CC63;110E 1163 11AA;CC63;110E 1163 11AA; # (챣; 챣; 챣; 챣; 챣; ) HANGUL SYLLABLE CYAGS +CC64;CC64;110E 1163 11AB;CC64;110E 1163 11AB; # (챤; 챤; 챤; 챤; 챤; ) HANGUL SYLLABLE CYAN +CC65;CC65;110E 1163 11AC;CC65;110E 1163 11AC; # (챥; 챥; 챥; 챥; 챥; ) HANGUL SYLLABLE CYANJ +CC66;CC66;110E 1163 11AD;CC66;110E 1163 11AD; # (챦; 챦; 챦; 챦; 챦; ) HANGUL SYLLABLE CYANH +CC67;CC67;110E 1163 11AE;CC67;110E 1163 11AE; # (챧; 챧; 챧; 챧; 챧; ) HANGUL SYLLABLE CYAD +CC68;CC68;110E 1163 11AF;CC68;110E 1163 11AF; # (챨; 챨; 챨; 챨; 챨; ) HANGUL SYLLABLE CYAL +CC69;CC69;110E 1163 11B0;CC69;110E 1163 11B0; # (챩; 챩; 챩; 챩; 챩; ) HANGUL SYLLABLE CYALG +CC6A;CC6A;110E 1163 11B1;CC6A;110E 1163 11B1; # (챪; 챪; 챪; 챪; 챪; ) HANGUL SYLLABLE CYALM +CC6B;CC6B;110E 1163 11B2;CC6B;110E 1163 11B2; # (챫; 챫; 챫; 챫; 챫; ) HANGUL SYLLABLE CYALB +CC6C;CC6C;110E 1163 11B3;CC6C;110E 1163 11B3; # (챬; 챬; 챬; 챬; 챬; ) HANGUL SYLLABLE CYALS +CC6D;CC6D;110E 1163 11B4;CC6D;110E 1163 11B4; # (챭; 챭; 챭; 챭; 챭; ) HANGUL SYLLABLE CYALT +CC6E;CC6E;110E 1163 11B5;CC6E;110E 1163 11B5; # (챮; 챮; 챮; 챮; 챮; ) HANGUL SYLLABLE CYALP +CC6F;CC6F;110E 1163 11B6;CC6F;110E 1163 11B6; # (챯; 챯; 챯; 챯; 챯; ) HANGUL SYLLABLE CYALH +CC70;CC70;110E 1163 11B7;CC70;110E 1163 11B7; # (챰; 챰; 챰; 챰; 챰; ) HANGUL SYLLABLE CYAM +CC71;CC71;110E 1163 11B8;CC71;110E 1163 11B8; # (챱; 챱; 챱; 챱; 챱; ) HANGUL SYLLABLE CYAB +CC72;CC72;110E 1163 11B9;CC72;110E 1163 11B9; # (챲; 챲; 챲; 챲; 챲; ) HANGUL SYLLABLE CYABS +CC73;CC73;110E 1163 11BA;CC73;110E 1163 11BA; # (챳; 챳; 챳; 챳; 챳; ) HANGUL SYLLABLE CYAS +CC74;CC74;110E 1163 11BB;CC74;110E 1163 11BB; # (챴; 챴; 챴; 챴; 챴; ) HANGUL SYLLABLE CYASS +CC75;CC75;110E 1163 11BC;CC75;110E 1163 11BC; # (챵; 챵; 챵; 챵; 챵; ) HANGUL SYLLABLE CYANG +CC76;CC76;110E 1163 11BD;CC76;110E 1163 11BD; # (챶; 챶; 챶; 챶; 챶; ) HANGUL SYLLABLE CYAJ +CC77;CC77;110E 1163 11BE;CC77;110E 1163 11BE; # (챷; 챷; 챷; 챷; 챷; ) HANGUL SYLLABLE CYAC +CC78;CC78;110E 1163 11BF;CC78;110E 1163 11BF; # (챸; 챸; 챸; 챸; 챸; ) HANGUL SYLLABLE CYAK +CC79;CC79;110E 1163 11C0;CC79;110E 1163 11C0; # (챹; 챹; 챹; 챹; 챹; ) HANGUL SYLLABLE CYAT +CC7A;CC7A;110E 1163 11C1;CC7A;110E 1163 11C1; # (챺; 챺; 챺; 챺; 챺; ) HANGUL SYLLABLE CYAP +CC7B;CC7B;110E 1163 11C2;CC7B;110E 1163 11C2; # (챻; 챻; 챻; 챻; 챻; ) HANGUL SYLLABLE CYAH +CC7C;CC7C;110E 1164;CC7C;110E 1164; # (챼; 챼; 챼; 챼; 챼; ) HANGUL SYLLABLE CYAE +CC7D;CC7D;110E 1164 11A8;CC7D;110E 1164 11A8; # (챽; 챽; 챽; 챽; 챽; ) HANGUL SYLLABLE CYAEG +CC7E;CC7E;110E 1164 11A9;CC7E;110E 1164 11A9; # (챾; 챾; 챾; 챾; 챾; ) HANGUL SYLLABLE CYAEGG +CC7F;CC7F;110E 1164 11AA;CC7F;110E 1164 11AA; # (챿; 챿; 챿; 챿; 챿; ) HANGUL SYLLABLE CYAEGS +CC80;CC80;110E 1164 11AB;CC80;110E 1164 11AB; # (첀; 첀; 첀; 첀; 첀; ) HANGUL SYLLABLE CYAEN +CC81;CC81;110E 1164 11AC;CC81;110E 1164 11AC; # (첁; 첁; 첁; 첁; 첁; ) HANGUL SYLLABLE CYAENJ +CC82;CC82;110E 1164 11AD;CC82;110E 1164 11AD; # (첂; 첂; 첂; 첂; 첂; ) HANGUL SYLLABLE CYAENH +CC83;CC83;110E 1164 11AE;CC83;110E 1164 11AE; # (첃; 첃; 첃; 첃; 첃; ) HANGUL SYLLABLE CYAED +CC84;CC84;110E 1164 11AF;CC84;110E 1164 11AF; # (첄; 첄; 첄; 첄; 첄; ) HANGUL SYLLABLE CYAEL +CC85;CC85;110E 1164 11B0;CC85;110E 1164 11B0; # (첅; 첅; 첅; 첅; 첅; ) HANGUL SYLLABLE CYAELG +CC86;CC86;110E 1164 11B1;CC86;110E 1164 11B1; # (첆; 첆; 첆; 첆; 첆; ) HANGUL SYLLABLE CYAELM +CC87;CC87;110E 1164 11B2;CC87;110E 1164 11B2; # (첇; 첇; 첇; 첇; 첇; ) HANGUL SYLLABLE CYAELB +CC88;CC88;110E 1164 11B3;CC88;110E 1164 11B3; # (첈; 첈; 첈; 첈; 첈; ) HANGUL SYLLABLE CYAELS +CC89;CC89;110E 1164 11B4;CC89;110E 1164 11B4; # (첉; 첉; 첉; 첉; 첉; ) HANGUL SYLLABLE CYAELT +CC8A;CC8A;110E 1164 11B5;CC8A;110E 1164 11B5; # (첊; 첊; 첊; 첊; 첊; ) HANGUL SYLLABLE CYAELP +CC8B;CC8B;110E 1164 11B6;CC8B;110E 1164 11B6; # (첋; 첋; 첋; 첋; 첋; ) HANGUL SYLLABLE CYAELH +CC8C;CC8C;110E 1164 11B7;CC8C;110E 1164 11B7; # (첌; 첌; 첌; 첌; 첌; ) HANGUL SYLLABLE CYAEM +CC8D;CC8D;110E 1164 11B8;CC8D;110E 1164 11B8; # (첍; 첍; 첍; 첍; 첍; ) HANGUL SYLLABLE CYAEB +CC8E;CC8E;110E 1164 11B9;CC8E;110E 1164 11B9; # (첎; 첎; 첎; 첎; 첎; ) HANGUL SYLLABLE CYAEBS +CC8F;CC8F;110E 1164 11BA;CC8F;110E 1164 11BA; # (첏; 첏; 첏; 첏; 첏; ) HANGUL SYLLABLE CYAES +CC90;CC90;110E 1164 11BB;CC90;110E 1164 11BB; # (첐; 첐; 첐; 첐; 첐; ) HANGUL SYLLABLE CYAESS +CC91;CC91;110E 1164 11BC;CC91;110E 1164 11BC; # (첑; 첑; 첑; 첑; 첑; ) HANGUL SYLLABLE CYAENG +CC92;CC92;110E 1164 11BD;CC92;110E 1164 11BD; # (첒; 첒; 첒; 첒; 첒; ) HANGUL SYLLABLE CYAEJ +CC93;CC93;110E 1164 11BE;CC93;110E 1164 11BE; # (첓; 첓; 첓; 첓; 첓; ) HANGUL SYLLABLE CYAEC +CC94;CC94;110E 1164 11BF;CC94;110E 1164 11BF; # (첔; 첔; 첔; 첔; 첔; ) HANGUL SYLLABLE CYAEK +CC95;CC95;110E 1164 11C0;CC95;110E 1164 11C0; # (첕; 첕; 첕; 첕; 첕; ) HANGUL SYLLABLE CYAET +CC96;CC96;110E 1164 11C1;CC96;110E 1164 11C1; # (첖; 첖; 첖; 첖; 첖; ) HANGUL SYLLABLE CYAEP +CC97;CC97;110E 1164 11C2;CC97;110E 1164 11C2; # (첗; 첗; 첗; 첗; 첗; ) HANGUL SYLLABLE CYAEH +CC98;CC98;110E 1165;CC98;110E 1165; # (처; 처; 처; 처; 처; ) HANGUL SYLLABLE CEO +CC99;CC99;110E 1165 11A8;CC99;110E 1165 11A8; # (척; 척; 척; 척; 척; ) HANGUL SYLLABLE CEOG +CC9A;CC9A;110E 1165 11A9;CC9A;110E 1165 11A9; # (첚; 첚; 첚; 첚; 첚; ) HANGUL SYLLABLE CEOGG +CC9B;CC9B;110E 1165 11AA;CC9B;110E 1165 11AA; # (첛; 첛; 첛; 첛; 첛; ) HANGUL SYLLABLE CEOGS +CC9C;CC9C;110E 1165 11AB;CC9C;110E 1165 11AB; # (천; 천; 천; 천; 천; ) HANGUL SYLLABLE CEON +CC9D;CC9D;110E 1165 11AC;CC9D;110E 1165 11AC; # (첝; 첝; 첝; 첝; 첝; ) HANGUL SYLLABLE CEONJ +CC9E;CC9E;110E 1165 11AD;CC9E;110E 1165 11AD; # (첞; 첞; 첞; 첞; 첞; ) HANGUL SYLLABLE CEONH +CC9F;CC9F;110E 1165 11AE;CC9F;110E 1165 11AE; # (첟; 첟; 첟; 첟; 첟; ) HANGUL SYLLABLE CEOD +CCA0;CCA0;110E 1165 11AF;CCA0;110E 1165 11AF; # (철; 철; 철; 철; 철; ) HANGUL SYLLABLE CEOL +CCA1;CCA1;110E 1165 11B0;CCA1;110E 1165 11B0; # (첡; 첡; 첡; 첡; 첡; ) HANGUL SYLLABLE CEOLG +CCA2;CCA2;110E 1165 11B1;CCA2;110E 1165 11B1; # (첢; 첢; 첢; 첢; 첢; ) HANGUL SYLLABLE CEOLM +CCA3;CCA3;110E 1165 11B2;CCA3;110E 1165 11B2; # (첣; 첣; 첣; 첣; 첣; ) HANGUL SYLLABLE CEOLB +CCA4;CCA4;110E 1165 11B3;CCA4;110E 1165 11B3; # (첤; 첤; 첤; 첤; 첤; ) HANGUL SYLLABLE CEOLS +CCA5;CCA5;110E 1165 11B4;CCA5;110E 1165 11B4; # (첥; 첥; 첥; 첥; 첥; ) HANGUL SYLLABLE CEOLT +CCA6;CCA6;110E 1165 11B5;CCA6;110E 1165 11B5; # (첦; 첦; 첦; 첦; 첦; ) HANGUL SYLLABLE CEOLP +CCA7;CCA7;110E 1165 11B6;CCA7;110E 1165 11B6; # (첧; 첧; 첧; 첧; 첧; ) HANGUL SYLLABLE CEOLH +CCA8;CCA8;110E 1165 11B7;CCA8;110E 1165 11B7; # (첨; 첨; 첨; 첨; 첨; ) HANGUL SYLLABLE CEOM +CCA9;CCA9;110E 1165 11B8;CCA9;110E 1165 11B8; # (첩; 첩; 첩; 첩; 첩; ) HANGUL SYLLABLE CEOB +CCAA;CCAA;110E 1165 11B9;CCAA;110E 1165 11B9; # (첪; 첪; 첪; 첪; 첪; ) HANGUL SYLLABLE CEOBS +CCAB;CCAB;110E 1165 11BA;CCAB;110E 1165 11BA; # (첫; 첫; 첫; 첫; 첫; ) HANGUL SYLLABLE CEOS +CCAC;CCAC;110E 1165 11BB;CCAC;110E 1165 11BB; # (첬; 첬; 첬; 첬; 첬; ) HANGUL SYLLABLE CEOSS +CCAD;CCAD;110E 1165 11BC;CCAD;110E 1165 11BC; # (청; 청; 청; 청; 청; ) HANGUL SYLLABLE CEONG +CCAE;CCAE;110E 1165 11BD;CCAE;110E 1165 11BD; # (첮; 첮; 첮; 첮; 첮; ) HANGUL SYLLABLE CEOJ +CCAF;CCAF;110E 1165 11BE;CCAF;110E 1165 11BE; # (첯; 첯; 첯; 첯; 첯; ) HANGUL SYLLABLE CEOC +CCB0;CCB0;110E 1165 11BF;CCB0;110E 1165 11BF; # (첰; 첰; 첰; 첰; 첰; ) HANGUL SYLLABLE CEOK +CCB1;CCB1;110E 1165 11C0;CCB1;110E 1165 11C0; # (첱; 첱; 첱; 첱; 첱; ) HANGUL SYLLABLE CEOT +CCB2;CCB2;110E 1165 11C1;CCB2;110E 1165 11C1; # (첲; 첲; 첲; 첲; 첲; ) HANGUL SYLLABLE CEOP +CCB3;CCB3;110E 1165 11C2;CCB3;110E 1165 11C2; # (첳; 첳; 첳; 첳; 첳; ) HANGUL SYLLABLE CEOH +CCB4;CCB4;110E 1166;CCB4;110E 1166; # (체; 체; 체; 체; 체; ) HANGUL SYLLABLE CE +CCB5;CCB5;110E 1166 11A8;CCB5;110E 1166 11A8; # (첵; 첵; 첵; 첵; 첵; ) HANGUL SYLLABLE CEG +CCB6;CCB6;110E 1166 11A9;CCB6;110E 1166 11A9; # (첶; 첶; 첶; 첶; 첶; ) HANGUL SYLLABLE CEGG +CCB7;CCB7;110E 1166 11AA;CCB7;110E 1166 11AA; # (첷; 첷; 첷; 첷; 첷; ) HANGUL SYLLABLE CEGS +CCB8;CCB8;110E 1166 11AB;CCB8;110E 1166 11AB; # (첸; 첸; 첸; 첸; 첸; ) HANGUL SYLLABLE CEN +CCB9;CCB9;110E 1166 11AC;CCB9;110E 1166 11AC; # (첹; 첹; 첹; 첹; 첹; ) HANGUL SYLLABLE CENJ +CCBA;CCBA;110E 1166 11AD;CCBA;110E 1166 11AD; # (첺; 첺; 첺; 첺; 첺; ) HANGUL SYLLABLE CENH +CCBB;CCBB;110E 1166 11AE;CCBB;110E 1166 11AE; # (첻; 첻; 첻; 첻; 첻; ) HANGUL SYLLABLE CED +CCBC;CCBC;110E 1166 11AF;CCBC;110E 1166 11AF; # (첼; 첼; 첼; 첼; 첼; ) HANGUL SYLLABLE CEL +CCBD;CCBD;110E 1166 11B0;CCBD;110E 1166 11B0; # (첽; 첽; 첽; 첽; 첽; ) HANGUL SYLLABLE CELG +CCBE;CCBE;110E 1166 11B1;CCBE;110E 1166 11B1; # (첾; 첾; 첾; 첾; 첾; ) HANGUL SYLLABLE CELM +CCBF;CCBF;110E 1166 11B2;CCBF;110E 1166 11B2; # (첿; 첿; 첿; 첿; 첿; ) HANGUL SYLLABLE CELB +CCC0;CCC0;110E 1166 11B3;CCC0;110E 1166 11B3; # (쳀; 쳀; 쳀; 쳀; 쳀; ) HANGUL SYLLABLE CELS +CCC1;CCC1;110E 1166 11B4;CCC1;110E 1166 11B4; # (쳁; 쳁; 쳁; 쳁; 쳁; ) HANGUL SYLLABLE CELT +CCC2;CCC2;110E 1166 11B5;CCC2;110E 1166 11B5; # (쳂; 쳂; 쳂; 쳂; 쳂; ) HANGUL SYLLABLE CELP +CCC3;CCC3;110E 1166 11B6;CCC3;110E 1166 11B6; # (쳃; 쳃; 쳃; 쳃; 쳃; ) HANGUL SYLLABLE CELH +CCC4;CCC4;110E 1166 11B7;CCC4;110E 1166 11B7; # (쳄; 쳄; 쳄; 쳄; 쳄; ) HANGUL SYLLABLE CEM +CCC5;CCC5;110E 1166 11B8;CCC5;110E 1166 11B8; # (쳅; 쳅; 쳅; 쳅; 쳅; ) HANGUL SYLLABLE CEB +CCC6;CCC6;110E 1166 11B9;CCC6;110E 1166 11B9; # (쳆; 쳆; 쳆; 쳆; 쳆; ) HANGUL SYLLABLE CEBS +CCC7;CCC7;110E 1166 11BA;CCC7;110E 1166 11BA; # (쳇; 쳇; 쳇; 쳇; 쳇; ) HANGUL SYLLABLE CES +CCC8;CCC8;110E 1166 11BB;CCC8;110E 1166 11BB; # (쳈; 쳈; 쳈; 쳈; 쳈; ) HANGUL SYLLABLE CESS +CCC9;CCC9;110E 1166 11BC;CCC9;110E 1166 11BC; # (쳉; 쳉; 쳉; 쳉; 쳉; ) HANGUL SYLLABLE CENG +CCCA;CCCA;110E 1166 11BD;CCCA;110E 1166 11BD; # (쳊; 쳊; 쳊; 쳊; 쳊; ) HANGUL SYLLABLE CEJ +CCCB;CCCB;110E 1166 11BE;CCCB;110E 1166 11BE; # (쳋; 쳋; 쳋; 쳋; 쳋; ) HANGUL SYLLABLE CEC +CCCC;CCCC;110E 1166 11BF;CCCC;110E 1166 11BF; # (쳌; 쳌; 쳌; 쳌; 쳌; ) HANGUL SYLLABLE CEK +CCCD;CCCD;110E 1166 11C0;CCCD;110E 1166 11C0; # (쳍; 쳍; 쳍; 쳍; 쳍; ) HANGUL SYLLABLE CET +CCCE;CCCE;110E 1166 11C1;CCCE;110E 1166 11C1; # (쳎; 쳎; 쳎; 쳎; 쳎; ) HANGUL SYLLABLE CEP +CCCF;CCCF;110E 1166 11C2;CCCF;110E 1166 11C2; # (쳏; 쳏; 쳏; 쳏; 쳏; ) HANGUL SYLLABLE CEH +CCD0;CCD0;110E 1167;CCD0;110E 1167; # (쳐; 쳐; 쳐; 쳐; 쳐; ) HANGUL SYLLABLE CYEO +CCD1;CCD1;110E 1167 11A8;CCD1;110E 1167 11A8; # (쳑; 쳑; 쳑; 쳑; 쳑; ) HANGUL SYLLABLE CYEOG +CCD2;CCD2;110E 1167 11A9;CCD2;110E 1167 11A9; # (쳒; 쳒; 쳒; 쳒; 쳒; ) HANGUL SYLLABLE CYEOGG +CCD3;CCD3;110E 1167 11AA;CCD3;110E 1167 11AA; # (쳓; 쳓; 쳓; 쳓; 쳓; ) HANGUL SYLLABLE CYEOGS +CCD4;CCD4;110E 1167 11AB;CCD4;110E 1167 11AB; # (쳔; 쳔; 쳔; 쳔; 쳔; ) HANGUL SYLLABLE CYEON +CCD5;CCD5;110E 1167 11AC;CCD5;110E 1167 11AC; # (쳕; 쳕; 쳕; 쳕; 쳕; ) HANGUL SYLLABLE CYEONJ +CCD6;CCD6;110E 1167 11AD;CCD6;110E 1167 11AD; # (쳖; 쳖; 쳖; 쳖; 쳖; ) HANGUL SYLLABLE CYEONH +CCD7;CCD7;110E 1167 11AE;CCD7;110E 1167 11AE; # (쳗; 쳗; 쳗; 쳗; 쳗; ) HANGUL SYLLABLE CYEOD +CCD8;CCD8;110E 1167 11AF;CCD8;110E 1167 11AF; # (쳘; 쳘; 쳘; 쳘; 쳘; ) HANGUL SYLLABLE CYEOL +CCD9;CCD9;110E 1167 11B0;CCD9;110E 1167 11B0; # (쳙; 쳙; 쳙; 쳙; 쳙; ) HANGUL SYLLABLE CYEOLG +CCDA;CCDA;110E 1167 11B1;CCDA;110E 1167 11B1; # (쳚; 쳚; 쳚; 쳚; 쳚; ) HANGUL SYLLABLE CYEOLM +CCDB;CCDB;110E 1167 11B2;CCDB;110E 1167 11B2; # (쳛; 쳛; 쳛; 쳛; 쳛; ) HANGUL SYLLABLE CYEOLB +CCDC;CCDC;110E 1167 11B3;CCDC;110E 1167 11B3; # (쳜; 쳜; 쳜; 쳜; 쳜; ) HANGUL SYLLABLE CYEOLS +CCDD;CCDD;110E 1167 11B4;CCDD;110E 1167 11B4; # (쳝; 쳝; 쳝; 쳝; 쳝; ) HANGUL SYLLABLE CYEOLT +CCDE;CCDE;110E 1167 11B5;CCDE;110E 1167 11B5; # (쳞; 쳞; 쳞; 쳞; 쳞; ) HANGUL SYLLABLE CYEOLP +CCDF;CCDF;110E 1167 11B6;CCDF;110E 1167 11B6; # (쳟; 쳟; 쳟; 쳟; 쳟; ) HANGUL SYLLABLE CYEOLH +CCE0;CCE0;110E 1167 11B7;CCE0;110E 1167 11B7; # (쳠; 쳠; 쳠; 쳠; 쳠; ) HANGUL SYLLABLE CYEOM +CCE1;CCE1;110E 1167 11B8;CCE1;110E 1167 11B8; # (쳡; 쳡; 쳡; 쳡; 쳡; ) HANGUL SYLLABLE CYEOB +CCE2;CCE2;110E 1167 11B9;CCE2;110E 1167 11B9; # (쳢; 쳢; 쳢; 쳢; 쳢; ) HANGUL SYLLABLE CYEOBS +CCE3;CCE3;110E 1167 11BA;CCE3;110E 1167 11BA; # (쳣; 쳣; 쳣; 쳣; 쳣; ) HANGUL SYLLABLE CYEOS +CCE4;CCE4;110E 1167 11BB;CCE4;110E 1167 11BB; # (쳤; 쳤; 쳤; 쳤; 쳤; ) HANGUL SYLLABLE CYEOSS +CCE5;CCE5;110E 1167 11BC;CCE5;110E 1167 11BC; # (쳥; 쳥; 쳥; 쳥; 쳥; ) HANGUL SYLLABLE CYEONG +CCE6;CCE6;110E 1167 11BD;CCE6;110E 1167 11BD; # (쳦; 쳦; 쳦; 쳦; 쳦; ) HANGUL SYLLABLE CYEOJ +CCE7;CCE7;110E 1167 11BE;CCE7;110E 1167 11BE; # (쳧; 쳧; 쳧; 쳧; 쳧; ) HANGUL SYLLABLE CYEOC +CCE8;CCE8;110E 1167 11BF;CCE8;110E 1167 11BF; # (쳨; 쳨; 쳨; 쳨; 쳨; ) HANGUL SYLLABLE CYEOK +CCE9;CCE9;110E 1167 11C0;CCE9;110E 1167 11C0; # (쳩; 쳩; 쳩; 쳩; 쳩; ) HANGUL SYLLABLE CYEOT +CCEA;CCEA;110E 1167 11C1;CCEA;110E 1167 11C1; # (쳪; 쳪; 쳪; 쳪; 쳪; ) HANGUL SYLLABLE CYEOP +CCEB;CCEB;110E 1167 11C2;CCEB;110E 1167 11C2; # (쳫; 쳫; 쳫; 쳫; 쳫; ) HANGUL SYLLABLE CYEOH +CCEC;CCEC;110E 1168;CCEC;110E 1168; # (쳬; 쳬; 쳬; 쳬; 쳬; ) HANGUL SYLLABLE CYE +CCED;CCED;110E 1168 11A8;CCED;110E 1168 11A8; # (쳭; 쳭; 쳭; 쳭; 쳭; ) HANGUL SYLLABLE CYEG +CCEE;CCEE;110E 1168 11A9;CCEE;110E 1168 11A9; # (쳮; 쳮; 쳮; 쳮; 쳮; ) HANGUL SYLLABLE CYEGG +CCEF;CCEF;110E 1168 11AA;CCEF;110E 1168 11AA; # (쳯; 쳯; 쳯; 쳯; 쳯; ) HANGUL SYLLABLE CYEGS +CCF0;CCF0;110E 1168 11AB;CCF0;110E 1168 11AB; # (쳰; 쳰; 쳰; 쳰; 쳰; ) HANGUL SYLLABLE CYEN +CCF1;CCF1;110E 1168 11AC;CCF1;110E 1168 11AC; # (쳱; 쳱; 쳱; 쳱; 쳱; ) HANGUL SYLLABLE CYENJ +CCF2;CCF2;110E 1168 11AD;CCF2;110E 1168 11AD; # (쳲; 쳲; 쳲; 쳲; 쳲; ) HANGUL SYLLABLE CYENH +CCF3;CCF3;110E 1168 11AE;CCF3;110E 1168 11AE; # (쳳; 쳳; 쳳; 쳳; 쳳; ) HANGUL SYLLABLE CYED +CCF4;CCF4;110E 1168 11AF;CCF4;110E 1168 11AF; # (쳴; 쳴; 쳴; 쳴; 쳴; ) HANGUL SYLLABLE CYEL +CCF5;CCF5;110E 1168 11B0;CCF5;110E 1168 11B0; # (쳵; 쳵; 쳵; 쳵; 쳵; ) HANGUL SYLLABLE CYELG +CCF6;CCF6;110E 1168 11B1;CCF6;110E 1168 11B1; # (쳶; 쳶; 쳶; 쳶; 쳶; ) HANGUL SYLLABLE CYELM +CCF7;CCF7;110E 1168 11B2;CCF7;110E 1168 11B2; # (쳷; 쳷; 쳷; 쳷; 쳷; ) HANGUL SYLLABLE CYELB +CCF8;CCF8;110E 1168 11B3;CCF8;110E 1168 11B3; # (쳸; 쳸; 쳸; 쳸; 쳸; ) HANGUL SYLLABLE CYELS +CCF9;CCF9;110E 1168 11B4;CCF9;110E 1168 11B4; # (쳹; 쳹; 쳹; 쳹; 쳹; ) HANGUL SYLLABLE CYELT +CCFA;CCFA;110E 1168 11B5;CCFA;110E 1168 11B5; # (쳺; 쳺; 쳺; 쳺; 쳺; ) HANGUL SYLLABLE CYELP +CCFB;CCFB;110E 1168 11B6;CCFB;110E 1168 11B6; # (쳻; 쳻; 쳻; 쳻; 쳻; ) HANGUL SYLLABLE CYELH +CCFC;CCFC;110E 1168 11B7;CCFC;110E 1168 11B7; # (쳼; 쳼; 쳼; 쳼; 쳼; ) HANGUL SYLLABLE CYEM +CCFD;CCFD;110E 1168 11B8;CCFD;110E 1168 11B8; # (쳽; 쳽; 쳽; 쳽; 쳽; ) HANGUL SYLLABLE CYEB +CCFE;CCFE;110E 1168 11B9;CCFE;110E 1168 11B9; # (쳾; 쳾; 쳾; 쳾; 쳾; ) HANGUL SYLLABLE CYEBS +CCFF;CCFF;110E 1168 11BA;CCFF;110E 1168 11BA; # (쳿; 쳿; 쳿; 쳿; 쳿; ) HANGUL SYLLABLE CYES +CD00;CD00;110E 1168 11BB;CD00;110E 1168 11BB; # (촀; 촀; 촀; 촀; 촀; ) HANGUL SYLLABLE CYESS +CD01;CD01;110E 1168 11BC;CD01;110E 1168 11BC; # (촁; 촁; 촁; 촁; 촁; ) HANGUL SYLLABLE CYENG +CD02;CD02;110E 1168 11BD;CD02;110E 1168 11BD; # (촂; 촂; 촂; 촂; 촂; ) HANGUL SYLLABLE CYEJ +CD03;CD03;110E 1168 11BE;CD03;110E 1168 11BE; # (촃; 촃; 촃; 촃; 촃; ) HANGUL SYLLABLE CYEC +CD04;CD04;110E 1168 11BF;CD04;110E 1168 11BF; # (촄; 촄; 촄; 촄; 촄; ) HANGUL SYLLABLE CYEK +CD05;CD05;110E 1168 11C0;CD05;110E 1168 11C0; # (촅; 촅; 촅; 촅; 촅; ) HANGUL SYLLABLE CYET +CD06;CD06;110E 1168 11C1;CD06;110E 1168 11C1; # (촆; 촆; 촆; 촆; 촆; ) HANGUL SYLLABLE CYEP +CD07;CD07;110E 1168 11C2;CD07;110E 1168 11C2; # (촇; 촇; 촇; 촇; 촇; ) HANGUL SYLLABLE CYEH +CD08;CD08;110E 1169;CD08;110E 1169; # (초; 초; 초; 초; 초; ) HANGUL SYLLABLE CO +CD09;CD09;110E 1169 11A8;CD09;110E 1169 11A8; # (촉; 촉; 촉; 촉; 촉; ) HANGUL SYLLABLE COG +CD0A;CD0A;110E 1169 11A9;CD0A;110E 1169 11A9; # (촊; 촊; 촊; 촊; 촊; ) HANGUL SYLLABLE COGG +CD0B;CD0B;110E 1169 11AA;CD0B;110E 1169 11AA; # (촋; 촋; 촋; 촋; 촋; ) HANGUL SYLLABLE COGS +CD0C;CD0C;110E 1169 11AB;CD0C;110E 1169 11AB; # (촌; 촌; 촌; 촌; 촌; ) HANGUL SYLLABLE CON +CD0D;CD0D;110E 1169 11AC;CD0D;110E 1169 11AC; # (촍; 촍; 촍; 촍; 촍; ) HANGUL SYLLABLE CONJ +CD0E;CD0E;110E 1169 11AD;CD0E;110E 1169 11AD; # (촎; 촎; 촎; 촎; 촎; ) HANGUL SYLLABLE CONH +CD0F;CD0F;110E 1169 11AE;CD0F;110E 1169 11AE; # (촏; 촏; 촏; 촏; 촏; ) HANGUL SYLLABLE COD +CD10;CD10;110E 1169 11AF;CD10;110E 1169 11AF; # (촐; 촐; 촐; 촐; 촐; ) HANGUL SYLLABLE COL +CD11;CD11;110E 1169 11B0;CD11;110E 1169 11B0; # (촑; 촑; 촑; 촑; 촑; ) HANGUL SYLLABLE COLG +CD12;CD12;110E 1169 11B1;CD12;110E 1169 11B1; # (촒; 촒; 촒; 촒; 촒; ) HANGUL SYLLABLE COLM +CD13;CD13;110E 1169 11B2;CD13;110E 1169 11B2; # (촓; 촓; 촓; 촓; 촓; ) HANGUL SYLLABLE COLB +CD14;CD14;110E 1169 11B3;CD14;110E 1169 11B3; # (촔; 촔; 촔; 촔; 촔; ) HANGUL SYLLABLE COLS +CD15;CD15;110E 1169 11B4;CD15;110E 1169 11B4; # (촕; 촕; 촕; 촕; 촕; ) HANGUL SYLLABLE COLT +CD16;CD16;110E 1169 11B5;CD16;110E 1169 11B5; # (촖; 촖; 촖; 촖; 촖; ) HANGUL SYLLABLE COLP +CD17;CD17;110E 1169 11B6;CD17;110E 1169 11B6; # (촗; 촗; 촗; 촗; 촗; ) HANGUL SYLLABLE COLH +CD18;CD18;110E 1169 11B7;CD18;110E 1169 11B7; # (촘; 촘; 촘; 촘; 촘; ) HANGUL SYLLABLE COM +CD19;CD19;110E 1169 11B8;CD19;110E 1169 11B8; # (촙; 촙; 촙; 촙; 촙; ) HANGUL SYLLABLE COB +CD1A;CD1A;110E 1169 11B9;CD1A;110E 1169 11B9; # (촚; 촚; 촚; 촚; 촚; ) HANGUL SYLLABLE COBS +CD1B;CD1B;110E 1169 11BA;CD1B;110E 1169 11BA; # (촛; 촛; 촛; 촛; 촛; ) HANGUL SYLLABLE COS +CD1C;CD1C;110E 1169 11BB;CD1C;110E 1169 11BB; # (촜; 촜; 촜; 촜; 촜; ) HANGUL SYLLABLE COSS +CD1D;CD1D;110E 1169 11BC;CD1D;110E 1169 11BC; # (총; 총; 총; 총; 총; ) HANGUL SYLLABLE CONG +CD1E;CD1E;110E 1169 11BD;CD1E;110E 1169 11BD; # (촞; 촞; 촞; 촞; 촞; ) HANGUL SYLLABLE COJ +CD1F;CD1F;110E 1169 11BE;CD1F;110E 1169 11BE; # (촟; 촟; 촟; 촟; 촟; ) HANGUL SYLLABLE COC +CD20;CD20;110E 1169 11BF;CD20;110E 1169 11BF; # (촠; 촠; 촠; 촠; 촠; ) HANGUL SYLLABLE COK +CD21;CD21;110E 1169 11C0;CD21;110E 1169 11C0; # (촡; 촡; 촡; 촡; 촡; ) HANGUL SYLLABLE COT +CD22;CD22;110E 1169 11C1;CD22;110E 1169 11C1; # (촢; 촢; 촢; 촢; 촢; ) HANGUL SYLLABLE COP +CD23;CD23;110E 1169 11C2;CD23;110E 1169 11C2; # (촣; 촣; 촣; 촣; 촣; ) HANGUL SYLLABLE COH +CD24;CD24;110E 116A;CD24;110E 116A; # (촤; 촤; 촤; 촤; 촤; ) HANGUL SYLLABLE CWA +CD25;CD25;110E 116A 11A8;CD25;110E 116A 11A8; # (촥; 촥; 촥; 촥; 촥; ) HANGUL SYLLABLE CWAG +CD26;CD26;110E 116A 11A9;CD26;110E 116A 11A9; # (촦; 촦; 촦; 촦; 촦; ) HANGUL SYLLABLE CWAGG +CD27;CD27;110E 116A 11AA;CD27;110E 116A 11AA; # (촧; 촧; 촧; 촧; 촧; ) HANGUL SYLLABLE CWAGS +CD28;CD28;110E 116A 11AB;CD28;110E 116A 11AB; # (촨; 촨; 촨; 촨; 촨; ) HANGUL SYLLABLE CWAN +CD29;CD29;110E 116A 11AC;CD29;110E 116A 11AC; # (촩; 촩; 촩; 촩; 촩; ) HANGUL SYLLABLE CWANJ +CD2A;CD2A;110E 116A 11AD;CD2A;110E 116A 11AD; # (촪; 촪; 촪; 촪; 촪; ) HANGUL SYLLABLE CWANH +CD2B;CD2B;110E 116A 11AE;CD2B;110E 116A 11AE; # (촫; 촫; 촫; 촫; 촫; ) HANGUL SYLLABLE CWAD +CD2C;CD2C;110E 116A 11AF;CD2C;110E 116A 11AF; # (촬; 촬; 촬; 촬; 촬; ) HANGUL SYLLABLE CWAL +CD2D;CD2D;110E 116A 11B0;CD2D;110E 116A 11B0; # (촭; 촭; 촭; 촭; 촭; ) HANGUL SYLLABLE CWALG +CD2E;CD2E;110E 116A 11B1;CD2E;110E 116A 11B1; # (촮; 촮; 촮; 촮; 촮; ) HANGUL SYLLABLE CWALM +CD2F;CD2F;110E 116A 11B2;CD2F;110E 116A 11B2; # (촯; 촯; 촯; 촯; 촯; ) HANGUL SYLLABLE CWALB +CD30;CD30;110E 116A 11B3;CD30;110E 116A 11B3; # (촰; 촰; 촰; 촰; 촰; ) HANGUL SYLLABLE CWALS +CD31;CD31;110E 116A 11B4;CD31;110E 116A 11B4; # (촱; 촱; 촱; 촱; 촱; ) HANGUL SYLLABLE CWALT +CD32;CD32;110E 116A 11B5;CD32;110E 116A 11B5; # (촲; 촲; 촲; 촲; 촲; ) HANGUL SYLLABLE CWALP +CD33;CD33;110E 116A 11B6;CD33;110E 116A 11B6; # (촳; 촳; 촳; 촳; 촳; ) HANGUL SYLLABLE CWALH +CD34;CD34;110E 116A 11B7;CD34;110E 116A 11B7; # (촴; 촴; 촴; 촴; 촴; ) HANGUL SYLLABLE CWAM +CD35;CD35;110E 116A 11B8;CD35;110E 116A 11B8; # (촵; 촵; 촵; 촵; 촵; ) HANGUL SYLLABLE CWAB +CD36;CD36;110E 116A 11B9;CD36;110E 116A 11B9; # (촶; 촶; 촶; 촶; 촶; ) HANGUL SYLLABLE CWABS +CD37;CD37;110E 116A 11BA;CD37;110E 116A 11BA; # (촷; 촷; 촷; 촷; 촷; ) HANGUL SYLLABLE CWAS +CD38;CD38;110E 116A 11BB;CD38;110E 116A 11BB; # (촸; 촸; 촸; 촸; 촸; ) HANGUL SYLLABLE CWASS +CD39;CD39;110E 116A 11BC;CD39;110E 116A 11BC; # (촹; 촹; 촹; 촹; 촹; ) HANGUL SYLLABLE CWANG +CD3A;CD3A;110E 116A 11BD;CD3A;110E 116A 11BD; # (촺; 촺; 촺; 촺; 촺; ) HANGUL SYLLABLE CWAJ +CD3B;CD3B;110E 116A 11BE;CD3B;110E 116A 11BE; # (촻; 촻; 촻; 촻; 촻; ) HANGUL SYLLABLE CWAC +CD3C;CD3C;110E 116A 11BF;CD3C;110E 116A 11BF; # (촼; 촼; 촼; 촼; 촼; ) HANGUL SYLLABLE CWAK +CD3D;CD3D;110E 116A 11C0;CD3D;110E 116A 11C0; # (촽; 촽; 촽; 촽; 촽; ) HANGUL SYLLABLE CWAT +CD3E;CD3E;110E 116A 11C1;CD3E;110E 116A 11C1; # (촾; 촾; 촾; 촾; 촾; ) HANGUL SYLLABLE CWAP +CD3F;CD3F;110E 116A 11C2;CD3F;110E 116A 11C2; # (촿; 촿; 촿; 촿; 촿; ) HANGUL SYLLABLE CWAH +CD40;CD40;110E 116B;CD40;110E 116B; # (쵀; 쵀; 쵀; 쵀; 쵀; ) HANGUL SYLLABLE CWAE +CD41;CD41;110E 116B 11A8;CD41;110E 116B 11A8; # (쵁; 쵁; 쵁; 쵁; 쵁; ) HANGUL SYLLABLE CWAEG +CD42;CD42;110E 116B 11A9;CD42;110E 116B 11A9; # (쵂; 쵂; 쵂; 쵂; 쵂; ) HANGUL SYLLABLE CWAEGG +CD43;CD43;110E 116B 11AA;CD43;110E 116B 11AA; # (쵃; 쵃; 쵃; 쵃; 쵃; ) HANGUL SYLLABLE CWAEGS +CD44;CD44;110E 116B 11AB;CD44;110E 116B 11AB; # (쵄; 쵄; 쵄; 쵄; 쵄; ) HANGUL SYLLABLE CWAEN +CD45;CD45;110E 116B 11AC;CD45;110E 116B 11AC; # (쵅; 쵅; 쵅; 쵅; 쵅; ) HANGUL SYLLABLE CWAENJ +CD46;CD46;110E 116B 11AD;CD46;110E 116B 11AD; # (쵆; 쵆; 쵆; 쵆; 쵆; ) HANGUL SYLLABLE CWAENH +CD47;CD47;110E 116B 11AE;CD47;110E 116B 11AE; # (쵇; 쵇; 쵇; 쵇; 쵇; ) HANGUL SYLLABLE CWAED +CD48;CD48;110E 116B 11AF;CD48;110E 116B 11AF; # (쵈; 쵈; 쵈; 쵈; 쵈; ) HANGUL SYLLABLE CWAEL +CD49;CD49;110E 116B 11B0;CD49;110E 116B 11B0; # (쵉; 쵉; 쵉; 쵉; 쵉; ) HANGUL SYLLABLE CWAELG +CD4A;CD4A;110E 116B 11B1;CD4A;110E 116B 11B1; # (쵊; 쵊; 쵊; 쵊; 쵊; ) HANGUL SYLLABLE CWAELM +CD4B;CD4B;110E 116B 11B2;CD4B;110E 116B 11B2; # (쵋; 쵋; 쵋; 쵋; 쵋; ) HANGUL SYLLABLE CWAELB +CD4C;CD4C;110E 116B 11B3;CD4C;110E 116B 11B3; # (쵌; 쵌; 쵌; 쵌; 쵌; ) HANGUL SYLLABLE CWAELS +CD4D;CD4D;110E 116B 11B4;CD4D;110E 116B 11B4; # (쵍; 쵍; 쵍; 쵍; 쵍; ) HANGUL SYLLABLE CWAELT +CD4E;CD4E;110E 116B 11B5;CD4E;110E 116B 11B5; # (쵎; 쵎; 쵎; 쵎; 쵎; ) HANGUL SYLLABLE CWAELP +CD4F;CD4F;110E 116B 11B6;CD4F;110E 116B 11B6; # (쵏; 쵏; 쵏; 쵏; 쵏; ) HANGUL SYLLABLE CWAELH +CD50;CD50;110E 116B 11B7;CD50;110E 116B 11B7; # (쵐; 쵐; 쵐; 쵐; 쵐; ) HANGUL SYLLABLE CWAEM +CD51;CD51;110E 116B 11B8;CD51;110E 116B 11B8; # (쵑; 쵑; 쵑; 쵑; 쵑; ) HANGUL SYLLABLE CWAEB +CD52;CD52;110E 116B 11B9;CD52;110E 116B 11B9; # (쵒; 쵒; 쵒; 쵒; 쵒; ) HANGUL SYLLABLE CWAEBS +CD53;CD53;110E 116B 11BA;CD53;110E 116B 11BA; # (쵓; 쵓; 쵓; 쵓; 쵓; ) HANGUL SYLLABLE CWAES +CD54;CD54;110E 116B 11BB;CD54;110E 116B 11BB; # (쵔; 쵔; 쵔; 쵔; 쵔; ) HANGUL SYLLABLE CWAESS +CD55;CD55;110E 116B 11BC;CD55;110E 116B 11BC; # (쵕; 쵕; 쵕; 쵕; 쵕; ) HANGUL SYLLABLE CWAENG +CD56;CD56;110E 116B 11BD;CD56;110E 116B 11BD; # (쵖; 쵖; 쵖; 쵖; 쵖; ) HANGUL SYLLABLE CWAEJ +CD57;CD57;110E 116B 11BE;CD57;110E 116B 11BE; # (쵗; 쵗; 쵗; 쵗; 쵗; ) HANGUL SYLLABLE CWAEC +CD58;CD58;110E 116B 11BF;CD58;110E 116B 11BF; # (쵘; 쵘; 쵘; 쵘; 쵘; ) HANGUL SYLLABLE CWAEK +CD59;CD59;110E 116B 11C0;CD59;110E 116B 11C0; # (쵙; 쵙; 쵙; 쵙; 쵙; ) HANGUL SYLLABLE CWAET +CD5A;CD5A;110E 116B 11C1;CD5A;110E 116B 11C1; # (쵚; 쵚; 쵚; 쵚; 쵚; ) HANGUL SYLLABLE CWAEP +CD5B;CD5B;110E 116B 11C2;CD5B;110E 116B 11C2; # (쵛; 쵛; 쵛; 쵛; 쵛; ) HANGUL SYLLABLE CWAEH +CD5C;CD5C;110E 116C;CD5C;110E 116C; # (최; 최; 최; 최; 최; ) HANGUL SYLLABLE COE +CD5D;CD5D;110E 116C 11A8;CD5D;110E 116C 11A8; # (쵝; 쵝; 쵝; 쵝; 쵝; ) HANGUL SYLLABLE COEG +CD5E;CD5E;110E 116C 11A9;CD5E;110E 116C 11A9; # (쵞; 쵞; 쵞; 쵞; 쵞; ) HANGUL SYLLABLE COEGG +CD5F;CD5F;110E 116C 11AA;CD5F;110E 116C 11AA; # (쵟; 쵟; 쵟; 쵟; 쵟; ) HANGUL SYLLABLE COEGS +CD60;CD60;110E 116C 11AB;CD60;110E 116C 11AB; # (쵠; 쵠; 쵠; 쵠; 쵠; ) HANGUL SYLLABLE COEN +CD61;CD61;110E 116C 11AC;CD61;110E 116C 11AC; # (쵡; 쵡; 쵡; 쵡; 쵡; ) HANGUL SYLLABLE COENJ +CD62;CD62;110E 116C 11AD;CD62;110E 116C 11AD; # (쵢; 쵢; 쵢; 쵢; 쵢; ) HANGUL SYLLABLE COENH +CD63;CD63;110E 116C 11AE;CD63;110E 116C 11AE; # (쵣; 쵣; 쵣; 쵣; 쵣; ) HANGUL SYLLABLE COED +CD64;CD64;110E 116C 11AF;CD64;110E 116C 11AF; # (쵤; 쵤; 쵤; 쵤; 쵤; ) HANGUL SYLLABLE COEL +CD65;CD65;110E 116C 11B0;CD65;110E 116C 11B0; # (쵥; 쵥; 쵥; 쵥; 쵥; ) HANGUL SYLLABLE COELG +CD66;CD66;110E 116C 11B1;CD66;110E 116C 11B1; # (쵦; 쵦; 쵦; 쵦; 쵦; ) HANGUL SYLLABLE COELM +CD67;CD67;110E 116C 11B2;CD67;110E 116C 11B2; # (쵧; 쵧; 쵧; 쵧; 쵧; ) HANGUL SYLLABLE COELB +CD68;CD68;110E 116C 11B3;CD68;110E 116C 11B3; # (쵨; 쵨; 쵨; 쵨; 쵨; ) HANGUL SYLLABLE COELS +CD69;CD69;110E 116C 11B4;CD69;110E 116C 11B4; # (쵩; 쵩; 쵩; 쵩; 쵩; ) HANGUL SYLLABLE COELT +CD6A;CD6A;110E 116C 11B5;CD6A;110E 116C 11B5; # (쵪; 쵪; 쵪; 쵪; 쵪; ) HANGUL SYLLABLE COELP +CD6B;CD6B;110E 116C 11B6;CD6B;110E 116C 11B6; # (쵫; 쵫; 쵫; 쵫; 쵫; ) HANGUL SYLLABLE COELH +CD6C;CD6C;110E 116C 11B7;CD6C;110E 116C 11B7; # (쵬; 쵬; 쵬; 쵬; 쵬; ) HANGUL SYLLABLE COEM +CD6D;CD6D;110E 116C 11B8;CD6D;110E 116C 11B8; # (쵭; 쵭; 쵭; 쵭; 쵭; ) HANGUL SYLLABLE COEB +CD6E;CD6E;110E 116C 11B9;CD6E;110E 116C 11B9; # (쵮; 쵮; 쵮; 쵮; 쵮; ) HANGUL SYLLABLE COEBS +CD6F;CD6F;110E 116C 11BA;CD6F;110E 116C 11BA; # (쵯; 쵯; 쵯; 쵯; 쵯; ) HANGUL SYLLABLE COES +CD70;CD70;110E 116C 11BB;CD70;110E 116C 11BB; # (쵰; 쵰; 쵰; 쵰; 쵰; ) HANGUL SYLLABLE COESS +CD71;CD71;110E 116C 11BC;CD71;110E 116C 11BC; # (쵱; 쵱; 쵱; 쵱; 쵱; ) HANGUL SYLLABLE COENG +CD72;CD72;110E 116C 11BD;CD72;110E 116C 11BD; # (쵲; 쵲; 쵲; 쵲; 쵲; ) HANGUL SYLLABLE COEJ +CD73;CD73;110E 116C 11BE;CD73;110E 116C 11BE; # (쵳; 쵳; 쵳; 쵳; 쵳; ) HANGUL SYLLABLE COEC +CD74;CD74;110E 116C 11BF;CD74;110E 116C 11BF; # (쵴; 쵴; 쵴; 쵴; 쵴; ) HANGUL SYLLABLE COEK +CD75;CD75;110E 116C 11C0;CD75;110E 116C 11C0; # (쵵; 쵵; 쵵; 쵵; 쵵; ) HANGUL SYLLABLE COET +CD76;CD76;110E 116C 11C1;CD76;110E 116C 11C1; # (쵶; 쵶; 쵶; 쵶; 쵶; ) HANGUL SYLLABLE COEP +CD77;CD77;110E 116C 11C2;CD77;110E 116C 11C2; # (쵷; 쵷; 쵷; 쵷; 쵷; ) HANGUL SYLLABLE COEH +CD78;CD78;110E 116D;CD78;110E 116D; # (쵸; 쵸; 쵸; 쵸; 쵸; ) HANGUL SYLLABLE CYO +CD79;CD79;110E 116D 11A8;CD79;110E 116D 11A8; # (쵹; 쵹; 쵹; 쵹; 쵹; ) HANGUL SYLLABLE CYOG +CD7A;CD7A;110E 116D 11A9;CD7A;110E 116D 11A9; # (쵺; 쵺; 쵺; 쵺; 쵺; ) HANGUL SYLLABLE CYOGG +CD7B;CD7B;110E 116D 11AA;CD7B;110E 116D 11AA; # (쵻; 쵻; 쵻; 쵻; 쵻; ) HANGUL SYLLABLE CYOGS +CD7C;CD7C;110E 116D 11AB;CD7C;110E 116D 11AB; # (쵼; 쵼; 쵼; 쵼; 쵼; ) HANGUL SYLLABLE CYON +CD7D;CD7D;110E 116D 11AC;CD7D;110E 116D 11AC; # (쵽; 쵽; 쵽; 쵽; 쵽; ) HANGUL SYLLABLE CYONJ +CD7E;CD7E;110E 116D 11AD;CD7E;110E 116D 11AD; # (쵾; 쵾; 쵾; 쵾; 쵾; ) HANGUL SYLLABLE CYONH +CD7F;CD7F;110E 116D 11AE;CD7F;110E 116D 11AE; # (쵿; 쵿; 쵿; 쵿; 쵿; ) HANGUL SYLLABLE CYOD +CD80;CD80;110E 116D 11AF;CD80;110E 116D 11AF; # (춀; 춀; 춀; 춀; 춀; ) HANGUL SYLLABLE CYOL +CD81;CD81;110E 116D 11B0;CD81;110E 116D 11B0; # (춁; 춁; 춁; 춁; 춁; ) HANGUL SYLLABLE CYOLG +CD82;CD82;110E 116D 11B1;CD82;110E 116D 11B1; # (춂; 춂; 춂; 춂; 춂; ) HANGUL SYLLABLE CYOLM +CD83;CD83;110E 116D 11B2;CD83;110E 116D 11B2; # (춃; 춃; 춃; 춃; 춃; ) HANGUL SYLLABLE CYOLB +CD84;CD84;110E 116D 11B3;CD84;110E 116D 11B3; # (춄; 춄; 춄; 춄; 춄; ) HANGUL SYLLABLE CYOLS +CD85;CD85;110E 116D 11B4;CD85;110E 116D 11B4; # (춅; 춅; 춅; 춅; 춅; ) HANGUL SYLLABLE CYOLT +CD86;CD86;110E 116D 11B5;CD86;110E 116D 11B5; # (춆; 춆; 춆; 춆; 춆; ) HANGUL SYLLABLE CYOLP +CD87;CD87;110E 116D 11B6;CD87;110E 116D 11B6; # (춇; 춇; 춇; 춇; 춇; ) HANGUL SYLLABLE CYOLH +CD88;CD88;110E 116D 11B7;CD88;110E 116D 11B7; # (춈; 춈; 춈; 춈; 춈; ) HANGUL SYLLABLE CYOM +CD89;CD89;110E 116D 11B8;CD89;110E 116D 11B8; # (춉; 춉; 춉; 춉; 춉; ) HANGUL SYLLABLE CYOB +CD8A;CD8A;110E 116D 11B9;CD8A;110E 116D 11B9; # (춊; 춊; 춊; 춊; 춊; ) HANGUL SYLLABLE CYOBS +CD8B;CD8B;110E 116D 11BA;CD8B;110E 116D 11BA; # (춋; 춋; 춋; 춋; 춋; ) HANGUL SYLLABLE CYOS +CD8C;CD8C;110E 116D 11BB;CD8C;110E 116D 11BB; # (춌; 춌; 춌; 춌; 춌; ) HANGUL SYLLABLE CYOSS +CD8D;CD8D;110E 116D 11BC;CD8D;110E 116D 11BC; # (춍; 춍; 춍; 춍; 춍; ) HANGUL SYLLABLE CYONG +CD8E;CD8E;110E 116D 11BD;CD8E;110E 116D 11BD; # (춎; 춎; 춎; 춎; 춎; ) HANGUL SYLLABLE CYOJ +CD8F;CD8F;110E 116D 11BE;CD8F;110E 116D 11BE; # (춏; 춏; 춏; 춏; 춏; ) HANGUL SYLLABLE CYOC +CD90;CD90;110E 116D 11BF;CD90;110E 116D 11BF; # (춐; 춐; 춐; 춐; 춐; ) HANGUL SYLLABLE CYOK +CD91;CD91;110E 116D 11C0;CD91;110E 116D 11C0; # (춑; 춑; 춑; 춑; 춑; ) HANGUL SYLLABLE CYOT +CD92;CD92;110E 116D 11C1;CD92;110E 116D 11C1; # (춒; 춒; 춒; 춒; 춒; ) HANGUL SYLLABLE CYOP +CD93;CD93;110E 116D 11C2;CD93;110E 116D 11C2; # (춓; 춓; 춓; 춓; 춓; ) HANGUL SYLLABLE CYOH +CD94;CD94;110E 116E;CD94;110E 116E; # (추; 추; 추; 추; 추; ) HANGUL SYLLABLE CU +CD95;CD95;110E 116E 11A8;CD95;110E 116E 11A8; # (축; 축; 축; 축; 축; ) HANGUL SYLLABLE CUG +CD96;CD96;110E 116E 11A9;CD96;110E 116E 11A9; # (춖; 춖; 춖; 춖; 춖; ) HANGUL SYLLABLE CUGG +CD97;CD97;110E 116E 11AA;CD97;110E 116E 11AA; # (춗; 춗; 춗; 춗; 춗; ) HANGUL SYLLABLE CUGS +CD98;CD98;110E 116E 11AB;CD98;110E 116E 11AB; # (춘; 춘; 춘; 춘; 춘; ) HANGUL SYLLABLE CUN +CD99;CD99;110E 116E 11AC;CD99;110E 116E 11AC; # (춙; 춙; 춙; 춙; 춙; ) HANGUL SYLLABLE CUNJ +CD9A;CD9A;110E 116E 11AD;CD9A;110E 116E 11AD; # (춚; 춚; 춚; 춚; 춚; ) HANGUL SYLLABLE CUNH +CD9B;CD9B;110E 116E 11AE;CD9B;110E 116E 11AE; # (춛; 춛; 춛; 춛; 춛; ) HANGUL SYLLABLE CUD +CD9C;CD9C;110E 116E 11AF;CD9C;110E 116E 11AF; # (출; 출; 출; 출; 출; ) HANGUL SYLLABLE CUL +CD9D;CD9D;110E 116E 11B0;CD9D;110E 116E 11B0; # (춝; 춝; 춝; 춝; 춝; ) HANGUL SYLLABLE CULG +CD9E;CD9E;110E 116E 11B1;CD9E;110E 116E 11B1; # (춞; 춞; 춞; 춞; 춞; ) HANGUL SYLLABLE CULM +CD9F;CD9F;110E 116E 11B2;CD9F;110E 116E 11B2; # (춟; 춟; 춟; 춟; 춟; ) HANGUL SYLLABLE CULB +CDA0;CDA0;110E 116E 11B3;CDA0;110E 116E 11B3; # (춠; 춠; 춠; 춠; 춠; ) HANGUL SYLLABLE CULS +CDA1;CDA1;110E 116E 11B4;CDA1;110E 116E 11B4; # (춡; 춡; 춡; 춡; 춡; ) HANGUL SYLLABLE CULT +CDA2;CDA2;110E 116E 11B5;CDA2;110E 116E 11B5; # (춢; 춢; 춢; 춢; 춢; ) HANGUL SYLLABLE CULP +CDA3;CDA3;110E 116E 11B6;CDA3;110E 116E 11B6; # (춣; 춣; 춣; 춣; 춣; ) HANGUL SYLLABLE CULH +CDA4;CDA4;110E 116E 11B7;CDA4;110E 116E 11B7; # (춤; 춤; 춤; 춤; 춤; ) HANGUL SYLLABLE CUM +CDA5;CDA5;110E 116E 11B8;CDA5;110E 116E 11B8; # (춥; 춥; 춥; 춥; 춥; ) HANGUL SYLLABLE CUB +CDA6;CDA6;110E 116E 11B9;CDA6;110E 116E 11B9; # (춦; 춦; 춦; 춦; 춦; ) HANGUL SYLLABLE CUBS +CDA7;CDA7;110E 116E 11BA;CDA7;110E 116E 11BA; # (춧; 춧; 춧; 춧; 춧; ) HANGUL SYLLABLE CUS +CDA8;CDA8;110E 116E 11BB;CDA8;110E 116E 11BB; # (춨; 춨; 춨; 춨; 춨; ) HANGUL SYLLABLE CUSS +CDA9;CDA9;110E 116E 11BC;CDA9;110E 116E 11BC; # (충; 충; 충; 충; 충; ) HANGUL SYLLABLE CUNG +CDAA;CDAA;110E 116E 11BD;CDAA;110E 116E 11BD; # (춪; 춪; 춪; 춪; 춪; ) HANGUL SYLLABLE CUJ +CDAB;CDAB;110E 116E 11BE;CDAB;110E 116E 11BE; # (춫; 춫; 춫; 춫; 춫; ) HANGUL SYLLABLE CUC +CDAC;CDAC;110E 116E 11BF;CDAC;110E 116E 11BF; # (춬; 춬; 춬; 춬; 춬; ) HANGUL SYLLABLE CUK +CDAD;CDAD;110E 116E 11C0;CDAD;110E 116E 11C0; # (춭; 춭; 춭; 춭; 춭; ) HANGUL SYLLABLE CUT +CDAE;CDAE;110E 116E 11C1;CDAE;110E 116E 11C1; # (춮; 춮; 춮; 춮; 춮; ) HANGUL SYLLABLE CUP +CDAF;CDAF;110E 116E 11C2;CDAF;110E 116E 11C2; # (춯; 춯; 춯; 춯; 춯; ) HANGUL SYLLABLE CUH +CDB0;CDB0;110E 116F;CDB0;110E 116F; # (춰; 춰; 춰; 춰; 춰; ) HANGUL SYLLABLE CWEO +CDB1;CDB1;110E 116F 11A8;CDB1;110E 116F 11A8; # (춱; 춱; 춱; 춱; 춱; ) HANGUL SYLLABLE CWEOG +CDB2;CDB2;110E 116F 11A9;CDB2;110E 116F 11A9; # (춲; 춲; 춲; 춲; 춲; ) HANGUL SYLLABLE CWEOGG +CDB3;CDB3;110E 116F 11AA;CDB3;110E 116F 11AA; # (춳; 춳; 춳; 춳; 춳; ) HANGUL SYLLABLE CWEOGS +CDB4;CDB4;110E 116F 11AB;CDB4;110E 116F 11AB; # (춴; 춴; 춴; 춴; 춴; ) HANGUL SYLLABLE CWEON +CDB5;CDB5;110E 116F 11AC;CDB5;110E 116F 11AC; # (춵; 춵; 춵; 춵; 춵; ) HANGUL SYLLABLE CWEONJ +CDB6;CDB6;110E 116F 11AD;CDB6;110E 116F 11AD; # (춶; 춶; 춶; 춶; 춶; ) HANGUL SYLLABLE CWEONH +CDB7;CDB7;110E 116F 11AE;CDB7;110E 116F 11AE; # (춷; 춷; 춷; 춷; 춷; ) HANGUL SYLLABLE CWEOD +CDB8;CDB8;110E 116F 11AF;CDB8;110E 116F 11AF; # (춸; 춸; 춸; 춸; 춸; ) HANGUL SYLLABLE CWEOL +CDB9;CDB9;110E 116F 11B0;CDB9;110E 116F 11B0; # (춹; 춹; 춹; 춹; 춹; ) HANGUL SYLLABLE CWEOLG +CDBA;CDBA;110E 116F 11B1;CDBA;110E 116F 11B1; # (춺; 춺; 춺; 춺; 춺; ) HANGUL SYLLABLE CWEOLM +CDBB;CDBB;110E 116F 11B2;CDBB;110E 116F 11B2; # (춻; 춻; 춻; 춻; 춻; ) HANGUL SYLLABLE CWEOLB +CDBC;CDBC;110E 116F 11B3;CDBC;110E 116F 11B3; # (춼; 춼; 춼; 춼; 춼; ) HANGUL SYLLABLE CWEOLS +CDBD;CDBD;110E 116F 11B4;CDBD;110E 116F 11B4; # (춽; 춽; 춽; 춽; 춽; ) HANGUL SYLLABLE CWEOLT +CDBE;CDBE;110E 116F 11B5;CDBE;110E 116F 11B5; # (춾; 춾; 춾; 춾; 춾; ) HANGUL SYLLABLE CWEOLP +CDBF;CDBF;110E 116F 11B6;CDBF;110E 116F 11B6; # (춿; 춿; 춿; 춿; 춿; ) HANGUL SYLLABLE CWEOLH +CDC0;CDC0;110E 116F 11B7;CDC0;110E 116F 11B7; # (췀; 췀; 췀; 췀; 췀; ) HANGUL SYLLABLE CWEOM +CDC1;CDC1;110E 116F 11B8;CDC1;110E 116F 11B8; # (췁; 췁; 췁; 췁; 췁; ) HANGUL SYLLABLE CWEOB +CDC2;CDC2;110E 116F 11B9;CDC2;110E 116F 11B9; # (췂; 췂; 췂; 췂; 췂; ) HANGUL SYLLABLE CWEOBS +CDC3;CDC3;110E 116F 11BA;CDC3;110E 116F 11BA; # (췃; 췃; 췃; 췃; 췃; ) HANGUL SYLLABLE CWEOS +CDC4;CDC4;110E 116F 11BB;CDC4;110E 116F 11BB; # (췄; 췄; 췄; 췄; 췄; ) HANGUL SYLLABLE CWEOSS +CDC5;CDC5;110E 116F 11BC;CDC5;110E 116F 11BC; # (췅; 췅; 췅; 췅; 췅; ) HANGUL SYLLABLE CWEONG +CDC6;CDC6;110E 116F 11BD;CDC6;110E 116F 11BD; # (췆; 췆; 췆; 췆; 췆; ) HANGUL SYLLABLE CWEOJ +CDC7;CDC7;110E 116F 11BE;CDC7;110E 116F 11BE; # (췇; 췇; 췇; 췇; 췇; ) HANGUL SYLLABLE CWEOC +CDC8;CDC8;110E 116F 11BF;CDC8;110E 116F 11BF; # (췈; 췈; 췈; 췈; 췈; ) HANGUL SYLLABLE CWEOK +CDC9;CDC9;110E 116F 11C0;CDC9;110E 116F 11C0; # (췉; 췉; 췉; 췉; 췉; ) HANGUL SYLLABLE CWEOT +CDCA;CDCA;110E 116F 11C1;CDCA;110E 116F 11C1; # (췊; 췊; 췊; 췊; 췊; ) HANGUL SYLLABLE CWEOP +CDCB;CDCB;110E 116F 11C2;CDCB;110E 116F 11C2; # (췋; 췋; 췋; 췋; 췋; ) HANGUL SYLLABLE CWEOH +CDCC;CDCC;110E 1170;CDCC;110E 1170; # (췌; 췌; 췌; 췌; 췌; ) HANGUL SYLLABLE CWE +CDCD;CDCD;110E 1170 11A8;CDCD;110E 1170 11A8; # (췍; 췍; 췍; 췍; 췍; ) HANGUL SYLLABLE CWEG +CDCE;CDCE;110E 1170 11A9;CDCE;110E 1170 11A9; # (췎; 췎; 췎; 췎; 췎; ) HANGUL SYLLABLE CWEGG +CDCF;CDCF;110E 1170 11AA;CDCF;110E 1170 11AA; # (췏; 췏; 췏; 췏; 췏; ) HANGUL SYLLABLE CWEGS +CDD0;CDD0;110E 1170 11AB;CDD0;110E 1170 11AB; # (췐; 췐; 췐; 췐; 췐; ) HANGUL SYLLABLE CWEN +CDD1;CDD1;110E 1170 11AC;CDD1;110E 1170 11AC; # (췑; 췑; 췑; 췑; 췑; ) HANGUL SYLLABLE CWENJ +CDD2;CDD2;110E 1170 11AD;CDD2;110E 1170 11AD; # (췒; 췒; 췒; 췒; 췒; ) HANGUL SYLLABLE CWENH +CDD3;CDD3;110E 1170 11AE;CDD3;110E 1170 11AE; # (췓; 췓; 췓; 췓; 췓; ) HANGUL SYLLABLE CWED +CDD4;CDD4;110E 1170 11AF;CDD4;110E 1170 11AF; # (췔; 췔; 췔; 췔; 췔; ) HANGUL SYLLABLE CWEL +CDD5;CDD5;110E 1170 11B0;CDD5;110E 1170 11B0; # (췕; 췕; 췕; 췕; 췕; ) HANGUL SYLLABLE CWELG +CDD6;CDD6;110E 1170 11B1;CDD6;110E 1170 11B1; # (췖; 췖; 췖; 췖; 췖; ) HANGUL SYLLABLE CWELM +CDD7;CDD7;110E 1170 11B2;CDD7;110E 1170 11B2; # (췗; 췗; 췗; 췗; 췗; ) HANGUL SYLLABLE CWELB +CDD8;CDD8;110E 1170 11B3;CDD8;110E 1170 11B3; # (췘; 췘; 췘; 췘; 췘; ) HANGUL SYLLABLE CWELS +CDD9;CDD9;110E 1170 11B4;CDD9;110E 1170 11B4; # (췙; 췙; 췙; 췙; 췙; ) HANGUL SYLLABLE CWELT +CDDA;CDDA;110E 1170 11B5;CDDA;110E 1170 11B5; # (췚; 췚; 췚; 췚; 췚; ) HANGUL SYLLABLE CWELP +CDDB;CDDB;110E 1170 11B6;CDDB;110E 1170 11B6; # (췛; 췛; 췛; 췛; 췛; ) HANGUL SYLLABLE CWELH +CDDC;CDDC;110E 1170 11B7;CDDC;110E 1170 11B7; # (췜; 췜; 췜; 췜; 췜; ) HANGUL SYLLABLE CWEM +CDDD;CDDD;110E 1170 11B8;CDDD;110E 1170 11B8; # (췝; 췝; 췝; 췝; 췝; ) HANGUL SYLLABLE CWEB +CDDE;CDDE;110E 1170 11B9;CDDE;110E 1170 11B9; # (췞; 췞; 췞; 췞; 췞; ) HANGUL SYLLABLE CWEBS +CDDF;CDDF;110E 1170 11BA;CDDF;110E 1170 11BA; # (췟; 췟; 췟; 췟; 췟; ) HANGUL SYLLABLE CWES +CDE0;CDE0;110E 1170 11BB;CDE0;110E 1170 11BB; # (췠; 췠; 췠; 췠; 췠; ) HANGUL SYLLABLE CWESS +CDE1;CDE1;110E 1170 11BC;CDE1;110E 1170 11BC; # (췡; 췡; 췡; 췡; 췡; ) HANGUL SYLLABLE CWENG +CDE2;CDE2;110E 1170 11BD;CDE2;110E 1170 11BD; # (췢; 췢; 췢; 췢; 췢; ) HANGUL SYLLABLE CWEJ +CDE3;CDE3;110E 1170 11BE;CDE3;110E 1170 11BE; # (췣; 췣; 췣; 췣; 췣; ) HANGUL SYLLABLE CWEC +CDE4;CDE4;110E 1170 11BF;CDE4;110E 1170 11BF; # (췤; 췤; 췤; 췤; 췤; ) HANGUL SYLLABLE CWEK +CDE5;CDE5;110E 1170 11C0;CDE5;110E 1170 11C0; # (췥; 췥; 췥; 췥; 췥; ) HANGUL SYLLABLE CWET +CDE6;CDE6;110E 1170 11C1;CDE6;110E 1170 11C1; # (췦; 췦; 췦; 췦; 췦; ) HANGUL SYLLABLE CWEP +CDE7;CDE7;110E 1170 11C2;CDE7;110E 1170 11C2; # (췧; 췧; 췧; 췧; 췧; ) HANGUL SYLLABLE CWEH +CDE8;CDE8;110E 1171;CDE8;110E 1171; # (취; 취; 취; 취; 취; ) HANGUL SYLLABLE CWI +CDE9;CDE9;110E 1171 11A8;CDE9;110E 1171 11A8; # (췩; 췩; 췩; 췩; 췩; ) HANGUL SYLLABLE CWIG +CDEA;CDEA;110E 1171 11A9;CDEA;110E 1171 11A9; # (췪; 췪; 췪; 췪; 췪; ) HANGUL SYLLABLE CWIGG +CDEB;CDEB;110E 1171 11AA;CDEB;110E 1171 11AA; # (췫; 췫; 췫; 췫; 췫; ) HANGUL SYLLABLE CWIGS +CDEC;CDEC;110E 1171 11AB;CDEC;110E 1171 11AB; # (췬; 췬; 췬; 췬; 췬; ) HANGUL SYLLABLE CWIN +CDED;CDED;110E 1171 11AC;CDED;110E 1171 11AC; # (췭; 췭; 췭; 췭; 췭; ) HANGUL SYLLABLE CWINJ +CDEE;CDEE;110E 1171 11AD;CDEE;110E 1171 11AD; # (췮; 췮; 췮; 췮; 췮; ) HANGUL SYLLABLE CWINH +CDEF;CDEF;110E 1171 11AE;CDEF;110E 1171 11AE; # (췯; 췯; 췯; 췯; 췯; ) HANGUL SYLLABLE CWID +CDF0;CDF0;110E 1171 11AF;CDF0;110E 1171 11AF; # (췰; 췰; 췰; 췰; 췰; ) HANGUL SYLLABLE CWIL +CDF1;CDF1;110E 1171 11B0;CDF1;110E 1171 11B0; # (췱; 췱; 췱; 췱; 췱; ) HANGUL SYLLABLE CWILG +CDF2;CDF2;110E 1171 11B1;CDF2;110E 1171 11B1; # (췲; 췲; 췲; 췲; 췲; ) HANGUL SYLLABLE CWILM +CDF3;CDF3;110E 1171 11B2;CDF3;110E 1171 11B2; # (췳; 췳; 췳; 췳; 췳; ) HANGUL SYLLABLE CWILB +CDF4;CDF4;110E 1171 11B3;CDF4;110E 1171 11B3; # (췴; 췴; 췴; 췴; 췴; ) HANGUL SYLLABLE CWILS +CDF5;CDF5;110E 1171 11B4;CDF5;110E 1171 11B4; # (췵; 췵; 췵; 췵; 췵; ) HANGUL SYLLABLE CWILT +CDF6;CDF6;110E 1171 11B5;CDF6;110E 1171 11B5; # (췶; 췶; 췶; 췶; 췶; ) HANGUL SYLLABLE CWILP +CDF7;CDF7;110E 1171 11B6;CDF7;110E 1171 11B6; # (췷; 췷; 췷; 췷; 췷; ) HANGUL SYLLABLE CWILH +CDF8;CDF8;110E 1171 11B7;CDF8;110E 1171 11B7; # (췸; 췸; 췸; 췸; 췸; ) HANGUL SYLLABLE CWIM +CDF9;CDF9;110E 1171 11B8;CDF9;110E 1171 11B8; # (췹; 췹; 췹; 췹; 췹; ) HANGUL SYLLABLE CWIB +CDFA;CDFA;110E 1171 11B9;CDFA;110E 1171 11B9; # (췺; 췺; 췺; 췺; 췺; ) HANGUL SYLLABLE CWIBS +CDFB;CDFB;110E 1171 11BA;CDFB;110E 1171 11BA; # (췻; 췻; 췻; 췻; 췻; ) HANGUL SYLLABLE CWIS +CDFC;CDFC;110E 1171 11BB;CDFC;110E 1171 11BB; # (췼; 췼; 췼; 췼; 췼; ) HANGUL SYLLABLE CWISS +CDFD;CDFD;110E 1171 11BC;CDFD;110E 1171 11BC; # (췽; 췽; 췽; 췽; 췽; ) HANGUL SYLLABLE CWING +CDFE;CDFE;110E 1171 11BD;CDFE;110E 1171 11BD; # (췾; 췾; 췾; 췾; 췾; ) HANGUL SYLLABLE CWIJ +CDFF;CDFF;110E 1171 11BE;CDFF;110E 1171 11BE; # (췿; 췿; 췿; 췿; 췿; ) HANGUL SYLLABLE CWIC +CE00;CE00;110E 1171 11BF;CE00;110E 1171 11BF; # (츀; 츀; 츀; 츀; 츀; ) HANGUL SYLLABLE CWIK +CE01;CE01;110E 1171 11C0;CE01;110E 1171 11C0; # (츁; 츁; 츁; 츁; 츁; ) HANGUL SYLLABLE CWIT +CE02;CE02;110E 1171 11C1;CE02;110E 1171 11C1; # (츂; 츂; 츂; 츂; 츂; ) HANGUL SYLLABLE CWIP +CE03;CE03;110E 1171 11C2;CE03;110E 1171 11C2; # (츃; 츃; 츃; 츃; 츃; ) HANGUL SYLLABLE CWIH +CE04;CE04;110E 1172;CE04;110E 1172; # (츄; 츄; 츄; 츄; 츄; ) HANGUL SYLLABLE CYU +CE05;CE05;110E 1172 11A8;CE05;110E 1172 11A8; # (츅; 츅; 츅; 츅; 츅; ) HANGUL SYLLABLE CYUG +CE06;CE06;110E 1172 11A9;CE06;110E 1172 11A9; # (츆; 츆; 츆; 츆; 츆; ) HANGUL SYLLABLE CYUGG +CE07;CE07;110E 1172 11AA;CE07;110E 1172 11AA; # (츇; 츇; 츇; 츇; 츇; ) HANGUL SYLLABLE CYUGS +CE08;CE08;110E 1172 11AB;CE08;110E 1172 11AB; # (츈; 츈; 츈; 츈; 츈; ) HANGUL SYLLABLE CYUN +CE09;CE09;110E 1172 11AC;CE09;110E 1172 11AC; # (츉; 츉; 츉; 츉; 츉; ) HANGUL SYLLABLE CYUNJ +CE0A;CE0A;110E 1172 11AD;CE0A;110E 1172 11AD; # (츊; 츊; 츊; 츊; 츊; ) HANGUL SYLLABLE CYUNH +CE0B;CE0B;110E 1172 11AE;CE0B;110E 1172 11AE; # (츋; 츋; 츋; 츋; 츋; ) HANGUL SYLLABLE CYUD +CE0C;CE0C;110E 1172 11AF;CE0C;110E 1172 11AF; # (츌; 츌; 츌; 츌; 츌; ) HANGUL SYLLABLE CYUL +CE0D;CE0D;110E 1172 11B0;CE0D;110E 1172 11B0; # (츍; 츍; 츍; 츍; 츍; ) HANGUL SYLLABLE CYULG +CE0E;CE0E;110E 1172 11B1;CE0E;110E 1172 11B1; # (츎; 츎; 츎; 츎; 츎; ) HANGUL SYLLABLE CYULM +CE0F;CE0F;110E 1172 11B2;CE0F;110E 1172 11B2; # (츏; 츏; 츏; 츏; 츏; ) HANGUL SYLLABLE CYULB +CE10;CE10;110E 1172 11B3;CE10;110E 1172 11B3; # (츐; 츐; 츐; 츐; 츐; ) HANGUL SYLLABLE CYULS +CE11;CE11;110E 1172 11B4;CE11;110E 1172 11B4; # (츑; 츑; 츑; 츑; 츑; ) HANGUL SYLLABLE CYULT +CE12;CE12;110E 1172 11B5;CE12;110E 1172 11B5; # (츒; 츒; 츒; 츒; 츒; ) HANGUL SYLLABLE CYULP +CE13;CE13;110E 1172 11B6;CE13;110E 1172 11B6; # (츓; 츓; 츓; 츓; 츓; ) HANGUL SYLLABLE CYULH +CE14;CE14;110E 1172 11B7;CE14;110E 1172 11B7; # (츔; 츔; 츔; 츔; 츔; ) HANGUL SYLLABLE CYUM +CE15;CE15;110E 1172 11B8;CE15;110E 1172 11B8; # (츕; 츕; 츕; 츕; 츕; ) HANGUL SYLLABLE CYUB +CE16;CE16;110E 1172 11B9;CE16;110E 1172 11B9; # (츖; 츖; 츖; 츖; 츖; ) HANGUL SYLLABLE CYUBS +CE17;CE17;110E 1172 11BA;CE17;110E 1172 11BA; # (츗; 츗; 츗; 츗; 츗; ) HANGUL SYLLABLE CYUS +CE18;CE18;110E 1172 11BB;CE18;110E 1172 11BB; # (츘; 츘; 츘; 츘; 츘; ) HANGUL SYLLABLE CYUSS +CE19;CE19;110E 1172 11BC;CE19;110E 1172 11BC; # (츙; 츙; 츙; 츙; 츙; ) HANGUL SYLLABLE CYUNG +CE1A;CE1A;110E 1172 11BD;CE1A;110E 1172 11BD; # (츚; 츚; 츚; 츚; 츚; ) HANGUL SYLLABLE CYUJ +CE1B;CE1B;110E 1172 11BE;CE1B;110E 1172 11BE; # (츛; 츛; 츛; 츛; 츛; ) HANGUL SYLLABLE CYUC +CE1C;CE1C;110E 1172 11BF;CE1C;110E 1172 11BF; # (츜; 츜; 츜; 츜; 츜; ) HANGUL SYLLABLE CYUK +CE1D;CE1D;110E 1172 11C0;CE1D;110E 1172 11C0; # (츝; 츝; 츝; 츝; 츝; ) HANGUL SYLLABLE CYUT +CE1E;CE1E;110E 1172 11C1;CE1E;110E 1172 11C1; # (츞; 츞; 츞; 츞; 츞; ) HANGUL SYLLABLE CYUP +CE1F;CE1F;110E 1172 11C2;CE1F;110E 1172 11C2; # (츟; 츟; 츟; 츟; 츟; ) HANGUL SYLLABLE CYUH +CE20;CE20;110E 1173;CE20;110E 1173; # (츠; 츠; 츠; 츠; 츠; ) HANGUL SYLLABLE CEU +CE21;CE21;110E 1173 11A8;CE21;110E 1173 11A8; # (측; 측; 측; 측; 측; ) HANGUL SYLLABLE CEUG +CE22;CE22;110E 1173 11A9;CE22;110E 1173 11A9; # (츢; 츢; 츢; 츢; 츢; ) HANGUL SYLLABLE CEUGG +CE23;CE23;110E 1173 11AA;CE23;110E 1173 11AA; # (츣; 츣; 츣; 츣; 츣; ) HANGUL SYLLABLE CEUGS +CE24;CE24;110E 1173 11AB;CE24;110E 1173 11AB; # (츤; 츤; 츤; 츤; 츤; ) HANGUL SYLLABLE CEUN +CE25;CE25;110E 1173 11AC;CE25;110E 1173 11AC; # (츥; 츥; 츥; 츥; 츥; ) HANGUL SYLLABLE CEUNJ +CE26;CE26;110E 1173 11AD;CE26;110E 1173 11AD; # (츦; 츦; 츦; 츦; 츦; ) HANGUL SYLLABLE CEUNH +CE27;CE27;110E 1173 11AE;CE27;110E 1173 11AE; # (츧; 츧; 츧; 츧; 츧; ) HANGUL SYLLABLE CEUD +CE28;CE28;110E 1173 11AF;CE28;110E 1173 11AF; # (츨; 츨; 츨; 츨; 츨; ) HANGUL SYLLABLE CEUL +CE29;CE29;110E 1173 11B0;CE29;110E 1173 11B0; # (츩; 츩; 츩; 츩; 츩; ) HANGUL SYLLABLE CEULG +CE2A;CE2A;110E 1173 11B1;CE2A;110E 1173 11B1; # (츪; 츪; 츪; 츪; 츪; ) HANGUL SYLLABLE CEULM +CE2B;CE2B;110E 1173 11B2;CE2B;110E 1173 11B2; # (츫; 츫; 츫; 츫; 츫; ) HANGUL SYLLABLE CEULB +CE2C;CE2C;110E 1173 11B3;CE2C;110E 1173 11B3; # (츬; 츬; 츬; 츬; 츬; ) HANGUL SYLLABLE CEULS +CE2D;CE2D;110E 1173 11B4;CE2D;110E 1173 11B4; # (츭; 츭; 츭; 츭; 츭; ) HANGUL SYLLABLE CEULT +CE2E;CE2E;110E 1173 11B5;CE2E;110E 1173 11B5; # (츮; 츮; 츮; 츮; 츮; ) HANGUL SYLLABLE CEULP +CE2F;CE2F;110E 1173 11B6;CE2F;110E 1173 11B6; # (츯; 츯; 츯; 츯; 츯; ) HANGUL SYLLABLE CEULH +CE30;CE30;110E 1173 11B7;CE30;110E 1173 11B7; # (츰; 츰; 츰; 츰; 츰; ) HANGUL SYLLABLE CEUM +CE31;CE31;110E 1173 11B8;CE31;110E 1173 11B8; # (츱; 츱; 츱; 츱; 츱; ) HANGUL SYLLABLE CEUB +CE32;CE32;110E 1173 11B9;CE32;110E 1173 11B9; # (츲; 츲; 츲; 츲; 츲; ) HANGUL SYLLABLE CEUBS +CE33;CE33;110E 1173 11BA;CE33;110E 1173 11BA; # (츳; 츳; 츳; 츳; 츳; ) HANGUL SYLLABLE CEUS +CE34;CE34;110E 1173 11BB;CE34;110E 1173 11BB; # (츴; 츴; 츴; 츴; 츴; ) HANGUL SYLLABLE CEUSS +CE35;CE35;110E 1173 11BC;CE35;110E 1173 11BC; # (층; 층; 층; 층; 층; ) HANGUL SYLLABLE CEUNG +CE36;CE36;110E 1173 11BD;CE36;110E 1173 11BD; # (츶; 츶; 츶; 츶; 츶; ) HANGUL SYLLABLE CEUJ +CE37;CE37;110E 1173 11BE;CE37;110E 1173 11BE; # (츷; 츷; 츷; 츷; 츷; ) HANGUL SYLLABLE CEUC +CE38;CE38;110E 1173 11BF;CE38;110E 1173 11BF; # (츸; 츸; 츸; 츸; 츸; ) HANGUL SYLLABLE CEUK +CE39;CE39;110E 1173 11C0;CE39;110E 1173 11C0; # (츹; 츹; 츹; 츹; 츹; ) HANGUL SYLLABLE CEUT +CE3A;CE3A;110E 1173 11C1;CE3A;110E 1173 11C1; # (츺; 츺; 츺; 츺; 츺; ) HANGUL SYLLABLE CEUP +CE3B;CE3B;110E 1173 11C2;CE3B;110E 1173 11C2; # (츻; 츻; 츻; 츻; 츻; ) HANGUL SYLLABLE CEUH +CE3C;CE3C;110E 1174;CE3C;110E 1174; # (츼; 츼; 츼; 츼; 츼; ) HANGUL SYLLABLE CYI +CE3D;CE3D;110E 1174 11A8;CE3D;110E 1174 11A8; # (츽; 츽; 츽; 츽; 츽; ) HANGUL SYLLABLE CYIG +CE3E;CE3E;110E 1174 11A9;CE3E;110E 1174 11A9; # (츾; 츾; 츾; 츾; 츾; ) HANGUL SYLLABLE CYIGG +CE3F;CE3F;110E 1174 11AA;CE3F;110E 1174 11AA; # (츿; 츿; 츿; 츿; 츿; ) HANGUL SYLLABLE CYIGS +CE40;CE40;110E 1174 11AB;CE40;110E 1174 11AB; # (칀; 칀; 칀; 칀; 칀; ) HANGUL SYLLABLE CYIN +CE41;CE41;110E 1174 11AC;CE41;110E 1174 11AC; # (칁; 칁; 칁; 칁; 칁; ) HANGUL SYLLABLE CYINJ +CE42;CE42;110E 1174 11AD;CE42;110E 1174 11AD; # (칂; 칂; 칂; 칂; 칂; ) HANGUL SYLLABLE CYINH +CE43;CE43;110E 1174 11AE;CE43;110E 1174 11AE; # (칃; 칃; 칃; 칃; 칃; ) HANGUL SYLLABLE CYID +CE44;CE44;110E 1174 11AF;CE44;110E 1174 11AF; # (칄; 칄; 칄; 칄; 칄; ) HANGUL SYLLABLE CYIL +CE45;CE45;110E 1174 11B0;CE45;110E 1174 11B0; # (칅; 칅; 칅; 칅; 칅; ) HANGUL SYLLABLE CYILG +CE46;CE46;110E 1174 11B1;CE46;110E 1174 11B1; # (칆; 칆; 칆; 칆; 칆; ) HANGUL SYLLABLE CYILM +CE47;CE47;110E 1174 11B2;CE47;110E 1174 11B2; # (칇; 칇; 칇; 칇; 칇; ) HANGUL SYLLABLE CYILB +CE48;CE48;110E 1174 11B3;CE48;110E 1174 11B3; # (칈; 칈; 칈; 칈; 칈; ) HANGUL SYLLABLE CYILS +CE49;CE49;110E 1174 11B4;CE49;110E 1174 11B4; # (칉; 칉; 칉; 칉; 칉; ) HANGUL SYLLABLE CYILT +CE4A;CE4A;110E 1174 11B5;CE4A;110E 1174 11B5; # (칊; 칊; 칊; 칊; 칊; ) HANGUL SYLLABLE CYILP +CE4B;CE4B;110E 1174 11B6;CE4B;110E 1174 11B6; # (칋; 칋; 칋; 칋; 칋; ) HANGUL SYLLABLE CYILH +CE4C;CE4C;110E 1174 11B7;CE4C;110E 1174 11B7; # (칌; 칌; 칌; 칌; 칌; ) HANGUL SYLLABLE CYIM +CE4D;CE4D;110E 1174 11B8;CE4D;110E 1174 11B8; # (칍; 칍; 칍; 칍; 칍; ) HANGUL SYLLABLE CYIB +CE4E;CE4E;110E 1174 11B9;CE4E;110E 1174 11B9; # (칎; 칎; 칎; 칎; 칎; ) HANGUL SYLLABLE CYIBS +CE4F;CE4F;110E 1174 11BA;CE4F;110E 1174 11BA; # (칏; 칏; 칏; 칏; 칏; ) HANGUL SYLLABLE CYIS +CE50;CE50;110E 1174 11BB;CE50;110E 1174 11BB; # (칐; 칐; 칐; 칐; 칐; ) HANGUL SYLLABLE CYISS +CE51;CE51;110E 1174 11BC;CE51;110E 1174 11BC; # (칑; 칑; 칑; 칑; 칑; ) HANGUL SYLLABLE CYING +CE52;CE52;110E 1174 11BD;CE52;110E 1174 11BD; # (칒; 칒; 칒; 칒; 칒; ) HANGUL SYLLABLE CYIJ +CE53;CE53;110E 1174 11BE;CE53;110E 1174 11BE; # (칓; 칓; 칓; 칓; 칓; ) HANGUL SYLLABLE CYIC +CE54;CE54;110E 1174 11BF;CE54;110E 1174 11BF; # (칔; 칔; 칔; 칔; 칔; ) HANGUL SYLLABLE CYIK +CE55;CE55;110E 1174 11C0;CE55;110E 1174 11C0; # (칕; 칕; 칕; 칕; 칕; ) HANGUL SYLLABLE CYIT +CE56;CE56;110E 1174 11C1;CE56;110E 1174 11C1; # (칖; 칖; 칖; 칖; 칖; ) HANGUL SYLLABLE CYIP +CE57;CE57;110E 1174 11C2;CE57;110E 1174 11C2; # (칗; 칗; 칗; 칗; 칗; ) HANGUL SYLLABLE CYIH +CE58;CE58;110E 1175;CE58;110E 1175; # (치; 치; 치; 치; 치; ) HANGUL SYLLABLE CI +CE59;CE59;110E 1175 11A8;CE59;110E 1175 11A8; # (칙; 칙; 칙; 칙; 칙; ) HANGUL SYLLABLE CIG +CE5A;CE5A;110E 1175 11A9;CE5A;110E 1175 11A9; # (칚; 칚; 칚; 칚; 칚; ) HANGUL SYLLABLE CIGG +CE5B;CE5B;110E 1175 11AA;CE5B;110E 1175 11AA; # (칛; 칛; 칛; 칛; 칛; ) HANGUL SYLLABLE CIGS +CE5C;CE5C;110E 1175 11AB;CE5C;110E 1175 11AB; # (친; 친; 친; 친; 친; ) HANGUL SYLLABLE CIN +CE5D;CE5D;110E 1175 11AC;CE5D;110E 1175 11AC; # (칝; 칝; 칝; 칝; 칝; ) HANGUL SYLLABLE CINJ +CE5E;CE5E;110E 1175 11AD;CE5E;110E 1175 11AD; # (칞; 칞; 칞; 칞; 칞; ) HANGUL SYLLABLE CINH +CE5F;CE5F;110E 1175 11AE;CE5F;110E 1175 11AE; # (칟; 칟; 칟; 칟; 칟; ) HANGUL SYLLABLE CID +CE60;CE60;110E 1175 11AF;CE60;110E 1175 11AF; # (칠; 칠; 칠; 칠; 칠; ) HANGUL SYLLABLE CIL +CE61;CE61;110E 1175 11B0;CE61;110E 1175 11B0; # (칡; 칡; 칡; 칡; 칡; ) HANGUL SYLLABLE CILG +CE62;CE62;110E 1175 11B1;CE62;110E 1175 11B1; # (칢; 칢; 칢; 칢; 칢; ) HANGUL SYLLABLE CILM +CE63;CE63;110E 1175 11B2;CE63;110E 1175 11B2; # (칣; 칣; 칣; 칣; 칣; ) HANGUL SYLLABLE CILB +CE64;CE64;110E 1175 11B3;CE64;110E 1175 11B3; # (칤; 칤; 칤; 칤; 칤; ) HANGUL SYLLABLE CILS +CE65;CE65;110E 1175 11B4;CE65;110E 1175 11B4; # (칥; 칥; 칥; 칥; 칥; ) HANGUL SYLLABLE CILT +CE66;CE66;110E 1175 11B5;CE66;110E 1175 11B5; # (칦; 칦; 칦; 칦; 칦; ) HANGUL SYLLABLE CILP +CE67;CE67;110E 1175 11B6;CE67;110E 1175 11B6; # (칧; 칧; 칧; 칧; 칧; ) HANGUL SYLLABLE CILH +CE68;CE68;110E 1175 11B7;CE68;110E 1175 11B7; # (침; 침; 침; 침; 침; ) HANGUL SYLLABLE CIM +CE69;CE69;110E 1175 11B8;CE69;110E 1175 11B8; # (칩; 칩; 칩; 칩; 칩; ) HANGUL SYLLABLE CIB +CE6A;CE6A;110E 1175 11B9;CE6A;110E 1175 11B9; # (칪; 칪; 칪; 칪; 칪; ) HANGUL SYLLABLE CIBS +CE6B;CE6B;110E 1175 11BA;CE6B;110E 1175 11BA; # (칫; 칫; 칫; 칫; 칫; ) HANGUL SYLLABLE CIS +CE6C;CE6C;110E 1175 11BB;CE6C;110E 1175 11BB; # (칬; 칬; 칬; 칬; 칬; ) HANGUL SYLLABLE CISS +CE6D;CE6D;110E 1175 11BC;CE6D;110E 1175 11BC; # (칭; 칭; 칭; 칭; 칭; ) HANGUL SYLLABLE CING +CE6E;CE6E;110E 1175 11BD;CE6E;110E 1175 11BD; # (칮; 칮; 칮; 칮; 칮; ) HANGUL SYLLABLE CIJ +CE6F;CE6F;110E 1175 11BE;CE6F;110E 1175 11BE; # (칯; 칯; 칯; 칯; 칯; ) HANGUL SYLLABLE CIC +CE70;CE70;110E 1175 11BF;CE70;110E 1175 11BF; # (칰; 칰; 칰; 칰; 칰; ) HANGUL SYLLABLE CIK +CE71;CE71;110E 1175 11C0;CE71;110E 1175 11C0; # (칱; 칱; 칱; 칱; 칱; ) HANGUL SYLLABLE CIT +CE72;CE72;110E 1175 11C1;CE72;110E 1175 11C1; # (칲; 칲; 칲; 칲; 칲; ) HANGUL SYLLABLE CIP +CE73;CE73;110E 1175 11C2;CE73;110E 1175 11C2; # (칳; 칳; 칳; 칳; 칳; ) HANGUL SYLLABLE CIH +CE74;CE74;110F 1161;CE74;110F 1161; # (카; 카; 카; 카; 카; ) HANGUL SYLLABLE KA +CE75;CE75;110F 1161 11A8;CE75;110F 1161 11A8; # (칵; 칵; 칵; 칵; 칵; ) HANGUL SYLLABLE KAG +CE76;CE76;110F 1161 11A9;CE76;110F 1161 11A9; # (칶; 칶; 칶; 칶; 칶; ) HANGUL SYLLABLE KAGG +CE77;CE77;110F 1161 11AA;CE77;110F 1161 11AA; # (칷; 칷; 칷; 칷; 칷; ) HANGUL SYLLABLE KAGS +CE78;CE78;110F 1161 11AB;CE78;110F 1161 11AB; # (칸; 칸; 칸; 칸; 칸; ) HANGUL SYLLABLE KAN +CE79;CE79;110F 1161 11AC;CE79;110F 1161 11AC; # (칹; 칹; 칹; 칹; 칹; ) HANGUL SYLLABLE KANJ +CE7A;CE7A;110F 1161 11AD;CE7A;110F 1161 11AD; # (칺; 칺; 칺; 칺; 칺; ) HANGUL SYLLABLE KANH +CE7B;CE7B;110F 1161 11AE;CE7B;110F 1161 11AE; # (칻; 칻; 칻; 칻; 칻; ) HANGUL SYLLABLE KAD +CE7C;CE7C;110F 1161 11AF;CE7C;110F 1161 11AF; # (칼; 칼; 칼; 칼; 칼; ) HANGUL SYLLABLE KAL +CE7D;CE7D;110F 1161 11B0;CE7D;110F 1161 11B0; # (칽; 칽; 칽; 칽; 칽; ) HANGUL SYLLABLE KALG +CE7E;CE7E;110F 1161 11B1;CE7E;110F 1161 11B1; # (칾; 칾; 칾; 칾; 칾; ) HANGUL SYLLABLE KALM +CE7F;CE7F;110F 1161 11B2;CE7F;110F 1161 11B2; # (칿; 칿; 칿; 칿; 칿; ) HANGUL SYLLABLE KALB +CE80;CE80;110F 1161 11B3;CE80;110F 1161 11B3; # (캀; 캀; 캀; 캀; 캀; ) HANGUL SYLLABLE KALS +CE81;CE81;110F 1161 11B4;CE81;110F 1161 11B4; # (캁; 캁; 캁; 캁; 캁; ) HANGUL SYLLABLE KALT +CE82;CE82;110F 1161 11B5;CE82;110F 1161 11B5; # (캂; 캂; 캂; 캂; 캂; ) HANGUL SYLLABLE KALP +CE83;CE83;110F 1161 11B6;CE83;110F 1161 11B6; # (캃; 캃; 캃; 캃; 캃; ) HANGUL SYLLABLE KALH +CE84;CE84;110F 1161 11B7;CE84;110F 1161 11B7; # (캄; 캄; 캄; 캄; 캄; ) HANGUL SYLLABLE KAM +CE85;CE85;110F 1161 11B8;CE85;110F 1161 11B8; # (캅; 캅; 캅; 캅; 캅; ) HANGUL SYLLABLE KAB +CE86;CE86;110F 1161 11B9;CE86;110F 1161 11B9; # (캆; 캆; 캆; 캆; 캆; ) HANGUL SYLLABLE KABS +CE87;CE87;110F 1161 11BA;CE87;110F 1161 11BA; # (캇; 캇; 캇; 캇; 캇; ) HANGUL SYLLABLE KAS +CE88;CE88;110F 1161 11BB;CE88;110F 1161 11BB; # (캈; 캈; 캈; 캈; 캈; ) HANGUL SYLLABLE KASS +CE89;CE89;110F 1161 11BC;CE89;110F 1161 11BC; # (캉; 캉; 캉; 캉; 캉; ) HANGUL SYLLABLE KANG +CE8A;CE8A;110F 1161 11BD;CE8A;110F 1161 11BD; # (캊; 캊; 캊; 캊; 캊; ) HANGUL SYLLABLE KAJ +CE8B;CE8B;110F 1161 11BE;CE8B;110F 1161 11BE; # (캋; 캋; 캋; 캋; 캋; ) HANGUL SYLLABLE KAC +CE8C;CE8C;110F 1161 11BF;CE8C;110F 1161 11BF; # (캌; 캌; 캌; 캌; 캌; ) HANGUL SYLLABLE KAK +CE8D;CE8D;110F 1161 11C0;CE8D;110F 1161 11C0; # (캍; 캍; 캍; 캍; 캍; ) HANGUL SYLLABLE KAT +CE8E;CE8E;110F 1161 11C1;CE8E;110F 1161 11C1; # (캎; 캎; 캎; 캎; 캎; ) HANGUL SYLLABLE KAP +CE8F;CE8F;110F 1161 11C2;CE8F;110F 1161 11C2; # (캏; 캏; 캏; 캏; 캏; ) HANGUL SYLLABLE KAH +CE90;CE90;110F 1162;CE90;110F 1162; # (캐; 캐; 캐; 캐; 캐; ) HANGUL SYLLABLE KAE +CE91;CE91;110F 1162 11A8;CE91;110F 1162 11A8; # (캑; 캑; 캑; 캑; 캑; ) HANGUL SYLLABLE KAEG +CE92;CE92;110F 1162 11A9;CE92;110F 1162 11A9; # (캒; 캒; 캒; 캒; 캒; ) HANGUL SYLLABLE KAEGG +CE93;CE93;110F 1162 11AA;CE93;110F 1162 11AA; # (캓; 캓; 캓; 캓; 캓; ) HANGUL SYLLABLE KAEGS +CE94;CE94;110F 1162 11AB;CE94;110F 1162 11AB; # (캔; 캔; 캔; 캔; 캔; ) HANGUL SYLLABLE KAEN +CE95;CE95;110F 1162 11AC;CE95;110F 1162 11AC; # (캕; 캕; 캕; 캕; 캕; ) HANGUL SYLLABLE KAENJ +CE96;CE96;110F 1162 11AD;CE96;110F 1162 11AD; # (캖; 캖; 캖; 캖; 캖; ) HANGUL SYLLABLE KAENH +CE97;CE97;110F 1162 11AE;CE97;110F 1162 11AE; # (캗; 캗; 캗; 캗; 캗; ) HANGUL SYLLABLE KAED +CE98;CE98;110F 1162 11AF;CE98;110F 1162 11AF; # (캘; 캘; 캘; 캘; 캘; ) HANGUL SYLLABLE KAEL +CE99;CE99;110F 1162 11B0;CE99;110F 1162 11B0; # (캙; 캙; 캙; 캙; 캙; ) HANGUL SYLLABLE KAELG +CE9A;CE9A;110F 1162 11B1;CE9A;110F 1162 11B1; # (캚; 캚; 캚; 캚; 캚; ) HANGUL SYLLABLE KAELM +CE9B;CE9B;110F 1162 11B2;CE9B;110F 1162 11B2; # (캛; 캛; 캛; 캛; 캛; ) HANGUL SYLLABLE KAELB +CE9C;CE9C;110F 1162 11B3;CE9C;110F 1162 11B3; # (캜; 캜; 캜; 캜; 캜; ) HANGUL SYLLABLE KAELS +CE9D;CE9D;110F 1162 11B4;CE9D;110F 1162 11B4; # (캝; 캝; 캝; 캝; 캝; ) HANGUL SYLLABLE KAELT +CE9E;CE9E;110F 1162 11B5;CE9E;110F 1162 11B5; # (캞; 캞; 캞; 캞; 캞; ) HANGUL SYLLABLE KAELP +CE9F;CE9F;110F 1162 11B6;CE9F;110F 1162 11B6; # (캟; 캟; 캟; 캟; 캟; ) HANGUL SYLLABLE KAELH +CEA0;CEA0;110F 1162 11B7;CEA0;110F 1162 11B7; # (캠; 캠; 캠; 캠; 캠; ) HANGUL SYLLABLE KAEM +CEA1;CEA1;110F 1162 11B8;CEA1;110F 1162 11B8; # (캡; 캡; 캡; 캡; 캡; ) HANGUL SYLLABLE KAEB +CEA2;CEA2;110F 1162 11B9;CEA2;110F 1162 11B9; # (캢; 캢; 캢; 캢; 캢; ) HANGUL SYLLABLE KAEBS +CEA3;CEA3;110F 1162 11BA;CEA3;110F 1162 11BA; # (캣; 캣; 캣; 캣; 캣; ) HANGUL SYLLABLE KAES +CEA4;CEA4;110F 1162 11BB;CEA4;110F 1162 11BB; # (캤; 캤; 캤; 캤; 캤; ) HANGUL SYLLABLE KAESS +CEA5;CEA5;110F 1162 11BC;CEA5;110F 1162 11BC; # (캥; 캥; 캥; 캥; 캥; ) HANGUL SYLLABLE KAENG +CEA6;CEA6;110F 1162 11BD;CEA6;110F 1162 11BD; # (캦; 캦; 캦; 캦; 캦; ) HANGUL SYLLABLE KAEJ +CEA7;CEA7;110F 1162 11BE;CEA7;110F 1162 11BE; # (캧; 캧; 캧; 캧; 캧; ) HANGUL SYLLABLE KAEC +CEA8;CEA8;110F 1162 11BF;CEA8;110F 1162 11BF; # (캨; 캨; 캨; 캨; 캨; ) HANGUL SYLLABLE KAEK +CEA9;CEA9;110F 1162 11C0;CEA9;110F 1162 11C0; # (캩; 캩; 캩; 캩; 캩; ) HANGUL SYLLABLE KAET +CEAA;CEAA;110F 1162 11C1;CEAA;110F 1162 11C1; # (캪; 캪; 캪; 캪; 캪; ) HANGUL SYLLABLE KAEP +CEAB;CEAB;110F 1162 11C2;CEAB;110F 1162 11C2; # (캫; 캫; 캫; 캫; 캫; ) HANGUL SYLLABLE KAEH +CEAC;CEAC;110F 1163;CEAC;110F 1163; # (캬; 캬; 캬; 캬; 캬; ) HANGUL SYLLABLE KYA +CEAD;CEAD;110F 1163 11A8;CEAD;110F 1163 11A8; # (캭; 캭; 캭; 캭; 캭; ) HANGUL SYLLABLE KYAG +CEAE;CEAE;110F 1163 11A9;CEAE;110F 1163 11A9; # (캮; 캮; 캮; 캮; 캮; ) HANGUL SYLLABLE KYAGG +CEAF;CEAF;110F 1163 11AA;CEAF;110F 1163 11AA; # (캯; 캯; 캯; 캯; 캯; ) HANGUL SYLLABLE KYAGS +CEB0;CEB0;110F 1163 11AB;CEB0;110F 1163 11AB; # (캰; 캰; 캰; 캰; 캰; ) HANGUL SYLLABLE KYAN +CEB1;CEB1;110F 1163 11AC;CEB1;110F 1163 11AC; # (캱; 캱; 캱; 캱; 캱; ) HANGUL SYLLABLE KYANJ +CEB2;CEB2;110F 1163 11AD;CEB2;110F 1163 11AD; # (캲; 캲; 캲; 캲; 캲; ) HANGUL SYLLABLE KYANH +CEB3;CEB3;110F 1163 11AE;CEB3;110F 1163 11AE; # (캳; 캳; 캳; 캳; 캳; ) HANGUL SYLLABLE KYAD +CEB4;CEB4;110F 1163 11AF;CEB4;110F 1163 11AF; # (캴; 캴; 캴; 캴; 캴; ) HANGUL SYLLABLE KYAL +CEB5;CEB5;110F 1163 11B0;CEB5;110F 1163 11B0; # (캵; 캵; 캵; 캵; 캵; ) HANGUL SYLLABLE KYALG +CEB6;CEB6;110F 1163 11B1;CEB6;110F 1163 11B1; # (캶; 캶; 캶; 캶; 캶; ) HANGUL SYLLABLE KYALM +CEB7;CEB7;110F 1163 11B2;CEB7;110F 1163 11B2; # (캷; 캷; 캷; 캷; 캷; ) HANGUL SYLLABLE KYALB +CEB8;CEB8;110F 1163 11B3;CEB8;110F 1163 11B3; # (캸; 캸; 캸; 캸; 캸; ) HANGUL SYLLABLE KYALS +CEB9;CEB9;110F 1163 11B4;CEB9;110F 1163 11B4; # (캹; 캹; 캹; 캹; 캹; ) HANGUL SYLLABLE KYALT +CEBA;CEBA;110F 1163 11B5;CEBA;110F 1163 11B5; # (캺; 캺; 캺; 캺; 캺; ) HANGUL SYLLABLE KYALP +CEBB;CEBB;110F 1163 11B6;CEBB;110F 1163 11B6; # (캻; 캻; 캻; 캻; 캻; ) HANGUL SYLLABLE KYALH +CEBC;CEBC;110F 1163 11B7;CEBC;110F 1163 11B7; # (캼; 캼; 캼; 캼; 캼; ) HANGUL SYLLABLE KYAM +CEBD;CEBD;110F 1163 11B8;CEBD;110F 1163 11B8; # (캽; 캽; 캽; 캽; 캽; ) HANGUL SYLLABLE KYAB +CEBE;CEBE;110F 1163 11B9;CEBE;110F 1163 11B9; # (캾; 캾; 캾; 캾; 캾; ) HANGUL SYLLABLE KYABS +CEBF;CEBF;110F 1163 11BA;CEBF;110F 1163 11BA; # (캿; 캿; 캿; 캿; 캿; ) HANGUL SYLLABLE KYAS +CEC0;CEC0;110F 1163 11BB;CEC0;110F 1163 11BB; # (컀; 컀; 컀; 컀; 컀; ) HANGUL SYLLABLE KYASS +CEC1;CEC1;110F 1163 11BC;CEC1;110F 1163 11BC; # (컁; 컁; 컁; 컁; 컁; ) HANGUL SYLLABLE KYANG +CEC2;CEC2;110F 1163 11BD;CEC2;110F 1163 11BD; # (컂; 컂; 컂; 컂; 컂; ) HANGUL SYLLABLE KYAJ +CEC3;CEC3;110F 1163 11BE;CEC3;110F 1163 11BE; # (컃; 컃; 컃; 컃; 컃; ) HANGUL SYLLABLE KYAC +CEC4;CEC4;110F 1163 11BF;CEC4;110F 1163 11BF; # (컄; 컄; 컄; 컄; 컄; ) HANGUL SYLLABLE KYAK +CEC5;CEC5;110F 1163 11C0;CEC5;110F 1163 11C0; # (컅; 컅; 컅; 컅; 컅; ) HANGUL SYLLABLE KYAT +CEC6;CEC6;110F 1163 11C1;CEC6;110F 1163 11C1; # (컆; 컆; 컆; 컆; 컆; ) HANGUL SYLLABLE KYAP +CEC7;CEC7;110F 1163 11C2;CEC7;110F 1163 11C2; # (컇; 컇; 컇; 컇; 컇; ) HANGUL SYLLABLE KYAH +CEC8;CEC8;110F 1164;CEC8;110F 1164; # (컈; 컈; 컈; 컈; 컈; ) HANGUL SYLLABLE KYAE +CEC9;CEC9;110F 1164 11A8;CEC9;110F 1164 11A8; # (컉; 컉; 컉; 컉; 컉; ) HANGUL SYLLABLE KYAEG +CECA;CECA;110F 1164 11A9;CECA;110F 1164 11A9; # (컊; 컊; 컊; 컊; 컊; ) HANGUL SYLLABLE KYAEGG +CECB;CECB;110F 1164 11AA;CECB;110F 1164 11AA; # (컋; 컋; 컋; 컋; 컋; ) HANGUL SYLLABLE KYAEGS +CECC;CECC;110F 1164 11AB;CECC;110F 1164 11AB; # (컌; 컌; 컌; 컌; 컌; ) HANGUL SYLLABLE KYAEN +CECD;CECD;110F 1164 11AC;CECD;110F 1164 11AC; # (컍; 컍; 컍; 컍; 컍; ) HANGUL SYLLABLE KYAENJ +CECE;CECE;110F 1164 11AD;CECE;110F 1164 11AD; # (컎; 컎; 컎; 컎; 컎; ) HANGUL SYLLABLE KYAENH +CECF;CECF;110F 1164 11AE;CECF;110F 1164 11AE; # (컏; 컏; 컏; 컏; 컏; ) HANGUL SYLLABLE KYAED +CED0;CED0;110F 1164 11AF;CED0;110F 1164 11AF; # (컐; 컐; 컐; 컐; 컐; ) HANGUL SYLLABLE KYAEL +CED1;CED1;110F 1164 11B0;CED1;110F 1164 11B0; # (컑; 컑; 컑; 컑; 컑; ) HANGUL SYLLABLE KYAELG +CED2;CED2;110F 1164 11B1;CED2;110F 1164 11B1; # (컒; 컒; 컒; 컒; 컒; ) HANGUL SYLLABLE KYAELM +CED3;CED3;110F 1164 11B2;CED3;110F 1164 11B2; # (컓; 컓; 컓; 컓; 컓; ) HANGUL SYLLABLE KYAELB +CED4;CED4;110F 1164 11B3;CED4;110F 1164 11B3; # (컔; 컔; 컔; 컔; 컔; ) HANGUL SYLLABLE KYAELS +CED5;CED5;110F 1164 11B4;CED5;110F 1164 11B4; # (컕; 컕; 컕; 컕; 컕; ) HANGUL SYLLABLE KYAELT +CED6;CED6;110F 1164 11B5;CED6;110F 1164 11B5; # (컖; 컖; 컖; 컖; 컖; ) HANGUL SYLLABLE KYAELP +CED7;CED7;110F 1164 11B6;CED7;110F 1164 11B6; # (컗; 컗; 컗; 컗; 컗; ) HANGUL SYLLABLE KYAELH +CED8;CED8;110F 1164 11B7;CED8;110F 1164 11B7; # (컘; 컘; 컘; 컘; 컘; ) HANGUL SYLLABLE KYAEM +CED9;CED9;110F 1164 11B8;CED9;110F 1164 11B8; # (컙; 컙; 컙; 컙; 컙; ) HANGUL SYLLABLE KYAEB +CEDA;CEDA;110F 1164 11B9;CEDA;110F 1164 11B9; # (컚; 컚; 컚; 컚; 컚; ) HANGUL SYLLABLE KYAEBS +CEDB;CEDB;110F 1164 11BA;CEDB;110F 1164 11BA; # (컛; 컛; 컛; 컛; 컛; ) HANGUL SYLLABLE KYAES +CEDC;CEDC;110F 1164 11BB;CEDC;110F 1164 11BB; # (컜; 컜; 컜; 컜; 컜; ) HANGUL SYLLABLE KYAESS +CEDD;CEDD;110F 1164 11BC;CEDD;110F 1164 11BC; # (컝; 컝; 컝; 컝; 컝; ) HANGUL SYLLABLE KYAENG +CEDE;CEDE;110F 1164 11BD;CEDE;110F 1164 11BD; # (컞; 컞; 컞; 컞; 컞; ) HANGUL SYLLABLE KYAEJ +CEDF;CEDF;110F 1164 11BE;CEDF;110F 1164 11BE; # (컟; 컟; 컟; 컟; 컟; ) HANGUL SYLLABLE KYAEC +CEE0;CEE0;110F 1164 11BF;CEE0;110F 1164 11BF; # (컠; 컠; 컠; 컠; 컠; ) HANGUL SYLLABLE KYAEK +CEE1;CEE1;110F 1164 11C0;CEE1;110F 1164 11C0; # (컡; 컡; 컡; 컡; 컡; ) HANGUL SYLLABLE KYAET +CEE2;CEE2;110F 1164 11C1;CEE2;110F 1164 11C1; # (컢; 컢; 컢; 컢; 컢; ) HANGUL SYLLABLE KYAEP +CEE3;CEE3;110F 1164 11C2;CEE3;110F 1164 11C2; # (컣; 컣; 컣; 컣; 컣; ) HANGUL SYLLABLE KYAEH +CEE4;CEE4;110F 1165;CEE4;110F 1165; # (커; 커; 커; 커; 커; ) HANGUL SYLLABLE KEO +CEE5;CEE5;110F 1165 11A8;CEE5;110F 1165 11A8; # (컥; 컥; 컥; 컥; 컥; ) HANGUL SYLLABLE KEOG +CEE6;CEE6;110F 1165 11A9;CEE6;110F 1165 11A9; # (컦; 컦; 컦; 컦; 컦; ) HANGUL SYLLABLE KEOGG +CEE7;CEE7;110F 1165 11AA;CEE7;110F 1165 11AA; # (컧; 컧; 컧; 컧; 컧; ) HANGUL SYLLABLE KEOGS +CEE8;CEE8;110F 1165 11AB;CEE8;110F 1165 11AB; # (컨; 컨; 컨; 컨; 컨; ) HANGUL SYLLABLE KEON +CEE9;CEE9;110F 1165 11AC;CEE9;110F 1165 11AC; # (컩; 컩; 컩; 컩; 컩; ) HANGUL SYLLABLE KEONJ +CEEA;CEEA;110F 1165 11AD;CEEA;110F 1165 11AD; # (컪; 컪; 컪; 컪; 컪; ) HANGUL SYLLABLE KEONH +CEEB;CEEB;110F 1165 11AE;CEEB;110F 1165 11AE; # (컫; 컫; 컫; 컫; 컫; ) HANGUL SYLLABLE KEOD +CEEC;CEEC;110F 1165 11AF;CEEC;110F 1165 11AF; # (컬; 컬; 컬; 컬; 컬; ) HANGUL SYLLABLE KEOL +CEED;CEED;110F 1165 11B0;CEED;110F 1165 11B0; # (컭; 컭; 컭; 컭; 컭; ) HANGUL SYLLABLE KEOLG +CEEE;CEEE;110F 1165 11B1;CEEE;110F 1165 11B1; # (컮; 컮; 컮; 컮; 컮; ) HANGUL SYLLABLE KEOLM +CEEF;CEEF;110F 1165 11B2;CEEF;110F 1165 11B2; # (컯; 컯; 컯; 컯; 컯; ) HANGUL SYLLABLE KEOLB +CEF0;CEF0;110F 1165 11B3;CEF0;110F 1165 11B3; # (컰; 컰; 컰; 컰; 컰; ) HANGUL SYLLABLE KEOLS +CEF1;CEF1;110F 1165 11B4;CEF1;110F 1165 11B4; # (컱; 컱; 컱; 컱; 컱; ) HANGUL SYLLABLE KEOLT +CEF2;CEF2;110F 1165 11B5;CEF2;110F 1165 11B5; # (컲; 컲; 컲; 컲; 컲; ) HANGUL SYLLABLE KEOLP +CEF3;CEF3;110F 1165 11B6;CEF3;110F 1165 11B6; # (컳; 컳; 컳; 컳; 컳; ) HANGUL SYLLABLE KEOLH +CEF4;CEF4;110F 1165 11B7;CEF4;110F 1165 11B7; # (컴; 컴; 컴; 컴; 컴; ) HANGUL SYLLABLE KEOM +CEF5;CEF5;110F 1165 11B8;CEF5;110F 1165 11B8; # (컵; 컵; 컵; 컵; 컵; ) HANGUL SYLLABLE KEOB +CEF6;CEF6;110F 1165 11B9;CEF6;110F 1165 11B9; # (컶; 컶; 컶; 컶; 컶; ) HANGUL SYLLABLE KEOBS +CEF7;CEF7;110F 1165 11BA;CEF7;110F 1165 11BA; # (컷; 컷; 컷; 컷; 컷; ) HANGUL SYLLABLE KEOS +CEF8;CEF8;110F 1165 11BB;CEF8;110F 1165 11BB; # (컸; 컸; 컸; 컸; 컸; ) HANGUL SYLLABLE KEOSS +CEF9;CEF9;110F 1165 11BC;CEF9;110F 1165 11BC; # (컹; 컹; 컹; 컹; 컹; ) HANGUL SYLLABLE KEONG +CEFA;CEFA;110F 1165 11BD;CEFA;110F 1165 11BD; # (컺; 컺; 컺; 컺; 컺; ) HANGUL SYLLABLE KEOJ +CEFB;CEFB;110F 1165 11BE;CEFB;110F 1165 11BE; # (컻; 컻; 컻; 컻; 컻; ) HANGUL SYLLABLE KEOC +CEFC;CEFC;110F 1165 11BF;CEFC;110F 1165 11BF; # (컼; 컼; 컼; 컼; 컼; ) HANGUL SYLLABLE KEOK +CEFD;CEFD;110F 1165 11C0;CEFD;110F 1165 11C0; # (컽; 컽; 컽; 컽; 컽; ) HANGUL SYLLABLE KEOT +CEFE;CEFE;110F 1165 11C1;CEFE;110F 1165 11C1; # (컾; 컾; 컾; 컾; 컾; ) HANGUL SYLLABLE KEOP +CEFF;CEFF;110F 1165 11C2;CEFF;110F 1165 11C2; # (컿; 컿; 컿; 컿; 컿; ) HANGUL SYLLABLE KEOH +CF00;CF00;110F 1166;CF00;110F 1166; # (케; 케; 케; 케; 케; ) HANGUL SYLLABLE KE +CF01;CF01;110F 1166 11A8;CF01;110F 1166 11A8; # (켁; 켁; 켁; 켁; 켁; ) HANGUL SYLLABLE KEG +CF02;CF02;110F 1166 11A9;CF02;110F 1166 11A9; # (켂; 켂; 켂; 켂; 켂; ) HANGUL SYLLABLE KEGG +CF03;CF03;110F 1166 11AA;CF03;110F 1166 11AA; # (켃; 켃; 켃; 켃; 켃; ) HANGUL SYLLABLE KEGS +CF04;CF04;110F 1166 11AB;CF04;110F 1166 11AB; # (켄; 켄; 켄; 켄; 켄; ) HANGUL SYLLABLE KEN +CF05;CF05;110F 1166 11AC;CF05;110F 1166 11AC; # (켅; 켅; 켅; 켅; 켅; ) HANGUL SYLLABLE KENJ +CF06;CF06;110F 1166 11AD;CF06;110F 1166 11AD; # (켆; 켆; 켆; 켆; 켆; ) HANGUL SYLLABLE KENH +CF07;CF07;110F 1166 11AE;CF07;110F 1166 11AE; # (켇; 켇; 켇; 켇; 켇; ) HANGUL SYLLABLE KED +CF08;CF08;110F 1166 11AF;CF08;110F 1166 11AF; # (켈; 켈; 켈; 켈; 켈; ) HANGUL SYLLABLE KEL +CF09;CF09;110F 1166 11B0;CF09;110F 1166 11B0; # (켉; 켉; 켉; 켉; 켉; ) HANGUL SYLLABLE KELG +CF0A;CF0A;110F 1166 11B1;CF0A;110F 1166 11B1; # (켊; 켊; 켊; 켊; 켊; ) HANGUL SYLLABLE KELM +CF0B;CF0B;110F 1166 11B2;CF0B;110F 1166 11B2; # (켋; 켋; 켋; 켋; 켋; ) HANGUL SYLLABLE KELB +CF0C;CF0C;110F 1166 11B3;CF0C;110F 1166 11B3; # (켌; 켌; 켌; 켌; 켌; ) HANGUL SYLLABLE KELS +CF0D;CF0D;110F 1166 11B4;CF0D;110F 1166 11B4; # (켍; 켍; 켍; 켍; 켍; ) HANGUL SYLLABLE KELT +CF0E;CF0E;110F 1166 11B5;CF0E;110F 1166 11B5; # (켎; 켎; 켎; 켎; 켎; ) HANGUL SYLLABLE KELP +CF0F;CF0F;110F 1166 11B6;CF0F;110F 1166 11B6; # (켏; 켏; 켏; 켏; 켏; ) HANGUL SYLLABLE KELH +CF10;CF10;110F 1166 11B7;CF10;110F 1166 11B7; # (켐; 켐; 켐; 켐; 켐; ) HANGUL SYLLABLE KEM +CF11;CF11;110F 1166 11B8;CF11;110F 1166 11B8; # (켑; 켑; 켑; 켑; 켑; ) HANGUL SYLLABLE KEB +CF12;CF12;110F 1166 11B9;CF12;110F 1166 11B9; # (켒; 켒; 켒; 켒; 켒; ) HANGUL SYLLABLE KEBS +CF13;CF13;110F 1166 11BA;CF13;110F 1166 11BA; # (켓; 켓; 켓; 켓; 켓; ) HANGUL SYLLABLE KES +CF14;CF14;110F 1166 11BB;CF14;110F 1166 11BB; # (켔; 켔; 켔; 켔; 켔; ) HANGUL SYLLABLE KESS +CF15;CF15;110F 1166 11BC;CF15;110F 1166 11BC; # (켕; 켕; 켕; 켕; 켕; ) HANGUL SYLLABLE KENG +CF16;CF16;110F 1166 11BD;CF16;110F 1166 11BD; # (켖; 켖; 켖; 켖; 켖; ) HANGUL SYLLABLE KEJ +CF17;CF17;110F 1166 11BE;CF17;110F 1166 11BE; # (켗; 켗; 켗; 켗; 켗; ) HANGUL SYLLABLE KEC +CF18;CF18;110F 1166 11BF;CF18;110F 1166 11BF; # (켘; 켘; 켘; 켘; 켘; ) HANGUL SYLLABLE KEK +CF19;CF19;110F 1166 11C0;CF19;110F 1166 11C0; # (켙; 켙; 켙; 켙; 켙; ) HANGUL SYLLABLE KET +CF1A;CF1A;110F 1166 11C1;CF1A;110F 1166 11C1; # (켚; 켚; 켚; 켚; 켚; ) HANGUL SYLLABLE KEP +CF1B;CF1B;110F 1166 11C2;CF1B;110F 1166 11C2; # (켛; 켛; 켛; 켛; 켛; ) HANGUL SYLLABLE KEH +CF1C;CF1C;110F 1167;CF1C;110F 1167; # (켜; 켜; 켜; 켜; 켜; ) HANGUL SYLLABLE KYEO +CF1D;CF1D;110F 1167 11A8;CF1D;110F 1167 11A8; # (켝; 켝; 켝; 켝; 켝; ) HANGUL SYLLABLE KYEOG +CF1E;CF1E;110F 1167 11A9;CF1E;110F 1167 11A9; # (켞; 켞; 켞; 켞; 켞; ) HANGUL SYLLABLE KYEOGG +CF1F;CF1F;110F 1167 11AA;CF1F;110F 1167 11AA; # (켟; 켟; 켟; 켟; 켟; ) HANGUL SYLLABLE KYEOGS +CF20;CF20;110F 1167 11AB;CF20;110F 1167 11AB; # (켠; 켠; 켠; 켠; 켠; ) HANGUL SYLLABLE KYEON +CF21;CF21;110F 1167 11AC;CF21;110F 1167 11AC; # (켡; 켡; 켡; 켡; 켡; ) HANGUL SYLLABLE KYEONJ +CF22;CF22;110F 1167 11AD;CF22;110F 1167 11AD; # (켢; 켢; 켢; 켢; 켢; ) HANGUL SYLLABLE KYEONH +CF23;CF23;110F 1167 11AE;CF23;110F 1167 11AE; # (켣; 켣; 켣; 켣; 켣; ) HANGUL SYLLABLE KYEOD +CF24;CF24;110F 1167 11AF;CF24;110F 1167 11AF; # (켤; 켤; 켤; 켤; 켤; ) HANGUL SYLLABLE KYEOL +CF25;CF25;110F 1167 11B0;CF25;110F 1167 11B0; # (켥; 켥; 켥; 켥; 켥; ) HANGUL SYLLABLE KYEOLG +CF26;CF26;110F 1167 11B1;CF26;110F 1167 11B1; # (켦; 켦; 켦; 켦; 켦; ) HANGUL SYLLABLE KYEOLM +CF27;CF27;110F 1167 11B2;CF27;110F 1167 11B2; # (켧; 켧; 켧; 켧; 켧; ) HANGUL SYLLABLE KYEOLB +CF28;CF28;110F 1167 11B3;CF28;110F 1167 11B3; # (켨; 켨; 켨; 켨; 켨; ) HANGUL SYLLABLE KYEOLS +CF29;CF29;110F 1167 11B4;CF29;110F 1167 11B4; # (켩; 켩; 켩; 켩; 켩; ) HANGUL SYLLABLE KYEOLT +CF2A;CF2A;110F 1167 11B5;CF2A;110F 1167 11B5; # (켪; 켪; 켪; 켪; 켪; ) HANGUL SYLLABLE KYEOLP +CF2B;CF2B;110F 1167 11B6;CF2B;110F 1167 11B6; # (켫; 켫; 켫; 켫; 켫; ) HANGUL SYLLABLE KYEOLH +CF2C;CF2C;110F 1167 11B7;CF2C;110F 1167 11B7; # (켬; 켬; 켬; 켬; 켬; ) HANGUL SYLLABLE KYEOM +CF2D;CF2D;110F 1167 11B8;CF2D;110F 1167 11B8; # (켭; 켭; 켭; 켭; 켭; ) HANGUL SYLLABLE KYEOB +CF2E;CF2E;110F 1167 11B9;CF2E;110F 1167 11B9; # (켮; 켮; 켮; 켮; 켮; ) HANGUL SYLLABLE KYEOBS +CF2F;CF2F;110F 1167 11BA;CF2F;110F 1167 11BA; # (켯; 켯; 켯; 켯; 켯; ) HANGUL SYLLABLE KYEOS +CF30;CF30;110F 1167 11BB;CF30;110F 1167 11BB; # (켰; 켰; 켰; 켰; 켰; ) HANGUL SYLLABLE KYEOSS +CF31;CF31;110F 1167 11BC;CF31;110F 1167 11BC; # (켱; 켱; 켱; 켱; 켱; ) HANGUL SYLLABLE KYEONG +CF32;CF32;110F 1167 11BD;CF32;110F 1167 11BD; # (켲; 켲; 켲; 켲; 켲; ) HANGUL SYLLABLE KYEOJ +CF33;CF33;110F 1167 11BE;CF33;110F 1167 11BE; # (켳; 켳; 켳; 켳; 켳; ) HANGUL SYLLABLE KYEOC +CF34;CF34;110F 1167 11BF;CF34;110F 1167 11BF; # (켴; 켴; 켴; 켴; 켴; ) HANGUL SYLLABLE KYEOK +CF35;CF35;110F 1167 11C0;CF35;110F 1167 11C0; # (켵; 켵; 켵; 켵; 켵; ) HANGUL SYLLABLE KYEOT +CF36;CF36;110F 1167 11C1;CF36;110F 1167 11C1; # (켶; 켶; 켶; 켶; 켶; ) HANGUL SYLLABLE KYEOP +CF37;CF37;110F 1167 11C2;CF37;110F 1167 11C2; # (켷; 켷; 켷; 켷; 켷; ) HANGUL SYLLABLE KYEOH +CF38;CF38;110F 1168;CF38;110F 1168; # (켸; 켸; 켸; 켸; 켸; ) HANGUL SYLLABLE KYE +CF39;CF39;110F 1168 11A8;CF39;110F 1168 11A8; # (켹; 켹; 켹; 켹; 켹; ) HANGUL SYLLABLE KYEG +CF3A;CF3A;110F 1168 11A9;CF3A;110F 1168 11A9; # (켺; 켺; 켺; 켺; 켺; ) HANGUL SYLLABLE KYEGG +CF3B;CF3B;110F 1168 11AA;CF3B;110F 1168 11AA; # (켻; 켻; 켻; 켻; 켻; ) HANGUL SYLLABLE KYEGS +CF3C;CF3C;110F 1168 11AB;CF3C;110F 1168 11AB; # (켼; 켼; 켼; 켼; 켼; ) HANGUL SYLLABLE KYEN +CF3D;CF3D;110F 1168 11AC;CF3D;110F 1168 11AC; # (켽; 켽; 켽; 켽; 켽; ) HANGUL SYLLABLE KYENJ +CF3E;CF3E;110F 1168 11AD;CF3E;110F 1168 11AD; # (켾; 켾; 켾; 켾; 켾; ) HANGUL SYLLABLE KYENH +CF3F;CF3F;110F 1168 11AE;CF3F;110F 1168 11AE; # (켿; 켿; 켿; 켿; 켿; ) HANGUL SYLLABLE KYED +CF40;CF40;110F 1168 11AF;CF40;110F 1168 11AF; # (콀; 콀; 콀; 콀; 콀; ) HANGUL SYLLABLE KYEL +CF41;CF41;110F 1168 11B0;CF41;110F 1168 11B0; # (콁; 콁; 콁; 콁; 콁; ) HANGUL SYLLABLE KYELG +CF42;CF42;110F 1168 11B1;CF42;110F 1168 11B1; # (콂; 콂; 콂; 콂; 콂; ) HANGUL SYLLABLE KYELM +CF43;CF43;110F 1168 11B2;CF43;110F 1168 11B2; # (콃; 콃; 콃; 콃; 콃; ) HANGUL SYLLABLE KYELB +CF44;CF44;110F 1168 11B3;CF44;110F 1168 11B3; # (콄; 콄; 콄; 콄; 콄; ) HANGUL SYLLABLE KYELS +CF45;CF45;110F 1168 11B4;CF45;110F 1168 11B4; # (콅; 콅; 콅; 콅; 콅; ) HANGUL SYLLABLE KYELT +CF46;CF46;110F 1168 11B5;CF46;110F 1168 11B5; # (콆; 콆; 콆; 콆; 콆; ) HANGUL SYLLABLE KYELP +CF47;CF47;110F 1168 11B6;CF47;110F 1168 11B6; # (콇; 콇; 콇; 콇; 콇; ) HANGUL SYLLABLE KYELH +CF48;CF48;110F 1168 11B7;CF48;110F 1168 11B7; # (콈; 콈; 콈; 콈; 콈; ) HANGUL SYLLABLE KYEM +CF49;CF49;110F 1168 11B8;CF49;110F 1168 11B8; # (콉; 콉; 콉; 콉; 콉; ) HANGUL SYLLABLE KYEB +CF4A;CF4A;110F 1168 11B9;CF4A;110F 1168 11B9; # (콊; 콊; 콊; 콊; 콊; ) HANGUL SYLLABLE KYEBS +CF4B;CF4B;110F 1168 11BA;CF4B;110F 1168 11BA; # (콋; 콋; 콋; 콋; 콋; ) HANGUL SYLLABLE KYES +CF4C;CF4C;110F 1168 11BB;CF4C;110F 1168 11BB; # (콌; 콌; 콌; 콌; 콌; ) HANGUL SYLLABLE KYESS +CF4D;CF4D;110F 1168 11BC;CF4D;110F 1168 11BC; # (콍; 콍; 콍; 콍; 콍; ) HANGUL SYLLABLE KYENG +CF4E;CF4E;110F 1168 11BD;CF4E;110F 1168 11BD; # (콎; 콎; 콎; 콎; 콎; ) HANGUL SYLLABLE KYEJ +CF4F;CF4F;110F 1168 11BE;CF4F;110F 1168 11BE; # (콏; 콏; 콏; 콏; 콏; ) HANGUL SYLLABLE KYEC +CF50;CF50;110F 1168 11BF;CF50;110F 1168 11BF; # (콐; 콐; 콐; 콐; 콐; ) HANGUL SYLLABLE KYEK +CF51;CF51;110F 1168 11C0;CF51;110F 1168 11C0; # (콑; 콑; 콑; 콑; 콑; ) HANGUL SYLLABLE KYET +CF52;CF52;110F 1168 11C1;CF52;110F 1168 11C1; # (콒; 콒; 콒; 콒; 콒; ) HANGUL SYLLABLE KYEP +CF53;CF53;110F 1168 11C2;CF53;110F 1168 11C2; # (콓; 콓; 콓; 콓; 콓; ) HANGUL SYLLABLE KYEH +CF54;CF54;110F 1169;CF54;110F 1169; # (코; 코; 코; 코; 코; ) HANGUL SYLLABLE KO +CF55;CF55;110F 1169 11A8;CF55;110F 1169 11A8; # (콕; 콕; 콕; 콕; 콕; ) HANGUL SYLLABLE KOG +CF56;CF56;110F 1169 11A9;CF56;110F 1169 11A9; # (콖; 콖; 콖; 콖; 콖; ) HANGUL SYLLABLE KOGG +CF57;CF57;110F 1169 11AA;CF57;110F 1169 11AA; # (콗; 콗; 콗; 콗; 콗; ) HANGUL SYLLABLE KOGS +CF58;CF58;110F 1169 11AB;CF58;110F 1169 11AB; # (콘; 콘; 콘; 콘; 콘; ) HANGUL SYLLABLE KON +CF59;CF59;110F 1169 11AC;CF59;110F 1169 11AC; # (콙; 콙; 콙; 콙; 콙; ) HANGUL SYLLABLE KONJ +CF5A;CF5A;110F 1169 11AD;CF5A;110F 1169 11AD; # (콚; 콚; 콚; 콚; 콚; ) HANGUL SYLLABLE KONH +CF5B;CF5B;110F 1169 11AE;CF5B;110F 1169 11AE; # (콛; 콛; 콛; 콛; 콛; ) HANGUL SYLLABLE KOD +CF5C;CF5C;110F 1169 11AF;CF5C;110F 1169 11AF; # (콜; 콜; 콜; 콜; 콜; ) HANGUL SYLLABLE KOL +CF5D;CF5D;110F 1169 11B0;CF5D;110F 1169 11B0; # (콝; 콝; 콝; 콝; 콝; ) HANGUL SYLLABLE KOLG +CF5E;CF5E;110F 1169 11B1;CF5E;110F 1169 11B1; # (콞; 콞; 콞; 콞; 콞; ) HANGUL SYLLABLE KOLM +CF5F;CF5F;110F 1169 11B2;CF5F;110F 1169 11B2; # (콟; 콟; 콟; 콟; 콟; ) HANGUL SYLLABLE KOLB +CF60;CF60;110F 1169 11B3;CF60;110F 1169 11B3; # (콠; 콠; 콠; 콠; 콠; ) HANGUL SYLLABLE KOLS +CF61;CF61;110F 1169 11B4;CF61;110F 1169 11B4; # (콡; 콡; 콡; 콡; 콡; ) HANGUL SYLLABLE KOLT +CF62;CF62;110F 1169 11B5;CF62;110F 1169 11B5; # (콢; 콢; 콢; 콢; 콢; ) HANGUL SYLLABLE KOLP +CF63;CF63;110F 1169 11B6;CF63;110F 1169 11B6; # (콣; 콣; 콣; 콣; 콣; ) HANGUL SYLLABLE KOLH +CF64;CF64;110F 1169 11B7;CF64;110F 1169 11B7; # (콤; 콤; 콤; 콤; 콤; ) HANGUL SYLLABLE KOM +CF65;CF65;110F 1169 11B8;CF65;110F 1169 11B8; # (콥; 콥; 콥; 콥; 콥; ) HANGUL SYLLABLE KOB +CF66;CF66;110F 1169 11B9;CF66;110F 1169 11B9; # (콦; 콦; 콦; 콦; 콦; ) HANGUL SYLLABLE KOBS +CF67;CF67;110F 1169 11BA;CF67;110F 1169 11BA; # (콧; 콧; 콧; 콧; 콧; ) HANGUL SYLLABLE KOS +CF68;CF68;110F 1169 11BB;CF68;110F 1169 11BB; # (콨; 콨; 콨; 콨; 콨; ) HANGUL SYLLABLE KOSS +CF69;CF69;110F 1169 11BC;CF69;110F 1169 11BC; # (콩; 콩; 콩; 콩; 콩; ) HANGUL SYLLABLE KONG +CF6A;CF6A;110F 1169 11BD;CF6A;110F 1169 11BD; # (콪; 콪; 콪; 콪; 콪; ) HANGUL SYLLABLE KOJ +CF6B;CF6B;110F 1169 11BE;CF6B;110F 1169 11BE; # (콫; 콫; 콫; 콫; 콫; ) HANGUL SYLLABLE KOC +CF6C;CF6C;110F 1169 11BF;CF6C;110F 1169 11BF; # (콬; 콬; 콬; 콬; 콬; ) HANGUL SYLLABLE KOK +CF6D;CF6D;110F 1169 11C0;CF6D;110F 1169 11C0; # (콭; 콭; 콭; 콭; 콭; ) HANGUL SYLLABLE KOT +CF6E;CF6E;110F 1169 11C1;CF6E;110F 1169 11C1; # (콮; 콮; 콮; 콮; 콮; ) HANGUL SYLLABLE KOP +CF6F;CF6F;110F 1169 11C2;CF6F;110F 1169 11C2; # (콯; 콯; 콯; 콯; 콯; ) HANGUL SYLLABLE KOH +CF70;CF70;110F 116A;CF70;110F 116A; # (콰; 콰; 콰; 콰; 콰; ) HANGUL SYLLABLE KWA +CF71;CF71;110F 116A 11A8;CF71;110F 116A 11A8; # (콱; 콱; 콱; 콱; 콱; ) HANGUL SYLLABLE KWAG +CF72;CF72;110F 116A 11A9;CF72;110F 116A 11A9; # (콲; 콲; 콲; 콲; 콲; ) HANGUL SYLLABLE KWAGG +CF73;CF73;110F 116A 11AA;CF73;110F 116A 11AA; # (콳; 콳; 콳; 콳; 콳; ) HANGUL SYLLABLE KWAGS +CF74;CF74;110F 116A 11AB;CF74;110F 116A 11AB; # (콴; 콴; 콴; 콴; 콴; ) HANGUL SYLLABLE KWAN +CF75;CF75;110F 116A 11AC;CF75;110F 116A 11AC; # (콵; 콵; 콵; 콵; 콵; ) HANGUL SYLLABLE KWANJ +CF76;CF76;110F 116A 11AD;CF76;110F 116A 11AD; # (콶; 콶; 콶; 콶; 콶; ) HANGUL SYLLABLE KWANH +CF77;CF77;110F 116A 11AE;CF77;110F 116A 11AE; # (콷; 콷; 콷; 콷; 콷; ) HANGUL SYLLABLE KWAD +CF78;CF78;110F 116A 11AF;CF78;110F 116A 11AF; # (콸; 콸; 콸; 콸; 콸; ) HANGUL SYLLABLE KWAL +CF79;CF79;110F 116A 11B0;CF79;110F 116A 11B0; # (콹; 콹; 콹; 콹; 콹; ) HANGUL SYLLABLE KWALG +CF7A;CF7A;110F 116A 11B1;CF7A;110F 116A 11B1; # (콺; 콺; 콺; 콺; 콺; ) HANGUL SYLLABLE KWALM +CF7B;CF7B;110F 116A 11B2;CF7B;110F 116A 11B2; # (콻; 콻; 콻; 콻; 콻; ) HANGUL SYLLABLE KWALB +CF7C;CF7C;110F 116A 11B3;CF7C;110F 116A 11B3; # (콼; 콼; 콼; 콼; 콼; ) HANGUL SYLLABLE KWALS +CF7D;CF7D;110F 116A 11B4;CF7D;110F 116A 11B4; # (콽; 콽; 콽; 콽; 콽; ) HANGUL SYLLABLE KWALT +CF7E;CF7E;110F 116A 11B5;CF7E;110F 116A 11B5; # (콾; 콾; 콾; 콾; 콾; ) HANGUL SYLLABLE KWALP +CF7F;CF7F;110F 116A 11B6;CF7F;110F 116A 11B6; # (콿; 콿; 콿; 콿; 콿; ) HANGUL SYLLABLE KWALH +CF80;CF80;110F 116A 11B7;CF80;110F 116A 11B7; # (쾀; 쾀; 쾀; 쾀; 쾀; ) HANGUL SYLLABLE KWAM +CF81;CF81;110F 116A 11B8;CF81;110F 116A 11B8; # (쾁; 쾁; 쾁; 쾁; 쾁; ) HANGUL SYLLABLE KWAB +CF82;CF82;110F 116A 11B9;CF82;110F 116A 11B9; # (쾂; 쾂; 쾂; 쾂; 쾂; ) HANGUL SYLLABLE KWABS +CF83;CF83;110F 116A 11BA;CF83;110F 116A 11BA; # (쾃; 쾃; 쾃; 쾃; 쾃; ) HANGUL SYLLABLE KWAS +CF84;CF84;110F 116A 11BB;CF84;110F 116A 11BB; # (쾄; 쾄; 쾄; 쾄; 쾄; ) HANGUL SYLLABLE KWASS +CF85;CF85;110F 116A 11BC;CF85;110F 116A 11BC; # (쾅; 쾅; 쾅; 쾅; 쾅; ) HANGUL SYLLABLE KWANG +CF86;CF86;110F 116A 11BD;CF86;110F 116A 11BD; # (쾆; 쾆; 쾆; 쾆; 쾆; ) HANGUL SYLLABLE KWAJ +CF87;CF87;110F 116A 11BE;CF87;110F 116A 11BE; # (쾇; 쾇; 쾇; 쾇; 쾇; ) HANGUL SYLLABLE KWAC +CF88;CF88;110F 116A 11BF;CF88;110F 116A 11BF; # (쾈; 쾈; 쾈; 쾈; 쾈; ) HANGUL SYLLABLE KWAK +CF89;CF89;110F 116A 11C0;CF89;110F 116A 11C0; # (쾉; 쾉; 쾉; 쾉; 쾉; ) HANGUL SYLLABLE KWAT +CF8A;CF8A;110F 116A 11C1;CF8A;110F 116A 11C1; # (쾊; 쾊; 쾊; 쾊; 쾊; ) HANGUL SYLLABLE KWAP +CF8B;CF8B;110F 116A 11C2;CF8B;110F 116A 11C2; # (쾋; 쾋; 쾋; 쾋; 쾋; ) HANGUL SYLLABLE KWAH +CF8C;CF8C;110F 116B;CF8C;110F 116B; # (쾌; 쾌; 쾌; 쾌; 쾌; ) HANGUL SYLLABLE KWAE +CF8D;CF8D;110F 116B 11A8;CF8D;110F 116B 11A8; # (쾍; 쾍; 쾍; 쾍; 쾍; ) HANGUL SYLLABLE KWAEG +CF8E;CF8E;110F 116B 11A9;CF8E;110F 116B 11A9; # (쾎; 쾎; 쾎; 쾎; 쾎; ) HANGUL SYLLABLE KWAEGG +CF8F;CF8F;110F 116B 11AA;CF8F;110F 116B 11AA; # (쾏; 쾏; 쾏; 쾏; 쾏; ) HANGUL SYLLABLE KWAEGS +CF90;CF90;110F 116B 11AB;CF90;110F 116B 11AB; # (쾐; 쾐; 쾐; 쾐; 쾐; ) HANGUL SYLLABLE KWAEN +CF91;CF91;110F 116B 11AC;CF91;110F 116B 11AC; # (쾑; 쾑; 쾑; 쾑; 쾑; ) HANGUL SYLLABLE KWAENJ +CF92;CF92;110F 116B 11AD;CF92;110F 116B 11AD; # (쾒; 쾒; 쾒; 쾒; 쾒; ) HANGUL SYLLABLE KWAENH +CF93;CF93;110F 116B 11AE;CF93;110F 116B 11AE; # (쾓; 쾓; 쾓; 쾓; 쾓; ) HANGUL SYLLABLE KWAED +CF94;CF94;110F 116B 11AF;CF94;110F 116B 11AF; # (쾔; 쾔; 쾔; 쾔; 쾔; ) HANGUL SYLLABLE KWAEL +CF95;CF95;110F 116B 11B0;CF95;110F 116B 11B0; # (쾕; 쾕; 쾕; 쾕; 쾕; ) HANGUL SYLLABLE KWAELG +CF96;CF96;110F 116B 11B1;CF96;110F 116B 11B1; # (쾖; 쾖; 쾖; 쾖; 쾖; ) HANGUL SYLLABLE KWAELM +CF97;CF97;110F 116B 11B2;CF97;110F 116B 11B2; # (쾗; 쾗; 쾗; 쾗; 쾗; ) HANGUL SYLLABLE KWAELB +CF98;CF98;110F 116B 11B3;CF98;110F 116B 11B3; # (쾘; 쾘; 쾘; 쾘; 쾘; ) HANGUL SYLLABLE KWAELS +CF99;CF99;110F 116B 11B4;CF99;110F 116B 11B4; # (쾙; 쾙; 쾙; 쾙; 쾙; ) HANGUL SYLLABLE KWAELT +CF9A;CF9A;110F 116B 11B5;CF9A;110F 116B 11B5; # (쾚; 쾚; 쾚; 쾚; 쾚; ) HANGUL SYLLABLE KWAELP +CF9B;CF9B;110F 116B 11B6;CF9B;110F 116B 11B6; # (쾛; 쾛; 쾛; 쾛; 쾛; ) HANGUL SYLLABLE KWAELH +CF9C;CF9C;110F 116B 11B7;CF9C;110F 116B 11B7; # (쾜; 쾜; 쾜; 쾜; 쾜; ) HANGUL SYLLABLE KWAEM +CF9D;CF9D;110F 116B 11B8;CF9D;110F 116B 11B8; # (쾝; 쾝; 쾝; 쾝; 쾝; ) HANGUL SYLLABLE KWAEB +CF9E;CF9E;110F 116B 11B9;CF9E;110F 116B 11B9; # (쾞; 쾞; 쾞; 쾞; 쾞; ) HANGUL SYLLABLE KWAEBS +CF9F;CF9F;110F 116B 11BA;CF9F;110F 116B 11BA; # (쾟; 쾟; 쾟; 쾟; 쾟; ) HANGUL SYLLABLE KWAES +CFA0;CFA0;110F 116B 11BB;CFA0;110F 116B 11BB; # (쾠; 쾠; 쾠; 쾠; 쾠; ) HANGUL SYLLABLE KWAESS +CFA1;CFA1;110F 116B 11BC;CFA1;110F 116B 11BC; # (쾡; 쾡; 쾡; 쾡; 쾡; ) HANGUL SYLLABLE KWAENG +CFA2;CFA2;110F 116B 11BD;CFA2;110F 116B 11BD; # (쾢; 쾢; 쾢; 쾢; 쾢; ) HANGUL SYLLABLE KWAEJ +CFA3;CFA3;110F 116B 11BE;CFA3;110F 116B 11BE; # (쾣; 쾣; 쾣; 쾣; 쾣; ) HANGUL SYLLABLE KWAEC +CFA4;CFA4;110F 116B 11BF;CFA4;110F 116B 11BF; # (쾤; 쾤; 쾤; 쾤; 쾤; ) HANGUL SYLLABLE KWAEK +CFA5;CFA5;110F 116B 11C0;CFA5;110F 116B 11C0; # (쾥; 쾥; 쾥; 쾥; 쾥; ) HANGUL SYLLABLE KWAET +CFA6;CFA6;110F 116B 11C1;CFA6;110F 116B 11C1; # (쾦; 쾦; 쾦; 쾦; 쾦; ) HANGUL SYLLABLE KWAEP +CFA7;CFA7;110F 116B 11C2;CFA7;110F 116B 11C2; # (쾧; 쾧; 쾧; 쾧; 쾧; ) HANGUL SYLLABLE KWAEH +CFA8;CFA8;110F 116C;CFA8;110F 116C; # (쾨; 쾨; 쾨; 쾨; 쾨; ) HANGUL SYLLABLE KOE +CFA9;CFA9;110F 116C 11A8;CFA9;110F 116C 11A8; # (쾩; 쾩; 쾩; 쾩; 쾩; ) HANGUL SYLLABLE KOEG +CFAA;CFAA;110F 116C 11A9;CFAA;110F 116C 11A9; # (쾪; 쾪; 쾪; 쾪; 쾪; ) HANGUL SYLLABLE KOEGG +CFAB;CFAB;110F 116C 11AA;CFAB;110F 116C 11AA; # (쾫; 쾫; 쾫; 쾫; 쾫; ) HANGUL SYLLABLE KOEGS +CFAC;CFAC;110F 116C 11AB;CFAC;110F 116C 11AB; # (쾬; 쾬; 쾬; 쾬; 쾬; ) HANGUL SYLLABLE KOEN +CFAD;CFAD;110F 116C 11AC;CFAD;110F 116C 11AC; # (쾭; 쾭; 쾭; 쾭; 쾭; ) HANGUL SYLLABLE KOENJ +CFAE;CFAE;110F 116C 11AD;CFAE;110F 116C 11AD; # (쾮; 쾮; 쾮; 쾮; 쾮; ) HANGUL SYLLABLE KOENH +CFAF;CFAF;110F 116C 11AE;CFAF;110F 116C 11AE; # (쾯; 쾯; 쾯; 쾯; 쾯; ) HANGUL SYLLABLE KOED +CFB0;CFB0;110F 116C 11AF;CFB0;110F 116C 11AF; # (쾰; 쾰; 쾰; 쾰; 쾰; ) HANGUL SYLLABLE KOEL +CFB1;CFB1;110F 116C 11B0;CFB1;110F 116C 11B0; # (쾱; 쾱; 쾱; 쾱; 쾱; ) HANGUL SYLLABLE KOELG +CFB2;CFB2;110F 116C 11B1;CFB2;110F 116C 11B1; # (쾲; 쾲; 쾲; 쾲; 쾲; ) HANGUL SYLLABLE KOELM +CFB3;CFB3;110F 116C 11B2;CFB3;110F 116C 11B2; # (쾳; 쾳; 쾳; 쾳; 쾳; ) HANGUL SYLLABLE KOELB +CFB4;CFB4;110F 116C 11B3;CFB4;110F 116C 11B3; # (쾴; 쾴; 쾴; 쾴; 쾴; ) HANGUL SYLLABLE KOELS +CFB5;CFB5;110F 116C 11B4;CFB5;110F 116C 11B4; # (쾵; 쾵; 쾵; 쾵; 쾵; ) HANGUL SYLLABLE KOELT +CFB6;CFB6;110F 116C 11B5;CFB6;110F 116C 11B5; # (쾶; 쾶; 쾶; 쾶; 쾶; ) HANGUL SYLLABLE KOELP +CFB7;CFB7;110F 116C 11B6;CFB7;110F 116C 11B6; # (쾷; 쾷; 쾷; 쾷; 쾷; ) HANGUL SYLLABLE KOELH +CFB8;CFB8;110F 116C 11B7;CFB8;110F 116C 11B7; # (쾸; 쾸; 쾸; 쾸; 쾸; ) HANGUL SYLLABLE KOEM +CFB9;CFB9;110F 116C 11B8;CFB9;110F 116C 11B8; # (쾹; 쾹; 쾹; 쾹; 쾹; ) HANGUL SYLLABLE KOEB +CFBA;CFBA;110F 116C 11B9;CFBA;110F 116C 11B9; # (쾺; 쾺; 쾺; 쾺; 쾺; ) HANGUL SYLLABLE KOEBS +CFBB;CFBB;110F 116C 11BA;CFBB;110F 116C 11BA; # (쾻; 쾻; 쾻; 쾻; 쾻; ) HANGUL SYLLABLE KOES +CFBC;CFBC;110F 116C 11BB;CFBC;110F 116C 11BB; # (쾼; 쾼; 쾼; 쾼; 쾼; ) HANGUL SYLLABLE KOESS +CFBD;CFBD;110F 116C 11BC;CFBD;110F 116C 11BC; # (쾽; 쾽; 쾽; 쾽; 쾽; ) HANGUL SYLLABLE KOENG +CFBE;CFBE;110F 116C 11BD;CFBE;110F 116C 11BD; # (쾾; 쾾; 쾾; 쾾; 쾾; ) HANGUL SYLLABLE KOEJ +CFBF;CFBF;110F 116C 11BE;CFBF;110F 116C 11BE; # (쾿; 쾿; 쾿; 쾿; 쾿; ) HANGUL SYLLABLE KOEC +CFC0;CFC0;110F 116C 11BF;CFC0;110F 116C 11BF; # (쿀; 쿀; 쿀; 쿀; 쿀; ) HANGUL SYLLABLE KOEK +CFC1;CFC1;110F 116C 11C0;CFC1;110F 116C 11C0; # (쿁; 쿁; 쿁; 쿁; 쿁; ) HANGUL SYLLABLE KOET +CFC2;CFC2;110F 116C 11C1;CFC2;110F 116C 11C1; # (쿂; 쿂; 쿂; 쿂; 쿂; ) HANGUL SYLLABLE KOEP +CFC3;CFC3;110F 116C 11C2;CFC3;110F 116C 11C2; # (쿃; 쿃; 쿃; 쿃; 쿃; ) HANGUL SYLLABLE KOEH +CFC4;CFC4;110F 116D;CFC4;110F 116D; # (쿄; 쿄; 쿄; 쿄; 쿄; ) HANGUL SYLLABLE KYO +CFC5;CFC5;110F 116D 11A8;CFC5;110F 116D 11A8; # (쿅; 쿅; 쿅; 쿅; 쿅; ) HANGUL SYLLABLE KYOG +CFC6;CFC6;110F 116D 11A9;CFC6;110F 116D 11A9; # (쿆; 쿆; 쿆; 쿆; 쿆; ) HANGUL SYLLABLE KYOGG +CFC7;CFC7;110F 116D 11AA;CFC7;110F 116D 11AA; # (쿇; 쿇; 쿇; 쿇; 쿇; ) HANGUL SYLLABLE KYOGS +CFC8;CFC8;110F 116D 11AB;CFC8;110F 116D 11AB; # (쿈; 쿈; 쿈; 쿈; 쿈; ) HANGUL SYLLABLE KYON +CFC9;CFC9;110F 116D 11AC;CFC9;110F 116D 11AC; # (쿉; 쿉; 쿉; 쿉; 쿉; ) HANGUL SYLLABLE KYONJ +CFCA;CFCA;110F 116D 11AD;CFCA;110F 116D 11AD; # (쿊; 쿊; 쿊; 쿊; 쿊; ) HANGUL SYLLABLE KYONH +CFCB;CFCB;110F 116D 11AE;CFCB;110F 116D 11AE; # (쿋; 쿋; 쿋; 쿋; 쿋; ) HANGUL SYLLABLE KYOD +CFCC;CFCC;110F 116D 11AF;CFCC;110F 116D 11AF; # (쿌; 쿌; 쿌; 쿌; 쿌; ) HANGUL SYLLABLE KYOL +CFCD;CFCD;110F 116D 11B0;CFCD;110F 116D 11B0; # (쿍; 쿍; 쿍; 쿍; 쿍; ) HANGUL SYLLABLE KYOLG +CFCE;CFCE;110F 116D 11B1;CFCE;110F 116D 11B1; # (쿎; 쿎; 쿎; 쿎; 쿎; ) HANGUL SYLLABLE KYOLM +CFCF;CFCF;110F 116D 11B2;CFCF;110F 116D 11B2; # (쿏; 쿏; 쿏; 쿏; 쿏; ) HANGUL SYLLABLE KYOLB +CFD0;CFD0;110F 116D 11B3;CFD0;110F 116D 11B3; # (쿐; 쿐; 쿐; 쿐; 쿐; ) HANGUL SYLLABLE KYOLS +CFD1;CFD1;110F 116D 11B4;CFD1;110F 116D 11B4; # (쿑; 쿑; 쿑; 쿑; 쿑; ) HANGUL SYLLABLE KYOLT +CFD2;CFD2;110F 116D 11B5;CFD2;110F 116D 11B5; # (쿒; 쿒; 쿒; 쿒; 쿒; ) HANGUL SYLLABLE KYOLP +CFD3;CFD3;110F 116D 11B6;CFD3;110F 116D 11B6; # (쿓; 쿓; 쿓; 쿓; 쿓; ) HANGUL SYLLABLE KYOLH +CFD4;CFD4;110F 116D 11B7;CFD4;110F 116D 11B7; # (쿔; 쿔; 쿔; 쿔; 쿔; ) HANGUL SYLLABLE KYOM +CFD5;CFD5;110F 116D 11B8;CFD5;110F 116D 11B8; # (쿕; 쿕; 쿕; 쿕; 쿕; ) HANGUL SYLLABLE KYOB +CFD6;CFD6;110F 116D 11B9;CFD6;110F 116D 11B9; # (쿖; 쿖; 쿖; 쿖; 쿖; ) HANGUL SYLLABLE KYOBS +CFD7;CFD7;110F 116D 11BA;CFD7;110F 116D 11BA; # (쿗; 쿗; 쿗; 쿗; 쿗; ) HANGUL SYLLABLE KYOS +CFD8;CFD8;110F 116D 11BB;CFD8;110F 116D 11BB; # (쿘; 쿘; 쿘; 쿘; 쿘; ) HANGUL SYLLABLE KYOSS +CFD9;CFD9;110F 116D 11BC;CFD9;110F 116D 11BC; # (쿙; 쿙; 쿙; 쿙; 쿙; ) HANGUL SYLLABLE KYONG +CFDA;CFDA;110F 116D 11BD;CFDA;110F 116D 11BD; # (쿚; 쿚; 쿚; 쿚; 쿚; ) HANGUL SYLLABLE KYOJ +CFDB;CFDB;110F 116D 11BE;CFDB;110F 116D 11BE; # (쿛; 쿛; 쿛; 쿛; 쿛; ) HANGUL SYLLABLE KYOC +CFDC;CFDC;110F 116D 11BF;CFDC;110F 116D 11BF; # (쿜; 쿜; 쿜; 쿜; 쿜; ) HANGUL SYLLABLE KYOK +CFDD;CFDD;110F 116D 11C0;CFDD;110F 116D 11C0; # (쿝; 쿝; 쿝; 쿝; 쿝; ) HANGUL SYLLABLE KYOT +CFDE;CFDE;110F 116D 11C1;CFDE;110F 116D 11C1; # (쿞; 쿞; 쿞; 쿞; 쿞; ) HANGUL SYLLABLE KYOP +CFDF;CFDF;110F 116D 11C2;CFDF;110F 116D 11C2; # (쿟; 쿟; 쿟; 쿟; 쿟; ) HANGUL SYLLABLE KYOH +CFE0;CFE0;110F 116E;CFE0;110F 116E; # (쿠; 쿠; 쿠; 쿠; 쿠; ) HANGUL SYLLABLE KU +CFE1;CFE1;110F 116E 11A8;CFE1;110F 116E 11A8; # (쿡; 쿡; 쿡; 쿡; 쿡; ) HANGUL SYLLABLE KUG +CFE2;CFE2;110F 116E 11A9;CFE2;110F 116E 11A9; # (쿢; 쿢; 쿢; 쿢; 쿢; ) HANGUL SYLLABLE KUGG +CFE3;CFE3;110F 116E 11AA;CFE3;110F 116E 11AA; # (쿣; 쿣; 쿣; 쿣; 쿣; ) HANGUL SYLLABLE KUGS +CFE4;CFE4;110F 116E 11AB;CFE4;110F 116E 11AB; # (쿤; 쿤; 쿤; 쿤; 쿤; ) HANGUL SYLLABLE KUN +CFE5;CFE5;110F 116E 11AC;CFE5;110F 116E 11AC; # (쿥; 쿥; 쿥; 쿥; 쿥; ) HANGUL SYLLABLE KUNJ +CFE6;CFE6;110F 116E 11AD;CFE6;110F 116E 11AD; # (쿦; 쿦; 쿦; 쿦; 쿦; ) HANGUL SYLLABLE KUNH +CFE7;CFE7;110F 116E 11AE;CFE7;110F 116E 11AE; # (쿧; 쿧; 쿧; 쿧; 쿧; ) HANGUL SYLLABLE KUD +CFE8;CFE8;110F 116E 11AF;CFE8;110F 116E 11AF; # (쿨; 쿨; 쿨; 쿨; 쿨; ) HANGUL SYLLABLE KUL +CFE9;CFE9;110F 116E 11B0;CFE9;110F 116E 11B0; # (쿩; 쿩; 쿩; 쿩; 쿩; ) HANGUL SYLLABLE KULG +CFEA;CFEA;110F 116E 11B1;CFEA;110F 116E 11B1; # (쿪; 쿪; 쿪; 쿪; 쿪; ) HANGUL SYLLABLE KULM +CFEB;CFEB;110F 116E 11B2;CFEB;110F 116E 11B2; # (쿫; 쿫; 쿫; 쿫; 쿫; ) HANGUL SYLLABLE KULB +CFEC;CFEC;110F 116E 11B3;CFEC;110F 116E 11B3; # (쿬; 쿬; 쿬; 쿬; 쿬; ) HANGUL SYLLABLE KULS +CFED;CFED;110F 116E 11B4;CFED;110F 116E 11B4; # (쿭; 쿭; 쿭; 쿭; 쿭; ) HANGUL SYLLABLE KULT +CFEE;CFEE;110F 116E 11B5;CFEE;110F 116E 11B5; # (쿮; 쿮; 쿮; 쿮; 쿮; ) HANGUL SYLLABLE KULP +CFEF;CFEF;110F 116E 11B6;CFEF;110F 116E 11B6; # (쿯; 쿯; 쿯; 쿯; 쿯; ) HANGUL SYLLABLE KULH +CFF0;CFF0;110F 116E 11B7;CFF0;110F 116E 11B7; # (쿰; 쿰; 쿰; 쿰; 쿰; ) HANGUL SYLLABLE KUM +CFF1;CFF1;110F 116E 11B8;CFF1;110F 116E 11B8; # (쿱; 쿱; 쿱; 쿱; 쿱; ) HANGUL SYLLABLE KUB +CFF2;CFF2;110F 116E 11B9;CFF2;110F 116E 11B9; # (쿲; 쿲; 쿲; 쿲; 쿲; ) HANGUL SYLLABLE KUBS +CFF3;CFF3;110F 116E 11BA;CFF3;110F 116E 11BA; # (쿳; 쿳; 쿳; 쿳; 쿳; ) HANGUL SYLLABLE KUS +CFF4;CFF4;110F 116E 11BB;CFF4;110F 116E 11BB; # (쿴; 쿴; 쿴; 쿴; 쿴; ) HANGUL SYLLABLE KUSS +CFF5;CFF5;110F 116E 11BC;CFF5;110F 116E 11BC; # (쿵; 쿵; 쿵; 쿵; 쿵; ) HANGUL SYLLABLE KUNG +CFF6;CFF6;110F 116E 11BD;CFF6;110F 116E 11BD; # (쿶; 쿶; 쿶; 쿶; 쿶; ) HANGUL SYLLABLE KUJ +CFF7;CFF7;110F 116E 11BE;CFF7;110F 116E 11BE; # (쿷; 쿷; 쿷; 쿷; 쿷; ) HANGUL SYLLABLE KUC +CFF8;CFF8;110F 116E 11BF;CFF8;110F 116E 11BF; # (쿸; 쿸; 쿸; 쿸; 쿸; ) HANGUL SYLLABLE KUK +CFF9;CFF9;110F 116E 11C0;CFF9;110F 116E 11C0; # (쿹; 쿹; 쿹; 쿹; 쿹; ) HANGUL SYLLABLE KUT +CFFA;CFFA;110F 116E 11C1;CFFA;110F 116E 11C1; # (쿺; 쿺; 쿺; 쿺; 쿺; ) HANGUL SYLLABLE KUP +CFFB;CFFB;110F 116E 11C2;CFFB;110F 116E 11C2; # (쿻; 쿻; 쿻; 쿻; 쿻; ) HANGUL SYLLABLE KUH +CFFC;CFFC;110F 116F;CFFC;110F 116F; # (쿼; 쿼; 쿼; 쿼; 쿼; ) HANGUL SYLLABLE KWEO +CFFD;CFFD;110F 116F 11A8;CFFD;110F 116F 11A8; # (쿽; 쿽; 쿽; 쿽; 쿽; ) HANGUL SYLLABLE KWEOG +CFFE;CFFE;110F 116F 11A9;CFFE;110F 116F 11A9; # (쿾; 쿾; 쿾; 쿾; 쿾; ) HANGUL SYLLABLE KWEOGG +CFFF;CFFF;110F 116F 11AA;CFFF;110F 116F 11AA; # (쿿; 쿿; 쿿; 쿿; 쿿; ) HANGUL SYLLABLE KWEOGS +D000;D000;110F 116F 11AB;D000;110F 116F 11AB; # (퀀; 퀀; 퀀; 퀀; 퀀; ) HANGUL SYLLABLE KWEON +D001;D001;110F 116F 11AC;D001;110F 116F 11AC; # (퀁; 퀁; 퀁; 퀁; 퀁; ) HANGUL SYLLABLE KWEONJ +D002;D002;110F 116F 11AD;D002;110F 116F 11AD; # (퀂; 퀂; 퀂; 퀂; 퀂; ) HANGUL SYLLABLE KWEONH +D003;D003;110F 116F 11AE;D003;110F 116F 11AE; # (퀃; 퀃; 퀃; 퀃; 퀃; ) HANGUL SYLLABLE KWEOD +D004;D004;110F 116F 11AF;D004;110F 116F 11AF; # (퀄; 퀄; 퀄; 퀄; 퀄; ) HANGUL SYLLABLE KWEOL +D005;D005;110F 116F 11B0;D005;110F 116F 11B0; # (퀅; 퀅; 퀅; 퀅; 퀅; ) HANGUL SYLLABLE KWEOLG +D006;D006;110F 116F 11B1;D006;110F 116F 11B1; # (퀆; 퀆; 퀆; 퀆; 퀆; ) HANGUL SYLLABLE KWEOLM +D007;D007;110F 116F 11B2;D007;110F 116F 11B2; # (퀇; 퀇; 퀇; 퀇; 퀇; ) HANGUL SYLLABLE KWEOLB +D008;D008;110F 116F 11B3;D008;110F 116F 11B3; # (퀈; 퀈; 퀈; 퀈; 퀈; ) HANGUL SYLLABLE KWEOLS +D009;D009;110F 116F 11B4;D009;110F 116F 11B4; # (퀉; 퀉; 퀉; 퀉; 퀉; ) HANGUL SYLLABLE KWEOLT +D00A;D00A;110F 116F 11B5;D00A;110F 116F 11B5; # (퀊; 퀊; 퀊; 퀊; 퀊; ) HANGUL SYLLABLE KWEOLP +D00B;D00B;110F 116F 11B6;D00B;110F 116F 11B6; # (퀋; 퀋; 퀋; 퀋; 퀋; ) HANGUL SYLLABLE KWEOLH +D00C;D00C;110F 116F 11B7;D00C;110F 116F 11B7; # (퀌; 퀌; 퀌; 퀌; 퀌; ) HANGUL SYLLABLE KWEOM +D00D;D00D;110F 116F 11B8;D00D;110F 116F 11B8; # (퀍; 퀍; 퀍; 퀍; 퀍; ) HANGUL SYLLABLE KWEOB +D00E;D00E;110F 116F 11B9;D00E;110F 116F 11B9; # (퀎; 퀎; 퀎; 퀎; 퀎; ) HANGUL SYLLABLE KWEOBS +D00F;D00F;110F 116F 11BA;D00F;110F 116F 11BA; # (퀏; 퀏; 퀏; 퀏; 퀏; ) HANGUL SYLLABLE KWEOS +D010;D010;110F 116F 11BB;D010;110F 116F 11BB; # (퀐; 퀐; 퀐; 퀐; 퀐; ) HANGUL SYLLABLE KWEOSS +D011;D011;110F 116F 11BC;D011;110F 116F 11BC; # (퀑; 퀑; 퀑; 퀑; 퀑; ) HANGUL SYLLABLE KWEONG +D012;D012;110F 116F 11BD;D012;110F 116F 11BD; # (퀒; 퀒; 퀒; 퀒; 퀒; ) HANGUL SYLLABLE KWEOJ +D013;D013;110F 116F 11BE;D013;110F 116F 11BE; # (퀓; 퀓; 퀓; 퀓; 퀓; ) HANGUL SYLLABLE KWEOC +D014;D014;110F 116F 11BF;D014;110F 116F 11BF; # (퀔; 퀔; 퀔; 퀔; 퀔; ) HANGUL SYLLABLE KWEOK +D015;D015;110F 116F 11C0;D015;110F 116F 11C0; # (퀕; 퀕; 퀕; 퀕; 퀕; ) HANGUL SYLLABLE KWEOT +D016;D016;110F 116F 11C1;D016;110F 116F 11C1; # (퀖; 퀖; 퀖; 퀖; 퀖; ) HANGUL SYLLABLE KWEOP +D017;D017;110F 116F 11C2;D017;110F 116F 11C2; # (퀗; 퀗; 퀗; 퀗; 퀗; ) HANGUL SYLLABLE KWEOH +D018;D018;110F 1170;D018;110F 1170; # (퀘; 퀘; 퀘; 퀘; 퀘; ) HANGUL SYLLABLE KWE +D019;D019;110F 1170 11A8;D019;110F 1170 11A8; # (퀙; 퀙; 퀙; 퀙; 퀙; ) HANGUL SYLLABLE KWEG +D01A;D01A;110F 1170 11A9;D01A;110F 1170 11A9; # (퀚; 퀚; 퀚; 퀚; 퀚; ) HANGUL SYLLABLE KWEGG +D01B;D01B;110F 1170 11AA;D01B;110F 1170 11AA; # (퀛; 퀛; 퀛; 퀛; 퀛; ) HANGUL SYLLABLE KWEGS +D01C;D01C;110F 1170 11AB;D01C;110F 1170 11AB; # (퀜; 퀜; 퀜; 퀜; 퀜; ) HANGUL SYLLABLE KWEN +D01D;D01D;110F 1170 11AC;D01D;110F 1170 11AC; # (퀝; 퀝; 퀝; 퀝; 퀝; ) HANGUL SYLLABLE KWENJ +D01E;D01E;110F 1170 11AD;D01E;110F 1170 11AD; # (퀞; 퀞; 퀞; 퀞; 퀞; ) HANGUL SYLLABLE KWENH +D01F;D01F;110F 1170 11AE;D01F;110F 1170 11AE; # (퀟; 퀟; 퀟; 퀟; 퀟; ) HANGUL SYLLABLE KWED +D020;D020;110F 1170 11AF;D020;110F 1170 11AF; # (퀠; 퀠; 퀠; 퀠; 퀠; ) HANGUL SYLLABLE KWEL +D021;D021;110F 1170 11B0;D021;110F 1170 11B0; # (퀡; 퀡; 퀡; 퀡; 퀡; ) HANGUL SYLLABLE KWELG +D022;D022;110F 1170 11B1;D022;110F 1170 11B1; # (퀢; 퀢; 퀢; 퀢; 퀢; ) HANGUL SYLLABLE KWELM +D023;D023;110F 1170 11B2;D023;110F 1170 11B2; # (퀣; 퀣; 퀣; 퀣; 퀣; ) HANGUL SYLLABLE KWELB +D024;D024;110F 1170 11B3;D024;110F 1170 11B3; # (퀤; 퀤; 퀤; 퀤; 퀤; ) HANGUL SYLLABLE KWELS +D025;D025;110F 1170 11B4;D025;110F 1170 11B4; # (퀥; 퀥; 퀥; 퀥; 퀥; ) HANGUL SYLLABLE KWELT +D026;D026;110F 1170 11B5;D026;110F 1170 11B5; # (퀦; 퀦; 퀦; 퀦; 퀦; ) HANGUL SYLLABLE KWELP +D027;D027;110F 1170 11B6;D027;110F 1170 11B6; # (퀧; 퀧; 퀧; 퀧; 퀧; ) HANGUL SYLLABLE KWELH +D028;D028;110F 1170 11B7;D028;110F 1170 11B7; # (퀨; 퀨; 퀨; 퀨; 퀨; ) HANGUL SYLLABLE KWEM +D029;D029;110F 1170 11B8;D029;110F 1170 11B8; # (퀩; 퀩; 퀩; 퀩; 퀩; ) HANGUL SYLLABLE KWEB +D02A;D02A;110F 1170 11B9;D02A;110F 1170 11B9; # (퀪; 퀪; 퀪; 퀪; 퀪; ) HANGUL SYLLABLE KWEBS +D02B;D02B;110F 1170 11BA;D02B;110F 1170 11BA; # (퀫; 퀫; 퀫; 퀫; 퀫; ) HANGUL SYLLABLE KWES +D02C;D02C;110F 1170 11BB;D02C;110F 1170 11BB; # (퀬; 퀬; 퀬; 퀬; 퀬; ) HANGUL SYLLABLE KWESS +D02D;D02D;110F 1170 11BC;D02D;110F 1170 11BC; # (퀭; 퀭; 퀭; 퀭; 퀭; ) HANGUL SYLLABLE KWENG +D02E;D02E;110F 1170 11BD;D02E;110F 1170 11BD; # (퀮; 퀮; 퀮; 퀮; 퀮; ) HANGUL SYLLABLE KWEJ +D02F;D02F;110F 1170 11BE;D02F;110F 1170 11BE; # (퀯; 퀯; 퀯; 퀯; 퀯; ) HANGUL SYLLABLE KWEC +D030;D030;110F 1170 11BF;D030;110F 1170 11BF; # (퀰; 퀰; 퀰; 퀰; 퀰; ) HANGUL SYLLABLE KWEK +D031;D031;110F 1170 11C0;D031;110F 1170 11C0; # (퀱; 퀱; 퀱; 퀱; 퀱; ) HANGUL SYLLABLE KWET +D032;D032;110F 1170 11C1;D032;110F 1170 11C1; # (퀲; 퀲; 퀲; 퀲; 퀲; ) HANGUL SYLLABLE KWEP +D033;D033;110F 1170 11C2;D033;110F 1170 11C2; # (퀳; 퀳; 퀳; 퀳; 퀳; ) HANGUL SYLLABLE KWEH +D034;D034;110F 1171;D034;110F 1171; # (퀴; 퀴; 퀴; 퀴; 퀴; ) HANGUL SYLLABLE KWI +D035;D035;110F 1171 11A8;D035;110F 1171 11A8; # (퀵; 퀵; 퀵; 퀵; 퀵; ) HANGUL SYLLABLE KWIG +D036;D036;110F 1171 11A9;D036;110F 1171 11A9; # (퀶; 퀶; 퀶; 퀶; 퀶; ) HANGUL SYLLABLE KWIGG +D037;D037;110F 1171 11AA;D037;110F 1171 11AA; # (퀷; 퀷; 퀷; 퀷; 퀷; ) HANGUL SYLLABLE KWIGS +D038;D038;110F 1171 11AB;D038;110F 1171 11AB; # (퀸; 퀸; 퀸; 퀸; 퀸; ) HANGUL SYLLABLE KWIN +D039;D039;110F 1171 11AC;D039;110F 1171 11AC; # (퀹; 퀹; 퀹; 퀹; 퀹; ) HANGUL SYLLABLE KWINJ +D03A;D03A;110F 1171 11AD;D03A;110F 1171 11AD; # (퀺; 퀺; 퀺; 퀺; 퀺; ) HANGUL SYLLABLE KWINH +D03B;D03B;110F 1171 11AE;D03B;110F 1171 11AE; # (퀻; 퀻; 퀻; 퀻; 퀻; ) HANGUL SYLLABLE KWID +D03C;D03C;110F 1171 11AF;D03C;110F 1171 11AF; # (퀼; 퀼; 퀼; 퀼; 퀼; ) HANGUL SYLLABLE KWIL +D03D;D03D;110F 1171 11B0;D03D;110F 1171 11B0; # (퀽; 퀽; 퀽; 퀽; 퀽; ) HANGUL SYLLABLE KWILG +D03E;D03E;110F 1171 11B1;D03E;110F 1171 11B1; # (퀾; 퀾; 퀾; 퀾; 퀾; ) HANGUL SYLLABLE KWILM +D03F;D03F;110F 1171 11B2;D03F;110F 1171 11B2; # (퀿; 퀿; 퀿; 퀿; 퀿; ) HANGUL SYLLABLE KWILB +D040;D040;110F 1171 11B3;D040;110F 1171 11B3; # (큀; 큀; 큀; 큀; 큀; ) HANGUL SYLLABLE KWILS +D041;D041;110F 1171 11B4;D041;110F 1171 11B4; # (큁; 큁; 큁; 큁; 큁; ) HANGUL SYLLABLE KWILT +D042;D042;110F 1171 11B5;D042;110F 1171 11B5; # (큂; 큂; 큂; 큂; 큂; ) HANGUL SYLLABLE KWILP +D043;D043;110F 1171 11B6;D043;110F 1171 11B6; # (큃; 큃; 큃; 큃; 큃; ) HANGUL SYLLABLE KWILH +D044;D044;110F 1171 11B7;D044;110F 1171 11B7; # (큄; 큄; 큄; 큄; 큄; ) HANGUL SYLLABLE KWIM +D045;D045;110F 1171 11B8;D045;110F 1171 11B8; # (큅; 큅; 큅; 큅; 큅; ) HANGUL SYLLABLE KWIB +D046;D046;110F 1171 11B9;D046;110F 1171 11B9; # (큆; 큆; 큆; 큆; 큆; ) HANGUL SYLLABLE KWIBS +D047;D047;110F 1171 11BA;D047;110F 1171 11BA; # (큇; 큇; 큇; 큇; 큇; ) HANGUL SYLLABLE KWIS +D048;D048;110F 1171 11BB;D048;110F 1171 11BB; # (큈; 큈; 큈; 큈; 큈; ) HANGUL SYLLABLE KWISS +D049;D049;110F 1171 11BC;D049;110F 1171 11BC; # (큉; 큉; 큉; 큉; 큉; ) HANGUL SYLLABLE KWING +D04A;D04A;110F 1171 11BD;D04A;110F 1171 11BD; # (큊; 큊; 큊; 큊; 큊; ) HANGUL SYLLABLE KWIJ +D04B;D04B;110F 1171 11BE;D04B;110F 1171 11BE; # (큋; 큋; 큋; 큋; 큋; ) HANGUL SYLLABLE KWIC +D04C;D04C;110F 1171 11BF;D04C;110F 1171 11BF; # (큌; 큌; 큌; 큌; 큌; ) HANGUL SYLLABLE KWIK +D04D;D04D;110F 1171 11C0;D04D;110F 1171 11C0; # (큍; 큍; 큍; 큍; 큍; ) HANGUL SYLLABLE KWIT +D04E;D04E;110F 1171 11C1;D04E;110F 1171 11C1; # (큎; 큎; 큎; 큎; 큎; ) HANGUL SYLLABLE KWIP +D04F;D04F;110F 1171 11C2;D04F;110F 1171 11C2; # (큏; 큏; 큏; 큏; 큏; ) HANGUL SYLLABLE KWIH +D050;D050;110F 1172;D050;110F 1172; # (큐; 큐; 큐; 큐; 큐; ) HANGUL SYLLABLE KYU +D051;D051;110F 1172 11A8;D051;110F 1172 11A8; # (큑; 큑; 큑; 큑; 큑; ) HANGUL SYLLABLE KYUG +D052;D052;110F 1172 11A9;D052;110F 1172 11A9; # (큒; 큒; 큒; 큒; 큒; ) HANGUL SYLLABLE KYUGG +D053;D053;110F 1172 11AA;D053;110F 1172 11AA; # (큓; 큓; 큓; 큓; 큓; ) HANGUL SYLLABLE KYUGS +D054;D054;110F 1172 11AB;D054;110F 1172 11AB; # (큔; 큔; 큔; 큔; 큔; ) HANGUL SYLLABLE KYUN +D055;D055;110F 1172 11AC;D055;110F 1172 11AC; # (큕; 큕; 큕; 큕; 큕; ) HANGUL SYLLABLE KYUNJ +D056;D056;110F 1172 11AD;D056;110F 1172 11AD; # (큖; 큖; 큖; 큖; 큖; ) HANGUL SYLLABLE KYUNH +D057;D057;110F 1172 11AE;D057;110F 1172 11AE; # (큗; 큗; 큗; 큗; 큗; ) HANGUL SYLLABLE KYUD +D058;D058;110F 1172 11AF;D058;110F 1172 11AF; # (큘; 큘; 큘; 큘; 큘; ) HANGUL SYLLABLE KYUL +D059;D059;110F 1172 11B0;D059;110F 1172 11B0; # (큙; 큙; 큙; 큙; 큙; ) HANGUL SYLLABLE KYULG +D05A;D05A;110F 1172 11B1;D05A;110F 1172 11B1; # (큚; 큚; 큚; 큚; 큚; ) HANGUL SYLLABLE KYULM +D05B;D05B;110F 1172 11B2;D05B;110F 1172 11B2; # (큛; 큛; 큛; 큛; 큛; ) HANGUL SYLLABLE KYULB +D05C;D05C;110F 1172 11B3;D05C;110F 1172 11B3; # (큜; 큜; 큜; 큜; 큜; ) HANGUL SYLLABLE KYULS +D05D;D05D;110F 1172 11B4;D05D;110F 1172 11B4; # (큝; 큝; 큝; 큝; 큝; ) HANGUL SYLLABLE KYULT +D05E;D05E;110F 1172 11B5;D05E;110F 1172 11B5; # (큞; 큞; 큞; 큞; 큞; ) HANGUL SYLLABLE KYULP +D05F;D05F;110F 1172 11B6;D05F;110F 1172 11B6; # (큟; 큟; 큟; 큟; 큟; ) HANGUL SYLLABLE KYULH +D060;D060;110F 1172 11B7;D060;110F 1172 11B7; # (큠; 큠; 큠; 큠; 큠; ) HANGUL SYLLABLE KYUM +D061;D061;110F 1172 11B8;D061;110F 1172 11B8; # (큡; 큡; 큡; 큡; 큡; ) HANGUL SYLLABLE KYUB +D062;D062;110F 1172 11B9;D062;110F 1172 11B9; # (큢; 큢; 큢; 큢; 큢; ) HANGUL SYLLABLE KYUBS +D063;D063;110F 1172 11BA;D063;110F 1172 11BA; # (큣; 큣; 큣; 큣; 큣; ) HANGUL SYLLABLE KYUS +D064;D064;110F 1172 11BB;D064;110F 1172 11BB; # (큤; 큤; 큤; 큤; 큤; ) HANGUL SYLLABLE KYUSS +D065;D065;110F 1172 11BC;D065;110F 1172 11BC; # (큥; 큥; 큥; 큥; 큥; ) HANGUL SYLLABLE KYUNG +D066;D066;110F 1172 11BD;D066;110F 1172 11BD; # (큦; 큦; 큦; 큦; 큦; ) HANGUL SYLLABLE KYUJ +D067;D067;110F 1172 11BE;D067;110F 1172 11BE; # (큧; 큧; 큧; 큧; 큧; ) HANGUL SYLLABLE KYUC +D068;D068;110F 1172 11BF;D068;110F 1172 11BF; # (큨; 큨; 큨; 큨; 큨; ) HANGUL SYLLABLE KYUK +D069;D069;110F 1172 11C0;D069;110F 1172 11C0; # (큩; 큩; 큩; 큩; 큩; ) HANGUL SYLLABLE KYUT +D06A;D06A;110F 1172 11C1;D06A;110F 1172 11C1; # (큪; 큪; 큪; 큪; 큪; ) HANGUL SYLLABLE KYUP +D06B;D06B;110F 1172 11C2;D06B;110F 1172 11C2; # (큫; 큫; 큫; 큫; 큫; ) HANGUL SYLLABLE KYUH +D06C;D06C;110F 1173;D06C;110F 1173; # (크; 크; 크; 크; 크; ) HANGUL SYLLABLE KEU +D06D;D06D;110F 1173 11A8;D06D;110F 1173 11A8; # (큭; 큭; 큭; 큭; 큭; ) HANGUL SYLLABLE KEUG +D06E;D06E;110F 1173 11A9;D06E;110F 1173 11A9; # (큮; 큮; 큮; 큮; 큮; ) HANGUL SYLLABLE KEUGG +D06F;D06F;110F 1173 11AA;D06F;110F 1173 11AA; # (큯; 큯; 큯; 큯; 큯; ) HANGUL SYLLABLE KEUGS +D070;D070;110F 1173 11AB;D070;110F 1173 11AB; # (큰; 큰; 큰; 큰; 큰; ) HANGUL SYLLABLE KEUN +D071;D071;110F 1173 11AC;D071;110F 1173 11AC; # (큱; 큱; 큱; 큱; 큱; ) HANGUL SYLLABLE KEUNJ +D072;D072;110F 1173 11AD;D072;110F 1173 11AD; # (큲; 큲; 큲; 큲; 큲; ) HANGUL SYLLABLE KEUNH +D073;D073;110F 1173 11AE;D073;110F 1173 11AE; # (큳; 큳; 큳; 큳; 큳; ) HANGUL SYLLABLE KEUD +D074;D074;110F 1173 11AF;D074;110F 1173 11AF; # (클; 클; 클; 클; 클; ) HANGUL SYLLABLE KEUL +D075;D075;110F 1173 11B0;D075;110F 1173 11B0; # (큵; 큵; 큵; 큵; 큵; ) HANGUL SYLLABLE KEULG +D076;D076;110F 1173 11B1;D076;110F 1173 11B1; # (큶; 큶; 큶; 큶; 큶; ) HANGUL SYLLABLE KEULM +D077;D077;110F 1173 11B2;D077;110F 1173 11B2; # (큷; 큷; 큷; 큷; 큷; ) HANGUL SYLLABLE KEULB +D078;D078;110F 1173 11B3;D078;110F 1173 11B3; # (큸; 큸; 큸; 큸; 큸; ) HANGUL SYLLABLE KEULS +D079;D079;110F 1173 11B4;D079;110F 1173 11B4; # (큹; 큹; 큹; 큹; 큹; ) HANGUL SYLLABLE KEULT +D07A;D07A;110F 1173 11B5;D07A;110F 1173 11B5; # (큺; 큺; 큺; 큺; 큺; ) HANGUL SYLLABLE KEULP +D07B;D07B;110F 1173 11B6;D07B;110F 1173 11B6; # (큻; 큻; 큻; 큻; 큻; ) HANGUL SYLLABLE KEULH +D07C;D07C;110F 1173 11B7;D07C;110F 1173 11B7; # (큼; 큼; 큼; 큼; 큼; ) HANGUL SYLLABLE KEUM +D07D;D07D;110F 1173 11B8;D07D;110F 1173 11B8; # (큽; 큽; 큽; 큽; 큽; ) HANGUL SYLLABLE KEUB +D07E;D07E;110F 1173 11B9;D07E;110F 1173 11B9; # (큾; 큾; 큾; 큾; 큾; ) HANGUL SYLLABLE KEUBS +D07F;D07F;110F 1173 11BA;D07F;110F 1173 11BA; # (큿; 큿; 큿; 큿; 큿; ) HANGUL SYLLABLE KEUS +D080;D080;110F 1173 11BB;D080;110F 1173 11BB; # (킀; 킀; 킀; 킀; 킀; ) HANGUL SYLLABLE KEUSS +D081;D081;110F 1173 11BC;D081;110F 1173 11BC; # (킁; 킁; 킁; 킁; 킁; ) HANGUL SYLLABLE KEUNG +D082;D082;110F 1173 11BD;D082;110F 1173 11BD; # (킂; 킂; 킂; 킂; 킂; ) HANGUL SYLLABLE KEUJ +D083;D083;110F 1173 11BE;D083;110F 1173 11BE; # (킃; 킃; 킃; 킃; 킃; ) HANGUL SYLLABLE KEUC +D084;D084;110F 1173 11BF;D084;110F 1173 11BF; # (킄; 킄; 킄; 킄; 킄; ) HANGUL SYLLABLE KEUK +D085;D085;110F 1173 11C0;D085;110F 1173 11C0; # (킅; 킅; 킅; 킅; 킅; ) HANGUL SYLLABLE KEUT +D086;D086;110F 1173 11C1;D086;110F 1173 11C1; # (킆; 킆; 킆; 킆; 킆; ) HANGUL SYLLABLE KEUP +D087;D087;110F 1173 11C2;D087;110F 1173 11C2; # (킇; 킇; 킇; 킇; 킇; ) HANGUL SYLLABLE KEUH +D088;D088;110F 1174;D088;110F 1174; # (킈; 킈; 킈; 킈; 킈; ) HANGUL SYLLABLE KYI +D089;D089;110F 1174 11A8;D089;110F 1174 11A8; # (킉; 킉; 킉; 킉; 킉; ) HANGUL SYLLABLE KYIG +D08A;D08A;110F 1174 11A9;D08A;110F 1174 11A9; # (킊; 킊; 킊; 킊; 킊; ) HANGUL SYLLABLE KYIGG +D08B;D08B;110F 1174 11AA;D08B;110F 1174 11AA; # (킋; 킋; 킋; 킋; 킋; ) HANGUL SYLLABLE KYIGS +D08C;D08C;110F 1174 11AB;D08C;110F 1174 11AB; # (킌; 킌; 킌; 킌; 킌; ) HANGUL SYLLABLE KYIN +D08D;D08D;110F 1174 11AC;D08D;110F 1174 11AC; # (킍; 킍; 킍; 킍; 킍; ) HANGUL SYLLABLE KYINJ +D08E;D08E;110F 1174 11AD;D08E;110F 1174 11AD; # (킎; 킎; 킎; 킎; 킎; ) HANGUL SYLLABLE KYINH +D08F;D08F;110F 1174 11AE;D08F;110F 1174 11AE; # (킏; 킏; 킏; 킏; 킏; ) HANGUL SYLLABLE KYID +D090;D090;110F 1174 11AF;D090;110F 1174 11AF; # (킐; 킐; 킐; 킐; 킐; ) HANGUL SYLLABLE KYIL +D091;D091;110F 1174 11B0;D091;110F 1174 11B0; # (킑; 킑; 킑; 킑; 킑; ) HANGUL SYLLABLE KYILG +D092;D092;110F 1174 11B1;D092;110F 1174 11B1; # (킒; 킒; 킒; 킒; 킒; ) HANGUL SYLLABLE KYILM +D093;D093;110F 1174 11B2;D093;110F 1174 11B2; # (킓; 킓; 킓; 킓; 킓; ) HANGUL SYLLABLE KYILB +D094;D094;110F 1174 11B3;D094;110F 1174 11B3; # (킔; 킔; 킔; 킔; 킔; ) HANGUL SYLLABLE KYILS +D095;D095;110F 1174 11B4;D095;110F 1174 11B4; # (킕; 킕; 킕; 킕; 킕; ) HANGUL SYLLABLE KYILT +D096;D096;110F 1174 11B5;D096;110F 1174 11B5; # (킖; 킖; 킖; 킖; 킖; ) HANGUL SYLLABLE KYILP +D097;D097;110F 1174 11B6;D097;110F 1174 11B6; # (킗; 킗; 킗; 킗; 킗; ) HANGUL SYLLABLE KYILH +D098;D098;110F 1174 11B7;D098;110F 1174 11B7; # (킘; 킘; 킘; 킘; 킘; ) HANGUL SYLLABLE KYIM +D099;D099;110F 1174 11B8;D099;110F 1174 11B8; # (킙; 킙; 킙; 킙; 킙; ) HANGUL SYLLABLE KYIB +D09A;D09A;110F 1174 11B9;D09A;110F 1174 11B9; # (킚; 킚; 킚; 킚; 킚; ) HANGUL SYLLABLE KYIBS +D09B;D09B;110F 1174 11BA;D09B;110F 1174 11BA; # (킛; 킛; 킛; 킛; 킛; ) HANGUL SYLLABLE KYIS +D09C;D09C;110F 1174 11BB;D09C;110F 1174 11BB; # (킜; 킜; 킜; 킜; 킜; ) HANGUL SYLLABLE KYISS +D09D;D09D;110F 1174 11BC;D09D;110F 1174 11BC; # (킝; 킝; 킝; 킝; 킝; ) HANGUL SYLLABLE KYING +D09E;D09E;110F 1174 11BD;D09E;110F 1174 11BD; # (킞; 킞; 킞; 킞; 킞; ) HANGUL SYLLABLE KYIJ +D09F;D09F;110F 1174 11BE;D09F;110F 1174 11BE; # (킟; 킟; 킟; 킟; 킟; ) HANGUL SYLLABLE KYIC +D0A0;D0A0;110F 1174 11BF;D0A0;110F 1174 11BF; # (킠; 킠; 킠; 킠; 킠; ) HANGUL SYLLABLE KYIK +D0A1;D0A1;110F 1174 11C0;D0A1;110F 1174 11C0; # (킡; 킡; 킡; 킡; 킡; ) HANGUL SYLLABLE KYIT +D0A2;D0A2;110F 1174 11C1;D0A2;110F 1174 11C1; # (킢; 킢; 킢; 킢; 킢; ) HANGUL SYLLABLE KYIP +D0A3;D0A3;110F 1174 11C2;D0A3;110F 1174 11C2; # (킣; 킣; 킣; 킣; 킣; ) HANGUL SYLLABLE KYIH +D0A4;D0A4;110F 1175;D0A4;110F 1175; # (키; 키; 키; 키; 키; ) HANGUL SYLLABLE KI +D0A5;D0A5;110F 1175 11A8;D0A5;110F 1175 11A8; # (킥; 킥; 킥; 킥; 킥; ) HANGUL SYLLABLE KIG +D0A6;D0A6;110F 1175 11A9;D0A6;110F 1175 11A9; # (킦; 킦; 킦; 킦; 킦; ) HANGUL SYLLABLE KIGG +D0A7;D0A7;110F 1175 11AA;D0A7;110F 1175 11AA; # (킧; 킧; 킧; 킧; 킧; ) HANGUL SYLLABLE KIGS +D0A8;D0A8;110F 1175 11AB;D0A8;110F 1175 11AB; # (킨; 킨; 킨; 킨; 킨; ) HANGUL SYLLABLE KIN +D0A9;D0A9;110F 1175 11AC;D0A9;110F 1175 11AC; # (킩; 킩; 킩; 킩; 킩; ) HANGUL SYLLABLE KINJ +D0AA;D0AA;110F 1175 11AD;D0AA;110F 1175 11AD; # (킪; 킪; 킪; 킪; 킪; ) HANGUL SYLLABLE KINH +D0AB;D0AB;110F 1175 11AE;D0AB;110F 1175 11AE; # (킫; 킫; 킫; 킫; 킫; ) HANGUL SYLLABLE KID +D0AC;D0AC;110F 1175 11AF;D0AC;110F 1175 11AF; # (킬; 킬; 킬; 킬; 킬; ) HANGUL SYLLABLE KIL +D0AD;D0AD;110F 1175 11B0;D0AD;110F 1175 11B0; # (킭; 킭; 킭; 킭; 킭; ) HANGUL SYLLABLE KILG +D0AE;D0AE;110F 1175 11B1;D0AE;110F 1175 11B1; # (킮; 킮; 킮; 킮; 킮; ) HANGUL SYLLABLE KILM +D0AF;D0AF;110F 1175 11B2;D0AF;110F 1175 11B2; # (킯; 킯; 킯; 킯; 킯; ) HANGUL SYLLABLE KILB +D0B0;D0B0;110F 1175 11B3;D0B0;110F 1175 11B3; # (킰; 킰; 킰; 킰; 킰; ) HANGUL SYLLABLE KILS +D0B1;D0B1;110F 1175 11B4;D0B1;110F 1175 11B4; # (킱; 킱; 킱; 킱; 킱; ) HANGUL SYLLABLE KILT +D0B2;D0B2;110F 1175 11B5;D0B2;110F 1175 11B5; # (킲; 킲; 킲; 킲; 킲; ) HANGUL SYLLABLE KILP +D0B3;D0B3;110F 1175 11B6;D0B3;110F 1175 11B6; # (킳; 킳; 킳; 킳; 킳; ) HANGUL SYLLABLE KILH +D0B4;D0B4;110F 1175 11B7;D0B4;110F 1175 11B7; # (킴; 킴; 킴; 킴; 킴; ) HANGUL SYLLABLE KIM +D0B5;D0B5;110F 1175 11B8;D0B5;110F 1175 11B8; # (킵; 킵; 킵; 킵; 킵; ) HANGUL SYLLABLE KIB +D0B6;D0B6;110F 1175 11B9;D0B6;110F 1175 11B9; # (킶; 킶; 킶; 킶; 킶; ) HANGUL SYLLABLE KIBS +D0B7;D0B7;110F 1175 11BA;D0B7;110F 1175 11BA; # (킷; 킷; 킷; 킷; 킷; ) HANGUL SYLLABLE KIS +D0B8;D0B8;110F 1175 11BB;D0B8;110F 1175 11BB; # (킸; 킸; 킸; 킸; 킸; ) HANGUL SYLLABLE KISS +D0B9;D0B9;110F 1175 11BC;D0B9;110F 1175 11BC; # (킹; 킹; 킹; 킹; 킹; ) HANGUL SYLLABLE KING +D0BA;D0BA;110F 1175 11BD;D0BA;110F 1175 11BD; # (킺; 킺; 킺; 킺; 킺; ) HANGUL SYLLABLE KIJ +D0BB;D0BB;110F 1175 11BE;D0BB;110F 1175 11BE; # (킻; 킻; 킻; 킻; 킻; ) HANGUL SYLLABLE KIC +D0BC;D0BC;110F 1175 11BF;D0BC;110F 1175 11BF; # (킼; 킼; 킼; 킼; 킼; ) HANGUL SYLLABLE KIK +D0BD;D0BD;110F 1175 11C0;D0BD;110F 1175 11C0; # (킽; 킽; 킽; 킽; 킽; ) HANGUL SYLLABLE KIT +D0BE;D0BE;110F 1175 11C1;D0BE;110F 1175 11C1; # (킾; 킾; 킾; 킾; 킾; ) HANGUL SYLLABLE KIP +D0BF;D0BF;110F 1175 11C2;D0BF;110F 1175 11C2; # (킿; 킿; 킿; 킿; 킿; ) HANGUL SYLLABLE KIH +D0C0;D0C0;1110 1161;D0C0;1110 1161; # (타; 타; 타; 타; 타; ) HANGUL SYLLABLE TA +D0C1;D0C1;1110 1161 11A8;D0C1;1110 1161 11A8; # (탁; 탁; 탁; 탁; 탁; ) HANGUL SYLLABLE TAG +D0C2;D0C2;1110 1161 11A9;D0C2;1110 1161 11A9; # (탂; 탂; 탂; 탂; 탂; ) HANGUL SYLLABLE TAGG +D0C3;D0C3;1110 1161 11AA;D0C3;1110 1161 11AA; # (탃; 탃; 탃; 탃; 탃; ) HANGUL SYLLABLE TAGS +D0C4;D0C4;1110 1161 11AB;D0C4;1110 1161 11AB; # (탄; 탄; 탄; 탄; 탄; ) HANGUL SYLLABLE TAN +D0C5;D0C5;1110 1161 11AC;D0C5;1110 1161 11AC; # (탅; 탅; 탅; 탅; 탅; ) HANGUL SYLLABLE TANJ +D0C6;D0C6;1110 1161 11AD;D0C6;1110 1161 11AD; # (탆; 탆; 탆; 탆; 탆; ) HANGUL SYLLABLE TANH +D0C7;D0C7;1110 1161 11AE;D0C7;1110 1161 11AE; # (탇; 탇; 탇; 탇; 탇; ) HANGUL SYLLABLE TAD +D0C8;D0C8;1110 1161 11AF;D0C8;1110 1161 11AF; # (탈; 탈; 탈; 탈; 탈; ) HANGUL SYLLABLE TAL +D0C9;D0C9;1110 1161 11B0;D0C9;1110 1161 11B0; # (탉; 탉; 탉; 탉; 탉; ) HANGUL SYLLABLE TALG +D0CA;D0CA;1110 1161 11B1;D0CA;1110 1161 11B1; # (탊; 탊; 탊; 탊; 탊; ) HANGUL SYLLABLE TALM +D0CB;D0CB;1110 1161 11B2;D0CB;1110 1161 11B2; # (탋; 탋; 탋; 탋; 탋; ) HANGUL SYLLABLE TALB +D0CC;D0CC;1110 1161 11B3;D0CC;1110 1161 11B3; # (탌; 탌; 탌; 탌; 탌; ) HANGUL SYLLABLE TALS +D0CD;D0CD;1110 1161 11B4;D0CD;1110 1161 11B4; # (탍; 탍; 탍; 탍; 탍; ) HANGUL SYLLABLE TALT +D0CE;D0CE;1110 1161 11B5;D0CE;1110 1161 11B5; # (탎; 탎; 탎; 탎; 탎; ) HANGUL SYLLABLE TALP +D0CF;D0CF;1110 1161 11B6;D0CF;1110 1161 11B6; # (탏; 탏; 탏; 탏; 탏; ) HANGUL SYLLABLE TALH +D0D0;D0D0;1110 1161 11B7;D0D0;1110 1161 11B7; # (탐; 탐; 탐; 탐; 탐; ) HANGUL SYLLABLE TAM +D0D1;D0D1;1110 1161 11B8;D0D1;1110 1161 11B8; # (탑; 탑; 탑; 탑; 탑; ) HANGUL SYLLABLE TAB +D0D2;D0D2;1110 1161 11B9;D0D2;1110 1161 11B9; # (탒; 탒; 탒; 탒; 탒; ) HANGUL SYLLABLE TABS +D0D3;D0D3;1110 1161 11BA;D0D3;1110 1161 11BA; # (탓; 탓; 탓; 탓; 탓; ) HANGUL SYLLABLE TAS +D0D4;D0D4;1110 1161 11BB;D0D4;1110 1161 11BB; # (탔; 탔; 탔; 탔; 탔; ) HANGUL SYLLABLE TASS +D0D5;D0D5;1110 1161 11BC;D0D5;1110 1161 11BC; # (탕; 탕; 탕; 탕; 탕; ) HANGUL SYLLABLE TANG +D0D6;D0D6;1110 1161 11BD;D0D6;1110 1161 11BD; # (탖; 탖; 탖; 탖; 탖; ) HANGUL SYLLABLE TAJ +D0D7;D0D7;1110 1161 11BE;D0D7;1110 1161 11BE; # (탗; 탗; 탗; 탗; 탗; ) HANGUL SYLLABLE TAC +D0D8;D0D8;1110 1161 11BF;D0D8;1110 1161 11BF; # (탘; 탘; 탘; 탘; 탘; ) HANGUL SYLLABLE TAK +D0D9;D0D9;1110 1161 11C0;D0D9;1110 1161 11C0; # (탙; 탙; 탙; 탙; 탙; ) HANGUL SYLLABLE TAT +D0DA;D0DA;1110 1161 11C1;D0DA;1110 1161 11C1; # (탚; 탚; 탚; 탚; 탚; ) HANGUL SYLLABLE TAP +D0DB;D0DB;1110 1161 11C2;D0DB;1110 1161 11C2; # (탛; 탛; 탛; 탛; 탛; ) HANGUL SYLLABLE TAH +D0DC;D0DC;1110 1162;D0DC;1110 1162; # (태; 태; 태; 태; 태; ) HANGUL SYLLABLE TAE +D0DD;D0DD;1110 1162 11A8;D0DD;1110 1162 11A8; # (택; 택; 택; 택; 택; ) HANGUL SYLLABLE TAEG +D0DE;D0DE;1110 1162 11A9;D0DE;1110 1162 11A9; # (탞; 탞; 탞; 탞; 탞; ) HANGUL SYLLABLE TAEGG +D0DF;D0DF;1110 1162 11AA;D0DF;1110 1162 11AA; # (탟; 탟; 탟; 탟; 탟; ) HANGUL SYLLABLE TAEGS +D0E0;D0E0;1110 1162 11AB;D0E0;1110 1162 11AB; # (탠; 탠; 탠; 탠; 탠; ) HANGUL SYLLABLE TAEN +D0E1;D0E1;1110 1162 11AC;D0E1;1110 1162 11AC; # (탡; 탡; 탡; 탡; 탡; ) HANGUL SYLLABLE TAENJ +D0E2;D0E2;1110 1162 11AD;D0E2;1110 1162 11AD; # (탢; 탢; 탢; 탢; 탢; ) HANGUL SYLLABLE TAENH +D0E3;D0E3;1110 1162 11AE;D0E3;1110 1162 11AE; # (탣; 탣; 탣; 탣; 탣; ) HANGUL SYLLABLE TAED +D0E4;D0E4;1110 1162 11AF;D0E4;1110 1162 11AF; # (탤; 탤; 탤; 탤; 탤; ) HANGUL SYLLABLE TAEL +D0E5;D0E5;1110 1162 11B0;D0E5;1110 1162 11B0; # (탥; 탥; 탥; 탥; 탥; ) HANGUL SYLLABLE TAELG +D0E6;D0E6;1110 1162 11B1;D0E6;1110 1162 11B1; # (탦; 탦; 탦; 탦; 탦; ) HANGUL SYLLABLE TAELM +D0E7;D0E7;1110 1162 11B2;D0E7;1110 1162 11B2; # (탧; 탧; 탧; 탧; 탧; ) HANGUL SYLLABLE TAELB +D0E8;D0E8;1110 1162 11B3;D0E8;1110 1162 11B3; # (탨; 탨; 탨; 탨; 탨; ) HANGUL SYLLABLE TAELS +D0E9;D0E9;1110 1162 11B4;D0E9;1110 1162 11B4; # (탩; 탩; 탩; 탩; 탩; ) HANGUL SYLLABLE TAELT +D0EA;D0EA;1110 1162 11B5;D0EA;1110 1162 11B5; # (탪; 탪; 탪; 탪; 탪; ) HANGUL SYLLABLE TAELP +D0EB;D0EB;1110 1162 11B6;D0EB;1110 1162 11B6; # (탫; 탫; 탫; 탫; 탫; ) HANGUL SYLLABLE TAELH +D0EC;D0EC;1110 1162 11B7;D0EC;1110 1162 11B7; # (탬; 탬; 탬; 탬; 탬; ) HANGUL SYLLABLE TAEM +D0ED;D0ED;1110 1162 11B8;D0ED;1110 1162 11B8; # (탭; 탭; 탭; 탭; 탭; ) HANGUL SYLLABLE TAEB +D0EE;D0EE;1110 1162 11B9;D0EE;1110 1162 11B9; # (탮; 탮; 탮; 탮; 탮; ) HANGUL SYLLABLE TAEBS +D0EF;D0EF;1110 1162 11BA;D0EF;1110 1162 11BA; # (탯; 탯; 탯; 탯; 탯; ) HANGUL SYLLABLE TAES +D0F0;D0F0;1110 1162 11BB;D0F0;1110 1162 11BB; # (탰; 탰; 탰; 탰; 탰; ) HANGUL SYLLABLE TAESS +D0F1;D0F1;1110 1162 11BC;D0F1;1110 1162 11BC; # (탱; 탱; 탱; 탱; 탱; ) HANGUL SYLLABLE TAENG +D0F2;D0F2;1110 1162 11BD;D0F2;1110 1162 11BD; # (탲; 탲; 탲; 탲; 탲; ) HANGUL SYLLABLE TAEJ +D0F3;D0F3;1110 1162 11BE;D0F3;1110 1162 11BE; # (탳; 탳; 탳; 탳; 탳; ) HANGUL SYLLABLE TAEC +D0F4;D0F4;1110 1162 11BF;D0F4;1110 1162 11BF; # (탴; 탴; 탴; 탴; 탴; ) HANGUL SYLLABLE TAEK +D0F5;D0F5;1110 1162 11C0;D0F5;1110 1162 11C0; # (탵; 탵; 탵; 탵; 탵; ) HANGUL SYLLABLE TAET +D0F6;D0F6;1110 1162 11C1;D0F6;1110 1162 11C1; # (탶; 탶; 탶; 탶; 탶; ) HANGUL SYLLABLE TAEP +D0F7;D0F7;1110 1162 11C2;D0F7;1110 1162 11C2; # (탷; 탷; 탷; 탷; 탷; ) HANGUL SYLLABLE TAEH +D0F8;D0F8;1110 1163;D0F8;1110 1163; # (탸; 탸; 탸; 탸; 탸; ) HANGUL SYLLABLE TYA +D0F9;D0F9;1110 1163 11A8;D0F9;1110 1163 11A8; # (탹; 탹; 탹; 탹; 탹; ) HANGUL SYLLABLE TYAG +D0FA;D0FA;1110 1163 11A9;D0FA;1110 1163 11A9; # (탺; 탺; 탺; 탺; 탺; ) HANGUL SYLLABLE TYAGG +D0FB;D0FB;1110 1163 11AA;D0FB;1110 1163 11AA; # (탻; 탻; 탻; 탻; 탻; ) HANGUL SYLLABLE TYAGS +D0FC;D0FC;1110 1163 11AB;D0FC;1110 1163 11AB; # (탼; 탼; 탼; 탼; 탼; ) HANGUL SYLLABLE TYAN +D0FD;D0FD;1110 1163 11AC;D0FD;1110 1163 11AC; # (탽; 탽; 탽; 탽; 탽; ) HANGUL SYLLABLE TYANJ +D0FE;D0FE;1110 1163 11AD;D0FE;1110 1163 11AD; # (탾; 탾; 탾; 탾; 탾; ) HANGUL SYLLABLE TYANH +D0FF;D0FF;1110 1163 11AE;D0FF;1110 1163 11AE; # (탿; 탿; 탿; 탿; 탿; ) HANGUL SYLLABLE TYAD +D100;D100;1110 1163 11AF;D100;1110 1163 11AF; # (턀; 턀; 턀; 턀; 턀; ) HANGUL SYLLABLE TYAL +D101;D101;1110 1163 11B0;D101;1110 1163 11B0; # (턁; 턁; 턁; 턁; 턁; ) HANGUL SYLLABLE TYALG +D102;D102;1110 1163 11B1;D102;1110 1163 11B1; # (턂; 턂; 턂; 턂; 턂; ) HANGUL SYLLABLE TYALM +D103;D103;1110 1163 11B2;D103;1110 1163 11B2; # (턃; 턃; 턃; 턃; 턃; ) HANGUL SYLLABLE TYALB +D104;D104;1110 1163 11B3;D104;1110 1163 11B3; # (턄; 턄; 턄; 턄; 턄; ) HANGUL SYLLABLE TYALS +D105;D105;1110 1163 11B4;D105;1110 1163 11B4; # (턅; 턅; 턅; 턅; 턅; ) HANGUL SYLLABLE TYALT +D106;D106;1110 1163 11B5;D106;1110 1163 11B5; # (턆; 턆; 턆; 턆; 턆; ) HANGUL SYLLABLE TYALP +D107;D107;1110 1163 11B6;D107;1110 1163 11B6; # (턇; 턇; 턇; 턇; 턇; ) HANGUL SYLLABLE TYALH +D108;D108;1110 1163 11B7;D108;1110 1163 11B7; # (턈; 턈; 턈; 턈; 턈; ) HANGUL SYLLABLE TYAM +D109;D109;1110 1163 11B8;D109;1110 1163 11B8; # (턉; 턉; 턉; 턉; 턉; ) HANGUL SYLLABLE TYAB +D10A;D10A;1110 1163 11B9;D10A;1110 1163 11B9; # (턊; 턊; 턊; 턊; 턊; ) HANGUL SYLLABLE TYABS +D10B;D10B;1110 1163 11BA;D10B;1110 1163 11BA; # (턋; 턋; 턋; 턋; 턋; ) HANGUL SYLLABLE TYAS +D10C;D10C;1110 1163 11BB;D10C;1110 1163 11BB; # (턌; 턌; 턌; 턌; 턌; ) HANGUL SYLLABLE TYASS +D10D;D10D;1110 1163 11BC;D10D;1110 1163 11BC; # (턍; 턍; 턍; 턍; 턍; ) HANGUL SYLLABLE TYANG +D10E;D10E;1110 1163 11BD;D10E;1110 1163 11BD; # (턎; 턎; 턎; 턎; 턎; ) HANGUL SYLLABLE TYAJ +D10F;D10F;1110 1163 11BE;D10F;1110 1163 11BE; # (턏; 턏; 턏; 턏; 턏; ) HANGUL SYLLABLE TYAC +D110;D110;1110 1163 11BF;D110;1110 1163 11BF; # (턐; 턐; 턐; 턐; 턐; ) HANGUL SYLLABLE TYAK +D111;D111;1110 1163 11C0;D111;1110 1163 11C0; # (턑; 턑; 턑; 턑; 턑; ) HANGUL SYLLABLE TYAT +D112;D112;1110 1163 11C1;D112;1110 1163 11C1; # (턒; 턒; 턒; 턒; 턒; ) HANGUL SYLLABLE TYAP +D113;D113;1110 1163 11C2;D113;1110 1163 11C2; # (턓; 턓; 턓; 턓; 턓; ) HANGUL SYLLABLE TYAH +D114;D114;1110 1164;D114;1110 1164; # (턔; 턔; 턔; 턔; 턔; ) HANGUL SYLLABLE TYAE +D115;D115;1110 1164 11A8;D115;1110 1164 11A8; # (턕; 턕; 턕; 턕; 턕; ) HANGUL SYLLABLE TYAEG +D116;D116;1110 1164 11A9;D116;1110 1164 11A9; # (턖; 턖; 턖; 턖; 턖; ) HANGUL SYLLABLE TYAEGG +D117;D117;1110 1164 11AA;D117;1110 1164 11AA; # (턗; 턗; 턗; 턗; 턗; ) HANGUL SYLLABLE TYAEGS +D118;D118;1110 1164 11AB;D118;1110 1164 11AB; # (턘; 턘; 턘; 턘; 턘; ) HANGUL SYLLABLE TYAEN +D119;D119;1110 1164 11AC;D119;1110 1164 11AC; # (턙; 턙; 턙; 턙; 턙; ) HANGUL SYLLABLE TYAENJ +D11A;D11A;1110 1164 11AD;D11A;1110 1164 11AD; # (턚; 턚; 턚; 턚; 턚; ) HANGUL SYLLABLE TYAENH +D11B;D11B;1110 1164 11AE;D11B;1110 1164 11AE; # (턛; 턛; 턛; 턛; 턛; ) HANGUL SYLLABLE TYAED +D11C;D11C;1110 1164 11AF;D11C;1110 1164 11AF; # (턜; 턜; 턜; 턜; 턜; ) HANGUL SYLLABLE TYAEL +D11D;D11D;1110 1164 11B0;D11D;1110 1164 11B0; # (턝; 턝; 턝; 턝; 턝; ) HANGUL SYLLABLE TYAELG +D11E;D11E;1110 1164 11B1;D11E;1110 1164 11B1; # (턞; 턞; 턞; 턞; 턞; ) HANGUL SYLLABLE TYAELM +D11F;D11F;1110 1164 11B2;D11F;1110 1164 11B2; # (턟; 턟; 턟; 턟; 턟; ) HANGUL SYLLABLE TYAELB +D120;D120;1110 1164 11B3;D120;1110 1164 11B3; # (턠; 턠; 턠; 턠; 턠; ) HANGUL SYLLABLE TYAELS +D121;D121;1110 1164 11B4;D121;1110 1164 11B4; # (턡; 턡; 턡; 턡; 턡; ) HANGUL SYLLABLE TYAELT +D122;D122;1110 1164 11B5;D122;1110 1164 11B5; # (턢; 턢; 턢; 턢; 턢; ) HANGUL SYLLABLE TYAELP +D123;D123;1110 1164 11B6;D123;1110 1164 11B6; # (턣; 턣; 턣; 턣; 턣; ) HANGUL SYLLABLE TYAELH +D124;D124;1110 1164 11B7;D124;1110 1164 11B7; # (턤; 턤; 턤; 턤; 턤; ) HANGUL SYLLABLE TYAEM +D125;D125;1110 1164 11B8;D125;1110 1164 11B8; # (턥; 턥; 턥; 턥; 턥; ) HANGUL SYLLABLE TYAEB +D126;D126;1110 1164 11B9;D126;1110 1164 11B9; # (턦; 턦; 턦; 턦; 턦; ) HANGUL SYLLABLE TYAEBS +D127;D127;1110 1164 11BA;D127;1110 1164 11BA; # (턧; 턧; 턧; 턧; 턧; ) HANGUL SYLLABLE TYAES +D128;D128;1110 1164 11BB;D128;1110 1164 11BB; # (턨; 턨; 턨; 턨; 턨; ) HANGUL SYLLABLE TYAESS +D129;D129;1110 1164 11BC;D129;1110 1164 11BC; # (턩; 턩; 턩; 턩; 턩; ) HANGUL SYLLABLE TYAENG +D12A;D12A;1110 1164 11BD;D12A;1110 1164 11BD; # (턪; 턪; 턪; 턪; 턪; ) HANGUL SYLLABLE TYAEJ +D12B;D12B;1110 1164 11BE;D12B;1110 1164 11BE; # (턫; 턫; 턫; 턫; 턫; ) HANGUL SYLLABLE TYAEC +D12C;D12C;1110 1164 11BF;D12C;1110 1164 11BF; # (턬; 턬; 턬; 턬; 턬; ) HANGUL SYLLABLE TYAEK +D12D;D12D;1110 1164 11C0;D12D;1110 1164 11C0; # (턭; 턭; 턭; 턭; 턭; ) HANGUL SYLLABLE TYAET +D12E;D12E;1110 1164 11C1;D12E;1110 1164 11C1; # (턮; 턮; 턮; 턮; 턮; ) HANGUL SYLLABLE TYAEP +D12F;D12F;1110 1164 11C2;D12F;1110 1164 11C2; # (턯; 턯; 턯; 턯; 턯; ) HANGUL SYLLABLE TYAEH +D130;D130;1110 1165;D130;1110 1165; # (터; 터; 터; 터; 터; ) HANGUL SYLLABLE TEO +D131;D131;1110 1165 11A8;D131;1110 1165 11A8; # (턱; 턱; 턱; 턱; 턱; ) HANGUL SYLLABLE TEOG +D132;D132;1110 1165 11A9;D132;1110 1165 11A9; # (턲; 턲; 턲; 턲; 턲; ) HANGUL SYLLABLE TEOGG +D133;D133;1110 1165 11AA;D133;1110 1165 11AA; # (턳; 턳; 턳; 턳; 턳; ) HANGUL SYLLABLE TEOGS +D134;D134;1110 1165 11AB;D134;1110 1165 11AB; # (턴; 턴; 턴; 턴; 턴; ) HANGUL SYLLABLE TEON +D135;D135;1110 1165 11AC;D135;1110 1165 11AC; # (턵; 턵; 턵; 턵; 턵; ) HANGUL SYLLABLE TEONJ +D136;D136;1110 1165 11AD;D136;1110 1165 11AD; # (턶; 턶; 턶; 턶; 턶; ) HANGUL SYLLABLE TEONH +D137;D137;1110 1165 11AE;D137;1110 1165 11AE; # (턷; 턷; 턷; 턷; 턷; ) HANGUL SYLLABLE TEOD +D138;D138;1110 1165 11AF;D138;1110 1165 11AF; # (털; 털; 털; 털; 털; ) HANGUL SYLLABLE TEOL +D139;D139;1110 1165 11B0;D139;1110 1165 11B0; # (턹; 턹; 턹; 턹; 턹; ) HANGUL SYLLABLE TEOLG +D13A;D13A;1110 1165 11B1;D13A;1110 1165 11B1; # (턺; 턺; 턺; 턺; 턺; ) HANGUL SYLLABLE TEOLM +D13B;D13B;1110 1165 11B2;D13B;1110 1165 11B2; # (턻; 턻; 턻; 턻; 턻; ) HANGUL SYLLABLE TEOLB +D13C;D13C;1110 1165 11B3;D13C;1110 1165 11B3; # (턼; 턼; 턼; 턼; 턼; ) HANGUL SYLLABLE TEOLS +D13D;D13D;1110 1165 11B4;D13D;1110 1165 11B4; # (턽; 턽; 턽; 턽; 턽; ) HANGUL SYLLABLE TEOLT +D13E;D13E;1110 1165 11B5;D13E;1110 1165 11B5; # (턾; 턾; 턾; 턾; 턾; ) HANGUL SYLLABLE TEOLP +D13F;D13F;1110 1165 11B6;D13F;1110 1165 11B6; # (턿; 턿; 턿; 턿; 턿; ) HANGUL SYLLABLE TEOLH +D140;D140;1110 1165 11B7;D140;1110 1165 11B7; # (텀; 텀; 텀; 텀; 텀; ) HANGUL SYLLABLE TEOM +D141;D141;1110 1165 11B8;D141;1110 1165 11B8; # (텁; 텁; 텁; 텁; 텁; ) HANGUL SYLLABLE TEOB +D142;D142;1110 1165 11B9;D142;1110 1165 11B9; # (텂; 텂; 텂; 텂; 텂; ) HANGUL SYLLABLE TEOBS +D143;D143;1110 1165 11BA;D143;1110 1165 11BA; # (텃; 텃; 텃; 텃; 텃; ) HANGUL SYLLABLE TEOS +D144;D144;1110 1165 11BB;D144;1110 1165 11BB; # (텄; 텄; 텄; 텄; 텄; ) HANGUL SYLLABLE TEOSS +D145;D145;1110 1165 11BC;D145;1110 1165 11BC; # (텅; 텅; 텅; 텅; 텅; ) HANGUL SYLLABLE TEONG +D146;D146;1110 1165 11BD;D146;1110 1165 11BD; # (텆; 텆; 텆; 텆; 텆; ) HANGUL SYLLABLE TEOJ +D147;D147;1110 1165 11BE;D147;1110 1165 11BE; # (텇; 텇; 텇; 텇; 텇; ) HANGUL SYLLABLE TEOC +D148;D148;1110 1165 11BF;D148;1110 1165 11BF; # (텈; 텈; 텈; 텈; 텈; ) HANGUL SYLLABLE TEOK +D149;D149;1110 1165 11C0;D149;1110 1165 11C0; # (텉; 텉; 텉; 텉; 텉; ) HANGUL SYLLABLE TEOT +D14A;D14A;1110 1165 11C1;D14A;1110 1165 11C1; # (텊; 텊; 텊; 텊; 텊; ) HANGUL SYLLABLE TEOP +D14B;D14B;1110 1165 11C2;D14B;1110 1165 11C2; # (텋; 텋; 텋; 텋; 텋; ) HANGUL SYLLABLE TEOH +D14C;D14C;1110 1166;D14C;1110 1166; # (테; 테; 테; 테; 테; ) HANGUL SYLLABLE TE +D14D;D14D;1110 1166 11A8;D14D;1110 1166 11A8; # (텍; 텍; 텍; 텍; 텍; ) HANGUL SYLLABLE TEG +D14E;D14E;1110 1166 11A9;D14E;1110 1166 11A9; # (텎; 텎; 텎; 텎; 텎; ) HANGUL SYLLABLE TEGG +D14F;D14F;1110 1166 11AA;D14F;1110 1166 11AA; # (텏; 텏; 텏; 텏; 텏; ) HANGUL SYLLABLE TEGS +D150;D150;1110 1166 11AB;D150;1110 1166 11AB; # (텐; 텐; 텐; 텐; 텐; ) HANGUL SYLLABLE TEN +D151;D151;1110 1166 11AC;D151;1110 1166 11AC; # (텑; 텑; 텑; 텑; 텑; ) HANGUL SYLLABLE TENJ +D152;D152;1110 1166 11AD;D152;1110 1166 11AD; # (텒; 텒; 텒; 텒; 텒; ) HANGUL SYLLABLE TENH +D153;D153;1110 1166 11AE;D153;1110 1166 11AE; # (텓; 텓; 텓; 텓; 텓; ) HANGUL SYLLABLE TED +D154;D154;1110 1166 11AF;D154;1110 1166 11AF; # (텔; 텔; 텔; 텔; 텔; ) HANGUL SYLLABLE TEL +D155;D155;1110 1166 11B0;D155;1110 1166 11B0; # (텕; 텕; 텕; 텕; 텕; ) HANGUL SYLLABLE TELG +D156;D156;1110 1166 11B1;D156;1110 1166 11B1; # (텖; 텖; 텖; 텖; 텖; ) HANGUL SYLLABLE TELM +D157;D157;1110 1166 11B2;D157;1110 1166 11B2; # (텗; 텗; 텗; 텗; 텗; ) HANGUL SYLLABLE TELB +D158;D158;1110 1166 11B3;D158;1110 1166 11B3; # (텘; 텘; 텘; 텘; 텘; ) HANGUL SYLLABLE TELS +D159;D159;1110 1166 11B4;D159;1110 1166 11B4; # (텙; 텙; 텙; 텙; 텙; ) HANGUL SYLLABLE TELT +D15A;D15A;1110 1166 11B5;D15A;1110 1166 11B5; # (텚; 텚; 텚; 텚; 텚; ) HANGUL SYLLABLE TELP +D15B;D15B;1110 1166 11B6;D15B;1110 1166 11B6; # (텛; 텛; 텛; 텛; 텛; ) HANGUL SYLLABLE TELH +D15C;D15C;1110 1166 11B7;D15C;1110 1166 11B7; # (템; 템; 템; 템; 템; ) HANGUL SYLLABLE TEM +D15D;D15D;1110 1166 11B8;D15D;1110 1166 11B8; # (텝; 텝; 텝; 텝; 텝; ) HANGUL SYLLABLE TEB +D15E;D15E;1110 1166 11B9;D15E;1110 1166 11B9; # (텞; 텞; 텞; 텞; 텞; ) HANGUL SYLLABLE TEBS +D15F;D15F;1110 1166 11BA;D15F;1110 1166 11BA; # (텟; 텟; 텟; 텟; 텟; ) HANGUL SYLLABLE TES +D160;D160;1110 1166 11BB;D160;1110 1166 11BB; # (텠; 텠; 텠; 텠; 텠; ) HANGUL SYLLABLE TESS +D161;D161;1110 1166 11BC;D161;1110 1166 11BC; # (텡; 텡; 텡; 텡; 텡; ) HANGUL SYLLABLE TENG +D162;D162;1110 1166 11BD;D162;1110 1166 11BD; # (텢; 텢; 텢; 텢; 텢; ) HANGUL SYLLABLE TEJ +D163;D163;1110 1166 11BE;D163;1110 1166 11BE; # (텣; 텣; 텣; 텣; 텣; ) HANGUL SYLLABLE TEC +D164;D164;1110 1166 11BF;D164;1110 1166 11BF; # (텤; 텤; 텤; 텤; 텤; ) HANGUL SYLLABLE TEK +D165;D165;1110 1166 11C0;D165;1110 1166 11C0; # (텥; 텥; 텥; 텥; 텥; ) HANGUL SYLLABLE TET +D166;D166;1110 1166 11C1;D166;1110 1166 11C1; # (텦; 텦; 텦; 텦; 텦; ) HANGUL SYLLABLE TEP +D167;D167;1110 1166 11C2;D167;1110 1166 11C2; # (텧; 텧; 텧; 텧; 텧; ) HANGUL SYLLABLE TEH +D168;D168;1110 1167;D168;1110 1167; # (텨; 텨; 텨; 텨; 텨; ) HANGUL SYLLABLE TYEO +D169;D169;1110 1167 11A8;D169;1110 1167 11A8; # (텩; 텩; 텩; 텩; 텩; ) HANGUL SYLLABLE TYEOG +D16A;D16A;1110 1167 11A9;D16A;1110 1167 11A9; # (텪; 텪; 텪; 텪; 텪; ) HANGUL SYLLABLE TYEOGG +D16B;D16B;1110 1167 11AA;D16B;1110 1167 11AA; # (텫; 텫; 텫; 텫; 텫; ) HANGUL SYLLABLE TYEOGS +D16C;D16C;1110 1167 11AB;D16C;1110 1167 11AB; # (텬; 텬; 텬; 텬; 텬; ) HANGUL SYLLABLE TYEON +D16D;D16D;1110 1167 11AC;D16D;1110 1167 11AC; # (텭; 텭; 텭; 텭; 텭; ) HANGUL SYLLABLE TYEONJ +D16E;D16E;1110 1167 11AD;D16E;1110 1167 11AD; # (텮; 텮; 텮; 텮; 텮; ) HANGUL SYLLABLE TYEONH +D16F;D16F;1110 1167 11AE;D16F;1110 1167 11AE; # (텯; 텯; 텯; 텯; 텯; ) HANGUL SYLLABLE TYEOD +D170;D170;1110 1167 11AF;D170;1110 1167 11AF; # (텰; 텰; 텰; 텰; 텰; ) HANGUL SYLLABLE TYEOL +D171;D171;1110 1167 11B0;D171;1110 1167 11B0; # (텱; 텱; 텱; 텱; 텱; ) HANGUL SYLLABLE TYEOLG +D172;D172;1110 1167 11B1;D172;1110 1167 11B1; # (텲; 텲; 텲; 텲; 텲; ) HANGUL SYLLABLE TYEOLM +D173;D173;1110 1167 11B2;D173;1110 1167 11B2; # (텳; 텳; 텳; 텳; 텳; ) HANGUL SYLLABLE TYEOLB +D174;D174;1110 1167 11B3;D174;1110 1167 11B3; # (텴; 텴; 텴; 텴; 텴; ) HANGUL SYLLABLE TYEOLS +D175;D175;1110 1167 11B4;D175;1110 1167 11B4; # (텵; 텵; 텵; 텵; 텵; ) HANGUL SYLLABLE TYEOLT +D176;D176;1110 1167 11B5;D176;1110 1167 11B5; # (텶; 텶; 텶; 텶; 텶; ) HANGUL SYLLABLE TYEOLP +D177;D177;1110 1167 11B6;D177;1110 1167 11B6; # (텷; 텷; 텷; 텷; 텷; ) HANGUL SYLLABLE TYEOLH +D178;D178;1110 1167 11B7;D178;1110 1167 11B7; # (텸; 텸; 텸; 텸; 텸; ) HANGUL SYLLABLE TYEOM +D179;D179;1110 1167 11B8;D179;1110 1167 11B8; # (텹; 텹; 텹; 텹; 텹; ) HANGUL SYLLABLE TYEOB +D17A;D17A;1110 1167 11B9;D17A;1110 1167 11B9; # (텺; 텺; 텺; 텺; 텺; ) HANGUL SYLLABLE TYEOBS +D17B;D17B;1110 1167 11BA;D17B;1110 1167 11BA; # (텻; 텻; 텻; 텻; 텻; ) HANGUL SYLLABLE TYEOS +D17C;D17C;1110 1167 11BB;D17C;1110 1167 11BB; # (텼; 텼; 텼; 텼; 텼; ) HANGUL SYLLABLE TYEOSS +D17D;D17D;1110 1167 11BC;D17D;1110 1167 11BC; # (텽; 텽; 텽; 텽; 텽; ) HANGUL SYLLABLE TYEONG +D17E;D17E;1110 1167 11BD;D17E;1110 1167 11BD; # (텾; 텾; 텾; 텾; 텾; ) HANGUL SYLLABLE TYEOJ +D17F;D17F;1110 1167 11BE;D17F;1110 1167 11BE; # (텿; 텿; 텿; 텿; 텿; ) HANGUL SYLLABLE TYEOC +D180;D180;1110 1167 11BF;D180;1110 1167 11BF; # (톀; 톀; 톀; 톀; 톀; ) HANGUL SYLLABLE TYEOK +D181;D181;1110 1167 11C0;D181;1110 1167 11C0; # (톁; 톁; 톁; 톁; 톁; ) HANGUL SYLLABLE TYEOT +D182;D182;1110 1167 11C1;D182;1110 1167 11C1; # (톂; 톂; 톂; 톂; 톂; ) HANGUL SYLLABLE TYEOP +D183;D183;1110 1167 11C2;D183;1110 1167 11C2; # (톃; 톃; 톃; 톃; 톃; ) HANGUL SYLLABLE TYEOH +D184;D184;1110 1168;D184;1110 1168; # (톄; 톄; 톄; 톄; 톄; ) HANGUL SYLLABLE TYE +D185;D185;1110 1168 11A8;D185;1110 1168 11A8; # (톅; 톅; 톅; 톅; 톅; ) HANGUL SYLLABLE TYEG +D186;D186;1110 1168 11A9;D186;1110 1168 11A9; # (톆; 톆; 톆; 톆; 톆; ) HANGUL SYLLABLE TYEGG +D187;D187;1110 1168 11AA;D187;1110 1168 11AA; # (톇; 톇; 톇; 톇; 톇; ) HANGUL SYLLABLE TYEGS +D188;D188;1110 1168 11AB;D188;1110 1168 11AB; # (톈; 톈; 톈; 톈; 톈; ) HANGUL SYLLABLE TYEN +D189;D189;1110 1168 11AC;D189;1110 1168 11AC; # (톉; 톉; 톉; 톉; 톉; ) HANGUL SYLLABLE TYENJ +D18A;D18A;1110 1168 11AD;D18A;1110 1168 11AD; # (톊; 톊; 톊; 톊; 톊; ) HANGUL SYLLABLE TYENH +D18B;D18B;1110 1168 11AE;D18B;1110 1168 11AE; # (톋; 톋; 톋; 톋; 톋; ) HANGUL SYLLABLE TYED +D18C;D18C;1110 1168 11AF;D18C;1110 1168 11AF; # (톌; 톌; 톌; 톌; 톌; ) HANGUL SYLLABLE TYEL +D18D;D18D;1110 1168 11B0;D18D;1110 1168 11B0; # (톍; 톍; 톍; 톍; 톍; ) HANGUL SYLLABLE TYELG +D18E;D18E;1110 1168 11B1;D18E;1110 1168 11B1; # (톎; 톎; 톎; 톎; 톎; ) HANGUL SYLLABLE TYELM +D18F;D18F;1110 1168 11B2;D18F;1110 1168 11B2; # (톏; 톏; 톏; 톏; 톏; ) HANGUL SYLLABLE TYELB +D190;D190;1110 1168 11B3;D190;1110 1168 11B3; # (톐; 톐; 톐; 톐; 톐; ) HANGUL SYLLABLE TYELS +D191;D191;1110 1168 11B4;D191;1110 1168 11B4; # (톑; 톑; 톑; 톑; 톑; ) HANGUL SYLLABLE TYELT +D192;D192;1110 1168 11B5;D192;1110 1168 11B5; # (톒; 톒; 톒; 톒; 톒; ) HANGUL SYLLABLE TYELP +D193;D193;1110 1168 11B6;D193;1110 1168 11B6; # (톓; 톓; 톓; 톓; 톓; ) HANGUL SYLLABLE TYELH +D194;D194;1110 1168 11B7;D194;1110 1168 11B7; # (톔; 톔; 톔; 톔; 톔; ) HANGUL SYLLABLE TYEM +D195;D195;1110 1168 11B8;D195;1110 1168 11B8; # (톕; 톕; 톕; 톕; 톕; ) HANGUL SYLLABLE TYEB +D196;D196;1110 1168 11B9;D196;1110 1168 11B9; # (톖; 톖; 톖; 톖; 톖; ) HANGUL SYLLABLE TYEBS +D197;D197;1110 1168 11BA;D197;1110 1168 11BA; # (톗; 톗; 톗; 톗; 톗; ) HANGUL SYLLABLE TYES +D198;D198;1110 1168 11BB;D198;1110 1168 11BB; # (톘; 톘; 톘; 톘; 톘; ) HANGUL SYLLABLE TYESS +D199;D199;1110 1168 11BC;D199;1110 1168 11BC; # (톙; 톙; 톙; 톙; 톙; ) HANGUL SYLLABLE TYENG +D19A;D19A;1110 1168 11BD;D19A;1110 1168 11BD; # (톚; 톚; 톚; 톚; 톚; ) HANGUL SYLLABLE TYEJ +D19B;D19B;1110 1168 11BE;D19B;1110 1168 11BE; # (톛; 톛; 톛; 톛; 톛; ) HANGUL SYLLABLE TYEC +D19C;D19C;1110 1168 11BF;D19C;1110 1168 11BF; # (톜; 톜; 톜; 톜; 톜; ) HANGUL SYLLABLE TYEK +D19D;D19D;1110 1168 11C0;D19D;1110 1168 11C0; # (톝; 톝; 톝; 톝; 톝; ) HANGUL SYLLABLE TYET +D19E;D19E;1110 1168 11C1;D19E;1110 1168 11C1; # (톞; 톞; 톞; 톞; 톞; ) HANGUL SYLLABLE TYEP +D19F;D19F;1110 1168 11C2;D19F;1110 1168 11C2; # (톟; 톟; 톟; 톟; 톟; ) HANGUL SYLLABLE TYEH +D1A0;D1A0;1110 1169;D1A0;1110 1169; # (토; 토; 토; 토; 토; ) HANGUL SYLLABLE TO +D1A1;D1A1;1110 1169 11A8;D1A1;1110 1169 11A8; # (톡; 톡; 톡; 톡; 톡; ) HANGUL SYLLABLE TOG +D1A2;D1A2;1110 1169 11A9;D1A2;1110 1169 11A9; # (톢; 톢; 톢; 톢; 톢; ) HANGUL SYLLABLE TOGG +D1A3;D1A3;1110 1169 11AA;D1A3;1110 1169 11AA; # (톣; 톣; 톣; 톣; 톣; ) HANGUL SYLLABLE TOGS +D1A4;D1A4;1110 1169 11AB;D1A4;1110 1169 11AB; # (톤; 톤; 톤; 톤; 톤; ) HANGUL SYLLABLE TON +D1A5;D1A5;1110 1169 11AC;D1A5;1110 1169 11AC; # (톥; 톥; 톥; 톥; 톥; ) HANGUL SYLLABLE TONJ +D1A6;D1A6;1110 1169 11AD;D1A6;1110 1169 11AD; # (톦; 톦; 톦; 톦; 톦; ) HANGUL SYLLABLE TONH +D1A7;D1A7;1110 1169 11AE;D1A7;1110 1169 11AE; # (톧; 톧; 톧; 톧; 톧; ) HANGUL SYLLABLE TOD +D1A8;D1A8;1110 1169 11AF;D1A8;1110 1169 11AF; # (톨; 톨; 톨; 톨; 톨; ) HANGUL SYLLABLE TOL +D1A9;D1A9;1110 1169 11B0;D1A9;1110 1169 11B0; # (톩; 톩; 톩; 톩; 톩; ) HANGUL SYLLABLE TOLG +D1AA;D1AA;1110 1169 11B1;D1AA;1110 1169 11B1; # (톪; 톪; 톪; 톪; 톪; ) HANGUL SYLLABLE TOLM +D1AB;D1AB;1110 1169 11B2;D1AB;1110 1169 11B2; # (톫; 톫; 톫; 톫; 톫; ) HANGUL SYLLABLE TOLB +D1AC;D1AC;1110 1169 11B3;D1AC;1110 1169 11B3; # (톬; 톬; 톬; 톬; 톬; ) HANGUL SYLLABLE TOLS +D1AD;D1AD;1110 1169 11B4;D1AD;1110 1169 11B4; # (톭; 톭; 톭; 톭; 톭; ) HANGUL SYLLABLE TOLT +D1AE;D1AE;1110 1169 11B5;D1AE;1110 1169 11B5; # (톮; 톮; 톮; 톮; 톮; ) HANGUL SYLLABLE TOLP +D1AF;D1AF;1110 1169 11B6;D1AF;1110 1169 11B6; # (톯; 톯; 톯; 톯; 톯; ) HANGUL SYLLABLE TOLH +D1B0;D1B0;1110 1169 11B7;D1B0;1110 1169 11B7; # (톰; 톰; 톰; 톰; 톰; ) HANGUL SYLLABLE TOM +D1B1;D1B1;1110 1169 11B8;D1B1;1110 1169 11B8; # (톱; 톱; 톱; 톱; 톱; ) HANGUL SYLLABLE TOB +D1B2;D1B2;1110 1169 11B9;D1B2;1110 1169 11B9; # (톲; 톲; 톲; 톲; 톲; ) HANGUL SYLLABLE TOBS +D1B3;D1B3;1110 1169 11BA;D1B3;1110 1169 11BA; # (톳; 톳; 톳; 톳; 톳; ) HANGUL SYLLABLE TOS +D1B4;D1B4;1110 1169 11BB;D1B4;1110 1169 11BB; # (톴; 톴; 톴; 톴; 톴; ) HANGUL SYLLABLE TOSS +D1B5;D1B5;1110 1169 11BC;D1B5;1110 1169 11BC; # (통; 통; 통; 통; 통; ) HANGUL SYLLABLE TONG +D1B6;D1B6;1110 1169 11BD;D1B6;1110 1169 11BD; # (톶; 톶; 톶; 톶; 톶; ) HANGUL SYLLABLE TOJ +D1B7;D1B7;1110 1169 11BE;D1B7;1110 1169 11BE; # (톷; 톷; 톷; 톷; 톷; ) HANGUL SYLLABLE TOC +D1B8;D1B8;1110 1169 11BF;D1B8;1110 1169 11BF; # (톸; 톸; 톸; 톸; 톸; ) HANGUL SYLLABLE TOK +D1B9;D1B9;1110 1169 11C0;D1B9;1110 1169 11C0; # (톹; 톹; 톹; 톹; 톹; ) HANGUL SYLLABLE TOT +D1BA;D1BA;1110 1169 11C1;D1BA;1110 1169 11C1; # (톺; 톺; 톺; 톺; 톺; ) HANGUL SYLLABLE TOP +D1BB;D1BB;1110 1169 11C2;D1BB;1110 1169 11C2; # (톻; 톻; 톻; 톻; 톻; ) HANGUL SYLLABLE TOH +D1BC;D1BC;1110 116A;D1BC;1110 116A; # (톼; 톼; 톼; 톼; 톼; ) HANGUL SYLLABLE TWA +D1BD;D1BD;1110 116A 11A8;D1BD;1110 116A 11A8; # (톽; 톽; 톽; 톽; 톽; ) HANGUL SYLLABLE TWAG +D1BE;D1BE;1110 116A 11A9;D1BE;1110 116A 11A9; # (톾; 톾; 톾; 톾; 톾; ) HANGUL SYLLABLE TWAGG +D1BF;D1BF;1110 116A 11AA;D1BF;1110 116A 11AA; # (톿; 톿; 톿; 톿; 톿; ) HANGUL SYLLABLE TWAGS +D1C0;D1C0;1110 116A 11AB;D1C0;1110 116A 11AB; # (퇀; 퇀; 퇀; 퇀; 퇀; ) HANGUL SYLLABLE TWAN +D1C1;D1C1;1110 116A 11AC;D1C1;1110 116A 11AC; # (퇁; 퇁; 퇁; 퇁; 퇁; ) HANGUL SYLLABLE TWANJ +D1C2;D1C2;1110 116A 11AD;D1C2;1110 116A 11AD; # (퇂; 퇂; 퇂; 퇂; 퇂; ) HANGUL SYLLABLE TWANH +D1C3;D1C3;1110 116A 11AE;D1C3;1110 116A 11AE; # (퇃; 퇃; 퇃; 퇃; 퇃; ) HANGUL SYLLABLE TWAD +D1C4;D1C4;1110 116A 11AF;D1C4;1110 116A 11AF; # (퇄; 퇄; 퇄; 퇄; 퇄; ) HANGUL SYLLABLE TWAL +D1C5;D1C5;1110 116A 11B0;D1C5;1110 116A 11B0; # (퇅; 퇅; 퇅; 퇅; 퇅; ) HANGUL SYLLABLE TWALG +D1C6;D1C6;1110 116A 11B1;D1C6;1110 116A 11B1; # (퇆; 퇆; 퇆; 퇆; 퇆; ) HANGUL SYLLABLE TWALM +D1C7;D1C7;1110 116A 11B2;D1C7;1110 116A 11B2; # (퇇; 퇇; 퇇; 퇇; 퇇; ) HANGUL SYLLABLE TWALB +D1C8;D1C8;1110 116A 11B3;D1C8;1110 116A 11B3; # (퇈; 퇈; 퇈; 퇈; 퇈; ) HANGUL SYLLABLE TWALS +D1C9;D1C9;1110 116A 11B4;D1C9;1110 116A 11B4; # (퇉; 퇉; 퇉; 퇉; 퇉; ) HANGUL SYLLABLE TWALT +D1CA;D1CA;1110 116A 11B5;D1CA;1110 116A 11B5; # (퇊; 퇊; 퇊; 퇊; 퇊; ) HANGUL SYLLABLE TWALP +D1CB;D1CB;1110 116A 11B6;D1CB;1110 116A 11B6; # (퇋; 퇋; 퇋; 퇋; 퇋; ) HANGUL SYLLABLE TWALH +D1CC;D1CC;1110 116A 11B7;D1CC;1110 116A 11B7; # (퇌; 퇌; 퇌; 퇌; 퇌; ) HANGUL SYLLABLE TWAM +D1CD;D1CD;1110 116A 11B8;D1CD;1110 116A 11B8; # (퇍; 퇍; 퇍; 퇍; 퇍; ) HANGUL SYLLABLE TWAB +D1CE;D1CE;1110 116A 11B9;D1CE;1110 116A 11B9; # (퇎; 퇎; 퇎; 퇎; 퇎; ) HANGUL SYLLABLE TWABS +D1CF;D1CF;1110 116A 11BA;D1CF;1110 116A 11BA; # (퇏; 퇏; 퇏; 퇏; 퇏; ) HANGUL SYLLABLE TWAS +D1D0;D1D0;1110 116A 11BB;D1D0;1110 116A 11BB; # (퇐; 퇐; 퇐; 퇐; 퇐; ) HANGUL SYLLABLE TWASS +D1D1;D1D1;1110 116A 11BC;D1D1;1110 116A 11BC; # (퇑; 퇑; 퇑; 퇑; 퇑; ) HANGUL SYLLABLE TWANG +D1D2;D1D2;1110 116A 11BD;D1D2;1110 116A 11BD; # (퇒; 퇒; 퇒; 퇒; 퇒; ) HANGUL SYLLABLE TWAJ +D1D3;D1D3;1110 116A 11BE;D1D3;1110 116A 11BE; # (퇓; 퇓; 퇓; 퇓; 퇓; ) HANGUL SYLLABLE TWAC +D1D4;D1D4;1110 116A 11BF;D1D4;1110 116A 11BF; # (퇔; 퇔; 퇔; 퇔; 퇔; ) HANGUL SYLLABLE TWAK +D1D5;D1D5;1110 116A 11C0;D1D5;1110 116A 11C0; # (퇕; 퇕; 퇕; 퇕; 퇕; ) HANGUL SYLLABLE TWAT +D1D6;D1D6;1110 116A 11C1;D1D6;1110 116A 11C1; # (퇖; 퇖; 퇖; 퇖; 퇖; ) HANGUL SYLLABLE TWAP +D1D7;D1D7;1110 116A 11C2;D1D7;1110 116A 11C2; # (퇗; 퇗; 퇗; 퇗; 퇗; ) HANGUL SYLLABLE TWAH +D1D8;D1D8;1110 116B;D1D8;1110 116B; # (퇘; 퇘; 퇘; 퇘; 퇘; ) HANGUL SYLLABLE TWAE +D1D9;D1D9;1110 116B 11A8;D1D9;1110 116B 11A8; # (퇙; 퇙; 퇙; 퇙; 퇙; ) HANGUL SYLLABLE TWAEG +D1DA;D1DA;1110 116B 11A9;D1DA;1110 116B 11A9; # (퇚; 퇚; 퇚; 퇚; 퇚; ) HANGUL SYLLABLE TWAEGG +D1DB;D1DB;1110 116B 11AA;D1DB;1110 116B 11AA; # (퇛; 퇛; 퇛; 퇛; 퇛; ) HANGUL SYLLABLE TWAEGS +D1DC;D1DC;1110 116B 11AB;D1DC;1110 116B 11AB; # (퇜; 퇜; 퇜; 퇜; 퇜; ) HANGUL SYLLABLE TWAEN +D1DD;D1DD;1110 116B 11AC;D1DD;1110 116B 11AC; # (퇝; 퇝; 퇝; 퇝; 퇝; ) HANGUL SYLLABLE TWAENJ +D1DE;D1DE;1110 116B 11AD;D1DE;1110 116B 11AD; # (퇞; 퇞; 퇞; 퇞; 퇞; ) HANGUL SYLLABLE TWAENH +D1DF;D1DF;1110 116B 11AE;D1DF;1110 116B 11AE; # (퇟; 퇟; 퇟; 퇟; 퇟; ) HANGUL SYLLABLE TWAED +D1E0;D1E0;1110 116B 11AF;D1E0;1110 116B 11AF; # (퇠; 퇠; 퇠; 퇠; 퇠; ) HANGUL SYLLABLE TWAEL +D1E1;D1E1;1110 116B 11B0;D1E1;1110 116B 11B0; # (퇡; 퇡; 퇡; 퇡; 퇡; ) HANGUL SYLLABLE TWAELG +D1E2;D1E2;1110 116B 11B1;D1E2;1110 116B 11B1; # (퇢; 퇢; 퇢; 퇢; 퇢; ) HANGUL SYLLABLE TWAELM +D1E3;D1E3;1110 116B 11B2;D1E3;1110 116B 11B2; # (퇣; 퇣; 퇣; 퇣; 퇣; ) HANGUL SYLLABLE TWAELB +D1E4;D1E4;1110 116B 11B3;D1E4;1110 116B 11B3; # (퇤; 퇤; 퇤; 퇤; 퇤; ) HANGUL SYLLABLE TWAELS +D1E5;D1E5;1110 116B 11B4;D1E5;1110 116B 11B4; # (퇥; 퇥; 퇥; 퇥; 퇥; ) HANGUL SYLLABLE TWAELT +D1E6;D1E6;1110 116B 11B5;D1E6;1110 116B 11B5; # (퇦; 퇦; 퇦; 퇦; 퇦; ) HANGUL SYLLABLE TWAELP +D1E7;D1E7;1110 116B 11B6;D1E7;1110 116B 11B6; # (퇧; 퇧; 퇧; 퇧; 퇧; ) HANGUL SYLLABLE TWAELH +D1E8;D1E8;1110 116B 11B7;D1E8;1110 116B 11B7; # (퇨; 퇨; 퇨; 퇨; 퇨; ) HANGUL SYLLABLE TWAEM +D1E9;D1E9;1110 116B 11B8;D1E9;1110 116B 11B8; # (퇩; 퇩; 퇩; 퇩; 퇩; ) HANGUL SYLLABLE TWAEB +D1EA;D1EA;1110 116B 11B9;D1EA;1110 116B 11B9; # (퇪; 퇪; 퇪; 퇪; 퇪; ) HANGUL SYLLABLE TWAEBS +D1EB;D1EB;1110 116B 11BA;D1EB;1110 116B 11BA; # (퇫; 퇫; 퇫; 퇫; 퇫; ) HANGUL SYLLABLE TWAES +D1EC;D1EC;1110 116B 11BB;D1EC;1110 116B 11BB; # (퇬; 퇬; 퇬; 퇬; 퇬; ) HANGUL SYLLABLE TWAESS +D1ED;D1ED;1110 116B 11BC;D1ED;1110 116B 11BC; # (퇭; 퇭; 퇭; 퇭; 퇭; ) HANGUL SYLLABLE TWAENG +D1EE;D1EE;1110 116B 11BD;D1EE;1110 116B 11BD; # (퇮; 퇮; 퇮; 퇮; 퇮; ) HANGUL SYLLABLE TWAEJ +D1EF;D1EF;1110 116B 11BE;D1EF;1110 116B 11BE; # (퇯; 퇯; 퇯; 퇯; 퇯; ) HANGUL SYLLABLE TWAEC +D1F0;D1F0;1110 116B 11BF;D1F0;1110 116B 11BF; # (퇰; 퇰; 퇰; 퇰; 퇰; ) HANGUL SYLLABLE TWAEK +D1F1;D1F1;1110 116B 11C0;D1F1;1110 116B 11C0; # (퇱; 퇱; 퇱; 퇱; 퇱; ) HANGUL SYLLABLE TWAET +D1F2;D1F2;1110 116B 11C1;D1F2;1110 116B 11C1; # (퇲; 퇲; 퇲; 퇲; 퇲; ) HANGUL SYLLABLE TWAEP +D1F3;D1F3;1110 116B 11C2;D1F3;1110 116B 11C2; # (퇳; 퇳; 퇳; 퇳; 퇳; ) HANGUL SYLLABLE TWAEH +D1F4;D1F4;1110 116C;D1F4;1110 116C; # (퇴; 퇴; 퇴; 퇴; 퇴; ) HANGUL SYLLABLE TOE +D1F5;D1F5;1110 116C 11A8;D1F5;1110 116C 11A8; # (퇵; 퇵; 퇵; 퇵; 퇵; ) HANGUL SYLLABLE TOEG +D1F6;D1F6;1110 116C 11A9;D1F6;1110 116C 11A9; # (퇶; 퇶; 퇶; 퇶; 퇶; ) HANGUL SYLLABLE TOEGG +D1F7;D1F7;1110 116C 11AA;D1F7;1110 116C 11AA; # (퇷; 퇷; 퇷; 퇷; 퇷; ) HANGUL SYLLABLE TOEGS +D1F8;D1F8;1110 116C 11AB;D1F8;1110 116C 11AB; # (퇸; 퇸; 퇸; 퇸; 퇸; ) HANGUL SYLLABLE TOEN +D1F9;D1F9;1110 116C 11AC;D1F9;1110 116C 11AC; # (퇹; 퇹; 퇹; 퇹; 퇹; ) HANGUL SYLLABLE TOENJ +D1FA;D1FA;1110 116C 11AD;D1FA;1110 116C 11AD; # (퇺; 퇺; 퇺; 퇺; 퇺; ) HANGUL SYLLABLE TOENH +D1FB;D1FB;1110 116C 11AE;D1FB;1110 116C 11AE; # (퇻; 퇻; 퇻; 퇻; 퇻; ) HANGUL SYLLABLE TOED +D1FC;D1FC;1110 116C 11AF;D1FC;1110 116C 11AF; # (퇼; 퇼; 퇼; 퇼; 퇼; ) HANGUL SYLLABLE TOEL +D1FD;D1FD;1110 116C 11B0;D1FD;1110 116C 11B0; # (퇽; 퇽; 퇽; 퇽; 퇽; ) HANGUL SYLLABLE TOELG +D1FE;D1FE;1110 116C 11B1;D1FE;1110 116C 11B1; # (퇾; 퇾; 퇾; 퇾; 퇾; ) HANGUL SYLLABLE TOELM +D1FF;D1FF;1110 116C 11B2;D1FF;1110 116C 11B2; # (퇿; 퇿; 퇿; 퇿; 퇿; ) HANGUL SYLLABLE TOELB +D200;D200;1110 116C 11B3;D200;1110 116C 11B3; # (툀; 툀; 툀; 툀; 툀; ) HANGUL SYLLABLE TOELS +D201;D201;1110 116C 11B4;D201;1110 116C 11B4; # (툁; 툁; 툁; 툁; 툁; ) HANGUL SYLLABLE TOELT +D202;D202;1110 116C 11B5;D202;1110 116C 11B5; # (툂; 툂; 툂; 툂; 툂; ) HANGUL SYLLABLE TOELP +D203;D203;1110 116C 11B6;D203;1110 116C 11B6; # (툃; 툃; 툃; 툃; 툃; ) HANGUL SYLLABLE TOELH +D204;D204;1110 116C 11B7;D204;1110 116C 11B7; # (툄; 툄; 툄; 툄; 툄; ) HANGUL SYLLABLE TOEM +D205;D205;1110 116C 11B8;D205;1110 116C 11B8; # (툅; 툅; 툅; 툅; 툅; ) HANGUL SYLLABLE TOEB +D206;D206;1110 116C 11B9;D206;1110 116C 11B9; # (툆; 툆; 툆; 툆; 툆; ) HANGUL SYLLABLE TOEBS +D207;D207;1110 116C 11BA;D207;1110 116C 11BA; # (툇; 툇; 툇; 툇; 툇; ) HANGUL SYLLABLE TOES +D208;D208;1110 116C 11BB;D208;1110 116C 11BB; # (툈; 툈; 툈; 툈; 툈; ) HANGUL SYLLABLE TOESS +D209;D209;1110 116C 11BC;D209;1110 116C 11BC; # (툉; 툉; 툉; 툉; 툉; ) HANGUL SYLLABLE TOENG +D20A;D20A;1110 116C 11BD;D20A;1110 116C 11BD; # (툊; 툊; 툊; 툊; 툊; ) HANGUL SYLLABLE TOEJ +D20B;D20B;1110 116C 11BE;D20B;1110 116C 11BE; # (툋; 툋; 툋; 툋; 툋; ) HANGUL SYLLABLE TOEC +D20C;D20C;1110 116C 11BF;D20C;1110 116C 11BF; # (툌; 툌; 툌; 툌; 툌; ) HANGUL SYLLABLE TOEK +D20D;D20D;1110 116C 11C0;D20D;1110 116C 11C0; # (툍; 툍; 툍; 툍; 툍; ) HANGUL SYLLABLE TOET +D20E;D20E;1110 116C 11C1;D20E;1110 116C 11C1; # (툎; 툎; 툎; 툎; 툎; ) HANGUL SYLLABLE TOEP +D20F;D20F;1110 116C 11C2;D20F;1110 116C 11C2; # (툏; 툏; 툏; 툏; 툏; ) HANGUL SYLLABLE TOEH +D210;D210;1110 116D;D210;1110 116D; # (툐; 툐; 툐; 툐; 툐; ) HANGUL SYLLABLE TYO +D211;D211;1110 116D 11A8;D211;1110 116D 11A8; # (툑; 툑; 툑; 툑; 툑; ) HANGUL SYLLABLE TYOG +D212;D212;1110 116D 11A9;D212;1110 116D 11A9; # (툒; 툒; 툒; 툒; 툒; ) HANGUL SYLLABLE TYOGG +D213;D213;1110 116D 11AA;D213;1110 116D 11AA; # (툓; 툓; 툓; 툓; 툓; ) HANGUL SYLLABLE TYOGS +D214;D214;1110 116D 11AB;D214;1110 116D 11AB; # (툔; 툔; 툔; 툔; 툔; ) HANGUL SYLLABLE TYON +D215;D215;1110 116D 11AC;D215;1110 116D 11AC; # (툕; 툕; 툕; 툕; 툕; ) HANGUL SYLLABLE TYONJ +D216;D216;1110 116D 11AD;D216;1110 116D 11AD; # (툖; 툖; 툖; 툖; 툖; ) HANGUL SYLLABLE TYONH +D217;D217;1110 116D 11AE;D217;1110 116D 11AE; # (툗; 툗; 툗; 툗; 툗; ) HANGUL SYLLABLE TYOD +D218;D218;1110 116D 11AF;D218;1110 116D 11AF; # (툘; 툘; 툘; 툘; 툘; ) HANGUL SYLLABLE TYOL +D219;D219;1110 116D 11B0;D219;1110 116D 11B0; # (툙; 툙; 툙; 툙; 툙; ) HANGUL SYLLABLE TYOLG +D21A;D21A;1110 116D 11B1;D21A;1110 116D 11B1; # (툚; 툚; 툚; 툚; 툚; ) HANGUL SYLLABLE TYOLM +D21B;D21B;1110 116D 11B2;D21B;1110 116D 11B2; # (툛; 툛; 툛; 툛; 툛; ) HANGUL SYLLABLE TYOLB +D21C;D21C;1110 116D 11B3;D21C;1110 116D 11B3; # (툜; 툜; 툜; 툜; 툜; ) HANGUL SYLLABLE TYOLS +D21D;D21D;1110 116D 11B4;D21D;1110 116D 11B4; # (툝; 툝; 툝; 툝; 툝; ) HANGUL SYLLABLE TYOLT +D21E;D21E;1110 116D 11B5;D21E;1110 116D 11B5; # (툞; 툞; 툞; 툞; 툞; ) HANGUL SYLLABLE TYOLP +D21F;D21F;1110 116D 11B6;D21F;1110 116D 11B6; # (툟; 툟; 툟; 툟; 툟; ) HANGUL SYLLABLE TYOLH +D220;D220;1110 116D 11B7;D220;1110 116D 11B7; # (툠; 툠; 툠; 툠; 툠; ) HANGUL SYLLABLE TYOM +D221;D221;1110 116D 11B8;D221;1110 116D 11B8; # (툡; 툡; 툡; 툡; 툡; ) HANGUL SYLLABLE TYOB +D222;D222;1110 116D 11B9;D222;1110 116D 11B9; # (툢; 툢; 툢; 툢; 툢; ) HANGUL SYLLABLE TYOBS +D223;D223;1110 116D 11BA;D223;1110 116D 11BA; # (툣; 툣; 툣; 툣; 툣; ) HANGUL SYLLABLE TYOS +D224;D224;1110 116D 11BB;D224;1110 116D 11BB; # (툤; 툤; 툤; 툤; 툤; ) HANGUL SYLLABLE TYOSS +D225;D225;1110 116D 11BC;D225;1110 116D 11BC; # (툥; 툥; 툥; 툥; 툥; ) HANGUL SYLLABLE TYONG +D226;D226;1110 116D 11BD;D226;1110 116D 11BD; # (툦; 툦; 툦; 툦; 툦; ) HANGUL SYLLABLE TYOJ +D227;D227;1110 116D 11BE;D227;1110 116D 11BE; # (툧; 툧; 툧; 툧; 툧; ) HANGUL SYLLABLE TYOC +D228;D228;1110 116D 11BF;D228;1110 116D 11BF; # (툨; 툨; 툨; 툨; 툨; ) HANGUL SYLLABLE TYOK +D229;D229;1110 116D 11C0;D229;1110 116D 11C0; # (툩; 툩; 툩; 툩; 툩; ) HANGUL SYLLABLE TYOT +D22A;D22A;1110 116D 11C1;D22A;1110 116D 11C1; # (툪; 툪; 툪; 툪; 툪; ) HANGUL SYLLABLE TYOP +D22B;D22B;1110 116D 11C2;D22B;1110 116D 11C2; # (툫; 툫; 툫; 툫; 툫; ) HANGUL SYLLABLE TYOH +D22C;D22C;1110 116E;D22C;1110 116E; # (투; 투; 투; 투; 투; ) HANGUL SYLLABLE TU +D22D;D22D;1110 116E 11A8;D22D;1110 116E 11A8; # (툭; 툭; 툭; 툭; 툭; ) HANGUL SYLLABLE TUG +D22E;D22E;1110 116E 11A9;D22E;1110 116E 11A9; # (툮; 툮; 툮; 툮; 툮; ) HANGUL SYLLABLE TUGG +D22F;D22F;1110 116E 11AA;D22F;1110 116E 11AA; # (툯; 툯; 툯; 툯; 툯; ) HANGUL SYLLABLE TUGS +D230;D230;1110 116E 11AB;D230;1110 116E 11AB; # (툰; 툰; 툰; 툰; 툰; ) HANGUL SYLLABLE TUN +D231;D231;1110 116E 11AC;D231;1110 116E 11AC; # (툱; 툱; 툱; 툱; 툱; ) HANGUL SYLLABLE TUNJ +D232;D232;1110 116E 11AD;D232;1110 116E 11AD; # (툲; 툲; 툲; 툲; 툲; ) HANGUL SYLLABLE TUNH +D233;D233;1110 116E 11AE;D233;1110 116E 11AE; # (툳; 툳; 툳; 툳; 툳; ) HANGUL SYLLABLE TUD +D234;D234;1110 116E 11AF;D234;1110 116E 11AF; # (툴; 툴; 툴; 툴; 툴; ) HANGUL SYLLABLE TUL +D235;D235;1110 116E 11B0;D235;1110 116E 11B0; # (툵; 툵; 툵; 툵; 툵; ) HANGUL SYLLABLE TULG +D236;D236;1110 116E 11B1;D236;1110 116E 11B1; # (툶; 툶; 툶; 툶; 툶; ) HANGUL SYLLABLE TULM +D237;D237;1110 116E 11B2;D237;1110 116E 11B2; # (툷; 툷; 툷; 툷; 툷; ) HANGUL SYLLABLE TULB +D238;D238;1110 116E 11B3;D238;1110 116E 11B3; # (툸; 툸; 툸; 툸; 툸; ) HANGUL SYLLABLE TULS +D239;D239;1110 116E 11B4;D239;1110 116E 11B4; # (툹; 툹; 툹; 툹; 툹; ) HANGUL SYLLABLE TULT +D23A;D23A;1110 116E 11B5;D23A;1110 116E 11B5; # (툺; 툺; 툺; 툺; 툺; ) HANGUL SYLLABLE TULP +D23B;D23B;1110 116E 11B6;D23B;1110 116E 11B6; # (툻; 툻; 툻; 툻; 툻; ) HANGUL SYLLABLE TULH +D23C;D23C;1110 116E 11B7;D23C;1110 116E 11B7; # (툼; 툼; 툼; 툼; 툼; ) HANGUL SYLLABLE TUM +D23D;D23D;1110 116E 11B8;D23D;1110 116E 11B8; # (툽; 툽; 툽; 툽; 툽; ) HANGUL SYLLABLE TUB +D23E;D23E;1110 116E 11B9;D23E;1110 116E 11B9; # (툾; 툾; 툾; 툾; 툾; ) HANGUL SYLLABLE TUBS +D23F;D23F;1110 116E 11BA;D23F;1110 116E 11BA; # (툿; 툿; 툿; 툿; 툿; ) HANGUL SYLLABLE TUS +D240;D240;1110 116E 11BB;D240;1110 116E 11BB; # (퉀; 퉀; 퉀; 퉀; 퉀; ) HANGUL SYLLABLE TUSS +D241;D241;1110 116E 11BC;D241;1110 116E 11BC; # (퉁; 퉁; 퉁; 퉁; 퉁; ) HANGUL SYLLABLE TUNG +D242;D242;1110 116E 11BD;D242;1110 116E 11BD; # (퉂; 퉂; 퉂; 퉂; 퉂; ) HANGUL SYLLABLE TUJ +D243;D243;1110 116E 11BE;D243;1110 116E 11BE; # (퉃; 퉃; 퉃; 퉃; 퉃; ) HANGUL SYLLABLE TUC +D244;D244;1110 116E 11BF;D244;1110 116E 11BF; # (퉄; 퉄; 퉄; 퉄; 퉄; ) HANGUL SYLLABLE TUK +D245;D245;1110 116E 11C0;D245;1110 116E 11C0; # (퉅; 퉅; 퉅; 퉅; 퉅; ) HANGUL SYLLABLE TUT +D246;D246;1110 116E 11C1;D246;1110 116E 11C1; # (퉆; 퉆; 퉆; 퉆; 퉆; ) HANGUL SYLLABLE TUP +D247;D247;1110 116E 11C2;D247;1110 116E 11C2; # (퉇; 퉇; 퉇; 퉇; 퉇; ) HANGUL SYLLABLE TUH +D248;D248;1110 116F;D248;1110 116F; # (퉈; 퉈; 퉈; 퉈; 퉈; ) HANGUL SYLLABLE TWEO +D249;D249;1110 116F 11A8;D249;1110 116F 11A8; # (퉉; 퉉; 퉉; 퉉; 퉉; ) HANGUL SYLLABLE TWEOG +D24A;D24A;1110 116F 11A9;D24A;1110 116F 11A9; # (퉊; 퉊; 퉊; 퉊; 퉊; ) HANGUL SYLLABLE TWEOGG +D24B;D24B;1110 116F 11AA;D24B;1110 116F 11AA; # (퉋; 퉋; 퉋; 퉋; 퉋; ) HANGUL SYLLABLE TWEOGS +D24C;D24C;1110 116F 11AB;D24C;1110 116F 11AB; # (퉌; 퉌; 퉌; 퉌; 퉌; ) HANGUL SYLLABLE TWEON +D24D;D24D;1110 116F 11AC;D24D;1110 116F 11AC; # (퉍; 퉍; 퉍; 퉍; 퉍; ) HANGUL SYLLABLE TWEONJ +D24E;D24E;1110 116F 11AD;D24E;1110 116F 11AD; # (퉎; 퉎; 퉎; 퉎; 퉎; ) HANGUL SYLLABLE TWEONH +D24F;D24F;1110 116F 11AE;D24F;1110 116F 11AE; # (퉏; 퉏; 퉏; 퉏; 퉏; ) HANGUL SYLLABLE TWEOD +D250;D250;1110 116F 11AF;D250;1110 116F 11AF; # (퉐; 퉐; 퉐; 퉐; 퉐; ) HANGUL SYLLABLE TWEOL +D251;D251;1110 116F 11B0;D251;1110 116F 11B0; # (퉑; 퉑; 퉑; 퉑; 퉑; ) HANGUL SYLLABLE TWEOLG +D252;D252;1110 116F 11B1;D252;1110 116F 11B1; # (퉒; 퉒; 퉒; 퉒; 퉒; ) HANGUL SYLLABLE TWEOLM +D253;D253;1110 116F 11B2;D253;1110 116F 11B2; # (퉓; 퉓; 퉓; 퉓; 퉓; ) HANGUL SYLLABLE TWEOLB +D254;D254;1110 116F 11B3;D254;1110 116F 11B3; # (퉔; 퉔; 퉔; 퉔; 퉔; ) HANGUL SYLLABLE TWEOLS +D255;D255;1110 116F 11B4;D255;1110 116F 11B4; # (퉕; 퉕; 퉕; 퉕; 퉕; ) HANGUL SYLLABLE TWEOLT +D256;D256;1110 116F 11B5;D256;1110 116F 11B5; # (퉖; 퉖; 퉖; 퉖; 퉖; ) HANGUL SYLLABLE TWEOLP +D257;D257;1110 116F 11B6;D257;1110 116F 11B6; # (퉗; 퉗; 퉗; 퉗; 퉗; ) HANGUL SYLLABLE TWEOLH +D258;D258;1110 116F 11B7;D258;1110 116F 11B7; # (퉘; 퉘; 퉘; 퉘; 퉘; ) HANGUL SYLLABLE TWEOM +D259;D259;1110 116F 11B8;D259;1110 116F 11B8; # (퉙; 퉙; 퉙; 퉙; 퉙; ) HANGUL SYLLABLE TWEOB +D25A;D25A;1110 116F 11B9;D25A;1110 116F 11B9; # (퉚; 퉚; 퉚; 퉚; 퉚; ) HANGUL SYLLABLE TWEOBS +D25B;D25B;1110 116F 11BA;D25B;1110 116F 11BA; # (퉛; 퉛; 퉛; 퉛; 퉛; ) HANGUL SYLLABLE TWEOS +D25C;D25C;1110 116F 11BB;D25C;1110 116F 11BB; # (퉜; 퉜; 퉜; 퉜; 퉜; ) HANGUL SYLLABLE TWEOSS +D25D;D25D;1110 116F 11BC;D25D;1110 116F 11BC; # (퉝; 퉝; 퉝; 퉝; 퉝; ) HANGUL SYLLABLE TWEONG +D25E;D25E;1110 116F 11BD;D25E;1110 116F 11BD; # (퉞; 퉞; 퉞; 퉞; 퉞; ) HANGUL SYLLABLE TWEOJ +D25F;D25F;1110 116F 11BE;D25F;1110 116F 11BE; # (퉟; 퉟; 퉟; 퉟; 퉟; ) HANGUL SYLLABLE TWEOC +D260;D260;1110 116F 11BF;D260;1110 116F 11BF; # (퉠; 퉠; 퉠; 퉠; 퉠; ) HANGUL SYLLABLE TWEOK +D261;D261;1110 116F 11C0;D261;1110 116F 11C0; # (퉡; 퉡; 퉡; 퉡; 퉡; ) HANGUL SYLLABLE TWEOT +D262;D262;1110 116F 11C1;D262;1110 116F 11C1; # (퉢; 퉢; 퉢; 퉢; 퉢; ) HANGUL SYLLABLE TWEOP +D263;D263;1110 116F 11C2;D263;1110 116F 11C2; # (퉣; 퉣; 퉣; 퉣; 퉣; ) HANGUL SYLLABLE TWEOH +D264;D264;1110 1170;D264;1110 1170; # (퉤; 퉤; 퉤; 퉤; 퉤; ) HANGUL SYLLABLE TWE +D265;D265;1110 1170 11A8;D265;1110 1170 11A8; # (퉥; 퉥; 퉥; 퉥; 퉥; ) HANGUL SYLLABLE TWEG +D266;D266;1110 1170 11A9;D266;1110 1170 11A9; # (퉦; 퉦; 퉦; 퉦; 퉦; ) HANGUL SYLLABLE TWEGG +D267;D267;1110 1170 11AA;D267;1110 1170 11AA; # (퉧; 퉧; 퉧; 퉧; 퉧; ) HANGUL SYLLABLE TWEGS +D268;D268;1110 1170 11AB;D268;1110 1170 11AB; # (퉨; 퉨; 퉨; 퉨; 퉨; ) HANGUL SYLLABLE TWEN +D269;D269;1110 1170 11AC;D269;1110 1170 11AC; # (퉩; 퉩; 퉩; 퉩; 퉩; ) HANGUL SYLLABLE TWENJ +D26A;D26A;1110 1170 11AD;D26A;1110 1170 11AD; # (퉪; 퉪; 퉪; 퉪; 퉪; ) HANGUL SYLLABLE TWENH +D26B;D26B;1110 1170 11AE;D26B;1110 1170 11AE; # (퉫; 퉫; 퉫; 퉫; 퉫; ) HANGUL SYLLABLE TWED +D26C;D26C;1110 1170 11AF;D26C;1110 1170 11AF; # (퉬; 퉬; 퉬; 퉬; 퉬; ) HANGUL SYLLABLE TWEL +D26D;D26D;1110 1170 11B0;D26D;1110 1170 11B0; # (퉭; 퉭; 퉭; 퉭; 퉭; ) HANGUL SYLLABLE TWELG +D26E;D26E;1110 1170 11B1;D26E;1110 1170 11B1; # (퉮; 퉮; 퉮; 퉮; 퉮; ) HANGUL SYLLABLE TWELM +D26F;D26F;1110 1170 11B2;D26F;1110 1170 11B2; # (퉯; 퉯; 퉯; 퉯; 퉯; ) HANGUL SYLLABLE TWELB +D270;D270;1110 1170 11B3;D270;1110 1170 11B3; # (퉰; 퉰; 퉰; 퉰; 퉰; ) HANGUL SYLLABLE TWELS +D271;D271;1110 1170 11B4;D271;1110 1170 11B4; # (퉱; 퉱; 퉱; 퉱; 퉱; ) HANGUL SYLLABLE TWELT +D272;D272;1110 1170 11B5;D272;1110 1170 11B5; # (퉲; 퉲; 퉲; 퉲; 퉲; ) HANGUL SYLLABLE TWELP +D273;D273;1110 1170 11B6;D273;1110 1170 11B6; # (퉳; 퉳; 퉳; 퉳; 퉳; ) HANGUL SYLLABLE TWELH +D274;D274;1110 1170 11B7;D274;1110 1170 11B7; # (퉴; 퉴; 퉴; 퉴; 퉴; ) HANGUL SYLLABLE TWEM +D275;D275;1110 1170 11B8;D275;1110 1170 11B8; # (퉵; 퉵; 퉵; 퉵; 퉵; ) HANGUL SYLLABLE TWEB +D276;D276;1110 1170 11B9;D276;1110 1170 11B9; # (퉶; 퉶; 퉶; 퉶; 퉶; ) HANGUL SYLLABLE TWEBS +D277;D277;1110 1170 11BA;D277;1110 1170 11BA; # (퉷; 퉷; 퉷; 퉷; 퉷; ) HANGUL SYLLABLE TWES +D278;D278;1110 1170 11BB;D278;1110 1170 11BB; # (퉸; 퉸; 퉸; 퉸; 퉸; ) HANGUL SYLLABLE TWESS +D279;D279;1110 1170 11BC;D279;1110 1170 11BC; # (퉹; 퉹; 퉹; 퉹; 퉹; ) HANGUL SYLLABLE TWENG +D27A;D27A;1110 1170 11BD;D27A;1110 1170 11BD; # (퉺; 퉺; 퉺; 퉺; 퉺; ) HANGUL SYLLABLE TWEJ +D27B;D27B;1110 1170 11BE;D27B;1110 1170 11BE; # (퉻; 퉻; 퉻; 퉻; 퉻; ) HANGUL SYLLABLE TWEC +D27C;D27C;1110 1170 11BF;D27C;1110 1170 11BF; # (퉼; 퉼; 퉼; 퉼; 퉼; ) HANGUL SYLLABLE TWEK +D27D;D27D;1110 1170 11C0;D27D;1110 1170 11C0; # (퉽; 퉽; 퉽; 퉽; 퉽; ) HANGUL SYLLABLE TWET +D27E;D27E;1110 1170 11C1;D27E;1110 1170 11C1; # (퉾; 퉾; 퉾; 퉾; 퉾; ) HANGUL SYLLABLE TWEP +D27F;D27F;1110 1170 11C2;D27F;1110 1170 11C2; # (퉿; 퉿; 퉿; 퉿; 퉿; ) HANGUL SYLLABLE TWEH +D280;D280;1110 1171;D280;1110 1171; # (튀; 튀; 튀; 튀; 튀; ) HANGUL SYLLABLE TWI +D281;D281;1110 1171 11A8;D281;1110 1171 11A8; # (튁; 튁; 튁; 튁; 튁; ) HANGUL SYLLABLE TWIG +D282;D282;1110 1171 11A9;D282;1110 1171 11A9; # (튂; 튂; 튂; 튂; 튂; ) HANGUL SYLLABLE TWIGG +D283;D283;1110 1171 11AA;D283;1110 1171 11AA; # (튃; 튃; 튃; 튃; 튃; ) HANGUL SYLLABLE TWIGS +D284;D284;1110 1171 11AB;D284;1110 1171 11AB; # (튄; 튄; 튄; 튄; 튄; ) HANGUL SYLLABLE TWIN +D285;D285;1110 1171 11AC;D285;1110 1171 11AC; # (튅; 튅; 튅; 튅; 튅; ) HANGUL SYLLABLE TWINJ +D286;D286;1110 1171 11AD;D286;1110 1171 11AD; # (튆; 튆; 튆; 튆; 튆; ) HANGUL SYLLABLE TWINH +D287;D287;1110 1171 11AE;D287;1110 1171 11AE; # (튇; 튇; 튇; 튇; 튇; ) HANGUL SYLLABLE TWID +D288;D288;1110 1171 11AF;D288;1110 1171 11AF; # (튈; 튈; 튈; 튈; 튈; ) HANGUL SYLLABLE TWIL +D289;D289;1110 1171 11B0;D289;1110 1171 11B0; # (튉; 튉; 튉; 튉; 튉; ) HANGUL SYLLABLE TWILG +D28A;D28A;1110 1171 11B1;D28A;1110 1171 11B1; # (튊; 튊; 튊; 튊; 튊; ) HANGUL SYLLABLE TWILM +D28B;D28B;1110 1171 11B2;D28B;1110 1171 11B2; # (튋; 튋; 튋; 튋; 튋; ) HANGUL SYLLABLE TWILB +D28C;D28C;1110 1171 11B3;D28C;1110 1171 11B3; # (튌; 튌; 튌; 튌; 튌; ) HANGUL SYLLABLE TWILS +D28D;D28D;1110 1171 11B4;D28D;1110 1171 11B4; # (튍; 튍; 튍; 튍; 튍; ) HANGUL SYLLABLE TWILT +D28E;D28E;1110 1171 11B5;D28E;1110 1171 11B5; # (튎; 튎; 튎; 튎; 튎; ) HANGUL SYLLABLE TWILP +D28F;D28F;1110 1171 11B6;D28F;1110 1171 11B6; # (튏; 튏; 튏; 튏; 튏; ) HANGUL SYLLABLE TWILH +D290;D290;1110 1171 11B7;D290;1110 1171 11B7; # (튐; 튐; 튐; 튐; 튐; ) HANGUL SYLLABLE TWIM +D291;D291;1110 1171 11B8;D291;1110 1171 11B8; # (튑; 튑; 튑; 튑; 튑; ) HANGUL SYLLABLE TWIB +D292;D292;1110 1171 11B9;D292;1110 1171 11B9; # (튒; 튒; 튒; 튒; 튒; ) HANGUL SYLLABLE TWIBS +D293;D293;1110 1171 11BA;D293;1110 1171 11BA; # (튓; 튓; 튓; 튓; 튓; ) HANGUL SYLLABLE TWIS +D294;D294;1110 1171 11BB;D294;1110 1171 11BB; # (튔; 튔; 튔; 튔; 튔; ) HANGUL SYLLABLE TWISS +D295;D295;1110 1171 11BC;D295;1110 1171 11BC; # (튕; 튕; 튕; 튕; 튕; ) HANGUL SYLLABLE TWING +D296;D296;1110 1171 11BD;D296;1110 1171 11BD; # (튖; 튖; 튖; 튖; 튖; ) HANGUL SYLLABLE TWIJ +D297;D297;1110 1171 11BE;D297;1110 1171 11BE; # (튗; 튗; 튗; 튗; 튗; ) HANGUL SYLLABLE TWIC +D298;D298;1110 1171 11BF;D298;1110 1171 11BF; # (튘; 튘; 튘; 튘; 튘; ) HANGUL SYLLABLE TWIK +D299;D299;1110 1171 11C0;D299;1110 1171 11C0; # (튙; 튙; 튙; 튙; 튙; ) HANGUL SYLLABLE TWIT +D29A;D29A;1110 1171 11C1;D29A;1110 1171 11C1; # (튚; 튚; 튚; 튚; 튚; ) HANGUL SYLLABLE TWIP +D29B;D29B;1110 1171 11C2;D29B;1110 1171 11C2; # (튛; 튛; 튛; 튛; 튛; ) HANGUL SYLLABLE TWIH +D29C;D29C;1110 1172;D29C;1110 1172; # (튜; 튜; 튜; 튜; 튜; ) HANGUL SYLLABLE TYU +D29D;D29D;1110 1172 11A8;D29D;1110 1172 11A8; # (튝; 튝; 튝; 튝; 튝; ) HANGUL SYLLABLE TYUG +D29E;D29E;1110 1172 11A9;D29E;1110 1172 11A9; # (튞; 튞; 튞; 튞; 튞; ) HANGUL SYLLABLE TYUGG +D29F;D29F;1110 1172 11AA;D29F;1110 1172 11AA; # (튟; 튟; 튟; 튟; 튟; ) HANGUL SYLLABLE TYUGS +D2A0;D2A0;1110 1172 11AB;D2A0;1110 1172 11AB; # (튠; 튠; 튠; 튠; 튠; ) HANGUL SYLLABLE TYUN +D2A1;D2A1;1110 1172 11AC;D2A1;1110 1172 11AC; # (튡; 튡; 튡; 튡; 튡; ) HANGUL SYLLABLE TYUNJ +D2A2;D2A2;1110 1172 11AD;D2A2;1110 1172 11AD; # (튢; 튢; 튢; 튢; 튢; ) HANGUL SYLLABLE TYUNH +D2A3;D2A3;1110 1172 11AE;D2A3;1110 1172 11AE; # (튣; 튣; 튣; 튣; 튣; ) HANGUL SYLLABLE TYUD +D2A4;D2A4;1110 1172 11AF;D2A4;1110 1172 11AF; # (튤; 튤; 튤; 튤; 튤; ) HANGUL SYLLABLE TYUL +D2A5;D2A5;1110 1172 11B0;D2A5;1110 1172 11B0; # (튥; 튥; 튥; 튥; 튥; ) HANGUL SYLLABLE TYULG +D2A6;D2A6;1110 1172 11B1;D2A6;1110 1172 11B1; # (튦; 튦; 튦; 튦; 튦; ) HANGUL SYLLABLE TYULM +D2A7;D2A7;1110 1172 11B2;D2A7;1110 1172 11B2; # (튧; 튧; 튧; 튧; 튧; ) HANGUL SYLLABLE TYULB +D2A8;D2A8;1110 1172 11B3;D2A8;1110 1172 11B3; # (튨; 튨; 튨; 튨; 튨; ) HANGUL SYLLABLE TYULS +D2A9;D2A9;1110 1172 11B4;D2A9;1110 1172 11B4; # (튩; 튩; 튩; 튩; 튩; ) HANGUL SYLLABLE TYULT +D2AA;D2AA;1110 1172 11B5;D2AA;1110 1172 11B5; # (튪; 튪; 튪; 튪; 튪; ) HANGUL SYLLABLE TYULP +D2AB;D2AB;1110 1172 11B6;D2AB;1110 1172 11B6; # (튫; 튫; 튫; 튫; 튫; ) HANGUL SYLLABLE TYULH +D2AC;D2AC;1110 1172 11B7;D2AC;1110 1172 11B7; # (튬; 튬; 튬; 튬; 튬; ) HANGUL SYLLABLE TYUM +D2AD;D2AD;1110 1172 11B8;D2AD;1110 1172 11B8; # (튭; 튭; 튭; 튭; 튭; ) HANGUL SYLLABLE TYUB +D2AE;D2AE;1110 1172 11B9;D2AE;1110 1172 11B9; # (튮; 튮; 튮; 튮; 튮; ) HANGUL SYLLABLE TYUBS +D2AF;D2AF;1110 1172 11BA;D2AF;1110 1172 11BA; # (튯; 튯; 튯; 튯; 튯; ) HANGUL SYLLABLE TYUS +D2B0;D2B0;1110 1172 11BB;D2B0;1110 1172 11BB; # (튰; 튰; 튰; 튰; 튰; ) HANGUL SYLLABLE TYUSS +D2B1;D2B1;1110 1172 11BC;D2B1;1110 1172 11BC; # (튱; 튱; 튱; 튱; 튱; ) HANGUL SYLLABLE TYUNG +D2B2;D2B2;1110 1172 11BD;D2B2;1110 1172 11BD; # (튲; 튲; 튲; 튲; 튲; ) HANGUL SYLLABLE TYUJ +D2B3;D2B3;1110 1172 11BE;D2B3;1110 1172 11BE; # (튳; 튳; 튳; 튳; 튳; ) HANGUL SYLLABLE TYUC +D2B4;D2B4;1110 1172 11BF;D2B4;1110 1172 11BF; # (튴; 튴; 튴; 튴; 튴; ) HANGUL SYLLABLE TYUK +D2B5;D2B5;1110 1172 11C0;D2B5;1110 1172 11C0; # (튵; 튵; 튵; 튵; 튵; ) HANGUL SYLLABLE TYUT +D2B6;D2B6;1110 1172 11C1;D2B6;1110 1172 11C1; # (튶; 튶; 튶; 튶; 튶; ) HANGUL SYLLABLE TYUP +D2B7;D2B7;1110 1172 11C2;D2B7;1110 1172 11C2; # (튷; 튷; 튷; 튷; 튷; ) HANGUL SYLLABLE TYUH +D2B8;D2B8;1110 1173;D2B8;1110 1173; # (트; 트; 트; 트; 트; ) HANGUL SYLLABLE TEU +D2B9;D2B9;1110 1173 11A8;D2B9;1110 1173 11A8; # (특; 특; 특; 특; 특; ) HANGUL SYLLABLE TEUG +D2BA;D2BA;1110 1173 11A9;D2BA;1110 1173 11A9; # (튺; 튺; 튺; 튺; 튺; ) HANGUL SYLLABLE TEUGG +D2BB;D2BB;1110 1173 11AA;D2BB;1110 1173 11AA; # (튻; 튻; 튻; 튻; 튻; ) HANGUL SYLLABLE TEUGS +D2BC;D2BC;1110 1173 11AB;D2BC;1110 1173 11AB; # (튼; 튼; 튼; 튼; 튼; ) HANGUL SYLLABLE TEUN +D2BD;D2BD;1110 1173 11AC;D2BD;1110 1173 11AC; # (튽; 튽; 튽; 튽; 튽; ) HANGUL SYLLABLE TEUNJ +D2BE;D2BE;1110 1173 11AD;D2BE;1110 1173 11AD; # (튾; 튾; 튾; 튾; 튾; ) HANGUL SYLLABLE TEUNH +D2BF;D2BF;1110 1173 11AE;D2BF;1110 1173 11AE; # (튿; 튿; 튿; 튿; 튿; ) HANGUL SYLLABLE TEUD +D2C0;D2C0;1110 1173 11AF;D2C0;1110 1173 11AF; # (틀; 틀; 틀; 틀; 틀; ) HANGUL SYLLABLE TEUL +D2C1;D2C1;1110 1173 11B0;D2C1;1110 1173 11B0; # (틁; 틁; 틁; 틁; 틁; ) HANGUL SYLLABLE TEULG +D2C2;D2C2;1110 1173 11B1;D2C2;1110 1173 11B1; # (틂; 틂; 틂; 틂; 틂; ) HANGUL SYLLABLE TEULM +D2C3;D2C3;1110 1173 11B2;D2C3;1110 1173 11B2; # (틃; 틃; 틃; 틃; 틃; ) HANGUL SYLLABLE TEULB +D2C4;D2C4;1110 1173 11B3;D2C4;1110 1173 11B3; # (틄; 틄; 틄; 틄; 틄; ) HANGUL SYLLABLE TEULS +D2C5;D2C5;1110 1173 11B4;D2C5;1110 1173 11B4; # (틅; 틅; 틅; 틅; 틅; ) HANGUL SYLLABLE TEULT +D2C6;D2C6;1110 1173 11B5;D2C6;1110 1173 11B5; # (틆; 틆; 틆; 틆; 틆; ) HANGUL SYLLABLE TEULP +D2C7;D2C7;1110 1173 11B6;D2C7;1110 1173 11B6; # (틇; 틇; 틇; 틇; 틇; ) HANGUL SYLLABLE TEULH +D2C8;D2C8;1110 1173 11B7;D2C8;1110 1173 11B7; # (틈; 틈; 틈; 틈; 틈; ) HANGUL SYLLABLE TEUM +D2C9;D2C9;1110 1173 11B8;D2C9;1110 1173 11B8; # (틉; 틉; 틉; 틉; 틉; ) HANGUL SYLLABLE TEUB +D2CA;D2CA;1110 1173 11B9;D2CA;1110 1173 11B9; # (틊; 틊; 틊; 틊; 틊; ) HANGUL SYLLABLE TEUBS +D2CB;D2CB;1110 1173 11BA;D2CB;1110 1173 11BA; # (틋; 틋; 틋; 틋; 틋; ) HANGUL SYLLABLE TEUS +D2CC;D2CC;1110 1173 11BB;D2CC;1110 1173 11BB; # (틌; 틌; 틌; 틌; 틌; ) HANGUL SYLLABLE TEUSS +D2CD;D2CD;1110 1173 11BC;D2CD;1110 1173 11BC; # (틍; 틍; 틍; 틍; 틍; ) HANGUL SYLLABLE TEUNG +D2CE;D2CE;1110 1173 11BD;D2CE;1110 1173 11BD; # (틎; 틎; 틎; 틎; 틎; ) HANGUL SYLLABLE TEUJ +D2CF;D2CF;1110 1173 11BE;D2CF;1110 1173 11BE; # (틏; 틏; 틏; 틏; 틏; ) HANGUL SYLLABLE TEUC +D2D0;D2D0;1110 1173 11BF;D2D0;1110 1173 11BF; # (틐; 틐; 틐; 틐; 틐; ) HANGUL SYLLABLE TEUK +D2D1;D2D1;1110 1173 11C0;D2D1;1110 1173 11C0; # (틑; 틑; 틑; 틑; 틑; ) HANGUL SYLLABLE TEUT +D2D2;D2D2;1110 1173 11C1;D2D2;1110 1173 11C1; # (틒; 틒; 틒; 틒; 틒; ) HANGUL SYLLABLE TEUP +D2D3;D2D3;1110 1173 11C2;D2D3;1110 1173 11C2; # (틓; 틓; 틓; 틓; 틓; ) HANGUL SYLLABLE TEUH +D2D4;D2D4;1110 1174;D2D4;1110 1174; # (틔; 틔; 틔; 틔; 틔; ) HANGUL SYLLABLE TYI +D2D5;D2D5;1110 1174 11A8;D2D5;1110 1174 11A8; # (틕; 틕; 틕; 틕; 틕; ) HANGUL SYLLABLE TYIG +D2D6;D2D6;1110 1174 11A9;D2D6;1110 1174 11A9; # (틖; 틖; 틖; 틖; 틖; ) HANGUL SYLLABLE TYIGG +D2D7;D2D7;1110 1174 11AA;D2D7;1110 1174 11AA; # (틗; 틗; 틗; 틗; 틗; ) HANGUL SYLLABLE TYIGS +D2D8;D2D8;1110 1174 11AB;D2D8;1110 1174 11AB; # (틘; 틘; 틘; 틘; 틘; ) HANGUL SYLLABLE TYIN +D2D9;D2D9;1110 1174 11AC;D2D9;1110 1174 11AC; # (틙; 틙; 틙; 틙; 틙; ) HANGUL SYLLABLE TYINJ +D2DA;D2DA;1110 1174 11AD;D2DA;1110 1174 11AD; # (틚; 틚; 틚; 틚; 틚; ) HANGUL SYLLABLE TYINH +D2DB;D2DB;1110 1174 11AE;D2DB;1110 1174 11AE; # (틛; 틛; 틛; 틛; 틛; ) HANGUL SYLLABLE TYID +D2DC;D2DC;1110 1174 11AF;D2DC;1110 1174 11AF; # (틜; 틜; 틜; 틜; 틜; ) HANGUL SYLLABLE TYIL +D2DD;D2DD;1110 1174 11B0;D2DD;1110 1174 11B0; # (틝; 틝; 틝; 틝; 틝; ) HANGUL SYLLABLE TYILG +D2DE;D2DE;1110 1174 11B1;D2DE;1110 1174 11B1; # (틞; 틞; 틞; 틞; 틞; ) HANGUL SYLLABLE TYILM +D2DF;D2DF;1110 1174 11B2;D2DF;1110 1174 11B2; # (틟; 틟; 틟; 틟; 틟; ) HANGUL SYLLABLE TYILB +D2E0;D2E0;1110 1174 11B3;D2E0;1110 1174 11B3; # (틠; 틠; 틠; 틠; 틠; ) HANGUL SYLLABLE TYILS +D2E1;D2E1;1110 1174 11B4;D2E1;1110 1174 11B4; # (틡; 틡; 틡; 틡; 틡; ) HANGUL SYLLABLE TYILT +D2E2;D2E2;1110 1174 11B5;D2E2;1110 1174 11B5; # (틢; 틢; 틢; 틢; 틢; ) HANGUL SYLLABLE TYILP +D2E3;D2E3;1110 1174 11B6;D2E3;1110 1174 11B6; # (틣; 틣; 틣; 틣; 틣; ) HANGUL SYLLABLE TYILH +D2E4;D2E4;1110 1174 11B7;D2E4;1110 1174 11B7; # (틤; 틤; 틤; 틤; 틤; ) HANGUL SYLLABLE TYIM +D2E5;D2E5;1110 1174 11B8;D2E5;1110 1174 11B8; # (틥; 틥; 틥; 틥; 틥; ) HANGUL SYLLABLE TYIB +D2E6;D2E6;1110 1174 11B9;D2E6;1110 1174 11B9; # (틦; 틦; 틦; 틦; 틦; ) HANGUL SYLLABLE TYIBS +D2E7;D2E7;1110 1174 11BA;D2E7;1110 1174 11BA; # (틧; 틧; 틧; 틧; 틧; ) HANGUL SYLLABLE TYIS +D2E8;D2E8;1110 1174 11BB;D2E8;1110 1174 11BB; # (틨; 틨; 틨; 틨; 틨; ) HANGUL SYLLABLE TYISS +D2E9;D2E9;1110 1174 11BC;D2E9;1110 1174 11BC; # (틩; 틩; 틩; 틩; 틩; ) HANGUL SYLLABLE TYING +D2EA;D2EA;1110 1174 11BD;D2EA;1110 1174 11BD; # (틪; 틪; 틪; 틪; 틪; ) HANGUL SYLLABLE TYIJ +D2EB;D2EB;1110 1174 11BE;D2EB;1110 1174 11BE; # (틫; 틫; 틫; 틫; 틫; ) HANGUL SYLLABLE TYIC +D2EC;D2EC;1110 1174 11BF;D2EC;1110 1174 11BF; # (틬; 틬; 틬; 틬; 틬; ) HANGUL SYLLABLE TYIK +D2ED;D2ED;1110 1174 11C0;D2ED;1110 1174 11C0; # (틭; 틭; 틭; 틭; 틭; ) HANGUL SYLLABLE TYIT +D2EE;D2EE;1110 1174 11C1;D2EE;1110 1174 11C1; # (틮; 틮; 틮; 틮; 틮; ) HANGUL SYLLABLE TYIP +D2EF;D2EF;1110 1174 11C2;D2EF;1110 1174 11C2; # (틯; 틯; 틯; 틯; 틯; ) HANGUL SYLLABLE TYIH +D2F0;D2F0;1110 1175;D2F0;1110 1175; # (티; 티; 티; 티; 티; ) HANGUL SYLLABLE TI +D2F1;D2F1;1110 1175 11A8;D2F1;1110 1175 11A8; # (틱; 틱; 틱; 틱; 틱; ) HANGUL SYLLABLE TIG +D2F2;D2F2;1110 1175 11A9;D2F2;1110 1175 11A9; # (틲; 틲; 틲; 틲; 틲; ) HANGUL SYLLABLE TIGG +D2F3;D2F3;1110 1175 11AA;D2F3;1110 1175 11AA; # (틳; 틳; 틳; 틳; 틳; ) HANGUL SYLLABLE TIGS +D2F4;D2F4;1110 1175 11AB;D2F4;1110 1175 11AB; # (틴; 틴; 틴; 틴; 틴; ) HANGUL SYLLABLE TIN +D2F5;D2F5;1110 1175 11AC;D2F5;1110 1175 11AC; # (틵; 틵; 틵; 틵; 틵; ) HANGUL SYLLABLE TINJ +D2F6;D2F6;1110 1175 11AD;D2F6;1110 1175 11AD; # (틶; 틶; 틶; 틶; 틶; ) HANGUL SYLLABLE TINH +D2F7;D2F7;1110 1175 11AE;D2F7;1110 1175 11AE; # (틷; 틷; 틷; 틷; 틷; ) HANGUL SYLLABLE TID +D2F8;D2F8;1110 1175 11AF;D2F8;1110 1175 11AF; # (틸; 틸; 틸; 틸; 틸; ) HANGUL SYLLABLE TIL +D2F9;D2F9;1110 1175 11B0;D2F9;1110 1175 11B0; # (틹; 틹; 틹; 틹; 틹; ) HANGUL SYLLABLE TILG +D2FA;D2FA;1110 1175 11B1;D2FA;1110 1175 11B1; # (틺; 틺; 틺; 틺; 틺; ) HANGUL SYLLABLE TILM +D2FB;D2FB;1110 1175 11B2;D2FB;1110 1175 11B2; # (틻; 틻; 틻; 틻; 틻; ) HANGUL SYLLABLE TILB +D2FC;D2FC;1110 1175 11B3;D2FC;1110 1175 11B3; # (틼; 틼; 틼; 틼; 틼; ) HANGUL SYLLABLE TILS +D2FD;D2FD;1110 1175 11B4;D2FD;1110 1175 11B4; # (틽; 틽; 틽; 틽; 틽; ) HANGUL SYLLABLE TILT +D2FE;D2FE;1110 1175 11B5;D2FE;1110 1175 11B5; # (틾; 틾; 틾; 틾; 틾; ) HANGUL SYLLABLE TILP +D2FF;D2FF;1110 1175 11B6;D2FF;1110 1175 11B6; # (틿; 틿; 틿; 틿; 틿; ) HANGUL SYLLABLE TILH +D300;D300;1110 1175 11B7;D300;1110 1175 11B7; # (팀; 팀; 팀; 팀; 팀; ) HANGUL SYLLABLE TIM +D301;D301;1110 1175 11B8;D301;1110 1175 11B8; # (팁; 팁; 팁; 팁; 팁; ) HANGUL SYLLABLE TIB +D302;D302;1110 1175 11B9;D302;1110 1175 11B9; # (팂; 팂; 팂; 팂; 팂; ) HANGUL SYLLABLE TIBS +D303;D303;1110 1175 11BA;D303;1110 1175 11BA; # (팃; 팃; 팃; 팃; 팃; ) HANGUL SYLLABLE TIS +D304;D304;1110 1175 11BB;D304;1110 1175 11BB; # (팄; 팄; 팄; 팄; 팄; ) HANGUL SYLLABLE TISS +D305;D305;1110 1175 11BC;D305;1110 1175 11BC; # (팅; 팅; 팅; 팅; 팅; ) HANGUL SYLLABLE TING +D306;D306;1110 1175 11BD;D306;1110 1175 11BD; # (팆; 팆; 팆; 팆; 팆; ) HANGUL SYLLABLE TIJ +D307;D307;1110 1175 11BE;D307;1110 1175 11BE; # (팇; 팇; 팇; 팇; 팇; ) HANGUL SYLLABLE TIC +D308;D308;1110 1175 11BF;D308;1110 1175 11BF; # (팈; 팈; 팈; 팈; 팈; ) HANGUL SYLLABLE TIK +D309;D309;1110 1175 11C0;D309;1110 1175 11C0; # (팉; 팉; 팉; 팉; 팉; ) HANGUL SYLLABLE TIT +D30A;D30A;1110 1175 11C1;D30A;1110 1175 11C1; # (팊; 팊; 팊; 팊; 팊; ) HANGUL SYLLABLE TIP +D30B;D30B;1110 1175 11C2;D30B;1110 1175 11C2; # (팋; 팋; 팋; 팋; 팋; ) HANGUL SYLLABLE TIH +D30C;D30C;1111 1161;D30C;1111 1161; # (파; 파; 파; 파; 파; ) HANGUL SYLLABLE PA +D30D;D30D;1111 1161 11A8;D30D;1111 1161 11A8; # (팍; 팍; 팍; 팍; 팍; ) HANGUL SYLLABLE PAG +D30E;D30E;1111 1161 11A9;D30E;1111 1161 11A9; # (팎; 팎; 팎; 팎; 팎; ) HANGUL SYLLABLE PAGG +D30F;D30F;1111 1161 11AA;D30F;1111 1161 11AA; # (팏; 팏; 팏; 팏; 팏; ) HANGUL SYLLABLE PAGS +D310;D310;1111 1161 11AB;D310;1111 1161 11AB; # (판; 판; 판; 판; 판; ) HANGUL SYLLABLE PAN +D311;D311;1111 1161 11AC;D311;1111 1161 11AC; # (팑; 팑; 팑; 팑; 팑; ) HANGUL SYLLABLE PANJ +D312;D312;1111 1161 11AD;D312;1111 1161 11AD; # (팒; 팒; 팒; 팒; 팒; ) HANGUL SYLLABLE PANH +D313;D313;1111 1161 11AE;D313;1111 1161 11AE; # (팓; 팓; 팓; 팓; 팓; ) HANGUL SYLLABLE PAD +D314;D314;1111 1161 11AF;D314;1111 1161 11AF; # (팔; 팔; 팔; 팔; 팔; ) HANGUL SYLLABLE PAL +D315;D315;1111 1161 11B0;D315;1111 1161 11B0; # (팕; 팕; 팕; 팕; 팕; ) HANGUL SYLLABLE PALG +D316;D316;1111 1161 11B1;D316;1111 1161 11B1; # (팖; 팖; 팖; 팖; 팖; ) HANGUL SYLLABLE PALM +D317;D317;1111 1161 11B2;D317;1111 1161 11B2; # (팗; 팗; 팗; 팗; 팗; ) HANGUL SYLLABLE PALB +D318;D318;1111 1161 11B3;D318;1111 1161 11B3; # (팘; 팘; 팘; 팘; 팘; ) HANGUL SYLLABLE PALS +D319;D319;1111 1161 11B4;D319;1111 1161 11B4; # (팙; 팙; 팙; 팙; 팙; ) HANGUL SYLLABLE PALT +D31A;D31A;1111 1161 11B5;D31A;1111 1161 11B5; # (팚; 팚; 팚; 팚; 팚; ) HANGUL SYLLABLE PALP +D31B;D31B;1111 1161 11B6;D31B;1111 1161 11B6; # (팛; 팛; 팛; 팛; 팛; ) HANGUL SYLLABLE PALH +D31C;D31C;1111 1161 11B7;D31C;1111 1161 11B7; # (팜; 팜; 팜; 팜; 팜; ) HANGUL SYLLABLE PAM +D31D;D31D;1111 1161 11B8;D31D;1111 1161 11B8; # (팝; 팝; 팝; 팝; 팝; ) HANGUL SYLLABLE PAB +D31E;D31E;1111 1161 11B9;D31E;1111 1161 11B9; # (팞; 팞; 팞; 팞; 팞; ) HANGUL SYLLABLE PABS +D31F;D31F;1111 1161 11BA;D31F;1111 1161 11BA; # (팟; 팟; 팟; 팟; 팟; ) HANGUL SYLLABLE PAS +D320;D320;1111 1161 11BB;D320;1111 1161 11BB; # (팠; 팠; 팠; 팠; 팠; ) HANGUL SYLLABLE PASS +D321;D321;1111 1161 11BC;D321;1111 1161 11BC; # (팡; 팡; 팡; 팡; 팡; ) HANGUL SYLLABLE PANG +D322;D322;1111 1161 11BD;D322;1111 1161 11BD; # (팢; 팢; 팢; 팢; 팢; ) HANGUL SYLLABLE PAJ +D323;D323;1111 1161 11BE;D323;1111 1161 11BE; # (팣; 팣; 팣; 팣; 팣; ) HANGUL SYLLABLE PAC +D324;D324;1111 1161 11BF;D324;1111 1161 11BF; # (팤; 팤; 팤; 팤; 팤; ) HANGUL SYLLABLE PAK +D325;D325;1111 1161 11C0;D325;1111 1161 11C0; # (팥; 팥; 팥; 팥; 팥; ) HANGUL SYLLABLE PAT +D326;D326;1111 1161 11C1;D326;1111 1161 11C1; # (팦; 팦; 팦; 팦; 팦; ) HANGUL SYLLABLE PAP +D327;D327;1111 1161 11C2;D327;1111 1161 11C2; # (팧; 팧; 팧; 팧; 팧; ) HANGUL SYLLABLE PAH +D328;D328;1111 1162;D328;1111 1162; # (패; 패; 패; 패; 패; ) HANGUL SYLLABLE PAE +D329;D329;1111 1162 11A8;D329;1111 1162 11A8; # (팩; 팩; 팩; 팩; 팩; ) HANGUL SYLLABLE PAEG +D32A;D32A;1111 1162 11A9;D32A;1111 1162 11A9; # (팪; 팪; 팪; 팪; 팪; ) HANGUL SYLLABLE PAEGG +D32B;D32B;1111 1162 11AA;D32B;1111 1162 11AA; # (팫; 팫; 팫; 팫; 팫; ) HANGUL SYLLABLE PAEGS +D32C;D32C;1111 1162 11AB;D32C;1111 1162 11AB; # (팬; 팬; 팬; 팬; 팬; ) HANGUL SYLLABLE PAEN +D32D;D32D;1111 1162 11AC;D32D;1111 1162 11AC; # (팭; 팭; 팭; 팭; 팭; ) HANGUL SYLLABLE PAENJ +D32E;D32E;1111 1162 11AD;D32E;1111 1162 11AD; # (팮; 팮; 팮; 팮; 팮; ) HANGUL SYLLABLE PAENH +D32F;D32F;1111 1162 11AE;D32F;1111 1162 11AE; # (팯; 팯; 팯; 팯; 팯; ) HANGUL SYLLABLE PAED +D330;D330;1111 1162 11AF;D330;1111 1162 11AF; # (팰; 팰; 팰; 팰; 팰; ) HANGUL SYLLABLE PAEL +D331;D331;1111 1162 11B0;D331;1111 1162 11B0; # (팱; 팱; 팱; 팱; 팱; ) HANGUL SYLLABLE PAELG +D332;D332;1111 1162 11B1;D332;1111 1162 11B1; # (팲; 팲; 팲; 팲; 팲; ) HANGUL SYLLABLE PAELM +D333;D333;1111 1162 11B2;D333;1111 1162 11B2; # (팳; 팳; 팳; 팳; 팳; ) HANGUL SYLLABLE PAELB +D334;D334;1111 1162 11B3;D334;1111 1162 11B3; # (팴; 팴; 팴; 팴; 팴; ) HANGUL SYLLABLE PAELS +D335;D335;1111 1162 11B4;D335;1111 1162 11B4; # (팵; 팵; 팵; 팵; 팵; ) HANGUL SYLLABLE PAELT +D336;D336;1111 1162 11B5;D336;1111 1162 11B5; # (팶; 팶; 팶; 팶; 팶; ) HANGUL SYLLABLE PAELP +D337;D337;1111 1162 11B6;D337;1111 1162 11B6; # (팷; 팷; 팷; 팷; 팷; ) HANGUL SYLLABLE PAELH +D338;D338;1111 1162 11B7;D338;1111 1162 11B7; # (팸; 팸; 팸; 팸; 팸; ) HANGUL SYLLABLE PAEM +D339;D339;1111 1162 11B8;D339;1111 1162 11B8; # (팹; 팹; 팹; 팹; 팹; ) HANGUL SYLLABLE PAEB +D33A;D33A;1111 1162 11B9;D33A;1111 1162 11B9; # (팺; 팺; 팺; 팺; 팺; ) HANGUL SYLLABLE PAEBS +D33B;D33B;1111 1162 11BA;D33B;1111 1162 11BA; # (팻; 팻; 팻; 팻; 팻; ) HANGUL SYLLABLE PAES +D33C;D33C;1111 1162 11BB;D33C;1111 1162 11BB; # (팼; 팼; 팼; 팼; 팼; ) HANGUL SYLLABLE PAESS +D33D;D33D;1111 1162 11BC;D33D;1111 1162 11BC; # (팽; 팽; 팽; 팽; 팽; ) HANGUL SYLLABLE PAENG +D33E;D33E;1111 1162 11BD;D33E;1111 1162 11BD; # (팾; 팾; 팾; 팾; 팾; ) HANGUL SYLLABLE PAEJ +D33F;D33F;1111 1162 11BE;D33F;1111 1162 11BE; # (팿; 팿; 팿; 팿; 팿; ) HANGUL SYLLABLE PAEC +D340;D340;1111 1162 11BF;D340;1111 1162 11BF; # (퍀; 퍀; 퍀; 퍀; 퍀; ) HANGUL SYLLABLE PAEK +D341;D341;1111 1162 11C0;D341;1111 1162 11C0; # (퍁; 퍁; 퍁; 퍁; 퍁; ) HANGUL SYLLABLE PAET +D342;D342;1111 1162 11C1;D342;1111 1162 11C1; # (퍂; 퍂; 퍂; 퍂; 퍂; ) HANGUL SYLLABLE PAEP +D343;D343;1111 1162 11C2;D343;1111 1162 11C2; # (퍃; 퍃; 퍃; 퍃; 퍃; ) HANGUL SYLLABLE PAEH +D344;D344;1111 1163;D344;1111 1163; # (퍄; 퍄; 퍄; 퍄; 퍄; ) HANGUL SYLLABLE PYA +D345;D345;1111 1163 11A8;D345;1111 1163 11A8; # (퍅; 퍅; 퍅; 퍅; 퍅; ) HANGUL SYLLABLE PYAG +D346;D346;1111 1163 11A9;D346;1111 1163 11A9; # (퍆; 퍆; 퍆; 퍆; 퍆; ) HANGUL SYLLABLE PYAGG +D347;D347;1111 1163 11AA;D347;1111 1163 11AA; # (퍇; 퍇; 퍇; 퍇; 퍇; ) HANGUL SYLLABLE PYAGS +D348;D348;1111 1163 11AB;D348;1111 1163 11AB; # (퍈; 퍈; 퍈; 퍈; 퍈; ) HANGUL SYLLABLE PYAN +D349;D349;1111 1163 11AC;D349;1111 1163 11AC; # (퍉; 퍉; 퍉; 퍉; 퍉; ) HANGUL SYLLABLE PYANJ +D34A;D34A;1111 1163 11AD;D34A;1111 1163 11AD; # (퍊; 퍊; 퍊; 퍊; 퍊; ) HANGUL SYLLABLE PYANH +D34B;D34B;1111 1163 11AE;D34B;1111 1163 11AE; # (퍋; 퍋; 퍋; 퍋; 퍋; ) HANGUL SYLLABLE PYAD +D34C;D34C;1111 1163 11AF;D34C;1111 1163 11AF; # (퍌; 퍌; 퍌; 퍌; 퍌; ) HANGUL SYLLABLE PYAL +D34D;D34D;1111 1163 11B0;D34D;1111 1163 11B0; # (퍍; 퍍; 퍍; 퍍; 퍍; ) HANGUL SYLLABLE PYALG +D34E;D34E;1111 1163 11B1;D34E;1111 1163 11B1; # (퍎; 퍎; 퍎; 퍎; 퍎; ) HANGUL SYLLABLE PYALM +D34F;D34F;1111 1163 11B2;D34F;1111 1163 11B2; # (퍏; 퍏; 퍏; 퍏; 퍏; ) HANGUL SYLLABLE PYALB +D350;D350;1111 1163 11B3;D350;1111 1163 11B3; # (퍐; 퍐; 퍐; 퍐; 퍐; ) HANGUL SYLLABLE PYALS +D351;D351;1111 1163 11B4;D351;1111 1163 11B4; # (퍑; 퍑; 퍑; 퍑; 퍑; ) HANGUL SYLLABLE PYALT +D352;D352;1111 1163 11B5;D352;1111 1163 11B5; # (퍒; 퍒; 퍒; 퍒; 퍒; ) HANGUL SYLLABLE PYALP +D353;D353;1111 1163 11B6;D353;1111 1163 11B6; # (퍓; 퍓; 퍓; 퍓; 퍓; ) HANGUL SYLLABLE PYALH +D354;D354;1111 1163 11B7;D354;1111 1163 11B7; # (퍔; 퍔; 퍔; 퍔; 퍔; ) HANGUL SYLLABLE PYAM +D355;D355;1111 1163 11B8;D355;1111 1163 11B8; # (퍕; 퍕; 퍕; 퍕; 퍕; ) HANGUL SYLLABLE PYAB +D356;D356;1111 1163 11B9;D356;1111 1163 11B9; # (퍖; 퍖; 퍖; 퍖; 퍖; ) HANGUL SYLLABLE PYABS +D357;D357;1111 1163 11BA;D357;1111 1163 11BA; # (퍗; 퍗; 퍗; 퍗; 퍗; ) HANGUL SYLLABLE PYAS +D358;D358;1111 1163 11BB;D358;1111 1163 11BB; # (퍘; 퍘; 퍘; 퍘; 퍘; ) HANGUL SYLLABLE PYASS +D359;D359;1111 1163 11BC;D359;1111 1163 11BC; # (퍙; 퍙; 퍙; 퍙; 퍙; ) HANGUL SYLLABLE PYANG +D35A;D35A;1111 1163 11BD;D35A;1111 1163 11BD; # (퍚; 퍚; 퍚; 퍚; 퍚; ) HANGUL SYLLABLE PYAJ +D35B;D35B;1111 1163 11BE;D35B;1111 1163 11BE; # (퍛; 퍛; 퍛; 퍛; 퍛; ) HANGUL SYLLABLE PYAC +D35C;D35C;1111 1163 11BF;D35C;1111 1163 11BF; # (퍜; 퍜; 퍜; 퍜; 퍜; ) HANGUL SYLLABLE PYAK +D35D;D35D;1111 1163 11C0;D35D;1111 1163 11C0; # (퍝; 퍝; 퍝; 퍝; 퍝; ) HANGUL SYLLABLE PYAT +D35E;D35E;1111 1163 11C1;D35E;1111 1163 11C1; # (퍞; 퍞; 퍞; 퍞; 퍞; ) HANGUL SYLLABLE PYAP +D35F;D35F;1111 1163 11C2;D35F;1111 1163 11C2; # (퍟; 퍟; 퍟; 퍟; 퍟; ) HANGUL SYLLABLE PYAH +D360;D360;1111 1164;D360;1111 1164; # (퍠; 퍠; 퍠; 퍠; 퍠; ) HANGUL SYLLABLE PYAE +D361;D361;1111 1164 11A8;D361;1111 1164 11A8; # (퍡; 퍡; 퍡; 퍡; 퍡; ) HANGUL SYLLABLE PYAEG +D362;D362;1111 1164 11A9;D362;1111 1164 11A9; # (퍢; 퍢; 퍢; 퍢; 퍢; ) HANGUL SYLLABLE PYAEGG +D363;D363;1111 1164 11AA;D363;1111 1164 11AA; # (퍣; 퍣; 퍣; 퍣; 퍣; ) HANGUL SYLLABLE PYAEGS +D364;D364;1111 1164 11AB;D364;1111 1164 11AB; # (퍤; 퍤; 퍤; 퍤; 퍤; ) HANGUL SYLLABLE PYAEN +D365;D365;1111 1164 11AC;D365;1111 1164 11AC; # (퍥; 퍥; 퍥; 퍥; 퍥; ) HANGUL SYLLABLE PYAENJ +D366;D366;1111 1164 11AD;D366;1111 1164 11AD; # (퍦; 퍦; 퍦; 퍦; 퍦; ) HANGUL SYLLABLE PYAENH +D367;D367;1111 1164 11AE;D367;1111 1164 11AE; # (퍧; 퍧; 퍧; 퍧; 퍧; ) HANGUL SYLLABLE PYAED +D368;D368;1111 1164 11AF;D368;1111 1164 11AF; # (퍨; 퍨; 퍨; 퍨; 퍨; ) HANGUL SYLLABLE PYAEL +D369;D369;1111 1164 11B0;D369;1111 1164 11B0; # (퍩; 퍩; 퍩; 퍩; 퍩; ) HANGUL SYLLABLE PYAELG +D36A;D36A;1111 1164 11B1;D36A;1111 1164 11B1; # (퍪; 퍪; 퍪; 퍪; 퍪; ) HANGUL SYLLABLE PYAELM +D36B;D36B;1111 1164 11B2;D36B;1111 1164 11B2; # (퍫; 퍫; 퍫; 퍫; 퍫; ) HANGUL SYLLABLE PYAELB +D36C;D36C;1111 1164 11B3;D36C;1111 1164 11B3; # (퍬; 퍬; 퍬; 퍬; 퍬; ) HANGUL SYLLABLE PYAELS +D36D;D36D;1111 1164 11B4;D36D;1111 1164 11B4; # (퍭; 퍭; 퍭; 퍭; 퍭; ) HANGUL SYLLABLE PYAELT +D36E;D36E;1111 1164 11B5;D36E;1111 1164 11B5; # (퍮; 퍮; 퍮; 퍮; 퍮; ) HANGUL SYLLABLE PYAELP +D36F;D36F;1111 1164 11B6;D36F;1111 1164 11B6; # (퍯; 퍯; 퍯; 퍯; 퍯; ) HANGUL SYLLABLE PYAELH +D370;D370;1111 1164 11B7;D370;1111 1164 11B7; # (퍰; 퍰; 퍰; 퍰; 퍰; ) HANGUL SYLLABLE PYAEM +D371;D371;1111 1164 11B8;D371;1111 1164 11B8; # (퍱; 퍱; 퍱; 퍱; 퍱; ) HANGUL SYLLABLE PYAEB +D372;D372;1111 1164 11B9;D372;1111 1164 11B9; # (퍲; 퍲; 퍲; 퍲; 퍲; ) HANGUL SYLLABLE PYAEBS +D373;D373;1111 1164 11BA;D373;1111 1164 11BA; # (퍳; 퍳; 퍳; 퍳; 퍳; ) HANGUL SYLLABLE PYAES +D374;D374;1111 1164 11BB;D374;1111 1164 11BB; # (퍴; 퍴; 퍴; 퍴; 퍴; ) HANGUL SYLLABLE PYAESS +D375;D375;1111 1164 11BC;D375;1111 1164 11BC; # (퍵; 퍵; 퍵; 퍵; 퍵; ) HANGUL SYLLABLE PYAENG +D376;D376;1111 1164 11BD;D376;1111 1164 11BD; # (퍶; 퍶; 퍶; 퍶; 퍶; ) HANGUL SYLLABLE PYAEJ +D377;D377;1111 1164 11BE;D377;1111 1164 11BE; # (퍷; 퍷; 퍷; 퍷; 퍷; ) HANGUL SYLLABLE PYAEC +D378;D378;1111 1164 11BF;D378;1111 1164 11BF; # (퍸; 퍸; 퍸; 퍸; 퍸; ) HANGUL SYLLABLE PYAEK +D379;D379;1111 1164 11C0;D379;1111 1164 11C0; # (퍹; 퍹; 퍹; 퍹; 퍹; ) HANGUL SYLLABLE PYAET +D37A;D37A;1111 1164 11C1;D37A;1111 1164 11C1; # (퍺; 퍺; 퍺; 퍺; 퍺; ) HANGUL SYLLABLE PYAEP +D37B;D37B;1111 1164 11C2;D37B;1111 1164 11C2; # (퍻; 퍻; 퍻; 퍻; 퍻; ) HANGUL SYLLABLE PYAEH +D37C;D37C;1111 1165;D37C;1111 1165; # (퍼; 퍼; 퍼; 퍼; 퍼; ) HANGUL SYLLABLE PEO +D37D;D37D;1111 1165 11A8;D37D;1111 1165 11A8; # (퍽; 퍽; 퍽; 퍽; 퍽; ) HANGUL SYLLABLE PEOG +D37E;D37E;1111 1165 11A9;D37E;1111 1165 11A9; # (퍾; 퍾; 퍾; 퍾; 퍾; ) HANGUL SYLLABLE PEOGG +D37F;D37F;1111 1165 11AA;D37F;1111 1165 11AA; # (퍿; 퍿; 퍿; 퍿; 퍿; ) HANGUL SYLLABLE PEOGS +D380;D380;1111 1165 11AB;D380;1111 1165 11AB; # (펀; 펀; 펀; 펀; 펀; ) HANGUL SYLLABLE PEON +D381;D381;1111 1165 11AC;D381;1111 1165 11AC; # (펁; 펁; 펁; 펁; 펁; ) HANGUL SYLLABLE PEONJ +D382;D382;1111 1165 11AD;D382;1111 1165 11AD; # (펂; 펂; 펂; 펂; 펂; ) HANGUL SYLLABLE PEONH +D383;D383;1111 1165 11AE;D383;1111 1165 11AE; # (펃; 펃; 펃; 펃; 펃; ) HANGUL SYLLABLE PEOD +D384;D384;1111 1165 11AF;D384;1111 1165 11AF; # (펄; 펄; 펄; 펄; 펄; ) HANGUL SYLLABLE PEOL +D385;D385;1111 1165 11B0;D385;1111 1165 11B0; # (펅; 펅; 펅; 펅; 펅; ) HANGUL SYLLABLE PEOLG +D386;D386;1111 1165 11B1;D386;1111 1165 11B1; # (펆; 펆; 펆; 펆; 펆; ) HANGUL SYLLABLE PEOLM +D387;D387;1111 1165 11B2;D387;1111 1165 11B2; # (펇; 펇; 펇; 펇; 펇; ) HANGUL SYLLABLE PEOLB +D388;D388;1111 1165 11B3;D388;1111 1165 11B3; # (펈; 펈; 펈; 펈; 펈; ) HANGUL SYLLABLE PEOLS +D389;D389;1111 1165 11B4;D389;1111 1165 11B4; # (펉; 펉; 펉; 펉; 펉; ) HANGUL SYLLABLE PEOLT +D38A;D38A;1111 1165 11B5;D38A;1111 1165 11B5; # (펊; 펊; 펊; 펊; 펊; ) HANGUL SYLLABLE PEOLP +D38B;D38B;1111 1165 11B6;D38B;1111 1165 11B6; # (펋; 펋; 펋; 펋; 펋; ) HANGUL SYLLABLE PEOLH +D38C;D38C;1111 1165 11B7;D38C;1111 1165 11B7; # (펌; 펌; 펌; 펌; 펌; ) HANGUL SYLLABLE PEOM +D38D;D38D;1111 1165 11B8;D38D;1111 1165 11B8; # (펍; 펍; 펍; 펍; 펍; ) HANGUL SYLLABLE PEOB +D38E;D38E;1111 1165 11B9;D38E;1111 1165 11B9; # (펎; 펎; 펎; 펎; 펎; ) HANGUL SYLLABLE PEOBS +D38F;D38F;1111 1165 11BA;D38F;1111 1165 11BA; # (펏; 펏; 펏; 펏; 펏; ) HANGUL SYLLABLE PEOS +D390;D390;1111 1165 11BB;D390;1111 1165 11BB; # (펐; 펐; 펐; 펐; 펐; ) HANGUL SYLLABLE PEOSS +D391;D391;1111 1165 11BC;D391;1111 1165 11BC; # (펑; 펑; 펑; 펑; 펑; ) HANGUL SYLLABLE PEONG +D392;D392;1111 1165 11BD;D392;1111 1165 11BD; # (펒; 펒; 펒; 펒; 펒; ) HANGUL SYLLABLE PEOJ +D393;D393;1111 1165 11BE;D393;1111 1165 11BE; # (펓; 펓; 펓; 펓; 펓; ) HANGUL SYLLABLE PEOC +D394;D394;1111 1165 11BF;D394;1111 1165 11BF; # (펔; 펔; 펔; 펔; 펔; ) HANGUL SYLLABLE PEOK +D395;D395;1111 1165 11C0;D395;1111 1165 11C0; # (펕; 펕; 펕; 펕; 펕; ) HANGUL SYLLABLE PEOT +D396;D396;1111 1165 11C1;D396;1111 1165 11C1; # (펖; 펖; 펖; 펖; 펖; ) HANGUL SYLLABLE PEOP +D397;D397;1111 1165 11C2;D397;1111 1165 11C2; # (펗; 펗; 펗; 펗; 펗; ) HANGUL SYLLABLE PEOH +D398;D398;1111 1166;D398;1111 1166; # (페; 페; 페; 페; 페; ) HANGUL SYLLABLE PE +D399;D399;1111 1166 11A8;D399;1111 1166 11A8; # (펙; 펙; 펙; 펙; 펙; ) HANGUL SYLLABLE PEG +D39A;D39A;1111 1166 11A9;D39A;1111 1166 11A9; # (펚; 펚; 펚; 펚; 펚; ) HANGUL SYLLABLE PEGG +D39B;D39B;1111 1166 11AA;D39B;1111 1166 11AA; # (펛; 펛; 펛; 펛; 펛; ) HANGUL SYLLABLE PEGS +D39C;D39C;1111 1166 11AB;D39C;1111 1166 11AB; # (펜; 펜; 펜; 펜; 펜; ) HANGUL SYLLABLE PEN +D39D;D39D;1111 1166 11AC;D39D;1111 1166 11AC; # (펝; 펝; 펝; 펝; 펝; ) HANGUL SYLLABLE PENJ +D39E;D39E;1111 1166 11AD;D39E;1111 1166 11AD; # (펞; 펞; 펞; 펞; 펞; ) HANGUL SYLLABLE PENH +D39F;D39F;1111 1166 11AE;D39F;1111 1166 11AE; # (펟; 펟; 펟; 펟; 펟; ) HANGUL SYLLABLE PED +D3A0;D3A0;1111 1166 11AF;D3A0;1111 1166 11AF; # (펠; 펠; 펠; 펠; 펠; ) HANGUL SYLLABLE PEL +D3A1;D3A1;1111 1166 11B0;D3A1;1111 1166 11B0; # (펡; 펡; 펡; 펡; 펡; ) HANGUL SYLLABLE PELG +D3A2;D3A2;1111 1166 11B1;D3A2;1111 1166 11B1; # (펢; 펢; 펢; 펢; 펢; ) HANGUL SYLLABLE PELM +D3A3;D3A3;1111 1166 11B2;D3A3;1111 1166 11B2; # (펣; 펣; 펣; 펣; 펣; ) HANGUL SYLLABLE PELB +D3A4;D3A4;1111 1166 11B3;D3A4;1111 1166 11B3; # (펤; 펤; 펤; 펤; 펤; ) HANGUL SYLLABLE PELS +D3A5;D3A5;1111 1166 11B4;D3A5;1111 1166 11B4; # (펥; 펥; 펥; 펥; 펥; ) HANGUL SYLLABLE PELT +D3A6;D3A6;1111 1166 11B5;D3A6;1111 1166 11B5; # (펦; 펦; 펦; 펦; 펦; ) HANGUL SYLLABLE PELP +D3A7;D3A7;1111 1166 11B6;D3A7;1111 1166 11B6; # (펧; 펧; 펧; 펧; 펧; ) HANGUL SYLLABLE PELH +D3A8;D3A8;1111 1166 11B7;D3A8;1111 1166 11B7; # (펨; 펨; 펨; 펨; 펨; ) HANGUL SYLLABLE PEM +D3A9;D3A9;1111 1166 11B8;D3A9;1111 1166 11B8; # (펩; 펩; 펩; 펩; 펩; ) HANGUL SYLLABLE PEB +D3AA;D3AA;1111 1166 11B9;D3AA;1111 1166 11B9; # (펪; 펪; 펪; 펪; 펪; ) HANGUL SYLLABLE PEBS +D3AB;D3AB;1111 1166 11BA;D3AB;1111 1166 11BA; # (펫; 펫; 펫; 펫; 펫; ) HANGUL SYLLABLE PES +D3AC;D3AC;1111 1166 11BB;D3AC;1111 1166 11BB; # (펬; 펬; 펬; 펬; 펬; ) HANGUL SYLLABLE PESS +D3AD;D3AD;1111 1166 11BC;D3AD;1111 1166 11BC; # (펭; 펭; 펭; 펭; 펭; ) HANGUL SYLLABLE PENG +D3AE;D3AE;1111 1166 11BD;D3AE;1111 1166 11BD; # (펮; 펮; 펮; 펮; 펮; ) HANGUL SYLLABLE PEJ +D3AF;D3AF;1111 1166 11BE;D3AF;1111 1166 11BE; # (펯; 펯; 펯; 펯; 펯; ) HANGUL SYLLABLE PEC +D3B0;D3B0;1111 1166 11BF;D3B0;1111 1166 11BF; # (펰; 펰; 펰; 펰; 펰; ) HANGUL SYLLABLE PEK +D3B1;D3B1;1111 1166 11C0;D3B1;1111 1166 11C0; # (펱; 펱; 펱; 펱; 펱; ) HANGUL SYLLABLE PET +D3B2;D3B2;1111 1166 11C1;D3B2;1111 1166 11C1; # (펲; 펲; 펲; 펲; 펲; ) HANGUL SYLLABLE PEP +D3B3;D3B3;1111 1166 11C2;D3B3;1111 1166 11C2; # (펳; 펳; 펳; 펳; 펳; ) HANGUL SYLLABLE PEH +D3B4;D3B4;1111 1167;D3B4;1111 1167; # (펴; 펴; 펴; 펴; 펴; ) HANGUL SYLLABLE PYEO +D3B5;D3B5;1111 1167 11A8;D3B5;1111 1167 11A8; # (펵; 펵; 펵; 펵; 펵; ) HANGUL SYLLABLE PYEOG +D3B6;D3B6;1111 1167 11A9;D3B6;1111 1167 11A9; # (펶; 펶; 펶; 펶; 펶; ) HANGUL SYLLABLE PYEOGG +D3B7;D3B7;1111 1167 11AA;D3B7;1111 1167 11AA; # (펷; 펷; 펷; 펷; 펷; ) HANGUL SYLLABLE PYEOGS +D3B8;D3B8;1111 1167 11AB;D3B8;1111 1167 11AB; # (편; 편; 편; 편; 편; ) HANGUL SYLLABLE PYEON +D3B9;D3B9;1111 1167 11AC;D3B9;1111 1167 11AC; # (펹; 펹; 펹; 펹; 펹; ) HANGUL SYLLABLE PYEONJ +D3BA;D3BA;1111 1167 11AD;D3BA;1111 1167 11AD; # (펺; 펺; 펺; 펺; 펺; ) HANGUL SYLLABLE PYEONH +D3BB;D3BB;1111 1167 11AE;D3BB;1111 1167 11AE; # (펻; 펻; 펻; 펻; 펻; ) HANGUL SYLLABLE PYEOD +D3BC;D3BC;1111 1167 11AF;D3BC;1111 1167 11AF; # (펼; 펼; 펼; 펼; 펼; ) HANGUL SYLLABLE PYEOL +D3BD;D3BD;1111 1167 11B0;D3BD;1111 1167 11B0; # (펽; 펽; 펽; 펽; 펽; ) HANGUL SYLLABLE PYEOLG +D3BE;D3BE;1111 1167 11B1;D3BE;1111 1167 11B1; # (펾; 펾; 펾; 펾; 펾; ) HANGUL SYLLABLE PYEOLM +D3BF;D3BF;1111 1167 11B2;D3BF;1111 1167 11B2; # (펿; 펿; 펿; 펿; 펿; ) HANGUL SYLLABLE PYEOLB +D3C0;D3C0;1111 1167 11B3;D3C0;1111 1167 11B3; # (폀; 폀; 폀; 폀; 폀; ) HANGUL SYLLABLE PYEOLS +D3C1;D3C1;1111 1167 11B4;D3C1;1111 1167 11B4; # (폁; 폁; 폁; 폁; 폁; ) HANGUL SYLLABLE PYEOLT +D3C2;D3C2;1111 1167 11B5;D3C2;1111 1167 11B5; # (폂; 폂; 폂; 폂; 폂; ) HANGUL SYLLABLE PYEOLP +D3C3;D3C3;1111 1167 11B6;D3C3;1111 1167 11B6; # (폃; 폃; 폃; 폃; 폃; ) HANGUL SYLLABLE PYEOLH +D3C4;D3C4;1111 1167 11B7;D3C4;1111 1167 11B7; # (폄; 폄; 폄; 폄; 폄; ) HANGUL SYLLABLE PYEOM +D3C5;D3C5;1111 1167 11B8;D3C5;1111 1167 11B8; # (폅; 폅; 폅; 폅; 폅; ) HANGUL SYLLABLE PYEOB +D3C6;D3C6;1111 1167 11B9;D3C6;1111 1167 11B9; # (폆; 폆; 폆; 폆; 폆; ) HANGUL SYLLABLE PYEOBS +D3C7;D3C7;1111 1167 11BA;D3C7;1111 1167 11BA; # (폇; 폇; 폇; 폇; 폇; ) HANGUL SYLLABLE PYEOS +D3C8;D3C8;1111 1167 11BB;D3C8;1111 1167 11BB; # (폈; 폈; 폈; 폈; 폈; ) HANGUL SYLLABLE PYEOSS +D3C9;D3C9;1111 1167 11BC;D3C9;1111 1167 11BC; # (평; 평; 평; 평; 평; ) HANGUL SYLLABLE PYEONG +D3CA;D3CA;1111 1167 11BD;D3CA;1111 1167 11BD; # (폊; 폊; 폊; 폊; 폊; ) HANGUL SYLLABLE PYEOJ +D3CB;D3CB;1111 1167 11BE;D3CB;1111 1167 11BE; # (폋; 폋; 폋; 폋; 폋; ) HANGUL SYLLABLE PYEOC +D3CC;D3CC;1111 1167 11BF;D3CC;1111 1167 11BF; # (폌; 폌; 폌; 폌; 폌; ) HANGUL SYLLABLE PYEOK +D3CD;D3CD;1111 1167 11C0;D3CD;1111 1167 11C0; # (폍; 폍; 폍; 폍; 폍; ) HANGUL SYLLABLE PYEOT +D3CE;D3CE;1111 1167 11C1;D3CE;1111 1167 11C1; # (폎; 폎; 폎; 폎; 폎; ) HANGUL SYLLABLE PYEOP +D3CF;D3CF;1111 1167 11C2;D3CF;1111 1167 11C2; # (폏; 폏; 폏; 폏; 폏; ) HANGUL SYLLABLE PYEOH +D3D0;D3D0;1111 1168;D3D0;1111 1168; # (폐; 폐; 폐; 폐; 폐; ) HANGUL SYLLABLE PYE +D3D1;D3D1;1111 1168 11A8;D3D1;1111 1168 11A8; # (폑; 폑; 폑; 폑; 폑; ) HANGUL SYLLABLE PYEG +D3D2;D3D2;1111 1168 11A9;D3D2;1111 1168 11A9; # (폒; 폒; 폒; 폒; 폒; ) HANGUL SYLLABLE PYEGG +D3D3;D3D3;1111 1168 11AA;D3D3;1111 1168 11AA; # (폓; 폓; 폓; 폓; 폓; ) HANGUL SYLLABLE PYEGS +D3D4;D3D4;1111 1168 11AB;D3D4;1111 1168 11AB; # (폔; 폔; 폔; 폔; 폔; ) HANGUL SYLLABLE PYEN +D3D5;D3D5;1111 1168 11AC;D3D5;1111 1168 11AC; # (폕; 폕; 폕; 폕; 폕; ) HANGUL SYLLABLE PYENJ +D3D6;D3D6;1111 1168 11AD;D3D6;1111 1168 11AD; # (폖; 폖; 폖; 폖; 폖; ) HANGUL SYLLABLE PYENH +D3D7;D3D7;1111 1168 11AE;D3D7;1111 1168 11AE; # (폗; 폗; 폗; 폗; 폗; ) HANGUL SYLLABLE PYED +D3D8;D3D8;1111 1168 11AF;D3D8;1111 1168 11AF; # (폘; 폘; 폘; 폘; 폘; ) HANGUL SYLLABLE PYEL +D3D9;D3D9;1111 1168 11B0;D3D9;1111 1168 11B0; # (폙; 폙; 폙; 폙; 폙; ) HANGUL SYLLABLE PYELG +D3DA;D3DA;1111 1168 11B1;D3DA;1111 1168 11B1; # (폚; 폚; 폚; 폚; 폚; ) HANGUL SYLLABLE PYELM +D3DB;D3DB;1111 1168 11B2;D3DB;1111 1168 11B2; # (폛; 폛; 폛; 폛; 폛; ) HANGUL SYLLABLE PYELB +D3DC;D3DC;1111 1168 11B3;D3DC;1111 1168 11B3; # (폜; 폜; 폜; 폜; 폜; ) HANGUL SYLLABLE PYELS +D3DD;D3DD;1111 1168 11B4;D3DD;1111 1168 11B4; # (폝; 폝; 폝; 폝; 폝; ) HANGUL SYLLABLE PYELT +D3DE;D3DE;1111 1168 11B5;D3DE;1111 1168 11B5; # (폞; 폞; 폞; 폞; 폞; ) HANGUL SYLLABLE PYELP +D3DF;D3DF;1111 1168 11B6;D3DF;1111 1168 11B6; # (폟; 폟; 폟; 폟; 폟; ) HANGUL SYLLABLE PYELH +D3E0;D3E0;1111 1168 11B7;D3E0;1111 1168 11B7; # (폠; 폠; 폠; 폠; 폠; ) HANGUL SYLLABLE PYEM +D3E1;D3E1;1111 1168 11B8;D3E1;1111 1168 11B8; # (폡; 폡; 폡; 폡; 폡; ) HANGUL SYLLABLE PYEB +D3E2;D3E2;1111 1168 11B9;D3E2;1111 1168 11B9; # (폢; 폢; 폢; 폢; 폢; ) HANGUL SYLLABLE PYEBS +D3E3;D3E3;1111 1168 11BA;D3E3;1111 1168 11BA; # (폣; 폣; 폣; 폣; 폣; ) HANGUL SYLLABLE PYES +D3E4;D3E4;1111 1168 11BB;D3E4;1111 1168 11BB; # (폤; 폤; 폤; 폤; 폤; ) HANGUL SYLLABLE PYESS +D3E5;D3E5;1111 1168 11BC;D3E5;1111 1168 11BC; # (폥; 폥; 폥; 폥; 폥; ) HANGUL SYLLABLE PYENG +D3E6;D3E6;1111 1168 11BD;D3E6;1111 1168 11BD; # (폦; 폦; 폦; 폦; 폦; ) HANGUL SYLLABLE PYEJ +D3E7;D3E7;1111 1168 11BE;D3E7;1111 1168 11BE; # (폧; 폧; 폧; 폧; 폧; ) HANGUL SYLLABLE PYEC +D3E8;D3E8;1111 1168 11BF;D3E8;1111 1168 11BF; # (폨; 폨; 폨; 폨; 폨; ) HANGUL SYLLABLE PYEK +D3E9;D3E9;1111 1168 11C0;D3E9;1111 1168 11C0; # (폩; 폩; 폩; 폩; 폩; ) HANGUL SYLLABLE PYET +D3EA;D3EA;1111 1168 11C1;D3EA;1111 1168 11C1; # (폪; 폪; 폪; 폪; 폪; ) HANGUL SYLLABLE PYEP +D3EB;D3EB;1111 1168 11C2;D3EB;1111 1168 11C2; # (폫; 폫; 폫; 폫; 폫; ) HANGUL SYLLABLE PYEH +D3EC;D3EC;1111 1169;D3EC;1111 1169; # (포; 포; 포; 포; 포; ) HANGUL SYLLABLE PO +D3ED;D3ED;1111 1169 11A8;D3ED;1111 1169 11A8; # (폭; 폭; 폭; 폭; 폭; ) HANGUL SYLLABLE POG +D3EE;D3EE;1111 1169 11A9;D3EE;1111 1169 11A9; # (폮; 폮; 폮; 폮; 폮; ) HANGUL SYLLABLE POGG +D3EF;D3EF;1111 1169 11AA;D3EF;1111 1169 11AA; # (폯; 폯; 폯; 폯; 폯; ) HANGUL SYLLABLE POGS +D3F0;D3F0;1111 1169 11AB;D3F0;1111 1169 11AB; # (폰; 폰; 폰; 폰; 폰; ) HANGUL SYLLABLE PON +D3F1;D3F1;1111 1169 11AC;D3F1;1111 1169 11AC; # (폱; 폱; 폱; 폱; 폱; ) HANGUL SYLLABLE PONJ +D3F2;D3F2;1111 1169 11AD;D3F2;1111 1169 11AD; # (폲; 폲; 폲; 폲; 폲; ) HANGUL SYLLABLE PONH +D3F3;D3F3;1111 1169 11AE;D3F3;1111 1169 11AE; # (폳; 폳; 폳; 폳; 폳; ) HANGUL SYLLABLE POD +D3F4;D3F4;1111 1169 11AF;D3F4;1111 1169 11AF; # (폴; 폴; 폴; 폴; 폴; ) HANGUL SYLLABLE POL +D3F5;D3F5;1111 1169 11B0;D3F5;1111 1169 11B0; # (폵; 폵; 폵; 폵; 폵; ) HANGUL SYLLABLE POLG +D3F6;D3F6;1111 1169 11B1;D3F6;1111 1169 11B1; # (폶; 폶; 폶; 폶; 폶; ) HANGUL SYLLABLE POLM +D3F7;D3F7;1111 1169 11B2;D3F7;1111 1169 11B2; # (폷; 폷; 폷; 폷; 폷; ) HANGUL SYLLABLE POLB +D3F8;D3F8;1111 1169 11B3;D3F8;1111 1169 11B3; # (폸; 폸; 폸; 폸; 폸; ) HANGUL SYLLABLE POLS +D3F9;D3F9;1111 1169 11B4;D3F9;1111 1169 11B4; # (폹; 폹; 폹; 폹; 폹; ) HANGUL SYLLABLE POLT +D3FA;D3FA;1111 1169 11B5;D3FA;1111 1169 11B5; # (폺; 폺; 폺; 폺; 폺; ) HANGUL SYLLABLE POLP +D3FB;D3FB;1111 1169 11B6;D3FB;1111 1169 11B6; # (폻; 폻; 폻; 폻; 폻; ) HANGUL SYLLABLE POLH +D3FC;D3FC;1111 1169 11B7;D3FC;1111 1169 11B7; # (폼; 폼; 폼; 폼; 폼; ) HANGUL SYLLABLE POM +D3FD;D3FD;1111 1169 11B8;D3FD;1111 1169 11B8; # (폽; 폽; 폽; 폽; 폽; ) HANGUL SYLLABLE POB +D3FE;D3FE;1111 1169 11B9;D3FE;1111 1169 11B9; # (폾; 폾; 폾; 폾; 폾; ) HANGUL SYLLABLE POBS +D3FF;D3FF;1111 1169 11BA;D3FF;1111 1169 11BA; # (폿; 폿; 폿; 폿; 폿; ) HANGUL SYLLABLE POS +D400;D400;1111 1169 11BB;D400;1111 1169 11BB; # (퐀; 퐀; 퐀; 퐀; 퐀; ) HANGUL SYLLABLE POSS +D401;D401;1111 1169 11BC;D401;1111 1169 11BC; # (퐁; 퐁; 퐁; 퐁; 퐁; ) HANGUL SYLLABLE PONG +D402;D402;1111 1169 11BD;D402;1111 1169 11BD; # (퐂; 퐂; 퐂; 퐂; 퐂; ) HANGUL SYLLABLE POJ +D403;D403;1111 1169 11BE;D403;1111 1169 11BE; # (퐃; 퐃; 퐃; 퐃; 퐃; ) HANGUL SYLLABLE POC +D404;D404;1111 1169 11BF;D404;1111 1169 11BF; # (퐄; 퐄; 퐄; 퐄; 퐄; ) HANGUL SYLLABLE POK +D405;D405;1111 1169 11C0;D405;1111 1169 11C0; # (퐅; 퐅; 퐅; 퐅; 퐅; ) HANGUL SYLLABLE POT +D406;D406;1111 1169 11C1;D406;1111 1169 11C1; # (퐆; 퐆; 퐆; 퐆; 퐆; ) HANGUL SYLLABLE POP +D407;D407;1111 1169 11C2;D407;1111 1169 11C2; # (퐇; 퐇; 퐇; 퐇; 퐇; ) HANGUL SYLLABLE POH +D408;D408;1111 116A;D408;1111 116A; # (퐈; 퐈; 퐈; 퐈; 퐈; ) HANGUL SYLLABLE PWA +D409;D409;1111 116A 11A8;D409;1111 116A 11A8; # (퐉; 퐉; 퐉; 퐉; 퐉; ) HANGUL SYLLABLE PWAG +D40A;D40A;1111 116A 11A9;D40A;1111 116A 11A9; # (퐊; 퐊; 퐊; 퐊; 퐊; ) HANGUL SYLLABLE PWAGG +D40B;D40B;1111 116A 11AA;D40B;1111 116A 11AA; # (퐋; 퐋; 퐋; 퐋; 퐋; ) HANGUL SYLLABLE PWAGS +D40C;D40C;1111 116A 11AB;D40C;1111 116A 11AB; # (퐌; 퐌; 퐌; 퐌; 퐌; ) HANGUL SYLLABLE PWAN +D40D;D40D;1111 116A 11AC;D40D;1111 116A 11AC; # (퐍; 퐍; 퐍; 퐍; 퐍; ) HANGUL SYLLABLE PWANJ +D40E;D40E;1111 116A 11AD;D40E;1111 116A 11AD; # (퐎; 퐎; 퐎; 퐎; 퐎; ) HANGUL SYLLABLE PWANH +D40F;D40F;1111 116A 11AE;D40F;1111 116A 11AE; # (퐏; 퐏; 퐏; 퐏; 퐏; ) HANGUL SYLLABLE PWAD +D410;D410;1111 116A 11AF;D410;1111 116A 11AF; # (퐐; 퐐; 퐐; 퐐; 퐐; ) HANGUL SYLLABLE PWAL +D411;D411;1111 116A 11B0;D411;1111 116A 11B0; # (퐑; 퐑; 퐑; 퐑; 퐑; ) HANGUL SYLLABLE PWALG +D412;D412;1111 116A 11B1;D412;1111 116A 11B1; # (퐒; 퐒; 퐒; 퐒; 퐒; ) HANGUL SYLLABLE PWALM +D413;D413;1111 116A 11B2;D413;1111 116A 11B2; # (퐓; 퐓; 퐓; 퐓; 퐓; ) HANGUL SYLLABLE PWALB +D414;D414;1111 116A 11B3;D414;1111 116A 11B3; # (퐔; 퐔; 퐔; 퐔; 퐔; ) HANGUL SYLLABLE PWALS +D415;D415;1111 116A 11B4;D415;1111 116A 11B4; # (퐕; 퐕; 퐕; 퐕; 퐕; ) HANGUL SYLLABLE PWALT +D416;D416;1111 116A 11B5;D416;1111 116A 11B5; # (퐖; 퐖; 퐖; 퐖; 퐖; ) HANGUL SYLLABLE PWALP +D417;D417;1111 116A 11B6;D417;1111 116A 11B6; # (퐗; 퐗; 퐗; 퐗; 퐗; ) HANGUL SYLLABLE PWALH +D418;D418;1111 116A 11B7;D418;1111 116A 11B7; # (퐘; 퐘; 퐘; 퐘; 퐘; ) HANGUL SYLLABLE PWAM +D419;D419;1111 116A 11B8;D419;1111 116A 11B8; # (퐙; 퐙; 퐙; 퐙; 퐙; ) HANGUL SYLLABLE PWAB +D41A;D41A;1111 116A 11B9;D41A;1111 116A 11B9; # (퐚; 퐚; 퐚; 퐚; 퐚; ) HANGUL SYLLABLE PWABS +D41B;D41B;1111 116A 11BA;D41B;1111 116A 11BA; # (퐛; 퐛; 퐛; 퐛; 퐛; ) HANGUL SYLLABLE PWAS +D41C;D41C;1111 116A 11BB;D41C;1111 116A 11BB; # (퐜; 퐜; 퐜; 퐜; 퐜; ) HANGUL SYLLABLE PWASS +D41D;D41D;1111 116A 11BC;D41D;1111 116A 11BC; # (퐝; 퐝; 퐝; 퐝; 퐝; ) HANGUL SYLLABLE PWANG +D41E;D41E;1111 116A 11BD;D41E;1111 116A 11BD; # (퐞; 퐞; 퐞; 퐞; 퐞; ) HANGUL SYLLABLE PWAJ +D41F;D41F;1111 116A 11BE;D41F;1111 116A 11BE; # (퐟; 퐟; 퐟; 퐟; 퐟; ) HANGUL SYLLABLE PWAC +D420;D420;1111 116A 11BF;D420;1111 116A 11BF; # (퐠; 퐠; 퐠; 퐠; 퐠; ) HANGUL SYLLABLE PWAK +D421;D421;1111 116A 11C0;D421;1111 116A 11C0; # (퐡; 퐡; 퐡; 퐡; 퐡; ) HANGUL SYLLABLE PWAT +D422;D422;1111 116A 11C1;D422;1111 116A 11C1; # (퐢; 퐢; 퐢; 퐢; 퐢; ) HANGUL SYLLABLE PWAP +D423;D423;1111 116A 11C2;D423;1111 116A 11C2; # (퐣; 퐣; 퐣; 퐣; 퐣; ) HANGUL SYLLABLE PWAH +D424;D424;1111 116B;D424;1111 116B; # (퐤; 퐤; 퐤; 퐤; 퐤; ) HANGUL SYLLABLE PWAE +D425;D425;1111 116B 11A8;D425;1111 116B 11A8; # (퐥; 퐥; 퐥; 퐥; 퐥; ) HANGUL SYLLABLE PWAEG +D426;D426;1111 116B 11A9;D426;1111 116B 11A9; # (퐦; 퐦; 퐦; 퐦; 퐦; ) HANGUL SYLLABLE PWAEGG +D427;D427;1111 116B 11AA;D427;1111 116B 11AA; # (퐧; 퐧; 퐧; 퐧; 퐧; ) HANGUL SYLLABLE PWAEGS +D428;D428;1111 116B 11AB;D428;1111 116B 11AB; # (퐨; 퐨; 퐨; 퐨; 퐨; ) HANGUL SYLLABLE PWAEN +D429;D429;1111 116B 11AC;D429;1111 116B 11AC; # (퐩; 퐩; 퐩; 퐩; 퐩; ) HANGUL SYLLABLE PWAENJ +D42A;D42A;1111 116B 11AD;D42A;1111 116B 11AD; # (퐪; 퐪; 퐪; 퐪; 퐪; ) HANGUL SYLLABLE PWAENH +D42B;D42B;1111 116B 11AE;D42B;1111 116B 11AE; # (퐫; 퐫; 퐫; 퐫; 퐫; ) HANGUL SYLLABLE PWAED +D42C;D42C;1111 116B 11AF;D42C;1111 116B 11AF; # (퐬; 퐬; 퐬; 퐬; 퐬; ) HANGUL SYLLABLE PWAEL +D42D;D42D;1111 116B 11B0;D42D;1111 116B 11B0; # (퐭; 퐭; 퐭; 퐭; 퐭; ) HANGUL SYLLABLE PWAELG +D42E;D42E;1111 116B 11B1;D42E;1111 116B 11B1; # (퐮; 퐮; 퐮; 퐮; 퐮; ) HANGUL SYLLABLE PWAELM +D42F;D42F;1111 116B 11B2;D42F;1111 116B 11B2; # (퐯; 퐯; 퐯; 퐯; 퐯; ) HANGUL SYLLABLE PWAELB +D430;D430;1111 116B 11B3;D430;1111 116B 11B3; # (퐰; 퐰; 퐰; 퐰; 퐰; ) HANGUL SYLLABLE PWAELS +D431;D431;1111 116B 11B4;D431;1111 116B 11B4; # (퐱; 퐱; 퐱; 퐱; 퐱; ) HANGUL SYLLABLE PWAELT +D432;D432;1111 116B 11B5;D432;1111 116B 11B5; # (퐲; 퐲; 퐲; 퐲; 퐲; ) HANGUL SYLLABLE PWAELP +D433;D433;1111 116B 11B6;D433;1111 116B 11B6; # (퐳; 퐳; 퐳; 퐳; 퐳; ) HANGUL SYLLABLE PWAELH +D434;D434;1111 116B 11B7;D434;1111 116B 11B7; # (퐴; 퐴; 퐴; 퐴; 퐴; ) HANGUL SYLLABLE PWAEM +D435;D435;1111 116B 11B8;D435;1111 116B 11B8; # (퐵; 퐵; 퐵; 퐵; 퐵; ) HANGUL SYLLABLE PWAEB +D436;D436;1111 116B 11B9;D436;1111 116B 11B9; # (퐶; 퐶; 퐶; 퐶; 퐶; ) HANGUL SYLLABLE PWAEBS +D437;D437;1111 116B 11BA;D437;1111 116B 11BA; # (퐷; 퐷; 퐷; 퐷; 퐷; ) HANGUL SYLLABLE PWAES +D438;D438;1111 116B 11BB;D438;1111 116B 11BB; # (퐸; 퐸; 퐸; 퐸; 퐸; ) HANGUL SYLLABLE PWAESS +D439;D439;1111 116B 11BC;D439;1111 116B 11BC; # (퐹; 퐹; 퐹; 퐹; 퐹; ) HANGUL SYLLABLE PWAENG +D43A;D43A;1111 116B 11BD;D43A;1111 116B 11BD; # (퐺; 퐺; 퐺; 퐺; 퐺; ) HANGUL SYLLABLE PWAEJ +D43B;D43B;1111 116B 11BE;D43B;1111 116B 11BE; # (퐻; 퐻; 퐻; 퐻; 퐻; ) HANGUL SYLLABLE PWAEC +D43C;D43C;1111 116B 11BF;D43C;1111 116B 11BF; # (퐼; 퐼; 퐼; 퐼; 퐼; ) HANGUL SYLLABLE PWAEK +D43D;D43D;1111 116B 11C0;D43D;1111 116B 11C0; # (퐽; 퐽; 퐽; 퐽; 퐽; ) HANGUL SYLLABLE PWAET +D43E;D43E;1111 116B 11C1;D43E;1111 116B 11C1; # (퐾; 퐾; 퐾; 퐾; 퐾; ) HANGUL SYLLABLE PWAEP +D43F;D43F;1111 116B 11C2;D43F;1111 116B 11C2; # (퐿; 퐿; 퐿; 퐿; 퐿; ) HANGUL SYLLABLE PWAEH +D440;D440;1111 116C;D440;1111 116C; # (푀; 푀; 푀; 푀; 푀; ) HANGUL SYLLABLE POE +D441;D441;1111 116C 11A8;D441;1111 116C 11A8; # (푁; 푁; 푁; 푁; 푁; ) HANGUL SYLLABLE POEG +D442;D442;1111 116C 11A9;D442;1111 116C 11A9; # (푂; 푂; 푂; 푂; 푂; ) HANGUL SYLLABLE POEGG +D443;D443;1111 116C 11AA;D443;1111 116C 11AA; # (푃; 푃; 푃; 푃; 푃; ) HANGUL SYLLABLE POEGS +D444;D444;1111 116C 11AB;D444;1111 116C 11AB; # (푄; 푄; 푄; 푄; 푄; ) HANGUL SYLLABLE POEN +D445;D445;1111 116C 11AC;D445;1111 116C 11AC; # (푅; 푅; 푅; 푅; 푅; ) HANGUL SYLLABLE POENJ +D446;D446;1111 116C 11AD;D446;1111 116C 11AD; # (푆; 푆; 푆; 푆; 푆; ) HANGUL SYLLABLE POENH +D447;D447;1111 116C 11AE;D447;1111 116C 11AE; # (푇; 푇; 푇; 푇; 푇; ) HANGUL SYLLABLE POED +D448;D448;1111 116C 11AF;D448;1111 116C 11AF; # (푈; 푈; 푈; 푈; 푈; ) HANGUL SYLLABLE POEL +D449;D449;1111 116C 11B0;D449;1111 116C 11B0; # (푉; 푉; 푉; 푉; 푉; ) HANGUL SYLLABLE POELG +D44A;D44A;1111 116C 11B1;D44A;1111 116C 11B1; # (푊; 푊; 푊; 푊; 푊; ) HANGUL SYLLABLE POELM +D44B;D44B;1111 116C 11B2;D44B;1111 116C 11B2; # (푋; 푋; 푋; 푋; 푋; ) HANGUL SYLLABLE POELB +D44C;D44C;1111 116C 11B3;D44C;1111 116C 11B3; # (푌; 푌; 푌; 푌; 푌; ) HANGUL SYLLABLE POELS +D44D;D44D;1111 116C 11B4;D44D;1111 116C 11B4; # (푍; 푍; 푍; 푍; 푍; ) HANGUL SYLLABLE POELT +D44E;D44E;1111 116C 11B5;D44E;1111 116C 11B5; # (푎; 푎; 푎; 푎; 푎; ) HANGUL SYLLABLE POELP +D44F;D44F;1111 116C 11B6;D44F;1111 116C 11B6; # (푏; 푏; 푏; 푏; 푏; ) HANGUL SYLLABLE POELH +D450;D450;1111 116C 11B7;D450;1111 116C 11B7; # (푐; 푐; 푐; 푐; 푐; ) HANGUL SYLLABLE POEM +D451;D451;1111 116C 11B8;D451;1111 116C 11B8; # (푑; 푑; 푑; 푑; 푑; ) HANGUL SYLLABLE POEB +D452;D452;1111 116C 11B9;D452;1111 116C 11B9; # (푒; 푒; 푒; 푒; 푒; ) HANGUL SYLLABLE POEBS +D453;D453;1111 116C 11BA;D453;1111 116C 11BA; # (푓; 푓; 푓; 푓; 푓; ) HANGUL SYLLABLE POES +D454;D454;1111 116C 11BB;D454;1111 116C 11BB; # (푔; 푔; 푔; 푔; 푔; ) HANGUL SYLLABLE POESS +D455;D455;1111 116C 11BC;D455;1111 116C 11BC; # (푕; 푕; 푕; 푕; 푕; ) HANGUL SYLLABLE POENG +D456;D456;1111 116C 11BD;D456;1111 116C 11BD; # (푖; 푖; 푖; 푖; 푖; ) HANGUL SYLLABLE POEJ +D457;D457;1111 116C 11BE;D457;1111 116C 11BE; # (푗; 푗; 푗; 푗; 푗; ) HANGUL SYLLABLE POEC +D458;D458;1111 116C 11BF;D458;1111 116C 11BF; # (푘; 푘; 푘; 푘; 푘; ) HANGUL SYLLABLE POEK +D459;D459;1111 116C 11C0;D459;1111 116C 11C0; # (푙; 푙; 푙; 푙; 푙; ) HANGUL SYLLABLE POET +D45A;D45A;1111 116C 11C1;D45A;1111 116C 11C1; # (푚; 푚; 푚; 푚; 푚; ) HANGUL SYLLABLE POEP +D45B;D45B;1111 116C 11C2;D45B;1111 116C 11C2; # (푛; 푛; 푛; 푛; 푛; ) HANGUL SYLLABLE POEH +D45C;D45C;1111 116D;D45C;1111 116D; # (표; 표; 표; 표; 표; ) HANGUL SYLLABLE PYO +D45D;D45D;1111 116D 11A8;D45D;1111 116D 11A8; # (푝; 푝; 푝; 푝; 푝; ) HANGUL SYLLABLE PYOG +D45E;D45E;1111 116D 11A9;D45E;1111 116D 11A9; # (푞; 푞; 푞; 푞; 푞; ) HANGUL SYLLABLE PYOGG +D45F;D45F;1111 116D 11AA;D45F;1111 116D 11AA; # (푟; 푟; 푟; 푟; 푟; ) HANGUL SYLLABLE PYOGS +D460;D460;1111 116D 11AB;D460;1111 116D 11AB; # (푠; 푠; 푠; 푠; 푠; ) HANGUL SYLLABLE PYON +D461;D461;1111 116D 11AC;D461;1111 116D 11AC; # (푡; 푡; 푡; 푡; 푡; ) HANGUL SYLLABLE PYONJ +D462;D462;1111 116D 11AD;D462;1111 116D 11AD; # (푢; 푢; 푢; 푢; 푢; ) HANGUL SYLLABLE PYONH +D463;D463;1111 116D 11AE;D463;1111 116D 11AE; # (푣; 푣; 푣; 푣; 푣; ) HANGUL SYLLABLE PYOD +D464;D464;1111 116D 11AF;D464;1111 116D 11AF; # (푤; 푤; 푤; 푤; 푤; ) HANGUL SYLLABLE PYOL +D465;D465;1111 116D 11B0;D465;1111 116D 11B0; # (푥; 푥; 푥; 푥; 푥; ) HANGUL SYLLABLE PYOLG +D466;D466;1111 116D 11B1;D466;1111 116D 11B1; # (푦; 푦; 푦; 푦; 푦; ) HANGUL SYLLABLE PYOLM +D467;D467;1111 116D 11B2;D467;1111 116D 11B2; # (푧; 푧; 푧; 푧; 푧; ) HANGUL SYLLABLE PYOLB +D468;D468;1111 116D 11B3;D468;1111 116D 11B3; # (푨; 푨; 푨; 푨; 푨; ) HANGUL SYLLABLE PYOLS +D469;D469;1111 116D 11B4;D469;1111 116D 11B4; # (푩; 푩; 푩; 푩; 푩; ) HANGUL SYLLABLE PYOLT +D46A;D46A;1111 116D 11B5;D46A;1111 116D 11B5; # (푪; 푪; 푪; 푪; 푪; ) HANGUL SYLLABLE PYOLP +D46B;D46B;1111 116D 11B6;D46B;1111 116D 11B6; # (푫; 푫; 푫; 푫; 푫; ) HANGUL SYLLABLE PYOLH +D46C;D46C;1111 116D 11B7;D46C;1111 116D 11B7; # (푬; 푬; 푬; 푬; 푬; ) HANGUL SYLLABLE PYOM +D46D;D46D;1111 116D 11B8;D46D;1111 116D 11B8; # (푭; 푭; 푭; 푭; 푭; ) HANGUL SYLLABLE PYOB +D46E;D46E;1111 116D 11B9;D46E;1111 116D 11B9; # (푮; 푮; 푮; 푮; 푮; ) HANGUL SYLLABLE PYOBS +D46F;D46F;1111 116D 11BA;D46F;1111 116D 11BA; # (푯; 푯; 푯; 푯; 푯; ) HANGUL SYLLABLE PYOS +D470;D470;1111 116D 11BB;D470;1111 116D 11BB; # (푰; 푰; 푰; 푰; 푰; ) HANGUL SYLLABLE PYOSS +D471;D471;1111 116D 11BC;D471;1111 116D 11BC; # (푱; 푱; 푱; 푱; 푱; ) HANGUL SYLLABLE PYONG +D472;D472;1111 116D 11BD;D472;1111 116D 11BD; # (푲; 푲; 푲; 푲; 푲; ) HANGUL SYLLABLE PYOJ +D473;D473;1111 116D 11BE;D473;1111 116D 11BE; # (푳; 푳; 푳; 푳; 푳; ) HANGUL SYLLABLE PYOC +D474;D474;1111 116D 11BF;D474;1111 116D 11BF; # (푴; 푴; 푴; 푴; 푴; ) HANGUL SYLLABLE PYOK +D475;D475;1111 116D 11C0;D475;1111 116D 11C0; # (푵; 푵; 푵; 푵; 푵; ) HANGUL SYLLABLE PYOT +D476;D476;1111 116D 11C1;D476;1111 116D 11C1; # (푶; 푶; 푶; 푶; 푶; ) HANGUL SYLLABLE PYOP +D477;D477;1111 116D 11C2;D477;1111 116D 11C2; # (푷; 푷; 푷; 푷; 푷; ) HANGUL SYLLABLE PYOH +D478;D478;1111 116E;D478;1111 116E; # (푸; 푸; 푸; 푸; 푸; ) HANGUL SYLLABLE PU +D479;D479;1111 116E 11A8;D479;1111 116E 11A8; # (푹; 푹; 푹; 푹; 푹; ) HANGUL SYLLABLE PUG +D47A;D47A;1111 116E 11A9;D47A;1111 116E 11A9; # (푺; 푺; 푺; 푺; 푺; ) HANGUL SYLLABLE PUGG +D47B;D47B;1111 116E 11AA;D47B;1111 116E 11AA; # (푻; 푻; 푻; 푻; 푻; ) HANGUL SYLLABLE PUGS +D47C;D47C;1111 116E 11AB;D47C;1111 116E 11AB; # (푼; 푼; 푼; 푼; 푼; ) HANGUL SYLLABLE PUN +D47D;D47D;1111 116E 11AC;D47D;1111 116E 11AC; # (푽; 푽; 푽; 푽; 푽; ) HANGUL SYLLABLE PUNJ +D47E;D47E;1111 116E 11AD;D47E;1111 116E 11AD; # (푾; 푾; 푾; 푾; 푾; ) HANGUL SYLLABLE PUNH +D47F;D47F;1111 116E 11AE;D47F;1111 116E 11AE; # (푿; 푿; 푿; 푿; 푿; ) HANGUL SYLLABLE PUD +D480;D480;1111 116E 11AF;D480;1111 116E 11AF; # (풀; 풀; 풀; 풀; 풀; ) HANGUL SYLLABLE PUL +D481;D481;1111 116E 11B0;D481;1111 116E 11B0; # (풁; 풁; 풁; 풁; 풁; ) HANGUL SYLLABLE PULG +D482;D482;1111 116E 11B1;D482;1111 116E 11B1; # (풂; 풂; 풂; 풂; 풂; ) HANGUL SYLLABLE PULM +D483;D483;1111 116E 11B2;D483;1111 116E 11B2; # (풃; 풃; 풃; 풃; 풃; ) HANGUL SYLLABLE PULB +D484;D484;1111 116E 11B3;D484;1111 116E 11B3; # (풄; 풄; 풄; 풄; 풄; ) HANGUL SYLLABLE PULS +D485;D485;1111 116E 11B4;D485;1111 116E 11B4; # (풅; 풅; 풅; 풅; 풅; ) HANGUL SYLLABLE PULT +D486;D486;1111 116E 11B5;D486;1111 116E 11B5; # (풆; 풆; 풆; 풆; 풆; ) HANGUL SYLLABLE PULP +D487;D487;1111 116E 11B6;D487;1111 116E 11B6; # (풇; 풇; 풇; 풇; 풇; ) HANGUL SYLLABLE PULH +D488;D488;1111 116E 11B7;D488;1111 116E 11B7; # (품; 품; 품; 품; 품; ) HANGUL SYLLABLE PUM +D489;D489;1111 116E 11B8;D489;1111 116E 11B8; # (풉; 풉; 풉; 풉; 풉; ) HANGUL SYLLABLE PUB +D48A;D48A;1111 116E 11B9;D48A;1111 116E 11B9; # (풊; 풊; 풊; 풊; 풊; ) HANGUL SYLLABLE PUBS +D48B;D48B;1111 116E 11BA;D48B;1111 116E 11BA; # (풋; 풋; 풋; 풋; 풋; ) HANGUL SYLLABLE PUS +D48C;D48C;1111 116E 11BB;D48C;1111 116E 11BB; # (풌; 풌; 풌; 풌; 풌; ) HANGUL SYLLABLE PUSS +D48D;D48D;1111 116E 11BC;D48D;1111 116E 11BC; # (풍; 풍; 풍; 풍; 풍; ) HANGUL SYLLABLE PUNG +D48E;D48E;1111 116E 11BD;D48E;1111 116E 11BD; # (풎; 풎; 풎; 풎; 풎; ) HANGUL SYLLABLE PUJ +D48F;D48F;1111 116E 11BE;D48F;1111 116E 11BE; # (풏; 풏; 풏; 풏; 풏; ) HANGUL SYLLABLE PUC +D490;D490;1111 116E 11BF;D490;1111 116E 11BF; # (풐; 풐; 풐; 풐; 풐; ) HANGUL SYLLABLE PUK +D491;D491;1111 116E 11C0;D491;1111 116E 11C0; # (풑; 풑; 풑; 풑; 풑; ) HANGUL SYLLABLE PUT +D492;D492;1111 116E 11C1;D492;1111 116E 11C1; # (풒; 풒; 풒; 풒; 풒; ) HANGUL SYLLABLE PUP +D493;D493;1111 116E 11C2;D493;1111 116E 11C2; # (풓; 풓; 풓; 풓; 풓; ) HANGUL SYLLABLE PUH +D494;D494;1111 116F;D494;1111 116F; # (풔; 풔; 풔; 풔; 풔; ) HANGUL SYLLABLE PWEO +D495;D495;1111 116F 11A8;D495;1111 116F 11A8; # (풕; 풕; 풕; 풕; 풕; ) HANGUL SYLLABLE PWEOG +D496;D496;1111 116F 11A9;D496;1111 116F 11A9; # (풖; 풖; 풖; 풖; 풖; ) HANGUL SYLLABLE PWEOGG +D497;D497;1111 116F 11AA;D497;1111 116F 11AA; # (풗; 풗; 풗; 풗; 풗; ) HANGUL SYLLABLE PWEOGS +D498;D498;1111 116F 11AB;D498;1111 116F 11AB; # (풘; 풘; 풘; 풘; 풘; ) HANGUL SYLLABLE PWEON +D499;D499;1111 116F 11AC;D499;1111 116F 11AC; # (풙; 풙; 풙; 풙; 풙; ) HANGUL SYLLABLE PWEONJ +D49A;D49A;1111 116F 11AD;D49A;1111 116F 11AD; # (풚; 풚; 풚; 풚; 풚; ) HANGUL SYLLABLE PWEONH +D49B;D49B;1111 116F 11AE;D49B;1111 116F 11AE; # (풛; 풛; 풛; 풛; 풛; ) HANGUL SYLLABLE PWEOD +D49C;D49C;1111 116F 11AF;D49C;1111 116F 11AF; # (풜; 풜; 풜; 풜; 풜; ) HANGUL SYLLABLE PWEOL +D49D;D49D;1111 116F 11B0;D49D;1111 116F 11B0; # (풝; 풝; 풝; 풝; 풝; ) HANGUL SYLLABLE PWEOLG +D49E;D49E;1111 116F 11B1;D49E;1111 116F 11B1; # (풞; 풞; 풞; 풞; 풞; ) HANGUL SYLLABLE PWEOLM +D49F;D49F;1111 116F 11B2;D49F;1111 116F 11B2; # (풟; 풟; 풟; 풟; 풟; ) HANGUL SYLLABLE PWEOLB +D4A0;D4A0;1111 116F 11B3;D4A0;1111 116F 11B3; # (풠; 풠; 풠; 풠; 풠; ) HANGUL SYLLABLE PWEOLS +D4A1;D4A1;1111 116F 11B4;D4A1;1111 116F 11B4; # (풡; 풡; 풡; 풡; 풡; ) HANGUL SYLLABLE PWEOLT +D4A2;D4A2;1111 116F 11B5;D4A2;1111 116F 11B5; # (풢; 풢; 풢; 풢; 풢; ) HANGUL SYLLABLE PWEOLP +D4A3;D4A3;1111 116F 11B6;D4A3;1111 116F 11B6; # (풣; 풣; 풣; 풣; 풣; ) HANGUL SYLLABLE PWEOLH +D4A4;D4A4;1111 116F 11B7;D4A4;1111 116F 11B7; # (풤; 풤; 풤; 풤; 풤; ) HANGUL SYLLABLE PWEOM +D4A5;D4A5;1111 116F 11B8;D4A5;1111 116F 11B8; # (풥; 풥; 풥; 풥; 풥; ) HANGUL SYLLABLE PWEOB +D4A6;D4A6;1111 116F 11B9;D4A6;1111 116F 11B9; # (풦; 풦; 풦; 풦; 풦; ) HANGUL SYLLABLE PWEOBS +D4A7;D4A7;1111 116F 11BA;D4A7;1111 116F 11BA; # (풧; 풧; 풧; 풧; 풧; ) HANGUL SYLLABLE PWEOS +D4A8;D4A8;1111 116F 11BB;D4A8;1111 116F 11BB; # (풨; 풨; 풨; 풨; 풨; ) HANGUL SYLLABLE PWEOSS +D4A9;D4A9;1111 116F 11BC;D4A9;1111 116F 11BC; # (풩; 풩; 풩; 풩; 풩; ) HANGUL SYLLABLE PWEONG +D4AA;D4AA;1111 116F 11BD;D4AA;1111 116F 11BD; # (풪; 풪; 풪; 풪; 풪; ) HANGUL SYLLABLE PWEOJ +D4AB;D4AB;1111 116F 11BE;D4AB;1111 116F 11BE; # (풫; 풫; 풫; 풫; 풫; ) HANGUL SYLLABLE PWEOC +D4AC;D4AC;1111 116F 11BF;D4AC;1111 116F 11BF; # (풬; 풬; 풬; 풬; 풬; ) HANGUL SYLLABLE PWEOK +D4AD;D4AD;1111 116F 11C0;D4AD;1111 116F 11C0; # (풭; 풭; 풭; 풭; 풭; ) HANGUL SYLLABLE PWEOT +D4AE;D4AE;1111 116F 11C1;D4AE;1111 116F 11C1; # (풮; 풮; 풮; 풮; 풮; ) HANGUL SYLLABLE PWEOP +D4AF;D4AF;1111 116F 11C2;D4AF;1111 116F 11C2; # (풯; 풯; 풯; 풯; 풯; ) HANGUL SYLLABLE PWEOH +D4B0;D4B0;1111 1170;D4B0;1111 1170; # (풰; 풰; 풰; 풰; 풰; ) HANGUL SYLLABLE PWE +D4B1;D4B1;1111 1170 11A8;D4B1;1111 1170 11A8; # (풱; 풱; 풱; 풱; 풱; ) HANGUL SYLLABLE PWEG +D4B2;D4B2;1111 1170 11A9;D4B2;1111 1170 11A9; # (풲; 풲; 풲; 풲; 풲; ) HANGUL SYLLABLE PWEGG +D4B3;D4B3;1111 1170 11AA;D4B3;1111 1170 11AA; # (풳; 풳; 풳; 풳; 풳; ) HANGUL SYLLABLE PWEGS +D4B4;D4B4;1111 1170 11AB;D4B4;1111 1170 11AB; # (풴; 풴; 풴; 풴; 풴; ) HANGUL SYLLABLE PWEN +D4B5;D4B5;1111 1170 11AC;D4B5;1111 1170 11AC; # (풵; 풵; 풵; 풵; 풵; ) HANGUL SYLLABLE PWENJ +D4B6;D4B6;1111 1170 11AD;D4B6;1111 1170 11AD; # (풶; 풶; 풶; 풶; 풶; ) HANGUL SYLLABLE PWENH +D4B7;D4B7;1111 1170 11AE;D4B7;1111 1170 11AE; # (풷; 풷; 풷; 풷; 풷; ) HANGUL SYLLABLE PWED +D4B8;D4B8;1111 1170 11AF;D4B8;1111 1170 11AF; # (풸; 풸; 풸; 풸; 풸; ) HANGUL SYLLABLE PWEL +D4B9;D4B9;1111 1170 11B0;D4B9;1111 1170 11B0; # (풹; 풹; 풹; 풹; 풹; ) HANGUL SYLLABLE PWELG +D4BA;D4BA;1111 1170 11B1;D4BA;1111 1170 11B1; # (풺; 풺; 풺; 풺; 풺; ) HANGUL SYLLABLE PWELM +D4BB;D4BB;1111 1170 11B2;D4BB;1111 1170 11B2; # (풻; 풻; 풻; 풻; 풻; ) HANGUL SYLLABLE PWELB +D4BC;D4BC;1111 1170 11B3;D4BC;1111 1170 11B3; # (풼; 풼; 풼; 풼; 풼; ) HANGUL SYLLABLE PWELS +D4BD;D4BD;1111 1170 11B4;D4BD;1111 1170 11B4; # (풽; 풽; 풽; 풽; 풽; ) HANGUL SYLLABLE PWELT +D4BE;D4BE;1111 1170 11B5;D4BE;1111 1170 11B5; # (풾; 풾; 풾; 풾; 풾; ) HANGUL SYLLABLE PWELP +D4BF;D4BF;1111 1170 11B6;D4BF;1111 1170 11B6; # (풿; 풿; 풿; 풿; 풿; ) HANGUL SYLLABLE PWELH +D4C0;D4C0;1111 1170 11B7;D4C0;1111 1170 11B7; # (퓀; 퓀; 퓀; 퓀; 퓀; ) HANGUL SYLLABLE PWEM +D4C1;D4C1;1111 1170 11B8;D4C1;1111 1170 11B8; # (퓁; 퓁; 퓁; 퓁; 퓁; ) HANGUL SYLLABLE PWEB +D4C2;D4C2;1111 1170 11B9;D4C2;1111 1170 11B9; # (퓂; 퓂; 퓂; 퓂; 퓂; ) HANGUL SYLLABLE PWEBS +D4C3;D4C3;1111 1170 11BA;D4C3;1111 1170 11BA; # (퓃; 퓃; 퓃; 퓃; 퓃; ) HANGUL SYLLABLE PWES +D4C4;D4C4;1111 1170 11BB;D4C4;1111 1170 11BB; # (퓄; 퓄; 퓄; 퓄; 퓄; ) HANGUL SYLLABLE PWESS +D4C5;D4C5;1111 1170 11BC;D4C5;1111 1170 11BC; # (퓅; 퓅; 퓅; 퓅; 퓅; ) HANGUL SYLLABLE PWENG +D4C6;D4C6;1111 1170 11BD;D4C6;1111 1170 11BD; # (퓆; 퓆; 퓆; 퓆; 퓆; ) HANGUL SYLLABLE PWEJ +D4C7;D4C7;1111 1170 11BE;D4C7;1111 1170 11BE; # (퓇; 퓇; 퓇; 퓇; 퓇; ) HANGUL SYLLABLE PWEC +D4C8;D4C8;1111 1170 11BF;D4C8;1111 1170 11BF; # (퓈; 퓈; 퓈; 퓈; 퓈; ) HANGUL SYLLABLE PWEK +D4C9;D4C9;1111 1170 11C0;D4C9;1111 1170 11C0; # (퓉; 퓉; 퓉; 퓉; 퓉; ) HANGUL SYLLABLE PWET +D4CA;D4CA;1111 1170 11C1;D4CA;1111 1170 11C1; # (퓊; 퓊; 퓊; 퓊; 퓊; ) HANGUL SYLLABLE PWEP +D4CB;D4CB;1111 1170 11C2;D4CB;1111 1170 11C2; # (퓋; 퓋; 퓋; 퓋; 퓋; ) HANGUL SYLLABLE PWEH +D4CC;D4CC;1111 1171;D4CC;1111 1171; # (퓌; 퓌; 퓌; 퓌; 퓌; ) HANGUL SYLLABLE PWI +D4CD;D4CD;1111 1171 11A8;D4CD;1111 1171 11A8; # (퓍; 퓍; 퓍; 퓍; 퓍; ) HANGUL SYLLABLE PWIG +D4CE;D4CE;1111 1171 11A9;D4CE;1111 1171 11A9; # (퓎; 퓎; 퓎; 퓎; 퓎; ) HANGUL SYLLABLE PWIGG +D4CF;D4CF;1111 1171 11AA;D4CF;1111 1171 11AA; # (퓏; 퓏; 퓏; 퓏; 퓏; ) HANGUL SYLLABLE PWIGS +D4D0;D4D0;1111 1171 11AB;D4D0;1111 1171 11AB; # (퓐; 퓐; 퓐; 퓐; 퓐; ) HANGUL SYLLABLE PWIN +D4D1;D4D1;1111 1171 11AC;D4D1;1111 1171 11AC; # (퓑; 퓑; 퓑; 퓑; 퓑; ) HANGUL SYLLABLE PWINJ +D4D2;D4D2;1111 1171 11AD;D4D2;1111 1171 11AD; # (퓒; 퓒; 퓒; 퓒; 퓒; ) HANGUL SYLLABLE PWINH +D4D3;D4D3;1111 1171 11AE;D4D3;1111 1171 11AE; # (퓓; 퓓; 퓓; 퓓; 퓓; ) HANGUL SYLLABLE PWID +D4D4;D4D4;1111 1171 11AF;D4D4;1111 1171 11AF; # (퓔; 퓔; 퓔; 퓔; 퓔; ) HANGUL SYLLABLE PWIL +D4D5;D4D5;1111 1171 11B0;D4D5;1111 1171 11B0; # (퓕; 퓕; 퓕; 퓕; 퓕; ) HANGUL SYLLABLE PWILG +D4D6;D4D6;1111 1171 11B1;D4D6;1111 1171 11B1; # (퓖; 퓖; 퓖; 퓖; 퓖; ) HANGUL SYLLABLE PWILM +D4D7;D4D7;1111 1171 11B2;D4D7;1111 1171 11B2; # (퓗; 퓗; 퓗; 퓗; 퓗; ) HANGUL SYLLABLE PWILB +D4D8;D4D8;1111 1171 11B3;D4D8;1111 1171 11B3; # (퓘; 퓘; 퓘; 퓘; 퓘; ) HANGUL SYLLABLE PWILS +D4D9;D4D9;1111 1171 11B4;D4D9;1111 1171 11B4; # (퓙; 퓙; 퓙; 퓙; 퓙; ) HANGUL SYLLABLE PWILT +D4DA;D4DA;1111 1171 11B5;D4DA;1111 1171 11B5; # (퓚; 퓚; 퓚; 퓚; 퓚; ) HANGUL SYLLABLE PWILP +D4DB;D4DB;1111 1171 11B6;D4DB;1111 1171 11B6; # (퓛; 퓛; 퓛; 퓛; 퓛; ) HANGUL SYLLABLE PWILH +D4DC;D4DC;1111 1171 11B7;D4DC;1111 1171 11B7; # (퓜; 퓜; 퓜; 퓜; 퓜; ) HANGUL SYLLABLE PWIM +D4DD;D4DD;1111 1171 11B8;D4DD;1111 1171 11B8; # (퓝; 퓝; 퓝; 퓝; 퓝; ) HANGUL SYLLABLE PWIB +D4DE;D4DE;1111 1171 11B9;D4DE;1111 1171 11B9; # (퓞; 퓞; 퓞; 퓞; 퓞; ) HANGUL SYLLABLE PWIBS +D4DF;D4DF;1111 1171 11BA;D4DF;1111 1171 11BA; # (퓟; 퓟; 퓟; 퓟; 퓟; ) HANGUL SYLLABLE PWIS +D4E0;D4E0;1111 1171 11BB;D4E0;1111 1171 11BB; # (퓠; 퓠; 퓠; 퓠; 퓠; ) HANGUL SYLLABLE PWISS +D4E1;D4E1;1111 1171 11BC;D4E1;1111 1171 11BC; # (퓡; 퓡; 퓡; 퓡; 퓡; ) HANGUL SYLLABLE PWING +D4E2;D4E2;1111 1171 11BD;D4E2;1111 1171 11BD; # (퓢; 퓢; 퓢; 퓢; 퓢; ) HANGUL SYLLABLE PWIJ +D4E3;D4E3;1111 1171 11BE;D4E3;1111 1171 11BE; # (퓣; 퓣; 퓣; 퓣; 퓣; ) HANGUL SYLLABLE PWIC +D4E4;D4E4;1111 1171 11BF;D4E4;1111 1171 11BF; # (퓤; 퓤; 퓤; 퓤; 퓤; ) HANGUL SYLLABLE PWIK +D4E5;D4E5;1111 1171 11C0;D4E5;1111 1171 11C0; # (퓥; 퓥; 퓥; 퓥; 퓥; ) HANGUL SYLLABLE PWIT +D4E6;D4E6;1111 1171 11C1;D4E6;1111 1171 11C1; # (퓦; 퓦; 퓦; 퓦; 퓦; ) HANGUL SYLLABLE PWIP +D4E7;D4E7;1111 1171 11C2;D4E7;1111 1171 11C2; # (퓧; 퓧; 퓧; 퓧; 퓧; ) HANGUL SYLLABLE PWIH +D4E8;D4E8;1111 1172;D4E8;1111 1172; # (퓨; 퓨; 퓨; 퓨; 퓨; ) HANGUL SYLLABLE PYU +D4E9;D4E9;1111 1172 11A8;D4E9;1111 1172 11A8; # (퓩; 퓩; 퓩; 퓩; 퓩; ) HANGUL SYLLABLE PYUG +D4EA;D4EA;1111 1172 11A9;D4EA;1111 1172 11A9; # (퓪; 퓪; 퓪; 퓪; 퓪; ) HANGUL SYLLABLE PYUGG +D4EB;D4EB;1111 1172 11AA;D4EB;1111 1172 11AA; # (퓫; 퓫; 퓫; 퓫; 퓫; ) HANGUL SYLLABLE PYUGS +D4EC;D4EC;1111 1172 11AB;D4EC;1111 1172 11AB; # (퓬; 퓬; 퓬; 퓬; 퓬; ) HANGUL SYLLABLE PYUN +D4ED;D4ED;1111 1172 11AC;D4ED;1111 1172 11AC; # (퓭; 퓭; 퓭; 퓭; 퓭; ) HANGUL SYLLABLE PYUNJ +D4EE;D4EE;1111 1172 11AD;D4EE;1111 1172 11AD; # (퓮; 퓮; 퓮; 퓮; 퓮; ) HANGUL SYLLABLE PYUNH +D4EF;D4EF;1111 1172 11AE;D4EF;1111 1172 11AE; # (퓯; 퓯; 퓯; 퓯; 퓯; ) HANGUL SYLLABLE PYUD +D4F0;D4F0;1111 1172 11AF;D4F0;1111 1172 11AF; # (퓰; 퓰; 퓰; 퓰; 퓰; ) HANGUL SYLLABLE PYUL +D4F1;D4F1;1111 1172 11B0;D4F1;1111 1172 11B0; # (퓱; 퓱; 퓱; 퓱; 퓱; ) HANGUL SYLLABLE PYULG +D4F2;D4F2;1111 1172 11B1;D4F2;1111 1172 11B1; # (퓲; 퓲; 퓲; 퓲; 퓲; ) HANGUL SYLLABLE PYULM +D4F3;D4F3;1111 1172 11B2;D4F3;1111 1172 11B2; # (퓳; 퓳; 퓳; 퓳; 퓳; ) HANGUL SYLLABLE PYULB +D4F4;D4F4;1111 1172 11B3;D4F4;1111 1172 11B3; # (퓴; 퓴; 퓴; 퓴; 퓴; ) HANGUL SYLLABLE PYULS +D4F5;D4F5;1111 1172 11B4;D4F5;1111 1172 11B4; # (퓵; 퓵; 퓵; 퓵; 퓵; ) HANGUL SYLLABLE PYULT +D4F6;D4F6;1111 1172 11B5;D4F6;1111 1172 11B5; # (퓶; 퓶; 퓶; 퓶; 퓶; ) HANGUL SYLLABLE PYULP +D4F7;D4F7;1111 1172 11B6;D4F7;1111 1172 11B6; # (퓷; 퓷; 퓷; 퓷; 퓷; ) HANGUL SYLLABLE PYULH +D4F8;D4F8;1111 1172 11B7;D4F8;1111 1172 11B7; # (퓸; 퓸; 퓸; 퓸; 퓸; ) HANGUL SYLLABLE PYUM +D4F9;D4F9;1111 1172 11B8;D4F9;1111 1172 11B8; # (퓹; 퓹; 퓹; 퓹; 퓹; ) HANGUL SYLLABLE PYUB +D4FA;D4FA;1111 1172 11B9;D4FA;1111 1172 11B9; # (퓺; 퓺; 퓺; 퓺; 퓺; ) HANGUL SYLLABLE PYUBS +D4FB;D4FB;1111 1172 11BA;D4FB;1111 1172 11BA; # (퓻; 퓻; 퓻; 퓻; 퓻; ) HANGUL SYLLABLE PYUS +D4FC;D4FC;1111 1172 11BB;D4FC;1111 1172 11BB; # (퓼; 퓼; 퓼; 퓼; 퓼; ) HANGUL SYLLABLE PYUSS +D4FD;D4FD;1111 1172 11BC;D4FD;1111 1172 11BC; # (퓽; 퓽; 퓽; 퓽; 퓽; ) HANGUL SYLLABLE PYUNG +D4FE;D4FE;1111 1172 11BD;D4FE;1111 1172 11BD; # (퓾; 퓾; 퓾; 퓾; 퓾; ) HANGUL SYLLABLE PYUJ +D4FF;D4FF;1111 1172 11BE;D4FF;1111 1172 11BE; # (퓿; 퓿; 퓿; 퓿; 퓿; ) HANGUL SYLLABLE PYUC +D500;D500;1111 1172 11BF;D500;1111 1172 11BF; # (픀; 픀; 픀; 픀; 픀; ) HANGUL SYLLABLE PYUK +D501;D501;1111 1172 11C0;D501;1111 1172 11C0; # (픁; 픁; 픁; 픁; 픁; ) HANGUL SYLLABLE PYUT +D502;D502;1111 1172 11C1;D502;1111 1172 11C1; # (픂; 픂; 픂; 픂; 픂; ) HANGUL SYLLABLE PYUP +D503;D503;1111 1172 11C2;D503;1111 1172 11C2; # (픃; 픃; 픃; 픃; 픃; ) HANGUL SYLLABLE PYUH +D504;D504;1111 1173;D504;1111 1173; # (프; 프; 프; 프; 프; ) HANGUL SYLLABLE PEU +D505;D505;1111 1173 11A8;D505;1111 1173 11A8; # (픅; 픅; 픅; 픅; 픅; ) HANGUL SYLLABLE PEUG +D506;D506;1111 1173 11A9;D506;1111 1173 11A9; # (픆; 픆; 픆; 픆; 픆; ) HANGUL SYLLABLE PEUGG +D507;D507;1111 1173 11AA;D507;1111 1173 11AA; # (픇; 픇; 픇; 픇; 픇; ) HANGUL SYLLABLE PEUGS +D508;D508;1111 1173 11AB;D508;1111 1173 11AB; # (픈; 픈; 픈; 픈; 픈; ) HANGUL SYLLABLE PEUN +D509;D509;1111 1173 11AC;D509;1111 1173 11AC; # (픉; 픉; 픉; 픉; 픉; ) HANGUL SYLLABLE PEUNJ +D50A;D50A;1111 1173 11AD;D50A;1111 1173 11AD; # (픊; 픊; 픊; 픊; 픊; ) HANGUL SYLLABLE PEUNH +D50B;D50B;1111 1173 11AE;D50B;1111 1173 11AE; # (픋; 픋; 픋; 픋; 픋; ) HANGUL SYLLABLE PEUD +D50C;D50C;1111 1173 11AF;D50C;1111 1173 11AF; # (플; 플; 플; 플; 플; ) HANGUL SYLLABLE PEUL +D50D;D50D;1111 1173 11B0;D50D;1111 1173 11B0; # (픍; 픍; 픍; 픍; 픍; ) HANGUL SYLLABLE PEULG +D50E;D50E;1111 1173 11B1;D50E;1111 1173 11B1; # (픎; 픎; 픎; 픎; 픎; ) HANGUL SYLLABLE PEULM +D50F;D50F;1111 1173 11B2;D50F;1111 1173 11B2; # (픏; 픏; 픏; 픏; 픏; ) HANGUL SYLLABLE PEULB +D510;D510;1111 1173 11B3;D510;1111 1173 11B3; # (픐; 픐; 픐; 픐; 픐; ) HANGUL SYLLABLE PEULS +D511;D511;1111 1173 11B4;D511;1111 1173 11B4; # (픑; 픑; 픑; 픑; 픑; ) HANGUL SYLLABLE PEULT +D512;D512;1111 1173 11B5;D512;1111 1173 11B5; # (픒; 픒; 픒; 픒; 픒; ) HANGUL SYLLABLE PEULP +D513;D513;1111 1173 11B6;D513;1111 1173 11B6; # (픓; 픓; 픓; 픓; 픓; ) HANGUL SYLLABLE PEULH +D514;D514;1111 1173 11B7;D514;1111 1173 11B7; # (픔; 픔; 픔; 픔; 픔; ) HANGUL SYLLABLE PEUM +D515;D515;1111 1173 11B8;D515;1111 1173 11B8; # (픕; 픕; 픕; 픕; 픕; ) HANGUL SYLLABLE PEUB +D516;D516;1111 1173 11B9;D516;1111 1173 11B9; # (픖; 픖; 픖; 픖; 픖; ) HANGUL SYLLABLE PEUBS +D517;D517;1111 1173 11BA;D517;1111 1173 11BA; # (픗; 픗; 픗; 픗; 픗; ) HANGUL SYLLABLE PEUS +D518;D518;1111 1173 11BB;D518;1111 1173 11BB; # (픘; 픘; 픘; 픘; 픘; ) HANGUL SYLLABLE PEUSS +D519;D519;1111 1173 11BC;D519;1111 1173 11BC; # (픙; 픙; 픙; 픙; 픙; ) HANGUL SYLLABLE PEUNG +D51A;D51A;1111 1173 11BD;D51A;1111 1173 11BD; # (픚; 픚; 픚; 픚; 픚; ) HANGUL SYLLABLE PEUJ +D51B;D51B;1111 1173 11BE;D51B;1111 1173 11BE; # (픛; 픛; 픛; 픛; 픛; ) HANGUL SYLLABLE PEUC +D51C;D51C;1111 1173 11BF;D51C;1111 1173 11BF; # (픜; 픜; 픜; 픜; 픜; ) HANGUL SYLLABLE PEUK +D51D;D51D;1111 1173 11C0;D51D;1111 1173 11C0; # (픝; 픝; 픝; 픝; 픝; ) HANGUL SYLLABLE PEUT +D51E;D51E;1111 1173 11C1;D51E;1111 1173 11C1; # (픞; 픞; 픞; 픞; 픞; ) HANGUL SYLLABLE PEUP +D51F;D51F;1111 1173 11C2;D51F;1111 1173 11C2; # (픟; 픟; 픟; 픟; 픟; ) HANGUL SYLLABLE PEUH +D520;D520;1111 1174;D520;1111 1174; # (픠; 픠; 픠; 픠; 픠; ) HANGUL SYLLABLE PYI +D521;D521;1111 1174 11A8;D521;1111 1174 11A8; # (픡; 픡; 픡; 픡; 픡; ) HANGUL SYLLABLE PYIG +D522;D522;1111 1174 11A9;D522;1111 1174 11A9; # (픢; 픢; 픢; 픢; 픢; ) HANGUL SYLLABLE PYIGG +D523;D523;1111 1174 11AA;D523;1111 1174 11AA; # (픣; 픣; 픣; 픣; 픣; ) HANGUL SYLLABLE PYIGS +D524;D524;1111 1174 11AB;D524;1111 1174 11AB; # (픤; 픤; 픤; 픤; 픤; ) HANGUL SYLLABLE PYIN +D525;D525;1111 1174 11AC;D525;1111 1174 11AC; # (픥; 픥; 픥; 픥; 픥; ) HANGUL SYLLABLE PYINJ +D526;D526;1111 1174 11AD;D526;1111 1174 11AD; # (픦; 픦; 픦; 픦; 픦; ) HANGUL SYLLABLE PYINH +D527;D527;1111 1174 11AE;D527;1111 1174 11AE; # (픧; 픧; 픧; 픧; 픧; ) HANGUL SYLLABLE PYID +D528;D528;1111 1174 11AF;D528;1111 1174 11AF; # (픨; 픨; 픨; 픨; 픨; ) HANGUL SYLLABLE PYIL +D529;D529;1111 1174 11B0;D529;1111 1174 11B0; # (픩; 픩; 픩; 픩; 픩; ) HANGUL SYLLABLE PYILG +D52A;D52A;1111 1174 11B1;D52A;1111 1174 11B1; # (픪; 픪; 픪; 픪; 픪; ) HANGUL SYLLABLE PYILM +D52B;D52B;1111 1174 11B2;D52B;1111 1174 11B2; # (픫; 픫; 픫; 픫; 픫; ) HANGUL SYLLABLE PYILB +D52C;D52C;1111 1174 11B3;D52C;1111 1174 11B3; # (픬; 픬; 픬; 픬; 픬; ) HANGUL SYLLABLE PYILS +D52D;D52D;1111 1174 11B4;D52D;1111 1174 11B4; # (픭; 픭; 픭; 픭; 픭; ) HANGUL SYLLABLE PYILT +D52E;D52E;1111 1174 11B5;D52E;1111 1174 11B5; # (픮; 픮; 픮; 픮; 픮; ) HANGUL SYLLABLE PYILP +D52F;D52F;1111 1174 11B6;D52F;1111 1174 11B6; # (픯; 픯; 픯; 픯; 픯; ) HANGUL SYLLABLE PYILH +D530;D530;1111 1174 11B7;D530;1111 1174 11B7; # (픰; 픰; 픰; 픰; 픰; ) HANGUL SYLLABLE PYIM +D531;D531;1111 1174 11B8;D531;1111 1174 11B8; # (픱; 픱; 픱; 픱; 픱; ) HANGUL SYLLABLE PYIB +D532;D532;1111 1174 11B9;D532;1111 1174 11B9; # (픲; 픲; 픲; 픲; 픲; ) HANGUL SYLLABLE PYIBS +D533;D533;1111 1174 11BA;D533;1111 1174 11BA; # (픳; 픳; 픳; 픳; 픳; ) HANGUL SYLLABLE PYIS +D534;D534;1111 1174 11BB;D534;1111 1174 11BB; # (픴; 픴; 픴; 픴; 픴; ) HANGUL SYLLABLE PYISS +D535;D535;1111 1174 11BC;D535;1111 1174 11BC; # (픵; 픵; 픵; 픵; 픵; ) HANGUL SYLLABLE PYING +D536;D536;1111 1174 11BD;D536;1111 1174 11BD; # (픶; 픶; 픶; 픶; 픶; ) HANGUL SYLLABLE PYIJ +D537;D537;1111 1174 11BE;D537;1111 1174 11BE; # (픷; 픷; 픷; 픷; 픷; ) HANGUL SYLLABLE PYIC +D538;D538;1111 1174 11BF;D538;1111 1174 11BF; # (픸; 픸; 픸; 픸; 픸; ) HANGUL SYLLABLE PYIK +D539;D539;1111 1174 11C0;D539;1111 1174 11C0; # (픹; 픹; 픹; 픹; 픹; ) HANGUL SYLLABLE PYIT +D53A;D53A;1111 1174 11C1;D53A;1111 1174 11C1; # (픺; 픺; 픺; 픺; 픺; ) HANGUL SYLLABLE PYIP +D53B;D53B;1111 1174 11C2;D53B;1111 1174 11C2; # (픻; 픻; 픻; 픻; 픻; ) HANGUL SYLLABLE PYIH +D53C;D53C;1111 1175;D53C;1111 1175; # (피; 피; 피; 피; 피; ) HANGUL SYLLABLE PI +D53D;D53D;1111 1175 11A8;D53D;1111 1175 11A8; # (픽; 픽; 픽; 픽; 픽; ) HANGUL SYLLABLE PIG +D53E;D53E;1111 1175 11A9;D53E;1111 1175 11A9; # (픾; 픾; 픾; 픾; 픾; ) HANGUL SYLLABLE PIGG +D53F;D53F;1111 1175 11AA;D53F;1111 1175 11AA; # (픿; 픿; 픿; 픿; 픿; ) HANGUL SYLLABLE PIGS +D540;D540;1111 1175 11AB;D540;1111 1175 11AB; # (핀; 핀; 핀; 핀; 핀; ) HANGUL SYLLABLE PIN +D541;D541;1111 1175 11AC;D541;1111 1175 11AC; # (핁; 핁; 핁; 핁; 핁; ) HANGUL SYLLABLE PINJ +D542;D542;1111 1175 11AD;D542;1111 1175 11AD; # (핂; 핂; 핂; 핂; 핂; ) HANGUL SYLLABLE PINH +D543;D543;1111 1175 11AE;D543;1111 1175 11AE; # (핃; 핃; 핃; 핃; 핃; ) HANGUL SYLLABLE PID +D544;D544;1111 1175 11AF;D544;1111 1175 11AF; # (필; 필; 필; 필; 필; ) HANGUL SYLLABLE PIL +D545;D545;1111 1175 11B0;D545;1111 1175 11B0; # (핅; 핅; 핅; 핅; 핅; ) HANGUL SYLLABLE PILG +D546;D546;1111 1175 11B1;D546;1111 1175 11B1; # (핆; 핆; 핆; 핆; 핆; ) HANGUL SYLLABLE PILM +D547;D547;1111 1175 11B2;D547;1111 1175 11B2; # (핇; 핇; 핇; 핇; 핇; ) HANGUL SYLLABLE PILB +D548;D548;1111 1175 11B3;D548;1111 1175 11B3; # (핈; 핈; 핈; 핈; 핈; ) HANGUL SYLLABLE PILS +D549;D549;1111 1175 11B4;D549;1111 1175 11B4; # (핉; 핉; 핉; 핉; 핉; ) HANGUL SYLLABLE PILT +D54A;D54A;1111 1175 11B5;D54A;1111 1175 11B5; # (핊; 핊; 핊; 핊; 핊; ) HANGUL SYLLABLE PILP +D54B;D54B;1111 1175 11B6;D54B;1111 1175 11B6; # (핋; 핋; 핋; 핋; 핋; ) HANGUL SYLLABLE PILH +D54C;D54C;1111 1175 11B7;D54C;1111 1175 11B7; # (핌; 핌; 핌; 핌; 핌; ) HANGUL SYLLABLE PIM +D54D;D54D;1111 1175 11B8;D54D;1111 1175 11B8; # (핍; 핍; 핍; 핍; 핍; ) HANGUL SYLLABLE PIB +D54E;D54E;1111 1175 11B9;D54E;1111 1175 11B9; # (핎; 핎; 핎; 핎; 핎; ) HANGUL SYLLABLE PIBS +D54F;D54F;1111 1175 11BA;D54F;1111 1175 11BA; # (핏; 핏; 핏; 핏; 핏; ) HANGUL SYLLABLE PIS +D550;D550;1111 1175 11BB;D550;1111 1175 11BB; # (핐; 핐; 핐; 핐; 핐; ) HANGUL SYLLABLE PISS +D551;D551;1111 1175 11BC;D551;1111 1175 11BC; # (핑; 핑; 핑; 핑; 핑; ) HANGUL SYLLABLE PING +D552;D552;1111 1175 11BD;D552;1111 1175 11BD; # (핒; 핒; 핒; 핒; 핒; ) HANGUL SYLLABLE PIJ +D553;D553;1111 1175 11BE;D553;1111 1175 11BE; # (핓; 핓; 핓; 핓; 핓; ) HANGUL SYLLABLE PIC +D554;D554;1111 1175 11BF;D554;1111 1175 11BF; # (핔; 핔; 핔; 핔; 핔; ) HANGUL SYLLABLE PIK +D555;D555;1111 1175 11C0;D555;1111 1175 11C0; # (핕; 핕; 핕; 핕; 핕; ) HANGUL SYLLABLE PIT +D556;D556;1111 1175 11C1;D556;1111 1175 11C1; # (핖; 핖; 핖; 핖; 핖; ) HANGUL SYLLABLE PIP +D557;D557;1111 1175 11C2;D557;1111 1175 11C2; # (핗; 핗; 핗; 핗; 핗; ) HANGUL SYLLABLE PIH +D558;D558;1112 1161;D558;1112 1161; # (하; 하; 하; 하; 하; ) HANGUL SYLLABLE HA +D559;D559;1112 1161 11A8;D559;1112 1161 11A8; # (학; 학; 학; 학; 학; ) HANGUL SYLLABLE HAG +D55A;D55A;1112 1161 11A9;D55A;1112 1161 11A9; # (핚; 핚; 핚; 핚; 핚; ) HANGUL SYLLABLE HAGG +D55B;D55B;1112 1161 11AA;D55B;1112 1161 11AA; # (핛; 핛; 핛; 핛; 핛; ) HANGUL SYLLABLE HAGS +D55C;D55C;1112 1161 11AB;D55C;1112 1161 11AB; # (한; 한; 한; 한; 한; ) HANGUL SYLLABLE HAN +D55D;D55D;1112 1161 11AC;D55D;1112 1161 11AC; # (핝; 핝; 핝; 핝; 핝; ) HANGUL SYLLABLE HANJ +D55E;D55E;1112 1161 11AD;D55E;1112 1161 11AD; # (핞; 핞; 핞; 핞; 핞; ) HANGUL SYLLABLE HANH +D55F;D55F;1112 1161 11AE;D55F;1112 1161 11AE; # (핟; 핟; 핟; 핟; 핟; ) HANGUL SYLLABLE HAD +D560;D560;1112 1161 11AF;D560;1112 1161 11AF; # (할; 할; 할; 할; 할; ) HANGUL SYLLABLE HAL +D561;D561;1112 1161 11B0;D561;1112 1161 11B0; # (핡; 핡; 핡; 핡; 핡; ) HANGUL SYLLABLE HALG +D562;D562;1112 1161 11B1;D562;1112 1161 11B1; # (핢; 핢; 핢; 핢; 핢; ) HANGUL SYLLABLE HALM +D563;D563;1112 1161 11B2;D563;1112 1161 11B2; # (핣; 핣; 핣; 핣; 핣; ) HANGUL SYLLABLE HALB +D564;D564;1112 1161 11B3;D564;1112 1161 11B3; # (핤; 핤; 핤; 핤; 핤; ) HANGUL SYLLABLE HALS +D565;D565;1112 1161 11B4;D565;1112 1161 11B4; # (핥; 핥; 핥; 핥; 핥; ) HANGUL SYLLABLE HALT +D566;D566;1112 1161 11B5;D566;1112 1161 11B5; # (핦; 핦; 핦; 핦; 핦; ) HANGUL SYLLABLE HALP +D567;D567;1112 1161 11B6;D567;1112 1161 11B6; # (핧; 핧; 핧; 핧; 핧; ) HANGUL SYLLABLE HALH +D568;D568;1112 1161 11B7;D568;1112 1161 11B7; # (함; 함; 함; 함; 함; ) HANGUL SYLLABLE HAM +D569;D569;1112 1161 11B8;D569;1112 1161 11B8; # (합; 합; 합; 합; 합; ) HANGUL SYLLABLE HAB +D56A;D56A;1112 1161 11B9;D56A;1112 1161 11B9; # (핪; 핪; 핪; 핪; 핪; ) HANGUL SYLLABLE HABS +D56B;D56B;1112 1161 11BA;D56B;1112 1161 11BA; # (핫; 핫; 핫; 핫; 핫; ) HANGUL SYLLABLE HAS +D56C;D56C;1112 1161 11BB;D56C;1112 1161 11BB; # (핬; 핬; 핬; 핬; 핬; ) HANGUL SYLLABLE HASS +D56D;D56D;1112 1161 11BC;D56D;1112 1161 11BC; # (항; 항; 항; 항; 항; ) HANGUL SYLLABLE HANG +D56E;D56E;1112 1161 11BD;D56E;1112 1161 11BD; # (핮; 핮; 핮; 핮; 핮; ) HANGUL SYLLABLE HAJ +D56F;D56F;1112 1161 11BE;D56F;1112 1161 11BE; # (핯; 핯; 핯; 핯; 핯; ) HANGUL SYLLABLE HAC +D570;D570;1112 1161 11BF;D570;1112 1161 11BF; # (핰; 핰; 핰; 핰; 핰; ) HANGUL SYLLABLE HAK +D571;D571;1112 1161 11C0;D571;1112 1161 11C0; # (핱; 핱; 핱; 핱; 핱; ) HANGUL SYLLABLE HAT +D572;D572;1112 1161 11C1;D572;1112 1161 11C1; # (핲; 핲; 핲; 핲; 핲; ) HANGUL SYLLABLE HAP +D573;D573;1112 1161 11C2;D573;1112 1161 11C2; # (핳; 핳; 핳; 핳; 핳; ) HANGUL SYLLABLE HAH +D574;D574;1112 1162;D574;1112 1162; # (해; 해; 해; 해; 해; ) HANGUL SYLLABLE HAE +D575;D575;1112 1162 11A8;D575;1112 1162 11A8; # (핵; 핵; 핵; 핵; 핵; ) HANGUL SYLLABLE HAEG +D576;D576;1112 1162 11A9;D576;1112 1162 11A9; # (핶; 핶; 핶; 핶; 핶; ) HANGUL SYLLABLE HAEGG +D577;D577;1112 1162 11AA;D577;1112 1162 11AA; # (핷; 핷; 핷; 핷; 핷; ) HANGUL SYLLABLE HAEGS +D578;D578;1112 1162 11AB;D578;1112 1162 11AB; # (핸; 핸; 핸; 핸; 핸; ) HANGUL SYLLABLE HAEN +D579;D579;1112 1162 11AC;D579;1112 1162 11AC; # (핹; 핹; 핹; 핹; 핹; ) HANGUL SYLLABLE HAENJ +D57A;D57A;1112 1162 11AD;D57A;1112 1162 11AD; # (핺; 핺; 핺; 핺; 핺; ) HANGUL SYLLABLE HAENH +D57B;D57B;1112 1162 11AE;D57B;1112 1162 11AE; # (핻; 핻; 핻; 핻; 핻; ) HANGUL SYLLABLE HAED +D57C;D57C;1112 1162 11AF;D57C;1112 1162 11AF; # (핼; 핼; 핼; 핼; 핼; ) HANGUL SYLLABLE HAEL +D57D;D57D;1112 1162 11B0;D57D;1112 1162 11B0; # (핽; 핽; 핽; 핽; 핽; ) HANGUL SYLLABLE HAELG +D57E;D57E;1112 1162 11B1;D57E;1112 1162 11B1; # (핾; 핾; 핾; 핾; 핾; ) HANGUL SYLLABLE HAELM +D57F;D57F;1112 1162 11B2;D57F;1112 1162 11B2; # (핿; 핿; 핿; 핿; 핿; ) HANGUL SYLLABLE HAELB +D580;D580;1112 1162 11B3;D580;1112 1162 11B3; # (햀; 햀; 햀; 햀; 햀; ) HANGUL SYLLABLE HAELS +D581;D581;1112 1162 11B4;D581;1112 1162 11B4; # (햁; 햁; 햁; 햁; 햁; ) HANGUL SYLLABLE HAELT +D582;D582;1112 1162 11B5;D582;1112 1162 11B5; # (햂; 햂; 햂; 햂; 햂; ) HANGUL SYLLABLE HAELP +D583;D583;1112 1162 11B6;D583;1112 1162 11B6; # (햃; 햃; 햃; 햃; 햃; ) HANGUL SYLLABLE HAELH +D584;D584;1112 1162 11B7;D584;1112 1162 11B7; # (햄; 햄; 햄; 햄; 햄; ) HANGUL SYLLABLE HAEM +D585;D585;1112 1162 11B8;D585;1112 1162 11B8; # (햅; 햅; 햅; 햅; 햅; ) HANGUL SYLLABLE HAEB +D586;D586;1112 1162 11B9;D586;1112 1162 11B9; # (햆; 햆; 햆; 햆; 햆; ) HANGUL SYLLABLE HAEBS +D587;D587;1112 1162 11BA;D587;1112 1162 11BA; # (햇; 햇; 햇; 햇; 햇; ) HANGUL SYLLABLE HAES +D588;D588;1112 1162 11BB;D588;1112 1162 11BB; # (했; 했; 했; 했; 했; ) HANGUL SYLLABLE HAESS +D589;D589;1112 1162 11BC;D589;1112 1162 11BC; # (행; 행; 행; 행; 행; ) HANGUL SYLLABLE HAENG +D58A;D58A;1112 1162 11BD;D58A;1112 1162 11BD; # (햊; 햊; 햊; 햊; 햊; ) HANGUL SYLLABLE HAEJ +D58B;D58B;1112 1162 11BE;D58B;1112 1162 11BE; # (햋; 햋; 햋; 햋; 햋; ) HANGUL SYLLABLE HAEC +D58C;D58C;1112 1162 11BF;D58C;1112 1162 11BF; # (햌; 햌; 햌; 햌; 햌; ) HANGUL SYLLABLE HAEK +D58D;D58D;1112 1162 11C0;D58D;1112 1162 11C0; # (햍; 햍; 햍; 햍; 햍; ) HANGUL SYLLABLE HAET +D58E;D58E;1112 1162 11C1;D58E;1112 1162 11C1; # (햎; 햎; 햎; 햎; 햎; ) HANGUL SYLLABLE HAEP +D58F;D58F;1112 1162 11C2;D58F;1112 1162 11C2; # (햏; 햏; 햏; 햏; 햏; ) HANGUL SYLLABLE HAEH +D590;D590;1112 1163;D590;1112 1163; # (햐; 햐; 햐; 햐; 햐; ) HANGUL SYLLABLE HYA +D591;D591;1112 1163 11A8;D591;1112 1163 11A8; # (햑; 햑; 햑; 햑; 햑; ) HANGUL SYLLABLE HYAG +D592;D592;1112 1163 11A9;D592;1112 1163 11A9; # (햒; 햒; 햒; 햒; 햒; ) HANGUL SYLLABLE HYAGG +D593;D593;1112 1163 11AA;D593;1112 1163 11AA; # (햓; 햓; 햓; 햓; 햓; ) HANGUL SYLLABLE HYAGS +D594;D594;1112 1163 11AB;D594;1112 1163 11AB; # (햔; 햔; 햔; 햔; 햔; ) HANGUL SYLLABLE HYAN +D595;D595;1112 1163 11AC;D595;1112 1163 11AC; # (햕; 햕; 햕; 햕; 햕; ) HANGUL SYLLABLE HYANJ +D596;D596;1112 1163 11AD;D596;1112 1163 11AD; # (햖; 햖; 햖; 햖; 햖; ) HANGUL SYLLABLE HYANH +D597;D597;1112 1163 11AE;D597;1112 1163 11AE; # (햗; 햗; 햗; 햗; 햗; ) HANGUL SYLLABLE HYAD +D598;D598;1112 1163 11AF;D598;1112 1163 11AF; # (햘; 햘; 햘; 햘; 햘; ) HANGUL SYLLABLE HYAL +D599;D599;1112 1163 11B0;D599;1112 1163 11B0; # (햙; 햙; 햙; 햙; 햙; ) HANGUL SYLLABLE HYALG +D59A;D59A;1112 1163 11B1;D59A;1112 1163 11B1; # (햚; 햚; 햚; 햚; 햚; ) HANGUL SYLLABLE HYALM +D59B;D59B;1112 1163 11B2;D59B;1112 1163 11B2; # (햛; 햛; 햛; 햛; 햛; ) HANGUL SYLLABLE HYALB +D59C;D59C;1112 1163 11B3;D59C;1112 1163 11B3; # (햜; 햜; 햜; 햜; 햜; ) HANGUL SYLLABLE HYALS +D59D;D59D;1112 1163 11B4;D59D;1112 1163 11B4; # (햝; 햝; 햝; 햝; 햝; ) HANGUL SYLLABLE HYALT +D59E;D59E;1112 1163 11B5;D59E;1112 1163 11B5; # (햞; 햞; 햞; 햞; 햞; ) HANGUL SYLLABLE HYALP +D59F;D59F;1112 1163 11B6;D59F;1112 1163 11B6; # (햟; 햟; 햟; 햟; 햟; ) HANGUL SYLLABLE HYALH +D5A0;D5A0;1112 1163 11B7;D5A0;1112 1163 11B7; # (햠; 햠; 햠; 햠; 햠; ) HANGUL SYLLABLE HYAM +D5A1;D5A1;1112 1163 11B8;D5A1;1112 1163 11B8; # (햡; 햡; 햡; 햡; 햡; ) HANGUL SYLLABLE HYAB +D5A2;D5A2;1112 1163 11B9;D5A2;1112 1163 11B9; # (햢; 햢; 햢; 햢; 햢; ) HANGUL SYLLABLE HYABS +D5A3;D5A3;1112 1163 11BA;D5A3;1112 1163 11BA; # (햣; 햣; 햣; 햣; 햣; ) HANGUL SYLLABLE HYAS +D5A4;D5A4;1112 1163 11BB;D5A4;1112 1163 11BB; # (햤; 햤; 햤; 햤; 햤; ) HANGUL SYLLABLE HYASS +D5A5;D5A5;1112 1163 11BC;D5A5;1112 1163 11BC; # (향; 향; 향; 향; 향; ) HANGUL SYLLABLE HYANG +D5A6;D5A6;1112 1163 11BD;D5A6;1112 1163 11BD; # (햦; 햦; 햦; 햦; 햦; ) HANGUL SYLLABLE HYAJ +D5A7;D5A7;1112 1163 11BE;D5A7;1112 1163 11BE; # (햧; 햧; 햧; 햧; 햧; ) HANGUL SYLLABLE HYAC +D5A8;D5A8;1112 1163 11BF;D5A8;1112 1163 11BF; # (햨; 햨; 햨; 햨; 햨; ) HANGUL SYLLABLE HYAK +D5A9;D5A9;1112 1163 11C0;D5A9;1112 1163 11C0; # (햩; 햩; 햩; 햩; 햩; ) HANGUL SYLLABLE HYAT +D5AA;D5AA;1112 1163 11C1;D5AA;1112 1163 11C1; # (햪; 햪; 햪; 햪; 햪; ) HANGUL SYLLABLE HYAP +D5AB;D5AB;1112 1163 11C2;D5AB;1112 1163 11C2; # (햫; 햫; 햫; 햫; 햫; ) HANGUL SYLLABLE HYAH +D5AC;D5AC;1112 1164;D5AC;1112 1164; # (햬; 햬; 햬; 햬; 햬; ) HANGUL SYLLABLE HYAE +D5AD;D5AD;1112 1164 11A8;D5AD;1112 1164 11A8; # (햭; 햭; 햭; 햭; 햭; ) HANGUL SYLLABLE HYAEG +D5AE;D5AE;1112 1164 11A9;D5AE;1112 1164 11A9; # (햮; 햮; 햮; 햮; 햮; ) HANGUL SYLLABLE HYAEGG +D5AF;D5AF;1112 1164 11AA;D5AF;1112 1164 11AA; # (햯; 햯; 햯; 햯; 햯; ) HANGUL SYLLABLE HYAEGS +D5B0;D5B0;1112 1164 11AB;D5B0;1112 1164 11AB; # (햰; 햰; 햰; 햰; 햰; ) HANGUL SYLLABLE HYAEN +D5B1;D5B1;1112 1164 11AC;D5B1;1112 1164 11AC; # (햱; 햱; 햱; 햱; 햱; ) HANGUL SYLLABLE HYAENJ +D5B2;D5B2;1112 1164 11AD;D5B2;1112 1164 11AD; # (햲; 햲; 햲; 햲; 햲; ) HANGUL SYLLABLE HYAENH +D5B3;D5B3;1112 1164 11AE;D5B3;1112 1164 11AE; # (햳; 햳; 햳; 햳; 햳; ) HANGUL SYLLABLE HYAED +D5B4;D5B4;1112 1164 11AF;D5B4;1112 1164 11AF; # (햴; 햴; 햴; 햴; 햴; ) HANGUL SYLLABLE HYAEL +D5B5;D5B5;1112 1164 11B0;D5B5;1112 1164 11B0; # (햵; 햵; 햵; 햵; 햵; ) HANGUL SYLLABLE HYAELG +D5B6;D5B6;1112 1164 11B1;D5B6;1112 1164 11B1; # (햶; 햶; 햶; 햶; 햶; ) HANGUL SYLLABLE HYAELM +D5B7;D5B7;1112 1164 11B2;D5B7;1112 1164 11B2; # (햷; 햷; 햷; 햷; 햷; ) HANGUL SYLLABLE HYAELB +D5B8;D5B8;1112 1164 11B3;D5B8;1112 1164 11B3; # (햸; 햸; 햸; 햸; 햸; ) HANGUL SYLLABLE HYAELS +D5B9;D5B9;1112 1164 11B4;D5B9;1112 1164 11B4; # (햹; 햹; 햹; 햹; 햹; ) HANGUL SYLLABLE HYAELT +D5BA;D5BA;1112 1164 11B5;D5BA;1112 1164 11B5; # (햺; 햺; 햺; 햺; 햺; ) HANGUL SYLLABLE HYAELP +D5BB;D5BB;1112 1164 11B6;D5BB;1112 1164 11B6; # (햻; 햻; 햻; 햻; 햻; ) HANGUL SYLLABLE HYAELH +D5BC;D5BC;1112 1164 11B7;D5BC;1112 1164 11B7; # (햼; 햼; 햼; 햼; 햼; ) HANGUL SYLLABLE HYAEM +D5BD;D5BD;1112 1164 11B8;D5BD;1112 1164 11B8; # (햽; 햽; 햽; 햽; 햽; ) HANGUL SYLLABLE HYAEB +D5BE;D5BE;1112 1164 11B9;D5BE;1112 1164 11B9; # (햾; 햾; 햾; 햾; 햾; ) HANGUL SYLLABLE HYAEBS +D5BF;D5BF;1112 1164 11BA;D5BF;1112 1164 11BA; # (햿; 햿; 햿; 햿; 햿; ) HANGUL SYLLABLE HYAES +D5C0;D5C0;1112 1164 11BB;D5C0;1112 1164 11BB; # (헀; 헀; 헀; 헀; 헀; ) HANGUL SYLLABLE HYAESS +D5C1;D5C1;1112 1164 11BC;D5C1;1112 1164 11BC; # (헁; 헁; 헁; 헁; 헁; ) HANGUL SYLLABLE HYAENG +D5C2;D5C2;1112 1164 11BD;D5C2;1112 1164 11BD; # (헂; 헂; 헂; 헂; 헂; ) HANGUL SYLLABLE HYAEJ +D5C3;D5C3;1112 1164 11BE;D5C3;1112 1164 11BE; # (헃; 헃; 헃; 헃; 헃; ) HANGUL SYLLABLE HYAEC +D5C4;D5C4;1112 1164 11BF;D5C4;1112 1164 11BF; # (헄; 헄; 헄; 헄; 헄; ) HANGUL SYLLABLE HYAEK +D5C5;D5C5;1112 1164 11C0;D5C5;1112 1164 11C0; # (헅; 헅; 헅; 헅; 헅; ) HANGUL SYLLABLE HYAET +D5C6;D5C6;1112 1164 11C1;D5C6;1112 1164 11C1; # (헆; 헆; 헆; 헆; 헆; ) HANGUL SYLLABLE HYAEP +D5C7;D5C7;1112 1164 11C2;D5C7;1112 1164 11C2; # (헇; 헇; 헇; 헇; 헇; ) HANGUL SYLLABLE HYAEH +D5C8;D5C8;1112 1165;D5C8;1112 1165; # (허; 허; 허; 허; 허; ) HANGUL SYLLABLE HEO +D5C9;D5C9;1112 1165 11A8;D5C9;1112 1165 11A8; # (헉; 헉; 헉; 헉; 헉; ) HANGUL SYLLABLE HEOG +D5CA;D5CA;1112 1165 11A9;D5CA;1112 1165 11A9; # (헊; 헊; 헊; 헊; 헊; ) HANGUL SYLLABLE HEOGG +D5CB;D5CB;1112 1165 11AA;D5CB;1112 1165 11AA; # (헋; 헋; 헋; 헋; 헋; ) HANGUL SYLLABLE HEOGS +D5CC;D5CC;1112 1165 11AB;D5CC;1112 1165 11AB; # (헌; 헌; 헌; 헌; 헌; ) HANGUL SYLLABLE HEON +D5CD;D5CD;1112 1165 11AC;D5CD;1112 1165 11AC; # (헍; 헍; 헍; 헍; 헍; ) HANGUL SYLLABLE HEONJ +D5CE;D5CE;1112 1165 11AD;D5CE;1112 1165 11AD; # (헎; 헎; 헎; 헎; 헎; ) HANGUL SYLLABLE HEONH +D5CF;D5CF;1112 1165 11AE;D5CF;1112 1165 11AE; # (헏; 헏; 헏; 헏; 헏; ) HANGUL SYLLABLE HEOD +D5D0;D5D0;1112 1165 11AF;D5D0;1112 1165 11AF; # (헐; 헐; 헐; 헐; 헐; ) HANGUL SYLLABLE HEOL +D5D1;D5D1;1112 1165 11B0;D5D1;1112 1165 11B0; # (헑; 헑; 헑; 헑; 헑; ) HANGUL SYLLABLE HEOLG +D5D2;D5D2;1112 1165 11B1;D5D2;1112 1165 11B1; # (헒; 헒; 헒; 헒; 헒; ) HANGUL SYLLABLE HEOLM +D5D3;D5D3;1112 1165 11B2;D5D3;1112 1165 11B2; # (헓; 헓; 헓; 헓; 헓; ) HANGUL SYLLABLE HEOLB +D5D4;D5D4;1112 1165 11B3;D5D4;1112 1165 11B3; # (헔; 헔; 헔; 헔; 헔; ) HANGUL SYLLABLE HEOLS +D5D5;D5D5;1112 1165 11B4;D5D5;1112 1165 11B4; # (헕; 헕; 헕; 헕; 헕; ) HANGUL SYLLABLE HEOLT +D5D6;D5D6;1112 1165 11B5;D5D6;1112 1165 11B5; # (헖; 헖; 헖; 헖; 헖; ) HANGUL SYLLABLE HEOLP +D5D7;D5D7;1112 1165 11B6;D5D7;1112 1165 11B6; # (헗; 헗; 헗; 헗; 헗; ) HANGUL SYLLABLE HEOLH +D5D8;D5D8;1112 1165 11B7;D5D8;1112 1165 11B7; # (험; 험; 험; 험; 험; ) HANGUL SYLLABLE HEOM +D5D9;D5D9;1112 1165 11B8;D5D9;1112 1165 11B8; # (헙; 헙; 헙; 헙; 헙; ) HANGUL SYLLABLE HEOB +D5DA;D5DA;1112 1165 11B9;D5DA;1112 1165 11B9; # (헚; 헚; 헚; 헚; 헚; ) HANGUL SYLLABLE HEOBS +D5DB;D5DB;1112 1165 11BA;D5DB;1112 1165 11BA; # (헛; 헛; 헛; 헛; 헛; ) HANGUL SYLLABLE HEOS +D5DC;D5DC;1112 1165 11BB;D5DC;1112 1165 11BB; # (헜; 헜; 헜; 헜; 헜; ) HANGUL SYLLABLE HEOSS +D5DD;D5DD;1112 1165 11BC;D5DD;1112 1165 11BC; # (헝; 헝; 헝; 헝; 헝; ) HANGUL SYLLABLE HEONG +D5DE;D5DE;1112 1165 11BD;D5DE;1112 1165 11BD; # (헞; 헞; 헞; 헞; 헞; ) HANGUL SYLLABLE HEOJ +D5DF;D5DF;1112 1165 11BE;D5DF;1112 1165 11BE; # (헟; 헟; 헟; 헟; 헟; ) HANGUL SYLLABLE HEOC +D5E0;D5E0;1112 1165 11BF;D5E0;1112 1165 11BF; # (헠; 헠; 헠; 헠; 헠; ) HANGUL SYLLABLE HEOK +D5E1;D5E1;1112 1165 11C0;D5E1;1112 1165 11C0; # (헡; 헡; 헡; 헡; 헡; ) HANGUL SYLLABLE HEOT +D5E2;D5E2;1112 1165 11C1;D5E2;1112 1165 11C1; # (헢; 헢; 헢; 헢; 헢; ) HANGUL SYLLABLE HEOP +D5E3;D5E3;1112 1165 11C2;D5E3;1112 1165 11C2; # (헣; 헣; 헣; 헣; 헣; ) HANGUL SYLLABLE HEOH +D5E4;D5E4;1112 1166;D5E4;1112 1166; # (헤; 헤; 헤; 헤; 헤; ) HANGUL SYLLABLE HE +D5E5;D5E5;1112 1166 11A8;D5E5;1112 1166 11A8; # (헥; 헥; 헥; 헥; 헥; ) HANGUL SYLLABLE HEG +D5E6;D5E6;1112 1166 11A9;D5E6;1112 1166 11A9; # (헦; 헦; 헦; 헦; 헦; ) HANGUL SYLLABLE HEGG +D5E7;D5E7;1112 1166 11AA;D5E7;1112 1166 11AA; # (헧; 헧; 헧; 헧; 헧; ) HANGUL SYLLABLE HEGS +D5E8;D5E8;1112 1166 11AB;D5E8;1112 1166 11AB; # (헨; 헨; 헨; 헨; 헨; ) HANGUL SYLLABLE HEN +D5E9;D5E9;1112 1166 11AC;D5E9;1112 1166 11AC; # (헩; 헩; 헩; 헩; 헩; ) HANGUL SYLLABLE HENJ +D5EA;D5EA;1112 1166 11AD;D5EA;1112 1166 11AD; # (헪; 헪; 헪; 헪; 헪; ) HANGUL SYLLABLE HENH +D5EB;D5EB;1112 1166 11AE;D5EB;1112 1166 11AE; # (헫; 헫; 헫; 헫; 헫; ) HANGUL SYLLABLE HED +D5EC;D5EC;1112 1166 11AF;D5EC;1112 1166 11AF; # (헬; 헬; 헬; 헬; 헬; ) HANGUL SYLLABLE HEL +D5ED;D5ED;1112 1166 11B0;D5ED;1112 1166 11B0; # (헭; 헭; 헭; 헭; 헭; ) HANGUL SYLLABLE HELG +D5EE;D5EE;1112 1166 11B1;D5EE;1112 1166 11B1; # (헮; 헮; 헮; 헮; 헮; ) HANGUL SYLLABLE HELM +D5EF;D5EF;1112 1166 11B2;D5EF;1112 1166 11B2; # (헯; 헯; 헯; 헯; 헯; ) HANGUL SYLLABLE HELB +D5F0;D5F0;1112 1166 11B3;D5F0;1112 1166 11B3; # (헰; 헰; 헰; 헰; 헰; ) HANGUL SYLLABLE HELS +D5F1;D5F1;1112 1166 11B4;D5F1;1112 1166 11B4; # (헱; 헱; 헱; 헱; 헱; ) HANGUL SYLLABLE HELT +D5F2;D5F2;1112 1166 11B5;D5F2;1112 1166 11B5; # (헲; 헲; 헲; 헲; 헲; ) HANGUL SYLLABLE HELP +D5F3;D5F3;1112 1166 11B6;D5F3;1112 1166 11B6; # (헳; 헳; 헳; 헳; 헳; ) HANGUL SYLLABLE HELH +D5F4;D5F4;1112 1166 11B7;D5F4;1112 1166 11B7; # (헴; 헴; 헴; 헴; 헴; ) HANGUL SYLLABLE HEM +D5F5;D5F5;1112 1166 11B8;D5F5;1112 1166 11B8; # (헵; 헵; 헵; 헵; 헵; ) HANGUL SYLLABLE HEB +D5F6;D5F6;1112 1166 11B9;D5F6;1112 1166 11B9; # (헶; 헶; 헶; 헶; 헶; ) HANGUL SYLLABLE HEBS +D5F7;D5F7;1112 1166 11BA;D5F7;1112 1166 11BA; # (헷; 헷; 헷; 헷; 헷; ) HANGUL SYLLABLE HES +D5F8;D5F8;1112 1166 11BB;D5F8;1112 1166 11BB; # (헸; 헸; 헸; 헸; 헸; ) HANGUL SYLLABLE HESS +D5F9;D5F9;1112 1166 11BC;D5F9;1112 1166 11BC; # (헹; 헹; 헹; 헹; 헹; ) HANGUL SYLLABLE HENG +D5FA;D5FA;1112 1166 11BD;D5FA;1112 1166 11BD; # (헺; 헺; 헺; 헺; 헺; ) HANGUL SYLLABLE HEJ +D5FB;D5FB;1112 1166 11BE;D5FB;1112 1166 11BE; # (헻; 헻; 헻; 헻; 헻; ) HANGUL SYLLABLE HEC +D5FC;D5FC;1112 1166 11BF;D5FC;1112 1166 11BF; # (헼; 헼; 헼; 헼; 헼; ) HANGUL SYLLABLE HEK +D5FD;D5FD;1112 1166 11C0;D5FD;1112 1166 11C0; # (헽; 헽; 헽; 헽; 헽; ) HANGUL SYLLABLE HET +D5FE;D5FE;1112 1166 11C1;D5FE;1112 1166 11C1; # (헾; 헾; 헾; 헾; 헾; ) HANGUL SYLLABLE HEP +D5FF;D5FF;1112 1166 11C2;D5FF;1112 1166 11C2; # (헿; 헿; 헿; 헿; 헿; ) HANGUL SYLLABLE HEH +D600;D600;1112 1167;D600;1112 1167; # (혀; 혀; 혀; 혀; 혀; ) HANGUL SYLLABLE HYEO +D601;D601;1112 1167 11A8;D601;1112 1167 11A8; # (혁; 혁; 혁; 혁; 혁; ) HANGUL SYLLABLE HYEOG +D602;D602;1112 1167 11A9;D602;1112 1167 11A9; # (혂; 혂; 혂; 혂; 혂; ) HANGUL SYLLABLE HYEOGG +D603;D603;1112 1167 11AA;D603;1112 1167 11AA; # (혃; 혃; 혃; 혃; 혃; ) HANGUL SYLLABLE HYEOGS +D604;D604;1112 1167 11AB;D604;1112 1167 11AB; # (현; 현; 현; 현; 현; ) HANGUL SYLLABLE HYEON +D605;D605;1112 1167 11AC;D605;1112 1167 11AC; # (혅; 혅; 혅; 혅; 혅; ) HANGUL SYLLABLE HYEONJ +D606;D606;1112 1167 11AD;D606;1112 1167 11AD; # (혆; 혆; 혆; 혆; 혆; ) HANGUL SYLLABLE HYEONH +D607;D607;1112 1167 11AE;D607;1112 1167 11AE; # (혇; 혇; 혇; 혇; 혇; ) HANGUL SYLLABLE HYEOD +D608;D608;1112 1167 11AF;D608;1112 1167 11AF; # (혈; 혈; 혈; 혈; 혈; ) HANGUL SYLLABLE HYEOL +D609;D609;1112 1167 11B0;D609;1112 1167 11B0; # (혉; 혉; 혉; 혉; 혉; ) HANGUL SYLLABLE HYEOLG +D60A;D60A;1112 1167 11B1;D60A;1112 1167 11B1; # (혊; 혊; 혊; 혊; 혊; ) HANGUL SYLLABLE HYEOLM +D60B;D60B;1112 1167 11B2;D60B;1112 1167 11B2; # (혋; 혋; 혋; 혋; 혋; ) HANGUL SYLLABLE HYEOLB +D60C;D60C;1112 1167 11B3;D60C;1112 1167 11B3; # (혌; 혌; 혌; 혌; 혌; ) HANGUL SYLLABLE HYEOLS +D60D;D60D;1112 1167 11B4;D60D;1112 1167 11B4; # (혍; 혍; 혍; 혍; 혍; ) HANGUL SYLLABLE HYEOLT +D60E;D60E;1112 1167 11B5;D60E;1112 1167 11B5; # (혎; 혎; 혎; 혎; 혎; ) HANGUL SYLLABLE HYEOLP +D60F;D60F;1112 1167 11B6;D60F;1112 1167 11B6; # (혏; 혏; 혏; 혏; 혏; ) HANGUL SYLLABLE HYEOLH +D610;D610;1112 1167 11B7;D610;1112 1167 11B7; # (혐; 혐; 혐; 혐; 혐; ) HANGUL SYLLABLE HYEOM +D611;D611;1112 1167 11B8;D611;1112 1167 11B8; # (협; 협; 협; 협; 협; ) HANGUL SYLLABLE HYEOB +D612;D612;1112 1167 11B9;D612;1112 1167 11B9; # (혒; 혒; 혒; 혒; 혒; ) HANGUL SYLLABLE HYEOBS +D613;D613;1112 1167 11BA;D613;1112 1167 11BA; # (혓; 혓; 혓; 혓; 혓; ) HANGUL SYLLABLE HYEOS +D614;D614;1112 1167 11BB;D614;1112 1167 11BB; # (혔; 혔; 혔; 혔; 혔; ) HANGUL SYLLABLE HYEOSS +D615;D615;1112 1167 11BC;D615;1112 1167 11BC; # (형; 형; 형; 형; 형; ) HANGUL SYLLABLE HYEONG +D616;D616;1112 1167 11BD;D616;1112 1167 11BD; # (혖; 혖; 혖; 혖; 혖; ) HANGUL SYLLABLE HYEOJ +D617;D617;1112 1167 11BE;D617;1112 1167 11BE; # (혗; 혗; 혗; 혗; 혗; ) HANGUL SYLLABLE HYEOC +D618;D618;1112 1167 11BF;D618;1112 1167 11BF; # (혘; 혘; 혘; 혘; 혘; ) HANGUL SYLLABLE HYEOK +D619;D619;1112 1167 11C0;D619;1112 1167 11C0; # (혙; 혙; 혙; 혙; 혙; ) HANGUL SYLLABLE HYEOT +D61A;D61A;1112 1167 11C1;D61A;1112 1167 11C1; # (혚; 혚; 혚; 혚; 혚; ) HANGUL SYLLABLE HYEOP +D61B;D61B;1112 1167 11C2;D61B;1112 1167 11C2; # (혛; 혛; 혛; 혛; 혛; ) HANGUL SYLLABLE HYEOH +D61C;D61C;1112 1168;D61C;1112 1168; # (혜; 혜; 혜; 혜; 혜; ) HANGUL SYLLABLE HYE +D61D;D61D;1112 1168 11A8;D61D;1112 1168 11A8; # (혝; 혝; 혝; 혝; 혝; ) HANGUL SYLLABLE HYEG +D61E;D61E;1112 1168 11A9;D61E;1112 1168 11A9; # (혞; 혞; 혞; 혞; 혞; ) HANGUL SYLLABLE HYEGG +D61F;D61F;1112 1168 11AA;D61F;1112 1168 11AA; # (혟; 혟; 혟; 혟; 혟; ) HANGUL SYLLABLE HYEGS +D620;D620;1112 1168 11AB;D620;1112 1168 11AB; # (혠; 혠; 혠; 혠; 혠; ) HANGUL SYLLABLE HYEN +D621;D621;1112 1168 11AC;D621;1112 1168 11AC; # (혡; 혡; 혡; 혡; 혡; ) HANGUL SYLLABLE HYENJ +D622;D622;1112 1168 11AD;D622;1112 1168 11AD; # (혢; 혢; 혢; 혢; 혢; ) HANGUL SYLLABLE HYENH +D623;D623;1112 1168 11AE;D623;1112 1168 11AE; # (혣; 혣; 혣; 혣; 혣; ) HANGUL SYLLABLE HYED +D624;D624;1112 1168 11AF;D624;1112 1168 11AF; # (혤; 혤; 혤; 혤; 혤; ) HANGUL SYLLABLE HYEL +D625;D625;1112 1168 11B0;D625;1112 1168 11B0; # (혥; 혥; 혥; 혥; 혥; ) HANGUL SYLLABLE HYELG +D626;D626;1112 1168 11B1;D626;1112 1168 11B1; # (혦; 혦; 혦; 혦; 혦; ) HANGUL SYLLABLE HYELM +D627;D627;1112 1168 11B2;D627;1112 1168 11B2; # (혧; 혧; 혧; 혧; 혧; ) HANGUL SYLLABLE HYELB +D628;D628;1112 1168 11B3;D628;1112 1168 11B3; # (혨; 혨; 혨; 혨; 혨; ) HANGUL SYLLABLE HYELS +D629;D629;1112 1168 11B4;D629;1112 1168 11B4; # (혩; 혩; 혩; 혩; 혩; ) HANGUL SYLLABLE HYELT +D62A;D62A;1112 1168 11B5;D62A;1112 1168 11B5; # (혪; 혪; 혪; 혪; 혪; ) HANGUL SYLLABLE HYELP +D62B;D62B;1112 1168 11B6;D62B;1112 1168 11B6; # (혫; 혫; 혫; 혫; 혫; ) HANGUL SYLLABLE HYELH +D62C;D62C;1112 1168 11B7;D62C;1112 1168 11B7; # (혬; 혬; 혬; 혬; 혬; ) HANGUL SYLLABLE HYEM +D62D;D62D;1112 1168 11B8;D62D;1112 1168 11B8; # (혭; 혭; 혭; 혭; 혭; ) HANGUL SYLLABLE HYEB +D62E;D62E;1112 1168 11B9;D62E;1112 1168 11B9; # (혮; 혮; 혮; 혮; 혮; ) HANGUL SYLLABLE HYEBS +D62F;D62F;1112 1168 11BA;D62F;1112 1168 11BA; # (혯; 혯; 혯; 혯; 혯; ) HANGUL SYLLABLE HYES +D630;D630;1112 1168 11BB;D630;1112 1168 11BB; # (혰; 혰; 혰; 혰; 혰; ) HANGUL SYLLABLE HYESS +D631;D631;1112 1168 11BC;D631;1112 1168 11BC; # (혱; 혱; 혱; 혱; 혱; ) HANGUL SYLLABLE HYENG +D632;D632;1112 1168 11BD;D632;1112 1168 11BD; # (혲; 혲; 혲; 혲; 혲; ) HANGUL SYLLABLE HYEJ +D633;D633;1112 1168 11BE;D633;1112 1168 11BE; # (혳; 혳; 혳; 혳; 혳; ) HANGUL SYLLABLE HYEC +D634;D634;1112 1168 11BF;D634;1112 1168 11BF; # (혴; 혴; 혴; 혴; 혴; ) HANGUL SYLLABLE HYEK +D635;D635;1112 1168 11C0;D635;1112 1168 11C0; # (혵; 혵; 혵; 혵; 혵; ) HANGUL SYLLABLE HYET +D636;D636;1112 1168 11C1;D636;1112 1168 11C1; # (혶; 혶; 혶; 혶; 혶; ) HANGUL SYLLABLE HYEP +D637;D637;1112 1168 11C2;D637;1112 1168 11C2; # (혷; 혷; 혷; 혷; 혷; ) HANGUL SYLLABLE HYEH +D638;D638;1112 1169;D638;1112 1169; # (호; 호; 호; 호; 호; ) HANGUL SYLLABLE HO +D639;D639;1112 1169 11A8;D639;1112 1169 11A8; # (혹; 혹; 혹; 혹; 혹; ) HANGUL SYLLABLE HOG +D63A;D63A;1112 1169 11A9;D63A;1112 1169 11A9; # (혺; 혺; 혺; 혺; 혺; ) HANGUL SYLLABLE HOGG +D63B;D63B;1112 1169 11AA;D63B;1112 1169 11AA; # (혻; 혻; 혻; 혻; 혻; ) HANGUL SYLLABLE HOGS +D63C;D63C;1112 1169 11AB;D63C;1112 1169 11AB; # (혼; 혼; 혼; 혼; 혼; ) HANGUL SYLLABLE HON +D63D;D63D;1112 1169 11AC;D63D;1112 1169 11AC; # (혽; 혽; 혽; 혽; 혽; ) HANGUL SYLLABLE HONJ +D63E;D63E;1112 1169 11AD;D63E;1112 1169 11AD; # (혾; 혾; 혾; 혾; 혾; ) HANGUL SYLLABLE HONH +D63F;D63F;1112 1169 11AE;D63F;1112 1169 11AE; # (혿; 혿; 혿; 혿; 혿; ) HANGUL SYLLABLE HOD +D640;D640;1112 1169 11AF;D640;1112 1169 11AF; # (홀; 홀; 홀; 홀; 홀; ) HANGUL SYLLABLE HOL +D641;D641;1112 1169 11B0;D641;1112 1169 11B0; # (홁; 홁; 홁; 홁; 홁; ) HANGUL SYLLABLE HOLG +D642;D642;1112 1169 11B1;D642;1112 1169 11B1; # (홂; 홂; 홂; 홂; 홂; ) HANGUL SYLLABLE HOLM +D643;D643;1112 1169 11B2;D643;1112 1169 11B2; # (홃; 홃; 홃; 홃; 홃; ) HANGUL SYLLABLE HOLB +D644;D644;1112 1169 11B3;D644;1112 1169 11B3; # (홄; 홄; 홄; 홄; 홄; ) HANGUL SYLLABLE HOLS +D645;D645;1112 1169 11B4;D645;1112 1169 11B4; # (홅; 홅; 홅; 홅; 홅; ) HANGUL SYLLABLE HOLT +D646;D646;1112 1169 11B5;D646;1112 1169 11B5; # (홆; 홆; 홆; 홆; 홆; ) HANGUL SYLLABLE HOLP +D647;D647;1112 1169 11B6;D647;1112 1169 11B6; # (홇; 홇; 홇; 홇; 홇; ) HANGUL SYLLABLE HOLH +D648;D648;1112 1169 11B7;D648;1112 1169 11B7; # (홈; 홈; 홈; 홈; 홈; ) HANGUL SYLLABLE HOM +D649;D649;1112 1169 11B8;D649;1112 1169 11B8; # (홉; 홉; 홉; 홉; 홉; ) HANGUL SYLLABLE HOB +D64A;D64A;1112 1169 11B9;D64A;1112 1169 11B9; # (홊; 홊; 홊; 홊; 홊; ) HANGUL SYLLABLE HOBS +D64B;D64B;1112 1169 11BA;D64B;1112 1169 11BA; # (홋; 홋; 홋; 홋; 홋; ) HANGUL SYLLABLE HOS +D64C;D64C;1112 1169 11BB;D64C;1112 1169 11BB; # (홌; 홌; 홌; 홌; 홌; ) HANGUL SYLLABLE HOSS +D64D;D64D;1112 1169 11BC;D64D;1112 1169 11BC; # (홍; 홍; 홍; 홍; 홍; ) HANGUL SYLLABLE HONG +D64E;D64E;1112 1169 11BD;D64E;1112 1169 11BD; # (홎; 홎; 홎; 홎; 홎; ) HANGUL SYLLABLE HOJ +D64F;D64F;1112 1169 11BE;D64F;1112 1169 11BE; # (홏; 홏; 홏; 홏; 홏; ) HANGUL SYLLABLE HOC +D650;D650;1112 1169 11BF;D650;1112 1169 11BF; # (홐; 홐; 홐; 홐; 홐; ) HANGUL SYLLABLE HOK +D651;D651;1112 1169 11C0;D651;1112 1169 11C0; # (홑; 홑; 홑; 홑; 홑; ) HANGUL SYLLABLE HOT +D652;D652;1112 1169 11C1;D652;1112 1169 11C1; # (홒; 홒; 홒; 홒; 홒; ) HANGUL SYLLABLE HOP +D653;D653;1112 1169 11C2;D653;1112 1169 11C2; # (홓; 홓; 홓; 홓; 홓; ) HANGUL SYLLABLE HOH +D654;D654;1112 116A;D654;1112 116A; # (화; 화; 화; 화; 화; ) HANGUL SYLLABLE HWA +D655;D655;1112 116A 11A8;D655;1112 116A 11A8; # (확; 확; 확; 확; 확; ) HANGUL SYLLABLE HWAG +D656;D656;1112 116A 11A9;D656;1112 116A 11A9; # (홖; 홖; 홖; 홖; 홖; ) HANGUL SYLLABLE HWAGG +D657;D657;1112 116A 11AA;D657;1112 116A 11AA; # (홗; 홗; 홗; 홗; 홗; ) HANGUL SYLLABLE HWAGS +D658;D658;1112 116A 11AB;D658;1112 116A 11AB; # (환; 환; 환; 환; 환; ) HANGUL SYLLABLE HWAN +D659;D659;1112 116A 11AC;D659;1112 116A 11AC; # (홙; 홙; 홙; 홙; 홙; ) HANGUL SYLLABLE HWANJ +D65A;D65A;1112 116A 11AD;D65A;1112 116A 11AD; # (홚; 홚; 홚; 홚; 홚; ) HANGUL SYLLABLE HWANH +D65B;D65B;1112 116A 11AE;D65B;1112 116A 11AE; # (홛; 홛; 홛; 홛; 홛; ) HANGUL SYLLABLE HWAD +D65C;D65C;1112 116A 11AF;D65C;1112 116A 11AF; # (활; 활; 활; 활; 활; ) HANGUL SYLLABLE HWAL +D65D;D65D;1112 116A 11B0;D65D;1112 116A 11B0; # (홝; 홝; 홝; 홝; 홝; ) HANGUL SYLLABLE HWALG +D65E;D65E;1112 116A 11B1;D65E;1112 116A 11B1; # (홞; 홞; 홞; 홞; 홞; ) HANGUL SYLLABLE HWALM +D65F;D65F;1112 116A 11B2;D65F;1112 116A 11B2; # (홟; 홟; 홟; 홟; 홟; ) HANGUL SYLLABLE HWALB +D660;D660;1112 116A 11B3;D660;1112 116A 11B3; # (홠; 홠; 홠; 홠; 홠; ) HANGUL SYLLABLE HWALS +D661;D661;1112 116A 11B4;D661;1112 116A 11B4; # (홡; 홡; 홡; 홡; 홡; ) HANGUL SYLLABLE HWALT +D662;D662;1112 116A 11B5;D662;1112 116A 11B5; # (홢; 홢; 홢; 홢; 홢; ) HANGUL SYLLABLE HWALP +D663;D663;1112 116A 11B6;D663;1112 116A 11B6; # (홣; 홣; 홣; 홣; 홣; ) HANGUL SYLLABLE HWALH +D664;D664;1112 116A 11B7;D664;1112 116A 11B7; # (홤; 홤; 홤; 홤; 홤; ) HANGUL SYLLABLE HWAM +D665;D665;1112 116A 11B8;D665;1112 116A 11B8; # (홥; 홥; 홥; 홥; 홥; ) HANGUL SYLLABLE HWAB +D666;D666;1112 116A 11B9;D666;1112 116A 11B9; # (홦; 홦; 홦; 홦; 홦; ) HANGUL SYLLABLE HWABS +D667;D667;1112 116A 11BA;D667;1112 116A 11BA; # (홧; 홧; 홧; 홧; 홧; ) HANGUL SYLLABLE HWAS +D668;D668;1112 116A 11BB;D668;1112 116A 11BB; # (홨; 홨; 홨; 홨; 홨; ) HANGUL SYLLABLE HWASS +D669;D669;1112 116A 11BC;D669;1112 116A 11BC; # (황; 황; 황; 황; 황; ) HANGUL SYLLABLE HWANG +D66A;D66A;1112 116A 11BD;D66A;1112 116A 11BD; # (홪; 홪; 홪; 홪; 홪; ) HANGUL SYLLABLE HWAJ +D66B;D66B;1112 116A 11BE;D66B;1112 116A 11BE; # (홫; 홫; 홫; 홫; 홫; ) HANGUL SYLLABLE HWAC +D66C;D66C;1112 116A 11BF;D66C;1112 116A 11BF; # (홬; 홬; 홬; 홬; 홬; ) HANGUL SYLLABLE HWAK +D66D;D66D;1112 116A 11C0;D66D;1112 116A 11C0; # (홭; 홭; 홭; 홭; 홭; ) HANGUL SYLLABLE HWAT +D66E;D66E;1112 116A 11C1;D66E;1112 116A 11C1; # (홮; 홮; 홮; 홮; 홮; ) HANGUL SYLLABLE HWAP +D66F;D66F;1112 116A 11C2;D66F;1112 116A 11C2; # (홯; 홯; 홯; 홯; 홯; ) HANGUL SYLLABLE HWAH +D670;D670;1112 116B;D670;1112 116B; # (홰; 홰; 홰; 홰; 홰; ) HANGUL SYLLABLE HWAE +D671;D671;1112 116B 11A8;D671;1112 116B 11A8; # (홱; 홱; 홱; 홱; 홱; ) HANGUL SYLLABLE HWAEG +D672;D672;1112 116B 11A9;D672;1112 116B 11A9; # (홲; 홲; 홲; 홲; 홲; ) HANGUL SYLLABLE HWAEGG +D673;D673;1112 116B 11AA;D673;1112 116B 11AA; # (홳; 홳; 홳; 홳; 홳; ) HANGUL SYLLABLE HWAEGS +D674;D674;1112 116B 11AB;D674;1112 116B 11AB; # (홴; 홴; 홴; 홴; 홴; ) HANGUL SYLLABLE HWAEN +D675;D675;1112 116B 11AC;D675;1112 116B 11AC; # (홵; 홵; 홵; 홵; 홵; ) HANGUL SYLLABLE HWAENJ +D676;D676;1112 116B 11AD;D676;1112 116B 11AD; # (홶; 홶; 홶; 홶; 홶; ) HANGUL SYLLABLE HWAENH +D677;D677;1112 116B 11AE;D677;1112 116B 11AE; # (홷; 홷; 홷; 홷; 홷; ) HANGUL SYLLABLE HWAED +D678;D678;1112 116B 11AF;D678;1112 116B 11AF; # (홸; 홸; 홸; 홸; 홸; ) HANGUL SYLLABLE HWAEL +D679;D679;1112 116B 11B0;D679;1112 116B 11B0; # (홹; 홹; 홹; 홹; 홹; ) HANGUL SYLLABLE HWAELG +D67A;D67A;1112 116B 11B1;D67A;1112 116B 11B1; # (홺; 홺; 홺; 홺; 홺; ) HANGUL SYLLABLE HWAELM +D67B;D67B;1112 116B 11B2;D67B;1112 116B 11B2; # (홻; 홻; 홻; 홻; 홻; ) HANGUL SYLLABLE HWAELB +D67C;D67C;1112 116B 11B3;D67C;1112 116B 11B3; # (홼; 홼; 홼; 홼; 홼; ) HANGUL SYLLABLE HWAELS +D67D;D67D;1112 116B 11B4;D67D;1112 116B 11B4; # (홽; 홽; 홽; 홽; 홽; ) HANGUL SYLLABLE HWAELT +D67E;D67E;1112 116B 11B5;D67E;1112 116B 11B5; # (홾; 홾; 홾; 홾; 홾; ) HANGUL SYLLABLE HWAELP +D67F;D67F;1112 116B 11B6;D67F;1112 116B 11B6; # (홿; 홿; 홿; 홿; 홿; ) HANGUL SYLLABLE HWAELH +D680;D680;1112 116B 11B7;D680;1112 116B 11B7; # (횀; 횀; 횀; 횀; 횀; ) HANGUL SYLLABLE HWAEM +D681;D681;1112 116B 11B8;D681;1112 116B 11B8; # (횁; 횁; 횁; 횁; 횁; ) HANGUL SYLLABLE HWAEB +D682;D682;1112 116B 11B9;D682;1112 116B 11B9; # (횂; 횂; 횂; 횂; 횂; ) HANGUL SYLLABLE HWAEBS +D683;D683;1112 116B 11BA;D683;1112 116B 11BA; # (횃; 횃; 횃; 횃; 횃; ) HANGUL SYLLABLE HWAES +D684;D684;1112 116B 11BB;D684;1112 116B 11BB; # (횄; 횄; 횄; 횄; 횄; ) HANGUL SYLLABLE HWAESS +D685;D685;1112 116B 11BC;D685;1112 116B 11BC; # (횅; 횅; 횅; 횅; 횅; ) HANGUL SYLLABLE HWAENG +D686;D686;1112 116B 11BD;D686;1112 116B 11BD; # (횆; 횆; 횆; 횆; 횆; ) HANGUL SYLLABLE HWAEJ +D687;D687;1112 116B 11BE;D687;1112 116B 11BE; # (횇; 횇; 횇; 횇; 횇; ) HANGUL SYLLABLE HWAEC +D688;D688;1112 116B 11BF;D688;1112 116B 11BF; # (횈; 횈; 횈; 횈; 횈; ) HANGUL SYLLABLE HWAEK +D689;D689;1112 116B 11C0;D689;1112 116B 11C0; # (횉; 횉; 횉; 횉; 횉; ) HANGUL SYLLABLE HWAET +D68A;D68A;1112 116B 11C1;D68A;1112 116B 11C1; # (횊; 횊; 횊; 횊; 횊; ) HANGUL SYLLABLE HWAEP +D68B;D68B;1112 116B 11C2;D68B;1112 116B 11C2; # (횋; 횋; 횋; 횋; 횋; ) HANGUL SYLLABLE HWAEH +D68C;D68C;1112 116C;D68C;1112 116C; # (회; 회; 회; 회; 회; ) HANGUL SYLLABLE HOE +D68D;D68D;1112 116C 11A8;D68D;1112 116C 11A8; # (획; 획; 획; 획; 획; ) HANGUL SYLLABLE HOEG +D68E;D68E;1112 116C 11A9;D68E;1112 116C 11A9; # (횎; 횎; 횎; 횎; 횎; ) HANGUL SYLLABLE HOEGG +D68F;D68F;1112 116C 11AA;D68F;1112 116C 11AA; # (횏; 횏; 횏; 횏; 횏; ) HANGUL SYLLABLE HOEGS +D690;D690;1112 116C 11AB;D690;1112 116C 11AB; # (횐; 횐; 횐; 횐; 횐; ) HANGUL SYLLABLE HOEN +D691;D691;1112 116C 11AC;D691;1112 116C 11AC; # (횑; 횑; 횑; 횑; 횑; ) HANGUL SYLLABLE HOENJ +D692;D692;1112 116C 11AD;D692;1112 116C 11AD; # (횒; 횒; 횒; 횒; 횒; ) HANGUL SYLLABLE HOENH +D693;D693;1112 116C 11AE;D693;1112 116C 11AE; # (횓; 횓; 횓; 횓; 횓; ) HANGUL SYLLABLE HOED +D694;D694;1112 116C 11AF;D694;1112 116C 11AF; # (횔; 횔; 횔; 횔; 횔; ) HANGUL SYLLABLE HOEL +D695;D695;1112 116C 11B0;D695;1112 116C 11B0; # (횕; 횕; 횕; 횕; 횕; ) HANGUL SYLLABLE HOELG +D696;D696;1112 116C 11B1;D696;1112 116C 11B1; # (횖; 횖; 횖; 횖; 횖; ) HANGUL SYLLABLE HOELM +D697;D697;1112 116C 11B2;D697;1112 116C 11B2; # (횗; 횗; 횗; 횗; 횗; ) HANGUL SYLLABLE HOELB +D698;D698;1112 116C 11B3;D698;1112 116C 11B3; # (횘; 횘; 횘; 횘; 횘; ) HANGUL SYLLABLE HOELS +D699;D699;1112 116C 11B4;D699;1112 116C 11B4; # (횙; 횙; 횙; 횙; 횙; ) HANGUL SYLLABLE HOELT +D69A;D69A;1112 116C 11B5;D69A;1112 116C 11B5; # (횚; 횚; 횚; 횚; 횚; ) HANGUL SYLLABLE HOELP +D69B;D69B;1112 116C 11B6;D69B;1112 116C 11B6; # (횛; 횛; 횛; 횛; 횛; ) HANGUL SYLLABLE HOELH +D69C;D69C;1112 116C 11B7;D69C;1112 116C 11B7; # (횜; 횜; 횜; 횜; 횜; ) HANGUL SYLLABLE HOEM +D69D;D69D;1112 116C 11B8;D69D;1112 116C 11B8; # (횝; 횝; 횝; 횝; 횝; ) HANGUL SYLLABLE HOEB +D69E;D69E;1112 116C 11B9;D69E;1112 116C 11B9; # (횞; 횞; 횞; 횞; 횞; ) HANGUL SYLLABLE HOEBS +D69F;D69F;1112 116C 11BA;D69F;1112 116C 11BA; # (횟; 횟; 횟; 횟; 횟; ) HANGUL SYLLABLE HOES +D6A0;D6A0;1112 116C 11BB;D6A0;1112 116C 11BB; # (횠; 횠; 횠; 횠; 횠; ) HANGUL SYLLABLE HOESS +D6A1;D6A1;1112 116C 11BC;D6A1;1112 116C 11BC; # (횡; 횡; 횡; 횡; 횡; ) HANGUL SYLLABLE HOENG +D6A2;D6A2;1112 116C 11BD;D6A2;1112 116C 11BD; # (횢; 횢; 횢; 횢; 횢; ) HANGUL SYLLABLE HOEJ +D6A3;D6A3;1112 116C 11BE;D6A3;1112 116C 11BE; # (횣; 횣; 횣; 횣; 횣; ) HANGUL SYLLABLE HOEC +D6A4;D6A4;1112 116C 11BF;D6A4;1112 116C 11BF; # (횤; 횤; 횤; 횤; 횤; ) HANGUL SYLLABLE HOEK +D6A5;D6A5;1112 116C 11C0;D6A5;1112 116C 11C0; # (횥; 횥; 횥; 횥; 횥; ) HANGUL SYLLABLE HOET +D6A6;D6A6;1112 116C 11C1;D6A6;1112 116C 11C1; # (횦; 횦; 횦; 횦; 횦; ) HANGUL SYLLABLE HOEP +D6A7;D6A7;1112 116C 11C2;D6A7;1112 116C 11C2; # (횧; 횧; 횧; 횧; 횧; ) HANGUL SYLLABLE HOEH +D6A8;D6A8;1112 116D;D6A8;1112 116D; # (효; 효; 효; 효; 효; ) HANGUL SYLLABLE HYO +D6A9;D6A9;1112 116D 11A8;D6A9;1112 116D 11A8; # (횩; 횩; 횩; 횩; 횩; ) HANGUL SYLLABLE HYOG +D6AA;D6AA;1112 116D 11A9;D6AA;1112 116D 11A9; # (횪; 횪; 횪; 횪; 횪; ) HANGUL SYLLABLE HYOGG +D6AB;D6AB;1112 116D 11AA;D6AB;1112 116D 11AA; # (횫; 횫; 횫; 횫; 횫; ) HANGUL SYLLABLE HYOGS +D6AC;D6AC;1112 116D 11AB;D6AC;1112 116D 11AB; # (횬; 횬; 횬; 횬; 횬; ) HANGUL SYLLABLE HYON +D6AD;D6AD;1112 116D 11AC;D6AD;1112 116D 11AC; # (횭; 횭; 횭; 횭; 횭; ) HANGUL SYLLABLE HYONJ +D6AE;D6AE;1112 116D 11AD;D6AE;1112 116D 11AD; # (횮; 횮; 횮; 횮; 횮; ) HANGUL SYLLABLE HYONH +D6AF;D6AF;1112 116D 11AE;D6AF;1112 116D 11AE; # (횯; 횯; 횯; 횯; 횯; ) HANGUL SYLLABLE HYOD +D6B0;D6B0;1112 116D 11AF;D6B0;1112 116D 11AF; # (횰; 횰; 횰; 횰; 횰; ) HANGUL SYLLABLE HYOL +D6B1;D6B1;1112 116D 11B0;D6B1;1112 116D 11B0; # (횱; 횱; 횱; 횱; 횱; ) HANGUL SYLLABLE HYOLG +D6B2;D6B2;1112 116D 11B1;D6B2;1112 116D 11B1; # (횲; 횲; 횲; 횲; 횲; ) HANGUL SYLLABLE HYOLM +D6B3;D6B3;1112 116D 11B2;D6B3;1112 116D 11B2; # (횳; 횳; 횳; 횳; 횳; ) HANGUL SYLLABLE HYOLB +D6B4;D6B4;1112 116D 11B3;D6B4;1112 116D 11B3; # (횴; 횴; 횴; 횴; 횴; ) HANGUL SYLLABLE HYOLS +D6B5;D6B5;1112 116D 11B4;D6B5;1112 116D 11B4; # (횵; 횵; 횵; 횵; 횵; ) HANGUL SYLLABLE HYOLT +D6B6;D6B6;1112 116D 11B5;D6B6;1112 116D 11B5; # (횶; 횶; 횶; 횶; 횶; ) HANGUL SYLLABLE HYOLP +D6B7;D6B7;1112 116D 11B6;D6B7;1112 116D 11B6; # (횷; 횷; 횷; 횷; 횷; ) HANGUL SYLLABLE HYOLH +D6B8;D6B8;1112 116D 11B7;D6B8;1112 116D 11B7; # (횸; 횸; 횸; 횸; 횸; ) HANGUL SYLLABLE HYOM +D6B9;D6B9;1112 116D 11B8;D6B9;1112 116D 11B8; # (횹; 횹; 횹; 횹; 횹; ) HANGUL SYLLABLE HYOB +D6BA;D6BA;1112 116D 11B9;D6BA;1112 116D 11B9; # (횺; 횺; 횺; 횺; 횺; ) HANGUL SYLLABLE HYOBS +D6BB;D6BB;1112 116D 11BA;D6BB;1112 116D 11BA; # (횻; 횻; 횻; 횻; 횻; ) HANGUL SYLLABLE HYOS +D6BC;D6BC;1112 116D 11BB;D6BC;1112 116D 11BB; # (횼; 횼; 횼; 횼; 횼; ) HANGUL SYLLABLE HYOSS +D6BD;D6BD;1112 116D 11BC;D6BD;1112 116D 11BC; # (횽; 횽; 횽; 횽; 횽; ) HANGUL SYLLABLE HYONG +D6BE;D6BE;1112 116D 11BD;D6BE;1112 116D 11BD; # (횾; 횾; 횾; 횾; 횾; ) HANGUL SYLLABLE HYOJ +D6BF;D6BF;1112 116D 11BE;D6BF;1112 116D 11BE; # (횿; 횿; 횿; 횿; 횿; ) HANGUL SYLLABLE HYOC +D6C0;D6C0;1112 116D 11BF;D6C0;1112 116D 11BF; # (훀; 훀; 훀; 훀; 훀; ) HANGUL SYLLABLE HYOK +D6C1;D6C1;1112 116D 11C0;D6C1;1112 116D 11C0; # (훁; 훁; 훁; 훁; 훁; ) HANGUL SYLLABLE HYOT +D6C2;D6C2;1112 116D 11C1;D6C2;1112 116D 11C1; # (훂; 훂; 훂; 훂; 훂; ) HANGUL SYLLABLE HYOP +D6C3;D6C3;1112 116D 11C2;D6C3;1112 116D 11C2; # (훃; 훃; 훃; 훃; 훃; ) HANGUL SYLLABLE HYOH +D6C4;D6C4;1112 116E;D6C4;1112 116E; # (후; 후; 후; 후; 후; ) HANGUL SYLLABLE HU +D6C5;D6C5;1112 116E 11A8;D6C5;1112 116E 11A8; # (훅; 훅; 훅; 훅; 훅; ) HANGUL SYLLABLE HUG +D6C6;D6C6;1112 116E 11A9;D6C6;1112 116E 11A9; # (훆; 훆; 훆; 훆; 훆; ) HANGUL SYLLABLE HUGG +D6C7;D6C7;1112 116E 11AA;D6C7;1112 116E 11AA; # (훇; 훇; 훇; 훇; 훇; ) HANGUL SYLLABLE HUGS +D6C8;D6C8;1112 116E 11AB;D6C8;1112 116E 11AB; # (훈; 훈; 훈; 훈; 훈; ) HANGUL SYLLABLE HUN +D6C9;D6C9;1112 116E 11AC;D6C9;1112 116E 11AC; # (훉; 훉; 훉; 훉; 훉; ) HANGUL SYLLABLE HUNJ +D6CA;D6CA;1112 116E 11AD;D6CA;1112 116E 11AD; # (훊; 훊; 훊; 훊; 훊; ) HANGUL SYLLABLE HUNH +D6CB;D6CB;1112 116E 11AE;D6CB;1112 116E 11AE; # (훋; 훋; 훋; 훋; 훋; ) HANGUL SYLLABLE HUD +D6CC;D6CC;1112 116E 11AF;D6CC;1112 116E 11AF; # (훌; 훌; 훌; 훌; 훌; ) HANGUL SYLLABLE HUL +D6CD;D6CD;1112 116E 11B0;D6CD;1112 116E 11B0; # (훍; 훍; 훍; 훍; 훍; ) HANGUL SYLLABLE HULG +D6CE;D6CE;1112 116E 11B1;D6CE;1112 116E 11B1; # (훎; 훎; 훎; 훎; 훎; ) HANGUL SYLLABLE HULM +D6CF;D6CF;1112 116E 11B2;D6CF;1112 116E 11B2; # (훏; 훏; 훏; 훏; 훏; ) HANGUL SYLLABLE HULB +D6D0;D6D0;1112 116E 11B3;D6D0;1112 116E 11B3; # (훐; 훐; 훐; 훐; 훐; ) HANGUL SYLLABLE HULS +D6D1;D6D1;1112 116E 11B4;D6D1;1112 116E 11B4; # (훑; 훑; 훑; 훑; 훑; ) HANGUL SYLLABLE HULT +D6D2;D6D2;1112 116E 11B5;D6D2;1112 116E 11B5; # (훒; 훒; 훒; 훒; 훒; ) HANGUL SYLLABLE HULP +D6D3;D6D3;1112 116E 11B6;D6D3;1112 116E 11B6; # (훓; 훓; 훓; 훓; 훓; ) HANGUL SYLLABLE HULH +D6D4;D6D4;1112 116E 11B7;D6D4;1112 116E 11B7; # (훔; 훔; 훔; 훔; 훔; ) HANGUL SYLLABLE HUM +D6D5;D6D5;1112 116E 11B8;D6D5;1112 116E 11B8; # (훕; 훕; 훕; 훕; 훕; ) HANGUL SYLLABLE HUB +D6D6;D6D6;1112 116E 11B9;D6D6;1112 116E 11B9; # (훖; 훖; 훖; 훖; 훖; ) HANGUL SYLLABLE HUBS +D6D7;D6D7;1112 116E 11BA;D6D7;1112 116E 11BA; # (훗; 훗; 훗; 훗; 훗; ) HANGUL SYLLABLE HUS +D6D8;D6D8;1112 116E 11BB;D6D8;1112 116E 11BB; # (훘; 훘; 훘; 훘; 훘; ) HANGUL SYLLABLE HUSS +D6D9;D6D9;1112 116E 11BC;D6D9;1112 116E 11BC; # (훙; 훙; 훙; 훙; 훙; ) HANGUL SYLLABLE HUNG +D6DA;D6DA;1112 116E 11BD;D6DA;1112 116E 11BD; # (훚; 훚; 훚; 훚; 훚; ) HANGUL SYLLABLE HUJ +D6DB;D6DB;1112 116E 11BE;D6DB;1112 116E 11BE; # (훛; 훛; 훛; 훛; 훛; ) HANGUL SYLLABLE HUC +D6DC;D6DC;1112 116E 11BF;D6DC;1112 116E 11BF; # (훜; 훜; 훜; 훜; 훜; ) HANGUL SYLLABLE HUK +D6DD;D6DD;1112 116E 11C0;D6DD;1112 116E 11C0; # (훝; 훝; 훝; 훝; 훝; ) HANGUL SYLLABLE HUT +D6DE;D6DE;1112 116E 11C1;D6DE;1112 116E 11C1; # (훞; 훞; 훞; 훞; 훞; ) HANGUL SYLLABLE HUP +D6DF;D6DF;1112 116E 11C2;D6DF;1112 116E 11C2; # (훟; 훟; 훟; 훟; 훟; ) HANGUL SYLLABLE HUH +D6E0;D6E0;1112 116F;D6E0;1112 116F; # (훠; 훠; 훠; 훠; 훠; ) HANGUL SYLLABLE HWEO +D6E1;D6E1;1112 116F 11A8;D6E1;1112 116F 11A8; # (훡; 훡; 훡; 훡; 훡; ) HANGUL SYLLABLE HWEOG +D6E2;D6E2;1112 116F 11A9;D6E2;1112 116F 11A9; # (훢; 훢; 훢; 훢; 훢; ) HANGUL SYLLABLE HWEOGG +D6E3;D6E3;1112 116F 11AA;D6E3;1112 116F 11AA; # (훣; 훣; 훣; 훣; 훣; ) HANGUL SYLLABLE HWEOGS +D6E4;D6E4;1112 116F 11AB;D6E4;1112 116F 11AB; # (훤; 훤; 훤; 훤; 훤; ) HANGUL SYLLABLE HWEON +D6E5;D6E5;1112 116F 11AC;D6E5;1112 116F 11AC; # (훥; 훥; 훥; 훥; 훥; ) HANGUL SYLLABLE HWEONJ +D6E6;D6E6;1112 116F 11AD;D6E6;1112 116F 11AD; # (훦; 훦; 훦; 훦; 훦; ) HANGUL SYLLABLE HWEONH +D6E7;D6E7;1112 116F 11AE;D6E7;1112 116F 11AE; # (훧; 훧; 훧; 훧; 훧; ) HANGUL SYLLABLE HWEOD +D6E8;D6E8;1112 116F 11AF;D6E8;1112 116F 11AF; # (훨; 훨; 훨; 훨; 훨; ) HANGUL SYLLABLE HWEOL +D6E9;D6E9;1112 116F 11B0;D6E9;1112 116F 11B0; # (훩; 훩; 훩; 훩; 훩; ) HANGUL SYLLABLE HWEOLG +D6EA;D6EA;1112 116F 11B1;D6EA;1112 116F 11B1; # (훪; 훪; 훪; 훪; 훪; ) HANGUL SYLLABLE HWEOLM +D6EB;D6EB;1112 116F 11B2;D6EB;1112 116F 11B2; # (훫; 훫; 훫; 훫; 훫; ) HANGUL SYLLABLE HWEOLB +D6EC;D6EC;1112 116F 11B3;D6EC;1112 116F 11B3; # (훬; 훬; 훬; 훬; 훬; ) HANGUL SYLLABLE HWEOLS +D6ED;D6ED;1112 116F 11B4;D6ED;1112 116F 11B4; # (훭; 훭; 훭; 훭; 훭; ) HANGUL SYLLABLE HWEOLT +D6EE;D6EE;1112 116F 11B5;D6EE;1112 116F 11B5; # (훮; 훮; 훮; 훮; 훮; ) HANGUL SYLLABLE HWEOLP +D6EF;D6EF;1112 116F 11B6;D6EF;1112 116F 11B6; # (훯; 훯; 훯; 훯; 훯; ) HANGUL SYLLABLE HWEOLH +D6F0;D6F0;1112 116F 11B7;D6F0;1112 116F 11B7; # (훰; 훰; 훰; 훰; 훰; ) HANGUL SYLLABLE HWEOM +D6F1;D6F1;1112 116F 11B8;D6F1;1112 116F 11B8; # (훱; 훱; 훱; 훱; 훱; ) HANGUL SYLLABLE HWEOB +D6F2;D6F2;1112 116F 11B9;D6F2;1112 116F 11B9; # (훲; 훲; 훲; 훲; 훲; ) HANGUL SYLLABLE HWEOBS +D6F3;D6F3;1112 116F 11BA;D6F3;1112 116F 11BA; # (훳; 훳; 훳; 훳; 훳; ) HANGUL SYLLABLE HWEOS +D6F4;D6F4;1112 116F 11BB;D6F4;1112 116F 11BB; # (훴; 훴; 훴; 훴; 훴; ) HANGUL SYLLABLE HWEOSS +D6F5;D6F5;1112 116F 11BC;D6F5;1112 116F 11BC; # (훵; 훵; 훵; 훵; 훵; ) HANGUL SYLLABLE HWEONG +D6F6;D6F6;1112 116F 11BD;D6F6;1112 116F 11BD; # (훶; 훶; 훶; 훶; 훶; ) HANGUL SYLLABLE HWEOJ +D6F7;D6F7;1112 116F 11BE;D6F7;1112 116F 11BE; # (훷; 훷; 훷; 훷; 훷; ) HANGUL SYLLABLE HWEOC +D6F8;D6F8;1112 116F 11BF;D6F8;1112 116F 11BF; # (훸; 훸; 훸; 훸; 훸; ) HANGUL SYLLABLE HWEOK +D6F9;D6F9;1112 116F 11C0;D6F9;1112 116F 11C0; # (훹; 훹; 훹; 훹; 훹; ) HANGUL SYLLABLE HWEOT +D6FA;D6FA;1112 116F 11C1;D6FA;1112 116F 11C1; # (훺; 훺; 훺; 훺; 훺; ) HANGUL SYLLABLE HWEOP +D6FB;D6FB;1112 116F 11C2;D6FB;1112 116F 11C2; # (훻; 훻; 훻; 훻; 훻; ) HANGUL SYLLABLE HWEOH +D6FC;D6FC;1112 1170;D6FC;1112 1170; # (훼; 훼; 훼; 훼; 훼; ) HANGUL SYLLABLE HWE +D6FD;D6FD;1112 1170 11A8;D6FD;1112 1170 11A8; # (훽; 훽; 훽; 훽; 훽; ) HANGUL SYLLABLE HWEG +D6FE;D6FE;1112 1170 11A9;D6FE;1112 1170 11A9; # (훾; 훾; 훾; 훾; 훾; ) HANGUL SYLLABLE HWEGG +D6FF;D6FF;1112 1170 11AA;D6FF;1112 1170 11AA; # (훿; 훿; 훿; 훿; 훿; ) HANGUL SYLLABLE HWEGS +D700;D700;1112 1170 11AB;D700;1112 1170 11AB; # (휀; 휀; 휀; 휀; 휀; ) HANGUL SYLLABLE HWEN +D701;D701;1112 1170 11AC;D701;1112 1170 11AC; # (휁; 휁; 휁; 휁; 휁; ) HANGUL SYLLABLE HWENJ +D702;D702;1112 1170 11AD;D702;1112 1170 11AD; # (휂; 휂; 휂; 휂; 휂; ) HANGUL SYLLABLE HWENH +D703;D703;1112 1170 11AE;D703;1112 1170 11AE; # (휃; 휃; 휃; 휃; 휃; ) HANGUL SYLLABLE HWED +D704;D704;1112 1170 11AF;D704;1112 1170 11AF; # (휄; 휄; 휄; 휄; 휄; ) HANGUL SYLLABLE HWEL +D705;D705;1112 1170 11B0;D705;1112 1170 11B0; # (휅; 휅; 휅; 휅; 휅; ) HANGUL SYLLABLE HWELG +D706;D706;1112 1170 11B1;D706;1112 1170 11B1; # (휆; 휆; 휆; 휆; 휆; ) HANGUL SYLLABLE HWELM +D707;D707;1112 1170 11B2;D707;1112 1170 11B2; # (휇; 휇; 휇; 휇; 휇; ) HANGUL SYLLABLE HWELB +D708;D708;1112 1170 11B3;D708;1112 1170 11B3; # (휈; 휈; 휈; 휈; 휈; ) HANGUL SYLLABLE HWELS +D709;D709;1112 1170 11B4;D709;1112 1170 11B4; # (휉; 휉; 휉; 휉; 휉; ) HANGUL SYLLABLE HWELT +D70A;D70A;1112 1170 11B5;D70A;1112 1170 11B5; # (휊; 휊; 휊; 휊; 휊; ) HANGUL SYLLABLE HWELP +D70B;D70B;1112 1170 11B6;D70B;1112 1170 11B6; # (휋; 휋; 휋; 휋; 휋; ) HANGUL SYLLABLE HWELH +D70C;D70C;1112 1170 11B7;D70C;1112 1170 11B7; # (휌; 휌; 휌; 휌; 휌; ) HANGUL SYLLABLE HWEM +D70D;D70D;1112 1170 11B8;D70D;1112 1170 11B8; # (휍; 휍; 휍; 휍; 휍; ) HANGUL SYLLABLE HWEB +D70E;D70E;1112 1170 11B9;D70E;1112 1170 11B9; # (휎; 휎; 휎; 휎; 휎; ) HANGUL SYLLABLE HWEBS +D70F;D70F;1112 1170 11BA;D70F;1112 1170 11BA; # (휏; 휏; 휏; 휏; 휏; ) HANGUL SYLLABLE HWES +D710;D710;1112 1170 11BB;D710;1112 1170 11BB; # (휐; 휐; 휐; 휐; 휐; ) HANGUL SYLLABLE HWESS +D711;D711;1112 1170 11BC;D711;1112 1170 11BC; # (휑; 휑; 휑; 휑; 휑; ) HANGUL SYLLABLE HWENG +D712;D712;1112 1170 11BD;D712;1112 1170 11BD; # (휒; 휒; 휒; 휒; 휒; ) HANGUL SYLLABLE HWEJ +D713;D713;1112 1170 11BE;D713;1112 1170 11BE; # (휓; 휓; 휓; 휓; 휓; ) HANGUL SYLLABLE HWEC +D714;D714;1112 1170 11BF;D714;1112 1170 11BF; # (휔; 휔; 휔; 휔; 휔; ) HANGUL SYLLABLE HWEK +D715;D715;1112 1170 11C0;D715;1112 1170 11C0; # (휕; 휕; 휕; 휕; 휕; ) HANGUL SYLLABLE HWET +D716;D716;1112 1170 11C1;D716;1112 1170 11C1; # (휖; 휖; 휖; 휖; 휖; ) HANGUL SYLLABLE HWEP +D717;D717;1112 1170 11C2;D717;1112 1170 11C2; # (휗; 휗; 휗; 휗; 휗; ) HANGUL SYLLABLE HWEH +D718;D718;1112 1171;D718;1112 1171; # (휘; 휘; 휘; 휘; 휘; ) HANGUL SYLLABLE HWI +D719;D719;1112 1171 11A8;D719;1112 1171 11A8; # (휙; 휙; 휙; 휙; 휙; ) HANGUL SYLLABLE HWIG +D71A;D71A;1112 1171 11A9;D71A;1112 1171 11A9; # (휚; 휚; 휚; 휚; 휚; ) HANGUL SYLLABLE HWIGG +D71B;D71B;1112 1171 11AA;D71B;1112 1171 11AA; # (휛; 휛; 휛; 휛; 휛; ) HANGUL SYLLABLE HWIGS +D71C;D71C;1112 1171 11AB;D71C;1112 1171 11AB; # (휜; 휜; 휜; 휜; 휜; ) HANGUL SYLLABLE HWIN +D71D;D71D;1112 1171 11AC;D71D;1112 1171 11AC; # (휝; 휝; 휝; 휝; 휝; ) HANGUL SYLLABLE HWINJ +D71E;D71E;1112 1171 11AD;D71E;1112 1171 11AD; # (휞; 휞; 휞; 휞; 휞; ) HANGUL SYLLABLE HWINH +D71F;D71F;1112 1171 11AE;D71F;1112 1171 11AE; # (휟; 휟; 휟; 휟; 휟; ) HANGUL SYLLABLE HWID +D720;D720;1112 1171 11AF;D720;1112 1171 11AF; # (휠; 휠; 휠; 휠; 휠; ) HANGUL SYLLABLE HWIL +D721;D721;1112 1171 11B0;D721;1112 1171 11B0; # (휡; 휡; 휡; 휡; 휡; ) HANGUL SYLLABLE HWILG +D722;D722;1112 1171 11B1;D722;1112 1171 11B1; # (휢; 휢; 휢; 휢; 휢; ) HANGUL SYLLABLE HWILM +D723;D723;1112 1171 11B2;D723;1112 1171 11B2; # (휣; 휣; 휣; 휣; 휣; ) HANGUL SYLLABLE HWILB +D724;D724;1112 1171 11B3;D724;1112 1171 11B3; # (휤; 휤; 휤; 휤; 휤; ) HANGUL SYLLABLE HWILS +D725;D725;1112 1171 11B4;D725;1112 1171 11B4; # (휥; 휥; 휥; 휥; 휥; ) HANGUL SYLLABLE HWILT +D726;D726;1112 1171 11B5;D726;1112 1171 11B5; # (휦; 휦; 휦; 휦; 휦; ) HANGUL SYLLABLE HWILP +D727;D727;1112 1171 11B6;D727;1112 1171 11B6; # (휧; 휧; 휧; 휧; 휧; ) HANGUL SYLLABLE HWILH +D728;D728;1112 1171 11B7;D728;1112 1171 11B7; # (휨; 휨; 휨; 휨; 휨; ) HANGUL SYLLABLE HWIM +D729;D729;1112 1171 11B8;D729;1112 1171 11B8; # (휩; 휩; 휩; 휩; 휩; ) HANGUL SYLLABLE HWIB +D72A;D72A;1112 1171 11B9;D72A;1112 1171 11B9; # (휪; 휪; 휪; 휪; 휪; ) HANGUL SYLLABLE HWIBS +D72B;D72B;1112 1171 11BA;D72B;1112 1171 11BA; # (휫; 휫; 휫; 휫; 휫; ) HANGUL SYLLABLE HWIS +D72C;D72C;1112 1171 11BB;D72C;1112 1171 11BB; # (휬; 휬; 휬; 휬; 휬; ) HANGUL SYLLABLE HWISS +D72D;D72D;1112 1171 11BC;D72D;1112 1171 11BC; # (휭; 휭; 휭; 휭; 휭; ) HANGUL SYLLABLE HWING +D72E;D72E;1112 1171 11BD;D72E;1112 1171 11BD; # (휮; 휮; 휮; 휮; 휮; ) HANGUL SYLLABLE HWIJ +D72F;D72F;1112 1171 11BE;D72F;1112 1171 11BE; # (휯; 휯; 휯; 휯; 휯; ) HANGUL SYLLABLE HWIC +D730;D730;1112 1171 11BF;D730;1112 1171 11BF; # (휰; 휰; 휰; 휰; 휰; ) HANGUL SYLLABLE HWIK +D731;D731;1112 1171 11C0;D731;1112 1171 11C0; # (휱; 휱; 휱; 휱; 휱; ) HANGUL SYLLABLE HWIT +D732;D732;1112 1171 11C1;D732;1112 1171 11C1; # (휲; 휲; 휲; 휲; 휲; ) HANGUL SYLLABLE HWIP +D733;D733;1112 1171 11C2;D733;1112 1171 11C2; # (휳; 휳; 휳; 휳; 휳; ) HANGUL SYLLABLE HWIH +D734;D734;1112 1172;D734;1112 1172; # (휴; 휴; 휴; 휴; 휴; ) HANGUL SYLLABLE HYU +D735;D735;1112 1172 11A8;D735;1112 1172 11A8; # (휵; 휵; 휵; 휵; 휵; ) HANGUL SYLLABLE HYUG +D736;D736;1112 1172 11A9;D736;1112 1172 11A9; # (휶; 휶; 휶; 휶; 휶; ) HANGUL SYLLABLE HYUGG +D737;D737;1112 1172 11AA;D737;1112 1172 11AA; # (휷; 휷; 휷; 휷; 휷; ) HANGUL SYLLABLE HYUGS +D738;D738;1112 1172 11AB;D738;1112 1172 11AB; # (휸; 휸; 휸; 휸; 휸; ) HANGUL SYLLABLE HYUN +D739;D739;1112 1172 11AC;D739;1112 1172 11AC; # (휹; 휹; 휹; 휹; 휹; ) HANGUL SYLLABLE HYUNJ +D73A;D73A;1112 1172 11AD;D73A;1112 1172 11AD; # (휺; 휺; 휺; 휺; 휺; ) HANGUL SYLLABLE HYUNH +D73B;D73B;1112 1172 11AE;D73B;1112 1172 11AE; # (휻; 휻; 휻; 휻; 휻; ) HANGUL SYLLABLE HYUD +D73C;D73C;1112 1172 11AF;D73C;1112 1172 11AF; # (휼; 휼; 휼; 휼; 휼; ) HANGUL SYLLABLE HYUL +D73D;D73D;1112 1172 11B0;D73D;1112 1172 11B0; # (휽; 휽; 휽; 휽; 휽; ) HANGUL SYLLABLE HYULG +D73E;D73E;1112 1172 11B1;D73E;1112 1172 11B1; # (휾; 휾; 휾; 휾; 휾; ) HANGUL SYLLABLE HYULM +D73F;D73F;1112 1172 11B2;D73F;1112 1172 11B2; # (휿; 휿; 휿; 휿; 휿; ) HANGUL SYLLABLE HYULB +D740;D740;1112 1172 11B3;D740;1112 1172 11B3; # (흀; 흀; 흀; 흀; 흀; ) HANGUL SYLLABLE HYULS +D741;D741;1112 1172 11B4;D741;1112 1172 11B4; # (흁; 흁; 흁; 흁; 흁; ) HANGUL SYLLABLE HYULT +D742;D742;1112 1172 11B5;D742;1112 1172 11B5; # (흂; 흂; 흂; 흂; 흂; ) HANGUL SYLLABLE HYULP +D743;D743;1112 1172 11B6;D743;1112 1172 11B6; # (흃; 흃; 흃; 흃; 흃; ) HANGUL SYLLABLE HYULH +D744;D744;1112 1172 11B7;D744;1112 1172 11B7; # (흄; 흄; 흄; 흄; 흄; ) HANGUL SYLLABLE HYUM +D745;D745;1112 1172 11B8;D745;1112 1172 11B8; # (흅; 흅; 흅; 흅; 흅; ) HANGUL SYLLABLE HYUB +D746;D746;1112 1172 11B9;D746;1112 1172 11B9; # (흆; 흆; 흆; 흆; 흆; ) HANGUL SYLLABLE HYUBS +D747;D747;1112 1172 11BA;D747;1112 1172 11BA; # (흇; 흇; 흇; 흇; 흇; ) HANGUL SYLLABLE HYUS +D748;D748;1112 1172 11BB;D748;1112 1172 11BB; # (흈; 흈; 흈; 흈; 흈; ) HANGUL SYLLABLE HYUSS +D749;D749;1112 1172 11BC;D749;1112 1172 11BC; # (흉; 흉; 흉; 흉; 흉; ) HANGUL SYLLABLE HYUNG +D74A;D74A;1112 1172 11BD;D74A;1112 1172 11BD; # (흊; 흊; 흊; 흊; 흊; ) HANGUL SYLLABLE HYUJ +D74B;D74B;1112 1172 11BE;D74B;1112 1172 11BE; # (흋; 흋; 흋; 흋; 흋; ) HANGUL SYLLABLE HYUC +D74C;D74C;1112 1172 11BF;D74C;1112 1172 11BF; # (흌; 흌; 흌; 흌; 흌; ) HANGUL SYLLABLE HYUK +D74D;D74D;1112 1172 11C0;D74D;1112 1172 11C0; # (흍; 흍; 흍; 흍; 흍; ) HANGUL SYLLABLE HYUT +D74E;D74E;1112 1172 11C1;D74E;1112 1172 11C1; # (흎; 흎; 흎; 흎; 흎; ) HANGUL SYLLABLE HYUP +D74F;D74F;1112 1172 11C2;D74F;1112 1172 11C2; # (흏; 흏; 흏; 흏; 흏; ) HANGUL SYLLABLE HYUH +D750;D750;1112 1173;D750;1112 1173; # (흐; 흐; 흐; 흐; 흐; ) HANGUL SYLLABLE HEU +D751;D751;1112 1173 11A8;D751;1112 1173 11A8; # (흑; 흑; 흑; 흑; 흑; ) HANGUL SYLLABLE HEUG +D752;D752;1112 1173 11A9;D752;1112 1173 11A9; # (흒; 흒; 흒; 흒; 흒; ) HANGUL SYLLABLE HEUGG +D753;D753;1112 1173 11AA;D753;1112 1173 11AA; # (흓; 흓; 흓; 흓; 흓; ) HANGUL SYLLABLE HEUGS +D754;D754;1112 1173 11AB;D754;1112 1173 11AB; # (흔; 흔; 흔; 흔; 흔; ) HANGUL SYLLABLE HEUN +D755;D755;1112 1173 11AC;D755;1112 1173 11AC; # (흕; 흕; 흕; 흕; 흕; ) HANGUL SYLLABLE HEUNJ +D756;D756;1112 1173 11AD;D756;1112 1173 11AD; # (흖; 흖; 흖; 흖; 흖; ) HANGUL SYLLABLE HEUNH +D757;D757;1112 1173 11AE;D757;1112 1173 11AE; # (흗; 흗; 흗; 흗; 흗; ) HANGUL SYLLABLE HEUD +D758;D758;1112 1173 11AF;D758;1112 1173 11AF; # (흘; 흘; 흘; 흘; 흘; ) HANGUL SYLLABLE HEUL +D759;D759;1112 1173 11B0;D759;1112 1173 11B0; # (흙; 흙; 흙; 흙; 흙; ) HANGUL SYLLABLE HEULG +D75A;D75A;1112 1173 11B1;D75A;1112 1173 11B1; # (흚; 흚; 흚; 흚; 흚; ) HANGUL SYLLABLE HEULM +D75B;D75B;1112 1173 11B2;D75B;1112 1173 11B2; # (흛; 흛; 흛; 흛; 흛; ) HANGUL SYLLABLE HEULB +D75C;D75C;1112 1173 11B3;D75C;1112 1173 11B3; # (흜; 흜; 흜; 흜; 흜; ) HANGUL SYLLABLE HEULS +D75D;D75D;1112 1173 11B4;D75D;1112 1173 11B4; # (흝; 흝; 흝; 흝; 흝; ) HANGUL SYLLABLE HEULT +D75E;D75E;1112 1173 11B5;D75E;1112 1173 11B5; # (흞; 흞; 흞; 흞; 흞; ) HANGUL SYLLABLE HEULP +D75F;D75F;1112 1173 11B6;D75F;1112 1173 11B6; # (흟; 흟; 흟; 흟; 흟; ) HANGUL SYLLABLE HEULH +D760;D760;1112 1173 11B7;D760;1112 1173 11B7; # (흠; 흠; 흠; 흠; 흠; ) HANGUL SYLLABLE HEUM +D761;D761;1112 1173 11B8;D761;1112 1173 11B8; # (흡; 흡; 흡; 흡; 흡; ) HANGUL SYLLABLE HEUB +D762;D762;1112 1173 11B9;D762;1112 1173 11B9; # (흢; 흢; 흢; 흢; 흢; ) HANGUL SYLLABLE HEUBS +D763;D763;1112 1173 11BA;D763;1112 1173 11BA; # (흣; 흣; 흣; 흣; 흣; ) HANGUL SYLLABLE HEUS +D764;D764;1112 1173 11BB;D764;1112 1173 11BB; # (흤; 흤; 흤; 흤; 흤; ) HANGUL SYLLABLE HEUSS +D765;D765;1112 1173 11BC;D765;1112 1173 11BC; # (흥; 흥; 흥; 흥; 흥; ) HANGUL SYLLABLE HEUNG +D766;D766;1112 1173 11BD;D766;1112 1173 11BD; # (흦; 흦; 흦; 흦; 흦; ) HANGUL SYLLABLE HEUJ +D767;D767;1112 1173 11BE;D767;1112 1173 11BE; # (흧; 흧; 흧; 흧; 흧; ) HANGUL SYLLABLE HEUC +D768;D768;1112 1173 11BF;D768;1112 1173 11BF; # (흨; 흨; 흨; 흨; 흨; ) HANGUL SYLLABLE HEUK +D769;D769;1112 1173 11C0;D769;1112 1173 11C0; # (흩; 흩; 흩; 흩; 흩; ) HANGUL SYLLABLE HEUT +D76A;D76A;1112 1173 11C1;D76A;1112 1173 11C1; # (흪; 흪; 흪; 흪; 흪; ) HANGUL SYLLABLE HEUP +D76B;D76B;1112 1173 11C2;D76B;1112 1173 11C2; # (흫; 흫; 흫; 흫; 흫; ) HANGUL SYLLABLE HEUH +D76C;D76C;1112 1174;D76C;1112 1174; # (희; 희; 희; 희; 희; ) HANGUL SYLLABLE HYI +D76D;D76D;1112 1174 11A8;D76D;1112 1174 11A8; # (흭; 흭; 흭; 흭; 흭; ) HANGUL SYLLABLE HYIG +D76E;D76E;1112 1174 11A9;D76E;1112 1174 11A9; # (흮; 흮; 흮; 흮; 흮; ) HANGUL SYLLABLE HYIGG +D76F;D76F;1112 1174 11AA;D76F;1112 1174 11AA; # (흯; 흯; 흯; 흯; 흯; ) HANGUL SYLLABLE HYIGS +D770;D770;1112 1174 11AB;D770;1112 1174 11AB; # (흰; 흰; 흰; 흰; 흰; ) HANGUL SYLLABLE HYIN +D771;D771;1112 1174 11AC;D771;1112 1174 11AC; # (흱; 흱; 흱; 흱; 흱; ) HANGUL SYLLABLE HYINJ +D772;D772;1112 1174 11AD;D772;1112 1174 11AD; # (흲; 흲; 흲; 흲; 흲; ) HANGUL SYLLABLE HYINH +D773;D773;1112 1174 11AE;D773;1112 1174 11AE; # (흳; 흳; 흳; 흳; 흳; ) HANGUL SYLLABLE HYID +D774;D774;1112 1174 11AF;D774;1112 1174 11AF; # (흴; 흴; 흴; 흴; 흴; ) HANGUL SYLLABLE HYIL +D775;D775;1112 1174 11B0;D775;1112 1174 11B0; # (흵; 흵; 흵; 흵; 흵; ) HANGUL SYLLABLE HYILG +D776;D776;1112 1174 11B1;D776;1112 1174 11B1; # (흶; 흶; 흶; 흶; 흶; ) HANGUL SYLLABLE HYILM +D777;D777;1112 1174 11B2;D777;1112 1174 11B2; # (흷; 흷; 흷; 흷; 흷; ) HANGUL SYLLABLE HYILB +D778;D778;1112 1174 11B3;D778;1112 1174 11B3; # (흸; 흸; 흸; 흸; 흸; ) HANGUL SYLLABLE HYILS +D779;D779;1112 1174 11B4;D779;1112 1174 11B4; # (흹; 흹; 흹; 흹; 흹; ) HANGUL SYLLABLE HYILT +D77A;D77A;1112 1174 11B5;D77A;1112 1174 11B5; # (흺; 흺; 흺; 흺; 흺; ) HANGUL SYLLABLE HYILP +D77B;D77B;1112 1174 11B6;D77B;1112 1174 11B6; # (흻; 흻; 흻; 흻; 흻; ) HANGUL SYLLABLE HYILH +D77C;D77C;1112 1174 11B7;D77C;1112 1174 11B7; # (흼; 흼; 흼; 흼; 흼; ) HANGUL SYLLABLE HYIM +D77D;D77D;1112 1174 11B8;D77D;1112 1174 11B8; # (흽; 흽; 흽; 흽; 흽; ) HANGUL SYLLABLE HYIB +D77E;D77E;1112 1174 11B9;D77E;1112 1174 11B9; # (흾; 흾; 흾; 흾; 흾; ) HANGUL SYLLABLE HYIBS +D77F;D77F;1112 1174 11BA;D77F;1112 1174 11BA; # (흿; 흿; 흿; 흿; 흿; ) HANGUL SYLLABLE HYIS +D780;D780;1112 1174 11BB;D780;1112 1174 11BB; # (힀; 힀; 힀; 힀; 힀; ) HANGUL SYLLABLE HYISS +D781;D781;1112 1174 11BC;D781;1112 1174 11BC; # (힁; 힁; 힁; 힁; 힁; ) HANGUL SYLLABLE HYING +D782;D782;1112 1174 11BD;D782;1112 1174 11BD; # (힂; 힂; 힂; 힂; 힂; ) HANGUL SYLLABLE HYIJ +D783;D783;1112 1174 11BE;D783;1112 1174 11BE; # (힃; 힃; 힃; 힃; 힃; ) HANGUL SYLLABLE HYIC +D784;D784;1112 1174 11BF;D784;1112 1174 11BF; # (힄; 힄; 힄; 힄; 힄; ) HANGUL SYLLABLE HYIK +D785;D785;1112 1174 11C0;D785;1112 1174 11C0; # (힅; 힅; 힅; 힅; 힅; ) HANGUL SYLLABLE HYIT +D786;D786;1112 1174 11C1;D786;1112 1174 11C1; # (힆; 힆; 힆; 힆; 힆; ) HANGUL SYLLABLE HYIP +D787;D787;1112 1174 11C2;D787;1112 1174 11C2; # (힇; 힇; 힇; 힇; 힇; ) HANGUL SYLLABLE HYIH +D788;D788;1112 1175;D788;1112 1175; # (히; 히; 히; 히; 히; ) HANGUL SYLLABLE HI +D789;D789;1112 1175 11A8;D789;1112 1175 11A8; # (힉; 힉; 힉; 힉; 힉; ) HANGUL SYLLABLE HIG +D78A;D78A;1112 1175 11A9;D78A;1112 1175 11A9; # (힊; 힊; 힊; 힊; 힊; ) HANGUL SYLLABLE HIGG +D78B;D78B;1112 1175 11AA;D78B;1112 1175 11AA; # (힋; 힋; 힋; 힋; 힋; ) HANGUL SYLLABLE HIGS +D78C;D78C;1112 1175 11AB;D78C;1112 1175 11AB; # (힌; 힌; 힌; 힌; 힌; ) HANGUL SYLLABLE HIN +D78D;D78D;1112 1175 11AC;D78D;1112 1175 11AC; # (힍; 힍; 힍; 힍; 힍; ) HANGUL SYLLABLE HINJ +D78E;D78E;1112 1175 11AD;D78E;1112 1175 11AD; # (힎; 힎; 힎; 힎; 힎; ) HANGUL SYLLABLE HINH +D78F;D78F;1112 1175 11AE;D78F;1112 1175 11AE; # (힏; 힏; 힏; 힏; 힏; ) HANGUL SYLLABLE HID +D790;D790;1112 1175 11AF;D790;1112 1175 11AF; # (힐; 힐; 힐; 힐; 힐; ) HANGUL SYLLABLE HIL +D791;D791;1112 1175 11B0;D791;1112 1175 11B0; # (힑; 힑; 힑; 힑; 힑; ) HANGUL SYLLABLE HILG +D792;D792;1112 1175 11B1;D792;1112 1175 11B1; # (힒; 힒; 힒; 힒; 힒; ) HANGUL SYLLABLE HILM +D793;D793;1112 1175 11B2;D793;1112 1175 11B2; # (힓; 힓; 힓; 힓; 힓; ) HANGUL SYLLABLE HILB +D794;D794;1112 1175 11B3;D794;1112 1175 11B3; # (힔; 힔; 힔; 힔; 힔; ) HANGUL SYLLABLE HILS +D795;D795;1112 1175 11B4;D795;1112 1175 11B4; # (힕; 힕; 힕; 힕; 힕; ) HANGUL SYLLABLE HILT +D796;D796;1112 1175 11B5;D796;1112 1175 11B5; # (힖; 힖; 힖; 힖; 힖; ) HANGUL SYLLABLE HILP +D797;D797;1112 1175 11B6;D797;1112 1175 11B6; # (힗; 힗; 힗; 힗; 힗; ) HANGUL SYLLABLE HILH +D798;D798;1112 1175 11B7;D798;1112 1175 11B7; # (힘; 힘; 힘; 힘; 힘; ) HANGUL SYLLABLE HIM +D799;D799;1112 1175 11B8;D799;1112 1175 11B8; # (힙; 힙; 힙; 힙; 힙; ) HANGUL SYLLABLE HIB +D79A;D79A;1112 1175 11B9;D79A;1112 1175 11B9; # (힚; 힚; 힚; 힚; 힚; ) HANGUL SYLLABLE HIBS +D79B;D79B;1112 1175 11BA;D79B;1112 1175 11BA; # (힛; 힛; 힛; 힛; 힛; ) HANGUL SYLLABLE HIS +D79C;D79C;1112 1175 11BB;D79C;1112 1175 11BB; # (힜; 힜; 힜; 힜; 힜; ) HANGUL SYLLABLE HISS +D79D;D79D;1112 1175 11BC;D79D;1112 1175 11BC; # (힝; 힝; 힝; 힝; 힝; ) HANGUL SYLLABLE HING +D79E;D79E;1112 1175 11BD;D79E;1112 1175 11BD; # (힞; 힞; 힞; 힞; 힞; ) HANGUL SYLLABLE HIJ +D79F;D79F;1112 1175 11BE;D79F;1112 1175 11BE; # (힟; 힟; 힟; 힟; 힟; ) HANGUL SYLLABLE HIC +D7A0;D7A0;1112 1175 11BF;D7A0;1112 1175 11BF; # (힠; 힠; 힠; 힠; 힠; ) HANGUL SYLLABLE HIK +D7A1;D7A1;1112 1175 11C0;D7A1;1112 1175 11C0; # (힡; 힡; 힡; 힡; 힡; ) HANGUL SYLLABLE HIT +D7A2;D7A2;1112 1175 11C1;D7A2;1112 1175 11C1; # (힢; 힢; 힢; 힢; 힢; ) HANGUL SYLLABLE HIP +D7A3;D7A3;1112 1175 11C2;D7A3;1112 1175 11C2; # (힣; 힣; 힣; 힣; 힣; ) HANGUL SYLLABLE HIH +F900;8C48;8C48;8C48;8C48; # (豈; 豈; 豈; 豈; 豈; ) CJK COMPATIBILITY IDEOGRAPH-F900 +F901;66F4;66F4;66F4;66F4; # (更; 更; 更; 更; 更; ) CJK COMPATIBILITY IDEOGRAPH-F901 +F902;8ECA;8ECA;8ECA;8ECA; # (車; 車; 車; 車; 車; ) CJK COMPATIBILITY IDEOGRAPH-F902 +F903;8CC8;8CC8;8CC8;8CC8; # (賈; 賈; 賈; 賈; 賈; ) CJK COMPATIBILITY IDEOGRAPH-F903 +F904;6ED1;6ED1;6ED1;6ED1; # (滑; 滑; 滑; 滑; 滑; ) CJK COMPATIBILITY IDEOGRAPH-F904 +F905;4E32;4E32;4E32;4E32; # (串; 串; 串; 串; 串; ) CJK COMPATIBILITY IDEOGRAPH-F905 +F906;53E5;53E5;53E5;53E5; # (句; 句; 句; 句; 句; ) CJK COMPATIBILITY IDEOGRAPH-F906 +F907;9F9C;9F9C;9F9C;9F9C; # (龜; 龜; 龜; 龜; 龜; ) CJK COMPATIBILITY IDEOGRAPH-F907 +F908;9F9C;9F9C;9F9C;9F9C; # (龜; 龜; 龜; 龜; 龜; ) CJK COMPATIBILITY IDEOGRAPH-F908 +F909;5951;5951;5951;5951; # (契; 契; 契; 契; 契; ) CJK COMPATIBILITY IDEOGRAPH-F909 +F90A;91D1;91D1;91D1;91D1; # (金; 金; 金; 金; 金; ) CJK COMPATIBILITY IDEOGRAPH-F90A +F90B;5587;5587;5587;5587; # (喇; 喇; 喇; 喇; 喇; ) CJK COMPATIBILITY IDEOGRAPH-F90B +F90C;5948;5948;5948;5948; # (奈; 奈; 奈; 奈; 奈; ) CJK COMPATIBILITY IDEOGRAPH-F90C +F90D;61F6;61F6;61F6;61F6; # (懶; 懶; 懶; 懶; 懶; ) CJK COMPATIBILITY IDEOGRAPH-F90D +F90E;7669;7669;7669;7669; # (癩; 癩; 癩; 癩; 癩; ) CJK COMPATIBILITY IDEOGRAPH-F90E +F90F;7F85;7F85;7F85;7F85; # (羅; 羅; 羅; 羅; 羅; ) CJK COMPATIBILITY IDEOGRAPH-F90F +F910;863F;863F;863F;863F; # (蘿; 蘿; 蘿; 蘿; 蘿; ) CJK COMPATIBILITY IDEOGRAPH-F910 +F911;87BA;87BA;87BA;87BA; # (螺; 螺; 螺; 螺; 螺; ) CJK COMPATIBILITY IDEOGRAPH-F911 +F912;88F8;88F8;88F8;88F8; # (裸; 裸; 裸; 裸; 裸; ) CJK COMPATIBILITY IDEOGRAPH-F912 +F913;908F;908F;908F;908F; # (邏; 邏; 邏; 邏; 邏; ) CJK COMPATIBILITY IDEOGRAPH-F913 +F914;6A02;6A02;6A02;6A02; # (樂; 樂; 樂; 樂; 樂; ) CJK COMPATIBILITY IDEOGRAPH-F914 +F915;6D1B;6D1B;6D1B;6D1B; # (洛; 洛; 洛; 洛; 洛; ) CJK COMPATIBILITY IDEOGRAPH-F915 +F916;70D9;70D9;70D9;70D9; # (烙; 烙; 烙; 烙; 烙; ) CJK COMPATIBILITY IDEOGRAPH-F916 +F917;73DE;73DE;73DE;73DE; # (珞; 珞; 珞; 珞; 珞; ) CJK COMPATIBILITY IDEOGRAPH-F917 +F918;843D;843D;843D;843D; # (落; 落; 落; 落; 落; ) CJK COMPATIBILITY IDEOGRAPH-F918 +F919;916A;916A;916A;916A; # (酪; 酪; 酪; 酪; 酪; ) CJK COMPATIBILITY IDEOGRAPH-F919 +F91A;99F1;99F1;99F1;99F1; # (駱; 駱; 駱; 駱; 駱; ) CJK COMPATIBILITY IDEOGRAPH-F91A +F91B;4E82;4E82;4E82;4E82; # (亂; 亂; 亂; 亂; 亂; ) CJK COMPATIBILITY IDEOGRAPH-F91B +F91C;5375;5375;5375;5375; # (卵; 卵; 卵; 卵; 卵; ) CJK COMPATIBILITY IDEOGRAPH-F91C +F91D;6B04;6B04;6B04;6B04; # (欄; 欄; 欄; 欄; 欄; ) CJK COMPATIBILITY IDEOGRAPH-F91D +F91E;721B;721B;721B;721B; # (爛; 爛; 爛; 爛; 爛; ) CJK COMPATIBILITY IDEOGRAPH-F91E +F91F;862D;862D;862D;862D; # (蘭; 蘭; 蘭; 蘭; 蘭; ) CJK COMPATIBILITY IDEOGRAPH-F91F +F920;9E1E;9E1E;9E1E;9E1E; # (鸞; 鸞; 鸞; 鸞; 鸞; ) CJK COMPATIBILITY IDEOGRAPH-F920 +F921;5D50;5D50;5D50;5D50; # (嵐; 嵐; 嵐; 嵐; 嵐; ) CJK COMPATIBILITY IDEOGRAPH-F921 +F922;6FEB;6FEB;6FEB;6FEB; # (濫; 濫; 濫; 濫; 濫; ) CJK COMPATIBILITY IDEOGRAPH-F922 +F923;85CD;85CD;85CD;85CD; # (藍; 藍; 藍; 藍; 藍; ) CJK COMPATIBILITY IDEOGRAPH-F923 +F924;8964;8964;8964;8964; # (襤; 襤; 襤; 襤; 襤; ) CJK COMPATIBILITY IDEOGRAPH-F924 +F925;62C9;62C9;62C9;62C9; # (拉; 拉; 拉; 拉; 拉; ) CJK COMPATIBILITY IDEOGRAPH-F925 +F926;81D8;81D8;81D8;81D8; # (臘; 臘; 臘; 臘; 臘; ) CJK COMPATIBILITY IDEOGRAPH-F926 +F927;881F;881F;881F;881F; # (蠟; 蠟; 蠟; 蠟; 蠟; ) CJK COMPATIBILITY IDEOGRAPH-F927 +F928;5ECA;5ECA;5ECA;5ECA; # (廊; 廊; 廊; 廊; 廊; ) CJK COMPATIBILITY IDEOGRAPH-F928 +F929;6717;6717;6717;6717; # (朗; 朗; 朗; 朗; 朗; ) CJK COMPATIBILITY IDEOGRAPH-F929 +F92A;6D6A;6D6A;6D6A;6D6A; # (浪; 浪; 浪; 浪; 浪; ) CJK COMPATIBILITY IDEOGRAPH-F92A +F92B;72FC;72FC;72FC;72FC; # (狼; 狼; 狼; 狼; 狼; ) CJK COMPATIBILITY IDEOGRAPH-F92B +F92C;90CE;90CE;90CE;90CE; # (郎; 郎; 郎; 郎; 郎; ) CJK COMPATIBILITY IDEOGRAPH-F92C +F92D;4F86;4F86;4F86;4F86; # (來; 來; 來; 來; 來; ) CJK COMPATIBILITY IDEOGRAPH-F92D +F92E;51B7;51B7;51B7;51B7; # (冷; 冷; 冷; 冷; 冷; ) CJK COMPATIBILITY IDEOGRAPH-F92E +F92F;52DE;52DE;52DE;52DE; # (勞; 勞; 勞; 勞; 勞; ) CJK COMPATIBILITY IDEOGRAPH-F92F +F930;64C4;64C4;64C4;64C4; # (擄; 擄; 擄; 擄; 擄; ) CJK COMPATIBILITY IDEOGRAPH-F930 +F931;6AD3;6AD3;6AD3;6AD3; # (櫓; 櫓; 櫓; 櫓; 櫓; ) CJK COMPATIBILITY IDEOGRAPH-F931 +F932;7210;7210;7210;7210; # (爐; 爐; 爐; 爐; 爐; ) CJK COMPATIBILITY IDEOGRAPH-F932 +F933;76E7;76E7;76E7;76E7; # (盧; 盧; 盧; 盧; 盧; ) CJK COMPATIBILITY IDEOGRAPH-F933 +F934;8001;8001;8001;8001; # (老; 老; 老; 老; 老; ) CJK COMPATIBILITY IDEOGRAPH-F934 +F935;8606;8606;8606;8606; # (蘆; 蘆; 蘆; 蘆; 蘆; ) CJK COMPATIBILITY IDEOGRAPH-F935 +F936;865C;865C;865C;865C; # (虜; 虜; 虜; 虜; 虜; ) CJK COMPATIBILITY IDEOGRAPH-F936 +F937;8DEF;8DEF;8DEF;8DEF; # (路; 路; 路; 路; 路; ) CJK COMPATIBILITY IDEOGRAPH-F937 +F938;9732;9732;9732;9732; # (露; 露; 露; 露; 露; ) CJK COMPATIBILITY IDEOGRAPH-F938 +F939;9B6F;9B6F;9B6F;9B6F; # (魯; 魯; 魯; 魯; 魯; ) CJK COMPATIBILITY IDEOGRAPH-F939 +F93A;9DFA;9DFA;9DFA;9DFA; # (鷺; 鷺; 鷺; 鷺; 鷺; ) CJK COMPATIBILITY IDEOGRAPH-F93A +F93B;788C;788C;788C;788C; # (碌; 碌; 碌; 碌; 碌; ) CJK COMPATIBILITY IDEOGRAPH-F93B +F93C;797F;797F;797F;797F; # (祿; 祿; 祿; 祿; 祿; ) CJK COMPATIBILITY IDEOGRAPH-F93C +F93D;7DA0;7DA0;7DA0;7DA0; # (綠; 綠; 綠; 綠; 綠; ) CJK COMPATIBILITY IDEOGRAPH-F93D +F93E;83C9;83C9;83C9;83C9; # (菉; 菉; 菉; 菉; 菉; ) CJK COMPATIBILITY IDEOGRAPH-F93E +F93F;9304;9304;9304;9304; # (錄; 錄; 錄; 錄; 錄; ) CJK COMPATIBILITY IDEOGRAPH-F93F +F940;9E7F;9E7F;9E7F;9E7F; # (鹿; 鹿; 鹿; 鹿; 鹿; ) CJK COMPATIBILITY IDEOGRAPH-F940 +F941;8AD6;8AD6;8AD6;8AD6; # (論; 論; 論; 論; 論; ) CJK COMPATIBILITY IDEOGRAPH-F941 +F942;58DF;58DF;58DF;58DF; # (壟; 壟; 壟; 壟; 壟; ) CJK COMPATIBILITY IDEOGRAPH-F942 +F943;5F04;5F04;5F04;5F04; # (弄; 弄; 弄; 弄; 弄; ) CJK COMPATIBILITY IDEOGRAPH-F943 +F944;7C60;7C60;7C60;7C60; # (籠; 籠; 籠; 籠; 籠; ) CJK COMPATIBILITY IDEOGRAPH-F944 +F945;807E;807E;807E;807E; # (聾; 聾; 聾; 聾; 聾; ) CJK COMPATIBILITY IDEOGRAPH-F945 +F946;7262;7262;7262;7262; # (牢; 牢; 牢; 牢; 牢; ) CJK COMPATIBILITY IDEOGRAPH-F946 +F947;78CA;78CA;78CA;78CA; # (磊; 磊; 磊; 磊; 磊; ) CJK COMPATIBILITY IDEOGRAPH-F947 +F948;8CC2;8CC2;8CC2;8CC2; # (賂; 賂; 賂; 賂; 賂; ) CJK COMPATIBILITY IDEOGRAPH-F948 +F949;96F7;96F7;96F7;96F7; # (雷; 雷; 雷; 雷; 雷; ) CJK COMPATIBILITY IDEOGRAPH-F949 +F94A;58D8;58D8;58D8;58D8; # (壘; 壘; 壘; 壘; 壘; ) CJK COMPATIBILITY IDEOGRAPH-F94A +F94B;5C62;5C62;5C62;5C62; # (屢; 屢; 屢; 屢; 屢; ) CJK COMPATIBILITY IDEOGRAPH-F94B +F94C;6A13;6A13;6A13;6A13; # (樓; 樓; 樓; 樓; 樓; ) CJK COMPATIBILITY IDEOGRAPH-F94C +F94D;6DDA;6DDA;6DDA;6DDA; # (淚; 淚; 淚; 淚; 淚; ) CJK COMPATIBILITY IDEOGRAPH-F94D +F94E;6F0F;6F0F;6F0F;6F0F; # (漏; 漏; 漏; 漏; 漏; ) CJK COMPATIBILITY IDEOGRAPH-F94E +F94F;7D2F;7D2F;7D2F;7D2F; # (累; 累; 累; 累; 累; ) CJK COMPATIBILITY IDEOGRAPH-F94F +F950;7E37;7E37;7E37;7E37; # (縷; 縷; 縷; 縷; 縷; ) CJK COMPATIBILITY IDEOGRAPH-F950 +F951;964B;964B;964B;964B; # (陋; 陋; 陋; 陋; 陋; ) CJK COMPATIBILITY IDEOGRAPH-F951 +F952;52D2;52D2;52D2;52D2; # (勒; 勒; 勒; 勒; 勒; ) CJK COMPATIBILITY IDEOGRAPH-F952 +F953;808B;808B;808B;808B; # (肋; 肋; 肋; 肋; 肋; ) CJK COMPATIBILITY IDEOGRAPH-F953 +F954;51DC;51DC;51DC;51DC; # (凜; 凜; 凜; 凜; 凜; ) CJK COMPATIBILITY IDEOGRAPH-F954 +F955;51CC;51CC;51CC;51CC; # (凌; 凌; 凌; 凌; 凌; ) CJK COMPATIBILITY IDEOGRAPH-F955 +F956;7A1C;7A1C;7A1C;7A1C; # (稜; 稜; 稜; 稜; 稜; ) CJK COMPATIBILITY IDEOGRAPH-F956 +F957;7DBE;7DBE;7DBE;7DBE; # (綾; 綾; 綾; 綾; 綾; ) CJK COMPATIBILITY IDEOGRAPH-F957 +F958;83F1;83F1;83F1;83F1; # (菱; 菱; 菱; 菱; 菱; ) CJK COMPATIBILITY IDEOGRAPH-F958 +F959;9675;9675;9675;9675; # (陵; 陵; 陵; 陵; 陵; ) CJK COMPATIBILITY IDEOGRAPH-F959 +F95A;8B80;8B80;8B80;8B80; # (讀; 讀; 讀; 讀; 讀; ) CJK COMPATIBILITY IDEOGRAPH-F95A +F95B;62CF;62CF;62CF;62CF; # (拏; 拏; 拏; 拏; 拏; ) CJK COMPATIBILITY IDEOGRAPH-F95B +F95C;6A02;6A02;6A02;6A02; # (樂; 樂; 樂; 樂; 樂; ) CJK COMPATIBILITY IDEOGRAPH-F95C +F95D;8AFE;8AFE;8AFE;8AFE; # (諾; 諾; 諾; 諾; 諾; ) CJK COMPATIBILITY IDEOGRAPH-F95D +F95E;4E39;4E39;4E39;4E39; # (丹; 丹; 丹; 丹; 丹; ) CJK COMPATIBILITY IDEOGRAPH-F95E +F95F;5BE7;5BE7;5BE7;5BE7; # (寧; 寧; 寧; 寧; 寧; ) CJK COMPATIBILITY IDEOGRAPH-F95F +F960;6012;6012;6012;6012; # (怒; 怒; 怒; 怒; 怒; ) CJK COMPATIBILITY IDEOGRAPH-F960 +F961;7387;7387;7387;7387; # (率; 率; 率; 率; 率; ) CJK COMPATIBILITY IDEOGRAPH-F961 +F962;7570;7570;7570;7570; # (異; 異; 異; 異; 異; ) CJK COMPATIBILITY IDEOGRAPH-F962 +F963;5317;5317;5317;5317; # (北; 北; 北; 北; 北; ) CJK COMPATIBILITY IDEOGRAPH-F963 +F964;78FB;78FB;78FB;78FB; # (磻; 磻; 磻; 磻; 磻; ) CJK COMPATIBILITY IDEOGRAPH-F964 +F965;4FBF;4FBF;4FBF;4FBF; # (便; 便; 便; 便; 便; ) CJK COMPATIBILITY IDEOGRAPH-F965 +F966;5FA9;5FA9;5FA9;5FA9; # (復; 復; 復; 復; 復; ) CJK COMPATIBILITY IDEOGRAPH-F966 +F967;4E0D;4E0D;4E0D;4E0D; # (不; 不; 不; 不; 不; ) CJK COMPATIBILITY IDEOGRAPH-F967 +F968;6CCC;6CCC;6CCC;6CCC; # (泌; 泌; 泌; 泌; 泌; ) CJK COMPATIBILITY IDEOGRAPH-F968 +F969;6578;6578;6578;6578; # (數; 數; 數; 數; 數; ) CJK COMPATIBILITY IDEOGRAPH-F969 +F96A;7D22;7D22;7D22;7D22; # (索; 索; 索; 索; 索; ) CJK COMPATIBILITY IDEOGRAPH-F96A +F96B;53C3;53C3;53C3;53C3; # (參; 參; 參; 參; 參; ) CJK COMPATIBILITY IDEOGRAPH-F96B +F96C;585E;585E;585E;585E; # (塞; 塞; 塞; 塞; 塞; ) CJK COMPATIBILITY IDEOGRAPH-F96C +F96D;7701;7701;7701;7701; # (省; 省; 省; 省; 省; ) CJK COMPATIBILITY IDEOGRAPH-F96D +F96E;8449;8449;8449;8449; # (葉; 葉; 葉; 葉; 葉; ) CJK COMPATIBILITY IDEOGRAPH-F96E +F96F;8AAA;8AAA;8AAA;8AAA; # (說; 說; 說; 說; 說; ) CJK COMPATIBILITY IDEOGRAPH-F96F +F970;6BBA;6BBA;6BBA;6BBA; # (殺; 殺; 殺; 殺; 殺; ) CJK COMPATIBILITY IDEOGRAPH-F970 +F971;8FB0;8FB0;8FB0;8FB0; # (辰; 辰; 辰; 辰; 辰; ) CJK COMPATIBILITY IDEOGRAPH-F971 +F972;6C88;6C88;6C88;6C88; # (沈; 沈; 沈; 沈; 沈; ) CJK COMPATIBILITY IDEOGRAPH-F972 +F973;62FE;62FE;62FE;62FE; # (拾; 拾; 拾; 拾; 拾; ) CJK COMPATIBILITY IDEOGRAPH-F973 +F974;82E5;82E5;82E5;82E5; # (若; 若; 若; 若; 若; ) CJK COMPATIBILITY IDEOGRAPH-F974 +F975;63A0;63A0;63A0;63A0; # (掠; 掠; 掠; 掠; 掠; ) CJK COMPATIBILITY IDEOGRAPH-F975 +F976;7565;7565;7565;7565; # (略; 略; 略; 略; 略; ) CJK COMPATIBILITY IDEOGRAPH-F976 +F977;4EAE;4EAE;4EAE;4EAE; # (亮; 亮; 亮; 亮; 亮; ) CJK COMPATIBILITY IDEOGRAPH-F977 +F978;5169;5169;5169;5169; # (兩; 兩; 兩; 兩; 兩; ) CJK COMPATIBILITY IDEOGRAPH-F978 +F979;51C9;51C9;51C9;51C9; # (凉; 凉; 凉; 凉; 凉; ) CJK COMPATIBILITY IDEOGRAPH-F979 +F97A;6881;6881;6881;6881; # (梁; 梁; 梁; 梁; 梁; ) CJK COMPATIBILITY IDEOGRAPH-F97A +F97B;7CE7;7CE7;7CE7;7CE7; # (糧; 糧; 糧; 糧; 糧; ) CJK COMPATIBILITY IDEOGRAPH-F97B +F97C;826F;826F;826F;826F; # (良; 良; 良; 良; 良; ) CJK COMPATIBILITY IDEOGRAPH-F97C +F97D;8AD2;8AD2;8AD2;8AD2; # (諒; 諒; 諒; 諒; 諒; ) CJK COMPATIBILITY IDEOGRAPH-F97D +F97E;91CF;91CF;91CF;91CF; # (量; 量; 量; 量; 量; ) CJK COMPATIBILITY IDEOGRAPH-F97E +F97F;52F5;52F5;52F5;52F5; # (勵; 勵; 勵; 勵; 勵; ) CJK COMPATIBILITY IDEOGRAPH-F97F +F980;5442;5442;5442;5442; # (呂; 呂; 呂; 呂; 呂; ) CJK COMPATIBILITY IDEOGRAPH-F980 +F981;5973;5973;5973;5973; # (女; 女; 女; 女; 女; ) CJK COMPATIBILITY IDEOGRAPH-F981 +F982;5EEC;5EEC;5EEC;5EEC; # (廬; 廬; 廬; 廬; 廬; ) CJK COMPATIBILITY IDEOGRAPH-F982 +F983;65C5;65C5;65C5;65C5; # (旅; 旅; 旅; 旅; 旅; ) CJK COMPATIBILITY IDEOGRAPH-F983 +F984;6FFE;6FFE;6FFE;6FFE; # (濾; 濾; 濾; 濾; 濾; ) CJK COMPATIBILITY IDEOGRAPH-F984 +F985;792A;792A;792A;792A; # (礪; 礪; 礪; 礪; 礪; ) CJK COMPATIBILITY IDEOGRAPH-F985 +F986;95AD;95AD;95AD;95AD; # (閭; 閭; 閭; 閭; 閭; ) CJK COMPATIBILITY IDEOGRAPH-F986 +F987;9A6A;9A6A;9A6A;9A6A; # (驪; 驪; 驪; 驪; 驪; ) CJK COMPATIBILITY IDEOGRAPH-F987 +F988;9E97;9E97;9E97;9E97; # (麗; 麗; 麗; 麗; 麗; ) CJK COMPATIBILITY IDEOGRAPH-F988 +F989;9ECE;9ECE;9ECE;9ECE; # (黎; 黎; 黎; 黎; 黎; ) CJK COMPATIBILITY IDEOGRAPH-F989 +F98A;529B;529B;529B;529B; # (力; 力; 力; 力; 力; ) CJK COMPATIBILITY IDEOGRAPH-F98A +F98B;66C6;66C6;66C6;66C6; # (曆; 曆; 曆; 曆; 曆; ) CJK COMPATIBILITY IDEOGRAPH-F98B +F98C;6B77;6B77;6B77;6B77; # (歷; 歷; 歷; 歷; 歷; ) CJK COMPATIBILITY IDEOGRAPH-F98C +F98D;8F62;8F62;8F62;8F62; # (轢; 轢; 轢; 轢; 轢; ) CJK COMPATIBILITY IDEOGRAPH-F98D +F98E;5E74;5E74;5E74;5E74; # (年; 年; 年; 年; 年; ) CJK COMPATIBILITY IDEOGRAPH-F98E +F98F;6190;6190;6190;6190; # (憐; 憐; 憐; 憐; 憐; ) CJK COMPATIBILITY IDEOGRAPH-F98F +F990;6200;6200;6200;6200; # (戀; 戀; 戀; 戀; 戀; ) CJK COMPATIBILITY IDEOGRAPH-F990 +F991;649A;649A;649A;649A; # (撚; 撚; 撚; 撚; 撚; ) CJK COMPATIBILITY IDEOGRAPH-F991 +F992;6F23;6F23;6F23;6F23; # (漣; 漣; 漣; 漣; 漣; ) CJK COMPATIBILITY IDEOGRAPH-F992 +F993;7149;7149;7149;7149; # (煉; 煉; 煉; 煉; 煉; ) CJK COMPATIBILITY IDEOGRAPH-F993 +F994;7489;7489;7489;7489; # (璉; 璉; 璉; 璉; 璉; ) CJK COMPATIBILITY IDEOGRAPH-F994 +F995;79CA;79CA;79CA;79CA; # (秊; 秊; 秊; 秊; 秊; ) CJK COMPATIBILITY IDEOGRAPH-F995 +F996;7DF4;7DF4;7DF4;7DF4; # (練; 練; 練; 練; 練; ) CJK COMPATIBILITY IDEOGRAPH-F996 +F997;806F;806F;806F;806F; # (聯; 聯; 聯; 聯; 聯; ) CJK COMPATIBILITY IDEOGRAPH-F997 +F998;8F26;8F26;8F26;8F26; # (輦; 輦; 輦; 輦; 輦; ) CJK COMPATIBILITY IDEOGRAPH-F998 +F999;84EE;84EE;84EE;84EE; # (蓮; 蓮; 蓮; 蓮; 蓮; ) CJK COMPATIBILITY IDEOGRAPH-F999 +F99A;9023;9023;9023;9023; # (連; 連; 連; 連; 連; ) CJK COMPATIBILITY IDEOGRAPH-F99A +F99B;934A;934A;934A;934A; # (鍊; 鍊; 鍊; 鍊; 鍊; ) CJK COMPATIBILITY IDEOGRAPH-F99B +F99C;5217;5217;5217;5217; # (列; 列; 列; 列; 列; ) CJK COMPATIBILITY IDEOGRAPH-F99C +F99D;52A3;52A3;52A3;52A3; # (劣; 劣; 劣; 劣; 劣; ) CJK COMPATIBILITY IDEOGRAPH-F99D +F99E;54BD;54BD;54BD;54BD; # (咽; 咽; 咽; 咽; 咽; ) CJK COMPATIBILITY IDEOGRAPH-F99E +F99F;70C8;70C8;70C8;70C8; # (烈; 烈; 烈; 烈; 烈; ) CJK COMPATIBILITY IDEOGRAPH-F99F +F9A0;88C2;88C2;88C2;88C2; # (裂; 裂; 裂; 裂; 裂; ) CJK COMPATIBILITY IDEOGRAPH-F9A0 +F9A1;8AAA;8AAA;8AAA;8AAA; # (說; 說; 說; 說; 說; ) CJK COMPATIBILITY IDEOGRAPH-F9A1 +F9A2;5EC9;5EC9;5EC9;5EC9; # (廉; 廉; 廉; 廉; 廉; ) CJK COMPATIBILITY IDEOGRAPH-F9A2 +F9A3;5FF5;5FF5;5FF5;5FF5; # (念; 念; 念; 念; 念; ) CJK COMPATIBILITY IDEOGRAPH-F9A3 +F9A4;637B;637B;637B;637B; # (捻; 捻; 捻; 捻; 捻; ) CJK COMPATIBILITY IDEOGRAPH-F9A4 +F9A5;6BAE;6BAE;6BAE;6BAE; # (殮; 殮; 殮; 殮; 殮; ) CJK COMPATIBILITY IDEOGRAPH-F9A5 +F9A6;7C3E;7C3E;7C3E;7C3E; # (簾; 簾; 簾; 簾; 簾; ) CJK COMPATIBILITY IDEOGRAPH-F9A6 +F9A7;7375;7375;7375;7375; # (獵; 獵; 獵; 獵; 獵; ) CJK COMPATIBILITY IDEOGRAPH-F9A7 +F9A8;4EE4;4EE4;4EE4;4EE4; # (令; 令; 令; 令; 令; ) CJK COMPATIBILITY IDEOGRAPH-F9A8 +F9A9;56F9;56F9;56F9;56F9; # (囹; 囹; 囹; 囹; 囹; ) CJK COMPATIBILITY IDEOGRAPH-F9A9 +F9AA;5BE7;5BE7;5BE7;5BE7; # (寧; 寧; 寧; 寧; 寧; ) CJK COMPATIBILITY IDEOGRAPH-F9AA +F9AB;5DBA;5DBA;5DBA;5DBA; # (嶺; 嶺; 嶺; 嶺; 嶺; ) CJK COMPATIBILITY IDEOGRAPH-F9AB +F9AC;601C;601C;601C;601C; # (怜; 怜; 怜; 怜; 怜; ) CJK COMPATIBILITY IDEOGRAPH-F9AC +F9AD;73B2;73B2;73B2;73B2; # (玲; 玲; 玲; 玲; 玲; ) CJK COMPATIBILITY IDEOGRAPH-F9AD +F9AE;7469;7469;7469;7469; # (瑩; 瑩; 瑩; 瑩; 瑩; ) CJK COMPATIBILITY IDEOGRAPH-F9AE +F9AF;7F9A;7F9A;7F9A;7F9A; # (羚; 羚; 羚; 羚; 羚; ) CJK COMPATIBILITY IDEOGRAPH-F9AF +F9B0;8046;8046;8046;8046; # (聆; 聆; 聆; 聆; 聆; ) CJK COMPATIBILITY IDEOGRAPH-F9B0 +F9B1;9234;9234;9234;9234; # (鈴; 鈴; 鈴; 鈴; 鈴; ) CJK COMPATIBILITY IDEOGRAPH-F9B1 +F9B2;96F6;96F6;96F6;96F6; # (零; 零; 零; 零; 零; ) CJK COMPATIBILITY IDEOGRAPH-F9B2 +F9B3;9748;9748;9748;9748; # (靈; 靈; 靈; 靈; 靈; ) CJK COMPATIBILITY IDEOGRAPH-F9B3 +F9B4;9818;9818;9818;9818; # (領; 領; 領; 領; 領; ) CJK COMPATIBILITY IDEOGRAPH-F9B4 +F9B5;4F8B;4F8B;4F8B;4F8B; # (例; 例; 例; 例; 例; ) CJK COMPATIBILITY IDEOGRAPH-F9B5 +F9B6;79AE;79AE;79AE;79AE; # (禮; 禮; 禮; 禮; 禮; ) CJK COMPATIBILITY IDEOGRAPH-F9B6 +F9B7;91B4;91B4;91B4;91B4; # (醴; 醴; 醴; 醴; 醴; ) CJK COMPATIBILITY IDEOGRAPH-F9B7 +F9B8;96B8;96B8;96B8;96B8; # (隸; 隸; 隸; 隸; 隸; ) CJK COMPATIBILITY IDEOGRAPH-F9B8 +F9B9;60E1;60E1;60E1;60E1; # (惡; 惡; 惡; 惡; 惡; ) CJK COMPATIBILITY IDEOGRAPH-F9B9 +F9BA;4E86;4E86;4E86;4E86; # (了; 了; 了; 了; 了; ) CJK COMPATIBILITY IDEOGRAPH-F9BA +F9BB;50DA;50DA;50DA;50DA; # (僚; 僚; 僚; 僚; 僚; ) CJK COMPATIBILITY IDEOGRAPH-F9BB +F9BC;5BEE;5BEE;5BEE;5BEE; # (寮; 寮; 寮; 寮; 寮; ) CJK COMPATIBILITY IDEOGRAPH-F9BC +F9BD;5C3F;5C3F;5C3F;5C3F; # (尿; 尿; 尿; 尿; 尿; ) CJK COMPATIBILITY IDEOGRAPH-F9BD +F9BE;6599;6599;6599;6599; # (料; 料; 料; 料; 料; ) CJK COMPATIBILITY IDEOGRAPH-F9BE +F9BF;6A02;6A02;6A02;6A02; # (樂; 樂; 樂; 樂; 樂; ) CJK COMPATIBILITY IDEOGRAPH-F9BF +F9C0;71CE;71CE;71CE;71CE; # (燎; 燎; 燎; 燎; 燎; ) CJK COMPATIBILITY IDEOGRAPH-F9C0 +F9C1;7642;7642;7642;7642; # (療; 療; 療; 療; 療; ) CJK COMPATIBILITY IDEOGRAPH-F9C1 +F9C2;84FC;84FC;84FC;84FC; # (蓼; 蓼; 蓼; 蓼; 蓼; ) CJK COMPATIBILITY IDEOGRAPH-F9C2 +F9C3;907C;907C;907C;907C; # (遼; 遼; 遼; 遼; 遼; ) CJK COMPATIBILITY IDEOGRAPH-F9C3 +F9C4;9F8D;9F8D;9F8D;9F8D; # (龍; 龍; 龍; 龍; 龍; ) CJK COMPATIBILITY IDEOGRAPH-F9C4 +F9C5;6688;6688;6688;6688; # (暈; 暈; 暈; 暈; 暈; ) CJK COMPATIBILITY IDEOGRAPH-F9C5 +F9C6;962E;962E;962E;962E; # (阮; 阮; 阮; 阮; 阮; ) CJK COMPATIBILITY IDEOGRAPH-F9C6 +F9C7;5289;5289;5289;5289; # (劉; 劉; 劉; 劉; 劉; ) CJK COMPATIBILITY IDEOGRAPH-F9C7 +F9C8;677B;677B;677B;677B; # (杻; 杻; 杻; 杻; 杻; ) CJK COMPATIBILITY IDEOGRAPH-F9C8 +F9C9;67F3;67F3;67F3;67F3; # (柳; 柳; 柳; 柳; 柳; ) CJK COMPATIBILITY IDEOGRAPH-F9C9 +F9CA;6D41;6D41;6D41;6D41; # (流; 流; 流; 流; 流; ) CJK COMPATIBILITY IDEOGRAPH-F9CA +F9CB;6E9C;6E9C;6E9C;6E9C; # (溜; 溜; 溜; 溜; 溜; ) CJK COMPATIBILITY IDEOGRAPH-F9CB +F9CC;7409;7409;7409;7409; # (琉; 琉; 琉; 琉; 琉; ) CJK COMPATIBILITY IDEOGRAPH-F9CC +F9CD;7559;7559;7559;7559; # (留; 留; 留; 留; 留; ) CJK COMPATIBILITY IDEOGRAPH-F9CD +F9CE;786B;786B;786B;786B; # (硫; 硫; 硫; 硫; 硫; ) CJK COMPATIBILITY IDEOGRAPH-F9CE +F9CF;7D10;7D10;7D10;7D10; # (紐; 紐; 紐; 紐; 紐; ) CJK COMPATIBILITY IDEOGRAPH-F9CF +F9D0;985E;985E;985E;985E; # (類; 類; 類; 類; 類; ) CJK COMPATIBILITY IDEOGRAPH-F9D0 +F9D1;516D;516D;516D;516D; # (六; 六; 六; 六; 六; ) CJK COMPATIBILITY IDEOGRAPH-F9D1 +F9D2;622E;622E;622E;622E; # (戮; 戮; 戮; 戮; 戮; ) CJK COMPATIBILITY IDEOGRAPH-F9D2 +F9D3;9678;9678;9678;9678; # (陸; 陸; 陸; 陸; 陸; ) CJK COMPATIBILITY IDEOGRAPH-F9D3 +F9D4;502B;502B;502B;502B; # (倫; 倫; 倫; 倫; 倫; ) CJK COMPATIBILITY IDEOGRAPH-F9D4 +F9D5;5D19;5D19;5D19;5D19; # (崙; 崙; 崙; 崙; 崙; ) CJK COMPATIBILITY IDEOGRAPH-F9D5 +F9D6;6DEA;6DEA;6DEA;6DEA; # (淪; 淪; 淪; 淪; 淪; ) CJK COMPATIBILITY IDEOGRAPH-F9D6 +F9D7;8F2A;8F2A;8F2A;8F2A; # (輪; 輪; 輪; 輪; 輪; ) CJK COMPATIBILITY IDEOGRAPH-F9D7 +F9D8;5F8B;5F8B;5F8B;5F8B; # (律; 律; 律; 律; 律; ) CJK COMPATIBILITY IDEOGRAPH-F9D8 +F9D9;6144;6144;6144;6144; # (慄; 慄; 慄; 慄; 慄; ) CJK COMPATIBILITY IDEOGRAPH-F9D9 +F9DA;6817;6817;6817;6817; # (栗; 栗; 栗; 栗; 栗; ) CJK COMPATIBILITY IDEOGRAPH-F9DA +F9DB;7387;7387;7387;7387; # (率; 率; 率; 率; 率; ) CJK COMPATIBILITY IDEOGRAPH-F9DB +F9DC;9686;9686;9686;9686; # (隆; 隆; 隆; 隆; 隆; ) CJK COMPATIBILITY IDEOGRAPH-F9DC +F9DD;5229;5229;5229;5229; # (利; 利; 利; 利; 利; ) CJK COMPATIBILITY IDEOGRAPH-F9DD +F9DE;540F;540F;540F;540F; # (吏; 吏; 吏; 吏; 吏; ) CJK COMPATIBILITY IDEOGRAPH-F9DE +F9DF;5C65;5C65;5C65;5C65; # (履; 履; 履; 履; 履; ) CJK COMPATIBILITY IDEOGRAPH-F9DF +F9E0;6613;6613;6613;6613; # (易; 易; 易; 易; 易; ) CJK COMPATIBILITY IDEOGRAPH-F9E0 +F9E1;674E;674E;674E;674E; # (李; 李; 李; 李; 李; ) CJK COMPATIBILITY IDEOGRAPH-F9E1 +F9E2;68A8;68A8;68A8;68A8; # (梨; 梨; 梨; 梨; 梨; ) CJK COMPATIBILITY IDEOGRAPH-F9E2 +F9E3;6CE5;6CE5;6CE5;6CE5; # (泥; 泥; 泥; 泥; 泥; ) CJK COMPATIBILITY IDEOGRAPH-F9E3 +F9E4;7406;7406;7406;7406; # (理; 理; 理; 理; 理; ) CJK COMPATIBILITY IDEOGRAPH-F9E4 +F9E5;75E2;75E2;75E2;75E2; # (痢; 痢; 痢; 痢; 痢; ) CJK COMPATIBILITY IDEOGRAPH-F9E5 +F9E6;7F79;7F79;7F79;7F79; # (罹; 罹; 罹; 罹; 罹; ) CJK COMPATIBILITY IDEOGRAPH-F9E6 +F9E7;88CF;88CF;88CF;88CF; # (裏; 裏; 裏; 裏; 裏; ) CJK COMPATIBILITY IDEOGRAPH-F9E7 +F9E8;88E1;88E1;88E1;88E1; # (裡; 裡; 裡; 裡; 裡; ) CJK COMPATIBILITY IDEOGRAPH-F9E8 +F9E9;91CC;91CC;91CC;91CC; # (里; 里; 里; 里; 里; ) CJK COMPATIBILITY IDEOGRAPH-F9E9 +F9EA;96E2;96E2;96E2;96E2; # (離; 離; 離; 離; 離; ) CJK COMPATIBILITY IDEOGRAPH-F9EA +F9EB;533F;533F;533F;533F; # (匿; 匿; 匿; 匿; 匿; ) CJK COMPATIBILITY IDEOGRAPH-F9EB +F9EC;6EBA;6EBA;6EBA;6EBA; # (溺; 溺; 溺; 溺; 溺; ) CJK COMPATIBILITY IDEOGRAPH-F9EC +F9ED;541D;541D;541D;541D; # (吝; 吝; 吝; 吝; 吝; ) CJK COMPATIBILITY IDEOGRAPH-F9ED +F9EE;71D0;71D0;71D0;71D0; # (燐; 燐; 燐; 燐; 燐; ) CJK COMPATIBILITY IDEOGRAPH-F9EE +F9EF;7498;7498;7498;7498; # (璘; 璘; 璘; 璘; 璘; ) CJK COMPATIBILITY IDEOGRAPH-F9EF +F9F0;85FA;85FA;85FA;85FA; # (藺; 藺; 藺; 藺; 藺; ) CJK COMPATIBILITY IDEOGRAPH-F9F0 +F9F1;96A3;96A3;96A3;96A3; # (隣; 隣; 隣; 隣; 隣; ) CJK COMPATIBILITY IDEOGRAPH-F9F1 +F9F2;9C57;9C57;9C57;9C57; # (鱗; 鱗; 鱗; 鱗; 鱗; ) CJK COMPATIBILITY IDEOGRAPH-F9F2 +F9F3;9E9F;9E9F;9E9F;9E9F; # (麟; 麟; 麟; 麟; 麟; ) CJK COMPATIBILITY IDEOGRAPH-F9F3 +F9F4;6797;6797;6797;6797; # (林; 林; 林; 林; 林; ) CJK COMPATIBILITY IDEOGRAPH-F9F4 +F9F5;6DCB;6DCB;6DCB;6DCB; # (淋; 淋; 淋; 淋; 淋; ) CJK COMPATIBILITY IDEOGRAPH-F9F5 +F9F6;81E8;81E8;81E8;81E8; # (臨; 臨; 臨; 臨; 臨; ) CJK COMPATIBILITY IDEOGRAPH-F9F6 +F9F7;7ACB;7ACB;7ACB;7ACB; # (立; 立; 立; 立; 立; ) CJK COMPATIBILITY IDEOGRAPH-F9F7 +F9F8;7B20;7B20;7B20;7B20; # (笠; 笠; 笠; 笠; 笠; ) CJK COMPATIBILITY IDEOGRAPH-F9F8 +F9F9;7C92;7C92;7C92;7C92; # (粒; 粒; 粒; 粒; 粒; ) CJK COMPATIBILITY IDEOGRAPH-F9F9 +F9FA;72C0;72C0;72C0;72C0; # (狀; 狀; 狀; 狀; 狀; ) CJK COMPATIBILITY IDEOGRAPH-F9FA +F9FB;7099;7099;7099;7099; # (炙; 炙; 炙; 炙; 炙; ) CJK COMPATIBILITY IDEOGRAPH-F9FB +F9FC;8B58;8B58;8B58;8B58; # (識; 識; 識; 識; 識; ) CJK COMPATIBILITY IDEOGRAPH-F9FC +F9FD;4EC0;4EC0;4EC0;4EC0; # (什; 什; 什; 什; 什; ) CJK COMPATIBILITY IDEOGRAPH-F9FD +F9FE;8336;8336;8336;8336; # (茶; 茶; 茶; 茶; 茶; ) CJK COMPATIBILITY IDEOGRAPH-F9FE +F9FF;523A;523A;523A;523A; # (刺; 刺; 刺; 刺; 刺; ) CJK COMPATIBILITY IDEOGRAPH-F9FF +FA00;5207;5207;5207;5207; # (切; 切; 切; 切; 切; ) CJK COMPATIBILITY IDEOGRAPH-FA00 +FA01;5EA6;5EA6;5EA6;5EA6; # (度; 度; 度; 度; 度; ) CJK COMPATIBILITY IDEOGRAPH-FA01 +FA02;62D3;62D3;62D3;62D3; # (拓; 拓; 拓; 拓; 拓; ) CJK COMPATIBILITY IDEOGRAPH-FA02 +FA03;7CD6;7CD6;7CD6;7CD6; # (糖; 糖; 糖; 糖; 糖; ) CJK COMPATIBILITY IDEOGRAPH-FA03 +FA04;5B85;5B85;5B85;5B85; # (宅; 宅; 宅; 宅; 宅; ) CJK COMPATIBILITY IDEOGRAPH-FA04 +FA05;6D1E;6D1E;6D1E;6D1E; # (洞; 洞; 洞; 洞; 洞; ) CJK COMPATIBILITY IDEOGRAPH-FA05 +FA06;66B4;66B4;66B4;66B4; # (暴; 暴; 暴; 暴; 暴; ) CJK COMPATIBILITY IDEOGRAPH-FA06 +FA07;8F3B;8F3B;8F3B;8F3B; # (輻; 輻; 輻; 輻; 輻; ) CJK COMPATIBILITY IDEOGRAPH-FA07 +FA08;884C;884C;884C;884C; # (行; 行; 行; 行; 行; ) CJK COMPATIBILITY IDEOGRAPH-FA08 +FA09;964D;964D;964D;964D; # (降; 降; 降; 降; 降; ) CJK COMPATIBILITY IDEOGRAPH-FA09 +FA0A;898B;898B;898B;898B; # (見; 見; 見; 見; 見; ) CJK COMPATIBILITY IDEOGRAPH-FA0A +FA0B;5ED3;5ED3;5ED3;5ED3; # (廓; 廓; 廓; 廓; 廓; ) CJK COMPATIBILITY IDEOGRAPH-FA0B +FA0C;5140;5140;5140;5140; # (兀; 兀; 兀; 兀; 兀; ) CJK COMPATIBILITY IDEOGRAPH-FA0C +FA0D;55C0;55C0;55C0;55C0; # (嗀; 嗀; 嗀; 嗀; 嗀; ) CJK COMPATIBILITY IDEOGRAPH-FA0D +FA10;585A;585A;585A;585A; # (塚; 塚; 塚; 塚; 塚; ) CJK COMPATIBILITY IDEOGRAPH-FA10 +FA12;6674;6674;6674;6674; # (晴; 晴; 晴; 晴; 晴; ) CJK COMPATIBILITY IDEOGRAPH-FA12 +FA15;51DE;51DE;51DE;51DE; # (凞; 凞; 凞; 凞; 凞; ) CJK COMPATIBILITY IDEOGRAPH-FA15 +FA16;732A;732A;732A;732A; # (猪; 猪; 猪; 猪; 猪; ) CJK COMPATIBILITY IDEOGRAPH-FA16 +FA17;76CA;76CA;76CA;76CA; # (益; 益; 益; 益; 益; ) CJK COMPATIBILITY IDEOGRAPH-FA17 +FA18;793C;793C;793C;793C; # (礼; 礼; 礼; 礼; 礼; ) CJK COMPATIBILITY IDEOGRAPH-FA18 +FA19;795E;795E;795E;795E; # (神; 神; 神; 神; 神; ) CJK COMPATIBILITY IDEOGRAPH-FA19 +FA1A;7965;7965;7965;7965; # (祥; 祥; 祥; 祥; 祥; ) CJK COMPATIBILITY IDEOGRAPH-FA1A +FA1B;798F;798F;798F;798F; # (福; 福; 福; 福; 福; ) CJK COMPATIBILITY IDEOGRAPH-FA1B +FA1C;9756;9756;9756;9756; # (靖; 靖; 靖; 靖; 靖; ) CJK COMPATIBILITY IDEOGRAPH-FA1C +FA1D;7CBE;7CBE;7CBE;7CBE; # (精; 精; 精; 精; 精; ) CJK COMPATIBILITY IDEOGRAPH-FA1D +FA1E;7FBD;7FBD;7FBD;7FBD; # (羽; 羽; 羽; 羽; 羽; ) CJK COMPATIBILITY IDEOGRAPH-FA1E +FA20;8612;8612;8612;8612; # (蘒; 蘒; 蘒; 蘒; 蘒; ) CJK COMPATIBILITY IDEOGRAPH-FA20 +FA22;8AF8;8AF8;8AF8;8AF8; # (諸; 諸; 諸; 諸; 諸; ) CJK COMPATIBILITY IDEOGRAPH-FA22 +FA25;9038;9038;9038;9038; # (逸; 逸; 逸; 逸; 逸; ) CJK COMPATIBILITY IDEOGRAPH-FA25 +FA26;90FD;90FD;90FD;90FD; # (都; 都; 都; 都; 都; ) CJK COMPATIBILITY IDEOGRAPH-FA26 +FA2A;98EF;98EF;98EF;98EF; # (飯; 飯; 飯; 飯; 飯; ) CJK COMPATIBILITY IDEOGRAPH-FA2A +FA2B;98FC;98FC;98FC;98FC; # (飼; 飼; 飼; 飼; 飼; ) CJK COMPATIBILITY IDEOGRAPH-FA2B +FA2C;9928;9928;9928;9928; # (館; 館; 館; 館; 館; ) CJK COMPATIBILITY IDEOGRAPH-FA2C +FA2D;9DB4;9DB4;9DB4;9DB4; # (鶴; 鶴; 鶴; 鶴; 鶴; ) CJK COMPATIBILITY IDEOGRAPH-FA2D +FA30;4FAE;4FAE;4FAE;4FAE; # (侮; 侮; 侮; 侮; 侮; ) CJK COMPATIBILITY IDEOGRAPH-FA30 +FA31;50E7;50E7;50E7;50E7; # (僧; 僧; 僧; 僧; 僧; ) CJK COMPATIBILITY IDEOGRAPH-FA31 +FA32;514D;514D;514D;514D; # (免; 免; 免; 免; 免; ) CJK COMPATIBILITY IDEOGRAPH-FA32 +FA33;52C9;52C9;52C9;52C9; # (勉; 勉; 勉; 勉; 勉; ) CJK COMPATIBILITY IDEOGRAPH-FA33 +FA34;52E4;52E4;52E4;52E4; # (勤; 勤; 勤; 勤; 勤; ) CJK COMPATIBILITY IDEOGRAPH-FA34 +FA35;5351;5351;5351;5351; # (卑; 卑; 卑; 卑; 卑; ) CJK COMPATIBILITY IDEOGRAPH-FA35 +FA36;559D;559D;559D;559D; # (喝; 喝; 喝; 喝; 喝; ) CJK COMPATIBILITY IDEOGRAPH-FA36 +FA37;5606;5606;5606;5606; # (嘆; 嘆; 嘆; 嘆; 嘆; ) CJK COMPATIBILITY IDEOGRAPH-FA37 +FA38;5668;5668;5668;5668; # (器; 器; 器; 器; 器; ) CJK COMPATIBILITY IDEOGRAPH-FA38 +FA39;5840;5840;5840;5840; # (塀; 塀; 塀; 塀; 塀; ) CJK COMPATIBILITY IDEOGRAPH-FA39 +FA3A;58A8;58A8;58A8;58A8; # (墨; 墨; 墨; 墨; 墨; ) CJK COMPATIBILITY IDEOGRAPH-FA3A +FA3B;5C64;5C64;5C64;5C64; # (層; 層; 層; 層; 層; ) CJK COMPATIBILITY IDEOGRAPH-FA3B +FA3C;5C6E;5C6E;5C6E;5C6E; # (屮; 屮; 屮; 屮; 屮; ) CJK COMPATIBILITY IDEOGRAPH-FA3C +FA3D;6094;6094;6094;6094; # (悔; 悔; 悔; 悔; 悔; ) CJK COMPATIBILITY IDEOGRAPH-FA3D +FA3E;6168;6168;6168;6168; # (慨; 慨; 慨; 慨; 慨; ) CJK COMPATIBILITY IDEOGRAPH-FA3E +FA3F;618E;618E;618E;618E; # (憎; 憎; 憎; 憎; 憎; ) CJK COMPATIBILITY IDEOGRAPH-FA3F +FA40;61F2;61F2;61F2;61F2; # (懲; 懲; 懲; 懲; 懲; ) CJK COMPATIBILITY IDEOGRAPH-FA40 +FA41;654F;654F;654F;654F; # (敏; 敏; 敏; 敏; 敏; ) CJK COMPATIBILITY IDEOGRAPH-FA41 +FA42;65E2;65E2;65E2;65E2; # (既; 既; 既; 既; 既; ) CJK COMPATIBILITY IDEOGRAPH-FA42 +FA43;6691;6691;6691;6691; # (暑; 暑; 暑; 暑; 暑; ) CJK COMPATIBILITY IDEOGRAPH-FA43 +FA44;6885;6885;6885;6885; # (梅; 梅; 梅; 梅; 梅; ) CJK COMPATIBILITY IDEOGRAPH-FA44 +FA45;6D77;6D77;6D77;6D77; # (海; 海; 海; 海; 海; ) CJK COMPATIBILITY IDEOGRAPH-FA45 +FA46;6E1A;6E1A;6E1A;6E1A; # (渚; 渚; 渚; 渚; 渚; ) CJK COMPATIBILITY IDEOGRAPH-FA46 +FA47;6F22;6F22;6F22;6F22; # (漢; 漢; 漢; 漢; 漢; ) CJK COMPATIBILITY IDEOGRAPH-FA47 +FA48;716E;716E;716E;716E; # (煮; 煮; 煮; 煮; 煮; ) CJK COMPATIBILITY IDEOGRAPH-FA48 +FA49;722B;722B;722B;722B; # (爫; 爫; 爫; 爫; 爫; ) CJK COMPATIBILITY IDEOGRAPH-FA49 +FA4A;7422;7422;7422;7422; # (琢; 琢; 琢; 琢; 琢; ) CJK COMPATIBILITY IDEOGRAPH-FA4A +FA4B;7891;7891;7891;7891; # (碑; 碑; 碑; 碑; 碑; ) CJK COMPATIBILITY IDEOGRAPH-FA4B +FA4C;793E;793E;793E;793E; # (社; 社; 社; 社; 社; ) CJK COMPATIBILITY IDEOGRAPH-FA4C +FA4D;7949;7949;7949;7949; # (祉; 祉; 祉; 祉; 祉; ) CJK COMPATIBILITY IDEOGRAPH-FA4D +FA4E;7948;7948;7948;7948; # (祈; 祈; 祈; 祈; 祈; ) CJK COMPATIBILITY IDEOGRAPH-FA4E +FA4F;7950;7950;7950;7950; # (祐; 祐; 祐; 祐; 祐; ) CJK COMPATIBILITY IDEOGRAPH-FA4F +FA50;7956;7956;7956;7956; # (祖; 祖; 祖; 祖; 祖; ) CJK COMPATIBILITY IDEOGRAPH-FA50 +FA51;795D;795D;795D;795D; # (祝; 祝; 祝; 祝; 祝; ) CJK COMPATIBILITY IDEOGRAPH-FA51 +FA52;798D;798D;798D;798D; # (禍; 禍; 禍; 禍; 禍; ) CJK COMPATIBILITY IDEOGRAPH-FA52 +FA53;798E;798E;798E;798E; # (禎; 禎; 禎; 禎; 禎; ) CJK COMPATIBILITY IDEOGRAPH-FA53 +FA54;7A40;7A40;7A40;7A40; # (穀; 穀; 穀; 穀; 穀; ) CJK COMPATIBILITY IDEOGRAPH-FA54 +FA55;7A81;7A81;7A81;7A81; # (突; 突; 突; 突; 突; ) CJK COMPATIBILITY IDEOGRAPH-FA55 +FA56;7BC0;7BC0;7BC0;7BC0; # (節; 節; 節; 節; 節; ) CJK COMPATIBILITY IDEOGRAPH-FA56 +FA57;7DF4;7DF4;7DF4;7DF4; # (練; 練; 練; 練; 練; ) CJK COMPATIBILITY IDEOGRAPH-FA57 +FA58;7E09;7E09;7E09;7E09; # (縉; 縉; 縉; 縉; 縉; ) CJK COMPATIBILITY IDEOGRAPH-FA58 +FA59;7E41;7E41;7E41;7E41; # (繁; 繁; 繁; 繁; 繁; ) CJK COMPATIBILITY IDEOGRAPH-FA59 +FA5A;7F72;7F72;7F72;7F72; # (署; 署; 署; 署; 署; ) CJK COMPATIBILITY IDEOGRAPH-FA5A +FA5B;8005;8005;8005;8005; # (者; 者; 者; 者; 者; ) CJK COMPATIBILITY IDEOGRAPH-FA5B +FA5C;81ED;81ED;81ED;81ED; # (臭; 臭; 臭; 臭; 臭; ) CJK COMPATIBILITY IDEOGRAPH-FA5C +FA5D;8279;8279;8279;8279; # (艹; 艹; 艹; 艹; 艹; ) CJK COMPATIBILITY IDEOGRAPH-FA5D +FA5E;8279;8279;8279;8279; # (艹; 艹; 艹; 艹; 艹; ) CJK COMPATIBILITY IDEOGRAPH-FA5E +FA5F;8457;8457;8457;8457; # (著; 著; 著; 著; 著; ) CJK COMPATIBILITY IDEOGRAPH-FA5F +FA60;8910;8910;8910;8910; # (褐; 褐; 褐; 褐; 褐; ) CJK COMPATIBILITY IDEOGRAPH-FA60 +FA61;8996;8996;8996;8996; # (視; 視; 視; 視; 視; ) CJK COMPATIBILITY IDEOGRAPH-FA61 +FA62;8B01;8B01;8B01;8B01; # (謁; 謁; 謁; 謁; 謁; ) CJK COMPATIBILITY IDEOGRAPH-FA62 +FA63;8B39;8B39;8B39;8B39; # (謹; 謹; 謹; 謹; 謹; ) CJK COMPATIBILITY IDEOGRAPH-FA63 +FA64;8CD3;8CD3;8CD3;8CD3; # (賓; 賓; 賓; 賓; 賓; ) CJK COMPATIBILITY IDEOGRAPH-FA64 +FA65;8D08;8D08;8D08;8D08; # (贈; 贈; 贈; 贈; 贈; ) CJK COMPATIBILITY IDEOGRAPH-FA65 +FA66;8FB6;8FB6;8FB6;8FB6; # (辶; 辶; 辶; 辶; 辶; ) CJK COMPATIBILITY IDEOGRAPH-FA66 +FA67;9038;9038;9038;9038; # (逸; 逸; 逸; 逸; 逸; ) CJK COMPATIBILITY IDEOGRAPH-FA67 +FA68;96E3;96E3;96E3;96E3; # (難; 難; 難; 難; 難; ) CJK COMPATIBILITY IDEOGRAPH-FA68 +FA69;97FF;97FF;97FF;97FF; # (響; 響; 響; 響; 響; ) CJK COMPATIBILITY IDEOGRAPH-FA69 +FA6A;983B;983B;983B;983B; # (頻; 頻; 頻; 頻; 頻; ) CJK COMPATIBILITY IDEOGRAPH-FA6A +FB00;FB00;FB00;0066 0066;0066 0066; # (ff; ff; ff; ff; ff; ) LATIN SMALL LIGATURE FF +FB01;FB01;FB01;0066 0069;0066 0069; # (fi; fi; fi; fi; fi; ) LATIN SMALL LIGATURE FI +FB02;FB02;FB02;0066 006C;0066 006C; # (fl; fl; fl; fl; fl; ) LATIN SMALL LIGATURE FL +FB03;FB03;FB03;0066 0066 0069;0066 0066 0069; # (ffi; ffi; ffi; ffi; ffi; ) LATIN SMALL LIGATURE FFI +FB04;FB04;FB04;0066 0066 006C;0066 0066 006C; # (ffl; ffl; ffl; ffl; ffl; ) LATIN SMALL LIGATURE FFL +FB05;FB05;FB05;0073 0074;0073 0074; # (ſt; ſt; ſt; st; st; ) LATIN SMALL LIGATURE LONG S T +FB06;FB06;FB06;0073 0074;0073 0074; # (st; st; st; st; st; ) LATIN SMALL LIGATURE ST +FB13;FB13;FB13;0574 0576;0574 0576; # (ﬓ; ﬓ; ﬓ; մն; մն; ) ARMENIAN SMALL LIGATURE MEN NOW +FB14;FB14;FB14;0574 0565;0574 0565; # (ﬔ; ﬔ; ﬔ; մե; մե; ) ARMENIAN SMALL LIGATURE MEN ECH +FB15;FB15;FB15;0574 056B;0574 056B; # (ﬕ; ﬕ; ﬕ; մի; մի; ) ARMENIAN SMALL LIGATURE MEN INI +FB16;FB16;FB16;057E 0576;057E 0576; # (ﬖ; ﬖ; ﬖ; վն; վն; ) ARMENIAN SMALL LIGATURE VEW NOW +FB17;FB17;FB17;0574 056D;0574 056D; # (ﬗ; ﬗ; ﬗ; մխ; մխ; ) ARMENIAN SMALL LIGATURE MEN XEH +FB1D;05D9 05B4;05D9 05B4;05D9 05B4;05D9 05B4; # (יִ; י◌ִ; י◌ִ; י◌ִ; י◌ִ; ) HEBREW LETTER YOD WITH HIRIQ +FB1F;05F2 05B7;05F2 05B7;05F2 05B7;05F2 05B7; # (ײַ; ײ◌ַ; ײ◌ַ; ײ◌ַ; ײ◌ַ; ) HEBREW LIGATURE YIDDISH YOD YOD PATAH +FB20;FB20;FB20;05E2;05E2; # (ﬠ; ﬠ; ﬠ; ע; ע; ) HEBREW LETTER ALTERNATIVE AYIN +FB21;FB21;FB21;05D0;05D0; # (ﬡ; ﬡ; ﬡ; א; א; ) HEBREW LETTER WIDE ALEF +FB22;FB22;FB22;05D3;05D3; # (ﬢ; ﬢ; ﬢ; ד; ד; ) HEBREW LETTER WIDE DALET +FB23;FB23;FB23;05D4;05D4; # (ﬣ; ﬣ; ﬣ; ה; ה; ) HEBREW LETTER WIDE HE +FB24;FB24;FB24;05DB;05DB; # (ﬤ; ﬤ; ﬤ; כ; כ; ) HEBREW LETTER WIDE KAF +FB25;FB25;FB25;05DC;05DC; # (ﬥ; ﬥ; ﬥ; ל; ל; ) HEBREW LETTER WIDE LAMED +FB26;FB26;FB26;05DD;05DD; # (ﬦ; ﬦ; ﬦ; ם; ם; ) HEBREW LETTER WIDE FINAL MEM +FB27;FB27;FB27;05E8;05E8; # (ﬧ; ﬧ; ﬧ; ר; ר; ) HEBREW LETTER WIDE RESH +FB28;FB28;FB28;05EA;05EA; # (ﬨ; ﬨ; ﬨ; ת; ת; ) HEBREW LETTER WIDE TAV +FB29;FB29;FB29;002B;002B; # (﬩; ﬩; ﬩; +; +; ) HEBREW LETTER ALTERNATIVE PLUS SIGN +FB2A;05E9 05C1;05E9 05C1;05E9 05C1;05E9 05C1; # (שׁ; ש◌ׁ; ש◌ׁ; ש◌ׁ; ש◌ׁ; ) HEBREW LETTER SHIN WITH SHIN DOT +FB2B;05E9 05C2;05E9 05C2;05E9 05C2;05E9 05C2; # (שׂ; ש◌ׂ; ש◌ׂ; ש◌ׂ; ש◌ׂ; ) HEBREW LETTER SHIN WITH SIN DOT +FB2C;05E9 05BC 05C1;05E9 05BC 05C1;05E9 05BC 05C1;05E9 05BC 05C1; # (שּׁ; ש◌ּ◌ׁ; ש◌ּ◌ׁ; ש◌ּ◌ׁ; ש◌ּ◌ׁ; ) HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT +FB2D;05E9 05BC 05C2;05E9 05BC 05C2;05E9 05BC 05C2;05E9 05BC 05C2; # (שּׂ; ש◌ּ◌ׂ; ש◌ּ◌ׂ; ש◌ּ◌ׂ; ש◌ּ◌ׂ; ) HEBREW LETTER SHIN WITH DAGESH AND SIN DOT +FB2E;05D0 05B7;05D0 05B7;05D0 05B7;05D0 05B7; # (אַ; א◌ַ; א◌ַ; א◌ַ; א◌ַ; ) HEBREW LETTER ALEF WITH PATAH +FB2F;05D0 05B8;05D0 05B8;05D0 05B8;05D0 05B8; # (אָ; א◌ָ; א◌ָ; א◌ָ; א◌ָ; ) HEBREW LETTER ALEF WITH QAMATS +FB30;05D0 05BC;05D0 05BC;05D0 05BC;05D0 05BC; # (אּ; א◌ּ; א◌ּ; א◌ּ; א◌ּ; ) HEBREW LETTER ALEF WITH MAPIQ +FB31;05D1 05BC;05D1 05BC;05D1 05BC;05D1 05BC; # (בּ; ב◌ּ; ב◌ּ; ב◌ּ; ב◌ּ; ) HEBREW LETTER BET WITH DAGESH +FB32;05D2 05BC;05D2 05BC;05D2 05BC;05D2 05BC; # (גּ; ג◌ּ; ג◌ּ; ג◌ּ; ג◌ּ; ) HEBREW LETTER GIMEL WITH DAGESH +FB33;05D3 05BC;05D3 05BC;05D3 05BC;05D3 05BC; # (דּ; ד◌ּ; ד◌ּ; ד◌ּ; ד◌ּ; ) HEBREW LETTER DALET WITH DAGESH +FB34;05D4 05BC;05D4 05BC;05D4 05BC;05D4 05BC; # (הּ; ה◌ּ; ה◌ּ; ה◌ּ; ה◌ּ; ) HEBREW LETTER HE WITH MAPIQ +FB35;05D5 05BC;05D5 05BC;05D5 05BC;05D5 05BC; # (וּ; ו◌ּ; ו◌ּ; ו◌ּ; ו◌ּ; ) HEBREW LETTER VAV WITH DAGESH +FB36;05D6 05BC;05D6 05BC;05D6 05BC;05D6 05BC; # (זּ; ז◌ּ; ז◌ּ; ז◌ּ; ז◌ּ; ) HEBREW LETTER ZAYIN WITH DAGESH +FB38;05D8 05BC;05D8 05BC;05D8 05BC;05D8 05BC; # (טּ; ט◌ּ; ט◌ּ; ט◌ּ; ט◌ּ; ) HEBREW LETTER TET WITH DAGESH +FB39;05D9 05BC;05D9 05BC;05D9 05BC;05D9 05BC; # (יּ; י◌ּ; י◌ּ; י◌ּ; י◌ּ; ) HEBREW LETTER YOD WITH DAGESH +FB3A;05DA 05BC;05DA 05BC;05DA 05BC;05DA 05BC; # (ךּ; ך◌ּ; ך◌ּ; ך◌ּ; ך◌ּ; ) HEBREW LETTER FINAL KAF WITH DAGESH +FB3B;05DB 05BC;05DB 05BC;05DB 05BC;05DB 05BC; # (כּ; כ◌ּ; כ◌ּ; כ◌ּ; כ◌ּ; ) HEBREW LETTER KAF WITH DAGESH +FB3C;05DC 05BC;05DC 05BC;05DC 05BC;05DC 05BC; # (לּ; ל◌ּ; ל◌ּ; ל◌ּ; ל◌ּ; ) HEBREW LETTER LAMED WITH DAGESH +FB3E;05DE 05BC;05DE 05BC;05DE 05BC;05DE 05BC; # (מּ; מ◌ּ; מ◌ּ; מ◌ּ; מ◌ּ; ) HEBREW LETTER MEM WITH DAGESH +FB40;05E0 05BC;05E0 05BC;05E0 05BC;05E0 05BC; # (נּ; נ◌ּ; נ◌ּ; נ◌ּ; נ◌ּ; ) HEBREW LETTER NUN WITH DAGESH +FB41;05E1 05BC;05E1 05BC;05E1 05BC;05E1 05BC; # (סּ; ס◌ּ; ס◌ּ; ס◌ּ; ס◌ּ; ) HEBREW LETTER SAMEKH WITH DAGESH +FB43;05E3 05BC;05E3 05BC;05E3 05BC;05E3 05BC; # (ףּ; ף◌ּ; ף◌ּ; ף◌ּ; ף◌ּ; ) HEBREW LETTER FINAL PE WITH DAGESH +FB44;05E4 05BC;05E4 05BC;05E4 05BC;05E4 05BC; # (פּ; פ◌ּ; פ◌ּ; פ◌ּ; פ◌ּ; ) HEBREW LETTER PE WITH DAGESH +FB46;05E6 05BC;05E6 05BC;05E6 05BC;05E6 05BC; # (צּ; צ◌ּ; צ◌ּ; צ◌ּ; צ◌ּ; ) HEBREW LETTER TSADI WITH DAGESH +FB47;05E7 05BC;05E7 05BC;05E7 05BC;05E7 05BC; # (קּ; ק◌ּ; ק◌ּ; ק◌ּ; ק◌ּ; ) HEBREW LETTER QOF WITH DAGESH +FB48;05E8 05BC;05E8 05BC;05E8 05BC;05E8 05BC; # (רּ; ר◌ּ; ר◌ּ; ר◌ּ; ר◌ּ; ) HEBREW LETTER RESH WITH DAGESH +FB49;05E9 05BC;05E9 05BC;05E9 05BC;05E9 05BC; # (שּ; ש◌ּ; ש◌ּ; ש◌ּ; ש◌ּ; ) HEBREW LETTER SHIN WITH DAGESH +FB4A;05EA 05BC;05EA 05BC;05EA 05BC;05EA 05BC; # (תּ; ת◌ּ; ת◌ּ; ת◌ּ; ת◌ּ; ) HEBREW LETTER TAV WITH DAGESH +FB4B;05D5 05B9;05D5 05B9;05D5 05B9;05D5 05B9; # (וֹ; ו◌ֹ; ו◌ֹ; ו◌ֹ; ו◌ֹ; ) HEBREW LETTER VAV WITH HOLAM +FB4C;05D1 05BF;05D1 05BF;05D1 05BF;05D1 05BF; # (בֿ; ב◌ֿ; ב◌ֿ; ב◌ֿ; ב◌ֿ; ) HEBREW LETTER BET WITH RAFE +FB4D;05DB 05BF;05DB 05BF;05DB 05BF;05DB 05BF; # (כֿ; כ◌ֿ; כ◌ֿ; כ◌ֿ; כ◌ֿ; ) HEBREW LETTER KAF WITH RAFE +FB4E;05E4 05BF;05E4 05BF;05E4 05BF;05E4 05BF; # (פֿ; פ◌ֿ; פ◌ֿ; פ◌ֿ; פ◌ֿ; ) HEBREW LETTER PE WITH RAFE +FB4F;FB4F;FB4F;05D0 05DC;05D0 05DC; # (ﭏ; ﭏ; ﭏ; אל; אל; ) HEBREW LIGATURE ALEF LAMED +FB50;FB50;FB50;0671;0671; # (ﭐ; ﭐ; ﭐ; ٱ; ٱ; ) ARABIC LETTER ALEF WASLA ISOLATED FORM +FB51;FB51;FB51;0671;0671; # (ﭑ; ﭑ; ﭑ; ٱ; ٱ; ) ARABIC LETTER ALEF WASLA FINAL FORM +FB52;FB52;FB52;067B;067B; # (ﭒ; ﭒ; ﭒ; ٻ; ٻ; ) ARABIC LETTER BEEH ISOLATED FORM +FB53;FB53;FB53;067B;067B; # (ﭓ; ﭓ; ﭓ; ٻ; ٻ; ) ARABIC LETTER BEEH FINAL FORM +FB54;FB54;FB54;067B;067B; # (ﭔ; ﭔ; ﭔ; ٻ; ٻ; ) ARABIC LETTER BEEH INITIAL FORM +FB55;FB55;FB55;067B;067B; # (ﭕ; ﭕ; ﭕ; ٻ; ٻ; ) ARABIC LETTER BEEH MEDIAL FORM +FB56;FB56;FB56;067E;067E; # (ﭖ; ﭖ; ﭖ; پ; پ; ) ARABIC LETTER PEH ISOLATED FORM +FB57;FB57;FB57;067E;067E; # (ﭗ; ﭗ; ﭗ; پ; پ; ) ARABIC LETTER PEH FINAL FORM +FB58;FB58;FB58;067E;067E; # (ﭘ; ﭘ; ﭘ; پ; پ; ) ARABIC LETTER PEH INITIAL FORM +FB59;FB59;FB59;067E;067E; # (ﭙ; ﭙ; ﭙ; پ; پ; ) ARABIC LETTER PEH MEDIAL FORM +FB5A;FB5A;FB5A;0680;0680; # (ﭚ; ﭚ; ﭚ; ڀ; ڀ; ) ARABIC LETTER BEHEH ISOLATED FORM +FB5B;FB5B;FB5B;0680;0680; # (ﭛ; ﭛ; ﭛ; ڀ; ڀ; ) ARABIC LETTER BEHEH FINAL FORM +FB5C;FB5C;FB5C;0680;0680; # (ﭜ; ﭜ; ﭜ; ڀ; ڀ; ) ARABIC LETTER BEHEH INITIAL FORM +FB5D;FB5D;FB5D;0680;0680; # (ﭝ; ﭝ; ﭝ; ڀ; ڀ; ) ARABIC LETTER BEHEH MEDIAL FORM +FB5E;FB5E;FB5E;067A;067A; # (ﭞ; ﭞ; ﭞ; ٺ; ٺ; ) ARABIC LETTER TTEHEH ISOLATED FORM +FB5F;FB5F;FB5F;067A;067A; # (ﭟ; ﭟ; ﭟ; ٺ; ٺ; ) ARABIC LETTER TTEHEH FINAL FORM +FB60;FB60;FB60;067A;067A; # (ﭠ; ﭠ; ﭠ; ٺ; ٺ; ) ARABIC LETTER TTEHEH INITIAL FORM +FB61;FB61;FB61;067A;067A; # (ﭡ; ﭡ; ﭡ; ٺ; ٺ; ) ARABIC LETTER TTEHEH MEDIAL FORM +FB62;FB62;FB62;067F;067F; # (ﭢ; ﭢ; ﭢ; ٿ; ٿ; ) ARABIC LETTER TEHEH ISOLATED FORM +FB63;FB63;FB63;067F;067F; # (ﭣ; ﭣ; ﭣ; ٿ; ٿ; ) ARABIC LETTER TEHEH FINAL FORM +FB64;FB64;FB64;067F;067F; # (ﭤ; ﭤ; ﭤ; ٿ; ٿ; ) ARABIC LETTER TEHEH INITIAL FORM +FB65;FB65;FB65;067F;067F; # (ﭥ; ﭥ; ﭥ; ٿ; ٿ; ) ARABIC LETTER TEHEH MEDIAL FORM +FB66;FB66;FB66;0679;0679; # (ﭦ; ﭦ; ﭦ; ٹ; ٹ; ) ARABIC LETTER TTEH ISOLATED FORM +FB67;FB67;FB67;0679;0679; # (ﭧ; ﭧ; ﭧ; ٹ; ٹ; ) ARABIC LETTER TTEH FINAL FORM +FB68;FB68;FB68;0679;0679; # (ﭨ; ﭨ; ﭨ; ٹ; ٹ; ) ARABIC LETTER TTEH INITIAL FORM +FB69;FB69;FB69;0679;0679; # (ﭩ; ﭩ; ﭩ; ٹ; ٹ; ) ARABIC LETTER TTEH MEDIAL FORM +FB6A;FB6A;FB6A;06A4;06A4; # (ﭪ; ﭪ; ﭪ; ڤ; ڤ; ) ARABIC LETTER VEH ISOLATED FORM +FB6B;FB6B;FB6B;06A4;06A4; # (ﭫ; ﭫ; ﭫ; ڤ; ڤ; ) ARABIC LETTER VEH FINAL FORM +FB6C;FB6C;FB6C;06A4;06A4; # (ﭬ; ﭬ; ﭬ; ڤ; ڤ; ) ARABIC LETTER VEH INITIAL FORM +FB6D;FB6D;FB6D;06A4;06A4; # (ﭭ; ﭭ; ﭭ; ڤ; ڤ; ) ARABIC LETTER VEH MEDIAL FORM +FB6E;FB6E;FB6E;06A6;06A6; # (ﭮ; ﭮ; ﭮ; ڦ; ڦ; ) ARABIC LETTER PEHEH ISOLATED FORM +FB6F;FB6F;FB6F;06A6;06A6; # (ﭯ; ﭯ; ﭯ; ڦ; ڦ; ) ARABIC LETTER PEHEH FINAL FORM +FB70;FB70;FB70;06A6;06A6; # (ﭰ; ﭰ; ﭰ; ڦ; ڦ; ) ARABIC LETTER PEHEH INITIAL FORM +FB71;FB71;FB71;06A6;06A6; # (ﭱ; ﭱ; ﭱ; ڦ; ڦ; ) ARABIC LETTER PEHEH MEDIAL FORM +FB72;FB72;FB72;0684;0684; # (ﭲ; ﭲ; ﭲ; ڄ; ڄ; ) ARABIC LETTER DYEH ISOLATED FORM +FB73;FB73;FB73;0684;0684; # (ﭳ; ﭳ; ﭳ; ڄ; ڄ; ) ARABIC LETTER DYEH FINAL FORM +FB74;FB74;FB74;0684;0684; # (ﭴ; ﭴ; ﭴ; ڄ; ڄ; ) ARABIC LETTER DYEH INITIAL FORM +FB75;FB75;FB75;0684;0684; # (ﭵ; ﭵ; ﭵ; ڄ; ڄ; ) ARABIC LETTER DYEH MEDIAL FORM +FB76;FB76;FB76;0683;0683; # (ﭶ; ﭶ; ﭶ; ڃ; ڃ; ) ARABIC LETTER NYEH ISOLATED FORM +FB77;FB77;FB77;0683;0683; # (ﭷ; ﭷ; ﭷ; ڃ; ڃ; ) ARABIC LETTER NYEH FINAL FORM +FB78;FB78;FB78;0683;0683; # (ﭸ; ﭸ; ﭸ; ڃ; ڃ; ) ARABIC LETTER NYEH INITIAL FORM +FB79;FB79;FB79;0683;0683; # (ﭹ; ﭹ; ﭹ; ڃ; ڃ; ) ARABIC LETTER NYEH MEDIAL FORM +FB7A;FB7A;FB7A;0686;0686; # (ﭺ; ﭺ; ﭺ; چ; چ; ) ARABIC LETTER TCHEH ISOLATED FORM +FB7B;FB7B;FB7B;0686;0686; # (ﭻ; ﭻ; ﭻ; چ; چ; ) ARABIC LETTER TCHEH FINAL FORM +FB7C;FB7C;FB7C;0686;0686; # (ﭼ; ﭼ; ﭼ; چ; چ; ) ARABIC LETTER TCHEH INITIAL FORM +FB7D;FB7D;FB7D;0686;0686; # (ﭽ; ﭽ; ﭽ; چ; چ; ) ARABIC LETTER TCHEH MEDIAL FORM +FB7E;FB7E;FB7E;0687;0687; # (ﭾ; ﭾ; ﭾ; ڇ; ڇ; ) ARABIC LETTER TCHEHEH ISOLATED FORM +FB7F;FB7F;FB7F;0687;0687; # (ﭿ; ﭿ; ﭿ; ڇ; ڇ; ) ARABIC LETTER TCHEHEH FINAL FORM +FB80;FB80;FB80;0687;0687; # (ﮀ; ﮀ; ﮀ; ڇ; ڇ; ) ARABIC LETTER TCHEHEH INITIAL FORM +FB81;FB81;FB81;0687;0687; # (ﮁ; ﮁ; ﮁ; ڇ; ڇ; ) ARABIC LETTER TCHEHEH MEDIAL FORM +FB82;FB82;FB82;068D;068D; # (ﮂ; ﮂ; ﮂ; ڍ; ڍ; ) ARABIC LETTER DDAHAL ISOLATED FORM +FB83;FB83;FB83;068D;068D; # (ﮃ; ﮃ; ﮃ; ڍ; ڍ; ) ARABIC LETTER DDAHAL FINAL FORM +FB84;FB84;FB84;068C;068C; # (ﮄ; ﮄ; ﮄ; ڌ; ڌ; ) ARABIC LETTER DAHAL ISOLATED FORM +FB85;FB85;FB85;068C;068C; # (ﮅ; ﮅ; ﮅ; ڌ; ڌ; ) ARABIC LETTER DAHAL FINAL FORM +FB86;FB86;FB86;068E;068E; # (ﮆ; ﮆ; ﮆ; ڎ; ڎ; ) ARABIC LETTER DUL ISOLATED FORM +FB87;FB87;FB87;068E;068E; # (ﮇ; ﮇ; ﮇ; ڎ; ڎ; ) ARABIC LETTER DUL FINAL FORM +FB88;FB88;FB88;0688;0688; # (ﮈ; ﮈ; ﮈ; ڈ; ڈ; ) ARABIC LETTER DDAL ISOLATED FORM +FB89;FB89;FB89;0688;0688; # (ﮉ; ﮉ; ﮉ; ڈ; ڈ; ) ARABIC LETTER DDAL FINAL FORM +FB8A;FB8A;FB8A;0698;0698; # (ﮊ; ﮊ; ﮊ; ژ; ژ; ) ARABIC LETTER JEH ISOLATED FORM +FB8B;FB8B;FB8B;0698;0698; # (ﮋ; ﮋ; ﮋ; ژ; ژ; ) ARABIC LETTER JEH FINAL FORM +FB8C;FB8C;FB8C;0691;0691; # (ﮌ; ﮌ; ﮌ; ڑ; ڑ; ) ARABIC LETTER RREH ISOLATED FORM +FB8D;FB8D;FB8D;0691;0691; # (ﮍ; ﮍ; ﮍ; ڑ; ڑ; ) ARABIC LETTER RREH FINAL FORM +FB8E;FB8E;FB8E;06A9;06A9; # (ﮎ; ﮎ; ﮎ; ک; ک; ) ARABIC LETTER KEHEH ISOLATED FORM +FB8F;FB8F;FB8F;06A9;06A9; # (ﮏ; ﮏ; ﮏ; ک; ک; ) ARABIC LETTER KEHEH FINAL FORM +FB90;FB90;FB90;06A9;06A9; # (ﮐ; ﮐ; ﮐ; ک; ک; ) ARABIC LETTER KEHEH INITIAL FORM +FB91;FB91;FB91;06A9;06A9; # (ﮑ; ﮑ; ﮑ; ک; ک; ) ARABIC LETTER KEHEH MEDIAL FORM +FB92;FB92;FB92;06AF;06AF; # (ﮒ; ﮒ; ﮒ; گ; گ; ) ARABIC LETTER GAF ISOLATED FORM +FB93;FB93;FB93;06AF;06AF; # (ﮓ; ﮓ; ﮓ; گ; گ; ) ARABIC LETTER GAF FINAL FORM +FB94;FB94;FB94;06AF;06AF; # (ﮔ; ﮔ; ﮔ; گ; گ; ) ARABIC LETTER GAF INITIAL FORM +FB95;FB95;FB95;06AF;06AF; # (ﮕ; ﮕ; ﮕ; گ; گ; ) ARABIC LETTER GAF MEDIAL FORM +FB96;FB96;FB96;06B3;06B3; # (ﮖ; ﮖ; ﮖ; ڳ; ڳ; ) ARABIC LETTER GUEH ISOLATED FORM +FB97;FB97;FB97;06B3;06B3; # (ﮗ; ﮗ; ﮗ; ڳ; ڳ; ) ARABIC LETTER GUEH FINAL FORM +FB98;FB98;FB98;06B3;06B3; # (ﮘ; ﮘ; ﮘ; ڳ; ڳ; ) ARABIC LETTER GUEH INITIAL FORM +FB99;FB99;FB99;06B3;06B3; # (ﮙ; ﮙ; ﮙ; ڳ; ڳ; ) ARABIC LETTER GUEH MEDIAL FORM +FB9A;FB9A;FB9A;06B1;06B1; # (ﮚ; ﮚ; ﮚ; ڱ; ڱ; ) ARABIC LETTER NGOEH ISOLATED FORM +FB9B;FB9B;FB9B;06B1;06B1; # (ﮛ; ﮛ; ﮛ; ڱ; ڱ; ) ARABIC LETTER NGOEH FINAL FORM +FB9C;FB9C;FB9C;06B1;06B1; # (ﮜ; ﮜ; ﮜ; ڱ; ڱ; ) ARABIC LETTER NGOEH INITIAL FORM +FB9D;FB9D;FB9D;06B1;06B1; # (ﮝ; ﮝ; ﮝ; ڱ; ڱ; ) ARABIC LETTER NGOEH MEDIAL FORM +FB9E;FB9E;FB9E;06BA;06BA; # (ﮞ; ﮞ; ﮞ; ں; ں; ) ARABIC LETTER NOON GHUNNA ISOLATED FORM +FB9F;FB9F;FB9F;06BA;06BA; # (ﮟ; ﮟ; ﮟ; ں; ں; ) ARABIC LETTER NOON GHUNNA FINAL FORM +FBA0;FBA0;FBA0;06BB;06BB; # (ﮠ; ﮠ; ﮠ; ڻ; ڻ; ) ARABIC LETTER RNOON ISOLATED FORM +FBA1;FBA1;FBA1;06BB;06BB; # (ﮡ; ﮡ; ﮡ; ڻ; ڻ; ) ARABIC LETTER RNOON FINAL FORM +FBA2;FBA2;FBA2;06BB;06BB; # (ﮢ; ﮢ; ﮢ; ڻ; ڻ; ) ARABIC LETTER RNOON INITIAL FORM +FBA3;FBA3;FBA3;06BB;06BB; # (ﮣ; ﮣ; ﮣ; ڻ; ڻ; ) ARABIC LETTER RNOON MEDIAL FORM +FBA4;FBA4;FBA4;06C0;06D5 0654; # (ﮤ; ﮤ; ﮤ; ۀ; ە◌ٔ; ) ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM +FBA5;FBA5;FBA5;06C0;06D5 0654; # (ﮥ; ﮥ; ﮥ; ۀ; ە◌ٔ; ) ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM +FBA6;FBA6;FBA6;06C1;06C1; # (ﮦ; ﮦ; ﮦ; ہ; ہ; ) ARABIC LETTER HEH GOAL ISOLATED FORM +FBA7;FBA7;FBA7;06C1;06C1; # (ﮧ; ﮧ; ﮧ; ہ; ہ; ) ARABIC LETTER HEH GOAL FINAL FORM +FBA8;FBA8;FBA8;06C1;06C1; # (ﮨ; ﮨ; ﮨ; ہ; ہ; ) ARABIC LETTER HEH GOAL INITIAL FORM +FBA9;FBA9;FBA9;06C1;06C1; # (ﮩ; ﮩ; ﮩ; ہ; ہ; ) ARABIC LETTER HEH GOAL MEDIAL FORM +FBAA;FBAA;FBAA;06BE;06BE; # (ﮪ; ﮪ; ﮪ; ھ; ھ; ) ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM +FBAB;FBAB;FBAB;06BE;06BE; # (ﮫ; ﮫ; ﮫ; ھ; ھ; ) ARABIC LETTER HEH DOACHASHMEE FINAL FORM +FBAC;FBAC;FBAC;06BE;06BE; # (ﮬ; ﮬ; ﮬ; ھ; ھ; ) ARABIC LETTER HEH DOACHASHMEE INITIAL FORM +FBAD;FBAD;FBAD;06BE;06BE; # (ﮭ; ﮭ; ﮭ; ھ; ھ; ) ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM +FBAE;FBAE;FBAE;06D2;06D2; # (ﮮ; ﮮ; ﮮ; ے; ے; ) ARABIC LETTER YEH BARREE ISOLATED FORM +FBAF;FBAF;FBAF;06D2;06D2; # (ﮯ; ﮯ; ﮯ; ے; ے; ) ARABIC LETTER YEH BARREE FINAL FORM +FBB0;FBB0;FBB0;06D3;06D2 0654; # (ﮰ; ﮰ; ﮰ; ۓ; ے◌ٔ; ) ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM +FBB1;FBB1;FBB1;06D3;06D2 0654; # (ﮱ; ﮱ; ﮱ; ۓ; ے◌ٔ; ) ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM +FBD3;FBD3;FBD3;06AD;06AD; # (ﯓ; ﯓ; ﯓ; ڭ; ڭ; ) ARABIC LETTER NG ISOLATED FORM +FBD4;FBD4;FBD4;06AD;06AD; # (ﯔ; ﯔ; ﯔ; ڭ; ڭ; ) ARABIC LETTER NG FINAL FORM +FBD5;FBD5;FBD5;06AD;06AD; # (ﯕ; ﯕ; ﯕ; ڭ; ڭ; ) ARABIC LETTER NG INITIAL FORM +FBD6;FBD6;FBD6;06AD;06AD; # (ﯖ; ﯖ; ﯖ; ڭ; ڭ; ) ARABIC LETTER NG MEDIAL FORM +FBD7;FBD7;FBD7;06C7;06C7; # (ﯗ; ﯗ; ﯗ; ۇ; ۇ; ) ARABIC LETTER U ISOLATED FORM +FBD8;FBD8;FBD8;06C7;06C7; # (ﯘ; ﯘ; ﯘ; ۇ; ۇ; ) ARABIC LETTER U FINAL FORM +FBD9;FBD9;FBD9;06C6;06C6; # (ﯙ; ﯙ; ﯙ; ۆ; ۆ; ) ARABIC LETTER OE ISOLATED FORM +FBDA;FBDA;FBDA;06C6;06C6; # (ﯚ; ﯚ; ﯚ; ۆ; ۆ; ) ARABIC LETTER OE FINAL FORM +FBDB;FBDB;FBDB;06C8;06C8; # (ﯛ; ﯛ; ﯛ; ۈ; ۈ; ) ARABIC LETTER YU ISOLATED FORM +FBDC;FBDC;FBDC;06C8;06C8; # (ﯜ; ﯜ; ﯜ; ۈ; ۈ; ) ARABIC LETTER YU FINAL FORM +FBDD;FBDD;FBDD;06C7 0674;06C7 0674; # (ﯝ; ﯝ; ﯝ; ۇٴ; ۇٴ; ) ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM +FBDE;FBDE;FBDE;06CB;06CB; # (ﯞ; ﯞ; ﯞ; ۋ; ۋ; ) ARABIC LETTER VE ISOLATED FORM +FBDF;FBDF;FBDF;06CB;06CB; # (ﯟ; ﯟ; ﯟ; ۋ; ۋ; ) ARABIC LETTER VE FINAL FORM +FBE0;FBE0;FBE0;06C5;06C5; # (ﯠ; ﯠ; ﯠ; ۅ; ۅ; ) ARABIC LETTER KIRGHIZ OE ISOLATED FORM +FBE1;FBE1;FBE1;06C5;06C5; # (ﯡ; ﯡ; ﯡ; ۅ; ۅ; ) ARABIC LETTER KIRGHIZ OE FINAL FORM +FBE2;FBE2;FBE2;06C9;06C9; # (ﯢ; ﯢ; ﯢ; ۉ; ۉ; ) ARABIC LETTER KIRGHIZ YU ISOLATED FORM +FBE3;FBE3;FBE3;06C9;06C9; # (ﯣ; ﯣ; ﯣ; ۉ; ۉ; ) ARABIC LETTER KIRGHIZ YU FINAL FORM +FBE4;FBE4;FBE4;06D0;06D0; # (ﯤ; ﯤ; ﯤ; ې; ې; ) ARABIC LETTER E ISOLATED FORM +FBE5;FBE5;FBE5;06D0;06D0; # (ﯥ; ﯥ; ﯥ; ې; ې; ) ARABIC LETTER E FINAL FORM +FBE6;FBE6;FBE6;06D0;06D0; # (ﯦ; ﯦ; ﯦ; ې; ې; ) ARABIC LETTER E INITIAL FORM +FBE7;FBE7;FBE7;06D0;06D0; # (ﯧ; ﯧ; ﯧ; ې; ې; ) ARABIC LETTER E MEDIAL FORM +FBE8;FBE8;FBE8;0649;0649; # (ﯨ; ﯨ; ﯨ; ى; ى; ) ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM +FBE9;FBE9;FBE9;0649;0649; # (ﯩ; ﯩ; ﯩ; ى; ى; ) ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM +FBEA;FBEA;FBEA;0626 0627;064A 0654 0627; # (ﯪ; ﯪ; ﯪ; ئا; ي◌ٔا; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM +FBEB;FBEB;FBEB;0626 0627;064A 0654 0627; # (ﯫ; ﯫ; ﯫ; ئا; ي◌ٔا; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM +FBEC;FBEC;FBEC;0626 06D5;064A 0654 06D5; # (ﯬ; ﯬ; ﯬ; ئە; ي◌ٔە; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM +FBED;FBED;FBED;0626 06D5;064A 0654 06D5; # (ﯭ; ﯭ; ﯭ; ئە; ي◌ٔە; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM +FBEE;FBEE;FBEE;0626 0648;064A 0654 0648; # (ﯮ; ﯮ; ﯮ; ئو; ي◌ٔو; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM +FBEF;FBEF;FBEF;0626 0648;064A 0654 0648; # (ﯯ; ﯯ; ﯯ; ئو; ي◌ٔو; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM +FBF0;FBF0;FBF0;0626 06C7;064A 0654 06C7; # (ﯰ; ﯰ; ﯰ; ئۇ; ي◌ٔۇ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM +FBF1;FBF1;FBF1;0626 06C7;064A 0654 06C7; # (ﯱ; ﯱ; ﯱ; ئۇ; ي◌ٔۇ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM +FBF2;FBF2;FBF2;0626 06C6;064A 0654 06C6; # (ﯲ; ﯲ; ﯲ; ئۆ; ي◌ٔۆ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM +FBF3;FBF3;FBF3;0626 06C6;064A 0654 06C6; # (ﯳ; ﯳ; ﯳ; ئۆ; ي◌ٔۆ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM +FBF4;FBF4;FBF4;0626 06C8;064A 0654 06C8; # (ﯴ; ﯴ; ﯴ; ئۈ; ي◌ٔۈ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM +FBF5;FBF5;FBF5;0626 06C8;064A 0654 06C8; # (ﯵ; ﯵ; ﯵ; ئۈ; ي◌ٔۈ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM +FBF6;FBF6;FBF6;0626 06D0;064A 0654 06D0; # (ﯶ; ﯶ; ﯶ; ئې; ي◌ٔې; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM +FBF7;FBF7;FBF7;0626 06D0;064A 0654 06D0; # (ﯷ; ﯷ; ﯷ; ئې; ي◌ٔې; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM +FBF8;FBF8;FBF8;0626 06D0;064A 0654 06D0; # (ﯸ; ﯸ; ﯸ; ئې; ي◌ٔې; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM +FBF9;FBF9;FBF9;0626 0649;064A 0654 0649; # (ﯹ; ﯹ; ﯹ; ئى; ي◌ٔى; ) ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM +FBFA;FBFA;FBFA;0626 0649;064A 0654 0649; # (ﯺ; ﯺ; ﯺ; ئى; ي◌ٔى; ) ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM +FBFB;FBFB;FBFB;0626 0649;064A 0654 0649; # (ﯻ; ﯻ; ﯻ; ئى; ي◌ٔى; ) ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM +FBFC;FBFC;FBFC;06CC;06CC; # (ﯼ; ﯼ; ﯼ; ی; ی; ) ARABIC LETTER FARSI YEH ISOLATED FORM +FBFD;FBFD;FBFD;06CC;06CC; # (ﯽ; ﯽ; ﯽ; ی; ی; ) ARABIC LETTER FARSI YEH FINAL FORM +FBFE;FBFE;FBFE;06CC;06CC; # (ﯾ; ﯾ; ﯾ; ی; ی; ) ARABIC LETTER FARSI YEH INITIAL FORM +FBFF;FBFF;FBFF;06CC;06CC; # (ﯿ; ﯿ; ﯿ; ی; ی; ) ARABIC LETTER FARSI YEH MEDIAL FORM +FC00;FC00;FC00;0626 062C;064A 0654 062C; # (ﰀ; ﰀ; ﰀ; ئج; ي◌ٔج; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM +FC01;FC01;FC01;0626 062D;064A 0654 062D; # (ﰁ; ﰁ; ﰁ; ئح; ي◌ٔح; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM +FC02;FC02;FC02;0626 0645;064A 0654 0645; # (ﰂ; ﰂ; ﰂ; ئم; ي◌ٔم; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM +FC03;FC03;FC03;0626 0649;064A 0654 0649; # (ﰃ; ﰃ; ﰃ; ئى; ي◌ٔى; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM +FC04;FC04;FC04;0626 064A;064A 0654 064A; # (ﰄ; ﰄ; ﰄ; ئي; ي◌ٔي; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM +FC05;FC05;FC05;0628 062C;0628 062C; # (ﰅ; ﰅ; ﰅ; بج; بج; ) ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM +FC06;FC06;FC06;0628 062D;0628 062D; # (ﰆ; ﰆ; ﰆ; بح; بح; ) ARABIC LIGATURE BEH WITH HAH ISOLATED FORM +FC07;FC07;FC07;0628 062E;0628 062E; # (ﰇ; ﰇ; ﰇ; بخ; بخ; ) ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM +FC08;FC08;FC08;0628 0645;0628 0645; # (ﰈ; ﰈ; ﰈ; بم; بم; ) ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM +FC09;FC09;FC09;0628 0649;0628 0649; # (ﰉ; ﰉ; ﰉ; بى; بى; ) ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM +FC0A;FC0A;FC0A;0628 064A;0628 064A; # (ﰊ; ﰊ; ﰊ; بي; بي; ) ARABIC LIGATURE BEH WITH YEH ISOLATED FORM +FC0B;FC0B;FC0B;062A 062C;062A 062C; # (ﰋ; ﰋ; ﰋ; تج; تج; ) ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM +FC0C;FC0C;FC0C;062A 062D;062A 062D; # (ﰌ; ﰌ; ﰌ; تح; تح; ) ARABIC LIGATURE TEH WITH HAH ISOLATED FORM +FC0D;FC0D;FC0D;062A 062E;062A 062E; # (ﰍ; ﰍ; ﰍ; تخ; تخ; ) ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM +FC0E;FC0E;FC0E;062A 0645;062A 0645; # (ﰎ; ﰎ; ﰎ; تم; تم; ) ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM +FC0F;FC0F;FC0F;062A 0649;062A 0649; # (ﰏ; ﰏ; ﰏ; تى; تى; ) ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM +FC10;FC10;FC10;062A 064A;062A 064A; # (ﰐ; ﰐ; ﰐ; تي; تي; ) ARABIC LIGATURE TEH WITH YEH ISOLATED FORM +FC11;FC11;FC11;062B 062C;062B 062C; # (ﰑ; ﰑ; ﰑ; ثج; ثج; ) ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM +FC12;FC12;FC12;062B 0645;062B 0645; # (ﰒ; ﰒ; ﰒ; ثم; ثم; ) ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM +FC13;FC13;FC13;062B 0649;062B 0649; # (ﰓ; ﰓ; ﰓ; ثى; ثى; ) ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM +FC14;FC14;FC14;062B 064A;062B 064A; # (ﰔ; ﰔ; ﰔ; ثي; ثي; ) ARABIC LIGATURE THEH WITH YEH ISOLATED FORM +FC15;FC15;FC15;062C 062D;062C 062D; # (ﰕ; ﰕ; ﰕ; جح; جح; ) ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM +FC16;FC16;FC16;062C 0645;062C 0645; # (ﰖ; ﰖ; ﰖ; جم; جم; ) ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM +FC17;FC17;FC17;062D 062C;062D 062C; # (ﰗ; ﰗ; ﰗ; حج; حج; ) ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM +FC18;FC18;FC18;062D 0645;062D 0645; # (ﰘ; ﰘ; ﰘ; حم; حم; ) ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM +FC19;FC19;FC19;062E 062C;062E 062C; # (ﰙ; ﰙ; ﰙ; خج; خج; ) ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM +FC1A;FC1A;FC1A;062E 062D;062E 062D; # (ﰚ; ﰚ; ﰚ; خح; خح; ) ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM +FC1B;FC1B;FC1B;062E 0645;062E 0645; # (ﰛ; ﰛ; ﰛ; خم; خم; ) ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM +FC1C;FC1C;FC1C;0633 062C;0633 062C; # (ﰜ; ﰜ; ﰜ; سج; سج; ) ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM +FC1D;FC1D;FC1D;0633 062D;0633 062D; # (ﰝ; ﰝ; ﰝ; سح; سح; ) ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM +FC1E;FC1E;FC1E;0633 062E;0633 062E; # (ﰞ; ﰞ; ﰞ; سخ; سخ; ) ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM +FC1F;FC1F;FC1F;0633 0645;0633 0645; # (ﰟ; ﰟ; ﰟ; سم; سم; ) ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM +FC20;FC20;FC20;0635 062D;0635 062D; # (ﰠ; ﰠ; ﰠ; صح; صح; ) ARABIC LIGATURE SAD WITH HAH ISOLATED FORM +FC21;FC21;FC21;0635 0645;0635 0645; # (ﰡ; ﰡ; ﰡ; صم; صم; ) ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM +FC22;FC22;FC22;0636 062C;0636 062C; # (ﰢ; ﰢ; ﰢ; ضج; ضج; ) ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM +FC23;FC23;FC23;0636 062D;0636 062D; # (ﰣ; ﰣ; ﰣ; ضح; ضح; ) ARABIC LIGATURE DAD WITH HAH ISOLATED FORM +FC24;FC24;FC24;0636 062E;0636 062E; # (ﰤ; ﰤ; ﰤ; ضخ; ضخ; ) ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM +FC25;FC25;FC25;0636 0645;0636 0645; # (ﰥ; ﰥ; ﰥ; ضم; ضم; ) ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM +FC26;FC26;FC26;0637 062D;0637 062D; # (ﰦ; ﰦ; ﰦ; طح; طح; ) ARABIC LIGATURE TAH WITH HAH ISOLATED FORM +FC27;FC27;FC27;0637 0645;0637 0645; # (ﰧ; ﰧ; ﰧ; طم; طم; ) ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM +FC28;FC28;FC28;0638 0645;0638 0645; # (ﰨ; ﰨ; ﰨ; ظم; ظم; ) ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM +FC29;FC29;FC29;0639 062C;0639 062C; # (ﰩ; ﰩ; ﰩ; عج; عج; ) ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM +FC2A;FC2A;FC2A;0639 0645;0639 0645; # (ﰪ; ﰪ; ﰪ; عم; عم; ) ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM +FC2B;FC2B;FC2B;063A 062C;063A 062C; # (ﰫ; ﰫ; ﰫ; غج; غج; ) ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM +FC2C;FC2C;FC2C;063A 0645;063A 0645; # (ﰬ; ﰬ; ﰬ; غم; غم; ) ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM +FC2D;FC2D;FC2D;0641 062C;0641 062C; # (ﰭ; ﰭ; ﰭ; فج; فج; ) ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM +FC2E;FC2E;FC2E;0641 062D;0641 062D; # (ﰮ; ﰮ; ﰮ; فح; فح; ) ARABIC LIGATURE FEH WITH HAH ISOLATED FORM +FC2F;FC2F;FC2F;0641 062E;0641 062E; # (ﰯ; ﰯ; ﰯ; فخ; فخ; ) ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM +FC30;FC30;FC30;0641 0645;0641 0645; # (ﰰ; ﰰ; ﰰ; فم; فم; ) ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM +FC31;FC31;FC31;0641 0649;0641 0649; # (ﰱ; ﰱ; ﰱ; فى; فى; ) ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM +FC32;FC32;FC32;0641 064A;0641 064A; # (ﰲ; ﰲ; ﰲ; في; في; ) ARABIC LIGATURE FEH WITH YEH ISOLATED FORM +FC33;FC33;FC33;0642 062D;0642 062D; # (ﰳ; ﰳ; ﰳ; قح; قح; ) ARABIC LIGATURE QAF WITH HAH ISOLATED FORM +FC34;FC34;FC34;0642 0645;0642 0645; # (ﰴ; ﰴ; ﰴ; قم; قم; ) ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM +FC35;FC35;FC35;0642 0649;0642 0649; # (ﰵ; ﰵ; ﰵ; قى; قى; ) ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM +FC36;FC36;FC36;0642 064A;0642 064A; # (ﰶ; ﰶ; ﰶ; قي; قي; ) ARABIC LIGATURE QAF WITH YEH ISOLATED FORM +FC37;FC37;FC37;0643 0627;0643 0627; # (ﰷ; ﰷ; ﰷ; كا; كا; ) ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM +FC38;FC38;FC38;0643 062C;0643 062C; # (ﰸ; ﰸ; ﰸ; كج; كج; ) ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM +FC39;FC39;FC39;0643 062D;0643 062D; # (ﰹ; ﰹ; ﰹ; كح; كح; ) ARABIC LIGATURE KAF WITH HAH ISOLATED FORM +FC3A;FC3A;FC3A;0643 062E;0643 062E; # (ﰺ; ﰺ; ﰺ; كخ; كخ; ) ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM +FC3B;FC3B;FC3B;0643 0644;0643 0644; # (ﰻ; ﰻ; ﰻ; كل; كل; ) ARABIC LIGATURE KAF WITH LAM ISOLATED FORM +FC3C;FC3C;FC3C;0643 0645;0643 0645; # (ﰼ; ﰼ; ﰼ; كم; كم; ) ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM +FC3D;FC3D;FC3D;0643 0649;0643 0649; # (ﰽ; ﰽ; ﰽ; كى; كى; ) ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM +FC3E;FC3E;FC3E;0643 064A;0643 064A; # (ﰾ; ﰾ; ﰾ; كي; كي; ) ARABIC LIGATURE KAF WITH YEH ISOLATED FORM +FC3F;FC3F;FC3F;0644 062C;0644 062C; # (ﰿ; ﰿ; ﰿ; لج; لج; ) ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM +FC40;FC40;FC40;0644 062D;0644 062D; # (ﱀ; ﱀ; ﱀ; لح; لح; ) ARABIC LIGATURE LAM WITH HAH ISOLATED FORM +FC41;FC41;FC41;0644 062E;0644 062E; # (ﱁ; ﱁ; ﱁ; لخ; لخ; ) ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM +FC42;FC42;FC42;0644 0645;0644 0645; # (ﱂ; ﱂ; ﱂ; لم; لم; ) ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM +FC43;FC43;FC43;0644 0649;0644 0649; # (ﱃ; ﱃ; ﱃ; لى; لى; ) ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM +FC44;FC44;FC44;0644 064A;0644 064A; # (ﱄ; ﱄ; ﱄ; لي; لي; ) ARABIC LIGATURE LAM WITH YEH ISOLATED FORM +FC45;FC45;FC45;0645 062C;0645 062C; # (ﱅ; ﱅ; ﱅ; مج; مج; ) ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM +FC46;FC46;FC46;0645 062D;0645 062D; # (ﱆ; ﱆ; ﱆ; مح; مح; ) ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM +FC47;FC47;FC47;0645 062E;0645 062E; # (ﱇ; ﱇ; ﱇ; مخ; مخ; ) ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM +FC48;FC48;FC48;0645 0645;0645 0645; # (ﱈ; ﱈ; ﱈ; مم; مم; ) ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM +FC49;FC49;FC49;0645 0649;0645 0649; # (ﱉ; ﱉ; ﱉ; مى; مى; ) ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM +FC4A;FC4A;FC4A;0645 064A;0645 064A; # (ﱊ; ﱊ; ﱊ; مي; مي; ) ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM +FC4B;FC4B;FC4B;0646 062C;0646 062C; # (ﱋ; ﱋ; ﱋ; نج; نج; ) ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM +FC4C;FC4C;FC4C;0646 062D;0646 062D; # (ﱌ; ﱌ; ﱌ; نح; نح; ) ARABIC LIGATURE NOON WITH HAH ISOLATED FORM +FC4D;FC4D;FC4D;0646 062E;0646 062E; # (ﱍ; ﱍ; ﱍ; نخ; نخ; ) ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM +FC4E;FC4E;FC4E;0646 0645;0646 0645; # (ﱎ; ﱎ; ﱎ; نم; نم; ) ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM +FC4F;FC4F;FC4F;0646 0649;0646 0649; # (ﱏ; ﱏ; ﱏ; نى; نى; ) ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM +FC50;FC50;FC50;0646 064A;0646 064A; # (ﱐ; ﱐ; ﱐ; ني; ني; ) ARABIC LIGATURE NOON WITH YEH ISOLATED FORM +FC51;FC51;FC51;0647 062C;0647 062C; # (ﱑ; ﱑ; ﱑ; هج; هج; ) ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM +FC52;FC52;FC52;0647 0645;0647 0645; # (ﱒ; ﱒ; ﱒ; هم; هم; ) ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM +FC53;FC53;FC53;0647 0649;0647 0649; # (ﱓ; ﱓ; ﱓ; هى; هى; ) ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM +FC54;FC54;FC54;0647 064A;0647 064A; # (ﱔ; ﱔ; ﱔ; هي; هي; ) ARABIC LIGATURE HEH WITH YEH ISOLATED FORM +FC55;FC55;FC55;064A 062C;064A 062C; # (ﱕ; ﱕ; ﱕ; يج; يج; ) ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM +FC56;FC56;FC56;064A 062D;064A 062D; # (ﱖ; ﱖ; ﱖ; يح; يح; ) ARABIC LIGATURE YEH WITH HAH ISOLATED FORM +FC57;FC57;FC57;064A 062E;064A 062E; # (ﱗ; ﱗ; ﱗ; يخ; يخ; ) ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM +FC58;FC58;FC58;064A 0645;064A 0645; # (ﱘ; ﱘ; ﱘ; يم; يم; ) ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM +FC59;FC59;FC59;064A 0649;064A 0649; # (ﱙ; ﱙ; ﱙ; يى; يى; ) ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM +FC5A;FC5A;FC5A;064A 064A;064A 064A; # (ﱚ; ﱚ; ﱚ; يي; يي; ) ARABIC LIGATURE YEH WITH YEH ISOLATED FORM +FC5B;FC5B;FC5B;0630 0670;0630 0670; # (ﱛ; ﱛ; ﱛ; ذ◌ٰ; ذ◌ٰ; ) ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM +FC5C;FC5C;FC5C;0631 0670;0631 0670; # (ﱜ; ﱜ; ﱜ; ر◌ٰ; ر◌ٰ; ) ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM +FC5D;FC5D;FC5D;0649 0670;0649 0670; # (ﱝ; ﱝ; ﱝ; ى◌ٰ; ى◌ٰ; ) ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM +FC5E;FC5E;FC5E;0020 064C 0651;0020 064C 0651; # (ﱞ; ﱞ; ﱞ; ◌ٌ◌ّ; ◌ٌ◌ّ; ) ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM +FC5F;FC5F;FC5F;0020 064D 0651;0020 064D 0651; # (ﱟ; ﱟ; ﱟ; ◌ٍ◌ّ; ◌ٍ◌ّ; ) ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM +FC60;FC60;FC60;0020 064E 0651;0020 064E 0651; # (ﱠ; ﱠ; ﱠ; ◌َ◌ّ; ◌َ◌ّ; ) ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM +FC61;FC61;FC61;0020 064F 0651;0020 064F 0651; # (ﱡ; ﱡ; ﱡ; ◌ُ◌ّ; ◌ُ◌ّ; ) ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM +FC62;FC62;FC62;0020 0650 0651;0020 0650 0651; # (ﱢ; ﱢ; ﱢ; ◌ِ◌ّ; ◌ِ◌ّ; ) ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM +FC63;FC63;FC63;0020 0651 0670;0020 0651 0670; # (ﱣ; ﱣ; ﱣ; ◌ّ◌ٰ; ◌ّ◌ٰ; ) ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM +FC64;FC64;FC64;0626 0631;064A 0654 0631; # (ﱤ; ﱤ; ﱤ; ئر; ي◌ٔر; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM +FC65;FC65;FC65;0626 0632;064A 0654 0632; # (ﱥ; ﱥ; ﱥ; ئز; ي◌ٔز; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM +FC66;FC66;FC66;0626 0645;064A 0654 0645; # (ﱦ; ﱦ; ﱦ; ئم; ي◌ٔم; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM +FC67;FC67;FC67;0626 0646;064A 0654 0646; # (ﱧ; ﱧ; ﱧ; ئن; ي◌ٔن; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM +FC68;FC68;FC68;0626 0649;064A 0654 0649; # (ﱨ; ﱨ; ﱨ; ئى; ي◌ٔى; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM +FC69;FC69;FC69;0626 064A;064A 0654 064A; # (ﱩ; ﱩ; ﱩ; ئي; ي◌ٔي; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM +FC6A;FC6A;FC6A;0628 0631;0628 0631; # (ﱪ; ﱪ; ﱪ; بر; بر; ) ARABIC LIGATURE BEH WITH REH FINAL FORM +FC6B;FC6B;FC6B;0628 0632;0628 0632; # (ﱫ; ﱫ; ﱫ; بز; بز; ) ARABIC LIGATURE BEH WITH ZAIN FINAL FORM +FC6C;FC6C;FC6C;0628 0645;0628 0645; # (ﱬ; ﱬ; ﱬ; بم; بم; ) ARABIC LIGATURE BEH WITH MEEM FINAL FORM +FC6D;FC6D;FC6D;0628 0646;0628 0646; # (ﱭ; ﱭ; ﱭ; بن; بن; ) ARABIC LIGATURE BEH WITH NOON FINAL FORM +FC6E;FC6E;FC6E;0628 0649;0628 0649; # (ﱮ; ﱮ; ﱮ; بى; بى; ) ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM +FC6F;FC6F;FC6F;0628 064A;0628 064A; # (ﱯ; ﱯ; ﱯ; بي; بي; ) ARABIC LIGATURE BEH WITH YEH FINAL FORM +FC70;FC70;FC70;062A 0631;062A 0631; # (ﱰ; ﱰ; ﱰ; تر; تر; ) ARABIC LIGATURE TEH WITH REH FINAL FORM +FC71;FC71;FC71;062A 0632;062A 0632; # (ﱱ; ﱱ; ﱱ; تز; تز; ) ARABIC LIGATURE TEH WITH ZAIN FINAL FORM +FC72;FC72;FC72;062A 0645;062A 0645; # (ﱲ; ﱲ; ﱲ; تم; تم; ) ARABIC LIGATURE TEH WITH MEEM FINAL FORM +FC73;FC73;FC73;062A 0646;062A 0646; # (ﱳ; ﱳ; ﱳ; تن; تن; ) ARABIC LIGATURE TEH WITH NOON FINAL FORM +FC74;FC74;FC74;062A 0649;062A 0649; # (ﱴ; ﱴ; ﱴ; تى; تى; ) ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM +FC75;FC75;FC75;062A 064A;062A 064A; # (ﱵ; ﱵ; ﱵ; تي; تي; ) ARABIC LIGATURE TEH WITH YEH FINAL FORM +FC76;FC76;FC76;062B 0631;062B 0631; # (ﱶ; ﱶ; ﱶ; ثر; ثر; ) ARABIC LIGATURE THEH WITH REH FINAL FORM +FC77;FC77;FC77;062B 0632;062B 0632; # (ﱷ; ﱷ; ﱷ; ثز; ثز; ) ARABIC LIGATURE THEH WITH ZAIN FINAL FORM +FC78;FC78;FC78;062B 0645;062B 0645; # (ﱸ; ﱸ; ﱸ; ثم; ثم; ) ARABIC LIGATURE THEH WITH MEEM FINAL FORM +FC79;FC79;FC79;062B 0646;062B 0646; # (ﱹ; ﱹ; ﱹ; ثن; ثن; ) ARABIC LIGATURE THEH WITH NOON FINAL FORM +FC7A;FC7A;FC7A;062B 0649;062B 0649; # (ﱺ; ﱺ; ﱺ; ثى; ثى; ) ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM +FC7B;FC7B;FC7B;062B 064A;062B 064A; # (ﱻ; ﱻ; ﱻ; ثي; ثي; ) ARABIC LIGATURE THEH WITH YEH FINAL FORM +FC7C;FC7C;FC7C;0641 0649;0641 0649; # (ﱼ; ﱼ; ﱼ; فى; فى; ) ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM +FC7D;FC7D;FC7D;0641 064A;0641 064A; # (ﱽ; ﱽ; ﱽ; في; في; ) ARABIC LIGATURE FEH WITH YEH FINAL FORM +FC7E;FC7E;FC7E;0642 0649;0642 0649; # (ﱾ; ﱾ; ﱾ; قى; قى; ) ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM +FC7F;FC7F;FC7F;0642 064A;0642 064A; # (ﱿ; ﱿ; ﱿ; قي; قي; ) ARABIC LIGATURE QAF WITH YEH FINAL FORM +FC80;FC80;FC80;0643 0627;0643 0627; # (ﲀ; ﲀ; ﲀ; كا; كا; ) ARABIC LIGATURE KAF WITH ALEF FINAL FORM +FC81;FC81;FC81;0643 0644;0643 0644; # (ﲁ; ﲁ; ﲁ; كل; كل; ) ARABIC LIGATURE KAF WITH LAM FINAL FORM +FC82;FC82;FC82;0643 0645;0643 0645; # (ﲂ; ﲂ; ﲂ; كم; كم; ) ARABIC LIGATURE KAF WITH MEEM FINAL FORM +FC83;FC83;FC83;0643 0649;0643 0649; # (ﲃ; ﲃ; ﲃ; كى; كى; ) ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM +FC84;FC84;FC84;0643 064A;0643 064A; # (ﲄ; ﲄ; ﲄ; كي; كي; ) ARABIC LIGATURE KAF WITH YEH FINAL FORM +FC85;FC85;FC85;0644 0645;0644 0645; # (ﲅ; ﲅ; ﲅ; لم; لم; ) ARABIC LIGATURE LAM WITH MEEM FINAL FORM +FC86;FC86;FC86;0644 0649;0644 0649; # (ﲆ; ﲆ; ﲆ; لى; لى; ) ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM +FC87;FC87;FC87;0644 064A;0644 064A; # (ﲇ; ﲇ; ﲇ; لي; لي; ) ARABIC LIGATURE LAM WITH YEH FINAL FORM +FC88;FC88;FC88;0645 0627;0645 0627; # (ﲈ; ﲈ; ﲈ; ما; ما; ) ARABIC LIGATURE MEEM WITH ALEF FINAL FORM +FC89;FC89;FC89;0645 0645;0645 0645; # (ﲉ; ﲉ; ﲉ; مم; مم; ) ARABIC LIGATURE MEEM WITH MEEM FINAL FORM +FC8A;FC8A;FC8A;0646 0631;0646 0631; # (ﲊ; ﲊ; ﲊ; نر; نر; ) ARABIC LIGATURE NOON WITH REH FINAL FORM +FC8B;FC8B;FC8B;0646 0632;0646 0632; # (ﲋ; ﲋ; ﲋ; نز; نز; ) ARABIC LIGATURE NOON WITH ZAIN FINAL FORM +FC8C;FC8C;FC8C;0646 0645;0646 0645; # (ﲌ; ﲌ; ﲌ; نم; نم; ) ARABIC LIGATURE NOON WITH MEEM FINAL FORM +FC8D;FC8D;FC8D;0646 0646;0646 0646; # (ﲍ; ﲍ; ﲍ; نن; نن; ) ARABIC LIGATURE NOON WITH NOON FINAL FORM +FC8E;FC8E;FC8E;0646 0649;0646 0649; # (ﲎ; ﲎ; ﲎ; نى; نى; ) ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM +FC8F;FC8F;FC8F;0646 064A;0646 064A; # (ﲏ; ﲏ; ﲏ; ني; ني; ) ARABIC LIGATURE NOON WITH YEH FINAL FORM +FC90;FC90;FC90;0649 0670;0649 0670; # (ﲐ; ﲐ; ﲐ; ى◌ٰ; ى◌ٰ; ) ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM +FC91;FC91;FC91;064A 0631;064A 0631; # (ﲑ; ﲑ; ﲑ; ير; ير; ) ARABIC LIGATURE YEH WITH REH FINAL FORM +FC92;FC92;FC92;064A 0632;064A 0632; # (ﲒ; ﲒ; ﲒ; يز; يز; ) ARABIC LIGATURE YEH WITH ZAIN FINAL FORM +FC93;FC93;FC93;064A 0645;064A 0645; # (ﲓ; ﲓ; ﲓ; يم; يم; ) ARABIC LIGATURE YEH WITH MEEM FINAL FORM +FC94;FC94;FC94;064A 0646;064A 0646; # (ﲔ; ﲔ; ﲔ; ين; ين; ) ARABIC LIGATURE YEH WITH NOON FINAL FORM +FC95;FC95;FC95;064A 0649;064A 0649; # (ﲕ; ﲕ; ﲕ; يى; يى; ) ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM +FC96;FC96;FC96;064A 064A;064A 064A; # (ﲖ; ﲖ; ﲖ; يي; يي; ) ARABIC LIGATURE YEH WITH YEH FINAL FORM +FC97;FC97;FC97;0626 062C;064A 0654 062C; # (ﲗ; ﲗ; ﲗ; ئج; ي◌ٔج; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM +FC98;FC98;FC98;0626 062D;064A 0654 062D; # (ﲘ; ﲘ; ﲘ; ئح; ي◌ٔح; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM +FC99;FC99;FC99;0626 062E;064A 0654 062E; # (ﲙ; ﲙ; ﲙ; ئخ; ي◌ٔخ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM +FC9A;FC9A;FC9A;0626 0645;064A 0654 0645; # (ﲚ; ﲚ; ﲚ; ئم; ي◌ٔم; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM +FC9B;FC9B;FC9B;0626 0647;064A 0654 0647; # (ﲛ; ﲛ; ﲛ; ئه; ي◌ٔه; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM +FC9C;FC9C;FC9C;0628 062C;0628 062C; # (ﲜ; ﲜ; ﲜ; بج; بج; ) ARABIC LIGATURE BEH WITH JEEM INITIAL FORM +FC9D;FC9D;FC9D;0628 062D;0628 062D; # (ﲝ; ﲝ; ﲝ; بح; بح; ) ARABIC LIGATURE BEH WITH HAH INITIAL FORM +FC9E;FC9E;FC9E;0628 062E;0628 062E; # (ﲞ; ﲞ; ﲞ; بخ; بخ; ) ARABIC LIGATURE BEH WITH KHAH INITIAL FORM +FC9F;FC9F;FC9F;0628 0645;0628 0645; # (ﲟ; ﲟ; ﲟ; بم; بم; ) ARABIC LIGATURE BEH WITH MEEM INITIAL FORM +FCA0;FCA0;FCA0;0628 0647;0628 0647; # (ﲠ; ﲠ; ﲠ; به; به; ) ARABIC LIGATURE BEH WITH HEH INITIAL FORM +FCA1;FCA1;FCA1;062A 062C;062A 062C; # (ﲡ; ﲡ; ﲡ; تج; تج; ) ARABIC LIGATURE TEH WITH JEEM INITIAL FORM +FCA2;FCA2;FCA2;062A 062D;062A 062D; # (ﲢ; ﲢ; ﲢ; تح; تح; ) ARABIC LIGATURE TEH WITH HAH INITIAL FORM +FCA3;FCA3;FCA3;062A 062E;062A 062E; # (ﲣ; ﲣ; ﲣ; تخ; تخ; ) ARABIC LIGATURE TEH WITH KHAH INITIAL FORM +FCA4;FCA4;FCA4;062A 0645;062A 0645; # (ﲤ; ﲤ; ﲤ; تم; تم; ) ARABIC LIGATURE TEH WITH MEEM INITIAL FORM +FCA5;FCA5;FCA5;062A 0647;062A 0647; # (ﲥ; ﲥ; ﲥ; ته; ته; ) ARABIC LIGATURE TEH WITH HEH INITIAL FORM +FCA6;FCA6;FCA6;062B 0645;062B 0645; # (ﲦ; ﲦ; ﲦ; ثم; ثم; ) ARABIC LIGATURE THEH WITH MEEM INITIAL FORM +FCA7;FCA7;FCA7;062C 062D;062C 062D; # (ﲧ; ﲧ; ﲧ; جح; جح; ) ARABIC LIGATURE JEEM WITH HAH INITIAL FORM +FCA8;FCA8;FCA8;062C 0645;062C 0645; # (ﲨ; ﲨ; ﲨ; جم; جم; ) ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM +FCA9;FCA9;FCA9;062D 062C;062D 062C; # (ﲩ; ﲩ; ﲩ; حج; حج; ) ARABIC LIGATURE HAH WITH JEEM INITIAL FORM +FCAA;FCAA;FCAA;062D 0645;062D 0645; # (ﲪ; ﲪ; ﲪ; حم; حم; ) ARABIC LIGATURE HAH WITH MEEM INITIAL FORM +FCAB;FCAB;FCAB;062E 062C;062E 062C; # (ﲫ; ﲫ; ﲫ; خج; خج; ) ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM +FCAC;FCAC;FCAC;062E 0645;062E 0645; # (ﲬ; ﲬ; ﲬ; خم; خم; ) ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM +FCAD;FCAD;FCAD;0633 062C;0633 062C; # (ﲭ; ﲭ; ﲭ; سج; سج; ) ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM +FCAE;FCAE;FCAE;0633 062D;0633 062D; # (ﲮ; ﲮ; ﲮ; سح; سح; ) ARABIC LIGATURE SEEN WITH HAH INITIAL FORM +FCAF;FCAF;FCAF;0633 062E;0633 062E; # (ﲯ; ﲯ; ﲯ; سخ; سخ; ) ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM +FCB0;FCB0;FCB0;0633 0645;0633 0645; # (ﲰ; ﲰ; ﲰ; سم; سم; ) ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM +FCB1;FCB1;FCB1;0635 062D;0635 062D; # (ﲱ; ﲱ; ﲱ; صح; صح; ) ARABIC LIGATURE SAD WITH HAH INITIAL FORM +FCB2;FCB2;FCB2;0635 062E;0635 062E; # (ﲲ; ﲲ; ﲲ; صخ; صخ; ) ARABIC LIGATURE SAD WITH KHAH INITIAL FORM +FCB3;FCB3;FCB3;0635 0645;0635 0645; # (ﲳ; ﲳ; ﲳ; صم; صم; ) ARABIC LIGATURE SAD WITH MEEM INITIAL FORM +FCB4;FCB4;FCB4;0636 062C;0636 062C; # (ﲴ; ﲴ; ﲴ; ضج; ضج; ) ARABIC LIGATURE DAD WITH JEEM INITIAL FORM +FCB5;FCB5;FCB5;0636 062D;0636 062D; # (ﲵ; ﲵ; ﲵ; ضح; ضح; ) ARABIC LIGATURE DAD WITH HAH INITIAL FORM +FCB6;FCB6;FCB6;0636 062E;0636 062E; # (ﲶ; ﲶ; ﲶ; ضخ; ضخ; ) ARABIC LIGATURE DAD WITH KHAH INITIAL FORM +FCB7;FCB7;FCB7;0636 0645;0636 0645; # (ﲷ; ﲷ; ﲷ; ضم; ضم; ) ARABIC LIGATURE DAD WITH MEEM INITIAL FORM +FCB8;FCB8;FCB8;0637 062D;0637 062D; # (ﲸ; ﲸ; ﲸ; طح; طح; ) ARABIC LIGATURE TAH WITH HAH INITIAL FORM +FCB9;FCB9;FCB9;0638 0645;0638 0645; # (ﲹ; ﲹ; ﲹ; ظم; ظم; ) ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM +FCBA;FCBA;FCBA;0639 062C;0639 062C; # (ﲺ; ﲺ; ﲺ; عج; عج; ) ARABIC LIGATURE AIN WITH JEEM INITIAL FORM +FCBB;FCBB;FCBB;0639 0645;0639 0645; # (ﲻ; ﲻ; ﲻ; عم; عم; ) ARABIC LIGATURE AIN WITH MEEM INITIAL FORM +FCBC;FCBC;FCBC;063A 062C;063A 062C; # (ﲼ; ﲼ; ﲼ; غج; غج; ) ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM +FCBD;FCBD;FCBD;063A 0645;063A 0645; # (ﲽ; ﲽ; ﲽ; غم; غم; ) ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM +FCBE;FCBE;FCBE;0641 062C;0641 062C; # (ﲾ; ﲾ; ﲾ; فج; فج; ) ARABIC LIGATURE FEH WITH JEEM INITIAL FORM +FCBF;FCBF;FCBF;0641 062D;0641 062D; # (ﲿ; ﲿ; ﲿ; فح; فح; ) ARABIC LIGATURE FEH WITH HAH INITIAL FORM +FCC0;FCC0;FCC0;0641 062E;0641 062E; # (ﳀ; ﳀ; ﳀ; فخ; فخ; ) ARABIC LIGATURE FEH WITH KHAH INITIAL FORM +FCC1;FCC1;FCC1;0641 0645;0641 0645; # (ﳁ; ﳁ; ﳁ; فم; فم; ) ARABIC LIGATURE FEH WITH MEEM INITIAL FORM +FCC2;FCC2;FCC2;0642 062D;0642 062D; # (ﳂ; ﳂ; ﳂ; قح; قح; ) ARABIC LIGATURE QAF WITH HAH INITIAL FORM +FCC3;FCC3;FCC3;0642 0645;0642 0645; # (ﳃ; ﳃ; ﳃ; قم; قم; ) ARABIC LIGATURE QAF WITH MEEM INITIAL FORM +FCC4;FCC4;FCC4;0643 062C;0643 062C; # (ﳄ; ﳄ; ﳄ; كج; كج; ) ARABIC LIGATURE KAF WITH JEEM INITIAL FORM +FCC5;FCC5;FCC5;0643 062D;0643 062D; # (ﳅ; ﳅ; ﳅ; كح; كح; ) ARABIC LIGATURE KAF WITH HAH INITIAL FORM +FCC6;FCC6;FCC6;0643 062E;0643 062E; # (ﳆ; ﳆ; ﳆ; كخ; كخ; ) ARABIC LIGATURE KAF WITH KHAH INITIAL FORM +FCC7;FCC7;FCC7;0643 0644;0643 0644; # (ﳇ; ﳇ; ﳇ; كل; كل; ) ARABIC LIGATURE KAF WITH LAM INITIAL FORM +FCC8;FCC8;FCC8;0643 0645;0643 0645; # (ﳈ; ﳈ; ﳈ; كم; كم; ) ARABIC LIGATURE KAF WITH MEEM INITIAL FORM +FCC9;FCC9;FCC9;0644 062C;0644 062C; # (ﳉ; ﳉ; ﳉ; لج; لج; ) ARABIC LIGATURE LAM WITH JEEM INITIAL FORM +FCCA;FCCA;FCCA;0644 062D;0644 062D; # (ﳊ; ﳊ; ﳊ; لح; لح; ) ARABIC LIGATURE LAM WITH HAH INITIAL FORM +FCCB;FCCB;FCCB;0644 062E;0644 062E; # (ﳋ; ﳋ; ﳋ; لخ; لخ; ) ARABIC LIGATURE LAM WITH KHAH INITIAL FORM +FCCC;FCCC;FCCC;0644 0645;0644 0645; # (ﳌ; ﳌ; ﳌ; لم; لم; ) ARABIC LIGATURE LAM WITH MEEM INITIAL FORM +FCCD;FCCD;FCCD;0644 0647;0644 0647; # (ﳍ; ﳍ; ﳍ; له; له; ) ARABIC LIGATURE LAM WITH HEH INITIAL FORM +FCCE;FCCE;FCCE;0645 062C;0645 062C; # (ﳎ; ﳎ; ﳎ; مج; مج; ) ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM +FCCF;FCCF;FCCF;0645 062D;0645 062D; # (ﳏ; ﳏ; ﳏ; مح; مح; ) ARABIC LIGATURE MEEM WITH HAH INITIAL FORM +FCD0;FCD0;FCD0;0645 062E;0645 062E; # (ﳐ; ﳐ; ﳐ; مخ; مخ; ) ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM +FCD1;FCD1;FCD1;0645 0645;0645 0645; # (ﳑ; ﳑ; ﳑ; مم; مم; ) ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM +FCD2;FCD2;FCD2;0646 062C;0646 062C; # (ﳒ; ﳒ; ﳒ; نج; نج; ) ARABIC LIGATURE NOON WITH JEEM INITIAL FORM +FCD3;FCD3;FCD3;0646 062D;0646 062D; # (ﳓ; ﳓ; ﳓ; نح; نح; ) ARABIC LIGATURE NOON WITH HAH INITIAL FORM +FCD4;FCD4;FCD4;0646 062E;0646 062E; # (ﳔ; ﳔ; ﳔ; نخ; نخ; ) ARABIC LIGATURE NOON WITH KHAH INITIAL FORM +FCD5;FCD5;FCD5;0646 0645;0646 0645; # (ﳕ; ﳕ; ﳕ; نم; نم; ) ARABIC LIGATURE NOON WITH MEEM INITIAL FORM +FCD6;FCD6;FCD6;0646 0647;0646 0647; # (ﳖ; ﳖ; ﳖ; نه; نه; ) ARABIC LIGATURE NOON WITH HEH INITIAL FORM +FCD7;FCD7;FCD7;0647 062C;0647 062C; # (ﳗ; ﳗ; ﳗ; هج; هج; ) ARABIC LIGATURE HEH WITH JEEM INITIAL FORM +FCD8;FCD8;FCD8;0647 0645;0647 0645; # (ﳘ; ﳘ; ﳘ; هم; هم; ) ARABIC LIGATURE HEH WITH MEEM INITIAL FORM +FCD9;FCD9;FCD9;0647 0670;0647 0670; # (ﳙ; ﳙ; ﳙ; ه◌ٰ; ه◌ٰ; ) ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM +FCDA;FCDA;FCDA;064A 062C;064A 062C; # (ﳚ; ﳚ; ﳚ; يج; يج; ) ARABIC LIGATURE YEH WITH JEEM INITIAL FORM +FCDB;FCDB;FCDB;064A 062D;064A 062D; # (ﳛ; ﳛ; ﳛ; يح; يح; ) ARABIC LIGATURE YEH WITH HAH INITIAL FORM +FCDC;FCDC;FCDC;064A 062E;064A 062E; # (ﳜ; ﳜ; ﳜ; يخ; يخ; ) ARABIC LIGATURE YEH WITH KHAH INITIAL FORM +FCDD;FCDD;FCDD;064A 0645;064A 0645; # (ﳝ; ﳝ; ﳝ; يم; يم; ) ARABIC LIGATURE YEH WITH MEEM INITIAL FORM +FCDE;FCDE;FCDE;064A 0647;064A 0647; # (ﳞ; ﳞ; ﳞ; يه; يه; ) ARABIC LIGATURE YEH WITH HEH INITIAL FORM +FCDF;FCDF;FCDF;0626 0645;064A 0654 0645; # (ﳟ; ﳟ; ﳟ; ئم; ي◌ٔم; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM +FCE0;FCE0;FCE0;0626 0647;064A 0654 0647; # (ﳠ; ﳠ; ﳠ; ئه; ي◌ٔه; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM +FCE1;FCE1;FCE1;0628 0645;0628 0645; # (ﳡ; ﳡ; ﳡ; بم; بم; ) ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM +FCE2;FCE2;FCE2;0628 0647;0628 0647; # (ﳢ; ﳢ; ﳢ; به; به; ) ARABIC LIGATURE BEH WITH HEH MEDIAL FORM +FCE3;FCE3;FCE3;062A 0645;062A 0645; # (ﳣ; ﳣ; ﳣ; تم; تم; ) ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM +FCE4;FCE4;FCE4;062A 0647;062A 0647; # (ﳤ; ﳤ; ﳤ; ته; ته; ) ARABIC LIGATURE TEH WITH HEH MEDIAL FORM +FCE5;FCE5;FCE5;062B 0645;062B 0645; # (ﳥ; ﳥ; ﳥ; ثم; ثم; ) ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM +FCE6;FCE6;FCE6;062B 0647;062B 0647; # (ﳦ; ﳦ; ﳦ; ثه; ثه; ) ARABIC LIGATURE THEH WITH HEH MEDIAL FORM +FCE7;FCE7;FCE7;0633 0645;0633 0645; # (ﳧ; ﳧ; ﳧ; سم; سم; ) ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM +FCE8;FCE8;FCE8;0633 0647;0633 0647; # (ﳨ; ﳨ; ﳨ; سه; سه; ) ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM +FCE9;FCE9;FCE9;0634 0645;0634 0645; # (ﳩ; ﳩ; ﳩ; شم; شم; ) ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM +FCEA;FCEA;FCEA;0634 0647;0634 0647; # (ﳪ; ﳪ; ﳪ; شه; شه; ) ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM +FCEB;FCEB;FCEB;0643 0644;0643 0644; # (ﳫ; ﳫ; ﳫ; كل; كل; ) ARABIC LIGATURE KAF WITH LAM MEDIAL FORM +FCEC;FCEC;FCEC;0643 0645;0643 0645; # (ﳬ; ﳬ; ﳬ; كم; كم; ) ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM +FCED;FCED;FCED;0644 0645;0644 0645; # (ﳭ; ﳭ; ﳭ; لم; لم; ) ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM +FCEE;FCEE;FCEE;0646 0645;0646 0645; # (ﳮ; ﳮ; ﳮ; نم; نم; ) ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM +FCEF;FCEF;FCEF;0646 0647;0646 0647; # (ﳯ; ﳯ; ﳯ; نه; نه; ) ARABIC LIGATURE NOON WITH HEH MEDIAL FORM +FCF0;FCF0;FCF0;064A 0645;064A 0645; # (ﳰ; ﳰ; ﳰ; يم; يم; ) ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM +FCF1;FCF1;FCF1;064A 0647;064A 0647; # (ﳱ; ﳱ; ﳱ; يه; يه; ) ARABIC LIGATURE YEH WITH HEH MEDIAL FORM +FCF2;FCF2;FCF2;0640 064E 0651;0640 064E 0651; # (ﳲ; ﳲ; ﳲ; ـ◌َ◌ّ; ـ◌َ◌ّ; ) ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM +FCF3;FCF3;FCF3;0640 064F 0651;0640 064F 0651; # (ﳳ; ﳳ; ﳳ; ـ◌ُ◌ّ; ـ◌ُ◌ّ; ) ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM +FCF4;FCF4;FCF4;0640 0650 0651;0640 0650 0651; # (ﳴ; ﳴ; ﳴ; ـ◌ِ◌ّ; ـ◌ِ◌ّ; ) ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM +FCF5;FCF5;FCF5;0637 0649;0637 0649; # (ﳵ; ﳵ; ﳵ; طى; طى; ) ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM +FCF6;FCF6;FCF6;0637 064A;0637 064A; # (ﳶ; ﳶ; ﳶ; طي; طي; ) ARABIC LIGATURE TAH WITH YEH ISOLATED FORM +FCF7;FCF7;FCF7;0639 0649;0639 0649; # (ﳷ; ﳷ; ﳷ; عى; عى; ) ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM +FCF8;FCF8;FCF8;0639 064A;0639 064A; # (ﳸ; ﳸ; ﳸ; عي; عي; ) ARABIC LIGATURE AIN WITH YEH ISOLATED FORM +FCF9;FCF9;FCF9;063A 0649;063A 0649; # (ﳹ; ﳹ; ﳹ; غى; غى; ) ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM +FCFA;FCFA;FCFA;063A 064A;063A 064A; # (ﳺ; ﳺ; ﳺ; غي; غي; ) ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM +FCFB;FCFB;FCFB;0633 0649;0633 0649; # (ﳻ; ﳻ; ﳻ; سى; سى; ) ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM +FCFC;FCFC;FCFC;0633 064A;0633 064A; # (ﳼ; ﳼ; ﳼ; سي; سي; ) ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM +FCFD;FCFD;FCFD;0634 0649;0634 0649; # (ﳽ; ﳽ; ﳽ; شى; شى; ) ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM +FCFE;FCFE;FCFE;0634 064A;0634 064A; # (ﳾ; ﳾ; ﳾ; شي; شي; ) ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM +FCFF;FCFF;FCFF;062D 0649;062D 0649; # (ﳿ; ﳿ; ﳿ; حى; حى; ) ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM +FD00;FD00;FD00;062D 064A;062D 064A; # (ﴀ; ﴀ; ﴀ; حي; حي; ) ARABIC LIGATURE HAH WITH YEH ISOLATED FORM +FD01;FD01;FD01;062C 0649;062C 0649; # (ﴁ; ﴁ; ﴁ; جى; جى; ) ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM +FD02;FD02;FD02;062C 064A;062C 064A; # (ﴂ; ﴂ; ﴂ; جي; جي; ) ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM +FD03;FD03;FD03;062E 0649;062E 0649; # (ﴃ; ﴃ; ﴃ; خى; خى; ) ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM +FD04;FD04;FD04;062E 064A;062E 064A; # (ﴄ; ﴄ; ﴄ; خي; خي; ) ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM +FD05;FD05;FD05;0635 0649;0635 0649; # (ﴅ; ﴅ; ﴅ; صى; صى; ) ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM +FD06;FD06;FD06;0635 064A;0635 064A; # (ﴆ; ﴆ; ﴆ; صي; صي; ) ARABIC LIGATURE SAD WITH YEH ISOLATED FORM +FD07;FD07;FD07;0636 0649;0636 0649; # (ﴇ; ﴇ; ﴇ; ضى; ضى; ) ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM +FD08;FD08;FD08;0636 064A;0636 064A; # (ﴈ; ﴈ; ﴈ; ضي; ضي; ) ARABIC LIGATURE DAD WITH YEH ISOLATED FORM +FD09;FD09;FD09;0634 062C;0634 062C; # (ﴉ; ﴉ; ﴉ; شج; شج; ) ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM +FD0A;FD0A;FD0A;0634 062D;0634 062D; # (ﴊ; ﴊ; ﴊ; شح; شح; ) ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM +FD0B;FD0B;FD0B;0634 062E;0634 062E; # (ﴋ; ﴋ; ﴋ; شخ; شخ; ) ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM +FD0C;FD0C;FD0C;0634 0645;0634 0645; # (ﴌ; ﴌ; ﴌ; شم; شم; ) ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM +FD0D;FD0D;FD0D;0634 0631;0634 0631; # (ﴍ; ﴍ; ﴍ; شر; شر; ) ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM +FD0E;FD0E;FD0E;0633 0631;0633 0631; # (ﴎ; ﴎ; ﴎ; سر; سر; ) ARABIC LIGATURE SEEN WITH REH ISOLATED FORM +FD0F;FD0F;FD0F;0635 0631;0635 0631; # (ﴏ; ﴏ; ﴏ; صر; صر; ) ARABIC LIGATURE SAD WITH REH ISOLATED FORM +FD10;FD10;FD10;0636 0631;0636 0631; # (ﴐ; ﴐ; ﴐ; ضر; ضر; ) ARABIC LIGATURE DAD WITH REH ISOLATED FORM +FD11;FD11;FD11;0637 0649;0637 0649; # (ﴑ; ﴑ; ﴑ; طى; طى; ) ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM +FD12;FD12;FD12;0637 064A;0637 064A; # (ﴒ; ﴒ; ﴒ; طي; طي; ) ARABIC LIGATURE TAH WITH YEH FINAL FORM +FD13;FD13;FD13;0639 0649;0639 0649; # (ﴓ; ﴓ; ﴓ; عى; عى; ) ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM +FD14;FD14;FD14;0639 064A;0639 064A; # (ﴔ; ﴔ; ﴔ; عي; عي; ) ARABIC LIGATURE AIN WITH YEH FINAL FORM +FD15;FD15;FD15;063A 0649;063A 0649; # (ﴕ; ﴕ; ﴕ; غى; غى; ) ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM +FD16;FD16;FD16;063A 064A;063A 064A; # (ﴖ; ﴖ; ﴖ; غي; غي; ) ARABIC LIGATURE GHAIN WITH YEH FINAL FORM +FD17;FD17;FD17;0633 0649;0633 0649; # (ﴗ; ﴗ; ﴗ; سى; سى; ) ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM +FD18;FD18;FD18;0633 064A;0633 064A; # (ﴘ; ﴘ; ﴘ; سي; سي; ) ARABIC LIGATURE SEEN WITH YEH FINAL FORM +FD19;FD19;FD19;0634 0649;0634 0649; # (ﴙ; ﴙ; ﴙ; شى; شى; ) ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM +FD1A;FD1A;FD1A;0634 064A;0634 064A; # (ﴚ; ﴚ; ﴚ; شي; شي; ) ARABIC LIGATURE SHEEN WITH YEH FINAL FORM +FD1B;FD1B;FD1B;062D 0649;062D 0649; # (ﴛ; ﴛ; ﴛ; حى; حى; ) ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM +FD1C;FD1C;FD1C;062D 064A;062D 064A; # (ﴜ; ﴜ; ﴜ; حي; حي; ) ARABIC LIGATURE HAH WITH YEH FINAL FORM +FD1D;FD1D;FD1D;062C 0649;062C 0649; # (ﴝ; ﴝ; ﴝ; جى; جى; ) ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM +FD1E;FD1E;FD1E;062C 064A;062C 064A; # (ﴞ; ﴞ; ﴞ; جي; جي; ) ARABIC LIGATURE JEEM WITH YEH FINAL FORM +FD1F;FD1F;FD1F;062E 0649;062E 0649; # (ﴟ; ﴟ; ﴟ; خى; خى; ) ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM +FD20;FD20;FD20;062E 064A;062E 064A; # (ﴠ; ﴠ; ﴠ; خي; خي; ) ARABIC LIGATURE KHAH WITH YEH FINAL FORM +FD21;FD21;FD21;0635 0649;0635 0649; # (ﴡ; ﴡ; ﴡ; صى; صى; ) ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM +FD22;FD22;FD22;0635 064A;0635 064A; # (ﴢ; ﴢ; ﴢ; صي; صي; ) ARABIC LIGATURE SAD WITH YEH FINAL FORM +FD23;FD23;FD23;0636 0649;0636 0649; # (ﴣ; ﴣ; ﴣ; ضى; ضى; ) ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM +FD24;FD24;FD24;0636 064A;0636 064A; # (ﴤ; ﴤ; ﴤ; ضي; ضي; ) ARABIC LIGATURE DAD WITH YEH FINAL FORM +FD25;FD25;FD25;0634 062C;0634 062C; # (ﴥ; ﴥ; ﴥ; شج; شج; ) ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM +FD26;FD26;FD26;0634 062D;0634 062D; # (ﴦ; ﴦ; ﴦ; شح; شح; ) ARABIC LIGATURE SHEEN WITH HAH FINAL FORM +FD27;FD27;FD27;0634 062E;0634 062E; # (ﴧ; ﴧ; ﴧ; شخ; شخ; ) ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM +FD28;FD28;FD28;0634 0645;0634 0645; # (ﴨ; ﴨ; ﴨ; شم; شم; ) ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM +FD29;FD29;FD29;0634 0631;0634 0631; # (ﴩ; ﴩ; ﴩ; شر; شر; ) ARABIC LIGATURE SHEEN WITH REH FINAL FORM +FD2A;FD2A;FD2A;0633 0631;0633 0631; # (ﴪ; ﴪ; ﴪ; سر; سر; ) ARABIC LIGATURE SEEN WITH REH FINAL FORM +FD2B;FD2B;FD2B;0635 0631;0635 0631; # (ﴫ; ﴫ; ﴫ; صر; صر; ) ARABIC LIGATURE SAD WITH REH FINAL FORM +FD2C;FD2C;FD2C;0636 0631;0636 0631; # (ﴬ; ﴬ; ﴬ; ضر; ضر; ) ARABIC LIGATURE DAD WITH REH FINAL FORM +FD2D;FD2D;FD2D;0634 062C;0634 062C; # (ﴭ; ﴭ; ﴭ; شج; شج; ) ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM +FD2E;FD2E;FD2E;0634 062D;0634 062D; # (ﴮ; ﴮ; ﴮ; شح; شح; ) ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM +FD2F;FD2F;FD2F;0634 062E;0634 062E; # (ﴯ; ﴯ; ﴯ; شخ; شخ; ) ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM +FD30;FD30;FD30;0634 0645;0634 0645; # (ﴰ; ﴰ; ﴰ; شم; شم; ) ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM +FD31;FD31;FD31;0633 0647;0633 0647; # (ﴱ; ﴱ; ﴱ; سه; سه; ) ARABIC LIGATURE SEEN WITH HEH INITIAL FORM +FD32;FD32;FD32;0634 0647;0634 0647; # (ﴲ; ﴲ; ﴲ; شه; شه; ) ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM +FD33;FD33;FD33;0637 0645;0637 0645; # (ﴳ; ﴳ; ﴳ; طم; طم; ) ARABIC LIGATURE TAH WITH MEEM INITIAL FORM +FD34;FD34;FD34;0633 062C;0633 062C; # (ﴴ; ﴴ; ﴴ; سج; سج; ) ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM +FD35;FD35;FD35;0633 062D;0633 062D; # (ﴵ; ﴵ; ﴵ; سح; سح; ) ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM +FD36;FD36;FD36;0633 062E;0633 062E; # (ﴶ; ﴶ; ﴶ; سخ; سخ; ) ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM +FD37;FD37;FD37;0634 062C;0634 062C; # (ﴷ; ﴷ; ﴷ; شج; شج; ) ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM +FD38;FD38;FD38;0634 062D;0634 062D; # (ﴸ; ﴸ; ﴸ; شح; شح; ) ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM +FD39;FD39;FD39;0634 062E;0634 062E; # (ﴹ; ﴹ; ﴹ; شخ; شخ; ) ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM +FD3A;FD3A;FD3A;0637 0645;0637 0645; # (ﴺ; ﴺ; ﴺ; طم; طم; ) ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM +FD3B;FD3B;FD3B;0638 0645;0638 0645; # (ﴻ; ﴻ; ﴻ; ظم; ظم; ) ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM +FD3C;FD3C;FD3C;0627 064B;0627 064B; # (ﴼ; ﴼ; ﴼ; ا◌ً; ا◌ً; ) ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM +FD3D;FD3D;FD3D;0627 064B;0627 064B; # (ﴽ; ﴽ; ﴽ; ا◌ً; ا◌ً; ) ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM +FD50;FD50;FD50;062A 062C 0645;062A 062C 0645; # (ﵐ; ﵐ; ﵐ; تجم; تجم; ) ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM +FD51;FD51;FD51;062A 062D 062C;062A 062D 062C; # (ﵑ; ﵑ; ﵑ; تحج; تحج; ) ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM +FD52;FD52;FD52;062A 062D 062C;062A 062D 062C; # (ﵒ; ﵒ; ﵒ; تحج; تحج; ) ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM +FD53;FD53;FD53;062A 062D 0645;062A 062D 0645; # (ﵓ; ﵓ; ﵓ; تحم; تحم; ) ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM +FD54;FD54;FD54;062A 062E 0645;062A 062E 0645; # (ﵔ; ﵔ; ﵔ; تخم; تخم; ) ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM +FD55;FD55;FD55;062A 0645 062C;062A 0645 062C; # (ﵕ; ﵕ; ﵕ; تمج; تمج; ) ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM +FD56;FD56;FD56;062A 0645 062D;062A 0645 062D; # (ﵖ; ﵖ; ﵖ; تمح; تمح; ) ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM +FD57;FD57;FD57;062A 0645 062E;062A 0645 062E; # (ﵗ; ﵗ; ﵗ; تمخ; تمخ; ) ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM +FD58;FD58;FD58;062C 0645 062D;062C 0645 062D; # (ﵘ; ﵘ; ﵘ; جمح; جمح; ) ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM +FD59;FD59;FD59;062C 0645 062D;062C 0645 062D; # (ﵙ; ﵙ; ﵙ; جمح; جمح; ) ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM +FD5A;FD5A;FD5A;062D 0645 064A;062D 0645 064A; # (ﵚ; ﵚ; ﵚ; حمي; حمي; ) ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM +FD5B;FD5B;FD5B;062D 0645 0649;062D 0645 0649; # (ﵛ; ﵛ; ﵛ; حمى; حمى; ) ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM +FD5C;FD5C;FD5C;0633 062D 062C;0633 062D 062C; # (ﵜ; ﵜ; ﵜ; سحج; سحج; ) ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM +FD5D;FD5D;FD5D;0633 062C 062D;0633 062C 062D; # (ﵝ; ﵝ; ﵝ; سجح; سجح; ) ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM +FD5E;FD5E;FD5E;0633 062C 0649;0633 062C 0649; # (ﵞ; ﵞ; ﵞ; سجى; سجى; ) ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM +FD5F;FD5F;FD5F;0633 0645 062D;0633 0645 062D; # (ﵟ; ﵟ; ﵟ; سمح; سمح; ) ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM +FD60;FD60;FD60;0633 0645 062D;0633 0645 062D; # (ﵠ; ﵠ; ﵠ; سمح; سمح; ) ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM +FD61;FD61;FD61;0633 0645 062C;0633 0645 062C; # (ﵡ; ﵡ; ﵡ; سمج; سمج; ) ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM +FD62;FD62;FD62;0633 0645 0645;0633 0645 0645; # (ﵢ; ﵢ; ﵢ; سمم; سمم; ) ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM +FD63;FD63;FD63;0633 0645 0645;0633 0645 0645; # (ﵣ; ﵣ; ﵣ; سمم; سمم; ) ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM +FD64;FD64;FD64;0635 062D 062D;0635 062D 062D; # (ﵤ; ﵤ; ﵤ; صحح; صحح; ) ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM +FD65;FD65;FD65;0635 062D 062D;0635 062D 062D; # (ﵥ; ﵥ; ﵥ; صحح; صحح; ) ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM +FD66;FD66;FD66;0635 0645 0645;0635 0645 0645; # (ﵦ; ﵦ; ﵦ; صمم; صمم; ) ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM +FD67;FD67;FD67;0634 062D 0645;0634 062D 0645; # (ﵧ; ﵧ; ﵧ; شحم; شحم; ) ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM +FD68;FD68;FD68;0634 062D 0645;0634 062D 0645; # (ﵨ; ﵨ; ﵨ; شحم; شحم; ) ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM +FD69;FD69;FD69;0634 062C 064A;0634 062C 064A; # (ﵩ; ﵩ; ﵩ; شجي; شجي; ) ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM +FD6A;FD6A;FD6A;0634 0645 062E;0634 0645 062E; # (ﵪ; ﵪ; ﵪ; شمخ; شمخ; ) ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM +FD6B;FD6B;FD6B;0634 0645 062E;0634 0645 062E; # (ﵫ; ﵫ; ﵫ; شمخ; شمخ; ) ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM +FD6C;FD6C;FD6C;0634 0645 0645;0634 0645 0645; # (ﵬ; ﵬ; ﵬ; شمم; شمم; ) ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM +FD6D;FD6D;FD6D;0634 0645 0645;0634 0645 0645; # (ﵭ; ﵭ; ﵭ; شمم; شمم; ) ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM +FD6E;FD6E;FD6E;0636 062D 0649;0636 062D 0649; # (ﵮ; ﵮ; ﵮ; ضحى; ضحى; ) ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM +FD6F;FD6F;FD6F;0636 062E 0645;0636 062E 0645; # (ﵯ; ﵯ; ﵯ; ضخم; ضخم; ) ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM +FD70;FD70;FD70;0636 062E 0645;0636 062E 0645; # (ﵰ; ﵰ; ﵰ; ضخم; ضخم; ) ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM +FD71;FD71;FD71;0637 0645 062D;0637 0645 062D; # (ﵱ; ﵱ; ﵱ; طمح; طمح; ) ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM +FD72;FD72;FD72;0637 0645 062D;0637 0645 062D; # (ﵲ; ﵲ; ﵲ; طمح; طمح; ) ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM +FD73;FD73;FD73;0637 0645 0645;0637 0645 0645; # (ﵳ; ﵳ; ﵳ; طمم; طمم; ) ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM +FD74;FD74;FD74;0637 0645 064A;0637 0645 064A; # (ﵴ; ﵴ; ﵴ; طمي; طمي; ) ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM +FD75;FD75;FD75;0639 062C 0645;0639 062C 0645; # (ﵵ; ﵵ; ﵵ; عجم; عجم; ) ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM +FD76;FD76;FD76;0639 0645 0645;0639 0645 0645; # (ﵶ; ﵶ; ﵶ; عمم; عمم; ) ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM +FD77;FD77;FD77;0639 0645 0645;0639 0645 0645; # (ﵷ; ﵷ; ﵷ; عمم; عمم; ) ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM +FD78;FD78;FD78;0639 0645 0649;0639 0645 0649; # (ﵸ; ﵸ; ﵸ; عمى; عمى; ) ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM +FD79;FD79;FD79;063A 0645 0645;063A 0645 0645; # (ﵹ; ﵹ; ﵹ; غمم; غمم; ) ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM +FD7A;FD7A;FD7A;063A 0645 064A;063A 0645 064A; # (ﵺ; ﵺ; ﵺ; غمي; غمي; ) ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM +FD7B;FD7B;FD7B;063A 0645 0649;063A 0645 0649; # (ﵻ; ﵻ; ﵻ; غمى; غمى; ) ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM +FD7C;FD7C;FD7C;0641 062E 0645;0641 062E 0645; # (ﵼ; ﵼ; ﵼ; فخم; فخم; ) ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM +FD7D;FD7D;FD7D;0641 062E 0645;0641 062E 0645; # (ﵽ; ﵽ; ﵽ; فخم; فخم; ) ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM +FD7E;FD7E;FD7E;0642 0645 062D;0642 0645 062D; # (ﵾ; ﵾ; ﵾ; قمح; قمح; ) ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM +FD7F;FD7F;FD7F;0642 0645 0645;0642 0645 0645; # (ﵿ; ﵿ; ﵿ; قمم; قمم; ) ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM +FD80;FD80;FD80;0644 062D 0645;0644 062D 0645; # (ﶀ; ﶀ; ﶀ; لحم; لحم; ) ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM +FD81;FD81;FD81;0644 062D 064A;0644 062D 064A; # (ﶁ; ﶁ; ﶁ; لحي; لحي; ) ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM +FD82;FD82;FD82;0644 062D 0649;0644 062D 0649; # (ﶂ; ﶂ; ﶂ; لحى; لحى; ) ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM +FD83;FD83;FD83;0644 062C 062C;0644 062C 062C; # (ﶃ; ﶃ; ﶃ; لجج; لجج; ) ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM +FD84;FD84;FD84;0644 062C 062C;0644 062C 062C; # (ﶄ; ﶄ; ﶄ; لجج; لجج; ) ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM +FD85;FD85;FD85;0644 062E 0645;0644 062E 0645; # (ﶅ; ﶅ; ﶅ; لخم; لخم; ) ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM +FD86;FD86;FD86;0644 062E 0645;0644 062E 0645; # (ﶆ; ﶆ; ﶆ; لخم; لخم; ) ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM +FD87;FD87;FD87;0644 0645 062D;0644 0645 062D; # (ﶇ; ﶇ; ﶇ; لمح; لمح; ) ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM +FD88;FD88;FD88;0644 0645 062D;0644 0645 062D; # (ﶈ; ﶈ; ﶈ; لمح; لمح; ) ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM +FD89;FD89;FD89;0645 062D 062C;0645 062D 062C; # (ﶉ; ﶉ; ﶉ; محج; محج; ) ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM +FD8A;FD8A;FD8A;0645 062D 0645;0645 062D 0645; # (ﶊ; ﶊ; ﶊ; محم; محم; ) ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM +FD8B;FD8B;FD8B;0645 062D 064A;0645 062D 064A; # (ﶋ; ﶋ; ﶋ; محي; محي; ) ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM +FD8C;FD8C;FD8C;0645 062C 062D;0645 062C 062D; # (ﶌ; ﶌ; ﶌ; مجح; مجح; ) ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM +FD8D;FD8D;FD8D;0645 062C 0645;0645 062C 0645; # (ﶍ; ﶍ; ﶍ; مجم; مجم; ) ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM +FD8E;FD8E;FD8E;0645 062E 062C;0645 062E 062C; # (ﶎ; ﶎ; ﶎ; مخج; مخج; ) ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM +FD8F;FD8F;FD8F;0645 062E 0645;0645 062E 0645; # (ﶏ; ﶏ; ﶏ; مخم; مخم; ) ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM +FD92;FD92;FD92;0645 062C 062E;0645 062C 062E; # (ﶒ; ﶒ; ﶒ; مجخ; مجخ; ) ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM +FD93;FD93;FD93;0647 0645 062C;0647 0645 062C; # (ﶓ; ﶓ; ﶓ; همج; همج; ) ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM +FD94;FD94;FD94;0647 0645 0645;0647 0645 0645; # (ﶔ; ﶔ; ﶔ; همم; همم; ) ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM +FD95;FD95;FD95;0646 062D 0645;0646 062D 0645; # (ﶕ; ﶕ; ﶕ; نحم; نحم; ) ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM +FD96;FD96;FD96;0646 062D 0649;0646 062D 0649; # (ﶖ; ﶖ; ﶖ; نحى; نحى; ) ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM +FD97;FD97;FD97;0646 062C 0645;0646 062C 0645; # (ﶗ; ﶗ; ﶗ; نجم; نجم; ) ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM +FD98;FD98;FD98;0646 062C 0645;0646 062C 0645; # (ﶘ; ﶘ; ﶘ; نجم; نجم; ) ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM +FD99;FD99;FD99;0646 062C 0649;0646 062C 0649; # (ﶙ; ﶙ; ﶙ; نجى; نجى; ) ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM +FD9A;FD9A;FD9A;0646 0645 064A;0646 0645 064A; # (ﶚ; ﶚ; ﶚ; نمي; نمي; ) ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM +FD9B;FD9B;FD9B;0646 0645 0649;0646 0645 0649; # (ﶛ; ﶛ; ﶛ; نمى; نمى; ) ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM +FD9C;FD9C;FD9C;064A 0645 0645;064A 0645 0645; # (ﶜ; ﶜ; ﶜ; يمم; يمم; ) ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM +FD9D;FD9D;FD9D;064A 0645 0645;064A 0645 0645; # (ﶝ; ﶝ; ﶝ; يمم; يمم; ) ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM +FD9E;FD9E;FD9E;0628 062E 064A;0628 062E 064A; # (ﶞ; ﶞ; ﶞ; بخي; بخي; ) ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM +FD9F;FD9F;FD9F;062A 062C 064A;062A 062C 064A; # (ﶟ; ﶟ; ﶟ; تجي; تجي; ) ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM +FDA0;FDA0;FDA0;062A 062C 0649;062A 062C 0649; # (ﶠ; ﶠ; ﶠ; تجى; تجى; ) ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM +FDA1;FDA1;FDA1;062A 062E 064A;062A 062E 064A; # (ﶡ; ﶡ; ﶡ; تخي; تخي; ) ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM +FDA2;FDA2;FDA2;062A 062E 0649;062A 062E 0649; # (ﶢ; ﶢ; ﶢ; تخى; تخى; ) ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM +FDA3;FDA3;FDA3;062A 0645 064A;062A 0645 064A; # (ﶣ; ﶣ; ﶣ; تمي; تمي; ) ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM +FDA4;FDA4;FDA4;062A 0645 0649;062A 0645 0649; # (ﶤ; ﶤ; ﶤ; تمى; تمى; ) ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM +FDA5;FDA5;FDA5;062C 0645 064A;062C 0645 064A; # (ﶥ; ﶥ; ﶥ; جمي; جمي; ) ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM +FDA6;FDA6;FDA6;062C 062D 0649;062C 062D 0649; # (ﶦ; ﶦ; ﶦ; جحى; جحى; ) ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM +FDA7;FDA7;FDA7;062C 0645 0649;062C 0645 0649; # (ﶧ; ﶧ; ﶧ; جمى; جمى; ) ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM +FDA8;FDA8;FDA8;0633 062E 0649;0633 062E 0649; # (ﶨ; ﶨ; ﶨ; سخى; سخى; ) ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM +FDA9;FDA9;FDA9;0635 062D 064A;0635 062D 064A; # (ﶩ; ﶩ; ﶩ; صحي; صحي; ) ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM +FDAA;FDAA;FDAA;0634 062D 064A;0634 062D 064A; # (ﶪ; ﶪ; ﶪ; شحي; شحي; ) ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM +FDAB;FDAB;FDAB;0636 062D 064A;0636 062D 064A; # (ﶫ; ﶫ; ﶫ; ضحي; ضحي; ) ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM +FDAC;FDAC;FDAC;0644 062C 064A;0644 062C 064A; # (ﶬ; ﶬ; ﶬ; لجي; لجي; ) ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM +FDAD;FDAD;FDAD;0644 0645 064A;0644 0645 064A; # (ﶭ; ﶭ; ﶭ; لمي; لمي; ) ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM +FDAE;FDAE;FDAE;064A 062D 064A;064A 062D 064A; # (ﶮ; ﶮ; ﶮ; يحي; يحي; ) ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM +FDAF;FDAF;FDAF;064A 062C 064A;064A 062C 064A; # (ﶯ; ﶯ; ﶯ; يجي; يجي; ) ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM +FDB0;FDB0;FDB0;064A 0645 064A;064A 0645 064A; # (ﶰ; ﶰ; ﶰ; يمي; يمي; ) ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM +FDB1;FDB1;FDB1;0645 0645 064A;0645 0645 064A; # (ﶱ; ﶱ; ﶱ; ممي; ممي; ) ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM +FDB2;FDB2;FDB2;0642 0645 064A;0642 0645 064A; # (ﶲ; ﶲ; ﶲ; قمي; قمي; ) ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM +FDB3;FDB3;FDB3;0646 062D 064A;0646 062D 064A; # (ﶳ; ﶳ; ﶳ; نحي; نحي; ) ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM +FDB4;FDB4;FDB4;0642 0645 062D;0642 0645 062D; # (ﶴ; ﶴ; ﶴ; قمح; قمح; ) ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM +FDB5;FDB5;FDB5;0644 062D 0645;0644 062D 0645; # (ﶵ; ﶵ; ﶵ; لحم; لحم; ) ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM +FDB6;FDB6;FDB6;0639 0645 064A;0639 0645 064A; # (ﶶ; ﶶ; ﶶ; عمي; عمي; ) ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM +FDB7;FDB7;FDB7;0643 0645 064A;0643 0645 064A; # (ﶷ; ﶷ; ﶷ; كمي; كمي; ) ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM +FDB8;FDB8;FDB8;0646 062C 062D;0646 062C 062D; # (ﶸ; ﶸ; ﶸ; نجح; نجح; ) ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM +FDB9;FDB9;FDB9;0645 062E 064A;0645 062E 064A; # (ﶹ; ﶹ; ﶹ; مخي; مخي; ) ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM +FDBA;FDBA;FDBA;0644 062C 0645;0644 062C 0645; # (ﶺ; ﶺ; ﶺ; لجم; لجم; ) ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM +FDBB;FDBB;FDBB;0643 0645 0645;0643 0645 0645; # (ﶻ; ﶻ; ﶻ; كمم; كمم; ) ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM +FDBC;FDBC;FDBC;0644 062C 0645;0644 062C 0645; # (ﶼ; ﶼ; ﶼ; لجم; لجم; ) ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM +FDBD;FDBD;FDBD;0646 062C 062D;0646 062C 062D; # (ﶽ; ﶽ; ﶽ; نجح; نجح; ) ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM +FDBE;FDBE;FDBE;062C 062D 064A;062C 062D 064A; # (ﶾ; ﶾ; ﶾ; جحي; جحي; ) ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM +FDBF;FDBF;FDBF;062D 062C 064A;062D 062C 064A; # (ﶿ; ﶿ; ﶿ; حجي; حجي; ) ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM +FDC0;FDC0;FDC0;0645 062C 064A;0645 062C 064A; # (ﷀ; ﷀ; ﷀ; مجي; مجي; ) ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM +FDC1;FDC1;FDC1;0641 0645 064A;0641 0645 064A; # (ﷁ; ﷁ; ﷁ; فمي; فمي; ) ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM +FDC2;FDC2;FDC2;0628 062D 064A;0628 062D 064A; # (ﷂ; ﷂ; ﷂ; بحي; بحي; ) ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM +FDC3;FDC3;FDC3;0643 0645 0645;0643 0645 0645; # (ﷃ; ﷃ; ﷃ; كمم; كمم; ) ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM +FDC4;FDC4;FDC4;0639 062C 0645;0639 062C 0645; # (ﷄ; ﷄ; ﷄ; عجم; عجم; ) ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM +FDC5;FDC5;FDC5;0635 0645 0645;0635 0645 0645; # (ﷅ; ﷅ; ﷅ; صمم; صمم; ) ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM +FDC6;FDC6;FDC6;0633 062E 064A;0633 062E 064A; # (ﷆ; ﷆ; ﷆ; سخي; سخي; ) ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM +FDC7;FDC7;FDC7;0646 062C 064A;0646 062C 064A; # (ﷇ; ﷇ; ﷇ; نجي; نجي; ) ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM +FDF0;FDF0;FDF0;0635 0644 06D2;0635 0644 06D2; # (ﷰ; ﷰ; ﷰ; صلے; صلے; ) ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM +FDF1;FDF1;FDF1;0642 0644 06D2;0642 0644 06D2; # (ﷱ; ﷱ; ﷱ; قلے; قلے; ) ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM +FDF2;FDF2;FDF2;0627 0644 0644 0647;0627 0644 0644 0647; # (ﷲ; ﷲ; ﷲ; الله; الله; ) ARABIC LIGATURE ALLAH ISOLATED FORM +FDF3;FDF3;FDF3;0627 0643 0628 0631;0627 0643 0628 0631; # (ﷳ; ﷳ; ﷳ; اكبر; اكبر; ) ARABIC LIGATURE AKBAR ISOLATED FORM +FDF4;FDF4;FDF4;0645 062D 0645 062F;0645 062D 0645 062F; # (ﷴ; ﷴ; ﷴ; محمد; محمد; ) ARABIC LIGATURE MOHAMMAD ISOLATED FORM +FDF5;FDF5;FDF5;0635 0644 0639 0645;0635 0644 0639 0645; # (ﷵ; ﷵ; ﷵ; صلعم; صلعم; ) ARABIC LIGATURE SALAM ISOLATED FORM +FDF6;FDF6;FDF6;0631 0633 0648 0644;0631 0633 0648 0644; # (ﷶ; ﷶ; ﷶ; رسول; رسول; ) ARABIC LIGATURE RASOUL ISOLATED FORM +FDF7;FDF7;FDF7;0639 0644 064A 0647;0639 0644 064A 0647; # (ﷷ; ﷷ; ﷷ; عليه; عليه; ) ARABIC LIGATURE ALAYHE ISOLATED FORM +FDF8;FDF8;FDF8;0648 0633 0644 0645;0648 0633 0644 0645; # (ﷸ; ﷸ; ﷸ; وسلم; وسلم; ) ARABIC LIGATURE WASALLAM ISOLATED FORM +FDF9;FDF9;FDF9;0635 0644 0649;0635 0644 0649; # (ﷹ; ﷹ; ﷹ; صلى; صلى; ) ARABIC LIGATURE SALLA ISOLATED FORM +FDFA;FDFA;FDFA;0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645;0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645; # (ﷺ; ﷺ; ﷺ; صلى الله عليه وسلم; صلى الله عليه وسلم; ) ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM +FDFB;FDFB;FDFB;062C 0644 0020 062C 0644 0627 0644 0647;062C 0644 0020 062C 0644 0627 0644 0647; # (ﷻ; ﷻ; ﷻ; جل جلاله; جل جلاله; ) ARABIC LIGATURE JALLAJALALOUHOU +FDFC;FDFC;FDFC;0631 06CC 0627 0644;0631 06CC 0627 0644; # (﷼; ﷼; ﷼; ریال; ریال; ) RIAL SIGN +FE30;FE30;FE30;002E 002E;002E 002E; # (︰; ︰; ︰; ..; ..; ) PRESENTATION FORM FOR VERTICAL TWO DOT LEADER +FE31;FE31;FE31;2014;2014; # (︱; ︱; ︱; —; —; ) PRESENTATION FORM FOR VERTICAL EM DASH +FE32;FE32;FE32;2013;2013; # (︲; ︲; ︲; –; –; ) PRESENTATION FORM FOR VERTICAL EN DASH +FE33;FE33;FE33;005F;005F; # (︳; ︳; ︳; _; _; ) PRESENTATION FORM FOR VERTICAL LOW LINE +FE34;FE34;FE34;005F;005F; # (︴; ︴; ︴; _; _; ) PRESENTATION FORM FOR VERTICAL WAVY LOW LINE +FE35;FE35;FE35;0028;0028; # (︵; ︵; ︵; (; (; ) PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS +FE36;FE36;FE36;0029;0029; # (︶; ︶; ︶; ); ); ) PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS +FE37;FE37;FE37;007B;007B; # (︷; ︷; ︷; {; {; ) PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET +FE38;FE38;FE38;007D;007D; # (︸; ︸; ︸; }; }; ) PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET +FE39;FE39;FE39;3014;3014; # (︹; ︹; ︹; 〔; 〔; ) PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET +FE3A;FE3A;FE3A;3015;3015; # (︺; ︺; ︺; 〕; 〕; ) PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET +FE3B;FE3B;FE3B;3010;3010; # (︻; ︻; ︻; 【; 【; ) PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET +FE3C;FE3C;FE3C;3011;3011; # (︼; ︼; ︼; 】; 】; ) PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET +FE3D;FE3D;FE3D;300A;300A; # (︽; ︽; ︽; 《; 《; ) PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET +FE3E;FE3E;FE3E;300B;300B; # (︾; ︾; ︾; 》; 》; ) PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET +FE3F;FE3F;FE3F;3008;3008; # (︿; ︿; ︿; 〈; 〈; ) PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET +FE40;FE40;FE40;3009;3009; # (﹀; ﹀; ﹀; 〉; 〉; ) PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET +FE41;FE41;FE41;300C;300C; # (﹁; ﹁; ﹁; 「; 「; ) PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET +FE42;FE42;FE42;300D;300D; # (﹂; ﹂; ﹂; 」; 」; ) PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET +FE43;FE43;FE43;300E;300E; # (﹃; ﹃; ﹃; 『; 『; ) PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET +FE44;FE44;FE44;300F;300F; # (﹄; ﹄; ﹄; 』; 』; ) PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET +FE49;FE49;FE49;0020 0305;0020 0305; # (﹉; ﹉; ﹉; ◌̅; ◌̅; ) DASHED OVERLINE +FE4A;FE4A;FE4A;0020 0305;0020 0305; # (﹊; ﹊; ﹊; ◌̅; ◌̅; ) CENTRELINE OVERLINE +FE4B;FE4B;FE4B;0020 0305;0020 0305; # (﹋; ﹋; ﹋; ◌̅; ◌̅; ) WAVY OVERLINE +FE4C;FE4C;FE4C;0020 0305;0020 0305; # (﹌; ﹌; ﹌; ◌̅; ◌̅; ) DOUBLE WAVY OVERLINE +FE4D;FE4D;FE4D;005F;005F; # (﹍; ﹍; ﹍; _; _; ) DASHED LOW LINE +FE4E;FE4E;FE4E;005F;005F; # (﹎; ﹎; ﹎; _; _; ) CENTRELINE LOW LINE +FE4F;FE4F;FE4F;005F;005F; # (﹏; ﹏; ﹏; _; _; ) WAVY LOW LINE +FE50;FE50;FE50;002C;002C; # (﹐; ﹐; ﹐; ,; ,; ) SMALL COMMA +FE51;FE51;FE51;3001;3001; # (﹑; ﹑; ﹑; 、; 、; ) SMALL IDEOGRAPHIC COMMA +FE52;FE52;FE52;002E;002E; # (﹒; ﹒; ﹒; .; .; ) SMALL FULL STOP +FE54;FE54;FE54;003B;003B; # (﹔; ﹔; ﹔; ;; ;; ) SMALL SEMICOLON +FE55;FE55;FE55;003A;003A; # (﹕; ﹕; ﹕; :; :; ) SMALL COLON +FE56;FE56;FE56;003F;003F; # (﹖; ﹖; ﹖; ?; ?; ) SMALL QUESTION MARK +FE57;FE57;FE57;0021;0021; # (﹗; ﹗; ﹗; !; !; ) SMALL EXCLAMATION MARK +FE58;FE58;FE58;2014;2014; # (﹘; ﹘; ﹘; —; —; ) SMALL EM DASH +FE59;FE59;FE59;0028;0028; # (﹙; ﹙; ﹙; (; (; ) SMALL LEFT PARENTHESIS +FE5A;FE5A;FE5A;0029;0029; # (﹚; ﹚; ﹚; ); ); ) SMALL RIGHT PARENTHESIS +FE5B;FE5B;FE5B;007B;007B; # (﹛; ﹛; ﹛; {; {; ) SMALL LEFT CURLY BRACKET +FE5C;FE5C;FE5C;007D;007D; # (﹜; ﹜; ﹜; }; }; ) SMALL RIGHT CURLY BRACKET +FE5D;FE5D;FE5D;3014;3014; # (﹝; ﹝; ﹝; 〔; 〔; ) SMALL LEFT TORTOISE SHELL BRACKET +FE5E;FE5E;FE5E;3015;3015; # (﹞; ﹞; ﹞; 〕; 〕; ) SMALL RIGHT TORTOISE SHELL BRACKET +FE5F;FE5F;FE5F;0023;0023; # (﹟; ﹟; ﹟; #; #; ) SMALL NUMBER SIGN +FE60;FE60;FE60;0026;0026; # (﹠; ﹠; ﹠; &; &; ) SMALL AMPERSAND +FE61;FE61;FE61;002A;002A; # (﹡; ﹡; ﹡; *; *; ) SMALL ASTERISK +FE62;FE62;FE62;002B;002B; # (﹢; ﹢; ﹢; +; +; ) SMALL PLUS SIGN +FE63;FE63;FE63;002D;002D; # (﹣; ﹣; ﹣; -; -; ) SMALL HYPHEN-MINUS +FE64;FE64;FE64;003C;003C; # (﹤; ﹤; ﹤; <; <; ) SMALL LESS-THAN SIGN +FE65;FE65;FE65;003E;003E; # (﹥; ﹥; ﹥; >; >; ) SMALL GREATER-THAN SIGN +FE66;FE66;FE66;003D;003D; # (﹦; ﹦; ﹦; =; =; ) SMALL EQUALS SIGN +FE68;FE68;FE68;005C;005C; # (﹨; ﹨; ﹨; \; \; ) SMALL REVERSE SOLIDUS +FE69;FE69;FE69;0024;0024; # (﹩; ﹩; ﹩; $; $; ) SMALL DOLLAR SIGN +FE6A;FE6A;FE6A;0025;0025; # (﹪; ﹪; ﹪; %; %; ) SMALL PERCENT SIGN +FE6B;FE6B;FE6B;0040;0040; # (﹫; ﹫; ﹫; @; @; ) SMALL COMMERCIAL AT +FE70;FE70;FE70;0020 064B;0020 064B; # (ﹰ; ﹰ; ﹰ; ◌ً; ◌ً; ) ARABIC FATHATAN ISOLATED FORM +FE71;FE71;FE71;0640 064B;0640 064B; # (ﹱ; ﹱ; ﹱ; ـ◌ً; ـ◌ً; ) ARABIC TATWEEL WITH FATHATAN ABOVE +FE72;FE72;FE72;0020 064C;0020 064C; # (ﹲ; ﹲ; ﹲ; ◌ٌ; ◌ٌ; ) ARABIC DAMMATAN ISOLATED FORM +FE74;FE74;FE74;0020 064D;0020 064D; # (ﹴ; ﹴ; ﹴ; ◌ٍ; ◌ٍ; ) ARABIC KASRATAN ISOLATED FORM +FE76;FE76;FE76;0020 064E;0020 064E; # (ﹶ; ﹶ; ﹶ; ◌َ; ◌َ; ) ARABIC FATHA ISOLATED FORM +FE77;FE77;FE77;0640 064E;0640 064E; # (ﹷ; ﹷ; ﹷ; ـ◌َ; ـ◌َ; ) ARABIC FATHA MEDIAL FORM +FE78;FE78;FE78;0020 064F;0020 064F; # (ﹸ; ﹸ; ﹸ; ◌ُ; ◌ُ; ) ARABIC DAMMA ISOLATED FORM +FE79;FE79;FE79;0640 064F;0640 064F; # (ﹹ; ﹹ; ﹹ; ـ◌ُ; ـ◌ُ; ) ARABIC DAMMA MEDIAL FORM +FE7A;FE7A;FE7A;0020 0650;0020 0650; # (ﹺ; ﹺ; ﹺ; ◌ِ; ◌ِ; ) ARABIC KASRA ISOLATED FORM +FE7B;FE7B;FE7B;0640 0650;0640 0650; # (ﹻ; ﹻ; ﹻ; ـ◌ِ; ـ◌ِ; ) ARABIC KASRA MEDIAL FORM +FE7C;FE7C;FE7C;0020 0651;0020 0651; # (ﹼ; ﹼ; ﹼ; ◌ّ; ◌ّ; ) ARABIC SHADDA ISOLATED FORM +FE7D;FE7D;FE7D;0640 0651;0640 0651; # (ﹽ; ﹽ; ﹽ; ـ◌ّ; ـ◌ّ; ) ARABIC SHADDA MEDIAL FORM +FE7E;FE7E;FE7E;0020 0652;0020 0652; # (ﹾ; ﹾ; ﹾ; ◌ْ; ◌ْ; ) ARABIC SUKUN ISOLATED FORM +FE7F;FE7F;FE7F;0640 0652;0640 0652; # (ﹿ; ﹿ; ﹿ; ـ◌ْ; ـ◌ْ; ) ARABIC SUKUN MEDIAL FORM +FE80;FE80;FE80;0621;0621; # (ﺀ; ﺀ; ﺀ; ء; ء; ) ARABIC LETTER HAMZA ISOLATED FORM +FE81;FE81;FE81;0622;0627 0653; # (ﺁ; ﺁ; ﺁ; آ; ا◌ٓ; ) ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM +FE82;FE82;FE82;0622;0627 0653; # (ﺂ; ﺂ; ﺂ; آ; ا◌ٓ; ) ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM +FE83;FE83;FE83;0623;0627 0654; # (ﺃ; ﺃ; ﺃ; أ; ا◌ٔ; ) ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM +FE84;FE84;FE84;0623;0627 0654; # (ﺄ; ﺄ; ﺄ; أ; ا◌ٔ; ) ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM +FE85;FE85;FE85;0624;0648 0654; # (ﺅ; ﺅ; ﺅ; ؤ; و◌ٔ; ) ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM +FE86;FE86;FE86;0624;0648 0654; # (ﺆ; ﺆ; ﺆ; ؤ; و◌ٔ; ) ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM +FE87;FE87;FE87;0625;0627 0655; # (ﺇ; ﺇ; ﺇ; إ; ا◌ٕ; ) ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM +FE88;FE88;FE88;0625;0627 0655; # (ﺈ; ﺈ; ﺈ; إ; ا◌ٕ; ) ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM +FE89;FE89;FE89;0626;064A 0654; # (ﺉ; ﺉ; ﺉ; ئ; ي◌ٔ; ) ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM +FE8A;FE8A;FE8A;0626;064A 0654; # (ﺊ; ﺊ; ﺊ; ئ; ي◌ٔ; ) ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM +FE8B;FE8B;FE8B;0626;064A 0654; # (ﺋ; ﺋ; ﺋ; ئ; ي◌ٔ; ) ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM +FE8C;FE8C;FE8C;0626;064A 0654; # (ﺌ; ﺌ; ﺌ; ئ; ي◌ٔ; ) ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM +FE8D;FE8D;FE8D;0627;0627; # (ﺍ; ﺍ; ﺍ; ا; ا; ) ARABIC LETTER ALEF ISOLATED FORM +FE8E;FE8E;FE8E;0627;0627; # (ﺎ; ﺎ; ﺎ; ا; ا; ) ARABIC LETTER ALEF FINAL FORM +FE8F;FE8F;FE8F;0628;0628; # (ﺏ; ﺏ; ﺏ; ب; ب; ) ARABIC LETTER BEH ISOLATED FORM +FE90;FE90;FE90;0628;0628; # (ﺐ; ﺐ; ﺐ; ب; ب; ) ARABIC LETTER BEH FINAL FORM +FE91;FE91;FE91;0628;0628; # (ﺑ; ﺑ; ﺑ; ب; ب; ) ARABIC LETTER BEH INITIAL FORM +FE92;FE92;FE92;0628;0628; # (ﺒ; ﺒ; ﺒ; ب; ب; ) ARABIC LETTER BEH MEDIAL FORM +FE93;FE93;FE93;0629;0629; # (ﺓ; ﺓ; ﺓ; ة; ة; ) ARABIC LETTER TEH MARBUTA ISOLATED FORM +FE94;FE94;FE94;0629;0629; # (ﺔ; ﺔ; ﺔ; ة; ة; ) ARABIC LETTER TEH MARBUTA FINAL FORM +FE95;FE95;FE95;062A;062A; # (ﺕ; ﺕ; ﺕ; ت; ت; ) ARABIC LETTER TEH ISOLATED FORM +FE96;FE96;FE96;062A;062A; # (ﺖ; ﺖ; ﺖ; ت; ت; ) ARABIC LETTER TEH FINAL FORM +FE97;FE97;FE97;062A;062A; # (ﺗ; ﺗ; ﺗ; ت; ت; ) ARABIC LETTER TEH INITIAL FORM +FE98;FE98;FE98;062A;062A; # (ﺘ; ﺘ; ﺘ; ت; ت; ) ARABIC LETTER TEH MEDIAL FORM +FE99;FE99;FE99;062B;062B; # (ﺙ; ﺙ; ﺙ; ث; ث; ) ARABIC LETTER THEH ISOLATED FORM +FE9A;FE9A;FE9A;062B;062B; # (ﺚ; ﺚ; ﺚ; ث; ث; ) ARABIC LETTER THEH FINAL FORM +FE9B;FE9B;FE9B;062B;062B; # (ﺛ; ﺛ; ﺛ; ث; ث; ) ARABIC LETTER THEH INITIAL FORM +FE9C;FE9C;FE9C;062B;062B; # (ﺜ; ﺜ; ﺜ; ث; ث; ) ARABIC LETTER THEH MEDIAL FORM +FE9D;FE9D;FE9D;062C;062C; # (ﺝ; ﺝ; ﺝ; ج; ج; ) ARABIC LETTER JEEM ISOLATED FORM +FE9E;FE9E;FE9E;062C;062C; # (ﺞ; ﺞ; ﺞ; ج; ج; ) ARABIC LETTER JEEM FINAL FORM +FE9F;FE9F;FE9F;062C;062C; # (ﺟ; ﺟ; ﺟ; ج; ج; ) ARABIC LETTER JEEM INITIAL FORM +FEA0;FEA0;FEA0;062C;062C; # (ﺠ; ﺠ; ﺠ; ج; ج; ) ARABIC LETTER JEEM MEDIAL FORM +FEA1;FEA1;FEA1;062D;062D; # (ﺡ; ﺡ; ﺡ; ح; ح; ) ARABIC LETTER HAH ISOLATED FORM +FEA2;FEA2;FEA2;062D;062D; # (ﺢ; ﺢ; ﺢ; ح; ح; ) ARABIC LETTER HAH FINAL FORM +FEA3;FEA3;FEA3;062D;062D; # (ﺣ; ﺣ; ﺣ; ح; ح; ) ARABIC LETTER HAH INITIAL FORM +FEA4;FEA4;FEA4;062D;062D; # (ﺤ; ﺤ; ﺤ; ح; ح; ) ARABIC LETTER HAH MEDIAL FORM +FEA5;FEA5;FEA5;062E;062E; # (ﺥ; ﺥ; ﺥ; خ; خ; ) ARABIC LETTER KHAH ISOLATED FORM +FEA6;FEA6;FEA6;062E;062E; # (ﺦ; ﺦ; ﺦ; خ; خ; ) ARABIC LETTER KHAH FINAL FORM +FEA7;FEA7;FEA7;062E;062E; # (ﺧ; ﺧ; ﺧ; خ; خ; ) ARABIC LETTER KHAH INITIAL FORM +FEA8;FEA8;FEA8;062E;062E; # (ﺨ; ﺨ; ﺨ; خ; خ; ) ARABIC LETTER KHAH MEDIAL FORM +FEA9;FEA9;FEA9;062F;062F; # (ﺩ; ﺩ; ﺩ; د; د; ) ARABIC LETTER DAL ISOLATED FORM +FEAA;FEAA;FEAA;062F;062F; # (ﺪ; ﺪ; ﺪ; د; د; ) ARABIC LETTER DAL FINAL FORM +FEAB;FEAB;FEAB;0630;0630; # (ﺫ; ﺫ; ﺫ; ذ; ذ; ) ARABIC LETTER THAL ISOLATED FORM +FEAC;FEAC;FEAC;0630;0630; # (ﺬ; ﺬ; ﺬ; ذ; ذ; ) ARABIC LETTER THAL FINAL FORM +FEAD;FEAD;FEAD;0631;0631; # (ﺭ; ﺭ; ﺭ; ر; ر; ) ARABIC LETTER REH ISOLATED FORM +FEAE;FEAE;FEAE;0631;0631; # (ﺮ; ﺮ; ﺮ; ر; ر; ) ARABIC LETTER REH FINAL FORM +FEAF;FEAF;FEAF;0632;0632; # (ﺯ; ﺯ; ﺯ; ز; ز; ) ARABIC LETTER ZAIN ISOLATED FORM +FEB0;FEB0;FEB0;0632;0632; # (ﺰ; ﺰ; ﺰ; ز; ز; ) ARABIC LETTER ZAIN FINAL FORM +FEB1;FEB1;FEB1;0633;0633; # (ﺱ; ﺱ; ﺱ; س; س; ) ARABIC LETTER SEEN ISOLATED FORM +FEB2;FEB2;FEB2;0633;0633; # (ﺲ; ﺲ; ﺲ; س; س; ) ARABIC LETTER SEEN FINAL FORM +FEB3;FEB3;FEB3;0633;0633; # (ﺳ; ﺳ; ﺳ; س; س; ) ARABIC LETTER SEEN INITIAL FORM +FEB4;FEB4;FEB4;0633;0633; # (ﺴ; ﺴ; ﺴ; س; س; ) ARABIC LETTER SEEN MEDIAL FORM +FEB5;FEB5;FEB5;0634;0634; # (ﺵ; ﺵ; ﺵ; ش; ش; ) ARABIC LETTER SHEEN ISOLATED FORM +FEB6;FEB6;FEB6;0634;0634; # (ﺶ; ﺶ; ﺶ; ش; ش; ) ARABIC LETTER SHEEN FINAL FORM +FEB7;FEB7;FEB7;0634;0634; # (ﺷ; ﺷ; ﺷ; ش; ش; ) ARABIC LETTER SHEEN INITIAL FORM +FEB8;FEB8;FEB8;0634;0634; # (ﺸ; ﺸ; ﺸ; ش; ش; ) ARABIC LETTER SHEEN MEDIAL FORM +FEB9;FEB9;FEB9;0635;0635; # (ﺹ; ﺹ; ﺹ; ص; ص; ) ARABIC LETTER SAD ISOLATED FORM +FEBA;FEBA;FEBA;0635;0635; # (ﺺ; ﺺ; ﺺ; ص; ص; ) ARABIC LETTER SAD FINAL FORM +FEBB;FEBB;FEBB;0635;0635; # (ﺻ; ﺻ; ﺻ; ص; ص; ) ARABIC LETTER SAD INITIAL FORM +FEBC;FEBC;FEBC;0635;0635; # (ﺼ; ﺼ; ﺼ; ص; ص; ) ARABIC LETTER SAD MEDIAL FORM +FEBD;FEBD;FEBD;0636;0636; # (ﺽ; ﺽ; ﺽ; ض; ض; ) ARABIC LETTER DAD ISOLATED FORM +FEBE;FEBE;FEBE;0636;0636; # (ﺾ; ﺾ; ﺾ; ض; ض; ) ARABIC LETTER DAD FINAL FORM +FEBF;FEBF;FEBF;0636;0636; # (ﺿ; ﺿ; ﺿ; ض; ض; ) ARABIC LETTER DAD INITIAL FORM +FEC0;FEC0;FEC0;0636;0636; # (ﻀ; ﻀ; ﻀ; ض; ض; ) ARABIC LETTER DAD MEDIAL FORM +FEC1;FEC1;FEC1;0637;0637; # (ﻁ; ﻁ; ﻁ; ط; ط; ) ARABIC LETTER TAH ISOLATED FORM +FEC2;FEC2;FEC2;0637;0637; # (ﻂ; ﻂ; ﻂ; ط; ط; ) ARABIC LETTER TAH FINAL FORM +FEC3;FEC3;FEC3;0637;0637; # (ﻃ; ﻃ; ﻃ; ط; ط; ) ARABIC LETTER TAH INITIAL FORM +FEC4;FEC4;FEC4;0637;0637; # (ﻄ; ﻄ; ﻄ; ط; ط; ) ARABIC LETTER TAH MEDIAL FORM +FEC5;FEC5;FEC5;0638;0638; # (ﻅ; ﻅ; ﻅ; ظ; ظ; ) ARABIC LETTER ZAH ISOLATED FORM +FEC6;FEC6;FEC6;0638;0638; # (ﻆ; ﻆ; ﻆ; ظ; ظ; ) ARABIC LETTER ZAH FINAL FORM +FEC7;FEC7;FEC7;0638;0638; # (ﻇ; ﻇ; ﻇ; ظ; ظ; ) ARABIC LETTER ZAH INITIAL FORM +FEC8;FEC8;FEC8;0638;0638; # (ﻈ; ﻈ; ﻈ; ظ; ظ; ) ARABIC LETTER ZAH MEDIAL FORM +FEC9;FEC9;FEC9;0639;0639; # (ﻉ; ﻉ; ﻉ; ع; ع; ) ARABIC LETTER AIN ISOLATED FORM +FECA;FECA;FECA;0639;0639; # (ﻊ; ﻊ; ﻊ; ع; ع; ) ARABIC LETTER AIN FINAL FORM +FECB;FECB;FECB;0639;0639; # (ﻋ; ﻋ; ﻋ; ع; ع; ) ARABIC LETTER AIN INITIAL FORM +FECC;FECC;FECC;0639;0639; # (ﻌ; ﻌ; ﻌ; ع; ع; ) ARABIC LETTER AIN MEDIAL FORM +FECD;FECD;FECD;063A;063A; # (ﻍ; ﻍ; ﻍ; غ; غ; ) ARABIC LETTER GHAIN ISOLATED FORM +FECE;FECE;FECE;063A;063A; # (ﻎ; ﻎ; ﻎ; غ; غ; ) ARABIC LETTER GHAIN FINAL FORM +FECF;FECF;FECF;063A;063A; # (ﻏ; ﻏ; ﻏ; غ; غ; ) ARABIC LETTER GHAIN INITIAL FORM +FED0;FED0;FED0;063A;063A; # (ﻐ; ﻐ; ﻐ; غ; غ; ) ARABIC LETTER GHAIN MEDIAL FORM +FED1;FED1;FED1;0641;0641; # (ﻑ; ﻑ; ﻑ; ف; ف; ) ARABIC LETTER FEH ISOLATED FORM +FED2;FED2;FED2;0641;0641; # (ﻒ; ﻒ; ﻒ; ف; ف; ) ARABIC LETTER FEH FINAL FORM +FED3;FED3;FED3;0641;0641; # (ﻓ; ﻓ; ﻓ; ف; ف; ) ARABIC LETTER FEH INITIAL FORM +FED4;FED4;FED4;0641;0641; # (ﻔ; ﻔ; ﻔ; ف; ف; ) ARABIC LETTER FEH MEDIAL FORM +FED5;FED5;FED5;0642;0642; # (ﻕ; ﻕ; ﻕ; ق; ق; ) ARABIC LETTER QAF ISOLATED FORM +FED6;FED6;FED6;0642;0642; # (ﻖ; ﻖ; ﻖ; ق; ق; ) ARABIC LETTER QAF FINAL FORM +FED7;FED7;FED7;0642;0642; # (ﻗ; ﻗ; ﻗ; ق; ق; ) ARABIC LETTER QAF INITIAL FORM +FED8;FED8;FED8;0642;0642; # (ﻘ; ﻘ; ﻘ; ق; ق; ) ARABIC LETTER QAF MEDIAL FORM +FED9;FED9;FED9;0643;0643; # (ﻙ; ﻙ; ﻙ; ك; ك; ) ARABIC LETTER KAF ISOLATED FORM +FEDA;FEDA;FEDA;0643;0643; # (ﻚ; ﻚ; ﻚ; ك; ك; ) ARABIC LETTER KAF FINAL FORM +FEDB;FEDB;FEDB;0643;0643; # (ﻛ; ﻛ; ﻛ; ك; ك; ) ARABIC LETTER KAF INITIAL FORM +FEDC;FEDC;FEDC;0643;0643; # (ﻜ; ﻜ; ﻜ; ك; ك; ) ARABIC LETTER KAF MEDIAL FORM +FEDD;FEDD;FEDD;0644;0644; # (ﻝ; ﻝ; ﻝ; ل; ل; ) ARABIC LETTER LAM ISOLATED FORM +FEDE;FEDE;FEDE;0644;0644; # (ﻞ; ﻞ; ﻞ; ل; ل; ) ARABIC LETTER LAM FINAL FORM +FEDF;FEDF;FEDF;0644;0644; # (ﻟ; ﻟ; ﻟ; ل; ل; ) ARABIC LETTER LAM INITIAL FORM +FEE0;FEE0;FEE0;0644;0644; # (ﻠ; ﻠ; ﻠ; ل; ل; ) ARABIC LETTER LAM MEDIAL FORM +FEE1;FEE1;FEE1;0645;0645; # (ﻡ; ﻡ; ﻡ; م; م; ) ARABIC LETTER MEEM ISOLATED FORM +FEE2;FEE2;FEE2;0645;0645; # (ﻢ; ﻢ; ﻢ; م; م; ) ARABIC LETTER MEEM FINAL FORM +FEE3;FEE3;FEE3;0645;0645; # (ﻣ; ﻣ; ﻣ; م; م; ) ARABIC LETTER MEEM INITIAL FORM +FEE4;FEE4;FEE4;0645;0645; # (ﻤ; ﻤ; ﻤ; م; م; ) ARABIC LETTER MEEM MEDIAL FORM +FEE5;FEE5;FEE5;0646;0646; # (ﻥ; ﻥ; ﻥ; ن; ن; ) ARABIC LETTER NOON ISOLATED FORM +FEE6;FEE6;FEE6;0646;0646; # (ﻦ; ﻦ; ﻦ; ن; ن; ) ARABIC LETTER NOON FINAL FORM +FEE7;FEE7;FEE7;0646;0646; # (ﻧ; ﻧ; ﻧ; ن; ن; ) ARABIC LETTER NOON INITIAL FORM +FEE8;FEE8;FEE8;0646;0646; # (ﻨ; ﻨ; ﻨ; ن; ن; ) ARABIC LETTER NOON MEDIAL FORM +FEE9;FEE9;FEE9;0647;0647; # (ﻩ; ﻩ; ﻩ; ه; ه; ) ARABIC LETTER HEH ISOLATED FORM +FEEA;FEEA;FEEA;0647;0647; # (ﻪ; ﻪ; ﻪ; ه; ه; ) ARABIC LETTER HEH FINAL FORM +FEEB;FEEB;FEEB;0647;0647; # (ﻫ; ﻫ; ﻫ; ه; ه; ) ARABIC LETTER HEH INITIAL FORM +FEEC;FEEC;FEEC;0647;0647; # (ﻬ; ﻬ; ﻬ; ه; ه; ) ARABIC LETTER HEH MEDIAL FORM +FEED;FEED;FEED;0648;0648; # (ﻭ; ﻭ; ﻭ; و; و; ) ARABIC LETTER WAW ISOLATED FORM +FEEE;FEEE;FEEE;0648;0648; # (ﻮ; ﻮ; ﻮ; و; و; ) ARABIC LETTER WAW FINAL FORM +FEEF;FEEF;FEEF;0649;0649; # (ﻯ; ﻯ; ﻯ; ى; ى; ) ARABIC LETTER ALEF MAKSURA ISOLATED FORM +FEF0;FEF0;FEF0;0649;0649; # (ﻰ; ﻰ; ﻰ; ى; ى; ) ARABIC LETTER ALEF MAKSURA FINAL FORM +FEF1;FEF1;FEF1;064A;064A; # (ﻱ; ﻱ; ﻱ; ي; ي; ) ARABIC LETTER YEH ISOLATED FORM +FEF2;FEF2;FEF2;064A;064A; # (ﻲ; ﻲ; ﻲ; ي; ي; ) ARABIC LETTER YEH FINAL FORM +FEF3;FEF3;FEF3;064A;064A; # (ﻳ; ﻳ; ﻳ; ي; ي; ) ARABIC LETTER YEH INITIAL FORM +FEF4;FEF4;FEF4;064A;064A; # (ﻴ; ﻴ; ﻴ; ي; ي; ) ARABIC LETTER YEH MEDIAL FORM +FEF5;FEF5;FEF5;0644 0622;0644 0627 0653; # (ﻵ; ﻵ; ﻵ; لآ; لا◌ٓ; ) ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM +FEF6;FEF6;FEF6;0644 0622;0644 0627 0653; # (ﻶ; ﻶ; ﻶ; لآ; لا◌ٓ; ) ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM +FEF7;FEF7;FEF7;0644 0623;0644 0627 0654; # (ﻷ; ﻷ; ﻷ; لأ; لا◌ٔ; ) ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM +FEF8;FEF8;FEF8;0644 0623;0644 0627 0654; # (ﻸ; ﻸ; ﻸ; لأ; لا◌ٔ; ) ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM +FEF9;FEF9;FEF9;0644 0625;0644 0627 0655; # (ﻹ; ﻹ; ﻹ; لإ; لا◌ٕ; ) ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM +FEFA;FEFA;FEFA;0644 0625;0644 0627 0655; # (ﻺ; ﻺ; ﻺ; لإ; لا◌ٕ; ) ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM +FEFB;FEFB;FEFB;0644 0627;0644 0627; # (ﻻ; ﻻ; ﻻ; لا; لا; ) ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM +FEFC;FEFC;FEFC;0644 0627;0644 0627; # (ﻼ; ﻼ; ﻼ; لا; لا; ) ARABIC LIGATURE LAM WITH ALEF FINAL FORM +FF01;FF01;FF01;0021;0021; # (!; !; !; !; !; ) FULLWIDTH EXCLAMATION MARK +FF02;FF02;FF02;0022;0022; # ("; "; "; "; "; ) FULLWIDTH QUOTATION MARK +FF03;FF03;FF03;0023;0023; # (#; #; #; #; #; ) FULLWIDTH NUMBER SIGN +FF04;FF04;FF04;0024;0024; # ($; $; $; $; $; ) FULLWIDTH DOLLAR SIGN +FF05;FF05;FF05;0025;0025; # (%; %; %; %; %; ) FULLWIDTH PERCENT SIGN +FF06;FF06;FF06;0026;0026; # (&; &; &; &; &; ) FULLWIDTH AMPERSAND +FF07;FF07;FF07;0027;0027; # ('; '; '; '; '; ) FULLWIDTH APOSTROPHE +FF08;FF08;FF08;0028;0028; # ((; (; (; (; (; ) FULLWIDTH LEFT PARENTHESIS +FF09;FF09;FF09;0029;0029; # (); ); ); ); ); ) FULLWIDTH RIGHT PARENTHESIS +FF0A;FF0A;FF0A;002A;002A; # (*; *; *; *; *; ) FULLWIDTH ASTERISK +FF0B;FF0B;FF0B;002B;002B; # (+; +; +; +; +; ) FULLWIDTH PLUS SIGN +FF0C;FF0C;FF0C;002C;002C; # (,; ,; ,; ,; ,; ) FULLWIDTH COMMA +FF0D;FF0D;FF0D;002D;002D; # (-; -; -; -; -; ) FULLWIDTH HYPHEN-MINUS +FF0E;FF0E;FF0E;002E;002E; # (.; .; .; .; .; ) FULLWIDTH FULL STOP +FF0F;FF0F;FF0F;002F;002F; # (/; /; /; /; /; ) FULLWIDTH SOLIDUS +FF10;FF10;FF10;0030;0030; # (0; 0; 0; 0; 0; ) FULLWIDTH DIGIT ZERO +FF11;FF11;FF11;0031;0031; # (1; 1; 1; 1; 1; ) FULLWIDTH DIGIT ONE +FF12;FF12;FF12;0032;0032; # (2; 2; 2; 2; 2; ) FULLWIDTH DIGIT TWO +FF13;FF13;FF13;0033;0033; # (3; 3; 3; 3; 3; ) FULLWIDTH DIGIT THREE +FF14;FF14;FF14;0034;0034; # (4; 4; 4; 4; 4; ) FULLWIDTH DIGIT FOUR +FF15;FF15;FF15;0035;0035; # (5; 5; 5; 5; 5; ) FULLWIDTH DIGIT FIVE +FF16;FF16;FF16;0036;0036; # (6; 6; 6; 6; 6; ) FULLWIDTH DIGIT SIX +FF17;FF17;FF17;0037;0037; # (7; 7; 7; 7; 7; ) FULLWIDTH DIGIT SEVEN +FF18;FF18;FF18;0038;0038; # (8; 8; 8; 8; 8; ) FULLWIDTH DIGIT EIGHT +FF19;FF19;FF19;0039;0039; # (9; 9; 9; 9; 9; ) FULLWIDTH DIGIT NINE +FF1A;FF1A;FF1A;003A;003A; # (:; :; :; :; :; ) FULLWIDTH COLON +FF1B;FF1B;FF1B;003B;003B; # (;; ;; ;; ;; ;; ) FULLWIDTH SEMICOLON +FF1C;FF1C;FF1C;003C;003C; # (<; <; <; <; <; ) FULLWIDTH LESS-THAN SIGN +FF1D;FF1D;FF1D;003D;003D; # (=; =; =; =; =; ) FULLWIDTH EQUALS SIGN +FF1E;FF1E;FF1E;003E;003E; # (>; >; >; >; >; ) FULLWIDTH GREATER-THAN SIGN +FF1F;FF1F;FF1F;003F;003F; # (?; ?; ?; ?; ?; ) FULLWIDTH QUESTION MARK +FF20;FF20;FF20;0040;0040; # (@; @; @; @; @; ) FULLWIDTH COMMERCIAL AT +FF21;FF21;FF21;0041;0041; # (A; A; A; A; A; ) FULLWIDTH LATIN CAPITAL LETTER A +FF22;FF22;FF22;0042;0042; # (B; B; B; B; B; ) FULLWIDTH LATIN CAPITAL LETTER B +FF23;FF23;FF23;0043;0043; # (C; C; C; C; C; ) FULLWIDTH LATIN CAPITAL LETTER C +FF24;FF24;FF24;0044;0044; # (D; D; D; D; D; ) FULLWIDTH LATIN CAPITAL LETTER D +FF25;FF25;FF25;0045;0045; # (E; E; E; E; E; ) FULLWIDTH LATIN CAPITAL LETTER E +FF26;FF26;FF26;0046;0046; # (F; F; F; F; F; ) FULLWIDTH LATIN CAPITAL LETTER F +FF27;FF27;FF27;0047;0047; # (G; G; G; G; G; ) FULLWIDTH LATIN CAPITAL LETTER G +FF28;FF28;FF28;0048;0048; # (H; H; H; H; H; ) FULLWIDTH LATIN CAPITAL LETTER H +FF29;FF29;FF29;0049;0049; # (I; I; I; I; I; ) FULLWIDTH LATIN CAPITAL LETTER I +FF2A;FF2A;FF2A;004A;004A; # (J; J; J; J; J; ) FULLWIDTH LATIN CAPITAL LETTER J +FF2B;FF2B;FF2B;004B;004B; # (K; K; K; K; K; ) FULLWIDTH LATIN CAPITAL LETTER K +FF2C;FF2C;FF2C;004C;004C; # (L; L; L; L; L; ) FULLWIDTH LATIN CAPITAL LETTER L +FF2D;FF2D;FF2D;004D;004D; # (M; M; M; M; M; ) FULLWIDTH LATIN CAPITAL LETTER M +FF2E;FF2E;FF2E;004E;004E; # (N; N; N; N; N; ) FULLWIDTH LATIN CAPITAL LETTER N +FF2F;FF2F;FF2F;004F;004F; # (O; O; O; O; O; ) FULLWIDTH LATIN CAPITAL LETTER O +FF30;FF30;FF30;0050;0050; # (P; P; P; P; P; ) FULLWIDTH LATIN CAPITAL LETTER P +FF31;FF31;FF31;0051;0051; # (Q; Q; Q; Q; Q; ) FULLWIDTH LATIN CAPITAL LETTER Q +FF32;FF32;FF32;0052;0052; # (R; R; R; R; R; ) FULLWIDTH LATIN CAPITAL LETTER R +FF33;FF33;FF33;0053;0053; # (S; S; S; S; S; ) FULLWIDTH LATIN CAPITAL LETTER S +FF34;FF34;FF34;0054;0054; # (T; T; T; T; T; ) FULLWIDTH LATIN CAPITAL LETTER T +FF35;FF35;FF35;0055;0055; # (U; U; U; U; U; ) FULLWIDTH LATIN CAPITAL LETTER U +FF36;FF36;FF36;0056;0056; # (V; V; V; V; V; ) FULLWIDTH LATIN CAPITAL LETTER V +FF37;FF37;FF37;0057;0057; # (W; W; W; W; W; ) FULLWIDTH LATIN CAPITAL LETTER W +FF38;FF38;FF38;0058;0058; # (X; X; X; X; X; ) FULLWIDTH LATIN CAPITAL LETTER X +FF39;FF39;FF39;0059;0059; # (Y; Y; Y; Y; Y; ) FULLWIDTH LATIN CAPITAL LETTER Y +FF3A;FF3A;FF3A;005A;005A; # (Z; Z; Z; Z; Z; ) FULLWIDTH LATIN CAPITAL LETTER Z +FF3B;FF3B;FF3B;005B;005B; # ([; [; [; [; [; ) FULLWIDTH LEFT SQUARE BRACKET +FF3C;FF3C;FF3C;005C;005C; # (\; \; \; \; \; ) FULLWIDTH REVERSE SOLIDUS +FF3D;FF3D;FF3D;005D;005D; # (]; ]; ]; ]; ]; ) FULLWIDTH RIGHT SQUARE BRACKET +FF3E;FF3E;FF3E;005E;005E; # (^; ^; ^; ^; ^; ) FULLWIDTH CIRCUMFLEX ACCENT +FF3F;FF3F;FF3F;005F;005F; # (_; _; _; _; _; ) FULLWIDTH LOW LINE +FF40;FF40;FF40;0060;0060; # (`; `; `; `; `; ) FULLWIDTH GRAVE ACCENT +FF41;FF41;FF41;0061;0061; # (a; a; a; a; a; ) FULLWIDTH LATIN SMALL LETTER A +FF42;FF42;FF42;0062;0062; # (b; b; b; b; b; ) FULLWIDTH LATIN SMALL LETTER B +FF43;FF43;FF43;0063;0063; # (c; c; c; c; c; ) FULLWIDTH LATIN SMALL LETTER C +FF44;FF44;FF44;0064;0064; # (d; d; d; d; d; ) FULLWIDTH LATIN SMALL LETTER D +FF45;FF45;FF45;0065;0065; # (e; e; e; e; e; ) FULLWIDTH LATIN SMALL LETTER E +FF46;FF46;FF46;0066;0066; # (f; f; f; f; f; ) FULLWIDTH LATIN SMALL LETTER F +FF47;FF47;FF47;0067;0067; # (g; g; g; g; g; ) FULLWIDTH LATIN SMALL LETTER G +FF48;FF48;FF48;0068;0068; # (h; h; h; h; h; ) FULLWIDTH LATIN SMALL LETTER H +FF49;FF49;FF49;0069;0069; # (i; i; i; i; i; ) FULLWIDTH LATIN SMALL LETTER I +FF4A;FF4A;FF4A;006A;006A; # (j; j; j; j; j; ) FULLWIDTH LATIN SMALL LETTER J +FF4B;FF4B;FF4B;006B;006B; # (k; k; k; k; k; ) FULLWIDTH LATIN SMALL LETTER K +FF4C;FF4C;FF4C;006C;006C; # (l; l; l; l; l; ) FULLWIDTH LATIN SMALL LETTER L +FF4D;FF4D;FF4D;006D;006D; # (m; m; m; m; m; ) FULLWIDTH LATIN SMALL LETTER M +FF4E;FF4E;FF4E;006E;006E; # (n; n; n; n; n; ) FULLWIDTH LATIN SMALL LETTER N +FF4F;FF4F;FF4F;006F;006F; # (o; o; o; o; o; ) FULLWIDTH LATIN SMALL LETTER O +FF50;FF50;FF50;0070;0070; # (p; p; p; p; p; ) FULLWIDTH LATIN SMALL LETTER P +FF51;FF51;FF51;0071;0071; # (q; q; q; q; q; ) FULLWIDTH LATIN SMALL LETTER Q +FF52;FF52;FF52;0072;0072; # (r; r; r; r; r; ) FULLWIDTH LATIN SMALL LETTER R +FF53;FF53;FF53;0073;0073; # (s; s; s; s; s; ) FULLWIDTH LATIN SMALL LETTER S +FF54;FF54;FF54;0074;0074; # (t; t; t; t; t; ) FULLWIDTH LATIN SMALL LETTER T +FF55;FF55;FF55;0075;0075; # (u; u; u; u; u; ) FULLWIDTH LATIN SMALL LETTER U +FF56;FF56;FF56;0076;0076; # (v; v; v; v; v; ) FULLWIDTH LATIN SMALL LETTER V +FF57;FF57;FF57;0077;0077; # (w; w; w; w; w; ) FULLWIDTH LATIN SMALL LETTER W +FF58;FF58;FF58;0078;0078; # (x; x; x; x; x; ) FULLWIDTH LATIN SMALL LETTER X +FF59;FF59;FF59;0079;0079; # (y; y; y; y; y; ) FULLWIDTH LATIN SMALL LETTER Y +FF5A;FF5A;FF5A;007A;007A; # (z; z; z; z; z; ) FULLWIDTH LATIN SMALL LETTER Z +FF5B;FF5B;FF5B;007B;007B; # ({; {; {; {; {; ) FULLWIDTH LEFT CURLY BRACKET +FF5C;FF5C;FF5C;007C;007C; # (|; |; |; |; |; ) FULLWIDTH VERTICAL LINE +FF5D;FF5D;FF5D;007D;007D; # (}; }; }; }; }; ) FULLWIDTH RIGHT CURLY BRACKET +FF5E;FF5E;FF5E;007E;007E; # (~; ~; ~; ~; ~; ) FULLWIDTH TILDE +FF5F;FF5F;FF5F;2985;2985; # (⦅; ⦅; ⦅; ⦅; ⦅; ) FULLWIDTH LEFT WHITE PARENTHESIS +FF60;FF60;FF60;2986;2986; # (⦆; ⦆; ⦆; ⦆; ⦆; ) FULLWIDTH RIGHT WHITE PARENTHESIS +FF61;FF61;FF61;3002;3002; # (。; 。; 。; 。; 。; ) HALFWIDTH IDEOGRAPHIC FULL STOP +FF62;FF62;FF62;300C;300C; # (「; 「; 「; 「; 「; ) HALFWIDTH LEFT CORNER BRACKET +FF63;FF63;FF63;300D;300D; # (」; 」; 」; 」; 」; ) HALFWIDTH RIGHT CORNER BRACKET +FF64;FF64;FF64;3001;3001; # (、; 、; 、; 、; 、; ) HALFWIDTH IDEOGRAPHIC COMMA +FF65;FF65;FF65;30FB;30FB; # (・; ・; ・; ・; ・; ) HALFWIDTH KATAKANA MIDDLE DOT +FF66;FF66;FF66;30F2;30F2; # (ヲ; ヲ; ヲ; ヲ; ヲ; ) HALFWIDTH KATAKANA LETTER WO +FF67;FF67;FF67;30A1;30A1; # (ァ; ァ; ァ; ァ; ァ; ) HALFWIDTH KATAKANA LETTER SMALL A +FF68;FF68;FF68;30A3;30A3; # (ィ; ィ; ィ; ィ; ィ; ) HALFWIDTH KATAKANA LETTER SMALL I +FF69;FF69;FF69;30A5;30A5; # (ゥ; ゥ; ゥ; ゥ; ゥ; ) HALFWIDTH KATAKANA LETTER SMALL U +FF6A;FF6A;FF6A;30A7;30A7; # (ェ; ェ; ェ; ェ; ェ; ) HALFWIDTH KATAKANA LETTER SMALL E +FF6B;FF6B;FF6B;30A9;30A9; # (ォ; ォ; ォ; ォ; ォ; ) HALFWIDTH KATAKANA LETTER SMALL O +FF6C;FF6C;FF6C;30E3;30E3; # (ャ; ャ; ャ; ャ; ャ; ) HALFWIDTH KATAKANA LETTER SMALL YA +FF6D;FF6D;FF6D;30E5;30E5; # (ュ; ュ; ュ; ュ; ュ; ) HALFWIDTH KATAKANA LETTER SMALL YU +FF6E;FF6E;FF6E;30E7;30E7; # (ョ; ョ; ョ; ョ; ョ; ) HALFWIDTH KATAKANA LETTER SMALL YO +FF6F;FF6F;FF6F;30C3;30C3; # (ッ; ッ; ッ; ッ; ッ; ) HALFWIDTH KATAKANA LETTER SMALL TU +FF70;FF70;FF70;30FC;30FC; # (ー; ー; ー; ー; ー; ) HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK +FF71;FF71;FF71;30A2;30A2; # (ア; ア; ア; ア; ア; ) HALFWIDTH KATAKANA LETTER A +FF72;FF72;FF72;30A4;30A4; # (イ; イ; イ; イ; イ; ) HALFWIDTH KATAKANA LETTER I +FF73;FF73;FF73;30A6;30A6; # (ウ; ウ; ウ; ウ; ウ; ) HALFWIDTH KATAKANA LETTER U +FF74;FF74;FF74;30A8;30A8; # (エ; エ; エ; エ; エ; ) HALFWIDTH KATAKANA LETTER E +FF75;FF75;FF75;30AA;30AA; # (オ; オ; オ; オ; オ; ) HALFWIDTH KATAKANA LETTER O +FF76;FF76;FF76;30AB;30AB; # (カ; カ; カ; カ; カ; ) HALFWIDTH KATAKANA LETTER KA +FF77;FF77;FF77;30AD;30AD; # (キ; キ; キ; キ; キ; ) HALFWIDTH KATAKANA LETTER KI +FF78;FF78;FF78;30AF;30AF; # (ク; ク; ク; ク; ク; ) HALFWIDTH KATAKANA LETTER KU +FF79;FF79;FF79;30B1;30B1; # (ケ; ケ; ケ; ケ; ケ; ) HALFWIDTH KATAKANA LETTER KE +FF7A;FF7A;FF7A;30B3;30B3; # (コ; コ; コ; コ; コ; ) HALFWIDTH KATAKANA LETTER KO +FF7B;FF7B;FF7B;30B5;30B5; # (サ; サ; サ; サ; サ; ) HALFWIDTH KATAKANA LETTER SA +FF7C;FF7C;FF7C;30B7;30B7; # (シ; シ; シ; シ; シ; ) HALFWIDTH KATAKANA LETTER SI +FF7D;FF7D;FF7D;30B9;30B9; # (ス; ス; ス; ス; ス; ) HALFWIDTH KATAKANA LETTER SU +FF7E;FF7E;FF7E;30BB;30BB; # (セ; セ; セ; セ; セ; ) HALFWIDTH KATAKANA LETTER SE +FF7F;FF7F;FF7F;30BD;30BD; # (ソ; ソ; ソ; ソ; ソ; ) HALFWIDTH KATAKANA LETTER SO +FF80;FF80;FF80;30BF;30BF; # (タ; タ; タ; タ; タ; ) HALFWIDTH KATAKANA LETTER TA +FF81;FF81;FF81;30C1;30C1; # (チ; チ; チ; チ; チ; ) HALFWIDTH KATAKANA LETTER TI +FF82;FF82;FF82;30C4;30C4; # (ツ; ツ; ツ; ツ; ツ; ) HALFWIDTH KATAKANA LETTER TU +FF83;FF83;FF83;30C6;30C6; # (テ; テ; テ; テ; テ; ) HALFWIDTH KATAKANA LETTER TE +FF84;FF84;FF84;30C8;30C8; # (ト; ト; ト; ト; ト; ) HALFWIDTH KATAKANA LETTER TO +FF85;FF85;FF85;30CA;30CA; # (ナ; ナ; ナ; ナ; ナ; ) HALFWIDTH KATAKANA LETTER NA +FF86;FF86;FF86;30CB;30CB; # (ニ; ニ; ニ; ニ; ニ; ) HALFWIDTH KATAKANA LETTER NI +FF87;FF87;FF87;30CC;30CC; # (ヌ; ヌ; ヌ; ヌ; ヌ; ) HALFWIDTH KATAKANA LETTER NU +FF88;FF88;FF88;30CD;30CD; # (ネ; ネ; ネ; ネ; ネ; ) HALFWIDTH KATAKANA LETTER NE +FF89;FF89;FF89;30CE;30CE; # (ノ; ノ; ノ; ノ; ノ; ) HALFWIDTH KATAKANA LETTER NO +FF8A;FF8A;FF8A;30CF;30CF; # (ハ; ハ; ハ; ハ; ハ; ) HALFWIDTH KATAKANA LETTER HA +FF8B;FF8B;FF8B;30D2;30D2; # (ヒ; ヒ; ヒ; ヒ; ヒ; ) HALFWIDTH KATAKANA LETTER HI +FF8C;FF8C;FF8C;30D5;30D5; # (フ; フ; フ; フ; フ; ) HALFWIDTH KATAKANA LETTER HU +FF8D;FF8D;FF8D;30D8;30D8; # (ヘ; ヘ; ヘ; ヘ; ヘ; ) HALFWIDTH KATAKANA LETTER HE +FF8E;FF8E;FF8E;30DB;30DB; # (ホ; ホ; ホ; ホ; ホ; ) HALFWIDTH KATAKANA LETTER HO +FF8F;FF8F;FF8F;30DE;30DE; # (マ; マ; マ; マ; マ; ) HALFWIDTH KATAKANA LETTER MA +FF90;FF90;FF90;30DF;30DF; # (ミ; ミ; ミ; ミ; ミ; ) HALFWIDTH KATAKANA LETTER MI +FF91;FF91;FF91;30E0;30E0; # (ム; ム; ム; ム; ム; ) HALFWIDTH KATAKANA LETTER MU +FF92;FF92;FF92;30E1;30E1; # (メ; メ; メ; メ; メ; ) HALFWIDTH KATAKANA LETTER ME +FF93;FF93;FF93;30E2;30E2; # (モ; モ; モ; モ; モ; ) HALFWIDTH KATAKANA LETTER MO +FF94;FF94;FF94;30E4;30E4; # (ヤ; ヤ; ヤ; ヤ; ヤ; ) HALFWIDTH KATAKANA LETTER YA +FF95;FF95;FF95;30E6;30E6; # (ユ; ユ; ユ; ユ; ユ; ) HALFWIDTH KATAKANA LETTER YU +FF96;FF96;FF96;30E8;30E8; # (ヨ; ヨ; ヨ; ヨ; ヨ; ) HALFWIDTH KATAKANA LETTER YO +FF97;FF97;FF97;30E9;30E9; # (ラ; ラ; ラ; ラ; ラ; ) HALFWIDTH KATAKANA LETTER RA +FF98;FF98;FF98;30EA;30EA; # (リ; リ; リ; リ; リ; ) HALFWIDTH KATAKANA LETTER RI +FF99;FF99;FF99;30EB;30EB; # (ル; ル; ル; ル; ル; ) HALFWIDTH KATAKANA LETTER RU +FF9A;FF9A;FF9A;30EC;30EC; # (レ; レ; レ; レ; レ; ) HALFWIDTH KATAKANA LETTER RE +FF9B;FF9B;FF9B;30ED;30ED; # (ロ; ロ; ロ; ロ; ロ; ) HALFWIDTH KATAKANA LETTER RO +FF9C;FF9C;FF9C;30EF;30EF; # (ワ; ワ; ワ; ワ; ワ; ) HALFWIDTH KATAKANA LETTER WA +FF9D;FF9D;FF9D;30F3;30F3; # (ン; ン; ン; ン; ン; ) HALFWIDTH KATAKANA LETTER N +FF9E;FF9E;FF9E;3099;3099; # (゙; ゙; ゙; ◌゙; ◌゙; ) HALFWIDTH KATAKANA VOICED SOUND MARK +FF9F;FF9F;FF9F;309A;309A; # (゚; ゚; ゚; ◌゚; ◌゚; ) HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK +FFA0;FFA0;FFA0;1160;1160; # (ᅠ; ᅠ; ᅠ; ᅠ; ᅠ; ) HALFWIDTH HANGUL FILLER +FFA1;FFA1;FFA1;1100;1100; # (ᄀ; ᄀ; ᄀ; ᄀ; ᄀ; ) HALFWIDTH HANGUL LETTER KIYEOK +FFA2;FFA2;FFA2;1101;1101; # (ᄁ; ᄁ; ᄁ; ᄁ; ᄁ; ) HALFWIDTH HANGUL LETTER SSANGKIYEOK +FFA3;FFA3;FFA3;11AA;11AA; # (ᆪ; ᆪ; ᆪ; ᆪ; ᆪ; ) HALFWIDTH HANGUL LETTER KIYEOK-SIOS +FFA4;FFA4;FFA4;1102;1102; # (ᄂ; ᄂ; ᄂ; ᄂ; ᄂ; ) HALFWIDTH HANGUL LETTER NIEUN +FFA5;FFA5;FFA5;11AC;11AC; # (ᆬ; ᆬ; ᆬ; ᆬ; ᆬ; ) HALFWIDTH HANGUL LETTER NIEUN-CIEUC +FFA6;FFA6;FFA6;11AD;11AD; # (ᆭ; ᆭ; ᆭ; ᆭ; ᆭ; ) HALFWIDTH HANGUL LETTER NIEUN-HIEUH +FFA7;FFA7;FFA7;1103;1103; # (ᄃ; ᄃ; ᄃ; ᄃ; ᄃ; ) HALFWIDTH HANGUL LETTER TIKEUT +FFA8;FFA8;FFA8;1104;1104; # (ᄄ; ᄄ; ᄄ; ᄄ; ᄄ; ) HALFWIDTH HANGUL LETTER SSANGTIKEUT +FFA9;FFA9;FFA9;1105;1105; # (ᄅ; ᄅ; ᄅ; ᄅ; ᄅ; ) HALFWIDTH HANGUL LETTER RIEUL +FFAA;FFAA;FFAA;11B0;11B0; # (ᆰ; ᆰ; ᆰ; ᆰ; ᆰ; ) HALFWIDTH HANGUL LETTER RIEUL-KIYEOK +FFAB;FFAB;FFAB;11B1;11B1; # (ᆱ; ᆱ; ᆱ; ᆱ; ᆱ; ) HALFWIDTH HANGUL LETTER RIEUL-MIEUM +FFAC;FFAC;FFAC;11B2;11B2; # (ᆲ; ᆲ; ᆲ; ᆲ; ᆲ; ) HALFWIDTH HANGUL LETTER RIEUL-PIEUP +FFAD;FFAD;FFAD;11B3;11B3; # (ᆳ; ᆳ; ᆳ; ᆳ; ᆳ; ) HALFWIDTH HANGUL LETTER RIEUL-SIOS +FFAE;FFAE;FFAE;11B4;11B4; # (ᆴ; ᆴ; ᆴ; ᆴ; ᆴ; ) HALFWIDTH HANGUL LETTER RIEUL-THIEUTH +FFAF;FFAF;FFAF;11B5;11B5; # (ᆵ; ᆵ; ᆵ; ᆵ; ᆵ; ) HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH +FFB0;FFB0;FFB0;111A;111A; # (ᄚ; ᄚ; ᄚ; ᄚ; ᄚ; ) HALFWIDTH HANGUL LETTER RIEUL-HIEUH +FFB1;FFB1;FFB1;1106;1106; # (ᄆ; ᄆ; ᄆ; ᄆ; ᄆ; ) HALFWIDTH HANGUL LETTER MIEUM +FFB2;FFB2;FFB2;1107;1107; # (ᄇ; ᄇ; ᄇ; ᄇ; ᄇ; ) HALFWIDTH HANGUL LETTER PIEUP +FFB3;FFB3;FFB3;1108;1108; # (ᄈ; ᄈ; ᄈ; ᄈ; ᄈ; ) HALFWIDTH HANGUL LETTER SSANGPIEUP +FFB4;FFB4;FFB4;1121;1121; # (ᄡ; ᄡ; ᄡ; ᄡ; ᄡ; ) HALFWIDTH HANGUL LETTER PIEUP-SIOS +FFB5;FFB5;FFB5;1109;1109; # (ᄉ; ᄉ; ᄉ; ᄉ; ᄉ; ) HALFWIDTH HANGUL LETTER SIOS +FFB6;FFB6;FFB6;110A;110A; # (ᄊ; ᄊ; ᄊ; ᄊ; ᄊ; ) HALFWIDTH HANGUL LETTER SSANGSIOS +FFB7;FFB7;FFB7;110B;110B; # (ᄋ; ᄋ; ᄋ; ᄋ; ᄋ; ) HALFWIDTH HANGUL LETTER IEUNG +FFB8;FFB8;FFB8;110C;110C; # (ᄌ; ᄌ; ᄌ; ᄌ; ᄌ; ) HALFWIDTH HANGUL LETTER CIEUC +FFB9;FFB9;FFB9;110D;110D; # (ᄍ; ᄍ; ᄍ; ᄍ; ᄍ; ) HALFWIDTH HANGUL LETTER SSANGCIEUC +FFBA;FFBA;FFBA;110E;110E; # (ᄎ; ᄎ; ᄎ; ᄎ; ᄎ; ) HALFWIDTH HANGUL LETTER CHIEUCH +FFBB;FFBB;FFBB;110F;110F; # (ᄏ; ᄏ; ᄏ; ᄏ; ᄏ; ) HALFWIDTH HANGUL LETTER KHIEUKH +FFBC;FFBC;FFBC;1110;1110; # (ᄐ; ᄐ; ᄐ; ᄐ; ᄐ; ) HALFWIDTH HANGUL LETTER THIEUTH +FFBD;FFBD;FFBD;1111;1111; # (ᄑ; ᄑ; ᄑ; ᄑ; ᄑ; ) HALFWIDTH HANGUL LETTER PHIEUPH +FFBE;FFBE;FFBE;1112;1112; # (ᄒ; ᄒ; ᄒ; ᄒ; ᄒ; ) HALFWIDTH HANGUL LETTER HIEUH +FFC2;FFC2;FFC2;1161;1161; # (ᅡ; ᅡ; ᅡ; ᅡ; ᅡ; ) HALFWIDTH HANGUL LETTER A +FFC3;FFC3;FFC3;1162;1162; # (ᅢ; ᅢ; ᅢ; ᅢ; ᅢ; ) HALFWIDTH HANGUL LETTER AE +FFC4;FFC4;FFC4;1163;1163; # (ᅣ; ᅣ; ᅣ; ᅣ; ᅣ; ) HALFWIDTH HANGUL LETTER YA +FFC5;FFC5;FFC5;1164;1164; # (ᅤ; ᅤ; ᅤ; ᅤ; ᅤ; ) HALFWIDTH HANGUL LETTER YAE +FFC6;FFC6;FFC6;1165;1165; # (ᅥ; ᅥ; ᅥ; ᅥ; ᅥ; ) HALFWIDTH HANGUL LETTER EO +FFC7;FFC7;FFC7;1166;1166; # (ᅦ; ᅦ; ᅦ; ᅦ; ᅦ; ) HALFWIDTH HANGUL LETTER E +FFCA;FFCA;FFCA;1167;1167; # (ᅧ; ᅧ; ᅧ; ᅧ; ᅧ; ) HALFWIDTH HANGUL LETTER YEO +FFCB;FFCB;FFCB;1168;1168; # (ᅨ; ᅨ; ᅨ; ᅨ; ᅨ; ) HALFWIDTH HANGUL LETTER YE +FFCC;FFCC;FFCC;1169;1169; # (ᅩ; ᅩ; ᅩ; ᅩ; ᅩ; ) HALFWIDTH HANGUL LETTER O +FFCD;FFCD;FFCD;116A;116A; # (ᅪ; ᅪ; ᅪ; ᅪ; ᅪ; ) HALFWIDTH HANGUL LETTER WA +FFCE;FFCE;FFCE;116B;116B; # (ᅫ; ᅫ; ᅫ; ᅫ; ᅫ; ) HALFWIDTH HANGUL LETTER WAE +FFCF;FFCF;FFCF;116C;116C; # (ᅬ; ᅬ; ᅬ; ᅬ; ᅬ; ) HALFWIDTH HANGUL LETTER OE +FFD2;FFD2;FFD2;116D;116D; # (ᅭ; ᅭ; ᅭ; ᅭ; ᅭ; ) HALFWIDTH HANGUL LETTER YO +FFD3;FFD3;FFD3;116E;116E; # (ᅮ; ᅮ; ᅮ; ᅮ; ᅮ; ) HALFWIDTH HANGUL LETTER U +FFD4;FFD4;FFD4;116F;116F; # (ᅯ; ᅯ; ᅯ; ᅯ; ᅯ; ) HALFWIDTH HANGUL LETTER WEO +FFD5;FFD5;FFD5;1170;1170; # (ᅰ; ᅰ; ᅰ; ᅰ; ᅰ; ) HALFWIDTH HANGUL LETTER WE +FFD6;FFD6;FFD6;1171;1171; # (ᅱ; ᅱ; ᅱ; ᅱ; ᅱ; ) HALFWIDTH HANGUL LETTER WI +FFD7;FFD7;FFD7;1172;1172; # (ᅲ; ᅲ; ᅲ; ᅲ; ᅲ; ) HALFWIDTH HANGUL LETTER YU +FFDA;FFDA;FFDA;1173;1173; # (ᅳ; ᅳ; ᅳ; ᅳ; ᅳ; ) HALFWIDTH HANGUL LETTER EU +FFDB;FFDB;FFDB;1174;1174; # (ᅴ; ᅴ; ᅴ; ᅴ; ᅴ; ) HALFWIDTH HANGUL LETTER YI +FFDC;FFDC;FFDC;1175;1175; # (ᅵ; ᅵ; ᅵ; ᅵ; ᅵ; ) HALFWIDTH HANGUL LETTER I +FFE0;FFE0;FFE0;00A2;00A2; # (¢; ¢; ¢; ¢; ¢; ) FULLWIDTH CENT SIGN +FFE1;FFE1;FFE1;00A3;00A3; # (£; £; £; £; £; ) FULLWIDTH POUND SIGN +FFE2;FFE2;FFE2;00AC;00AC; # (¬; ¬; ¬; ¬; ¬; ) FULLWIDTH NOT SIGN +FFE3;FFE3;FFE3;0020 0304;0020 0304; # ( ̄;  ̄;  ̄; ◌̄; ◌̄; ) FULLWIDTH MACRON +FFE4;FFE4;FFE4;00A6;00A6; # (¦; ¦; ¦; ¦; ¦; ) FULLWIDTH BROKEN BAR +FFE5;FFE5;FFE5;00A5;00A5; # (¥; ¥; ¥; ¥; ¥; ) FULLWIDTH YEN SIGN +FFE6;FFE6;FFE6;20A9;20A9; # (₩; ₩; ₩; ₩; ₩; ) FULLWIDTH WON SIGN +FFE8;FFE8;FFE8;2502;2502; # (│; │; │; │; │; ) HALFWIDTH FORMS LIGHT VERTICAL +FFE9;FFE9;FFE9;2190;2190; # (←; ←; ←; ←; ←; ) HALFWIDTH LEFTWARDS ARROW +FFEA;FFEA;FFEA;2191;2191; # (↑; ↑; ↑; ↑; ↑; ) HALFWIDTH UPWARDS ARROW +FFEB;FFEB;FFEB;2192;2192; # (→; →; →; →; →; ) HALFWIDTH RIGHTWARDS ARROW +FFEC;FFEC;FFEC;2193;2193; # (↓; ↓; ↓; ↓; ↓; ) HALFWIDTH DOWNWARDS ARROW +FFED;FFED;FFED;25A0;25A0; # (■; ■; ■; ■; ■; ) HALFWIDTH BLACK SQUARE +FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE +1D15E;1D157 1D165;1D157 1D165;1D157 1D165;1D157 1D165; # (𝅗𝅥𝅗𝅥; 𝅗𝅗𝅥𝅥; 𝅗𝅗𝅥𝅥; 𝅗𝅗𝅥𝅥; 𝅗𝅗𝅥𝅥; ) MUSICAL SYMBOL HALF NOTE +1D15F;1D158 1D165;1D158 1D165;1D158 1D165;1D158 1D165; # (𝅘𝅥𝅘𝅥; 𝅘𝅘𝅥𝅥; 𝅘𝅘𝅥𝅥; 𝅘𝅘𝅥𝅥; 𝅘𝅘𝅥𝅥; ) MUSICAL SYMBOL QUARTER NOTE +1D160;1D158 1D165 1D16E;1D158 1D165 1D16E;1D158 1D165 1D16E;1D158 1D165 1D16E; # (𝅘𝅥𝅮𝅘𝅥𝅮; 𝅘𝅘𝅥𝅥𝅮𝅮; 𝅘𝅘𝅥𝅥𝅮𝅮; 𝅘𝅘𝅥𝅥𝅮𝅮; 𝅘𝅘𝅥𝅥𝅮𝅮; ) MUSICAL SYMBOL EIGHTH NOTE +1D161;1D158 1D165 1D16F;1D158 1D165 1D16F;1D158 1D165 1D16F;1D158 1D165 1D16F; # (𝅘𝅥𝅯𝅘𝅥𝅯; 𝅘𝅘𝅥𝅥𝅯𝅯; 𝅘𝅘𝅥𝅥𝅯𝅯; 𝅘𝅘𝅥𝅥𝅯𝅯; 𝅘𝅘𝅥𝅥𝅯𝅯; ) MUSICAL SYMBOL SIXTEENTH NOTE +1D162;1D158 1D165 1D170;1D158 1D165 1D170;1D158 1D165 1D170;1D158 1D165 1D170; # (𝅘𝅥𝅰𝅘𝅥𝅰; 𝅘𝅘𝅥𝅥𝅰𝅰; 𝅘𝅘𝅥𝅥𝅰𝅰; 𝅘𝅘𝅥𝅥𝅰𝅰; 𝅘𝅘𝅥𝅥𝅰𝅰; ) MUSICAL SYMBOL THIRTY-SECOND NOTE +1D163;1D158 1D165 1D171;1D158 1D165 1D171;1D158 1D165 1D171;1D158 1D165 1D171; # (𝅘𝅥𝅱𝅘𝅥𝅱; 𝅘𝅘𝅥𝅥𝅱𝅱; 𝅘𝅘𝅥𝅥𝅱𝅱; 𝅘𝅘𝅥𝅥𝅱𝅱; 𝅘𝅘𝅥𝅥𝅱𝅱; ) MUSICAL SYMBOL SIXTY-FOURTH NOTE +1D164;1D158 1D165 1D172;1D158 1D165 1D172;1D158 1D165 1D172;1D158 1D165 1D172; # (𝅘𝅥𝅲𝅘𝅥𝅲; 𝅘𝅘𝅥𝅥𝅲𝅲; 𝅘𝅘𝅥𝅥𝅲𝅲; 𝅘𝅘𝅥𝅥𝅲𝅲; 𝅘𝅘𝅥𝅥𝅲𝅲; ) MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE +1D1BB;1D1B9 1D165;1D1B9 1D165;1D1B9 1D165;1D1B9 1D165; # (𝆹𝅥𝆹𝅥; 𝆹𝆹𝅥𝅥; 𝆹𝆹𝅥𝅥; 𝆹𝆹𝅥𝅥; 𝆹𝆹𝅥𝅥; ) MUSICAL SYMBOL MINIMA +1D1BC;1D1BA 1D165;1D1BA 1D165;1D1BA 1D165;1D1BA 1D165; # (𝆺𝅥𝆺𝅥; 𝆺𝆺𝅥𝅥; 𝆺𝆺𝅥𝅥; 𝆺𝆺𝅥𝅥; 𝆺𝆺𝅥𝅥; ) MUSICAL SYMBOL MINIMA BLACK +1D1BD;1D1B9 1D165 1D16E;1D1B9 1D165 1D16E;1D1B9 1D165 1D16E;1D1B9 1D165 1D16E; # (𝆹𝅥𝅮𝆹𝅥𝅮; 𝆹𝆹𝅥𝅥𝅮𝅮; 𝆹𝆹𝅥𝅥𝅮𝅮; 𝆹𝆹𝅥𝅥𝅮𝅮; 𝆹𝆹𝅥𝅥𝅮𝅮; ) MUSICAL SYMBOL SEMIMINIMA WHITE +1D1BE;1D1BA 1D165 1D16E;1D1BA 1D165 1D16E;1D1BA 1D165 1D16E;1D1BA 1D165 1D16E; # (𝆺𝅥𝅮𝆺𝅥𝅮; 𝆺𝆺𝅥𝅥𝅮𝅮; 𝆺𝆺𝅥𝅥𝅮𝅮; 𝆺𝆺𝅥𝅥𝅮𝅮; 𝆺𝆺𝅥𝅥𝅮𝅮; ) MUSICAL SYMBOL SEMIMINIMA BLACK +1D1BF;1D1B9 1D165 1D16F;1D1B9 1D165 1D16F;1D1B9 1D165 1D16F;1D1B9 1D165 1D16F; # (𝆹𝅥𝅯𝆹𝅥𝅯; 𝆹𝆹𝅥𝅥𝅯𝅯; 𝆹𝆹𝅥𝅥𝅯𝅯; 𝆹𝆹𝅥𝅥𝅯𝅯; 𝆹𝆹𝅥𝅥𝅯𝅯; ) MUSICAL SYMBOL FUSA WHITE +1D1C0;1D1BA 1D165 1D16F;1D1BA 1D165 1D16F;1D1BA 1D165 1D16F;1D1BA 1D165 1D16F; # (𝆺𝅥𝅯𝆺𝅥𝅯; 𝆺𝆺𝅥𝅥𝅯𝅯; 𝆺𝆺𝅥𝅥𝅯𝅯; 𝆺𝆺𝅥𝅥𝅯𝅯; 𝆺𝆺𝅥𝅥𝅯𝅯; ) MUSICAL SYMBOL FUSA BLACK +1D400;1D400;1D400;0041;0041; # (𝐀𝐀; 𝐀𝐀; 𝐀𝐀; A; A; ) MATHEMATICAL BOLD CAPITAL A +1D401;1D401;1D401;0042;0042; # (𝐁𝐁; 𝐁𝐁; 𝐁𝐁; B; B; ) MATHEMATICAL BOLD CAPITAL B +1D402;1D402;1D402;0043;0043; # (𝐂𝐂; 𝐂𝐂; 𝐂𝐂; C; C; ) MATHEMATICAL BOLD CAPITAL C +1D403;1D403;1D403;0044;0044; # (𝐃𝐃; 𝐃𝐃; 𝐃𝐃; D; D; ) MATHEMATICAL BOLD CAPITAL D +1D404;1D404;1D404;0045;0045; # (𝐄𝐄; 𝐄𝐄; 𝐄𝐄; E; E; ) MATHEMATICAL BOLD CAPITAL E +1D405;1D405;1D405;0046;0046; # (𝐅𝐅; 𝐅𝐅; 𝐅𝐅; F; F; ) MATHEMATICAL BOLD CAPITAL F +1D406;1D406;1D406;0047;0047; # (𝐆𝐆; 𝐆𝐆; 𝐆𝐆; G; G; ) MATHEMATICAL BOLD CAPITAL G +1D407;1D407;1D407;0048;0048; # (𝐇𝐇; 𝐇𝐇; 𝐇𝐇; H; H; ) MATHEMATICAL BOLD CAPITAL H +1D408;1D408;1D408;0049;0049; # (𝐈𝐈; 𝐈𝐈; 𝐈𝐈; I; I; ) MATHEMATICAL BOLD CAPITAL I +1D409;1D409;1D409;004A;004A; # (𝐉𝐉; 𝐉𝐉; 𝐉𝐉; J; J; ) MATHEMATICAL BOLD CAPITAL J +1D40A;1D40A;1D40A;004B;004B; # (𝐊𝐊; 𝐊𝐊; 𝐊𝐊; K; K; ) MATHEMATICAL BOLD CAPITAL K +1D40B;1D40B;1D40B;004C;004C; # (𝐋𝐋; 𝐋𝐋; 𝐋𝐋; L; L; ) MATHEMATICAL BOLD CAPITAL L +1D40C;1D40C;1D40C;004D;004D; # (𝐌𝐌; 𝐌𝐌; 𝐌𝐌; M; M; ) MATHEMATICAL BOLD CAPITAL M +1D40D;1D40D;1D40D;004E;004E; # (𝐍𝐍; 𝐍𝐍; 𝐍𝐍; N; N; ) MATHEMATICAL BOLD CAPITAL N +1D40E;1D40E;1D40E;004F;004F; # (𝐎𝐎; 𝐎𝐎; 𝐎𝐎; O; O; ) MATHEMATICAL BOLD CAPITAL O +1D40F;1D40F;1D40F;0050;0050; # (𝐏𝐏; 𝐏𝐏; 𝐏𝐏; P; P; ) MATHEMATICAL BOLD CAPITAL P +1D410;1D410;1D410;0051;0051; # (𝐐𝐐; 𝐐𝐐; 𝐐𝐐; Q; Q; ) MATHEMATICAL BOLD CAPITAL Q +1D411;1D411;1D411;0052;0052; # (𝐑𝐑; 𝐑𝐑; 𝐑𝐑; R; R; ) MATHEMATICAL BOLD CAPITAL R +1D412;1D412;1D412;0053;0053; # (𝐒𝐒; 𝐒𝐒; 𝐒𝐒; S; S; ) MATHEMATICAL BOLD CAPITAL S +1D413;1D413;1D413;0054;0054; # (𝐓𝐓; 𝐓𝐓; 𝐓𝐓; T; T; ) MATHEMATICAL BOLD CAPITAL T +1D414;1D414;1D414;0055;0055; # (𝐔𝐔; 𝐔𝐔; 𝐔𝐔; U; U; ) MATHEMATICAL BOLD CAPITAL U +1D415;1D415;1D415;0056;0056; # (𝐕𝐕; 𝐕𝐕; 𝐕𝐕; V; V; ) MATHEMATICAL BOLD CAPITAL V +1D416;1D416;1D416;0057;0057; # (𝐖𝐖; 𝐖𝐖; 𝐖𝐖; W; W; ) MATHEMATICAL BOLD CAPITAL W +1D417;1D417;1D417;0058;0058; # (𝐗𝐗; 𝐗𝐗; 𝐗𝐗; X; X; ) MATHEMATICAL BOLD CAPITAL X +1D418;1D418;1D418;0059;0059; # (𝐘𝐘; 𝐘𝐘; 𝐘𝐘; Y; Y; ) MATHEMATICAL BOLD CAPITAL Y +1D419;1D419;1D419;005A;005A; # (𝐙𝐙; 𝐙𝐙; 𝐙𝐙; Z; Z; ) MATHEMATICAL BOLD CAPITAL Z +1D41A;1D41A;1D41A;0061;0061; # (𝐚𝐚; 𝐚𝐚; 𝐚𝐚; a; a; ) MATHEMATICAL BOLD SMALL A +1D41B;1D41B;1D41B;0062;0062; # (𝐛𝐛; 𝐛𝐛; 𝐛𝐛; b; b; ) MATHEMATICAL BOLD SMALL B +1D41C;1D41C;1D41C;0063;0063; # (𝐜𝐜; 𝐜𝐜; 𝐜𝐜; c; c; ) MATHEMATICAL BOLD SMALL C +1D41D;1D41D;1D41D;0064;0064; # (𝐝𝐝; 𝐝𝐝; 𝐝𝐝; d; d; ) MATHEMATICAL BOLD SMALL D +1D41E;1D41E;1D41E;0065;0065; # (𝐞𝐞; 𝐞𝐞; 𝐞𝐞; e; e; ) MATHEMATICAL BOLD SMALL E +1D41F;1D41F;1D41F;0066;0066; # (𝐟𝐟; 𝐟𝐟; 𝐟𝐟; f; f; ) MATHEMATICAL BOLD SMALL F +1D420;1D420;1D420;0067;0067; # (𝐠𝐠; 𝐠𝐠; 𝐠𝐠; g; g; ) MATHEMATICAL BOLD SMALL G +1D421;1D421;1D421;0068;0068; # (𝐡𝐡; 𝐡𝐡; 𝐡𝐡; h; h; ) MATHEMATICAL BOLD SMALL H +1D422;1D422;1D422;0069;0069; # (𝐢𝐢; 𝐢𝐢; 𝐢𝐢; i; i; ) MATHEMATICAL BOLD SMALL I +1D423;1D423;1D423;006A;006A; # (𝐣𝐣; 𝐣𝐣; 𝐣𝐣; j; j; ) MATHEMATICAL BOLD SMALL J +1D424;1D424;1D424;006B;006B; # (𝐤𝐤; 𝐤𝐤; 𝐤𝐤; k; k; ) MATHEMATICAL BOLD SMALL K +1D425;1D425;1D425;006C;006C; # (𝐥𝐥; 𝐥𝐥; 𝐥𝐥; l; l; ) MATHEMATICAL BOLD SMALL L +1D426;1D426;1D426;006D;006D; # (𝐦𝐦; 𝐦𝐦; 𝐦𝐦; m; m; ) MATHEMATICAL BOLD SMALL M +1D427;1D427;1D427;006E;006E; # (𝐧𝐧; 𝐧𝐧; 𝐧𝐧; n; n; ) MATHEMATICAL BOLD SMALL N +1D428;1D428;1D428;006F;006F; # (𝐨𝐨; 𝐨𝐨; 𝐨𝐨; o; o; ) MATHEMATICAL BOLD SMALL O +1D429;1D429;1D429;0070;0070; # (𝐩𝐩; 𝐩𝐩; 𝐩𝐩; p; p; ) MATHEMATICAL BOLD SMALL P +1D42A;1D42A;1D42A;0071;0071; # (𝐪𝐪; 𝐪𝐪; 𝐪𝐪; q; q; ) MATHEMATICAL BOLD SMALL Q +1D42B;1D42B;1D42B;0072;0072; # (𝐫𝐫; 𝐫𝐫; 𝐫𝐫; r; r; ) MATHEMATICAL BOLD SMALL R +1D42C;1D42C;1D42C;0073;0073; # (𝐬𝐬; 𝐬𝐬; 𝐬𝐬; s; s; ) MATHEMATICAL BOLD SMALL S +1D42D;1D42D;1D42D;0074;0074; # (𝐭𝐭; 𝐭𝐭; 𝐭𝐭; t; t; ) MATHEMATICAL BOLD SMALL T +1D42E;1D42E;1D42E;0075;0075; # (𝐮𝐮; 𝐮𝐮; 𝐮𝐮; u; u; ) MATHEMATICAL BOLD SMALL U +1D42F;1D42F;1D42F;0076;0076; # (𝐯𝐯; 𝐯𝐯; 𝐯𝐯; v; v; ) MATHEMATICAL BOLD SMALL V +1D430;1D430;1D430;0077;0077; # (𝐰𝐰; 𝐰𝐰; 𝐰𝐰; w; w; ) MATHEMATICAL BOLD SMALL W +1D431;1D431;1D431;0078;0078; # (𝐱𝐱; 𝐱𝐱; 𝐱𝐱; x; x; ) MATHEMATICAL BOLD SMALL X +1D432;1D432;1D432;0079;0079; # (𝐲𝐲; 𝐲𝐲; 𝐲𝐲; y; y; ) MATHEMATICAL BOLD SMALL Y +1D433;1D433;1D433;007A;007A; # (𝐳𝐳; 𝐳𝐳; 𝐳𝐳; z; z; ) MATHEMATICAL BOLD SMALL Z +1D434;1D434;1D434;0041;0041; # (𝐴𝐴; 𝐴𝐴; 𝐴𝐴; A; A; ) MATHEMATICAL ITALIC CAPITAL A +1D435;1D435;1D435;0042;0042; # (𝐵𝐵; 𝐵𝐵; 𝐵𝐵; B; B; ) MATHEMATICAL ITALIC CAPITAL B +1D436;1D436;1D436;0043;0043; # (𝐶𝐶; 𝐶𝐶; 𝐶𝐶; C; C; ) MATHEMATICAL ITALIC CAPITAL C +1D437;1D437;1D437;0044;0044; # (𝐷𝐷; 𝐷𝐷; 𝐷𝐷; D; D; ) MATHEMATICAL ITALIC CAPITAL D +1D438;1D438;1D438;0045;0045; # (𝐸𝐸; 𝐸𝐸; 𝐸𝐸; E; E; ) MATHEMATICAL ITALIC CAPITAL E +1D439;1D439;1D439;0046;0046; # (𝐹𝐹; 𝐹𝐹; 𝐹𝐹; F; F; ) MATHEMATICAL ITALIC CAPITAL F +1D43A;1D43A;1D43A;0047;0047; # (𝐺𝐺; 𝐺𝐺; 𝐺𝐺; G; G; ) MATHEMATICAL ITALIC CAPITAL G +1D43B;1D43B;1D43B;0048;0048; # (𝐻𝐻; 𝐻𝐻; 𝐻𝐻; H; H; ) MATHEMATICAL ITALIC CAPITAL H +1D43C;1D43C;1D43C;0049;0049; # (𝐼𝐼; 𝐼𝐼; 𝐼𝐼; I; I; ) MATHEMATICAL ITALIC CAPITAL I +1D43D;1D43D;1D43D;004A;004A; # (𝐽𝐽; 𝐽𝐽; 𝐽𝐽; J; J; ) MATHEMATICAL ITALIC CAPITAL J +1D43E;1D43E;1D43E;004B;004B; # (𝐾𝐾; 𝐾𝐾; 𝐾𝐾; K; K; ) MATHEMATICAL ITALIC CAPITAL K +1D43F;1D43F;1D43F;004C;004C; # (𝐿𝐿; 𝐿𝐿; 𝐿𝐿; L; L; ) MATHEMATICAL ITALIC CAPITAL L +1D440;1D440;1D440;004D;004D; # (𝑀𝑀; 𝑀𝑀; 𝑀𝑀; M; M; ) MATHEMATICAL ITALIC CAPITAL M +1D441;1D441;1D441;004E;004E; # (𝑁𝑁; 𝑁𝑁; 𝑁𝑁; N; N; ) MATHEMATICAL ITALIC CAPITAL N +1D442;1D442;1D442;004F;004F; # (𝑂𝑂; 𝑂𝑂; 𝑂𝑂; O; O; ) MATHEMATICAL ITALIC CAPITAL O +1D443;1D443;1D443;0050;0050; # (𝑃𝑃; 𝑃𝑃; 𝑃𝑃; P; P; ) MATHEMATICAL ITALIC CAPITAL P +1D444;1D444;1D444;0051;0051; # (𝑄𝑄; 𝑄𝑄; 𝑄𝑄; Q; Q; ) MATHEMATICAL ITALIC CAPITAL Q +1D445;1D445;1D445;0052;0052; # (𝑅𝑅; 𝑅𝑅; 𝑅𝑅; R; R; ) MATHEMATICAL ITALIC CAPITAL R +1D446;1D446;1D446;0053;0053; # (𝑆𝑆; 𝑆𝑆; 𝑆𝑆; S; S; ) MATHEMATICAL ITALIC CAPITAL S +1D447;1D447;1D447;0054;0054; # (𝑇𝑇; 𝑇𝑇; 𝑇𝑇; T; T; ) MATHEMATICAL ITALIC CAPITAL T +1D448;1D448;1D448;0055;0055; # (𝑈𝑈; 𝑈𝑈; 𝑈𝑈; U; U; ) MATHEMATICAL ITALIC CAPITAL U +1D449;1D449;1D449;0056;0056; # (𝑉𝑉; 𝑉𝑉; 𝑉𝑉; V; V; ) MATHEMATICAL ITALIC CAPITAL V +1D44A;1D44A;1D44A;0057;0057; # (𝑊𝑊; 𝑊𝑊; 𝑊𝑊; W; W; ) MATHEMATICAL ITALIC CAPITAL W +1D44B;1D44B;1D44B;0058;0058; # (𝑋𝑋; 𝑋𝑋; 𝑋𝑋; X; X; ) MATHEMATICAL ITALIC CAPITAL X +1D44C;1D44C;1D44C;0059;0059; # (𝑌𝑌; 𝑌𝑌; 𝑌𝑌; Y; Y; ) MATHEMATICAL ITALIC CAPITAL Y +1D44D;1D44D;1D44D;005A;005A; # (𝑍𝑍; 𝑍𝑍; 𝑍𝑍; Z; Z; ) MATHEMATICAL ITALIC CAPITAL Z +1D44E;1D44E;1D44E;0061;0061; # (𝑎𝑎; 𝑎𝑎; 𝑎𝑎; a; a; ) MATHEMATICAL ITALIC SMALL A +1D44F;1D44F;1D44F;0062;0062; # (𝑏𝑏; 𝑏𝑏; 𝑏𝑏; b; b; ) MATHEMATICAL ITALIC SMALL B +1D450;1D450;1D450;0063;0063; # (𝑐𝑐; 𝑐𝑐; 𝑐𝑐; c; c; ) MATHEMATICAL ITALIC SMALL C +1D451;1D451;1D451;0064;0064; # (𝑑𝑑; 𝑑𝑑; 𝑑𝑑; d; d; ) MATHEMATICAL ITALIC SMALL D +1D452;1D452;1D452;0065;0065; # (𝑒𝑒; 𝑒𝑒; 𝑒𝑒; e; e; ) MATHEMATICAL ITALIC SMALL E +1D453;1D453;1D453;0066;0066; # (𝑓𝑓; 𝑓𝑓; 𝑓𝑓; f; f; ) MATHEMATICAL ITALIC SMALL F +1D454;1D454;1D454;0067;0067; # (𝑔𝑔; 𝑔𝑔; 𝑔𝑔; g; g; ) MATHEMATICAL ITALIC SMALL G +1D456;1D456;1D456;0069;0069; # (𝑖𝑖; 𝑖𝑖; 𝑖𝑖; i; i; ) MATHEMATICAL ITALIC SMALL I +1D457;1D457;1D457;006A;006A; # (𝑗𝑗; 𝑗𝑗; 𝑗𝑗; j; j; ) MATHEMATICAL ITALIC SMALL J +1D458;1D458;1D458;006B;006B; # (𝑘𝑘; 𝑘𝑘; 𝑘𝑘; k; k; ) MATHEMATICAL ITALIC SMALL K +1D459;1D459;1D459;006C;006C; # (𝑙𝑙; 𝑙𝑙; 𝑙𝑙; l; l; ) MATHEMATICAL ITALIC SMALL L +1D45A;1D45A;1D45A;006D;006D; # (𝑚𝑚; 𝑚𝑚; 𝑚𝑚; m; m; ) MATHEMATICAL ITALIC SMALL M +1D45B;1D45B;1D45B;006E;006E; # (𝑛𝑛; 𝑛𝑛; 𝑛𝑛; n; n; ) MATHEMATICAL ITALIC SMALL N +1D45C;1D45C;1D45C;006F;006F; # (𝑜𝑜; 𝑜𝑜; 𝑜𝑜; o; o; ) MATHEMATICAL ITALIC SMALL O +1D45D;1D45D;1D45D;0070;0070; # (𝑝𝑝; 𝑝𝑝; 𝑝𝑝; p; p; ) MATHEMATICAL ITALIC SMALL P +1D45E;1D45E;1D45E;0071;0071; # (𝑞𝑞; 𝑞𝑞; 𝑞𝑞; q; q; ) MATHEMATICAL ITALIC SMALL Q +1D45F;1D45F;1D45F;0072;0072; # (𝑟𝑟; 𝑟𝑟; 𝑟𝑟; r; r; ) MATHEMATICAL ITALIC SMALL R +1D460;1D460;1D460;0073;0073; # (𝑠𝑠; 𝑠𝑠; 𝑠𝑠; s; s; ) MATHEMATICAL ITALIC SMALL S +1D461;1D461;1D461;0074;0074; # (𝑡𝑡; 𝑡𝑡; 𝑡𝑡; t; t; ) MATHEMATICAL ITALIC SMALL T +1D462;1D462;1D462;0075;0075; # (𝑢𝑢; 𝑢𝑢; 𝑢𝑢; u; u; ) MATHEMATICAL ITALIC SMALL U +1D463;1D463;1D463;0076;0076; # (𝑣𝑣; 𝑣𝑣; 𝑣𝑣; v; v; ) MATHEMATICAL ITALIC SMALL V +1D464;1D464;1D464;0077;0077; # (𝑤𝑤; 𝑤𝑤; 𝑤𝑤; w; w; ) MATHEMATICAL ITALIC SMALL W +1D465;1D465;1D465;0078;0078; # (𝑥𝑥; 𝑥𝑥; 𝑥𝑥; x; x; ) MATHEMATICAL ITALIC SMALL X +1D466;1D466;1D466;0079;0079; # (𝑦𝑦; 𝑦𝑦; 𝑦𝑦; y; y; ) MATHEMATICAL ITALIC SMALL Y +1D467;1D467;1D467;007A;007A; # (𝑧𝑧; 𝑧𝑧; 𝑧𝑧; z; z; ) MATHEMATICAL ITALIC SMALL Z +1D468;1D468;1D468;0041;0041; # (𝑨𝑨; 𝑨𝑨; 𝑨𝑨; A; A; ) MATHEMATICAL BOLD ITALIC CAPITAL A +1D469;1D469;1D469;0042;0042; # (𝑩𝑩; 𝑩𝑩; 𝑩𝑩; B; B; ) MATHEMATICAL BOLD ITALIC CAPITAL B +1D46A;1D46A;1D46A;0043;0043; # (𝑪𝑪; 𝑪𝑪; 𝑪𝑪; C; C; ) MATHEMATICAL BOLD ITALIC CAPITAL C +1D46B;1D46B;1D46B;0044;0044; # (𝑫𝑫; 𝑫𝑫; 𝑫𝑫; D; D; ) MATHEMATICAL BOLD ITALIC CAPITAL D +1D46C;1D46C;1D46C;0045;0045; # (𝑬𝑬; 𝑬𝑬; 𝑬𝑬; E; E; ) MATHEMATICAL BOLD ITALIC CAPITAL E +1D46D;1D46D;1D46D;0046;0046; # (𝑭𝑭; 𝑭𝑭; 𝑭𝑭; F; F; ) MATHEMATICAL BOLD ITALIC CAPITAL F +1D46E;1D46E;1D46E;0047;0047; # (𝑮𝑮; 𝑮𝑮; 𝑮𝑮; G; G; ) MATHEMATICAL BOLD ITALIC CAPITAL G +1D46F;1D46F;1D46F;0048;0048; # (𝑯𝑯; 𝑯𝑯; 𝑯𝑯; H; H; ) MATHEMATICAL BOLD ITALIC CAPITAL H +1D470;1D470;1D470;0049;0049; # (𝑰𝑰; 𝑰𝑰; 𝑰𝑰; I; I; ) MATHEMATICAL BOLD ITALIC CAPITAL I +1D471;1D471;1D471;004A;004A; # (𝑱𝑱; 𝑱𝑱; 𝑱𝑱; J; J; ) MATHEMATICAL BOLD ITALIC CAPITAL J +1D472;1D472;1D472;004B;004B; # (𝑲𝑲; 𝑲𝑲; 𝑲𝑲; K; K; ) MATHEMATICAL BOLD ITALIC CAPITAL K +1D473;1D473;1D473;004C;004C; # (𝑳𝑳; 𝑳𝑳; 𝑳𝑳; L; L; ) MATHEMATICAL BOLD ITALIC CAPITAL L +1D474;1D474;1D474;004D;004D; # (𝑴𝑴; 𝑴𝑴; 𝑴𝑴; M; M; ) MATHEMATICAL BOLD ITALIC CAPITAL M +1D475;1D475;1D475;004E;004E; # (𝑵𝑵; 𝑵𝑵; 𝑵𝑵; N; N; ) MATHEMATICAL BOLD ITALIC CAPITAL N +1D476;1D476;1D476;004F;004F; # (𝑶𝑶; 𝑶𝑶; 𝑶𝑶; O; O; ) MATHEMATICAL BOLD ITALIC CAPITAL O +1D477;1D477;1D477;0050;0050; # (𝑷𝑷; 𝑷𝑷; 𝑷𝑷; P; P; ) MATHEMATICAL BOLD ITALIC CAPITAL P +1D478;1D478;1D478;0051;0051; # (𝑸𝑸; 𝑸𝑸; 𝑸𝑸; Q; Q; ) MATHEMATICAL BOLD ITALIC CAPITAL Q +1D479;1D479;1D479;0052;0052; # (𝑹𝑹; 𝑹𝑹; 𝑹𝑹; R; R; ) MATHEMATICAL BOLD ITALIC CAPITAL R +1D47A;1D47A;1D47A;0053;0053; # (𝑺𝑺; 𝑺𝑺; 𝑺𝑺; S; S; ) MATHEMATICAL BOLD ITALIC CAPITAL S +1D47B;1D47B;1D47B;0054;0054; # (𝑻𝑻; 𝑻𝑻; 𝑻𝑻; T; T; ) MATHEMATICAL BOLD ITALIC CAPITAL T +1D47C;1D47C;1D47C;0055;0055; # (𝑼𝑼; 𝑼𝑼; 𝑼𝑼; U; U; ) MATHEMATICAL BOLD ITALIC CAPITAL U +1D47D;1D47D;1D47D;0056;0056; # (𝑽𝑽; 𝑽𝑽; 𝑽𝑽; V; V; ) MATHEMATICAL BOLD ITALIC CAPITAL V +1D47E;1D47E;1D47E;0057;0057; # (𝑾𝑾; 𝑾𝑾; 𝑾𝑾; W; W; ) MATHEMATICAL BOLD ITALIC CAPITAL W +1D47F;1D47F;1D47F;0058;0058; # (𝑿𝑿; 𝑿𝑿; 𝑿𝑿; X; X; ) MATHEMATICAL BOLD ITALIC CAPITAL X +1D480;1D480;1D480;0059;0059; # (𝒀𝒀; 𝒀𝒀; 𝒀𝒀; Y; Y; ) MATHEMATICAL BOLD ITALIC CAPITAL Y +1D481;1D481;1D481;005A;005A; # (𝒁𝒁; 𝒁𝒁; 𝒁𝒁; Z; Z; ) MATHEMATICAL BOLD ITALIC CAPITAL Z +1D482;1D482;1D482;0061;0061; # (𝒂𝒂; 𝒂𝒂; 𝒂𝒂; a; a; ) MATHEMATICAL BOLD ITALIC SMALL A +1D483;1D483;1D483;0062;0062; # (𝒃𝒃; 𝒃𝒃; 𝒃𝒃; b; b; ) MATHEMATICAL BOLD ITALIC SMALL B +1D484;1D484;1D484;0063;0063; # (𝒄𝒄; 𝒄𝒄; 𝒄𝒄; c; c; ) MATHEMATICAL BOLD ITALIC SMALL C +1D485;1D485;1D485;0064;0064; # (𝒅𝒅; 𝒅𝒅; 𝒅𝒅; d; d; ) MATHEMATICAL BOLD ITALIC SMALL D +1D486;1D486;1D486;0065;0065; # (𝒆𝒆; 𝒆𝒆; 𝒆𝒆; e; e; ) MATHEMATICAL BOLD ITALIC SMALL E +1D487;1D487;1D487;0066;0066; # (𝒇𝒇; 𝒇𝒇; 𝒇𝒇; f; f; ) MATHEMATICAL BOLD ITALIC SMALL F +1D488;1D488;1D488;0067;0067; # (𝒈𝒈; 𝒈𝒈; 𝒈𝒈; g; g; ) MATHEMATICAL BOLD ITALIC SMALL G +1D489;1D489;1D489;0068;0068; # (𝒉𝒉; 𝒉𝒉; 𝒉𝒉; h; h; ) MATHEMATICAL BOLD ITALIC SMALL H +1D48A;1D48A;1D48A;0069;0069; # (𝒊𝒊; 𝒊𝒊; 𝒊𝒊; i; i; ) MATHEMATICAL BOLD ITALIC SMALL I +1D48B;1D48B;1D48B;006A;006A; # (𝒋𝒋; 𝒋𝒋; 𝒋𝒋; j; j; ) MATHEMATICAL BOLD ITALIC SMALL J +1D48C;1D48C;1D48C;006B;006B; # (𝒌𝒌; 𝒌𝒌; 𝒌𝒌; k; k; ) MATHEMATICAL BOLD ITALIC SMALL K +1D48D;1D48D;1D48D;006C;006C; # (𝒍𝒍; 𝒍𝒍; 𝒍𝒍; l; l; ) MATHEMATICAL BOLD ITALIC SMALL L +1D48E;1D48E;1D48E;006D;006D; # (𝒎𝒎; 𝒎𝒎; 𝒎𝒎; m; m; ) MATHEMATICAL BOLD ITALIC SMALL M +1D48F;1D48F;1D48F;006E;006E; # (𝒏𝒏; 𝒏𝒏; 𝒏𝒏; n; n; ) MATHEMATICAL BOLD ITALIC SMALL N +1D490;1D490;1D490;006F;006F; # (𝒐𝒐; 𝒐𝒐; 𝒐𝒐; o; o; ) MATHEMATICAL BOLD ITALIC SMALL O +1D491;1D491;1D491;0070;0070; # (𝒑𝒑; 𝒑𝒑; 𝒑𝒑; p; p; ) MATHEMATICAL BOLD ITALIC SMALL P +1D492;1D492;1D492;0071;0071; # (𝒒𝒒; 𝒒𝒒; 𝒒𝒒; q; q; ) MATHEMATICAL BOLD ITALIC SMALL Q +1D493;1D493;1D493;0072;0072; # (𝒓𝒓; 𝒓𝒓; 𝒓𝒓; r; r; ) MATHEMATICAL BOLD ITALIC SMALL R +1D494;1D494;1D494;0073;0073; # (𝒔𝒔; 𝒔𝒔; 𝒔𝒔; s; s; ) MATHEMATICAL BOLD ITALIC SMALL S +1D495;1D495;1D495;0074;0074; # (𝒕𝒕; 𝒕𝒕; 𝒕𝒕; t; t; ) MATHEMATICAL BOLD ITALIC SMALL T +1D496;1D496;1D496;0075;0075; # (𝒖𝒖; 𝒖𝒖; 𝒖𝒖; u; u; ) MATHEMATICAL BOLD ITALIC SMALL U +1D497;1D497;1D497;0076;0076; # (𝒗𝒗; 𝒗𝒗; 𝒗𝒗; v; v; ) MATHEMATICAL BOLD ITALIC SMALL V +1D498;1D498;1D498;0077;0077; # (𝒘𝒘; 𝒘𝒘; 𝒘𝒘; w; w; ) MATHEMATICAL BOLD ITALIC SMALL W +1D499;1D499;1D499;0078;0078; # (𝒙𝒙; 𝒙𝒙; 𝒙𝒙; x; x; ) MATHEMATICAL BOLD ITALIC SMALL X +1D49A;1D49A;1D49A;0079;0079; # (𝒚𝒚; 𝒚𝒚; 𝒚𝒚; y; y; ) MATHEMATICAL BOLD ITALIC SMALL Y +1D49B;1D49B;1D49B;007A;007A; # (𝒛𝒛; 𝒛𝒛; 𝒛𝒛; z; z; ) MATHEMATICAL BOLD ITALIC SMALL Z +1D49C;1D49C;1D49C;0041;0041; # (𝒜𝒜; 𝒜𝒜; 𝒜𝒜; A; A; ) MATHEMATICAL SCRIPT CAPITAL A +1D49E;1D49E;1D49E;0043;0043; # (𝒞𝒞; 𝒞𝒞; 𝒞𝒞; C; C; ) MATHEMATICAL SCRIPT CAPITAL C +1D49F;1D49F;1D49F;0044;0044; # (𝒟𝒟; 𝒟𝒟; 𝒟𝒟; D; D; ) MATHEMATICAL SCRIPT CAPITAL D +1D4A2;1D4A2;1D4A2;0047;0047; # (𝒢𝒢; 𝒢𝒢; 𝒢𝒢; G; G; ) MATHEMATICAL SCRIPT CAPITAL G +1D4A5;1D4A5;1D4A5;004A;004A; # (𝒥𝒥; 𝒥𝒥; 𝒥𝒥; J; J; ) MATHEMATICAL SCRIPT CAPITAL J +1D4A6;1D4A6;1D4A6;004B;004B; # (𝒦𝒦; 𝒦𝒦; 𝒦𝒦; K; K; ) MATHEMATICAL SCRIPT CAPITAL K +1D4A9;1D4A9;1D4A9;004E;004E; # (𝒩𝒩; 𝒩𝒩; 𝒩𝒩; N; N; ) MATHEMATICAL SCRIPT CAPITAL N +1D4AA;1D4AA;1D4AA;004F;004F; # (𝒪𝒪; 𝒪𝒪; 𝒪𝒪; O; O; ) MATHEMATICAL SCRIPT CAPITAL O +1D4AB;1D4AB;1D4AB;0050;0050; # (𝒫𝒫; 𝒫𝒫; 𝒫𝒫; P; P; ) MATHEMATICAL SCRIPT CAPITAL P +1D4AC;1D4AC;1D4AC;0051;0051; # (𝒬𝒬; 𝒬𝒬; 𝒬𝒬; Q; Q; ) MATHEMATICAL SCRIPT CAPITAL Q +1D4AE;1D4AE;1D4AE;0053;0053; # (𝒮𝒮; 𝒮𝒮; 𝒮𝒮; S; S; ) MATHEMATICAL SCRIPT CAPITAL S +1D4AF;1D4AF;1D4AF;0054;0054; # (𝒯𝒯; 𝒯𝒯; 𝒯𝒯; T; T; ) MATHEMATICAL SCRIPT CAPITAL T +1D4B0;1D4B0;1D4B0;0055;0055; # (𝒰𝒰; 𝒰𝒰; 𝒰𝒰; U; U; ) MATHEMATICAL SCRIPT CAPITAL U +1D4B1;1D4B1;1D4B1;0056;0056; # (𝒱𝒱; 𝒱𝒱; 𝒱𝒱; V; V; ) MATHEMATICAL SCRIPT CAPITAL V +1D4B2;1D4B2;1D4B2;0057;0057; # (𝒲𝒲; 𝒲𝒲; 𝒲𝒲; W; W; ) MATHEMATICAL SCRIPT CAPITAL W +1D4B3;1D4B3;1D4B3;0058;0058; # (𝒳𝒳; 𝒳𝒳; 𝒳𝒳; X; X; ) MATHEMATICAL SCRIPT CAPITAL X +1D4B4;1D4B4;1D4B4;0059;0059; # (𝒴𝒴; 𝒴𝒴; 𝒴𝒴; Y; Y; ) MATHEMATICAL SCRIPT CAPITAL Y +1D4B5;1D4B5;1D4B5;005A;005A; # (𝒵𝒵; 𝒵𝒵; 𝒵𝒵; Z; Z; ) MATHEMATICAL SCRIPT CAPITAL Z +1D4B6;1D4B6;1D4B6;0061;0061; # (𝒶𝒶; 𝒶𝒶; 𝒶𝒶; a; a; ) MATHEMATICAL SCRIPT SMALL A +1D4B7;1D4B7;1D4B7;0062;0062; # (𝒷𝒷; 𝒷𝒷; 𝒷𝒷; b; b; ) MATHEMATICAL SCRIPT SMALL B +1D4B8;1D4B8;1D4B8;0063;0063; # (𝒸𝒸; 𝒸𝒸; 𝒸𝒸; c; c; ) MATHEMATICAL SCRIPT SMALL C +1D4B9;1D4B9;1D4B9;0064;0064; # (𝒹𝒹; 𝒹𝒹; 𝒹𝒹; d; d; ) MATHEMATICAL SCRIPT SMALL D +1D4BB;1D4BB;1D4BB;0066;0066; # (𝒻𝒻; 𝒻𝒻; 𝒻𝒻; f; f; ) MATHEMATICAL SCRIPT SMALL F +1D4BD;1D4BD;1D4BD;0068;0068; # (𝒽𝒽; 𝒽𝒽; 𝒽𝒽; h; h; ) MATHEMATICAL SCRIPT SMALL H +1D4BE;1D4BE;1D4BE;0069;0069; # (𝒾𝒾; 𝒾𝒾; 𝒾𝒾; i; i; ) MATHEMATICAL SCRIPT SMALL I +1D4BF;1D4BF;1D4BF;006A;006A; # (𝒿𝒿; 𝒿𝒿; 𝒿𝒿; j; j; ) MATHEMATICAL SCRIPT SMALL J +1D4C0;1D4C0;1D4C0;006B;006B; # (𝓀𝓀; 𝓀𝓀; 𝓀𝓀; k; k; ) MATHEMATICAL SCRIPT SMALL K +1D4C2;1D4C2;1D4C2;006D;006D; # (𝓂𝓂; 𝓂𝓂; 𝓂𝓂; m; m; ) MATHEMATICAL SCRIPT SMALL M +1D4C3;1D4C3;1D4C3;006E;006E; # (𝓃𝓃; 𝓃𝓃; 𝓃𝓃; n; n; ) MATHEMATICAL SCRIPT SMALL N +1D4C5;1D4C5;1D4C5;0070;0070; # (𝓅𝓅; 𝓅𝓅; 𝓅𝓅; p; p; ) MATHEMATICAL SCRIPT SMALL P +1D4C6;1D4C6;1D4C6;0071;0071; # (𝓆𝓆; 𝓆𝓆; 𝓆𝓆; q; q; ) MATHEMATICAL SCRIPT SMALL Q +1D4C7;1D4C7;1D4C7;0072;0072; # (𝓇𝓇; 𝓇𝓇; 𝓇𝓇; r; r; ) MATHEMATICAL SCRIPT SMALL R +1D4C8;1D4C8;1D4C8;0073;0073; # (𝓈𝓈; 𝓈𝓈; 𝓈𝓈; s; s; ) MATHEMATICAL SCRIPT SMALL S +1D4C9;1D4C9;1D4C9;0074;0074; # (𝓉𝓉; 𝓉𝓉; 𝓉𝓉; t; t; ) MATHEMATICAL SCRIPT SMALL T +1D4CA;1D4CA;1D4CA;0075;0075; # (𝓊𝓊; 𝓊𝓊; 𝓊𝓊; u; u; ) MATHEMATICAL SCRIPT SMALL U +1D4CB;1D4CB;1D4CB;0076;0076; # (𝓋𝓋; 𝓋𝓋; 𝓋𝓋; v; v; ) MATHEMATICAL SCRIPT SMALL V +1D4CC;1D4CC;1D4CC;0077;0077; # (𝓌𝓌; 𝓌𝓌; 𝓌𝓌; w; w; ) MATHEMATICAL SCRIPT SMALL W +1D4CD;1D4CD;1D4CD;0078;0078; # (𝓍𝓍; 𝓍𝓍; 𝓍𝓍; x; x; ) MATHEMATICAL SCRIPT SMALL X +1D4CE;1D4CE;1D4CE;0079;0079; # (𝓎𝓎; 𝓎𝓎; 𝓎𝓎; y; y; ) MATHEMATICAL SCRIPT SMALL Y +1D4CF;1D4CF;1D4CF;007A;007A; # (𝓏𝓏; 𝓏𝓏; 𝓏𝓏; z; z; ) MATHEMATICAL SCRIPT SMALL Z +1D4D0;1D4D0;1D4D0;0041;0041; # (𝓐𝓐; 𝓐𝓐; 𝓐𝓐; A; A; ) MATHEMATICAL BOLD SCRIPT CAPITAL A +1D4D1;1D4D1;1D4D1;0042;0042; # (𝓑𝓑; 𝓑𝓑; 𝓑𝓑; B; B; ) MATHEMATICAL BOLD SCRIPT CAPITAL B +1D4D2;1D4D2;1D4D2;0043;0043; # (𝓒𝓒; 𝓒𝓒; 𝓒𝓒; C; C; ) MATHEMATICAL BOLD SCRIPT CAPITAL C +1D4D3;1D4D3;1D4D3;0044;0044; # (𝓓𝓓; 𝓓𝓓; 𝓓𝓓; D; D; ) MATHEMATICAL BOLD SCRIPT CAPITAL D +1D4D4;1D4D4;1D4D4;0045;0045; # (𝓔𝓔; 𝓔𝓔; 𝓔𝓔; E; E; ) MATHEMATICAL BOLD SCRIPT CAPITAL E +1D4D5;1D4D5;1D4D5;0046;0046; # (𝓕𝓕; 𝓕𝓕; 𝓕𝓕; F; F; ) MATHEMATICAL BOLD SCRIPT CAPITAL F +1D4D6;1D4D6;1D4D6;0047;0047; # (𝓖𝓖; 𝓖𝓖; 𝓖𝓖; G; G; ) MATHEMATICAL BOLD SCRIPT CAPITAL G +1D4D7;1D4D7;1D4D7;0048;0048; # (𝓗𝓗; 𝓗𝓗; 𝓗𝓗; H; H; ) MATHEMATICAL BOLD SCRIPT CAPITAL H +1D4D8;1D4D8;1D4D8;0049;0049; # (𝓘𝓘; 𝓘𝓘; 𝓘𝓘; I; I; ) MATHEMATICAL BOLD SCRIPT CAPITAL I +1D4D9;1D4D9;1D4D9;004A;004A; # (𝓙𝓙; 𝓙𝓙; 𝓙𝓙; J; J; ) MATHEMATICAL BOLD SCRIPT CAPITAL J +1D4DA;1D4DA;1D4DA;004B;004B; # (𝓚𝓚; 𝓚𝓚; 𝓚𝓚; K; K; ) MATHEMATICAL BOLD SCRIPT CAPITAL K +1D4DB;1D4DB;1D4DB;004C;004C; # (𝓛𝓛; 𝓛𝓛; 𝓛𝓛; L; L; ) MATHEMATICAL BOLD SCRIPT CAPITAL L +1D4DC;1D4DC;1D4DC;004D;004D; # (𝓜𝓜; 𝓜𝓜; 𝓜𝓜; M; M; ) MATHEMATICAL BOLD SCRIPT CAPITAL M +1D4DD;1D4DD;1D4DD;004E;004E; # (𝓝𝓝; 𝓝𝓝; 𝓝𝓝; N; N; ) MATHEMATICAL BOLD SCRIPT CAPITAL N +1D4DE;1D4DE;1D4DE;004F;004F; # (𝓞𝓞; 𝓞𝓞; 𝓞𝓞; O; O; ) MATHEMATICAL BOLD SCRIPT CAPITAL O +1D4DF;1D4DF;1D4DF;0050;0050; # (𝓟𝓟; 𝓟𝓟; 𝓟𝓟; P; P; ) MATHEMATICAL BOLD SCRIPT CAPITAL P +1D4E0;1D4E0;1D4E0;0051;0051; # (𝓠𝓠; 𝓠𝓠; 𝓠𝓠; Q; Q; ) MATHEMATICAL BOLD SCRIPT CAPITAL Q +1D4E1;1D4E1;1D4E1;0052;0052; # (𝓡𝓡; 𝓡𝓡; 𝓡𝓡; R; R; ) MATHEMATICAL BOLD SCRIPT CAPITAL R +1D4E2;1D4E2;1D4E2;0053;0053; # (𝓢𝓢; 𝓢𝓢; 𝓢𝓢; S; S; ) MATHEMATICAL BOLD SCRIPT CAPITAL S +1D4E3;1D4E3;1D4E3;0054;0054; # (𝓣𝓣; 𝓣𝓣; 𝓣𝓣; T; T; ) MATHEMATICAL BOLD SCRIPT CAPITAL T +1D4E4;1D4E4;1D4E4;0055;0055; # (𝓤𝓤; 𝓤𝓤; 𝓤𝓤; U; U; ) MATHEMATICAL BOLD SCRIPT CAPITAL U +1D4E5;1D4E5;1D4E5;0056;0056; # (𝓥𝓥; 𝓥𝓥; 𝓥𝓥; V; V; ) MATHEMATICAL BOLD SCRIPT CAPITAL V +1D4E6;1D4E6;1D4E6;0057;0057; # (𝓦𝓦; 𝓦𝓦; 𝓦𝓦; W; W; ) MATHEMATICAL BOLD SCRIPT CAPITAL W +1D4E7;1D4E7;1D4E7;0058;0058; # (𝓧𝓧; 𝓧𝓧; 𝓧𝓧; X; X; ) MATHEMATICAL BOLD SCRIPT CAPITAL X +1D4E8;1D4E8;1D4E8;0059;0059; # (𝓨𝓨; 𝓨𝓨; 𝓨𝓨; Y; Y; ) MATHEMATICAL BOLD SCRIPT CAPITAL Y +1D4E9;1D4E9;1D4E9;005A;005A; # (𝓩𝓩; 𝓩𝓩; 𝓩𝓩; Z; Z; ) MATHEMATICAL BOLD SCRIPT CAPITAL Z +1D4EA;1D4EA;1D4EA;0061;0061; # (𝓪𝓪; 𝓪𝓪; 𝓪𝓪; a; a; ) MATHEMATICAL BOLD SCRIPT SMALL A +1D4EB;1D4EB;1D4EB;0062;0062; # (𝓫𝓫; 𝓫𝓫; 𝓫𝓫; b; b; ) MATHEMATICAL BOLD SCRIPT SMALL B +1D4EC;1D4EC;1D4EC;0063;0063; # (𝓬𝓬; 𝓬𝓬; 𝓬𝓬; c; c; ) MATHEMATICAL BOLD SCRIPT SMALL C +1D4ED;1D4ED;1D4ED;0064;0064; # (𝓭𝓭; 𝓭𝓭; 𝓭𝓭; d; d; ) MATHEMATICAL BOLD SCRIPT SMALL D +1D4EE;1D4EE;1D4EE;0065;0065; # (𝓮𝓮; 𝓮𝓮; 𝓮𝓮; e; e; ) MATHEMATICAL BOLD SCRIPT SMALL E +1D4EF;1D4EF;1D4EF;0066;0066; # (𝓯𝓯; 𝓯𝓯; 𝓯𝓯; f; f; ) MATHEMATICAL BOLD SCRIPT SMALL F +1D4F0;1D4F0;1D4F0;0067;0067; # (𝓰𝓰; 𝓰𝓰; 𝓰𝓰; g; g; ) MATHEMATICAL BOLD SCRIPT SMALL G +1D4F1;1D4F1;1D4F1;0068;0068; # (𝓱𝓱; 𝓱𝓱; 𝓱𝓱; h; h; ) MATHEMATICAL BOLD SCRIPT SMALL H +1D4F2;1D4F2;1D4F2;0069;0069; # (𝓲𝓲; 𝓲𝓲; 𝓲𝓲; i; i; ) MATHEMATICAL BOLD SCRIPT SMALL I +1D4F3;1D4F3;1D4F3;006A;006A; # (𝓳𝓳; 𝓳𝓳; 𝓳𝓳; j; j; ) MATHEMATICAL BOLD SCRIPT SMALL J +1D4F4;1D4F4;1D4F4;006B;006B; # (𝓴𝓴; 𝓴𝓴; 𝓴𝓴; k; k; ) MATHEMATICAL BOLD SCRIPT SMALL K +1D4F5;1D4F5;1D4F5;006C;006C; # (𝓵𝓵; 𝓵𝓵; 𝓵𝓵; l; l; ) MATHEMATICAL BOLD SCRIPT SMALL L +1D4F6;1D4F6;1D4F6;006D;006D; # (𝓶𝓶; 𝓶𝓶; 𝓶𝓶; m; m; ) MATHEMATICAL BOLD SCRIPT SMALL M +1D4F7;1D4F7;1D4F7;006E;006E; # (𝓷𝓷; 𝓷𝓷; 𝓷𝓷; n; n; ) MATHEMATICAL BOLD SCRIPT SMALL N +1D4F8;1D4F8;1D4F8;006F;006F; # (𝓸𝓸; 𝓸𝓸; 𝓸𝓸; o; o; ) MATHEMATICAL BOLD SCRIPT SMALL O +1D4F9;1D4F9;1D4F9;0070;0070; # (𝓹𝓹; 𝓹𝓹; 𝓹𝓹; p; p; ) MATHEMATICAL BOLD SCRIPT SMALL P +1D4FA;1D4FA;1D4FA;0071;0071; # (𝓺𝓺; 𝓺𝓺; 𝓺𝓺; q; q; ) MATHEMATICAL BOLD SCRIPT SMALL Q +1D4FB;1D4FB;1D4FB;0072;0072; # (𝓻𝓻; 𝓻𝓻; 𝓻𝓻; r; r; ) MATHEMATICAL BOLD SCRIPT SMALL R +1D4FC;1D4FC;1D4FC;0073;0073; # (𝓼𝓼; 𝓼𝓼; 𝓼𝓼; s; s; ) MATHEMATICAL BOLD SCRIPT SMALL S +1D4FD;1D4FD;1D4FD;0074;0074; # (𝓽𝓽; 𝓽𝓽; 𝓽𝓽; t; t; ) MATHEMATICAL BOLD SCRIPT SMALL T +1D4FE;1D4FE;1D4FE;0075;0075; # (𝓾𝓾; 𝓾𝓾; 𝓾𝓾; u; u; ) MATHEMATICAL BOLD SCRIPT SMALL U +1D4FF;1D4FF;1D4FF;0076;0076; # (𝓿𝓿; 𝓿𝓿; 𝓿𝓿; v; v; ) MATHEMATICAL BOLD SCRIPT SMALL V +1D500;1D500;1D500;0077;0077; # (𝔀𝔀; 𝔀𝔀; 𝔀𝔀; w; w; ) MATHEMATICAL BOLD SCRIPT SMALL W +1D501;1D501;1D501;0078;0078; # (𝔁𝔁; 𝔁𝔁; 𝔁𝔁; x; x; ) MATHEMATICAL BOLD SCRIPT SMALL X +1D502;1D502;1D502;0079;0079; # (𝔂𝔂; 𝔂𝔂; 𝔂𝔂; y; y; ) MATHEMATICAL BOLD SCRIPT SMALL Y +1D503;1D503;1D503;007A;007A; # (𝔃𝔃; 𝔃𝔃; 𝔃𝔃; z; z; ) MATHEMATICAL BOLD SCRIPT SMALL Z +1D504;1D504;1D504;0041;0041; # (𝔄𝔄; 𝔄𝔄; 𝔄𝔄; A; A; ) MATHEMATICAL FRAKTUR CAPITAL A +1D505;1D505;1D505;0042;0042; # (𝔅𝔅; 𝔅𝔅; 𝔅𝔅; B; B; ) MATHEMATICAL FRAKTUR CAPITAL B +1D507;1D507;1D507;0044;0044; # (𝔇𝔇; 𝔇𝔇; 𝔇𝔇; D; D; ) MATHEMATICAL FRAKTUR CAPITAL D +1D508;1D508;1D508;0045;0045; # (𝔈𝔈; 𝔈𝔈; 𝔈𝔈; E; E; ) MATHEMATICAL FRAKTUR CAPITAL E +1D509;1D509;1D509;0046;0046; # (𝔉𝔉; 𝔉𝔉; 𝔉𝔉; F; F; ) MATHEMATICAL FRAKTUR CAPITAL F +1D50A;1D50A;1D50A;0047;0047; # (𝔊𝔊; 𝔊𝔊; 𝔊𝔊; G; G; ) MATHEMATICAL FRAKTUR CAPITAL G +1D50D;1D50D;1D50D;004A;004A; # (𝔍𝔍; 𝔍𝔍; 𝔍𝔍; J; J; ) MATHEMATICAL FRAKTUR CAPITAL J +1D50E;1D50E;1D50E;004B;004B; # (𝔎𝔎; 𝔎𝔎; 𝔎𝔎; K; K; ) MATHEMATICAL FRAKTUR CAPITAL K +1D50F;1D50F;1D50F;004C;004C; # (𝔏𝔏; 𝔏𝔏; 𝔏𝔏; L; L; ) MATHEMATICAL FRAKTUR CAPITAL L +1D510;1D510;1D510;004D;004D; # (𝔐𝔐; 𝔐𝔐; 𝔐𝔐; M; M; ) MATHEMATICAL FRAKTUR CAPITAL M +1D511;1D511;1D511;004E;004E; # (𝔑𝔑; 𝔑𝔑; 𝔑𝔑; N; N; ) MATHEMATICAL FRAKTUR CAPITAL N +1D512;1D512;1D512;004F;004F; # (𝔒𝔒; 𝔒𝔒; 𝔒𝔒; O; O; ) MATHEMATICAL FRAKTUR CAPITAL O +1D513;1D513;1D513;0050;0050; # (𝔓𝔓; 𝔓𝔓; 𝔓𝔓; P; P; ) MATHEMATICAL FRAKTUR CAPITAL P +1D514;1D514;1D514;0051;0051; # (𝔔𝔔; 𝔔𝔔; 𝔔𝔔; Q; Q; ) MATHEMATICAL FRAKTUR CAPITAL Q +1D516;1D516;1D516;0053;0053; # (𝔖𝔖; 𝔖𝔖; 𝔖𝔖; S; S; ) MATHEMATICAL FRAKTUR CAPITAL S +1D517;1D517;1D517;0054;0054; # (𝔗𝔗; 𝔗𝔗; 𝔗𝔗; T; T; ) MATHEMATICAL FRAKTUR CAPITAL T +1D518;1D518;1D518;0055;0055; # (𝔘𝔘; 𝔘𝔘; 𝔘𝔘; U; U; ) MATHEMATICAL FRAKTUR CAPITAL U +1D519;1D519;1D519;0056;0056; # (𝔙𝔙; 𝔙𝔙; 𝔙𝔙; V; V; ) MATHEMATICAL FRAKTUR CAPITAL V +1D51A;1D51A;1D51A;0057;0057; # (𝔚𝔚; 𝔚𝔚; 𝔚𝔚; W; W; ) MATHEMATICAL FRAKTUR CAPITAL W +1D51B;1D51B;1D51B;0058;0058; # (𝔛𝔛; 𝔛𝔛; 𝔛𝔛; X; X; ) MATHEMATICAL FRAKTUR CAPITAL X +1D51C;1D51C;1D51C;0059;0059; # (𝔜𝔜; 𝔜𝔜; 𝔜𝔜; Y; Y; ) MATHEMATICAL FRAKTUR CAPITAL Y +1D51E;1D51E;1D51E;0061;0061; # (𝔞𝔞; 𝔞𝔞; 𝔞𝔞; a; a; ) MATHEMATICAL FRAKTUR SMALL A +1D51F;1D51F;1D51F;0062;0062; # (𝔟𝔟; 𝔟𝔟; 𝔟𝔟; b; b; ) MATHEMATICAL FRAKTUR SMALL B +1D520;1D520;1D520;0063;0063; # (𝔠𝔠; 𝔠𝔠; 𝔠𝔠; c; c; ) MATHEMATICAL FRAKTUR SMALL C +1D521;1D521;1D521;0064;0064; # (𝔡𝔡; 𝔡𝔡; 𝔡𝔡; d; d; ) MATHEMATICAL FRAKTUR SMALL D +1D522;1D522;1D522;0065;0065; # (𝔢𝔢; 𝔢𝔢; 𝔢𝔢; e; e; ) MATHEMATICAL FRAKTUR SMALL E +1D523;1D523;1D523;0066;0066; # (𝔣𝔣; 𝔣𝔣; 𝔣𝔣; f; f; ) MATHEMATICAL FRAKTUR SMALL F +1D524;1D524;1D524;0067;0067; # (𝔤𝔤; 𝔤𝔤; 𝔤𝔤; g; g; ) MATHEMATICAL FRAKTUR SMALL G +1D525;1D525;1D525;0068;0068; # (𝔥𝔥; 𝔥𝔥; 𝔥𝔥; h; h; ) MATHEMATICAL FRAKTUR SMALL H +1D526;1D526;1D526;0069;0069; # (𝔦𝔦; 𝔦𝔦; 𝔦𝔦; i; i; ) MATHEMATICAL FRAKTUR SMALL I +1D527;1D527;1D527;006A;006A; # (𝔧𝔧; 𝔧𝔧; 𝔧𝔧; j; j; ) MATHEMATICAL FRAKTUR SMALL J +1D528;1D528;1D528;006B;006B; # (𝔨𝔨; 𝔨𝔨; 𝔨𝔨; k; k; ) MATHEMATICAL FRAKTUR SMALL K +1D529;1D529;1D529;006C;006C; # (𝔩𝔩; 𝔩𝔩; 𝔩𝔩; l; l; ) MATHEMATICAL FRAKTUR SMALL L +1D52A;1D52A;1D52A;006D;006D; # (𝔪𝔪; 𝔪𝔪; 𝔪𝔪; m; m; ) MATHEMATICAL FRAKTUR SMALL M +1D52B;1D52B;1D52B;006E;006E; # (𝔫𝔫; 𝔫𝔫; 𝔫𝔫; n; n; ) MATHEMATICAL FRAKTUR SMALL N +1D52C;1D52C;1D52C;006F;006F; # (𝔬𝔬; 𝔬𝔬; 𝔬𝔬; o; o; ) MATHEMATICAL FRAKTUR SMALL O +1D52D;1D52D;1D52D;0070;0070; # (𝔭𝔭; 𝔭𝔭; 𝔭𝔭; p; p; ) MATHEMATICAL FRAKTUR SMALL P +1D52E;1D52E;1D52E;0071;0071; # (𝔮𝔮; 𝔮𝔮; 𝔮𝔮; q; q; ) MATHEMATICAL FRAKTUR SMALL Q +1D52F;1D52F;1D52F;0072;0072; # (𝔯𝔯; 𝔯𝔯; 𝔯𝔯; r; r; ) MATHEMATICAL FRAKTUR SMALL R +1D530;1D530;1D530;0073;0073; # (𝔰𝔰; 𝔰𝔰; 𝔰𝔰; s; s; ) MATHEMATICAL FRAKTUR SMALL S +1D531;1D531;1D531;0074;0074; # (𝔱𝔱; 𝔱𝔱; 𝔱𝔱; t; t; ) MATHEMATICAL FRAKTUR SMALL T +1D532;1D532;1D532;0075;0075; # (𝔲𝔲; 𝔲𝔲; 𝔲𝔲; u; u; ) MATHEMATICAL FRAKTUR SMALL U +1D533;1D533;1D533;0076;0076; # (𝔳𝔳; 𝔳𝔳; 𝔳𝔳; v; v; ) MATHEMATICAL FRAKTUR SMALL V +1D534;1D534;1D534;0077;0077; # (𝔴𝔴; 𝔴𝔴; 𝔴𝔴; w; w; ) MATHEMATICAL FRAKTUR SMALL W +1D535;1D535;1D535;0078;0078; # (𝔵𝔵; 𝔵𝔵; 𝔵𝔵; x; x; ) MATHEMATICAL FRAKTUR SMALL X +1D536;1D536;1D536;0079;0079; # (𝔶𝔶; 𝔶𝔶; 𝔶𝔶; y; y; ) MATHEMATICAL FRAKTUR SMALL Y +1D537;1D537;1D537;007A;007A; # (𝔷𝔷; 𝔷𝔷; 𝔷𝔷; z; z; ) MATHEMATICAL FRAKTUR SMALL Z +1D538;1D538;1D538;0041;0041; # (𝔸𝔸; 𝔸𝔸; 𝔸𝔸; A; A; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL A +1D539;1D539;1D539;0042;0042; # (𝔹𝔹; 𝔹𝔹; 𝔹𝔹; B; B; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL B +1D53B;1D53B;1D53B;0044;0044; # (𝔻𝔻; 𝔻𝔻; 𝔻𝔻; D; D; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL D +1D53C;1D53C;1D53C;0045;0045; # (𝔼𝔼; 𝔼𝔼; 𝔼𝔼; E; E; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL E +1D53D;1D53D;1D53D;0046;0046; # (𝔽𝔽; 𝔽𝔽; 𝔽𝔽; F; F; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL F +1D53E;1D53E;1D53E;0047;0047; # (𝔾𝔾; 𝔾𝔾; 𝔾𝔾; G; G; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL G +1D540;1D540;1D540;0049;0049; # (𝕀𝕀; 𝕀𝕀; 𝕀𝕀; I; I; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL I +1D541;1D541;1D541;004A;004A; # (𝕁𝕁; 𝕁𝕁; 𝕁𝕁; J; J; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL J +1D542;1D542;1D542;004B;004B; # (𝕂𝕂; 𝕂𝕂; 𝕂𝕂; K; K; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL K +1D543;1D543;1D543;004C;004C; # (𝕃𝕃; 𝕃𝕃; 𝕃𝕃; L; L; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL L +1D544;1D544;1D544;004D;004D; # (𝕄𝕄; 𝕄𝕄; 𝕄𝕄; M; M; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL M +1D546;1D546;1D546;004F;004F; # (𝕆𝕆; 𝕆𝕆; 𝕆𝕆; O; O; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL O +1D54A;1D54A;1D54A;0053;0053; # (𝕊𝕊; 𝕊𝕊; 𝕊𝕊; S; S; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL S +1D54B;1D54B;1D54B;0054;0054; # (𝕋𝕋; 𝕋𝕋; 𝕋𝕋; T; T; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL T +1D54C;1D54C;1D54C;0055;0055; # (𝕌𝕌; 𝕌𝕌; 𝕌𝕌; U; U; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL U +1D54D;1D54D;1D54D;0056;0056; # (𝕍𝕍; 𝕍𝕍; 𝕍𝕍; V; V; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL V +1D54E;1D54E;1D54E;0057;0057; # (𝕎𝕎; 𝕎𝕎; 𝕎𝕎; W; W; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL W +1D54F;1D54F;1D54F;0058;0058; # (𝕏𝕏; 𝕏𝕏; 𝕏𝕏; X; X; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL X +1D550;1D550;1D550;0059;0059; # (𝕐𝕐; 𝕐𝕐; 𝕐𝕐; Y; Y; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL Y +1D552;1D552;1D552;0061;0061; # (𝕒𝕒; 𝕒𝕒; 𝕒𝕒; a; a; ) MATHEMATICAL DOUBLE-STRUCK SMALL A +1D553;1D553;1D553;0062;0062; # (𝕓𝕓; 𝕓𝕓; 𝕓𝕓; b; b; ) MATHEMATICAL DOUBLE-STRUCK SMALL B +1D554;1D554;1D554;0063;0063; # (𝕔𝕔; 𝕔𝕔; 𝕔𝕔; c; c; ) MATHEMATICAL DOUBLE-STRUCK SMALL C +1D555;1D555;1D555;0064;0064; # (𝕕𝕕; 𝕕𝕕; 𝕕𝕕; d; d; ) MATHEMATICAL DOUBLE-STRUCK SMALL D +1D556;1D556;1D556;0065;0065; # (𝕖𝕖; 𝕖𝕖; 𝕖𝕖; e; e; ) MATHEMATICAL DOUBLE-STRUCK SMALL E +1D557;1D557;1D557;0066;0066; # (𝕗𝕗; 𝕗𝕗; 𝕗𝕗; f; f; ) MATHEMATICAL DOUBLE-STRUCK SMALL F +1D558;1D558;1D558;0067;0067; # (𝕘𝕘; 𝕘𝕘; 𝕘𝕘; g; g; ) MATHEMATICAL DOUBLE-STRUCK SMALL G +1D559;1D559;1D559;0068;0068; # (𝕙𝕙; 𝕙𝕙; 𝕙𝕙; h; h; ) MATHEMATICAL DOUBLE-STRUCK SMALL H +1D55A;1D55A;1D55A;0069;0069; # (𝕚𝕚; 𝕚𝕚; 𝕚𝕚; i; i; ) MATHEMATICAL DOUBLE-STRUCK SMALL I +1D55B;1D55B;1D55B;006A;006A; # (𝕛𝕛; 𝕛𝕛; 𝕛𝕛; j; j; ) MATHEMATICAL DOUBLE-STRUCK SMALL J +1D55C;1D55C;1D55C;006B;006B; # (𝕜𝕜; 𝕜𝕜; 𝕜𝕜; k; k; ) MATHEMATICAL DOUBLE-STRUCK SMALL K +1D55D;1D55D;1D55D;006C;006C; # (𝕝𝕝; 𝕝𝕝; 𝕝𝕝; l; l; ) MATHEMATICAL DOUBLE-STRUCK SMALL L +1D55E;1D55E;1D55E;006D;006D; # (𝕞𝕞; 𝕞𝕞; 𝕞𝕞; m; m; ) MATHEMATICAL DOUBLE-STRUCK SMALL M +1D55F;1D55F;1D55F;006E;006E; # (𝕟𝕟; 𝕟𝕟; 𝕟𝕟; n; n; ) MATHEMATICAL DOUBLE-STRUCK SMALL N +1D560;1D560;1D560;006F;006F; # (𝕠𝕠; 𝕠𝕠; 𝕠𝕠; o; o; ) MATHEMATICAL DOUBLE-STRUCK SMALL O +1D561;1D561;1D561;0070;0070; # (𝕡𝕡; 𝕡𝕡; 𝕡𝕡; p; p; ) MATHEMATICAL DOUBLE-STRUCK SMALL P +1D562;1D562;1D562;0071;0071; # (𝕢𝕢; 𝕢𝕢; 𝕢𝕢; q; q; ) MATHEMATICAL DOUBLE-STRUCK SMALL Q +1D563;1D563;1D563;0072;0072; # (𝕣𝕣; 𝕣𝕣; 𝕣𝕣; r; r; ) MATHEMATICAL DOUBLE-STRUCK SMALL R +1D564;1D564;1D564;0073;0073; # (𝕤𝕤; 𝕤𝕤; 𝕤𝕤; s; s; ) MATHEMATICAL DOUBLE-STRUCK SMALL S +1D565;1D565;1D565;0074;0074; # (𝕥𝕥; 𝕥𝕥; 𝕥𝕥; t; t; ) MATHEMATICAL DOUBLE-STRUCK SMALL T +1D566;1D566;1D566;0075;0075; # (𝕦𝕦; 𝕦𝕦; 𝕦𝕦; u; u; ) MATHEMATICAL DOUBLE-STRUCK SMALL U +1D567;1D567;1D567;0076;0076; # (𝕧𝕧; 𝕧𝕧; 𝕧𝕧; v; v; ) MATHEMATICAL DOUBLE-STRUCK SMALL V +1D568;1D568;1D568;0077;0077; # (𝕨𝕨; 𝕨𝕨; 𝕨𝕨; w; w; ) MATHEMATICAL DOUBLE-STRUCK SMALL W +1D569;1D569;1D569;0078;0078; # (𝕩𝕩; 𝕩𝕩; 𝕩𝕩; x; x; ) MATHEMATICAL DOUBLE-STRUCK SMALL X +1D56A;1D56A;1D56A;0079;0079; # (𝕪𝕪; 𝕪𝕪; 𝕪𝕪; y; y; ) MATHEMATICAL DOUBLE-STRUCK SMALL Y +1D56B;1D56B;1D56B;007A;007A; # (𝕫𝕫; 𝕫𝕫; 𝕫𝕫; z; z; ) MATHEMATICAL DOUBLE-STRUCK SMALL Z +1D56C;1D56C;1D56C;0041;0041; # (𝕬𝕬; 𝕬𝕬; 𝕬𝕬; A; A; ) MATHEMATICAL BOLD FRAKTUR CAPITAL A +1D56D;1D56D;1D56D;0042;0042; # (𝕭𝕭; 𝕭𝕭; 𝕭𝕭; B; B; ) MATHEMATICAL BOLD FRAKTUR CAPITAL B +1D56E;1D56E;1D56E;0043;0043; # (𝕮𝕮; 𝕮𝕮; 𝕮𝕮; C; C; ) MATHEMATICAL BOLD FRAKTUR CAPITAL C +1D56F;1D56F;1D56F;0044;0044; # (𝕯𝕯; 𝕯𝕯; 𝕯𝕯; D; D; ) MATHEMATICAL BOLD FRAKTUR CAPITAL D +1D570;1D570;1D570;0045;0045; # (𝕰𝕰; 𝕰𝕰; 𝕰𝕰; E; E; ) MATHEMATICAL BOLD FRAKTUR CAPITAL E +1D571;1D571;1D571;0046;0046; # (𝕱𝕱; 𝕱𝕱; 𝕱𝕱; F; F; ) MATHEMATICAL BOLD FRAKTUR CAPITAL F +1D572;1D572;1D572;0047;0047; # (𝕲𝕲; 𝕲𝕲; 𝕲𝕲; G; G; ) MATHEMATICAL BOLD FRAKTUR CAPITAL G +1D573;1D573;1D573;0048;0048; # (𝕳𝕳; 𝕳𝕳; 𝕳𝕳; H; H; ) MATHEMATICAL BOLD FRAKTUR CAPITAL H +1D574;1D574;1D574;0049;0049; # (𝕴𝕴; 𝕴𝕴; 𝕴𝕴; I; I; ) MATHEMATICAL BOLD FRAKTUR CAPITAL I +1D575;1D575;1D575;004A;004A; # (𝕵𝕵; 𝕵𝕵; 𝕵𝕵; J; J; ) MATHEMATICAL BOLD FRAKTUR CAPITAL J +1D576;1D576;1D576;004B;004B; # (𝕶𝕶; 𝕶𝕶; 𝕶𝕶; K; K; ) MATHEMATICAL BOLD FRAKTUR CAPITAL K +1D577;1D577;1D577;004C;004C; # (𝕷𝕷; 𝕷𝕷; 𝕷𝕷; L; L; ) MATHEMATICAL BOLD FRAKTUR CAPITAL L +1D578;1D578;1D578;004D;004D; # (𝕸𝕸; 𝕸𝕸; 𝕸𝕸; M; M; ) MATHEMATICAL BOLD FRAKTUR CAPITAL M +1D579;1D579;1D579;004E;004E; # (𝕹𝕹; 𝕹𝕹; 𝕹𝕹; N; N; ) MATHEMATICAL BOLD FRAKTUR CAPITAL N +1D57A;1D57A;1D57A;004F;004F; # (𝕺𝕺; 𝕺𝕺; 𝕺𝕺; O; O; ) MATHEMATICAL BOLD FRAKTUR CAPITAL O +1D57B;1D57B;1D57B;0050;0050; # (𝕻𝕻; 𝕻𝕻; 𝕻𝕻; P; P; ) MATHEMATICAL BOLD FRAKTUR CAPITAL P +1D57C;1D57C;1D57C;0051;0051; # (𝕼𝕼; 𝕼𝕼; 𝕼𝕼; Q; Q; ) MATHEMATICAL BOLD FRAKTUR CAPITAL Q +1D57D;1D57D;1D57D;0052;0052; # (𝕽𝕽; 𝕽𝕽; 𝕽𝕽; R; R; ) MATHEMATICAL BOLD FRAKTUR CAPITAL R +1D57E;1D57E;1D57E;0053;0053; # (𝕾𝕾; 𝕾𝕾; 𝕾𝕾; S; S; ) MATHEMATICAL BOLD FRAKTUR CAPITAL S +1D57F;1D57F;1D57F;0054;0054; # (𝕿𝕿; 𝕿𝕿; 𝕿𝕿; T; T; ) MATHEMATICAL BOLD FRAKTUR CAPITAL T +1D580;1D580;1D580;0055;0055; # (𝖀𝖀; 𝖀𝖀; 𝖀𝖀; U; U; ) MATHEMATICAL BOLD FRAKTUR CAPITAL U +1D581;1D581;1D581;0056;0056; # (𝖁𝖁; 𝖁𝖁; 𝖁𝖁; V; V; ) MATHEMATICAL BOLD FRAKTUR CAPITAL V +1D582;1D582;1D582;0057;0057; # (𝖂𝖂; 𝖂𝖂; 𝖂𝖂; W; W; ) MATHEMATICAL BOLD FRAKTUR CAPITAL W +1D583;1D583;1D583;0058;0058; # (𝖃𝖃; 𝖃𝖃; 𝖃𝖃; X; X; ) MATHEMATICAL BOLD FRAKTUR CAPITAL X +1D584;1D584;1D584;0059;0059; # (𝖄𝖄; 𝖄𝖄; 𝖄𝖄; Y; Y; ) MATHEMATICAL BOLD FRAKTUR CAPITAL Y +1D585;1D585;1D585;005A;005A; # (𝖅𝖅; 𝖅𝖅; 𝖅𝖅; Z; Z; ) MATHEMATICAL BOLD FRAKTUR CAPITAL Z +1D586;1D586;1D586;0061;0061; # (𝖆𝖆; 𝖆𝖆; 𝖆𝖆; a; a; ) MATHEMATICAL BOLD FRAKTUR SMALL A +1D587;1D587;1D587;0062;0062; # (𝖇𝖇; 𝖇𝖇; 𝖇𝖇; b; b; ) MATHEMATICAL BOLD FRAKTUR SMALL B +1D588;1D588;1D588;0063;0063; # (𝖈𝖈; 𝖈𝖈; 𝖈𝖈; c; c; ) MATHEMATICAL BOLD FRAKTUR SMALL C +1D589;1D589;1D589;0064;0064; # (𝖉𝖉; 𝖉𝖉; 𝖉𝖉; d; d; ) MATHEMATICAL BOLD FRAKTUR SMALL D +1D58A;1D58A;1D58A;0065;0065; # (𝖊𝖊; 𝖊𝖊; 𝖊𝖊; e; e; ) MATHEMATICAL BOLD FRAKTUR SMALL E +1D58B;1D58B;1D58B;0066;0066; # (𝖋𝖋; 𝖋𝖋; 𝖋𝖋; f; f; ) MATHEMATICAL BOLD FRAKTUR SMALL F +1D58C;1D58C;1D58C;0067;0067; # (𝖌𝖌; 𝖌𝖌; 𝖌𝖌; g; g; ) MATHEMATICAL BOLD FRAKTUR SMALL G +1D58D;1D58D;1D58D;0068;0068; # (𝖍𝖍; 𝖍𝖍; 𝖍𝖍; h; h; ) MATHEMATICAL BOLD FRAKTUR SMALL H +1D58E;1D58E;1D58E;0069;0069; # (𝖎𝖎; 𝖎𝖎; 𝖎𝖎; i; i; ) MATHEMATICAL BOLD FRAKTUR SMALL I +1D58F;1D58F;1D58F;006A;006A; # (𝖏𝖏; 𝖏𝖏; 𝖏𝖏; j; j; ) MATHEMATICAL BOLD FRAKTUR SMALL J +1D590;1D590;1D590;006B;006B; # (𝖐𝖐; 𝖐𝖐; 𝖐𝖐; k; k; ) MATHEMATICAL BOLD FRAKTUR SMALL K +1D591;1D591;1D591;006C;006C; # (𝖑𝖑; 𝖑𝖑; 𝖑𝖑; l; l; ) MATHEMATICAL BOLD FRAKTUR SMALL L +1D592;1D592;1D592;006D;006D; # (𝖒𝖒; 𝖒𝖒; 𝖒𝖒; m; m; ) MATHEMATICAL BOLD FRAKTUR SMALL M +1D593;1D593;1D593;006E;006E; # (𝖓𝖓; 𝖓𝖓; 𝖓𝖓; n; n; ) MATHEMATICAL BOLD FRAKTUR SMALL N +1D594;1D594;1D594;006F;006F; # (𝖔𝖔; 𝖔𝖔; 𝖔𝖔; o; o; ) MATHEMATICAL BOLD FRAKTUR SMALL O +1D595;1D595;1D595;0070;0070; # (𝖕𝖕; 𝖕𝖕; 𝖕𝖕; p; p; ) MATHEMATICAL BOLD FRAKTUR SMALL P +1D596;1D596;1D596;0071;0071; # (𝖖𝖖; 𝖖𝖖; 𝖖𝖖; q; q; ) MATHEMATICAL BOLD FRAKTUR SMALL Q +1D597;1D597;1D597;0072;0072; # (𝖗𝖗; 𝖗𝖗; 𝖗𝖗; r; r; ) MATHEMATICAL BOLD FRAKTUR SMALL R +1D598;1D598;1D598;0073;0073; # (𝖘𝖘; 𝖘𝖘; 𝖘𝖘; s; s; ) MATHEMATICAL BOLD FRAKTUR SMALL S +1D599;1D599;1D599;0074;0074; # (𝖙𝖙; 𝖙𝖙; 𝖙𝖙; t; t; ) MATHEMATICAL BOLD FRAKTUR SMALL T +1D59A;1D59A;1D59A;0075;0075; # (𝖚𝖚; 𝖚𝖚; 𝖚𝖚; u; u; ) MATHEMATICAL BOLD FRAKTUR SMALL U +1D59B;1D59B;1D59B;0076;0076; # (𝖛𝖛; 𝖛𝖛; 𝖛𝖛; v; v; ) MATHEMATICAL BOLD FRAKTUR SMALL V +1D59C;1D59C;1D59C;0077;0077; # (𝖜𝖜; 𝖜𝖜; 𝖜𝖜; w; w; ) MATHEMATICAL BOLD FRAKTUR SMALL W +1D59D;1D59D;1D59D;0078;0078; # (𝖝𝖝; 𝖝𝖝; 𝖝𝖝; x; x; ) MATHEMATICAL BOLD FRAKTUR SMALL X +1D59E;1D59E;1D59E;0079;0079; # (𝖞𝖞; 𝖞𝖞; 𝖞𝖞; y; y; ) MATHEMATICAL BOLD FRAKTUR SMALL Y +1D59F;1D59F;1D59F;007A;007A; # (𝖟𝖟; 𝖟𝖟; 𝖟𝖟; z; z; ) MATHEMATICAL BOLD FRAKTUR SMALL Z +1D5A0;1D5A0;1D5A0;0041;0041; # (𝖠𝖠; 𝖠𝖠; 𝖠𝖠; A; A; ) MATHEMATICAL SANS-SERIF CAPITAL A +1D5A1;1D5A1;1D5A1;0042;0042; # (𝖡𝖡; 𝖡𝖡; 𝖡𝖡; B; B; ) MATHEMATICAL SANS-SERIF CAPITAL B +1D5A2;1D5A2;1D5A2;0043;0043; # (𝖢𝖢; 𝖢𝖢; 𝖢𝖢; C; C; ) MATHEMATICAL SANS-SERIF CAPITAL C +1D5A3;1D5A3;1D5A3;0044;0044; # (𝖣𝖣; 𝖣𝖣; 𝖣𝖣; D; D; ) MATHEMATICAL SANS-SERIF CAPITAL D +1D5A4;1D5A4;1D5A4;0045;0045; # (𝖤𝖤; 𝖤𝖤; 𝖤𝖤; E; E; ) MATHEMATICAL SANS-SERIF CAPITAL E +1D5A5;1D5A5;1D5A5;0046;0046; # (𝖥𝖥; 𝖥𝖥; 𝖥𝖥; F; F; ) MATHEMATICAL SANS-SERIF CAPITAL F +1D5A6;1D5A6;1D5A6;0047;0047; # (𝖦𝖦; 𝖦𝖦; 𝖦𝖦; G; G; ) MATHEMATICAL SANS-SERIF CAPITAL G +1D5A7;1D5A7;1D5A7;0048;0048; # (𝖧𝖧; 𝖧𝖧; 𝖧𝖧; H; H; ) MATHEMATICAL SANS-SERIF CAPITAL H +1D5A8;1D5A8;1D5A8;0049;0049; # (𝖨𝖨; 𝖨𝖨; 𝖨𝖨; I; I; ) MATHEMATICAL SANS-SERIF CAPITAL I +1D5A9;1D5A9;1D5A9;004A;004A; # (𝖩𝖩; 𝖩𝖩; 𝖩𝖩; J; J; ) MATHEMATICAL SANS-SERIF CAPITAL J +1D5AA;1D5AA;1D5AA;004B;004B; # (𝖪𝖪; 𝖪𝖪; 𝖪𝖪; K; K; ) MATHEMATICAL SANS-SERIF CAPITAL K +1D5AB;1D5AB;1D5AB;004C;004C; # (𝖫𝖫; 𝖫𝖫; 𝖫𝖫; L; L; ) MATHEMATICAL SANS-SERIF CAPITAL L +1D5AC;1D5AC;1D5AC;004D;004D; # (𝖬𝖬; 𝖬𝖬; 𝖬𝖬; M; M; ) MATHEMATICAL SANS-SERIF CAPITAL M +1D5AD;1D5AD;1D5AD;004E;004E; # (𝖭𝖭; 𝖭𝖭; 𝖭𝖭; N; N; ) MATHEMATICAL SANS-SERIF CAPITAL N +1D5AE;1D5AE;1D5AE;004F;004F; # (𝖮𝖮; 𝖮𝖮; 𝖮𝖮; O; O; ) MATHEMATICAL SANS-SERIF CAPITAL O +1D5AF;1D5AF;1D5AF;0050;0050; # (𝖯𝖯; 𝖯𝖯; 𝖯𝖯; P; P; ) MATHEMATICAL SANS-SERIF CAPITAL P +1D5B0;1D5B0;1D5B0;0051;0051; # (𝖰𝖰; 𝖰𝖰; 𝖰𝖰; Q; Q; ) MATHEMATICAL SANS-SERIF CAPITAL Q +1D5B1;1D5B1;1D5B1;0052;0052; # (𝖱𝖱; 𝖱𝖱; 𝖱𝖱; R; R; ) MATHEMATICAL SANS-SERIF CAPITAL R +1D5B2;1D5B2;1D5B2;0053;0053; # (𝖲𝖲; 𝖲𝖲; 𝖲𝖲; S; S; ) MATHEMATICAL SANS-SERIF CAPITAL S +1D5B3;1D5B3;1D5B3;0054;0054; # (𝖳𝖳; 𝖳𝖳; 𝖳𝖳; T; T; ) MATHEMATICAL SANS-SERIF CAPITAL T +1D5B4;1D5B4;1D5B4;0055;0055; # (𝖴𝖴; 𝖴𝖴; 𝖴𝖴; U; U; ) MATHEMATICAL SANS-SERIF CAPITAL U +1D5B5;1D5B5;1D5B5;0056;0056; # (𝖵𝖵; 𝖵𝖵; 𝖵𝖵; V; V; ) MATHEMATICAL SANS-SERIF CAPITAL V +1D5B6;1D5B6;1D5B6;0057;0057; # (𝖶𝖶; 𝖶𝖶; 𝖶𝖶; W; W; ) MATHEMATICAL SANS-SERIF CAPITAL W +1D5B7;1D5B7;1D5B7;0058;0058; # (𝖷𝖷; 𝖷𝖷; 𝖷𝖷; X; X; ) MATHEMATICAL SANS-SERIF CAPITAL X +1D5B8;1D5B8;1D5B8;0059;0059; # (𝖸𝖸; 𝖸𝖸; 𝖸𝖸; Y; Y; ) MATHEMATICAL SANS-SERIF CAPITAL Y +1D5B9;1D5B9;1D5B9;005A;005A; # (𝖹𝖹; 𝖹𝖹; 𝖹𝖹; Z; Z; ) MATHEMATICAL SANS-SERIF CAPITAL Z +1D5BA;1D5BA;1D5BA;0061;0061; # (𝖺𝖺; 𝖺𝖺; 𝖺𝖺; a; a; ) MATHEMATICAL SANS-SERIF SMALL A +1D5BB;1D5BB;1D5BB;0062;0062; # (𝖻𝖻; 𝖻𝖻; 𝖻𝖻; b; b; ) MATHEMATICAL SANS-SERIF SMALL B +1D5BC;1D5BC;1D5BC;0063;0063; # (𝖼𝖼; 𝖼𝖼; 𝖼𝖼; c; c; ) MATHEMATICAL SANS-SERIF SMALL C +1D5BD;1D5BD;1D5BD;0064;0064; # (𝖽𝖽; 𝖽𝖽; 𝖽𝖽; d; d; ) MATHEMATICAL SANS-SERIF SMALL D +1D5BE;1D5BE;1D5BE;0065;0065; # (𝖾𝖾; 𝖾𝖾; 𝖾𝖾; e; e; ) MATHEMATICAL SANS-SERIF SMALL E +1D5BF;1D5BF;1D5BF;0066;0066; # (𝖿𝖿; 𝖿𝖿; 𝖿𝖿; f; f; ) MATHEMATICAL SANS-SERIF SMALL F +1D5C0;1D5C0;1D5C0;0067;0067; # (𝗀𝗀; 𝗀𝗀; 𝗀𝗀; g; g; ) MATHEMATICAL SANS-SERIF SMALL G +1D5C1;1D5C1;1D5C1;0068;0068; # (𝗁𝗁; 𝗁𝗁; 𝗁𝗁; h; h; ) MATHEMATICAL SANS-SERIF SMALL H +1D5C2;1D5C2;1D5C2;0069;0069; # (𝗂𝗂; 𝗂𝗂; 𝗂𝗂; i; i; ) MATHEMATICAL SANS-SERIF SMALL I +1D5C3;1D5C3;1D5C3;006A;006A; # (𝗃𝗃; 𝗃𝗃; 𝗃𝗃; j; j; ) MATHEMATICAL SANS-SERIF SMALL J +1D5C4;1D5C4;1D5C4;006B;006B; # (𝗄𝗄; 𝗄𝗄; 𝗄𝗄; k; k; ) MATHEMATICAL SANS-SERIF SMALL K +1D5C5;1D5C5;1D5C5;006C;006C; # (𝗅𝗅; 𝗅𝗅; 𝗅𝗅; l; l; ) MATHEMATICAL SANS-SERIF SMALL L +1D5C6;1D5C6;1D5C6;006D;006D; # (𝗆𝗆; 𝗆𝗆; 𝗆𝗆; m; m; ) MATHEMATICAL SANS-SERIF SMALL M +1D5C7;1D5C7;1D5C7;006E;006E; # (𝗇𝗇; 𝗇𝗇; 𝗇𝗇; n; n; ) MATHEMATICAL SANS-SERIF SMALL N +1D5C8;1D5C8;1D5C8;006F;006F; # (𝗈𝗈; 𝗈𝗈; 𝗈𝗈; o; o; ) MATHEMATICAL SANS-SERIF SMALL O +1D5C9;1D5C9;1D5C9;0070;0070; # (𝗉𝗉; 𝗉𝗉; 𝗉𝗉; p; p; ) MATHEMATICAL SANS-SERIF SMALL P +1D5CA;1D5CA;1D5CA;0071;0071; # (𝗊𝗊; 𝗊𝗊; 𝗊𝗊; q; q; ) MATHEMATICAL SANS-SERIF SMALL Q +1D5CB;1D5CB;1D5CB;0072;0072; # (𝗋𝗋; 𝗋𝗋; 𝗋𝗋; r; r; ) MATHEMATICAL SANS-SERIF SMALL R +1D5CC;1D5CC;1D5CC;0073;0073; # (𝗌𝗌; 𝗌𝗌; 𝗌𝗌; s; s; ) MATHEMATICAL SANS-SERIF SMALL S +1D5CD;1D5CD;1D5CD;0074;0074; # (𝗍𝗍; 𝗍𝗍; 𝗍𝗍; t; t; ) MATHEMATICAL SANS-SERIF SMALL T +1D5CE;1D5CE;1D5CE;0075;0075; # (𝗎𝗎; 𝗎𝗎; 𝗎𝗎; u; u; ) MATHEMATICAL SANS-SERIF SMALL U +1D5CF;1D5CF;1D5CF;0076;0076; # (𝗏𝗏; 𝗏𝗏; 𝗏𝗏; v; v; ) MATHEMATICAL SANS-SERIF SMALL V +1D5D0;1D5D0;1D5D0;0077;0077; # (𝗐𝗐; 𝗐𝗐; 𝗐𝗐; w; w; ) MATHEMATICAL SANS-SERIF SMALL W +1D5D1;1D5D1;1D5D1;0078;0078; # (𝗑𝗑; 𝗑𝗑; 𝗑𝗑; x; x; ) MATHEMATICAL SANS-SERIF SMALL X +1D5D2;1D5D2;1D5D2;0079;0079; # (𝗒𝗒; 𝗒𝗒; 𝗒𝗒; y; y; ) MATHEMATICAL SANS-SERIF SMALL Y +1D5D3;1D5D3;1D5D3;007A;007A; # (𝗓𝗓; 𝗓𝗓; 𝗓𝗓; z; z; ) MATHEMATICAL SANS-SERIF SMALL Z +1D5D4;1D5D4;1D5D4;0041;0041; # (𝗔𝗔; 𝗔𝗔; 𝗔𝗔; A; A; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL A +1D5D5;1D5D5;1D5D5;0042;0042; # (𝗕𝗕; 𝗕𝗕; 𝗕𝗕; B; B; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL B +1D5D6;1D5D6;1D5D6;0043;0043; # (𝗖𝗖; 𝗖𝗖; 𝗖𝗖; C; C; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL C +1D5D7;1D5D7;1D5D7;0044;0044; # (𝗗𝗗; 𝗗𝗗; 𝗗𝗗; D; D; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL D +1D5D8;1D5D8;1D5D8;0045;0045; # (𝗘𝗘; 𝗘𝗘; 𝗘𝗘; E; E; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL E +1D5D9;1D5D9;1D5D9;0046;0046; # (𝗙𝗙; 𝗙𝗙; 𝗙𝗙; F; F; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL F +1D5DA;1D5DA;1D5DA;0047;0047; # (𝗚𝗚; 𝗚𝗚; 𝗚𝗚; G; G; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL G +1D5DB;1D5DB;1D5DB;0048;0048; # (𝗛𝗛; 𝗛𝗛; 𝗛𝗛; H; H; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL H +1D5DC;1D5DC;1D5DC;0049;0049; # (𝗜𝗜; 𝗜𝗜; 𝗜𝗜; I; I; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL I +1D5DD;1D5DD;1D5DD;004A;004A; # (𝗝𝗝; 𝗝𝗝; 𝗝𝗝; J; J; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL J +1D5DE;1D5DE;1D5DE;004B;004B; # (𝗞𝗞; 𝗞𝗞; 𝗞𝗞; K; K; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL K +1D5DF;1D5DF;1D5DF;004C;004C; # (𝗟𝗟; 𝗟𝗟; 𝗟𝗟; L; L; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL L +1D5E0;1D5E0;1D5E0;004D;004D; # (𝗠𝗠; 𝗠𝗠; 𝗠𝗠; M; M; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL M +1D5E1;1D5E1;1D5E1;004E;004E; # (𝗡𝗡; 𝗡𝗡; 𝗡𝗡; N; N; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL N +1D5E2;1D5E2;1D5E2;004F;004F; # (𝗢𝗢; 𝗢𝗢; 𝗢𝗢; O; O; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL O +1D5E3;1D5E3;1D5E3;0050;0050; # (𝗣𝗣; 𝗣𝗣; 𝗣𝗣; P; P; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL P +1D5E4;1D5E4;1D5E4;0051;0051; # (𝗤𝗤; 𝗤𝗤; 𝗤𝗤; Q; Q; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL Q +1D5E5;1D5E5;1D5E5;0052;0052; # (𝗥𝗥; 𝗥𝗥; 𝗥𝗥; R; R; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL R +1D5E6;1D5E6;1D5E6;0053;0053; # (𝗦𝗦; 𝗦𝗦; 𝗦𝗦; S; S; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL S +1D5E7;1D5E7;1D5E7;0054;0054; # (𝗧𝗧; 𝗧𝗧; 𝗧𝗧; T; T; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL T +1D5E8;1D5E8;1D5E8;0055;0055; # (𝗨𝗨; 𝗨𝗨; 𝗨𝗨; U; U; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL U +1D5E9;1D5E9;1D5E9;0056;0056; # (𝗩𝗩; 𝗩𝗩; 𝗩𝗩; V; V; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL V +1D5EA;1D5EA;1D5EA;0057;0057; # (𝗪𝗪; 𝗪𝗪; 𝗪𝗪; W; W; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL W +1D5EB;1D5EB;1D5EB;0058;0058; # (𝗫𝗫; 𝗫𝗫; 𝗫𝗫; X; X; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL X +1D5EC;1D5EC;1D5EC;0059;0059; # (𝗬𝗬; 𝗬𝗬; 𝗬𝗬; Y; Y; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL Y +1D5ED;1D5ED;1D5ED;005A;005A; # (𝗭𝗭; 𝗭𝗭; 𝗭𝗭; Z; Z; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL Z +1D5EE;1D5EE;1D5EE;0061;0061; # (𝗮𝗮; 𝗮𝗮; 𝗮𝗮; a; a; ) MATHEMATICAL SANS-SERIF BOLD SMALL A +1D5EF;1D5EF;1D5EF;0062;0062; # (𝗯𝗯; 𝗯𝗯; 𝗯𝗯; b; b; ) MATHEMATICAL SANS-SERIF BOLD SMALL B +1D5F0;1D5F0;1D5F0;0063;0063; # (𝗰𝗰; 𝗰𝗰; 𝗰𝗰; c; c; ) MATHEMATICAL SANS-SERIF BOLD SMALL C +1D5F1;1D5F1;1D5F1;0064;0064; # (𝗱𝗱; 𝗱𝗱; 𝗱𝗱; d; d; ) MATHEMATICAL SANS-SERIF BOLD SMALL D +1D5F2;1D5F2;1D5F2;0065;0065; # (𝗲𝗲; 𝗲𝗲; 𝗲𝗲; e; e; ) MATHEMATICAL SANS-SERIF BOLD SMALL E +1D5F3;1D5F3;1D5F3;0066;0066; # (𝗳𝗳; 𝗳𝗳; 𝗳𝗳; f; f; ) MATHEMATICAL SANS-SERIF BOLD SMALL F +1D5F4;1D5F4;1D5F4;0067;0067; # (𝗴𝗴; 𝗴𝗴; 𝗴𝗴; g; g; ) MATHEMATICAL SANS-SERIF BOLD SMALL G +1D5F5;1D5F5;1D5F5;0068;0068; # (𝗵𝗵; 𝗵𝗵; 𝗵𝗵; h; h; ) MATHEMATICAL SANS-SERIF BOLD SMALL H +1D5F6;1D5F6;1D5F6;0069;0069; # (𝗶𝗶; 𝗶𝗶; 𝗶𝗶; i; i; ) MATHEMATICAL SANS-SERIF BOLD SMALL I +1D5F7;1D5F7;1D5F7;006A;006A; # (𝗷𝗷; 𝗷𝗷; 𝗷𝗷; j; j; ) MATHEMATICAL SANS-SERIF BOLD SMALL J +1D5F8;1D5F8;1D5F8;006B;006B; # (𝗸𝗸; 𝗸𝗸; 𝗸𝗸; k; k; ) MATHEMATICAL SANS-SERIF BOLD SMALL K +1D5F9;1D5F9;1D5F9;006C;006C; # (𝗹𝗹; 𝗹𝗹; 𝗹𝗹; l; l; ) MATHEMATICAL SANS-SERIF BOLD SMALL L +1D5FA;1D5FA;1D5FA;006D;006D; # (𝗺𝗺; 𝗺𝗺; 𝗺𝗺; m; m; ) MATHEMATICAL SANS-SERIF BOLD SMALL M +1D5FB;1D5FB;1D5FB;006E;006E; # (𝗻𝗻; 𝗻𝗻; 𝗻𝗻; n; n; ) MATHEMATICAL SANS-SERIF BOLD SMALL N +1D5FC;1D5FC;1D5FC;006F;006F; # (𝗼𝗼; 𝗼𝗼; 𝗼𝗼; o; o; ) MATHEMATICAL SANS-SERIF BOLD SMALL O +1D5FD;1D5FD;1D5FD;0070;0070; # (𝗽𝗽; 𝗽𝗽; 𝗽𝗽; p; p; ) MATHEMATICAL SANS-SERIF BOLD SMALL P +1D5FE;1D5FE;1D5FE;0071;0071; # (𝗾𝗾; 𝗾𝗾; 𝗾𝗾; q; q; ) MATHEMATICAL SANS-SERIF BOLD SMALL Q +1D5FF;1D5FF;1D5FF;0072;0072; # (𝗿𝗿; 𝗿𝗿; 𝗿𝗿; r; r; ) MATHEMATICAL SANS-SERIF BOLD SMALL R +1D600;1D600;1D600;0073;0073; # (𝘀𝘀; 𝘀𝘀; 𝘀𝘀; s; s; ) MATHEMATICAL SANS-SERIF BOLD SMALL S +1D601;1D601;1D601;0074;0074; # (𝘁𝘁; 𝘁𝘁; 𝘁𝘁; t; t; ) MATHEMATICAL SANS-SERIF BOLD SMALL T +1D602;1D602;1D602;0075;0075; # (𝘂𝘂; 𝘂𝘂; 𝘂𝘂; u; u; ) MATHEMATICAL SANS-SERIF BOLD SMALL U +1D603;1D603;1D603;0076;0076; # (𝘃𝘃; 𝘃𝘃; 𝘃𝘃; v; v; ) MATHEMATICAL SANS-SERIF BOLD SMALL V +1D604;1D604;1D604;0077;0077; # (𝘄𝘄; 𝘄𝘄; 𝘄𝘄; w; w; ) MATHEMATICAL SANS-SERIF BOLD SMALL W +1D605;1D605;1D605;0078;0078; # (𝘅𝘅; 𝘅𝘅; 𝘅𝘅; x; x; ) MATHEMATICAL SANS-SERIF BOLD SMALL X +1D606;1D606;1D606;0079;0079; # (𝘆𝘆; 𝘆𝘆; 𝘆𝘆; y; y; ) MATHEMATICAL SANS-SERIF BOLD SMALL Y +1D607;1D607;1D607;007A;007A; # (𝘇𝘇; 𝘇𝘇; 𝘇𝘇; z; z; ) MATHEMATICAL SANS-SERIF BOLD SMALL Z +1D608;1D608;1D608;0041;0041; # (𝘈𝘈; 𝘈𝘈; 𝘈𝘈; A; A; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL A +1D609;1D609;1D609;0042;0042; # (𝘉𝘉; 𝘉𝘉; 𝘉𝘉; B; B; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL B +1D60A;1D60A;1D60A;0043;0043; # (𝘊𝘊; 𝘊𝘊; 𝘊𝘊; C; C; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL C +1D60B;1D60B;1D60B;0044;0044; # (𝘋𝘋; 𝘋𝘋; 𝘋𝘋; D; D; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL D +1D60C;1D60C;1D60C;0045;0045; # (𝘌𝘌; 𝘌𝘌; 𝘌𝘌; E; E; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL E +1D60D;1D60D;1D60D;0046;0046; # (𝘍𝘍; 𝘍𝘍; 𝘍𝘍; F; F; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL F +1D60E;1D60E;1D60E;0047;0047; # (𝘎𝘎; 𝘎𝘎; 𝘎𝘎; G; G; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL G +1D60F;1D60F;1D60F;0048;0048; # (𝘏𝘏; 𝘏𝘏; 𝘏𝘏; H; H; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL H +1D610;1D610;1D610;0049;0049; # (𝘐𝘐; 𝘐𝘐; 𝘐𝘐; I; I; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL I +1D611;1D611;1D611;004A;004A; # (𝘑𝘑; 𝘑𝘑; 𝘑𝘑; J; J; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL J +1D612;1D612;1D612;004B;004B; # (𝘒𝘒; 𝘒𝘒; 𝘒𝘒; K; K; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL K +1D613;1D613;1D613;004C;004C; # (𝘓𝘓; 𝘓𝘓; 𝘓𝘓; L; L; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL L +1D614;1D614;1D614;004D;004D; # (𝘔𝘔; 𝘔𝘔; 𝘔𝘔; M; M; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL M +1D615;1D615;1D615;004E;004E; # (𝘕𝘕; 𝘕𝘕; 𝘕𝘕; N; N; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL N +1D616;1D616;1D616;004F;004F; # (𝘖𝘖; 𝘖𝘖; 𝘖𝘖; O; O; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL O +1D617;1D617;1D617;0050;0050; # (𝘗𝘗; 𝘗𝘗; 𝘗𝘗; P; P; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL P +1D618;1D618;1D618;0051;0051; # (𝘘𝘘; 𝘘𝘘; 𝘘𝘘; Q; Q; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q +1D619;1D619;1D619;0052;0052; # (𝘙𝘙; 𝘙𝘙; 𝘙𝘙; R; R; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL R +1D61A;1D61A;1D61A;0053;0053; # (𝘚𝘚; 𝘚𝘚; 𝘚𝘚; S; S; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL S +1D61B;1D61B;1D61B;0054;0054; # (𝘛𝘛; 𝘛𝘛; 𝘛𝘛; T; T; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL T +1D61C;1D61C;1D61C;0055;0055; # (𝘜𝘜; 𝘜𝘜; 𝘜𝘜; U; U; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL U +1D61D;1D61D;1D61D;0056;0056; # (𝘝𝘝; 𝘝𝘝; 𝘝𝘝; V; V; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL V +1D61E;1D61E;1D61E;0057;0057; # (𝘞𝘞; 𝘞𝘞; 𝘞𝘞; W; W; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL W +1D61F;1D61F;1D61F;0058;0058; # (𝘟𝘟; 𝘟𝘟; 𝘟𝘟; X; X; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL X +1D620;1D620;1D620;0059;0059; # (𝘠𝘠; 𝘠𝘠; 𝘠𝘠; Y; Y; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y +1D621;1D621;1D621;005A;005A; # (𝘡𝘡; 𝘡𝘡; 𝘡𝘡; Z; Z; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z +1D622;1D622;1D622;0061;0061; # (𝘢𝘢; 𝘢𝘢; 𝘢𝘢; a; a; ) MATHEMATICAL SANS-SERIF ITALIC SMALL A +1D623;1D623;1D623;0062;0062; # (𝘣𝘣; 𝘣𝘣; 𝘣𝘣; b; b; ) MATHEMATICAL SANS-SERIF ITALIC SMALL B +1D624;1D624;1D624;0063;0063; # (𝘤𝘤; 𝘤𝘤; 𝘤𝘤; c; c; ) MATHEMATICAL SANS-SERIF ITALIC SMALL C +1D625;1D625;1D625;0064;0064; # (𝘥𝘥; 𝘥𝘥; 𝘥𝘥; d; d; ) MATHEMATICAL SANS-SERIF ITALIC SMALL D +1D626;1D626;1D626;0065;0065; # (𝘦𝘦; 𝘦𝘦; 𝘦𝘦; e; e; ) MATHEMATICAL SANS-SERIF ITALIC SMALL E +1D627;1D627;1D627;0066;0066; # (𝘧𝘧; 𝘧𝘧; 𝘧𝘧; f; f; ) MATHEMATICAL SANS-SERIF ITALIC SMALL F +1D628;1D628;1D628;0067;0067; # (𝘨𝘨; 𝘨𝘨; 𝘨𝘨; g; g; ) MATHEMATICAL SANS-SERIF ITALIC SMALL G +1D629;1D629;1D629;0068;0068; # (𝘩𝘩; 𝘩𝘩; 𝘩𝘩; h; h; ) MATHEMATICAL SANS-SERIF ITALIC SMALL H +1D62A;1D62A;1D62A;0069;0069; # (𝘪𝘪; 𝘪𝘪; 𝘪𝘪; i; i; ) MATHEMATICAL SANS-SERIF ITALIC SMALL I +1D62B;1D62B;1D62B;006A;006A; # (𝘫𝘫; 𝘫𝘫; 𝘫𝘫; j; j; ) MATHEMATICAL SANS-SERIF ITALIC SMALL J +1D62C;1D62C;1D62C;006B;006B; # (𝘬𝘬; 𝘬𝘬; 𝘬𝘬; k; k; ) MATHEMATICAL SANS-SERIF ITALIC SMALL K +1D62D;1D62D;1D62D;006C;006C; # (𝘭𝘭; 𝘭𝘭; 𝘭𝘭; l; l; ) MATHEMATICAL SANS-SERIF ITALIC SMALL L +1D62E;1D62E;1D62E;006D;006D; # (𝘮𝘮; 𝘮𝘮; 𝘮𝘮; m; m; ) MATHEMATICAL SANS-SERIF ITALIC SMALL M +1D62F;1D62F;1D62F;006E;006E; # (𝘯𝘯; 𝘯𝘯; 𝘯𝘯; n; n; ) MATHEMATICAL SANS-SERIF ITALIC SMALL N +1D630;1D630;1D630;006F;006F; # (𝘰𝘰; 𝘰𝘰; 𝘰𝘰; o; o; ) MATHEMATICAL SANS-SERIF ITALIC SMALL O +1D631;1D631;1D631;0070;0070; # (𝘱𝘱; 𝘱𝘱; 𝘱𝘱; p; p; ) MATHEMATICAL SANS-SERIF ITALIC SMALL P +1D632;1D632;1D632;0071;0071; # (𝘲𝘲; 𝘲𝘲; 𝘲𝘲; q; q; ) MATHEMATICAL SANS-SERIF ITALIC SMALL Q +1D633;1D633;1D633;0072;0072; # (𝘳𝘳; 𝘳𝘳; 𝘳𝘳; r; r; ) MATHEMATICAL SANS-SERIF ITALIC SMALL R +1D634;1D634;1D634;0073;0073; # (𝘴𝘴; 𝘴𝘴; 𝘴𝘴; s; s; ) MATHEMATICAL SANS-SERIF ITALIC SMALL S +1D635;1D635;1D635;0074;0074; # (𝘵𝘵; 𝘵𝘵; 𝘵𝘵; t; t; ) MATHEMATICAL SANS-SERIF ITALIC SMALL T +1D636;1D636;1D636;0075;0075; # (𝘶𝘶; 𝘶𝘶; 𝘶𝘶; u; u; ) MATHEMATICAL SANS-SERIF ITALIC SMALL U +1D637;1D637;1D637;0076;0076; # (𝘷𝘷; 𝘷𝘷; 𝘷𝘷; v; v; ) MATHEMATICAL SANS-SERIF ITALIC SMALL V +1D638;1D638;1D638;0077;0077; # (𝘸𝘸; 𝘸𝘸; 𝘸𝘸; w; w; ) MATHEMATICAL SANS-SERIF ITALIC SMALL W +1D639;1D639;1D639;0078;0078; # (𝘹𝘹; 𝘹𝘹; 𝘹𝘹; x; x; ) MATHEMATICAL SANS-SERIF ITALIC SMALL X +1D63A;1D63A;1D63A;0079;0079; # (𝘺𝘺; 𝘺𝘺; 𝘺𝘺; y; y; ) MATHEMATICAL SANS-SERIF ITALIC SMALL Y +1D63B;1D63B;1D63B;007A;007A; # (𝘻𝘻; 𝘻𝘻; 𝘻𝘻; z; z; ) MATHEMATICAL SANS-SERIF ITALIC SMALL Z +1D63C;1D63C;1D63C;0041;0041; # (𝘼𝘼; 𝘼𝘼; 𝘼𝘼; A; A; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A +1D63D;1D63D;1D63D;0042;0042; # (𝘽𝘽; 𝘽𝘽; 𝘽𝘽; B; B; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B +1D63E;1D63E;1D63E;0043;0043; # (𝘾𝘾; 𝘾𝘾; 𝘾𝘾; C; C; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C +1D63F;1D63F;1D63F;0044;0044; # (𝘿𝘿; 𝘿𝘿; 𝘿𝘿; D; D; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D +1D640;1D640;1D640;0045;0045; # (𝙀𝙀; 𝙀𝙀; 𝙀𝙀; E; E; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E +1D641;1D641;1D641;0046;0046; # (𝙁𝙁; 𝙁𝙁; 𝙁𝙁; F; F; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F +1D642;1D642;1D642;0047;0047; # (𝙂𝙂; 𝙂𝙂; 𝙂𝙂; G; G; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G +1D643;1D643;1D643;0048;0048; # (𝙃𝙃; 𝙃𝙃; 𝙃𝙃; H; H; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H +1D644;1D644;1D644;0049;0049; # (𝙄𝙄; 𝙄𝙄; 𝙄𝙄; I; I; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I +1D645;1D645;1D645;004A;004A; # (𝙅𝙅; 𝙅𝙅; 𝙅𝙅; J; J; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J +1D646;1D646;1D646;004B;004B; # (𝙆𝙆; 𝙆𝙆; 𝙆𝙆; K; K; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K +1D647;1D647;1D647;004C;004C; # (𝙇𝙇; 𝙇𝙇; 𝙇𝙇; L; L; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L +1D648;1D648;1D648;004D;004D; # (𝙈𝙈; 𝙈𝙈; 𝙈𝙈; M; M; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M +1D649;1D649;1D649;004E;004E; # (𝙉𝙉; 𝙉𝙉; 𝙉𝙉; N; N; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N +1D64A;1D64A;1D64A;004F;004F; # (𝙊𝙊; 𝙊𝙊; 𝙊𝙊; O; O; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O +1D64B;1D64B;1D64B;0050;0050; # (𝙋𝙋; 𝙋𝙋; 𝙋𝙋; P; P; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P +1D64C;1D64C;1D64C;0051;0051; # (𝙌𝙌; 𝙌𝙌; 𝙌𝙌; Q; Q; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q +1D64D;1D64D;1D64D;0052;0052; # (𝙍𝙍; 𝙍𝙍; 𝙍𝙍; R; R; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R +1D64E;1D64E;1D64E;0053;0053; # (𝙎𝙎; 𝙎𝙎; 𝙎𝙎; S; S; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S +1D64F;1D64F;1D64F;0054;0054; # (𝙏𝙏; 𝙏𝙏; 𝙏𝙏; T; T; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T +1D650;1D650;1D650;0055;0055; # (𝙐𝙐; 𝙐𝙐; 𝙐𝙐; U; U; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U +1D651;1D651;1D651;0056;0056; # (𝙑𝙑; 𝙑𝙑; 𝙑𝙑; V; V; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V +1D652;1D652;1D652;0057;0057; # (𝙒𝙒; 𝙒𝙒; 𝙒𝙒; W; W; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W +1D653;1D653;1D653;0058;0058; # (𝙓𝙓; 𝙓𝙓; 𝙓𝙓; X; X; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X +1D654;1D654;1D654;0059;0059; # (𝙔𝙔; 𝙔𝙔; 𝙔𝙔; Y; Y; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y +1D655;1D655;1D655;005A;005A; # (𝙕𝙕; 𝙕𝙕; 𝙕𝙕; Z; Z; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z +1D656;1D656;1D656;0061;0061; # (𝙖𝙖; 𝙖𝙖; 𝙖𝙖; a; a; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A +1D657;1D657;1D657;0062;0062; # (𝙗𝙗; 𝙗𝙗; 𝙗𝙗; b; b; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B +1D658;1D658;1D658;0063;0063; # (𝙘𝙘; 𝙘𝙘; 𝙘𝙘; c; c; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C +1D659;1D659;1D659;0064;0064; # (𝙙𝙙; 𝙙𝙙; 𝙙𝙙; d; d; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D +1D65A;1D65A;1D65A;0065;0065; # (𝙚𝙚; 𝙚𝙚; 𝙚𝙚; e; e; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E +1D65B;1D65B;1D65B;0066;0066; # (𝙛𝙛; 𝙛𝙛; 𝙛𝙛; f; f; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F +1D65C;1D65C;1D65C;0067;0067; # (𝙜𝙜; 𝙜𝙜; 𝙜𝙜; g; g; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G +1D65D;1D65D;1D65D;0068;0068; # (𝙝𝙝; 𝙝𝙝; 𝙝𝙝; h; h; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H +1D65E;1D65E;1D65E;0069;0069; # (𝙞𝙞; 𝙞𝙞; 𝙞𝙞; i; i; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I +1D65F;1D65F;1D65F;006A;006A; # (𝙟𝙟; 𝙟𝙟; 𝙟𝙟; j; j; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J +1D660;1D660;1D660;006B;006B; # (𝙠𝙠; 𝙠𝙠; 𝙠𝙠; k; k; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K +1D661;1D661;1D661;006C;006C; # (𝙡𝙡; 𝙡𝙡; 𝙡𝙡; l; l; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L +1D662;1D662;1D662;006D;006D; # (𝙢𝙢; 𝙢𝙢; 𝙢𝙢; m; m; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M +1D663;1D663;1D663;006E;006E; # (𝙣𝙣; 𝙣𝙣; 𝙣𝙣; n; n; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N +1D664;1D664;1D664;006F;006F; # (𝙤𝙤; 𝙤𝙤; 𝙤𝙤; o; o; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O +1D665;1D665;1D665;0070;0070; # (𝙥𝙥; 𝙥𝙥; 𝙥𝙥; p; p; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P +1D666;1D666;1D666;0071;0071; # (𝙦𝙦; 𝙦𝙦; 𝙦𝙦; q; q; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q +1D667;1D667;1D667;0072;0072; # (𝙧𝙧; 𝙧𝙧; 𝙧𝙧; r; r; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R +1D668;1D668;1D668;0073;0073; # (𝙨𝙨; 𝙨𝙨; 𝙨𝙨; s; s; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S +1D669;1D669;1D669;0074;0074; # (𝙩𝙩; 𝙩𝙩; 𝙩𝙩; t; t; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T +1D66A;1D66A;1D66A;0075;0075; # (𝙪𝙪; 𝙪𝙪; 𝙪𝙪; u; u; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U +1D66B;1D66B;1D66B;0076;0076; # (𝙫𝙫; 𝙫𝙫; 𝙫𝙫; v; v; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V +1D66C;1D66C;1D66C;0077;0077; # (𝙬𝙬; 𝙬𝙬; 𝙬𝙬; w; w; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W +1D66D;1D66D;1D66D;0078;0078; # (𝙭𝙭; 𝙭𝙭; 𝙭𝙭; x; x; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X +1D66E;1D66E;1D66E;0079;0079; # (𝙮𝙮; 𝙮𝙮; 𝙮𝙮; y; y; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y +1D66F;1D66F;1D66F;007A;007A; # (𝙯𝙯; 𝙯𝙯; 𝙯𝙯; z; z; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z +1D670;1D670;1D670;0041;0041; # (𝙰𝙰; 𝙰𝙰; 𝙰𝙰; A; A; ) MATHEMATICAL MONOSPACE CAPITAL A +1D671;1D671;1D671;0042;0042; # (𝙱𝙱; 𝙱𝙱; 𝙱𝙱; B; B; ) MATHEMATICAL MONOSPACE CAPITAL B +1D672;1D672;1D672;0043;0043; # (𝙲𝙲; 𝙲𝙲; 𝙲𝙲; C; C; ) MATHEMATICAL MONOSPACE CAPITAL C +1D673;1D673;1D673;0044;0044; # (𝙳𝙳; 𝙳𝙳; 𝙳𝙳; D; D; ) MATHEMATICAL MONOSPACE CAPITAL D +1D674;1D674;1D674;0045;0045; # (𝙴𝙴; 𝙴𝙴; 𝙴𝙴; E; E; ) MATHEMATICAL MONOSPACE CAPITAL E +1D675;1D675;1D675;0046;0046; # (𝙵𝙵; 𝙵𝙵; 𝙵𝙵; F; F; ) MATHEMATICAL MONOSPACE CAPITAL F +1D676;1D676;1D676;0047;0047; # (𝙶𝙶; 𝙶𝙶; 𝙶𝙶; G; G; ) MATHEMATICAL MONOSPACE CAPITAL G +1D677;1D677;1D677;0048;0048; # (𝙷𝙷; 𝙷𝙷; 𝙷𝙷; H; H; ) MATHEMATICAL MONOSPACE CAPITAL H +1D678;1D678;1D678;0049;0049; # (𝙸𝙸; 𝙸𝙸; 𝙸𝙸; I; I; ) MATHEMATICAL MONOSPACE CAPITAL I +1D679;1D679;1D679;004A;004A; # (𝙹𝙹; 𝙹𝙹; 𝙹𝙹; J; J; ) MATHEMATICAL MONOSPACE CAPITAL J +1D67A;1D67A;1D67A;004B;004B; # (𝙺𝙺; 𝙺𝙺; 𝙺𝙺; K; K; ) MATHEMATICAL MONOSPACE CAPITAL K +1D67B;1D67B;1D67B;004C;004C; # (𝙻𝙻; 𝙻𝙻; 𝙻𝙻; L; L; ) MATHEMATICAL MONOSPACE CAPITAL L +1D67C;1D67C;1D67C;004D;004D; # (𝙼𝙼; 𝙼𝙼; 𝙼𝙼; M; M; ) MATHEMATICAL MONOSPACE CAPITAL M +1D67D;1D67D;1D67D;004E;004E; # (𝙽𝙽; 𝙽𝙽; 𝙽𝙽; N; N; ) MATHEMATICAL MONOSPACE CAPITAL N +1D67E;1D67E;1D67E;004F;004F; # (𝙾𝙾; 𝙾𝙾; 𝙾𝙾; O; O; ) MATHEMATICAL MONOSPACE CAPITAL O +1D67F;1D67F;1D67F;0050;0050; # (𝙿𝙿; 𝙿𝙿; 𝙿𝙿; P; P; ) MATHEMATICAL MONOSPACE CAPITAL P +1D680;1D680;1D680;0051;0051; # (𝚀𝚀; 𝚀𝚀; 𝚀𝚀; Q; Q; ) MATHEMATICAL MONOSPACE CAPITAL Q +1D681;1D681;1D681;0052;0052; # (𝚁𝚁; 𝚁𝚁; 𝚁𝚁; R; R; ) MATHEMATICAL MONOSPACE CAPITAL R +1D682;1D682;1D682;0053;0053; # (𝚂𝚂; 𝚂𝚂; 𝚂𝚂; S; S; ) MATHEMATICAL MONOSPACE CAPITAL S +1D683;1D683;1D683;0054;0054; # (𝚃𝚃; 𝚃𝚃; 𝚃𝚃; T; T; ) MATHEMATICAL MONOSPACE CAPITAL T +1D684;1D684;1D684;0055;0055; # (𝚄𝚄; 𝚄𝚄; 𝚄𝚄; U; U; ) MATHEMATICAL MONOSPACE CAPITAL U +1D685;1D685;1D685;0056;0056; # (𝚅𝚅; 𝚅𝚅; 𝚅𝚅; V; V; ) MATHEMATICAL MONOSPACE CAPITAL V +1D686;1D686;1D686;0057;0057; # (𝚆𝚆; 𝚆𝚆; 𝚆𝚆; W; W; ) MATHEMATICAL MONOSPACE CAPITAL W +1D687;1D687;1D687;0058;0058; # (𝚇𝚇; 𝚇𝚇; 𝚇𝚇; X; X; ) MATHEMATICAL MONOSPACE CAPITAL X +1D688;1D688;1D688;0059;0059; # (𝚈𝚈; 𝚈𝚈; 𝚈𝚈; Y; Y; ) MATHEMATICAL MONOSPACE CAPITAL Y +1D689;1D689;1D689;005A;005A; # (𝚉𝚉; 𝚉𝚉; 𝚉𝚉; Z; Z; ) MATHEMATICAL MONOSPACE CAPITAL Z +1D68A;1D68A;1D68A;0061;0061; # (𝚊𝚊; 𝚊𝚊; 𝚊𝚊; a; a; ) MATHEMATICAL MONOSPACE SMALL A +1D68B;1D68B;1D68B;0062;0062; # (𝚋𝚋; 𝚋𝚋; 𝚋𝚋; b; b; ) MATHEMATICAL MONOSPACE SMALL B +1D68C;1D68C;1D68C;0063;0063; # (𝚌𝚌; 𝚌𝚌; 𝚌𝚌; c; c; ) MATHEMATICAL MONOSPACE SMALL C +1D68D;1D68D;1D68D;0064;0064; # (𝚍𝚍; 𝚍𝚍; 𝚍𝚍; d; d; ) MATHEMATICAL MONOSPACE SMALL D +1D68E;1D68E;1D68E;0065;0065; # (𝚎𝚎; 𝚎𝚎; 𝚎𝚎; e; e; ) MATHEMATICAL MONOSPACE SMALL E +1D68F;1D68F;1D68F;0066;0066; # (𝚏𝚏; 𝚏𝚏; 𝚏𝚏; f; f; ) MATHEMATICAL MONOSPACE SMALL F +1D690;1D690;1D690;0067;0067; # (𝚐𝚐; 𝚐𝚐; 𝚐𝚐; g; g; ) MATHEMATICAL MONOSPACE SMALL G +1D691;1D691;1D691;0068;0068; # (𝚑𝚑; 𝚑𝚑; 𝚑𝚑; h; h; ) MATHEMATICAL MONOSPACE SMALL H +1D692;1D692;1D692;0069;0069; # (𝚒𝚒; 𝚒𝚒; 𝚒𝚒; i; i; ) MATHEMATICAL MONOSPACE SMALL I +1D693;1D693;1D693;006A;006A; # (𝚓𝚓; 𝚓𝚓; 𝚓𝚓; j; j; ) MATHEMATICAL MONOSPACE SMALL J +1D694;1D694;1D694;006B;006B; # (𝚔𝚔; 𝚔𝚔; 𝚔𝚔; k; k; ) MATHEMATICAL MONOSPACE SMALL K +1D695;1D695;1D695;006C;006C; # (𝚕𝚕; 𝚕𝚕; 𝚕𝚕; l; l; ) MATHEMATICAL MONOSPACE SMALL L +1D696;1D696;1D696;006D;006D; # (𝚖𝚖; 𝚖𝚖; 𝚖𝚖; m; m; ) MATHEMATICAL MONOSPACE SMALL M +1D697;1D697;1D697;006E;006E; # (𝚗𝚗; 𝚗𝚗; 𝚗𝚗; n; n; ) MATHEMATICAL MONOSPACE SMALL N +1D698;1D698;1D698;006F;006F; # (𝚘𝚘; 𝚘𝚘; 𝚘𝚘; o; o; ) MATHEMATICAL MONOSPACE SMALL O +1D699;1D699;1D699;0070;0070; # (𝚙𝚙; 𝚙𝚙; 𝚙𝚙; p; p; ) MATHEMATICAL MONOSPACE SMALL P +1D69A;1D69A;1D69A;0071;0071; # (𝚚𝚚; 𝚚𝚚; 𝚚𝚚; q; q; ) MATHEMATICAL MONOSPACE SMALL Q +1D69B;1D69B;1D69B;0072;0072; # (𝚛𝚛; 𝚛𝚛; 𝚛𝚛; r; r; ) MATHEMATICAL MONOSPACE SMALL R +1D69C;1D69C;1D69C;0073;0073; # (𝚜𝚜; 𝚜𝚜; 𝚜𝚜; s; s; ) MATHEMATICAL MONOSPACE SMALL S +1D69D;1D69D;1D69D;0074;0074; # (𝚝𝚝; 𝚝𝚝; 𝚝𝚝; t; t; ) MATHEMATICAL MONOSPACE SMALL T +1D69E;1D69E;1D69E;0075;0075; # (𝚞𝚞; 𝚞𝚞; 𝚞𝚞; u; u; ) MATHEMATICAL MONOSPACE SMALL U +1D69F;1D69F;1D69F;0076;0076; # (𝚟𝚟; 𝚟𝚟; 𝚟𝚟; v; v; ) MATHEMATICAL MONOSPACE SMALL V +1D6A0;1D6A0;1D6A0;0077;0077; # (𝚠𝚠; 𝚠𝚠; 𝚠𝚠; w; w; ) MATHEMATICAL MONOSPACE SMALL W +1D6A1;1D6A1;1D6A1;0078;0078; # (𝚡𝚡; 𝚡𝚡; 𝚡𝚡; x; x; ) MATHEMATICAL MONOSPACE SMALL X +1D6A2;1D6A2;1D6A2;0079;0079; # (𝚢𝚢; 𝚢𝚢; 𝚢𝚢; y; y; ) MATHEMATICAL MONOSPACE SMALL Y +1D6A3;1D6A3;1D6A3;007A;007A; # (𝚣𝚣; 𝚣𝚣; 𝚣𝚣; z; z; ) MATHEMATICAL MONOSPACE SMALL Z +1D6A8;1D6A8;1D6A8;0391;0391; # (𝚨𝚨; 𝚨𝚨; 𝚨𝚨; Α; Α; ) MATHEMATICAL BOLD CAPITAL ALPHA +1D6A9;1D6A9;1D6A9;0392;0392; # (𝚩𝚩; 𝚩𝚩; 𝚩𝚩; Β; Β; ) MATHEMATICAL BOLD CAPITAL BETA +1D6AA;1D6AA;1D6AA;0393;0393; # (𝚪𝚪; 𝚪𝚪; 𝚪𝚪; Γ; Γ; ) MATHEMATICAL BOLD CAPITAL GAMMA +1D6AB;1D6AB;1D6AB;0394;0394; # (𝚫𝚫; 𝚫𝚫; 𝚫𝚫; Δ; Δ; ) MATHEMATICAL BOLD CAPITAL DELTA +1D6AC;1D6AC;1D6AC;0395;0395; # (𝚬𝚬; 𝚬𝚬; 𝚬𝚬; Ε; Ε; ) MATHEMATICAL BOLD CAPITAL EPSILON +1D6AD;1D6AD;1D6AD;0396;0396; # (𝚭𝚭; 𝚭𝚭; 𝚭𝚭; Ζ; Ζ; ) MATHEMATICAL BOLD CAPITAL ZETA +1D6AE;1D6AE;1D6AE;0397;0397; # (𝚮𝚮; 𝚮𝚮; 𝚮𝚮; Η; Η; ) MATHEMATICAL BOLD CAPITAL ETA +1D6AF;1D6AF;1D6AF;0398;0398; # (𝚯𝚯; 𝚯𝚯; 𝚯𝚯; Θ; Θ; ) MATHEMATICAL BOLD CAPITAL THETA +1D6B0;1D6B0;1D6B0;0399;0399; # (𝚰𝚰; 𝚰𝚰; 𝚰𝚰; Ι; Ι; ) MATHEMATICAL BOLD CAPITAL IOTA +1D6B1;1D6B1;1D6B1;039A;039A; # (𝚱𝚱; 𝚱𝚱; 𝚱𝚱; Κ; Κ; ) MATHEMATICAL BOLD CAPITAL KAPPA +1D6B2;1D6B2;1D6B2;039B;039B; # (𝚲𝚲; 𝚲𝚲; 𝚲𝚲; Λ; Λ; ) MATHEMATICAL BOLD CAPITAL LAMDA +1D6B3;1D6B3;1D6B3;039C;039C; # (𝚳𝚳; 𝚳𝚳; 𝚳𝚳; Μ; Μ; ) MATHEMATICAL BOLD CAPITAL MU +1D6B4;1D6B4;1D6B4;039D;039D; # (𝚴𝚴; 𝚴𝚴; 𝚴𝚴; Ν; Ν; ) MATHEMATICAL BOLD CAPITAL NU +1D6B5;1D6B5;1D6B5;039E;039E; # (𝚵𝚵; 𝚵𝚵; 𝚵𝚵; Ξ; Ξ; ) MATHEMATICAL BOLD CAPITAL XI +1D6B6;1D6B6;1D6B6;039F;039F; # (𝚶𝚶; 𝚶𝚶; 𝚶𝚶; Ο; Ο; ) MATHEMATICAL BOLD CAPITAL OMICRON +1D6B7;1D6B7;1D6B7;03A0;03A0; # (𝚷𝚷; 𝚷𝚷; 𝚷𝚷; Π; Π; ) MATHEMATICAL BOLD CAPITAL PI +1D6B8;1D6B8;1D6B8;03A1;03A1; # (𝚸𝚸; 𝚸𝚸; 𝚸𝚸; Ρ; Ρ; ) MATHEMATICAL BOLD CAPITAL RHO +1D6B9;1D6B9;1D6B9;0398;0398; # (𝚹𝚹; 𝚹𝚹; 𝚹𝚹; Θ; Θ; ) MATHEMATICAL BOLD CAPITAL THETA SYMBOL +1D6BA;1D6BA;1D6BA;03A3;03A3; # (𝚺𝚺; 𝚺𝚺; 𝚺𝚺; Σ; Σ; ) MATHEMATICAL BOLD CAPITAL SIGMA +1D6BB;1D6BB;1D6BB;03A4;03A4; # (𝚻𝚻; 𝚻𝚻; 𝚻𝚻; Τ; Τ; ) MATHEMATICAL BOLD CAPITAL TAU +1D6BC;1D6BC;1D6BC;03A5;03A5; # (𝚼𝚼; 𝚼𝚼; 𝚼𝚼; Υ; Υ; ) MATHEMATICAL BOLD CAPITAL UPSILON +1D6BD;1D6BD;1D6BD;03A6;03A6; # (𝚽𝚽; 𝚽𝚽; 𝚽𝚽; Φ; Φ; ) MATHEMATICAL BOLD CAPITAL PHI +1D6BE;1D6BE;1D6BE;03A7;03A7; # (𝚾𝚾; 𝚾𝚾; 𝚾𝚾; Χ; Χ; ) MATHEMATICAL BOLD CAPITAL CHI +1D6BF;1D6BF;1D6BF;03A8;03A8; # (𝚿𝚿; 𝚿𝚿; 𝚿𝚿; Ψ; Ψ; ) MATHEMATICAL BOLD CAPITAL PSI +1D6C0;1D6C0;1D6C0;03A9;03A9; # (𝛀𝛀; 𝛀𝛀; 𝛀𝛀; Ω; Ω; ) MATHEMATICAL BOLD CAPITAL OMEGA +1D6C1;1D6C1;1D6C1;2207;2207; # (𝛁𝛁; 𝛁𝛁; 𝛁𝛁; ∇; ∇; ) MATHEMATICAL BOLD NABLA +1D6C2;1D6C2;1D6C2;03B1;03B1; # (𝛂𝛂; 𝛂𝛂; 𝛂𝛂; α; α; ) MATHEMATICAL BOLD SMALL ALPHA +1D6C3;1D6C3;1D6C3;03B2;03B2; # (𝛃𝛃; 𝛃𝛃; 𝛃𝛃; β; β; ) MATHEMATICAL BOLD SMALL BETA +1D6C4;1D6C4;1D6C4;03B3;03B3; # (𝛄𝛄; 𝛄𝛄; 𝛄𝛄; γ; γ; ) MATHEMATICAL BOLD SMALL GAMMA +1D6C5;1D6C5;1D6C5;03B4;03B4; # (𝛅𝛅; 𝛅𝛅; 𝛅𝛅; δ; δ; ) MATHEMATICAL BOLD SMALL DELTA +1D6C6;1D6C6;1D6C6;03B5;03B5; # (𝛆𝛆; 𝛆𝛆; 𝛆𝛆; ε; ε; ) MATHEMATICAL BOLD SMALL EPSILON +1D6C7;1D6C7;1D6C7;03B6;03B6; # (𝛇𝛇; 𝛇𝛇; 𝛇𝛇; ζ; ζ; ) MATHEMATICAL BOLD SMALL ZETA +1D6C8;1D6C8;1D6C8;03B7;03B7; # (𝛈𝛈; 𝛈𝛈; 𝛈𝛈; η; η; ) MATHEMATICAL BOLD SMALL ETA +1D6C9;1D6C9;1D6C9;03B8;03B8; # (𝛉𝛉; 𝛉𝛉; 𝛉𝛉; θ; θ; ) MATHEMATICAL BOLD SMALL THETA +1D6CA;1D6CA;1D6CA;03B9;03B9; # (𝛊𝛊; 𝛊𝛊; 𝛊𝛊; ι; ι; ) MATHEMATICAL BOLD SMALL IOTA +1D6CB;1D6CB;1D6CB;03BA;03BA; # (𝛋𝛋; 𝛋𝛋; 𝛋𝛋; κ; κ; ) MATHEMATICAL BOLD SMALL KAPPA +1D6CC;1D6CC;1D6CC;03BB;03BB; # (𝛌𝛌; 𝛌𝛌; 𝛌𝛌; λ; λ; ) MATHEMATICAL BOLD SMALL LAMDA +1D6CD;1D6CD;1D6CD;03BC;03BC; # (𝛍𝛍; 𝛍𝛍; 𝛍𝛍; μ; μ; ) MATHEMATICAL BOLD SMALL MU +1D6CE;1D6CE;1D6CE;03BD;03BD; # (𝛎𝛎; 𝛎𝛎; 𝛎𝛎; ν; ν; ) MATHEMATICAL BOLD SMALL NU +1D6CF;1D6CF;1D6CF;03BE;03BE; # (𝛏𝛏; 𝛏𝛏; 𝛏𝛏; ξ; ξ; ) MATHEMATICAL BOLD SMALL XI +1D6D0;1D6D0;1D6D0;03BF;03BF; # (𝛐𝛐; 𝛐𝛐; 𝛐𝛐; ο; ο; ) MATHEMATICAL BOLD SMALL OMICRON +1D6D1;1D6D1;1D6D1;03C0;03C0; # (𝛑𝛑; 𝛑𝛑; 𝛑𝛑; π; π; ) MATHEMATICAL BOLD SMALL PI +1D6D2;1D6D2;1D6D2;03C1;03C1; # (𝛒𝛒; 𝛒𝛒; 𝛒𝛒; ρ; ρ; ) MATHEMATICAL BOLD SMALL RHO +1D6D3;1D6D3;1D6D3;03C2;03C2; # (𝛓𝛓; 𝛓𝛓; 𝛓𝛓; ς; ς; ) MATHEMATICAL BOLD SMALL FINAL SIGMA +1D6D4;1D6D4;1D6D4;03C3;03C3; # (𝛔𝛔; 𝛔𝛔; 𝛔𝛔; σ; σ; ) MATHEMATICAL BOLD SMALL SIGMA +1D6D5;1D6D5;1D6D5;03C4;03C4; # (𝛕𝛕; 𝛕𝛕; 𝛕𝛕; τ; τ; ) MATHEMATICAL BOLD SMALL TAU +1D6D6;1D6D6;1D6D6;03C5;03C5; # (𝛖𝛖; 𝛖𝛖; 𝛖𝛖; υ; υ; ) MATHEMATICAL BOLD SMALL UPSILON +1D6D7;1D6D7;1D6D7;03C6;03C6; # (𝛗𝛗; 𝛗𝛗; 𝛗𝛗; φ; φ; ) MATHEMATICAL BOLD SMALL PHI +1D6D8;1D6D8;1D6D8;03C7;03C7; # (𝛘𝛘; 𝛘𝛘; 𝛘𝛘; χ; χ; ) MATHEMATICAL BOLD SMALL CHI +1D6D9;1D6D9;1D6D9;03C8;03C8; # (𝛙𝛙; 𝛙𝛙; 𝛙𝛙; ψ; ψ; ) MATHEMATICAL BOLD SMALL PSI +1D6DA;1D6DA;1D6DA;03C9;03C9; # (𝛚𝛚; 𝛚𝛚; 𝛚𝛚; ω; ω; ) MATHEMATICAL BOLD SMALL OMEGA +1D6DB;1D6DB;1D6DB;2202;2202; # (𝛛𝛛; 𝛛𝛛; 𝛛𝛛; ∂; ∂; ) MATHEMATICAL BOLD PARTIAL DIFFERENTIAL +1D6DC;1D6DC;1D6DC;03B5;03B5; # (𝛜𝛜; 𝛜𝛜; 𝛜𝛜; ε; ε; ) MATHEMATICAL BOLD EPSILON SYMBOL +1D6DD;1D6DD;1D6DD;03B8;03B8; # (𝛝𝛝; 𝛝𝛝; 𝛝𝛝; θ; θ; ) MATHEMATICAL BOLD THETA SYMBOL +1D6DE;1D6DE;1D6DE;03BA;03BA; # (𝛞𝛞; 𝛞𝛞; 𝛞𝛞; κ; κ; ) MATHEMATICAL BOLD KAPPA SYMBOL +1D6DF;1D6DF;1D6DF;03C6;03C6; # (𝛟𝛟; 𝛟𝛟; 𝛟𝛟; φ; φ; ) MATHEMATICAL BOLD PHI SYMBOL +1D6E0;1D6E0;1D6E0;03C1;03C1; # (𝛠𝛠; 𝛠𝛠; 𝛠𝛠; ρ; ρ; ) MATHEMATICAL BOLD RHO SYMBOL +1D6E1;1D6E1;1D6E1;03C0;03C0; # (𝛡𝛡; 𝛡𝛡; 𝛡𝛡; π; π; ) MATHEMATICAL BOLD PI SYMBOL +1D6E2;1D6E2;1D6E2;0391;0391; # (𝛢𝛢; 𝛢𝛢; 𝛢𝛢; Α; Α; ) MATHEMATICAL ITALIC CAPITAL ALPHA +1D6E3;1D6E3;1D6E3;0392;0392; # (𝛣𝛣; 𝛣𝛣; 𝛣𝛣; Β; Β; ) MATHEMATICAL ITALIC CAPITAL BETA +1D6E4;1D6E4;1D6E4;0393;0393; # (𝛤𝛤; 𝛤𝛤; 𝛤𝛤; Γ; Γ; ) MATHEMATICAL ITALIC CAPITAL GAMMA +1D6E5;1D6E5;1D6E5;0394;0394; # (𝛥𝛥; 𝛥𝛥; 𝛥𝛥; Δ; Δ; ) MATHEMATICAL ITALIC CAPITAL DELTA +1D6E6;1D6E6;1D6E6;0395;0395; # (𝛦𝛦; 𝛦𝛦; 𝛦𝛦; Ε; Ε; ) MATHEMATICAL ITALIC CAPITAL EPSILON +1D6E7;1D6E7;1D6E7;0396;0396; # (𝛧𝛧; 𝛧𝛧; 𝛧𝛧; Ζ; Ζ; ) MATHEMATICAL ITALIC CAPITAL ZETA +1D6E8;1D6E8;1D6E8;0397;0397; # (𝛨𝛨; 𝛨𝛨; 𝛨𝛨; Η; Η; ) MATHEMATICAL ITALIC CAPITAL ETA +1D6E9;1D6E9;1D6E9;0398;0398; # (𝛩𝛩; 𝛩𝛩; 𝛩𝛩; Θ; Θ; ) MATHEMATICAL ITALIC CAPITAL THETA +1D6EA;1D6EA;1D6EA;0399;0399; # (𝛪𝛪; 𝛪𝛪; 𝛪𝛪; Ι; Ι; ) MATHEMATICAL ITALIC CAPITAL IOTA +1D6EB;1D6EB;1D6EB;039A;039A; # (𝛫𝛫; 𝛫𝛫; 𝛫𝛫; Κ; Κ; ) MATHEMATICAL ITALIC CAPITAL KAPPA +1D6EC;1D6EC;1D6EC;039B;039B; # (𝛬𝛬; 𝛬𝛬; 𝛬𝛬; Λ; Λ; ) MATHEMATICAL ITALIC CAPITAL LAMDA +1D6ED;1D6ED;1D6ED;039C;039C; # (𝛭𝛭; 𝛭𝛭; 𝛭𝛭; Μ; Μ; ) MATHEMATICAL ITALIC CAPITAL MU +1D6EE;1D6EE;1D6EE;039D;039D; # (𝛮𝛮; 𝛮𝛮; 𝛮𝛮; Ν; Ν; ) MATHEMATICAL ITALIC CAPITAL NU +1D6EF;1D6EF;1D6EF;039E;039E; # (𝛯𝛯; 𝛯𝛯; 𝛯𝛯; Ξ; Ξ; ) MATHEMATICAL ITALIC CAPITAL XI +1D6F0;1D6F0;1D6F0;039F;039F; # (𝛰𝛰; 𝛰𝛰; 𝛰𝛰; Ο; Ο; ) MATHEMATICAL ITALIC CAPITAL OMICRON +1D6F1;1D6F1;1D6F1;03A0;03A0; # (𝛱𝛱; 𝛱𝛱; 𝛱𝛱; Π; Π; ) MATHEMATICAL ITALIC CAPITAL PI +1D6F2;1D6F2;1D6F2;03A1;03A1; # (𝛲𝛲; 𝛲𝛲; 𝛲𝛲; Ρ; Ρ; ) MATHEMATICAL ITALIC CAPITAL RHO +1D6F3;1D6F3;1D6F3;0398;0398; # (𝛳𝛳; 𝛳𝛳; 𝛳𝛳; Θ; Θ; ) MATHEMATICAL ITALIC CAPITAL THETA SYMBOL +1D6F4;1D6F4;1D6F4;03A3;03A3; # (𝛴𝛴; 𝛴𝛴; 𝛴𝛴; Σ; Σ; ) MATHEMATICAL ITALIC CAPITAL SIGMA +1D6F5;1D6F5;1D6F5;03A4;03A4; # (𝛵𝛵; 𝛵𝛵; 𝛵𝛵; Τ; Τ; ) MATHEMATICAL ITALIC CAPITAL TAU +1D6F6;1D6F6;1D6F6;03A5;03A5; # (𝛶𝛶; 𝛶𝛶; 𝛶𝛶; Υ; Υ; ) MATHEMATICAL ITALIC CAPITAL UPSILON +1D6F7;1D6F7;1D6F7;03A6;03A6; # (𝛷𝛷; 𝛷𝛷; 𝛷𝛷; Φ; Φ; ) MATHEMATICAL ITALIC CAPITAL PHI +1D6F8;1D6F8;1D6F8;03A7;03A7; # (𝛸𝛸; 𝛸𝛸; 𝛸𝛸; Χ; Χ; ) MATHEMATICAL ITALIC CAPITAL CHI +1D6F9;1D6F9;1D6F9;03A8;03A8; # (𝛹𝛹; 𝛹𝛹; 𝛹𝛹; Ψ; Ψ; ) MATHEMATICAL ITALIC CAPITAL PSI +1D6FA;1D6FA;1D6FA;03A9;03A9; # (𝛺𝛺; 𝛺𝛺; 𝛺𝛺; Ω; Ω; ) MATHEMATICAL ITALIC CAPITAL OMEGA +1D6FB;1D6FB;1D6FB;2207;2207; # (𝛻𝛻; 𝛻𝛻; 𝛻𝛻; ∇; ∇; ) MATHEMATICAL ITALIC NABLA +1D6FC;1D6FC;1D6FC;03B1;03B1; # (𝛼𝛼; 𝛼𝛼; 𝛼𝛼; α; α; ) MATHEMATICAL ITALIC SMALL ALPHA +1D6FD;1D6FD;1D6FD;03B2;03B2; # (𝛽𝛽; 𝛽𝛽; 𝛽𝛽; β; β; ) MATHEMATICAL ITALIC SMALL BETA +1D6FE;1D6FE;1D6FE;03B3;03B3; # (𝛾𝛾; 𝛾𝛾; 𝛾𝛾; γ; γ; ) MATHEMATICAL ITALIC SMALL GAMMA +1D6FF;1D6FF;1D6FF;03B4;03B4; # (𝛿𝛿; 𝛿𝛿; 𝛿𝛿; δ; δ; ) MATHEMATICAL ITALIC SMALL DELTA +1D700;1D700;1D700;03B5;03B5; # (𝜀𝜀; 𝜀𝜀; 𝜀𝜀; ε; ε; ) MATHEMATICAL ITALIC SMALL EPSILON +1D701;1D701;1D701;03B6;03B6; # (𝜁𝜁; 𝜁𝜁; 𝜁𝜁; ζ; ζ; ) MATHEMATICAL ITALIC SMALL ZETA +1D702;1D702;1D702;03B7;03B7; # (𝜂𝜂; 𝜂𝜂; 𝜂𝜂; η; η; ) MATHEMATICAL ITALIC SMALL ETA +1D703;1D703;1D703;03B8;03B8; # (𝜃𝜃; 𝜃𝜃; 𝜃𝜃; θ; θ; ) MATHEMATICAL ITALIC SMALL THETA +1D704;1D704;1D704;03B9;03B9; # (𝜄𝜄; 𝜄𝜄; 𝜄𝜄; ι; ι; ) MATHEMATICAL ITALIC SMALL IOTA +1D705;1D705;1D705;03BA;03BA; # (𝜅𝜅; 𝜅𝜅; 𝜅𝜅; κ; κ; ) MATHEMATICAL ITALIC SMALL KAPPA +1D706;1D706;1D706;03BB;03BB; # (𝜆𝜆; 𝜆𝜆; 𝜆𝜆; λ; λ; ) MATHEMATICAL ITALIC SMALL LAMDA +1D707;1D707;1D707;03BC;03BC; # (𝜇𝜇; 𝜇𝜇; 𝜇𝜇; μ; μ; ) MATHEMATICAL ITALIC SMALL MU +1D708;1D708;1D708;03BD;03BD; # (𝜈𝜈; 𝜈𝜈; 𝜈𝜈; ν; ν; ) MATHEMATICAL ITALIC SMALL NU +1D709;1D709;1D709;03BE;03BE; # (𝜉𝜉; 𝜉𝜉; 𝜉𝜉; ξ; ξ; ) MATHEMATICAL ITALIC SMALL XI +1D70A;1D70A;1D70A;03BF;03BF; # (𝜊𝜊; 𝜊𝜊; 𝜊𝜊; ο; ο; ) MATHEMATICAL ITALIC SMALL OMICRON +1D70B;1D70B;1D70B;03C0;03C0; # (𝜋𝜋; 𝜋𝜋; 𝜋𝜋; π; π; ) MATHEMATICAL ITALIC SMALL PI +1D70C;1D70C;1D70C;03C1;03C1; # (𝜌𝜌; 𝜌𝜌; 𝜌𝜌; ρ; ρ; ) MATHEMATICAL ITALIC SMALL RHO +1D70D;1D70D;1D70D;03C2;03C2; # (𝜍𝜍; 𝜍𝜍; 𝜍𝜍; ς; ς; ) MATHEMATICAL ITALIC SMALL FINAL SIGMA +1D70E;1D70E;1D70E;03C3;03C3; # (𝜎𝜎; 𝜎𝜎; 𝜎𝜎; σ; σ; ) MATHEMATICAL ITALIC SMALL SIGMA +1D70F;1D70F;1D70F;03C4;03C4; # (𝜏𝜏; 𝜏𝜏; 𝜏𝜏; τ; τ; ) MATHEMATICAL ITALIC SMALL TAU +1D710;1D710;1D710;03C5;03C5; # (𝜐𝜐; 𝜐𝜐; 𝜐𝜐; υ; υ; ) MATHEMATICAL ITALIC SMALL UPSILON +1D711;1D711;1D711;03C6;03C6; # (𝜑𝜑; 𝜑𝜑; 𝜑𝜑; φ; φ; ) MATHEMATICAL ITALIC SMALL PHI +1D712;1D712;1D712;03C7;03C7; # (𝜒𝜒; 𝜒𝜒; 𝜒𝜒; χ; χ; ) MATHEMATICAL ITALIC SMALL CHI +1D713;1D713;1D713;03C8;03C8; # (𝜓𝜓; 𝜓𝜓; 𝜓𝜓; ψ; ψ; ) MATHEMATICAL ITALIC SMALL PSI +1D714;1D714;1D714;03C9;03C9; # (𝜔𝜔; 𝜔𝜔; 𝜔𝜔; ω; ω; ) MATHEMATICAL ITALIC SMALL OMEGA +1D715;1D715;1D715;2202;2202; # (𝜕𝜕; 𝜕𝜕; 𝜕𝜕; ∂; ∂; ) MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL +1D716;1D716;1D716;03B5;03B5; # (𝜖𝜖; 𝜖𝜖; 𝜖𝜖; ε; ε; ) MATHEMATICAL ITALIC EPSILON SYMBOL +1D717;1D717;1D717;03B8;03B8; # (𝜗𝜗; 𝜗𝜗; 𝜗𝜗; θ; θ; ) MATHEMATICAL ITALIC THETA SYMBOL +1D718;1D718;1D718;03BA;03BA; # (𝜘𝜘; 𝜘𝜘; 𝜘𝜘; κ; κ; ) MATHEMATICAL ITALIC KAPPA SYMBOL +1D719;1D719;1D719;03C6;03C6; # (𝜙𝜙; 𝜙𝜙; 𝜙𝜙; φ; φ; ) MATHEMATICAL ITALIC PHI SYMBOL +1D71A;1D71A;1D71A;03C1;03C1; # (𝜚𝜚; 𝜚𝜚; 𝜚𝜚; ρ; ρ; ) MATHEMATICAL ITALIC RHO SYMBOL +1D71B;1D71B;1D71B;03C0;03C0; # (𝜛𝜛; 𝜛𝜛; 𝜛𝜛; π; π; ) MATHEMATICAL ITALIC PI SYMBOL +1D71C;1D71C;1D71C;0391;0391; # (𝜜𝜜; 𝜜𝜜; 𝜜𝜜; Α; Α; ) MATHEMATICAL BOLD ITALIC CAPITAL ALPHA +1D71D;1D71D;1D71D;0392;0392; # (𝜝𝜝; 𝜝𝜝; 𝜝𝜝; Β; Β; ) MATHEMATICAL BOLD ITALIC CAPITAL BETA +1D71E;1D71E;1D71E;0393;0393; # (𝜞𝜞; 𝜞𝜞; 𝜞𝜞; Γ; Γ; ) MATHEMATICAL BOLD ITALIC CAPITAL GAMMA +1D71F;1D71F;1D71F;0394;0394; # (𝜟𝜟; 𝜟𝜟; 𝜟𝜟; Δ; Δ; ) MATHEMATICAL BOLD ITALIC CAPITAL DELTA +1D720;1D720;1D720;0395;0395; # (𝜠𝜠; 𝜠𝜠; 𝜠𝜠; Ε; Ε; ) MATHEMATICAL BOLD ITALIC CAPITAL EPSILON +1D721;1D721;1D721;0396;0396; # (𝜡𝜡; 𝜡𝜡; 𝜡𝜡; Ζ; Ζ; ) MATHEMATICAL BOLD ITALIC CAPITAL ZETA +1D722;1D722;1D722;0397;0397; # (𝜢𝜢; 𝜢𝜢; 𝜢𝜢; Η; Η; ) MATHEMATICAL BOLD ITALIC CAPITAL ETA +1D723;1D723;1D723;0398;0398; # (𝜣𝜣; 𝜣𝜣; 𝜣𝜣; Θ; Θ; ) MATHEMATICAL BOLD ITALIC CAPITAL THETA +1D724;1D724;1D724;0399;0399; # (𝜤𝜤; 𝜤𝜤; 𝜤𝜤; Ι; Ι; ) MATHEMATICAL BOLD ITALIC CAPITAL IOTA +1D725;1D725;1D725;039A;039A; # (𝜥𝜥; 𝜥𝜥; 𝜥𝜥; Κ; Κ; ) MATHEMATICAL BOLD ITALIC CAPITAL KAPPA +1D726;1D726;1D726;039B;039B; # (𝜦𝜦; 𝜦𝜦; 𝜦𝜦; Λ; Λ; ) MATHEMATICAL BOLD ITALIC CAPITAL LAMDA +1D727;1D727;1D727;039C;039C; # (𝜧𝜧; 𝜧𝜧; 𝜧𝜧; Μ; Μ; ) MATHEMATICAL BOLD ITALIC CAPITAL MU +1D728;1D728;1D728;039D;039D; # (𝜨𝜨; 𝜨𝜨; 𝜨𝜨; Ν; Ν; ) MATHEMATICAL BOLD ITALIC CAPITAL NU +1D729;1D729;1D729;039E;039E; # (𝜩𝜩; 𝜩𝜩; 𝜩𝜩; Ξ; Ξ; ) MATHEMATICAL BOLD ITALIC CAPITAL XI +1D72A;1D72A;1D72A;039F;039F; # (𝜪𝜪; 𝜪𝜪; 𝜪𝜪; Ο; Ο; ) MATHEMATICAL BOLD ITALIC CAPITAL OMICRON +1D72B;1D72B;1D72B;03A0;03A0; # (𝜫𝜫; 𝜫𝜫; 𝜫𝜫; Π; Π; ) MATHEMATICAL BOLD ITALIC CAPITAL PI +1D72C;1D72C;1D72C;03A1;03A1; # (𝜬𝜬; 𝜬𝜬; 𝜬𝜬; Ρ; Ρ; ) MATHEMATICAL BOLD ITALIC CAPITAL RHO +1D72D;1D72D;1D72D;0398;0398; # (𝜭𝜭; 𝜭𝜭; 𝜭𝜭; Θ; Θ; ) MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL +1D72E;1D72E;1D72E;03A3;03A3; # (𝜮𝜮; 𝜮𝜮; 𝜮𝜮; Σ; Σ; ) MATHEMATICAL BOLD ITALIC CAPITAL SIGMA +1D72F;1D72F;1D72F;03A4;03A4; # (𝜯𝜯; 𝜯𝜯; 𝜯𝜯; Τ; Τ; ) MATHEMATICAL BOLD ITALIC CAPITAL TAU +1D730;1D730;1D730;03A5;03A5; # (𝜰𝜰; 𝜰𝜰; 𝜰𝜰; Υ; Υ; ) MATHEMATICAL BOLD ITALIC CAPITAL UPSILON +1D731;1D731;1D731;03A6;03A6; # (𝜱𝜱; 𝜱𝜱; 𝜱𝜱; Φ; Φ; ) MATHEMATICAL BOLD ITALIC CAPITAL PHI +1D732;1D732;1D732;03A7;03A7; # (𝜲𝜲; 𝜲𝜲; 𝜲𝜲; Χ; Χ; ) MATHEMATICAL BOLD ITALIC CAPITAL CHI +1D733;1D733;1D733;03A8;03A8; # (𝜳𝜳; 𝜳𝜳; 𝜳𝜳; Ψ; Ψ; ) MATHEMATICAL BOLD ITALIC CAPITAL PSI +1D734;1D734;1D734;03A9;03A9; # (𝜴𝜴; 𝜴𝜴; 𝜴𝜴; Ω; Ω; ) MATHEMATICAL BOLD ITALIC CAPITAL OMEGA +1D735;1D735;1D735;2207;2207; # (𝜵𝜵; 𝜵𝜵; 𝜵𝜵; ∇; ∇; ) MATHEMATICAL BOLD ITALIC NABLA +1D736;1D736;1D736;03B1;03B1; # (𝜶𝜶; 𝜶𝜶; 𝜶𝜶; α; α; ) MATHEMATICAL BOLD ITALIC SMALL ALPHA +1D737;1D737;1D737;03B2;03B2; # (𝜷𝜷; 𝜷𝜷; 𝜷𝜷; β; β; ) MATHEMATICAL BOLD ITALIC SMALL BETA +1D738;1D738;1D738;03B3;03B3; # (𝜸𝜸; 𝜸𝜸; 𝜸𝜸; γ; γ; ) MATHEMATICAL BOLD ITALIC SMALL GAMMA +1D739;1D739;1D739;03B4;03B4; # (𝜹𝜹; 𝜹𝜹; 𝜹𝜹; δ; δ; ) MATHEMATICAL BOLD ITALIC SMALL DELTA +1D73A;1D73A;1D73A;03B5;03B5; # (𝜺𝜺; 𝜺𝜺; 𝜺𝜺; ε; ε; ) MATHEMATICAL BOLD ITALIC SMALL EPSILON +1D73B;1D73B;1D73B;03B6;03B6; # (𝜻𝜻; 𝜻𝜻; 𝜻𝜻; ζ; ζ; ) MATHEMATICAL BOLD ITALIC SMALL ZETA +1D73C;1D73C;1D73C;03B7;03B7; # (𝜼𝜼; 𝜼𝜼; 𝜼𝜼; η; η; ) MATHEMATICAL BOLD ITALIC SMALL ETA +1D73D;1D73D;1D73D;03B8;03B8; # (𝜽𝜽; 𝜽𝜽; 𝜽𝜽; θ; θ; ) MATHEMATICAL BOLD ITALIC SMALL THETA +1D73E;1D73E;1D73E;03B9;03B9; # (𝜾𝜾; 𝜾𝜾; 𝜾𝜾; ι; ι; ) MATHEMATICAL BOLD ITALIC SMALL IOTA +1D73F;1D73F;1D73F;03BA;03BA; # (𝜿𝜿; 𝜿𝜿; 𝜿𝜿; κ; κ; ) MATHEMATICAL BOLD ITALIC SMALL KAPPA +1D740;1D740;1D740;03BB;03BB; # (𝝀𝝀; 𝝀𝝀; 𝝀𝝀; λ; λ; ) MATHEMATICAL BOLD ITALIC SMALL LAMDA +1D741;1D741;1D741;03BC;03BC; # (𝝁𝝁; 𝝁𝝁; 𝝁𝝁; μ; μ; ) MATHEMATICAL BOLD ITALIC SMALL MU +1D742;1D742;1D742;03BD;03BD; # (𝝂𝝂; 𝝂𝝂; 𝝂𝝂; ν; ν; ) MATHEMATICAL BOLD ITALIC SMALL NU +1D743;1D743;1D743;03BE;03BE; # (𝝃𝝃; 𝝃𝝃; 𝝃𝝃; ξ; ξ; ) MATHEMATICAL BOLD ITALIC SMALL XI +1D744;1D744;1D744;03BF;03BF; # (𝝄𝝄; 𝝄𝝄; 𝝄𝝄; ο; ο; ) MATHEMATICAL BOLD ITALIC SMALL OMICRON +1D745;1D745;1D745;03C0;03C0; # (𝝅𝝅; 𝝅𝝅; 𝝅𝝅; π; π; ) MATHEMATICAL BOLD ITALIC SMALL PI +1D746;1D746;1D746;03C1;03C1; # (𝝆𝝆; 𝝆𝝆; 𝝆𝝆; ρ; ρ; ) MATHEMATICAL BOLD ITALIC SMALL RHO +1D747;1D747;1D747;03C2;03C2; # (𝝇𝝇; 𝝇𝝇; 𝝇𝝇; ς; ς; ) MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA +1D748;1D748;1D748;03C3;03C3; # (𝝈𝝈; 𝝈𝝈; 𝝈𝝈; σ; σ; ) MATHEMATICAL BOLD ITALIC SMALL SIGMA +1D749;1D749;1D749;03C4;03C4; # (𝝉𝝉; 𝝉𝝉; 𝝉𝝉; τ; τ; ) MATHEMATICAL BOLD ITALIC SMALL TAU +1D74A;1D74A;1D74A;03C5;03C5; # (𝝊𝝊; 𝝊𝝊; 𝝊𝝊; υ; υ; ) MATHEMATICAL BOLD ITALIC SMALL UPSILON +1D74B;1D74B;1D74B;03C6;03C6; # (𝝋𝝋; 𝝋𝝋; 𝝋𝝋; φ; φ; ) MATHEMATICAL BOLD ITALIC SMALL PHI +1D74C;1D74C;1D74C;03C7;03C7; # (𝝌𝝌; 𝝌𝝌; 𝝌𝝌; χ; χ; ) MATHEMATICAL BOLD ITALIC SMALL CHI +1D74D;1D74D;1D74D;03C8;03C8; # (𝝍𝝍; 𝝍𝝍; 𝝍𝝍; ψ; ψ; ) MATHEMATICAL BOLD ITALIC SMALL PSI +1D74E;1D74E;1D74E;03C9;03C9; # (𝝎𝝎; 𝝎𝝎; 𝝎𝝎; ω; ω; ) MATHEMATICAL BOLD ITALIC SMALL OMEGA +1D74F;1D74F;1D74F;2202;2202; # (𝝏𝝏; 𝝏𝝏; 𝝏𝝏; ∂; ∂; ) MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL +1D750;1D750;1D750;03B5;03B5; # (𝝐𝝐; 𝝐𝝐; 𝝐𝝐; ε; ε; ) MATHEMATICAL BOLD ITALIC EPSILON SYMBOL +1D751;1D751;1D751;03B8;03B8; # (𝝑𝝑; 𝝑𝝑; 𝝑𝝑; θ; θ; ) MATHEMATICAL BOLD ITALIC THETA SYMBOL +1D752;1D752;1D752;03BA;03BA; # (𝝒𝝒; 𝝒𝝒; 𝝒𝝒; κ; κ; ) MATHEMATICAL BOLD ITALIC KAPPA SYMBOL +1D753;1D753;1D753;03C6;03C6; # (𝝓𝝓; 𝝓𝝓; 𝝓𝝓; φ; φ; ) MATHEMATICAL BOLD ITALIC PHI SYMBOL +1D754;1D754;1D754;03C1;03C1; # (𝝔𝝔; 𝝔𝝔; 𝝔𝝔; ρ; ρ; ) MATHEMATICAL BOLD ITALIC RHO SYMBOL +1D755;1D755;1D755;03C0;03C0; # (𝝕𝝕; 𝝕𝝕; 𝝕𝝕; π; π; ) MATHEMATICAL BOLD ITALIC PI SYMBOL +1D756;1D756;1D756;0391;0391; # (𝝖𝝖; 𝝖𝝖; 𝝖𝝖; Α; Α; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA +1D757;1D757;1D757;0392;0392; # (𝝗𝝗; 𝝗𝝗; 𝝗𝝗; Β; Β; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA +1D758;1D758;1D758;0393;0393; # (𝝘𝝘; 𝝘𝝘; 𝝘𝝘; Γ; Γ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA +1D759;1D759;1D759;0394;0394; # (𝝙𝝙; 𝝙𝝙; 𝝙𝝙; Δ; Δ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA +1D75A;1D75A;1D75A;0395;0395; # (𝝚𝝚; 𝝚𝝚; 𝝚𝝚; Ε; Ε; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON +1D75B;1D75B;1D75B;0396;0396; # (𝝛𝝛; 𝝛𝝛; 𝝛𝝛; Ζ; Ζ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA +1D75C;1D75C;1D75C;0397;0397; # (𝝜𝝜; 𝝜𝝜; 𝝜𝝜; Η; Η; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA +1D75D;1D75D;1D75D;0398;0398; # (𝝝𝝝; 𝝝𝝝; 𝝝𝝝; Θ; Θ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA +1D75E;1D75E;1D75E;0399;0399; # (𝝞𝝞; 𝝞𝝞; 𝝞𝝞; Ι; Ι; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA +1D75F;1D75F;1D75F;039A;039A; # (𝝟𝝟; 𝝟𝝟; 𝝟𝝟; Κ; Κ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA +1D760;1D760;1D760;039B;039B; # (𝝠𝝠; 𝝠𝝠; 𝝠𝝠; Λ; Λ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA +1D761;1D761;1D761;039C;039C; # (𝝡𝝡; 𝝡𝝡; 𝝡𝝡; Μ; Μ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL MU +1D762;1D762;1D762;039D;039D; # (𝝢𝝢; 𝝢𝝢; 𝝢𝝢; Ν; Ν; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL NU +1D763;1D763;1D763;039E;039E; # (𝝣𝝣; 𝝣𝝣; 𝝣𝝣; Ξ; Ξ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL XI +1D764;1D764;1D764;039F;039F; # (𝝤𝝤; 𝝤𝝤; 𝝤𝝤; Ο; Ο; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON +1D765;1D765;1D765;03A0;03A0; # (𝝥𝝥; 𝝥𝝥; 𝝥𝝥; Π; Π; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL PI +1D766;1D766;1D766;03A1;03A1; # (𝝦𝝦; 𝝦𝝦; 𝝦𝝦; Ρ; Ρ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO +1D767;1D767;1D767;0398;0398; # (𝝧𝝧; 𝝧𝝧; 𝝧𝝧; Θ; Θ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL +1D768;1D768;1D768;03A3;03A3; # (𝝨𝝨; 𝝨𝝨; 𝝨𝝨; Σ; Σ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA +1D769;1D769;1D769;03A4;03A4; # (𝝩𝝩; 𝝩𝝩; 𝝩𝝩; Τ; Τ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU +1D76A;1D76A;1D76A;03A5;03A5; # (𝝪𝝪; 𝝪𝝪; 𝝪𝝪; Υ; Υ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON +1D76B;1D76B;1D76B;03A6;03A6; # (𝝫𝝫; 𝝫𝝫; 𝝫𝝫; Φ; Φ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI +1D76C;1D76C;1D76C;03A7;03A7; # (𝝬𝝬; 𝝬𝝬; 𝝬𝝬; Χ; Χ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI +1D76D;1D76D;1D76D;03A8;03A8; # (𝝭𝝭; 𝝭𝝭; 𝝭𝝭; Ψ; Ψ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI +1D76E;1D76E;1D76E;03A9;03A9; # (𝝮𝝮; 𝝮𝝮; 𝝮𝝮; Ω; Ω; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA +1D76F;1D76F;1D76F;2207;2207; # (𝝯𝝯; 𝝯𝝯; 𝝯𝝯; ∇; ∇; ) MATHEMATICAL SANS-SERIF BOLD NABLA +1D770;1D770;1D770;03B1;03B1; # (𝝰𝝰; 𝝰𝝰; 𝝰𝝰; α; α; ) MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA +1D771;1D771;1D771;03B2;03B2; # (𝝱𝝱; 𝝱𝝱; 𝝱𝝱; β; β; ) MATHEMATICAL SANS-SERIF BOLD SMALL BETA +1D772;1D772;1D772;03B3;03B3; # (𝝲𝝲; 𝝲𝝲; 𝝲𝝲; γ; γ; ) MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA +1D773;1D773;1D773;03B4;03B4; # (𝝳𝝳; 𝝳𝝳; 𝝳𝝳; δ; δ; ) MATHEMATICAL SANS-SERIF BOLD SMALL DELTA +1D774;1D774;1D774;03B5;03B5; # (𝝴𝝴; 𝝴𝝴; 𝝴𝝴; ε; ε; ) MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON +1D775;1D775;1D775;03B6;03B6; # (𝝵𝝵; 𝝵𝝵; 𝝵𝝵; ζ; ζ; ) MATHEMATICAL SANS-SERIF BOLD SMALL ZETA +1D776;1D776;1D776;03B7;03B7; # (𝝶𝝶; 𝝶𝝶; 𝝶𝝶; η; η; ) MATHEMATICAL SANS-SERIF BOLD SMALL ETA +1D777;1D777;1D777;03B8;03B8; # (𝝷𝝷; 𝝷𝝷; 𝝷𝝷; θ; θ; ) MATHEMATICAL SANS-SERIF BOLD SMALL THETA +1D778;1D778;1D778;03B9;03B9; # (𝝸𝝸; 𝝸𝝸; 𝝸𝝸; ι; ι; ) MATHEMATICAL SANS-SERIF BOLD SMALL IOTA +1D779;1D779;1D779;03BA;03BA; # (𝝹𝝹; 𝝹𝝹; 𝝹𝝹; κ; κ; ) MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA +1D77A;1D77A;1D77A;03BB;03BB; # (𝝺𝝺; 𝝺𝝺; 𝝺𝝺; λ; λ; ) MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA +1D77B;1D77B;1D77B;03BC;03BC; # (𝝻𝝻; 𝝻𝝻; 𝝻𝝻; μ; μ; ) MATHEMATICAL SANS-SERIF BOLD SMALL MU +1D77C;1D77C;1D77C;03BD;03BD; # (𝝼𝝼; 𝝼𝝼; 𝝼𝝼; ν; ν; ) MATHEMATICAL SANS-SERIF BOLD SMALL NU +1D77D;1D77D;1D77D;03BE;03BE; # (𝝽𝝽; 𝝽𝝽; 𝝽𝝽; ξ; ξ; ) MATHEMATICAL SANS-SERIF BOLD SMALL XI +1D77E;1D77E;1D77E;03BF;03BF; # (𝝾𝝾; 𝝾𝝾; 𝝾𝝾; ο; ο; ) MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON +1D77F;1D77F;1D77F;03C0;03C0; # (𝝿𝝿; 𝝿𝝿; 𝝿𝝿; π; π; ) MATHEMATICAL SANS-SERIF BOLD SMALL PI +1D780;1D780;1D780;03C1;03C1; # (𝞀𝞀; 𝞀𝞀; 𝞀𝞀; ρ; ρ; ) MATHEMATICAL SANS-SERIF BOLD SMALL RHO +1D781;1D781;1D781;03C2;03C2; # (𝞁𝞁; 𝞁𝞁; 𝞁𝞁; ς; ς; ) MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA +1D782;1D782;1D782;03C3;03C3; # (𝞂𝞂; 𝞂𝞂; 𝞂𝞂; σ; σ; ) MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA +1D783;1D783;1D783;03C4;03C4; # (𝞃𝞃; 𝞃𝞃; 𝞃𝞃; τ; τ; ) MATHEMATICAL SANS-SERIF BOLD SMALL TAU +1D784;1D784;1D784;03C5;03C5; # (𝞄𝞄; 𝞄𝞄; 𝞄𝞄; υ; υ; ) MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON +1D785;1D785;1D785;03C6;03C6; # (𝞅𝞅; 𝞅𝞅; 𝞅𝞅; φ; φ; ) MATHEMATICAL SANS-SERIF BOLD SMALL PHI +1D786;1D786;1D786;03C7;03C7; # (𝞆𝞆; 𝞆𝞆; 𝞆𝞆; χ; χ; ) MATHEMATICAL SANS-SERIF BOLD SMALL CHI +1D787;1D787;1D787;03C8;03C8; # (𝞇𝞇; 𝞇𝞇; 𝞇𝞇; ψ; ψ; ) MATHEMATICAL SANS-SERIF BOLD SMALL PSI +1D788;1D788;1D788;03C9;03C9; # (𝞈𝞈; 𝞈𝞈; 𝞈𝞈; ω; ω; ) MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA +1D789;1D789;1D789;2202;2202; # (𝞉𝞉; 𝞉𝞉; 𝞉𝞉; ∂; ∂; ) MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL +1D78A;1D78A;1D78A;03B5;03B5; # (𝞊𝞊; 𝞊𝞊; 𝞊𝞊; ε; ε; ) MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL +1D78B;1D78B;1D78B;03B8;03B8; # (𝞋𝞋; 𝞋𝞋; 𝞋𝞋; θ; θ; ) MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL +1D78C;1D78C;1D78C;03BA;03BA; # (𝞌𝞌; 𝞌𝞌; 𝞌𝞌; κ; κ; ) MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL +1D78D;1D78D;1D78D;03C6;03C6; # (𝞍𝞍; 𝞍𝞍; 𝞍𝞍; φ; φ; ) MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL +1D78E;1D78E;1D78E;03C1;03C1; # (𝞎𝞎; 𝞎𝞎; 𝞎𝞎; ρ; ρ; ) MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL +1D78F;1D78F;1D78F;03C0;03C0; # (𝞏𝞏; 𝞏𝞏; 𝞏𝞏; π; π; ) MATHEMATICAL SANS-SERIF BOLD PI SYMBOL +1D790;1D790;1D790;0391;0391; # (𝞐𝞐; 𝞐𝞐; 𝞐𝞐; Α; Α; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA +1D791;1D791;1D791;0392;0392; # (𝞑𝞑; 𝞑𝞑; 𝞑𝞑; Β; Β; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA +1D792;1D792;1D792;0393;0393; # (𝞒𝞒; 𝞒𝞒; 𝞒𝞒; Γ; Γ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA +1D793;1D793;1D793;0394;0394; # (𝞓𝞓; 𝞓𝞓; 𝞓𝞓; Δ; Δ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA +1D794;1D794;1D794;0395;0395; # (𝞔𝞔; 𝞔𝞔; 𝞔𝞔; Ε; Ε; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON +1D795;1D795;1D795;0396;0396; # (𝞕𝞕; 𝞕𝞕; 𝞕𝞕; Ζ; Ζ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA +1D796;1D796;1D796;0397;0397; # (𝞖𝞖; 𝞖𝞖; 𝞖𝞖; Η; Η; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA +1D797;1D797;1D797;0398;0398; # (𝞗𝞗; 𝞗𝞗; 𝞗𝞗; Θ; Θ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA +1D798;1D798;1D798;0399;0399; # (𝞘𝞘; 𝞘𝞘; 𝞘𝞘; Ι; Ι; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA +1D799;1D799;1D799;039A;039A; # (𝞙𝞙; 𝞙𝞙; 𝞙𝞙; Κ; Κ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA +1D79A;1D79A;1D79A;039B;039B; # (𝞚𝞚; 𝞚𝞚; 𝞚𝞚; Λ; Λ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA +1D79B;1D79B;1D79B;039C;039C; # (𝞛𝞛; 𝞛𝞛; 𝞛𝞛; Μ; Μ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU +1D79C;1D79C;1D79C;039D;039D; # (𝞜𝞜; 𝞜𝞜; 𝞜𝞜; Ν; Ν; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU +1D79D;1D79D;1D79D;039E;039E; # (𝞝𝞝; 𝞝𝞝; 𝞝𝞝; Ξ; Ξ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI +1D79E;1D79E;1D79E;039F;039F; # (𝞞𝞞; 𝞞𝞞; 𝞞𝞞; Ο; Ο; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON +1D79F;1D79F;1D79F;03A0;03A0; # (𝞟𝞟; 𝞟𝞟; 𝞟𝞟; Π; Π; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI +1D7A0;1D7A0;1D7A0;03A1;03A1; # (𝞠𝞠; 𝞠𝞠; 𝞠𝞠; Ρ; Ρ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO +1D7A1;1D7A1;1D7A1;0398;0398; # (𝞡𝞡; 𝞡𝞡; 𝞡𝞡; Θ; Θ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL +1D7A2;1D7A2;1D7A2;03A3;03A3; # (𝞢𝞢; 𝞢𝞢; 𝞢𝞢; Σ; Σ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA +1D7A3;1D7A3;1D7A3;03A4;03A4; # (𝞣𝞣; 𝞣𝞣; 𝞣𝞣; Τ; Τ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU +1D7A4;1D7A4;1D7A4;03A5;03A5; # (𝞤𝞤; 𝞤𝞤; 𝞤𝞤; Υ; Υ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON +1D7A5;1D7A5;1D7A5;03A6;03A6; # (𝞥𝞥; 𝞥𝞥; 𝞥𝞥; Φ; Φ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI +1D7A6;1D7A6;1D7A6;03A7;03A7; # (𝞦𝞦; 𝞦𝞦; 𝞦𝞦; Χ; Χ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI +1D7A7;1D7A7;1D7A7;03A8;03A8; # (𝞧𝞧; 𝞧𝞧; 𝞧𝞧; Ψ; Ψ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI +1D7A8;1D7A8;1D7A8;03A9;03A9; # (𝞨𝞨; 𝞨𝞨; 𝞨𝞨; Ω; Ω; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA +1D7A9;1D7A9;1D7A9;2207;2207; # (𝞩𝞩; 𝞩𝞩; 𝞩𝞩; ∇; ∇; ) MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA +1D7AA;1D7AA;1D7AA;03B1;03B1; # (𝞪𝞪; 𝞪𝞪; 𝞪𝞪; α; α; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA +1D7AB;1D7AB;1D7AB;03B2;03B2; # (𝞫𝞫; 𝞫𝞫; 𝞫𝞫; β; β; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA +1D7AC;1D7AC;1D7AC;03B3;03B3; # (𝞬𝞬; 𝞬𝞬; 𝞬𝞬; γ; γ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA +1D7AD;1D7AD;1D7AD;03B4;03B4; # (𝞭𝞭; 𝞭𝞭; 𝞭𝞭; δ; δ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA +1D7AE;1D7AE;1D7AE;03B5;03B5; # (𝞮𝞮; 𝞮𝞮; 𝞮𝞮; ε; ε; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON +1D7AF;1D7AF;1D7AF;03B6;03B6; # (𝞯𝞯; 𝞯𝞯; 𝞯𝞯; ζ; ζ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA +1D7B0;1D7B0;1D7B0;03B7;03B7; # (𝞰𝞰; 𝞰𝞰; 𝞰𝞰; η; η; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA +1D7B1;1D7B1;1D7B1;03B8;03B8; # (𝞱𝞱; 𝞱𝞱; 𝞱𝞱; θ; θ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA +1D7B2;1D7B2;1D7B2;03B9;03B9; # (𝞲𝞲; 𝞲𝞲; 𝞲𝞲; ι; ι; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA +1D7B3;1D7B3;1D7B3;03BA;03BA; # (𝞳𝞳; 𝞳𝞳; 𝞳𝞳; κ; κ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA +1D7B4;1D7B4;1D7B4;03BB;03BB; # (𝞴𝞴; 𝞴𝞴; 𝞴𝞴; λ; λ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA +1D7B5;1D7B5;1D7B5;03BC;03BC; # (𝞵𝞵; 𝞵𝞵; 𝞵𝞵; μ; μ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU +1D7B6;1D7B6;1D7B6;03BD;03BD; # (𝞶𝞶; 𝞶𝞶; 𝞶𝞶; ν; ν; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU +1D7B7;1D7B7;1D7B7;03BE;03BE; # (𝞷𝞷; 𝞷𝞷; 𝞷𝞷; ξ; ξ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI +1D7B8;1D7B8;1D7B8;03BF;03BF; # (𝞸𝞸; 𝞸𝞸; 𝞸𝞸; ο; ο; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON +1D7B9;1D7B9;1D7B9;03C0;03C0; # (𝞹𝞹; 𝞹𝞹; 𝞹𝞹; π; π; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI +1D7BA;1D7BA;1D7BA;03C1;03C1; # (𝞺𝞺; 𝞺𝞺; 𝞺𝞺; ρ; ρ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO +1D7BB;1D7BB;1D7BB;03C2;03C2; # (𝞻𝞻; 𝞻𝞻; 𝞻𝞻; ς; ς; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA +1D7BC;1D7BC;1D7BC;03C3;03C3; # (𝞼𝞼; 𝞼𝞼; 𝞼𝞼; σ; σ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA +1D7BD;1D7BD;1D7BD;03C4;03C4; # (𝞽𝞽; 𝞽𝞽; 𝞽𝞽; τ; τ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU +1D7BE;1D7BE;1D7BE;03C5;03C5; # (𝞾𝞾; 𝞾𝞾; 𝞾𝞾; υ; υ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON +1D7BF;1D7BF;1D7BF;03C6;03C6; # (𝞿𝞿; 𝞿𝞿; 𝞿𝞿; φ; φ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI +1D7C0;1D7C0;1D7C0;03C7;03C7; # (𝟀𝟀; 𝟀𝟀; 𝟀𝟀; χ; χ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI +1D7C1;1D7C1;1D7C1;03C8;03C8; # (𝟁𝟁; 𝟁𝟁; 𝟁𝟁; ψ; ψ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI +1D7C2;1D7C2;1D7C2;03C9;03C9; # (𝟂𝟂; 𝟂𝟂; 𝟂𝟂; ω; ω; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA +1D7C3;1D7C3;1D7C3;2202;2202; # (𝟃𝟃; 𝟃𝟃; 𝟃𝟃; ∂; ∂; ) MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL +1D7C4;1D7C4;1D7C4;03B5;03B5; # (𝟄𝟄; 𝟄𝟄; 𝟄𝟄; ε; ε; ) MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL +1D7C5;1D7C5;1D7C5;03B8;03B8; # (𝟅𝟅; 𝟅𝟅; 𝟅𝟅; θ; θ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL +1D7C6;1D7C6;1D7C6;03BA;03BA; # (𝟆𝟆; 𝟆𝟆; 𝟆𝟆; κ; κ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL +1D7C7;1D7C7;1D7C7;03C6;03C6; # (𝟇𝟇; 𝟇𝟇; 𝟇𝟇; φ; φ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL +1D7C8;1D7C8;1D7C8;03C1;03C1; # (𝟈𝟈; 𝟈𝟈; 𝟈𝟈; ρ; ρ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL +1D7C9;1D7C9;1D7C9;03C0;03C0; # (𝟉𝟉; 𝟉𝟉; 𝟉𝟉; π; π; ) MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL +1D7CE;1D7CE;1D7CE;0030;0030; # (𝟎𝟎; 𝟎𝟎; 𝟎𝟎; 0; 0; ) MATHEMATICAL BOLD DIGIT ZERO +1D7CF;1D7CF;1D7CF;0031;0031; # (𝟏𝟏; 𝟏𝟏; 𝟏𝟏; 1; 1; ) MATHEMATICAL BOLD DIGIT ONE +1D7D0;1D7D0;1D7D0;0032;0032; # (𝟐𝟐; 𝟐𝟐; 𝟐𝟐; 2; 2; ) MATHEMATICAL BOLD DIGIT TWO +1D7D1;1D7D1;1D7D1;0033;0033; # (𝟑𝟑; 𝟑𝟑; 𝟑𝟑; 3; 3; ) MATHEMATICAL BOLD DIGIT THREE +1D7D2;1D7D2;1D7D2;0034;0034; # (𝟒𝟒; 𝟒𝟒; 𝟒𝟒; 4; 4; ) MATHEMATICAL BOLD DIGIT FOUR +1D7D3;1D7D3;1D7D3;0035;0035; # (𝟓𝟓; 𝟓𝟓; 𝟓𝟓; 5; 5; ) MATHEMATICAL BOLD DIGIT FIVE +1D7D4;1D7D4;1D7D4;0036;0036; # (𝟔𝟔; 𝟔𝟔; 𝟔𝟔; 6; 6; ) MATHEMATICAL BOLD DIGIT SIX +1D7D5;1D7D5;1D7D5;0037;0037; # (𝟕𝟕; 𝟕𝟕; 𝟕𝟕; 7; 7; ) MATHEMATICAL BOLD DIGIT SEVEN +1D7D6;1D7D6;1D7D6;0038;0038; # (𝟖𝟖; 𝟖𝟖; 𝟖𝟖; 8; 8; ) MATHEMATICAL BOLD DIGIT EIGHT +1D7D7;1D7D7;1D7D7;0039;0039; # (𝟗𝟗; 𝟗𝟗; 𝟗𝟗; 9; 9; ) MATHEMATICAL BOLD DIGIT NINE +1D7D8;1D7D8;1D7D8;0030;0030; # (𝟘𝟘; 𝟘𝟘; 𝟘𝟘; 0; 0; ) MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO +1D7D9;1D7D9;1D7D9;0031;0031; # (𝟙𝟙; 𝟙𝟙; 𝟙𝟙; 1; 1; ) MATHEMATICAL DOUBLE-STRUCK DIGIT ONE +1D7DA;1D7DA;1D7DA;0032;0032; # (𝟚𝟚; 𝟚𝟚; 𝟚𝟚; 2; 2; ) MATHEMATICAL DOUBLE-STRUCK DIGIT TWO +1D7DB;1D7DB;1D7DB;0033;0033; # (𝟛𝟛; 𝟛𝟛; 𝟛𝟛; 3; 3; ) MATHEMATICAL DOUBLE-STRUCK DIGIT THREE +1D7DC;1D7DC;1D7DC;0034;0034; # (𝟜𝟜; 𝟜𝟜; 𝟜𝟜; 4; 4; ) MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR +1D7DD;1D7DD;1D7DD;0035;0035; # (𝟝𝟝; 𝟝𝟝; 𝟝𝟝; 5; 5; ) MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE +1D7DE;1D7DE;1D7DE;0036;0036; # (𝟞𝟞; 𝟞𝟞; 𝟞𝟞; 6; 6; ) MATHEMATICAL DOUBLE-STRUCK DIGIT SIX +1D7DF;1D7DF;1D7DF;0037;0037; # (𝟟𝟟; 𝟟𝟟; 𝟟𝟟; 7; 7; ) MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN +1D7E0;1D7E0;1D7E0;0038;0038; # (𝟠𝟠; 𝟠𝟠; 𝟠𝟠; 8; 8; ) MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT +1D7E1;1D7E1;1D7E1;0039;0039; # (𝟡𝟡; 𝟡𝟡; 𝟡𝟡; 9; 9; ) MATHEMATICAL DOUBLE-STRUCK DIGIT NINE +1D7E2;1D7E2;1D7E2;0030;0030; # (𝟢𝟢; 𝟢𝟢; 𝟢𝟢; 0; 0; ) MATHEMATICAL SANS-SERIF DIGIT ZERO +1D7E3;1D7E3;1D7E3;0031;0031; # (𝟣𝟣; 𝟣𝟣; 𝟣𝟣; 1; 1; ) MATHEMATICAL SANS-SERIF DIGIT ONE +1D7E4;1D7E4;1D7E4;0032;0032; # (𝟤𝟤; 𝟤𝟤; 𝟤𝟤; 2; 2; ) MATHEMATICAL SANS-SERIF DIGIT TWO +1D7E5;1D7E5;1D7E5;0033;0033; # (𝟥𝟥; 𝟥𝟥; 𝟥𝟥; 3; 3; ) MATHEMATICAL SANS-SERIF DIGIT THREE +1D7E6;1D7E6;1D7E6;0034;0034; # (𝟦𝟦; 𝟦𝟦; 𝟦𝟦; 4; 4; ) MATHEMATICAL SANS-SERIF DIGIT FOUR +1D7E7;1D7E7;1D7E7;0035;0035; # (𝟧𝟧; 𝟧𝟧; 𝟧𝟧; 5; 5; ) MATHEMATICAL SANS-SERIF DIGIT FIVE +1D7E8;1D7E8;1D7E8;0036;0036; # (𝟨𝟨; 𝟨𝟨; 𝟨𝟨; 6; 6; ) MATHEMATICAL SANS-SERIF DIGIT SIX +1D7E9;1D7E9;1D7E9;0037;0037; # (𝟩𝟩; 𝟩𝟩; 𝟩𝟩; 7; 7; ) MATHEMATICAL SANS-SERIF DIGIT SEVEN +1D7EA;1D7EA;1D7EA;0038;0038; # (𝟪𝟪; 𝟪𝟪; 𝟪𝟪; 8; 8; ) MATHEMATICAL SANS-SERIF DIGIT EIGHT +1D7EB;1D7EB;1D7EB;0039;0039; # (𝟫𝟫; 𝟫𝟫; 𝟫𝟫; 9; 9; ) MATHEMATICAL SANS-SERIF DIGIT NINE +1D7EC;1D7EC;1D7EC;0030;0030; # (𝟬𝟬; 𝟬𝟬; 𝟬𝟬; 0; 0; ) MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO +1D7ED;1D7ED;1D7ED;0031;0031; # (𝟭𝟭; 𝟭𝟭; 𝟭𝟭; 1; 1; ) MATHEMATICAL SANS-SERIF BOLD DIGIT ONE +1D7EE;1D7EE;1D7EE;0032;0032; # (𝟮𝟮; 𝟮𝟮; 𝟮𝟮; 2; 2; ) MATHEMATICAL SANS-SERIF BOLD DIGIT TWO +1D7EF;1D7EF;1D7EF;0033;0033; # (𝟯𝟯; 𝟯𝟯; 𝟯𝟯; 3; 3; ) MATHEMATICAL SANS-SERIF BOLD DIGIT THREE +1D7F0;1D7F0;1D7F0;0034;0034; # (𝟰𝟰; 𝟰𝟰; 𝟰𝟰; 4; 4; ) MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR +1D7F1;1D7F1;1D7F1;0035;0035; # (𝟱𝟱; 𝟱𝟱; 𝟱𝟱; 5; 5; ) MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE +1D7F2;1D7F2;1D7F2;0036;0036; # (𝟲𝟲; 𝟲𝟲; 𝟲𝟲; 6; 6; ) MATHEMATICAL SANS-SERIF BOLD DIGIT SIX +1D7F3;1D7F3;1D7F3;0037;0037; # (𝟳𝟳; 𝟳𝟳; 𝟳𝟳; 7; 7; ) MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN +1D7F4;1D7F4;1D7F4;0038;0038; # (𝟴𝟴; 𝟴𝟴; 𝟴𝟴; 8; 8; ) MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT +1D7F5;1D7F5;1D7F5;0039;0039; # (𝟵𝟵; 𝟵𝟵; 𝟵𝟵; 9; 9; ) MATHEMATICAL SANS-SERIF BOLD DIGIT NINE +1D7F6;1D7F6;1D7F6;0030;0030; # (𝟶𝟶; 𝟶𝟶; 𝟶𝟶; 0; 0; ) MATHEMATICAL MONOSPACE DIGIT ZERO +1D7F7;1D7F7;1D7F7;0031;0031; # (𝟷𝟷; 𝟷𝟷; 𝟷𝟷; 1; 1; ) MATHEMATICAL MONOSPACE DIGIT ONE +1D7F8;1D7F8;1D7F8;0032;0032; # (𝟸𝟸; 𝟸𝟸; 𝟸𝟸; 2; 2; ) MATHEMATICAL MONOSPACE DIGIT TWO +1D7F9;1D7F9;1D7F9;0033;0033; # (𝟹𝟹; 𝟹𝟹; 𝟹𝟹; 3; 3; ) MATHEMATICAL MONOSPACE DIGIT THREE +1D7FA;1D7FA;1D7FA;0034;0034; # (𝟺𝟺; 𝟺𝟺; 𝟺𝟺; 4; 4; ) MATHEMATICAL MONOSPACE DIGIT FOUR +1D7FB;1D7FB;1D7FB;0035;0035; # (𝟻𝟻; 𝟻𝟻; 𝟻𝟻; 5; 5; ) MATHEMATICAL MONOSPACE DIGIT FIVE +1D7FC;1D7FC;1D7FC;0036;0036; # (𝟼𝟼; 𝟼𝟼; 𝟼𝟼; 6; 6; ) MATHEMATICAL MONOSPACE DIGIT SIX +1D7FD;1D7FD;1D7FD;0037;0037; # (𝟽𝟽; 𝟽𝟽; 𝟽𝟽; 7; 7; ) MATHEMATICAL MONOSPACE DIGIT SEVEN +1D7FE;1D7FE;1D7FE;0038;0038; # (𝟾𝟾; 𝟾𝟾; 𝟾𝟾; 8; 8; ) MATHEMATICAL MONOSPACE DIGIT EIGHT +1D7FF;1D7FF;1D7FF;0039;0039; # (𝟿𝟿; 𝟿𝟿; 𝟿𝟿; 9; 9; ) MATHEMATICAL MONOSPACE DIGIT NINE +2F800;4E3D;4E3D;4E3D;4E3D; # (丽丽; 丽; 丽; 丽; 丽; ) CJK COMPATIBILITY IDEOGRAPH-2F800 +2F801;4E38;4E38;4E38;4E38; # (丸丸; 丸; 丸; 丸; 丸; ) CJK COMPATIBILITY IDEOGRAPH-2F801 +2F802;4E41;4E41;4E41;4E41; # (乁乁; 乁; 乁; 乁; 乁; ) CJK COMPATIBILITY IDEOGRAPH-2F802 +2F803;20122;20122;20122;20122; # (𠄢𠄢; 𠄢𠄢; 𠄢𠄢; 𠄢𠄢; 𠄢𠄢; ) CJK COMPATIBILITY IDEOGRAPH-2F803 +2F804;4F60;4F60;4F60;4F60; # (你你; 你; 你; 你; 你; ) CJK COMPATIBILITY IDEOGRAPH-2F804 +2F805;4FAE;4FAE;4FAE;4FAE; # (侮侮; 侮; 侮; 侮; 侮; ) CJK COMPATIBILITY IDEOGRAPH-2F805 +2F806;4FBB;4FBB;4FBB;4FBB; # (侻侻; 侻; 侻; 侻; 侻; ) CJK COMPATIBILITY IDEOGRAPH-2F806 +2F807;5002;5002;5002;5002; # (倂倂; 倂; 倂; 倂; 倂; ) CJK COMPATIBILITY IDEOGRAPH-2F807 +2F808;507A;507A;507A;507A; # (偺偺; 偺; 偺; 偺; 偺; ) CJK COMPATIBILITY IDEOGRAPH-2F808 +2F809;5099;5099;5099;5099; # (備備; 備; 備; 備; 備; ) CJK COMPATIBILITY IDEOGRAPH-2F809 +2F80A;50E7;50E7;50E7;50E7; # (僧僧; 僧; 僧; 僧; 僧; ) CJK COMPATIBILITY IDEOGRAPH-2F80A +2F80B;50CF;50CF;50CF;50CF; # (像像; 像; 像; 像; 像; ) CJK COMPATIBILITY IDEOGRAPH-2F80B +2F80C;349E;349E;349E;349E; # (㒞㒞; 㒞; 㒞; 㒞; 㒞; ) CJK COMPATIBILITY IDEOGRAPH-2F80C +2F80D;2063A;2063A;2063A;2063A; # (𠘺𠘺; 𠘺𠘺; 𠘺𠘺; 𠘺𠘺; 𠘺𠘺; ) CJK COMPATIBILITY IDEOGRAPH-2F80D +2F80E;514D;514D;514D;514D; # (免免; 免; 免; 免; 免; ) CJK COMPATIBILITY IDEOGRAPH-2F80E +2F80F;5154;5154;5154;5154; # (兔兔; 兔; 兔; 兔; 兔; ) CJK COMPATIBILITY IDEOGRAPH-2F80F +2F810;5164;5164;5164;5164; # (兤兤; 兤; 兤; 兤; 兤; ) CJK COMPATIBILITY IDEOGRAPH-2F810 +2F811;5177;5177;5177;5177; # (具具; 具; 具; 具; 具; ) CJK COMPATIBILITY IDEOGRAPH-2F811 +2F812;2051C;2051C;2051C;2051C; # (𠔜𠔜; 𠔜𠔜; 𠔜𠔜; 𠔜𠔜; 𠔜𠔜; ) CJK COMPATIBILITY IDEOGRAPH-2F812 +2F813;34B9;34B9;34B9;34B9; # (㒹㒹; 㒹; 㒹; 㒹; 㒹; ) CJK COMPATIBILITY IDEOGRAPH-2F813 +2F814;5167;5167;5167;5167; # (內內; 內; 內; 內; 內; ) CJK COMPATIBILITY IDEOGRAPH-2F814 +2F815;518D;518D;518D;518D; # (再再; 再; 再; 再; 再; ) CJK COMPATIBILITY IDEOGRAPH-2F815 +2F816;2054B;2054B;2054B;2054B; # (𠕋𠕋; 𠕋𠕋; 𠕋𠕋; 𠕋𠕋; 𠕋𠕋; ) CJK COMPATIBILITY IDEOGRAPH-2F816 +2F817;5197;5197;5197;5197; # (冗冗; 冗; 冗; 冗; 冗; ) CJK COMPATIBILITY IDEOGRAPH-2F817 +2F818;51A4;51A4;51A4;51A4; # (冤冤; 冤; 冤; 冤; 冤; ) CJK COMPATIBILITY IDEOGRAPH-2F818 +2F819;4ECC;4ECC;4ECC;4ECC; # (仌仌; 仌; 仌; 仌; 仌; ) CJK COMPATIBILITY IDEOGRAPH-2F819 +2F81A;51AC;51AC;51AC;51AC; # (冬冬; 冬; 冬; 冬; 冬; ) CJK COMPATIBILITY IDEOGRAPH-2F81A +2F81B;51B5;51B5;51B5;51B5; # (况况; 况; 况; 况; 况; ) CJK COMPATIBILITY IDEOGRAPH-2F81B +2F81C;291DF;291DF;291DF;291DF; # (𩇟𩇟; 𩇟𩇟; 𩇟𩇟; 𩇟𩇟; 𩇟𩇟; ) CJK COMPATIBILITY IDEOGRAPH-2F81C +2F81D;51F5;51F5;51F5;51F5; # (凵凵; 凵; 凵; 凵; 凵; ) CJK COMPATIBILITY IDEOGRAPH-2F81D +2F81E;5203;5203;5203;5203; # (刃刃; 刃; 刃; 刃; 刃; ) CJK COMPATIBILITY IDEOGRAPH-2F81E +2F81F;34DF;34DF;34DF;34DF; # (㓟㓟; 㓟; 㓟; 㓟; 㓟; ) CJK COMPATIBILITY IDEOGRAPH-2F81F +2F820;523B;523B;523B;523B; # (刻刻; 刻; 刻; 刻; 刻; ) CJK COMPATIBILITY IDEOGRAPH-2F820 +2F821;5246;5246;5246;5246; # (剆剆; 剆; 剆; 剆; 剆; ) CJK COMPATIBILITY IDEOGRAPH-2F821 +2F822;5272;5272;5272;5272; # (割割; 割; 割; 割; 割; ) CJK COMPATIBILITY IDEOGRAPH-2F822 +2F823;5277;5277;5277;5277; # (剷剷; 剷; 剷; 剷; 剷; ) CJK COMPATIBILITY IDEOGRAPH-2F823 +2F824;3515;3515;3515;3515; # (㔕㔕; 㔕; 㔕; 㔕; 㔕; ) CJK COMPATIBILITY IDEOGRAPH-2F824 +2F825;52C7;52C7;52C7;52C7; # (勇勇; 勇; 勇; 勇; 勇; ) CJK COMPATIBILITY IDEOGRAPH-2F825 +2F826;52C9;52C9;52C9;52C9; # (勉勉; 勉; 勉; 勉; 勉; ) CJK COMPATIBILITY IDEOGRAPH-2F826 +2F827;52E4;52E4;52E4;52E4; # (勤勤; 勤; 勤; 勤; 勤; ) CJK COMPATIBILITY IDEOGRAPH-2F827 +2F828;52FA;52FA;52FA;52FA; # (勺勺; 勺; 勺; 勺; 勺; ) CJK COMPATIBILITY IDEOGRAPH-2F828 +2F829;5305;5305;5305;5305; # (包包; 包; 包; 包; 包; ) CJK COMPATIBILITY IDEOGRAPH-2F829 +2F82A;5306;5306;5306;5306; # (匆匆; 匆; 匆; 匆; 匆; ) CJK COMPATIBILITY IDEOGRAPH-2F82A +2F82B;5317;5317;5317;5317; # (北北; 北; 北; 北; 北; ) CJK COMPATIBILITY IDEOGRAPH-2F82B +2F82C;5349;5349;5349;5349; # (卉卉; 卉; 卉; 卉; 卉; ) CJK COMPATIBILITY IDEOGRAPH-2F82C +2F82D;5351;5351;5351;5351; # (卑卑; 卑; 卑; 卑; 卑; ) CJK COMPATIBILITY IDEOGRAPH-2F82D +2F82E;535A;535A;535A;535A; # (博博; 博; 博; 博; 博; ) CJK COMPATIBILITY IDEOGRAPH-2F82E +2F82F;5373;5373;5373;5373; # (即即; 即; 即; 即; 即; ) CJK COMPATIBILITY IDEOGRAPH-2F82F +2F830;537D;537D;537D;537D; # (卽卽; 卽; 卽; 卽; 卽; ) CJK COMPATIBILITY IDEOGRAPH-2F830 +2F831;537F;537F;537F;537F; # (卿卿; 卿; 卿; 卿; 卿; ) CJK COMPATIBILITY IDEOGRAPH-2F831 +2F832;537F;537F;537F;537F; # (卿卿; 卿; 卿; 卿; 卿; ) CJK COMPATIBILITY IDEOGRAPH-2F832 +2F833;537F;537F;537F;537F; # (卿卿; 卿; 卿; 卿; 卿; ) CJK COMPATIBILITY IDEOGRAPH-2F833 +2F834;20A2C;20A2C;20A2C;20A2C; # (𠨬𠨬; 𠨬𠨬; 𠨬𠨬; 𠨬𠨬; 𠨬𠨬; ) CJK COMPATIBILITY IDEOGRAPH-2F834 +2F835;7070;7070;7070;7070; # (灰灰; 灰; 灰; 灰; 灰; ) CJK COMPATIBILITY IDEOGRAPH-2F835 +2F836;53CA;53CA;53CA;53CA; # (及及; 及; 及; 及; 及; ) CJK COMPATIBILITY IDEOGRAPH-2F836 +2F837;53DF;53DF;53DF;53DF; # (叟叟; 叟; 叟; 叟; 叟; ) CJK COMPATIBILITY IDEOGRAPH-2F837 +2F838;20B63;20B63;20B63;20B63; # (𠭣𠭣; 𠭣𠭣; 𠭣𠭣; 𠭣𠭣; 𠭣𠭣; ) CJK COMPATIBILITY IDEOGRAPH-2F838 +2F839;53EB;53EB;53EB;53EB; # (叫叫; 叫; 叫; 叫; 叫; ) CJK COMPATIBILITY IDEOGRAPH-2F839 +2F83A;53F1;53F1;53F1;53F1; # (叱叱; 叱; 叱; 叱; 叱; ) CJK COMPATIBILITY IDEOGRAPH-2F83A +2F83B;5406;5406;5406;5406; # (吆吆; 吆; 吆; 吆; 吆; ) CJK COMPATIBILITY IDEOGRAPH-2F83B +2F83C;549E;549E;549E;549E; # (咞咞; 咞; 咞; 咞; 咞; ) CJK COMPATIBILITY IDEOGRAPH-2F83C +2F83D;5438;5438;5438;5438; # (吸吸; 吸; 吸; 吸; 吸; ) CJK COMPATIBILITY IDEOGRAPH-2F83D +2F83E;5448;5448;5448;5448; # (呈呈; 呈; 呈; 呈; 呈; ) CJK COMPATIBILITY IDEOGRAPH-2F83E +2F83F;5468;5468;5468;5468; # (周周; 周; 周; 周; 周; ) CJK COMPATIBILITY IDEOGRAPH-2F83F +2F840;54A2;54A2;54A2;54A2; # (咢咢; 咢; 咢; 咢; 咢; ) CJK COMPATIBILITY IDEOGRAPH-2F840 +2F841;54F6;54F6;54F6;54F6; # (哶哶; 哶; 哶; 哶; 哶; ) CJK COMPATIBILITY IDEOGRAPH-2F841 +2F842;5510;5510;5510;5510; # (唐唐; 唐; 唐; 唐; 唐; ) CJK COMPATIBILITY IDEOGRAPH-2F842 +2F843;5553;5553;5553;5553; # (啓啓; 啓; 啓; 啓; 啓; ) CJK COMPATIBILITY IDEOGRAPH-2F843 +2F844;5563;5563;5563;5563; # (啣啣; 啣; 啣; 啣; 啣; ) CJK COMPATIBILITY IDEOGRAPH-2F844 +2F845;5584;5584;5584;5584; # (善善; 善; 善; 善; 善; ) CJK COMPATIBILITY IDEOGRAPH-2F845 +2F846;5584;5584;5584;5584; # (善善; 善; 善; 善; 善; ) CJK COMPATIBILITY IDEOGRAPH-2F846 +2F847;5599;5599;5599;5599; # (喙喙; 喙; 喙; 喙; 喙; ) CJK COMPATIBILITY IDEOGRAPH-2F847 +2F848;55AB;55AB;55AB;55AB; # (喫喫; 喫; 喫; 喫; 喫; ) CJK COMPATIBILITY IDEOGRAPH-2F848 +2F849;55B3;55B3;55B3;55B3; # (喳喳; 喳; 喳; 喳; 喳; ) CJK COMPATIBILITY IDEOGRAPH-2F849 +2F84A;55C2;55C2;55C2;55C2; # (嗂嗂; 嗂; 嗂; 嗂; 嗂; ) CJK COMPATIBILITY IDEOGRAPH-2F84A +2F84B;5716;5716;5716;5716; # (圖圖; 圖; 圖; 圖; 圖; ) CJK COMPATIBILITY IDEOGRAPH-2F84B +2F84C;5606;5606;5606;5606; # (嘆嘆; 嘆; 嘆; 嘆; 嘆; ) CJK COMPATIBILITY IDEOGRAPH-2F84C +2F84D;5717;5717;5717;5717; # (圗圗; 圗; 圗; 圗; 圗; ) CJK COMPATIBILITY IDEOGRAPH-2F84D +2F84E;5651;5651;5651;5651; # (噑噑; 噑; 噑; 噑; 噑; ) CJK COMPATIBILITY IDEOGRAPH-2F84E +2F84F;5674;5674;5674;5674; # (噴噴; 噴; 噴; 噴; 噴; ) CJK COMPATIBILITY IDEOGRAPH-2F84F +2F850;5207;5207;5207;5207; # (切切; 切; 切; 切; 切; ) CJK COMPATIBILITY IDEOGRAPH-2F850 +2F851;58EE;58EE;58EE;58EE; # (壮壮; 壮; 壮; 壮; 壮; ) CJK COMPATIBILITY IDEOGRAPH-2F851 +2F852;57CE;57CE;57CE;57CE; # (城城; 城; 城; 城; 城; ) CJK COMPATIBILITY IDEOGRAPH-2F852 +2F853;57F4;57F4;57F4;57F4; # (埴埴; 埴; 埴; 埴; 埴; ) CJK COMPATIBILITY IDEOGRAPH-2F853 +2F854;580D;580D;580D;580D; # (堍堍; 堍; 堍; 堍; 堍; ) CJK COMPATIBILITY IDEOGRAPH-2F854 +2F855;578B;578B;578B;578B; # (型型; 型; 型; 型; 型; ) CJK COMPATIBILITY IDEOGRAPH-2F855 +2F856;5832;5832;5832;5832; # (堲堲; 堲; 堲; 堲; 堲; ) CJK COMPATIBILITY IDEOGRAPH-2F856 +2F857;5831;5831;5831;5831; # (報報; 報; 報; 報; 報; ) CJK COMPATIBILITY IDEOGRAPH-2F857 +2F858;58AC;58AC;58AC;58AC; # (墬墬; 墬; 墬; 墬; 墬; ) CJK COMPATIBILITY IDEOGRAPH-2F858 +2F859;214E4;214E4;214E4;214E4; # (𡓤𡓤; 𡓤𡓤; 𡓤𡓤; 𡓤𡓤; 𡓤𡓤; ) CJK COMPATIBILITY IDEOGRAPH-2F859 +2F85A;58F2;58F2;58F2;58F2; # (売売; 売; 売; 売; 売; ) CJK COMPATIBILITY IDEOGRAPH-2F85A +2F85B;58F7;58F7;58F7;58F7; # (壷壷; 壷; 壷; 壷; 壷; ) CJK COMPATIBILITY IDEOGRAPH-2F85B +2F85C;5906;5906;5906;5906; # (夆夆; 夆; 夆; 夆; 夆; ) CJK COMPATIBILITY IDEOGRAPH-2F85C +2F85D;591A;591A;591A;591A; # (多多; 多; 多; 多; 多; ) CJK COMPATIBILITY IDEOGRAPH-2F85D +2F85E;5922;5922;5922;5922; # (夢夢; 夢; 夢; 夢; 夢; ) CJK COMPATIBILITY IDEOGRAPH-2F85E +2F85F;5962;5962;5962;5962; # (奢奢; 奢; 奢; 奢; 奢; ) CJK COMPATIBILITY IDEOGRAPH-2F85F +2F860;216A8;216A8;216A8;216A8; # (𡚨𡚨; 𡚨𡚨; 𡚨𡚨; 𡚨𡚨; 𡚨𡚨; ) CJK COMPATIBILITY IDEOGRAPH-2F860 +2F861;216EA;216EA;216EA;216EA; # (𡛪𡛪; 𡛪𡛪; 𡛪𡛪; 𡛪𡛪; 𡛪𡛪; ) CJK COMPATIBILITY IDEOGRAPH-2F861 +2F862;59EC;59EC;59EC;59EC; # (姬姬; 姬; 姬; 姬; 姬; ) CJK COMPATIBILITY IDEOGRAPH-2F862 +2F863;5A1B;5A1B;5A1B;5A1B; # (娛娛; 娛; 娛; 娛; 娛; ) CJK COMPATIBILITY IDEOGRAPH-2F863 +2F864;5A27;5A27;5A27;5A27; # (娧娧; 娧; 娧; 娧; 娧; ) CJK COMPATIBILITY IDEOGRAPH-2F864 +2F865;59D8;59D8;59D8;59D8; # (姘姘; 姘; 姘; 姘; 姘; ) CJK COMPATIBILITY IDEOGRAPH-2F865 +2F866;5A66;5A66;5A66;5A66; # (婦婦; 婦; 婦; 婦; 婦; ) CJK COMPATIBILITY IDEOGRAPH-2F866 +2F867;36EE;36EE;36EE;36EE; # (㛮㛮; 㛮; 㛮; 㛮; 㛮; ) CJK COMPATIBILITY IDEOGRAPH-2F867 +2F868;2136A;2136A;2136A;2136A; # (㛼㛼; 𡍪𡍪; 𡍪𡍪; 𡍪𡍪; 𡍪𡍪; ) CJK COMPATIBILITY IDEOGRAPH-2F868 +2F869;5B08;5B08;5B08;5B08; # (嬈嬈; 嬈; 嬈; 嬈; 嬈; ) CJK COMPATIBILITY IDEOGRAPH-2F869 +2F86A;5B3E;5B3E;5B3E;5B3E; # (嬾嬾; 嬾; 嬾; 嬾; 嬾; ) CJK COMPATIBILITY IDEOGRAPH-2F86A +2F86B;5B3E;5B3E;5B3E;5B3E; # (嬾嬾; 嬾; 嬾; 嬾; 嬾; ) CJK COMPATIBILITY IDEOGRAPH-2F86B +2F86C;219C8;219C8;219C8;219C8; # (𡧈𡧈; 𡧈𡧈; 𡧈𡧈; 𡧈𡧈; 𡧈𡧈; ) CJK COMPATIBILITY IDEOGRAPH-2F86C +2F86D;5BC3;5BC3;5BC3;5BC3; # (寃寃; 寃; 寃; 寃; 寃; ) CJK COMPATIBILITY IDEOGRAPH-2F86D +2F86E;5BD8;5BD8;5BD8;5BD8; # (寘寘; 寘; 寘; 寘; 寘; ) CJK COMPATIBILITY IDEOGRAPH-2F86E +2F86F;5BE7;5BE7;5BE7;5BE7; # (寧寧; 寧; 寧; 寧; 寧; ) CJK COMPATIBILITY IDEOGRAPH-2F86F +2F870;5BF3;5BF3;5BF3;5BF3; # (寳寳; 寳; 寳; 寳; 寳; ) CJK COMPATIBILITY IDEOGRAPH-2F870 +2F871;21B18;21B18;21B18;21B18; # (𡬘𡬘; 𡬘𡬘; 𡬘𡬘; 𡬘𡬘; 𡬘𡬘; ) CJK COMPATIBILITY IDEOGRAPH-2F871 +2F872;5BFF;5BFF;5BFF;5BFF; # (寿寿; 寿; 寿; 寿; 寿; ) CJK COMPATIBILITY IDEOGRAPH-2F872 +2F873;5C06;5C06;5C06;5C06; # (将将; 将; 将; 将; 将; ) CJK COMPATIBILITY IDEOGRAPH-2F873 +2F874;5F33;5F33;5F33;5F33; # (当当; 弳; 弳; 弳; 弳; ) CJK COMPATIBILITY IDEOGRAPH-2F874 +2F875;5C22;5C22;5C22;5C22; # (尢尢; 尢; 尢; 尢; 尢; ) CJK COMPATIBILITY IDEOGRAPH-2F875 +2F876;3781;3781;3781;3781; # (㞁㞁; 㞁; 㞁; 㞁; 㞁; ) CJK COMPATIBILITY IDEOGRAPH-2F876 +2F877;5C60;5C60;5C60;5C60; # (屠屠; 屠; 屠; 屠; 屠; ) CJK COMPATIBILITY IDEOGRAPH-2F877 +2F878;5C6E;5C6E;5C6E;5C6E; # (屮屮; 屮; 屮; 屮; 屮; ) CJK COMPATIBILITY IDEOGRAPH-2F878 +2F879;5CC0;5CC0;5CC0;5CC0; # (峀峀; 峀; 峀; 峀; 峀; ) CJK COMPATIBILITY IDEOGRAPH-2F879 +2F87A;5C8D;5C8D;5C8D;5C8D; # (岍岍; 岍; 岍; 岍; 岍; ) CJK COMPATIBILITY IDEOGRAPH-2F87A +2F87B;21DE4;21DE4;21DE4;21DE4; # (𡷤𡷤; 𡷤𡷤; 𡷤𡷤; 𡷤𡷤; 𡷤𡷤; ) CJK COMPATIBILITY IDEOGRAPH-2F87B +2F87C;5D43;5D43;5D43;5D43; # (嵃嵃; 嵃; 嵃; 嵃; 嵃; ) CJK COMPATIBILITY IDEOGRAPH-2F87C +2F87D;21DE6;21DE6;21DE6;21DE6; # (𡷦𡷦; 𡷦𡷦; 𡷦𡷦; 𡷦𡷦; 𡷦𡷦; ) CJK COMPATIBILITY IDEOGRAPH-2F87D +2F87E;5D6E;5D6E;5D6E;5D6E; # (嵮嵮; 嵮; 嵮; 嵮; 嵮; ) CJK COMPATIBILITY IDEOGRAPH-2F87E +2F87F;5D6B;5D6B;5D6B;5D6B; # (嵫嵫; 嵫; 嵫; 嵫; 嵫; ) CJK COMPATIBILITY IDEOGRAPH-2F87F +2F880;5D7C;5D7C;5D7C;5D7C; # (嵼嵼; 嵼; 嵼; 嵼; 嵼; ) CJK COMPATIBILITY IDEOGRAPH-2F880 +2F881;5DE1;5DE1;5DE1;5DE1; # (巡巡; 巡; 巡; 巡; 巡; ) CJK COMPATIBILITY IDEOGRAPH-2F881 +2F882;5DE2;5DE2;5DE2;5DE2; # (巢巢; 巢; 巢; 巢; 巢; ) CJK COMPATIBILITY IDEOGRAPH-2F882 +2F883;382F;382F;382F;382F; # (㠯㠯; 㠯; 㠯; 㠯; 㠯; ) CJK COMPATIBILITY IDEOGRAPH-2F883 +2F884;5DFD;5DFD;5DFD;5DFD; # (巽巽; 巽; 巽; 巽; 巽; ) CJK COMPATIBILITY IDEOGRAPH-2F884 +2F885;5E28;5E28;5E28;5E28; # (帨帨; 帨; 帨; 帨; 帨; ) CJK COMPATIBILITY IDEOGRAPH-2F885 +2F886;5E3D;5E3D;5E3D;5E3D; # (帽帽; 帽; 帽; 帽; 帽; ) CJK COMPATIBILITY IDEOGRAPH-2F886 +2F887;5E69;5E69;5E69;5E69; # (幩幩; 幩; 幩; 幩; 幩; ) CJK COMPATIBILITY IDEOGRAPH-2F887 +2F888;3862;3862;3862;3862; # (㡢㡢; 㡢; 㡢; 㡢; 㡢; ) CJK COMPATIBILITY IDEOGRAPH-2F888 +2F889;22183;22183;22183;22183; # (𢆃𢆃; 𢆃𢆃; 𢆃𢆃; 𢆃𢆃; 𢆃𢆃; ) CJK COMPATIBILITY IDEOGRAPH-2F889 +2F88A;387C;387C;387C;387C; # (㡼㡼; 㡼; 㡼; 㡼; 㡼; ) CJK COMPATIBILITY IDEOGRAPH-2F88A +2F88B;5EB0;5EB0;5EB0;5EB0; # (庰庰; 庰; 庰; 庰; 庰; ) CJK COMPATIBILITY IDEOGRAPH-2F88B +2F88C;5EB3;5EB3;5EB3;5EB3; # (庳庳; 庳; 庳; 庳; 庳; ) CJK COMPATIBILITY IDEOGRAPH-2F88C +2F88D;5EB6;5EB6;5EB6;5EB6; # (庶庶; 庶; 庶; 庶; 庶; ) CJK COMPATIBILITY IDEOGRAPH-2F88D +2F88E;5ECA;5ECA;5ECA;5ECA; # (廊廊; 廊; 廊; 廊; 廊; ) CJK COMPATIBILITY IDEOGRAPH-2F88E +2F88F;2A392;2A392;2A392;2A392; # (𪎒𪎒; 𪎒𪎒; 𪎒𪎒; 𪎒𪎒; 𪎒𪎒; ) CJK COMPATIBILITY IDEOGRAPH-2F88F +2F890;5EFE;5EFE;5EFE;5EFE; # (廾廾; 廾; 廾; 廾; 廾; ) CJK COMPATIBILITY IDEOGRAPH-2F890 +2F891;22331;22331;22331;22331; # (𢌱𢌱; 𢌱𢌱; 𢌱𢌱; 𢌱𢌱; 𢌱𢌱; ) CJK COMPATIBILITY IDEOGRAPH-2F891 +2F892;22331;22331;22331;22331; # (𢌱𢌱; 𢌱𢌱; 𢌱𢌱; 𢌱𢌱; 𢌱𢌱; ) CJK COMPATIBILITY IDEOGRAPH-2F892 +2F893;8201;8201;8201;8201; # (舁舁; 舁; 舁; 舁; 舁; ) CJK COMPATIBILITY IDEOGRAPH-2F893 +2F894;5F22;5F22;5F22;5F22; # (弢弢; 弢; 弢; 弢; 弢; ) CJK COMPATIBILITY IDEOGRAPH-2F894 +2F895;5F22;5F22;5F22;5F22; # (弢弢; 弢; 弢; 弢; 弢; ) CJK COMPATIBILITY IDEOGRAPH-2F895 +2F896;38C7;38C7;38C7;38C7; # (㣇㣇; 㣇; 㣇; 㣇; 㣇; ) CJK COMPATIBILITY IDEOGRAPH-2F896 +2F897;232B8;232B8;232B8;232B8; # (𣊸𣊸; 𣊸𣊸; 𣊸𣊸; 𣊸𣊸; 𣊸𣊸; ) CJK COMPATIBILITY IDEOGRAPH-2F897 +2F898;261DA;261DA;261DA;261DA; # (𦇚𦇚; 𦇚𦇚; 𦇚𦇚; 𦇚𦇚; 𦇚𦇚; ) CJK COMPATIBILITY IDEOGRAPH-2F898 +2F899;5F62;5F62;5F62;5F62; # (形形; 形; 形; 形; 形; ) CJK COMPATIBILITY IDEOGRAPH-2F899 +2F89A;5F6B;5F6B;5F6B;5F6B; # (彫彫; 彫; 彫; 彫; 彫; ) CJK COMPATIBILITY IDEOGRAPH-2F89A +2F89B;38E3;38E3;38E3;38E3; # (㣣㣣; 㣣; 㣣; 㣣; 㣣; ) CJK COMPATIBILITY IDEOGRAPH-2F89B +2F89C;5F9A;5F9A;5F9A;5F9A; # (徚徚; 徚; 徚; 徚; 徚; ) CJK COMPATIBILITY IDEOGRAPH-2F89C +2F89D;5FCD;5FCD;5FCD;5FCD; # (忍忍; 忍; 忍; 忍; 忍; ) CJK COMPATIBILITY IDEOGRAPH-2F89D +2F89E;5FD7;5FD7;5FD7;5FD7; # (志志; 志; 志; 志; 志; ) CJK COMPATIBILITY IDEOGRAPH-2F89E +2F89F;5FF9;5FF9;5FF9;5FF9; # (忹忹; 忹; 忹; 忹; 忹; ) CJK COMPATIBILITY IDEOGRAPH-2F89F +2F8A0;6081;6081;6081;6081; # (悁悁; 悁; 悁; 悁; 悁; ) CJK COMPATIBILITY IDEOGRAPH-2F8A0 +2F8A1;393A;393A;393A;393A; # (㤺㤺; 㤺; 㤺; 㤺; 㤺; ) CJK COMPATIBILITY IDEOGRAPH-2F8A1 +2F8A2;391C;391C;391C;391C; # (㤜㤜; 㤜; 㤜; 㤜; 㤜; ) CJK COMPATIBILITY IDEOGRAPH-2F8A2 +2F8A3;6094;6094;6094;6094; # (悔悔; 悔; 悔; 悔; 悔; ) CJK COMPATIBILITY IDEOGRAPH-2F8A3 +2F8A4;226D4;226D4;226D4;226D4; # (𢛔𢛔; 𢛔𢛔; 𢛔𢛔; 𢛔𢛔; 𢛔𢛔; ) CJK COMPATIBILITY IDEOGRAPH-2F8A4 +2F8A5;60C7;60C7;60C7;60C7; # (惇惇; 惇; 惇; 惇; 惇; ) CJK COMPATIBILITY IDEOGRAPH-2F8A5 +2F8A6;6148;6148;6148;6148; # (慈慈; 慈; 慈; 慈; 慈; ) CJK COMPATIBILITY IDEOGRAPH-2F8A6 +2F8A7;614C;614C;614C;614C; # (慌慌; 慌; 慌; 慌; 慌; ) CJK COMPATIBILITY IDEOGRAPH-2F8A7 +2F8A8;614E;614E;614E;614E; # (慎慎; 慎; 慎; 慎; 慎; ) CJK COMPATIBILITY IDEOGRAPH-2F8A8 +2F8A9;614C;614C;614C;614C; # (慌慌; 慌; 慌; 慌; 慌; ) CJK COMPATIBILITY IDEOGRAPH-2F8A9 +2F8AA;617A;617A;617A;617A; # (慺慺; 慺; 慺; 慺; 慺; ) CJK COMPATIBILITY IDEOGRAPH-2F8AA +2F8AB;618E;618E;618E;618E; # (憎憎; 憎; 憎; 憎; 憎; ) CJK COMPATIBILITY IDEOGRAPH-2F8AB +2F8AC;61B2;61B2;61B2;61B2; # (憲憲; 憲; 憲; 憲; 憲; ) CJK COMPATIBILITY IDEOGRAPH-2F8AC +2F8AD;61A4;61A4;61A4;61A4; # (憤憤; 憤; 憤; 憤; 憤; ) CJK COMPATIBILITY IDEOGRAPH-2F8AD +2F8AE;61AF;61AF;61AF;61AF; # (憯憯; 憯; 憯; 憯; 憯; ) CJK COMPATIBILITY IDEOGRAPH-2F8AE +2F8AF;61DE;61DE;61DE;61DE; # (懞懞; 懞; 懞; 懞; 懞; ) CJK COMPATIBILITY IDEOGRAPH-2F8AF +2F8B0;61F2;61F2;61F2;61F2; # (懲懲; 懲; 懲; 懲; 懲; ) CJK COMPATIBILITY IDEOGRAPH-2F8B0 +2F8B1;61F6;61F6;61F6;61F6; # (懶懶; 懶; 懶; 懶; 懶; ) CJK COMPATIBILITY IDEOGRAPH-2F8B1 +2F8B2;6210;6210;6210;6210; # (成成; 成; 成; 成; 成; ) CJK COMPATIBILITY IDEOGRAPH-2F8B2 +2F8B3;621B;621B;621B;621B; # (戛戛; 戛; 戛; 戛; 戛; ) CJK COMPATIBILITY IDEOGRAPH-2F8B3 +2F8B4;625D;625D;625D;625D; # (扝扝; 扝; 扝; 扝; 扝; ) CJK COMPATIBILITY IDEOGRAPH-2F8B4 +2F8B5;62B1;62B1;62B1;62B1; # (抱抱; 抱; 抱; 抱; 抱; ) CJK COMPATIBILITY IDEOGRAPH-2F8B5 +2F8B6;62D4;62D4;62D4;62D4; # (拔拔; 拔; 拔; 拔; 拔; ) CJK COMPATIBILITY IDEOGRAPH-2F8B6 +2F8B7;6350;6350;6350;6350; # (捐捐; 捐; 捐; 捐; 捐; ) CJK COMPATIBILITY IDEOGRAPH-2F8B7 +2F8B8;22B0C;22B0C;22B0C;22B0C; # (𢬌𢬌; 𢬌𢬌; 𢬌𢬌; 𢬌𢬌; 𢬌𢬌; ) CJK COMPATIBILITY IDEOGRAPH-2F8B8 +2F8B9;633D;633D;633D;633D; # (挽挽; 挽; 挽; 挽; 挽; ) CJK COMPATIBILITY IDEOGRAPH-2F8B9 +2F8BA;62FC;62FC;62FC;62FC; # (拼拼; 拼; 拼; 拼; 拼; ) CJK COMPATIBILITY IDEOGRAPH-2F8BA +2F8BB;6368;6368;6368;6368; # (捨捨; 捨; 捨; 捨; 捨; ) CJK COMPATIBILITY IDEOGRAPH-2F8BB +2F8BC;6383;6383;6383;6383; # (掃掃; 掃; 掃; 掃; 掃; ) CJK COMPATIBILITY IDEOGRAPH-2F8BC +2F8BD;63E4;63E4;63E4;63E4; # (揤揤; 揤; 揤; 揤; 揤; ) CJK COMPATIBILITY IDEOGRAPH-2F8BD +2F8BE;22BF1;22BF1;22BF1;22BF1; # (𢯱𢯱; 𢯱𢯱; 𢯱𢯱; 𢯱𢯱; 𢯱𢯱; ) CJK COMPATIBILITY IDEOGRAPH-2F8BE +2F8BF;6422;6422;6422;6422; # (搢搢; 搢; 搢; 搢; 搢; ) CJK COMPATIBILITY IDEOGRAPH-2F8BF +2F8C0;63C5;63C5;63C5;63C5; # (揅揅; 揅; 揅; 揅; 揅; ) CJK COMPATIBILITY IDEOGRAPH-2F8C0 +2F8C1;63A9;63A9;63A9;63A9; # (掩掩; 掩; 掩; 掩; 掩; ) CJK COMPATIBILITY IDEOGRAPH-2F8C1 +2F8C2;3A2E;3A2E;3A2E;3A2E; # (㨮㨮; 㨮; 㨮; 㨮; 㨮; ) CJK COMPATIBILITY IDEOGRAPH-2F8C2 +2F8C3;6469;6469;6469;6469; # (摩摩; 摩; 摩; 摩; 摩; ) CJK COMPATIBILITY IDEOGRAPH-2F8C3 +2F8C4;647E;647E;647E;647E; # (摾摾; 摾; 摾; 摾; 摾; ) CJK COMPATIBILITY IDEOGRAPH-2F8C4 +2F8C5;649D;649D;649D;649D; # (撝撝; 撝; 撝; 撝; 撝; ) CJK COMPATIBILITY IDEOGRAPH-2F8C5 +2F8C6;6477;6477;6477;6477; # (摷摷; 摷; 摷; 摷; 摷; ) CJK COMPATIBILITY IDEOGRAPH-2F8C6 +2F8C7;3A6C;3A6C;3A6C;3A6C; # (㩬㩬; 㩬; 㩬; 㩬; 㩬; ) CJK COMPATIBILITY IDEOGRAPH-2F8C7 +2F8C8;654F;654F;654F;654F; # (敏敏; 敏; 敏; 敏; 敏; ) CJK COMPATIBILITY IDEOGRAPH-2F8C8 +2F8C9;656C;656C;656C;656C; # (敬敬; 敬; 敬; 敬; 敬; ) CJK COMPATIBILITY IDEOGRAPH-2F8C9 +2F8CA;2300A;2300A;2300A;2300A; # (𣀊𣀊; 𣀊𣀊; 𣀊𣀊; 𣀊𣀊; 𣀊𣀊; ) CJK COMPATIBILITY IDEOGRAPH-2F8CA +2F8CB;65E3;65E3;65E3;65E3; # (旣旣; 旣; 旣; 旣; 旣; ) CJK COMPATIBILITY IDEOGRAPH-2F8CB +2F8CC;66F8;66F8;66F8;66F8; # (書書; 書; 書; 書; 書; ) CJK COMPATIBILITY IDEOGRAPH-2F8CC +2F8CD;6649;6649;6649;6649; # (晉晉; 晉; 晉; 晉; 晉; ) CJK COMPATIBILITY IDEOGRAPH-2F8CD +2F8CE;3B19;3B19;3B19;3B19; # (㬙㬙; 㬙; 㬙; 㬙; 㬙; ) CJK COMPATIBILITY IDEOGRAPH-2F8CE +2F8CF;6691;6691;6691;6691; # (暑暑; 暑; 暑; 暑; 暑; ) CJK COMPATIBILITY IDEOGRAPH-2F8CF +2F8D0;3B08;3B08;3B08;3B08; # (㬈㬈; 㬈; 㬈; 㬈; 㬈; ) CJK COMPATIBILITY IDEOGRAPH-2F8D0 +2F8D1;3AE4;3AE4;3AE4;3AE4; # (㫤㫤; 㫤; 㫤; 㫤; 㫤; ) CJK COMPATIBILITY IDEOGRAPH-2F8D1 +2F8D2;5192;5192;5192;5192; # (冒冒; 冒; 冒; 冒; 冒; ) CJK COMPATIBILITY IDEOGRAPH-2F8D2 +2F8D3;5195;5195;5195;5195; # (冕冕; 冕; 冕; 冕; 冕; ) CJK COMPATIBILITY IDEOGRAPH-2F8D3 +2F8D4;6700;6700;6700;6700; # (最最; 最; 最; 最; 最; ) CJK COMPATIBILITY IDEOGRAPH-2F8D4 +2F8D5;669C;669C;669C;669C; # (暜暜; 暜; 暜; 暜; 暜; ) CJK COMPATIBILITY IDEOGRAPH-2F8D5 +2F8D6;80AD;80AD;80AD;80AD; # (肭肭; 肭; 肭; 肭; 肭; ) CJK COMPATIBILITY IDEOGRAPH-2F8D6 +2F8D7;43D9;43D9;43D9;43D9; # (䏙䏙; 䏙; 䏙; 䏙; 䏙; ) CJK COMPATIBILITY IDEOGRAPH-2F8D7 +2F8D8;6717;6717;6717;6717; # (朗朗; 朗; 朗; 朗; 朗; ) CJK COMPATIBILITY IDEOGRAPH-2F8D8 +2F8D9;671B;671B;671B;671B; # (望望; 望; 望; 望; 望; ) CJK COMPATIBILITY IDEOGRAPH-2F8D9 +2F8DA;6721;6721;6721;6721; # (朡朡; 朡; 朡; 朡; 朡; ) CJK COMPATIBILITY IDEOGRAPH-2F8DA +2F8DB;675E;675E;675E;675E; # (杞杞; 杞; 杞; 杞; 杞; ) CJK COMPATIBILITY IDEOGRAPH-2F8DB +2F8DC;6753;6753;6753;6753; # (杓杓; 杓; 杓; 杓; 杓; ) CJK COMPATIBILITY IDEOGRAPH-2F8DC +2F8DD;233C3;233C3;233C3;233C3; # (𣏃𣏃; 𣏃𣏃; 𣏃𣏃; 𣏃𣏃; 𣏃𣏃; ) CJK COMPATIBILITY IDEOGRAPH-2F8DD +2F8DE;3B49;3B49;3B49;3B49; # (㭉㭉; 㭉; 㭉; 㭉; 㭉; ) CJK COMPATIBILITY IDEOGRAPH-2F8DE +2F8DF;67FA;67FA;67FA;67FA; # (柺柺; 柺; 柺; 柺; 柺; ) CJK COMPATIBILITY IDEOGRAPH-2F8DF +2F8E0;6785;6785;6785;6785; # (枅枅; 枅; 枅; 枅; 枅; ) CJK COMPATIBILITY IDEOGRAPH-2F8E0 +2F8E1;6852;6852;6852;6852; # (桒桒; 桒; 桒; 桒; 桒; ) CJK COMPATIBILITY IDEOGRAPH-2F8E1 +2F8E2;6885;6885;6885;6885; # (梅梅; 梅; 梅; 梅; 梅; ) CJK COMPATIBILITY IDEOGRAPH-2F8E2 +2F8E3;2346D;2346D;2346D;2346D; # (𣑭𣑭; 𣑭𣑭; 𣑭𣑭; 𣑭𣑭; 𣑭𣑭; ) CJK COMPATIBILITY IDEOGRAPH-2F8E3 +2F8E4;688E;688E;688E;688E; # (梎梎; 梎; 梎; 梎; 梎; ) CJK COMPATIBILITY IDEOGRAPH-2F8E4 +2F8E5;681F;681F;681F;681F; # (栟栟; 栟; 栟; 栟; 栟; ) CJK COMPATIBILITY IDEOGRAPH-2F8E5 +2F8E6;6914;6914;6914;6914; # (椔椔; 椔; 椔; 椔; 椔; ) CJK COMPATIBILITY IDEOGRAPH-2F8E6 +2F8E7;3B9D;3B9D;3B9D;3B9D; # (㮝㮝; 㮝; 㮝; 㮝; 㮝; ) CJK COMPATIBILITY IDEOGRAPH-2F8E7 +2F8E8;6942;6942;6942;6942; # (楂楂; 楂; 楂; 楂; 楂; ) CJK COMPATIBILITY IDEOGRAPH-2F8E8 +2F8E9;69A3;69A3;69A3;69A3; # (榣榣; 榣; 榣; 榣; 榣; ) CJK COMPATIBILITY IDEOGRAPH-2F8E9 +2F8EA;69EA;69EA;69EA;69EA; # (槪槪; 槪; 槪; 槪; 槪; ) CJK COMPATIBILITY IDEOGRAPH-2F8EA +2F8EB;6AA8;6AA8;6AA8;6AA8; # (檨檨; 檨; 檨; 檨; 檨; ) CJK COMPATIBILITY IDEOGRAPH-2F8EB +2F8EC;236A3;236A3;236A3;236A3; # (𣚣𣚣; 𣚣𣚣; 𣚣𣚣; 𣚣𣚣; 𣚣𣚣; ) CJK COMPATIBILITY IDEOGRAPH-2F8EC +2F8ED;6ADB;6ADB;6ADB;6ADB; # (櫛櫛; 櫛; 櫛; 櫛; 櫛; ) CJK COMPATIBILITY IDEOGRAPH-2F8ED +2F8EE;3C18;3C18;3C18;3C18; # (㰘㰘; 㰘; 㰘; 㰘; 㰘; ) CJK COMPATIBILITY IDEOGRAPH-2F8EE +2F8EF;6B21;6B21;6B21;6B21; # (次次; 次; 次; 次; 次; ) CJK COMPATIBILITY IDEOGRAPH-2F8EF +2F8F0;238A7;238A7;238A7;238A7; # (𣢧𣢧; 𣢧𣢧; 𣢧𣢧; 𣢧𣢧; 𣢧𣢧; ) CJK COMPATIBILITY IDEOGRAPH-2F8F0 +2F8F1;6B54;6B54;6B54;6B54; # (歔歔; 歔; 歔; 歔; 歔; ) CJK COMPATIBILITY IDEOGRAPH-2F8F1 +2F8F2;3C4E;3C4E;3C4E;3C4E; # (㱎㱎; 㱎; 㱎; 㱎; 㱎; ) CJK COMPATIBILITY IDEOGRAPH-2F8F2 +2F8F3;6B72;6B72;6B72;6B72; # (歲歲; 歲; 歲; 歲; 歲; ) CJK COMPATIBILITY IDEOGRAPH-2F8F3 +2F8F4;6B9F;6B9F;6B9F;6B9F; # (殟殟; 殟; 殟; 殟; 殟; ) CJK COMPATIBILITY IDEOGRAPH-2F8F4 +2F8F5;6BBA;6BBA;6BBA;6BBA; # (殺殺; 殺; 殺; 殺; 殺; ) CJK COMPATIBILITY IDEOGRAPH-2F8F5 +2F8F6;6BBB;6BBB;6BBB;6BBB; # (殻殻; 殻; 殻; 殻; 殻; ) CJK COMPATIBILITY IDEOGRAPH-2F8F6 +2F8F7;23A8D;23A8D;23A8D;23A8D; # (𣪍𣪍; 𣪍𣪍; 𣪍𣪍; 𣪍𣪍; 𣪍𣪍; ) CJK COMPATIBILITY IDEOGRAPH-2F8F7 +2F8F8;21D0B;21D0B;21D0B;21D0B; # (𡴋𡴋; 𡴋𡴋; 𡴋𡴋; 𡴋𡴋; 𡴋𡴋; ) CJK COMPATIBILITY IDEOGRAPH-2F8F8 +2F8F9;23AFA;23AFA;23AFA;23AFA; # (𣫺𣫺; 𣫺𣫺; 𣫺𣫺; 𣫺𣫺; 𣫺𣫺; ) CJK COMPATIBILITY IDEOGRAPH-2F8F9 +2F8FA;6C4E;6C4E;6C4E;6C4E; # (汎汎; 汎; 汎; 汎; 汎; ) CJK COMPATIBILITY IDEOGRAPH-2F8FA +2F8FB;23CBC;23CBC;23CBC;23CBC; # (𣲼𣲼; 𣲼𣲼; 𣲼𣲼; 𣲼𣲼; 𣲼𣲼; ) CJK COMPATIBILITY IDEOGRAPH-2F8FB +2F8FC;6CBF;6CBF;6CBF;6CBF; # (沿沿; 沿; 沿; 沿; 沿; ) CJK COMPATIBILITY IDEOGRAPH-2F8FC +2F8FD;6CCD;6CCD;6CCD;6CCD; # (泍泍; 泍; 泍; 泍; 泍; ) CJK COMPATIBILITY IDEOGRAPH-2F8FD +2F8FE;6C67;6C67;6C67;6C67; # (汧汧; 汧; 汧; 汧; 汧; ) CJK COMPATIBILITY IDEOGRAPH-2F8FE +2F8FF;6D16;6D16;6D16;6D16; # (洖洖; 洖; 洖; 洖; 洖; ) CJK COMPATIBILITY IDEOGRAPH-2F8FF +2F900;6D3E;6D3E;6D3E;6D3E; # (派派; 派; 派; 派; 派; ) CJK COMPATIBILITY IDEOGRAPH-2F900 +2F901;6D77;6D77;6D77;6D77; # (海海; 海; 海; 海; 海; ) CJK COMPATIBILITY IDEOGRAPH-2F901 +2F902;6D41;6D41;6D41;6D41; # (流流; 流; 流; 流; 流; ) CJK COMPATIBILITY IDEOGRAPH-2F902 +2F903;6D69;6D69;6D69;6D69; # (浩浩; 浩; 浩; 浩; 浩; ) CJK COMPATIBILITY IDEOGRAPH-2F903 +2F904;6D78;6D78;6D78;6D78; # (浸浸; 浸; 浸; 浸; 浸; ) CJK COMPATIBILITY IDEOGRAPH-2F904 +2F905;6D85;6D85;6D85;6D85; # (涅涅; 涅; 涅; 涅; 涅; ) CJK COMPATIBILITY IDEOGRAPH-2F905 +2F906;23D1E;23D1E;23D1E;23D1E; # (𣴞𣴞; 𣴞𣴞; 𣴞𣴞; 𣴞𣴞; 𣴞𣴞; ) CJK COMPATIBILITY IDEOGRAPH-2F906 +2F907;6D34;6D34;6D34;6D34; # (洴洴; 洴; 洴; 洴; 洴; ) CJK COMPATIBILITY IDEOGRAPH-2F907 +2F908;6E2F;6E2F;6E2F;6E2F; # (港港; 港; 港; 港; 港; ) CJK COMPATIBILITY IDEOGRAPH-2F908 +2F909;6E6E;6E6E;6E6E;6E6E; # (湮湮; 湮; 湮; 湮; 湮; ) CJK COMPATIBILITY IDEOGRAPH-2F909 +2F90A;3D33;3D33;3D33;3D33; # (㴳㴳; 㴳; 㴳; 㴳; 㴳; ) CJK COMPATIBILITY IDEOGRAPH-2F90A +2F90B;6ECB;6ECB;6ECB;6ECB; # (滋滋; 滋; 滋; 滋; 滋; ) CJK COMPATIBILITY IDEOGRAPH-2F90B +2F90C;6EC7;6EC7;6EC7;6EC7; # (滇滇; 滇; 滇; 滇; 滇; ) CJK COMPATIBILITY IDEOGRAPH-2F90C +2F90D;23ED1;23ED1;23ED1;23ED1; # (𣻑𣻑; 𣻑𣻑; 𣻑𣻑; 𣻑𣻑; 𣻑𣻑; ) CJK COMPATIBILITY IDEOGRAPH-2F90D +2F90E;6DF9;6DF9;6DF9;6DF9; # (淹淹; 淹; 淹; 淹; 淹; ) CJK COMPATIBILITY IDEOGRAPH-2F90E +2F90F;6F6E;6F6E;6F6E;6F6E; # (潮潮; 潮; 潮; 潮; 潮; ) CJK COMPATIBILITY IDEOGRAPH-2F90F +2F910;23F5E;23F5E;23F5E;23F5E; # (𣽞𣽞; 𣽞𣽞; 𣽞𣽞; 𣽞𣽞; 𣽞𣽞; ) CJK COMPATIBILITY IDEOGRAPH-2F910 +2F911;23F8E;23F8E;23F8E;23F8E; # (𣾎𣾎; 𣾎𣾎; 𣾎𣾎; 𣾎𣾎; 𣾎𣾎; ) CJK COMPATIBILITY IDEOGRAPH-2F911 +2F912;6FC6;6FC6;6FC6;6FC6; # (濆濆; 濆; 濆; 濆; 濆; ) CJK COMPATIBILITY IDEOGRAPH-2F912 +2F913;7039;7039;7039;7039; # (瀹瀹; 瀹; 瀹; 瀹; 瀹; ) CJK COMPATIBILITY IDEOGRAPH-2F913 +2F914;701E;701E;701E;701E; # (瀞瀞; 瀞; 瀞; 瀞; 瀞; ) CJK COMPATIBILITY IDEOGRAPH-2F914 +2F915;701B;701B;701B;701B; # (瀛瀛; 瀛; 瀛; 瀛; 瀛; ) CJK COMPATIBILITY IDEOGRAPH-2F915 +2F916;3D96;3D96;3D96;3D96; # (㶖㶖; 㶖; 㶖; 㶖; 㶖; ) CJK COMPATIBILITY IDEOGRAPH-2F916 +2F917;704A;704A;704A;704A; # (灊灊; 灊; 灊; 灊; 灊; ) CJK COMPATIBILITY IDEOGRAPH-2F917 +2F918;707D;707D;707D;707D; # (災災; 災; 災; 災; 災; ) CJK COMPATIBILITY IDEOGRAPH-2F918 +2F919;7077;7077;7077;7077; # (灷灷; 灷; 灷; 灷; 灷; ) CJK COMPATIBILITY IDEOGRAPH-2F919 +2F91A;70AD;70AD;70AD;70AD; # (炭炭; 炭; 炭; 炭; 炭; ) CJK COMPATIBILITY IDEOGRAPH-2F91A +2F91B;20525;20525;20525;20525; # (𠔥𠔥; 𠔥𠔥; 𠔥𠔥; 𠔥𠔥; 𠔥𠔥; ) CJK COMPATIBILITY IDEOGRAPH-2F91B +2F91C;7145;7145;7145;7145; # (煅煅; 煅; 煅; 煅; 煅; ) CJK COMPATIBILITY IDEOGRAPH-2F91C +2F91D;24263;24263;24263;24263; # (𤉣𤉣; 𤉣𤉣; 𤉣𤉣; 𤉣𤉣; 𤉣𤉣; ) CJK COMPATIBILITY IDEOGRAPH-2F91D +2F91E;719C;719C;719C;719C; # (熜熜; 熜; 熜; 熜; 熜; ) CJK COMPATIBILITY IDEOGRAPH-2F91E +2F91F;43AB;43AB;43AB;43AB; # (𤎫𤎫; 䎫; 䎫; 䎫; 䎫; ) CJK COMPATIBILITY IDEOGRAPH-2F91F +2F920;7228;7228;7228;7228; # (爨爨; 爨; 爨; 爨; 爨; ) CJK COMPATIBILITY IDEOGRAPH-2F920 +2F921;7235;7235;7235;7235; # (爵爵; 爵; 爵; 爵; 爵; ) CJK COMPATIBILITY IDEOGRAPH-2F921 +2F922;7250;7250;7250;7250; # (牐牐; 牐; 牐; 牐; 牐; ) CJK COMPATIBILITY IDEOGRAPH-2F922 +2F923;24608;24608;24608;24608; # (𤘈𤘈; 𤘈𤘈; 𤘈𤘈; 𤘈𤘈; 𤘈𤘈; ) CJK COMPATIBILITY IDEOGRAPH-2F923 +2F924;7280;7280;7280;7280; # (犀犀; 犀; 犀; 犀; 犀; ) CJK COMPATIBILITY IDEOGRAPH-2F924 +2F925;7295;7295;7295;7295; # (犕犕; 犕; 犕; 犕; 犕; ) CJK COMPATIBILITY IDEOGRAPH-2F925 +2F926;24735;24735;24735;24735; # (𤜵𤜵; 𤜵𤜵; 𤜵𤜵; 𤜵𤜵; 𤜵𤜵; ) CJK COMPATIBILITY IDEOGRAPH-2F926 +2F927;24814;24814;24814;24814; # (𤠔𤠔; 𤠔𤠔; 𤠔𤠔; 𤠔𤠔; 𤠔𤠔; ) CJK COMPATIBILITY IDEOGRAPH-2F927 +2F928;737A;737A;737A;737A; # (獺獺; 獺; 獺; 獺; 獺; ) CJK COMPATIBILITY IDEOGRAPH-2F928 +2F929;738B;738B;738B;738B; # (王王; 王; 王; 王; 王; ) CJK COMPATIBILITY IDEOGRAPH-2F929 +2F92A;3EAC;3EAC;3EAC;3EAC; # (㺬㺬; 㺬; 㺬; 㺬; 㺬; ) CJK COMPATIBILITY IDEOGRAPH-2F92A +2F92B;73A5;73A5;73A5;73A5; # (玥玥; 玥; 玥; 玥; 玥; ) CJK COMPATIBILITY IDEOGRAPH-2F92B +2F92C;3EB8;3EB8;3EB8;3EB8; # (㺸㺸; 㺸; 㺸; 㺸; 㺸; ) CJK COMPATIBILITY IDEOGRAPH-2F92C +2F92D;3EB8;3EB8;3EB8;3EB8; # (㺸㺸; 㺸; 㺸; 㺸; 㺸; ) CJK COMPATIBILITY IDEOGRAPH-2F92D +2F92E;7447;7447;7447;7447; # (瑇瑇; 瑇; 瑇; 瑇; 瑇; ) CJK COMPATIBILITY IDEOGRAPH-2F92E +2F92F;745C;745C;745C;745C; # (瑜瑜; 瑜; 瑜; 瑜; 瑜; ) CJK COMPATIBILITY IDEOGRAPH-2F92F +2F930;7471;7471;7471;7471; # (瑱瑱; 瑱; 瑱; 瑱; 瑱; ) CJK COMPATIBILITY IDEOGRAPH-2F930 +2F931;7485;7485;7485;7485; # (璅璅; 璅; 璅; 璅; 璅; ) CJK COMPATIBILITY IDEOGRAPH-2F931 +2F932;74CA;74CA;74CA;74CA; # (瓊瓊; 瓊; 瓊; 瓊; 瓊; ) CJK COMPATIBILITY IDEOGRAPH-2F932 +2F933;3F1B;3F1B;3F1B;3F1B; # (㼛㼛; 㼛; 㼛; 㼛; 㼛; ) CJK COMPATIBILITY IDEOGRAPH-2F933 +2F934;7524;7524;7524;7524; # (甤甤; 甤; 甤; 甤; 甤; ) CJK COMPATIBILITY IDEOGRAPH-2F934 +2F935;24C36;24C36;24C36;24C36; # (𤰶𤰶; 𤰶𤰶; 𤰶𤰶; 𤰶𤰶; 𤰶𤰶; ) CJK COMPATIBILITY IDEOGRAPH-2F935 +2F936;753E;753E;753E;753E; # (甾甾; 甾; 甾; 甾; 甾; ) CJK COMPATIBILITY IDEOGRAPH-2F936 +2F937;24C92;24C92;24C92;24C92; # (𤲒𤲒; 𤲒𤲒; 𤲒𤲒; 𤲒𤲒; 𤲒𤲒; ) CJK COMPATIBILITY IDEOGRAPH-2F937 +2F938;7570;7570;7570;7570; # (異異; 異; 異; 異; 異; ) CJK COMPATIBILITY IDEOGRAPH-2F938 +2F939;2219F;2219F;2219F;2219F; # (𢆟𢆟; 𢆟𢆟; 𢆟𢆟; 𢆟𢆟; 𢆟𢆟; ) CJK COMPATIBILITY IDEOGRAPH-2F939 +2F93A;7610;7610;7610;7610; # (瘐瘐; 瘐; 瘐; 瘐; 瘐; ) CJK COMPATIBILITY IDEOGRAPH-2F93A +2F93B;24FA1;24FA1;24FA1;24FA1; # (𤾡𤾡; 𤾡𤾡; 𤾡𤾡; 𤾡𤾡; 𤾡𤾡; ) CJK COMPATIBILITY IDEOGRAPH-2F93B +2F93C;24FB8;24FB8;24FB8;24FB8; # (𤾸𤾸; 𤾸𤾸; 𤾸𤾸; 𤾸𤾸; 𤾸𤾸; ) CJK COMPATIBILITY IDEOGRAPH-2F93C +2F93D;25044;25044;25044;25044; # (𥁄𥁄; 𥁄𥁄; 𥁄𥁄; 𥁄𥁄; 𥁄𥁄; ) CJK COMPATIBILITY IDEOGRAPH-2F93D +2F93E;3FFC;3FFC;3FFC;3FFC; # (㿼㿼; 㿼; 㿼; 㿼; 㿼; ) CJK COMPATIBILITY IDEOGRAPH-2F93E +2F93F;4008;4008;4008;4008; # (䀈䀈; 䀈; 䀈; 䀈; 䀈; ) CJK COMPATIBILITY IDEOGRAPH-2F93F +2F940;76F4;76F4;76F4;76F4; # (直直; 直; 直; 直; 直; ) CJK COMPATIBILITY IDEOGRAPH-2F940 +2F941;250F3;250F3;250F3;250F3; # (𥃳𥃳; 𥃳𥃳; 𥃳𥃳; 𥃳𥃳; 𥃳𥃳; ) CJK COMPATIBILITY IDEOGRAPH-2F941 +2F942;250F2;250F2;250F2;250F2; # (𥃲𥃲; 𥃲𥃲; 𥃲𥃲; 𥃲𥃲; 𥃲𥃲; ) CJK COMPATIBILITY IDEOGRAPH-2F942 +2F943;25119;25119;25119;25119; # (𥄙𥄙; 𥄙𥄙; 𥄙𥄙; 𥄙𥄙; 𥄙𥄙; ) CJK COMPATIBILITY IDEOGRAPH-2F943 +2F944;25133;25133;25133;25133; # (𥄳𥄳; 𥄳𥄳; 𥄳𥄳; 𥄳𥄳; 𥄳𥄳; ) CJK COMPATIBILITY IDEOGRAPH-2F944 +2F945;771E;771E;771E;771E; # (眞眞; 眞; 眞; 眞; 眞; ) CJK COMPATIBILITY IDEOGRAPH-2F945 +2F946;771F;771F;771F;771F; # (真真; 真; 真; 真; 真; ) CJK COMPATIBILITY IDEOGRAPH-2F946 +2F947;771F;771F;771F;771F; # (真真; 真; 真; 真; 真; ) CJK COMPATIBILITY IDEOGRAPH-2F947 +2F948;774A;774A;774A;774A; # (睊睊; 睊; 睊; 睊; 睊; ) CJK COMPATIBILITY IDEOGRAPH-2F948 +2F949;4039;4039;4039;4039; # (䀹䀹; 䀹; 䀹; 䀹; 䀹; ) CJK COMPATIBILITY IDEOGRAPH-2F949 +2F94A;778B;778B;778B;778B; # (瞋瞋; 瞋; 瞋; 瞋; 瞋; ) CJK COMPATIBILITY IDEOGRAPH-2F94A +2F94B;4046;4046;4046;4046; # (䁆䁆; 䁆; 䁆; 䁆; 䁆; ) CJK COMPATIBILITY IDEOGRAPH-2F94B +2F94C;4096;4096;4096;4096; # (䂖䂖; 䂖; 䂖; 䂖; 䂖; ) CJK COMPATIBILITY IDEOGRAPH-2F94C +2F94D;2541D;2541D;2541D;2541D; # (𥐝𥐝; 𥐝𥐝; 𥐝𥐝; 𥐝𥐝; 𥐝𥐝; ) CJK COMPATIBILITY IDEOGRAPH-2F94D +2F94E;784E;784E;784E;784E; # (硎硎; 硎; 硎; 硎; 硎; ) CJK COMPATIBILITY IDEOGRAPH-2F94E +2F94F;788C;788C;788C;788C; # (碌碌; 碌; 碌; 碌; 碌; ) CJK COMPATIBILITY IDEOGRAPH-2F94F +2F950;78CC;78CC;78CC;78CC; # (磌磌; 磌; 磌; 磌; 磌; ) CJK COMPATIBILITY IDEOGRAPH-2F950 +2F951;40E3;40E3;40E3;40E3; # (䃣䃣; 䃣; 䃣; 䃣; 䃣; ) CJK COMPATIBILITY IDEOGRAPH-2F951 +2F952;25626;25626;25626;25626; # (𥘦𥘦; 𥘦𥘦; 𥘦𥘦; 𥘦𥘦; 𥘦𥘦; ) CJK COMPATIBILITY IDEOGRAPH-2F952 +2F953;7956;7956;7956;7956; # (祖祖; 祖; 祖; 祖; 祖; ) CJK COMPATIBILITY IDEOGRAPH-2F953 +2F954;2569A;2569A;2569A;2569A; # (𥚚𥚚; 𥚚𥚚; 𥚚𥚚; 𥚚𥚚; 𥚚𥚚; ) CJK COMPATIBILITY IDEOGRAPH-2F954 +2F955;256C5;256C5;256C5;256C5; # (𥛅𥛅; 𥛅𥛅; 𥛅𥛅; 𥛅𥛅; 𥛅𥛅; ) CJK COMPATIBILITY IDEOGRAPH-2F955 +2F956;798F;798F;798F;798F; # (福福; 福; 福; 福; 福; ) CJK COMPATIBILITY IDEOGRAPH-2F956 +2F957;79EB;79EB;79EB;79EB; # (秫秫; 秫; 秫; 秫; 秫; ) CJK COMPATIBILITY IDEOGRAPH-2F957 +2F958;412F;412F;412F;412F; # (䄯䄯; 䄯; 䄯; 䄯; 䄯; ) CJK COMPATIBILITY IDEOGRAPH-2F958 +2F959;7A40;7A40;7A40;7A40; # (穀穀; 穀; 穀; 穀; 穀; ) CJK COMPATIBILITY IDEOGRAPH-2F959 +2F95A;7A4A;7A4A;7A4A;7A4A; # (穊穊; 穊; 穊; 穊; 穊; ) CJK COMPATIBILITY IDEOGRAPH-2F95A +2F95B;7A4F;7A4F;7A4F;7A4F; # (穏穏; 穏; 穏; 穏; 穏; ) CJK COMPATIBILITY IDEOGRAPH-2F95B +2F95C;2597C;2597C;2597C;2597C; # (𥥼𥥼; 𥥼𥥼; 𥥼𥥼; 𥥼𥥼; 𥥼𥥼; ) CJK COMPATIBILITY IDEOGRAPH-2F95C +2F95D;25AA7;25AA7;25AA7;25AA7; # (𥪧𥪧; 𥪧𥪧; 𥪧𥪧; 𥪧𥪧; 𥪧𥪧; ) CJK COMPATIBILITY IDEOGRAPH-2F95D +2F95E;25AA7;25AA7;25AA7;25AA7; # (𥪧𥪧; 𥪧𥪧; 𥪧𥪧; 𥪧𥪧; 𥪧𥪧; ) CJK COMPATIBILITY IDEOGRAPH-2F95E +2F95F;7AAE;7AAE;7AAE;7AAE; # (竮竮; 窮; 窮; 窮; 窮; ) CJK COMPATIBILITY IDEOGRAPH-2F95F +2F960;4202;4202;4202;4202; # (䈂䈂; 䈂; 䈂; 䈂; 䈂; ) CJK COMPATIBILITY IDEOGRAPH-2F960 +2F961;25BAB;25BAB;25BAB;25BAB; # (𥮫𥮫; 𥮫𥮫; 𥮫𥮫; 𥮫𥮫; 𥮫𥮫; ) CJK COMPATIBILITY IDEOGRAPH-2F961 +2F962;7BC6;7BC6;7BC6;7BC6; # (篆篆; 篆; 篆; 篆; 篆; ) CJK COMPATIBILITY IDEOGRAPH-2F962 +2F963;7BC9;7BC9;7BC9;7BC9; # (築築; 築; 築; 築; 築; ) CJK COMPATIBILITY IDEOGRAPH-2F963 +2F964;4227;4227;4227;4227; # (䈧䈧; 䈧; 䈧; 䈧; 䈧; ) CJK COMPATIBILITY IDEOGRAPH-2F964 +2F965;25C80;25C80;25C80;25C80; # (𥲀𥲀; 𥲀𥲀; 𥲀𥲀; 𥲀𥲀; 𥲀𥲀; ) CJK COMPATIBILITY IDEOGRAPH-2F965 +2F966;7CD2;7CD2;7CD2;7CD2; # (糒糒; 糒; 糒; 糒; 糒; ) CJK COMPATIBILITY IDEOGRAPH-2F966 +2F967;42A0;42A0;42A0;42A0; # (䊠䊠; 䊠; 䊠; 䊠; 䊠; ) CJK COMPATIBILITY IDEOGRAPH-2F967 +2F968;7CE8;7CE8;7CE8;7CE8; # (糨糨; 糨; 糨; 糨; 糨; ) CJK COMPATIBILITY IDEOGRAPH-2F968 +2F969;7CE3;7CE3;7CE3;7CE3; # (糣糣; 糣; 糣; 糣; 糣; ) CJK COMPATIBILITY IDEOGRAPH-2F969 +2F96A;7D00;7D00;7D00;7D00; # (紀紀; 紀; 紀; 紀; 紀; ) CJK COMPATIBILITY IDEOGRAPH-2F96A +2F96B;25F86;25F86;25F86;25F86; # (𥾆𥾆; 𥾆𥾆; 𥾆𥾆; 𥾆𥾆; 𥾆𥾆; ) CJK COMPATIBILITY IDEOGRAPH-2F96B +2F96C;7D63;7D63;7D63;7D63; # (絣絣; 絣; 絣; 絣; 絣; ) CJK COMPATIBILITY IDEOGRAPH-2F96C +2F96D;4301;4301;4301;4301; # (䌁䌁; 䌁; 䌁; 䌁; 䌁; ) CJK COMPATIBILITY IDEOGRAPH-2F96D +2F96E;7DC7;7DC7;7DC7;7DC7; # (緇緇; 緇; 緇; 緇; 緇; ) CJK COMPATIBILITY IDEOGRAPH-2F96E +2F96F;7E02;7E02;7E02;7E02; # (縂縂; 縂; 縂; 縂; 縂; ) CJK COMPATIBILITY IDEOGRAPH-2F96F +2F970;7E45;7E45;7E45;7E45; # (繅繅; 繅; 繅; 繅; 繅; ) CJK COMPATIBILITY IDEOGRAPH-2F970 +2F971;4334;4334;4334;4334; # (䌴䌴; 䌴; 䌴; 䌴; 䌴; ) CJK COMPATIBILITY IDEOGRAPH-2F971 +2F972;26228;26228;26228;26228; # (𦈨𦈨; 𦈨𦈨; 𦈨𦈨; 𦈨𦈨; 𦈨𦈨; ) CJK COMPATIBILITY IDEOGRAPH-2F972 +2F973;26247;26247;26247;26247; # (𦉇𦉇; 𦉇𦉇; 𦉇𦉇; 𦉇𦉇; 𦉇𦉇; ) CJK COMPATIBILITY IDEOGRAPH-2F973 +2F974;4359;4359;4359;4359; # (䍙䍙; 䍙; 䍙; 䍙; 䍙; ) CJK COMPATIBILITY IDEOGRAPH-2F974 +2F975;262D9;262D9;262D9;262D9; # (𦋙𦋙; 𦋙𦋙; 𦋙𦋙; 𦋙𦋙; 𦋙𦋙; ) CJK COMPATIBILITY IDEOGRAPH-2F975 +2F976;7F7A;7F7A;7F7A;7F7A; # (罺罺; 罺; 罺; 罺; 罺; ) CJK COMPATIBILITY IDEOGRAPH-2F976 +2F977;2633E;2633E;2633E;2633E; # (𦌾𦌾; 𦌾𦌾; 𦌾𦌾; 𦌾𦌾; 𦌾𦌾; ) CJK COMPATIBILITY IDEOGRAPH-2F977 +2F978;7F95;7F95;7F95;7F95; # (羕羕; 羕; 羕; 羕; 羕; ) CJK COMPATIBILITY IDEOGRAPH-2F978 +2F979;7FFA;7FFA;7FFA;7FFA; # (翺翺; 翺; 翺; 翺; 翺; ) CJK COMPATIBILITY IDEOGRAPH-2F979 +2F97A;8005;8005;8005;8005; # (者者; 者; 者; 者; 者; ) CJK COMPATIBILITY IDEOGRAPH-2F97A +2F97B;264DA;264DA;264DA;264DA; # (𦓚𦓚; 𦓚𦓚; 𦓚𦓚; 𦓚𦓚; 𦓚𦓚; ) CJK COMPATIBILITY IDEOGRAPH-2F97B +2F97C;26523;26523;26523;26523; # (𦔣𦔣; 𦔣𦔣; 𦔣𦔣; 𦔣𦔣; 𦔣𦔣; ) CJK COMPATIBILITY IDEOGRAPH-2F97C +2F97D;8060;8060;8060;8060; # (聠聠; 聠; 聠; 聠; 聠; ) CJK COMPATIBILITY IDEOGRAPH-2F97D +2F97E;265A8;265A8;265A8;265A8; # (𦖨𦖨; 𦖨𦖨; 𦖨𦖨; 𦖨𦖨; 𦖨𦖨; ) CJK COMPATIBILITY IDEOGRAPH-2F97E +2F97F;8070;8070;8070;8070; # (聰聰; 聰; 聰; 聰; 聰; ) CJK COMPATIBILITY IDEOGRAPH-2F97F +2F980;2335F;2335F;2335F;2335F; # (𣍟𣍟; 𣍟𣍟; 𣍟𣍟; 𣍟𣍟; 𣍟𣍟; ) CJK COMPATIBILITY IDEOGRAPH-2F980 +2F981;43D5;43D5;43D5;43D5; # (䏕䏕; 䏕; 䏕; 䏕; 䏕; ) CJK COMPATIBILITY IDEOGRAPH-2F981 +2F982;80B2;80B2;80B2;80B2; # (育育; 育; 育; 育; 育; ) CJK COMPATIBILITY IDEOGRAPH-2F982 +2F983;8103;8103;8103;8103; # (脃脃; 脃; 脃; 脃; 脃; ) CJK COMPATIBILITY IDEOGRAPH-2F983 +2F984;440B;440B;440B;440B; # (䐋䐋; 䐋; 䐋; 䐋; 䐋; ) CJK COMPATIBILITY IDEOGRAPH-2F984 +2F985;813E;813E;813E;813E; # (脾脾; 脾; 脾; 脾; 脾; ) CJK COMPATIBILITY IDEOGRAPH-2F985 +2F986;5AB5;5AB5;5AB5;5AB5; # (媵媵; 媵; 媵; 媵; 媵; ) CJK COMPATIBILITY IDEOGRAPH-2F986 +2F987;267A7;267A7;267A7;267A7; # (𦞧𦞧; 𦞧𦞧; 𦞧𦞧; 𦞧𦞧; 𦞧𦞧; ) CJK COMPATIBILITY IDEOGRAPH-2F987 +2F988;267B5;267B5;267B5;267B5; # (𦞵𦞵; 𦞵𦞵; 𦞵𦞵; 𦞵𦞵; 𦞵𦞵; ) CJK COMPATIBILITY IDEOGRAPH-2F988 +2F989;23393;23393;23393;23393; # (𣎓𣎓; 𣎓𣎓; 𣎓𣎓; 𣎓𣎓; 𣎓𣎓; ) CJK COMPATIBILITY IDEOGRAPH-2F989 +2F98A;2339C;2339C;2339C;2339C; # (𣎜𣎜; 𣎜𣎜; 𣎜𣎜; 𣎜𣎜; 𣎜𣎜; ) CJK COMPATIBILITY IDEOGRAPH-2F98A +2F98B;8201;8201;8201;8201; # (舁舁; 舁; 舁; 舁; 舁; ) CJK COMPATIBILITY IDEOGRAPH-2F98B +2F98C;8204;8204;8204;8204; # (舄舄; 舄; 舄; 舄; 舄; ) CJK COMPATIBILITY IDEOGRAPH-2F98C +2F98D;8F9E;8F9E;8F9E;8F9E; # (辞辞; 辞; 辞; 辞; 辞; ) CJK COMPATIBILITY IDEOGRAPH-2F98D +2F98E;446B;446B;446B;446B; # (䑫䑫; 䑫; 䑫; 䑫; 䑫; ) CJK COMPATIBILITY IDEOGRAPH-2F98E +2F98F;8291;8291;8291;8291; # (芑芑; 芑; 芑; 芑; 芑; ) CJK COMPATIBILITY IDEOGRAPH-2F98F +2F990;828B;828B;828B;828B; # (芋芋; 芋; 芋; 芋; 芋; ) CJK COMPATIBILITY IDEOGRAPH-2F990 +2F991;829D;829D;829D;829D; # (芝芝; 芝; 芝; 芝; 芝; ) CJK COMPATIBILITY IDEOGRAPH-2F991 +2F992;52B3;52B3;52B3;52B3; # (劳劳; 劳; 劳; 劳; 劳; ) CJK COMPATIBILITY IDEOGRAPH-2F992 +2F993;82B1;82B1;82B1;82B1; # (花花; 花; 花; 花; 花; ) CJK COMPATIBILITY IDEOGRAPH-2F993 +2F994;82B3;82B3;82B3;82B3; # (芳芳; 芳; 芳; 芳; 芳; ) CJK COMPATIBILITY IDEOGRAPH-2F994 +2F995;82BD;82BD;82BD;82BD; # (芽芽; 芽; 芽; 芽; 芽; ) CJK COMPATIBILITY IDEOGRAPH-2F995 +2F996;82E6;82E6;82E6;82E6; # (苦苦; 苦; 苦; 苦; 苦; ) CJK COMPATIBILITY IDEOGRAPH-2F996 +2F997;26B3C;26B3C;26B3C;26B3C; # (𦬼𦬼; 𦬼𦬼; 𦬼𦬼; 𦬼𦬼; 𦬼𦬼; ) CJK COMPATIBILITY IDEOGRAPH-2F997 +2F998;82E5;82E5;82E5;82E5; # (若若; 若; 若; 若; 若; ) CJK COMPATIBILITY IDEOGRAPH-2F998 +2F999;831D;831D;831D;831D; # (茝茝; 茝; 茝; 茝; 茝; ) CJK COMPATIBILITY IDEOGRAPH-2F999 +2F99A;8363;8363;8363;8363; # (荣荣; 荣; 荣; 荣; 荣; ) CJK COMPATIBILITY IDEOGRAPH-2F99A +2F99B;83AD;83AD;83AD;83AD; # (莭莭; 莭; 莭; 莭; 莭; ) CJK COMPATIBILITY IDEOGRAPH-2F99B +2F99C;8323;8323;8323;8323; # (茣茣; 茣; 茣; 茣; 茣; ) CJK COMPATIBILITY IDEOGRAPH-2F99C +2F99D;83BD;83BD;83BD;83BD; # (莽莽; 莽; 莽; 莽; 莽; ) CJK COMPATIBILITY IDEOGRAPH-2F99D +2F99E;83E7;83E7;83E7;83E7; # (菧菧; 菧; 菧; 菧; 菧; ) CJK COMPATIBILITY IDEOGRAPH-2F99E +2F99F;8457;8457;8457;8457; # (著著; 著; 著; 著; 著; ) CJK COMPATIBILITY IDEOGRAPH-2F99F +2F9A0;8353;8353;8353;8353; # (荓荓; 荓; 荓; 荓; 荓; ) CJK COMPATIBILITY IDEOGRAPH-2F9A0 +2F9A1;83CA;83CA;83CA;83CA; # (菊菊; 菊; 菊; 菊; 菊; ) CJK COMPATIBILITY IDEOGRAPH-2F9A1 +2F9A2;83CC;83CC;83CC;83CC; # (菌菌; 菌; 菌; 菌; 菌; ) CJK COMPATIBILITY IDEOGRAPH-2F9A2 +2F9A3;83DC;83DC;83DC;83DC; # (菜菜; 菜; 菜; 菜; 菜; ) CJK COMPATIBILITY IDEOGRAPH-2F9A3 +2F9A4;26C36;26C36;26C36;26C36; # (𦰶𦰶; 𦰶𦰶; 𦰶𦰶; 𦰶𦰶; 𦰶𦰶; ) CJK COMPATIBILITY IDEOGRAPH-2F9A4 +2F9A5;26D6B;26D6B;26D6B;26D6B; # (𦵫𦵫; 𦵫𦵫; 𦵫𦵫; 𦵫𦵫; 𦵫𦵫; ) CJK COMPATIBILITY IDEOGRAPH-2F9A5 +2F9A6;26CD5;26CD5;26CD5;26CD5; # (𦳕𦳕; 𦳕𦳕; 𦳕𦳕; 𦳕𦳕; 𦳕𦳕; ) CJK COMPATIBILITY IDEOGRAPH-2F9A6 +2F9A7;452B;452B;452B;452B; # (䔫䔫; 䔫; 䔫; 䔫; 䔫; ) CJK COMPATIBILITY IDEOGRAPH-2F9A7 +2F9A8;84F1;84F1;84F1;84F1; # (蓱蓱; 蓱; 蓱; 蓱; 蓱; ) CJK COMPATIBILITY IDEOGRAPH-2F9A8 +2F9A9;84F3;84F3;84F3;84F3; # (蓳蓳; 蓳; 蓳; 蓳; 蓳; ) CJK COMPATIBILITY IDEOGRAPH-2F9A9 +2F9AA;8516;8516;8516;8516; # (蔖蔖; 蔖; 蔖; 蔖; 蔖; ) CJK COMPATIBILITY IDEOGRAPH-2F9AA +2F9AB;273CA;273CA;273CA;273CA; # (𧏊𧏊; 𧏊𧏊; 𧏊𧏊; 𧏊𧏊; 𧏊𧏊; ) CJK COMPATIBILITY IDEOGRAPH-2F9AB +2F9AC;8564;8564;8564;8564; # (蕤蕤; 蕤; 蕤; 蕤; 蕤; ) CJK COMPATIBILITY IDEOGRAPH-2F9AC +2F9AD;26F2C;26F2C;26F2C;26F2C; # (𦼬𦼬; 𦼬𦼬; 𦼬𦼬; 𦼬𦼬; 𦼬𦼬; ) CJK COMPATIBILITY IDEOGRAPH-2F9AD +2F9AE;455D;455D;455D;455D; # (䕝䕝; 䕝; 䕝; 䕝; 䕝; ) CJK COMPATIBILITY IDEOGRAPH-2F9AE +2F9AF;4561;4561;4561;4561; # (䕡䕡; 䕡; 䕡; 䕡; 䕡; ) CJK COMPATIBILITY IDEOGRAPH-2F9AF +2F9B0;26FB1;26FB1;26FB1;26FB1; # (𦾱𦾱; 𦾱𦾱; 𦾱𦾱; 𦾱𦾱; 𦾱𦾱; ) CJK COMPATIBILITY IDEOGRAPH-2F9B0 +2F9B1;270D2;270D2;270D2;270D2; # (𧃒𧃒; 𧃒𧃒; 𧃒𧃒; 𧃒𧃒; 𧃒𧃒; ) CJK COMPATIBILITY IDEOGRAPH-2F9B1 +2F9B2;456B;456B;456B;456B; # (䕫䕫; 䕫; 䕫; 䕫; 䕫; ) CJK COMPATIBILITY IDEOGRAPH-2F9B2 +2F9B3;8650;8650;8650;8650; # (虐虐; 虐; 虐; 虐; 虐; ) CJK COMPATIBILITY IDEOGRAPH-2F9B3 +2F9B4;865C;865C;865C;865C; # (虜虜; 虜; 虜; 虜; 虜; ) CJK COMPATIBILITY IDEOGRAPH-2F9B4 +2F9B5;8667;8667;8667;8667; # (虧虧; 虧; 虧; 虧; 虧; ) CJK COMPATIBILITY IDEOGRAPH-2F9B5 +2F9B6;8669;8669;8669;8669; # (虩虩; 虩; 虩; 虩; 虩; ) CJK COMPATIBILITY IDEOGRAPH-2F9B6 +2F9B7;86A9;86A9;86A9;86A9; # (蚩蚩; 蚩; 蚩; 蚩; 蚩; ) CJK COMPATIBILITY IDEOGRAPH-2F9B7 +2F9B8;8688;8688;8688;8688; # (蚈蚈; 蚈; 蚈; 蚈; 蚈; ) CJK COMPATIBILITY IDEOGRAPH-2F9B8 +2F9B9;870E;870E;870E;870E; # (蜎蜎; 蜎; 蜎; 蜎; 蜎; ) CJK COMPATIBILITY IDEOGRAPH-2F9B9 +2F9BA;86E2;86E2;86E2;86E2; # (蛢蛢; 蛢; 蛢; 蛢; 蛢; ) CJK COMPATIBILITY IDEOGRAPH-2F9BA +2F9BB;8779;8779;8779;8779; # (蝹蝹; 蝹; 蝹; 蝹; 蝹; ) CJK COMPATIBILITY IDEOGRAPH-2F9BB +2F9BC;8728;8728;8728;8728; # (蜨蜨; 蜨; 蜨; 蜨; 蜨; ) CJK COMPATIBILITY IDEOGRAPH-2F9BC +2F9BD;876B;876B;876B;876B; # (蝫蝫; 蝫; 蝫; 蝫; 蝫; ) CJK COMPATIBILITY IDEOGRAPH-2F9BD +2F9BE;8786;8786;8786;8786; # (螆螆; 螆; 螆; 螆; 螆; ) CJK COMPATIBILITY IDEOGRAPH-2F9BE +2F9BF;4D57;4D57;4D57;4D57; # (䗗䗗; 䵗; 䵗; 䵗; 䵗; ) CJK COMPATIBILITY IDEOGRAPH-2F9BF +2F9C0;87E1;87E1;87E1;87E1; # (蟡蟡; 蟡; 蟡; 蟡; 蟡; ) CJK COMPATIBILITY IDEOGRAPH-2F9C0 +2F9C1;8801;8801;8801;8801; # (蠁蠁; 蠁; 蠁; 蠁; 蠁; ) CJK COMPATIBILITY IDEOGRAPH-2F9C1 +2F9C2;45F9;45F9;45F9;45F9; # (䗹䗹; 䗹; 䗹; 䗹; 䗹; ) CJK COMPATIBILITY IDEOGRAPH-2F9C2 +2F9C3;8860;8860;8860;8860; # (衠衠; 衠; 衠; 衠; 衠; ) CJK COMPATIBILITY IDEOGRAPH-2F9C3 +2F9C4;8863;8863;8863;8863; # (衣衣; 衣; 衣; 衣; 衣; ) CJK COMPATIBILITY IDEOGRAPH-2F9C4 +2F9C5;27667;27667;27667;27667; # (𧙧𧙧; 𧙧𧙧; 𧙧𧙧; 𧙧𧙧; 𧙧𧙧; ) CJK COMPATIBILITY IDEOGRAPH-2F9C5 +2F9C6;88D7;88D7;88D7;88D7; # (裗裗; 裗; 裗; 裗; 裗; ) CJK COMPATIBILITY IDEOGRAPH-2F9C6 +2F9C7;88DE;88DE;88DE;88DE; # (裞裞; 裞; 裞; 裞; 裞; ) CJK COMPATIBILITY IDEOGRAPH-2F9C7 +2F9C8;4635;4635;4635;4635; # (䘵䘵; 䘵; 䘵; 䘵; 䘵; ) CJK COMPATIBILITY IDEOGRAPH-2F9C8 +2F9C9;88FA;88FA;88FA;88FA; # (裺裺; 裺; 裺; 裺; 裺; ) CJK COMPATIBILITY IDEOGRAPH-2F9C9 +2F9CA;34BB;34BB;34BB;34BB; # (㒻㒻; 㒻; 㒻; 㒻; 㒻; ) CJK COMPATIBILITY IDEOGRAPH-2F9CA +2F9CB;278AE;278AE;278AE;278AE; # (𧢮𧢮; 𧢮𧢮; 𧢮𧢮; 𧢮𧢮; 𧢮𧢮; ) CJK COMPATIBILITY IDEOGRAPH-2F9CB +2F9CC;27966;27966;27966;27966; # (𧥦𧥦; 𧥦𧥦; 𧥦𧥦; 𧥦𧥦; 𧥦𧥦; ) CJK COMPATIBILITY IDEOGRAPH-2F9CC +2F9CD;46BE;46BE;46BE;46BE; # (䚾䚾; 䚾; 䚾; 䚾; 䚾; ) CJK COMPATIBILITY IDEOGRAPH-2F9CD +2F9CE;46C7;46C7;46C7;46C7; # (䛇䛇; 䛇; 䛇; 䛇; 䛇; ) CJK COMPATIBILITY IDEOGRAPH-2F9CE +2F9CF;8AA0;8AA0;8AA0;8AA0; # (誠誠; 誠; 誠; 誠; 誠; ) CJK COMPATIBILITY IDEOGRAPH-2F9CF +2F9D0;8AED;8AED;8AED;8AED; # (諭諭; 諭; 諭; 諭; 諭; ) CJK COMPATIBILITY IDEOGRAPH-2F9D0 +2F9D1;8B8A;8B8A;8B8A;8B8A; # (變變; 變; 變; 變; 變; ) CJK COMPATIBILITY IDEOGRAPH-2F9D1 +2F9D2;8C55;8C55;8C55;8C55; # (豕豕; 豕; 豕; 豕; 豕; ) CJK COMPATIBILITY IDEOGRAPH-2F9D2 +2F9D3;27CA8;27CA8;27CA8;27CA8; # (𧲨𧲨; 𧲨𧲨; 𧲨𧲨; 𧲨𧲨; 𧲨𧲨; ) CJK COMPATIBILITY IDEOGRAPH-2F9D3 +2F9D4;8CAB;8CAB;8CAB;8CAB; # (貫貫; 貫; 貫; 貫; 貫; ) CJK COMPATIBILITY IDEOGRAPH-2F9D4 +2F9D5;8CC1;8CC1;8CC1;8CC1; # (賁賁; 賁; 賁; 賁; 賁; ) CJK COMPATIBILITY IDEOGRAPH-2F9D5 +2F9D6;8D1B;8D1B;8D1B;8D1B; # (贛贛; 贛; 贛; 贛; 贛; ) CJK COMPATIBILITY IDEOGRAPH-2F9D6 +2F9D7;8D77;8D77;8D77;8D77; # (起起; 起; 起; 起; 起; ) CJK COMPATIBILITY IDEOGRAPH-2F9D7 +2F9D8;27F2F;27F2F;27F2F;27F2F; # (𧼯𧼯; 𧼯𧼯; 𧼯𧼯; 𧼯𧼯; 𧼯𧼯; ) CJK COMPATIBILITY IDEOGRAPH-2F9D8 +2F9D9;20804;20804;20804;20804; # (𠠄𠠄; 𠠄𠠄; 𠠄𠠄; 𠠄𠠄; 𠠄𠠄; ) CJK COMPATIBILITY IDEOGRAPH-2F9D9 +2F9DA;8DCB;8DCB;8DCB;8DCB; # (跋跋; 跋; 跋; 跋; 跋; ) CJK COMPATIBILITY IDEOGRAPH-2F9DA +2F9DB;8DBC;8DBC;8DBC;8DBC; # (趼趼; 趼; 趼; 趼; 趼; ) CJK COMPATIBILITY IDEOGRAPH-2F9DB +2F9DC;8DF0;8DF0;8DF0;8DF0; # (跰跰; 跰; 跰; 跰; 跰; ) CJK COMPATIBILITY IDEOGRAPH-2F9DC +2F9DD;208DE;208DE;208DE;208DE; # (𠣞𠣞; 𠣞𠣞; 𠣞𠣞; 𠣞𠣞; 𠣞𠣞; ) CJK COMPATIBILITY IDEOGRAPH-2F9DD +2F9DE;8ED4;8ED4;8ED4;8ED4; # (軔軔; 軔; 軔; 軔; 軔; ) CJK COMPATIBILITY IDEOGRAPH-2F9DE +2F9DF;8F38;8F38;8F38;8F38; # (輸輸; 輸; 輸; 輸; 輸; ) CJK COMPATIBILITY IDEOGRAPH-2F9DF +2F9E0;285D2;285D2;285D2;285D2; # (𨗒𨗒; 𨗒𨗒; 𨗒𨗒; 𨗒𨗒; 𨗒𨗒; ) CJK COMPATIBILITY IDEOGRAPH-2F9E0 +2F9E1;285ED;285ED;285ED;285ED; # (𨗭𨗭; 𨗭𨗭; 𨗭𨗭; 𨗭𨗭; 𨗭𨗭; ) CJK COMPATIBILITY IDEOGRAPH-2F9E1 +2F9E2;9094;9094;9094;9094; # (邔邔; 邔; 邔; 邔; 邔; ) CJK COMPATIBILITY IDEOGRAPH-2F9E2 +2F9E3;90F1;90F1;90F1;90F1; # (郱郱; 郱; 郱; 郱; 郱; ) CJK COMPATIBILITY IDEOGRAPH-2F9E3 +2F9E4;9111;9111;9111;9111; # (鄑鄑; 鄑; 鄑; 鄑; 鄑; ) CJK COMPATIBILITY IDEOGRAPH-2F9E4 +2F9E5;2872E;2872E;2872E;2872E; # (𨜮𨜮; 𨜮𨜮; 𨜮𨜮; 𨜮𨜮; 𨜮𨜮; ) CJK COMPATIBILITY IDEOGRAPH-2F9E5 +2F9E6;911B;911B;911B;911B; # (鄛鄛; 鄛; 鄛; 鄛; 鄛; ) CJK COMPATIBILITY IDEOGRAPH-2F9E6 +2F9E7;9238;9238;9238;9238; # (鈸鈸; 鈸; 鈸; 鈸; 鈸; ) CJK COMPATIBILITY IDEOGRAPH-2F9E7 +2F9E8;92D7;92D7;92D7;92D7; # (鋗鋗; 鋗; 鋗; 鋗; 鋗; ) CJK COMPATIBILITY IDEOGRAPH-2F9E8 +2F9E9;92D8;92D8;92D8;92D8; # (鋘鋘; 鋘; 鋘; 鋘; 鋘; ) CJK COMPATIBILITY IDEOGRAPH-2F9E9 +2F9EA;927C;927C;927C;927C; # (鉼鉼; 鉼; 鉼; 鉼; 鉼; ) CJK COMPATIBILITY IDEOGRAPH-2F9EA +2F9EB;93F9;93F9;93F9;93F9; # (鏹鏹; 鏹; 鏹; 鏹; 鏹; ) CJK COMPATIBILITY IDEOGRAPH-2F9EB +2F9EC;9415;9415;9415;9415; # (鐕鐕; 鐕; 鐕; 鐕; 鐕; ) CJK COMPATIBILITY IDEOGRAPH-2F9EC +2F9ED;28BFA;28BFA;28BFA;28BFA; # (𨯺𨯺; 𨯺𨯺; 𨯺𨯺; 𨯺𨯺; 𨯺𨯺; ) CJK COMPATIBILITY IDEOGRAPH-2F9ED +2F9EE;958B;958B;958B;958B; # (開開; 開; 開; 開; 開; ) CJK COMPATIBILITY IDEOGRAPH-2F9EE +2F9EF;4995;4995;4995;4995; # (䦕䦕; 䦕; 䦕; 䦕; 䦕; ) CJK COMPATIBILITY IDEOGRAPH-2F9EF +2F9F0;95B7;95B7;95B7;95B7; # (閷閷; 閷; 閷; 閷; 閷; ) CJK COMPATIBILITY IDEOGRAPH-2F9F0 +2F9F1;28D77;28D77;28D77;28D77; # (𨵷𨵷; 𨵷𨵷; 𨵷𨵷; 𨵷𨵷; 𨵷𨵷; ) CJK COMPATIBILITY IDEOGRAPH-2F9F1 +2F9F2;49E6;49E6;49E6;49E6; # (䧦䧦; 䧦; 䧦; 䧦; 䧦; ) CJK COMPATIBILITY IDEOGRAPH-2F9F2 +2F9F3;96C3;96C3;96C3;96C3; # (雃雃; 雃; 雃; 雃; 雃; ) CJK COMPATIBILITY IDEOGRAPH-2F9F3 +2F9F4;5DB2;5DB2;5DB2;5DB2; # (嶲嶲; 嶲; 嶲; 嶲; 嶲; ) CJK COMPATIBILITY IDEOGRAPH-2F9F4 +2F9F5;9723;9723;9723;9723; # (霣霣; 霣; 霣; 霣; 霣; ) CJK COMPATIBILITY IDEOGRAPH-2F9F5 +2F9F6;29145;29145;29145;29145; # (𩅅𩅅; 𩅅𩅅; 𩅅𩅅; 𩅅𩅅; 𩅅𩅅; ) CJK COMPATIBILITY IDEOGRAPH-2F9F6 +2F9F7;2921A;2921A;2921A;2921A; # (𩈚𩈚; 𩈚𩈚; 𩈚𩈚; 𩈚𩈚; 𩈚𩈚; ) CJK COMPATIBILITY IDEOGRAPH-2F9F7 +2F9F8;4A6E;4A6E;4A6E;4A6E; # (䩮䩮; 䩮; 䩮; 䩮; 䩮; ) CJK COMPATIBILITY IDEOGRAPH-2F9F8 +2F9F9;4A76;4A76;4A76;4A76; # (䩶䩶; 䩶; 䩶; 䩶; 䩶; ) CJK COMPATIBILITY IDEOGRAPH-2F9F9 +2F9FA;97E0;97E0;97E0;97E0; # (韠韠; 韠; 韠; 韠; 韠; ) CJK COMPATIBILITY IDEOGRAPH-2F9FA +2F9FB;2940A;2940A;2940A;2940A; # (𩐊𩐊; 𩐊𩐊; 𩐊𩐊; 𩐊𩐊; 𩐊𩐊; ) CJK COMPATIBILITY IDEOGRAPH-2F9FB +2F9FC;4AB2;4AB2;4AB2;4AB2; # (䪲䪲; 䪲; 䪲; 䪲; 䪲; ) CJK COMPATIBILITY IDEOGRAPH-2F9FC +2F9FD;29496;29496;29496;29496; # (𩒖𩒖; 𩒖𩒖; 𩒖𩒖; 𩒖𩒖; 𩒖𩒖; ) CJK COMPATIBILITY IDEOGRAPH-2F9FD +2F9FE;980B;980B;980B;980B; # (頋頋; 頋; 頋; 頋; 頋; ) CJK COMPATIBILITY IDEOGRAPH-2F9FE +2F9FF;980B;980B;980B;980B; # (頋頋; 頋; 頋; 頋; 頋; ) CJK COMPATIBILITY IDEOGRAPH-2F9FF +2FA00;9829;9829;9829;9829; # (頩頩; 頩; 頩; 頩; 頩; ) CJK COMPATIBILITY IDEOGRAPH-2FA00 +2FA01;295B6;295B6;295B6;295B6; # (𩖶𩖶; 𩖶𩖶; 𩖶𩖶; 𩖶𩖶; 𩖶𩖶; ) CJK COMPATIBILITY IDEOGRAPH-2FA01 +2FA02;98E2;98E2;98E2;98E2; # (飢飢; 飢; 飢; 飢; 飢; ) CJK COMPATIBILITY IDEOGRAPH-2FA02 +2FA03;4B33;4B33;4B33;4B33; # (䬳䬳; 䬳; 䬳; 䬳; 䬳; ) CJK COMPATIBILITY IDEOGRAPH-2FA03 +2FA04;9929;9929;9929;9929; # (餩餩; 餩; 餩; 餩; 餩; ) CJK COMPATIBILITY IDEOGRAPH-2FA04 +2FA05;99A7;99A7;99A7;99A7; # (馧馧; 馧; 馧; 馧; 馧; ) CJK COMPATIBILITY IDEOGRAPH-2FA05 +2FA06;99C2;99C2;99C2;99C2; # (駂駂; 駂; 駂; 駂; 駂; ) CJK COMPATIBILITY IDEOGRAPH-2FA06 +2FA07;99FE;99FE;99FE;99FE; # (駾駾; 駾; 駾; 駾; 駾; ) CJK COMPATIBILITY IDEOGRAPH-2FA07 +2FA08;4BCE;4BCE;4BCE;4BCE; # (䯎䯎; 䯎; 䯎; 䯎; 䯎; ) CJK COMPATIBILITY IDEOGRAPH-2FA08 +2FA09;29B30;29B30;29B30;29B30; # (𩬰𩬰; 𩬰𩬰; 𩬰𩬰; 𩬰𩬰; 𩬰𩬰; ) CJK COMPATIBILITY IDEOGRAPH-2FA09 +2FA0A;9B12;9B12;9B12;9B12; # (鬒鬒; 鬒; 鬒; 鬒; 鬒; ) CJK COMPATIBILITY IDEOGRAPH-2FA0A +2FA0B;9C40;9C40;9C40;9C40; # (鱀鱀; 鱀; 鱀; 鱀; 鱀; ) CJK COMPATIBILITY IDEOGRAPH-2FA0B +2FA0C;9CFD;9CFD;9CFD;9CFD; # (鳽鳽; 鳽; 鳽; 鳽; 鳽; ) CJK COMPATIBILITY IDEOGRAPH-2FA0C +2FA0D;4CCE;4CCE;4CCE;4CCE; # (䳎䳎; 䳎; 䳎; 䳎; 䳎; ) CJK COMPATIBILITY IDEOGRAPH-2FA0D +2FA0E;4CED;4CED;4CED;4CED; # (䳭䳭; 䳭; 䳭; 䳭; 䳭; ) CJK COMPATIBILITY IDEOGRAPH-2FA0E +2FA0F;9D67;9D67;9D67;9D67; # (鵧鵧; 鵧; 鵧; 鵧; 鵧; ) CJK COMPATIBILITY IDEOGRAPH-2FA0F +2FA10;2A0CE;2A0CE;2A0CE;2A0CE; # (𪃎𪃎; 𪃎𪃎; 𪃎𪃎; 𪃎𪃎; 𪃎𪃎; ) CJK COMPATIBILITY IDEOGRAPH-2FA10 +2FA11;4CF8;4CF8;4CF8;4CF8; # (䳸䳸; 䳸; 䳸; 䳸; 䳸; ) CJK COMPATIBILITY IDEOGRAPH-2FA11 +2FA12;2A105;2A105;2A105;2A105; # (𪄅𪄅; 𪄅𪄅; 𪄅𪄅; 𪄅𪄅; 𪄅𪄅; ) CJK COMPATIBILITY IDEOGRAPH-2FA12 +2FA13;2A20E;2A20E;2A20E;2A20E; # (𪈎𪈎; 𪈎𪈎; 𪈎𪈎; 𪈎𪈎; 𪈎𪈎; ) CJK COMPATIBILITY IDEOGRAPH-2FA13 +2FA14;2A291;2A291;2A291;2A291; # (𪊑𪊑; 𪊑𪊑; 𪊑𪊑; 𪊑𪊑; 𪊑𪊑; ) CJK COMPATIBILITY IDEOGRAPH-2FA14 +2FA15;9EBB;9EBB;9EBB;9EBB; # (麻麻; 麻; 麻; 麻; 麻; ) CJK COMPATIBILITY IDEOGRAPH-2FA15 +2FA16;4D56;4D56;4D56;4D56; # (䵖䵖; 䵖; 䵖; 䵖; 䵖; ) CJK COMPATIBILITY IDEOGRAPH-2FA16 +2FA17;9EF9;9EF9;9EF9;9EF9; # (黹黹; 黹; 黹; 黹; 黹; ) CJK COMPATIBILITY IDEOGRAPH-2FA17 +2FA18;9EFE;9EFE;9EFE;9EFE; # (黾黾; 黾; 黾; 黾; 黾; ) CJK COMPATIBILITY IDEOGRAPH-2FA18 +2FA19;9F05;9F05;9F05;9F05; # (鼅鼅; 鼅; 鼅; 鼅; 鼅; ) CJK COMPATIBILITY IDEOGRAPH-2FA19 +2FA1A;9F0F;9F0F;9F0F;9F0F; # (鼏鼏; 鼏; 鼏; 鼏; 鼏; ) CJK COMPATIBILITY IDEOGRAPH-2FA1A +2FA1B;9F16;9F16;9F16;9F16; # (鼖鼖; 鼖; 鼖; 鼖; 鼖; ) CJK COMPATIBILITY IDEOGRAPH-2FA1B +2FA1C;9F3B;9F3B;9F3B;9F3B; # (鼻鼻; 鼻; 鼻; 鼻; 鼻; ) CJK COMPATIBILITY IDEOGRAPH-2FA1C +2FA1D;2A600;2A600;2A600;2A600; # (𪘀𪘀; 𪘀𪘀; 𪘀𪘀; 𪘀𪘀; 𪘀𪘀; ) CJK COMPATIBILITY IDEOGRAPH-2FA1D +# +@Part2 # Canonical Order Test +# +0061 0315 0300 05AE 0300 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062; # (a◌̕◌̀◌֮◌̀b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GRAVE ACCENT, LATIN SMALL LETTER B +0061 0300 0315 0300 05AE 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062; # (a◌̀◌̕◌̀◌֮b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0301 0062;00E0 05AE 0301 0315 0062;0061 05AE 0300 0301 0315 0062;00E0 05AE 0301 0315 0062;0061 05AE 0300 0301 0315 0062; # (a◌̕◌̀◌֮◌́b; à◌֮◌́◌̕b; a◌֮◌̀◌́◌̕b; à◌֮◌́◌̕b; a◌֮◌̀◌́◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING ACUTE ACCENT, LATIN SMALL LETTER B +0061 0301 0315 0300 05AE 0062;00E1 05AE 0300 0315 0062;0061 05AE 0301 0300 0315 0062;00E1 05AE 0300 0315 0062;0061 05AE 0301 0300 0315 0062; # (a◌́◌̕◌̀◌֮b; á◌֮◌̀◌̕b; a◌֮◌́◌̀◌̕b; á◌֮◌̀◌̕b; a◌֮◌́◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING ACUTE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0302 0062;00E0 05AE 0302 0315 0062;0061 05AE 0300 0302 0315 0062;00E0 05AE 0302 0315 0062;0061 05AE 0300 0302 0315 0062; # (a◌̕◌̀◌֮◌̂b; à◌֮◌̂◌̕b; a◌֮◌̀◌̂◌̕b; à◌֮◌̂◌̕b; a◌֮◌̀◌̂◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CIRCUMFLEX ACCENT, LATIN SMALL LETTER B +0061 0302 0315 0300 05AE 0062;1EA7 05AE 0315 0062;0061 05AE 0302 0300 0315 0062;1EA7 05AE 0315 0062;0061 05AE 0302 0300 0315 0062; # (a◌̂◌̕◌̀◌֮b; ầ◌֮◌̕b; a◌֮◌̂◌̀◌̕b; ầ◌֮◌̕b; a◌֮◌̂◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CIRCUMFLEX ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0303 0062;00E0 05AE 0303 0315 0062;0061 05AE 0300 0303 0315 0062;00E0 05AE 0303 0315 0062;0061 05AE 0300 0303 0315 0062; # (a◌̕◌̀◌֮◌̃b; à◌֮◌̃◌̕b; a◌֮◌̀◌̃◌̕b; à◌֮◌̃◌̕b; a◌֮◌̀◌̃◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING TILDE, LATIN SMALL LETTER B +0061 0303 0315 0300 05AE 0062;00E3 05AE 0300 0315 0062;0061 05AE 0303 0300 0315 0062;00E3 05AE 0300 0315 0062;0061 05AE 0303 0300 0315 0062; # (a◌̃◌̕◌̀◌֮b; ã◌֮◌̀◌̕b; a◌֮◌̃◌̀◌̕b; ã◌֮◌̀◌̕b; a◌֮◌̃◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING TILDE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0304 0062;00E0 05AE 0304 0315 0062;0061 05AE 0300 0304 0315 0062;00E0 05AE 0304 0315 0062;0061 05AE 0300 0304 0315 0062; # (a◌̕◌̀◌֮◌̄b; à◌֮◌̄◌̕b; a◌֮◌̀◌̄◌̕b; à◌֮◌̄◌̕b; a◌֮◌̀◌̄◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING MACRON, LATIN SMALL LETTER B +0061 0304 0315 0300 05AE 0062;0101 05AE 0300 0315 0062;0061 05AE 0304 0300 0315 0062;0101 05AE 0300 0315 0062;0061 05AE 0304 0300 0315 0062; # (a◌̄◌̕◌̀◌֮b; ā◌֮◌̀◌̕b; a◌֮◌̄◌̀◌̕b; ā◌֮◌̀◌̕b; a◌֮◌̄◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING MACRON, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0305 0062;00E0 05AE 0305 0315 0062;0061 05AE 0300 0305 0315 0062;00E0 05AE 0305 0315 0062;0061 05AE 0300 0305 0315 0062; # (a◌̕◌̀◌֮◌̅b; à◌֮◌̅◌̕b; a◌֮◌̀◌̅◌̕b; à◌֮◌̅◌̕b; a◌֮◌̀◌̅◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING OVERLINE, LATIN SMALL LETTER B +0061 0305 0315 0300 05AE 0062;0061 05AE 0305 0300 0315 0062;0061 05AE 0305 0300 0315 0062;0061 05AE 0305 0300 0315 0062;0061 05AE 0305 0300 0315 0062; # (a◌̅◌̕◌̀◌֮b; a◌֮◌̅◌̀◌̕b; a◌֮◌̅◌̀◌̕b; a◌֮◌̅◌̀◌̕b; a◌֮◌̅◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING OVERLINE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0306 0062;00E0 05AE 0306 0315 0062;0061 05AE 0300 0306 0315 0062;00E0 05AE 0306 0315 0062;0061 05AE 0300 0306 0315 0062; # (a◌̕◌̀◌֮◌̆b; à◌֮◌̆◌̕b; a◌֮◌̀◌̆◌̕b; à◌֮◌̆◌̕b; a◌֮◌̀◌̆◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING BREVE, LATIN SMALL LETTER B +0061 0306 0315 0300 05AE 0062;1EB1 05AE 0315 0062;0061 05AE 0306 0300 0315 0062;1EB1 05AE 0315 0062;0061 05AE 0306 0300 0315 0062; # (a◌̆◌̕◌̀◌֮b; ằ◌֮◌̕b; a◌֮◌̆◌̀◌̕b; ằ◌֮◌̕b; a◌֮◌̆◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING BREVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0307 0062;00E0 05AE 0307 0315 0062;0061 05AE 0300 0307 0315 0062;00E0 05AE 0307 0315 0062;0061 05AE 0300 0307 0315 0062; # (a◌̕◌̀◌֮◌̇b; à◌֮◌̇◌̕b; a◌֮◌̀◌̇◌̕b; à◌֮◌̇◌̕b; a◌֮◌̀◌̇◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOT ABOVE, LATIN SMALL LETTER B +0061 0307 0315 0300 05AE 0062;0227 05AE 0300 0315 0062;0061 05AE 0307 0300 0315 0062;0227 05AE 0300 0315 0062;0061 05AE 0307 0300 0315 0062; # (a◌̇◌̕◌̀◌֮b; ȧ◌֮◌̀◌̕b; a◌֮◌̇◌̀◌̕b; ȧ◌֮◌̀◌̕b; a◌֮◌̇◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOT ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0308 0062;00E0 05AE 0308 0315 0062;0061 05AE 0300 0308 0315 0062;00E0 05AE 0308 0315 0062;0061 05AE 0300 0308 0315 0062; # (a◌̕◌̀◌֮◌̈b; à◌֮◌̈◌̕b; a◌֮◌̀◌̈◌̕b; à◌֮◌̈◌̕b; a◌֮◌̀◌̈◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DIAERESIS, LATIN SMALL LETTER B +0061 0308 0315 0300 05AE 0062;00E4 05AE 0300 0315 0062;0061 05AE 0308 0300 0315 0062;00E4 05AE 0300 0315 0062;0061 05AE 0308 0300 0315 0062; # (a◌̈◌̕◌̀◌֮b; ä◌֮◌̀◌̕b; a◌֮◌̈◌̀◌̕b; ä◌֮◌̀◌̕b; a◌֮◌̈◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DIAERESIS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0309 0062;00E0 05AE 0309 0315 0062;0061 05AE 0300 0309 0315 0062;00E0 05AE 0309 0315 0062;0061 05AE 0300 0309 0315 0062; # (a◌̕◌̀◌֮◌̉b; à◌֮◌̉◌̕b; a◌֮◌̀◌̉◌̕b; à◌֮◌̉◌̕b; a◌֮◌̀◌̉◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING HOOK ABOVE, LATIN SMALL LETTER B +0061 0309 0315 0300 05AE 0062;1EA3 05AE 0300 0315 0062;0061 05AE 0309 0300 0315 0062;1EA3 05AE 0300 0315 0062;0061 05AE 0309 0300 0315 0062; # (a◌̉◌̕◌̀◌֮b; ả◌֮◌̀◌̕b; a◌֮◌̉◌̀◌̕b; ả◌֮◌̀◌̕b; a◌֮◌̉◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING HOOK ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 030A 0062;00E0 05AE 030A 0315 0062;0061 05AE 0300 030A 0315 0062;00E0 05AE 030A 0315 0062;0061 05AE 0300 030A 0315 0062; # (a◌̕◌̀◌֮◌̊b; à◌֮◌̊◌̕b; a◌֮◌̀◌̊◌̕b; à◌֮◌̊◌̕b; a◌֮◌̀◌̊◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING RING ABOVE, LATIN SMALL LETTER B +0061 030A 0315 0300 05AE 0062;00E5 05AE 0300 0315 0062;0061 05AE 030A 0300 0315 0062;00E5 05AE 0300 0315 0062;0061 05AE 030A 0300 0315 0062; # (a◌̊◌̕◌̀◌֮b; å◌֮◌̀◌̕b; a◌֮◌̊◌̀◌̕b; å◌֮◌̀◌̕b; a◌֮◌̊◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING RING ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 030B 0062;00E0 05AE 030B 0315 0062;0061 05AE 0300 030B 0315 0062;00E0 05AE 030B 0315 0062;0061 05AE 0300 030B 0315 0062; # (a◌̕◌̀◌֮◌̋b; à◌֮◌̋◌̕b; a◌֮◌̀◌̋◌̕b; à◌֮◌̋◌̕b; a◌֮◌̀◌̋◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE ACUTE ACCENT, LATIN SMALL LETTER B +0061 030B 0315 0300 05AE 0062;0061 05AE 030B 0300 0315 0062;0061 05AE 030B 0300 0315 0062;0061 05AE 030B 0300 0315 0062;0061 05AE 030B 0300 0315 0062; # (a◌̋◌̕◌̀◌֮b; a◌֮◌̋◌̀◌̕b; a◌֮◌̋◌̀◌̕b; a◌֮◌̋◌̀◌̕b; a◌֮◌̋◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE ACUTE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 030C 0062;00E0 05AE 030C 0315 0062;0061 05AE 0300 030C 0315 0062;00E0 05AE 030C 0315 0062;0061 05AE 0300 030C 0315 0062; # (a◌̕◌̀◌֮◌̌b; à◌֮◌̌◌̕b; a◌֮◌̀◌̌◌̕b; à◌֮◌̌◌̕b; a◌֮◌̀◌̌◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CARON, LATIN SMALL LETTER B +0061 030C 0315 0300 05AE 0062;01CE 05AE 0300 0315 0062;0061 05AE 030C 0300 0315 0062;01CE 05AE 0300 0315 0062;0061 05AE 030C 0300 0315 0062; # (a◌̌◌̕◌̀◌֮b; ǎ◌֮◌̀◌̕b; a◌֮◌̌◌̀◌̕b; ǎ◌֮◌̀◌̕b; a◌֮◌̌◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CARON, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 030D 0062;00E0 05AE 030D 0315 0062;0061 05AE 0300 030D 0315 0062;00E0 05AE 030D 0315 0062;0061 05AE 0300 030D 0315 0062; # (a◌̕◌̀◌֮◌̍b; à◌֮◌̍◌̕b; a◌֮◌̀◌̍◌̕b; à◌֮◌̍◌̕b; a◌֮◌̀◌̍◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING VERTICAL LINE ABOVE, LATIN SMALL LETTER B +0061 030D 0315 0300 05AE 0062;0061 05AE 030D 0300 0315 0062;0061 05AE 030D 0300 0315 0062;0061 05AE 030D 0300 0315 0062;0061 05AE 030D 0300 0315 0062; # (a◌̍◌̕◌̀◌֮b; a◌֮◌̍◌̀◌̕b; a◌֮◌̍◌̀◌̕b; a◌֮◌̍◌̀◌̕b; a◌֮◌̍◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING VERTICAL LINE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 030E 0062;00E0 05AE 030E 0315 0062;0061 05AE 0300 030E 0315 0062;00E0 05AE 030E 0315 0062;0061 05AE 0300 030E 0315 0062; # (a◌̕◌̀◌֮◌̎b; à◌֮◌̎◌̕b; a◌֮◌̀◌̎◌̕b; à◌֮◌̎◌̕b; a◌֮◌̀◌̎◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE VERTICAL LINE ABOVE, LATIN SMALL LETTER B +0061 030E 0315 0300 05AE 0062;0061 05AE 030E 0300 0315 0062;0061 05AE 030E 0300 0315 0062;0061 05AE 030E 0300 0315 0062;0061 05AE 030E 0300 0315 0062; # (a◌̎◌̕◌̀◌֮b; a◌֮◌̎◌̀◌̕b; a◌֮◌̎◌̀◌̕b; a◌֮◌̎◌̀◌̕b; a◌֮◌̎◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE VERTICAL LINE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 030F 0062;00E0 05AE 030F 0315 0062;0061 05AE 0300 030F 0315 0062;00E0 05AE 030F 0315 0062;0061 05AE 0300 030F 0315 0062; # (a◌̕◌̀◌֮◌̏b; à◌֮◌̏◌̕b; a◌֮◌̀◌̏◌̕b; à◌֮◌̏◌̕b; a◌֮◌̀◌̏◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE GRAVE ACCENT, LATIN SMALL LETTER B +0061 030F 0315 0300 05AE 0062;0201 05AE 0300 0315 0062;0061 05AE 030F 0300 0315 0062;0201 05AE 0300 0315 0062;0061 05AE 030F 0300 0315 0062; # (a◌̏◌̕◌̀◌֮b; ȁ◌֮◌̀◌̕b; a◌֮◌̏◌̀◌̕b; ȁ◌֮◌̀◌̕b; a◌֮◌̏◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE GRAVE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0310 0062;00E0 05AE 0310 0315 0062;0061 05AE 0300 0310 0315 0062;00E0 05AE 0310 0315 0062;0061 05AE 0300 0310 0315 0062; # (a◌̕◌̀◌֮◌̐b; à◌֮◌̐◌̕b; a◌֮◌̀◌̐◌̕b; à◌֮◌̐◌̕b; a◌֮◌̀◌̐◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CANDRABINDU, LATIN SMALL LETTER B +0061 0310 0315 0300 05AE 0062;0061 05AE 0310 0300 0315 0062;0061 05AE 0310 0300 0315 0062;0061 05AE 0310 0300 0315 0062;0061 05AE 0310 0300 0315 0062; # (a◌̐◌̕◌̀◌֮b; a◌֮◌̐◌̀◌̕b; a◌֮◌̐◌̀◌̕b; a◌֮◌̐◌̀◌̕b; a◌֮◌̐◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CANDRABINDU, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0311 0062;00E0 05AE 0311 0315 0062;0061 05AE 0300 0311 0315 0062;00E0 05AE 0311 0315 0062;0061 05AE 0300 0311 0315 0062; # (a◌̕◌̀◌֮◌̑b; à◌֮◌̑◌̕b; a◌֮◌̀◌̑◌̕b; à◌֮◌̑◌̕b; a◌֮◌̀◌̑◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING INVERTED BREVE, LATIN SMALL LETTER B +0061 0311 0315 0300 05AE 0062;0203 05AE 0300 0315 0062;0061 05AE 0311 0300 0315 0062;0203 05AE 0300 0315 0062;0061 05AE 0311 0300 0315 0062; # (a◌̑◌̕◌̀◌֮b; ȃ◌֮◌̀◌̕b; a◌֮◌̑◌̀◌̕b; ȃ◌֮◌̀◌̕b; a◌֮◌̑◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING INVERTED BREVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0312 0062;00E0 05AE 0312 0315 0062;0061 05AE 0300 0312 0315 0062;00E0 05AE 0312 0315 0062;0061 05AE 0300 0312 0315 0062; # (a◌̕◌̀◌֮◌̒b; à◌֮◌̒◌̕b; a◌֮◌̀◌̒◌̕b; à◌֮◌̒◌̕b; a◌֮◌̀◌̒◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING TURNED COMMA ABOVE, LATIN SMALL LETTER B +0061 0312 0315 0300 05AE 0062;0061 05AE 0312 0300 0315 0062;0061 05AE 0312 0300 0315 0062;0061 05AE 0312 0300 0315 0062;0061 05AE 0312 0300 0315 0062; # (a◌̒◌̕◌̀◌֮b; a◌֮◌̒◌̀◌̕b; a◌֮◌̒◌̀◌̕b; a◌֮◌̒◌̀◌̕b; a◌֮◌̒◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING TURNED COMMA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0313 0062;00E0 05AE 0313 0315 0062;0061 05AE 0300 0313 0315 0062;00E0 05AE 0313 0315 0062;0061 05AE 0300 0313 0315 0062; # (a◌̕◌̀◌֮◌̓b; à◌֮◌̓◌̕b; a◌֮◌̀◌̓◌̕b; à◌֮◌̓◌̕b; a◌֮◌̀◌̓◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING COMMA ABOVE, LATIN SMALL LETTER B +0061 0313 0315 0300 05AE 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062; # (a◌̓◌̕◌̀◌֮b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0314 0062;00E0 05AE 0314 0315 0062;0061 05AE 0300 0314 0315 0062;00E0 05AE 0314 0315 0062;0061 05AE 0300 0314 0315 0062; # (a◌̕◌̀◌֮◌̔b; à◌֮◌̔◌̕b; a◌֮◌̀◌̔◌̕b; à◌֮◌̔◌̕b; a◌֮◌̀◌̔◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING REVERSED COMMA ABOVE, LATIN SMALL LETTER B +0061 0314 0315 0300 05AE 0062;0061 05AE 0314 0300 0315 0062;0061 05AE 0314 0300 0315 0062;0061 05AE 0314 0300 0315 0062;0061 05AE 0314 0300 0315 0062; # (a◌̔◌̕◌̀◌֮b; a◌֮◌̔◌̀◌̕b; a◌֮◌̔◌̀◌̕b; a◌֮◌̔◌̀◌̕b; a◌֮◌̔◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING REVERSED COMMA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0362 0315 0300 0315 0062;00E0 0315 0315 0362 0062;0061 0300 0315 0315 0362 0062;00E0 0315 0315 0362 0062;0061 0300 0315 0315 0362 0062; # (a◌͢◌̕◌̀◌̕b; à◌̕◌̕◌͢b; a◌̀◌̕◌̕◌͢b; à◌̕◌̕◌͢b; a◌̀◌̕◌̕◌͢b; ) LATIN SMALL LETTER A, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, COMBINING COMMA ABOVE RIGHT, LATIN SMALL LETTER B +0061 0315 0362 0315 0300 0062;00E0 0315 0315 0362 0062;0061 0300 0315 0315 0362 0062;00E0 0315 0315 0362 0062;0061 0300 0315 0315 0362 0062; # (a◌̕◌͢◌̕◌̀b; à◌̕◌̕◌͢b; a◌̀◌̕◌̕◌͢b; à◌̕◌̕◌͢b; a◌̀◌̕◌̕◌͢b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, LATIN SMALL LETTER B +0061 059A 0316 302A 0316 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062; # (a◌֚◌̖◌〪◌̖b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING GRAVE ACCENT BELOW, LATIN SMALL LETTER B +0061 0316 059A 0316 302A 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062; # (a◌̖◌֚◌̖◌〪b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0317 0062;0061 302A 0316 0317 059A 0062;0061 302A 0316 0317 059A 0062;0061 302A 0316 0317 059A 0062;0061 302A 0316 0317 059A 0062; # (a◌֚◌̖◌〪◌̗b; a◌〪◌̖◌̗◌֚b; a◌〪◌̖◌̗◌֚b; a◌〪◌̖◌̗◌֚b; a◌〪◌̖◌̗◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING ACUTE ACCENT BELOW, LATIN SMALL LETTER B +0061 0317 059A 0316 302A 0062;0061 302A 0317 0316 059A 0062;0061 302A 0317 0316 059A 0062;0061 302A 0317 0316 059A 0062;0061 302A 0317 0316 059A 0062; # (a◌̗◌֚◌̖◌〪b; a◌〪◌̗◌̖◌֚b; a◌〪◌̗◌̖◌֚b; a◌〪◌̗◌̖◌֚b; a◌〪◌̗◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING ACUTE ACCENT BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0318 0062;0061 302A 0316 0318 059A 0062;0061 302A 0316 0318 059A 0062;0061 302A 0316 0318 059A 0062;0061 302A 0316 0318 059A 0062; # (a◌֚◌̖◌〪◌̘b; a◌〪◌̖◌̘◌֚b; a◌〪◌̖◌̘◌֚b; a◌〪◌̖◌̘◌֚b; a◌〪◌̖◌̘◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LEFT TACK BELOW, LATIN SMALL LETTER B +0061 0318 059A 0316 302A 0062;0061 302A 0318 0316 059A 0062;0061 302A 0318 0316 059A 0062;0061 302A 0318 0316 059A 0062;0061 302A 0318 0316 059A 0062; # (a◌̘◌֚◌̖◌〪b; a◌〪◌̘◌̖◌֚b; a◌〪◌̘◌̖◌֚b; a◌〪◌̘◌̖◌֚b; a◌〪◌̘◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LEFT TACK BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0319 0062;0061 302A 0316 0319 059A 0062;0061 302A 0316 0319 059A 0062;0061 302A 0316 0319 059A 0062;0061 302A 0316 0319 059A 0062; # (a◌֚◌̖◌〪◌̙b; a◌〪◌̖◌̙◌֚b; a◌〪◌̖◌̙◌֚b; a◌〪◌̖◌̙◌֚b; a◌〪◌̖◌̙◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING RIGHT TACK BELOW, LATIN SMALL LETTER B +0061 0319 059A 0316 302A 0062;0061 302A 0319 0316 059A 0062;0061 302A 0319 0316 059A 0062;0061 302A 0319 0316 059A 0062;0061 302A 0319 0316 059A 0062; # (a◌̙◌֚◌̖◌〪b; a◌〪◌̙◌̖◌֚b; a◌〪◌̙◌̖◌֚b; a◌〪◌̙◌̖◌֚b; a◌〪◌̙◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING RIGHT TACK BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0362 0315 0300 031A 0062;00E0 0315 031A 0362 0062;0061 0300 0315 031A 0362 0062;00E0 0315 031A 0362 0062;0061 0300 0315 031A 0362 0062; # (a◌͢◌̕◌̀◌̚b; à◌̕◌̚◌͢b; a◌̀◌̕◌̚◌͢b; à◌̕◌̚◌͢b; a◌̀◌̕◌̚◌͢b; ) LATIN SMALL LETTER A, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, COMBINING LEFT ANGLE ABOVE, LATIN SMALL LETTER B +0061 031A 0362 0315 0300 0062;00E0 031A 0315 0362 0062;0061 0300 031A 0315 0362 0062;00E0 031A 0315 0362 0062;0061 0300 031A 0315 0362 0062; # (a◌̚◌͢◌̕◌̀b; à◌̚◌̕◌͢b; a◌̀◌̚◌̕◌͢b; à◌̚◌̕◌͢b; a◌̀◌̚◌̕◌͢b; ) LATIN SMALL LETTER A, COMBINING LEFT ANGLE ABOVE, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, LATIN SMALL LETTER B +0061 302A 031B 0321 031B 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062; # (a◌〪◌̛◌̡◌̛b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, COMBINING HORN, LATIN SMALL LETTER B +0061 031B 302A 031B 0321 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062; # (a◌̛◌〪◌̛◌̡b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; ) LATIN SMALL LETTER A, COMBINING HORN, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B +0061 059A 0316 302A 031C 0062;0061 302A 0316 031C 059A 0062;0061 302A 0316 031C 059A 0062;0061 302A 0316 031C 059A 0062;0061 302A 0316 031C 059A 0062; # (a◌֚◌̖◌〪◌̜b; a◌〪◌̖◌̜◌֚b; a◌〪◌̖◌̜◌֚b; a◌〪◌̖◌̜◌֚b; a◌〪◌̖◌̜◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LEFT HALF RING BELOW, LATIN SMALL LETTER B +0061 031C 059A 0316 302A 0062;0061 302A 031C 0316 059A 0062;0061 302A 031C 0316 059A 0062;0061 302A 031C 0316 059A 0062;0061 302A 031C 0316 059A 0062; # (a◌̜◌֚◌̖◌〪b; a◌〪◌̜◌̖◌֚b; a◌〪◌̜◌̖◌֚b; a◌〪◌̜◌̖◌֚b; a◌〪◌̜◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LEFT HALF RING BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 031D 0062;0061 302A 0316 031D 059A 0062;0061 302A 0316 031D 059A 0062;0061 302A 0316 031D 059A 0062;0061 302A 0316 031D 059A 0062; # (a◌֚◌̖◌〪◌̝b; a◌〪◌̖◌̝◌֚b; a◌〪◌̖◌̝◌֚b; a◌〪◌̖◌̝◌֚b; a◌〪◌̖◌̝◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING UP TACK BELOW, LATIN SMALL LETTER B +0061 031D 059A 0316 302A 0062;0061 302A 031D 0316 059A 0062;0061 302A 031D 0316 059A 0062;0061 302A 031D 0316 059A 0062;0061 302A 031D 0316 059A 0062; # (a◌̝◌֚◌̖◌〪b; a◌〪◌̝◌̖◌֚b; a◌〪◌̝◌̖◌֚b; a◌〪◌̝◌̖◌֚b; a◌〪◌̝◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING UP TACK BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 031E 0062;0061 302A 0316 031E 059A 0062;0061 302A 0316 031E 059A 0062;0061 302A 0316 031E 059A 0062;0061 302A 0316 031E 059A 0062; # (a◌֚◌̖◌〪◌̞b; a◌〪◌̖◌̞◌֚b; a◌〪◌̖◌̞◌֚b; a◌〪◌̖◌̞◌֚b; a◌〪◌̖◌̞◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING DOWN TACK BELOW, LATIN SMALL LETTER B +0061 031E 059A 0316 302A 0062;0061 302A 031E 0316 059A 0062;0061 302A 031E 0316 059A 0062;0061 302A 031E 0316 059A 0062;0061 302A 031E 0316 059A 0062; # (a◌̞◌֚◌̖◌〪b; a◌〪◌̞◌̖◌֚b; a◌〪◌̞◌̖◌֚b; a◌〪◌̞◌̖◌֚b; a◌〪◌̞◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING DOWN TACK BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 031F 0062;0061 302A 0316 031F 059A 0062;0061 302A 0316 031F 059A 0062;0061 302A 0316 031F 059A 0062;0061 302A 0316 031F 059A 0062; # (a◌֚◌̖◌〪◌̟b; a◌〪◌̖◌̟◌֚b; a◌〪◌̖◌̟◌֚b; a◌〪◌̖◌̟◌֚b; a◌〪◌̖◌̟◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING PLUS SIGN BELOW, LATIN SMALL LETTER B +0061 031F 059A 0316 302A 0062;0061 302A 031F 0316 059A 0062;0061 302A 031F 0316 059A 0062;0061 302A 031F 0316 059A 0062;0061 302A 031F 0316 059A 0062; # (a◌̟◌֚◌̖◌〪b; a◌〪◌̟◌̖◌֚b; a◌〪◌̟◌̖◌֚b; a◌〪◌̟◌̖◌֚b; a◌〪◌̟◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING PLUS SIGN BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0320 0062;0061 302A 0316 0320 059A 0062;0061 302A 0316 0320 059A 0062;0061 302A 0316 0320 059A 0062;0061 302A 0316 0320 059A 0062; # (a◌֚◌̖◌〪◌̠b; a◌〪◌̖◌̠◌֚b; a◌〪◌̖◌̠◌֚b; a◌〪◌̖◌̠◌֚b; a◌〪◌̖◌̠◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING MINUS SIGN BELOW, LATIN SMALL LETTER B +0061 0320 059A 0316 302A 0062;0061 302A 0320 0316 059A 0062;0061 302A 0320 0316 059A 0062;0061 302A 0320 0316 059A 0062;0061 302A 0320 0316 059A 0062; # (a◌̠◌֚◌̖◌〪b; a◌〪◌̠◌̖◌֚b; a◌〪◌̠◌̖◌֚b; a◌〪◌̠◌̖◌֚b; a◌〪◌̠◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING MINUS SIGN BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 031B 0321 0F74 0321 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062; # (a◌̛◌̡◌ུ◌̡b; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; ) LATIN SMALL LETTER A, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B +0061 0321 031B 0321 0F74 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062; # (a◌̡◌̛◌̡◌ུb; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; ) LATIN SMALL LETTER A, COMBINING PALATALIZED HOOK BELOW, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, LATIN SMALL LETTER B +0061 031B 0321 0F74 0322 0062;0061 0F74 0321 0322 031B 0062;0061 0F74 0321 0322 031B 0062;0061 0F74 0321 0322 031B 0062;0061 0F74 0321 0322 031B 0062; # (a◌̛◌̡◌ུ◌̢b; a◌ུ◌̡◌̢◌̛b; a◌ུ◌̡◌̢◌̛b; a◌ུ◌̡◌̢◌̛b; a◌ུ◌̡◌̢◌̛b; ) LATIN SMALL LETTER A, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, COMBINING RETROFLEX HOOK BELOW, LATIN SMALL LETTER B +0061 0322 031B 0321 0F74 0062;0061 0F74 0322 0321 031B 0062;0061 0F74 0322 0321 031B 0062;0061 0F74 0322 0321 031B 0062;0061 0F74 0322 0321 031B 0062; # (a◌̢◌̛◌̡◌ུb; a◌ུ◌̢◌̡◌̛b; a◌ུ◌̢◌̡◌̛b; a◌ུ◌̢◌̡◌̛b; a◌ུ◌̢◌̡◌̛b; ) LATIN SMALL LETTER A, COMBINING RETROFLEX HOOK BELOW, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, LATIN SMALL LETTER B +0061 059A 0316 302A 0323 0062;0061 302A 0316 0323 059A 0062;0061 302A 0316 0323 059A 0062;0061 302A 0316 0323 059A 0062;0061 302A 0316 0323 059A 0062; # (a◌֚◌̖◌〪◌̣b; a◌〪◌̖◌̣◌֚b; a◌〪◌̖◌̣◌֚b; a◌〪◌̖◌̣◌֚b; a◌〪◌̖◌̣◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING DOT BELOW, LATIN SMALL LETTER B +0061 0323 059A 0316 302A 0062;1EA1 302A 0316 059A 0062;0061 302A 0323 0316 059A 0062;1EA1 302A 0316 059A 0062;0061 302A 0323 0316 059A 0062; # (a◌̣◌֚◌̖◌〪b; ạ◌〪◌̖◌֚b; a◌〪◌̣◌̖◌֚b; ạ◌〪◌̖◌֚b; a◌〪◌̣◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING DOT BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0324 0062;0061 302A 0316 0324 059A 0062;0061 302A 0316 0324 059A 0062;0061 302A 0316 0324 059A 0062;0061 302A 0316 0324 059A 0062; # (a◌֚◌̖◌〪◌̤b; a◌〪◌̖◌̤◌֚b; a◌〪◌̖◌̤◌֚b; a◌〪◌̖◌̤◌֚b; a◌〪◌̖◌̤◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING DIAERESIS BELOW, LATIN SMALL LETTER B +0061 0324 059A 0316 302A 0062;0061 302A 0324 0316 059A 0062;0061 302A 0324 0316 059A 0062;0061 302A 0324 0316 059A 0062;0061 302A 0324 0316 059A 0062; # (a◌̤◌֚◌̖◌〪b; a◌〪◌̤◌̖◌֚b; a◌〪◌̤◌̖◌֚b; a◌〪◌̤◌̖◌֚b; a◌〪◌̤◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING DIAERESIS BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0325 0062;0061 302A 0316 0325 059A 0062;0061 302A 0316 0325 059A 0062;0061 302A 0316 0325 059A 0062;0061 302A 0316 0325 059A 0062; # (a◌֚◌̖◌〪◌̥b; a◌〪◌̖◌̥◌֚b; a◌〪◌̖◌̥◌֚b; a◌〪◌̖◌̥◌֚b; a◌〪◌̖◌̥◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING RING BELOW, LATIN SMALL LETTER B +0061 0325 059A 0316 302A 0062;1E01 302A 0316 059A 0062;0061 302A 0325 0316 059A 0062;1E01 302A 0316 059A 0062;0061 302A 0325 0316 059A 0062; # (a◌̥◌֚◌̖◌〪b; ḁ◌〪◌̖◌֚b; a◌〪◌̥◌̖◌֚b; ḁ◌〪◌̖◌֚b; a◌〪◌̥◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING RING BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0326 0062;0061 302A 0316 0326 059A 0062;0061 302A 0316 0326 059A 0062;0061 302A 0316 0326 059A 0062;0061 302A 0316 0326 059A 0062; # (a◌֚◌̖◌〪◌̦b; a◌〪◌̖◌̦◌֚b; a◌〪◌̖◌̦◌֚b; a◌〪◌̖◌̦◌֚b; a◌〪◌̖◌̦◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING COMMA BELOW, LATIN SMALL LETTER B +0061 0326 059A 0316 302A 0062;0061 302A 0326 0316 059A 0062;0061 302A 0326 0316 059A 0062;0061 302A 0326 0316 059A 0062;0061 302A 0326 0316 059A 0062; # (a◌̦◌֚◌̖◌〪b; a◌〪◌̦◌̖◌֚b; a◌〪◌̦◌̖◌֚b; a◌〪◌̦◌̖◌֚b; a◌〪◌̦◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING COMMA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 031B 0321 0F74 0327 0062;0061 0F74 0321 0327 031B 0062;0061 0F74 0321 0327 031B 0062;0061 0F74 0321 0327 031B 0062;0061 0F74 0321 0327 031B 0062; # (a◌̛◌̡◌ུ◌̧b; a◌ུ◌̡◌̧◌̛b; a◌ུ◌̡◌̧◌̛b; a◌ུ◌̡◌̧◌̛b; a◌ུ◌̡◌̧◌̛b; ) LATIN SMALL LETTER A, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, COMBINING CEDILLA, LATIN SMALL LETTER B +0061 0327 031B 0321 0F74 0062;0061 0F74 0327 0321 031B 0062;0061 0F74 0327 0321 031B 0062;0061 0F74 0327 0321 031B 0062;0061 0F74 0327 0321 031B 0062; # (a◌̧◌̛◌̡◌ུb; a◌ུ◌̧◌̡◌̛b; a◌ུ◌̧◌̡◌̛b; a◌ུ◌̧◌̡◌̛b; a◌ུ◌̧◌̡◌̛b; ) LATIN SMALL LETTER A, COMBINING CEDILLA, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, LATIN SMALL LETTER B +0061 031B 0321 0F74 0328 0062;0061 0F74 0321 0328 031B 0062;0061 0F74 0321 0328 031B 0062;0061 0F74 0321 0328 031B 0062;0061 0F74 0321 0328 031B 0062; # (a◌̛◌̡◌ུ◌̨b; a◌ུ◌̡◌̨◌̛b; a◌ུ◌̡◌̨◌̛b; a◌ུ◌̡◌̨◌̛b; a◌ུ◌̡◌̨◌̛b; ) LATIN SMALL LETTER A, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, COMBINING OGONEK, LATIN SMALL LETTER B +0061 0328 031B 0321 0F74 0062;0105 0F74 0321 031B 0062;0061 0F74 0328 0321 031B 0062;0105 0F74 0321 031B 0062;0061 0F74 0328 0321 031B 0062; # (a◌̨◌̛◌̡◌ུb; ą◌ུ◌̡◌̛b; a◌ུ◌̨◌̡◌̛b; ą◌ུ◌̡◌̛b; a◌ུ◌̨◌̡◌̛b; ) LATIN SMALL LETTER A, COMBINING OGONEK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, LATIN SMALL LETTER B +0061 059A 0316 302A 0329 0062;0061 302A 0316 0329 059A 0062;0061 302A 0316 0329 059A 0062;0061 302A 0316 0329 059A 0062;0061 302A 0316 0329 059A 0062; # (a◌֚◌̖◌〪◌̩b; a◌〪◌̖◌̩◌֚b; a◌〪◌̖◌̩◌֚b; a◌〪◌̖◌̩◌֚b; a◌〪◌̖◌̩◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING VERTICAL LINE BELOW, LATIN SMALL LETTER B +0061 0329 059A 0316 302A 0062;0061 302A 0329 0316 059A 0062;0061 302A 0329 0316 059A 0062;0061 302A 0329 0316 059A 0062;0061 302A 0329 0316 059A 0062; # (a◌̩◌֚◌̖◌〪b; a◌〪◌̩◌̖◌֚b; a◌〪◌̩◌̖◌֚b; a◌〪◌̩◌̖◌֚b; a◌〪◌̩◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING VERTICAL LINE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 032A 0062;0061 302A 0316 032A 059A 0062;0061 302A 0316 032A 059A 0062;0061 302A 0316 032A 059A 0062;0061 302A 0316 032A 059A 0062; # (a◌֚◌̖◌〪◌̪b; a◌〪◌̖◌̪◌֚b; a◌〪◌̖◌̪◌֚b; a◌〪◌̖◌̪◌֚b; a◌〪◌̖◌̪◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING BRIDGE BELOW, LATIN SMALL LETTER B +0061 032A 059A 0316 302A 0062;0061 302A 032A 0316 059A 0062;0061 302A 032A 0316 059A 0062;0061 302A 032A 0316 059A 0062;0061 302A 032A 0316 059A 0062; # (a◌̪◌֚◌̖◌〪b; a◌〪◌̪◌̖◌֚b; a◌〪◌̪◌̖◌֚b; a◌〪◌̪◌̖◌֚b; a◌〪◌̪◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING BRIDGE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 032B 0062;0061 302A 0316 032B 059A 0062;0061 302A 0316 032B 059A 0062;0061 302A 0316 032B 059A 0062;0061 302A 0316 032B 059A 0062; # (a◌֚◌̖◌〪◌̫b; a◌〪◌̖◌̫◌֚b; a◌〪◌̖◌̫◌֚b; a◌〪◌̖◌̫◌֚b; a◌〪◌̖◌̫◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING INVERTED DOUBLE ARCH BELOW, LATIN SMALL LETTER B +0061 032B 059A 0316 302A 0062;0061 302A 032B 0316 059A 0062;0061 302A 032B 0316 059A 0062;0061 302A 032B 0316 059A 0062;0061 302A 032B 0316 059A 0062; # (a◌̫◌֚◌̖◌〪b; a◌〪◌̫◌̖◌֚b; a◌〪◌̫◌̖◌֚b; a◌〪◌̫◌̖◌֚b; a◌〪◌̫◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING INVERTED DOUBLE ARCH BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 032C 0062;0061 302A 0316 032C 059A 0062;0061 302A 0316 032C 059A 0062;0061 302A 0316 032C 059A 0062;0061 302A 0316 032C 059A 0062; # (a◌֚◌̖◌〪◌̬b; a◌〪◌̖◌̬◌֚b; a◌〪◌̖◌̬◌֚b; a◌〪◌̖◌̬◌֚b; a◌〪◌̖◌̬◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING CARON BELOW, LATIN SMALL LETTER B +0061 032C 059A 0316 302A 0062;0061 302A 032C 0316 059A 0062;0061 302A 032C 0316 059A 0062;0061 302A 032C 0316 059A 0062;0061 302A 032C 0316 059A 0062; # (a◌̬◌֚◌̖◌〪b; a◌〪◌̬◌̖◌֚b; a◌〪◌̬◌̖◌֚b; a◌〪◌̬◌̖◌֚b; a◌〪◌̬◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING CARON BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 032D 0062;0061 302A 0316 032D 059A 0062;0061 302A 0316 032D 059A 0062;0061 302A 0316 032D 059A 0062;0061 302A 0316 032D 059A 0062; # (a◌֚◌̖◌〪◌̭b; a◌〪◌̖◌̭◌֚b; a◌〪◌̖◌̭◌֚b; a◌〪◌̖◌̭◌֚b; a◌〪◌̖◌̭◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING CIRCUMFLEX ACCENT BELOW, LATIN SMALL LETTER B +0061 032D 059A 0316 302A 0062;0061 302A 032D 0316 059A 0062;0061 302A 032D 0316 059A 0062;0061 302A 032D 0316 059A 0062;0061 302A 032D 0316 059A 0062; # (a◌̭◌֚◌̖◌〪b; a◌〪◌̭◌̖◌֚b; a◌〪◌̭◌̖◌֚b; a◌〪◌̭◌̖◌֚b; a◌〪◌̭◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING CIRCUMFLEX ACCENT BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 032E 0062;0061 302A 0316 032E 059A 0062;0061 302A 0316 032E 059A 0062;0061 302A 0316 032E 059A 0062;0061 302A 0316 032E 059A 0062; # (a◌֚◌̖◌〪◌̮b; a◌〪◌̖◌̮◌֚b; a◌〪◌̖◌̮◌֚b; a◌〪◌̖◌̮◌֚b; a◌〪◌̖◌̮◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING BREVE BELOW, LATIN SMALL LETTER B +0061 032E 059A 0316 302A 0062;0061 302A 032E 0316 059A 0062;0061 302A 032E 0316 059A 0062;0061 302A 032E 0316 059A 0062;0061 302A 032E 0316 059A 0062; # (a◌̮◌֚◌̖◌〪b; a◌〪◌̮◌̖◌֚b; a◌〪◌̮◌̖◌֚b; a◌〪◌̮◌̖◌֚b; a◌〪◌̮◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING BREVE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 032F 0062;0061 302A 0316 032F 059A 0062;0061 302A 0316 032F 059A 0062;0061 302A 0316 032F 059A 0062;0061 302A 0316 032F 059A 0062; # (a◌֚◌̖◌〪◌̯b; a◌〪◌̖◌̯◌֚b; a◌〪◌̖◌̯◌֚b; a◌〪◌̖◌̯◌֚b; a◌〪◌̖◌̯◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING INVERTED BREVE BELOW, LATIN SMALL LETTER B +0061 032F 059A 0316 302A 0062;0061 302A 032F 0316 059A 0062;0061 302A 032F 0316 059A 0062;0061 302A 032F 0316 059A 0062;0061 302A 032F 0316 059A 0062; # (a◌̯◌֚◌̖◌〪b; a◌〪◌̯◌̖◌֚b; a◌〪◌̯◌̖◌֚b; a◌〪◌̯◌̖◌֚b; a◌〪◌̯◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING INVERTED BREVE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0330 0062;0061 302A 0316 0330 059A 0062;0061 302A 0316 0330 059A 0062;0061 302A 0316 0330 059A 0062;0061 302A 0316 0330 059A 0062; # (a◌֚◌̖◌〪◌̰b; a◌〪◌̖◌̰◌֚b; a◌〪◌̖◌̰◌֚b; a◌〪◌̖◌̰◌֚b; a◌〪◌̖◌̰◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING TILDE BELOW, LATIN SMALL LETTER B +0061 0330 059A 0316 302A 0062;0061 302A 0330 0316 059A 0062;0061 302A 0330 0316 059A 0062;0061 302A 0330 0316 059A 0062;0061 302A 0330 0316 059A 0062; # (a◌̰◌֚◌̖◌〪b; a◌〪◌̰◌̖◌֚b; a◌〪◌̰◌̖◌֚b; a◌〪◌̰◌̖◌֚b; a◌〪◌̰◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING TILDE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0331 0062;0061 302A 0316 0331 059A 0062;0061 302A 0316 0331 059A 0062;0061 302A 0316 0331 059A 0062;0061 302A 0316 0331 059A 0062; # (a◌֚◌̖◌〪◌̱b; a◌〪◌̖◌̱◌֚b; a◌〪◌̖◌̱◌֚b; a◌〪◌̖◌̱◌֚b; a◌〪◌̖◌̱◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING MACRON BELOW, LATIN SMALL LETTER B +0061 0331 059A 0316 302A 0062;0061 302A 0331 0316 059A 0062;0061 302A 0331 0316 059A 0062;0061 302A 0331 0316 059A 0062;0061 302A 0331 0316 059A 0062; # (a◌̱◌֚◌̖◌〪b; a◌〪◌̱◌̖◌֚b; a◌〪◌̱◌̖◌֚b; a◌〪◌̱◌̖◌֚b; a◌〪◌̱◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING MACRON BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0332 0062;0061 302A 0316 0332 059A 0062;0061 302A 0316 0332 059A 0062;0061 302A 0316 0332 059A 0062;0061 302A 0316 0332 059A 0062; # (a◌֚◌̖◌〪◌̲b; a◌〪◌̖◌̲◌֚b; a◌〪◌̖◌̲◌֚b; a◌〪◌̖◌̲◌֚b; a◌〪◌̖◌̲◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LOW LINE, LATIN SMALL LETTER B +0061 0332 059A 0316 302A 0062;0061 302A 0332 0316 059A 0062;0061 302A 0332 0316 059A 0062;0061 302A 0332 0316 059A 0062;0061 302A 0332 0316 059A 0062; # (a◌̲◌֚◌̖◌〪b; a◌〪◌̲◌̖◌֚b; a◌〪◌̲◌̖◌֚b; a◌〪◌̲◌̖◌֚b; a◌〪◌̲◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LOW LINE, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0333 0062;0061 302A 0316 0333 059A 0062;0061 302A 0316 0333 059A 0062;0061 302A 0316 0333 059A 0062;0061 302A 0316 0333 059A 0062; # (a◌֚◌̖◌〪◌̳b; a◌〪◌̖◌̳◌֚b; a◌〪◌̖◌̳◌֚b; a◌〪◌̖◌̳◌֚b; a◌〪◌̖◌̳◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING DOUBLE LOW LINE, LATIN SMALL LETTER B +0061 0333 059A 0316 302A 0062;0061 302A 0333 0316 059A 0062;0061 302A 0333 0316 059A 0062;0061 302A 0333 0316 059A 0062;0061 302A 0333 0316 059A 0062; # (a◌̳◌֚◌̖◌〪b; a◌〪◌̳◌̖◌֚b; a◌〪◌̳◌̖◌֚b; a◌〪◌̳◌̖◌֚b; a◌〪◌̳◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING DOUBLE LOW LINE, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 093C 0334 0334 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062; # (a◌़◌̴◌̴b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 0334 093C 0334 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062; # (a◌̴◌़◌̴b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING TILDE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 093C 0334 0335 0062;0061 0334 0335 093C 0062;0061 0334 0335 093C 0062;0061 0334 0335 093C 0062;0061 0334 0335 093C 0062; # (a◌़◌̴◌̵b; a◌̴◌̵◌़b; a◌̴◌̵◌़b; a◌̴◌̵◌़b; a◌̴◌̵◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING SHORT STROKE OVERLAY, LATIN SMALL LETTER B +0061 0335 093C 0334 0062;0061 0335 0334 093C 0062;0061 0335 0334 093C 0062;0061 0335 0334 093C 0062;0061 0335 0334 093C 0062; # (a◌̵◌़◌̴b; a◌̵◌̴◌़b; a◌̵◌̴◌़b; a◌̵◌̴◌़b; a◌̵◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING SHORT STROKE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 093C 0334 0336 0062;0061 0334 0336 093C 0062;0061 0334 0336 093C 0062;0061 0334 0336 093C 0062;0061 0334 0336 093C 0062; # (a◌़◌̴◌̶b; a◌̴◌̶◌़b; a◌̴◌̶◌़b; a◌̴◌̶◌़b; a◌̴◌̶◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING LONG STROKE OVERLAY, LATIN SMALL LETTER B +0061 0336 093C 0334 0062;0061 0336 0334 093C 0062;0061 0336 0334 093C 0062;0061 0336 0334 093C 0062;0061 0336 0334 093C 0062; # (a◌̶◌़◌̴b; a◌̶◌̴◌़b; a◌̶◌̴◌़b; a◌̶◌̴◌़b; a◌̶◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING LONG STROKE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 093C 0334 0337 0062;0061 0334 0337 093C 0062;0061 0334 0337 093C 0062;0061 0334 0337 093C 0062;0061 0334 0337 093C 0062; # (a◌़◌̴◌̷b; a◌̴◌̷◌़b; a◌̴◌̷◌़b; a◌̴◌̷◌़b; a◌̴◌̷◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING SHORT SOLIDUS OVERLAY, LATIN SMALL LETTER B +0061 0337 093C 0334 0062;0061 0337 0334 093C 0062;0061 0337 0334 093C 0062;0061 0337 0334 093C 0062;0061 0337 0334 093C 0062; # (a◌̷◌़◌̴b; a◌̷◌̴◌़b; a◌̷◌̴◌़b; a◌̷◌̴◌़b; a◌̷◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING SHORT SOLIDUS OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 093C 0334 0338 0062;0061 0334 0338 093C 0062;0061 0334 0338 093C 0062;0061 0334 0338 093C 0062;0061 0334 0338 093C 0062; # (a◌़◌̴◌̸b; a◌̴◌̸◌़b; a◌̴◌̸◌़b; a◌̴◌̸◌़b; a◌̴◌̸◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING LONG SOLIDUS OVERLAY, LATIN SMALL LETTER B +0061 0338 093C 0334 0062;0061 0338 0334 093C 0062;0061 0338 0334 093C 0062;0061 0338 0334 093C 0062;0061 0338 0334 093C 0062; # (a◌̸◌़◌̴b; a◌̸◌̴◌़b; a◌̸◌̴◌़b; a◌̸◌̴◌़b; a◌̸◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING LONG SOLIDUS OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 059A 0316 302A 0339 0062;0061 302A 0316 0339 059A 0062;0061 302A 0316 0339 059A 0062;0061 302A 0316 0339 059A 0062;0061 302A 0316 0339 059A 0062; # (a◌֚◌̖◌〪◌̹b; a◌〪◌̖◌̹◌֚b; a◌〪◌̖◌̹◌֚b; a◌〪◌̖◌̹◌֚b; a◌〪◌̖◌̹◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING RIGHT HALF RING BELOW, LATIN SMALL LETTER B +0061 0339 059A 0316 302A 0062;0061 302A 0339 0316 059A 0062;0061 302A 0339 0316 059A 0062;0061 302A 0339 0316 059A 0062;0061 302A 0339 0316 059A 0062; # (a◌̹◌֚◌̖◌〪b; a◌〪◌̹◌̖◌֚b; a◌〪◌̹◌̖◌֚b; a◌〪◌̹◌̖◌֚b; a◌〪◌̹◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING RIGHT HALF RING BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 033A 0062;0061 302A 0316 033A 059A 0062;0061 302A 0316 033A 059A 0062;0061 302A 0316 033A 059A 0062;0061 302A 0316 033A 059A 0062; # (a◌֚◌̖◌〪◌̺b; a◌〪◌̖◌̺◌֚b; a◌〪◌̖◌̺◌֚b; a◌〪◌̖◌̺◌֚b; a◌〪◌̖◌̺◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING INVERTED BRIDGE BELOW, LATIN SMALL LETTER B +0061 033A 059A 0316 302A 0062;0061 302A 033A 0316 059A 0062;0061 302A 033A 0316 059A 0062;0061 302A 033A 0316 059A 0062;0061 302A 033A 0316 059A 0062; # (a◌̺◌֚◌̖◌〪b; a◌〪◌̺◌̖◌֚b; a◌〪◌̺◌̖◌֚b; a◌〪◌̺◌̖◌֚b; a◌〪◌̺◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING INVERTED BRIDGE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 033B 0062;0061 302A 0316 033B 059A 0062;0061 302A 0316 033B 059A 0062;0061 302A 0316 033B 059A 0062;0061 302A 0316 033B 059A 0062; # (a◌֚◌̖◌〪◌̻b; a◌〪◌̖◌̻◌֚b; a◌〪◌̖◌̻◌֚b; a◌〪◌̖◌̻◌֚b; a◌〪◌̖◌̻◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING SQUARE BELOW, LATIN SMALL LETTER B +0061 033B 059A 0316 302A 0062;0061 302A 033B 0316 059A 0062;0061 302A 033B 0316 059A 0062;0061 302A 033B 0316 059A 0062;0061 302A 033B 0316 059A 0062; # (a◌̻◌֚◌̖◌〪b; a◌〪◌̻◌̖◌֚b; a◌〪◌̻◌̖◌֚b; a◌〪◌̻◌̖◌֚b; a◌〪◌̻◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING SQUARE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 033C 0062;0061 302A 0316 033C 059A 0062;0061 302A 0316 033C 059A 0062;0061 302A 0316 033C 059A 0062;0061 302A 0316 033C 059A 0062; # (a◌֚◌̖◌〪◌̼b; a◌〪◌̖◌̼◌֚b; a◌〪◌̖◌̼◌֚b; a◌〪◌̖◌̼◌֚b; a◌〪◌̖◌̼◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING SEAGULL BELOW, LATIN SMALL LETTER B +0061 033C 059A 0316 302A 0062;0061 302A 033C 0316 059A 0062;0061 302A 033C 0316 059A 0062;0061 302A 033C 0316 059A 0062;0061 302A 033C 0316 059A 0062; # (a◌̼◌֚◌̖◌〪b; a◌〪◌̼◌̖◌֚b; a◌〪◌̼◌̖◌֚b; a◌〪◌̼◌̖◌֚b; a◌〪◌̼◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING SEAGULL BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 033D 0062;00E0 05AE 033D 0315 0062;0061 05AE 0300 033D 0315 0062;00E0 05AE 033D 0315 0062;0061 05AE 0300 033D 0315 0062; # (a◌̕◌̀◌֮◌̽b; à◌֮◌̽◌̕b; a◌֮◌̀◌̽◌̕b; à◌֮◌̽◌̕b; a◌֮◌̀◌̽◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING X ABOVE, LATIN SMALL LETTER B +0061 033D 0315 0300 05AE 0062;0061 05AE 033D 0300 0315 0062;0061 05AE 033D 0300 0315 0062;0061 05AE 033D 0300 0315 0062;0061 05AE 033D 0300 0315 0062; # (a◌̽◌̕◌̀◌֮b; a◌֮◌̽◌̀◌̕b; a◌֮◌̽◌̀◌̕b; a◌֮◌̽◌̀◌̕b; a◌֮◌̽◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING X ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 033E 0062;00E0 05AE 033E 0315 0062;0061 05AE 0300 033E 0315 0062;00E0 05AE 033E 0315 0062;0061 05AE 0300 033E 0315 0062; # (a◌̕◌̀◌֮◌̾b; à◌֮◌̾◌̕b; a◌֮◌̀◌̾◌̕b; à◌֮◌̾◌̕b; a◌֮◌̀◌̾◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING VERTICAL TILDE, LATIN SMALL LETTER B +0061 033E 0315 0300 05AE 0062;0061 05AE 033E 0300 0315 0062;0061 05AE 033E 0300 0315 0062;0061 05AE 033E 0300 0315 0062;0061 05AE 033E 0300 0315 0062; # (a◌̾◌̕◌̀◌֮b; a◌֮◌̾◌̀◌̕b; a◌֮◌̾◌̀◌̕b; a◌֮◌̾◌̀◌̕b; a◌֮◌̾◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING VERTICAL TILDE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 033F 0062;00E0 05AE 033F 0315 0062;0061 05AE 0300 033F 0315 0062;00E0 05AE 033F 0315 0062;0061 05AE 0300 033F 0315 0062; # (a◌̕◌̀◌֮◌̿b; à◌֮◌̿◌̕b; a◌֮◌̀◌̿◌̕b; à◌֮◌̿◌̕b; a◌֮◌̀◌̿◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE OVERLINE, LATIN SMALL LETTER B +0061 033F 0315 0300 05AE 0062;0061 05AE 033F 0300 0315 0062;0061 05AE 033F 0300 0315 0062;0061 05AE 033F 0300 0315 0062;0061 05AE 033F 0300 0315 0062; # (a◌̿◌̕◌̀◌֮b; a◌֮◌̿◌̀◌̕b; a◌֮◌̿◌̀◌̕b; a◌֮◌̿◌̀◌̕b; a◌֮◌̿◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE OVERLINE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0340 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062; # (a◌̕◌̀◌֮◌̀b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GRAVE TONE MARK, LATIN SMALL LETTER B +0061 0340 0315 0300 05AE 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062; # (a◌̀◌̕◌̀◌֮b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GRAVE TONE MARK, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0341 0062;00E0 05AE 0301 0315 0062;0061 05AE 0300 0301 0315 0062;00E0 05AE 0301 0315 0062;0061 05AE 0300 0301 0315 0062; # (a◌̕◌̀◌֮◌́b; à◌֮◌́◌̕b; a◌֮◌̀◌́◌̕b; à◌֮◌́◌̕b; a◌֮◌̀◌́◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING ACUTE TONE MARK, LATIN SMALL LETTER B +0061 0341 0315 0300 05AE 0062;00E1 05AE 0300 0315 0062;0061 05AE 0301 0300 0315 0062;00E1 05AE 0300 0315 0062;0061 05AE 0301 0300 0315 0062; # (a◌́◌̕◌̀◌֮b; á◌֮◌̀◌̕b; a◌֮◌́◌̀◌̕b; á◌֮◌̀◌̕b; a◌֮◌́◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING ACUTE TONE MARK, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0342 0062;00E0 05AE 0342 0315 0062;0061 05AE 0300 0342 0315 0062;00E0 05AE 0342 0315 0062;0061 05AE 0300 0342 0315 0062; # (a◌̕◌̀◌֮◌͂b; à◌֮◌͂◌̕b; a◌֮◌̀◌͂◌̕b; à◌֮◌͂◌̕b; a◌֮◌̀◌͂◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GREEK PERISPOMENI, LATIN SMALL LETTER B +0061 0342 0315 0300 05AE 0062;0061 05AE 0342 0300 0315 0062;0061 05AE 0342 0300 0315 0062;0061 05AE 0342 0300 0315 0062;0061 05AE 0342 0300 0315 0062; # (a◌͂◌̕◌̀◌֮b; a◌֮◌͂◌̀◌̕b; a◌֮◌͂◌̀◌̕b; a◌֮◌͂◌̀◌̕b; a◌֮◌͂◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GREEK PERISPOMENI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0343 0062;00E0 05AE 0313 0315 0062;0061 05AE 0300 0313 0315 0062;00E0 05AE 0313 0315 0062;0061 05AE 0300 0313 0315 0062; # (a◌̕◌̀◌֮◌̓b; à◌֮◌̓◌̕b; a◌֮◌̀◌̓◌̕b; à◌֮◌̓◌̕b; a◌֮◌̀◌̓◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GREEK KORONIS, LATIN SMALL LETTER B +0061 0343 0315 0300 05AE 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062; # (a◌̓◌̕◌̀◌֮b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GREEK KORONIS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0344 0062;00E0 05AE 0308 0301 0315 0062;0061 05AE 0300 0308 0301 0315 0062;00E0 05AE 0308 0301 0315 0062;0061 05AE 0300 0308 0301 0315 0062; # (a◌̕◌̀◌֮◌̈́b; à◌֮◌̈◌́◌̕b; a◌֮◌̀◌̈◌́◌̕b; à◌֮◌̈◌́◌̕b; a◌֮◌̀◌̈◌́◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GREEK DIALYTIKA TONOS, LATIN SMALL LETTER B +0061 0344 0315 0300 05AE 0062;00E4 05AE 0301 0300 0315 0062;0061 05AE 0308 0301 0300 0315 0062;00E4 05AE 0301 0300 0315 0062;0061 05AE 0308 0301 0300 0315 0062; # (a◌̈́◌̕◌̀◌֮b; ä◌֮◌́◌̀◌̕b; a◌֮◌̈◌́◌̀◌̕b; ä◌֮◌́◌̀◌̕b; a◌֮◌̈◌́◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GREEK DIALYTIKA TONOS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0345 0360 0345 0062;0061 0360 0345 0345 0062;0061 0360 0345 0345 0062;0061 0360 0345 0345 0062;0061 0360 0345 0345 0062; # (a◌ͅ◌͠◌ͅb; a◌͠◌ͅ◌ͅb; a◌͠◌ͅ◌ͅb; a◌͠◌ͅ◌ͅb; a◌͠◌ͅ◌ͅb; ) LATIN SMALL LETTER A, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE TILDE, COMBINING GREEK YPOGEGRAMMENI, LATIN SMALL LETTER B +0061 0345 0345 0360 0062;0061 0360 0345 0345 0062;0061 0360 0345 0345 0062;0061 0360 0345 0345 0062;0061 0360 0345 0345 0062; # (a◌ͅ◌ͅ◌͠b; a◌͠◌ͅ◌ͅb; a◌͠◌ͅ◌ͅb; a◌͠◌ͅ◌ͅb; a◌͠◌ͅ◌ͅb; ) LATIN SMALL LETTER A, COMBINING GREEK YPOGEGRAMMENI, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE TILDE, LATIN SMALL LETTER B +0061 0315 0300 05AE 0346 0062;00E0 05AE 0346 0315 0062;0061 05AE 0300 0346 0315 0062;00E0 05AE 0346 0315 0062;0061 05AE 0300 0346 0315 0062; # (a◌̕◌̀◌֮◌͆b; à◌֮◌͆◌̕b; a◌֮◌̀◌͆◌̕b; à◌֮◌͆◌̕b; a◌֮◌̀◌͆◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING BRIDGE ABOVE, LATIN SMALL LETTER B +0061 0346 0315 0300 05AE 0062;0061 05AE 0346 0300 0315 0062;0061 05AE 0346 0300 0315 0062;0061 05AE 0346 0300 0315 0062;0061 05AE 0346 0300 0315 0062; # (a◌͆◌̕◌̀◌֮b; a◌֮◌͆◌̀◌̕b; a◌֮◌͆◌̀◌̕b; a◌֮◌͆◌̀◌̕b; a◌֮◌͆◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING BRIDGE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0347 0062;0061 302A 0316 0347 059A 0062;0061 302A 0316 0347 059A 0062;0061 302A 0316 0347 059A 0062;0061 302A 0316 0347 059A 0062; # (a◌֚◌̖◌〪◌͇b; a◌〪◌̖◌͇◌֚b; a◌〪◌̖◌͇◌֚b; a◌〪◌̖◌͇◌֚b; a◌〪◌̖◌͇◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING EQUALS SIGN BELOW, LATIN SMALL LETTER B +0061 0347 059A 0316 302A 0062;0061 302A 0347 0316 059A 0062;0061 302A 0347 0316 059A 0062;0061 302A 0347 0316 059A 0062;0061 302A 0347 0316 059A 0062; # (a◌͇◌֚◌̖◌〪b; a◌〪◌͇◌̖◌֚b; a◌〪◌͇◌̖◌֚b; a◌〪◌͇◌̖◌֚b; a◌〪◌͇◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING EQUALS SIGN BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0348 0062;0061 302A 0316 0348 059A 0062;0061 302A 0316 0348 059A 0062;0061 302A 0316 0348 059A 0062;0061 302A 0316 0348 059A 0062; # (a◌֚◌̖◌〪◌͈b; a◌〪◌̖◌͈◌֚b; a◌〪◌̖◌͈◌֚b; a◌〪◌̖◌͈◌֚b; a◌〪◌̖◌͈◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING DOUBLE VERTICAL LINE BELOW, LATIN SMALL LETTER B +0061 0348 059A 0316 302A 0062;0061 302A 0348 0316 059A 0062;0061 302A 0348 0316 059A 0062;0061 302A 0348 0316 059A 0062;0061 302A 0348 0316 059A 0062; # (a◌͈◌֚◌̖◌〪b; a◌〪◌͈◌̖◌֚b; a◌〪◌͈◌̖◌֚b; a◌〪◌͈◌̖◌֚b; a◌〪◌͈◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING DOUBLE VERTICAL LINE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0349 0062;0061 302A 0316 0349 059A 0062;0061 302A 0316 0349 059A 0062;0061 302A 0316 0349 059A 0062;0061 302A 0316 0349 059A 0062; # (a◌֚◌̖◌〪◌͉b; a◌〪◌̖◌͉◌֚b; a◌〪◌̖◌͉◌֚b; a◌〪◌̖◌͉◌֚b; a◌〪◌̖◌͉◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LEFT ANGLE BELOW, LATIN SMALL LETTER B +0061 0349 059A 0316 302A 0062;0061 302A 0349 0316 059A 0062;0061 302A 0349 0316 059A 0062;0061 302A 0349 0316 059A 0062;0061 302A 0349 0316 059A 0062; # (a◌͉◌֚◌̖◌〪b; a◌〪◌͉◌̖◌֚b; a◌〪◌͉◌̖◌֚b; a◌〪◌͉◌̖◌֚b; a◌〪◌͉◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LEFT ANGLE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 034A 0062;00E0 05AE 034A 0315 0062;0061 05AE 0300 034A 0315 0062;00E0 05AE 034A 0315 0062;0061 05AE 0300 034A 0315 0062; # (a◌̕◌̀◌֮◌͊b; à◌֮◌͊◌̕b; a◌֮◌̀◌͊◌̕b; à◌֮◌͊◌̕b; a◌֮◌̀◌͊◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING NOT TILDE ABOVE, LATIN SMALL LETTER B +0061 034A 0315 0300 05AE 0062;0061 05AE 034A 0300 0315 0062;0061 05AE 034A 0300 0315 0062;0061 05AE 034A 0300 0315 0062;0061 05AE 034A 0300 0315 0062; # (a◌͊◌̕◌̀◌֮b; a◌֮◌͊◌̀◌̕b; a◌֮◌͊◌̀◌̕b; a◌֮◌͊◌̀◌̕b; a◌֮◌͊◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING NOT TILDE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 034B 0062;00E0 05AE 034B 0315 0062;0061 05AE 0300 034B 0315 0062;00E0 05AE 034B 0315 0062;0061 05AE 0300 034B 0315 0062; # (a◌̕◌̀◌֮◌͋b; à◌֮◌͋◌̕b; a◌֮◌̀◌͋◌̕b; à◌֮◌͋◌̕b; a◌֮◌̀◌͋◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING HOMOTHETIC ABOVE, LATIN SMALL LETTER B +0061 034B 0315 0300 05AE 0062;0061 05AE 034B 0300 0315 0062;0061 05AE 034B 0300 0315 0062;0061 05AE 034B 0300 0315 0062;0061 05AE 034B 0300 0315 0062; # (a◌͋◌̕◌̀◌֮b; a◌֮◌͋◌̀◌̕b; a◌֮◌͋◌̀◌̕b; a◌֮◌͋◌̀◌̕b; a◌֮◌͋◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING HOMOTHETIC ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 034C 0062;00E0 05AE 034C 0315 0062;0061 05AE 0300 034C 0315 0062;00E0 05AE 034C 0315 0062;0061 05AE 0300 034C 0315 0062; # (a◌̕◌̀◌֮◌͌b; à◌֮◌͌◌̕b; a◌֮◌̀◌͌◌̕b; à◌֮◌͌◌̕b; a◌֮◌̀◌͌◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING ALMOST EQUAL TO ABOVE, LATIN SMALL LETTER B +0061 034C 0315 0300 05AE 0062;0061 05AE 034C 0300 0315 0062;0061 05AE 034C 0300 0315 0062;0061 05AE 034C 0300 0315 0062;0061 05AE 034C 0300 0315 0062; # (a◌͌◌̕◌̀◌֮b; a◌֮◌͌◌̀◌̕b; a◌֮◌͌◌̀◌̕b; a◌֮◌͌◌̀◌̕b; a◌֮◌͌◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING ALMOST EQUAL TO ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 034D 0062;0061 302A 0316 034D 059A 0062;0061 302A 0316 034D 059A 0062;0061 302A 0316 034D 059A 0062;0061 302A 0316 034D 059A 0062; # (a◌֚◌̖◌〪◌͍b; a◌〪◌̖◌͍◌֚b; a◌〪◌̖◌͍◌֚b; a◌〪◌̖◌͍◌֚b; a◌〪◌̖◌͍◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LEFT RIGHT ARROW BELOW, LATIN SMALL LETTER B +0061 034D 059A 0316 302A 0062;0061 302A 034D 0316 059A 0062;0061 302A 034D 0316 059A 0062;0061 302A 034D 0316 059A 0062;0061 302A 034D 0316 059A 0062; # (a◌͍◌֚◌̖◌〪b; a◌〪◌͍◌̖◌֚b; a◌〪◌͍◌̖◌֚b; a◌〪◌͍◌̖◌֚b; a◌〪◌͍◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LEFT RIGHT ARROW BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 034E 0062;0061 302A 0316 034E 059A 0062;0061 302A 0316 034E 059A 0062;0061 302A 0316 034E 059A 0062;0061 302A 0316 034E 059A 0062; # (a◌֚◌̖◌〪◌͎b; a◌〪◌̖◌͎◌֚b; a◌〪◌̖◌͎◌֚b; a◌〪◌̖◌͎◌֚b; a◌〪◌̖◌͎◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING UPWARDS ARROW BELOW, LATIN SMALL LETTER B +0061 034E 059A 0316 302A 0062;0061 302A 034E 0316 059A 0062;0061 302A 034E 0316 059A 0062;0061 302A 034E 0316 059A 0062;0061 302A 034E 0316 059A 0062; # (a◌͎◌֚◌̖◌〪b; a◌〪◌͎◌̖◌֚b; a◌〪◌͎◌̖◌֚b; a◌〪◌͎◌̖◌֚b; a◌〪◌͎◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING UPWARDS ARROW BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0345 0360 0362 0360 0062;0061 0362 0360 0360 0345 0062;0061 0362 0360 0360 0345 0062;0061 0362 0360 0360 0345 0062;0061 0362 0360 0360 0345 0062; # (a◌ͅ◌͠◌͢◌͠b; a◌͢◌͠◌͠◌ͅb; a◌͢◌͠◌͠◌ͅb; a◌͢◌͠◌͠◌ͅb; a◌͢◌͠◌͠◌ͅb; ) LATIN SMALL LETTER A, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE TILDE, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING DOUBLE TILDE, LATIN SMALL LETTER B +0061 0360 0345 0360 0362 0062;0061 0362 0360 0360 0345 0062;0061 0362 0360 0360 0345 0062;0061 0362 0360 0360 0345 0062;0061 0362 0360 0360 0345 0062; # (a◌͠◌ͅ◌͠◌͢b; a◌͢◌͠◌͠◌ͅb; a◌͢◌͠◌͠◌ͅb; a◌͢◌͠◌͠◌ͅb; a◌͢◌͠◌͠◌ͅb; ) LATIN SMALL LETTER A, COMBINING DOUBLE TILDE, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE TILDE, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, LATIN SMALL LETTER B +0061 0345 0360 0362 0361 0062;0061 0362 0360 0361 0345 0062;0061 0362 0360 0361 0345 0062;0061 0362 0360 0361 0345 0062;0061 0362 0360 0361 0345 0062; # (a◌ͅ◌͠◌͢◌͡b; a◌͢◌͠◌͡◌ͅb; a◌͢◌͠◌͡◌ͅb; a◌͢◌͠◌͡◌ͅb; a◌͢◌͠◌͡◌ͅb; ) LATIN SMALL LETTER A, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE TILDE, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING DOUBLE INVERTED BREVE, LATIN SMALL LETTER B +0061 0361 0345 0360 0362 0062;0061 0362 0361 0360 0345 0062;0061 0362 0361 0360 0345 0062;0061 0362 0361 0360 0345 0062;0061 0362 0361 0360 0345 0062; # (a◌͡◌ͅ◌͠◌͢b; a◌͢◌͡◌͠◌ͅb; a◌͢◌͡◌͠◌ͅb; a◌͢◌͡◌͠◌ͅb; a◌͢◌͡◌͠◌ͅb; ) LATIN SMALL LETTER A, COMBINING DOUBLE INVERTED BREVE, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE TILDE, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, LATIN SMALL LETTER B +0061 0360 0362 0315 0362 0062;0061 0315 0362 0362 0360 0062;0061 0315 0362 0362 0360 0062;0061 0315 0362 0362 0360 0062;0061 0315 0362 0362 0360 0062; # (a◌͠◌͢◌̕◌͢b; a◌̕◌͢◌͢◌͠b; a◌̕◌͢◌͢◌͠b; a◌̕◌͢◌͢◌͠b; a◌̕◌͢◌͢◌͠b; ) LATIN SMALL LETTER A, COMBINING DOUBLE TILDE, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, LATIN SMALL LETTER B +0061 0362 0360 0362 0315 0062;0061 0315 0362 0362 0360 0062;0061 0315 0362 0362 0360 0062;0061 0315 0362 0362 0360 0062;0061 0315 0362 0362 0360 0062; # (a◌͢◌͠◌͢◌̕b; a◌̕◌͢◌͢◌͠b; a◌̕◌͢◌͢◌͠b; a◌̕◌͢◌͢◌͠b; a◌̕◌͢◌͢◌͠b; ) LATIN SMALL LETTER A, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING DOUBLE TILDE, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING COMMA ABOVE RIGHT, LATIN SMALL LETTER B +0061 0315 0300 05AE 0363 0062;00E0 05AE 0363 0315 0062;0061 05AE 0300 0363 0315 0062;00E0 05AE 0363 0315 0062;0061 05AE 0300 0363 0315 0062; # (a◌̕◌̀◌֮◌ͣb; à◌֮◌ͣ◌̕b; a◌֮◌̀◌ͣ◌̕b; à◌֮◌ͣ◌̕b; a◌֮◌̀◌ͣ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER A, LATIN SMALL LETTER B +0061 0363 0315 0300 05AE 0062;0061 05AE 0363 0300 0315 0062;0061 05AE 0363 0300 0315 0062;0061 05AE 0363 0300 0315 0062;0061 05AE 0363 0300 0315 0062; # (a◌ͣ◌̕◌̀◌֮b; a◌֮◌ͣ◌̀◌̕b; a◌֮◌ͣ◌̀◌̕b; a◌֮◌ͣ◌̀◌̕b; a◌֮◌ͣ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0364 0062;00E0 05AE 0364 0315 0062;0061 05AE 0300 0364 0315 0062;00E0 05AE 0364 0315 0062;0061 05AE 0300 0364 0315 0062; # (a◌̕◌̀◌֮◌ͤb; à◌֮◌ͤ◌̕b; a◌֮◌̀◌ͤ◌̕b; à◌֮◌ͤ◌̕b; a◌֮◌̀◌ͤ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER E, LATIN SMALL LETTER B +0061 0364 0315 0300 05AE 0062;0061 05AE 0364 0300 0315 0062;0061 05AE 0364 0300 0315 0062;0061 05AE 0364 0300 0315 0062;0061 05AE 0364 0300 0315 0062; # (a◌ͤ◌̕◌̀◌֮b; a◌֮◌ͤ◌̀◌̕b; a◌֮◌ͤ◌̀◌̕b; a◌֮◌ͤ◌̀◌̕b; a◌֮◌ͤ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER E, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0365 0062;00E0 05AE 0365 0315 0062;0061 05AE 0300 0365 0315 0062;00E0 05AE 0365 0315 0062;0061 05AE 0300 0365 0315 0062; # (a◌̕◌̀◌֮◌ͥb; à◌֮◌ͥ◌̕b; a◌֮◌̀◌ͥ◌̕b; à◌֮◌ͥ◌̕b; a◌֮◌̀◌ͥ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER I, LATIN SMALL LETTER B +0061 0365 0315 0300 05AE 0062;0061 05AE 0365 0300 0315 0062;0061 05AE 0365 0300 0315 0062;0061 05AE 0365 0300 0315 0062;0061 05AE 0365 0300 0315 0062; # (a◌ͥ◌̕◌̀◌֮b; a◌֮◌ͥ◌̀◌̕b; a◌֮◌ͥ◌̀◌̕b; a◌֮◌ͥ◌̀◌̕b; a◌֮◌ͥ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER I, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0366 0062;00E0 05AE 0366 0315 0062;0061 05AE 0300 0366 0315 0062;00E0 05AE 0366 0315 0062;0061 05AE 0300 0366 0315 0062; # (a◌̕◌̀◌֮◌ͦb; à◌֮◌ͦ◌̕b; a◌֮◌̀◌ͦ◌̕b; à◌֮◌ͦ◌̕b; a◌֮◌̀◌ͦ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER O, LATIN SMALL LETTER B +0061 0366 0315 0300 05AE 0062;0061 05AE 0366 0300 0315 0062;0061 05AE 0366 0300 0315 0062;0061 05AE 0366 0300 0315 0062;0061 05AE 0366 0300 0315 0062; # (a◌ͦ◌̕◌̀◌֮b; a◌֮◌ͦ◌̀◌̕b; a◌֮◌ͦ◌̀◌̕b; a◌֮◌ͦ◌̀◌̕b; a◌֮◌ͦ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER O, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0367 0062;00E0 05AE 0367 0315 0062;0061 05AE 0300 0367 0315 0062;00E0 05AE 0367 0315 0062;0061 05AE 0300 0367 0315 0062; # (a◌̕◌̀◌֮◌ͧb; à◌֮◌ͧ◌̕b; a◌֮◌̀◌ͧ◌̕b; à◌֮◌ͧ◌̕b; a◌֮◌̀◌ͧ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER U, LATIN SMALL LETTER B +0061 0367 0315 0300 05AE 0062;0061 05AE 0367 0300 0315 0062;0061 05AE 0367 0300 0315 0062;0061 05AE 0367 0300 0315 0062;0061 05AE 0367 0300 0315 0062; # (a◌ͧ◌̕◌̀◌֮b; a◌֮◌ͧ◌̀◌̕b; a◌֮◌ͧ◌̀◌̕b; a◌֮◌ͧ◌̀◌̕b; a◌֮◌ͧ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER U, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0368 0062;00E0 05AE 0368 0315 0062;0061 05AE 0300 0368 0315 0062;00E0 05AE 0368 0315 0062;0061 05AE 0300 0368 0315 0062; # (a◌̕◌̀◌֮◌ͨb; à◌֮◌ͨ◌̕b; a◌֮◌̀◌ͨ◌̕b; à◌֮◌ͨ◌̕b; a◌֮◌̀◌ͨ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER C, LATIN SMALL LETTER B +0061 0368 0315 0300 05AE 0062;0061 05AE 0368 0300 0315 0062;0061 05AE 0368 0300 0315 0062;0061 05AE 0368 0300 0315 0062;0061 05AE 0368 0300 0315 0062; # (a◌ͨ◌̕◌̀◌֮b; a◌֮◌ͨ◌̀◌̕b; a◌֮◌ͨ◌̀◌̕b; a◌֮◌ͨ◌̀◌̕b; a◌֮◌ͨ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER C, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0369 0062;00E0 05AE 0369 0315 0062;0061 05AE 0300 0369 0315 0062;00E0 05AE 0369 0315 0062;0061 05AE 0300 0369 0315 0062; # (a◌̕◌̀◌֮◌ͩb; à◌֮◌ͩ◌̕b; a◌֮◌̀◌ͩ◌̕b; à◌֮◌ͩ◌̕b; a◌֮◌̀◌ͩ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER D, LATIN SMALL LETTER B +0061 0369 0315 0300 05AE 0062;0061 05AE 0369 0300 0315 0062;0061 05AE 0369 0300 0315 0062;0061 05AE 0369 0300 0315 0062;0061 05AE 0369 0300 0315 0062; # (a◌ͩ◌̕◌̀◌֮b; a◌֮◌ͩ◌̀◌̕b; a◌֮◌ͩ◌̀◌̕b; a◌֮◌ͩ◌̀◌̕b; a◌֮◌ͩ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER D, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 036A 0062;00E0 05AE 036A 0315 0062;0061 05AE 0300 036A 0315 0062;00E0 05AE 036A 0315 0062;0061 05AE 0300 036A 0315 0062; # (a◌̕◌̀◌֮◌ͪb; à◌֮◌ͪ◌̕b; a◌֮◌̀◌ͪ◌̕b; à◌֮◌ͪ◌̕b; a◌֮◌̀◌ͪ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER H, LATIN SMALL LETTER B +0061 036A 0315 0300 05AE 0062;0061 05AE 036A 0300 0315 0062;0061 05AE 036A 0300 0315 0062;0061 05AE 036A 0300 0315 0062;0061 05AE 036A 0300 0315 0062; # (a◌ͪ◌̕◌̀◌֮b; a◌֮◌ͪ◌̀◌̕b; a◌֮◌ͪ◌̀◌̕b; a◌֮◌ͪ◌̀◌̕b; a◌֮◌ͪ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER H, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 036B 0062;00E0 05AE 036B 0315 0062;0061 05AE 0300 036B 0315 0062;00E0 05AE 036B 0315 0062;0061 05AE 0300 036B 0315 0062; # (a◌̕◌̀◌֮◌ͫb; à◌֮◌ͫ◌̕b; a◌֮◌̀◌ͫ◌̕b; à◌֮◌ͫ◌̕b; a◌֮◌̀◌ͫ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER M, LATIN SMALL LETTER B +0061 036B 0315 0300 05AE 0062;0061 05AE 036B 0300 0315 0062;0061 05AE 036B 0300 0315 0062;0061 05AE 036B 0300 0315 0062;0061 05AE 036B 0300 0315 0062; # (a◌ͫ◌̕◌̀◌֮b; a◌֮◌ͫ◌̀◌̕b; a◌֮◌ͫ◌̀◌̕b; a◌֮◌ͫ◌̀◌̕b; a◌֮◌ͫ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER M, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 036C 0062;00E0 05AE 036C 0315 0062;0061 05AE 0300 036C 0315 0062;00E0 05AE 036C 0315 0062;0061 05AE 0300 036C 0315 0062; # (a◌̕◌̀◌֮◌ͬb; à◌֮◌ͬ◌̕b; a◌֮◌̀◌ͬ◌̕b; à◌֮◌ͬ◌̕b; a◌֮◌̀◌ͬ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER R, LATIN SMALL LETTER B +0061 036C 0315 0300 05AE 0062;0061 05AE 036C 0300 0315 0062;0061 05AE 036C 0300 0315 0062;0061 05AE 036C 0300 0315 0062;0061 05AE 036C 0300 0315 0062; # (a◌ͬ◌̕◌̀◌֮b; a◌֮◌ͬ◌̀◌̕b; a◌֮◌ͬ◌̀◌̕b; a◌֮◌ͬ◌̀◌̕b; a◌֮◌ͬ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER R, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 036D 0062;00E0 05AE 036D 0315 0062;0061 05AE 0300 036D 0315 0062;00E0 05AE 036D 0315 0062;0061 05AE 0300 036D 0315 0062; # (a◌̕◌̀◌֮◌ͭb; à◌֮◌ͭ◌̕b; a◌֮◌̀◌ͭ◌̕b; à◌֮◌ͭ◌̕b; a◌֮◌̀◌ͭ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER T, LATIN SMALL LETTER B +0061 036D 0315 0300 05AE 0062;0061 05AE 036D 0300 0315 0062;0061 05AE 036D 0300 0315 0062;0061 05AE 036D 0300 0315 0062;0061 05AE 036D 0300 0315 0062; # (a◌ͭ◌̕◌̀◌֮b; a◌֮◌ͭ◌̀◌̕b; a◌֮◌ͭ◌̀◌̕b; a◌֮◌ͭ◌̀◌̕b; a◌֮◌ͭ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER T, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 036E 0062;00E0 05AE 036E 0315 0062;0061 05AE 0300 036E 0315 0062;00E0 05AE 036E 0315 0062;0061 05AE 0300 036E 0315 0062; # (a◌̕◌̀◌֮◌ͮb; à◌֮◌ͮ◌̕b; a◌֮◌̀◌ͮ◌̕b; à◌֮◌ͮ◌̕b; a◌֮◌̀◌ͮ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER V, LATIN SMALL LETTER B +0061 036E 0315 0300 05AE 0062;0061 05AE 036E 0300 0315 0062;0061 05AE 036E 0300 0315 0062;0061 05AE 036E 0300 0315 0062;0061 05AE 036E 0300 0315 0062; # (a◌ͮ◌̕◌̀◌֮b; a◌֮◌ͮ◌̀◌̕b; a◌֮◌ͮ◌̀◌̕b; a◌֮◌ͮ◌̀◌̕b; a◌֮◌ͮ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER V, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 036F 0062;00E0 05AE 036F 0315 0062;0061 05AE 0300 036F 0315 0062;00E0 05AE 036F 0315 0062;0061 05AE 0300 036F 0315 0062; # (a◌̕◌̀◌֮◌ͯb; à◌֮◌ͯ◌̕b; a◌֮◌̀◌ͯ◌̕b; à◌֮◌ͯ◌̕b; a◌֮◌̀◌ͯ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER X, LATIN SMALL LETTER B +0061 036F 0315 0300 05AE 0062;0061 05AE 036F 0300 0315 0062;0061 05AE 036F 0300 0315 0062;0061 05AE 036F 0300 0315 0062;0061 05AE 036F 0300 0315 0062; # (a◌ͯ◌̕◌̀◌֮b; a◌֮◌ͯ◌̀◌̕b; a◌֮◌ͯ◌̀◌̕b; a◌֮◌ͯ◌̀◌̕b; a◌֮◌ͯ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER X, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0483 0062;00E0 05AE 0483 0315 0062;0061 05AE 0300 0483 0315 0062;00E0 05AE 0483 0315 0062;0061 05AE 0300 0483 0315 0062; # (a◌̕◌̀◌֮◌҃b; à◌֮◌҃◌̕b; a◌֮◌̀◌҃◌̕b; à◌֮◌҃◌̕b; a◌֮◌̀◌҃◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CYRILLIC TITLO, LATIN SMALL LETTER B +0061 0483 0315 0300 05AE 0062;0061 05AE 0483 0300 0315 0062;0061 05AE 0483 0300 0315 0062;0061 05AE 0483 0300 0315 0062;0061 05AE 0483 0300 0315 0062; # (a◌҃◌̕◌̀◌֮b; a◌֮◌҃◌̀◌̕b; a◌֮◌҃◌̀◌̕b; a◌֮◌҃◌̀◌̕b; a◌֮◌҃◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CYRILLIC TITLO, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0484 0062;00E0 05AE 0484 0315 0062;0061 05AE 0300 0484 0315 0062;00E0 05AE 0484 0315 0062;0061 05AE 0300 0484 0315 0062; # (a◌̕◌̀◌֮◌҄b; à◌֮◌҄◌̕b; a◌֮◌̀◌҄◌̕b; à◌֮◌҄◌̕b; a◌֮◌̀◌҄◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CYRILLIC PALATALIZATION, LATIN SMALL LETTER B +0061 0484 0315 0300 05AE 0062;0061 05AE 0484 0300 0315 0062;0061 05AE 0484 0300 0315 0062;0061 05AE 0484 0300 0315 0062;0061 05AE 0484 0300 0315 0062; # (a◌҄◌̕◌̀◌֮b; a◌֮◌҄◌̀◌̕b; a◌֮◌҄◌̀◌̕b; a◌֮◌҄◌̀◌̕b; a◌֮◌҄◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CYRILLIC PALATALIZATION, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0485 0062;00E0 05AE 0485 0315 0062;0061 05AE 0300 0485 0315 0062;00E0 05AE 0485 0315 0062;0061 05AE 0300 0485 0315 0062; # (a◌̕◌̀◌֮◌҅b; à◌֮◌҅◌̕b; a◌֮◌̀◌҅◌̕b; à◌֮◌҅◌̕b; a◌֮◌̀◌҅◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CYRILLIC DASIA PNEUMATA, LATIN SMALL LETTER B +0061 0485 0315 0300 05AE 0062;0061 05AE 0485 0300 0315 0062;0061 05AE 0485 0300 0315 0062;0061 05AE 0485 0300 0315 0062;0061 05AE 0485 0300 0315 0062; # (a◌҅◌̕◌̀◌֮b; a◌֮◌҅◌̀◌̕b; a◌֮◌҅◌̀◌̕b; a◌֮◌҅◌̀◌̕b; a◌֮◌҅◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CYRILLIC DASIA PNEUMATA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0486 0062;00E0 05AE 0486 0315 0062;0061 05AE 0300 0486 0315 0062;00E0 05AE 0486 0315 0062;0061 05AE 0300 0486 0315 0062; # (a◌̕◌̀◌֮◌҆b; à◌֮◌҆◌̕b; a◌֮◌̀◌҆◌̕b; à◌֮◌҆◌̕b; a◌֮◌̀◌҆◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CYRILLIC PSILI PNEUMATA, LATIN SMALL LETTER B +0061 0486 0315 0300 05AE 0062;0061 05AE 0486 0300 0315 0062;0061 05AE 0486 0300 0315 0062;0061 05AE 0486 0300 0315 0062;0061 05AE 0486 0300 0315 0062; # (a◌҆◌̕◌̀◌֮b; a◌֮◌҆◌̀◌̕b; a◌֮◌҆◌̀◌̕b; a◌֮◌҆◌̀◌̕b; a◌֮◌҆◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CYRILLIC PSILI PNEUMATA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0591 0062;0061 302A 0316 0591 059A 0062;0061 302A 0316 0591 059A 0062;0061 302A 0316 0591 059A 0062;0061 302A 0316 0591 059A 0062; # (a◌֚◌̖◌〪◌֑b; a◌〪◌̖◌֑◌֚b; a◌〪◌̖◌֑◌֚b; a◌〪◌̖◌֑◌֚b; a◌〪◌̖◌֑◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT ETNAHTA, LATIN SMALL LETTER B +0061 0591 059A 0316 302A 0062;0061 302A 0591 0316 059A 0062;0061 302A 0591 0316 059A 0062;0061 302A 0591 0316 059A 0062;0061 302A 0591 0316 059A 0062; # (a◌֑◌֚◌̖◌〪b; a◌〪◌֑◌̖◌֚b; a◌〪◌֑◌̖◌֚b; a◌〪◌֑◌̖◌֚b; a◌〪◌֑◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT ETNAHTA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0592 0062;00E0 05AE 0592 0315 0062;0061 05AE 0300 0592 0315 0062;00E0 05AE 0592 0315 0062;0061 05AE 0300 0592 0315 0062; # (a◌̕◌̀◌֮◌֒b; à◌֮◌֒◌̕b; a◌֮◌̀◌֒◌̕b; à◌֮◌֒◌̕b; a◌֮◌̀◌֒◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT SEGOL, LATIN SMALL LETTER B +0061 0592 0315 0300 05AE 0062;0061 05AE 0592 0300 0315 0062;0061 05AE 0592 0300 0315 0062;0061 05AE 0592 0300 0315 0062;0061 05AE 0592 0300 0315 0062; # (a◌֒◌̕◌̀◌֮b; a◌֮◌֒◌̀◌̕b; a◌֮◌֒◌̀◌̕b; a◌֮◌֒◌̀◌̕b; a◌֮◌֒◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT SEGOL, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0593 0062;00E0 05AE 0593 0315 0062;0061 05AE 0300 0593 0315 0062;00E0 05AE 0593 0315 0062;0061 05AE 0300 0593 0315 0062; # (a◌̕◌̀◌֮◌֓b; à◌֮◌֓◌̕b; a◌֮◌̀◌֓◌̕b; à◌֮◌֓◌̕b; a◌֮◌̀◌֓◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT SHALSHELET, LATIN SMALL LETTER B +0061 0593 0315 0300 05AE 0062;0061 05AE 0593 0300 0315 0062;0061 05AE 0593 0300 0315 0062;0061 05AE 0593 0300 0315 0062;0061 05AE 0593 0300 0315 0062; # (a◌֓◌̕◌̀◌֮b; a◌֮◌֓◌̀◌̕b; a◌֮◌֓◌̀◌̕b; a◌֮◌֓◌̀◌̕b; a◌֮◌֓◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT SHALSHELET, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0594 0062;00E0 05AE 0594 0315 0062;0061 05AE 0300 0594 0315 0062;00E0 05AE 0594 0315 0062;0061 05AE 0300 0594 0315 0062; # (a◌̕◌̀◌֮◌֔b; à◌֮◌֔◌̕b; a◌֮◌̀◌֔◌̕b; à◌֮◌֔◌̕b; a◌֮◌̀◌֔◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT ZAQEF QATAN, LATIN SMALL LETTER B +0061 0594 0315 0300 05AE 0062;0061 05AE 0594 0300 0315 0062;0061 05AE 0594 0300 0315 0062;0061 05AE 0594 0300 0315 0062;0061 05AE 0594 0300 0315 0062; # (a◌֔◌̕◌̀◌֮b; a◌֮◌֔◌̀◌̕b; a◌֮◌֔◌̀◌̕b; a◌֮◌֔◌̀◌̕b; a◌֮◌֔◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT ZAQEF QATAN, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0595 0062;00E0 05AE 0595 0315 0062;0061 05AE 0300 0595 0315 0062;00E0 05AE 0595 0315 0062;0061 05AE 0300 0595 0315 0062; # (a◌̕◌̀◌֮◌֕b; à◌֮◌֕◌̕b; a◌֮◌̀◌֕◌̕b; à◌֮◌֕◌̕b; a◌֮◌̀◌֕◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT ZAQEF GADOL, LATIN SMALL LETTER B +0061 0595 0315 0300 05AE 0062;0061 05AE 0595 0300 0315 0062;0061 05AE 0595 0300 0315 0062;0061 05AE 0595 0300 0315 0062;0061 05AE 0595 0300 0315 0062; # (a◌֕◌̕◌̀◌֮b; a◌֮◌֕◌̀◌̕b; a◌֮◌֕◌̀◌̕b; a◌֮◌֕◌̀◌̕b; a◌֮◌֕◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT ZAQEF GADOL, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0596 0062;0061 302A 0316 0596 059A 0062;0061 302A 0316 0596 059A 0062;0061 302A 0316 0596 059A 0062;0061 302A 0316 0596 059A 0062; # (a◌֚◌̖◌〪◌֖b; a◌〪◌̖◌֖◌֚b; a◌〪◌̖◌֖◌֚b; a◌〪◌̖◌֖◌֚b; a◌〪◌̖◌֖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT TIPEHA, LATIN SMALL LETTER B +0061 0596 059A 0316 302A 0062;0061 302A 0596 0316 059A 0062;0061 302A 0596 0316 059A 0062;0061 302A 0596 0316 059A 0062;0061 302A 0596 0316 059A 0062; # (a◌֖◌֚◌̖◌〪b; a◌〪◌֖◌̖◌֚b; a◌〪◌֖◌̖◌֚b; a◌〪◌֖◌̖◌֚b; a◌〪◌֖◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT TIPEHA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0597 0062;00E0 05AE 0597 0315 0062;0061 05AE 0300 0597 0315 0062;00E0 05AE 0597 0315 0062;0061 05AE 0300 0597 0315 0062; # (a◌̕◌̀◌֮◌֗b; à◌֮◌֗◌̕b; a◌֮◌̀◌֗◌̕b; à◌֮◌֗◌̕b; a◌֮◌̀◌֗◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT REVIA, LATIN SMALL LETTER B +0061 0597 0315 0300 05AE 0062;0061 05AE 0597 0300 0315 0062;0061 05AE 0597 0300 0315 0062;0061 05AE 0597 0300 0315 0062;0061 05AE 0597 0300 0315 0062; # (a◌֗◌̕◌̀◌֮b; a◌֮◌֗◌̀◌̕b; a◌֮◌֗◌̀◌̕b; a◌֮◌֗◌̀◌̕b; a◌֮◌֗◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT REVIA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0598 0062;00E0 05AE 0598 0315 0062;0061 05AE 0300 0598 0315 0062;00E0 05AE 0598 0315 0062;0061 05AE 0300 0598 0315 0062; # (a◌̕◌̀◌֮◌֘b; à◌֮◌֘◌̕b; a◌֮◌̀◌֘◌̕b; à◌֮◌֘◌̕b; a◌֮◌̀◌֘◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT ZARQA, LATIN SMALL LETTER B +0061 0598 0315 0300 05AE 0062;0061 05AE 0598 0300 0315 0062;0061 05AE 0598 0300 0315 0062;0061 05AE 0598 0300 0315 0062;0061 05AE 0598 0300 0315 0062; # (a◌֘◌̕◌̀◌֮b; a◌֮◌֘◌̀◌̕b; a◌֮◌֘◌̀◌̕b; a◌֮◌֘◌̀◌̕b; a◌֮◌֘◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT ZARQA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0599 0062;00E0 05AE 0599 0315 0062;0061 05AE 0300 0599 0315 0062;00E0 05AE 0599 0315 0062;0061 05AE 0300 0599 0315 0062; # (a◌̕◌̀◌֮◌֙b; à◌֮◌֙◌̕b; a◌֮◌̀◌֙◌̕b; à◌֮◌֙◌̕b; a◌֮◌̀◌֙◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT PASHTA, LATIN SMALL LETTER B +0061 0599 0315 0300 05AE 0062;0061 05AE 0599 0300 0315 0062;0061 05AE 0599 0300 0315 0062;0061 05AE 0599 0300 0315 0062;0061 05AE 0599 0300 0315 0062; # (a◌֙◌̕◌̀◌֮b; a◌֮◌֙◌̀◌̕b; a◌֮◌֙◌̀◌̕b; a◌֮◌֙◌̀◌̕b; a◌֮◌֙◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT PASHTA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 302E 059A 0316 059A 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062; # (a◌〮◌֚◌̖◌֚b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; ) LATIN SMALL LETTER A, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, HEBREW ACCENT YETIV, LATIN SMALL LETTER B +0061 059A 302E 059A 0316 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062; # (a◌֚◌〮◌֚◌̖b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, LATIN SMALL LETTER B +0061 059A 0316 302A 059B 0062;0061 302A 0316 059B 059A 0062;0061 302A 0316 059B 059A 0062;0061 302A 0316 059B 059A 0062;0061 302A 0316 059B 059A 0062; # (a◌֚◌̖◌〪◌֛b; a◌〪◌̖◌֛◌֚b; a◌〪◌̖◌֛◌֚b; a◌〪◌̖◌֛◌֚b; a◌〪◌̖◌֛◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT TEVIR, LATIN SMALL LETTER B +0061 059B 059A 0316 302A 0062;0061 302A 059B 0316 059A 0062;0061 302A 059B 0316 059A 0062;0061 302A 059B 0316 059A 0062;0061 302A 059B 0316 059A 0062; # (a◌֛◌֚◌̖◌〪b; a◌〪◌֛◌̖◌֚b; a◌〪◌֛◌̖◌֚b; a◌〪◌֛◌̖◌֚b; a◌〪◌֛◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT TEVIR, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 059C 0062;00E0 05AE 059C 0315 0062;0061 05AE 0300 059C 0315 0062;00E0 05AE 059C 0315 0062;0061 05AE 0300 059C 0315 0062; # (a◌̕◌̀◌֮◌֜b; à◌֮◌֜◌̕b; a◌֮◌̀◌֜◌̕b; à◌֮◌֜◌̕b; a◌֮◌̀◌֜◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT GERESH, LATIN SMALL LETTER B +0061 059C 0315 0300 05AE 0062;0061 05AE 059C 0300 0315 0062;0061 05AE 059C 0300 0315 0062;0061 05AE 059C 0300 0315 0062;0061 05AE 059C 0300 0315 0062; # (a◌֜◌̕◌̀◌֮b; a◌֮◌֜◌̀◌̕b; a◌֮◌֜◌̀◌̕b; a◌֮◌֜◌̀◌̕b; a◌֮◌֜◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT GERESH, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 059D 0062;00E0 05AE 059D 0315 0062;0061 05AE 0300 059D 0315 0062;00E0 05AE 059D 0315 0062;0061 05AE 0300 059D 0315 0062; # (a◌̕◌̀◌֮◌֝b; à◌֮◌֝◌̕b; a◌֮◌̀◌֝◌̕b; à◌֮◌֝◌̕b; a◌֮◌̀◌֝◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT GERESH MUQDAM, LATIN SMALL LETTER B +0061 059D 0315 0300 05AE 0062;0061 05AE 059D 0300 0315 0062;0061 05AE 059D 0300 0315 0062;0061 05AE 059D 0300 0315 0062;0061 05AE 059D 0300 0315 0062; # (a◌֝◌̕◌̀◌֮b; a◌֮◌֝◌̀◌̕b; a◌֮◌֝◌̀◌̕b; a◌֮◌֝◌̀◌̕b; a◌֮◌֝◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT GERESH MUQDAM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 059E 0062;00E0 05AE 059E 0315 0062;0061 05AE 0300 059E 0315 0062;00E0 05AE 059E 0315 0062;0061 05AE 0300 059E 0315 0062; # (a◌̕◌̀◌֮◌֞b; à◌֮◌֞◌̕b; a◌֮◌̀◌֞◌̕b; à◌֮◌֞◌̕b; a◌֮◌̀◌֞◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT GERSHAYIM, LATIN SMALL LETTER B +0061 059E 0315 0300 05AE 0062;0061 05AE 059E 0300 0315 0062;0061 05AE 059E 0300 0315 0062;0061 05AE 059E 0300 0315 0062;0061 05AE 059E 0300 0315 0062; # (a◌֞◌̕◌̀◌֮b; a◌֮◌֞◌̀◌̕b; a◌֮◌֞◌̀◌̕b; a◌֮◌֞◌̀◌̕b; a◌֮◌֞◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT GERSHAYIM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 059F 0062;00E0 05AE 059F 0315 0062;0061 05AE 0300 059F 0315 0062;00E0 05AE 059F 0315 0062;0061 05AE 0300 059F 0315 0062; # (a◌̕◌̀◌֮◌֟b; à◌֮◌֟◌̕b; a◌֮◌̀◌֟◌̕b; à◌֮◌֟◌̕b; a◌֮◌̀◌֟◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT QARNEY PARA, LATIN SMALL LETTER B +0061 059F 0315 0300 05AE 0062;0061 05AE 059F 0300 0315 0062;0061 05AE 059F 0300 0315 0062;0061 05AE 059F 0300 0315 0062;0061 05AE 059F 0300 0315 0062; # (a◌֟◌̕◌̀◌֮b; a◌֮◌֟◌̀◌̕b; a◌֮◌֟◌̀◌̕b; a◌֮◌֟◌̀◌̕b; a◌֮◌֟◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT QARNEY PARA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 05A0 0062;00E0 05AE 05A0 0315 0062;0061 05AE 0300 05A0 0315 0062;00E0 05AE 05A0 0315 0062;0061 05AE 0300 05A0 0315 0062; # (a◌̕◌̀◌֮◌֠b; à◌֮◌֠◌̕b; a◌֮◌̀◌֠◌̕b; à◌֮◌֠◌̕b; a◌֮◌̀◌֠◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT TELISHA GEDOLA, LATIN SMALL LETTER B +0061 05A0 0315 0300 05AE 0062;0061 05AE 05A0 0300 0315 0062;0061 05AE 05A0 0300 0315 0062;0061 05AE 05A0 0300 0315 0062;0061 05AE 05A0 0300 0315 0062; # (a◌֠◌̕◌̀◌֮b; a◌֮◌֠◌̀◌̕b; a◌֮◌֠◌̀◌̕b; a◌֮◌֠◌̀◌̕b; a◌֮◌֠◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT TELISHA GEDOLA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 05A1 0062;00E0 05AE 05A1 0315 0062;0061 05AE 0300 05A1 0315 0062;00E0 05AE 05A1 0315 0062;0061 05AE 0300 05A1 0315 0062; # (a◌̕◌̀◌֮◌֡b; à◌֮◌֡◌̕b; a◌֮◌̀◌֡◌̕b; à◌֮◌֡◌̕b; a◌֮◌̀◌֡◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT PAZER, LATIN SMALL LETTER B +0061 05A1 0315 0300 05AE 0062;0061 05AE 05A1 0300 0315 0062;0061 05AE 05A1 0300 0315 0062;0061 05AE 05A1 0300 0315 0062;0061 05AE 05A1 0300 0315 0062; # (a◌֡◌̕◌̀◌֮b; a◌֮◌֡◌̀◌̕b; a◌֮◌֡◌̀◌̕b; a◌֮◌֡◌̀◌̕b; a◌֮◌֡◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT PAZER, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 05A3 0062;0061 302A 0316 05A3 059A 0062;0061 302A 0316 05A3 059A 0062;0061 302A 0316 05A3 059A 0062;0061 302A 0316 05A3 059A 0062; # (a◌֚◌̖◌〪◌֣b; a◌〪◌̖◌֣◌֚b; a◌〪◌̖◌֣◌֚b; a◌〪◌̖◌֣◌֚b; a◌〪◌̖◌֣◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT MUNAH, LATIN SMALL LETTER B +0061 05A3 059A 0316 302A 0062;0061 302A 05A3 0316 059A 0062;0061 302A 05A3 0316 059A 0062;0061 302A 05A3 0316 059A 0062;0061 302A 05A3 0316 059A 0062; # (a◌֣◌֚◌̖◌〪b; a◌〪◌֣◌̖◌֚b; a◌〪◌֣◌̖◌֚b; a◌〪◌֣◌̖◌֚b; a◌〪◌֣◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT MUNAH, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 05A4 0062;0061 302A 0316 05A4 059A 0062;0061 302A 0316 05A4 059A 0062;0061 302A 0316 05A4 059A 0062;0061 302A 0316 05A4 059A 0062; # (a◌֚◌̖◌〪◌֤b; a◌〪◌̖◌֤◌֚b; a◌〪◌̖◌֤◌֚b; a◌〪◌̖◌֤◌֚b; a◌〪◌̖◌֤◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT MAHAPAKH, LATIN SMALL LETTER B +0061 05A4 059A 0316 302A 0062;0061 302A 05A4 0316 059A 0062;0061 302A 05A4 0316 059A 0062;0061 302A 05A4 0316 059A 0062;0061 302A 05A4 0316 059A 0062; # (a◌֤◌֚◌̖◌〪b; a◌〪◌֤◌̖◌֚b; a◌〪◌֤◌̖◌֚b; a◌〪◌֤◌̖◌֚b; a◌〪◌֤◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT MAHAPAKH, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 05A5 0062;0061 302A 0316 05A5 059A 0062;0061 302A 0316 05A5 059A 0062;0061 302A 0316 05A5 059A 0062;0061 302A 0316 05A5 059A 0062; # (a◌֚◌̖◌〪◌֥b; a◌〪◌̖◌֥◌֚b; a◌〪◌̖◌֥◌֚b; a◌〪◌̖◌֥◌֚b; a◌〪◌̖◌֥◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT MERKHA, LATIN SMALL LETTER B +0061 05A5 059A 0316 302A 0062;0061 302A 05A5 0316 059A 0062;0061 302A 05A5 0316 059A 0062;0061 302A 05A5 0316 059A 0062;0061 302A 05A5 0316 059A 0062; # (a◌֥◌֚◌̖◌〪b; a◌〪◌֥◌̖◌֚b; a◌〪◌֥◌̖◌֚b; a◌〪◌֥◌̖◌֚b; a◌〪◌֥◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT MERKHA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 05A6 0062;0061 302A 0316 05A6 059A 0062;0061 302A 0316 05A6 059A 0062;0061 302A 0316 05A6 059A 0062;0061 302A 0316 05A6 059A 0062; # (a◌֚◌̖◌〪◌֦b; a◌〪◌̖◌֦◌֚b; a◌〪◌̖◌֦◌֚b; a◌〪◌̖◌֦◌֚b; a◌〪◌̖◌֦◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT MERKHA KEFULA, LATIN SMALL LETTER B +0061 05A6 059A 0316 302A 0062;0061 302A 05A6 0316 059A 0062;0061 302A 05A6 0316 059A 0062;0061 302A 05A6 0316 059A 0062;0061 302A 05A6 0316 059A 0062; # (a◌֦◌֚◌̖◌〪b; a◌〪◌֦◌̖◌֚b; a◌〪◌֦◌̖◌֚b; a◌〪◌֦◌̖◌֚b; a◌〪◌֦◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT MERKHA KEFULA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 05A7 0062;0061 302A 0316 05A7 059A 0062;0061 302A 0316 05A7 059A 0062;0061 302A 0316 05A7 059A 0062;0061 302A 0316 05A7 059A 0062; # (a◌֚◌̖◌〪◌֧b; a◌〪◌̖◌֧◌֚b; a◌〪◌̖◌֧◌֚b; a◌〪◌̖◌֧◌֚b; a◌〪◌̖◌֧◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT DARGA, LATIN SMALL LETTER B +0061 05A7 059A 0316 302A 0062;0061 302A 05A7 0316 059A 0062;0061 302A 05A7 0316 059A 0062;0061 302A 05A7 0316 059A 0062;0061 302A 05A7 0316 059A 0062; # (a◌֧◌֚◌̖◌〪b; a◌〪◌֧◌̖◌֚b; a◌〪◌֧◌̖◌֚b; a◌〪◌֧◌̖◌֚b; a◌〪◌֧◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT DARGA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 05A8 0062;00E0 05AE 05A8 0315 0062;0061 05AE 0300 05A8 0315 0062;00E0 05AE 05A8 0315 0062;0061 05AE 0300 05A8 0315 0062; # (a◌̕◌̀◌֮◌֨b; à◌֮◌֨◌̕b; a◌֮◌̀◌֨◌̕b; à◌֮◌֨◌̕b; a◌֮◌̀◌֨◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT QADMA, LATIN SMALL LETTER B +0061 05A8 0315 0300 05AE 0062;0061 05AE 05A8 0300 0315 0062;0061 05AE 05A8 0300 0315 0062;0061 05AE 05A8 0300 0315 0062;0061 05AE 05A8 0300 0315 0062; # (a◌֨◌̕◌̀◌֮b; a◌֮◌֨◌̀◌̕b; a◌֮◌֨◌̀◌̕b; a◌֮◌֨◌̀◌̕b; a◌֮◌֨◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT QADMA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 05A9 0062;00E0 05AE 05A9 0315 0062;0061 05AE 0300 05A9 0315 0062;00E0 05AE 05A9 0315 0062;0061 05AE 0300 05A9 0315 0062; # (a◌̕◌̀◌֮◌֩b; à◌֮◌֩◌̕b; a◌֮◌̀◌֩◌̕b; à◌֮◌֩◌̕b; a◌֮◌̀◌֩◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT TELISHA QETANA, LATIN SMALL LETTER B +0061 05A9 0315 0300 05AE 0062;0061 05AE 05A9 0300 0315 0062;0061 05AE 05A9 0300 0315 0062;0061 05AE 05A9 0300 0315 0062;0061 05AE 05A9 0300 0315 0062; # (a◌֩◌̕◌̀◌֮b; a◌֮◌֩◌̀◌̕b; a◌֮◌֩◌̀◌̕b; a◌֮◌֩◌̀◌̕b; a◌֮◌֩◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT TELISHA QETANA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 05AA 0062;0061 302A 0316 05AA 059A 0062;0061 302A 0316 05AA 059A 0062;0061 302A 0316 05AA 059A 0062;0061 302A 0316 05AA 059A 0062; # (a◌֚◌̖◌〪◌֪b; a◌〪◌̖◌֪◌֚b; a◌〪◌̖◌֪◌֚b; a◌〪◌̖◌֪◌֚b; a◌〪◌̖◌֪◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT YERAH BEN YOMO, LATIN SMALL LETTER B +0061 05AA 059A 0316 302A 0062;0061 302A 05AA 0316 059A 0062;0061 302A 05AA 0316 059A 0062;0061 302A 05AA 0316 059A 0062;0061 302A 05AA 0316 059A 0062; # (a◌֪◌֚◌̖◌〪b; a◌〪◌֪◌̖◌֚b; a◌〪◌֪◌̖◌֚b; a◌〪◌֪◌̖◌֚b; a◌〪◌֪◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YERAH BEN YOMO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 05AB 0062;00E0 05AE 05AB 0315 0062;0061 05AE 0300 05AB 0315 0062;00E0 05AE 05AB 0315 0062;0061 05AE 0300 05AB 0315 0062; # (a◌̕◌̀◌֮◌֫b; à◌֮◌֫◌̕b; a◌֮◌̀◌֫◌̕b; à◌֮◌֫◌̕b; a◌֮◌̀◌֫◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT OLE, LATIN SMALL LETTER B +0061 05AB 0315 0300 05AE 0062;0061 05AE 05AB 0300 0315 0062;0061 05AE 05AB 0300 0315 0062;0061 05AE 05AB 0300 0315 0062;0061 05AE 05AB 0300 0315 0062; # (a◌֫◌̕◌̀◌֮b; a◌֮◌֫◌̀◌̕b; a◌֮◌֫◌̀◌̕b; a◌֮◌֫◌̀◌̕b; a◌֮◌֫◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT OLE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 05AC 0062;00E0 05AE 05AC 0315 0062;0061 05AE 0300 05AC 0315 0062;00E0 05AE 05AC 0315 0062;0061 05AE 0300 05AC 0315 0062; # (a◌̕◌̀◌֮◌֬b; à◌֮◌֬◌̕b; a◌֮◌̀◌֬◌̕b; à◌֮◌֬◌̕b; a◌֮◌̀◌֬◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT ILUY, LATIN SMALL LETTER B +0061 05AC 0315 0300 05AE 0062;0061 05AE 05AC 0300 0315 0062;0061 05AE 05AC 0300 0315 0062;0061 05AE 05AC 0300 0315 0062;0061 05AE 05AC 0300 0315 0062; # (a◌֬◌̕◌̀◌֮b; a◌֮◌֬◌̀◌̕b; a◌֮◌֬◌̀◌̕b; a◌֮◌֬◌̀◌̕b; a◌֮◌֬◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT ILUY, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 302E 059A 0316 05AD 0062;0061 0316 059A 05AD 302E 0062;0061 0316 059A 05AD 302E 0062;0061 0316 059A 05AD 302E 0062;0061 0316 059A 05AD 302E 0062; # (a◌〮◌֚◌̖◌֭b; a◌̖◌֚◌֭◌〮b; a◌̖◌֚◌֭◌〮b; a◌̖◌֚◌֭◌〮b; a◌̖◌֚◌֭◌〮b; ) LATIN SMALL LETTER A, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, HEBREW ACCENT DEHI, LATIN SMALL LETTER B +0061 05AD 302E 059A 0316 0062;0061 0316 05AD 059A 302E 0062;0061 0316 05AD 059A 302E 0062;0061 0316 05AD 059A 302E 0062;0061 0316 05AD 059A 302E 0062; # (a◌֭◌〮◌֚◌̖b; a◌̖◌֭◌֚◌〮b; a◌̖◌֭◌֚◌〮b; a◌̖◌֭◌֚◌〮b; a◌̖◌֭◌֚◌〮b; ) LATIN SMALL LETTER A, HEBREW ACCENT DEHI, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, LATIN SMALL LETTER B +0061 0300 05AE 1D16D 05AE 0062;00E0 1D16D 05AE 05AE 0062;0061 1D16D 05AE 05AE 0300 0062;00E0 1D16D 05AE 05AE 0062;0061 1D16D 05AE 05AE 0300 0062; # (a◌̀◌𝅭𝅭֮◌֮b; à𝅭𝅭◌֮◌֮b; a𝅭𝅭◌֮◌֮◌̀b; à𝅭𝅭◌֮◌֮b; a𝅭𝅭◌֮◌֮◌̀b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 05AE 0300 05AE 1D16D 0062;00E0 1D16D 05AE 05AE 0062;0061 1D16D 05AE 05AE 0300 0062;00E0 1D16D 05AE 05AE 0062;0061 1D16D 05AE 05AE 0300 0062; # (a◌֮◌̀◌𝅭𝅭֮b; à𝅭𝅭◌֮◌֮b; a𝅭𝅭◌֮◌֮◌̀b; à𝅭𝅭◌֮◌֮b; a𝅭𝅭◌֮◌֮◌̀b; ) LATIN SMALL LETTER A, HEBREW ACCENT ZINOR, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, LATIN SMALL LETTER B +0061 0315 0300 05AE 05AF 0062;00E0 05AE 05AF 0315 0062;0061 05AE 0300 05AF 0315 0062;00E0 05AE 05AF 0315 0062;0061 05AE 0300 05AF 0315 0062; # (a◌̕◌̀◌֮◌֯b; à◌֮◌֯◌̕b; a◌֮◌̀◌֯◌̕b; à◌֮◌֯◌̕b; a◌֮◌̀◌֯◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW MARK MASORA CIRCLE, LATIN SMALL LETTER B +0061 05AF 0315 0300 05AE 0062;0061 05AE 05AF 0300 0315 0062;0061 05AE 05AF 0300 0315 0062;0061 05AE 05AF 0300 0315 0062;0061 05AE 05AF 0300 0315 0062; # (a◌֯◌̕◌̀◌֮b; a◌֮◌֯◌̀◌̕b; a◌֮◌֯◌̀◌̕b; a◌֮◌֯◌̀◌̕b; a◌֮◌֯◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW MARK MASORA CIRCLE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 05B1 05B0 094D 05B0 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062; # (a◌ֱ◌ְ◌्◌ְb; a◌्◌ְ◌ְ◌ֱb; a◌्◌ְ◌ְ◌ֱb; a◌्◌ְ◌ְ◌ֱb; a◌्◌ְ◌ְ◌ֱb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF SEGOL, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, HEBREW POINT SHEVA, LATIN SMALL LETTER B +0061 05B0 05B1 05B0 094D 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062; # (a◌ְ◌ֱ◌ְ◌्b; a◌्◌ְ◌ְ◌ֱb; a◌्◌ְ◌ְ◌ֱb; a◌्◌ְ◌ְ◌ֱb; a◌्◌ְ◌ְ◌ֱb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, HEBREW POINT HATAF SEGOL, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, LATIN SMALL LETTER B +0061 05B2 05B1 05B0 05B1 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062; # (a◌ֲ◌ֱ◌ְ◌ֱb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF SEGOL, HEBREW POINT SHEVA, HEBREW POINT HATAF SEGOL, LATIN SMALL LETTER B +0061 05B1 05B2 05B1 05B0 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062; # (a◌ֱ◌ֲ◌ֱ◌ְb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF SEGOL, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF SEGOL, HEBREW POINT SHEVA, LATIN SMALL LETTER B +0061 05B3 05B2 05B1 05B2 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062; # (a◌ֳ◌ֲ◌ֱ◌ֲb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF QAMATS, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF SEGOL, HEBREW POINT HATAF PATAH, LATIN SMALL LETTER B +0061 05B2 05B3 05B2 05B1 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062; # (a◌ֲ◌ֳ◌ֲ◌ֱb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF QAMATS, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF SEGOL, LATIN SMALL LETTER B +0061 05B4 05B3 05B2 05B3 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062; # (a◌ִ◌ֳ◌ֲ◌ֳb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; ) LATIN SMALL LETTER A, HEBREW POINT HIRIQ, HEBREW POINT HATAF QAMATS, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF QAMATS, LATIN SMALL LETTER B +0061 05B3 05B4 05B3 05B2 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062; # (a◌ֳ◌ִ◌ֳ◌ֲb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF QAMATS, HEBREW POINT HIRIQ, HEBREW POINT HATAF QAMATS, HEBREW POINT HATAF PATAH, LATIN SMALL LETTER B +0061 05B5 05B4 05B3 05B4 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062; # (a◌ֵ◌ִ◌ֳ◌ִb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; ) LATIN SMALL LETTER A, HEBREW POINT TSERE, HEBREW POINT HIRIQ, HEBREW POINT HATAF QAMATS, HEBREW POINT HIRIQ, LATIN SMALL LETTER B +0061 05B4 05B5 05B4 05B3 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062; # (a◌ִ◌ֵ◌ִ◌ֳb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; ) LATIN SMALL LETTER A, HEBREW POINT HIRIQ, HEBREW POINT TSERE, HEBREW POINT HIRIQ, HEBREW POINT HATAF QAMATS, LATIN SMALL LETTER B +0061 05B6 05B5 05B4 05B5 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062; # (a◌ֶ◌ֵ◌ִ◌ֵb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; ) LATIN SMALL LETTER A, HEBREW POINT SEGOL, HEBREW POINT TSERE, HEBREW POINT HIRIQ, HEBREW POINT TSERE, LATIN SMALL LETTER B +0061 05B5 05B6 05B5 05B4 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062; # (a◌ֵ◌ֶ◌ֵ◌ִb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; ) LATIN SMALL LETTER A, HEBREW POINT TSERE, HEBREW POINT SEGOL, HEBREW POINT TSERE, HEBREW POINT HIRIQ, LATIN SMALL LETTER B +0061 05B7 05B6 05B5 05B6 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062; # (a◌ַ◌ֶ◌ֵ◌ֶb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; ) LATIN SMALL LETTER A, HEBREW POINT PATAH, HEBREW POINT SEGOL, HEBREW POINT TSERE, HEBREW POINT SEGOL, LATIN SMALL LETTER B +0061 05B6 05B7 05B6 05B5 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062; # (a◌ֶ◌ַ◌ֶ◌ֵb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; ) LATIN SMALL LETTER A, HEBREW POINT SEGOL, HEBREW POINT PATAH, HEBREW POINT SEGOL, HEBREW POINT TSERE, LATIN SMALL LETTER B +0061 05B8 05B7 05B6 05B7 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062; # (a◌ָ◌ַ◌ֶ◌ַb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; ) LATIN SMALL LETTER A, HEBREW POINT QAMATS, HEBREW POINT PATAH, HEBREW POINT SEGOL, HEBREW POINT PATAH, LATIN SMALL LETTER B +0061 05B7 05B8 05B7 05B6 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062; # (a◌ַ◌ָ◌ַ◌ֶb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; ) LATIN SMALL LETTER A, HEBREW POINT PATAH, HEBREW POINT QAMATS, HEBREW POINT PATAH, HEBREW POINT SEGOL, LATIN SMALL LETTER B +0061 05B9 05B8 05B7 05B8 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062; # (a◌ֹ◌ָ◌ַ◌ָb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; ) LATIN SMALL LETTER A, HEBREW POINT HOLAM, HEBREW POINT QAMATS, HEBREW POINT PATAH, HEBREW POINT QAMATS, LATIN SMALL LETTER B +0061 05B8 05B9 05B8 05B7 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062; # (a◌ָ◌ֹ◌ָ◌ַb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; ) LATIN SMALL LETTER A, HEBREW POINT QAMATS, HEBREW POINT HOLAM, HEBREW POINT QAMATS, HEBREW POINT PATAH, LATIN SMALL LETTER B +0061 05BB 05B9 05B8 05B9 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062; # (a◌ֻ◌ֹ◌ָ◌ֹb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; ) LATIN SMALL LETTER A, HEBREW POINT QUBUTS, HEBREW POINT HOLAM, HEBREW POINT QAMATS, HEBREW POINT HOLAM, LATIN SMALL LETTER B +0061 05B9 05BB 05B9 05B8 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062; # (a◌ֹ◌ֻ◌ֹ◌ָb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; ) LATIN SMALL LETTER A, HEBREW POINT HOLAM, HEBREW POINT QUBUTS, HEBREW POINT HOLAM, HEBREW POINT QAMATS, LATIN SMALL LETTER B +0061 05BC 05BB 05B9 05BB 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062; # (a◌ּ◌ֻ◌ֹ◌ֻb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; ) LATIN SMALL LETTER A, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT QUBUTS, HEBREW POINT HOLAM, HEBREW POINT QUBUTS, LATIN SMALL LETTER B +0061 05BB 05BC 05BB 05B9 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062; # (a◌ֻ◌ּ◌ֻ◌ֹb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; ) LATIN SMALL LETTER A, HEBREW POINT QUBUTS, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT QUBUTS, HEBREW POINT HOLAM, LATIN SMALL LETTER B +0061 05BD 05BC 05BB 05BC 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062; # (a◌ֽ◌ּ◌ֻ◌ּb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; ) LATIN SMALL LETTER A, HEBREW POINT METEG, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT QUBUTS, HEBREW POINT DAGESH OR MAPIQ, LATIN SMALL LETTER B +0061 05BC 05BD 05BC 05BB 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062; # (a◌ּ◌ֽ◌ּ◌ֻb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; ) LATIN SMALL LETTER A, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT METEG, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT QUBUTS, LATIN SMALL LETTER B +0061 05BF 05BD 05BC 05BD 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062; # (a◌ֿ◌ֽ◌ּ◌ֽb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; ) LATIN SMALL LETTER A, HEBREW POINT RAFE, HEBREW POINT METEG, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT METEG, LATIN SMALL LETTER B +0061 05BD 05BF 05BD 05BC 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062; # (a◌ֽ◌ֿ◌ֽ◌ּb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; ) LATIN SMALL LETTER A, HEBREW POINT METEG, HEBREW POINT RAFE, HEBREW POINT METEG, HEBREW POINT DAGESH OR MAPIQ, LATIN SMALL LETTER B +0061 05C1 05BF 05BD 05BF 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062; # (a◌ׁ◌ֿ◌ֽ◌ֿb; a◌ֽ◌ֿ◌ֿ◌ׁb; a◌ֽ◌ֿ◌ֿ◌ׁb; a◌ֽ◌ֿ◌ֿ◌ׁb; a◌ֽ◌ֿ◌ֿ◌ׁb; ) LATIN SMALL LETTER A, HEBREW POINT SHIN DOT, HEBREW POINT RAFE, HEBREW POINT METEG, HEBREW POINT RAFE, LATIN SMALL LETTER B +0061 05BF 05C1 05BF 05BD 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062; # (a◌ֿ◌ׁ◌ֿ◌ֽb; a◌ֽ◌ֿ◌ֿ◌ׁb; a◌ֽ◌ֿ◌ֿ◌ׁb; a◌ֽ◌ֿ◌ֿ◌ׁb; a◌ֽ◌ֿ◌ֿ◌ׁb; ) LATIN SMALL LETTER A, HEBREW POINT RAFE, HEBREW POINT SHIN DOT, HEBREW POINT RAFE, HEBREW POINT METEG, LATIN SMALL LETTER B +0061 05C2 05C1 05BF 05C1 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062; # (a◌ׂ◌ׁ◌ֿ◌ׁb; a◌ֿ◌ׁ◌ׁ◌ׂb; a◌ֿ◌ׁ◌ׁ◌ׂb; a◌ֿ◌ׁ◌ׁ◌ׂb; a◌ֿ◌ׁ◌ׁ◌ׂb; ) LATIN SMALL LETTER A, HEBREW POINT SIN DOT, HEBREW POINT SHIN DOT, HEBREW POINT RAFE, HEBREW POINT SHIN DOT, LATIN SMALL LETTER B +0061 05C1 05C2 05C1 05BF 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062; # (a◌ׁ◌ׂ◌ׁ◌ֿb; a◌ֿ◌ׁ◌ׁ◌ׂb; a◌ֿ◌ׁ◌ׁ◌ׂb; a◌ֿ◌ׁ◌ׁ◌ׂb; a◌ֿ◌ׁ◌ׁ◌ׂb; ) LATIN SMALL LETTER A, HEBREW POINT SHIN DOT, HEBREW POINT SIN DOT, HEBREW POINT SHIN DOT, HEBREW POINT RAFE, LATIN SMALL LETTER B +0061 FB1E 05C2 05C1 05C2 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062; # (a◌ﬞ◌ׂ◌ׁ◌ׂb; a◌ׁ◌ׂ◌ׂ◌ﬞb; a◌ׁ◌ׂ◌ׂ◌ﬞb; a◌ׁ◌ׂ◌ׂ◌ﬞb; a◌ׁ◌ׂ◌ׂ◌ﬞb; ) LATIN SMALL LETTER A, HEBREW POINT JUDEO-SPANISH VARIKA, HEBREW POINT SIN DOT, HEBREW POINT SHIN DOT, HEBREW POINT SIN DOT, LATIN SMALL LETTER B +0061 05C2 FB1E 05C2 05C1 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062; # (a◌ׂ◌ﬞ◌ׂ◌ׁb; a◌ׁ◌ׂ◌ׂ◌ﬞb; a◌ׁ◌ׂ◌ׂ◌ﬞb; a◌ׁ◌ׂ◌ׂ◌ﬞb; a◌ׁ◌ׂ◌ׂ◌ﬞb; ) LATIN SMALL LETTER A, HEBREW POINT SIN DOT, HEBREW POINT JUDEO-SPANISH VARIKA, HEBREW POINT SIN DOT, HEBREW POINT SHIN DOT, LATIN SMALL LETTER B +0061 0315 0300 05AE 05C4 0062;00E0 05AE 05C4 0315 0062;0061 05AE 0300 05C4 0315 0062;00E0 05AE 05C4 0315 0062;0061 05AE 0300 05C4 0315 0062; # (a◌̕◌̀◌֮◌ׄb; à◌֮◌ׄ◌̕b; a◌֮◌̀◌ׄ◌̕b; à◌֮◌ׄ◌̕b; a◌֮◌̀◌ׄ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW MARK UPPER DOT, LATIN SMALL LETTER B +0061 05C4 0315 0300 05AE 0062;0061 05AE 05C4 0300 0315 0062;0061 05AE 05C4 0300 0315 0062;0061 05AE 05C4 0300 0315 0062;0061 05AE 05C4 0300 0315 0062; # (a◌ׄ◌̕◌̀◌֮b; a◌֮◌ׄ◌̀◌̕b; a◌֮◌ׄ◌̀◌̕b; a◌֮◌ׄ◌̀◌̕b; a◌֮◌ׄ◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW MARK UPPER DOT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 064C 064B FB1E 064B 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062; # (a◌ٌ◌ً◌ﬞ◌ًb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; ) LATIN SMALL LETTER A, ARABIC DAMMATAN, ARABIC FATHATAN, HEBREW POINT JUDEO-SPANISH VARIKA, ARABIC FATHATAN, LATIN SMALL LETTER B +0061 064B 064C 064B FB1E 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062; # (a◌ً◌ٌ◌ً◌ﬞb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; ) LATIN SMALL LETTER A, ARABIC FATHATAN, ARABIC DAMMATAN, ARABIC FATHATAN, HEBREW POINT JUDEO-SPANISH VARIKA, LATIN SMALL LETTER B +0061 064D 064C 064B 064C 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062; # (a◌ٍ◌ٌ◌ً◌ٌb; a◌ً◌ٌ◌ٌ◌ٍb; a◌ً◌ٌ◌ٌ◌ٍb; a◌ً◌ٌ◌ٌ◌ٍb; a◌ً◌ٌ◌ٌ◌ٍb; ) LATIN SMALL LETTER A, ARABIC KASRATAN, ARABIC DAMMATAN, ARABIC FATHATAN, ARABIC DAMMATAN, LATIN SMALL LETTER B +0061 064C 064D 064C 064B 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062; # (a◌ٌ◌ٍ◌ٌ◌ًb; a◌ً◌ٌ◌ٌ◌ٍb; a◌ً◌ٌ◌ٌ◌ٍb; a◌ً◌ٌ◌ٌ◌ٍb; a◌ً◌ٌ◌ٌ◌ٍb; ) LATIN SMALL LETTER A, ARABIC DAMMATAN, ARABIC KASRATAN, ARABIC DAMMATAN, ARABIC FATHATAN, LATIN SMALL LETTER B +0061 064E 064D 064C 064D 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062; # (a◌َ◌ٍ◌ٌ◌ٍb; a◌ٌ◌ٍ◌ٍ◌َb; a◌ٌ◌ٍ◌ٍ◌َb; a◌ٌ◌ٍ◌ٍ◌َb; a◌ٌ◌ٍ◌ٍ◌َb; ) LATIN SMALL LETTER A, ARABIC FATHA, ARABIC KASRATAN, ARABIC DAMMATAN, ARABIC KASRATAN, LATIN SMALL LETTER B +0061 064D 064E 064D 064C 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062; # (a◌ٍ◌َ◌ٍ◌ٌb; a◌ٌ◌ٍ◌ٍ◌َb; a◌ٌ◌ٍ◌ٍ◌َb; a◌ٌ◌ٍ◌ٍ◌َb; a◌ٌ◌ٍ◌ٍ◌َb; ) LATIN SMALL LETTER A, ARABIC KASRATAN, ARABIC FATHA, ARABIC KASRATAN, ARABIC DAMMATAN, LATIN SMALL LETTER B +0061 064F 064E 064D 064E 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062; # (a◌ُ◌َ◌ٍ◌َb; a◌ٍ◌َ◌َ◌ُb; a◌ٍ◌َ◌َ◌ُb; a◌ٍ◌َ◌َ◌ُb; a◌ٍ◌َ◌َ◌ُb; ) LATIN SMALL LETTER A, ARABIC DAMMA, ARABIC FATHA, ARABIC KASRATAN, ARABIC FATHA, LATIN SMALL LETTER B +0061 064E 064F 064E 064D 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062; # (a◌َ◌ُ◌َ◌ٍb; a◌ٍ◌َ◌َ◌ُb; a◌ٍ◌َ◌َ◌ُb; a◌ٍ◌َ◌َ◌ُb; a◌ٍ◌َ◌َ◌ُb; ) LATIN SMALL LETTER A, ARABIC FATHA, ARABIC DAMMA, ARABIC FATHA, ARABIC KASRATAN, LATIN SMALL LETTER B +0061 0650 064F 064E 064F 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062; # (a◌ِ◌ُ◌َ◌ُb; a◌َ◌ُ◌ُ◌ِb; a◌َ◌ُ◌ُ◌ِb; a◌َ◌ُ◌ُ◌ِb; a◌َ◌ُ◌ُ◌ِb; ) LATIN SMALL LETTER A, ARABIC KASRA, ARABIC DAMMA, ARABIC FATHA, ARABIC DAMMA, LATIN SMALL LETTER B +0061 064F 0650 064F 064E 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062; # (a◌ُ◌ِ◌ُ◌َb; a◌َ◌ُ◌ُ◌ِb; a◌َ◌ُ◌ُ◌ِb; a◌َ◌ُ◌ُ◌ِb; a◌َ◌ُ◌ُ◌ِb; ) LATIN SMALL LETTER A, ARABIC DAMMA, ARABIC KASRA, ARABIC DAMMA, ARABIC FATHA, LATIN SMALL LETTER B +0061 0651 0650 064F 0650 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062; # (a◌ّ◌ِ◌ُ◌ِb; a◌ُ◌ِ◌ِ◌ّb; a◌ُ◌ِ◌ِ◌ّb; a◌ُ◌ِ◌ِ◌ّb; a◌ُ◌ِ◌ِ◌ّb; ) LATIN SMALL LETTER A, ARABIC SHADDA, ARABIC KASRA, ARABIC DAMMA, ARABIC KASRA, LATIN SMALL LETTER B +0061 0650 0651 0650 064F 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062; # (a◌ِ◌ّ◌ِ◌ُb; a◌ُ◌ِ◌ِ◌ّb; a◌ُ◌ِ◌ِ◌ّb; a◌ُ◌ِ◌ِ◌ّb; a◌ُ◌ِ◌ِ◌ّb; ) LATIN SMALL LETTER A, ARABIC KASRA, ARABIC SHADDA, ARABIC KASRA, ARABIC DAMMA, LATIN SMALL LETTER B +0061 0652 0651 0650 0651 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062; # (a◌ْ◌ّ◌ِ◌ّb; a◌ِ◌ّ◌ّ◌ْb; a◌ِ◌ّ◌ّ◌ْb; a◌ِ◌ّ◌ّ◌ْb; a◌ِ◌ّ◌ّ◌ْb; ) LATIN SMALL LETTER A, ARABIC SUKUN, ARABIC SHADDA, ARABIC KASRA, ARABIC SHADDA, LATIN SMALL LETTER B +0061 0651 0652 0651 0650 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062; # (a◌ّ◌ْ◌ّ◌ِb; a◌ِ◌ّ◌ّ◌ْb; a◌ِ◌ّ◌ّ◌ْb; a◌ِ◌ّ◌ّ◌ْb; a◌ِ◌ّ◌ّ◌ْb; ) LATIN SMALL LETTER A, ARABIC SHADDA, ARABIC SUKUN, ARABIC SHADDA, ARABIC KASRA, LATIN SMALL LETTER B +0061 0670 0652 0651 0652 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062; # (a◌ٰ◌ْ◌ّ◌ْb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; ) LATIN SMALL LETTER A, ARABIC LETTER SUPERSCRIPT ALEF, ARABIC SUKUN, ARABIC SHADDA, ARABIC SUKUN, LATIN SMALL LETTER B +0061 0652 0670 0652 0651 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062; # (a◌ْ◌ٰ◌ْ◌ّb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; ) LATIN SMALL LETTER A, ARABIC SUKUN, ARABIC LETTER SUPERSCRIPT ALEF, ARABIC SUKUN, ARABIC SHADDA, LATIN SMALL LETTER B +0061 0315 0300 05AE 0653 0062;00E0 05AE 0653 0315 0062;0061 05AE 0300 0653 0315 0062;00E0 05AE 0653 0315 0062;0061 05AE 0300 0653 0315 0062; # (a◌̕◌̀◌֮◌ٓb; à◌֮◌ٓ◌̕b; a◌֮◌̀◌ٓ◌̕b; à◌֮◌ٓ◌̕b; a◌֮◌̀◌ٓ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC MADDAH ABOVE, LATIN SMALL LETTER B +0061 0653 0315 0300 05AE 0062;0061 05AE 0653 0300 0315 0062;0061 05AE 0653 0300 0315 0062;0061 05AE 0653 0300 0315 0062;0061 05AE 0653 0300 0315 0062; # (a◌ٓ◌̕◌̀◌֮b; a◌֮◌ٓ◌̀◌̕b; a◌֮◌ٓ◌̀◌̕b; a◌֮◌ٓ◌̀◌̕b; a◌֮◌ٓ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC MADDAH ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0654 0062;00E0 05AE 0654 0315 0062;0061 05AE 0300 0654 0315 0062;00E0 05AE 0654 0315 0062;0061 05AE 0300 0654 0315 0062; # (a◌̕◌̀◌֮◌ٔb; à◌֮◌ٔ◌̕b; a◌֮◌̀◌ٔ◌̕b; à◌֮◌ٔ◌̕b; a◌֮◌̀◌ٔ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC HAMZA ABOVE, LATIN SMALL LETTER B +0061 0654 0315 0300 05AE 0062;0061 05AE 0654 0300 0315 0062;0061 05AE 0654 0300 0315 0062;0061 05AE 0654 0300 0315 0062;0061 05AE 0654 0300 0315 0062; # (a◌ٔ◌̕◌̀◌֮b; a◌֮◌ٔ◌̀◌̕b; a◌֮◌ٔ◌̀◌̕b; a◌֮◌ٔ◌̀◌̕b; a◌֮◌ٔ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC HAMZA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0655 0062;0061 302A 0316 0655 059A 0062;0061 302A 0316 0655 059A 0062;0061 302A 0316 0655 059A 0062;0061 302A 0316 0655 059A 0062; # (a◌֚◌̖◌〪◌ٕb; a◌〪◌̖◌ٕ◌֚b; a◌〪◌̖◌ٕ◌֚b; a◌〪◌̖◌ٕ◌֚b; a◌〪◌̖◌ٕ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, ARABIC HAMZA BELOW, LATIN SMALL LETTER B +0061 0655 059A 0316 302A 0062;0061 302A 0655 0316 059A 0062;0061 302A 0655 0316 059A 0062;0061 302A 0655 0316 059A 0062;0061 302A 0655 0316 059A 0062; # (a◌ٕ◌֚◌̖◌〪b; a◌〪◌ٕ◌̖◌֚b; a◌〪◌ٕ◌̖◌֚b; a◌〪◌ٕ◌̖◌֚b; a◌〪◌ٕ◌̖◌֚b; ) LATIN SMALL LETTER A, ARABIC HAMZA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0711 0670 0652 0670 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062; # (a◌ܑ◌ٰ◌ْ◌ٰb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; ) LATIN SMALL LETTER A, SYRIAC LETTER SUPERSCRIPT ALAPH, ARABIC LETTER SUPERSCRIPT ALEF, ARABIC SUKUN, ARABIC LETTER SUPERSCRIPT ALEF, LATIN SMALL LETTER B +0061 0670 0711 0670 0652 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062; # (a◌ٰ◌ܑ◌ٰ◌ْb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; ) LATIN SMALL LETTER A, ARABIC LETTER SUPERSCRIPT ALEF, SYRIAC LETTER SUPERSCRIPT ALAPH, ARABIC LETTER SUPERSCRIPT ALEF, ARABIC SUKUN, LATIN SMALL LETTER B +0061 0315 0300 05AE 06D6 0062;00E0 05AE 06D6 0315 0062;0061 05AE 0300 06D6 0315 0062;00E0 05AE 06D6 0315 0062;0061 05AE 0300 06D6 0315 0062; # (a◌̕◌̀◌֮◌ۖb; à◌֮◌ۖ◌̕b; a◌֮◌̀◌ۖ◌̕b; à◌֮◌ۖ◌̕b; a◌֮◌̀◌ۖ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA, LATIN SMALL LETTER B +0061 06D6 0315 0300 05AE 0062;0061 05AE 06D6 0300 0315 0062;0061 05AE 06D6 0300 0315 0062;0061 05AE 06D6 0300 0315 0062;0061 05AE 06D6 0300 0315 0062; # (a◌ۖ◌̕◌̀◌֮b; a◌֮◌ۖ◌̀◌̕b; a◌֮◌ۖ◌̀◌̕b; a◌֮◌ۖ◌̀◌̕b; a◌֮◌ۖ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06D7 0062;00E0 05AE 06D7 0315 0062;0061 05AE 0300 06D7 0315 0062;00E0 05AE 06D7 0315 0062;0061 05AE 0300 06D7 0315 0062; # (a◌̕◌̀◌֮◌ۗb; à◌֮◌ۗ◌̕b; a◌֮◌̀◌ۗ◌̕b; à◌֮◌ۗ◌̕b; a◌֮◌̀◌ۗ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA, LATIN SMALL LETTER B +0061 06D7 0315 0300 05AE 0062;0061 05AE 06D7 0300 0315 0062;0061 05AE 06D7 0300 0315 0062;0061 05AE 06D7 0300 0315 0062;0061 05AE 06D7 0300 0315 0062; # (a◌ۗ◌̕◌̀◌֮b; a◌֮◌ۗ◌̀◌̕b; a◌֮◌ۗ◌̀◌̕b; a◌֮◌ۗ◌̀◌̕b; a◌֮◌ۗ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06D8 0062;00E0 05AE 06D8 0315 0062;0061 05AE 0300 06D8 0315 0062;00E0 05AE 06D8 0315 0062;0061 05AE 0300 06D8 0315 0062; # (a◌̕◌̀◌֮◌ۘb; à◌֮◌ۘ◌̕b; a◌֮◌̀◌ۘ◌̕b; à◌֮◌ۘ◌̕b; a◌֮◌̀◌ۘ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH MEEM INITIAL FORM, LATIN SMALL LETTER B +0061 06D8 0315 0300 05AE 0062;0061 05AE 06D8 0300 0315 0062;0061 05AE 06D8 0300 0315 0062;0061 05AE 06D8 0300 0315 0062;0061 05AE 06D8 0300 0315 0062; # (a◌ۘ◌̕◌̀◌֮b; a◌֮◌ۘ◌̀◌̕b; a◌֮◌ۘ◌̀◌̕b; a◌֮◌ۘ◌̀◌̕b; a◌֮◌ۘ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH MEEM INITIAL FORM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06D9 0062;00E0 05AE 06D9 0315 0062;0061 05AE 0300 06D9 0315 0062;00E0 05AE 06D9 0315 0062;0061 05AE 0300 06D9 0315 0062; # (a◌̕◌̀◌֮◌ۙb; à◌֮◌ۙ◌̕b; a◌֮◌̀◌ۙ◌̕b; à◌֮◌ۙ◌̕b; a◌֮◌̀◌ۙ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH LAM ALEF, LATIN SMALL LETTER B +0061 06D9 0315 0300 05AE 0062;0061 05AE 06D9 0300 0315 0062;0061 05AE 06D9 0300 0315 0062;0061 05AE 06D9 0300 0315 0062;0061 05AE 06D9 0300 0315 0062; # (a◌ۙ◌̕◌̀◌֮b; a◌֮◌ۙ◌̀◌̕b; a◌֮◌ۙ◌̀◌̕b; a◌֮◌ۙ◌̀◌̕b; a◌֮◌ۙ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH LAM ALEF, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06DA 0062;00E0 05AE 06DA 0315 0062;0061 05AE 0300 06DA 0315 0062;00E0 05AE 06DA 0315 0062;0061 05AE 0300 06DA 0315 0062; # (a◌̕◌̀◌֮◌ۚb; à◌֮◌ۚ◌̕b; a◌֮◌̀◌ۚ◌̕b; à◌֮◌ۚ◌̕b; a◌֮◌̀◌ۚ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH JEEM, LATIN SMALL LETTER B +0061 06DA 0315 0300 05AE 0062;0061 05AE 06DA 0300 0315 0062;0061 05AE 06DA 0300 0315 0062;0061 05AE 06DA 0300 0315 0062;0061 05AE 06DA 0300 0315 0062; # (a◌ۚ◌̕◌̀◌֮b; a◌֮◌ۚ◌̀◌̕b; a◌֮◌ۚ◌̀◌̕b; a◌֮◌ۚ◌̀◌̕b; a◌֮◌ۚ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH JEEM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06DB 0062;00E0 05AE 06DB 0315 0062;0061 05AE 0300 06DB 0315 0062;00E0 05AE 06DB 0315 0062;0061 05AE 0300 06DB 0315 0062; # (a◌̕◌̀◌֮◌ۛb; à◌֮◌ۛ◌̕b; a◌֮◌̀◌ۛ◌̕b; à◌֮◌ۛ◌̕b; a◌֮◌̀◌ۛ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH THREE DOTS, LATIN SMALL LETTER B +0061 06DB 0315 0300 05AE 0062;0061 05AE 06DB 0300 0315 0062;0061 05AE 06DB 0300 0315 0062;0061 05AE 06DB 0300 0315 0062;0061 05AE 06DB 0300 0315 0062; # (a◌ۛ◌̕◌̀◌֮b; a◌֮◌ۛ◌̀◌̕b; a◌֮◌ۛ◌̀◌̕b; a◌֮◌ۛ◌̀◌̕b; a◌֮◌ۛ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH THREE DOTS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06DC 0062;00E0 05AE 06DC 0315 0062;0061 05AE 0300 06DC 0315 0062;00E0 05AE 06DC 0315 0062;0061 05AE 0300 06DC 0315 0062; # (a◌̕◌̀◌֮◌ۜb; à◌֮◌ۜ◌̕b; a◌֮◌̀◌ۜ◌̕b; à◌֮◌ۜ◌̕b; a◌֮◌̀◌ۜ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH SEEN, LATIN SMALL LETTER B +0061 06DC 0315 0300 05AE 0062;0061 05AE 06DC 0300 0315 0062;0061 05AE 06DC 0300 0315 0062;0061 05AE 06DC 0300 0315 0062;0061 05AE 06DC 0300 0315 0062; # (a◌ۜ◌̕◌̀◌֮b; a◌֮◌ۜ◌̀◌̕b; a◌֮◌ۜ◌̀◌̕b; a◌֮◌ۜ◌̀◌̕b; a◌֮◌ۜ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH SEEN, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06DF 0062;00E0 05AE 06DF 0315 0062;0061 05AE 0300 06DF 0315 0062;00E0 05AE 06DF 0315 0062;0061 05AE 0300 06DF 0315 0062; # (a◌̕◌̀◌֮◌۟b; à◌֮◌۟◌̕b; a◌֮◌̀◌۟◌̕b; à◌֮◌۟◌̕b; a◌֮◌̀◌۟◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH ROUNDED ZERO, LATIN SMALL LETTER B +0061 06DF 0315 0300 05AE 0062;0061 05AE 06DF 0300 0315 0062;0061 05AE 06DF 0300 0315 0062;0061 05AE 06DF 0300 0315 0062;0061 05AE 06DF 0300 0315 0062; # (a◌۟◌̕◌̀◌֮b; a◌֮◌۟◌̀◌̕b; a◌֮◌۟◌̀◌̕b; a◌֮◌۟◌̀◌̕b; a◌֮◌۟◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH ROUNDED ZERO, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06E0 0062;00E0 05AE 06E0 0315 0062;0061 05AE 0300 06E0 0315 0062;00E0 05AE 06E0 0315 0062;0061 05AE 0300 06E0 0315 0062; # (a◌̕◌̀◌֮◌۠b; à◌֮◌۠◌̕b; a◌֮◌̀◌۠◌̕b; à◌֮◌۠◌̕b; a◌֮◌̀◌۠◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO, LATIN SMALL LETTER B +0061 06E0 0315 0300 05AE 0062;0061 05AE 06E0 0300 0315 0062;0061 05AE 06E0 0300 0315 0062;0061 05AE 06E0 0300 0315 0062;0061 05AE 06E0 0300 0315 0062; # (a◌۠◌̕◌̀◌֮b; a◌֮◌۠◌̀◌̕b; a◌֮◌۠◌̀◌̕b; a◌֮◌۠◌̀◌̕b; a◌֮◌۠◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06E1 0062;00E0 05AE 06E1 0315 0062;0061 05AE 0300 06E1 0315 0062;00E0 05AE 06E1 0315 0062;0061 05AE 0300 06E1 0315 0062; # (a◌̕◌̀◌֮◌ۡb; à◌֮◌ۡ◌̕b; a◌֮◌̀◌ۡ◌̕b; à◌֮◌ۡ◌̕b; a◌֮◌̀◌ۡ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH DOTLESS HEAD OF KHAH, LATIN SMALL LETTER B +0061 06E1 0315 0300 05AE 0062;0061 05AE 06E1 0300 0315 0062;0061 05AE 06E1 0300 0315 0062;0061 05AE 06E1 0300 0315 0062;0061 05AE 06E1 0300 0315 0062; # (a◌ۡ◌̕◌̀◌֮b; a◌֮◌ۡ◌̀◌̕b; a◌֮◌ۡ◌̀◌̕b; a◌֮◌ۡ◌̀◌̕b; a◌֮◌ۡ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH DOTLESS HEAD OF KHAH, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06E2 0062;00E0 05AE 06E2 0315 0062;0061 05AE 0300 06E2 0315 0062;00E0 05AE 06E2 0315 0062;0061 05AE 0300 06E2 0315 0062; # (a◌̕◌̀◌֮◌ۢb; à◌֮◌ۢ◌̕b; a◌֮◌̀◌ۢ◌̕b; à◌֮◌ۢ◌̕b; a◌֮◌̀◌ۢ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH MEEM ISOLATED FORM, LATIN SMALL LETTER B +0061 06E2 0315 0300 05AE 0062;0061 05AE 06E2 0300 0315 0062;0061 05AE 06E2 0300 0315 0062;0061 05AE 06E2 0300 0315 0062;0061 05AE 06E2 0300 0315 0062; # (a◌ۢ◌̕◌̀◌֮b; a◌֮◌ۢ◌̀◌̕b; a◌֮◌ۢ◌̀◌̕b; a◌֮◌ۢ◌̀◌̕b; a◌֮◌ۢ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH MEEM ISOLATED FORM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 06E3 0062;0061 302A 0316 06E3 059A 0062;0061 302A 0316 06E3 059A 0062;0061 302A 0316 06E3 059A 0062;0061 302A 0316 06E3 059A 0062; # (a◌֚◌̖◌〪◌ۣb; a◌〪◌̖◌ۣ◌֚b; a◌〪◌̖◌ۣ◌֚b; a◌〪◌̖◌ۣ◌֚b; a◌〪◌̖◌ۣ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, ARABIC SMALL LOW SEEN, LATIN SMALL LETTER B +0061 06E3 059A 0316 302A 0062;0061 302A 06E3 0316 059A 0062;0061 302A 06E3 0316 059A 0062;0061 302A 06E3 0316 059A 0062;0061 302A 06E3 0316 059A 0062; # (a◌ۣ◌֚◌̖◌〪b; a◌〪◌ۣ◌̖◌֚b; a◌〪◌ۣ◌̖◌֚b; a◌〪◌ۣ◌̖◌֚b; a◌〪◌ۣ◌̖◌֚b; ) LATIN SMALL LETTER A, ARABIC SMALL LOW SEEN, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 06E4 0062;00E0 05AE 06E4 0315 0062;0061 05AE 0300 06E4 0315 0062;00E0 05AE 06E4 0315 0062;0061 05AE 0300 06E4 0315 0062; # (a◌̕◌̀◌֮◌ۤb; à◌֮◌ۤ◌̕b; a◌֮◌̀◌ۤ◌̕b; à◌֮◌ۤ◌̕b; a◌֮◌̀◌ۤ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH MADDA, LATIN SMALL LETTER B +0061 06E4 0315 0300 05AE 0062;0061 05AE 06E4 0300 0315 0062;0061 05AE 06E4 0300 0315 0062;0061 05AE 06E4 0300 0315 0062;0061 05AE 06E4 0300 0315 0062; # (a◌ۤ◌̕◌̀◌֮b; a◌֮◌ۤ◌̀◌̕b; a◌֮◌ۤ◌̀◌̕b; a◌֮◌ۤ◌̀◌̕b; a◌֮◌ۤ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH MADDA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06E7 0062;00E0 05AE 06E7 0315 0062;0061 05AE 0300 06E7 0315 0062;00E0 05AE 06E7 0315 0062;0061 05AE 0300 06E7 0315 0062; # (a◌̕◌̀◌֮◌ۧb; à◌֮◌ۧ◌̕b; a◌֮◌̀◌ۧ◌̕b; à◌֮◌ۧ◌̕b; a◌֮◌̀◌ۧ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH YEH, LATIN SMALL LETTER B +0061 06E7 0315 0300 05AE 0062;0061 05AE 06E7 0300 0315 0062;0061 05AE 06E7 0300 0315 0062;0061 05AE 06E7 0300 0315 0062;0061 05AE 06E7 0300 0315 0062; # (a◌ۧ◌̕◌̀◌֮b; a◌֮◌ۧ◌̀◌̕b; a◌֮◌ۧ◌̀◌̕b; a◌֮◌ۧ◌̀◌̕b; a◌֮◌ۧ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH YEH, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06E8 0062;00E0 05AE 06E8 0315 0062;0061 05AE 0300 06E8 0315 0062;00E0 05AE 06E8 0315 0062;0061 05AE 0300 06E8 0315 0062; # (a◌̕◌̀◌֮◌ۨb; à◌֮◌ۨ◌̕b; a◌֮◌̀◌ۨ◌̕b; à◌֮◌ۨ◌̕b; a◌֮◌̀◌ۨ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH NOON, LATIN SMALL LETTER B +0061 06E8 0315 0300 05AE 0062;0061 05AE 06E8 0300 0315 0062;0061 05AE 06E8 0300 0315 0062;0061 05AE 06E8 0300 0315 0062;0061 05AE 06E8 0300 0315 0062; # (a◌ۨ◌̕◌̀◌֮b; a◌֮◌ۨ◌̀◌̕b; a◌֮◌ۨ◌̀◌̕b; a◌֮◌ۨ◌̀◌̕b; a◌֮◌ۨ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH NOON, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 06EA 0062;0061 302A 0316 06EA 059A 0062;0061 302A 0316 06EA 059A 0062;0061 302A 0316 06EA 059A 0062;0061 302A 0316 06EA 059A 0062; # (a◌֚◌̖◌〪◌۪b; a◌〪◌̖◌۪◌֚b; a◌〪◌̖◌۪◌֚b; a◌〪◌̖◌۪◌֚b; a◌〪◌̖◌۪◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, ARABIC EMPTY CENTRE LOW STOP, LATIN SMALL LETTER B +0061 06EA 059A 0316 302A 0062;0061 302A 06EA 0316 059A 0062;0061 302A 06EA 0316 059A 0062;0061 302A 06EA 0316 059A 0062;0061 302A 06EA 0316 059A 0062; # (a◌۪◌֚◌̖◌〪b; a◌〪◌۪◌̖◌֚b; a◌〪◌۪◌̖◌֚b; a◌〪◌۪◌̖◌֚b; a◌〪◌۪◌̖◌֚b; ) LATIN SMALL LETTER A, ARABIC EMPTY CENTRE LOW STOP, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 06EB 0062;00E0 05AE 06EB 0315 0062;0061 05AE 0300 06EB 0315 0062;00E0 05AE 06EB 0315 0062;0061 05AE 0300 06EB 0315 0062; # (a◌̕◌̀◌֮◌۫b; à◌֮◌۫◌̕b; a◌֮◌̀◌۫◌̕b; à◌֮◌۫◌̕b; a◌֮◌̀◌۫◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC EMPTY CENTRE HIGH STOP, LATIN SMALL LETTER B +0061 06EB 0315 0300 05AE 0062;0061 05AE 06EB 0300 0315 0062;0061 05AE 06EB 0300 0315 0062;0061 05AE 06EB 0300 0315 0062;0061 05AE 06EB 0300 0315 0062; # (a◌۫◌̕◌̀◌֮b; a◌֮◌۫◌̀◌̕b; a◌֮◌۫◌̀◌̕b; a◌֮◌۫◌̀◌̕b; a◌֮◌۫◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC EMPTY CENTRE HIGH STOP, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 06EC 0062;00E0 05AE 06EC 0315 0062;0061 05AE 0300 06EC 0315 0062;00E0 05AE 06EC 0315 0062;0061 05AE 0300 06EC 0315 0062; # (a◌̕◌̀◌֮◌۬b; à◌֮◌۬◌̕b; a◌֮◌̀◌۬◌̕b; à◌֮◌۬◌̕b; a◌֮◌̀◌۬◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE, LATIN SMALL LETTER B +0061 06EC 0315 0300 05AE 0062;0061 05AE 06EC 0300 0315 0062;0061 05AE 06EC 0300 0315 0062;0061 05AE 06EC 0300 0315 0062;0061 05AE 06EC 0300 0315 0062; # (a◌۬◌̕◌̀◌֮b; a◌֮◌۬◌̀◌̕b; a◌֮◌۬◌̀◌̕b; a◌֮◌۬◌̀◌̕b; a◌֮◌۬◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 06ED 0062;0061 302A 0316 06ED 059A 0062;0061 302A 0316 06ED 059A 0062;0061 302A 0316 06ED 059A 0062;0061 302A 0316 06ED 059A 0062; # (a◌֚◌̖◌〪◌ۭb; a◌〪◌̖◌ۭ◌֚b; a◌〪◌̖◌ۭ◌֚b; a◌〪◌̖◌ۭ◌֚b; a◌〪◌̖◌ۭ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, ARABIC SMALL LOW MEEM, LATIN SMALL LETTER B +0061 06ED 059A 0316 302A 0062;0061 302A 06ED 0316 059A 0062;0061 302A 06ED 0316 059A 0062;0061 302A 06ED 0316 059A 0062;0061 302A 06ED 0316 059A 0062; # (a◌ۭ◌֚◌̖◌〪b; a◌〪◌ۭ◌̖◌֚b; a◌〪◌ۭ◌̖◌֚b; a◌〪◌ۭ◌̖◌֚b; a◌〪◌ۭ◌̖◌֚b; ) LATIN SMALL LETTER A, ARABIC SMALL LOW MEEM, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0C55 0711 0670 0711 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062; # (a◌ౕ◌ܑ◌ٰ◌ܑb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; ) LATIN SMALL LETTER A, TELUGU LENGTH MARK, SYRIAC LETTER SUPERSCRIPT ALAPH, ARABIC LETTER SUPERSCRIPT ALEF, SYRIAC LETTER SUPERSCRIPT ALAPH, LATIN SMALL LETTER B +0061 0711 0C55 0711 0670 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062; # (a◌ܑ◌ౕ◌ܑ◌ٰb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; ) LATIN SMALL LETTER A, SYRIAC LETTER SUPERSCRIPT ALAPH, TELUGU LENGTH MARK, SYRIAC LETTER SUPERSCRIPT ALAPH, ARABIC LETTER SUPERSCRIPT ALEF, LATIN SMALL LETTER B +0061 0315 0300 05AE 0730 0062;00E0 05AE 0730 0315 0062;0061 05AE 0300 0730 0315 0062;00E0 05AE 0730 0315 0062;0061 05AE 0300 0730 0315 0062; # (a◌̕◌̀◌֮◌ܰb; à◌֮◌ܰ◌̕b; a◌֮◌̀◌ܰ◌̕b; à◌֮◌ܰ◌̕b; a◌֮◌̀◌ܰ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC PTHAHA ABOVE, LATIN SMALL LETTER B +0061 0730 0315 0300 05AE 0062;0061 05AE 0730 0300 0315 0062;0061 05AE 0730 0300 0315 0062;0061 05AE 0730 0300 0315 0062;0061 05AE 0730 0300 0315 0062; # (a◌ܰ◌̕◌̀◌֮b; a◌֮◌ܰ◌̀◌̕b; a◌֮◌ܰ◌̀◌̕b; a◌֮◌ܰ◌̀◌̕b; a◌֮◌ܰ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC PTHAHA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0731 0062;0061 302A 0316 0731 059A 0062;0061 302A 0316 0731 059A 0062;0061 302A 0316 0731 059A 0062;0061 302A 0316 0731 059A 0062; # (a◌֚◌̖◌〪◌ܱb; a◌〪◌̖◌ܱ◌֚b; a◌〪◌̖◌ܱ◌֚b; a◌〪◌̖◌ܱ◌֚b; a◌〪◌̖◌ܱ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC PTHAHA BELOW, LATIN SMALL LETTER B +0061 0731 059A 0316 302A 0062;0061 302A 0731 0316 059A 0062;0061 302A 0731 0316 059A 0062;0061 302A 0731 0316 059A 0062;0061 302A 0731 0316 059A 0062; # (a◌ܱ◌֚◌̖◌〪b; a◌〪◌ܱ◌̖◌֚b; a◌〪◌ܱ◌̖◌֚b; a◌〪◌ܱ◌̖◌֚b; a◌〪◌ܱ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC PTHAHA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0732 0062;00E0 05AE 0732 0315 0062;0061 05AE 0300 0732 0315 0062;00E0 05AE 0732 0315 0062;0061 05AE 0300 0732 0315 0062; # (a◌̕◌̀◌֮◌ܲb; à◌֮◌ܲ◌̕b; a◌֮◌̀◌ܲ◌̕b; à◌֮◌ܲ◌̕b; a◌֮◌̀◌ܲ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC PTHAHA DOTTED, LATIN SMALL LETTER B +0061 0732 0315 0300 05AE 0062;0061 05AE 0732 0300 0315 0062;0061 05AE 0732 0300 0315 0062;0061 05AE 0732 0300 0315 0062;0061 05AE 0732 0300 0315 0062; # (a◌ܲ◌̕◌̀◌֮b; a◌֮◌ܲ◌̀◌̕b; a◌֮◌ܲ◌̀◌̕b; a◌֮◌ܲ◌̀◌̕b; a◌֮◌ܲ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC PTHAHA DOTTED, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0733 0062;00E0 05AE 0733 0315 0062;0061 05AE 0300 0733 0315 0062;00E0 05AE 0733 0315 0062;0061 05AE 0300 0733 0315 0062; # (a◌̕◌̀◌֮◌ܳb; à◌֮◌ܳ◌̕b; a◌֮◌̀◌ܳ◌̕b; à◌֮◌ܳ◌̕b; a◌֮◌̀◌ܳ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC ZQAPHA ABOVE, LATIN SMALL LETTER B +0061 0733 0315 0300 05AE 0062;0061 05AE 0733 0300 0315 0062;0061 05AE 0733 0300 0315 0062;0061 05AE 0733 0300 0315 0062;0061 05AE 0733 0300 0315 0062; # (a◌ܳ◌̕◌̀◌֮b; a◌֮◌ܳ◌̀◌̕b; a◌֮◌ܳ◌̀◌̕b; a◌֮◌ܳ◌̀◌̕b; a◌֮◌ܳ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC ZQAPHA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0734 0062;0061 302A 0316 0734 059A 0062;0061 302A 0316 0734 059A 0062;0061 302A 0316 0734 059A 0062;0061 302A 0316 0734 059A 0062; # (a◌֚◌̖◌〪◌ܴb; a◌〪◌̖◌ܴ◌֚b; a◌〪◌̖◌ܴ◌֚b; a◌〪◌̖◌ܴ◌֚b; a◌〪◌̖◌ܴ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC ZQAPHA BELOW, LATIN SMALL LETTER B +0061 0734 059A 0316 302A 0062;0061 302A 0734 0316 059A 0062;0061 302A 0734 0316 059A 0062;0061 302A 0734 0316 059A 0062;0061 302A 0734 0316 059A 0062; # (a◌ܴ◌֚◌̖◌〪b; a◌〪◌ܴ◌̖◌֚b; a◌〪◌ܴ◌̖◌֚b; a◌〪◌ܴ◌̖◌֚b; a◌〪◌ܴ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC ZQAPHA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0735 0062;00E0 05AE 0735 0315 0062;0061 05AE 0300 0735 0315 0062;00E0 05AE 0735 0315 0062;0061 05AE 0300 0735 0315 0062; # (a◌̕◌̀◌֮◌ܵb; à◌֮◌ܵ◌̕b; a◌֮◌̀◌ܵ◌̕b; à◌֮◌ܵ◌̕b; a◌֮◌̀◌ܵ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC ZQAPHA DOTTED, LATIN SMALL LETTER B +0061 0735 0315 0300 05AE 0062;0061 05AE 0735 0300 0315 0062;0061 05AE 0735 0300 0315 0062;0061 05AE 0735 0300 0315 0062;0061 05AE 0735 0300 0315 0062; # (a◌ܵ◌̕◌̀◌֮b; a◌֮◌ܵ◌̀◌̕b; a◌֮◌ܵ◌̀◌̕b; a◌֮◌ܵ◌̀◌̕b; a◌֮◌ܵ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC ZQAPHA DOTTED, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0736 0062;00E0 05AE 0736 0315 0062;0061 05AE 0300 0736 0315 0062;00E0 05AE 0736 0315 0062;0061 05AE 0300 0736 0315 0062; # (a◌̕◌̀◌֮◌ܶb; à◌֮◌ܶ◌̕b; a◌֮◌̀◌ܶ◌̕b; à◌֮◌ܶ◌̕b; a◌֮◌̀◌ܶ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC RBASA ABOVE, LATIN SMALL LETTER B +0061 0736 0315 0300 05AE 0062;0061 05AE 0736 0300 0315 0062;0061 05AE 0736 0300 0315 0062;0061 05AE 0736 0300 0315 0062;0061 05AE 0736 0300 0315 0062; # (a◌ܶ◌̕◌̀◌֮b; a◌֮◌ܶ◌̀◌̕b; a◌֮◌ܶ◌̀◌̕b; a◌֮◌ܶ◌̀◌̕b; a◌֮◌ܶ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC RBASA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0737 0062;0061 302A 0316 0737 059A 0062;0061 302A 0316 0737 059A 0062;0061 302A 0316 0737 059A 0062;0061 302A 0316 0737 059A 0062; # (a◌֚◌̖◌〪◌ܷb; a◌〪◌̖◌ܷ◌֚b; a◌〪◌̖◌ܷ◌֚b; a◌〪◌̖◌ܷ◌֚b; a◌〪◌̖◌ܷ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC RBASA BELOW, LATIN SMALL LETTER B +0061 0737 059A 0316 302A 0062;0061 302A 0737 0316 059A 0062;0061 302A 0737 0316 059A 0062;0061 302A 0737 0316 059A 0062;0061 302A 0737 0316 059A 0062; # (a◌ܷ◌֚◌̖◌〪b; a◌〪◌ܷ◌̖◌֚b; a◌〪◌ܷ◌̖◌֚b; a◌〪◌ܷ◌̖◌֚b; a◌〪◌ܷ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC RBASA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0738 0062;0061 302A 0316 0738 059A 0062;0061 302A 0316 0738 059A 0062;0061 302A 0316 0738 059A 0062;0061 302A 0316 0738 059A 0062; # (a◌֚◌̖◌〪◌ܸb; a◌〪◌̖◌ܸ◌֚b; a◌〪◌̖◌ܸ◌֚b; a◌〪◌̖◌ܸ◌֚b; a◌〪◌̖◌ܸ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC DOTTED ZLAMA HORIZONTAL, LATIN SMALL LETTER B +0061 0738 059A 0316 302A 0062;0061 302A 0738 0316 059A 0062;0061 302A 0738 0316 059A 0062;0061 302A 0738 0316 059A 0062;0061 302A 0738 0316 059A 0062; # (a◌ܸ◌֚◌̖◌〪b; a◌〪◌ܸ◌̖◌֚b; a◌〪◌ܸ◌̖◌֚b; a◌〪◌ܸ◌̖◌֚b; a◌〪◌ܸ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC DOTTED ZLAMA HORIZONTAL, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0739 0062;0061 302A 0316 0739 059A 0062;0061 302A 0316 0739 059A 0062;0061 302A 0316 0739 059A 0062;0061 302A 0316 0739 059A 0062; # (a◌֚◌̖◌〪◌ܹb; a◌〪◌̖◌ܹ◌֚b; a◌〪◌̖◌ܹ◌֚b; a◌〪◌̖◌ܹ◌֚b; a◌〪◌̖◌ܹ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC DOTTED ZLAMA ANGULAR, LATIN SMALL LETTER B +0061 0739 059A 0316 302A 0062;0061 302A 0739 0316 059A 0062;0061 302A 0739 0316 059A 0062;0061 302A 0739 0316 059A 0062;0061 302A 0739 0316 059A 0062; # (a◌ܹ◌֚◌̖◌〪b; a◌〪◌ܹ◌̖◌֚b; a◌〪◌ܹ◌̖◌֚b; a◌〪◌ܹ◌̖◌֚b; a◌〪◌ܹ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC DOTTED ZLAMA ANGULAR, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 073A 0062;00E0 05AE 073A 0315 0062;0061 05AE 0300 073A 0315 0062;00E0 05AE 073A 0315 0062;0061 05AE 0300 073A 0315 0062; # (a◌̕◌̀◌֮◌ܺb; à◌֮◌ܺ◌̕b; a◌֮◌̀◌ܺ◌̕b; à◌֮◌ܺ◌̕b; a◌֮◌̀◌ܺ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC HBASA ABOVE, LATIN SMALL LETTER B +0061 073A 0315 0300 05AE 0062;0061 05AE 073A 0300 0315 0062;0061 05AE 073A 0300 0315 0062;0061 05AE 073A 0300 0315 0062;0061 05AE 073A 0300 0315 0062; # (a◌ܺ◌̕◌̀◌֮b; a◌֮◌ܺ◌̀◌̕b; a◌֮◌ܺ◌̀◌̕b; a◌֮◌ܺ◌̀◌̕b; a◌֮◌ܺ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC HBASA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 073B 0062;0061 302A 0316 073B 059A 0062;0061 302A 0316 073B 059A 0062;0061 302A 0316 073B 059A 0062;0061 302A 0316 073B 059A 0062; # (a◌֚◌̖◌〪◌ܻb; a◌〪◌̖◌ܻ◌֚b; a◌〪◌̖◌ܻ◌֚b; a◌〪◌̖◌ܻ◌֚b; a◌〪◌̖◌ܻ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC HBASA BELOW, LATIN SMALL LETTER B +0061 073B 059A 0316 302A 0062;0061 302A 073B 0316 059A 0062;0061 302A 073B 0316 059A 0062;0061 302A 073B 0316 059A 0062;0061 302A 073B 0316 059A 0062; # (a◌ܻ◌֚◌̖◌〪b; a◌〪◌ܻ◌̖◌֚b; a◌〪◌ܻ◌̖◌֚b; a◌〪◌ܻ◌̖◌֚b; a◌〪◌ܻ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC HBASA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 073C 0062;0061 302A 0316 073C 059A 0062;0061 302A 0316 073C 059A 0062;0061 302A 0316 073C 059A 0062;0061 302A 0316 073C 059A 0062; # (a◌֚◌̖◌〪◌ܼb; a◌〪◌̖◌ܼ◌֚b; a◌〪◌̖◌ܼ◌֚b; a◌〪◌̖◌ܼ◌֚b; a◌〪◌̖◌ܼ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC HBASA-ESASA DOTTED, LATIN SMALL LETTER B +0061 073C 059A 0316 302A 0062;0061 302A 073C 0316 059A 0062;0061 302A 073C 0316 059A 0062;0061 302A 073C 0316 059A 0062;0061 302A 073C 0316 059A 0062; # (a◌ܼ◌֚◌̖◌〪b; a◌〪◌ܼ◌̖◌֚b; a◌〪◌ܼ◌̖◌֚b; a◌〪◌ܼ◌̖◌֚b; a◌〪◌ܼ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC HBASA-ESASA DOTTED, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 073D 0062;00E0 05AE 073D 0315 0062;0061 05AE 0300 073D 0315 0062;00E0 05AE 073D 0315 0062;0061 05AE 0300 073D 0315 0062; # (a◌̕◌̀◌֮◌ܽb; à◌֮◌ܽ◌̕b; a◌֮◌̀◌ܽ◌̕b; à◌֮◌ܽ◌̕b; a◌֮◌̀◌ܽ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC ESASA ABOVE, LATIN SMALL LETTER B +0061 073D 0315 0300 05AE 0062;0061 05AE 073D 0300 0315 0062;0061 05AE 073D 0300 0315 0062;0061 05AE 073D 0300 0315 0062;0061 05AE 073D 0300 0315 0062; # (a◌ܽ◌̕◌̀◌֮b; a◌֮◌ܽ◌̀◌̕b; a◌֮◌ܽ◌̀◌̕b; a◌֮◌ܽ◌̀◌̕b; a◌֮◌ܽ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC ESASA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 073E 0062;0061 302A 0316 073E 059A 0062;0061 302A 0316 073E 059A 0062;0061 302A 0316 073E 059A 0062;0061 302A 0316 073E 059A 0062; # (a◌֚◌̖◌〪◌ܾb; a◌〪◌̖◌ܾ◌֚b; a◌〪◌̖◌ܾ◌֚b; a◌〪◌̖◌ܾ◌֚b; a◌〪◌̖◌ܾ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC ESASA BELOW, LATIN SMALL LETTER B +0061 073E 059A 0316 302A 0062;0061 302A 073E 0316 059A 0062;0061 302A 073E 0316 059A 0062;0061 302A 073E 0316 059A 0062;0061 302A 073E 0316 059A 0062; # (a◌ܾ◌֚◌̖◌〪b; a◌〪◌ܾ◌̖◌֚b; a◌〪◌ܾ◌̖◌֚b; a◌〪◌ܾ◌̖◌֚b; a◌〪◌ܾ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC ESASA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 073F 0062;00E0 05AE 073F 0315 0062;0061 05AE 0300 073F 0315 0062;00E0 05AE 073F 0315 0062;0061 05AE 0300 073F 0315 0062; # (a◌̕◌̀◌֮◌ܿb; à◌֮◌ܿ◌̕b; a◌֮◌̀◌ܿ◌̕b; à◌֮◌ܿ◌̕b; a◌֮◌̀◌ܿ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC RWAHA, LATIN SMALL LETTER B +0061 073F 0315 0300 05AE 0062;0061 05AE 073F 0300 0315 0062;0061 05AE 073F 0300 0315 0062;0061 05AE 073F 0300 0315 0062;0061 05AE 073F 0300 0315 0062; # (a◌ܿ◌̕◌̀◌֮b; a◌֮◌ܿ◌̀◌̕b; a◌֮◌ܿ◌̀◌̕b; a◌֮◌ܿ◌̀◌̕b; a◌֮◌ܿ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC RWAHA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0740 0062;00E0 05AE 0740 0315 0062;0061 05AE 0300 0740 0315 0062;00E0 05AE 0740 0315 0062;0061 05AE 0300 0740 0315 0062; # (a◌̕◌̀◌֮◌݀b; à◌֮◌݀◌̕b; a◌֮◌̀◌݀◌̕b; à◌֮◌݀◌̕b; a◌֮◌̀◌݀◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC FEMININE DOT, LATIN SMALL LETTER B +0061 0740 0315 0300 05AE 0062;0061 05AE 0740 0300 0315 0062;0061 05AE 0740 0300 0315 0062;0061 05AE 0740 0300 0315 0062;0061 05AE 0740 0300 0315 0062; # (a◌݀◌̕◌̀◌֮b; a◌֮◌݀◌̀◌̕b; a◌֮◌݀◌̀◌̕b; a◌֮◌݀◌̀◌̕b; a◌֮◌݀◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC FEMININE DOT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0741 0062;00E0 05AE 0741 0315 0062;0061 05AE 0300 0741 0315 0062;00E0 05AE 0741 0315 0062;0061 05AE 0300 0741 0315 0062; # (a◌̕◌̀◌֮◌݁b; à◌֮◌݁◌̕b; a◌֮◌̀◌݁◌̕b; à◌֮◌݁◌̕b; a◌֮◌̀◌݁◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC QUSHSHAYA, LATIN SMALL LETTER B +0061 0741 0315 0300 05AE 0062;0061 05AE 0741 0300 0315 0062;0061 05AE 0741 0300 0315 0062;0061 05AE 0741 0300 0315 0062;0061 05AE 0741 0300 0315 0062; # (a◌݁◌̕◌̀◌֮b; a◌֮◌݁◌̀◌̕b; a◌֮◌݁◌̀◌̕b; a◌֮◌݁◌̀◌̕b; a◌֮◌݁◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC QUSHSHAYA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0742 0062;0061 302A 0316 0742 059A 0062;0061 302A 0316 0742 059A 0062;0061 302A 0316 0742 059A 0062;0061 302A 0316 0742 059A 0062; # (a◌֚◌̖◌〪◌݂b; a◌〪◌̖◌݂◌֚b; a◌〪◌̖◌݂◌֚b; a◌〪◌̖◌݂◌֚b; a◌〪◌̖◌݂◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC RUKKAKHA, LATIN SMALL LETTER B +0061 0742 059A 0316 302A 0062;0061 302A 0742 0316 059A 0062;0061 302A 0742 0316 059A 0062;0061 302A 0742 0316 059A 0062;0061 302A 0742 0316 059A 0062; # (a◌݂◌֚◌̖◌〪b; a◌〪◌݂◌̖◌֚b; a◌〪◌݂◌̖◌֚b; a◌〪◌݂◌̖◌֚b; a◌〪◌݂◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC RUKKAKHA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0743 0062;00E0 05AE 0743 0315 0062;0061 05AE 0300 0743 0315 0062;00E0 05AE 0743 0315 0062;0061 05AE 0300 0743 0315 0062; # (a◌̕◌̀◌֮◌݃b; à◌֮◌݃◌̕b; a◌֮◌̀◌݃◌̕b; à◌֮◌݃◌̕b; a◌֮◌̀◌݃◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC TWO VERTICAL DOTS ABOVE, LATIN SMALL LETTER B +0061 0743 0315 0300 05AE 0062;0061 05AE 0743 0300 0315 0062;0061 05AE 0743 0300 0315 0062;0061 05AE 0743 0300 0315 0062;0061 05AE 0743 0300 0315 0062; # (a◌݃◌̕◌̀◌֮b; a◌֮◌݃◌̀◌̕b; a◌֮◌݃◌̀◌̕b; a◌֮◌݃◌̀◌̕b; a◌֮◌݃◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC TWO VERTICAL DOTS ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0744 0062;0061 302A 0316 0744 059A 0062;0061 302A 0316 0744 059A 0062;0061 302A 0316 0744 059A 0062;0061 302A 0316 0744 059A 0062; # (a◌֚◌̖◌〪◌݄b; a◌〪◌̖◌݄◌֚b; a◌〪◌̖◌݄◌֚b; a◌〪◌̖◌݄◌֚b; a◌〪◌̖◌݄◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC TWO VERTICAL DOTS BELOW, LATIN SMALL LETTER B +0061 0744 059A 0316 302A 0062;0061 302A 0744 0316 059A 0062;0061 302A 0744 0316 059A 0062;0061 302A 0744 0316 059A 0062;0061 302A 0744 0316 059A 0062; # (a◌݄◌֚◌̖◌〪b; a◌〪◌݄◌̖◌֚b; a◌〪◌݄◌̖◌֚b; a◌〪◌݄◌̖◌֚b; a◌〪◌݄◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC TWO VERTICAL DOTS BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0745 0062;00E0 05AE 0745 0315 0062;0061 05AE 0300 0745 0315 0062;00E0 05AE 0745 0315 0062;0061 05AE 0300 0745 0315 0062; # (a◌̕◌̀◌֮◌݅b; à◌֮◌݅◌̕b; a◌֮◌̀◌݅◌̕b; à◌֮◌݅◌̕b; a◌֮◌̀◌݅◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC THREE DOTS ABOVE, LATIN SMALL LETTER B +0061 0745 0315 0300 05AE 0062;0061 05AE 0745 0300 0315 0062;0061 05AE 0745 0300 0315 0062;0061 05AE 0745 0300 0315 0062;0061 05AE 0745 0300 0315 0062; # (a◌݅◌̕◌̀◌֮b; a◌֮◌݅◌̀◌̕b; a◌֮◌݅◌̀◌̕b; a◌֮◌݅◌̀◌̕b; a◌֮◌݅◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC THREE DOTS ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0746 0062;0061 302A 0316 0746 059A 0062;0061 302A 0316 0746 059A 0062;0061 302A 0316 0746 059A 0062;0061 302A 0316 0746 059A 0062; # (a◌֚◌̖◌〪◌݆b; a◌〪◌̖◌݆◌֚b; a◌〪◌̖◌݆◌֚b; a◌〪◌̖◌݆◌֚b; a◌〪◌̖◌݆◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC THREE DOTS BELOW, LATIN SMALL LETTER B +0061 0746 059A 0316 302A 0062;0061 302A 0746 0316 059A 0062;0061 302A 0746 0316 059A 0062;0061 302A 0746 0316 059A 0062;0061 302A 0746 0316 059A 0062; # (a◌݆◌֚◌̖◌〪b; a◌〪◌݆◌̖◌֚b; a◌〪◌݆◌̖◌֚b; a◌〪◌݆◌̖◌֚b; a◌〪◌݆◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC THREE DOTS BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0747 0062;00E0 05AE 0747 0315 0062;0061 05AE 0300 0747 0315 0062;00E0 05AE 0747 0315 0062;0061 05AE 0300 0747 0315 0062; # (a◌̕◌̀◌֮◌݇b; à◌֮◌݇◌̕b; a◌֮◌̀◌݇◌̕b; à◌֮◌݇◌̕b; a◌֮◌̀◌݇◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC OBLIQUE LINE ABOVE, LATIN SMALL LETTER B +0061 0747 0315 0300 05AE 0062;0061 05AE 0747 0300 0315 0062;0061 05AE 0747 0300 0315 0062;0061 05AE 0747 0300 0315 0062;0061 05AE 0747 0300 0315 0062; # (a◌݇◌̕◌̀◌֮b; a◌֮◌݇◌̀◌̕b; a◌֮◌݇◌̀◌̕b; a◌֮◌݇◌̀◌̕b; a◌֮◌݇◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC OBLIQUE LINE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0748 0062;0061 302A 0316 0748 059A 0062;0061 302A 0316 0748 059A 0062;0061 302A 0316 0748 059A 0062;0061 302A 0316 0748 059A 0062; # (a◌֚◌̖◌〪◌݈b; a◌〪◌̖◌݈◌֚b; a◌〪◌̖◌݈◌֚b; a◌〪◌̖◌݈◌֚b; a◌〪◌̖◌݈◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC OBLIQUE LINE BELOW, LATIN SMALL LETTER B +0061 0748 059A 0316 302A 0062;0061 302A 0748 0316 059A 0062;0061 302A 0748 0316 059A 0062;0061 302A 0748 0316 059A 0062;0061 302A 0748 0316 059A 0062; # (a◌݈◌֚◌̖◌〪b; a◌〪◌݈◌̖◌֚b; a◌〪◌݈◌̖◌֚b; a◌〪◌݈◌̖◌֚b; a◌〪◌݈◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC OBLIQUE LINE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0749 0062;00E0 05AE 0749 0315 0062;0061 05AE 0300 0749 0315 0062;00E0 05AE 0749 0315 0062;0061 05AE 0300 0749 0315 0062; # (a◌̕◌̀◌֮◌݉b; à◌֮◌݉◌̕b; a◌֮◌̀◌݉◌̕b; à◌֮◌݉◌̕b; a◌֮◌̀◌݉◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC MUSIC, LATIN SMALL LETTER B +0061 0749 0315 0300 05AE 0062;0061 05AE 0749 0300 0315 0062;0061 05AE 0749 0300 0315 0062;0061 05AE 0749 0300 0315 0062;0061 05AE 0749 0300 0315 0062; # (a◌݉◌̕◌̀◌֮b; a◌֮◌݉◌̀◌̕b; a◌֮◌݉◌̀◌̕b; a◌֮◌݉◌̀◌̕b; a◌֮◌݉◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC MUSIC, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 074A 0062;00E0 05AE 074A 0315 0062;0061 05AE 0300 074A 0315 0062;00E0 05AE 074A 0315 0062;0061 05AE 0300 074A 0315 0062; # (a◌̕◌̀◌֮◌݊b; à◌֮◌݊◌̕b; a◌֮◌̀◌݊◌̕b; à◌֮◌݊◌̕b; a◌֮◌̀◌݊◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC BARREKH, LATIN SMALL LETTER B +0061 074A 0315 0300 05AE 0062;0061 05AE 074A 0300 0315 0062;0061 05AE 074A 0300 0315 0062;0061 05AE 074A 0300 0315 0062;0061 05AE 074A 0300 0315 0062; # (a◌݊◌̕◌̀◌֮b; a◌֮◌݊◌̀◌̕b; a◌֮◌݊◌̀◌̕b; a◌֮◌݊◌̀◌̕b; a◌֮◌݊◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC BARREKH, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 3099 093C 0334 093C 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062; # (a◌゙◌़◌̴◌़b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, DEVANAGARI SIGN NUKTA, LATIN SMALL LETTER B +0061 093C 3099 093C 0334 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062; # (a◌़◌゙◌़◌̴b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 05B0 094D 3099 094D 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062; # (a◌ְ◌्◌゙◌्b; a◌゙◌्◌्◌ְb; a◌゙◌्◌्◌ְb; a◌゙◌्◌्◌ְb; a◌゙◌्◌्◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN VIRAMA, LATIN SMALL LETTER B +0061 094D 05B0 094D 3099 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062; # (a◌्◌ְ◌्◌゙b; a◌゙◌्◌्◌ְb; a◌゙◌्◌्◌ְb; a◌゙◌्◌्◌ְb; a◌゙◌्◌्◌ְb; ) LATIN SMALL LETTER A, DEVANAGARI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0951 0062;00E0 05AE 0951 0315 0062;0061 05AE 0300 0951 0315 0062;00E0 05AE 0951 0315 0062;0061 05AE 0300 0951 0315 0062; # (a◌̕◌̀◌֮◌॑b; à◌֮◌॑◌̕b; a◌֮◌̀◌॑◌̕b; à◌֮◌॑◌̕b; a◌֮◌̀◌॑◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, DEVANAGARI STRESS SIGN UDATTA, LATIN SMALL LETTER B +0061 0951 0315 0300 05AE 0062;0061 05AE 0951 0300 0315 0062;0061 05AE 0951 0300 0315 0062;0061 05AE 0951 0300 0315 0062;0061 05AE 0951 0300 0315 0062; # (a◌॑◌̕◌̀◌֮b; a◌֮◌॑◌̀◌̕b; a◌֮◌॑◌̀◌̕b; a◌֮◌॑◌̀◌̕b; a◌֮◌॑◌̀◌̕b; ) LATIN SMALL LETTER A, DEVANAGARI STRESS SIGN UDATTA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0952 0062;0061 302A 0316 0952 059A 0062;0061 302A 0316 0952 059A 0062;0061 302A 0316 0952 059A 0062;0061 302A 0316 0952 059A 0062; # (a◌֚◌̖◌〪◌॒b; a◌〪◌̖◌॒◌֚b; a◌〪◌̖◌॒◌֚b; a◌〪◌̖◌॒◌֚b; a◌〪◌̖◌॒◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, DEVANAGARI STRESS SIGN ANUDATTA, LATIN SMALL LETTER B +0061 0952 059A 0316 302A 0062;0061 302A 0952 0316 059A 0062;0061 302A 0952 0316 059A 0062;0061 302A 0952 0316 059A 0062;0061 302A 0952 0316 059A 0062; # (a◌॒◌֚◌̖◌〪b; a◌〪◌॒◌̖◌֚b; a◌〪◌॒◌̖◌֚b; a◌〪◌॒◌̖◌֚b; a◌〪◌॒◌̖◌֚b; ) LATIN SMALL LETTER A, DEVANAGARI STRESS SIGN ANUDATTA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0953 0062;00E0 05AE 0953 0315 0062;0061 05AE 0300 0953 0315 0062;00E0 05AE 0953 0315 0062;0061 05AE 0300 0953 0315 0062; # (a◌̕◌̀◌֮◌॓b; à◌֮◌॓◌̕b; a◌֮◌̀◌॓◌̕b; à◌֮◌॓◌̕b; a◌֮◌̀◌॓◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, DEVANAGARI GRAVE ACCENT, LATIN SMALL LETTER B +0061 0953 0315 0300 05AE 0062;0061 05AE 0953 0300 0315 0062;0061 05AE 0953 0300 0315 0062;0061 05AE 0953 0300 0315 0062;0061 05AE 0953 0300 0315 0062; # (a◌॓◌̕◌̀◌֮b; a◌֮◌॓◌̀◌̕b; a◌֮◌॓◌̀◌̕b; a◌֮◌॓◌̀◌̕b; a◌֮◌॓◌̀◌̕b; ) LATIN SMALL LETTER A, DEVANAGARI GRAVE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0954 0062;00E0 05AE 0954 0315 0062;0061 05AE 0300 0954 0315 0062;00E0 05AE 0954 0315 0062;0061 05AE 0300 0954 0315 0062; # (a◌̕◌̀◌֮◌॔b; à◌֮◌॔◌̕b; a◌֮◌̀◌॔◌̕b; à◌֮◌॔◌̕b; a◌֮◌̀◌॔◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, DEVANAGARI ACUTE ACCENT, LATIN SMALL LETTER B +0061 0954 0315 0300 05AE 0062;0061 05AE 0954 0300 0315 0062;0061 05AE 0954 0300 0315 0062;0061 05AE 0954 0300 0315 0062;0061 05AE 0954 0300 0315 0062; # (a◌॔◌̕◌̀◌֮b; a◌֮◌॔◌̀◌̕b; a◌֮◌॔◌̀◌̕b; a◌֮◌॔◌̀◌̕b; a◌֮◌॔◌̀◌̕b; ) LATIN SMALL LETTER A, DEVANAGARI ACUTE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 3099 093C 0334 09BC 0062;0061 0334 093C 09BC 3099 0062;0061 0334 093C 09BC 3099 0062;0061 0334 093C 09BC 3099 0062;0061 0334 093C 09BC 3099 0062; # (a◌゙◌़◌̴◌়b; a◌̴◌़◌়◌゙b; a◌̴◌़◌়◌゙b; a◌̴◌़◌়◌゙b; a◌̴◌़◌়◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, BENGALI SIGN NUKTA, LATIN SMALL LETTER B +0061 09BC 3099 093C 0334 0062;0061 0334 09BC 093C 3099 0062;0061 0334 09BC 093C 3099 0062;0061 0334 09BC 093C 3099 0062;0061 0334 09BC 093C 3099 0062; # (a◌়◌゙◌़◌̴b; a◌̴◌়◌़◌゙b; a◌̴◌়◌़◌゙b; a◌̴◌়◌़◌゙b; a◌̴◌়◌़◌゙b; ) LATIN SMALL LETTER A, BENGALI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 05B0 094D 3099 09CD 0062;0061 3099 094D 09CD 05B0 0062;0061 3099 094D 09CD 05B0 0062;0061 3099 094D 09CD 05B0 0062;0061 3099 094D 09CD 05B0 0062; # (a◌ְ◌्◌゙◌্b; a◌゙◌्◌্◌ְb; a◌゙◌्◌্◌ְb; a◌゙◌्◌্◌ְb; a◌゙◌्◌্◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, BENGALI SIGN VIRAMA, LATIN SMALL LETTER B +0061 09CD 05B0 094D 3099 0062;0061 3099 09CD 094D 05B0 0062;0061 3099 09CD 094D 05B0 0062;0061 3099 09CD 094D 05B0 0062;0061 3099 09CD 094D 05B0 0062; # (a◌্◌ְ◌्◌゙b; a◌゙◌্◌्◌ְb; a◌゙◌্◌्◌ְb; a◌゙◌্◌्◌ְb; a◌゙◌্◌्◌ְb; ) LATIN SMALL LETTER A, BENGALI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 3099 093C 0334 0A3C 0062;0061 0334 093C 0A3C 3099 0062;0061 0334 093C 0A3C 3099 0062;0061 0334 093C 0A3C 3099 0062;0061 0334 093C 0A3C 3099 0062; # (a◌゙◌़◌̴◌਼b; a◌̴◌़◌਼◌゙b; a◌̴◌़◌਼◌゙b; a◌̴◌़◌਼◌゙b; a◌̴◌़◌਼◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, GURMUKHI SIGN NUKTA, LATIN SMALL LETTER B +0061 0A3C 3099 093C 0334 0062;0061 0334 0A3C 093C 3099 0062;0061 0334 0A3C 093C 3099 0062;0061 0334 0A3C 093C 3099 0062;0061 0334 0A3C 093C 3099 0062; # (a◌਼◌゙◌़◌̴b; a◌̴◌਼◌़◌゙b; a◌̴◌਼◌़◌゙b; a◌̴◌਼◌़◌゙b; a◌̴◌਼◌़◌゙b; ) LATIN SMALL LETTER A, GURMUKHI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 05B0 094D 3099 0A4D 0062;0061 3099 094D 0A4D 05B0 0062;0061 3099 094D 0A4D 05B0 0062;0061 3099 094D 0A4D 05B0 0062;0061 3099 094D 0A4D 05B0 0062; # (a◌ְ◌्◌゙◌੍b; a◌゙◌्◌੍◌ְb; a◌゙◌्◌੍◌ְb; a◌゙◌्◌੍◌ְb; a◌゙◌्◌੍◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, GURMUKHI SIGN VIRAMA, LATIN SMALL LETTER B +0061 0A4D 05B0 094D 3099 0062;0061 3099 0A4D 094D 05B0 0062;0061 3099 0A4D 094D 05B0 0062;0061 3099 0A4D 094D 05B0 0062;0061 3099 0A4D 094D 05B0 0062; # (a◌੍◌ְ◌्◌゙b; a◌゙◌੍◌्◌ְb; a◌゙◌੍◌्◌ְb; a◌゙◌੍◌्◌ְb; a◌゙◌੍◌्◌ְb; ) LATIN SMALL LETTER A, GURMUKHI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 3099 093C 0334 0ABC 0062;0061 0334 093C 0ABC 3099 0062;0061 0334 093C 0ABC 3099 0062;0061 0334 093C 0ABC 3099 0062;0061 0334 093C 0ABC 3099 0062; # (a◌゙◌़◌̴◌઼b; a◌̴◌़◌઼◌゙b; a◌̴◌़◌઼◌゙b; a◌̴◌़◌઼◌゙b; a◌̴◌़◌઼◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, GUJARATI SIGN NUKTA, LATIN SMALL LETTER B +0061 0ABC 3099 093C 0334 0062;0061 0334 0ABC 093C 3099 0062;0061 0334 0ABC 093C 3099 0062;0061 0334 0ABC 093C 3099 0062;0061 0334 0ABC 093C 3099 0062; # (a◌઼◌゙◌़◌̴b; a◌̴◌઼◌़◌゙b; a◌̴◌઼◌़◌゙b; a◌̴◌઼◌़◌゙b; a◌̴◌઼◌़◌゙b; ) LATIN SMALL LETTER A, GUJARATI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 05B0 094D 3099 0ACD 0062;0061 3099 094D 0ACD 05B0 0062;0061 3099 094D 0ACD 05B0 0062;0061 3099 094D 0ACD 05B0 0062;0061 3099 094D 0ACD 05B0 0062; # (a◌ְ◌्◌゙◌્b; a◌゙◌्◌્◌ְb; a◌゙◌्◌્◌ְb; a◌゙◌्◌્◌ְb; a◌゙◌्◌્◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, GUJARATI SIGN VIRAMA, LATIN SMALL LETTER B +0061 0ACD 05B0 094D 3099 0062;0061 3099 0ACD 094D 05B0 0062;0061 3099 0ACD 094D 05B0 0062;0061 3099 0ACD 094D 05B0 0062;0061 3099 0ACD 094D 05B0 0062; # (a◌્◌ְ◌्◌゙b; a◌゙◌્◌्◌ְb; a◌゙◌્◌्◌ְb; a◌゙◌્◌्◌ְb; a◌゙◌્◌्◌ְb; ) LATIN SMALL LETTER A, GUJARATI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 3099 093C 0334 0B3C 0062;0061 0334 093C 0B3C 3099 0062;0061 0334 093C 0B3C 3099 0062;0061 0334 093C 0B3C 3099 0062;0061 0334 093C 0B3C 3099 0062; # (a◌゙◌़◌̴◌଼b; a◌̴◌़◌଼◌゙b; a◌̴◌़◌଼◌゙b; a◌̴◌़◌଼◌゙b; a◌̴◌़◌଼◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, ORIYA SIGN NUKTA, LATIN SMALL LETTER B +0061 0B3C 3099 093C 0334 0062;0061 0334 0B3C 093C 3099 0062;0061 0334 0B3C 093C 3099 0062;0061 0334 0B3C 093C 3099 0062;0061 0334 0B3C 093C 3099 0062; # (a◌଼◌゙◌़◌̴b; a◌̴◌଼◌़◌゙b; a◌̴◌଼◌़◌゙b; a◌̴◌଼◌़◌゙b; a◌̴◌଼◌़◌゙b; ) LATIN SMALL LETTER A, ORIYA SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 05B0 094D 3099 0B4D 0062;0061 3099 094D 0B4D 05B0 0062;0061 3099 094D 0B4D 05B0 0062;0061 3099 094D 0B4D 05B0 0062;0061 3099 094D 0B4D 05B0 0062; # (a◌ְ◌्◌゙◌୍b; a◌゙◌्◌୍◌ְb; a◌゙◌्◌୍◌ְb; a◌゙◌्◌୍◌ְb; a◌゙◌्◌୍◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, ORIYA SIGN VIRAMA, LATIN SMALL LETTER B +0061 0B4D 05B0 094D 3099 0062;0061 3099 0B4D 094D 05B0 0062;0061 3099 0B4D 094D 05B0 0062;0061 3099 0B4D 094D 05B0 0062;0061 3099 0B4D 094D 05B0 0062; # (a◌୍◌ְ◌्◌゙b; a◌゙◌୍◌्◌ְb; a◌゙◌୍◌्◌ְb; a◌゙◌୍◌्◌ְb; a◌゙◌୍◌्◌ְb; ) LATIN SMALL LETTER A, ORIYA SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 05B0 094D 3099 0BCD 0062;0061 3099 094D 0BCD 05B0 0062;0061 3099 094D 0BCD 05B0 0062;0061 3099 094D 0BCD 05B0 0062;0061 3099 094D 0BCD 05B0 0062; # (a◌ְ◌्◌゙◌்b; a◌゙◌्◌்◌ְb; a◌゙◌्◌்◌ְb; a◌゙◌्◌்◌ְb; a◌゙◌्◌்◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, TAMIL SIGN VIRAMA, LATIN SMALL LETTER B +0061 0BCD 05B0 094D 3099 0062;0061 3099 0BCD 094D 05B0 0062;0061 3099 0BCD 094D 05B0 0062;0061 3099 0BCD 094D 05B0 0062;0061 3099 0BCD 094D 05B0 0062; # (a◌்◌ְ◌्◌゙b; a◌゙◌்◌्◌ְb; a◌゙◌்◌्◌ְb; a◌゙◌்◌्◌ְb; a◌゙◌்◌्◌ְb; ) LATIN SMALL LETTER A, TAMIL SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 05B0 094D 3099 0C4D 0062;0061 3099 094D 0C4D 05B0 0062;0061 3099 094D 0C4D 05B0 0062;0061 3099 094D 0C4D 05B0 0062;0061 3099 094D 0C4D 05B0 0062; # (a◌ְ◌्◌゙◌్b; a◌゙◌्◌్◌ְb; a◌゙◌्◌్◌ְb; a◌゙◌्◌్◌ְb; a◌゙◌्◌్◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, TELUGU SIGN VIRAMA, LATIN SMALL LETTER B +0061 0C4D 05B0 094D 3099 0062;0061 3099 0C4D 094D 05B0 0062;0061 3099 0C4D 094D 05B0 0062;0061 3099 0C4D 094D 05B0 0062;0061 3099 0C4D 094D 05B0 0062; # (a◌్◌ְ◌्◌゙b; a◌゙◌్◌्◌ְb; a◌゙◌్◌्◌ְb; a◌゙◌్◌्◌ְb; a◌゙◌్◌्◌ְb; ) LATIN SMALL LETTER A, TELUGU SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 0C56 0C55 0711 0C55 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062; # (a◌ౖ◌ౕ◌ܑ◌ౕb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; ) LATIN SMALL LETTER A, TELUGU AI LENGTH MARK, TELUGU LENGTH MARK, SYRIAC LETTER SUPERSCRIPT ALAPH, TELUGU LENGTH MARK, LATIN SMALL LETTER B +0061 0C55 0C56 0C55 0711 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062; # (a◌ౕ◌ౖ◌ౕ◌ܑb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; ) LATIN SMALL LETTER A, TELUGU LENGTH MARK, TELUGU AI LENGTH MARK, TELUGU LENGTH MARK, SYRIAC LETTER SUPERSCRIPT ALAPH, LATIN SMALL LETTER B +0061 0E38 0C56 0C55 0C56 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062; # (a◌ุ◌ౖ◌ౕ◌ౖb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; ) LATIN SMALL LETTER A, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, TELUGU LENGTH MARK, TELUGU AI LENGTH MARK, LATIN SMALL LETTER B +0061 0C56 0E38 0C56 0C55 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062; # (a◌ౖ◌ุ◌ౖ◌ౕb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; ) LATIN SMALL LETTER A, TELUGU AI LENGTH MARK, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, TELUGU LENGTH MARK, LATIN SMALL LETTER B +0061 05B0 094D 3099 0CCD 0062;0061 3099 094D 0CCD 05B0 0062;0061 3099 094D 0CCD 05B0 0062;0061 3099 094D 0CCD 05B0 0062;0061 3099 094D 0CCD 05B0 0062; # (a◌ְ◌्◌゙◌್b; a◌゙◌्◌್◌ְb; a◌゙◌्◌್◌ְb; a◌゙◌्◌್◌ְb; a◌゙◌्◌್◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, KANNADA SIGN VIRAMA, LATIN SMALL LETTER B +0061 0CCD 05B0 094D 3099 0062;0061 3099 0CCD 094D 05B0 0062;0061 3099 0CCD 094D 05B0 0062;0061 3099 0CCD 094D 05B0 0062;0061 3099 0CCD 094D 05B0 0062; # (a◌್◌ְ◌्◌゙b; a◌゙◌್◌्◌ְb; a◌゙◌್◌्◌ְb; a◌゙◌್◌्◌ְb; a◌゙◌್◌्◌ְb; ) LATIN SMALL LETTER A, KANNADA SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 05B0 094D 3099 0D4D 0062;0061 3099 094D 0D4D 05B0 0062;0061 3099 094D 0D4D 05B0 0062;0061 3099 094D 0D4D 05B0 0062;0061 3099 094D 0D4D 05B0 0062; # (a◌ְ◌्◌゙◌്b; a◌゙◌्◌്◌ְb; a◌゙◌्◌്◌ְb; a◌゙◌्◌്◌ְb; a◌゙◌्◌്◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, MALAYALAM SIGN VIRAMA, LATIN SMALL LETTER B +0061 0D4D 05B0 094D 3099 0062;0061 3099 0D4D 094D 05B0 0062;0061 3099 0D4D 094D 05B0 0062;0061 3099 0D4D 094D 05B0 0062;0061 3099 0D4D 094D 05B0 0062; # (a◌്◌ְ◌्◌゙b; a◌゙◌്◌्◌ְb; a◌゙◌്◌्◌ְb; a◌゙◌്◌्◌ְb; a◌゙◌്◌्◌ְb; ) LATIN SMALL LETTER A, MALAYALAM SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 05B0 094D 3099 0DCA 0062;0061 3099 094D 0DCA 05B0 0062;0061 3099 094D 0DCA 05B0 0062;0061 3099 094D 0DCA 05B0 0062;0061 3099 094D 0DCA 05B0 0062; # (a◌ְ◌्◌゙◌්b; a◌゙◌्◌්◌ְb; a◌゙◌्◌්◌ְb; a◌゙◌्◌්◌ְb; a◌゙◌्◌්◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, SINHALA SIGN AL-LAKUNA, LATIN SMALL LETTER B +0061 0DCA 05B0 094D 3099 0062;0061 3099 0DCA 094D 05B0 0062;0061 3099 0DCA 094D 05B0 0062;0061 3099 0DCA 094D 05B0 0062;0061 3099 0DCA 094D 05B0 0062; # (a◌්◌ְ◌्◌゙b; a◌゙◌්◌्◌ְb; a◌゙◌්◌्◌ְb; a◌゙◌්◌्◌ְb; a◌゙◌්◌्◌ְb; ) LATIN SMALL LETTER A, SINHALA SIGN AL-LAKUNA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 0E48 0E38 0C56 0E38 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062; # (a◌่◌ุ◌ౖ◌ุb; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; ) LATIN SMALL LETTER A, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, THAI CHARACTER SARA U, LATIN SMALL LETTER B +0061 0E38 0E48 0E38 0C56 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062; # (a◌ุ◌่◌ุ◌ౖb; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; ) LATIN SMALL LETTER A, THAI CHARACTER SARA U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, LATIN SMALL LETTER B +0061 0E48 0E38 0C56 0E39 0062;0061 0C56 0E38 0E39 0E48 0062;0061 0C56 0E38 0E39 0E48 0062;0061 0C56 0E38 0E39 0E48 0062;0061 0C56 0E38 0E39 0E48 0062; # (a◌่◌ุ◌ౖ◌ูb; a◌ౖ◌ุ◌ู◌่b; a◌ౖ◌ุ◌ู◌่b; a◌ౖ◌ุ◌ู◌่b; a◌ౖ◌ุ◌ู◌่b; ) LATIN SMALL LETTER A, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, THAI CHARACTER SARA UU, LATIN SMALL LETTER B +0061 0E39 0E48 0E38 0C56 0062;0061 0C56 0E39 0E38 0E48 0062;0061 0C56 0E39 0E38 0E48 0062;0061 0C56 0E39 0E38 0E48 0062;0061 0C56 0E39 0E38 0E48 0062; # (a◌ู◌่◌ุ◌ౖb; a◌ౖ◌ู◌ุ◌่b; a◌ౖ◌ู◌ุ◌่b; a◌ౖ◌ู◌ุ◌่b; a◌ౖ◌ู◌ุ◌่b; ) LATIN SMALL LETTER A, THAI CHARACTER SARA UU, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, LATIN SMALL LETTER B +0061 05B0 094D 3099 0E3A 0062;0061 3099 094D 0E3A 05B0 0062;0061 3099 094D 0E3A 05B0 0062;0061 3099 094D 0E3A 05B0 0062;0061 3099 094D 0E3A 05B0 0062; # (a◌ְ◌्◌゙◌ฺb; a◌゙◌्◌ฺ◌ְb; a◌゙◌्◌ฺ◌ְb; a◌゙◌्◌ฺ◌ְb; a◌゙◌्◌ฺ◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, THAI CHARACTER PHINTHU, LATIN SMALL LETTER B +0061 0E3A 05B0 094D 3099 0062;0061 3099 0E3A 094D 05B0 0062;0061 3099 0E3A 094D 05B0 0062;0061 3099 0E3A 094D 05B0 0062;0061 3099 0E3A 094D 05B0 0062; # (a◌ฺ◌ְ◌्◌゙b; a◌゙◌ฺ◌्◌ְb; a◌゙◌ฺ◌्◌ְb; a◌゙◌ฺ◌्◌ְb; a◌゙◌ฺ◌्◌ְb; ) LATIN SMALL LETTER A, THAI CHARACTER PHINTHU, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 0EB8 0E48 0E38 0E48 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062; # (a◌ຸ◌่◌ุ◌่b; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B +0061 0E48 0EB8 0E48 0E38 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062; # (a◌่◌ຸ◌่◌ุb; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; ) LATIN SMALL LETTER A, THAI CHARACTER MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, LATIN SMALL LETTER B +0061 0EB8 0E48 0E38 0E49 0062;0061 0E38 0E48 0E49 0EB8 0062;0061 0E38 0E48 0E49 0EB8 0062;0061 0E38 0E48 0E49 0EB8 0062;0061 0E38 0E48 0E49 0EB8 0062; # (a◌ຸ◌่◌ุ◌้b; a◌ุ◌่◌้◌ຸb; a◌ุ◌่◌้◌ຸb; a◌ุ◌่◌้◌ຸb; a◌ุ◌่◌้◌ຸb; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, THAI CHARACTER MAI THO, LATIN SMALL LETTER B +0061 0E49 0EB8 0E48 0E38 0062;0061 0E38 0E49 0E48 0EB8 0062;0061 0E38 0E49 0E48 0EB8 0062;0061 0E38 0E49 0E48 0EB8 0062;0061 0E38 0E49 0E48 0EB8 0062; # (a◌้◌ຸ◌่◌ุb; a◌ุ◌้◌่◌ຸb; a◌ุ◌้◌่◌ຸb; a◌ุ◌้◌่◌ຸb; a◌ุ◌้◌่◌ຸb; ) LATIN SMALL LETTER A, THAI CHARACTER MAI THO, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, LATIN SMALL LETTER B +0061 0EB8 0E48 0E38 0E4A 0062;0061 0E38 0E48 0E4A 0EB8 0062;0061 0E38 0E48 0E4A 0EB8 0062;0061 0E38 0E48 0E4A 0EB8 0062;0061 0E38 0E48 0E4A 0EB8 0062; # (a◌ຸ◌่◌ุ◌๊b; a◌ุ◌่◌๊◌ຸb; a◌ุ◌่◌๊◌ຸb; a◌ุ◌่◌๊◌ຸb; a◌ุ◌่◌๊◌ຸb; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, THAI CHARACTER MAI TRI, LATIN SMALL LETTER B +0061 0E4A 0EB8 0E48 0E38 0062;0061 0E38 0E4A 0E48 0EB8 0062;0061 0E38 0E4A 0E48 0EB8 0062;0061 0E38 0E4A 0E48 0EB8 0062;0061 0E38 0E4A 0E48 0EB8 0062; # (a◌๊◌ຸ◌่◌ุb; a◌ุ◌๊◌่◌ຸb; a◌ุ◌๊◌่◌ຸb; a◌ุ◌๊◌่◌ຸb; a◌ุ◌๊◌่◌ຸb; ) LATIN SMALL LETTER A, THAI CHARACTER MAI TRI, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, LATIN SMALL LETTER B +0061 0EB8 0E48 0E38 0E4B 0062;0061 0E38 0E48 0E4B 0EB8 0062;0061 0E38 0E48 0E4B 0EB8 0062;0061 0E38 0E48 0E4B 0EB8 0062;0061 0E38 0E48 0E4B 0EB8 0062; # (a◌ຸ◌่◌ุ◌๋b; a◌ุ◌่◌๋◌ຸb; a◌ุ◌่◌๋◌ຸb; a◌ุ◌่◌๋◌ຸb; a◌ุ◌่◌๋◌ຸb; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, THAI CHARACTER MAI CHATTAWA, LATIN SMALL LETTER B +0061 0E4B 0EB8 0E48 0E38 0062;0061 0E38 0E4B 0E48 0EB8 0062;0061 0E38 0E4B 0E48 0EB8 0062;0061 0E38 0E4B 0E48 0EB8 0062;0061 0E38 0E4B 0E48 0EB8 0062; # (a◌๋◌ຸ◌่◌ุb; a◌ุ◌๋◌่◌ຸb; a◌ุ◌๋◌่◌ຸb; a◌ุ◌๋◌่◌ຸb; a◌ุ◌๋◌่◌ຸb; ) LATIN SMALL LETTER A, THAI CHARACTER MAI CHATTAWA, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, LATIN SMALL LETTER B +0061 0EC8 0EB8 0E48 0EB8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062; # (a◌່◌ຸ◌่◌ຸb; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B +0061 0EB8 0EC8 0EB8 0E48 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062; # (a◌ຸ◌່◌ຸ◌่b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B +0061 0EC8 0EB8 0E48 0EB9 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062; # (a◌່◌ຸ◌่◌ູb; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; ) LATIN SMALL LETTER A, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LAO VOWEL SIGN UU, LATIN SMALL LETTER B +0061 0EB9 0EC8 0EB8 0E48 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062; # (a◌ູ◌່◌ຸ◌่b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO VOWEL SIGN UU, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B +0061 0F71 0EC8 0EB8 0EC8 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062; # (a◌ཱ◌່◌ຸ◌່b; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI EK, LATIN SMALL LETTER B +0061 0EC8 0F71 0EC8 0EB8 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062; # (a◌່◌ཱ◌່◌ຸb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI EK, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B +0061 0F71 0EC8 0EB8 0EC9 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062; # (a◌ཱ◌່◌ຸ◌້b; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI THO, LATIN SMALL LETTER B +0061 0EC9 0F71 0EC8 0EB8 0062;0061 0EB8 0EC9 0EC8 0F71 0062;0061 0EB8 0EC9 0EC8 0F71 0062;0061 0EB8 0EC9 0EC8 0F71 0062;0061 0EB8 0EC9 0EC8 0F71 0062; # (a◌້◌ཱ◌່◌ຸb; a◌ຸ◌້◌່◌ཱb; a◌ຸ◌້◌່◌ཱb; a◌ຸ◌້◌່◌ཱb; a◌ຸ◌້◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI THO, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B +0061 0F71 0EC8 0EB8 0ECA 0062;0061 0EB8 0EC8 0ECA 0F71 0062;0061 0EB8 0EC8 0ECA 0F71 0062;0061 0EB8 0EC8 0ECA 0F71 0062;0061 0EB8 0EC8 0ECA 0F71 0062; # (a◌ཱ◌່◌ຸ◌໊b; a◌ຸ◌່◌໊◌ཱb; a◌ຸ◌່◌໊◌ཱb; a◌ຸ◌່◌໊◌ཱb; a◌ຸ◌່◌໊◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI TI, LATIN SMALL LETTER B +0061 0ECA 0F71 0EC8 0EB8 0062;0061 0EB8 0ECA 0EC8 0F71 0062;0061 0EB8 0ECA 0EC8 0F71 0062;0061 0EB8 0ECA 0EC8 0F71 0062;0061 0EB8 0ECA 0EC8 0F71 0062; # (a◌໊◌ཱ◌່◌ຸb; a◌ຸ◌໊◌່◌ཱb; a◌ຸ◌໊◌່◌ཱb; a◌ຸ◌໊◌່◌ཱb; a◌ຸ◌໊◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI TI, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B +0061 0F71 0EC8 0EB8 0ECB 0062;0061 0EB8 0EC8 0ECB 0F71 0062;0061 0EB8 0EC8 0ECB 0F71 0062;0061 0EB8 0EC8 0ECB 0F71 0062;0061 0EB8 0EC8 0ECB 0F71 0062; # (a◌ཱ◌່◌ຸ◌໋b; a◌ຸ◌່◌໋◌ཱb; a◌ຸ◌່◌໋◌ཱb; a◌ຸ◌່◌໋◌ཱb; a◌ຸ◌່◌໋◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI CATAWA, LATIN SMALL LETTER B +0061 0ECB 0F71 0EC8 0EB8 0062;0061 0EB8 0ECB 0EC8 0F71 0062;0061 0EB8 0ECB 0EC8 0F71 0062;0061 0EB8 0ECB 0EC8 0F71 0062;0061 0EB8 0ECB 0EC8 0F71 0062; # (a◌໋◌ཱ◌່◌ຸb; a◌ຸ◌໋◌່◌ཱb; a◌ຸ◌໋◌່◌ཱb; a◌ຸ◌໋◌່◌ཱb; a◌ຸ◌໋◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI CATAWA, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B +0061 059A 0316 302A 0F18 0062;0061 302A 0316 0F18 059A 0062;0061 302A 0316 0F18 059A 0062;0061 302A 0316 0F18 059A 0062;0061 302A 0316 0F18 059A 0062; # (a◌֚◌̖◌〪◌༘b; a◌〪◌̖◌༘◌֚b; a◌〪◌̖◌༘◌֚b; a◌〪◌̖◌༘◌֚b; a◌〪◌̖◌༘◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, TIBETAN ASTROLOGICAL SIGN -KHYUD PA, LATIN SMALL LETTER B +0061 0F18 059A 0316 302A 0062;0061 302A 0F18 0316 059A 0062;0061 302A 0F18 0316 059A 0062;0061 302A 0F18 0316 059A 0062;0061 302A 0F18 0316 059A 0062; # (a◌༘◌֚◌̖◌〪b; a◌〪◌༘◌̖◌֚b; a◌〪◌༘◌̖◌֚b; a◌〪◌༘◌̖◌֚b; a◌〪◌༘◌̖◌֚b; ) LATIN SMALL LETTER A, TIBETAN ASTROLOGICAL SIGN -KHYUD PA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0F19 0062;0061 302A 0316 0F19 059A 0062;0061 302A 0316 0F19 059A 0062;0061 302A 0316 0F19 059A 0062;0061 302A 0316 0F19 059A 0062; # (a◌֚◌̖◌〪◌༙b; a◌〪◌̖◌༙◌֚b; a◌〪◌̖◌༙◌֚b; a◌〪◌̖◌༙◌֚b; a◌〪◌̖◌༙◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS, LATIN SMALL LETTER B +0061 0F19 059A 0316 302A 0062;0061 302A 0F19 0316 059A 0062;0061 302A 0F19 0316 059A 0062;0061 302A 0F19 0316 059A 0062;0061 302A 0F19 0316 059A 0062; # (a◌༙◌֚◌̖◌〪b; a◌〪◌༙◌̖◌֚b; a◌〪◌༙◌̖◌֚b; a◌〪◌༙◌̖◌֚b; a◌〪◌༙◌̖◌֚b; ) LATIN SMALL LETTER A, TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0F35 0062;0061 302A 0316 0F35 059A 0062;0061 302A 0316 0F35 059A 0062;0061 302A 0316 0F35 059A 0062;0061 302A 0316 0F35 059A 0062; # (a◌֚◌̖◌〪◌༵b; a◌〪◌̖◌༵◌֚b; a◌〪◌̖◌༵◌֚b; a◌〪◌̖◌༵◌֚b; a◌〪◌̖◌༵◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, TIBETAN MARK NGAS BZUNG NYI ZLA, LATIN SMALL LETTER B +0061 0F35 059A 0316 302A 0062;0061 302A 0F35 0316 059A 0062;0061 302A 0F35 0316 059A 0062;0061 302A 0F35 0316 059A 0062;0061 302A 0F35 0316 059A 0062; # (a◌༵◌֚◌̖◌〪b; a◌〪◌༵◌̖◌֚b; a◌〪◌༵◌̖◌֚b; a◌〪◌༵◌̖◌֚b; a◌〪◌༵◌̖◌֚b; ) LATIN SMALL LETTER A, TIBETAN MARK NGAS BZUNG NYI ZLA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 0F37 0062;0061 302A 0316 0F37 059A 0062;0061 302A 0316 0F37 059A 0062;0061 302A 0316 0F37 059A 0062;0061 302A 0316 0F37 059A 0062; # (a◌֚◌̖◌〪◌༷b; a◌〪◌̖◌༷◌֚b; a◌〪◌̖◌༷◌֚b; a◌〪◌̖◌༷◌֚b; a◌〪◌̖◌༷◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, TIBETAN MARK NGAS BZUNG SGOR RTAGS, LATIN SMALL LETTER B +0061 0F37 059A 0316 302A 0062;0061 302A 0F37 0316 059A 0062;0061 302A 0F37 0316 059A 0062;0061 302A 0F37 0316 059A 0062;0061 302A 0F37 0316 059A 0062; # (a◌༷◌֚◌̖◌〪b; a◌〪◌༷◌̖◌֚b; a◌〪◌༷◌̖◌֚b; a◌〪◌༷◌̖◌֚b; a◌〪◌༷◌̖◌֚b; ) LATIN SMALL LETTER A, TIBETAN MARK NGAS BZUNG SGOR RTAGS, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 302A 031B 0321 0F39 0062;0061 0321 031B 0F39 302A 0062;0061 0321 031B 0F39 302A 0062;0061 0321 031B 0F39 302A 0062;0061 0321 031B 0F39 302A 0062; # (a◌〪◌̛◌̡◌༹b; a◌̡◌̛◌༹◌〪b; a◌̡◌̛◌༹◌〪b; a◌̡◌̛◌༹◌〪b; a◌̡◌̛◌༹◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN MARK TSA -PHRU, LATIN SMALL LETTER B +0061 0F39 302A 031B 0321 0062;0061 0321 0F39 031B 302A 0062;0061 0321 0F39 031B 302A 0062;0061 0321 0F39 031B 302A 0062;0061 0321 0F39 031B 302A 0062; # (a◌༹◌〪◌̛◌̡b; a◌̡◌༹◌̛◌〪b; a◌̡◌༹◌̛◌〪b; a◌̡◌༹◌̛◌〪b; a◌̡◌༹◌̛◌〪b; ) LATIN SMALL LETTER A, TIBETAN MARK TSA -PHRU, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B +0061 0F72 0F71 0EC8 0F71 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062; # (a◌ི◌ཱ◌່◌ཱb; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B +0061 0F71 0F72 0F71 0EC8 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062; # (a◌ཱ◌ི◌ཱ◌່b; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LATIN SMALL LETTER B +0061 0F74 0F72 0F71 0F72 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062; # (a◌ུ◌ི◌ཱ◌ིb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN I, LATIN SMALL LETTER B +0061 0F72 0F74 0F72 0F71 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062; # (a◌ི◌ུ◌ི◌ཱb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B +0061 0321 0F74 0F72 0F74 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062; # (a◌̡◌ུ◌ི◌ུb; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; ) LATIN SMALL LETTER A, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN U, LATIN SMALL LETTER B +0061 0F74 0321 0F74 0F72 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062; # (a◌ུ◌̡◌ུ◌ིb; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, LATIN SMALL LETTER B +0061 0F74 0F72 0F71 0F7A 0062;0061 0F71 0F72 0F7A 0F74 0062;0061 0F71 0F72 0F7A 0F74 0062;0061 0F71 0F72 0F7A 0F74 0062;0061 0F71 0F72 0F7A 0F74 0062; # (a◌ུ◌ི◌ཱ◌ེb; a◌ཱ◌ི◌ེ◌ུb; a◌ཱ◌ི◌ེ◌ུb; a◌ཱ◌ི◌ེ◌ུb; a◌ཱ◌ི◌ེ◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN E, LATIN SMALL LETTER B +0061 0F7A 0F74 0F72 0F71 0062;0061 0F71 0F7A 0F72 0F74 0062;0061 0F71 0F7A 0F72 0F74 0062;0061 0F71 0F7A 0F72 0F74 0062;0061 0F71 0F7A 0F72 0F74 0062; # (a◌ེ◌ུ◌ི◌ཱb; a◌ཱ◌ེ◌ི◌ུb; a◌ཱ◌ེ◌ི◌ུb; a◌ཱ◌ེ◌ི◌ུb; a◌ཱ◌ེ◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN E, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B +0061 0F74 0F72 0F71 0F7B 0062;0061 0F71 0F72 0F7B 0F74 0062;0061 0F71 0F72 0F7B 0F74 0062;0061 0F71 0F72 0F7B 0F74 0062;0061 0F71 0F72 0F7B 0F74 0062; # (a◌ུ◌ི◌ཱ◌ཻb; a◌ཱ◌ི◌ཻ◌ུb; a◌ཱ◌ི◌ཻ◌ུb; a◌ཱ◌ི◌ཻ◌ུb; a◌ཱ◌ི◌ཻ◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN EE, LATIN SMALL LETTER B +0061 0F7B 0F74 0F72 0F71 0062;0061 0F71 0F7B 0F72 0F74 0062;0061 0F71 0F7B 0F72 0F74 0062;0061 0F71 0F7B 0F72 0F74 0062;0061 0F71 0F7B 0F72 0F74 0062; # (a◌ཻ◌ུ◌ི◌ཱb; a◌ཱ◌ཻ◌ི◌ུb; a◌ཱ◌ཻ◌ི◌ུb; a◌ཱ◌ཻ◌ི◌ུb; a◌ཱ◌ཻ◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN EE, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B +0061 0F74 0F72 0F71 0F7C 0062;0061 0F71 0F72 0F7C 0F74 0062;0061 0F71 0F72 0F7C 0F74 0062;0061 0F71 0F72 0F7C 0F74 0062;0061 0F71 0F72 0F7C 0F74 0062; # (a◌ུ◌ི◌ཱ◌ོb; a◌ཱ◌ི◌ོ◌ུb; a◌ཱ◌ི◌ོ◌ུb; a◌ཱ◌ི◌ོ◌ུb; a◌ཱ◌ི◌ོ◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN O, LATIN SMALL LETTER B +0061 0F7C 0F74 0F72 0F71 0062;0061 0F71 0F7C 0F72 0F74 0062;0061 0F71 0F7C 0F72 0F74 0062;0061 0F71 0F7C 0F72 0F74 0062;0061 0F71 0F7C 0F72 0F74 0062; # (a◌ོ◌ུ◌ི◌ཱb; a◌ཱ◌ོ◌ི◌ུb; a◌ཱ◌ོ◌ི◌ུb; a◌ཱ◌ོ◌ི◌ུb; a◌ཱ◌ོ◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN O, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B +0061 0F74 0F72 0F71 0F7D 0062;0061 0F71 0F72 0F7D 0F74 0062;0061 0F71 0F72 0F7D 0F74 0062;0061 0F71 0F72 0F7D 0F74 0062;0061 0F71 0F72 0F7D 0F74 0062; # (a◌ུ◌ི◌ཱ◌ཽb; a◌ཱ◌ི◌ཽ◌ུb; a◌ཱ◌ི◌ཽ◌ུb; a◌ཱ◌ི◌ཽ◌ུb; a◌ཱ◌ི◌ཽ◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN OO, LATIN SMALL LETTER B +0061 0F7D 0F74 0F72 0F71 0062;0061 0F71 0F7D 0F72 0F74 0062;0061 0F71 0F7D 0F72 0F74 0062;0061 0F71 0F7D 0F72 0F74 0062;0061 0F71 0F7D 0F72 0F74 0062; # (a◌ཽ◌ུ◌ི◌ཱb; a◌ཱ◌ཽ◌ི◌ུb; a◌ཱ◌ཽ◌ི◌ུb; a◌ཱ◌ཽ◌ི◌ུb; a◌ཱ◌ཽ◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN OO, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B +0061 0F74 0F72 0F71 0F80 0062;0061 0F71 0F72 0F80 0F74 0062;0061 0F71 0F72 0F80 0F74 0062;0061 0F71 0F72 0F80 0F74 0062;0061 0F71 0F72 0F80 0F74 0062; # (a◌ུ◌ི◌ཱ◌ྀb; a◌ཱ◌ི◌ྀ◌ུb; a◌ཱ◌ི◌ྀ◌ུb; a◌ཱ◌ི◌ྀ◌ུb; a◌ཱ◌ི◌ྀ◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN REVERSED I, LATIN SMALL LETTER B +0061 0F80 0F74 0F72 0F71 0062;0061 0F71 0F80 0F72 0F74 0062;0061 0F71 0F80 0F72 0F74 0062;0061 0F71 0F80 0F72 0F74 0062;0061 0F71 0F80 0F72 0F74 0062; # (a◌ྀ◌ུ◌ི◌ཱb; a◌ཱ◌ྀ◌ི◌ུb; a◌ཱ◌ྀ◌ི◌ུb; a◌ཱ◌ྀ◌ི◌ུb; a◌ཱ◌ྀ◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN REVERSED I, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B +0061 0315 0300 05AE 0F82 0062;00E0 05AE 0F82 0315 0062;0061 05AE 0300 0F82 0315 0062;00E0 05AE 0F82 0315 0062;0061 05AE 0300 0F82 0315 0062; # (a◌̕◌̀◌֮◌ྂb; à◌֮◌ྂ◌̕b; a◌֮◌̀◌ྂ◌̕b; à◌֮◌ྂ◌̕b; a◌֮◌̀◌ྂ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, TIBETAN SIGN NYI ZLA NAA DA, LATIN SMALL LETTER B +0061 0F82 0315 0300 05AE 0062;0061 05AE 0F82 0300 0315 0062;0061 05AE 0F82 0300 0315 0062;0061 05AE 0F82 0300 0315 0062;0061 05AE 0F82 0300 0315 0062; # (a◌ྂ◌̕◌̀◌֮b; a◌֮◌ྂ◌̀◌̕b; a◌֮◌ྂ◌̀◌̕b; a◌֮◌ྂ◌̀◌̕b; a◌֮◌ྂ◌̀◌̕b; ) LATIN SMALL LETTER A, TIBETAN SIGN NYI ZLA NAA DA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0F83 0062;00E0 05AE 0F83 0315 0062;0061 05AE 0300 0F83 0315 0062;00E0 05AE 0F83 0315 0062;0061 05AE 0300 0F83 0315 0062; # (a◌̕◌̀◌֮◌ྃb; à◌֮◌ྃ◌̕b; a◌֮◌̀◌ྃ◌̕b; à◌֮◌ྃ◌̕b; a◌֮◌̀◌ྃ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, TIBETAN SIGN SNA LDAN, LATIN SMALL LETTER B +0061 0F83 0315 0300 05AE 0062;0061 05AE 0F83 0300 0315 0062;0061 05AE 0F83 0300 0315 0062;0061 05AE 0F83 0300 0315 0062;0061 05AE 0F83 0300 0315 0062; # (a◌ྃ◌̕◌̀◌֮b; a◌֮◌ྃ◌̀◌̕b; a◌֮◌ྃ◌̀◌̕b; a◌֮◌ྃ◌̀◌̕b; a◌֮◌ྃ◌̀◌̕b; ) LATIN SMALL LETTER A, TIBETAN SIGN SNA LDAN, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 05B0 094D 3099 0F84 0062;0061 3099 094D 0F84 05B0 0062;0061 3099 094D 0F84 05B0 0062;0061 3099 094D 0F84 05B0 0062;0061 3099 094D 0F84 05B0 0062; # (a◌ְ◌्◌゙◌྄b; a◌゙◌्◌྄◌ְb; a◌゙◌्◌྄◌ְb; a◌゙◌्◌྄◌ְb; a◌゙◌्◌྄◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, TIBETAN MARK HALANTA, LATIN SMALL LETTER B +0061 0F84 05B0 094D 3099 0062;0061 3099 0F84 094D 05B0 0062;0061 3099 0F84 094D 05B0 0062;0061 3099 0F84 094D 05B0 0062;0061 3099 0F84 094D 05B0 0062; # (a◌྄◌ְ◌्◌゙b; a◌゙◌྄◌्◌ְb; a◌゙◌྄◌्◌ְb; a◌゙◌྄◌्◌ְb; a◌゙◌྄◌्◌ְb; ) LATIN SMALL LETTER A, TIBETAN MARK HALANTA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 0F86 0062;00E0 05AE 0F86 0315 0062;0061 05AE 0300 0F86 0315 0062;00E0 05AE 0F86 0315 0062;0061 05AE 0300 0F86 0315 0062; # (a◌̕◌̀◌֮◌྆b; à◌֮◌྆◌̕b; a◌֮◌̀◌྆◌̕b; à◌֮◌྆◌̕b; a◌֮◌̀◌྆◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, TIBETAN SIGN LCI RTAGS, LATIN SMALL LETTER B +0061 0F86 0315 0300 05AE 0062;0061 05AE 0F86 0300 0315 0062;0061 05AE 0F86 0300 0315 0062;0061 05AE 0F86 0300 0315 0062;0061 05AE 0F86 0300 0315 0062; # (a◌྆◌̕◌̀◌֮b; a◌֮◌྆◌̀◌̕b; a◌֮◌྆◌̀◌̕b; a◌֮◌྆◌̀◌̕b; a◌֮◌྆◌̀◌̕b; ) LATIN SMALL LETTER A, TIBETAN SIGN LCI RTAGS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 0F87 0062;00E0 05AE 0F87 0315 0062;0061 05AE 0300 0F87 0315 0062;00E0 05AE 0F87 0315 0062;0061 05AE 0300 0F87 0315 0062; # (a◌̕◌̀◌֮◌྇b; à◌֮◌྇◌̕b; a◌֮◌̀◌྇◌̕b; à◌֮◌྇◌̕b; a◌֮◌̀◌྇◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, TIBETAN SIGN YANG RTAGS, LATIN SMALL LETTER B +0061 0F87 0315 0300 05AE 0062;0061 05AE 0F87 0300 0315 0062;0061 05AE 0F87 0300 0315 0062;0061 05AE 0F87 0300 0315 0062;0061 05AE 0F87 0300 0315 0062; # (a◌྇◌̕◌̀◌֮b; a◌֮◌྇◌̀◌̕b; a◌֮◌྇◌̀◌̕b; a◌֮◌྇◌̀◌̕b; a◌֮◌྇◌̀◌̕b; ) LATIN SMALL LETTER A, TIBETAN SIGN YANG RTAGS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 0FC6 0062;0061 302A 0316 0FC6 059A 0062;0061 302A 0316 0FC6 059A 0062;0061 302A 0316 0FC6 059A 0062;0061 302A 0316 0FC6 059A 0062; # (a◌֚◌̖◌〪◌࿆b; a◌〪◌̖◌࿆◌֚b; a◌〪◌̖◌࿆◌֚b; a◌〪◌̖◌࿆◌֚b; a◌〪◌̖◌࿆◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, TIBETAN SYMBOL PADMA GDAN, LATIN SMALL LETTER B +0061 0FC6 059A 0316 302A 0062;0061 302A 0FC6 0316 059A 0062;0061 302A 0FC6 0316 059A 0062;0061 302A 0FC6 0316 059A 0062;0061 302A 0FC6 0316 059A 0062; # (a◌࿆◌֚◌̖◌〪b; a◌〪◌࿆◌̖◌֚b; a◌〪◌࿆◌̖◌֚b; a◌〪◌࿆◌̖◌֚b; a◌〪◌࿆◌̖◌֚b; ) LATIN SMALL LETTER A, TIBETAN SYMBOL PADMA GDAN, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 3099 093C 0334 1037 0062;0061 0334 093C 1037 3099 0062;0061 0334 093C 1037 3099 0062;0061 0334 093C 1037 3099 0062;0061 0334 093C 1037 3099 0062; # (a◌゙◌़◌̴◌့b; a◌̴◌़◌့◌゙b; a◌̴◌़◌့◌゙b; a◌̴◌़◌့◌゙b; a◌̴◌़◌့◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, MYANMAR SIGN DOT BELOW, LATIN SMALL LETTER B +0061 1037 3099 093C 0334 0062;0061 0334 1037 093C 3099 0062;0061 0334 1037 093C 3099 0062;0061 0334 1037 093C 3099 0062;0061 0334 1037 093C 3099 0062; # (a◌့◌゙◌़◌̴b; a◌̴◌့◌़◌゙b; a◌̴◌့◌़◌゙b; a◌̴◌့◌़◌゙b; a◌̴◌့◌़◌゙b; ) LATIN SMALL LETTER A, MYANMAR SIGN DOT BELOW, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 05B0 094D 3099 1039 0062;0061 3099 094D 1039 05B0 0062;0061 3099 094D 1039 05B0 0062;0061 3099 094D 1039 05B0 0062;0061 3099 094D 1039 05B0 0062; # (a◌ְ◌्◌゙◌္b; a◌゙◌्◌္◌ְb; a◌゙◌्◌္◌ְb; a◌゙◌्◌္◌ְb; a◌゙◌्◌္◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, MYANMAR SIGN VIRAMA, LATIN SMALL LETTER B +0061 1039 05B0 094D 3099 0062;0061 3099 1039 094D 05B0 0062;0061 3099 1039 094D 05B0 0062;0061 3099 1039 094D 05B0 0062;0061 3099 1039 094D 05B0 0062; # (a◌္◌ְ◌्◌゙b; a◌゙◌္◌्◌ְb; a◌゙◌္◌्◌ְb; a◌゙◌္◌्◌ְb; a◌゙◌္◌्◌ְb; ) LATIN SMALL LETTER A, MYANMAR SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 05B0 094D 3099 1714 0062;0061 3099 094D 1714 05B0 0062;0061 3099 094D 1714 05B0 0062;0061 3099 094D 1714 05B0 0062;0061 3099 094D 1714 05B0 0062; # (a◌ְ◌्◌゙◌᜔b; a◌゙◌्◌᜔◌ְb; a◌゙◌्◌᜔◌ְb; a◌゙◌्◌᜔◌ְb; a◌゙◌्◌᜔◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, TAGALOG SIGN VIRAMA, LATIN SMALL LETTER B +0061 1714 05B0 094D 3099 0062;0061 3099 1714 094D 05B0 0062;0061 3099 1714 094D 05B0 0062;0061 3099 1714 094D 05B0 0062;0061 3099 1714 094D 05B0 0062; # (a◌᜔◌ְ◌्◌゙b; a◌゙◌᜔◌्◌ְb; a◌゙◌᜔◌्◌ְb; a◌゙◌᜔◌्◌ְb; a◌゙◌᜔◌्◌ְb; ) LATIN SMALL LETTER A, TAGALOG SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 05B0 094D 3099 1734 0062;0061 3099 094D 1734 05B0 0062;0061 3099 094D 1734 05B0 0062;0061 3099 094D 1734 05B0 0062;0061 3099 094D 1734 05B0 0062; # (a◌ְ◌्◌゙◌᜴b; a◌゙◌्◌᜴◌ְb; a◌゙◌्◌᜴◌ְb; a◌゙◌्◌᜴◌ְb; a◌゙◌्◌᜴◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, HANUNOO SIGN PAMUDPOD, LATIN SMALL LETTER B +0061 1734 05B0 094D 3099 0062;0061 3099 1734 094D 05B0 0062;0061 3099 1734 094D 05B0 0062;0061 3099 1734 094D 05B0 0062;0061 3099 1734 094D 05B0 0062; # (a◌᜴◌ְ◌्◌゙b; a◌゙◌᜴◌्◌ְb; a◌゙◌᜴◌्◌ְb; a◌゙◌᜴◌्◌ְb; a◌゙◌᜴◌्◌ְb; ) LATIN SMALL LETTER A, HANUNOO SIGN PAMUDPOD, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 05B0 094D 3099 17D2 0062;0061 3099 094D 17D2 05B0 0062;0061 3099 094D 17D2 05B0 0062;0061 3099 094D 17D2 05B0 0062;0061 3099 094D 17D2 05B0 0062; # (a◌ְ◌्◌゙◌្b; a◌゙◌्◌្◌ְb; a◌゙◌्◌្◌ְb; a◌゙◌्◌្◌ְb; a◌゙◌्◌្◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, KHMER SIGN COENG, LATIN SMALL LETTER B +0061 17D2 05B0 094D 3099 0062;0061 3099 17D2 094D 05B0 0062;0061 3099 17D2 094D 05B0 0062;0061 3099 17D2 094D 05B0 0062;0061 3099 17D2 094D 05B0 0062; # (a◌្◌ְ◌्◌゙b; a◌゙◌្◌्◌ְb; a◌゙◌្◌्◌ְb; a◌゙◌្◌्◌ְb; a◌゙◌្◌्◌ְb; ) LATIN SMALL LETTER A, KHMER SIGN COENG, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 0300 05AE 1D16D 18A9 0062;00E0 1D16D 05AE 18A9 0062;0061 1D16D 05AE 18A9 0300 0062;00E0 1D16D 05AE 18A9 0062;0061 1D16D 05AE 18A9 0300 0062; # (a◌̀◌𝅭𝅭֮◌ᢩb; à𝅭𝅭◌֮◌ᢩb; a𝅭𝅭◌֮◌ᢩ◌̀b; à𝅭𝅭◌֮◌ᢩb; a𝅭𝅭◌֮◌ᢩ◌̀b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, MONGOLIAN LETTER ALI GALI DAGALGA, LATIN SMALL LETTER B +0061 18A9 0300 05AE 1D16D 0062;00E0 1D16D 18A9 05AE 0062;0061 1D16D 18A9 05AE 0300 0062;00E0 1D16D 18A9 05AE 0062;0061 1D16D 18A9 05AE 0300 0062; # (a◌ᢩ◌̀◌𝅭𝅭֮b; à𝅭𝅭◌ᢩ◌֮b; a𝅭𝅭◌ᢩ◌֮◌̀b; à𝅭𝅭◌ᢩ◌֮b; a𝅭𝅭◌ᢩ◌֮◌̀b; ) LATIN SMALL LETTER A, MONGOLIAN LETTER ALI GALI DAGALGA, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, LATIN SMALL LETTER B +0061 0315 0300 05AE 20D0 0062;00E0 05AE 20D0 0315 0062;0061 05AE 0300 20D0 0315 0062;00E0 05AE 20D0 0315 0062;0061 05AE 0300 20D0 0315 0062; # (a◌̕◌̀◌֮◌⃐b; à◌֮◌⃐◌̕b; a◌֮◌̀◌⃐◌̕b; à◌֮◌⃐◌̕b; a◌֮◌̀◌⃐◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LEFT HARPOON ABOVE, LATIN SMALL LETTER B +0061 20D0 0315 0300 05AE 0062;0061 05AE 20D0 0300 0315 0062;0061 05AE 20D0 0300 0315 0062;0061 05AE 20D0 0300 0315 0062;0061 05AE 20D0 0300 0315 0062; # (a◌⃐◌̕◌̀◌֮b; a◌֮◌⃐◌̀◌̕b; a◌֮◌⃐◌̀◌̕b; a◌֮◌⃐◌̀◌̕b; a◌֮◌⃐◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LEFT HARPOON ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 20D1 0062;00E0 05AE 20D1 0315 0062;0061 05AE 0300 20D1 0315 0062;00E0 05AE 20D1 0315 0062;0061 05AE 0300 20D1 0315 0062; # (a◌̕◌̀◌֮◌⃑b; à◌֮◌⃑◌̕b; a◌֮◌̀◌⃑◌̕b; à◌֮◌⃑◌̕b; a◌֮◌̀◌⃑◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING RIGHT HARPOON ABOVE, LATIN SMALL LETTER B +0061 20D1 0315 0300 05AE 0062;0061 05AE 20D1 0300 0315 0062;0061 05AE 20D1 0300 0315 0062;0061 05AE 20D1 0300 0315 0062;0061 05AE 20D1 0300 0315 0062; # (a◌⃑◌̕◌̀◌֮b; a◌֮◌⃑◌̀◌̕b; a◌֮◌⃑◌̀◌̕b; a◌֮◌⃑◌̀◌̕b; a◌֮◌⃑◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING RIGHT HARPOON ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 093C 0334 20D2 0062;0061 0334 20D2 093C 0062;0061 0334 20D2 093C 0062;0061 0334 20D2 093C 0062;0061 0334 20D2 093C 0062; # (a◌़◌̴◌⃒b; a◌̴◌⃒◌़b; a◌̴◌⃒◌़b; a◌̴◌⃒◌़b; a◌̴◌⃒◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING LONG VERTICAL LINE OVERLAY, LATIN SMALL LETTER B +0061 20D2 093C 0334 0062;0061 20D2 0334 093C 0062;0061 20D2 0334 093C 0062;0061 20D2 0334 093C 0062;0061 20D2 0334 093C 0062; # (a◌⃒◌़◌̴b; a◌⃒◌̴◌़b; a◌⃒◌̴◌़b; a◌⃒◌̴◌़b; a◌⃒◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING LONG VERTICAL LINE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 093C 0334 20D3 0062;0061 0334 20D3 093C 0062;0061 0334 20D3 093C 0062;0061 0334 20D3 093C 0062;0061 0334 20D3 093C 0062; # (a◌़◌̴◌⃓b; a◌̴◌⃓◌़b; a◌̴◌⃓◌़b; a◌̴◌⃓◌़b; a◌̴◌⃓◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING SHORT VERTICAL LINE OVERLAY, LATIN SMALL LETTER B +0061 20D3 093C 0334 0062;0061 20D3 0334 093C 0062;0061 20D3 0334 093C 0062;0061 20D3 0334 093C 0062;0061 20D3 0334 093C 0062; # (a◌⃓◌़◌̴b; a◌⃓◌̴◌़b; a◌⃓◌̴◌़b; a◌⃓◌̴◌़b; a◌⃓◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING SHORT VERTICAL LINE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 0315 0300 05AE 20D4 0062;00E0 05AE 20D4 0315 0062;0061 05AE 0300 20D4 0315 0062;00E0 05AE 20D4 0315 0062;0061 05AE 0300 20D4 0315 0062; # (a◌̕◌̀◌֮◌⃔b; à◌֮◌⃔◌̕b; a◌֮◌̀◌⃔◌̕b; à◌֮◌⃔◌̕b; a◌֮◌̀◌⃔◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING ANTICLOCKWISE ARROW ABOVE, LATIN SMALL LETTER B +0061 20D4 0315 0300 05AE 0062;0061 05AE 20D4 0300 0315 0062;0061 05AE 20D4 0300 0315 0062;0061 05AE 20D4 0300 0315 0062;0061 05AE 20D4 0300 0315 0062; # (a◌⃔◌̕◌̀◌֮b; a◌֮◌⃔◌̀◌̕b; a◌֮◌⃔◌̀◌̕b; a◌֮◌⃔◌̀◌̕b; a◌֮◌⃔◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING ANTICLOCKWISE ARROW ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 20D5 0062;00E0 05AE 20D5 0315 0062;0061 05AE 0300 20D5 0315 0062;00E0 05AE 20D5 0315 0062;0061 05AE 0300 20D5 0315 0062; # (a◌̕◌̀◌֮◌⃕b; à◌֮◌⃕◌̕b; a◌֮◌̀◌⃕◌̕b; à◌֮◌⃕◌̕b; a◌֮◌̀◌⃕◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CLOCKWISE ARROW ABOVE, LATIN SMALL LETTER B +0061 20D5 0315 0300 05AE 0062;0061 05AE 20D5 0300 0315 0062;0061 05AE 20D5 0300 0315 0062;0061 05AE 20D5 0300 0315 0062;0061 05AE 20D5 0300 0315 0062; # (a◌⃕◌̕◌̀◌֮b; a◌֮◌⃕◌̀◌̕b; a◌֮◌⃕◌̀◌̕b; a◌֮◌⃕◌̀◌̕b; a◌֮◌⃕◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CLOCKWISE ARROW ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 20D6 0062;00E0 05AE 20D6 0315 0062;0061 05AE 0300 20D6 0315 0062;00E0 05AE 20D6 0315 0062;0061 05AE 0300 20D6 0315 0062; # (a◌̕◌̀◌֮◌⃖b; à◌֮◌⃖◌̕b; a◌֮◌̀◌⃖◌̕b; à◌֮◌⃖◌̕b; a◌֮◌̀◌⃖◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LEFT ARROW ABOVE, LATIN SMALL LETTER B +0061 20D6 0315 0300 05AE 0062;0061 05AE 20D6 0300 0315 0062;0061 05AE 20D6 0300 0315 0062;0061 05AE 20D6 0300 0315 0062;0061 05AE 20D6 0300 0315 0062; # (a◌⃖◌̕◌̀◌֮b; a◌֮◌⃖◌̀◌̕b; a◌֮◌⃖◌̀◌̕b; a◌֮◌⃖◌̀◌̕b; a◌֮◌⃖◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LEFT ARROW ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 20D7 0062;00E0 05AE 20D7 0315 0062;0061 05AE 0300 20D7 0315 0062;00E0 05AE 20D7 0315 0062;0061 05AE 0300 20D7 0315 0062; # (a◌̕◌̀◌֮◌⃗b; à◌֮◌⃗◌̕b; a◌֮◌̀◌⃗◌̕b; à◌֮◌⃗◌̕b; a◌֮◌̀◌⃗◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING RIGHT ARROW ABOVE, LATIN SMALL LETTER B +0061 20D7 0315 0300 05AE 0062;0061 05AE 20D7 0300 0315 0062;0061 05AE 20D7 0300 0315 0062;0061 05AE 20D7 0300 0315 0062;0061 05AE 20D7 0300 0315 0062; # (a◌⃗◌̕◌̀◌֮b; a◌֮◌⃗◌̀◌̕b; a◌֮◌⃗◌̀◌̕b; a◌֮◌⃗◌̀◌̕b; a◌֮◌⃗◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING RIGHT ARROW ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 093C 0334 20D8 0062;0061 0334 20D8 093C 0062;0061 0334 20D8 093C 0062;0061 0334 20D8 093C 0062;0061 0334 20D8 093C 0062; # (a◌़◌̴◌⃘b; a◌̴◌⃘◌़b; a◌̴◌⃘◌़b; a◌̴◌⃘◌़b; a◌̴◌⃘◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING RING OVERLAY, LATIN SMALL LETTER B +0061 20D8 093C 0334 0062;0061 20D8 0334 093C 0062;0061 20D8 0334 093C 0062;0061 20D8 0334 093C 0062;0061 20D8 0334 093C 0062; # (a◌⃘◌़◌̴b; a◌⃘◌̴◌़b; a◌⃘◌̴◌़b; a◌⃘◌̴◌़b; a◌⃘◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING RING OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 093C 0334 20D9 0062;0061 0334 20D9 093C 0062;0061 0334 20D9 093C 0062;0061 0334 20D9 093C 0062;0061 0334 20D9 093C 0062; # (a◌़◌̴◌⃙b; a◌̴◌⃙◌़b; a◌̴◌⃙◌़b; a◌̴◌⃙◌़b; a◌̴◌⃙◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING CLOCKWISE RING OVERLAY, LATIN SMALL LETTER B +0061 20D9 093C 0334 0062;0061 20D9 0334 093C 0062;0061 20D9 0334 093C 0062;0061 20D9 0334 093C 0062;0061 20D9 0334 093C 0062; # (a◌⃙◌़◌̴b; a◌⃙◌̴◌़b; a◌⃙◌̴◌़b; a◌⃙◌̴◌़b; a◌⃙◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING CLOCKWISE RING OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 093C 0334 20DA 0062;0061 0334 20DA 093C 0062;0061 0334 20DA 093C 0062;0061 0334 20DA 093C 0062;0061 0334 20DA 093C 0062; # (a◌़◌̴◌⃚b; a◌̴◌⃚◌़b; a◌̴◌⃚◌़b; a◌̴◌⃚◌़b; a◌̴◌⃚◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING ANTICLOCKWISE RING OVERLAY, LATIN SMALL LETTER B +0061 20DA 093C 0334 0062;0061 20DA 0334 093C 0062;0061 20DA 0334 093C 0062;0061 20DA 0334 093C 0062;0061 20DA 0334 093C 0062; # (a◌⃚◌़◌̴b; a◌⃚◌̴◌़b; a◌⃚◌̴◌़b; a◌⃚◌̴◌़b; a◌⃚◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING ANTICLOCKWISE RING OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 0315 0300 05AE 20DB 0062;00E0 05AE 20DB 0315 0062;0061 05AE 0300 20DB 0315 0062;00E0 05AE 20DB 0315 0062;0061 05AE 0300 20DB 0315 0062; # (a◌̕◌̀◌֮◌⃛b; à◌֮◌⃛◌̕b; a◌֮◌̀◌⃛◌̕b; à◌֮◌⃛◌̕b; a◌֮◌̀◌⃛◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING THREE DOTS ABOVE, LATIN SMALL LETTER B +0061 20DB 0315 0300 05AE 0062;0061 05AE 20DB 0300 0315 0062;0061 05AE 20DB 0300 0315 0062;0061 05AE 20DB 0300 0315 0062;0061 05AE 20DB 0300 0315 0062; # (a◌⃛◌̕◌̀◌֮b; a◌֮◌⃛◌̀◌̕b; a◌֮◌⃛◌̀◌̕b; a◌֮◌⃛◌̀◌̕b; a◌֮◌⃛◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING THREE DOTS ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 20DC 0062;00E0 05AE 20DC 0315 0062;0061 05AE 0300 20DC 0315 0062;00E0 05AE 20DC 0315 0062;0061 05AE 0300 20DC 0315 0062; # (a◌̕◌̀◌֮◌⃜b; à◌֮◌⃜◌̕b; a◌֮◌̀◌⃜◌̕b; à◌֮◌⃜◌̕b; a◌֮◌̀◌⃜◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING FOUR DOTS ABOVE, LATIN SMALL LETTER B +0061 20DC 0315 0300 05AE 0062;0061 05AE 20DC 0300 0315 0062;0061 05AE 20DC 0300 0315 0062;0061 05AE 20DC 0300 0315 0062;0061 05AE 20DC 0300 0315 0062; # (a◌⃜◌̕◌̀◌֮b; a◌֮◌⃜◌̀◌̕b; a◌֮◌⃜◌̀◌̕b; a◌֮◌⃜◌̀◌̕b; a◌֮◌⃜◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING FOUR DOTS ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 20E1 0062;00E0 05AE 20E1 0315 0062;0061 05AE 0300 20E1 0315 0062;00E0 05AE 20E1 0315 0062;0061 05AE 0300 20E1 0315 0062; # (a◌̕◌̀◌֮◌⃡b; à◌֮◌⃡◌̕b; a◌֮◌̀◌⃡◌̕b; à◌֮◌⃡◌̕b; a◌֮◌̀◌⃡◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LEFT RIGHT ARROW ABOVE, LATIN SMALL LETTER B +0061 20E1 0315 0300 05AE 0062;0061 05AE 20E1 0300 0315 0062;0061 05AE 20E1 0300 0315 0062;0061 05AE 20E1 0300 0315 0062;0061 05AE 20E1 0300 0315 0062; # (a◌⃡◌̕◌̀◌֮b; a◌֮◌⃡◌̀◌̕b; a◌֮◌⃡◌̀◌̕b; a◌֮◌⃡◌̀◌̕b; a◌֮◌⃡◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LEFT RIGHT ARROW ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 093C 0334 20E5 0062;0061 0334 20E5 093C 0062;0061 0334 20E5 093C 0062;0061 0334 20E5 093C 0062;0061 0334 20E5 093C 0062; # (a◌़◌̴◌⃥b; a◌̴◌⃥◌़b; a◌̴◌⃥◌़b; a◌̴◌⃥◌़b; a◌̴◌⃥◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING REVERSE SOLIDUS OVERLAY, LATIN SMALL LETTER B +0061 20E5 093C 0334 0062;0061 20E5 0334 093C 0062;0061 20E5 0334 093C 0062;0061 20E5 0334 093C 0062;0061 20E5 0334 093C 0062; # (a◌⃥◌़◌̴b; a◌⃥◌̴◌़b; a◌⃥◌̴◌़b; a◌⃥◌̴◌़b; a◌⃥◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING REVERSE SOLIDUS OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 093C 0334 20E6 0062;0061 0334 20E6 093C 0062;0061 0334 20E6 093C 0062;0061 0334 20E6 093C 0062;0061 0334 20E6 093C 0062; # (a◌़◌̴◌⃦b; a◌̴◌⃦◌़b; a◌̴◌⃦◌़b; a◌̴◌⃦◌़b; a◌̴◌⃦◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING DOUBLE VERTICAL STROKE OVERLAY, LATIN SMALL LETTER B +0061 20E6 093C 0334 0062;0061 20E6 0334 093C 0062;0061 20E6 0334 093C 0062;0061 20E6 0334 093C 0062;0061 20E6 0334 093C 0062; # (a◌⃦◌़◌̴b; a◌⃦◌̴◌़b; a◌⃦◌̴◌़b; a◌⃦◌̴◌़b; a◌⃦◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING DOUBLE VERTICAL STROKE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 0315 0300 05AE 20E7 0062;00E0 05AE 20E7 0315 0062;0061 05AE 0300 20E7 0315 0062;00E0 05AE 20E7 0315 0062;0061 05AE 0300 20E7 0315 0062; # (a◌̕◌̀◌֮◌⃧b; à◌֮◌⃧◌̕b; a◌֮◌̀◌⃧◌̕b; à◌֮◌⃧◌̕b; a◌֮◌̀◌⃧◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING ANNUITY SYMBOL, LATIN SMALL LETTER B +0061 20E7 0315 0300 05AE 0062;0061 05AE 20E7 0300 0315 0062;0061 05AE 20E7 0300 0315 0062;0061 05AE 20E7 0300 0315 0062;0061 05AE 20E7 0300 0315 0062; # (a◌⃧◌̕◌̀◌֮b; a◌֮◌⃧◌̀◌̕b; a◌֮◌⃧◌̀◌̕b; a◌֮◌⃧◌̀◌̕b; a◌֮◌⃧◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING ANNUITY SYMBOL, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 20E8 0062;0061 302A 0316 20E8 059A 0062;0061 302A 0316 20E8 059A 0062;0061 302A 0316 20E8 059A 0062;0061 302A 0316 20E8 059A 0062; # (a◌֚◌̖◌〪◌⃨b; a◌〪◌̖◌⃨◌֚b; a◌〪◌̖◌⃨◌֚b; a◌〪◌̖◌⃨◌֚b; a◌〪◌̖◌⃨◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING TRIPLE UNDERDOT, LATIN SMALL LETTER B +0061 20E8 059A 0316 302A 0062;0061 302A 20E8 0316 059A 0062;0061 302A 20E8 0316 059A 0062;0061 302A 20E8 0316 059A 0062;0061 302A 20E8 0316 059A 0062; # (a◌⃨◌֚◌̖◌〪b; a◌〪◌⃨◌̖◌֚b; a◌〪◌⃨◌̖◌֚b; a◌〪◌⃨◌̖◌֚b; a◌〪◌⃨◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING TRIPLE UNDERDOT, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 20E9 0062;00E0 05AE 20E9 0315 0062;0061 05AE 0300 20E9 0315 0062;00E0 05AE 20E9 0315 0062;0061 05AE 0300 20E9 0315 0062; # (a◌̕◌̀◌֮◌⃩b; à◌֮◌⃩◌̕b; a◌֮◌̀◌⃩◌̕b; à◌֮◌⃩◌̕b; a◌֮◌̀◌⃩◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING WIDE BRIDGE ABOVE, LATIN SMALL LETTER B +0061 20E9 0315 0300 05AE 0062;0061 05AE 20E9 0300 0315 0062;0061 05AE 20E9 0300 0315 0062;0061 05AE 20E9 0300 0315 0062;0061 05AE 20E9 0300 0315 0062; # (a◌⃩◌̕◌̀◌֮b; a◌֮◌⃩◌̀◌̕b; a◌֮◌⃩◌̀◌̕b; a◌֮◌⃩◌̀◌̕b; a◌֮◌⃩◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING WIDE BRIDGE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 093C 0334 20EA 0062;0061 0334 20EA 093C 0062;0061 0334 20EA 093C 0062;0061 0334 20EA 093C 0062;0061 0334 20EA 093C 0062; # (a◌़◌̴◌⃪b; a◌̴◌⃪◌़b; a◌̴◌⃪◌़b; a◌̴◌⃪◌़b; a◌̴◌⃪◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING LEFTWARDS ARROW OVERLAY, LATIN SMALL LETTER B +0061 20EA 093C 0334 0062;0061 20EA 0334 093C 0062;0061 20EA 0334 093C 0062;0061 20EA 0334 093C 0062;0061 20EA 0334 093C 0062; # (a◌⃪◌़◌̴b; a◌⃪◌̴◌़b; a◌⃪◌̴◌़b; a◌⃪◌̴◌़b; a◌⃪◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING LEFTWARDS ARROW OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 0316 302A 031B 302A 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062; # (a◌̖◌〪◌̛◌〪b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 302A 0316 302A 031B 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062; # (a◌〪◌̖◌〪◌̛b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, LATIN SMALL LETTER B +0061 0300 05AE 1D16D 302B 0062;00E0 1D16D 05AE 302B 0062;0061 1D16D 05AE 302B 0300 0062;00E0 1D16D 05AE 302B 0062;0061 1D16D 05AE 302B 0300 0062; # (a◌̀◌𝅭𝅭֮◌〫b; à𝅭𝅭◌֮◌〫b; a𝅭𝅭◌֮◌〫◌̀b; à𝅭𝅭◌֮◌〫b; a𝅭𝅭◌֮◌〫◌̀b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, IDEOGRAPHIC RISING TONE MARK, LATIN SMALL LETTER B +0061 302B 0300 05AE 1D16D 0062;00E0 1D16D 302B 05AE 0062;0061 1D16D 302B 05AE 0300 0062;00E0 1D16D 302B 05AE 0062;0061 1D16D 302B 05AE 0300 0062; # (a◌〫◌̀◌𝅭𝅭֮b; à𝅭𝅭◌〫◌֮b; a𝅭𝅭◌〫◌֮◌̀b; à𝅭𝅭◌〫◌֮b; a𝅭𝅭◌〫◌֮◌̀b; ) LATIN SMALL LETTER A, IDEOGRAPHIC RISING TONE MARK, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, LATIN SMALL LETTER B +0061 0362 0315 0300 302C 0062;00E0 0315 302C 0362 0062;0061 0300 0315 302C 0362 0062;00E0 0315 302C 0362 0062;0061 0300 0315 302C 0362 0062; # (a◌͢◌̕◌̀◌〬b; à◌̕◌〬◌͢b; a◌̀◌̕◌〬◌͢b; à◌̕◌〬◌͢b; a◌̀◌̕◌〬◌͢b; ) LATIN SMALL LETTER A, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, IDEOGRAPHIC DEPARTING TONE MARK, LATIN SMALL LETTER B +0061 302C 0362 0315 0300 0062;00E0 302C 0315 0362 0062;0061 0300 302C 0315 0362 0062;00E0 302C 0315 0362 0062;0061 0300 302C 0315 0362 0062; # (a◌〬◌͢◌̕◌̀b; à◌〬◌̕◌͢b; a◌̀◌〬◌̕◌͢b; à◌〬◌̕◌͢b; a◌̀◌〬◌̕◌͢b; ) LATIN SMALL LETTER A, IDEOGRAPHIC DEPARTING TONE MARK, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, LATIN SMALL LETTER B +0061 302E 059A 0316 302D 0062;0061 0316 059A 302D 302E 0062;0061 0316 059A 302D 302E 0062;0061 0316 059A 302D 302E 0062;0061 0316 059A 302D 302E 0062; # (a◌〮◌֚◌̖◌〭b; a◌̖◌֚◌〭◌〮b; a◌̖◌֚◌〭◌〮b; a◌̖◌֚◌〭◌〮b; a◌̖◌֚◌〭◌〮b; ) LATIN SMALL LETTER A, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC ENTERING TONE MARK, LATIN SMALL LETTER B +0061 302D 302E 059A 0316 0062;0061 0316 302D 059A 302E 0062;0061 0316 302D 059A 302E 0062;0061 0316 302D 059A 302E 0062;0061 0316 302D 059A 302E 0062; # (a◌〭◌〮◌֚◌̖b; a◌̖◌〭◌֚◌〮b; a◌̖◌〭◌֚◌〮b; a◌̖◌〭◌֚◌〮b; a◌̖◌〭◌֚◌〮b; ) LATIN SMALL LETTER A, IDEOGRAPHIC ENTERING TONE MARK, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, LATIN SMALL LETTER B +0061 1D16D 302E 059A 302E 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062; # (a𝅭𝅭◌〮◌֚◌〮b; a◌֚◌〮◌〮𝅭𝅭b; a◌֚◌〮◌〮𝅭𝅭b; a◌֚◌〮◌〮𝅭𝅭b; a◌֚◌〮◌〮𝅭𝅭b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, HANGUL SINGLE DOT TONE MARK, LATIN SMALL LETTER B +0061 302E 1D16D 302E 059A 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062; # (a◌〮𝅭𝅭◌〮◌֚b; a◌֚◌〮◌〮𝅭𝅭b; a◌֚◌〮◌〮𝅭𝅭b; a◌֚◌〮◌〮𝅭𝅭b; a◌֚◌〮◌〮𝅭𝅭b; ) LATIN SMALL LETTER A, HANGUL SINGLE DOT TONE MARK, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, LATIN SMALL LETTER B +0061 1D16D 302E 059A 302F 0062;0061 059A 302E 302F 1D16D 0062;0061 059A 302E 302F 1D16D 0062;0061 059A 302E 302F 1D16D 0062;0061 059A 302E 302F 1D16D 0062; # (a𝅭𝅭◌〮◌֚◌〯b; a◌֚◌〮◌〯𝅭𝅭b; a◌֚◌〮◌〯𝅭𝅭b; a◌֚◌〮◌〯𝅭𝅭b; a◌֚◌〮◌〯𝅭𝅭b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, HANGUL DOUBLE DOT TONE MARK, LATIN SMALL LETTER B +0061 302F 1D16D 302E 059A 0062;0061 059A 302F 302E 1D16D 0062;0061 059A 302F 302E 1D16D 0062;0061 059A 302F 302E 1D16D 0062;0061 059A 302F 302E 1D16D 0062; # (a◌〯𝅭𝅭◌〮◌֚b; a◌֚◌〯◌〮𝅭𝅭b; a◌֚◌〯◌〮𝅭𝅭b; a◌֚◌〯◌〮𝅭𝅭b; a◌֚◌〯◌〮𝅭𝅭b; ) LATIN SMALL LETTER A, HANGUL DOUBLE DOT TONE MARK, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, LATIN SMALL LETTER B +0061 094D 3099 093C 3099 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062; # (a◌्◌゙◌़◌゙b; a◌़◌゙◌゙◌्b; a◌़◌゙◌゙◌्b; a◌़◌゙◌゙◌्b; a◌़◌゙◌゙◌्b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B +0061 3099 094D 3099 093C 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062; # (a◌゙◌्◌゙◌़b; a◌़◌゙◌゙◌्b; a◌़◌゙◌゙◌्b; a◌़◌゙◌゙◌्b; a◌़◌゙◌゙◌्b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, LATIN SMALL LETTER B +0061 094D 3099 093C 309A 0062;0061 093C 3099 309A 094D 0062;0061 093C 3099 309A 094D 0062;0061 093C 3099 309A 094D 0062;0061 093C 3099 309A 094D 0062; # (a◌्◌゙◌़◌゚b; a◌़◌゙◌゚◌्b; a◌़◌゙◌゚◌्b; a◌़◌゙◌゚◌्b; a◌़◌゙◌゚◌्b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK, LATIN SMALL LETTER B +0061 309A 094D 3099 093C 0062;0061 093C 309A 3099 094D 0062;0061 093C 309A 3099 094D 0062;0061 093C 309A 3099 094D 0062;0061 093C 309A 3099 094D 0062; # (a◌゚◌्◌゙◌़b; a◌़◌゚◌゙◌्b; a◌़◌゚◌゙◌्b; a◌़◌゚◌゙◌्b; a◌़◌゚◌゙◌्b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, LATIN SMALL LETTER B +0061 064B FB1E 05C2 FB1E 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062; # (a◌ً◌ﬞ◌ׂ◌ﬞb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; ) LATIN SMALL LETTER A, ARABIC FATHATAN, HEBREW POINT JUDEO-SPANISH VARIKA, HEBREW POINT SIN DOT, HEBREW POINT JUDEO-SPANISH VARIKA, LATIN SMALL LETTER B +0061 FB1E 064B FB1E 05C2 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062; # (a◌ﬞ◌ً◌ﬞ◌ׂb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; ) LATIN SMALL LETTER A, HEBREW POINT JUDEO-SPANISH VARIKA, ARABIC FATHATAN, HEBREW POINT JUDEO-SPANISH VARIKA, HEBREW POINT SIN DOT, LATIN SMALL LETTER B +0061 0315 0300 05AE FE20 0062;00E0 05AE FE20 0315 0062;0061 05AE 0300 FE20 0315 0062;00E0 05AE FE20 0315 0062;0061 05AE 0300 FE20 0315 0062; # (a◌̕◌̀◌֮◌︠b; à◌֮◌︠◌̕b; a◌֮◌̀◌︠◌̕b; à◌֮◌︠◌̕b; a◌֮◌̀◌︠◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LIGATURE LEFT HALF, LATIN SMALL LETTER B +0061 FE20 0315 0300 05AE 0062;0061 05AE FE20 0300 0315 0062;0061 05AE FE20 0300 0315 0062;0061 05AE FE20 0300 0315 0062;0061 05AE FE20 0300 0315 0062; # (a◌︠◌̕◌̀◌֮b; a◌֮◌︠◌̀◌̕b; a◌֮◌︠◌̀◌̕b; a◌֮◌︠◌̀◌̕b; a◌֮◌︠◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LIGATURE LEFT HALF, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE FE21 0062;00E0 05AE FE21 0315 0062;0061 05AE 0300 FE21 0315 0062;00E0 05AE FE21 0315 0062;0061 05AE 0300 FE21 0315 0062; # (a◌̕◌̀◌֮◌︡b; à◌֮◌︡◌̕b; a◌֮◌̀◌︡◌̕b; à◌֮◌︡◌̕b; a◌֮◌̀◌︡◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LIGATURE RIGHT HALF, LATIN SMALL LETTER B +0061 FE21 0315 0300 05AE 0062;0061 05AE FE21 0300 0315 0062;0061 05AE FE21 0300 0315 0062;0061 05AE FE21 0300 0315 0062;0061 05AE FE21 0300 0315 0062; # (a◌︡◌̕◌̀◌֮b; a◌֮◌︡◌̀◌̕b; a◌֮◌︡◌̀◌̕b; a◌֮◌︡◌̀◌̕b; a◌֮◌︡◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LIGATURE RIGHT HALF, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE FE22 0062;00E0 05AE FE22 0315 0062;0061 05AE 0300 FE22 0315 0062;00E0 05AE FE22 0315 0062;0061 05AE 0300 FE22 0315 0062; # (a◌̕◌̀◌֮◌︢b; à◌֮◌︢◌̕b; a◌֮◌̀◌︢◌̕b; à◌֮◌︢◌̕b; a◌֮◌̀◌︢◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE TILDE LEFT HALF, LATIN SMALL LETTER B +0061 FE22 0315 0300 05AE 0062;0061 05AE FE22 0300 0315 0062;0061 05AE FE22 0300 0315 0062;0061 05AE FE22 0300 0315 0062;0061 05AE FE22 0300 0315 0062; # (a◌︢◌̕◌̀◌֮b; a◌֮◌︢◌̀◌̕b; a◌֮◌︢◌̀◌̕b; a◌֮◌︢◌̀◌̕b; a◌֮◌︢◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE TILDE LEFT HALF, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE FE23 0062;00E0 05AE FE23 0315 0062;0061 05AE 0300 FE23 0315 0062;00E0 05AE FE23 0315 0062;0061 05AE 0300 FE23 0315 0062; # (a◌̕◌̀◌֮◌︣b; à◌֮◌︣◌̕b; a◌֮◌̀◌︣◌̕b; à◌֮◌︣◌̕b; a◌֮◌̀◌︣◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE TILDE RIGHT HALF, LATIN SMALL LETTER B +0061 FE23 0315 0300 05AE 0062;0061 05AE FE23 0300 0315 0062;0061 05AE FE23 0300 0315 0062;0061 05AE FE23 0300 0315 0062;0061 05AE FE23 0300 0315 0062; # (a◌︣◌̕◌̀◌֮b; a◌֮◌︣◌̀◌̕b; a◌֮◌︣◌̀◌̕b; a◌֮◌︣◌̀◌̕b; a◌֮◌︣◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE TILDE RIGHT HALF, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 302A 031B 0321 1D165 0062;0061 0321 031B 1D165 302A 0062;0061 0321 031B 1D165 302A 0062;0061 0321 031B 1D165 302A 0062;0061 0321 031B 1D165 302A 0062; # (a◌〪◌̛◌̡𝅥𝅥b; a◌̡◌̛𝅥𝅥◌〪b; a◌̡◌̛𝅥𝅥◌〪b; a◌̡◌̛𝅥𝅥◌〪b; a◌̡◌̛𝅥𝅥◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING STEM, LATIN SMALL LETTER B +0061 1D165 302A 031B 0321 0062;0061 0321 1D165 031B 302A 0062;0061 0321 1D165 031B 302A 0062;0061 0321 1D165 031B 302A 0062;0061 0321 1D165 031B 302A 0062; # (a𝅥𝅥◌〪◌̛◌̡b; a◌̡𝅥𝅥◌̛◌〪b; a◌̡𝅥𝅥◌̛◌〪b; a◌̡𝅥𝅥◌̛◌〪b; a◌̡𝅥𝅥◌̛◌〪b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING STEM, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B +0061 302A 031B 0321 1D166 0062;0061 0321 031B 1D166 302A 0062;0061 0321 031B 1D166 302A 0062;0061 0321 031B 1D166 302A 0062;0061 0321 031B 1D166 302A 0062; # (a◌〪◌̛◌̡𝅦𝅦b; a◌̡◌̛𝅦𝅦◌〪b; a◌̡◌̛𝅦𝅦◌〪b; a◌̡◌̛𝅦𝅦◌〪b; a◌̡◌̛𝅦𝅦◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING SPRECHGESANG STEM, LATIN SMALL LETTER B +0061 1D166 302A 031B 0321 0062;0061 0321 1D166 031B 302A 0062;0061 0321 1D166 031B 302A 0062;0061 0321 1D166 031B 302A 0062;0061 0321 1D166 031B 302A 0062; # (a𝅦𝅦◌〪◌̛◌̡b; a◌̡𝅦𝅦◌̛◌〪b; a◌̡𝅦𝅦◌̛◌〪b; a◌̡𝅦𝅦◌̛◌〪b; a◌̡𝅦𝅦◌̛◌〪b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING SPRECHGESANG STEM, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B +0061 093C 0334 1D167 0062;0061 0334 1D167 093C 0062;0061 0334 1D167 093C 0062;0061 0334 1D167 093C 0062;0061 0334 1D167 093C 0062; # (a◌़◌̴◌𝅧◌𝅧b; a◌̴◌𝅧◌𝅧◌़b; a◌̴◌𝅧◌𝅧◌़b; a◌̴◌𝅧◌𝅧◌़b; a◌̴◌𝅧◌𝅧◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, MUSICAL SYMBOL COMBINING TREMOLO-1, LATIN SMALL LETTER B +0061 1D167 093C 0334 0062;0061 1D167 0334 093C 0062;0061 1D167 0334 093C 0062;0061 1D167 0334 093C 0062;0061 1D167 0334 093C 0062; # (a◌𝅧◌𝅧◌़◌̴b; a◌𝅧◌𝅧◌̴◌़b; a◌𝅧◌𝅧◌̴◌़b; a◌𝅧◌𝅧◌̴◌़b; a◌𝅧◌𝅧◌̴◌़b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING TREMOLO-1, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 093C 0334 1D168 0062;0061 0334 1D168 093C 0062;0061 0334 1D168 093C 0062;0061 0334 1D168 093C 0062;0061 0334 1D168 093C 0062; # (a◌़◌̴◌𝅨◌𝅨b; a◌̴◌𝅨◌𝅨◌़b; a◌̴◌𝅨◌𝅨◌़b; a◌̴◌𝅨◌𝅨◌़b; a◌̴◌𝅨◌𝅨◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, MUSICAL SYMBOL COMBINING TREMOLO-2, LATIN SMALL LETTER B +0061 1D168 093C 0334 0062;0061 1D168 0334 093C 0062;0061 1D168 0334 093C 0062;0061 1D168 0334 093C 0062;0061 1D168 0334 093C 0062; # (a◌𝅨◌𝅨◌़◌̴b; a◌𝅨◌𝅨◌̴◌़b; a◌𝅨◌𝅨◌̴◌़b; a◌𝅨◌𝅨◌̴◌़b; a◌𝅨◌𝅨◌̴◌़b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING TREMOLO-2, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 093C 0334 1D169 0062;0061 0334 1D169 093C 0062;0061 0334 1D169 093C 0062;0061 0334 1D169 093C 0062;0061 0334 1D169 093C 0062; # (a◌़◌̴◌𝅩◌𝅩b; a◌̴◌𝅩◌𝅩◌़b; a◌̴◌𝅩◌𝅩◌़b; a◌̴◌𝅩◌𝅩◌़b; a◌̴◌𝅩◌𝅩◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, MUSICAL SYMBOL COMBINING TREMOLO-3, LATIN SMALL LETTER B +0061 1D169 093C 0334 0062;0061 1D169 0334 093C 0062;0061 1D169 0334 093C 0062;0061 1D169 0334 093C 0062;0061 1D169 0334 093C 0062; # (a◌𝅩◌𝅩◌़◌̴b; a◌𝅩◌𝅩◌̴◌़b; a◌𝅩◌𝅩◌̴◌़b; a◌𝅩◌𝅩◌̴◌़b; a◌𝅩◌𝅩◌̴◌़b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING TREMOLO-3, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B +0061 05AE 1D16D 302E 1D16D 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062; # (a◌𝅭𝅭֮◌〮𝅭𝅭b; a◌〮𝅭𝅭𝅭𝅭◌֮b; a◌〮𝅭𝅭𝅭𝅭◌֮b; a◌〮𝅭𝅭𝅭𝅭◌֮b; a◌〮𝅭𝅭𝅭𝅭◌֮b; ) LATIN SMALL LETTER A, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, LATIN SMALL LETTER B +0061 1D16D 05AE 1D16D 302E 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062; # (a𝅭𝅭◌𝅭𝅭֮◌〮b; a◌〮𝅭𝅭𝅭𝅭◌֮b; a◌〮𝅭𝅭𝅭𝅭◌֮b; a◌〮𝅭𝅭𝅭𝅭◌֮b; a◌〮𝅭𝅭𝅭𝅭◌֮b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, LATIN SMALL LETTER B +0061 302A 031B 0321 1D16E 0062;0061 0321 031B 1D16E 302A 0062;0061 0321 031B 1D16E 302A 0062;0061 0321 031B 1D16E 302A 0062;0061 0321 031B 1D16E 302A 0062; # (a◌〪◌̛◌̡𝅮𝅮b; a◌̡◌̛𝅮𝅮◌〪b; a◌̡◌̛𝅮𝅮◌〪b; a◌̡◌̛𝅮𝅮◌〪b; a◌̡◌̛𝅮𝅮◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING FLAG-1, LATIN SMALL LETTER B +0061 1D16E 302A 031B 0321 0062;0061 0321 1D16E 031B 302A 0062;0061 0321 1D16E 031B 302A 0062;0061 0321 1D16E 031B 302A 0062;0061 0321 1D16E 031B 302A 0062; # (a𝅮𝅮◌〪◌̛◌̡b; a◌̡𝅮𝅮◌̛◌〪b; a◌̡𝅮𝅮◌̛◌〪b; a◌̡𝅮𝅮◌̛◌〪b; a◌̡𝅮𝅮◌̛◌〪b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLAG-1, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B +0061 302A 031B 0321 1D16F 0062;0061 0321 031B 1D16F 302A 0062;0061 0321 031B 1D16F 302A 0062;0061 0321 031B 1D16F 302A 0062;0061 0321 031B 1D16F 302A 0062; # (a◌〪◌̛◌̡𝅯𝅯b; a◌̡◌̛𝅯𝅯◌〪b; a◌̡◌̛𝅯𝅯◌〪b; a◌̡◌̛𝅯𝅯◌〪b; a◌̡◌̛𝅯𝅯◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING FLAG-2, LATIN SMALL LETTER B +0061 1D16F 302A 031B 0321 0062;0061 0321 1D16F 031B 302A 0062;0061 0321 1D16F 031B 302A 0062;0061 0321 1D16F 031B 302A 0062;0061 0321 1D16F 031B 302A 0062; # (a𝅯𝅯◌〪◌̛◌̡b; a◌̡𝅯𝅯◌̛◌〪b; a◌̡𝅯𝅯◌̛◌〪b; a◌̡𝅯𝅯◌̛◌〪b; a◌̡𝅯𝅯◌̛◌〪b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLAG-2, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B +0061 302A 031B 0321 1D170 0062;0061 0321 031B 1D170 302A 0062;0061 0321 031B 1D170 302A 0062;0061 0321 031B 1D170 302A 0062;0061 0321 031B 1D170 302A 0062; # (a◌〪◌̛◌̡𝅰𝅰b; a◌̡◌̛𝅰𝅰◌〪b; a◌̡◌̛𝅰𝅰◌〪b; a◌̡◌̛𝅰𝅰◌〪b; a◌̡◌̛𝅰𝅰◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING FLAG-3, LATIN SMALL LETTER B +0061 1D170 302A 031B 0321 0062;0061 0321 1D170 031B 302A 0062;0061 0321 1D170 031B 302A 0062;0061 0321 1D170 031B 302A 0062;0061 0321 1D170 031B 302A 0062; # (a𝅰𝅰◌〪◌̛◌̡b; a◌̡𝅰𝅰◌̛◌〪b; a◌̡𝅰𝅰◌̛◌〪b; a◌̡𝅰𝅰◌̛◌〪b; a◌̡𝅰𝅰◌̛◌〪b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLAG-3, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B +0061 302A 031B 0321 1D171 0062;0061 0321 031B 1D171 302A 0062;0061 0321 031B 1D171 302A 0062;0061 0321 031B 1D171 302A 0062;0061 0321 031B 1D171 302A 0062; # (a◌〪◌̛◌̡𝅱𝅱b; a◌̡◌̛𝅱𝅱◌〪b; a◌̡◌̛𝅱𝅱◌〪b; a◌̡◌̛𝅱𝅱◌〪b; a◌̡◌̛𝅱𝅱◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING FLAG-4, LATIN SMALL LETTER B +0061 1D171 302A 031B 0321 0062;0061 0321 1D171 031B 302A 0062;0061 0321 1D171 031B 302A 0062;0061 0321 1D171 031B 302A 0062;0061 0321 1D171 031B 302A 0062; # (a𝅱𝅱◌〪◌̛◌̡b; a◌̡𝅱𝅱◌̛◌〪b; a◌̡𝅱𝅱◌̛◌〪b; a◌̡𝅱𝅱◌̛◌〪b; a◌̡𝅱𝅱◌̛◌〪b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLAG-4, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B +0061 302A 031B 0321 1D172 0062;0061 0321 031B 1D172 302A 0062;0061 0321 031B 1D172 302A 0062;0061 0321 031B 1D172 302A 0062;0061 0321 031B 1D172 302A 0062; # (a◌〪◌̛◌̡𝅲𝅲b; a◌̡◌̛𝅲𝅲◌〪b; a◌̡◌̛𝅲𝅲◌〪b; a◌̡◌̛𝅲𝅲◌〪b; a◌̡◌̛𝅲𝅲◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING FLAG-5, LATIN SMALL LETTER B +0061 1D172 302A 031B 0321 0062;0061 0321 1D172 031B 302A 0062;0061 0321 1D172 031B 302A 0062;0061 0321 1D172 031B 302A 0062;0061 0321 1D172 031B 302A 0062; # (a𝅲𝅲◌〪◌̛◌̡b; a◌̡𝅲𝅲◌̛◌〪b; a◌̡𝅲𝅲◌̛◌〪b; a◌̡𝅲𝅲◌̛◌〪b; a◌̡𝅲𝅲◌̛◌〪b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLAG-5, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B +0061 059A 0316 302A 1D17B 0062;0061 302A 0316 1D17B 059A 0062;0061 302A 0316 1D17B 059A 0062;0061 302A 0316 1D17B 059A 0062;0061 302A 0316 1D17B 059A 0062; # (a◌֚◌̖◌〪◌𝅻◌𝅻b; a◌〪◌̖◌𝅻◌𝅻◌֚b; a◌〪◌̖◌𝅻◌𝅻◌֚b; a◌〪◌̖◌𝅻◌𝅻◌֚b; a◌〪◌̖◌𝅻◌𝅻◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING ACCENT, LATIN SMALL LETTER B +0061 1D17B 059A 0316 302A 0062;0061 302A 1D17B 0316 059A 0062;0061 302A 1D17B 0316 059A 0062;0061 302A 1D17B 0316 059A 0062;0061 302A 1D17B 0316 059A 0062; # (a◌𝅻◌𝅻◌֚◌̖◌〪b; a◌〪◌𝅻◌𝅻◌̖◌֚b; a◌〪◌𝅻◌𝅻◌̖◌֚b; a◌〪◌𝅻◌𝅻◌̖◌֚b; a◌〪◌𝅻◌𝅻◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING ACCENT, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 1D17C 0062;0061 302A 0316 1D17C 059A 0062;0061 302A 0316 1D17C 059A 0062;0061 302A 0316 1D17C 059A 0062;0061 302A 0316 1D17C 059A 0062; # (a◌֚◌̖◌〪◌𝅼◌𝅼b; a◌〪◌̖◌𝅼◌𝅼◌֚b; a◌〪◌̖◌𝅼◌𝅼◌֚b; a◌〪◌̖◌𝅼◌𝅼◌֚b; a◌〪◌̖◌𝅼◌𝅼◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING STACCATO, LATIN SMALL LETTER B +0061 1D17C 059A 0316 302A 0062;0061 302A 1D17C 0316 059A 0062;0061 302A 1D17C 0316 059A 0062;0061 302A 1D17C 0316 059A 0062;0061 302A 1D17C 0316 059A 0062; # (a◌𝅼◌𝅼◌֚◌̖◌〪b; a◌〪◌𝅼◌𝅼◌̖◌֚b; a◌〪◌𝅼◌𝅼◌̖◌֚b; a◌〪◌𝅼◌𝅼◌̖◌֚b; a◌〪◌𝅼◌𝅼◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING STACCATO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 1D17D 0062;0061 302A 0316 1D17D 059A 0062;0061 302A 0316 1D17D 059A 0062;0061 302A 0316 1D17D 059A 0062;0061 302A 0316 1D17D 059A 0062; # (a◌֚◌̖◌〪◌𝅽◌𝅽b; a◌〪◌̖◌𝅽◌𝅽◌֚b; a◌〪◌̖◌𝅽◌𝅽◌֚b; a◌〪◌̖◌𝅽◌𝅽◌֚b; a◌〪◌̖◌𝅽◌𝅽◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING TENUTO, LATIN SMALL LETTER B +0061 1D17D 059A 0316 302A 0062;0061 302A 1D17D 0316 059A 0062;0061 302A 1D17D 0316 059A 0062;0061 302A 1D17D 0316 059A 0062;0061 302A 1D17D 0316 059A 0062; # (a◌𝅽◌𝅽◌֚◌̖◌〪b; a◌〪◌𝅽◌𝅽◌̖◌֚b; a◌〪◌𝅽◌𝅽◌̖◌֚b; a◌〪◌𝅽◌𝅽◌̖◌֚b; a◌〪◌𝅽◌𝅽◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING TENUTO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 1D17E 0062;0061 302A 0316 1D17E 059A 0062;0061 302A 0316 1D17E 059A 0062;0061 302A 0316 1D17E 059A 0062;0061 302A 0316 1D17E 059A 0062; # (a◌֚◌̖◌〪◌𝅾◌𝅾b; a◌〪◌̖◌𝅾◌𝅾◌֚b; a◌〪◌̖◌𝅾◌𝅾◌֚b; a◌〪◌̖◌𝅾◌𝅾◌֚b; a◌〪◌̖◌𝅾◌𝅾◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING STACCATISSIMO, LATIN SMALL LETTER B +0061 1D17E 059A 0316 302A 0062;0061 302A 1D17E 0316 059A 0062;0061 302A 1D17E 0316 059A 0062;0061 302A 1D17E 0316 059A 0062;0061 302A 1D17E 0316 059A 0062; # (a◌𝅾◌𝅾◌֚◌̖◌〪b; a◌〪◌𝅾◌𝅾◌̖◌֚b; a◌〪◌𝅾◌𝅾◌̖◌֚b; a◌〪◌𝅾◌𝅾◌̖◌֚b; a◌〪◌𝅾◌𝅾◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING STACCATISSIMO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 1D17F 0062;0061 302A 0316 1D17F 059A 0062;0061 302A 0316 1D17F 059A 0062;0061 302A 0316 1D17F 059A 0062;0061 302A 0316 1D17F 059A 0062; # (a◌֚◌̖◌〪◌𝅿◌𝅿b; a◌〪◌̖◌𝅿◌𝅿◌֚b; a◌〪◌̖◌𝅿◌𝅿◌֚b; a◌〪◌̖◌𝅿◌𝅿◌֚b; a◌〪◌̖◌𝅿◌𝅿◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING MARCATO, LATIN SMALL LETTER B +0061 1D17F 059A 0316 302A 0062;0061 302A 1D17F 0316 059A 0062;0061 302A 1D17F 0316 059A 0062;0061 302A 1D17F 0316 059A 0062;0061 302A 1D17F 0316 059A 0062; # (a◌𝅿◌𝅿◌֚◌̖◌〪b; a◌〪◌𝅿◌𝅿◌̖◌֚b; a◌〪◌𝅿◌𝅿◌̖◌֚b; a◌〪◌𝅿◌𝅿◌̖◌֚b; a◌〪◌𝅿◌𝅿◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING MARCATO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 1D180 0062;0061 302A 0316 1D180 059A 0062;0061 302A 0316 1D180 059A 0062;0061 302A 0316 1D180 059A 0062;0061 302A 0316 1D180 059A 0062; # (a◌֚◌̖◌〪◌𝆀◌𝆀b; a◌〪◌̖◌𝆀◌𝆀◌֚b; a◌〪◌̖◌𝆀◌𝆀◌֚b; a◌〪◌̖◌𝆀◌𝆀◌֚b; a◌〪◌̖◌𝆀◌𝆀◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING MARCATO-STACCATO, LATIN SMALL LETTER B +0061 1D180 059A 0316 302A 0062;0061 302A 1D180 0316 059A 0062;0061 302A 1D180 0316 059A 0062;0061 302A 1D180 0316 059A 0062;0061 302A 1D180 0316 059A 0062; # (a◌𝆀◌𝆀◌֚◌̖◌〪b; a◌〪◌𝆀◌𝆀◌̖◌֚b; a◌〪◌𝆀◌𝆀◌̖◌֚b; a◌〪◌𝆀◌𝆀◌̖◌֚b; a◌〪◌𝆀◌𝆀◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING MARCATO-STACCATO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 1D181 0062;0061 302A 0316 1D181 059A 0062;0061 302A 0316 1D181 059A 0062;0061 302A 0316 1D181 059A 0062;0061 302A 0316 1D181 059A 0062; # (a◌֚◌̖◌〪◌𝆁◌𝆁b; a◌〪◌̖◌𝆁◌𝆁◌֚b; a◌〪◌̖◌𝆁◌𝆁◌֚b; a◌〪◌̖◌𝆁◌𝆁◌֚b; a◌〪◌̖◌𝆁◌𝆁◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING ACCENT-STACCATO, LATIN SMALL LETTER B +0061 1D181 059A 0316 302A 0062;0061 302A 1D181 0316 059A 0062;0061 302A 1D181 0316 059A 0062;0061 302A 1D181 0316 059A 0062;0061 302A 1D181 0316 059A 0062; # (a◌𝆁◌𝆁◌֚◌̖◌〪b; a◌〪◌𝆁◌𝆁◌̖◌֚b; a◌〪◌𝆁◌𝆁◌̖◌֚b; a◌〪◌𝆁◌𝆁◌̖◌֚b; a◌〪◌𝆁◌𝆁◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING ACCENT-STACCATO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 1D182 0062;0061 302A 0316 1D182 059A 0062;0061 302A 0316 1D182 059A 0062;0061 302A 0316 1D182 059A 0062;0061 302A 0316 1D182 059A 0062; # (a◌֚◌̖◌〪◌𝆂◌𝆂b; a◌〪◌̖◌𝆂◌𝆂◌֚b; a◌〪◌̖◌𝆂◌𝆂◌֚b; a◌〪◌̖◌𝆂◌𝆂◌֚b; a◌〪◌̖◌𝆂◌𝆂◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING LOURE, LATIN SMALL LETTER B +0061 1D182 059A 0316 302A 0062;0061 302A 1D182 0316 059A 0062;0061 302A 1D182 0316 059A 0062;0061 302A 1D182 0316 059A 0062;0061 302A 1D182 0316 059A 0062; # (a◌𝆂◌𝆂◌֚◌̖◌〪b; a◌〪◌𝆂◌𝆂◌̖◌֚b; a◌〪◌𝆂◌𝆂◌̖◌֚b; a◌〪◌𝆂◌𝆂◌̖◌֚b; a◌〪◌𝆂◌𝆂◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING LOURE, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 1D185 0062;00E0 05AE 1D185 0315 0062;0061 05AE 0300 1D185 0315 0062;00E0 05AE 1D185 0315 0062;0061 05AE 0300 1D185 0315 0062; # (a◌̕◌̀◌֮◌𝆅◌𝆅b; à◌֮◌𝆅◌𝆅◌̕b; a◌֮◌̀◌𝆅◌𝆅◌̕b; à◌֮◌𝆅◌𝆅◌̕b; a◌֮◌̀◌𝆅◌𝆅◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING DOIT, LATIN SMALL LETTER B +0061 1D185 0315 0300 05AE 0062;0061 05AE 1D185 0300 0315 0062;0061 05AE 1D185 0300 0315 0062;0061 05AE 1D185 0300 0315 0062;0061 05AE 1D185 0300 0315 0062; # (a◌𝆅◌𝆅◌̕◌̀◌֮b; a◌֮◌𝆅◌𝆅◌̀◌̕b; a◌֮◌𝆅◌𝆅◌̀◌̕b; a◌֮◌𝆅◌𝆅◌̀◌̕b; a◌֮◌𝆅◌𝆅◌̀◌̕b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING DOIT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 1D186 0062;00E0 05AE 1D186 0315 0062;0061 05AE 0300 1D186 0315 0062;00E0 05AE 1D186 0315 0062;0061 05AE 0300 1D186 0315 0062; # (a◌̕◌̀◌֮◌𝆆◌𝆆b; à◌֮◌𝆆◌𝆆◌̕b; a◌֮◌̀◌𝆆◌𝆆◌̕b; à◌֮◌𝆆◌𝆆◌̕b; a◌֮◌̀◌𝆆◌𝆆◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING RIP, LATIN SMALL LETTER B +0061 1D186 0315 0300 05AE 0062;0061 05AE 1D186 0300 0315 0062;0061 05AE 1D186 0300 0315 0062;0061 05AE 1D186 0300 0315 0062;0061 05AE 1D186 0300 0315 0062; # (a◌𝆆◌𝆆◌̕◌̀◌֮b; a◌֮◌𝆆◌𝆆◌̀◌̕b; a◌֮◌𝆆◌𝆆◌̀◌̕b; a◌֮◌𝆆◌𝆆◌̀◌̕b; a◌֮◌𝆆◌𝆆◌̀◌̕b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING RIP, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 1D187 0062;00E0 05AE 1D187 0315 0062;0061 05AE 0300 1D187 0315 0062;00E0 05AE 1D187 0315 0062;0061 05AE 0300 1D187 0315 0062; # (a◌̕◌̀◌֮◌𝆇◌𝆇b; à◌֮◌𝆇◌𝆇◌̕b; a◌֮◌̀◌𝆇◌𝆇◌̕b; à◌֮◌𝆇◌𝆇◌̕b; a◌֮◌̀◌𝆇◌𝆇◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING FLIP, LATIN SMALL LETTER B +0061 1D187 0315 0300 05AE 0062;0061 05AE 1D187 0300 0315 0062;0061 05AE 1D187 0300 0315 0062;0061 05AE 1D187 0300 0315 0062;0061 05AE 1D187 0300 0315 0062; # (a◌𝆇◌𝆇◌̕◌̀◌֮b; a◌֮◌𝆇◌𝆇◌̀◌̕b; a◌֮◌𝆇◌𝆇◌̀◌̕b; a◌֮◌𝆇◌𝆇◌̀◌̕b; a◌֮◌𝆇◌𝆇◌̀◌̕b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLIP, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 1D188 0062;00E0 05AE 1D188 0315 0062;0061 05AE 0300 1D188 0315 0062;00E0 05AE 1D188 0315 0062;0061 05AE 0300 1D188 0315 0062; # (a◌̕◌̀◌֮◌𝆈◌𝆈b; à◌֮◌𝆈◌𝆈◌̕b; a◌֮◌̀◌𝆈◌𝆈◌̕b; à◌֮◌𝆈◌𝆈◌̕b; a◌֮◌̀◌𝆈◌𝆈◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING SMEAR, LATIN SMALL LETTER B +0061 1D188 0315 0300 05AE 0062;0061 05AE 1D188 0300 0315 0062;0061 05AE 1D188 0300 0315 0062;0061 05AE 1D188 0300 0315 0062;0061 05AE 1D188 0300 0315 0062; # (a◌𝆈◌𝆈◌̕◌̀◌֮b; a◌֮◌𝆈◌𝆈◌̀◌̕b; a◌֮◌𝆈◌𝆈◌̀◌̕b; a◌֮◌𝆈◌𝆈◌̀◌̕b; a◌֮◌𝆈◌𝆈◌̀◌̕b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING SMEAR, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 1D189 0062;00E0 05AE 1D189 0315 0062;0061 05AE 0300 1D189 0315 0062;00E0 05AE 1D189 0315 0062;0061 05AE 0300 1D189 0315 0062; # (a◌̕◌̀◌֮◌𝆉◌𝆉b; à◌֮◌𝆉◌𝆉◌̕b; a◌֮◌̀◌𝆉◌𝆉◌̕b; à◌֮◌𝆉◌𝆉◌̕b; a◌֮◌̀◌𝆉◌𝆉◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING BEND, LATIN SMALL LETTER B +0061 1D189 0315 0300 05AE 0062;0061 05AE 1D189 0300 0315 0062;0061 05AE 1D189 0300 0315 0062;0061 05AE 1D189 0300 0315 0062;0061 05AE 1D189 0300 0315 0062; # (a◌𝆉◌𝆉◌̕◌̀◌֮b; a◌֮◌𝆉◌𝆉◌̀◌̕b; a◌֮◌𝆉◌𝆉◌̀◌̕b; a◌֮◌𝆉◌𝆉◌̀◌̕b; a◌֮◌𝆉◌𝆉◌̀◌̕b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING BEND, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 059A 0316 302A 1D18A 0062;0061 302A 0316 1D18A 059A 0062;0061 302A 0316 1D18A 059A 0062;0061 302A 0316 1D18A 059A 0062;0061 302A 0316 1D18A 059A 0062; # (a◌֚◌̖◌〪◌𝆊◌𝆊b; a◌〪◌̖◌𝆊◌𝆊◌֚b; a◌〪◌̖◌𝆊◌𝆊◌֚b; a◌〪◌̖◌𝆊◌𝆊◌֚b; a◌〪◌̖◌𝆊◌𝆊◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING DOUBLE TONGUE, LATIN SMALL LETTER B +0061 1D18A 059A 0316 302A 0062;0061 302A 1D18A 0316 059A 0062;0061 302A 1D18A 0316 059A 0062;0061 302A 1D18A 0316 059A 0062;0061 302A 1D18A 0316 059A 0062; # (a◌𝆊◌𝆊◌֚◌̖◌〪b; a◌〪◌𝆊◌𝆊◌̖◌֚b; a◌〪◌𝆊◌𝆊◌̖◌֚b; a◌〪◌𝆊◌𝆊◌̖◌֚b; a◌〪◌𝆊◌𝆊◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING DOUBLE TONGUE, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 059A 0316 302A 1D18B 0062;0061 302A 0316 1D18B 059A 0062;0061 302A 0316 1D18B 059A 0062;0061 302A 0316 1D18B 059A 0062;0061 302A 0316 1D18B 059A 0062; # (a◌֚◌̖◌〪◌𝆋◌𝆋b; a◌〪◌̖◌𝆋◌𝆋◌֚b; a◌〪◌̖◌𝆋◌𝆋◌֚b; a◌〪◌̖◌𝆋◌𝆋◌֚b; a◌〪◌̖◌𝆋◌𝆋◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING TRIPLE TONGUE, LATIN SMALL LETTER B +0061 1D18B 059A 0316 302A 0062;0061 302A 1D18B 0316 059A 0062;0061 302A 1D18B 0316 059A 0062;0061 302A 1D18B 0316 059A 0062;0061 302A 1D18B 0316 059A 0062; # (a◌𝆋◌𝆋◌֚◌̖◌〪b; a◌〪◌𝆋◌𝆋◌̖◌֚b; a◌〪◌𝆋◌𝆋◌̖◌֚b; a◌〪◌𝆋◌𝆋◌̖◌֚b; a◌〪◌𝆋◌𝆋◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING TRIPLE TONGUE, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B +0061 0315 0300 05AE 1D1AA 0062;00E0 05AE 1D1AA 0315 0062;0061 05AE 0300 1D1AA 0315 0062;00E0 05AE 1D1AA 0315 0062;0061 05AE 0300 1D1AA 0315 0062; # (a◌̕◌̀◌֮◌𝆪◌𝆪b; à◌֮◌𝆪◌𝆪◌̕b; a◌֮◌̀◌𝆪◌𝆪◌̕b; à◌֮◌𝆪◌𝆪◌̕b; a◌֮◌̀◌𝆪◌𝆪◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING DOWN BOW, LATIN SMALL LETTER B +0061 1D1AA 0315 0300 05AE 0062;0061 05AE 1D1AA 0300 0315 0062;0061 05AE 1D1AA 0300 0315 0062;0061 05AE 1D1AA 0300 0315 0062;0061 05AE 1D1AA 0300 0315 0062; # (a◌𝆪◌𝆪◌̕◌̀◌֮b; a◌֮◌𝆪◌𝆪◌̀◌̕b; a◌֮◌𝆪◌𝆪◌̀◌̕b; a◌֮◌𝆪◌𝆪◌̀◌̕b; a◌֮◌𝆪◌𝆪◌̀◌̕b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING DOWN BOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 1D1AB 0062;00E0 05AE 1D1AB 0315 0062;0061 05AE 0300 1D1AB 0315 0062;00E0 05AE 1D1AB 0315 0062;0061 05AE 0300 1D1AB 0315 0062; # (a◌̕◌̀◌֮◌𝆫◌𝆫b; à◌֮◌𝆫◌𝆫◌̕b; a◌֮◌̀◌𝆫◌𝆫◌̕b; à◌֮◌𝆫◌𝆫◌̕b; a◌֮◌̀◌𝆫◌𝆫◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING UP BOW, LATIN SMALL LETTER B +0061 1D1AB 0315 0300 05AE 0062;0061 05AE 1D1AB 0300 0315 0062;0061 05AE 1D1AB 0300 0315 0062;0061 05AE 1D1AB 0300 0315 0062;0061 05AE 1D1AB 0300 0315 0062; # (a◌𝆫◌𝆫◌̕◌̀◌֮b; a◌֮◌𝆫◌𝆫◌̀◌̕b; a◌֮◌𝆫◌𝆫◌̀◌̕b; a◌֮◌𝆫◌𝆫◌̀◌̕b; a◌֮◌𝆫◌𝆫◌̀◌̕b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING UP BOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 1D1AC 0062;00E0 05AE 1D1AC 0315 0062;0061 05AE 0300 1D1AC 0315 0062;00E0 05AE 1D1AC 0315 0062;0061 05AE 0300 1D1AC 0315 0062; # (a◌̕◌̀◌֮◌𝆬◌𝆬b; à◌֮◌𝆬◌𝆬◌̕b; a◌֮◌̀◌𝆬◌𝆬◌̕b; à◌֮◌𝆬◌𝆬◌̕b; a◌֮◌̀◌𝆬◌𝆬◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING HARMONIC, LATIN SMALL LETTER B +0061 1D1AC 0315 0300 05AE 0062;0061 05AE 1D1AC 0300 0315 0062;0061 05AE 1D1AC 0300 0315 0062;0061 05AE 1D1AC 0300 0315 0062;0061 05AE 1D1AC 0300 0315 0062; # (a◌𝆬◌𝆬◌̕◌̀◌֮b; a◌֮◌𝆬◌𝆬◌̀◌̕b; a◌֮◌𝆬◌𝆬◌̀◌̕b; a◌֮◌𝆬◌𝆬◌̀◌̕b; a◌֮◌𝆬◌𝆬◌̀◌̕b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING HARMONIC, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +0061 0315 0300 05AE 1D1AD 0062;00E0 05AE 1D1AD 0315 0062;0061 05AE 0300 1D1AD 0315 0062;00E0 05AE 1D1AD 0315 0062;0061 05AE 0300 1D1AD 0315 0062; # (a◌̕◌̀◌֮◌𝆭◌𝆭b; à◌֮◌𝆭◌𝆭◌̕b; a◌֮◌̀◌𝆭◌𝆭◌̕b; à◌֮◌𝆭◌𝆭◌̕b; a◌֮◌̀◌𝆭◌𝆭◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING SNAP PIZZICATO, LATIN SMALL LETTER B +0061 1D1AD 0315 0300 05AE 0062;0061 05AE 1D1AD 0300 0315 0062;0061 05AE 1D1AD 0300 0315 0062;0061 05AE 1D1AD 0300 0315 0062;0061 05AE 1D1AD 0300 0315 0062; # (a◌𝆭◌𝆭◌̕◌̀◌֮b; a◌֮◌𝆭◌𝆭◌̀◌̕b; a◌֮◌𝆭◌𝆭◌̀◌̕b; a◌֮◌𝆭◌𝆭◌̀◌̕b; a◌֮◌𝆭◌𝆭◌̀◌̕b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING SNAP PIZZICATO, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B +# +# END OF FILE diff --git a/Lib/test/_test_atexit.py b/Lib/test/_test_atexit.py index f618c1fcbca52b..2e961d6a4854a0 100644 --- a/Lib/test/_test_atexit.py +++ b/Lib/test/_test_atexit.py @@ -135,6 +135,53 @@ def func(): finally: atexit.unregister(func) + def test_eq_unregister_clear(self): + # Issue #112127: callback's __eq__ may call unregister or _clear + class Evil: + def __eq__(self, other): + action(other) + return NotImplemented + + for action in atexit.unregister, lambda o: atexit._clear(): + with self.subTest(action=action): + atexit.register(lambda: None) + atexit.unregister(Evil()) + atexit._clear() + + def test_eq_unregister(self): + # Issue #112127: callback's __eq__ may call unregister + def f1(): + log.append(1) + def f2(): + log.append(2) + def f3(): + log.append(3) + + class Pred: + def __eq__(self, other): + nonlocal cnt + cnt += 1 + if cnt == when: + atexit.unregister(what) + if other is f2: + return True + return False + + for what, expected in ( + (f1, [3]), + (f2, [3, 1]), + (f3, [1]), + ): + for when in range(1, 4): + with self.subTest(what=what.__name__, when=when): + cnt = 0 + log = [] + for f in (f1, f2, f3): + atexit.register(f) + atexit.unregister(Pred()) + atexit._run_exitfuncs() + self.assertEqual(log, expected) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/_test_eintr.py b/Lib/test/_test_eintr.py index 0ce42276bfe3d6..4a050792df73c4 100644 --- a/Lib/test/_test_eintr.py +++ b/Lib/test/_test_eintr.py @@ -380,6 +380,8 @@ def os_open(self, path): @unittest.skipIf(sys.platform == "darwin", "hangs under macOS; see bpo-25234, bpo-35363") + @unittest.skipIf(sys.platform.startswith('netbsd'), + "hangs on NetBSD; see gh-137397") def test_os_open(self): self._test_open("fd = os.open(path, os.O_RDONLY)\nos.close(fd)", self.os_open) diff --git a/Lib/test/_test_embed_structseq.py b/Lib/test/_test_embed_structseq.py index 4cac84d7a469ac..c6050ca62aafca 100644 --- a/Lib/test/_test_embed_structseq.py +++ b/Lib/test/_test_embed_structseq.py @@ -47,16 +47,21 @@ def test_sys_funcs(self): self.check_structseq(type(obj)) -try: - unittest.main( - module=( - '__main__' - if __name__ == '__main__' - # Avoiding a circular import: - else sys.modules['test._test_embed_structseq'] +def main(): + try: + unittest.main( + module=( + '__main__' + if __name__ == '__main__' + # Avoiding a circular import: + else sys.modules['test._test_embed_structseq'] + ) ) - ) -except SystemExit as exc: - if exc.args[0] != 0: - raise -print("Tests passed") + except SystemExit as exc: + if exc.args[0] != 0: + raise + print("Tests passed") + + +if __name__ == "__main__": + main() diff --git a/Lib/test/_test_gc_fast_cycles.py b/Lib/test/_test_gc_fast_cycles.py deleted file mode 100644 index 4e2c7d72a02713..00000000000000 --- a/Lib/test/_test_gc_fast_cycles.py +++ /dev/null @@ -1,48 +0,0 @@ -# Run by test_gc. -from test import support -import _testinternalcapi -import gc -import unittest - -class IncrementalGCTests(unittest.TestCase): - - # Use small increments to emulate longer running process in a shorter time - @support.gc_threshold(200, 10) - def test_incremental_gc_handles_fast_cycle_creation(self): - - class LinkedList: - - #Use slots to reduce number of implicit objects - __slots__ = "next", "prev", "surprise" - - def __init__(self, next=None, prev=None): - self.next = next - if next is not None: - next.prev = self - self.prev = prev - if prev is not None: - prev.next = self - - def make_ll(depth): - head = LinkedList() - for i in range(depth): - head = LinkedList(head, head.prev) - return head - - head = make_ll(1000) - - assert(gc.isenabled()) - olds = [] - initial_heap_size = _testinternalcapi.get_tracked_heap_size() - for i in range(20_000): - newhead = make_ll(20) - newhead.surprise = head - olds.append(newhead) - if len(olds) == 20: - new_objects = _testinternalcapi.get_tracked_heap_size() - initial_heap_size - self.assertLess(new_objects, 27_000, f"Heap growing. Reached limit after {i} iterations") - del olds[:] - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index a1259ff1d63d18..115a187a8a8588 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -39,7 +39,8 @@ from test.support import socket_helper from test.support import threading_helper from test.support import warnings_helper - +from test.support import subTests +from test.support.script_helper import assert_python_failure, assert_python_ok # Skip tests if _multiprocessing wasn't built. _multiprocessing = import_helper.import_module('_multiprocessing') @@ -206,10 +207,38 @@ def spawn_check_wrapper(*args, **kwargs): return decorator +def only_run_in_forkserver_testsuite(reason): + """Returns a decorator: raises SkipTest unless fork is supported + and the current start method is forkserver. + + Combines @support.requires_fork() with the single-run semantics of + only_run_in_spawn_testsuite(), but uses the forkserver testsuite as + the single-run target. Appropriate for tests that exercise + os.fork() directly (raw fork or mp.set_start_method("fork") in a + subprocess) and don't vary by start method, since forkserver is + only available on platforms that support fork. + """ + + def decorator(test_item): + + @functools.wraps(test_item) + def forkserver_check_wrapper(*args, **kwargs): + if not support.has_fork_support: + raise unittest.SkipTest("requires working os.fork()") + if (start_method := multiprocessing.get_start_method()) != "forkserver": + raise unittest.SkipTest( + f"{start_method=}, not 'forkserver'; {reason}") + return test_item(*args, **kwargs) + + return forkserver_check_wrapper + + return decorator + + class TestInternalDecorators(unittest.TestCase): """Logic within a test suite that could errantly skip tests? Test it!""" - @unittest.skipIf(sys.platform == "win32", "test requires that fork exists.") + @support.requires_fork() def test_only_run_in_spawn_testsuite(self): if multiprocessing.get_start_method() != "spawn": raise unittest.SkipTest("only run in test_multiprocessing_spawn.") @@ -233,6 +262,30 @@ def return_four_if_spawn(): finally: multiprocessing.set_start_method(orig_start_method, force=True) + @support.requires_fork() + def test_only_run_in_forkserver_testsuite(self): + if multiprocessing.get_start_method() != "forkserver": + raise unittest.SkipTest("only run in test_multiprocessing_forkserver.") + + try: + @only_run_in_forkserver_testsuite("testing this decorator") + def return_four_if_forkserver(): + return 4 + except Exception as err: + self.fail(f"expected decorated `def` not to raise; caught {err}") + + orig_start_method = multiprocessing.get_start_method(allow_none=True) + try: + multiprocessing.set_start_method("forkserver", force=True) + self.assertEqual(return_four_if_forkserver(), 4) + multiprocessing.set_start_method("spawn", force=True) + with self.assertRaises(unittest.SkipTest) as ctx: + return_four_if_forkserver() + self.assertIn("testing this decorator", str(ctx.exception)) + self.assertIn("start_method=", str(ctx.exception)) + finally: + multiprocessing.set_start_method(orig_start_method, force=True) + # # Creates a wrapper for a function which records the time it takes to finish @@ -325,6 +378,7 @@ def test_current(self): self.assertEqual(current.ident, os.getpid()) self.assertEqual(current.exitcode, None) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_set_executable(self): if self.TYPE == 'threads': self.skipTest(f'test not appropriate for {self.TYPE}') @@ -341,6 +395,7 @@ def test_set_executable(self): p.join() self.assertEqual(p.exitcode, 0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_resource('cpu') def test_args_argument(self): # bpo-45735: Using list or tuple as *args* in constructor could @@ -388,6 +443,7 @@ def _test(cls, q, *args, **kwds): q.put(bytes(current.authkey)) q.put(current.pid) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_parent_process_attributes(self): if self.TYPE == "threads": self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -408,6 +464,7 @@ def _test_send_parent_process(cls, wconn): from multiprocessing.process import parent_process wconn.send([parent_process().pid, parent_process().name]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_parent_process(self): if self.TYPE == "threads": self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -446,6 +503,7 @@ def _test_report_parent_status(cls, wconn): parent_process().join(timeout=support.SHORT_TIMEOUT) wconn.send("alive" if parent_process().is_alive() else "not alive") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_process(self): q = self.Queue(1) e = self.Event() @@ -486,6 +544,7 @@ def test_process(self): self.assertNotIn(p, self.active_children()) close_queue(q) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(threading._HAVE_THREAD_NATIVE_ID, "needs native_id") def test_process_mainthread_native_id(self): if self.TYPE == 'threads': @@ -526,6 +585,7 @@ def _sleep_no_int_handler(cls, event): def _test_sleep(cls, delay): time.sleep(delay) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def _kill_process(self, meth, target=None): if self.TYPE == 'threads': self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -583,6 +643,7 @@ def handler(*args): return p.exitcode + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipIf(os.name == 'nt', "POSIX only") def test_interrupt(self): exitcode = self._kill_process(multiprocessing.Process.interrupt) @@ -591,15 +652,18 @@ def test_interrupt(self): # (KeyboardInterrupt in this case) # in multiprocessing.BaseProcess._bootstrap + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipIf(os.name == 'nt', "POSIX only") def test_interrupt_no_handler(self): exitcode = self._kill_process(multiprocessing.Process.interrupt, target=self._sleep_no_int_handler) self.assertEqual(exitcode, -signal.SIGINT) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_terminate(self): exitcode = self._kill_process(multiprocessing.Process.terminate) self.assertEqual(exitcode, -signal.SIGTERM) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_kill(self): exitcode = self._kill_process(multiprocessing.Process.kill) if os.name != 'nt': @@ -615,6 +679,7 @@ def test_cpu_count(self): self.assertIsInstance(cpus, int) self.assertGreaterEqual(cpus, 1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_active_children(self): self.assertEqual(type(self.active_children()), list) @@ -643,6 +708,7 @@ def _test_recursion(cls, wconn, id): p.start() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_recursion(self): rconn, wconn = self.Pipe(duplex=False) self._test_recursion(wconn, []) @@ -667,6 +733,7 @@ def test_recursion(self): def _test_sentinel(cls, event): event.wait(10.0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_sentinel(self): if self.TYPE == "threads": self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -689,6 +756,7 @@ def _test_close(cls, rc=0, q=None): q.get() sys.exit(rc) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_close(self): if self.TYPE == "threads": self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -721,6 +789,7 @@ def test_close(self): close_queue(q) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_resource('walltime') def test_many_processes(self): if self.TYPE == 'threads': @@ -757,6 +826,7 @@ def test_many_processes(self): for p in procs: self.assertIn(p.exitcode, exitcodes) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_lose_target_ref(self): c = DummyCallable() wr = weakref.ref(c) @@ -819,6 +889,7 @@ def func2(): threading.Thread(target=func1).start() threading.Thread(target=func2, daemon=True).start() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wait_for_threads(self): # A child process should wait for non-daemonic threads to end # before exiting @@ -843,6 +914,7 @@ def _test_error_on_stdio_flush(self, evt, break_std_streams={}): setattr(sys, stream_name, None) evt.set() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_error_on_stdio_flush_1(self): # Check that Process works with broken standard streams streams = [io.StringIO(), None] @@ -862,6 +934,7 @@ def test_error_on_stdio_flush_1(self): finally: setattr(sys, stream_name, old_stream) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_error_on_stdio_flush_2(self): # Same as test_error_on_stdio_flush_1(), but standard streams are # broken by the child process @@ -1012,6 +1085,7 @@ class _TestSubclassingProcess(BaseTestCase): ALLOWED_TYPES = ('processes',) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_subclassing(self): uppercaser = _UpperCaser() uppercaser.daemon = True @@ -1021,6 +1095,7 @@ def test_subclassing(self): uppercaser.stop() uppercaser.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_stderr_flush(self): # sys.stderr is flushed at process shutdown (issue #13812) if self.TYPE == "threads": @@ -1051,6 +1126,7 @@ def _test_sys_exit(cls, reason, testfn): sys.stderr = open(fd, 'w', encoding="utf-8", closefd=False) sys.exit(reason) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_sys_exit(self): # See Issue 13854 if self.TYPE == 'threads': @@ -1118,6 +1194,7 @@ def _test_put(cls, queue, child_can_start, parent_can_continue): queue.get() parent_can_continue.set() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_put(self): MAXSIZE = 6 queue = self.Queue(maxsize=MAXSIZE) @@ -1180,13 +1257,14 @@ def test_put(self): @classmethod def _test_get(cls, queue, child_can_start, parent_can_continue): child_can_start.wait() - #queue.put(1) + queue.put(1) queue.put(2) queue.put(3) queue.put(4) queue.put(5) parent_can_continue.set() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_get(self): queue = self.Queue() child_can_start = self.Event() @@ -1204,15 +1282,16 @@ def test_get(self): child_can_start.set() parent_can_continue.wait() - time.sleep(DELTA) + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if not queue_empty(queue): + break self.assertEqual(queue_empty(queue), False) - # Hangs unexpectedly, remove for now - #self.assertEqual(queue.get(), 1) + self.assertEqual(queue.get_nowait(), 1) self.assertEqual(queue.get(True, None), 2) self.assertEqual(queue.get(True), 3) self.assertEqual(queue.get(timeout=1), 4) - self.assertEqual(queue.get_nowait(), 5) + self.assertEqual(queue.get(), 5) self.assertEqual(queue_empty(queue), True) @@ -1248,6 +1327,7 @@ def _test_fork(cls, queue): # process cannot shutdown until the feeder thread has finished # pushing items onto the pipe. + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_fork(self): # Old versions of Queue would fail to create a new feeder # thread for a forked process if the original process had its @@ -1298,6 +1378,7 @@ def _test_task_done(cls, q): time.sleep(DELTA) q.task_done() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_task_done(self): queue = self.JoinableQueue() @@ -1341,6 +1422,7 @@ def test_no_import_lock_contention(self): self.fail("Probable regression on import lock contention;" " see Issue #22853") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_timeout(self): q = multiprocessing.Queue() start = time.monotonic() @@ -1464,6 +1546,7 @@ def _acquire_event(lock, event): event.set() time.sleep(1.0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_repr_lock(self): if self.TYPE != 'processes': self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -1527,6 +1610,7 @@ def _test_lock_locked_2processes(cls, lock, event, res): res.value = lock.locked() event.set() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes') def test_lock_locked_2processes(self): if self.TYPE != 'processes': @@ -1553,6 +1637,7 @@ def _acquire_release(lock, timeout, l=None, n=1): for _ in range(n): lock.release() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_repr_rlock(self): if self.TYPE != 'processes': self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -1612,6 +1697,7 @@ def test_rlock(self): self.assertFalse(lock.locked()) self.assertRaises((AssertionError, RuntimeError), lock.release) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes') def test_rlock_locked_2processes(self): if self.TYPE != 'processes': @@ -1723,6 +1809,7 @@ def check_invariant(self, cond): except NotImplementedError: pass + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_notify(self): cond = self.Condition() sleeping = self.Semaphore(0) @@ -1765,6 +1852,7 @@ def test_notify(self): threading_helper.join_thread(t) join_process(p) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_notify_all(self): cond = self.Condition() sleeping = self.Semaphore(0) @@ -1834,6 +1922,7 @@ def test_notify_all(self): # NOTE: join_process and join_thread are the same threading_helper.join_thread(w) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_notify_n(self): cond = self.Condition() sleeping = self.Semaphore(0) @@ -1907,6 +1996,7 @@ def _test_waitfor_f(cls, cond, state): if not result or state.value != 4: sys.exit(1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes') def test_waitfor(self): # based on test in test/lock_tests.py @@ -1942,6 +2032,7 @@ def _test_waitfor_timeout_f(cls, cond, state, success, sem): if not result and (expected - CLOCK_RES) <= dt: success.value = True + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes') def test_waitfor_timeout(self): # based on test in test/lock_tests.py @@ -1974,6 +2065,7 @@ def _test_wait_result(cls, c, pid): if pid is not None: os.kill(pid, signal.SIGINT) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wait_result(self): if isinstance(self, ProcessesMixin) and sys.platform != 'win32': pid = os.getpid() @@ -2002,6 +2094,7 @@ def _test_event(cls, event): time.sleep(TIMEOUT2) event.set() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_event(self): event = self.Event() wait = TimingWrapper(event.wait) @@ -2198,6 +2291,7 @@ def multipass(cls, barrier, results, n): pass assert not barrier.broken + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_barrier(self, passes=1): """ Test that a barrier is passed in lockstep @@ -2205,6 +2299,7 @@ def test_barrier(self, passes=1): results = [self.DummyList(), self.DummyList()] self.run_threads(self.multipass, (self.barrier, results, passes)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_barrier_10(self): """ Test that a barrier works for 10 consecutive runs @@ -2216,6 +2311,7 @@ def _test_wait_return_f(cls, barrier, queue): res = barrier.wait() queue.put(res) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wait_return(self): """ test the return value from barrier.wait @@ -2232,6 +2328,7 @@ def _test_action_f(cls, barrier, results): if len(results) != 1: raise RuntimeError + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_action(self): """ Test the 'action' callback @@ -2254,6 +2351,7 @@ def _test_abort_f(cls, barrier, results1, results2): except RuntimeError: barrier.abort() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_abort(self): """ Test that an abort will put the barrier in a broken state @@ -2284,6 +2382,7 @@ def _test_reset_f(cls, barrier, results1, results2, results3): barrier.wait() results3.append(True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_reset(self): """ Test that a 'reset' on a barrier frees the waiting threads @@ -2319,6 +2418,7 @@ def _test_abort_and_reset_f(cls, barrier, barrier2, barrier.wait() results3.append(True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_abort_and_reset(self): """ Test that a barrier can be reset after being broken. @@ -2345,6 +2445,7 @@ def _test_timeout_f(cls, barrier, results): except threading.BrokenBarrierError: results.append(True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_timeout(self): """ Test wait(timeout) @@ -2364,6 +2465,7 @@ def _test_default_timeout_f(cls, barrier, results): except threading.BrokenBarrierError: results.append(True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_default_timeout(self): """ Test the barrier's default timeout @@ -2385,6 +2487,7 @@ def _test_thousand_f(cls, barrier, passes, conn, lock): with lock: conn.send(i) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_thousand(self): if self.TYPE == 'manager': self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -2426,7 +2529,7 @@ def _test(cls, values): for sv, cv in zip(values, cls.codes_values): sv.value = cv[2] - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_value(self, raw=False): if raw: values = [self.RawValue(code, value) @@ -2490,6 +2593,7 @@ def f(cls, seq): for i in range(1, len(seq)): seq[i] += seq[i-1] + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipIf(c_int is None, "requires _ctypes") def test_array(self, raw=False): seq = [680, 626, 934, 821, 150, 233, 548, 982, 714, 831] @@ -2801,8 +2905,9 @@ class _TestPool(BaseTestCase): @classmethod def setUpClass(cls): - super().setUpClass() - cls.pool = cls.Pool(4) + with warnings_helper.ignore_fork_in_thread_deprecation_warnings(): + super().setUpClass() + cls.pool = cls.Pool(4) @classmethod def tearDownClass(cls): @@ -2901,6 +3006,7 @@ def test_async(self): self.assertEqual(get(), 49) self.assertTimingAlmostEqual(get.elapsed, TIMEOUT1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_async_timeout(self): p = self.Pool(3) try: @@ -2998,6 +3104,7 @@ def test_imap_unordered_handle_iterable_exception(self): self.assertIn(value, expected_values) expected_values.remove(value) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_make_pool(self): expected_error = (RemoteError if self.TYPE == 'manager' else ValueError) @@ -3013,6 +3120,7 @@ def test_make_pool(self): p.close() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_terminate(self): # Simulate slow tasks which take "forever" to complete sleep_time = support.LONG_TIMEOUT @@ -3030,6 +3138,7 @@ def test_terminate(self): p.terminate() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_empty_iterable(self): # See Issue 12157 p = self.Pool(1) @@ -3042,6 +3151,7 @@ def test_empty_iterable(self): p.close() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_context(self): if self.TYPE == 'processes': L = list(range(10)) @@ -3056,6 +3166,7 @@ def test_context(self): def _test_traceback(cls): raise RuntimeError(123) # some comment + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_traceback(self): # We want ensure that the traceback from the child process is # contained in the traceback raised in the main process. @@ -3095,9 +3206,11 @@ def test_traceback(self): p.join() @classmethod + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def _test_wrapped_exception(cls): raise RuntimeError('foo') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wrapped_exception(self): # Issue #20980: Should not wrap exception when using thread pool with self.Pool(1) as p: @@ -3105,6 +3218,7 @@ def test_wrapped_exception(self): p.apply(self._test_wrapped_exception) p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_no_failfast(self): # Issue #23992: the fail-fast behaviour when an exception is raised # during map() would make Pool.join() deadlock, because a worker @@ -3140,6 +3254,7 @@ def test_release_task_refs(self): # they were released too. self.assertEqual(CountedObject.n_instances, 0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_enter(self): if self.TYPE == 'manager': self.skipTest("test not applicable to manager") @@ -3156,6 +3271,7 @@ def test_enter(self): pass pool.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_resource_warning(self): if self.TYPE == 'manager': self.skipTest("test not applicable to manager") @@ -3181,6 +3297,7 @@ def unpickleable_result(): class _TestPoolWorkerErrors(BaseTestCase): ALLOWED_TYPES = ('processes', ) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_async_error_callback(self): p = multiprocessing.Pool(2) @@ -3196,6 +3313,7 @@ def errback(exc): p.close() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_unpickleable_result(self): from multiprocessing.pool import MaybeEncodingError p = multiprocessing.Pool(2) @@ -3221,6 +3339,7 @@ def errback(exc): class _TestPoolWorkerLifetime(BaseTestCase): ALLOWED_TYPES = ('processes', ) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pool_worker_lifetime(self): p = multiprocessing.Pool(3, maxtasksperchild=10) self.assertEqual(3, len(p._pool)) @@ -3250,6 +3369,7 @@ def test_pool_worker_lifetime(self): p.close() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pool_worker_lifetime_early_close(self): # Issue #10332: closing a pool whose workers have limited lifetimes # before all the tasks completed would make join() hang. @@ -3323,6 +3443,8 @@ class _TestMyManager(BaseTestCase): ALLOWED_TYPES = ('manager',) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + @support.skip_if_sanitizer('TSan: leaks threads', thread=True) def test_mymanager(self): manager = MyManager(shutdown_timeout=SHUTDOWN_TIMEOUT) manager.start() @@ -3334,6 +3456,8 @@ def test_mymanager(self): # which happens on slow buildbots. self.assertIn(manager._process.exitcode, (0, -signal.SIGTERM)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + @support.skip_if_sanitizer('TSan: leaks threads', thread=True) def test_mymanager_context(self): manager = MyManager(shutdown_timeout=SHUTDOWN_TIMEOUT) with manager: @@ -3343,6 +3467,8 @@ def test_mymanager_context(self): # which happens on slow buildbots. self.assertIn(manager._process.exitcode, (0, -signal.SIGTERM)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + @support.skip_if_sanitizer('TSan: leaks threads', thread=True) def test_mymanager_context_prestarted(self): manager = MyManager(shutdown_timeout=SHUTDOWN_TIMEOUT) manager.start() @@ -3413,6 +3539,8 @@ def _putter(cls, address, authkey): # Note that xmlrpclib will deserialize object as a list not a tuple queue.put(tuple(cls.values)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + @support.skip_if_sanitizer('TSan: leaks threads', thread=True) def test_remote(self): authkey = os.urandom(32) @@ -3454,6 +3582,8 @@ def _putter(cls, address, authkey): queue = manager.get_queue() queue.put('hello world') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + @support.skip_if_sanitizer("TSan: leaks threads", thread=True) def test_rapid_restart(self): authkey = os.urandom(32) manager = QueueManager( @@ -3506,12 +3636,14 @@ def recv(self): class TestManagerExceptions(unittest.TestCase): # Issue 106558: Manager exceptions avoids creating cyclic references. def setUp(self): - self.mgr = multiprocessing.Manager() + with warnings_helper.ignore_fork_in_thread_deprecation_warnings(): + self.mgr = multiprocessing.Manager() def tearDown(self): self.mgr.shutdown() self.mgr.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_queue_get(self): queue = self.mgr.Queue() if gc.isenabled(): @@ -3523,6 +3655,7 @@ def test_queue_get(self): wr = weakref.ref(e) self.assertEqual(wr(), None) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_dispatch(self): if gc.isenabled(): gc.disable() @@ -3549,6 +3682,7 @@ def _echo(cls, conn): conn.send_bytes(msg) conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_connection(self): conn, child_conn = self.Pipe() @@ -3641,6 +3775,7 @@ def test_duplex_false(self): self.assertRaises(OSError, writer.recv) self.assertRaises(OSError, writer.poll) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_spawn_close(self): # We test that a pipe connection can be closed by parent # process immediately after child is spawned. On Windows this @@ -3717,6 +3852,7 @@ def _writefd(cls, conn, data, create_dummy_fds=False): os.write(fd, data) os.close(fd) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_REDUCTION, "test needs multiprocessing.reduction") def test_fd_transfer(self): if self.TYPE != 'processes': @@ -3736,6 +3872,7 @@ def test_fd_transfer(self): with open(os_helper.TESTFN, "rb") as f: self.assertEqual(f.read(), b"foo") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_REDUCTION, "test needs multiprocessing.reduction") @unittest.skipIf(sys.platform == "win32", "test semantics don't make sense on Windows") @@ -3773,6 +3910,7 @@ def test_large_fd_transfer(self): def _send_data_without_fd(self, conn): os.write(conn.fileno(), b"\0") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_REDUCTION, "test needs multiprocessing.reduction") @unittest.skipIf(sys.platform == "win32", "doesn't make sense on Windows") def test_missing_fd_transfer(self): @@ -3804,6 +3942,19 @@ def test_context(self): self.assertRaises(OSError, a.recv) self.assertRaises(OSError, b.recv) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + def test_wait_empty(self): + if self.TYPE != 'processes': + self.skipTest('test not appropriate for {}'.format(self.TYPE)) + # gh-145587: wait() with empty list should respect timeout + timeout = 0.5 + start = time.monotonic() + res = self.connection.wait([], timeout=timeout) + duration = time.monotonic() - start + + self.assertEqual(res, []) + self.assertGreaterEqual(duration, timeout - 0.1) + class _TestListener(BaseTestCase): ALLOWED_TYPES = ('processes',) @@ -3872,6 +4023,7 @@ def _test(cls, address): conn.send('hello') conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_listener_client(self): for family in self.connection.families: l = self.connection.Listener(family=family) @@ -3883,6 +4035,7 @@ def test_listener_client(self): p.join() l.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_issue14725(self): l = self.connection.Listener() p = self.Process(target=self._test, args=(l.address,)) @@ -3928,6 +4081,7 @@ def _child_strings(cls, conn, strings): conn.send_bytes(s) conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_strings(self): strings = (b'hello', b'', b'a', b'b', b'', b'bye', b'', b'lop') a, b = self.Pipe() @@ -3951,6 +4105,7 @@ def _child_boundaries(cls, r): # read from it. r.poll(5) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_boundaries(self): r, w = self.Pipe(False) p = self.Process(target=self._child_boundaries, args=(r,)) @@ -3969,6 +4124,7 @@ def _child_dont_merge(cls, b): b.send_bytes(b'b') b.send_bytes(b'cd') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_dont_merge(self): a, b = self.Pipe() self.assertEqual(a.poll(0.0), False) @@ -4037,6 +4193,7 @@ def _remote(cls, conn): conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pickling(self): families = self.connection.families @@ -4095,6 +4252,7 @@ def child_access(cls, conn): conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_access(self): # On Windows, if we do not specify a destination pid when # using DupHandle then we need to be careful to use the @@ -4258,6 +4416,7 @@ def _double(cls, x, y, z, foo, arr, string): for i in range(len(arr)): arr[i] *= 2 + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_sharedctypes(self, lock=False): x = Value('i', 7, lock=lock) y = Value(c_double, 1.0/3.0, lock=lock) @@ -4295,6 +4454,19 @@ def test_copy(self): self.assertEqual(bar.z, 2 ** 33) +def resource_tracker_format_subtests(func): + """Run given test using both resource tracker communication formats""" + def _inner(self, *args, **kwargs): + tracker = resource_tracker._resource_tracker + for use_simple_format in False, True: + with ( + self.subTest(use_simple_format=use_simple_format), + unittest.mock.patch.object( + tracker, '_use_simple_format', use_simple_format) + ): + func(self, *args, **kwargs) + return _inner + @unittest.skipUnless(HAS_SHMEM, "requires multiprocessing.shared_memory") @hashlib_helper.requires_hashdigest('sha256') class _TestSharedMemory(BaseTestCase): @@ -4519,6 +4691,7 @@ def test_shared_memory_pickle_unpickle_dead_object(self): with self.assertRaises(FileNotFoundError): pickle.loads(pickled_sms) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shared_memory_across_processes(self): # bpo-40135: don't define shared memory block's name in case of # the failure when we run multiprocessing tests in parallel. @@ -4547,6 +4720,7 @@ def test_shared_memory_across_processes(self): sms.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipIf(os.name != "posix", "not feasible in non-posix platforms") def test_shared_memory_SharedMemoryServer_ignores_sigint(self): # bpo-36368: protect SharedMemoryManager server process from @@ -4572,6 +4746,7 @@ def test_shared_memory_SharedMemoryServer_ignores_sigint(self): smm.shutdown() @unittest.skipIf(os.name != "posix", "resource_tracker is posix only") + @resource_tracker_format_subtests def test_shared_memory_SharedMemoryManager_reuses_resource_tracker(self): # bpo-36867: test that a SharedMemoryManager uses the # same resource_tracker process as its parent. @@ -4592,6 +4767,7 @@ def test_shared_memory_SharedMemoryManager_reuses_resource_tracker(self): # properly released sl. self.assertFalse(err) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shared_memory_SharedMemoryManager_basics(self): smm1 = multiprocessing.managers.SharedMemoryManager() with self.assertRaises(ValueError): @@ -4822,6 +4998,7 @@ def test_shared_memory_cleaned_after_process_termination(self): "shared_memory objects to clean up at shutdown", err) @unittest.skipIf(os.name != "posix", "resource_tracker is posix only") + @resource_tracker_format_subtests def test_shared_memory_untracking(self): # gh-82300: When a separate Python process accesses shared memory # with track=False, it must not cause the memory to be deleted @@ -4849,6 +5026,7 @@ def test_shared_memory_untracking(self): mem.close() @unittest.skipIf(os.name != "posix", "resource_tracker is posix only") + @resource_tracker_format_subtests def test_shared_memory_tracking(self): # gh-82300: When a separate Python process accesses shared memory # with track=True, it must cause the memory to be deleted when @@ -4931,6 +5109,7 @@ class Foo(object): conn.close() os._exit(0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_finalize(self): conn, child_conn = self.Pipe() @@ -5058,6 +5237,7 @@ def _test_level(cls, conn): logger = multiprocessing.get_logger() conn.send(logger.getEffectiveLevel()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_level(self): LEVEL1 = 32 LEVEL2 = 37 @@ -5142,6 +5322,7 @@ def _killer(cls, pid): time.sleep(0.1) os.kill(pid, signal.SIGUSR1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1') def test_poll_eintr(self): got_signal = [False] @@ -5185,6 +5366,23 @@ def test_invalid_handles(self): multiprocessing.connection.Connection, -1) +# +# Regression tests for BaseProcess kwargs handling +# + +class TestBaseProcessKwargs(unittest.TestCase): + def test_default_kwargs_not_shared_between_instances(self): + # Creating multiple Process instances without passing kwargs + # must create independent empty dicts (no shared state). + p1 = multiprocessing.Process(target=lambda: None) + p2 = multiprocessing.Process(target=lambda: None) + self.assertIsInstance(p1._kwargs, dict) + self.assertIsInstance(p2._kwargs, dict) + self.assertIsNot(p1._kwargs, p2._kwargs) + # Mutating one should not affect the other + p1._kwargs['x'] = 1 + self.assertNotIn('x', p2._kwargs) + @hashlib_helper.requires_hashdigest('sha256') class OtherTest(unittest.TestCase): @@ -5264,14 +5462,16 @@ def initializer(ns): @hashlib_helper.requires_hashdigest('sha256') class TestInitializers(unittest.TestCase): def setUp(self): - self.mgr = multiprocessing.Manager() - self.ns = self.mgr.Namespace() - self.ns.test = 0 + with warnings_helper.ignore_fork_in_thread_deprecation_warnings(): + self.mgr = multiprocessing.Manager() + self.ns = self.mgr.Namespace() + self.ns.test = 0 def tearDown(self): self.mgr.shutdown() self.mgr.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_manager_initializer(self): m = multiprocessing.managers.SyncManager() self.assertRaises(TypeError, m.start, 1) @@ -5280,6 +5480,7 @@ def test_manager_initializer(self): m.shutdown() m.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pool_initializer(self): self.assertRaises(TypeError, multiprocessing.Pool, initializer=1) p = multiprocessing.Pool(1, initializer, (self.ns,)) @@ -5337,16 +5538,19 @@ def flush(self): class TestStdinBadfiledescriptor(unittest.TestCase): + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_queue_in_process(self): proc = multiprocessing.Process(target=_test_process) proc.start() proc.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pool_in_process(self): p = multiprocessing.Process(target=pool_in_process) p.start() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_flushing(self): sio = io.StringIO() flike = _file_like(sio) @@ -5366,6 +5570,7 @@ def _child_test_wait(cls, w, slow): w.send((i, os.getpid())) w.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wait(self, slow=False): from multiprocessing.connection import wait readers = [] @@ -5406,6 +5611,7 @@ def _child_test_wait_socket(cls, address, slow): s.sendall(('%s\n' % i).encode('ascii')) s.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wait_socket(self, slow=False): from multiprocessing.connection import wait l = socket.create_server((socket_helper.HOST, 0)) @@ -5470,6 +5676,7 @@ def signal_and_sleep(cls, sem, period): sem.release() time.sleep(period) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_resource('walltime') def test_wait_integer(self): from multiprocessing.connection import wait @@ -5514,6 +5721,7 @@ def test_wait_integer(self): p.terminate() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_neg_timeout(self): from multiprocessing.connection import wait a, b = multiprocessing.Pipe() @@ -5591,6 +5799,7 @@ def _test_timeout(cls, child, address): conn.send(456) conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_timeout(self): old_timeout = socket.getdefaulttimeout() try: @@ -5648,6 +5857,7 @@ def child(cls, n, conn): conn.send(len(util._afterfork_registry)) conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_lock(self): r, w = multiprocessing.Pipe(False) l = util.ForkAwareThreadLock() @@ -5699,6 +5909,7 @@ def _test_closefds(cls, conn, fd): s.close() conn.send(None) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_closefd(self): if not HAS_REDUCTION: raise unittest.SkipTest('requires fd pickling') @@ -5744,6 +5955,7 @@ def handler(signum, frame): conn.send(x) conn.send_bytes(b'x' * cls.CONN_MAX_SIZE) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1') def test_ignore(self): conn, child_conn = multiprocessing.Pipe() @@ -5777,6 +5989,7 @@ def handler(signum, frame): a = l.accept() a.send('welcome') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1') def test_ignore_listener(self): conn, child_conn = multiprocessing.Pipe() @@ -5811,6 +6024,7 @@ def check_context(self, ctx): p.join() self.assertEqual(child_method, ctx.get_start_method()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_context(self): for method in ('fork', 'spawn', 'forkserver'): try: @@ -5823,6 +6037,40 @@ def test_context(self): self.assertRaises(ValueError, ctx.set_start_method, None) self.check_context(ctx) + @staticmethod + def _dummy_func(): + pass + + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + def test_spawn_dont_set_context(self): + # Run a process with spawn or forkserver context may change + # the global start method, see gh-109263. + for method in ('fork', 'spawn', 'forkserver'): + multiprocessing.set_start_method(None, force=True) + + try: + ctx = multiprocessing.get_context(method) + except ValueError: + continue + process = ctx.Process(target=self._dummy_func) + process.start() + process.join() + self.assertIsNone(multiprocessing.get_start_method(allow_none=True)) + + @only_run_in_spawn_testsuite("freeze_support is not start method specific") + def test_freeze_support_dont_set_context(self): + # gh-140814: freeze_support() should not set the start method + # as a side effect, so a later set_start_method() still works. + multiprocessing.set_start_method(None, force=True) + try: + multiprocessing.freeze_support() + self.assertIsNone( + multiprocessing.get_start_method(allow_none=True)) + # Should not raise "context has already been set" + multiprocessing.set_start_method('spawn') + finally: + multiprocessing.set_start_method(None, force=True) + def test_context_check_module_types(self): try: ctx = multiprocessing.get_context('forkserver') @@ -5831,6 +6079,7 @@ def test_context_check_module_types(self): with self.assertRaisesRegex(TypeError, 'module_names must be a list of strings'): ctx.set_forkserver_preload([1, 2, 3]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_set_get(self): multiprocessing.set_forkserver_preload(PRELOAD) count = 0 @@ -5888,12 +6137,16 @@ def test_preload_resources(self): print(err) self.fail("failed spawning forkserver or grandchild") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipIf(sys.platform == "win32", "Only Spawn on windows so no risk of mixing") @only_run_in_spawn_testsuite("avoids redundant testing.") def test_mixed_startmethod(self): # Fork-based locks cannot be used with spawned process - for process_method in ["spawn", "forkserver"]: + test_methods = ["spawn"] + if "forkserver" in multiprocessing.get_all_start_methods(): + test_methods.append("forkserver") + for process_method in test_methods: queue = multiprocessing.get_context("fork").Queue() process_ctx = multiprocessing.get_context(process_method) p = process_ctx.Process(target=close_queue, args=(queue,)) @@ -5902,7 +6155,7 @@ def test_mixed_startmethod(self): p.start() # non-fork-based locks can be used with all other start methods - for queue_method in ["spawn", "forkserver"]: + for queue_method in test_methods: for process_method in multiprocessing.get_all_start_methods(): queue = multiprocessing.get_context(queue_method).Queue() process_ctx = multiprocessing.get_context(process_method) @@ -5921,6 +6174,7 @@ def _put_two_and_nest_once(cls, queue): process.start() process.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_nested_startmethod(self): # gh-108520: Regression test to ensure that child process can send its # arguments to another process @@ -6056,6 +6310,8 @@ def test_resource_tracker_sigint(self): # Catchable signal (ignored by semaphore tracker) self.check_resource_tracker_death(signal.SIGINT, False) + @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'), + 'need signal.pthread_sigmask') def test_resource_tracker_sigterm(self): # Catchable signal (ignored by semaphore tracker) self.check_resource_tracker_death(signal.SIGTERM, False) @@ -6070,12 +6326,14 @@ def test_resource_tracker_sigkill(self): def _is_resource_tracker_reused(conn, pid): from multiprocessing.resource_tracker import _resource_tracker _resource_tracker.ensure_running() - # The pid should be None in the child process, expect for the fork - # context. It should not be a new value. + # The pid should be None in the child (the at-fork handler clears + # it for fork; spawn/forkserver children never had it set). It + # should not be a new value. reused = _resource_tracker._pid in (None, pid) reused &= _resource_tracker._check_alive() conn.send(reused) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_resource_tracker_reused(self): from multiprocessing.resource_tracker import _resource_tracker _resource_tracker.ensure_running() @@ -6156,6 +6414,183 @@ def test_resource_tracker_blocked_signals(self): # restore sigmask to what it was before executing test signal.pthread_sigmask(signal.SIG_SETMASK, orig_sigmask) + @only_run_in_forkserver_testsuite("avoids redundant testing.") + def test_resource_tracker_fork_deadlock(self): + # gh-146313: ResourceTracker.__del__ used to deadlock if a forked + # child still held the pipe's write end open when the parent + # exited, because the parent would block in waitpid() waiting for + # the tracker to exit, but the tracker would never see EOF. + cmd = '''if 1: + import os, signal + from multiprocessing.resource_tracker import ensure_running + ensure_running() + if os.fork() == 0: + signal.pause() + os._exit(0) + # parent falls through and exits, triggering __del__ + ''' + proc = subprocess.Popen([sys.executable, '-c', cmd], + start_new_session=True) + try: + try: + proc.wait(timeout=support.SHORT_TIMEOUT) + except subprocess.TimeoutExpired: + self.fail( + "Parent process deadlocked in ResourceTracker.__del__" + ) + self.assertEqual(proc.returncode, 0) + finally: + try: + os.killpg(proc.pid, signal.SIGKILL) + except ProcessLookupError: + pass + proc.wait() + + @only_run_in_forkserver_testsuite("avoids redundant testing.") + def test_resource_tracker_mp_fork_reuse_and_prompt_reap(self): + # gh-146313 / gh-80849: A child started via multiprocessing.Process + # with the 'fork' start method should reuse the parent's resource + # tracker (the at-fork handler preserves the inherited pipe fd), + # *and* the parent should be able to reap the tracker promptly + # after joining the child, without hitting the waitpid timeout. + cmd = textwrap.dedent(''' + import multiprocessing as mp + from multiprocessing.resource_tracker import _resource_tracker + + def child(conn): + # Prove we can talk to the parent's tracker by registering + # and unregistering a dummy resource over the inherited fd. + # If the fd were closed, ensure_running would launch a new + # tracker and _pid would be non-None. + _resource_tracker.register("x", "dummy") + _resource_tracker.unregister("x", "dummy") + conn.send((_resource_tracker._fd is not None, + _resource_tracker._pid is None, + _resource_tracker._check_alive())) + + if __name__ == "__main__": + mp.set_start_method("fork") + _resource_tracker.ensure_running() + r, w = mp.Pipe(duplex=False) + p = mp.Process(target=child, args=(w,)) + p.start() + child_has_fd, child_pid_none, child_alive = r.recv() + p.join() + w.close(); r.close() + + # Now simulate __del__: the child has exited and released + # its fd copy, so the tracker should see EOF and exit + # promptly -- no timeout. + _resource_tracker._stop(wait_timeout=5.0) + print(child_has_fd, child_pid_none, child_alive, + _resource_tracker._waitpid_timed_out, + _resource_tracker._exitcode) + ''') + rc, out, err = script_helper.assert_python_ok('-c', cmd) + parts = out.decode().split() + self.assertEqual(parts, ['True', 'True', 'True', 'False', '0'], + f"unexpected: {parts!r} stderr={err!r}") + + @only_run_in_forkserver_testsuite("avoids redundant testing.") + def test_resource_tracker_raw_fork_prompt_reap(self): + # gh-146313: After a raw os.fork() the at-fork handler closes the + # child's inherited fd, so the parent can reap the tracker + # immediately -- even while the child is still alive -- rather + # than waiting out the 1s timeout. + cmd = textwrap.dedent(''' + import os, signal + from multiprocessing.resource_tracker import _resource_tracker + + _resource_tracker.ensure_running() + r, w = os.pipe() + pid = os.fork() + if pid == 0: + os.close(r) + # Report whether our fd was closed by the at-fork handler. + os.write(w, b"1" if _resource_tracker._fd is None else b"0") + os.close(w) + signal.pause() # stay alive so parent's reap is meaningful + os._exit(0) + os.close(w) + child_fd_closed = os.read(r, 1) == b"1" + os.close(r) + + # Child is still alive and paused. Because it closed its fd + # copy, our close below is the last one and the tracker exits. + _resource_tracker._stop(wait_timeout=5.0) + + os.kill(pid, signal.SIGKILL) + os.waitpid(pid, 0) + print(child_fd_closed, + _resource_tracker._waitpid_timed_out, + _resource_tracker._exitcode) + ''') + rc, out, err = script_helper.assert_python_ok('-c', cmd) + parts = out.decode().split() + self.assertEqual(parts, ['True', 'False', '0'], + f"unexpected: {parts!r} stderr={err!r}") + + @only_run_in_forkserver_testsuite("avoids redundant testing.") + def test_resource_tracker_lock_reinit_after_fork(self): + # gh-146313: If a parent thread held the tracker's lock at fork + # time, the child would inherit the held lock and deadlock on + # its next ensure_running(). The at-fork handler reinits it. + cmd = textwrap.dedent(''' + import os, threading + from multiprocessing.resource_tracker import _resource_tracker + + held = threading.Event() + release = threading.Event() + def hold(): + with _resource_tracker._lock: + held.set() + release.wait() + t = threading.Thread(target=hold) + t.start() + held.wait() + + pid = os.fork() + if pid == 0: + ok = _resource_tracker._lock.acquire(timeout=5.0) + os._exit(0 if ok else 1) + + release.set() + t.join() + _, status = os.waitpid(pid, 0) + print(os.waitstatus_to_exitcode(status)) + ''') + rc, out, err = script_helper.assert_python_ok( + '-W', 'ignore::DeprecationWarning', '-c', cmd) + self.assertEqual(out.strip(), b'0', + f"child failed to acquire lock: stderr={err!r}") + + @only_run_in_forkserver_testsuite("avoids redundant testing.") + def test_resource_tracker_safety_net_timeout(self): + # gh-146313: When an mp.Process(fork) child holds the preserved + # fd and the parent calls _stop() without joining (simulating + # abnormal shutdown), the safety-net timeout should fire rather + # than deadlocking. + cmd = textwrap.dedent(''' + import multiprocessing as mp + import signal + from multiprocessing.resource_tracker import _resource_tracker + + if __name__ == "__main__": + mp.set_start_method("fork") + _resource_tracker.ensure_running() + p = mp.Process(target=signal.pause) + p.start() + # Stop WITHOUT joining -- child still holds preserved fd + _resource_tracker._stop(wait_timeout=0.5) + print(_resource_tracker._waitpid_timed_out) + p.terminate() + p.join() + ''') + rc, out, err = script_helper.assert_python_ok('-c', cmd) + self.assertEqual(out.strip(), b'True', + f"safety-net timeout did not fire: stderr={err!r}") + + class TestSimpleQueue(unittest.TestCase): @classmethod @@ -6177,6 +6612,7 @@ def test_empty_exceptions(self): with self.assertRaisesRegex(OSError, 'is closed'): q.empty() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_empty(self): queue = multiprocessing.SimpleQueue() child_can_start = multiprocessing.Event() @@ -6284,7 +6720,8 @@ def test_list(self): def setUp(self): self.manager = self.manager_class() - self.manager.start() + with warnings_helper.ignore_fork_in_thread_deprecation_warnings(): + self.manager.start() self.proc = None def tearDown(self): @@ -6333,6 +6770,7 @@ def _test_event(cls, obj): obj.clear() obj.wait(0.001) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_event(self): o = self.manager.Event() o.set() @@ -6345,6 +6783,7 @@ def _test_lock(cls, obj): obj.acquire() obj.locked() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_lock(self, lname="Lock"): o = getattr(self.manager, lname)() self.run_worker(self._test_lock, o) @@ -6357,6 +6796,7 @@ def _test_rlock(cls, obj): obj.release() obj.locked() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_rlock(self, lname="RLock"): o = getattr(self.manager, lname)() self.run_worker(self._test_rlock, o) @@ -6365,6 +6805,7 @@ def test_rlock(self, lname="RLock"): def _test_semaphore(cls, obj): obj.acquire() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_semaphore(self, sname="Semaphore"): o = getattr(self.manager, sname)() self.run_worker(self._test_semaphore, o) @@ -6378,6 +6819,7 @@ def _test_condition(cls, obj): obj.acquire() obj.release() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_condition(self): o = self.manager.Condition() self.run_worker(self._test_condition, o) @@ -6387,6 +6829,7 @@ def _test_barrier(cls, obj): assert obj.parties == 5 obj.reset() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_barrier(self): o = self.manager.Barrier(5) self.run_worker(self._test_barrier, o) @@ -6397,6 +6840,7 @@ def _test_pool(cls, obj): with obj: pass + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pool(self): o = self.manager.Pool(processes=4) self.run_worker(self._test_pool, o) @@ -6411,6 +6855,7 @@ def _test_queue(cls, obj): assert obj.get() == 6 assert obj.empty() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_queue(self, qname="Queue"): o = getattr(self.manager, qname)(2) o.put(5) @@ -6419,6 +6864,7 @@ def test_queue(self, qname="Queue"): assert o.empty() assert not o.full() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_joinable_queue(self): self.test_queue("JoinableQueue") @@ -6453,6 +6899,7 @@ def _test_list(cls, obj): obj.clear() case.assertEqual(len(obj), 0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_list(self): o = self.manager.list() o.append(5) @@ -6494,6 +6941,7 @@ def _test_dict(cls, obj): obj.clear() case.assertEqual(len(obj), 0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_dict(self): o = self.manager.dict() o['foo'] = 5 @@ -6508,6 +6956,7 @@ def _test_value(cls, obj): case.assertEqual(obj.get(), 1) obj.set(2) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_value(self): o = self.manager.Value('i', 1) self.run_worker(self._test_value, o) @@ -6522,6 +6971,7 @@ def _test_array(cls, obj): case.assertEqual(len(obj), 2) case.assertListEqual(list(obj), [0, 1]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_array(self): o = self.manager.Array('i', [0, 1]) self.run_worker(self._test_array, o) @@ -6532,6 +6982,7 @@ def _test_namespace(cls, obj): case.assertEqual(obj.x, 0) case.assertEqual(obj.y, 1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_namespace(self): o = self.manager.Namespace() o.x = 0 @@ -6651,6 +7102,7 @@ def _test_set_comparisons(cls, obj): case.assertGreater(obj, {'a'}) case.assertGreaterEqual(obj, {'a', 'b'}) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_set(self): o = self.manager.set() self.run_worker(self._test_set_operator_symbols, o) @@ -6668,6 +7120,7 @@ def test_set_init(self): self.assertSetEqual(o, {"a", "b", "c"}) self.assertRaises(RemoteError, self.manager.set, 1234) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_set_contain_all_method(self): o = self.manager.set() set_methods = { @@ -6721,6 +7174,7 @@ def exit_handler(): f.write("deadbeef") atexit.register(exit_handler) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_atexit(self): # gh-83856 with os_helper.temp_dir() as temp_dir: @@ -6807,28 +7261,13 @@ def test_std_streams_flushed_after_preload(self): if multiprocessing.get_start_method() != "forkserver": self.skipTest("forkserver specific test") - # Create a test module in the temporary directory on the child's path - # TODO: This can all be simplified once gh-126631 is fixed and we can - # use __main__ instead of a module. - dirname = os.path.join(self._temp_dir, 'preloaded_module') - init_name = os.path.join(dirname, '__init__.py') - os.mkdir(dirname) - with open(init_name, "w") as f: - cmd = '''if 1: - import sys - print('stderr', end='', file=sys.stderr) - print('stdout', end='', file=sys.stdout) - ''' - f.write(cmd) - name = os.path.join(os.path.dirname(__file__), 'mp_preload_flush.py') - env = {'PYTHONPATH': self._temp_dir} - _, out, err = test.support.script_helper.assert_python_ok(name, **env) + _, out, err = test.support.script_helper.assert_python_ok(name) # Check stderr first, as it is more likely to be useful to see in the # event of a failure. - self.assertEqual(err.decode().rstrip(), 'stderr') - self.assertEqual(out.decode().rstrip(), 'stdout') + self.assertEqual(err.decode().rstrip(), '__main____mp_main__') + self.assertEqual(out.decode().rstrip(), '__main____mp_main__') class MiscTestCase(unittest.TestCase): @@ -6873,6 +7312,7 @@ def f(x): return x*x self.assertEqual("332833500", out.decode('utf-8').strip()) self.assertFalse(err, msg=err.decode('utf-8')) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_forked_thread_not_started(self): # gh-134381: Ensure that a thread that has not been started yet in # the parent process can be started within a forked child process. @@ -6895,6 +7335,55 @@ def child(): self.assertEqual(q.get_nowait(), "done") close_queue(q) + def test_preload_main(self): + # gh-126631: Check that __main__ can be pre-loaded + if multiprocessing.get_start_method() != "forkserver": + self.skipTest("forkserver specific test") + + name = os.path.join(os.path.dirname(__file__), 'mp_preload_main.py') + _, out, err = test.support.script_helper.assert_python_ok(name) + self.assertEqual(err, b'') + + # The trailing empty string comes from split() on output ending with \n + out = out.decode().split("\n") + self.assertEqual(out, ['__main__', '__mp_main__', 'f', 'f', '']) + + def test_preload_main_sys_argv(self): + # gh-143706: Check that sys.argv is set before __main__ is pre-loaded + if multiprocessing.get_start_method() != "forkserver": + self.skipTest("forkserver specific test") + + name = os.path.join(os.path.dirname(__file__), 'mp_preload_sysargv.py') + _, out, err = test.support.script_helper.assert_python_ok( + name, 'foo', 'bar') + self.assertEqual(err, b'') + + out = out.decode().split("\n") + expected_argv = "['foo', 'bar']" + self.assertEqual(out, [ + f"module:{expected_argv}", + f"fun:{expected_argv}", + f"module:{expected_argv}", + f"fun:{expected_argv}", + '', + ]) + + @only_run_in_forkserver_testsuite("forkserver specific test.") + def test_preload_main_large_sys_argv(self): + # gh-144503: a very large parent sys.argv must not prevent the + # forkserver from starting (it previously overflowed the OS + # per-argument length limit when repr'd into the -c command string). + name = os.path.join(os.path.dirname(__file__), + 'mp_preload_large_sysargv.py') + _, out, err = test.support.script_helper.assert_python_ok(name) + self.assertEqual(err, b'') + + out = out.decode().split("\n") + self.assertEqual(out, [ + 'preload:5002:sentinel', + 'worker:5002:sentinel', + '', + ]) # # Mixins @@ -6974,8 +7463,9 @@ def Pool(cls, *args, **kwds): @classmethod def setUpClass(cls): - super().setUpClass() - cls.manager = multiprocessing.Manager() + with warnings_helper.ignore_fork_in_thread_deprecation_warnings(): + super().setUpClass() + cls.manager = multiprocessing.Manager() @classmethod def tearDownClass(cls): @@ -7142,3 +7632,110 @@ class SemLock(_multiprocessing.SemLock): name = f'test_semlock_subclass-{os.getpid()}' s = SemLock(1, 0, 10, name, False) _multiprocessing.sem_unlink(name) + + +@unittest.skipIf(sys.platform != "linux", "Linux only") +class ForkInThreads(unittest.TestCase): + + def test_fork(self): + code = """ + import os, sys, threading, time + + t = threading.Thread(target=time.sleep, args=(1,), daemon=True) + t.start() + + assert threading.active_count() == 2 + + pid = os.fork() + if pid < 0: + print("Fork failed") + elif pid == 0: + print("In child") + sys.exit(0) + print("In parent") + """ + + res = assert_python_ok("-c", code, PYTHONWARNINGS='always') + self.assertIn(b'In child', res.out) + self.assertIn(b'In parent', res.out) + self.assertIn(b'DeprecationWarning', res.err) + self.assertIn(b'is multi-threaded, use of fork() may lead to deadlocks in the child', res.err) + + res = assert_python_failure("-c", code, PYTHONWARNINGS='error') + self.assertIn(b'DeprecationWarning', res.err) + self.assertIn(b'is multi-threaded, use of fork() may lead to deadlocks in the child', res.err) + + def test_forkpty(self): + code = """ + import os, sys, threading, time + + t = threading.Thread(target=time.sleep, args=(1,), daemon=True) + t.start() + + assert threading.active_count() == 2 + + pid, _ = os.forkpty() + if pid < 0: + print(f"forkpty failed") + elif pid == 0: + print(f"In child") + sys.exit(0) + print(f"In parent") + """ + + res = assert_python_ok("-c", code, PYTHONWARNINGS='always') + self.assertIn(b'In parent', res.out) + self.assertIn(b'DeprecationWarning', res.err) + self.assertIn(b'is multi-threaded, use of forkpty() may lead to deadlocks in the child', res.err) + + res = assert_python_failure("-c", code, PYTHONWARNINGS='error') + self.assertIn(b'DeprecationWarning', res.err) + self.assertIn(b'is multi-threaded, use of forkpty() may lead to deadlocks in the child', res.err) + +@unittest.skipUnless(HAS_SHMEM, "requires multiprocessing.shared_memory") +class TestSharedMemoryNames(unittest.TestCase): + @subTests('use_simple_format', (True, False)) + def test_that_shared_memory_name_with_colons_has_no_resource_tracker_errors( + self, use_simple_format): + # Test script that creates and cleans up shared memory with colon in name + test_script = textwrap.dedent(""" + import sys + from multiprocessing import shared_memory + from multiprocessing import resource_tracker + import time + + resource_tracker._resource_tracker._use_simple_format = %s + + # Test various patterns of colons in names + test_names = [ + "a:b", + "a:b:c", + "test:name:with:many:colons", + ":starts:with:colon", + "ends:with:colon:", + "::double::colons::", + "name\\nwithnewline", + "name-with-trailing-newline\\n", + "\\nname-starts-with-newline", + "colons:and\\nnewlines:mix", + "multi\\nline\\nname", + ] + + for name in test_names: + try: + shm = shared_memory.SharedMemory(create=True, size=100, name=name) + shm.buf[:5] = b'hello' # Write something to the shared memory + shm.close() + shm.unlink() + + except Exception as e: + print(f"Error with name '{name}': {e}", file=sys.stderr) + sys.exit(1) + + print("SUCCESS") + """ % use_simple_format) + + rc, out, err = assert_python_ok("-c", test_script) + self.assertIn(b"SUCCESS", out) + self.assertNotIn(b"traceback", err.lower(), err) + self.assertNotIn(b"resource_tracker.py", err, err) diff --git a/Lib/test/audiodata/pluck-float32.wav b/Lib/test/audiodata/pluck-float32.wav new file mode 100644 index 00000000000000..2030fb16d6e3bd Binary files /dev/null and b/Lib/test/audiodata/pluck-float32.wav differ diff --git a/Lib/test/audiotests.py b/Lib/test/audiotests.py index 9d6c4cc2b4b02c..394097df17dca9 100644 --- a/Lib/test/audiotests.py +++ b/Lib/test/audiotests.py @@ -27,17 +27,18 @@ def tearDown(self): unlink(TESTFN) def check_params(self, f, nchannels, sampwidth, framerate, nframes, - comptype, compname): + comptype, compname, format): self.assertEqual(f.getnchannels(), nchannels) self.assertEqual(f.getsampwidth(), sampwidth) self.assertEqual(f.getframerate(), framerate) self.assertEqual(f.getnframes(), nframes) self.assertEqual(f.getcomptype(), comptype) self.assertEqual(f.getcompname(), compname) + self.assertEqual(f.getformat(), format) params = f.getparams() self.assertEqual(params, - (nchannels, sampwidth, framerate, nframes, comptype, compname)) + (nchannels, sampwidth, framerate, nframes, comptype, compname)) self.assertEqual(params.nchannels, nchannels) self.assertEqual(params.sampwidth, sampwidth) self.assertEqual(params.framerate, framerate) @@ -51,13 +52,17 @@ def check_params(self, f, nchannels, sampwidth, framerate, nframes, class AudioWriteTests(AudioTests): + readonly = False def create_file(self, testfile): + if self.readonly: + self.skipTest('Read only file format') f = self.fout = self.module.open(testfile, 'wb') f.setnchannels(self.nchannels) f.setsampwidth(self.sampwidth) f.setframerate(self.framerate) f.setcomptype(self.comptype, self.compname) + f.setformat(self.format) return f def check_file(self, testfile, nframes, frames): @@ -67,13 +72,14 @@ def check_file(self, testfile, nframes, frames): self.assertEqual(f.getframerate(), self.framerate) self.assertEqual(f.getnframes(), nframes) self.assertEqual(f.readframes(nframes), frames) + self.assertEqual(f.getformat(), self.format) def test_write_params(self): f = self.create_file(TESTFN) f.setnframes(self.nframes) f.writeframes(self.frames) self.check_params(f, self.nchannels, self.sampwidth, self.framerate, - self.nframes, self.comptype, self.compname) + self.nframes, self.comptype, self.compname, self.format) f.close() def test_write_context_manager_calls_close(self): @@ -257,7 +263,7 @@ def test_read_params(self): f = self.f = self.module.open(self.sndfilepath) #self.assertEqual(f.getfp().name, self.sndfilepath) self.check_params(f, self.nchannels, self.sampwidth, self.framerate, - self.sndfilenframes, self.comptype, self.compname) + self.sndfilenframes, self.comptype, self.compname, self.format) def test_close(self): with open(self.sndfilepath, 'rb') as testfile: @@ -298,6 +304,8 @@ def test_read(self): f.setpos(f.getnframes() + 1) def test_copy(self): + if self.readonly: + self.skipTest('Read only file format') f = self.f = self.module.open(self.sndfilepath) fout = self.fout = self.module.open(TESTFN, 'wb') fout.setparams(f.getparams()) diff --git a/Lib/test/audit-tests.py b/Lib/test/audit-tests.py index 6884ac0dbe6ff0..8be5bf8aa4f546 100644 --- a/Lib/test/audit-tests.py +++ b/Lib/test/audit-tests.py @@ -8,6 +8,8 @@ import contextlib import os import sys +import unittest.mock +from test.support import swap_item class TestHook: @@ -206,6 +208,16 @@ def rl(name): else: return None + try: + import _remote_debugging + except ImportError: + _remote_debugging = None + + def rd(name): + if _remote_debugging: + return getattr(_remote_debugging, name, None) + return None + # Try a range of "open" functions. # All of them should fail with TestHook(raise_on_events={"open"}) as hook: @@ -223,6 +235,8 @@ def rl(name): (rl("append_history_file"), 0, None), (rl("read_init_file"), testfn), (rl("read_init_file"), None), + (rd("BinaryWriter"), testfn, 1000, 0), + (rd("BinaryReader"), testfn), ]: if not fn: continue @@ -256,6 +270,8 @@ def rl(name): ("~/.history", "a") if rl("append_history_file") else None, (testfn, "r") if readline else None, ("", "r") if readline else None, + (testfn, "wb") if rd("BinaryWriter") else None, + (testfn, "rb") if rd("BinaryReader") else None, ] if i is not None ], @@ -672,6 +688,84 @@ def hook(event, args): assertEqual(event_script_path, tmp_file.name) assertEqual(remote_event_script_path, tmp_file.name) +def test_import_module(): + import importlib + + with TestHook() as hook: + importlib.import_module("importlib") # already imported, won't get logged + importlib.import_module("email") # standard library module + importlib.import_module("pythoninfo") # random module + importlib.import_module(".audit_test_data.submodule", "test") # relative import + importlib.import_module("test.audit_test_data.submodule2") # absolute import + importlib.import_module("_testcapi") # extension module + + actual = [a for e, a in hook.seen if e == "import"] + assertSequenceEqual( + [ + ("email", None, sys.path, sys.meta_path, sys.path_hooks), + ("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks), + ("test.audit_test_data.submodule", None, sys.path, sys.meta_path, sys.path_hooks), + ("test.audit_test_data", None, sys.path, sys.meta_path, sys.path_hooks), + ("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, sys.path_hooks), + ("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks), + ("_testcapi", unittest.mock.ANY, None, None, None) + ], + actual, + ) + +def test_builtin__import__(): + import importlib # noqa: F401 + + with TestHook() as hook: + __import__("importlib") + __import__("email") + __import__("pythoninfo") + __import__("audit_test_data.submodule", level=1, globals={"__package__": "test"}) + __import__("test.audit_test_data.submodule2") + __import__("_testcapi") + + actual = [a for e, a in hook.seen if e == "import"] + assertSequenceEqual( + [ + ("email", None, sys.path, sys.meta_path, sys.path_hooks), + ("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks), + ("test.audit_test_data.submodule", None, sys.path, sys.meta_path, sys.path_hooks), + ("test.audit_test_data", None, sys.path, sys.meta_path, sys.path_hooks), + ("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, sys.path_hooks), + ("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks), + ("_testcapi", unittest.mock.ANY, None, None, None) + ], + actual, + ) + +def test_import_statement(): + import importlib # noqa: F401 + # Set __package__ so relative imports work + with swap_item(globals(), "__package__", "test"): + with TestHook() as hook: + import importlib # noqa: F401 + import email # noqa: F401 + import pythoninfo # noqa: F401 + from .audit_test_data import submodule # noqa: F401 + import test.audit_test_data.submodule2 # noqa: F401 + import _testcapi # noqa: F401 + + actual = [a for e, a in hook.seen if e == "import"] + # Import statement ordering is different because the package is + # loaded first and then the submodule + assertSequenceEqual( + [ + ("email", None, sys.path, sys.meta_path, sys.path_hooks), + ("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks), + ("test.audit_test_data", None, sys.path, sys.meta_path, sys.path_hooks), + ("test.audit_test_data.submodule", None, sys.path, sys.meta_path, sys.path_hooks), + ("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, sys.path_hooks), + ("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks), + ("_testcapi", unittest.mock.ANY, None, None, None) + ], + actual, + ) + if __name__ == "__main__": from test.support import suppress_msvcrt_asserts diff --git a/Lib/test/audit_test_data/__init__.py b/Lib/test/audit_test_data/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/Lib/test/audit_test_data/submodule.py b/Lib/test/audit_test_data/submodule.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/Lib/test/audit_test_data/submodule2.py b/Lib/test/audit_test_data/submodule2.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index dc5b4b27a07f99..171570588e7a2b 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -530,19 +530,19 @@ test_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; char a = 'A'; - char b = '\x07'; - char c = '\x08'; + char b = '\a'; + char c = '\b'; char d = '\t'; char e = '\n'; - char f = '\x0b'; - char g = '\x0c'; + char f = '\v'; + char g = '\f'; char h = '\r'; char i = '"'; char j = '\''; char k = '?'; char l = '\\'; - char m = '\x00'; - char n = '\xff'; + char m = '\0'; + char n = '\377'; if (!_PyArg_CheckPositional("test_char_converter", nargs, 0, 14)) { goto exit; @@ -936,7 +936,7 @@ static PyObject * test_char_converter_impl(PyObject *module, char a, char b, char c, char d, char e, char f, char g, char h, char i, char j, char k, char l, char m, char n) -/*[clinic end generated code: output=ff11e203248582df input=e42330417a44feac]*/ +/*[clinic end generated code: output=6503d15448e1d4c4 input=e42330417a44feac]*/ /*[clinic input] @@ -1192,14 +1192,14 @@ test_int_converter a: int = 12 b: int(accept={int}) = 34 - c: int(accept={str}) = 45 + c: int(accept={str}) = '-' d: int(type='myenum') = 67 / [clinic start generated code]*/ PyDoc_STRVAR(test_int_converter__doc__, -"test_int_converter($module, a=12, b=34, c=45, d=67, /)\n" +"test_int_converter($module, a=12, b=34, c=\'-\', d=67, /)\n" "--\n" "\n"); @@ -1215,7 +1215,7 @@ test_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; int a = 12; int b = 34; - int c = 45; + int c = '-'; myenum d = 67; if (!_PyArg_CheckPositional("test_int_converter", nargs, 0, 4)) { @@ -1266,7 +1266,7 @@ test_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) static PyObject * test_int_converter_impl(PyObject *module, int a, int b, int c, myenum d) -/*[clinic end generated code: output=fbcfb7554688663d input=d20541fc1ca0553e]*/ +/*[clinic end generated code: output=d5357b563bdb8789 input=5d8f4eb5899b24de]*/ /*[clinic input] @@ -1612,12 +1612,17 @@ test_Py_ssize_t_converter a: Py_ssize_t = 12 b: Py_ssize_t(accept={int}) = 34 c: Py_ssize_t(accept={int, NoneType}) = 56 + d: Py_ssize_t(accept={int}, allow_negative=False) = 78 + e: Py_ssize_t(accept={int, NoneType}, allow_negative=False) = 90 + f: Py_ssize_t(accept={int}, allow_negative=True) = -12 + g: Py_ssize_t(accept={int, NoneType}, allow_negative=True) = -34 / [clinic start generated code]*/ PyDoc_STRVAR(test_Py_ssize_t_converter__doc__, -"test_Py_ssize_t_converter($module, a=12, b=34, c=56, /)\n" +"test_Py_ssize_t_converter($module, a=12, b=34, c=56, d=78, e=90, f=-12,\n" +" g=-34, /)\n" "--\n" "\n"); @@ -1626,7 +1631,8 @@ PyDoc_STRVAR(test_Py_ssize_t_converter__doc__, static PyObject * test_Py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b, - Py_ssize_t c); + Py_ssize_t c, Py_ssize_t d, Py_ssize_t e, + Py_ssize_t f, Py_ssize_t g); static PyObject * test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) @@ -1635,8 +1641,12 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na Py_ssize_t a = 12; Py_ssize_t b = 34; Py_ssize_t c = 56; + Py_ssize_t d = 78; + Py_ssize_t e = 90; + Py_ssize_t f = -12; + Py_ssize_t g = -34; - if (!_PyArg_CheckPositional("test_Py_ssize_t_converter", nargs, 0, 3)) { + if (!_PyArg_CheckPositional("test_Py_ssize_t_converter", nargs, 0, 7)) { goto exit; } if (nargs < 1) { @@ -1675,8 +1685,55 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na if (!_Py_convert_optional_to_ssize_t(args[2], &c)) { goto exit; } + if (nargs < 4) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[3]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + d = ival; + if (d < 0) { + PyErr_SetString(PyExc_ValueError, + "d cannot be negative"); + goto exit; + } + } + if (nargs < 5) { + goto skip_optional; + } + if (!_Py_convert_optional_to_non_negative_ssize_t(args[4], &e)) { + goto exit; + } + if (nargs < 6) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[5]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + f = ival; + } + if (nargs < 7) { + goto skip_optional; + } + if (!_Py_convert_optional_to_ssize_t(args[6], &g)) { + goto exit; + } skip_optional: - return_value = test_Py_ssize_t_converter_impl(module, a, b, c); + return_value = test_Py_ssize_t_converter_impl(module, a, b, c, d, e, f, g); exit: return return_value; @@ -1684,8 +1741,9 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na static PyObject * test_Py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b, - Py_ssize_t c) -/*[clinic end generated code: output=48214bc3d01f4dd7 input=3855f184bb3f299d]*/ + Py_ssize_t c, Py_ssize_t d, Py_ssize_t e, + Py_ssize_t f, Py_ssize_t g) +/*[clinic end generated code: output=4ae0a56a1447fba9 input=a25bac8ecf2890aa]*/ /*[clinic input] @@ -4080,13 +4138,14 @@ test_preprocessor_guarded_if_with_continuation_impl(PyObject *module) #if CONDITION_E || CONDITION_F #warning "different type of CPP directive" /*[clinic input] +@permit_long_summary test_preprocessor_guarded_if_e_or_f Makes sure cpp.Monitor handles other directives than preprocessor conditionals. [clinic start generated code]*/ static PyObject * test_preprocessor_guarded_if_e_or_f_impl(PyObject *module) -/*[clinic end generated code: output=e49d24ff64ad88bc input=57b9c37f938bc4f1]*/ +/*[clinic end generated code: output=e49d24ff64ad88bc input=3ca9ab4e883300ed]*/ #endif /*[clinic input] @@ -4282,7 +4341,7 @@ test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t narg goto exit; } a = args[0]; - __clinic_args = _PyTuple_FromArray(args + 1, nargs - 1); + __clinic_args = PyTuple_FromArray(args + 1, nargs - 1); if (__clinic_args == NULL) { goto exit; } @@ -4297,7 +4356,7 @@ test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t narg static PyObject * test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=0c11c475e240869e input=2c49a482f68545c0]*/ +/*[clinic end generated code: output=83cbe9554d04add2 input=2c49a482f68545c0]*/ /*[clinic input] test_vararg @@ -4362,7 +4421,7 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject } a = fastargs[0]; __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -4378,7 +4437,7 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject static PyObject * test_vararg_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=17ba625cdd0369c1 input=7448995636d9186a]*/ +/*[clinic end generated code: output=d773f7b54e61f73a input=7448995636d9186a]*/ /*[clinic input] test_vararg_with_default @@ -4455,7 +4514,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar } skip_optional_kwonly: __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -4472,7 +4531,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, int b) -/*[clinic end generated code: output=3f2b06ab08d5d0be input=3a0f9f557ce1f712]*/ +/*[clinic end generated code: output=d25e56802c197344 input=3a0f9f557ce1f712]*/ /*[clinic input] test_vararg_with_only_defaults @@ -4553,7 +4612,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize } c = fastargs[1]; skip_optional_kwonly: - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -4569,7 +4628,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, PyObject *c) -/*[clinic end generated code: output=f46666f0b1bf86b9 input=6983e66817f82924]*/ +/*[clinic end generated code: output=7366943a7df42e05 input=6983e66817f82924]*/ /*[clinic input] test_paramname_module @@ -5084,14 +5143,18 @@ Test_an_metho_arg_named_arg_impl(TestObj *self, int arg) Test.__init__ *args: tuple -Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE. +Varargs init method. + +For example, nargs is translated to PyTuple_GET_SIZE. [clinic start generated code]*/ PyDoc_STRVAR(Test___init____doc__, "Test(*args)\n" "--\n" "\n" -"Varargs init method. For example, nargs is translated to PyTuple_GET_SIZE."); +"Varargs init method.\n" +"\n" +"For example, nargs is translated to PyTuple_GET_SIZE."); static int Test___init___impl(TestObj *self, PyObject *args); @@ -5120,21 +5183,25 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) static int Test___init___impl(TestObj *self, PyObject *args) -/*[clinic end generated code: output=f172425cec373cd6 input=4b8388c4e6baab6f]*/ +/*[clinic end generated code: output=0e5836c40dbc2397 input=a615a4485c0fc3e2]*/ /*[clinic input] @classmethod Test.__new__ *args: tuple -Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE. +Varargs new method. + +For example, nargs is translated to PyTuple_GET_SIZE. [clinic start generated code]*/ PyDoc_STRVAR(Test__doc__, "Test(*args)\n" "--\n" "\n" -"Varargs new method. For example, nargs is translated to PyTuple_GET_SIZE."); +"Varargs new method.\n" +"\n" +"For example, nargs is translated to PyTuple_GET_SIZE."); static PyObject * Test_impl(PyTypeObject *type, PyObject *args); @@ -5162,7 +5229,7 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs) static PyObject * Test_impl(PyTypeObject *type, PyObject *args) -/*[clinic end generated code: output=ee1e8892a67abd4a input=a8259521129cad20]*/ +/*[clinic end generated code: output=e6fba0c8951882fd input=8ce30adb836aeacb]*/ /*[clinic input] diff --git a/Lib/test/cov.py b/Lib/test/cov.py index e4699c7afe174a..8717b1f20dd979 100644 --- a/Lib/test/cov.py +++ b/Lib/test/cov.py @@ -1,8 +1,7 @@ """A minimal hook for gathering line coverage of the standard library. -Designed to be used with -Xpresite= which means: -* it installs itself on import -* it's not imported as `__main__` so can't use the ifmain idiom +Designed to be used with -Xpresite=test.cov:enable which means: + * it can't import anything besides `sys` to avoid tainting gathered coverage * filenames are not normalized @@ -45,4 +44,5 @@ def disable(): mon.free_tool_id(mon.COVERAGE_ID) -enable() +if __name__ == "__main__": + enable() diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 3bd3a866570042..d26e41982deb81 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3,6 +3,7 @@ import contextlib import copy import decimal +import fractions import io import itertools import os @@ -21,7 +22,7 @@ from test import support from test.support import is_resource_enabled, ALWAYS_EQ, LARGEST, SMALLEST -from test.support import os_helper, script_helper, warnings_helper +from test.support import os_helper, script_helper import datetime as datetime_module from datetime import MINYEAR, MAXYEAR @@ -47,7 +48,11 @@ try: import _pydatetime except ImportError: - pass + _pydatetime = None +try: + import _datetime +except ImportError: + _datetime = None # pickle_loads = {pickle.loads, pickle._loads} @@ -1201,15 +1206,20 @@ def test_strptime_single_digit(self): newdate = strptime(string, format) self.assertEqual(newdate, target, msg=reason) - @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_strptime_leap_year(self): - # GH-70647: warns if parsing a format with a day and no year. + # GH-70647: %d errors if parsing a format with a day and no year. with self.assertRaises(ValueError): # The existing behavior that GH-70647 seeks to change. date.strptime('02-29', '%m-%d') + # %e without a year is deprecated, scheduled for removal in 3.17. + _strptime._regex_cache.clear() + with self.assertWarnsRegex(DeprecationWarning, + r'.*day of month without a year.*'): + date.strptime('02-01', '%m-%e') with self._assertNotWarns(DeprecationWarning): date.strptime('20-03-14', '%y-%m-%d') date.strptime('02-29,2024', '%m-%d,%Y') + date.strptime('02-29,2024', '%m-%e,%Y') class SubclassDate(date): sub_var = 1 @@ -1807,7 +1817,7 @@ def test_bool(self): self.assertTrue(self.theclass.min) self.assertTrue(self.theclass.max) - def test_strftime_y2k(self): + def check_strftime_y2k(self, specifier): # Test that years less than 1000 are 0-padded; note that the beginning # of an ISO 8601 year may fall in an ISO week of the year before, and # therefore needs an offset of -1 when formatting with '%G'. @@ -1821,22 +1831,28 @@ def test_strftime_y2k(self): (1000, 0), (1970, 0), ) - specifiers = 'YG' - if _time.strftime('%F', (1900, 1, 1, 0, 0, 0, 0, 1, 0)) == '1900-01-01': - specifiers += 'FC' for year, g_offset in dataset: - for specifier in specifiers: - with self.subTest(year=year, specifier=specifier): - d = self.theclass(year, 1, 1) - if specifier == 'G': - year += g_offset - if specifier == 'C': - expected = f"{year // 100:02d}" - else: - expected = f"{year:04d}" - if specifier == 'F': - expected += f"-01-01" - self.assertEqual(d.strftime(f"%{specifier}"), expected) + with self.subTest(year=year, specifier=specifier): + d = self.theclass(year, 1, 1) + if specifier == 'G': + year += g_offset + if specifier == 'C': + expected = f"{year // 100:02d}" + else: + expected = f"{year:04d}" + if specifier == 'F': + expected += f"-01-01" + self.assertEqual(d.strftime(f"%{specifier}"), expected) + + def test_strftime_y2k(self): + self.check_strftime_y2k('Y') + self.check_strftime_y2k('G') + + def test_strftime_y2k_c99(self): + # CPython requires C11; specifiers new in C99 must work. + # (Other implementations may want to disable this test.) + self.check_strftime_y2k('F') + self.check_strftime_y2k('C') def test_replace(self): cls = self.theclass @@ -2147,14 +2163,20 @@ def test_fromisocalendar_value_errors(self): (10000, 1, 1), (0, 1, 1), (9999999, 1, 1), + ] + for isocal in isocals: + with self.subTest(isocal=isocal): + with self.assertRaises(ValueError): + self.theclass.fromisocalendar(*isocal) + + isocals = [ (2<<32, 1, 1), (2019, 2<<32, 1), (2019, 1, 2<<32), ] - for isocal in isocals: with self.subTest(isocal=isocal): - with self.assertRaises(ValueError): + with self.assertRaises((ValueError, OverflowError)): self.theclass.fromisocalendar(*isocal) def test_fromisocalendar_type_errors(self): @@ -2180,6 +2202,34 @@ def test_fromisocalendar_type_errors(self): with self.assertRaises(TypeError): self.theclass.fromisocalendar(*isocal) + def test_strptime_F_format(self): + test_date = "2025-10-26" + self.assertEqual( + self.theclass.strptime(test_date, "%F"), + self.theclass.strptime(test_date, "%Y-%m-%d") + ) + + def test_strptime_D_format(self): + test_date = "11/28/25" + self.assertEqual( + self.theclass.strptime(test_date, "%D"), + self.theclass.strptime(test_date, "%m/%d/%y") + ) + + def test_strptime_n_and_t_format(self): + format_directives = ('%n', '%t', '%n%t', '%t%n') + whitespaces = ('', ' ', '\t', '\r', '\v', '\n', '\f') + for fd in format_directives: + for ws in (*whitespaces, ''.join(whitespaces)): + with self.subTest(format_directive=fd, whitespace=ws): + self.assertEqual( + self.theclass.strptime( + f"2026{ws}02{ws}03", + f"%Y{fd}%m{fd}%d", + ), + self.theclass(2026, 2, 3), + ) + ############################################################################# # datetime tests @@ -2301,7 +2351,7 @@ def test_isoformat_timezone(self): dt = dt_base.replace(tzinfo=tzi) exp = exp_base + exp_tz with self.subTest(tzi=tzi): - assert dt.isoformat() == exp + self.assertEqual(dt.isoformat(), exp) def test_format(self): dt = self.theclass(2007, 9, 10, 4, 5, 1, 123) @@ -2614,6 +2664,10 @@ def test_fromtimestamp(self): expected = time.localtime(ts) got = self.theclass.fromtimestamp(ts) self.verify_field_equality(expected, got) + got = self.theclass.fromtimestamp(decimal.Decimal(ts)) + self.verify_field_equality(expected, got) + got = self.theclass.fromtimestamp(fractions.Fraction(ts)) + self.verify_field_equality(expected, got) def test_fromtimestamp_keyword_arg(self): import time @@ -2629,6 +2683,12 @@ def test_utcfromtimestamp(self): with self.assertWarns(DeprecationWarning): got = self.theclass.utcfromtimestamp(ts) self.verify_field_equality(expected, got) + with self.assertWarns(DeprecationWarning): + got = self.theclass.utcfromtimestamp(decimal.Decimal(ts)) + self.verify_field_equality(expected, got) + with self.assertWarns(DeprecationWarning): + got = self.theclass.utcfromtimestamp(fractions.Fraction(ts)) + self.verify_field_equality(expected, got) # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0). @@ -2683,24 +2743,20 @@ def utcfromtimestamp(*args, **kwargs): self.assertEqual(zero.second, 0) self.assertEqual(zero.microsecond, 0) one = fts(1e-6) - try: - minus_one = fts(-1e-6) - except OSError: - # localtime(-1) and gmtime(-1) is not supported on Windows - pass - else: - self.assertEqual(minus_one.second, 59) - self.assertEqual(minus_one.microsecond, 999999) - - t = fts(-1e-8) - self.assertEqual(t, zero) - t = fts(-9e-7) - self.assertEqual(t, minus_one) - t = fts(-1e-7) - self.assertEqual(t, zero) - t = fts(-1/2**7) - self.assertEqual(t.second, 59) - self.assertEqual(t.microsecond, 992188) + minus_one = fts(-1e-6) + + self.assertEqual(minus_one.second, 59) + self.assertEqual(minus_one.microsecond, 999999) + + t = fts(-1e-8) + self.assertEqual(t, zero) + t = fts(-9e-7) + self.assertEqual(t, minus_one) + t = fts(-1e-7) + self.assertEqual(t, zero) + t = fts(-1/2**7) + self.assertEqual(t.second, 59) + self.assertEqual(t.microsecond, 992188) t = fts(1e-7) self.assertEqual(t, zero) @@ -2716,6 +2772,100 @@ def utcfromtimestamp(*args, **kwargs): self.assertEqual(t.second, 0) self.assertEqual(t.microsecond, 7812) + @support.run_with_tz('MSK-03') # Something east of Greenwich + def test_microsecond_rounding_decimal(self): + D = decimal.Decimal + def utcfromtimestamp(*args, **kwargs): + with self.assertWarns(DeprecationWarning): + return self.theclass.utcfromtimestamp(*args, **kwargs) + + for fts in [self.theclass.fromtimestamp, + utcfromtimestamp]: + zero = fts(D(0)) + self.assertEqual(zero.second, 0) + self.assertEqual(zero.microsecond, 0) + one = fts(D('0.000_001')) + minus_one = fts(D('-0.000_001')) + + self.assertEqual(minus_one.second, 59) + self.assertEqual(minus_one.microsecond, 999_999) + + t = fts(D('-0.000_000_1')) + self.assertEqual(t, zero) + t = fts(D('-0.000_000_9')) + self.assertEqual(t, minus_one) + t = fts(D(-1)/2**7) + self.assertEqual(t.second, 59) + self.assertEqual(t.microsecond, 992188) + + t = fts(D('0.000_000_1')) + self.assertEqual(t, zero) + t = fts(D('0.000_000_5')) + self.assertEqual(t, zero) + t = fts(D('0.000_000_500_000_000_000_000_1')) + self.assertEqual(t, one) + t = fts(D('0.000_000_9')) + self.assertEqual(t, one) + t = fts(D('0.999_999_499_999_999_9')) + self.assertEqual(t.second, 0) + self.assertEqual(t.microsecond, 999_999) + t = fts(D('0.999_999_5')) + self.assertEqual(t.second, 1) + self.assertEqual(t.microsecond, 0) + t = fts(D('0.999_999_9')) + self.assertEqual(t.second, 1) + self.assertEqual(t.microsecond, 0) + t = fts(D(1)/2**7) + self.assertEqual(t.second, 0) + self.assertEqual(t.microsecond, 7812) + + @support.run_with_tz('MSK-03') # Something east of Greenwich + def test_microsecond_rounding_fraction(self): + F = fractions.Fraction + def utcfromtimestamp(*args, **kwargs): + with self.assertWarns(DeprecationWarning): + return self.theclass.utcfromtimestamp(*args, **kwargs) + + for fts in [self.theclass.fromtimestamp, + utcfromtimestamp]: + zero = fts(F(0)) + self.assertEqual(zero.second, 0) + self.assertEqual(zero.microsecond, 0) + one = fts(F(1, 1_000_000)) + minus_one = fts(F(-1, 1_000_000)) + + self.assertEqual(minus_one.second, 59) + self.assertEqual(minus_one.microsecond, 999_999) + + t = fts(F(-1, 10_000_000)) + self.assertEqual(t, zero) + t = fts(F(-9, 10_000_000)) + self.assertEqual(t, minus_one) + t = fts(F(-1, 2**7)) + self.assertEqual(t.second, 59) + self.assertEqual(t.microsecond, 992188) + + t = fts(F(1, 10_000_000)) + self.assertEqual(t, zero) + t = fts(F(5, 10_000_000)) + self.assertEqual(t, zero) + t = fts(F(5_000_000_000, 9_999_999_999_999_999)) + self.assertEqual(t, one) + t = fts(F(9, 10_000_000)) + self.assertEqual(t, one) + t = fts(F(9_999_995_000_000_000, 10_000_000_000_000_001)) + self.assertEqual(t.second, 0) + self.assertEqual(t.microsecond, 999_999) + t = fts(F(9_999_995, 10_000_000)) + self.assertEqual(t.second, 1) + self.assertEqual(t.microsecond, 0) + t = fts(F(9_999_999, 10_000_000)) + self.assertEqual(t.second, 1) + self.assertEqual(t.microsecond, 0) + t = fts(F(1, 2**7)) + self.assertEqual(t.second, 0) + self.assertEqual(t.microsecond, 7812) + def test_timestamp_limits(self): with self.subTest("minimum UTC"): min_dt = self.theclass.min.replace(tzinfo=timezone.utc) @@ -2735,6 +2885,7 @@ def test_timestamp_limits(self): # If that assumption changes, this value can change as well self.assertEqual(max_ts, 253402300799.0) + @unittest.skipIf(sys.platform == "win32", "Windows doesn't support min timestamp") def test_fromtimestamp_limits(self): try: self.theclass.fromtimestamp(-2**32 - 1) @@ -2774,6 +2925,7 @@ def test_fromtimestamp_limits(self): # OverflowError, especially on 32-bit platforms. self.theclass.fromtimestamp(ts) + @unittest.skipIf(sys.platform == "win32", "Windows doesn't support min timestamp") def test_utcfromtimestamp_limits(self): with self.assertWarns(DeprecationWarning): try: @@ -2835,13 +2987,11 @@ def test_insane_utcfromtimestamp(self): self.assertRaises(OverflowError, self.theclass.utcfromtimestamp, insane) - @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps") def test_negative_float_fromtimestamp(self): # The result is tz-dependent; at least test that this doesn't # fail (like it did before bug 1646728 was fixed). self.theclass.fromtimestamp(-1.05) - @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps") def test_negative_float_utcfromtimestamp(self): with self.assertWarns(DeprecationWarning): d = self.theclass.utcfromtimestamp(-1.05) @@ -2895,6 +3045,12 @@ def test_strptime(self): strptime("-00:02:01.000003", "%z").utcoffset(), -timedelta(minutes=2, seconds=1, microseconds=3) ) + self.assertEqual(strptime("+01:07", "%:z").utcoffset(), + 1 * HOUR + 7 * MINUTE) + self.assertEqual(strptime("-10:02", "%:z").utcoffset(), + -(10 * HOUR + 2 * MINUTE)) + self.assertEqual(strptime("-00:00:01.00001", "%:z").utcoffset(), + -timedelta(seconds=1, microseconds=10)) # Only local timezone and UTC are supported for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'), (-_time.timezone, _time.tzname[0])): @@ -2924,6 +3080,16 @@ def test_strptime(self): with self.assertRaises(ValueError): strptime("-000", "%z") with self.assertRaises(ValueError): strptime("z", "%z") + def test_strptime_ampm(self): + dt = datetime(1999, 3, 17, 0, 44, 55, 2) + for hour in range(0, 24): + with self.subTest(hour=hour): + new_dt = dt.replace(hour=hour) + dt_str = new_dt.strftime("%I %p") + + self.assertEqual(self.theclass.strptime(dt_str, "%I %p").hour, + hour) + def test_strptime_single_digit(self): # bpo-34903: Check that single digit dates and times are allowed. @@ -2958,22 +3124,27 @@ def test_strptime_single_digit(self): newdate = strptime(string, format) self.assertEqual(newdate, target, msg=reason) - @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_strptime_leap_year(self): - # GH-70647: warns if parsing a format with a day and no year. + # GH-70647: %d errors if parsing a format with a day and no year. with self.assertRaises(ValueError): # The existing behavior that GH-70647 seeks to change. self.theclass.strptime('02-29', '%m-%d') + with self.assertRaises(ValueError): + self.theclass.strptime('03-14.159265', '%m-%d.%f') + # %e without a year is deprecated, scheduled for removal in 3.17. + _strptime._regex_cache.clear() with self.assertWarnsRegex(DeprecationWarning, r'.*day of month without a year.*'): - self.theclass.strptime('03-14.159265', '%m-%d.%f') + self.theclass.strptime('03-14.159265', '%m-%e.%f') with self._assertNotWarns(DeprecationWarning): self.theclass.strptime('20-03-14.159265', '%y-%m-%d.%f') with self._assertNotWarns(DeprecationWarning): self.theclass.strptime('02-29,2024', '%m-%d,%Y') + with self._assertNotWarns(DeprecationWarning): + self.theclass.strptime('02-29,2024', '%m-%e,%Y') def test_strptime_z_empty(self): - for directive in ('z',): + for directive in ('z', ':z'): string = '2025-04-25 11:42:47' format = f'%Y-%m-%d %H:%M:%S%{directive}' target = self.theclass(2025, 4, 25, 11, 42, 47) @@ -3349,7 +3520,7 @@ def test_fromisoformat_timezone(self): with self.subTest(tstr=dtstr): dt_rt = self.theclass.fromisoformat(dtstr) - assert dt == dt_rt, dt_rt + self.assertEqual(dt_rt, dt) def test_fromisoformat_separators(self): separators = [ @@ -3651,6 +3822,13 @@ def test_repr_subclass(self): td = SubclassDatetime(2010, 10, 2, second=3) self.assertEqual(repr(td), "SubclassDatetime(2010, 10, 2, 0, 0, 3)") + def test_strptime_T_format(self): + test_time = "15:00:00" + self.assertEqual( + self.theclass.strptime(test_time, "%T"), + self.theclass.strptime(test_time, "%H:%M:%S") + ) + class TestSubclassDateTime(TestDateTime): theclass = SubclassDatetime @@ -3865,7 +4043,7 @@ def test_isoformat_timezone(self): t = t_base.replace(tzinfo=tzi) exp = exp_base + exp_tz with self.subTest(tzi=tzi): - assert t.isoformat() == exp + self.assertEqual(t.isoformat(), exp) def test_1653736(self): # verify it doesn't accept extra keyword arguments @@ -4041,6 +4219,12 @@ def test_strptime_tz(self): strptime("-00:02:01.000003", "%z").utcoffset(), -timedelta(minutes=2, seconds=1, microseconds=3) ) + self.assertEqual(strptime("+01:07", "%:z").utcoffset(), + 1 * HOUR + 7 * MINUTE) + self.assertEqual(strptime("-10:02", "%:z").utcoffset(), + -(10 * HOUR + 2 * MINUTE)) + self.assertEqual(strptime("-00:00:01.00001", "%:z").utcoffset(), + -timedelta(seconds=1, microseconds=10)) # Only local timezone and UTC are supported for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'), (-_time.timezone, _time.tzname[0])): @@ -4070,9 +4254,11 @@ def test_strptime_tz(self): self.assertEqual(strptime("UTC", "%Z").tzinfo, None) def test_strptime_errors(self): - for tzstr in ("-2400", "-000", "z"): + for tzstr in ("-2400", "-000", "z", "24:00"): with self.assertRaises(ValueError): self.theclass.strptime(tzstr, "%z") + with self.assertRaises(ValueError): + self.theclass.strptime(tzstr, "%:z") def test_strptime_single_digit(self): # bpo-34903: Check that single digit times are allowed. @@ -4350,7 +4536,7 @@ def utcoffset(self, t): elif x is d2: expected = -1 else: - assert y is d2 + self.assertIs(y, d2) expected = 1 self.assertEqual(got, expected) @@ -4678,7 +4864,7 @@ def test_fromisoformat_timezone(self): with self.subTest(tstr=tstr): t_rt = self.theclass.fromisoformat(tstr) - assert t == t_rt + self.assertEqual(t_rt, t) def test_fromisoformat_timespecs(self): time_bases = [ @@ -5515,7 +5701,7 @@ def utcoffset(self, t): elif x is d2: expected = timedelta(minutes=(11-59)-0) else: - assert y is d2 + self.assertIs(y, d2) expected = timedelta(minutes=0-(11-59)) self.assertEqual(got, expected) @@ -6151,21 +6337,21 @@ def test_vilnius_1941_fromutc(self): gdt = datetime(1941, 6, 23, 20, 59, 59, tzinfo=timezone.utc) ldt = gdt.astimezone(Vilnius) - self.assertEqual(ldt.strftime("%c %Z%z"), + self.assertEqual(ldt.strftime("%a %b %d %H:%M:%S %Y %Z%z"), 'Mon Jun 23 23:59:59 1941 MSK+0300') self.assertEqual(ldt.fold, 0) self.assertFalse(ldt.dst()) gdt = datetime(1941, 6, 23, 21, tzinfo=timezone.utc) ldt = gdt.astimezone(Vilnius) - self.assertEqual(ldt.strftime("%c %Z%z"), + self.assertEqual(ldt.strftime("%a %b %d %H:%M:%S %Y %Z%z"), 'Mon Jun 23 23:00:00 1941 CEST+0200') self.assertEqual(ldt.fold, 1) self.assertTrue(ldt.dst()) gdt = datetime(1941, 6, 23, 22, tzinfo=timezone.utc) ldt = gdt.astimezone(Vilnius) - self.assertEqual(ldt.strftime("%c %Z%z"), + self.assertEqual(ldt.strftime("%a %b %d %H:%M:%S %Y %Z%z"), 'Tue Jun 24 00:00:00 1941 CEST+0200') self.assertEqual(ldt.fold, 0) self.assertTrue(ldt.dst()) @@ -6175,22 +6361,22 @@ def test_vilnius_1941_toutc(self): ldt = datetime(1941, 6, 23, 22, 59, 59, tzinfo=Vilnius) gdt = ldt.astimezone(timezone.utc) - self.assertEqual(gdt.strftime("%c %Z"), + self.assertEqual(gdt.strftime("%a %b %d %H:%M:%S %Y %Z"), 'Mon Jun 23 19:59:59 1941 UTC') ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius) gdt = ldt.astimezone(timezone.utc) - self.assertEqual(gdt.strftime("%c %Z"), + self.assertEqual(gdt.strftime("%a %b %d %H:%M:%S %Y %Z"), 'Mon Jun 23 20:59:59 1941 UTC') ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius, fold=1) gdt = ldt.astimezone(timezone.utc) - self.assertEqual(gdt.strftime("%c %Z"), + self.assertEqual(gdt.strftime("%a %b %d %H:%M:%S %Y %Z"), 'Mon Jun 23 21:59:59 1941 UTC') ldt = datetime(1941, 6, 24, 0, tzinfo=Vilnius) gdt = ldt.astimezone(timezone.utc) - self.assertEqual(gdt.strftime("%c %Z"), + self.assertEqual(gdt.strftime("%a %b %d %H:%M:%S %Y %Z"), 'Mon Jun 23 22:00:00 1941 UTC') def test_constructors(self): @@ -7323,6 +7509,36 @@ def func(): self.assertEqual(out, b"a" * 8) self.assertEqual(err, b"") + @support.cpython_only + @support.subTests(("setup", "call"), [ + ("obj = _datetime.timedelta", "obj(seconds=2)"), + ("obj = _datetime.timedelta(seconds=2)", "obj.total_seconds()"), + ("obj = _datetime.date(2026, 6, 7)", "obj.isocalendar()"), + ]) + def test_static_datetime_types_outlive_collected_module(self, setup, call): + # gh-151039: This code used to crash + script = f"""if True: + import sys, gc + import _datetime + + {setup} # static C type, survives the module + del sys.modules['_datetime'] + del _datetime + sys.modules['_datetime'] = None # block re-import + gc.collect() # module object is collected + + try: + {call} # used to be a segmentation fault + except ImportError: + pass + else: + raise AssertionError("ImportError not raised") + """ + rc, out, err = script_helper.assert_python_ok("-c", script) + self.assertEqual(rc, 0) + self.assertEqual(out, b'') + self.assertEqual(err, b'') + def load_tests(loader, standard_tests, pattern): standard_tests.addTest(ZoneInfoCompleteTest()) diff --git a/Lib/test/dtracedata/call_stack.py b/Lib/test/dtracedata/call_stack.py index ee9f3ae8d6c9f4..11c0369d4baa04 100644 --- a/Lib/test/dtracedata/call_stack.py +++ b/Lib/test/dtracedata/call_stack.py @@ -5,16 +5,16 @@ def function_1(): def function_2(): function_1() -# CALL_FUNCTION_VAR +# CALL with positional args def function_3(dummy, dummy2): pass -# CALL_FUNCTION_KW +# CALL_KW (keyword arguments) def function_4(**dummy): return 1 return 2 # unreachable -# CALL_FUNCTION_VAR_KW +# CALL_FUNCTION_EX (unpacking) def function_5(dummy, dummy2, **dummy3): if False: return 7 diff --git a/Lib/test/dtracedata/call_stack.stp.expected b/Lib/test/dtracedata/call_stack.stp.expected index 32cf396f820629..044eae62018f57 100644 --- a/Lib/test/dtracedata/call_stack.stp.expected +++ b/Lib/test/dtracedata/call_stack.stp.expected @@ -1,8 +1,11 @@ -function__entry:call_stack.py:start:23 function__entry:call_stack.py:function_1:1 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 function__return:call_stack.py:function_1:2 function__entry:call_stack.py:function_2:5 function__entry:call_stack.py:function_1:1 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 function__return:call_stack.py:function_1:2 function__return:call_stack.py:function_2:6 function__entry:call_stack.py:function_3:9 @@ -11,4 +14,3 @@ function__entry:call_stack.py:function_4:13 function__return:call_stack.py:function_4:14 function__entry:call_stack.py:function_5:18 function__return:call_stack.py:function_5:21 -function__return:call_stack.py:start:28 diff --git a/Lib/test/dtracedata/line.d b/Lib/test/dtracedata/line.d deleted file mode 100644 index 03f22db6fcc1a0..00000000000000 --- a/Lib/test/dtracedata/line.d +++ /dev/null @@ -1,7 +0,0 @@ -python$target:::line -/(copyinstr(arg1)=="test_line")/ -{ - printf("%d\t%s:%s:%s:%d\n", timestamp, - probename, basename(copyinstr(arg0)), - copyinstr(arg1), arg2); -} diff --git a/Lib/test/dtracedata/line.d.expected b/Lib/test/dtracedata/line.d.expected deleted file mode 100644 index 9b16ce76ee60a4..00000000000000 --- a/Lib/test/dtracedata/line.d.expected +++ /dev/null @@ -1,20 +0,0 @@ -line:line.py:test_line:2 -line:line.py:test_line:3 -line:line.py:test_line:4 -line:line.py:test_line:5 -line:line.py:test_line:6 -line:line.py:test_line:7 -line:line.py:test_line:8 -line:line.py:test_line:9 -line:line.py:test_line:10 -line:line.py:test_line:11 -line:line.py:test_line:4 -line:line.py:test_line:5 -line:line.py:test_line:6 -line:line.py:test_line:7 -line:line.py:test_line:8 -line:line.py:test_line:10 -line:line.py:test_line:11 -line:line.py:test_line:4 -line:line.py:test_line:12 -line:line.py:test_line:13 diff --git a/Lib/test/dtracedata/line.py b/Lib/test/dtracedata/line.py deleted file mode 100644 index 0930ff391f7a05..00000000000000 --- a/Lib/test/dtracedata/line.py +++ /dev/null @@ -1,17 +0,0 @@ -def test_line(): - a = 1 - print('# Preamble', a) - for i in range(2): - a = i - b = i+2 - c = i+3 - if c < 4: - a = c - d = a + b +c - print('#', a, b, c, d) - a = 1 - print('# Epilogue', a) - - -if __name__ == '__main__': - test_line() diff --git a/Lib/test/exception_hierarchy.txt b/Lib/test/exception_hierarchy.txt index f2649aa2d41fef..98a5e950602eaf 100644 --- a/Lib/test/exception_hierarchy.txt +++ b/Lib/test/exception_hierarchy.txt @@ -14,6 +14,7 @@ BaseException ├── EOFError ├── ExceptionGroup [BaseExceptionGroup] ├── ImportError + │ └── ImportCycleError │ └── ModuleNotFoundError ├── LookupError │ ├── IndexError diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index 07681d75448e24..64c035307e6654 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -15,73 +15,73 @@ Run Python regression tests. If no arguments or options are provided, finds all files matching -the pattern "test_*" in the Lib/test subdirectory and runs -them in alphabetical order (but see -M and -u, below, for exceptions). +the pattern `test_*` in the `Lib/test` subdirectory and runs +them in alphabetical order (but see `-M` and `-u`, below, for exceptions). For more rigorous testing, it is useful to use the following command line: -python -E -Wd -m test [options] [test_name1 ...] +`python -E -Wd -m test [options] [test_name1 ...]` """ EPILOG = """\ Additional option details: --r randomizes test execution order. You can use --randseed=int to provide an -int seed value for the randomizer. The randseed value will be used +`-r` randomizes test execution order. You can use `--randseed=int` to provide an +int seed value for the randomizer. The `randseed` value will be used to set seeds for all random usages in tests -(including randomizing the tests order if -r is set). +(including randomizing the tests order if `-r` is set). By default we always set random seed, but do not randomize test order. --s On the first invocation of regrtest using -s, the first test file found +`-s` On the first invocation of regrtest using `-s`, the first test file found or the first test file given on the command line is run, and the name of -the next test is recorded in a file named pynexttest. If run from the -Python build directory, pynexttest is located in the 'build' subdirectory, -otherwise it is located in tempfile.gettempdir(). On subsequent runs, -the test in pynexttest is run, and the next test is written to pynexttest. -When the last test has been run, pynexttest is deleted. In this way it +the next test is recorded in a file named `pynexttest`. If run from the +Python build directory, `pynexttest` is located in the 'build' subdirectory, +otherwise it is located in `tempfile.gettempdir()`. On subsequent runs, +the test in `pynexttest` is run, and the next test is written to `pynexttest`. +When the last test has been run, `pynexttest` is deleted. In this way it is possible to single step through the test files. This is useful when doing memory analysis on the Python interpreter, which process tends to consume too many resources to run the full regression test non-stop. --S is used to resume running tests after an interrupted run. It will -maintain the order a standard run (i.e. it assumes -r is not used). +`-S` is used to resume running tests after an interrupted run. It will +maintain the order a standard run (i.e. it assumes `-r` is not used). This is useful after the tests have prematurely stopped for some external reason and you want to resume the run from where you left off rather -than starting from the beginning. Note: this is different from --prioritize. +than starting from the beginning. Note: this is different from `--prioritize`. ---prioritize is used to influence the order of selected tests, such that +`--prioritize` is used to influence the order of selected tests, such that the tests listed as an argument are executed first. This is especially -useful when combined with -j and -r to pin the longest-running tests -to start at the beginning of a test run. Pass --prioritize=test_a,test_b -to make test_a run first, followed by test_b, and then the other tests. -If test_a wasn't selected for execution by regular means, --prioritize will +useful when combined with `-j` and `-r` to pin the longest-running tests +to start at the beginning of a test run. Pass `--prioritize=test_a,test_b` +to make `test_a` run first, followed by `test_b`, and then the other tests. +If test_a wasn't selected for execution by regular means, `--prioritize` will not make it execute. --f reads the names of tests from the file given as f's argument, one +`-f` reads the names of tests from the file given as `f`'s argument, one or more test names per line. Whitespace is ignored. Blank lines and -lines beginning with '#' are ignored. This is especially useful for +lines beginning with `#` are ignored. This is especially useful for whittling down failures involving interactions among tests. --L causes the leaks(1) command to be run just before exit if it exists. -leaks(1) is available on Mac OS X and presumably on some other +`-L` causes the leaks(1) command to be run just before exit if it exists. +leaks(1) is available on macOS and presumably on some other FreeBSD-derived systems. --R runs each test several times and examines sys.gettotalrefcount() to +`-R` runs each test several times and examines `sys.gettotalrefcount()` to see if the test appears to be leaking references. The argument should -be of the form stab:run:fname where 'stab' is the number of times the -test is run to let gettotalrefcount settle down, 'run' is the number -of times further it is run and 'fname' is the name of the file the -reports are written to. These parameters all have defaults (5, 4 and -"reflog.txt" respectively), and the minimal invocation is '-R :'. +be of the form `stab:run:fname` where `stab` is the number of times the +test is run to let gettotalrefcount settle down, `run` is the number +of times further it is run and `fname` is the name of the file the +reports are written to. These parameters all have defaults (`5`, `4` and +`"reflog.txt"` respectively), and the minimal invocation is `-R :`. --M runs tests that require an exorbitant amount of memory. These tests +`-M` runs tests that require an exorbitant amount of memory. These tests typically try to ascertain containers keep working when containing more than 2 billion objects, which only works on 64-bit systems. There are also some tests that try to exhaust the address space of the process, which only makes sense on 32-bit systems with at least 2Gb of memory. The passed-in memlimit, -which is a string in the form of '2.5Gb', determines how much memory the -tests will limit themselves to (but they may go slightly over.) The number +which is a string in the form of `'2.5Gb'`, determines how much memory the +tests will limit themselves to (but they may go slightly over). The number shouldn't be more memory than the machine has (including swap memory). You should also keep in mind that swap memory is generally much, much slower than RAM, and setting memlimit to all available RAM or higher will heavily @@ -90,7 +90,7 @@ to use more than memlimit memory will be skipped. The big-memory tests generally run very, very long. --u is used to specify which special resource intensive tests to run, +`-u` is used to specify which special resource intensive tests to run, such as those requiring large file support or network connectivity. The argument is a comma-separated list of words indicating the resources to test. Currently only the following are defined: @@ -130,16 +130,23 @@ tzdata - Run tests that require timezone data. -To enable all resources except one, use '-uall,-'. For -example, to run all the tests except for the gui tests, give the -option '-uall,-gui'. + xpickle - Test pickle and _pickle against Python 3.6, 3.7, 3.8 + and 3.9 to test backwards compatibility. These tests + may take very long to complete. ---matchfile filters tests using a text file, one pattern per line. + wantobjects - Allows to run Tkinter tests with the specified value of + tkinter.wantobjects. + +To enable all resources except one, use `-uall,-`. For +example, to run all the tests except for the `gui` tests, give the +option `-uall,-gui`. + +`--matchfile` filters tests using a text file, one pattern per line. Pattern examples: -- test method: test_stat_attributes -- test class: FileTests -- test identifier: test_os.FileTests.test_stat_attributes +- test method: `test_stat_attributes` +- test class: `FileTests` +- test identifier: `test_os.FileTests.test_stat_attributes` """ @@ -158,7 +165,7 @@ def __init__(self, **kwargs) -> None: self.randomize = False self.fromfile = None self.fail_env_changed = False - self.use_resources: list[str] = [] + self.use_resources: dict[str, str | None] = {} self.trace = False self.coverdir = 'coverage' self.runleaks = False @@ -238,7 +245,7 @@ def _create_parser(): 'buildbot workers') group.add_argument('--timeout', metavar='TIMEOUT', help='dump the traceback and exit if a test takes ' - 'more than TIMEOUT seconds; disabled if TIMEOUT ' + 'more than `TIMEOUT` seconds; disabled if `TIMEOUT` ' 'is negative or equals to zero') group.add_argument('--wait', action='store_true', help='wait for user input, e.g., allow a debugger ' @@ -254,11 +261,11 @@ def _create_parser(): group = parser.add_argument_group('Verbosity') group.add_argument('-v', '--verbose', action='count', - help='run tests in verbose mode with output to stdout') + help='run tests in verbose mode with output to `stdout`') group.add_argument('-w', '--rerun', action='store_true', help='re-run failed tests in verbose mode') group.add_argument('--verbose2', action='store_true', dest='rerun', - help='deprecated alias to --rerun') + help='deprecated alias to `--rerun`') group.add_argument('-W', '--verbose3', action='store_true', help='display test output on failure') group.add_argument('-q', '--quiet', action='store_true', @@ -271,6 +278,9 @@ def _create_parser(): group = parser.add_argument_group('Selecting tests') group.add_argument('-r', '--randomize', action='store_true', help='randomize test execution order.' + more_details) + group.add_argument('--no-randomize', dest='no_randomize', action='store_true', + help='do not randomize test execution order, even if ' + 'it would be implied by another option') group.add_argument('--prioritize', metavar='TEST1,TEST2,...', action='append', type=priority_list, help='select these tests first, even if the order is' @@ -285,24 +295,24 @@ def _create_parser(): more_details) group.add_argument('-m', '--match', metavar='PAT', dest='match_tests', action=FilterAction, const=True, - help='match test cases and methods with glob pattern PAT') + help='match test cases and methods with glob pattern `PAT`') group.add_argument('-i', '--ignore', metavar='PAT', dest='match_tests', action=FilterAction, const=False, - help='ignore test cases and methods with glob pattern PAT') + help='ignore test cases and methods with glob pattern `PAT`') group.add_argument('--matchfile', metavar='FILENAME', dest='match_tests', action=FromFileFilterAction, const=True, - help='similar to --match but get patterns from a ' + help='similar to `--match` but get patterns from a ' 'text file, one pattern per line') group.add_argument('--ignorefile', metavar='FILENAME', dest='match_tests', action=FromFileFilterAction, const=False, - help='similar to --matchfile but it receives patterns ' + help='similar to `--matchfile` but it receives patterns ' 'from text file to ignore') group.add_argument('-G', '--failfast', action='store_true', - help='fail as soon as a test fails (only with -v or -W)') + help='fail as soon as a test fails (only with `-v` or `-W`)') group.add_argument('-u', '--use', metavar='RES1,RES2,...', - action='append', type=resources_list, + action='extend', type=resources_list, help='specify which special resource intensive tests ' 'to run.' + more_details) group.add_argument('-M', '--memlimit', metavar='LIMIT', @@ -315,7 +325,7 @@ def _create_parser(): group = parser.add_argument_group('Special runs') group.add_argument('-L', '--runleaks', action='store_true', - help='run the leaks(1) command just before exit.' + + help='run the `leaks(1)` command just before exit.' + more_details) group.add_argument('-R', '--huntrleaks', metavar='RUNCOUNTS', type=huntrleaks, @@ -323,20 +333,20 @@ def _create_parser(): 'very slow).' + more_details) group.add_argument('-j', '--multiprocess', metavar='PROCESSES', dest='use_mp', type=int, - help='run PROCESSES processes at once') + help='run `PROCESSES` processes at once') group.add_argument('--single-process', action='store_true', dest='single_process', help='always run all tests sequentially in ' - 'a single process, ignore -jN option, ' + 'a single process, ignore `-jN` option, ' 'and failed tests are also rerun sequentially ' 'in the same process') group.add_argument('--parallel-threads', metavar='PARALLEL_THREADS', type=int, - help='run copies of each test in PARALLEL_THREADS at ' + help='run copies of each test in `PARALLEL_THREADS` at ' 'once') group.add_argument('-T', '--coverage', action='store_true', dest='trace', - help='turn on code coverage tracing using the trace ' + help='turn on code coverage tracing using the `trace` ' 'module') group.add_argument('-D', '--coverdir', metavar='DIR', type=relative_filename, @@ -346,18 +356,18 @@ def _create_parser(): help='put coverage files alongside modules') group.add_argument('-t', '--threshold', metavar='THRESHOLD', type=int, - help='call gc.set_threshold(THRESHOLD)') + help='call `gc.set_threshold(THRESHOLD)`') group.add_argument('-n', '--nowindows', action='store_true', help='suppress error message boxes on Windows') group.add_argument('-F', '--forever', action='store_true', help='run the specified tests in a loop, until an ' - 'error happens; imply --failfast') + 'error happens; imply `--failfast`') group.add_argument('--list-tests', action='store_true', help="only write the name of tests that will be run, " "don't execute them") group.add_argument('--list-cases', action='store_true', - help='only write the name of test cases that will be run' - ' , don\'t execute them') + help='only write the name of test cases that will be run, ' + 'don\'t execute them') group.add_argument('-P', '--pgo', dest='pgo', action='store_true', help='enable Profile Guided Optimization (PGO) training') group.add_argument('--pgo-extended', action='store_true', @@ -380,9 +390,11 @@ def _create_parser(): group.add_argument('--tempdir', metavar='PATH', help='override the working directory for the test run') group.add_argument('--cleanup', action='store_true', - help='remove old test_python_* directories') + help='remove old `test_python_*` directories') group.add_argument('--bisect', action='store_true', - help='if some tests fail, run test.bisect_cmd on them') + help='if some tests fail, run `test.bisect_cmd` on them') + group.add_argument('--pythoninfo', action='store_true', + help="run `python -m test.pythoninfo` before tests") group.add_argument('--dont-add-python-opts', dest='_add_python_opts', action='store_false', help="internal option, don't use it") @@ -407,11 +419,18 @@ def huntrleaks(string): def resources_list(string): - u = [x.lower() for x in string.split(',')] - for r in u: + u = [] + for x in string.split(','): + r, eq, v = x.partition('=') + r = r.lower() + u.append((r, v if eq else None)) if r == 'all' or r == 'none': + if eq: + raise argparse.ArgumentTypeError('invalid resource: ' + x) continue if r[0] == '-': + if eq: + raise argparse.ArgumentTypeError('invalid resource: ' + x) r = r[1:] if r not in RESOURCE_NAMES: raise argparse.ArgumentTypeError('invalid resource: ' + r) @@ -461,7 +480,11 @@ def _parse_args(args, **kwargs): if ns.python is None: ns.rerun = True ns.print_slow = True - ns.verbose3 = True + if not ns.verbose: + ns.verbose3 = True + else: + # --verbose has the priority over --verbose3 + pass else: ns._add_python_opts = False @@ -475,14 +498,14 @@ def _parse_args(args, **kwargs): # Similar to: -u "all" --timeout=1200 if ns.use is None: ns.use = [] - ns.use.insert(0, ['all']) + ns.use[:0] = [('all', None)] if ns.timeout is None: ns.timeout = 1200 # 20 minutes elif ns.fast_ci: # Similar to: -u "all,-cpu" --timeout=600 if ns.use is None: ns.use = [] - ns.use.insert(0, ['all', '-cpu']) + ns.use[:0] = [('all', None), ('-cpu', None)] if ns.timeout is None: ns.timeout = 600 # 10 minutes @@ -520,25 +543,21 @@ def _parse_args(args, **kwargs): if ns.timeout <= 0: ns.timeout = None if ns.use: - for a in ns.use: - for r in a: - if r == 'all': - ns.use_resources[:] = ALL_RESOURCES - continue - if r == 'none': - del ns.use_resources[:] - continue - remove = False - if r[0] == '-': - remove = True - r = r[1:] - if remove: - if r in ns.use_resources: - ns.use_resources.remove(r) - elif r not in ns.use_resources: - ns.use_resources.append(r) + for r, v in ns.use: + if r == 'all': + for r in ALL_RESOURCES: + ns.use_resources[r] = None + elif r == 'none': + ns.use_resources.clear() + elif r[0] == '-': + r = r[1:] + ns.use_resources.pop(r, None) + else: + ns.use_resources[r] = v if ns.random_seed is not None: ns.randomize = True + if ns.no_randomize: + ns.randomize = False if ns.verbose: ns.header = True diff --git a/Lib/test/libregrtest/findtests.py b/Lib/test/libregrtest/findtests.py index f01c1240774707..6c0e50846a466b 100644 --- a/Lib/test/libregrtest/findtests.py +++ b/Lib/test/libregrtest/findtests.py @@ -24,10 +24,12 @@ "test_future_stmt", "test_gdb", "test_inspect", - "test_pydoc", + "test_io", "test_multiprocessing_fork", "test_multiprocessing_forkserver", "test_multiprocessing_spawn", + "test_os", + "test_pydoc", } diff --git a/Lib/test/libregrtest/logger.py b/Lib/test/libregrtest/logger.py index fa1d4d575c8fd4..4e011ef06f8a91 100644 --- a/Lib/test/libregrtest/logger.py +++ b/Lib/test/libregrtest/logger.py @@ -1,5 +1,6 @@ import os import time +from typing import Callable from test.support import MS_WINDOWS from .results import TestResults @@ -19,16 +20,27 @@ def __init__(self, results: TestResults, quiet: bool, pgo: bool): self._results: TestResults = results self._quiet: bool = quiet self._pgo: bool = pgo + self.get_mem_usage: Callable[[], int | None] | None = None def log(self, line: str = '') -> None: empty = not line - # add the system load prefix: "load avg: 1.80 " + # Add the memory usage: "mem: 1 GiB " + if self.get_mem_usage is not None: + mem = self.get_mem_usage() + if mem: + mib = mem / (1024*1024) + if mib >= 1024: + line = f"mem: {mib / 1024:.1f} GiB {line}" + else: + line = f"mem: {mib:.1f} MiB {line}" + + # Add the system load prefix: "load avg: 1.80 " load_avg = self.get_load_avg() if load_avg is not None: line = f"load avg: {load_avg:.2f} {line}" - # add the timestamp prefix: "0:01:05 " + # Add the timestamp prefix: "0:01:05 " log_time = time.perf_counter() - self.start_time mins, secs = divmod(int(log_time), 60) diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index a2d01b157ac89b..8773e9df73263b 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -26,8 +26,8 @@ strip_py_suffix, count, format_duration, printlist, get_temp_dir, get_work_dir, exit_timeout, display_header, cleanup_temp_dir, print_warning, - is_cross_compiled, get_host_runner, - EXIT_TIMEOUT) + is_cross_compiled, get_host_runner, display_title, + get_process_memory_usage, EXIT_TIMEOUT) class Regrtest: @@ -118,7 +118,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): self.junit_filename: StrPath | None = ns.xmlpath self.memory_limit: str | None = ns.memlimit self.gc_threshold: int | None = ns.threshold - self.use_resources: tuple[str, ...] = tuple(ns.use_resources) + self.use_resources: dict[str, str | None] = dict(ns.use_resources) if ns.python: self.python_cmd: tuple[str, ...] | None = tuple(ns.python) else: @@ -126,6 +126,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): self.coverage: bool = ns.trace self.coverage_dir: StrPath | None = ns.coverdir self._tmp_dir: StrPath | None = ns.tempdir + self.pythoninfo: bool = ns.pythoninfo # Randomize self.randomize: bool = ns.randomize @@ -322,9 +323,7 @@ def _run_bisect(self, runtests: RunTests, test: str, progress: str) -> bool: title = f"Bisect {test}" if progress: title = f"{title} ({progress})" - print(title) - print("#" * len(title)) - print() + display_title(title) cmd = runtests.create_python_cmd() cmd.extend([ @@ -345,9 +344,7 @@ def _run_bisect(self, runtests: RunTests, test: str, progress: str) -> bool: exitcode = proc.returncode title = f"{title}: exit code {exitcode}" - print(title) - print("#" * len(title)) - print(flush=True) + display_title(title) if exitcode: print(f"Bisect failed with exit code {exitcode}") @@ -396,7 +393,12 @@ def run_test( return result + def _get_mem_usage(self): + return get_process_memory_usage(os.getpid()) + def run_tests_sequentially(self, runtests: RunTests) -> None: + if not self.pgo: + self.logger.get_mem_usage = self._get_mem_usage if self.coverage: tracer = trace.Trace(trace=False, count=True) else: @@ -646,15 +648,23 @@ def _add_cross_compile_opts(self, regrtest_opts): return (environ, keep_environ) def _add_ci_python_opts(self, python_opts, keep_environ): - # --fast-ci and --slow-ci add options to Python: - # "-u -W default -bb -E" - - # Unbuffered stdout and stderr - if not sys.stdout.write_through: + # --fast-ci and --slow-ci add options to Python. + # + # Some platforms run tests in embedded mode and cannot change options + # after startup, so if this function changes, consider also updating: + # * gradle_task in Android/android.py + + # Unbuffered stdout and stderr. This isn't helpful on Android, because + # it would cause lines to be split into multiple log messages. + if not sys.stdout.write_through and sys.platform != "android": python_opts.append('-u') - # Add warnings filter 'error' - if 'default' not in sys.warnoptions: + # Add warnings filter 'error', unless the user specified a different + # filter. Ignore BytesWarning since it's controlled by '-b' below. + if not [ + opt for opt in sys.warnoptions + if not opt.endswith("::BytesWarning") + ]: python_opts.extend(('-W', 'error')) # Error on bytes/str comparison @@ -673,8 +683,12 @@ def _execute_python(self, cmd, environ): cmd_text = shlex.join(cmd) try: - print(f"+ {cmd_text}", flush=True) + # Android and iOS run tests in embedded mode. To update their + # Python options, see the comment in _add_ci_python_opts. + if not cmd[0]: + raise ValueError("No Python executable is present") + print(f"+ {cmd_text}", flush=True) if hasattr(os, 'execv') and not MS_WINDOWS: os.execv(cmd[0], cmd) # On success, execv() do no return. @@ -740,6 +754,15 @@ def tmp_dir(self) -> StrPath: ) return self._tmp_dir + def run_pythoninfo(self): + from test import pythoninfo + try: + pythoninfo.main() + except SystemExit: + # Ignore non-zero exit code on purpose + pass + print() + def main(self, tests: TestList | None = None) -> NoReturn: if self.want_add_python_opts: self._add_python_opts() @@ -753,6 +776,9 @@ def main(self, tests: TestList | None = None) -> NoReturn: if self.want_wait: input("Press any key to continue...") + if self.pythoninfo: + self.run_pythoninfo() + setup_test_dir(self.test_dir) selected, tests = self.find_tests(tests) diff --git a/Lib/test/libregrtest/refleak.py b/Lib/test/libregrtest/refleak.py index 5c78515506df59..e7da17e500ead9 100644 --- a/Lib/test/libregrtest/refleak.py +++ b/Lib/test/libregrtest/refleak.py @@ -93,6 +93,13 @@ def runtest_refleak(test_name, test_func, for obj in abc.__subclasses__() + [abc]: abcs[obj] = _get_dump(obj)[0] + # `ByteString` is not included in `collections.abc.__all__` + with warnings.catch_warnings(action='ignore', category=DeprecationWarning): + ByteString = collections.abc.ByteString + # Mypy doesn't even think `ByteString` is a class, hence the `type: ignore` + for obj in ByteString.__subclasses__() + [ByteString]: # type: ignore[attr-defined] + abcs[obj] = _get_dump(obj)[0] + # bpo-31217: Integer pool to get a single integer object for the same # value. The pool is used to prevent false alarm when checking for memory # block leaks. Fill the pool with values in -1000..1000 which are the most @@ -254,6 +261,8 @@ def dash_R_cleanup(fs, ps, pic, zdc, abcs, linecache_data): # Clear ABC registries, restoring previously saved ABC registries. abs_classes = [getattr(collections.abc, a) for a in collections.abc.__all__] + with warnings.catch_warnings(action='ignore', category=DeprecationWarning): + abs_classes.append(collections.abc.ByteString) abs_classes = filter(isabstract, abs_classes) for abc in abs_classes: for obj in abc.__subclasses__() + [abc]: diff --git a/Lib/test/libregrtest/run_workers.py b/Lib/test/libregrtest/run_workers.py index 424085a0050eb5..befdac7ee77f10 100644 --- a/Lib/test/libregrtest/run_workers.py +++ b/Lib/test/libregrtest/run_workers.py @@ -22,7 +22,7 @@ from .single import PROGRESS_MIN_TIME from .utils import ( StrPath, TestName, - format_duration, print_warning, count, plural) + format_duration, print_warning, count, plural, get_process_memory_usage) from .worker import create_worker_process, USE_PROCESS_GROUP if MS_WINDOWS: @@ -452,6 +452,12 @@ def wait_stopped(self, start_time: float) -> None: print_warning(f"Failed to join {self} in {format_duration(dt)}") break + def get_mem_usage(self): + popen = self._popen + if popen is None: + return + return get_process_memory_usage(popen.pid) + def get_running(workers: list[WorkerThread]) -> str | None: running: list[str] = [] @@ -473,6 +479,7 @@ def __init__(self, num_workers: int, runtests: RunTests, logger: Logger, results: TestResults) -> None: self.num_workers = num_workers self.runtests = runtests + self.logger = logger self.log = logger.log self.display_progress = logger.display_progress self.results: TestResults = results @@ -598,9 +605,21 @@ def _process_result(self, item: QueueOutput) -> TestResult: return result + def get_mem_usage(self): + usage = 0 + main_mem = get_process_memory_usage(os.getpid()) + if main_mem: + usage += main_mem + for worker in self.workers: + worker_mem = worker.get_mem_usage() + if worker_mem: + usage += worker_mem + return usage + def run(self) -> None: fail_fast = self.runtests.fail_fast fail_env_changed = self.runtests.fail_env_changed + self.logger.get_mem_usage = self.get_mem_usage self.start_workers() @@ -625,3 +644,4 @@ def run(self) -> None: # worker when we exit this function self.pending.stop() self.stop_workers() + self.logger.get_mem_usage = None diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py index 759f24fc25e38c..0a9edce1085be5 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -96,7 +96,7 @@ class RunTests: coverage: bool memory_limit: str | None gc_threshold: int | None - use_resources: tuple[str, ...] + use_resources: dict[str, str | None] python_cmd: tuple[str, ...] | None randomize: bool random_seed: int | str @@ -159,7 +159,7 @@ def create_python_cmd(self) -> list[str]: if '-u' not in python_opts: cmd.append('-u') # Unbuffered stdout and stderr if self.coverage: - cmd.append("-Xpresite=test.cov") + cmd.append("-Xpresite=test.cov:enable") return cmd def bisect_cmd_args(self) -> list[str]: @@ -179,7 +179,14 @@ def bisect_cmd_args(self) -> list[str]: if self.gc_threshold: args.append(f"--threshold={self.gc_threshold}") if self.use_resources: - args.extend(("-u", ','.join(self.use_resources))) + simple = ','.join(resource + for resource, value in self.use_resources.items() + if value is None) + if simple: + args.extend(("-u", simple)) + for resource, value in self.use_resources.items(): + if value is not None: + args.extend(("-u", f"{resource}={value}")) if self.python_cmd: cmd = shlex.join(self.python_cmd) args.extend(("--python", cmd)) diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index 4cf1a075b30013..138465012a252c 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -9,6 +9,13 @@ from .utils import print_warning +# Import termios to save and restore terminal echo. This is only available on +# Unix, and it's fine if the module can't be found. +try: + import termios # noqa: F401 +except ModuleNotFoundError: + pass + class SkipTestEnvironment(Exception): pass @@ -65,6 +72,7 @@ def __init__(self, test_name, verbose, quiet, *, pgo): 'shutil_archive_formats', 'shutil_unpack_formats', 'asyncio.events._event_loop_policy', 'urllib.requests._url_tempfiles', 'urllib.requests._opener', + 'stty_echo', ) def get_module(self, name): @@ -292,6 +300,24 @@ def restore_warnings_showwarning(self, fxn): warnings = self.get_module('warnings') warnings.showwarning = fxn + def get_stty_echo(self): + termios = self.try_get_module('termios') + if not os.isatty(fd := sys.__stdin__.fileno()): + return None + attrs = termios.tcgetattr(fd) + lflags = attrs[3] + return bool(lflags & termios.ECHO) + def restore_stty_echo(self, echo): + termios = self.get_module('termios') + attrs = termios.tcgetattr(fd := sys.__stdin__.fileno()) + if echo: + # Turn echo on. + attrs[3] |= termios.ECHO + else: + # Turn echo off. + attrs[3] &= ~termios.ECHO + termios.tcsetattr(fd, termios.TCSADRAIN, attrs) + def resource_info(self): for name in self.resources: method_suffix = name.replace('.', '_') diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index 9bfc414cd615c8..b9b76a44e3b4e7 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -8,6 +8,7 @@ import unittest from test import support from test.support.os_helper import TESTFN_UNDECODABLE, FS_NONASCII +from _colorize import can_colorize # type: ignore[import-not-found] from .filter import set_match_tests from .runtests import RunTests @@ -139,3 +140,10 @@ def setup_tests(runtests: RunTests) -> None: gc.set_threshold(runtests.gc_threshold) random.seed(runtests.random_seed) + + # sys.stdout is redirected to a StringIO in single process mode on which + # color auto-detect fails as StringIO is not a TTY. If the original + # sys.stdout supports color pass that through with FORCE_COLOR so that when + # results are printed, such as with -W, they get color. + if can_colorize(file=sys.stdout): + os.environ['FORCE_COLOR'] = "1" diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index 958a915626ad24..d0759d2626989d 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -145,7 +145,7 @@ def regrtest_runner(result: TestResult, test_func, runtests: RunTests) -> None: # Storage of uncollectable GC objects (gc.garbage) -GC_GARBAGE = [] +GC_GARBAGE: list[object] = [] def _load_run_test(result: TestResult, runtests: RunTests) -> None: diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py index f1f8c8bde920ae..bacfe5e21ba0b7 100644 --- a/Lib/test/libregrtest/tsan.py +++ b/Lib/test/libregrtest/tsan.py @@ -29,6 +29,7 @@ 'test_threadsignals', 'test_weakref', 'test_free_threading', + 'test_embed', ] # Tests that should be run with `--parallel-threads=N` under TSAN. These tests diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index 72b8ea89e62ee0..32f02429ff3307 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -12,7 +12,17 @@ import sysconfig import tempfile import textwrap -from collections.abc import Callable, Iterable +import types +from collections.abc import Callable +_winapi: types.ModuleType | None +try: + import _winapi +except ImportError: + _winapi = None +try: + from _testcapi import get_process_memory_usage as _get_process_memory_usage +except ImportError: + _get_process_memory_usage = None from test import support from test.support import os_helper @@ -41,7 +51,7 @@ # - tzdata: while needed to validate fully test_datetime, it makes # test_datetime too slow (15-20 min on some buildbots) and so is disabled by # default (see bpo-30822). -RESOURCE_NAMES = ALL_RESOURCES + ('extralargefile', 'tzdata') +RESOURCE_NAMES = ALL_RESOURCES + ('extralargefile', 'tzdata', 'xpickle', 'wantobjects') # Types for types hints @@ -150,7 +160,7 @@ def setup_unraisable_hook() -> None: sys.unraisablehook = regrtest_unraisable_hook -orig_threading_excepthook: Callable[..., None] | None = None +orig_threading_excepthook: Callable[..., object] | None = None def regrtest_threading_excepthook(args) -> None: @@ -294,6 +304,25 @@ def clear_caches(): else: importlib_metadata.FastPath.__new__.cache_clear() + try: + encodings = sys.modules['encodings'] + except KeyError: + pass + else: + encodings._cache.clear() + + try: + codecs = sys.modules['codecs'] + except KeyError: + pass + else: + # There's no direct API to clear the codecs search cache, but + # `unregister` clears it implicitly. + def noop_search_function(name): + return None + codecs.register(noop_search_function) + codecs.unregister(noop_search_function) + def get_build_info(): # Get most important configure and build options as a list of strings. @@ -433,12 +462,6 @@ def get_temp_dir(tmp_dir: StrPath | None = None) -> StrPath: f"unexpectedly returned {tmp_dir!r} on WASI" ) tmp_dir = os.path.join(tmp_dir, 'build') - - # When get_temp_dir() is called in a worker process, - # get_temp_dir() path is different than in the parent process - # which is not a WASI process. So the parent does not create - # the same "tmp_dir" than the test worker process. - os.makedirs(tmp_dir, exist_ok=True) else: tmp_dir = tempfile.gettempdir() @@ -536,7 +559,7 @@ def normalize_test_name(test_full_name: str, *, if is_error and short_name in _TEST_LIFECYCLE_HOOKS: if test_full_name.startswith(('setUpModule (', 'tearDownModule (')): # if setUpModule() or tearDownModule() failed, don't filter - # tests with the test file name, don't use use filters. + # tests with the test file name, don't use filters. return None # This means that we have a failure in a life-cycle hook, @@ -588,21 +611,30 @@ def is_cross_compiled() -> bool: return ('_PYTHON_HOST_PLATFORM' in os.environ) -def format_resources(use_resources: Iterable[str]) -> str: - use_resources = set(use_resources) +def format_resources(use_resources: dict[str, str | None]) -> str: all_resources = set(ALL_RESOURCES) + values = [] + for name in sorted(use_resources): + if use_resources[name] is not None: + values.append(f'{name}={use_resources[name]}') + # Express resources relative to "all" relative_all = ['all'] - for name in sorted(all_resources - use_resources): + for name in sorted(all_resources - set(use_resources)): relative_all.append(f'-{name}') - for name in sorted(use_resources - all_resources): - relative_all.append(f'{name}') - all_text = ','.join(relative_all) + for name in sorted(set(use_resources) - all_resources): + if use_resources[name] is None: + relative_all.append(name) + all_text = ','.join(relative_all + values) all_text = f"resources: {all_text}" # List of enabled resources - text = ','.join(sorted(use_resources)) + resources = [] + for name in sorted(use_resources): + if use_resources[name] is None: + resources.append(name) + text = ','.join(resources + values) text = f"resources ({len(use_resources)}): {text}" # Pick the shortest string (prefer relative to all if lengths are equal) @@ -612,7 +644,7 @@ def format_resources(use_resources: Iterable[str]) -> str: return text -def display_header(use_resources: tuple[str, ...], +def display_header(use_resources: dict[str, str | None], python_cmd: tuple[str, ...] | None) -> None: # Print basic platform information print("==", platform.python_implementation(), *sys.version.split()) @@ -724,3 +756,61 @@ def _sanitize_xml_replace(regs): def sanitize_xml(text: str) -> str: return ILLEGAL_XML_CHARS_RE.sub(_sanitize_xml_replace, text) + + +def display_title(title): + print(title) + print("#" * len(title)) + print(flush=True) + + +def _get_process_memory_usage_linux(pid: int) -> int | None: + # Linux implementation: read the private memory in bytes from + # /proc/pid/smaps. + try: + fp = open(f"/proc/{pid}/smaps", "rb") + except OSError: + return None + + try: + total = 0 + with fp: + for line in fp: + # Include both Private_Clean and Private_Dirty sections. + line = line.rstrip() + if line.startswith(b"Private_") and line.endswith(b'kB'): + parts = line.split() + total += int(parts[1]) * 1024 + return total + except ProcessLookupError: + return None + + +def _get_process_memory_usage_windows(pid: int) -> int | None: + assert _winapi is not None # to make mypy happy + try: + handle = _winapi.OpenProcess(_winapi.PROCESS_QUERY_LIMITED_INFORMATION, + False, pid) + except OSError: + return None + try: + mem_info = _winapi.GetProcessMemoryInfo(handle) + finally: + _winapi.CloseHandle(handle) + return mem_info['WorkingSetSize'] + + +if _get_process_memory_usage is not None: + def get_process_memory_usage(pid: int) -> int | None: + try: + return _get_process_memory_usage(pid) + except ProcessLookupError: + return None +elif _winapi is not None: + get_process_memory_usage = _get_process_memory_usage_windows +elif sys.platform == 'linux': + get_process_memory_usage = _get_process_memory_usage_linux +else: + def get_process_memory_usage(pid: int) -> int | None: + return None +get_process_memory_usage.__doc__ = "Get process memory usage in bytes." diff --git a/Lib/test/libregrtest/worker.py b/Lib/test/libregrtest/worker.py index 5d75bf7ae787ed..4e69ab9d8fad05 100644 --- a/Lib/test/libregrtest/worker.py +++ b/Lib/test/libregrtest/worker.py @@ -1,6 +1,7 @@ import subprocess import sys import os +from _colorize import can_colorize # type: ignore[import-not-found] from typing import Any, NoReturn from test.support import os_helper, Py_DEBUG @@ -32,6 +33,12 @@ def create_worker_process(runtests: WorkerRunTests, output_fd: int, env['TEMP'] = tmp_dir env['TMP'] = tmp_dir + # The subcommand is run with a temporary output which means it is not a TTY + # and won't auto-color. The test results are printed to stdout so if we can + # color that have the subprocess use color. + if can_colorize(file=sys.stdout): + env['FORCE_COLOR'] = '1' + # Running the child from the same working directory as regrtest's original # invocation ensures that TEMPDIR for the child is the same when # sysconfig.is_python_build() is true. See issue 15300. @@ -120,6 +127,9 @@ def main() -> NoReturn: worker_json = sys.argv[1] tmp_dir = get_temp_dir() + # get_temp_dir() can be different in the worker and the parent process. + # For example, if --tempdir option is used. + os.makedirs(tmp_dir, exist_ok=True) work_dir = get_work_dir(tmp_dir, worker=True) with exit_timeout(): diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index 68d6bad2094268..e76f79c274e744 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -32,13 +32,13 @@ def test_init(self): self.assertEqual(a, b) def test_getitem_error(self): - a = [] + a = self.type2test([]) msg = "list indices must be integers or slices" with self.assertRaisesRegex(TypeError, msg): a['a'] def test_setitem_error(self): - a = [] + a = self.type2test([]) msg = "list indices must be integers or slices" with self.assertRaisesRegex(TypeError, msg): a['a'] = "python" @@ -561,7 +561,7 @@ def test_constructor_exception_handling(self): class F(object): def __iter__(self): raise KeyboardInterrupt - self.assertRaises(KeyboardInterrupt, list, F()) + self.assertRaises(KeyboardInterrupt, self.type2test, F()) def test_exhausted_iterator(self): a = self.type2test([1, 2, 3]) diff --git a/Lib/test/mapping_tests.py b/Lib/test/mapping_tests.py index 20306e1526d7b8..9624072e69adfc 100644 --- a/Lib/test/mapping_tests.py +++ b/Lib/test/mapping_tests.py @@ -4,7 +4,7 @@ from test import support -class BasicTestMappingProtocol(unittest.TestCase): +class BasicTestImmutableMappingProtocol(unittest.TestCase): # This base class can be used to check that an object conforms to the # mapping protocol @@ -20,12 +20,9 @@ def _empty_mapping(self): """Return an empty mapping object""" return self.type2test() def _full_mapping(self, data): - """Return a mapping object with the value contained in data + """Return a mapping object with the values contained in data dictionary""" - x = self._empty_mapping() - for key, value in data.items(): - x[key] = value - return x + return self.type2test(data) def __init__(self, *args, **kw): unittest.TestCase.__init__(self, *args, **kw) @@ -88,6 +85,72 @@ def check_iterandlist(iter, lst, ref): self.assertEqual(d.get(knownkey, knownvalue), knownvalue) self.assertNotIn(knownkey, d) + def test_constructor(self): + self.assertEqual(self._empty_mapping(), self._empty_mapping()) + + def test_bool(self): + self.assertTrue(not self._empty_mapping()) + self.assertTrue(self.reference) + self.assertFalse(bool(self._empty_mapping())) + self.assertTrue(bool(self.reference)) + + def test_keys(self): + d = self._empty_mapping() + self.assertEqual(list(d.keys()), []) + d = self.reference + self.assertIn(list(self.inmapping.keys())[0], d.keys()) + self.assertNotIn(list(self.other.keys())[0], d.keys()) + self.assertRaises(TypeError, d.keys, None) + + def test_values(self): + d = self._empty_mapping() + self.assertEqual(list(d.values()), []) + + self.assertRaises(TypeError, d.values, None) + + def test_items(self): + d = self._empty_mapping() + self.assertEqual(list(d.items()), []) + + self.assertRaises(TypeError, d.items, None) + + def test_len(self): + d = self._empty_mapping() + self.assertEqual(len(d), 0) + + def test_getitem(self): + d = self.reference + self.assertEqual(d[list(self.inmapping.keys())[0]], + list(self.inmapping.values())[0]) + + self.assertRaises(TypeError, d.__getitem__) + + # no test_fromkeys or test_copy as both os.environ and selves don't support it + + def test_get(self): + d = self._empty_mapping() + self.assertIsNone(d.get(list(self.other.keys())[0])) + self.assertEqual(d.get(list(self.other.keys())[0], 3), 3) + d = self.reference + self.assertIsNone(d.get(list(self.other.keys())[0])) + self.assertEqual(d.get(list(self.other.keys())[0], 3), 3) + self.assertEqual(d.get(list(self.inmapping.keys())[0]), + list(self.inmapping.values())[0]) + self.assertEqual(d.get(list(self.inmapping.keys())[0], 3), + list(self.inmapping.values())[0]) + self.assertRaises(TypeError, d.get) + self.assertRaises(TypeError, d.get, None, None, None) + + +class BasicTestMappingProtocol(BasicTestImmutableMappingProtocol): + def _full_mapping(self, data): + """Return a mapping object with the values contained in data + dictionary""" + x = self._empty_mapping() + for key, value in data.items(): + x[key] = value + return x + def test_write(self): # Test for write operations on mapping p = self._empty_mapping() @@ -130,46 +193,6 @@ def test_write(self): p=self._empty_mapping() self.assertRaises(KeyError, p.popitem) - def test_constructor(self): - self.assertEqual(self._empty_mapping(), self._empty_mapping()) - - def test_bool(self): - self.assertTrue(not self._empty_mapping()) - self.assertTrue(self.reference) - self.assertTrue(bool(self._empty_mapping()) is False) - self.assertTrue(bool(self.reference) is True) - - def test_keys(self): - d = self._empty_mapping() - self.assertEqual(list(d.keys()), []) - d = self.reference - self.assertIn(list(self.inmapping.keys())[0], d.keys()) - self.assertNotIn(list(self.other.keys())[0], d.keys()) - self.assertRaises(TypeError, d.keys, None) - - def test_values(self): - d = self._empty_mapping() - self.assertEqual(list(d.values()), []) - - self.assertRaises(TypeError, d.values, None) - - def test_items(self): - d = self._empty_mapping() - self.assertEqual(list(d.items()), []) - - self.assertRaises(TypeError, d.items, None) - - def test_len(self): - d = self._empty_mapping() - self.assertEqual(len(d), 0) - - def test_getitem(self): - d = self.reference - self.assertEqual(d[list(self.inmapping.keys())[0]], - list(self.inmapping.values())[0]) - - self.assertRaises(TypeError, d.__getitem__) - def test_update(self): # mapping argument d = self._empty_mapping() @@ -265,22 +288,6 @@ def __next__(self): self.assertRaises(ValueError, d.update, [(1, 2, 3)]) - # no test_fromkeys or test_copy as both os.environ and selves don't support it - - def test_get(self): - d = self._empty_mapping() - self.assertTrue(d.get(list(self.other.keys())[0]) is None) - self.assertEqual(d.get(list(self.other.keys())[0], 3), 3) - d = self.reference - self.assertTrue(d.get(list(self.other.keys())[0]) is None) - self.assertEqual(d.get(list(self.other.keys())[0], 3), 3) - self.assertEqual(d.get(list(self.inmapping.keys())[0]), - list(self.inmapping.values())[0]) - self.assertEqual(d.get(list(self.inmapping.keys())[0], 3), - list(self.inmapping.values())[0]) - self.assertRaises(TypeError, d.get) - self.assertRaises(TypeError, d.get, None, None, None) - def test_setdefault(self): d = self._empty_mapping() self.assertRaises(TypeError, d.setdefault) diff --git a/Lib/test/mathdata/math_testcases.txt b/Lib/test/mathdata/math_testcases.txt index 958518824376f8..4a38a3666bab20 100644 --- a/Lib/test/mathdata/math_testcases.txt +++ b/Lib/test/mathdata/math_testcases.txt @@ -631,3 +631,1619 @@ log20646 log2 2.535995592365391e+208 -> 692.30359597460460 log20647 log2 6.2011236566089916e+233 -> 776.64177576730913 log20648 log2 2.1843274820677632e+253 -> 841.57499717289647 log20649 log2 8.7493931063474791e+297 -> 989.74182713073981 + +------------ +-- acospi -- +------------ + +acospi10000 acospi 0.0 -> 0.5 +acospi10001 acospi -0.0 -> 0.5 +acospi10002 acospi 1.0 -> 0.0 +acospi10003 acospi -1.0 -> 1.0 +acospi10004 acospi 0.5 -> 0.3333333333333333 +acospi10005 acospi -0.5 -> 0.6666666666666666 +acospi10006 acospi 0.75 -> 0.23005345616261588 +acospi10007 acospi 2.0000001329864406e-17 -> 0.5 +acospi10008 acospi 1.999999967550318e-17 -> 0.5 +acospi10009 acospi 2e-17 -> 0.5 +acospi10010 acospi 1.9999999999999998e-17 -> 0.5 +acospi10011 acospi 0.0625 -> 0.48009265723071126 +acospi10012 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10013 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10014 acospi 1.0 -> 0.0 +acospi10015 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10016 acospi 0.9999999998835847 -> 4.857023409833964e-06 +acospi10017 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10018 acospi -1.0 -> 1.0 +acospi10019 acospi -0.9999999998835847 -> 0.9999951429765902 +acospi10020 acospi 1.0 -> 0.0 +acospi10021 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10022 acospi 0.9999999999999964 -> 2.6831517105016303e-08 +acospi10023 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10024 acospi -1.0 -> 1.0 +acospi10025 acospi -0.9999999999999964 -> 0.9999999731684829 +acospi10026 acospi 1.0 -> 0.0 +acospi10027 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10028 acospi 0.9999999999999999 -> 4.743186923619966e-09 +acospi10029 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10030 acospi -1.0 -> 1.0 +acospi10031 acospi -0.9999999999999999 -> 0.999999995256813 +acospi10032 acospi 1.0 -> 0.0 +acospi10033 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10034 acospi 0.9999999999999999 -> 4.743186923619966e-09 +acospi10035 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10036 acospi -1.0 -> 1.0 +acospi10037 acospi -0.9999999999999999 -> 0.999999995256813 +acospi10038 acospi 1.0 -> 0.0 +acospi10039 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10040 acospi 0.9999999999999999 -> 4.743186923619966e-09 +acospi10041 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10042 acospi -1.0 -> 1.0 +acospi10043 acospi -0.9999999999999999 -> 0.999999995256813 +acospi10044 acospi 1.0 -> 0.0 +acospi10045 acospi 0.9999999403953552 -> 0.00010990189460803536 +acospi10046 acospi 0.9999999999999999 -> 4.743186923619966e-09 +acospi10047 acospi -0.9999999403953552 -> 0.999890098105392 +acospi10048 acospi -1.0 -> 1.0 +acospi10049 acospi -0.9999999999999999 -> 0.999999995256813 +acospi10050 acospi 0.03125 -> 0.4900511963370614 +acospi10051 acospi 0.0009765625 -> 0.49968915045236545 +acospi10052 acospi 3.0517578125e-05 -> 0.4999902859531789 +acospi10053 acospi 9.5367431640625e-07 -> 0.49999969643603687 +acospi10054 acospi 2.9802322387695312e-08 -> 0.49999999051362615 +acospi10055 acospi 9.313225746154785e-10 -> 0.4999999997035508 +acospi10056 acospi 2.9103830456733704e-11 -> 0.49999999999073597 +acospi10057 acospi 9.094947017729282e-13 -> 0.4999999999997105 +acospi10058 acospi 2.842170943040401e-14 -> 0.49999999999999095 +acospi10059 acospi 8.881784197001252e-16 -> 0.4999999999999997 +acospi10060 acospi 2.7755575615628914e-17 -> 0.5 +acospi10061 acospi 8.673617379884035e-19 -> 0.5 +acospi10062 acospi 2.710505431213761e-20 -> 0.5 +acospi10063 acospi 8.470329472543003e-22 -> 0.5 +acospi10064 acospi 2.6469779601696886e-23 -> 0.5 +acospi10065 acospi 8.271806125530277e-25 -> 0.5 +acospi10066 acospi 2.5849394142282115e-26 -> 0.5 +acospi10067 acospi 8.077935669463161e-28 -> 0.5 +acospi10068 acospi 2.524354896707238e-29 -> 0.5 +acospi10069 acospi 7.888609052210118e-31 -> 0.5 +acospi10070 acospi 2.465190328815662e-32 -> 0.5 +acospi10071 acospi 7.703719777548943e-34 -> 0.5 +acospi10072 acospi 2.407412430484045e-35 -> 0.5 +acospi10073 acospi 7.52316384526264e-37 -> 0.5 +acospi10074 acospi -0.03125 -> 0.5099488036629386 +acospi10075 acospi -2.9802322387695312e-08 -> 0.5000000094863738 +acospi10076 acospi -2.842170943040401e-14 -> 0.500000000000009 +acospi10077 acospi -2.710505431213761e-20 -> 0.5 +acospi10078 acospi -2.5849394142282115e-26 -> 0.5 +acospi10079 acospi -2.465190328815662e-32 -> 0.5 +acospi10080 acospi -2.350988701644575e-38 -> 0.5 +acospi10081 acospi -0.1251312643289566 -> 0.5399352010629168 +acospi10082 acospi 0.9386433362960815 -> 0.1120835750337698 +acospi10083 acospi 0.3664908707141876 -> 0.3805590984950769 +acospi10084 acospi 0.36649084091186523 -> 0.38055910869085624 +acospi10085 acospi 0.366490841000206 -> 0.38055910866063364 +acospi10086 acospi -0.13816863298416138 -> 0.5441215931223088 +acospi10087 acospi -0.13816864788532257 -> 0.5441215979114296 +acospi10088 acospi -0.13816864007172486 -> 0.5441215954001982 +acospi10089 acospi 0.9126854538917542 -> 0.1340047775091756 +acospi10090 acospi 0.9126853942871094 -> 0.13400482393558352 +acospi10091 acospi 0.912685444573738 -> 0.13400478476703412 +acospi10092 acospi 0.9126854445737379 -> 0.1340047847670342 +acospi10093 acospi 0.880692720413208 -> 0.1570775606195134 +acospi10094 acospi 0.8806926608085632 -> 0.15707760067276577 +acospi10095 acospi 0.8806926814412734 -> 0.15707758680795558 +acospi10096 acospi 0.8806926814412733 -> 0.15707758680795567 +acospi10097 acospi 0.2435227781534195 -> 0.4216969371828132 +acospi10098 acospi 0.2435227632522583 -> 0.42169694207322506 +acospi10099 acospi 0.24352277005396675 -> 0.42169693984097245 +acospi10100 acospi 0.1279129981994629 -> 0.45917217189807447 +acospi10101 acospi -0.11572346091270447 -> 0.5369186384114879 +acospi10102 acospi 0.9999996423721313 -> 0.0002692035702405639 +acospi10103 acospi 0.9999995827674866 -> 0.000290773090413406 +acospi10104 acospi 0.9999996241289419 -> 0.0002759844303031754 +acospi10105 acospi 0.9995118975639343 -> 0.009945766966749 +acospi10106 acospi 0.9995118379592896 -> 0.009946374261451639 +acospi10107 acospi 0.9995118644629029 -> 0.009946104228281858 +acospi10108 acospi 0.9995118644629027 -> 0.009946104228282989 +acospi10109 acospi 1.1754943508222875e-38 -> 0.5 +acospi10110 acospi 2.2250738585072014e-308 -> 0.5 +acospi10111 acospi 2.004168360008973e-292 -> 0.5 +acospi10112 acospi -1.1754943508222875e-38 -> 0.5 +acospi10113 acospi -2.2250738585072014e-308 -> 0.5 +acospi10114 acospi -2.004168360008973e-292 -> 0.5 +acospi10115 acospi 1.401298464324817e-45 -> 0.5 +acospi10116 acospi 5e-324 -> 0.5 +acospi10117 acospi -1.401298464324817e-45 -> 0.5 +acospi10118 acospi -5e-324 -> 0.5 +acospi10119 acospi -0.5356916189193726 -> 0.6799490414927629 +acospi10120 acospi 0.9258930683135986 -> 0.12331439084862783 +acospi10121 acospi 0.9258930087089539 -> 0.12331444106954574 +acospi10122 acospi 0.9258930140454763 -> 0.12331443657316782 +acospi10123 acospi 0.9997028112411499 -> 0.007760542300314392 +acospi10124 acospi 0.9997027516365051 -> 0.007761320533135704 +acospi10125 acospi 0.9997027756274229 -> 0.00776100730317654 +acospi10126 acospi 0.9997027756274228 -> 0.007761007303177989 +acospi10127 acospi 0.7071993947029114 -> 0.24995830653935586 +acospi10128 acospi 0.7071993350982666 -> 0.249958333374387 +acospi10129 acospi 0.7071993911300235 -> 0.24995830814793119 +acospi10130 acospi 0.7071993911300234 -> 0.24995830814793124 + +acospi20001 acospi inf -> nan invalid +acospi20002 acospi -inf -> nan invalid +acospi20003 acospi nan -> nan + +------------ +-- asinpi -- +------------ + +asinpi10000 asinpi 0.0 -> 0.0 +asinpi10001 asinpi -0.0 -> -0.0 +asinpi10002 asinpi 0.5 -> 0.16666666666666666 +asinpi10003 asinpi -0.5 -> -0.16666666666666666 +asinpi10004 asinpi 1.0 -> 0.5 +asinpi10005 asinpi -1.0 -> -0.5 +asinpi10006 asinpi 0.75 -> 0.2699465438373841 +asinpi10007 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10008 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10009 asinpi 1.0 -> 0.5 +asinpi10010 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10011 asinpi 0.9999999998835847 -> 0.49999514297659015 +asinpi10012 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10013 asinpi -1.0 -> -0.5 +asinpi10014 asinpi -0.9999999998835847 -> -0.49999514297659015 +asinpi10015 asinpi 1.0 -> 0.5 +asinpi10016 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10017 asinpi 0.9999999999999964 -> 0.4999999731684829 +asinpi10018 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10019 asinpi -1.0 -> -0.5 +asinpi10020 asinpi -0.9999999999999964 -> -0.4999999731684829 +asinpi10021 asinpi 1.0 -> 0.5 +asinpi10022 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10023 asinpi 0.9999999999999999 -> 0.4999999952568131 +asinpi10024 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10025 asinpi -1.0 -> -0.5 +asinpi10026 asinpi -0.9999999999999999 -> -0.4999999952568131 +asinpi10027 asinpi 1.0 -> 0.5 +asinpi10028 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10029 asinpi 0.9999999999999999 -> 0.4999999952568131 +asinpi10030 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10031 asinpi -1.0 -> -0.5 +asinpi10032 asinpi -0.9999999999999999 -> -0.4999999952568131 +asinpi10033 asinpi 1.0 -> 0.5 +asinpi10034 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10035 asinpi 0.9999999999999999 -> 0.4999999952568131 +asinpi10036 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10037 asinpi -1.0 -> -0.5 +asinpi10038 asinpi -0.9999999999999999 -> -0.4999999952568131 +asinpi10039 asinpi 1.0 -> 0.5 +asinpi10040 asinpi 0.9999999403953552 -> 0.499890098105392 +asinpi10041 asinpi 0.9999999999999999 -> 0.4999999952568131 +asinpi10042 asinpi -0.9999999403953552 -> -0.499890098105392 +asinpi10043 asinpi -1.0 -> -0.5 +asinpi10044 asinpi -0.9999999999999999 -> -0.4999999952568131 +asinpi10045 asinpi 0.03125 -> 0.009948803662938608 +asinpi10046 asinpi 0.0009765625 -> 0.0003108495476345764 +asinpi10047 asinpi 3.0517578125e-05 -> 9.714046821081509e-06 +asinpi10048 asinpi 9.5367431640625e-07 -> 3.0356396311172383e-07 +asinpi10049 asinpi 2.9802322387695312e-08 -> 9.486373847239932e-09 +asinpi10050 asinpi 9.313225746154785e-10 -> 2.964491827262479e-10 +asinpi10051 asinpi 2.9103830456733704e-11 -> 9.264036960195246e-12 +asinpi10052 asinpi 9.094947017729282e-13 -> 2.8950115500610145e-13 +asinpi10053 asinpi 2.842170943040401e-14 -> 9.04691109394067e-15 +asinpi10054 asinpi 8.881784197001252e-16 -> 2.8271597168564594e-16 +asinpi10055 asinpi 2.7755575615628914e-17 -> 8.834874115176436e-18 +asinpi10056 asinpi 8.673617379884035e-19 -> 2.760898160992636e-19 +asinpi10057 asinpi 7.888609052210118e-31 -> 2.5110222495574236e-31 +asinpi10058 asinpi 1.401298464324817e-45 -> 4.460471546887532e-46 +asinpi10059 asinpi 0.0 -> 0.0 +asinpi10060 asinpi 2.409919865102884e-181 -> 7.671013179729553e-182 +asinpi10061 asinpi 1.401298464324817e-45 -> 4.460471546887532e-46 +asinpi10062 asinpi 0.0 -> 0.0 +asinpi10063 asinpi 5e-324 -> 0.0 +asinpi10064 asinpi -0.13099800050258636 -> -0.04181814856647883 +asinpi10065 asinpi -0.21591897308826447 -> -0.06927470561424247 +asinpi10066 asinpi -0.1944168210029602 -> -0.06228143288596484 +asinpi10067 asinpi -0.1944168359041214 -> -0.062281437721417 +asinpi10068 asinpi -0.19441682493723628 -> -0.06228143416264412 +asinpi10069 asinpi 0.9866708517074585 -> 0.4479704696000938 +asinpi10070 asinpi 0.9866707921028137 -> 0.4479703530089429 +asinpi10071 asinpi 0.9866708504122479 -> 0.4479704670665621 +asinpi10072 asinpi 0.9866708504122478 -> 0.44797046706656185 +asinpi10073 asinpi -0.6866101622581482 -> -0.24090205124758424 +asinpi10074 asinpi 0.8438399434089661 -> 0.3197103200989401 +asinpi10075 asinpi -0.13135038316249847 -> -0.04193129309542388 +asinpi10076 asinpi -0.5068531036376953 -> -0.16919134974732694 +asinpi10077 asinpi -0.5068531632423401 -> -0.1691913717566472 +asinpi10078 asinpi -0.5068531437412152 -> -0.16919136455575687 +asinpi10079 asinpi -0.7008922100067139 -> -0.2472146113103207 +asinpi10080 asinpi 0.12256252765655518 -> 0.03911120286288364 +asinpi10081 asinpi 0.12256252020597458 -> 0.03911120047327442 +asinpi10082 asinpi 0.12256252763528681 -> 0.03911120285606228 +asinpi10083 asinpi 1.1754943508222875e-38 -> 3.7417147301993126e-39 +asinpi10084 asinpi 2.2250738585072014e-308 -> 7.082630066519554e-309 +asinpi10085 asinpi 2.004168360008973e-292 -> 6.379466025676106e-293 +asinpi10086 asinpi -1.1754943508222875e-38 -> -3.7417147301993126e-39 +asinpi10087 asinpi -2.2250738585072014e-308 -> -7.082630066519554e-309 +asinpi10088 asinpi -2.004168360008973e-292 -> -6.379466025676106e-293 +asinpi10089 asinpi 1.401298464324817e-45 -> 4.460471546887532e-46 +asinpi10090 asinpi 5e-324 -> 0.0 +asinpi10091 asinpi -1.401298464324817e-45 -> -4.460471546887532e-46 +asinpi10092 asinpi -5e-324 -> -0.0 +asinpi10093 asinpi 0.972168505191803 -> 0.4247258559057922 +asinpi10094 asinpi -0.19141583144664764 -> -0.06130790022106249 +asinpi10095 asinpi -0.19141584634780884 -> -0.06130790505360787 +asinpi10096 asinpi -0.19141583181687138 -> -0.06130790034112851 +asinpi10097 asinpi 0.505088746547699 -> 0.16854024296386302 +asinpi10098 asinpi 0.5050886869430542 -> 0.16854022098093674 +asinpi10099 asinpi 0.5050886987499023 -> 0.1685402253354475 +asinpi10100 asinpi 0.5050886987499021 -> 0.16854022533544744 +asinpi10101 asinpi -0.6554324626922607 -> -0.2275136094679927 +asinpi10102 asinpi -0.6554325222969055 -> -0.2275136345890164 +asinpi10103 asinpi -0.6554324978014275 -> -0.22751362426513144 +asinpi10104 asinpi -0.6554324978014276 -> -0.2275136242651315 + +asinpi20001 asinpi inf -> nan invalid +asinpi20002 asinpi -inf -> nan invalid +asinpi20003 asinpi nan -> nan + +------------ +-- atanpi -- +------------ + +atanpi10000 atanpi 0.0 -> 0.0 +atanpi10001 atanpi -0.0 -> -0.0 +atanpi10002 atanpi 3.4028234663852886e+38 -> 0.5 +atanpi10003 atanpi 1.7976931348623157e+308 -> 0.5 +atanpi10004 atanpi -3.4028234663852886e+38 -> -0.5 +atanpi10005 atanpi -1.7976931348623157e+308 -> -0.5 +atanpi10006 atanpi 1.0 -> 0.25 +atanpi10007 atanpi -1.0 -> -0.25 +atanpi10008 atanpi 0.75 -> 0.20483276469913345 +atanpi10009 atanpi 0.03125 -> 0.009943947823589275 +atanpi10010 atanpi 0.0009765625 -> 0.0003108493994100204 +atanpi10011 atanpi 3.0517578125e-05 -> 9.714046816558053e-06 +atanpi10012 atanpi 9.5367431640625e-07 -> 3.0356396311158577e-07 +atanpi10013 atanpi 2.9802322387695312e-08 -> 9.486373847239929e-09 +atanpi10014 atanpi 9.313225746154785e-10 -> 2.964491827262479e-10 +atanpi10015 atanpi 2.9103830456733704e-11 -> 9.264036960195246e-12 +atanpi10016 atanpi 9.094947017729282e-13 -> 2.8950115500610145e-13 +atanpi10017 atanpi 2.842170943040401e-14 -> 9.04691109394067e-15 +atanpi10018 atanpi 8.881784197001252e-16 -> 2.8271597168564594e-16 +atanpi10019 atanpi 2.7755575615628914e-17 -> 8.834874115176436e-18 +atanpi10020 atanpi 8.673617379884035e-19 -> 2.760898160992636e-19 +atanpi10021 atanpi 2.5 -> 0.3788810584091566 +atanpi10022 atanpi 10.0 -> 0.4682744825694464 +atanpi10023 atanpi 1000000.0 -> 0.49999968169011383 +atanpi10024 atanpi 2147483648.0 -> 0.4999999998517754 +atanpi10025 atanpi 7.888609052210118e-31 -> 2.5110222495574236e-31 +atanpi10026 atanpi 1.401298464324817e-45 -> 4.460471546887532e-46 +atanpi10027 atanpi 0.0 -> 0.0 +atanpi10028 atanpi 2.409919865102884e-181 -> 7.671013179729553e-182 +atanpi10029 atanpi 1.401298464324817e-45 -> 4.460471546887532e-46 +atanpi10030 atanpi 0.0 -> 0.0 +atanpi10031 atanpi 5e-324 -> 0.0 +atanpi10032 atanpi -0.23051215708255768 -> -0.07211461946620386 +atanpi10033 atanpi -0.20305541157722473 -> -0.06376756760558985 +atanpi10034 atanpi -2.1964359283447266 -> -0.36400572992968316 +atanpi10035 atanpi -1.2119029760360718 -> -0.2804016904047878 +atanpi10036 atanpi -1.6769415140151978 -> -0.32884096476652747 +atanpi10037 atanpi -3.9201369285583496 -> -0.4204968782420327 +atanpi10038 atanpi 3.327171802520752 -> 0.40706420561165213 +atanpi10039 atanpi -1.3468046188354492 -> -0.2967010012978093 +atanpi10040 atanpi 0.692195475101471 -> 0.1927267050671652 +atanpi10041 atanpi 1.1754943508222875e-38 -> 3.7417147301993126e-39 +atanpi10042 atanpi 2.2250738585072014e-308 -> 7.082630066519554e-309 +atanpi10043 atanpi 2.004168360008973e-292 -> 6.379466025676106e-293 +atanpi10044 atanpi -1.1754943508222875e-38 -> -3.7417147301993126e-39 +atanpi10045 atanpi -2.2250738585072014e-308 -> -7.082630066519554e-309 +atanpi10046 atanpi -2.004168360008973e-292 -> -6.379466025676106e-293 +atanpi10047 atanpi 1.401298464324817e-45 -> 4.460471546887532e-46 +atanpi10048 atanpi 5e-324 -> 0.0 +atanpi10049 atanpi -1.401298464324817e-45 -> -4.460471546887532e-46 +atanpi10050 atanpi -5e-324 -> -0.0 +atanpi10051 atanpi 0.721666157245636 -> 0.19898171607590034 +atanpi10052 atanpi 0.19824542105197906 -> 0.06229575837467328 +atanpi10053 atanpi 0.19824540615081787 -> 0.06229575381085028 +atanpi10054 atanpi 0.198245408653517 -> 0.06229575457735939 +atanpi10055 atanpi 0.049094054847955704 -> 0.01561458616872582 +atanpi10056 atanpi 0.049094051122665405 -> 0.015614584985780253 +atanpi10057 atanpi 0.04909405149251647 -> 0.015614585103224438 +atanpi10058 atanpi 0.049094051492516465 -> 0.015614585103224436 +atanpi10059 atanpi 0.19859454035758972 -> 0.062402677059315244 +atanpi10060 atanpi 0.19859452545642853 -> 0.06240267249610054 +atanpi10061 atanpi 0.1985945401764544 -> 0.06240267700384578 +atanpi10062 atanpi 0.19859454017645436 -> 0.06240267700384577 + +atanpi20001 atanpi inf -> 0.5 +atanpi20002 atanpi -inf -> -0.5 +atanpi20003 atanpi nan -> nan + +------------- +-- atan2pi -- +------------- + +atan2pi10000 atan2pi 0.0 1.0 -> 0.0 +atan2pi10001 atan2pi -0.0 1.0 -> -0.0 +atan2pi10002 atan2pi 0.0 0.0 -> 0.0 +atan2pi10003 atan2pi -0.0 0.0 -> -0.0 +atan2pi10004 atan2pi 0.0 -1.0 -> 1.0 +atan2pi10005 atan2pi -0.0 -1.0 -> -1.0 +atan2pi10006 atan2pi 0.0 -0.0 -> 1.0 +atan2pi10007 atan2pi -0.0 -0.0 -> -1.0 +atan2pi10008 atan2pi 1.0 0.0 -> 0.5 +atan2pi10009 atan2pi 1.0 -0.0 -> 0.5 +atan2pi10010 atan2pi -1.0 0.0 -> -0.5 +atan2pi10011 atan2pi -1.0 -0.0 -> -0.5 +atan2pi10012 atan2pi 3.4028234663852886e+38 3.4028234663852886e+38 -> 0.25 +atan2pi10013 atan2pi 3.4028234663852886e+38 1.7976931348623157e+308 -> 6.025234948519683e-271 +atan2pi10014 atan2pi 1.7976931348623157e+308 3.4028234663852886e+38 -> 0.5 +atan2pi10015 atan2pi 1.7976931348623157e+308 1.7976931348623157e+308 -> 0.25 +atan2pi10016 atan2pi 3.4028234663852886e+38 -3.4028234663852886e+38 -> 0.75 +atan2pi10017 atan2pi 3.4028234663852886e+38 -1.7976931348623157e+308 -> 1.0 +atan2pi10018 atan2pi 1.7976931348623157e+308 -3.4028234663852886e+38 -> 0.5 +atan2pi10019 atan2pi 1.7976931348623157e+308 -1.7976931348623157e+308 -> 0.75 +atan2pi10020 atan2pi -3.4028234663852886e+38 3.4028234663852886e+38 -> -0.25 +atan2pi10021 atan2pi -3.4028234663852886e+38 1.7976931348623157e+308 -> -6.025234948519683e-271 +atan2pi10022 atan2pi -1.7976931348623157e+308 3.4028234663852886e+38 -> -0.5 +atan2pi10023 atan2pi -1.7976931348623157e+308 1.7976931348623157e+308 -> -0.25 +atan2pi10024 atan2pi -3.4028234663852886e+38 -3.4028234663852886e+38 -> -0.75 +atan2pi10025 atan2pi -3.4028234663852886e+38 -1.7976931348623157e+308 -> -1.0 +atan2pi10026 atan2pi -1.7976931348623157e+308 -3.4028234663852886e+38 -> -0.5 +atan2pi10027 atan2pi -1.7976931348623157e+308 -1.7976931348623157e+308 -> -0.75 +atan2pi10028 atan2pi 3.4028234663852886e+38 1.1754943508222875e-38 -> 0.5 +atan2pi10029 atan2pi 3.4028234663852886e+38 2.2250738585072014e-308 -> 0.5 +atan2pi10030 atan2pi 3.4028234663852886e+38 2.004168360008973e-292 -> 0.5 +atan2pi10031 atan2pi 1.7976931348623157e+308 1.1754943508222875e-38 -> 0.5 +atan2pi10032 atan2pi 1.7976931348623157e+308 2.2250738585072014e-308 -> 0.5 +atan2pi10033 atan2pi 1.7976931348623157e+308 2.004168360008973e-292 -> 0.5 +atan2pi10034 atan2pi -3.4028234663852886e+38 -1.1754943508222875e-38 -> -0.5 +atan2pi10035 atan2pi -3.4028234663852886e+38 -2.2250738585072014e-308 -> -0.5 +atan2pi10036 atan2pi -3.4028234663852886e+38 -2.004168360008973e-292 -> -0.5 +atan2pi10037 atan2pi -1.7976931348623157e+308 -1.1754943508222875e-38 -> -0.5 +atan2pi10038 atan2pi -1.7976931348623157e+308 -2.2250738585072014e-308 -> -0.5 +atan2pi10039 atan2pi -1.7976931348623157e+308 -2.004168360008973e-292 -> -0.5 +atan2pi10040 atan2pi -3.4028234663852886e+38 1.1754943508222875e-38 -> -0.5 +atan2pi10041 atan2pi -3.4028234663852886e+38 2.2250738585072014e-308 -> -0.5 +atan2pi10042 atan2pi -3.4028234663852886e+38 2.004168360008973e-292 -> -0.5 +atan2pi10043 atan2pi -1.7976931348623157e+308 1.1754943508222875e-38 -> -0.5 +atan2pi10044 atan2pi -1.7976931348623157e+308 2.2250738585072014e-308 -> -0.5 +atan2pi10045 atan2pi -1.7976931348623157e+308 2.004168360008973e-292 -> -0.5 +atan2pi10046 atan2pi 3.4028234663852886e+38 -1.1754943508222875e-38 -> 0.5 +atan2pi10047 atan2pi 3.4028234663852886e+38 -2.2250738585072014e-308 -> 0.5 +atan2pi10048 atan2pi 3.4028234663852886e+38 -2.004168360008973e-292 -> 0.5 +atan2pi10049 atan2pi 1.7976931348623157e+308 -1.1754943508222875e-38 -> 0.5 +atan2pi10050 atan2pi 1.7976931348623157e+308 -2.2250738585072014e-308 -> 0.5 +atan2pi10051 atan2pi 1.7976931348623157e+308 -2.004168360008973e-292 -> 0.5 +atan2pi10052 atan2pi 3.4028234663852886e+38 1.401298464324817e-45 -> 0.5 +atan2pi10053 atan2pi 3.4028234663852886e+38 5e-324 -> 0.5 +atan2pi10054 atan2pi 1.7976931348623157e+308 1.401298464324817e-45 -> 0.5 +atan2pi10055 atan2pi 1.7976931348623157e+308 5e-324 -> 0.5 +atan2pi10056 atan2pi -3.4028234663852886e+38 -1.401298464324817e-45 -> -0.5 +atan2pi10057 atan2pi -3.4028234663852886e+38 -5e-324 -> -0.5 +atan2pi10058 atan2pi -1.7976931348623157e+308 -1.401298464324817e-45 -> -0.5 +atan2pi10059 atan2pi -1.7976931348623157e+308 -5e-324 -> -0.5 +atan2pi10060 atan2pi -3.4028234663852886e+38 1.401298464324817e-45 -> -0.5 +atan2pi10061 atan2pi -3.4028234663852886e+38 5e-324 -> -0.5 +atan2pi10062 atan2pi -1.7976931348623157e+308 1.401298464324817e-45 -> -0.5 +atan2pi10063 atan2pi -1.7976931348623157e+308 5e-324 -> -0.5 +atan2pi10064 atan2pi 3.4028234663852886e+38 -1.401298464324817e-45 -> 0.5 +atan2pi10065 atan2pi 3.4028234663852886e+38 -5e-324 -> 0.5 +atan2pi10066 atan2pi 1.7976931348623157e+308 -1.401298464324817e-45 -> 0.5 +atan2pi10067 atan2pi 1.7976931348623157e+308 -5e-324 -> 0.5 +atan2pi10068 atan2pi 0.75 1.0 -> 0.20483276469913345 +atan2pi10069 atan2pi -0.75 1.0 -> -0.20483276469913345 +atan2pi10070 atan2pi 0.75 -1.0 -> 0.7951672353008665 +atan2pi10071 atan2pi -0.75 -1.0 -> -0.7951672353008665 +atan2pi10072 atan2pi 0.390625 0.00029000002541579306 -> 0.4997636867632018 +atan2pi10073 atan2pi 0.390625 0.0002899999963119626 -> 0.49976368678691774 +atan2pi10074 atan2pi 0.390625 0.00029 -> 0.4997636867839124 +atan2pi10075 atan2pi 0.390625 0.00028999999999999995 -> 0.4997636867839124 +atan2pi10076 atan2pi 1.390625 0.9296875 -> 0.3124206399141909 +atan2pi10077 atan2pi -0.007568269968032837 -0.0017927357694134116 -> -0.5740351606831543 +atan2pi10078 atan2pi -0.007568269968032837 -0.0017927358858287334 -> -0.574035165319273 +atan2pi10079 atan2pi -0.007568269968032837 -0.001792735857538728 -> -0.5740351641926531 +atan2pi10080 atan2pi -0.007568269968032837 -0.0017927358575387281 -> -0.5740351641926531 +atan2pi10081 atan2pi -0.007568270433694124 -0.0017927357694134116 -> -0.5740351562904277 +atan2pi10082 atan2pi -0.007568270433694124 -0.0017927358858287334 -> -0.574035160926546 +atan2pi10083 atan2pi -0.007568270433694124 -0.001792735857538728 -> -0.5740351597999263 +atan2pi10084 atan2pi -0.007568270433694124 -0.0017927358575387281 -> -0.5740351597999263 +atan2pi10085 atan2pi -0.0075682704267110625 -0.0017927357694134116 -> -0.574035156356301 +atan2pi10086 atan2pi -0.0075682704267110625 -0.0017927358858287334 -> -0.5740351609924195 +atan2pi10087 atan2pi -0.0075682704267110625 -0.001792735857538728 -> -0.5740351598657996 +atan2pi10088 atan2pi -0.0075682704267110625 -0.0017927358575387281 -> -0.5740351598657996 +atan2pi10089 atan2pi -0.007568270426711063 -0.0017927357694134116 -> -0.574035156356301 +atan2pi10090 atan2pi -0.007568270426711063 -0.0017927358858287334 -> -0.5740351609924195 +atan2pi10091 atan2pi -0.007568270426711063 -0.001792735857538728 -> -0.5740351598657996 +atan2pi10092 atan2pi -0.007568270426711063 -0.0017927358575387281 -> -0.5740351598657996 +atan2pi10093 atan2pi 1.0000001192092896 1.0000001192092896 -> 0.25 +atan2pi10094 atan2pi 1.0000001192092896 1.0 -> 0.25000001897274654 +atan2pi10095 atan2pi 1.0000001192092896 1.0000000000000002 -> 0.25000001897274654 +atan2pi10096 atan2pi 1.0 1.0000001192092896 -> 0.24999998102725343 +atan2pi10097 atan2pi 1.0 1.0 -> 0.25 +atan2pi10098 atan2pi 1.0 1.0000000000000002 -> 0.24999999999999997 +atan2pi10099 atan2pi 1.0000000000000002 1.0000001192092896 -> 0.24999998102725346 +atan2pi10100 atan2pi 1.0000000000000002 1.0 -> 0.25000000000000006 +atan2pi10101 atan2pi 1.0000000000000002 1.0000000000000002 -> 0.25 +atan2pi10102 atan2pi 0.2977333664894104 2.9490208625793457 -> 0.032028064084188616 +atan2pi10103 atan2pi -1.346861771417407e-11 1.9269377760089412e-10 -> -0.022212615773618685 +atan2pi10104 atan2pi 0.3338441550731659 2.132420539855957 -> 0.04943220711796703 +atan2pi10105 atan2pi -0.2600718140602112 1.9549157619476318 -> -0.04209909110779262 +atan2pi10106 atan2pi -11223297.0 -0.9756588935852051 -> -0.50000002767118 +atan2pi10107 atan2pi 0.2590596377849579 7.252630710601807 -> 0.011365007240777592 +atan2pi10108 atan2pi 3.7716573755879195e+20 3.019230078687783e+21 -> 0.03955871588967838 +atan2pi10109 atan2pi -0.41808438301086426 3.5622718334198 -> -0.03718816634303022 +atan2pi10110 atan2pi -0.0 3.4028234663852886e+38 -> -0.0 +atan2pi10111 atan2pi -1.401298464324817e-45 3.4028234663852886e+38 -> -1.3108148544731147e-84 +atan2pi10112 atan2pi -8.028930810726831e-218 3.4028234663852886e+38 -> -7.510492618222167e-257 +atan2pi10113 atan2pi 3.943100929260254 -1.3295949697494507 -> 0.6035214600176284 +atan2pi10114 atan2pi -9.648418597407726e-08 1.6104578133839705e-15 -> -0.49999999468695683 +atan2pi10115 atan2pi -1112287276433408.0 4415830896934912.0 -> -0.07854398572055855 +atan2pi10116 atan2pi 9.642213451703619e-17 9.633302400400708e-17 -> 0.2501471543249841 +atan2pi10117 atan2pi 9.642213451703619e-17 9.633301738656218e-17 -> 0.25014716525787717 +atan2pi10118 atan2pi 9.642213451703619e-17 9.633302010283342e-17 -> 0.2501471607702382 +atan2pi10119 atan2pi 9.642212789959129e-17 9.633302400400708e-17 -> 0.25014714340219485 +atan2pi10120 atan2pi 9.642212789959129e-17 9.633301738656218e-17 -> 0.25014715433508794 +atan2pi10121 atan2pi 9.642212789959129e-17 9.633302010283342e-17 -> 0.25014714984744896 +atan2pi10122 atan2pi 9.642213402663379e-17 9.633302400400708e-17 -> 0.25014715351552336 +atan2pi10123 atan2pi 9.642213402663379e-17 9.633301738656218e-17 -> 0.25014716444841645 +atan2pi10124 atan2pi 9.642213402663379e-17 9.633302010283342e-17 -> 0.25014715996077747 +atan2pi10125 atan2pi 1.401298464324817e-45 1.401298464324817e-45 -> 0.25 +atan2pi10126 atan2pi 1.401298464324817e-45 0.0 -> 0.5 +atan2pi10127 atan2pi 1.401298464324817e-45 2.6248243660802892e-105 -> 0.5 +atan2pi10128 atan2pi 0.0 1.401298464324817e-45 -> 0.0 +atan2pi10129 atan2pi 0.0 0.0 -> 0.0 +atan2pi10130 atan2pi 0.0 2.6248243660802892e-105 -> 0.0 +atan2pi10131 atan2pi 2.841564668406762e-105 1.401298464324817e-45 -> 6.454714318268012e-61 +atan2pi10132 atan2pi 2.841564668406762e-105 0.0 -> 0.5 +atan2pi10133 atan2pi 2.841564668406762e-105 2.6248243660802892e-105 -> 0.262614261697575 +atan2pi10134 atan2pi 1.401298464324817e-45 1.401298464324817e-45 -> 0.25 +atan2pi10135 atan2pi 1.401298464324817e-45 0.0 -> 0.5 +atan2pi10136 atan2pi 1.401298464324817e-45 1.6955960445645656e-177 -> 0.5 +atan2pi10137 atan2pi 0.0 1.401298464324817e-45 -> 0.0 +atan2pi10138 atan2pi 0.0 0.0 -> 0.0 +atan2pi10139 atan2pi 0.0 1.6955960445645656e-177 -> 0.0 +atan2pi10140 atan2pi 1.697410900901164e-177 1.401298464324817e-45 -> 3.855728700404359e-133 +atan2pi10141 atan2pi 1.697410900901164e-177 0.0 -> 0.5 +atan2pi10142 atan2pi 1.697410900901164e-177 1.6955960445645656e-177 -> 0.2501702580242645 +atan2pi10143 atan2pi 1.401298464324817e-45 1.401298464324817e-45 -> 0.25 +atan2pi10144 atan2pi 1.401298464324817e-45 0.0 -> 0.5 +atan2pi10145 atan2pi 1.401298464324817e-45 4.642262744535978e-191 -> 0.5 +atan2pi10146 atan2pi 0.0 1.401298464324817e-45 -> 0.0 +atan2pi10147 atan2pi 0.0 0.0 -> 0.0 +atan2pi10148 atan2pi 0.0 4.642262744535978e-191 -> 0.0 +atan2pi10149 atan2pi 4.6493548767531986e-191 1.401298464324817e-45 -> 1.0561173506747795e-146 +atan2pi10150 atan2pi 4.6493548767531986e-191 0.0 -> 0.5 +atan2pi10151 atan2pi 4.6493548767531986e-191 4.642262744535978e-191 -> 0.2502429604243814 +atan2pi10152 atan2pi 1.401298464324817e-45 1.401298464324817e-45 -> 0.25 +atan2pi10153 atan2pi 1.401298464324817e-45 0.0 -> 0.5 +atan2pi10154 atan2pi 1.401298464324817e-45 5e-324 -> 0.5 +atan2pi10155 atan2pi 0.0 1.401298464324817e-45 -> 0.0 +atan2pi10156 atan2pi 0.0 0.0 -> 0.0 +atan2pi10157 atan2pi 0.0 5e-324 -> 0.0 +atan2pi10158 atan2pi 5e-324 1.401298464324817e-45 -> 1.122287531877252e-279 +atan2pi10159 atan2pi 5e-324 0.0 -> 0.5 +atan2pi10160 atan2pi 5e-324 5e-324 -> 0.25 +atan2pi10161 atan2pi 1.1754943508222875e-38 1.1754943508222875e-38 -> 0.25 +atan2pi10162 atan2pi 1.1754943508222875e-38 2.2250738585072014e-308 -> 0.5 +atan2pi10163 atan2pi 1.1754943508222875e-38 2.004168360008973e-292 -> 0.5 +atan2pi10164 atan2pi 2.2250738585072014e-308 1.1754943508222875e-38 -> 6.025235307651693e-271 +atan2pi10165 atan2pi 2.2250738585072014e-308 2.2250738585072014e-308 -> 0.25 +atan2pi10166 atan2pi 2.2250738585072014e-308 2.004168360008973e-292 -> 3.533949646070574e-17 +atan2pi10167 atan2pi 2.004168360008973e-292 1.1754943508222875e-38 -> 5.427049497271944e-255 +atan2pi10168 atan2pi 2.004168360008973e-292 2.2250738585072014e-308 -> 0.49999999999999994 +atan2pi10169 atan2pi 2.004168360008973e-292 2.004168360008973e-292 -> 0.25 +atan2pi10170 atan2pi 1.1754943508222875e-38 -1.1754943508222875e-38 -> 0.75 +atan2pi10171 atan2pi 1.1754943508222875e-38 -2.2250738585072014e-308 -> 0.5 +atan2pi10172 atan2pi 1.1754943508222875e-38 -2.004168360008973e-292 -> 0.5 +atan2pi10173 atan2pi 2.2250738585072014e-308 -1.1754943508222875e-38 -> 1.0 +atan2pi10174 atan2pi 2.2250738585072014e-308 -2.2250738585072014e-308 -> 0.75 +atan2pi10175 atan2pi 2.2250738585072014e-308 -2.004168360008973e-292 -> 1.0 +atan2pi10176 atan2pi 2.004168360008973e-292 -1.1754943508222875e-38 -> 1.0 +atan2pi10177 atan2pi 2.004168360008973e-292 -2.2250738585072014e-308 -> 0.5 +atan2pi10178 atan2pi 2.004168360008973e-292 -2.004168360008973e-292 -> 0.75 +atan2pi10179 atan2pi -1.1754943508222875e-38 1.1754943508222875e-38 -> -0.25 +atan2pi10180 atan2pi -1.1754943508222875e-38 2.2250738585072014e-308 -> -0.5 +atan2pi10181 atan2pi -1.1754943508222875e-38 2.004168360008973e-292 -> -0.5 +atan2pi10182 atan2pi -2.2250738585072014e-308 1.1754943508222875e-38 -> -6.025235307651693e-271 +atan2pi10183 atan2pi -2.2250738585072014e-308 2.2250738585072014e-308 -> -0.25 +atan2pi10184 atan2pi -2.2250738585072014e-308 2.004168360008973e-292 -> -3.533949646070574e-17 +atan2pi10185 atan2pi -2.004168360008973e-292 1.1754943508222875e-38 -> -5.427049497271944e-255 +atan2pi10186 atan2pi -2.004168360008973e-292 2.2250738585072014e-308 -> -0.49999999999999994 +atan2pi10187 atan2pi -2.004168360008973e-292 2.004168360008973e-292 -> -0.25 +atan2pi10188 atan2pi -1.1754943508222875e-38 -1.1754943508222875e-38 -> -0.75 +atan2pi10189 atan2pi -1.1754943508222875e-38 -2.2250738585072014e-308 -> -0.5 +atan2pi10190 atan2pi -1.1754943508222875e-38 -2.004168360008973e-292 -> -0.5 +atan2pi10191 atan2pi -2.2250738585072014e-308 -1.1754943508222875e-38 -> -1.0 +atan2pi10192 atan2pi -2.2250738585072014e-308 -2.2250738585072014e-308 -> -0.75 +atan2pi10193 atan2pi -2.2250738585072014e-308 -2.004168360008973e-292 -> -1.0 +atan2pi10194 atan2pi -2.004168360008973e-292 -1.1754943508222875e-38 -> -1.0 +atan2pi10195 atan2pi -2.004168360008973e-292 -2.2250738585072014e-308 -> -0.5 +atan2pi10196 atan2pi -2.004168360008973e-292 -2.004168360008973e-292 -> -0.75 +atan2pi10197 atan2pi 1.401298464324817e-45 1.401298464324817e-45 -> 0.25 +atan2pi10198 atan2pi 1.401298464324817e-45 5e-324 -> 0.5 +atan2pi10199 atan2pi 5e-324 1.401298464324817e-45 -> 1.122287531877252e-279 +atan2pi10200 atan2pi 5e-324 5e-324 -> 0.25 +atan2pi10201 atan2pi 1.401298464324817e-45 -1.401298464324817e-45 -> 0.75 +atan2pi10202 atan2pi 1.401298464324817e-45 -5e-324 -> 0.5 +atan2pi10203 atan2pi 5e-324 -1.401298464324817e-45 -> 1.0 +atan2pi10204 atan2pi 5e-324 -5e-324 -> 0.75 +atan2pi10205 atan2pi -1.401298464324817e-45 1.401298464324817e-45 -> -0.25 +atan2pi10206 atan2pi -1.401298464324817e-45 5e-324 -> -0.5 +atan2pi10207 atan2pi -5e-324 1.401298464324817e-45 -> -1.122287531877252e-279 +atan2pi10208 atan2pi -5e-324 5e-324 -> -0.25 +atan2pi10209 atan2pi -1.401298464324817e-45 -1.401298464324817e-45 -> -0.75 +atan2pi10210 atan2pi -1.401298464324817e-45 -5e-324 -> -0.5 +atan2pi10211 atan2pi -5e-324 -1.401298464324817e-45 -> -1.0 +atan2pi10212 atan2pi -5e-324 -5e-324 -> -0.75 +atan2pi10213 atan2pi 1.1754943508222875e-38 1.401298464324817e-45 -> 0.4999999620545046 +atan2pi10214 atan2pi 1.1754943508222875e-38 5e-324 -> 0.5 +atan2pi10215 atan2pi 2.2250738585072014e-308 1.401298464324817e-45 -> 5.054333710364945e-264 +atan2pi10216 atan2pi 2.2250738585072014e-308 5e-324 -> 0.49999999999999994 +atan2pi10217 atan2pi 2.004168360008973e-292 1.401298464324817e-45 -> 4.552539082921141e-248 +atan2pi10218 atan2pi 2.004168360008973e-292 5e-324 -> 0.5 +atan2pi10219 atan2pi 1.1754943508222875e-38 -1.401298464324817e-45 -> 0.5000000379454954 +atan2pi10220 atan2pi 1.1754943508222875e-38 -5e-324 -> 0.5 +atan2pi10221 atan2pi 2.2250738585072014e-308 -1.401298464324817e-45 -> 1.0 +atan2pi10222 atan2pi 2.2250738585072014e-308 -5e-324 -> 0.5000000000000001 +atan2pi10223 atan2pi 2.004168360008973e-292 -1.401298464324817e-45 -> 1.0 +atan2pi10224 atan2pi 2.004168360008973e-292 -5e-324 -> 0.5 +atan2pi10225 atan2pi -1.1754943508222875e-38 1.401298464324817e-45 -> -0.4999999620545046 +atan2pi10226 atan2pi -1.1754943508222875e-38 5e-324 -> -0.5 +atan2pi10227 atan2pi -2.2250738585072014e-308 1.401298464324817e-45 -> -5.054333710364945e-264 +atan2pi10228 atan2pi -2.2250738585072014e-308 5e-324 -> -0.49999999999999994 +atan2pi10229 atan2pi -2.004168360008973e-292 1.401298464324817e-45 -> -4.552539082921141e-248 +atan2pi10230 atan2pi -2.004168360008973e-292 5e-324 -> -0.5 +atan2pi10231 atan2pi -1.1754943508222875e-38 -1.401298464324817e-45 -> -0.5000000379454954 +atan2pi10232 atan2pi -1.1754943508222875e-38 -5e-324 -> -0.5 +atan2pi10233 atan2pi -2.2250738585072014e-308 -1.401298464324817e-45 -> -1.0 +atan2pi10234 atan2pi -2.2250738585072014e-308 -5e-324 -> -0.5000000000000001 +atan2pi10235 atan2pi -2.004168360008973e-292 -1.401298464324817e-45 -> -1.0 +atan2pi10236 atan2pi -2.004168360008973e-292 -5e-324 -> -0.5 +atan2pi10237 atan2pi 1.401298464324817e-45 1.1754943508222875e-38 -> 3.7945495388959543e-08 +atan2pi10238 atan2pi 1.401298464324817e-45 2.2250738585072014e-308 -> 0.5 +atan2pi10239 atan2pi 1.401298464324817e-45 2.004168360008973e-292 -> 0.5 +atan2pi10240 atan2pi 5e-324 1.1754943508222875e-38 -> 1.3378709934678696e-286 +atan2pi10241 atan2pi 5e-324 2.2250738585072014e-308 -> 7.067899292141149e-17 +atan2pi10242 atan2pi 5e-324 2.004168360008973e-292 -> 7.846944529866949e-33 +atan2pi10243 atan2pi 1.401298464324817e-45 -1.1754943508222875e-38 -> 0.9999999620545046 +atan2pi10244 atan2pi 1.401298464324817e-45 -2.2250738585072014e-308 -> 0.5 +atan2pi10245 atan2pi 1.401298464324817e-45 -2.004168360008973e-292 -> 0.5 +atan2pi10246 atan2pi 5e-324 -1.1754943508222875e-38 -> 1.0 +atan2pi10247 atan2pi 5e-324 -2.2250738585072014e-308 -> 0.9999999999999999 +atan2pi10248 atan2pi 5e-324 -2.004168360008973e-292 -> 1.0 +atan2pi10249 atan2pi -1.401298464324817e-45 1.1754943508222875e-38 -> -3.7945495388959543e-08 +atan2pi10250 atan2pi -1.401298464324817e-45 2.2250738585072014e-308 -> -0.5 +atan2pi10251 atan2pi -1.401298464324817e-45 2.004168360008973e-292 -> -0.5 +atan2pi10252 atan2pi -5e-324 1.1754943508222875e-38 -> -1.3378709934678696e-286 +atan2pi10253 atan2pi -5e-324 2.2250738585072014e-308 -> -7.067899292141149e-17 +atan2pi10254 atan2pi -5e-324 2.004168360008973e-292 -> -7.846944529866949e-33 +atan2pi10255 atan2pi -1.401298464324817e-45 -1.1754943508222875e-38 -> -0.9999999620545046 +atan2pi10256 atan2pi -1.401298464324817e-45 -2.2250738585072014e-308 -> -0.5 +atan2pi10257 atan2pi -1.401298464324817e-45 -2.004168360008973e-292 -> -0.5 +atan2pi10258 atan2pi -5e-324 -1.1754943508222875e-38 -> -1.0 +atan2pi10259 atan2pi -5e-324 -2.2250738585072014e-308 -> -0.9999999999999999 +atan2pi10260 atan2pi -5e-324 -2.004168360008973e-292 -> -1.0 +atan2pi10261 atan2pi 1.0 -3.4028234663852886e+38 -> 1.0 +atan2pi10262 atan2pi 1.0 -1.7976931348623157e+308 -> 1.0 +atan2pi10263 atan2pi -1.0 -3.4028234663852886e+38 -> -1.0 +atan2pi10264 atan2pi -1.0 -1.7976931348623157e+308 -> -1.0 +atan2pi10265 atan2pi 1.1754943508222875e-38 -3.4028234663852886e+38 -> 1.0 +atan2pi10266 atan2pi 1.1754943508222875e-38 -1.7976931348623157e+308 -> 1.0 +atan2pi10267 atan2pi 2.2250738585072014e-308 -3.4028234663852886e+38 -> 1.0 +atan2pi10268 atan2pi 2.2250738585072014e-308 -1.7976931348623157e+308 -> 1.0 +atan2pi10269 atan2pi 2.004168360008973e-292 -3.4028234663852886e+38 -> 1.0 +atan2pi10270 atan2pi 2.004168360008973e-292 -1.7976931348623157e+308 -> 1.0 +atan2pi10271 atan2pi -1.1754943508222875e-38 -3.4028234663852886e+38 -> -1.0 +atan2pi10272 atan2pi -1.1754943508222875e-38 -1.7976931348623157e+308 -> -1.0 +atan2pi10273 atan2pi -2.2250738585072014e-308 -3.4028234663852886e+38 -> -1.0 +atan2pi10274 atan2pi -2.2250738585072014e-308 -1.7976931348623157e+308 -> -1.0 +atan2pi10275 atan2pi -2.004168360008973e-292 -3.4028234663852886e+38 -> -1.0 +atan2pi10276 atan2pi -2.004168360008973e-292 -1.7976931348623157e+308 -> -1.0 +atan2pi10277 atan2pi 1.401298464324817e-45 -3.4028234663852886e+38 -> 1.0 +atan2pi10278 atan2pi 1.401298464324817e-45 -1.7976931348623157e+308 -> 1.0 +atan2pi10279 atan2pi 5e-324 -3.4028234663852886e+38 -> 1.0 +atan2pi10280 atan2pi 5e-324 -1.7976931348623157e+308 -> 1.0 +atan2pi10281 atan2pi -1.401298464324817e-45 -3.4028234663852886e+38 -> -1.0 +atan2pi10282 atan2pi -1.401298464324817e-45 -1.7976931348623157e+308 -> -1.0 +atan2pi10283 atan2pi -5e-324 -3.4028234663852886e+38 -> -1.0 +atan2pi10284 atan2pi -5e-324 -1.7976931348623157e+308 -> -1.0 +atan2pi10285 atan2pi 1.0 3.4028234663852886e+38 -> 9.354287383057258e-40 +atan2pi10286 atan2pi 1.0 1.7976931348623157e+308 -> 1.770657516629887e-309 +atan2pi10287 atan2pi -1.0 3.4028234663852886e+38 -> -9.354287383057258e-40 +atan2pi10288 atan2pi -1.0 1.7976931348623157e+308 -> -1.770657516629887e-309 +atan2pi10289 atan2pi 1.1754943508222875e-38 3.4028234663852886e+38 -> 1.0995911974752006e-77 +atan2pi10290 atan2pi 1.1754943508222875e-38 1.7976931348623157e+308 -> 0.0 +atan2pi10291 atan2pi 2.2250738585072014e-308 3.4028234663852886e+38 -> 0.0 +atan2pi10292 atan2pi 2.2250738585072014e-308 1.7976931348623157e+308 -> 0.0 +atan2pi10293 atan2pi 2.004168360008973e-292 3.4028234663852886e+38 -> 0.0 +atan2pi10294 atan2pi 2.004168360008973e-292 1.7976931348623157e+308 -> 0.0 +atan2pi10295 atan2pi -1.1754943508222875e-38 3.4028234663852886e+38 -> -1.0995911974752006e-77 +atan2pi10296 atan2pi -1.1754943508222875e-38 1.7976931348623157e+308 -> -0.0 +atan2pi10297 atan2pi -2.2250738585072014e-308 3.4028234663852886e+38 -> -0.0 +atan2pi10298 atan2pi -2.2250738585072014e-308 1.7976931348623157e+308 -> -0.0 +atan2pi10299 atan2pi -2.004168360008973e-292 3.4028234663852886e+38 -> -0.0 +atan2pi10300 atan2pi -2.004168360008973e-292 1.7976931348623157e+308 -> -0.0 +atan2pi10301 atan2pi 1.401298464324817e-45 3.4028234663852886e+38 -> 1.3108148544731147e-84 +atan2pi10302 atan2pi 1.401298464324817e-45 1.7976931348623157e+308 -> 0.0 +atan2pi10303 atan2pi 5e-324 3.4028234663852886e+38 -> 0.0 +atan2pi10304 atan2pi 5e-324 1.7976931348623157e+308 -> 0.0 +atan2pi10305 atan2pi -1.401298464324817e-45 3.4028234663852886e+38 -> -1.3108148544731147e-84 +atan2pi10306 atan2pi -1.401298464324817e-45 1.7976931348623157e+308 -> -0.0 +atan2pi10307 atan2pi -5e-324 3.4028234663852886e+38 -> -0.0 +atan2pi10308 atan2pi -5e-324 1.7976931348623157e+308 -> -0.0 +atan2pi10309 atan2pi 1.1754943508222875e-38 1.0 -> 3.7417147301993126e-39 +atan2pi10310 atan2pi 2.2250738585072014e-308 1.0 -> 7.082630066519554e-309 +atan2pi10311 atan2pi 2.004168360008973e-292 1.0 -> 6.379466025676106e-293 +atan2pi10312 atan2pi -1.1754943508222875e-38 1.0 -> -3.7417147301993126e-39 +atan2pi10313 atan2pi -2.2250738585072014e-308 1.0 -> -7.082630066519554e-309 +atan2pi10314 atan2pi -2.004168360008973e-292 1.0 -> -6.379466025676106e-293 +atan2pi10315 atan2pi 1.401298464324817e-45 1.0 -> 4.460471546887532e-46 +atan2pi10316 atan2pi 5e-324 1.0 -> 0.0 +atan2pi10317 atan2pi -1.401298464324817e-45 1.0 -> -4.460471546887532e-46 +atan2pi10318 atan2pi -5e-324 1.0 -> -0.0 +atan2pi10319 atan2pi 1.1754943508222875e-38 -1.0 -> 1.0 +atan2pi10320 atan2pi 2.2250738585072014e-308 -1.0 -> 1.0 +atan2pi10321 atan2pi 2.004168360008973e-292 -1.0 -> 1.0 +atan2pi10322 atan2pi -1.1754943508222875e-38 -1.0 -> -1.0 +atan2pi10323 atan2pi -2.2250738585072014e-308 -1.0 -> -1.0 +atan2pi10324 atan2pi -2.004168360008973e-292 -1.0 -> -1.0 +atan2pi10325 atan2pi 1.401298464324817e-45 -1.0 -> 1.0 +atan2pi10326 atan2pi 5e-324 -1.0 -> 1.0 +atan2pi10327 atan2pi -1.401298464324817e-45 -1.0 -> -1.0 +atan2pi10328 atan2pi -5e-324 -1.0 -> -1.0 +atan2pi10329 atan2pi -4.3609299609670416e-05 0.05748599022626877 -> -0.0002414722000586593 +atan2pi10330 atan2pi -3.4028234663852886e+38 3.4028234663852886e+38 -> -0.25 +atan2pi10331 atan2pi -3.4028234663852886e+38 2.8061581043948965e+103 -> -3.8599120576714543e-66 +atan2pi10332 atan2pi -9.822050856552661e+114 3.4028234663852886e+38 -> -0.5 +atan2pi10333 atan2pi -9.822050856552661e+114 2.8061581043948965e+103 -> -0.4999999999990906 +atan2pi10334 atan2pi 3.4028234663852886e+38 3.4028234663852886e+38 -> 0.25 +atan2pi10335 atan2pi 3.4028234663852886e+38 1.7976931348623157e+308 -> 6.025234948519683e-271 +atan2pi10336 atan2pi 2.962077767429005e+64 3.4028234663852886e+38 -> 0.5 +atan2pi10337 atan2pi 2.962077767429005e+64 1.7976931348623157e+308 -> 5.244825263740446e-245 +atan2pi10338 atan2pi 2.9620777674290047e+64 3.4028234663852886e+38 -> 0.5 +atan2pi10339 atan2pi 2.9620777674290047e+64 1.7976931348623157e+308 -> 5.244825263740445e-245 +atan2pi10340 atan2pi -3.4028234663852886e+38 3.4028234663852886e+38 -> -0.25 +atan2pi10341 atan2pi -3.4028234663852886e+38 1.7976931348623157e+308 -> -6.025234948519683e-271 +atan2pi10342 atan2pi -1.7976931348623157e+308 3.4028234663852886e+38 -> -0.5 +atan2pi10343 atan2pi -1.7976931348623157e+308 1.7976931348623157e+308 -> -0.25 + +atan2pi20000 atan2pi inf 0 -> 0.5 +atan2pi20001 atan2pi -inf 0 -> -0.5 +atan2pi20002 atan2pi nan 0 -> nan + +atan2pi20000 atan2pi inf -0 -> 0.5 +atan2pi20001 atan2pi -inf -0 -> -0.5 +atan2pi20002 atan2pi nan -0 -> nan + +atan2pi20003 atan2pi inf 1 -> 0.5 +atan2pi20004 atan2pi -inf 1 -> -0.5 +atan2pi20005 atan2pi nan 1 -> nan + +atan2pi20006 atan2pi inf -1 -> 0.5 +atan2pi20007 atan2pi -inf -1 -> -0.5 +atan2pi20008 atan2pi nan -1 -> nan + +atan2pi20009 atan2pi inf inf -> 0.25 +atan2pi20010 atan2pi -inf inf -> -0.25 +atan2pi20011 atan2pi nan inf -> nan + +atan2pi20012 atan2pi inf -inf -> .75 +atan2pi20013 atan2pi -inf -inf -> -.75 +atan2pi20014 atan2pi nan -inf -> nan + +atan2pi20015 atan2pi inf nan -> nan +atan2pi20016 atan2pi -inf nan -> nan +atan2pi20017 atan2pi nan nan -> nan + +atan2pi30000 atan2pi 0 inf -> 0.0 +atan2pi30001 atan2pi 0 -inf -> 1.0 +atan2pi30002 atan2pi 0 nan -> nan + +atan2pi30000 atan2pi -0 inf -> -0.0 +atan2pi30001 atan2pi -0 -inf -> -1.0 +atan2pi30002 atan2pi -0 nan -> nan + +atan2pi30003 atan2pi 1 inf -> 0.0 +atan2pi30004 atan2pi 1 -inf -> 1.0 +atan2pi30005 atan2pi 1 nan -> nan + +atan2pi30006 atan2pi -1 inf -> -0.0 +atan2pi30007 atan2pi -1 -inf -> -1.0 +atan2pi30008 atan2pi -1 nan -> nan + +----------- +-- cospi -- +----------- + +cospi10000 cospi 0.0 -> 1.0 +cospi10001 cospi -0.0 -> 1.0 +cospi10002 cospi 1.1754943508222875e-38 -> 1.0 +cospi10003 cospi 2.2250738585072014e-308 -> 1.0 +cospi10004 cospi 2.004168360008973e-292 -> 1.0 +cospi10005 cospi -1.1754943508222875e-38 -> 1.0 +cospi10006 cospi -2.2250738585072014e-308 -> 1.0 +cospi10007 cospi -2.004168360008973e-292 -> 1.0 +cospi10008 cospi 1.401298464324817e-45 -> 1.0 +cospi10009 cospi 5e-324 -> 1.0 +cospi10010 cospi -1.401298464324817e-45 -> 1.0 +cospi10011 cospi -5e-324 -> 1.0 +cospi10012 cospi 3.4028234663852886e+38 -> 1.0 +cospi10013 cospi 1.7976931348623157e+308 -> 1.0 +cospi10014 cospi -3.4028234663852886e+38 -> 1.0 +cospi10015 cospi -1.7976931348623157e+308 -> 1.0 +cospi10016 cospi 1.0 -> -1.0 +cospi10017 cospi -1.0 -> -1.0 +cospi10018 cospi 2.0 -> 1.0 +cospi10019 cospi -2.0 -> 1.0 +cospi10020 cospi 3.0 -> -1.0 +cospi10021 cospi -3.0 -> -1.0 +cospi10022 cospi 0.10000000149011612 -> 0.9510565148485406 +cospi10023 cospi 0.09999999403953552 -> 0.9510565220816052 +cospi10024 cospi 0.1 -> 0.9510565162951535 +cospi10025 cospi 0.09999999999999999 -> 0.9510565162951535 +cospi10026 cospi -0.09999999403953552 -> 0.9510565220816052 +cospi10027 cospi -0.10000000149011612 -> 0.9510565148485406 +cospi10028 cospi -0.09999999999999999 -> 0.9510565162951535 +cospi10029 cospi -0.1 -> 0.9510565162951535 +cospi10030 cospi 0.20000000298023224 -> 0.8090169888717047 +cospi10031 cospi 0.19999998807907104 -> 0.8090170163879177 +cospi10032 cospi 0.2 -> 0.8090169943749475 +cospi10033 cospi 0.19999999999999998 -> 0.8090169943749475 +cospi10034 cospi -0.19999998807907104 -> 0.8090170163879177 +cospi10035 cospi -0.20000000298023224 -> 0.8090169888717047 +cospi10036 cospi -0.19999999999999998 -> 0.8090169943749475 +cospi10037 cospi -0.2 -> 0.8090169943749475 +cospi10038 cospi 0.30000001192092896 -> 0.5877852219942177 +cospi10039 cospi 0.29999998211860657 -> 0.5877852977398548 +cospi10040 cospi 0.30000000000000004 -> 0.587785252292473 +cospi10041 cospi 0.3 -> 0.5877852522924731 +cospi10042 cospi -0.29999998211860657 -> 0.5877852977398548 +cospi10043 cospi -0.30000001192092896 -> 0.5877852219942177 +cospi10044 cospi -0.3 -> 0.5877852522924731 +cospi10045 cospi -0.30000000000000004 -> 0.587785252292473 +cospi10046 cospi 0.4000000059604645 -> 0.3090169765660799 +cospi10047 cospi 0.3999999761581421 -> 0.3090170656104165 +cospi10048 cospi 0.4 -> 0.30901699437494734 +cospi10049 cospi 0.39999999999999997 -> 0.3090169943749475 +cospi10050 cospi -0.3999999761581421 -> 0.3090170656104165 +cospi10051 cospi -0.4000000059604645 -> 0.3090169765660799 +cospi10052 cospi -0.39999999999999997 -> 0.3090169943749475 +cospi10053 cospi -0.4 -> 0.30901699437494734 +cospi10054 cospi 0.5 -> 0.0 +cospi10055 cospi -0.5 -> 0.0 +cospi10056 cospi 0.6000000238418579 -> -0.3090170656104165 +cospi10057 cospi 0.5999999642372131 -> -0.30901688752174056 +cospi10058 cospi 0.6000000000000001 -> -0.3090169943749477 +cospi10059 cospi 0.6 -> -0.30901699437494734 +cospi10060 cospi -0.5999999642372131 -> -0.30901688752174056 +cospi10061 cospi -0.6000000238418579 -> -0.3090170656104165 +cospi10062 cospi -0.6 -> -0.30901699437494734 +cospi10063 cospi -0.6000000000000001 -> -0.3090169943749477 +cospi10064 cospi 0.7000000476837158 -> -0.5877853734854867 +cospi10065 cospi 0.699999988079071 -> -0.5877852219942177 +cospi10066 cospi 0.7000000000000001 -> -0.5877852522924732 +cospi10067 cospi 0.7 -> -0.587785252292473 +cospi10068 cospi -0.699999988079071 -> -0.5877852219942177 +cospi10069 cospi -0.7000000476837158 -> -0.5877853734854867 +cospi10070 cospi -0.7 -> -0.587785252292473 +cospi10071 cospi -0.7000000000000001 -> -0.5877852522924732 +cospi10072 cospi 0.800000011920929 -> -0.8090170163879177 +cospi10073 cospi 0.7999999523162842 -> -0.8090169063230551 +cospi10074 cospi 0.8 -> -0.8090169943749475 +cospi10075 cospi 0.7999999999999999 -> -0.8090169943749473 +cospi10076 cospi -0.7999999523162842 -> -0.8090169063230551 +cospi10077 cospi -0.800000011920929 -> -0.8090170163879177 +cospi10078 cospi -0.7999999999999999 -> -0.8090169943749473 +cospi10079 cospi -0.8 -> -0.8090169943749475 +cospi10080 cospi 0.9000000357627869 -> -0.9510565510138584 +cospi10081 cospi 0.8999999761581421 -> -0.9510564931493437 +cospi10082 cospi 0.9 -> -0.9510565162951536 +cospi10083 cospi 0.8999999999999999 -> -0.9510565162951535 +cospi10084 cospi -0.8999999761581421 -> -0.9510564931493437 +cospi10085 cospi -0.9000000357627869 -> -0.9510565510138584 +cospi10086 cospi -0.8999999999999999 -> -0.9510565162951535 +cospi10087 cospi -0.9 -> -0.9510565162951536 +cospi10088 cospi 1.100000023841858 -> -0.9510564931493437 +cospi10089 cospi 1.0999999046325684 -> -0.9510566088783399 +cospi10090 cospi 1.1 -> -0.9510565162951535 +cospi10091 cospi 1.0999999999999999 -> -0.9510565162951538 +cospi10092 cospi -1.0999999046325684 -> -0.9510566088783399 +cospi10093 cospi -1.100000023841858 -> -0.9510564931493437 +cospi10094 cospi -1.0999999999999999 -> -0.9510565162951538 +cospi10095 cospi -1.1 -> -0.9510565162951535 +cospi10096 cospi 1.2000000476837158 -> -0.8090169063230551 +cospi10097 cospi 1.1999999284744263 -> -0.8090171264527518 +cospi10098 cospi 1.2000000000000002 -> -0.8090169943749471 +cospi10099 cospi 1.2 -> -0.8090169943749475 +cospi10100 cospi -1.1999999284744263 -> -0.8090171264527518 +cospi10101 cospi -1.2000000476837158 -> -0.8090169063230551 +cospi10102 cospi -1.2 -> -0.8090169943749475 +cospi10103 cospi -1.2000000000000002 -> -0.8090169943749471 +cospi10104 cospi 1.3000000715255737 -> -0.587785070502928 +cospi10105 cospi 1.2999999523162842 -> -0.5877853734854867 +cospi10106 cospi 1.3 -> -0.587785252292473 +cospi10107 cospi 1.2999999999999998 -> -0.5877852522924736 +cospi10108 cospi -1.2999999523162842 -> -0.5877853734854867 +cospi10109 cospi -1.3000000715255737 -> -0.587785070502928 +cospi10110 cospi -1.2999999999999998 -> -0.5877852522924736 +cospi10111 cospi -1.3 -> -0.587785252292473 +cospi10112 cospi 1.4000000953674316 -> -0.3090167094330538 +cospi10113 cospi 1.399999976158142 -> -0.3090170656104165 +cospi10114 cospi 1.4000000000000001 -> -0.309016994374947 +cospi10115 cospi 1.4 -> -0.3090169943749477 +cospi10116 cospi -1.399999976158142 -> -0.3090170656104165 +cospi10117 cospi -1.4000000953674316 -> -0.3090167094330538 +cospi10118 cospi -1.4 -> -0.3090169943749477 +cospi10119 cospi -1.4000000000000001 -> -0.309016994374947 +cospi10120 cospi 1.5 -> 0.0 +cospi10121 cospi -1.5 -> 0.0 +cospi10122 cospi 2.5 -> 0.0 +cospi10123 cospi -2.5 -> 0.0 +cospi10124 cospi 3.5 -> 0.0 +cospi10125 cospi -3.5 -> 0.0 +cospi10126 cospi 4.5 -> 0.0 +cospi10127 cospi -4.5 -> 0.0 +cospi10128 cospi 0.24000000953674316 -> 0.728968606911995 +cospi10129 cospi 0.23999999463558197 -> 0.728968638957958 +cospi10130 cospi 0.24000000000000002 -> 0.7289686274214114 +cospi10131 cospi 0.24 -> 0.7289686274214116 +cospi10132 cospi -0.23999999463558197 -> 0.728968638957958 +cospi10133 cospi -0.24000000953674316 -> 0.728968606911995 +cospi10134 cospi -0.24 -> 0.7289686274214116 +cospi10135 cospi -0.24000000000000002 -> 0.7289686274214114 +cospi10136 cospi 0.49000000953674316 -> 0.03141072913234975 +cospi10137 cospi 0.4899999797344208 -> 0.03141082271290761 +cospi10138 cospi 0.49000000000000005 -> 0.031410759078128146 +cospi10139 cospi 0.49 -> 0.03141075907812832 +cospi10140 cospi -0.4899999797344208 -> 0.03141082271290761 +cospi10141 cospi -0.49000000953674316 -> 0.03141072913234975 +cospi10142 cospi -0.49 -> 0.03141075907812832 +cospi10143 cospi -0.49000000000000005 -> 0.031410759078128146 +cospi10144 cospi 0.5100000500679016 -> -0.031410916293465214 +cospi10145 cospi 0.5099999904632568 -> -0.03141072913234975 +cospi10146 cospi 0.51 -> -0.03141075907812832 +cospi10147 cospi 0.5099999999999999 -> -0.03141075907812797 +cospi10148 cospi -0.5099999904632568 -> -0.03141072913234975 +cospi10149 cospi -0.5100000500679016 -> -0.031410916293465214 +cospi10150 cospi -0.5099999999999999 -> -0.03141075907812797 +cospi10151 cospi -0.51 -> -0.03141075907812832 +cospi10152 cospi 0.7600000500679016 -> -0.7289687350958375 +cospi10153 cospi 0.7599999904632568 -> -0.728968606911995 +cospi10154 cospi 0.76 -> -0.7289686274214116 +cospi10155 cospi 0.7599999999999999 -> -0.7289686274214113 +cospi10156 cospi -0.7599999904632568 -> -0.728968606911995 +cospi10157 cospi -0.7600000500679016 -> -0.7289687350958375 +cospi10158 cospi -0.7599999999999999 -> -0.7289686274214113 +cospi10159 cospi -0.76 -> -0.7289686274214116 +cospi10160 cospi 1.2400000095367432 -> -0.728968606911995 +cospi10161 cospi 1.2399998903274536 -> -0.7289688632796544 +cospi10162 cospi 1.2400000000000002 -> -0.7289686274214111 +cospi10163 cospi 1.24 -> -0.7289686274214116 +cospi10164 cospi -1.2399998903274536 -> -0.7289688632796544 +cospi10165 cospi -1.2400000095367432 -> -0.728968606911995 +cospi10166 cospi -1.24 -> -0.7289686274214116 +cospi10167 cospi -1.2400000000000002 -> -0.7289686274214111 +cospi10168 cospi 1.4900000095367432 -> -0.03141072913234975 +cospi10169 cospi 1.4899998903274536 -> -0.03141110345457958 +cospi10170 cospi 1.4900000000000002 -> -0.031410759078127626 +cospi10171 cospi 1.49 -> -0.03141075907812832 +cospi10172 cospi -1.4899998903274536 -> -0.03141110345457958 +cospi10173 cospi -1.4900000095367432 -> -0.03141072913234975 +cospi10174 cospi -1.49 -> -0.03141075907812832 +cospi10175 cospi -1.4900000000000002 -> -0.031410759078127626 +cospi10176 cospi 1.5100001096725464 -> 0.03141110345457958 +cospi10177 cospi 1.5099999904632568 -> 0.03141072913234975 +cospi10178 cospi 1.51 -> 0.03141075907812832 +cospi10179 cospi 1.5099999999999998 -> 0.031410759078127626 +cospi10180 cospi -1.5099999904632568 -> 0.03141072913234975 +cospi10181 cospi -1.5100001096725464 -> 0.03141110345457958 +cospi10182 cospi -1.5099999999999998 -> 0.031410759078127626 +cospi10183 cospi -1.51 -> 0.03141075907812832 +cospi10184 cospi 1.7600001096725464 -> 0.7289688632796544 +cospi10185 cospi 1.7599999904632568 -> 0.728968606911995 +cospi10186 cospi 1.76 -> 0.7289686274214116 +cospi10187 cospi 1.7599999999999998 -> 0.7289686274214111 +cospi10188 cospi -1.7599999904632568 -> 0.728968606911995 +cospi10189 cospi -1.7600001096725464 -> 0.7289688632796544 +cospi10190 cospi -1.7599999999999998 -> 0.7289686274214111 +cospi10191 cospi -1.76 -> 0.7289686274214116 +cospi10192 cospi 100.24000549316406 -> 0.7289568138891386 +cospi10193 cospi 100.23999786376953 -> 0.7289732215142216 +cospi10194 cospi 100.24000000000001 -> 0.728968627421392 +cospi10195 cospi 100.24 -> 0.7289686274214225 +cospi10196 cospi -100.23999786376953 -> 0.7289732215142216 +cospi10197 cospi -100.24000549316406 -> 0.7289568138891386 +cospi10198 cospi -100.24 -> 0.7289686274214225 +cospi10199 cospi -100.24000000000001 -> 0.728968627421392 +cospi10200 cospi 100.49000549316406 -> 0.031393510305016 +cospi10201 cospi 100.48999786376953 -> 0.031417466931812604 +cospi10202 cospi 100.49000000000001 -> 0.03141075907809974 +cospi10203 cospi 100.49 -> 0.031410759078144355 +cospi10204 cospi -100.48999786376953 -> 0.031417466931812604 +cospi10205 cospi -100.49000549316406 -> 0.031393510305016 +cospi10206 cospi -100.49 -> 0.031410759078144355 +cospi10207 cospi -100.49000000000001 -> 0.03141075907809974 +cospi10208 cospi 100.51000213623047 -> -0.031417466931812604 +cospi10209 cospi 100.50999450683594 -> -0.031393510305016 +cospi10210 cospi 100.51 -> -0.031410759078144355 +cospi10211 cospi 100.50999999999999 -> -0.03141075907809974 +cospi10212 cospi -100.50999450683594 -> -0.031393510305016 +cospi10213 cospi -100.51000213623047 -> -0.031417466931812604 +cospi10214 cospi -100.50999999999999 -> -0.03141075907809974 +cospi10215 cospi -100.51 -> -0.031410759078144355 +cospi10216 cospi 100.76000213623047 -> -0.7289732215142216 +cospi10217 cospi 100.75999450683594 -> -0.7289568138891386 +cospi10218 cospi 100.76 -> -0.7289686274214225 +cospi10219 cospi 100.75999999999999 -> -0.728968627421392 +cospi10220 cospi -100.75999450683594 -> -0.7289568138891386 +cospi10221 cospi -100.76000213623047 -> -0.7289732215142216 +cospi10222 cospi -100.75999999999999 -> -0.728968627421392 +cospi10223 cospi -100.76 -> -0.7289686274214225 +cospi10224 cospi 1234.56005859375 -> -0.1875621285825296 +cospi10225 cospi 1234.5599365234375 -> -0.18718542559099033 +cospi10226 cospi 1234.5600000000002 -> -0.1873813145862579 +cospi10227 cospi 1234.56 -> -0.18738131458555624 +cospi10228 cospi -1234.5599365234375 -> -0.18718542559099033 +cospi10229 cospi -1234.56005859375 -> -0.1875621285825296 +cospi10230 cospi -1234.56 -> -0.18738131458555624 +cospi10231 cospi -1234.5600000000002 -> -0.1873813145862579 +cospi10232 cospi 2097151.375 -> -0.3826834323650898 +cospi10233 cospi -2097151.375 -> -0.3826834323650898 +cospi10234 cospi 2097151.625 -> 0.3826834323650898 +cospi10235 cospi -2097151.625 -> 0.3826834323650898 +cospi10236 cospi 1125899906842624.0 -> 1.0 +cospi10237 cospi 1125899839733760.0 -> 1.0 +cospi10238 cospi 1125899906842623.4 -> -0.3826834323650898 +cospi10239 cospi -1125899839733760.0 -> 1.0 +cospi10240 cospi -1125899906842624.0 -> 1.0 +cospi10241 cospi -1125899906842623.4 -> -0.3826834323650898 +cospi10242 cospi 1125899906842624.0 -> 1.0 +cospi10243 cospi 1125899839733760.0 -> 1.0 +cospi10244 cospi 1125899906842623.6 -> 0.3826834323650898 +cospi10245 cospi -1125899839733760.0 -> 1.0 +cospi10246 cospi -1125899906842624.0 -> 1.0 +cospi10247 cospi -1125899906842623.6 -> 0.3826834323650898 +cospi10248 cospi 2.305843009213694e+18 -> 1.0 +cospi10249 cospi 2.3058428717747405e+18 -> 1.0 +cospi10250 cospi 2.3058430092136937e+18 -> 1.0 +cospi10251 cospi -2.3058428717747405e+18 -> 1.0 +cospi10252 cospi -2.305843009213694e+18 -> 1.0 +cospi10253 cospi -2.3058430092136937e+18 -> 1.0 +cospi10254 cospi 2.305843009213694e+18 -> 1.0 +cospi10255 cospi 2.3058428717747405e+18 -> 1.0 +cospi10256 cospi 2.3058430092136937e+18 -> 1.0 +cospi10257 cospi -2.3058428717747405e+18 -> 1.0 +cospi10258 cospi -2.305843009213694e+18 -> 1.0 +cospi10259 cospi -2.3058430092136937e+18 -> 1.0 +cospi10260 cospi 1.298074214633707e+33 -> 1.0 +cospi10261 cospi 1.2980741372624545e+33 -> 1.0 +cospi10262 cospi 1.2980742146337068e+33 -> 1.0 +cospi10263 cospi -1.2980741372624545e+33 -> 1.0 +cospi10264 cospi -1.298074214633707e+33 -> 1.0 +cospi10265 cospi -1.2980742146337068e+33 -> 1.0 +cospi10266 cospi 1.298074214633707e+33 -> 1.0 +cospi10267 cospi 1.2980741372624545e+33 -> 1.0 +cospi10268 cospi 1.2980742146337068e+33 -> 1.0 +cospi10269 cospi -1.2980741372624545e+33 -> 1.0 +cospi10270 cospi -1.298074214633707e+33 -> 1.0 +cospi10271 cospi -1.2980742146337068e+33 -> 1.0 +cospi10272 cospi 0.419987291097641 -> 0.24872855880609973 +cospi10273 cospi -70.50994110107422 -> -0.031225813413226215 +cospi10274 cospi -70.50994873046875 -> -0.031249770165979865 +cospi10275 cospi -70.50994733870746 -> -0.03124539995406979 +cospi10276 cospi 0.4801006019115448 -> 0.06247508982524335 +cospi10277 cospi 0.4801005721092224 -> 0.06247518326910248 +cospi10278 cospi 0.48010059600345173 -> 0.062475108349807 +cospi10279 cospi 0.4801005960034517 -> 0.06247510834980717 +cospi10280 cospi -629430784.0 -> 1.0 +cospi10281 cospi -629430848.0 -> 1.0 +cospi10282 cospi -629430785.4800935 -> -0.06249742289199851 +cospi10283 cospi -629430785.4800936 -> -0.06249704911708015 + +cospi20001 cospi inf -> nan invalid +cospi20002 cospi -inf -> nan invalid +cospi20003 cospi nan -> nan + +----------- +-- sinpi -- +----------- + +sinpi10000 sinpi 0.0 -> 0.0 +sinpi10001 sinpi -0.0 -> -0.0 +sinpi10002 sinpi 1.1754943508222875e-38 -> 3.6929244168796014e-38 +sinpi10003 sinpi 2.2250738585072014e-308 -> 6.990275687580919e-308 +sinpi10004 sinpi 2.004168360008973e-292 -> 6.296280596361293e-292 +sinpi10005 sinpi -1.1754943508222875e-38 -> -3.6929244168796014e-38 +sinpi10006 sinpi -2.2250738585072014e-308 -> -6.990275687580919e-308 +sinpi10007 sinpi -2.004168360008973e-292 -> -6.296280596361293e-292 +sinpi10008 sinpi 1.401298464324817e-45 -> 4.402308961009504e-45 +sinpi10009 sinpi 5e-324 -> 1.5e-323 +sinpi10010 sinpi -1.401298464324817e-45 -> -4.402308961009504e-45 +sinpi10011 sinpi -5e-324 -> -1.5e-323 +sinpi10012 sinpi 3.4028234663852886e+38 -> 0.0 +sinpi10013 sinpi 1.7976931348623157e+308 -> 0.0 +sinpi10014 sinpi -3.4028234663852886e+38 -> -0.0 +sinpi10015 sinpi -1.7976931348623157e+308 -> -0.0 +sinpi10016 sinpi 1.0 -> 0.0 +sinpi10017 sinpi -1.0 -> -0.0 +sinpi10018 sinpi 2.0 -> 0.0 +sinpi10019 sinpi -2.0 -> -0.0 +sinpi10020 sinpi 3.0 -> 0.0 +sinpi10021 sinpi -3.0 -> -0.0 +sinpi10022 sinpi 0.10000000149011612 -> 0.30901699882716427 +sinpi10023 sinpi 0.09999999403953552 -> 0.3090169765660799 +sinpi10024 sinpi 0.1 -> 0.30901699437494745 +sinpi10025 sinpi 0.09999999999999999 -> 0.3090169943749474 +sinpi10026 sinpi -0.09999999403953552 -> -0.3090169765660799 +sinpi10027 sinpi -0.10000000149011612 -> -0.30901699882716427 +sinpi10028 sinpi -0.09999999999999999 -> -0.3090169943749474 +sinpi10029 sinpi -0.1 -> -0.30901699437494745 +sinpi10030 sinpi 0.20000000298023224 -> 0.5877852598670369 +sinpi10031 sinpi 0.19999998807907104 -> 0.5877852219942177 +sinpi10032 sinpi 0.2 -> 0.5877852522924731 +sinpi10033 sinpi 0.19999999999999998 -> 0.5877852522924731 +sinpi10034 sinpi -0.19999998807907104 -> -0.5877852219942177 +sinpi10035 sinpi -0.20000000298023224 -> -0.5877852598670369 +sinpi10036 sinpi -0.19999999999999998 -> -0.5877852522924731 +sinpi10037 sinpi -0.2 -> -0.5877852522924731 +sinpi10038 sinpi 0.30000001192092896 -> 0.8090170163879177 +sinpi10039 sinpi 0.29999998211860657 -> 0.80901696135549 +sinpi10040 sinpi 0.30000000000000004 -> 0.8090169943749475 +sinpi10041 sinpi 0.3 -> 0.8090169943749475 +sinpi10042 sinpi -0.29999998211860657 -> -0.80901696135549 +sinpi10043 sinpi -0.30000001192092896 -> -0.8090170163879177 +sinpi10044 sinpi -0.3 -> -0.8090169943749475 +sinpi10045 sinpi -0.30000000000000004 -> -0.8090169943749475 +sinpi10046 sinpi 0.4000000059604645 -> 0.9510565220816052 +sinpi10047 sinpi 0.3999999761581421 -> 0.9510564931493437 +sinpi10048 sinpi 0.4 -> 0.9510565162951536 +sinpi10049 sinpi 0.39999999999999997 -> 0.9510565162951535 +sinpi10050 sinpi -0.3999999761581421 -> -0.9510564931493437 +sinpi10051 sinpi -0.4000000059604645 -> -0.9510565220816052 +sinpi10052 sinpi -0.39999999999999997 -> -0.9510565162951535 +sinpi10053 sinpi -0.4 -> -0.9510565162951536 +sinpi10054 sinpi 0.5 -> 1.0 +sinpi10055 sinpi -0.5 -> -1.0 +sinpi10056 sinpi 0.6000000238418579 -> 0.9510564931493437 +sinpi10057 sinpi 0.5999999642372131 -> 0.9510565510138584 +sinpi10058 sinpi 0.6000000000000001 -> 0.9510565162951535 +sinpi10059 sinpi 0.6 -> 0.9510565162951536 +sinpi10060 sinpi -0.5999999642372131 -> -0.9510565510138584 +sinpi10061 sinpi -0.6000000238418579 -> -0.9510564931493437 +sinpi10062 sinpi -0.6 -> -0.9510565162951536 +sinpi10063 sinpi -0.6000000000000001 -> -0.9510565162951535 +sinpi10064 sinpi 0.7000000476837158 -> 0.8090169063230551 +sinpi10065 sinpi 0.699999988079071 -> 0.8090170163879177 +sinpi10066 sinpi 0.7000000000000001 -> 0.8090169943749473 +sinpi10067 sinpi 0.7 -> 0.8090169943749475 +sinpi10068 sinpi -0.699999988079071 -> -0.8090170163879177 +sinpi10069 sinpi -0.7000000476837158 -> -0.8090169063230551 +sinpi10070 sinpi -0.7 -> -0.8090169943749475 +sinpi10071 sinpi -0.7000000000000001 -> -0.8090169943749473 +sinpi10072 sinpi 0.800000011920929 -> 0.5877852219942177 +sinpi10073 sinpi 0.7999999523162842 -> 0.5877853734854867 +sinpi10074 sinpi 0.8 -> 0.587785252292473 +sinpi10075 sinpi 0.7999999999999999 -> 0.5877852522924732 +sinpi10076 sinpi -0.7999999523162842 -> -0.5877853734854867 +sinpi10077 sinpi -0.800000011920929 -> -0.5877852219942177 +sinpi10078 sinpi -0.7999999999999999 -> -0.5877852522924732 +sinpi10079 sinpi -0.8 -> -0.587785252292473 +sinpi10080 sinpi 0.9000000357627869 -> 0.30901688752174056 +sinpi10081 sinpi 0.8999999761581421 -> 0.3090170656104165 +sinpi10082 sinpi 0.9 -> 0.30901699437494734 +sinpi10083 sinpi 0.8999999999999999 -> 0.3090169943749477 +sinpi10084 sinpi -0.8999999761581421 -> -0.3090170656104165 +sinpi10085 sinpi -0.9000000357627869 -> -0.30901688752174056 +sinpi10086 sinpi -0.8999999999999999 -> -0.3090169943749477 +sinpi10087 sinpi -0.9 -> -0.30901699437494734 +sinpi10088 sinpi 1.100000023841858 -> -0.3090170656104165 +sinpi10089 sinpi 1.0999999046325684 -> -0.3090167094330538 +sinpi10090 sinpi 1.1 -> -0.3090169943749477 +sinpi10091 sinpi 1.0999999999999999 -> -0.309016994374947 +sinpi10092 sinpi -1.0999999046325684 -> 0.3090167094330538 +sinpi10093 sinpi -1.100000023841858 -> 0.3090170656104165 +sinpi10094 sinpi -1.0999999999999999 -> 0.309016994374947 +sinpi10095 sinpi -1.1 -> 0.3090169943749477 +sinpi10096 sinpi 1.2000000476837158 -> -0.5877853734854867 +sinpi10097 sinpi 1.1999999284744263 -> -0.587785070502928 +sinpi10098 sinpi 1.2000000000000002 -> -0.5877852522924736 +sinpi10099 sinpi 1.2 -> -0.587785252292473 +sinpi10100 sinpi -1.1999999284744263 -> 0.587785070502928 +sinpi10101 sinpi -1.2000000476837158 -> 0.5877853734854867 +sinpi10102 sinpi -1.2 -> 0.587785252292473 +sinpi10103 sinpi -1.2000000000000002 -> 0.5877852522924736 +sinpi10104 sinpi 1.3000000715255737 -> -0.8090171264527518 +sinpi10105 sinpi 1.2999999523162842 -> -0.8090169063230551 +sinpi10106 sinpi 1.3 -> -0.8090169943749475 +sinpi10107 sinpi 1.2999999999999998 -> -0.8090169943749471 +sinpi10108 sinpi -1.2999999523162842 -> 0.8090169063230551 +sinpi10109 sinpi -1.3000000715255737 -> 0.8090171264527518 +sinpi10110 sinpi -1.2999999999999998 -> 0.8090169943749471 +sinpi10111 sinpi -1.3 -> 0.8090169943749475 +sinpi10112 sinpi 1.4000000953674316 -> -0.9510566088783399 +sinpi10113 sinpi 1.399999976158142 -> -0.9510564931493437 +sinpi10114 sinpi 1.4000000000000001 -> -0.9510565162951538 +sinpi10115 sinpi 1.4 -> -0.9510565162951535 +sinpi10116 sinpi -1.399999976158142 -> 0.9510564931493437 +sinpi10117 sinpi -1.4000000953674316 -> 0.9510566088783399 +sinpi10118 sinpi -1.4 -> 0.9510565162951535 +sinpi10119 sinpi -1.4000000000000001 -> 0.9510565162951538 +sinpi10120 sinpi 1.5 -> -1.0 +sinpi10121 sinpi -1.5 -> 1.0 +sinpi10122 sinpi 2.5 -> 1.0 +sinpi10123 sinpi -2.5 -> -1.0 +sinpi10124 sinpi 3.5 -> -1.0 +sinpi10125 sinpi -3.5 -> 1.0 +sinpi10126 sinpi 4.5 -> 1.0 +sinpi10127 sinpi -4.5 -> -1.0 +sinpi10128 sinpi 0.24000000953674316 -> 0.6845471277689983 +sinpi10129 sinpi 0.23999999463558197 -> 0.6845470936435142 +sinpi10130 sinpi 0.24000000000000002 -> 0.6845471059286887 +sinpi10131 sinpi 0.24 -> 0.6845471059286886 +sinpi10132 sinpi -0.23999999463558197 -> -0.6845470936435142 +sinpi10133 sinpi -0.24000000953674316 -> -0.6845471277689983 +sinpi10134 sinpi -0.24 -> -0.6845471059286886 +sinpi10135 sinpi -0.24000000000000002 -> -0.6845471059286887 +sinpi10136 sinpi 0.49000000953674316 -> 0.9995065613068151 +sinpi10137 sinpi 0.4899999797344208 -> 0.9995065583659261 +sinpi10138 sinpi 0.49000000000000005 -> 0.9995065603657316 +sinpi10139 sinpi 0.49 -> 0.9995065603657316 +sinpi10140 sinpi -0.4899999797344208 -> -0.9995065583659261 +sinpi10141 sinpi -0.49000000953674316 -> -0.9995065613068151 +sinpi10142 sinpi -0.49 -> -0.9995065603657316 +sinpi10143 sinpi -0.49000000000000005 -> -0.9995065603657316 +sinpi10144 sinpi 0.5100000500679016 -> 0.9995065554250282 +sinpi10145 sinpi 0.5099999904632568 -> 0.9995065613068151 +sinpi10146 sinpi 0.51 -> 0.9995065603657316 +sinpi10147 sinpi 0.5099999999999999 -> 0.9995065603657316 +sinpi10148 sinpi -0.5099999904632568 -> -0.9995065613068151 +sinpi10149 sinpi -0.5100000500679016 -> -0.9995065554250282 +sinpi10150 sinpi -0.5099999999999999 -> -0.9995065603657316 +sinpi10151 sinpi -0.51 -> -0.9995065603657316 +sinpi10152 sinpi 0.7600000500679016 -> 0.684546991267053 +sinpi10153 sinpi 0.7599999904632568 -> 0.6845471277689983 +sinpi10154 sinpi 0.76 -> 0.6845471059286886 +sinpi10155 sinpi 0.7599999999999999 -> 0.684547105928689 +sinpi10156 sinpi -0.7599999904632568 -> -0.6845471277689983 +sinpi10157 sinpi -0.7600000500679016 -> -0.684546991267053 +sinpi10158 sinpi -0.7599999999999999 -> -0.684547105928689 +sinpi10159 sinpi -0.76 -> -0.6845471059286886 +sinpi10160 sinpi 1.2400000095367432 -> -0.6845471277689983 +sinpi10161 sinpi 1.2399998903274536 -> -0.6845468547650836 +sinpi10162 sinpi 1.2400000000000002 -> -0.6845471059286892 +sinpi10163 sinpi 1.24 -> -0.6845471059286886 +sinpi10164 sinpi -1.2399998903274536 -> 0.6845468547650836 +sinpi10165 sinpi -1.2400000095367432 -> 0.6845471277689983 +sinpi10166 sinpi -1.24 -> 0.6845471059286886 +sinpi10167 sinpi -1.2400000000000002 -> 0.6845471059286892 +sinpi10168 sinpi 1.4900000095367432 -> -0.9995065613068151 +sinpi10169 sinpi 1.4899998903274536 -> -0.9995065495432062 +sinpi10170 sinpi 1.4900000000000002 -> -0.9995065603657316 +sinpi10171 sinpi 1.49 -> -0.9995065603657316 +sinpi10172 sinpi -1.4899998903274536 -> 0.9995065495432062 +sinpi10173 sinpi -1.4900000095367432 -> 0.9995065613068151 +sinpi10174 sinpi -1.49 -> 0.9995065603657316 +sinpi10175 sinpi -1.4900000000000002 -> 0.9995065603657316 +sinpi10176 sinpi 1.5100001096725464 -> -0.9995065495432062 +sinpi10177 sinpi 1.5099999904632568 -> -0.9995065613068151 +sinpi10178 sinpi 1.51 -> -0.9995065603657316 +sinpi10179 sinpi 1.5099999999999998 -> -0.9995065603657316 +sinpi10180 sinpi -1.5099999904632568 -> 0.9995065613068151 +sinpi10181 sinpi -1.5100001096725464 -> 0.9995065495432062 +sinpi10182 sinpi -1.5099999999999998 -> 0.9995065603657316 +sinpi10183 sinpi -1.51 -> 0.9995065603657316 +sinpi10184 sinpi 1.7600001096725464 -> -0.6845468547650836 +sinpi10185 sinpi 1.7599999904632568 -> -0.6845471277689983 +sinpi10186 sinpi 1.76 -> -0.6845471059286886 +sinpi10187 sinpi 1.7599999999999998 -> -0.6845471059286892 +sinpi10188 sinpi -1.7599999904632568 -> 0.6845471277689983 +sinpi10189 sinpi -1.7600001096725464 -> 0.6845468547650836 +sinpi10190 sinpi -1.7599999999999998 -> 0.6845471059286892 +sinpi10191 sinpi -1.76 -> 0.6845471059286886 +sinpi10192 sinpi 100.24000549316406 -> 0.6845596858452854 +sinpi10193 sinpi 100.23999786376953 -> 0.684542213683844 +sinpi10194 sinpi 100.24000000000001 -> 0.6845471059287095 +sinpi10195 sinpi 100.24 -> 0.684547105928677 +sinpi10196 sinpi -100.23999786376953 -> -0.684542213683844 +sinpi10197 sinpi -100.24000549316406 -> -0.6845596858452854 +sinpi10198 sinpi -100.24 -> -0.684547105928677 +sinpi10199 sinpi -100.24000000000001 -> -0.6845471059287095 +sinpi10200 sinpi 100.49000549316406 -> 0.9995071022812839 +sinpi10201 sinpi 100.48999786376953 -> 0.9995063495404061 +sinpi10202 sinpi 100.49000000000001 -> 0.9995065603657325 +sinpi10203 sinpi 100.49 -> 0.999506560365731 +sinpi10204 sinpi -100.48999786376953 -> -0.9995063495404061 +sinpi10205 sinpi -100.49000549316406 -> -0.9995071022812839 +sinpi10206 sinpi -100.49 -> -0.999506560365731 +sinpi10207 sinpi -100.49000000000001 -> -0.9995065603657325 +sinpi10208 sinpi 100.51000213623047 -> 0.9995063495404061 +sinpi10209 sinpi 100.50999450683594 -> 0.9995071022812839 +sinpi10210 sinpi 100.51 -> 0.999506560365731 +sinpi10211 sinpi 100.50999999999999 -> 0.9995065603657325 +sinpi10212 sinpi -100.50999450683594 -> -0.9995071022812839 +sinpi10213 sinpi -100.51000213623047 -> -0.9995063495404061 +sinpi10214 sinpi -100.50999999999999 -> -0.9995065603657325 +sinpi10215 sinpi -100.51 -> -0.999506560365731 +sinpi10216 sinpi 100.76000213623047 -> 0.684542213683844 +sinpi10217 sinpi 100.75999450683594 -> 0.6845596858452854 +sinpi10218 sinpi 100.76 -> 0.684547105928677 +sinpi10219 sinpi 100.75999999999999 -> 0.6845471059287095 +sinpi10220 sinpi -100.75999450683594 -> -0.6845596858452854 +sinpi10221 sinpi -100.76000213623047 -> -0.684542213683844 +sinpi10222 sinpi -100.75999999999999 -> -0.6845471059287095 +sinpi10223 sinpi -100.76 -> -0.684547105928677 +sinpi10224 sinpi 1234.56005859375 -> 0.9822527413662894 +sinpi10225 sinpi 1234.5599365234375 -> 0.9823245983107213 +sinpi10226 sinpi 1234.5600000000002 -> 0.9822872507285869 +sinpi10227 sinpi 1234.56 -> 0.9822872507287208 +sinpi10228 sinpi -1234.5599365234375 -> -0.9823245983107213 +sinpi10229 sinpi -1234.56005859375 -> -0.9822527413662894 +sinpi10230 sinpi -1234.56 -> -0.9822872507287208 +sinpi10231 sinpi -1234.5600000000002 -> -0.9822872507285869 +sinpi10232 sinpi 2097151.125 -> -0.3826834323650898 +sinpi10233 sinpi -2097151.125 -> 0.3826834323650898 +sinpi10234 sinpi 2097151.875 -> -0.3826834323650898 +sinpi10235 sinpi -2097151.875 -> 0.3826834323650898 +sinpi10236 sinpi 1125899906842624.0 -> 0.0 +sinpi10237 sinpi 1125899839733760.0 -> 0.0 +sinpi10238 sinpi 1125899906842623.1 -> -0.3826834323650898 +sinpi10239 sinpi -1125899839733760.0 -> -0.0 +sinpi10240 sinpi -1125899906842624.0 -> -0.0 +sinpi10241 sinpi -1125899906842623.1 -> 0.3826834323650898 +sinpi10242 sinpi 1125899906842624.0 -> 0.0 +sinpi10243 sinpi 1125899839733760.0 -> 0.0 +sinpi10244 sinpi 1125899906842623.9 -> -0.3826834323650898 +sinpi10245 sinpi -1125899839733760.0 -> -0.0 +sinpi10246 sinpi -1125899906842624.0 -> -0.0 +sinpi10247 sinpi -1125899906842623.9 -> 0.3826834323650898 +sinpi10248 sinpi 2.305843009213694e+18 -> 0.0 +sinpi10249 sinpi 2.3058428717747405e+18 -> 0.0 +sinpi10250 sinpi 2.3058430092136937e+18 -> 0.0 +sinpi10251 sinpi -2.3058428717747405e+18 -> -0.0 +sinpi10252 sinpi -2.305843009213694e+18 -> -0.0 +sinpi10253 sinpi -2.3058430092136937e+18 -> -0.0 +sinpi10254 sinpi 2.305843009213694e+18 -> 0.0 +sinpi10255 sinpi 2.3058428717747405e+18 -> 0.0 +sinpi10256 sinpi 2.3058430092136937e+18 -> 0.0 +sinpi10257 sinpi -2.3058428717747405e+18 -> -0.0 +sinpi10258 sinpi -2.305843009213694e+18 -> -0.0 +sinpi10259 sinpi -2.3058430092136937e+18 -> -0.0 +sinpi10260 sinpi 1.298074214633707e+33 -> 0.0 +sinpi10261 sinpi 1.2980741372624545e+33 -> 0.0 +sinpi10262 sinpi 1.2980742146337068e+33 -> 0.0 +sinpi10263 sinpi -1.2980741372624545e+33 -> -0.0 +sinpi10264 sinpi -1.298074214633707e+33 -> -0.0 +sinpi10265 sinpi -1.2980742146337068e+33 -> -0.0 +sinpi10266 sinpi 1.298074214633707e+33 -> 0.0 +sinpi10267 sinpi 1.2980741372624545e+33 -> 0.0 +sinpi10268 sinpi 1.2980742146337068e+33 -> 0.0 +sinpi10269 sinpi -1.2980741372624545e+33 -> -0.0 +sinpi10270 sinpi -1.298074214633707e+33 -> -0.0 +sinpi10271 sinpi -1.2980742146337068e+33 -> -0.0 +sinpi10272 sinpi 2.938735877055719e-39 -> 9.232311042199004e-39 +sinpi10273 sinpi -2.938735877055719e-39 -> -9.232311042199004e-39 +sinpi10274 sinpi 1.401298464324817e-45 -> 4.402308961009504e-45 +sinpi10275 sinpi 0.0 -> 0.0 +sinpi10276 sinpi 5.010420900022432e-293 -> 1.5740701490903231e-292 +sinpi10277 sinpi -0.0 -> -0.0 +sinpi10278 sinpi -1.401298464324817e-45 -> -4.402308961009504e-45 +sinpi10279 sinpi -5.010420900022432e-293 -> -1.5740701490903231e-292 +sinpi10280 sinpi 1.401298464324817e-45 -> 4.402308961009504e-45 +sinpi10281 sinpi 0.0 -> 0.0 +sinpi10282 sinpi 5.562684646268003e-309 -> 1.7475689218952297e-308 +sinpi10283 sinpi -0.0 -> -0.0 +sinpi10284 sinpi -1.401298464324817e-45 -> -4.402308961009504e-45 +sinpi10285 sinpi -5.562684646268003e-309 -> -1.7475689218952297e-308 +sinpi10286 sinpi 1.401298464324817e-45 -> 4.402308961009504e-45 +sinpi10287 sinpi 0.0 -> 0.0 +sinpi10288 sinpi 5e-324 -> 1.5e-323 +sinpi10289 sinpi -0.0 -> -0.0 +sinpi10290 sinpi -1.401298464324817e-45 -> -4.402308961009504e-45 +sinpi10291 sinpi -5e-324 -> -1.5e-323 +sinpi10292 sinpi 1.401298464324817e-45 -> 4.402308961009504e-45 +sinpi10293 sinpi 0.0 -> 0.0 +sinpi10294 sinpi 5e-324 -> 1.5e-323 +sinpi10295 sinpi -0.0 -> -0.0 +sinpi10296 sinpi -1.401298464324817e-45 -> -4.402308961009504e-45 +sinpi10297 sinpi -5e-324 -> -1.5e-323 +sinpi10298 sinpi 0.039865780621767044 -> 0.12491488475763585 +sinpi10299 sinpi -0.009947286918759346 -> -0.03124523733449905 +sinpi10300 sinpi -0.00994728785008192 -> -0.03124524025890666 +sinpi10301 sinpi -0.009947287497407154 -> -0.03124523915148717 +sinpi10302 sinpi 0.009947438724339008 -> 0.03124571401293662 +sinpi10303 sinpi 0.009947437793016434 -> 0.03124571108852905 +sinpi10304 sinpi 0.009947437917099084 -> 0.03124571147815586 +sinpi10305 sinpi 0.009947437917099082 -> 0.031245711478155854 +sinpi10306 sinpi -0.0049737198278307915 -> -0.015624765847977973 +sinpi10307 sinpi -0.004973720293492079 -> -0.015624767310717469 +sinpi10308 sinpi -0.004973719955059152 -> -0.015624766247628864 +sinpi10309 sinpi -0.004973719955059153 -> -0.015624766247628865 + +sinpi20001 sinpi inf -> nan invalid +sinpi20002 sinpi -inf -> nan invalid +sinpi20003 sinpi nan -> nan + +----------- +-- tanpi -- +----------- + +tanpi10000 tanpi 0.0 -> 0.0 +tanpi10001 tanpi -0.0 -> -0.0 +tanpi10002 tanpi 1.1754943508222875e-38 -> 3.6929244168796014e-38 +tanpi10003 tanpi 2.2250738585072014e-308 -> 6.990275687580919e-308 +tanpi10004 tanpi 2.004168360008973e-292 -> 6.296280596361293e-292 +tanpi10005 tanpi -1.1754943508222875e-38 -> -3.6929244168796014e-38 +tanpi10006 tanpi -2.2250738585072014e-308 -> -6.990275687580919e-308 +tanpi10007 tanpi -2.004168360008973e-292 -> -6.296280596361293e-292 +tanpi10008 tanpi 1.401298464324817e-45 -> 4.402308961009504e-45 +tanpi10009 tanpi 5e-324 -> 1.5e-323 +tanpi10010 tanpi -1.401298464324817e-45 -> -4.402308961009504e-45 +tanpi10011 tanpi -5e-324 -> -1.5e-323 +tanpi10012 tanpi 3.4028234663852886e+38 -> 0.0 +tanpi10013 tanpi 1.7976931348623157e+308 -> 0.0 +tanpi10014 tanpi -3.4028234663852886e+38 -> -0.0 +tanpi10015 tanpi -1.7976931348623157e+308 -> -0.0 +tanpi10016 tanpi 1.0 -> -0.0 +tanpi10017 tanpi -1.0 -> 0.0 +tanpi10018 tanpi 2.0 -> 0.0 +tanpi10019 tanpi -2.0 -> -0.0 +tanpi10020 tanpi 3.0 -> -0.0 +tanpi10021 tanpi -3.0 -> 0.0 +tanpi10022 tanpi 4.0 -> 0.0 +tanpi10023 tanpi -4.0 -> -0.0 +tanpi10024 tanpi 0.10000000149011612 -> 0.32491970140846616 +tanpi10025 tanpi 0.09999999403953552 -> 0.3249196755306671 +tanpi10026 tanpi 0.1 -> 0.32491969623290634 +tanpi10027 tanpi 0.09999999999999999 -> 0.3249196962329063 +tanpi10028 tanpi -0.09999999403953552 -> -0.3249196755306671 +tanpi10029 tanpi -0.10000000149011612 -> -0.32491970140846616 +tanpi10030 tanpi -0.09999999999999999 -> -0.3249196962329063 +tanpi10031 tanpi -0.1 -> -0.32491969623290634 +tanpi10032 tanpi 0.20000000298023224 -> 0.7265425423102566 +tanpi10033 tanpi 0.19999998807907104 -> 0.7265424707857802 +tanpi10034 tanpi 0.2 -> 0.7265425280053609 +tanpi10035 tanpi 0.19999999999999998 -> 0.7265425280053608 +tanpi10036 tanpi -0.19999998807907104 -> -0.7265424707857802 +tanpi10037 tanpi -0.20000000298023224 -> -0.7265425423102566 +tanpi10038 tanpi -0.19999999999999998 -> -0.7265425280053608 +tanpi10039 tanpi -0.2 -> -0.7265425280053609 +tanpi10040 tanpi 0.30000001192092896 -> 1.3763820288695117 +tanpi10041 tanpi 0.29999998211860657 -> 1.3763817578736872 +tanpi10042 tanpi 0.30000000000000004 -> 1.376381920471174 +tanpi10043 tanpi 0.3 -> 1.3763819204711734 +tanpi10044 tanpi -0.29999998211860657 -> -1.3763817578736872 +tanpi10045 tanpi -0.30000001192092896 -> -1.3763820288695117 +tanpi10046 tanpi -0.3 -> -1.3763819204711734 +tanpi10047 tanpi -0.30000000000000004 -> -1.376381920471174 +tanpi10048 tanpi 0.4000000059604645 -> 3.0776837332696907 +tanpi10049 tanpi 0.3999999761581421 -> 3.077682752797731 +tanpi10050 tanpi 0.4 -> 3.077683537175254 +tanpi10051 tanpi 0.39999999999999997 -> 3.0776835371752522 +tanpi10052 tanpi -0.3999999761581421 -> -3.077682752797731 +tanpi10053 tanpi -0.4000000059604645 -> -3.0776837332696907 +tanpi10054 tanpi -0.39999999999999997 -> -3.0776835371752522 +tanpi10055 tanpi -0.4 -> -3.077683537175254 +tanpi10056 tanpi 0.6000000238418579 -> -3.077682752797731 +tanpi10057 tanpi 0.5999999642372131 -> -3.077684713742215 +tanpi10058 tanpi 0.6000000000000001 -> -3.0776835371752504 +tanpi10059 tanpi 0.6 -> -3.077683537175254 +tanpi10060 tanpi -0.5999999642372131 -> 3.077684713742215 +tanpi10061 tanpi -0.6000000238418579 -> 3.077682752797731 +tanpi10062 tanpi -0.6 -> 3.077683537175254 +tanpi10063 tanpi -0.6000000000000001 -> 3.0776835371752504 +tanpi10064 tanpi 0.7000000476837158 -> -1.3763814868779325 +tanpi10065 tanpi 0.699999988079071 -> -1.3763820288695117 +tanpi10066 tanpi 0.7000000000000001 -> -1.376381920471173 +tanpi10067 tanpi 0.7 -> -1.376381920471174 +tanpi10068 tanpi -0.699999988079071 -> 1.3763820288695117 +tanpi10069 tanpi -0.7000000476837158 -> 1.3763814868779325 +tanpi10070 tanpi -0.7 -> 1.376381920471174 +tanpi10071 tanpi -0.7000000000000001 -> 1.376381920471173 +tanpi10072 tanpi 0.800000011920929 -> -0.7265424707857802 +tanpi10073 tanpi 0.7999999523162842 -> -0.7265427568837151 +tanpi10074 tanpi 0.8 -> -0.7265425280053607 +tanpi10075 tanpi 0.7999999999999999 -> -0.7265425280053612 +tanpi10076 tanpi -0.7999999523162842 -> 0.7265427568837151 +tanpi10077 tanpi -0.800000011920929 -> 0.7265424707857802 +tanpi10078 tanpi -0.7999999999999999 -> 0.7265425280053612 +tanpi10079 tanpi -0.8 -> 0.7265425280053607 +tanpi10080 tanpi 0.9000000357627869 -> -0.3249195720194747 +tanpi10081 tanpi 0.8999999761581421 -> -0.3249197790418658 +tanpi10082 tanpi 0.9 -> -0.32491969623290623 +tanpi10083 tanpi 0.8999999999999999 -> -0.3249196962329066 +tanpi10084 tanpi -0.8999999761581421 -> 0.3249197790418658 +tanpi10085 tanpi -0.9000000357627869 -> 0.3249195720194747 +tanpi10086 tanpi -0.8999999999999999 -> 0.3249196962329066 +tanpi10087 tanpi -0.9 -> 0.32491969623290623 +tanpi10088 tanpi 1.100000023841858 -> 0.3249197790418658 +tanpi10089 tanpi 1.0999999046325684 -> 0.3249193649971088 +tanpi10090 tanpi 1.1 -> 0.3249196962329066 +tanpi10091 tanpi 1.0999999999999999 -> 0.32491969623290584 +tanpi10092 tanpi -1.0999999046325684 -> -0.3249193649971088 +tanpi10093 tanpi -1.100000023841858 -> -0.3249197790418658 +tanpi10094 tanpi -1.0999999999999999 -> -0.32491969623290584 +tanpi10095 tanpi -1.1 -> -0.3249196962329066 +tanpi10096 tanpi 1.2000000476837158 -> 0.7265427568837151 +tanpi10097 tanpi 1.1999999284744263 -> 0.7265421846879231 +tanpi10098 tanpi 1.2000000000000002 -> 0.7265425280053618 +tanpi10099 tanpi 1.2 -> 0.7265425280053607 +tanpi10100 tanpi -1.1999999284744263 -> -0.7265421846879231 +tanpi10101 tanpi -1.2000000476837158 -> -0.7265427568837151 +tanpi10102 tanpi -1.2 -> -0.7265425280053607 +tanpi10103 tanpi -1.2000000000000002 -> -0.7265425280053618 +tanpi10104 tanpi 1.3000000715255737 -> 1.3763825708613702 +tanpi10105 tanpi 1.2999999523162842 -> 1.3763814868779325 +tanpi10106 tanpi 1.3 -> 1.376381920471174 +tanpi10107 tanpi 1.2999999999999998 -> 1.376381920471172 +tanpi10108 tanpi -1.2999999523162842 -> -1.3763814868779325 +tanpi10109 tanpi -1.3000000715255737 -> -1.3763825708613702 +tanpi10110 tanpi -1.2999999999999998 -> -1.376381920471172 +tanpi10111 tanpi -1.3 -> -1.376381920471174 +tanpi10112 tanpi 1.4000000953674316 -> 3.0776866746889597 +tanpi10113 tanpi 1.399999976158142 -> 3.077682752797731 +tanpi10114 tanpi 1.4000000000000001 -> 3.077683537175258 +tanpi10115 tanpi 1.4 -> 3.0776835371752504 +tanpi10116 tanpi -1.399999976158142 -> -3.077682752797731 +tanpi10117 tanpi -1.4000000953674316 -> -3.0776866746889597 +tanpi10118 tanpi -1.4 -> -3.0776835371752504 +tanpi10119 tanpi -1.4000000000000001 -> -3.077683537175258 +tanpi10120 tanpi 0.24000000953674316 -> 0.9390625621984302 +tanpi10121 tanpi 0.23999999463558197 -> 0.9390624741032162 +tanpi10122 tanpi 0.24000000000000002 -> 0.9390625058174925 +tanpi10123 tanpi 0.24 -> 0.9390625058174923 +tanpi10124 tanpi -0.23999999463558197 -> -0.9390624741032162 +tanpi10125 tanpi -0.24000000953674316 -> -0.9390625621984302 +tanpi10126 tanpi -0.24 -> -0.9390625058174923 +tanpi10127 tanpi -0.24000000000000002 -> -0.9390625058174925 +tanpi10128 tanpi 0.49000000953674316 -> 31.820546320188043 +tanpi10129 tanpi 0.4899999797344208 -> 31.82045142533627 +tanpi10130 tanpi 0.49000000000000005 -> 31.820515953774105 +tanpi10131 tanpi 0.49 -> 31.82051595377393 +tanpi10132 tanpi -0.4899999797344208 -> -31.82045142533627 +tanpi10133 tanpi -0.49000000953674316 -> -31.820546320188043 +tanpi10134 tanpi -0.49 -> -31.82051595377393 +tanpi10135 tanpi -0.49000000000000005 -> -31.820515953774105 +tanpi10136 tanpi 0.5100000500679016 -> -31.820356531049924 +tanpi10137 tanpi 0.5099999904632568 -> -31.820546320188043 +tanpi10138 tanpi 0.51 -> -31.82051595377393 +tanpi10139 tanpi 0.5099999999999999 -> -31.820515953774283 +tanpi10140 tanpi -0.5099999904632568 -> 31.820546320188043 +tanpi10141 tanpi -0.5100000500679016 -> 31.820356531049924 +tanpi10142 tanpi -0.5099999999999999 -> 31.820515953774283 +tanpi10143 tanpi -0.51 -> 31.82051595377393 +tanpi10144 tanpi 0.7600000500679016 -> -0.9390622098176208 +tanpi10145 tanpi 0.7599999904632568 -> -0.9390625621984302 +tanpi10146 tanpi 0.76 -> -0.9390625058174923 +tanpi10147 tanpi 0.7599999999999999 -> -0.939062505817493 +tanpi10148 tanpi -0.7599999904632568 -> 0.9390625621984302 +tanpi10149 tanpi -0.7600000500679016 -> 0.9390622098176208 +tanpi10150 tanpi -0.7599999999999999 -> 0.939062505817493 +tanpi10151 tanpi -0.76 -> 0.9390625058174923 +tanpi10152 tanpi 1.2400000095367432 -> 0.9390625621984302 +tanpi10153 tanpi 1.2399998903274536 -> 0.9390618574369353 +tanpi10154 tanpi 1.2400000000000002 -> 0.9390625058174936 +tanpi10155 tanpi 1.24 -> 0.9390625058174923 +tanpi10156 tanpi -1.2399998903274536 -> -0.9390618574369353 +tanpi10157 tanpi -1.2400000095367432 -> -0.9390625621984302 +tanpi10158 tanpi -1.24 -> -0.9390625058174923 +tanpi10159 tanpi -1.2400000000000002 -> -0.9390625058174936 +tanpi10160 tanpi 1.4900000095367432 -> 31.820546320188043 +tanpi10161 tanpi 1.4899998903274536 -> 31.820166744173495 +tanpi10162 tanpi 1.4900000000000002 -> 31.82051595377464 +tanpi10163 tanpi 1.49 -> 31.82051595377393 +tanpi10164 tanpi -1.4899998903274536 -> -31.820166744173495 +tanpi10165 tanpi -1.4900000095367432 -> -31.820546320188043 +tanpi10166 tanpi -1.49 -> -31.82051595377393 +tanpi10167 tanpi -1.4900000000000002 -> -31.82051595377464 +tanpi10168 tanpi 1.5100001096725464 -> -31.820166744173495 +tanpi10169 tanpi 1.5099999904632568 -> -31.820546320188043 +tanpi10170 tanpi 1.51 -> -31.82051595377393 +tanpi10171 tanpi 1.5099999999999998 -> -31.82051595377464 +tanpi10172 tanpi -1.5099999904632568 -> 31.820546320188043 +tanpi10173 tanpi -1.5100001096725464 -> 31.820166744173495 +tanpi10174 tanpi -1.5099999999999998 -> 31.82051595377464 +tanpi10175 tanpi -1.51 -> 31.82051595377393 +tanpi10176 tanpi 1.7600001096725464 -> -0.9390618574369353 +tanpi10177 tanpi 1.7599999904632568 -> -0.9390625621984302 +tanpi10178 tanpi 1.76 -> -0.9390625058174923 +tanpi10179 tanpi 1.7599999999999998 -> -0.9390625058174936 +tanpi10180 tanpi -1.7599999904632568 -> 0.9390625621984302 +tanpi10181 tanpi -1.7600001096725464 -> 0.9390618574369353 +tanpi10182 tanpi -1.7599999999999998 -> 0.9390625058174936 +tanpi10183 tanpi -1.76 -> 0.9390625058174923 +tanpi10184 tanpi 100.24000549316406 -> 0.9390949817630689 +tanpi10185 tanpi 100.23999786376953 -> 0.9390498765673647 +tanpi10186 tanpi 100.24000000000001 -> 0.9390625058175461 +tanpi10187 tanpi 100.24 -> 0.9390625058174621 +tanpi10188 tanpi -100.23999786376953 -> -0.9390498765673647 +tanpi10189 tanpi -100.24000549316406 -> -0.9390949817630689 +tanpi10190 tanpi -100.24 -> -0.9390625058174621 +tanpi10191 tanpi -100.24000000000001 -> -0.9390625058175461 +tanpi10192 tanpi 100.49000549316406 -> 31.83801660184476 +tanpi10193 tanpi 100.48999786376953 -> 31.81371533579395 +tanpi10194 tanpi 100.49000000000001 -> 31.820515953802918 +tanpi10195 tanpi 100.49 -> 31.820515953757667 +tanpi10196 tanpi -100.48999786376953 -> -31.81371533579395 +tanpi10197 tanpi -100.49000549316406 -> -31.83801660184476 +tanpi10198 tanpi -100.49 -> -31.820515953757667 +tanpi10199 tanpi -100.49000000000001 -> -31.820515953802918 +tanpi10200 tanpi 100.51000213623047 -> -31.81371533579395 +tanpi10201 tanpi 100.50999450683594 -> -31.83801660184476 +tanpi10202 tanpi 100.51 -> -31.820515953757667 +tanpi10203 tanpi 100.50999999999999 -> -31.820515953802918 +tanpi10204 tanpi -100.50999450683594 -> 31.83801660184476 +tanpi10205 tanpi -100.51000213623047 -> 31.81371533579395 +tanpi10206 tanpi -100.50999999999999 -> 31.820515953802918 +tanpi10207 tanpi -100.51 -> 31.820515953757667 +tanpi10208 tanpi 100.76000213623047 -> -0.9390498765673647 +tanpi10209 tanpi 100.75999450683594 -> -0.9390949817630689 +tanpi10210 tanpi 100.76 -> -0.9390625058174621 +tanpi10211 tanpi 100.75999999999999 -> -0.9390625058175461 +tanpi10212 tanpi -100.75999450683594 -> 0.9390949817630689 +tanpi10213 tanpi -100.76000213623047 -> 0.9390498765673647 +tanpi10214 tanpi -100.75999999999999 -> 0.9390625058175461 +tanpi10215 tanpi -100.76 -> 0.9390625058174621 +tanpi10216 tanpi 1234.56005859375 -> -5.236946012446678 +tanpi10217 tanpi 1234.5599365234375 -> -5.247869032587774 +tanpi10218 tanpi 1234.5600000000002 -> -5.2421835810977155 +tanpi10219 tanpi 1234.56 -> -5.242183581118059 +tanpi10220 tanpi -1234.5599365234375 -> 5.247869032587774 +tanpi10221 tanpi -1234.56005859375 -> 5.236946012446678 +tanpi10222 tanpi -1234.56 -> 5.242183581118059 +tanpi10223 tanpi -1234.5600000000002 -> 5.2421835810977155 +tanpi10224 tanpi 2097151.375 -> 2.414213562373095 +tanpi10225 tanpi -2097151.375 -> -2.414213562373095 +tanpi10226 tanpi 2097151.625 -> -2.414213562373095 +tanpi10227 tanpi -2097151.625 -> 2.414213562373095 +tanpi10228 tanpi 1125899906842624.0 -> 0.0 +tanpi10229 tanpi 1125899839733760.0 -> 0.0 +tanpi10230 tanpi 1125899906842623.4 -> 2.414213562373095 +tanpi10231 tanpi -1125899839733760.0 -> -0.0 +tanpi10232 tanpi -1125899906842624.0 -> -0.0 +tanpi10233 tanpi -1125899906842623.4 -> -2.414213562373095 +tanpi10234 tanpi 1125899906842624.0 -> 0.0 +tanpi10235 tanpi 1125899839733760.0 -> 0.0 +tanpi10236 tanpi 1125899906842623.6 -> -2.414213562373095 +tanpi10237 tanpi -1125899839733760.0 -> -0.0 +tanpi10238 tanpi -1125899906842624.0 -> -0.0 +tanpi10239 tanpi -1125899906842623.6 -> 2.414213562373095 +tanpi10240 tanpi 2.305843009213694e+18 -> 0.0 +tanpi10241 tanpi 2.3058428717747405e+18 -> 0.0 +tanpi10242 tanpi 2.3058430092136937e+18 -> 0.0 +tanpi10243 tanpi -2.3058428717747405e+18 -> -0.0 +tanpi10244 tanpi -2.305843009213694e+18 -> -0.0 +tanpi10245 tanpi -2.3058430092136937e+18 -> -0.0 +tanpi10246 tanpi 2.305843009213694e+18 -> 0.0 +tanpi10247 tanpi 2.3058428717747405e+18 -> 0.0 +tanpi10248 tanpi 2.3058430092136937e+18 -> 0.0 +tanpi10249 tanpi -2.3058428717747405e+18 -> -0.0 +tanpi10250 tanpi -2.305843009213694e+18 -> -0.0 +tanpi10251 tanpi -2.3058430092136937e+18 -> -0.0 +tanpi10252 tanpi 1.298074214633707e+33 -> 0.0 +tanpi10253 tanpi 1.2980741372624545e+33 -> 0.0 +tanpi10254 tanpi 1.2980742146337068e+33 -> 0.0 +tanpi10255 tanpi -1.2980741372624545e+33 -> -0.0 +tanpi10256 tanpi -1.298074214633707e+33 -> -0.0 +tanpi10257 tanpi -1.2980742146337068e+33 -> -0.0 +tanpi10258 tanpi 1.298074214633707e+33 -> 0.0 +tanpi10259 tanpi 1.2980741372624545e+33 -> 0.0 +tanpi10260 tanpi 1.2980742146337068e+33 -> 0.0 +tanpi10261 tanpi -1.2980741372624545e+33 -> -0.0 +tanpi10262 tanpi -1.298074214633707e+33 -> -0.0 +tanpi10263 tanpi -1.2980742146337068e+33 -> -0.0 +tanpi10264 tanpi 0.48004481196403503 -> 15.930332115434627 +tanpi10265 tanpi -0.4950249195098877 -> -63.97564155226383 +tanpi10266 tanpi -0.4950249493122101 -> -63.97602485152189 +tanpi10267 tanpi -0.49502493342943843 -> -63.97582057644752 +tanpi10268 tanpi -3.659269094467163 -> 1.828928514937406 +tanpi10269 tanpi -3.659269332885742 -> 1.8289252604911337 +tanpi10270 tanpi -3.659269111498701 -> 1.8289282824542836 +tanpi10271 tanpi -3.6592691114987015 -> 1.8289282824542774 +tanpi10272 tanpi -1.6591962575912476 -> 1.829923165916756 +tanpi10273 tanpi -1.659196376800537 -> 1.8299215373295707 +tanpi10274 tanpi -1.6591963470121214 -> 1.8299219442861712 +tanpi10275 tanpi -1.6591963470121216 -> 1.829921944286168 + +tanpi20001 tanpi inf -> nan invalid +tanpi20002 tanpi -inf -> nan invalid +tanpi20003 tanpi nan -> nan diff --git a/Lib/test/memory_watchdog.py b/Lib/test/memory_watchdog.py index fee062ecc9b300..4a3f66e1f822ba 100644 --- a/Lib/test/memory_watchdog.py +++ b/Lib/test/memory_watchdog.py @@ -1,21 +1,40 @@ """Memory watchdog: periodically read the memory usage of the main test process and print it out, until terminated.""" -# stdin should refer to the process' /proc//statm: we don't pass the -# process' PID to avoid a race condition in case of - unlikely - PID recycling. -# If the process crashes, reading from the /proc entry will fail with ESRCH. import sys import time -from test.support import get_pagesize - - -while True: - page_size = get_pagesize() - sys.stdin.seek(0) - statm = sys.stdin.read() - data = int(statm.split()[5]) - sys.stdout.write(" ... process data size: {data:.1f}G\n" - .format(data=data * page_size / (1024 ** 3))) - sys.stdout.flush() - time.sleep(1) +from test.libregrtest.utils import get_process_memory_usage + + +ONE_GIB = (1024 ** 3) + + +def watchdog(pid): + while True: + mem = get_process_memory_usage(pid) + if mem is None: + # get_process_memory_usage() is not supported on the platform, + # or something went wrong. Exit since the next call is likely to + # fail the same way. + return + + # Prefer sys.stdout.write() to print() to use a single write() syscall. + # print(msg) calls write(msg.encode()) and then write(b"\n"). + sys.stdout.write(f" ... process data size: {mem / ONE_GIB:.1f} GiB\n") + sys.stdout.flush() + time.sleep(1) + +def main(): + if len(sys.argv) != 2: + print(f"usage: python {sys.argv[0]} pid") + sys.exit(1) + pid = int(sys.argv[1]) + + try: + watchdog(pid) + except KeyboardInterrupt: + pass + +if __name__ == "__main__": + main() diff --git a/Lib/test/mime.types2 b/Lib/test/mime.types2 new file mode 100644 index 00000000000000..b05c318d1c05c2 --- /dev/null +++ b/Lib/test/mime.types2 @@ -0,0 +1,3 @@ +# MIME type Extensions +testing/test2 test test2 +testing/test3 test test3 diff --git a/Lib/test/mp_preload_flush.py b/Lib/test/mp_preload_flush.py index 3501554d366a21..c195a9ef6b26fe 100644 --- a/Lib/test/mp_preload_flush.py +++ b/Lib/test/mp_preload_flush.py @@ -1,15 +1,11 @@ import multiprocessing import sys -modname = 'preloaded_module' +print(__name__, end='', file=sys.stderr) +print(__name__, end='', file=sys.stdout) if __name__ == '__main__': - if modname in sys.modules: - raise AssertionError(f'{modname!r} is not in sys.modules') multiprocessing.set_start_method('forkserver') - multiprocessing.set_forkserver_preload([modname]) for _ in range(2): p = multiprocessing.Process() p.start() p.join() -elif modname not in sys.modules: - raise AssertionError(f'{modname!r} is not in sys.modules') diff --git a/Lib/test/mp_preload_large_sysargv.py b/Lib/test/mp_preload_large_sysargv.py new file mode 100644 index 00000000000000..790fcd76eadae6 --- /dev/null +++ b/Lib/test/mp_preload_large_sysargv.py @@ -0,0 +1,30 @@ +# gh-144503: Test that the forkserver can start when the parent process has +# a very large sys.argv. Prior to the fix, sys.argv was repr'd into the +# forkserver ``-c`` command string which could exceed the OS limit on the +# length of a single argv element (MAX_ARG_STRLEN on Linux, ~128 KiB), +# causing posix_spawn to fail and the parent to see a BrokenPipeError. + +import multiprocessing +import sys + +EXPECTED_LEN = 5002 # argv[0] + 5000 padding entries + sentinel + + +def fun(): + print(f"worker:{len(sys.argv)}:{sys.argv[-1]}") + + +if __name__ == "__main__": + # Inflate sys.argv well past 128 KiB before the forkserver is started. + sys.argv[1:] = ["x" * 50] * 5000 + ["sentinel"] + assert len(sys.argv) == EXPECTED_LEN + + ctx = multiprocessing.get_context("forkserver") + p = ctx.Process(target=fun) + p.start() + p.join() + sys.exit(p.exitcode) +else: + # This branch runs when the forkserver preloads this module as + # __mp_main__; confirm the large argv was propagated intact. + print(f"preload:{len(sys.argv)}:{sys.argv[-1]}") diff --git a/Lib/test/mp_preload_main.py b/Lib/test/mp_preload_main.py new file mode 100644 index 00000000000000..acb342822ecc89 --- /dev/null +++ b/Lib/test/mp_preload_main.py @@ -0,0 +1,14 @@ +import multiprocessing + +print(f"{__name__}") + +def f(): + print("f") + +if __name__ == "__main__": + ctx = multiprocessing.get_context("forkserver") + ctx.set_forkserver_preload(['__main__']) + for _ in range(2): + p = ctx.Process(target=f) + p.start() + p.join() diff --git a/Lib/test/mp_preload_sysargv.py b/Lib/test/mp_preload_sysargv.py new file mode 100644 index 00000000000000..5ad38cda889f55 --- /dev/null +++ b/Lib/test/mp_preload_sysargv.py @@ -0,0 +1,22 @@ +# gh-143706: Test that sys.argv is correctly set during main module import +# when using forkserver with __main__ preloading. + +import multiprocessing +import sys + +# This will be printed during module import - sys.argv should be correct here +print(f"module:{sys.argv[1:]}") + +def fun(): + # This will be printed when the function is called + print(f"fun:{sys.argv[1:]}") + +if __name__ == "__main__": + ctx = multiprocessing.get_context("forkserver") + ctx.set_forkserver_preload(['__main__']) + + fun() + + p = ctx.Process(target=fun) + p.start() + p.join() diff --git a/Lib/test/multibytecodec_support.py b/Lib/test/multibytecodec_support.py index dbf0cc428e3ff6..6b4c57d0b4bad7 100644 --- a/Lib/test/multibytecodec_support.py +++ b/Lib/test/multibytecodec_support.py @@ -282,6 +282,23 @@ def test_incrementalencoder_del_segfault(self): with self.assertRaises(AttributeError): del e.errors + def test_null_terminator(self): + # see gh-101828 + text = "フルーツ" + try: + text.encode(self.encoding) + except UnicodeEncodeError: + text = "Python is cool" + encode_w_null = (text + "\0").encode(self.encoding) + encode_plus_null = text.encode(self.encoding) + "\0".encode(self.encoding) + self.assertTrue(encode_w_null.endswith(b'\x00')) + self.assertEqual(encode_w_null, encode_plus_null) + + encode_w_null_2 = (text + "\0" + text + "\0").encode(self.encoding) + encode_plus_null_2 = encode_plus_null + encode_plus_null + self.assertEqual(encode_w_null_2.count(b'\x00'), 2) + self.assertEqual(encode_w_null_2, encode_plus_null_2) + class TestBase_Mapping(unittest.TestCase): pass_enctest = [] diff --git a/Lib/test/picklecommon.py b/Lib/test/picklecommon.py new file mode 100644 index 00000000000000..bb8e41b01492ea --- /dev/null +++ b/Lib/test/picklecommon.py @@ -0,0 +1,446 @@ +# Classes used for pickle testing. +# They are moved to separate file, so they can be loaded +# in other Python version for test_xpickle. + +import sys + +class C: + def __eq__(self, other): + return self.__dict__ == other.__dict__ + +# For test_load_classic_instance +class D(C): + def __init__(self, arg): + pass + +class E(C): + def __getinitargs__(self): + return () + +import __main__ +__main__.C = C +C.__module__ = "__main__" +__main__.D = D +D.__module__ = "__main__" +__main__.E = E +E.__module__ = "__main__" + +# Simple mutable object. +class Object(object): + pass + +# Hashable immutable key object containing unheshable mutable data. +class K: + def __init__(self, value): + self.value = value + + def __reduce__(self): + # Shouldn't support the recursion itself + return K, (self.value,) + +class WithSlots(object): + __slots__ = ('a', 'b') + +class WithSlotsSubclass(WithSlots): + __slots__ = ('c',) + +class WithSlotsAndDict(object): + __slots__ = ('a', '__dict__') + +class WithPrivateAttrs(object): + def __init__(self, a): + self.__private = a + def get(self): + return self.__private + +class WithPrivateAttrsSubclass(WithPrivateAttrs): + def __init__(self, a, b): + super().__init__(a) + self.__private = b + def get2(self): + return self.__private + +class WithPrivateSlots(object): + __slots__ = ('__private',) + def __init__(self, a): + self.__private = a + def get(self): + return self.__private + +class WithPrivateSlotsSubclass(WithPrivateSlots): + __slots__ = ('__private',) + def __init__(self, a, b): + super().__init__(a) + self.__private = b + def get2(self): + return self.__private + +# For test_misc +class myint(int): + def __init__(self, x): + self.str = str(x) + +# For test_misc and test_getinitargs +class initarg(C): + + def __init__(self, a, b): + self.a = a + self.b = b + + def __getinitargs__(self): + return self.a, self.b + +# For test_metaclass +class metaclass(type): + pass + +if sys.version_info >= (3,): + # Syntax not compatible with Python 2 + exec(''' +class use_metaclass(object, metaclass=metaclass): + pass +''') +else: + class use_metaclass(object): + __metaclass__ = metaclass + + +# Test classes for reduce_ex + +class R: + def __init__(self, reduce=None): + self.reduce = reduce + def __reduce__(self, proto): + return self.reduce + +class REX: + def __init__(self, reduce_ex=None): + self.reduce_ex = reduce_ex + def __reduce_ex__(self, proto): + return self.reduce_ex + +class REX_one(object): + """No __reduce_ex__ here, but inheriting it from object""" + _reduce_called = 0 + def __reduce__(self): + self._reduce_called = 1 + return REX_one, () + +class REX_two(object): + """No __reduce__ here, but inheriting it from object""" + _proto = None + def __reduce_ex__(self, proto): + self._proto = proto + return REX_two, () + +class REX_three(object): + _proto = None + def __reduce_ex__(self, proto): + self._proto = proto + return REX_two, () + def __reduce__(self): + raise AssertionError("This __reduce__ shouldn't be called") + +class REX_four(object): + """Calling base class method should succeed""" + _proto = None + def __reduce_ex__(self, proto): + self._proto = proto + return object.__reduce_ex__(self, proto) + +class REX_five(object): + """This one used to fail with infinite recursion""" + _reduce_called = 0 + def __reduce__(self): + self._reduce_called = 1 + return object.__reduce__(self) + +class REX_six(object): + """This class is used to check the 4th argument (list iterator) of + the reduce protocol. + """ + def __init__(self, items=None): + self.items = items if items is not None else [] + def __eq__(self, other): + return type(self) is type(other) and self.items == other.items + def append(self, item): + self.items.append(item) + def __reduce__(self): + return type(self), (), None, iter(self.items), None + +class REX_seven(object): + """This class is used to check the 5th argument (dict iterator) of + the reduce protocol. + """ + def __init__(self, table=None): + self.table = table if table is not None else {} + def __eq__(self, other): + return type(self) is type(other) and self.table == other.table + def __setitem__(self, key, value): + self.table[key] = value + def __reduce__(self): + return type(self), (), None, None, iter(self.table.items()) + +class REX_state(object): + """This class is used to check the 3th argument (state) of + the reduce protocol. + """ + def __init__(self, state=None): + self.state = state + def __eq__(self, other): + return type(self) is type(other) and self.state == other.state + def __setstate__(self, state): + self.state = state + def __reduce__(self): + return type(self), (), self.state + +# For test_reduce_ex_None +class REX_None: + """ Setting __reduce_ex__ to None should fail """ + __reduce_ex__ = None + +# For test_reduce_None +class R_None: + """ Setting __reduce__ to None should fail """ + __reduce__ = None + +# For test_pickle_setstate_None +class C_None_setstate: + """ Setting __setstate__ to None should fail """ + def __getstate__(self): + return 1 + + __setstate__ = None + + +# Test classes for newobj + +# For test_newobj_generic and test_newobj_proxies + +class MyInt(int): + sample = 1 + +if sys.version_info >= (3,): + class MyLong(int): + sample = 1 +else: + class MyLong(long): + sample = long(1) + +class MyFloat(float): + sample = 1.0 + +class MyComplex(complex): + sample = 1.0 + 0.0j + +class MyStr(str): + sample = "hello" + +if sys.version_info >= (3,): + class MyUnicode(str): + sample = "hello \u1234" +else: + class MyUnicode(unicode): + sample = unicode(r"hello \u1234", "raw-unicode-escape") + +class MyTuple(tuple): + sample = (1, 2, 3) + +class MyList(list): + sample = [1, 2, 3] + +class MyDict(dict): + sample = {"a": 1, "b": 2} + +class MySet(set): + sample = {"a", "b"} + +class MyFrozenSet(frozenset): + sample = frozenset({"a", "b"}) + +myclasses = [MyInt, MyLong, MyFloat, + MyComplex, + MyStr, MyUnicode, + MyTuple, MyList, MyDict, MySet, MyFrozenSet] + +try: + frozendict +except NameError: + # Python 3.14 and older + pass +else: + class MyFrozenDict(dict): + sample = frozendict({"a": 1, "b": 2}) + myclasses.append(MyFrozenDict) + + +# For test_newobj_overridden_new +class MyIntWithNew(int): + def __new__(cls, value): + raise AssertionError + +class MyIntWithNew2(MyIntWithNew): + __new__ = int.__new__ + + +# For test_newobj_list_slots +class SlotList(MyList): + __slots__ = ["foo"] + +# Ruff "redefined while unused" false positive here due to `global` variables +# being assigned (and then restored) from within test methods earlier in the file +class SimpleNewObj(int): # noqa: F811 + def __init__(self, *args, **kwargs): + # raise an error, to make sure this isn't called + raise TypeError("SimpleNewObj.__init__() didn't expect to get called") + def __eq__(self, other): + return int(self) == int(other) and self.__dict__ == other.__dict__ + +class ComplexNewObj(SimpleNewObj): + def __getnewargs__(self): + return ('%X' % self, 16) + +class ComplexNewObjEx(SimpleNewObj): + def __getnewargs_ex__(self): + return ('%X' % self,), {'base': 16} + + +class ZeroCopyBytes(bytes): + readonly = True + c_contiguous = True + f_contiguous = True + zero_copy_reconstruct = True + + def __reduce_ex__(self, protocol): + if protocol >= 5: + import pickle + return type(self)._reconstruct, (pickle.PickleBuffer(self),), None + else: + return type(self)._reconstruct, (bytes(self),) + + def __repr__(self): + return "{}({!r})".format(self.__class__.__name__, bytes(self)) + + __str__ = __repr__ + + @classmethod + def _reconstruct(cls, obj): + with memoryview(obj) as m: + obj = m.obj + if type(obj) is cls: + # Zero-copy + return obj + else: + return cls(obj) + + +class ZeroCopyBytearray(bytearray): + readonly = False + c_contiguous = True + f_contiguous = True + zero_copy_reconstruct = True + + def __reduce_ex__(self, protocol): + if protocol >= 5: + import pickle + return type(self)._reconstruct, (pickle.PickleBuffer(self),), None + else: + return type(self)._reconstruct, (bytes(self),) + + def __repr__(self): + return "{}({!r})".format(self.__class__.__name__, bytes(self)) + + __str__ = __repr__ + + @classmethod + def _reconstruct(cls, obj): + with memoryview(obj) as m: + obj = m.obj + if type(obj) is cls: + # Zero-copy + return obj + else: + return cls(obj) + + +# For test_nested_names +class Nested: + class A: + class B: + class C: + pass + +# For test_py_methods +class PyMethodsTest: + @staticmethod + def cheese(): + return "cheese" + @classmethod + def wine(cls): + assert cls is PyMethodsTest + return "wine" + def biscuits(self): + assert isinstance(self, PyMethodsTest) + return "biscuits" + class Nested: + "Nested class" + @staticmethod + def ketchup(): + return "ketchup" + @classmethod + def maple(cls): + assert cls is PyMethodsTest.Nested + return "maple" + def pie(self): + assert isinstance(self, PyMethodsTest.Nested) + return "pie" + +# For test_c_methods +class Subclass(tuple): + class Nested(str): + pass + +# For test_private_methods +class PrivateMethods: + def __init__(self, value): + self.value = value + + def __private_method(self): + return self.value + + def get_method(self): + return self.__private_method + + @classmethod + def get_unbound_method(cls): + return cls.__private_method + + @classmethod + def __private_classmethod(cls): + return 43 + + @classmethod + def get_classmethod(cls): + return cls.__private_classmethod + + @staticmethod + def __private_staticmethod(): + return 44 + + @classmethod + def get_staticmethod(cls): + return cls.__private_staticmethod + +# For test_private_nested_classes +class PrivateNestedClasses: + @classmethod + def get_nested(cls): + return cls.__Nested + + class __Nested: + @classmethod + def get_nested2(cls): + return cls.__Nested2 + + class __Nested2: + pass diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 9a3a26a8400844..9ba498ce8f575d 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -15,6 +15,7 @@ import types import unittest import weakref +import __main__ from textwrap import dedent from http.cookies import SimpleCookie @@ -26,13 +27,15 @@ from test import support from test.support import os_helper from test.support import ( - TestFailed, run_with_locales, no_tracing, + run_with_locales, no_tracing, _2G, _4G, bigmemtest ) from test.support.import_helper import forget from test.support.os_helper import TESTFN from test.support import threading_helper from test.support.warnings_helper import save_restore_warnings_filters +from test import picklecommon +from test.picklecommon import * from pickle import bytes_types @@ -54,6 +57,8 @@ # kind of outer loop. protocols = range(pickle.HIGHEST_PROTOCOL + 1) +FAST_NESTING_LIMIT = 50 + # Return True if opcode code appears in the pickle, else False. def opcode_in_pickle(code, pickle): @@ -74,6 +79,15 @@ def count_opcode(code, pickle): def identity(x): return x +def itersize(start, stop): + # Produce geometrical increasing sequence from start to stop + # (inclusively) for tests. + size = start + while size < stop: + yield size + size <<= 1 + yield stop + class UnseekableIO(io.BytesIO): def peek(self, *args): @@ -132,58 +146,6 @@ def restore(self): if pair is not None: copyreg.add_extension(pair[0], pair[1], code) -class C: - def __eq__(self, other): - return self.__dict__ == other.__dict__ - -class D(C): - def __init__(self, arg): - pass - -class E(C): - def __getinitargs__(self): - return () - -import __main__ -__main__.C = C -C.__module__ = "__main__" -__main__.D = D -D.__module__ = "__main__" -__main__.E = E -E.__module__ = "__main__" - -# Simple mutable object. -class Object: - pass - -# Hashable immutable key object containing unheshable mutable data. -class K: - def __init__(self, value): - self.value = value - - def __reduce__(self): - # Shouldn't support the recursion itself - return K, (self.value,) - -class myint(int): - def __init__(self, x): - self.str = str(x) - -class initarg(C): - - def __init__(self, a, b): - self.a = a - self.b = b - - def __getinitargs__(self): - return self.a, self.b - -class metaclass(type): - pass - -class use_metaclass(object, metaclass=metaclass): - pass - class pickling_metaclass(type): def __eq__(self, other): return (type(self) == type(other) and @@ -198,62 +160,6 @@ def create_dynamic_class(name, bases): return result -class ZeroCopyBytes(bytes): - readonly = True - c_contiguous = True - f_contiguous = True - zero_copy_reconstruct = True - - def __reduce_ex__(self, protocol): - if protocol >= 5: - return type(self)._reconstruct, (pickle.PickleBuffer(self),), None - else: - return type(self)._reconstruct, (bytes(self),) - - def __repr__(self): - return "{}({!r})".format(self.__class__.__name__, bytes(self)) - - __str__ = __repr__ - - @classmethod - def _reconstruct(cls, obj): - with memoryview(obj) as m: - obj = m.obj - if type(obj) is cls: - # Zero-copy - return obj - else: - return cls(obj) - - -class ZeroCopyBytearray(bytearray): - readonly = False - c_contiguous = True - f_contiguous = True - zero_copy_reconstruct = True - - def __reduce_ex__(self, protocol): - if protocol >= 5: - return type(self)._reconstruct, (pickle.PickleBuffer(self),), None - else: - return type(self)._reconstruct, (bytes(self),) - - def __repr__(self): - return "{}({!r})".format(self.__class__.__name__, bytes(self)) - - __str__ = __repr__ - - @classmethod - def _reconstruct(cls, obj): - with memoryview(obj) as m: - obj = m.obj - if type(obj) is cls: - # Zero-copy - return obj - else: - return cls(obj) - - if _testbuffer is not None: class PicklableNDArray: @@ -298,9 +204,10 @@ def __ne__(self, other): return not (self == other) def __repr__(self): - return (f"{type(self)}(shape={self.array.shape}," - f"strides={self.array.strides}, " - f"bytes={self.array.tobytes()})") + return ("{name}(shape={array.shape}," + "strides={array.strides}, " + "bytes={array.tobytes()})").format( + name=type(self).__name__, array=self.array.shape) def __reduce_ex__(self, protocol): if not self.array.contiguous: @@ -853,9 +760,8 @@ def assert_is_copy(self, obj, objcopy, msg=None): self.assertEqual(getattr(obj, slot, None), getattr(objcopy, slot, None), msg=msg) - def check_unpickling_error(self, errors, data): - with self.subTest(data=data), \ - self.assertRaises(errors): + def check_unpickling_error_strict(self, errors, data): + with self.assertRaises(errors): try: self.loads(data) except BaseException as exc: @@ -864,6 +770,10 @@ def check_unpickling_error(self, errors, data): (data, exc.__class__.__name__, exc)) raise + def check_unpickling_error(self, errors, data): + with self.subTest(data=data): + self.check_unpickling_error_strict(errors, data) + def test_load_from_data0(self): self.assert_is_copy(self._testdata, self.loads(DATA0)) @@ -1012,6 +922,16 @@ def test_constants(self): self.assertIs(self.loads(b'I01\n.'), True) self.assertIs(self.loads(b'I00\n.'), False) + def test_issue135241(self): + # C implementation should check for hardcoded values 00 and 01 + # when getting booleans from the INT opcode. Doing a str comparison + # to bypass truthy/falsy comparisons. These payloads should return + # 0, not False. + out1 = self.loads(b'I+0\n.') + self.assertEqual(str(out1), '0') + out2 = self.loads(b'I 0\n.') + self.assertEqual(str(out2), '0') + def test_zero_padded_integers(self): self.assertEqual(self.loads(b'I010\n.'), 10) self.assertEqual(self.loads(b'I-010\n.'), -10) @@ -1140,6 +1060,155 @@ def test_negative_32b_binput(self): dumped = b'\x80\x03X\x01\x00\x00\x00ar\xff\xff\xff\xff.' self.check_unpickling_error(ValueError, dumped) + def test_too_large_put(self): + # Test that PUT with large id does not cause allocation of + # too large memo table. The C implementation uses a dict-based memo + # for sparse indices (when idx > memo_len * 2) instead of allocating + # a massive array. This test verifies large sparse indices work without + # causing memory exhaustion. + # + # The following simple pickle creates an empty list, memoizes it + # using a large index, then loads it back on the stack, builds + # a tuple containing 2 identical empty lists and returns it. + data = lambda n: (b'((lp' + str(n).encode() + b'\n' + + b'g' + str(n).encode() + b'\nt.') + # 0: ( MARK + # 1: ( MARK + # 2: l LIST (MARK at 1) + # 3: p PUT 1000000000000 + # 18: g GET 1000000000000 + # 33: t TUPLE (MARK at 0) + # 34: . STOP + for idx in [10**6, 10**9, 10**12]: + if idx > sys.maxsize: + continue + self.assertEqual(self.loads(data(idx)), ([],)*2) + + def test_too_large_long_binput(self): + # Test that LONG_BINPUT with large id does not cause allocation of + # too large memo table. The C implementation uses a dict-based memo + # for sparse indices (when idx > memo_len * 2) instead of allocating + # a massive array. This test verifies large sparse indices work without + # causing memory exhaustion. + # + # The following simple pickle creates an empty list, memoizes it + # using a large index, then loads it back on the stack, builds + # a tuple containing 2 identical empty lists and returns it. + data = lambda n: (b'(]r' + struct.pack(' sys.maxsize')) + + def test_truncated_large_binunicode8(self): + data = lambda size: b'\x8d' + struct.pack('= 2: self.assertEqual(cm.exception.__notes__, [ 'when serializing tuple item 2', - 'when serializing test.pickletester.REX __new__ arguments', - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX __new__ arguments', + f'when serializing {REX.__module__}.REX object']) else: self.assertEqual(cm.exception.__notes__, [ 'when serializing tuple item 3', - 'when serializing test.pickletester.REX reconstructor arguments', - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX reconstructor arguments', + f'when serializing {REX.__module__}.REX object']) def test_bad_newobj_ex_args(self): obj = REX((copyreg.__newobj_ex__, ())) @@ -1832,7 +1924,7 @@ def test_bad_newobj_ex_args(self): 'not enough values to unpack (expected 3, got 0)', '__newobj_ex__ expected 3 arguments, got 0'}) self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) obj = REX((copyreg.__newobj_ex__, 42)) for proto in protocols[2:]: @@ -1843,7 +1935,7 @@ def test_bad_newobj_ex_args(self): 'second item of the tuple returned by __reduce__ ' 'must be a tuple, not int') self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) obj = REX((copyreg.__newobj_ex__, (REX, 42, {}))) if self.pickler is pickle._Pickler: @@ -1854,7 +1946,7 @@ def test_bad_newobj_ex_args(self): self.assertEqual(str(cm.exception), 'Value after * must be an iterable, not int') self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) else: for proto in protocols[2:]: with self.subTest(proto=proto): @@ -1863,7 +1955,7 @@ def test_bad_newobj_ex_args(self): self.assertEqual(str(cm.exception), 'second argument to __newobj_ex__() must be a tuple, not int') self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) obj = REX((copyreg.__newobj_ex__, (REX, (), []))) if self.pickler is pickle._Pickler: @@ -1872,9 +1964,9 @@ def test_bad_newobj_ex_args(self): with self.assertRaises(TypeError) as cm: self.dumps(obj, proto) self.assertEqual(str(cm.exception), - 'functools.partial() argument after ** must be a mapping, not list') + 'Value after ** must be a mapping, not list') self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) else: for proto in protocols[2:]: with self.subTest(proto=proto): @@ -1883,7 +1975,7 @@ def test_bad_newobj_ex_args(self): self.assertEqual(str(cm.exception), 'third argument to __newobj_ex__() must be a dict, not list') self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) def test_bad_newobj_ex__class(self): obj = REX((copyreg.__newobj_ex__, (NoNew(), (), {}))) @@ -1893,9 +1985,9 @@ def test_bad_newobj_ex__class(self): self.dumps(obj, proto) self.assertIn(str(cm.exception), { 'first argument to __newobj_ex__() has no __new__', - f'first argument to __newobj_ex__() must be a class, not {__name__}.NoNew'}) + f'first argument to __newobj_ex__() must be a class, not {NoNew.__module__}.NoNew'}) self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) def test_wrong_newobj_ex_class(self): if self.pickler is not pickle._Pickler: @@ -1908,7 +2000,7 @@ def test_wrong_newobj_ex_class(self): self.assertEqual(str(cm.exception), f'first argument to __newobj_ex__() must be {REX}, not {str}') self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) def test_unpickleable_newobj_ex_class(self): class LocalREX(REX): pass @@ -1944,22 +2036,22 @@ def test_unpickleable_newobj_ex_args(self): if proto >= 4: self.assertEqual(cm.exception.__notes__, [ 'when serializing tuple item 2', - 'when serializing test.pickletester.REX __new__ arguments', - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX __new__ arguments', + f'when serializing {REX.__module__}.REX object']) elif proto >= 2: self.assertEqual(cm.exception.__notes__, [ 'when serializing tuple item 3', 'when serializing tuple item 1', 'when serializing functools.partial state', 'when serializing functools.partial object', - 'when serializing test.pickletester.REX reconstructor', - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX reconstructor', + f'when serializing {REX.__module__}.REX object']) else: self.assertEqual(cm.exception.__notes__, [ 'when serializing tuple item 2', 'when serializing tuple item 1', - 'when serializing test.pickletester.REX reconstructor arguments', - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX reconstructor arguments', + f'when serializing {REX.__module__}.REX object']) def test_unpickleable_newobj_ex_kwargs(self): obj = REX((copyreg.__newobj_ex__, (REX, (), {'a': UNPICKLEABLE}))) @@ -1970,22 +2062,22 @@ def test_unpickleable_newobj_ex_kwargs(self): if proto >= 4: self.assertEqual(cm.exception.__notes__, [ "when serializing dict item 'a'", - 'when serializing test.pickletester.REX __new__ arguments', - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX __new__ arguments', + f'when serializing {REX.__module__}.REX object']) elif proto >= 2: self.assertEqual(cm.exception.__notes__, [ "when serializing dict item 'a'", 'when serializing tuple item 2', 'when serializing functools.partial state', 'when serializing functools.partial object', - 'when serializing test.pickletester.REX reconstructor', - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX reconstructor', + f'when serializing {REX.__module__}.REX object']) else: self.assertEqual(cm.exception.__notes__, [ "when serializing dict item 'a'", 'when serializing tuple item 2', - 'when serializing test.pickletester.REX reconstructor arguments', - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX reconstructor arguments', + f'when serializing {REX.__module__}.REX object']) def test_unpickleable_state(self): obj = REX_state(UNPICKLEABLE) @@ -1994,8 +2086,8 @@ def test_unpickleable_state(self): with self.assertRaises(CustomError) as cm: self.dumps(obj, proto) self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX_state state', - 'when serializing test.pickletester.REX_state object']) + f'when serializing {REX_state.__module__}.REX_state state', + f'when serializing {REX_state.__module__}.REX_state object']) def test_bad_state_setter(self): if self.pickler is pickle._Pickler: @@ -2009,7 +2101,7 @@ def test_bad_state_setter(self): 'sixth item of the tuple returned by __reduce__ ' 'must be callable, not int') self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) def test_unpickleable_state_setter(self): obj = REX((print, (), 'state', None, None, UnpickleableCallable())) @@ -2018,8 +2110,8 @@ def test_unpickleable_state_setter(self): with self.assertRaises(CustomError) as cm: self.dumps(obj, proto) self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX state setter', - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX state setter', + f'when serializing {REX.__module__}.REX object']) def test_unpickleable_state_with_state_setter(self): obj = REX((print, (), UNPICKLEABLE, None, None, print)) @@ -2028,8 +2120,8 @@ def test_unpickleable_state_with_state_setter(self): with self.assertRaises(CustomError) as cm: self.dumps(obj, proto) self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX state', - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX state', + f'when serializing {REX.__module__}.REX object']) def test_bad_object_list_items(self): # Issue4176: crash when 4th and 5th items of __reduce__() @@ -2044,7 +2136,7 @@ def test_bad_object_list_items(self): 'fourth item of the tuple returned by __reduce__ ' 'must be an iterator, not int'}) self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) if self.pickler is not pickle._Pickler: # Python implementation is less strict and also accepts iterables. @@ -2057,7 +2149,7 @@ def test_bad_object_list_items(self): 'fourth item of the tuple returned by __reduce__ ' 'must be an iterator, not int') self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) def test_unpickleable_object_list_items(self): obj = REX_six([1, 2, UNPICKLEABLE]) @@ -2066,8 +2158,8 @@ def test_unpickleable_object_list_items(self): with self.assertRaises(CustomError) as cm: self.dumps(obj, proto) self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX_six item 2', - 'when serializing test.pickletester.REX_six object']) + f'when serializing {REX_six.__module__}.REX_six item 2', + f'when serializing {REX_six.__module__}.REX_six object']) def test_bad_object_dict_items(self): # Issue4176: crash when 4th and 5th items of __reduce__() @@ -2082,7 +2174,7 @@ def test_bad_object_dict_items(self): 'fifth item of the tuple returned by __reduce__ ' 'must be an iterator, not int'}) self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) for proto in protocols: obj = REX((dict, (), None, None, iter([('a',)]))) @@ -2093,7 +2185,7 @@ def test_bad_object_dict_items(self): 'not enough values to unpack (expected 2, got 1)', 'dict items iterator must return 2-tuples'}) self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) if self.pickler is not pickle._Pickler: # Python implementation is less strict and also accepts iterables. @@ -2105,7 +2197,7 @@ def test_bad_object_dict_items(self): self.assertEqual(str(cm.exception), 'dict items iterator must return 2-tuples') self.assertEqual(cm.exception.__notes__, [ - 'when serializing test.pickletester.REX object']) + f'when serializing {REX.__module__}.REX object']) def test_unpickleable_object_dict_items(self): obj = REX_seven({'a': UNPICKLEABLE}) @@ -2114,8 +2206,8 @@ def test_unpickleable_object_dict_items(self): with self.assertRaises(CustomError) as cm: self.dumps(obj, proto) self.assertEqual(cm.exception.__notes__, [ - "when serializing test.pickletester.REX_seven item 'a'", - 'when serializing test.pickletester.REX_seven object']) + f"when serializing {REX_seven.__module__}.REX_seven item 'a'", + f'when serializing {REX_seven.__module__}.REX_seven object']) def test_unpickleable_list_items(self): obj = [1, [2, 3, UNPICKLEABLE]] @@ -2208,15 +2300,15 @@ def test_unpickleable_frozenset_items(self): def test_global_lookup_error(self): # Global name does not exist obj = REX('spam') - obj.__module__ = __name__ + obj.__module__ = 'test.picklecommon' for proto in protocols: with self.subTest(proto=proto): with self.assertRaises(pickle.PicklingError) as cm: self.dumps(obj, proto) self.assertEqual(str(cm.exception), - f"Can't pickle {obj!r}: it's not found as {__name__}.spam") + f"Can't pickle {obj!r}: it's not found as test.picklecommon.spam") self.assertEqual(str(cm.exception.__context__), - f"module '{__name__}' has no attribute 'spam'") + "module 'test.picklecommon' has no attribute 'spam'") obj.__module__ = 'nonexisting' for proto in protocols: @@ -2371,6 +2463,7 @@ def test_reduce_None(self): with self.assertRaises(TypeError): self.dumps(c) + @support.skip_if_unlimited_stack_size @no_tracing def test_bad_getattr(self): # Issue #3514: crash when there is an infinite loop in __getattr__ @@ -2437,7 +2530,7 @@ def persistent_id(self, obj): def test_bad_ext_code(self): # This should never happen in normal circumstances, because the type # and the value of the extension code is checked in copyreg.add_extension(). - key = (__name__, 'MyList') + key = (MyList.__module__, 'MyList') def check(code, exc): assert key not in copyreg._extension_registry assert code not in copyreg._inverted_registry @@ -2459,6 +2552,7 @@ def check(code, exc): class AbstractPickleTests: # Subclass must define self.dumps, self.loads. + py_version = sys.version_info # for test_xpickle optimized = False _testdata = AbstractUnpickleTests._testdata @@ -2471,24 +2565,33 @@ def setUp(self): def test_misc(self): # test various datatypes not tested by testdata for proto in protocols: - x = myint(4) - s = self.dumps(x, proto) - y = self.loads(s) - self.assert_is_copy(x, y) + with self.subTest('myint', proto=proto): + if self.py_version < (3, 0) and proto < 2: + self.skipTest('int subclasses are not interoperable with Python 2') + x = myint(4) + s = self.dumps(x, proto) + y = self.loads(s) + self.assert_is_copy(x, y) - x = (1, ()) - s = self.dumps(x, proto) - y = self.loads(s) - self.assert_is_copy(x, y) + with self.subTest('tuple', proto=proto): + x = (1, ()) + s = self.dumps(x, proto) + y = self.loads(s) + self.assert_is_copy(x, y) - x = initarg(1, x) - s = self.dumps(x, proto) - y = self.loads(s) - self.assert_is_copy(x, y) + with self.subTest('initarg', proto=proto): + if self.py_version < (3, 0): + self.skipTest('"classic" classes are not interoperable with Python 2') + x = initarg(1, x) + s = self.dumps(x, proto) + y = self.loads(s) + self.assert_is_copy(x, y) # XXX test __reduce__ protocol? def test_roundtrip_equality(self): + if self.py_version < (3, 0): + self.skipTest('"classic" classes are not interoperable with Python 2') expected = self._testdata for proto in protocols: s = self.dumps(expected, proto) @@ -2685,6 +2788,8 @@ def test_recursive_tuple_and_dict_like_key(self): self._test_recursive_tuple_and_dict_key(REX_seven, asdict=lambda x: x.table) def test_recursive_set(self): + if self.py_version < (3, 4): + self.skipTest('not supported in Python < 3.4') # Set containing an immutable object containing the original set. y = set() y.add(K(y)) @@ -2708,6 +2813,8 @@ def test_recursive_set(self): def test_recursive_inst(self): # Mutable object containing itself. + if self.py_version < (3, 0): + self.skipTest('"classic" classes are not interoperable with Python 2') i = Object() i.attr = i for proto in protocols: @@ -2718,6 +2825,8 @@ def test_recursive_inst(self): self.assertIs(x.attr, x) def test_recursive_multi(self): + if self.py_version < (3, 0): + self.skipTest('"classic" classes are not interoperable with Python 2') l = [] d = {1:l} i = Object() @@ -2732,39 +2841,56 @@ def test_recursive_multi(self): self.assertEqual(list(x[0].attr.keys()), [1]) self.assertIs(x[0].attr[1], x) - def _test_recursive_collection_and_inst(self, factory): + def _test_recursive_collection_and_inst(self, factory, oldminproto=None, + minprotocol=0): + if self.py_version < (3, 0): + self.skipTest('"classic" classes are not interoperable with Python 2') # Mutable object containing a collection containing the original # object. + protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1) o = Object() o.attr = factory([o]) t = type(o.attr) - for proto in protocols: - s = self.dumps(o, proto) - x = self.loads(s) - self.assertIsInstance(x.attr, t) - self.assertEqual(len(x.attr), 1) - self.assertIsInstance(list(x.attr)[0], Object) - self.assertIs(list(x.attr)[0], x) + with self.subTest('obj -> {t.__name__} -> obj'): + for proto in protocols: + with self.subTest(proto=proto): + s = self.dumps(o, proto) + x = self.loads(s) + self.assertIsInstance(x.attr, t) + self.assertEqual(len(x.attr), 1) + self.assertIsInstance(list(x.attr)[0], Object) + self.assertIs(list(x.attr)[0], x) # Collection containing a mutable object containing the original # collection. o = o.attr - for proto in protocols: - s = self.dumps(o, proto) - x = self.loads(s) - self.assertIsInstance(x, t) - self.assertEqual(len(x), 1) - self.assertIsInstance(list(x)[0], Object) - self.assertIs(list(x)[0].attr, x) + with self.subTest(f'{t.__name__} -> obj -> {t.__name__}'): + if self.py_version < (3, 4) and oldminproto is None: + self.skipTest('not supported in Python < 3.4') + for proto in protocols: + with self.subTest(proto=proto): + if self.py_version < (3, 4) and proto < oldminproto: + self.skipTest(f'requires protocol {oldminproto} in Python < 3.4') + s = self.dumps(o, proto) + x = self.loads(s) + self.assertIsInstance(x, t) + self.assertEqual(len(x), 1) + self.assertIsInstance(list(x)[0], Object) + self.assertIs(list(x)[0].attr, x) def test_recursive_list_and_inst(self): - self._test_recursive_collection_and_inst(list) + self._test_recursive_collection_and_inst(list, oldminproto=0) def test_recursive_tuple_and_inst(self): - self._test_recursive_collection_and_inst(tuple) + self._test_recursive_collection_and_inst(tuple, oldminproto=0) def test_recursive_dict_and_inst(self): - self._test_recursive_collection_and_inst(dict.fromkeys) + self._test_recursive_collection_and_inst(dict.fromkeys, oldminproto=0) + + def test_recursive_frozendict_and_inst(self): + if self.py_version < (3, 15): + self.skipTest('need frozendict') + self._test_recursive_collection_and_inst(frozendict.fromkeys, minprotocol=2) def test_recursive_set_and_inst(self): self._test_recursive_collection_and_inst(set) @@ -2773,13 +2899,13 @@ def test_recursive_frozenset_and_inst(self): self._test_recursive_collection_and_inst(frozenset) def test_recursive_list_subclass_and_inst(self): - self._test_recursive_collection_and_inst(MyList) + self._test_recursive_collection_and_inst(MyList, oldminproto=2) def test_recursive_tuple_subclass_and_inst(self): self._test_recursive_collection_and_inst(MyTuple) def test_recursive_dict_subclass_and_inst(self): - self._test_recursive_collection_and_inst(MyDict.fromkeys) + self._test_recursive_collection_and_inst(MyDict.fromkeys, oldminproto=2) def test_recursive_set_subclass_and_inst(self): self._test_recursive_collection_and_inst(MySet) @@ -2787,6 +2913,50 @@ def test_recursive_set_subclass_and_inst(self): def test_recursive_frozenset_subclass_and_inst(self): self._test_recursive_collection_and_inst(MyFrozenSet) + def _test_recursive_collection_in_key(self, factory, minprotocol=0): + protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1) + key = Object() + o = factory({key: 1}) + key.attr = o + for proto in protocols: + with self.subTest(proto=proto): + s = self.dumps(o, proto) + x = self.loads(s) + keys = list(x.keys()) + self.assertEqual(len(keys), 1) + self.assertIs(keys[0].attr, x) + + def test_recursive_frozendict_in_key(self): + if self.py_version < (3, 15): + self.skipTest('need frozendict') + self._test_recursive_collection_in_key(frozendict, minprotocol=2) + + def test_recursive_frozendict_subclass_in_key(self): + if self.py_version < (3, 15): + self.skipTest('need frozendict') + self._test_recursive_collection_in_key(MyFrozenDict) + + def _test_recursive_collection_in_value(self, factory, minprotocol=0): + protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1) + o = factory(key=[]) + o['key'].append(o) + for proto in protocols: + with self.subTest(proto=proto): + s = self.dumps(o, proto) + x = self.loads(s) + self.assertEqual(len(x['key']), 1) + self.assertIs(x['key'][0], x) + + def test_recursive_frozendict_in_value(self): + if self.py_version < (3, 15): + self.skipTest('need frozendict') + self._test_recursive_collection_in_value(frozendict, minprotocol=2) + + def test_recursive_frozendict_subclass_in_value(self): + if self.py_version < (3, 15): + self.skipTest('need frozendict') + self._test_recursive_collection_in_value(MyFrozenDict) + def test_recursive_inst_state(self): # Mutable object containing itself. y = REX_state() @@ -2839,6 +3009,8 @@ def test_unicode_high_plane(self): def test_unicode_memoization(self): # Repeated str is re-used (even when escapes added). + if self.py_version < (3, 0): + self.skipTest('not supported in Python < 3.0') for proto in protocols: for s in '', 'xyz', 'xyz\n', 'x\\yz', 'x\xa1yz\r': p = self.dumps((s, s), proto) @@ -2858,23 +3030,27 @@ def test_bytes(self): self.assert_is_copy(s, self.loads(p)) def test_bytes_memoization(self): + array_types = [bytes] + if self.py_version >= (3, 4): + array_types += [ZeroCopyBytes] for proto in protocols: - for array_type in [bytes, ZeroCopyBytes]: + for array_type in array_types: for s in b'', b'xyz', b'xyz'*100: + b = array_type(s) + expected = (b, b) if self.py_version >= (3, 0) else (b.decode(),)*2 with self.subTest(proto=proto, array_type=array_type, s=s, independent=False): - b = array_type(s) p = self.dumps((b, b), proto) x, y = self.loads(p) self.assertIs(x, y) - self.assert_is_copy((b, b), (x, y)) + self.assert_is_copy(expected, (x, y)) + b2 = array_type(s) with self.subTest(proto=proto, array_type=array_type, s=s, independent=True): - b1, b2 = array_type(s), array_type(s) - p = self.dumps((b1, b2), proto) - # Note that (b1, b2) = self.loads(p) might have identical - # components, i.e., b1 is b2, but this is not always the + p = self.dumps((b, b2), proto) + # Note that (b, b2) = self.loads(p) might have identical + # components, i.e., b is b2, but this is not always the # case if the content is large (equality still holds). - self.assert_is_copy((b1, b2), self.loads(p)) + self.assert_is_copy(expected, self.loads(p)) def test_bytearray(self): for proto in protocols: @@ -2896,8 +3072,11 @@ def test_bytearray(self): self.assertTrue(opcode_in_pickle(pickle.BYTEARRAY8, p)) def test_bytearray_memoization(self): + array_types = [bytearray] + if self.py_version >= (3, 4): + array_types += [ZeroCopyBytearray] for proto in protocols: - for array_type in [bytearray, ZeroCopyBytearray]: + for array_type in array_types: for s in b'', b'xyz', b'xyz'*100: with self.subTest(proto=proto, array_type=array_type, s=s, independent=False): b = array_type(s) @@ -2921,6 +3100,51 @@ def test_bytearray_memoization(self): self.assertIsNot(b2a, b2b) self.assert_is_copy(b2a, b2b) + def test_picklebuffer_memoization(self): + if self.py_version < (3, 8): + self.skipTest('not supported in Python < 3.8') + array_types = [bytes, bytearray] + for proto in range(5, pickle.HIGHEST_PROTOCOL + 1): + for array_type in array_types: + for s in b'', b'xyz', b'xyz'*100: + with self.subTest(proto=proto, array_type=array_type, s=s, independent=False): + b = pickle.PickleBuffer(array_type(s)) + p = self.dumps((b, b), proto) + b1, b2 = self.loads(p) + self.assertIs(b1, b2) + + with self.subTest(proto=proto, array_type=array_type, s=s, independent=True): + b = array_type(s) + b1a = pickle.PickleBuffer(b) + b2a = pickle.PickleBuffer(b) + p = self.dumps((b1a, b2a), proto) + b1b, b2b = self.loads(p) + if array_type is not bytes: + self.assertIsNot(b1b, b2b) + self.assert_is_copy(b1b, b) + self.assert_is_copy(b2b, b) + + def test_empty_picklebuffer_memoization(self): + # gh-148914: Empty writable PickleBuffer memoized an empty bytearray + # with the id of b'' (a singleton in CPython). + if self.py_version < (3, 8): + self.skipTest('not supported in Python < 3.8') + for proto in range(5, pickle.HIGHEST_PROTOCOL + 1): + for readonly in False, True: + with self.subTest(proto=proto, readonly=readonly): + b = b'' + ba = bytearray() + buf = pickle.PickleBuffer(b if readonly else ba) + p = self.dumps((buf, b, ba), proto) + buf, b, ba = self.loads(p) + array_type = bytes if readonly else bytearray + self.assertIsInstance(buf, array_type) + self.assertIsInstance(b, bytes) + self.assertIsInstance(ba, bytearray) + self.assertEqual(buf, b'') + self.assertEqual(b, b'') + self.assertEqual(ba, b'') + def test_ints(self): for proto in protocols: n = sys.maxsize @@ -2971,12 +3195,17 @@ def test_float_format(self): def test_reduce(self): for proto in protocols: - inst = AAA() - dumped = self.dumps(inst, proto) - loaded = self.loads(dumped) - self.assertEqual(loaded, REDUCE_A) + with self.subTest(proto=proto): + if self.py_version < (3, 4) and proto < 3: + self.skipTest('str is not interoperable with Python < 3.4') + inst = AAA() + dumped = self.dumps(inst, proto) + loaded = self.loads(dumped) + self.assertEqual(loaded, REDUCE_A) def test_getinitargs(self): + if self.py_version < (3, 0): + self.skipTest('"classic" classes are not interoperable with Python 2') for proto in protocols: inst = initarg(1, 2) dumped = self.dumps(inst, proto) @@ -2984,6 +3213,7 @@ def test_getinitargs(self): self.assert_is_copy(inst, loaded) def test_metaclass(self): + self.assertEqual(type(use_metaclass), metaclass) a = use_metaclass() for proto in protocols: s = self.dumps(a, proto) @@ -3008,6 +3238,10 @@ def test_structseq(self): s = self.dumps(t, proto) u = self.loads(s) self.assert_is_copy(t, u) + if self.py_version < (3, 4): + # module 'os' has no attributes '_make_stat_result' and + # '_make_statvfs_result' + continue t = os.stat(os.curdir) s = self.dumps(t, proto) u = self.loads(s) @@ -3019,52 +3253,115 @@ def test_structseq(self): self.assert_is_copy(t, u) def test_ellipsis(self): + if self.py_version < (3, 3): + self.skipTest('not supported in Python < 3.3') for proto in protocols: - s = self.dumps(..., proto) - u = self.loads(s) - self.assertIs(..., u) + with self.subTest(proto=proto): + s = self.dumps(..., proto) + u = self.loads(s) + self.assertIs(..., u) def test_notimplemented(self): + if self.py_version < (3, 3): + self.skipTest('not supported in Python < 3.3') for proto in protocols: - s = self.dumps(NotImplemented, proto) - u = self.loads(s) - self.assertIs(NotImplemented, u) + with self.subTest(proto=proto): + s = self.dumps(NotImplemented, proto) + u = self.loads(s) + self.assertIs(NotImplemented, u) def test_singleton_types(self): # Issue #6477: Test that types of built-in singletons can be pickled. + if self.py_version < (3, 3): + self.skipTest('not supported in Python < 3.3') singletons = [None, ..., NotImplemented] for singleton in singletons: + t = type(singleton) for proto in protocols: - s = self.dumps(type(singleton), proto) - u = self.loads(s) - self.assertIs(type(singleton), u) + with self.subTest(name=t.__name__, proto=proto): + s = self.dumps(t, proto) + u = self.loads(s) + self.assertIs(t, u) def test_builtin_types(self): + new_names = { + 'bytes': (3, 0), + 'BuiltinImporter': (3, 3), + 'str': (3, 4), # not interoperable with Python < 3.4 + 'frozendict': (3, 15), + 'sentinel': (3, 15), + } for t in builtins.__dict__.values(): if isinstance(t, type) and not issubclass(t, BaseException): + if t.__name__ in new_names and self.py_version < new_names[t.__name__]: + continue for proto in protocols: - s = self.dumps(t, proto) - self.assertIs(self.loads(s), t) + with self.subTest(name=t.__name__, proto=proto): + s = self.dumps(t, proto) + self.assertIs(self.loads(s), t) def test_builtin_exceptions(self): + new_names = { + 'BlockingIOError': (3, 3), + 'BrokenPipeError': (3, 3), + 'ChildProcessError': (3, 3), + 'ConnectionError': (3, 3), + 'ConnectionAbortedError': (3, 3), + 'ConnectionRefusedError': (3, 3), + 'ConnectionResetError': (3, 3), + 'FileExistsError': (3, 3), + 'FileNotFoundError': (3, 3), + 'InterruptedError': (3, 3), + 'IsADirectoryError': (3, 3), + 'NotADirectoryError': (3, 3), + 'PermissionError': (3, 3), + 'ProcessLookupError': (3, 3), + 'TimeoutError': (3, 3), + 'RecursionError': (3, 5), + 'StopAsyncIteration': (3, 5), + 'ModuleNotFoundError': (3, 6), + 'EncodingWarning': (3, 10), + 'BaseExceptionGroup': (3, 11), + 'ExceptionGroup': (3, 11), + '_IncompleteInputError': (3, 13), + 'PythonFinalizationError': (3, 13), + 'ImportCycleError': (3, 15), + } for t in builtins.__dict__.values(): if isinstance(t, type) and issubclass(t, BaseException): + if t.__name__ in new_names and self.py_version < new_names[t.__name__]: + continue for proto in protocols: - s = self.dumps(t, proto) - u = self.loads(s) - if proto <= 2 and issubclass(t, OSError) and t is not BlockingIOError: - self.assertIs(u, OSError) - elif proto <= 2 and issubclass(t, ImportError): - self.assertIs(u, ImportError) - else: - self.assertIs(u, t) + with self.subTest(name=t.__name__, proto=proto): + if self.py_version < (3, 3) and proto < 3: + self.skipTest('exception classes are not interoperable with Python < 3.3') + s = self.dumps(t, proto) + u = self.loads(s) + if proto <= 2 and issubclass(t, OSError) and t is not BlockingIOError: + self.assertIs(u, OSError) + elif proto <= 2 and issubclass(t, ImportError): + self.assertIs(u, ImportError) + else: + self.assertIs(u, t) def test_builtin_functions(self): + new_names = { + '__build_class__': (3, 0), + 'ascii': (3, 0), + 'exec': (3, 0), + 'breakpoint': (3, 7), + 'aiter': (3, 10), + 'anext': (3, 10), + '__lazy_import__': (3, 15), + } for t in builtins.__dict__.values(): if isinstance(t, types.BuiltinFunctionType): + if t.__name__ in new_names and self.py_version < new_names[t.__name__]: + continue for proto in protocols: - s = self.dumps(t, proto) - self.assertIs(self.loads(s), t) + with self.subTest(name=t.__name__, proto=proto): + s = self.dumps(t, proto) + self.assertIs(self.loads(s), t) # Tests for protocol 2 @@ -3077,6 +3374,9 @@ def test_proto(self): else: self.assertEqual(count_opcode(pickle.PROTO, pickled), 0) + def test_bad_proto(self): + if self.py_version < (3, 8): + self.skipTest('no protocol validation in Python < 3.8') oob = protocols[-1] + 1 # a future protocol build_none = pickle.NONE + pickle.STOP badpickle = pickle.PROTO + bytes([oob]) + build_none @@ -3188,57 +3488,69 @@ def test_newobj_list(self): def test_newobj_generic(self): for proto in protocols: for C in myclasses: - B = C.__base__ - x = C(C.sample) - x.foo = 42 - s = self.dumps(x, proto) - y = self.loads(s) - detail = (proto, C, B, x, y, type(y)) - self.assert_is_copy(x, y) # XXX revisit - self.assertEqual(B(x), B(y), detail) - self.assertEqual(x.__dict__, y.__dict__, detail) + with self.subTest(proto=proto, C=C): + if self.py_version < (3, 0) and proto < 2 and C in (MyInt, MyStr): + self.skipTest('int and str subclasses are not interoperable with Python 2') + if (3, 0) <= self.py_version < (3, 4) and proto < 2 and C in (MyStr, MyUnicode): + self.skipTest('str subclasses are not interoperable with Python < 3.4') + if self.py_version < (3, 15) and C == MyFrozenDict: + self.skipTest('frozendict is not available on Python < 3.15') + B = C.__base__ + x = C(C.sample) + x.foo = 42 + s = self.dumps(x, proto) + y = self.loads(s) + detail = (proto, C, B, x, y, type(y)) + self.assert_is_copy(x, y) # XXX revisit + self.assertEqual(B(x), B(y), detail) + self.assertEqual(x.__dict__, y.__dict__, detail) def test_newobj_proxies(self): # NEWOBJ should use the __class__ rather than the raw type classes = myclasses[:] # Cannot create weakproxies to these classes - for c in (MyInt, MyTuple): + for c in (MyInt, MyLong, MyTuple): classes.remove(c) for proto in protocols: for C in classes: - B = C.__base__ - x = C(C.sample) - x.foo = 42 - p = weakref.proxy(x) - s = self.dumps(p, proto) - y = self.loads(s) - self.assertEqual(type(y), type(x)) # rather than type(p) - detail = (proto, C, B, x, y, type(y)) - self.assertEqual(B(x), B(y), detail) - self.assertEqual(x.__dict__, y.__dict__, detail) + with self.subTest(proto=proto, C=C): + if self.py_version < (3, 4) and proto < 3 and C in (MyStr, MyUnicode): + self.skipTest('str subclasses are not interoperable with Python < 3.4') + if self.py_version < (3, 15) and C == MyFrozenDict: + self.skipTest('frozendict is not available on Python < 3.15') + B = C.__base__ + x = C(C.sample) + x.foo = 42 + p = weakref.proxy(x) + s = self.dumps(p, proto) + y = self.loads(s) + self.assertEqual(type(y), type(x)) # rather than type(p) + detail = (proto, C, B, x, y, type(y)) + self.assertEqual(B(x), B(y), detail) + self.assertEqual(x.__dict__, y.__dict__, detail) def test_newobj_overridden_new(self): # Test that Python class with C implemented __new__ is pickleable for proto in protocols: - x = MyIntWithNew2(1) - x.foo = 42 - s = self.dumps(x, proto) - y = self.loads(s) - self.assertIs(type(y), MyIntWithNew2) - self.assertEqual(int(y), 1) - self.assertEqual(y.foo, 42) + with self.subTest(proto=proto): + if self.py_version < (3, 0) and proto < 2: + self.skipTest('int subclasses are not interoperable with Python 2') + x = MyIntWithNew2(1) + x.foo = 42 + s = self.dumps(x, proto) + y = self.loads(s) + self.assertIs(type(y), MyIntWithNew2) + self.assertEqual(int(y), 1) + self.assertEqual(y.foo, 42) def test_newobj_not_class(self): # Issue 24552 - global SimpleNewObj - save = SimpleNewObj + if self.py_version < (3, 4): + self.skipTest('not supported in Python < 3.4') o = SimpleNewObj.__new__(SimpleNewObj) b = self.dumps(o, 4) - try: - SimpleNewObj = 42 + with support.swap_attr(picklecommon, 'SimpleNewObj', 42): self.assertRaises((TypeError, pickle.UnpicklingError), self.loads, b) - finally: - SimpleNewObj = save # Register a type with copyreg, with extension code extcode. Pickle # an object of that type. Check that the resulting pickle uses opcode @@ -3247,14 +3559,14 @@ def test_newobj_not_class(self): def produce_global_ext(self, extcode, opcode): e = ExtensionSaver(extcode) try: - copyreg.add_extension(__name__, "MyList", extcode) + copyreg.add_extension(MyList.__module__, "MyList", extcode) x = MyList([1, 2, 3]) x.foo = 42 x.bar = "hello" # Dump using protocol 1 for comparison. s1 = self.dumps(x, 1) - self.assertIn(__name__.encode("utf-8"), s1) + self.assertIn(MyList.__module__.encode(), s1) self.assertIn(b"MyList", s1) self.assertFalse(opcode_in_pickle(opcode, s1)) @@ -3263,7 +3575,7 @@ def produce_global_ext(self, extcode, opcode): # Dump using protocol 2 for test. s2 = self.dumps(x, 2) - self.assertNotIn(__name__.encode("utf-8"), s2) + self.assertNotIn(MyList.__module__.encode(), s2) self.assertNotIn(b"MyList", s2) self.assertEqual(opcode_in_pickle(opcode, s2), True, repr(s2)) @@ -3361,14 +3673,20 @@ def test_simple_newobj(self): x.abc = 666 for proto in protocols: with self.subTest(proto=proto): + if self.py_version < (3, 0) and proto < 2: + self.skipTest('int subclasses are not interoperable with Python 2') s = self.dumps(x, proto) if proto < 1: - self.assertIn(b'\nI64206', s) # INT + if self.py_version >= (3, 7): + self.assertIn(b'\nI64206', s) # INT + else: # for test_xpickle + self.assertIn(b'64206', s) # INT or LONG else: self.assertIn(b'M\xce\xfa', s) # BININT2 - self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s), - 2 <= proto) - self.assertFalse(opcode_in_pickle(pickle.NEWOBJ_EX, s)) + if not (self.py_version < (3, 5) and proto == 4): + self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s), + 2 <= proto) + self.assertFalse(opcode_in_pickle(pickle.NEWOBJ_EX, s)) y = self.loads(s) # will raise TypeError if __init__ called self.assert_is_copy(x, y) @@ -3377,29 +3695,45 @@ def test_complex_newobj(self): x.abc = 666 for proto in protocols: with self.subTest(proto=proto): + if self.py_version < (3, 0) and proto < 2: + self.skipTest('int subclasses are not interoperable with Python 2') s = self.dumps(x, proto) if proto < 1: - self.assertIn(b'\nI64206', s) # INT + if self.py_version >= (3, 7): + self.assertIn(b'\nI64206', s) # INT + else: # for test_xpickle + self.assertIn(b'64206', s) # INT or LONG elif proto < 2: self.assertIn(b'M\xce\xfa', s) # BININT2 elif proto < 4: - self.assertIn(b'X\x04\x00\x00\x00FACE', s) # BINUNICODE + if self.py_version >= (3, 0): + self.assertIn(b'X\x04\x00\x00\x00FACE', s) # BINUNICODE + else: # for test_xpickle + self.assertIn(b'U\x04FACE', s) # SHORT_BINSTRING else: self.assertIn(b'\x8c\x04FACE', s) # SHORT_BINUNICODE - self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s), - 2 <= proto) - self.assertFalse(opcode_in_pickle(pickle.NEWOBJ_EX, s)) + if not (self.py_version < (3, 5) and proto == 4): + self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s), + 2 <= proto) + self.assertFalse(opcode_in_pickle(pickle.NEWOBJ_EX, s)) y = self.loads(s) # will raise TypeError if __init__ called self.assert_is_copy(x, y) def test_complex_newobj_ex(self): + if self.py_version < (3, 4): + self.skipTest('not supported in Python < 3.4') x = ComplexNewObjEx.__new__(ComplexNewObjEx, 0xface) # avoid __init__ x.abc = 666 for proto in protocols: with self.subTest(proto=proto): + if self.py_version < (3, 6) and proto < 4: + self.skipTest('requires protocol 4 in Python < 3.6') s = self.dumps(x, proto) if proto < 1: - self.assertIn(b'\nI64206', s) # INT + if self.py_version >= (3, 7): + self.assertIn(b'\nI64206', s) # INT + else: # for test_xpickle + self.assertIn(b'64206', s) # INT or LONG elif proto < 2: self.assertIn(b'M\xce\xfa', s) # BININT2 elif proto < 4: @@ -3487,6 +3821,8 @@ def test_many_puts_and_gets(self): def test_attribute_name_interning(self): # Test that attribute names of pickled objects are interned when # unpickling. + if self.py_version < (3, 0): + self.skipTest('"classic" classes are not interoperable with Python 2') for proto in protocols: x = C() x.foo = 42 @@ -3516,10 +3852,14 @@ def test_large_pickles(self): dumped = self.dumps(data, proto) loaded = self.loads(dumped) self.assertEqual(len(loaded), len(data)) + if self.py_version < (3, 0): + data = (1, min, 'xy' * (30 * 1024), len) self.assertEqual(loaded, data) def test_int_pickling_efficiency(self): # Test compacity of int representation (see issue #12744) + if self.py_version < (3, 3): + self.skipTest('not supported in Python < 3.3') for proto in protocols: with self.subTest(proto=proto): pickles = [self.dumps(2**n, proto) for n in range(70)] @@ -3540,10 +3880,13 @@ def test_appends_on_non_lists(self): # Issue #17720 obj = REX_six([1, 2, 3]) for proto in protocols: - if proto == 0: - self._check_pickling_with_opcode(obj, pickle.APPEND, proto) - else: - self._check_pickling_with_opcode(obj, pickle.APPENDS, proto) + with self.subTest(proto=proto): + if proto == 0: + self._check_pickling_with_opcode(obj, pickle.APPEND, proto) + else: + if self.py_version < (3, 0): + self.skipTest('not supported in Python 2') + self._check_pickling_with_opcode(obj, pickle.APPENDS, proto) def test_setitems_on_non_dicts(self): obj = REX_seven({1: -1, 2: -2, 3: -3}) @@ -3608,6 +3951,8 @@ def check_frame_opcodes(self, pickled): @support.skip_if_pgo_task @support.requires_resource('cpu') def test_framing_many_objects(self): + if self.py_version < (3, 4): + self.skipTest('not supported in Python < 3.4') obj = list(range(10**5)) for proto in range(4, pickle.HIGHEST_PROTOCOL + 1): with self.subTest(proto=proto): @@ -3623,6 +3968,8 @@ def test_framing_many_objects(self): self.check_frame_opcodes(pickled) def test_framing_large_objects(self): + if self.py_version < (3, 4): + self.skipTest('not supported in Python < 3.4') N = 1024 * 1024 small_items = [[i] for i in range(10)] obj = [b'x' * N, *small_items, b'y' * N, 'z' * N] @@ -3648,15 +3995,16 @@ def test_framing_large_objects(self): [len(x) for x in unpickled]) # Perform full equality check if the lengths match. self.assertEqual(obj, unpickled) - n_frames = count_opcode(pickle.FRAME, pickled) - # A single frame for small objects between - # first two large objects. - self.assertEqual(n_frames, 1) - self.check_frame_opcodes(pickled) + if self.py_version >= (3, 7): + n_frames = count_opcode(pickle.FRAME, pickled) + # A single frame for small objects between + # first two large objects. + self.assertEqual(n_frames, 1) + self.check_frame_opcodes(pickled) def test_optional_frames(self): - if pickle.HIGHEST_PROTOCOL < 4: - return + if self.py_version < (3, 4): + self.skipTest('not supported in Python < 3.4') def remove_frames(pickled, keep_frame=None): """Remove frame opcodes from the given pickle.""" @@ -3698,6 +4046,9 @@ def remove_frames(pickled, keep_frame=None): @support.skip_if_pgo_task def test_framed_write_sizes_with_delayed_writer(self): + if self.py_version < (3, 4): + self.skipTest('not supported in Python < 3.4') + class ChunkAccumulator: """Accumulate pickler output in a list of raw chunks.""" def __init__(self): @@ -3763,13 +4114,12 @@ def concatenate_chunks(self): chunk_sizes) def test_nested_names(self): - global Nested - class Nested: - class A: - class B: - class C: - pass + if self.py_version < (3, 4): + self.skipTest('not supported in Python < 3.4') + # required protocol 4 in Python 3.4 for proto in range(pickle.HIGHEST_PROTOCOL + 1): + if self.py_version < (3, 5) and proto < 4: + continue for obj in [Nested.A, Nested.A.B, Nested.A.B.C]: with self.subTest(proto=proto, obj=obj): unpickled = self.loads(self.dumps(obj, proto)) @@ -3800,35 +4150,21 @@ class Recursive: del Recursive.ref # break reference loop def test_py_methods(self): - global PyMethodsTest - class PyMethodsTest: - @staticmethod - def cheese(): - return "cheese" - @classmethod - def wine(cls): - assert cls is PyMethodsTest - return "wine" - def biscuits(self): - assert isinstance(self, PyMethodsTest) - return "biscuits" - class Nested: - "Nested class" - @staticmethod - def ketchup(): - return "ketchup" - @classmethod - def maple(cls): - assert cls is PyMethodsTest.Nested - return "maple" - def pie(self): - assert isinstance(self, PyMethodsTest.Nested) - return "pie" - + if self.py_version < (3, 4): + self.skipTest('not supported in Python < 3.4') py_methods = ( - PyMethodsTest.cheese, PyMethodsTest.wine, PyMethodsTest().biscuits, + ) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + for method in py_methods: + with self.subTest(proto=proto, method=method): + unpickled = self.loads(self.dumps(method, proto)) + self.assertEqual(method(), unpickled()) + + # required protocol 4 in Python 3.4 + py_methods = ( + PyMethodsTest.cheese, PyMethodsTest.Nested.ketchup, PyMethodsTest.Nested.maple, PyMethodsTest.Nested().pie @@ -3838,6 +4174,8 @@ def pie(self): (PyMethodsTest.Nested.pie, PyMethodsTest.Nested) ) for proto in range(pickle.HIGHEST_PROTOCOL + 1): + if self.py_version < (3, 5) and proto < 4: + continue for method in py_methods: with self.subTest(proto=proto, method=method): unpickled = self.loads(self.dumps(method, proto)) @@ -3858,11 +4196,8 @@ def pie(self): self.assertRaises(TypeError, self.dumps, descr, proto) def test_c_methods(self): - global Subclass - class Subclass(tuple): - class Nested(str): - pass - + if self.py_version < (3, 4): + self.skipTest('not supported in Python < 3.4') c_methods = ( # bound built-in method ("abcd".index, ("c",)), @@ -3883,7 +4218,6 @@ class Nested(str): # subclass methods (Subclass([1,2,2]).count, (2,)), (Subclass.count, (Subclass([1,2,2]), 2)), - (Subclass.Nested("sweet").count, ("e",)), (Subclass.Nested.count, (Subclass.Nested("sweet"), "e")), ) for proto in range(pickle.HIGHEST_PROTOCOL + 1): @@ -3892,6 +4226,18 @@ class Nested(str): unpickled = self.loads(self.dumps(method, proto)) self.assertEqual(method(*args), unpickled(*args)) + # required protocol 4 in Python 3.4 + c_methods = ( + (Subclass.Nested("sweet").count, ("e",)), + ) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + if self.py_version < (3, 5) and proto < 4: + continue + for method, args in c_methods: + with self.subTest(proto=proto, method=method): + unpickled = self.loads(self.dumps(method, proto)) + self.assertEqual(method(*args), unpickled(*args)) + descriptors = ( bytearray.__dict__['maketrans'], # built-in static method descriptor dict.__dict__['fromkeys'], # built-in class method descriptor @@ -3901,7 +4247,112 @@ class Nested(str): with self.subTest(proto=proto, descr=descr): self.assertRaises(TypeError, self.dumps, descr, proto) + def test_private_methods(self): + if self.py_version < (3, 15): + self.skipTest('not supported in Python < 3.15') + obj = PrivateMethods(42) + for proto in protocols: + with self.subTest(proto=proto): + unpickled = self.loads(self.dumps(obj.get_method(), proto)) + self.assertEqual(unpickled(), 42) + unpickled = self.loads(self.dumps(obj.get_unbound_method(), proto)) + self.assertEqual(unpickled(obj), 42) + unpickled = self.loads(self.dumps(obj.get_classmethod(), proto)) + self.assertEqual(unpickled(), 43) + unpickled = self.loads(self.dumps(obj.get_staticmethod(), proto)) + self.assertEqual(unpickled(), 44) + + def test_private_nested_classes(self): + if self.py_version < (3, 15): + self.skipTest('not supported in Python < 3.15') + cls1 = PrivateNestedClasses.get_nested() + cls2 = cls1.get_nested2() + for proto in protocols: + with self.subTest(proto=proto): + unpickled = self.loads(self.dumps(cls1, proto)) + self.assertIs(unpickled, cls1) + unpickled = self.loads(self.dumps(cls2, proto)) + self.assertIs(unpickled, cls2) + + def test_object_with_attrs(self): + obj = Object() + obj.a = 1 + for proto in protocols: + with self.subTest(proto=proto): + unpickled = self.loads(self.dumps(obj, proto)) + self.assertEqual(unpickled.a, obj.a) + + def test_object_with_slots(self): + obj = WithSlots() + obj.a = 1 + self.assertRaises(TypeError, self.dumps, obj, 0) + self.assertRaises(TypeError, self.dumps, obj, 1) + for proto in protocols[2:]: + with self.subTest(proto=proto): + unpickled = self.loads(self.dumps(obj, proto)) + self.assertEqual(unpickled.a, obj.a) + self.assertNotHasAttr(unpickled, 'b') + + obj = WithSlotsSubclass() + obj.a = 1 + obj.c = 2 + self.assertRaises(TypeError, self.dumps, obj, 0) + self.assertRaises(TypeError, self.dumps, obj, 1) + for proto in protocols[2:]: + with self.subTest(proto=proto): + unpickled = self.loads(self.dumps(obj, proto)) + self.assertEqual(unpickled.a, obj.a) + self.assertEqual(unpickled.c, obj.c) + self.assertNotHasAttr(unpickled, 'b') + + obj = WithSlotsAndDict() + obj.a = 1 + obj.c = 2 + self.assertRaises(TypeError, self.dumps, obj, 0) + self.assertRaises(TypeError, self.dumps, obj, 1) + for proto in protocols[2:]: + with self.subTest(proto=proto): + unpickled = self.loads(self.dumps(obj, proto)) + self.assertEqual(unpickled.a, obj.a) + self.assertEqual(unpickled.c, obj.c) + self.assertEqual(unpickled.__dict__, obj.__dict__) + self.assertNotHasAttr(unpickled, 'b') + + def test_object_with_private_attrs(self): + obj = WithPrivateAttrs(1) + for proto in protocols: + with self.subTest(proto=proto): + unpickled = self.loads(self.dumps(obj, proto)) + self.assertEqual(unpickled.get(), obj.get()) + + obj = WithPrivateAttrsSubclass(1, 2) + for proto in protocols: + with self.subTest(proto=proto): + unpickled = self.loads(self.dumps(obj, proto)) + self.assertEqual(unpickled.get(), obj.get()) + self.assertEqual(unpickled.get2(), obj.get2()) + + def test_object_with_private_slots(self): + obj = WithPrivateSlots(1) + self.assertRaises(TypeError, self.dumps, obj, 0) + self.assertRaises(TypeError, self.dumps, obj, 1) + for proto in protocols[2:]: + with self.subTest(proto=proto): + unpickled = self.loads(self.dumps(obj, proto)) + self.assertEqual(unpickled.get(), obj.get()) + + obj = WithPrivateSlotsSubclass(1, 2) + self.assertRaises(TypeError, self.dumps, obj, 0) + self.assertRaises(TypeError, self.dumps, obj, 1) + for proto in protocols[2:]: + with self.subTest(proto=proto): + unpickled = self.loads(self.dumps(obj, proto)) + self.assertEqual(unpickled.get(), obj.get()) + self.assertEqual(unpickled.get2(), obj.get2()) + def test_compat_pickle(self): + if self.py_version < (3, 4): + self.skipTest("doesn't work in Python < 3.4'") tests = [ (range(1, 7), '__builtin__', 'xrange'), (map(int, '123'), 'itertools', 'imap'), @@ -4090,6 +4541,35 @@ def check_array(arr): # 2-D, non-contiguous check_array(arr[::2]) + def test_concurrent_mutation_in_buffer_with_bytearray(self): + def factory(): + s = b"a" * 16 + return bytearray(s), s + self.do_test_concurrent_mutation_in_buffer_callback(factory) + + def test_concurrent_mutation_in_buffer_with_memoryview(self): + def factory(): + obj = memoryview(b"a" * 32)[10:26] + sub = b"a" * len(obj) + return obj, sub + self.do_test_concurrent_mutation_in_buffer_callback(factory) + + def do_test_concurrent_mutation_in_buffer_callback(self, factory): + # See: https://github.com/python/cpython/issues/143308. + class R: + def __bool__(self): + buf.release() + return True + + for proto in range(5, pickle.HIGHEST_PROTOCOL + 1): + obj, sub = factory() + buf = pickle.PickleBuffer(obj) + buffer_callback = lambda _: R() + + with self.subTest(proto=proto, obj=obj, sub=sub): + res = self.dumps(buf, proto, buffer_callback=buffer_callback) + self.assertIn(sub, res) + def test_evil_class_mutating_dict(self): # https://github.com/python/cpython/issues/92930 from random import getrandbits @@ -4120,6 +4600,94 @@ def __reduce__(self): expected = "changed size during iteration" self.assertIn(expected, str(e)) + def fast_save_enter(self, create_data, minprotocol=0): + # gh-146059: Check that fast_save_leave() is called when + # fast_save_enter() is called. + if not hasattr(self, "pickler"): + self.skipTest("need Pickler class") + + data = [create_data(i) for i in range(FAST_NESTING_LIMIT * 2)] + protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1) + for proto in protocols: + with self.subTest(proto=proto): + buf = io.BytesIO() + pickler = self.pickler(buf, protocol=proto) + # Enable fast mode (disables memo, enables cycle detection) + pickler.fast = 1 + pickler.dump(data) + + buf.seek(0) + data2 = self.unpickler(buf).load() + self.assertEqual(data2, data) + + def test_fast_save_enter_tuple(self): + self.fast_save_enter(lambda i: (i,)) + + def test_fast_save_enter_list(self): + self.fast_save_enter(lambda i: [i]) + + def test_fast_save_enter_frozenset(self): + self.fast_save_enter(lambda i: frozenset([i])) + + def test_fast_save_enter_set(self): + self.fast_save_enter(lambda i: set([i])) + + def test_fast_save_enter_frozendict(self): + if self.py_version < (3, 15): + self.skipTest('need frozendict') + self.fast_save_enter(lambda i: frozendict(key=i), minprotocol=2) + + def test_fast_save_enter_dict(self): + self.fast_save_enter(lambda i: {"key": i}) + + def deep_nested_struct(self, create_nested, + minprotocol=0, compare_equal=True, + depth=FAST_NESTING_LIMIT * 2): + # gh-146059: Check that fast_save_leave() is called when + # fast_save_enter() is called. + if not hasattr(self, "pickler"): + self.skipTest("need Pickler class") + + data = None + for i in range(depth): + data = create_nested(data) + protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1) + for proto in protocols: + with self.subTest(proto=proto): + buf = io.BytesIO() + pickler = self.pickler(buf, protocol=proto) + # Enable fast mode (disables memo, enables cycle detection) + pickler.fast = 1 + pickler.dump(data) + + buf.seek(0) + data2 = self.unpickler(buf).load() + if compare_equal: + self.assertEqual(data2, data) + + def test_deep_nested_struct_tuple(self): + self.deep_nested_struct(lambda data: (data,)) + + def test_deep_nested_struct_list(self): + self.deep_nested_struct(lambda data: [data]) + + def test_deep_nested_struct_frozenset(self): + self.deep_nested_struct(lambda data: frozenset((1, data))) + + def test_deep_nested_struct_set(self): + self.deep_nested_struct(lambda data: {K(data)}, + depth=FAST_NESTING_LIMIT+1, + compare_equal=False) + + def test_deep_nested_struct_frozendict(self): + if self.py_version < (3, 15): + self.skipTest('need frozendict') + self.deep_nested_struct(lambda data: frozendict(x=data), + minprotocol=2) + + def test_deep_nested_struct_dict(self): + self.deep_nested_struct(lambda data: {'x': data}) + class BigmemPickleTests: @@ -4248,110 +4816,6 @@ def test_huge_str_64b(self, size): data = None -# Test classes for reduce_ex - -class R: - def __init__(self, reduce=None): - self.reduce = reduce - def __reduce__(self, proto): - return self.reduce - -class REX: - def __init__(self, reduce_ex=None): - self.reduce_ex = reduce_ex - def __reduce_ex__(self, proto): - return self.reduce_ex - -class REX_one(object): - """No __reduce_ex__ here, but inheriting it from object""" - _reduce_called = 0 - def __reduce__(self): - self._reduce_called = 1 - return REX_one, () - -class REX_two(object): - """No __reduce__ here, but inheriting it from object""" - _proto = None - def __reduce_ex__(self, proto): - self._proto = proto - return REX_two, () - -class REX_three(object): - _proto = None - def __reduce_ex__(self, proto): - self._proto = proto - return REX_two, () - def __reduce__(self): - raise TestFailed("This __reduce__ shouldn't be called") - -class REX_four(object): - """Calling base class method should succeed""" - _proto = None - def __reduce_ex__(self, proto): - self._proto = proto - return object.__reduce_ex__(self, proto) - -class REX_five(object): - """This one used to fail with infinite recursion""" - _reduce_called = 0 - def __reduce__(self): - self._reduce_called = 1 - return object.__reduce__(self) - -class REX_six(object): - """This class is used to check the 4th argument (list iterator) of - the reduce protocol. - """ - def __init__(self, items=None): - self.items = items if items is not None else [] - def __eq__(self, other): - return type(self) is type(other) and self.items == other.items - def append(self, item): - self.items.append(item) - def __reduce__(self): - return type(self), (), None, iter(self.items), None - -class REX_seven(object): - """This class is used to check the 5th argument (dict iterator) of - the reduce protocol. - """ - def __init__(self, table=None): - self.table = table if table is not None else {} - def __eq__(self, other): - return type(self) is type(other) and self.table == other.table - def __setitem__(self, key, value): - self.table[key] = value - def __reduce__(self): - return type(self), (), None, None, iter(self.table.items()) - -class REX_state(object): - """This class is used to check the 3th argument (state) of - the reduce protocol. - """ - def __init__(self, state=None): - self.state = state - def __eq__(self, other): - return type(self) is type(other) and self.state == other.state - def __setstate__(self, state): - self.state = state - def __reduce__(self): - return type(self), (), self.state - -class REX_None: - """ Setting __reduce_ex__ to None should fail """ - __reduce_ex__ = None - -class R_None: - """ Setting __reduce__ to None should fail """ - __reduce__ = None - -class C_None_setstate: - """ Setting __setstate__ to None should fail """ - def __getstate__(self): - return 1 - - __setstate__ = None - class CustomError(Exception): pass @@ -4361,80 +4825,17 @@ def __reduce__(self): UNPICKLEABLE = Unpickleable() +# For test_unpickleable_reconstructor and test_unpickleable_state_setter class UnpickleableCallable(Unpickleable): def __call__(self, *args, **kwargs): pass - -# Test classes for newobj - -class MyInt(int): - sample = 1 - -class MyFloat(float): - sample = 1.0 - -class MyComplex(complex): - sample = 1.0 + 0.0j - -class MyStr(str): - sample = "hello" - -class MyUnicode(str): - sample = "hello \u1234" - -class MyTuple(tuple): - sample = (1, 2, 3) - -class MyList(list): - sample = [1, 2, 3] - -class MyDict(dict): - sample = {"a": 1, "b": 2} - -class MySet(set): - sample = {"a", "b"} - -class MyFrozenSet(frozenset): - sample = frozenset({"a", "b"}) - -myclasses = [MyInt, MyFloat, - MyComplex, - MyStr, MyUnicode, - MyTuple, MyList, MyDict, MySet, MyFrozenSet] - -class MyIntWithNew(int): - def __new__(cls, value): - raise AssertionError - -class MyIntWithNew2(MyIntWithNew): - __new__ = int.__new__ - - -class SlotList(MyList): - __slots__ = ["foo"] - -# Ruff "redefined while unused" false positive here due to `global` variables -# being assigned (and then restored) from within test methods earlier in the file -class SimpleNewObj(int): # noqa: F811 - def __init__(self, *args, **kwargs): - # raise an error, to make sure this isn't called - raise TypeError("SimpleNewObj.__init__() didn't expect to get called") - def __eq__(self, other): - return int(self) == int(other) and self.__dict__ == other.__dict__ - -class ComplexNewObj(SimpleNewObj): - def __getnewargs__(self): - return ('%X' % self, 16) - -class ComplexNewObjEx(SimpleNewObj): - def __getnewargs_ex__(self): - return ('%X' % self,), {'base': 16} - +# For test_bad_getattr class BadGetattr: def __getattr__(self, key): self.foo +# For test_bad_newobj_class and test_bad_newobj_ex__class class NoNew: def __getattribute__(self, name): if name == '__new__': diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index e8718decf6d6e9..7f735d75b318e7 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -291,6 +291,8 @@ def format_groups(groups): "DISTUTILS_USE_SDK", "DYLD_LIBRARY_PATH", "ENSUREPIP_OPTIONS", + "FORCE_COLOR", + "GITHUB_ACTIONS", "HISTORY_FILE", "HOME", "HOMEDRIVE", @@ -307,11 +309,13 @@ def format_groups(groups): "MAKEFLAGS", "MIXERDEV", "MSSDK", + "NO_COLOR", "PATH", "PATHEXT", "PIP_CONFIG_FILE", "PLAT", "POSIXLY_CORRECT", + "PYTHON_COLORS", "PY_SAX_PARSER", "ProgramFiles", "ProgramFiles(x86)", @@ -524,6 +528,7 @@ def collect_sysconfig(info_add): 'PY_CFLAGS', 'PY_CFLAGS_NODIST', 'PY_CORE_LDFLAGS', + 'PY_CORE_EXE_LDFLAGS', 'PY_LDFLAGS', 'PY_LDFLAGS_NODIST', 'PY_STDMODULE_CFLAGS', @@ -534,6 +539,7 @@ def collect_sysconfig(info_add): 'SHELL', 'SOABI', 'TEST_MODULES', + 'VAPTH', 'abs_builddir', 'abs_srcdir', 'prefix', @@ -745,6 +751,10 @@ def collect_test_socket(info_add): if name.startswith('HAVE_')] copy_attributes(info_add, test_socket, 'test_socket.%s', attributes) + # Get IOCTL_VM_SOCKETS_GET_LOCAL_CID of /dev/vsock + cid = test_socket.get_cid() + info_add('test_socket.get_cid', cid) + def collect_support(info_add): try: @@ -1093,9 +1103,9 @@ def collect_info(info): def dump_info(info, file=None): - title = "Python debug information" + title = "Python build information" print(title) - print("=" * len(title)) + print("#" * len(title)) print() infos = info.get_infos() diff --git a/Lib/test/seq_tests.py b/Lib/test/seq_tests.py index a41970d8f3f55a..b7875fe8f2f75d 100644 --- a/Lib/test/seq_tests.py +++ b/Lib/test/seq_tests.py @@ -261,23 +261,20 @@ def test_minmax(self): self.assertEqual(min(u), 0) self.assertEqual(max(u), 2) - def test_addmul(self): + def test_add(self): u1 = self.type2test([0]) u2 = self.type2test([0, 1]) self.assertEqual(u1, u1 + self.type2test()) self.assertEqual(u1, self.type2test() + u1) self.assertEqual(u1 + self.type2test([1]), u2) self.assertEqual(self.type2test([-1]) + u1, self.type2test([-1, 0])) - self.assertEqual(self.type2test(), u2*0) - self.assertEqual(self.type2test(), 0*u2) + + def test_mul(self): + u2 = self.type2test([0, 1]) self.assertEqual(self.type2test(), u2*0) self.assertEqual(self.type2test(), 0*u2) self.assertEqual(u2, u2*1) self.assertEqual(u2, 1*u2) - self.assertEqual(u2, u2*1) - self.assertEqual(u2, 1*u2) - self.assertEqual(u2+u2, u2*2) - self.assertEqual(u2+u2, 2*u2) self.assertEqual(u2+u2, u2*2) self.assertEqual(u2+u2, 2*u2) self.assertEqual(u2+u2+u2, u2*3) @@ -286,8 +283,9 @@ def test_addmul(self): class subclass(self.type2test): pass u3 = subclass([0, 1]) - self.assertEqual(u3, u3*1) - self.assertIsNot(u3, u3*1) + r = u3*1 + self.assertEqual(r, u3) + self.assertIsNot(r, u3) def test_iadd(self): u = self.type2test([0, 1]) @@ -348,6 +346,21 @@ def test_subscript(self): self.assertRaises(ValueError, a.__getitem__, slice(0, 10, 0)) self.assertRaises(TypeError, a.__getitem__, 'x') + def _assert_cmp(self, a, b, r): + self.assertIs(a == b, r == 0) + self.assertIs(a != b, r != 0) + self.assertIs(a > b, r > 0) + self.assertIs(a <= b, r <= 0) + self.assertIs(a < b, r < 0) + self.assertIs(a >= b, r >= 0) + + def test_cmp(self): + a = self.type2test([0, 1]) + self._assert_cmp(a, a, 0) + self._assert_cmp(a, self.type2test([0, 1]), 0) + self._assert_cmp(a, self.type2test([0]), 1) + self._assert_cmp(a, self.type2test([0, 2]), -1) + def test_count(self): a = self.type2test([0, 1, 2])*3 self.assertEqual(a.count(0), 3) diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py index 1814a55b74ea0c..f66051531faaa1 100644 --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -90,6 +90,55 @@ def checkcall(self, obj, methodname, *args): args = self.fixtype(args) getattr(obj, methodname)(*args) + def _get_teststrings(self, charset, digits): + base = len(charset) + teststrings = set() + for i in range(base ** digits): + entry = [] + for j in range(digits): + i, m = divmod(i, base) + entry.append(charset[m]) + teststrings.add(''.join(entry)) + teststrings = [self.fixtype(ts) for ts in teststrings] + return teststrings + + def test_add(self): + s = self.fixtype('ab') + self.assertEqual(s + self.fixtype(''), s) + self.assertEqual(self.fixtype('') + s, s) + self.assertEqual(s + self.fixtype('cd'), self.fixtype('abcd')) + + def test_mul(self): + s = self.fixtype('ab') + self.assertEqual(s*0, self.fixtype('')) + self.assertEqual(0*s, self.fixtype('')) + self.assertEqual(s*1, s) + self.assertEqual(1*s, s) + self.assertEqual(s*2, self.fixtype('abab')) + self.assertEqual(2*s, self.fixtype('abab')) + + class subclass(self.type2test): + pass + s = subclass(self.fixtype('ab')) + r = s*1 + self.assertEqual(r, s) + self.assertIsNot(r, s) + + def _assert_cmp(self, a, b, r): + self.assertIs(a == b, r == 0) + self.assertIs(a != b, r != 0) + self.assertIs(a > b, r > 0) + self.assertIs(a <= b, r <= 0) + self.assertIs(a < b, r < 0) + self.assertIs(a >= b, r >= 0) + + def test_cmp(self): + a = self.fixtype('ab') + self._assert_cmp(a, a, 0) + self._assert_cmp(a, self.fixtype('ab'), 0) + self._assert_cmp(a, self.fixtype('a'), 1) + self._assert_cmp(a, self.fixtype('ac'), -1) + def test_count(self): self.checkequal(3, 'aaa', 'count', 'a') self.checkequal(0, 'aaa', 'count', 'b') @@ -130,17 +179,7 @@ def test_count(self): # For a variety of combinations, # verify that str.count() matches an equivalent function # replacing all occurrences and then differencing the string lengths - charset = ['', 'a', 'b'] - digits = 7 - base = len(charset) - teststrings = set() - for i in range(base ** digits): - entry = [] - for j in range(digits): - i, m = divmod(i, base) - entry.append(charset[m]) - teststrings.add(''.join(entry)) - teststrings = [self.fixtype(ts) for ts in teststrings] + teststrings = self._get_teststrings(['', 'a', 'b'], 7) for i in teststrings: n = len(i) for j in teststrings: @@ -197,17 +236,7 @@ def test_find(self): # For a variety of combinations, # verify that str.find() matches __contains__ # and that the found substring is really at that location - charset = ['', 'a', 'b', 'c'] - digits = 5 - base = len(charset) - teststrings = set() - for i in range(base ** digits): - entry = [] - for j in range(digits): - i, m = divmod(i, base) - entry.append(charset[m]) - teststrings.add(''.join(entry)) - teststrings = [self.fixtype(ts) for ts in teststrings] + teststrings = self._get_teststrings(['', 'a', 'b', 'c'], 5) for i in teststrings: for j in teststrings: loc = i.find(j) @@ -244,17 +273,7 @@ def test_rfind(self): # For a variety of combinations, # verify that str.rfind() matches __contains__ # and that the found substring is really at that location - charset = ['', 'a', 'b', 'c'] - digits = 5 - base = len(charset) - teststrings = set() - for i in range(base ** digits): - entry = [] - for j in range(digits): - i, m = divmod(i, base) - entry.append(charset[m]) - teststrings.add(''.join(entry)) - teststrings = [self.fixtype(ts) for ts in teststrings] + teststrings = self._get_teststrings(['', 'a', 'b', 'c'], 5) for i in teststrings: for j in teststrings: loc = i.rfind(j) @@ -295,6 +314,19 @@ def test_index(self): else: self.checkraises(TypeError, 'hello', 'index', 42) + # For a variety of combinations, + # verify that str.index() matches __contains__ + # and that the found substring is really at that location + teststrings = self._get_teststrings(['', 'a', 'b', 'c'], 5) + for i in teststrings: + for j in teststrings: + if j in i: + loc = i.index(j) + self.assertGreaterEqual(loc, 0) + self.assertEqual(i[loc:loc+len(j)], j) + else: + self.assertRaises(ValueError, i.index, j) + def test_rindex(self): self.checkequal(12, 'abcdefghiabc', 'rindex', '') self.checkequal(3, 'abcdefghiabc', 'rindex', 'def') @@ -321,6 +353,19 @@ def test_rindex(self): else: self.checkraises(TypeError, 'hello', 'rindex', 42) + # For a variety of combinations, + # verify that str.rindex() matches __contains__ + # and that the found substring is really at that location + teststrings = self._get_teststrings(['', 'a', 'b', 'c'], 5) + for i in teststrings: + for j in teststrings: + if j in i: + loc = i.rindex(j) + self.assertGreaterEqual(loc, 0) + self.assertEqual(i[loc:loc+len(j)], j) + else: + self.assertRaises(ValueError, i.rindex, j) + def test_find_periodic_pattern(self): """Cover the special path for periodic patterns.""" def reference_find(p, s): @@ -1296,6 +1341,7 @@ def test_extended_getslice(self): slice(start, stop, step)) def test_mul(self): + super().test_mul() self.checkequal('', 'abc', '__mul__', -1) self.checkequal('', 'abc', '__mul__', 0) self.checkequal('abc', 'abc', '__mul__', 1) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 100438bf71d3a6..cd85ef60a80f4b 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -30,7 +30,8 @@ "record_original_stdout", "get_original_stdout", "captured_stdout", "captured_stdin", "captured_stderr", "captured_output", # unittest - "is_resource_enabled", "requires", "requires_freebsd_version", + "is_resource_enabled", "get_resource_value", "requires", "requires_resource", + "requires_freebsd_version", "requires_gil_enabled", "requires_linux_version", "requires_mac_ver", "check_syntax_error", "requires_gzip", "requires_bz2", "requires_lzma", "requires_zstd", @@ -39,10 +40,12 @@ "has_fork_support", "requires_fork", "has_subprocess_support", "requires_subprocess", "has_socket_support", "requires_working_socket", + "has_remote_subprocess_debugging", "requires_remote_subprocess_debugging", "anticipate_failure", "load_package_tests", "detect_api_mismatch", "check__all__", "skip_if_buggy_ucrt_strfptime", "check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer", "requires_limited_api", "requires_specialization", "thread_unsafe", + "skip_if_unlimited_stack_size", # sys "MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi", "is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval", @@ -68,7 +71,9 @@ "BrokenIter", "in_systemd_nspawn_sync_suppressed", "run_no_yield_async_fn", "run_yielding_async_fn", "async_yield", - "reset_code", + "reset_code", "on_github_actions", + "requires_root_user", "requires_non_root_user", + "skip_if_double_rounding", ] @@ -184,7 +189,7 @@ def get_attribute(obj, name): return attribute verbose = 1 # Flag set to 0 by regrtest.py -use_resources = None # Flag set to [] by regrtest.py +use_resources = None # Flag set to {} by regrtest.py max_memuse = 0 # Disable bigmem tests (they will still be run with # small sizes, to make sure they work.) real_max_memuse = 0 @@ -299,6 +304,16 @@ def is_resource_enabled(resource): """ return use_resources is None or resource in use_resources +def get_resource_value(resource): + """Test whether a resource is enabled. + + Known resources are set by regrtest.py. If not running under regrtest.py, + all resources are assumed enabled unless use_resources has been set. + """ + if use_resources is None: + return None + return use_resources.get(resource) + def requires(resource, msg=None): """Raise ResourceDenied if the specified resource is not available.""" if not is_resource_enabled(resource): @@ -500,6 +515,15 @@ def dec(*args, **kwargs): float.__getformat__("double").startswith("IEEE"), "test requires IEEE 754 doubles") +# detect evidence of double-rounding: +x, y = 1e16, 2.9999 # use temporary values to defeat peephole optimizer +HAVE_DOUBLE_ROUNDING = (x + y == 1e16 + 4) +skip_if_double_rounding = unittest.skipIf(HAVE_DOUBLE_ROUNDING, + "accuracy not guaranteed on " + "machines with double rounding") +del x, y, HAVE_DOUBLE_ROUNDING + + def requires_zlib(reason='requires zlib'): try: import zlib @@ -541,10 +565,14 @@ def has_no_debug_ranges(): except ImportError: raise unittest.SkipTest("_testinternalcapi required") return not _testcapi.config_get('code_debug_ranges') - return not bool(config['code_debug_ranges']) def requires_debug_ranges(reason='requires co_positions / debug_ranges'): - return unittest.skipIf(has_no_debug_ranges(), reason) + try: + skip = has_no_debug_ranges() + except unittest.SkipTest as e: + skip = True + reason = e.args[0] if e.args else reason + return unittest.skipIf(skip, reason) MS_WINDOWS = (sys.platform == 'win32') @@ -629,6 +657,93 @@ def requires_working_socket(*, module=False): else: return unittest.skipUnless(has_socket_support, msg) + +@functools.cache +def has_remote_subprocess_debugging(): + """Check if we have permissions to debug subprocesses remotely. + + Returns True if we have permissions, False if we don't. + Checks for: + - Platform support (Linux, macOS, Windows only) + - On Linux: process_vm_readv support + - _remote_debugging module availability + - Actual subprocess debugging permissions (e.g., macOS entitlements) + Result is cached. + """ + # Check platform support + if sys.platform not in ("linux", "darwin", "win32"): + return False + + try: + import _remote_debugging + except ImportError: + return False + + # On Linux, check for process_vm_readv support + if sys.platform == "linux": + if not getattr(_remote_debugging, "PROCESS_VM_READV_SUPPORTED", False): + return False + + # First check if we can read our own process + if not _remote_debugging.is_python_process(os.getpid()): + return False + + # Check subprocess access - debugging child processes may require + # additional permissions depending on platform security settings + import socket + import subprocess + + # Create a socket for child to signal readiness + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server.bind(("127.0.0.1", 0)) + server.listen(1) + port = server.getsockname()[1] + + # Child connects to signal it's ready, then waits for parent to close + child_code = f""" +import socket +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.connect(("127.0.0.1", {port})) +s.recv(1) # Wait for parent to signal done +""" + proc = subprocess.Popen( + [sys.executable, "-c", child_code], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + try: + server.settimeout(5.0) + conn, _ = server.accept() + # Child is ready, test if we can probe it + result = _remote_debugging.is_python_process(proc.pid) + # Check if subprocess is still alive after probing + if proc.poll() is not None: + return False + conn.close() # Signal child to exit + return result + except (socket.timeout, OSError): + return False + finally: + server.close() + proc.kill() + proc.wait() + + +def requires_remote_subprocess_debugging(): + """Skip tests that require remote subprocess debugging permissions. + + This also implies subprocess support, so no need to use both + @requires_subprocess() and @requires_remote_subprocess_debugging(). + """ + if not has_subprocess_support: + return unittest.skip("requires subprocess support") + return unittest.skipUnless( + has_remote_subprocess_debugging(), + "requires remote subprocess debugging permissions" + ) + + # Does strftime() support glibc extension like '%4Y'? has_strftime_extensions = False if sys.platform != "win32": @@ -1126,29 +1241,20 @@ class _MemoryWatchdog: """ def __init__(self): - self.procfile = '/proc/{pid}/statm'.format(pid=os.getpid()) self.started = False def start(self): - try: - f = open(self.procfile, 'r') - except OSError as e: - logging.getLogger(__name__).warning('/proc not available for stats: %s', e, exc_info=e) - sys.stderr.flush() - return - import subprocess - with f: - watchdog_script = findfile("memory_watchdog.py") - self.mem_watchdog = subprocess.Popen([sys.executable, watchdog_script], - stdin=f, - stderr=subprocess.DEVNULL) + watchdog_script = findfile("memory_watchdog.py") + cmd = [sys.executable, watchdog_script, str(os.getpid())] + self.mem_watchdog = subprocess.Popen(cmd) self.started = True def stop(self): - if self.started: - self.mem_watchdog.terminate() - self.mem_watchdog.wait() + if not self.started: + return + self.mem_watchdog.terminate() + self.mem_watchdog.wait() def bigmemtest(size, memuse, dry_run=True): @@ -1181,8 +1287,8 @@ def wrapper(self): if real_max_memuse and verbose: print() - print(" ... expected peak memory use: {peak:.1f}G" - .format(peak=size * memuse / (1024 ** 3))) + peak = (size * memuse) / (1024 ** 3) + print(f" ... expected peak memory use: {peak:.1f} GiB") watchdog = _MemoryWatchdog() watchdog.start() else: @@ -1282,7 +1388,7 @@ def trace_wrapper(*args, **kwargs): sys.settrace(original_trace) coverage_wrapper = trace_wrapper - if 'test.cov' in sys.modules: # -Xpresite=test.cov used + if 'test.cov' in sys.modules: # -Xpresite=test.cov:enable used cov = sys.monitoring.COVERAGE_ID @functools.wraps(func) def coverage_wrapper(*args, **kwargs): @@ -1345,16 +1451,12 @@ def requires_specialization(test): _opcode.ENABLE_SPECIALIZATION, "requires specialization")(test) -def requires_specialization_ft(test): - return unittest.skipUnless( - _opcode.ENABLE_SPECIALIZATION_FT, "requires specialization")(test) - - def reset_code(f: types.FunctionType) -> types.FunctionType: """Clear all specializations, local instrumentation, and JIT code for the given function.""" f.__code__ = f.__code__.replace() return f +on_github_actions = "GITHUB_ACTIONS" in os.environ #======================================================================= # Check for the presence of docstrings. @@ -1606,9 +1708,10 @@ def _platform_specific(self): )) self._env = {k.upper(): os.getenv(k) for k in os.environ} - self._env["PYTHONHOME"] = os.path.dirname(self.real) + home = os.path.dirname(self.real) if sysconfig.is_python_build(): - self._env["PYTHONPATH"] = STDLIB_DIR + home = os.path.join(home, sysconfig.get_config_var('VPATH')) + self._env["PYTHONHOME"] = home else: def _platform_specific(self): pass @@ -1657,6 +1760,25 @@ def skip_if_pgo_task(test): return test if ok else unittest.skip(msg)(test) +def skip_if_unlimited_stack_size(test): + """Skip decorator for tests not run when an unlimited stack size is configured. + + Tests using support.infinite_recursion([...]) may otherwise run into + an infinite loop, running until the memory on the system is filled and + crashing due to OOM. + + See https://github.com/python/cpython/issues/143460. + """ + if is_emscripten or is_wasi or os.name == "nt": + return test + + import resource + curlim, maxlim = resource.getrlimit(resource.RLIMIT_STACK) + unlimited_stack_size_cond = curlim == maxlim and curlim in (-1, 0xFFFF_FFFF_FFFF_FFFF) + reason = "Not run due to unlimited stack size" + return unittest.skipIf(unlimited_stack_size_cond, reason)(test) + + def detect_api_mismatch(ref_api, other_api, *, ignore=()): """Returns the set of items in ref_api not in other_api, except for a defined list of items to be ignored in this check. @@ -1681,7 +1803,7 @@ def check__all__(test_case, module, name_of_module=None, extra=(), 'module'. The 'name_of_module' argument can specify (as a string or tuple thereof) - what module(s) an API could be defined in in order to be detected as a + what module(s) an API could be defined in order to be detected as a public API. One case for this is when 'module' imports part of its public API from other modules, possibly a C backend (like 'csv' and its '_csv'). @@ -2684,6 +2806,10 @@ def exceeds_recursion_limit(): is_s390x = hasattr(os, 'uname') and os.uname().machine == 's390x' skip_on_s390x = unittest.skipIf(is_s390x, 'skipped on s390x') +# Cygwin uses the newlib C library +skip_on_newlib = unittest.skipIf(sys.platform == 'cygwin', + 'the test fails on newlib C library') + Py_TRACE_REFS = hasattr(sys, 'getobjects') _JIT_ENABLED = sys._jit.is_enabled() @@ -2893,8 +3019,15 @@ def force_color(color: bool): import _colorize from .os_helper import EnvironmentVarGuard + if color: + try: + import _pyrepl # noqa: F401 + except ModuleNotFoundError: + # Can't force enable color without _pyrepl, so just skip. + raise unittest.SkipTest("_pyrepl is missing") + with ( - swap_attr(_colorize, "can_colorize", lambda file=None: color), + swap_attr(_colorize, "can_colorize", lambda *, file=None: color), EnvironmentVarGuard() as env, ): env.unset("FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS") @@ -2987,6 +3120,10 @@ def get_signal_name(exitcode): except KeyError: pass + # Format Windows exit status as hexadecimal + if 0xC0000000 <= exitcode: + return f"0x{exitcode:X}" + return None class BrokenIter: @@ -3169,3 +3306,30 @@ def linked_to_musl(): return _linked_to_musl _linked_to_musl = tuple(map(int, version.split('.'))) return _linked_to_musl + + +def control_characters_c0() -> list[str]: + """Returns a list of C0 control characters as strings. + C0 control characters defined as the byte range 0x00-0x1F, and 0x7F. + """ + return [chr(c) for c in range(0x00, 0x20)] + ["\x7F"] + + +_ROOT_IN_POSIX = hasattr(os, 'geteuid') and os.geteuid() == 0 +requires_root_user = unittest.skipUnless(_ROOT_IN_POSIX, "test needs root privilege") +requires_non_root_user = unittest.skipIf(_ROOT_IN_POSIX, "test needs non-root account") + + +STATUS_DLL_INIT_FAILED = 0xC0000142 +def skip_on_low_desktop_heap_memory_subprocess(returncode): + if sys.platform not in ('win32', 'cygwin'): + return + # On Windows, STATUS_DLL_INIT_FAILED is a generic error code that could + # come from any of the DLLs being loaded when a new Python process is + # created. In practice, it's likely a memory allocation failure in the + # desktop heap memory which caused the DLL init failure, especially on + # process created with CREATE_NEW_CONSOLE creation flag. See the article: + # https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/desktop-heap-limitation-out-of-memory + if returncode == STATUS_DLL_INIT_FAILED: + raise unittest.SkipTest('gh-150436: DLL init failed, likely because ' + 'of low desktop heap memory') diff --git a/Lib/test/support/_hypothesis_stubs/__init__.py b/Lib/test/support/_hypothesis_stubs/__init__.py index 6ba5bb814b92f7..6fa013b55b2ac4 100644 --- a/Lib/test/support/_hypothesis_stubs/__init__.py +++ b/Lib/test/support/_hypothesis_stubs/__init__.py @@ -24,7 +24,13 @@ def decorator(f): @functools.wraps(f) def test_function(self): for example_args, example_kwargs in examples: - with self.subTest(*example_args, **example_kwargs): + if len(example_args) < 2: + subtest_args = example_args + else: + # subTest takes up to one positional argument. + # When there are more, display them as a tuple + subtest_args = [example_args] + with self.subTest(*subtest_args, **example_kwargs): f(self, *example_args, **example_kwargs) else: diff --git a/Lib/test/support/hashlib_helper.py b/Lib/test/support/hashlib_helper.py index 96be74e4105c18..818f99d0023dae 100644 --- a/Lib/test/support/hashlib_helper.py +++ b/Lib/test/support/hashlib_helper.py @@ -6,18 +6,70 @@ import unittest import unittest.mock from test.support import import_helper -from types import MappingProxyType -def try_import_module(module_name): - """Try to import a module and return None on failure.""" +def _parse_fullname(fullname, *, strict=False): + """Parse a fully-qualified name ``.``. + + The ``module_name`` component contains one or more dots. + The ``member_name`` component does not contain any dot. + + If *strict* is true, *fullname* must be a string. Otherwise, + it can be None, and, ``module_name`` and ``member_name`` will + also be None. + """ + if fullname is None: + assert not strict + return None, None + assert isinstance(fullname, str), fullname + assert fullname.count(".") >= 1, fullname + module_name, member_name = fullname.rsplit(".", maxsplit=1) + return module_name, member_name + + +def _import_module(module_name, *, strict=False): + """Import a module from its fully-qualified name. + + If *strict* is false, import failures are suppressed and None is returned. + """ + if module_name is None: + # To prevent a TypeError in importlib.import_module + if strict: + raise ImportError("no module to import") + return None try: return importlib.import_module(module_name) - except ImportError: + except ImportError as exc: + if strict: + raise exc + return None + + +def _import_member(module_name, member_name, *, strict=False): + """Import a member from a module. + + If *strict* is false, import failures are suppressed and None is returned. + """ + if member_name is None: + if strict: + raise ImportError(f"no member to import from {module_name}") return None + module = _import_module(module_name, strict=strict) + if strict: + return getattr(module, member_name) + return getattr(module, member_name, None) + + +class Implementation(enum.StrEnum): + # Indicate that the hash function is implemented by a built-in module. + builtin = enum.auto() + # Indicate that the hash function is implemented by OpenSSL. + openssl = enum.auto() + # Indicate that the hash function is provided through the public API. + hashlib = enum.auto() -class HID(enum.StrEnum): +class _HashId(enum.StrEnum): """Enumeration containing the canonical digest names. Those names should only be used by hashlib.new() or hmac.new(). @@ -57,62 +109,162 @@ def is_keyed(self): return self.startswith("blake2") -CANONICAL_DIGEST_NAMES = frozenset(map(str, HID.__members__)) +CANONICAL_DIGEST_NAMES = frozenset(map(str, _HashId.__members__)) NON_HMAC_DIGEST_NAMES = frozenset(( - HID.shake_128, HID.shake_256, - HID.blake2s, HID.blake2b, + _HashId.shake_128, _HashId.shake_256, + _HashId.blake2s, _HashId.blake2b, )) -class HashInfo: - """Dataclass storing explicit hash constructor names. +class _HashInfoItem: + """Interface for interacting with a named object. - - *builtin* is the fully-qualified name for the explicit HACL* - hash constructor function, e.g., "_md5.md5". - - - *openssl* is the name of the "_hashlib" module method for the explicit - OpenSSL hash constructor function, e.g., "openssl_md5". + The object is entirely described by its fully-qualified *fullname*. - - *hashlib* is the name of the "hashlib" module method for the explicit - hash constructor function, e.g., "md5". + *fullname* must be None or a string ".". """ - def __init__(self, builtin, openssl=None, hashlib=None): - assert isinstance(builtin, str), builtin - assert len(builtin.split(".")) == 2, builtin + def __init__(self, fullname=None, *, strict=False): + module_name, member_name = _parse_fullname(fullname, strict=strict) + self.fullname = fullname + self.module_name = module_name + self.member_name = member_name + + def import_module(self, *, strict=False): + """Import the described module. + + If *strict* is true, an ImportError may be raised if importing fails, + otherwise, None is returned on error. + """ + return _import_module(self.module_name, strict=strict) - self.builtin = builtin - self.builtin_module_name, self.builtin_method_name = ( - self.builtin.split(".", maxsplit=1) + def import_member(self, *, strict=False): + """Import the described member. + + If *strict* is true, an AttributeError or an ImportError may be + raised if importing fails; otherwise, None is returned on error. + """ + return _import_member( + self.module_name, self.member_name, strict=strict ) - assert openssl is None or openssl.startswith("openssl_") - self.openssl = self.openssl_method_name = openssl - self.openssl_module_name = "_hashlib" if openssl else None - assert hashlib is None or isinstance(hashlib, str) - self.hashlib = self.hashlib_method_name = hashlib - self.hashlib_module_name = "hashlib" if hashlib else None +class _HashInfoBase: + """Base dataclass containing "backend" information. + + Subclasses may define an attribute named after one of the known + implementations ("builtin", "openssl" or "hashlib") which stores + an _HashInfoItem object. + + Those attributes can be retrieved through __getitem__(), e.g., + ``info["builtin"]`` returns the _HashInfoItem corresponding to + the builtin implementation. + """ + + def __init__(self, canonical_name): + assert isinstance(canonical_name, _HashId), canonical_name + self.canonical_name = canonical_name + + def __getitem__(self, implementation): + try: + attrname = Implementation(implementation) + except ValueError: + raise self.invalid_implementation_error(implementation) from None + + try: + provider = getattr(self, attrname) + except AttributeError: + raise self.invalid_implementation_error(implementation) from None + + if not isinstance(provider, _HashInfoItem): + raise KeyError(implementation) + return provider + + def invalid_implementation_error(self, implementation): + msg = f"no implementation {implementation} for {self.canonical_name}" + return AssertionError(msg) + + +class _HashTypeInfo(_HashInfoBase): + """Dataclass containing information for hash functions types. + + - *canonical_name* must be a _HashId. + + - *builtin* is the fully-qualified name for the builtin HACL* type, + e.g., "_md5.MD5Type". + + - *openssl* is the fully-qualified name for the OpenSSL wrapper type, + e.g., "_hashlib.HASH". + """ + + def __init__(self, canonical_name, builtin, openssl): + super().__init__(canonical_name) + self.builtin = _HashInfoItem(builtin, strict=True) + self.openssl = _HashInfoItem(openssl, strict=True) + + def fullname(self, implementation): + """Get the fully qualified name of a given implementation. + + This returns a string of the form "MODULE_NAME.OBJECT_NAME" or None + if the hash function does not have a corresponding implementation. + + *implementation* must be "builtin" or "openssl". + """ + return self[implementation].fullname def module_name(self, implementation): - match implementation: - case "builtin": - return self.builtin_module_name - case "openssl": - return self.openssl_module_name - case "hashlib": - return self.hashlib_module_name - raise AssertionError(f"invalid implementation {implementation}") + """Get the name of the module containing the hash object type.""" + return self[implementation].module_name + + def object_type_name(self, implementation): + """Get the name of the hash object class name.""" + return self[implementation].member_name + + def import_module(self, implementation, *, allow_skip=False): + """Import the module containing the hash object type. + + On error, return None if *allow_skip* is false, or raise SkipNoHash. + """ + target = self[implementation] + module = target.import_module() + if allow_skip and module is None: + reason = f"cannot import module {target.module_name}" + raise SkipNoHash(self.canonical_name, implementation, reason) + return module + + def import_object_type(self, implementation, *, allow_skip=False): + """Get the runtime hash object type. + + On error, return None if *allow_skip* is false, or raise SkipNoHash. + """ + target = self[implementation] + member = target.import_member() + if allow_skip and member is None: + reason = f"cannot import class {target.fullname}" + raise SkipNoHash(self.canonical_name, implementation, reason) + return member - def method_name(self, implementation): - match implementation: - case "builtin": - return self.builtin_method_name - case "openssl": - return self.openssl_method_name - case "hashlib": - return self.hashlib_method_name - raise AssertionError(f"invalid implementation {implementation}") + +class _HashFuncInfo(_HashInfoBase): + """Dataclass containing information for hash functions constructors. + + - *canonical_name* must be a _HashId. + + - *builtin* is the fully-qualified name of the HACL* + hash constructor function, e.g., "_md5.md5". + + - *openssl* is the fully-qualified name of the "_hashlib" method + for the OpenSSL named constructor, e.g., "_hashlib.openssl_md5". + + - *hashlib* is the fully-qualified name of the "hashlib" method + for the explicit named hash constructor, e.g., "hashlib.md5". + """ + + def __init__(self, canonical_name, builtin, openssl=None, hashlib=None): + super().__init__(canonical_name) + self.builtin = _HashInfoItem(builtin, strict=True) + self.openssl = _HashInfoItem(openssl, strict=False) + self.hashlib = _HashInfoItem(hashlib, strict=False) def fullname(self, implementation): """Get the fully qualified name of a given implementation. @@ -122,63 +274,239 @@ def fullname(self, implementation): *implementation* must be "builtin", "openssl" or "hashlib". """ - module_name = self.module_name(implementation) - method_name = self.method_name(implementation) - if module_name is None or method_name is None: - return None - return f"{module_name}.{method_name}" - - -# Mapping from a "canonical" name to a pair (HACL*, _hashlib.*, hashlib.*) -# constructors. If the constructor name is None, then this means that the -# algorithm can only be used by the "agile" new() interfaces. -_EXPLICIT_CONSTRUCTORS = MappingProxyType({ # fmt: skip - HID.md5: HashInfo("_md5.md5", "openssl_md5", "md5"), - HID.sha1: HashInfo("_sha1.sha1", "openssl_sha1", "sha1"), - HID.sha224: HashInfo("_sha2.sha224", "openssl_sha224", "sha224"), - HID.sha256: HashInfo("_sha2.sha256", "openssl_sha256", "sha256"), - HID.sha384: HashInfo("_sha2.sha384", "openssl_sha384", "sha384"), - HID.sha512: HashInfo("_sha2.sha512", "openssl_sha512", "sha512"), - HID.sha3_224: HashInfo( - "_sha3.sha3_224", "openssl_sha3_224", "sha3_224" + return self[implementation].fullname + + def module_name(self, implementation): + """Get the name of the constructor function module. + + The *implementation* must be "builtin", "openssl" or "hashlib". + """ + return self[implementation].module_name + + def method_name(self, implementation): + """Get the name of the constructor function module method. + + Use fullname() to get the constructor function fully-qualified name. + + The *implementation* must be "builtin", "openssl" or "hashlib". + """ + return self[implementation].member_name + + +class _HashInfo: + """Dataclass containing information for supported hash functions. + + Attributes + ---------- + canonical_name : _HashId + The hash function canonical name. + type : _HashTypeInfo + The hash object types information. + func : _HashTypeInfo + The hash object constructors information. + """ + + def __init__( + self, + canonical_name, + builtin_object_type_fullname, + openssl_object_type_fullname, + builtin_method_fullname, + openssl_method_fullname=None, + hashlib_method_fullname=None, + ): + """ + - *canonical_name* must be a _HashId. + + - *builtin_object_type_fullname* is the fully-qualified name + for the builtin HACL* type, e.g., "_md5.MD5Type". + + - *openssl_object_type_fullname* is the fully-qualified name + for the OpenSSL wrapper type, e.g., "_hashlib.HASH". + + - *builtin_method_fullname* is the fully-qualified name + of the HACL* hash constructor function, e.g., "_md5.md5". + + - *openssl_method_fullname* is the fully-qualified name + of the "_hashlib" module method for the explicit OpenSSL + hash constructor function, e.g., "_hashlib.openssl_md5". + + - *hashlib_method_fullname* is the fully-qualified name + of the "hashlib" module method for the explicit hash + constructor function, e.g., "hashlib.md5". + """ + assert isinstance(canonical_name, _HashId), canonical_name + self.canonical_name = canonical_name + self.type = _HashTypeInfo( + canonical_name, + builtin_object_type_fullname, + openssl_object_type_fullname, + ) + self.func = _HashFuncInfo( + canonical_name, + builtin_method_fullname, + openssl_method_fullname, + hashlib_method_fullname, + ) + + +_HASHINFO_DATABASE = frozendict({ + _HashId.md5: _HashInfo( + _HashId.md5, + "_md5.MD5Type", + "_hashlib.HASH", + "_md5.md5", + "_hashlib.openssl_md5", + "hashlib.md5", + ), + _HashId.sha1: _HashInfo( + _HashId.sha1, + "_sha1.SHA1Type", + "_hashlib.HASH", + "_sha1.sha1", + "_hashlib.openssl_sha1", + "hashlib.sha1", + ), + _HashId.sha224: _HashInfo( + _HashId.sha224, + "_sha2.SHA224Type", + "_hashlib.HASH", + "_sha2.sha224", + "_hashlib.openssl_sha224", + "hashlib.sha224", + ), + _HashId.sha256: _HashInfo( + _HashId.sha256, + "_sha2.SHA256Type", + "_hashlib.HASH", + "_sha2.sha256", + "_hashlib.openssl_sha256", + "hashlib.sha256", + ), + _HashId.sha384: _HashInfo( + _HashId.sha384, + "_sha2.SHA384Type", + "_hashlib.HASH", + "_sha2.sha384", + "_hashlib.openssl_sha384", + "hashlib.sha384", ), - HID.sha3_256: HashInfo( - "_sha3.sha3_256", "openssl_sha3_256", "sha3_256" + _HashId.sha512: _HashInfo( + _HashId.sha512, + "_sha2.SHA512Type", + "_hashlib.HASH", + "_sha2.sha512", + "_hashlib.openssl_sha512", + "hashlib.sha512", ), - HID.sha3_384: HashInfo( - "_sha3.sha3_384", "openssl_sha3_384", "sha3_384" + _HashId.sha3_224: _HashInfo( + _HashId.sha3_224, + "_sha3.sha3_224", + "_hashlib.HASH", + "_sha3.sha3_224", + "_hashlib.openssl_sha3_224", + "hashlib.sha3_224", ), - HID.sha3_512: HashInfo( - "_sha3.sha3_512", "openssl_sha3_512", "sha3_512" + _HashId.sha3_256: _HashInfo( + _HashId.sha3_256, + "_sha3.sha3_256", + "_hashlib.HASH", + "_sha3.sha3_256", + "_hashlib.openssl_sha3_256", + "hashlib.sha3_256", ), - HID.shake_128: HashInfo( - "_sha3.shake_128", "openssl_shake_128", "shake_128" + _HashId.sha3_384: _HashInfo( + _HashId.sha3_384, + "_sha3.sha3_384", + "_hashlib.HASH", + "_sha3.sha3_384", + "_hashlib.openssl_sha3_384", + "hashlib.sha3_384", ), - HID.shake_256: HashInfo( - "_sha3.shake_256", "openssl_shake_256", "shake_256" + _HashId.sha3_512: _HashInfo( + _HashId.sha3_512, + "_sha3.sha3_512", + "_hashlib.HASH", + "_sha3.sha3_512", + "_hashlib.openssl_sha3_512", + "hashlib.sha3_512", + ), + _HashId.shake_128: _HashInfo( + _HashId.shake_128, + "_sha3.shake_128", + "_hashlib.HASHXOF", + "_sha3.shake_128", + "_hashlib.openssl_shake_128", + "hashlib.shake_128", + ), + _HashId.shake_256: _HashInfo( + _HashId.shake_256, + "_sha3.shake_256", + "_hashlib.HASHXOF", + "_sha3.shake_256", + "_hashlib.openssl_shake_256", + "hashlib.shake_256", + ), + _HashId.blake2s: _HashInfo( + _HashId.blake2s, + "_blake2.blake2s", + "_hashlib.HASH", + "_blake2.blake2s", + None, + "hashlib.blake2s", + ), + _HashId.blake2b: _HashInfo( + _HashId.blake2b, + "_blake2.blake2b", + "_hashlib.HASH", + "_blake2.blake2b", + None, + "hashlib.blake2b", ), - HID.blake2s: HashInfo("_blake2.blake2s", None, "blake2s"), - HID.blake2b: HashInfo("_blake2.blake2b", None, "blake2b"), }) -assert _EXPLICIT_CONSTRUCTORS.keys() == CANONICAL_DIGEST_NAMES -get_hash_info = _EXPLICIT_CONSTRUCTORS.__getitem__ +assert _HASHINFO_DATABASE.keys() == CANONICAL_DIGEST_NAMES + + +def get_hash_type_info(name): + info = _HASHINFO_DATABASE[name] + assert isinstance(info, _HashInfo), info + return info.type + + +def get_hash_func_info(name): + info = _HASHINFO_DATABASE[name] + assert isinstance(info, _HashInfo), info + return info.func + + +def _iter_hash_func_info(excluded): + for name, info in _HASHINFO_DATABASE.items(): + if name not in excluded: + yield info.func + # Mapping from canonical hash names to their explicit HACL* HMAC constructor. # There is currently no OpenSSL one-shot named function and there will likely # be none in the future. -_EXPLICIT_HMAC_CONSTRUCTORS = { - HID(name): f"_hmac.compute_{name}" - for name in CANONICAL_DIGEST_NAMES +_HMACINFO_DATABASE = { + _HashId(canonical_name): _HashInfoItem(f"_hmac.compute_{canonical_name}") + for canonical_name in CANONICAL_DIGEST_NAMES } # Neither HACL* nor OpenSSL supports HMAC over XOFs. -_EXPLICIT_HMAC_CONSTRUCTORS[HID.shake_128] = None -_EXPLICIT_HMAC_CONSTRUCTORS[HID.shake_256] = None +_HMACINFO_DATABASE[_HashId.shake_128] = _HashInfoItem() +_HMACINFO_DATABASE[_HashId.shake_256] = _HashInfoItem() # Strictly speaking, HMAC-BLAKE is meaningless as BLAKE2 is already a # keyed hash function. However, as it's exposed by HACL*, we test it. -_EXPLICIT_HMAC_CONSTRUCTORS[HID.blake2s] = '_hmac.compute_blake2s_32' -_EXPLICIT_HMAC_CONSTRUCTORS[HID.blake2b] = '_hmac.compute_blake2b_32' -_EXPLICIT_HMAC_CONSTRUCTORS = MappingProxyType(_EXPLICIT_HMAC_CONSTRUCTORS) -assert _EXPLICIT_HMAC_CONSTRUCTORS.keys() == CANONICAL_DIGEST_NAMES +_HMACINFO_DATABASE[_HashId.blake2s] = _HashInfoItem('_hmac.compute_blake2s_32') +_HMACINFO_DATABASE[_HashId.blake2b] = _HashInfoItem('_hmac.compute_blake2b_32') +_HMACINFO_DATABASE = frozendict(_HMACINFO_DATABASE) +assert _HMACINFO_DATABASE.keys() == CANONICAL_DIGEST_NAMES + + +def get_hmac_item_info(name): + info = _HMACINFO_DATABASE[name] + assert isinstance(info, _HashInfoItem), info + return info def _decorate_func_or_class(decorator_func, func_or_class): @@ -230,26 +558,42 @@ def _ensure_wrapper_signature(wrapper, wrapped): ) -def requires_hashlib(): - _hashlib = try_import_module("_hashlib") +def _make_conditional_decorator(test, /, *test_args, **test_kwargs): + def decorator_func(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + test(*test_args, **test_kwargs) + return func(*args, **kwargs) + return wrapper + return functools.partial(_decorate_func_or_class, decorator_func) + + +def requires_openssl_hashlib(): + _hashlib = _import_module("_hashlib") return unittest.skipIf(_hashlib is None, "requires _hashlib") def requires_builtin_hmac(): - _hmac = try_import_module("_hmac") + _hmac = _import_module("_hmac") return unittest.skipIf(_hmac is None, "requires _hmac") class SkipNoHash(unittest.SkipTest): """A SkipTest exception raised when a hash is not available.""" - def __init__(self, digestname, implementation=None, interface=None): + def __init__(self, digestname, implementation=None, reason=None): parts = ["missing", implementation, f"hash algorithm {digestname!r}"] - if interface is not None: - parts.append(f"for {interface}") + if reason is not None: + parts.insert(0, f"{reason}: ") super().__init__(" ".join(filter(None, parts))) +class SkipNoHashInCall(SkipNoHash): + + def __init__(self, func, digestname, implementation=None): + super().__init__(digestname, implementation, f"cannot use {func}") + + def _hashlib_new(digestname, openssl, /, **kwargs): """Check availability of [hashlib|_hashlib].new(digestname, **kwargs). @@ -264,13 +608,12 @@ def _hashlib_new(digestname, openssl, /, **kwargs): # exceptions as it should be unconditionally available. hashlib = importlib.import_module("hashlib") # re-import '_hashlib' in case it was mocked - _hashlib = try_import_module("_hashlib") + _hashlib = _import_module("_hashlib") module = _hashlib if openssl and _hashlib is not None else hashlib try: module.new(digestname, **kwargs) except ValueError as exc: - interface = f"{module.__name__}.new" - raise SkipNoHash(digestname, interface=interface) from exc + raise SkipNoHashInCall(f"{module.__name__}.new", digestname) from exc return functools.partial(module.new, digestname) @@ -315,7 +658,7 @@ def _openssl_new(digestname, /, **kwargs): try: _hashlib.new(digestname, **kwargs) except ValueError as exc: - raise SkipNoHash(digestname, interface="_hashlib.new") from exc + raise SkipNoHashInCall("_hashlib.new", digestname) from exc return functools.partial(_hashlib.new, digestname) @@ -326,14 +669,15 @@ def _openssl_hash(digestname, /, **kwargs): or SkipTest is raised if none exists. """ assert isinstance(digestname, str), digestname - fullname = f"_hashlib.openssl_{digestname}" + method_name = f"openssl_{digestname}" + fullname = f"_hashlib.{method_name}" try: # re-import '_hashlib' in case it was mocked _hashlib = importlib.import_module("_hashlib") except ImportError as exc: raise SkipNoHash(fullname, "openssl") from exc try: - constructor = getattr(_hashlib, f"openssl_{digestname}", None) + constructor = getattr(_hashlib, method_name) except AttributeError as exc: raise SkipNoHash(fullname, "openssl") from exc try: @@ -343,16 +687,6 @@ def _openssl_hash(digestname, /, **kwargs): return constructor -def _make_requires_hashdigest_decorator(test, /, *test_args, **test_kwargs): - def decorator_func(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - test(*test_args, **test_kwargs) - return func(*args, **kwargs) - return wrapper - return functools.partial(_decorate_func_or_class, decorator_func) - - def requires_hashdigest(digestname, openssl=None, *, usedforsecurity=True): """Decorator raising SkipTest if a hashing algorithm is not available. @@ -370,7 +704,7 @@ def requires_hashdigest(digestname, openssl=None, *, usedforsecurity=True): ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS ValueError: unsupported hash type md4 """ - return _make_requires_hashdigest_decorator( + return _make_conditional_decorator( _hashlib_new, digestname, openssl, usedforsecurity=usedforsecurity ) @@ -380,34 +714,35 @@ def requires_openssl_hashdigest(digestname, *, usedforsecurity=True): The hashing algorithm may be missing or blocked by a strict crypto policy. """ - return _make_requires_hashdigest_decorator( + return _make_conditional_decorator( _openssl_new, digestname, usedforsecurity=usedforsecurity ) -def requires_builtin_hashdigest( - module_name, digestname, *, usedforsecurity=True -): - """Decorator raising SkipTest if a HACL* hashing algorithm is missing. +def _make_requires_builtin_hashdigest_decorator(item, *, usedforsecurity=True): + assert isinstance(item, _HashInfoItem), item + return _make_conditional_decorator( + _builtin_hash, + item.module_name, + item.member_name, + usedforsecurity=usedforsecurity, + ) - - The *module_name* is the C extension module name based on HACL*. - - The *digestname* is one of its member, e.g., 'md5'. - """ - return _make_requires_hashdigest_decorator( - _builtin_hash, module_name, digestname, usedforsecurity=usedforsecurity + +def requires_builtin_hashdigest(canonical_name, *, usedforsecurity=True): + """Decorator raising SkipTest if a HACL* hashing algorithm is missing.""" + info = get_hash_func_info(canonical_name) + return _make_requires_builtin_hashdigest_decorator( + info.builtin, usedforsecurity=usedforsecurity ) -def requires_builtin_hashes(*ignored, usedforsecurity=True): +def requires_builtin_hashes(*, exclude=(), usedforsecurity=True): """Decorator raising SkipTest if one HACL* hashing algorithm is missing.""" return _chain_decorators(( - requires_builtin_hashdigest( - api.builtin_module_name, - api.builtin_method_name, - usedforsecurity=usedforsecurity, - ) - for name, api in _EXPLICIT_CONSTRUCTORS.items() - if name not in ignored + _make_requires_builtin_hashdigest_decorator( + info.builtin, usedforsecurity=usedforsecurity + ) for info in _iter_hash_func_info(exclude) )) @@ -424,69 +759,31 @@ class HashFunctionsTrait: implementation of HMAC). """ - DIGEST_NAMES = [ - 'md5', 'sha1', - 'sha224', 'sha256', 'sha384', 'sha512', - 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', - ] - # Default 'usedforsecurity' to use when checking a hash function. # When the trait properties are callables (e.g., _md5.md5) and # not strings, they must be called with the same 'usedforsecurity'. usedforsecurity = True - @classmethod - def setUpClass(cls): - super().setUpClass() - assert CANONICAL_DIGEST_NAMES.issuperset(cls.DIGEST_NAMES) - def is_valid_digest_name(self, digestname): - self.assertIn(digestname, self.DIGEST_NAMES) + self.assertIn(digestname, _HashId) def _find_constructor(self, digestname): # By default, a missing algorithm skips the test that uses it. self.is_valid_digest_name(digestname) self.skipTest(f"missing hash function: {digestname}") - @property - def md5(self): - return self._find_constructor("md5") + md5 = property(lambda self: self._find_constructor("md5")) + sha1 = property(lambda self: self._find_constructor("sha1")) - @property - def sha1(self): - return self._find_constructor("sha1") - - @property - def sha224(self): - return self._find_constructor("sha224") - - @property - def sha256(self): - return self._find_constructor("sha256") - - @property - def sha384(self): - return self._find_constructor("sha384") - - @property - def sha512(self): - return self._find_constructor("sha512") - - @property - def sha3_224(self): - return self._find_constructor("sha3_224") - - @property - def sha3_256(self): - return self._find_constructor("sha3_256") - - @property - def sha3_384(self): - return self._find_constructor("sha3_384") + sha224 = property(lambda self: self._find_constructor("sha224")) + sha256 = property(lambda self: self._find_constructor("sha256")) + sha384 = property(lambda self: self._find_constructor("sha384")) + sha512 = property(lambda self: self._find_constructor("sha512")) - @property - def sha3_512(self): - return self._find_constructor("sha3_512") + sha3_224 = property(lambda self: self._find_constructor("sha3_224")) + sha3_256 = property(lambda self: self._find_constructor("sha3_256")) + sha3_384 = property(lambda self: self._find_constructor("sha3_384")) + sha3_512 = property(lambda self: self._find_constructor("sha3_512")) class NamedHashFunctionsTrait(HashFunctionsTrait): @@ -497,7 +794,7 @@ class NamedHashFunctionsTrait(HashFunctionsTrait): def _find_constructor(self, digestname): self.is_valid_digest_name(digestname) - return digestname + return str(digestname) # ensure that we are an exact string class OpenSSLHashFunctionsTrait(HashFunctionsTrait): @@ -523,10 +820,10 @@ class BuiltinHashFunctionsTrait(HashFunctionsTrait): def _find_constructor(self, digestname): self.is_valid_digest_name(digestname) - info = _EXPLICIT_CONSTRUCTORS[digestname] + info = get_hash_func_info(digestname) return _builtin_hash( - info.builtin_module_name, - info.builtin_method_name, + info.builtin.module_name, + info.builtin.member_name, usedforsecurity=self.usedforsecurity, ) @@ -542,7 +839,7 @@ def find_gil_minsize(modules_names, default=2048): """ sizes = [] for module_name in modules_names: - module = try_import_module(module_name) + module = _import_module(module_name) if module is not None: sizes.append(getattr(module, '_GIL_MINSIZE', default)) return max(sizes, default=default) @@ -553,7 +850,7 @@ def _block_openssl_hash_new(blocked_name): assert isinstance(blocked_name, str), blocked_name # re-import '_hashlib' in case it was mocked - if (_hashlib := try_import_module("_hashlib")) is None: + if (_hashlib := _import_module("_hashlib")) is None: return contextlib.nullcontext() @functools.wraps(wrapped := _hashlib.new) @@ -572,7 +869,7 @@ def _block_openssl_hmac_new(blocked_name): assert isinstance(blocked_name, str), blocked_name # re-import '_hashlib' in case it was mocked - if (_hashlib := try_import_module("_hashlib")) is None: + if (_hashlib := _import_module("_hashlib")) is None: return contextlib.nullcontext() @functools.wraps(wrapped := _hashlib.hmac_new) @@ -590,7 +887,7 @@ def _block_openssl_hmac_digest(blocked_name): assert isinstance(blocked_name, str), blocked_name # re-import '_hashlib' in case it was mocked - if (_hashlib := try_import_module("_hashlib")) is None: + if (_hashlib := _import_module("_hashlib")) is None: return contextlib.nullcontext() @functools.wraps(wrapped := _hashlib.hmac_digest) @@ -607,7 +904,7 @@ def _block_builtin_hash_new(name): """Block a buitin-in hash name from the hashlib.new() interface.""" assert isinstance(name, str), name assert name.lower() == name, f"invalid name: {name}" - assert name in HID, f"invalid hash: {name}" + assert name in _HashId, f"invalid hash: {name}" # Re-import 'hashlib' in case it was mocked hashlib = importlib.import_module('hashlib') @@ -620,7 +917,7 @@ def _block_builtin_hash_new(name): # so we need to block the possibility of importing it, but only # during the call to __get_builtin_constructor(). get_builtin_constructor = getattr(hashlib, '__get_builtin_constructor') - builtin_module_name = _EXPLICIT_CONSTRUCTORS[name].builtin_module_name + builtin_module_name = get_hash_func_info(name).builtin.module_name @functools.wraps(get_builtin_constructor) def get_builtin_constructor_mock(name): @@ -632,7 +929,7 @@ def get_builtin_constructor_mock(name): return unittest.mock.patch.multiple( hashlib, __get_builtin_constructor=get_builtin_constructor_mock, - __builtin_constructor_cache=builtin_constructor_cache_mock + __builtin_constructor_cache=builtin_constructor_cache_mock, ) @@ -640,7 +937,7 @@ def _block_builtin_hmac_new(blocked_name): assert isinstance(blocked_name, str), blocked_name # re-import '_hmac' in case it was mocked - if (_hmac := try_import_module("_hmac")) is None: + if (_hmac := _import_module("_hmac")) is None: return contextlib.nullcontext() @functools.wraps(wrapped := _hmac.new) @@ -657,7 +954,7 @@ def _block_builtin_hmac_digest(blocked_name): assert isinstance(blocked_name, str), blocked_name # re-import '_hmac' in case it was mocked - if (_hmac := try_import_module("_hmac")) is None: + if (_hmac := _import_module("_hmac")) is None: return contextlib.nullcontext() @functools.wraps(wrapped := _hmac.compute_digest) @@ -671,30 +968,19 @@ def _hmac_compute_digest(key, msg, digest): def _make_hash_constructor_blocker(name, dummy, implementation): - info = _EXPLICIT_CONSTRUCTORS[name] - module_name = info.module_name(implementation) - method_name = info.method_name(implementation) - if module_name is None or method_name is None: + info = get_hash_func_info(name)[implementation] + if (wrapped := info.import_member()) is None: # function shouldn't exist for this implementation return contextlib.nullcontext() - - try: - module = importlib.import_module(module_name) - except ImportError: - # module is already disabled - return contextlib.nullcontext() - - wrapped = getattr(module, method_name) wrapper = functools.wraps(wrapped)(dummy) _ensure_wrapper_signature(wrapper, wrapped) - return unittest.mock.patch(info.fullname(implementation), wrapper) + return unittest.mock.patch(info.fullname, wrapper) def _block_hashlib_hash_constructor(name): """Block explicit public constructors.""" def dummy(data=b'', *, usedforsecurity=True, string=None): raise ValueError(f"blocked explicit public hash name: {name}") - return _make_hash_constructor_blocker(name, dummy, 'hashlib') @@ -714,23 +1000,18 @@ def dummy(data=b'', *, usedforsecurity=True, string=b''): def _block_builtin_hmac_constructor(name): """Block explicit HACL* HMAC constructors.""" - fullname = _EXPLICIT_HMAC_CONSTRUCTORS[name] - if fullname is None: + info = get_hmac_item_info(name) + assert info.module_name is None or info.module_name == "_hmac", info + if (wrapped := info.import_member()) is None: # function shouldn't exist for this implementation return contextlib.nullcontext() - assert fullname.count('.') == 1, fullname - module_name, method = fullname.split('.', maxsplit=1) - assert module_name == '_hmac', module_name - try: - module = importlib.import_module(module_name) - except ImportError: - # module is already disabled - return contextlib.nullcontext() - @functools.wraps(wrapped := getattr(module, method)) + + @functools.wraps(wrapped) def wrapper(key, obj): raise ValueError(f"blocked hash name: {name}") + _ensure_wrapper_signature(wrapper, wrapped) - return unittest.mock.patch(fullname, wrapper) + return unittest.mock.patch(info.fullname, wrapper) @contextlib.contextmanager @@ -760,14 +1041,14 @@ def block_algorithm(name, *, allow_openssl=False, allow_builtin=False): # the OpenSSL implementation, except with usedforsecurity=False. # However, blocking such functions also means blocking them # so we again need to block them if we want to. - (_hashlib := try_import_module("_hashlib")) + (_hashlib := _import_module("_hashlib")) and _hashlib.get_fips_mode() and not allow_openssl ) or ( # Without OpenSSL, hashlib.() functions are aliases # to built-in functions, so both of them must be blocked # as the module may have been imported before the HACL ones. - not (_hashlib := try_import_module("_hashlib")) + not (_hashlib := _import_module("_hashlib")) and not allow_builtin ): stack.enter_context(_block_hashlib_hash_constructor(name)) @@ -794,3 +1075,21 @@ def block_algorithm(name, *, allow_openssl=False, allow_builtin=False): # _hmac.compute_digest(..., name) stack.enter_context(_block_builtin_hmac_digest(name)) yield + + +@contextlib.contextmanager +def block_openssl_algorithms(*, exclude=()): + """Block OpenSSL implementations, except those given in *exclude*.""" + with contextlib.ExitStack() as stack: + for name in CANONICAL_DIGEST_NAMES.difference(exclude): + stack.enter_context(block_algorithm(name, allow_builtin=True)) + yield + + +@contextlib.contextmanager +def block_builtin_algorithms(*, exclude=()): + """Block HACL* implementations, except those given in *exclude*.""" + with contextlib.ExitStack() as stack: + for name in CANONICAL_DIGEST_NAMES.difference(exclude): + stack.enter_context(block_algorithm(name, allow_openssl=True)) + yield diff --git a/Lib/test/support/import_helper.py b/Lib/test/support/import_helper.py index 0af63501f93bc8..e8a3d176ad6943 100644 --- a/Lib/test/support/import_helper.py +++ b/Lib/test/support/import_helper.py @@ -4,6 +4,7 @@ import importlib.machinery import importlib.util import os +import py_compile import shutil import sys import textwrap @@ -49,20 +50,31 @@ def forget(modname): # combinations of PEP 3147/488 and legacy pyc files. unlink(source + 'c') for opt in ('', 1, 2): - unlink(importlib.util.cache_from_source(source, optimization=opt)) + try: + unlink(importlib.util.cache_from_source(source, optimization=opt)) + except NotImplementedError: + pass -def make_legacy_pyc(source): +def make_legacy_pyc(source, allow_compile=False): """Move a PEP 3147/488 pyc file to its legacy pyc location. :param source: The file system path to the source file. The source file - does not need to exist, however the PEP 3147/488 pyc file must exist. + does not need to exist, however the PEP 3147/488 pyc file must exist or + allow_compile must be set. + :param allow_compile: If True, uses py_compile to create a .pyc if it does + not exist. This should be passed as True if cache_tag may be None. :return: The file system path to the legacy pyc file. """ - pyc_file = importlib.util.cache_from_source(source) assert source.endswith('.py') legacy_pyc = source + 'c' - shutil.move(pyc_file, legacy_pyc) + try: + pyc_file = importlib.util.cache_from_source(source) + shutil.move(pyc_file, legacy_pyc) + except FileNotFoundError, NotImplementedError: + if not allow_compile: + raise + py_compile.compile(source, legacy_pyc, doraise=True) return legacy_pyc @@ -305,15 +317,15 @@ def ready_to_import(name=None, source=""): try: sys.path.insert(0, tempdir) yield name, path - sys.path.remove(tempdir) finally: + sys.path.remove(tempdir) if old_module is not None: sys.modules[name] = old_module else: sys.modules.pop(name, None) -def ensure_lazy_imports(imported_module, modules_to_block): +def ensure_lazy_imports(imported_module, modules_to_block, *, additional_code=None): """Test that when imported_module is imported, none of the modules in modules_to_block are imported as a side effect.""" modules_to_block = frozenset(modules_to_block) @@ -331,6 +343,16 @@ def ensure_lazy_imports(imported_module, modules_to_block): raise AssertionError(f'unexpectedly imported after importing {imported_module}: {{after}}') """ ) + if additional_code: + script += additional_code + script += textwrap.dedent( + f""" + if unexpected := modules_to_block & sys.modules.keys(): + after = ", ".join(unexpected) + raise AssertionError(f'unexpectedly imported after additional code: {{after}}') + """ + ) + from .script_helper import assert_python_ok assert_python_ok("-S", "-c", script) diff --git a/Lib/test/support/pty_helper.py b/Lib/test/support/pty_helper.py index 6587fd40333c51..dbe7fa429096fc 100644 --- a/Lib/test/support/pty_helper.py +++ b/Lib/test/support/pty_helper.py @@ -15,6 +15,14 @@ def run_pty(script, input=b"dummy input\r", env=None): output = bytearray() [master, slave] = pty.openpty() args = (sys.executable, '-c', script) + + # Isolate readline from personal init files by setting INPUTRC + # to an empty file. See also GH-142353. + if env is None: + env = {**os.environ.copy(), "INPUTRC": os.devnull} + else: + env.setdefault("INPUTRC", os.devnull) + proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave, env=env) os.close(slave) with ExitStack() as cleanup: diff --git a/Lib/test/support/socket_helper.py b/Lib/test/support/socket_helper.py index 87941ee1791b4e..66e5379d2c6e25 100644 --- a/Lib/test/support/socket_helper.py +++ b/Lib/test/support/socket_helper.py @@ -149,6 +149,8 @@ def skip_unless_bind_unix_socket(test): """Decorator for tests requiring a functional bind() for unix sockets.""" if not hasattr(socket, 'AF_UNIX'): return unittest.skip('No UNIX Sockets')(test) + if sys.platform == 'cygwin': + return unittest.skip('UNIX sockets hang on Cygwin')(test) global _bind_nix_socket_error if _bind_nix_socket_error is None: from .os_helper import TESTFN, unlink @@ -259,6 +261,10 @@ def filter_error(err): # raise OSError('socket error', msg) from msg elif len(a) >= 2 and isinstance(a[1], OSError): err = a[1] + # The error can also be wrapped as __cause__: + # raise URLError(f"ftp error: {exp}") from exp + elif isinstance(err, urllib.error.URLError) and err.__cause__: + err = err.__cause__ else: break filter_error(err) diff --git a/Lib/test/support/strace_helper.py b/Lib/test/support/strace_helper.py index cf95f7bdc7d2ca..bf15283d3027da 100644 --- a/Lib/test/support/strace_helper.py +++ b/Lib/test/support/strace_helper.py @@ -74,7 +74,7 @@ def sections(self): def _filter_memory_call(call): # mmap can operate on a fd or "MAP_ANONYMOUS" which gives a block of memory. # Ignore "MAP_ANONYMOUS + the "MAP_ANON" alias. - if call.syscall == "mmap" and "MAP_ANON" in call.args[3]: + if call.syscall in ("mmap", "mmap2") and "MAP_ANON" in call.args[3]: return True if call.syscall in ("munmap", "mprotect"): diff --git a/Lib/test/support/threading_helper.py b/Lib/test/support/threading_helper.py index 3e04c344a0d66f..cf87233f0e2e93 100644 --- a/Lib/test/support/threading_helper.py +++ b/Lib/test/support/threading_helper.py @@ -250,21 +250,32 @@ def requires_working_threading(*, module=False): return unittest.skipUnless(can_start_thread, msg) -def run_concurrently(worker_func, nthreads, args=(), kwargs={}): +def run_concurrently(worker_func, nthreads=None, args=(), kwargs={}): """ - Run the worker function concurrently in multiple threads. + Run the worker function(s) concurrently in multiple threads. + + If `worker_func` is a single callable, it is used for all threads. + If it is a list of callables, each callable is used for one thread. """ + from collections.abc import Iterable + + if nthreads is None: + nthreads = len(worker_func) + if not isinstance(worker_func, Iterable): + worker_func = [worker_func] * nthreads + assert len(worker_func) == nthreads + barrier = threading.Barrier(nthreads) - def wrapper_func(*args, **kwargs): + def wrapper_func(func, *args, **kwargs): # Wait for all threads to reach this point before proceeding. barrier.wait() - worker_func(*args, **kwargs) + func(*args, **kwargs) with catch_threading_exception() as cm: workers = [ - threading.Thread(target=wrapper_func, args=args, kwargs=kwargs) - for _ in range(nthreads) + threading.Thread(target=wrapper_func, args=(func, *args), kwargs=kwargs) + for func in worker_func ] with start_threads(workers): pass diff --git a/Lib/test/support/warnings_helper.py b/Lib/test/support/warnings_helper.py index 5f6f14afd74a6e..c046f39fbff511 100644 --- a/Lib/test/support/warnings_helper.py +++ b/Lib/test/support/warnings_helper.py @@ -1,11 +1,11 @@ import contextlib -import functools import importlib import re import sys import warnings + def import_deprecated(name): """Import *name* while suppressing DeprecationWarning.""" with warnings.catch_warnings(): @@ -42,20 +42,32 @@ def check_syntax_warning(testcase, statement, errtext='', testcase.assertEqual(warns, []) -def ignore_warnings(*, category): +@contextlib.contextmanager +def ignore_warnings(*, category, message=''): """Decorator to suppress warnings. - Use of context managers to hide warnings make diffs - more noisy and tools like 'git blame' less useful. + Can also be used as a context manager. This is not preferred, + because it makes diffs more noisy and tools like 'git blame' less useful. + But, it's useful for async functions. """ - def decorator(test): - @functools.wraps(test) - def wrapper(self, *args, **kwargs): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=category) - return test(self, *args, **kwargs) - return wrapper - return decorator + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', category=category, message=message) + yield + + +@contextlib.contextmanager +def ignore_fork_in_thread_deprecation_warnings(): + """Suppress deprecation warnings related to forking in multi-threaded code. + + See gh-135427 + + Can be used as decorator (preferred) or context manager. + """ + with ignore_warnings( + message=".*fork.*may lead to deadlocks in the child.*", + category=DeprecationWarning, + ): + yield class WarningsRecorder(object): diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index f35b1194308262..8ded9f99248372 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -72,6 +72,8 @@ def check_all(self, modname): all_set = set(all_list) self.assertCountEqual(all_set, all_list, "in module {}".format(modname)) self.assertEqual(keys, all_set, "in module {}".format(modname)) + # Verify __dir__ is non-empty and doesn't produce an error + self.assertTrue(dir(sys.modules[modname])) def walk_modules(self, basedir, modpath): for fn in sorted(os.listdir(basedir)): diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py index b2f0bb1386fe5b..48fa52bfd5672c 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -1,9 +1,11 @@ import contextlib +import dataclasses import io import sys import unittest import unittest.mock import _colorize +from test.support import cpython_only, import_helper from test.support.os_helper import EnvironmentVarGuard @@ -21,6 +23,51 @@ def supports_virtual_terminal(): return contextlib.nullcontext() +class TestImportTime(unittest.TestCase): + + @cpython_only + def test_lazy_import(self): + import_helper.ensure_lazy_imports( + "_colorize", {"copy", "re", "inspect"} + ) + + +class TestTheme(unittest.TestCase): + + def test_attributes(self): + # only theme configurations attributes by default + for field in dataclasses.fields(_colorize.Theme): + with self.subTest(field.name): + self.assertIsSubclass(field.type, _colorize.ThemeSection) + self.assertIsNotNone(field.default_factory) + + def test_copy_with(self): + theme = _colorize.Theme() + + copy = theme.copy_with() + self.assertEqual(theme, copy) + + unittest_no_colors = _colorize.Unittest.no_colors() + copy = theme.copy_with(unittest=unittest_no_colors) + self.assertEqual(copy.argparse, theme.argparse) + self.assertEqual(copy.difflib, theme.difflib) + self.assertEqual(copy.syntax, theme.syntax) + self.assertEqual(copy.traceback, theme.traceback) + self.assertEqual(copy.unittest, unittest_no_colors) + + def test_no_colors(self): + # idempotence test + theme_no_colors = _colorize.Theme().no_colors() + theme_no_colors_no_colors = theme_no_colors.no_colors() + self.assertEqual(theme_no_colors, theme_no_colors_no_colors) + + # attributes check + for section in dataclasses.fields(_colorize.Theme): + with self.subTest(section.name): + section_theme = getattr(theme_no_colors, section.name) + self.assertEqual(section_theme, section.type.no_colors()) + + class TestColorizeFunction(unittest.TestCase): def test_colorized_detection_checks_for_environment_variables(self): def check(env, fallback, expected): @@ -129,6 +176,17 @@ def test_colorized_detection_checks_for_file(self): file.isatty.return_value = False self.assertEqual(_colorize.can_colorize(file=file), False) + # The documentation for file.fileno says: + # > An OSError is raised if the IO object does not use a file descriptor. + # gh-141570: Check OSError is caught and handled + with unittest.mock.patch("os.isatty", side_effect=ZeroDivisionError): + file = unittest.mock.MagicMock() + file.fileno.side_effect = OSError + file.isatty.return_value = True + self.assertEqual(_colorize.can_colorize(file=file), True) + file.isatty.return_value = False + self.assertEqual(_colorize.can_colorize(file=file), False) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test__interpchannels.py b/Lib/test/test__interpchannels.py index 858d31a73cf4f4..2b0aba42896c06 100644 --- a/Lib/test/test__interpchannels.py +++ b/Lib/test/test__interpchannels.py @@ -6,7 +6,7 @@ import time import unittest -from test.support import import_helper, skip_if_sanitizer +from test.support import import_helper _channels = import_helper.import_module('_interpchannels') from concurrent.interpreters import _crossinterp @@ -365,7 +365,6 @@ def test_shareable(self): #self.assertIsNot(got, obj) -@skip_if_sanitizer('gh-129824: race on _waiting_release', thread=True) class ChannelTests(TestBase): def test_create_cid(self): @@ -383,6 +382,38 @@ def test_sequential_ids(self): self.assertEqual(id3, int(id2) + 1) self.assertEqual(set(after) - set(before), {id1, id2, id3}) + def test_channel_list_all_closed(self): + id1 = _channels.create() + id2 = _channels.create() + id3 = _channels.create() + before = _channels.list_all() + expected = [info for info in before if info[0] != id2] + _channels.close(id2, force=True) + after = _channels.list_all() + self.assertEqual(set(after), set(expected)) + self.assertEqual(len(after), len(before) - 1) + + def test_channel_list_all_destroyed(self): + id1 = _channels.create() + id2 = _channels.create() + id3 = _channels.create() + before = _channels.list_all() + expected = [info for info in before if info[0] != id2] + _channels.destroy(id2) + after = _channels.list_all() + self.assertEqual(set(after), set(expected)) + self.assertEqual(len(after), len(before) - 1) + + def test_channel_list_all_released(self): + id1 = _channels.create() + id2 = _channels.create() + id3 = _channels.create() + before = _channels.list_all() + _channels.release(id2, send=True, recv=True) + after = _channels.list_all() + self.assertEqual(set(after), set(before)) + self.assertEqual(len(after), len(before)) + def test_ids_global(self): id1 = _interpreters.create() out = _run_output(id1, dedent(""" diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py index a32d5d81d2bf2d..ec5a26bc1c05ad 100644 --- a/Lib/test/test__interpreters.py +++ b/Lib/test/test__interpreters.py @@ -474,19 +474,19 @@ def setUp(self): def test_signatures(self): # See https://github.com/python/cpython/issues/126654 - msg = r'_interpreters.exec\(\) argument 3 must be dict, not int' + msg = r"exec\(\) argument 'shared' must be dict, not int" with self.assertRaisesRegex(TypeError, msg): _interpreters.exec(self.id, 'a', 1) with self.assertRaisesRegex(TypeError, msg): _interpreters.exec(self.id, 'a', shared=1) - msg = r'_interpreters.run_string\(\) argument 3 must be dict, not int' + msg = r"run_string\(\) argument 'shared' must be dict, not int" with self.assertRaisesRegex(TypeError, msg): _interpreters.run_string(self.id, 'a', shared=1) - msg = r'_interpreters.run_func\(\) argument 3 must be dict, not int' + msg = r"run_func\(\) argument 'shared' must be dict, not int" with self.assertRaisesRegex(TypeError, msg): _interpreters.run_func(self.id, lambda: None, shared=1) # See https://github.com/python/cpython/issues/135855 - msg = r'_interpreters.set___main___attrs\(\) argument 2 must be dict, not int' + msg = r"set___main___attrs\(\) argument 'updates' must be dict, not int" with self.assertRaisesRegex(TypeError, msg): _interpreters.set___main___attrs(self.id, 1) diff --git a/Lib/test/test__locale.py b/Lib/test/test__locale.py index 11b2c9545a1b43..29a29ca0c65097 100644 --- a/Lib/test/test__locale.py +++ b/Lib/test/test__locale.py @@ -89,6 +89,9 @@ def accept(loc): 'ar_AE': (100, {0: '\u0660', 10: '\u0661\u0660', 99: '\u0669\u0669'}), 'bn_IN': (100, {0: '\u09e6', 10: '\u09e7\u09e6', 99: '\u09ef\u09ef'}), } +if sys.platform == 'cygwin': + count, samples = known_alt_digits['ja_JP'] + known_alt_digits['ja_JP'] = (101, samples) known_era = { 'C': (0, ''), diff --git a/Lib/test/test__osx_support.py b/Lib/test/test__osx_support.py index 0813c4804c1cdc..b92ce6796b50fb 100644 --- a/Lib/test/test__osx_support.py +++ b/Lib/test/test__osx_support.py @@ -25,7 +25,7 @@ def setUp(self): 'CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC', 'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS', - 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS' + 'PY_CORE_CFLAGS', 'PY_CORE_LDFLAGS', 'PY_CORE_EXE_LDFLAGS' ) def add_expected_saved_initial_values(self, config_vars, expected_vars): diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index 80ee9e0ba56e75..59a45a2eda07b0 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -11,20 +11,30 @@ import abc import _py_abc from inspect import isabstract +from test.support import warnings_helper def test_factory(abc_ABCMeta, abc_get_cache_token): class TestLegacyAPI(unittest.TestCase): def test_abstractproperty_basics(self): - @abc.abstractproperty - def foo(self): pass + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractproperty', + ): + @abc.abstractproperty + def foo(self): pass + self.assertTrue(foo.__isabstractmethod__) def bar(self): pass self.assertNotHasAttr(bar, "__isabstractmethod__") - class C(metaclass=abc_ABCMeta): - @abc.abstractproperty - def foo(self): return 3 + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractproperty', + ): + class C(metaclass=abc_ABCMeta): + @abc.abstractproperty + def foo(self): return 3 self.assertRaises(TypeError, C) class D(C): @property @@ -33,16 +43,26 @@ def foo(self): return super().foo self.assertFalse(getattr(D.foo, "__isabstractmethod__", False)) def test_abstractclassmethod_basics(self): - @abc.abstractclassmethod - def foo(cls): pass + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractclassmethod', + ): + @abc.abstractclassmethod + def foo(cls): pass + self.assertTrue(foo.__isabstractmethod__) @classmethod def bar(cls): pass self.assertFalse(getattr(bar, "__isabstractmethod__", False)) - class C(metaclass=abc_ABCMeta): - @abc.abstractclassmethod - def foo(cls): return cls.__name__ + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractclassmethod', + ): + class C(metaclass=abc_ABCMeta): + @abc.abstractclassmethod + def foo(cls): return cls.__name__ + self.assertRaises(TypeError, C) class D(C): @classmethod @@ -51,16 +71,26 @@ def foo(cls): return super().foo() self.assertEqual(D().foo(), 'D') def test_abstractstaticmethod_basics(self): - @abc.abstractstaticmethod - def foo(): pass + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractstaticmethod', + ): + @abc.abstractstaticmethod + def foo(): pass + self.assertTrue(foo.__isabstractmethod__) @staticmethod def bar(): pass self.assertFalse(getattr(bar, "__isabstractmethod__", False)) - class C(metaclass=abc_ABCMeta): - @abc.abstractstaticmethod - def foo(): return 3 + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractstaticmethod', + ): + class C(metaclass=abc_ABCMeta): + @abc.abstractstaticmethod + def foo(): return 3 + self.assertRaises(TypeError, C) class D(C): @staticmethod @@ -168,6 +198,7 @@ def method_two(self): msg = r"class C without an implementation for abstract methods 'method_one', 'method_two'" self.assertRaisesRegex(TypeError, msg, C) + @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_abstractmethod_integration(self): for abstractthing in [abc.abstractmethod, abc.abstractproperty, abc.abstractclassmethod, diff --git a/Lib/test/test_android.py b/Lib/test/test_android.py index de83ce081c2790..31daafbc3d6300 100644 --- a/Lib/test/test_android.py +++ b/Lib/test/test_android.py @@ -1,5 +1,4 @@ import io -import platform import queue import re import subprocess @@ -17,8 +16,6 @@ if sys.platform != "android": raise unittest.SkipTest("Android-specific") -api_level = platform.android_ver().api_level - # (name, level, fileno) STREAM_INFO = [("stdout", "I", 1), ("stderr", "W", 2)] @@ -91,34 +88,38 @@ def tearDown(self): self.logcat_thread = None @contextmanager - def unbuffered(self, stream): - stream.reconfigure(write_through=True) + def reconfigure(self, stream, **settings): + original_settings = {key: getattr(stream, key, None) for key in settings.keys()} + stream.reconfigure(**settings) try: yield finally: - stream.reconfigure(write_through=False) + stream.reconfigure(**original_settings) - # In --verbose3 mode, sys.stdout and sys.stderr are captured, so we can't - # test them directly. Detect this mode and use some temporary streams with - # the same properties. def stream_context(self, stream_name, level): - # https://developer.android.com/ndk/reference/group/logging - prio = {"I": 4, "W": 5}[level] - stack = ExitStack() stack.enter_context(self.subTest(stream_name)) + + # In --verbose3 mode, sys.stdout and sys.stderr are captured, so we can't + # test them directly. Detect this mode and use some temporary streams with + # the same properties. stream = getattr(sys, stream_name) native_stream = getattr(sys, f"__{stream_name}__") if isinstance(stream, io.StringIO): + # https://developer.android.com/ndk/reference/group/logging + prio = {"I": 4, "W": 5}[level] stack.enter_context( patch( f"sys.{stream_name}", - TextLogStream( - prio, f"python.{stream_name}", native_stream.fileno(), - errors="backslashreplace" + stream := TextLogStream( + prio, f"python.{stream_name}", native_stream, ), ) ) + + # The tests assume the stream is initially buffered. + stack.enter_context(self.reconfigure(stream, write_through=False)) + return stack def test_str(self): @@ -145,7 +146,7 @@ def write(s, lines=None, *, write_len=None): self.assert_logs(level, tag, lines) # Single-line messages, - with self.unbuffered(stream): + with self.reconfigure(stream, write_through=True): write("", []) write("a") @@ -175,14 +176,18 @@ def write(s, lines=None, *, write_len=None): # Multi-line messages. Avoid identical consecutive lines, as # they may activate "chatty" filtering and break the tests. - write("\nx", [""]) + # + # Additional spaces will appear in the output where necessary to + # protect leading newlines. + write("\nx", [" "]) write("\na\n", ["x", "a"]) - write("\n", [""]) + write("\n", [" "]) + write("\n\n", [" ", " "]) write("b\n", ["b"]) - write("c\n\n", ["c", ""]) + write("c\n\n", ["c", " "]) write("d\ne", ["d"]) write("xx", []) - write("f\n\ng", ["exxf", ""]) + write("f\n\ng", ["exxf", " "]) write("\n", ["g"]) # Since this is a line-based logging system, line buffering @@ -192,16 +197,17 @@ def write(s, lines=None, *, write_len=None): # However, buffering can be turned off completely if you want a # flush after every write. - with self.unbuffered(stream): - write("\nx", ["", "x"]) - write("\na\n", ["", "a"]) - write("\n", [""]) + with self.reconfigure(stream, write_through=True): + write("\nx", [" ", "x"]) + write("\na\n", [" ", "a"]) + write("\n", [" "]) + write("\n\n", [" ", " "]) write("b\n", ["b"]) - write("c\n\n", ["c", ""]) + write("c\n\n", ["c", " "]) write("d\ne", ["d", "e"]) write("xx", ["xx"]) - write("f\n\ng", ["f", "", "g"]) - write("\n", [""]) + write("f\n\ng", ["f", " ", "g"]) + write("\n", [" "]) # "\r\n" should be translated into "\n". write("hello\r\n", ["hello"]) @@ -321,19 +327,16 @@ def write(b, lines=None, *, write_len=None): # currently use `logcat -v tag`, which shows each line as if it # was a separate log entry, but strips a single trailing # newline. - # - # On newer versions of Android, all three of the above tools (or - # maybe Logcat itself) will also strip any number of leading - # newlines. - write(b"\nx", ["", "x"] if api_level < 30 else ["x"]) - write(b"\na\n", ["", "a"] if api_level < 30 else ["a"]) - write(b"\n", [""]) + write(b"\nx", [" ", "x"]) + write(b"\na\n", [" ", "a"]) + write(b"\n", [" "]) + write(b"\n\n", [" ", ""]) write(b"b\n", ["b"]) write(b"c\n\n", ["c", ""]) write(b"d\ne", ["d", "e"]) write(b"xx", ["xx"]) write(b"f\n\ng", ["f", "", "g"]) - write(b"\n", [""]) + write(b"\n", [" "]) # "\r\n" should be translated into "\n". write(b"hello\r\n", ["hello"]) diff --git a/Lib/test/test_annotationlib.py b/Lib/test/test_annotationlib.py index ae0e73f08c5bd0..5087c3ca425f1f 100644 --- a/Lib/test/test_annotationlib.py +++ b/Lib/test/test_annotationlib.py @@ -7,8 +7,10 @@ import functools import itertools import pickle -from string.templatelib import Template +from string.templatelib import Template, Interpolation +import types import typing +import sys import unittest from annotationlib import ( Format, @@ -73,6 +75,30 @@ def inner(arg: x): anno = get_annotations(inner, format=Format.FORWARDREF) self.assertEqual(anno["arg"], x) + def test_multiple_closure(self): + def inner(arg: x[y]): + pass + + fwdref = get_annotations(inner, format=Format.FORWARDREF)["arg"] + self.assertIsInstance(fwdref, ForwardRef) + self.assertEqual(fwdref.__forward_arg__, "x[y]") + with self.assertRaises(NameError): + fwdref.evaluate() + + y = str + fwdref = get_annotations(inner, format=Format.FORWARDREF)["arg"] + self.assertIsInstance(fwdref, ForwardRef) + extra_name, extra_val = next(iter(fwdref.__extra_names__.items())) + self.assertEqual(fwdref.__forward_arg__.replace(extra_name, extra_val.__name__), "x[str]") + with self.assertRaises(NameError): + fwdref.evaluate() + + x = list + self.assertEqual(fwdref.evaluate(), x[y]) + + fwdref = get_annotations(inner, format=Format.FORWARDREF)["arg"] + self.assertEqual(fwdref, x[y]) + def test_function(self): def f(x: int, y: doesntexist): pass @@ -94,6 +120,10 @@ def f( alpha: some | obj, beta: +some, gamma: some < obj, + delta: some | {obj: module}, + epsilon: some | {obj}, + zeta: some | [obj, module], + eta: some | (), ): pass @@ -122,6 +152,69 @@ def f( self.assertIsInstance(gamma_anno, ForwardRef) self.assertEqual(gamma_anno, support.EqualToForwardRef("some < obj", owner=f)) + delta_anno = anno["delta"] + self.assertIsInstance(delta_anno, ForwardRef) + self.assertEqual(delta_anno, support.EqualToForwardRef("some | {obj: module}", owner=f)) + + epsilon_anno = anno["epsilon"] + self.assertIsInstance(epsilon_anno, ForwardRef) + self.assertEqual(epsilon_anno, support.EqualToForwardRef("some | {obj}", owner=f)) + + zeta_anno = anno["zeta"] + self.assertIsInstance(zeta_anno, ForwardRef) + self.assertEqual(zeta_anno, support.EqualToForwardRef("some | [obj, module]", owner=f)) + + eta_anno = anno["eta"] + self.assertIsInstance(eta_anno, ForwardRef) + self.assertEqual(eta_anno, support.EqualToForwardRef("some | ()", owner=f)) + + def test_partially_nonexistent(self): + # These annotations start with a non-existent variable and then use + # global types with defined values. This partially evaluates by putting + # those globals into `fwdref.__extra_names__`. + def f( + x: obj | int, + y: container[int:obj, int], + z: dict_val | {str: int}, + alpha: set_val | {str, int}, + beta: obj | bool | int, + gamma: obj | call_func(int, kwd=bool), + ): + pass + + def func(*args, **kwargs): + return Union[*args, *(kwargs.values())] + + anno = get_annotations(f, format=Format.FORWARDREF) + globals_ = { + "obj": str, "container": list, "dict_val": {1: 2}, "set_val": {1, 2}, + "call_func": func + } + + x_anno = anno["x"] + self.assertIsInstance(x_anno, ForwardRef) + self.assertEqual(x_anno.evaluate(globals=globals_), str | int) + + y_anno = anno["y"] + self.assertIsInstance(y_anno, ForwardRef) + self.assertEqual(y_anno.evaluate(globals=globals_), list[int:str, int]) + + z_anno = anno["z"] + self.assertIsInstance(z_anno, ForwardRef) + self.assertEqual(z_anno.evaluate(globals=globals_), {1: 2} | {str: int}) + + alpha_anno = anno["alpha"] + self.assertIsInstance(alpha_anno, ForwardRef) + self.assertEqual(alpha_anno.evaluate(globals=globals_), {1, 2} | {str, int}) + + beta_anno = anno["beta"] + self.assertIsInstance(beta_anno, ForwardRef) + self.assertEqual(beta_anno.evaluate(globals=globals_), str | bool | int) + + gamma_anno = anno["gamma"] + self.assertIsInstance(gamma_anno, ForwardRef) + self.assertEqual(gamma_anno.evaluate(globals=globals_), str | func(int, kwd=bool)) + def test_partially_nonexistent_union(self): # Test unions with '|' syntax equal unions with typing.Union[] with some forwardrefs class UnionForwardrefs: @@ -282,6 +375,7 @@ def f( a: t"a{b}c{d}e{f}g", b: t"{a:{1}}", c: t"{a | b * c}", + gh138558: t"{ 0}", ): pass annos = get_annotations(f, format=Format.STRING) @@ -293,6 +387,7 @@ def f( # interpolations in the format spec are eagerly evaluated so we can't recover the source "b": "t'{a:1}'", "c": "t'{a | b * c}'", + "gh138558": "t'{ 0}'", }) def g( @@ -551,6 +646,31 @@ def foo(): get_annotations(foo, format=Format.FORWARDREF, eval_str=True) get_annotations(foo, format=Format.STRING, eval_str=True) + def test_eval_str_wrapped_cycle_self(self): + # gh-146556: self-referential __wrapped__ cycle must not hang. + def f(x: 'int') -> 'str': ... + f.__wrapped__ = f + # Cycle is detected and broken; globals from f itself are used. + result = get_annotations(f, eval_str=True) + self.assertEqual(result, {'x': int, 'return': str}) + + def test_eval_str_wrapped_cycle_mutual(self): + # gh-146556: mutual __wrapped__ cycle (a -> b -> a) must not hang. + def a(x: 'int'): ... + def b(): ... + a.__wrapped__ = b + b.__wrapped__ = a + result = get_annotations(a, eval_str=True) + self.assertEqual(result, {'x': int}) + + def test_eval_str_wrapped_chain_no_cycle(self): + # gh-146556: a valid (non-cyclic) __wrapped__ chain must still work. + def inner(x: 'int'): ... + def outer(x: 'int'): ... + outer.__wrapped__ = inner + result = get_annotations(outer, eval_str=True) + self.assertEqual(result, {'x': int}) + def test_stock_annotations(self): def foo(a: int, b: str): pass @@ -753,6 +873,8 @@ def test_stringized_annotations_in_module(self): for kwargs in [ {"eval_str": True}, + {"eval_str": True, "globals": isa.__dict__, "locals": {}}, + {"eval_str": True, "globals": {}, "locals": isa.__dict__}, {"format": Format.VALUE, "eval_str": True}, ]: with self.subTest(**kwargs): @@ -785,6 +907,15 @@ def test_stringized_annotations_in_empty_module(self): self.assertEqual(get_annotations(isa2, eval_str=True), {}) self.assertEqual(get_annotations(isa2, eval_str=False), {}) + def test_stringized_annotations_with_star_unpack(self): + def f(*args: "*tuple[int, ...]"): ... + self.assertEqual(get_annotations(f, eval_str=True), + {'args': (*tuple[int, ...],)[0]}) + def f(*args: " *tuple[int, ...]"): ... + self.assertEqual(get_annotations(f, eval_str=True), + {'args': (*tuple[int, ...],)[0]}) + + def test_stringized_annotations_on_wrapper(self): isa = inspect_stringized_annotations wrapped = times_three(isa.function) @@ -803,6 +934,44 @@ def test_stringized_annotations_on_wrapper(self): {"a": "int", "b": "str", "return": "MyClass"}, ) + def test_stringized_annotations_on_partial_wrapper(self): + isa = inspect_stringized_annotations + + def times_three_str(fn: typing.Callable[[str], isa.MyClass]): + @functools.wraps(fn) + def wrapper(b: "str") -> "MyClass": + return fn(b * 3) + + return wrapper + + wrapped = times_three_str(functools.partial(isa.function, 1)) + self.assertEqual(wrapped("x"), isa.MyClass(1, "xxx")) + self.assertIsNot(wrapped.__globals__, isa.function.__globals__) + self.assertEqual( + get_annotations(wrapped, eval_str=True), + {"b": str, "return": isa.MyClass}, + ) + self.assertEqual( + get_annotations(wrapped, eval_str=False), + {"b": "str", "return": "MyClass"}, + ) + + # If functools is not loaded, names will be evaluated in the current + # module instead of being unwrapped to the original. + functools_mod = sys.modules["functools"] + del sys.modules["functools"] + + self.assertEqual( + get_annotations(wrapped, eval_str=True), + {"b": str, "return": MyClass}, + ) + self.assertEqual( + get_annotations(wrapped, eval_str=False), + {"b": "str", "return": "MyClass"}, + ) + + sys.modules["functools"] = functools_mod + def test_stringized_annotations_on_class(self): isa = inspect_stringized_annotations # test that local namespace lookups work @@ -815,6 +984,16 @@ def test_stringized_annotations_on_class(self): {"x": int}, ) + def test_stringized_annotations_on_custom_object(self): + class HasAnnotations: + @property + def __annotations__(self): + return {"x": "int"} + + ha = HasAnnotations() + self.assertEqual(get_annotations(ha), {"x": "int"}) + self.assertEqual(get_annotations(ha, eval_str=True), {"x": int}) + def test_stringized_annotation_permutations(self): def define_class(name, has_future, has_annos, base_text, extra_names=None): lines = [] @@ -982,6 +1161,23 @@ def __annotate__(self): {"x": "int"}, ) + def test_non_dict_annotate(self): + class WeirdAnnotate: + def __annotate__(self, *args, **kwargs): + return "not a dict" + + wa = WeirdAnnotate() + for format in Format: + if format == Format.VALUE_WITH_FAKE_GLOBALS: + continue + with ( + self.subTest(format=format), + self.assertRaisesRegex( + ValueError, r".*__annotate__ returned a non-dict" + ), + ): + get_annotations(wa, format=format) + def test_no_annotations(self): class CustomClass: pass @@ -1186,6 +1382,40 @@ class RaisesAttributeError: }, ) + def test_nonlocal_in_annotation_scope(self): + class Demo: + nonlocal sequence_b + x: sequence_b + y: sequence_b[int] + + fwdrefs = get_annotations(Demo, format=Format.FORWARDREF) + + self.assertIsInstance(fwdrefs["x"], ForwardRef) + self.assertIsInstance(fwdrefs["y"], ForwardRef) + + sequence_b = list + self.assertIs(fwdrefs["x"].evaluate(), list) + self.assertEqual(fwdrefs["y"].evaluate(), list[int]) + + def test_raises_error_from_value(self): + # test that if VALUE is the only supported format, but raises an error + # that error is propagated from get_annotations + class DemoException(Exception): ... + + def annotate(format, /): + if format == Format.VALUE: + raise DemoException() + else: + raise NotImplementedError(format) + + def f(): ... + + f.__annotate__ = annotate + + for fmt in [Format.VALUE, Format.FORWARDREF, Format.STRING]: + with self.assertRaises(DemoException): + get_annotations(f, format=fmt) + class TestCallEvaluateFunction(unittest.TestCase): def test_evaluation(self): @@ -1205,6 +1435,284 @@ def evaluate(format, exc=NotImplementedError): "undefined", ) + def test_fake_global_evaluation(self): + # This will raise an AttributeError + def evaluate_union(format, exc=NotImplementedError): + if format == Format.VALUE_WITH_FAKE_GLOBALS: + # Return a ForwardRef + return builtins.undefined | list[int] + raise exc + + self.assertEqual( + annotationlib.call_evaluate_function(evaluate_union, Format.FORWARDREF), + support.EqualToForwardRef("builtins.undefined | list[int]"), + ) + + # This will raise an AttributeError + def evaluate_intermediate(format, exc=NotImplementedError): + if format == Format.VALUE_WITH_FAKE_GLOBALS: + intermediate = builtins.undefined + # Return a literal + return intermediate is None + raise exc + + self.assertIs( + annotationlib.call_evaluate_function(evaluate_intermediate, Format.FORWARDREF), + False, + ) + + +class TestCallAnnotateFunction(unittest.TestCase): + # Tests for user defined annotate functions. + + # Format and NotImplementedError are provided as arguments so they exist in + # the fake globals namespace. + # This avoids non-matching conditions passing by being converted to stringifiers. + # See: https://github.com/python/cpython/issues/138764 + + def test_user_annotate_value(self): + def annotate(format, /): + if format == Format.VALUE: + return {"x": str} + else: + raise NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.VALUE, + ) + + self.assertEqual(annotations, {"x": str}) + + def test_user_annotate_forwardref_supported(self): + # If Format.FORWARDREF is supported prefer it over Format.VALUE + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {'x': str} + elif format == __Format.VALUE_WITH_FAKE_GLOBALS: + return {'x': int} + elif format == __Format.FORWARDREF: + return {'x': float} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.FORWARDREF + ) + + self.assertEqual(annotations, {"x": float}) + + def test_user_annotate_forwardref_fakeglobals(self): + # If Format.FORWARDREF is not supported, use Format.VALUE_WITH_FAKE_GLOBALS + # before falling back to Format.VALUE + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {'x': str} + elif format == __Format.VALUE_WITH_FAKE_GLOBALS: + return {'x': int} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.FORWARDREF + ) + + self.assertEqual(annotations, {"x": int}) + + def test_user_annotate_forwardref_value_fallback(self): + # If Format.FORWARDREF and Format.VALUE_WITH_FAKE_GLOBALS are not supported + # use Format.VALUE + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {"x": str} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.FORWARDREF, + ) + + self.assertEqual(annotations, {"x": str}) + + def test_user_annotate_string_supported(self): + # If Format.STRING is supported prefer it over Format.VALUE + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {'x': str} + elif format == __Format.VALUE_WITH_FAKE_GLOBALS: + return {'x': int} + elif format == __Format.STRING: + return {'x': "float"} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.STRING, + ) + + self.assertEqual(annotations, {"x": "float"}) + + def test_user_annotate_string_fakeglobals(self): + # If Format.STRING is not supported but Format.VALUE_WITH_FAKE_GLOBALS is + # prefer that over Format.VALUE + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {'x': str} + elif format == __Format.VALUE_WITH_FAKE_GLOBALS: + return {'x': int} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.STRING, + ) + + self.assertEqual(annotations, {"x": "int"}) + + def test_user_annotate_string_value_fallback(self): + # If Format.STRING and Format.VALUE_WITH_FAKE_GLOBALS are not + # supported fall back to Format.VALUE and convert to strings + def annotate(format, /, __Format=Format, __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {"x": str} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function( + annotate, + Format.STRING, + ) + + self.assertEqual(annotations, {"x": "str"}) + + def test_condition_not_stringified(self): + # Make sure the first condition isn't evaluated as True by being converted + # to a _Stringifier + def annotate(format, /): + if format == Format.FORWARDREF: + return {"x": str} + else: + raise NotImplementedError(format) + + with self.assertRaises(NotImplementedError): + annotationlib.call_annotate_function(annotate, Format.STRING) + + def test_unsupported_formats(self): + def annotate(format, /): + if format == Format.FORWARDREF: + return {"x": str} + else: + raise NotImplementedError(format) + + with self.assertRaises(ValueError): + annotationlib.call_annotate_function(annotate, Format.VALUE_WITH_FAKE_GLOBALS) + + with self.assertRaises(RuntimeError): + annotationlib.call_annotate_function(annotate, Format.VALUE) + + with self.assertRaises(ValueError): + # Some non-Format value + annotationlib.call_annotate_function(annotate, 7) + + def test_basic_non_function_annotate(self): + class Annotate: + def __call__(self, format, /, __Format=Format, + __NotImplementedError=NotImplementedError): + if format == __Format.VALUE: + return {'x': str} + elif format == __Format.VALUE_WITH_FAKE_GLOBALS: + return {'x': int} + elif format == __Format.STRING: + return {'x': "float"} + else: + raise __NotImplementedError(format) + + annotations = annotationlib.call_annotate_function(Annotate(), Format.VALUE) + self.assertEqual(annotations, {"x": str}) + + annotations = annotationlib.call_annotate_function(Annotate(), Format.STRING) + self.assertEqual(annotations, {"x": "float"}) + + with self.assertRaises(AttributeError) as cm: + annotations = annotationlib.call_annotate_function( + Annotate(), Format.FORWARDREF + ) + + self.assertEqual(cm.exception.name, "__builtins__") + self.assertIsInstance(cm.exception.obj, Annotate) + + def test_full_non_function_annotate(self): + def outer(): + local = str + + class Annotate: + called_formats = [] + + def __call__(self, format=None, *, _self=None): + nonlocal local + if _self is not None: + self, format = _self, self + + self.called_formats.append(format) + if format == 1: # VALUE + return {"x": MyClass, "y": int, "z": local} + if format == 2: # VALUE_WITH_FAKE_GLOBALS + return {"w": unknown, "x": MyClass, "y": int, "z": local} + raise NotImplementedError + + __globals__ = {"MyClass": MyClass} + __builtins__ = {"int": int} + __closure__ = (types.CellType(str),) + __defaults__ = (None,) + + __kwdefaults__ = property(lambda self: dict(_self=self)) + __code__ = property(lambda self: self.__call__.__code__) + + return Annotate() + + annotate = outer() + + self.assertEqual( + annotationlib.call_annotate_function(annotate, Format.VALUE), + {"x": MyClass, "y": int, "z": str} + ) + self.assertEqual(annotate.called_formats[-1], Format.VALUE) + + self.assertEqual( + annotationlib.call_annotate_function(annotate, Format.STRING), + {"w": "unknown", "x": "MyClass", "y": "int", "z": "local"} + ) + self.assertIn(Format.STRING, annotate.called_formats) + self.assertEqual(annotate.called_formats[-1], Format.VALUE_WITH_FAKE_GLOBALS) + + self.assertEqual( + annotationlib.call_annotate_function(annotate, Format.FORWARDREF), + {"w": support.EqualToForwardRef("unknown"), "x": MyClass, "y": int, "z": str} + ) + self.assertIn(Format.FORWARDREF, annotate.called_formats) + self.assertEqual(annotate.called_formats[-1], Format.VALUE_WITH_FAKE_GLOBALS) + + def test_error_from_value_raised(self): + # Test that the error from format.VALUE is raised + # if all formats fail + + class DemoException(Exception): ... + + def annotate(format, /): + if format == Format.VALUE: + raise DemoException() + else: + raise NotImplementedError(format) + + for fmt in [Format.VALUE, Format.FORWARDREF, Format.STRING]: + with self.assertRaises(DemoException): + annotationlib.call_annotate_function(annotate, format=fmt) + class MetaclassTests(unittest.TestCase): def test_annotated_meta(self): @@ -1350,6 +1858,24 @@ def nested(): self.assertEqual(type_repr("1"), "'1'") self.assertEqual(type_repr(Format.VALUE), repr(Format.VALUE)) self.assertEqual(type_repr(MyClass()), "my repr") + # gh138558 tests + self.assertEqual(type_repr(t'''{ 0 + & 1 + | 2 + }'''), 't"""{ 0\n & 1\n | 2}"""') + self.assertEqual( + type_repr(Template("hi", Interpolation(42, "42"))), "t'hi{42}'" + ) + self.assertEqual( + type_repr(Template("hi", Interpolation(42))), + "Template('hi', Interpolation(42, '', None, ''))", + ) + self.assertEqual( + type_repr(Template("hi", Interpolation(42, " "))), + "Template('hi', Interpolation(42, ' ', None, ''))", + ) + # gh138558: perhaps in the future, we can improve this behavior: + self.assertEqual(type_repr(Template(Interpolation(42, "99"))), "t'{99}'") class TestAnnotationsToString(unittest.TestCase): @@ -1365,6 +1891,11 @@ def test_annotations_to_string(self): class A: pass +TypeParamsAlias1 = int + +class TypeParamsSample[TypeParamsAlias1, TypeParamsAlias2]: + TypeParamsAlias2 = str + class TestForwardRefClass(unittest.TestCase): def test_forwardref_instance_type_error(self): @@ -1435,6 +1966,39 @@ def foo(a: c1_gth, b: c2_gth): self.assertNotEqual(hash(c3), hash(c4)) self.assertEqual(hash(c3), hash(ForwardRef("int", module=__name__))) + def test_forward_equality_and_hash_with_cells(self): + """Regression test for GH-143831.""" + class A: + def one(_) -> C1: + """One cell.""" + + one_f = ForwardRef("C1", owner=one) + one_f_ga1 = get_annotations(one, format=Format.FORWARDREF)["return"] + one_f_ga2 = get_annotations(one, format=Format.FORWARDREF)["return"] + self.assertIsInstance(one_f_ga1.__cell__, types.CellType) + self.assertIs(one_f_ga1.__cell__, one_f_ga2.__cell__) + + def two(_) -> C1 | C2: + """Two cells.""" + + two_f_ga1 = get_annotations(two, format=Format.FORWARDREF)["return"] + two_f_ga2 = get_annotations(two, format=Format.FORWARDREF)["return"] + self.assertIsNot(two_f_ga1.__cell__, two_f_ga2.__cell__) + self.assertIsInstance(two_f_ga1.__cell__, dict) + self.assertIsInstance(two_f_ga2.__cell__, dict) + + type C1 = None + type C2 = None + + self.assertNotEqual(A.one_f, A.one_f_ga1) + self.assertNotEqual(hash(A.one_f), hash(A.one_f_ga1)) + + self.assertEqual(A.one_f_ga1, A.one_f_ga2) + self.assertEqual(hash(A.one_f_ga1), hash(A.one_f_ga2)) + + self.assertEqual(A.two_f_ga1, A.two_f_ga2) + self.assertEqual(hash(A.two_f_ga1), hash(A.two_f_ga2)) + def test_forward_equality_namespace(self): def namespace1(): a = ForwardRef("A") @@ -1466,6 +2030,23 @@ def test_forward_repr(self): repr(List[ForwardRef("int", module="mod")]), "typing.List[ForwardRef('int', module='mod')]", ) + self.assertEqual( + repr(List[ForwardRef("int", module="mod", is_class=True)]), + "typing.List[ForwardRef('int', module='mod', is_class=True)]", + ) + self.assertEqual( + repr(List[ForwardRef("int", owner="class")]), + "typing.List[ForwardRef('int', owner='class')]", + ) + + def test_forward_repr_extra_names(self): + def f(a: undefined | str): ... + + annos = get_annotations(f, format=Format.FORWARDREF) + + self.assertRegex( + repr(annos['a']), r"ForwardRef\('undefined \| str'.*\)" + ) def test_forward_recursion_actually(self): def namespace1(): @@ -1543,6 +2124,17 @@ def test_evaluate_string_format(self): fr = ForwardRef("set[Any]") self.assertEqual(fr.evaluate(format=Format.STRING), "set[Any]") + def test_evaluate_string_format_extra_names(self): + # Test that internal extra_names are replaced when evaluating as strings + def f(a: unknown | str | int | list[str] | tuple[int, ...]): ... + + fr = get_annotations(f, format=Format.FORWARDREF)['a'] + # Test the cache is not populated before access + self.assertIsNone(fr.__resolved_str_cache__) + + self.assertEqual(fr.evaluate(format=Format.STRING), "unknown | str | int | list[str] | tuple[int, ...]") + self.assertEqual(fr.__resolved_str_cache__, "unknown | str | int | list[str] | tuple[int, ...]") + def test_evaluate_forwardref_format(self): fr = ForwardRef("undef") evaluated = fr.evaluate(format=Format.FORWARDREF) @@ -1571,6 +2163,19 @@ def test_evaluate_forwardref_format(self): support.EqualToForwardRef('"a" + 1'), ) + def test_evaluate_notimplemented_format(self): + class C: + x: alias + + fwdref = get_annotations(C, format=Format.FORWARDREF)["x"] + + with self.assertRaises(NotImplementedError): + fwdref.evaluate(format=Format.VALUE_WITH_FAKE_GLOBALS) + + with self.assertRaises(NotImplementedError): + # Some other unsupported value + fwdref.evaluate(format=7) + def test_evaluate_with_type_params(self): class Gen[T]: alias = int @@ -1597,6 +2202,21 @@ class Gen[T]: ForwardRef("alias").evaluate(owner=Gen, locals={"alias": str}), str ) + def test_evaluate_with_type_params_and_scope_conflict(self): + for is_class in (False, True): + with self.subTest(is_class=is_class): + fwdref1 = ForwardRef("TypeParamsAlias1", owner=TypeParamsSample, is_class=is_class) + fwdref2 = ForwardRef("TypeParamsAlias2", owner=TypeParamsSample, is_class=is_class) + + self.assertIs( + fwdref1.evaluate(), + TypeParamsSample.__type_params__[0], + ) + self.assertIs( + fwdref2.evaluate(), + TypeParamsSample.TypeParamsAlias2, + ) + def test_fwdref_with_module(self): self.assertIs(ForwardRef("Format", module="annotationlib").evaluate(), Format) self.assertIs( @@ -1655,6 +2275,32 @@ def test_name_lookup_without_eval(self): self.assertEqual(exc.exception.name, "doesntexist") + def test_evaluate_undefined_generic(self): + # Test the codepath where have to eval() with undefined variables. + class C: + x: alias[int, undef] + + generic = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate( + format=Format.FORWARDREF, + globals={"alias": dict} + ) + self.assertNotIsInstance(generic, ForwardRef) + self.assertIs(generic.__origin__, dict) + self.assertEqual(len(generic.__args__), 2) + self.assertIs(generic.__args__[0], int) + self.assertIsInstance(generic.__args__[1], ForwardRef) + + generic = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate( + format=Format.FORWARDREF, + globals={"alias": Union}, + locals={"alias": dict} + ) + self.assertNotIsInstance(generic, ForwardRef) + self.assertIs(generic.__origin__, dict) + self.assertEqual(len(generic.__args__), 2) + self.assertIs(generic.__args__[0], int) + self.assertIsInstance(generic.__args__[1], ForwardRef) + def test_fwdref_invalid_syntax(self): fr = ForwardRef("if") with self.assertRaises(SyntaxError): @@ -1663,6 +2309,56 @@ def test_fwdref_invalid_syntax(self): with self.assertRaises(SyntaxError): fr.evaluate() + def test_re_evaluate_generics(self): + global global_alias + + # If we've already run this test before, + # ensure the variable is still undefined + if "global_alias" in globals(): + del global_alias + + class C: + x: global_alias[int] + + # Evaluate the ForwardRef once + evaluated = get_annotations(C, format=Format.FORWARDREF)["x"].evaluate( + format=Format.FORWARDREF + ) + + # Now define the global and ensure that the ForwardRef evaluates + global_alias = list + self.assertEqual(evaluated.evaluate(), list[int]) + + def test_fwdref_evaluate_argument_mutation(self): + class C[T]: + nonlocal alias + x: alias[T] + + # Mutable arguments + globals_ = globals() + globals_copy = globals_.copy() + locals_ = locals() + locals_copy = locals_.copy() + + # Evaluate the ForwardRef, ensuring we use __cell__ and type params + get_annotations(C, format=Format.FORWARDREF)["x"].evaluate( + globals=globals_, + locals=locals_, + type_params=C.__type_params__, + format=Format.FORWARDREF, + ) + + # Check if the passed in mutable arguments equal the originals + self.assertEqual(globals_, globals_copy) + self.assertEqual(locals_, locals_copy) + + alias = list + + def test_fwdref_final_class(self): + with self.assertRaises(TypeError): + class C(ForwardRef): + pass + class TestAnnotationLib(unittest.TestCase): def test__all__(self): diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index fc73174d98cd6f..1dc3f538f4ad8b 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -7,7 +7,6 @@ import io import operator import os -import py_compile import shutil import stat import sys @@ -22,6 +21,7 @@ captured_stderr, force_not_colorized, force_not_colorized_test_class, + swap_attr, ) from test.support import import_helper from test.support import os_helper @@ -80,6 +80,129 @@ def test_skip_invalid_stdout(self): self.assertRegex(mocked_stderr.getvalue(), r'usage:') +class TestLazyImports(unittest.TestCase): + LAZY_IMPORTS = { + "_colorize", + "copy", + "difflib", + "shutil", + "textwrap", + "warnings", + } + def test_module_import(self): + import_helper.ensure_lazy_imports( + "argparse", + self.LAZY_IMPORTS, + ) + + def test_create_parser(self): + # Test imports are still unused after + # creating a parser + create_parser = "argparse.ArgumentParser()" + imported_modules = {"shutil"} + + import_helper.ensure_lazy_imports( + "argparse", + self.LAZY_IMPORTS - imported_modules, + additional_code=create_parser, + ) + + def test_add_subparser(self): + add_subparser = textwrap.dedent( + """ + parser = argparse.ArgumentParser() + parser.add_subparsers(dest='command', required=False) + """ + ) + imported_modules = {"shutil"} + + import_helper.ensure_lazy_imports( + "argparse", + self.LAZY_IMPORTS - imported_modules, + additional_code=add_subparser, + ) + + def test_parse_args(self): + example_parser = textwrap.dedent( + """ + parser = argparse.ArgumentParser(prog='PROG') + parser.add_argument('-f', '--foo') + parser.add_argument('bar') + parser.parse_args(['BAR']) + parser.parse_args(['BAR', '--foo', 'FOO']) + """ + ) + imported_modules = {"shutil"} + import_helper.ensure_lazy_imports( + "argparse", + self.LAZY_IMPORTS - imported_modules, + additional_code=example_parser + ) + + +class TestArgumentParserCopiable(unittest.TestCase): + def _get_parser(self): + parser = argparse.ArgumentParser(exit_on_error=False) + parser.add_argument('--foo', type=int, default=42) + parser.add_argument('bar', nargs='?', default='baz') + return parser + + @force_not_colorized + def test_copiable(self): + import copy + parser = self._get_parser() + parser2 = copy.copy(parser) + ns = parser2.parse_args(['--foo', '123', 'quux']) + self.assertEqual(ns.foo, 123) + self.assertEqual(ns.bar, 'quux') + ns2 = parser2.parse_args([]) + self.assertEqual(ns2.foo, 42) + self.assertEqual(ns2.bar, 'baz') + + # Test shallow copy also gets new arguments + parser.add_argument("--extra") + ns3 = parser2.parse_args(["--extra", "bar"]) + self.assertEqual(ns3.extra, "bar") + + @force_not_colorized + def test_deepcopiable(self): + import copy + parser = self._get_parser() + parser2 = copy.deepcopy(parser) + ns = parser2.parse_args(['--foo', '123', 'quux']) + self.assertEqual(ns.foo, 123) + self.assertEqual(ns.bar, 'quux') + ns2 = parser2.parse_args([]) + self.assertEqual(ns2.foo, 42) + self.assertEqual(ns2.bar, 'baz') + + # Test deep copy does not get new arguments + parser.add_argument("--extra") + with self.assertRaises(argparse.ArgumentError): + parser2.parse_args(["--extra", "bar"]) + + +class TestArgumentParserPickleable(unittest.TestCase): + + @force_not_colorized + def test_pickle_roundtrip(self): + import pickle + parser = argparse.ArgumentParser(exit_on_error=False) + parser.add_argument('--foo', type=int, default=42) + parser.add_argument('bar', nargs='?', default='baz') + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + # Try to pickle and unpickle the parser + parser2 = pickle.loads(pickle.dumps(parser, protocol=proto)) + # Check that the round-tripped parser still works + ns = parser2.parse_args(['--foo', '123', 'quux']) + self.assertEqual(ns.foo, 123) + self.assertEqual(ns.bar, 'quux') + ns2 = parser2.parse_args([]) + self.assertEqual(ns2.foo, 42) + self.assertEqual(ns2.bar, 'baz') + + class TestCase(unittest.TestCase): def setUp(self): @@ -580,13 +703,22 @@ class TestOptionalsShortLong(ParserTestCase): class TestOptionalsDest(ParserTestCase): """Tests various means of setting destination""" - argument_signatures = [Sig('--foo-bar'), Sig('--baz', dest='zabbaz')] + argument_signatures = [ + Sig('-x', '-foobar', '--foo-bar', '-barfoo', '-X'), + Sig('--baz', dest='zabbaz'), + Sig('-y', '-qux', '-Y'), + Sig('-z'), + ] failures = ['a'] successes = [ - ('--foo-bar f', NS(foo_bar='f', zabbaz=None)), - ('--baz g', NS(foo_bar=None, zabbaz='g')), - ('--foo-bar h --baz i', NS(foo_bar='h', zabbaz='i')), - ('--baz j --foo-bar k', NS(foo_bar='k', zabbaz='j')), + ('--foo-bar f', NS(foo_bar='f', zabbaz=None, qux=None, z=None)), + ('-x f', NS(foo_bar='f', zabbaz=None, qux=None, z=None)), + ('--baz g', NS(foo_bar=None, zabbaz='g', qux=None, z=None)), + ('--foo-bar h --baz i', NS(foo_bar='h', zabbaz='i', qux=None, z=None)), + ('--baz j --foo-bar k', NS(foo_bar='k', zabbaz='j', qux=None, z=None)), + ('-qux l', NS(foo_bar=None, zabbaz=None, qux='l', z=None)), + ('-y l', NS(foo_bar=None, zabbaz=None, qux='l', z=None)), + ('-z m', NS(foo_bar=None, zabbaz=None, qux=None, z='m')), ] @@ -795,6 +927,76 @@ def test_invalid_name(self): self.assertEqual(str(cm.exception), "invalid option name '--no-foo' for BooleanOptionalAction") +class TestBooleanOptionalActionSingleDash(ParserTestCase): + """Tests BooleanOptionalAction with single dash""" + + argument_signatures = [ + Sig('-foo', '-x', action=argparse.BooleanOptionalAction), + ] + failures = ['--foo', '--no-foo', '-no-foo', '-no-x', '-nox'] + successes = [ + ('', NS(foo=None)), + ('-foo', NS(foo=True)), + ('-nofoo', NS(foo=False)), + ('-x', NS(foo=True)), + ] + + def test_invalid_name(self): + parser = argparse.ArgumentParser() + with self.assertRaises(ValueError) as cm: + parser.add_argument('-nofoo', action=argparse.BooleanOptionalAction) + self.assertEqual(str(cm.exception), + "invalid option name '-nofoo' for BooleanOptionalAction") + +class TestBooleanOptionalActionAlternatePrefixChars(ParserTestCase): + """Tests BooleanOptionalAction with custom prefixes""" + + parser_signature = Sig(prefix_chars='+-', add_help=False) + argument_signatures = [Sig('++foo', action=argparse.BooleanOptionalAction)] + failures = ['--foo', '--no-foo'] + successes = [ + ('', NS(foo=None)), + ('++foo', NS(foo=True)), + ('++no-foo', NS(foo=False)), + ] + + def test_invalid_name(self): + parser = argparse.ArgumentParser(prefix_chars='+/') + with self.assertRaisesRegex(ValueError, + 'BooleanOptionalAction.*is not valid for positional arguments'): + parser.add_argument('--foo', action=argparse.BooleanOptionalAction) + with self.assertRaises(ValueError) as cm: + parser.add_argument('++no-foo', action=argparse.BooleanOptionalAction) + self.assertEqual(str(cm.exception), + "invalid option name '++no-foo' for BooleanOptionalAction") + +class TestBooleanOptionalActionSingleAlternatePrefixChar(ParserTestCase): + """Tests BooleanOptionalAction with single alternate prefix char""" + + parser_signature = Sig(prefix_chars='+/', add_help=False) + argument_signatures = [ + Sig('+foo', '+x', action=argparse.BooleanOptionalAction), + ] + failures = ['++foo', '++no-foo', '++nofoo', + '-no-foo', '-nofoo', '+no-foo', '-nofoo', + '+no-x', '+nox', '-no-x', '-nox'] + successes = [ + ('', NS(foo=None)), + ('+foo', NS(foo=True)), + ('+nofoo', NS(foo=False)), + ('+x', NS(foo=True)), + ] + + def test_invalid_name(self): + parser = argparse.ArgumentParser(prefix_chars='+/') + with self.assertRaisesRegex(ValueError, + 'BooleanOptionalAction.*is not valid for positional arguments'): + parser.add_argument('-foo', action=argparse.BooleanOptionalAction) + with self.assertRaises(ValueError) as cm: + parser.add_argument('+nofoo', action=argparse.BooleanOptionalAction) + self.assertEqual(str(cm.exception), + "invalid option name '+nofoo' for BooleanOptionalAction") + class TestBooleanOptionalActionRequired(ParserTestCase): """Tests BooleanOptionalAction required""" @@ -1023,7 +1225,7 @@ def test_invalid_enum_value_raises_error(self): parser.add_argument('--color', choices=self.Color) self.assertRaisesRegex( argparse.ArgumentError, - r"invalid choice: 'yellow' \(choose from red, green, blue\)", + r"invalid choice: 'yellow' \(choose from 'red', 'green', 'blue'\)", parser.parse_args, ['--color', 'yellow'], ) @@ -2282,16 +2484,17 @@ class TestNegativeNumber(ParserTestCase): ('--complex -1e-3j', NS(int=None, float=None, complex=-0.001j)), ] +@force_not_colorized_test_class class TestArgumentAndSubparserSuggestions(TestCase): """Test error handling and suggestion when a user makes a typo""" def test_wrong_argument_error_with_suggestions(self): - parser = ErrorRaisingArgumentParser(suggest_on_error=True) + parser = ErrorRaisingArgumentParser() parser.add_argument('foo', choices=['bar', 'baz']) with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('bazz',)) self.assertIn( - "error: argument foo: invalid choice: 'bazz', maybe you meant 'baz'? (choose from bar, baz)", + "error: argument foo: invalid choice: 'bazz', maybe you meant 'baz'? (choose from 'bar', 'baz')", excinfo.exception.stderr ) @@ -2301,12 +2504,12 @@ def test_wrong_argument_error_no_suggestions(self): with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('bazz',)) self.assertIn( - "error: argument foo: invalid choice: 'bazz' (choose from bar, baz)", + "error: argument foo: invalid choice: 'bazz' (choose from 'bar', 'baz')", excinfo.exception.stderr, ) def test_wrong_argument_subparsers_with_suggestions(self): - parser = ErrorRaisingArgumentParser(suggest_on_error=True) + parser = ErrorRaisingArgumentParser() subparsers = parser.add_subparsers(required=True) subparsers.add_parser('foo') subparsers.add_parser('bar') @@ -2314,7 +2517,7 @@ def test_wrong_argument_subparsers_with_suggestions(self): parser.parse_args(('baz',)) self.assertIn( "error: argument {foo,bar}: invalid choice: 'baz', maybe you meant" - " 'bar'? (choose from foo, bar)", + " 'bar'? (choose from 'foo', 'bar')", excinfo.exception.stderr, ) @@ -2326,22 +2529,23 @@ def test_wrong_argument_subparsers_no_suggestions(self): with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('baz',)) self.assertIn( - "error: argument {foo,bar}: invalid choice: 'baz' (choose from foo, bar)", + "error: argument {foo,bar}: invalid choice: 'baz' (choose from 'foo', 'bar')", excinfo.exception.stderr, ) - def test_wrong_argument_no_suggestion_implicit(self): - parser = ErrorRaisingArgumentParser() + def test_wrong_argument_with_suggestion_explicit(self): + parser = ErrorRaisingArgumentParser(suggest_on_error=True) parser.add_argument('foo', choices=['bar', 'baz']) with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('bazz',)) self.assertIn( - "error: argument foo: invalid choice: 'bazz' (choose from bar, baz)", + "error: argument foo: invalid choice: 'bazz', maybe you meant" + " 'baz'? (choose from 'bar', 'baz')", excinfo.exception.stderr, ) def test_suggestions_choices_empty(self): - parser = ErrorRaisingArgumentParser(suggest_on_error=True) + parser = ErrorRaisingArgumentParser() parser.add_argument('foo', choices=[]) with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('bazz',)) @@ -2351,22 +2555,22 @@ def test_suggestions_choices_empty(self): ) def test_suggestions_choices_int(self): - parser = ErrorRaisingArgumentParser(suggest_on_error=True) + parser = ErrorRaisingArgumentParser() parser.add_argument('foo', choices=[1, 2]) with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('3',)) self.assertIn( - "error: argument foo: invalid choice: '3' (choose from 1, 2)", + "error: argument foo: invalid choice: '3' (choose from '1', '2')", excinfo.exception.stderr, ) def test_suggestions_choices_mixed_types(self): - parser = ErrorRaisingArgumentParser(suggest_on_error=True) + parser = ErrorRaisingArgumentParser() parser.add_argument('foo', choices=[1, '2']) with self.assertRaises(ArgumentParserError) as excinfo: parser.parse_args(('3',)) self.assertIn( - "error: argument foo: invalid choice: '3' (choose from 1, 2)", + "error: argument foo: invalid choice: '3' (choose from '1', '2')", excinfo.exception.stderr, ) @@ -2688,6 +2892,16 @@ def test_optional_subparsers(self): ret = parser.parse_args(()) self.assertIsNone(ret.command) + def test_subparser_help_with_parent_required_optional(self): + parser = ErrorRaisingArgumentParser(prog='PROG') + parser.add_argument('--foo', required=True) + parser.add_argument('--bar') + subparsers = parser.add_subparsers() + parser_sub = subparsers.add_parser('sub') + parser_sub.add_argument('arg') + self.assertEqual(parser_sub.format_usage(), + 'usage: PROG --foo FOO sub [-h] arg\n') + def test_help(self): self.assertEqual(self.parser.format_usage(), 'usage: PROG [-h] [--foo] bar {1,2,3} ...\n') @@ -3306,12 +3520,11 @@ def test_help_subparser_all_mutually_exclusive_group_members_suppressed(self): ''' self.assertEqual(cmd_foo.format_help(), textwrap.dedent(expected)) - def test_empty_group(self): + def test_usage_empty_group(self): # See issue 26952 - parser = argparse.ArgumentParser() + parser = ErrorRaisingArgumentParser(prog='PROG') group = parser.add_mutually_exclusive_group() - with self.assertRaises(ValueError): - parser.parse_args(['-h']) + self.assertEqual(parser.format_usage(), 'usage: PROG [-h]\n') def test_nested_mutex_groups(self): parser = argparse.ArgumentParser(prog='PROG') @@ -3579,25 +3792,29 @@ def get_parser(self, required): group.add_argument('-b', action='store_true', help='b help') parser.add_argument('-y', action='store_true', help='y help') group.add_argument('-c', action='store_true', help='c help') + parser.add_argument('-z', action='store_true', help='z help') return parser failures = ['-a -b', '-b -c', '-a -c', '-a -b -c'] successes = [ - ('-a', NS(a=True, b=False, c=False, x=False, y=False)), - ('-b', NS(a=False, b=True, c=False, x=False, y=False)), - ('-c', NS(a=False, b=False, c=True, x=False, y=False)), - ('-a -x', NS(a=True, b=False, c=False, x=True, y=False)), - ('-y -b', NS(a=False, b=True, c=False, x=False, y=True)), - ('-x -y -c', NS(a=False, b=False, c=True, x=True, y=True)), + ('-a', NS(a=True, b=False, c=False, x=False, y=False, z=False)), + ('-b', NS(a=False, b=True, c=False, x=False, y=False, z=False)), + ('-c', NS(a=False, b=False, c=True, x=False, y=False, z=False)), + ('-a -x', NS(a=True, b=False, c=False, x=True, y=False, z=False)), + ('-y -b', NS(a=False, b=True, c=False, x=False, y=True, z=False)), + ('-x -y -c', NS(a=False, b=False, c=True, x=True, y=True, z=False)), ] successes_when_not_required = [ - ('', NS(a=False, b=False, c=False, x=False, y=False)), - ('-x', NS(a=False, b=False, c=False, x=True, y=False)), - ('-y', NS(a=False, b=False, c=False, x=False, y=True)), + ('', NS(a=False, b=False, c=False, x=False, y=False, z=False)), + ('-x', NS(a=False, b=False, c=False, x=True, y=False, z=False)), + ('-y', NS(a=False, b=False, c=False, x=False, y=True, z=False)), ] - usage_when_required = usage_when_not_required = '''\ - usage: PROG [-h] [-x] [-a] [-b] [-y] [-c] + usage_when_not_required = '''\ + usage: PROG [-h] [-x] [-a | -b | -c] [-y] [-z] + ''' + usage_when_required = '''\ + usage: PROG [-h] [-x] (-a | -b | -c) [-y] [-z] ''' help = '''\ @@ -3608,6 +3825,7 @@ def get_parser(self, required): -b b help -y y help -c c help + -z z help ''' @@ -3661,23 +3879,27 @@ def get_parser(self, required): group.add_argument('a', nargs='?', help='a help') group.add_argument('-b', action='store_true', help='b help') group.add_argument('-c', action='store_true', help='c help') + parser.add_argument('-z', action='store_true', help='z help') return parser failures = ['X A -b', '-b -c', '-c X A'] successes = [ - ('X A', NS(a='A', b=False, c=False, x='X', y=False)), - ('X -b', NS(a=None, b=True, c=False, x='X', y=False)), - ('X -c', NS(a=None, b=False, c=True, x='X', y=False)), - ('X A -y', NS(a='A', b=False, c=False, x='X', y=True)), - ('X -y -b', NS(a=None, b=True, c=False, x='X', y=True)), + ('X A', NS(a='A', b=False, c=False, x='X', y=False, z=False)), + ('X -b', NS(a=None, b=True, c=False, x='X', y=False, z=False)), + ('X -c', NS(a=None, b=False, c=True, x='X', y=False, z=False)), + ('X A -y', NS(a='A', b=False, c=False, x='X', y=True, z=False)), + ('X -y -b', NS(a=None, b=True, c=False, x='X', y=True, z=False)), ] successes_when_not_required = [ - ('X', NS(a=None, b=False, c=False, x='X', y=False)), - ('X -y', NS(a=None, b=False, c=False, x='X', y=True)), + ('X', NS(a=None, b=False, c=False, x='X', y=False, z=False)), + ('X -y', NS(a=None, b=False, c=False, x='X', y=True, z=False)), ] - usage_when_required = usage_when_not_required = '''\ - usage: PROG [-h] [-y] [-b] [-c] x [a] + usage_when_not_required = '''\ + usage: PROG [-h] [-y] [-z] x [-b | -c | a] + ''' + usage_when_required = '''\ + usage: PROG [-h] [-y] [-z] x (-b | -c | a) ''' help = '''\ @@ -3690,6 +3912,7 @@ def get_parser(self, required): -y y help -b b help -c c help + -z z help ''' @@ -4884,6 +5107,25 @@ def test_long_mutex_groups_wrap(self): ''') self.assertEqual(parser.format_usage(), usage) + def test_mutex_groups_with_mixed_optionals_positionals_wrap(self): + # https://github.com/python/cpython/issues/75949 + # Mutually exclusive groups containing both optionals and positionals + # should preserve pipe separators when the usage line wraps. + parser = argparse.ArgumentParser(prog='PROG') + g = parser.add_mutually_exclusive_group() + g.add_argument('-v', '--verbose', action='store_true') + g.add_argument('-q', '--quiet', action='store_true') + g.add_argument('-x', '--extra-long-option-name', nargs='?') + g.add_argument('-y', '--yet-another-long-option', nargs='?') + g.add_argument('positional', nargs='?') + + usage = textwrap.dedent('''\ + usage: PROG [-h] + [-v | -q | -x [EXTRA_LONG_OPTION_NAME] | + -y [YET_ANOTHER_LONG_OPTION] | positional] + ''') + self.assertEqual(parser.format_usage(), usage) + class TestHelpVariableExpansion(HelpTestCase): """Test that variables are expanded properly in help messages""" @@ -5299,6 +5541,7 @@ class TestHelpArgumentDefaults(HelpTestCase): argument_signatures = [ Sig('--foo', help='foo help - oh and by the way, %(default)s'), Sig('--bar', action='store_true', help='bar help'), + Sig('--required', required=True, help='some help'), Sig('--taz', action=argparse.BooleanOptionalAction, help='Whether to taz it', default=True), Sig('--corge', action=argparse.BooleanOptionalAction, @@ -5312,8 +5555,8 @@ class TestHelpArgumentDefaults(HelpTestCase): [Sig('--baz', type=int, default=42, help='baz help')]), ] usage = '''\ - usage: PROG [-h] [--foo FOO] [--bar] [--taz | --no-taz] [--corge | --no-corge] - [--quux QUUX] [--baz BAZ] + usage: PROG [-h] [--foo FOO] [--bar] --required REQUIRED [--taz | --no-taz] + [--corge | --no-corge] [--quux QUUX] [--baz BAZ] spam [badger] ''' help = usage + '''\ @@ -5328,6 +5571,7 @@ class TestHelpArgumentDefaults(HelpTestCase): -h, --help show this help message and exit --foo FOO foo help - oh and by the way, None --bar bar help (default: False) + --required REQUIRED some help --taz, --no-taz Whether to taz it (default: True) --corge, --no-corge Whether to corge it --quux QUUX Set the quux (default: 42) @@ -5574,6 +5818,11 @@ def custom_formatter(prog): a-very-long-command command that does something ''')) + def test_direct_formatter_instantiation(self): + formatter = argparse.HelpFormatter(prog="program") + formatter.add_usage(usage=None, actions=[], groups=[]) + help_text = formatter.format_help() + self.assertEqual(help_text, "usage: program\n") # ===================================== # Optional/Positional constructor tests @@ -5608,6 +5857,8 @@ def test_invalid_option_strings(self): self.assertTypeError('-', errmsg='dest= is required') self.assertTypeError('--', errmsg='dest= is required') self.assertTypeError('---', errmsg='dest= is required') + self.assertTypeError('-', '--', '---', + errmsg="dest= is required for options like '-', '--', '---'") def test_invalid_prefix(self): self.assertValueError('--foo', '+foo', @@ -5830,6 +6081,7 @@ def test_subparser_conflict(self): # Help and Version option tests # ============================= +@force_not_colorized_test_class class TestOptionalsHelpVersionActions(TestCase): """Test the help and version actions""" @@ -6144,6 +6396,7 @@ def spam(string_to_convert): # Check that deprecated arguments output warning # ============================================== +@force_not_colorized_test_class class TestDeprecatedArguments(TestCase): def test_deprecated_option(self): @@ -6454,6 +6707,20 @@ def test_remainder(self): args = parser.parse_args(['--foo', 'a', '--', 'b', '--', 'c']) self.assertEqual(NS(foo='a', bar=['--', 'b', '--', 'c']), args) + def test_optional_remainder(self): + parser = argparse.ArgumentParser(exit_on_error=False) + parser.add_argument('--foo', nargs='...') + parser.add_argument('bar', nargs='*') + + args = parser.parse_args(['--', '--foo', 'a', 'b']) + self.assertEqual(NS(foo=None, bar=['--foo', 'a', 'b']), args) + args = parser.parse_args(['--foo', '--', 'a', 'b']) + self.assertEqual(NS(foo=['--', 'a', 'b'], bar=[]), args) + args = parser.parse_args(['--foo', 'a', '--', 'b']) + self.assertEqual(NS(foo=['a', '--', 'b'], bar=[]), args) + args = parser.parse_args(['--foo', 'a', 'b', '--']) + self.assertEqual(NS(foo=['a', 'b', '--'], bar=[]), args) + def test_subparser(self): parser = argparse.ArgumentParser(exit_on_error=False) parser.add_argument('foo') @@ -7031,9 +7298,8 @@ def make_script(self, dirname, basename, *, compiled=False): script_name = script_helper.make_script(dirname, basename, self.source) if not compiled: return script_name - py_compile.compile(script_name, doraise=True) + pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True) os.remove(script_name) - pyc_file = import_helper.make_legacy_pyc(script_name) return pyc_file def make_zip_script(self, script_name, name_in_zip=None): @@ -7128,7 +7394,8 @@ class TestColorized(TestCase): def setUp(self): super().setUp() # Ensure color even if ran with NO_COLOR=1 - _colorize.can_colorize = lambda *args, **kwargs: True + self.enterContext(swap_attr(_colorize, 'can_colorize', + lambda *args, **kwargs: True)) self.theme = _colorize.get_theme(force_color=True).argparse def test_argparse_color(self): @@ -7176,6 +7443,13 @@ def test_argparse_color(self): choices=("Aaaaa", "Bbbbb", "Ccccc", "Ddddd"), help="pick one", ) + parser.add_argument( + "--optional8", + default="A", + metavar="X", + choices=("A", "B", "C"), + help="among %(choices)s, default is %(default)s", + ) parser.add_argument("+f") parser.add_argument("++bar") @@ -7201,6 +7475,8 @@ def test_argparse_color(self): short_b = self.theme.short_option label_b = self.theme.label pos_b = self.theme.action + default = self.theme.default + interp = self.theme.interpolated_value reset = self.theme.reset # Act @@ -7213,8 +7489,8 @@ def test_argparse_color(self): f"""\ {heading}usage: {reset}{prog}PROG{reset} [{short}-h{reset}] [{short}-v{reset} | {short}-q{reset}] [{short}-o{reset}] [{long}--optional2 {label}OPTIONAL2{reset}] [{long}--optional3 {label}{{X,Y,Z}}{reset}] [{long}--optional4 {label}{{X,Y,Z}}{reset}] [{long}--optional5 {label}{{X,Y,Z}}{reset}] [{long}--optional6 {label}{{X,Y,Z}}{reset}] - [{short}-p {label}{{Aaaaa,Bbbbb,Ccccc,Ddddd}}{reset}] [{short}+f {label}F{reset}] [{long}++bar {label}BAR{reset}] [{long}-+baz {label}BAZ{reset}] - [{short}-c {label}COUNT{reset}] + [{short}-p {label}{{Aaaaa,Bbbbb,Ccccc,Ddddd}}{reset}] [{long}--optional8 {label}X{reset}] [{short}+f {label}F{reset}] [{long}++bar {label}BAR{reset}] + [{long}-+baz {label}BAZ{reset}] [{short}-c {label}COUNT{reset}] {pos}x{reset} {pos}y{reset} {pos}this_indeed_is_a_very_long_action_name{reset} {pos}{{sub1,sub2}} ...{reset} Colorful help @@ -7227,17 +7503,18 @@ def test_argparse_color(self): {heading}options:{reset} {short_b}-h{reset}, {long_b}--help{reset} show this help message and exit - {short_b}-v{reset}, {long_b}--verbose{reset} more spam (default: False) - {short_b}-q{reset}, {long_b}--quiet{reset} less spam (default: False) + {short_b}-v{reset}, {long_b}--verbose{reset} more spam {default}(default: {reset}{interp}False{reset}{default}){reset} + {short_b}-q{reset}, {long_b}--quiet{reset} less spam {default}(default: {reset}{interp}False{reset}{default}){reset} {short_b}-o{reset}, {long_b}--optional1{reset} {long_b}--optional2{reset} {label_b}OPTIONAL2{reset} - pick one (default: None) + pick one {default}(default: {reset}{interp}None{reset}{default}){reset} {long_b}--optional3{reset} {label_b}{{X,Y,Z}}{reset} - {long_b}--optional4{reset} {label_b}{{X,Y,Z}}{reset} pick one (default: None) - {long_b}--optional5{reset} {label_b}{{X,Y,Z}}{reset} pick one (default: None) - {long_b}--optional6{reset} {label_b}{{X,Y,Z}}{reset} pick one (default: None) + {long_b}--optional4{reset} {label_b}{{X,Y,Z}}{reset} pick one {default}(default: {reset}{interp}None{reset}{default}){reset} + {long_b}--optional5{reset} {label_b}{{X,Y,Z}}{reset} pick one {default}(default: {reset}{interp}None{reset}{default}){reset} + {long_b}--optional6{reset} {label_b}{{X,Y,Z}}{reset} pick one {default}(default: {reset}{interp}None{reset}{default}){reset} {short_b}-p{reset}, {long_b}--optional7{reset} {label_b}{{Aaaaa,Bbbbb,Ccccc,Ddddd}}{reset} - pick one (default: None) + pick one {default}(default: {reset}{interp}None{reset}{default}){reset} + {long_b}--optional8{reset} {label_b}X{reset} among {interp}A, B, C{reset}, default is {interp}A{reset} {short_b}+f{reset} {label_b}F{reset} {long_b}++bar{reset} {label_b}BAR{reset} {long_b}-+baz{reset} {label_b}BAZ{reset} @@ -7253,7 +7530,28 @@ def test_argparse_color(self): ), ) - def test_argparse_color_usage(self): + def test_argparse_color_mutually_exclusive_group_usage(self): + parser = argparse.ArgumentParser(color=True, prog="PROG") + group = parser.add_mutually_exclusive_group() + group.add_argument('--foo', action='store_true', help='FOO') + group.add_argument('--spam', help='SPAM') + group.add_argument('badger', nargs='*', help='BADGER') + + prog = self.theme.prog + heading = self.theme.heading + long = self.theme.summary_long_option + short = self.theme.summary_short_option + label = self.theme.summary_label + pos = self.theme.summary_action + reset = self.theme.reset + + self.assertEqual(parser.format_usage(), + f"{heading}usage: {reset}{prog}PROG{reset} [{short}-h{reset}] " + f"[{long}--foo{reset} | " + f"{long}--spam {label}SPAM{reset} | " + f"{pos}badger ...{reset}]\n") + + def test_argparse_color_custom_usage(self): # Arrange parser = argparse.ArgumentParser( add_help=False, @@ -7311,11 +7609,11 @@ def custom_formatter(prog): {heading}usage: {reset}{prog}PROG{reset} [{short}-h{reset}] [{short}+f {label}FOO{reset}] {pos}spam{reset} {heading}positional arguments:{reset} - {pos_b}spam{reset} spam help + {pos_b}spam{reset} spam help {heading}options:{reset} - {short_b}-h{reset}, {long_b}--help{reset} show this help message and exit - {short_b}+f{reset}, {long_b}++foo{reset} {label_b}FOO{reset} foo help + {short_b}-h{reset}, {long_b}--help{reset} show this help message and exit + {short_b}+f{reset}, {long_b}++foo{reset} {label_b}FOO{reset} foo help ''')) def test_custom_formatter_class(self): @@ -7348,13 +7646,294 @@ def __init__(self, prog): {heading}usage: {reset}{prog}PROG{reset} [{short}-h{reset}] [{short}+f {label}FOO{reset}] {pos}spam{reset} {heading}positional arguments:{reset} - {pos_b}spam{reset} spam help + {pos_b}spam{reset} spam help {heading}options:{reset} - {short_b}-h{reset}, {long_b}--help{reset} show this help message and exit - {short_b}+f{reset}, {long_b}++foo{reset} {label_b}FOO{reset} foo help + {short_b}-h{reset}, {long_b}--help{reset} show this help message and exit + {short_b}+f{reset}, {long_b}++foo{reset} {label_b}FOO{reset} foo help ''')) + def test_subparser_prog_is_stored_without_color(self): + parser = argparse.ArgumentParser(prog='complex', color=True) + sub = parser.add_subparsers(dest='command') + demo_parser = sub.add_parser('demo') + + self.assertNotIn('\x1b[', demo_parser.prog) + + demo_parser.color = False + help_text = demo_parser.format_help() + self.assertNotIn('\x1b[', help_text) + + def test_error_and_warning_keywords_colorized(self): + parser = argparse.ArgumentParser(prog='PROG') + parser.add_argument('foo') + + with self.assertRaises(SystemExit): + with captured_stderr() as stderr: + parser.parse_args([]) + + err = stderr.getvalue() + error_color = self.theme.error + reset = self.theme.reset + self.assertIn(f'{error_color}error:{reset}', err) + + with captured_stderr() as stderr: + parser._warning('test warning') + + warn = stderr.getvalue() + warning_color = self.theme.warning + self.assertIn(f'{warning_color}warning:{reset}', warn) + + def test_error_and_warning_not_colorized_when_disabled(self): + parser = argparse.ArgumentParser(prog='PROG', color=False) + parser.add_argument('foo') + + with self.assertRaises(SystemExit): + with captured_stderr() as stderr: + parser.parse_args([]) + + err = stderr.getvalue() + self.assertNotIn('\x1b[', err) + self.assertIn('error:', err) + + with captured_stderr() as stderr: + parser._warning('test warning') + + warn = stderr.getvalue() + self.assertNotIn('\x1b[', warn) + self.assertIn('warning:', warn) + + def test_backtick_markup_in_epilog(self): + parser = argparse.ArgumentParser( + prog='PROG', + color=True, + epilog='Example: `python -m myapp --verbose`', + ) + + prog_extra = self.theme.prog_extra + reset = self.theme.reset + + help_text = parser.format_help() + self.assertIn(f'Example: {prog_extra}python -m myapp --verbose{reset}', + help_text) + self.assertNotIn('`', help_text) + + def test_backtick_markup_in_description(self): + parser = argparse.ArgumentParser( + prog='PROG', + color=True, + description='Run `python myapp` or ``python -m myapp`` to start.', + ) + + prog_extra = self.theme.prog_extra + reset = self.theme.reset + + help_text = parser.format_help() + self.assertIn( + f'Run {prog_extra}python myapp{reset} or ' + f'{prog_extra}python -m myapp{reset} to start.', + help_text, + ) + self.assertNotIn("`", help_text) + + def test_backtick_markup_multiple(self): + parser = argparse.ArgumentParser( + prog='PROG', + color=True, + epilog='Try `app run` or ``app test``.', + ) + + prog_extra = self.theme.prog_extra + reset = self.theme.reset + + help_text = parser.format_help() + self.assertIn(f'{prog_extra}app run{reset}', help_text) + self.assertIn(f'{prog_extra}app test{reset}', help_text) + self.assertNotIn('`', help_text) + + def test_backtick_markup_not_applied_when_color_disabled(self): + # When color is disabled, backticks are preserved as-is + parser = argparse.ArgumentParser( + prog='PROG', + color=False, + epilog='Examples: `python -m myapp` or ``python -m myapp --x``', + ) + + help_text = parser.format_help() + self.assertIn('`python -m myapp`', help_text) + self.assertIn('``python -m myapp --x``', help_text) + self.assertNotIn('\x1b[', help_text) + + def test_backtick_markup_with_format_string(self): + parser = argparse.ArgumentParser( + prog='myapp', + color=True, + epilog='Run `%(prog)s --help` for more info.', + ) + + prog_extra = self.theme.prog_extra + reset = self.theme.reset + + help_text = parser.format_help() + self.assertIn(f'{prog_extra}myapp --help{reset}', help_text) + + def test_backtick_markup_in_subparser(self): + parser = argparse.ArgumentParser(prog='PROG', color=True) + subparsers = parser.add_subparsers() + sub = subparsers.add_parser( + 'sub', + description='Run `PROG sub --foo` to start.', + ) + + prog_extra = self.theme.prog_extra + reset = self.theme.reset + + help_text = sub.format_help() + self.assertIn(f'{prog_extra}PROG sub --foo{reset}', help_text) + + def test_backtick_markup_special_regex_chars(self): + parser = argparse.ArgumentParser( + prog='PROG', + color=True, + epilog='`grep "foo.*bar" | sort`', + ) + + prog_extra = self.theme.prog_extra + reset = self.theme.reset + + help_text = parser.format_help() + self.assertIn(f'{prog_extra}grep "foo.*bar" | sort{reset}', help_text) + + def test_backtick_markup_in_argument_help(self): + parser = argparse.ArgumentParser(prog="PROG", color=True) + parser.add_argument("--foo", help="set the `foo` value") + parser.add_argument("--bar", help="set the ``bar`` value") + + prog_extra = self.theme.prog_extra + reset = self.theme.reset + + help_text = parser.format_help() + self.assertIn(f"set the {prog_extra}foo{reset} value", help_text) + self.assertIn(f"set the {prog_extra}bar{reset} value", help_text) + self.assertNotIn("`", help_text) + + def test_backtick_markup_in_argument_help_with_format(self): + parser = argparse.ArgumentParser(prog="PROG", color=True) + parser.add_argument( + "--foo", default="bar", help="set `foo` (default: %(default)s)" + ) + + prog_extra = self.theme.prog_extra + reset = self.theme.reset + + help_text = parser.format_help() + self.assertIn(f"set {prog_extra}foo{reset}", help_text) + + def test_backtick_markup_in_argument_help_color_disabled(self): + parser = argparse.ArgumentParser(prog="PROG", color=False) + parser.add_argument("--foo", help="set the `foo` value") + + help_text = parser.format_help() + self.assertIn("set the `foo` value", help_text) + self.assertNotIn("\x1b[", help_text) + + def test_help_with_format_specifiers(self): + # GH-142950: format specifiers like %x should work with color=True + parser = argparse.ArgumentParser(prog='PROG', color=True) + parser.add_argument('--hex', type=int, default=255, + help='hex: %(default)x, alt: %(default)#x') + parser.add_argument('--zero', type=int, default=7, + help='zero: %(default)05d') + parser.add_argument('--str', default='test', + help='str: %(default)s') + parser.add_argument('--pct', type=int, default=50, + help='pct: %(default)d%%') + parser.add_argument('--literal', help='literal: 100%%') + parser.add_argument('--prog', help='prog: %(prog)s') + parser.add_argument('--type', type=int, help='type: %(type)s') + parser.add_argument('--choices', choices=['a', 'b'], + help='choices: %(choices)s') + + help_text = parser.format_help() + + interp = self.theme.interpolated_value + reset = self.theme.reset + + self.assertIn(f'hex: {interp}ff{reset}', help_text) + self.assertIn(f'alt: {interp}0xff{reset}', help_text) + self.assertIn(f'zero: {interp}00007{reset}', help_text) + self.assertIn(f'str: {interp}test{reset}', help_text) + self.assertIn(f'pct: {interp}50{reset}%', help_text) + self.assertIn('literal: 100%', help_text) + self.assertIn(f'prog: {interp}PROG{reset}', help_text) + self.assertIn(f'type: {interp}int{reset}', help_text) + self.assertIn(f'choices: {interp}a, b{reset}', help_text) + + def test_print_help_uses_target_file_for_color_decision(self): + parser = argparse.ArgumentParser(prog='PROG', color=True) + parser.add_argument('--opt') + output = io.StringIO() + calls = [] + + def fake_can_colorize(*, file=None): + calls.append(file) + return file is None + + with swap_attr(_colorize, 'can_colorize', fake_can_colorize): + parser.print_help(file=output) + + self.assertIs(calls[-1], output) + self.assertIn(output, calls) + self.assertNotIn('\x1b[', output.getvalue()) + + def test_print_usage_uses_target_file_for_color_decision(self): + parser = argparse.ArgumentParser(prog='PROG', color=True) + parser.add_argument('--opt') + output = io.StringIO() + calls = [] + + def fake_can_colorize(*, file=None): + calls.append(file) + return file is None + + with swap_attr(_colorize, 'can_colorize', fake_can_colorize): + parser.print_usage(file=output) + + self.assertIs(calls[-1], output) + self.assertIn(output, calls) + self.assertNotIn('\x1b[', output.getvalue()) + + def test_fake_color_theme_matches_real(self): + from argparse import _colorless_theme + + # Check the attributes match those of the 'real' theme + _colorize_nocolor = _colorize.get_theme(force_no_color=True).argparse + for k in _colorize_nocolor: + self.assertEqual( + getattr(_colorless_theme, k), getattr(_colorize_nocolor, k) + ) + + def test_fake_color_theme_raises(self): + from argparse import _colorless_theme + + # Make sure the _colorless_theme doesn't return empty strings + # for magic methods or private attributes + with self.assertRaises(AttributeError): + _colorless_theme.__unknown_dunder__ + + with self.assertRaises(AttributeError): + _colorless_theme._private_attribute + + +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(argparse, "__version__") + self.assertEqual(cm.filename, __file__) + def tearDownModule(): # Remove global references to avoid looking like we have refleaks. diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 58ea89c4fac833..23d9f3c9667d86 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -8,20 +8,16 @@ from test.support import import_helper from test.support import os_helper from test.support import _2G +from test.support import subTests import weakref import pickle import operator import struct import sys -import warnings import array from array import _array_reconstructor as array_reconstructor -with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - sizeof_wchar = array.array('u').itemsize - class ArraySubclass(array.array): pass @@ -30,7 +26,8 @@ class ArraySubclassWithKwargs(array.array): def __init__(self, typecode, newarg=None): array.array.__init__(self) -typecodes = 'uwbBhHiIlLfdqQ' +typecodes = array.typecodes + class MiscTest(unittest.TestCase): @@ -41,8 +38,9 @@ def test_array_is_sequence(self): def test_bad_constructor(self): self.assertRaises(TypeError, array.array) self.assertRaises(TypeError, array.array, spam=42) - self.assertRaises(TypeError, array.array, 'xx') + self.assertRaises(ValueError, array.array, 'xx') self.assertRaises(ValueError, array.array, 'x') + self.assertRaises(ValueError, array.array, 'Z') @support.cpython_only def test_disallow_instantiation(self): @@ -67,6 +65,29 @@ def test_empty(self): a += a self.assertEqual(len(a), 0) + def test_fromlist_reentrant_index_mutation(self): + + class Evil: + def __init__(self, lst): + self.lst = lst + def __index__(self): + self.lst.clear() + return "not an int" + + for typecode in ('I', 'L', 'Q'): + with self.subTest(typecode=typecode): + lst = [] + lst.append(Evil(lst)) + a = array.array(typecode) + with self.assertRaises(TypeError): + a.fromlist(lst) + + def test_typecodes(self): + self.assertIsInstance(array.typecodes, tuple) + for typecode in array.typecodes: + self.assertIsInstance(typecode, str) + self.assertGreaterEqual(len(typecode), 1) + # Machine format codes. # @@ -95,17 +116,17 @@ def test_empty(self): UTF16_BE = 19 UTF32_LE = 20 UTF32_BE = 21 +IEEE_754_FLOAT_COMPLEX_LE = 22 +IEEE_754_FLOAT_COMPLEX_BE = 23 +IEEE_754_DOUBLE_COMPLEX_LE = 24 +IEEE_754_DOUBLE_COMPLEX_BE = 25 +IEEE_754_FLOAT16_LE = 26 +IEEE_754_FLOAT16_BE = 27 +MACHINE_FORMAT_CODE_MAX = 27 -class ArrayReconstructorTest(unittest.TestCase): - def setUp(self): - self.enterContext(warnings.catch_warnings()) - warnings.filterwarnings( - "ignore", - message="The 'u' type code is deprecated and " - "will be removed in Python 3.16", - category=DeprecationWarning) +class ArrayReconstructorTest(unittest.TestCase): def test_error(self): self.assertRaises(TypeError, array_reconstructor, @@ -121,7 +142,7 @@ def test_error(self): self.assertRaises(ValueError, array_reconstructor, array.array, "b", UNKNOWN_FORMAT, b"") self.assertRaises(ValueError, array_reconstructor, - array.array, "b", 22, b"") + array.array, "b", MACHINE_FORMAT_CODE_MAX + 1, b"") self.assertRaises(ValueError, array_reconstructor, array.array, "d", 16, b"a") @@ -173,7 +194,15 @@ def test_numbers(self): (['d'], IEEE_754_DOUBLE_LE, 'dddd', - [9006104071832581.0, float('inf'), float('-inf'), -0.0]) + [9006104071832581.0, float('inf'), float('-inf'), -0.0]), + (['Zf'], IEEE_754_FLOAT_COMPLEX_LE, 'ZfZfZfZf', + [16711938.0j, float('inf'), complex('1-infj'), -0.0]), + (['Zd'], IEEE_754_DOUBLE_COMPLEX_LE, 'ZdZdZdZd', + [9006104071832581.0j, float('inf'), complex('1-infj'), -0.0]), ) for testcase in testcases: valid_typecodes, mformat_code, struct_fmt, values = testcase @@ -198,12 +227,11 @@ def test_unicode(self): ) for testcase in testcases: mformat_code, encoding = testcase - for c in 'uw': - a = array.array(c, teststr) - b = array_reconstructor( - array.array, c, mformat_code, teststr.encode(encoding)) - self.assertEqual(a, b, - msg="{0!r} != {1!r}; testcase={2!r}".format(a, b, testcase)) + a = array.array('w', teststr) + b = array_reconstructor( + array.array, 'w', mformat_code, teststr.encode(encoding)) + self.assertEqual(a, b, + msg="{0!r} != {1!r}; testcase={2!r}".format(a, b, testcase)) class BaseTest: @@ -215,14 +243,6 @@ class BaseTest: # outside: An entry that is not in example # minitemsize: the minimum guaranteed itemsize - def setUp(self): - self.enterContext(warnings.catch_warnings()) - warnings.filterwarnings( - "ignore", - message="The 'u' type code is deprecated and " - "will be removed in Python 3.16", - category=DeprecationWarning) - def assertEntryEqual(self, entry1, entry2): self.assertEqual(entry1, entry2) @@ -255,13 +275,13 @@ def test_buffer_info(self): self.assertEqual(bi[1], len(a)) def test_byteswap(self): - if self.typecode in ('u', 'w'): + if self.typecode == 'w': example = '\U00100100' else: example = self.example a = array.array(self.typecode, example) self.assertRaises(TypeError, a.byteswap, 42) - if a.itemsize in (1, 2, 4, 8): + if a.itemsize in (1, 2, 4, 8, 16): b = array.array(self.typecode, example) b.byteswap() if a.itemsize==1: @@ -1123,7 +1143,7 @@ def test_buffer(self): self.assertEqual(m.tobytes(), expected) self.assertRaises(BufferError, a.frombytes, a.tobytes()) self.assertEqual(m.tobytes(), expected) - if self.typecode in ('u', 'w'): + if self.typecode == 'w': self.assertRaises(BufferError, a.fromunicode, a.tounicode()) self.assertEqual(m.tobytes(), expected) self.assertRaises(BufferError, operator.imul, a, 2) @@ -1179,7 +1199,7 @@ def test_sizeof_without_buffer(self): support.check_sizeof(self, a, basesize) def test_initialize_with_unicode(self): - if self.typecode not in ('u', 'w'): + if self.typecode != 'w': with self.assertRaises(TypeError) as cm: a = array.array(self.typecode, 'foo') self.assertIn("cannot use a str", str(cm.exception)) @@ -1188,7 +1208,6 @@ def test_initialize_with_unicode(self): self.assertIn("cannot use a unicode array", str(cm.exception)) else: a = array.array(self.typecode, "foo") - a = array.array(self.typecode, array.array('u', 'foo')) a = array.array(self.typecode, array.array('w', 'foo')) @support.cpython_only @@ -1203,6 +1222,9 @@ def test_free_after_iterating(self): support.check_free_after_iterating(self, reversed, array.array, (self.typecode,)) + def test_known_typecode(self): + self.assertIn(self.typecode, array.typecodes) + class StringTest(BaseTest): def test_setitem(self): @@ -1211,12 +1233,12 @@ def test_setitem(self): self.assertRaises(TypeError, a.__setitem__, 0, self.example[:2]) class UnicodeTest(StringTest, unittest.TestCase): - typecode = 'u' + typecode = 'w' example = '\x01\u263a\x00\ufeff' smallerexample = '\x01\u263a\x00\ufefe' biggerexample = '\x01\u263a\x01\ufeff' outside = str('\x33') - minitemsize = sizeof_wchar + minitemsize = 4 def test_unicode(self): self.assertRaises(TypeError, array.array, 'b', 'foo') @@ -1238,28 +1260,6 @@ def test_unicode(self): self.assertRaises(TypeError, a.fromunicode) - def test_issue17223(self): - if self.typecode == 'u' and sizeof_wchar == 2: - # PyUnicode_FromUnicode() cannot fail with 16-bit wchar_t - self.skipTest("specific to 32-bit wchar_t") - - # this used to crash - # U+FFFFFFFF is an invalid code point in Unicode 6.0 - invalid_str = b'\xff\xff\xff\xff' - - a = array.array(self.typecode, invalid_str) - self.assertRaises(ValueError, a.tounicode) - self.assertRaises(ValueError, str, a) - - def test_typecode_u_deprecation(self): - with self.assertWarns(DeprecationWarning): - array.array("u") - - -class UCS4Test(UnicodeTest): - typecode = 'w' - minitemsize = 4 - class NumberTest(BaseTest): @@ -1499,6 +1499,62 @@ def test_byteswap(self): b.byteswap() self.assertEqual(a, b) +class CFPTest(NumberTest): + example = [-42j, 0, 42+1j, 1e5j, -1e10] + outside = 23 + + def assertEntryEqual(self, entry1, entry2): + self.assertAlmostEqual(entry1, entry2) + + def test_cmp(self): + a = array.array(self.typecode, self.example) + self.assertIs(a == 42, False) + self.assertIs(a != 42, True) + + self.assertIs(a == a, True) + self.assertIs(a != a, False) + self.assertIs(a < a, False) + self.assertIs(a <= a, True) + self.assertIs(a > a, False) + self.assertIs(a >= a, True) + + self.assertIs(a == 2*a, False) + self.assertIs(a != 2*a, True) + self.assertIs(a < 2*a, True) + self.assertIs(a <= 2*a, True) + self.assertIs(a > 2*a, False) + self.assertIs(a >= 2*a, False) + + def test_nan(self): + a = array.array(self.typecode, [float('nan')]) + b = array.array(self.typecode, [float('nan')]) + self.assertIs(a != b, True) + self.assertIs(a == b, False) + + def test_byteswap(self): + a = array.array(self.typecode, self.example) + self.assertRaises(TypeError, a.byteswap, 42) + if a.itemsize in (1, 2, 4, 8, 16): + b = array.array(self.typecode, self.example) + b.byteswap() + if a.itemsize == 1: + self.assertEqual(a, b) + else: + # On alphas treating the byte swapped bit patterns as + # floats/doubles results in floating-point exceptions + # => compare the 8bit string values instead + self.assertNotEqual(a.tobytes(), b.tobytes()) + b.byteswap() + self.assertEqual(a, b) + + +class HalfFloatTest(FPTest, unittest.TestCase): + example = [-42.0, 0, 42, 1e2, -1e4] + smallerexample = [-42.0, 0, 42, 1e2, -2e4] + biggerexample = [-42.0, 0, 42, 1e2, 1e4] + typecode = 'e' + minitemsize = 2 + class FloatTest(FPTest, unittest.TestCase): typecode = 'f' minitemsize = 4 @@ -1525,6 +1581,15 @@ def test_alloc_overflow(self): self.fail("Array of size > maxsize created - MemoryError expected") +class ComplexFloatTest(CFPTest, unittest.TestCase): + typecode = 'Zf' + minitemsize = 8 + +class ComplexDoubleTest(CFPTest, unittest.TestCase): + typecode = 'Zd' + minitemsize = 16 + + class LargeArrayTest(unittest.TestCase): typecode = 'b' @@ -1672,6 +1737,45 @@ def test_gh_128961(self): it.__setstate__(0) self.assertRaises(StopIteration, next, it) + # Tests for NULL pointer dereference in array.__setitem__ + # when the index conversion mutates the array. + # See: https://github.com/python/cpython/issues/142555. + + @subTests("dtype", ["b", "B", "h", "H", "i", "l", "q", "I", "L", "Q"]) + def test_setitem_use_after_clear_with_int_data(self, dtype): + victim = array.array(dtype, list(range(64))) + + class Index: + def __index__(self): + victim.clear() + return 0 + + self.assertRaises(IndexError, victim.__setitem__, 1, Index()) + self.assertEqual(len(victim), 0) + + def test_setitem_use_after_shrink_with_int_data(self): + victim = array.array('b', [1, 2, 3]) + + class Index: + def __index__(self): + victim.pop() + victim.pop() + return 0 + + self.assertRaises(IndexError, victim.__setitem__, 1, Index()) + + @subTests("dtype", ["f", "d"]) + def test_setitem_use_after_clear_with_float_data(self, dtype): + victim = array.array(dtype, [1.0, 2.0, 3.0]) + + class Float: + def __float__(self): + victim.clear() + return 0.0 + + self.assertRaises(IndexError, victim.__setitem__, 1, Float()) + self.assertEqual(len(victim), 0) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ast/data/ast_repr.txt b/Lib/test/test_ast/data/ast_repr.txt index 1c1985519cd8b4..cc6accd766b78a 100644 --- a/Lib/test/test_ast/data/ast_repr.txt +++ b/Lib/test/test_ast/data/ast_repr.txt @@ -69,10 +69,14 @@ Module(body=[Try(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='ex Module(body=[TryStar(body=[Pass()], handlers=[ExceptHandler(type=Name(...), name='exc', body=[Pass(...)])], orelse=[Pass()], finalbody=[Pass()])], type_ignores=[]) Module(body=[Assert(test=Name(id='v', ctx=Load(...)), msg=None)], type_ignores=[]) Module(body=[Assert(test=Name(id='v', ctx=Load(...)), msg=Constant(value='message', kind=None))], type_ignores=[]) -Module(body=[Import(names=[alias(name='sys', asname=None)])], type_ignores=[]) -Module(body=[Import(names=[alias(name='foo', asname='bar')])], type_ignores=[]) -Module(body=[ImportFrom(module='sys', names=[alias(name='x', asname='y')], level=0)], type_ignores=[]) -Module(body=[ImportFrom(module='sys', names=[alias(name='v', asname=None)], level=0)], type_ignores=[]) +Module(body=[Import(names=[alias(name='sys', asname=None)], is_lazy=0)], type_ignores=[]) +Module(body=[Import(names=[alias(name='foo', asname='bar')], is_lazy=0)], type_ignores=[]) +Module(body=[ImportFrom(module='sys', names=[alias(name='x', asname='y')], level=0, is_lazy=0)], type_ignores=[]) +Module(body=[ImportFrom(module='sys', names=[alias(name='v', asname=None)], level=0, is_lazy=0)], type_ignores=[]) +Module(body=[Import(names=[alias(name='sys', asname=None)], is_lazy=1)], type_ignores=[]) +Module(body=[Import(names=[alias(name='foo', asname='bar')], is_lazy=1)], type_ignores=[]) +Module(body=[ImportFrom(module='sys', names=[alias(name='x', asname='y')], level=0, is_lazy=1)], type_ignores=[]) +Module(body=[ImportFrom(module='sys', names=[alias(name='v', asname=None)], level=0, is_lazy=1)], type_ignores=[]) Module(body=[Global(names=['v'])], type_ignores=[]) Module(body=[Expr(value=Constant(value=1, kind=None))], type_ignores=[]) Module(body=[Pass()], type_ignores=[]) diff --git a/Lib/test/test_ast/snippets.py b/Lib/test/test_ast/snippets.py index b76f98901d2ad8..a565ed10f8b434 100644 --- a/Lib/test/test_ast/snippets.py +++ b/Lib/test/test_ast/snippets.py @@ -118,6 +118,12 @@ # ImportFrom "from sys import x as y", "from sys import v", + # Lazy Import + "lazy import sys", + "lazy import foo as bar", + # Lazy ImportFrom + "lazy from sys import x as y", + "lazy from sys import v", # Global "global v", # Expr @@ -460,10 +466,14 @@ def main(): ('Module', [('TryStar', (1, 0, 7, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 8, 3, 17), 'Exception', ('Load',)), 'exc', [('Pass', (4, 2, 4, 6))])], [('Pass', (5, 7, 5, 11))], [('Pass', (7, 2, 7, 6))])], []), ('Module', [('Assert', (1, 0, 1, 8), ('Name', (1, 7, 1, 8), 'v', ('Load',)), None)], []), ('Module', [('Assert', (1, 0, 1, 19), ('Name', (1, 7, 1, 8), 'v', ('Load',)), ('Constant', (1, 10, 1, 19), 'message', None))], []), -('Module', [('Import', (1, 0, 1, 10), [('alias', (1, 7, 1, 10), 'sys', None)])], []), -('Module', [('Import', (1, 0, 1, 17), [('alias', (1, 7, 1, 17), 'foo', 'bar')])], []), -('Module', [('ImportFrom', (1, 0, 1, 22), 'sys', [('alias', (1, 16, 1, 22), 'x', 'y')], 0)], []), -('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', (1, 16, 1, 17), 'v', None)], 0)], []), +('Module', [('Import', (1, 0, 1, 10), [('alias', (1, 7, 1, 10), 'sys', None)], 0)], []), +('Module', [('Import', (1, 0, 1, 17), [('alias', (1, 7, 1, 17), 'foo', 'bar')], 0)], []), +('Module', [('ImportFrom', (1, 0, 1, 22), 'sys', [('alias', (1, 16, 1, 22), 'x', 'y')], 0, 0)], []), +('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', (1, 16, 1, 17), 'v', None)], 0, 0)], []), +('Module', [('Import', (1, 0, 1, 15), [('alias', (1, 12, 1, 15), 'sys', None)], 1)], []), +('Module', [('Import', (1, 0, 1, 22), [('alias', (1, 12, 1, 22), 'foo', 'bar')], 1)], []), +('Module', [('ImportFrom', (1, 0, 1, 27), 'sys', [('alias', (1, 21, 1, 27), 'x', 'y')], 0, 1)], []), +('Module', [('ImportFrom', (1, 0, 1, 22), 'sys', [('alias', (1, 21, 1, 22), 'v', None)], 0, 1)], []), ('Module', [('Global', (1, 0, 1, 8), ['v'])], []), ('Module', [('Expr', (1, 0, 1, 1), ('Constant', (1, 0, 1, 1), 1, None))], []), ('Module', [('Pass', (1, 0, 1, 4))], []), diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py index 13dcb5238945b6..fd3b33bab7f833 100644 --- a/Lib/test/test_ast/test_ast.py +++ b/Lib/test/test_ast/test_ast.py @@ -1,4 +1,5 @@ import _ast_unparse +import _ast import ast import builtins import contextlib @@ -13,6 +14,7 @@ import textwrap import types import unittest +import warnings import weakref from io import StringIO from pathlib import Path @@ -24,7 +26,7 @@ from test import support from test.support import os_helper -from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow +from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow, skip_if_unlimited_stack_size from test.support.ast_helper import ASTTestMixin from test.support.import_helper import ensure_lazy_imports from test.test_ast.utils import to_tuple @@ -84,7 +86,9 @@ def _assertTrueorder(self, ast_node, parent_pos): self.assertEqual(ast_node._fields, ast_node.__match_args__) def test_AST_objects(self): - x = ast.AST() + # Directly instantiating abstract node class AST is allowed (but deprecated) + with self.assertWarns(DeprecationWarning): + x = ast.AST() self.assertEqual(x._fields, ()) x.foobar = 42 self.assertEqual(x.foobar, 42) @@ -93,7 +97,7 @@ def test_AST_objects(self): with self.assertRaises(AttributeError): x.vararg - with self.assertRaises(TypeError): + with self.assertRaises(TypeError), self.assertWarns(DeprecationWarning): # "ast.AST constructor takes 0 positional arguments" ast.AST(2) @@ -109,15 +113,21 @@ def cleanup(): msg = "type object 'ast.AST' has no attribute '_fields'" # Both examples used to crash: - with self.assertRaisesRegex(AttributeError, msg): + with ( + self.assertRaisesRegex(AttributeError, msg), + self.assertWarns(DeprecationWarning), + ): ast.AST(arg1=123) - with self.assertRaisesRegex(AttributeError, msg): + with ( + self.assertRaisesRegex(AttributeError, msg), + self.assertWarns(DeprecationWarning), + ): ast.AST() - def test_AST_garbage_collection(self): + def test_node_garbage_collection(self): class X: pass - a = ast.AST() + a = ast.Module() a.x = X() a.x.a = a ref = weakref.ref(a.x) @@ -220,6 +230,131 @@ def test_negative_locations_for_compile(self): # This also must not crash: ast.parse(tree, optimize=2) + def test_docstring_optimization_single_node(self): + # https://github.com/python/cpython/issues/137308 + class_example1 = textwrap.dedent(''' + class A: + """Docstring""" + ''') + class_example2 = textwrap.dedent(''' + class A: + """ + Docstring""" + ''') + def_example1 = textwrap.dedent(''' + def some(): + """Docstring""" + ''') + def_example2 = textwrap.dedent(''' + def some(): + """Docstring + """ + ''') + async_def_example1 = textwrap.dedent(''' + async def some(): + """Docstring""" + ''') + async_def_example2 = textwrap.dedent(''' + async def some(): + """ + Docstring + """ + ''') + for code in [ + class_example1, + class_example2, + def_example1, + def_example2, + async_def_example1, + async_def_example2, + ]: + for opt_level in [0, 1, 2]: + with self.subTest(code=code, opt_level=opt_level): + mod = ast.parse(code, optimize=opt_level) + self.assertEqual(len(mod.body[0].body), 1) + if opt_level == 2: + pass_stmt = mod.body[0].body[0] + self.assertIsInstance(pass_stmt, ast.Pass) + self.assertEqual( + vars(pass_stmt), + { + 'lineno': 3, + 'col_offset': 4, + 'end_lineno': 3, + 'end_col_offset': 8, + }, + ) + else: + self.assertIsInstance(mod.body[0].body[0], ast.Expr) + self.assertIsInstance( + mod.body[0].body[0].value, + ast.Constant, + ) + + compile(code, "a", "exec") + compile(code, "a", "exec", optimize=opt_level) + compile(mod, "a", "exec") + compile(mod, "a", "exec", optimize=opt_level) + + def test_docstring_optimization_multiple_nodes(self): + # https://github.com/python/cpython/issues/137308 + class_example = textwrap.dedent( + """ + class A: + ''' + Docstring + ''' + x = 1 + """ + ) + + def_example = textwrap.dedent( + """ + def some(): + ''' + Docstring + + ''' + x = 1 + """ + ) + + async_def_example = textwrap.dedent( + """ + async def some(): + + '''Docstring + + ''' + x = 1 + """ + ) + + for code in [ + class_example, + def_example, + async_def_example, + ]: + for opt_level in [0, 1, 2]: + with self.subTest(code=code, opt_level=opt_level): + mod = ast.parse(code, optimize=opt_level) + if opt_level == 2: + self.assertNotIsInstance( + mod.body[0].body[0], + (ast.Pass, ast.Expr), + ) + else: + self.assertIsInstance(mod.body[0].body[0], ast.Expr) + self.assertIsInstance( + mod.body[0].body[0].value, + ast.Constant, + ) + + compile(code, "a", "exec") + compile(code, "a", "exec", optimize=opt_level) + compile(mod, "a", "exec") + compile(mod, "a", "exec", optimize=opt_level) + def test_slice(self): slc = ast.parse("x[::]").body[0].value.slice self.assertIsNone(slc.upper) @@ -292,6 +427,17 @@ def test_field_attr_existence(self): if isinstance(x, ast.AST): self.assertIs(type(x._fields), tuple) + def test_dynamic_attr(self): + for name, item in ast.__dict__.items(): + # constructor has a different signature + if name == 'Index': + continue + if self._is_ast_node(name, item): + x = self._construct_ast_class(item) + # Custom attribute assignment is allowed + x.foo = 5 + self.assertEqual(x.foo, 5) + def _construct_ast_class(self, cls): kwargs = {} for name, typ in cls.__annotations__.items(): @@ -302,7 +448,15 @@ def _construct_ast_class(self, cls): elif typ is object: kwargs[name] = b'capybara' elif isinstance(typ, type) and issubclass(typ, ast.AST): - kwargs[name] = self._construct_ast_class(typ) + if _ast._is_abstract(typ): + # Use an arbitrary concrete subclass + concrete = next((sub for sub in typ.__subclasses__() + if not _ast._is_abstract(sub)), None) + msg = f"abstract node class {typ} has no concrete subclasses" + self.assertIsNotNone(concrete, msg) + else: + concrete = typ + kwargs[name] = self._construct_ast_class(concrete) return cls(**kwargs) def test_arguments(self): @@ -333,14 +487,9 @@ def test_field_attr_writable(self): self.assertEqual(x._fields, 666) def test_classattrs(self): - with self.assertWarns(DeprecationWarning): - x = ast.Constant() + x = ast.Constant(42) self.assertEqual(x._fields, ('value', 'kind')) - with self.assertRaises(AttributeError): - x.value - - x = ast.Constant(42) self.assertEqual(x.value, 42) with self.assertRaises(AttributeError): @@ -360,11 +509,8 @@ def test_classattrs(self): self.assertRaises(TypeError, ast.Constant, 1, None, 2) self.assertRaises(TypeError, ast.Constant, 1, None, 2, lineno=0) - # Arbitrary keyword arguments are supported (but deprecated) - with self.assertWarns(DeprecationWarning): - self.assertEqual(ast.Constant(1, foo='bar').foo, 'bar') - - with self.assertRaisesRegex(TypeError, "Constant got multiple values for argument 'value'"): + msg = "ast.Constant got multiple values for argument 'value'" + with self.assertRaisesRegex(TypeError, re.escape(msg)): ast.Constant(1, value=2) self.assertEqual(ast.Constant(42).value, 42) @@ -377,6 +523,12 @@ def test_classattrs(self): self.assertIs(ast.Constant(None).value, None) self.assertIs(ast.Constant(...).value, ...) + with self.assertWarns(DeprecationWarning): + ast.Tuple().dims + + with self.assertWarns(DeprecationWarning): + ast.Tuple().dims = 3 + def test_constant_subclasses(self): class N(ast.Constant): def __init__(self, *args, **kwargs): @@ -403,23 +555,24 @@ def test_module(self): self.assertEqual(x.body, body) def test_nodeclasses(self): - # Zero arguments constructor explicitly allowed (but deprecated) - with self.assertWarns(DeprecationWarning): - x = ast.BinOp() - self.assertEqual(x._fields, ('left', 'op', 'right')) - - # Random attribute allowed too - x.foobarbaz = 5 - self.assertEqual(x.foobarbaz, 5) + # Zero arguments constructor is not allowed + msg = "ast.BinOp.__init__ missing 3 required positional arguments: 'left', 'op', and 'right'" + self.assertRaisesRegex(TypeError, re.escape(msg), ast.BinOp) n1 = ast.Constant(1) n3 = ast.Constant(3) addop = ast.Add() x = ast.BinOp(n1, addop, n3) + self.assertEqual(x._fields, ('left', 'op', 'right')) self.assertEqual(x.left, n1) self.assertEqual(x.op, addop) self.assertEqual(x.right, n3) + # Arbitrary attributes are allowed + x.foobarbaz = 5 + self.assertEqual(x.foobarbaz, 5) + self.assertEqual(x._fields, ('left', 'op', 'right')) + x = ast.BinOp(1, 2, 3) self.assertEqual(x.left, 1) self.assertEqual(x.op, 2) @@ -443,10 +596,14 @@ def test_nodeclasses(self): self.assertEqual(x.right, 3) self.assertEqual(x.lineno, 0) - # Random kwargs also allowed (but deprecated) - with self.assertWarns(DeprecationWarning): - x = ast.BinOp(1, 2, 3, foobarbaz=42) - self.assertEqual(x.foobarbaz, 42) + # Arbitrary keyword arguments are not allowed + msg = "ast.BinOp.__init__ got an unexpected keyword argument 'foobarbaz'" + with self.assertRaisesRegex(TypeError, re.escape(msg)): + ast.BinOp(1, 2, 3, foobarbaz=42) + + # Directly instantiating abstract node types is allowed (but deprecated) + self.assertWarns(DeprecationWarning, ast.stmt) + self.assertWarns(DeprecationWarning, ast.expr_context) def test_no_fields(self): # this used to fail because Sub._fields was None @@ -455,7 +612,10 @@ def test_no_fields(self): def test_invalid_sum(self): pos = dict(lineno=2, col_offset=3) - m = ast.Module([ast.Expr(ast.expr(**pos), **pos)], []) + with self.assertWarns(DeprecationWarning): + # Creating instances of ast.expr is deprecated + e = ast.expr(**pos) + m = ast.Module([ast.Expr(e, **pos)], []) with self.assertRaises(TypeError) as cm: compile(m, "", "exec") self.assertIn("but got expr()", str(cm.exception)) @@ -465,7 +625,7 @@ def test_invalid_identifier(self): ast.fix_missing_locations(m) with self.assertRaises(TypeError) as cm: compile(m, "", "exec") - self.assertIn("identifier must be of type str", str(cm.exception)) + self.assertIn("expecting a string object", str(cm.exception)) def test_invalid_constant(self): for invalid_constant in int, (1, 2, int), frozenset((1, 2, int)): @@ -863,10 +1023,12 @@ def next(self): enum._test_simple_enum(_Precedence, _ast_unparse._Precedence) @support.cpython_only + @skip_if_unlimited_stack_size @skip_wasi_stack_overflow() @skip_emscripten_stack_overflow() def test_ast_recursion_limit(self): - crash_depth = 500_000 + # Android test devices have less memory. + crash_depth = 100_000 if sys.platform == "android" else 500_000 success_depth = 200 if _testinternalcapi is not None: remaining = _testinternalcapi.get_c_recursion_remaining() @@ -919,6 +1081,30 @@ def test_none_checks(self) -> None: for node, attr, source in tests: self.assert_none_check(node, attr, source) + def test_required_field_messages(self): + binop = ast.BinOp( + left=ast.Constant(value=2), + right=ast.Constant(value=2), + op=ast.Add(), + ) + expr_without_position = ast.Expression(body=binop) + expr_with_wrong_body = ast.Expression(body=[binop]) + + with self.assertRaisesRegex(TypeError, "required field") as cm: + compile(expr_without_position, "", "eval") + with self.assertRaisesRegex( + TypeError, + "field 'body' was expecting node of type 'expr', got 'list'", + ): + compile(expr_with_wrong_body, "", "eval") + + constant = ast.parse("u'test'", mode="eval") + constant.body.kind = 0xFF + with self.assertRaisesRegex( + TypeError, "field 'kind' was expecting a string or bytes object" + ): + compile(constant, "", "eval") + def test_repr(self) -> None: snapshots = AST_REPR_DATA_FILE.read_text().split("\n") for test, snapshot in zip(ast_repr_get_test_cases(), snapshots, strict=True): @@ -932,61 +1118,6 @@ def test_repr_large_input_crash(self): r"Exceeds the limit \(\d+ digits\)"): repr(ast.Constant(value=eval(source))) - def test_pep_765_warnings(self): - srcs = [ - textwrap.dedent(""" - def f(): - try: - pass - finally: - return 42 - """), - textwrap.dedent(""" - for x in y: - try: - pass - finally: - break - """), - textwrap.dedent(""" - for x in y: - try: - pass - finally: - continue - """), - ] - for src in srcs: - with self.assertWarnsRegex(SyntaxWarning, 'finally'): - ast.parse(src) - - def test_pep_765_no_warnings(self): - srcs = [ - textwrap.dedent(""" - try: - pass - finally: - def f(): - return 42 - """), - textwrap.dedent(""" - try: - pass - finally: - for x in y: - break - """), - textwrap.dedent(""" - try: - pass - finally: - for x in y: - continue - """), - ] - for src in srcs: - ast.parse(src) - def test_tstring(self): # Test AST structure for simple t-string tree = ast.parse('t"Hello"') @@ -999,6 +1130,61 @@ def test_tstring(self): self.assertIsInstance(tree.body[0].value.values[0], ast.Constant) self.assertIsInstance(tree.body[0].value.values[1], ast.Interpolation) + def test_deprecated(self): + with self.assertWarns(DeprecationWarning): + ast.slice + + with self.assertWarns(DeprecationWarning): + ast.Index + + with self.assertWarns(DeprecationWarning): + ast.ExtSlice + + with self.assertWarns(DeprecationWarning): + ast.Suite + + with self.assertWarns(DeprecationWarning): + ast.AugLoad + + with self.assertWarns(DeprecationWarning): + ast.AugStore + + with self.assertWarns(DeprecationWarning): + ast.Param + + namespace = {} + exec("from ast import *", namespace) + self.assertNotIn("slice", namespace) + self.assertNotIn("Index", namespace) + self.assertNotIn("ExtSlice", namespace) + self.assertNotIn("Suite", namespace) + self.assertNotIn("AugLoad", namespace) + self.assertNotIn("AugStore", namespace) + self.assertNotIn("Param", namespace) + + def test_filter_syntax_warnings_by_module(self): + filename = support.findfile('test_import/data/syntax_warnings.py') + with open(filename, 'rb') as f: + source = f.read() + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=r'\z') + ast.parse(source) + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10]) + for wm in wlog: + self.assertEqual(wm.filename, '') + self.assertIs(wm.category, SyntaxWarning) + + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=r'package\.module\z') + warnings.filterwarnings('error', module=r'') + ast.parse(source, filename, module='package.module') + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10]) + for wm in wlog: + self.assertEqual(wm.filename, filename) + self.assertIs(wm.category, SyntaxWarning) + class CopyTests(unittest.TestCase): """Test copying and pickling AST nodes.""" @@ -1007,13 +1193,19 @@ class CopyTests(unittest.TestCase): def iter_ast_classes(): """Iterate over the (native) subclasses of ast.AST recursively. - This excludes the special class ast.Index since its constructor - returns an integer. + This excludes: + * abstract AST nodes + * the special class ast.Index, since its constructor returns + an integer. """ def do(cls): if cls.__module__ != 'ast': return - if cls is ast.Index: + with warnings.catch_warnings(action="ignore", category=DeprecationWarning): + if cls is ast.Index: + return + # Don't attempt to create instances of abstract AST nodes + if _ast._is_abstract(cls): return yield cls @@ -1032,6 +1224,7 @@ def test_pickling(self): ast2 = pickle.loads(pickle.dumps(tree, protocol)) self.assertEqual(to_tuple(ast2), to_tuple(tree)) + @skip_if_unlimited_stack_size def test_copy_with_parents(self): # gh-120108 code = """ @@ -1280,14 +1473,14 @@ def test_replace_ignore_known_custom_instance_fields(self): self.assertRaises(AttributeError, getattr, repl, 'extra') def test_replace_reject_missing_field(self): - # case: warn if deleted field is not replaced + # case: raise if deleted field is not replaced node = ast.parse('x').body[0].value context = node.ctx del node.id self.assertRaises(AttributeError, getattr, node, 'id') self.assertIs(node.ctx, context) - msg = "Name.__replace__ missing 1 keyword argument: 'id'." + msg = "ast.Name.__init__ missing 1 required positional argument: 'id'" with self.assertRaisesRegex(TypeError, re.escape(msg)): copy.replace(node) # assert that there is no side-effect @@ -1324,7 +1517,7 @@ def test_replace_reject_known_custom_instance_fields_commits(self): # explicit rejection of known instance fields self.assertHasAttr(node, 'extra') - msg = "Name.__replace__ got an unexpected keyword argument 'extra'." + msg = "ast.Name.__init__ got an unexpected keyword argument 'extra'" with self.assertRaisesRegex(TypeError, re.escape(msg)): copy.replace(node, extra=1) # assert that there is no side-effect @@ -1338,7 +1531,7 @@ def test_replace_reject_unknown_instance_fields(self): # explicit rejection of unknown extra fields self.assertRaises(AttributeError, getattr, node, 'unknown') - msg = "Name.__replace__ got an unexpected keyword argument 'unknown'." + msg = "ast.Name.__init__ got an unexpected keyword argument 'unknown'" with self.assertRaisesRegex(TypeError, re.escape(msg)): copy.replace(node, unknown=1) # assert that there is no side-effect @@ -1346,6 +1539,13 @@ def test_replace_reject_unknown_instance_fields(self): self.assertIs(node.ctx, context) self.assertRaises(AttributeError, getattr, node, 'unknown') + def test_replace_non_str_kwarg(self): + node = ast.Name(id="x") + errmsg = "got an unexpected keyword argument ", "eval") + @skip_if_unlimited_stack_size @skip_emscripten_stack_overflow() def test_recursion_indirect(self): e = ast.UnaryOp(op=ast.Not(), lineno=0, col_offset=0, operand=ast.Constant(1)) @@ -2536,11 +2748,12 @@ def test_get_docstring(self): def get_load_const(self, tree): # Compile to bytecode, disassemble and get parameter of LOAD_CONST - # instructions + # and LOAD_COMMON_CONSTANT instructions co = compile(tree, '', 'exec') consts = [] for instr in dis.get_instructions(co): - if instr.opcode in dis.hasconst: + if instr.opcode in dis.hasconst or \ + instr.opname == 'LOAD_COMMON_CONSTANT': consts.append(instr.argval) return consts @@ -2963,8 +3176,8 @@ def test_source_segment_missing_info(self): class NodeTransformerTests(ASTTestMixin, unittest.TestCase): def assertASTTransformation(self, transformer_class, - initial_code, expected_code): - initial_ast = ast.parse(dedent(initial_code)) + code, expected_code): + initial_ast = ast.parse(dedent(code)) expected_ast = ast.parse(dedent(expected_code)) transformer = transformer_class() @@ -3084,11 +3297,10 @@ def test_FunctionDef(self): args = ast.arguments() self.assertEqual(args.args, []) self.assertEqual(args.posonlyargs, []) - with self.assertWarnsRegex(DeprecationWarning, - r"FunctionDef\.__init__ missing 1 required positional argument: 'name'"): - node = ast.FunctionDef(args=args) - self.assertNotHasAttr(node, "name") - self.assertEqual(node.decorator_list, []) + msg = "ast.FunctionDef.__init__ missing 1 required positional argument: 'name'" + with self.assertRaisesRegex(TypeError, re.escape(msg)): + ast.FunctionDef(args=args) + node = ast.FunctionDef(name='foo', args=args) self.assertEqual(node.name, 'foo') self.assertEqual(node.decorator_list, []) @@ -3106,9 +3318,8 @@ def test_expr_context(self): self.assertEqual(name3.id, "x") self.assertIsInstance(name3.ctx, ast.Del) - with self.assertWarnsRegex(DeprecationWarning, - r"Name\.__init__ missing 1 required positional argument: 'id'"): - name3 = ast.Name() + msg = "ast.Name.__init__ missing 1 required positional argument: 'id'" + self.assertRaisesRegex(TypeError, re.escape(msg), ast.Name) def test_custom_subclass_with_no_fields(self): class NoInit(ast.AST): @@ -3147,20 +3358,18 @@ class MyAttrs(ast.AST): self.assertEqual(obj.a, 1) self.assertEqual(obj.b, 2) - with self.assertWarnsRegex(DeprecationWarning, - r"MyAttrs.__init__ got an unexpected keyword argument 'c'."): - obj = MyAttrs(c=3) + msg = "MyAttrs.__init__ got an unexpected keyword argument 'c'" + with self.assertRaisesRegex(TypeError, re.escape(msg)): + MyAttrs(c=3) def test_fields_and_types_no_default(self): class FieldsAndTypesNoDefault(ast.AST): _fields = ('a',) _field_types = {'a': int} - with self.assertWarnsRegex(DeprecationWarning, - r"FieldsAndTypesNoDefault\.__init__ missing 1 required positional argument: 'a'\."): - obj = FieldsAndTypesNoDefault() - with self.assertRaises(AttributeError): - obj.a + msg = "FieldsAndTypesNoDefault.__init__ missing 1 required positional argument: 'a'" + self.assertRaisesRegex(TypeError, re.escape(msg), FieldsAndTypesNoDefault) + obj = FieldsAndTypesNoDefault(a=1) self.assertEqual(obj.a, 1) @@ -3171,18 +3380,21 @@ class MoreFieldsThanTypes(ast.AST): a: int | None = None b: int | None = None - with self.assertWarnsRegex( - DeprecationWarning, - r"Field 'b' is missing from MoreFieldsThanTypes\._field_types" - ): - obj = MoreFieldsThanTypes() - self.assertIs(obj.a, None) - self.assertIs(obj.b, None) + msg = r"Field 'b' is missing from .*\.MoreFieldsThanTypes\._field_types" + self.assertRaisesRegex(TypeError, msg, MoreFieldsThanTypes) obj = MoreFieldsThanTypes(a=1, b=2) self.assertEqual(obj.a, 1) self.assertEqual(obj.b, 2) + def test_malformed_fields_with_bytes(self): + class BadFields(ast.AST): + _fields = (b'\xff'*64,) + _field_types = {'a': int} + + # This should not crash + self.assertRaisesRegex(TypeError, r"Field b'\\xff\\xff.*' .*", BadFields) + def test_complete_field_types(self): class _AllFieldTypes(ast.AST): _fields = ('a', 'b') @@ -3196,6 +3408,27 @@ class _AllFieldTypes(ast.AST): self.assertIs(obj.a, None) self.assertEqual(obj.b, []) + def test_non_str_kwarg(self): + warn_msg = "got an unexpected keyword argument ]+\.gen\(\)') + @unittest.skipIf( not hasattr(asyncio.futures, "_c_future_add_to_awaited_by"), diff --git a/Lib/test/test_asyncio/test_pep492.py b/Lib/test/test_asyncio/test_pep492.py index a0c8434c9457d2..95a9f3a9a7cf71 100644 --- a/Lib/test/test_asyncio/test_pep492.py +++ b/Lib/test/test_asyncio/test_pep492.py @@ -124,11 +124,6 @@ def foo(): yield self.assertFalse(asyncio.iscoroutine(foo())) - def test_iscoroutinefunction(self): - async def foo(): pass - with self.assertWarns(DeprecationWarning): - self.assertTrue(asyncio.iscoroutinefunction(foo)) - def test_async_def_coroutines(self): async def bar(): return 'spam' diff --git a/Lib/test/test_asyncio/test_proactor_events.py b/Lib/test/test_asyncio/test_proactor_events.py index b25daaface0807..edfad5e11db35e 100644 --- a/Lib/test/test_asyncio/test_proactor_events.py +++ b/Lib/test/test_asyncio/test_proactor_events.py @@ -566,6 +566,8 @@ def test_sendto(self): self.assertTrue(self.proactor.sendto.called) self.proactor.sendto.assert_called_with( self.sock, data, addr=('0.0.0.0', 1234)) + self.assertFalse(transport._buffer) + self.assertEqual(0, transport._buffer_size) def test_sendto_bytearray(self): data = bytearray(b'data') diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index 7b6d1bce5e460f..4bb5d4fb816a9a 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -854,6 +854,22 @@ def test_writelines_pauses_protocol(self): self.assertTrue(self.sock.send.called) self.assertTrue(self.loop.writers) + def test_writelines_after_connection_lost(self): + # GH-136234 + transport = self.socket_transport() + self.sock.send = mock.Mock() + self.sock.send.side_effect = ConnectionResetError + transport.write(b'data1') # Will fail immediately, causing connection lost + + transport.writelines([b'data2']) + self.assertFalse(transport._buffer) + self.assertFalse(self.loop.writers) + + test_utils.run_briefly(self.loop) # Allow _call_connection_lost to run + transport.writelines([b'data2']) + self.assertFalse(transport._buffer) + self.assertFalse(self.loop.writers) + @unittest.skipUnless(selector_events._HAS_SENDMSG, 'no sendmsg') def test_write_sendmsg_full(self): data = memoryview(b'data') @@ -1497,6 +1513,47 @@ def test_sendto_closing(self): transport.sendto(b'data', (1,)) self.assertEqual(transport._conn_lost, 2) + def test_sendto_sendto_ready(self): + data = b'data' + + # First queue up the buffer by having the socket blocked + self.sock.sendto.side_effect = BlockingIOError + transport = self.datagram_transport() + transport.sendto(data, ('0.0.0.0', 12345)) + self.loop.assert_writer(7, transport._sendto_ready) + self.assertEqual(1, len(transport._buffer)) + self.assertEqual(transport._buffer_size, len(data) + transport._header_size) + + # Now let the socket send the buffer + self.sock.sendto.side_effect = None + transport._sendto_ready() + self.assertTrue(self.sock.sendto.called) + self.assertEqual( + self.sock.sendto.call_args[0], (data, ('0.0.0.0', 12345))) + self.assertFalse(self.loop.writers) + self.assertFalse(transport._buffer) + self.assertEqual(transport._buffer_size, 0) + + def test_sendto_sendto_ready_blocked(self): + data = b'data' + + # First queue up the buffer by having the socket blocked + self.sock.sendto.side_effect = BlockingIOError + transport = self.datagram_transport() + transport.sendto(data, ('0.0.0.0', 12345)) + self.loop.assert_writer(7, transport._sendto_ready) + self.assertEqual(1, len(transport._buffer)) + self.assertEqual(transport._buffer_size, len(data) + transport._header_size) + + # Now try to send the buffer, it will be added to buffer again if it fails + transport._sendto_ready() + self.assertTrue(self.sock.sendto.called) + self.assertEqual( + self.sock.sendto.call_args[0], (data, ('0.0.0.0', 12345))) + self.assertTrue(self.loop.writers) + self.assertEqual(1, len(transport._buffer)) + self.assertEqual(transport._buffer_size, len(data) + transport._header_size) + def test_sendto_ready(self): data = b'data' self.sock.sendto.return_value = len(data) diff --git a/Lib/test/test_asyncio/test_sendfile.py b/Lib/test/test_asyncio/test_sendfile.py index dcd963b3355ef8..7afd7de3bb936e 100644 --- a/Lib/test/test_asyncio/test_sendfile.py +++ b/Lib/test/test_asyncio/test_sendfile.py @@ -228,6 +228,61 @@ def test_sock_sendfile_zero_size(self): self.assertEqual(ret, 0) self.assertEqual(self.file.tell(), 0) + def check_sock_sendfile_offset(self, data, offset, force_fallback=False): + sock, proto = self.prepare_socksendfile() + with tempfile.TemporaryFile() as f: + f.write(data) + f.flush() + self.assertEqual(f.tell(), len(data)) + + if force_fallback: + async def _sock_sendfile_fail(sock, file, offset, count): + raise asyncio.exceptions.SendfileNotAvailableError() + with support.swap_attr(self.loop, '_sock_sendfile_native', _sock_sendfile_fail): + ret = self.run_loop(self.loop.sock_sendfile(sock, f, offset, None)) + else: + ret = self.run_loop(self.loop.sock_sendfile(sock, f, offset, None)) + + self.assertEqual(f.tell(), len(data)) + + sock.close() + self.run_loop(proto.wait_closed()) + + self.assertEqual(ret, len(data) - offset) + + + def test_sock_sendfile_offset(self): + data = b'abcdef' + for offset in (0, len(data) // 2, len(data)): + for force_fallback in (False, True): + with self.subTest(offset=offset, force_fallback=force_fallback): + self.check_sock_sendfile_offset(data, offset, force_fallback) + + def check_sendfile_offset(self, offset, fallback): + srv_proto, cli_proto = self.prepare_sendfile() + self.file.seek(123) + coro = self.loop.sendfile(cli_proto.transport, self.file, offset, fallback=fallback) + try: + ret = self.run_loop(coro) + except asyncio.SendfileNotAvailableError: + if fallback: + raise + cli_proto.transport.close() + self.run_loop(srv_proto.done) + return + cli_proto.transport.close() + self.run_loop(srv_proto.done) + self.assertEqual(ret, len(self.DATA) - offset) + self.assertEqual(srv_proto.nbytes, len(self.DATA) - offset) + self.assertEqual(srv_proto.data, self.DATA[offset:]) + self.assertEqual(self.file.tell(), len(self.DATA)) + + def test_sendfile_offset(self): + for offset in (0, len(self.DATA) // 2, len(self.DATA)): + for fallback in (False, True): + with self.subTest(offset=offset, fallback=fallback): + self.check_sendfile_offset(offset, fallback) + def test_sock_sendfile_mix_with_regular_send(self): buf = b"mix_regular_send" * (4 * 1024) # 64 KiB sock, proto = self.prepare_socksendfile() diff --git a/Lib/test/test_asyncio/test_server.py b/Lib/test/test_asyncio/test_server.py index 5bd0f7e2af4f84..581ea47d2dec97 100644 --- a/Lib/test/test_asyncio/test_server.py +++ b/Lib/test/test_asyncio/test_server.py @@ -266,6 +266,38 @@ async def serve(rd, wr): await asyncio.sleep(0) self.assertTrue(task.done()) + async def test_close_with_hanging_client(self): + # Synchronize server cancellation only after the socket connection is + # accepted and this event is set + conn_event = asyncio.Event() + class Proto(asyncio.Protocol): + def connection_made(self, transport): + conn_event.set() + + loop = asyncio.get_running_loop() + srv = await loop.create_server(Proto, socket_helper.HOSTv4, 0) + + # Start the server + serve_forever_task = asyncio.create_task(srv.serve_forever()) + await asyncio.sleep(0) + + # Create a connection to server + addr = srv.sockets[0].getsockname() + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(addr) + self.addCleanup(sock.close) + + # Send a CancelledError into the server to emulate a Ctrl+C + # KeyboardInterrupt whilst the server is handling a hanging client + await conn_event.wait() + serve_forever_task.cancel() + + # Ensure the client is closed within a timeout + async with asyncio.timeout(0.5): + await srv.wait_closed() + + self.assertFalse(srv.is_serving()) + # Test the various corner cases of Unix server socket removal class UnixServerCleanupTests(unittest.IsolatedAsyncioTestCase): diff --git a/Lib/test/test_asyncio/test_server_context.py b/Lib/test/test_asyncio/test_server_context.py new file mode 100644 index 00000000000000..3f15654a1af467 --- /dev/null +++ b/Lib/test/test_asyncio/test_server_context.py @@ -0,0 +1,314 @@ +import asyncio +import contextvars +import unittest +import sys + +from unittest import TestCase + +try: + import ssl +except ImportError: + ssl = None + +from test.test_asyncio import utils as test_utils + +def tearDownModule(): + asyncio.events._set_event_loop_policy(None) + +class ServerContextvarsTestCase: + loop_factory = None # To be defined in subclasses + server_ssl_context = None # To be defined in subclasses for SSL tests + client_ssl_context = None # To be defined in subclasses for SSL tests + + def run_coro(self, coro): + return asyncio.run(coro, loop_factory=self.loop_factory) + + def test_start_server1(self): + # Test that asyncio.start_server captures the context at the time of server creation + async def test(): + var = contextvars.ContextVar("var", default="default") + + async def handle_client(reader, writer): + value = var.get() + writer.write(value.encode()) + await writer.drain() + writer.close() + + server = await asyncio.start_server(handle_client, '127.0.0.1', 0, + ssl=self.server_ssl_context) + # change the value + var.set("after_server") + + async def client(addr): + reader, writer = await asyncio.open_connection(*addr, + ssl=self.client_ssl_context) + data = await reader.read(100) + writer.close() + await writer.wait_closed() + return data.decode() + + async with server: + addr = server.sockets[0].getsockname() + self.assertEqual(await client(addr), "default") + + self.assertEqual(var.get(), "after_server") + + self.run_coro(test()) + + def test_start_server2(self): + # Test that mutations to the context in one handler don't affect other handlers or the server's context + async def test(): + var = contextvars.ContextVar("var", default="default") + + async def handle_client(reader, writer): + value = var.get() + writer.write(value.encode()) + var.set("in_handler") + await writer.drain() + writer.close() + + server = await asyncio.start_server(handle_client, '127.0.0.1', 0, + ssl=self.server_ssl_context) + var.set("after_server") + + async def client(addr): + reader, writer = await asyncio.open_connection(*addr, + ssl=self.client_ssl_context) + data = await reader.read(100) + writer.close() + await writer.wait_closed() + return data.decode() + + async with server: + addr = server.sockets[0].getsockname() + self.assertEqual(await client(addr), "default") + self.assertEqual(await client(addr), "default") + self.assertEqual(await client(addr), "default") + + self.assertEqual(var.get(), "after_server") + + self.run_coro(test()) + + def test_start_server3(self): + # Test that mutations to context in concurrent handlers don't affect each other or the server's context + async def test(): + var = contextvars.ContextVar("var", default="default") + var.set("before_server") + + async def handle_client(reader, writer): + writer.write(var.get().encode()) + await writer.drain() + writer.close() + + server = await asyncio.start_server(handle_client, '127.0.0.1', 0, + ssl=self.server_ssl_context) + var.set("after_server") + + async def client(addr): + reader, writer = await asyncio.open_connection(*addr, + ssl=self.client_ssl_context) + data = await reader.read(100) + self.assertEqual(data.decode(), "before_server") + writer.close() + await writer.wait_closed() + + async with server: + addr = server.sockets[0].getsockname() + async with asyncio.TaskGroup() as tg: + for _ in range(100): + tg.create_task(client(addr)) + + self.assertEqual(var.get(), "after_server") + + self.run_coro(test()) + + def test_create_server1(self): + # Test that loop.create_server captures the context at the time of server creation + # and that mutations to the context in protocol callbacks don't affect the server's context + async def test(): + var = contextvars.ContextVar("var", default="default") + + class EchoProtocol(asyncio.Protocol): + def connection_made(self, transport): + self.transport = transport + value = var.get() + var.set("in_handler") + self.transport.write(value.encode()) + self.transport.close() + + server = await asyncio.get_running_loop().create_server( + lambda: EchoProtocol(), '127.0.0.1', 0, + ssl=self.server_ssl_context) + var.set("after_server") + + async def client(addr): + reader, writer = await asyncio.open_connection(*addr, + ssl=self.client_ssl_context) + data = await reader.read(100) + self.assertEqual(data.decode(), "default") + writer.close() + await writer.wait_closed() + + async with server: + addr = server.sockets[0].getsockname() + await client(addr) + + self.assertEqual(var.get(), "after_server") + + self.run_coro(test()) + + def test_create_server2(self): + # Test that mutations to context in one protocol instance don't affect other instances or the server's context + async def test(): + var = contextvars.ContextVar("var", default="default") + + class EchoProtocol(asyncio.Protocol): + def __init__(self): + super().__init__() + assert var.get() == "default", var.get() + def connection_made(self, transport): + self.transport = transport + value = var.get() + var.set("in_handler") + self.transport.write(value.encode()) + self.transport.close() + + server = await asyncio.get_running_loop().create_server( + lambda: EchoProtocol(), '127.0.0.1', 0, + ssl=self.server_ssl_context) + + var.set("after_server") + + async def client(addr, expected): + reader, writer = await asyncio.open_connection(*addr, + ssl=self.client_ssl_context) + data = await reader.read(100) + self.assertEqual(data.decode(), expected) + writer.close() + await writer.wait_closed() + + async with server: + addr = server.sockets[0].getsockname() + await client(addr, "default") + await client(addr, "default") + + self.assertEqual(var.get(), "after_server") + + self.run_coro(test()) + + def test_gh140947(self): + # See https://github.com/python/cpython/issues/140947 + + cvar1 = contextvars.ContextVar("cvar1") + cvar2 = contextvars.ContextVar("cvar2") + cvar3 = contextvars.ContextVar("cvar3") + results = {} + is_ssl = self.server_ssl_context is not None + + def capture_context(meth): + result = [] + for k,v in contextvars.copy_context().items(): + if k.name.startswith("cvar"): + result.append((k.name, v)) + results[meth] = sorted(result) + + class DemoProtocol(asyncio.Protocol): + def __init__(self, on_conn_lost): + self.transport = None + self.on_conn_lost = on_conn_lost + self.tasks = set() + + def connection_made(self, transport): + capture_context("connection_made") + self.transport = transport + + def data_received(self, data): + capture_context("data_received") + + task = asyncio.create_task(self.asgi()) + self.tasks.add(task) + task.add_done_callback(self.tasks.discard) + + self.transport.pause_reading() + + def connection_lost(self, exc): + capture_context("connection_lost") + if not self.on_conn_lost.done(): + self.on_conn_lost.set_result(True) + + async def asgi(self): + capture_context("asgi start") + cvar1.set(True) + # make sure that we only resume after the pause + # otherwise the resume does nothing + if is_ssl: + while not self.transport._ssl_protocol._app_reading_paused: + await asyncio.sleep(0.01) + else: + while not self.transport._paused: + await asyncio.sleep(0.01) + cvar2.set(True) + self.transport.resume_reading() + cvar3.set(True) + capture_context("asgi end") + + async def main(): + loop = asyncio.get_running_loop() + on_conn_lost = loop.create_future() + + server = await loop.create_server( + lambda: DemoProtocol(on_conn_lost), '127.0.0.1', 0, + ssl=self.server_ssl_context) + async with server: + addr = server.sockets[0].getsockname() + reader, writer = await asyncio.open_connection(*addr, + ssl=self.client_ssl_context) + writer.write(b"anything") + await writer.drain() + writer.close() + await writer.wait_closed() + await on_conn_lost + + self.run_coro(main()) + self.assertDictEqual(results, { + "connection_made": [], + "data_received": [], + "asgi start": [], + "asgi end": [("cvar1", True), ("cvar2", True), ("cvar3", True)], + "connection_lost": [], + }) + + +class AsyncioEventLoopTests(TestCase, ServerContextvarsTestCase): + loop_factory = staticmethod(asyncio.new_event_loop) + +@unittest.skipUnless(ssl, "SSL not available") +class AsyncioEventLoopSSLTests(AsyncioEventLoopTests): + def setUp(self): + super().setUp() + self.server_ssl_context = test_utils.simple_server_sslcontext() + self.client_ssl_context = test_utils.simple_client_sslcontext() + +if sys.platform == "win32": + class AsyncioProactorEventLoopTests(TestCase, ServerContextvarsTestCase): + loop_factory = asyncio.ProactorEventLoop + + class AsyncioSelectorEventLoopTests(TestCase, ServerContextvarsTestCase): + loop_factory = asyncio.SelectorEventLoop + + @unittest.skipUnless(ssl, "SSL not available") + class AsyncioProactorEventLoopSSLTests(AsyncioProactorEventLoopTests): + def setUp(self): + super().setUp() + self.server_ssl_context = test_utils.simple_server_sslcontext() + self.client_ssl_context = test_utils.simple_client_sslcontext() + + @unittest.skipUnless(ssl, "SSL not available") + class AsyncioSelectorEventLoopSSLTests(AsyncioSelectorEventLoopTests): + def setUp(self): + super().setUp() + self.server_ssl_context = test_utils.simple_server_sslcontext() + self.client_ssl_context = test_utils.simple_client_sslcontext() + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_asyncio/test_sock_lowlevel.py b/Lib/test/test_asyncio/test_sock_lowlevel.py index df4ec7948975f6..f32dcd589e2de2 100644 --- a/Lib/test/test_asyncio/test_sock_lowlevel.py +++ b/Lib/test/test_asyncio/test_sock_lowlevel.py @@ -427,6 +427,27 @@ def test_recvfrom_into(self): self.loop.run_until_complete( self._basetest_datagram_recvfrom_into(server_address)) + async def _basetest_datagram_recvfrom_into_wrong_size(self, server_address): + # Call sock_sendto() with a size larger than the buffer + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: + sock.setblocking(False) + + buf = bytearray(5000) + data = b'\x01' * 4096 + wrong_size = len(buf) + 1 + await self.loop.sock_sendto(sock, data, server_address) + with self.assertRaises(ValueError): + await self.loop.sock_recvfrom_into( + sock, buf, wrong_size) + + size, addr = await self.loop.sock_recvfrom_into(sock, buf) + self.assertEqual(buf[:size], data) + + def test_recvfrom_into_wrong_size(self): + with test_utils.run_udp_echo_server() as server_address: + self.loop.run_until_complete( + self._basetest_datagram_recvfrom_into_wrong_size(server_address)) + async def _basetest_datagram_sendto_blocking(self, server_address): # Sad path, sock.sendto() raises BlockingIOError # This involves patching sock.sendto() to raise BlockingIOError but diff --git a/Lib/test/test_asyncio/test_ssl.py b/Lib/test/test_asyncio/test_ssl.py index 06118f3a61587b..ca15fc3bdd42dd 100644 --- a/Lib/test/test_asyncio/test_ssl.py +++ b/Lib/test/test_asyncio/test_ssl.py @@ -27,6 +27,7 @@ MACOS = (sys.platform == 'darwin') BUF_MULTIPLIER = 1024 if not MACOS else 64 +HANDSHAKE_TIMEOUT = support.LONG_TIMEOUT def tearDownModule(): @@ -257,15 +258,12 @@ def prog(sock): await fut async def start_server(): - extras = {} - extras = dict(ssl_handshake_timeout=support.SHORT_TIMEOUT) - srv = await asyncio.start_server( handle_client, '127.0.0.1', 0, family=socket.AF_INET, ssl=sslctx, - **extras) + ssl_handshake_timeout=HANDSHAKE_TIMEOUT) try: srv_socks = srv.sockets @@ -322,14 +320,11 @@ def server(sock): sock.close() async def client(addr): - extras = {} - extras = dict(ssl_handshake_timeout=support.SHORT_TIMEOUT) - reader, writer = await asyncio.open_connection( *addr, ssl=client_sslctx, server_hostname='', - **extras) + ssl_handshake_timeout=HANDSHAKE_TIMEOUT) writer.write(A_DATA) self.assertEqual(await reader.readexactly(2), b'OK') @@ -349,7 +344,8 @@ async def client_sock(addr): reader, writer = await asyncio.open_connection( sock=sock, ssl=client_sslctx, - server_hostname='') + server_hostname='', + ssl_handshake_timeout=HANDSHAKE_TIMEOUT) writer.write(A_DATA) self.assertEqual(await reader.readexactly(2), b'OK') @@ -448,7 +444,7 @@ async def client(addr): *addr, ssl=client_sslctx, server_hostname='', - ssl_handshake_timeout=support.SHORT_TIMEOUT) + ssl_handshake_timeout=HANDSHAKE_TIMEOUT) writer.close() await self.wait_closed(writer) @@ -610,7 +606,7 @@ def client(): extras = {} if server_ssl: - extras = dict(ssl_handshake_timeout=support.SHORT_TIMEOUT) + extras = dict(ssl_handshake_timeout=HANDSHAKE_TIMEOUT) f = loop.create_task( loop.connect_accepted_socket( @@ -659,7 +655,8 @@ async def client(addr): reader, writer = await asyncio.open_connection( *addr, ssl=client_sslctx, - server_hostname='') + server_hostname='', + ssl_handshake_timeout=HANDSHAKE_TIMEOUT) self.assertEqual(await reader.readline(), b'A\n') writer.write(b'B') @@ -1152,14 +1149,11 @@ def do(func, *args): await fut async def start_server(): - extras = {} - srv = await self.loop.create_server( server_protocol_factory, '127.0.0.1', 0, family=socket.AF_INET, - ssl=sslctx_1, - **extras) + ssl=sslctx_1) try: srv_socks = srv.sockets @@ -1209,14 +1203,11 @@ def server(sock): sock.close() async def client(addr): - extras = {} - extras = dict(ssl_handshake_timeout=support.SHORT_TIMEOUT) - reader, writer = await asyncio.open_connection( *addr, ssl=client_sslctx, server_hostname='', - **extras) + ssl_handshake_timeout=HANDSHAKE_TIMEOUT) writer.write(A_DATA) self.assertEqual(await reader.readexactly(2), b'OK') @@ -1286,7 +1277,8 @@ async def client(addr): reader, writer = await asyncio.open_connection( *addr, ssl=client_sslctx, - server_hostname='') + server_hostname='', + ssl_handshake_timeout=HANDSHAKE_TIMEOUT) sslprotocol = writer.transport._ssl_protocol writer.write(b'ping') data = await reader.readexactly(4) @@ -1398,7 +1390,8 @@ async def client(addr): reader, writer = await asyncio.open_connection( *addr, ssl=client_sslctx, - server_hostname='') + server_hostname='', + ssl_handshake_timeout=HANDSHAKE_TIMEOUT) writer.write(b'ping') data = await reader.readexactly(4) self.assertEqual(data, b'pong') @@ -1529,7 +1522,8 @@ async def client(addr): reader, writer = await asyncio.open_connection( *addr, ssl=client_sslctx, - server_hostname='') + server_hostname='', + ssl_handshake_timeout=HANDSHAKE_TIMEOUT) writer.write(b'ping') data = await reader.readexactly(4) self.assertEqual(data, b'pong') diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index f93ee54abc6469..cae8c7c6f7c94c 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -819,6 +819,48 @@ async def client(addr): self.assertEqual(msg1, b"hello world 1!\n") self.assertEqual(msg2, b"hello world 2!\n") + @unittest.skipIf(ssl is None, 'No ssl module') + def test_start_tls_buffered_data(self): + # gh-142352: test start_tls() with buffered data + + async def server_handler(client_reader, client_writer): + # Wait for TLS ClientHello to be buffered before start_tls(). + await client_reader._wait_for_data('test_start_tls_buffered_data'), + self.assertTrue(client_reader._buffer) + await client_writer.start_tls(test_utils.simple_server_sslcontext()) + + line = await client_reader.readline() + self.assertEqual(line, b"ping\n") + client_writer.write(b"pong\n") + await client_writer.drain() + client_writer.close() + await client_writer.wait_closed() + + async def client(addr): + reader, writer = await asyncio.open_connection(*addr) + await writer.start_tls(test_utils.simple_client_sslcontext()) + + writer.write(b"ping\n") + await writer.drain() + line = await reader.readline() + self.assertEqual(line, b"pong\n") + writer.close() + await writer.wait_closed() + + async def run_test(): + server = await asyncio.start_server( + server_handler, socket_helper.HOSTv4, 0) + server_addr = server.sockets[0].getsockname() + + await client(server_addr) + server.close() + await server.wait_closed() + + messages = [] + self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx)) + self.loop.run_until_complete(run_test()) + self.assertEqual(messages, []) + def test_streamreader_constructor_without_loop(self): with self.assertRaisesRegex(RuntimeError, 'no current event loop'): asyncio.StreamReader() diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 3a17c169c34f12..4ac6b23b7120fc 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -1,4 +1,5 @@ import os +import shlex import signal import sys import textwrap @@ -11,7 +12,7 @@ from asyncio import subprocess from test.test_asyncio import utils as test_utils from test import support -from test.support import os_helper +from test.support import os_helper, warnings_helper, gc_collect if not support.has_subprocess_support: raise unittest.SkipTest("test module requires subprocess") @@ -111,6 +112,37 @@ def test_subprocess_repr(self): ) transport.close() + def test_proc_exited_no_invalid_state_error_on_exit_waiters(self): + # gh-145541: when _connect_pipes hasn't completed (so + # _pipes_connected is False) and the process exits, _try_finish() + # sets the result on exit waiters. Then _call_connection_lost() must + # not call set_result() again on the same waiters. + self.loop.set_exception_handler( + lambda loop, context: self.fail( + f"unexpected exception: {context}") + ) + waiter = self.loop.create_future() + transport, protocol = self.create_transport(waiter) + + # Simulate a waiter registered via _wait() before the process exits. + exit_waiter = self.loop.create_future() + transport._exit_waiters.append(exit_waiter) + + # _connect_pipes hasn't completed, so _pipes_connected is False. + self.assertFalse(transport._pipes_connected) + + # Simulate process exit. _try_finish() will set the result on + # exit_waiter because _pipes_connected is False, and then schedule + # _call_connection_lost() because _pipes is empty (vacuously all + # disconnected). _call_connection_lost() must skip exit_waiter + # because it's already done. + transport._process_exited(6) + self.loop.run_until_complete(waiter) + + self.assertEqual(exit_waiter.result(), 6) + + transport.close() + class SubprocessMixin: @@ -739,9 +771,7 @@ async def check_stdout_output(self, coro, output): def test_create_subprocess_env_shell(self) -> None: async def main() -> None: - executable = sys.executable - if sys.platform == "win32": - executable = f'"{executable}"' + executable = f'"{sys.executable}"' if sys.platform == "win32" else shlex.quote(sys.executable) cmd = f'''{executable} -c "import os, sys; sys.stdout.write(os.getenv('FOO'))"''' env = os.environ.copy() env["FOO"] = "bar" @@ -879,6 +909,44 @@ async def main(): self.loop.run_until_complete(main()) + @warnings_helper.ignore_warnings(category=ResourceWarning) + def test_subprocess_read_pipe_cancelled(self): + async def main(): + loop = asyncio.get_running_loop() + loop.connect_read_pipe = mock.AsyncMock(side_effect=asyncio.CancelledError) + with self.assertRaises(asyncio.CancelledError): + await asyncio.create_subprocess_exec(*PROGRAM_BLOCKED, stderr=asyncio.subprocess.PIPE) + + asyncio.run(main()) + gc_collect() + + @warnings_helper.ignore_warnings(category=ResourceWarning) + def test_subprocess_write_pipe_cancelled(self): + async def main(): + loop = asyncio.get_running_loop() + loop.connect_write_pipe = mock.AsyncMock(side_effect=asyncio.CancelledError) + with self.assertRaises(asyncio.CancelledError): + await asyncio.create_subprocess_exec(*PROGRAM_BLOCKED, stdin=asyncio.subprocess.PIPE) + + asyncio.run(main()) + gc_collect() + + @warnings_helper.ignore_warnings(category=ResourceWarning) + def test_subprocess_read_write_pipe_cancelled(self): + async def main(): + loop = asyncio.get_running_loop() + loop.connect_read_pipe = mock.AsyncMock(side_effect=asyncio.CancelledError) + loop.connect_write_pipe = mock.AsyncMock(side_effect=asyncio.CancelledError) + with self.assertRaises(asyncio.CancelledError): + await asyncio.create_subprocess_exec( + *PROGRAM_BLOCKED, + stdin=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + + asyncio.run(main()) + gc_collect() if sys.platform != 'win32': # Unix diff --git a/Lib/test/test_asyncio/test_taskgroups.py b/Lib/test/test_asyncio/test_taskgroups.py index 91f6b03b4597a5..8925884b9dcf73 100644 --- a/Lib/test/test_asyncio/test_taskgroups.py +++ b/Lib/test/test_asyncio/test_taskgroups.py @@ -1102,6 +1102,131 @@ async def throw_error(): # cancellation happens here and error is more understandable await asyncio.sleep(0) + async def test_taskgroup_cancel_children(self): + # (asserting that TimeoutError is not raised) + async with asyncio.timeout(1): + async with asyncio.TaskGroup() as tg: + tg.create_task(asyncio.sleep(10)) + tg.create_task(asyncio.sleep(10)) + await asyncio.sleep(0) + tg.cancel() + + async def test_taskgroup_cancel_body(self): + count = 0 + async with asyncio.TaskGroup() as tg: + tg.cancel() + count += 1 + await asyncio.sleep(0) + count += 1 + self.assertEqual(count, 1) + + async def test_taskgroup_cancel_idempotent(self): + count = 0 + async with asyncio.TaskGroup() as tg: + tg.cancel() + tg.cancel() + count += 1 + await asyncio.sleep(0) + count += 1 + self.assertEqual(count, 1) + + async def test_taskgroup_cancel_after_exit(self): + async with asyncio.TaskGroup() as tg: + await asyncio.sleep(0) + # (asserting that exception is not raised) + tg.cancel() + + async def test_taskgroup_cancel_before_enter(self): + tg = asyncio.TaskGroup() + tg.cancel() + count = 0 + async with tg: + count += 1 + await asyncio.sleep(0) + count += 1 + self.assertEqual(count, 1) + + async def test_taskgroup_cancel_before_create_task(self): + async with asyncio.TaskGroup() as tg: + tg.cancel() + # TODO: This behavior is not ideal. We'd rather have no exception + # raised, and the child task run until the first await. + with self.assertRaises(RuntimeError): + tg.create_task(asyncio.sleep(1)) + + async def test_taskgroup_cancel_before_exception(self): + async def raise_exc(parent_tg: asyncio.TaskGroup): + parent_tg.cancel() + raise RuntimeError + + with self.assertRaises(ExceptionGroup): + async with asyncio.TaskGroup() as tg: + tg.create_task(raise_exc(tg)) + await asyncio.sleep(1) + + async def test_taskgroup_cancel_after_exception(self): + async def raise_exc(parent_tg: asyncio.TaskGroup): + try: + raise RuntimeError + finally: + parent_tg.cancel() + + with self.assertRaises(ExceptionGroup): + async with asyncio.TaskGroup() as tg: + tg.create_task(raise_exc(tg)) + await asyncio.sleep(1) + + async def test_taskgroup_body_cancel_before_exception(self): + with self.assertRaises(ExceptionGroup): + async with asyncio.TaskGroup() as tg: + tg.cancel() + raise RuntimeError + + async def test_taskgroup_body_cancel_after_exception(self): + with self.assertRaises(ExceptionGroup): + async with asyncio.TaskGroup() as tg: + try: + raise RuntimeError + finally: + tg.cancel() + + async def test_taskgroup_cancel_one_winner(self): + async def race(*fns): + outcome = None + async def run(fn): + nonlocal outcome + outcome = await fn() + tg.cancel() + + async with asyncio.TaskGroup() as tg: + for fn in fns: + tg.create_task(run(fn)) + return outcome + + event = asyncio.Event() + record = [] + async def fn_1(): + record.append("1 started") + await event.wait() + record.append("1 finished") + return 1 + + async def fn_2(): + record.append("2 started") + await event.wait() + record.append("2 finished") + return 2 + + async def fn_3(): + record.append("3 started") + event.set() + await asyncio.sleep(10) + record.append("3 finished") + return 3 + + self.assertEqual(await race(fn_1, fn_2, fn_3), 1) + self.assertListEqual(record, ["1 started", "2 started", "3 started", "1 finished"]) + class TestTaskGroup(BaseTestTaskGroup, unittest.IsolatedAsyncioTestCase): loop_factory = asyncio.EventLoop diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 931a43816a257a..56b1494c8363ca 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -11,6 +11,7 @@ import traceback import types import unittest +import inspect from unittest import mock from types import GenericAlias @@ -20,7 +21,6 @@ from test.test_asyncio import utils as test_utils from test import support from test.support.script_helper import assert_python_ok -from test.support.warnings_helper import ignore_warnings def tearDownModule(): @@ -1940,30 +1940,11 @@ async def notmutch(): self.assertFalse(task.cancelled()) self.assertIs(task.exception(), base_exc) - @ignore_warnings(category=DeprecationWarning) - def test_iscoroutinefunction(self): - def fn(): - pass - - self.assertFalse(asyncio.iscoroutinefunction(fn)) - - def fn1(): - yield - self.assertFalse(asyncio.iscoroutinefunction(fn1)) - - async def fn2(): - pass - self.assertTrue(asyncio.iscoroutinefunction(fn2)) - - self.assertFalse(asyncio.iscoroutinefunction(mock.Mock())) - self.assertTrue(asyncio.iscoroutinefunction(mock.AsyncMock())) - - @ignore_warnings(category=DeprecationWarning) def test_coroutine_non_gen_function(self): async def func(): return 'test' - self.assertTrue(asyncio.iscoroutinefunction(func)) + self.assertTrue(inspect.iscoroutinefunction(func)) coro = func() self.assertTrue(asyncio.iscoroutine(coro)) @@ -2776,28 +2757,17 @@ def test_get_context(self): finally: loop.close() - def test_proper_refcounts(self): - # see: https://github.com/python/cpython/issues/126083 - class Break: - def __str__(self): - raise RuntimeError("break") - - obj = object() - initial_refcount = sys.getrefcount(obj) - - coro = coroutine_function() - with contextlib.closing(asyncio.EventLoop()) as loop: - task = asyncio.Task.__new__(asyncio.Task) - for _ in range(5): - with self.assertRaisesRegex(RuntimeError, 'break'): - task.__init__(coro, loop=loop, context=obj, name=Break()) - - coro.close() - task._log_destroy_pending = False - del task + def test_task_disallow_multiple_initialization(self): + async def foo(): + pass - self.assertEqual(sys.getrefcount(obj), initial_refcount) + coro = foo() + self.addCleanup(coro.close) + task = self.new_task(self.loop, coro) + task._log_destroy_pending = False + with self.assertRaises(RuntimeError, msg="is already initialized"): + task.__init__(coro, loop=self.loop) def add_subclass_tests(cls): BaseTask = cls.Task @@ -2921,19 +2891,6 @@ class CTask_CFuture_Tests(BaseTaskTests, SetMethodsTest, all_tasks = getattr(tasks, '_c_all_tasks', None) current_task = staticmethod(getattr(tasks, '_c_current_task', None)) - @support.refcount_test - def test_refleaks_in_task___init__(self): - gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount') - async def coro(): - pass - task = self.new_task(self.loop, coro()) - self.loop.run_until_complete(task) - refs_before = gettotalrefcount() - for i in range(100): - task.__init__(coro(), loop=self.loop) - self.loop.run_until_complete(task) - self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) - def test_del__log_destroy_pending_segfault(self): async def coro(): pass @@ -3045,6 +3002,26 @@ class BaseTaskIntrospectionTests: _enter_task = None _leave_task = None all_tasks = None + Task = None + + def test_register_task_resurrection(self): + register_task = self._register_task + class EvilLoop: + def get_debug(self): + return False + + def call_exception_handler(self, context): + register_task(context["task"]) + + async def coro_fn (): + pass + + coro = coro_fn() + self.addCleanup(coro.close) + loop = EvilLoop() + with self.assertRaises(AttributeError): + self.Task(coro, loop=loop) + def test__register_task_1(self): class TaskLike: @@ -3175,6 +3152,7 @@ class PyIntrospectionTests(test_utils.TestCase, BaseTaskIntrospectionTests): _leave_task = staticmethod(tasks._py_leave_task) all_tasks = staticmethod(tasks._py_all_tasks) current_task = staticmethod(tasks._py_current_task) + Task = tasks._PyTask @unittest.skipUnless(hasattr(tasks, '_c_register_task'), @@ -3187,10 +3165,12 @@ class CIntrospectionTests(test_utils.TestCase, BaseTaskIntrospectionTests): _leave_task = staticmethod(tasks._c_leave_task) all_tasks = staticmethod(tasks._c_all_tasks) current_task = staticmethod(tasks._c_current_task) + Task = tasks._CTask else: _register_task = _unregister_task = _enter_task = _leave_task = None + class BaseCurrentLoopTests: current_task = None @@ -3680,6 +3660,30 @@ def task_factory(loop, coro): (loop, context), kwargs = callback.call_args self.assertEqual(context['exception'], exc_context.exception) + def test_run_coroutine_threadsafe_and_cancel(self): + task = None + thread_future = None + # Use a custom task factory to capture the created Task + def task_factory(loop, coro): + nonlocal task + task = asyncio.Task(coro, loop=loop) + return task + + self.addCleanup(self.loop.set_task_factory, + self.loop.get_task_factory()) + + async def target(): + nonlocal thread_future + self.loop.set_task_factory(task_factory) + thread_future = asyncio.run_coroutine_threadsafe(asyncio.sleep(10), self.loop) + await asyncio.sleep(0) + + thread_future.cancel() + + self.loop.run_until_complete(target()) + self.assertTrue(task.cancelled()) + self.assertTrue(thread_future.cancelled()) + class SleepTests(test_utils.TestCase): def setUp(self): diff --git a/Lib/test/test_asyncio/test_tools.py b/Lib/test/test_asyncio/test_tools.py index 34e94830204cd8..df934164eb9fd6 100644 --- a/Lib/test/test_asyncio/test_tools.py +++ b/Lib/test/test_asyncio/test_tools.py @@ -2,12 +2,27 @@ from asyncio import tools -from collections import namedtuple +import _remote_debugging -FrameInfo = namedtuple('FrameInfo', ['funcname', 'filename', 'lineno']) -CoroInfo = namedtuple('CoroInfo', ['call_stack', 'task_name']) -TaskInfo = namedtuple('TaskInfo', ['task_id', 'task_name', 'coroutine_stack', 'awaited_by']) -AwaitedInfo = namedtuple('AwaitedInfo', ['thread_id', 'awaited_by']) + +def LocationInfo(lineno, end_lineno=None, col_offset=None, end_col_offset=None): + return _remote_debugging.LocationInfo((lineno, end_lineno, col_offset, end_col_offset)) + + +def FrameInfo(funcname, filename, location, opcode=None): + return _remote_debugging.FrameInfo((filename, location, funcname, opcode)) + + +def CoroInfo(call_stack, task_name): + return _remote_debugging.CoroInfo((call_stack, task_name)) + + +def TaskInfo(task_id, task_name, coroutine_stack, awaited_by): + return _remote_debugging.TaskInfo((task_id, task_name, coroutine_stack, awaited_by)) + + +def AwaitedInfo(thread_id, awaited_by): + return _remote_debugging.AwaitedInfo((thread_id, awaited_by)) # mock output of get_all_awaited_by function. @@ -33,33 +48,33 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("awaiter3", "/path/to/app.py", 130), - FrameInfo("awaiter2", "/path/to/app.py", 120), - FrameInfo("awaiter", "/path/to/app.py", 110) + FrameInfo("awaiter3", "/path/to/app.py", LocationInfo(130)), + FrameInfo("awaiter2", "/path/to/app.py", LocationInfo(120)), + FrameInfo("awaiter", "/path/to/app.py", LocationInfo(110)) ], task_name=4 ), CoroInfo( call_stack=[ - FrameInfo("awaiterB3", "/path/to/app.py", 190), - FrameInfo("awaiterB2", "/path/to/app.py", 180), - FrameInfo("awaiterB", "/path/to/app.py", 170) + FrameInfo("awaiterB3", "/path/to/app.py", LocationInfo(190)), + FrameInfo("awaiterB2", "/path/to/app.py", LocationInfo(180)), + FrameInfo("awaiterB", "/path/to/app.py", LocationInfo(170)) ], task_name=5 ), CoroInfo( call_stack=[ - FrameInfo("awaiterB3", "/path/to/app.py", 190), - FrameInfo("awaiterB2", "/path/to/app.py", 180), - FrameInfo("awaiterB", "/path/to/app.py", 170) + FrameInfo("awaiterB3", "/path/to/app.py", LocationInfo(190)), + FrameInfo("awaiterB2", "/path/to/app.py", LocationInfo(180)), + FrameInfo("awaiterB", "/path/to/app.py", LocationInfo(170)) ], task_name=6 ), CoroInfo( call_stack=[ - FrameInfo("awaiter3", "/path/to/app.py", 130), - FrameInfo("awaiter2", "/path/to/app.py", 120), - FrameInfo("awaiter", "/path/to/app.py", 110) + FrameInfo("awaiter3", "/path/to/app.py", LocationInfo(130)), + FrameInfo("awaiter2", "/path/to/app.py", LocationInfo(120)), + FrameInfo("awaiter", "/path/to/app.py", LocationInfo(110)) ], task_name=7 ) @@ -72,9 +87,9 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("main", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("main", "", LocationInfo(0)) ], task_name=2 ) @@ -87,9 +102,9 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("main", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("main", "", LocationInfo(0)) ], task_name=2 ) @@ -102,10 +117,10 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("blocho_caller", "", 0), - FrameInfo("bloch", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("blocho_caller", "", LocationInfo(0)), + FrameInfo("bloch", "", LocationInfo(0)) ], task_name=8 ) @@ -118,10 +133,10 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("blocho_caller", "", 0), - FrameInfo("bloch", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("blocho_caller", "", LocationInfo(0)), + FrameInfo("bloch", "", LocationInfo(0)) ], task_name=8 ) @@ -134,10 +149,10 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("blocho_caller", "", 0), - FrameInfo("bloch", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("blocho_caller", "", LocationInfo(0)), + FrameInfo("bloch", "", LocationInfo(0)) ], task_name=9 ) @@ -150,10 +165,10 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("blocho_caller", "", 0), - FrameInfo("bloch", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("blocho_caller", "", LocationInfo(0)), + FrameInfo("bloch", "", LocationInfo(0)) ], task_name=9 ) @@ -222,7 +237,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main2", "", 0)], + call_stack=[FrameInfo("main2", "", LocationInfo(0))], task_name=5 ) ] @@ -233,7 +248,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main2", "", 0)], + call_stack=[FrameInfo("main2", "", LocationInfo(0))], task_name=5 ) ] @@ -244,7 +259,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main2", "", 0)], + call_stack=[FrameInfo("main2", "", LocationInfo(0))], task_name=5 ) ] @@ -266,7 +281,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=1 ) ] @@ -277,7 +292,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=1 ) ] @@ -288,7 +303,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=1 ) ] @@ -347,7 +362,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=4 ) ] @@ -358,7 +373,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=4 ) ] @@ -369,7 +384,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=4 ) ] @@ -415,11 +430,11 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("awaiter2", "", 0)], + call_stack=[FrameInfo("awaiter2", "", LocationInfo(0))], task_name=4 ), CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=2 ) ] @@ -430,7 +445,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("awaiter", "", 0)], + call_stack=[FrameInfo("awaiter", "", LocationInfo(0))], task_name=3 ) ] @@ -462,9 +477,9 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("nested", "", 0), - FrameInfo("nested", "", 0), - FrameInfo("task_b", "", 0) + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("task_b", "", LocationInfo(0)) ], task_name=4 ) @@ -477,17 +492,17 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("nested", "", 0), - FrameInfo("nested", "", 0), - FrameInfo("task_c", "", 0) + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("task_c", "", LocationInfo(0)) ], task_name=5 ), CoroInfo( call_stack=[ - FrameInfo("nested", "", 0), - FrameInfo("nested", "", 0), - FrameInfo("task_a", "", 0) + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("task_a", "", LocationInfo(0)) ], task_name=3 ) @@ -500,8 +515,8 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("nested", "", 0), - FrameInfo("nested", "", 0) + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("nested", "", LocationInfo(0)) ], task_name=6 ) @@ -514,9 +529,9 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("nested", "", 0), - FrameInfo("nested", "", 0), - FrameInfo("task_b", "", 0) + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("task_b", "", LocationInfo(0)) ], task_name=4 ) @@ -553,33 +568,33 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("awaiter3", "", 0), - FrameInfo("awaiter2", "", 0), - FrameInfo("awaiter", "", 0) + FrameInfo("awaiter3", "", LocationInfo(0)), + FrameInfo("awaiter2", "", LocationInfo(0)), + FrameInfo("awaiter", "", LocationInfo(0)) ], task_name=4 ), CoroInfo( call_stack=[ - FrameInfo("awaiter1_3", "", 0), - FrameInfo("awaiter1_2", "", 0), - FrameInfo("awaiter1", "", 0) + FrameInfo("awaiter1_3", "", LocationInfo(0)), + FrameInfo("awaiter1_2", "", LocationInfo(0)), + FrameInfo("awaiter1", "", LocationInfo(0)) ], task_name=5 ), CoroInfo( call_stack=[ - FrameInfo("awaiter1_3", "", 0), - FrameInfo("awaiter1_2", "", 0), - FrameInfo("awaiter1", "", 0) + FrameInfo("awaiter1_3", "", LocationInfo(0)), + FrameInfo("awaiter1_2", "", LocationInfo(0)), + FrameInfo("awaiter1", "", LocationInfo(0)) ], task_name=6 ), CoroInfo( call_stack=[ - FrameInfo("awaiter3", "", 0), - FrameInfo("awaiter2", "", 0), - FrameInfo("awaiter", "", 0) + FrameInfo("awaiter3", "", LocationInfo(0)), + FrameInfo("awaiter2", "", LocationInfo(0)), + FrameInfo("awaiter", "", LocationInfo(0)) ], task_name=7 ) @@ -592,9 +607,9 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("main", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("main", "", LocationInfo(0)) ], task_name=2 ) @@ -607,9 +622,9 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("main", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("main", "", LocationInfo(0)) ], task_name=2 ) @@ -622,10 +637,10 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("blocho_caller", "", 0), - FrameInfo("bloch", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("blocho_caller", "", LocationInfo(0)), + FrameInfo("bloch", "", LocationInfo(0)) ], task_name=8 ) @@ -638,10 +653,10 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("blocho_caller", "", 0), - FrameInfo("bloch", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("blocho_caller", "", LocationInfo(0)), + FrameInfo("bloch", "", LocationInfo(0)) ], task_name=8 ) @@ -654,10 +669,10 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("blocho_caller", "", 0), - FrameInfo("bloch", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("blocho_caller", "", LocationInfo(0)), + FrameInfo("bloch", "", LocationInfo(0)) ], task_name=9 ) @@ -670,10 +685,10 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("_aexit", "", 0), - FrameInfo("__aexit__", "", 0), - FrameInfo("blocho_caller", "", 0), - FrameInfo("bloch", "", 0) + FrameInfo("_aexit", "", LocationInfo(0)), + FrameInfo("__aexit__", "", LocationInfo(0)), + FrameInfo("blocho_caller", "", LocationInfo(0)), + FrameInfo("bloch", "", LocationInfo(0)) ], task_name=9 ) @@ -797,7 +812,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main2", "", 0)], + call_stack=[FrameInfo("main2", "", LocationInfo(0))], task_name=5 ) ] @@ -808,7 +823,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main2", "", 0)], + call_stack=[FrameInfo("main2", "", LocationInfo(0))], task_name=5 ) ] @@ -819,7 +834,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main2", "", 0)], + call_stack=[FrameInfo("main2", "", LocationInfo(0))], task_name=5 ) ] @@ -841,7 +856,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=1 ) ] @@ -852,7 +867,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=1 ) ] @@ -863,7 +878,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=1 ) ] @@ -916,7 +931,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=4 ) ] @@ -927,7 +942,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=4 ) ] @@ -938,7 +953,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=4 ) ] @@ -979,11 +994,11 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("awaiter2", "", 0)], + call_stack=[FrameInfo("awaiter2", "", LocationInfo(0))], task_name=4 ), CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=2 ) ] @@ -994,7 +1009,7 @@ coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("awaiter", "", 0)], + call_stack=[FrameInfo("awaiter", "", LocationInfo(0))], task_name=3 ) ] @@ -1033,9 +1048,9 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("nested", "", 0), - FrameInfo("nested", "", 0), - FrameInfo("task_b", "", 0) + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("task_b", "", LocationInfo(0)) ], task_name=4 ) @@ -1048,17 +1063,17 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("nested", "", 0), - FrameInfo("nested", "", 0), - FrameInfo("task_c", "", 0) + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("task_c", "", LocationInfo(0)) ], task_name=5 ), CoroInfo( call_stack=[ - FrameInfo("nested", "", 0), - FrameInfo("nested", "", 0), - FrameInfo("task_a", "", 0) + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("task_a", "", LocationInfo(0)) ], task_name=3 ) @@ -1071,8 +1086,8 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("nested", "", 0), - FrameInfo("nested", "", 0) + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("nested", "", LocationInfo(0)) ], task_name=6 ) @@ -1085,9 +1100,9 @@ awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("nested", "", 0), - FrameInfo("nested", "", 0), - FrameInfo("task_b", "", 0) + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("nested", "", LocationInfo(0)), + FrameInfo("task_b", "", LocationInfo(0)) ], task_name=4 ) @@ -1290,7 +1305,7 @@ def test_cycle_detection(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=3 ) ] @@ -1301,7 +1316,7 @@ def test_cycle_detection(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=2 ) ] @@ -1331,7 +1346,7 @@ def test_complex_tree(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=2 ) ] @@ -1342,7 +1357,7 @@ def test_complex_tree(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=3 ) ] @@ -1379,7 +1394,7 @@ def test_complex_table(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=2 ) ] @@ -1390,7 +1405,7 @@ def test_complex_table(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("main", "", 0)], + call_stack=[FrameInfo("main", "", LocationInfo(0))], task_name=3 ) ] @@ -1417,11 +1432,11 @@ def test_deep_coroutine_chain(self): awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("c1", "", 0), - FrameInfo("c2", "", 0), - FrameInfo("c3", "", 0), - FrameInfo("c4", "", 0), - FrameInfo("c5", "", 0) + FrameInfo("c1", "", LocationInfo(0)), + FrameInfo("c2", "", LocationInfo(0)), + FrameInfo("c3", "", LocationInfo(0)), + FrameInfo("c4", "", LocationInfo(0)), + FrameInfo("c5", "", LocationInfo(0)) ], task_name=11 ) @@ -1461,7 +1476,7 @@ def test_multiple_cycles_same_node(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("call1", "", 0)], + call_stack=[FrameInfo("call1", "", LocationInfo(0))], task_name=2 ) ] @@ -1472,7 +1487,7 @@ def test_multiple_cycles_same_node(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("call2", "", 0)], + call_stack=[FrameInfo("call2", "", LocationInfo(0))], task_name=3 ) ] @@ -1483,11 +1498,11 @@ def test_multiple_cycles_same_node(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("call3", "", 0)], + call_stack=[FrameInfo("call3", "", LocationInfo(0))], task_name=1 ), CoroInfo( - call_stack=[FrameInfo("call4", "", 0)], + call_stack=[FrameInfo("call4", "", LocationInfo(0))], task_name=2 ) ] @@ -1511,7 +1526,7 @@ def test_table_output_format(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("foo", "", 0)], + call_stack=[FrameInfo("foo", "", LocationInfo(0))], task_name=2 ) ] @@ -1555,7 +1570,7 @@ def test_task_awaits_self(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("loopback", "", 0)], + call_stack=[FrameInfo("loopback", "", LocationInfo(0))], task_name=1 ) ] @@ -1579,7 +1594,7 @@ def test_task_with_missing_awaiter_id(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("coro", "", 0)], + call_stack=[FrameInfo("coro", "", LocationInfo(0))], task_name=999 ) ] @@ -1603,11 +1618,11 @@ def test_duplicate_coroutine_frames(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("frameA", "", 0)], + call_stack=[FrameInfo("frameA", "", LocationInfo(0))], task_name=2 ), CoroInfo( - call_stack=[FrameInfo("frameA", "", 0)], + call_stack=[FrameInfo("frameA", "", LocationInfo(0))], task_name=3 ) ] @@ -1651,7 +1666,7 @@ def test_task_with_no_name(self): coroutine_stack=[], awaited_by=[ CoroInfo( - call_stack=[FrameInfo("f1", "", 0)], + call_stack=[FrameInfo("f1", "", LocationInfo(0))], task_name=2 ) ] @@ -1682,8 +1697,8 @@ def test_tree_rendering_with_custom_emojis(self): awaited_by=[ CoroInfo( call_stack=[ - FrameInfo("f1", "", 0), - FrameInfo("f2", "", 0) + FrameInfo("f1", "", LocationInfo(0)), + FrameInfo("f2", "", LocationInfo(0)) ], task_name=2 ) diff --git a/Lib/test/test_asyncio/test_transports.py b/Lib/test/test_asyncio/test_transports.py index dbb572e2e1536b..1781f1f67753f3 100644 --- a/Lib/test/test_asyncio/test_transports.py +++ b/Lib/test/test_asyncio/test_transports.py @@ -98,6 +98,26 @@ def get_write_buffer_size(self): self.assertTrue(transport._protocol_paused) self.assertEqual(transport.get_write_buffer_limits(), (128, 256)) + def test_flowcontrol_mixin_compute_write_limits(self): + + class MyTransport(transports._FlowControlMixin, + transports.Transport): + + def get_write_buffer_size(self): + return 0 + + loop = mock.Mock() + transport = MyTransport(loop=loop) + + self.assertEqual(transport.get_write_buffer_limits(), + (16 * 1024, 64 * 1024)) + + transport.set_write_buffer_limits(low=100) + self.assertEqual(transport.get_write_buffer_limits(), (100, 400)) + + transport.set_write_buffer_limits(high=200) + self.assertEqual(transport.get_write_buffer_limits(), (50, 200)) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 22982dc9d8aefe..118cb866997c51 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -15,7 +15,7 @@ from unittest import mock from test import support -from test.support import os_helper +from test.support import os_helper, warnings_helper from test.support import socket_helper from test.support import wait_process from test.support import hashlib_helper @@ -1180,11 +1180,48 @@ async def runner(): @support.requires_fork() -class TestFork(unittest.IsolatedAsyncioTestCase): +class TestFork(unittest.TestCase): + + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + def test_fork_not_share_current_task(self): + loop = object() + task = object() + asyncio._set_running_loop(loop) + self.addCleanup(asyncio._set_running_loop, None) + asyncio.tasks._enter_task(loop, task) + self.addCleanup(asyncio.tasks._leave_task, loop, task) + self.assertIs(asyncio.current_task(), task) + r, w = os.pipe() + self.addCleanup(os.close, r) + self.addCleanup(os.close, w) + pid = os.fork() + if pid == 0: + # child + try: + asyncio._set_running_loop(loop) + current_task = asyncio.current_task() + if current_task is None: + os.write(w, b'NO TASK') + else: + os.write(w, b'TASK:' + str(id(current_task)).encode()) + except BaseException as e: + os.write(w, b'ERROR:' + ascii(e).encode()) + finally: + asyncio._set_running_loop(None) + os._exit(0) + else: + # parent + result = os.read(r, 100) + self.assertEqual(result, b'NO TASK') + wait_process(pid, exitcode=0) - async def test_fork_not_share_event_loop(self): + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + def test_fork_not_share_event_loop(self): # The forked process should not share the event loop with the parent - loop = asyncio.get_running_loop() + loop = object() + asyncio._set_running_loop(loop) + self.assertIs(asyncio.get_running_loop(), loop) + self.addCleanup(asyncio._set_running_loop, None) r, w = os.pipe() self.addCleanup(os.close, r) self.addCleanup(os.close, w) @@ -1206,6 +1243,7 @@ async def test_fork_not_share_event_loop(self): self.assertEqual(result, b'NO LOOP') wait_process(pid, exitcode=0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @hashlib_helper.requires_hashdigest('md5') @support.skip_if_sanitizer("TSAN doesn't support threads after fork", thread=True) def test_fork_signal_handling(self): @@ -1253,6 +1291,7 @@ async def func(): self.assertFalse(parent_handled.is_set()) self.assertTrue(child_handled.is_set()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @hashlib_helper.requires_hashdigest('md5') @support.skip_if_sanitizer("TSAN doesn't support threads after fork", thread=True) def test_fork_asyncio_run(self): @@ -1273,6 +1312,7 @@ async def child_main(): self.assertEqual(result.value, 42) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @hashlib_helper.requires_hashdigest('md5') @support.skip_if_sanitizer("TSAN doesn't support threads after fork", thread=True) def test_fork_asyncio_subprocess(self): @@ -1293,5 +1333,45 @@ async def child_main(): self.assertEqual(result.value, 0) + +@unittest.skipUnless( + unix_events.can_use_pidfd(), + "operating system does not support pidfd", +) +class PidfdChildWatcherTests(test_utils.TestCase): + + def setUp(self): + super().setUp() + self.loop = asyncio.new_event_loop() + self.set_event_loop(self.loop) + + def test_pidfd_closed_when_waitpid_raises(self): + # _do_wait() must close the pidfd even when waitpid() + # fails with something other than ChildProcessError, otherwise the + # pidfd is leaked + self.loop.set_exception_handler(lambda loop, context: None) + + async def coro(): + before = os_helper.fd_count() + proc = await asyncio.create_subprocess_exec( + sys.executable, '-c', 'import sys; sys.stdin.read()', + stdin=asyncio.subprocess.PIPE + ) + + with mock.patch.object(os, 'waitpid', + side_effect=OSError('unexpected')) as m: + proc.stdin.close() + while not m.called: + await asyncio.sleep(0) + + os.waitpid(proc.pid, 0) + proc._transport._process_exited(0) + await proc.wait() + + self.assertEqual(os_helper.fd_count(), before) + + self.loop.run_until_complete(coro()) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_asyncio/test_windows_utils.py b/Lib/test/test_asyncio/test_windows_utils.py index 97f078ff911b5a..50969761347595 100644 --- a/Lib/test/test_asyncio/test_windows_utils.py +++ b/Lib/test/test_asyncio/test_windows_utils.py @@ -77,6 +77,30 @@ def test_pipe_handle(self): else: raise RuntimeError('expected ERROR_INVALID_HANDLE') + def test_pipe_handle_close_after_external_close(self): + # gh-149388: PipeHandle.close() must clear ``_handle`` before calling + # CloseHandle so that if CloseHandle raises on a stale handle the + # PipeHandle is still marked closed and __del__ / subsequent close() + # calls are silent no-ops. + h1, h2 = windows_utils.pipe(overlapped=(False, False)) + try: + p = windows_utils.PipeHandle(h1) + # Simulate an external close of the underlying handle (e.g. + # a finalizer race or a concurrent close on the same object). + _winapi.CloseHandle(p.handle) + # First close() still propagates the OSError from CloseHandle, + # but must clear ``_handle`` first. + with self.assertRaises(OSError): + p.close() + self.assertIsNone(p.handle) + # Second close() is a no-op. + p.close() + # __del__ through GC is also a silent no-op — no unraisable. + del p + support.gc_collect() + finally: + _winapi.CloseHandle(h2) + class PopenTests(unittest.TestCase): @@ -129,5 +153,25 @@ def test_popen(self): pass +class OverlappedRefleakTests(unittest.TestCase): + + def test_wsasendto_failure(self): + ov = _overlapped.Overlapped() + buf = bytearray(4096) + with self.assertRaises(OSError): + ov.WSASendTo(0x1234, buf, 0, ("127.0.0.1", 1)) + + def test_wsarecvfrom_failure(self): + ov = _overlapped.Overlapped() + with self.assertRaises(OSError): + ov.WSARecvFrom(0x1234, 1024, 0) + + def test_wsarecvfrominto_failure(self): + ov = _overlapped.Overlapped() + buf = bytearray(4096) + with self.assertRaises(OSError): + ov.WSARecvFromInto(0x1234, buf, len(buf), 0) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py index a480e16e81bb91..62cfcf8ceb5f2a 100644 --- a/Lib/test/test_asyncio/utils.py +++ b/Lib/test/test_asyncio/utils.py @@ -388,8 +388,8 @@ def close(self): else: # pragma: no cover raise AssertionError("Time generator is not finished") - def _add_reader(self, fd, callback, *args): - self.readers[fd] = events.Handle(callback, args, self, None) + def _add_reader(self, fd, callback, *args, context=None): + self.readers[fd] = events.Handle(callback, args, self, context) def _remove_reader(self, fd): self.remove_reader_count[fd] += 1 @@ -414,8 +414,8 @@ def assert_no_reader(self, fd): if fd in self.readers: raise AssertionError(f'fd {fd} is registered') - def _add_writer(self, fd, callback, *args): - self.writers[fd] = events.Handle(callback, args, self, None) + def _add_writer(self, fd, callback, *args, context=None): + self.writers[fd] = events.Handle(callback, args, self, context) def _remove_writer(self, fd): self.remove_writer_count[fd] += 1 diff --git a/Lib/test/test_atexit.py b/Lib/test/test_atexit.py index eb01da6e88a8bc..8256ff183f28c9 100644 --- a/Lib/test/test_atexit.py +++ b/Lib/test/test_atexit.py @@ -1,9 +1,11 @@ import atexit import os +import subprocess import textwrap import unittest from test import support -from test.support import script_helper +from test.support import SuppressCrashReport, script_helper +from test.support import os_helper from test.support import threading_helper class GeneralTest(unittest.TestCase): @@ -79,6 +81,62 @@ def thready(): # want them to affect the rest of the tests. script_helper.assert_python_ok("-c", textwrap.dedent(source)) + @threading_helper.requires_working_threading() + def test_thread_created_in_atexit(self): + source = """if True: + import atexit + import threading + import time + + + def run(): + print(24) + time.sleep(1) + print(42) + + @atexit.register + def start_thread(): + threading.Thread(target=run).start() + """ + return_code, stdout, stderr = script_helper.assert_python_ok("-c", source) + self.assertEqual(return_code, 0) + self.assertEqual(stdout, f"24{os.linesep}42{os.linesep}".encode("utf-8")) + self.assertEqual(stderr, b"") + + @threading_helper.requires_working_threading() + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_thread_created_in_atexit_subinterpreter(self): + try: + from concurrent import interpreters + except ImportError: + self.skipTest("subinterpreters are not available") + + read, write = os.pipe() + source = f"""if True: + import atexit + import threading + import time + import os + + def run(): + os.write({write}, b'spanish') + time.sleep(1) + os.write({write}, b'inquisition') + + @atexit.register + def start_thread(): + threading.Thread(target=run).start() + """ + interp = interpreters.create() + try: + interp.exec(source) + + # Close the interpreter to invoke atexit callbacks + interp.close() + self.assertEqual(os.read(read, 100), b"spanishinquisition") + finally: + os.close(read) + os.close(write) @support.cpython_only class SubinterpreterTest(unittest.TestCase): @@ -133,6 +191,37 @@ def callback(): self.assertEqual(os.read(r, len(expected)), expected) os.close(r) + # Python built with Py_TRACE_REFS fail with a fatal error in + # _PyRefchain_Trace() on memory allocation error. + @unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build') + def test_atexit_with_low_memory(self): + # gh-140080: Test that setting low memory after registering an atexit + # callback doesn't cause an infinite loop during finalization. + code = textwrap.dedent(""" + import atexit + import _testcapi + + def callback(): + print("hello") + + atexit.register(callback) + # Simulate low memory condition + _testcapi.set_nomemory(0) + """) + + with os_helper.temp_dir() as temp_dir: + script = script_helper.make_script(temp_dir, 'test_atexit_script', code) + with SuppressCrashReport(): + with script_helper.spawn_python(script, + stderr=subprocess.PIPE) as proc: + proc.wait() + stdout = proc.stdout.read() + stderr = proc.stderr.read() + + self.assertIn(proc.returncode, (0, 1)) + self.assertNotIn(b"hello", stdout) + self.assertIn(b"MemoryError", stderr) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_audit.py b/Lib/test/test_audit.py index 077765fcda210a..db4e1eb9999c1f 100644 --- a/Lib/test/test_audit.py +++ b/Lib/test/test_audit.py @@ -331,5 +331,14 @@ def test_sys_remote_exec(self): if returncode: self.fail(stderr) + def test_import_module(self): + self.do_test("test_import_module") + + def test_builtin__import__(self): + self.do_test("test_builtin__import__") + + def test_import_statement(self): + self.do_test("test_import_statement") + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_base64.py b/Lib/test/test_base64.py index ce2e3e3726fcd0..868abcfee24e10 100644 --- a/Lib/test/test_base64.py +++ b/Lib/test/test_base64.py @@ -1,9 +1,11 @@ import unittest import base64 import binascii +import string +import sys import os from array import array -from test.support import cpython_only +from test.support import cpython_only, check_impl_detail from test.support import os_helper from test.support import script_helper from test.support.import_helper import ensure_lazy_imports @@ -14,6 +16,8 @@ class LazyImportTest(unittest.TestCase): def test_lazy_import(self): ensure_lazy_imports("base64", {"re", "getopt"}) +from test.support.hypothesis_helper import hypothesis + class LegacyBase64TestCase(unittest.TestCase): @@ -68,6 +72,13 @@ def test_decodebytes(self): eq(base64.decodebytes(array('B', b'YWJj\n')), b'abc') self.check_type_errors(base64.decodebytes) + @hypothesis.given(payload=hypothesis.strategies.binary()) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz') + def test_bytes_encode_decode_round_trip(self, payload): + encoded = base64.encodebytes(payload) + decoded = base64.decodebytes(encoded) + self.assertEqual(payload, decoded) + def test_encode(self): eq = self.assertEqual from io import BytesIO, StringIO @@ -96,6 +107,19 @@ def test_decode(self): self.assertRaises(TypeError, base64.encode, BytesIO(b'YWJj\n'), StringIO()) self.assertRaises(TypeError, base64.encode, StringIO('YWJj\n'), StringIO()) + @hypothesis.given(payload=hypothesis.strategies.binary()) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz') + def test_legacy_encode_decode_round_trip(self, payload): + from io import BytesIO + payload_file_r = BytesIO(payload) + encoded_file_w = BytesIO() + base64.encode(payload_file_r, encoded_file_w) + encoded_file_r = BytesIO(encoded_file_w.getvalue()) + decoded_file_w = BytesIO() + base64.decode(encoded_file_r, decoded_file_w) + decoded = decoded_file_w.getvalue() + self.assertEqual(payload, decoded) + class BaseXYTestCase(unittest.TestCase): @@ -149,6 +173,7 @@ def test_b64encode(self): b"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNE" b"RUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NT" b"Y3ODkhQCMwXiYqKCk7Ojw+LC4gW117fQ==") + # Test with arbitrary alternative characters eq(base64.b64encode(b'\xd3V\xbeo\xf7\x1d', altchars=b'*$'), b'01a*b$cd') eq(base64.b64encode(b'\xd3V\xbeo\xf7\x1d', altchars=bytearray(b'*$')), @@ -184,6 +209,55 @@ def test_b64encode(self): b'\xd3V\xbeo\xf7\x1d', b'01a-b_cd') self.check_encode_type_errors(base64.urlsafe_b64encode) + def test_b64encode_padded(self): + b64encode = base64.b64encode + self.assertEqual(b64encode(b'', padded=False), b'') + self.assertEqual(b64encode(b'a', padded=False), b'YQ') + self.assertEqual(b64encode(b'ab', padded=False), b'YWI') + self.assertEqual(b64encode(b'abc', padded=False), b'YWJj') + self.assertEqual(b64encode(b'\xfb', padded=False, altchars=b'-_'), b'-w') + self.assertEqual(b64encode(b'\xfb\xff', padded=False, altchars=b'-_'), + b'-_8') + self.assertEqual(b64encode(b'\xfb\xff\xbf', padded=False, altchars=b'-_'), + b'-_-_') + + urlsafe_b64encode = base64.urlsafe_b64encode + self.assertEqual(urlsafe_b64encode(b'', padded=False), b'') + self.assertEqual(urlsafe_b64encode(b'\xfb', padded=False), b'-w') + self.assertEqual(urlsafe_b64encode(b'\xfb\xff', padded=False), b'-_8') + self.assertEqual(urlsafe_b64encode(b'\xfb\xff\xbf', padded=False), + b'-_-_') + + def _common_test_wrapcol(self, func, data): + eq = self.assertEqual + expected = func(data) + eq(func(data, wrapcol=0), expected) + eq(func(data, wrapcol=80), expected) + eq(func(b'', wrapcol=0), func(b'')) + eq(func(b'', wrapcol=1), func(b'')) + eq(func(data, wrapcol=sys.maxsize), expected) + if check_impl_detail(): + eq(func(data, wrapcol=sys.maxsize*2), expected) + with self.assertRaises(OverflowError): + func(data, wrapcol=2**1000) + with self.assertRaises(ValueError): + func(data, wrapcol=-80) + with self.assertRaises(TypeError): + func(data, wrapcol=80.0) + with self.assertRaises(TypeError): + func(data, wrapcol='80') + if func is not base64.b16encode: + with self.assertRaises(TypeError): + func(data, wrapcol=None) + + def test_b64encode_wrapcol(self): + eq = self.assertEqual + b = b'www.python.org' + self._common_test_wrapcol(base64.b64encode, b) + eq(base64.b64encode(b, wrapcol=8), b'd3d3LnB5\ndGhvbi5v\ncmc=') + eq(base64.b64encode(b, wrapcol=11), b'd3d3LnB5\ndGhvbi5v\ncmc=') + eq(base64.b64encode(b, wrapcol=1), b'd3d3\nLnB5\ndGhv\nbi5v\ncmc=') + def test_b64decode(self): eq = self.assertEqual @@ -208,18 +282,6 @@ def test_b64decode(self): self.check_other_types(base64.b64decode, b"YWJj", b"abc") self.check_decode_type_errors(base64.b64decode) - # Test with arbitrary alternative characters - tests_altchars = {(b'01a*b$cd', b'*$'): b'\xd3V\xbeo\xf7\x1d', - } - for (data, altchars), res in tests_altchars.items(): - data_str = data.decode('ascii') - altchars_str = altchars.decode('ascii') - - eq(base64.b64decode(data, altchars=altchars), res) - eq(base64.b64decode(data_str, altchars=altchars), res) - eq(base64.b64decode(data, altchars=altchars_str), res) - eq(base64.b64decode(data_str, altchars=altchars_str), res) - # Test standard alphabet for data, res in tests.items(): eq(base64.standard_b64decode(data), res) @@ -240,28 +302,113 @@ def test_b64decode(self): b'\xd3V\xbeo\xf7\x1d') self.check_decode_type_errors(base64.urlsafe_b64decode) + def test_b64decode_altchars(self): + # Test with arbitrary alternative characters + eq = self.assertEqual + res = b'\xd3V\xbeo\xf7\x1d' + for altchars in b'*$', b'+/', b'/+', b'+_', b'-+', b'-/', b'/_': + data = b'01a%cb%ccd' % tuple(altchars) + data_str = data.decode('ascii') + altchars_str = altchars.decode('ascii') + + eq(base64.b64decode(data, altchars=altchars), res) + eq(base64.b64decode(data_str, altchars=altchars), res) + eq(base64.b64decode(data, altchars=altchars_str), res) + eq(base64.b64decode(data_str, altchars=altchars_str), res) + eq(base64.b64decode(data, altchars=altchars, ignorechars=b'\n'), res) + + eq(base64.b64decode(b'/----', altchars=b'-+', ignorechars=b'/'), b'\xfb\xef\xbe') + eq(base64.b64decode(b'/----', altchars=b'+-', ignorechars=b'/'), b'\xff\xff\xff') + eq(base64.b64decode(b'+----', altchars=b'-/', ignorechars=b'+'), b'\xfb\xef\xbe') + eq(base64.b64decode(b'+----', altchars=b'/-', ignorechars=b'+'), b'\xff\xff\xff') + eq(base64.b64decode(b'+/+/', altchars=b'/+', ignorechars=b''), b'\xff\xef\xfe') + eq(base64.b64decode(b'/+/+', altchars=b'+/', ignorechars=b''), b'\xff\xef\xfe') + + self.assertRaises(ValueError, base64.b64decode, b'', altchars=b'+') + self.assertRaises(ValueError, base64.b64decode, b'', altchars=b'+/-') + self.assertRaises(ValueError, base64.b64decode, '', altchars='+') + self.assertRaises(ValueError, base64.b64decode, '', altchars='+/-') + def test_b64decode_padding_error(self): self.assertRaises(binascii.Error, base64.b64decode, b'abc') self.assertRaises(binascii.Error, base64.b64decode, 'abc') + def test_b64decode_padded(self): + b64decode = base64.b64decode + urlsafe_b64decode = base64.urlsafe_b64decode + def check(data, expected, padded=0): + if b'=' in data: + with self.assertRaisesRegex(binascii.Error, 'Padding not allowed'): + b64decode(data, padded=False, validate=True) + self.assertEqual(b64decode(data, padded=False, ignorechars=b'='), + expected) + self.assertEqual(urlsafe_b64decode(data, padded=True), expected) + self.assertEqual(urlsafe_b64decode(data, padded=False), expected) + data = data.replace(b'=', b'') + self.assertEqual(b64decode(data, padded=False), expected) + self.assertEqual(b64decode(data, padded=False, validate=True), + expected) + self.assertEqual(urlsafe_b64decode(data), expected) + + check(b'', b'') + check(b'YQ==', b'a') + check(b'YWI=', b'ab') + check(b'YWJj', b'abc') + check(b'Y=WJj', b'abc') + check(b'YW=Jj', b'abc') + check(b'YWJ=j', b'abc') + + with self.assertRaisesRegex(binascii.Error, 'Incorrect padding'): + urlsafe_b64decode(b'YQ', padded=True) + with self.assertRaisesRegex(binascii.Error, 'Incorrect padding'): + urlsafe_b64decode(b'YWI', padded=True) + + def _common_test_ignorechars(self, func): + eq = self.assertEqual + eq(func(b'', ignorechars=b' \n'), b'') + eq(func(b'', ignorechars=b''), b'') + eq(func(b' \n', ignorechars=b' \n'), b'') + with self.assertRaises(binascii.Error): + func(b' \n', ignorechars=b'') + with self.assertRaises(binascii.Error): + func(b' \n', ignorechars=b' ') + with self.assertRaises(binascii.Error): + func(b' \n', ignorechars=b'\n') + with self.assertRaises(TypeError): + func(b'', ignorechars='') + with self.assertRaises(TypeError): + func(b'', ignorechars=[]) + with self.assertRaises(TypeError): + func(b'', ignorechars=None) + def test_b64decode_invalid_chars(self): # issue 1466065: Test some invalid characters. - tests = ((b'%3d==', b'\xdd'), - (b'$3d==', b'\xdd'), - (b'[==', b''), - (b'YW]3=', b'am'), - (b'3{d==', b'\xdd'), - (b'3d}==', b'\xdd'), - (b'@@', b''), - (b'!', b''), - (b"YWJj\n", b"abc"), - (b'YWJj\nYWI=', b'abcab')) + tests = ((b'%3d==', b'\xdd', b'%$'), + (b'$3d==', b'\xdd', b'%$'), + (b'[==', b'', b'[='), + (b'YW]3=', b'am', b']'), + (b'3{d==', b'\xdd', b'{}'), + (b'3d}==', b'\xdd', b'{}'), + (b'@@', b'', b'@!'), + (b'!', b'', b'@!'), + (b"YWJj\n", b"abc", b'\n'), + (b'YWJj\nYWI=', b'abcab', b'\n'), + (b'=YWJj', b'abc', b'='), + (b'Y=WJj', b'abc', b'='), + (b'Y==WJj', b'abc', b'='), + (b'Y===WJj', b'abc', b'='), + (b'YW=Jj', b'abc', b'='), + (b'YWJj=', b'abc', b'='), + (b'YW\nJj', b'abc', b'\n'), + (b'YW\nJj', b'abc', bytearray(b'\n')), + (b'YW\nJj', b'abc', memoryview(b'\n')), + ) funcs = ( base64.b64decode, base64.standard_b64decode, base64.urlsafe_b64decode, ) - for bstr, res in tests: + for bstr, res, ignorechars in tests: for func in funcs: with self.subTest(bstr=bstr, func=func): self.assertEqual(func(bstr), res) @@ -270,11 +417,111 @@ def test_b64decode_invalid_chars(self): base64.b64decode(bstr, validate=True) with self.assertRaises(binascii.Error): base64.b64decode(bstr.decode('ascii'), validate=True) - - # Normal alphabet characters not discarded when alternative given - res = b'\xFB\xEF\xBE\xFF\xFF\xFF' - self.assertEqual(base64.b64decode(b'++[[//]]', b'[]'), res) - self.assertEqual(base64.urlsafe_b64decode(b'++--//__'), res) + with self.assertRaises(binascii.Error): + # Even empty ignorechars enables the strict mode. + base64.b64decode(bstr, ignorechars=b'') + r = base64.b64decode(bstr, ignorechars=ignorechars) + self.assertEqual(r, res) + + self._common_test_ignorechars(base64.b64decode) + + # Normal alphabet characters will be discarded when alternative given + discarded = ("invalid character %a in Base64 data with %s " + "will be discarded in future Python versions") + error = ("invalid character %a in Base64 data with %s " + "will be an error in future Python versions") + with self.assertWarns(FutureWarning) as cm: + r = base64.b64decode(b'++++', altchars=b'-_') + self.assertEqual(r, b'\xfb\xef\xbe') + self.assertEqual(str(cm.warning), + discarded % ('+', "altchars=b'-_' and validate=False")) + with self.assertWarns(FutureWarning) as cm: + r = base64.b64decode(b'////', altchars=b'-_') + self.assertEqual(r, b'\xff\xff\xff') + self.assertEqual(str(cm.warning), + discarded % ('/', "altchars=b'-_' and validate=False")) + with self.assertWarns(DeprecationWarning) as cm: + r = base64.b64decode(b'++++', altchars=b'-_', validate=True) + self.assertEqual(r, b'\xfb\xef\xbe') + self.assertEqual(str(cm.warning), + error % ('+', "altchars=b'-_' and validate=True")) + with self.assertWarns(DeprecationWarning) as cm: + r = base64.b64decode(b'////', altchars=b'-_', validate=True) + self.assertEqual(r, b'\xff\xff\xff') + self.assertEqual(str(cm.warning), + error % ('/', "altchars=b'-_' and validate=True")) + r = base64.b64decode(b'++++', altchars=b'-_', ignorechars=b'+') + self.assertEqual(r, b'') + r = base64.b64decode(b'////', altchars=b'-_', ignorechars=b'/') + self.assertEqual(r, b'') + r = base64.b64decode(b'++++////', altchars=b'-_', validate=False, ignorechars=b'') + self.assertEqual(r, b'') + with self.assertRaisesRegex(binascii.Error, 'Only base64 data is allowed'): + base64.b64decode(b'////', altchars=b'-_', ignorechars=b'') + with self.assertRaisesRegex(binascii.Error, 'Only base64 data is allowed'): + base64.b64decode(b'++++', altchars=b'-_', ignorechars=b'') + r = base64.b64decode(b'++++YWJj----____', altchars=b'-_', ignorechars=b'+') + self.assertEqual(r, b'abc\xfb\xef\xbe\xff\xff\xff') + r = base64.b64decode(b'////YWJj----____', altchars=b'-_', ignorechars=b'/') + self.assertEqual(r, b'abc\xfb\xef\xbe\xff\xff\xff') + r = base64.b64decode(b'++++,,,,', altchars=b'+,', ignorechars=b'+') + self.assertEqual(r, b'\xfb\xef\xbe\xff\xff\xff') + r = base64.b64decode(b'////YWJj++++,,,,', altchars=b'+,', ignorechars=b'/') + self.assertEqual(r, b'abc\xfb\xef\xbe\xff\xff\xff') + r = base64.b64decode(b'----////', altchars=b'-/', ignorechars=b'/') + self.assertEqual(r, b'\xfb\xef\xbe\xff\xff\xff') + r = base64.b64decode(b'++++YWJj----////', altchars=b'-/', ignorechars=b'+') + self.assertEqual(r, b'abc\xfb\xef\xbe\xff\xff\xff') + + with self.assertWarns(FutureWarning) as cm: + self.assertEqual(base64.urlsafe_b64decode(b'++++'), b'\xfb\xef\xbe') + self.assertEqual(str(cm.warning), + "invalid character '+' in URL-safe Base64 data " + "will be discarded in future Python versions") + with self.assertWarns(FutureWarning) as cm: + self.assertEqual(base64.urlsafe_b64decode(b'////'), b'\xff\xff\xff') + self.assertEqual(str(cm.warning), + "invalid character '/' in URL-safe Base64 data " + "will be discarded in future Python versions") + with self.assertRaises(binascii.Error): + base64.b64decode(b'+/!', altchars=b'-_') + + def _altchars_strategy(): + """Generate 'altchars' for base64 encoding.""" + reserved_chars = (string.digits + string.ascii_letters + "=").encode() + allowed_chars = hypothesis.strategies.sampled_from( + [n for n in range(256) if n not in reserved_chars]) + two_bytes_strategy = hypothesis.strategies.lists( + allowed_chars, min_size=2, max_size=2, unique=True).map(bytes) + return (hypothesis.strategies.none() + | hypothesis.strategies.just(b"_-") + | two_bytes_strategy) + + @hypothesis.given( + payload=hypothesis.strategies.binary(), + altchars=_altchars_strategy(), + validate=hypothesis.strategies.booleans()) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', b"_-", True) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', b"_-", False) + def test_b64_encode_decode_round_trip(self, payload, altchars, validate): + encoded = base64.b64encode(payload, altchars=altchars) + decoded = base64.b64decode(encoded, altchars=altchars, + validate=validate) + self.assertEqual(payload, decoded) + + @hypothesis.given(payload=hypothesis.strategies.binary()) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz') + def test_standard_b64_encode_decode_round_trip(self, payload): + encoded = base64.standard_b64encode(payload) + decoded = base64.standard_b64decode(encoded) + self.assertEqual(payload, decoded) + + @hypothesis.given(payload=hypothesis.strategies.binary()) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz') + def test_urlsafe_b64_encode_decode_round_trip(self, payload): + encoded = base64.urlsafe_b64encode(payload) + decoded = base64.urlsafe_b64decode(encoded) + self.assertEqual(payload, decoded) def test_b32encode(self): eq = self.assertEqual @@ -289,6 +536,23 @@ def test_b32encode(self): self.check_other_types(base64.b32encode, b'abcd', b'MFRGGZA=') self.check_encode_type_errors(base64.b32encode) + def test_b32encode_padded(self): + b32encode = base64.b32encode + self.assertEqual(b32encode(b'', padded=False), b'') + self.assertEqual(b32encode(b'a', padded=False), b'ME') + self.assertEqual(b32encode(b'ab', padded=False), b'MFRA') + self.assertEqual(b32encode(b'abc', padded=False), b'MFRGG') + self.assertEqual(b32encode(b'abcd', padded=False), b'MFRGGZA') + self.assertEqual(b32encode(b'abcde', padded=False), b'MFRGGZDF') + + def test_b32encode_wrapcol(self): + eq = self.assertEqual + b = b'www.python.org' + self._common_test_wrapcol(base64.b32encode, b) + eq(base64.b32encode(b, wrapcol=16), b'O53XOLTQPF2GQ33O\nFZXXEZY=') + eq(base64.b32encode(b, wrapcol=23), b'O53XOLTQPF2GQ33O\nFZXXEZY=') + eq(base64.b32encode(b, wrapcol=1), b'O53XOLTQ\nPF2GQ33O\nFZXXEZY=') + def test_b32decode(self): eq = self.assertEqual tests = {b'': b'', @@ -324,28 +588,94 @@ def test_b32decode_casefold(self): for data, res in tests.items(): eq(base64.b32decode(data, True), res) + eq(base64.b32decode(data, casefold=True), res) eq(base64.b32decode(data.decode('ascii'), True), res) self.assertRaises(binascii.Error, base64.b32decode, b'me======') self.assertRaises(binascii.Error, base64.b32decode, 'me======') + def test_b32decode_map01(self): # Mapping zero and one - eq(base64.b32decode(b'MLO23456'), b'b\xdd\xad\xf3\xbe') - eq(base64.b32decode('MLO23456'), b'b\xdd\xad\xf3\xbe') - - map_tests = {(b'M1023456', b'L'): b'b\xdd\xad\xf3\xbe', - (b'M1023456', b'I'): b'b\x1d\xad\xf3\xbe', - } - for (data, map01), res in map_tests.items(): - data_str = data.decode('ascii') + eq = self.assertEqual + res_L = b'b\xdd\xad\xf3\xbe' + res_I = b'b\x1d\xad\xf3\xbe' + eq(base64.b32decode(b'MLO23456'), res_L) + eq(base64.b32decode('MLO23456'), res_L) + eq(base64.b32decode(b'MIO23456'), res_I) + eq(base64.b32decode('MIO23456'), res_I) + self.assertRaises(binascii.Error, base64.b32decode, b'M1023456') + self.assertRaises(binascii.Error, base64.b32decode, b'M1O23456') + self.assertRaises(binascii.Error, base64.b32decode, b'ML023456') + self.assertRaises(binascii.Error, base64.b32decode, b'MI023456') + self.assertRaises(ValueError, base64.b32decode, b'', map01=b'') + self.assertRaises(ValueError, base64.b32decode, b'', map01=b'LI') + self.assertRaises(TypeError, base64.b32decode, b'', map01=0) + eq(base64.b32decode(b'MLO23456', map01=None), res_L) + self.assertRaises(binascii.Error, base64.b32decode, b'M1023456', map01=None) + + data = b'M1023456' + data_str = data.decode('ascii') + for map01, res in [(b'L', res_L), (b'I', res_I)]: map01_str = map01.decode('ascii') eq(base64.b32decode(data, map01=map01), res) eq(base64.b32decode(data_str, map01=map01), res) eq(base64.b32decode(data, map01=map01_str), res) eq(base64.b32decode(data_str, map01=map01_str), res) - self.assertRaises(binascii.Error, base64.b32decode, data) - self.assertRaises(binascii.Error, base64.b32decode, data_str) + + eq(base64.b32decode(b'M1O23456', map01=map01), res) + eq(base64.b32decode(b'M%c023456' % map01, map01=map01), res) + eq(base64.b32decode(b'M%cO23456' % map01, map01=map01), res) + + def test_b32decode_padded(self): + b32decode = base64.b32decode + def check(data, expected): + if b'=' in data: + with self.assertRaisesRegex(binascii.Error, 'Padding not allowed'): + b32decode(data, padded=False) + self.assertEqual(b32decode(data, padded=False, ignorechars=b'='), + expected) + data = data.replace(b'=', b'') + self.assertEqual(b32decode(data, padded=False), expected) + + check(b'', b'') + check(b'ME======', b'a') + check(b'MFRA====', b'ab') + check(b'MFRGG===', b'abc') + check(b'MFRGGZA=', b'abcd') + check(b'MFRGGZDF', b'abcde') + check(b'M=FRGGZDF', b'abcde') + check(b'MF=RGGZDF', b'abcde') + check(b'MFR=GGZDF', b'abcde') + check(b'MFRG=GZDF', b'abcde') + check(b'MFRGG=ZDF', b'abcde') + check(b'MFRGGZ=DF', b'abcde') + check(b'MFRGGZD=F', b'abcde') + + def test_b32decode_ignorechars(self): + self._common_test_ignorechars(base64.b32decode) + eq = self.assertEqual + eq(base64.b32decode(b'MFRG\n GZDF\n', ignorechars=b' \n'), b'abcde') + eq(base64.b32decode(b'MFRG\n GZDF\n', ignorechars=bytearray(b' \n')), b'abcde') + eq(base64.b32decode(b'M=======FRGGZDF', ignorechars=b'='), b'abcde') + eq(base64.b32decode(b'MF======RGGZDF', ignorechars=b'='), b'abcde') + eq(base64.b32decode(b'MFR=====GGZDF', ignorechars=b'='), b'abcde') + eq(base64.b32decode(b'MFRG====GZDF', ignorechars=b'='), b'abcde') + eq(base64.b32decode(b'MFRGG===ZDF', ignorechars=b'='), b'abcde') + eq(base64.b32decode(b'MFRGGZ==DF', ignorechars=b'='), b'abcde') + eq(base64.b32decode(b'MFRGGZD=F', ignorechars=b'='), b'abcde') + eq(base64.b32decode(b'MFRGGZDF=', ignorechars=b'='), b'abcde') + eq(base64.b32decode(b'MFRA======', ignorechars=b'='), b'ab') + + eq(base64.b32decode(b'mfRggzDfmzTQ====', ignorechars=b'mfgz'), + b'\x88\xe7') + eq(base64.b32decode(b'mfRggzDfmzTQ====', casefold=True, ignorechars=b'mfgz'), + b'abcdefg') + eq(base64.b32decode(b'M0F1R0G1G0Z1D0F1', ignorechars=b'01'), b'abcde') + eq(base64.b32decode(b'M0F1R0G1G0Z1D0F1', map01=b'L', ignorechars=b'01'), + b'c\x8a\xb8\xb8\xcb3\xb2\xb1\xb8\xab') + eq(base64.b32decode(b'M0F1R0G1G0Z1D0F1', map01=b'I', ignorechars=b'01'), + b'c\x8a\x88\xb8\xc83\xb2\x81\xb8\xa8') def test_b32decode_error(self): tests = [b'abc', b'ABCDEF==', b'==ABCDEF'] @@ -363,6 +693,19 @@ def test_b32decode_error(self): with self.assertRaises(binascii.Error): base64.b32decode(data.decode('ascii')) + @hypothesis.given( + payload=hypothesis.strategies.binary(), + casefold=hypothesis.strategies.booleans(), + map01=( + hypothesis.strategies.none() + | hypothesis.strategies.binary(min_size=1, max_size=1))) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True, None) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False, None) + def test_b32_encode_decode_round_trip(self, payload, casefold, map01): + encoded = base64.b32encode(payload) + decoded = base64.b32decode(encoded, casefold=casefold, map01=map01) + self.assertEqual(payload, decoded) + def test_b32hexencode(self): test_cases = [ # to_encode, expected @@ -377,11 +720,21 @@ def test_b32hexencode(self): for to_encode, expected in test_cases: with self.subTest(to_decode=to_encode): self.assertEqual(base64.b32hexencode(to_encode), expected) + self.assertEqual(base64.b32hexencode(to_encode, padded=False), + expected.rstrip(b'=')) def test_b32hexencode_other_types(self): self.check_other_types(base64.b32hexencode, b'abcd', b'C5H66P0=') self.check_encode_type_errors(base64.b32hexencode) + def test_b32hexencode_wrapcol(self): + eq = self.assertEqual + b = b'www.python.org' + self._common_test_wrapcol(base64.b32hexencode, b) + eq(base64.b32hexencode(b, wrapcol=16), b'ETRNEBJGF5Q6GRRE\n5PNN4PO=') + eq(base64.b32hexencode(b, wrapcol=23), b'ETRNEBJGF5Q6GRRE\n5PNN4PO=') + eq(base64.b32hexencode(b, wrapcol=1), b'ETRNEBJG\nF5Q6GRRE\n5PNN4PO=') + def test_b32hexdecode(self): test_cases = [ # to_decode, expected, casefold @@ -416,6 +769,53 @@ def test_b32hexdecode_other_types(self): self.check_other_types(base64.b32hexdecode, b'C5H66===', b'abc') self.check_decode_type_errors(base64.b32hexdecode) + def test_b32hexdecode_padded(self): + b32hexdecode = base64.b32hexdecode + def check(data, expected): + if b'=' in data: + with self.assertRaisesRegex(binascii.Error, 'Padding not allowed'): + b32hexdecode(data, padded=False) + self.assertEqual(b32hexdecode(data, padded=False, ignorechars=b'='), + expected) + data = data.replace(b'=', b'') + self.assertEqual(b32hexdecode(data, padded=False), expected) + + check(b'', b'') + check(b'C4======', b'a') + check(b'C5H0====', b'ab') + check(b'C5H66===', b'abc') + check(b'C5H66P0=', b'abcd') + check(b'C5H66P35', b'abcde') + check(b'C=5H66P35', b'abcde') + check(b'C5=H66P35', b'abcde') + check(b'C5H=66P35', b'abcde') + check(b'C5H6=6P35', b'abcde') + check(b'C5H66=P35', b'abcde') + check(b'C5H66P=35', b'abcde') + check(b'C5H66P3=5', b'abcde') + + def test_b32hexdecode_ignorechars(self): + self._common_test_ignorechars(base64.b32hexdecode) + eq = self.assertEqual + eq(base64.b32hexdecode(b'C5H6\n 6P35\n', ignorechars=b' \n'), b'abcde') + eq(base64.b32hexdecode(b'C5H6\n 6P35\n', ignorechars=bytearray(b' \n')), b'abcde') + eq(base64.b32hexdecode(b'========C5H66P35', ignorechars=b'='), b'abcde') + eq(base64.b32hexdecode(b'C=======5H66P35', ignorechars=b'='), b'abcde') + eq(base64.b32hexdecode(b'C5======H66P35', ignorechars=b'='), b'abcde') + eq(base64.b32hexdecode(b'C5H=====66P35', ignorechars=b'='), b'abcde') + eq(base64.b32hexdecode(b'C5H6====6P35', ignorechars=b'='), b'abcde') + eq(base64.b32hexdecode(b'C5H66===P35', ignorechars=b'='), b'abcde') + eq(base64.b32hexdecode(b'C5H66P==35', ignorechars=b'='), b'abcde') + eq(base64.b32hexdecode(b'C5H66P3=5', ignorechars=b'='), b'abcde') + eq(base64.b32hexdecode(b'C5H66P35=', ignorechars=b'='), b'abcde') + eq(base64.b32hexdecode(b'C5H0======', ignorechars=b'='), b'ab') + + eq(base64.b32hexdecode(b'c5h66p35cpjmg===', ignorechars=b'cghjmp'), + b')\x8c2') + eq(base64.b32hexdecode(b'c5h66p35cpjmg===', casefold=True, + ignorechars=b'cghjmp'), + b'abcdefgh') + def test_b32hexdecode_error(self): tests = [b'abc', b'ABCDEF==', b'==ABCDEF', b'c4======'] prefixes = [b'M', b'ME', b'MFRA', b'MFRGG', b'MFRGGZA', b'MFRGGZDF'] @@ -432,6 +832,15 @@ def test_b32hexdecode_error(self): with self.assertRaises(binascii.Error): base64.b32hexdecode(data.decode('ascii')) + @hypothesis.given( + payload=hypothesis.strategies.binary(), + casefold=hypothesis.strategies.booleans()) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False) + def test_b32_hexencode_decode_round_trip(self, payload, casefold): + encoded = base64.b32hexencode(payload) + decoded = base64.b32hexdecode(encoded, casefold=casefold) + self.assertEqual(payload, decoded) def test_b16encode(self): eq = self.assertEqual @@ -442,6 +851,14 @@ def test_b16encode(self): b'0102ABCDEF') self.check_encode_type_errors(base64.b16encode) + def test_b16encode_wrapcol(self): + eq = self.assertEqual + b = b'\x01\x02\xab\xcd\xef' + self._common_test_wrapcol(base64.b16encode, b) + eq(base64.b16encode(b, wrapcol=4), b'0102\nABCD\nEF') + eq(base64.b16encode(b, wrapcol=5), b'0102\nABCD\nEF') + eq(base64.b16encode(b, wrapcol=1), b'01\n02\nAB\nCD\nEF') + def test_b16decode(self): eq = self.assertEqual eq(base64.b16decode(b'0102ABCDEF'), b'\x01\x02\xab\xcd\xef') @@ -469,6 +886,24 @@ def test_b16decode(self): # Incorrect "padding" self.assertRaises(binascii.Error, base64.b16decode, '010') + def test_b16decode_ignorechars(self): + self._common_test_ignorechars(base64.b16decode) + eq = self.assertEqual + eq(base64.b16decode(b'A B\nC D\n', ignorechars=b' \n'), b'\xab\xcd') + eq(base64.b16decode(b'A B\nC D\n', ignorechars=bytearray(b' \n')), b'\xab\xcd') + eq(base64.b16decode(b'aBcD', ignorechars=b'ac'), b'\xbd') + eq(base64.b16decode(b'aBcD', casefold=True, ignorechars=b'ac'), b'\xab\xcd') + + @hypothesis.given( + payload=hypothesis.strategies.binary(), + casefold=hypothesis.strategies.booleans()) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False) + def test_b16_encode_decode_round_trip(self, payload, casefold): + endoded = base64.b16encode(payload) + decoded = base64.b16decode(endoded, casefold=casefold) + self.assertEqual(payload, decoded) + def test_a85encode(self): eq = self.assertEqual @@ -507,18 +942,33 @@ def test_a85encode(self): self.assertRaises(TypeError, base64.a85encode, "") - eq(base64.a85encode(b"www.python.org", wrapcol=7, adobe=False), - b'GB\\6`E-\nZP=Df.1\nGEb>') - eq(base64.a85encode(b"\0\0\0\0www.python.org", wrapcol=7, adobe=False), - b'zGB\\6`E\n-ZP=Df.\n1GEb>') - eq(base64.a85encode(b"www.python.org", wrapcol=7, adobe=True), - b'<~GB\\6`\nE-ZP=Df\n.1GEb>\n~>') - eq(base64.a85encode(b' '*8, foldspaces=True, adobe=False), b'yy') eq(base64.a85encode(b' '*7, foldspaces=True, adobe=False), b'y+') + eq(base64.a85encode(b"\0\0\0\0www.python.org", wrapcol=7), + b'zGB\\6`E\n-ZP=Df.\n1GEb>') + eq(base64.a85encode(b, wrapcol=75), b'GB\\6`E-ZP=Df.1GEb>') + eq(base64.a85encode(b, wrapcol=1), + b'G\nB\n\\\n6\n`\nE\n-\nZ\nP\n=\nD\nf\n.\n1\nG\nE\nb\n>') + eq(base64.a85encode(b, wrapcol=7, adobe=True), + b'<~GB\\6`\nE-ZP=Df\n.1GEb>\n~>') + eq(base64.a85encode(b, wrapcol=1), + b'G\nB\n\\\n6\n`\nE\n-\nZ\nP\n=\nD\nf\n.\n1\nG\nE\nb\n>') + eq(base64.a85encode(b, wrapcol=1, adobe=True), + b'<~\nGB\n\\6\n`E\n-Z\nP=\nDf\n.1\nGE\nb>\n~>') + with self.assertRaises(ValueError): + base64.a85encode(b, wrapcol=-7, adobe=True) + eq(base64.a85encode(b'', wrapcol=1, adobe=True), b'<~\n~>') + eq(base64.a85encode(b'', wrapcol=3, adobe=True), b'<~\n~>') + eq(base64.a85encode(b'', wrapcol=4, adobe=True), b'<~~>') + def test_b85encode(self): eq = self.assertEqual @@ -553,11 +1003,20 @@ def test_b85encode(self): self.check_other_types(base64.b85encode, b"www.python.org", b'cXxL#aCvlSZ*DGca%T') + def test_b85encode_wrapcol(self): + eq = self.assertEqual + b = b'www.python.org' + self._common_test_wrapcol(base64.b85encode, b) + eq(base64.b85encode(b, wrapcol=10), b'cXxL#aCvlS\nZ*DGca%T') + eq(base64.b85encode(b, wrapcol=14), b'cXxL#aCvlS\nZ*DGca%T') + eq(base64.b85encode(b, wrapcol=1), b'cXxL#\naCvlS\nZ*DGc\na%T') + def test_z85encode(self): eq = self.assertEqual tests = { b'': b'', + b'\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B': b'HelloWorld', b'www.python.org': b'CxXl-AcVLsz/dgCA+t', bytes(range(255)): b"""009c61o!#m2NH?C3>iWS5d]J*6CRx17-skh9337x""" b"""ar.{NbQB=+c[cR@eg&FcfFLssg=mfIi5%2YjuU>)kTv.7l}6Nnnj=AD""" @@ -587,6 +1046,14 @@ def test_z85encode(self): self.check_other_types(base64.z85encode, b"www.python.org", b'CxXl-AcVLsz/dgCA+t') + def test_z85encode_wrapcol(self): + eq = self.assertEqual + b = b'www.python.org' + self._common_test_wrapcol(base64.z85encode, b) + eq(base64.z85encode(b, wrapcol=10), b'CxXl-AcVLs\nz/dgCA+t') + eq(base64.z85encode(b, wrapcol=14), b'CxXl-AcVLs\nz/dgCA+t') + eq(base64.z85encode(b, wrapcol=1), b'CxXl-\nAcVLs\nz/dgC\nA+t') + def test_a85decode(self): eq = self.assertEqual @@ -718,6 +1185,12 @@ def test_a85_padding(self): eq(base64.a85decode(b'G^+IX'), b"xxxx") eq(base64.a85decode(b'G^+IXGQ7^D'), b"xxxxx\x00\x00\x00") + eq(base64.a85encode(b"\x00", pad=True), b'z') + eq(base64.a85encode(b"\x00"*2, pad=True), b'z') + eq(base64.a85encode(b"\x00"*3, pad=True), b'z') + eq(base64.a85encode(b"\x00"*4, pad=True), b'z') + eq(base64.a85encode(b"\x00"*5, pad=True), b'zz') + def test_b85_padding(self): eq = self.assertEqual @@ -733,6 +1206,21 @@ def test_b85_padding(self): eq(base64.b85decode(b'czAet'), b"xxxx") eq(base64.b85decode(b'czAetcmMzZ'), b"xxxxx\x00\x00\x00") + def test_z85_padding(self): + eq = self.assertEqual + + eq(base64.z85encode(b"x", pad=True), b'CMmZz') + eq(base64.z85encode(b"xx", pad=True), b'CZ6h*') + eq(base64.z85encode(b"xxx", pad=True), b'CZaDk') + eq(base64.z85encode(b"xxxx", pad=True), b'CZaET') + eq(base64.z85encode(b"xxxxx", pad=True), b'CZaETCMmZz') + + eq(base64.z85decode(b'CMmZz'), b"x\x00\x00\x00") + eq(base64.z85decode(b'CZ6h*'), b"xx\x00\x00") + eq(base64.z85decode(b'CZaDk'), b"xxx\x00") + eq(base64.z85decode(b'CZaET'), b"xxxx") + eq(base64.z85decode(b'CZaETCMmZz'), b"xxxxx\x00\x00\x00") + def test_a85decode_errors(self): illegal = (set(range(32)) | set(range(118, 256))) - set(b' \t\n\r\v') for c in illegal: @@ -770,13 +1258,22 @@ def test_a85decode_errors(self): self.assertRaises(ValueError, base64.a85decode, b'aaaay', foldspaces=True) + self.assertEqual(base64.a85decode(b"a b\nc", ignorechars=b" \n"), + b'\xc9\x89') + self._common_test_ignorechars(base64.a85decode) + def test_b85decode_errors(self): illegal = list(range(33)) + \ list(b'"\',./:[\\]') + \ list(range(128, 256)) for c in illegal: - with self.assertRaises(ValueError, msg=bytes([c])): - base64.b85decode(b'0000' + bytes([c])) + b = bytes([c]) + with self.assertRaises(ValueError, msg=b): + base64.b85decode(b'0000' + b) + self.assertEqual(base64.b85decode(b'0000' + b, ignorechars=b), + b'\x00\x00\x00') + + self._common_test_ignorechars(base64.b85decode) self.assertRaises(ValueError, base64.b85decode, b'|') self.assertRaises(ValueError, base64.b85decode, b'|N') @@ -789,8 +1286,13 @@ def test_z85decode_errors(self): list(b'"\',;_`|\\~') + \ list(range(128, 256)) for c in illegal: - with self.assertRaises(ValueError, msg=bytes([c])): - base64.z85decode(b'0000' + bytes([c])) + b = bytes([c]) + with self.assertRaises(ValueError, msg=b): + base64.z85decode(b'0000' + b) + self.assertEqual(base64.z85decode(b'0000' + b, ignorechars=b), + b'\x00\x00\x00') + + self._common_test_ignorechars(base64.z85decode) # b'\xff\xff\xff\xff' encodes to b'%nSc0', the following will overflow: self.assertRaises(ValueError, base64.z85decode, b'%') @@ -799,6 +1301,61 @@ def test_z85decode_errors(self): self.assertRaises(ValueError, base64.z85decode, b'%nSc') self.assertRaises(ValueError, base64.z85decode, b'%nSc1') + def add_padding(self, payload): + """Add the expected padding for test_?85_encode_decode_round_trip.""" + if len(payload) % 4 != 0: + padding = b"\0" * ((-len(payload)) % 4) + payload = payload + padding + return payload + + @hypothesis.given( + payload=hypothesis.strategies.binary(), + foldspaces=hypothesis.strategies.booleans(), + wrapcol=( + hypothesis.strategies.just(0) + | hypothesis.strategies.integers(1, 1000)), + pad=hypothesis.strategies.booleans(), + adobe=hypothesis.strategies.booleans(), + ) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False, 0, False, False) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False, 20, True, True) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True, 0, False, True) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True, 20, True, False) + def test_a85_encode_decode_round_trip( + self, payload, foldspaces, wrapcol, pad, adobe + ): + encoded = base64.a85encode( + payload, foldspaces=foldspaces, wrapcol=wrapcol, + pad=pad, adobe=adobe, + ) + if wrapcol: + if adobe and wrapcol == 1: + # "adobe" needs wrapcol to be at least 2. + # a85decode quietly uses 2 if 1 is given; it's not worth + # loudly deprecating this behavior. + wrapcol = 2 + for line in encoded.splitlines(keepends=False): + self.assertLessEqual(len(line), wrapcol) + if adobe: + self.assertTrue(encoded.startswith(b'<~')) + self.assertTrue(encoded.endswith(b'~>')) + decoded = base64.a85decode(encoded, foldspaces=foldspaces, adobe=adobe) + if pad: + payload = self.add_padding(payload) + self.assertEqual(payload, decoded) + + @hypothesis.given( + payload=hypothesis.strategies.binary(), + pad=hypothesis.strategies.booleans()) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', True) + @hypothesis.example(b'abcdefghijklmnopqrstuvwxyz', False) + def test_b85_encode_decode_round_trip(self, payload, pad): + encoded = base64.b85encode(payload, pad=pad) + if pad: + payload = self.add_padding(payload) + decoded = base64.b85decode(encoded) + self.assertEqual(payload, decoded) + def test_decode_nonascii_str(self): decode_funcs = (base64.b64decode, base64.standard_b64decode, diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 7ed7d7c47b6de1..cedbdc61f18f34 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -4,14 +4,17 @@ import binascii import array import re -from test.support import bigmemtest, _1G, _4G +import sys +from test.support import bigmemtest, _1G, _4G, check_impl_detail from test.support.hypothesis_helper import hypothesis # Note: "*_hex" functions are aliases for "(un)hexlify" -b2a_functions = ['b2a_base64', 'b2a_hex', 'b2a_qp', 'b2a_uu', +b2a_functions = ['b2a_ascii85', 'b2a_base32', 'b2a_base64', 'b2a_base85', + 'b2a_hex', 'b2a_qp', 'b2a_uu', 'hexlify'] -a2b_functions = ['a2b_base64', 'a2b_hex', 'a2b_qp', 'a2b_uu', +a2b_functions = ['a2b_ascii85', 'a2b_base32', 'a2b_base64', 'a2b_base85', + 'a2b_hex', 'a2b_qp', 'a2b_uu', 'unhexlify'] all_functions = a2b_functions + b2a_functions + ['crc32', 'crc_hqx'] @@ -23,6 +26,8 @@ class BinASCIITest(unittest.TestCase): rawdata = b"The quick brown fox jumps over the lazy dog.\r\n" # Be slow so we don't depend on other modules rawdata += bytes(range(256)) + rawdata += b'\0'*32 + rawdata += b' '*32 rawdata += b"\r\nHello world.\n" def setUp(self): @@ -41,6 +46,40 @@ def test_exceptions(self): self.assertIsSubclass(binascii.Error, Exception) self.assertIsSubclass(binascii.Incomplete, Exception) + def test_constants(self): + self.assertEqual(binascii.BASE64_ALPHABET, + b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + b'abcdefghijklmnopqrstuvwxyz' + b'0123456789+/') + self.assertEqual(binascii.URLSAFE_BASE64_ALPHABET, + binascii.BASE64_ALPHABET[:-2] + b'-_') + self.assertEqual(binascii.UU_ALPHABET, bytes(range(32, 32+64))) + self.assertEqual(binascii.CRYPT_ALPHABET, + b'./0123456789' + b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + b'abcdefghijklmnopqrstuvwxyz') + self.assertEqual(binascii.BINHEX_ALPHABET, + b'!"#$%&\'()*+,-012345689' + b'@ABCDEFGHIJKLMNPQRSTUVXYZ[' + b'`abcdefhijklmpqr') + + self.assertEqual(binascii.BASE85_ALPHABET, + b'0123456789' + b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + b'abcdefghijklmnopqrstuvwxyz' + b'!#$%&()*+-;<=>?@^_`{|}~') + self.assertEqual(binascii.ASCII85_ALPHABET, bytes(range(33, 33+85))) + self.assertEqual(binascii.Z85_ALPHABET, + b'0123456789' + b'abcdefghijklmnopqrstuvwxyz' + b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + b'.-:+=^!/*?&<>()[]{}@%$#') + + self.assertEqual(binascii.BASE32_ALPHABET, + b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') + self.assertEqual(binascii.BASE32HEX_ALPHABET, + b'0123456789ABCDEFGHIJKLMNOPQRSTUV') + def test_functions(self): # Check presence of all functions for name in all_functions: @@ -117,70 +156,200 @@ def addnoise(line): # empty strings. TBD: shouldn't it raise an exception instead ? self.assertEqual(binascii.a2b_base64(self.type2test(fillers)), b'') - def test_base64_strict_mode(self): - # Test base64 with strict mode on - def _assertRegexTemplate(assert_regex: str, data: bytes, non_strict_mode_expected_result: bytes): + def test_base64_bad_padding(self): + # Test malformed padding + def _assertRegexTemplate(assert_regex, data, + non_strict_mode_expected_result, **kwargs): + data = self.type2test(data) with self.assertRaisesRegex(binascii.Error, assert_regex): - binascii.a2b_base64(self.type2test(data), strict_mode=True) - self.assertEqual(binascii.a2b_base64(self.type2test(data), strict_mode=False), + binascii.a2b_base64(data, strict_mode=True, **kwargs) + self.assertEqual(binascii.a2b_base64(data, strict_mode=False), non_strict_mode_expected_result) - self.assertEqual(binascii.a2b_base64(self.type2test(data)), + self.assertEqual(binascii.a2b_base64(data, strict_mode=True, + ignorechars=b' ='), + non_strict_mode_expected_result) + self.assertEqual(binascii.a2b_base64(data), non_strict_mode_expected_result) - def assertExcessData(data, non_strict_mode_expected_result: bytes): - _assertRegexTemplate(r'(?i)Excess data', data, non_strict_mode_expected_result) - - def assertNonBase64Data(data, non_strict_mode_expected_result: bytes): - _assertRegexTemplate(r'(?i)Only base64 data', data, non_strict_mode_expected_result) - - def assertLeadingPadding(data, non_strict_mode_expected_result: bytes): - _assertRegexTemplate(r'(?i)Leading padding', data, non_strict_mode_expected_result) + def assertLeadingPadding(*args, **kwargs): + _assertRegexTemplate(r'(?i)Leading padding', *args, **kwargs) - def assertDiscontinuousPadding(data, non_strict_mode_expected_result: bytes): - _assertRegexTemplate(r'(?i)Discontinuous padding', data, non_strict_mode_expected_result) + def assertDiscontinuousPadding(*args): + _assertRegexTemplate(r'(?i)Discontinuous padding', *args) - def assertExcessPadding(data, non_strict_mode_expected_result: bytes): - _assertRegexTemplate(r'(?i)Excess padding', data, non_strict_mode_expected_result) + def assertExcessPadding(*args): + _assertRegexTemplate(r'(?i)Excess padding', *args) - # Test excess data exceptions - assertExcessData(b'ab==a', b'i') - assertExcessData(b'ab===', b'i') - assertExcessData(b'ab====', b'i') - assertExcessData(b'ab==:', b'i') - assertExcessData(b'abc=a', b'i\xb7') - assertExcessData(b'abc=:', b'i\xb7') - assertExcessData(b'ab==\n', b'i') - assertExcessData(b'abc==', b'i\xb7') - assertExcessData(b'abc===', b'i\xb7') - assertExcessData(b'abc====', b'i\xb7') - assertExcessData(b'abc=====', b'i\xb7') + def assertInvalidLength(data, *args, length=None, **kwargs): + if length is None: + length = len(data.split(b'=', 1)[0].replace(b' ', b'')) + assert_regex = fr"(?i)Invalid.+number of data characters \({length}\)" + _assertRegexTemplate(assert_regex, data, *args, **kwargs) - # Test non-base64 data exceptions - assertNonBase64Data(b'\nab==', b'i') - assertNonBase64Data(b'ab:(){:|:&};:==', b'i') - assertNonBase64Data(b'a\nb==', b'i') - assertNonBase64Data(b'a\x00b==', b'i') + assertExcessPadding(b'ab===', b'i') + assertExcessPadding(b'ab====', b'i') + assertExcessPadding(b'abc==', b'i\xb7') + assertExcessPadding(b'abc===', b'i\xb7') + assertExcessPadding(b'abc====', b'i\xb7') + assertExcessPadding(b'abc=====', b'i\xb7') - # Test malformed padding assertLeadingPadding(b'=', b'') assertLeadingPadding(b'==', b'') assertLeadingPadding(b'===', b'') assertLeadingPadding(b'====', b'') assertLeadingPadding(b'=====', b'') + assertLeadingPadding(b'=abcd', b'i\xb7\x1d') + assertLeadingPadding(b'==abcd', b'i\xb7\x1d') + assertLeadingPadding(b'===abcd', b'i\xb7\x1d') + assertLeadingPadding(b'====abcd', b'i\xb7\x1d') + assertLeadingPadding(b'=====abcd', b'i\xb7\x1d') + assertLeadingPadding(b' =abcd', b'i\xb7\x1d', ignorechars=b' ') + + assertInvalidLength(b'a=b==', b'i') + assertInvalidLength(b'a=bc=', b'i\xb7') + assertInvalidLength(b'a=bc==', b'i\xb7') + assertInvalidLength(b'a=bcd', b'i\xb7\x1d') + assertInvalidLength(b'a=bcd=', b'i\xb7\x1d') + assertInvalidLength(b' a=b==', b'i', ignorechars=b' ') + assertInvalidLength(b'abcde=f==', b'i\xb7\x1dy') + assertInvalidLength(b' abcde=f==', b'i\xb7\x1dy', ignorechars=b' ') + assertDiscontinuousPadding(b'ab=c=', b'i\xb7') - assertDiscontinuousPadding(b'ab=ab==', b'i\xb6\x9b') + assertDiscontinuousPadding(b'ab=cd', b'i\xb7\x1d') + assertDiscontinuousPadding(b'ab=cd==', b'i\xb7\x1d') + assertExcessPadding(b'abcd=', b'i\xb7\x1d') assertExcessPadding(b'abcd==', b'i\xb7\x1d') assertExcessPadding(b'abcd===', b'i\xb7\x1d') assertExcessPadding(b'abcd====', b'i\xb7\x1d') assertExcessPadding(b'abcd=====', b'i\xb7\x1d') + assertExcessPadding(b'abcd==', b'i\xb7\x1d') + assertExcessPadding(b'abcd===', b'i\xb7\x1d') + assertExcessPadding(b'abcd====', b'i\xb7\x1d') + assertExcessPadding(b'abcd=====', b'i\xb7\x1d') + assertExcessPadding(b'abcd=efgh', b'i\xb7\x1dy\xf8!') + assertExcessPadding(b'abcd==efgh', b'i\xb7\x1dy\xf8!') + assertExcessPadding(b'abcd===efgh', b'i\xb7\x1dy\xf8!') + assertExcessPadding(b'abcd====efgh', b'i\xb7\x1dy\xf8!') + assertExcessPadding(b'abcd=====efgh', b'i\xb7\x1dy\xf8!') + + def test_a2b_base64_padded(self): + a2b_base64 = binascii.a2b_base64 + t = self.type2test + def check(data, expected): + if b'=' in data: + with self.assertRaisesRegex(binascii.Error, 'Padding not allowed'): + a2b_base64(t(data), padded=False, strict_mode=True) + self.assertEqual(a2b_base64(t(data), padded=False, ignorechars=b'='), + expected) + data = data.replace(b'=', b'') + self.assertEqual(a2b_base64(t(data), padded=False), expected) + self.assertEqual(a2b_base64(t(data), padded=False, strict_mode=True), + expected) + + check(b'', b'') + check(b'YQ==', b'a') + check(b'YWI=', b'ab') + check(b'YWJj', b'abc') + check(b'Y=WJj', b'abc') + check(b'YW=Jj', b'abc') + check(b'YWJ=j', b'abc') + + def _common_test_ignorechars(self, func): + eq = self.assertEqual + empty = self.type2test(b'') + data = self.type2test(b'\n \n') + ignorechars = self.type2test(b' \n') + eq(func(empty, ignorechars=ignorechars), b'') + eq(func(empty, ignorechars=empty), b'') + eq(func(data, ignorechars=ignorechars), b'') + with self.assertRaises(binascii.Error): + func(data, ignorechars=empty) + with self.assertRaises(binascii.Error): + func(data, ignorechars=ignorechars[1:]) + with self.assertRaises(binascii.Error): + func(data, ignorechars=ignorechars[:-1]) + with self.assertRaises(TypeError): + func(empty, ignorechars='') + with self.assertRaises(TypeError): + func(empty, ignorechars=[]) + with self.assertRaises(TypeError): + func(empty, ignorechars=None) + def test_base64_invalidchars(self): + # Test non-base64 data exceptions + self._common_test_ignorechars(binascii.a2b_base64) + def assertNonBase64Data(data, expected, ignorechars): + data = self.type2test(data) + assert_regex = r'(?i)Only base64 data' + self.assertEqual(binascii.a2b_base64(data), expected) + with self.assertRaisesRegex(binascii.Error, assert_regex): + binascii.a2b_base64(data, strict_mode=True) + with self.assertRaisesRegex(binascii.Error, assert_regex): + binascii.a2b_base64(data, ignorechars=b'') + self.assertEqual(binascii.a2b_base64(data, ignorechars=ignorechars), + expected) + self.assertEqual(binascii.a2b_base64(data, strict_mode=False, ignorechars=b''), + expected) + + assertNonBase64Data(b'\nab==', b'i', ignorechars=b'\n') + assertNonBase64Data(b'ab:(){:|:&};:==', b'i', ignorechars=b':;(){}|&') + assertNonBase64Data(b'a\nb==', b'i', ignorechars=b'\n') + assertNonBase64Data(b'a\x00b==', b'i', ignorechars=b'\x00') + assertNonBase64Data(b'ab:==', b'i', ignorechars=b':') + assertNonBase64Data(b'ab=:=', b'i', ignorechars=b':') + assertNonBase64Data(b'ab==:', b'i', ignorechars=b':') + assertNonBase64Data(b'abc=:', b'i\xb7', ignorechars=b':') + assertNonBase64Data(b'ab==\n', b'i', ignorechars=b'\n') + assertNonBase64Data(b'a\nb==', b'i', ignorechars=bytearray(b'\n')) + assertNonBase64Data(b'a\nb==', b'i', ignorechars=memoryview(b'\n')) + + self.assertEqual(binascii.a2b_base64(b'+A-/B_', ignorechars=b'+/-_'), + b'\xf8\x0f\xc1') + self.assertEqual(binascii.a2b_base64(b'+A-/B_', ignorechars=b'+/-_', + alphabet=binascii.URLSAFE_BASE64_ALPHABET), + b'\x03\xe0\x7f') + + # Same cell in the cache: '\r' >> 3 == '\n' >> 3. + data = self.type2test(b'\r\n') + with self.assertRaises(binascii.Error): + binascii.a2b_base64(data, ignorechars=b'\r') + self.assertEqual(binascii.a2b_base64(data, ignorechars=b'\r\n'), b'') + # Same bit mask in the cache: '*' & 31 == '\n' & 31. + data = self.type2test(b'*\n') + with self.assertRaises(binascii.Error): + binascii.a2b_base64(data, ignorechars=b'*') + self.assertEqual(binascii.a2b_base64(data, ignorechars=b'*\n'), b'') + + def test_base64_excess_data(self): + # Test excess data exceptions + def assertExcessData(data, expected): + assert_regex = r'(?i)Excess data' + data = self.type2test(data) + with self.assertRaisesRegex(binascii.Error, assert_regex): + binascii.a2b_base64(data, strict_mode=True) + self.assertEqual(binascii.a2b_base64(data, strict_mode=False), + expected) + self.assertEqual(binascii.a2b_base64(data, strict_mode=True, + ignorechars=b'='), + expected) + self.assertEqual(binascii.a2b_base64(data), expected) + + assertExcessData(b'ab==c=', b'i\xb7') + assertExcessData(b'ab==cd', b'i\xb7\x1d') + assertExcessData(b'abc=d', b'i\xb7\x1d') def test_base64errors(self): # Test base64 with invalid padding - def assertIncorrectPadding(data): + def assertIncorrectPadding(data, strict_mode=True): + data = self.type2test(data) + with self.assertRaisesRegex(binascii.Error, r'(?i)Incorrect padding'): + binascii.a2b_base64(data) with self.assertRaisesRegex(binascii.Error, r'(?i)Incorrect padding'): - binascii.a2b_base64(self.type2test(data)) + binascii.a2b_base64(data, strict_mode=False) + if strict_mode: + with self.assertRaisesRegex(binascii.Error, r'(?i)Incorrect padding'): + binascii.a2b_base64(data, strict_mode=True) assertIncorrectPadding(b'ab') assertIncorrectPadding(b'ab=') @@ -188,16 +357,22 @@ def assertIncorrectPadding(data): assertIncorrectPadding(b'abcdef') assertIncorrectPadding(b'abcdef=') assertIncorrectPadding(b'abcdefg') - assertIncorrectPadding(b'a=b=') - assertIncorrectPadding(b'a\nb=') + assertIncorrectPadding(b'a=b=', strict_mode=False) + assertIncorrectPadding(b'a\nb=', strict_mode=False) # Test base64 with invalid number of valid characters (1 mod 4) - def assertInvalidLength(data): + def assertInvalidLength(data, strict_mode=True): n_data_chars = len(re.sub(br'[^A-Za-z0-9/+]', br'', data)) + data = self.type2test(data) expected_errmsg_re = \ r'(?i)Invalid.+number of data characters.+' + str(n_data_chars) with self.assertRaisesRegex(binascii.Error, expected_errmsg_re): - binascii.a2b_base64(self.type2test(data)) + binascii.a2b_base64(data) + with self.assertRaisesRegex(binascii.Error, expected_errmsg_re): + binascii.a2b_base64(data, strict_mode=False) + if strict_mode: + with self.assertRaisesRegex(binascii.Error, expected_errmsg_re): + binascii.a2b_base64(data, strict_mode=True) assertInvalidLength(b'a') assertInvalidLength(b'a=') @@ -205,7 +380,911 @@ def assertInvalidLength(data): assertInvalidLength(b'a===') assertInvalidLength(b'a' * 5) assertInvalidLength(b'a' * (4 * 87 + 1)) - assertInvalidLength(b'A\tB\nC ??DE') # only 5 valid characters + assertInvalidLength(b'A\tB\nC ??DE', # only 5 valid characters + strict_mode=False) + + def test_base64_canonical(self): + # https://datatracker.ietf.org/doc/html/rfc4648.html#section-3.5 + # Decoders MAY reject encoded data if the pad bits are not zero. + + # Without canonical=True, non-zero padding bits are accepted + self.assertEqual(binascii.a2b_base64(self.type2test(b'AB==')), b'\x00') + self.assertEqual(binascii.a2b_base64(self.type2test(b'AB=='), + strict_mode=True), b'\x00') + + # 2 data chars + "==": last char has 4 padding bits + # 'A' = 0, 'B' = 1 -> leftover 0001 (non-zero) + with self.assertRaises(binascii.Error): + binascii.a2b_base64(self.type2test(b'AB=='), canonical=True) + # 'A' = 0, 'P' = 15 -> leftover 1111 (non-zero) + with self.assertRaises(binascii.Error): + binascii.a2b_base64(self.type2test(b'AP=='), canonical=True) + + # 3 data chars + "=": last char has 2 padding bits + # 'A' = 0, 'A' = 0, 'B' = 1 -> leftover 01 (non-zero) + with self.assertRaises(binascii.Error): + binascii.a2b_base64(self.type2test(b'AAB='), canonical=True) + # 'A' = 0, 'A' = 0, 'D' = 3 -> leftover 11 (non-zero) + with self.assertRaises(binascii.Error): + binascii.a2b_base64(self.type2test(b'AAD='), canonical=True) + + # Verify that zero padding bits are accepted + binascii.a2b_base64(self.type2test(b'AA=='), canonical=True) + binascii.a2b_base64(self.type2test(b'AAA='), canonical=True) + + # Full quads with no padding have no leftover bits -- always valid + binascii.a2b_base64(self.type2test(b'AAAA'), canonical=True) + + @hypothesis.given(payload=hypothesis.strategies.binary()) + @hypothesis.example(b'') + @hypothesis.example(b'\x00') + @hypothesis.example(b'\xff\xff') + @hypothesis.example(b'abc') + def test_base64_canonical_roundtrip(self, payload): + # The encoder must always produce canonical output. + encoded = binascii.b2a_base64(payload, newline=False) + decoded = binascii.a2b_base64(encoded, canonical=True) + self.assertEqual(decoded, payload) + + def test_base64_alphabet(self): + alphabet = (b'!"#$%&\'()*+,-012345689@' + b'ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr') + data = self.type2test(self.rawdata) + encoded = binascii.b2a_base64(data, alphabet=alphabet) + trans = bytes.maketrans(binascii.BASE64_ALPHABET, alphabet) + expected = binascii.b2a_base64(data).translate(trans) + self.assertEqual(encoded, expected) + self.assertEqual(binascii.a2b_base64(encoded, alphabet=alphabet), self.rawdata) + self.assertEqual(binascii.b2a_base64(data, alphabet=self.type2test(alphabet)), expected) + + data = self.type2test(b'') + self.assertEqual(binascii.b2a_base64(data, alphabet=alphabet), b'\n') + self.assertEqual(binascii.a2b_base64(data, alphabet=alphabet), b'') + + for func in binascii.b2a_base64, binascii.a2b_base64: + with self.assertRaises(TypeError): + func(data, alphabet=None) + with self.assertRaises(TypeError): + func(data, alphabet=alphabet.decode()) + with self.assertRaises(ValueError): + func(data, alphabet=alphabet[:-1]) + with self.assertRaises(ValueError): + func(data, alphabet=alphabet+b'?') + with self.assertRaises(TypeError): + binascii.a2b_base64(data, alphabet=bytearray(alphabet)) + + def test_ascii85_valid(self): + # Test Ascii85 with valid data + ASCII85_PREFIX = b"<~" + ASCII85_SUFFIX = b"~>" + + # Interleave blocks of 4 null bytes and 4 spaces into test data + rawdata = bytearray() + rawlines, i = [], 0 + for k in range(1, len(self.rawdata) + 1): + b = b"\0\0\0\0" if k & 1 else b" " + b = b + self.rawdata[i:i + k] + b = b" " if k & 1 else b"\0\0\0\0" + rawdata += b + rawlines.append(b) + i += k + if i >= len(self.rawdata): + break + + # Test core parameter combinations + params = (False, False), (False, True), (True, False), (True, True) + for foldspaces, adobe in params: + lines = [] + for rawline in rawlines: + b = self.type2test(rawline) + a = binascii.b2a_ascii85(b, foldspaces=foldspaces, adobe=adobe) + lines.append(a) + res = bytearray() + for line in lines: + a = self.type2test(line) + b = binascii.a2b_ascii85(a, foldspaces=foldspaces, adobe=adobe) + res += b + self.assertEqual(res, rawdata) + + # Inputs with length 1 mod 5 end with a 1-char group, which is + # an encoding violation per the PLRM spec. + error_params = [ + (b"a", False, False, b""), + (b"xbw", False, False, b"wx"), + (b"<~c~>", False, True, b""), + (b"{d ~>", False, True, b" {"), + (b"ye", True, False, b""), + (b"z\x01y\x00f", True, False, b"\x00\x01"), + (b"<~FCfN8yg~>", True, True, b""), + (b"FE;\x03#8zFCf\x02N8yh~>", True, True, b"\x02\x03"), + ] + for a, foldspaces, adobe, ignorechars in error_params: + kwargs = {"foldspaces": foldspaces, "adobe": adobe, "ignorechars": ignorechars} + with self.assertRaises(binascii.Error): + binascii.a2b_ascii85(self.type2test(a), **kwargs) + + def test_ascii85_invalid(self): + # Test Ascii85 with invalid characters interleaved + lines, i = [], 0 + for k in range(1, len(self.rawdata) + 1): + b = self.type2test(self.rawdata[i:i + k]) + a = binascii.b2a_ascii85(b) + lines.append(a) + i += k + if i >= len(self.rawdata): + break + + fillers = bytearray() + valid = b"!\"#$%&'()*+,-./0123456789:;<=>?@" \ + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu" + b"z" + for i in range(256): + if i not in valid: + fillers.append(i) + def addnoise(line): + res = bytearray() + for i in range(len(line)): + res.append(line[i]) + for j in range(i, len(fillers), len(line)): + res.append(fillers[j]) + return res + res = bytearray() + for line in map(addnoise, lines): + a = self.type2test(line) + b = binascii.a2b_ascii85(a, ignorechars=fillers) + res += b + self.assertEqual(res, self.rawdata) + + # Test Ascii85 with only invalid characters + fillers = self.type2test(fillers) + b = binascii.a2b_ascii85(fillers, ignorechars=fillers) + self.assertEqual(b, b"") + + def test_ascii85_errors(self): + def _assertRegexTemplate(assert_regex, data, **kwargs): + with self.assertRaisesRegex(binascii.Error, assert_regex): + binascii.a2b_ascii85(self.type2test(data), **kwargs) + + def assertMissingDelimiter(data): + _assertRegexTemplate(r"(?i)end with b'~>'", data, adobe=True) + + def assertOverflow(data): + _assertRegexTemplate(r"(?i)Ascii85 overflow", data) + + def assertInvalidSpecial(data): + _assertRegexTemplate(r"(?i)'[yz]'.+5-tuple", data, foldspaces=True) + + def assertInvalidChar(data, **kwargs): + _assertRegexTemplate(r"(?i)Non-Ascii85 digit", data, **kwargs) + + # Test Ascii85 with missing delimiters + assertMissingDelimiter(b"") + assertMissingDelimiter(b"a") + assertMissingDelimiter(b"<~") + assertMissingDelimiter(b"<~!~") + assertMissingDelimiter(b"<~abc>") + assertMissingDelimiter(b"<~has delimiter but not terminal~> !") + + # Test Ascii85 with out-of-range encoded value + assertOverflow(b"t") + assertOverflow(b"s9") + assertOverflow(b"s8X") + assertOverflow(b"s8W.") + assertOverflow(b's8W-"') + assertOverflow(b"s8W-!u") + assertOverflow(b"s8W-!s8W-!zs8X") + + # Test Ascii85 with misplaced short form groups + assertInvalidSpecial(b"ay") + assertInvalidSpecial(b"az") + assertInvalidSpecial(b"aby") + assertInvalidSpecial(b"ayz") + assertInvalidSpecial(b"abcz") + assertInvalidSpecial(b"abcdy") + assertInvalidSpecial(b"y!and!z!then!!y") + + # Test Ascii85 with non-ignored invalid characters + assertInvalidChar(b"j\n") + assertInvalidChar(b" ", ignorechars=b"") + assertInvalidChar(b" valid\x02until\x03", ignorechars=b"\x00\x01\x02\x04") + assertInvalidChar(b"\tFCb", ignorechars=b"\n") + assertInvalidChar(b"xxxB\nP\thU'D v/F+", ignorechars=b" \n\tv") + + def _common_test_wrapcol(self, func): + eq = self.assertEqual + data = self.data + expected = func(data) + eq(func(data, wrapcol=0), expected) + eq(func(data, wrapcol=8000), expected) + eq(func(b'', wrapcol=0), func(b'')) + eq(func(b'', wrapcol=1), func(b'')) + eq(func(data, wrapcol=sys.maxsize), expected) + if check_impl_detail(): + eq(func(data, wrapcol=sys.maxsize*2), expected) + with self.assertRaises(OverflowError): + func(data, wrapcol=2**1000) + with self.assertRaises(ValueError): + func(data, wrapcol=-80) + with self.assertRaises(TypeError): + func(data, wrapcol=80.0) + with self.assertRaises(TypeError): + func(data, wrapcol='80') + with self.assertRaises(TypeError): + func(data, wrapcol=None) + + def test_ascii85_wrapcol(self): + # Test Ascii85 splitting lines + self._common_test_wrapcol(binascii.b2a_ascii85) + + def assertEncode(a_expected, data, n, adobe=False): + b = self.type2test(data) + a = binascii.b2a_ascii85(b, adobe=adobe, wrapcol=n) + self.assertEqual(a, a_expected) + + def assertDecode(data, b_expected, adobe=False): + a = self.type2test(data) + b = binascii.a2b_ascii85(a, adobe=adobe, ignorechars=b"\n") + self.assertEqual(b, b_expected) + + tests = [ + (b"", 0, b"", b"<~~>"), + (b"", 1, b"", b"<~\n~>"), + (b"a", 0, b"@/", b"<~@/~>"), + (b"a", 1, b"@\n/", b"<~\n@/\n~>"), + (b"a", 2, b"@/", b"<~\n@/\n~>"), + (b"a", 3, b"@/", b"<~@\n/~>"), + (b"a", 4, b"@/", b"<~@/\n~>"), + (b"a", 5, b"@/", b"<~@/\n~>"), + (b"a", 6, b"@/", b"<~@/~>"), + (b"a", 7, b"@/", b"<~@/~>"), + (b"a", 123, b"@/", b"<~@/~>"), + (b"this is a test", 7, b"FD,B0+D\nGm>@3BZ\n'F*%", + b"<~FD,B0\n+DGm>@3\nBZ'F*%\n~>"), + (b"a test!!!!!!! ", 11, b"@3BZ'F*&QK+\nX&!P+WqmM+9", + b"<~@3BZ'F*&Q\nK+X&!P+WqmM\n+9~>"), + (b"\0" * 56, 7, b"zzzzzzz\nzzzzzzz", b"<~zzzzz\nzzzzzzz\nzz~>"), + ] + for b, n, a, a_wrap in tests: + assertEncode(a, b, n) + assertEncode(a_wrap, b, n, adobe=True) + assertDecode(a, b) + assertDecode(a_wrap, b, adobe=True) + + def test_ascii85_pad(self): + # Test Ascii85 with encode padding + rawdata = b"n1n3tee\n ch@rAcTer$" + for i in range(1, len(rawdata) + 1): + padding = -i % 4 + b = rawdata[:i] + a_pad = binascii.b2a_ascii85(self.type2test(b), pad=True) + b_pad = binascii.a2b_ascii85(self.type2test(a_pad)) + b_pad_expected = b + b"\0" * padding + self.assertEqual(b_pad, b_pad_expected) + + # Test Ascii85 short form groups with encode padding + def assertShortPad(data, expected, **kwargs): + data = self.type2test(data) + res = binascii.b2a_ascii85(data, **kwargs) + self.assertEqual(res, expected) + + assertShortPad(b"\0", b"!!", pad=False) + assertShortPad(b"\0", b"z", pad=True) + assertShortPad(b"\0" * 2, b"z", pad=True) + assertShortPad(b"\0" * 3, b"z", pad=True) + assertShortPad(b"\0" * 4, b"z", pad=True) + assertShortPad(b"\0" * 5, b"zz", pad=True) + assertShortPad(b"\0" * 6, b"z!!!") + assertShortPad(b" " * 7, b"y+", + foldspaces=True, adobe=True, pad=True) + assertShortPad(b"\0\0\0\0abcd \0\0", b"<~z@:E_Wy\nz~>", + foldspaces=True, adobe=True, wrapcol=9, pad=True) + + def test_ascii85_ignorechars(self): + # Test Ascii85 with ignored characters + def assertIgnore(data, expected, ignorechars=b"", **kwargs): + data = self.type2test(data) + ignore = self.type2test(ignorechars) + with self.assertRaisesRegex(binascii.Error, r"(?i)Non-Ascii85 digit"): + binascii.a2b_ascii85(data, **kwargs) + res = binascii.a2b_ascii85(data, ignorechars=ignorechars, **kwargs) + self.assertEqual(res, expected) + + assertIgnore(b"\n", b"", ignorechars=b"\n") + assertIgnore(b"<~ ~>", b"", ignorechars=b" ", adobe=True) + assertIgnore(b"z|z", b"\0" * 8, ignorechars=b"|||") # repeats don't matter + assertIgnore(b"zz!!|", b"\0" * 9, ignorechars=b"|!z") # ignore only if invalid + assertIgnore(b"<~B P~@~>", b"hi", ignorechars=b" <~>", adobe=True) + assertIgnore(b"zy}", b"\0\0\0\0", ignorechars=b"zy}") + assertIgnore(b"zy}", b"\0\0\0\0 ", ignorechars=b"zy}", foldspaces=True) + + def test_base85_valid(self): + # Test base85 with valid data + lines, i = [], 0 + for k in range(1, len(self.rawdata) + 1): + b = self.type2test(self.rawdata[i:i + k]) + a = binascii.b2a_base85(b) + lines.append(a) + i += k + if i >= len(self.rawdata): + break + res = bytes() + for line in lines: + a = self.type2test(line) + b = binascii.a2b_base85(a) + res += b + self.assertEqual(res, self.rawdata) + + # Test decoding inputs with different length + # 1-char groups are rejected (encoding violation) + with self.assertRaises(binascii.Error): + binascii.a2b_base85(self.type2test(b'a')) + self.assertEqual(binascii.a2b_base85(self.type2test(b'ab')), b'q') + self.assertEqual(binascii.a2b_base85(self.type2test(b'abc')), b'qa') + self.assertEqual(binascii.a2b_base85(self.type2test(b'abcd')), + b'qa\x9e') + self.assertEqual(binascii.a2b_base85(self.type2test(b'abcde')), + b'qa\x9e\xb6') + # 6-char input = full 5-char group + trailing 1-char group (rejected) + with self.assertRaises(binascii.Error): + binascii.a2b_base85(self.type2test(b'abcdef')) + self.assertEqual(binascii.a2b_base85(self.type2test(b'abcdefg')), + b'qa\x9e\xb6\x81') + + def test_base85_errors(self): + def _assertRegexTemplate(assert_regex, data, **kwargs): + with self.assertRaisesRegex(binascii.Error, assert_regex): + binascii.a2b_base85(self.type2test(data), **kwargs) + + def assertNonBase85Data(data): + _assertRegexTemplate(r"(?i)bad base85 character", data) + + def assertOverflow(data): + _assertRegexTemplate(r"(?i)base85 overflow", data) + + assertNonBase85Data(b"\xda") + assertNonBase85Data(b"00\0\0") + assertNonBase85Data(b"Z )*") + assertNonBase85Data(b"bY*jNb0Hyq\n") + + # Test base85 with out-of-range encoded value + assertOverflow(b"}") + assertOverflow(b"|O") + assertOverflow(b"|Nt") + assertOverflow(b"|NsD") + assertOverflow(b"|NsC1") + assertOverflow(b"|NsC0~") + assertOverflow(b"|NsC0|NsC0|NsD0") + + def test_base85_wrapcol(self): + self._common_test_wrapcol(binascii.b2a_base85) + b = self.type2test(b'www.python.org') + self.assertEqual(binascii.b2a_base85(b, wrapcol=10), + b'cXxL#aCvlS\nZ*DGca%T') + self.assertEqual(binascii.b2a_base85(b, wrapcol=14), + b'cXxL#aCvlS\nZ*DGca%T') + self.assertEqual(binascii.b2a_base85(b, wrapcol=1), + b'cXxL#\naCvlS\nZ*DGc\na%T') + + def test_base85_pad(self): + # Test base85 with encode padding + rawdata = b"n1n3Tee\n ch@rAc\te\r$" + for i in range(1, len(rawdata) + 1): + padding = -i % 4 + b = rawdata[:i] + a_pad = binascii.b2a_base85(self.type2test(b), pad=True) + b_pad = binascii.a2b_base85(self.type2test(a_pad)) + b_pad_expected = b + b"\0" * padding + self.assertEqual(b_pad, b_pad_expected) + + def test_base85_ignorechars(self): + a2b_base85 = binascii.a2b_base85 + self._common_test_ignorechars(a2b_base85) + eq = self.assertEqual + eq(a2b_base85(b'VPa\n !s\n', ignorechars=b' \n'), b'abcd') + eq(a2b_base85(b'VPa\n !s\n', ignorechars=bytearray(b' \n')), b'abcd') + + eq(a2b_base85(b'A~[B];C', ignorechars=b';[]~'), b'"1\xa3\x15') + eq(a2b_base85(b'A~[B];C', ignorechars=b';[]~', + alphabet=binascii.Z85_ALPHABET), b'r\xd8dv') + + def test_base85_alphabet(self): + alphabet = (b'0123456789abcdefghijklmnopqrstuvwxyz' + b'ABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#') + data = self.type2test(self.rawdata) + encoded = binascii.b2a_base85(data, alphabet=alphabet) + trans = bytes.maketrans(binascii.BASE85_ALPHABET, alphabet) + expected = binascii.b2a_base85(data).translate(trans) + self.assertEqual(encoded, expected) + self.assertEqual(binascii.a2b_base85(encoded, alphabet=alphabet), self.rawdata) + self.assertEqual(binascii.b2a_base85(data, alphabet=self.type2test(alphabet)), expected) + + data = self.type2test(b'') + self.assertEqual(binascii.b2a_base85(data, alphabet=alphabet), b'') + self.assertEqual(binascii.a2b_base85(data, alphabet=alphabet), b'') + + for func in binascii.b2a_base85, binascii.a2b_base85: + with self.assertRaises(TypeError): + func(data, alphabet=None) + with self.assertRaises(TypeError): + func(data, alphabet=alphabet.decode()) + with self.assertRaises(ValueError): + func(data, alphabet=alphabet[:-1]) + with self.assertRaises(ValueError): + func(data, alphabet=alphabet+b'?') + with self.assertRaises(TypeError): + binascii.a2b_base64(data, alphabet=bytearray(alphabet)) + + def test_base85_canonical(self): + # Non-canonical encodings are accepted without canonical=True + self.assertEqual(binascii.a2b_base85(b'VF'), b'a') + + # 1-char partial groups are always rejected (encoding violation: + # no conforming encoder produces them) + with self.assertRaises(binascii.Error): + binascii.a2b_base85(b'V') + with self.assertRaises(binascii.Error): + binascii.a2b_base85(b'0') + + # Verify round-trip: encode then decode with canonical=True works + for data in [b'a', b'ab', b'abc', b'abcd', b'abcde', + b'\x00', b'\xff', b'\x00\x00', b'\xff\xff\xff']: + encoded = binascii.b2a_base85(data) + decoded = binascii.a2b_base85(encoded, canonical=True) + self.assertEqual(decoded, data) + + # Test non-canonical rejection for each partial group size + # (2-char/1-byte, 3-char/2-byte, 4-char/3-byte). + # Incrementing the last digit by 1 produces a non-canonical + # encoding. For 4-char groups (n_pad=1) a +1 can change the + # output byte, so we use b'ab\x00' whose canonical form allows + # a +1 that still decodes to the same 3 bytes. + for data in [b'a', b'ab', b'ab\x00']: + canonical_enc = binascii.b2a_base85(data) + non_canonical = (canonical_enc[:-1] + + bytes([canonical_enc[-1] + 1])) + # Same decoded output without canonical check + self.assertEqual(binascii.a2b_base85(non_canonical), data) + # Rejected with canonical=True + with self.assertRaises(binascii.Error): + binascii.a2b_base85(non_canonical, canonical=True) + + # Boundary bytes: \x00 and \xff for each partial group size + for data in [b'\x00', b'\x00\x00', b'\x00\x00\x00', + b'\xff', b'\xff\xff', b'\xff\xff\xff']: + canonical_enc = binascii.b2a_base85(data) + binascii.a2b_base85(canonical_enc, canonical=True) + + # Full 5-char groups are always canonical (no padding bits) + self.assertEqual( + binascii.a2b_base85(b'VPa!s', canonical=True), b'abcd') + + # Empty input is valid + self.assertEqual(binascii.a2b_base85(b'', canonical=True), b'') + + @hypothesis.given(payload=hypothesis.strategies.binary()) + @hypothesis.example(b'') + @hypothesis.example(b'\x00') + @hypothesis.example(b'\xff\xff') + @hypothesis.example(b'abc') + def test_base85_canonical_roundtrip(self, payload): + encoded = binascii.b2a_base85(payload) + decoded = binascii.a2b_base85(encoded, canonical=True) + self.assertEqual(decoded, payload) + + @hypothesis.given(payload=hypothesis.strategies.binary(min_size=1, max_size=3)) + @hypothesis.example(b'\x00') + @hypothesis.example(b'\xff') + @hypothesis.example(b'ab\x00') + def test_base85_canonical_unique(self, payload): + # For a partial group, sweeping all 85 last-digit values should + # yield exactly one encoding that both decodes to the original + # payload AND passes canonical=True. + hypothesis.assume(len(payload) % 4 != 0) + canonical_enc = binascii.b2a_base85(payload) + table = binascii.BASE85_ALPHABET + accepted = [] + for digit in table: + candidate = canonical_enc[:-1] + bytes([digit]) + try: + result = binascii.a2b_base85(candidate, canonical=True) + if result == payload: + accepted.append(candidate) + except binascii.Error: + pass + self.assertEqual(accepted, [canonical_enc]) + + def test_ascii85_canonical(self): + # Non-canonical encodings are accepted without canonical=True + self.assertEqual(binascii.a2b_ascii85(b'@0'), b'a') + + # 1-char partial groups are always rejected (PLRM encoding violation) + with self.assertRaises(binascii.Error): + binascii.a2b_ascii85(b'@') + with self.assertRaises(binascii.Error): + binascii.a2b_ascii85(b'!') + + # Verify round-trip: encode then decode with canonical=True works + for data in [b'a', b'ab', b'abc', b'abcd', b'abcde', + b'\x00', b'\xff', b'\x00\x00', b'\xff\xff\xff']: + encoded = binascii.b2a_ascii85(data) + decoded = binascii.a2b_ascii85(encoded, canonical=True) + self.assertEqual(decoded, data) + + # Test non-canonical rejection for each partial group size. + # See test_base85_canonical for why b'ab\x00' is used for 3 bytes. + for data in [b'a', b'ab', b'ab\x00']: + canonical_enc = binascii.b2a_ascii85(data) + non_canonical = (canonical_enc[:-1] + + bytes([canonical_enc[-1] + 1])) + self.assertEqual(binascii.a2b_ascii85(non_canonical), data) + with self.assertRaises(binascii.Error): + binascii.a2b_ascii85(non_canonical, canonical=True) + + # Full 5-char groups are always canonical + self.assertEqual( + binascii.a2b_ascii85(b'@:E_W', canonical=True), b'abcd') + + # 'z' is the canonical form for all-zero groups per the PLRM. + # '!!!!!' decodes identically but is non-canonical. + self.assertEqual(binascii.a2b_ascii85(b'!!!!!'), b'\x00' * 4) + self.assertEqual(binascii.a2b_ascii85(b'z'), b'\x00' * 4) + self.assertEqual( + binascii.a2b_ascii85(b'z', canonical=True), b'\x00' * 4) + with self.assertRaises(binascii.Error): + binascii.a2b_ascii85(b'!!!!!', canonical=True) + # Multiple groups: z + !!!!! should fail + with self.assertRaises(binascii.Error): + binascii.a2b_ascii85(b'z!!!!!', canonical=True) + # Multiple z groups are fine + self.assertEqual( + binascii.a2b_ascii85(b'zz', canonical=True), b'\x00' * 8) + + # Empty input is valid + self.assertEqual(binascii.a2b_ascii85(b'', canonical=True), b'') + + # Adobe-wrapped with canonical + self.assertEqual( + binascii.a2b_ascii85(b'<~@:E_W~>', canonical=True, adobe=True), + b'abcd') + + @hypothesis.given(payload=hypothesis.strategies.binary()) + @hypothesis.example(b'') + @hypothesis.example(b'\x00') + @hypothesis.example(b'\x00\x00\x00\x00') # triggers z abbreviation + @hypothesis.example(b'\xff\xff') + @hypothesis.example(b'abc') + def test_ascii85_canonical_roundtrip(self, payload): + encoded = binascii.b2a_ascii85(payload) + decoded = binascii.a2b_ascii85(encoded, canonical=True) + self.assertEqual(decoded, payload) + + @hypothesis.given(payload=hypothesis.strategies.binary(min_size=1, max_size=3)) + @hypothesis.example(b'\x00') + @hypothesis.example(b'\xff') + @hypothesis.example(b'ab\x00') + def test_ascii85_canonical_unique(self, payload): + hypothesis.assume(len(payload) % 4 != 0) + canonical_enc = binascii.b2a_ascii85(payload) + # Ascii85 alphabet: '!' (33) through 'u' (117) + accepted = [] + for digit in range(33, 118): + candidate = canonical_enc[:-1] + bytes([digit]) + try: + result = binascii.a2b_ascii85(candidate, canonical=True) + if result == payload: + accepted.append(candidate) + except binascii.Error: + pass + self.assertEqual(accepted, [canonical_enc]) + + def test_base32_valid(self): + # Test base32 with valid data + lines = [] + step = 0 + i = 0 + while i < len(self.rawdata): + b = self.type2test(self.rawdata[i:i + step]) + a = binascii.b2a_base32(b) + lines.append(a) + i += step + step += 1 + res = bytes() + for line in lines: + a = self.type2test(line) + b = binascii.a2b_base32(a) + res += b + self.assertEqual(res, self.rawdata) + + def test_base32_errors(self): + def _fixPadding(data): + fixed = data.replace(b"=", b"") + len_8 = len(fixed) % 8 + p = 8 - len_8 if len_8 else 0 + return fixed + b"=" * p + + def _assertRegexTemplate(assert_regex, data, good_padding_result=None, **kwargs): + with self.assertRaisesRegex(binascii.Error, assert_regex): + binascii.a2b_base32(self.type2test(data), **kwargs) + if good_padding_result: + fixed = self.type2test(_fixPadding(data)) + self.assertEqual(binascii.a2b_base32(fixed), good_padding_result) + + def assertNonBase32Data(*args): + _assertRegexTemplate(r"(?i)Only base32 data", *args) + + def assertExcessData(*args): + _assertRegexTemplate(r"(?i)Excess data", *args) + + def assertExcessPadding(*args): + _assertRegexTemplate(r"(?i)Excess padding", *args) + + def assertLeadingPadding(*args, **kwargs): + _assertRegexTemplate(r"(?i)Leading padding", *args, **kwargs) + + def assertIncorrectPadding(*args): + _assertRegexTemplate(r"(?i)Incorrect padding", *args) + + def assertDiscontinuousPadding(*args): + _assertRegexTemplate(r"(?i)Discontinuous padding", *args) + + def assertInvalidLength(data, *args, length=None, **kwargs): + if length is None: + length = len(data.split(b'=', 1)[0].replace(b' ', b'')) + assert_regex = fr"(?i)Invalid.+number of data characters \({length}\)" + _assertRegexTemplate(assert_regex, data, *args, **kwargs) + + assertNonBase32Data(b"a") + assertNonBase32Data(b"AA-") + assertNonBase32Data(b"ABCDE==!") + assertNonBase32Data(b"ab:(){:|:&};:==") + + assertExcessData(b"AB======C") + assertExcessData(b"AB======CD") + assertExcessData(b"ABCD====E") + assertExcessData(b"ABCDE===FGH") + assertExcessData(b"ABCDEFG=H") + assertExcessData(b"432Z====55555555") + + assertExcessData(b"BE======EF", b"\t\x08") + assertExcessData(b"BEEF====C", b"\t\x08Q") + assertExcessData(b"BEEFC===AK", b"\t\x08Q\x01") + assertExcessData(b"BEEFCAK=E", b"\t\x08Q\x01D") + + assertExcessPadding(b"BE=======", b"\t") + assertExcessPadding(b"BE========", b"\t") + assertExcessPadding(b"BEEF=====", b"\t\x08") + assertExcessPadding(b"BEEF======", b"\t\x08") + assertExcessPadding(b"BEEFC====", b"\t\x08Q") + assertExcessPadding(b"BEEFC=====", b"\t\x08Q") + assertExcessPadding(b"BEEFCAK==", b"\t\x08Q\x01") + assertExcessPadding(b"BEEFCAK===", b"\t\x08Q\x01") + assertExcessPadding(b"BEEFCAKE=", b"\t\x08Q\x01D") + assertExcessPadding(b"BEEFCAKE==", b"\t\x08Q\x01D") + assertExcessPadding(b"BEEFCAKE===", b"\t\x08Q\x01D") + assertExcessPadding(b"BEEFCAKE====", b"\t\x08Q\x01D") + assertExcessPadding(b"BEEFCAKE=====", b"\t\x08Q\x01D") + assertExcessPadding(b"BEEFCAKE======", b"\t\x08Q\x01D") + assertExcessPadding(b"BEEFCAKE=======", b"\t\x08Q\x01D") + assertExcessPadding(b"BEEFCAKE========", b"\t\x08Q\x01D") + assertExcessPadding(b"BEEFCAKE=========", b"\t\x08Q\x01D") + + assertLeadingPadding(b"=", b"") + assertLeadingPadding(b"==", b"") + assertLeadingPadding(b"===", b"") + assertLeadingPadding(b"====", b"") + assertLeadingPadding(b"=====", b"") + assertLeadingPadding(b"======", b"") + assertLeadingPadding(b"=======", b"") + assertLeadingPadding(b"========", b"") + assertLeadingPadding(b"=========", b"") + assertLeadingPadding(b"=BEEFCAKE", b"\t\x08Q\x01D") + assertLeadingPadding(b"==BEEFCAKE", b"\t\x08Q\x01D") + assertLeadingPadding(b"===BEEFCAKE", b"\t\x08Q\x01D") + assertLeadingPadding(b"====BEEFCAKE", b"\t\x08Q\x01D") + assertLeadingPadding(b"=====BEEFCAKE", b"\t\x08Q\x01D") + assertLeadingPadding(b"======BEEFCAKE", b"\t\x08Q\x01D") + assertLeadingPadding(b"=======BEEFCAKE", b"\t\x08Q\x01D") + assertLeadingPadding(b"========BEEFCAKE", b"\t\x08Q\x01D") + assertLeadingPadding(b"=========BEEFCAKE", b"\t\x08Q\x01D") + assertLeadingPadding(b" =BEEFCAKE", ignorechars=b' ') + + assertIncorrectPadding(b"AB") + assertIncorrectPadding(b"ABCD") + assertIncorrectPadding(b"ABCDE") + assertIncorrectPadding(b"ABCDEFG") + + assertIncorrectPadding(b"BE=", b"\t") + assertIncorrectPadding(b"BE==", b"\t") + assertIncorrectPadding(b"BE===", b"\t") + assertIncorrectPadding(b"BE====", b"\t") + assertIncorrectPadding(b"BE=====", b"\t") + assertIncorrectPadding(b"BEEF=", b"\t\x08") + assertIncorrectPadding(b"BEEF==", b"\t\x08") + assertIncorrectPadding(b"BEEF===", b"\t\x08") + assertIncorrectPadding(b"BEEFC=", b"\t\x08Q") + assertIncorrectPadding(b"BEEFC==", b"\t\x08Q") + + assertDiscontinuousPadding(b"BE=EF===", b"\t\x08") + assertDiscontinuousPadding(b"BE==EF==", b"\t\x08") + assertDiscontinuousPadding(b"BEEF=C==", b"\t\x08Q") + assertDiscontinuousPadding(b"BEEFC=AK", b"\t\x08Q\x01") + + assertInvalidLength(b"A") + assertInvalidLength(b"ABC") + assertInvalidLength(b"ABCDEF") + assertInvalidLength(b"ABCDEFGHI") + assertInvalidLength(b"ABCDEFGHIJK") + assertInvalidLength(b"ABCDEFGHIJKLMN") + + assertInvalidLength(b"A=") + assertInvalidLength(b"A==") + assertInvalidLength(b"A===") + assertInvalidLength(b"A====") + assertInvalidLength(b"A=====") + assertInvalidLength(b"A======") + assertInvalidLength(b"ABC=") + assertInvalidLength(b"ABC==") + assertInvalidLength(b"ABC===") + assertInvalidLength(b"ABC====") + assertInvalidLength(b"ABCDEF=") + + assertInvalidLength(b"B=E=====", b"\t") + assertInvalidLength(b"B==E====", b"\t") + assertInvalidLength(b"BEE=F===", b"\t\x08") + assertInvalidLength(b"BEE==F==", b"\t\x08") + assertInvalidLength(b"BEEFCA=K", b"\t\x08Q\x01") + assertInvalidLength(b"BEEFCA=====K", b"\t\x08Q\x01") + + assertInvalidLength(b" A", ignorechars=b' ') + assertInvalidLength(b" ABC", ignorechars=b' ') + assertInvalidLength(b" ABCDEF", ignorechars=b' ') + assertInvalidLength(b" ABCDEFGHI", ignorechars=b' ') + assertInvalidLength(b" ABCDEFGHIJK", ignorechars=b' ') + assertInvalidLength(b" ABCDEFGHIJKLMN", ignorechars=b' ') + assertInvalidLength(b" A=======", ignorechars=b' ') + assertInvalidLength(b" ABC=====", ignorechars=b' ') + assertInvalidLength(b" ABCDEF==", ignorechars=b' ') + + def test_base32_canonical(self): + # https://datatracker.ietf.org/doc/html/rfc4648.html#section-3.5 + # Decoders MAY reject encoded data if the pad bits are not zero. + + # Without canonical=True, non-zero padding bits are accepted + self.assertEqual(binascii.a2b_base32(self.type2test(b'AB======')), + b'\x00') + + # 2 data chars + "======": last char has 2 padding bits + with self.assertRaises(binascii.Error): + binascii.a2b_base32(self.type2test(b'AB======'), canonical=True) + with self.assertRaises(binascii.Error): + binascii.a2b_base32(self.type2test(b'AD======'), canonical=True) + + # 4 data chars + "====": last char has 4 padding bits + with self.assertRaises(binascii.Error): + binascii.a2b_base32(self.type2test(b'AAAB===='), canonical=True) + with self.assertRaises(binascii.Error): + binascii.a2b_base32(self.type2test(b'AAAP===='), canonical=True) + + # 5 data chars + "===": last char has 1 padding bit + with self.assertRaises(binascii.Error): + binascii.a2b_base32(self.type2test(b'AAAAB==='), canonical=True) + + # 7 data chars + "=": last char has 3 padding bits + with self.assertRaises(binascii.Error): + binascii.a2b_base32(self.type2test(b'AAAAAAB='), canonical=True) + with self.assertRaises(binascii.Error): + binascii.a2b_base32(self.type2test(b'AAAAAAH='), canonical=True) + + # Verify that zero padding bits are accepted + binascii.a2b_base32(self.type2test(b'AA======'), canonical=True) + binascii.a2b_base32(self.type2test(b'AAAA===='), canonical=True) + binascii.a2b_base32(self.type2test(b'AAAAA==='), canonical=True) + binascii.a2b_base32(self.type2test(b'AAAAAAA='), canonical=True) + + # Full octet with no padding -- always valid + binascii.a2b_base32(self.type2test(b'AAAAAAAA'), canonical=True) + + @hypothesis.given(payload=hypothesis.strategies.binary()) + @hypothesis.example(b'') + @hypothesis.example(b'\x00') + @hypothesis.example(b'\xff\xff') + @hypothesis.example(b'abc') + def test_base32_canonical_roundtrip(self, payload): + encoded = binascii.b2a_base32(payload) + decoded = binascii.a2b_base32(encoded, canonical=True) + self.assertEqual(decoded, payload) + + def test_a2b_base32_padded(self): + a2b_base32 = binascii.a2b_base32 + t = self.type2test + def check(data, expected): + if b'=' in data: + with self.assertRaisesRegex(binascii.Error, 'Padding not allowed'): + a2b_base32(t(data), padded=False) + self.assertEqual(a2b_base32(t(data), padded=False, ignorechars=b'='), + expected) + data = data.replace(b'=', b'') + self.assertEqual(a2b_base32(t(data), padded=False), expected) + + check(b'', b'') + check(b'ME======', b'a') + check(b'MFRA====', b'ab') + check(b'MFRGG===', b'abc') + check(b'MFRGGZA=', b'abcd') + check(b'MFRGGZDF', b'abcde') + check(b'M=FRGGZDF', b'abcde') + check(b'MF=RGGZDF', b'abcde') + check(b'MFR=GGZDF', b'abcde') + check(b'MFRG=GZDF', b'abcde') + check(b'MFRGG=ZDF', b'abcde') + check(b'MFRGGZ=DF', b'abcde') + check(b'MFRGGZD=F', b'abcde') + + def test_b2a_base32_padded(self): + b2a_base32 = binascii.b2a_base32 + t = self.type2test + self.assertEqual(b2a_base32(t(b''), padded=False), b'') + self.assertEqual(b2a_base32(t(b'a'), padded=False), b'ME') + self.assertEqual(b2a_base32(t(b'ab'), padded=False), b'MFRA') + self.assertEqual(b2a_base32(t(b'abc'), padded=False), b'MFRGG') + self.assertEqual(b2a_base32(t(b'abcd'), padded=False), b'MFRGGZA') + self.assertEqual(b2a_base32(t(b'abcde'), padded=False), b'MFRGGZDF') + + def test_base32_wrapcol(self): + self._common_test_wrapcol(binascii.b2a_base32) + b = self.type2test(b'www.python.org') + self.assertEqual(binascii.b2a_base32(b, wrapcol=16), + b'O53XOLTQPF2GQ33O\nFZXXEZY=') + self.assertEqual(binascii.b2a_base32(b, wrapcol=23), + b'O53XOLTQPF2GQ33O\nFZXXEZY=') + self.assertEqual(binascii.b2a_base32(b, wrapcol=1), + b'O53XOLTQ\nPF2GQ33O\nFZXXEZY=') + + def test_base32_ignorechars(self): + a2b_base32 = binascii.a2b_base32 + self._common_test_ignorechars(a2b_base32) + eq = self.assertEqual + eq(a2b_base32(b'MFRG\n GZDF\n', ignorechars=b' \n'), b'abcde') + eq(a2b_base32(b'MFRG\n GZDF\n', ignorechars=bytearray(b' \n')), b'abcde') + eq(a2b_base32(b'M=======FRGGZDF', ignorechars=b'='), b'abcde') + eq(a2b_base32(b'MF======RGGZDF', ignorechars=b'='), b'abcde') + eq(a2b_base32(b'MFR=====GGZDF', ignorechars=b'='), b'abcde') + eq(a2b_base32(b'MFRG====GZDF', ignorechars=b'='), b'abcde') + eq(a2b_base32(b'MFRGG===ZDF', ignorechars=b'='), b'abcde') + eq(a2b_base32(b'MFRGGZ==DF', ignorechars=b'='), b'abcde') + eq(a2b_base32(b'MFRGGZD=F', ignorechars=b'='), b'abcde') + eq(a2b_base32(b'MFRGGZDF=', ignorechars=b'='), b'abcde') + eq(a2b_base32(b'MFRA======', ignorechars=b'='), b'ab') + + eq(a2b_base32(b'A1B3C5W7Z9', ignorechars=b'19WZ'), b'\x00v.\xdb\xf9') + eq(a2b_base32(b'A1B3C5W7Z9', ignorechars=b'19WZ', + alphabet=binascii.BASE32HEX_ALPHABET), b'PV6\x14\xe9') + + def test_base32_alphabet(self): + alphabet = b'0Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii9JjKk' + data = self.type2test(self.rawdata) + encoded = binascii.b2a_base32(data, alphabet=alphabet) + trans = bytes.maketrans(binascii.BASE32_ALPHABET, alphabet) + expected = binascii.b2a_base32(data).translate(trans) + self.assertEqual(encoded, expected) + self.assertEqual(binascii.a2b_base32(encoded, alphabet=alphabet), self.rawdata) + self.assertEqual(binascii.b2a_base32(data, alphabet=self.type2test(alphabet)), expected) + + data = self.type2test(b'') + self.assertEqual(binascii.b2a_base32(data, alphabet=alphabet), b'') + self.assertEqual(binascii.a2b_base32(data, alphabet=alphabet), b'') + + for func in binascii.b2a_base32, binascii.a2b_base32: + with self.assertRaises(TypeError): + func(data, alphabet=None) + with self.assertRaises(TypeError): + func(data, alphabet=alphabet.decode()) + with self.assertRaises(ValueError): + func(data, alphabet=alphabet[:-1]) + with self.assertRaises(ValueError): + func(data, alphabet=alphabet+b'?') + with self.assertRaises(TypeError): + binascii.a2b_base32(data, alphabet=bytearray(alphabet)) def test_uu(self): MAX_UU = 45 @@ -227,6 +1306,10 @@ def test_uu(self): self.assertEqual(binascii.a2b_uu(b"\xff"), b"\x00"*31) self.assertRaises(binascii.Error, binascii.a2b_uu, b"\xff\x00") self.assertRaises(binascii.Error, binascii.a2b_uu, b"!!!!") + self.assertRaises(binascii.Error, binascii.a2b_uu, + self.type2test(b"")) + self.assertRaises(binascii.Error, binascii.a2b_uu, + self.type2test(b"#86)C")[:0]) self.assertRaises(binascii.Error, binascii.b2a_uu, 46*b"!") # Issue #7701 (crash on a pydebug build) @@ -308,6 +1391,15 @@ def test_hex_separator(self): expected1 = s.hex(':').encode('ascii') self.assertEqual(binascii.b2a_hex(self.type2test(s), ':'), expected1) + def test_hex_ignorechars(self): + a2b_hex = binascii.a2b_hex + self._common_test_ignorechars(a2b_hex) + self._common_test_ignorechars(binascii.unhexlify) + eq = self.assertEqual + eq(a2b_hex(b'A B\nC D\n', ignorechars=b' \n'), b'\xab\xcd') + eq(a2b_hex(b'A B\nC D\n', ignorechars=bytearray(b' \n')), b'\xab\xcd') + eq(a2b_hex(b'aBcD', ignorechars=b'ac'), b'\xab\xcd') + def test_qp(self): type2test = self.type2test a2b_qp = binascii.a2b_qp @@ -434,6 +1526,9 @@ def test_empty_string(self): binascii.crc_hqx(empty, 0) continue f = getattr(binascii, func) + if func == 'a2b_uu': + self.assertRaises(binascii.Error, f, empty) + continue try: f(empty) except Exception as err: @@ -479,6 +1574,32 @@ def test_b2a_base64_newline(self): b'aGVsbG8=\n') self.assertEqual(binascii.b2a_base64(b, newline=False), b'aGVsbG8=') + b = self.type2test(b'') + self.assertEqual(binascii.b2a_base64(b), b'\n') + self.assertEqual(binascii.b2a_base64(b, newline=True), b'\n') + self.assertEqual(binascii.b2a_base64(b, newline=False), b'') + + def test_b2a_base64_padded(self): + b2a_base64 = binascii.b2a_base64 + t = self.type2test + self.assertEqual(b2a_base64(t(b''), padded=False), b'\n') + self.assertEqual(b2a_base64(t(b'a'), padded=False), b'YQ\n') + self.assertEqual(b2a_base64(t(b'ab'), padded=False), b'YWI\n') + self.assertEqual(b2a_base64(t(b'abc'), padded=False), b'YWJj\n') + + def test_b2a_base64_wrapcol(self): + self._common_test_wrapcol(binascii.b2a_base64) + b = self.type2test(b'www.python.org') + self.assertEqual(binascii.b2a_base64(b, wrapcol=8), + b'd3d3LnB5\ndGhvbi5v\ncmc=\n') + self.assertEqual(binascii.b2a_base64(b, wrapcol=11), + b'd3d3LnB5\ndGhvbi5v\ncmc=\n') + self.assertEqual(binascii.b2a_base64(b, wrapcol=8, newline=False), + b'd3d3LnB5\ndGhvbi5v\ncmc=') + self.assertEqual(binascii.b2a_base64(b, wrapcol=1), + b'd3d3\nLnB5\ndGhv\nbi5v\ncmc=\n') + b = self.type2test(b'') + self.assertEqual(binascii.b2a_base64(b, wrapcol=8, newline=False), b'') @hypothesis.given( binary=hypothesis.strategies.binary(), diff --git a/Lib/test/test_bisect.py b/Lib/test/test_bisect.py index 97204d4cad3871..a7e1f533ff2adc 100644 --- a/Lib/test/test_bisect.py +++ b/Lib/test/test_bisect.py @@ -391,9 +391,9 @@ class TestErrorHandlingC(TestErrorHandling, unittest.TestCase): class TestDocExample: def test_grades(self): - def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'): - i = self.module.bisect(breakpoints, score) - return grades[i] + def grade(score): + i = self.module.bisect([60, 70, 80, 90], score) + return "FDCBA"[i] result = [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]] self.assertEqual(result, ['F', 'A', 'C', 'C', 'B', 'A', 'A']) diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 19582e757161fc..3213a475127343 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -24,7 +24,6 @@ import sys, array, io, os from decimal import Decimal from fractions import Fraction -from test.support import warnings_helper try: from _testbuffer import * @@ -66,7 +65,8 @@ '?':0, 'c':0, 'b':0, 'B':0, 'h':0, 'H':0, 'i':0, 'I':0, 'l':0, 'L':0, 'n':0, 'N':0, - 'e':0, 'f':0, 'd':0, 'P':0 + 'e':0, 'f':0, 'd':0, 'P':0, + 'Zf':0, 'Zd':0, } # NumPy does not have 'n' or 'N': @@ -92,7 +92,9 @@ 'l':(-(1<<31), 1<<31), 'L':(0, 1<<32), 'q':(-(1<<63), 1<<63), 'Q':(0, 1<<64), 'e':(-65519, 65520), 'f':(-(1<<63), 1<<63), - 'd':(-(1<<1023), 1<<1023) + 'd':(-(1<<1023), 1<<1023), + 'Zf':(-(1<<63), 1<<63), + 'Zd':(-(1<<1023), 1<<1023), } def native_type_range(fmt): @@ -107,6 +109,10 @@ def native_type_range(fmt): lh = (-(1<<63), 1<<63) elif fmt == 'd': lh = (-(1<<1023), 1<<1023) + elif fmt == 'Zf': + lh = (-(1<<63), 1<<63) + elif fmt == 'Zd': + lh = (-(1<<1023), 1<<1023) else: for exp in (128, 127, 64, 63, 32, 31, 16, 15, 8, 7): try: @@ -136,7 +142,7 @@ def native_type_range(fmt): # Format codes supported by array.array ARRAY = NATIVE.copy() for k in NATIVE: - if not k in "bBhHiIlLfd": + if k not in list("bBhHiIlLefd") + ['Zf', 'Zd']: del ARRAY[k] BYTEFMT = NATIVE.copy() @@ -175,13 +181,28 @@ def randrange_fmt(mode, char, obj): if char in 'efd': x = struct.pack(char, x) x = struct.unpack(char, x)[0] + if char in ('Zf', 'Zd'): + y = randrange(*fmtdict[mode][char]) + x = complex(x, y) + x = struct.pack(char, x) + x = struct.unpack(char, x)[0] return x +def split_format(fmt): + i = 0 + while i < len(fmt): + if fmt[i] == 'Z': + n = 2 + else: + n = 1 + yield fmt[i:i + n] + i += n + def gen_item(fmt, obj): """Return single random item.""" mode, chars = fmt.split('#') x = [] - for c in chars: + for c in split_format(chars): x.append(randrange_fmt(mode, c, obj)) return x[0] if len(x) == 1 else tuple(x) @@ -242,9 +263,7 @@ def is_byte_format(fmt): def is_memoryview_format(fmt): """format suitable for memoryview""" - x = len(fmt) - return ((x == 1 or (x == 2 and fmt[0] == '@')) and - fmt[x-1] in MEMORYVIEW) + return fmt.removeprefix('@') in MEMORYVIEW NON_BYTE_FORMAT = [c for c in fmtdict['@'] if not is_byte_format(c)] @@ -636,14 +655,22 @@ def ndarray_from_structure(items, fmt, t, flags=0): return ndarray(items, shape=shape, strides=strides, format=fmt, offset=offset, flags=ND_WRITABLE|flags) +# Convert PEP 3118 formats to numpy dtypes +FORMAT_TO_DTYPE = { + 'Zf': 'F', + 'Zd': 'D', +} + def numpy_array_from_structure(items, fmt, t): """Return numpy_array from the tuple returned by rand_structure()""" memlen, itemsize, ndim, shape, strides, offset = t buf = bytearray(memlen) for j, v in enumerate(items): struct.pack_into(fmt, buf, j*itemsize, v) + # Replace Zd/Zf formats with D/F dtypes + dtype = FORMAT_TO_DTYPE.get(fmt, fmt) return numpy_array(buffer=buf, shape=shape, strides=strides, - dtype=fmt, offset=offset) + dtype=dtype, offset=offset) # ====================================================================== @@ -3015,7 +3042,7 @@ def test_memoryview_assign(self): m = memoryview(nd) self.assertRaises(TypeError, m.__setitem__, 0, 100) - ex = ndarray(list(range(120)), shape=[1,2,3,4,5], flags=ND_WRITABLE) + ex = ndarray(list(range(144)), shape=[1,2,3,4,6], flags=ND_WRITABLE) m1 = memoryview(ex) for fmt, _range in fmtdict['@'].items(): @@ -3025,7 +3052,7 @@ def test_memoryview_assign(self): continue m2 = m1.cast(fmt) lo, hi = _range - if fmt == 'd' or fmt == 'f': + if fmt in ("d", "f", "Zd", "Zf"): lo, hi = -2**1024, 2**1024 if fmt != 'P': # PyLong_AsVoidPtr() accepts negative numbers self.assertRaises(ValueError, m2.__setitem__, 0, lo-1) @@ -3233,15 +3260,6 @@ class BEPoint(ctypes.BigEndianStructure): self.assertNotEqual(point, a) self.assertRaises(NotImplementedError, a.tolist) - @warnings_helper.ignore_warnings(category=DeprecationWarning) # gh-80480 array('u') - def test_memoryview_compare_special_cases_deprecated_u_type_code(self): - - # Depends on issue #15625: the struct module does not understand 'u'. - a = array.array('u', 'xyz') - v = memoryview(a) - self.assertNotEqual(a, v) - self.assertNotEqual(v, a) - def test_memoryview_compare_ndim_zero(self): nd1 = ndarray(1729, shape=[], format='@L') @@ -4447,6 +4465,43 @@ def test_bytearray_release_buffer_read_flag(self): with self.assertRaises(SystemError): obj.__buffer__(inspect.BufferFlags.WRITE) + @support.cpython_only + @unittest.skipIf(_testcapi is None, "requires _testcapi") + def test_bytearray_alignment(self): + # gh-140557: pointer alignment of buffers including empty allocation + # should be at least to `size_t`. + align = struct.calcsize("N") + cases = [ + bytearray(), + bytearray(1), + bytearray(b"0123456789abcdef"), + bytearray(16), + ] + ptrs = [_testcapi.buffer_pointer_as_int(array) for array in cases] + self.assertEqual([ptr % align for ptr in ptrs], [0]*len(ptrs)) + + @support.cpython_only + @unittest.skipIf(_testcapi is None, "requires _testcapi") + def test_array_alignment(self): + # gh-140557: pointer alignment of buffers including empty allocation + # should match the maximum array alignment. + formats = [fmt for fmt in ARRAY + if struct.calcsize(fmt) <= struct.calcsize('P')] + align = max(struct.calcsize(fmt) for fmt in formats) + cases = [array.array(fmt) for fmt in formats] + # Empty arrays + self.assertEqual( + [_testcapi.buffer_pointer_as_int(case) % align for case in cases], + [0] * len(cases), + ) + for case in cases: + case.append(0) + # Allocated arrays + self.assertEqual( + [_testcapi.buffer_pointer_as_int(case) % align for case in cases], + [0] * len(cases), + ) + @support.cpython_only def test_pybuffer_size_from_format(self): # basic tests diff --git a/Lib/test/test_bufio.py b/Lib/test/test_bufio.py deleted file mode 100644 index cb9cb4d0bc7e9c..00000000000000 --- a/Lib/test/test_bufio.py +++ /dev/null @@ -1,73 +0,0 @@ -import unittest -from test.support import os_helper - -import io # C implementation. -import _pyio as pyio # Python implementation. - -# Simple test to ensure that optimizations in the IO library deliver the -# expected results. For best testing, run this under a debug-build Python too -# (to exercise asserts in the C code). - -lengths = list(range(1, 257)) + [512, 1000, 1024, 2048, 4096, 8192, 10000, - 16384, 32768, 65536, 1000000] - -class BufferSizeTest: - def try_one(self, s): - # Write s + "\n" + s to file, then open it and ensure that successive - # .readline()s deliver what we wrote. - - # Ensure we can open TESTFN for writing. - os_helper.unlink(os_helper.TESTFN) - - # Since C doesn't guarantee we can write/read arbitrary bytes in text - # files, use binary mode. - f = self.open(os_helper.TESTFN, "wb") - try: - # write once with \n and once without - f.write(s) - f.write(b"\n") - f.write(s) - f.close() - f = self.open(os_helper.TESTFN, "rb") - line = f.readline() - self.assertEqual(line, s + b"\n") - line = f.readline() - self.assertEqual(line, s) - line = f.readline() - self.assertFalse(line) # Must be at EOF - f.close() - finally: - os_helper.unlink(os_helper.TESTFN) - - def drive_one(self, pattern): - for length in lengths: - # Repeat string 'pattern' as often as needed to reach total length - # 'length'. Then call try_one with that string, a string one larger - # than that, and a string one smaller than that. Try this with all - # small sizes and various powers of 2, so we exercise all likely - # stdio buffer sizes, and "off by one" errors on both sides. - q, r = divmod(length, len(pattern)) - teststring = pattern * q + pattern[:r] - self.assertEqual(len(teststring), length) - self.try_one(teststring) - self.try_one(teststring + b"x") - self.try_one(teststring[:-1]) - - def test_primepat(self): - # A pattern with prime length, to avoid simple relationships with - # stdio buffer sizes. - self.drive_one(b"1234567890\00\01\02\03\04\05\06") - - def test_nullpat(self): - self.drive_one(b'\0' * 1000) - - -class CBufferSizeTest(BufferSizeTest, unittest.TestCase): - open = io.open - -class PyBufferSizeTest(BufferSizeTest, unittest.TestCase): - open = staticmethod(pyio.open) - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_build_details.py b/Lib/test/test_build_details.py index bc04963f5ad613..30d9c213077ab7 100644 --- a/Lib/test/test_build_details.py +++ b/Lib/test/test_build_details.py @@ -1,17 +1,39 @@ +import importlib import json import os +import os.path import sys import sysconfig import string import unittest +from pathlib import Path from test.support import is_android, is_apple_mobile, is_wasm32 +BASE_PATH = Path( + __file__, # Lib/test/test_build_details.py + '..', # Lib/test + '..', # Lib + '..', # +).resolve() +MODULE_PATH = BASE_PATH / 'Tools' / 'build' / 'generate-build-details.py' + +try: + # Import "generate-build-details.py" as "generate_build_details" + spec = importlib.util.spec_from_file_location( + "generate_build_details", MODULE_PATH + ) + generate_build_details = importlib.util.module_from_spec(spec) + sys.modules["generate_build_details"] = generate_build_details + spec.loader.exec_module(generate_build_details) +except (FileNotFoundError, ImportError): + generate_build_details = None + class FormatTestsBase: @property def contents(self): - """Install details file contents. Should be overriden by subclasses.""" + """Install details file contents. Should be overridden by subclasses.""" raise NotImplementedError @property @@ -31,16 +53,15 @@ def key(self, name): value = value[part] return value - def test_parse(self): - self.data - def test_top_level_container(self): self.assertIsInstance(self.data, dict) for key, value in self.data.items(): with self.subTest(key=key): - if key in ('schema_version', 'base_prefix', 'base_interpreter', 'platform'): + if key in ('schema_version', 'base_prefix', 'base_interpreter', + 'platform'): self.assertIsInstance(value, str) - elif key in ('language', 'implementation', 'abi', 'suffixes', 'libpython', 'c_api', 'arbitrary_data'): + elif key in ('language', 'implementation', 'abi', 'suffixes', + 'libpython', 'c_api', 'arbitrary_data'): self.assertIsInstance(value, dict) def test_base_prefix(self): @@ -71,15 +92,20 @@ def test_language_version_info(self): self.assertEqual(len(value), sys.version_info.n_fields) for part_name, part_value in value.items(): with self.subTest(part=part_name): - self.assertEqual(part_value, getattr(sys.version_info, part_name)) + sys_version_value = getattr(sys.version_info, part_name) + self.assertEqual(part_value, sys_version_value) def test_implementation(self): + impl_ver = sys.implementation.version for key, value in self.key('implementation').items(): with self.subTest(part=key): if key == 'version': - self.assertEqual(len(value), len(sys.implementation.version)) + self.assertEqual(len(value), len(impl_ver)) for part_name, part_value in value.items(): - self.assertEqual(getattr(sys.implementation.version, part_name), part_value) + self.assertFalse(isinstance(sys.implementation.version, dict)) + getattr(sys.implementation.version, part_name) + sys_implementation_value = getattr(impl_ver, part_name) + self.assertEqual(sys_implementation_value, part_value) else: self.assertEqual(getattr(sys.implementation, key), value) @@ -99,7 +125,8 @@ class CPythonBuildDetailsTests(unittest.TestCase, FormatTestsBase): def location(self): if sysconfig.is_python_build(): projectdir = sysconfig.get_config_var('projectbase') - with open(os.path.join(projectdir, 'pybuilddir.txt')) as f: + pybuilddir = os.path.join(projectdir, 'pybuilddir.txt') + with open(pybuilddir, encoding='utf-8') as f: dirname = os.path.join(projectdir, f.read()) else: dirname = sysconfig.get_path('stdlib') @@ -107,7 +134,7 @@ def location(self): @property def contents(self): - with open(self.location, 'r') as f: + with open(self.location, 'r', encoding='utf-8') as f: return f.read() @needs_installed_python @@ -147,5 +174,64 @@ def test_c_api(self): self.assertTrue(os.path.exists(os.path.join(value['pkgconfig_path'], f'python-{version}.pc'))) +@unittest.skipIf( + generate_build_details is None, + "Failed to import generate-build-details" +) +@unittest.skipIf(os.name != 'posix', 'Feature only implemented on POSIX right now') +@unittest.skipIf(is_wasm32, 'Feature not available on WebAssembly builds') +class BuildDetailsRelativePathsTests(unittest.TestCase): + @property + def build_details_absolute_paths(self): + data = generate_build_details.generate_data(schema_version='1.0') + return json.loads(json.dumps(data)) + + @property + def build_details_relative_paths(self): + data = self.build_details_absolute_paths + generate_build_details.make_paths_relative(data, config_path=None) + return data + + def test_round_trip(self): + data_abs_path = self.build_details_absolute_paths + data_rel_path = self.build_details_relative_paths + + self.assertEqual(data_abs_path['base_prefix'], + data_rel_path['base_prefix']) + + base_prefix = data_abs_path['base_prefix'] + + top_level_keys = ('base_interpreter',) + for key in top_level_keys: + self.assertEqual(key in data_abs_path, key in data_rel_path) + if key not in data_abs_path: + continue + + abs_rel_path = os.path.join(base_prefix, data_rel_path[key]) + abs_rel_path = os.path.normpath(abs_rel_path) + self.assertEqual(data_abs_path[key], abs_rel_path) + + second_level_keys = ( + ('libpython', 'dynamic'), + ('libpython', 'dynamic_stableabi'), + ('libpython', 'static'), + ('c_api', 'headers'), + ('c_api', 'pkgconfig_path'), + + ) + for part, key in second_level_keys: + self.assertEqual(part in data_abs_path, part in data_rel_path) + if part not in data_abs_path: + continue + self.assertEqual(key in data_abs_path[part], + key in data_rel_path[part]) + if key not in data_abs_path[part]: + continue + + abs_rel_path = os.path.join(base_prefix, data_rel_path[part][key]) + abs_rel_path = os.path.normpath(abs_rel_path) + self.assertEqual(data_abs_path[part][key], abs_rel_path) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 8830641f0abdc7..1d2c105ac047e1 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -4,6 +4,7 @@ import builtins import collections import contextlib +import copy import decimal import fractions import gc @@ -21,6 +22,7 @@ import typing import unittest import warnings +import weakref from contextlib import ExitStack from functools import partial from inspect import CO_COROUTINE @@ -31,12 +33,13 @@ from test import support from test.support import cpython_only, swap_attr from test.support import async_yield, run_yielding_async_fn +from test.support import warnings_helper from test.support.import_helper import import_module from test.support.os_helper import (EnvironmentVarGuard, TESTFN, unlink) from test.support.script_helper import assert_python_ok from test.support.testcase import ComplexesAreIdenticalMixin from test.support.warnings_helper import check_warnings -from test.support import requires_IEEE_754 +from test.support import requires_IEEE_754, skip_if_double_rounding from unittest.mock import MagicMock, patch try: import pty, signal @@ -44,13 +47,12 @@ pty = signal = None -# Detect evidence of double-rounding: sum() does not always -# get improved accuracy on machines that suffer from double rounding. -x, y = 1e16, 2.9999 # use temporary values to defeat peephole optimizer -HAVE_DOUBLE_ROUNDING = (x + y == 1e16 + 4) - # used as proof of globals being used A_GLOBAL_VALUE = 123 +A_SENTINEL = sentinel("A_SENTINEL") + +class SentinelContainer: + CLASS_SENTINEL = sentinel("SentinelContainer.CLASS_SENTINEL") class Squares: @@ -245,7 +247,7 @@ def test_any(self): S = [10, 20, 30] self.assertEqual(any(x > 42 for x in S), False) - def test_all_any_tuple_optimization(self): + def test_all_any_tuple_list_set_optimization(self): def f_all(): return all(x-2 for x in [1,2,3]) @@ -255,7 +257,16 @@ def f_any(): def f_tuple(): return tuple(2*x for x in [1,2,3]) - funcs = [f_all, f_any, f_tuple] + def f_list(): + return list(2*x for x in [1,2,3]) + + def f_set(): + return set(2*x for x in [1,2,3]) + + def f_frozenset(): + return frozenset(2*x for x in [1,2,3]) + + funcs = [f_all, f_any, f_tuple, f_list, f_set, f_frozenset] for f in funcs: # check that generator code object is not duplicated @@ -265,33 +276,58 @@ def f_tuple(): # check the overriding the builtins works - global all, any, tuple - saved = all, any, tuple + global all, any, tuple, list, set, frozenset + saved = all, any, tuple, list, set, frozenset try: all = lambda x : "all" any = lambda x : "any" tuple = lambda x : "tuple" + list = lambda x : "list" + set = lambda x : "set" + frozenset = lambda x : "frozenset" overridden_outputs = [f() for f in funcs] finally: - all, any, tuple = saved - - self.assertEqual(overridden_outputs, ['all', 'any', 'tuple']) + all, any, tuple, list, set, frozenset = saved + self.assertEqual(overridden_outputs, ['all', 'any', 'tuple', 'list', 'set', 'frozenset']) # Now repeat, overriding the builtins module as well - saved = all, any, tuple + saved = all, any, tuple, list, set, frozenset try: builtins.all = all = lambda x : "all" builtins.any = any = lambda x : "any" builtins.tuple = tuple = lambda x : "tuple" + builtins.list = list = lambda x : "list" + builtins.set = set = lambda x : "set" + builtins.frozenset = frozenset = lambda x : "frozenset" overridden_outputs = [f() for f in funcs] finally: - all, any, tuple = saved - builtins.all, builtins.any, builtins.tuple = saved + all, any, tuple, list, set, frozenset = saved + builtins.all, builtins.any, builtins.tuple, builtins.list, builtins.set, builtins.frozenset = saved - self.assertEqual(overridden_outputs, ['all', 'any', 'tuple']) + self.assertEqual(overridden_outputs, ['all', 'any', 'tuple', 'list', 'set', 'frozenset']) + def test_builtin_call_async_genexpr_no_crash(self): + async def f_all(): + return all(await 2 for _ in []) + + async def f_any(): + return any(await 2 for _ in []) + + async def f_tuple(): + return tuple(await 2 for _ in []) + + async def f_list(): + return list(await 2 for _ in []) + + async def f_set(): + return set(await 2 for _ in []) + + for f in (f_all, f_any, f_tuple, f_list, f_set): + with self.subTest(func=f.__name__): + with self.assertRaises(TypeError): + run_yielding_async_fn(f) def test_ascii(self): self.assertEqual(ascii(''), '\'\'') @@ -775,6 +811,16 @@ def __getitem__(self, key): raise ValueError self.assertRaises(ValueError, eval, "foo", {}, X()) + def test_eval_frozendict(self): + ns = frozendict(x=1, data=[], __builtins__=__builtins__) + eval("data.append(x)", ns, ns) + self.assertEqual(ns['data'], [1]) + + ns = frozendict() + errmsg = "cannot assign __builtins__ to frozendict globals" + with self.assertRaisesRegex(TypeError, errmsg): + eval("", ns, ns) + def test_eval_kwargs(self): data = {"A_GLOBAL_VALUE": 456} self.assertEqual(eval("globals()['A_GLOBAL_VALUE']", globals=data), 456) @@ -873,6 +919,21 @@ def test_exec(self): del l['__builtins__'] self.assertEqual((g, l), ({'a': 1}, {'b': 2})) + def test_exec_frozendict(self): + ns = frozendict(x=1, data=[], __builtins__=__builtins__) + exec("data.append(x)", ns, ns) + self.assertEqual(ns['data'], [1]) + + ns = frozendict(__builtins__=__builtins__) + errmsg = "'frozendict' object does not support item assignment" + with self.assertRaisesRegex(TypeError, errmsg): + exec("x = 1", ns, ns) + + ns = frozendict() + errmsg = "cannot assign __builtins__ to frozendict globals" + with self.assertRaisesRegex(TypeError, errmsg): + exec("", ns, ns) + def test_exec_kwargs(self): g = {} exec('global z\nz = 1', globals=g) @@ -1087,6 +1148,29 @@ def four_freevars(): three_freevars.__globals__, closure=my_closure) + def test_exec_filter_syntax_warnings_by_module(self): + filename = support.findfile('test_import/data/syntax_warnings.py') + with open(filename, 'rb') as f: + source = f.read() + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=r'\z') + exec(source, {}) + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21]) + for wm in wlog: + self.assertEqual(wm.filename, '') + self.assertIs(wm.category, SyntaxWarning) + + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=r'package.module\z') + warnings.filterwarnings('error', module=r'') + exec(source, {'__name__': 'package.module', '__file__': filename}) + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21]) + for wm in wlog: + self.assertEqual(wm.filename, '') + self.assertIs(wm.category, SyntaxWarning) + def test_filter(self): self.assertEqual(list(filter(lambda c: 'a' <= c <= 'z', 'Hello World')), list('elloorld')) @@ -1183,6 +1267,16 @@ def __hash__(self): return self self.assertEqual(hash(Z(42)), hash(42)) + def test_invalid_hash_typeerror(self): + # GH-140406: The returned object from __hash__() would leak if it + # wasn't an integer. + class A: + def __hash__(self): + return 1.0 + + with self.assertRaises(TypeError): + hash(A()) + def test_hex(self): self.assertEqual(hex(16), '0x10') self.assertEqual(hex(-16), '-0x10') @@ -1374,6 +1468,22 @@ def test_map_strict(self): self.assertRaises(ValueError, tuple, map(pack, (1, 2), (1, 2), 'abc', strict=True)) + # gh-140517: Testing refleaks with mortal objects. + t1 = (None, object()) + t2 = (object(), object()) + t3 = (object(),) + + self.assertRaises(ValueError, tuple, + map(pack, t1, 'a', strict=True)) + self.assertRaises(ValueError, tuple, + map(pack, t1, t2, 'a', strict=True)) + self.assertRaises(ValueError, tuple, + map(pack, t1, t2, t3, strict=True)) + self.assertRaises(ValueError, tuple, + map(pack, 'a', t1, strict=True)) + self.assertRaises(ValueError, tuple, + map(pack, 'a', t2, t3, strict=True)) + def test_map_strict_iterators(self): x = iter(range(5)) y = [0] @@ -1820,6 +1930,115 @@ class C: __repr__ = None self.assertRaises(TypeError, repr, C()) + def test_sentinel(self): + missing = sentinel("MISSING") + other = sentinel("MISSING") + + self.assertIsInstance(missing, sentinel) + self.assertIs(type(missing), sentinel) + self.assertEqual(missing.__name__, "MISSING") + self.assertEqual(missing.__module__, __name__) + self.assertIsNot(missing, other) + self.assertEqual(repr(missing), "MISSING") + self.assertTrue(missing) + self.assertIs(copy.copy(missing), missing) + self.assertIs(copy.deepcopy(missing), missing) + self.assertEqual(missing, missing) + self.assertNotEqual(missing, other) + self.assertRaises(TypeError, sentinel) + self.assertRaises(TypeError, sentinel, "MISSING", "EXTRA") + self.assertRaises(TypeError, sentinel, name="MISSING") + with self.assertRaisesRegex(TypeError, "must be str"): + sentinel(1) + self.assertTrue(sentinel.__flags__ & support._TPFLAGS_IMMUTABLETYPE) + self.assertTrue(sentinel.__flags__ & support._TPFLAGS_HAVE_GC) + self.assertFalse(sentinel.__flags__ & support._TPFLAGS_BASETYPE) + with self.assertRaises(TypeError): + class SubSentinel(sentinel): + pass + + def test_sentinel_attributes(self): + missing = sentinel("MISSING") + with self.assertRaises(TypeError): + sentinel.attribute = "value" + with self.assertRaises(AttributeError): + missing.attribute = "value" + with self.assertRaises(AttributeError): + missing.__name__ = "CHANGED" + missing.__module__ = "changed" + self.assertEqual(missing.__module__, "changed") + with self.assertRaises(AttributeError): + del missing.__name__ + del missing.__module__ + with self.assertRaises(AttributeError): + missing.__module__ + + def test_sentinel_repr(self): + with_repr = sentinel("WITH_REPR", repr="custom") + without_repr = sentinel("WITHOUT_REPR", repr=None) + self.assertEqual(repr(with_repr), "custom") + self.assertEqual(repr(without_repr), "WITHOUT_REPR") + self.assertEqual(str(with_repr), "custom") + self.assertEqual(str(without_repr), "WITHOUT_REPR") + + with self.assertRaisesRegex(TypeError, "repr.*str or None"): + sentinel("BAD_REPR", repr=42) + + def test_sentinel_pickle(self): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + self.assertIs( + pickle.loads(pickle.dumps(A_SENTINEL, protocol=proto)), + A_SENTINEL) + self.assertIs( + pickle.loads(pickle.dumps( + SentinelContainer.CLASS_SENTINEL, protocol=proto)), + SentinelContainer.CLASS_SENTINEL) + + missing = sentinel("MISSING") + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + with self.assertRaises(pickle.PicklingError): + pickle.dumps(missing, protocol=proto) + + def test_sentinel_str_subclass_name_cycle(self): + class Name(str): + pass + + name = Name("MISSING") + missing = sentinel(name) + self.assertIs(missing.__name__, name) + self.assertTrue(gc.is_tracked(missing)) + + name.missing = missing + ref = weakref.ref(name) + del name, missing + support.gc_collect() + self.assertIsNone(ref()) + + def test_sentinel_union(self): + missing = sentinel("MISSING") + + self.assertIsInstance(missing | int, typing.Union) + self.assertEqual((missing | int).__args__, (missing, int)) + self.assertIsInstance(int | missing, typing.Union) + self.assertEqual((int | missing).__args__, (int, missing)) + self.assertIs(missing | missing, missing) + self.assertEqual(repr(int | missing), "int | MISSING") + self.assertIsInstance(missing | None, typing.Union) + self.assertEqual((missing | None).__args__, (missing, type(None))) + self.assertIsInstance(None | missing, typing.Union) + self.assertEqual((None | missing).__args__, (type(None), missing)) + self.assertIsInstance(missing | list[int], typing.Union) + self.assertEqual((missing | list[int]).__args__, (missing, list[int])) + self.assertIsInstance(missing | (int | str), typing.Union) + self.assertEqual((missing | (int | str)).__args__, (missing, int, str)) + + with self.assertRaises(TypeError): + missing | 1 + with self.assertRaises(TypeError): + 1 | missing + def test_round(self): self.assertEqual(round(0.0), 0.0) self.assertEqual(type(round(0.0)), int) @@ -2011,8 +2230,7 @@ def __getitem__(self, index): complex(2, -0.0)) @requires_IEEE_754 - @unittest.skipIf(HAVE_DOUBLE_ROUNDING, - "sum accuracy not guaranteed on machines with double rounding") + @skip_if_double_rounding @support.cpython_only # Other implementations may choose a different algorithm def test_sum_accuracy(self): self.assertEqual(sum([0.1] * 10), 1.0) @@ -2545,6 +2763,7 @@ def run_child(self, child, terminal_input): finally: signal.signal(signal.SIGHUP, old_sighup) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def _run_child(self, child, terminal_input): r, w = os.pipe() # Pipe test results from child back to parent try: @@ -2934,6 +3153,12 @@ def test_type_doc(self): A.__doc__ = doc self.assertEqual(A.__doc__, doc) + def test_type_frozendict(self): + A = type('A', (), frozendict({'x': 4, 'y': 2})) + self.assertEqual(A.x, 4) + self.assertEqual(A.y, 2) + self.assertEqual(A.__name__, 'A') + def test_bad_args(self): with self.assertRaises(TypeError): type() diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 2591e7ca6ab0ec..e211c3d15a4ed2 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -549,6 +549,17 @@ def test_hex_separator_basics(self): self.assertEqual(three_bytes.hex(':', 2), 'b9:01ef') self.assertEqual(three_bytes.hex(':', 1), 'b9:01:ef') self.assertEqual(three_bytes.hex('*', -2), 'b901*ef') + self.assertEqual(three_bytes.hex(sep=':', bytes_per_sep=2), 'b9:01ef') + self.assertEqual(three_bytes.hex(sep='*', bytes_per_sep=-2), 'b901*ef') + for bytes_per_sep in 3, -3, sys.maxsize, -sys.maxsize: + with self.subTest(bytes_per_sep=bytes_per_sep): + self.assertEqual(three_bytes.hex(':', bytes_per_sep), 'b901ef') + for bytes_per_sep in sys.maxsize+1, -sys.maxsize-1, 2**1000, -2**1000: + with self.subTest(bytes_per_sep=bytes_per_sep): + try: + self.assertEqual(three_bytes.hex(':', bytes_per_sep), 'b901ef') + except OverflowError: + pass value = b'{s\005\000\000\000worldi\002\000\000\000s\005\000\000\000helloi\001\000\000\0000' self.assertEqual(value.hex('.', 8), '7b7305000000776f.726c646902000000.730500000068656c.6c6f690100000030') @@ -573,6 +584,37 @@ def test_hex_separator_six_bytes(self): self.assertEqual(six_bytes.hex(':', -6), '0306090c0f12') self.assertEqual(six_bytes.hex(' ', -95), '0306090c0f12') + def test_hex_simd_boundaries(self): + # Test lengths around the SIMD threshold (16 bytes). + # SIMD processes 16 bytes at a time; smaller inputs use scalar code. + for length in (14, 15, 16, 17, 31, 32, 33, 64, 65): + data = self.type2test(bytes(range(length))) + expected = ''.join(f'{b:02x}' for b in range(length)) + with self.subTest(length=length): + self.assertEqual(data.hex(), expected) + + def test_hex_nibble_boundaries(self): + # Test the nibble value boundary at 9/10 (where '9' becomes 'a'). + # SIMD uses signed comparison for efficiency; verify correctness + # at this boundary for various nibble combinations. + boundary_bytes = self.type2test(bytes([ + 0x09, # both nibbles: 0, 9 + 0x0a, # both nibbles: 0, 10 + 0x90, # both nibbles: 9, 0 + 0x99, # both nibbles: 9, 9 (max all-digit) + 0x9a, # both nibbles: 9, 10 + 0xa0, # both nibbles: 10, 0 + 0xa9, # both nibbles: 10, 9 + 0xaa, # both nibbles: 10, 10 (min all-letter) + 0x00, # min value + 0xff, # max value + ])) + self.assertEqual(boundary_bytes.hex(), '090a90999aa0a9aa00ff') + + # Repeat with 16+ bytes to exercise SIMD path + simd_boundary = self.type2test(boundary_bytes * 2) + self.assertEqual(simd_boundary.hex(), '090a90999aa0a9aa00ff' * 2) + def test_join(self): self.assertEqual(self.type2test(b"").join([]), b"") self.assertEqual(self.type2test(b"").join([b""]), b"") @@ -603,6 +645,32 @@ def test_join(self): with self.assertRaises(TypeError): dot_join([memoryview(b"ab"), "cd", b"ef"]) + def test_join_concurrent_buffer_mutation(self): + # __buffer__() can release the GIL, letting another thread concurrently + # mutate the joined sequence (simulated here by mutating in __buffer__). + # See: https://github.com/python/cpython/issues/151295 + def make_seq(mutate): + # Item is only referenced from the list slot, so mutate() frees it. + class Item: + def __buffer__(self, flags): + mutate(seq) + return memoryview(b'x') + seq = [b'a', Item(), b'c'] + return seq + + for sep in (self.type2test(b''), self.type2test(b'::')): + with self.subTest(sep=sep): + # Changing the list length is reported as a RuntimeError. + seq = make_seq(lambda seq: seq.clear()) + self.assertRaises(RuntimeError, sep.join, seq) + + # The list length is unchanged, so the size-change recheck + # cannot fire: only keeping the item alive avoids the crash. + def replace(seq): + seq[1] = b'z' + seq = make_seq(replace) + self.assertEqual(sep.join(seq), sep.join([b'a', b'x', b'c'])) + def test_count(self): b = self.type2test(b'mississippi') i = 105 @@ -781,16 +849,16 @@ def __int__(self): pi = PseudoFloat(3.1415) exceptions_params = [ - ('%x format: an integer is required, not float', b'%x', 3.14), - ('%X format: an integer is required, not float', b'%X', 2.11), - ('%o format: an integer is required, not float', b'%o', 1.79), - ('%x format: an integer is required, not PseudoFloat', b'%x', pi), - ('%x format: an integer is required, not complex', b'%x', 3j), - ('%X format: an integer is required, not complex', b'%X', 2j), - ('%o format: an integer is required, not complex', b'%o', 1j), - ('%u format: a real number is required, not complex', b'%u', 3j), - ('%i format: a real number is required, not complex', b'%i', 2j), - ('%d format: a real number is required, not complex', b'%d', 2j), + ('%x requires an integer, not float', b'%x', 3.14), + ('%X requires an integer, not float', b'%X', 2.11), + ('%o requires an integer, not float', b'%o', 1.79), + (r'%x requires an integer, not .*\.PseudoFloat', b'%x', pi), + ('%x requires an integer, not complex', b'%x', 3j), + ('%X requires an integer, not complex', b'%X', 2j), + ('%o requires an integer, not complex', b'%o', 1j), + ('%u requires a real number, not complex', b'%u', 3j), + ('%i requires a real number, not complex', b'%i', 2j), + ('%d requires a real number, not complex', b'%d', 2j), ( r'%c requires an integer in range\(256\)' r' or a single byte, not .*\.PseudoFloat', @@ -799,9 +867,16 @@ def __int__(self): ] for msg, format_bytes, value in exceptions_params: - with self.assertRaisesRegex(TypeError, msg): + with self.assertRaisesRegex(TypeError, 'format argument: ' + msg): operator.mod(format_bytes, value) + def test_memory_leak_gh_140939(self): + # gh-140939: MemoryError is raised without leaking + _testcapi = import_helper.import_module('_testcapi') + with self.assertRaises(MemoryError): + b = self.type2test(b'%*b') + b % (_testcapi.PY_SSIZE_T_MAX, b'abc') + def test_imod(self): b = self.type2test(b'hello, %b!') orig = b @@ -829,6 +904,13 @@ def test_replace(self): self.assertEqual(b.replace(b'i', b'a'), b'massassappa') self.assertEqual(b.replace(b'ss', b'x'), b'mixixippi') + def test_replace_count_keyword(self): + b = self.type2test(b'aa') + self.assertEqual(b.replace(b'a', b'b', count=0), b'aa') + self.assertEqual(b.replace(b'a', b'b', count=1), b'ba') + self.assertEqual(b.replace(b'a', b'b', count=2), b'bb') + self.assertEqual(b.replace(b'a', b'b', count=3), b'bb') + def test_replace_int_error(self): self.assertRaises(TypeError, self.type2test(b'a b').replace, 32, b'') @@ -1364,6 +1446,18 @@ def test_bytearray_api(self): except OSError: pass + def test_mod_concurrent_mutation(self): + # Prevent crash in __mod__ when formatting mutates the bytearray. + # Regression test for https://github.com/python/cpython/issues/142557. + fmt = bytearray(b"%a end") + + class S: + def __repr__(self): + fmt.clear() + return "E" + + self.assertRaises(BufferError, fmt.__mod__, S()) + def test_reverse(self): b = bytearray(b'hello') self.assertEqual(b.reverse(), None) @@ -1390,6 +1484,16 @@ def test_clear(self): b.append(ord('p')) self.assertEqual(b, b'p') + # Cleared object should be empty. + b = bytearray(b'abc') + b.clear() + self.assertEqual(b.__alloc__(), 0) + base_size = sys.getsizeof(bytearray()) + self.assertEqual(sys.getsizeof(b), base_size) + c = b.copy() + self.assertEqual(c.__alloc__(), 0) + self.assertEqual(sys.getsizeof(c), base_size) + def test_copy(self): b = bytearray(b'abc') bb = b.copy() @@ -1451,6 +1555,87 @@ def test_resize(self): self.assertRaises(MemoryError, bytearray().resize, sys.maxsize) self.assertRaises(MemoryError, bytearray(1000).resize, sys.maxsize) + def test_take_bytes(self): + ba = bytearray(b'ab') + self.assertEqual(ba.take_bytes(), b'ab') + self.assertEqual(len(ba), 0) + self.assertEqual(ba, bytearray(b'')) + self.assertEqual(ba.__alloc__(), 0) + base_size = sys.getsizeof(bytearray()) + self.assertEqual(sys.getsizeof(ba), base_size) + + # Positive and negative slicing. + ba = bytearray(b'abcdef') + self.assertEqual(ba.take_bytes(1), b'a') + self.assertEqual(ba, bytearray(b'bcdef')) + self.assertEqual(len(ba), 5) + self.assertEqual(ba.take_bytes(-5), b'') + self.assertEqual(ba, bytearray(b'bcdef')) + self.assertEqual(len(ba), 5) + self.assertEqual(ba.take_bytes(-3), b'bc') + self.assertEqual(ba, bytearray(b'def')) + self.assertEqual(len(ba), 3) + self.assertEqual(ba.take_bytes(3), b'def') + self.assertEqual(ba, bytearray(b'')) + self.assertEqual(len(ba), 0) + + # Take nothing from emptiness. + self.assertEqual(ba.take_bytes(0), b'') + self.assertEqual(ba.take_bytes(), b'') + self.assertEqual(ba.take_bytes(None), b'') + + # Out of bounds, bad take value. + self.assertRaises(IndexError, ba.take_bytes, -1) + self.assertRaises(TypeError, ba.take_bytes, 3.14) + ba = bytearray(b'abcdef') + self.assertRaises(IndexError, ba.take_bytes, 7) + + # Offset between physical and logical start (ob_bytes != ob_start). + ba = bytearray(b'abcde') + del ba[:2] + self.assertEqual(ba, bytearray(b'cde')) + self.assertEqual(ba.take_bytes(), b'cde') + + # Overallocation at end. + ba = bytearray(b'abcde') + del ba[-2:] + self.assertEqual(ba, bytearray(b'abc')) + self.assertEqual(ba.take_bytes(), b'abc') + ba = bytearray(b'abcde') + ba.resize(4) + self.assertEqual(ba.take_bytes(), b'abcd') + + # Take of a bytearray with references should fail. + ba = bytearray(b'abc') + with memoryview(ba) as mv: + self.assertRaises(BufferError, ba.take_bytes) + self.assertEqual(ba.take_bytes(), b'abc') + + @support.cpython_only # tests an implementation detail + def test_take_bytes_optimization(self): + # Validate optimization around taking lots of little chunks out of a + # much bigger buffer. Save work by only copying a little rather than + # moving a lot. + ba = bytearray(b'abcdef' + b'0' * 1000) + start_alloc = ba.__alloc__() + + # Take two bytes at a time, checking alloc doesn't change. + self.assertEqual(ba.take_bytes(2), b'ab') + self.assertEqual(ba.__alloc__(), start_alloc) + self.assertEqual(len(ba), 4 + 1000) + self.assertEqual(ba.take_bytes(2), b'cd') + self.assertEqual(ba.__alloc__(), start_alloc) + self.assertEqual(len(ba), 2 + 1000) + self.assertEqual(ba.take_bytes(2), b'ef') + self.assertEqual(ba.__alloc__(), start_alloc) + self.assertEqual(len(ba), 0 + 1000) + self.assertEqual(ba.__alloc__(), start_alloc) + + # Take over half, alloc shrinks to exact size. + self.assertEqual(ba.take_bytes(501), b'0' * 501) + self.assertEqual(len(ba), 499) + bytes_header_size = sys.getsizeof(b'') + self.assertEqual(ba.__alloc__(), 499 + bytes_header_size) def test_setitem(self): def setitem_as_mapping(b, i, val): @@ -1951,22 +2136,110 @@ def __index__(self): self.assertEqual(instance.ba[0], ord("?"), "Assigned bytearray not altered") self.assertEqual(instance.new_ba, bytearray(0x180), "Wrong object altered") + def test_search_methods_reentrancy_raises_buffererror(self): + # gh-142560: Raise BufferError if buffer mutates during search arg conversion. + class Evil: + def __init__(self, ba): + self.ba = ba + def __buffer__(self, flags): + self.ba.clear() + return memoryview(self.ba) + def __release_buffer__(self, view: memoryview) -> None: + view.release() + def __index__(self): + self.ba.clear() + return ord("A") + + def make_case(): + ba = bytearray(b"A") + return ba, Evil(ba) + + for name in ("find", "count", "index", "rindex", "rfind"): + ba, evil = make_case() + with self.subTest(name): + with self.assertRaises(BufferError): + getattr(ba, name)(evil) + + ba, evil = make_case() + with self.assertRaises(BufferError): + evil in ba + with self.assertRaises(BufferError): + ba.split(evil) + with self.assertRaises(BufferError): + ba.rsplit(evil) + + def test_extend_empty_buffer_overflow(self): + # gh-143003 + class EvilIter: + def __iter__(self): + return self + def __next__(self): + return next(source) + def __length_hint__(self): + return 0 + + # Use ASCII digits so float() takes the fast path that expects a NUL terminator. + source = iter(b'42') + ba = bytearray() + ba.extend(EvilIter()) + + self.assertRaises(ValueError, float, bytearray()) + + def test_hex_use_after_free(self): + # Prevent UAF in bytearray.hex(sep) with re-entrant sep.__len__. + # Regression test for https://github.com/python/cpython/issues/143195. + ba = bytearray(b'\xAA') + + class S(bytes): + def __len__(self): + ba.clear() + return 1 + + self.assertRaises(BufferError, ba.hex, S(b':')) + class AssortedBytesTest(unittest.TestCase): # # Test various combinations of bytes and bytearray # + def test_bytes_repr(self, f=repr): + self.assertEqual(f(b''), "b''") + self.assertEqual(f(b"abc"), "b'abc'") + self.assertEqual(f(bytes([92])), r"b'\\'") + self.assertEqual(f(bytes([0, 1, 254, 255])), r"b'\x00\x01\xfe\xff'") + self.assertEqual(f(b'\a\b\t\n\v\f\r'), r"b'\x07\x08\t\n\x0b\x0c\r'") + self.assertEqual(f(b'"'), """b'"'""") # '"' + self.assertEqual(f(b"'"), '''b"'"''') # "'" + self.assertEqual(f(b"'\""), r"""b'\'"'""") # '\'"' + self.assertEqual(f(b"\"'\""), r"""b'"\'"'""") # '"\'"' + self.assertEqual(f(b"'\"'"), r"""b'\'"\''""") # '\'"\'' + self.assertEqual(f(BytesSubclass(b"abc")), "b'abc'") + + def test_bytearray_repr(self, f=repr): + self.assertEqual(f(bytearray()), "bytearray(b'')") + self.assertEqual(f(bytearray(b'abc')), "bytearray(b'abc')") + self.assertEqual(f(bytearray([92])), r"bytearray(b'\\')") + self.assertEqual(f(bytearray([0, 1, 254, 255])), + r"bytearray(b'\x00\x01\xfe\xff')") + self.assertEqual(f(bytearray([7, 8, 9, 10, 11, 12, 13])), + r"bytearray(b'\x07\x08\t\n\x0b\x0c\r')") + self.assertEqual(f(bytearray(b'"')), """bytearray(b'"')""") # '"' + self.assertEqual(f(bytearray(b"'")), '''bytearray(b"'")''') # "'" + self.assertEqual(f(bytearray(b"'\"")), r"""bytearray(b'\'"')""") # '\'"' + self.assertEqual(f(bytearray(b"\"'\"")), r"""bytearray(b'"\'"')""") # '"\'"' + self.assertEqual(f(bytearray(b'\'"\'')), r"""bytearray(b'\'"\'')""") # '\'"\'' + self.assertEqual(f(ByteArraySubclass(b"abc")), "ByteArraySubclass(b'abc')") + self.assertEqual(f(ByteArraySubclass.Nested(b"abc")), "Nested(b'abc')") + self.assertEqual(f(ByteArraySubclass.Ŭñıçöđë(b"abc")), "Ŭñıçöđë(b'abc')") + + @check_bytes_warnings + def test_bytes_str(self): + self.test_bytes_repr(str) + @check_bytes_warnings - def test_repr_str(self): - for f in str, repr: - self.assertEqual(f(bytearray()), "bytearray(b'')") - self.assertEqual(f(bytearray([0])), "bytearray(b'\\x00')") - self.assertEqual(f(bytearray([0, 1, 254, 255])), - "bytearray(b'\\x00\\x01\\xfe\\xff')") - self.assertEqual(f(b"abc"), "b'abc'") - self.assertEqual(f(b"'"), '''b"'"''') # ''' - self.assertEqual(f(b"'\""), r"""b'\'"'""") # ' + def test_bytearray_str(self): + self.test_bytearray_repr(str) @check_bytes_warnings def test_format(self): @@ -2019,15 +2292,6 @@ def test_from_bytearray(self): b = bytearray(buf) self.assertEqual(b, bytearray(sample)) - @check_bytes_warnings - def test_to_str(self): - self.assertEqual(str(b''), "b''") - self.assertEqual(str(b'x'), "b'x'") - self.assertEqual(str(b'\x80'), "b'\\x80'") - self.assertEqual(str(bytearray(b'')), "bytearray(b'')") - self.assertEqual(str(bytearray(b'x')), "bytearray(b'x')") - self.assertEqual(str(bytearray(b'\x80')), "bytearray(b'\\x80')") - def test_literal(self): tests = [ (b"Wonderful spam", "Wonderful spam"), @@ -2132,13 +2396,20 @@ def fixtype(self, obj): contains_bytes = True + def test_mixed_cmp(self): + a = self.type2test(b'ab') + for t in bytes, bytearray, BytesSubclass, ByteArraySubclass: + with self.subTest(t.__name__): + self._assert_cmp(a, t(b'ab'), 0) + self._assert_cmp(a, t(b'a'), 1) + self._assert_cmp(a, t(b'ac'), -1) + class ByteArrayAsStringTest(FixedStringTest, unittest.TestCase): type2test = bytearray class BytesAsStringTest(FixedStringTest, unittest.TestCase): type2test = bytes - class SubclassTest: def test_basic(self): @@ -2235,7 +2506,10 @@ def __init__(me, *args, **kwargs): class ByteArraySubclass(bytearray): - pass + class Nested(bytearray): + pass + class Ŭñıçöđë(bytearray): + pass class ByteArraySubclassWithSlots(bytearray): __slots__ = ('x', 'y', '__dict__') @@ -2452,10 +2726,6 @@ def iconcat(b, a): # MODIFIES! b.wait() a += c - def irepeat(b, a): # MODIFIES! - b.wait() - a *= 2 - def subscript(b, a): b.wait() try: assert a[0] != 0xdd @@ -2536,6 +2806,22 @@ def zfill(b, a): c = a.zfill(0x400000) assert not c or c[-1] not in (0xdd, 0xcd) + def resize(b, a): # MODIFIES! + b.wait() + a.resize(10) + + def take_bytes(b, a): # MODIFIES! + b.wait() + c = a.take_bytes() + assert not c or c[0] == 48 # '0' + + def take_bytes_n(b, a): # MODIFIES! + b.wait() + try: + c = a.take_bytes(10) + assert c == b'0123456789' + except IndexError: pass + def check(funcs, a=None, *args): if a is None: a = bytearray(b'0' * 0x400000) @@ -2573,9 +2859,10 @@ def check(funcs, a=None, *args): check([clear] + [repeat] * 10) check([clear] + [iconcat] * 10) - check([clear] + [irepeat] * 10) check([clear] + [ass_subscript] * 10) check([clear] + [repr_] * 10) + # gh-148605: Do not test "a *= 2" since it allocates up to 4 GiB using + # 10 threads # value errors @@ -2597,6 +2884,12 @@ def check(funcs, a=None, *args): check([clear] + [startswith] * 10) check([clear] + [strip] * 10) + check([clear] + [resize] * 10) + + check([clear] + [take_bytes] * 10) + check([take_bytes_n] * 10, bytearray(b'0123456789' * 0x400)) + check([take_bytes_n] * 10, bytearray(b'0123456789' * 5)) + check([clear] + [contains] * 10) check([clear] + [subscript] * 10) check([clear2] + [ass_subscript2] * 10, None, bytearray(b'0' * 0x400000)) @@ -2652,6 +2945,22 @@ def check(funcs, it): check([iter_next] + [iter_reduce] * 10, iter(ba)) # for tsan check([iter_next] + [iter_setstate] * 10, iter(ba)) # for tsan + @unittest.skipUnless(support.Py_GIL_DISABLED, 'this test can only possibly fail with GIL disabled') + @threading_helper.reap_threads + @threading_helper.requires_working_threading() + def test_free_threading_bytearray_resize(self): + def resize_stress(ba): + for _ in range(1000): + try: + ba.resize(1000) + ba.resize(1) + except (BufferError, ValueError): + pass + + ba = bytearray(100) + threads = [threading.Thread(target=resize_stress, args=(ba,)) for _ in range(4)] + with threading_helper.start_threads(threads): + pass if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index 3b7897b8a88a45..64293d757331d7 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -66,18 +66,28 @@ class BaseTest(unittest.TestCase): EMPTY_DATA = b'BZh9\x17rE8P\x90\x00\x00\x00\x00' BAD_DATA = b'this is not a valid bzip2 file' - # Some tests need more than one block of uncompressed data. Since one block - # is at least 100,000 bytes, we gather some data dynamically and compress it. - # Note that this assumes that compression works correctly, so we cannot - # simply use the bigger test data for all tests. + # Some tests need more than one block of data. The bz2 module does not + # support flushing a block during compression, so we must read in data until + # there are at least 2 blocks. Since different orderings of Python files may + # be compressed differently, we need to check the compression output for + # more than one bzip2 block header magic, a hex encoding of Pi + # (0x314159265359) + bz2_block_magic = bytes.fromhex('314159265359') test_size = 0 - BIG_TEXT = bytearray(128*1024) + BIG_TEXT = b'' + BIG_DATA = b'' + compressor = BZ2Compressor(1) for fname in glob.glob(os.path.join(glob.escape(os.path.dirname(__file__)), '*.py')): with open(fname, 'rb') as fh: - test_size += fh.readinto(memoryview(BIG_TEXT)[test_size:]) - if test_size > 128*1024: + data = fh.read() + BIG_DATA += compressor.compress(data) + BIG_TEXT += data + # TODO(emmatyping): if it is impossible for a block header to cross + # multiple outputs, we can just search the output of each compress call + # which should be more efficient + if BIG_DATA.count(bz2_block_magic) > 1: + BIG_DATA += compressor.flush() break - BIG_DATA = bz2.compress(BIG_TEXT, compresslevel=1) def setUp(self): fd, self.filename = tempfile.mkstemp() @@ -1022,6 +1032,21 @@ def test_failure(self): # Previously, a second call could crash due to internal inconsistency self.assertRaises(Exception, bzd.decompress, self.BAD_DATA * 30) + def test_decompress_after_data_error(self): + data = bytes.fromhex( + "425a6839314159265359000000000000007fffff000000000000000000000000" + "00000000000000000000000000000000000000e0370000000000000000000000" + "000000000000000000000000000000000000000000000000000083f3" + ) + bzd = BZ2Decompressor() + with self.assertRaisesRegex(OSError, "Invalid data stream"): + bzd.decompress(data) + # Previously, a second call could crash due to internal inconsistency + self.assertFalse(bzd.needs_input) + self.assertFalse(bzd.eof) + with self.assertRaisesRegex(ValueError, "previous error"): + bzd.decompress(b'\x00' * 18) + @support.refcount_test def test_refleaks_in___init__(self): gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount') diff --git a/Lib/test/test_c_locale_coercion.py b/Lib/test/test_c_locale_coercion.py index e4b0b8c451fd45..52323a0ec0ac2b 100644 --- a/Lib/test/test_c_locale_coercion.py +++ b/Lib/test/test_c_locale_coercion.py @@ -12,8 +12,10 @@ from test.support.script_helper import run_python_until_end -# Set the list of ways we expect to be able to ask for the "C" locale -EXPECTED_C_LOCALE_EQUIVALENTS = ["C", "invalid.ascii"] +# Set the list of ways we expect to be able to ask for the "C" locale. +# 'invalid.ascii' is an invalid LOCALE name and so should get turned in to the +# default locale, which is traditionally C. +EXPECTED_C_LOCALE_EQUIVALENTS = ["C", "POSIX", "invalid.ascii"] # Set our expectation for the default encoding used in the C locale # for the filesystem encoding and the standard streams @@ -21,6 +23,7 @@ EXPECTED_C_LOCALE_FS_ENCODING = "ascii" # Set our expectation for the default locale used when none is specified +DEFAULT_LOCALE_IS_C = True EXPECT_COERCION_IN_DEFAULT_LOCALE = True TARGET_LOCALES = ["C.UTF-8", "C.utf8", "UTF-8"] @@ -30,12 +33,12 @@ # Android defaults to using UTF-8 for all system interfaces EXPECTED_C_LOCALE_STREAM_ENCODING = "utf-8" EXPECTED_C_LOCALE_FS_ENCODING = "utf-8" -elif sys.platform.startswith("linux"): - # Linux distros typically alias the POSIX locale directly to the C - # locale. - # TODO: Once https://bugs.python.org/issue30672 is addressed, we'll be - # able to check this case unconditionally - EXPECTED_C_LOCALE_EQUIVALENTS.append("POSIX") +elif support.linked_to_musl(): + # MUSL defaults to utf-8 unless the C locale is set explicitly. + EXPECTED_C_LOCALE_EQUIVALENTS = ["C"] + DEFAULT_LOCALE_IS_C = False + DEFAULT_ENCODING = 'utf-8' + EXPECT_COERCION_IN_DEFAULT_LOCALE = False elif sys.platform.startswith("aix"): # AIX uses iso8859-1 in the C locale, other *nix platforms use ASCII EXPECTED_C_LOCALE_STREAM_ENCODING = "iso8859-1" @@ -44,9 +47,8 @@ # FS encoding is UTF-8 on macOS EXPECTED_C_LOCALE_FS_ENCODING = "utf-8" elif sys.platform == "cygwin": - # Cygwin defaults to using C.UTF-8 - # TODO: Work out a robust dynamic test for this that doesn't rely on - # CPython's own locale handling machinery + DEFAULT_LOCALE_IS_C = False + DEFAULT_ENCODING = "utf-8" EXPECT_COERCION_IN_DEFAULT_LOCALE = False elif sys.platform == "vxworks": # VxWorks defaults to using UTF-8 for all system interfaces @@ -362,9 +364,14 @@ def _check_c_locale_coercion(self, base_var_dict["PYTHONCOERCECLOCALE"] = coerce_c_locale # Check behaviour for the default locale + _fs_encoding = fs_encoding + _stream_encoding = stream_encoding + if not DEFAULT_LOCALE_IS_C and 'LC_ALL' not in extra_vars: + _fs_encoding = _stream_encoding = DEFAULT_ENCODING with self.subTest(default_locale=True, PYTHONCOERCECLOCALE=coerce_c_locale): - if EXPECT_COERCION_IN_DEFAULT_LOCALE: + if (EXPECT_COERCION_IN_DEFAULT_LOCALE + or (not DEFAULT_LOCALE_IS_C and 'LC_ALL' in extra_vars)): _expected_warnings = expected_warnings _coercion_expected = coercion_expected else: @@ -378,8 +385,8 @@ def _check_c_locale_coercion(self, _expected_warnings == [CLI_COERCION_WARNING]): _expected_warnings = None self._check_child_encoding_details(base_var_dict, - fs_encoding, - stream_encoding, + _fs_encoding, + _stream_encoding, None, _expected_warnings, _coercion_expected) @@ -454,8 +461,9 @@ def test_PYTHONCOERCECLOCALE_set_to_one(self): loc = locale.setlocale(locale.LC_CTYPE, "") except locale.Error as e: self.skipTest(str(e)) - if loc == "C": - self.skipTest("test requires LC_CTYPE locale different than C") + if loc in ("C", "POSIX"): + self.skipTest("test requires LC_CTYPE locale different " + "than C and POSIX") if loc in TARGET_LOCALES : self.skipTest("coerced LC_CTYPE locale: %s" % loc) diff --git a/Lib/test/test_c_stack_unwind.py b/Lib/test/test_c_stack_unwind.py new file mode 100644 index 00000000000000..91bf44e463473d --- /dev/null +++ b/Lib/test/test_c_stack_unwind.py @@ -0,0 +1,407 @@ +"""Test in-process C stack unwinders against Python and JIT frames. + +The tests build a recursive Python call stack, ask each _testinternalcapi +unwinder for return addresses, and classify those addresses as Python, JIT, or +other frames. The backends include CPython's manual stack-chain unwinder and +GNU backtrace(), so this module is about in-process C stack unwinding rather +than a single unwind mechanism. GDB integration tests live in test_gdb. +""" + +import json +import os +import platform +import subprocess +import sys +import sysconfig +import unittest + +from test import support +from test.support import import_helper + + +_testinternalcapi = import_helper.import_module("_testinternalcapi") + + +if not support.has_subprocess_support: + raise unittest.SkipTest("test requires subprocess support") + + +STACK_DEPTH = 10 + + +def _manual_unwind_expected(machine): + _Py_WITH_FRAME_POINTERS = getattr( + _testinternalcapi, + "_Py_WITH_FRAME_POINTERS", + -1, + ) + if _Py_WITH_FRAME_POINTERS > 0: + return True + if _Py_WITH_FRAME_POINTERS == 0: + return False + + cflags = " ".join( + value for value in ( + sysconfig.get_config_var("PY_CORE_CFLAGS"), + sysconfig.get_config_var("CFLAGS"), + ) + if value + ) + + if "no-omit-frame-pointer" in cflags: + # For example, configure adds -fno-omit-frame-pointer by default on + # supported GCC-compatible builds. + return True + if "omit-frame-pointer" in cflags: + return False + + if sys.platform == "darwin": + # macOS x86_64/ARM64 always have frame pointer by default. + return True + + if sys.platform == "linux": + if machine in {"aarch64", "arm64"}: + # 32-bit Linux is not supported + if sys.maxsize < 2**32: + return None + return True + if machine == "ppc64le": + # The power ABI specification requires that compilers maintain a + # back chain by default, so unwinding already works without a + # dedicated frame pointer. + # https://openpowerfoundation.org/specifications/64bitelfabi/ + return True + if machine == "x86_64": + final_opt = "" + for opt in cflags.split(): + if opt.startswith('-O'): + final_opt = opt + if final_opt in ("-O0", "-Og", "-O1"): + # Unwinding works if the optimization level is low + return True + + Py_ENABLE_SHARED = int(sysconfig.get_config_var('Py_ENABLE_SHARED') or '0') + if Py_ENABLE_SHARED: + # Unwinding does crash using gcc -O2 or gcc -O3 + # when Python is built with --enable-shared + return "crash" + return False + + if sys.platform == "win32": + # MSVC ignores /Oy and /Oy- on x64/ARM64. + if machine == "arm64": + # Windows ARM64 guidelines recommend frame pointers (x29) for stack walking. + return True + elif machine == "x86_64": + # Windows x64 uses unwind metadata; frame pointers are not required. + return None + return None + + +def _is_arm32_build(): + if sys.maxsize >= 2**32: + return False + + abi = " ".join( + value for value in ( + sysconfig.get_config_var("MULTIARCH"), + sysconfig.get_config_var("HOST_GNU_TYPE"), + sysconfig.get_config_var("SOABI"), + ) + if value + ).lower() + return "arm" in abi + + +def _build_stack_and_unwind(unwinder): + import operator + + def build_stack(n, unwinder, warming_up_caller=False): + if warming_up_caller: + return + if n == 0: + return unwinder() + warming_up = True + while warming_up: + # Can't branch on JIT state inside JITted code, so compute here. + warming_up = ( + hasattr(sys, "_jit") + and sys._jit.is_enabled() + and not sys._jit.is_active() + ) + result = operator.call(build_stack, n - 1, unwinder, warming_up) + return result + + stack = build_stack(STACK_DEPTH, unwinder) + return stack + + +def _classify_stack(stack, jit_enabled): + labels = _testinternalcapi.classify_stack_addresses(stack, jit_enabled) + + annotated = [] + jit_frames = 0 + python_frames = 0 + other_frames = 0 + for idx, (frame, tag) in enumerate(zip(stack, labels)): + addr = int(frame) + if tag == "jit": + jit_frames += 1 + elif tag == "python": + python_frames += 1 + else: + other_frames += 1 + annotated.append((idx, addr, tag)) + return annotated, python_frames, jit_frames, other_frames + + +def _summarize_unwind(stack, unwinder_name): + jit_enabled = hasattr(sys, "_jit") and sys._jit.is_enabled() + jit_backend = _testinternalcapi.get_jit_backend() + ranges = _testinternalcapi.get_jit_code_ranges() if jit_enabled else [] + if jit_enabled and ranges: + print("JIT ranges:") + for start, end in ranges: + print(f" {int(start):#x}-{int(end):#x}") + annotated, python_frames, jit_frames, other_frames = _classify_stack( + stack, jit_enabled + ) + for idx, addr, tag in annotated: + print(f"#{idx:02d} {addr:#x} -> {tag}") + return { + "length": len(stack), + "python_frames": python_frames, + "jit_frames": jit_frames, + "other_frames": other_frames, + "jit_backend": jit_backend, + "unwinder": unwinder_name, + } + + +def _annotate_unwind(unwinder_name="manual_frame_pointer_unwind"): + unwinder = getattr(_testinternalcapi, unwinder_name) + stack = _build_stack_and_unwind(unwinder) + return json.dumps(_summarize_unwind(stack, unwinder_name)) + + +def _annotate_unwind_after_executor_free(unwinder_name="gnu_backtrace_unwind"): + # The first unwind runs at the bottom of _build_stack_and_unwind(), while + # the recursive helper may be executing in JIT code. After it returns, this + # helper is back in normal test code; clearing executor caches should remove + # the old JIT ranges, so the second unwind must not report stale JIT frames. + live = json.loads(_annotate_unwind(unwinder_name)) + + sys._clear_internal_caches() + _testinternalcapi.clear_executor_deletion_list() + + unwinder = getattr(_testinternalcapi, unwinder_name) + after_free = _summarize_unwind(unwinder(), unwinder_name) + return json.dumps({ + "live": live, + "after_free": after_free, + }) + + +def _run_unwind_helper(helper_name, unwinder_name, **env): + code = ( + f"from test.test_c_stack_unwind import {helper_name}; " + f"print({helper_name}({unwinder_name!r}));" + ) + run_env = os.environ.copy() + run_env.update(env) + proc = subprocess.run( + [sys.executable, "-c", code], + env=run_env, + capture_output=True, + text=True, + ) + # Surface the output for debugging/visibility when running this test + if proc.stdout: + print(proc.stdout, end="") + if proc.returncode: + raise RuntimeError( + f"unwind helper failed (rc={proc.returncode}): {proc.stderr or proc.stdout}" + ) + stdout_lines = proc.stdout.strip().splitlines() + if not stdout_lines: + raise RuntimeError("unwind helper produced no output") + try: + return json.loads(stdout_lines[-1]) + except ValueError as exc: + raise RuntimeError( + f"unexpected output from unwind helper: {proc.stdout!r}" + ) from exc + + +def _unwind_result(unwinder_name, **env): + return _run_unwind_helper("_annotate_unwind", unwinder_name, **env) + + +def _unwind_after_executor_free_result(unwinder_name, **env): + return _run_unwind_helper( + "_annotate_unwind_after_executor_free", unwinder_name, **env) + + +@support.requires_gil_enabled("test requires the GIL enabled") +@unittest.skipIf(support.is_wasi, "test not supported on WASI") +class ManualStackUnwindTests(unittest.TestCase): + + def setUp(self): + super().setUp() + + machine = platform.machine().lower() + expected = _manual_unwind_expected(machine) + if expected is None: + self.skipTest( + f"unsupported architecture for manual stack unwind check: {machine}" + ) + if expected == "crash": + self.skipTest(f"test does crash on {machine}") + + try: + _testinternalcapi.manual_frame_pointer_unwind() + except RuntimeError as exc: + if "not supported" in str(exc): + self.skipTest( + "manual stack unwinding not supported on this platform" + ) + raise + self.machine = machine + self.manual_unwind_expected = expected + + def test_manual_unwind_finds_expected_frames(self): + jit_available = hasattr(sys, "_jit") and sys._jit.is_available() + envs = [({"PYTHON_JIT": "0"}, False)] + if jit_available: + envs.append(({"PYTHON_JIT": "1"}, True)) + + for env, using_jit in envs: + with self.subTest(env=env): + result = _unwind_result("manual_frame_pointer_unwind", **env) + jit_frames = result["jit_frames"] + python_frames = result.get("python_frames", 0) + jit_backend = result.get("jit_backend") + if self.manual_unwind_expected: + self.assertGreaterEqual( + python_frames, + STACK_DEPTH, + f"expected to find Python frames on {self.machine} with env {env}", + ) + if using_jit: + if jit_backend == "jit": + self.assertGreater( + jit_frames, + 0, + f"expected to find JIT frames on {self.machine} with env {env}", + ) + else: + # jit_backend is "interpreter" or not present + self.assertEqual( + jit_frames, + 0, + f"unexpected JIT frames counted on {self.machine} with env {env}", + ) + else: + self.assertEqual( + jit_frames, + 0, + f"unexpected JIT frames counted on {self.machine} with env {env}", + ) + else: + self.assertLessEqual( + python_frames, + 1, + f"unexpected Python frames counted on {self.machine} with env {env}", + ) + self.assertEqual( + jit_frames, + 0, + f"unexpected JIT frames counted on {self.machine} with env {env}", + ) + + +@support.requires_gil_enabled("test requires the GIL enabled") +@unittest.skipIf(support.is_wasi, "test not supported on WASI") +@unittest.skipUnless(sys.platform == "linux", "GNU backtrace unwinding test requires Linux") +@unittest.skipIf( + _is_arm32_build(), + "GNU backtrace unwinding skipped on Arm 32-bit", +) +class GnuBacktraceUnwindTests(unittest.TestCase): + + def setUp(self): + super().setUp() + try: + _testinternalcapi.gnu_backtrace_unwind() + except RuntimeError as exc: + if "not supported" in str(exc): + self.skipTest("gnu backtrace unwinding not supported on this platform") + raise + + def test_gnu_backtrace_unwinds_through_jit_frames(self): + jit_available = hasattr(sys, "_jit") and sys._jit.is_available() + envs = [({"PYTHON_JIT": "0"}, False)] + if jit_available: + envs.append(({"PYTHON_JIT": "1"}, True)) + + for env, using_jit in envs: + with self.subTest(env=env): + result = _unwind_result("gnu_backtrace_unwind", **env) + python_frames = result.get("python_frames", 0) + jit_frames = result.get("jit_frames", 0) + jit_backend = result.get("jit_backend") + + self.assertGreaterEqual( + python_frames, + STACK_DEPTH, + f"expected to find Python frames in GNU backtrace with env {env}", + ) + if using_jit and jit_backend == "jit": + self.assertGreater( + jit_frames, + 0, + f"expected GNU backtrace to include JIT frames with env {env}", + ) + else: + self.assertEqual( + jit_frames, + 0, + f"unexpected JIT frames counted in GNU backtrace with env {env}", + ) + + def test_gnu_backtrace_jit_frames_disappear_after_executor_free(self): + if not (hasattr(sys, "_jit") and sys._jit.is_available()): + self.skipTest("JIT is not available") + + result = _unwind_after_executor_free_result( + "gnu_backtrace_unwind", PYTHON_JIT="1") + live = result["live"] + if live.get("jit_backend") != "jit": + self.skipTest("JIT backend is not active") + + self.assertGreaterEqual( + live.get("python_frames", 0), + STACK_DEPTH, + "expected live GNU backtrace to include recursive Python frames", + ) + self.assertGreater( + live.get("jit_frames", 0), + 0, + "expected live GNU backtrace to include JIT frames", + ) + + after_free = result["after_free"] + self.assertGreater( + after_free.get("python_frames", 0), + 0, + "expected GNU backtrace after executor free to include Python frames", + ) + self.assertEqual( + after_free.get("jit_frames", 0), + 0, + "unexpected JIT frames in GNU backtrace after executor free", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py index bc39c86b8cf62d..6ed27b4095bc1d 100644 --- a/Lib/test/test_calendar.py +++ b/Lib/test/test_calendar.py @@ -8,6 +8,7 @@ import io import locale import os +import platform import sys import time @@ -112,18 +113,25 @@ default_format = dict(year="year", month="month", encoding="ascii") +result_2004_css = """""" + result_2004_html = """\ - - - + + - - + + Calendar for 2004 +{css_styles} + - -
    2004
    +
    +
    2004
    @@ -132,7 +140,7 @@
    January
    MonTueWedThuFriSatSun
       1234
    19202122232425
    262728293031 
    -
    +
    @@ -141,7 +149,7 @@
    February
    MonTueWedThuFriSatSun
          1
    16171819202122
    23242526272829
    -
    +
    @@ -150,7 +158,7 @@
    March
    MonTueWedThuFriSatSun
    1234567
    22232425262728
    293031    
    -
    +
    @@ -159,7 +167,7 @@
    April
    MonTueWedThuFriSatSun
       1234
    19202122232425
    2627282930  
    -
    +
    @@ -169,7 +177,7 @@
    May
    MonTueWedThuFriSatSun
         12
    24252627282930
    31      
    -
    +
    @@ -178,7 +186,7 @@
    June
    MonTueWedThuFriSatSun
     123456
    21222324252627
    282930    
    -
    +
    @@ -187,7 +195,7 @@
    July
    MonTueWedThuFriSatSun
       1234
    19202122232425
    262728293031 
    -
    + - - + + - - - - - - + + + + + + - - + + - - - - - - - - - + + + + + + + + +
    @@ -197,7 +205,7 @@
    August
    MonTueWedThuFriSatSun
          1
    23242526272829
    3031     
    -
    + - - + + - - - - - - + + + + + + - - + + - - - - - - - - - + + + + + + + + +
    @@ -206,7 +214,7 @@
    September
    MonTueWedThuFriSatSun
      12345
    20212223242526
    27282930   
    -
    + - - + + - - - - - - + + + + + + - - + + - - - - - - - - - + + + + + + + + +
    @@ -215,7 +223,7 @@
    October
    MonTueWedThuFriSatSun
        123
    18192021222324
    25262728293031
    -
    + - - - - - + + + + +
    @@ -224,7 +232,7 @@
    November
    MonTueWedThuFriSatSun
    1234567
    22232425262728
    2930     
    -
    + - - - - - + + + + +
    @@ -237,6 +245,34 @@ """ +result_2009_6_html = """\ + + + + + +Calendar for 2009 + + + + +
    December
    MonTueWedThuFriSatSun
      12345
    + + + + + + + +
    June 2009
    MonTueWedThuFriSatSun
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
    + + +""" + result_2004_days = [ [[[0, 0, 0, 1, 2, 3, 4], [5, 6, 7, 8, 9, 10, 11], @@ -384,10 +420,12 @@ def check_htmlcalendar_encoding(self, req, res): cal = calendar.HTMLCalendar() format_ = default_format.copy() format_["encoding"] = req or 'utf-8' + format_with_css = {**format_, "css_styles": result_2004_css} + formatted_html = result_2004_html.format(**format_with_css) output = cal.formatyearpage(2004, encoding=req) self.assertEqual( output, - result_2004_html.format(**format_).encode(res) + formatted_html.encode(res) ) def test_output(self): @@ -457,12 +495,17 @@ def test_formatmonth(self): calendar.TextCalendar().formatmonth(0, 2), result_0_02_text ) + def test_formatmonth_with_invalid_month(self): with self.assertRaises(calendar.IllegalMonthError): calendar.TextCalendar().formatmonth(2017, 13) with self.assertRaises(calendar.IllegalMonthError): calendar.TextCalendar().formatmonth(2017, -1) + def test_illegal_month_error_bases(self): + self.assertIsSubclass(calendar.IllegalMonthError, ValueError) + self.assertIsSubclass(calendar.IllegalMonthError, IndexError) + def test_formatmonthname_with_year(self): self.assertEqual( calendar.HTMLCalendar().formatmonthname(2004, 1, withyear=True), @@ -496,6 +539,13 @@ def test_format(self): calendar.format(["1", "2", "3"], colwidth=3, spacing=1) self.assertEqual(out.getvalue().strip(), "1 2 3") + def test_format_html_year_with_month(self): + self.assertEqual( + calendar.HTMLCalendar().formatmonthpage(2009, 6).decode("ascii"), + result_2009_6_html + ) + + class CalendarTestCase(unittest.TestCase): def test_deprecation_warning(self): @@ -546,7 +596,8 @@ def test_days(self): self.assertEqual(value[::-1], list(reversed(value))) def test_months(self): - for attr in "month_name", "month_abbr": + for attr in ("month_name", "month_abbr", "standalone_month_name", + "standalone_month_abbr"): value = getattr(calendar, attr) self.assertEqual(len(value), 13) self.assertEqual(len(value[:]), 13) @@ -556,6 +607,38 @@ def test_months(self): # verify it "acts like a sequence" in two forms of iteration self.assertEqual(value[::-1], list(reversed(value))) + @support.run_with_locale('LC_ALL', 'pl_PL') + @unittest.skipUnless(sys.platform == 'darwin' or platform.libc_ver()[0] == 'glibc', + "Guaranteed to work with glibc and macOS") + def test_standalone_month_name_and_abbr_pl_locale(self): + expected_standalone_month_names = [ + "", "styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", + "lipiec", "sierpień", "wrzesień", "październik", "listopad", + "grudzień" + ] + expected_standalone_month_abbr = [ + "", "sty", "lut", "mar", "kwi", "maj", "cze", + "lip", "sie", "wrz", "paź", "lis", "gru" + ] + self.assertEqual( + list(calendar.standalone_month_name), + expected_standalone_month_names + ) + self.assertEqual( + list(calendar.standalone_month_abbr), + expected_standalone_month_abbr + ) + + def test_standalone_month_name_and_abbr_C_locale(self): + # Ensure that the standalone month names and abbreviations are + # equal to the regular month names and abbreviations for + # the "C" locale. + with calendar.different_locale("C"): + self.assertListEqual(list(calendar.month_name), + list(calendar.standalone_month_name)) + self.assertListEqual(list(calendar.month_abbr), + list(calendar.standalone_month_abbr)) + def test_locale_text_calendar(self): try: cal = calendar.LocaleTextCalendar(locale='') @@ -662,6 +745,57 @@ def test_locale_calendar_formatweekday(self): except locale.Error: raise unittest.SkipTest('cannot set the en_US locale') + # These locales have weekday names all shorter than English's longest + # 'Wednesday'. They should not be abbreviated unnecessarily + @support.run_with_locales("LC_ALL", + 'Chinese', 'zh_CN.UTF-8', + 'French', 'fr_FR.UTF-8', + 'Norwegian', 'nb_NO.UTF-8', + 'Malay', 'ms_MY.UTF8' + ) + def test_locale_calendar_short_weekday_names(self): + names = (datetime.date(2001, 1, i+1).strftime('%A') for i in range(7)) + max_length = max(map(len, names)) + if max_length >= 9: + self.skipTest('weekday names are too long') + + def get_weekday_names(width): + return calendar.TextCalendar().formatweekheader(width).split() + + # Weekday names should not be abbreviated if the width is sufficient + self.assertEqual( + get_weekday_names(max_length), + get_weekday_names(max_length + 10) + ) + + # Any width shorter than necessary should produce abbreviations + self.assertNotEqual( + get_weekday_names(max_length), + get_weekday_names(max_length - 1) + ) + + # These locales have a weekday name longer than 'Wednesday' + # They should be properly abbreviated rather than truncated + @support.run_with_locales("LC_ALL", + 'Portuguese', 'pt_PT.UTF-8', + 'German', 'de_DE.UTF-8', + 'Russian', 'ru_RU.UTF-8', + ) + def test_locale_calendar_long_weekday_names(self): + names = (datetime.date(2001, 1, i+1).strftime('%A') for i in range(7)) + max_length = max(map(len, names)) + abbrev_names = (datetime.date(2001, 1, i+1).strftime('%a') for i in range(7)) + abbrev_max_length = max(map(len, abbrev_names)) + + if max_length <= 9: + self.skipTest('weekday names are too short') + if abbrev_max_length >= 9: + self.skipTest('abbreviated weekday names are too long') + + def get_weekday_names(width): + return calendar.TextCalendar().formatweekheader(width).split() + self.assertEqual(get_weekday_names(abbrev_max_length), get_weekday_names(max_length-1)) + def test_locale_calendar_formatmonthname(self): try: # formatmonthname uses the same month names regardless of the width argument. @@ -934,6 +1068,7 @@ def test_several_leapyears_in_range(self): def conv(s): return s.replace('\n', os.linesep).encode() +@support.force_not_colorized_test_class class CommandLineTestCase(unittest.TestCase): def setUp(self): self.runners = [self.run_cli_ok, self.run_cmd_ok] @@ -987,7 +1122,6 @@ def assertFailure(self, *args): self.assertCLIFails(*args) self.assertCmdFails(*args) - @support.force_not_colorized def test_help(self): stdout = self.run_cmd_ok('-h') self.assertIn(b'usage:', stdout) @@ -1008,7 +1142,6 @@ def test_illegal_arguments(self): self.assertFailure('2004', '1', 'spam') self.assertFailure('2004', '1', '1') self.assertFailure('2004', '1', '1', 'spam') - self.assertFailure('-t', 'html', '2004', '1') def test_output_current_year(self): for run in self.runners: @@ -1098,7 +1231,7 @@ def test_option_type(self): output = run('--type', 'text', '2004') self.assertEqual(output, conv(result_2004_text)) output = run('--type', 'html', '2004') - self.assertStartsWith(output, b'') self.assertIn(b'Calendar for 2004', output) def test_html_output_current_year(self): @@ -1111,15 +1244,25 @@ def test_html_output_current_year(self): def test_html_output_year_encoding(self): for run in self.runners: output = run('-t', 'html', '--encoding', 'ascii', '2004') - self.assertEqual(output, result_2004_html.format(**default_format).encode('ascii')) + format_with_css = default_format.copy() + format_with_css["css_styles"] = result_2004_css + self.assertEqual(output, result_2004_html.format(**format_with_css).encode('ascii')) def test_html_output_year_css(self): self.assertFailure('-t', 'html', '-c') self.assertFailure('-t', 'html', '--css') for run in self.runners: output = run('-t', 'html', '--css', 'custom.css', '2004') - self.assertIn(b'', output) + self.assertIn(b'', output) + + +@support.force_colorized_test_class +class ColorTestCase(unittest.TestCase): + def test_formatmonth_color(self): + today = datetime.date(2026, 5, 4) + cal = calendar._CLIDemoCalendar(highlight_day=today) + output = cal.formatmonth(2026, 5) + self.assertIn("\x1b[30m\x1b[43mMay 2026\x1b[0m\n\x1b[36m", output) class MiscTestCase(unittest.TestCase): @@ -1173,7 +1316,7 @@ def test_formatweek_head(self): def test_format_year(self): self.assertIn( - ('' % + ('
    ' % self.cal.cssclass_year), self.cal.formatyear(2017)) def test_format_year_head(self): diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 1c73aaafb71fd5..f42526aee19417 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -12,6 +12,10 @@ import _testlimitedcapi except ImportError: _testlimitedcapi = None +try: + import _testinternalcapi +except ImportError: + _testinternalcapi = None import struct import collections import itertools @@ -1037,6 +1041,23 @@ def test_unexpected_keyword_suggestion_via_getargs(self): @cpython_only class TestRecursion(unittest.TestCase): + def test_margin_is_sufficient(self): + + def get_sp(): + return _testinternalcapi.get_stack_pointer() + + this_sp = _testinternalcapi.get_stack_pointer() + lower_sp = _testcapi.pyobject_vectorcall(get_sp, (), ()) + if _testcapi._Py_STACK_GROWS_DOWN: + self.assertLess(lower_sp, this_sp) + safe_margin = this_sp - lower_sp + else: + self.assertGreater(lower_sp, this_sp) + safe_margin = lower_sp - this_sp + # Add an (arbitrary) extra 25% for safety + safe_margin = safe_margin * 5 / 4 + self.assertLess(safe_margin, _testinternalcapi.get_stack_margin()) + @skip_on_s390x @unittest.skipIf(is_wasi and Py_DEBUG, "requires deep stack") @skip_if_sanitizer("requires deep stack", thread=True) @@ -1074,6 +1095,14 @@ def c_py_recurse(m): with self.assertRaises(RecursionError): c_py_recurse(100_000) + def test_recursion_with_kwargs(self): + # GH-137883: The interpreter forgot to check the recursion limit when + # calling with keywords. + def recurse_kw(a=0): + recurse_kw(a=0) + with self.assertRaises(RecursionError): + recurse_kw() + class TestFunctionWithManyArgs(unittest.TestCase): def test_function_with_many_args(self): diff --git a/Lib/test/test_capi/test_bytearray.py b/Lib/test/test_capi/test_bytearray.py index 52565ea34c61b8..cb7ad8b22252d9 100644 --- a/Lib/test/test_capi/test_bytearray.py +++ b/Lib/test/test_capi/test_bytearray.py @@ -1,3 +1,4 @@ +import sys import unittest from test.support import import_helper @@ -55,7 +56,9 @@ def test_fromstringandsize(self): self.assertEqual(fromstringandsize(b'', 0), bytearray()) self.assertEqual(fromstringandsize(NULL, 0), bytearray()) self.assertEqual(len(fromstringandsize(NULL, 3)), 3) - self.assertRaises(MemoryError, fromstringandsize, NULL, PY_SSIZE_T_MAX) + self.assertRaises(OverflowError, fromstringandsize, NULL, PY_SSIZE_T_MAX) + self.assertRaises(OverflowError, fromstringandsize, NULL, + PY_SSIZE_T_MAX-sys.getsizeof(b'') + 1) self.assertRaises(SystemError, fromstringandsize, b'abc', -1) self.assertRaises(SystemError, fromstringandsize, b'abc', PY_SSIZE_T_MIN) diff --git a/Lib/test/test_capi/test_bytes.py b/Lib/test/test_capi/test_bytes.py index bc820bd68d9e21..410ebab729c2cf 100644 --- a/Lib/test/test_capi/test_bytes.py +++ b/Lib/test/test_capi/test_bytes.py @@ -299,5 +299,95 @@ def test_join(self): bytes_join(b'', NULL) +class BytesWriterTest(unittest.TestCase): + result_type = bytes + + def create_writer(self, alloc=0, string=b''): + return _testcapi.PyBytesWriter(alloc, string, 0) + + def test_create(self): + # Test PyBytesWriter_Create() + writer = self.create_writer() + self.assertEqual(writer.get_size(), 0) + self.assertEqual(writer.finish(), self.result_type(b'')) + + writer = self.create_writer(3, b'abc') + self.assertEqual(writer.get_size(), 3) + self.assertEqual(writer.finish(), self.result_type(b'abc')) + + writer = self.create_writer(10, b'abc') + self.assertEqual(writer.get_size(), 10) + self.assertEqual(writer.finish_with_size(3), self.result_type(b'abc')) + + def test_write_bytes(self): + # Test PyBytesWriter_WriteBytes() + writer = self.create_writer() + writer.write_bytes(b'Hello World!', -1) + self.assertEqual(writer.finish(), self.result_type(b'Hello World!')) + + writer = self.create_writer() + writer.write_bytes(b'Hello ', -1) + writer.write_bytes(b'World! ', 6) + self.assertEqual(writer.finish(), self.result_type(b'Hello World!')) + + def test_resize(self): + # Test PyBytesWriter_Resize() + writer = self.create_writer() + writer.resize(len(b'number=123456'), b'number=123456') + writer.resize(len(b'number=123456'), b'') + self.assertEqual(writer.get_size(), len(b'number=123456')) + self.assertEqual(writer.finish(), self.result_type(b'number=123456')) + + writer = self.create_writer() + writer.resize(0, b'') + writer.resize(len(b'number=123456'), b'number=123456') + self.assertEqual(writer.finish(), self.result_type(b'number=123456')) + + writer = self.create_writer() + writer.resize(len(b'number='), b'number=') + writer.resize(len(b'number=123456'), b'123456') + self.assertEqual(writer.finish(), self.result_type(b'number=123456')) + + writer = self.create_writer() + writer.resize(len(b'number='), b'number=') + writer.resize(len(b'number='), b'') + writer.resize(len(b'number=123456'), b'123456') + self.assertEqual(writer.finish(), self.result_type(b'number=123456')) + + writer = self.create_writer() + writer.resize(len(b'number'), b'number') + writer.resize(len(b'number='), b'=') + writer.resize(len(b'number=123'), b'123') + writer.resize(len(b'number=123456'), b'456') + self.assertEqual(writer.finish(), self.result_type(b'number=123456')) + + def test_format_i(self): + # Test PyBytesWriter_Format() + writer = self.create_writer() + writer.format_i(b'x=%i', 123456) + self.assertEqual(writer.finish(), self.result_type(b'x=123456')) + + writer = self.create_writer() + writer.format_i(b'x=%i, ', 123) + writer.format_i(b'y=%i', 456) + self.assertEqual(writer.finish(), self.result_type(b'x=123, y=456')) + + def test_example_abc(self): + self.assertEqual(_testcapi.byteswriter_abc(), b'abc') + + def test_example_resize(self): + self.assertEqual(_testcapi.byteswriter_resize(), b'Hello World') + + def test_example_highlevel(self): + self.assertEqual(_testcapi.byteswriter_highlevel(), b'Hello World!') + + +class ByteArrayWriterTest(BytesWriterTest): + result_type = bytearray + + def create_writer(self, alloc=0, string=b''): + return _testcapi.PyBytesWriter(alloc, string, 1) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_codecs.py b/Lib/test/test_capi/test_codecs.py index a0355c7a388c57..1a3f476ed0f30d 100644 --- a/Lib/test/test_capi/test_codecs.py +++ b/Lib/test/test_capi/test_codecs.py @@ -630,7 +630,6 @@ def test_codec_known_encoding(self): for name in [ encoding_name, encoding_name.upper(), - encoding_name.replace('_', '-'), ]: with self.subTest(name): self.assertTrue(_testcapi.codec_known_encoding(name)) diff --git a/Lib/test/test_capi/test_config.py b/Lib/test/test_capi/test_config.py index 04a27de8d84994..f10ad50d3bea7e 100644 --- a/Lib/test/test_capi/test_config.py +++ b/Lib/test/test_capi/test_config.py @@ -62,7 +62,9 @@ def test_config_get(self): ("int_max_str_digits", int, None), ("interactive", bool, None), ("isolated", bool, None), + ("lazy_imports", int, None), ("malloc_stats", bool, None), + ("pymalloc_hugepages", bool, None), ("module_search_paths", list[str], "path"), ("optimization_level", int, None), ("orig_argv", list[str], "orig_argv"), diff --git a/Lib/test/test_capi/test_dict.py b/Lib/test/test_capi/test_dict.py index e726e3d813d888..4cf404e9f56327 100644 --- a/Lib/test/test_capi/test_dict.py +++ b/Lib/test/test_capi/test_dict.py @@ -1,3 +1,4 @@ +import contextlib import unittest from collections import OrderedDict, UserDict from types import MappingProxyType @@ -26,27 +27,46 @@ def gen(): yield 'c' +class FrozenDictSubclass(frozendict): + pass + + +DICT_TYPES = (dict, DictSubclass, OrderedDict) +FROZENDICT_TYPES = (frozendict, FrozenDictSubclass) +ANYDICT_TYPES = DICT_TYPES + FROZENDICT_TYPES +MAPPING_TYPES = (UserDict,) +NOT_DICT_TYPES = FROZENDICT_TYPES + MAPPING_TYPES +NOT_FROZENDICT_TYPES = DICT_TYPES + MAPPING_TYPES +NOT_ANYDICT_TYPES = MAPPING_TYPES +OTHER_TYPES = (lambda: [1], lambda: 42, object) # (list, int, object) + + class CAPITest(unittest.TestCase): def test_dict_check(self): + # Test PyDict_Check() check = _testlimitedcapi.dict_check - self.assertTrue(check({1: 2})) - self.assertTrue(check(OrderedDict({1: 2}))) - self.assertFalse(check(UserDict({1: 2}))) - self.assertFalse(check([1, 2])) - self.assertFalse(check(object())) + for dict_type in DICT_TYPES: + self.assertTrue(check(dict_type({1: 2}))) + for test_type in NOT_DICT_TYPES: + self.assertFalse(check(test_type({1: 2}))) + for test_type in OTHER_TYPES: + self.assertFalse(check(test_type())) # CRASHES check(NULL) def test_dict_checkexact(self): + # Test PyDict_CheckExact() check = _testlimitedcapi.dict_checkexact - self.assertTrue(check({1: 2})) - self.assertFalse(check(OrderedDict({1: 2}))) - self.assertFalse(check(UserDict({1: 2}))) - self.assertFalse(check([1, 2])) - self.assertFalse(check(object())) + for dict_type in DICT_TYPES: + self.assertEqual(check(dict_type({1: 2})), dict_type == dict) + for test_type in NOT_DICT_TYPES: + self.assertFalse(check(test_type({1: 2}))) + for test_type in OTHER_TYPES: + self.assertFalse(check(test_type())) # CRASHES check(NULL) def test_dict_new(self): + # Test PyDict_New() dict_new = _testlimitedcapi.dict_new dct = dict_new() self.assertEqual(dct, {}) @@ -55,73 +75,80 @@ def test_dict_new(self): self.assertIsNot(dct2, dct) def test_dictproxy_new(self): + # Test PyDictProxy_New() dictproxy_new = _testlimitedcapi.dictproxy_new - for dct in {1: 2}, OrderedDict({1: 2}), UserDict({1: 2}): + for dict_type in ANYDICT_TYPES + MAPPING_TYPES: + if dict_type == DictSubclass: + continue + dct = dict_type({1: 2}) proxy = dictproxy_new(dct) self.assertIs(type(proxy), MappingProxyType) self.assertEqual(proxy, dct) with self.assertRaises(TypeError): proxy[1] = 3 self.assertEqual(proxy[1], 2) - dct[1] = 4 - self.assertEqual(proxy[1], 4) + if not isinstance(dct, frozendict): + dct[1] = 4 + self.assertEqual(proxy[1], 4) self.assertRaises(TypeError, dictproxy_new, []) self.assertRaises(TypeError, dictproxy_new, 42) # CRASHES dictproxy_new(NULL) def test_dict_copy(self): + # Test PyDict_Copy() copy = _testlimitedcapi.dict_copy - for dct in {1: 2}, OrderedDict({1: 2}): + for dict_type in DICT_TYPES: + dct = dict_type({1: 2}) dct_copy = copy(dct) self.assertIs(type(dct_copy), dict) self.assertEqual(dct_copy, dct) - self.assertRaises(SystemError, copy, UserDict()) - self.assertRaises(SystemError, copy, []) - self.assertRaises(SystemError, copy, 42) + for test_type in NOT_DICT_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, copy, test_type()) self.assertRaises(SystemError, copy, NULL) def test_dict_clear(self): + # Test PyDict_Clear() clear = _testlimitedcapi.dict_clear - dct = {1: 2} - clear(dct) - self.assertEqual(dct, {}) - - # NOTE: It is not safe to call it with OrderedDict. + for dict_type in DICT_TYPES: + if dict_type == OrderedDict: + # NOTE: It is not safe to call it with OrderedDict. + continue + dct = dict_type({1: 2}) + clear(dct) + self.assertEqual(dct, {}) # Has no effect for non-dicts. - dct = UserDict({1: 2}) - clear(dct) - self.assertEqual(dct, {1: 2}) + for test_type in NOT_DICT_TYPES: + dct = test_type({1: 2}) + clear(dct) + self.assertEqual(dct, {1: 2}) lst = [1, 2] clear(lst) self.assertEqual(lst, [1, 2]) clear(object()) - # CRASHES? clear(NULL) + # CRASHES clear(NULL) def test_dict_size(self): + # Test PyDict_Size() size = _testlimitedcapi.dict_size - self.assertEqual(size({1: 2}), 1) - self.assertEqual(size(OrderedDict({1: 2})), 1) + for dict_type in ANYDICT_TYPES: + self.assertEqual(size(dict_type({1: 2, 3: 4})), 2) - self.assertRaises(SystemError, size, UserDict()) - self.assertRaises(SystemError, size, []) - self.assertRaises(SystemError, size, 42) - self.assertRaises(SystemError, size, object()) + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, size, test_type()) self.assertRaises(SystemError, size, NULL) def test_dict_getitem(self): + # Test PyDict_GetItem() getitem = _testlimitedcapi.dict_getitem - dct = {'a': 1, '\U0001f40d': 2} - self.assertEqual(getitem(dct, 'a'), 1) - self.assertIs(getitem(dct, 'b'), KeyError) - self.assertEqual(getitem(dct, '\U0001f40d'), 2) - - dct2 = DictSubclass(dct) - self.assertEqual(getitem(dct2, 'a'), 1) - self.assertIs(getitem(dct2, 'b'), KeyError) + for dict_type in ANYDICT_TYPES: + dct = dict_type({'a': 1, '\U0001f40d': 2}) + self.assertEqual(getitem(dct, 'a'), 1) + self.assertIs(getitem(dct, 'b'), KeyError) + self.assertEqual(getitem(dct, '\U0001f40d'), 2) with support.catch_unraisable_exception() as cm: self.assertIs(getitem({}, []), KeyError) # unhashable @@ -129,21 +156,20 @@ def test_dict_getitem(self): self.assertEqual(str(cm.unraisable.exc_value), "unhashable type: 'list'") - self.assertIs(getitem(42, 'a'), KeyError) - self.assertIs(getitem([1], 0), KeyError) + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertIs(getitem(test_type(), 'a'), KeyError) + # CRASHES getitem({}, NULL) # CRASHES getitem(NULL, 'a') def test_dict_getitemstring(self): + # Test PyDict_GetItemString() getitemstring = _testlimitedcapi.dict_getitemstring - dct = {'a': 1, '\U0001f40d': 2} - self.assertEqual(getitemstring(dct, b'a'), 1) - self.assertIs(getitemstring(dct, b'b'), KeyError) - self.assertEqual(getitemstring(dct, '\U0001f40d'.encode()), 2) - - dct2 = DictSubclass(dct) - self.assertEqual(getitemstring(dct2, b'a'), 1) - self.assertIs(getitemstring(dct2, b'b'), KeyError) + for dict_type in ANYDICT_TYPES: + dct = dict_type({'a': 1, '\U0001f40d': 2}) + self.assertEqual(getitemstring(dct, b'a'), 1) + self.assertIs(getitemstring(dct, b'b'), KeyError) + self.assertEqual(getitemstring(dct, '\U0001f40d'.encode()), 2) with support.catch_unraisable_exception() as cm: self.assertIs(getitemstring({}, INVALID_UTF8), KeyError) @@ -151,221 +177,220 @@ def test_dict_getitemstring(self): self.assertRegex(str(cm.unraisable.exc_value), "'utf-8' codec can't decode") - self.assertIs(getitemstring(42, b'a'), KeyError) - self.assertIs(getitemstring([], b'a'), KeyError) + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertIs(getitemstring(test_type(), b'a'), KeyError) # CRASHES getitemstring({}, NULL) # CRASHES getitemstring(NULL, b'a') def test_dict_getitemref(self): + # Test PyDict_GetItemRef() getitem = _testcapi.dict_getitemref - dct = {'a': 1, '\U0001f40d': 2} - self.assertEqual(getitem(dct, 'a'), 1) - self.assertIs(getitem(dct, 'b'), KeyError) - self.assertEqual(getitem(dct, '\U0001f40d'), 2) + for dict_type in ANYDICT_TYPES: + dct = dict_type({'a': 1, '\U0001f40d': 2}) + self.assertEqual(getitem(dct, 'a'), 1) + self.assertIs(getitem(dct, 'b'), KeyError) + self.assertEqual(getitem(dct, '\U0001f40d'), 2) - dct2 = DictSubclass(dct) - self.assertEqual(getitem(dct2, 'a'), 1) - self.assertIs(getitem(dct2, 'b'), KeyError) - - self.assertRaises(SystemError, getitem, 42, 'a') self.assertRaises(TypeError, getitem, {}, []) # unhashable - self.assertRaises(SystemError, getitem, [], 1) - self.assertRaises(SystemError, getitem, [], 'a') + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, getitem, test_type(), 'a') # CRASHES getitem({}, NULL) # CRASHES getitem(NULL, 'a') def test_dict_getitemstringref(self): + # Test PyDict_GetItemStringRef() getitemstring = _testcapi.dict_getitemstringref - dct = {'a': 1, '\U0001f40d': 2} - self.assertEqual(getitemstring(dct, b'a'), 1) - self.assertIs(getitemstring(dct, b'b'), KeyError) - self.assertEqual(getitemstring(dct, '\U0001f40d'.encode()), 2) - - dct2 = DictSubclass(dct) - self.assertEqual(getitemstring(dct2, b'a'), 1) - self.assertIs(getitemstring(dct2, b'b'), KeyError) + for dict_type in ANYDICT_TYPES: + dct = dict_type({'a': 1, '\U0001f40d': 2}) + self.assertEqual(getitemstring(dct, b'a'), 1) + self.assertIs(getitemstring(dct, b'b'), KeyError) + self.assertEqual(getitemstring(dct, '\U0001f40d'.encode()), 2) - self.assertRaises(SystemError, getitemstring, 42, b'a') self.assertRaises(UnicodeDecodeError, getitemstring, {}, INVALID_UTF8) - self.assertRaises(SystemError, getitemstring, [], b'a') + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, getitemstring, test_type(), b'a') # CRASHES getitemstring({}, NULL) # CRASHES getitemstring(NULL, b'a') def test_dict_getitemwitherror(self): + # Test PyDict_GetItemWithError() getitem = _testlimitedcapi.dict_getitemwitherror - dct = {'a': 1, '\U0001f40d': 2} - self.assertEqual(getitem(dct, 'a'), 1) - self.assertIs(getitem(dct, 'b'), KeyError) - self.assertEqual(getitem(dct, '\U0001f40d'), 2) + for dict_type in ANYDICT_TYPES: + dct = dict_type({'a': 1, '\U0001f40d': 2}) + self.assertEqual(getitem(dct, 'a'), 1) + self.assertIs(getitem(dct, 'b'), KeyError) + self.assertEqual(getitem(dct, '\U0001f40d'), 2) - dct2 = DictSubclass(dct) - self.assertEqual(getitem(dct2, 'a'), 1) - self.assertIs(getitem(dct2, 'b'), KeyError) - - self.assertRaises(SystemError, getitem, 42, 'a') self.assertRaises(TypeError, getitem, {}, []) # unhashable - self.assertRaises(SystemError, getitem, [], 1) - self.assertRaises(SystemError, getitem, [], 'a') + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, getitem, [], 'a') # CRASHES getitem({}, NULL) # CRASHES getitem(NULL, 'a') def test_dict_contains(self): + # Test PyDict_Contains() contains = _testlimitedcapi.dict_contains - dct = {'a': 1, '\U0001f40d': 2} - self.assertTrue(contains(dct, 'a')) - self.assertFalse(contains(dct, 'b')) - self.assertTrue(contains(dct, '\U0001f40d')) - - dct2 = DictSubclass(dct) - self.assertTrue(contains(dct2, 'a')) - self.assertFalse(contains(dct2, 'b')) + for dict_type in ANYDICT_TYPES: + dct = dict_type({'a': 1, '\U0001f40d': 2}) + self.assertTrue(contains(dct, 'a')) + self.assertFalse(contains(dct, 'b')) + self.assertTrue(contains(dct, '\U0001f40d')) self.assertRaises(TypeError, contains, {}, []) # unhashable + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, contains, test_type(), 'a') + # CRASHES contains({}, NULL) - # CRASHES contains(UserDict(), 'a') - # CRASHES contains(42, 'a') # CRASHES contains(NULL, 'a') def test_dict_contains_string(self): + # Test PyDict_ContainsString() contains_string = _testcapi.dict_containsstring - dct = {'a': 1, '\U0001f40d': 2} - self.assertTrue(contains_string(dct, b'a')) - self.assertFalse(contains_string(dct, b'b')) - self.assertTrue(contains_string(dct, '\U0001f40d'.encode())) - self.assertRaises(UnicodeDecodeError, contains_string, dct, INVALID_UTF8) + for dict_type in ANYDICT_TYPES: + dct = dict_type({'a': 1, '\U0001f40d': 2}) + self.assertTrue(contains_string(dct, b'a')) + self.assertFalse(contains_string(dct, b'b')) + self.assertTrue(contains_string(dct, '\U0001f40d'.encode())) + self.assertRaises(UnicodeDecodeError, contains_string, dct, INVALID_UTF8) - dct2 = DictSubclass(dct) - self.assertTrue(contains_string(dct2, b'a')) - self.assertFalse(contains_string(dct2, b'b')) + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, contains_string, test_type(), b'a') # CRASHES contains({}, NULL) # CRASHES contains(NULL, b'a') + @contextlib.contextmanager + def frozendict_does_not_support(self, what): + errmsg = f'frozendict object does not support item {what}' + with self.assertRaisesRegex(TypeError, errmsg): + yield + def test_dict_setitem(self): + # Test PyDict_SetItem() setitem = _testlimitedcapi.dict_setitem - dct = {} - setitem(dct, 'a', 5) - self.assertEqual(dct, {'a': 5}) - setitem(dct, '\U0001f40d', 8) - self.assertEqual(dct, {'a': 5, '\U0001f40d': 8}) - - dct2 = DictSubclass() - setitem(dct2, 'a', 5) - self.assertEqual(dct2, {'a': 5}) + for dict_type in DICT_TYPES: + dct = dict_type() + setitem(dct, 'a', 5) + self.assertEqual(dct, {'a': 5}) + setitem(dct, '\U0001f40d', 8) + self.assertEqual(dct, {'a': 5, '\U0001f40d': 8}) self.assertRaises(TypeError, setitem, {}, [], 5) # unhashable - self.assertRaises(SystemError, setitem, UserDict(), 'a', 5) - self.assertRaises(SystemError, setitem, [1], 0, 5) - self.assertRaises(SystemError, setitem, 42, 'a', 5) + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('assignment'): + setitem(test_type(), 'a', 5) + for test_type in MAPPING_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, setitem, test_type(), 'a', 5) # CRASHES setitem({}, NULL, 5) # CRASHES setitem({}, 'a', NULL) # CRASHES setitem(NULL, 'a', 5) def test_dict_setitemstring(self): + # Test PyDict_SetItemString() setitemstring = _testlimitedcapi.dict_setitemstring - dct = {} - setitemstring(dct, b'a', 5) - self.assertEqual(dct, {'a': 5}) - setitemstring(dct, '\U0001f40d'.encode(), 8) - self.assertEqual(dct, {'a': 5, '\U0001f40d': 8}) - - dct2 = DictSubclass() - setitemstring(dct2, b'a', 5) - self.assertEqual(dct2, {'a': 5}) + for dict_type in DICT_TYPES: + dct = dict_type() + setitemstring(dct, b'a', 5) + self.assertEqual(dct, {'a': 5}) + setitemstring(dct, '\U0001f40d'.encode(), 8) + self.assertEqual(dct, {'a': 5, '\U0001f40d': 8}) self.assertRaises(UnicodeDecodeError, setitemstring, {}, INVALID_UTF8, 5) - self.assertRaises(SystemError, setitemstring, UserDict(), b'a', 5) - self.assertRaises(SystemError, setitemstring, 42, b'a', 5) + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('assignment'): + setitemstring(test_type(), b'a', 5) + for test_type in MAPPING_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, setitemstring, test_type(), b'a', 5) # CRASHES setitemstring({}, NULL, 5) # CRASHES setitemstring({}, b'a', NULL) # CRASHES setitemstring(NULL, b'a', 5) def test_dict_delitem(self): + # Test PyDict_DelItem() delitem = _testlimitedcapi.dict_delitem - dct = {'a': 1, 'c': 2, '\U0001f40d': 3} - delitem(dct, 'a') - self.assertEqual(dct, {'c': 2, '\U0001f40d': 3}) - self.assertRaises(KeyError, delitem, dct, 'b') - delitem(dct, '\U0001f40d') - self.assertEqual(dct, {'c': 2}) - - dct2 = DictSubclass({'a': 1, 'c': 2}) - delitem(dct2, 'a') - self.assertEqual(dct2, {'c': 2}) - self.assertRaises(KeyError, delitem, dct2, 'b') + for dict_type in DICT_TYPES: + dct = dict_type({'a': 1, 'c': 2, '\U0001f40d': 3}) + delitem(dct, 'a') + self.assertEqual(dct, {'c': 2, '\U0001f40d': 3}) + self.assertRaises(KeyError, delitem, dct, 'b') + delitem(dct, '\U0001f40d') + self.assertEqual(dct, {'c': 2}) self.assertRaises(TypeError, delitem, {}, []) # unhashable - self.assertRaises(SystemError, delitem, UserDict({'a': 1}), 'a') - self.assertRaises(SystemError, delitem, [1], 0) - self.assertRaises(SystemError, delitem, 42, 'a') + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('deletion'): + delitem(test_type({'a': 1}), 'a') + for test_type in MAPPING_TYPES: + self.assertRaises(SystemError, delitem, test_type({'a': 1}), 'a') + for test_type in OTHER_TYPES: + self.assertRaises(SystemError, delitem, test_type(), 'a') # CRASHES delitem({}, NULL) # CRASHES delitem(NULL, 'a') def test_dict_delitemstring(self): + # Test PyDict_DelItemString() delitemstring = _testlimitedcapi.dict_delitemstring - dct = {'a': 1, 'c': 2, '\U0001f40d': 3} - delitemstring(dct, b'a') - self.assertEqual(dct, {'c': 2, '\U0001f40d': 3}) - self.assertRaises(KeyError, delitemstring, dct, b'b') - delitemstring(dct, '\U0001f40d'.encode()) - self.assertEqual(dct, {'c': 2}) - - dct2 = DictSubclass({'a': 1, 'c': 2}) - delitemstring(dct2, b'a') - self.assertEqual(dct2, {'c': 2}) - self.assertRaises(KeyError, delitemstring, dct2, b'b') + for dict_type in DICT_TYPES: + dct = dict_type({'a': 1, 'c': 2, '\U0001f40d': 3}) + delitemstring(dct, b'a') + self.assertEqual(dct, {'c': 2, '\U0001f40d': 3}) + self.assertRaises(KeyError, delitemstring, dct, b'b') + delitemstring(dct, '\U0001f40d'.encode()) + self.assertEqual(dct, {'c': 2}) self.assertRaises(UnicodeDecodeError, delitemstring, {}, INVALID_UTF8) - self.assertRaises(SystemError, delitemstring, UserDict({'a': 1}), b'a') - self.assertRaises(SystemError, delitemstring, 42, b'a') + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('deletion'): + delitemstring(test_type({'a': 1}), b'a') + for test_type in MAPPING_TYPES: + self.assertRaises(SystemError, delitemstring, test_type({'a': 1}), b'a') + for test_type in OTHER_TYPES: + self.assertRaises(SystemError, delitemstring, test_type(), b'a') # CRASHES delitemstring({}, NULL) # CRASHES delitemstring(NULL, b'a') def test_dict_setdefault(self): + # Test PyDict_SetDefault() setdefault = _testcapi.dict_setdefault - dct = {} - self.assertEqual(setdefault(dct, 'a', 5), 5) - self.assertEqual(dct, {'a': 5}) - self.assertEqual(setdefault(dct, 'a', 8), 5) - self.assertEqual(dct, {'a': 5}) - - dct2 = DictSubclass() - self.assertEqual(setdefault(dct2, 'a', 5), 5) - self.assertEqual(dct2, {'a': 5}) - self.assertEqual(setdefault(dct2, 'a', 8), 5) - self.assertEqual(dct2, {'a': 5}) + for dict_type in DICT_TYPES: + dct = dict_type() + self.assertEqual(setdefault(dct, 'a', 5), 5) + self.assertEqual(dct, {'a': 5}) + self.assertEqual(setdefault(dct, 'a', 8), 5) + self.assertEqual(dct, {'a': 5}) self.assertRaises(TypeError, setdefault, {}, [], 5) # unhashable - self.assertRaises(SystemError, setdefault, UserDict(), 'a', 5) - self.assertRaises(SystemError, setdefault, [1], 0, 5) - self.assertRaises(SystemError, setdefault, 42, 'a', 5) + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('assignment'): + setdefault(test_type(), 'a', 5) + for test_type in MAPPING_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, setdefault, test_type(), 'a', 5) # CRASHES setdefault({}, NULL, 5) # CRASHES setdefault({}, 'a', NULL) # CRASHES setdefault(NULL, 'a', 5) def test_dict_setdefaultref(self): + # Test PyDict_SetDefaultRef() setdefault = _testcapi.dict_setdefaultref - dct = {} - self.assertEqual(setdefault(dct, 'a', 5), 5) - self.assertEqual(dct, {'a': 5}) - self.assertEqual(setdefault(dct, 'a', 8), 5) - self.assertEqual(dct, {'a': 5}) - - dct2 = DictSubclass() - self.assertEqual(setdefault(dct2, 'a', 5), 5) - self.assertEqual(dct2, {'a': 5}) - self.assertEqual(setdefault(dct2, 'a', 8), 5) - self.assertEqual(dct2, {'a': 5}) + for dict_type in DICT_TYPES: + dct = dict_type() + self.assertEqual(setdefault(dct, 'a', 5), 5) + self.assertEqual(dct, {'a': 5}) + self.assertEqual(setdefault(dct, 'a', 8), 5) + self.assertEqual(dct, {'a': 5}) self.assertRaises(TypeError, setdefault, {}, [], 5) # unhashable - self.assertRaises(SystemError, setdefault, UserDict(), 'a', 5) - self.assertRaises(SystemError, setdefault, [1], 0, 5) - self.assertRaises(SystemError, setdefault, 42, 'a', 5) + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('assignment'): + setdefault(test_type(), 'a', 5) + for test_type in MAPPING_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, setdefault, test_type(), 'a', 5) # CRASHES setdefault({}, NULL, 5) # CRASHES setdefault({}, 'a', NULL) # CRASHES setdefault(NULL, 'a', 5) def test_mapping_keys_valuesitems(self): + # Test PyDict_Keys(), PyDict_Values() and PyDict_Items() class BadMapping(dict): def keys(self): return None @@ -374,7 +399,8 @@ def values(self): def items(self): return None dict_obj = {'foo': 1, 'bar': 2, 'spam': 3} - for mapping in [dict_obj, DictSubclass(dict_obj), BadMapping(dict_obj)]: + for dict_type in ANYDICT_TYPES + (BadMapping,): + mapping = dict_type(dict_obj) self.assertListEqual(_testlimitedcapi.dict_keys(mapping), list(dict_obj.keys())) self.assertListEqual(_testlimitedcapi.dict_values(mapping), @@ -382,48 +408,60 @@ def items(self): self.assertListEqual(_testlimitedcapi.dict_items(mapping), list(dict_obj.items())) - def test_dict_keys_valuesitems_bad_arg(self): - for mapping in UserDict(), [], object(): + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + mapping = test_type() self.assertRaises(SystemError, _testlimitedcapi.dict_keys, mapping) self.assertRaises(SystemError, _testlimitedcapi.dict_values, mapping) self.assertRaises(SystemError, _testlimitedcapi.dict_items, mapping) def test_dict_next(self): + # Test PyDict_Next() dict_next = _testlimitedcapi.dict_next self.assertIsNone(dict_next({}, 0)) - dct = {'a': 1, 'b': 2, 'c': 3} - pos = 0 - pairs = [] - while True: - res = dict_next(dct, pos) - if res is None: - break - rc, pos, key, value = res - self.assertEqual(rc, 1) - pairs.append((key, value)) - self.assertEqual(pairs, list(dct.items())) + for dict_type in ANYDICT_TYPES: + dct = dict_type({'a': 1, 'b': 2, 'c': 3}) + pos = 0 + pairs = [] + while True: + res = dict_next(dct, pos) + if res is None: + break + rc, pos, key, value = res + self.assertEqual(rc, 1) + pairs.append((key, value)) + self.assertEqual(pairs, list(dct.items())) + + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + dct = test_type() + pos = 0 + self.assertEqual(dict_next(dct, pos), None) # CRASHES dict_next(NULL, 0) def test_dict_update(self): + # Test PyDict_Update() update = _testlimitedcapi.dict_update - for cls1 in dict, DictSubclass: - for cls2 in dict, DictSubclass, UserDict: + for cls1 in DICT_TYPES: + for cls2 in ANYDICT_TYPES + MAPPING_TYPES: dct = cls1({'a': 1, 'b': 2}) update(dct, cls2({'b': 3, 'c': 4})) self.assertEqual(dct, {'a': 1, 'b': 3, 'c': 4}) self.assertRaises(AttributeError, update, {}, []) self.assertRaises(AttributeError, update, {}, 42) - self.assertRaises(SystemError, update, UserDict(), {}) - self.assertRaises(SystemError, update, 42, {}) + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('assignment'): + update(test_type(), {}) + for test_type in MAPPING_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, update, test_type(), {}) self.assertRaises(SystemError, update, {}, NULL) self.assertRaises(SystemError, update, NULL, {}) def test_dict_merge(self): + # Test PyDict_Merge() merge = _testlimitedcapi.dict_merge - for cls1 in dict, DictSubclass: - for cls2 in dict, DictSubclass, UserDict: + for cls1 in DICT_TYPES: + for cls2 in ANYDICT_TYPES + MAPPING_TYPES: dct = cls1({'a': 1, 'b': 2}) merge(dct, cls2({'b': 3, 'c': 4}), 0) self.assertEqual(dct, {'a': 1, 'b': 2, 'c': 4}) @@ -433,14 +471,18 @@ def test_dict_merge(self): self.assertRaises(AttributeError, merge, {}, [], 0) self.assertRaises(AttributeError, merge, {}, 42, 0) - self.assertRaises(SystemError, merge, UserDict(), {}, 0) - self.assertRaises(SystemError, merge, 42, {}, 0) + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('assignment'): + merge(test_type(), {}, 0) + for test_type in MAPPING_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, merge, test_type(), {}, 0) self.assertRaises(SystemError, merge, {}, NULL, 0) self.assertRaises(SystemError, merge, NULL, {}, 0) def test_dict_mergefromseq2(self): + # Test PyDict_MergeFromSeq2() mergefromseq2 = _testlimitedcapi.dict_mergefromseq2 - for cls1 in dict, DictSubclass: + for cls1 in DICT_TYPES: for cls2 in list, iter: dct = cls1({'a': 1, 'b': 2}) mergefromseq2(dct, cls2([('b', 3), ('c', 4)]), 0) @@ -453,8 +495,11 @@ def test_dict_mergefromseq2(self): self.assertRaises(ValueError, mergefromseq2, {}, [(1, 2, 3)], 0) self.assertRaises(TypeError, mergefromseq2, {}, [1], 0) self.assertRaises(TypeError, mergefromseq2, {}, 42, 0) - # CRASHES mergefromseq2(UserDict(), [], 0) - # CRASHES mergefromseq2(42, [], 0) + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('assignment'): + mergefromseq2(test_type(), [], 0) + for test_type in MAPPING_TYPES + OTHER_TYPES: + self.assertRaises(SystemError, mergefromseq2, test_type(), [], 0) # CRASHES mergefromseq2({}, NULL, 0) # CRASHES mergefromseq2(NULL, {}, 0) @@ -463,39 +508,50 @@ def test_dict_pop(self): dict_pop = _testcapi.dict_pop dict_pop_null = _testcapi.dict_pop_null - # key present, get removed value - mydict = {"key": "value", "key2": "value2"} - self.assertEqual(dict_pop(mydict, "key"), (1, "value")) - self.assertEqual(mydict, {"key2": "value2"}) - self.assertEqual(dict_pop(mydict, "key2"), (1, "value2")) - self.assertEqual(mydict, {}) - - # key present, ignore removed value - mydict = {"key": "value", "key2": "value2"} - self.assertEqual(dict_pop_null(mydict, "key"), 1) - self.assertEqual(mydict, {"key2": "value2"}) - self.assertEqual(dict_pop_null(mydict, "key2"), 1) - self.assertEqual(mydict, {}) - - # key missing, expect removed value; empty dict has a fast path - self.assertEqual(dict_pop({}, "key"), (0, NULL)) - self.assertEqual(dict_pop({"a": 1}, "key"), (0, NULL)) - - # key missing, ignored removed value; empty dict has a fast path - self.assertEqual(dict_pop_null({}, "key"), 0) - self.assertEqual(dict_pop_null({"a": 1}, "key"), 0) - - # dict error - not_dict = UserDict({1: 2}) - self.assertRaises(SystemError, dict_pop, not_dict, "key") - self.assertRaises(SystemError, dict_pop_null, not_dict, "key") - - # key error; don't hash key if dict is empty - not_hashable_key = ["list"] - self.assertEqual(dict_pop({}, not_hashable_key), (0, NULL)) - with self.assertRaises(TypeError): - dict_pop({'key': 1}, not_hashable_key) - dict_pop({}, NULL) # key is not checked if dict is empty + for dict_type in DICT_TYPES: + # key present, get removed value + mydict = dict_type({"key": "value", "key2": "value2"}) + self.assertEqual(dict_pop(mydict, "key"), (1, "value")) + self.assertEqual(mydict, {"key2": "value2"}) + self.assertEqual(dict_pop(mydict, "key2"), (1, "value2")) + self.assertEqual(mydict, {}) + + # key present, ignore removed value + mydict = dict_type({"key": "value", "key2": "value2"}) + self.assertEqual(dict_pop_null(mydict, "key"), 1) + self.assertEqual(mydict, {"key2": "value2"}) + self.assertEqual(dict_pop_null(mydict, "key2"), 1) + self.assertEqual(mydict, {}) + + # key missing, expect removed value; empty dict has a fast path + mydict = dict_type() + self.assertEqual(dict_pop(mydict, "key"), (0, NULL)) + mydict = dict_type({"a": 1}) + self.assertEqual(dict_pop(mydict, "key"), (0, NULL)) + + # key missing, ignored removed value; empty dict has a fast path + mydict = dict_type() + self.assertEqual(dict_pop_null(mydict, "key"), 0) + mydict = dict_type({"a": 1}) + self.assertEqual(dict_pop_null(mydict, "key"), 0) + + # key error; don't hash key if dict is empty + not_hashable_key = ["list"] + mydict = dict_type() + self.assertEqual(dict_pop(mydict, not_hashable_key), (0, NULL)) + dict_pop(mydict, NULL) # key is not checked if dict is empty + mydict = dict_type({'key': 1}) + with self.assertRaises(TypeError): + dict_pop(mydict, not_hashable_key) + + # wrong dict type + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('deletion'): + dict_pop(test_type(), "key") + for test_type in MAPPING_TYPES + OTHER_TYPES: + not_dict = test_type() + self.assertRaises(SystemError, dict_pop, not_dict, "key") + self.assertRaises(SystemError, dict_pop_null, not_dict, "key") # CRASHES dict_pop(NULL, "key") # CRASHES dict_pop({"a": 1}, NULL) @@ -505,46 +561,119 @@ def test_dict_popstring(self): dict_popstring = _testcapi.dict_popstring dict_popstring_null = _testcapi.dict_popstring_null - # key present, get removed value - mydict = {"key": "value", "key2": "value2"} - self.assertEqual(dict_popstring(mydict, "key"), (1, "value")) - self.assertEqual(mydict, {"key2": "value2"}) - self.assertEqual(dict_popstring(mydict, "key2"), (1, "value2")) - self.assertEqual(mydict, {}) - - # key present, ignore removed value - mydict = {"key": "value", "key2": "value2"} - self.assertEqual(dict_popstring_null(mydict, "key"), 1) - self.assertEqual(mydict, {"key2": "value2"}) - self.assertEqual(dict_popstring_null(mydict, "key2"), 1) - self.assertEqual(mydict, {}) - - # key missing; empty dict has a fast path - self.assertEqual(dict_popstring({}, "key"), (0, NULL)) - self.assertEqual(dict_popstring_null({}, "key"), 0) - self.assertEqual(dict_popstring({"a": 1}, "key"), (0, NULL)) - self.assertEqual(dict_popstring_null({"a": 1}, "key"), 0) - - # non-ASCII key - non_ascii = '\U0001f40d' - dct = {'\U0001f40d': 123} - self.assertEqual(dict_popstring(dct, '\U0001f40d'.encode()), (1, 123)) - dct = {'\U0001f40d': 123} - self.assertEqual(dict_popstring_null(dct, '\U0001f40d'.encode()), 1) - - # dict error - not_dict = UserDict({1: 2}) - self.assertRaises(SystemError, dict_popstring, not_dict, "key") - self.assertRaises(SystemError, dict_popstring_null, not_dict, "key") - - # key error - self.assertRaises(UnicodeDecodeError, dict_popstring, {1: 2}, INVALID_UTF8) - self.assertRaises(UnicodeDecodeError, dict_popstring_null, {1: 2}, INVALID_UTF8) + for dict_type in DICT_TYPES: + # key present, get removed value + mydict = dict_type({"key": "value", "key2": "value2"}) + self.assertEqual(dict_popstring(mydict, "key"), (1, "value")) + self.assertEqual(mydict, {"key2": "value2"}) + self.assertEqual(dict_popstring(mydict, "key2"), (1, "value2")) + self.assertEqual(mydict, {}) + + # key present, ignore removed value + mydict = dict_type({"key": "value", "key2": "value2"}) + self.assertEqual(dict_popstring_null(mydict, "key"), 1) + self.assertEqual(mydict, {"key2": "value2"}) + self.assertEqual(dict_popstring_null(mydict, "key2"), 1) + self.assertEqual(mydict, {}) + + # key missing; empty dict has a fast path + mydict = dict_type() + self.assertEqual(dict_popstring(mydict, "key"), (0, NULL)) + self.assertEqual(dict_popstring_null(mydict, "key"), 0) + mydict = dict_type({"a": 1}) + self.assertEqual(dict_popstring(mydict, "key"), (0, NULL)) + self.assertEqual(dict_popstring_null(mydict, "key"), 0) + + # non-ASCII key + non_ascii = '\U0001f40d' + dct = dict_type({'\U0001f40d': 123}) + self.assertEqual(dict_popstring(dct, '\U0001f40d'.encode()), (1, 123)) + dct = dict_type({'\U0001f40d': 123}) + self.assertEqual(dict_popstring_null(dct, '\U0001f40d'.encode()), 1) + + # key error + mydict = dict_type({1: 2}) + self.assertRaises(UnicodeDecodeError, dict_popstring, mydict, INVALID_UTF8) + self.assertRaises(UnicodeDecodeError, dict_popstring_null, mydict, INVALID_UTF8) + + # wrong dict type + for test_type in FROZENDICT_TYPES: + with self.frozendict_does_not_support('deletion'): + dict_popstring(test_type(), "key") + for test_type in MAPPING_TYPES + OTHER_TYPES: + not_dict = test_type() + self.assertRaises(SystemError, dict_popstring, not_dict, "key") + self.assertRaises(SystemError, dict_popstring_null, not_dict, "key") # CRASHES dict_popstring(NULL, "key") # CRASHES dict_popstring({}, NULL) # CRASHES dict_popstring({"a": 1}, NULL) + def test_frozendict_check(self): + # Test PyFrozenDict_Check() + check = _testcapi.frozendict_check + for dict_type in FROZENDICT_TYPES: + self.assertTrue(check(dict_type(x=1))) + for dict_type in NOT_FROZENDICT_TYPES + OTHER_TYPES: + self.assertFalse(check(dict_type())) + # CRASHES check(NULL) + + def test_frozendict_checkexact(self): + # Test PyFrozenDict_CheckExact() + check = _testcapi.frozendict_checkexact + for dict_type in FROZENDICT_TYPES: + self.assertEqual(check(dict_type(x=1)), dict_type == frozendict) + for dict_type in NOT_FROZENDICT_TYPES + OTHER_TYPES: + self.assertFalse(check(dict_type())) + # CRASHES check(NULL) + + def test_anydict_check(self): + # Test PyAnyDict_Check() + check = _testcapi.anydict_check + for dict_type in ANYDICT_TYPES: + self.assertTrue(check(dict_type({1: 2}))) + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertFalse(check(test_type())) + # CRASHES check(NULL) + + def test_anydict_checkexact(self): + # Test PyAnyDict_CheckExact() + check = _testcapi.anydict_checkexact + for dict_type in ANYDICT_TYPES: + self.assertEqual(check(dict_type(x=1)), + dict_type in (dict, frozendict)) + for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES: + self.assertFalse(check(test_type())) + # CRASHES check(NULL) + + def test_frozendict_new(self): + # Test PyFrozenDict_New() + frozendict_new = _testcapi.frozendict_new + + for dict_type in ANYDICT_TYPES: + dct = frozendict_new(dict_type({'x': 1})) + self.assertEqual(dct, frozendict(x=1)) + self.assertIs(type(dct), frozendict) + + dct = frozendict_new([('x', 1), ('y', 2)]) + self.assertEqual(dct, frozendict(x=1, y=2)) + self.assertIs(type(dct), frozendict) + + # PyFrozenDict_New(frozendict) returns the same object unmodified + fd = frozendict(a=1, b=2, c=3) + fd2 = frozendict_new(fd) + self.assertIs(fd2, fd) + + fd = FrozenDictSubclass(a=1, b=2, c=3) + fd2 = frozendict_new(fd) + self.assertIsNot(fd2, fd) + self.assertEqual(fd2, fd) + + # PyFrozenDict_New(NULL) creates an empty dictionary + dct = frozendict_new(NULL) + self.assertEqual(dct, frozendict()) + self.assertIs(type(dct), frozendict) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_exceptions.py b/Lib/test/test_capi/test_exceptions.py index ade55338e63b69..51ac41e33ac17a 100644 --- a/Lib/test/test_capi/test_exceptions.py +++ b/Lib/test/test_capi/test_exceptions.py @@ -6,15 +6,16 @@ import textwrap from test import support -from test.support import import_helper +from test.support import import_helper, force_not_colorized from test.support.os_helper import TESTFN, TESTFN_UNDECODABLE from test.support.script_helper import assert_python_failure, assert_python_ok from test.support.testcase import ExceptionIsLikeMixin from .test_misc import decode_stderr -# Skip this test if the _testcapi module isn't available. +# Skip this test if the _testcapi or _testinternalcapi module isn't available. _testcapi = import_helper.import_module('_testcapi') +_testinternalcapi = import_helper.import_module('_testinternalcapi') NULL = None @@ -108,6 +109,26 @@ def __del__(self): b':7: RuntimeWarning: Testing PyErr_WarnEx', ]) + def test__pyerr_setkeyerror(self): + # Test _PyErr_SetKeyError() + _pyerr_setkeyerror = _testinternalcapi._pyerr_setkeyerror + for arg in ( + "key", + # check that a tuple argument is not unpacked + (1, 2, 3), + # PyErr_SetObject(exc_type, exc_value) uses exc_value if it's + # already an exception, but _PyErr_SetKeyError() always creates a + # new KeyError. + KeyError('arg'), + ): + with self.subTest(arg=arg): + with self.assertRaises(KeyError) as cm: + # Test calling _PyErr_SetKeyError() with an exception set + # to check that the function overrides the current + # exception. + _pyerr_setkeyerror(arg) + self.assertEqual(cm.exception.args, (arg,)) + class Test_FatalError(unittest.TestCase): @@ -337,6 +358,10 @@ def test_err_writeunraisable(self): self.assertIsNone(cm.unraisable.err_msg) self.assertIsNone(cm.unraisable.object) + @force_not_colorized + def test_err_writeunraisable_lines(self): + writeunraisable = _testcapi.err_writeunraisable + with (support.swap_attr(sys, 'unraisablehook', None), support.captured_stderr() as stderr): writeunraisable(CustomError('oops!'), hex) @@ -387,6 +412,10 @@ def test_err_formatunraisable(self): self.assertIsNone(cm.unraisable.err_msg) self.assertIsNone(cm.unraisable.object) + @force_not_colorized + def test_err_formatunraisable_lines(self): + formatunraisable = _testcapi.err_formatunraisable + with (support.swap_attr(sys, 'unraisablehook', None), support.captured_stderr() as stderr): formatunraisable(CustomError('oops!'), b'Error in %R', []) diff --git a/Lib/test/test_capi/test_float.py b/Lib/test/test_capi/test_float.py index f7efe0d02549a3..8b25607b6d504f 100644 --- a/Lib/test/test_capi/test_float.py +++ b/Lib/test/test_capi/test_float.py @@ -28,6 +28,23 @@ NAN = float("nan") +def make_nan(size, sign, quiet, payload=None): + if size == 8: + payload_mask = 0x7ffffffffffff + i = (sign << 63) + (0x7ff << 52) + (quiet << 51) + elif size == 4: + payload_mask = 0x3fffff + i = (sign << 31) + (0xff << 23) + (quiet << 22) + elif size == 2: + payload_mask = 0x1ff + i = (sign << 15) + (0x1f << 10) + (quiet << 9) + else: + raise ValueError("size must be either 2, 4, or 8") + if payload is None: + payload = random.randint(not quiet, payload_mask) + return i + payload + + class CAPIFloatTest(unittest.TestCase): def test_check(self): # Test PyFloat_Check() @@ -197,16 +214,11 @@ def test_pack_unpack_roundtrip_for_nans(self): # PyFloat_Pack/Unpack*() API. See also gh-130317 and # e.g. https://developercommunity.visualstudio.com/t/155064 signaling = 0 - quiet = int(not signaling) - if size == 8: - payload = random.randint(signaling, 0x7ffffffffffff) - i = (sign << 63) + (0x7ff << 52) + (quiet << 51) + payload - elif size == 4: - payload = random.randint(signaling, 0x3fffff) - i = (sign << 31) + (0xff << 23) + (quiet << 22) + payload - elif size == 2: - payload = random.randint(signaling, 0x1ff) - i = (sign << 15) + (0x1f << 10) + (quiet << 9) + payload + if _testcapi.nan_msb_is_signaling: + # HP PA RISC and some MIPS CPUs use 0 for quiet, see: + # https://en.wikipedia.org/wiki/NaN#Encoding + signaling = 1 + i = make_nan(size, sign, not signaling) data = bytes.fromhex(f'{i:x}') for endian in (BIG_ENDIAN, LITTLE_ENDIAN): with self.subTest(data=data, size=size, endian=endian): @@ -216,6 +228,32 @@ def test_pack_unpack_roundtrip_for_nans(self): self.assertTrue(math.isnan(value)) self.assertEqual(data1, data2) + @unittest.skipUnless(HAVE_IEEE_754, "requires IEEE 754") + @unittest.skipUnless(sys.maxsize != 2147483647, "requires 64-bit mode") + def test_pack_unpack_nans_for_different_formats(self): + pack = _testcapi.float_pack + unpack = _testcapi.float_unpack + + for endian in (BIG_ENDIAN, LITTLE_ENDIAN): + with self.subTest(endian=endian): + byteorder = "big" if endian == BIG_ENDIAN else "little" + + # Convert sNaN to qNaN, if payload got truncated + data = make_nan(8, 0, False, 0x80001).to_bytes(8, byteorder) + snan_low = unpack(data, endian) + qnan4 = make_nan(4, 0, True, 0).to_bytes(4, byteorder) + qnan2 = make_nan(2, 0, True, 0).to_bytes(2, byteorder) + self.assertEqual(pack(4, snan_low, endian), qnan4) + self.assertEqual(pack(2, snan_low, endian), qnan2) + + # Preserve NaN type, if payload not truncated + data = make_nan(8, 0, False, 0x80000000001).to_bytes(8, byteorder) + snan_high = unpack(data, endian) + snan4 = make_nan(4, 0, False, 16384).to_bytes(4, byteorder) + snan2 = make_nan(2, 0, False, 2).to_bytes(2, byteorder) + self.assertEqual(pack(4, snan_high, endian), snan4) + self.assertEqual(pack(2, snan_high, endian), snan2) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_function.py b/Lib/test/test_capi/test_function.py index 9dca377e28ba42..c1a278e5d4da91 100644 --- a/Lib/test/test_capi/test_function.py +++ b/Lib/test/test_capi/test_function.py @@ -307,10 +307,27 @@ def function_without_closure(): ... _testcapi.function_get_closure(function_without_closure), (1, 2)) self.assertEqual(function_without_closure.__closure__, (1, 2)) + def test_function_get_annotations(self): + # Test PyFunction_GetAnnotations() + def normal(): + pass + + def annofn(arg: int) -> str: + return f'arg = {arg}' + + annotations = _testcapi.function_get_annotations(normal) + self.assertIsNone(annotations) + + annotations = _testcapi.function_get_annotations(annofn) + self.assertIsInstance(annotations, dict) + self.assertEqual(annotations, annofn.__annotations__) + + with self.assertRaises(SystemError): + _testcapi.function_get_annotations(None) + # TODO: test PyFunction_New() # TODO: test PyFunction_NewWithQualName() # TODO: test PyFunction_SetVectorcall() - # TODO: test PyFunction_GetAnnotations() # TODO: test PyFunction_SetAnnotations() # TODO: test PyClassMethod_New() # TODO: test PyStaticMethod_New() diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py index 0b2473bac2be11..bbc09e50eb8e45 100644 --- a/Lib/test/test_capi/test_getargs.py +++ b/Lib/test/test_capi/test_getargs.py @@ -13,7 +13,7 @@ try: import _testinternalcapi except ImportError: - _testinternalcapi = NULL + _testinternalcapi = None # > How about the following counterproposal. This also changes some of # > the other format codes to be a little more regular. diff --git a/Lib/test/test_capi/test_immortal.py b/Lib/test/test_capi/test_immortal.py index 660e8a0e789366..6b882d65a42391 100644 --- a/Lib/test/test_capi/test_immortal.py +++ b/Lib/test/test_capi/test_immortal.py @@ -25,14 +25,14 @@ def test_immortal(self): class TestInternalCAPI(unittest.TestCase): def test_immortal_builtins(self): - for obj in range(-5, 256): + for obj in range(-5, 1025): self.assertTrue(_testinternalcapi.is_static_immortal(obj)) self.assertTrue(_testinternalcapi.is_static_immortal(None)) self.assertTrue(_testinternalcapi.is_static_immortal(False)) self.assertTrue(_testinternalcapi.is_static_immortal(True)) self.assertTrue(_testinternalcapi.is_static_immortal(...)) self.assertTrue(_testinternalcapi.is_static_immortal(())) - for obj in range(300, 400): + for obj in range(1025, 1125): self.assertFalse(_testinternalcapi.is_static_immortal(obj)) for obj in ([], {}, set()): self.assertFalse(_testinternalcapi.is_static_immortal(obj)) diff --git a/Lib/test/test_capi/test_import.py b/Lib/test/test_capi/test_import.py index 57e0316fda8a52..ea845df75ef3a7 100644 --- a/Lib/test/test_capi/test_import.py +++ b/Lib/test/test_capi/test_import.py @@ -289,7 +289,10 @@ def check_executecode_pathnames(self, execute_code_func, object=False): self.check_executecodemodule(execute_code_func, NULL, pathname) # Test NULL pathname and non-NULL cpathname - pyc_filename = importlib.util.cache_from_source(__file__) + try: + pyc_filename = importlib.util.cache_from_source(__file__) + except NotImplementedError: + return py_filename = importlib.util.source_from_cache(pyc_filename) origin = self.check_executecodemodule(execute_code_func, NULL, pyc_filename) if not object: diff --git a/Lib/test/test_capi/test_list.py b/Lib/test/test_capi/test_list.py index 67ed5d0b4f8722..b95b8ba960bd8b 100644 --- a/Lib/test/test_capi/test_list.py +++ b/Lib/test/test_capi/test_list.py @@ -350,6 +350,10 @@ def test_list_extend(self): # CRASHES list_extend(NULL, []) # CRASHES list_extend([], NULL) + def test_uninitialized_list_repr(self): + lst = _testlimitedcapi.list_new(3) + self.assertEqual(repr(lst), '[, , ]') + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_long.py b/Lib/test/test_capi/test_long.py index d3156645eeec2d..d1467caf6880a6 100644 --- a/Lib/test/test_capi/test_long.py +++ b/Lib/test/test_capi/test_long.py @@ -803,6 +803,26 @@ def to_digits(num): self.assertEqual(pylongwriter_create(negative, digits), num, (negative, digits)) + @unittest.skipUnless(support.Py_DEBUG, "need a debug build (Py_DEBUG)") + def test_longwriter_finish(self): + # Test PyLongWriter_Create(0, 3, &digits) with PyLongWriter_Finish() + # where the last digit is left uninitialized + pylongwriter_finish_bug = _testcapi.pylongwriter_finish_bug + with self.assertRaises(SystemError) as cm: + pylongwriter_finish_bug() + self.assertEqual(str(cm.exception), + 'PyLongWriter_Finish: digit 2 is uninitialized') + + def test_bug_143050(self): + with support.adjust_int_max_str_digits(0): + # Bug coming from using _pylong.int_from_string(), that + # currently requires > 6000 decimal digits. + int('-' + '0' * 7000, 10) + _testcapi.test_immortal_small_ints() + # Test also nonzero small int + int('-' + '0' * 7000 + '123', 10) + _testcapi.test_immortal_small_ints() + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index ef950f5df04ad3..3debc6369e89fb 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -22,6 +22,7 @@ from test import support from test.support import MISSING_C_DOCSTRINGS from test.support import import_helper +from test.support import script_helper from test.support import threading_helper from test.support import warnings_helper from test.support import requires_limited_api @@ -306,7 +307,7 @@ def test_getitem_with_error(self): CURRENT_THREAD_REGEX + r' File .*, line 6 in \n' r'\n' - r'Extension modules: _testcapi \(total: 1\)\n') + r'Extension modules: ') else: # Python built with NDEBUG macro defined: # test _Py_CheckFunctionResult() instead. @@ -915,6 +916,18 @@ def genf(): yield gen = genf() self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code) + def test_tp_bases_slot(self): + cls = _testcapi.HeapCTypeWithBasesSlot + self.assertEqual(cls.__bases__, (int,)) + self.assertEqual(cls.__base__, int) + + def test_tp_bases_slot_none(self): + self.assertRaisesRegex( + TypeError, + "metaclass conflict", + _testcapi.create_heapctype_with_none_bases_slot + ) + @requires_limited_api class TestHeapTypeRelative(unittest.TestCase): @@ -1042,13 +1055,13 @@ def test_heaptype_relative_members(self): def test_heaptype_relative_members_errors(self): with self.assertRaisesRegex( SystemError, - r"With Py_RELATIVE_OFFSET, basicsize must be negative"): + r"With Py_RELATIVE_OFFSET, basicsize must be extended"): _testlimitedcapi.make_heaptype_with_member(0, 1234, 0, True) with self.assertRaisesRegex( - SystemError, r"Member offset out of range \(0\.\.-basicsize\)"): + SystemError, r"Member offset out of range \(0\.\.extra_basicsize\)"): _testlimitedcapi.make_heaptype_with_member(0, -8, 1234, True) with self.assertRaisesRegex( - SystemError, r"Member offset out of range \(0\.\.-basicsize\)"): + SystemError, r"Member offset must not be negative"): _testlimitedcapi.make_heaptype_with_member(0, -8, -1, True) Sub = _testlimitedcapi.make_heaptype_with_member(0, -8, 0, True) @@ -1065,7 +1078,7 @@ def test_heaptype_relative_special_members_errors(self): with self.subTest(member_name=member_name): with self.assertRaisesRegex( SystemError, - r"With Py_RELATIVE_OFFSET, basicsize must be negative."): + r"With Py_RELATIVE_OFFSET, basicsize must be extended"): _testlimitedcapi.make_heaptype_with_member( basicsize=sys.getsizeof(object()) + 100, add_relative_flag=True, @@ -1076,7 +1089,7 @@ def test_heaptype_relative_special_members_errors(self): ) with self.assertRaisesRegex( SystemError, - r"Member offset out of range \(0\.\.-basicsize\)"): + r"Member offset must not be negative"): _testlimitedcapi.make_heaptype_with_member( basicsize=-8, add_relative_flag=True, @@ -1085,6 +1098,17 @@ def test_heaptype_relative_special_members_errors(self): member_type=_testlimitedcapi.Py_T_PYSSIZET, member_flags=_testlimitedcapi.Py_READONLY, ) + with self.assertRaisesRegex( + SystemError, + r"Member offset out of range \(0\.\.extra_basicsize\)"): + _testlimitedcapi.make_heaptype_with_member( + basicsize=-8, + add_relative_flag=True, + member_name=member_name, + member_offset=1234, + member_type=_testlimitedcapi.Py_T_PYSSIZET, + member_flags=_testlimitedcapi.Py_READONLY, + ) with self.assertRaisesRegex( SystemError, r"type of %s must be Py_T_PYSSIZET" % member_name): @@ -1641,6 +1665,36 @@ def subthread(): self.assertEqual(actual, int(interpid)) + @threading_helper.requires_working_threading() + def test_pending_call_creates_thread(self): + source = """ + import _testinternalcapi + import threading + import time + + + def output(): + print(24) + time.sleep(1) + print(42) + + + def callback(): + threading.Thread(target=output).start() + + + def create_pending_call(): + time.sleep(1) + _testinternalcapi.simple_pending_call(callback) + + + threading.Thread(target=create_pending_call).start() + """ + return_code, stdout, stderr = script_helper.assert_python_ok('-c', textwrap.dedent(source)) + self.assertEqual(return_code, 0) + self.assertEqual(stdout, f"24{os.linesep}42{os.linesep}".encode("utf-8")) + self.assertEqual(stderr, b"") + class SubinterpreterTest(unittest.TestCase): @@ -1949,6 +2003,43 @@ def test_module_state_shared_in_global(self): subinterp_attr_id = os.read(r, 100) self.assertEqual(main_attr_id, subinterp_attr_id) + @threading_helper.requires_working_threading() + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + @requires_subinterpreters + def test_pending_call_creates_thread_subinterpreter(self): + interpreters = import_helper.import_module("concurrent.interpreters") + r, w = os.pipe() + source = f"""if True: + import _testinternalcapi + import threading + import time + import os + + + def output(): + time.sleep(1) + os.write({w}, b"x") + os.close({w}) + + + def callback(): + threading.Thread(target=output).start() + + + def create_pending_call(): + time.sleep(1) + _testinternalcapi.simple_pending_call(callback) + + + threading.Thread(target=create_pending_call).start() + """ + interp = interpreters.create() + interp.exec(source) + interp.close() + data = os.read(r, 1) + self.assertEqual(data, b"x") + os.close(r) + @requires_subinterpreters class InterpreterConfigTests(unittest.TestCase): @@ -2790,6 +2881,88 @@ def func(): self.do_test(func, names) +class Test_Pep523AllowSpecialization(unittest.TestCase): + """Tests for _PyInterpreterState_SetEvalFrameFunc with + allow_specialization=1.""" + + def test_is_specialization_enabled_default(self): + # With no custom eval frame, specialization should be enabled + self.assertTrue(_testinternalcapi.is_specialization_enabled()) + + def test_is_specialization_enabled_with_eval_frame(self): + # Setting eval frame with allow_specialization=0 disables specialization + try: + _testinternalcapi.set_eval_frame_record([]) + self.assertFalse(_testinternalcapi.is_specialization_enabled()) + finally: + _testinternalcapi.set_eval_frame_default() + + def test_is_specialization_enabled_after_restore(self): + # Restoring the default eval frame re-enables specialization + try: + _testinternalcapi.set_eval_frame_record([]) + self.assertFalse(_testinternalcapi.is_specialization_enabled()) + finally: + _testinternalcapi.set_eval_frame_default() + self.assertTrue(_testinternalcapi.is_specialization_enabled()) + + def test_is_specialization_enabled_with_allow(self): + # Setting eval frame with allow_specialization=1 keeps it enabled + try: + _testinternalcapi.set_eval_frame_interp([]) + self.assertTrue(_testinternalcapi.is_specialization_enabled()) + finally: + _testinternalcapi.set_eval_frame_default() + + def test_allow_specialization_call(self): + def func(): + pass + + def func_outer(): + func() + + actual_calls = [] + try: + _testinternalcapi.set_eval_frame_interp( + actual_calls) + for i in range(SUFFICIENT_TO_DEOPT_AND_SPECIALIZE * 2): + func_outer() + finally: + _testinternalcapi.set_eval_frame_default() + + # With specialization enabled, calls to inner() will dispatch + # through the installed frame evaluator + self.assertEqual(actual_calls.count("func"), 0) + + # But the normal interpreter loop still shouldn't be inlining things + self.assertNotEqual(actual_calls.count("func_outer"), 0) + + def test_no_specialization_call(self): + # Without allow_specialization, ALL calls go through the eval frame. + # This is the existing PEP 523 behavior. + def inner(x=42): + pass + def func(): + inner() + + # Pre-specialize + for _ in range(SUFFICIENT_TO_DEOPT_AND_SPECIALIZE): + func() + + actual_calls = [] + try: + _testinternalcapi.set_eval_frame_record(actual_calls) + for _ in range(SUFFICIENT_TO_DEOPT_AND_SPECIALIZE): + func() + finally: + _testinternalcapi.set_eval_frame_default() + + # Without allow_specialization, every call including inner() goes + # through the eval frame + expected = ["func", "inner"] * SUFFICIENT_TO_DEOPT_AND_SPECIALIZE + self.assertEqual(actual_calls, expected) + + @unittest.skipUnless(support.Py_GIL_DISABLED, 'need Py_GIL_DISABLED') class TestPyThreadId(unittest.TestCase): def test_py_thread_id(self): diff --git a/Lib/test/test_capi/test_modsupport.py b/Lib/test/test_capi/test_modsupport.py new file mode 100644 index 00000000000000..29bebf847aaba2 --- /dev/null +++ b/Lib/test/test_capi/test_modsupport.py @@ -0,0 +1,173 @@ +import sys +import unittest +import sysconfig + +from test.support import subTests +from test.support import import_helper + +_testcapi = import_helper.import_module('_testcapi') + + +class Test_ABIInfo_Check(unittest.TestCase): + @subTests('modname', (None, 'test_mod')) + def test_zero(self, modname): + _testcapi.pyabiinfo_check(modname, 0, 0, 0, 0, 0) + _testcapi.pyabiinfo_check(modname, 1, 0, 0, 0, 0) + + def test_large_major_version(self): + with self.assertRaisesRegex(ImportError, + '^PyABIInfo version too high$'): + _testcapi.pyabiinfo_check(None, 2, 0, 0, 0, 0) + with self.assertRaisesRegex(ImportError, + '^test_mod: PyABIInfo version too high$'): + _testcapi.pyabiinfo_check("test_mod", 2, 0, 0, 0, 0) + + @subTests('modname', (None, 'test_mod')) + def test_large_minor_version(self, modname): + _testcapi.pyabiinfo_check(modname, 1, 2, 0, 0, 0) + + @subTests('modname', (None, 'test_mod')) + @subTests('major', (0, 1)) + @subTests('minor', (0, 1, 9)) + @subTests('build', (0, sys.hexversion)) + def test_positive_regular(self, modname, major, minor, build): + ver = sys.hexversion + truncated = ver & 0xffff0000 + filled = truncated | 0x12b8 + maxed = truncated | 0xffff + for abi_version in (0, ver, truncated, filled, maxed): + with self.subTest(abi_version=abi_version): + _testcapi.pyabiinfo_check(modname, major, minor, 0, + build, abi_version) + + @subTests('modname', (None, 'test_mod')) + @subTests('minor', (0, 1, 9)) + @subTests('build', (0, sys.hexversion)) + @subTests('offset', (+0x00010000, -0x00010000)) + def test_negative_regular(self, modname, minor, build, offset): + ver = sys.hexversion + offset + truncated = ver & 0xffff0000 + filled = truncated | 0x12b8 + maxed = truncated | 0xffff + for abi_version in (ver, truncated, filled, maxed): + with self.subTest(abi_version=abi_version): + with self.assertRaisesRegex( + ImportError, + r'incompatible ABI version \(3\.\d+\)$'): + _testcapi.pyabiinfo_check(modname, 1, minor, 0, + build, + abi_version) + + @subTests('modname', (None, 'test_mod')) + @subTests('major', (0, 1)) + @subTests('minor', (0, 1, 9)) + @subTests('build', (0, sys.hexversion)) + @subTests('abi_version', ( + 0, + 0x03020000, + sys.hexversion, + sys.hexversion & 0xffff0000, + sys.hexversion - 0x00010000, + )) + def test_positive_stable(self, modname, major, minor, build, abi_version): + _testcapi.pyabiinfo_check(modname, major, minor, + _testcapi.PyABIInfo_STABLE, + build, + abi_version) + + @subTests('modname', (None, 'test_mod')) + @subTests('minor', (0, 1, 9)) + @subTests('build', (0, sys.hexversion)) + @subTests('abi_version_and_msg', ( + (1, 'invalid'), + (3, 'invalid'), + (0x0301ffff, 'invalid'), + ((sys.hexversion & 0xffff0000) + 0x00010000, 'incompatible future'), + (sys.hexversion + 0x00010000, 'incompatible future'), + (0x04000000, 'incompatible future'), + )) + def test_negative_stable(self, modname, minor, build, abi_version_and_msg): + abi_version, msg = abi_version_and_msg + with self.assertRaisesRegex( + ImportError, + rf'{msg} stable ABI version \(\d+\.\d+\)$'): + _testcapi.pyabiinfo_check(modname, 1, minor, + _testcapi.PyABIInfo_STABLE, + build, + abi_version) + + @subTests('modname', (None, 'test_mod')) + @subTests('major', (0, 1)) + @subTests('minor', (0, 1, 9)) + @subTests('build', (0, sys.hexversion)) + @subTests('abi_version', (0, sys.hexversion)) + def test_positive_internal(self, modname, major, minor, build, abi_version): + _testcapi.pyabiinfo_check(modname, major, minor, + _testcapi.PyABIInfo_INTERNAL, + build, + abi_version) + + @subTests('modname', (None, 'test_mod')) + @subTests('minor', (0, 1, 9)) + @subTests('build', (0, sys.hexversion)) + @subTests('abi_version', ( + sys.hexversion - 0x00010000, + sys.hexversion - 1, + sys.hexversion + 1, + sys.hexversion + 0x00010000, + )) + def test_negative_internal(self, modname, minor, build, abi_version): + with self.assertRaisesRegex( + ImportError, + r'incompatible internal ABI \(0x[\da-f]+ != 0x[\da-f]+\)$'): + _testcapi.pyabiinfo_check(modname, 1, minor, + _testcapi.PyABIInfo_INTERNAL, + build, + abi_version) + + @subTests('modname', (None, 'test_mod')) + @subTests('minor', (0, 1, 9)) + @subTests('build', (0, sys.hexversion)) + @subTests('ft_flag', ( + 0, + (_testcapi.PyABIInfo_FREETHREADED + if sysconfig.get_config_var("Py_GIL_DISABLED") + else _testcapi.PyABIInfo_GIL), + _testcapi.PyABIInfo_FREETHREADING_AGNOSTIC, + )) + def test_positive_freethreading(self, modname, minor, build, ft_flag): + self.assertEqual(ft_flag & _testcapi.PyABIInfo_FREETHREADING_AGNOSTIC, + ft_flag) + _testcapi.pyabiinfo_check(modname, 1, minor, ft_flag, build, 0) + + @subTests('modname', (None, 'test_mod')) + @subTests('minor', (0, 1, 9)) + @subTests('build', (0, sys.hexversion)) + def test_negative_freethreading(self, modname, minor, build): + if sysconfig.get_config_var("Py_GIL_DISABLED"): + ft_flag = _testcapi.PyABIInfo_GIL + msg = "incompatible with free-threaded CPython" + else: + ft_flag = _testcapi.PyABIInfo_FREETHREADED + msg = "only compatible with free-threaded CPython" + with self.assertRaisesRegex(ImportError, msg): + _testcapi.pyabiinfo_check(modname, 1, minor, ft_flag, build, 0) + + +class TestModsupport(unittest.TestCase): + def test_pyarg_parsearray(self): + func = _testcapi.pyarg_parsearray + self.assertEqual(func(1, 2), (1, 2, 0)) + self.assertEqual(func(1, 2, 3), (1, 2, 3)) + self.assertRaises(TypeError, func, 1) + self.assertRaises(TypeError, func, "str", 2) + + def test_funcandkeywords(self): + func = _testcapi.pyarg_parsearrayandkeywords + self.assertEqual(func(1, 2), (1, 2, 0)) + self.assertEqual(func(1, 2, 3), (1, 2, 3)) + self.assertEqual(func(1, b=2), (1, 2, 0)) + self.assertEqual(func(1, b=2, c=3), (1, 2, 3)) + self.assertRaises(TypeError, func, 1) + self.assertRaises(TypeError, func, "str", 2) + self.assertRaises(TypeError, func, 1, z=2) diff --git a/Lib/test/test_capi/test_module.py b/Lib/test/test_capi/test_module.py new file mode 100644 index 00000000000000..29e1ce5b9af87f --- /dev/null +++ b/Lib/test/test_capi/test_module.py @@ -0,0 +1,206 @@ +# The C functions used by this module are in: +# Modules/_testcapi/module.c + +import unittest +import types +from test.support import import_helper, subTests, requires_gil_enabled + +# Skip this test if the _testcapi module isn't available. +_testcapi = import_helper.import_module('_testcapi') + + +class FakeSpec: + name = 'testmod' + +DEF_SLOTS = ( + 'Py_mod_name', 'Py_mod_doc', 'Py_mod_state_size', 'Py_mod_methods', + 'Py_mod_state_traverse', 'Py_mod_state_clear', 'Py_mod_state_free', + 'Py_mod_token', +) + +def def_and_token(mod): + return ( + _testcapi.pymodule_get_def(mod), + _testcapi.pymodule_get_token(mod), + ) + +class TestModFromSlotsAndSpec(unittest.TestCase): + def test_empty(self): + with self.assertRaises(SystemError): + _testcapi.module_from_slots_empty(FakeSpec()) + + @requires_gil_enabled("minimal slots re-enable GIL") + def test_minimal(self): + mod = _testcapi.module_from_slots_minimal(FakeSpec()) + self.assertIsInstance(mod, types.ModuleType) + self.assertEqual(def_and_token(mod), (0, 0)) + self.assertEqual(mod.__name__, 'testmod') + size = _testcapi.pymodule_get_state_size(mod) + self.assertEqual(size, 0) + + def test_null_slots(self): + with self.assertRaises(SystemError): + _testcapi.module_from_slots_null(FakeSpec()) + + def test_none_spec(self): + # The spec currently must contain a name + with self.assertRaises(AttributeError): + _testcapi.module_from_slots_empty(None) + with self.assertRaises(AttributeError): + _testcapi.module_from_slots_name(None) + + def test_name(self): + # Py_mod_name (and PyModuleDef.m_name) are currently ignored when + # spec is given. + # We still test that it's accepted. + mod = _testcapi.module_from_slots_name(FakeSpec()) + self.assertIsInstance(mod, types.ModuleType) + self.assertEqual(def_and_token(mod), (0, 0)) + self.assertEqual(mod.__name__, 'testmod') + self.assertEqual(mod.__doc__, None) + + def test_doc(self): + mod = _testcapi.module_from_slots_doc(FakeSpec()) + self.assertIsInstance(mod, types.ModuleType) + self.assertEqual(def_and_token(mod), (0, 0)) + self.assertEqual(mod.__name__, 'testmod') + self.assertEqual(mod.__doc__, 'the docstring') + + def test_size(self): + mod = _testcapi.module_from_slots_size(FakeSpec()) + self.assertIsInstance(mod, types.ModuleType) + self.assertEqual(def_and_token(mod), (0, 0)) + self.assertEqual(mod.__name__, 'testmod') + self.assertEqual(mod.__doc__, None) + size = _testcapi.pymodule_get_state_size(mod) + self.assertEqual(size, 123) + + def test_methods(self): + mod = _testcapi.module_from_slots_methods(FakeSpec()) + self.assertIsInstance(mod, types.ModuleType) + self.assertEqual(def_and_token(mod), (0, 0)) + self.assertEqual(mod.__name__, 'testmod') + self.assertEqual(mod.__doc__, None) + self.assertEqual(mod.a_method(456), (mod, 456)) + + def test_gc(self): + mod = _testcapi.module_from_slots_gc(FakeSpec()) + self.assertIsInstance(mod, types.ModuleType) + self.assertEqual(def_and_token(mod), (0, 0)) + self.assertEqual(mod.__name__, 'testmod') + self.assertEqual(mod.__doc__, None) + + # Check that the requested hook functions (which module_from_slots_gc + # stores as attributes) match what's in the module (as retrieved by + # _testinternalcapi.module_get_gc_hooks) + _testinternalcapi = import_helper.import_module('_testinternalcapi') + traverse, clear, free = _testinternalcapi.module_get_gc_hooks(mod) + self.assertEqual(traverse, mod.traverse) + self.assertEqual(clear, mod.clear) + self.assertEqual(free, mod.free) + + def test_token(self): + mod = _testcapi.module_from_slots_token(FakeSpec()) + self.assertIsInstance(mod, types.ModuleType) + self.assertEqual(def_and_token(mod), (0, _testcapi.module_test_token)) + self.assertEqual(mod.__name__, 'testmod') + self.assertEqual(mod.__doc__, None) + + def test_exec(self): + mod = _testcapi.module_from_slots_exec(FakeSpec()) + self.assertIsInstance(mod, types.ModuleType) + self.assertEqual(def_and_token(mod), (0, 0)) + self.assertEqual(mod.__name__, 'testmod') + self.assertEqual(mod.__doc__, None) + self.assertEqual(mod.a_number, 456) + + def test_create(self): + spec = FakeSpec() + spec._gimme_this = "not a module object" + mod = _testcapi.module_from_slots_create(spec) + self.assertIsInstance(mod, str) + self.assertEqual(mod, "not a module object") + with self.assertRaises(TypeError): + _testcapi.pymodule_get_def(mod), + with self.assertRaises(TypeError): + _testcapi.pymodule_get_token(mod) + + def test_def_slot(self): + """Slots cannot contradict PyModuleDef fields""" + for name in DEF_SLOTS: + with self.subTest(name): + spec = FakeSpec() + spec._test_slot_id = getattr(_testcapi, name) + with self.assertRaises(SystemError) as cm: + _testcapi.module_from_def_slot(spec) + self.assertIn(name, str(cm.exception)) + self.assertIn("PyModuleDef", str(cm.exception)) + + def test_def_slot_parrot(self): + """Slots with same value as PyModuleDef fields are allowed""" + spec = FakeSpec() + _testcapi.module_from_def_slot_parrot(spec) + + def test_repeated_def_slot(self): + """Slots that replace PyModuleDef fields can't be repeated""" + for name in (*DEF_SLOTS, 'Py_mod_exec'): + with self.subTest(name): + spec = FakeSpec() + spec._test_slot_id = getattr(_testcapi, name) + with self.assertRaises(SystemError) as cm: + _testcapi.module_from_slots_repeat_slot(spec) + self.assertIn(name, str(cm.exception)) + self.assertRegex( + str(cm.exception), + rf"^module( [_\w]+)? has multiple {name}( \(\d+\))? slots$") + + def test_null_def_slot(self): + """Slots that replace PyModuleDef fields can't be NULL""" + for name in {*DEF_SLOTS, 'Py_mod_exec'} - {'Py_mod_state_size'}: + with self.subTest(name): + spec = FakeSpec() + spec._test_slot_id = getattr(_testcapi, name) + with self.assertRaises(SystemError) as cm: + _testcapi.module_from_slots_null_slot(spec) + self.assertIn(name, str(cm.exception)) + self.assertIn("NULL", str(cm.exception)) + + def test_bad_abiinfo(self): + """Slots that incompatible ABI is rejected""" + with self.assertRaises(ImportError) as cm: + _testcapi.module_from_bad_abiinfo(FakeSpec()) + + def test_multiple_abiinfo(self): + """Slots that Py_mod_abiinfo can be repeated""" + mod = _testcapi.module_from_multiple_abiinfo(FakeSpec()) + self.assertEqual(mod.__name__, 'testmod') + + def test_def_multiple_exec(self): + """PyModule_Exec runs all exec slots of PyModuleDef-defined module""" + mod = _testcapi.module_from_def_multiple_exec(FakeSpec()) + self.assertFalse(hasattr(mod, 'a_number')) + _testcapi.pymodule_exec(mod) + self.assertEqual(mod.a_number, 456) + self.assertEqual(mod.another_number, 789) + _testcapi.pymodule_exec(mod) + self.assertEqual(mod.a_number, 456) + self.assertEqual(mod.another_number, -789) + def_ptr, token = def_and_token(mod) + self.assertEqual(def_ptr, token) + + def test_def_token(self): + """In PyModuleDef-defined modules, the def is the token""" + mod = _testcapi.module_from_def_multiple_exec(FakeSpec()) + def_ptr, token = def_and_token(mod) + self.assertEqual(def_ptr, token) + self.assertGreater(def_ptr, 0) + + @subTests('name, expected_size', [ + (__name__, 0), # Python module + ('_testsinglephase', -1), # single-phase init + ('sys', -1), + ]) + def test_get_state_size(self, name, expected_size): + mod = import_helper.import_module(name) + size = _testcapi.pymodule_get_state_size(mod) + self.assertEqual(size, expected_size) diff --git a/Lib/test/test_capi/test_object.py b/Lib/test/test_capi/test_object.py index d4056727d07fbf..433afac875aa7b 100644 --- a/Lib/test/test_capi/test_object.py +++ b/Lib/test/test_capi/test_object.py @@ -1,4 +1,6 @@ import enum +import os +import pickle import sys import textwrap import unittest @@ -13,6 +15,9 @@ _testcapi = import_helper.import_module('_testcapi') _testinternalcapi = import_helper.import_module('_testinternalcapi') +NULL = None +STDERR_FD = 2 + class Constant(enum.IntEnum): Py_CONSTANT_NONE = 0 @@ -59,6 +64,35 @@ def test_get_constant_borrowed(self): self.check_get_constant(_testlimitedcapi.get_constant_borrowed) +class SentinelTest(unittest.TestCase): + + def test_pysentinel_new(self): + marker = _testcapi.pysentinel_new("CAPI_SENTINEL", __name__) + self.assertIs(type(marker), sentinel) + self.assertTrue(_testcapi.pysentinel_check(marker)) + self.assertFalse(_testcapi.pysentinel_check(object())) + self.assertTrue(_testcapi.pysentinel_checkexact(marker)) + self.assertFalse(_testcapi.pysentinel_checkexact(object())) + self.assertEqual(marker.__name__, "CAPI_SENTINEL") + self.assertEqual(marker.__module__, __name__) + self.assertEqual(repr(marker), "CAPI_SENTINEL") + + no_module = _testcapi.pysentinel_new("NO_MODULE") + self.assertIs(type(no_module), sentinel) + self.assertEqual(no_module.__name__, "NO_MODULE") + self.assertIs(no_module.__module__, None) + + with_repr = _testcapi.pysentinel_new("WITH_REPR", __name__, "custom repr") + self.assertIs(type(with_repr), sentinel) + self.assertEqual(with_repr.__name__, "WITH_REPR") + self.assertEqual(with_repr.__module__, __name__) + self.assertEqual(repr(with_repr), "custom repr") + + globals()["CAPI_SENTINEL"] = marker + self.addCleanup(globals().pop, "CAPI_SENTINEL", None) + self.assertIs(pickle.loads(pickle.dumps(marker)), marker) + + class PrintTest(unittest.TestCase): def testPyObjectPrintObject(self): @@ -144,11 +178,17 @@ class EnableDeferredRefcountingTest(unittest.TestCase): @support.requires_resource("cpu") def test_enable_deferred_refcount(self): from threading import Thread + import gc self.assertEqual(_testcapi.pyobject_enable_deferred_refcount("not tracked"), 0) foo = [] self.assertEqual(_testcapi.pyobject_enable_deferred_refcount(foo), int(support.Py_GIL_DISABLED)) + # The object must be tracked by the GC + not_gc_tracked = tuple([1, 2]) + self.assertFalse(gc.is_tracked(not_gc_tracked)) + self.assertEqual(_testcapi.pyobject_enable_deferred_refcount(not_gc_tracked), 0) + # Make sure reference counting works on foo now self.assertEqual(foo, []) if support.Py_GIL_DISABLED: @@ -247,5 +287,60 @@ def func(x): func(object()) + # Test that a newly created object in C is not considered + # a uniquely referenced temporary, because it's not on the stack. + # gh-142586: do the test in a loop over a list to test for handling + # tagged ints on the stack. + for i in [0, 1, 2]: + self.assertFalse(_testcapi.pyobject_is_unique_temporary_new_object()) + + def pyobject_dump(self, obj, release_gil=False): + pyobject_dump = _testcapi.pyobject_dump + + try: + old_stderr = os.dup(STDERR_FD) + except OSError as exc: + # os.dup(STDERR_FD) is not supported on WASI + self.skipTest(f"os.dup() failed with {exc!r}") + + filename = os_helper.TESTFN + try: + try: + with open(filename, "wb") as fp: + fd = fp.fileno() + os.dup2(fd, STDERR_FD) + pyobject_dump(obj, release_gil) + finally: + os.dup2(old_stderr, STDERR_FD) + os.close(old_stderr) + + with open(filename) as fp: + return fp.read().rstrip() + finally: + os_helper.unlink(filename) + + def test_pyobject_dump(self): + # test string object + str_obj = 'test string' + output = self.pyobject_dump(str_obj) + hex_regex = r'(0x)?[0-9a-fA-F]+' + regex = ( + fr"object address : {hex_regex}\n" + r"object refcount : [0-9]+\n" + fr"object type : {hex_regex}\n" + r"object type name: str\n" + r"object repr : 'test string'" + ) + self.assertRegex(output, regex) + + # release the GIL + output = self.pyobject_dump(str_obj, release_gil=True) + self.assertRegex(output, regex) + + # test NULL object + output = self.pyobject_dump(NULL) + self.assertRegex(output, r'') + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 7be1c9eebb3bf9..2248920c266aef 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -5,6 +5,7 @@ import unittest import gc import os +import types import _opcode @@ -14,7 +15,21 @@ _testinternalcapi = import_helper.import_module("_testinternalcapi") -from _testinternalcapi import TIER2_THRESHOLD +from _testinternalcapi import _PY_NSMALLPOSINTS, TIER2_THRESHOLD, TIER2_RESUME_THRESHOLD + +#For test of issue 136154 +GLOBAL_136154 = 42 + +# For frozendict JIT tests +FROZEN_DICT_CONST = frozendict(x=1, y=2) + +# For frozenset JIT tests +FROZEN_SET_CONST = frozenset({1, 2, 3}) + +class _GenericKey: + pass + +_GENERIC_KEY = _GenericKey() @contextlib.contextmanager @@ -37,6 +52,17 @@ def get_first_executor(func): pass return None +def get_all_executors(func): + code = func.__code__ + co_code = code.co_code + executors = [] + for i in range(0, len(co_code), 2): + try: + executors.append(_opcode.get_executor(code, i)) + except ValueError: + pass + return executors + def iter_opnames(ex): for item in ex: @@ -46,6 +72,16 @@ def iter_opnames(ex): def get_opnames(ex): return list(iter_opnames(ex)) +def iter_ops(ex): + for item in ex: + yield item + +def get_ops(ex): + return list(iter_ops(ex)) + +def count_ops(ex, name): + return len([opname for opname in iter_opnames(ex) if opname == name]) + @requires_specialization @unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds") @@ -86,6 +122,7 @@ def f{n}(): for exe in executors[:i]: self.assertTrue(exe.is_valid()) + @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") def test_uop_optimizer_invalidation(self): # Generate a new function at each call ns = {} @@ -115,6 +152,30 @@ def f(): exe = get_first_executor(f) self.assertIsNone(exe) + def test_prev_executor_freed_while_tracing(self): + def f(start, end, way): + for x in range(start, end): + # For the first trace, create a bad branch on purpose to trace into. + # A side exit will form from here on the second trace. + y = way + way + if x >= TIER2_THRESHOLD: + # Invalidate the first trace while tracing the second. + _testinternalcapi.invalidate_executors(f.__code__) + _testinternalcapi.clear_executor_deletion_list() + f(0, TIER2_THRESHOLD, 1) + f(1, TIER2_THRESHOLD + 1, 1.0) + + +def get_bool_guard_ops(): + delta = id(True) ^ id(False) + for bit in range(4, 8): + if delta & (1 << bit): + if id(True) & (1 << bit): + return f"_GUARD_BIT_IS_UNSET_POP_{bit}", f"_GUARD_BIT_IS_SET_POP_{bit}" + else: + return f"_GUARD_BIT_IS_SET_POP_{bit}", f"_GUARD_BIT_IS_UNSET_POP_{bit}" + return "_GUARD_IS_FALSE_POP", "_GUARD_IS_TRUE_POP" + @requires_specialization @unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds") @@ -122,6 +183,9 @@ def f(): @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") class TestUops(unittest.TestCase): + def setUp(self): + self.guard_is_false, self.guard_is_true = get_bool_guard_ops() + def test_basic_loop(self): def testfunc(x): i = 0 @@ -213,7 +277,7 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_GUARD_IS_TRUE_POP", uops) + self.assertIn(self.guard_is_true, uops) def test_pop_jump_if_none(self): def testfunc(a): @@ -255,7 +319,7 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_GUARD_IS_FALSE_POP", uops) + self.assertIn(self.guard_is_false, uops) def test_jump_backward(self): def testfunc(n): @@ -270,6 +334,23 @@ def testfunc(n): uops = get_opnames(ex) self.assertIn("_JUMP_TO_TOP", uops) + def test_resume(self): + def testfunc(x): + if x <= 1: + return 1 + return testfunc(x-1) + + for _ in range((TIER2_RESUME_THRESHOLD + 99)//100): + testfunc(101) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + # 0. _START_EXECUTOR + # 1. _MAKE_WARM + # 2. _TIER2_RESUME_CHECK + self.assertEqual(uops[2], "_TIER2_RESUME_CHECK") + def test_jump_forward(self): def testfunc(n): a = 0 @@ -290,6 +371,75 @@ def testfunc(n): # look for indirect evidence: the += operator self.assertIn("_BINARY_OP_ADD_INT", uops) + def test_get_iter_list(self): + l = list(range(10)) + def testfunc(n): + total = 0 + while n: + n -= 1 + total += n + for i in l: + break + return total + + total = testfunc(TIER2_THRESHOLD) + self.assertEqual(total, sum(range(TIER2_THRESHOLD))) + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_PUSH_TAGGED_ZERO", uops) + self.assertNotIn("_GET_ITER", uops) + self.assertNotIn("_GET_ITER_TRAD", uops) + self.assertNotIn("_GET_ITER_VIRTUAL", uops) + self.assertNotIn("_GET_ITER_SELF", uops) + + def test_get_iter_gen(self): + def gen(): + while True: + yield 1 + + def testfunc(n): + total = 0 + while n: + n -= 1 + total += n + for i in gen(): + break + return total + + total = testfunc(TIER2_THRESHOLD) + self.assertEqual(total, sum(range(TIER2_THRESHOLD))) + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_PUSH_NULL", uops) + self.assertNotIn("_GET_ITER", uops) + self.assertNotIn("_GET_ITER_TRAD", uops) + self.assertNotIn("_GET_ITER_VIRTUAL", uops) + self.assertNotIn("_GET_ITER_SELF", uops) + + def test_get_iter_trad(self): + d = {v:v for v in range(10)} + def testfunc(n): + total = 0 + while n: + n -= 1 + total += n + for i in d: + break + return total + + total = testfunc(TIER2_THRESHOLD) + self.assertEqual(total, sum(range(TIER2_THRESHOLD))) + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_GET_ITER_TRAD", uops) + self.assertNotIn("_GET_ITER", uops) + self.assertNotIn("_GET_ITER_VIRTUAL", uops) + self.assertNotIn("_GET_ITER_SELF", uops) + + def test_for_iter_range(self): def testfunc(n): total = 0 @@ -376,6 +526,24 @@ def dummy(x): self.assertIn("_PUSH_FRAME", uops) self.assertIn("_BINARY_OP_ADD_INT", uops) + def test_call_py_ex(self): + def testfunc(n): + def ex_py(*args, **kwargs): + return 1 + + for _ in range(n): + args = (1, 2, 3) + kwargs = {} + ex_py(*args, **kwargs) + + testfunc(TIER2_THRESHOLD) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_PUSH_FRAME", uops) + self.assertIn("_PY_FRAME_EX", uops) + def test_branch_taken(self): def testfunc(n): for i in range(n): @@ -389,7 +557,20 @@ def testfunc(n): ex = get_first_executor(testfunc) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_GUARD_IS_FALSE_POP", uops) + self.assertIn(self.guard_is_false, uops) + + def test_branch_coincident_targets(self): + # test for gh-144681: https://github.com/python/cpython/issues/144681 + def testfunc(n): + for _ in range(n): + r = [x for x in range(10) if [].append(x) or True] + return r + + res = testfunc(TIER2_THRESHOLD) + ex = get_first_executor(testfunc) + + self.assertEqual(res, list(range(10))) + self.assertIsNotNone(ex) def test_for_iter_tier_two(self): class MyIter: @@ -418,32 +599,7 @@ def testfunc(n, m): self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertIn("_FOR_ITER_TIER_TWO", uops) - - def test_confidence_score(self): - def testfunc(n): - bits = 0 - for i in range(n): - if i & 0x01: - bits += 1 - if i & 0x02: - bits += 1 - if i&0x04: - bits += 1 - if i&0x08: - bits += 1 - if i&0x10: - bits += 1 - return bits - - x = testfunc(TIER2_THRESHOLD * 2) - - self.assertEqual(x, TIER2_THRESHOLD * 5) - ex = get_first_executor(testfunc) - self.assertIsNotNone(ex) - ops = list(iter_opnames(ex)) - #Since branch is 50/50 the trace could go either way. - count = ops.count("_GUARD_IS_TRUE_POP") + ops.count("_GUARD_IS_FALSE_POP") - self.assertLessEqual(count, 2) + self.assertNotIn("_ITER_NEXT_INLINE", uops) @requires_specialization @@ -452,6 +608,9 @@ def testfunc(n): @unittest.skipIf(os.getenv("PYTHON_UOPS_OPTIMIZE") == "0", "Needs uop optimizer to run.") class TestUopsOptimization(unittest.TestCase): + def setUp(self): + self.guard_is_false, self.guard_is_true = get_bool_guard_ops() + def _run_with_optimizer(self, testfunc, arg): res = testfunc(arg) @@ -553,6 +712,8 @@ def dummy(x): self.assertIn("_PUSH_FRAME", uops) self.assertIn("_BINARY_OP_ADD_INT", uops) self.assertNotIn("_CHECK_PEP_523", uops) + self.assertNotIn("_GUARD_CODE_VERSION__PUSH_FRAME", uops) + self.assertNotIn("_GUARD_IP__PUSH_FRAME", uops) def test_int_type_propagate_through_range(self): def testfunc(n): @@ -678,7 +839,7 @@ def testfunc(n): self.assertLessEqual(len(guard_nos_float_count), 1) # TODO gh-115506: this assertion may change after propagating constants. # We'll also need to verify that propagation actually occurs. - self.assertIn("_BINARY_OP_ADD_FLOAT__NO_DECREF_INPUTS", uops) + self.assertIn("_POP_TOP_NOP", uops) def test_float_subtract_constant_propagation(self): def testfunc(n): @@ -700,7 +861,7 @@ def testfunc(n): self.assertLessEqual(len(guard_nos_float_count), 1) # TODO gh-115506: this assertion may change after propagating constants. # We'll also need to verify that propagation actually occurs. - self.assertIn("_BINARY_OP_SUBTRACT_FLOAT__NO_DECREF_INPUTS", uops) + self.assertIn("_POP_TOP_NOP", uops) def test_float_multiply_constant_propagation(self): def testfunc(n): @@ -722,7 +883,7 @@ def testfunc(n): self.assertLessEqual(len(guard_nos_float_count), 1) # TODO gh-115506: this assertion may change after propagating constants. # We'll also need to verify that propagation actually occurs. - self.assertIn("_BINARY_OP_MULTIPLY_FLOAT__NO_DECREF_INPUTS", uops) + self.assertIn("_POP_TOP_NOP", uops) def test_add_unicode_propagation(self): def testfunc(n): @@ -844,36 +1005,93 @@ def testfunc(n): self.assertLessEqual(len(guard_nos_unicode_count), 1) self.assertIn("_COMPARE_OP_STR", uops) - def test_type_inconsistency(self): - ns = {} - src = textwrap.dedent(""" - def testfunc(n): - for i in range(n): - x = _test_global + _test_global - """) - exec(src, ns, ns) - testfunc = ns['testfunc'] - ns['_test_global'] = 0 - _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD - 1) - self.assertIsNone(ex) - ns['_test_global'] = 1 - _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD - 1) + def test_compare_int_eq_narrows_to_constant(self): + def f(n): + def return_1(): + return 1 + + hits = 0 + v = return_1() + for _ in range(n): + if v == 1: + if v == 1: + hits += 1 + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertNotIn("_GUARD_TOS_INT", uops) - self.assertNotIn("_GUARD_NOS_INT", uops) - self.assertIn("_BINARY_OP_ADD_INT", uops) - # Try again, but between the runs, set the global to a float. - # This should result in no executor the second time. - ns = {} - exec(src, ns, ns) - testfunc = ns['testfunc'] - ns['_test_global'] = 0 - _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD - 1) - self.assertIsNone(ex) - ns['_test_global'] = 3.14 - _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD - 1) - self.assertIsNone(ex) + + # Constant narrowing allows constant folding for second comparison + self.assertLessEqual(count_ops(ex, "_COMPARE_OP_INT"), 1) + + def test_compare_int_ne_narrows_to_constant(self): + def f(n): + def return_1(): + return 1 + + hits = 0 + v = return_1() + for _ in range(n): + if v != 1: + hits += 1000 + else: + if v == 1: + hits += v + 1 + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 2) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + # Constant narrowing allows constant folding for second comparison + self.assertLessEqual(count_ops(ex, "_COMPARE_OP_INT"), 1) + + def test_compare_float_eq_narrows_to_constant(self): + def f(n): + def return_tenth(): + return 0.1 + + hits = 0 + v = return_tenth() + for _ in range(n): + if v == 0.1: + if v == 0.1: + hits += 1 + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + # Constant narrowing allows constant folding for second comparison + self.assertLessEqual(count_ops(ex, "_COMPARE_OP_FLOAT"), 1) + + def test_compare_float_ne_narrows_to_constant(self): + def f(n): + def return_tenth(): + return 0.1 + + hits = 0 + v = return_tenth() + for _ in range(n): + if v != 0.1: + hits += 1000 + else: + if v == 0.1: + hits += 1 + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + # Constant narrowing allows constant folding for second comparison + self.assertLessEqual(count_ops(ex, "_COMPARE_OP_FLOAT"), 1) def test_combine_stack_space_checks_sequential(self): def dummy12(x): @@ -898,10 +1116,13 @@ def testfunc(n): self.assertEqual(uop_names.count("_PUSH_FRAME"), 2) self.assertEqual(uop_names.count("_RETURN_VALUE"), 2) self.assertEqual(uop_names.count("_CHECK_STACK_SPACE"), 0) - self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 1) - # sequential calls: max(12, 13) == 13 - largest_stack = _testinternalcapi.get_co_framesize(dummy13.__code__) - self.assertIn(("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands) + # Each call gets its own _CHECK_STACK_SPACE_OPERAND + self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 2) + # Each _CHECK_STACK_SPACE_OPERAND has the framesize of its function + self.assertIn(("_CHECK_STACK_SPACE_OPERAND", + _testinternalcapi.get_co_framesize(dummy12.__code__)), uops_and_operands) + self.assertIn(("_CHECK_STACK_SPACE_OPERAND", + _testinternalcapi.get_co_framesize(dummy13.__code__)), uops_and_operands) def test_combine_stack_space_checks_nested(self): def dummy12(x): @@ -925,13 +1146,11 @@ def testfunc(n): self.assertEqual(uop_names.count("_PUSH_FRAME"), 2) self.assertEqual(uop_names.count("_RETURN_VALUE"), 2) self.assertEqual(uop_names.count("_CHECK_STACK_SPACE"), 0) - self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 1) - # nested calls: 15 + 12 == 27 - largest_stack = ( - _testinternalcapi.get_co_framesize(dummy15.__code__) + - _testinternalcapi.get_co_framesize(dummy12.__code__) - ) - self.assertIn(("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands) + self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 2) + self.assertIn(("_CHECK_STACK_SPACE_OPERAND", + _testinternalcapi.get_co_framesize(dummy15.__code__)), uops_and_operands) + self.assertIn(("_CHECK_STACK_SPACE_OPERAND", + _testinternalcapi.get_co_framesize(dummy12.__code__)), uops_and_operands) def test_combine_stack_space_checks_several_calls(self): def dummy12(x): @@ -960,13 +1179,13 @@ def testfunc(n): self.assertEqual(uop_names.count("_PUSH_FRAME"), 4) self.assertEqual(uop_names.count("_RETURN_VALUE"), 4) self.assertEqual(uop_names.count("_CHECK_STACK_SPACE"), 0) - self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 1) - # max(12, 18 + max(12, 13)) == 31 - largest_stack = ( - _testinternalcapi.get_co_framesize(dummy18.__code__) + - _testinternalcapi.get_co_framesize(dummy13.__code__) - ) - self.assertIn(("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands) + self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 4) + self.assertIn(("_CHECK_STACK_SPACE_OPERAND", + _testinternalcapi.get_co_framesize(dummy12.__code__)), uops_and_operands) + self.assertIn(("_CHECK_STACK_SPACE_OPERAND", + _testinternalcapi.get_co_framesize(dummy13.__code__)), uops_and_operands) + self.assertIn(("_CHECK_STACK_SPACE_OPERAND", + _testinternalcapi.get_co_framesize(dummy18.__code__)), uops_and_operands) def test_combine_stack_space_checks_several_calls_different_order(self): # same as `several_calls` but with top-level calls reversed @@ -996,14 +1215,15 @@ def testfunc(n): self.assertEqual(uop_names.count("_PUSH_FRAME"), 4) self.assertEqual(uop_names.count("_RETURN_VALUE"), 4) self.assertEqual(uop_names.count("_CHECK_STACK_SPACE"), 0) - self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 1) - # max(18 + max(12, 13), 12) == 31 - largest_stack = ( - _testinternalcapi.get_co_framesize(dummy18.__code__) + - _testinternalcapi.get_co_framesize(dummy13.__code__) - ) - self.assertIn(("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands) - + self.assertEqual(uop_names.count("_CHECK_STACK_SPACE_OPERAND"), 4) + self.assertIn(("_CHECK_STACK_SPACE_OPERAND", + _testinternalcapi.get_co_framesize(dummy12.__code__)), uops_and_operands) + self.assertIn(("_CHECK_STACK_SPACE_OPERAND", + _testinternalcapi.get_co_framesize(dummy13.__code__)), uops_and_operands) + self.assertIn(("_CHECK_STACK_SPACE_OPERAND", + _testinternalcapi.get_co_framesize(dummy18.__code__)), uops_and_operands) + + @unittest.skip("reopen when we combine multiple stack space checks into one") def test_combine_stack_space_complex(self): def dummy0(x): return x @@ -1053,6 +1273,7 @@ def testfunc(n): ("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands ) + @unittest.skip("reopen when we combine multiple stack space checks into one") def test_combine_stack_space_checks_large_framesize(self): # Create a function with a large framesize. This ensures _CHECK_STACK_SPACE is # actually doing its job. Note that the resulting trace hits @@ -1114,6 +1335,7 @@ def testfunc(n): ("_CHECK_STACK_SPACE_OPERAND", largest_stack), uops_and_operands ) + @unittest.skip("reopen when we combine multiple stack space checks into one") def test_combine_stack_space_checks_recursion(self): def dummy15(x): while x > 0: @@ -1183,6 +1405,54 @@ def testfunc(n): self.assertIsNotNone(ex) self.assertIn("_RETURN_GENERATOR", get_opnames(ex)) + def test_make_heap_safe_optimized_immortal(self): + def returns_immortal(): + return None + def testfunc(n): + a = 0 + for _ in range(n): + a = returns_immortal() + return a + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNone(res) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_MAKE_HEAP_SAFE", uops) + self.assertIn("_RETURN_VALUE", uops) + + def test_make_heap_safe_optimized_yield(self): + def gen(n): + for _ in range(n): + yield 1 + def testfunc(n): + for _ in gen(n): + pass + testfunc(TIER2_THRESHOLD * 2) + # The generator may be inlined into testfunc's trace, + # so check whichever executor contains _YIELD_VALUE. + gen_ex = get_first_executor(gen) + testfunc_ex = get_first_executor(testfunc) + ex = gen_ex or testfunc_ex + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_MAKE_HEAP_SAFE", uops) + self.assertIn("_YIELD_VALUE", uops) + + def test_make_heap_safe_not_optimized_for_owned(self): + def returns_owned(x): + return x + 1 + def testfunc(n): + a = 0 + for _ in range(n): + a = returns_owned(a) + return a + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_MAKE_HEAP_SAFE", uops) + self.assertIn("_RETURN_VALUE", uops) + def test_for_iter(self): def testfunc(n): t = 0 @@ -1192,23 +1462,132 @@ def testfunc(n): res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) self.assertEqual(res, TIER2_THRESHOLD * (TIER2_THRESHOLD - 1) // 2) self.assertIsNotNone(ex) - self.assertIn("_FOR_ITER_TIER_TWO", get_opnames(ex)) + self.assertIn("_ITER_NEXT_INLINE", get_opnames(ex)) - @unittest.skip("Tracing into generators currently isn't supported.") - def test_for_iter_gen(self): - def gen(n): - for i in range(n): - yield i + def test_for_iter_direct_dict_items(self): def testfunc(n): - g = gen(n) - s = 0 - for i in g: - s += i - return s + d = {i: i * 2 for i in range(10)} + total = 0 + for _ in range(n): + for k, v in d.items(): + total += k + v + return total + + expected = 0 + d = {i: i * 2 for i in range(10)} + for _ in range(TIER2_THRESHOLD): + for k, v in d.items(): + expected += k + v + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) - self.assertEqual(res, sum(range(TIER2_THRESHOLD))) + self.assertEqual(res, expected) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_ITER_NEXT_INLINE", uops) + self.assertNotIn("_FOR_ITER_TIER_TWO", uops) + + def test_for_iter_direct_dict_keys(self): + def testfunc(n): + d = {i: i for i in range(10)} + total = 0 + for _ in range(n): + for k in d.keys(): + total += k + return total + + expected = TIER2_THRESHOLD * sum(range(10)) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, expected) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_ITER_NEXT_INLINE", uops) + self.assertNotIn("_FOR_ITER_TIER_TWO", uops) + + def test_for_iter_direct_dict_values(self): + def testfunc(n): + d = {i: i * 3 for i in range(10)} + total = 0 + for _ in range(n): + for v in d.values(): + total += v + return total + + expected = TIER2_THRESHOLD * sum(i * 3 for i in range(10)) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, expected) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_ITER_NEXT_INLINE", uops) + self.assertNotIn("_FOR_ITER_TIER_TWO", uops) + + def test_for_iter_direct_set(self): + def testfunc(n): + s = set(range(10)) + total = 0 + for _ in range(n): + for x in s: + total += x + return total + + expected = TIER2_THRESHOLD * sum(range(10)) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, expected) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_ITER_NEXT_INLINE", uops) + self.assertNotIn("_FOR_ITER_TIER_TWO", uops) + + def test_for_iter_direct_reversed(self): + def testfunc(n): + lst = list(range(10)) + total = 0 + for _ in range(n): + for x in reversed(lst): + total += x + return total + + expected = TIER2_THRESHOLD * sum(range(10)) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, expected) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_ITER_NEXT_INLINE", uops) + self.assertNotIn("_FOR_ITER_TIER_TWO", uops) + + def test_for_iter_direct_enumerate(self): + def testfunc(n): + lst = list(range(10)) + total = 0 + for _ in range(n): + for i, x in enumerate(lst): + total += i + x + return total + + expected = TIER2_THRESHOLD * sum(i + x for i, x in enumerate(range(10))) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, expected) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_ITER_NEXT_INLINE", uops) + self.assertNotIn("_FOR_ITER_TIER_TWO", uops) + + def test_for_iter_direct_zip(self): + def testfunc(n): + a = list(range(10)) + b = list(range(10, 20)) + total = 0 + for _ in range(n): + for x, y in zip(a, b): + total += x + y + return total + + expected = TIER2_THRESHOLD * sum(x + y for x, y in zip(range(10), range(10, 20))) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, expected) self.assertIsNotNone(ex) - self.assertIn("_FOR_ITER_GEN_FRAME", get_opnames(ex)) + uops = get_opnames(ex) + self.assertIn("_ITER_NEXT_INLINE", uops) + self.assertNotIn("_FOR_ITER_TIER_TWO", uops) def test_modified_local_is_seen_by_optimized_code(self): l = sys._getframe().f_locals @@ -1270,7 +1649,7 @@ def test_guard_type_version_removed_invalidation(self): def thing(a): x = 0 - for i in range(TIER2_THRESHOLD * 2 + 1): + for i in range(TIER2_THRESHOLD + 1): x += a.attr # The first TIER2_THRESHOLD iterations we set the attribute on # this dummy class, which shouldn't trigger the type watcher. @@ -1288,11 +1667,10 @@ class Bar: res, ex = self._run_with_optimizer(thing, Foo()) opnames = list(iter_opnames(ex)) - self.assertIsNotNone(ex) - self.assertEqual(res, TIER2_THRESHOLD * 6 + 1) + self.assertEqual(res, TIER2_THRESHOLD * 2 + 2) call = opnames.index("_CALL_BUILTIN_FAST") - load_attr_top = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", 0, call) - load_attr_bottom = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", call) + load_attr_top = opnames.index("_LOAD_CONST_INLINE_BORROW", 0, call) + load_attr_bottom = opnames.index("_LOAD_CONST_INLINE_BORROW", call) self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1) self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2) @@ -1314,8 +1692,8 @@ class Foo: self.assertIsNotNone(ex) self.assertEqual(res, TIER2_THRESHOLD * 2) call = opnames.index("_CALL_BUILTIN_FAST_WITH_KEYWORDS") - load_attr_top = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", 0, call) - load_attr_bottom = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", call) + load_attr_top = opnames.index("_LOAD_CONST_INLINE_BORROW", 0, call) + load_attr_bottom = opnames.index("_LOAD_CONST_INLINE_BORROW", call) self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1) self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2) @@ -1342,6 +1720,29 @@ class Foo: Foo.attr = 0 self.assertFalse(ex.is_valid()) + def test_guard_type_version_locked_removed(self): + """ + Verify that redundant _GUARD_TYPE_VERSION_LOCKED guards are + eliminated for sequential STORE_ATTR_INSTANCE_VALUE in __init__. + """ + + class Foo: + def __init__(self): + self.a = 1 + self.b = 2 + self.c = 3 + + def thing(n): + for _ in range(n): + Foo() + + res, ex = self._run_with_optimizer(thing, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + opnames = list(iter_opnames(ex)) + guard_locked_count = opnames.count("_GUARD_TYPE_VERSION_LOCKED") + # Only the first store needs the guard; the rest should be NOPed. + self.assertEqual(guard_locked_count, 1) + def test_type_version_doesnt_segfault(self): """ Tests that setting a type version doesn't cause a segfault when later looking at the stack. @@ -1363,8 +1764,135 @@ def fn(a): fn(A()) - def test_func_guards_removed_or_reduced(self): - def testfunc(n): + def test_init_resolves_callable(self): + """ + _CHECK_AND_ALLOCATE_OBJECT should resolve __init__ to a constant, + enabling the optimizer to propagate type information through the frame + and eliminate redundant function version and arg count checks. + """ + class MyPoint: + def __init__(self, x, y): + # If __init__ callable is propagated through, then + # These will get promoted from globals to constants. + self.x = range(1) + self.y = range(1) + + def testfunc(n): + for _ in range(n): + p = MyPoint(1.0, 2.0) + + _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + # The __init__ call should be traced through via _PUSH_FRAME + self.assertIn("_PUSH_FRAME", uops) + # __init__ resolution allows promotion of range to constant + self.assertNotIn("_LOAD_GLOBAL_BUILTINS", uops) + + def test_init_guards_removed(self): + class MyPoint: + def __init__(self, x, y): + return None + + def testfunc(n): + point_local = MyPoint + for _ in range(n): + p = point_local(1.0, 2.0) + p = point_local(1.0, 2.0) + + _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + # The __init__ call should be traced through via _PUSH_FRAME + count = count_ops(ex, "_CREATE_INIT_FRAME") + self.assertEqual(count, 2) + # __init__ resolution allows promotion of range to constant + count = count_ops(ex, "_CHECK_OBJECT") + self.assertEqual(count, 1) + + def test_init_guards_removed_global(self): + + def testfunc(n): + for _ in range(n): + p = MyGlobalPoint(1.0, 2.0) + p = MyGlobalPoint(1.0, 2.0) + + _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + # The __init__ call should be traced through via _PUSH_FRAME + count = count_ops(ex, "_CREATE_INIT_FRAME") + self.assertEqual(count, 2) + # __init__ resolution allows promotion of range to constant + count = count_ops(ex, "_CHECK_OBJECT") + self.assertEqual(count, 0) + + def test_guard_type_version_locked_propagates(self): + """ + _GUARD_TYPE_VERSION_LOCKED should set the type version on the + symbol so repeated accesses to the same type can benefit. + """ + class Item: + def __init__(self, val): + self.val = val + + def get(self): + return self.val + + def get2(self): + return self.val + 1 + + def testfunc(n): + item = Item(42) + total = 0 + for _ in range(n): + # Two method calls on the same object — the second + # should benefit from type info set by the first. + total += item.get() + item.get2() + return total + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * (42 + 43)) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + # Both methods should be traced through + self.assertEqual(uops.count("_PUSH_FRAME"), 2) + # Type version propagation: one guard covers both method lookups + self.assertEqual(uops.count("_GUARD_TYPE_VERSION"), 1) + # Function checks cannot be eliminated for safety reasons. + self.assertIn("_CHECK_FUNCTION_VERSION", uops) + + def test_method_chain_guard_elimination(self): + """ + Calling two methods on the same object should share the outer + type guard — only one _GUARD_TYPE_VERSION for the two lookups. + """ + class Calc: + def __init__(self, val): + self.val = val + + def add(self, x): + self.val += x + return self + + def testfunc(n): + c = Calc(0) + for _ in range(n): + c.add(1).add(2) + return c.val + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 3) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + # Both add() calls should be inlined + push_count = uops.count("_PUSH_FRAME") + self.assertEqual(push_count, 2) + # Only one outer type version guard for the two method lookups + # on the same object c (the second lookup reuses type info) + guard_version_count = uops.count("_GUARD_TYPE_VERSION") + self.assertEqual(guard_version_count, 1) + + def test_func_guards_removed_or_reduced(self): + def testfunc(n): for i in range(n): # Only works on functions promoted to constants global_identity(i) @@ -1375,9 +1903,7 @@ def testfunc(n): self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertIn("_PUSH_FRAME", uops) - # Strength reduced version - self.assertIn("_CHECK_FUNCTION_VERSION_INLINE", uops) - self.assertNotIn("_CHECK_FUNCTION_VERSION", uops) + self.assertIn("_CHECK_FUNCTION_VERSION", uops) # Removed guard self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops) @@ -1396,6 +1922,49 @@ def testfunc(n): self.assertIn("_CHECK_FUNCTION_VERSION_INLINE", uops) self.assertNotIn("_CHECK_METHOD_VERSION", uops) + def test_record_bound_method_general(self): + class MyClass: + def method(self, *args): + return args[0] + 1 + + def testfunc(n): + obj = MyClass() + bound = obj.method + result = 0 + for i in range(n): + result += bound(i) + return result + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual( + res, sum(i + 1 for i in range(TIER2_THRESHOLD)) + ) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_PUSH_FRAME", uops) + + def test_record_bound_method_exact_args(self): + class MyClass: + def method(self, x): + return x + 1 + + def testfunc(n): + obj = MyClass() + bound = obj.method + result = 0 + for i in range(n): + result += bound(i) + return result + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual( + res, sum(i + 1 for i in range(TIER2_THRESHOLD)) + ) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_PUSH_FRAME", uops) + self.assertNotIn("_CHECK_FUNCTION_EXACT_ARGS", uops) + def test_jit_error_pops(self): """ Tests that the correct number of pops are inserted into the @@ -1462,7 +2031,7 @@ def testfunc(n): self.assertEqual(res, 3) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_BINARY_OP_ADD_INT", uops) + self.assertNotIn("_BINARY_OP_ADD_INT", uops) self.assertNotIn("_GUARD_NOS_INT", uops) self.assertNotIn("_GUARD_TOS_INT", uops) @@ -1511,8 +2080,8 @@ def f(n): self.assertIsNotNone(ex) uops = get_opnames(ex) # Only one guard remains: - self.assertEqual(uops.count("_GUARD_IS_FALSE_POP"), 1) - self.assertEqual(uops.count("_GUARD_IS_TRUE_POP"), 0) + self.assertEqual(uops.count(self.guard_is_false), 1) + self.assertEqual(uops.count(self.guard_is_true), 0) # But all of the appends we care about are still there: self.assertEqual(uops.count("_CALL_LIST_APPEND"), len("ABCDEFG")) @@ -1542,8 +2111,8 @@ def f(n): self.assertIsNotNone(ex) uops = get_opnames(ex) # Only one guard remains: - self.assertEqual(uops.count("_GUARD_IS_FALSE_POP"), 0) - self.assertEqual(uops.count("_GUARD_IS_TRUE_POP"), 1) + self.assertEqual(uops.count(self.guard_is_false), 0) + self.assertEqual(uops.count(self.guard_is_true), 1) # But all of the appends we care about are still there: self.assertEqual(uops.count("_CALL_LIST_APPEND"), len("ABCDEFG")) @@ -1574,8 +2143,8 @@ def f(n): self.assertIsNotNone(ex) uops = get_opnames(ex) # Only one guard remains: - self.assertEqual(uops.count("_GUARD_IS_FALSE_POP"), 1) - self.assertEqual(uops.count("_GUARD_IS_TRUE_POP"), 0) + self.assertEqual(uops.count(self.guard_is_false), 1) + self.assertEqual(uops.count(self.guard_is_true), 0) # But all of the appends we care about are still there: self.assertEqual(uops.count("_CALL_LIST_APPEND"), len("ABCDEFG")) @@ -1607,12 +2176,82 @@ def f(n): self.assertIsNotNone(ex) uops = get_opnames(ex) # Only one guard remains: - self.assertEqual(uops.count("_GUARD_IS_FALSE_POP"), 1) - self.assertEqual(uops.count("_GUARD_IS_TRUE_POP"), 0) + self.assertEqual(uops.count(self.guard_is_false), 1) + self.assertEqual(uops.count(self.guard_is_true), 0) # But all of the appends we care about are still there: self.assertEqual(uops.count("_CALL_LIST_APPEND"), len("ABCDEFG")) - def test_compare_pop_two_load_const_inline_borrow(self): + def test_unary_negative_pop_top_load_const_inline_borrow(self): + def testfunc(n): + x = 0 + for i in range(n): + a = 1 + result = -a + if result < 0: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_UNARY_NEGATIVE", uops) + + def test_unary_not_pop_top_load_const_inline_borrow(self): + def testfunc(n): + x = 0 + for i in range(n): + a = 42 + result = not a + if result: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_UNARY_NOT", uops) + # TODO (gh-143723): After refactoring TO_BOOL_INT to eliminate redundant + # refcounts, 'not a' is now constant-folded and currently lowered to + # _POP_TOP + _LOAD_CONST_INLINE_BORROW. Re-enable once constant folding + # avoids emitting these. + # self.assertNotIn("_LOAD_CONST_INLINE_BORROW", uops) + + def test_unary_invert_insert_1_load_const_inline_borrow(self): + def testfunc(n): + x = 0 + for i in range(n): + a = 0 + result = ~a + if result < 0: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_UNARY_INVERT", uops) + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) + + def test_compare_op_pop_two_load_const_inline_borrow(self): + def testfunc(n): + x = 0 + for _ in range(n): + a = 10 + b = 10.0 + if a == b: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_COMPARE_OP", uops) + + def test_compare_op_int_insert_two_load_const_inline_borrow(self): def testfunc(n): x = 0 for _ in range(n): @@ -1627,7 +2266,57 @@ def testfunc(n): self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertNotIn("_COMPARE_OP_INT", uops) - self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops) + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) + + def test_compare_op_str_insert_two_load_const_inline_borrow(self): + def testfunc(n): + x = 0 + for _ in range(n): + a = "foo" + b = "foo" + if a == b: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_COMPARE_OP_STR", uops) + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) + + def test_compare_op_float_insert_two_load_const_inline_borrow(self): + def testfunc(n): + x = 0 + for _ in range(n): + a = 1.0 + b = 1.0 + if a == b: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_COMPARE_OP_FLOAT", uops) + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) + + def test_contains_op_pop_two_load_const_inline_borrow(self): + def testfunc(n): + x = 0 + for _ in range(n): + a = "foo" + s = "foo bar baz" + if a in s: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_CONTAINS_OP", uops) def test_to_bool_bool_contains_op_set(self): """ @@ -1710,9 +2399,48 @@ def f(n): self.assertEqual(res, TIER2_THRESHOLD) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertEqual(uops.count("_GUARD_NOS_DICT"), 0) - self.assertEqual(uops.count("_STORE_SUBSCR_DICT"), 1) - self.assertEqual(uops.count("_BINARY_OP_SUBSCR_DICT"), 1) + self.assertEqual(uops.count("_GUARD_NOS_DICT_SUBSCRIPT"), 0) + self.assertEqual(uops.count("_GUARD_NOS_DICT_STORE_SUBSCRIPT"), 0) + self.assertEqual(uops.count("_BINARY_OP_SUBSCR_DICT_KNOWN_HASH"), 1) + + def test_dict_subclass_subscr(self): + import collections + + def f(n): + x = 0 + d = collections.defaultdict(int) + for _ in range(n): + d["key"] = 1 + x += d["key"] + return x + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertEqual(uops.count("_BINARY_OP_SUBSCR_DICT_KNOWN_HASH"), 1) + self.assertEqual(uops.count("_STORE_SUBSCR_DICT_KNOWN_HASH"), 1) + self.assertEqual(uops.count("_GUARD_NOS_DICT_SUBSCRIPT"), 0) + self.assertEqual(uops.count("_GUARD_NOS_DICT_STORE_SUBSCRIPT"), 0) + self.assertEqual(uops.count("_GUARD_TYPE"), 1) + + def test_dict_subclass_subscr_with_override(self): + class MyDict(dict): + def __getitem__(self, key): + return 42 + + def f(n): + d = MyDict() + x = 0 + for _ in range(n): + x += d["anything"] + return x + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, 42 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertEqual(uops.count("_BINARY_OP_SUBSCR_INIT_CALL"), 1) def test_remove_guard_for_known_type_list(self): def f(n): @@ -1737,6 +2465,125 @@ def f(n): self.assertEqual(uops.count("_BINARY_OP_SUBSCR_LIST_INT"), 1) self.assertEqual(uops.count("_TO_BOOL_LIST"), 1) + def test_unique_tuple_unpack(self): + def f(n): + def four_tuple(x): + return (x, x, x, x) + hits = 0 + for i in range(n): + w, x, y, z = four_tuple(1) + hits += w + x + y + z + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 4) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_BUILD_TUPLE", uops) + self.assertIn("_UNPACK_SEQUENCE_UNIQUE_TUPLE", uops) + self.assertNotIn("_UNPACK_SEQUENCE_TUPLE", uops) + + def test_non_unique_tuple_unpack(self): + def f(n): + def four_tuple(x): + return (x, x, x, x) + hits = 0 + for i in range(n): + t = four_tuple(1) + w, x, y, z = t + hits += w + x + y + z + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 4) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_BUILD_TUPLE", uops) + self.assertIn("_UNPACK_SEQUENCE_TUPLE", uops) + self.assertNotIn("_UNPACK_SEQUENCE_UNIQUE_TUPLE", uops) + + def test_unique_three_tuple_unpack(self): + def f(n): + def three_tuple(x): + return (x, x, x) + hits = 0 + for i in range(n): + x, y, z = three_tuple(1) + hits += x + y + z + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 3) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_BUILD_TUPLE", uops) + self.assertIn("_UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE", uops) + self.assertNotIn("_UNPACK_SEQUENCE_TUPLE", uops) + + def test_non_unique_three_tuple_unpack(self): + def f(n): + def three_tuple(x): + return (x, x, x) + hits = 0 + for i in range(n): + t = three_tuple(1) + x, y, z = t + hits += x + y + z + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 3) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_BUILD_TUPLE", uops) + self.assertIn("_UNPACK_SEQUENCE_TUPLE", uops) + self.assertNotIn("_UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE", uops) + + def test_unique_two_tuple_unpack(self): + def f(n): + def two_tuple(x): + return (x, x) + hits = 0 + for i in range(n): + x, y = two_tuple(1) + hits += x + y + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 2) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_BUILD_TUPLE", uops) + self.assertIn("_UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE", uops) + self.assertNotIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops) + self.assertNotIn("_UNPACK_SEQUENCE_TUPLE", uops) + + def test_non_unique_two_tuple_unpack(self): + def f(n): + def two_tuple(x): + return (x, x) + hits = 0 + for i in range(n): + tt = two_tuple(1) + x, y = tt + hits += x + y + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 2) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_BUILD_TUPLE", uops) + self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops) + self.assertNotIn("_UNPACK_SEQUENCE_TUPLE", uops) + self.assertNotIn("_UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE", uops) + def test_remove_guard_for_known_type_set(self): def f(n): x = 0 @@ -1749,7 +2596,8 @@ def f(n): self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertNotIn("_GUARD_TOS_ANY_SET", uops) - self.assertIn("_CONTAINS_OP_SET", uops) + # _CONTAINS_OP_SET is constant-folded away for frozenset literals + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) def test_remove_guard_for_known_type_tuple(self): def f(n): @@ -1771,19 +2619,51 @@ def f(n): self.assertNotIn("_GUARD_NOS_TUPLE", uops) self.assertIn("_BINARY_OP_SUBSCR_TUPLE_INT", uops) - def test_binary_subcsr_str_int_narrows_to_str(self): - def testfunc(n): - x = [] - s = "foo" + def test_remove_guard_for_known_type_slice(self): + def f(n): + x = 0 for _ in range(n): - y = s[0] # _BINARY_OP_SUBSCR_STR_INT - z = "bar" + y # (_GUARD_TOS_UNICODE) + _BINARY_OP_ADD_UNICODE - x.append(z) + l = [1, 2, 3] + slice_obj = slice(0, 1) + x += l[slice_obj][0] # guarded + x += l[slice_obj][0] # unguarded return x - - res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) - self.assertEqual(res, ["barf"] * TIER2_THRESHOLD) - self.assertIsNotNone(ex) + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 2) + uops = get_opnames(ex) + + count = count_ops(ex, "_GUARD_TOS_SLICE") + self.assertEqual(count, 1) + self.assertIn("_BINARY_OP_SUBSCR_LIST_INT", uops) + + def test_remove_guard_for_tuple_bounds_check(self): + def f(n): + x = 0 + for _ in range(n): + t = (1, 2, 3) + x += t[0] + return x + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS", uops) + self.assertIn("_BINARY_OP_SUBSCR_TUPLE_INT", uops) + + def test_binary_subcsr_str_int_narrows_to_str(self): + def testfunc(n): + x = [] + s = "foo" + for _ in range(n): + y = s[0] # _BINARY_OP_SUBSCR_STR_INT + z = "bar" + y # (_GUARD_TOS_UNICODE) + _BINARY_OP_ADD_UNICODE + x.append(z) + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, ["barf"] * TIER2_THRESHOLD) + self.assertIsNotNone(ex) uops = get_opnames(ex) self.assertIn("_BINARY_OP_SUBSCR_STR_INT", uops) # _BINARY_OP_SUBSCR_STR_INT narrows the result to 'str' so @@ -1791,6 +2671,225 @@ def testfunc(n): self.assertNotIn("_GUARD_TOS_UNICODE", uops) self.assertIn("_BINARY_OP_ADD_UNICODE", uops) + def test_binary_subcsr_ustr_int_narrows_to_str(self): + def testfunc(n): + x = [] + s = "바이트코f드_특수화" + for _ in range(n): + y = s[4] # _BINARY_OP_SUBSCR_USTR_INT + z = "bar" + y # (_GUARD_TOS_UNICODE) + _BINARY_OP_ADD_UNICODE + x.append(z) + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, ["barf"] * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBSCR_USTR_INT", uops) + # _BINARY_OP_SUBSCR_USTR_INT narrows the result to 'str' so + # the unicode guard before _BINARY_OP_ADD_UNICODE is removed. + self.assertNotIn("_GUARD_TOS_UNICODE", uops) + self.assertIn("_BINARY_OP_ADD_UNICODE", uops) + + def test_binary_op_subscr_str_int(self): + def testfunc(n): + x = 0 + s = "hello" + for _ in range(n): + c = s[1] # _BINARY_OP_SUBSCR_STR_INT + if c == 'e': + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBSCR_STR_INT", uops) + self.assertIn("_COMPARE_OP_STR", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + self.assertLessEqual(count_ops(ex, "_POP_TOP_INT"), 1) + + def test_binary_op_subscr_ustr_int(self): + def testfunc(n): + x = 0 + s = "hello바" + for _ in range(n): + c = s[1] # _BINARY_OP_SUBSCR_USTR_INT + if c == 'e': + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBSCR_USTR_INT", uops) + self.assertIn("_COMPARE_OP_STR", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + self.assertLessEqual(count_ops(ex, "_POP_TOP_INT"), 1) + + def test_binary_op_subscr_dict(self): + def testfunc(n): + x = 0 + d = {'a': 1, 'b': 2} + for _ in range(n): + v = d['a'] # _BINARY_OP_SUBSCR_DICT + if v == 1: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBSCR_DICT_KNOWN_HASH", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_binary_op_subscr_dict_known_hash(self): + # str, int, bytes, float, complex, tuple and any python object which has generic hash + def testfunc(n): + x = 0 + d = {'a': 1, 1: 2, b'b': 3, (1, 2): 4, _GENERIC_KEY: 5, 1.5: 6, 1+2j: 7} + for _ in range(n): + x += d['a'] + d[1] + d[b'b'] + d[(1, 2)] + d[_GENERIC_KEY] + d[1.5] + d[1+2j] + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 28 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBSCR_DICT_KNOWN_HASH", uops) + self.assertNotIn("_BINARY_OP_SUBSCR_DICT", uops) + + def test_binary_op_subscr_defaultdict_known_hash(self): + # str, int, bytes, float, complex, tuple and any python object which has generic hash + import collections + + def testfunc(n): + x = 0 + d = collections.defaultdict(lambda: 1) + for _ in range(n): + x += d['a'] + d[1] + d[b'b'] + d[(1, 2)] + d[_GENERIC_KEY] + d[1.5] + d[1+2j] + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 7 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBSCR_DICT_KNOWN_HASH", uops) + self.assertNotIn("_BINARY_OP_SUBSCR_DICT", uops) + + def test_binary_op_subscr_constant_frozendict_known_hash(self): + def testfunc(n): + x = 0 + for _ in range(n): + x += FROZEN_DICT_CONST['x'] + return x + + res, ex = self._run_with_optimizer(testfunc, 2 * TIER2_THRESHOLD) + self.assertEqual(res, 2 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_BINARY_OP_SUBSCR_DICT_KNOWN_HASH", uops) + self.assertNotIn("_BINARY_OP_SUBSCR_DICT", uops) + + def test_store_subscr_dict_known_hash(self): + # str, int, bytes, float, complex, tuple and any python object which has generic hash + def testfunc(n): + d = {'a': 0, 1: 0, b'b': 0, (1, 2): 0, _GENERIC_KEY: 0, 1.5: 0, 1+2j: 0} + for _ in range(n): + d['a'] += 1 + d[1] += 2 + d[b'b'] += 3 + d[(1, 2)] += 4 + d[_GENERIC_KEY] += 5 + d[1.5] += 6 + d[1+2j] += 7 + return d['a'] + d[1] + d[b'b'] + d[(1, 2)] + d[_GENERIC_KEY] + d[1.5] + d[1+2j] + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 28 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_STORE_SUBSCR_DICT_KNOWN_HASH", uops) + self.assertNotIn("_STORE_SUBSCR_DICT", uops) + + def test_store_subscr_defaultdict_known_hash(self): + import collections + + def testfunc(n): + d = collections.defaultdict(lambda: 0) + for _ in range(n): + d['a'] += 1 + d[1] += 2 + d[b'b'] += 3 + d[(1, 2)] += 4 + d[_GENERIC_KEY] += 5 + d[1.5] += 6 + d[1+2j] += 7 + return d['a'] + d[1] + d[b'b'] + d[(1, 2)] + d[_GENERIC_KEY] + d[1.5] + d[1+2j] + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 28 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_STORE_SUBSCR_DICT_KNOWN_HASH", uops) + self.assertNotIn("_STORE_SUBSCR_DICT", uops) + + def test_contains_op(self): + def testfunc(n): + x = 0 + items = [1, 2, 3] + for _ in range(n): + if 2 in items: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CONTAINS_OP", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_contains_op_set(self): + def testfunc(n): + x = 0 + s = {1, 2, 3} + for _ in range(n): + if 2 in s: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CONTAINS_OP_SET", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_contains_op_dict(self): + def testfunc(n): + x = 0 + d = {'a': 1, 'b': 2} + for _ in range(n): + if 'a' in d: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CONTAINS_OP_DICT", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + def test_call_type_1_guards_removed(self): def testfunc(n): x = 0 @@ -1818,13 +2917,11 @@ def testfunc(n): self.assertEqual(res, TIER2_THRESHOLD) self.assertIsNotNone(ex) uops = get_opnames(ex) - # When the result of type(...) is known, _CALL_TYPE_1 is replaced with - # _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW which is optimized away in - # remove_unneeded_uops. + # When the result of type(...) is known, _CALL_TYPE_1 is decomposed. self.assertNotIn("_CALL_TYPE_1", uops) - self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops) + # _CALL_TYPE_1 produces 2 _POP_TOP_NOP (callable and null) + # type(42) is int produces 4 _POP_TOP_NOP + self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 6) def test_call_type_1_result_is_const(self): def testfunc(n): @@ -1841,6 +2938,36 @@ def testfunc(n): uops = get_opnames(ex) self.assertNotIn("_GUARD_IS_NOT_NONE_POP", uops) + def test_call_type_1_pop_top(self): + def testfunc(n): + x = 0 + for _ in range(n): + foo = eval('42') + x += type(foo) is int + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CALL_TYPE_1", uops) + self.assertIn("_POP_TOP_NOP", uops) + + def test_call_tuple_1_pop_top(self): + def testfunc(n): + x = 0 + for _ in range(n): + t = tuple(()) + x += len(t) == 0 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CALL_TUPLE_1", uops) + self.assertIn("_POP_TOP_NOP", uops) + def test_call_str_1(self): def testfunc(n): x = 0 @@ -1858,6 +2985,20 @@ def testfunc(n): self.assertNotIn("_GUARD_NOS_NULL", uops) self.assertNotIn("_GUARD_CALLABLE_STR_1", uops) + def test_call_str_1_pop_top(self): + def testfunc(n): + x = 0 + for _ in range(n): + t = str("") + x += 1 if len(t) == 0 else 0 + return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CALL_STR_1", uops) + self.assertIn("_POP_TOP_NOP", uops) + def test_call_str_1_result_is_str(self): def testfunc(n): x = 0 @@ -1895,7 +3036,7 @@ def testfunc(n): uops = get_opnames(ex) self.assertIn("_CALL_STR_1", uops) self.assertNotIn("_TO_BOOL_STR", uops) - self.assertNotIn("_GUARD_IS_TRUE_POP", uops) + self.assertNotIn(self.guard_is_true, uops) def test_call_tuple_1(self): def testfunc(n): @@ -1952,7 +3093,7 @@ def testfunc(n): self.assertIn("_CALL_TUPLE_1", uops) self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops) self.assertNotIn("_COMPARE_OP_INT", uops) - self.assertNotIn("_GUARD_IS_TRUE_POP", uops) + self.assertNotIn(self.guard_is_true, uops) def test_call_len(self): def testfunc(n): @@ -1967,8 +3108,74 @@ def testfunc(n): self.assertIn("_CALL_LEN", uops) self.assertNotIn("_GUARD_NOS_INT", uops) self.assertNotIn("_GUARD_TOS_INT", uops) + self.assertIn("_POP_TOP_NOP", uops) + + def test_check_is_not_py_callable(self): + def testfunc(n): + total = 0 + f = len + xs = (1, 2, 3) + for _ in range(n): + total += f(xs) + return total + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 3 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_CHECK_IS_NOT_PY_CALLABLE", uops) + + def test_check_is_not_py_callable_ex(self): + def testfunc(n): + total = 0 + xs = (1, 2, 3) + args = (xs,) + for _ in range(n): + total += len(*args) + return total + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 3 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_CHECK_IS_NOT_PY_CALLABLE_EX", uops) + + def test_check_is_not_py_callable_kw(self): + def testfunc(n): + total = 0 + xs = (3, 1, 2) + for _ in range(n): + total += sorted(xs, reverse=False)[0] + return total + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_CHECK_IS_NOT_PY_CALLABLE_KW", uops) + + def test_call_len_string_frozen_set_dict(self): + def testfunc(n): + for _ in range(n): + _ = len("abc") + d = '' + _ = len(d) + _ = len(b"def") + _ = len(b"") + _ = len(FROZEN_SET_CONST) + _ = len(FROZEN_DICT_CONST) + + _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_CALL_LEN", uops) + self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 10) def test_call_len_known_length_small_int(self): + # Make sure that len(t) is optimized for a tuple of length 5. + # See https://github.com/python/cpython/issues/139393. + self.assertGreater(_PY_NSMALLPOSINTS, 5) + def testfunc(n): x = 0 for _ in range(n): @@ -1984,18 +3191,19 @@ def testfunc(n): # When the length is < _PY_NSMALLPOSINTS, the len() call is replaced # with just an inline load. self.assertNotIn("_CALL_LEN", uops) - self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops) def test_call_len_known_length(self): + # Make sure that len(t) is not optimized for a tuple of length 2048. + # See https://github.com/python/cpython/issues/139393. + self.assertLess(_PY_NSMALLPOSINTS, 2048) + def testfunc(n): class C: - t = tuple(range(300)) + t = tuple(range(2048)) x = 0 for _ in range(n): - if len(C.t) == 300: # comparison + guard removed + if len(C.t) == 2048: # comparison + guard removed x += 1 return x @@ -2009,29 +3217,214 @@ class C: # in this case self.assertIn("_CALL_LEN", uops) self.assertNotIn("_COMPARE_OP_INT", uops) - self.assertNotIn("_GUARD_IS_TRUE_POP", uops) + self.assertNotIn(self.guard_is_true, uops) - def test_get_len_with_const_tuple(self): + def test_call_builtin_class(self): def testfunc(n): - x = 0.0 + x = 0 for _ in range(n): - match (1, 2, 3, 4): - case [_, _, _, _]: - x += 1.0 + y = int("42") + x += y return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) - self.assertEqual(int(res), TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 42) + self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertNotIn("_GUARD_NOS_INT", uops) - self.assertNotIn("_GET_LEN", uops) - self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) + self.assertIn("_CALL_BUILTIN_CLASS", uops) + self.assertNotIn("_GUARD_CALLABLE_BUILTIN_CLASS", uops) - def test_get_len_with_non_const_tuple(self): + def test_call_builtin_o(self): def testfunc(n): - x = 0.0 + x = 0 for _ in range(n): - match object(), object(): - case [_, _]: + y = abs(1) + x += y + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CALL_BUILTIN_O", uops) + self.assertNotIn("_GUARD_CALLABLE_BUILTIN_O", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 4) + + def test_call_builtin_fast(self): + def testfunc(n): + x = 0 + for _ in range(n): + y = divmod(10, 3) + x += y[0] + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 3) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CALL_BUILTIN_FAST", uops) + self.assertNotIn("_GUARD_CALLABLE_BUILTIN_FAST", uops) + # divmod(10, 3) should have at least 3 _POP_TOP_NOP + # x += y[0] produces at least 3 _POP_TOP_NOP + self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 6) + + def test_call_builtin_fast_with_keywords(self): + def testfunc(n): + x = 0 + for _ in range(n): + y = sorted([3, 1, 2]) + x += y[0] + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CALL_BUILTIN_FAST_WITH_KEYWORDS", uops) + self.assertNotIn("_GUARD_CALLABLE_BUILTIN_FAST_WITH_KEYWORDS", uops) + + def test_call_method_descriptor_o(self): + def testfunc(n): + x = 0 + for _ in range(n): + y = (1, 2, 3) + z = y.count(2) + x += z + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CALL_METHOD_DESCRIPTOR_O_INLINE", uops) + self.assertNotIn("_CALL_METHOD_DESCRIPTOR_O", uops) + self.assertNotIn("_GUARD_CALLABLE_METHOD_DESCRIPTOR_O", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 4) + + def test_call_method_descriptor_noargs(self): + def testfunc(n): + x = 0 + for _ in range(n): + y = "hello" + z = y.upper() + x += len(z) + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 5) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE", uops) + self.assertNotIn("_CALL_METHOD_DESCRIPTOR_NOARGS", uops) + self.assertNotIn("_GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS", uops) + self.assertGreaterEqual(count_ops(ex, "_POP_TOP"), 5) + self.assertGreaterEqual(count_ops(ex, "_POP_TOP"), 3) + + def test_call_method_descriptor_fast(self): + def testfunc(n): + x = 0 + for _ in range(n): + y = (1, 2, 3) + z = y.index(2) + x += z + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CALL_METHOD_DESCRIPTOR_FAST_INLINE", uops) + self.assertNotIn("_CALL_METHOD_DESCRIPTOR_FAST", uops) + self.assertNotIn("_GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST", uops) + + def test_call_method_descriptor_fast_with_keywords(self): + def testfunc(n): + x = 0 + for _ in range(n): + y = "hello world" + a, b = y.split() + x += len(a) + return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 5) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE", uops) + self.assertNotIn("_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", uops) + self.assertNotIn("_GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", uops) + + def test_check_recursion_limit_deduplicated(self): + def testfunc(n): + x = 0 + for _ in range(n): + y = "hello" + a = y.upper() + b = y.lower() + x += len(a) + x += len(b) + return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 10) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE", uops) + self.assertEqual(count_ops(ex, "_CHECK_RECURSION_LIMIT"), 1) + + def test_call_intrinsic_1(self): + def testfunc(n): + x = 0 + for _ in range(n): + +x + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 0) + uops = get_opnames(ex) + + self.assertIn("_CALL_INTRINSIC_1", uops) + self.assertEqual(count_ops(ex, "_POP_TOP_NOP"), 1) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_call_intrinsic_2(self): + def testfunc(n): + x = 0 + for _ in range(n): + def test_testfunc[T](n): + pass + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 0) + uops = get_opnames(ex) + + self.assertIn("_CALL_INTRINSIC_2", uops) + self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 2) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 4) + + def test_get_len_with_const_tuple(self): + def testfunc(n): + x = 0.0 + for _ in range(n): + match (1, 2, 3, 4): + case [_, _, _, _]: + x += 1.0 + return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(int(res), TIER2_THRESHOLD) + uops = get_opnames(ex) + self.assertNotIn("_GUARD_NOS_INT", uops) + self.assertNotIn("_GET_LEN", uops) + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) + + def test_get_len_with_non_const_tuple(self): + def testfunc(n): + x = 0.0 + for _ in range(n): + match object(), object(): + case [_, _]: x += 1.0 return x res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) @@ -2070,7 +3463,7 @@ def testfunc(n): uops = get_opnames(ex) self.assertIn("_BINARY_OP_SUBSCR_TUPLE_INT", uops) self.assertNotIn("_COMPARE_OP_INT", uops) - self.assertNotIn("_GUARD_IS_TRUE_POP", uops) + self.assertNotIn(self.guard_is_true, uops) def test_call_isinstance_guards_removed(self): def testfunc(n): @@ -2088,10 +3481,6 @@ def testfunc(n): self.assertNotIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_GUARD_THIRD_NULL", uops) self.assertNotIn("_GUARD_CALLABLE_ISINSTANCE", uops) - self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) def test_call_list_append(self): def testfunc(n): @@ -2104,9 +3493,18 @@ def testfunc(n): self.assertEqual(res, sum(range(TIER2_THRESHOLD))) uops = get_opnames(ex) self.assertIn("_CALL_LIST_APPEND", uops) - # We should remove these in the future - self.assertIn("_GUARD_NOS_LIST", uops) - self.assertIn("_GUARD_CALLABLE_LIST_APPEND", uops) + + def test_call_list_append_pop_top(self): + def testfunc(n): + a = [] + for i in range(n): + a.append(1) + return sum(a) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + uops = get_opnames(ex) + self.assertIn("_CALL_LIST_APPEND", uops) + self.assertIn("_POP_TOP_NOP", uops) def test_call_isinstance_is_true(self): def testfunc(n): @@ -2123,11 +3521,7 @@ def testfunc(n): uops = get_opnames(ex) self.assertNotIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_TO_BOOL_BOOL", uops) - self.assertNotIn("_GUARD_IS_TRUE_POP", uops) - self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) + self.assertNotIn(self.guard_is_true, uops) def test_call_isinstance_is_false(self): def testfunc(n): @@ -2144,11 +3538,7 @@ def testfunc(n): uops = get_opnames(ex) self.assertNotIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_TO_BOOL_BOOL", uops) - self.assertNotIn("_GUARD_IS_FALSE_POP", uops) - self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) + self.assertNotIn(self.guard_is_false, uops) def test_call_isinstance_subclass(self): def testfunc(n): @@ -2165,11 +3555,7 @@ def testfunc(n): uops = get_opnames(ex) self.assertNotIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_TO_BOOL_BOOL", uops) - self.assertNotIn("_GUARD_IS_TRUE_POP", uops) - self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops) - self.assertNotIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) + self.assertNotIn(self.guard_is_true, uops) def test_call_isinstance_unknown_object(self): def testfunc(n): @@ -2189,7 +3575,7 @@ def testfunc(n): uops = get_opnames(ex) self.assertIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_TO_BOOL_BOOL", uops) - self.assertIn("_GUARD_IS_TRUE_POP", uops) + self.assertIn(self.guard_is_true, uops) def test_call_isinstance_tuple_of_classes(self): def testfunc(n): @@ -2208,7 +3594,7 @@ def testfunc(n): uops = get_opnames(ex) self.assertIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_TO_BOOL_BOOL", uops) - self.assertIn("_GUARD_IS_TRUE_POP", uops) + self.assertIn(self.guard_is_true, uops) def test_call_isinstance_metaclass(self): class EvenNumberMeta(type): @@ -2233,7 +3619,7 @@ def testfunc(n): uops = get_opnames(ex) self.assertIn("_CALL_ISINSTANCE", uops) self.assertNotIn("_TO_BOOL_BOOL", uops) - self.assertIn("_GUARD_IS_TRUE_POP", uops) + self.assertIn(self.guard_is_true, uops) def test_set_type_version_sets_type(self): class C: @@ -2254,6 +3640,39 @@ def testfunc(n): self.assertIn("_GUARD_TYPE_VERSION", uops) self.assertNotIn("_CHECK_ATTR_CLASS", uops) + def test_load_common_constant(self): + def testfunc(n): + for _ in range(n): + x = list(i for i in ()) + return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, list(())) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BUILD_LIST", uops) + self.assertNotIn("_LOAD_COMMON_CONSTANT", uops) + + def test_load_common_constant_new_literals(self): + def testfunc(n): + x = None + s = "" + t = True + f = False + m = -1 + for _ in range(n): + x = None + s = "" + t = True + f = False + m = -1 + return x, s, t, f, m + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, (None, "", True, False, -1)) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_LOAD_COMMON_CONSTANT", uops) + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) + def test_load_small_int(self): def testfunc(n): x = 0 @@ -2305,6 +3724,84 @@ def f(n): self.assertNotIn("_LOAD_ATTR_METHOD_NO_DICT", uops) self.assertNotIn("_LOAD_ATTR_METHOD_LAZY_DICT", uops) + def test_cached_attributes_fixed_version_tag(self): + def f(n): + c = 1 + x = 0 + for _ in range(n): + x += c.bit_length() + return x + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + self.assertEqual(res, TIER2_THRESHOLD) + uops = get_opnames(ex) + self.assertNotIn("_LOAD_ATTR_METHOD_NO_DICT", uops) + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) + + def test_cached_load_special(self): + class CM: + def __enter__(self): + return self + def __exit__(self, *args): + pass + def f(n): + cm = CM() + x = 0 + for _ in range(n): + with cm: + x += 1 + return x + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + self.assertEqual(res, TIER2_THRESHOLD) + uops = get_opnames(ex) + self.assertNotIn("_LOAD_SPECIAL", uops) + # __enter__/__exit__ produce 2 _POP_TOP_NOP + # x += 1 produces 2 _POP_TOP_NOP + # __exit__()'s None return produces 1 _POP_TOP_NOP + self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 5) + + def test_store_fast_refcount_elimination(self): + def foo(x): + # Since x is known to be + # a constant value (1) here, + # The refcount is eliminated in the STORE_FAST. + x = 2 + return x + def testfunc(n): + # The STORE_FAST for the range here needs a POP_TOP + # (for now, until we do loop peeling). + for _ in range(n): + foo(1) + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_POP_TOP_NOP", uops) + self.assertIn("_PUSH_FRAME", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 1) + + def test_store_fast_refcount_elimination_when_uninitialized(self): + def foo(): + # Since y is known to be + # uninitialized (NULL) here, + # The refcount is eliminated in the STORE_FAST. + y = 2 + return y + def testfunc(n): + # The STORE_FAST for the range here needs a POP_TOP + # (for now, until we do loop peeling). + for _ in range(n): + foo() + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_POP_TOP_NOP", uops) + self.assertIn("_PUSH_FRAME", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 1) + + def test_float_op_refcount_elimination(self): def testfunc(args): a, b, n = args @@ -2317,158 +3814,2390 @@ def testfunc(args): self.assertAlmostEqual(res, TIER2_THRESHOLD * (0.1 + 0.1)) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_BINARY_OP_ADD_FLOAT__NO_DECREF_INPUTS", uops) + self.assertIn("_POP_TOP_NOP", uops) - def test_remove_guard_for_slice_list(self): - def f(n): - for i in range(n): - false = i == TIER2_THRESHOLD - sliced = [1, 2, 3][:false] - if sliced: - return 1 - return 0 + def test_float_add_inplace_unique_lhs(self): + # a * b produces a unique float; adding c reuses it in place + def testfunc(args): + a, b, c, n = args + total = 0.0 + for _ in range(n): + total += a * b + c + return total - res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) - self.assertEqual(res, 0) + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 4.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * 10.0) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_TO_BOOL_LIST", uops) - self.assertNotIn("_GUARD_TOS_LIST", uops) + self.assertIn("_BINARY_OP_ADD_FLOAT_INPLACE", uops) - def test_remove_guard_for_slice_tuple(self): - def f(n): - for i in range(n): - false = i == TIER2_THRESHOLD - a, b = (1, 2, 3)[: false + 2] + def test_float_add_inplace_unique_rhs(self): + # a * b produces a unique float on the right side of + + def testfunc(args): + a, b, c, n = args + total = 0.0 + for _ in range(n): + total += c + a * b + return total - _, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 4.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * 10.0) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops) - self.assertNotIn("_GUARD_TOS_TUPLE", uops) + self.assertIn("_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT", uops) - def test_unary_invert_long_type(self): - def testfunc(n): + def test_float_add_no_inplace_non_unique(self): + # Both operands of a + b are locals — neither is unique, + # so the first add is regular. But total += (a+b) has a + # unique RHS, so it uses _INPLACE_RIGHT. + def testfunc(args): + a, b, n = args + total = 0.0 for _ in range(n): - a = 9397 - x = ~a + ~a - - testfunc(TIER2_THRESHOLD) + total += a + b + return total - ex = get_first_executor(testfunc) + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * 5.0) self.assertIsNotNone(ex) uops = get_opnames(ex) + # a + b: both are locals, no inplace + self.assertIn("_BINARY_OP_ADD_FLOAT", uops) + # total += result: result is unique RHS + self.assertIn("_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT", uops) + # No LHS inplace variant for the first add + self.assertNotIn("_BINARY_OP_ADD_FLOAT_INPLACE", uops) + + def test_float_subtract_inplace_unique_lhs(self): + # a * b produces a unique float; subtracting c reuses it + def testfunc(args): + a, b, c, n = args + total = 0.0 + for _ in range(n): + total += a * b - c + return total - self.assertNotIn("_GUARD_TOS_INT", uops) - self.assertNotIn("_GUARD_NOS_INT", uops) + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 1.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * 5.0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBTRACT_FLOAT_INPLACE", uops) - def test_attr_promotion_failure(self): - # We're not testing for any specific uops here, just - # testing it doesn't crash. - script_helper.assert_python_ok('-c', textwrap.dedent(""" - import _testinternalcapi - import _opcode - import email + def test_float_subtract_inplace_unique_rhs(self): + # a * b produces a unique float on the right of -; + # result is c - (a * b), must get the sign correct + def testfunc(args): + a, b, c, n = args + total = 0.0 + for _ in range(n): + total += c - a * b + return total - def get_first_executor(func): - code = func.__code__ - co_code = code.co_code - for i in range(0, len(co_code), 2): - try: - return _opcode.get_executor(code, i) - except ValueError: - pass - return None + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 1.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * -5.0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT", uops) - def testfunc(n): + def test_float_multiply_inplace_unique_lhs(self): + # (a + b) produces a unique float; multiplying by c reuses it + def testfunc(args): + a, b, c, n = args + total = 0.0 for _ in range(n): - email.jit_testing = None - prompt = email.jit_testing - del email.jit_testing + total += (a + b) * c + return total + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 4.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * 20.0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_MULTIPLY_FLOAT_INPLACE", uops) + + def test_float_multiply_inplace_unique_rhs(self): + # (a + b) produces a unique float on the right side of * + def testfunc(args): + a, b, c, n = args + total = 0.0 + for _ in range(n): + total += c * (a + b) + return total + + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 4.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * 20.0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT", uops) + + def test_float_inplace_chain_propagation(self): + # a * b + c * d: both products are unique, the + reuses one; + # result of + is also unique for the subsequent += + def testfunc(args): + a, b, c, d, n = args + total = 0.0 + for _ in range(n): + total += a * b + c * d + return total + + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 4.0, 5.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * 26.0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + # The + between the two products should use an inplace variant + inplace_add = ( + "_BINARY_OP_ADD_FLOAT_INPLACE" in uops + or "_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT" in uops + ) + self.assertTrue(inplace_add, + "Expected an inplace add for unique intermediate results") + + def test_float_negate_inplace_unique(self): + # -(a * b): the product is unique, negate it in place + def testfunc(args): + a, b, n = args + total = 0.0 + for _ in range(n): + total += -(a * b) + return total + + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * -6.0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_UNARY_NEGATIVE_FLOAT_INPLACE", uops) + + def test_float_negate_no_inplace_non_unique(self): + # -a where a is a local — not unique, no inplace + def testfunc(args): + a, n = args + total = 0.0 + for _ in range(n): + total += -a + return total + + res, ex = self._run_with_optimizer(testfunc, (2.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * -2.0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_UNARY_NEGATIVE_FLOAT_INPLACE", uops) + + def test_float_truediv_inplace_unique_lhs(self): + # (a + b) / (c + d): LHS is unique float from add, RHS is unique + # float from add. The division reuses the LHS in place. + def testfunc(args): + a, b, c, d, n = args + total = 0.0 + for _ in range(n): + total += (a + b) / (c + d) + return total + + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 1.0, 3.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * 1.25) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE", uops) + + def test_float_truediv_inplace_unique_rhs(self): + # x = c + d stores to a local (not unique when reloaded). + # (a + b) is unique. The division should use inplace on the RHS. + def testfunc(args): + a, b, c, d, n = args + total = 0.0 + for _ in range(n): + x = c + d + total += x / (a + b) + return total + + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, 4.0, 5.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * (9.0 / 5.0)) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT", uops) + + def test_float_truediv_speculative_guards_from_tracing(self): + # a, b are locals with no statically known type. _RECORD_TOS_TYPE / + # _RECORD_NOS_TYPE (added to the BINARY_OP macro) capture the observed + # operand types during tracing, and the optimizer then speculatively + # emits _GUARD_{TOS,NOS}_FLOAT and specializes the division. + def testfunc(args): + a, b, n = args + total = 0.0 + for _ in range(n): + total += a / b + return total + + res, ex = self._run_with_optimizer(testfunc, (10.0, 3.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * (10.0 / 3.0)) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_GUARD_TOS_FLOAT", uops) + self.assertIn("_GUARD_NOS_FLOAT", uops) + self.assertIn("_BINARY_OP_TRUEDIV_FLOAT", uops) + + def test_float_remainder_speculative_guards_from_tracing(self): + # a, b are locals with no statically known type. Tracing records + # them as floats; the optimizer then speculatively emits + # _GUARD_{TOS,NOS}_FLOAT for NB_REMAINDER. That narrows both + # operands to float, and the _BINARY_OP handler marks the result + # as a unique float. Downstream, `* 2.0` therefore specializes + # to _BINARY_OP_MULTIPLY_FLOAT_INPLACE. + def testfunc(args): + a, b, n = args + total = 0.0 + for _ in range(n): + total += (a % b) * 2.0 + return total + + res, ex = self._run_with_optimizer(testfunc, (10.0, 3.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * (10.0 % 3.0) * 2.0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_GUARD_TOS_FLOAT", uops) + self.assertIn("_GUARD_NOS_FLOAT", uops) + self.assertIn("_BINARY_OP_MULTIPLY_FLOAT_INPLACE", uops) + + def test_float_truediv_type_propagation(self): + # Test the _BINARY_OP_TRUEDIV_FLOAT propagates type information + def testfunc(args): + a, b, n = args + total = 0.0 + for _ in range(n): + x = (a + b) # type of x will specialize to float + total += x / x - x / x + return total + + res, ex = self._run_with_optimizer(testfunc, + (2.0, 3.0, TIER2_THRESHOLD)) + expected = TIER2_THRESHOLD * ((2.0 + 3.0) / (2.0 + 3.0) - (2.0 + 3.0) / (2.0 + 3.0)) + self.assertAlmostEqual(res, expected) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_TRUEDIV_FLOAT", uops) + self.assertIn("_BINARY_OP_SUBTRACT_FLOAT_INPLACE", uops) + + def test_float_truediv_unique_result_enables_inplace(self): + # (a+b) / (c+d) / (e+f): chained divisions where each result + # is unique, enabling inplace for subsequent divisions. + def testfunc(args): + a, b, c, d, e, f, n = args + total = 0.0 + for _ in range(n): + total += (a + b) / (c + d) / (e + f) + return total + + res, ex = self._run_with_optimizer(testfunc, + (2.0, 3.0, 1.0, 1.0, 1.0, 1.0, TIER2_THRESHOLD)) + expected = TIER2_THRESHOLD * ((2.0 + 3.0) / (1.0 + 1.0) / (1.0 + 1.0)) + self.assertAlmostEqual(res, expected) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_TRUEDIV_FLOAT_INPLACE", uops) + + def test_float_add_chain_both_unique(self): + # (a+b) + (c+d): both sub-additions produce unique floats. + # The outer + should use inplace on one of them. + def testfunc(args): + a, b, c, d, n = args + total = 0.0 + for _ in range(n): + total += (a + b) + (c + d) + return total + + res, ex = self._run_with_optimizer(testfunc, (1.0, 2.0, 3.0, 4.0, TIER2_THRESHOLD)) + self.assertAlmostEqual(res, TIER2_THRESHOLD * 10.0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + # The outer + should use inplace (at least one operand is unique) + inplace = ( + "_BINARY_OP_ADD_FLOAT_INPLACE" in uops + or "_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT" in uops + ) + self.assertTrue(inplace, "Expected inplace add for unique sub-results") + + def test_float_truediv_non_float_type_no_crash(self): + # Fraction / Fraction goes through _BINARY_OP with NB_TRUE_DIVIDE + # but returns Fraction, not float. The optimizer must not assume + # the result is float for non-int/float operands. See gh-146306. + from fractions import Fraction + def testfunc(args): + a, b, n = args + total = Fraction(0) + for _ in range(n): + total += a / b + return float(total) + + res, ex = self._run_with_optimizer(testfunc, (Fraction(10), Fraction(3), TIER2_THRESHOLD)) + expected = float(TIER2_THRESHOLD * Fraction(10, 3)) + self.assertAlmostEqual(res, expected) + + def test_float_truediv_mixed_float_fraction_no_crash(self): + # float / Fraction: lhs is known float from a prior guard, + # but rhs is Fraction. The guard insertion for rhs should + # deopt cleanly at runtime, not crash. + from fractions import Fraction + def testfunc(args): + a, b, c, n = args + total = 0.0 + for _ in range(n): + total += (a + b) / c # (a+b) is float, c is Fraction + return total + + res, ex = self._run_with_optimizer(testfunc, (2.0, 3.0, Fraction(4), TIER2_THRESHOLD)) + expected = TIER2_THRESHOLD * (5.0 / Fraction(4)) + self.assertAlmostEqual(res, float(expected)) + + def test_float_truediv_partial_float_no_stack_underflow(self): + # gh-149049: a speculative _GUARD_*_FLOAT for a partially-float + # truediv/remainder must not drop the original _BINARY_OP. + def truediv(args): + n, = args + nan = float("nan") + def victim(a=0, b=nan, c=2): + return (a + b) / c + for _ in range(n): + victim() + + def remainder(args): + n, = args + nan = float("nan") + def victim(a=0, b=nan, c=2): + return (a + b) % c + for _ in range(n): + victim() + + for testfunc in (truediv, remainder): + with self.subTest(op=testfunc.__name__): + # Iterations must be high enough that the buggy trace + # is not only built but executed (where it underflows). + _, ex = self._run_with_optimizer( + testfunc, (TIER2_THRESHOLD * 10,)) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertTrue( + "_GUARD_TOS_FLOAT" in uops or "_GUARD_NOS_FLOAT" in uops, + uops, + ) + + def test_int_add_inplace_unique_lhs(self): + # a * b produces a unique compact int; adding c reuses it in place + def testfunc(args): + a, b, c, n = args + total = 0 + for _ in range(n): + total += a * b + c + return total + + res, ex = self._run_with_optimizer(testfunc, (2000, 3, 4000, TIER2_THRESHOLD)) + self.assertEqual(res, TIER2_THRESHOLD * 10000) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_ADD_INT_INPLACE", uops) + + def test_int_add_inplace_unique_rhs(self): + # a * b produces a unique compact int on the right side of + + def testfunc(args): + a, b, c, n = args + total = 0 + for _ in range(n): + total += c + a * b + return total + + res, ex = self._run_with_optimizer(testfunc, (2000, 3, 4000, TIER2_THRESHOLD)) + self.assertEqual(res, TIER2_THRESHOLD * 10000) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_ADD_INT_INPLACE_RIGHT", uops) + + def test_int_add_no_inplace_non_unique(self): + # Both operands of a + b are locals — neither is unique, + # so the first add uses the regular op. But total += (a+b) + # has a unique RHS (result of a+b), so it uses _INPLACE_RIGHT. + def testfunc(args): + a, b, n = args + total = 0 + for _ in range(n): + total += a + b + return total + + res, ex = self._run_with_optimizer(testfunc, (2000, 3000, TIER2_THRESHOLD)) + self.assertEqual(res, TIER2_THRESHOLD * 5000) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + # a + b: both are locals, no inplace + self.assertIn("_BINARY_OP_ADD_INT", uops) + # total += result: result is unique RHS + self.assertIn("_BINARY_OP_ADD_INT_INPLACE_RIGHT", uops) + # No LHS inplace variant for the first add + self.assertNotIn("_BINARY_OP_ADD_INT_INPLACE", uops) + + def test_int_add_inplace_small_int_result(self): + # When the result is a small int, the inplace path falls back + # to _PyCompactLong_Add. Verify correctness (no singleton corruption). + def testfunc(args): + a, b, n = args + total = 0 + for _ in range(n): + total += a * b + 1 # a*b=6, +1=7, small int + return total + + res, ex = self._run_with_optimizer(testfunc, (2, 3, TIER2_THRESHOLD)) + self.assertEqual(res, TIER2_THRESHOLD * 7) + # Verify small int singletons are not corrupted + self.assertEqual(7, 3 + 4) + + def test_int_subtract_inplace_unique_lhs(self): + # a * b produces a unique compact int; subtracting c reuses it + def testfunc(args): + a, b, c, n = args + total = 0 + for _ in range(n): + total += a * b - c + return total + + res, ex = self._run_with_optimizer(testfunc, (2000, 3, 1000, TIER2_THRESHOLD)) + self.assertEqual(res, TIER2_THRESHOLD * 5000) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBTRACT_INT_INPLACE", uops) + + def test_int_subtract_inplace_unique_rhs(self): + # a * b produces a unique compact int on the right of - + def testfunc(args): + a, b, c, n = args + total = 0 + for _ in range(n): + total += c - a * b + return total + + res, ex = self._run_with_optimizer(testfunc, (2000, 3, 10000, TIER2_THRESHOLD)) + self.assertEqual(res, TIER2_THRESHOLD * 4000) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT", uops) + + def test_int_multiply_inplace_unique_lhs(self): + # (a + b) produces a unique compact int; multiplying by c reuses it + def testfunc(args): + a, b, c, n = args + total = 0 + for _ in range(n): + total += (a + b) * c + return total + + res, ex = self._run_with_optimizer(testfunc, (2000, 3000, 4, TIER2_THRESHOLD)) + self.assertEqual(res, TIER2_THRESHOLD * 20000) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_MULTIPLY_INT_INPLACE", uops) + + def test_int_multiply_inplace_unique_rhs(self): + # (a + b) produces a unique compact int on the right side of * + def testfunc(args): + a, b, c, n = args + total = 0 + for _ in range(n): + total += c * (a + b) + return total + + res, ex = self._run_with_optimizer(testfunc, (2000, 3000, 4, TIER2_THRESHOLD)) + self.assertEqual(res, TIER2_THRESHOLD * 20000) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT", uops) + + def test_int_inplace_chain_propagation(self): + # a * b + c * d: both products are unique, the + reuses one; + # result of + is also unique for the subsequent += + def testfunc(args): + a, b, c, d, n = args + total = 0 + for _ in range(n): + total += a * b + c * d + return total + + res, ex = self._run_with_optimizer(testfunc, (2000, 3, 4000, 5, TIER2_THRESHOLD)) + self.assertEqual(res, TIER2_THRESHOLD * 26000) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + inplace_add = ( + "_BINARY_OP_ADD_INT_INPLACE" in uops + or "_BINARY_OP_ADD_INT_INPLACE_RIGHT" in uops + ) + self.assertTrue(inplace_add, + "Expected an inplace add for unique intermediate results") + + def test_load_attr_instance_value(self): + def testfunc(n): + class C(): + pass + c = C() + c.x = n + x = 0 + for _ in range(n): + x = c.x + return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_LOAD_ATTR_INSTANCE_VALUE", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + self.assertIn("_POP_TOP_NOP", uops) + + def test_load_attr_module(self): + def testfunc(n): + import math + x = 0 + for _ in range(n): + y = math.pi + if y: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn(("_LOAD_ATTR_MODULE", "_POP_TOP_NOP"), itertools.pairwise(uops)) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_load_attr_with_hint(self): + def testfunc(n): + class C: + pass + c = C() + c.x = 42 + for i in range(_testinternalcapi.SHARED_KEYS_MAX_SIZE - 1): + setattr(c, f"_{i}", None) + x = 0 + for i in range(n): + x += c.x + return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 42 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_LOAD_ATTR_WITH_HINT", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + self.assertIn("_POP_TOP_NOP", uops) + + def test_load_addr_slot(self): + def testfunc(n): + class C: + __slots__ = ('x',) + c = C() + c.x = 42 + x = 0 + for _ in range(n): + x += c.x + return x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 42 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_LOAD_ATTR_SLOT", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + self.assertIn("_POP_TOP_NOP", uops) + + def test_int_add_op_refcount_elimination(self): + def testfunc(n): + c = 1 + res = 0 + for _ in range(n): + res = c + c + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_ADD_INT", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_int_sub_op_refcount_elimination(self): + def testfunc(n): + c = 1 + res = 0 + for _ in range(n): + res = c - c + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_SUBTRACT_INT", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_int_mul_op_refcount_elimination(self): + def testfunc(n): + c = 1 + res = 0 + for _ in range(n): + res = c * c + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_MULTIPLY_INT", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_int_cmp_op_refcount_elimination(self): + def testfunc(n): + c = 1 + res = 0 + for _ in range(n): + res = c == c + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_COMPARE_OP_INT", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_float_cmp_op_refcount_elimination(self): + def testfunc(n): + c = 1.0 + res = False + for _ in range(n): + res = c == c + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_COMPARE_OP_FLOAT", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_str_cmp_op_refcount_elimination(self): + def testfunc(n): + c = "a" + res = False + for _ in range(n): + res = c == c + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_COMPARE_OP_STR", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_unicode_add_op_refcount_elimination(self): + def testfunc(n): + c = "a" + res = "" + for _ in range(n): + res = c + c + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, "aa") + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_ADD_UNICODE", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_binary_op_refcount_elimination(self): + class CustomAdder: + def __init__(self, val): + self.val = val + def __add__(self, other): + return CustomAdder(self.val + other.val) + + def testfunc(n): + a = CustomAdder(1) + b = CustomAdder(2) + res = None + for _ in range(n): + res = a + b + return res.val if res else 0 + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 3) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_binary_op_extend_float_long_add_refcount_elimination(self): + def testfunc(n): + a = 1.5 + b = 2 + res = 0.0 + for _ in range(n): + res = a + b + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 3.5) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_EXTEND", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_remove_guard_for_slice_list(self): + def f(n): + for i in range(n): + false = i == TIER2_THRESHOLD + sliced = [1, 2, 3][:false] + if sliced: + return 1 + return 0 + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, 0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_TO_BOOL_LIST", uops) + self.assertNotIn("_GUARD_TOS_LIST", uops) + + def test_remove_guard_for_slice_tuple(self): + def f(n): + for i in range(n): + false = i == TIER2_THRESHOLD + a, b = (1, 2, 3)[: false + 2] + + _, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_UNPACK_SEQUENCE_TWO_TUPLE", uops) + self.assertNotIn("_GUARD_TOS_TUPLE", uops) + + def test_binary_op_extend_float_result_enables_inplace_multiply(self): + # (2 + x) * y with x, y floats: `2 + x` goes through _BINARY_OP_EXTEND + # (int + float). The result_type/result_unique info should let the + # subsequent float multiply use the inplace variant. + def testfunc(n): + x = 3.5 + y = 2.0 + res = 0.0 + for _ in range(n): + res = (2 + x) * y + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 11.0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_EXTEND", uops) + self.assertIn("_BINARY_OP_MULTIPLY_FLOAT_INPLACE", uops) + self.assertNotIn("_BINARY_OP_MULTIPLY_FLOAT", uops) + # NOS guard on the multiply is eliminated because _BINARY_OP_EXTEND + # propagates PyFloat_Type. + self.assertNotIn("_GUARD_NOS_FLOAT", uops) + + def test_binary_op_extend_list_concat_type_propagation(self): + # list + list is specialized via BINARY_OP_EXTEND. The tier 2 optimizer + # should learn that the result is a list and eliminate subsequent + # list-type guards. + def testfunc(n): + a = [1, 2] + b = [3, 4] + x = True + for _ in range(n): + c = a + b + if c[0]: + x = False + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, False) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_EXTEND", uops) + # The c[0] subscript emits _GUARD_NOS_LIST before _BINARY_OP_SUBSCR_LIST_INT; + # since _BINARY_OP_EXTEND now propagates PyList_Type, that guard is gone. + self.assertIn("_BINARY_OP_SUBSCR_LIST_INT", uops) + self.assertNotIn("_GUARD_NOS_LIST", uops) + + def test_binary_op_extend_tuple_concat_type_propagation(self): + # tuple + tuple is specialized via BINARY_OP_EXTEND. The tier 2 optimizer + # should learn the result is a tuple and eliminate subsequent tuple guards. + def testfunc(n): + t1 = (1, 2) + t2 = (3, 4) + for _ in range(n): + a, b, c, d = t1 + t2 + return a + b + c + d + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 10) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_EXTEND", uops) + self.assertIn("_UNPACK_SEQUENCE_TUPLE", uops) + self.assertNotIn("_GUARD_TOS_TUPLE", uops) + + def test_binary_op_extend_guard_elimination(self): + # When both operands have known types (e.g., from a prior + # _BINARY_OP_EXTEND result), the _GUARD_BINARY_OP_EXTEND + # should be eliminated. + def testfunc(n): + a = [1, 2] + b = [3, 4] + total = 0 + for _ in range(n): + c = a + b # first: guard stays, result type = list + d = c + c # second: both operands are list -> guard eliminated + total += d[0] + return total + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + # Both list additions use _BINARY_OP_EXTEND + self.assertEqual(uops.count("_BINARY_OP_EXTEND"), 2) + # But the second guard is eliminated because both operands + # are known to be lists from the first _BINARY_OP_EXTEND. + self.assertEqual(uops.count("_GUARD_BINARY_OP_EXTEND"), 1) + + def test_binary_op_extend_partial_guard_lhs_known(self): + # When the lhs type is already known (from a prior _BINARY_OP_EXTEND + # result) but the rhs type is not, the optimizer should emit + # _GUARD_BINARY_OP_EXTEND_RHS (checking only the rhs) instead of + # the full _GUARD_BINARY_OP_EXTEND. + def testfunc(n): + a = [1, 2] + b = [3, 4] + total = 0 + for _ in range(n): + c = a + b # result type is list (known) + d = c + b # lhs (c) is known list, rhs (b) is not -> _GUARD_BINARY_OP_EXTEND_RHS + total += d[0] + return total + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_EXTEND", uops) + self.assertIn("_GUARD_BINARY_OP_EXTEND_RHS", uops) + self.assertNotIn("_GUARD_BINARY_OP_EXTEND_LHS", uops) + + def test_binary_op_extend_partial_guard_rhs_known(self): + # When the rhs type is already known (from a prior _BINARY_OP_EXTEND + # result) but the lhs type is not, the optimizer should emit + # _GUARD_BINARY_OP_EXTEND_LHS (checking only the lhs) instead of + # the full _GUARD_BINARY_OP_EXTEND. + def testfunc(n): + a = [1, 2] + b = [3, 4] + total = 0 + for _ in range(n): + c = a + b # result type is list (known) + d = b + c # rhs (c) is known list, lhs (b) is not -> _GUARD_BINARY_OP_EXTEND_LHS + total += d[2] + return total + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_BINARY_OP_EXTEND", uops) + self.assertIn("_GUARD_BINARY_OP_EXTEND_LHS", uops) + self.assertNotIn("_GUARD_BINARY_OP_EXTEND_RHS", uops) + + def test_unary_invert_long_type(self): + def testfunc(n): + for _ in range(n): + a = 9397 + x = ~a + ~a + + testfunc(TIER2_THRESHOLD) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertNotIn("_GUARD_TOS_INT", uops) + self.assertNotIn("_GUARD_NOS_INT", uops) + + def test_store_attr_instance_value(self): + def testfunc(n): + class C: + pass + c = C() + for i in range(n): + c.a = i + return c.a + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD - 1) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_STORE_ATTR_INSTANCE_VALUE", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 1) + self.assertIn("_POP_TOP_NOP", uops) + + def test_store_attr_with_hint(self): + def testfunc(n): + class C: + pass + c = C() + for i in range(_testinternalcapi.SHARED_KEYS_MAX_SIZE - 1): + setattr(c, f"_{i}", None) + + for i in range(n): + c.x = i + return c.x + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD - 1) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_STORE_ATTR_WITH_HINT", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 1) + self.assertIn("_POP_TOP_NOP", uops) + + def test_store_subscr_int(self): + def testfunc(n): + l = [0, 0, 0, 0] + for _ in range(n): + l[0] = 1 + l[1] = 2 + l[2] = 3 + l[3] = 4 + return sum(l) + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 10) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_STORE_SUBSCR_LIST_INT", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 1) + self.assertNotIn("_POP_TOP_INT", uops) + self.assertIn("_POP_TOP_NOP", uops) + + def test_store_attr_slot(self): + class C: + __slots__ = ('x',) + + def testfunc(n): + c = C() + for _ in range(n): + c.x = 42 + y = c.x + return y + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 42) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_STORE_ATTR_SLOT", uops) + self.assertIn("_POP_TOP_NOP", uops) + + def test_store_subscr_dict(self): + def testfunc(n): + d = {} + for _ in range(n): + d['a'] = 1 + d['b'] = 2 + d['c'] = 3 + d['d'] = 4 + return sum(d.values()) + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 10) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_STORE_SUBSCR_DICT_KNOWN_HASH", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 1) + self.assertIn("_POP_TOP_NOP", uops) + + def test_to_bool_str(self): + def f(n): + for i in range(n): + false = i == TIER2_THRESHOLD + empty = "X"[:false] + if empty: + return 1 + return 0 + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, 0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_TO_BOOL_STR", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 3) + self.assertIn("_POP_TOP_NOP", uops) + + def test_to_bool_int(self): + def f(n): + for i in range(n): + truthy = (i == TIER2_THRESHOLD) + x = 0 + truthy + if x: + return 1 + return 0 + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, 0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_TO_BOOL_INT", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 3) + self.assertIn("_POP_TOP_NOP", uops) + + def test_to_bool_list(self): + def f(n): + for i in range(n): + lst = [] if i != TIER2_THRESHOLD else [1] + if lst: + return 1 + return 0 + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, 0) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_TO_BOOL_LIST", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 3) + self.assertIn("_POP_TOP_NOP", uops) + + def test_to_bool_always_true(self): + def testfunc(n): + class A: + pass + + a = A() + for _ in range(n): + if not a: + return 0 + return 1 + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 1) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_REPLACE_WITH_TRUE", uops) + + def test_attr_promotion_failure(self): + # We're not testing for any specific uops here, just + # testing it doesn't crash. + script_helper.assert_python_ok('-c', textwrap.dedent(""" + import _testinternalcapi + import _opcode + import email + + def get_first_executor(func): + code = func.__code__ + co_code = code.co_code + for i in range(0, len(co_code), 2): + try: + return _opcode.get_executor(code, i) + except ValueError: + pass + return None + + def testfunc(n): + for _ in range(n): + email.jit_testing = None + prompt = email.jit_testing + del email.jit_testing + + + testfunc(_testinternalcapi.TIER2_THRESHOLD) + """)) + + def test_pop_top_specialize_none(self): + def testfunc(n): + for _ in range(n): + global_identity(None) + + testfunc(TIER2_THRESHOLD) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_POP_TOP_NOP", uops) + + def test_pop_top_specialize_int(self): + def testfunc(n): + for _ in range(n): + global_identity(100000) + + testfunc(TIER2_THRESHOLD) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_POP_TOP_INT", uops) + + def test_pop_top_specialize_float(self): + def testfunc(n): + for _ in range(n): + global_identity(1e6) + + testfunc(TIER2_THRESHOLD) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_POP_TOP_FLOAT", uops) + + + def test_unary_negative_long_float_type(self): + def testfunc(n): + for _ in range(n): + a = 9397 + f = 9397.0 + x = -a + -a + y = -f + -f + + testfunc(TIER2_THRESHOLD) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertNotIn("_GUARD_TOS_INT", uops) + self.assertNotIn("_GUARD_NOS_INT", uops) + self.assertNotIn("_GUARD_TOS_FLOAT", uops) + self.assertNotIn("_GUARD_NOS_FLOAT", uops) + + def test_binary_op_constant_evaluate(self): + def testfunc(n): + for _ in range(n): + 2 ** 65 + + testfunc(TIER2_THRESHOLD) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + # For now... until we constant propagate it away. + self.assertIn("_BINARY_OP", uops) + + def test_jitted_code_sees_changed_globals(self): + "Issue 136154: Check that jitted code spots the change in the globals" + + def make_f(): + def f(): + return GLOBAL_136154 + return f + + make_f_with_bad_globals = types.FunctionType(make_f.__code__, {}) + + def jitted(funcs): + for func in funcs: + func() + + # Make a "good" f: + f = make_f() + # Compile jitted for the "good" f: + jitted([f] * TIER2_THRESHOLD) + # This "bad" f has different globals, but the *same* code/function versions: + f_with_bad_globals = make_f_with_bad_globals() + # A "good" f to enter the JIT code, and a "bad" f to trigger the bug: + with self.assertRaises(NameError): + jitted([f, f_with_bad_globals]) + + def test_reference_tracking_across_call_doesnt_crash(self): + + def f1(): + for _ in range(TIER2_THRESHOLD + 1): + # Choose a value that won't occur elsewhere to avoid sharing + str("value that won't occur elsewhere to avoid sharing") + + f1() + + def f2(): + for _ in range(TIER2_THRESHOLD + 1): + # Choose a value that won't occur elsewhere to avoid sharing + tuple((31, -17, 25, "won't occur elsewhere")) + + f2() + + def test_next_instr_for_exception_handler_set(self): + # gh-140104: We just want the exception to be caught properly. + def f(): + for i in range(TIER2_THRESHOLD + 3): + try: + undefined_variable(i) + except Exception: + pass + + f() + + def test_next_instr_for_exception_handler_set_lasts_instr(self): + # gh-140104: We just want the exception to be caught properly. + def f(): + a_list = [] + for _ in range(TIER2_THRESHOLD + 3): + try: + a_list[""] = 0 + except Exception: + pass + + f() + + def test_interpreter_finalization_with_generator_alive(self): + script_helper.assert_python_ok("-c", textwrap.dedent(""" + import sys + t = tuple(range(%d)) + def simple_for(): + for x in t: + x + + def gen(): + try: + yield + except: + simple_for() + + sys.settrace(lambda *args: None) + simple_for() + g = gen() + next(g) + """ % _testinternalcapi.SPECIALIZATION_THRESHOLD)) + + def test_executor_side_exits_create_another_executor(self): + def f(): + for x in range(TIER2_THRESHOLD + 3): + for y in range(TIER2_THRESHOLD + 3): + z = x + y + + f() + all_executors = get_all_executors(f) + # Inner loop warms up first. + # Outer loop warms up later, linking to the inner one. + # Therefore, we have at least two executors. + self.assertGreaterEqual(len(all_executors), 2) + executor_ids = [id(e) for e in all_executors] + for executor in all_executors: + ops = get_ops(executor) + # Assert all executors first terminator ends in + # _EXIT_TRACE or _JUMP_TO_TOP, not _DEOPT + for idx, op in enumerate(ops): + opname = op[0] + if opname == "_EXIT_TRACE": + # As this is a link outer executor to inner + # executor problem, all executors exits should point to + # another valid executor. In this case, none of them + # should be the cold executor. + exit = op[3] + link_to = _testinternalcapi.get_exit_executor(exit) + self.assertIn(id(link_to), executor_ids) + break + elif opname == "_JUMP_TO_TOP": + break + elif opname == "_DEOPT": + self.fail(f"_DEOPT encountered first at executor" + f" {executor} at offset {idx} rather" + f" than expected _EXIT_TRACE") + + def test_enter_executor_valid_op_arg(self): + script_helper.assert_python_ok("-c", textwrap.dedent(""" + import sys + sys.setrecursionlimit(30) # reduce time of the run + + str_v1 = '' + tuple_v2 = (None, None, None, None, None) + small_int_v3 = 4 + + def f1(): + + for _ in range(10): + abs(0) + + tuple_v2[small_int_v3] + tuple_v2[small_int_v3] + tuple_v2[small_int_v3] + + def recursive_wrapper_4569(): + str_v1 > str_v1 + str_v1 > str_v1 + str_v1 > str_v1 + recursive_wrapper_4569() + + recursive_wrapper_4569() + + for i_f1 in range(19000): + try: + f1() + except RecursionError: + pass + """)) + + def test_attribute_changes_are_watched(self): + # Just running to make sure it doesn't crash. + script_helper.assert_python_ok("-c", textwrap.dedent(""" + from concurrent.futures import ThreadPoolExecutor + from unittest import TestCase + NTHREADS = 6 + BOTTOM = 0 + TOP = 1250000 + class A: + attr = 10**1000 + class TestType(TestCase): + def read(id0): + for _ in range(BOTTOM, TOP): + A.attr + def write(id0): + x = A.attr + x += 1 + A.attr = x + with ThreadPoolExecutor(NTHREADS) as pool: + pool.submit(read, (1,)) + pool.submit(write, (1,)) + """)) + + def test_handling_of_tos_cache_with_side_exits(self): + # https://github.com/python/cpython/issues/142718 + class EvilAttr: + def __init__(self, d): + self.d = d + + def __del__(self): + try: + del self.d['attr'] + except Exception: + pass + + class Obj: + pass + + obj = Obj() + obj.__dict__ = {} + + for _ in range(TIER2_THRESHOLD+1): + obj.attr = EvilAttr(obj.__dict__) + + def test_promoted_global_refcount_eliminated(self): + result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + import _testinternalcapi + import opcode + import _opcode + + def get_first_executor(func): + code = func.__code__ + co_code = code.co_code + for i in range(0, len(co_code), 2): + try: + return _opcode.get_executor(code, i) + except ValueError: + pass + return None + + def get_opnames(ex): + return {item[0] for item in ex} + + + def testfunc(n): + y = [] + for i in range(n): + x = tuple(y) + return x testfunc(_testinternalcapi.TIER2_THRESHOLD) + ex = get_first_executor(testfunc) assert ex is not None - """)) + uops = get_opnames(ex) + assert "_LOAD_GLOBAL_BUILTIN" not in uops + assert "_LOAD_CONST_INLINE_BORROW" in uops + assert "_POP_TOP_NOP" in uops + pop_top_count = len([opname for opname in ex if opname == "_POP_TOP" ]) + assert pop_top_count <= 2 + """), PYTHON_JIT="1") + self.assertEqual(result[0].rc, 0, result) + + def test_constant_fold_tuple(self): + def testfunc(n): + for _ in range(n): + t = (1,) + p = len(t) + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertNotIn("_CALL_LEN", uops) + + def test_binary_subscr_list_int(self): + def testfunc(n): + l = [1] + x = 0 + for _ in range(n): + y = l[0] + x += y + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_BINARY_OP_SUBSCR_LIST_INT", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + self.assertLessEqual(count_ops(ex, "_POP_TOP_INT"), 1) + self.assertIn("_POP_TOP_NOP", uops) + + def test_binary_subscr_tuple_int(self): + def testfunc(n): + t = (1,) + x = 0 + for _ in range(n): + y = t[0] + x += y + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_BINARY_OP_SUBSCR_TUPLE_INT", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 3) + self.assertLessEqual(count_ops(ex, "_POP_TOP_INT"), 1) + self.assertIn("_POP_TOP_NOP", uops) + + def test_binary_subscr_frozendict_lowering(self): + def testfunc(n): + x = 0 + for _ in range(n): + x += FROZEN_DICT_CONST['x'] + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 2) + self.assertNotIn("_BINARY_OP_SUBSCR_DICT", uops) + + def test_binary_subscr_frozendict_const_fold(self): + def testfunc(n): + x = 0 + for _ in range(n): + if FROZEN_DICT_CONST['x'] == 1: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 3) + # lookup result is folded to constant 1, so comparison is optimized away + self.assertNotIn("_COMPARE_OP_INT", uops) + + def test_contains_op_frozenset_const_fold(self): + def testfunc(n): + x = 0 + for _ in range(n): + if 1 in FROZEN_SET_CONST: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 3) + self.assertNotIn("_CONTAINS_OP_SET", uops) + + def test_contains_op_frozendict_const_fold(self): + def testfunc(n): + x = 0 + for _ in range(n): + if 'x' in FROZEN_DICT_CONST: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 3) + self.assertNotIn("_CONTAINS_OP_DICT", uops) + + def test_not_contains_op_frozendict_const_fold(self): + def testfunc(n): + x = 0 + for _ in range(n): + if 'z' not in FROZEN_DICT_CONST: + x += 1 + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 3) + self.assertNotIn("_CONTAINS_OP_DICT", uops) + + def test_binary_subscr_list_slice(self): + def testfunc(n): + x = 0 + l = [1, 2, 3] + for _ in range(n): + x += l[0:1][0] + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + uops = get_opnames(ex) + + self.assertIn("_BINARY_OP_SUBSCR_LIST_SLICE", uops) + self.assertNotIn("_GUARD_TOS_LIST", uops) + self.assertEqual(count_ops(ex, "_POP_TOP"), 2) + self.assertEqual(count_ops(ex, "_POP_TOP_NOP"), 4) + + def test_is_op(self): + def test_is_false(n): + a = object() + b = object() + for _ in range(n): + res = a is b + return res + + res, ex = self._run_with_optimizer(test_is_false, TIER2_THRESHOLD) + self.assertEqual(res, False) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_IS_OP", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + + def test_is_true(n): + a = object() + for _ in range(n): + res = a is a + return res + + res, ex = self._run_with_optimizer(test_is_true, TIER2_THRESHOLD) + self.assertEqual(res, True) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_IS_OP", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + + def test_is_not(n): + a = object() + b = object() + for _ in range(n): + res = a is not b + return res + + res, ex = self._run_with_optimizer(test_is_not, TIER2_THRESHOLD) + self.assertEqual(res, True) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_IS_OP", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + + def test_is_none(n): + a = None + for _ in range(n): + res = a is None + return res + + res, ex = self._run_with_optimizer(test_is_none, TIER2_THRESHOLD) + self.assertEqual(res, True) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_IS_OP", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_is_true_narrows_to_constant(self): + def f(n): + def return_true(): + return True + + hits = 0 + v = return_true() + for i in range(n): + if v is True: + hits += v + 1 + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD * 2) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + # v + 1 should be constant folded + self.assertNotIn("_BINARY_OP", uops) + + def test_is_none_narrows_to_constant(self): + def testfunc(n): + value = None + hits = 0 + for _ in range(n): + if value is None: + hits += 1 + return hits + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertNotIn("_IS_NONE", uops) + self.assertIn("_GUARD_IS_NONE_POP", uops) + self.assertIn("_POP_TOP_NOP", uops) + + def test_is_false_narrows_to_constant(self): + def f(n): + def return_false(): + return False + + hits = 0 + v = return_false() + for i in range(n): + if v is False: + hits += v + 1 + return hits + + res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + # v + 1 should be constant folded + self.assertNotIn("_BINARY_OP", uops) + + def test_for_iter_gen_frame(self): + def f(n): + for i in range(n): + # Should be optimized to POP_TOP_NOP + yield i + i + def testfunc(n): + for _ in f(n): + pass - def test_pop_top_specialize_none(self): + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD*2) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_FOR_ITER_GEN_FRAME", uops) + # _POP_TOP_NOP is a sign the optimizer ran and didn't hit bottom. + self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 1) + + def test_send_gen_frame(self): + + def gen(n): + for i in range(n): + yield i + i + def send_gen(n): + yield from gen(n) def testfunc(n): - for _ in range(n): - global_identity(None) + for _ in send_gen(n): + pass - testfunc(TIER2_THRESHOLD) + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + # Ensure SEND is specialized to SEND_GEN + send_gen(10) - ex = get_first_executor(testfunc) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD*2) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_POP_TOP_NOP", uops) + self.assertIn("_FOR_ITER_GEN_FRAME", uops) + self.assertIn("_SEND_GEN_FRAME", uops) + # _POP_TOP_NOP is a sign the optimizer ran and didn't hit bottom. + self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 1) - def test_pop_top_specialize_int(self): + def test_send_virtual(self): + + def send_list(n): + yield from list(range(n)) def testfunc(n): + for _ in send_list(n): + pass + + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + # Ensure SEND is specialized to SEND_VIRTUAL + send_list(10) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD*2) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + + self.assertIn("_FOR_ITER_GEN_FRAME", uops) + self.assertIn("_SEND_VIRTUAL_TIER_TWO", uops) + + def test_binary_op_subscr_init_frame(self): + class B: + def __getitem__(self, other): + return other + 1 + def testfunc(*args): + n, b = args[0] for _ in range(n): - global_identity(100000) + y = b[2] - testfunc(TIER2_THRESHOLD) + res, ex = self._run_with_optimizer(testfunc, (TIER2_THRESHOLD, B())) + self.assertIsNotNone(ex) + uops = get_opnames(ex) - ex = get_first_executor(testfunc) + self.assertIn("_BINARY_OP_SUBSCR_INIT_CALL", uops) + # _POP_TOP_NOP is a sign the optimizer ran and didn't hit contradiction. + self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 1) + + def test_load_attr_property_frame(self): + class B: + @property + def prop(self): + return 3 + def testfunc(*args): + n, b = args[0] + for _ in range(n): + y = b.prop + b.prop + + testfunc((3, B())) + res, ex = self._run_with_optimizer(testfunc, (TIER2_THRESHOLD, B())) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_POP_TOP_INT", uops) + self.assertIn("_LOAD_ATTR_PROPERTY_FRAME", uops) + # This is a sign the optimizer ran and didn't hit contradiction. + self.assertIn("_LOAD_CONST_INLINE_BORROW", uops) + + def test_load_attr_getattribute_frame(self): + class B: + def __getattribute__(self, name): + return len(name) - def test_pop_top_specialize_float(self): def testfunc(n): + b = B() + y = 0 for _ in range(n): - global_identity(1e6) + y += b.x + b.y + return y - testfunc(TIER2_THRESHOLD) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + self.assertEqual(res, 2 * TIER2_THRESHOLD) + uops = get_opnames(ex) + self.assertIn("_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME", uops) + self.assertNotIn("_LOAD_GLOBAL_BUILTINS", uops) + + def test_load_attr_property_frame_invalidates_on_code_change(self): + class C: + @property + def val(self): + return int(1) + + fget = C.val.fget + + def testfunc(*args): + n, c = args[0] + total = 0 + for _ in range(n): + total += c.val + return total + + testfunc((3, C())) + res, ex = self._run_with_optimizer(testfunc, (TIER2_THRESHOLD, C())) + self.assertEqual(res, TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_LOAD_ATTR_PROPERTY_FRAME", uops) + # Check the optimizer traced through the property call. + self.assertNotIn("_LOAD_GLOBAL_BUILTINS", uops) + self.assertIn("_CALL_BUILTIN_CLASS", uops) + fget.__code__ = (lambda self: 2).__code__ + _testinternalcapi.clear_executor_deletion_list() ex = get_first_executor(testfunc) + self.assertIsNone(ex) + res = testfunc((TIER2_THRESHOLD, C())) + self.assertEqual(res, TIER2_THRESHOLD * 2) + + def test_unary_negative(self): + def testfunc(n): + a = 3 + for _ in range(n): + res = -a + return res + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, -3) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_POP_TOP_FLOAT", uops) + self.assertIn("_UNARY_NEGATIVE", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_unary_invert(self): + def testfunc(n): + a = 3 + for _ in range(n): + res = ~a + return res + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, -4) + self.assertIsNotNone(ex) + uops = get_opnames(ex) - def test_unary_negative_long_float_type(self): + self.assertIn("_UNARY_INVERT", uops) + self.assertIn("_POP_TOP_NOP", uops) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_make_function(self): def testfunc(n): + x = 0 for _ in range(n): - a = 9397 - f = 9397.0 - x = -a + -a - y = -f + -f + func = lambda: 1 + x += func() + return x - testfunc(TIER2_THRESHOLD) + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + uops = get_opnames(ex) - ex = get_first_executor(testfunc) + self.assertIn("_MAKE_FUNCTION", uops) + self.assertEqual(uops.count("_POP_TOP_NOP"), 2) + + def test_iter_check_list(self): + def testfunc(n): + x = 0 + for _ in range(n): + l = [1] + for num in l: # unguarded + x += num + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + uops = get_opnames(ex) + + self.assertIn("_BUILD_LIST", uops) + self.assertNotIn("_ITER_CHECK_LIST", uops) + + def test_match_class(self): + def testfunc(n): + class A: + val = 1 + x = A() + ret = 0 + for _ in range(n): + match x: + case A(): + ret += x.val + return ret + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, TIER2_THRESHOLD) + uops = get_opnames(ex) + + self.assertIn("_MATCH_CLASS", uops) + self.assertEqual(count_ops(ex, "_POP_TOP_NOP"), 4) + + def test_dict_update(self): + def testfunc(n): + d = {1: 2, 3: 4} + for _ in range(n): + x = {**d} + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, {1: 2, 3: 4}) + uops = get_opnames(ex) + + self.assertIn("_DICT_UPDATE", uops) + self.assertEqual(count_ops(ex, "_POP_TOP_NOP"), 1) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_set_update(self): + def testfunc(n): + s = {1, 2, 3} + for _ in range(n): + x = {*s} + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, {1, 2, 3}) + uops = get_opnames(ex) + + self.assertIn("_SET_UPDATE", uops) + self.assertEqual(count_ops(ex, "_POP_TOP_NOP"), 1) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_dict_merge(self): + def testfunc(n): + d = {"a": 1, "b": 2} + def f(**kwargs): + return kwargs + for _ in range(n): + x = f(**d) + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, {"a": 1, "b": 2}) + uops = get_opnames(ex) + + self.assertIn("_DICT_MERGE", uops) + self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 1) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_list_extend(self): + def testfunc(n): + a = [1, 2, 3] + for _ in range(n): + x = [*a] + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, [1, 2, 3]) + uops = get_opnames(ex) + + self.assertIn("_LIST_EXTEND", uops) + self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 1) + self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2) + + def test_143026(self): + # https://github.com/python/cpython/issues/143026 + + result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + import gc + thresholds = gc.get_threshold() + try: + gc.set_threshold(1) + + def f1(): + for i in range(5000): + globals()[''] = i + + f1() + finally: + gc.set_threshold(*thresholds) + """), PYTHON_JIT="1") + self.assertEqual(result[0].rc, 0, result) + + def test_143092(self): + def f1(): + a = "a" + for i in range(50): + x = a[i % len(a)] + + s = "" + for _ in range(10): + s += "" + + class A: ... + class B: ... + + match s: + case int(): ... + case str(): ... + case dict(): ... + + ( + u0, + *u1, + u2, + u4, + u5, + u6, + u7, + u8, + u9, u10, u11, + u12, u13, u14, u15, u16, u17, u18, u19, u20, u21, u22, u23, u24, u25, u26, u27, u28, u29, + ) = [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None,] + + s = "" + for _ in range(10): + s += "" + s += "" + + for i in range(TIER2_THRESHOLD * 10): + f1() + + def test_143183(self): + # https://github.com/python/cpython/issues/143183 + + result = script_helper.run_python_until_end('-c', textwrap.dedent(f""" + def f1(): + class AsyncIter: + def __init__(self): + self.limit = 0 + self.count = 0 + + def __aiter__(self): + return self + + async def __anext__(self): + if self.count >= self.limit: + ... + self.count += 1j + + class AsyncCtx: + async def async_for_driver(): + try: + for _ in range({TIER2_THRESHOLD}): + try: + async for _ in AsyncIter(): + ... + except TypeError: + ... + except Exception: + ... + + c = async_for_driver() + while True: + try: + c.send(None) + except StopIteration: + break + + for _ in range({TIER2_THRESHOLD // 40}): + f1() + """), PYTHON_JIT="1") + self.assertEqual(result[0].rc, 0, result) + + def test_143358(self): + # https://github.com/python/cpython/issues/143358 + + result = script_helper.run_python_until_end('-c', textwrap.dedent(f""" + def f1(): + + class EvilIterator: + + def __init__(self): + self._items = [1, 2] + self._index = 1 + + def __iter__(self): + return self + + def __next__(self): + if not len(self._items) % 13: + self._items.clear() + + for i_loop_9279 in range(10): + self._items.extend([1, "", None]) + + if not len(self._items) % 11: + return 'unexpected_type_from_iterator' + + if self._index >= len(self._items): + raise StopIteration + + item = self._items[self._index] + self._index += 1 + return item + + evil_iter = EvilIterator() + + large_num = 2**31 + for _ in range(400): + try: + _ = [x + y for x in evil_iter for y in evil_iter if evil_iter._items.append(x) or large_num] + except TypeError: + pass + + f1() + """), PYTHON_JIT="1", PYTHON_JIT_STRESS="1") + self.assertEqual(result[0].rc, 0, result) + + def test_149335_trace_buffer_guard(self): + # https://github.com/python/cpython/issues/149335 + + result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + import sys + + def f1(): + for i_3178 in 0, 2, 10: + mv162 = 162 + + mv3 = mv1 = mv_165 = mv16 = \ + mv167 = mv168 = \ + mv169 = \ + mv_1403_170 = \ + 169 + + mv_1403_170 + + mv_172 = mv_3 = mv_4 = mv175 = mv176 = mv17 = mv178 = mv179 = mv0 = mv1 = mv182 = ( + mv3 + ) = mv4 = mv185 = mv186 = mv187 = mv18 = mv189 = mv0 = mv1 = mv192 = mv3 = mv4 = ( + mv195 + ) = mv196 = mv197 = mv_198 = mv19 = mv0 = mv1 = mv2 = mv3 = mv4 = mv05 = mv06 = ( + mv07 + ) = mv08 = mv09 = mv0 = mv1 = mv2 = mv3 = mv4 = mv15 = mv16 = mv17 = mv18 = mv19 = ( + mv0 + ) = mv1 = mv_2 = mv3 = mv4 = mv_25 = mv_26 = mv_27 = mv_28 = mv_29 = mv0 = mv1 = ( + mv2 + ) = mv_1403 = mv4 = mv35 = mv36 = mv37 = mv38 = mv39 = mv0 = -sys.maxsize / 3 + + mv1 = mv_12 = mv3 = mv_14 = mv45 = sys.float_info.epsilon + mv46 = sys.float_info.epsilon + + for i in range(15000): + f1() + """), PYTHON_JIT="1") + self.assertEqual(result[0].rc, 0, result) + + def test_144068_daemon_thread_jit_cleanup(self): + result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + import threading + import time + + def hot_loop(): + end = time.time() + 5.0 + while time.time() < end: + pass + + # Create a daemon thread that will be abandoned at shutdown + t = threading.Thread(target=hot_loop, daemon=True) + t.start() + + time.sleep(0.1) + """), PYTHON_JIT="1", ASAN_OPTIONS="detect_leaks=1") + self.assertEqual(result[0].rc, 0, result) + stderr = result[0].err.decode('utf-8', errors='replace') + self.assertNotIn('LeakSanitizer', stderr, + f"Memory leak detected by ASan:\n{stderr}") + self.assertNotIn('_PyJit_TryInitializeTracing', stderr, + f"JIT tracer memory leak detected:\n{stderr}") + + def test_cold_exit_on_init_cleanup_frame(self): + + result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + class A: + __slots__ = ('x', 'y', 'z', 'w') + def __init__(self): + self.x = self.y = -1 + self.z = self.w = None + + class B(A): + __slots__ = ('a', 'b', 'c', 'd', 'e') + def __init__(self): + super().__init__() + self.a = self.b = None + self.c = "" + self.d = self.e = False + + class C(B): + __slots__ = ('name', 'flag') + def __init__(self, name): + super().__init__() + self.name = name + self.flag = False + + funcs = [] + for n in range(20, 80): + lines = [f"def f{n}(names, info):"] + for j in range(n): + lines.append(f" v{j} = names[{j % 3}]") + if j % 3 == 0: + lines.append(f" if v{j} in info:") + lines.append(f" v{j} = info[v{j}]") + elif j % 5 == 0: + lines.append(f" v{j} = len(v{j}) if isinstance(v{j}, str) else 0") + lines.append(" return C(names[0])") + ns = {'C': C} + exec("\\n".join(lines), ns) + funcs.append(ns[f"f{n}"]) + + names = ['alpha', 'beta', 'gamma'] + info = {'alpha': 'x', 'beta': 'y', 'gamma': 'z'} + + for f in funcs: + for _ in range(10): + f(names, info) + """), PYTHON_JIT="1", PYTHON_JIT_STRESS="1", + PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE="1") + self.assertEqual(result[0].rc, 0, result) + + def test_for_iter_gen_cleared_frame_does_not_crash(self): + # See: https://github.com/python/cpython/issues/145197 + result = script_helper.run_python_until_end('-c', textwrap.dedent(""" + def g(): + yield 1 + yield 2 + + for _ in range(4002): + for _ in g(): + pass + + for i in range(4002): + it = g() + if (i & 7) == 0: + next(it) + it.close() + for _ in it: + pass + """), + PYTHON_JIT="1", PYTHON_JIT_STRESS="1") + self.assertEqual(result[0].rc, 0, result) + + def test_call_kw(self): + def func(a): + return int(a) * 42 + + def testfunc(n): + x = 0 + for _ in range(n): + x += func(a=1) + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 42 * TIER2_THRESHOLD) self.assertIsNotNone(ex) uops = get_opnames(ex) + self.assertIn("_PUSH_FRAME", uops) + self.assertIn("_CHECK_FUNCTION_VERSION_KW", uops) + # Check the optimizer has optmized through the function call + # by promoting global `int` to a constant. + self.assertNotIn("_LOAD_GLOBAL_BUILTINS", uops) + self.assertIn("_CALL_BUILTIN_CLASS", uops) - self.assertNotIn("_GUARD_TOS_INT", uops) - self.assertNotIn("_GUARD_NOS_INT", uops) - self.assertNotIn("_GUARD_TOS_FLOAT", uops) - self.assertNotIn("_GUARD_NOS_FLOAT", uops) + def test_call_kw_bound_method(self): + class C: + def method(self, a, b): + return int(a) + int(b) - def test_binary_op_constant_evaluate(self): def testfunc(n): + obj = C() + x = 0 + meth = obj.method for _ in range(n): - 2 ** 65 + x += meth(a=1, b=2) + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 3 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertIn("_PUSH_FRAME", uops) + self.assertIn("_CHECK_METHOD_VERSION_KW", uops) + # Check the optimizer has optmized through the function call + # by promoting global `int` to a constant. + self.assertNotIn("_LOAD_GLOBAL_BUILTINS", uops) + self.assertIn("_CALL_BUILTIN_CLASS", uops) + + def test_func_version_guarded_on_change(self): + def testfunc(n): + for i in range(n): + # Only works on functions promoted to constants + global_identity_code_will_be_modified(i) testfunc(TIER2_THRESHOLD) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) uops = get_opnames(ex) + self.assertIn("_PUSH_FRAME", uops) + self.assertIn("_CHECK_FUNCTION_VERSION", uops) - # For now... until we constant propagate it away. - self.assertIn("_BINARY_OP", uops) + global_identity_code_will_be_modified.__code__ = (lambda a: 0xdeadead).__code__ + _testinternalcapi.clear_executor_deletion_list() + ex = get_first_executor(testfunc) + self.assertIsNone(ex) + # JItted code should've deopted. + self.assertEqual(global_identity_code_will_be_modified(None), 0xdeadead) + def test_call_super(self): + class A: + def method1(self): + return 42 + + def method2(self): + return 21 + + class B(A): + def method1(self): + return super().method1() + + def method2(self): + return super(B, self).method2() + + b = B() + + def testfunc(n): + x = 0 + for _ in range(n): + x += b.method1() + x += b.method2() + return x + + res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD) + self.assertEqual(res, 63 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_LOAD_SUPER_ATTR_METHOD", uops) + self.assertEqual(uops.count("_GUARD_NOS_TYPE_VERSION"), 2) + self.assertTrue(ex.is_valid()) + # this should change the type version of A, which should invalidate the executor + A.method1 = lambda self: 1 + self.assertFalse(ex.is_valid()) + # re-running should create a new executor + res, ex = self._run_with_optimizer(testfunc, 4 * TIER2_THRESHOLD) + self.assertEqual(res, 4 * 22 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_LOAD_SUPER_ATTR_METHOD", uops) + self.assertEqual(uops.count("_GUARD_NOS_TYPE_VERSION"), 2) + + def test_settrace_then_polymorphic_call_does_not_crash(self): + script_helper.assert_python_ok("-c", textwrap.dedent(""" + import sys + sys.settrace(lambda *_: None) + sys.settrace(None) + + class C: + def __init__(self, x): + pass + + for i in 0, 1, 0, 1: + C(0) if i else str(0) + """)) + + def test_load_special_type_guard_deopt(self): + script_helper.assert_python_ok("-s", "-c", textwrap.dedent(f""" + def f1(): + class Context: + def __enter__(self): ... + def __exit__(self, e, v, t): ... + + with Context(): + pass + + for _ in range({TIER2_THRESHOLD + 5}): + f1() + """), PYTHON_JIT="1") def global_identity(x): return x +def global_identity_code_will_be_modified(x): + return x + class TestObject: def test(self, *args, **kwargs): return args[0] @@ -2476,5 +6205,9 @@ def test(self, *args, **kwargs): test_object = TestObject() test_bound_method = TestObject.test.__get__(test_object) +class MyGlobalPoint: + def __init__(self, x, y): + return None + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_set.py b/Lib/test/test_capi/test_set.py index 62d90a3f94326d..a41c2fcaf12270 100644 --- a/Lib/test/test_capi/test_set.py +++ b/Lib/test/test_capi/test_set.py @@ -166,6 +166,12 @@ def test_add(self): # CRASHES: add(instance, NULL) # CRASHES: add(NULL, NULL) + def test_add_frozenset(self): + add = _testlimitedcapi.set_add + frozen_set = frozenset() + # test adding an element to a non-uniquely referenced frozenset throws an exception + self.assertRaises(SystemError, add, frozen_set, 1) + def test_discard(self): discard = _testlimitedcapi.set_discard for cls in (set, set_subclass): diff --git a/Lib/test/test_capi/test_slots.py b/Lib/test/test_capi/test_slots.py new file mode 100644 index 00000000000000..c78b118712b11d --- /dev/null +++ b/Lib/test/test_capi/test_slots.py @@ -0,0 +1,314 @@ +from test.support import import_helper, subTests +import contextlib +import unittest +import types +import sys + +_testlimitedcapi = import_helper.import_module('_testlimitedcapi') +_testcapi = import_helper.import_module('_testcapi') + +class FakeSpec: + name = 'module' + + +# See Modules/_testlimitedcapi/slots.c for the definitions. +# This module is full of "magic constants" which simply need to match +# between the C and Python part of the tests. + +class TypeSlotsTests(unittest.TestCase): + def test_basic_type_slots(self): + cls = _testlimitedcapi.type_from_slots("basic") + self.assertEqual(cls.__name__, "MyType") + + # Py_TPFLAGS_IMMUTABLETYPE is *not* set + cls.attr = 123 + + # Py_TPFLAGS_BASETYPE is *not* set + with self.assertRaises(TypeError): + class Sub(cls): + pass + + def test_mod_slot_in_type(self): + with self.assertRaisesRegex(SystemError, "invalid.* 100 .*Py_mod_name"): + _testlimitedcapi.type_from_slots("foreign_slot") + + def test_size_slots(self): + cls = _testlimitedcapi.type_from_slots("basicsize") + self.assertGreaterEqual(cls.__basicsize__, 256) + + cls = _testlimitedcapi.type_from_slots("extra_basicsize") + self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256) + + cls = _testlimitedcapi.type_from_slots("itemsize") + self.assertGreaterEqual(cls.__itemsize__, 16) + + def test_flag_slots(self): + cls = _testlimitedcapi.type_from_slots("flags") + with self.assertRaises(TypeError): + # Py_TPFLAGS_IMMUTABLETYPE is set + cls.attr = 123 + class Sub(cls): + # Py_TPFLAGS_BASETYPE is set + pass + + def test_func_slots(self): + cls = _testlimitedcapi.type_from_slots("matmul_123") + self.assertEqual(cls() @ None, 123) + + def test_optional_end(self): + with self.assertRaisesRegex(SystemError, "invalid flags.*Py_slot_end"): + cls = _testlimitedcapi.type_from_slots("optional_end") + + def test_invalid(self): + with self.assertRaisesRegex(SystemError, "Py_slot_invalid"): + cls = _testlimitedcapi.type_from_slots("invalid") + with self.assertRaisesRegex(SystemError, f"slot ID {0xfbad}"): + cls = _testlimitedcapi.type_from_slots("invalid_fbad") + + cls = _testlimitedcapi.type_from_slots("optional_invalid") + self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256) + + cls = _testlimitedcapi.type_from_slots("optional_invalid_fbad") + self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256) + + @subTests("case_name", ["old_slot_numbers", "new_slot_numbers"]) + def test_compat_slot_numbers(self, case_name): + cls = _testlimitedcapi.type_from_slots(case_name) + obj = cls() + + # Py_bf_getbuffer (1), Py_bf_releasebuffer (2) + self.assertEqual(obj.buf_counter, 0) + mem = memoryview(obj) + self.assertEqual(bytes(mem), b"buf\0") + self.assertEqual(obj.buf_counter, 1) + mem.release() + self.assertEqual(obj.buf_counter, 0) + + # Py_mp_ass_subscript (3) + with self.assertRaises(KeyError): + obj["key"] = "value" + + # Py_mp_length (4) + self.assertEqual(len(obj), 456) + + def test_nonstatic_tp_members(self): + with self.assertRaisesRegex(SystemError, "Py_tp_members.*STATIC"): + _testlimitedcapi.type_from_slots("nonstatic_tp_members") + + def test_intptr_flags(self): + cls = _testlimitedcapi.type_from_slots("intptr_flags_macro") + with self.assertRaises(TypeError): + # Py_TPFLAGS_IMMUTABLETYPE is set + cls.attr = 123 + + cls = _testlimitedcapi.type_from_slots("intptr_flags_struct") + with self.assertRaises(TypeError): + # Py_TPFLAGS_IMMUTABLETYPE is set + cls.attr = 123 + + cls = _testlimitedcapi.type_from_slots("intptr_static") + cls.attribute = 123 + + def test_nested(self): + cls = _testlimitedcapi.type_from_slots("nested") + self.assertEqual(cls() + 1, 123) + self.assertEqual(cls() - 1, 234) + + cls = _testlimitedcapi.type_from_slots("nested_max") + self.assertEqual(cls() + 1, 123) + self.assertEqual(cls() - 1, 234) + self.assertEqual(cls() * 1, 345) + self.assertEqual(cls() / 1, 456) + self.assertEqual(cls() % 1, 567) + + with self.assertRaisesRegex(SystemError, "too many levels"): + _testlimitedcapi.type_from_slots("nested_over_limit") + + cls = _testlimitedcapi.type_from_slots("nested_old") + self.assertEqual(cls() + 1, 123) + self.assertEqual(cls() - 1, 234) + + cls = _testlimitedcapi.type_from_slots("nested_old_max") + self.assertEqual(cls() + 1, 123) + self.assertEqual(cls() - 1, 234) + self.assertEqual(cls() * 1, 345) + self.assertEqual(cls() / 1, 456) + self.assertEqual(cls() % 1, 567) + + with self.assertRaisesRegex(SystemError, "too many levels"): + _testlimitedcapi.type_from_slots("nested_old_over_limit") + + cls = _testlimitedcapi.type_from_slots("nested_pingpong") + self.assertEqual(cls() + 1, 123) + self.assertEqual(cls() - 1, 234) + self.assertEqual(cls() * 1, 345) + self.assertEqual(cls() / 1, 456) + self.assertEqual(cls() % 1, 567) + + # Slot names aren't exposed to Python yet; see Include/slots_generated.h + # for the definitions. + + @subTests("slot_number", [ + *range(1, 83), # Original slots + *range(88, 92), # New compat slot values + *range(95, 99), # Slots for PyType_Spec fields + *range(107, 109), # Slots for PyType_FromMetaclass args + ]) + def test_null_slot_handling(self, slot_number): + if slot_number == 56: + # Py_tp_doc + return + elif slot_number == 72 or slot_number >= 95: + # Py_tp_members; all new slots + ctx = self.assertRaisesRegex( + SystemError, "NULL not allowed|must be positive") + ctx_old = ctx + else: + ctx = self.assertWarnsRegex(DeprecationWarning, "NULL") + ctx_old = contextlib.nullcontext() + with ctx: + _testlimitedcapi.type_from_null_slot(slot_number) + if slot_number < 95: + with ctx_old: + _testlimitedcapi.type_from_null_spec_slot(slot_number) + + def test_repeat_warning(self): + with self.assertWarnsRegex(DeprecationWarning, "multiple"): + cls = _testlimitedcapi.type_from_slots("repeat_add") + self.assertEqual(cls() + 1, 456) + + def test_repeat_error(self): + with self.assertRaisesRegex(SystemError, "multiple"): + cls = _testlimitedcapi.type_from_slots("repeat_module") + +class ModuleSlotsTests(unittest.TestCase): + def test_basic_module_slots(self): + mod = _testlimitedcapi.module_from_slots("basic", FakeSpec()) + self.assertIsInstance(mod, types.ModuleType) + + def test_type_slot_in_module(self): + with self.assertRaisesRegex(SystemError, "invalid.* 95 .*Py_tp_name"): + _testlimitedcapi.module_from_slots("foreign_slot", FakeSpec()) + + def test_size_slots(self): + mod = _testlimitedcapi.module_from_slots("state_size", FakeSpec()) + self.assertEqual(mod.state_size, 42) + + def test_flag_slots(self): + mod = _testlimitedcapi.module_from_slots("multi_interp", FakeSpec()) + + def test_exec_slot(self): + mod = _testlimitedcapi.module_from_slots("exec", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + + def test_optional_end(self): + with self.assertRaisesRegex(SystemError, "invalid flags.*Py_slot_end"): + _testlimitedcapi.module_from_slots("optional_end", FakeSpec()) + + def test_invalid(self): + with self.assertRaisesRegex(SystemError, "Py_slot_invalid"): + _testlimitedcapi.module_from_slots("invalid", FakeSpec()) + with self.assertRaisesRegex(SystemError, f"slot ID {0xfbad}"): + _testlimitedcapi.module_from_slots("invalid_fbad", FakeSpec()) + + mod = _testlimitedcapi.module_from_slots("optional_invalid", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + + mod = _testlimitedcapi.module_from_slots("optional_invalid_fbad", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + + @subTests("case_name", ["old_slot_numbers", "new_slot_numbers"]) + def test_compat_slot_numbers(self, case_name): + mod = _testlimitedcapi.module_from_slots(case_name, FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + + @subTests("case_name", ["old_slot_number_create", "new_slot_number_create"]) + def test_compat_slot_number_create(self, case_name): + spec = FakeSpec() + mod = _testlimitedcapi.module_from_slots(case_name, spec) + self.assertIs(mod, spec) + + @subTests("slot_number", [4, 87]) + def test_compat_slot_number_gil(self, slot_number): + spec = FakeSpec() + gil_enabled = sys._is_gil_enabled() + mod = _testlimitedcapi.module_from_gil_slot(slot_number, spec) + self.assertEqual(gil_enabled, sys._is_gil_enabled()) + + def test_nonstatic_mod_methods(self): + with self.assertRaisesRegex(SystemError, "Py_mod_methods.*STATIC"): + _testlimitedcapi.module_from_slots("nonstatic_mod_methods", + FakeSpec()) + + def test_intptr_methods(self): + mod = _testlimitedcapi.module_from_slots("intptr_methods", + FakeSpec()) + self.assertEqual(mod.type_from_slots.__name__, "type_from_slots") + + def test_nested(self): + mod = _testlimitedcapi.module_from_slots("nested", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + self.assertEqual(mod.__doc__, "doc") + + mod = _testlimitedcapi.module_from_slots("nested_max", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + self.assertEqual(mod.state_size, 53) + self.assertEqual(mod.__doc__, "doc") + + with self.assertRaisesRegex(SystemError, "too many levels"): + _testlimitedcapi.module_from_slots("nested_over_limit", FakeSpec()) + + mod = _testlimitedcapi.module_from_slots("nested_old", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + self.assertEqual(mod.__doc__, "doc") + + mod = _testlimitedcapi.module_from_slots("nested_old_max", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + self.assertEqual(mod.state_size, 53) + self.assertEqual(mod.__doc__, "doc") + + with self.assertRaisesRegex(SystemError, "too many levels"): + _testlimitedcapi.module_from_slots("nested_old_over_limit", FakeSpec()) + + mod = _testlimitedcapi.module_from_slots("nested_pingpong", FakeSpec()) + self.assertEqual(mod.exec_done, "yes") + self.assertEqual(mod.state_size, 53) + self.assertEqual(mod.__doc__, "doc") + + def test_nested_nonstatic_from_def(self): + with self.assertRaisesRegex(SystemError, "must be static"): + _testcapi.module_from_def_nonstatic_nested(FakeSpec()) + + # Slot names aren't exposed to Python yet; see Include/slots_generated.h + # for the definitions. + + @subTests("slot_number", [ + *range(1, 5), # Old compat slot values + *range(84, 88), # New compat slot values + *range(100, 107), # Slots for PyModuleDef fields + *range(109, 111), # Slots new in 3.15 + ]) + def test_null_slot_handling(self, slot_number): + if slot_number in {3, 86, 4, 87, 102}: + # Py_mod_mult.interp., Py_mod_gil, Py_mod_state_size + return + elif slot_number in {2, 85} or slot_number > 85: + # Py_mod_exec, new slots + ctx = self.assertRaisesRegex(SystemError, "NULL not allowed") + ctx_old = ctx + else: + ctx = self.assertWarnsRegex(DeprecationWarning, "NULL") + ctx_old = contextlib.nullcontext() + with ctx: + _testlimitedcapi.module_from_null_slot(slot_number, FakeSpec()) + with ctx_old: + _testcapi.module_from_null_def_slot(slot_number, + FakeSpec()) + + def test_repeat_error(self): + with self.assertRaisesRegex(SystemError, "multiple"): + _testlimitedcapi.module_from_slots("repeat_create", FakeSpec()) + with self.assertRaisesRegex(SystemError, "multiple"): + _testlimitedcapi.module_from_slots("repeat_exec", FakeSpec()) + with self.assertRaisesRegex(SystemError, "multiple"): + _testlimitedcapi.module_from_slots("repeat_gil", FakeSpec()) diff --git a/Lib/test/test_capi/test_tuple.py b/Lib/test/test_capi/test_tuple.py index 7c07bc64e247c5..d497ba30b001ac 100644 --- a/Lib/test/test_capi/test_tuple.py +++ b/Lib/test/test_capi/test_tuple.py @@ -1,9 +1,11 @@ import unittest import gc +from sys import getrefcount from test.support import import_helper _testcapi = import_helper.import_module('_testcapi') _testlimitedcapi = import_helper.import_module('_testlimitedcapi') +_testinternalcapi = import_helper.import_module('_testinternalcapi') NULL = None PY_SSIZE_T_MIN = _testcapi.PY_SSIZE_T_MIN @@ -14,6 +16,12 @@ class TupleSubclass(tuple): class CAPITest(unittest.TestCase): + def _not_tracked(self, t): + self.assertFalse(gc.is_tracked(t), t) + + def _tracked(self, t): + self.assertTrue(gc.is_tracked(t), t) + def test_check(self): # Test PyTuple_Check() check = _testlimitedcapi.tuple_check @@ -52,16 +60,48 @@ def test_tuple_new(self): self.assertEqual(tup1, ()) self.assertEqual(size(tup1), 0) self.assertIs(type(tup1), tuple) + self._not_tracked(tup1) + tup2 = tuple_new(1) self.assertIs(type(tup2), tuple) self.assertEqual(size(tup2), 1) self.assertIsNot(tup2, tup1) self.assertTrue(checknull(tup2, 0)) + self._tracked(tup2) self.assertRaises(SystemError, tuple_new, -1) self.assertRaises(SystemError, tuple_new, PY_SSIZE_T_MIN) self.assertRaises(MemoryError, tuple_new, PY_SSIZE_T_MAX) + + def test_tuple_fromarray(self): + # Test PyTuple_FromArray() + tuple_fromarray = _testcapi.tuple_fromarray + + tup = tuple([i] for i in range(5)) + copy = tuple_fromarray(tup) + self.assertEqual(copy, tup) + self._tracked(copy) + + tup = tuple(42**i for i in range(5)) + copy = tuple_fromarray(tup) + self.assertEqual(copy, tup) + self._not_tracked(copy) + + tup = () + copy = tuple_fromarray(tup) + self.assertIs(copy, tup) + + copy = tuple_fromarray(NULL, 0) + self.assertIs(copy, ()) + + with self.assertRaises(SystemError): + tuple_fromarray(NULL, -1) + with self.assertRaises(SystemError): + tuple_fromarray(NULL, PY_SSIZE_T_MIN) + with self.assertRaises(MemoryError): + tuple_fromarray(NULL, PY_SSIZE_T_MAX) + def test_tuple_pack(self): # Test PyTuple_Pack() pack = _testlimitedcapi.tuple_pack @@ -70,6 +110,10 @@ def test_tuple_pack(self): self.assertEqual(pack(1, [1]), ([1],)) self.assertEqual(pack(2, [1], [2]), ([1], [2])) + self._tracked(pack(1, [1])) + self._tracked(pack(2, [1], b'abc')) + self._not_tracked(pack(2, 42, b'abc')) + self.assertRaises(SystemError, pack, PY_SSIZE_T_MIN) self.assertRaises(SystemError, pack, -1) self.assertRaises(MemoryError, pack, PY_SSIZE_T_MAX) @@ -77,6 +121,41 @@ def test_tuple_pack(self): # CRASHES pack(1, NULL) # CRASHES pack(2, [1]) + def check_tuple_from_pair(self, from_pair): + self.assertEqual(type(from_pair(1, 2)), tuple) + self.assertEqual(from_pair(1, 145325), (1, 145325)) + self.assertEqual(from_pair(None, None), (None, None)) + self.assertEqual(from_pair(True, False), (True, False)) + + # user class supports gc + class Temp: + pass + temp = Temp() + temp_rc = getrefcount(temp) + self.assertEqual(from_pair(temp, temp), (temp, temp)) + self.assertEqual(getrefcount(temp), temp_rc) + + self._not_tracked(from_pair(1, 2)) + self._not_tracked(from_pair(None, None)) + self._not_tracked(from_pair(True, False)) + self._tracked(from_pair(temp, (1, 2))) + self._tracked(from_pair(temp, 1)) + self._tracked(from_pair([], {})) + + self.assertRaises(TypeError, from_pair, 1, 2, 3) + self.assertRaises(TypeError, from_pair, 1) + self.assertRaises(TypeError, from_pair) + + def test_tuple_from_pair(self): + # Test _PyTuple_FromPair() + from_pair = _testinternalcapi.tuple_from_pair + self.check_tuple_from_pair(from_pair) + + def test_tuple_from_pair_steal(self): + # Test _PyTuple_FromPairSteal() + from_pair = _testinternalcapi.tuple_from_pair_steal + self.check_tuple_from_pair(from_pair) + def test_tuple_size(self): # Test PyTuple_Size() size = _testlimitedcapi.tuple_size @@ -279,6 +358,10 @@ def my_iter(): self.assertEqual(tuple(my_iter()), (TAG, *range(10))) self.assertEqual(tuples, []) + def test_uninitialized_tuple_repr(self): + tup = _testlimitedcapi.tuple_new(3) + self.assertEqual(repr(tup), '(, , )') + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi/test_type.py b/Lib/test/test_capi/test_type.py index 15fb4a93e2ad74..e6a8ef9eed6fc4 100644 --- a/Lib/test/test_capi/test_type.py +++ b/Lib/test/test_capi/test_type.py @@ -195,6 +195,24 @@ class H2(int): pass with self.assertRaises(TypeError): _testcapi.pytype_getmodulebydef(H2) + def test_get_module_by_token(self): + token = _testcapi.pymodule_get_token(_testcapi) + + heaptype = _testcapi.create_type_with_token('_testcapi.H', 0) + mod = _testcapi.pytype_getmodulebytoken(heaptype, token) + self.assertIs(mod, _testcapi) + + class H1(heaptype): pass + mod = _testcapi.pytype_getmodulebytoken(H1, token) + self.assertIs(mod, _testcapi) + + with self.assertRaises(TypeError): + _testcapi.pytype_getmodulebytoken(int, token) + + class H2(int): pass + with self.assertRaises(TypeError): + _testcapi.pytype_getmodulebytoken(H2, token) + def test_freeze(self): # test PyType_Freeze() type_freeze = _testcapi.type_freeze @@ -274,3 +292,10 @@ def test_extension_managed_dict_type(self): obj.__dict__ = {'bar': 3} self.assertEqual(obj.__dict__, {'bar': 3}) self.assertEqual(obj.bar, 3) + + def test_extension_managed_weakref_nogc_type(self): + msg = ("type _testcapi.ManagedWeakrefNoGCType " + "has the Py_TPFLAGS_MANAGED_WEAKREF " + "flag but not Py_TPFLAGS_HAVE_GC flag") + with self.assertRaisesRegex(SystemError, msg): + _testcapi.create_managed_weakref_nogc_type() diff --git a/Lib/test/test_capi/test_unicode.py b/Lib/test/test_capi/test_unicode.py index 6a9c60f3a6d75e..0dcd8a25ad0128 100644 --- a/Lib/test/test_capi/test_unicode.py +++ b/Lib/test/test_capi/test_unicode.py @@ -842,9 +842,7 @@ def test_fromwidechar(self): if SIZEOF_WCHAR_T == 2: self.assertEqual(fromwidechar('a\U0001f600'.encode(encoding), 2), 'a\ud83d') - self.assertRaises(MemoryError, fromwidechar, b'', PY_SSIZE_T_MAX) self.assertRaises(SystemError, fromwidechar, b'\0'*SIZEOF_WCHAR_T, -2) - self.assertRaises(SystemError, fromwidechar, b'\0'*SIZEOF_WCHAR_T, PY_SSIZE_T_MIN) self.assertEqual(fromwidechar(NULL, 0), '') self.assertRaises(SystemError, fromwidechar, NULL, 1) self.assertRaises(SystemError, fromwidechar, NULL, PY_SSIZE_T_MAX) @@ -852,6 +850,10 @@ def test_fromwidechar(self): self.assertRaises(SystemError, fromwidechar, NULL, -2) self.assertRaises(SystemError, fromwidechar, NULL, PY_SSIZE_T_MIN) + # The following tests are skipped since they rely on undefined behavior + #self.assertRaises(MemoryError, fromwidechar, b'', PY_SSIZE_T_MAX) + #self.assertRaises(SystemError, fromwidechar, b'\0'*SIZEOF_WCHAR_T, PY_SSIZE_T_MIN) + @support.cpython_only @unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module') def test_aswidechar(self): @@ -1765,13 +1767,15 @@ def test_basic(self): writer.write_utf8(b'var', -1) # test PyUnicodeWriter_WriteChar() - writer.write_char('=') + writer.write_char(ord('=')) # test PyUnicodeWriter_WriteSubstring() writer.write_substring("[long]", 1, 5) + # CRASHES writer.write_substring(NULL, 0, 0) # test PyUnicodeWriter_WriteStr() writer.write_str(" value ") + # CRASHES writer.write_str(NULL) # test PyUnicodeWriter_WriteRepr() writer.write_repr("repr") @@ -1779,14 +1783,35 @@ def test_basic(self): self.assertEqual(writer.finish(), "var=long value 'repr'") + def test_repr_null(self): + writer = self.create_writer(0) + writer.write_utf8(b'var=', -1) + writer.write_repr(NULL) + self.assertEqual(writer.finish(), + "var=") + + def test_write_char(self): + writer = self.create_writer(0) + writer.write_char(0) + writer.write_char(ord('$')) + writer.write_char(0x20ac) + writer.write_char(0x10_ffff) + self.assertRaises(ValueError, writer.write_char, 0x11_0000) + self.assertRaises(ValueError, writer.write_char, 0xFFFF_FFFF) + self.assertEqual(writer.finish(), + "\0$\u20AC\U0010FFFF") + def test_utf8(self): writer = self.create_writer(0) writer.write_utf8(b"ascii", -1) - writer.write_char('-') + writer.write_char(ord('-')) writer.write_utf8(b"latin1=\xC3\xA9", -1) - writer.write_char('-') + writer.write_char(ord('-')) writer.write_utf8(b"euro=\xE2\x82\xAC", -1) - writer.write_char('.') + writer.write_char(ord('.')) + writer.write_utf8(NULL, 0) + # CRASHES writer.write_utf8(NULL, 1) + # CRASHES writer.write_utf8(NULL, -1) self.assertEqual(writer.finish(), "ascii-latin1=\xE9-euro=\u20AC.") @@ -1794,6 +1819,9 @@ def test_ascii(self): writer = self.create_writer(0) writer.write_ascii(b"Hello ", -1) writer.write_ascii(b"", 0) + writer.write_ascii(NULL, 0) + # CRASHES writer.write_ascii(NULL, 1) + # CRASHES writer.write_ascii(NULL, -1) writer.write_ascii(b"Python! ", 6) self.assertEqual(writer.finish(), "Hello Python") @@ -1810,6 +1838,9 @@ def test_recover_utf8_error(self): # write fails with an invalid string with self.assertRaises(UnicodeDecodeError): writer.write_utf8(b"invalid\xFF", -1) + with self.assertRaises(UnicodeDecodeError): + s = "truncated\u20AC".encode() + writer.write_utf8(s, len(s) - 1) # retry write with a valid string writer.write_utf8(b"valid", -1) @@ -1821,13 +1852,19 @@ def test_decode_utf8(self): # test PyUnicodeWriter_DecodeUTF8Stateful() writer = self.create_writer(0) writer.decodeutf8stateful(b"ign\xFFore", -1, b"ignore") - writer.write_char('-') + writer.write_char(ord('-')) writer.decodeutf8stateful(b"replace\xFF", -1, b"replace") - writer.write_char('-') + writer.write_char(ord('-')) # incomplete trailing UTF-8 sequence writer.decodeutf8stateful(b"incomplete\xC3", -1, b"replace") + writer.decodeutf8stateful(NULL, 0, b"replace") + # CRASHES writer.decodeutf8stateful(NULL, 1, b"replace") + # CRASHES writer.decodeutf8stateful(NULL, -1, b"replace") + with self.assertRaises(UnicodeDecodeError): + writer.decodeutf8stateful(b"default\xFF", -1, NULL) + self.assertEqual(writer.finish(), "ignore-replace\uFFFD-incomplete\uFFFD") @@ -1838,12 +1875,12 @@ def test_decode_utf8_consumed(self): # valid string consumed = writer.decodeutf8stateful(b"text", -1, b"strict", True) self.assertEqual(consumed, 4) - writer.write_char('-') + writer.write_char(ord('-')) # non-ASCII consumed = writer.decodeutf8stateful(b"\xC3\xA9-\xE2\x82\xAC", 6, b"strict", True) self.assertEqual(consumed, 6) - writer.write_char('-') + writer.write_char(ord('-')) # invalid UTF-8 (consumed is 0 on error) with self.assertRaises(UnicodeDecodeError): @@ -1852,54 +1889,92 @@ def test_decode_utf8_consumed(self): # ignore error handler consumed = writer.decodeutf8stateful(b"more\xFF", -1, b"ignore", True) self.assertEqual(consumed, 5) - writer.write_char('-') + writer.write_char(ord('-')) # incomplete trailing UTF-8 sequence consumed = writer.decodeutf8stateful(b"incomplete\xC3", -1, b"ignore", True) self.assertEqual(consumed, 10) + writer.write_char(ord('-')) - self.assertEqual(writer.finish(), "text-\xE9-\u20AC-more-incomplete") + consumed = writer.decodeutf8stateful(NULL, 0, b"replace", True) + self.assertEqual(consumed, 0) + # CRASHES writer.decodeutf8stateful(NULL, 1, b"replace", True) + # CRASHES writer.decodeutf8stateful(NULL, -1, b"replace", True) + consumed = writer.decodeutf8stateful(b"default\xC3", -1, NULL, True) + self.assertEqual(consumed, 7) + + self.assertEqual(writer.finish(), "text-\xE9-\u20AC-more-incomplete-default") def test_widechar(self): + from _testcapi import SIZEOF_WCHAR_T + + if SIZEOF_WCHAR_T == 2: + encoding = 'utf-16le' if sys.byteorder == 'little' else 'utf-16be' + elif SIZEOF_WCHAR_T == 4: + encoding = 'utf-32le' if sys.byteorder == 'little' else 'utf-32be' + writer = self.create_writer(0) - writer.write_widechar("latin1=\xE9") - writer.write_widechar("-") - writer.write_widechar("euro=\u20AC") - writer.write_char("-") - writer.write_widechar("max=\U0010ffff") - writer.write_char('.') + writer.write_widechar("latin1=\xE9".encode(encoding)) + writer.write_char(ord("-")) + writer.write_widechar("euro=\u20AC".encode(encoding)) + writer.write_char(ord("-")) + writer.write_widechar("max=\U0010ffff".encode(encoding)) + writer.write_char(ord("-")) + writer.write_widechar("zeroes=".encode(encoding).ljust(SIZEOF_WCHAR_T * 10, b'\0'), + 10) + writer.write_char(ord('.')) + + if SIZEOF_WCHAR_T == 4: + invalid = (b'\x00\x00\x11\x00' if sys.byteorder == 'little' else + b'\x00\x11\x00\x00') + with self.assertRaises(ValueError): + writer.write_widechar("invalid=".encode(encoding) + invalid) + writer.write_widechar(b'', -5) + writer.write_widechar(NULL, 0) + # CRASHES writer.write_widechar(NULL, 1) + # CRASHES writer.write_widechar(NULL, -1) + self.assertEqual(writer.finish(), - "latin1=\xE9-euro=\u20AC-max=\U0010ffff.") + "latin1=\xE9-euro=\u20AC-max=\U0010ffff-zeroes=\0\0\0.") def test_ucs4(self): + encoding = 'utf-32le' if sys.byteorder == 'little' else 'utf-32be' + writer = self.create_writer(0) - writer.write_ucs4("ascii IGNORED", 5) - writer.write_char("-") - writer.write_ucs4("latin1=\xe9", 8) - writer.write_char("-") - writer.write_ucs4("euro=\u20ac", 6) - writer.write_char("-") - writer.write_ucs4("max=\U0010ffff", 5) - writer.write_char(".") + writer.write_ucs4("ascii IGNORED".encode(encoding), 5) + writer.write_char(ord("-")) + writer.write_ucs4("latin1=\xe9".encode(encoding)) + writer.write_char(ord("-")) + writer.write_ucs4("euro=\u20ac".encode(encoding)) + writer.write_char(ord("-")) + writer.write_ucs4("max=\U0010ffff".encode(encoding)) + writer.write_char(ord(".")) self.assertEqual(writer.finish(), "ascii-latin1=\xE9-euro=\u20AC-max=\U0010ffff.") # Test some special characters writer = self.create_writer(0) # Lone surrogate character - writer.write_ucs4("lone\uDC80", 5) - writer.write_char("-") + writer.write_ucs4("lone\uDC80".encode(encoding, 'surrogatepass')) + writer.write_char(ord("-")) # Surrogate pair - writer.write_ucs4("pair\uDBFF\uDFFF", 5) - writer.write_char("-") - writer.write_ucs4("null[\0]", 7) + writer.write_ucs4("pair\uD83D\uDC0D".encode(encoding, 'surrogatepass')) + writer.write_char(ord("-")) + writer.write_ucs4("null[\0]".encode(encoding), 7) + invalid = (b'\x00\x00\x11\x00' if sys.byteorder == 'little' else + b'\x00\x11\x00\x00') + # CRASHES writer.write_ucs4("invalid".encode(encoding) + invalid) + writer.write_ucs4(NULL, 0) + # CRASHES writer.write_ucs4(NULL, 1) self.assertEqual(writer.finish(), - "lone\udc80-pair\udbff-null[\0]") + "lone\udc80-pair\ud83d\udc0d-null[\x00]") # invalid size writer = self.create_writer(0) with self.assertRaises(ValueError): - writer.write_ucs4("text", -1) + writer.write_ucs4("text".encode(encoding), -1) + self.assertRaises(ValueError, writer.write_ucs4, b'', -1) + self.assertRaises(ValueError, writer.write_ucs4, NULL, -1) def test_substring_empty(self): writer = self.create_writer(0) @@ -1925,7 +2000,7 @@ def test_format(self): from ctypes import c_int writer = self.create_writer(0) self.writer_format(writer, b'%s %i', b'abc', c_int(123)) - writer.write_char('.') + writer.write_char(ord('.')) self.assertEqual(writer.finish(), 'abc 123.') def test_recover_error(self): diff --git a/Lib/test/test_capi/test_watchers.py b/Lib/test/test_capi/test_watchers.py index 8644479d83d5ed..490ae7b23e6279 100644 --- a/Lib/test/test_capi/test_watchers.py +++ b/Lib/test/test_capi/test_watchers.py @@ -176,8 +176,9 @@ def test_watch_unassigned_watcher_id(self): def test_unwatch_non_dict(self): with self.watcher() as wid: - with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"): - self.unwatch(wid, 1) + for wrong_type in (frozendict(), 5, [123], object()): + with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"): + self.unwatch(wid, wrong_type) def test_unwatch_out_of_range_watcher_id(self): d = {} @@ -207,6 +208,7 @@ class TestTypeWatchers(unittest.TestCase): TYPES = 0 # appends modified types to global event list ERROR = 1 # unconditionally sets and signals a RuntimeException WRAP = 2 # appends modified type wrapped in list to global event list + NAME = 3 # appends type name (string) to global event list # duplicating the C constant TYPE_MAX_WATCHERS = 8 @@ -376,6 +378,27 @@ def test_clear_unassigned_watcher_id(self): with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"): self.clear_watcher(1) + def test_watch_type_dealloc(self): + # Use the NAME watcher (kind=3) which records the type's name as a + # string, avoiding any reference to the type object itself during + # deallocation. + with self.watcher(kind=self.NAME) as wid: + class MyTestType: pass + self.watch(wid, MyTestType) + del MyTestType + gc_collect() + events = _testcapi.get_type_modified_events() + self.assertIn("MyTestType", events) + + def test_watch_type_dealloc_error(self): + with self.watcher(kind=self.ERROR) as wid: + class MyTestType2: pass + self.watch(wid, MyTestType2) + with catch_unraisable_exception() as cm: + del MyTestType2 + gc_collect() + self.assertEqual(str(cm.unraisable.exc_value), "boom!") + def test_no_more_ids_available(self): with self.assertRaisesRegex(RuntimeError, r"no more type watcher IDs"): with ExitStack() as stack: @@ -514,6 +537,10 @@ def myfunc(): _testcapi.set_func_kwdefaults_via_capi(myfunc, new_kwdefaults) self.assertIn((_testcapi.PYFUNC_EVENT_MODIFY_KWDEFAULTS, myfunc, new_kwdefaults), events) + new_qualname = "foo.bar" + myfunc.__qualname__ = new_qualname + self.assertIn((_testcapi.PYFUNC_EVENT_MODIFY_QUALNAME, myfunc, new_qualname), events) + # Clear events reference to func events = [] del myfunc diff --git a/Lib/test/test_capi/test_weakref.py b/Lib/test/test_capi/test_weakref.py new file mode 100644 index 00000000000000..7c9e97f571979d --- /dev/null +++ b/Lib/test/test_capi/test_weakref.py @@ -0,0 +1,138 @@ +import weakref +import unittest +from test.support import import_helper + +_testcapi = import_helper.import_module('_testcapi') +_testlimitedcapi = import_helper.import_module('_testlimitedcapi') +NULL = None + +class Object: + pass + +class Ref(weakref.ReferenceType): + pass + + +class CAPIWeakrefTest(unittest.TestCase): + def test_pyweakref_check(self): + # Test PyWeakref_Check() + check = _testlimitedcapi.pyweakref_check + obj = Object() + self.assertEqual(check(obj), 0) + self.assertEqual(check(weakref.ref(obj)), 1) + self.assertEqual(check(Ref(obj)), 1) + self.assertEqual(check(weakref.proxy(obj)), 1) + + # CRASHES check(NULL) + + def test_pyweakref_checkref(self): + # Test PyWeakref_CheckRef() + checkref = _testlimitedcapi.pyweakref_checkref + obj = Object() + self.assertEqual(checkref(obj), 0) + self.assertEqual(checkref(weakref.ref(obj)), 1) + self.assertEqual(checkref(Ref(obj)), 1) + self.assertEqual(checkref(weakref.proxy(obj)), 0) + + # CRASHES checkref(NULL) + + def test_pyweakref_checkrefexact(self): + # Test PyWeakref_CheckRefExact() + checkrefexact = _testlimitedcapi.pyweakref_checkrefexact + obj = Object() + self.assertEqual(checkrefexact(obj), 0) + self.assertEqual(checkrefexact(weakref.ref(obj)), 1) + self.assertEqual(checkrefexact(Ref(obj)), 0) + self.assertEqual(checkrefexact(weakref.proxy(obj)), 0) + + # CRASHES checkrefexact(NULL) + + def test_pyweakref_checkproxy(self): + # Test PyWeakref_CheckProxy() + checkproxy = _testlimitedcapi.pyweakref_checkproxy + obj = Object() + self.assertEqual(checkproxy(obj), 0) + self.assertEqual(checkproxy(weakref.ref(obj)), 0) + self.assertEqual(checkproxy(Ref(obj)), 0) + self.assertEqual(checkproxy(weakref.proxy(obj)), 1) + + # CRASHES checkproxy(NULL) + + def test_pyweakref_getref(self): + # Test PyWeakref_GetRef() + getref = _testcapi.pyweakref_getref + obj = Object() + wr = weakref.ref(obj) + wp = weakref.proxy(obj) + self.assertEqual(getref(wr), (1, obj)) + self.assertEqual(getref(wp), (1, obj)) + del obj + self.assertEqual(getref(wr), 0) + self.assertEqual(getref(wp), 0) + + self.assertRaises(TypeError, getref, 42) + self.assertRaises(SystemError, getref, NULL) + + def test_pyweakref_isdead(self): + # Test PyWeakref_IsDead() + isdead = _testcapi.pyweakref_isdead + obj = Object() + wr = weakref.ref(obj) + wp = weakref.proxy(obj) + self.assertEqual(isdead(wr), 0) + self.assertEqual(isdead(wp), 0) + del obj + self.assertEqual(isdead(wr), 1) + self.assertEqual(isdead(wp), 1) + + self.assertRaises(TypeError, isdead, 42) + self.assertRaises(SystemError, isdead, NULL) + + def test_pyweakref_newref(self): + # Test PyWeakref_NewRef() + newref = _testlimitedcapi.pyweakref_newref + obj = Object() + wr = newref(obj) + self.assertIs(type(wr), weakref.ReferenceType) + # PyWeakref_NewRef() handles None callback as NULL callback + wr = newref(obj, None) + self.assertIs(type(wr), weakref.ReferenceType) + self.assertRaises(TypeError, newref, obj, 42) + log = [] + wr = newref(obj, log.append) + self.assertIs(type(wr), weakref.ReferenceType) + self.assertEqual(log, []) + del obj + self.assertEqual(log, [wr]) + + self.assertRaises(TypeError, newref, []) + # CRASHES newref(NULL) + + def test_pyweakref_newproxy(self): + # Test PyWeakref_NewProxy() + newproxy = _testlimitedcapi.pyweakref_newproxy + obj = Object() + wp = newproxy(obj) + self.assertIs(type(wp), weakref.ProxyType) + # PyWeakref_NewProxy() handles None callback as NULL callback + wp = newproxy(obj, None) + self.assertIs(type(wp), weakref.ProxyType) + self.assertRaises(TypeError, newproxy, obj, 42) + log = [] + wp = newproxy(obj, log.append) + self.assertIs(type(wp), weakref.ProxyType) + self.assertEqual(log, []) + del obj + self.assertEqual(log, [wp]) + + def func(): + pass + wp = newproxy(func) + self.assertIs(type(wp), weakref.CallableProxyType) + + self.assertRaises(TypeError, newproxy, []) + # CRASHES newproxy(NULL) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_cext/__init__.py b/Lib/test/test_cext/__init__.py index fb93c6ccbb637d..4cc5f843dd388d 100644 --- a/Lib/test/test_cext/__init__.py +++ b/Lib/test/test_cext/__init__.py @@ -8,13 +8,14 @@ import shlex import shutil import subprocess +import sysconfig +import sys import unittest from test import support SOURCES = [ os.path.join(os.path.dirname(__file__), 'extension.c'), - os.path.join(os.path.dirname(__file__), 'create_moduledef.c'), ] SETUP = os.path.join(os.path.dirname(__file__), 'setup.py') @@ -39,15 +40,15 @@ def test_build(self): self.check_build('_test_cext') def check_build(self, extension_name, std=None, limited=False, - opaque_pyobject=False): + abi3t=False): venv_dir = 'env' with support.setup_venv_with_pip_setuptools(venv_dir) as python_exe: self._check_build(extension_name, python_exe, std=std, limited=limited, - opaque_pyobject=opaque_pyobject) + abi3t=abi3t) def _check_build(self, extension_name, python_exe, std, limited, - opaque_pyobject): + abi3t): pkg_dir = 'pkg' os.mkdir(pkg_dir) shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP))) @@ -61,8 +62,11 @@ def run_cmd(operation, cmd): env['CPYTHON_TEST_STD'] = std if limited: env['CPYTHON_TEST_LIMITED'] = '1' - if opaque_pyobject: - env['CPYTHON_TEST_OPAQUE_PYOBJECT'] = '1' + if abi3t: + env['CPYTHON_TEST_ABI3T'] = '1' + if support.MS_WINDOWS and sysconfig.is_python_build(): + env['CPYTHON_EXTRA_INCDIRS'] = os.path.split(sysconfig.get_config_h_filename())[0] + env['CPYTHON_EXTRA_LIBDIRS'] = os.path.split(sys.executable)[0] env['CPYTHON_TEST_EXT_NAME'] = extension_name env['TEST_INTERNAL_C_API'] = str(int(self.TEST_INTERNAL_C_API)) if support.verbose: @@ -117,10 +121,9 @@ def test_build_limited_c11(self): def test_build_c11(self): self.check_build('_test_c11_cext', std='c11') - def test_build_opaque_pyobject(self): - # Test with _Py_OPAQUE_PYOBJECT - self.check_build('_test_limited_opaque_cext', limited=True, - opaque_pyobject=True) + def test_build_abi3t(self): + # Test with Py_TARGET_ABI3T + self.check_build('_test_abi3t', abi3t=True) @unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c99") def test_build_c99(self): diff --git a/Lib/test/test_cext/create_moduledef.c b/Lib/test/test_cext/create_moduledef.c deleted file mode 100644 index 249c3163552c03..00000000000000 --- a/Lib/test/test_cext/create_moduledef.c +++ /dev/null @@ -1,29 +0,0 @@ -// Workaround for testing _Py_OPAQUE_PYOBJECT. -// See end of 'extension.c' - - -#undef _Py_OPAQUE_PYOBJECT -#undef Py_LIMITED_API -#include "Python.h" - - -// (repeated definition to avoid creating a header) -extern PyObject *testcext_create_moduledef( - const char *name, const char *doc, - PyMethodDef *methods, PyModuleDef_Slot *slots); - -PyObject *testcext_create_moduledef( - const char *name, const char *doc, - PyMethodDef *methods, PyModuleDef_Slot *slots) { - - static struct PyModuleDef _testcext_module = { - PyModuleDef_HEAD_INIT, - }; - if (!_testcext_module.m_name) { - _testcext_module.m_name = name; - _testcext_module.m_doc = doc; - _testcext_module.m_methods = methods; - _testcext_module.m_slots = slots; - } - return PyModuleDef_Init(&_testcext_module); -} diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c index 73fc67ae59d18f..895eca50f03b98 100644 --- a/Lib/test/test_cext/extension.c +++ b/Lib/test/test_cext/extension.c @@ -11,12 +11,15 @@ #endif #include "Python.h" +#include "datetime.h" #ifdef TEST_INTERNAL_C_API // gh-135906: Check for compiler warnings in the internal C API. - // - Cython uses pycore_frame.h. + // - Cython uses pycore_critical_section.h, pycore_frame.h and + // pycore_template.h. // - greenlet uses pycore_frame.h, pycore_interpframe_structs.h and // pycore_interpframe.h. +# include "internal/pycore_critical_section.h" # include "internal/pycore_frame.h" # include "internal/pycore_gc.h" # include "internal/pycore_interp.h" @@ -24,6 +27,7 @@ # include "internal/pycore_interpframe_structs.h" # include "internal/pycore_object.h" # include "internal/pycore_pystate.h" +# include "internal/pycore_template.h" #endif #ifndef MODULE_NAME @@ -50,35 +54,59 @@ _testcext_add(PyObject *Py_UNUSED(module), PyObject *args) } +static PyObject * +test_datetime(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + // datetime.h is excluded from the limited C API +#ifndef Py_LIMITED_API + PyDateTime_IMPORT; + if (PyErr_Occurred()) { + return NULL; + } +#endif + + Py_RETURN_NONE; +} + + static PyMethodDef _testcext_methods[] = { {"add", _testcext_add, METH_VARARGS, _testcext_add_doc}, + {"test_datetime", test_datetime, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} // sentinel }; static int -_testcext_exec( -#ifdef __STDC_VERSION__ - PyObject *module -#else - PyObject *Py_UNUSED(module) -#endif - ) +_testcext_exec(PyObject *module) { + PyObject *result, *obj; + #ifdef __STDC_VERSION__ if (PyModule_AddIntMacro(module, __STDC_VERSION__) < 0) { return -1; } #endif + result = PyObject_CallMethod(module, "test_datetime", ""); + if (!result) return -1; + Py_DECREF(result); + // test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR() Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int)); assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0); + // Test Py_CLEAR() + obj = NULL; + Py_CLEAR(obj); + + // Test that Py_BEGIN_CRITICAL_SECTION is available + Py_BEGIN_CRITICAL_SECTION(module); + Py_END_CRITICAL_SECTION(); + return 0; } -#define _FUNC_NAME(NAME) PyInit_ ## NAME +#define _FUNC_NAME(NAME) PyModExport_ ## NAME #define FUNC_NAME(NAME) _FUNC_NAME(NAME) // Converting from function pointer to void* has undefined behavior, but @@ -88,58 +116,42 @@ _testcext_exec( _Py_COMP_DIAG_PUSH #if defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpedantic" +#pragma GCC diagnostic ignored "-Wcast-qual" #elif defined(__clang__) #pragma clang diagnostic ignored "-Wpedantic" +#pragma clang diagnostic ignored "-Wcast-qual" #endif -static PyModuleDef_Slot _testcext_slots[] = { - {Py_mod_exec, (void*)_testcext_exec}, - {0, NULL} -}; - -_Py_COMP_DIAG_POP - PyDoc_STRVAR(_testcext_doc, "C test extension."); - -#ifndef _Py_OPAQUE_PYOBJECT - -static struct PyModuleDef _testcext_module = { - PyModuleDef_HEAD_INIT, // m_base - STR(MODULE_NAME), // m_name - _testcext_doc, // m_doc - 0, // m_size - _testcext_methods, // m_methods - _testcext_slots, // m_slots - NULL, // m_traverse - NULL, // m_clear - NULL, // m_free +PyABIInfo_VAR(abi_info); + +static PySlot _testcext_slots[] = { + PySlot_STATIC_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_name, STR(MODULE_NAME)), + PySlot_STATIC_DATA(Py_mod_doc, (void*)(char*)_testcext_doc), + PySlot_FUNC(Py_mod_exec, (void*)_testcext_exec), + PySlot_STATIC_DATA(Py_mod_methods, _testcext_methods), + PySlot_END, }; +_Py_COMP_DIAG_POP -PyMODINIT_FUNC +PyMODEXPORT_FUNC FUNC_NAME(MODULE_NAME)(void) { - return PyModuleDef_Init(&_testcext_module); + return _testcext_slots; } -#else // _Py_OPAQUE_PYOBJECT - -// Opaque PyObject means that PyModuleDef is also opaque and cannot be -// declared statically. See PEP 793. -// So, this part of module creation is split into a separate source file -// which uses non-limited API. - -// (repeated definition to avoid creating a header) -extern PyObject *testcext_create_moduledef( - const char *name, const char *doc, - PyMethodDef *methods, PyModuleDef_Slot *slots); +// Also define the soft-deprecated entrypoint to ensure it isn't called +#define _INITFUNC_NAME(NAME) PyInit_ ## NAME +#define INITFUNC_NAME(NAME) _INITFUNC_NAME(NAME) PyMODINIT_FUNC -FUNC_NAME(MODULE_NAME)(void) +INITFUNC_NAME(MODULE_NAME)(void) { - return testcext_create_moduledef( - STR(MODULE_NAME), _testcext_doc, _testcext_methods, _testcext_slots); + PyErr_SetString( + PyExc_AssertionError, + "PyInit_* function called while a PyModExport_* one is available"); + return NULL; } - -#endif // _Py_OPAQUE_PYOBJECT diff --git a/Lib/test/test_cext/setup.py b/Lib/test/test_cext/setup.py index 4d71e4751f7afd..1eca44bdf823dc 100644 --- a/Lib/test/test_cext/setup.py +++ b/Lib/test/test_cext/setup.py @@ -1,7 +1,6 @@ # gh-91321: Build a basic C test extension to check that the Python C API is # compatible with C and does not emit C compiler warnings. import os -import platform import shlex import sys import sysconfig @@ -18,6 +17,11 @@ # The purpose of test_cext extension is to check that building a C # extension using the Python C API does not emit C compiler warnings. '-Werror', + # Enable extra checks for header files, which: + # - need to be enabled somewhere inside Python headers (rather than + # before including Python.h) + # - should not be checked for user code + '-D_Py_IS_TESTCEXT', ] # C compiler flags for GCC and clang @@ -59,8 +63,10 @@ def main(): std = os.environ.get("CPYTHON_TEST_STD", "") module_name = os.environ["CPYTHON_TEST_EXT_NAME"] limited = bool(os.environ.get("CPYTHON_TEST_LIMITED", "")) - opaque_pyobject = bool(os.environ.get("CPYTHON_TEST_OPAQUE_PYOBJECT", "")) + abi3t = bool(os.environ.get("CPYTHON_TEST_ABI3T", "")) internal = bool(int(os.environ.get("TEST_INTERNAL_C_API", "0"))) + incdirs = os.environ.get("CPYTHON_EXTRA_INCDIRS", "") + libdirs = os.environ.get("CPYTHON_EXTRA_LIBDIRS", "") sources = [SOURCE] @@ -91,35 +97,29 @@ def main(): # CC env var overrides sysconfig CC variable in setuptools os.environ['CC'] = cmd - # Define Py_LIMITED_API macro + # Define opt-in macros if limited: - version = sys.hexversion - cflags.append(f'-DPy_LIMITED_API={version:#x}') + cflags.append(f'-DPy_LIMITED_API={sys.hexversion:#x}') - # Define _Py_OPAQUE_PYOBJECT macro - if opaque_pyobject: - cflags.append(f'-D_Py_OPAQUE_PYOBJECT') - sources.append('create_moduledef.c') + if abi3t: + cflags.append(f'-DPy_TARGET_ABI3T={sys.hexversion:#x}') if internal: cflags.append('-DTEST_INTERNAL_C_API=1') - # On Windows, add PCbuild\amd64\ to include and library directories + # Add additional include and library directories, typically for in-tree + # testing where not all directories are inferred include_dirs = [] library_dirs = [] - if support.MS_WINDOWS: - srcdir = sysconfig.get_config_var('srcdir') - machine = platform.uname().machine - pcbuild = os.path.join(srcdir, 'PCbuild', machine) - if os.path.exists(pcbuild): - # pyconfig.h is generated in PCbuild\amd64\ - include_dirs.append(pcbuild) - # python313.lib is generated in PCbuild\amd64\ - library_dirs.append(pcbuild) - print(f"Add PCbuild directory: {pcbuild}") + if incdirs: + print("Add incdirs:", incdirs) + include_dirs.extend(incdirs.split(os.pathsep)) + if libdirs: + print("Add libdirs:", libdirs) + library_dirs.extend(libdirs.split(os.pathsep)) # Display information to help debugging - for env_name in ('CC', 'CFLAGS'): + for env_name in ('CC', 'CFLAGS', 'CPPFLAGS'): if env_name in os.environ: print(f"{env_name} env var: {os.environ[env_name]!r}") else: diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 8c7a62a74ba90e..64222555166a2e 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -859,7 +859,12 @@ def __init__(self, arg): from _testinternalcapi import has_inline_values -Py_TPFLAGS_MANAGED_DICT = (1 << 2) +Py_TPFLAGS_INLINE_VALUES = (1 << 2) +Py_TPFLAGS_MANAGED_DICT = (1 << 4) + +class NoManagedDict: + __slots__ = ('a',) + class Plain: pass @@ -874,11 +879,31 @@ def __init__(self): self.d = 4 +class VarSizedSubclass(tuple): + pass + + class TestInlineValues(unittest.TestCase): - def test_flags(self): - self.assertEqual(Plain.__flags__ & Py_TPFLAGS_MANAGED_DICT, Py_TPFLAGS_MANAGED_DICT) - self.assertEqual(WithAttrs.__flags__ & Py_TPFLAGS_MANAGED_DICT, Py_TPFLAGS_MANAGED_DICT) + def test_no_flags_for_slots_class(self): + flags = NoManagedDict.__flags__ + self.assertEqual(flags & Py_TPFLAGS_MANAGED_DICT, 0) + self.assertEqual(flags & Py_TPFLAGS_INLINE_VALUES, 0) + self.assertFalse(has_inline_values(NoManagedDict())) + + def test_both_flags_for_regular_class(self): + for cls in (Plain, WithAttrs): + with self.subTest(cls=cls.__name__): + flags = cls.__flags__ + self.assertEqual(flags & Py_TPFLAGS_MANAGED_DICT, Py_TPFLAGS_MANAGED_DICT) + self.assertEqual(flags & Py_TPFLAGS_INLINE_VALUES, Py_TPFLAGS_INLINE_VALUES) + self.assertTrue(has_inline_values(cls())) + + def test_managed_dict_only_for_varsized_subclass(self): + flags = VarSizedSubclass.__flags__ + self.assertEqual(flags & Py_TPFLAGS_MANAGED_DICT, Py_TPFLAGS_MANAGED_DICT) + self.assertEqual(flags & Py_TPFLAGS_INLINE_VALUES, 0) + self.assertFalse(has_inline_values(VarSizedSubclass())) def test_has_inline_values(self): c = Plain() diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index f8d9b0af8f92ec..93c284e58764f4 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -4,6 +4,7 @@ from functools import partial from test import support, test_tools +from test.support import force_not_colorized_test_class from test.support import os_helper from test.support.os_helper import TESTFN, unlink, rmtree from textwrap import dedent @@ -357,6 +358,32 @@ def test_vararg_after_star(self): """ self.expect_failure(block, err, lineno=6) + def test_double_star_after_var_keyword(self): + err = "Function 'my_test_func' has an invalid parameter declaration (**kwargs?): '**kwds: dict'" + block = """ + /*[clinic input] + my_test_func + + pos_arg: object + **kwds: dict + ** + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=5) + + def test_var_keyword_after_star(self): + err = "Function 'my_test_func' has an invalid parameter declaration: '**'" + block = """ + /*[clinic input] + my_test_func + + pos_arg: object + ** + **kwds: dict + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=5) + def test_module_already_got_one(self): err = "Already defined module 'm'!" block = """ @@ -748,6 +775,16 @@ def test_ignore_preprocessor_in_comments(self): """) self.clinic.parse(raw) + def test_var_keyword_non_dict(self): + err = "'var_keyword_object' is not a valid converter" + block = """ + /*[clinic input] + my_test_func + + **kwds: object + [clinic start generated code]*/ + """ + self.expect_failure(block, err, lineno=4) class ParseFileUnitTest(TestCase): def expect_parsing_failure( @@ -1044,6 +1081,187 @@ def test_param_with_continuations(self): p = function.parameters['follow_symlinks'] self.assertEqual(True, p.default) + def test_param_default_none(self): + function = self.parse_function(r""" + module test + test.func + obj: object = None + str: str(accept={str, NoneType}) = None + buf: Py_buffer(accept={str, buffer, NoneType}) = None + """) + p = function.parameters['obj'] + self.assertIs(p.default, None) + self.assertEqual(p.converter.py_default, 'None') + self.assertEqual(p.converter.c_default, 'Py_None') + + p = function.parameters['str'] + self.assertIs(p.default, None) + self.assertEqual(p.converter.py_default, 'None') + self.assertEqual(p.converter.c_default, 'NULL') + + p = function.parameters['buf'] + self.assertIs(p.default, None) + self.assertEqual(p.converter.py_default, 'None') + self.assertEqual(p.converter.c_default, '{NULL, NULL}') + + def test_param_default_null(self): + function = self.parse_function(r""" + module test + test.func + obj: object = NULL + str: str = NULL + buf: Py_buffer = NULL + fsencoded: unicode_fs_encoded = NULL + fsdecoded: unicode_fs_decoded = NULL + """) + p = function.parameters['obj'] + self.assertIs(p.default, NULL) + self.assertEqual(p.converter.py_default, '') + self.assertEqual(p.converter.c_default, 'NULL') + + p = function.parameters['str'] + self.assertIs(p.default, NULL) + self.assertEqual(p.converter.py_default, '') + self.assertEqual(p.converter.c_default, 'NULL') + + p = function.parameters['buf'] + self.assertIs(p.default, NULL) + self.assertEqual(p.converter.py_default, '') + self.assertEqual(p.converter.c_default, '{NULL, NULL}') + + p = function.parameters['fsencoded'] + self.assertIs(p.default, NULL) + self.assertEqual(p.converter.py_default, '') + self.assertEqual(p.converter.c_default, 'NULL') + + p = function.parameters['fsdecoded'] + self.assertIs(p.default, NULL) + self.assertEqual(p.converter.py_default, '') + self.assertEqual(p.converter.c_default, 'NULL') + + def test_param_default_str_literal(self): + function = self.parse_function(r""" + module test + test.func + str: str = ' \t\n\r\v\f\xa0' + buf: Py_buffer(accept={str, buffer}) = ' \t\n\r\v\f\xa0' + """) + p = function.parameters['str'] + self.assertEqual(p.default, ' \t\n\r\v\f\xa0') + self.assertEqual(p.converter.py_default, r"' \t\n\r\x0b\x0c\xa0'") + self.assertEqual(p.converter.c_default, r'" \t\n\r\v\f\u00a0"') + + p = function.parameters['buf'] + self.assertEqual(p.default, ' \t\n\r\v\f\xa0') + self.assertEqual(p.converter.py_default, r"' \t\n\r\x0b\x0c\xa0'") + self.assertEqual(p.converter.c_default, + r'{.buf = " \t\n\r\v\f\302\240", .obj = NULL, .len = 8}') + + def test_param_default_bytes_literal(self): + function = self.parse_function(r""" + module test + test.func + str: str(accept={robuffer}) = b' \t\n\r\v\f\xa0' + buf: Py_buffer = b' \t\n\r\v\f\xa0' + """) + p = function.parameters['str'] + self.assertEqual(p.default, b' \t\n\r\v\f\xa0') + self.assertEqual(p.converter.py_default, r"b' \t\n\r\x0b\x0c\xa0'") + self.assertEqual(p.converter.c_default, r'" \t\n\r\v\f\240"') + + p = function.parameters['buf'] + self.assertEqual(p.default, b' \t\n\r\v\f\xa0') + self.assertEqual(p.converter.py_default, r"b' \t\n\r\x0b\x0c\xa0'") + self.assertEqual(p.converter.c_default, + r'{.buf = " \t\n\r\v\f\240", .obj = NULL, .len = 7}') + + def test_param_default_byte_literal(self): + function = self.parse_function(r""" + module test + test.func + zero: char = b'\0' + one: char = b'\1' + lf: char = b'\n' + nbsp: char = b'\xa0' + """) + p = function.parameters['zero'] + self.assertEqual(p.default, b'\0') + self.assertEqual(p.converter.py_default, r"b'\x00'") + self.assertEqual(p.converter.c_default, r"'\0'") + + p = function.parameters['one'] + self.assertEqual(p.default, b'\1') + self.assertEqual(p.converter.py_default, r"b'\x01'") + self.assertEqual(p.converter.c_default, r"'\001'") + + p = function.parameters['lf'] + self.assertEqual(p.default, b'\n') + self.assertEqual(p.converter.py_default, r"b'\n'") + self.assertEqual(p.converter.c_default, r"'\n'") + + p = function.parameters['nbsp'] + self.assertEqual(p.default, b'\xa0') + self.assertEqual(p.converter.py_default, r"b'\xa0'") + self.assertEqual(p.converter.c_default, r"'\240'") + + def test_param_default_unicode_char(self): + function = self.parse_function(r""" + module test + test.func + zero: int(accept={str}) = '\0' + one: int(accept={str}) = '\1' + lf: int(accept={str}) = '\n' + nbsp: int(accept={str}) = '\xa0' + snake: int(accept={str}) = '\U0001f40d' + """) + p = function.parameters['zero'] + self.assertEqual(p.default, '\0') + self.assertEqual(p.converter.py_default, r"'\x00'") + self.assertEqual(p.converter.c_default, '0') + + p = function.parameters['one'] + self.assertEqual(p.default, '\1') + self.assertEqual(p.converter.py_default, r"'\x01'") + self.assertEqual(p.converter.c_default, '0x01') + + p = function.parameters['lf'] + self.assertEqual(p.default, '\n') + self.assertEqual(p.converter.py_default, r"'\n'") + self.assertEqual(p.converter.c_default, r"'\n'") + + p = function.parameters['nbsp'] + self.assertEqual(p.default, '\xa0') + self.assertEqual(p.converter.py_default, r"'\xa0'") + self.assertEqual(p.converter.c_default, '0xa0') + + p = function.parameters['snake'] + self.assertEqual(p.default, '\U0001f40d') + self.assertEqual(p.converter.py_default, "'\U0001f40d'") + self.assertEqual(p.converter.c_default, '0x1f40d') + + def test_param_default_bool(self): + function = self.parse_function(r""" + module test + test.func + bool: bool = True + intbool: bool(accept={int}) = True + intbool2: bool(accept={int}) = 2 + """) + p = function.parameters['bool'] + self.assertIs(p.default, True) + self.assertEqual(p.converter.py_default, 'True') + self.assertEqual(p.converter.c_default, '1') + + p = function.parameters['intbool'] + self.assertIs(p.default, True) + self.assertEqual(p.converter.py_default, 'True') + self.assertEqual(p.converter.c_default, '1') + + p = function.parameters['intbool2'] + self.assertEqual(p.default, 2) + self.assertEqual(p.converter.py_default, '2') + self.assertEqual(p.converter.c_default, '2') + def test_param_default_expr_named_constant(self): function = self.parse_function(""" module os @@ -1608,6 +1826,11 @@ def test_disallowed_grouping__must_be_position_only(self): [ a: object ] + """, """ + with_kwds + [ + **kwds: dict + ] """) err = ( "You cannot use optional groups ('[' and ']') unless all " @@ -1991,6 +2214,44 @@ def test_slash_after_vararg(self): err = "Function 'bar': '/' must precede '*'" self.expect_failure(block, err) + def test_slash_after_var_keyword(self): + block = """ + module foo + foo.bar + x: int + y: int + **kwds: dict + z: int + / + """ + err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'" + self.expect_failure(block, err) + + def test_star_after_var_keyword(self): + block = """ + module foo + foo.bar + x: int + y: int + **kwds: dict + z: int + * + """ + err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'" + self.expect_failure(block, err) + + def test_parameter_after_var_keyword(self): + block = """ + module foo + foo.bar + x: int + y: int + **kwds: dict + z: int + """ + err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'" + self.expect_failure(block, err) + def test_depr_star_must_come_after_slash(self): block = """ module foo @@ -2079,6 +2340,16 @@ def test_parameters_no_more_than_one_vararg(self): """ self.expect_failure(block, err, lineno=3) + def test_parameters_no_more_than_one_var_keyword(self): + err = "Encountered parameter line when not expecting parameters: **var_keyword_2: dict" + block = """ + module foo + foo.bar + **var_keyword_1: dict + **var_keyword_2: dict + """ + self.expect_failure(block, err, lineno=3) + def test_function_not_at_column_0(self): function = self.parse_function(""" module foo @@ -2513,6 +2784,14 @@ def test_vararg_cannot_take_default_value(self): """ self.expect_failure(block, err, lineno=1) + def test_var_keyword_cannot_take_default_value(self): + err = "Function 'fn' has an invalid parameter declaration:" + block = """ + fn + **kwds: dict = None + """ + self.expect_failure(block, err, lineno=1) + def test_default_is_not_of_correct_type(self): err = ("int_converter: default value 2.5 for field 'a' " "is not of type 'int'") @@ -2610,7 +2889,58 @@ def test_disallow_defining_class_at_module_level(self): """ self.expect_failure(block, err, lineno=2) + def test_var_keyword_with_pos_or_kw(self): + block = """ + module foo + foo.bar + x: int + **kwds: dict + """ + err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'" + self.expect_failure(block, err) + + def test_var_keyword_with_kw_only(self): + block = """ + module foo + foo.bar + x: int + / + * + y: int + **kwds: dict + """ + err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'" + self.expect_failure(block, err) + + def test_var_keyword_with_pos_or_kw_and_kw_only(self): + block = """ + module foo + foo.bar + x: int + / + y: int + * + z: int + **kwds: dict + """ + err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'" + self.expect_failure(block, err) + + def test_allow_negative_accepted_by_py_ssize_t_converter_only(self): + errmsg = re.escape("converter_init() got an unexpected keyword argument 'allow_negative'") + unsupported_converters = [converter_name for converter_name in converters.keys() + if converter_name != "Py_ssize_t"] + for converter in unsupported_converters: + with self.subTest(converter=converter): + block = f""" + module m + m.func + a: {converter}(allow_negative=True) + """ + with self.assertRaisesRegex((AssertionError, TypeError), errmsg): + self.parse_function(block) +@force_not_colorized_test_class class ClinicExternalTest(TestCase): maxDiff = None @@ -2833,6 +3163,8 @@ def test_cli_converters(self): "uint64", "uint8", "unicode", + "unicode_fs_decoded", + "unicode_fs_encoded", "unsigned_char", "unsigned_int", "unsigned_long", @@ -3194,8 +3526,12 @@ def test_py_ssize_t_converter(self): ac_tester.py_ssize_t_converter(PY_SSIZE_T_MAX + 1) with self.assertRaises(TypeError): ac_tester.py_ssize_t_converter([]) - self.assertEqual(ac_tester.py_ssize_t_converter(), (12, 34, 56)) - self.assertEqual(ac_tester.py_ssize_t_converter(1, 2, None), (1, 2, 56)) + with self.assertRaises(ValueError): + ac_tester.py_ssize_t_converter(12, 34, 56, -1) + with self.assertRaises(ValueError): + ac_tester.py_ssize_t_converter(12, 34, 56, 78, -1) + self.assertEqual(ac_tester.py_ssize_t_converter(), (12, 34, 56, 78, 90, -12, -34)) + self.assertEqual(ac_tester.py_ssize_t_converter(1, 2, None, 3, None, 4, None), (1, 2, 56, 3, 90, 4, -34)) def test_slice_index_converter(self): from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX @@ -3937,6 +4273,49 @@ def test_depr_multi(self): check("a", b="b", c="c", d="d", e="e", f="f", g="g") self.assertRaises(TypeError, fn, a="a", b="b", c="c", d="d", e="e", f="f", g="g") + def test_lone_kwds(self): + with self.assertRaises(TypeError): + ac_tester.lone_kwds(1, 2) + self.assertEqual(ac_tester.lone_kwds(), ({},)) + self.assertEqual(ac_tester.lone_kwds(y='y'), ({'y': 'y'},)) + kwds = {'y': 'y', 'z': 'z'} + self.assertEqual(ac_tester.lone_kwds(y='y', z='z'), (kwds,)) + self.assertEqual(ac_tester.lone_kwds(**kwds), (kwds,)) + + def test_kwds_with_pos_only(self): + with self.assertRaises(TypeError): + ac_tester.kwds_with_pos_only() + with self.assertRaises(TypeError): + ac_tester.kwds_with_pos_only(y='y') + with self.assertRaises(TypeError): + ac_tester.kwds_with_pos_only(1, y='y') + self.assertEqual(ac_tester.kwds_with_pos_only(1, 2), (1, 2, {})) + self.assertEqual(ac_tester.kwds_with_pos_only(1, 2, y='y'), (1, 2, {'y': 'y'})) + kwds = {'y': 'y', 'z': 'z'} + self.assertEqual(ac_tester.kwds_with_pos_only(1, 2, y='y', z='z'), (1, 2, kwds)) + self.assertEqual(ac_tester.kwds_with_pos_only(1, 2, **kwds), (1, 2, kwds)) + + def test_kwds_with_stararg(self): + self.assertEqual(ac_tester.kwds_with_stararg(), ((), {})) + self.assertEqual(ac_tester.kwds_with_stararg(1, 2), ((1, 2), {})) + self.assertEqual(ac_tester.kwds_with_stararg(y='y'), ((), {'y': 'y'})) + args = (1, 2) + kwds = {'y': 'y', 'z': 'z'} + self.assertEqual(ac_tester.kwds_with_stararg(1, 2, y='y', z='z'), (args, kwds)) + self.assertEqual(ac_tester.kwds_with_stararg(*args, **kwds), (args, kwds)) + + def test_kwds_with_pos_only_and_stararg(self): + with self.assertRaises(TypeError): + ac_tester.kwds_with_pos_only_and_stararg() + with self.assertRaises(TypeError): + ac_tester.kwds_with_pos_only_and_stararg(y='y') + self.assertEqual(ac_tester.kwds_with_pos_only_and_stararg(1, 2), (1, 2, (), {})) + self.assertEqual(ac_tester.kwds_with_pos_only_and_stararg(1, 2, y='y'), (1, 2, (), {'y': 'y'})) + args = ('lobster', 'thermidor') + kwds = {'y': 'y', 'z': 'z'} + self.assertEqual(ac_tester.kwds_with_pos_only_and_stararg(1, 2, 'lobster', 'thermidor', y='y', z='z'), (1, 2, args, kwds)) + self.assertEqual(ac_tester.kwds_with_pos_only_and_stararg(1, 2, *args, **kwds), (1, 2, args, kwds)) + class LimitedCAPIOutputTests(unittest.TestCase): @@ -4234,6 +4613,56 @@ def test_format_escape(self): out = libclinic.format_escape(line) self.assertEqual(out, expected) + def test_c_bytes_repr(self): + c_bytes_repr = libclinic.c_bytes_repr + self.assertEqual(c_bytes_repr(b''), '""') + self.assertEqual(c_bytes_repr(b'abc'), '"abc"') + self.assertEqual(c_bytes_repr(b'\a\b\f\n\r\t\v'), r'"\a\b\f\n\r\t\v"') + self.assertEqual(c_bytes_repr(b' \0\x7f'), r'" \000\177"') + self.assertEqual(c_bytes_repr(b'"'), r'"\""') + self.assertEqual(c_bytes_repr(b"'"), r'''"'"''') + self.assertEqual(c_bytes_repr(b'\\'), r'"\\"') + self.assertEqual(c_bytes_repr(b'??/'), r'"?\?/"') + self.assertEqual(c_bytes_repr(b'???/'), r'"?\?\?/"') + self.assertEqual(c_bytes_repr(b'/*****/ /*/ */*'), r'"/\*****\/ /\*\/ *\/\*"') + self.assertEqual(c_bytes_repr(b'\xa0'), r'"\240"') + self.assertEqual(c_bytes_repr(b'\xff'), r'"\377"') + + def test_c_str_repr(self): + c_str_repr = libclinic.c_str_repr + self.assertEqual(c_str_repr(''), '""') + self.assertEqual(c_str_repr('abc'), '"abc"') + self.assertEqual(c_str_repr('\a\b\f\n\r\t\v'), r'"\a\b\f\n\r\t\v"') + self.assertEqual(c_str_repr(' \0\x7f'), r'" \000\177"') + self.assertEqual(c_str_repr('"'), r'"\""') + self.assertEqual(c_str_repr("'"), r'''"'"''') + self.assertEqual(c_str_repr('\\'), r'"\\"') + self.assertEqual(c_str_repr('??/'), r'"?\?/"') + self.assertEqual(c_str_repr('???/'), r'"?\?\?/"') + self.assertEqual(c_str_repr('/*****/ /*/ */*'), r'"/\*****\/ /\*\/ *\/\*"') + self.assertEqual(c_str_repr('\xa0'), r'"\u00a0"') + self.assertEqual(c_str_repr('\xff'), r'"\u00ff"') + self.assertEqual(c_str_repr('\u20ac'), r'"\u20ac"') + self.assertEqual(c_str_repr('\U0001f40d'), r'"\U0001f40d"') + + def test_c_unichar_repr(self): + c_unichar_repr = libclinic.c_unichar_repr + self.assertEqual(c_unichar_repr('a'), "'a'") + self.assertEqual(c_unichar_repr('\n'), r"'\n'") + self.assertEqual(c_unichar_repr('\b'), r"'\b'") + self.assertEqual(c_unichar_repr('\0'), '0') + self.assertEqual(c_unichar_repr('\1'), '0x01') + self.assertEqual(c_unichar_repr('\x7f'), '0x7f') + self.assertEqual(c_unichar_repr(' '), "' '") + self.assertEqual(c_unichar_repr('"'), """'"'""") + self.assertEqual(c_unichar_repr("'"), r"'\''") + self.assertEqual(c_unichar_repr('\\'), r"'\\'") + self.assertEqual(c_unichar_repr('?'), "'?'") + self.assertEqual(c_unichar_repr('\xa0'), '0xa0') + self.assertEqual(c_unichar_repr('\xff'), '0xff') + self.assertEqual(c_unichar_repr('\u20ac'), '0x20ac') + self.assertEqual(c_unichar_repr('\U0001f40d'), '0x1f40d') + def test_indent_all_lines(self): # Blank lines are expected to be unchanged. self.assertEqual(libclinic.indent_all_lines("", prefix="bar"), "") diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index a96a5780b31b6f..a986fd6b892bd2 100644 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -406,6 +406,8 @@ def polar_with_errno_set(z): _testcapi.set_errno(0) self.check_polar(polar_with_errno_set) + @unittest.skipIf(sys.platform.startswith("sunos"), + "skipping, see gh-138573") def test_phase(self): self.assertAlmostEqual(phase(0), 0.) self.assertAlmostEqual(phase(1.), 0.) @@ -514,6 +516,7 @@ def test_isinf(self): self.assertFalse(cmath.isinf(1j)) self.assertFalse(cmath.isinf(NAN)) self.assertTrue(cmath.isinf(INF)) + self.assertTrue(cmath.isinf(-INF)) self.assertTrue(cmath.isinf(complex(INF, 0))) self.assertTrue(cmath.isinf(complex(0, INF))) self.assertTrue(cmath.isinf(complex(INF, INF))) diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index f30a1874ab96d4..a8645af26b25d8 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -3,13 +3,13 @@ # See test_cmd_line_script.py for testing of script execution import os +import re import subprocess import sys import sysconfig import tempfile import textwrap import unittest -import warnings from test import support from test.support import os_helper from test.support import force_not_colorized @@ -32,6 +32,17 @@ def _kill_python_and_exit_code(p): return data, returncode +def presite_func(): + print("presite func") + +class Namespace: + pass + +presite = Namespace() +presite.attr = Namespace() +presite.attr.func = presite_func + + class CmdLineTest(unittest.TestCase): def test_directories(self): assert_python_failure('.') @@ -46,6 +57,7 @@ def verify_valid_flag(self, cmd_line): return out @support.cpython_only + @support.force_not_colorized def test_help(self): self.verify_valid_flag('-h') self.verify_valid_flag('-?') @@ -57,16 +69,30 @@ def test_help(self): self.assertLess(len(lines), 50) @support.cpython_only + @support.force_not_colorized def test_help_env(self): out = self.verify_valid_flag('--help-env') self.assertIn(b'PYTHONHOME', out) + # Env vars in each section should be sorted alphabetically + # (ignoring underscores so PYTHON_FOO and PYTHONFOO intermix naturally) + sort_key = lambda name: name.replace(b'_', b'').lower() + sections = out.split(b'These variables have equivalent') + for section in sections: + envvars = re.findall(rb'^(PYTHON\w+)', section, re.MULTILINE) + self.assertEqual(envvars, sorted(envvars, key=sort_key), + "env vars should be sorted alphabetically") @support.cpython_only + @support.force_not_colorized def test_help_xoptions(self): out = self.verify_valid_flag('--help-xoptions') self.assertIn(b'-X dev', out) + options = re.findall(rb'^-X (\w+)', out, re.MULTILINE) + self.assertEqual(options, sorted(options), + "options should be sorted alphabetically") @support.cpython_only + @support.force_not_colorized def test_help_all(self): out = self.verify_valid_flag('--help-all') lines = out.splitlines() @@ -78,6 +104,25 @@ def test_help_all(self): # but the rest should be ASCII-only b''.join(lines[1:]).decode('ascii') + @support.cpython_only + @support.force_colorized + def test_help_colorized(self): + rc, out, err = assert_python_ok("--help", FORCE_COLOR="1") + # Check ANSI color codes are present + self.assertIn(b"\x1b[", out) + # Check that key text elements are still present + self.assertIn(b"usage:", out) + self.assertIn(b"-h", out) + self.assertIn(b"--help-all", out) + self.assertIn(b"cmd", out) + self.assertIn(b"Arguments:", out) + + @support.cpython_only + @support.force_not_colorized + def test_help_not_colorized(self): + rc, out, err = assert_python_ok("--help") + self.assertNotIn(b"\x1b[", out) + def test_optimize(self): self.verify_valid_flag('-O') self.verify_valid_flag('-OO') @@ -201,6 +246,14 @@ def test_run_module_bug1764407(self): self.assertTrue(data.find(b'1 loop') != -1) self.assertTrue(data.find(b'__main__.Timer') != -1) + @support.cpython_only + def test_null_byte_in_interactive_mode(self): + # gh-140594: Fix an out of bounds read when a single NUL character + # is read from the standard input in interactive mode. + proc = spawn_python('-i') + proc.communicate(b'\x00', timeout=support.SHORT_TIMEOUT) + self.assertEqual(proc.returncode, 0) + def test_relativedir_bug46421(self): # Test `python -m unittest` with a relative directory beginning with ./ # Note: We have to switch to the project's top module's directory, as per @@ -489,6 +542,7 @@ def test_unmached_quote(self): self.assertRegex(err.decode('ascii', 'ignore'), 'SyntaxError') self.assertEqual(b'', out) + @force_not_colorized def test_stdout_flush_at_shutdown(self): # Issue #5319: if stdout.flush() fails at shutdown, an error should # be printed out. @@ -942,21 +996,15 @@ def test_python_asyncio_debug(self): @unittest.skipUnless(sysconfig.get_config_var('Py_TRACE_REFS'), "Requires --with-trace-refs build option") def test_python_dump_refs(self): - code = 'import sys; sys._clear_type_cache()' - # TODO: Remove warnings context manager once sys._clear_type_cache is removed - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - rc, out, err = assert_python_ok('-c', code, PYTHONDUMPREFS='1') + code = 'import sys; sys._clear_internal_caches()' + rc, out, err = assert_python_ok('-c', code, PYTHONDUMPREFS='1') self.assertEqual(rc, 0) @unittest.skipUnless(sysconfig.get_config_var('Py_TRACE_REFS'), "Requires --with-trace-refs build option") def test_python_dump_refs_file(self): with tempfile.NamedTemporaryFile() as dump_file: - code = 'import sys; sys._clear_type_cache()' - # TODO: Remove warnings context manager once sys._clear_type_cache is removed - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - rc, out, err = assert_python_ok('-c', code, PYTHONDUMPREFSFILE=dump_file.name) + code = 'import sys; sys._clear_internal_caches()' + rc, out, err = assert_python_ok('-c', code, PYTHONDUMPREFSFILE=dump_file.name) self.assertEqual(rc, 0) with open(dump_file.name, 'r') as file: contents = file.read() @@ -988,6 +1036,7 @@ def test_python_legacy_windows_stdio(self): p = subprocess.run([sys.executable, "-c", code], creationflags=subprocess.CREATE_NEW_CONSOLE, env=env) + support.skip_on_low_desktop_heap_memory_subprocess(p.returncode) self.assertEqual(p.returncode, 0) # Then test that FIleIO is used when PYTHONLEGACYWINDOWSSTDIO is set. @@ -996,6 +1045,7 @@ def test_python_legacy_windows_stdio(self): p = subprocess.run([sys.executable, "-c", code], creationflags=subprocess.CREATE_NEW_CONSOLE, env=env) + support.skip_on_low_desktop_heap_memory_subprocess(p.returncode) self.assertEqual(p.returncode, 0) @unittest.skipIf("-fsanitize" in sysconfig.get_config_vars().get('PY_CFLAGS', ()), @@ -1038,6 +1088,7 @@ def test_argv0_normalization(self): self.assertEqual(proc.stdout.strip(), b'0') @support.cpython_only + @support.force_not_colorized def test_parsing_error(self): args = [sys.executable, '-I', '--unknown-option'] proc = subprocess.run(args, @@ -1252,6 +1303,28 @@ def test_invalid_thread_local_bytecode(self): rc, out, err = assert_python_failure(PYTHON_TLBC="2") self.assertIn(b"PYTHON_TLBC=N: N is missing or invalid", err) + @unittest.skipUnless(support.Py_DEBUG, + '-X presite requires a Python debug build') + def test_presite(self): + entrypoint = "test.test_cmd_line:presite_func" + proc = assert_python_ok("-X", f"presite={entrypoint}", "-c", "pass") + self.assertEqual(proc.out.rstrip(), b"presite func") + + entrypoint = "test.test_cmd_line:presite.attr.func" + proc = assert_python_ok("-X", f"presite={entrypoint}", "-c", "pass") + self.assertEqual(proc.out.rstrip(), b"presite func") + + def test_dump_path_config(self): + # gh-151253: At the first import (import encodings) during Python + # startup, if the import fails, dump the Python path configuration. + nonexistent = '/nonexistent-python-path' + # Use -X frozen_modules=off to disable frozen encodings module + # on release build. + cmd = ["-X", "frozen_modules=off", "-c", "pass"] + proc = assert_python_failure(*cmd, PYTHONHOME=nonexistent) + self.assertIn(b'Python path configuration:', proc.err) + self.assertIn(f"PYTHONHOME = '{nonexistent}'".encode(), proc.err) + @unittest.skipIf(interpreter_requires_environment(), 'Cannot run -I tests when PYTHON env vars are required.') diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index 784c45aa96f8a7..73b1f671c58555 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -44,7 +44,6 @@ def f(): _loader = __loader__ if __loader__ is BuiltinImporter else type(__loader__) print('__loader__==%a' % _loader) print('__file__==%a' % __file__) -print('__cached__==%a' % __cached__) print('__package__==%r' % __package__) # Check PEP 451 details import os.path @@ -58,8 +57,6 @@ def f(): assertEqual(__spec__.parent, __package__) assertIdentical(__spec__.submodule_search_locations, None) assertEqual(__spec__.origin, __file__) - if __spec__.cached is not None: - assertEqual(__spec__.cached, __cached__) # Check the sys module import sys assertIdentical(globals(), sys.modules[__name__].__dict__) @@ -243,9 +240,8 @@ def test_script_abspath(self): def test_script_compiled(self): with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script') - py_compile.compile(script_name, doraise=True) + pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True) os.remove(script_name) - pyc_file = import_helper.make_legacy_pyc(script_name) self._check_script(pyc_file, pyc_file, pyc_file, script_dir, None, importlib.machinery.SourcelessFileLoader) @@ -260,9 +256,8 @@ def test_directory(self): def test_directory_compiled(self): with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') - py_compile.compile(script_name, doraise=True) + pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True) os.remove(script_name) - pyc_file = import_helper.make_legacy_pyc(script_name) self._check_script(script_dir, pyc_file, script_dir, script_dir, '', importlib.machinery.SourcelessFileLoader) @@ -282,8 +277,8 @@ def test_zipfile(self): def test_zipfile_compiled_timestamp(self): with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') - compiled_name = py_compile.compile( - script_name, doraise=True, + compiled_name = script_name + 'c' + py_compile.compile(script_name, compiled_name, doraise=True, invalidation_mode=py_compile.PycInvalidationMode.TIMESTAMP) zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name) self._check_script(zip_name, run_name, zip_name, zip_name, '', @@ -292,8 +287,8 @@ def test_zipfile_compiled_timestamp(self): def test_zipfile_compiled_checked_hash(self): with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') - compiled_name = py_compile.compile( - script_name, doraise=True, + compiled_name = script_name + 'c' + py_compile.compile(script_name, compiled_name, doraise=True, invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH) zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name) self._check_script(zip_name, run_name, zip_name, zip_name, '', @@ -302,8 +297,8 @@ def test_zipfile_compiled_checked_hash(self): def test_zipfile_compiled_unchecked_hash(self): with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__') - compiled_name = py_compile.compile( - script_name, doraise=True, + compiled_name = script_name + 'c' + py_compile.compile(script_name, compiled_name, doraise=True, invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH) zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name) self._check_script(zip_name, run_name, zip_name, zip_name, '', @@ -356,9 +351,8 @@ def test_package_compiled(self): pkg_dir = os.path.join(script_dir, 'test_pkg') make_pkg(pkg_dir) script_name = _make_test_script(pkg_dir, '__main__') - compiled_name = py_compile.compile(script_name, doraise=True) + pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True) os.remove(script_name) - pyc_file = import_helper.make_legacy_pyc(script_name) self._check_script(["-m", "test_pkg"], pyc_file, pyc_file, script_dir, 'test_pkg', importlib.machinery.SourcelessFileLoader, @@ -810,6 +804,30 @@ def test_script_as_dev_fd(self): out, err = p.communicate() self.assertEqual(out, b"12345678912345678912345\n") + def test_filter_syntax_warnings_by_module(self): + filename = support.findfile('test_import/data/syntax_warnings.py') + rc, out, err = assert_python_ok( + '-Werror', + '-Walways:::__main__', + '-Werror:::test.test_import.data.syntax_warnings', + '-Werror:::syntax_warnings', + filename) + self.assertEqual(err.count(b': SyntaxWarning: '), 6) + + def test_zipfile_run_filter_syntax_warnings_by_module(self): + filename = support.findfile('test_import/data/syntax_warnings.py') + with open(filename, 'rb') as f: + source = f.read() + with os_helper.temp_dir() as script_dir: + zip_name, _ = make_zip_pkg( + script_dir, 'test_zip', 'test_pkg', '__main__', source) + rc, out, err = assert_python_ok( + '-Werror', + '-Walways:::__main__', + '-Werror:::test_pkg.__main__', + os.path.join(zip_name, 'test_pkg') + ) + self.assertEqual(err.count(b': SyntaxWarning: '), 12) def tearDownModule(): diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 655f5a9be7fa31..3588872ed23ac4 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -87,7 +87,7 @@ freevars: () nlocals: 0 flags: 67108867 -consts: ("'doc string'", 'None') +consts: ("'doc string'",) >>> def keywordonly_args(a,b,*,k1): ... return a,b,k1 @@ -161,7 +161,7 @@ freevars: () nlocals: 3 flags: 67108995 -consts: ("'This is a docstring from async function'", 'None') +consts: ("'This is a docstring from async function'",) >>> def no_docstring(x, y, z): ... return x + "hello" + y + z + "world" @@ -210,7 +210,7 @@ ctypes = None from test.support import (cpython_only, check_impl_detail, requires_debug_ranges, - gc_collect, Py_GIL_DISABLED) + gc_collect, Py_GIL_DISABLED, late_deletion) from test.support.script_helper import assert_python_ok from test.support import threading_helper, import_helper from test.support.bytecode_helper import instructions_with_positions @@ -424,13 +424,6 @@ def func(): new_code = code = func.__code__.replace(co_linetable=b'') self.assertEqual(list(new_code.co_lines()), []) - def test_co_lnotab_is_deprecated(self): # TODO: remove in 3.14 - def func(): - pass - - with self.assertWarns(DeprecationWarning): - func.__code__.co_lnotab - @unittest.skipIf(_testinternalcapi is None, '_testinternalcapi is missing') def test_returns_only_none(self): value = True @@ -539,7 +532,7 @@ def test_co_positions_artificial_instructions(self): ], [ ("PUSH_EXC_INFO", None), - ("LOAD_CONST", None), # artificial 'None' + ("LOAD_COMMON_CONSTANT", None), # artificial 'None' ("STORE_NAME", "e"), # XX: we know the location for this ("DELETE_NAME", "e"), ("RERAISE", 1), @@ -1164,6 +1157,18 @@ def test_stateless(self): with self.assertRaises(Exception): _testinternalcapi.verify_stateless_code(func) + def test_code_richcompare_raise_exception(self): + class BadStr(str): + def __eq__(self, _): + raise RuntimeError("Poison!") + + __hash__ = str.__hash__ + + c1 = compile("pass", "test", "exec") + c2 = c1.replace(co_name=BadStr("poison")) + c3 = compile("pass", "poison", "exec") + with self.assertRaises(RuntimeError): + c2 == c3 def isinterned(s): return s is sys.intern(('_' + s + '_')[1:-1]) @@ -1437,6 +1442,7 @@ def f(): co_code=bytes( [ dis.opmap["RESUME"], 0, + dis.opmap["CACHE"], 0, dis.opmap["LOAD_COMMON_CONSTANT"], 0, dis.opmap["RAISE_VARARGS"], 1, ] @@ -1445,7 +1451,7 @@ def f(): [ (1 << 7) | (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3) - | (3 - 1), + | (4 - 1), 0, ] ), @@ -1453,7 +1459,7 @@ def f(): self.assertRaises(AssertionError, f) self.assertEqual( list(f.__code__.co_positions()), - 3 * [(42, 42, None, None)], + 4 * [(42, 42, None, None)], ) @cpython_only @@ -1555,6 +1561,11 @@ def myfree(ptr): FREE_FUNC = freefunc(myfree) FREE_INDEX = RequestCodeExtraIndex(FREE_FUNC) + # Make sure myfree sticks around at least as long as the interpreter, + # since we (currently) can't unregister the function and leaving a + # dangling pointer will cause a crash on deallocation of code objects if + # something else uses co_extras, like test_capi.test_misc. + late_deletion(myfree) class CoExtra(unittest.TestCase): def get_func(self): diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index d8666f7290e72e..8fdd08df9e4f46 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -13,6 +13,7 @@ from test import support from test.support import os_helper +from test.support import warnings_helper try: import _testlimitedcapi @@ -1891,9 +1892,11 @@ def test_copy(self): self.assertIsNot(dup, orig) self.assertEqual(dup, orig) self.assertTrue(orig._is_text_encoding) + self.assertIsInstance(orig._expat_decoding_table, tuple) self.assertEqual(dup.encode, orig.encode) self.assertEqual(dup.name, orig.name) self.assertEqual(dup.incrementalencoder, orig.incrementalencoder) + self.assertIs(dup._expat_decoding_table, orig._expat_decoding_table) # Test a CodecInfo with _is_text_encoding equal to false. orig = codecs.lookup("base64") @@ -1901,9 +1904,11 @@ def test_copy(self): self.assertIsNot(dup, orig) self.assertEqual(dup, orig) self.assertFalse(orig._is_text_encoding) + self.assertNotHasAttr(orig, '_expat_decoding_table') self.assertEqual(dup.encode, orig.encode) self.assertEqual(dup.name, orig.name) self.assertEqual(dup.incrementalencoder, orig.incrementalencoder) + self.assertNotHasAttr(dup, '_expat_decoding_table') def test_deepcopy(self): orig = codecs.lookup('utf-8') @@ -1911,9 +1916,11 @@ def test_deepcopy(self): self.assertIsNot(dup, orig) self.assertEqual(dup, orig) self.assertTrue(orig._is_text_encoding) + self.assertIsInstance(orig._expat_decoding_table, tuple) self.assertEqual(dup.encode, orig.encode) self.assertEqual(dup.name, orig.name) self.assertEqual(dup.incrementalencoder, orig.incrementalencoder) + self.assertIs(dup._expat_decoding_table, orig._expat_decoding_table) # Test a CodecInfo with _is_text_encoding equal to false. orig = codecs.lookup("base64") @@ -1921,9 +1928,11 @@ def test_deepcopy(self): self.assertIsNot(dup, orig) self.assertEqual(dup, orig) self.assertFalse(orig._is_text_encoding) + self.assertNotHasAttr(orig, '_expat_decoding_table') self.assertEqual(dup.encode, orig.encode) self.assertEqual(dup.name, orig.name) self.assertEqual(dup.incrementalencoder, orig.incrementalencoder) + self.assertNotHasAttr(dup, '_expat_decoding_table') def test_pickle(self): codec_info = codecs.lookup('utf-8') @@ -1939,6 +1948,8 @@ def test_pickle(self): unpickled_codec_info.incrementalencoder ) self.assertTrue(unpickled_codec_info._is_text_encoding) + self.assertEqual(unpickled_codec_info._expat_decoding_table, + codec_info._expat_decoding_table) # Test a CodecInfo with _is_text_encoding equal to false. codec_info = codecs.lookup('base64') @@ -1954,6 +1965,7 @@ def test_pickle(self): unpickled_codec_info.incrementalencoder ) self.assertFalse(unpickled_codec_info._is_text_encoding) + self.assertNotHasAttr(unpickled_codec_info, '_expat_decoding_table') class StreamReaderTest(unittest.TestCase): @@ -3293,7 +3305,7 @@ def test_code_page_name(self): codecs.code_page_encode, 932, '\xff') self.assertRaisesRegex(UnicodeDecodeError, 'cp932', codecs.code_page_decode, 932, b'\x81\x00', 'strict', True) - self.assertRaisesRegex(UnicodeDecodeError, 'CP_UTF8', + self.assertRaisesRegex(UnicodeDecodeError, 'cp65001', codecs.code_page_decode, self.CP_UTF8, b'\xff', 'strict', True) def check_decode(self, cp, tests): @@ -3873,32 +3885,27 @@ def test_rot13_func(self): class CodecNameNormalizationTest(unittest.TestCase): """Test codec name normalization""" def test_codecs_lookup(self): - FOUND = (1, 2, 3, 4) - NOT_FOUND = (None, None, None, None) def search_function(encoding): - if encoding == "aaa_8": - return FOUND + if encoding.startswith("test."): + return (encoding, 2, 3, 4) else: - return NOT_FOUND + return None codecs.register(search_function) self.addCleanup(codecs.unregister, search_function) - self.assertEqual(FOUND, codecs.lookup('aaa_8')) - self.assertEqual(FOUND, codecs.lookup('AAA-8')) - self.assertEqual(FOUND, codecs.lookup('AAA---8')) - self.assertEqual(FOUND, codecs.lookup('AAA 8')) - self.assertEqual(FOUND, codecs.lookup('aaa\xe9\u20ac-8')) - self.assertEqual(NOT_FOUND, codecs.lookup('AAA.8')) - self.assertEqual(NOT_FOUND, codecs.lookup('AAA...8')) - self.assertEqual(NOT_FOUND, codecs.lookup('BBB-8')) - self.assertEqual(NOT_FOUND, codecs.lookup('BBB.8')) - self.assertEqual(NOT_FOUND, codecs.lookup('a\xe9\u20ac-8')) + self.assertEqual(codecs.lookup('test.aaa_8'), ('test.aaa_8', 2, 3, 4)) + self.assertEqual(codecs.lookup('TEST.AAA-8'), ('test.aaa-8', 2, 3, 4)) + self.assertEqual(codecs.lookup('TEST.AAA 8'), ('test.aaa-8', 2, 3, 4)) + self.assertEqual(codecs.lookup('TEST.AAA---8'), ('test.aaa---8', 2, 3, 4)) + self.assertEqual(codecs.lookup('TEST.AAA 8'), ('test.aaa---8', 2, 3, 4)) + self.assertEqual(codecs.lookup('TEST.AAA.8'), ('test.aaa.8', 2, 3, 4)) + self.assertEqual(codecs.lookup('TEST.AAA...8'), ('test.aaa...8', 2, 3, 4)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(codecs.lookup('TEST.AAA\xe9\u20ac-8'), ('test.aaa\xe9\u20ac-8', 2, 3, 4)) def test_encodings_normalize_encoding(self): - # encodings.normalize_encoding() ignores non-ASCII characters. normalize = encodings.normalize_encoding self.assertEqual(normalize('utf_8'), 'utf_8') - self.assertEqual(normalize('utf\xE9\u20AC\U0010ffff-8'), 'utf_8') self.assertEqual(normalize('utf 8'), 'utf_8') # encodings.normalize_encoding() doesn't convert # characters to lower case. @@ -3906,6 +3913,22 @@ def test_encodings_normalize_encoding(self): self.assertEqual(normalize('utf.8'), 'utf.8') self.assertEqual(normalize('utf...8'), 'utf...8') + # Non-ASCII *encoding* is deprecated. + msg = "Support for non-ascii encoding names will be removed in 3.17" + with warnings_helper.check_warnings((msg, DeprecationWarning)): + self.assertEqual(normalize('utf\xE9\u20AC\U0010ffff-8'), 'utf_8') + + +class CodecCacheTest(unittest.TestCase): + def test_cache_bounded(self): + for i in range(encodings._MAXCACHE + 1000): + try: + b'x'.decode(f'nonexist_{i}') + except LookupError: + pass + + self.assertLessEqual(len(encodings._cache), encodings._MAXCACHE) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index d9d61e5c2053e3..b1b2dd2ca5ca0d 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -12,6 +12,7 @@ import string import sys from test import support +from test.support.import_helper import import_fresh_module import types import unittest @@ -736,7 +737,7 @@ def validate_abstract_methods(self, abc, *names): stubs = methodstubs.copy() del stubs[name] C = type('C', (abc,), stubs) - self.assertRaises(TypeError, C, name) + self.assertRaises(TypeError, C) def validate_isinstance(self, abc, name): stub = lambda s, *args: 0 @@ -963,7 +964,7 @@ class AnextOnly: async def __anext__(self): raise StopAsyncIteration self.assertNotIsInstance(AnextOnly(), AsyncIterator) - self.validate_abstract_methods(AsyncIterator, '__anext__', '__aiter__') + self.validate_abstract_methods(AsyncIterator, '__anext__') def test_Iterable(self): # Check some non-iterables @@ -1159,7 +1160,7 @@ def test_Iterator(self): for x in samples: self.assertIsInstance(x, Iterator) self.assertIsSubclass(type(x), Iterator) - self.validate_abstract_methods(Iterator, '__next__', '__iter__') + self.validate_abstract_methods(Iterator, '__next__') # Issue 10565 class NextOnly: @@ -1843,8 +1844,7 @@ def test_Mapping(self): for sample in [dict]: self.assertIsInstance(sample(), Mapping) self.assertIsSubclass(sample, Mapping) - self.validate_abstract_methods(Mapping, '__contains__', '__iter__', '__len__', - '__getitem__') + self.validate_abstract_methods(Mapping, '__iter__', '__len__', '__getitem__') class MyMapping(Mapping): def __len__(self): return 0 @@ -1859,7 +1859,7 @@ def test_MutableMapping(self): for sample in [dict]: self.assertIsInstance(sample(), MutableMapping) self.assertIsSubclass(sample, MutableMapping) - self.validate_abstract_methods(MutableMapping, '__contains__', '__iter__', '__len__', + self.validate_abstract_methods(MutableMapping, '__iter__', '__len__', '__getitem__', '__setitem__', '__delitem__') def test_MutableMapping_subclass(self): @@ -1898,8 +1898,7 @@ def test_Sequence(self): self.assertIsInstance(memoryview(b""), Sequence) self.assertIsSubclass(memoryview, Sequence) self.assertIsSubclass(str, Sequence) - self.validate_abstract_methods(Sequence, '__contains__', '__iter__', '__len__', - '__getitem__') + self.validate_abstract_methods(Sequence, '__len__', '__getitem__') def test_Sequence_mixins(self): class SequenceSubclass(Sequence): @@ -1936,6 +1935,44 @@ def assert_index_same(seq1, seq2, index_args): assert_index_same( nativeseq, seqseq, (letter, start, stop)) + def test_ByteString(self): + previous_sys_modules = sys.modules.copy() + self.addCleanup(sys.modules.update, previous_sys_modules) + + for module in "collections", "_collections_abc", "collections.abc": + sys.modules.pop(module, None) + + with self.assertWarns(DeprecationWarning): + from collections.abc import ByteString + for sample in [bytes, bytearray]: + with self.assertWarns(DeprecationWarning): + self.assertIsInstance(sample(), ByteString) + self.assertTrue(issubclass(sample, ByteString)) + for sample in [str, list, tuple]: + with self.assertWarns(DeprecationWarning): + self.assertNotIsInstance(sample(), ByteString) + self.assertFalse(issubclass(sample, ByteString)) + with self.assertWarns(DeprecationWarning): + self.assertNotIsInstance(memoryview(b""), ByteString) + self.assertFalse(issubclass(memoryview, ByteString)) + with self.assertWarns(DeprecationWarning): + self.validate_abstract_methods(ByteString, '__getitem__', '__len__') + + with self.assertWarns(DeprecationWarning): + class X(ByteString): pass + + with self.assertWarns(DeprecationWarning): + # No metaclass conflict + class Z(ByteString, Awaitable): pass + + def test_ByteString_attribute_access(self): + collections_abc = import_fresh_module( + "collections.abc", + fresh=("collections", "_collections_abc") + ) + with self.assertWarns(DeprecationWarning): + collections_abc.ByteString + def test_Buffer(self): for sample in [bytes, bytearray, memoryview]: self.assertIsInstance(sample(b"x"), Buffer) @@ -1954,8 +1991,8 @@ def test_MutableSequence(self): self.assertIsSubclass(sample, MutableSequence) self.assertIsSubclass(array.array, MutableSequence) self.assertNotIsSubclass(str, MutableSequence) - self.validate_abstract_methods(MutableSequence, '__contains__', '__iter__', - '__len__', '__getitem__', '__setitem__', '__delitem__', 'insert') + self.validate_abstract_methods(MutableSequence, '__len__', '__getitem__', + '__setitem__', '__delitem__', 'insert') def test_MutableSequence_mixins(self): # Test the mixins of MutableSequence by creating a minimal concrete @@ -2098,6 +2135,19 @@ def test_basics(self): self.assertEqual(c.setdefault('e', 5), 5) self.assertEqual(c['e'], 5) + def test_update_reentrant_add_clears_counter(self): + c = Counter() + key = object() + + class Evil(int): + def __add__(self, other): + c.clear() + return NotImplemented + + c[key] = Evil() + c.update([key]) + self.assertEqual(c[key], 1) + def test_init(self): self.assertEqual(list(Counter(self=42).items()), [('self', 42)]) self.assertEqual(list(Counter(iterable=42).items()), [('iterable', 42)]) @@ -2142,6 +2192,7 @@ def correctly_ordered(seq): self.assertTrue(correctly_ordered(p - q)) self.assertTrue(correctly_ordered(p | q)) self.assertTrue(correctly_ordered(p & q)) + self.assertTrue(correctly_ordered(p ^ q)) p, q = Counter(ps), Counter(qs) p += q @@ -2159,6 +2210,10 @@ def correctly_ordered(seq): p &= q self.assertTrue(correctly_ordered(p)) + p, q = Counter(ps), Counter(qs) + p ^= q + self.assertTrue(correctly_ordered(p)) + p, q = Counter(ps), Counter(qs) p.update(q) self.assertTrue(correctly_ordered(p)) @@ -2241,6 +2296,7 @@ def test_multiset_operations(self): (Counter.__sub__, lambda x, y: max(0, x-y)), (Counter.__or__, lambda x, y: max(0,x,y)), (Counter.__and__, lambda x, y: max(0, min(x,y))), + (Counter.__xor__, lambda x, y: max(0, max(x,y) - min(x,y))), ]: result = counterop(p, q) for x in elements: @@ -2258,6 +2314,7 @@ def test_multiset_operations(self): (Counter.__sub__, set.__sub__), (Counter.__or__, set.__or__), (Counter.__and__, set.__and__), + (Counter.__xor__, set.__xor__), ]: counter_result = counterop(p, q) set_result = setop(set(p.elements()), set(q.elements())) @@ -2276,6 +2333,7 @@ def test_inplace_operations(self): (Counter.__isub__, Counter.__sub__), (Counter.__ior__, Counter.__or__), (Counter.__iand__, Counter.__and__), + (Counter.__ixor__, Counter.__xor__), ]: c = p.copy() c_id = id(c) @@ -2351,6 +2409,7 @@ def test_multiset_operations_equivalent_to_set_operations(self): self.assertEqual(set(cp - cq), sp - sq) self.assertEqual(set(cp | cq), sp | sq) self.assertEqual(set(cp & cq), sp & sq) + self.assertEqual(set(cp ^ cq), sp ^ sq) self.assertEqual(cp == cq, sp == sq) self.assertEqual(cp != cq, sp != sq) self.assertEqual(cp <= cq, sp <= sq) @@ -2364,6 +2423,8 @@ def test_eq(self): def test_le(self): self.assertTrue(Counter(a=3, b=2, c=0) <= Counter('ababa')) + self.assertTrue(Counter() <= Counter(c=1)) + self.assertFalse(Counter() <= Counter(c=-1)) self.assertFalse(Counter(a=3, b=2) <= Counter('babab')) def test_lt(self): @@ -2372,12 +2433,48 @@ def test_lt(self): def test_ge(self): self.assertTrue(Counter(a=2, b=1, c=0) >= Counter('aab')) + self.assertTrue(Counter() >= Counter(c=-1)) + self.assertFalse(Counter() >= Counter(c=1)) self.assertFalse(Counter(a=3, b=2, c=0) >= Counter('aabd')) def test_gt(self): self.assertTrue(Counter(a=3, b=2, c=0) > Counter('aab')) self.assertFalse(Counter(a=2, b=1, c=0) > Counter('aab')) + def test_symmetric_difference(self): + population = (-4, -3, -2, -1, 0, 1, 2, 3, 4) + + for a, b1, b2, c in product(population, repeat=4): + p = Counter(a=a, b=b1) + q = Counter(b=b2, c=c) + r = p ^ q + + # Elementwise invariants + for k in ('a', 'b', 'c'): + self.assertEqual(r[k], max(p[k], q[k]) - min(p[k], q[k])) + self.assertEqual(r[k], abs(p[k] - q[k])) + + # Invariant for all positive, negative, and zero counts + self.assertEqual(r, (p - q) | (q - p)) + + # Invariant for non-negative counts + if a >= 0 and b1 >= 0 and b2 >= 0 and c >= 0: + self.assertEqual(r, (p | q) - (p & q)) + + # Zeros and negatives eliminated + self.assertTrue(all(value > 0 for value in r.values())) + + # Output preserves input order: p first and then q + keys = list(p) + list(q) + indices = [keys.index(k) for k in r] + self.assertEqual(indices, sorted(indices)) + + # Inplace operation matches binary operation + pp = Counter(p) + qq = Counter(q) + pp ^= qq + self.assertEqual(pp, r) + def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite(collections)) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 57e5f29b015637..9edbca3c383b43 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -249,8 +249,8 @@ def test_32_63_bit_values(self): d = -281474976710656 # 1 << 48 e = +4611686018427387904 # 1 << 62 f = -4611686018427387904 # 1 << 62 - g = +9223372036854775807 # 1 << 63 - 1 - h = -9223372036854775807 # 1 << 63 - 1 + g = +9223372036854775807 # (1 << 63) - 1 + h = -9223372036854775807 # (1 << 63) - 1 for variable in self.test_32_63_bit_values.__code__.co_consts: if variable is not None: @@ -651,6 +651,21 @@ def test_compile_filename(self): compile('pass', filename, 'exec') self.assertRaises(TypeError, compile, 'pass', list(b'file.py'), 'exec') + def test_compile_filename_refleak(self): + # Regression tests for reference leak in PyUnicode_FSDecoder. + # See https://github.com/python/cpython/issues/139748. + mortal_str = 'this is a mortal string' + # check error path when 'mode' AC conversion failed + self.assertRaises(TypeError, compile, b'', mortal_str, mode=1234) + # check error path when 'optimize' AC conversion failed + self.assertRaises(OverflowError, compile, b'', mortal_str, + 'exec', optimize=1 << 1000) + # check error path when 'dont_inherit' AC conversion failed + class EvilBool: + def __bool__(self): raise ValueError + self.assertRaises(ValueError, compile, b'', mortal_str, + 'exec', dont_inherit=EvilBool()) + @support.cpython_only def test_same_filename_used(self): s = """def f(): pass\ndef g(): pass""" @@ -713,7 +728,8 @@ def test_yet_more_evil_still_undecodable(self): def test_compiler_recursion_limit(self): # Compiler frames are small limit = 100 - crash_depth = limit * 5000 + # Android test devices have less memory. + crash_depth = limit * (1000 if sys.platform == "android" else 5000) success_depth = limit def check_limit(prefix, repeated, mode="single"): @@ -823,8 +839,10 @@ def f(): else: return "unused" - self.assertEqual(f.__code__.co_consts, - (f.__doc__, "used")) + if f.__doc__ is None: + self.assertEqual(f.__code__.co_consts, (True, "used")) + else: + self.assertEqual(f.__code__.co_consts, (f.__doc__, "used")) @support.cpython_only def test_remove_unused_consts_no_docstring(self): @@ -869,7 +887,11 @@ def test_strip_unused_None(self): def f1(): "docstring" return 42 - self.assertEqual(f1.__code__.co_consts, (f1.__doc__,)) + + if f1.__doc__ is None: + self.assertEqual(f1.__code__.co_consts, (42,)) + else: + self.assertEqual(f1.__code__.co_consts, (f1.__doc__,)) # This is a regression test for a CPython specific peephole optimizer # implementation bug present in a few releases. It's assertion verifies @@ -1015,11 +1037,13 @@ def test_path_like_objects(self): # An implicit test for PyUnicode_FSDecoder(). compile("42", FakePath("test_compile_pathlike"), "single") + # bpo-31113: Stack overflow when compile a long sequence of + # complex statements. @support.requires_resource('cpu') def test_stack_overflow(self): - # bpo-31113: Stack overflow when compile a long sequence of - # complex statements. - compile("if a: b\n" * 200000, "", "exec") + # Android test devices have less memory. + size = 100_000 if sys.platform == "android" else 200_000 + compile("if a: b\n" * size, "", "exec") # Multiple users rely on the fact that CPython does not generate # bytecode for dead code blocks. See bpo-37500 for more context. @@ -1274,7 +1298,7 @@ def return_genexp(): x in y) - genexp_lines = [0, 4, 2, 0, 4] + genexp_lines = [4, 0, 4, 2, 0, 4] genexp_code = return_genexp.__code__.co_consts[0] code_lines = self.get_code_lines(genexp_code) @@ -1609,6 +1633,17 @@ def test_remove_redundant_nop_edge_case(self): def f(): a if (1 if b else c) else d + def test_lineno_propagation_empty_blocks(self): + # Smoke test. See gh-138714. + def f(): + while name: + try: + break + except: + pass + else: + 1 if 1 else 1 + def test_global_declaration_in_except_used_in_else(self): # See gh-111123 code = textwrap.dedent("""\ @@ -1647,22 +1682,21 @@ class WeirdDict(dict): self.assertRaises(NameError, ns['foo']) def test_compile_warnings(self): - # See gh-131927 - # Compile warnings originating from the same file and - # line are now only emitted once. + # Each invocation of compile() emits compiler warnings, even if they + # have the same message and line number. + source = textwrap.dedent(r""" + # tokenizer + 1or 0 # line 3 + # code generator + 1 is 1 # line 5 + """) with warnings.catch_warnings(record=True) as caught: warnings.simplefilter("default") - compile('1 is 1', '', 'eval') - compile('1 is 1', '', 'eval') - - self.assertEqual(len(caught), 1) + for i in range(2): + # Even if compile() is at the same line. + compile(source, '', 'exec') - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always") - compile('1 is 1', '', 'eval') - compile('1 is 1', '', 'eval') - - self.assertEqual(len(caught), 2) + self.assertEqual([wm.lineno for wm in caught], [3, 5] * 2) def test_compile_warning_in_finally(self): # Ensure that warnings inside finally blocks are @@ -1673,16 +1707,131 @@ def test_compile_warning_in_finally(self): try: pass finally: - 1 is 1 + 1 is 1 # line 5 + try: + pass + finally: # nested + 1 is 1 # line 9 """) with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("default") + warnings.simplefilter("always") + compile(source, '', 'exec') + + self.assertEqual(sorted(wm.lineno for wm in caught), [5, 9]) + for wm in caught: + self.assertEqual(wm.category, SyntaxWarning) + self.assertIn("\"is\" with 'int' literal", str(wm.message)) + + # Other code path is used for "try" with "except*". + source = textwrap.dedent(""" + try: + pass + except *Exception: + pass + finally: + 1 is 1 # line 7 + try: + pass + except *Exception: + pass + finally: # nested + 1 is 1 # line 13 + """) + + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") compile(source, '', 'exec') - self.assertEqual(len(caught), 1) - self.assertEqual(caught[0].category, SyntaxWarning) - self.assertIn("\"is\" with 'int' literal", str(caught[0].message)) + self.assertEqual(sorted(wm.lineno for wm in caught), [7, 13]) + for wm in caught: + self.assertEqual(wm.category, SyntaxWarning) + self.assertIn("\"is\" with 'int' literal", str(wm.message)) + + def test_filter_syntax_warnings_by_module(self): + filename = support.findfile('test_import/data/syntax_warnings.py') + with open(filename, 'rb') as f: + source = f.read() + module_re = r'test\.test_import\.data\.syntax_warnings\z' + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=module_re) + compile(source, filename, 'exec') + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21]) + for wm in wlog: + self.assertEqual(wm.filename, filename) + self.assertIs(wm.category, SyntaxWarning) + + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=r'package\.module\z') + warnings.filterwarnings('error', module=module_re) + compile(source, filename, 'exec', module='package.module') + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21]) + for wm in wlog: + self.assertEqual(wm.filename, filename) + self.assertIs(wm.category, SyntaxWarning) + + @support.subTests('src', [ + textwrap.dedent(""" + def f(): + try: + pass + finally: + return 42 + """), + textwrap.dedent(""" + for x in y: + try: + pass + finally: + break + """), + textwrap.dedent(""" + for x in y: + try: + pass + finally: + continue + """), + ]) + def test_pep_765_warnings(self, src): + with self.assertWarnsRegex(SyntaxWarning, 'finally'): + compile(src, '', 'exec') + with warnings.catch_warnings(): + warnings.simplefilter("error") + tree = ast.parse(src) + with self.assertWarnsRegex(SyntaxWarning, 'finally'): + compile(tree, '', 'exec') + + @support.subTests('src', [ + textwrap.dedent(""" + try: + pass + finally: + def f(): + return 42 + """), + textwrap.dedent(""" + try: + pass + finally: + for x in y: + break + """), + textwrap.dedent(""" + try: + pass + finally: + for x in y: + continue + """), + ]) + def test_pep_765_no_warnings(self, src): + with warnings.catch_warnings(): + warnings.simplefilter("error") + compile(src, '', 'exec') + class TestBooleanExpression(unittest.TestCase): class Value: @@ -1723,6 +1872,21 @@ def test_compound(self): self.assertIs(res, v[3]) self.assertEqual([e.called for e in v], [1, 1, 0, 1, 0]) + def test_exception(self): + # See gh-137288 + class Foo: + def __bool__(self): + raise NotImplementedError() + + a = Foo() + b = Foo() + + with self.assertRaises(NotImplementedError): + bool(a) + + with self.assertRaises(NotImplementedError): + c = a or b + @requires_debug_ranges() class TestSourcePositions(unittest.TestCase): # Ensure that compiled code snippets have correct line and column numbers @@ -2294,8 +2458,8 @@ def test_lambda_return_position(self): for i, pos in enumerate(positions): with self.subTest(i=i, pos=pos): start_line, end_line, start_col, end_col = pos - if i == 0 and start_col == end_col == 0: - # ignore the RESUME in the beginning + if i <= 1: + # ignore the RESUME and CACHE in the beginning continue self.assertEqual(start_line, 1) self.assertEqual(end_line, 1) @@ -2321,12 +2485,13 @@ def f(): start_line, end_line, _, _ = instr.positions self.assertEqual(start_line, end_line) - # Expect four `LOAD_CONST None` instructions: + # Expect four `None`-loading instructions: # three for the no-exception __exit__ call, and one for the return. # They should all have the locations of the context manager ('xyz'). load_none = [instr for instr in dis.get_instructions(f) if - instr.opname == 'LOAD_CONST' and instr.argval is None] + instr.opname in ('LOAD_CONST', 'LOAD_COMMON_CONSTANT') + and instr.argval is None] return_value = [instr for instr in dis.get_instructions(f) if instr.opname == 'RETURN_VALUE'] diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 8384c183dd92dd..b2150b621516ba 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -33,6 +33,10 @@ from test.support.os_helper import FakePath +if sys.implementation.cache_tag is None: + raise unittest.SkipTest('requires sys.implementation.cache_tag is not None') + + def get_pyc(script, opt): if not opt: # Replace None and 0 with '' diff --git a/Lib/test/test_compiler_codegen.py b/Lib/test/test_compiler_codegen.py index d02937c84d9534..36058854a41d63 100644 --- a/Lib/test/test_compiler_codegen.py +++ b/Lib/test/test_compiler_codegen.py @@ -161,3 +161,34 @@ def test_syntax_error__return_not_in_function(self): self.assertIsNone(cm.exception.text) self.assertEqual(cm.exception.offset, 1) self.assertEqual(cm.exception.end_offset, 10) + + def test_frozenset_optimization(self): + l1 = self.Label() + snippet = "frozenset({1, 2, 3})" + expected = [ + ('RESUME', 0), + ('ANNOTATIONS_PLACEHOLDER', None), + ('LOAD_NAME', 0), + ('COPY', 1), + ('LOAD_COMMON_CONSTANT', 12), + ('IS_OP', 0), + ('POP_JUMP_IF_FALSE', l1), + ('POP_TOP', None), + ('LOAD_CONST', 1), + ('LOAD_CONST', 2), + ('LOAD_CONST', 3), + ('BUILD_SET', 3), + ('CALL_INTRINSIC_1', 12), + ('JUMP', 0), + l1, + ('PUSH_NULL', None), + ('LOAD_CONST', 1), + ('LOAD_CONST', 2), + ('LOAD_CONST', 3), + ('BUILD_SET', 3), + ('CALL', 1), + ('POP_TOP', None), + ('LOAD_CONST', 0), + ('RETURN_VALUE', None) + ] + self.codegen_test(snippet, expected) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 0c7e7341f13d4e..bb307191dffcc1 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -72,8 +72,8 @@ def assertAlmostEqual(self, a, b): else: unittest.TestCase.assertAlmostEqual(self, a, b) - def assertCloseAbs(self, x, y, eps=1e-9): - """Return true iff floats x and y "are close".""" + def assertClose(self, x, y, eps=1e-9): + """Return true iff complexes x and y "are close".""" # put the one with larger magnitude second if abs(x) > abs(y): x, y = y, x @@ -82,26 +82,15 @@ def assertCloseAbs(self, x, y, eps=1e-9): if x == 0: return abs(y) < eps # check that relative difference < eps - self.assertTrue(abs((x-y)/y) < eps) - - def assertClose(self, x, y, eps=1e-9): - """Return true iff complexes x and y "are close".""" - self.assertCloseAbs(x.real, y.real, eps) - self.assertCloseAbs(x.imag, y.imag, eps) + self.assertTrue(abs(x-y)/abs(y) < eps) def check_div(self, x, y): """Compute complex z=x*y, and check that z/x==y and z/y==x.""" z = x * y - if x != 0: - q = z / x - self.assertClose(q, y) - q = z.__truediv__(x) - self.assertClose(q, y) - if y != 0: - q = z / y - self.assertClose(q, x) - q = z.__truediv__(y) - self.assertClose(q, x) + if x: + self.assertClose(z / x, y) + if y: + self.assertClose(z / y, x) def test_truediv(self): simple_real = [float(i) for i in range(-5, 6)] @@ -115,10 +104,20 @@ def test_truediv(self): self.check_div(complex(1e200, 1e200), 1+0j) self.check_div(complex(1e-200, 1e-200), 1+0j) + # Smith's algorithm has several sources of inaccuracy + # for components of the result. In examples below, + # it's cancellation of digits in computation of sum. + self.check_div(1e-09+1j, 1+1j) + self.check_div(8.289760544677449e-09+0.13257307440728516j, + 0.9059966714925808+0.5054864708672686j) + # Just for fun. for i in range(100): - self.check_div(complex(random(), random()), - complex(random(), random())) + x = complex(random(), random()) + y = complex(random(), random()) + self.check_div(x, y) + y = complex(1e10*y.real, y.imag) + self.check_div(x, y) self.assertAlmostEqual(complex.__truediv__(2+0j, 1+1j), 1-1j) self.assertRaises(TypeError, operator.truediv, 1j, None) @@ -454,7 +453,7 @@ def test_boolcontext(self): self.assertTrue(1j) def test_conjugate(self): - self.assertClose(complex(5.3, 9.8).conjugate(), 5.3-9.8j) + self.assertEqual(complex(5.3, 9.8).conjugate(), 5.3-9.8j) def test_constructor(self): def check(z, x, y): @@ -505,17 +504,25 @@ def check(z, x, y): with self.assertWarnsRegex(DeprecationWarning, "argument 'imag' must be a real number, not complex"): check(complex(0.0, 4.25j), -4.25, 0.0) - with self.assertWarnsRegex(DeprecationWarning, - "argument 'real' must be a real number, not complex"): + with (self.assertWarnsRegex(DeprecationWarning, + "argument 'real' must be a real number, not complex"), + self.assertWarnsRegex(DeprecationWarning, + "argument 'imag' must be a real number, not complex")): check(complex(4.25+0j, 0j), 4.25, 0.0) - with self.assertWarnsRegex(DeprecationWarning, - "argument 'real' must be a real number, not complex"): + with (self.assertWarnsRegex(DeprecationWarning, + "argument 'real' must be a real number, not complex"), + self.assertWarnsRegex(DeprecationWarning, + "argument 'imag' must be a real number, not complex")): check(complex(4.25j, 0j), 0.0, 4.25) - with self.assertWarnsRegex(DeprecationWarning, - "argument 'real' must be a real number, not complex"): + with (self.assertWarnsRegex(DeprecationWarning, + "argument 'real' must be a real number, not complex"), + self.assertWarnsRegex(DeprecationWarning, + "argument 'imag' must be a real number, not complex")): check(complex(0j, 4.25+0j), 0.0, 4.25) - with self.assertWarnsRegex(DeprecationWarning, - "argument 'real' must be a real number, not complex"): + with (self.assertWarnsRegex(DeprecationWarning, + "argument 'real' must be a real number, not complex"), + self.assertWarnsRegex(DeprecationWarning, + "argument 'imag' must be a real number, not complex")): check(complex(0j, 4.25j), -4.25, 0.0) check(complex(real=4.25), 4.25, 0.0) diff --git a/Lib/test/test_concurrent_futures/executor.py b/Lib/test/test_concurrent_futures/executor.py index 95bf8fcd25bf54..a37c4d45f07b17 100644 --- a/Lib/test/test_concurrent_futures/executor.py +++ b/Lib/test/test_concurrent_futures/executor.py @@ -5,7 +5,7 @@ from concurrent import futures from operator import add from test import support -from test.support import Py_GIL_DISABLED +from test.support import Py_GIL_DISABLED, warnings_helper def mul(x, y): @@ -43,10 +43,12 @@ class ExecutorTest: # Executor.shutdown() and context manager usage is tested by # ExecutorShutdownTest. + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_submit(self): future = self.executor.submit(pow, 2, 8) self.assertEqual(256, future.result()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_submit_keyword(self): future = self.executor.submit(mul, 2, y=8) self.assertEqual(16, future.result()) @@ -57,6 +59,7 @@ def test_submit_keyword(self): with self.assertRaises(TypeError): self.executor.submit(arg=1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map(self): self.assertEqual( list(self.executor.map(pow, range(10), range(10))), @@ -66,6 +69,7 @@ def test_map(self): list(self.executor.map(pow, range(10), range(10), chunksize=3)), list(map(pow, range(10), range(10)))) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_exception(self): i = self.executor.map(divmod, [1, 1, 1, 1], [2, 3, 0, 5]) self.assertEqual(i.__next__(), (0, 1)) @@ -73,6 +77,7 @@ def test_map_exception(self): with self.assertRaises(ZeroDivisionError): i.__next__() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_resource('walltime') def test_map_timeout(self): results = [] @@ -108,6 +113,7 @@ def test_map_buffersize_value_validation(self): ): self.executor.map(str, range(4), buffersize=buffersize) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_buffersize(self): ints = range(4) for buffersize in (1, 2, len(ints), len(ints) * 2): @@ -115,6 +121,7 @@ def test_map_buffersize(self): res = self.executor.map(str, ints, buffersize=buffersize) self.assertListEqual(list(res), ["0", "1", "2", "3"]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_buffersize_on_multiple_iterables(self): ints = range(4) for buffersize in (1, 2, len(ints), len(ints) * 2): @@ -122,12 +129,14 @@ def test_map_buffersize_on_multiple_iterables(self): res = self.executor.map(add, ints, ints, buffersize=buffersize) self.assertListEqual(list(res), [0, 2, 4, 6]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_buffersize_on_infinite_iterable(self): res = self.executor.map(str, itertools.count(), buffersize=2) self.assertEqual(next(res, None), "0") self.assertEqual(next(res, None), "1") self.assertEqual(next(res, None), "2") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_buffersize_on_multiple_infinite_iterables(self): res = self.executor.map( add, @@ -147,6 +156,7 @@ def test_map_buffersize_without_iterable(self): res = self.executor.map(str, buffersize=2) self.assertIsNone(next(res, None)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_buffersize_when_buffer_is_full(self): ints = iter(range(4)) buffersize = 2 @@ -158,6 +168,7 @@ def test_map_buffersize_when_buffer_is_full(self): msg="should have fetched only `buffersize` elements from `ints`.", ) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shutdown_race_issue12456(self): # Issue #12456: race condition at shutdown where trying to post a # sentinel in the call queue blocks (the queue is full while processes @@ -165,6 +176,7 @@ def test_shutdown_race_issue12456(self): self.executor.map(str, [2] * (self.worker_count + 1)) self.executor.shutdown() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.cpython_only def test_no_stale_references(self): # Issue #16284: check that the executors don't unnecessarily hang onto @@ -209,6 +221,7 @@ def test_max_workers_negative(self): "than 0"): self.executor_type(max_workers=number) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_free_reference(self): # Issue #14406: Result iterator should not keep an internal # reference to result objects. @@ -221,6 +234,7 @@ def test_free_reference(self): if wr() is None: break + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_swallows_falsey_exceptions(self): # see gh-132063: Prevent exceptions that evaluate as falsey # from being ignored. diff --git a/Lib/test/test_concurrent_futures/test_as_completed.py b/Lib/test/test_concurrent_futures/test_as_completed.py index c90b0021d85fc7..31c7bb3ebd872c 100644 --- a/Lib/test/test_concurrent_futures/test_as_completed.py +++ b/Lib/test/test_concurrent_futures/test_as_completed.py @@ -7,6 +7,7 @@ CANCELLED_AND_NOTIFIED, FINISHED, Future) from test import support +from test.support import warnings_helper from .util import ( PENDING_FUTURE, RUNNING_FUTURE, @@ -19,6 +20,7 @@ def mul(x, y): class AsCompletedTests: + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_no_timeout(self): future1 = self.executor.submit(mul, 2, 21) future2 = self.executor.submit(mul, 7, 6) @@ -35,6 +37,7 @@ def test_no_timeout(self): future1, future2]), completed) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_future_times_out(self): """Test ``futures.as_completed`` timing out before completing it's final future.""" @@ -62,6 +65,7 @@ def test_future_times_out(self): # Check that ``future`` wasn't completed. self.assertEqual(completed_futures, already_completed) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_duplicate_futures(self): # Issue 20367. Duplicate futures should not raise exceptions or give # duplicate responses. diff --git a/Lib/test/test_concurrent_futures/test_deadlock.py b/Lib/test/test_concurrent_futures/test_deadlock.py index a465400509d411..5cd84f7e43043c 100644 --- a/Lib/test/test_concurrent_futures/test_deadlock.py +++ b/Lib/test/test_concurrent_futures/test_deadlock.py @@ -10,6 +10,7 @@ from concurrent.futures.process import BrokenProcessPool, _ThreadWakeup from test import support +from test.support import warnings_helper from .util import ( create_executor_tests, setup_module, @@ -111,6 +112,7 @@ def _fail_on_deadlock(self, executor): print(f"\nTraceback:\n {tb}", file=sys.__stderr__) self.fail(f"Executor deadlock:\n\n{tb}") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def _check_error(self, error, func, *args, ignore_stderr=False): # test for deadlock caused by crashes or exiting in a pool self.executor.shutdown(wait=True) @@ -199,6 +201,7 @@ def test_exit_during_result_unpickle_in_result_handler(self): # the result_handler thread self._check_error(BrokenProcessPool, _return_instance, ExitAtUnpickle) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.skip_if_sanitizer("UBSan: explicit SIGSEV not allowed", ub=True) def test_shutdown_deadlock(self): # Test that the pool calling shutdown do not cause deadlock @@ -212,6 +215,7 @@ def test_shutdown_deadlock(self): with self.assertRaises(BrokenProcessPool): f.result() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shutdown_deadlock_pickle(self): # Test that the pool calling shutdown with wait=False does not cause # a deadlock if a task fails at pickle after the shutdown call. @@ -238,6 +242,7 @@ def test_shutdown_deadlock_pickle(self): # dangling threads executor_manager.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.skip_if_sanitizer("UBSan: explicit SIGSEV not allowed", ub=True) def test_crash_big_data(self): # Test that there is a clean exception instead of a deadlock when a @@ -254,6 +259,7 @@ def test_crash_big_data(self): executor.shutdown(wait=True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_gh105829_should_not_deadlock_if_wakeup_pipe_full(self): # Issue #105829: The _ExecutorManagerThread wakeup pipe could # fill up and block. See: https://github.com/python/cpython/issues/105829 diff --git a/Lib/test/test_concurrent_futures/test_init.py b/Lib/test/test_concurrent_futures/test_init.py index 6b8484c0d5f197..ca612db17ce802 100644 --- a/Lib/test/test_concurrent_futures/test_init.py +++ b/Lib/test/test_concurrent_futures/test_init.py @@ -11,6 +11,7 @@ from logging.handlers import QueueHandler from test import support +from test.support import warnings_helper from .util import ExecutorMixin, create_executor_tests, setup_module @@ -48,6 +49,7 @@ def setUp(self): initargs=('initialized',)) super().setUp() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_initializer(self): futures = [self.executor.submit(get_init_status) for _ in range(self.worker_count)] @@ -74,6 +76,7 @@ def setUp(self): self.executor_kwargs = dict(initializer=init_fail) super().setUp() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_initializer(self): with self._assert_logged('ValueError: error in initializer'): try: @@ -144,6 +147,8 @@ def test_spawn(self): self._test(ProcessPoolSpawnFailingInitializerTest) @support.skip_if_sanitizer("TSAN doesn't support threads after fork", thread=True) + @unittest.skipIf(sys.platform == "cygwin", + "Forkserver is not available on Cygwin") def test_forkserver(self): self._test(ProcessPoolForkserverFailingInitializerTest) diff --git a/Lib/test/test_concurrent_futures/test_process_pool.py b/Lib/test/test_concurrent_futures/test_process_pool.py index 3f13a1900a4ca4..45cfd46fb7befb 100644 --- a/Lib/test/test_concurrent_futures/test_process_pool.py +++ b/Lib/test/test_concurrent_futures/test_process_pool.py @@ -3,13 +3,14 @@ import sys import threading import time +import traceback import unittest import unittest.mock from concurrent import futures from concurrent.futures.process import BrokenProcessPool from test import support -from test.support import hashlib_helper +from test.support import hashlib_helper, warnings_helper from test.test_importlib.metadata.fixtures import parameterize from .executor import ExecutorTest, mul @@ -49,6 +50,7 @@ def test_max_workers_too_large(self): "max_workers must be <= 61"): futures.ProcessPoolExecutor(max_workers=62) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_killed_child(self): # When a child process is abruptly terminated, the whole pool gets # "broken". @@ -61,6 +63,33 @@ def test_killed_child(self): # Submitting other jobs fails as well. self.assertRaises(BrokenProcessPool, self.executor.submit, pow, 2, 8) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + def test_broken_process_pool_traceback(self): + # When a child process is abruptly terminated, the whole pool gets + # "broken", and a BrokenProcessPool exception should be created + # for each future instead of sharing one exception among all futures. + event = self.create_event() + futures = [self.executor.submit(event.wait) for _ in range(3)] + p = next(iter(self.executor._processes.values())) + p.terminate() + for fut in futures: + # Don't use assertRaises(): it clears the traceback off exc. + try: + fut.result() + except BrokenProcessPool as exc: + tb = exc.__traceback__ + else: + self.fail("BrokenProcessPool not raised") + count = sum( + 1 + for frame_summary in traceback.extract_tb(tb) + if frame_summary.filename == __file__ + ) + # This code file should appear exactly once in the traceback. + # A shared exception would accumulate a frame per result() call. + self.assertEqual(count, 1) + + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_chunksize(self): def bad_map(): list(self.executor.map(pow, range(40), range(40), chunksize=-1)) @@ -81,6 +110,7 @@ def bad_map(): def _test_traceback(cls): raise RuntimeError(123) # some comment + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_traceback(self): # We want ensure that the traceback from the child process is # contained in the traceback raised in the main process. @@ -103,6 +133,23 @@ def test_traceback(self): self.assertIn('raise RuntimeError(123) # some comment', f1.getvalue()) + def test_traceback_when_child_process_terminates_abruptly(self): + # gh-139462 enhancement - BrokenProcessPool exceptions + # should describe which process terminated. + exit_code = 99 + with self.executor_type(max_workers=1) as executor: + future = executor.submit(os._exit, exit_code) + with self.assertRaises(BrokenProcessPool) as bpe: + future.result() + + if sys.platform != 'cygwin': + cause = bpe.exception.__cause__ + self.assertIsInstance(cause, futures.process._RemoteTraceback) + self.assertIn( + f"terminated abruptly with exit code {exit_code}", cause.tb + ) + + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @hashlib_helper.requires_hashdigest('md5') def test_ressources_gced_in_workers(self): # Ensure that argument for a job are correctly gc-ed after the job @@ -123,6 +170,7 @@ def test_ressources_gced_in_workers(self): mgr.shutdown() mgr.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_saturation(self): executor = self.executor mp_context = self.get_context() @@ -208,6 +256,7 @@ def test_max_tasks_early_shutdown(self): for i, future in enumerate(futures): self.assertEqual(future.result(), mul(i, i)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_python_finalization_error(self): # gh-109047: Catch RuntimeError on thread creation # during Python finalization. @@ -258,6 +307,7 @@ def test_force_shutdown_workers_invalid_op(self): executor._force_shutdown, operation='invalid operation'), + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @parameterize(*FORCE_SHUTDOWN_PARAMS) def test_force_shutdown_workers(self, function_name): manager = self.get_context().Manager() diff --git a/Lib/test/test_concurrent_futures/test_shutdown.py b/Lib/test/test_concurrent_futures/test_shutdown.py index 99b315b47e2530..c576df1068ed32 100644 --- a/Lib/test/test_concurrent_futures/test_shutdown.py +++ b/Lib/test/test_concurrent_futures/test_shutdown.py @@ -6,6 +6,7 @@ from concurrent import futures from test import support +from test.support import warnings_helper from test.support.script_helper import assert_python_ok from .util import ( @@ -49,6 +50,7 @@ def test_interpreter_shutdown(self): self.assertFalse(err) self.assertEqual(out.strip(), b"apple") + @support.force_not_colorized def test_submit_after_interpreter_shutdown(self): # Test the atexit hook for shutdown of worker threads and processes rc, out, err = assert_python_ok('-c', """if 1: @@ -77,12 +79,14 @@ def run_last(): self.assertIn("RuntimeError: cannot schedule new futures", err.decode()) self.assertEqual(out.strip(), b"runtime-error") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_hang_issue12364(self): fs = [self.executor.submit(time.sleep, 0.1) for _ in range(50)] self.executor.shutdown() for f in fs: f.result() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_cancel_futures(self): assert self.worker_count <= 5, "test needs few workers" fs = [self.executor.submit(time.sleep, .1) for _ in range(50)] @@ -128,6 +132,7 @@ def test_hang_gh83386(self): self.assertFalse(err) self.assertEqual(out.strip(), b"apple") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_hang_gh94440(self): """shutdown(wait=True) doesn't hang when a future was submitted and quickly canceled right before shutdown. @@ -171,6 +176,7 @@ def acquire_lock(lock): for t in self.executor._threads: t.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_context_manager_shutdown(self): with futures.ThreadPoolExecutor(max_workers=5) as e: executor = e @@ -180,6 +186,7 @@ def test_context_manager_shutdown(self): for t in executor._threads: t.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_del_shutdown(self): executor = futures.ThreadPoolExecutor(max_workers=5) res = executor.map(abs, range(-5, 5)) @@ -193,6 +200,7 @@ def test_del_shutdown(self): # executor got shutdown. assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shutdown_no_wait(self): # Ensure that the executor cleans up the threads when calling # shutdown with wait=False @@ -207,7 +215,7 @@ def test_shutdown_no_wait(self): # executor got shutdown. assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_thread_names_assigned(self): executor = futures.ThreadPoolExecutor( max_workers=5, thread_name_prefix='SpecialPool') @@ -220,6 +228,7 @@ def test_thread_names_assigned(self): self.assertRegex(t.name, r'^SpecialPool_[0-4]$') t.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_thread_names_default(self): executor = futures.ThreadPoolExecutor(max_workers=5) executor.map(abs, range(-5, 5)) @@ -253,6 +262,7 @@ def test_cancel_futures_wait_false(self): class ProcessPoolShutdownTest(ExecutorShutdownTest): + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_processes_terminate(self): def acquire_lock(lock): lock.acquire() @@ -276,6 +286,7 @@ def acquire_lock(lock): for p in processes.values(): p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_context_manager_shutdown(self): with futures.ProcessPoolExecutor( max_workers=5, mp_context=self.get_context()) as e: @@ -286,6 +297,7 @@ def test_context_manager_shutdown(self): for p in processes.values(): p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_del_shutdown(self): executor = futures.ProcessPoolExecutor( max_workers=5, mp_context=self.get_context()) @@ -308,6 +320,7 @@ def test_del_shutdown(self): # executor got shutdown. assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shutdown_no_wait(self): # Ensure that the executor cleans up the processes when calling # shutdown with wait=False diff --git a/Lib/test/test_concurrent_futures/test_thread_pool.py b/Lib/test/test_concurrent_futures/test_thread_pool.py index 4324241b374967..226b7acb64fb29 100644 --- a/Lib/test/test_concurrent_futures/test_thread_pool.py +++ b/Lib/test/test_concurrent_futures/test_thread_pool.py @@ -7,6 +7,7 @@ import unittest from concurrent import futures from test import support +from test.support import warnings_helper from .executor import ExecutorTest, mul from .util import BaseTestCase, ThreadPoolMixin, setup_module @@ -53,6 +54,7 @@ def test_idle_thread_reuse(self): @support.requires_fork() @unittest.skipUnless(hasattr(os, 'register_at_fork'), 'need os.register_at_fork') @support.requires_resource('cpu') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_hang_global_shutdown_lock(self): # bpo-45021: _global_shutdown_lock should be reinitialized in the child # process, otherwise it will never exit @@ -68,6 +70,7 @@ def submit(pool): @support.requires_fork() @unittest.skipUnless(hasattr(os, 'register_at_fork'), 'need os.register_at_fork') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_process_fork_from_a_threadpool(self): # bpo-43944: clear concurrent.futures.thread._threads_queues after fork, # otherwise child process will try to join parent thread diff --git a/Lib/test/test_concurrent_futures/test_wait.py b/Lib/test/test_concurrent_futures/test_wait.py index cc387883141b0e..b8250cec7ab3e7 100644 --- a/Lib/test/test_concurrent_futures/test_wait.py +++ b/Lib/test/test_concurrent_futures/test_wait.py @@ -3,7 +3,7 @@ import unittest from concurrent import futures from test import support -from test.support import threading_helper +from test.support import threading_helper, warnings_helper from .util import ( CANCELLED_FUTURE, CANCELLED_AND_NOTIFIED_FUTURE, EXCEPTION_FUTURE, @@ -22,6 +22,7 @@ def wait_and_raise(e): class WaitTests: + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_20369(self): # See https://bugs.python.org/issue20369 future = self.executor.submit(mul, 1, 2) @@ -30,7 +31,7 @@ def test_20369(self): self.assertEqual({future}, done) self.assertEqual(set(), not_done) - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_first_completed(self): event = self.create_event() future1 = self.executor.submit(mul, 21, 2) @@ -47,6 +48,7 @@ def test_first_completed(self): event.set() future2.result() # wait for job to finish + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_first_completed_some_already_completed(self): event = self.create_event() future1 = self.executor.submit(event.wait) @@ -64,6 +66,7 @@ def test_first_completed_some_already_completed(self): event.set() future1.result() # wait for job to finish + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_first_exception(self): event1 = self.create_event() event2 = self.create_event() @@ -93,6 +96,7 @@ def wait_for_future1(): event2.set() future3.result() # wait for job to finish + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_first_exception_some_already_complete(self): event = self.create_event() future1 = self.executor.submit(divmod, 21, 0) @@ -114,6 +118,7 @@ def test_first_exception_some_already_complete(self): event.set() future2.result() # wait for job to finish + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_first_exception_one_already_failed(self): event = self.create_event() future1 = self.executor.submit(event.wait) @@ -129,6 +134,7 @@ def test_first_exception_one_already_failed(self): event.set() future1.result() # wait for job to finish + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_all_completed(self): future1 = self.executor.submit(divmod, 2, 0) future2 = self.executor.submit(mul, 2, 21) @@ -148,6 +154,7 @@ def test_all_completed(self): future2]), finished) self.assertEqual(set(), pending) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_timeout(self): short_timeout = 0.050 diff --git a/Lib/test/test_concurrent_futures/util.py b/Lib/test/test_concurrent_futures/util.py index b12940414d9142..006360c8d941c9 100644 --- a/Lib/test/test_concurrent_futures/util.py +++ b/Lib/test/test_concurrent_futures/util.py @@ -10,7 +10,7 @@ from concurrent.futures.process import _check_system_limits from test import support -from test.support import threading_helper +from test.support import threading_helper, warnings_helper def create_future(state=PENDING, exception=None, result=None): @@ -51,7 +51,8 @@ def setUp(self): max_workers=self.worker_count, mp_context=self.get_context(), **self.executor_kwargs) - self.manager = self.get_context().Manager() + with warnings_helper.ignore_fork_in_thread_deprecation_warnings(): + self.manager = self.get_context().Manager() else: self.executor = self.executor_type( max_workers=self.worker_count, @@ -134,7 +135,7 @@ def get_context(self): _check_system_limits() except NotImplementedError: self.skipTest("ProcessPoolExecutor unavailable on this system") - if sys.platform == "win32": + if sys.platform in ("win32", "cygwin"): self.skipTest("require unix system") if support.check_sanitizer(thread=True): self.skipTest("TSAN doesn't support threads after fork") diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index e7364e18742c16..8d8dd2a2bf27fb 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -1729,6 +1729,19 @@ def test_error(self): self.assertEqual(e1.message, e2.message) self.assertEqual(repr(e1), repr(e2)) + def test_combine_error_linear_complexity(self): + # Ensure that ParsingError.combine() has linear complexity. + # See https://github.com/python/cpython/issues/148370. + n = 50000 + s = '[*]\n' + (err_line := '=\n') * n + p = configparser.ConfigParser(strict=False) + with self.assertRaises(configparser.ParsingError) as cm: + p.read_string(s) + errlines = cm.exception.message.splitlines() + self.assertEqual(len(errlines), n + 1) + self.assertStartsWith(errlines[0], "Source contains parsing errors: ") + self.assertEqual(errlines[42], f"\t[line {43:2d}]: {err_line!r}") + def test_nosectionerror(self): import pickle e1 = configparser.NoSectionError('section') @@ -2215,6 +2228,16 @@ def test_add_section(self): cfg.add_section(configparser.UNNAMED_SECTION) cfg.set(configparser.UNNAMED_SECTION, 'a', '1') self.assertEqual('1', cfg[configparser.UNNAMED_SECTION]['a']) + output = io.StringIO() + cfg.write(output) + self.assertEqual(output.getvalue(), 'a = 1\n\n') + + cfg = configparser.ConfigParser(allow_unnamed_section=True) + cfg[configparser.UNNAMED_SECTION] = {'a': '1'} + self.assertEqual('1', cfg[configparser.UNNAMED_SECTION]['a']) + output = io.StringIO() + cfg.write(output) + self.assertEqual(output.getvalue(), 'a = 1\n\n') def test_disabled_error(self): with self.assertRaises(configparser.MissingSectionHeaderError): @@ -2223,6 +2246,9 @@ def test_disabled_error(self): with self.assertRaises(configparser.UnnamedSectionDisabledError): configparser.ConfigParser().add_section(configparser.UNNAMED_SECTION) + with self.assertRaises(configparser.UnnamedSectionDisabledError): + configparser.ConfigParser()[configparser.UNNAMED_SECTION] = {'a': '1'} + def test_multiple_configs(self): cfg = configparser.ConfigParser(allow_unnamed_section=True) cfg.read_string('a = 1') @@ -2257,6 +2283,26 @@ def test_section_bracket_in_key(self): output.close() +class ReDoSTestCase(unittest.TestCase): + """Regression tests for quadratic regex backtracking (gh-146333).""" + + def test_option_regex_does_not_backtrack(self): + # A line with many spaces between non-delimiter characters + # should be parsed in linear time, not quadratic. + parser = configparser.RawConfigParser() + content = "[section]\n" + "x" + " " * 40000 + "y" + "\n" + # This should complete almost instantly. Before the fix, + # it would take over a minute due to catastrophic backtracking. + with self.assertRaises(configparser.ParsingError): + parser.read_string(content) + + def test_option_regex_no_value_does_not_backtrack(self): + parser = configparser.RawConfigParser(allow_no_value=True) + content = "[section]\n" + "x" + " " * 40000 + "y" + "\n" + parser.read_string(content) + self.assertTrue(parser.has_option("section", "x" + " " * 40000 + "y")) + + class MiscTestCase(unittest.TestCase): def test__all__(self): support.check__all__(self, configparser, not_exported={"Error"}) diff --git a/Lib/test/test_context.py b/Lib/test/test_context.py index a08038b5dbd407..ef20495dcc01ea 100644 --- a/Lib/test/test_context.py +++ b/Lib/test/test_context.py @@ -556,6 +556,36 @@ def fun(): ctx.run(fun) + def test_context_eq_reentrant_contextvar_set(self): + var = contextvars.ContextVar("v") + ctx1 = contextvars.Context() + ctx2 = contextvars.Context() + + class ReentrantEq: + def __eq__(self, other): + ctx1.run(lambda: var.set(object())) + return True + + ctx1.run(var.set, ReentrantEq()) + ctx2.run(var.set, object()) + ctx1 == ctx2 + + def test_context_eq_reentrant_contextvar_set_in_hash(self): + var = contextvars.ContextVar("v") + ctx1 = contextvars.Context() + ctx2 = contextvars.Context() + + class ReentrantHash: + def __hash__(self): + ctx1.run(lambda: var.set(object())) + return 0 + def __eq__(self, other): + return isinstance(other, ReentrantHash) + + ctx1.run(var.set, ReentrantHash()) + ctx2.run(var.set, ReentrantHash()) + ctx1 == ctx2 + # HAMT Tests diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index 6a3329fa5aaace..e291f814edbd93 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -680,6 +680,154 @@ def test(x): self.assertEqual(state, [1, 'something else', 999]) + def test_contextmanager_decorate_generator_function(self): + @contextmanager + def woohoo(y): + state.append(y) + yield + state.append(999) + + state = [] + @woohoo(1) + def test(x): + self.assertEqual(state, [1]) + state.append(x) + yield + state.append("second item") + return "result" + + gen = test("something") + for _ in gen: + self.assertEqual(state, [1, "something"]) + self.assertEqual(state, [1, "something", "second item", 999]) + + # The wrapped generator's return value is preserved. + state = [] + gen = test("something") + with self.assertRaises(StopIteration) as cm: + while True: + next(gen) + self.assertEqual(cm.exception.value, "result") + + + def test_contextmanager_decorate_generator_function_exception(self): + @contextmanager + def woohoo(): + state.append("enter") + try: + yield + finally: + state.append("exit") + + state = [] + @woohoo() + def test(): + state.append("body") + yield + raise ZeroDivisionError + + with self.assertRaises(ZeroDivisionError): + for _ in test(): + pass + self.assertEqual(state, ["enter", "body", "exit"]) + + + def test_contextmanager_decorate_generator_function_early_stop(self): + @contextmanager + def woohoo(): + state.append("enter") + try: + yield + finally: + state.append("exit") + + state = [] + @woohoo() + def test(): + try: + yield 1 + yield 2 + finally: + state.append("inner closed") + + gen = test() + self.assertEqual(next(gen), 1) + gen.close() + # The inner generator is closed before the context manager exits. + self.assertEqual(state, ["enter", "inner closed", "exit"]) + + + def test_contextmanager_decorate_generator_function_send_throw(self): + @contextmanager + def woohoo(): + yield + + @woohoo() + def test(): + received = yield "first" + state.append(("received", received)) + try: + yield "second" + except ValueError as exc: + state.append(("caught", type(exc))) + yield "after throw" + + # .send() and .throw() are forwarded to the wrapped generator. + state = [] + gen = test() + self.assertEqual(next(gen), "first") + self.assertEqual(gen.send("VALUE"), "second") + self.assertEqual(gen.throw(ValueError), "after throw") + gen.close() + self.assertEqual( + state, [("received", "VALUE"), ("caught", ValueError)] + ) + + + def test_contextmanager_decorate_coroutine_function(self): + @contextmanager + def woohoo(y): + state.append(y) + yield + state.append(999) + + state = [] + @woohoo(1) + async def test(x): + self.assertEqual(state, [1]) + state.append(x) + + coro = test("something") + with self.assertRaises(StopIteration): + coro.send(None) + + self.assertEqual(state, [1, "something", 999]) + + + def test_contextmanager_decorate_asyncgen_function(self): + @contextmanager + def woohoo(y): + state.append(y) + yield + state.append(999) + + state = [] + @woohoo(1) + async def test(x): + self.assertEqual(state, [1]) + state.append(x) + yield + state.append("second item") + + agen = test("something") + with self.assertRaises(StopIteration): + agen.asend(None).send(None) + with self.assertRaises(StopAsyncIteration): + agen.asend(None).send(None) + + self.assertEqual(state, [1, "something", "second item", 999]) + + class TestBaseExitStack: exit_stack = None @@ -788,6 +936,75 @@ def _exit(): result.append(2) self.assertEqual(result, [1, 2, 3, 4]) + def test_enter_context_classmethod(self): + class TestCM: + @classmethod + def __enter__(cls): + result.append(('enter', cls)) + @classmethod + def __exit__(cls, *exc_details): + result.append(('exit', cls, *exc_details)) + + cm = TestCM() + result = [] + with self.exit_stack() as stack: + stack.enter_context(cm) + self.assertEqual(result, [('enter', TestCM)]) + self.assertEqual(result, [('enter', TestCM), + ('exit', TestCM, None, None, None)]) + + result = [] + with self.exit_stack() as stack: + stack.push(cm) + self.assertEqual(result, []) + self.assertEqual(result, [('exit', TestCM, None, None, None)]) + + def test_enter_context_staticmethod(self): + class TestCM: + @staticmethod + def __enter__(): + result.append('enter') + @staticmethod + def __exit__(*exc_details): + result.append(('exit', *exc_details)) + + cm = TestCM() + result = [] + with self.exit_stack() as stack: + stack.enter_context(cm) + self.assertEqual(result, ['enter']) + self.assertEqual(result, ['enter', ('exit', None, None, None)]) + + result = [] + with self.exit_stack() as stack: + stack.push(cm) + self.assertEqual(result, []) + self.assertEqual(result, [('exit', None, None, None)]) + + def test_enter_context_slots(self): + class TestCM: + __slots__ = ('__enter__', '__exit__') + def __init__(self): + def enter(): + result.append('enter') + def exit(*exc_details): + result.append(('exit', *exc_details)) + self.__enter__ = enter + self.__exit__ = exit + + cm = TestCM() + result = [] + with self.exit_stack() as stack: + stack.enter_context(cm) + self.assertEqual(result, ['enter']) + self.assertEqual(result, ['enter', ('exit', None, None, None)]) + + result = [] + with self.exit_stack() as stack: + stack.push(cm) + self.assertEqual(result, []) + self.assertEqual(result, [('exit', None, None, None)]) + def test_enter_context_errors(self): class LacksEnterAndExit: pass diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index dcd0072037950e..95bdfdb3d9d4a6 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -402,6 +402,144 @@ async def test(): await test() self.assertFalse(entered) + @_async_test + async def test_decorator_decorate_sync_function(self): + @asynccontextmanager + async def context(): + state.append(1) + yield + state.append(999) + + state = [] + @context() + def test(x): + self.assertEqual(state, [1]) + state.append(x) + + await test("something") + self.assertEqual(state, [1, "something", 999]) + + @_async_test + async def test_decorator_decorate_generator_function(self): + @asynccontextmanager + async def context(): + state.append(1) + yield + state.append(999) + + state = [] + @context() + def test(x): + self.assertEqual(state, [1]) + state.append(x) + yield + state.append("second item") + + async for _ in test("something"): + self.assertEqual(state, [1, "something"]) + self.assertEqual(state, [1, "something", "second item", 999]) + + @_async_test + async def test_decorator_decorate_asyncgen_function(self): + @asynccontextmanager + async def context(): + state.append(1) + yield + state.append(999) + + state = [] + @context() + async def test(x): + self.assertEqual(state, [1]) + state.append(x) + yield + state.append("second item") + + async for _ in test("something"): + self.assertEqual(state, [1, "something"]) + self.assertEqual(state, [1, "something", "second item", 999]) + + @_async_test + async def test_decorator_decorate_asyncgen_function_exception(self): + @asynccontextmanager + async def context(): + state.append("enter") + try: + yield + finally: + state.append("exit") + + state = [] + @context() + async def test(): + state.append("body") + yield + raise ZeroDivisionError + + with self.assertRaises(ZeroDivisionError): + async for _ in test(): + pass + self.assertEqual(state, ["enter", "body", "exit"]) + + @_async_test + async def test_decorator_decorate_asyncgen_function_early_stop(self): + @asynccontextmanager + async def context(): + state.append("enter") + try: + yield + finally: + state.append("exit") + + state = [] + @context() + async def test(): + try: + yield 1 + yield 2 + finally: + state.append("inner closed") + + agen = test() + async for value in agen: + self.assertEqual(value, 1) + break + await agen.aclose() + # The inner async generator is closed before the context + # manager exits. + self.assertEqual(state, ["enter", "inner closed", "exit"]) + + @_async_test + async def test_decorator_decorate_asyncgen_function_asend_athrow(self): + @asynccontextmanager + async def context(): + yield + + @context() + async def test(): + try: + received = yield "first" + state.append(("received", received)) + yield "second" + except ValueError: + state.append("inner saw ValueError") + raise + finally: + state.append("inner closed") + + # asend() values and athrow() exceptions are not forwarded to the + # wrapped generator (a documented limitation). + state = [] + agen = test() + self.assertEqual(await agen.__anext__(), "first") + self.assertEqual(await agen.asend("VALUE"), "second") + # The inner generator received None, not "VALUE". + self.assertEqual(state, [("received", None)]) + with self.assertRaises(ValueError): + await agen.athrow(ValueError) + # The inner generator was closed, not thrown into. + self.assertEqual(state, [("received", None), "inner closed"]) + @_async_test async def test_decorator_with_exception(self): entered = False @@ -641,6 +779,78 @@ async def _exit(): self.assertEqual(result, [1, 2, 3, 4]) + @_async_test + async def test_enter_async_context_classmethod(self): + class TestCM: + @classmethod + async def __aenter__(cls): + result.append(('enter', cls)) + @classmethod + async def __aexit__(cls, *exc_details): + result.append(('exit', cls, *exc_details)) + + cm = TestCM() + result = [] + async with self.exit_stack() as stack: + await stack.enter_async_context(cm) + self.assertEqual(result, [('enter', TestCM)]) + self.assertEqual(result, [('enter', TestCM), + ('exit', TestCM, None, None, None)]) + + result = [] + async with self.exit_stack() as stack: + stack.push_async_exit(cm) + self.assertEqual(result, []) + self.assertEqual(result, [('exit', TestCM, None, None, None)]) + + @_async_test + async def test_enter_async_context_staticmethod(self): + class TestCM: + @staticmethod + async def __aenter__(): + result.append('enter') + @staticmethod + async def __aexit__(*exc_details): + result.append(('exit', *exc_details)) + + cm = TestCM() + result = [] + async with self.exit_stack() as stack: + await stack.enter_async_context(cm) + self.assertEqual(result, ['enter']) + self.assertEqual(result, ['enter', ('exit', None, None, None)]) + + result = [] + async with self.exit_stack() as stack: + stack.push_async_exit(cm) + self.assertEqual(result, []) + self.assertEqual(result, [('exit', None, None, None)]) + + @_async_test + async def test_enter_async_context_slots(self): + class TestCM: + __slots__ = ('__aenter__', '__aexit__') + def __init__(self): + async def enter(): + result.append('enter') + async def exit(*exc_details): + result.append(('exit', *exc_details)) + self.__aenter__ = enter + self.__aexit__ = exit + + cm = TestCM() + result = [] + async with self.exit_stack() as stack: + await stack.enter_async_context(cm) + self.assertEqual(result, ['enter']) + self.assertEqual(result, ['enter', ('exit', None, None, None)]) + + result = [] + async with self.exit_stack() as stack: + stack.push_async_exit(cm) + self.assertEqual(result, []) + self.assertEqual(result, [('exit', None, None, None)]) + @_async_test async def test_enter_async_context_errors(self): class LacksEnterAndExit: diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py index 467ec09d99e462..98f56b5ae87f96 100644 --- a/Lib/test/test_copy.py +++ b/Lib/test/test_copy.py @@ -133,6 +133,12 @@ def test_copy_dict(self): self.assertEqual(y, x) self.assertIsNot(y, x) + def test_copy_frozendict(self): + x = frozendict(x=1, y=2) + self.assertIs(copy.copy(x), x) + x = frozendict() + self.assertIs(copy.copy(x), x) + def test_copy_set(self): x = {1, 2, 3} y = copy.copy(x) @@ -419,6 +425,30 @@ def test_deepcopy_dict(self): self.assertIsNot(x, y) self.assertIsNot(x["foo"], y["foo"]) + def test_deepcopy_frozendict(self): + x = frozendict({"foo": [1, 2], "bar": 3}) + y = copy.deepcopy(x) + self.assertEqual(y, x) + self.assertIsNot(x, y) + self.assertIsNot(x["foo"], y["foo"]) + + # recursive frozendict + x = frozendict(foo=[]) + x['foo'].append(x) + y = copy.deepcopy(x) + self.assertEqual(y.keys(), x.keys()) + self.assertIsNot(x, y) + self.assertIsNot(x["foo"], y["foo"]) + self.assertIs(y['foo'][0], y) + + x = frozendict(foo=[]) + x['foo'].append(x) + x = x['foo'] + y = copy.deepcopy(x) + self.assertIsNot(x, y) + self.assertIsNot(x[0], y[0]) + self.assertIs(y[0]['foo'], y) + @support.skip_emscripten_stack_overflow() @support.skip_wasi_stack_overflow() def test_deepcopy_reflexive_dict(self): @@ -672,7 +702,7 @@ def __eq__(self, other): def test_reduce_5tuple(self): class C(dict): def __reduce__(self): - return (C, (), self.__dict__, None, self.items()) + return (C, (), self.__dict__, None, iter(self.items())) def __eq__(self, other): return (dict(self) == dict(other) and self.__dict__ == other.__dict__) diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 42b13064befaa9..93e9e7a8393cb1 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -1008,7 +1008,7 @@ async def foo(): return (await Awaitable()) with self.assertRaisesRegex( - TypeError, "__await__.*returned non-iterator of type"): + TypeError, "__await__.*must return an iterator, not"): run_async(foo()) @@ -1106,7 +1106,7 @@ async def foo(): return await Awaitable() with self.assertRaisesRegex( - TypeError, r"__await__\(\) returned a coroutine"): + TypeError, r"__await__\(\) must return an iterator, not coroutine"): run_async(foo()) c.close() @@ -1120,7 +1120,7 @@ async def foo(): return await Awaitable() with self.assertRaisesRegex( - TypeError, "__await__.*returned non-iterator of type"): + TypeError, "__await__.*must return an iterator, not"): run_async(foo()) @@ -2265,6 +2265,20 @@ def c(): # before fixing, visible stack from throw would be shorter than from send. self.assertEqual(len_send, len_throw) + def test_call_generator_in_frame_clear(self): + # gh-143939: Running a generator while clearing the coroutine's frame + # should not be misinterpreted as a yield. + class CallGeneratorOnDealloc: + def __del__(self): + next(x for x in [1]) + + async def coro(): + obj = CallGeneratorOnDealloc() + return 42 + + yielded, result = run_async(coro()) + self.assertEqual(yielded, []) + self.assertEqual(result, 42) @unittest.skipIf( support.is_emscripten or support.is_wasi, @@ -2490,7 +2504,7 @@ async def foo(): return (await future) with self.assertRaisesRegex( - TypeError, "__await__.*returned non-iterator of type 'int'"): + TypeError, "__await__.*must return an iterator, not int"): self.assertEqual(foo().send(None), 1) diff --git a/Lib/test/test_cppext/__init__.py b/Lib/test/test_cppext/__init__.py index 2f54b3ccb35cc4..967feee6693c03 100644 --- a/Lib/test/test_cppext/__init__.py +++ b/Lib/test/test_cppext/__init__.py @@ -1,9 +1,12 @@ # gh-91321: Build a basic C++ test extension to check that the Python C API is # compatible with C++ and does not emit C++ compiler warnings. import os.path +import platform import shlex import shutil import subprocess +import sys +import sysconfig import unittest from test import support @@ -25,32 +28,18 @@ @support.requires_subprocess() @support.requires_resource('cpu') class BaseTests: - def test_build(self): - self.check_build('_testcppext') - - def test_build_cpp03(self): - # In public docs, we say C API is compatible with C++11. However, - # in practice we do maintain C++03 compatibility in public headers. - # Please ask the C API WG before adding a new C++11-only feature. - self.check_build('_testcpp03ext', std='c++03') - - @unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c++11") - def test_build_cpp11(self): - self.check_build('_testcpp11ext', std='c++11') - - # Only test C++14 on MSVC. - # On s390x RHEL7, GCC 4.8.5 doesn't support C++14. - @unittest.skipIf(not support.MS_WINDOWS, "need Windows") - def test_build_cpp14(self): - self.check_build('_testcpp14ext', std='c++14') + TEST_INTERNAL_C_API = False - def check_build(self, extension_name, std=None, limited=False): + def check_build(self, extension_name, std=None, limited=False, + extra_cflags=None): venv_dir = 'env' with support.setup_venv_with_pip_setuptools(venv_dir) as python_exe: self._check_build(extension_name, python_exe, - std=std, limited=limited) + std=std, limited=limited, + extra_cflags=extra_cflags) - def _check_build(self, extension_name, python_exe, std, limited): + def _check_build(self, extension_name, python_exe, std, limited, + extra_cflags=None): pkg_dir = 'pkg' os.mkdir(pkg_dir) shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP))) @@ -62,7 +51,13 @@ def run_cmd(operation, cmd): env['CPYTHON_TEST_CPP_STD'] = std if limited: env['CPYTHON_TEST_LIMITED'] = '1' + if support.MS_WINDOWS and sysconfig.is_python_build(): + env['CPYTHON_EXTRA_INCDIRS'] = os.path.split(sysconfig.get_config_h_filename())[0] + env['CPYTHON_EXTRA_LIBDIRS'] = os.path.split(sys.executable)[0] env['CPYTHON_TEST_EXT_NAME'] = extension_name + env['TEST_INTERNAL_C_API'] = str(int(self.TEST_INTERNAL_C_API)) + if extra_cflags: + env['CPYTHON_TEST_EXTRA_CFLAGS'] = extra_cflags if support.verbose: print('Run:', ' '.join(map(shlex.quote, cmd))) subprocess.run(cmd, check=True, env=env) @@ -104,6 +99,9 @@ def run_cmd(operation, cmd): class TestPublicCAPI(BaseTests, unittest.TestCase): + def test_build(self): + self.check_build('_testcppext') + @support.requires_gil_enabled('incompatible with Free Threading') def test_build_limited_cpp03(self): self.check_build('_test_limited_cpp03ext', std='c++03', limited=True) @@ -112,10 +110,41 @@ def test_build_limited_cpp03(self): def test_build_limited(self): self.check_build('_testcppext_limited', limited=True) + def test_build_cpp03(self): + # In public docs, we say C API is compatible with C++11. However, + # in practice we do maintain C++03 compatibility in public headers. + # Please ask the C API WG before adding a new C++11-only feature. + self.check_build('_testcpp03ext', std='c++03') + + @unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c++11") + def test_build_cpp11(self): + self.check_build('_testcpp11ext', std='c++11') + + # Only test C++14 on MSVC. + # On s390x RHEL7, GCC 4.8.5 doesn't support C++14. + @unittest.skipIf(not support.MS_WINDOWS, "need Windows") + def test_build_cpp14(self): + self.check_build('_testcpp14ext', std='c++14') + + # Test that headers compile with Intel asm syntax, which may conflict + # with inline assembly in free-threading headers that use AT&T syntax. + @unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support -masm=intel") + @unittest.skipUnless(platform.machine() in ('x86_64', 'i686', 'AMD64'), + "x86-specific flag") + def test_build_intel_asm(self): + self.check_build('_testcppext_asm', extra_cflags='-masm=intel') + class TestInteralCAPI(BaseTests, unittest.TestCase): TEST_INTERNAL_C_API = True + def test_build(self): + kwargs = {} + if sys.platform == 'darwin': + # Old Apple clang++ default C++ std is gnu++98 + kwargs['std'] = 'c++11' + self.check_build('_testcppext_internal', **kwargs) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_cppext/extension.cpp b/Lib/test/test_cppext/extension.cpp index 1affa176088d57..4db63df94f5233 100644 --- a/Lib/test/test_cppext/extension.cpp +++ b/Lib/test/test_cppext/extension.cpp @@ -7,14 +7,29 @@ #undef NDEBUG #ifdef TEST_INTERNAL_C_API -# define Py_BUILD_CORE 1 +# define Py_BUILD_CORE_MODULE 1 #endif #include "Python.h" +#include "datetime.h" #ifdef TEST_INTERNAL_C_API // gh-135906: Check for compiler warnings in the internal C API + // - Cython uses pycore_critical_section.h, pycore_frame.h and + // pycore_template.h. + // - greenlet uses pycore_frame.h, pycore_interpframe_structs.h and + // pycore_interpframe.h. # include "internal/pycore_frame.h" +# include "internal/pycore_interpframe_structs.h" +# include "internal/pycore_template.h" + + // mimalloc emits compiler warnings on Windows. +# if !defined(MS_WINDOWS) +# include "internal/pycore_backoff.h" +# include "internal/pycore_cell.h" +# include "internal/pycore_critical_section.h" +# include "internal/pycore_interpframe.h" +# endif #endif #ifndef MODULE_NAME @@ -223,11 +238,26 @@ test_virtual_object(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) Py_RETURN_NONE; } +static PyObject * +test_datetime(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + // datetime.h is excluded from the limited C API +#ifndef Py_LIMITED_API + PyDateTime_IMPORT; + if (PyErr_Occurred()) { + return NULL; + } +#endif + + Py_RETURN_NONE; +} + static PyMethodDef _testcppext_methods[] = { {"add", _testcppext_add, METH_VARARGS, _testcppext_add_doc}, {"test_api_casts", test_api_casts, METH_NOARGS, _Py_NULL}, {"test_unicode", test_unicode, METH_NOARGS, _Py_NULL}, {"test_virtual_object", test_virtual_object, METH_NOARGS, _Py_NULL}, + {"test_datetime", test_datetime, METH_NOARGS, _Py_NULL}, // Note: _testcppext_exec currently runs all test functions directly. // When adding a new one, add a call there. @@ -256,6 +286,10 @@ _testcppext_exec(PyObject *module) if (!result) return -1; Py_DECREF(result); + result = PyObject_CallMethod(module, "test_datetime", ""); + if (!result) return -1; + Py_DECREF(result); + // test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR() Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int)); assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0); diff --git a/Lib/test/test_cppext/setup.py b/Lib/test/test_cppext/setup.py index 98442b106b6113..5d004ca6e3ad78 100644 --- a/Lib/test/test_cppext/setup.py +++ b/Lib/test/test_cppext/setup.py @@ -1,7 +1,6 @@ # gh-91321: Build a basic C++ test extension to check that the Python C API is # compatible with C++ and does not emit C++ compiler warnings. import os -import platform import shlex import sys import sysconfig @@ -48,6 +47,8 @@ def main(): module_name = os.environ["CPYTHON_TEST_EXT_NAME"] limited = bool(os.environ.get("CPYTHON_TEST_LIMITED", "")) internal = bool(int(os.environ.get("TEST_INTERNAL_C_API", "0"))) + incdirs = os.environ.get("CPYTHON_EXTRA_INCDIRS", "") + libdirs = os.environ.get("CPYTHON_EXTRA_LIBDIRS", "") cppflags = list(CPPFLAGS) cppflags.append(f'-DMODULE_NAME={module_name}') @@ -59,7 +60,7 @@ def main(): else: cppflags.append(f'-std={std}') - if limited or (std != 'c++03'): + if limited or (std != 'c++03') and not internal: # See CPPFLAGS_PEDANTIC docstring cppflags.extend(CPPFLAGS_PEDANTIC) @@ -86,22 +87,23 @@ def main(): if internal: cppflags.append('-DTEST_INTERNAL_C_API=1') - # On Windows, add PCbuild\amd64\ to include and library directories + extra_cflags = os.environ.get("CPYTHON_TEST_EXTRA_CFLAGS", "") + if extra_cflags: + cppflags.extend(shlex.split(extra_cflags)) + + # Add additional include and library directories, typically for in-tree + # testing where not all directories are inferred include_dirs = [] library_dirs = [] - if support.MS_WINDOWS: - srcdir = sysconfig.get_config_var('srcdir') - machine = platform.uname().machine - pcbuild = os.path.join(srcdir, 'PCbuild', machine) - if os.path.exists(pcbuild): - # pyconfig.h is generated in PCbuild\amd64\ - include_dirs.append(pcbuild) - # python313.lib is generated in PCbuild\amd64\ - library_dirs.append(pcbuild) - print(f"Add PCbuild directory: {pcbuild}") + if incdirs: + print("Add incdirs:", incdirs) + include_dirs.extend(incdirs.split(os.pathsep)) + if libdirs: + print("Add libdirs:", libdirs) + library_dirs.extend(libdirs.split(os.pathsep)) # Display information to help debugging - for env_name in ('CC', 'CFLAGS', 'CPPFLAGS'): + for env_name in ('CC', 'CXX', 'CFLAGS', 'CPPFLAGS', 'CXXFLAGS'): if env_name in os.environ: print(f"{env_name} env var: {os.environ[env_name]!r}") else: diff --git a/Lib/test/test_cprofile.py b/Lib/test/test_cprofile.py deleted file mode 100644 index 57e818b1c68b38..00000000000000 --- a/Lib/test/test_cprofile.py +++ /dev/null @@ -1,230 +0,0 @@ -"""Test suite for the cProfile module.""" - -import sys -import unittest - -# rip off all interesting stuff from test_profile -import cProfile -import tempfile -import textwrap -from test.test_profile import ProfileTest, regenerate_expected_output -from test.support.script_helper import assert_python_failure, assert_python_ok -from test import support - - -class CProfileTest(ProfileTest): - profilerclass = cProfile.Profile - profilermodule = cProfile - expected_max_output = "{built-in method builtins.max}" - - def get_expected_output(self): - return _ProfileOutput - - def test_bad_counter_during_dealloc(self): - # bpo-3895 - import _lsprof - - with support.catch_unraisable_exception() as cm: - obj = _lsprof.Profiler(lambda: int) - obj.enable() - obj.disable() - obj.clear() - - self.assertEqual(cm.unraisable.exc_type, TypeError) - - def test_crash_with_not_enough_args(self): - # gh-126220 - import _lsprof - - for profile in [_lsprof.Profiler(), cProfile.Profile()]: - for method in [ - "_pystart_callback", - "_pyreturn_callback", - "_ccall_callback", - "_creturn_callback", - ]: - with self.subTest(profile=profile, method=method): - method_obj = getattr(profile, method) - with self.assertRaises(TypeError): - method_obj() # should not crash - - def test_evil_external_timer(self): - # gh-120289 - # Disabling profiler in external timer should not crash - import _lsprof - class EvilTimer(): - def __init__(self, disable_count): - self.count = 0 - self.disable_count = disable_count - - def __call__(self): - self.count += 1 - if self.count == self.disable_count: - profiler_with_evil_timer.disable() - return self.count - - # this will trigger external timer to disable profiler at - # call event - in initContext in _lsprof.c - with support.catch_unraisable_exception() as cm: - profiler_with_evil_timer = _lsprof.Profiler(EvilTimer(1)) - profiler_with_evil_timer.enable() - # Make a call to trigger timer - (lambda: None)() - profiler_with_evil_timer.disable() - profiler_with_evil_timer.clear() - self.assertEqual(cm.unraisable.exc_type, RuntimeError) - - # this will trigger external timer to disable profiler at - # return event - in Stop in _lsprof.c - with support.catch_unraisable_exception() as cm: - profiler_with_evil_timer = _lsprof.Profiler(EvilTimer(2)) - profiler_with_evil_timer.enable() - # Make a call to trigger timer - (lambda: None)() - profiler_with_evil_timer.disable() - profiler_with_evil_timer.clear() - self.assertEqual(cm.unraisable.exc_type, RuntimeError) - - def test_profile_enable_disable(self): - prof = self.profilerclass() - # Make sure we clean ourselves up if the test fails for some reason. - self.addCleanup(prof.disable) - - prof.enable() - self.assertEqual( - sys.monitoring.get_tool(sys.monitoring.PROFILER_ID), "cProfile") - - prof.disable() - self.assertIs(sys.monitoring.get_tool(sys.monitoring.PROFILER_ID), None) - - def test_profile_as_context_manager(self): - prof = self.profilerclass() - # Make sure we clean ourselves up if the test fails for some reason. - self.addCleanup(prof.disable) - - with prof as __enter__return_value: - # profile.__enter__ should return itself. - self.assertIs(prof, __enter__return_value) - - # profile should be set as the global profiler inside the - # with-block - self.assertEqual( - sys.monitoring.get_tool(sys.monitoring.PROFILER_ID), "cProfile") - - # profile shouldn't be set once we leave the with-block. - self.assertIs(sys.monitoring.get_tool(sys.monitoring.PROFILER_ID), None) - - def test_second_profiler(self): - pr = self.profilerclass() - pr2 = self.profilerclass() - pr.enable() - self.assertRaises(ValueError, pr2.enable) - pr.disable() - - def test_throw(self): - """ - gh-106152 - generator.throw() should trigger a call in cProfile - """ - - def gen(): - yield - - pr = self.profilerclass() - pr.enable() - g = gen() - try: - g.throw(SyntaxError) - except SyntaxError: - pass - pr.disable() - pr.create_stats() - - self.assertTrue(any("throw" in func[2] for func in pr.stats.keys())), - - def test_bad_descriptor(self): - # gh-132250 - # cProfile should not crash when the profiler callback fails to locate - # the actual function of a method. - with self.profilerclass() as prof: - with self.assertRaises(TypeError): - bytes.find(str()) - - -class TestCommandLine(unittest.TestCase): - def test_sort(self): - rc, out, err = assert_python_failure('-m', 'cProfile', '-s', 'demo') - self.assertGreater(rc, 0) - self.assertIn(b"option -s: invalid choice: 'demo'", err) - - def test_profile_script_importing_main(self): - """Check that scripts that reference __main__ see their own namespace - when being profiled.""" - with tempfile.NamedTemporaryFile("w+", delete_on_close=False) as f: - f.write(textwrap.dedent("""\ - class Foo: - pass - import __main__ - assert Foo == __main__.Foo - """)) - f.close() - assert_python_ok('-m', "cProfile", f.name) - - -def main(): - if '-r' not in sys.argv: - unittest.main() - else: - regenerate_expected_output(__file__, CProfileTest) - - -# Don't remove this comment. Everything below it is auto-generated. -#--cut-------------------------------------------------------------------------- -_ProfileOutput = {} -_ProfileOutput['print_stats'] = """\ - 28 0.028 0.001 0.028 0.001 profilee.py:110(__getattr__) - 1 0.270 0.270 1.000 1.000 profilee.py:25(testfunc) - 23/3 0.150 0.007 0.170 0.057 profilee.py:35(factorial) - 20 0.020 0.001 0.020 0.001 profilee.py:48(mul) - 2 0.040 0.020 0.600 0.300 profilee.py:55(helper) - 4 0.116 0.029 0.120 0.030 profilee.py:73(helper1) - 2 0.000 0.000 0.140 0.070 profilee.py:84(helper2_indirect) - 8 0.312 0.039 0.400 0.050 profilee.py:88(helper2) - 8 0.064 0.008 0.080 0.010 profilee.py:98(subhelper)""" -_ProfileOutput['print_callers'] = """\ -profilee.py:110(__getattr__) <- 16 0.016 0.016 profilee.py:98(subhelper) -profilee.py:25(testfunc) <- 1 0.270 1.000 :1() -profilee.py:35(factorial) <- 1 0.014 0.130 profilee.py:25(testfunc) - 20/3 0.130 0.147 profilee.py:35(factorial) - 2 0.006 0.040 profilee.py:84(helper2_indirect) -profilee.py:48(mul) <- 20 0.020 0.020 profilee.py:35(factorial) -profilee.py:55(helper) <- 2 0.040 0.600 profilee.py:25(testfunc) -profilee.py:73(helper1) <- 4 0.116 0.120 profilee.py:55(helper) -profilee.py:84(helper2_indirect) <- 2 0.000 0.140 profilee.py:55(helper) -profilee.py:88(helper2) <- 6 0.234 0.300 profilee.py:55(helper) - 2 0.078 0.100 profilee.py:84(helper2_indirect) -profilee.py:98(subhelper) <- 8 0.064 0.080 profilee.py:88(helper2) -{built-in method builtins.hasattr} <- 4 0.000 0.004 profilee.py:73(helper1) - 8 0.000 0.008 profilee.py:88(helper2) -{built-in method sys.exception} <- 4 0.000 0.000 profilee.py:73(helper1) -{method 'append' of 'list' objects} <- 4 0.000 0.000 profilee.py:73(helper1)""" -_ProfileOutput['print_callees'] = """\ -:1() -> 1 0.270 1.000 profilee.py:25(testfunc) -profilee.py:110(__getattr__) -> -profilee.py:25(testfunc) -> 1 0.014 0.130 profilee.py:35(factorial) - 2 0.040 0.600 profilee.py:55(helper) -profilee.py:35(factorial) -> 20/3 0.130 0.147 profilee.py:35(factorial) - 20 0.020 0.020 profilee.py:48(mul) -profilee.py:48(mul) -> -profilee.py:55(helper) -> 4 0.116 0.120 profilee.py:73(helper1) - 2 0.000 0.140 profilee.py:84(helper2_indirect) - 6 0.234 0.300 profilee.py:88(helper2) -profilee.py:73(helper1) -> 4 0.000 0.004 {built-in method builtins.hasattr} -profilee.py:84(helper2_indirect) -> 2 0.006 0.040 profilee.py:35(factorial) - 2 0.078 0.100 profilee.py:88(helper2) -profilee.py:88(helper2) -> 8 0.064 0.080 profilee.py:98(subhelper) -profilee.py:98(subhelper) -> 16 0.016 0.016 profilee.py:110(__getattr__) -{built-in method builtins.hasattr} -> 12 0.012 0.012 profilee.py:110(__getattr__)""" - -if __name__ == "__main__": - main() diff --git a/Lib/test/test_crossinterp.py b/Lib/test/test_crossinterp.py index 2fa0077a09bbbb..f4bf5a66ad2155 100644 --- a/Lib/test/test_crossinterp.py +++ b/Lib/test/test_crossinterp.py @@ -1,10 +1,12 @@ import contextlib import itertools import sys +import traceback import types import unittest import warnings +from test import support from test.support import import_helper _testinternalcapi = import_helper.import_module('_testinternalcapi') @@ -155,6 +157,10 @@ def ignore_byteswarning(): {}, {1: 7, 2: 8, 3: 9}, {1: [1], 2: (2,), 3: {3: 4}}, + # frozendict + frozendict(), + frozendict({1: 7, 2: 8, 3: 9}), + frozendict({1: [1], 2: (2,), 3: {3: 4}, 4: frozendict({5: 6})}), # set set(), {1, 2, 3}, @@ -1491,5 +1497,54 @@ def test_builtin_objects(self): ]) +class CaptureExceptionTests(unittest.TestCase): + + # Prevent crashes with incompatible TracebackException.format(). + # Regression test for https://github.com/python/cpython/issues/143377. + + def capture_with_formatter(self, exc, formatter): + with support.swap_attr(traceback.TracebackException, "format", formatter): + return _interpreters.capture_exception(exc) + + def test_capture_exception(self): + captured = _interpreters.capture_exception(ValueError("hello")) + + self.assertEqual(captured.type.__name__, "ValueError") + self.assertEqual(captured.type.__qualname__, "ValueError") + self.assertEqual(captured.type.__module__, "builtins") + + self.assertEqual(captured.msg, "hello") + self.assertEqual(captured.formatted, "ValueError: hello") + + def test_capture_exception_custom_format(self): + exc = ValueError("good bye!") + formatter = lambda self: ["hello\n", "world\n"] + captured = self.capture_with_formatter(exc, formatter) + self.assertEqual(captured.msg, "good bye!") + self.assertEqual(captured.formatted, "ValueError: good bye!") + self.assertEqual(captured.errdisplay, "hello\nworld") + + @support.subTests("exc_lines", ([], ["x-no-nl"], ["x-no-nl", "y-no-nl"])) + def test_capture_exception_invalid_format(self, exc_lines): + formatter = lambda self: exc_lines + captured = self.capture_with_formatter(ValueError(), formatter) + self.assertEqual(captured.msg, "") + self.assertEqual(captured.formatted, "ValueError: ") + self.assertEqual(captured.errdisplay, "".join(exc_lines)) + + @unittest.skipUnless( + support.Py_DEBUG, + "printing subinterpreter unraisable exceptions requires DEBUG build", + ) + def test_capture_exception_unraisable_exception(self): + formatter = lambda self: 1 + with support.catch_unraisable_exception() as cm: + captured = self.capture_with_formatter(ValueError(), formatter) + self.assertNotHasAttr(captured, "errdisplay") + self.assertEqual(cm.unraisable.exc_type, TypeError) + self.assertEqual(str(cm.unraisable.exc_value), + "can only join an iterable") + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 60feab225a107c..7327c1bd5f5053 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -553,6 +553,33 @@ def test_roundtrip_escaped_unquoted_newlines(self): self.assertEqual(row, rows[i]) + def test_reader_reentrant_iterator(self): + # gh-145105: re-entering the reader from the iterator must not crash. + class ReentrantIter: + def __init__(self): + self.reader = None + self.n = 0 + def __iter__(self): + return self + def __next__(self): + self.n += 1 + if self.n == 1: + try: + next(self.reader) + except StopIteration: + pass + return "a,b" + if self.n == 2: + return "x" + raise StopIteration + + it = ReentrantIter() + reader = csv.reader(it) + it.reader = reader + with self.assertRaises(csv.Error): + next(reader) + + class TestDialectRegistry(unittest.TestCase): def test_registry_badargs(self): self.assertRaises(TypeError, csv.list_dialects, None) @@ -918,6 +945,14 @@ def test_dict_reader_fieldnames_accepts_list(self): reader = csv.DictReader(f, fieldnames) self.assertEqual(reader.fieldnames, fieldnames) + def test_dict_reader_set_fieldnames(self): + fieldnames = ["a", "b", "c"] + f = StringIO() + reader = csv.DictReader(f) + self.assertIsNone(reader.fieldnames) + reader.fieldnames = fieldnames + self.assertEqual(reader.fieldnames, fieldnames) + def test_dict_writer_fieldnames_rejects_iter(self): fieldnames = ["a", "b", "c"] f = StringIO() @@ -933,6 +968,7 @@ def test_dict_writer_fieldnames_accepts_list(self): def test_dict_reader_fieldnames_is_optional(self): f = StringIO() reader = csv.DictReader(f, fieldnames=None) + self.assertIsNone(reader.fieldnames) def test_read_dict_fields(self): with TemporaryFile("w+", encoding="utf-8") as fileobj: @@ -1271,6 +1307,19 @@ class mydialect(csv.Dialect): self.assertRaises(ValueError, create_invalid, field_name, " ", skipinitialspace=True) + def test_dialect_getattr_non_attribute_error_propagates(self): + # gh-145966: non-AttributeError exceptions raised by __getattr__ + # during dialect attribute lookup must propagate, not be silenced. + class BadDialect: + def __getattr__(self, name): + raise RuntimeError("boom") + + with self.assertRaises(RuntimeError): + csv.reader([], dialect=BadDialect()) + + with self.assertRaises(RuntimeError): + csv.writer(StringIO(), dialect=BadDialect()) + class TestSniffer(unittest.TestCase): sample1 = """\ @@ -1353,6 +1402,19 @@ class TestSniffer(unittest.TestCase): ghi\0jkl """ + sample15 = "\n\n\n" + sample16 = "abc\ndef\nghi" + + sample17 = ["letter,offset"] + sample17.extend(f"{chr(ord('a') + i)},{i}" for i in range(20)) + sample17.append("v,twenty_one") # 'u' was skipped + sample17 = '\n'.join(sample17) + + sample18 = ["letter,offset"] + sample18.extend(f"{chr(ord('a') + i)},{i}" for i in range(21)) + sample18.append("v,twenty_one") # 'u' was not skipped + sample18 = '\n'.join(sample18) + def test_issue43625(self): sniffer = csv.Sniffer() self.assertTrue(sniffer.has_header(self.sample12)) @@ -1374,6 +1436,11 @@ def test_has_header_regex_special_delimiter(self): self.assertIs(sniffer.has_header(self.sample8), False) self.assertIs(sniffer.has_header(self.header2 + self.sample8), True) + def test_has_header_checks_20_rows(self): + sniffer = csv.Sniffer() + self.assertFalse(sniffer.has_header(self.sample17)) + self.assertTrue(sniffer.has_header(self.sample18)) + def test_guess_quote_and_delimiter(self): sniffer = csv.Sniffer() for header in (";'123;4';", "'123;4';", ";'123;4'", "'123;4'"): @@ -1423,6 +1490,10 @@ def test_delimiters(self): self.assertEqual(dialect.quotechar, "'") dialect = sniffer.sniff(self.sample14) self.assertEqual(dialect.delimiter, '\0') + self.assertRaisesRegex(csv.Error, "Could not determine delimiter", + sniffer.sniff, self.sample15) + self.assertRaisesRegex(csv.Error, "Could not determine delimiter", + sniffer.sniff, self.sample16) def test_doublequote(self): sniffer = csv.Sniffer() @@ -1437,6 +1508,56 @@ def test_doublequote(self): dialect = sniffer.sniff(self.sample9) self.assertTrue(dialect.doublequote) + def test_guess_delimiter_crlf_not_chosen(self): + # Ensure that we pick the real delimiter ("|") over "\r" in a tie. + sniffer = csv.Sniffer() + sample = "a|b\r\nc|d\r\ne|f\r\n" + self.assertEqual(sniffer.sniff(sample).delimiter, "|") + self.assertNotEqual(sniffer.sniff(sample).delimiter, "\r") + + def test_zero_mode_tie_order_independence(self): + sniffer = csv.Sniffer() + # ":" appears in half the rows (1, 0, 1, 0) - a tie between + # 0 and 1 per line. + # "," appears once every row (true delimiter). + # + # Even if the zero-frequency bucket is appended vs. inserted, the tie + # yields an adjusted score of 0, so ":" should not be promoted and + # "," must be selected. + sample = ( + "a,b:c\n" + "d,e\n" + "f,g:c\n" + "h,i\n" + ) + dialect = sniffer.sniff(sample) + self.assertEqual(dialect.delimiter, ",") + + def test_zero_mode_tie_order_comma_first(self): + sniffer = csv.Sniffer() + pattern = ( + "a,b\n" + "c:d\n" + "e,f\n" + "g:h\n" + ) + sample = pattern * 10 + with self.assertRaisesRegex(csv.Error, "Could not determine delimiter"): + sniffer.sniff(sample) + + def test_zero_mode_tie_order_colon_first(self): + sniffer = csv.Sniffer() + pattern = ( + "a:b\n" + "c,d\n" + "e:f\n" + "g,h\n" + ) + sample = pattern * 10 + with self.assertRaisesRegex(csv.Error, "Could not determine delimiter"): + sniffer.sniff(sample) + + class NUL: def write(s, *args): pass @@ -1603,5 +1724,16 @@ def test_disallow_instantiation(self): with self.subTest(tp=tp): check_disallow_instantiation(self, tp) + +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(csv, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_ctypes/__init__.py b/Lib/test/test_ctypes/__init__.py index eb9126cbe18081..d848beb1ff1857 100644 --- a/Lib/test/test_ctypes/__init__.py +++ b/Lib/test/test_ctypes/__init__.py @@ -1,4 +1,5 @@ import os +import unittest from test import support from test.support import import_helper @@ -6,5 +7,21 @@ # skip tests if the _ctypes extension was not built import_helper.import_module('ctypes') + +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + import ctypes + import _ctypes + + for mod in (ctypes, _ctypes): + with self.subTest(mod=mod): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(mod, "__version__") + self.assertEqual(cm.filename, __file__) + + def load_tests(*args): return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_ctypes/test_byteswap.py b/Lib/test/test_ctypes/test_byteswap.py index f14e1aa32e17ab..6a1bae14773d27 100644 --- a/Lib/test/test_ctypes/test_byteswap.py +++ b/Lib/test/test_ctypes/test_byteswap.py @@ -1,4 +1,5 @@ import binascii +import ctypes import math import struct import sys @@ -165,6 +166,48 @@ def test_endian_double(self): self.assertEqual(s.value, math.pi) self.assertEqual(bin(struct.pack(">d", math.pi)), bin(s)) + @unittest.skipUnless(hasattr(ctypes, 'c_float_complex'), "No complex types") + def test_endian_float_complex(self): + c_float_complex = ctypes.c_float_complex + if sys.byteorder == "little": + self.assertIs(c_float_complex.__ctype_le__, c_float_complex) + self.assertIs(c_float_complex.__ctype_be__.__ctype_le__, + c_float_complex) + else: + self.assertIs(c_float_complex.__ctype_be__, c_float_complex) + self.assertIs(c_float_complex.__ctype_le__.__ctype_be__, + c_float_complex) + s = c_float_complex(math.pi+1j) + self.assertEqual(bin(struct.pack("F", math.pi+1j)), bin(s)) + self.assertAlmostEqual(s.value, math.pi+1j, places=6) + s = c_float_complex.__ctype_le__(math.pi+1j) + self.assertAlmostEqual(s.value, math.pi+1j, places=6) + self.assertEqual(bin(struct.pack("F", math.pi+1j)), bin(s)) + + @unittest.skipUnless(hasattr(ctypes, 'c_double_complex'), "No complex types") + def test_endian_double_complex(self): + c_double_complex = ctypes.c_double_complex + if sys.byteorder == "little": + self.assertIs(c_double_complex.__ctype_le__, c_double_complex) + self.assertIs(c_double_complex.__ctype_be__.__ctype_le__, + c_double_complex) + else: + self.assertIs(c_double_complex.__ctype_be__, c_double_complex) + self.assertIs(c_double_complex.__ctype_le__.__ctype_be__, + c_double_complex) + s = c_double_complex(math.pi+1j) + self.assertEqual(bin(struct.pack("D", math.pi+1j)), bin(s)) + self.assertAlmostEqual(s.value, math.pi+1j, places=6) + s = c_double_complex.__ctype_le__(math.pi+1j) + self.assertAlmostEqual(s.value, math.pi+1j, places=6) + self.assertEqual(bin(struct.pack("D", math.pi+1j)), bin(s)) + def test_endian_other(self): self.assertIs(c_byte.__ctype_le__, c_byte) self.assertIs(c_byte.__ctype_be__, c_byte) diff --git a/Lib/test/test_ctypes/test_c_simple_type_meta.py b/Lib/test/test_ctypes/test_c_simple_type_meta.py index fd261acf49741f..3b4fcf9c42a138 100644 --- a/Lib/test/test_ctypes/test_c_simple_type_meta.py +++ b/Lib/test/test_ctypes/test_c_simple_type_meta.py @@ -213,14 +213,12 @@ def test_bad_type_message(self): class F(metaclass=PyCSimpleType): _type_ = "\0" message = str(cm.exception) - expected_type_chars = list('cbBhHiIlLdDFGfuzZqQPXOv?g') - if not hasattr(ctypes, 'c_float_complex'): - expected_type_chars.remove('F') - expected_type_chars.remove('D') - expected_type_chars.remove('G') + expected_type_chars = list('cbBhHiIlLdfuzZqQPXOv?g') if not MS_WINDOWS: expected_type_chars.remove('X') self.assertIn("'" + ''.join(expected_type_chars) + "'", message) + if hasattr(ctypes, 'c_float_complex'): + self.assertIn("'Zf', 'Zd', 'Zg'", message) def test_creating_pointer_in_dunder_init_3(self): """Check if interfcase subclasses properly creates according internal diff --git a/Lib/test/test_ctypes/test_dlerror.py b/Lib/test/test_ctypes/test_dlerror.py index 8af34e62b94faa..bb0aec0b3c5248 100644 --- a/Lib/test/test_ctypes/test_dlerror.py +++ b/Lib/test/test_ctypes/test_dlerror.py @@ -30,8 +30,10 @@ """ -@unittest.skipUnless(sys.platform.startswith('linux'), - 'test requires GNU IFUNC support') +@unittest.skipIf(not sys.platform.startswith('linux') + or platform.machine().startswith('parisc'), + 'test requires GNU IFUNC support') +@unittest.skipIf(test.support.linked_to_musl(), "Requires glibc") class TestNullDlsym(unittest.TestCase): """GH-126554: Ensure that we catch NULL dlsym return values diff --git a/Lib/test/test_ctypes/test_find.py b/Lib/test/test_ctypes/test_find.py index 3bd41a0e435d91..8bc84c3d2ef9f8 100644 --- a/Lib/test/test_ctypes/test_find.py +++ b/Lib/test/test_ctypes/test_find.py @@ -153,5 +153,73 @@ def test_find(self): self.assertIsNone(find_library(name)) +@unittest.skipUnless(test.support.is_emscripten, + 'Test only valid for Emscripten') +class FindLibraryEmscripten(unittest.TestCase): + @classmethod + def setUpClass(cls): + import tempfile + + # A very simple wasm module + # In WAT format: (module) + cls.wasm_module = b'\x00asm\x01\x00\x00\x00\x00\x08\x04name\x02\x01\x00' + + cls.non_wasm_content = b'This is not a WASM file' + + cls.temp_dir = tempfile.mkdtemp() + cls.libdummy_so_path = os.path.join(cls.temp_dir, 'libdummy.so') + with open(cls.libdummy_so_path, 'wb') as f: + f.write(cls.wasm_module) + + cls.libother_wasm_path = os.path.join(cls.temp_dir, 'libother.wasm') + with open(cls.libother_wasm_path, 'wb') as f: + f.write(cls.wasm_module) + + cls.libnowasm_so_path = os.path.join(cls.temp_dir, 'libnowasm.so') + with open(cls.libnowasm_so_path, 'wb') as f: + f.write(cls.non_wasm_content) + + @classmethod + def tearDownClass(cls): + import shutil + shutil.rmtree(cls.temp_dir) + + def test_find_wasm_file_with_so_extension(self): + with os_helper.EnvironmentVarGuard() as env: + env.set('LD_LIBRARY_PATH', self.temp_dir) + result = find_library('dummy') + self.assertEqual(result, self.libdummy_so_path) + def test_find_wasm_file_with_wasm_extension(self): + with os_helper.EnvironmentVarGuard() as env: + env.set('LD_LIBRARY_PATH', self.temp_dir) + result = find_library('other') + self.assertEqual(result, self.libother_wasm_path) + + def test_ignore_non_wasm_file(self): + with os_helper.EnvironmentVarGuard() as env: + env.set('LD_LIBRARY_PATH', self.temp_dir) + result = find_library('nowasm') + self.assertIsNone(result) + + def test_find_nothing_without_ld_library_path(self): + with os_helper.EnvironmentVarGuard() as env: + if 'LD_LIBRARY_PATH' in env: + del env['LD_LIBRARY_PATH'] + result = find_library('dummy') + self.assertIsNone(result) + result = find_library('other') + self.assertIsNone(result) + + def test_find_nothing_with_wrong_ld_library_path(self): + import tempfile + with tempfile.TemporaryDirectory() as empty_dir: + with os_helper.EnvironmentVarGuard() as env: + env.set('LD_LIBRARY_PATH', empty_dir) + result = find_library('dummy') + self.assertIsNone(result) + result = find_library('other') + self.assertIsNone(result) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ctypes/test_incomplete.py b/Lib/test/test_ctypes/test_incomplete.py index 3189fcd1bd1330..8f6316d6fba61a 100644 --- a/Lib/test/test_ctypes/test_incomplete.py +++ b/Lib/test/test_ctypes/test_incomplete.py @@ -1,6 +1,6 @@ import ctypes import unittest -from ctypes import Structure, POINTER, pointer, c_char_p +from ctypes import Structure, POINTER, pointer, c_char_p, c_int # String-based "incomplete pointers" were implemented in ctypes 0.6.3 (2003, when # ctypes was an external project). They made obsolete by the current @@ -50,6 +50,29 @@ class cell(Structure): lpcell.set_type(cell) self.assertIs(POINTER(cell), lpcell) + def test_set_type_updates_format(self): + # gh-142966: set_type should update StgInfo.format + # to match the element type's format + with self.assertWarns(DeprecationWarning): + lp = POINTER("node") + + class node(Structure): + _fields_ = [("value", c_int)] + + # Get the expected format before set_type + node_format = memoryview(node()).format + expected_format = "&" + node_format + + lp.set_type(node) + + # Create instance to check format via memoryview + n = node(42) + p = lp(n) + actual_format = memoryview(p).format + + # After set_type, the pointer's format should be "&" + self.assertEqual(actual_format, expected_format) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_ctypes/test_loading.py b/Lib/test/test_ctypes/test_loading.py index 13ed813ad98c31..343f6a07c0a32c 100644 --- a/Lib/test/test_ctypes/test_loading.py +++ b/Lib/test/test_ctypes/test_loading.py @@ -100,6 +100,20 @@ def test_load_ordinal_functions(self): self.assertRaises(AttributeError, dll.__getitem__, 1234) + @unittest.skipUnless(os.name == "nt", 'Windows-specific test') + def test_load_without_name_and_with_handle(self): + handle = ctypes.windll.kernel32._handle + lib = ctypes.WinDLL(name=None, handle=handle) + self.assertIs(handle, lib._handle) + + @unittest.skipIf(os.name == "nt", 'POSIX-specific test') + @unittest.skipIf(libc_name is None, 'could not find libc') + def test_load_without_name_and_with_handle_posix(self): + lib1 = CDLL(libc_name) + handle = lib1._handle + lib2 = CDLL(name=None, handle=handle) + self.assertIs(lib2._handle, handle) + @unittest.skipUnless(os.name == "nt", 'Windows-specific test') def test_1703286_A(self): # On winXP 64-bit, advapi32 loads at an address that does diff --git a/Lib/test/test_ctypes/test_macholib.py b/Lib/test/test_ctypes/test_macholib.py index 9d90617995623d..9a5e18ed589d44 100644 --- a/Lib/test/test_ctypes/test_macholib.py +++ b/Lib/test/test_ctypes/test_macholib.py @@ -108,5 +108,17 @@ def test_framework_info(self): d('P', 'F.framework/Versions/A/F_debug', 'F', 'A', 'debug')) +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + import ctypes.macholib + + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(ctypes.macholib, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ctypes/test_numbers.py b/Lib/test/test_ctypes/test_numbers.py index c57c58eb002328..b7df08f6079cd3 100644 --- a/Lib/test/test_ctypes/test_numbers.py +++ b/Lib/test/test_ctypes/test_numbers.py @@ -119,8 +119,11 @@ def test_floats(self): @unittest.skipUnless(hasattr(ctypes, "c_double_complex"), "requires C11 complex type") def test_complex(self): - for t in [ctypes.c_double_complex, ctypes.c_float_complex, - ctypes.c_longdouble_complex]: + for format, t in [ + ('Zd', ctypes.c_double_complex), + ('Zf', ctypes.c_float_complex), + ('Zg', ctypes.c_longdouble_complex), + ]: self.assertEqual(t(1).value, 1+0j) self.assertEqual(t(1.0).value, 1+0j) self.assertEqual(t(1+0.125j).value, 1+0.125j) @@ -128,6 +131,12 @@ def test_complex(self): self.assertEqual(t(FloatLike()).value, 2+0j) self.assertEqual(t(ComplexLike()).value, 1+1j) + prefix = '>' if sys.byteorder == 'big' else '<' + num = t(1.0) + self.assertEqual(memoryview(num).format, prefix + format) + array = (t * 3)() + self.assertEqual(memoryview(array).format, prefix + format) + @unittest.skipUnless(hasattr(ctypes, "c_double_complex"), "requires C11 complex type") def test_complex_round_trip(self): diff --git a/Lib/test/test_ctypes/test_parameters.py b/Lib/test/test_ctypes/test_parameters.py index 46f8ff93efa915..6dadb7b410d703 100644 --- a/Lib/test/test_ctypes/test_parameters.py +++ b/Lib/test/test_ctypes/test_parameters.py @@ -1,6 +1,7 @@ import sys import unittest import test.support +import ctypes from ctypes import (CDLL, PyDLL, ArgumentError, Structure, Array, Union, _Pointer, _SimpleCData, _CFuncPtr, @@ -247,6 +248,13 @@ def test_parameter_repr(self): self.assertRegex(repr(c_char_p.from_param(b'hihi')), r"^$") self.assertRegex(repr(c_wchar_p.from_param('hihi')), r"^$") self.assertRegex(repr(c_void_p.from_param(0x12)), r"^$") + if hasattr(ctypes, 'c_double_complex'): + self.assertRegex(repr(ctypes.c_double_complex.from_param(0)), + r"^$") + self.assertRegex(repr(ctypes.c_float_complex.from_param(0)), + r"^$") + self.assertRegex(repr(ctypes.c_longdouble_complex.from_param(0)), + r"^$") @test.support.cpython_only def test_from_param_result_refcount(self): diff --git a/Lib/test/test_ctypes/test_pointers.py b/Lib/test/test_ctypes/test_pointers.py index a8d243a45de0f4..771cc8fbe0ec93 100644 --- a/Lib/test/test_ctypes/test_pointers.py +++ b/Lib/test/test_ctypes/test_pointers.py @@ -403,6 +403,16 @@ class Cls(Structure): self.assertEqual(len(ws_typ), 0, ws_typ) self.assertEqual(len(ws_ptr), 0, ws_ptr) + def test_pointer_proto_missing_argtypes_error(self): + class BadType(ctypes._Pointer): + # _type_ is intentionally missing + pass + + func = ctypes.pythonapi.Py_GetVersion + func.argtypes = (BadType,) + + with self.assertRaises(ctypes.ArgumentError): + func(object()) class PointerTypeCacheTestCase(unittest.TestCase): # dummy tests to check warnings and base behavior diff --git a/Lib/test/test_ctypes/test_prototypes.py b/Lib/test/test_ctypes/test_prototypes.py index 63ae799ea86ab2..d976e8da0e2d30 100644 --- a/Lib/test/test_ctypes/test_prototypes.py +++ b/Lib/test/test_ctypes/test_prototypes.py @@ -72,6 +72,32 @@ def test_paramflags(self): self.assertEqual(func(None), None) self.assertEqual(func(input=None), None) + def test_invalid_paramflags(self): + proto = CFUNCTYPE(c_int, c_char_p) + with self.assertRaises(ValueError): + func = proto(("myprintf", testdll), ((1, "fmt"), (1, "arg1"))) + + def test_invalid_setattr_argtypes(self): + proto = CFUNCTYPE(c_int, c_char_p) + func = proto(("myprintf", testdll), ((1, "fmt"),)) + + with self.assertRaisesRegex(TypeError, "_argtypes_ must be a sequence of types"): + func.argtypes = 123 + self.assertEqual(func.argtypes, (c_char_p,)) + + with self.assertRaisesRegex(ValueError, "paramflags must have the same length as argtypes"): + func.argtypes = (c_char_p, c_int) + self.assertEqual(func.argtypes, (c_char_p,)) + + def test_paramflags_outarg(self): + proto = CFUNCTYPE(c_int, c_char_p, c_int) + with self.assertRaisesRegex(TypeError, "must be a pointer type"): + func = proto(("myprintf", testdll), ((1, "fmt"), (2, "out"))) + + proto = CFUNCTYPE(c_int, c_char_p, c_void_p) + func = proto(("myprintf", testdll), ((1, "fmt"), (2, "out"))) + with self.assertRaisesRegex(TypeError, "must be a pointer type"): + func.argtypes = (c_char_p, c_int) def test_int_pointer_arg(self): func = testdll._testfunc_p_p diff --git a/Lib/test/test_ctypes/test_struct_fields.py b/Lib/test/test_ctypes/test_struct_fields.py index b50bbcbb65c423..dc26e26d8a9fb1 100644 --- a/Lib/test/test_ctypes/test_struct_fields.py +++ b/Lib/test/test_ctypes/test_struct_fields.py @@ -130,6 +130,21 @@ class S(Structure): self.check_struct(S) self.assertEqual(S.largeField.bit_size, size * 8) + def test_bitfield_overflow_error_message(self): + with self.assertRaisesRegex( + ValueError, + r"bit field 'x' overflows its type \(2 \+ 7 > 8\)", + ): + CField( + name="x", + type=c_byte, + byte_size=1, + byte_offset=0, + index=0, + _internal_use=True, + bit_size=7, + bit_offset=2, + ) # __set__ and __get__ should raise a TypeError in case their self # argument is not a ctype instance. diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index e98a8f284cec9f..423247c92ce3c2 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -27,11 +27,21 @@ import dataclasses # Needed for the string "dataclasses.InitVar[int]" to work as an annotation. from test import support -from test.support import import_helper +from test.support import cpython_only, import_helper # Just any custom exception we can catch. class CustomError(Exception): pass + +class TestImportTime(unittest.TestCase): + + @cpython_only + def test_lazy_import(self): + import_helper.ensure_lazy_imports( + "dataclasses", {"inspect", "re", "copy"} + ) + + class TestCase(unittest.TestCase): def test_no_fields(self): @dataclass @@ -71,7 +81,7 @@ def test_field_repr(self): expected_output = "Field(name='id',type=None," \ f"default=1,default_factory={MISSING!r}," \ "init=True,repr=False,hash=None," \ - "compare=True,metadata=mappingproxy({})," \ + "compare=True,metadata=frozendict()," \ f"kw_only={MISSING!r}," \ "doc='Docstring'," \ "_field_type=None)" @@ -927,6 +937,20 @@ class C: validate_class(C) + def test_incomplete_annotations(self): + # gh-142214 + @dataclass + class C: + "doc" # needed because otherwise we fetch the annotations at the wrong time + x: int + + C.__annotate__ = lambda _: {} + + self.assertEqual( + annotationlib.get_annotations(C.__init__), + {"return": None} + ) + def test_missing_default(self): # Test that MISSING works the same as a default not being # specified. @@ -971,7 +995,7 @@ class D: self.assertNotIn('x', D.__dict__) def test_missing_repr(self): - self.assertIn('MISSING_TYPE object', repr(MISSING)) + self.assertEqual(repr(MISSING), 'MISSING') def test_dont_include_other_annotations(self): @dataclass @@ -1679,17 +1703,24 @@ class GroupTuple: class GroupDict: id: int users: Dict[str, User] + @dataclass + class GroupFrozenDict: + id: int + users: frozendict[str, User] a = User('Alice', 1) b = User('Bob', 2) gl = GroupList(0, [a, b]) gt = GroupTuple(0, (a, b)) gd = GroupDict(0, {'first': a, 'second': b}) + gfd = GroupFrozenDict(0, frozendict({'first': a, 'second': b})) self.assertEqual(asdict(gl), {'id': 0, 'users': [{'name': 'Alice', 'id': 1}, {'name': 'Bob', 'id': 2}]}) self.assertEqual(asdict(gt), {'id': 0, 'users': ({'name': 'Alice', 'id': 1}, {'name': 'Bob', 'id': 2})}) - self.assertEqual(asdict(gd), {'id': 0, 'users': {'first': {'name': 'Alice', 'id': 1}, - 'second': {'name': 'Bob', 'id': 2}}}) + expected_dict = {'id': 0, 'users': {'first': {'name': 'Alice', 'id': 1}, + 'second': {'name': 'Bob', 'id': 2}}} + self.assertEqual(asdict(gd), expected_dict) + self.assertEqual(asdict(gfd), expected_dict) def test_helper_asdict_builtin_object_containers(self): @dataclass @@ -1870,14 +1901,21 @@ class GroupTuple: class GroupDict: id: int users: Dict[str, User] + @dataclass + class GroupFrozenDict: + id: int + users: frozendict[str, User] a = User('Alice', 1) b = User('Bob', 2) gl = GroupList(0, [a, b]) gt = GroupTuple(0, (a, b)) gd = GroupDict(0, {'first': a, 'second': b}) + gfd = GroupFrozenDict(0, frozendict({'first': a, 'second': b})) self.assertEqual(astuple(gl), (0, [('Alice', 1), ('Bob', 2)])) self.assertEqual(astuple(gt), (0, (('Alice', 1), ('Bob', 2)))) - self.assertEqual(astuple(gd), (0, {'first': ('Alice', 1), 'second': ('Bob', 2)})) + d = {'first': ('Alice', 1), 'second': ('Bob', 2)} + self.assertEqual(astuple(gd), (0, d)) + self.assertEqual(astuple(gfd), (0, frozendict(d))) def test_helper_astuple_builtin_object_containers(self): @dataclass @@ -2281,6 +2319,20 @@ class C: self.assertDocStrEqual(C.__doc__, "C()") + def test_docstring_slotted(self): + @dataclass(slots=True) + class C: + x: int + + self.assertDocStrEqual(C.__doc__, "C(x:int)") + + def test_docstring_recursive(self): + @dataclass() + class C: + x: list[C] + + self.assertDocStrEqual(C.__doc__, "C(x:list[test.test_dataclasses.TestDocString.test_docstring_recursive..C])") + def test_docstring_one_field(self): @dataclass class C: @@ -2471,6 +2523,149 @@ def __init__(self, a): self.assertEqual(D(5).a, 10) +class TestInitAnnotate(unittest.TestCase): + # Tests for the generated __annotate__ function for __init__ + # See: https://github.com/python/cpython/issues/137530 + + def test_annotate_function(self): + # No forward references + @dataclass + class A: + a: int + + value_annos = annotationlib.get_annotations(A.__init__, format=annotationlib.Format.VALUE) + forwardref_annos = annotationlib.get_annotations(A.__init__, format=annotationlib.Format.FORWARDREF) + string_annos = annotationlib.get_annotations(A.__init__, format=annotationlib.Format.STRING) + + self.assertEqual(value_annos, {'a': int, 'return': None}) + self.assertEqual(forwardref_annos, {'a': int, 'return': None}) + self.assertEqual(string_annos, {'a': 'int', 'return': 'None'}) + + self.assertTrue(getattr(A.__init__.__annotate__, "__generated_by_dataclasses__")) + + def test_annotate_function_forwardref(self): + # With forward references + @dataclass + class B: + b: undefined + + # VALUE annotations should raise while unresolvable + with self.assertRaises(NameError): + _ = annotationlib.get_annotations(B.__init__, format=annotationlib.Format.VALUE) + + forwardref_annos = annotationlib.get_annotations(B.__init__, format=annotationlib.Format.FORWARDREF) + string_annos = annotationlib.get_annotations(B.__init__, format=annotationlib.Format.STRING) + + self.assertEqual(forwardref_annos, {'b': support.EqualToForwardRef('undefined', owner=B, is_class=True), 'return': None}) + self.assertEqual(string_annos, {'b': 'undefined', 'return': 'None'}) + + # Now VALUE and FORWARDREF should resolve, STRING should be unchanged + undefined = int + + value_annos = annotationlib.get_annotations(B.__init__, format=annotationlib.Format.VALUE) + forwardref_annos = annotationlib.get_annotations(B.__init__, format=annotationlib.Format.FORWARDREF) + string_annos = annotationlib.get_annotations(B.__init__, format=annotationlib.Format.STRING) + + self.assertEqual(value_annos, {'b': int, 'return': None}) + self.assertEqual(forwardref_annos, {'b': int, 'return': None}) + self.assertEqual(string_annos, {'b': 'undefined', 'return': 'None'}) + + def test_annotate_function_init_false(self): + # Check `init=False` attributes don't get into the annotations of the __init__ function + @dataclass + class C: + c: str = field(init=False) + + self.assertEqual(annotationlib.get_annotations(C.__init__), {'return': None}) + + def test_annotate_function_contains_forwardref(self): + # Check string annotations on objects containing a ForwardRef + @dataclass + class D: + d: list[undefined] + + with self.assertRaises(NameError): + annotationlib.get_annotations(D.__init__) + + self.assertEqual( + annotationlib.get_annotations(D.__init__, format=annotationlib.Format.FORWARDREF), + {"d": list[support.EqualToForwardRef("undefined", is_class=True, owner=D)], "return": None} + ) + + self.assertEqual( + annotationlib.get_annotations(D.__init__, format=annotationlib.Format.STRING), + {"d": "list[undefined]", "return": "None"} + ) + + # Now test when it is defined + undefined = str + + # VALUE should now resolve + self.assertEqual( + annotationlib.get_annotations(D.__init__), + {"d": list[str], "return": None} + ) + + self.assertEqual( + annotationlib.get_annotations(D.__init__, format=annotationlib.Format.FORWARDREF), + {"d": list[str], "return": None} + ) + + self.assertEqual( + annotationlib.get_annotations(D.__init__, format=annotationlib.Format.STRING), + {"d": "list[undefined]", "return": "None"} + ) + + def test_annotate_function_not_replaced(self): + # Check that __annotate__ is not replaced on non-generated __init__ functions + @dataclass(slots=True) + class E: + x: str + def __init__(self, x: int) -> None: + self.x = x + + self.assertEqual( + annotationlib.get_annotations(E.__init__), {"x": int, "return": None} + ) + + self.assertFalse(hasattr(E.__init__.__annotate__, "__generated_by_dataclasses__")) + + def test_slots_true_init_false(self): + # Test that slots=True and init=False work together and + # that __annotate__ is not added to __init__. + + @dataclass(slots=True, init=False) + class F: + x: int + + f = F() + f.x = 10 + self.assertEqual(f.x, 10) + + self.assertFalse(hasattr(F.__init__, "__annotate__")) + + def test_init_false_forwardref(self): + # Test forward references in fields not required for __init__ annotations. + + # At the moment this raises a NameError for VALUE annotations even though the + # undefined annotation is not required for the __init__ annotations. + # Ideally this will be fixed but currently there is no good way to resolve this + + @dataclass + class F: + not_in_init: list[undefined] = field(init=False, default=None) + in_init: int + + annos = annotationlib.get_annotations(F.__init__, format=annotationlib.Format.FORWARDREF) + self.assertEqual( + annos, + {"in_init": int, "return": None}, + ) + + with self.assertRaises(NameError): + annos = annotationlib.get_annotations(F.__init__) # NameError on not_in_init + + class TestRepr(unittest.TestCase): def test_repr(self): @dataclass @@ -2597,6 +2792,55 @@ def __eq__(self, other): self.assertEqual(C(1), 5) self.assertNotEqual(C(1), 1) + def test_eq_field_by_field(self): + @dataclasses.dataclass + class Point: + x: int + y: int + + p1 = Point(1, 2) + p2 = Point(1, 2) + p3 = Point(2, 1) + self.assertEqual(p1, p2) + self.assertNotEqual(p1, p3) + + def test_eq_type_check(self): + @dataclasses.dataclass + class A: + x: int + + @dataclasses.dataclass + class B: + x: int + + a = A(1) + b = B(1) + self.assertNotEqual(a, b) + + def test_eq_custom_field(self): + class AlwaysEqual(int): + def __eq__(self, other): + return True + + @dataclasses.dataclass + class Foo: + x: AlwaysEqual + y: int + + f1 = Foo(AlwaysEqual(1), 2) + f2 = Foo(AlwaysEqual(2), 2) + self.assertEqual(f1, f2) + + def test_eq_nan_field(self): + @dataclasses.dataclass + class D: + x: float + + nan = float('nan') + d1 = D(nan) + d2 = D(nan) + self.assertNotEqual(d1, d2) + class TestOrdering(unittest.TestCase): def test_functools_total_ordering(self): @@ -2895,29 +3139,41 @@ class C(base): class TestFrozen(unittest.TestCase): + # Some tests have a subtest with a slotted dataclass. + # See https://github.com/python/cpython/issues/105936 for the reasons. + def test_frozen(self): - @dataclass(frozen=True) - class C: - i: int + for slots in (False, True): + with self.subTest(slots=slots): - c = C(10) - self.assertEqual(c.i, 10) - with self.assertRaises(FrozenInstanceError): - c.i = 5 - self.assertEqual(c.i, 10) + @dataclass(frozen=True, slots=slots) + class C: + i: int + + c = C(10) + self.assertEqual(c.i, 10) + with self.assertRaises(FrozenInstanceError): + c.i = 5 + self.assertEqual(c.i, 10) + with self.assertRaises(FrozenInstanceError): + del c.i + self.assertEqual(c.i, 10) def test_frozen_empty(self): - @dataclass(frozen=True) - class C: - pass + for slots in (False, True): + with self.subTest(slots=slots): - c = C() - self.assertNotHasAttr(c, 'i') - with self.assertRaises(FrozenInstanceError): - c.i = 5 - self.assertNotHasAttr(c, 'i') - with self.assertRaises(FrozenInstanceError): - del c.i + @dataclass(frozen=True, slots=slots) + class C: + pass + + c = C() + self.assertNotHasAttr(c, 'i') + with self.assertRaises(FrozenInstanceError): + c.i = 5 + self.assertNotHasAttr(c, 'i') + with self.assertRaises(FrozenInstanceError): + del c.i def test_inherit(self): @dataclass(frozen=True) @@ -3113,41 +3369,43 @@ class D(I): d.i = 5 def test_non_frozen_normal_derived(self): - # See bpo-32953. + # See bpo-32953 and https://github.com/python/cpython/issues/105936 + for slots in (False, True): + with self.subTest(slots=slots): - @dataclass(frozen=True) - class D: - x: int - y: int = 10 + @dataclass(frozen=True, slots=slots) + class D: + x: int + y: int = 10 - class S(D): - pass - - s = S(3) - self.assertEqual(s.x, 3) - self.assertEqual(s.y, 10) - s.cached = True - - # But can't change the frozen attributes. - with self.assertRaises(FrozenInstanceError): - s.x = 5 - with self.assertRaises(FrozenInstanceError): - s.y = 5 - self.assertEqual(s.x, 3) - self.assertEqual(s.y, 10) - self.assertEqual(s.cached, True) + class S(D): + pass - with self.assertRaises(FrozenInstanceError): - del s.x - self.assertEqual(s.x, 3) - with self.assertRaises(FrozenInstanceError): - del s.y - self.assertEqual(s.y, 10) - del s.cached - self.assertNotHasAttr(s, 'cached') - with self.assertRaises(AttributeError) as cm: - del s.cached - self.assertNotIsInstance(cm.exception, FrozenInstanceError) + s = S(3) + self.assertEqual(s.x, 3) + self.assertEqual(s.y, 10) + s.cached = True + + # But can't change the frozen attributes. + with self.assertRaises(FrozenInstanceError): + s.x = 5 + with self.assertRaises(FrozenInstanceError): + s.y = 5 + self.assertEqual(s.x, 3) + self.assertEqual(s.y, 10) + self.assertEqual(s.cached, True) + + with self.assertRaises(FrozenInstanceError): + del s.x + self.assertEqual(s.x, 3) + with self.assertRaises(FrozenInstanceError): + del s.y + self.assertEqual(s.y, 10) + del s.cached + self.assertNotHasAttr(s, 'cached') + with self.assertRaises(AttributeError) as cm: + del s.cached + self.assertNotIsInstance(cm.exception, FrozenInstanceError) def test_non_frozen_normal_derived_from_empty_frozen(self): @dataclass(frozen=True) @@ -3804,6 +4062,57 @@ class WithCorrectSuper(CorrectSuper): # that we create internally. self.assertEqual(CorrectSuper.args, ["default", "default"]) + def test_original_class_is_gced(self): + # gh-135228: Make sure when we replace the class with slots=True, the original class + # gets garbage collected. + def make_simple(): + @dataclass(slots=True) + class SlotsTest: + pass + + return SlotsTest + + # See https://github.com/python/cpython/issues/135228#issuecomment-3755979059 + def make_frozen(): + @dataclass(frozen=True, slots=True) + class SlotsTest: + pass + + return SlotsTest + + def make_with_annotations(): + @dataclass(slots=True) + class SlotsTest: + x: int + + return SlotsTest + + def make_with_annotations_and_method(): + @dataclass(slots=True) + class SlotsTest: + x: int + + def method(self) -> int: + return self.x + + return SlotsTest + + def make_with_forwardref(): + @dataclass(slots=True) + class SlotsTest: + x: undefined + y: list[undefined] + + return SlotsTest + + for make in (make_simple, make_frozen, make_with_annotations, make_with_annotations_and_method, make_with_forwardref): + with self.subTest(make=make): + C = make() + support.gc_collect() + candidates = [cls for cls in object.__subclasses__() if cls.__name__ == 'SlotsTest' + and cls.__firstlineno__ == make.__code__.co_firstlineno + 1] + self.assertEqual(candidates, [C]) + class TestDescriptors(unittest.TestCase): def test_set_name(self): @@ -4093,10 +4402,17 @@ def test_classvar_module_level_import(self): from test.test_dataclasses import dataclass_module_1_str from test.test_dataclasses import dataclass_module_2 from test.test_dataclasses import dataclass_module_2_str - - for m in (dataclass_module_1, dataclass_module_1_str, - dataclass_module_2, dataclass_module_2_str, - ): + from test.test_dataclasses import dataclass_module_3 + from test.test_dataclasses import dataclass_module_3_str + from test.test_dataclasses import dataclass_module_4 + from test.test_dataclasses import dataclass_module_4_str + + for m in ( + dataclass_module_1, dataclass_module_1_str, + dataclass_module_2, dataclass_module_2_str, + dataclass_module_3, dataclass_module_3_str, + dataclass_module_4, dataclass_module_4_str, + ): with self.subTest(m=m): # There's a difference in how the ClassVars are # interpreted when using string annotations or @@ -4400,6 +4716,14 @@ def custom_dataclass(cls, *args, **kwargs): self.assertEqual(c.x, 10) self.assertEqual(c.__custom__, True) + def test_empty_annotation_string(self): + @dataclass + class DataclassWithEmptyTypeAnnotation: + x: "" + + c = DataclassWithEmptyTypeAnnotation(10) + self.assertEqual(c.x, 10) + class TestReplace(unittest.TestCase): def test(self): @@ -5014,6 +5338,15 @@ class A: self.assertEqual(len(fs), 1) self.assertEqual(fs[0].name, 'x') + def test_makedataclass_with_qualname(self): + A = make_dataclass("A", ['a'], qualname='ClassA') + self.assertEqual(A.__qualname__, 'ClassA') + + B = make_dataclass("B", ['b'], qualname='module1.ClassB') + self.assertEqual(B.__qualname__, 'module1.ClassB') + + C = make_dataclass("C", ['c']) + self.assertEqual(C.__qualname__, 'C') class TestZeroArgumentSuperWithSlots(unittest.TestCase): def test_zero_argument_super(self): @@ -5139,5 +5472,51 @@ def cls(self): # one will be keeping a reference to the underlying class A. self.assertIs(A().cls(), B) + def test_empty_class_cell(self): + # gh-148947: Make sure that we explicitly handle the empty class cell. + def maker(): + if False: + __class__ = 42 + + def method(self): + return __class__ + return method + + from dataclasses import dataclass + + @dataclass(slots=True) + class X: + a: int + + meth = maker() + + with self.assertRaisesRegex(NameError, '__class__'): + X(1).meth() + + def test_class_cell_from_other_class(self): + # This test fails without the "is oldcls" check in + # _update_func_cell_for__class__. + class Base: + def meth(self): + return "Base" + + class Child(Base): + def meth(self): + return super().meth() + " Child" + + @dataclass(slots=True) + class DC(Child): + a: int + + meth = Child.meth + + closure = DC.meth.__closure__ + self.assertEqual(len(closure), 1) + self.assertIs(closure[0].cell_contents, Child) + + self.assertEqual(DC(1).meth(), "Base Child") + + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_dataclasses/_types_proxy.py b/Lib/test/test_dataclasses/_types_proxy.py new file mode 100644 index 00000000000000..f4aaeef7aec59d --- /dev/null +++ b/Lib/test/test_dataclasses/_types_proxy.py @@ -0,0 +1,8 @@ +# We need this to test a case when a type +# is imported via some other package, +# like ClassVar from typing_extensions instead of typing. +# https://github.com/python/cpython/issues/133956 +from typing import ClassVar +from dataclasses import InitVar + +__all__ = ["ClassVar", "InitVar"] diff --git a/Lib/test/test_dataclasses/dataclass_module_3.py b/Lib/test/test_dataclasses/dataclass_module_3.py new file mode 100644 index 00000000000000..74abc091f35acd --- /dev/null +++ b/Lib/test/test_dataclasses/dataclass_module_3.py @@ -0,0 +1,32 @@ +#from __future__ import annotations +USING_STRINGS = False + +# dataclass_module_3.py and dataclass_module_3_str.py are identical +# except only the latter uses string annotations. + +from dataclasses import dataclass +import test.test_dataclasses._types_proxy as tp + +T_CV2 = tp.ClassVar[int] +T_CV3 = tp.ClassVar + +T_IV2 = tp.InitVar[int] +T_IV3 = tp.InitVar + +@dataclass +class CV: + T_CV4 = tp.ClassVar + cv0: tp.ClassVar[int] = 20 + cv1: tp.ClassVar = 30 + cv2: T_CV2 + cv3: T_CV3 + not_cv4: T_CV4 # When using string annotations, this field is not recognized as a ClassVar. + +@dataclass +class IV: + T_IV4 = tp.InitVar + iv0: tp.InitVar[int] + iv1: tp.InitVar + iv2: T_IV2 + iv3: T_IV3 + not_iv4: T_IV4 # When using string annotations, this field is not recognized as an InitVar. diff --git a/Lib/test/test_dataclasses/dataclass_module_3_str.py b/Lib/test/test_dataclasses/dataclass_module_3_str.py new file mode 100644 index 00000000000000..49e5fca61831b6 --- /dev/null +++ b/Lib/test/test_dataclasses/dataclass_module_3_str.py @@ -0,0 +1,32 @@ +from __future__ import annotations +USING_STRINGS = True + +# dataclass_module_3.py and dataclass_module_3_str.py are identical +# except only the latter uses string annotations. + +from dataclasses import dataclass +import test.test_dataclasses._types_proxy as tp + +T_CV2 = tp.ClassVar[int] +T_CV3 = tp.ClassVar + +T_IV2 = tp.InitVar[int] +T_IV3 = tp.InitVar + +@dataclass +class CV: + T_CV4 = tp.ClassVar + cv0: tp.ClassVar[int] = 20 + cv1: tp.ClassVar = 30 + cv2: T_CV2 + cv3: T_CV3 + not_cv4: T_CV4 # When using string annotations, this field is not recognized as a ClassVar. + +@dataclass +class IV: + T_IV4 = tp.InitVar + iv0: tp.InitVar[int] + iv1: tp.InitVar + iv2: T_IV2 + iv3: T_IV3 + not_iv4: T_IV4 # When using string annotations, this field is not recognized as an InitVar. diff --git a/Lib/test/test_dataclasses/dataclass_module_4.py b/Lib/test/test_dataclasses/dataclass_module_4.py new file mode 100644 index 00000000000000..7e0c8a18356590 --- /dev/null +++ b/Lib/test/test_dataclasses/dataclass_module_4.py @@ -0,0 +1,38 @@ +#from __future__ import annotations +USING_STRINGS = False + +# dataclass_module_4.py and dataclass_module_4_str.py are identical +# except only the latter uses string annotations. + +from dataclasses import dataclass +import dataclasses +import typing + +class TypingProxy: + class Nested: + ClassVar = typing.ClassVar + InitVar = dataclasses.InitVar + +T_CV2 = TypingProxy.Nested.ClassVar[int] +T_CV3 = TypingProxy.Nested.ClassVar + +T_IV2 = TypingProxy.Nested.InitVar[int] +T_IV3 = TypingProxy.Nested.InitVar + +@dataclass +class CV: + T_CV4 = TypingProxy.Nested.ClassVar + cv0: TypingProxy.Nested.ClassVar[int] = 20 + cv1: TypingProxy.Nested.ClassVar = 30 + cv2: T_CV2 + cv3: T_CV3 + not_cv4: T_CV4 # When using string annotations, this field is not recognized as a ClassVar. + +@dataclass +class IV: + T_IV4 = TypingProxy.Nested.InitVar + iv0: TypingProxy.Nested.InitVar[int] + iv1: TypingProxy.Nested.InitVar + iv2: T_IV2 + iv3: T_IV3 + not_iv4: T_IV4 # When using string annotations, this field is not recognized as an InitVar. diff --git a/Lib/test/test_dataclasses/dataclass_module_4_str.py b/Lib/test/test_dataclasses/dataclass_module_4_str.py new file mode 100644 index 00000000000000..876f3dcf7c88fa --- /dev/null +++ b/Lib/test/test_dataclasses/dataclass_module_4_str.py @@ -0,0 +1,38 @@ +from __future__ import annotations +USING_STRINGS = True + +# dataclass_module_4.py and dataclass_module_4_str.py are identical +# except only the latter uses string annotations. + +from dataclasses import dataclass +import dataclasses +import typing + +class TypingProxy: + class Nested: + ClassVar = typing.ClassVar + InitVar = dataclasses.InitVar + +T_CV2 = TypingProxy.Nested.ClassVar[int] +T_CV3 = TypingProxy.Nested.ClassVar + +T_IV2 = TypingProxy.Nested.InitVar[int] +T_IV3 = TypingProxy.Nested.InitVar + +@dataclass +class CV: + T_CV4 = TypingProxy.Nested.ClassVar + cv0: TypingProxy.Nested.ClassVar[int] = 20 + cv1: TypingProxy.Nested.ClassVar = 30 + cv2: T_CV2 + cv3: T_CV3 + not_cv4: T_CV4 # When using string annotations, this field is not recognized as a ClassVar. + +@dataclass +class IV: + T_IV4 = TypingProxy.Nested.InitVar + iv0: TypingProxy.Nested.InitVar[int] + iv1: TypingProxy.Nested.InitVar + iv2: T_IV2 + iv3: T_IV3 + not_iv4: T_IV4 # When using string annotations, this field is not recognized as an InitVar. diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 005187f13e665f..137c8d2686c224 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -18,16 +18,19 @@ def load_tests(loader, tests, pattern): finally: # XXX: import_fresh_module() is supposed to leave sys.module cache untouched, # XXX: but it does not, so we have to cleanup ourselves. - for modname in ['datetime', '_datetime', '_strptime']: + for modname in ['datetime', '_datetime', '_pydatetime', '_strptime']: sys.modules.pop(modname, None) test_modules = [pure_tests, fast_tests] test_suffixes = ["_Pure", "_Fast"] + # XXX(gb) First run all the _Pure tests, then all the _Fast tests. You might # not believe this, but in spite of all the sys.modules trickery running a _Pure # test last will leave a mix of pure and native datetime stuff lying around. for module, suffix in zip(test_modules, test_suffixes): test_classes = [] + if module is None: + continue for name, cls in module.__dict__.items(): if not isinstance(cls, type): continue @@ -48,8 +51,8 @@ def setUpClass(cls_, module=module): cls_._save_sys_modules = sys.modules.copy() sys.modules[TESTS] = module sys.modules['datetime'] = module.datetime_module - if hasattr(module, '_pydatetime'): - sys.modules['_pydatetime'] = module._pydatetime + sys.modules['_pydatetime'] = module._pydatetime + sys.modules['_datetime'] = module._datetime sys.modules['_strptime'] = module._strptime super().setUpClass() diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py index e0b988b7b95bbd..66268c42a300b5 100644 --- a/Lib/test/test_dbm_gnu.py +++ b/Lib/test/test_dbm_gnu.py @@ -74,12 +74,12 @@ def test_flags(self): # Test the flag parameter open() by trying all supported flag modes. all = set(gdbm.open_flags) # Test standard flags (presumably "crwn"). - modes = all - set('fsum') + modes = all - set('fsu') for mode in sorted(modes): # put "c" mode first self.g = gdbm.open(filename, mode) self.g.close() - # Test additional flags (presumably "fsum"). + # Test additional flags (presumably "fsu"). flags = all - set('crwn') for mode in modes: for flag in flags: @@ -217,29 +217,6 @@ def test_localized_error(self): create_empty_file(os.path.join(d, 'test')) self.assertRaises(gdbm.error, gdbm.open, filename, 'r') - @unittest.skipUnless('m' in gdbm.open_flags, "requires 'm' in open_flags") - def test_nommap_no_crash(self): - self.g = g = gdbm.open(filename, 'nm') - os.truncate(filename, 0) - - g.get(b'a', b'c') - g.keys() - g.firstkey() - g.nextkey(b'a') - with self.assertRaises(KeyError): - g[b'a'] - with self.assertRaises(gdbm.error): - len(g) - - with self.assertRaises(gdbm.error): - g[b'a'] = b'c' - with self.assertRaises(gdbm.error): - del g[b'a'] - with self.assertRaises(gdbm.error): - g.setdefault(b'a', b'c') - with self.assertRaises(gdbm.error): - g.reorganize() - if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_dbm_sqlite3.py b/Lib/test/test_dbm_sqlite3.py index 9216da8a63f957..f367a98865d4aa 100644 --- a/Lib/test/test_dbm_sqlite3.py +++ b/Lib/test/test_dbm_sqlite3.py @@ -1,3 +1,5 @@ +import os +import stat import sys import unittest from contextlib import closing @@ -14,6 +16,11 @@ from dbm.sqlite3 import _normalize_uri +root_in_posix = False +if hasattr(os, 'geteuid'): + root_in_posix = (os.geteuid() == 0) + + class _SQLiteDbmTests(unittest.TestCase): def setUp(self): @@ -90,6 +97,50 @@ def test_readonly_iter(self): self.assertEqual([k for k in self.db], [b"key1", b"key2"]) +@unittest.skipIf(root_in_posix, "test is meanless with root privilege") +class ReadOnlyFilesystem(unittest.TestCase): + + def setUp(self): + self.test_dir = os_helper.TESTFN + self.addCleanup(os_helper.rmtree, self.test_dir) + os.mkdir(self.test_dir) + self.db_path = os.path.join(self.test_dir, "test.db") + + db = dbm_sqlite3.open(self.db_path, "c") + db[b"key"] = b"value" + db.close() + + def test_readonly_file_read(self): + os.chmod(self.db_path, stat.S_IREAD) + with dbm_sqlite3.open(self.db_path, "r") as db: + self.assertEqual(db[b"key"], b"value") + + def test_readonly_file_write(self): + os.chmod(self.db_path, stat.S_IREAD) + with dbm_sqlite3.open(self.db_path, "w") as db: + with self.assertRaises(dbm_sqlite3.error): + db[b"newkey"] = b"newvalue" + + def test_readonly_dir_read(self): + os.chmod(self.test_dir, stat.S_IREAD | stat.S_IEXEC) + with dbm_sqlite3.open(self.db_path, "r") as db: + self.assertEqual(db[b"key"], b"value") + + def test_readonly_dir_write(self): + os.chmod(self.test_dir, stat.S_IREAD | stat.S_IEXEC) + with dbm_sqlite3.open(self.db_path, "w") as db: + try: + db[b"newkey"] = b"newvalue" + modified = True # on Windows and macOS + except dbm_sqlite3.error: + modified = False + with dbm_sqlite3.open(self.db_path, "r") as db: + if modified: + self.assertEqual(db[b"newkey"], b"newvalue") + else: + self.assertNotIn(b"newkey", db) + + class ReadWrite(_SQLiteDbmTests): def setUp(self): diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 08a8f4c3b36bd6..fe8c8ce12da0bf 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -3963,15 +3963,21 @@ def test_flag_comparisons(self): d.update(c.flags) self.assertEqual(d, c.flags) self.assertEqual(c.flags, d) + self.assertEqual(frozendict(d), c.flags) + self.assertEqual(c.flags, frozendict(d)) d[Inexact] = True self.assertNotEqual(d, c.flags) self.assertNotEqual(c.flags, d) + self.assertNotEqual(frozendict(d), c.flags) + self.assertNotEqual(c.flags, frozendict(d)) # Invalid SignalDict d = {Inexact:False} self.assertNotEqual(d, c.flags) self.assertNotEqual(c.flags, d) + self.assertNotEqual(frozendict(d), c.flags) + self.assertNotEqual(c.flags, frozendict(d)) d = ["xyz"] self.assertNotEqual(d, c.flags) @@ -4474,7 +4480,7 @@ def test_module_attributes(self): self.assertTrue(C.HAVE_THREADS is True or C.HAVE_THREADS is False) self.assertTrue(P.HAVE_THREADS is True or P.HAVE_THREADS is False) - self.assertEqual(C.__version__, P.__version__) + self.assertEqual(C.SPEC_VERSION, P.SPEC_VERSION) self.assertLessEqual(set(dir(C)), set(dir(P))) self.assertEqual([n for n in dir(C) if n[:2] != '__'], sorted(P.__all__)) @@ -5929,6 +5935,23 @@ def doit(ty): doit('Context') +class TestModule: + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(self.decimal, "__version__") + self.assertEqual(cm.filename, __file__) + + +@requires_cdecimal +class CTestModule(TestModule, unittest.TestCase): + decimal = C +class PyTestModule(TestModule, unittest.TestCase): + decimal = P + + def load_tests(loader, tests, pattern): if TODO_TESTS is not None: # Run only Arithmetic tests diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index bdbe9b81e8fb3f..cc78f01e3e2ebd 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -186,5 +186,50 @@ def test_union(self): with self.assertRaises(TypeError): i |= None + # frozendict + i_fd = i | frozendict(s) + self.assertIs(type(i_fd), defaultdict) + self.assertIs(i_fd.default_factory, int) + self.assertDictEqual(i_fd, {1: "one", 2: 2, 0: "zero"}) + self.assertEqual(list(i_fd), [1, 2, 0]) + + fd_i = frozendict(s) | i + self.assertIs(type(fd_i), frozendict) + self.assertEqual(fd_i, {1: "one", 2: 2, 0: "zero"}) + self.assertEqual(list(fd_i), [0, 1, 2]) + + def test_factory_conflict_with_set_value(self): + key = "conflict_test" + count = 0 + + def default_factory(): + nonlocal count + count += 1 + local_count = count + if count == 1: + test_dict[key] + return local_count + + test_dict = defaultdict(default_factory) + + self.assertEqual(count, 0) + self.assertEqual(test_dict[key], 2) + self.assertEqual(count, 2) + + def test_repr_recursive_factory(self): + # gh-145492: defaultdict.__repr__ should not cause infinite recursion + # when the factory's __repr__ calls repr() on the defaultdict. + class ProblematicFactory: + def __call__(self): + return {} + def __repr__(self): + repr(dd) + return f"ProblematicFactory for {dd}" + + dd = defaultdict(ProblematicFactory()) + # Should not raise RecursionError + r = repr(dd) + self.assertIn("ProblematicFactory for", r) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 4e1a489205a685..3c45032cda9138 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -287,6 +287,22 @@ def test_index(self): else: self.assertEqual(d.index(element, start, stop), target) + # Test stop argument + for elem in d: + index = d.index(elem) + self.assertEqual( + index, + d.index(elem, 0), + ) + self.assertEqual( + index, + d.index(elem, 0, len(d)), + ) + self.assertEqual( + index, + d.index(elem, 0, len(d) + 100), + ) + # Test large start argument d = deque(range(0, 10000, 10)) for step in range(100): diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 8da6647c3f71fc..8a8e70214e27ae 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1320,6 +1320,18 @@ class X(object): with self.assertRaisesRegex(AttributeError, "'X' object has no attribute 'a'"): X().a + def test_slots_after_items(self): + class C(tuple): + __slots__ = ['a'] + x = C((1, 2, 3)) + self.assertNotHasAttr(x, "__dict__") + self.assertNotHasAttr(x, "a") + x.a = 42 + self.assertEqual(x.a, 42) + del x.a + self.assertNotHasAttr(x, "a") + self.assertEqual(x, (1, 2, 3)) + def test_slots_special(self): # Testing __dict__ and __weakref__ in __slots__... class D(object): @@ -1329,18 +1341,17 @@ class D(object): self.assertNotHasAttr(a, "__weakref__") a.foo = 42 self.assertEqual(a.__dict__, {"foo": 42}) + with self.assertRaises(TypeError): + weakref.ref(a) class W(object): __slots__ = ["__weakref__"] a = W() self.assertHasAttr(a, "__weakref__") self.assertNotHasAttr(a, "__dict__") - try: + with self.assertRaises(AttributeError): a.foo = 42 - except AttributeError: - pass - else: - self.fail("shouldn't be allowed to set a.foo") + self.assertIs(weakref.ref(a)(), a) class C1(W, D): __slots__ = [] @@ -1349,6 +1360,7 @@ class C1(W, D): self.assertHasAttr(a, "__weakref__") a.foo = 42 self.assertEqual(a.__dict__, {"foo": 42}) + self.assertIs(weakref.ref(a)(), a) class C2(D, W): __slots__ = [] @@ -1357,6 +1369,80 @@ class C2(D, W): self.assertHasAttr(a, "__weakref__") a.foo = 42 self.assertEqual(a.__dict__, {"foo": 42}) + self.assertIs(weakref.ref(a)(), a) + + @unittest.skipIf(_testcapi is None, 'need the _testcapi module') + def test_slots_special_before_items(self): + class D(_testcapi.HeapCCollection): + __slots__ = ["__dict__"] + a = D(1, 2, 3) + self.assertHasAttr(a, "__dict__") + self.assertNotHasAttr(a, "__weakref__") + a.foo = 42 + self.assertEqual(a.__dict__, {"foo": 42}) + with self.assertRaises(TypeError): + weakref.ref(a) + del a.__dict__ + self.assertNotHasAttr(a, "foo") + self.assertEqual(a.__dict__, {}) + self.assertEqual(list(a), [1, 2, 3]) + + class W(_testcapi.HeapCCollection): + __slots__ = ["__weakref__"] + a = W(1, 2, 3) + self.assertHasAttr(a, "__weakref__") + self.assertNotHasAttr(a, "__dict__") + with self.assertRaises(AttributeError): + a.foo = 42 + self.assertIs(weakref.ref(a)(), a) + + with self.assertRaises(TypeError): + class X(_testcapi.HeapCCollection): + __slots__ = ['x'] + + with self.assertRaises(TypeError): + class X(_testcapi.HeapCCollection): + __slots__ = ['__dict__', 'x'] + + @support.subTests(('base', 'arg'), [ + (tuple, (1, 2, 3)), + (int, 9876543210**2), + (bytes, b'ab'), + ]) + def test_slots_special_after_items(self, base, arg): + class D(base): + __slots__ = ["__dict__"] + a = D(arg) + self.assertHasAttr(a, "__dict__") + self.assertNotHasAttr(a, "__weakref__") + a.foo = 42 + self.assertEqual(a.__dict__, {"foo": 42}) + with self.assertRaises(TypeError): + weakref.ref(a) + del a.__dict__ + self.assertNotHasAttr(a, "foo") + self.assertEqual(a.__dict__, {}) + self.assertEqual(a, base(arg)) + + class W(base): + __slots__ = ["__weakref__"] + a = W(arg) + self.assertHasAttr(a, "__weakref__") + self.assertNotHasAttr(a, "__dict__") + with self.assertRaises(AttributeError): + a.foo = 42 + self.assertIs(weakref.ref(a)(), a) + self.assertEqual(a, base(arg)) + + @support.subTests('base', [int, bytes] + + ([_testcapi.HeapCCollection] if _testcapi else [])) + def test_unsupported_slots(self, base): + with self.assertRaises(TypeError): + class X(base): + __slots__ = ['x'] + with self.assertRaises(TypeError): + class X(base): + __slots__ = ['__dict__', 'x'] def test_slots_special2(self): # Testing __qualname__ and __classcell__ in __slots__ @@ -1641,6 +1727,18 @@ def annotated(cls) -> int: pass del method.__annotate__ self.assertIs(method.__annotate__, original_annotate) + def test_classmethod_without_dict_access(self): + class Spam: + @classmethod + def method(cls, x, y): + pass + + obj = Spam.__dict__['method'] + self.assertIsInstance(obj, classmethod) + self.assertEqual(obj.__annotations__, {}) + self.assertEqual(obj.__name__, 'method') + self.assertEqual(obj.__module__, __name__) + def test_staticmethod_annotations_without_dict_access(self): # gh-125017: this used to crash class Spam: @@ -1651,15 +1749,8 @@ def __new__(cls, x, y): obj = Spam.__dict__['__new__'] self.assertIsInstance(obj, staticmethod) self.assertEqual(obj.__annotations__, {}) - - @support.refcount_test - def test_refleaks_in_classmethod___init__(self): - gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount') - cm = classmethod(None) - refs_before = gettotalrefcount() - for i in range(100): - cm.__init__(None) - self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) + self.assertEqual(obj.__name__, '__new__') + self.assertEqual(obj.__module__, __name__) @support.impl_detail("the module 'xxsubtype' is internal") @unittest.skipIf(xxsubtype is None, "requires xxsubtype module") @@ -1712,6 +1803,28 @@ class SubSpam(spam.spamlist): pass spam_cm.__get__(None, list) self.assertEqual(str(cm.exception), expected_errmsg) + @support.cpython_only + def test_method_get_meth_method_invalid_type(self): + # gh-146615: method_get() for METH_METHOD descriptors used to pass + # Py_TYPE(type)->tp_name as the %V fallback instead of the separate + # %s argument, causing a missing argument for %s and a crash. + # Verify the error message is correct when __get__() is called with a + # non-type as the second argument. + # + # METH_METHOD|METH_FASTCALL|METH_KEYWORDS is the only flag combination + # that enters the affected branch in method_get(). + import io + + obj = io.StringIO() + descr = io.TextIOBase.read + + with self.assertRaises(TypeError) as cm: + descr.__get__(obj, "not_a_type") + self.assertEqual( + str(cm.exception), + "descriptor 'read' needs a type, not 'str', as arg 2", + ) + def test_staticmethods(self): # Testing static methods... class C(object): @@ -1736,15 +1849,6 @@ class D(C): del sm.x self.assertNotHasAttr(sm, "x") - @support.refcount_test - def test_refleaks_in_staticmethod___init__(self): - gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount') - sm = staticmethod(None) - refs_before = gettotalrefcount() - for i in range(100): - sm.__init__(None) - self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) - @support.impl_detail("the module 'xxsubtype' is internal") @unittest.skipIf(xxsubtype is None, "requires xxsubtype module") def test_staticmethods_in_c(self): @@ -4077,42 +4181,167 @@ class E(D): self.assertEqual(e.a, 2) self.assertEqual(C2.__subclasses__(), [D]) - try: + with self.assertRaisesRegex(TypeError, + "cannot delete '__bases__' attribute of type 'D'"): del D.__bases__ - except (TypeError, AttributeError): - pass - else: - self.fail("shouldn't be able to delete .__bases__") - - try: + with self.assertRaisesRegex(TypeError, 'can only assign non-empty tuple'): D.__bases__ = () - except TypeError as msg: - if str(msg) == "a new-style class can't have only classic bases": - self.fail("wrong error message for .__bases__ = ()") - else: - self.fail("shouldn't be able to set .__bases__ to ()") - - try: + with self.assertRaisesRegex(TypeError, 'can only assign tuple'): + D.__bases__ = [C] + with self.assertRaisesRegex(TypeError, 'duplicate base class'): + D.__bases__ = (C, C) + with self.assertRaisesRegex(TypeError, 'inheritance cycle'): D.__bases__ = (D,) - except TypeError: - pass - else: - # actually, we'll have crashed by here... - self.fail("shouldn't be able to create inheritance cycles") + with self.assertRaisesRegex(TypeError, 'inheritance cycle'): + D.__bases__ = (E,) - try: - D.__bases__ = (C, C) - except TypeError: - pass - else: - self.fail("didn't detect repeated base classes") + class A: + __slots__ = () + def __repr__(self): + return '' + class A_with_dict: + __slots__ = ('__dict__',) + def __repr__(self): + return '' + class A_with_dict_weakref: + def __repr__(self): + return '' + class A_with_slots: + __slots__ = ('x',) + def __repr__(self): + return '' + class A_with_slots_dict: + __slots__ = ('x', '__dict__') + def __repr__(self): + return '' - try: - D.__bases__ = (E,) - except TypeError: - pass - else: - self.fail("shouldn't be able to create inheritance cycles") + class B: + __slots__ = () + b = B() + r = repr(b) + with self.assertRaisesRegex(TypeError, 'layout differs'): + B.__bases__ = (int,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + B.__bases__ = (A_with_dict_weakref,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + B.__bases__ = (A_with_dict,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + B.__bases__ = (A_with_slots,) + B.__bases__ = (A,) + self.assertNotHasAttr(b, '__dict__') + self.assertNotHasAttr(b, '__weakref__') + self.assertEqual(repr(b), '') + B.__bases__ = (object,) + self.assertEqual(repr(b), r) + + class B_with_dict_weakref: + pass + b = B_with_dict_weakref() + with self.assertRaisesRegex(TypeError, 'layout differs'): + B.__bases__ = (A_with_slots,) + B_with_dict_weakref.__bases__ = (A_with_dict_weakref,) + self.assertEqual(repr(b), '') + B_with_dict_weakref.__bases__ = (A_with_dict,) + self.assertEqual(repr(b), '') + B_with_dict_weakref.__bases__ = (A,) + self.assertEqual(repr(b), '') + B_with_dict_weakref.__bases__ = (object,) + + class B_with_slots: + __slots__ = ('x',) + b = B_with_slots() + with self.assertRaisesRegex(TypeError, 'layout differs'): + B_with_slots.__bases__ = (A_with_dict_weakref,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + B_with_slots.__bases__ = (A_with_dict,) + B_with_slots.__bases__ = (A,) + self.assertEqual(repr(b), '') + + class B_with_slots_dict: + __slots__ = ('x', '__dict__') + b = B_with_slots_dict() + with self.assertRaisesRegex(TypeError, 'layout differs'): + B_with_slots_dict.__bases__ = (A_with_dict_weakref,) + B_with_slots_dict.__bases__ = (A_with_dict,) + self.assertEqual(repr(b), '') + B_with_slots_dict.__bases__ = (A,) + self.assertEqual(repr(b), '') + + class B_with_slots_dict_weakref: + __slots__ = ('x', '__dict__', '__weakref__') + b = B_with_slots_dict_weakref() + with self.assertRaisesRegex(TypeError, 'layout differs'): + B_with_slots_dict_weakref.__bases__ = (A_with_slots_dict,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + B_with_slots_dict_weakref.__bases__ = (A_with_slots,) + B_with_slots_dict_weakref.__bases__ = (A_with_dict_weakref,) + self.assertEqual(repr(b), '') + B_with_slots_dict_weakref.__bases__ = (A_with_dict,) + self.assertEqual(repr(b), '') + B_with_slots_dict_weakref.__bases__ = (A,) + self.assertEqual(repr(b), '') + + class C_with_slots(A_with_slots): + __slots__ = () + c = C_with_slots() + with self.assertRaisesRegex(TypeError, 'layout differs'): + C_with_slots.__bases__ = (A_with_slots_dict,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + C_with_slots.__bases__ = (A_with_dict_weakref,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + C_with_slots.__bases__ = (A_with_dict,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + C_with_slots.__bases__ = (A,) + C_with_slots.__bases__ = (A_with_slots,) + self.assertEqual(repr(c), '') + + class C_with_slots_dict(A_with_slots): + pass + c = C_with_slots_dict() + with self.assertRaisesRegex(TypeError, 'layout differs'): + C_with_slots_dict.__bases__ = (A_with_dict_weakref,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + C_with_slots_dict.__bases__ = (A_with_dict,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + C_with_slots_dict.__bases__ = (A,) + C_with_slots_dict.__bases__ = (A_with_slots_dict,) + self.assertEqual(repr(c), '') + C_with_slots_dict.__bases__ = (A_with_slots,) + self.assertEqual(repr(c), '') + + class A_int(int): + __slots__ = () + def __repr__(self): + return '' + class B_int(int): + __slots__ = () + b = B_int(42) + with self.assertRaisesRegex(TypeError, 'layout differs'): + B_int.__bases__ = (object,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + B_int.__bases__ = (tuple,) + with self.assertRaisesRegex(TypeError, 'is not an acceptable base type'): + B_int.__bases__ = (bool,) + B_int.__bases__ = (A_int,) + self.assertEqual(repr(b), '') + B_int.__bases__ = (int,) + self.assertEqual(repr(b), '42') + + class A_tuple(tuple): + __slots__ = () + def __repr__(self): + return '' + class B_tuple(tuple): + __slots__ = () + b = B_tuple((1, 2)) + with self.assertRaisesRegex(TypeError, 'layout differs'): + B_tuple.__bases__ = (object,) + with self.assertRaisesRegex(TypeError, 'layout differs'): + B_tuple.__bases__ = (int,) + B_tuple.__bases__ = (A_tuple,) + self.assertEqual(repr(b), '') + B_tuple.__bases__ = (tuple,) + self.assertEqual(repr(b), '(1, 2)') def test_assign_bases_many_subclasses(self): # This is intended to check that typeobject.c:queue_slot_update() can @@ -4165,26 +4394,14 @@ class C(object): class D(C): pass - try: + with self.assertRaisesRegex(TypeError, 'layout differs'): L.__bases__ = (dict,) - except TypeError: - pass - else: - self.fail("shouldn't turn list subclass into dict subclass") - try: + with self.assertRaisesRegex(TypeError, 'immutable type'): list.__bases__ = (dict,) - except TypeError: - pass - else: - self.fail("shouldn't be able to assign to list.__bases__") - try: + with self.assertRaisesRegex(TypeError, 'layout differs'): D.__bases__ = (C, list) - except TypeError: - pass - else: - self.fail("best_base calculation found wanting") def test_unsubclassable_types(self): with self.assertRaises(TypeError): @@ -4949,7 +5166,7 @@ class X: with self.assertRaises(TypeError) as cm: type(X).__dict__["__doc__"].__delete__(X) - self.assertIn("cannot delete '__doc__' attribute of immutable type 'X'", str(cm.exception)) + self.assertIn("cannot delete '__doc__' attribute of type 'X'", str(cm.exception)) self.assertEqual(X.__doc__, "banana") def test_qualname(self): @@ -5166,6 +5383,31 @@ def foo(self): with self.assertRaisesRegex(NotImplementedError, "BAR"): B().foo + def test_gh146587(self): + # See https://github.com/python/cpython/issues/146587 + + class A: + def __radd__(self, other): ... + + class B(tuple): ... + + self.assertIsNone(() + A()) + self.assertIsNone(B() + A()) + + from typing import NamedTuple + + class T(NamedTuple): + x: int + + class A: + def __init__(self, *args): + self.lst = list(args) + def __radd__(self, other): + return A(*self.lst, other) + + self.assertEqual(((1,)+A()).lst, [(1,)]) + self.assertEqual((T(x=1)+A()).lst, [T(x=1)]) + class DictProxyTests(unittest.TestCase): def setUp(self): @@ -6013,5 +6255,69 @@ class A(metaclass=M): pass +class TestGenericDescriptors(unittest.TestCase): + def test___dict__(self): + class CustomClass: + pass + class SlotClass: + __slots__ = ['foo'] + class SlotSubClass(SlotClass): + pass + class IntSubclass(int): + pass + + dict_descriptor = CustomClass.__dict__['__dict__'] + self.assertEqual(dict_descriptor.__objclass__, object) + + for cls in CustomClass, SlotSubClass, IntSubclass: + with self.subTest(cls=cls): + self.assertIs(cls.__dict__['__dict__'], dict_descriptor) + instance = cls() + instance.attr = 123 + self.assertEqual( + dict_descriptor.__get__(instance, cls), + {'attr': 123}, + ) + with self.assertRaises(AttributeError): + print(dict_descriptor.__get__(True, bool)) + with self.assertRaises(AttributeError): + print(dict_descriptor.__get__(SlotClass(), SlotClass)) + + # delegation to type.__dict__ + self.assertIsInstance( + dict_descriptor.__get__(type, type), + types.MappingProxyType, + ) + + def test___weakref__(self): + class CustomClass: + pass + class SlotClass: + __slots__ = ['foo'] + class SlotSubClass(SlotClass): + pass + class IntSubclass(int): + pass + + weakref_descriptor = CustomClass.__dict__['__weakref__'] + self.assertEqual(weakref_descriptor.__objclass__, object) + + for cls in CustomClass, SlotSubClass: + with self.subTest(cls=cls): + self.assertIs(cls.__dict__['__weakref__'], weakref_descriptor) + instance = cls() + instance.attr = 123 + self.assertEqual( + weakref_descriptor.__get__(instance, cls), + None, + ) + with self.assertRaises(AttributeError): + weakref_descriptor.__get__(True, bool) + with self.assertRaises(AttributeError): + weakref_descriptor.__get__(SlotClass(), SlotClass) + with self.assertRaises(AttributeError): + weakref_descriptor.__get__(IntSubclass(), IntSubclass) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_devpoll.py b/Lib/test/test_devpoll.py index 85e0accb611b1d..5951f8817ab1c1 100644 --- a/Lib/test/test_devpoll.py +++ b/Lib/test/test_devpoll.py @@ -2,6 +2,7 @@ # Initial tests are copied as is from "test_poll.py" +import errno import os import random import select @@ -112,6 +113,15 @@ def test_close(self): self.assertRaises(ValueError, devpoll.register, fd, select.POLLIN) self.assertRaises(ValueError, devpoll.unregister, fd) + def test_close_error(self): + # gh-146205: close() should raise OSError if underlying fd is invalid + devpoll = select.devpoll() + fd = devpoll.fileno() + os.close(fd) + with self.assertRaises(OSError) as cm: + devpoll.close() + self.assertEqual(cm.exception.errno, errno.EBADF) + def test_fd_non_inheritable(self): devpoll = select.devpoll() self.addCleanup(devpoll.close) diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 60c62430370e96..f26586809238f0 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -12,6 +12,15 @@ from test.support import import_helper +class CustomHash: + def __init__(self, hash): + self.hash = hash + def __hash__(self): + return self.hash + def __repr__(self): + return f'' + + class DictTest(unittest.TestCase): def test_invalid_keyword_arguments(self): @@ -1560,6 +1569,26 @@ def make_pairs(): self.assertEqual(d.get(key3_3), 44) self.assertGreaterEqual(eq_count, 1) + def test_overwrite_managed_dict(self): + # GH-130327: Overwriting an object's managed dictionary with another object's + # skipped traversal in favor of inline values, causing the GC to believe that + # the __dict__ wasn't reachable. + import gc + + class Shenanigans: + pass + + to_be_deleted = Shenanigans() + to_be_deleted.attr = "whatever" + holds_reference = Shenanigans() + holds_reference.__dict__ = to_be_deleted.__dict__ + holds_reference.ref = {"circular": to_be_deleted, "data": 42} + + del to_be_deleted + gc.collect() + self.assertEqual(holds_reference.ref['data'], 42) + self.assertEqual(holds_reference.attr, "whatever") + def test_unhashable_key(self): d = {'a': 1} key = [1, 2, 3] @@ -1581,7 +1610,7 @@ def check_unhashable_key(): with check_unhashable_key(): d.get(key) - # Only TypeError exception is overriden, + # Only TypeError exception is overridden, # other exceptions are left unchanged. class HashError: def __hash__(self): @@ -1601,6 +1630,139 @@ def __hash__(self): with self.assertRaises(KeyError): d.get(key2) + def test_clear_at_lookup(self): + # gh-140551 dict crash if clear is called at lookup stage + class X: + def __hash__(self): + return 1 + def __eq__(self, other): + nonlocal d + d.clear() + + d = {} + for _ in range(10): + d[X()] = None + + self.assertEqual(len(d), 1) + + d = {} + for _ in range(10): + d.setdefault(X(), None) + + self.assertEqual(len(d), 1) + + def test_split_table_update_with_str_subclass(self): + # gh-142218: inserting into a split table dictionary with a non str + # key that matches an existing key. + class MyStr(str): pass + class MyClass: pass + obj = MyClass() + obj.attr = 1 + obj.__dict__[MyStr('attr')] = 2 + self.assertEqual(obj.attr, 2) + + def test_split_table_insert_with_str_subclass(self): + # gh-143189: inserting into split table dictionary with a non str + # key that matches an existing key in the shared table but not in + # the dict yet. + + class MyStr(str): pass + class MyClass: pass + + obj = MyClass() + obj.attr1 = 1 + + obj2 = MyClass() + d = obj2.__dict__ + d[MyStr("attr1")] = 2 + self.assertIsInstance(list(d)[0], MyStr) + + def test_hash_collision_remove_add(self): + self.maxDiff = None + # There should be enough space, so all elements with unique hash + # will be placed in corresponding cells without collision. + n = 64 + items = [(CustomHash(h), h) for h in range(n)] + # Keys with hash collision. + a = CustomHash(n) + b = CustomHash(n) + items += [(a, 'a'), (b, 'b')] + d = dict(items) + self.assertEqual(len(d), len(items), d) + del d[a] + # "a" has been replaced with a dummy. + del items[n] + self.assertEqual(len(d), len(items), d) + self.assertEqual(d, dict(items)) + d[b] = 'c' + # "b" should not replace the dummy. + items[n] = (b, 'c') + self.assertEqual(len(d), len(items), d) + self.assertEqual(d, dict(items)) + + def test_clear_reentrant_embedded(self): + # gh-130555: dict.clear() must be safe when values are embedded + # in an object and a destructor mutates the dict. + class MyObj: pass + class ClearOnDelete: + def __del__(self): + nonlocal x + del x + + x = MyObj() + x.a = ClearOnDelete() + + d = x.__dict__ + d.clear() + + def test_clear_reentrant_cycle(self): + # gh-130555: dict.clear() must be safe for embedded dicts when the + # object is part of a reference cycle and the last reference to the + # dict is via the cycle. + class MyObj: pass + obj = MyObj() + obj.f = obj + obj.attr = "attr" + + d = obj.__dict__ + del obj + + d.clear() + + def test_clear_reentrant_force_combined(self): + # gh-130555: dict.clear() must be safe when a destructor forces the + # dict from embedded/split to combined (setting ma_values to NULL). + class MyObj: pass + class ForceConvert: + def __del__(self): + d[1] = "trigger" + + x = MyObj() + x.a = ForceConvert() + x.b = "other" + + d = x.__dict__ + d.clear() + + def test_clear_reentrant_delete(self): + # gh-130555: dict.clear() must be safe when a destructor deletes + # a key from the same embedded dict. + class MyObj: pass + class DelKey: + def __del__(self): + try: + del d['b'] + except KeyError: + pass + + x = MyObj() + x.a = DelKey() + x.b = "value_b" + x.c = "value_c" + + d = x.__dict__ + d.clear() + class CAPITest(unittest.TestCase): @@ -1644,6 +1806,243 @@ class Dict(dict): class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol): type2test = Dict +class FrozenDictMappingTests(mapping_tests.BasicTestImmutableMappingProtocol): + type2test = frozendict + + +class FrozenDict(frozendict): + pass + + +class FrozenDictSlots(frozendict): + __slots__ = ('slot_attr',) + + +class FrozenDictTests(unittest.TestCase): + def test_constructor(self): + # frozendict.__init__() has no effect + d = frozendict(a=1, b=2, c=3) + d.__init__(x=1) + self.assertEqual(d, frozendict(a=1, b=2, c=3)) + + # dict constructor cannot be used on frozendict + with self.assertRaises(TypeError): + dict.__init__(d, x=1) + + # Avoid copy if it's frozendict type + d2 = frozendict(d) + self.assertIs(d2, d) + d2 = FrozenDict(d) + self.assertIsNot(d2, d) + self.assertEqual(d2, d) + + def test_copy(self): + d = frozendict(x=1, y=2) + d2 = d.copy() + self.assertIs(d2, d) + + d = FrozenDict(x=1, y=2) + d2 = d.copy() + self.assertIsNot(d2, d) + self.assertEqual(d2, frozendict(x=1, y=2)) + self.assertEqual(type(d2), frozendict) + + def test_merge(self): + # test "a | b" operator + self.assertEqual(frozendict(x=1) | frozendict(y=2), + frozendict({'x': 1, 'y': 2})) + self.assertEqual(frozendict(x=1) | dict(y=2), + frozendict({'x': 1, 'y': 2})) + self.assertEqual(frozendict(x=1, y=2) | frozendict(y=5), + frozendict({'x': 1, 'y': 5})) + self.assertEqual(FrozenDict(x=1, y=2) | FrozenDict(y=5), + frozendict({'x': 1, 'y': 5})) + + fd = frozendict(x=1, y=2) + self.assertIs(fd | frozendict(), fd) + self.assertIs(fd | {}, fd) + self.assertIs(frozendict() | fd, fd) + + fd = FrozenDict(x=1, y=2) + self.assertEqual(fd | frozendict(), fd) + self.assertEqual(fd | {}, fd) + self.assertEqual(frozendict() | fd, fd) + + # gh-149676: Test hash(frozendict | frozendict) + a = frozendict({"a": 1}) + b = frozendict({"b": 2}) + self.assertEqual(hash(a | b), hash(frozendict({"a": 1, "b": 2}))) + + def test_update(self): + # test "a |= b" operator + d = frozendict(x=1) + copy = d + self.assertIs(copy, d) + d |= frozendict(y=2) + self.assertIsNot(copy, d) + self.assertEqual(d, frozendict({'x': 1, 'y': 2})) + self.assertEqual(copy, frozendict({'x': 1})) + + def test_items_xor(self): + # test "a ^ b" operator on items views + res = frozendict(a=1, b=2).items() ^ frozendict(b=2, c=3).items() + self.assertEqual(res, {('a', 1), ('c', 3)}) + + def test_repr(self): + d = frozendict() + self.assertEqual(repr(d), "frozendict()") + + d = frozendict(x=1, y=2) + self.assertEqual(repr(d), "frozendict({'x': 1, 'y': 2})") + + d = FrozenDict(x=1, y=2) + self.assertEqual(repr(d), "FrozenDict({'x': 1, 'y': 2})") + + def test_hash(self): + # hash() doesn't rely on the items order + self.assertEqual(hash(frozendict(x=1, y=2)), + hash(frozendict(y=2, x=1))) + + # Check that hash() computes the hash of (key, value) pairs + cases = [ + frozendict(a=False, b=True, c=True), + frozendict(a=True, b=False, c=True), + frozendict(a=True, b=True, c=False), + frozendict({False: "a", "b": True, "c": True}), + frozendict({"a": "b", False: True, True: "c"}), + ] + hashes = {hash(fd) for fd in cases} + self.assertEqual(len(hashes), len(cases)) + + fd = frozendict(x=[1], y=[2]) + with self.assertRaisesRegex(TypeError, "unhashable type: 'list'"): + hash(fd) + + @support.cpython_only + def test_hash_cpython(self): + # Check that hash(frozendict) implementation is: + # hash(frozenset(fd.items())) + for fd in ( + frozendict(), + frozendict(x=1, y=2), + frozendict(y=2, x=1), + frozendict(a=False, b=True, c=True), + frozendict.fromkeys('abc'), + ): + with self.subTest(fd=fd): + self.assertEqual(hash(fd), hash(frozenset(fd.items()))) + + def test_fromkeys(self): + self.assertEqual(frozendict.fromkeys('abc'), + frozendict(a=None, b=None, c=None)) + + # Subclass which overrides the constructor + created = frozendict(x=1) + class FrozenDictSubclass(frozendict): + def __new__(self): + return created + + fd = FrozenDictSubclass.fromkeys("abc") + self.assertEqual(fd, frozendict(x=1, a=None, b=None, c=None)) + self.assertEqual(type(fd), FrozenDictSubclass) + self.assertEqual(created, frozendict(x=1)) + + fd = FrozenDictSubclass.fromkeys(frozendict(y=2)) + self.assertEqual(fd, frozendict(x=1, y=None)) + self.assertEqual(type(fd), FrozenDictSubclass) + self.assertEqual(created, frozendict(x=1)) + + # Subclass which doesn't override the constructor + class FrozenDictSubclass2(frozendict): + pass + + fd = FrozenDictSubclass2.fromkeys("abc") + self.assertEqual(fd, frozendict(a=None, b=None, c=None)) + self.assertEqual(type(fd), FrozenDictSubclass2) + + # Dict subclass which overrides the constructor + class DictSubclass(dict): + def __new__(self): + return created + + fd = DictSubclass.fromkeys("abc") + self.assertEqual(fd, frozendict(x=1, a=None, b=None, c=None)) + self.assertEqual(type(fd), DictSubclass) + self.assertEqual(created, frozendict(x=1)) + + def test_pickle(self): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + for fd in ( + frozendict(), + frozendict(x=1, y=2), + FrozenDict(x=1, y=2), + FrozenDictSlots(x=1, y=2), + ): + if type(fd) == FrozenDict: + fd.attr = 123 + if type(fd) == FrozenDictSlots: + fd.slot_attr = 456 + with self.subTest(fd=fd, proto=proto): + if proto >= 2: + p = pickle.dumps(fd, proto) + fd2 = pickle.loads(p) + self.assertEqual(fd2, fd) + self.assertEqual(type(fd2), type(fd)) + if type(fd) == FrozenDict: + self.assertEqual(fd2.attr, 123) + if type(fd) == FrozenDictSlots: + self.assertEqual(fd2.slot_attr, 456) + else: + # protocol 0 and 1 don't support frozendict + with self.assertRaises(TypeError): + pickle.dumps(fd, proto) + + def test_pickle_iter(self): + fd = frozendict(c=1, b=2, a=3, d=4, e=5, f=6) + for method_name in (None, 'keys', 'values', 'items'): + if method_name is not None: + meth = getattr(fd, method_name) + else: + meth = lambda: fd + expected = list(meth())[1:] + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(method_name=method_name, protocol=proto): + it = iter(meth()) + next(it) + p = pickle.dumps(it, proto) + unpickled = pickle.loads(p) + self.assertEqual(list(unpickled), expected) + self.assertEqual(list(it), expected) + + def test_unhashable_key(self): + d = frozendict(a=1) + key = [1, 2, 3] + + def check_unhashable_key(): + msg = "cannot use 'list' as a frozendict key (unhashable type: 'list')" + return self.assertRaisesRegex(TypeError, re.escape(msg)) + + with check_unhashable_key(): + key in d + with check_unhashable_key(): + d[key] + with check_unhashable_key(): + d.get(key) + + # Only TypeError exception is overridden, + # other exceptions are left unchanged. + class HashError: + def __hash__(self): + raise KeyError('error') + + key2 = HashError() + with self.assertRaises(KeyError): + key2 in d + with self.assertRaises(KeyError): + d[key2] + with self.assertRaises(KeyError): + d.get(key2) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_difflib.py b/Lib/test/test_difflib.py index 6ac584a08d1e86..46c9b2c1d8c9fc 100644 --- a/Lib/test/test_difflib.py +++ b/Lib/test/test_difflib.py @@ -1,5 +1,7 @@ import difflib -from test.support import findfile +from test import support +from test.support import findfile, force_colorized +from test.support.import_helper import ensure_lazy_imports import unittest import doctest import sys @@ -29,6 +31,16 @@ def test_one_delete(self): ('delete', 40, 41, 40, 40), ('equal', 41, 81, 40, 80)]) + def test_opcode_caching(self): + sm = difflib.SequenceMatcher(None, 'b' * 100, 'a' + 'b' * 100) + opcode = sm.get_opcodes() + self.assertEqual(opcode, + [ ('insert', 0, 0, 0, 1), + ('equal', 0, 100, 1, 101)]) + # Implementation detail: opcodes are cached; + # `get_opcodes()` returns the same object + self.assertIs(opcode, sm.get_opcodes()) + def test_bjunk(self): sm = difflib.SequenceMatcher(isjunk=lambda x: x == ' ', a='a' * 40 + 'b' * 40, b='a' * 44 + 'b' * 40) @@ -293,6 +305,15 @@ def test_close_matches_aligned(self): '+ kitten\n', '+ puppy\n']) + def test_one_insert(self): + m = difflib.Differ().compare('b' * 2, 'a' + 'b' * 2) + self.assertEqual(list(m), ['+ a', ' b', ' b']) + + def test_one_delete(self): + m = difflib.Differ().compare('a' + 'b' * 2, 'b' * 2) + self.assertEqual(list(m), ['- a', ' b', ' b']) + + class TestOutputFormat(unittest.TestCase): def test_tab_delimiter(self): args = [['one'], ['two'], 'Original', 'Current', @@ -355,6 +376,22 @@ def test_range_format_context(self): self.assertEqual(fmt(3,6), '4,6') self.assertEqual(fmt(0,0), '0') + @force_colorized + def test_unified_diff_colored_output(self): + args = [['one', 'three'], ['two', 'three'], 'Original', 'Current', + '2005-01-26 23:30:50', '2010-04-02 10:20:52'] + actual = list(difflib.unified_diff(*args, lineterm='', color=True)) + + expect = [ + "\033[1m--- Original\t2005-01-26 23:30:50\033[0m", + "\033[1m+++ Current\t2010-04-02 10:20:52\033[0m", + "\033[36m@@ -1,2 +1,2 @@\033[0m", + "\033[31m-one\033[0m", + "\033[32m+two\033[0m", + "\033[0m three\033[0m", + ] + self.assertEqual(expect, actual) + class TestBytes(unittest.TestCase): # don't really care about the content of the output, just the fact @@ -585,10 +622,36 @@ def test_longest_match_with_popular_chars(self): self.assertFalse(self.longer_match_exists(a, b, match.size)) +class TestCloseMatches(unittest.TestCase): + # Happy paths are tested in the doctests of `difflib.get_close_matches`. + + def test_invalid_inputs(self): + self.assertRaises(ValueError, difflib.get_close_matches, "spam", ['egg'], n=0) + self.assertRaises(ValueError, difflib.get_close_matches, "spam", ['egg'], n=-1) + self.assertRaises(ValueError, difflib.get_close_matches, "spam", ['egg'], cutoff=1.1) + self.assertRaises(ValueError, difflib.get_close_matches, "spam", ['egg'], cutoff=-0.1) + + +class TestRestore(unittest.TestCase): + # Happy paths are tested in the doctests of `difflib.restore`. + + def test_invalid_input(self): + with self.assertRaises(ValueError): + ''.join(difflib.restore([], 0)) + with self.assertRaises(ValueError): + ''.join(difflib.restore([], 3)) + + def setUpModule(): difflib.HtmlDiff._default_prefix = 0 +class LazyImportTest(unittest.TestCase): + @support.cpython_only + def test_lazy_import(self): + ensure_lazy_imports("difflib", {"_colorize"}) + + def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite(difflib)) return tests diff --git a/Lib/test/test_difflib_expect.html b/Lib/test/test_difflib_expect.html index 2346a6f9f8dddf..240e2c3336830a 100644 --- a/Lib/test/test_difflib_expect.html +++ b/Lib/test/test_difflib_expect.html @@ -59,11 +59,11 @@ - - - - - + + + + + @@ -75,11 +75,11 @@ - - - - - + + + + + @@ -91,11 +91,11 @@ - - - - - + + + + + @@ -133,11 +133,11 @@

    Context (first diff within numlines=5(default))

    - - - - - + + + + + @@ -150,11 +150,11 @@

    Context (first diff within numlines=5(default))

    - - - - - + + + + + @@ -167,11 +167,11 @@

    Context (first diff within numlines=5(default))

    - - - - - + + + + + @@ -192,11 +192,11 @@

    Context (first diff after numlines=5(default))

    - - - - - + + + + + @@ -209,11 +209,11 @@

    Context (first diff after numlines=5(default))

    - - - - - + + + + + @@ -226,11 +226,11 @@

    Context (first diff after numlines=5(default))

    - - - - - + + + + + @@ -247,11 +247,11 @@

    Context (numlines=6)

    - - - - - + + + + + @@ -263,11 +263,11 @@

    Context (numlines=6)

    - - - - - + + + + + @@ -279,11 +279,11 @@

    Context (numlines=6)

    - - - - - + + + + + @@ -300,25 +300,25 @@

    Context (numlines=0)

    - - - - - + + + + + - - - - - + + + + + - - - - - + + + + +

    from
    to
    f1f1
    n2   1. Beautiful is beTTer than ugly.n2   1. Beautiful is better than ugly.
    3   2. Explicit is better than implicit.
    4   3. Simple is better than complex.3   3.   Simple is better than complex.
    5   4. Complex is better than complicated.4   4. Complicated is better than complex.
    5   5. Flat is better than nested.
    n2   1. Beautiful is beTTer than ugly.n2   1. Beautiful is better than ugly.
    3   2. Explicit is better than implicit.
    4   3. Simple is better than complex.3   3.   Simple is better than complex.
    5   4. Complex is better than complicated.4   4. Complicated is better than complex.
    5   5. Flat is better than nested.
    61236123
    71237123
    81238123
    1412314123
    1512315123
    1616
    n17   1. Beautiful is beTTer than ugly.n17   1. Beautiful is better than ugly.
    18   2. Explicit is better than implicit.
    19   3. Simple is better than complex.18   3.   Simple is better than complex.
    20   4. Complex is better than complicated.19   4. Complicated is better than complex.
    20   5. Flat is better than nested.
    n17   1. Beautiful is beTTer than ugly.n17   1. Beautiful is better than ugly.
    18   2. Explicit is better than implicit.
    19   3. Simple is better than complex.18   3.   Simple is better than complex.
    20   4. Complex is better than complicated.19   4. Complicated is better than complex.
    20   5. Flat is better than nested.
    2112321123
    2212322123
    2312323123
    2912329123
    3012330123
    3131
    t32   1. Beautiful is beTTer than ugly.t32   1. Beautiful is better than ugly.
    33   2. Explicit is better than implicit.
    34   3. Simple is better than complex.33   3.   Simple is better than complex.
    35   4. Complex is better than complicated.34   4. Complicated is better than complex.
    35   5. Flat is better than nested.
    t32   1. Beautiful is beTTer than ugly.t32   1. Beautiful is better than ugly.
    33   2. Explicit is better than implicit.
    34   3. Simple is better than complex.33   3.   Simple is better than complex.
    35   4. Complex is better than complicated.34   4. Complicated is better than complex.
    35   5. Flat is better than nested.
    3612336123
    3712337123
    3812338123

    from
    to
    f1f1
    n2   1. Beautiful is beTTer than ugly.n2   1. Beautiful is better than ugly.
    3   2. Explicit is better than implicit.
    4   3. Simple is better than complex.3   3.   Simple is better than complex.
    5   4. Complex is better than complicated.4   4. Complicated is better than complex.
    5   5. Flat is better than nested.
    n2   1. Beautiful is beTTer than ugly.n2   1. Beautiful is better than ugly.
    3   2. Explicit is better than implicit.
    4   3. Simple is better than complex.3   3.   Simple is better than complex.
    5   4. Complex is better than complicated.4   4. Complicated is better than complex.
    5   5. Flat is better than nested.
    61236123
    71237123
    81238123
    1412314123
    1512315123
    1616
    n17   1. Beautiful is beTTer than ugly.n17   1. Beautiful is better than ugly.
    18   2. Explicit is better than implicit.
    19   3. Simple is better than complex.18   3.   Simple is better than complex.
    20   4. Complex is better than complicated.19   4. Complicated is better than complex.
    20   5. Flat is better than nested.
    n17   1. Beautiful is beTTer than ugly.n17   1. Beautiful is better than ugly.
    18   2. Explicit is better than implicit.
    19   3. Simple is better than complex.18   3.   Simple is better than complex.
    20   4. Complex is better than complicated.19   4. Complicated is better than complex.
    20   5. Flat is better than nested.
    2112321123
    2212322123
    2312323123
    2912329123
    3012330123
    3131
    t32   1. Beautiful is beTTer than ugly.t32   1. Beautiful is better than ugly.
    33   2. Explicit is better than implicit.
    34   3. Simple is better than complex.33   3.   Simple is better than complex.
    35   4. Complex is better than complicated.34   4. Complicated is better than complex.
    35   5. Flat is better than nested.
    t32   1. Beautiful is beTTer than ugly.t32   1. Beautiful is better than ugly.
    33   2. Explicit is better than implicit.
    34   3. Simple is better than complex.33   3.   Simple is better than complex.
    35   4. Complex is better than complicated.34   4. Complicated is better than complex.
    35   5. Flat is better than nested.
    3612336123
    3712337123
    3812338123
    94569456
    1045610456
    1111
    n12   1. Beautiful is beTTer than ugly.n12   1. Beautiful is better than ugly.
    13   2. Explicit is better than implicit.
    14   3. Simple is better than complex.13   3.   Simple is better than complex.
    15   4. Complex is better than complicated.14   4. Complicated is better than complex.
    15   5. Flat is better than nested.
    n12   1. Beautiful is beTTer than ugly.n12   1. Beautiful is better than ugly.
    13   2. Explicit is better than implicit.
    14   3. Simple is better than complex.13   3.   Simple is better than complex.
    15   4. Complex is better than complicated.14   4. Complicated is better than complex.
    15   5. Flat is better than nested.
    1612316123
    1712317123
    1812318123
    2412324123
    2512325123
    2626
    n27   1. Beautiful is beTTer than ugly.n27   1. Beautiful is better than ugly.
    28   2. Explicit is better than implicit.
    29   3. Simple is better than complex.28   3.   Simple is better than complex.
    30   4. Complex is better than complicated.29   4. Complicated is better than complex.
    30   5. Flat is better than nested.
    n27   1. Beautiful is beTTer than ugly.n27   1. Beautiful is better than ugly.
    28   2. Explicit is better than implicit.
    29   3. Simple is better than complex.28   3.   Simple is better than complex.
    30   4. Complex is better than complicated.29   4. Complicated is better than complex.
    30   5. Flat is better than nested.
    3112331123
    3212332123
    3312333123
    3912339123
    4012340123
    4141
    t42   1. Beautiful is beTTer than ugly.t42   1. Beautiful is better than ugly.
    43   2. Explicit is better than implicit.
    44   3. Simple is better than complex.43   3.   Simple is better than complex.
    45   4. Complex is better than complicated.44   4. Complicated is better than complex.
    45   5. Flat is better than nested.
    t42   1. Beautiful is beTTer than ugly.t42   1. Beautiful is better than ugly.
    43   2. Explicit is better than implicit.
    44   3. Simple is better than complex.43   3.   Simple is better than complex.
    45   4. Complex is better than complicated.44   4. Complicated is better than complex.
    45   5. Flat is better than nested.
    4612346123
    4712347123
    4812348123

    from
    to
    f1f1
    n2   1. Beautiful is beTTer than ugly.n2   1. Beautiful is better than ugly.
    3   2. Explicit is better than implicit.
    4   3. Simple is better than complex.3   3.   Simple is better than complex.
    5   4. Complex is better than complicated.4   4. Complicated is better than complex.
    5   5. Flat is better than nested.
    n2   1. Beautiful is beTTer than ugly.n2   1. Beautiful is better than ugly.
    3   2. Explicit is better than implicit.
    4   3. Simple is better than complex.3   3.   Simple is better than complex.
    5   4. Complex is better than complicated.4   4. Complicated is better than complex.
    5   5. Flat is better than nested.
    61236123
    71237123
    81238123
    1412314123
    1512315123
    1616
    n17   1. Beautiful is beTTer than ugly.n17   1. Beautiful is better than ugly.
    18   2. Explicit is better than implicit.
    19   3. Simple is better than complex.18   3.   Simple is better than complex.
    20   4. Complex is better than complicated.19   4. Complicated is better than complex.
    20   5. Flat is better than nested.
    n17   1. Beautiful is beTTer than ugly.n17   1. Beautiful is better than ugly.
    18   2. Explicit is better than implicit.
    19   3. Simple is better than complex.18   3.   Simple is better than complex.
    20   4. Complex is better than complicated.19   4. Complicated is better than complex.
    20   5. Flat is better than nested.
    2112321123
    2212322123
    2312323123
    2912329123
    3012330123
    3131
    t32   1. Beautiful is beTTer than ugly.t32   1. Beautiful is better than ugly.
    33   2. Explicit is better than implicit.
    34   3. Simple is better than complex.33   3.   Simple is better than complex.
    35   4. Complex is better than complicated.34   4. Complicated is better than complex.
    35   5. Flat is better than nested.
    t32   1. Beautiful is beTTer than ugly.t32   1. Beautiful is better than ugly.
    33   2. Explicit is better than implicit.
    34   3. Simple is better than complex.33   3.   Simple is better than complex.
    35   4. Complex is better than complicated.34   4. Complicated is better than complex.
    35   5. Flat is better than nested.
    3612336123
    3712337123
    3812338123

    from
    to
    n2   1. Beautiful is beTTer than ugly.n2   1. Beautiful is better than ugly.
    3   2. Explicit is better than implicit.
    4   3. Simple is better than complex.3   3.   Simple is better than complex.
    5   4. Complex is better than complicated.4   4. Complicated is better than complex.
    5   5. Flat is better than nested.
    n2   1. Beautiful is beTTer than ugly.n2   1. Beautiful is better than ugly.
    3   2. Explicit is better than implicit.
    4   3. Simple is better than complex.3   3.   Simple is better than complex.
    5   4. Complex is better than complicated.4   4. Complicated is better than complex.
    5   5. Flat is better than nested.
    n17   1. Beautiful is beTTer than ugly.n17   1. Beautiful is better than ugly.
    18   2. Explicit is better than implicit.
    19   3. Simple is better than complex.18   3.   Simple is better than complex.
    20   4. Complex is better than complicated.19   4. Complicated is better than complex.
    20   5. Flat is better than nested.
    n17   1. Beautiful is beTTer than ugly.n17   1. Beautiful is better than ugly.
    18   2. Explicit is better than implicit.
    19   3. Simple is better than complex.18   3.   Simple is better than complex.
    20   4. Complex is better than complicated.19   4. Complicated is better than complex.
    20   5. Flat is better than nested.
    t32   1. Beautiful is beTTer than ugly.t32   1. Beautiful is better than ugly.
    33   2. Explicit is better than implicit.
    34   3. Simple is better than complex.33   3.   Simple is better than complex.
    35   4. Complex is better than complicated.34   4. Complicated is better than complex.
    35   5. Flat is better than nested.
    t32   1. Beautiful is beTTer than ugly.t32   1. Beautiful is better than ugly.
    33   2. Explicit is better than implicit.
    34   3. Simple is better than complex.33   3.   Simple is better than complex.
    35   4. Complex is better than complicated.34   4. Complicated is better than complex.
    35   5. Flat is better than nested.

    Same Context

    @@ -418,11 +418,11 @@

    tabsize=2

    f1f1
    t2    Line 1: preceded by from:[tt] to:[ssss]t2    Line 1: preceded by from:[tt] to:[ssss]
    3      Line 2: preceded by from:[sstt] to:[sssst]3      Line 2: preceded by from:[sstt] to:[sssst]
    4      Line 3: preceded by from:[sstst] to:[ssssss]4      Line 3: preceded by from:[sstst] to:[ssssss]
    5Line 4:   has from:[sst] to:[sss] after :5Line 4:   has from:[sst] to:[sss] after :
    6Line 5: has from:[t] to:[ss] at end 6Line 5: has from:[t] to:[ss] at end
    t2    Line 1: preceded by from:[tt] to:[ssss]t2    Line 1: preceded by from:[tt] to:[ssss]
    3      Line 2: preceded by from:[sstt] to:[sssst]3      Line 2: preceded by from:[sstt] to:[sssst]
    4      Line 3: preceded by from:[sstst] to:[ssssss]4      Line 3: preceded by from:[sstst] to:[ssssss]
    5Line 4:   has from:[sst] to:[sss] after :5Line 4:   has from:[sst] to:[sss] after :
    6Line 5: has from:[t] to:[ss] at end 6Line 5: has from:[t] to:[ss] at end

    tabsize=default

    @@ -434,11 +434,11 @@

    tabsize=default

    f1f1
    t2                Line 1: preceded by from:[tt] to:[ssss]t2    Line 1: preceded by from:[tt] to:[ssss]
    3                Line 2: preceded by from:[sstt] to:[sssst]3        Line 2: preceded by from:[sstt] to:[sssst]
    4                Line 3: preceded by from:[sstst] to:[ssssss]4      Line 3: preceded by from:[sstst] to:[ssssss]
    5Line 4:         has from:[sst] to:[sss] after :5Line 4:   has from:[sst] to:[sss] after :
    6Line 5: has from:[t] to:[ss] at end     6Line 5: has from:[t] to:[ss] at end
    t2                Line 1: preceded by from:[tt] to:[ssss]t2    Line 1: preceded by from:[tt] to:[ssss]
    3                Line 2: preceded by from:[sstt] to:[sssst]3        Line 2: preceded by from:[sstt] to:[sssst]
    4                Line 3: preceded by from:[sstst] to:[ssssss]4      Line 3: preceded by from:[sstst] to:[ssssss]
    5Line 4:         has from:[sst] to:[sss] after :5Line 4:   has from:[sst] to:[sss] after :
    6Line 5: has from:[t] to:[ss] at end     6Line 5: has from:[t] to:[ss] at end

    Context (wrapcolumn=14,numlines=0)

    @@ -449,31 +449,31 @@

    Context (wrapcolumn=14,numlines=0)

    n4line 2n4line 2    adde
     >d
    n4line 2n4line 2    adde
     >d
    n6line 4   changn6line 4   chanG
    >ed>Ed
    7line 5   chang7line 5a  chanG
    >ed>ed
    8line 6   chang8line 6a  chang
    >ed>Ed
    n6line 4   changn6line 4   chanG
    >ed>Ed
    7line 5   chang7line 5a  chanG
    >ed>ed
    8line 6   chang8line 6a  chang
    >ed>Ed
    n10line 8  subtran10line 8
    >cted 
    n10line 8  subtran10line 8
    >cted 
    t1212345678901234t121234567890
    >56789012345689 
    >012345 
    13short line13another long l
     >ine that needs
     > to be wrapped
    14just fits in!!14just fitS in!!
    15just fits in t15just fits in t
    >wo lines yup!!>wo lineS yup!!
    t1212345678901234t121234567890
    >56789012345689 
    >012345 
    13short line13another long l
     >ine that needs
     > to be wrapped
    14just fits in!!14just fitS in!!
    15just fits in t15just fits in t
    >wo lines yup!!>wo lineS yup!!

    wrapcolumn=14,splitlines()

    @@ -489,28 +489,28 @@

    wrapcolumn=14,splitlines()

    >56789012345689>56789012345689
    >012345>012345
    3line 13line 1
    n4line 2n4line 2    adde
     >d
    n4line 2n4line 2    adde
     >d
    5line 35line 3
    n6line 4   changn6line 4   chanG
    >ed>Ed
    7line 5   chang7line 5a  chanG
    >ed>ed
    8line 6   chang8line 6a  chang
    >ed>Ed
    n6line 4   changn6line 4   chanG
    >ed>Ed
    7line 5   chang7line 5a  chanG
    >ed>ed
    8line 6   chang8line 6a  chang
    >ed>Ed
    9line 79line 7
    n10line 8  subtran10line 8
    >cted 
    n10line 8  subtran10line 8
    >cted 
    11line 911line 9
    t1212345678901234t121234567890
    >56789012345689 
    >012345 
    13short line13another long l
     >ine that needs
     > to be wrapped
    14just fits in!!14just fitS in!!
    15just fits in t15just fits in t
    >wo lines yup!!>wo lineS yup!!
    t1212345678901234t121234567890
    >56789012345689 
    >012345 
    13short line13another long l
     >ine that needs
     > to be wrapped
    14just fits in!!14just fitS in!!
    15just fits in t15just fits in t
    >wo lines yup!!>wo lineS yup!!
    16the end16the end
    @@ -527,28 +527,28 @@

    wrapcolumn=14,splitlines(True)

    >56789012345689>56789012345689
    >012345>012345
    3line 13line 1
    n4line 2n4line 2    adde
     >d
    n4line 2n4line 2    adde
     >d
    5line 35line 3
    n6line 4   changn6line 4   chanG
    >ed>Ed
    7line 5   chang7line 5a  chanG
    >ed>ed
    8line 6   chang8line 6a  chang
    >ed>Ed
    n6line 4   changn6line 4   chanG
    >ed>Ed
    7line 5   chang7line 5a  chanG
    >ed>ed
    8line 6   chang8line 6a  chang
    >ed>Ed
    9line 79line 7
    n10line 8  subtran10line 8
    >cted 
    n10line 8  subtran10line 8
    >cted 
    11line 911line 9
    t1212345678901234t121234567890
    >56789012345689 
    >012345 
    13short line13another long l
     >ine that needs
     > to be wrapped
    14just fits in!!14just fitS in!!
    15just fits in t15just fits in t
    >wo lines yup!!>wo lineS yup!!
    t1212345678901234t121234567890
    >56789012345689 
    >012345 
    13short line13another long l
     >ine that needs
     > to be wrapped
    14just fits in!!14just fitS in!!
    15just fits in t15just fits in t
    >wo lines yup!!>wo lineS yup!!
    16the end16the end
    diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 355990ed58ee09..c75992761d1334 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -15,7 +15,8 @@ import unittest from test.support import (captured_stdout, requires_debug_ranges, requires_specialization, cpython_only, - os_helper, import_helper, reset_code) + os_helper, import_helper, reset_code, + requires_jit_enabled) from test.support.bytecode_helper import BytecodeTestCase @@ -56,7 +57,7 @@ def cm(cls, x): COMPARE_OP 72 (==) LOAD_FAST_BORROW 0 (self) STORE_ATTR 0 (x) - LOAD_CONST 1 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ % (_C.__init__.__code__.co_firstlineno, _C.__init__.__code__.co_firstlineno + 1,) @@ -67,7 +68,7 @@ def cm(cls, x): COMPARE_OP 72 (==) LOAD_FAST_BORROW 0 STORE_ATTR 0 - LOAD_CONST 1 + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ @@ -79,7 +80,7 @@ def cm(cls, x): COMPARE_OP 72 (==) LOAD_FAST_BORROW 0 (cls) STORE_ATTR 0 (x) - LOAD_CONST 1 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ % (_C.cm.__code__.co_firstlineno, _C.cm.__code__.co_firstlineno + 2,) @@ -90,7 +91,7 @@ def cm(cls, x): LOAD_SMALL_INT 1 COMPARE_OP 72 (==) STORE_FAST 0 (x) - LOAD_CONST 1 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ % (_C.sm.__code__.co_firstlineno, _C.sm.__code__.co_firstlineno + 2,) @@ -127,13 +128,13 @@ def _f(a): dis_f_with_offsets = """\ %3d 0 RESUME 0 -%3d 2 LOAD_GLOBAL 1 (print + NULL) - 12 LOAD_FAST_BORROW 0 (a) - 14 CALL 1 - 22 POP_TOP +%3d 4 LOAD_GLOBAL 1 (print + NULL) + 14 LOAD_FAST_BORROW 0 (a) + 16 CALL 1 + 24 POP_TOP -%3d 24 LOAD_SMALL_INT 1 - 26 RETURN_VALUE +%3d 26 LOAD_SMALL_INT 1 + 28 RETURN_VALUE """ % (_f.__code__.co_firstlineno, _f.__code__.co_firstlineno + 1, _f.__code__.co_firstlineno + 2) @@ -174,7 +175,7 @@ def bug708901(): %3d LOAD_SMALL_INT 10 %3d CALL 2 - GET_ITER + GET_ITER 0 L1: FOR_ITER 3 (to L2) STORE_FAST 0 (res) @@ -182,7 +183,7 @@ def bug708901(): %3d L2: END_FOR POP_ITER - LOAD_CONST 1 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ % (bug708901.__code__.co_firstlineno, bug708901.__code__.co_firstlineno + 1, @@ -229,7 +230,7 @@ def bug42562(): dis_bug42562 = """\ RESUME 0 - LOAD_CONST 0 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ @@ -282,7 +283,7 @@ def wrap_func_w_kwargs(): LOAD_CONST 1 (('c',)) CALL_KW 3 POP_TOP - LOAD_CONST 2 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ % (wrap_func_w_kwargs.__code__.co_firstlineno, wrap_func_w_kwargs.__code__.co_firstlineno + 1) @@ -292,10 +293,10 @@ def wrap_func_w_kwargs(): 1 LOAD_SMALL_INT 0 LOAD_CONST 1 (('*',)) - IMPORT_NAME 0 (math) + IMPORT_NAME 2 (math + eager) CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR) POP_TOP - LOAD_CONST 2 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ @@ -322,7 +323,7 @@ def wrap_func_w_kwargs(): %3d LOAD_GLOBAL 0 (spam) POP_TOP - LOAD_CONST 0 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ @@ -331,19 +332,19 @@ def wrap_func_w_kwargs(): %4d LOAD_GLOBAL 0 (spam) POP_TOP - LOAD_CONST 0 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ dis_module_expected_results = """\ Disassembly of f: 4 RESUME 0 - LOAD_CONST 0 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE Disassembly of g: 5 RESUME 0 - LOAD_CONST 0 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ @@ -368,7 +369,7 @@ def wrap_func_w_kwargs(): LOAD_SMALL_INT 1 BINARY_OP 0 (+) STORE_NAME 0 (x) - LOAD_CONST 1 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ @@ -409,7 +410,7 @@ def wrap_func_w_kwargs(): LOAD_SMALL_INT 0 CALL 1 STORE_SUBSCR - LOAD_CONST 2 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ @@ -427,7 +428,7 @@ def foo(a: int, b: str) -> str: MAKE_FUNCTION SET_FUNCTION_ATTRIBUTE 16 (annotate) STORE_NAME 0 (foo) - LOAD_CONST 2 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ @@ -453,52 +454,53 @@ def foo(a: int, b: str) -> str: """ dis_traceback = """\ -%4d RESUME 0 +%4d RESUME 0 -%4d NOP +%4d NOP -%4d L1: LOAD_SMALL_INT 1 - LOAD_SMALL_INT 0 - --> BINARY_OP 11 (/) - POP_TOP +%4d L1: LOAD_SMALL_INT 1 + LOAD_SMALL_INT 0 + --> BINARY_OP 11 (/) + POP_TOP -%4d L2: LOAD_FAST_CHECK 1 (tb) - RETURN_VALUE +%4d L2: LOAD_FAST_CHECK 1 (tb) + RETURN_VALUE - -- L3: PUSH_EXC_INFO + -- L3: PUSH_EXC_INFO -%4d LOAD_GLOBAL 0 (Exception) - CHECK_EXC_MATCH - POP_JUMP_IF_FALSE 24 (to L7) - NOT_TAKEN - STORE_FAST 0 (e) +%4d LOAD_GLOBAL 0 (Exception) + CHECK_EXC_MATCH + POP_JUMP_IF_FALSE 24 (to L9) + L4: NOT_TAKEN + L5: STORE_FAST 0 (e) -%4d L4: LOAD_FAST 0 (e) - LOAD_ATTR 2 (__traceback__) - STORE_FAST 1 (tb) - L5: POP_EXCEPT - LOAD_CONST 1 (None) - STORE_FAST 0 (e) - DELETE_FAST 0 (e) +%4d L6: LOAD_FAST 0 (e) + LOAD_ATTR 2 (__traceback__) + STORE_FAST 1 (tb) + L7: POP_EXCEPT + LOAD_COMMON_CONSTANT 7 (None) + STORE_FAST 0 (e) + DELETE_FAST 0 (e) -%4d LOAD_FAST 1 (tb) - RETURN_VALUE +%4d LOAD_FAST 1 (tb) + RETURN_VALUE - -- L6: LOAD_CONST 1 (None) - STORE_FAST 0 (e) - DELETE_FAST 0 (e) - RERAISE 1 + -- L8: LOAD_COMMON_CONSTANT 7 (None) + STORE_FAST 0 (e) + DELETE_FAST 0 (e) + RERAISE 1 -%4d L7: RERAISE 0 +%4d L9: RERAISE 0 - -- L8: COPY 3 - POP_EXCEPT - RERAISE 1 + -- L10: COPY 3 + POP_EXCEPT + RERAISE 1 ExceptionTable: L1 to L2 -> L3 [0] - L3 to L4 -> L8 [1] lasti - L4 to L5 -> L6 [1] lasti - L6 to L8 -> L8 [1] lasti + L3 to L4 -> L10 [1] lasti + L5 to L6 -> L10 [1] lasti + L6 to L7 -> L8 [1] lasti + L8 to L10 -> L10 [1] lasti """ % (TRACEBACK_CODE.co_firstlineno, TRACEBACK_CODE.co_firstlineno + 1, TRACEBACK_CODE.co_firstlineno + 2, @@ -553,40 +555,41 @@ def _with(c): %4d LOAD_SMALL_INT 1 STORE_FAST 1 (x) -%4d L2: LOAD_CONST 1 (None) - LOAD_CONST 1 (None) - LOAD_CONST 1 (None) +%4d L2: LOAD_COMMON_CONSTANT 7 (None) + LOAD_COMMON_CONSTANT 7 (None) + LOAD_COMMON_CONSTANT 7 (None) CALL 3 POP_TOP %4d LOAD_SMALL_INT 2 STORE_FAST 2 (y) - LOAD_CONST 1 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE %4d L3: PUSH_EXC_INFO WITH_EXCEPT_START TO_BOOL - POP_JUMP_IF_TRUE 2 (to L4) - NOT_TAKEN - RERAISE 2 - L4: POP_TOP - L5: POP_EXCEPT + POP_JUMP_IF_TRUE 2 (to L6) + L4: NOT_TAKEN + L5: RERAISE 2 + L6: POP_TOP + L7: POP_EXCEPT POP_TOP POP_TOP POP_TOP %4d LOAD_SMALL_INT 2 STORE_FAST 2 (y) - LOAD_CONST 1 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE - -- L6: COPY 3 + -- L8: COPY 3 POP_EXCEPT RERAISE 1 ExceptionTable: L1 to L2 -> L3 [2] lasti - L3 to L5 -> L6 [4] lasti + L3 to L4 -> L8 [4] lasti + L5 to L7 -> L8 [4] lasti """ % (_with.__code__.co_firstlineno, _with.__code__.co_firstlineno + 1, _with.__code__.co_firstlineno + 2, @@ -614,47 +617,50 @@ async def _asyncwith(c): LOAD_SPECIAL 2 (__aenter__) CALL 0 GET_AWAITABLE 1 - LOAD_CONST 0 (None) - L2: SEND 3 (to L5) + PUSH_NULL + LOAD_COMMON_CONSTANT 7 (None) + L2: SEND 4 (to L5) L3: YIELD_VALUE 1 L4: RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 5 (to L2) + JUMP_BACKWARD_NO_INTERRUPT 6 (to L2) L5: END_SEND L6: POP_TOP %4d LOAD_SMALL_INT 1 STORE_FAST 1 (x) -%4d L7: LOAD_CONST 0 (None) - LOAD_CONST 0 (None) - LOAD_CONST 0 (None) +%4d L7: LOAD_COMMON_CONSTANT 7 (None) + LOAD_COMMON_CONSTANT 7 (None) + LOAD_COMMON_CONSTANT 7 (None) CALL 3 GET_AWAITABLE 2 - LOAD_CONST 0 (None) - L8: SEND 3 (to L11) + PUSH_NULL + LOAD_COMMON_CONSTANT 7 (None) + L8: SEND 4 (to L11) L9: YIELD_VALUE 1 L10: RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 5 (to L8) + JUMP_BACKWARD_NO_INTERRUPT 6 (to L8) L11: END_SEND POP_TOP %4d LOAD_SMALL_INT 2 STORE_FAST 2 (y) - LOAD_CONST 0 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE %4d L12: CLEANUP_THROW - L13: JUMP_BACKWARD_NO_INTERRUPT 26 (to L5) + L13: JUMP_BACKWARD_NO_INTERRUPT 28 (to L5) L14: CLEANUP_THROW L15: JUMP_BACKWARD_NO_INTERRUPT 10 (to L11) L16: PUSH_EXC_INFO WITH_EXCEPT_START GET_AWAITABLE 2 - LOAD_CONST 0 (None) - L17: SEND 4 (to L21) + PUSH_NULL + LOAD_COMMON_CONSTANT 7 (None) + L17: SEND 5 (to L21) L18: YIELD_VALUE 1 L19: RESUME 3 - JUMP_BACKWARD_NO_INTERRUPT 5 (to L17) + JUMP_BACKWARD_NO_INTERRUPT 6 (to L17) L20: CLEANUP_THROW L21: END_SEND TO_BOOL @@ -669,7 +675,7 @@ async def _asyncwith(c): %4d LOAD_SMALL_INT 2 STORE_FAST 2 (y) - LOAD_CONST 0 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE -- L26: COPY 3 @@ -679,15 +685,15 @@ async def _asyncwith(c): RERAISE 1 ExceptionTable: L1 to L3 -> L27 [0] lasti - L3 to L4 -> L12 [4] + L3 to L4 -> L12 [5] L4 to L6 -> L27 [0] lasti L6 to L7 -> L16 [2] lasti L7 to L9 -> L27 [0] lasti - L9 to L10 -> L14 [2] + L9 to L10 -> L14 [3] L10 to L13 -> L27 [0] lasti L14 to L15 -> L27 [0] lasti L16 to L18 -> L26 [4] lasti - L18 to L19 -> L20 [7] + L18 to L19 -> L20 [8] L19 to L22 -> L26 [4] lasti L23 to L25 -> L26 [4] lasti L25 to L27 -> L27 [0] lasti @@ -825,7 +831,29 @@ def foo(x): %4d RESUME 0 -%4d LOAD_GLOBAL 1 (list + NULL) +%4d LOAD_GLOBAL 0 (list) + COPY 1 + LOAD_COMMON_CONSTANT 5 (list) + IS_OP 0 (is) + POP_JUMP_IF_FALSE 22 (to L3) + NOT_TAKEN + POP_TOP + BUILD_LIST 0 + LOAD_FAST_BORROW 0 (x) + BUILD_TUPLE 1 + LOAD_CONST %d ( at 0x..., file "%s", line %d>) + MAKE_FUNCTION + SET_FUNCTION_ATTRIBUTE 8 (closure) + LOAD_DEREF 1 (y) + CALL 0 + PUSH_NULL + L1: FOR_ITER 3 (to L2) + LIST_APPEND 3 + JUMP_BACKWARD 5 (to L1) + L2: END_FOR + POP_ITER + RETURN_VALUE + L3: PUSH_NULL LOAD_FAST_BORROW 0 (x) BUILD_TUPLE 1 LOAD_CONST 1 ( at 0x..., file "%s", line %d>) @@ -840,6 +868,9 @@ def foo(x): _h.__code__.co_firstlineno + 1, _h.__code__.co_firstlineno + 1, _h.__code__.co_firstlineno + 3, + 1 if __debug__ else 0, + __file__, + _h.__code__.co_firstlineno + 3, __file__, _h.__code__.co_firstlineno + 3, ) @@ -847,30 +878,31 @@ def foo(x): dis_nested_2 = """%s Disassembly of at 0x..., file "%s", line %d>: -- COPY_FREE_VARS 1 + RESUME 4 -%4d RETURN_GENERATOR +%4d LOAD_FAST 0 (.0) + GET_ITER 0 + RETURN_GENERATOR POP_TOP L1: RESUME 0 - LOAD_FAST 0 (.0) - GET_ITER - L2: FOR_ITER 14 (to L3) + L2: FOR_ITER 15 (to L3) STORE_FAST 1 (z) LOAD_DEREF 2 (x) LOAD_FAST_BORROW 1 (z) BINARY_OP 0 (+) YIELD_VALUE 0 - RESUME 5 + RESUME 9 POP_TOP - JUMP_BACKWARD 16 (to L2) + JUMP_BACKWARD 17 (to L2) L3: END_FOR POP_ITER - LOAD_CONST 0 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE -- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR) RERAISE 1 ExceptionTable: - L1 to L4 -> L4 [0] lasti + L1 to L4 -> L4 [2] lasti """ % (dis_nested_1, __file__, _h.__code__.co_firstlineno + 3, @@ -882,7 +914,7 @@ def load_test(x, y=0): return a, b dis_load_test_quickened_code = """\ -%3d RESUME_CHECK 0 +%3d RESUME_CHECK{: <6} 0 %3d LOAD_FAST_LOAD_FAST 1 (x, y) STORE_FAST_STORE_FAST 50 (b, a) @@ -899,14 +931,14 @@ def loop_test(): load_test(i) dis_loop_test_quickened_code = """\ -%3d RESUME_CHECK 0 +%3d RESUME_CHECK{: <6} 0 %3d BUILD_LIST 0 - LOAD_CONST 2 ((1, 2, 3)) + LOAD_CONST 1 ((1, 2, 3)) LIST_EXTEND 1 LOAD_SMALL_INT 3 BINARY_OP 5 (*) - GET_ITER + GET_ITER_VIRTUAL 0 L1: FOR_ITER_LIST 14 (to L2) STORE_FAST 0 (i) @@ -918,7 +950,7 @@ def loop_test(): %3d L2: END_FOR POP_ITER - LOAD_CONST 1 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """ % (loop_test.__code__.co_firstlineno, loop_test.__code__.co_firstlineno + 1, @@ -936,7 +968,7 @@ def extended_arg_quick(): UNPACK_EX 256 POP_TOP STORE_FAST 0 (_) - LOAD_CONST 1 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE """% (extended_arg_quick.__code__.co_firstlineno, extended_arg_quick.__code__.co_firstlineno + 1,) @@ -1008,6 +1040,7 @@ def test_widths(self): long_opcodes = set(['JUMP_BACKWARD_NO_INTERRUPT', 'LOAD_FAST_BORROW_LOAD_FAST_BORROW', 'INSTRUMENTED_CALL_FUNCTION_EX', + 'YIELD_FROM_CORO_CHECK', 'ANNOTATIONS_PLACEHOLDER']) for op, opname in enumerate(dis.opname): if opname in long_opcodes or opname.startswith("INSTRUMENTED"): @@ -1052,7 +1085,7 @@ def test_dis_with_some_positions(self): '', '2:3-3:15 NOP', '', - '3:11-3:15 LOAD_CONST 0 (None)', + '3:11-3:15 LOAD_COMMON_CONSTANT 7 (None)', '3:11-3:15 RETURN_VALUE', '', ' -- L1: PUSH_EXC_INFO', @@ -1083,7 +1116,7 @@ def test_dis_with_linenos_but_no_columns(self): '', '2:5-2:6 LOAD_SMALL_INT 1', '2:?-2:? STORE_FAST 0 (x)', - '2:?-2:? LOAD_CONST 1 (None)', + '2:?-2:? LOAD_COMMON_CONSTANT 7 (None)', '2:?-2:? RETURN_VALUE', '', ]) @@ -1096,7 +1129,7 @@ def f(): f.__code__ = f.__code__.replace(co_linetable=b'') expect = '\n'.join([ ' RESUME 0', - ' LOAD_CONST 0 (None)', + ' LOAD_COMMON_CONSTANT 7 (None)', ' RETURN_VALUE', '', ]) @@ -1296,13 +1329,15 @@ def code_quicken(f): def test_super_instructions(self): self.code_quicken(lambda: load_test(0, 0)) got = self.get_disassembly(load_test, adaptive=True) - self.do_disassembly_compare(got, dis_load_test_quickened_code) + jit = sys._jit.is_enabled() + expected = dis_load_test_quickened_code.format("_JIT" if jit else "") + self.do_disassembly_compare(got, expected) @cpython_only @requires_specialization def test_load_attr_specialize(self): load_attr_quicken = """\ - 0 RESUME_CHECK 0 + 0 RESUME_CHECK{: <6} 0 1 LOAD_CONST 0 ('a') LOAD_ATTR_SLOT 0 (__class__) @@ -1311,13 +1346,15 @@ def test_load_attr_specialize(self): co = compile("'a'.__class__", "", "eval") self.code_quicken(lambda: exec(co, {}, {})) got = self.get_disassembly(co, adaptive=True) - self.do_disassembly_compare(got, load_attr_quicken) + jit = sys._jit.is_enabled() + expected = load_attr_quicken.format("_JIT" if jit else "") + self.do_disassembly_compare(got, expected) @cpython_only @requires_specialization def test_call_specialize(self): call_quicken = """\ - 0 RESUME_CHECK 0 + 0 RESUME_CHECK{: <6} 0 1 LOAD_NAME 0 (str) PUSH_NULL @@ -1328,7 +1365,9 @@ def test_call_specialize(self): co = compile("str(1)", "", "eval") self.code_quicken(lambda: exec(co, {}, {})) got = self.get_disassembly(co, adaptive=True) - self.do_disassembly_compare(got, call_quicken) + jit = sys._jit.is_enabled() + expected = call_quicken.format("_JIT" if jit else "") + self.do_disassembly_compare(got, expected) @cpython_only @requires_specialization @@ -1337,7 +1376,9 @@ def test_loop_quicken(self): self.code_quicken(loop_test) got = self.get_disassembly(loop_test, adaptive=True) jit = sys._jit.is_enabled() - expected = dis_loop_test_quickened_code.format("JIT" if jit else "NO_JIT") + resume_str = "_JIT" if jit else "" + jit_str = "JIT " if jit else "NO_JIT" + expected = dis_loop_test_quickened_code.format(resume_str, jit_str) self.do_disassembly_compare(got, expected) @cpython_only @@ -1404,7 +1445,7 @@ def test_show_caches(self): caches = list(self.get_cached_values(quickened, adaptive)) for cache in caches: self.assertRegex(cache, pattern) - total_caches = 21 + total_caches = 23 empty_caches = 7 self.assertEqual(caches.count(""), empty_caches) self.assertEqual(len(caches), total_caches) @@ -1441,6 +1482,37 @@ def f(): self.assertEqual(assem_op, assem_cache) + @cpython_only + @requires_specialization + @requires_jit_enabled + def test_show_jit(self): + def loop(n): + for i in range(n): + pass + for _ in range(10): + loop(500) + line = loop.__code__.co_firstlineno + loop_dis = f"""\ +{line} RESUME_CHECK_JIT 0 + +{line+1} LOAD_GLOBAL_BUILTIN 1 (range + NULL) + LOAD_FAST_BORROW 0 (n) + CALL_BUILTIN_CLASS 1 + GET_ITER 0 + L1: FOR_ITER_RANGE 3 (to L2) + STORE_FAST 1 (i) + +{line+2} ENTER_EXECUTOR 0 + +{line+1} L2: END_FOR + POP_ITER + LOAD_COMMON_CONSTANT 7 (None) + RETURN_VALUE +""" + got = self.get_disassembly(loop, adaptive=True, show_jit=True) + self.do_disassembly_compare(got, loop_dis) + + class DisWithFileTests(DisTests): # Run the tests again, using the file arg instead of print @@ -1466,7 +1538,7 @@ def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs): Kw-only arguments: 0 Number of locals: 1 Stack size: \\d+ -Flags: OPTIMIZED, NEWLOCALS, HAS_DOCSTRING +Flags: OPTIMIZED, NEWLOCALS(, HAS_DOCSTRING)? Constants: {code_info_consts} Names: @@ -1493,7 +1565,6 @@ def f(c=c): Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, GENERATOR Constants: 0: - 1: None Variable names: 0: a 1: b @@ -1565,7 +1636,6 @@ def f(c=c): Flags: 0x0 Constants: 0: 1 - 1: None Names: 0: x""" @@ -1600,7 +1670,6 @@ async def async_def(): Flags: OPTIMIZED, NEWLOCALS, COROUTINE Constants: 0: 1 - 1: None Names: 0: b 1: c @@ -1749,210 +1818,210 @@ def _prepare_test_cases(): expected_opinfo_outer = [ make_inst(opname='MAKE_CELL', arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None), make_inst(opname='MAKE_CELL', arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None), - make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1), - make_inst(opname='LOAD_CONST', arg=4, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2), - make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2), - make_inst(opname='LOAD_FAST_BORROW', arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2), - make_inst(opname='BUILD_TUPLE', arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2), - make_inst(opname='LOAD_CONST', arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2), - make_inst(opname='MAKE_FUNCTION', arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2), - make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2), - make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2), - make_inst(opname='STORE_FAST', arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2), - make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_DEREF', arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7), - make_inst(opname='LOAD_DEREF', arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7), - make_inst(opname='LOAD_CONST', arg=2, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7), - make_inst(opname='LOAD_SMALL_INT', arg=1, argval=1, argrepr='', offset=40, start_offset=40, starts_line=False, line_number=7), - make_inst(opname='BUILD_LIST', arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7), - make_inst(opname='BUILD_MAP', arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7), - make_inst(opname='LOAD_CONST', arg=3, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7), - make_inst(opname='CALL', arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7), - make_inst(opname='LOAD_FAST_BORROW', arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8), - make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8), + make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='LOAD_CONST', arg=3, argval=(3, 4), argrepr='(3, 4)', offset=8, start_offset=8, starts_line=True, line_number=2), + make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=2), + make_inst(opname='LOAD_FAST_BORROW', arg=1, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=2), + make_inst(opname='BUILD_TUPLE', arg=2, argval=2, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=2), + make_inst(opname='LOAD_CONST', arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=16, start_offset=16, starts_line=False, line_number=2), + make_inst(opname='MAKE_FUNCTION', arg=None, argval=None, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=2), + make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=8, argval=8, argrepr='closure', offset=20, start_offset=20, starts_line=False, line_number=2), + make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=1, argval=1, argrepr='defaults', offset=22, start_offset=22, starts_line=False, line_number=2), + make_inst(opname='STORE_FAST', arg=2, argval='f', argrepr='f', offset=24, start_offset=24, starts_line=False, line_number=2), + make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print + NULL', offset=26, start_offset=26, starts_line=True, line_number=7, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_DEREF', arg=0, argval='a', argrepr='a', offset=36, start_offset=36, starts_line=False, line_number=7), + make_inst(opname='LOAD_DEREF', arg=1, argval='b', argrepr='b', offset=38, start_offset=38, starts_line=False, line_number=7), + make_inst(opname='LOAD_COMMON_CONSTANT', arg=8, argval='', argrepr="''", offset=40, start_offset=40, starts_line=False, line_number=7), + make_inst(opname='LOAD_SMALL_INT', arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7), + make_inst(opname='BUILD_LIST', arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7), + make_inst(opname='BUILD_MAP', arg=0, argval=0, argrepr='', offset=46, start_offset=46, starts_line=False, line_number=7), + make_inst(opname='LOAD_CONST', arg=2, argval='Hello world!', argrepr="'Hello world!'", offset=48, start_offset=48, starts_line=False, line_number=7), + make_inst(opname='CALL', arg=7, argval=7, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=7, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=58, start_offset=58, starts_line=False, line_number=7), + make_inst(opname='LOAD_FAST_BORROW', arg=2, argval='f', argrepr='f', offset=60, start_offset=60, starts_line=True, line_number=8), + make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=62, start_offset=62, starts_line=False, line_number=8), ] expected_opinfo_f = [ make_inst(opname='COPY_FREE_VARS', arg=2, argval=2, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None), make_inst(opname='MAKE_CELL', arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None), make_inst(opname='MAKE_CELL', arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None), - make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2), - make_inst(opname='LOAD_CONST', arg=2, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3), - make_inst(opname='LOAD_FAST_BORROW', arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3), - make_inst(opname='LOAD_FAST_BORROW', arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3), - make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3), - make_inst(opname='LOAD_FAST_BORROW', arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3), - make_inst(opname='BUILD_TUPLE', arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3), - make_inst(opname='LOAD_CONST', arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3), - make_inst(opname='MAKE_FUNCTION', arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3), - make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3), - make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3), - make_inst(opname='STORE_FAST', arg=2, argval='inner', argrepr='inner', offset=28, start_offset=28, starts_line=False, line_number=3), - make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=5, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_DEREF', arg=3, argval='a', argrepr='a', offset=40, start_offset=40, starts_line=False, line_number=5), - make_inst(opname='LOAD_DEREF', arg=4, argval='b', argrepr='b', offset=42, start_offset=42, starts_line=False, line_number=5), - make_inst(opname='LOAD_DEREF', arg=0, argval='c', argrepr='c', offset=44, start_offset=44, starts_line=False, line_number=5), - make_inst(opname='LOAD_DEREF', arg=1, argval='d', argrepr='d', offset=46, start_offset=46, starts_line=False, line_number=5), - make_inst(opname='CALL', arg=4, argval=4, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=5, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=5), - make_inst(opname='LOAD_FAST_BORROW', arg=2, argval='inner', argrepr='inner', offset=58, start_offset=58, starts_line=True, line_number=6), - make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=6), + make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='LOAD_CONST', arg=2, argval=(5, 6), argrepr='(5, 6)', offset=10, start_offset=10, starts_line=True, line_number=3), + make_inst(opname='LOAD_FAST_BORROW', arg=3, argval='a', argrepr='a', offset=12, start_offset=12, starts_line=False, line_number=3), + make_inst(opname='LOAD_FAST_BORROW', arg=4, argval='b', argrepr='b', offset=14, start_offset=14, starts_line=False, line_number=3), + make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='c', argrepr='c', offset=16, start_offset=16, starts_line=False, line_number=3), + make_inst(opname='LOAD_FAST_BORROW', arg=1, argval='d', argrepr='d', offset=18, start_offset=18, starts_line=False, line_number=3), + make_inst(opname='BUILD_TUPLE', arg=4, argval=4, argrepr='', offset=20, start_offset=20, starts_line=False, line_number=3), + make_inst(opname='LOAD_CONST', arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=22, start_offset=22, starts_line=False, line_number=3), + make_inst(opname='MAKE_FUNCTION', arg=None, argval=None, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=3), + make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=8, argval=8, argrepr='closure', offset=26, start_offset=26, starts_line=False, line_number=3), + make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=1, argval=1, argrepr='defaults', offset=28, start_offset=28, starts_line=False, line_number=3), + make_inst(opname='STORE_FAST', arg=2, argval='inner', argrepr='inner', offset=30, start_offset=30, starts_line=False, line_number=3), + make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print + NULL', offset=32, start_offset=32, starts_line=True, line_number=5, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_DEREF', arg=3, argval='a', argrepr='a', offset=42, start_offset=42, starts_line=False, line_number=5), + make_inst(opname='LOAD_DEREF', arg=4, argval='b', argrepr='b', offset=44, start_offset=44, starts_line=False, line_number=5), + make_inst(opname='LOAD_DEREF', arg=0, argval='c', argrepr='c', offset=46, start_offset=46, starts_line=False, line_number=5), + make_inst(opname='LOAD_DEREF', arg=1, argval='d', argrepr='d', offset=48, start_offset=48, starts_line=False, line_number=5), + make_inst(opname='CALL', arg=4, argval=4, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=5, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=58, start_offset=58, starts_line=False, line_number=5), + make_inst(opname='LOAD_FAST_BORROW', arg=2, argval='inner', argrepr='inner', offset=60, start_offset=60, starts_line=True, line_number=6), + make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=62, start_offset=62, starts_line=False, line_number=6), ] expected_opinfo_inner = [ make_inst(opname='COPY_FREE_VARS', arg=4, argval=4, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=None), - make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=2, start_offset=2, starts_line=True, line_number=3), - make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print + NULL', offset=4, start_offset=4, starts_line=True, line_number=4, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_DEREF', arg=2, argval='a', argrepr='a', offset=14, start_offset=14, starts_line=False, line_number=4), - make_inst(opname='LOAD_DEREF', arg=3, argval='b', argrepr='b', offset=16, start_offset=16, starts_line=False, line_number=4), - make_inst(opname='LOAD_DEREF', arg=4, argval='c', argrepr='c', offset=18, start_offset=18, starts_line=False, line_number=4), - make_inst(opname='LOAD_DEREF', arg=5, argval='d', argrepr='d', offset=20, start_offset=20, starts_line=False, line_number=4), - make_inst(opname='LOAD_FAST_BORROW_LOAD_FAST_BORROW', arg=1, argval=('e', 'f'), argrepr='e, f', offset=22, start_offset=22, starts_line=False, line_number=4), - make_inst(opname='CALL', arg=6, argval=6, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=4, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=32, start_offset=32, starts_line=False, line_number=4), - make_inst(opname='LOAD_CONST', arg=0, argval=None, argrepr='None', offset=34, start_offset=34, starts_line=False, line_number=4), - make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=36, start_offset=36, starts_line=False, line_number=4), + make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=2, start_offset=2, starts_line=True, line_number=3, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print + NULL', offset=6, start_offset=6, starts_line=True, line_number=4, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_DEREF', arg=2, argval='a', argrepr='a', offset=16, start_offset=16, starts_line=False, line_number=4), + make_inst(opname='LOAD_DEREF', arg=3, argval='b', argrepr='b', offset=18, start_offset=18, starts_line=False, line_number=4), + make_inst(opname='LOAD_DEREF', arg=4, argval='c', argrepr='c', offset=20, start_offset=20, starts_line=False, line_number=4), + make_inst(opname='LOAD_DEREF', arg=5, argval='d', argrepr='d', offset=22, start_offset=22, starts_line=False, line_number=4), + make_inst(opname='LOAD_FAST_BORROW_LOAD_FAST_BORROW', arg=1, argval=('e', 'f'), argrepr='e, f', offset=24, start_offset=24, starts_line=False, line_number=4), + make_inst(opname='CALL', arg=6, argval=6, argrepr='', offset=26, start_offset=26, starts_line=False, line_number=4, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=34, start_offset=34, starts_line=False, line_number=4), + make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None', offset=36, start_offset=36, starts_line=False, line_number=4), + make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=38, start_offset=38, starts_line=False, line_number=4), ] expected_opinfo_jumpy = [ - make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=1), - make_inst(opname='LOAD_GLOBAL', arg=1, argval='range', argrepr='range + NULL', offset=2, start_offset=2, starts_line=True, line_number=3, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_SMALL_INT', arg=10, argval=10, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=3), - make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='GET_ITER', arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3), - make_inst(opname='FOR_ITER', arg=33, argval=94, argrepr='to L4', offset=24, start_offset=24, starts_line=False, line_number=3, label=1, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='STORE_FAST', arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3), - make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4), - make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=4, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=4), - make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5), - make_inst(opname='LOAD_SMALL_INT', arg=4, argval=4, argrepr='', offset=54, start_offset=54, starts_line=False, line_number=5), - make_inst(opname='COMPARE_OP', arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='POP_JUMP_IF_FALSE', arg=3, argval=70, argrepr='to L2', offset=60, start_offset=60, starts_line=False, line_number=5, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=64, start_offset=64, starts_line=False, line_number=5), - make_inst(opname='JUMP_BACKWARD', arg=23, argval=24, argrepr='to L1', offset=66, start_offset=66, starts_line=True, line_number=6, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=70, start_offset=70, starts_line=True, line_number=7, label=2), - make_inst(opname='LOAD_SMALL_INT', arg=6, argval=6, argrepr='', offset=72, start_offset=72, starts_line=False, line_number=7), - make_inst(opname='COMPARE_OP', arg=148, argval='>', argrepr='bool(>)', offset=74, start_offset=74, starts_line=False, line_number=7, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='POP_JUMP_IF_TRUE', arg=3, argval=88, argrepr='to L3', offset=78, start_offset=78, starts_line=False, line_number=7, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=82, start_offset=82, starts_line=False, line_number=7), - make_inst(opname='JUMP_BACKWARD', arg=32, argval=24, argrepr='to L1', offset=84, start_offset=84, starts_line=False, line_number=7, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=8, label=3), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=90, start_offset=90, starts_line=False, line_number=8), - make_inst(opname='JUMP_FORWARD', arg=13, argval=120, argrepr='to L5', offset=92, start_offset=92, starts_line=False, line_number=8), - make_inst(opname='END_FOR', arg=None, argval=None, argrepr='', offset=94, start_offset=94, starts_line=True, line_number=3, label=4), - make_inst(opname='POP_ITER', arg=None, argval=None, argrepr='', offset=96, start_offset=96, starts_line=False, line_number=3), - make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=98, start_offset=98, starts_line=True, line_number=10, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_CONST', arg=1, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=108, start_offset=108, starts_line=False, line_number=10), - make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=110, start_offset=110, starts_line=False, line_number=10, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=118, start_offset=118, starts_line=False, line_number=10), - make_inst(opname='LOAD_FAST_CHECK', arg=0, argval='i', argrepr='i', offset=120, start_offset=120, starts_line=True, line_number=11, label=5), - make_inst(opname='TO_BOOL', arg=None, argval=None, argrepr='', offset=122, start_offset=122, starts_line=False, line_number=11, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_JUMP_IF_FALSE', arg=40, argval=214, argrepr='to L8', offset=130, start_offset=130, starts_line=False, line_number=11, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=134, start_offset=134, starts_line=False, line_number=11), - make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=136, start_offset=136, starts_line=True, line_number=12, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=146, start_offset=146, starts_line=False, line_number=12), - make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=148, start_offset=148, starts_line=False, line_number=12, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=156, start_offset=156, starts_line=False, line_number=12), - make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=True, line_number=13), - make_inst(opname='LOAD_SMALL_INT', arg=1, argval=1, argrepr='', offset=160, start_offset=160, starts_line=False, line_number=13), - make_inst(opname='BINARY_OP', arg=23, argval=23, argrepr='-=', offset=162, start_offset=162, starts_line=False, line_number=13, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]), - make_inst(opname='STORE_FAST', arg=0, argval='i', argrepr='i', offset=174, start_offset=174, starts_line=False, line_number=13), - make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=176, start_offset=176, starts_line=True, line_number=14), - make_inst(opname='LOAD_SMALL_INT', arg=6, argval=6, argrepr='', offset=178, start_offset=178, starts_line=False, line_number=14), - make_inst(opname='COMPARE_OP', arg=148, argval='>', argrepr='bool(>)', offset=180, start_offset=180, starts_line=False, line_number=14, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='POP_JUMP_IF_FALSE', arg=3, argval=194, argrepr='to L6', offset=184, start_offset=184, starts_line=False, line_number=14, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=188, start_offset=188, starts_line=False, line_number=14), - make_inst(opname='JUMP_BACKWARD', arg=37, argval=120, argrepr='to L5', offset=190, start_offset=190, starts_line=True, line_number=15, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=194, start_offset=194, starts_line=True, line_number=16, label=6), - make_inst(opname='LOAD_SMALL_INT', arg=4, argval=4, argrepr='', offset=196, start_offset=196, starts_line=False, line_number=16), - make_inst(opname='COMPARE_OP', arg=18, argval='<', argrepr='bool(<)', offset=198, start_offset=198, starts_line=False, line_number=16, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='POP_JUMP_IF_TRUE', arg=3, argval=212, argrepr='to L7', offset=202, start_offset=202, starts_line=False, line_number=16, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=206, start_offset=206, starts_line=False, line_number=16), - make_inst(opname='JUMP_BACKWARD', arg=46, argval=120, argrepr='to L5', offset=208, start_offset=208, starts_line=False, line_number=16, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='JUMP_FORWARD', arg=11, argval=236, argrepr='to L9', offset=212, start_offset=212, starts_line=True, line_number=17, label=7), - make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=214, start_offset=214, starts_line=True, line_number=19, label=8, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_CONST', arg=2, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=224, start_offset=224, starts_line=False, line_number=19), - make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=226, start_offset=226, starts_line=False, line_number=19, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=234, start_offset=234, starts_line=False, line_number=19), - make_inst(opname='NOP', arg=None, argval=None, argrepr='', offset=236, start_offset=236, starts_line=True, line_number=20, label=9), - make_inst(opname='LOAD_SMALL_INT', arg=1, argval=1, argrepr='', offset=238, start_offset=238, starts_line=True, line_number=21), - make_inst(opname='LOAD_SMALL_INT', arg=0, argval=0, argrepr='', offset=240, start_offset=240, starts_line=False, line_number=21), - make_inst(opname='BINARY_OP', arg=11, argval=11, argrepr='/', offset=242, start_offset=242, starts_line=False, line_number=21, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=254, start_offset=254, starts_line=False, line_number=21), - make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=256, start_offset=256, starts_line=True, line_number=25), - make_inst(opname='COPY', arg=1, argval=1, argrepr='', offset=258, start_offset=258, starts_line=False, line_number=25), - make_inst(opname='LOAD_SPECIAL', arg=1, argval=1, argrepr='__exit__', offset=260, start_offset=260, starts_line=False, line_number=25), - make_inst(opname='SWAP', arg=2, argval=2, argrepr='', offset=262, start_offset=262, starts_line=False, line_number=25), - make_inst(opname='SWAP', arg=3, argval=3, argrepr='', offset=264, start_offset=264, starts_line=False, line_number=25), - make_inst(opname='LOAD_SPECIAL', arg=0, argval=0, argrepr='__enter__', offset=266, start_offset=266, starts_line=False, line_number=25), - make_inst(opname='CALL', arg=0, argval=0, argrepr='', offset=268, start_offset=268, starts_line=False, line_number=25, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='STORE_FAST', arg=1, argval='dodgy', argrepr='dodgy', offset=276, start_offset=276, starts_line=False, line_number=25), - make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=278, start_offset=278, starts_line=True, line_number=26, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_CONST', arg=3, argval='Never reach this', argrepr="'Never reach this'", offset=288, start_offset=288, starts_line=False, line_number=26), - make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=290, start_offset=290, starts_line=False, line_number=26, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=298, start_offset=298, starts_line=False, line_number=26), - make_inst(opname='LOAD_CONST', arg=4, argval=None, argrepr='None', offset=300, start_offset=300, starts_line=True, line_number=25), - make_inst(opname='LOAD_CONST', arg=4, argval=None, argrepr='None', offset=302, start_offset=302, starts_line=False, line_number=25), - make_inst(opname='LOAD_CONST', arg=4, argval=None, argrepr='None', offset=304, start_offset=304, starts_line=False, line_number=25), - make_inst(opname='CALL', arg=3, argval=3, argrepr='', offset=306, start_offset=306, starts_line=False, line_number=25, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=314, start_offset=314, starts_line=False, line_number=25), - make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=316, start_offset=316, starts_line=True, line_number=28, label=10, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_CONST', arg=6, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=326, start_offset=326, starts_line=False, line_number=28), - make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=28, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=336, start_offset=336, starts_line=False, line_number=28), - make_inst(opname='LOAD_CONST', arg=4, argval=None, argrepr='None', offset=338, start_offset=338, starts_line=False, line_number=28), - make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=False, line_number=28), - make_inst(opname='PUSH_EXC_INFO', arg=None, argval=None, argrepr='', offset=342, start_offset=342, starts_line=True, line_number=25), - make_inst(opname='WITH_EXCEPT_START', arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=25), - make_inst(opname='TO_BOOL', arg=None, argval=None, argrepr='', offset=346, start_offset=346, starts_line=False, line_number=25, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_JUMP_IF_TRUE', arg=2, argval=362, argrepr='to L11', offset=354, start_offset=354, starts_line=False, line_number=25, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=358, start_offset=358, starts_line=False, line_number=25), - make_inst(opname='RERAISE', arg=2, argval=2, argrepr='', offset=360, start_offset=360, starts_line=False, line_number=25), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=25, label=11), - make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=364, start_offset=364, starts_line=False, line_number=25), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=366, start_offset=366, starts_line=False, line_number=25), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=368, start_offset=368, starts_line=False, line_number=25), + make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=1, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='LOAD_GLOBAL', arg=1, argval='range', argrepr='range + NULL', offset=4, start_offset=4, starts_line=True, line_number=3, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_SMALL_INT', arg=10, argval=10, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3), + make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=3, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='GET_ITER', arg=0, argval=0, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=3, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='FOR_ITER', arg=33, argval=98, argrepr='to L4', offset=28, start_offset=28, starts_line=False, line_number=3, label=1, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='STORE_FAST', arg=0, argval='i', argrepr='i', offset=32, start_offset=32, starts_line=False, line_number=3), + make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=34, start_offset=34, starts_line=True, line_number=4, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=44, start_offset=44, starts_line=False, line_number=4), + make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=46, start_offset=46, starts_line=False, line_number=4, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=54, start_offset=54, starts_line=False, line_number=4), + make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=56, start_offset=56, starts_line=True, line_number=5), + make_inst(opname='LOAD_SMALL_INT', arg=4, argval=4, argrepr='', offset=58, start_offset=58, starts_line=False, line_number=5), + make_inst(opname='COMPARE_OP', arg=18, argval='<', argrepr='bool(<)', offset=60, start_offset=60, starts_line=False, line_number=5, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='POP_JUMP_IF_FALSE', arg=3, argval=74, argrepr='to L2', offset=64, start_offset=64, starts_line=False, line_number=5, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=68, start_offset=68, starts_line=False, line_number=5), + make_inst(opname='JUMP_BACKWARD', arg=23, argval=28, argrepr='to L1', offset=70, start_offset=70, starts_line=True, line_number=6, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=74, start_offset=74, starts_line=True, line_number=7, label=2), + make_inst(opname='LOAD_SMALL_INT', arg=6, argval=6, argrepr='', offset=76, start_offset=76, starts_line=False, line_number=7), + make_inst(opname='COMPARE_OP', arg=148, argval='>', argrepr='bool(>)', offset=78, start_offset=78, starts_line=False, line_number=7, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='POP_JUMP_IF_TRUE', arg=3, argval=92, argrepr='to L3', offset=82, start_offset=82, starts_line=False, line_number=7, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=86, start_offset=86, starts_line=False, line_number=7), + make_inst(opname='JUMP_BACKWARD', arg=32, argval=28, argrepr='to L1', offset=88, start_offset=88, starts_line=False, line_number=7, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=92, start_offset=92, starts_line=True, line_number=8, label=3), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=94, start_offset=94, starts_line=False, line_number=8), + make_inst(opname='JUMP_FORWARD', arg=13, argval=124, argrepr='to L5', offset=96, start_offset=96, starts_line=False, line_number=8), + make_inst(opname='END_FOR', arg=None, argval=None, argrepr='', offset=98, start_offset=98, starts_line=True, line_number=3, label=4), + make_inst(opname='POP_ITER', arg=None, argval=None, argrepr='', offset=100, start_offset=100, starts_line=False, line_number=3), + make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=102, start_offset=102, starts_line=True, line_number=10, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_CONST', arg=1, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=112, start_offset=112, starts_line=False, line_number=10), + make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=114, start_offset=114, starts_line=False, line_number=10, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=122, start_offset=122, starts_line=False, line_number=10), + make_inst(opname='LOAD_FAST_CHECK', arg=0, argval='i', argrepr='i', offset=124, start_offset=124, starts_line=True, line_number=11, label=5), + make_inst(opname='TO_BOOL', arg=None, argval=None, argrepr='', offset=126, start_offset=126, starts_line=False, line_number=11, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_JUMP_IF_FALSE', arg=40, argval=218, argrepr='to L8', offset=134, start_offset=134, starts_line=False, line_number=11, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=138, start_offset=138, starts_line=False, line_number=11), + make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=140, start_offset=140, starts_line=True, line_number=12, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=150, start_offset=150, starts_line=False, line_number=12), + make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=152, start_offset=152, starts_line=False, line_number=12, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=160, start_offset=160, starts_line=False, line_number=12), + make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=162, start_offset=162, starts_line=True, line_number=13), + make_inst(opname='LOAD_SMALL_INT', arg=1, argval=1, argrepr='', offset=164, start_offset=164, starts_line=False, line_number=13), + make_inst(opname='BINARY_OP', arg=23, argval=23, argrepr='-=', offset=166, start_offset=166, starts_line=False, line_number=13, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]), + make_inst(opname='STORE_FAST', arg=0, argval='i', argrepr='i', offset=178, start_offset=178, starts_line=False, line_number=13), + make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=180, start_offset=180, starts_line=True, line_number=14), + make_inst(opname='LOAD_SMALL_INT', arg=6, argval=6, argrepr='', offset=182, start_offset=182, starts_line=False, line_number=14), + make_inst(opname='COMPARE_OP', arg=148, argval='>', argrepr='bool(>)', offset=184, start_offset=184, starts_line=False, line_number=14, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='POP_JUMP_IF_FALSE', arg=3, argval=198, argrepr='to L6', offset=188, start_offset=188, starts_line=False, line_number=14, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=192, start_offset=192, starts_line=False, line_number=14), + make_inst(opname='JUMP_BACKWARD', arg=37, argval=124, argrepr='to L5', offset=194, start_offset=194, starts_line=True, line_number=15, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=198, start_offset=198, starts_line=True, line_number=16, label=6), + make_inst(opname='LOAD_SMALL_INT', arg=4, argval=4, argrepr='', offset=200, start_offset=200, starts_line=False, line_number=16), + make_inst(opname='COMPARE_OP', arg=18, argval='<', argrepr='bool(<)', offset=202, start_offset=202, starts_line=False, line_number=16, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='POP_JUMP_IF_TRUE', arg=3, argval=216, argrepr='to L7', offset=206, start_offset=206, starts_line=False, line_number=16, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=210, start_offset=210, starts_line=False, line_number=16), + make_inst(opname='JUMP_BACKWARD', arg=46, argval=124, argrepr='to L5', offset=212, start_offset=212, starts_line=False, line_number=16, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='JUMP_FORWARD', arg=11, argval=240, argrepr='to L9', offset=216, start_offset=216, starts_line=True, line_number=17, label=7), + make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=218, start_offset=218, starts_line=True, line_number=19, label=8, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_CONST', arg=2, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=228, start_offset=228, starts_line=False, line_number=19), + make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=230, start_offset=230, starts_line=False, line_number=19, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=238, start_offset=238, starts_line=False, line_number=19), + make_inst(opname='NOP', arg=None, argval=None, argrepr='', offset=240, start_offset=240, starts_line=True, line_number=20, label=9), + make_inst(opname='LOAD_SMALL_INT', arg=1, argval=1, argrepr='', offset=242, start_offset=242, starts_line=True, line_number=21), + make_inst(opname='LOAD_SMALL_INT', arg=0, argval=0, argrepr='', offset=244, start_offset=244, starts_line=False, line_number=21), + make_inst(opname='BINARY_OP', arg=11, argval=11, argrepr='/', offset=246, start_offset=246, starts_line=False, line_number=21, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=258, start_offset=258, starts_line=False, line_number=21), + make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=260, start_offset=260, starts_line=True, line_number=25), + make_inst(opname='COPY', arg=1, argval=1, argrepr='', offset=262, start_offset=262, starts_line=False, line_number=25), + make_inst(opname='LOAD_SPECIAL', arg=1, argval=1, argrepr='__exit__', offset=264, start_offset=264, starts_line=False, line_number=25), + make_inst(opname='SWAP', arg=2, argval=2, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=25), + make_inst(opname='SWAP', arg=3, argval=3, argrepr='', offset=268, start_offset=268, starts_line=False, line_number=25), + make_inst(opname='LOAD_SPECIAL', arg=0, argval=0, argrepr='__enter__', offset=270, start_offset=270, starts_line=False, line_number=25), + make_inst(opname='CALL', arg=0, argval=0, argrepr='', offset=272, start_offset=272, starts_line=False, line_number=25, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='STORE_FAST', arg=1, argval='dodgy', argrepr='dodgy', offset=280, start_offset=280, starts_line=False, line_number=25), + make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=282, start_offset=282, starts_line=True, line_number=26, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_CONST', arg=3, argval='Never reach this', argrepr="'Never reach this'", offset=292, start_offset=292, starts_line=False, line_number=26), + make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=294, start_offset=294, starts_line=False, line_number=26, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=302, start_offset=302, starts_line=False, line_number=26), + make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None', offset=304, start_offset=304, starts_line=True, line_number=25), + make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None', offset=306, start_offset=306, starts_line=False, line_number=25), + make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None', offset=308, start_offset=308, starts_line=False, line_number=25), + make_inst(opname='CALL', arg=3, argval=3, argrepr='', offset=310, start_offset=310, starts_line=False, line_number=25, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=318, start_offset=318, starts_line=False, line_number=25), + make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=320, start_offset=320, starts_line=True, line_number=28, label=10, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_CONST', arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=330, start_offset=330, starts_line=False, line_number=28), + make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=28, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=False, line_number=28), + make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None', offset=342, start_offset=342, starts_line=False, line_number=28), + make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=28), + make_inst(opname='PUSH_EXC_INFO', arg=None, argval=None, argrepr='', offset=346, start_offset=346, starts_line=True, line_number=25), + make_inst(opname='WITH_EXCEPT_START', arg=None, argval=None, argrepr='', offset=348, start_offset=348, starts_line=False, line_number=25), + make_inst(opname='TO_BOOL', arg=None, argval=None, argrepr='', offset=350, start_offset=350, starts_line=False, line_number=25, cache_info=[('counter', 1, b'\x00\x00'), ('version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_JUMP_IF_TRUE', arg=2, argval=366, argrepr='to L11', offset=358, start_offset=358, starts_line=False, line_number=25, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=25), + make_inst(opname='RERAISE', arg=2, argval=2, argrepr='', offset=364, start_offset=364, starts_line=False, line_number=25), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=366, start_offset=366, starts_line=False, line_number=25, label=11), + make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=368, start_offset=368, starts_line=False, line_number=25), make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=370, start_offset=370, starts_line=False, line_number=25), - make_inst(opname='JUMP_BACKWARD_NO_INTERRUPT', arg=29, argval=316, argrepr='to L10', offset=372, start_offset=372, starts_line=False, line_number=25), - make_inst(opname='COPY', arg=3, argval=3, argrepr='', offset=374, start_offset=374, starts_line=True, line_number=None), - make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=376, start_offset=376, starts_line=False, line_number=None), - make_inst(opname='RERAISE', arg=1, argval=1, argrepr='', offset=378, start_offset=378, starts_line=False, line_number=None), - make_inst(opname='PUSH_EXC_INFO', arg=None, argval=None, argrepr='', offset=380, start_offset=380, starts_line=False, line_number=None), - make_inst(opname='LOAD_GLOBAL', arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=382, start_offset=382, starts_line=True, line_number=22, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='CHECK_EXC_MATCH', arg=None, argval=None, argrepr='', offset=392, start_offset=392, starts_line=False, line_number=22), - make_inst(opname='POP_JUMP_IF_FALSE', arg=15, argval=428, argrepr='to L12', offset=394, start_offset=394, starts_line=False, line_number=22, cache_info=[('counter', 1, b'\x00\x00')]), - make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=398, start_offset=398, starts_line=False, line_number=22), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=400, start_offset=400, starts_line=False, line_number=22), - make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=402, start_offset=402, starts_line=True, line_number=23, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_CONST', arg=5, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=412, start_offset=412, starts_line=False, line_number=23), - make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=23, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=23), - make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=424, start_offset=424, starts_line=False, line_number=23), - make_inst(opname='JUMP_BACKWARD_NO_INTERRUPT', arg=56, argval=316, argrepr='to L10', offset=426, start_offset=426, starts_line=False, line_number=23), - make_inst(opname='RERAISE', arg=0, argval=0, argrepr='', offset=428, start_offset=428, starts_line=True, line_number=22, label=12), - make_inst(opname='COPY', arg=3, argval=3, argrepr='', offset=430, start_offset=430, starts_line=True, line_number=None), - make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=432, start_offset=432, starts_line=False, line_number=None), - make_inst(opname='RERAISE', arg=1, argval=1, argrepr='', offset=434, start_offset=434, starts_line=False, line_number=None), - make_inst(opname='PUSH_EXC_INFO', arg=None, argval=None, argrepr='', offset=436, start_offset=436, starts_line=False, line_number=None), - make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=438, start_offset=438, starts_line=True, line_number=28, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), - make_inst(opname='LOAD_CONST', arg=6, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=448, start_offset=448, starts_line=False, line_number=28), - make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=450, start_offset=450, starts_line=False, line_number=28, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), - make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=458, start_offset=458, starts_line=False, line_number=28), - make_inst(opname='RERAISE', arg=0, argval=0, argrepr='', offset=460, start_offset=460, starts_line=False, line_number=28), - make_inst(opname='COPY', arg=3, argval=3, argrepr='', offset=462, start_offset=462, starts_line=True, line_number=None), - make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=464, start_offset=464, starts_line=False, line_number=None), - make_inst(opname='RERAISE', arg=1, argval=1, argrepr='', offset=466, start_offset=466, starts_line=False, line_number=None), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=372, start_offset=372, starts_line=False, line_number=25), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=374, start_offset=374, starts_line=False, line_number=25), + make_inst(opname='JUMP_BACKWARD_NO_INTERRUPT', arg=29, argval=320, argrepr='to L10', offset=376, start_offset=376, starts_line=False, line_number=25), + make_inst(opname='COPY', arg=3, argval=3, argrepr='', offset=378, start_offset=378, starts_line=True, line_number=None), + make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=380, start_offset=380, starts_line=False, line_number=None), + make_inst(opname='RERAISE', arg=1, argval=1, argrepr='', offset=382, start_offset=382, starts_line=False, line_number=None), + make_inst(opname='PUSH_EXC_INFO', arg=None, argval=None, argrepr='', offset=384, start_offset=384, starts_line=False, line_number=None), + make_inst(opname='LOAD_GLOBAL', arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=386, start_offset=386, starts_line=True, line_number=22, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='CHECK_EXC_MATCH', arg=None, argval=None, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=22), + make_inst(opname='POP_JUMP_IF_FALSE', arg=15, argval=432, argrepr='to L12', offset=398, start_offset=398, starts_line=False, line_number=22, cache_info=[('counter', 1, b'\x00\x00')]), + make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=402, start_offset=402, starts_line=False, line_number=22), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=404, start_offset=404, starts_line=False, line_number=22), + make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=406, start_offset=406, starts_line=True, line_number=23, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_CONST', arg=4, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=416, start_offset=416, starts_line=False, line_number=23), + make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=418, start_offset=418, starts_line=False, line_number=23, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=426, start_offset=426, starts_line=False, line_number=23), + make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=428, start_offset=428, starts_line=False, line_number=23), + make_inst(opname='JUMP_BACKWARD_NO_INTERRUPT', arg=56, argval=320, argrepr='to L10', offset=430, start_offset=430, starts_line=False, line_number=23), + make_inst(opname='RERAISE', arg=0, argval=0, argrepr='', offset=432, start_offset=432, starts_line=True, line_number=22, label=12), + make_inst(opname='COPY', arg=3, argval=3, argrepr='', offset=434, start_offset=434, starts_line=True, line_number=None), + make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=436, start_offset=436, starts_line=False, line_number=None), + make_inst(opname='RERAISE', arg=1, argval=1, argrepr='', offset=438, start_offset=438, starts_line=False, line_number=None), + make_inst(opname='PUSH_EXC_INFO', arg=None, argval=None, argrepr='', offset=440, start_offset=440, starts_line=False, line_number=None), + make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print + NULL', offset=442, start_offset=442, starts_line=True, line_number=28, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]), + make_inst(opname='LOAD_CONST', arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=452, start_offset=452, starts_line=False, line_number=28), + make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=454, start_offset=454, starts_line=False, line_number=28, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]), + make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=462, start_offset=462, starts_line=False, line_number=28), + make_inst(opname='RERAISE', arg=0, argval=0, argrepr='', offset=464, start_offset=464, starts_line=False, line_number=28), + make_inst(opname='COPY', arg=3, argval=3, argrepr='', offset=466, start_offset=466, starts_line=True, line_number=None), + make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='', offset=468, start_offset=468, starts_line=False, line_number=None), + make_inst(opname='RERAISE', arg=1, argval=1, argrepr='', offset=470, start_offset=470, starts_line=False, line_number=None), ] # One last piece of inspect fodder to check the default line number handling def simple(): pass expected_opinfo_simple = [ make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=0, start_offset=0, starts_line=True, line_number=simple.__code__.co_firstlineno), - make_inst(opname='LOAD_CONST', arg=0, argval=None, argrepr='None', offset=2, start_offset=2, starts_line=False, line_number=simple.__code__.co_firstlineno), - make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=4, start_offset=4, starts_line=False, line_number=simple.__code__.co_firstlineno), + make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None', offset=4, start_offset=4, starts_line=False, line_number=simple.__code__.co_firstlineno), + make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=6, start_offset=6, starts_line=False, line_number=simple.__code__.co_firstlineno), ] @@ -2343,8 +2412,10 @@ def test_from_traceback_dis(self): @requires_debug_ranges() def test_bytecode_co_positions(self): bytecode = dis.Bytecode("a=1") - for instr, positions in zip(bytecode, bytecode.codeobj.co_positions()): - assert instr.positions == positions + it = bytecode.codeobj.co_positions() + next(it) # Ignore the CACHE for the RESUME + for instr, positions in zip(bytecode, it): + self.assertEqual(instr.positions, positions) class TestBytecodeTestCase(BytecodeTestCase): def test_assert_not_in_with_op_not_in_bytecode(self): @@ -2412,7 +2483,7 @@ def func(): code = func.__code__ offsets = [linestart[0] for linestart in dis.findlinestarts(code)] - self.assertEqual(offsets, [0, 2]) + self.assertEqual(offsets, [0, 4]) class TestDisTraceback(DisTestBase): @@ -2549,6 +2620,7 @@ def test_show_cache(self): source = 'print()' expect = ''' 0 RESUME 0 + CACHE 0 (counter: 0) 1 LOAD_NAME 0 (print) PUSH_NULL @@ -2557,7 +2629,7 @@ def test_show_cache(self): CACHE 0 (func_version: 0) CACHE 0 POP_TOP - LOAD_CONST 0 (None) + LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE ''' for flag in ['-C', '--show-caches']: @@ -2569,8 +2641,8 @@ def test_show_offsets(self): expect = ''' 0 0 RESUME 0 - 1 2 LOAD_CONST 0 (None) - 4 RETURN_VALUE + 1 4 LOAD_COMMON_CONSTANT 7 (None) + 6 RETURN_VALUE ''' for flag in ['-O', '--show-offsets']: self.check_output(source, expect, flag) @@ -2581,7 +2653,7 @@ def test_show_positions(self): expect = ''' 0:0-1:0 RESUME 0 - 1:0-1:4 LOAD_CONST 0 (None) + 1:0-1:4 LOAD_COMMON_CONSTANT 7 (None) 1:0-1:4 RETURN_VALUE ''' for flag in ['-P', '--show-positions']: @@ -2593,7 +2665,7 @@ def test_specialized_code(self): expect = ''' 0 RESUME 0 - 1 LOAD_CONST 0 (None) + 1 LOAD_COMMON_CONSTANT 7 (None) RETURN_VALUE ''' for flag in ['-S', '--specialized']: diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py index 0fa74407e3c436..b125693ab0891c 100644 --- a/Lib/test/test_doctest/test_doctest.py +++ b/Lib/test/test_doctest/test_doctest.py @@ -742,7 +742,7 @@ def non_Python_modules(): r""" >>> import builtins >>> tests = doctest.DocTestFinder().find(builtins) - >>> 750 < len(tests) < 800 # approximate number of objects with docstrings + >>> 750 < len(tests) < 850 # approximate number of objects with docstrings True >>> real_tests = [t for t in tests if len(t.examples) > 0] >>> len(real_tests) # objects that actually have doctests @@ -833,6 +833,118 @@ def test_empty_namespace_package(self): self.assertEqual(len(include_empty_finder.find(mod)), 1) self.assertEqual(len(exclude_empty_finder.find(mod)), 0) + def test_lineno_of_test_dict_strings(self): + """Test line numbers are found for __test__ dict strings.""" + module_content = '''\ +"""Module docstring.""" + +def dummy_function(): + """Dummy function docstring.""" + pass + +__test__ = { + 'test_string': """ + This is a test string. + >>> 1 + 1 + 2 + """, +} +''' + with tempfile.TemporaryDirectory() as tmpdir: + module_path = os.path.join(tmpdir, 'test_module_lineno.py') + with open(module_path, 'w') as f: + f.write(module_content) + + sys.path.insert(0, tmpdir) + try: + import test_module_lineno + finder = doctest.DocTestFinder() + tests = finder.find(test_module_lineno) + + test_dict_test = None + for test in tests: + if '__test__' in test.name: + test_dict_test = test + break + + self.assertIsNotNone( + test_dict_test, + "__test__ dict test not found" + ) + # gh-69113: line number should not be None for __test__ strings + self.assertIsNotNone( + test_dict_test.lineno, + "Line number should not be None for __test__ dict strings" + ) + self.assertGreater( + test_dict_test.lineno, + 0, + "Line number should be positive" + ) + finally: + if 'test_module_lineno' in sys.modules: + del sys.modules['test_module_lineno'] + sys.path.pop(0) + + def test_lineno_multiline_matching(self): + """Test multi-line matching when no unique line exists.""" + # gh-69113: test that line numbers are found even when lines + # appear multiple times (e.g., ">>> x = 1" in both test entries) + module_content = '''\ +"""Module docstring.""" + +__test__ = { + 'test_one': """ + >>> x = 1 + >>> x + 1 + """, + 'test_two': """ + >>> x = 1 + >>> x + 2 + """, +} +''' + with tempfile.TemporaryDirectory() as tmpdir: + module_path = os.path.join(tmpdir, 'test_module_multiline.py') + with open(module_path, 'w') as f: + f.write(module_content) + + sys.path.insert(0, tmpdir) + try: + import test_module_multiline + finder = doctest.DocTestFinder() + tests = finder.find(test_module_multiline) + + test_one = None + test_two = None + for test in tests: + if 'test_one' in test.name: + test_one = test + elif 'test_two' in test.name: + test_two = test + + self.assertIsNotNone(test_one, "test_one not found") + self.assertIsNotNone(test_two, "test_two not found") + self.assertIsNotNone( + test_one.lineno, + "Line number should not be None for test_one" + ) + self.assertIsNotNone( + test_two.lineno, + "Line number should not be None for test_two" + ) + self.assertNotEqual( + test_one.lineno, + test_two.lineno, + "test_one and test_two should have different line numbers" + ) + finally: + if 'test_module_multiline' in sys.modules: + del sys.modules['test_module_multiline'] + sys.path.pop(0) + def test_DocTestParser(): r""" Unit tests for the `DocTestParser` class. @@ -2434,7 +2546,8 @@ def test_DocTestSuite_errors(): >>> print(result.failures[1][1]) # doctest: +ELLIPSIS Traceback (most recent call last): - File "...sample_doctest_errors.py", line None, in test.test_doctest.sample_doctest_errors.__test__.bad + File "...sample_doctest_errors.py", line 37, in test.test_doctest.sample_doctest_errors.__test__.bad + >...>> 2 + 2 AssertionError: Failed example: 2 + 2 Expected: @@ -2464,7 +2577,8 @@ def test_DocTestSuite_errors(): >>> print(result.errors[1][1]) # doctest: +ELLIPSIS Traceback (most recent call last): - File "...sample_doctest_errors.py", line None, in test.test_doctest.sample_doctest_errors.__test__.bad + File "...sample_doctest_errors.py", line 39, in test.test_doctest.sample_doctest_errors.__test__.bad + >...>> 1/0 File "", line 1, in 1/0 ~^~ @@ -3256,7 +3370,7 @@ def test_testmod_errors(): r""" ~^~ ZeroDivisionError: division by zero ********************************************************************** - File "...sample_doctest_errors.py", line ?, in test.test_doctest.sample_doctest_errors.__test__.bad + File "...sample_doctest_errors.py", line 37, in test.test_doctest.sample_doctest_errors.__test__.bad Failed example: 2 + 2 Expected: @@ -3264,7 +3378,7 @@ def test_testmod_errors(): r""" Got: 4 ********************************************************************** - File "...sample_doctest_errors.py", line ?, in test.test_doctest.sample_doctest_errors.__test__.bad + File "...sample_doctest_errors.py", line 39, in test.test_doctest.sample_doctest_errors.__test__.bad Failed example: 1/0 Exception raised: diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index e1adf8e9748506..6286b6d21b572e 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -8,7 +8,7 @@ import unittest from test import support -from test.support import findfile +from test.support import findfile, MS_WINDOWS if not support.has_subprocess_support: @@ -33,11 +33,17 @@ def normalize_trace_output(output): result = [ row.split("\t") for row in output.splitlines() - if row and not row.startswith('#') + if row and not row.startswith('#') and not row.startswith('@') ] result.sort(key=lambda row: int(row[0])) result = [row[1] for row in result] - return "\n".join(result) + # Normalize paths to basenames (bpftrace outputs full paths) + normalized = [] + for line in result: + # Replace full paths with just the filename + line = re.sub(r'/[^:]+/([^/:]+\.py)', r'\1', line) + normalized.append(line) + return "\n".join(normalized) except (IndexError, ValueError): raise AssertionError( "tracer produced unparsable output:\n{}".format(output) @@ -96,6 +102,8 @@ def assert_usable(self): class DTraceBackend(TraceBackend): EXTENSION = ".d" COMMAND = ["dtrace", "-q", "-s"] + if sys.platform == "sunos5": + COMMAND.insert(2, "-Z") class SystemTapBackend(TraceBackend): @@ -103,6 +111,178 @@ class SystemTapBackend(TraceBackend): COMMAND = ["stap", "-g"] +class BPFTraceBackend(TraceBackend): + EXTENSION = ".bt" + COMMAND = ["bpftrace"] + + # Inline bpftrace programs for each test case + PROGRAMS = { + "call_stack": """ + usdt:{python}:python:function__entry {{ + printf("%lld\\tfunction__entry:%s:%s:%d\\n", + nsecs, str(arg0), str(arg1), arg2); + }} + usdt:{python}:python:function__return {{ + printf("%lld\\tfunction__return:%s:%s:%d\\n", + nsecs, str(arg0), str(arg1), arg2); + }} + """, + "gc": """ + usdt:{python}:python:function__entry {{ + if (str(arg1) == "start") {{ @tracing = 1; }} + }} + usdt:{python}:python:function__return {{ + if (str(arg1) == "start") {{ @tracing = 0; }} + }} + usdt:{python}:python:gc__start {{ + if (@tracing) {{ + printf("%lld\\tgc__start:%d\\n", nsecs, arg0); + }} + }} + usdt:{python}:python:gc__done {{ + if (@tracing) {{ + printf("%lld\\tgc__done:%lld\\n", nsecs, arg0); + }} + }} + END {{ clear(@tracing); }} + """, + } + + # Which test scripts to filter by filename (None = use @tracing flag) + FILTER_BY_FILENAME = {"call_stack": "call_stack.py"} + + @staticmethod + def _filter_probe_rows(output): + return "\n".join( + line for line in output.splitlines() + if line.partition("\t")[0].isdigit() + ) + + # Expected outputs for each test case + # Note: bpftrace captures entry/return and may have slight timing + # differences compared to SystemTap due to probe firing order + EXPECTED = { + "call_stack": """function__entry:call_stack.py::0 +function__entry:call_stack.py:start:23 +function__entry:call_stack.py:function_1:1 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 +function__return:call_stack.py:function_1:2 +function__entry:call_stack.py:function_2:5 +function__entry:call_stack.py:function_1:1 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 +function__return:call_stack.py:function_1:2 +function__return:call_stack.py:function_2:6 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 +function__entry:call_stack.py:function_4:13 +function__return:call_stack.py:function_4:14 +function__entry:call_stack.py:function_5:18 +function__return:call_stack.py:function_5:21 +function__return:call_stack.py:start:28 +function__return:call_stack.py::30""", + "gc": """gc__start:0 +gc__done:0 +gc__start:1 +gc__done:0 +gc__start:2 +gc__done:0 +gc__start:2 +gc__done:1""", + } + + def run_case(self, name, optimize_python=None): + if name not in self.PROGRAMS: + raise unittest.SkipTest(f"No bpftrace program for {name}") + + python_file = abspath(name + ".py") + python_flags = [] + if optimize_python: + python_flags.extend(["-O"] * optimize_python) + + subcommand = [sys.executable] + python_flags + [python_file] + program = self.PROGRAMS[name].format(python=sys.executable) + + try: + proc = subprocess.Popen( + ["bpftrace", "-e", program, "-c", " ".join(subcommand)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + stdout, stderr = proc.communicate(timeout=60) + except subprocess.TimeoutExpired: + proc.kill() + raise AssertionError("bpftrace timed out") + except (FileNotFoundError, PermissionError) as e: + raise unittest.SkipTest(f"bpftrace not available: {e}") + + if proc.returncode != 0: + raise AssertionError( + f"bpftrace failed with code {proc.returncode}:\n{stderr}" + ) + + stdout = self._filter_probe_rows(stdout) + + # Filter output by filename if specified (bpftrace captures everything) + if name in self.FILTER_BY_FILENAME: + filter_filename = self.FILTER_BY_FILENAME[name] + filtered_lines = [ + line for line in stdout.splitlines() + if filter_filename in line + ] + stdout = "\n".join(filtered_lines) + + actual_output = normalize_trace_output(stdout) + expected_output = self.EXPECTED[name].strip() + + return (expected_output, actual_output) + + def assert_usable(self): + # Check if bpftrace is available and can attach to USDT probes + program = f'usdt:{sys.executable}:python:function__entry {{ printf("probe: success\\n"); exit(); }}' + try: + proc = subprocess.Popen( + ["bpftrace", "-e", program, "-c", f"{sys.executable} -c pass"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + stdout, stderr = proc.communicate(timeout=10) + except subprocess.TimeoutExpired: + proc.kill() + proc.communicate() # Clean up + raise unittest.SkipTest("bpftrace timed out during usability check") + except OSError as e: + raise unittest.SkipTest(f"bpftrace not available: {e}") + + # Check for permission errors (bpftrace usually requires root) + if proc.returncode != 0: + raise unittest.SkipTest( + f"bpftrace(1) failed with code {proc.returncode}: {stderr}" + ) + + if "probe: success" not in stdout: + raise unittest.SkipTest( + f"bpftrace(1) failed: stdout={stdout!r} stderr={stderr!r}" + ) + + +class BPFTraceOutputTests(unittest.TestCase): + def test_filter_probe_rows_ignores_warnings(self): + output = """stdin:1-19: WARNING: found external warnings +HINT: include/vmlinux.h:1439:3: warning: declaration does not declare anything +4623214882928\tgc__start:0 +4623214885575\tgc__done:0 +""" + self.assertEqual( + BPFTraceBackend._filter_probe_rows(output), + "4623214882928\tgc__start:0\n4623214885575\tgc__done:0", + ) + + +@unittest.skipIf(MS_WINDOWS, "Tests not compliant with trace on Windows.") class TraceTests: # unittest.TestCase options maxDiff = None @@ -126,7 +306,8 @@ def test_function_entry_return(self): def test_verify_call_opcodes(self): """Ensure our call stack test hits all function call opcodes""" - opcodes = set(["CALL_FUNCTION", "CALL_FUNCTION_EX", "CALL_FUNCTION_KW"]) + # Modern Python uses CALL, CALL_KW, and CALL_FUNCTION_EX + opcodes = set(["CALL", "CALL_FUNCTION_EX", "CALL_KW"]) with open(abspath("call_stack.py")) as f: code_string = f.read() @@ -151,9 +332,6 @@ def get_function_instructions(funcname): def test_gc(self): self.run_case("gc") - def test_line(self): - self.run_case("line") - class DTraceNormalTests(TraceTests, unittest.TestCase): backend = DTraceBackend() @@ -174,6 +352,17 @@ class SystemTapOptimizedTests(TraceTests, unittest.TestCase): backend = SystemTapBackend() optimize_python = 2 + +class BPFTraceNormalTests(TraceTests, unittest.TestCase): + backend = BPFTraceBackend() + optimize_python = 0 + + +class BPFTraceOptimizedTests(TraceTests, unittest.TestCase): + backend = BPFTraceBackend() + optimize_python = 2 + + class CheckDtraceProbes(unittest.TestCase): @classmethod def setUpClass(cls): @@ -189,11 +378,14 @@ def setUpClass(cls): def get_readelf_version(): try: cmd = ["readelf", "--version"] + # Force the C locale to disable localization. + env = dict(os.environ, LC_ALL="C") proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, + env=env, ) with proc: version, stderr = proc.communicate() @@ -216,12 +408,36 @@ def get_readelf_version(): return int(match.group(1)), int(match.group(2)) def get_readelf_output(self): - command = ["readelf", "-n", sys.executable] + binary = sys.executable + if sysconfig.get_config_var("Py_ENABLE_SHARED"): + lib_dir = sysconfig.get_config_var("LIBDIR") + if not lib_dir or sysconfig.is_python_build(): + lib_dir = os.path.abspath(os.path.dirname(sys.executable)) + + lib_names = [] + for name in ( + sysconfig.get_config_var("INSTSONAME"), + sysconfig.get_config_var("LDLIBRARY"), + ): + if name and name not in lib_names: + lib_names.append(name) + + if lib_dir: + for name in lib_names: + libpython_path = os.path.join(lib_dir, name) + if os.path.exists(libpython_path): + binary = libpython_path + break + + command = ["readelf", "-n", binary] + # Force the C locale to disable localization. + env = dict(os.environ, LC_ALL="C") stdout, _ = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, + env=env, ).communicate() return stdout @@ -234,6 +450,8 @@ def test_check_probes(self): "Name: audit", "Name: gc__start", "Name: gc__done", + "Name: function__entry", + "Name: function__return", ] for probe_name in available_probe_names: @@ -246,8 +464,6 @@ def test_missing_probes(self): # Missing probes will be added in the future. missing_probe_names = [ - "Name: function__entry", - "Name: function__return", "Name: line", ] diff --git a/Lib/test/test_email/data/msg_35.txt b/Lib/test/test_email/data/msg_35.txt index be7d5a2f7b9d38..0e2bbcaf71816a 100644 --- a/Lib/test/test_email/data/msg_35.txt +++ b/Lib/test/test_email/data/msg_35.txt @@ -1,4 +1,4 @@ From: aperson@dom.ain To: bperson@dom.ain Subject: here's something interesting -counter to RFC 2822, there's no separating newline here +counter to RFC 5322, there's no separating newline here diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index 179e236ecdfd7f..9d9fe418ee4d06 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -1060,6 +1060,78 @@ def get_phrase_cfws_only_raises(self): with self.assertRaises(errors.HeaderParseError): parser.get_phrase(' (foo) ') + def test_get_phrase_adjacent_ew(self): + # "'linear-white-space' that separates a pair of adjacent + # 'encoded-word's is ignored" (rfc2047 section 6.2) + self._test_get_x(parser.get_phrase, '=?ascii?q?Joi?= \t =?ascii?q?ned?=', 'Joined', 'Joined', [], '') + + def test_get_phrase_adjacent_ew_different_encodings(self): + self._test_get_x( + parser.get_phrase, + '=?utf-8?q?B=C3=A9r?= =?iso-8859-1?q?=E9nice?=', 'Bérénice', 'Bérénice', [], '' + ) + + def test_get_phrase_adjacent_ew_encoded_spaces(self): + self._test_get_x( + parser.get_phrase, + '=?ascii?q?Encoded?= =?ascii?q?_spaces_?= =?ascii?q?preserved?=', + 'Encoded spaces preserved', + 'Encoded spaces preserved', + [], + '' + ) + + def test_get_phrase_adjacent_ew_comment_is_not_linear_white_space(self): + self._test_get_x( + parser.get_phrase, + '=?ascii?q?Comment?= (is not) =?ascii?q?linear-white-space?=', + 'Comment (is not) linear-white-space', + 'Comment linear-white-space', + [], + '', + comments=['is not'], + ) + + def test_get_phrase_adjacent_ew_no_error_on_defects(self): + self._test_get_x( + parser.get_phrase, + '=?ascii?q?Def?= =?ascii?q?ect still joins?=', + 'Defect still joins', + 'Defect still joins', + [errors.InvalidHeaderDefect], # whitespace inside encoded word + '' + ) + + def test_get_phrase_adjacent_ew_ignore_non_ew(self): + self._test_get_x( + parser.get_phrase, + '=?ascii?q?No?= =?join?= for non-ew', + 'No =?join?= for non-ew', + 'No =?join?= for non-ew', + [], + '' + ) + + def test_get_phrase_adjacent_ew_ignore_invalid_ew(self): + self._test_get_x( + parser.get_phrase, + '=?ascii?q?No?= =?ascii?rot13?wbva= for invalid ew', + 'No =?ascii?rot13?wbva= for invalid ew', + 'No =?ascii?rot13?wbva= for invalid ew', + [], + '' + ) + + def test_get_phrase_adjacent_ew_missing_space(self): + self._test_get_x( + parser.get_phrase, + '=?ascii?q?Joi?==?ascii?q?ned?=', + 'Joined', + 'Joined', + [errors.InvalidHeaderDefect], # missing trailing whitespace + '' + ) + # get_local_part def test_get_local_part_simple(self): @@ -1235,17 +1307,6 @@ def test_get_local_part_valid_and_invalid_qp_in_atom_list(self): '@example.com') self.assertEqual(local_part.local_part, r'\example\\ example') - def test_get_local_part_unicode_defect(self): - # Currently this only happens when parsing unicode, not when parsing - # stuff that was originally binary. - local_part = self._test_get_x(parser.get_local_part, - 'exámple@example.com', - 'exámple', - 'exámple', - [errors.NonASCIILocalPartDefect], - '@example.com') - self.assertEqual(local_part.local_part, 'exámple') - # get_dtext def test_get_dtext_only(self): @@ -2398,6 +2459,22 @@ def test_get_address_rfc2047_display_name(self): self.assertEqual(address[0].token_type, 'mailbox') + def test_get_address_rfc2047_display_name_adjacent_ews(self): + address = self._test_get_x(parser.get_address, + '=?utf-8?q?B=C3=A9r?= =?utf-8?q?=C3=A9nice?= ', + 'Bérénice ', + 'Bérénice ', + [], + '') + self.assertEqual(address.token_type, 'address') + self.assertEqual(len(address.mailboxes), 1) + self.assertEqual(address.mailboxes, + address.all_mailboxes) + self.assertEqual(address.mailboxes[0].display_name, + 'Bérénice') + self.assertEqual(address[0].token_type, + 'mailbox') + def test_get_address_empty_group(self): address = self._test_get_x(parser.get_address, 'Monty Python:;', @@ -2617,7 +2694,7 @@ def test_get_address_list_mailboxes_invalid_addresses(self): '') self.assertEqual(address_list.token_type, 'address-list') self.assertEqual(len(address_list.mailboxes), 1) - self.assertEqual(len(address_list.all_mailboxes), 3) + self.assertEqual(len(address_list.all_mailboxes), 4) self.assertEqual([str(x) for x in address_list.all_mailboxes], [str(x) for x in address_list.addresses]) self.assertEqual(address_list.mailboxes[0].domain, 'example.com') @@ -2626,11 +2703,13 @@ def test_get_address_list_mailboxes_invalid_addresses(self): self.assertEqual(address_list.addresses[1].token_type, 'address') self.assertEqual(len(address_list.addresses[0].mailboxes), 1) self.assertEqual(len(address_list.addresses[1].mailboxes), 0) - self.assertEqual(len(address_list.addresses[1].mailboxes), 0) + self.assertEqual(len(address_list.addresses[2].mailboxes), 0) + self.assertEqual(len(address_list.addresses[3].mailboxes), 0) self.assertEqual( address_list.addresses[1].all_mailboxes[0].local_part, 'Foo x') + self.assertEqual(address_list.addresses[2].all_mailboxes[0].value, '[]') self.assertEqual( - address_list.addresses[2].all_mailboxes[0].display_name, + address_list.addresses[3].all_mailboxes[0].display_name, "Nobody Is. Special") def test_get_address_list_group_empty(self): @@ -2695,6 +2774,14 @@ def test_get_address_list_group_and_mailboxes(self): self.assertEqual(str(address_list.addresses[1]), str(address_list.mailboxes[2])) + def test_get_address_list_trailing_garbage(self): + address_list = self._test_get_x(parser.get_address_list, + 'unlisted-recipients:; (no To-header on input)', + 'unlisted-recipients:; (no To-header on input)', + 'unlisted-recipients:; ', + [errors.InvalidHeaderDefect]*2 + [errors.ObsoleteHeaderDefect], + '') + def test_invalid_content_disposition(self): content_disp = self._test_parse_x( parser.parse_content_disposition_header, @@ -2867,6 +2954,81 @@ def test_get_msg_id_ws_only_local(self): ) self.assertEqual(msg_id.token_type, 'msg-id') + def test_parse_message_ids_valid(self): + message_ids = self._test_parse_x( + parser.parse_message_ids, + " ", + " ", + " ", + [], + ) + self.assertEqual(message_ids.token_type, 'message-id-list') + + def test_parse_message_ids_empty(self): + message_ids = self._test_parse_x( + parser.parse_message_ids, + " ", + " ", + " ", + [errors.InvalidHeaderDefect], + ) + self.assertEqual(message_ids.token_type, 'message-id-list') + + def test_parse_message_ids_comment(self): + message_ids = self._test_parse_x( + parser.parse_message_ids, + " (foo's message from \"bar\")", + " (foo's message from \"bar\")", + " ", + [], + ) + self.assertEqual(message_ids.message_ids[0].value, ' ') + self.assertEqual(message_ids.token_type, 'message-id-list') + + def test_parse_message_ids_no_sep(self): + message_ids = self._test_parse_x( + parser.parse_message_ids, + "", + "", + "", + [], + ) + self.assertEqual(message_ids.message_ids[0].value, '') + self.assertEqual(message_ids.message_ids[1].value, '') + self.assertEqual(message_ids.token_type, 'message-id-list') + + def test_parse_message_ids_comma_sep(self): + message_ids = self._test_parse_x( + parser.parse_message_ids, + ",", + " ", + " ", + [errors.InvalidHeaderDefect], + ) + self.assertEqual(message_ids.message_ids[0].value, '') + self.assertEqual(message_ids.message_ids[1].value, '') + self.assertEqual(message_ids.token_type, 'message-id-list') + + def test_parse_message_ids_invalid_id(self): + message_ids = self._test_parse_x( + parser.parse_message_ids, + "", + "", + "", + [errors.InvalidHeaderDefect]*2, + ) + self.assertEqual(message_ids.token_type, 'message-id-list') + + def test_parse_message_ids_broken_ang(self): + message_ids = self._test_parse_x( + parser.parse_message_ids, + " >bar@foo", + " >bar@foo", + " >bar@foo", + [errors.InvalidHeaderDefect]*1, + ) + self.assertEqual(message_ids.token_type, 'message-id-list') + @parameterize @@ -3219,6 +3381,29 @@ def test_address_list_with_specials_in_long_quoted_string(self): with self.subTest(to=to): self._test(parser.get_address_list(to)[0], folded, policy=policy) + def test_address_list_with_long_unwrapable_comment(self): + policy = self.policy.clone(max_line_length=40) + cases = [ + # (to, folded) + ('(loremipsumdolorsitametconsecteturadipi)', + '(loremipsumdolorsitametconsecteturadipi)\n'), + ('(loremipsumdolorsitametconsecteturadipi)', + '(loremipsumdolorsitametconsecteturadipi)\n'), + ('(loremipsum dolorsitametconsecteturadipi)', + '(loremipsum dolorsitametconsecteturadipi)\n'), + ('(loremipsum dolorsitametconsecteturadipi)', + '(loremipsum\n dolorsitametconsecteturadipi)\n'), + ('(Escaped \\( \\) chars \\\\ in comments stay escaped)', + '(Escaped \\( \\) chars \\\\ in comments stay\n escaped)\n'), + ('((loremipsum)(loremipsum)(loremipsum)(loremipsum))', + '((loremipsum)(loremipsum)(loremipsum)(loremipsum))\n'), + ('((loremipsum)(loremipsum)(loremipsum) (loremipsum))', + '((loremipsum)(loremipsum)(loremipsum)\n (loremipsum))\n'), + ] + for (to, folded) in cases: + with self.subTest(to=to): + self._test(parser.get_address_list(to)[0], folded, policy=policy) + # XXX Need tests with comments on various sides of a unicode token, # and with unicode tokens in the comments. Spaces inside the quotes # currently don't do the right thing. @@ -3255,5 +3440,25 @@ def test_long_filename_attachment(self): " filename*1*=_TEST_TES.txt\n", ) + def test_fold_unfoldable_element_stealing_whitespace(self): + # gh-142006: When an element is too long to fit on the current line + # the previous line's trailing whitespace should not trigger a double newline. + policy = self.policy.clone(max_line_length=10) + # The non-whitespace text needs to exactly fill the max_line_length (10). + text = ("a" * 9) + ", " + ("b" * 20) + expected = ("a" * 9) + ",\n " + ("b" * 20) + "\n" + token = parser.get_address_list(text)[0] + self._test(token, expected, policy=policy) + + def test_encoded_word_with_undecodable_bytes(self): + self._test( + parser.get_address_list( + ' =?utf-8?Q?=E5=AE=A2=E6=88=B6=E6=AD=A3=E8=A6=8F=E4=BA=A4=E7?=' + ' ' + )[0], + ' =?unknown-8bit?b?5a6i5oi25q2j6KaP5Lqk5w==?= \n', + ) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_email/test_asian_codecs.py b/Lib/test/test_email/test_asian_codecs.py index ca44f54c69b39b..59013f087199e3 100644 --- a/Lib/test/test_email/test_asian_codecs.py +++ b/Lib/test/test_email/test_asian_codecs.py @@ -58,6 +58,62 @@ def test_japanese_codecs(self): # TK: full decode comparison eq(str(h).encode(jcode), subject_bytes) + h = Header("Japanese") + s = '\u65e5\u672c\u8a9e' # 日本語 + h.append(s, Charset('euc-jp')) + h.append(s, Charset('iso-2022-jp')) + h.append(s, Charset('shift_jis')) + eq(h.encode(), """\ +Japanese =?iso-2022-jp?b?GyRCRnxLXDhsGyhC?= =?iso-2022-jp?b?GyRCRnxLXDhsGyhC?= + =?iso-2022-jp?b?GyRCRnxLXDhsGyhC?=""") + eq(decode_header(h.encode()), + [(b'Japanese ', None), + (b'\x1b$BF|K\\8l\x1b(B\x1b$BF|K\\8l\x1b(B\x1b$BF|K\\8l\x1b(B', 'iso-2022-jp'), + ]) + + def test_chinese_codecs(self): + eq = self.ndiffAssertEqual + h = Header("Chinese") + s = '\u4e2d\u6587' # 中文 + h.append(s, Charset('gb2312')) + h.append(s, Charset('gbk')) + h.append(s, Charset('gb18030')) + h.append(s, Charset('hz')) + h.append(s, Charset('big5')) + h.append(s, Charset('big5hkscs')) + eq(h.encode(), """\ +Chinese =?gb2312?b?1tDOxA==?= =?gbk?b?1tDOxA==?= =?gb18030?b?1tDOxA==?= + =?hz-gb-2312?b?fntWUE5Efn0=?= =?big5?b?pKSk5Q==?= =?big5-hkscs?b?pKSk5Q==?=""") + eq(decode_header(h.encode()), + [(b'Chinese ', None), + (b'\xd6\xd0\xce\xc4', 'gb2312'), + (b'\xd6\xd0\xce\xc4', 'gbk'), + (b'\xd6\xd0\xce\xc4', 'gb18030'), + (b'~{VPND~}', 'hz-gb-2312'), + (b'\xa4\xa4\xa4\xe5', 'big5'), + (b'\xa4\xa4\xa4\xe5', 'big5-hkscs'), + ]) + + def test_korean_codecs(self): + eq = self.ndiffAssertEqual + h = Header("Korean") + s = '\ud55c\uad6d\uc5b4' # 한국어 + h.append(s, Charset('euc-kr')) + h.append(s, Charset('ks_c_5601-1987')) + h.append(s, Charset('cp949')) + h.append(s, Charset('iso-2022-kr')) + h.append(s, Charset('johab')) + eq(h.encode(), """\ +Korean =?euc-kr?b?x9Gxub7u?= =?ks_c_5601-1987?b?x9Gxub7uIMfRsbm+7g==?= + =?iso-2022-kr?b?GyQpQw5HUTE5Pm4P?= =?johab?b?0GWKgrTh?=""") + eq(decode_header(h.encode()), + [(b'Korean ', None), + (b'\xc7\xd1\xb1\xb9\xbe\xee', 'euc-kr'), + (b'\xc7\xd1\xb1\xb9\xbe\xee \xc7\xd1\xb1\xb9\xbe\xee', 'ks_c_5601-1987'), + (b'\x1b$)C\x0eGQ19>n\x0f', 'iso-2022-kr'), + (b'\xd0e\x8a\x82\xb4\xe1', 'johab'), + ]) + def test_payload_encoding_utf8(self): jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc' b'\xa5\xeb\xa5\xc9\xa1\xaa', 'euc-jp') diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index dceb54f15e48f4..0b1b6e89f8c992 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -342,6 +342,26 @@ def test_set_text_charset_latin_1(self): self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content) self.assertEqual(m.get_content(), content) + def test_set_text_charset_cp949(self): + m = self._make_message() + content = "\ud55c\uad6d\uc5b4\n\uac02\n" + raw_data_manager.set_content(m, content, charset='cp949') + self.assertEqual(str(m), textwrap.dedent("""\ + Content-Type: text/plain; charset="ks_c_5601-1987" + Content-Transfer-Encoding: base64 + + x9Gxub7uCoFBCg== + """)) + self.assertEqual(bytes(m), textwrap.dedent("""\ + Content-Type: text/plain; charset="ks_c_5601-1987" + Content-Transfer-Encoding: 8bit + + \ud55c\uad6d\uc5b4 + \uac02 + """).encode('ks_c_5601-1987')) + self.assertEqual(m.get_payload(decode=True), content.encode('ks_c_5601-1987')) + self.assertEqual(m.get_content(), content) + def test_set_text_plain_long_line_heuristics(self): m = self._make_message() content = ("Simple but long message that is over 78 characters" diff --git a/Lib/test/test_email/test_defect_handling.py b/Lib/test/test_email/test_defect_handling.py index 44e76c8ce5e03a..acc4accccac756 100644 --- a/Lib/test/test_email/test_defect_handling.py +++ b/Lib/test/test_email/test_defect_handling.py @@ -126,12 +126,10 @@ def test_multipart_invalid_cte(self): errors.InvalidMultipartContentTransferEncodingDefect) def test_multipart_no_cte_no_defect(self): - if self.raise_expected: return msg = self._str_msg(self.multipart_msg.format('')) self.assertEqual(len(self.get_defects(msg)), 0) def test_multipart_valid_cte_no_defect(self): - if self.raise_expected: return for cte in ('7bit', '8bit', 'BINary'): msg = self._str_msg( self.multipart_msg.format("\nContent-Transfer-Encoding: "+cte)) @@ -300,6 +298,47 @@ def test_missing_ending_boundary(self): self.assertDefectsEqual(self.get_defects(msg), [errors.CloseBoundaryNotFoundDefect]) + def test_line_beginning_colon(self): + string = ( + "Subject: Dummy subject\r\n: faulty header line\r\n\r\nbody\r\n" + ) + + with self._raise_point(errors.InvalidHeaderDefect): + msg = self._str_msg(string) + self.assertEqual(len(self.get_defects(msg)), 1) + self.assertDefectsEqual( + self.get_defects(msg), [errors.InvalidHeaderDefect] + ) + + if msg: + self.assertEqual(msg.items(), [("Subject", "Dummy subject")]) + self.assertEqual(msg.get_payload(), "body\r\n") + + def test_misplaced_envelope(self): + string = ( + "Subject: Dummy subject\r\nFrom wtf\r\nTo: abc\r\n\r\nbody\r\n" + ) + with self._raise_point(errors.MisplacedEnvelopeHeaderDefect): + msg = self._str_msg(string) + self.assertEqual(len(self.get_defects(msg)), 1) + self.assertDefectsEqual( + self.get_defects(msg), [errors.MisplacedEnvelopeHeaderDefect] + ) + + if msg: + headers = [("Subject", "Dummy subject"), ("To", "abc")] + self.assertEqual(msg.items(), headers) + self.assertEqual(msg.get_payload(), "body\r\n") + + + +class TestCompat32(TestDefectsBase, TestEmailBase): + + policy = policy.compat32 + + def get_defects(self, obj): + return obj.defects + class TestDefectDetection(TestDefectsBase, TestEmailBase): @@ -332,6 +371,9 @@ def _raise_point(self, defect): with self.assertRaises(defect): yield + def get_defects(self, obj): + return obj.defects + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index b8116d073a2670..19555d87085e17 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -41,6 +41,7 @@ from test import support from test.support import threading_helper +from test.support import warnings_helper from test.support.os_helper import unlink from test.test_email import openfile, TestEmailBase @@ -481,6 +482,27 @@ def test_get_param_with_quotes(self): "Content-Type: foo; bar*0=\"baz\\\"foobar\"; bar*1=\"\\\"baz\"") self.assertEqual(msg.get_param('bar'), 'baz"foobar"baz') + def test_get_param_linear_complexity(self): + # Ensure that email.message._parseparam() is fast. + # See https://github.com/python/cpython/issues/136063. + N = 100_000 + for s, r in [ + ("", ""), + ("foo=bar", "foo=bar"), + (" FOO = bar ", "foo=bar"), + ]: + with self.subTest(s=s, r=r, N=N): + src = f'{s};' * (N - 1) + s + res = email.message._parseparam(src) + self.assertEqual(len(res), N) + self.assertEqual(len(set(res)), 1) + self.assertEqual(res[0], r) + + # This will be considered as a single parameter. + malformed = 's="' + ';' * (N - 1) + res = email.message._parseparam(malformed) + self.assertEqual(res, [malformed]) + def test_field_containment(self): msg = email.message_from_string('Header: exists') self.assertIn('header', msg) @@ -2241,70 +2263,6 @@ def test_parse_missing_minor_type(self): eq(msg.get_content_maintype(), 'text') eq(msg.get_content_subtype(), 'plain') - # test_defect_handling - def test_same_boundary_inner_outer(self): - msg = self._msgobj('msg_15.txt') - # XXX We can probably eventually do better - inner = msg.get_payload(0) - self.assertHasAttr(inner, 'defects') - self.assertEqual(len(inner.defects), 1) - self.assertIsInstance(inner.defects[0], - errors.StartBoundaryNotFoundDefect) - - # test_defect_handling - def test_multipart_no_boundary(self): - msg = self._msgobj('msg_25.txt') - self.assertIsInstance(msg.get_payload(), str) - self.assertEqual(len(msg.defects), 2) - self.assertIsInstance(msg.defects[0], - errors.NoBoundaryInMultipartDefect) - self.assertIsInstance(msg.defects[1], - errors.MultipartInvariantViolationDefect) - - multipart_msg = textwrap.dedent("""\ - Date: Wed, 14 Nov 2007 12:56:23 GMT - From: foo@bar.invalid - To: foo@bar.invalid - Subject: Content-Transfer-Encoding: base64 and multipart - MIME-Version: 1.0 - Content-Type: multipart/mixed; - boundary="===============3344438784458119861=="{} - - --===============3344438784458119861== - Content-Type: text/plain - - Test message - - --===============3344438784458119861== - Content-Type: application/octet-stream - Content-Transfer-Encoding: base64 - - YWJj - - --===============3344438784458119861==-- - """) - - # test_defect_handling - def test_multipart_invalid_cte(self): - msg = self._str_msg( - self.multipart_msg.format("\nContent-Transfer-Encoding: base64")) - self.assertEqual(len(msg.defects), 1) - self.assertIsInstance(msg.defects[0], - errors.InvalidMultipartContentTransferEncodingDefect) - - # test_defect_handling - def test_multipart_no_cte_no_defect(self): - msg = self._str_msg(self.multipart_msg.format('')) - self.assertEqual(len(msg.defects), 0) - - # test_defect_handling - def test_multipart_valid_cte_no_defect(self): - for cte in ('7bit', '8bit', 'BINary'): - msg = self._str_msg( - self.multipart_msg.format( - "\nContent-Transfer-Encoding: {}".format(cte))) - self.assertEqual(len(msg.defects), 0) - # test_headerregistry.TestContentTypeHeader invalid_1 and invalid_2. def test_invalid_content_type(self): eq = self.assertEqual @@ -2352,7 +2310,7 @@ def test_no_separating_blank_line(self): To: bperson@dom.ain Subject: here's something interesting -counter to RFC 2822, there's no separating newline here +counter to RFC 5322, there's no separating newline here """) # test_defect_handling @@ -2381,30 +2339,6 @@ def test_missing_start_boundary(self): self.assertIsInstance(bad.defects[0], errors.StartBoundaryNotFoundDefect) - # test_defect_handling - def test_first_line_is_continuation_header(self): - eq = self.assertEqual - m = ' Line 1\nSubject: test\n\nbody' - msg = email.message_from_string(m) - eq(msg.keys(), ['Subject']) - eq(msg.get_payload(), 'body') - eq(len(msg.defects), 1) - self.assertDefectsEqual(msg.defects, - [errors.FirstHeaderLineIsContinuationDefect]) - eq(msg.defects[0].line, ' Line 1\n') - - # test_defect_handling - def test_missing_header_body_separator(self): - # Our heuristic if we see a line that doesn't look like a header (no - # leading whitespace but no ':') is to assume that the blank line that - # separates the header from the body is missing, and to stop parsing - # headers and start parsing the body. - msg = self._str_msg('Subject: test\nnot a header\nTo: abc\n\nb\n') - self.assertEqual(msg.keys(), ['Subject']) - self.assertEqual(msg.get_payload(), 'not a header\nTo: abc\n\nb\n') - self.assertDefectsEqual(msg.defects, - [errors.MissingHeaderBodySeparatorDefect]) - def test_string_payload_with_extra_space_after_cte(self): # https://github.com/python/cpython/issues/98188 cte = "base64 " @@ -2508,49 +2442,49 @@ def test_rfc2047_Q_invalid_digits(self): [(b'andr\xe9=zz', 'iso-8859-1')]) def test_rfc2047_rfc2047_1(self): - # 1st testcase at end of rfc2047 + # 1st testcase at end of RFC 2047 s = '(=?ISO-8859-1?Q?a?=)' self.assertEqual(decode_header(s), [(b'(', None), (b'a', 'iso-8859-1'), (b')', None)]) def test_rfc2047_rfc2047_2(self): - # 2nd testcase at end of rfc2047 + # 2nd testcase at end of RFC 2047 s = '(=?ISO-8859-1?Q?a?= b)' self.assertEqual(decode_header(s), [(b'(', None), (b'a', 'iso-8859-1'), (b' b)', None)]) def test_rfc2047_rfc2047_3(self): - # 3rd testcase at end of rfc2047 + # 3rd testcase at end of RFC 2047 s = '(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)' self.assertEqual(decode_header(s), [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) def test_rfc2047_rfc2047_4(self): - # 4th testcase at end of rfc2047 + # 4th testcase at end of RFC 2047 s = '(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)' self.assertEqual(decode_header(s), [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) def test_rfc2047_rfc2047_5a(self): - # 5th testcase at end of rfc2047 newline is \r\n + # 5th testcase at end of RFC 2047 newline is \r\n s = '(=?ISO-8859-1?Q?a?=\r\n =?ISO-8859-1?Q?b?=)' self.assertEqual(decode_header(s), [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) def test_rfc2047_rfc2047_5b(self): - # 5th testcase at end of rfc2047 newline is \n + # 5th testcase at end of RFC 2047 newline is \n s = '(=?ISO-8859-1?Q?a?=\n =?ISO-8859-1?Q?b?=)' self.assertEqual(decode_header(s), [(b'(', None), (b'ab', 'iso-8859-1'), (b')', None)]) def test_rfc2047_rfc2047_6(self): - # 6th testcase at end of rfc2047 + # 6th testcase at end of RFC 2047 s = '(=?ISO-8859-1?Q?a_b?=)' self.assertEqual(decode_header(s), [(b'(', None), (b'a b', 'iso-8859-1'), (b')', None)]) def test_rfc2047_rfc2047_7(self): - # 7th testcase at end of rfc2047 + # 7th testcase at end of RFC 2047 s = '(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)' self.assertEqual(decode_header(s), [(b'(', None), (b'a', 'iso-8859-1'), (b' b', 'iso-8859-2'), @@ -3252,8 +3186,8 @@ def test_parsedate_y2k(self): """Test for parsing a date with a two-digit year. Parsing a date with a two-digit year should return the correct - four-digit year. RFC822 allows two-digit years, but RFC2822 (which - obsoletes RFC822) requires four-digit years. + four-digit year. RFC 822 allows two-digit years, but RFC 5322 (which + obsoletes RFC 2822, which obsoletes RFC 822) requires four-digit years. """ self.assertEqual(utils.parsedate_tz('25 Feb 03 13:47:26 -0800'), @@ -3304,7 +3238,7 @@ def test_escape_backslashes(self): self.assertEqual(utils.parseaddr(utils.formataddr((a, b))), (a, b)) def test_quotes_unicode_names(self): - # issue 1690608. email.utils.formataddr() should be rfc2047 aware. + # issue 1690608. email.utils.formataddr() should be RFC 2047 aware. name = "H\u00e4ns W\u00fcrst" addr = 'person@dom.ain' utf8_base64 = "=?utf-8?b?SMOkbnMgV8O8cnN0?= " @@ -3314,7 +3248,7 @@ def test_quotes_unicode_names(self): latin1_quopri) def test_accepts_any_charset_like_object(self): - # issue 1690608. email.utils.formataddr() should be rfc2047 aware. + # issue 1690608. email.utils.formataddr() should be RFC 2047 aware. name = "H\u00e4ns W\u00fcrst" addr = 'person@dom.ain' utf8_base64 = "=?utf-8?b?SMOkbnMgV8O8cnN0?= " @@ -3329,7 +3263,7 @@ def header_encode(self, string): utf8_base64) def test_invalid_charset_like_object_raises_error(self): - # issue 1690608. email.utils.formataddr() should be rfc2047 aware. + # issue 1690608. email.utils.formataddr() should be RFC 2047 aware. name = "H\u00e4ns W\u00fcrst" addr = 'person@dom.ain' # An object without a header_encode method: @@ -3338,7 +3272,7 @@ def test_invalid_charset_like_object_raises_error(self): bad_charset) def test_unicode_address_raises_error(self): - # issue 1690608. email.utils.formataddr() should be rfc2047 aware. + # issue 1690608. email.utils.formataddr() should be RFC 2047 aware. addr = 'pers\u00f6n@dom.in' self.assertRaises(UnicodeError, utils.formataddr, (None, addr)) self.assertRaises(UnicodeError, utils.formataddr, ("Name", addr)) @@ -3359,7 +3293,7 @@ def test_parseaddr_preserves_quoted_pairs_in_addresses(self): # string containing a quoted backslash, followed by 'example' and two # backslashes, followed by another quoted string containing a space and # the word 'example'. parseaddr copies those two backslashes - # literally. Per rfc5322 this is not technically correct since a \ may + # literally. Per RFC 5322 this is not technically correct since a \ may # not appear in an address outside of a quoted string. It is probably # a sensible Postel interpretation, though. eq = self.assertEqual @@ -3371,12 +3305,12 @@ def test_parseaddr_preserves_quoted_pairs_in_addresses(self): ('', '"\\\\"example\\\\" example"@example.com')) def test_parseaddr_preserves_spaces_in_local_part(self): - # issue 9286. A normal RFC5322 local part should not contain any + # issue 9286. A normal RFC 5322 local part should not contain any # folding white space, but legacy local parts can (they are a sequence # of atoms, not dotatoms). On the other hand we strip whitespace from # before the @ and around dots, on the assumption that the whitespace # around the punctuation is a mistake in what would otherwise be - # an RFC5322 local part. Leading whitespace is, usual, stripped as well. + # an RFC 5322 local part. Leading whitespace is, usual, stripped as well. self.assertEqual(('', "merwok wok@xample.com"), utils.parseaddr("merwok wok@xample.com")) self.assertEqual(('', "merwok wok@xample.com"), @@ -4894,6 +4828,15 @@ def test_decode_soft_line_break(self): def test_decode_false_quoting(self): self._test_decode('A=1,B=A ==> A+B==2', 'A=1,B=A ==> A+B==2') + def test_decode_crlf_eol_no_trailing_newline(self): + self._test_decode('abc', 'abc', eol='\r\n') + + def test_decode_crlf_eol_multiline_no_trailing_newline(self): + self._test_decode('a\r\nb', 'a\r\nb', eol='\r\n') + + def test_decode_crlf_eol_with_trailing_newline(self): + self._test_decode('abc\r\n', 'abc\r\n', eol='\r\n') + def _test_encode(self, body, expected_encoded_body, maxlinelen=None, eol=None): kwargs = {} if maxlinelen is None: @@ -5027,6 +4970,128 @@ def tearDown(self): except KeyError: pass + def test_attributes(self): + from email import charset + c = Charset() + self.assertEqual(c.input_charset, 'us-ascii') + self.assertEqual(c.header_encoding, None) + self.assertEqual(c.body_encoding, None) + self.assertEqual(c.output_charset, 'us-ascii') + self.assertEqual(c.input_codec, None) + self.assertEqual(c.output_codec, None) + + c = Charset('us-ascii') + self.assertEqual(c.input_charset, 'us-ascii') + self.assertEqual(c.header_encoding, None) + self.assertEqual(c.body_encoding, None) + self.assertEqual(c.output_charset, 'us-ascii') + self.assertEqual(c.input_codec, None) + self.assertEqual(c.output_codec, None) + + c = Charset('utf8') + self.assertEqual(c.input_charset, 'utf-8') + self.assertEqual(c.header_encoding, charset.SHORTEST) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'utf-8') + self.assertEqual(c.input_codec, 'utf-8') + self.assertEqual(c.output_codec, 'utf-8') + + c = Charset('latin1') + self.assertEqual(c.input_charset, 'iso-8859-1') + self.assertEqual(c.header_encoding, charset.QP) + self.assertEqual(c.body_encoding, charset.QP) + self.assertEqual(c.output_charset, 'iso-8859-1') + self.assertEqual(c.input_codec, 'iso-8859-1') + self.assertEqual(c.output_codec, 'iso-8859-1') + + c = Charset('latin9') + self.assertEqual(c.input_charset, 'iso-8859-15') + self.assertEqual(c.header_encoding, charset.QP) + self.assertEqual(c.body_encoding, charset.QP) + self.assertEqual(c.output_charset, 'iso-8859-15') + self.assertEqual(c.input_codec, 'iso-8859-15') + self.assertEqual(c.output_codec, 'iso-8859-15') + + c = Charset('cyrillic') + self.assertEqual(c.input_charset, 'iso-8859-5') + self.assertEqual(c.header_encoding, charset.SHORTEST) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'iso-8859-5') + self.assertEqual(c.input_codec, 'iso-8859-5') + self.assertEqual(c.output_codec, 'iso-8859-5') + + c = Charset('cp1251') + self.assertEqual(c.input_charset, 'windows-1251') + self.assertEqual(c.header_encoding, charset.SHORTEST) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'windows-1251') + self.assertEqual(c.input_codec, 'windows-1251') + self.assertEqual(c.output_codec, 'windows-1251') + + c = Charset('cp1252') + self.assertEqual(c.input_charset, 'windows-1252') + self.assertEqual(c.header_encoding, charset.QP) + self.assertEqual(c.body_encoding, charset.QP) + self.assertEqual(c.output_charset, 'windows-1252') + self.assertEqual(c.input_codec, 'windows-1252') + self.assertEqual(c.output_codec, 'windows-1252') + + c = Charset('eucjp') + self.assertEqual(c.input_charset, 'euc-jp') + self.assertEqual(c.header_encoding, charset.BASE64) + self.assertEqual(c.body_encoding, None) + self.assertEqual(c.output_charset, 'iso-2022-jp') + self.assertEqual(c.input_codec, 'euc-jp') + self.assertEqual(c.output_codec, 'iso-2022-jp') + + c = Charset('cp949') + self.assertEqual(c.input_charset, 'ks_c_5601-1987') + self.assertEqual(c.header_encoding, charset.SHORTEST) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'ks_c_5601-1987') + self.assertEqual(c.input_codec, 'ks_c_5601-1987') + self.assertEqual(c.output_codec, 'ks_c_5601-1987') + + c = Charset('gb2312') + self.assertEqual(c.input_charset, 'gb2312') + self.assertEqual(c.header_encoding, charset.BASE64) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'gb2312') + self.assertEqual(c.input_codec, 'gb2312') + self.assertEqual(c.output_codec, 'gb2312') + + c = Charset('big5') + self.assertEqual(c.input_charset, 'big5') + self.assertEqual(c.header_encoding, charset.BASE64) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'big5') + self.assertEqual(c.input_codec, 'big5') + self.assertEqual(c.output_codec, 'big5') + + def test_user_charsets(self): + from email import charset + c = Charset('fake0') + self.assertEqual(c.input_charset, 'fake0') + self.assertEqual(c.header_encoding, charset.SHORTEST) + self.assertEqual(c.body_encoding, charset.BASE64) + self.assertEqual(c.output_charset, 'fake0') + self.assertEqual(c.input_codec, 'fake0') + self.assertEqual(c.output_codec, 'fake0') + + charset.add_alias('fake1', 'mime-fake') + charset.add_alias('output-mime-fake', 'output-mime-fake-alias') + charset.add_codec('mime-fake', 'fakecodec') + charset.add_codec('output-mime-fake-alias', 'outputfakecodec') + charset.add_charset('mime-fake', charset.QP, None, 'output-mime-fake') + + c = Charset('fake1') + self.assertEqual(c.input_charset, 'mime-fake') + self.assertEqual(c.header_encoding, charset.QP) + self.assertEqual(c.body_encoding, None) + self.assertEqual(c.output_charset, 'output-mime-fake-alias') + self.assertEqual(c.input_codec, 'fakecodec') + self.assertEqual(c.output_codec, 'outputfakecodec') + def test_codec_encodeable(self): eq = self.assertEqual # Make sure us-ascii = no Unicode conversion @@ -5052,15 +5117,8 @@ def test_body_encode(self): # Try the convert argument, where input codec != output codec c = Charset('euc-jp') # With apologies to Tokio Kikuchi ;) - # XXX FIXME -## try: -## eq('\x1b$B5FCO;~IW\x1b(B', -## c.body_encode('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7')) -## eq('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7', -## c.body_encode('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7', False)) -## except LookupError: -## # We probably don't have the Japanese codecs installed -## pass + eq('\x1b$B5FCO;~IW\x1b(B', + c.body_encode('\u83ca\u5730\u6642\u592b')) # Testing SF bug #625509, which we have to fake, since there are no # built-in encodings where the header encoding is QP but the body # encoding is not. @@ -5074,6 +5132,11 @@ def test_unicode_charset_name(self): self.assertEqual(str(charset), 'us-ascii') self.assertRaises(errors.CharsetError, Charset, 'asc\xffii') + def test_bytes_charset_name(self): + charset = Charset(b'us-ascii') + self.assertEqual(str(charset), 'us-ascii') + self.assertRaises(errors.CharsetError, Charset, b'asc\xffii') + # Test multilingual MIME headers. @@ -5717,7 +5780,8 @@ def test_rfc2231_bad_character_in_encoding(self): """ msg = email.message_from_string(m) - self.assertEqual(msg.get_filename(), 'myfile.txt') + with warnings_helper.check_warnings(('', DeprecationWarning)): + self.assertEqual(msg.get_filename(), 'myfile.txt') def test_rfc2231_single_tick_in_filename_extended(self): eq = self.assertEqual diff --git a/Lib/test/test_email/test_generator.py b/Lib/test/test_email/test_generator.py index c75a842c33578e..8d912738029f78 100644 --- a/Lib/test/test_email/test_generator.py +++ b/Lib/test/test_email/test_generator.py @@ -1,13 +1,21 @@ import io +import re import textwrap import unittest +import random +import sys from email import message_from_string, message_from_bytes from email.message import EmailMessage +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText from email.generator import Generator, BytesGenerator +import email.generator from email.headerregistry import Address from email import policy import email.errors from test.test_email import TestEmailBase, parameterize +import test.support + @parameterize @@ -288,6 +296,99 @@ def test_keep_long_encoded_newlines(self): g.flatten(msg) self.assertEqual(s.getvalue(), self.typ(expected)) + def test_non_ascii_addr_spec_raises(self): + # non-ascii is not permitted in any part of an addr-spec. If the + # programmer generated it, it's an error. (See also + # test_non_ascii_addr_spec_preserved below.) + p = self.policy.clone(utf8=False, max_line_length=20) + g = self.genclass(self.ioclass(), policy=p) + # XXX The particular part detected here isn't part of a behavioral + # spec and may change in the future. + cases = [ + ('wők@example.com', 'wők', 'local-part'), + ('wok@exàmple.com', 'exàmple.com', 'domain'), + ('wők@exàmple.com', 'wők', 'local-part'), + ( + '"Name, for display" ', + 'wők@example.com', + 'addr-spec', + ), + ( + 'Näyttönimi ', + 'wők@example.com', + 'addr-spec', + ), + ( + '"a lőng quoted string as the local part"@example.com', + 'a lőng quoted string as the local part', + 'local-part', + ), + + ] + for address, badtoken, partname in cases: + with self.subTest(address=address): + msg = EmailMessage() + msg['To'] = address + expected_error = ( + fr"(?i)(?=.*non-ascii)" + fr"(?=.*{re.escape(badtoken)})" + fr"(?=.*{partname})" + fr"(?=.*policy.*utf8)" + ) + with self.assertRaisesRegex( + email.errors.HeaderWriteError, expected_error + ): + g.flatten(msg) + + def test_local_part_quoted_string_wrapped_correctly(self): + msg = self.msgmaker(self.typ(textwrap.dedent("""\ + To: <"a long local part in a quoted string"@example.com> + Subject: test + + None + """)), policy=self.policy.clone(max_line_length=20)) + expected = textwrap.dedent("""\ + To: <"a long local part in a + quoted string"@example.com> + Subject: test + + None + """) + s = self.ioclass() + g = self.genclass(s, policy=self.policy.clone(max_line_length=30)) + g.flatten(msg) + self.assertEqual(s.getvalue(), self.typ(expected)) + + def _test_boundary_detection(self, linesep): + # Generate a boundary token in the same way as _make_boundary + token = random.randrange(sys.maxsize) + + def _patch_random_randrange(*args, **kwargs): + return token + + with test.support.swap_attr( + random, "randrange", _patch_random_randrange + ): + boundary = self.genclass._make_boundary(text=None) + boundary_in_part = ( + "this goes before the boundary\n--" + + boundary + + "\nthis goes after\n" + ) + msg = MIMEMultipart() + msg.attach(MIMEText(boundary_in_part)) + self.genclass(self.ioclass()).flatten(msg, linesep=linesep) + # Generator checks the message content for the string it is about + # to use as a boundary ('token' in this test) and when it finds it + # in our attachment appends .0 to make the boundary it uses unique. + self.assertEqual(msg.get_boundary(), boundary + ".0") + + def test_lf_boundary_detection(self): + self._test_boundary_detection("\n") + + def test_crlf_boundary_detection(self): + self._test_boundary_detection("\r\n") + class TestGenerator(TestGeneratorBase, TestEmailBase): @@ -313,7 +414,7 @@ def test_flatten_unicode_linesep(self): self.assertEqual(s.getvalue(), self.typ(expected)) def test_verify_generated_headers(self): - """gh-121650: by default the generator prevents header injection""" + # gh-121650: by default the generator prevents header injection class LiteralHeader(str): name = 'Header' def fold(self, **kwargs): @@ -334,6 +435,8 @@ def fold(self, **kwargs): with self.assertRaises(email.errors.HeaderWriteError): message.as_string() + with self.assertRaises(email.errors.HeaderWriteError): + message.as_bytes() class TestBytesGenerator(TestGeneratorBase, TestEmailBase): @@ -391,6 +494,50 @@ def test_defaults_handle_spaces_at_start_of_continuation_line(self): g.flatten(msg) self.assertEqual(s.getvalue(), expected) + # gh-144156: fold between non-encoded and encoded words don't need to encoded + # the separating space + def test_defaults_handle_spaces_at_start_of_continuation_line_2(self): + source = ("Re: [SOS-1495488] Commande et livraison - Demande de retour - " + "bibijolie - 251210-AABBCC - Abo actualités digitales 20 semaines " + "d’abonnement à 24 heures, Bilan, Tribune de Genève et tous les titres Tamedia") + expected = ( + b"Subject: " + b"Re: [SOS-1495488] Commande et livraison - Demande de retour -\n" + b" bibijolie - 251210-AABBCC - Abo =?utf-8?q?actualit=C3=A9s?= digitales 20\n" + b" semaines =?utf-8?q?d=E2=80=99abonnement_=C3=A0?= 24 heures, Bilan, Tribune de\n" + b" =?utf-8?q?Gen=C3=A8ve?= et tous les titres Tamedia\n\n" + ) + msg = EmailMessage() + msg['Subject'] = source + s = io.BytesIO() + g = BytesGenerator(s) + g.flatten(msg) + self.assertEqual(s.getvalue(), expected) + + def test_ew_folding_round_trip_1(self): + print() + source = "aaaaaaaaa фффффффф " + msg = EmailMessage() + msg['Subject'] = source + s = io.BytesIO() + g = BytesGenerator(s, maxheaderlen=30) + g.flatten(msg) + flat = s.getvalue() + reparsed = message_from_bytes(flat, policy=policy.default)['Subject'] + self.assertMultiLineEqual(reparsed, source) + + def test_ew_folding_round_trip_2(self): + print() + source = "aaa aaaaaaa aaa ффф фффф " + msg = EmailMessage() + msg['Subject'] = source + s = io.BytesIO() + g = BytesGenerator(s, maxheaderlen=30) + g.flatten(msg) + flat = s.getvalue() + reparsed = message_from_bytes(flat, policy=policy.default)['Subject'] + self.assertMultiLineEqual(reparsed, source) + def test_cte_type_7bit_handles_unknown_8bit(self): source = ("Subject: Maintenant je vous présente mon " "collègue\n\n").encode('utf-8') @@ -432,12 +579,12 @@ def test_cte_type_7bit_transforms_8bit_cte(self): def test_smtputf8_policy(self): msg = EmailMessage() - msg['From'] = "Páolo " + msg['From'] = "Páolo " msg['To'] = 'Dinsdale' msg['Subject'] = 'Nudge nudge, wink, wink \u1F609' msg.set_content("oh là là, know what I mean, know what I mean?") expected = textwrap.dedent("""\ - From: Páolo + From: Páolo To: Dinsdale Subject: Nudge nudge, wink, wink \u1F609 Content-Type: text/plain; charset="utf-8" @@ -472,6 +619,37 @@ def test_smtp_policy(self): g.flatten(msg) self.assertEqual(s.getvalue(), expected) + def test_non_ascii_addr_spec_preserved(self): + # A defective non-ASCII addr-spec parsed from the original + # message is left unchanged when flattening. + # (See also test_non_ascii_addr_spec_raises above.) + source = ( + 'To: jörg@example.com, "But a long name still works with refold_source" ' + ).encode() + expected = ( + b'To: j\xc3\xb6rg@example.com,\n' + b' "But a long name still works with refold_source" \n' + b'\n' + ) + msg = message_from_bytes(source, policy=policy.default) + s = io.BytesIO() + g = BytesGenerator(s, policy=policy.default) + g.flatten(msg) + self.assertEqual(s.getvalue(), expected) + + def test_idna_encoding_preserved(self): + # Nothing tries to decode a pre-encoded IDNA domain. + msg = EmailMessage() + msg["To"] = Address( + username='jörg', + domain='☕.example'.encode('idna').decode() # IDNA 2003 + ) + expected = 'To: jörg@xn--53h.example\n\n'.encode() + s = io.BytesIO() + g = BytesGenerator(s, policy=policy.default.clone(utf8=True)) + g.flatten(msg) + self.assertEqual(s.getvalue(), expected) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py index ff7a6da644d572..aa918255d15c37 100644 --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -8,6 +8,7 @@ from email import headerregistry from email.headerregistry import Address, Group from test.support import ALWAYS_EQ +from test.support import warnings_helper DITTO = object() @@ -247,7 +248,15 @@ def content_type_as_value(self, decoded = args[2] if l>2 and args[2] is not DITTO else source header = 'Content-Type:' + ' ' if source else '' folded = args[3] if l>3 else header + decoded + '\n' - h = self.make_header('Content-Type', source) + # Both rfc2231 test cases with utf-8%E2%80%9D raise warnings, + # clear encoding cache to ensure test isolation. + if 'utf-8%E2%80%9D' in source and 'ascii' not in source: + import encodings + encodings._cache.clear() + with warnings_helper.check_warnings(('', DeprecationWarning)): + h = self.make_header('Content-Type', source) + else: + h = self.make_header('Content-Type', source) self.assertEqual(h.content_type, content_type) self.assertEqual(h.maintype, maintype) self.assertEqual(h.subtype, subtype) @@ -1262,12 +1271,12 @@ class TestAddressHeader(TestHeaderBase): 'example.com', None), - } - # XXX: Need many more examples, and in particular some with names in # trailing comments, which aren't currently handled. comments in # general are not handled yet. + } + def example_as_address(self, source, defects, decoded, display_name, addr_spec, username, domain, comment): h = self.make_header('sender', source) @@ -1285,6 +1294,43 @@ def example_as_address(self, source, defects, decoded, display_name, # XXX: we have no comment support yet. #self.assertEqual(a.comment, comment) + example_broken_header_params = { + + 'just_dquote': + ('"', + [errors.InvalidHeaderDefect]*2, + '<>', + '', + '<>', + '', + '', + ), + + } + + def example_broken_header_as_address( + self, + source, + defects, + decoded, + display_name, + addr_spec, + username, + domain, + ): + h = self.make_header('sender', source) + self.assertEqual(h, decoded) + self.assertDefectsEqual(h.defects, defects) + a = h.address + self.assertEqual(str(a), decoded) + self.assertEqual(len(h.groups), 1) + self.assertEqual([a], list(h.groups[0].addresses)) + self.assertEqual([a], list(h.addresses)) + self.assertEqual(a.display_name, display_name) + self.assertEqual(a.addr_spec, addr_spec) + self.assertEqual(a.username, username) + self.assertEqual(a.domain, domain) + def example_as_group(self, source, defects, decoded, display_name, addr_spec, username, domain, comment): source = 'foo: {};'.format(source) @@ -1497,17 +1543,19 @@ def test_quoting(self): self.assertEqual(str(a), '"Sara J." <"bad name"@example.com>') def test_il8n(self): - a = Address('Éric', 'wok', 'exàmple.com') + a = Address('Éric', 'wők', 'exàmple.com') self.assertEqual(a.display_name, 'Éric') - self.assertEqual(a.username, 'wok') + self.assertEqual(a.username, 'wők') self.assertEqual(a.domain, 'exàmple.com') - self.assertEqual(a.addr_spec, 'wok@exàmple.com') - self.assertEqual(str(a), 'Éric ') + self.assertEqual(a.addr_spec, 'wők@exàmple.com') + self.assertEqual(str(a), 'Éric ') - # XXX: there is an API design issue that needs to be solved here. - #def test_non_ascii_username_raises(self): - # with self.assertRaises(ValueError): - # Address('foo', 'wők', 'example.com') + def test_i18n_in_addr_spec(self): + a = Address(addr_spec='wők@exàmple.com') + self.assertEqual(a.username, 'wők') + self.assertEqual(a.domain, 'exàmple.com') + self.assertEqual(a.addr_spec, 'wők@exàmple.com') + self.assertEqual(str(a), 'wők@exàmple.com') def test_crlf_in_constructor_args_raises(self): cases = ( @@ -1528,10 +1576,6 @@ def test_crlf_in_constructor_args_raises(self): with self.subTest(kwargs=kwargs), self.assertRaisesRegex(ValueError, "invalid arguments"): Address(**kwargs) - def test_non_ascii_username_in_addr_spec_raises(self): - with self.assertRaises(ValueError): - Address('foo', addr_spec='wők@example.com') - def test_address_addr_spec_and_username_raises(self): with self.assertRaises(TypeError): Address('foo', username='bing', addr_spec='bar@baz') @@ -1702,7 +1746,7 @@ def test_fold_unstructured_with_overlong_word(self): 'singlewordthatwontfit') self.assertEqual( h.fold(policy=policy.default.clone(max_line_length=20)), - 'Subject: \n' + 'Subject:\n' ' =?utf-8?q?thisisa?=\n' ' =?utf-8?q?verylon?=\n' ' =?utf-8?q?glineco?=\n' @@ -1718,7 +1762,7 @@ def test_fold_unstructured_with_two_overlong_words(self): 'singlewordthatwontfit plusanotherverylongwordthatwontfit') self.assertEqual( h.fold(policy=policy.default.clone(max_line_length=20)), - 'Subject: \n' + 'Subject:\n' ' =?utf-8?q?thisisa?=\n' ' =?utf-8?q?verylon?=\n' ' =?utf-8?q?glineco?=\n' @@ -1812,5 +1856,18 @@ def test_message_id_header_is_not_folded(self): h.fold(policy=policy.default.clone(max_line_length=20)), 'Message-ID:\n <ईमेलfromMessage@wők.com>\n') + def test_fold_references(self): + h = self.make_header( + 'References', + ' ' + '' + ) + self.assertEqual( + h.fold(policy=policy.default.clone(max_line_length=20)), + 'References: ' + '\n' + ' \n') + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_email/test_message.py b/Lib/test/test_email/test_message.py index b4128f70f18412..56ad446694d1f8 100644 --- a/Lib/test/test_email/test_message.py +++ b/Lib/test/test_email/test_message.py @@ -1004,6 +1004,32 @@ def test_folding_with_long_nospace_http_policy_1(self): parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default) self.assertEqual(parsed_msg['Message-ID'], m['Message-ID']) + def test_no_wrapping_max_line_length(self): + # Test that falsey 'max_line_length' are converted to sys.maxsize. + for n in [0, None]: + with self.subTest(max_line_length=n): + self.do_test_no_wrapping_max_line_length(n) + + def do_test_no_wrapping_max_line_length(self, falsey): + self.assertFalse(falsey) + pol = policy.default.clone(max_line_length=falsey) + subj = "S" * 100 + body = "B" * 100 + msg = EmailMessage(policy=pol) + msg["From"] = "a@ex.com" + msg["To"] = "b@ex.com" + msg["Subject"] = subj + msg.set_content(body) + + raw = msg.as_bytes() + self.assertNotIn(b"=\n", raw, + "Found fold indicator; wrapping not disabled") + + parsed = message_from_bytes(raw, policy=policy.default) + self.assertEqual(parsed["Subject"], subj) + parsed_body = parsed.get_body().get_content().rstrip('\n') + self.assertEqual(parsed_body, body) + def test_invalid_header_names(self): invalid_headers = [ ('Invalid Header', 'contains space'), diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index baa35fd68e49c5..90e8e5580295f9 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -273,7 +273,7 @@ def test_non_ascii_chars_do_not_cause_inf_loop(self): actual = policy.fold('Subject', 'ą' * 12) self.assertEqual( actual, - 'Subject: \n' + + 'Subject:\n' + 12 * ' =?utf-8?q?=C4=85?=\n') def test_short_maxlen_error(self): @@ -296,7 +296,7 @@ def test_short_maxlen_error(self): policy.fold("Subject", subject) def test_verify_generated_headers(self): - """Turning protection off allows header injection""" + # Turning protection off allows header injection policy = email.policy.default.clone(verify_generated_headers=False) for text in ( 'Header: Value\r\nBad: Injection\r\n', @@ -319,6 +319,10 @@ def fold(self, **kwargs): message.as_string(), f"{text}\nBody", ) + self.assertEqual( + message.as_bytes(), + f"{text}\nBody".encode(), + ) # XXX: Need subclassing tests. # For adding subclassed objects, make sure the usual rules apply (subclass diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 22dfdb6bb6f138..2d1533c46b98f3 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -74,6 +74,27 @@ def debug_build(program): return name.casefold().endswith("_d".casefold()) +def getpath_which(program_name): + if sys.platform != 'cygwin': + return shutil.which(program_name) + + # shutil.which() checks for os.access(fn, os.F_OK | os.X_OK), whereas + # getpath.isxfile() doesn't. The difference matters on Cygwin. + import stat + def isxfile(fn): + try: + st = os.stat(fn) + except OSError: + return False + return stat.S_ISREG(st.st_mode) + + for p in os.environ['PATH'].split(':'): + p = os.path.join(p, program_name) + if isxfile(p): + return p + return None + + def remove_python_envvars(): env = dict(os.environ) # Remove PYTHON* environment variables to get deterministic environment @@ -92,6 +113,8 @@ def setUp(self): exename += ext exepath = builddir else: + if sys.platform == 'cygwin': + exename += '.exe' exepath = os.path.join(builddir, 'Programs') self.test_exe = exe = os.path.join(exepath, exename) if not os.path.exists(exe): @@ -239,6 +262,37 @@ def test_repeated_init_and_inittab(self): lines = "\n".join(lines) + "\n" self.assertEqual(out, lines) + def test_create_module_from_initfunc(self): + out, err = self.run_embedded_interpreter("test_create_module_from_initfunc") + self.assertEqual(self._nogil_filtered_err(err, "embedded_ext"), "") + self.assertEqual(out, + "\n" + "my_test_extension.executed='yes'\n" + "my_test_extension.exec_slot_ran='yes'\n" + "\n" + "embedded_ext.executed='yes'\n" + ) + + def test_inittab_submodule_multiphase(self): + out, err = self.run_embedded_interpreter("test_inittab_submodule_multiphase") + self.assertEqual(err, "") + self.assertEqual(out, + "\n" + "\n" + "Hello from sub-module\n" + "mp_pkg.mp_submod.mp_submod_exec_slot_ran='yes'\n" + "mp_pkg.mp_pkg_exec_slot_ran='yes'\n" + ) + + def test_inittab_submodule_singlephase(self): + out, err = self.run_embedded_interpreter("test_inittab_submodule_singlephase") + self.assertEqual(self._nogil_filtered_err(err, "sp_pkg"), "") + self.assertEqual(out, + "\n" + "\n" + "Hello from sub-module\n" + ) + def test_forced_io_encoding(self): # Checks forced configuration of embedded interpreter IO streams env = dict(os.environ, PYTHONIOENCODING="utf-8:surrogateescape") @@ -297,6 +351,8 @@ def test_pre_initialization_api(self): expected_path = self.test_exe else: expected_path = os.path.join(os.getcwd(), "_testembed") + if sys.platform == 'cygwin': + expected_path += '.exe' expected_output = f"sys.executable: {expected_path}\n" self.assertIn(expected_output, out) self.assertEqual(err, '') @@ -516,6 +572,24 @@ def test_getargs_reset_static_parser(self): out, err = self.run_embedded_interpreter("test_repeated_init_exec", code) self.assertEqual(out, '1\n2\n3\n' * INIT_LOOPS) + @staticmethod + def _nogil_filtered_err(err: str, mod_name: str) -> str: + if not support.Py_GIL_DISABLED: + return err + + # the test imports a singlephase init extension, so it emits a warning + # under the free-threaded build + expected_runtime_warning = ( + "RuntimeWarning: The global interpreter lock (GIL)" + f" has been enabled to load module '{mod_name}'" + ) + filtered_err_lines = [ + line + for line in err.strip().splitlines() + if expected_runtime_warning not in line + ] + return "\n".join(filtered_err_lines) + def config_dev_mode(preconfig, config): preconfig['allocator'] = PYMEM_ALLOCATOR_DEBUG @@ -586,6 +660,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'tracemalloc': 0, 'perf_profiling': 0, 'import_time': 0, + 'lazy_imports': -1, 'thread_inherit_context': DEFAULT_THREAD_INHERIT_CONTEXT, 'context_aware_warnings': DEFAULT_CONTEXT_AWARE_WARNINGS, 'code_debug_ranges': True, @@ -593,6 +668,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'dump_refs': False, 'dump_refs_file': None, 'malloc_stats': False, + 'pymalloc_hugepages': False, 'filesystem_encoding': GET_DEFAULT_CONFIG, 'filesystem_errors': GET_DEFAULT_CONFIG, @@ -821,12 +897,16 @@ def get_expected_config(self, expected_preconfig, expected, default_executable = os.path.abspath(expected['program_name']) else: default_executable = os.path.join(os.getcwd(), '_testembed') + if sys.platform == 'cygwin': + default_executable += '.exe' if expected['executable'] is self.GET_DEFAULT_CONFIG: expected['executable'] = default_executable if expected['base_executable'] is self.GET_DEFAULT_CONFIG: expected['base_executable'] = default_executable if expected['program_name'] is self.GET_DEFAULT_CONFIG: expected['program_name'] = './_testembed' + if sys.platform == 'cygwin': + expected['program_name'] += '.exe' config = configs['config'] for key, value in expected.items(): @@ -995,6 +1075,7 @@ def test_init_from_config(self): 'code_debug_ranges': False, 'show_ref_count': True, 'malloc_stats': True, + 'pymalloc_hugepages': True, 'stdio_encoding': 'iso8859-1', 'stdio_errors': 'replace', @@ -1060,6 +1141,7 @@ def test_init_compat_env(self): 'import_time': 1, 'code_debug_ranges': False, 'malloc_stats': True, + 'pymalloc_hugepages': True, 'inspect': True, 'optimization_level': 2, 'pythonpath_env': '/my/path', @@ -1096,6 +1178,7 @@ def test_init_python_env(self): 'import_time': 1, 'code_debug_ranges': False, 'malloc_stats': True, + 'pymalloc_hugepages': True, 'inspect': True, 'optimization_level': 2, 'pythonpath_env': '/my/path', @@ -1232,21 +1315,6 @@ def test_init_dont_configure_locale(self): self.check_all_configs("test_init_dont_configure_locale", {}, preconfig, api=API_PYTHON) - @unittest.skip('as of 3.11 this test no longer works because ' - 'path calculations do not occur on read') - def test_init_read_set(self): - config = { - 'program_name': './init_read_set', - 'executable': 'my_executable', - 'base_executable': 'my_executable', - } - def modify_path(path): - path.insert(1, "test_path_insert1") - path.append("test_path_append") - self.check_all_configs("test_init_read_set", config, - api=API_PYTHON, - modify_path_cb=modify_path) - def test_init_sys_add(self): config = { 'faulthandler': 1, @@ -1280,6 +1348,24 @@ def test_init_run_main(self): } self.check_all_configs("test_init_run_main", config, api=API_PYTHON) + def test_init_main(self): + code = ('import _testinternalcapi, json; ' + 'print(json.dumps(_testinternalcapi.get_configs()))') + config = { + 'argv': ['-c', 'arg2'], + 'orig_argv': ['python3', + '-c', code, + 'arg2'], + 'program_name': './python3', + 'run_command': code + '\n', + 'parse_argv': True, + '_init_main': False, + 'sys_path_0': '', + } + self.check_all_configs("test_init_main", config, + api=API_PYTHON, + stderr="Run Python code before _Py_InitializeMain") + def test_init_parse_argv(self): config = { 'parse_argv': True, @@ -1313,7 +1399,7 @@ def default_program_name(self, config): if MACOS: executable = self.test_exe else: - executable = shutil.which(program_name) or '' + executable = getpath_which(program_name) or '' config.update({ 'program_name': program_name, 'base_executable': executable, @@ -1412,6 +1498,13 @@ def tmpdir_with_python(self, subdir=None): shutil.copystat(self.test_exe, exec_copy) self.test_exe = exec_copy + if sys.platform == "cygwin": + # Copy libpython DLL + exe_path = os.path.dirname(sys.executable) + libpython_dll = sysconfig.get_config_var('DLLLIBRARY') + shutil.copy2(os.path.join(exe_path, libpython_dll), + os.path.join(tmpdir, libpython_dll)) + yield tmpdir def test_init_setpythonhome(self): @@ -1452,8 +1545,12 @@ def test_init_setpythonhome(self): } self.default_program_name(config) env = {'TESTHOME': home, 'PYTHONPATH': paths_str} + # When running from source, TESTHOME will be the build directory, which + # isn't a valid home unless _is_python_build is set. getpath will then + # fail to find the standard library and show a warning, so we need to + # ignore stderr. self.check_all_configs("test_init_setpythonhome", config, - api=API_COMPAT, env=env) + api=API_COMPAT, env=env, ignore_stderr=True) def test_init_is_python_build_with_home(self): # Test _Py_path_config._is_python_build configuration (gh-91985) @@ -1489,15 +1586,26 @@ def test_init_is_python_build_with_home(self): 'exec_prefix': exec_prefix, 'base_exec_prefix': exec_prefix, 'pythonpath_env': paths_str, - 'stdlib_dir': stdlib, + 'stdlib_dir': stdlib, # Only correct on _is_python_build==0! } # The code above is taken from test_init_setpythonhome() env = {'TESTHOME': home, 'PYTHONPATH': paths_str} env['NEGATIVE_ISPYTHONBUILD'] = '1' config['_is_python_build'] = 0 + # This configuration doesn't set a valid stdlibdir/plststdlibdir because + # with _is_python_build=0 getpath doesn't check for the build directory + # landmarks in PYTHONHOME/Py_SetPythonHome. + # getpath correctly shows a warning, which messes up check_all_configs, + # so we need to ignore stderr. self.check_all_configs("test_init_is_python_build", config, - api=API_COMPAT, env=env) + api=API_COMPAT, env=env, ignore_stderr=True) + + # config['stdlib_dir'] = os.path.join(home, 'Lib') + # FIXME: This test does not check if stdlib_dir is calculated correctly. + # test_init_is_python_build runs the initialization twice, + # setting stdlib_dir in _Py_path_config on the first run, which + # then overrides the stdlib_dir calculation (as of GH-108730). env['NEGATIVE_ISPYTHONBUILD'] = '0' config['_is_python_build'] = 1 @@ -1512,8 +1620,14 @@ def test_init_is_python_build_with_home(self): expected_paths[0] = self.module_search_paths(prefix=prefix)[0] config.update(prefix=prefix, base_prefix=prefix, exec_prefix=exec_prefix, base_exec_prefix=exec_prefix) + # This also shows the bad stdlib warning, getpath is run twice. The + # first time with _is_python_build=0, which results in the warning just + # as explained above. However, the second time a valid standard library + # should be found, but the stdlib_dir is cached in _Py_path_config from + # the first run, which ovewrites it, so it also shows the warning. + # Also ignore stderr. self.check_all_configs("test_init_is_python_build", config, - api=API_COMPAT, env=env) + api=API_COMPAT, env=env, ignore_stderr=True) def copy_paths_by_env(self, config): all_configs = self._get_expected_config() @@ -1573,6 +1687,7 @@ def test_init_pybuilddir_win32(self): prefix = os.path.normpath(os.path.join(tmpdir, vpath)) # The stdlib dir is dirname(executable) + VPATH + 'Lib' stdlibdir = os.path.normpath(os.path.join(tmpdir, vpath, 'Lib')) + os.mkdir(stdlibdir) filename = os.path.join(tmpdir, 'pybuilddir.txt') with open(filename, "w", encoding="utf8") as fp: @@ -1851,6 +1966,12 @@ def test_init_in_background_thread(self): out, err = self.run_embedded_interpreter("test_init_in_background_thread") self.assertEqual(err, "") + def test_isinitialized_false_during_site_import(self): + # gh-146302: Py_IsInitialized() must not return true during site import. + out, err = self.run_embedded_interpreter( + "test_isinitialized_false_during_site_import") + self.assertEqual(err, "") + class AuditingTests(EmbeddingTestsMixin, unittest.TestCase): def test_open_code_hook(self): @@ -1908,10 +2029,21 @@ def test_audit_run_stdin(self): def test_get_incomplete_frame(self): self.run_embedded_interpreter("test_get_incomplete_frame") - def test_gilstate_after_finalization(self): self.run_embedded_interpreter("test_gilstate_after_finalization") + def test_thread_state_ensure(self): + self.run_embedded_interpreter("test_thread_state_ensure") + + def test_main_interpreter_view(self): + self.run_embedded_interpreter("test_main_interpreter_view") + + def test_thread_state_ensure_from_view(self): + self.run_embedded_interpreter("test_thread_state_ensure_from_view") + + def test_concurrent_finalization_stress(self): + self.run_embedded_interpreter("test_concurrent_finalization_stress") + class MiscTests(EmbeddingTestsMixin, unittest.TestCase): def test_unicode_id_init(self): @@ -1972,7 +2104,7 @@ def test_no_memleak(self): def test_presite(self): cmd = [ sys.executable, - "-I", "-X", "presite=test._test_embed_structseq", + "-I", "-X", "presite=test._test_embed_structseq:main", "-c", "print('unique-python-message')", ] proc = subprocess.run( diff --git a/Lib/test/test_ensurepip.py b/Lib/test/test_ensurepip.py index 6d3c91b0b6d9f9..20a56ed715d8ab 100644 --- a/Lib/test/test_ensurepip.py +++ b/Lib/test/test_ensurepip.py @@ -7,11 +7,18 @@ import unittest import unittest.mock from pathlib import Path +from test.support import import_helper import ensurepip import ensurepip._uninstall +if sys.implementation.cache_tag is None: + COMPILE_OPT = ["--no-compile"] +else: + COMPILE_OPT = [] + + class TestPackages(unittest.TestCase): def touch(self, directory, filename): fullname = os.path.join(directory, filename) @@ -30,6 +37,15 @@ def test_version_no_dir(self): # when the bundled pip wheel is used, we get _PIP_VERSION self.assertEqual(ensurepip._PIP_VERSION, ensurepip.version()) + def test_wheel_pkg_dir_none(self): + # gh-146310: empty or None WHEEL_PKG_DIR should not search CWD + for value in ('', None): + with unittest.mock.patch('sysconfig.get_config_var', + return_value=value) as get_config_var: + module = import_helper.import_fresh_module('ensurepip') + self.assertIsNone(module._WHEEL_PKG_DIR) + get_config_var.assert_called_once_with('WHEEL_PKG_DIR') + def test_selected_wheel_path_no_dir(self): pip_filename = f'pip-{ensurepip._PIP_VERSION}-py3-none-any.whl' with unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', None): @@ -60,6 +76,11 @@ def setUp(self): self.run_pip.return_value = 0 self.addCleanup(run_pip_patch.stop) + # Allow testing on zlib-less platforms by avoiding the check for zlib in _bootstrap() + zlib_patch = unittest.mock.patch.dict('sys.modules', {'zlib': unittest.mock.MagicMock()}) + zlib_patch.start() + self.addCleanup(zlib_patch.stop) + # Avoid side effects on the actual os module real_devnull = os.devnull os_patch = unittest.mock.patch("ensurepip.os") @@ -80,7 +101,7 @@ def test_basic_bootstrapping(self): self.run_pip.assert_called_once_with( [ "install", "--no-cache-dir", "--no-index", "--find-links", - unittest.mock.ANY, "pip", + unittest.mock.ANY, *COMPILE_OPT, "pip", ], unittest.mock.ANY, ) @@ -94,7 +115,7 @@ def test_bootstrapping_with_root(self): self.run_pip.assert_called_once_with( [ "install", "--no-cache-dir", "--no-index", "--find-links", - unittest.mock.ANY, "--root", "/foo/bar/", + unittest.mock.ANY, "--root", "/foo/bar/", *COMPILE_OPT, "pip", ], unittest.mock.ANY, @@ -106,7 +127,7 @@ def test_bootstrapping_with_user(self): self.run_pip.assert_called_once_with( [ "install", "--no-cache-dir", "--no-index", "--find-links", - unittest.mock.ANY, "--user", "pip", + unittest.mock.ANY, "--user", *COMPILE_OPT, "pip", ], unittest.mock.ANY, ) @@ -117,7 +138,7 @@ def test_bootstrapping_with_upgrade(self): self.run_pip.assert_called_once_with( [ "install", "--no-cache-dir", "--no-index", "--find-links", - unittest.mock.ANY, "--upgrade", "pip", + unittest.mock.ANY, "--upgrade", *COMPILE_OPT, "pip", ], unittest.mock.ANY, ) @@ -128,7 +149,7 @@ def test_bootstrapping_with_verbosity_1(self): self.run_pip.assert_called_once_with( [ "install", "--no-cache-dir", "--no-index", "--find-links", - unittest.mock.ANY, "-v", "pip", + unittest.mock.ANY, "-v", *COMPILE_OPT, "pip", ], unittest.mock.ANY, ) @@ -139,7 +160,7 @@ def test_bootstrapping_with_verbosity_2(self): self.run_pip.assert_called_once_with( [ "install", "--no-cache-dir", "--no-index", "--find-links", - unittest.mock.ANY, "-vv", "pip", + unittest.mock.ANY, "-vv", *COMPILE_OPT, "pip", ], unittest.mock.ANY, ) @@ -150,7 +171,7 @@ def test_bootstrapping_with_verbosity_3(self): self.run_pip.assert_called_once_with( [ "install", "--no-cache-dir", "--no-index", "--find-links", - unittest.mock.ANY, "-vvv", "pip", + unittest.mock.ANY, "-vvv", *COMPILE_OPT, "pip", ], unittest.mock.ANY, ) @@ -185,6 +206,16 @@ def test_pip_config_file_disabled(self): ensurepip.bootstrap() self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull) + def test_missing_zlib(self): + with unittest.mock.patch.dict('sys.modules', {'zlib': None}): + with self.assertRaises(ModuleNotFoundError) as cm: + ensurepip.bootstrap() + + error_msg = str(cm.exception) + self.assertIn("ensurepip requires the standard library module 'zlib'", error_msg) + + self.assertFalse(self.run_pip.called) + @contextlib.contextmanager def fake_pip(version=ensurepip.version()): if version is None: @@ -297,7 +328,7 @@ def test_basic_bootstrapping(self): self.run_pip.assert_called_once_with( [ "install", "--no-cache-dir", "--no-index", "--find-links", - unittest.mock.ANY, "pip", + unittest.mock.ANY, *COMPILE_OPT, "pip", ], unittest.mock.ANY, ) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 2dd585f246d506..b05eab43bd9eff 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -4887,7 +4887,7 @@ class Color(Enum): def _generate_next_value_(name, start, count, last): return name - def test_auto_order_wierd(self): + def test_auto_order_weird(self): weird_auto = auto() weird_auto.value = 'pathological case' class Color(Enum): @@ -5021,8 +5021,8 @@ class Color(enum.Enum) | __members__ | Returns a mapping of member name->value. | - | This mapping lists all enum members, including aliases. Note that this - | is a read-only view of the internal mapping.""" + | This mapping lists all enum members, including aliases. Note that + | this is a read-only view of the internal mapping.""" expected_help_output_without_docs = """\ Help on class Color in module %s: @@ -5105,6 +5105,8 @@ def test_inspect_getmembers(self): ('__qualname__', 'TestStdLib.Color'), ('__init_subclass__', getattr(self.Color, '__init_subclass__')), ('__iter__', self.Color.__iter__), + ('_missing_', self.Color._missing_), + ('_generate_next_value_', self.Color._generate_next_value_), )) result = dict(inspect.getmembers(self.Color)) self.assertEqual(set(values.keys()), set(result.keys())) @@ -5147,6 +5149,10 @@ def test_inspect_classify_class_attrs(self): defining_class=self.Color, object='Color'), Attribute(name='__qualname__', kind='data', defining_class=self.Color, object='TestStdLib.Color'), + Attribute(name='_missing_', kind='class method', + defining_class=Enum, object=Enum.__dict__['_missing_']), + Attribute(name='_generate_next_value_', kind='static method', + defining_class=self.Color, object=self.Color.__dict__['_generate_next_value_']), Attribute(name='YELLOW', kind='data', defining_class=self.Color, object=self.Color.YELLOW), Attribute(name='MAGENTA', kind='data', @@ -5178,11 +5184,13 @@ def test_inspect_classify_class_attrs(self): # __doc__ is too big to check exactly, so treat the same as __init_subclass__ for name in ('name','kind','defining_class'): if getattr(v, name) != getattr(r, name): - print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='') + print('\n%s\nexpected: %s\nactual: %s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='') failed = True + # breakpoint() elif r != v: - print('\n%s\n%s\n%s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='') + print('\n%s\nexpected: %s\nactual: %s\n%s\n' % ('=' * 75, r, v, '=' * 75), sep='') failed = True + # breakpoint() if failed: self.fail("result does not equal expected, see print above") @@ -5324,7 +5332,7 @@ def __new__(cls, value, label): class MiscTestCase(unittest.TestCase): def test__all__(self): - support.check__all__(self, enum, not_exported={'bin', 'show_flag_values'}) + support.check__all__(self, enum) @cpython_only def test_lazy_import(self): @@ -5529,12 +5537,17 @@ def test_enum_dict_standalone(self): # helpers def enum_dir(cls): + if issubclass(cls, Flag): + members = list(cls._member_map_.keys()) + else: + members = cls._member_names_ interesting = set([ '__class__', '__contains__', '__doc__', '__getitem__', '__iter__', '__len__', '__members__', '__module__', '__name__', '__qualname__', + '_generate_next_value_', '_missing_', ] - + cls._member_names_ + + members ) if cls._new_member_ is not object.__new__: interesting.add('__new__') @@ -5548,7 +5561,8 @@ def enum_dir(cls): def member_dir(member): if member.__class__._member_type_ is object: - allowed = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value']) + allowed = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value', + '_generate_next_value_', '_missing_', '_add_alias_', '_add_value_alias_']) else: allowed = set(dir(member)) for cls in member.__class__.mro(): diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py index c94946a6ae6b7c..5e6a4ab0166a86 100644 --- a/Lib/test/test_epoll.py +++ b/Lib/test/test_epoll.py @@ -259,6 +259,15 @@ def test_close(self): self.assertRaises(ValueError, epoll.register, fd, select.EPOLLIN) self.assertRaises(ValueError, epoll.unregister, fd) + def test_close_error(self): + # gh-146205: close() should raise OSError if underlying fd is invalid + epoll = select.epoll() + fd = epoll.fileno() + os.close(fd) + with self.assertRaises(OSError) as cm: + epoll.close() + self.assertEqual(cm.exception.errno, errno.EBADF) + def test_fd_non_inheritable(self): epoll = select.epoll() self.addCleanup(epoll.close) diff --git a/Lib/test/test_exception_group.py b/Lib/test/test_exception_group.py index 5df2c41c6b56bc..35ffc9a0a4cf30 100644 --- a/Lib/test/test_exception_group.py +++ b/Lib/test/test_exception_group.py @@ -1,4 +1,4 @@ -import collections.abc +import collections import types import unittest from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow, exceeds_recursion_limit @@ -193,6 +193,89 @@ class MyEG(ExceptionGroup): "MyEG('flat', [ValueError(1), TypeError(2)]), " "TypeError(2)])")) + def test_exceptions_mutation(self): + class MyEG(ExceptionGroup): + pass + + excs = [ValueError(1), TypeError(2)] + eg = MyEG('test', excs) + + self.assertEqual(repr(eg), "MyEG('test', [ValueError(1), TypeError(2)])") + excs.clear() + + # Ensure that clearing the exceptions sequence doesn't change the repr. + self.assertEqual(repr(eg), "MyEG('test', [ValueError(1), TypeError(2)])") + + # Ensure that the args are still as passed. + self.assertEqual(eg.args, ('test', [])) + + excs = (ValueError(1), KeyboardInterrupt(2)) + eg = BaseExceptionGroup('test', excs) + + # Ensure that immutable sequences still work fine. + self.assertEqual( + repr(eg), + "BaseExceptionGroup('test', (ValueError(1), KeyboardInterrupt(2)))" + ) + + # Test non-standard custom sequences. + excs = collections.deque([ValueError(1), TypeError(2)]) + eg = ExceptionGroup('test', excs) + + self.assertEqual( + repr(eg), + "ExceptionGroup('test', deque([ValueError(1), TypeError(2)]))" + ) + excs.clear() + + # Ensure that clearing the exceptions sequence doesn't change the repr. + self.assertEqual( + repr(eg), + "ExceptionGroup('test', deque([ValueError(1), TypeError(2)]))" + ) + + def test_repr_small_size_args(self): + eg = ExceptionGroup("msg", [ValueError()]) + eg.args = () + # repr of the ExceptionGroup with empty args should not crash + self.assertEqual(repr(eg), "ExceptionGroup('msg', (ValueError(),))") + + eg.args = (1,) + # repr of the ExceptionGroup with 1-size args should not crash + self.assertEqual(repr(eg), "ExceptionGroup('msg', (ValueError(),))") + + + + def test_repr_raises(self): + class MySeq(collections.abc.Sequence): + def __init__(self, raises): + self.raises = raises + + def __len__(self): + return 1 + + def __getitem__(self, index): + if index == 0: + return ValueError(1) + raise IndexError + + def __repr__(self): + if self.raises: + raise self.raises + return None + + seq = MySeq(None) + with self.assertRaisesRegex( + TypeError, + r".*MySeq\.__repr__\(\) must return a str, not NoneType" + ): + ExceptionGroup("test", seq) + + seq = MySeq(ValueError) + with self.assertRaises(ValueError): + BaseExceptionGroup("test", seq) + + def create_simple_eg(): excs = [] diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 57d0656487d4db..df7a04273b9b41 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -224,6 +224,8 @@ def check(self, src, lineno, offset, end_lineno=None, end_offset=None, encoding= if not isinstance(src, str): src = src.decode(encoding, 'replace') line = src.split('\n')[lineno-1] + if lineno == 1: + line = line.removeprefix('\ufeff') self.assertIn(line, cm.exception.text) def test_error_offset_continuation_characters(self): @@ -239,7 +241,9 @@ def testSyntaxErrorOffset(self): check('Python = "\u1e54\xfd\u0163\u0125\xf2\xf1" +', 1, 20) check(b'# -*- coding: cp1251 -*-\nPython = "\xcf\xb3\xf2\xee\xed" +', 2, 19, encoding='cp1251') - check(b'Python = "\xcf\xb3\xf2\xee\xed" +', 1, 10) + check(b'Python = "\xcf\xb3\xf2\xee\xed" +', 1, 12) + check(b'\n\n\nPython = "\xcf\xb3\xf2\xee\xed" +', 4, 12) + check(b'\xef\xbb\xbfPython = "\xcf\xb3\xf2\xee\xed" +', 1, 12) check('x = "a', 1, 5) check('lambda x: x = 2', 1, 1) check('f{a + b + c}', 1, 2) @@ -248,7 +252,16 @@ def testSyntaxErrorOffset(self): check('[\nfile\nfor str(file)\nin\n[]\n]', 3, 5) check('[file for\n str(file) in []]', 2, 2) check("ages = {'Alice'=22, 'Bob'=23}", 1, 9) - check('match ...:\n case {**rest, "key": value}:\n ...', 2, 19) + check(dedent("""\ + match ...: + case {**rest1, "after": after}: + ... + """), 2, 11) + check(dedent("""\ + match ...: + case {"before": before, **rest2, "after": after}: + ... + """), 2, 29) check("[a b c d e f]", 1, 2) check("for x yfff:", 1, 7) check("f(a for a in b, c)", 1, 3, 1, 15) @@ -287,7 +300,7 @@ def baz(): check("pass\npass\npass\n(1+)\npass\npass\npass", 4, 4) check("(1+)", 1, 4) check("[interesting\nfoo()\n", 1, 1) - check(b"\xef\xbb\xbf#coding: utf8\nprint('\xe6\x88\x91')\n", 0, -1) + check(b"\xef\xbb\xbf#coding: utf8\nprint('\xe6\x88\x91')\n", 1, 0) check("""f''' { (123_a) @@ -324,7 +337,6 @@ def baz(): check('x=1\nfrom __future__ import division', 2, 1) check('foo(1=2)', 1, 5) check('def f():\n x, y: int', 2, 3) - check('[*x for x in xs]', 1, 2) check('foo(x for x in range(10), 100)', 1, 5) check('for 1 in []: pass', 1, 5) check('(yield i) = 2', 1, 2) @@ -1702,6 +1714,20 @@ def inner(): gc_collect() # For PyPy or other GCs. self.assertEqual(wr(), None) + def test_oserror_reinit_leak(self): + # gh-150988: Check for memory leak when re-initializing OSError. + # Previously, setting OSError attributes in a subclass + # before calling super().__init__() leaked memory. + class LeakingOSError(OSError): + def __init__(self, code, message, filename, filename2): + self.strerror = message + self.filename = filename + self.filename2 = filename2 + super().__init__(code, message, filename, None, filename2) + + exc = LeakingOSError(1, "some message", "filename.py", "filename2.py") + exc.__init__(2, "another message", "filename3.py", "filename4.py") + def test_errno_ENOTDIR(self): # Issue #12802: "not a directory" errors are ENOTDIR even on Windows with self.assertRaises(OSError) as cm: @@ -1910,6 +1936,39 @@ def test_keyerror_context(self): exc2 = None + @cpython_only + # Python built with Py_TRACE_REFS fail with a fatal error in + # _PyRefchain_Trace() on memory allocation error. + @unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build') + def test_exec_set_nomemory_hang(self): + import_module("_testcapi") + # gh-134163: A MemoryError inside code that was wrapped by a try/except + # block would lead to an infinite loop. + + # The frame_lasti needs to be greater than 257 to prevent + # PyLong_FromLong() from returning cached integers, which + # don't require a memory allocation. Prepend some dummy code + # to artificially increase the instruction index. + warmup_code = "a = list(range(0, 1))\n" * 60 + user_input = warmup_code + dedent(""" + try: + import _testcapi + _testcapi.set_nomemory(0) + b = list(range(1000, 2000)) + except Exception as e: + import traceback + traceback.print_exc() + """) + with SuppressCrashReport(): + with script_helper.spawn_python('-c', user_input) as p: + p.wait() + output = p.stdout.read() + + self.assertIn(p.returncode, (0, 1)) + self.assertGreater(len(output), 0) # At minimum, should not hang + self.assertIn(b"MemoryError", output) + + class NameErrorTests(unittest.TestCase): def test_name_error_has_name(self): try: @@ -2079,6 +2138,50 @@ def test_copy_pickle(self): self.assertEqual(exc.name, orig.name) self.assertEqual(exc.path, orig.path) + def test_repr(self): + exc = ImportError() + self.assertEqual(repr(exc), "ImportError()") + + exc = ImportError('test') + self.assertEqual(repr(exc), "ImportError('test')") + + exc = ImportError('test', 'case') + self.assertEqual(repr(exc), "ImportError('test', 'case')") + + exc = ImportError(name='somemodule') + self.assertEqual(repr(exc), "ImportError(name='somemodule')") + + exc = ImportError('test', name='somemodule') + self.assertEqual(repr(exc), "ImportError('test', name='somemodule')") + + exc = ImportError(path='somepath') + self.assertEqual(repr(exc), "ImportError(path='somepath')") + + exc = ImportError('test', path='somepath') + self.assertEqual(repr(exc), "ImportError('test', path='somepath')") + + exc = ImportError(name='somename', path='somepath') + self.assertEqual(repr(exc), + "ImportError(name='somename', path='somepath')") + + exc = ImportError('test', name='somename', path='somepath') + self.assertEqual(repr(exc), + "ImportError('test', name='somename', path='somepath')") + + exc = ModuleNotFoundError('test', name='somename', path='somepath') + self.assertEqual(repr(exc), + "ModuleNotFoundError('test', name='somename', path='somepath')") + + def test_ModuleNotFoundError_repr_with_failed_import(self): + with self.assertRaises(ModuleNotFoundError) as cm: + import does_not_exist # type: ignore[import] # noqa: F401 + + self.assertEqual(cm.exception.name, "does_not_exist") + self.assertIsNone(cm.exception.path) + + self.assertEqual(repr(cm.exception), + "ModuleNotFoundError(\"No module named 'does_not_exist'\", name='does_not_exist')") + def run_script(source): if isinstance(source, str): @@ -2343,7 +2446,8 @@ def test_encodings(self): ) err = run_script(source.encode('cp437')) self.assertEqual(err[-3], ' "┬ó┬ó┬ó┬ó┬ó┬ó" + f(4, x for x in range(1))') - self.assertEqual(err[-2], ' ^^^') + self.assertEqual(err[-2], ' ^^^^^^^^^^^^^^^^^^^') + self.assertEqual(err[-1], 'SyntaxError: Generator expression must be parenthesized') # Check backwards tokenizer errors source = '# -*- coding: ascii -*-\n\n(\n' @@ -2471,6 +2575,30 @@ def test_incorrect_constructor(self): args = ("bad.py", 1, 2, "abcdefg", 1) self.assertRaises(TypeError, SyntaxError, "bad bad", args) + def test_syntax_error_memory_leak(self): + # gh-146250: memory leak with re-initialization of SyntaxError + e = SyntaxError("msg", ("file.py", 1, 2, "txt", 2, 3)) + e.__init__("new_msg", ("new_file.py", 2, 3, "new_txt", 3, 4)) + self.assertEqual(e.msg, "new_msg") + self.assertEqual(e.args, ("new_msg", ("new_file.py", 2, 3, "new_txt", 3, 4))) + self.assertEqual(e.filename, "new_file.py") + self.assertEqual(e.lineno, 2) + self.assertEqual(e.offset, 3) + self.assertEqual(e.text, "new_txt") + self.assertEqual(e.end_lineno, 3) + self.assertEqual(e.end_offset, 4) + + e = SyntaxError("msg", ("file.py", 1, 2, "txt", 2, 3)) + e.__init__("new_msg", ("new_file.py", 2, 3, "new_txt")) + self.assertEqual(e.msg, "new_msg") + self.assertEqual(e.args, ("new_msg", ("new_file.py", 2, 3, "new_txt"))) + self.assertEqual(e.filename, "new_file.py") + self.assertEqual(e.lineno, 2) + self.assertEqual(e.offset, 3) + self.assertEqual(e.text, "new_txt") + self.assertIsNone(e.end_lineno) + self.assertIsNone(e.end_offset) + class TestInvalidExceptionMatcher(unittest.TestCase): def test_except_star_invalid_exception_type(self): diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index d9d85fe79af883..4da5601e80295f 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -137,7 +137,7 @@ >>> g(*Nothing()) Traceback (most recent call last): ... - TypeError: test.test_extcall.g() argument after * must be an iterable, not Nothing + TypeError: Value after * must be an iterable, not Nothing >>> class Nothing: ... def __len__(self): return 5 @@ -146,7 +146,7 @@ >>> g(*Nothing()) Traceback (most recent call last): ... - TypeError: test.test_extcall.g() argument after * must be an iterable, not Nothing + TypeError: Value after * must be an iterable, not Nothing >>> class Nothing(): ... def __len__(self): return 5 @@ -266,7 +266,7 @@ >>> h(*h) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after * must be an iterable, not function + TypeError: Value after * must be an iterable, not function >>> h(1, *h) Traceback (most recent call last): @@ -281,55 +281,69 @@ >>> dir(*h) Traceback (most recent call last): ... - TypeError: dir() argument after * must be an iterable, not function + TypeError: Value after * must be an iterable, not function >>> nothing = None >>> nothing(*h) Traceback (most recent call last): ... - TypeError: None argument after * must be an iterable, \ -not function + TypeError: Value after * must be an iterable, not function >>> h(**h) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not function + TypeError: Value after ** must be a mapping, not function >>> h(**[]) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not list + TypeError: Value after ** must be a mapping, not list >>> h(a=1, **h) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not function + TypeError: Value after ** must be a mapping, not function >>> h(a=1, **[]) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not list + TypeError: Value after ** must be a mapping, not list >>> h(**{'a': 1}, **h) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not function + TypeError: Value after ** must be a mapping, not function >>> h(**{'a': 1}, **[]) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not list + TypeError: Value after ** must be a mapping, not list >>> dir(**h) Traceback (most recent call last): ... - TypeError: dir() argument after ** must be a mapping, not function + TypeError: Value after ** must be a mapping, not function >>> nothing(**h) Traceback (most recent call last): ... - TypeError: None argument after ** must be a mapping, \ -not function + TypeError: Value after ** must be a mapping, not function + + >>> class OnlyKeys: + ... def keys(self): + ... return ['key'] + >>> h(**OnlyKeys()) + Traceback (most recent call last): + ... + TypeError: 'OnlyKeys' object is not subscriptable + + >>> class BrokenKeys: + ... def keys(self): + ... return 1 + >>> h(**BrokenKeys()) + Traceback (most recent call last): + ... + TypeError: test.test_extcall.BrokenKeys.keys() must return an iterable, not int >>> dir(b=1, **{'b': 1}) Traceback (most recent call last): @@ -542,6 +556,151 @@ """ +def test_errors_in_iter(): + """ + >>> class A: + ... def __iter__(self): + ... raise exc + ... + >>> def f(*args, **kwargs): pass + >>> exc = ZeroDivisionError('some error') + >>> f(*A()) + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> exc = AttributeError('some error') + >>> f(*A()) + Traceback (most recent call last): + ... + AttributeError: some error + + >>> exc = TypeError('some error') + >>> f(*A()) + Traceback (most recent call last): + ... + TypeError: some error + """ + +def test_errors_in_next(): + """ + >>> class I: + ... def __iter__(self): + ... return self + ... def __next__(self): + ... raise exc + ... + >>> class A: + ... def __iter__(self): + ... return I() + ... + + >>> def f(*args, **kwargs): pass + >>> exc = ZeroDivisionError('some error') + >>> f(*A()) + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> exc = AttributeError('some error') + >>> f(*A()) + Traceback (most recent call last): + ... + AttributeError: some error + + >>> exc = TypeError('some error') + >>> f(*A()) + Traceback (most recent call last): + ... + TypeError: some error + """ + +def test_errors_in_keys(): + """ + >>> class D: + ... def keys(self): + ... raise exc + ... + >>> def f(*args, **kwargs): pass + >>> exc = ZeroDivisionError('some error') + >>> f(**D()) + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> exc = AttributeError('some error') + >>> f(**D()) + Traceback (most recent call last): + ... + AttributeError: some error + + >>> exc = KeyError('some error') + >>> f(**D()) + Traceback (most recent call last): + ... + KeyError: 'some error' + """ + +def test_errors_in_keys_next(): + """ + >>> class I: + ... def __iter__(self): + ... return self + ... def __next__(self): + ... raise exc + ... + >>> class D: + ... def keys(self): + ... return I() + ... + >>> def f(*args, **kwargs): pass + >>> exc = ZeroDivisionError('some error') + >>> f(**D()) + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> exc = AttributeError('some error') + >>> f(**D()) + Traceback (most recent call last): + ... + AttributeError: some error + + >>> exc = KeyError('some error') + >>> f(**D()) + Traceback (most recent call last): + ... + KeyError: 'some error' + """ + +def test_errors_in_getitem(): + """ + >>> class D: + ... def keys(self): + ... return ['key'] + ... def __getitem__(self, key): + ... raise exc + ... + >>> def f(*args, **kwargs): pass + >>> exc = ZeroDivisionError('some error') + >>> f(**D()) + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> exc = AttributeError('some error') + >>> f(**D()) + Traceback (most recent call last): + ... + AttributeError: some error + + >>> exc = KeyError('some error') + >>> f(**D()) + Traceback (most recent call last): + ... + KeyError: 'some error' + """ + import doctest import unittest diff --git a/Lib/test/test_external_inspection.py b/Lib/test/test_external_inspection.py index 354a82a800fd21..6b1529aa173f01 100644 --- a/Lib/test/test_external_inspection.py +++ b/Lib/test/test_external_inspection.py @@ -1,11 +1,13 @@ import unittest import os import textwrap +import contextlib import importlib import sys import socket import threading import time +from contextlib import contextmanager from asyncio import staggered, taskgroups, base_events, tasks from unittest.mock import ANY from test.support import ( @@ -13,12 +15,39 @@ SHORT_TIMEOUT, busy_retry, requires_gil_enabled, + requires_remote_subprocess_debugging, ) +from test.support.import_helper import import_module from test.support.script_helper import make_script from test.support.socket_helper import find_unused_port import subprocess +# Profiling mode constants +PROFILING_MODE_WALL = 0 +PROFILING_MODE_CPU = 1 +PROFILING_MODE_GIL = 2 +PROFILING_MODE_ALL = 3 +PROFILING_MODE_EXCEPTION = 4 + +# Thread status flags +THREAD_STATUS_HAS_GIL = 1 << 0 +THREAD_STATUS_ON_CPU = 1 << 1 +THREAD_STATUS_UNKNOWN = 1 << 2 +THREAD_STATUS_HAS_EXCEPTION = 1 << 4 + +# Maximum number of retry attempts for operations that may fail transiently +MAX_TRIES = 10 +RETRY_DELAY = 0.1 + +# Exceptions that can occur transiently when reading from a live process +TRANSIENT_ERRORS = (OSError, RuntimeError, UnicodeDecodeError) + +try: + from concurrent import interpreters +except ImportError: + interpreters = None + PROCESS_VM_READV_SUPPORTED = False try: @@ -31,12 +60,149 @@ ) +# ============================================================================ +# Module-level helper functions +# ============================================================================ + + def _make_test_script(script_dir, script_basename, source): to_return = make_script(script_dir, script_basename, source) importlib.invalidate_caches() return to_return +def _create_server_socket(port, backlog=1): + """Create and configure a server socket for test communication.""" + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server_socket.bind(("localhost", port)) + server_socket.settimeout(SHORT_TIMEOUT) + server_socket.listen(backlog) + return server_socket + + +def _wait_for_signal(sock, expected_signals, timeout=SHORT_TIMEOUT): + """ + Wait for expected signal(s) from a socket with proper timeout and EOF handling. + + Args: + sock: Connected socket to read from + expected_signals: Single bytes object or list of bytes objects to wait for + timeout: Socket timeout in seconds + + Returns: + bytes: Complete accumulated response buffer + + Raises: + RuntimeError: If connection closed before signal received or timeout + """ + if isinstance(expected_signals, bytes): + expected_signals = [expected_signals] + + sock.settimeout(timeout) + buffer = b"" + + while True: + # Check if all expected signals are in buffer + if all(sig in buffer for sig in expected_signals): + return buffer + + try: + chunk = sock.recv(4096) + if not chunk: + # EOF - connection closed + raise RuntimeError( + f"Connection closed before receiving expected signals. " + f"Expected: {expected_signals}, Got: {buffer[-200:]!r}" + ) + buffer += chunk + except socket.timeout: + raise RuntimeError( + f"Timeout waiting for signals. " + f"Expected: {expected_signals}, Got: {buffer[-200:]!r}" + ) + + +def _wait_for_n_signals(sock, signal_pattern, count, timeout=SHORT_TIMEOUT): + """ + Wait for N occurrences of a signal pattern. + + Args: + sock: Connected socket to read from + signal_pattern: bytes pattern to count (e.g., b"ready") + count: Number of occurrences expected + timeout: Socket timeout in seconds + + Returns: + bytes: Complete accumulated response buffer + + Raises: + RuntimeError: If connection closed or timeout before receiving all signals + """ + sock.settimeout(timeout) + buffer = b"" + found_count = 0 + + while found_count < count: + try: + chunk = sock.recv(4096) + if not chunk: + raise RuntimeError( + f"Connection closed after {found_count}/{count} signals. " + f"Last 200 bytes: {buffer[-200:]!r}" + ) + buffer += chunk + # Count occurrences in entire buffer + found_count = buffer.count(signal_pattern) + except socket.timeout: + raise RuntimeError( + f"Timeout waiting for {count} signals (found {found_count}). " + f"Last 200 bytes: {buffer[-200:]!r}" + ) + + return buffer + + +@contextmanager +def _managed_subprocess(args, timeout=SHORT_TIMEOUT): + """ + Context manager for subprocess lifecycle management. + + Ensures process is properly terminated and cleaned up even on exceptions. + Uses graceful termination first, then forceful kill if needed. + """ + p = subprocess.Popen(args) + try: + yield p + finally: + try: + p.terminate() + try: + p.wait(timeout=timeout) + except subprocess.TimeoutExpired: + p.kill() + try: + p.wait(timeout=timeout) + except subprocess.TimeoutExpired: + pass # Process refuses to die, nothing more we can do + except OSError: + pass # Process already dead + + +def _cleanup_sockets(*sockets): + """Safely close multiple sockets, ignoring errors.""" + for sock in sockets: + if sock is not None: + try: + sock.close() + except OSError: + pass + + +# ============================================================================ +# Decorators and skip conditions +# ============================================================================ + skip_if_not_supported = unittest.skipIf( ( sys.platform != "darwin" @@ -47,36 +213,244 @@ def _make_test_script(script_dir, script_basename, source): ) +def requires_subinterpreters(meth): + """Decorator to skip a test if subinterpreters are not supported.""" + return unittest.skipIf(interpreters is None, "subinterpreters required")( + meth + ) + + +# ============================================================================ +# Simple wrapper functions for RemoteUnwinder +# ============================================================================ + def get_stack_trace(pid): - unwinder = RemoteUnwinder(pid, all_threads=True, debug=True) - return unwinder.get_stack_trace() + for _ in busy_retry(SHORT_TIMEOUT): + try: + unwinder = RemoteUnwinder(pid, all_threads=True, debug=True) + return unwinder.get_stack_trace() + except RuntimeError as e: + continue + raise RuntimeError("Failed to get stack trace after retries") def get_async_stack_trace(pid): - unwinder = RemoteUnwinder(pid, debug=True) - return unwinder.get_async_stack_trace() + for _ in busy_retry(SHORT_TIMEOUT): + try: + unwinder = RemoteUnwinder(pid, debug=True) + return unwinder.get_async_stack_trace() + except RuntimeError as e: + continue + raise RuntimeError("Failed to get async stack trace after retries") def get_all_awaited_by(pid): - unwinder = RemoteUnwinder(pid, debug=True) - return unwinder.get_all_awaited_by() + for _ in busy_retry(SHORT_TIMEOUT): + try: + unwinder = RemoteUnwinder(pid, debug=True) + return unwinder.get_all_awaited_by() + except RuntimeError as e: + continue + raise RuntimeError("Failed to get all awaited_by after retries") + + +def _get_stack_trace_with_retry(unwinder, timeout=SHORT_TIMEOUT, condition=None): + """Get stack trace from an existing unwinder with retry for transient errors. + + This handles the case where we want to reuse an existing RemoteUnwinder + instance but still handle transient failures like "Failed to parse initial + frame in chain" that can occur when sampling at an inopportune moment. + If condition is provided, keeps retrying until condition(traces) is True. + """ + last_error = None + for _ in busy_retry(timeout): + try: + traces = unwinder.get_stack_trace() + if condition is None or condition(traces): + return traces + # Condition not met yet, keep retrying + except TRANSIENT_ERRORS as e: + last_error = e + continue + if last_error: + raise RuntimeError( + f"Failed to get stack trace after retries: {last_error}" + ) + raise RuntimeError("Condition never satisfied within timeout") + + +# ============================================================================ +# Base test class with shared infrastructure +# ============================================================================ + +class RemoteInspectionTestBase(unittest.TestCase): + """Base class for remote inspection tests with common helpers.""" -class TestGetStackTrace(unittest.TestCase): maxDiff = None + def _run_script_and_get_trace( + self, + script, + trace_func, + wait_for_signals=None, + port=None, + backlog=1, + ): + """ + Common pattern: run a script, wait for signals, get trace. + + Args: + script: Script content (will be formatted with port if {port} present) + trace_func: Function to call with pid to get trace (e.g., get_stack_trace) + wait_for_signals: Signal(s) to wait for before getting trace + port: Port to use (auto-selected if None) + backlog: Socket listen backlog + + Returns: + tuple: (trace_result, script_name) + """ + if port is None: + port = find_unused_port() + + # Format script with port if needed + if "{port}" in script or "{{port}}" in script: + script = script.replace("{{port}}", "{port}").format(port=port) + + with os_helper.temp_dir() as work_dir: + script_dir = os.path.join(work_dir, "script_pkg") + os.mkdir(script_dir) + + server_socket = _create_server_socket(port, backlog) + script_name = _make_test_script(script_dir, "script", script) + client_socket = None + + try: + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None + + if wait_for_signals: + _wait_for_signal(client_socket, wait_for_signals) + + trace = trace_func(p.pid) + return trace, script_name + finally: + _cleanup_sockets(client_socket, server_socket) + + def _find_frame_in_trace(self, stack_trace, predicate): + """ + Find a frame matching predicate in stack trace. + + Args: + stack_trace: List of InterpreterInfo objects + predicate: Function(frame) -> bool + + Returns: + FrameInfo or None + """ + for interpreter_info in stack_trace: + for thread_info in interpreter_info.threads: + for frame in thread_info.frame_info: + if predicate(frame): + return frame + return None + + def _find_thread_by_id(self, stack_trace, thread_id): + """Find a thread by its native thread ID.""" + for interpreter_info in stack_trace: + for thread_info in interpreter_info.threads: + if thread_info.thread_id == thread_id: + return thread_info + return None + + def _find_thread_with_frame(self, stack_trace, frame_predicate): + """Find a thread containing a frame matching predicate.""" + for interpreter_info in stack_trace: + for thread_info in interpreter_info.threads: + for frame in thread_info.frame_info: + if frame_predicate(frame): + return thread_info + return None + + def _get_thread_statuses(self, stack_trace): + """Extract thread_id -> status mapping from stack trace.""" + statuses = {} + for interpreter_info in stack_trace: + for thread_info in interpreter_info.threads: + statuses[thread_info.thread_id] = thread_info.status + return statuses + + def _get_task_id_map(self, stack_trace): + """Create task_id -> task mapping from async stack trace.""" + return {task.task_id: task for task in stack_trace[0].awaited_by} + + def _get_awaited_by_relationships(self, stack_trace): + """Extract task name to awaited_by set mapping.""" + id_to_task = self._get_task_id_map(stack_trace) + return { + task.task_name: set( + id_to_task[awaited.task_name].task_name + for awaited in task.awaited_by + ) + for task in stack_trace[0].awaited_by + } + + def _extract_coroutine_stacks(self, stack_trace): + """Extract and format coroutine stacks from tasks.""" + return { + task.task_name: sorted( + tuple(tuple(frame) for frame in coro.call_stack) + for coro in task.coroutine_stack + ) + for task in stack_trace[0].awaited_by + } + + @staticmethod + def _frame_to_lineno_tuple(frame): + """Convert frame to (filename, lineno, funcname, opcode) tuple. + + This extracts just the line number from the location, ignoring column + offsets which can vary due to sampling timing (e.g., when two statements + are on the same line, the sample might catch either one). + """ + filename, location, funcname, opcode = frame + return (filename, location.lineno, funcname, opcode) + + def _extract_coroutine_stacks_lineno_only(self, stack_trace): + """Extract coroutine stacks with line numbers only (no column offsets). + + Use this for tests where sampling timing can cause column offset + variations (e.g., 'expr1; expr2' on the same line). + """ + return { + task.task_name: sorted( + tuple(self._frame_to_lineno_tuple(frame) for frame in coro.call_stack) + for coro in task.coroutine_stack + ) + for task in stack_trace[0].awaited_by + } + + +# ============================================================================ +# Test classes +# ============================================================================ + + +@requires_remote_subprocess_debugging() +class TestGetStackTrace(RemoteInspectionTestBase): @skip_if_not_supported @unittest.skipIf( sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, "Test only runs on Linux with process_vm_readv support", ) def test_remote_stack_trace(self): - # Spawn a process with some realistic Python code port = find_unused_port() script = textwrap.dedent( f"""\ import time, sys, socket, threading - # Connect to the test process + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', {port})) @@ -89,66 +463,170 @@ def baz(): foo() def foo(): - sock.sendall(b"ready:thread\\n"); time.sleep(10_000) # same line number + sock.sendall(b"ready:thread\\n"); time.sleep(10_000) t = threading.Thread(target=bar) t.start() - sock.sendall(b"ready:main\\n"); t.join() # same line number + sock.sendall(b"ready:main\\n"); t.join() """ ) - stack_trace = None + with os_helper.temp_dir() as work_dir: script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) - # Create a socket server to communicate with the target process - server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - server_socket.bind(("localhost", port)) - server_socket.settimeout(SHORT_TIMEOUT) - server_socket.listen(1) - + server_socket = _create_server_socket(port) script_name = _make_test_script(script_dir, "script", script) client_socket = None + + try: + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None + + _wait_for_signal( + client_socket, [b"ready:main", b"ready:thread"] + ) + + stack_trace = get_stack_trace(p.pid) + + # Find expected thread stack by funcname + found_thread = self._find_thread_with_frame( + stack_trace, + lambda f: f.funcname == "foo" and f.location.lineno == 15, + ) + self.assertIsNotNone( + found_thread, "Expected thread stack trace not found" + ) + # Check the funcnames in order + funcnames = [f.funcname for f in found_thread.frame_info] + self.assertEqual( + funcnames[:6], + ["foo", "baz", "bar", "Thread.run", "Thread._bootstrap_inner", "Thread._bootstrap"] + ) + + # Check main thread + found_main = self._find_frame_in_trace( + stack_trace, + lambda f: f.funcname == "" and f.location.lineno == 19, + ) + self.assertIsNotNone( + found_main, "Main thread stack trace not found" + ) + finally: + _cleanup_sockets(client_socket, server_socket) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_self_trace_after_ctypes_import(self): + """Test that RemoteUnwinder works on the same process after _ctypes import. + + When _ctypes is imported, it may call dlopen on the libpython shared + library, creating a duplicate mapping in the process address space. + The remote debugging code must skip these uninitialized duplicate + mappings and find the real PyRuntime. See gh-144563. + """ + + # Skip the test if the _ctypes module is missing. + import_module("_ctypes") + + # Run the test in a subprocess to avoid side effects + script = textwrap.dedent("""\ + import os + import _remote_debugging + + # Should work before _ctypes import + unwinder = _remote_debugging.RemoteUnwinder(os.getpid()) + + import _ctypes + + # Should still work after _ctypes import (gh-144563) + unwinder = _remote_debugging.RemoteUnwinder(os.getpid()) + """) + + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + timeout=SHORT_TIMEOUT, + ) + self.assertEqual( + result.returncode, 0, + f"stdout: {result.stdout}\nstderr: {result.stderr}" + ) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_remote_stack_trace_non_ascii_names(self): + # Exercise each PyUnicode kind (1-byte non-ASCII, 2-byte BMP, + # 4-byte non-BMP) for both the filename and the function name + # reported in the stack trace. + latin1 = "zażółć" # 1-byte non-ASCII (forces non-ASCII path) + bmp = "λάμβδα" # 2-byte BMP + astral = "𐌀𐌁𐌂𐌃" # 4-byte non-BMP (Old Italic; XID, no NFKC fold) + func_name = f"{latin1}_{bmp}_{astral}" + script_basename = f"mod_{latin1}_{bmp}_{astral}" + + port = find_unused_port() + script = textwrap.dedent( + f"""\ + import socket + import time + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + + def {func_name}(): + sock.sendall(b"ready") + time.sleep(10_000) + + {func_name}() + """ + ) + with os_helper.temp_dir() as work_dir: + script_dir = os.path.join(work_dir, "script_pkg") + os.mkdir(script_dir) + script_name = _make_test_script(script_dir, script_basename, script) + + server_socket = _create_server_socket(port) + client_socket = None try: p = subprocess.Popen([sys.executable, script_name]) client_socket, _ = server_socket.accept() server_socket.close() - response = b"" - while ( - b"ready:main" not in response - or b"ready:thread" not in response - ): - response += client_socket.recv(1024) + _wait_for_signal(client_socket, b"ready") + stack_trace = get_stack_trace(p.pid) except PermissionError: - self.skipTest( - "Insufficient permissions to read the stack trace" - ) + self.skipTest("Insufficient permissions to read the stack trace") finally: if client_socket is not None: client_socket.close() p.kill() - p.terminate() p.wait(timeout=SHORT_TIMEOUT) - thread_expected_stack_trace = [ - FrameInfo([script_name, 15, "foo"]), - FrameInfo([script_name, 12, "baz"]), - FrameInfo([script_name, 9, "bar"]), - FrameInfo([threading.__file__, ANY, "Thread.run"]), + frames = [ + frame + for interp in stack_trace + for thread in interp.threads + for frame in thread.frame_info ] - # Is possible that there are more threads, so we check that the - # expected stack traces are in the result (looking at you Windows!) - self.assertIn((ANY, thread_expected_stack_trace), stack_trace) - - # Check that the main thread stack trace is in the result - frame = FrameInfo([script_name, 19, ""]) - for _, stack in stack_trace: - if frame in stack: - break - else: - self.fail("Main thread stack trace not found in result") + target = next( + (f for f in frames if f.funcname == func_name), None + ) + self.assertIsNotNone( + target, + f"Frame for {func_name!r} missing; got " + f"{[(f.filename, f.funcname) for f in frames]}", + ) + self.assertEqual(target.filename, script_name) @skip_if_not_supported @unittest.skipIf( @@ -156,7 +634,6 @@ def foo(): "Test only runs on Linux with process_vm_readv support", ) def test_async_remote_stack_trace(self): - # Spawn a process with some realistic Python code port = find_unused_port() script = textwrap.dedent( f"""\ @@ -164,12 +641,12 @@ def test_async_remote_stack_trace(self): import time import sys import socket - # Connect to the test process + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', {port})) def c5(): - sock.sendall(b"ready"); time.sleep(10_000) # same line number + sock.sendall(b"ready"); time.sleep(10_000) async def c4(): await asyncio.sleep(0) @@ -200,7 +677,7 @@ def new_eager_loop(): asyncio.run(main(), loop_factory={{TASK_FACTORY}}) """ ) - stack_trace = None + for task_factory_variant in "asyncio.new_event_loop", "new_eager_loop": with ( self.subTest(task_factory_variant=task_factory_variant), @@ -208,195 +685,152 @@ def new_eager_loop(): ): script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) - server_socket = socket.socket( - socket.AF_INET, socket.SOCK_STREAM - ) - server_socket.setsockopt( - socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 - ) - server_socket.bind(("localhost", port)) - server_socket.settimeout(SHORT_TIMEOUT) - server_socket.listen(1) + + server_socket = _create_server_socket(port) script_name = _make_test_script( script_dir, "script", script.format(TASK_FACTORY=task_factory_variant), ) client_socket = None - try: - p = subprocess.Popen([sys.executable, script_name]) - client_socket, _ = server_socket.accept() - server_socket.close() - response = client_socket.recv(1024) - self.assertEqual(response, b"ready") - stack_trace = get_async_stack_trace(p.pid) - except PermissionError: - self.skipTest( - "Insufficient permissions to read the stack trace" - ) - finally: - if client_socket is not None: - client_socket.close() - p.kill() - p.terminate() - p.wait(timeout=SHORT_TIMEOUT) - - # First check all the tasks are present - tasks_names = [ - task.task_name for task in stack_trace[0].awaited_by - ] - for task_name in ["c2_root", "sub_main_1", "sub_main_2"]: - self.assertIn(task_name, tasks_names) - - # Now ensure that the awaited_by_relationships are correct - id_to_task = { - task.task_id: task for task in stack_trace[0].awaited_by - } - task_name_to_awaited_by = { - task.task_name: set( - id_to_task[awaited.task_name].task_name - for awaited in task.awaited_by - ) - for task in stack_trace[0].awaited_by - } - self.assertEqual( - task_name_to_awaited_by, - { - "c2_root": {"Task-1", "sub_main_1", "sub_main_2"}, - "Task-1": set(), - "sub_main_1": {"Task-1"}, - "sub_main_2": {"Task-1"}, - }, - ) - # Now ensure that the coroutine stacks are correct - coroutine_stacks = { - task.task_name: sorted( - tuple(tuple(frame) for frame in coro.call_stack) - for coro in task.coroutine_stack - ) - for task in stack_trace[0].awaited_by - } - self.assertEqual( - coroutine_stacks, - { - "Task-1": [ - ( - tuple( - [ - taskgroups.__file__, - ANY, - "TaskGroup._aexit", - ] - ), - tuple( - [ - taskgroups.__file__, - ANY, - "TaskGroup.__aexit__", - ] - ), - tuple([script_name, 26, "main"]), - ) - ], - "c2_root": [ - ( - tuple([script_name, 10, "c5"]), - tuple([script_name, 14, "c4"]), - tuple([script_name, 17, "c3"]), - tuple([script_name, 20, "c2"]), - ) - ], - "sub_main_1": [(tuple([script_name, 23, "c1"]),)], - "sub_main_2": [(tuple([script_name, 23, "c1"]),)], - }, - ) + try: + with _managed_subprocess( + [sys.executable, script_name] + ) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None + + response = _wait_for_signal(client_socket, b"ready") + self.assertIn(b"ready", response) + + stack_trace = get_async_stack_trace(p.pid) + + # Check all tasks are present + tasks_names = [ + task.task_name + for task in stack_trace[0].awaited_by + ] + for task_name in [ + "c2_root", + "sub_main_1", + "sub_main_2", + ]: + self.assertIn(task_name, tasks_names) + + # Check awaited_by relationships + relationships = self._get_awaited_by_relationships( + stack_trace + ) + self.assertEqual( + relationships, + { + "c2_root": { + "Task-1", + "sub_main_1", + "sub_main_2", + }, + "Task-1": set(), + "sub_main_1": {"Task-1"}, + "sub_main_2": {"Task-1"}, + }, + ) - # Now ensure the coroutine stacks for the awaited_by relationships are correct. - awaited_by_coroutine_stacks = { - task.task_name: sorted( - ( - id_to_task[coro.task_name].task_name, - tuple(tuple(frame) for frame in coro.call_stack), + # Check coroutine stacks (using line numbers only to avoid + # flakiness from column offset variations when sampling + # catches different statements on the same line) + coroutine_stacks = self._extract_coroutine_stacks_lineno_only( + stack_trace ) - for coro in task.awaited_by - ) - for task in stack_trace[0].awaited_by - } - self.assertEqual( - awaited_by_coroutine_stacks, - { - "Task-1": [], - "c2_root": [ - ( - "Task-1", - ( - tuple( - [ - taskgroups.__file__, - ANY, - "TaskGroup._aexit", - ] - ), - tuple( - [ - taskgroups.__file__, - ANY, - "TaskGroup.__aexit__", - ] - ), - tuple([script_name, 26, "main"]), - ), - ), - ("sub_main_1", (tuple([script_name, 23, "c1"]),)), - ("sub_main_2", (tuple([script_name, 23, "c1"]),)), - ], - "sub_main_1": [ - ( - "Task-1", + self.assertEqual( + coroutine_stacks, + { + "Task-1": [ + ( + (taskgroups.__file__, ANY, "TaskGroup._aexit", None), + (taskgroups.__file__, ANY, "TaskGroup.__aexit__", None), + (script_name, 26, "main", None), + ) + ], + "c2_root": [ + ( + (script_name, 10, "c5", None), + (script_name, 14, "c4", None), + (script_name, 17, "c3", None), + (script_name, 20, "c2", None), + ) + ], + "sub_main_1": [ + ((script_name, 23, "c1", None),) + ], + "sub_main_2": [ + ((script_name, 23, "c1", None),) + ], + }, + ) + + # Check awaited_by coroutine stacks (line numbers only) + id_to_task = self._get_task_id_map(stack_trace) + awaited_by_coroutine_stacks = { + task.task_name: sorted( ( + id_to_task[coro.task_name].task_name, tuple( - [ - taskgroups.__file__, - ANY, - "TaskGroup._aexit", - ] - ), - tuple( - [ - taskgroups.__file__, - ANY, - "TaskGroup.__aexit__", - ] + self._frame_to_lineno_tuple(frame) + for frame in coro.call_stack ), - tuple([script_name, 26, "main"]), - ), + ) + for coro in task.awaited_by ) - ], - "sub_main_2": [ - ( - "Task-1", - ( - tuple( - [ - taskgroups.__file__, - ANY, - "TaskGroup._aexit", - ] + for task in stack_trace[0].awaited_by + } + self.assertEqual( + awaited_by_coroutine_stacks, + { + "Task-1": [], + "c2_root": [ + ( + "Task-1", + ( + (taskgroups.__file__, ANY, "TaskGroup._aexit", None), + (taskgroups.__file__, ANY, "TaskGroup.__aexit__", None), + (script_name, 26, "main", None), + ), ), - tuple( - [ - taskgroups.__file__, - ANY, - "TaskGroup.__aexit__", - ] + ( + "sub_main_1", + ((script_name, 23, "c1", None),), ), - tuple([script_name, 26, "main"]), - ), - ) - ], - }, - ) + ( + "sub_main_2", + ((script_name, 23, "c1", None),), + ), + ], + "sub_main_1": [ + ( + "Task-1", + ( + (taskgroups.__file__, ANY, "TaskGroup._aexit", None), + (taskgroups.__file__, ANY, "TaskGroup.__aexit__", None), + (script_name, 26, "main", None), + ), + ) + ], + "sub_main_2": [ + ( + "Task-1", + ( + (taskgroups.__file__, ANY, "TaskGroup._aexit", None), + (taskgroups.__file__, ANY, "TaskGroup.__aexit__", None), + (script_name, 26, "main", None), + ), + ) + ], + }, + ) + finally: + _cleanup_sockets(client_socket, server_socket) @skip_if_not_supported @unittest.skipIf( @@ -404,7 +838,6 @@ def new_eager_loop(): "Test only runs on Linux with process_vm_readv support", ) def test_asyncgen_remote_stack_trace(self): - # Spawn a process with some realistic Python code port = find_unused_port() script = textwrap.dedent( f"""\ @@ -412,12 +845,12 @@ def test_asyncgen_remote_stack_trace(self): import time import sys import socket - # Connect to the test process + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', {port})) async def gen_nested_call(): - sock.sendall(b"ready"); time.sleep(10_000) # same line number + sock.sendall(b"ready"); time.sleep(10_000) async def gen(): for num in range(2): @@ -432,59 +865,53 @@ async def main(): asyncio.run(main()) """ ) - stack_trace = None + with os_helper.temp_dir() as work_dir: script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) - # Create a socket server to communicate with the target process - server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - server_socket.bind(("localhost", port)) - server_socket.settimeout(SHORT_TIMEOUT) - server_socket.listen(1) + + server_socket = _create_server_socket(port) script_name = _make_test_script(script_dir, "script", script) client_socket = None + try: - p = subprocess.Popen([sys.executable, script_name]) - client_socket, _ = server_socket.accept() - server_socket.close() - response = client_socket.recv(1024) - self.assertEqual(response, b"ready") - stack_trace = get_async_stack_trace(p.pid) - except PermissionError: - self.skipTest( - "Insufficient permissions to read the stack trace" - ) - finally: - if client_socket is not None: - client_socket.close() - p.kill() - p.terminate() - p.wait(timeout=SHORT_TIMEOUT) + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None - # For this simple asyncgen test, we only expect one task with the full coroutine stack - self.assertEqual(len(stack_trace[0].awaited_by), 1) - task = stack_trace[0].awaited_by[0] - self.assertEqual(task.task_name, "Task-1") + response = _wait_for_signal(client_socket, b"ready") + self.assertIn(b"ready", response) - # Check the coroutine stack - based on actual output, only shows main - coroutine_stack = sorted( - tuple(tuple(frame) for frame in coro.call_stack) - for coro in task.coroutine_stack - ) - self.assertEqual( - coroutine_stack, - [ - ( - tuple([script_name, 10, "gen_nested_call"]), - tuple([script_name, 16, "gen"]), - tuple([script_name, 19, "main"]), + stack_trace = get_async_stack_trace(p.pid) + + # For this simple asyncgen test, we only expect one task + self.assertEqual(len(stack_trace[0].awaited_by), 1) + task = stack_trace[0].awaited_by[0] + self.assertEqual(task.task_name, "Task-1") + + # Check the coroutine stack (using line numbers only to avoid + # flakiness from column offset variations when sampling + # catches different statements on the same line) + coroutine_stack = sorted( + tuple(self._frame_to_lineno_tuple(frame) for frame in coro.call_stack) + for coro in task.coroutine_stack + ) + self.assertEqual( + coroutine_stack, + [ + ( + (script_name, 10, "gen_nested_call", None), + (script_name, 16, "gen", None), + (script_name, 19, "main", None), + ) + ], ) - ], - ) - # No awaited_by relationships expected for this simple case - self.assertEqual(task.awaited_by, []) + # No awaited_by relationships expected + self.assertEqual(task.awaited_by, []) + finally: + _cleanup_sockets(client_socket, server_socket) @skip_if_not_supported @unittest.skipIf( @@ -492,7 +919,6 @@ async def main(): "Test only runs on Linux with process_vm_readv support", ) def test_async_gather_remote_stack_trace(self): - # Spawn a process with some realistic Python code port = find_unused_port() script = textwrap.dedent( f"""\ @@ -500,13 +926,13 @@ def test_async_gather_remote_stack_trace(self): import time import sys import socket - # Connect to the test process + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', {port})) async def deep(): await asyncio.sleep(0) - sock.sendall(b"ready"); time.sleep(10_000) # same line number + sock.sendall(b"ready"); time.sleep(10_000) async def c1(): await asyncio.sleep(0) @@ -521,103 +947,89 @@ async def main(): asyncio.run(main()) """ ) - stack_trace = None + with os_helper.temp_dir() as work_dir: script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) - # Create a socket server to communicate with the target process - server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - server_socket.bind(("localhost", port)) - server_socket.settimeout(SHORT_TIMEOUT) - server_socket.listen(1) + + server_socket = _create_server_socket(port) script_name = _make_test_script(script_dir, "script", script) client_socket = None + try: - p = subprocess.Popen([sys.executable, script_name]) - client_socket, _ = server_socket.accept() - server_socket.close() - response = client_socket.recv(1024) - self.assertEqual(response, b"ready") - stack_trace = get_async_stack_trace(p.pid) - except PermissionError: - self.skipTest( - "Insufficient permissions to read the stack trace" - ) - finally: - if client_socket is not None: - client_socket.close() - p.kill() - p.terminate() - p.wait(timeout=SHORT_TIMEOUT) + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None - # First check all the tasks are present - tasks_names = [ - task.task_name for task in stack_trace[0].awaited_by - ] - for task_name in ["Task-1", "Task-2"]: - self.assertIn(task_name, tasks_names) + response = _wait_for_signal(client_socket, b"ready") + self.assertIn(b"ready", response) - # Now ensure that the awaited_by_relationships are correct - id_to_task = { - task.task_id: task for task in stack_trace[0].awaited_by - } - task_name_to_awaited_by = { - task.task_name: set( - id_to_task[awaited.task_name].task_name - for awaited in task.awaited_by - ) - for task in stack_trace[0].awaited_by - } - self.assertEqual( - task_name_to_awaited_by, - { - "Task-1": set(), - "Task-2": {"Task-1"}, - }, - ) + stack_trace = get_async_stack_trace(p.pid) - # Now ensure that the coroutine stacks are correct - coroutine_stacks = { - task.task_name: sorted( - tuple(tuple(frame) for frame in coro.call_stack) - for coro in task.coroutine_stack - ) - for task in stack_trace[0].awaited_by - } - self.assertEqual( - coroutine_stacks, - { - "Task-1": [(tuple([script_name, 21, "main"]),)], - "Task-2": [ - ( - tuple([script_name, 11, "deep"]), - tuple([script_name, 15, "c1"]), - ) - ], - }, - ) + # Check all tasks are present + tasks_names = [ + task.task_name for task in stack_trace[0].awaited_by + ] + for task_name in ["Task-1", "Task-2"]: + self.assertIn(task_name, tasks_names) - # Now ensure the coroutine stacks for the awaited_by relationships are correct. - awaited_by_coroutine_stacks = { - task.task_name: sorted( - ( - id_to_task[coro.task_name].task_name, - tuple(tuple(frame) for frame in coro.call_stack), + # Check awaited_by relationships + relationships = self._get_awaited_by_relationships( + stack_trace ) - for coro in task.awaited_by - ) - for task in stack_trace[0].awaited_by - } - self.assertEqual( - awaited_by_coroutine_stacks, - { - "Task-1": [], - "Task-2": [ - ("Task-1", (tuple([script_name, 21, "main"]),)) - ], - }, - ) + self.assertEqual( + relationships, + { + "Task-1": set(), + "Task-2": {"Task-1"}, + }, + ) + + # Check coroutine stacks (using line numbers only to avoid + # flakiness from column offset variations when sampling + # catches different statements on the same line) + coroutine_stacks = self._extract_coroutine_stacks_lineno_only( + stack_trace + ) + self.assertEqual( + coroutine_stacks, + { + "Task-1": [((script_name, 21, "main", None),)], + "Task-2": [ + ( + (script_name, 11, "deep", None), + (script_name, 15, "c1", None), + ) + ], + }, + ) + + # Check awaited_by coroutine stacks (line numbers only) + id_to_task = self._get_task_id_map(stack_trace) + awaited_by_coroutine_stacks = { + task.task_name: sorted( + ( + id_to_task[coro.task_name].task_name, + tuple( + self._frame_to_lineno_tuple(frame) for frame in coro.call_stack + ), + ) + for coro in task.awaited_by + ) + for task in stack_trace[0].awaited_by + } + self.assertEqual( + awaited_by_coroutine_stacks, + { + "Task-1": [], + "Task-2": [ + ("Task-1", ((script_name, 21, "main", None),)) + ], + }, + ) + finally: + _cleanup_sockets(client_socket, server_socket) @skip_if_not_supported @unittest.skipIf( @@ -625,7 +1037,6 @@ async def main(): "Test only runs on Linux with process_vm_readv support", ) def test_async_staggered_race_remote_stack_trace(self): - # Spawn a process with some realistic Python code port = find_unused_port() script = textwrap.dedent( f"""\ @@ -633,13 +1044,13 @@ def test_async_staggered_race_remote_stack_trace(self): import time import sys import socket - # Connect to the test process + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', {port})) async def deep(): await asyncio.sleep(0) - sock.sendall(b"ready"); time.sleep(10_000) # same line number + sock.sendall(b"ready"); time.sleep(10_000) async def c1(): await asyncio.sleep(0) @@ -657,123 +1068,101 @@ async def main(): asyncio.run(main()) """ ) - stack_trace = None + with os_helper.temp_dir() as work_dir: script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) - # Create a socket server to communicate with the target process - server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - server_socket.bind(("localhost", port)) - server_socket.settimeout(SHORT_TIMEOUT) - server_socket.listen(1) + + server_socket = _create_server_socket(port) script_name = _make_test_script(script_dir, "script", script) client_socket = None + try: - p = subprocess.Popen([sys.executable, script_name]) - client_socket, _ = server_socket.accept() - server_socket.close() - response = client_socket.recv(1024) - self.assertEqual(response, b"ready") - stack_trace = get_async_stack_trace(p.pid) - except PermissionError: - self.skipTest( - "Insufficient permissions to read the stack trace" - ) - finally: - if client_socket is not None: - client_socket.close() - p.kill() - p.terminate() - p.wait(timeout=SHORT_TIMEOUT) + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None - # First check all the tasks are present - tasks_names = [ - task.task_name for task in stack_trace[0].awaited_by - ] - for task_name in ["Task-1", "Task-2"]: - self.assertIn(task_name, tasks_names) + response = _wait_for_signal(client_socket, b"ready") + self.assertIn(b"ready", response) - # Now ensure that the awaited_by_relationships are correct - id_to_task = { - task.task_id: task for task in stack_trace[0].awaited_by - } - task_name_to_awaited_by = { - task.task_name: set( - id_to_task[awaited.task_name].task_name - for awaited in task.awaited_by - ) - for task in stack_trace[0].awaited_by - } - self.assertEqual( - task_name_to_awaited_by, - { - "Task-1": set(), - "Task-2": {"Task-1"}, - }, - ) + stack_trace = get_async_stack_trace(p.pid) - # Now ensure that the coroutine stacks are correct - coroutine_stacks = { - task.task_name: sorted( - tuple(tuple(frame) for frame in coro.call_stack) - for coro in task.coroutine_stack - ) - for task in stack_trace[0].awaited_by - } - self.assertEqual( - coroutine_stacks, - { - "Task-1": [ - ( - tuple([staggered.__file__, ANY, "staggered_race"]), - tuple([script_name, 21, "main"]), - ) - ], - "Task-2": [ - ( - tuple([script_name, 11, "deep"]), - tuple([script_name, 15, "c1"]), - tuple( - [ - staggered.__file__, - ANY, - "staggered_race..run_one_coro", - ] - ), - ) - ], - }, - ) + # Check all tasks are present + tasks_names = [ + task.task_name for task in stack_trace[0].awaited_by + ] + for task_name in ["Task-1", "Task-2"]: + self.assertIn(task_name, tasks_names) - # Now ensure the coroutine stacks for the awaited_by relationships are correct. - awaited_by_coroutine_stacks = { - task.task_name: sorted( - ( - id_to_task[coro.task_name].task_name, - tuple(tuple(frame) for frame in coro.call_stack), + # Check awaited_by relationships + relationships = self._get_awaited_by_relationships( + stack_trace ) - for coro in task.awaited_by - ) - for task in stack_trace[0].awaited_by - } - self.assertEqual( - awaited_by_coroutine_stacks, - { - "Task-1": [], - "Task-2": [ - ( - "Task-1", + self.assertEqual( + relationships, + { + "Task-1": set(), + "Task-2": {"Task-1"}, + }, + ) + + # Check coroutine stacks (using line numbers only to avoid + # flakiness from column offset variations when sampling + # catches different statements on the same line) + coroutine_stacks = self._extract_coroutine_stacks_lineno_only( + stack_trace + ) + self.assertEqual( + coroutine_stacks, + { + "Task-1": [ + ( + (staggered.__file__, ANY, "staggered_race", None), + (script_name, 21, "main", None), + ) + ], + "Task-2": [ + ( + (script_name, 11, "deep", None), + (script_name, 15, "c1", None), + (staggered.__file__, ANY, "staggered_race..run_one_coro", None), + ) + ], + }, + ) + + # Check awaited_by coroutine stacks (line numbers only) + id_to_task = self._get_task_id_map(stack_trace) + awaited_by_coroutine_stacks = { + task.task_name: sorted( ( + id_to_task[coro.task_name].task_name, tuple( - [staggered.__file__, ANY, "staggered_race"] + self._frame_to_lineno_tuple(frame) for frame in coro.call_stack ), - tuple([script_name, 21, "main"]), - ), + ) + for coro in task.awaited_by ) - ], - }, - ) + for task in stack_trace[0].awaited_by + } + self.assertEqual( + awaited_by_coroutine_stacks, + { + "Task-1": [], + "Task-2": [ + ( + "Task-1", + ( + (staggered.__file__, ANY, "staggered_race", None), + (script_name, 21, "main", None), + ), + ) + ], + }, + ) + finally: + _cleanup_sockets(client_socket, server_socket) @skip_if_not_supported @unittest.skipIf( @@ -781,6 +1170,10 @@ async def main(): "Test only runs on Linux with process_vm_readv support", ) def test_async_global_awaited_by(self): + # Reduced from 1000 to 100 to avoid file descriptor exhaustion + # when running tests in parallel (e.g., -j 20) + NUM_TASKS = 100 + port = find_unused_port() script = textwrap.dedent( f"""\ @@ -796,7 +1189,6 @@ def test_async_global_awaited_by(self): PORT = socket_helper.find_unused_port() connections = 0 - # Connect to the test process sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', {port})) @@ -819,23 +1211,16 @@ async def echo_client(message): assert message == data.decode() writer.close() await writer.wait_closed() - # Signal we are ready to sleep sock.sendall(b"ready") await asyncio.sleep(SHORT_TIMEOUT) async def echo_client_spam(server): async with asyncio.TaskGroup() as tg: - while connections < 1000: + while connections < {NUM_TASKS}: msg = list(ascii_lowercase + digits) random.shuffle(msg) tg.create_task(echo_client("".join(msg))) await asyncio.sleep(0) - # at least a 1000 tasks created. Each task will signal - # when is ready to avoid the race caused by the fact that - # tasks are waited on tg.__exit__ and we cannot signal when - # that happens otherwise - # at this point all client tasks completed without assertion errors - # let's wrap up the test server.close() await server.wait_closed() @@ -850,323 +1235,323 @@ async def main(): asyncio.run(main()) """ ) - stack_trace = None + with os_helper.temp_dir() as work_dir: script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) - # Create a socket server to communicate with the target process - server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - server_socket.bind(("localhost", port)) - server_socket.settimeout(SHORT_TIMEOUT) - server_socket.listen(1) + + server_socket = _create_server_socket(port) script_name = _make_test_script(script_dir, "script", script) client_socket = None + try: - p = subprocess.Popen([sys.executable, script_name]) - client_socket, _ = server_socket.accept() - server_socket.close() - for _ in range(1000): - expected_response = b"ready" - response = client_socket.recv(len(expected_response)) - self.assertEqual(response, expected_response) - for _ in busy_retry(SHORT_TIMEOUT): + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None + + # Wait for NUM_TASKS "ready" signals try: - all_awaited_by = get_all_awaited_by(p.pid) - except RuntimeError as re: - # This call reads a linked list in another process with - # no synchronization. That occasionally leads to invalid - # reads. Here we avoid making the test flaky. - msg = str(re) - if msg.startswith("Task list appears corrupted"): - continue - elif msg.startswith( - "Invalid linked list structure reading remote memory" - ): - continue - elif msg.startswith("Unknown error reading memory"): - continue - elif msg.startswith("Unhandled frame owner"): - continue - raise # Unrecognized exception, safest not to ignore it - else: - break - # expected: a list of two elements: 1 thread, 1 interp - self.assertEqual(len(all_awaited_by), 2) - # expected: a tuple with the thread ID and the awaited_by list - self.assertEqual(len(all_awaited_by[0]), 2) - # expected: no tasks in the fallback per-interp task list - self.assertEqual(all_awaited_by[1], (0, [])) - entries = all_awaited_by[0][1] - # expected: at least 1000 pending tasks - self.assertGreaterEqual(len(entries), 1000) - # the first three tasks stem from the code structure - main_stack = [ - FrameInfo([taskgroups.__file__, ANY, "TaskGroup._aexit"]), - FrameInfo( - [taskgroups.__file__, ANY, "TaskGroup.__aexit__"] - ), - FrameInfo([script_name, 60, "main"]), - ] - self.assertIn( - TaskInfo( - [ANY, "Task-1", [CoroInfo([main_stack, ANY])], []] - ), - entries, - ) - self.assertIn( - TaskInfo( - [ - ANY, - "server task", + _wait_for_n_signals(client_socket, b"ready", NUM_TASKS) + except RuntimeError as e: + self.fail(str(e)) + + all_awaited_by = get_all_awaited_by(p.pid) + + # Expected: a list of two elements: 1 thread, 1 interp + self.assertEqual(len(all_awaited_by), 2) + # Expected: a tuple with the thread ID and the awaited_by list + self.assertEqual(len(all_awaited_by[0]), 2) + # Expected: no tasks in the fallback per-interp task list + self.assertEqual(all_awaited_by[1], (0, [])) + + entries = all_awaited_by[0][1] + # Expected: at least NUM_TASKS pending tasks + self.assertGreaterEqual(len(entries), NUM_TASKS) + + # Check the main task structure + main_stack = [ + FrameInfo( + [taskgroups.__file__, ANY, "TaskGroup._aexit", ANY] + ), + FrameInfo( + [taskgroups.__file__, ANY, "TaskGroup.__aexit__", ANY] + ), + FrameInfo([script_name, ANY, "main", ANY]), + ] + self.assertIn( + TaskInfo( + [ANY, "Task-1", [CoroInfo([main_stack, ANY])], []] + ), + entries, + ) + self.assertIn( + TaskInfo( [ - CoroInfo( - [ + ANY, + "server task", + [ + CoroInfo( [ - FrameInfo( - [ - base_events.__file__, - ANY, - "Server.serve_forever", - ] - ) - ], - ANY, - ] - ) - ], - [ - CoroInfo( - [ + [ + FrameInfo( + [ + base_events.__file__, + ANY, + "Server.serve_forever", + ANY, + ] + ) + ], + ANY, + ] + ) + ], + [ + CoroInfo( [ - FrameInfo( - [ - taskgroups.__file__, - ANY, - "TaskGroup._aexit", - ] - ), - FrameInfo( - [ - taskgroups.__file__, - ANY, - "TaskGroup.__aexit__", - ] - ), - FrameInfo( - [script_name, ANY, "main"] - ), - ], - ANY, - ] - ) - ], - ] - ), - entries, - ) - self.assertIn( - TaskInfo( - [ - ANY, - "Task-4", + [ + FrameInfo( + [ + taskgroups.__file__, + ANY, + "TaskGroup._aexit", + ANY, + ] + ), + FrameInfo( + [ + taskgroups.__file__, + ANY, + "TaskGroup.__aexit__", + ANY, + ] + ), + FrameInfo( + [script_name, ANY, "main", ANY] + ), + ], + ANY, + ] + ) + ], + ] + ), + entries, + ) + self.assertIn( + TaskInfo( [ - CoroInfo( - [ + ANY, + "Task-4", + [ + CoroInfo( [ - FrameInfo( - [tasks.__file__, ANY, "sleep"] - ), - FrameInfo( - [ - script_name, - 38, - "echo_client", - ] - ), - ], - ANY, - ] - ) - ], - [ - CoroInfo( - [ + [ + FrameInfo( + [ + tasks.__file__, + ANY, + "sleep", + ANY, + ] + ), + FrameInfo( + [ + script_name, + ANY, + "echo_client", + ANY, + ] + ), + ], + ANY, + ] + ) + ], + [ + CoroInfo( [ - FrameInfo( - [ - taskgroups.__file__, - ANY, - "TaskGroup._aexit", - ] - ), - FrameInfo( - [ - taskgroups.__file__, - ANY, - "TaskGroup.__aexit__", - ] - ), - FrameInfo( - [ - script_name, - 41, - "echo_client_spam", - ] - ), - ], - ANY, - ] - ) - ], - ] - ), - entries, - ) + [ + FrameInfo( + [ + taskgroups.__file__, + ANY, + "TaskGroup._aexit", + ANY, + ] + ), + FrameInfo( + [ + taskgroups.__file__, + ANY, + "TaskGroup.__aexit__", + ANY, + ] + ), + FrameInfo( + [ + script_name, + ANY, + "echo_client_spam", + ANY, + ] + ), + ], + ANY, + ] + ) + ], + ] + ), + entries, + ) - expected_awaited_by = [ - CoroInfo( - [ - [ - FrameInfo( - [ - taskgroups.__file__, - ANY, - "TaskGroup._aexit", - ] - ), - FrameInfo( - [ - taskgroups.__file__, - ANY, - "TaskGroup.__aexit__", - ] - ), - FrameInfo( - [script_name, 41, "echo_client_spam"] - ), - ], - ANY, + # Find tasks awaited by echo_client_spam via TaskGroup + def matches_awaited_by_pattern(task): + if len(task.awaited_by) != 1: + return False + coro = task.awaited_by[0] + if len(coro.call_stack) != 3: + return False + funcnames = [f.funcname for f in coro.call_stack] + return funcnames == [ + "TaskGroup._aexit", + "TaskGroup.__aexit__", + "echo_client_spam", ] - ) - ] - tasks_with_awaited = [ - task - for task in entries - if task.awaited_by == expected_awaited_by - ] - self.assertGreaterEqual(len(tasks_with_awaited), 1000) - - # the final task will have some random number, but it should for - # sure be one of the echo client spam horde (In windows this is not true - # for some reason) - if sys.platform != "win32": - self.assertEqual( - tasks_with_awaited[-1].awaited_by, - entries[-1].awaited_by, - ) - except PermissionError: - self.skipTest( - "Insufficient permissions to read the stack trace" - ) + + tasks_with_awaited = [ + task + for task in entries + if matches_awaited_by_pattern(task) + ] + self.assertGreaterEqual(len(tasks_with_awaited), NUM_TASKS) + + # Final task should be from echo client spam (not on Windows) + if sys.platform != "win32": + self.assertEqual( + tasks_with_awaited[-1].awaited_by, + entries[-1].awaited_by, + ) finally: - if client_socket is not None: - client_socket.close() - p.kill() - p.terminate() - p.wait(timeout=SHORT_TIMEOUT) + _cleanup_sockets(client_socket, server_socket) @skip_if_not_supported @unittest.skipIf( sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, "Test only runs on Linux with process_vm_readv support", ) - def test_self_trace(self): - stack_trace = get_stack_trace(os.getpid()) - # Is possible that there are more threads, so we check that the - # expected stack traces are in the result (looking at you Windows!) - this_tread_stack = None - for thread_id, stack in stack_trace: - if thread_id == threading.get_native_id(): - this_tread_stack = stack - break - self.assertIsNotNone(this_tread_stack) - self.assertEqual( - stack[:2], - [ - FrameInfo( - [ - __file__, - get_stack_trace.__code__.co_firstlineno + 2, - "get_stack_trace", - ] - ), - FrameInfo( - [ - __file__, - self.test_self_trace.__code__.co_firstlineno + 6, - "TestGetStackTrace.test_self_trace", - ] - ), - ], + def test_async_global_awaited_by_from_non_main_thread(self): + port = find_unused_port() + script = textwrap.dedent( + f"""\ + import asyncio + import socket + import threading + import time + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + + async def worker_main(): + task = asyncio.create_task( + asyncio.sleep(10_000), + name="worker task", + ) + await asyncio.sleep(0) + sock.sendall(f"ready:{{threading.get_native_id()}}\\n".encode()) + await task + + def run_worker_loop(): + asyncio.run(worker_main()) + + threading.Thread( + target=run_worker_loop, + name="async-worker", + daemon=True, + ).start() + time.sleep(10_000) + """ ) + with os_helper.temp_dir() as work_dir: + script_dir = os.path.join(work_dir, "script_pkg") + os.mkdir(script_dir) + + server_socket = _create_server_socket(port) + script_name = _make_test_script(script_dir, "script", script) + client_socket = None + + try: + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None + + response = _wait_for_signal(client_socket, b"ready:") + worker_thread_id = int( + response.split(b"ready:", 1)[1].splitlines()[0] + ) + + for _ in busy_retry(SHORT_TIMEOUT): + all_awaited_by = get_all_awaited_by(p.pid) + if any( + task.task_name == "worker task" + for info in all_awaited_by + if info.thread_id == worker_thread_id + for task in info.awaited_by + ): + break + else: + self.fail( + "get_all_awaited_by() did not report " + "the asyncio task from the non-main thread" + ) + finally: + _cleanup_sockets(client_socket, server_socket) + @skip_if_not_supported @unittest.skipIf( sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, "Test only runs on Linux with process_vm_readv support", ) - @requires_gil_enabled("Free threaded builds don't have an 'active thread'") - def test_only_active_thread(self): - # Test that only_active_thread parameter works correctly + def test_async_remote_stack_trace_from_non_main_thread(self): port = find_unused_port() script = textwrap.dedent( f"""\ - import time, sys, socket, threading + import asyncio + import socket + import threading + import time - # Connect to the test process sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', {port})) - def worker_thread(name, barrier, ready_event): - barrier.wait() # Synchronize thread start - ready_event.wait() # Wait for main thread signal - # Sleep to keep thread alive + def blocking_call(): + sock.sendall(f"ready:{{threading.get_native_id()}}\\n".encode()) time.sleep(10_000) - def main_work(): - # Do busy work to hold the GIL - sock.sendall(b"working\\n") - count = 0 - while count < 100000000: - count += 1 - if count % 10000000 == 0: - pass # Keep main thread busy - sock.sendall(b"done\\n") - - # Create synchronization primitives - num_threads = 3 - barrier = threading.Barrier(num_threads + 1) # +1 for main thread - ready_event = threading.Event() - - # Start worker threads - threads = [] - for i in range(num_threads): - t = threading.Thread(target=worker_thread, args=(f"Worker-{{i}}", barrier, ready_event)) - t.start() - threads.append(t) - - # Wait for all threads to be ready - barrier.wait() + async def worker_task(): + await asyncio.sleep(0) + blocking_call() - # Signal ready to parent process - sock.sendall(b"ready\\n") + async def worker_main(): + task = asyncio.create_task( + worker_task(), + name="worker task", + ) + await task - # Signal threads to start waiting - ready_event.set() + def run_worker_loop(): + asyncio.run(worker_main()) - # Now do busy work to hold the GIL - main_work() + threading.Thread( + target=run_worker_loop, + name="async-worker", + daemon=True, + ).start() + time.sleep(10_000) """ ) @@ -1174,87 +1559,631 @@ def main_work(): script_dir = os.path.join(work_dir, "script_pkg") os.mkdir(script_dir) - # Create a socket server to communicate with the target process - server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - server_socket.bind(("localhost", port)) - server_socket.settimeout(SHORT_TIMEOUT) - server_socket.listen(1) - + server_socket = _create_server_socket(port) script_name = _make_test_script(script_dir, "script", script) client_socket = None - try: - p = subprocess.Popen([sys.executable, script_name]) - client_socket, _ = server_socket.accept() - server_socket.close() - # Wait for ready signal - response = b"" - while b"ready" not in response: - response += client_socket.recv(1024) - - # Wait for the main thread to start its busy work - while b"working" not in response: - response += client_socket.recv(1024) - - # Get stack trace with all threads - unwinder_all = RemoteUnwinder(p.pid, all_threads=True) - for _ in range(10): - # Wait for the main thread to start its busy work - all_traces = unwinder_all.get_stack_trace() - found = False - for thread_id, stack in all_traces: - if not stack: - continue - current_frame = stack[0] - if ( - current_frame.funcname == "main_work" - and current_frame.lineno > 15 - ): - found = True + try: + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None - if found: - break - # Give a bit of time to take the next sample - time.sleep(0.1) - else: - self.fail( - "Main thread did not start its busy work on time" + response = _wait_for_signal(client_socket, b"ready:") + worker_thread_id = int( + response.split(b"ready:", 1)[1].splitlines()[0] ) - # Get stack trace with only GIL holder - unwinder_gil = RemoteUnwinder(p.pid, only_active_thread=True) - gil_traces = unwinder_gil.get_stack_trace() - - except PermissionError: - self.skipTest( - "Insufficient permissions to read the stack trace" - ) + for _ in busy_retry(SHORT_TIMEOUT): + stack_trace = get_async_stack_trace(p.pid) + if any( + task.task_name == "worker task" + for info in stack_trace + if info.thread_id == worker_thread_id + for task in info.awaited_by + ): + break + else: + self.fail( + "get_async_stack_trace() did not report " + "the running asyncio task from the non-main thread" + ) finally: - if client_socket is not None: - client_socket.close() - p.kill() - p.terminate() - p.wait(timeout=SHORT_TIMEOUT) + _cleanup_sockets(client_socket, server_socket) - # Verify we got multiple threads in all_traces - self.assertGreater( - len(all_traces), 1, "Should have multiple threads" - ) + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_self_trace(self): + stack_trace = get_stack_trace(os.getpid()) - # Verify we got exactly one thread in gil_traces - self.assertEqual( - len(gil_traces), 1, "Should have exactly one GIL holder" - ) + this_thread_stack = None + for interpreter_info in stack_trace: + for thread_info in interpreter_info.threads: + if thread_info.thread_id == threading.get_native_id(): + this_thread_stack = thread_info.frame_info + break + if this_thread_stack: + break - # The GIL holder should be in the all_traces list - gil_thread_id = gil_traces[0][0] - all_thread_ids = [trace[0] for trace in all_traces] - self.assertIn( - gil_thread_id, - all_thread_ids, - "GIL holder should be among all threads", - ) + self.assertIsNotNone(this_thread_stack) + # Check the top two frames + self.assertGreaterEqual(len(this_thread_stack), 2) + self.assertEqual(this_thread_stack[0].funcname, "get_stack_trace") + self.assertTrue(this_thread_stack[0].filename.endswith("test_external_inspection.py")) + self.assertEqual(this_thread_stack[1].funcname, "TestGetStackTrace.test_self_trace") + self.assertTrue(this_thread_stack[1].filename.endswith("test_external_inspection.py")) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + @requires_subinterpreters + def test_subinterpreter_stack_trace(self): + port = find_unused_port() + + import pickle + + subinterp_code = textwrap.dedent(f""" + import socket + import time + + def sub_worker(): + def nested_func(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + sock.sendall(b"ready:sub\\n") + time.sleep(10_000) + nested_func() + + sub_worker() + """).strip() + + pickled_code = pickle.dumps(subinterp_code) + + script = textwrap.dedent( + f""" + from concurrent import interpreters + import time + import sys + import socket + import threading + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + + def main_worker(): + sock.sendall(b"ready:main\\n") + time.sleep(10_000) + + def run_subinterp(): + subinterp = interpreters.create() + import pickle + pickled_code = {pickled_code!r} + subinterp_code = pickle.loads(pickled_code) + subinterp.exec(subinterp_code) + + sub_thread = threading.Thread(target=run_subinterp) + sub_thread.start() + + main_thread = threading.Thread(target=main_worker) + main_thread.start() + + main_thread.join() + sub_thread.join() + """ + ) + + with os_helper.temp_dir() as work_dir: + script_dir = os.path.join(work_dir, "script_pkg") + os.mkdir(script_dir) + + server_socket = _create_server_socket(port) + script_name = _make_test_script(script_dir, "script", script) + client_sockets = [] + + try: + with _managed_subprocess([sys.executable, script_name]) as p: + # Accept connections from both main and subinterpreter + responses = set() + while len(responses) < 2: + try: + client_socket, _ = server_socket.accept() + client_sockets.append(client_socket) + response = client_socket.recv(1024) + if b"ready:main" in response: + responses.add("main") + if b"ready:sub" in response: + responses.add("sub") + except socket.timeout: + break + + server_socket.close() + server_socket = None + + stack_trace = get_stack_trace(p.pid) + + # Verify we have at least one interpreter + self.assertGreaterEqual(len(stack_trace), 1) + + # Look for main interpreter (ID 0) and subinterpreter (ID > 0) + main_interp = None + sub_interp = None + for interpreter_info in stack_trace: + if interpreter_info.interpreter_id == 0: + main_interp = interpreter_info + elif interpreter_info.interpreter_id > 0: + sub_interp = interpreter_info + + self.assertIsNotNone( + main_interp, "Main interpreter should be present" + ) + + # Check main interpreter has expected stack trace + main_found = self._find_frame_in_trace( + [main_interp], lambda f: f.funcname == "main_worker" + ) + self.assertIsNotNone( + main_found, + "Main interpreter should have main_worker in stack", + ) + + # If subinterpreter is present, check its stack trace + if sub_interp: + sub_found = self._find_frame_in_trace( + [sub_interp], + lambda f: f.funcname + in ("sub_worker", "nested_func"), + ) + self.assertIsNotNone( + sub_found, + "Subinterpreter should have sub_worker or nested_func in stack", + ) + finally: + _cleanup_sockets(*client_sockets, server_socket) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + @requires_subinterpreters + def test_multiple_subinterpreters_with_threads(self): + port = find_unused_port() + + import pickle + + subinterp1_code = textwrap.dedent(f""" + import socket + import time + import threading + + def worker1(): + def nested_func(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + sock.sendall(b"ready:sub1-t1\\n") + time.sleep(10_000) + nested_func() + + def worker2(): + def nested_func(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + sock.sendall(b"ready:sub1-t2\\n") + time.sleep(10_000) + nested_func() + + t1 = threading.Thread(target=worker1) + t2 = threading.Thread(target=worker2) + t1.start() + t2.start() + t1.join() + t2.join() + """).strip() + + subinterp2_code = textwrap.dedent(f""" + import socket + import time + import threading + + def worker1(): + def nested_func(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + sock.sendall(b"ready:sub2-t1\\n") + time.sleep(10_000) + nested_func() + + def worker2(): + def nested_func(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + sock.sendall(b"ready:sub2-t2\\n") + time.sleep(10_000) + nested_func() + + t1 = threading.Thread(target=worker1) + t2 = threading.Thread(target=worker2) + t1.start() + t2.start() + t1.join() + t2.join() + """).strip() + + pickled_code1 = pickle.dumps(subinterp1_code) + pickled_code2 = pickle.dumps(subinterp2_code) + + script = textwrap.dedent( + f""" + from concurrent import interpreters + import time + import sys + import socket + import threading + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + + def main_worker(): + sock.sendall(b"ready:main\\n") + time.sleep(10_000) + + def run_subinterp1(): + subinterp = interpreters.create() + import pickle + pickled_code = {pickled_code1!r} + subinterp_code = pickle.loads(pickled_code) + subinterp.exec(subinterp_code) + + def run_subinterp2(): + subinterp = interpreters.create() + import pickle + pickled_code = {pickled_code2!r} + subinterp_code = pickle.loads(pickled_code) + subinterp.exec(subinterp_code) + + sub1_thread = threading.Thread(target=run_subinterp1) + sub2_thread = threading.Thread(target=run_subinterp2) + sub1_thread.start() + sub2_thread.start() + + main_thread = threading.Thread(target=main_worker) + main_thread.start() + + main_thread.join() + sub1_thread.join() + sub2_thread.join() + """ + ) + + with os_helper.temp_dir() as work_dir: + script_dir = os.path.join(work_dir, "script_pkg") + os.mkdir(script_dir) + + server_socket = _create_server_socket(port, backlog=5) + script_name = _make_test_script(script_dir, "script", script) + client_sockets = [] + + try: + with _managed_subprocess([sys.executable, script_name]) as p: + # Accept connections from main and all subinterpreter threads + expected_responses = { + "ready:main", + "ready:sub1-t1", + "ready:sub1-t2", + "ready:sub2-t1", + "ready:sub2-t2", + } + responses = set() + + while len(responses) < 5: + try: + client_socket, _ = server_socket.accept() + client_sockets.append(client_socket) + response = client_socket.recv(1024) + response_str = response.decode().strip() + if response_str in expected_responses: + responses.add(response_str) + except socket.timeout: + break + + server_socket.close() + server_socket = None + + stack_trace = get_stack_trace(p.pid) + + # Verify we have multiple interpreters + self.assertGreaterEqual(len(stack_trace), 2) + + # Count interpreters by ID + interpreter_ids = { + interp.interpreter_id for interp in stack_trace + } + self.assertIn( + 0, + interpreter_ids, + "Main interpreter should be present", + ) + self.assertGreaterEqual(len(interpreter_ids), 3) + + # Count total threads + total_threads = sum( + len(interp.threads) for interp in stack_trace + ) + self.assertGreaterEqual(total_threads, 5) + + # Look for expected function names + all_funcnames = set() + for interpreter_info in stack_trace: + for thread_info in interpreter_info.threads: + for frame in thread_info.frame_info: + all_funcnames.add(frame.funcname) + + expected_funcs = { + "main_worker", + "worker1", + "worker2", + "nested_func", + } + found_funcs = expected_funcs.intersection(all_funcnames) + self.assertGreater(len(found_funcs), 0) + finally: + _cleanup_sockets(*client_sockets, server_socket) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + @requires_gil_enabled("Free threaded builds don't have an 'active thread'") + def test_only_active_thread(self): + port = find_unused_port() + script = textwrap.dedent( + f"""\ + import time, sys, socket, threading + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + + def worker_thread(name, barrier, ready_event): + barrier.wait() + ready_event.wait() + time.sleep(10_000) + + def main_work(): + sock.sendall(b"working\\n") + count = 0 + while count < 100000000: + count += 1 + if count % 10000000 == 0: + pass + sock.sendall(b"done\\n") + + num_threads = 3 + barrier = threading.Barrier(num_threads + 1) + ready_event = threading.Event() + + threads = [] + for i in range(num_threads): + t = threading.Thread(target=worker_thread, args=(f"Worker-{{i}}", barrier, ready_event)) + t.start() + threads.append(t) + + barrier.wait() + sock.sendall(b"ready\\n") + ready_event.set() + main_work() + """ + ) + + with os_helper.temp_dir() as work_dir: + script_dir = os.path.join(work_dir, "script_pkg") + os.mkdir(script_dir) + + server_socket = _create_server_socket(port) + script_name = _make_test_script(script_dir, "script", script) + client_socket = None + + try: + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None + + # Wait for ready and working signals + _wait_for_signal(client_socket, [b"ready", b"working"]) + + # Get stack trace with all threads + unwinder_all = RemoteUnwinder(p.pid, all_threads=True) + for _ in busy_retry(SHORT_TIMEOUT): + with contextlib.suppress(*TRANSIENT_ERRORS): + all_traces = unwinder_all.get_stack_trace() + found = self._find_frame_in_trace( + all_traces, + lambda f: f.funcname == "main_work" + and f.location.lineno > 12, + ) + if found: + break + else: + self.fail( + "Main thread did not start its busy work on time" + ) + + # Get stack trace with only GIL holder + unwinder_gil = RemoteUnwinder( + p.pid, only_active_thread=True + ) + # Use condition to retry until we capture a thread holding the GIL + # (sampling may catch moments with no GIL holder on slow CI) + gil_traces = _get_stack_trace_with_retry( + unwinder_gil, + condition=lambda t: sum(len(i.threads) for i in t) >= 1, + ) + + # Count threads + total_threads = sum( + len(interp.threads) for interp in all_traces + ) + self.assertGreater(total_threads, 1) + + total_gil_threads = sum( + len(interp.threads) for interp in gil_traces + ) + self.assertEqual(total_gil_threads, 1) + + # Get the GIL holder thread ID + gil_thread_id = None + for interpreter_info in gil_traces: + if interpreter_info.threads: + gil_thread_id = interpreter_info.threads[ + 0 + ].thread_id + break + + # Get all thread IDs + all_thread_ids = [] + for interpreter_info in all_traces: + for thread_info in interpreter_info.threads: + all_thread_ids.append(thread_info.thread_id) + + self.assertIn(gil_thread_id, all_thread_ids) + finally: + _cleanup_sockets(client_socket, server_socket) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_opcodes_collection(self): + """Test that opcodes are collected when the opcodes flag is set.""" + script = textwrap.dedent( + """\ + import time, sys, socket + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + + def foo(): + sock.sendall(b"ready") + time.sleep(10_000) + + foo() + """ + ) + + def get_trace_with_opcodes(pid): + return RemoteUnwinder(pid, opcodes=True).get_stack_trace() + + stack_trace, _ = self._run_script_and_get_trace( + script, get_trace_with_opcodes, wait_for_signals=b"ready" + ) + + # Find our foo frame and verify it has an opcode + foo_frame = self._find_frame_in_trace( + stack_trace, lambda f: f.funcname == "foo" + ) + self.assertIsNotNone(foo_frame, "Could not find foo frame") + self.assertIsInstance(foo_frame.opcode, int) + self.assertGreaterEqual(foo_frame.opcode, 0) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_location_tuple_format(self): + """Test that location is a 4-tuple (lineno, end_lineno, col_offset, end_col_offset).""" + script = textwrap.dedent( + """\ + import time, sys, socket + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + + def foo(): + sock.sendall(b"ready") + time.sleep(10_000) + + foo() + """ + ) + + def get_trace_with_opcodes(pid): + return RemoteUnwinder(pid, opcodes=True).get_stack_trace() + + stack_trace, _ = self._run_script_and_get_trace( + script, get_trace_with_opcodes, wait_for_signals=b"ready" + ) + + # Find our foo frame + foo_frame = self._find_frame_in_trace( + stack_trace, lambda f: f.funcname == "foo" + ) + self.assertIsNotNone(foo_frame, "Could not find foo frame") + + # Check location is a 4-tuple with valid values + location = foo_frame.location + self.assertIsInstance(location, tuple) + self.assertEqual(len(location), 4) + lineno, end_lineno, col_offset, end_col_offset = location + self.assertIsInstance(lineno, int) + self.assertGreater(lineno, 0) + self.assertIsInstance(end_lineno, int) + self.assertGreaterEqual(end_lineno, lineno) + self.assertIsInstance(col_offset, int) + self.assertGreaterEqual(col_offset, 0) + self.assertIsInstance(end_col_offset, int) + self.assertGreaterEqual(end_col_offset, col_offset) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_location_tuple_exact_values(self): + """Test exact values of location tuple including column offsets.""" + script = textwrap.dedent( + """\ + import time, sys, socket + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + + def foo(): + sock.sendall(b"ready") + time.sleep(10_000) + + foo() + """ + ) + + def get_trace_with_opcodes(pid): + return RemoteUnwinder(pid, opcodes=True).get_stack_trace() + + stack_trace, _ = self._run_script_and_get_trace( + script, get_trace_with_opcodes, wait_for_signals=b"ready" + ) + + foo_frame = self._find_frame_in_trace( + stack_trace, lambda f: f.funcname == "foo" + ) + self.assertIsNotNone(foo_frame, "Could not find foo frame") + + # Can catch either sock.sendall (line 7) or time.sleep (line 8) + location = foo_frame.location + valid_locations = [ + (7, 7, 4, 26), # sock.sendall(b"ready") + (8, 8, 4, 22), # time.sleep(10_000) + ] + actual = (location.lineno, location.end_lineno, + location.col_offset, location.end_col_offset) + self.assertIn(actual, valid_locations) class TestUnsupportedPlatformHandling(unittest.TestCase): @@ -1262,16 +2191,1618 @@ class TestUnsupportedPlatformHandling(unittest.TestCase): sys.platform in ("linux", "darwin", "win32"), "Test only runs on unsupported platforms (not Linux, macOS, or Windows)", ) - @unittest.skipIf(sys.platform == "android", "Android raises Linux-specific exception") + @unittest.skipIf( + sys.platform == "android", "Android raises Linux-specific exception" + ) def test_unsupported_platform_error(self): with self.assertRaises(RuntimeError) as cm: RemoteUnwinder(os.getpid()) self.assertIn( "Reading the PyRuntime section is not supported on this platform", - str(cm.exception) + str(cm.exception), + ) + + +@requires_remote_subprocess_debugging() +class TestDetectionOfThreadStatus(RemoteInspectionTestBase): + def _run_thread_status_test(self, mode, check_condition): + """ + Common pattern for thread status detection tests. + + Args: + mode: Profiling mode (PROFILING_MODE_CPU, PROFILING_MODE_GIL, etc.) + check_condition: Function(statuses, sleeper_tid, busy_tid) -> bool + """ + port = find_unused_port() + script = textwrap.dedent( + f"""\ + import time, sys, socket, threading + import os + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect(('localhost', {port})) + + def sleeper(): + tid = threading.get_native_id() + sock.sendall(f'ready:sleeper:{{tid}}\\n'.encode()) + time.sleep(10000) + + def busy(): + tid = threading.get_native_id() + sock.sendall(f'ready:busy:{{tid}}\\n'.encode()) + x = 0 + while True: + x = x + 1 + time.sleep(0.5) + + t1 = threading.Thread(target=sleeper) + t2 = threading.Thread(target=busy) + t1.start() + t2.start() + sock.sendall(b'ready:main\\n') + t1.join() + t2.join() + sock.close() + """ ) + with os_helper.temp_dir() as work_dir: + script_dir = os.path.join(work_dir, "script_pkg") + os.mkdir(script_dir) + + server_socket = _create_server_socket(port) + script_name = _make_test_script( + script_dir, "thread_status_script", script + ) + client_socket = None + + try: + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None + + # Wait for all ready signals and parse TIDs + response = _wait_for_signal( + client_socket, + [b"ready:main", b"ready:sleeper", b"ready:busy"], + ) + + sleeper_tid = None + busy_tid = None + for line in response.split(b"\n"): + if line.startswith(b"ready:sleeper:"): + try: + sleeper_tid = int(line.split(b":")[-1]) + except (ValueError, IndexError): + pass + elif line.startswith(b"ready:busy:"): + try: + busy_tid = int(line.split(b":")[-1]) + except (ValueError, IndexError): + pass + + self.assertIsNotNone( + sleeper_tid, "Sleeper thread id not received" + ) + self.assertIsNotNone( + busy_tid, "Busy thread id not received" + ) + + # Sample until we see expected thread states + statuses = {} + unwinder = RemoteUnwinder( + p.pid, + all_threads=True, + mode=mode, + skip_non_matching_threads=False, + ) + for _ in busy_retry(SHORT_TIMEOUT): + with contextlib.suppress(*TRANSIENT_ERRORS): + traces = unwinder.get_stack_trace() + statuses = self._get_thread_statuses(traces) + + if check_condition( + statuses, sleeper_tid, busy_tid + ): + break + + return statuses, sleeper_tid, busy_tid + finally: + _cleanup_sockets(client_socket, server_socket) + + @unittest.skipIf( + sys.platform not in ("linux", "darwin", "win32"), + "Test only runs on supported platforms (Linux, macOS, or Windows)", + ) + @unittest.skipIf( + sys.platform == "android", "Android raises Linux-specific exception" + ) + def test_thread_status_detection(self): + def check_cpu_status(statuses, sleeper_tid, busy_tid): + return ( + sleeper_tid in statuses + and busy_tid in statuses + and not (statuses[sleeper_tid] & THREAD_STATUS_ON_CPU) + and (statuses[busy_tid] & THREAD_STATUS_ON_CPU) + ) + + statuses, sleeper_tid, busy_tid = self._run_thread_status_test( + PROFILING_MODE_CPU, check_cpu_status + ) + + self.assertIn(sleeper_tid, statuses) + self.assertIn(busy_tid, statuses) + self.assertFalse( + statuses[sleeper_tid] & THREAD_STATUS_ON_CPU, + "Sleeper thread should be off CPU", + ) + self.assertTrue( + statuses[busy_tid] & THREAD_STATUS_ON_CPU, + "Busy thread should be on CPU", + ) + + @unittest.skipIf( + sys.platform not in ("linux", "darwin", "win32"), + "Test only runs on supported platforms (Linux, macOS, or Windows)", + ) + @unittest.skipIf( + sys.platform == "android", "Android raises Linux-specific exception" + ) + def test_thread_status_gil_detection(self): + def check_gil_status(statuses, sleeper_tid, busy_tid): + return ( + sleeper_tid in statuses + and busy_tid in statuses + and not (statuses[sleeper_tid] & THREAD_STATUS_HAS_GIL) + and (statuses[busy_tid] & THREAD_STATUS_HAS_GIL) + ) + + statuses, sleeper_tid, busy_tid = self._run_thread_status_test( + PROFILING_MODE_GIL, check_gil_status + ) + + self.assertIn(sleeper_tid, statuses) + self.assertIn(busy_tid, statuses) + self.assertFalse( + statuses[sleeper_tid] & THREAD_STATUS_HAS_GIL, + "Sleeper thread should not have GIL", + ) + self.assertTrue( + statuses[busy_tid] & THREAD_STATUS_HAS_GIL, + "Busy thread should have GIL", + ) + + @unittest.skipIf( + sys.platform not in ("linux", "darwin", "win32"), + "Test only runs on supported platforms (Linux, macOS, or Windows)", + ) + @unittest.skipIf( + sys.platform == "android", "Android raises Linux-specific exception" + ) + def test_thread_status_all_mode_detection(self): + port = find_unused_port() + script = textwrap.dedent( + f"""\ + import socket + import threading + import time + import sys + + def sleeper_thread(): + conn = socket.create_connection(("localhost", {port})) + conn.sendall(b"sleeper:" + str(threading.get_native_id()).encode()) + while True: + time.sleep(1) + + def busy_thread(): + conn = socket.create_connection(("localhost", {port})) + conn.sendall(b"busy:" + str(threading.get_native_id()).encode()) + while True: + sum(range(100000)) + + t1 = threading.Thread(target=sleeper_thread) + t2 = threading.Thread(target=busy_thread) + t1.start() + t2.start() + t1.join() + t2.join() + """ + ) + + with os_helper.temp_dir() as tmp_dir: + script_file = make_script(tmp_dir, "script", script) + server_socket = _create_server_socket(port, backlog=2) + client_sockets = [] + + try: + with _managed_subprocess( + [sys.executable, script_file], + ) as p: + sleeper_tid = None + busy_tid = None + + # Receive thread IDs from the child process + for _ in range(2): + client_socket, _ = server_socket.accept() + client_sockets.append(client_socket) + line = client_socket.recv(1024) + if line: + if line.startswith(b"sleeper:"): + try: + sleeper_tid = int(line.split(b":")[-1]) + except (ValueError, IndexError): + pass + elif line.startswith(b"busy:"): + try: + busy_tid = int(line.split(b":")[-1]) + except (ValueError, IndexError): + pass + + server_socket.close() + server_socket = None + + statuses = {} + unwinder = RemoteUnwinder( + p.pid, + all_threads=True, + mode=PROFILING_MODE_ALL, + skip_non_matching_threads=False, + ) + for _ in busy_retry(SHORT_TIMEOUT): + with contextlib.suppress(*TRANSIENT_ERRORS): + traces = unwinder.get_stack_trace() + statuses = self._get_thread_statuses(traces) + + # Check ALL mode provides both GIL and CPU info + if ( + sleeper_tid in statuses + and busy_tid in statuses + and not ( + statuses[sleeper_tid] + & THREAD_STATUS_ON_CPU + ) + and not ( + statuses[sleeper_tid] + & THREAD_STATUS_HAS_GIL + ) + and (statuses[busy_tid] & THREAD_STATUS_ON_CPU) + and ( + statuses[busy_tid] & THREAD_STATUS_HAS_GIL + ) + ): + break + + self.assertIsNotNone( + sleeper_tid, "Sleeper thread id not received" + ) + self.assertIsNotNone( + busy_tid, "Busy thread id not received" + ) + self.assertIn(sleeper_tid, statuses) + self.assertIn(busy_tid, statuses) + + # Sleeper: off CPU, no GIL + self.assertFalse( + statuses[sleeper_tid] & THREAD_STATUS_ON_CPU, + "Sleeper should be off CPU", + ) + self.assertFalse( + statuses[sleeper_tid] & THREAD_STATUS_HAS_GIL, + "Sleeper should not have GIL", + ) + + # Busy: on CPU, has GIL + self.assertTrue( + statuses[busy_tid] & THREAD_STATUS_ON_CPU, + "Busy should be on CPU", + ) + self.assertTrue( + statuses[busy_tid] & THREAD_STATUS_HAS_GIL, + "Busy should have GIL", + ) + finally: + _cleanup_sockets(*client_sockets, server_socket) + + def _make_exception_test_script(self, port): + """Create script with exception and normal threads for testing.""" + return textwrap.dedent( + f"""\ + import socket + import threading + import time + + def exception_thread(): + conn = socket.create_connection(("localhost", {port})) + conn.sendall(b"exception:" + str(threading.get_native_id()).encode()) + try: + raise ValueError("test exception") + except ValueError: + while True: + time.sleep(0.01) + + def normal_thread(): + conn = socket.create_connection(("localhost", {port})) + conn.sendall(b"normal:" + str(threading.get_native_id()).encode()) + while True: + sum(range(1000)) + + t1 = threading.Thread(target=exception_thread) + t2 = threading.Thread(target=normal_thread) + t1.start() + t2.start() + t1.join() + t2.join() + """ + ) + + @contextmanager + def _run_exception_test_process(self): + """Context manager to run exception test script and yield thread IDs and process.""" + port = find_unused_port() + script = self._make_exception_test_script(port) + + with os_helper.temp_dir() as tmp_dir: + script_file = make_script(tmp_dir, "script", script) + server_socket = _create_server_socket(port, backlog=2) + client_sockets = [] + + try: + with _managed_subprocess([sys.executable, script_file]) as p: + exception_tid = None + normal_tid = None + + for _ in range(2): + client_socket, _ = server_socket.accept() + client_sockets.append(client_socket) + line = client_socket.recv(1024) + if line: + if line.startswith(b"exception:"): + try: + exception_tid = int(line.split(b":")[-1]) + except (ValueError, IndexError): + pass + elif line.startswith(b"normal:"): + try: + normal_tid = int(line.split(b":")[-1]) + except (ValueError, IndexError): + pass + + server_socket.close() + server_socket = None + + yield p, exception_tid, normal_tid + finally: + _cleanup_sockets(*client_sockets, server_socket) + + @unittest.skipIf( + sys.platform not in ("linux", "darwin", "win32"), + "Test only runs on supported platforms (Linux, macOS, or Windows)", + ) + @unittest.skipIf( + sys.platform == "android", "Android raises Linux-specific exception" + ) + def test_thread_status_exception_detection(self): + """Test that THREAD_STATUS_HAS_EXCEPTION is set when thread has an active exception.""" + with self._run_exception_test_process() as (p, exception_tid, normal_tid): + self.assertIsNotNone(exception_tid, "Exception thread id not received") + self.assertIsNotNone(normal_tid, "Normal thread id not received") + + statuses = {} + unwinder = RemoteUnwinder( + p.pid, + all_threads=True, + mode=PROFILING_MODE_ALL, + skip_non_matching_threads=False, + ) + for _ in busy_retry(SHORT_TIMEOUT): + with contextlib.suppress(*TRANSIENT_ERRORS): + traces = unwinder.get_stack_trace() + statuses = self._get_thread_statuses(traces) + + if ( + exception_tid in statuses + and normal_tid in statuses + and (statuses[exception_tid] & THREAD_STATUS_HAS_EXCEPTION) + and not (statuses[normal_tid] & THREAD_STATUS_HAS_EXCEPTION) + ): + break + + self.assertIn(exception_tid, statuses) + self.assertIn(normal_tid, statuses) + self.assertTrue( + statuses[exception_tid] & THREAD_STATUS_HAS_EXCEPTION, + "Exception thread should have HAS_EXCEPTION flag", + ) + self.assertFalse( + statuses[normal_tid] & THREAD_STATUS_HAS_EXCEPTION, + "Normal thread should not have HAS_EXCEPTION flag", + ) + + @unittest.skipIf( + sys.platform not in ("linux", "darwin", "win32"), + "Test only runs on supported platforms (Linux, macOS, or Windows)", + ) + @unittest.skipIf( + sys.platform == "android", "Android raises Linux-specific exception" + ) + def test_thread_status_exception_mode_filtering(self): + """Test that PROFILING_MODE_EXCEPTION correctly filters threads.""" + with self._run_exception_test_process() as (p, exception_tid, normal_tid): + self.assertIsNotNone(exception_tid, "Exception thread id not received") + self.assertIsNotNone(normal_tid, "Normal thread id not received") + + unwinder = RemoteUnwinder( + p.pid, + all_threads=True, + mode=PROFILING_MODE_EXCEPTION, + skip_non_matching_threads=True, + ) + for _ in busy_retry(SHORT_TIMEOUT): + with contextlib.suppress(*TRANSIENT_ERRORS): + traces = unwinder.get_stack_trace() + statuses = self._get_thread_statuses(traces) + + if exception_tid in statuses: + self.assertNotIn( + normal_tid, + statuses, + "Normal thread should be filtered out in exception mode", + ) + return + + self.fail("Never found exception thread in exception mode") + +@requires_remote_subprocess_debugging() +class TestExceptionDetectionScenarios(RemoteInspectionTestBase): + """Test exception detection across all scenarios. + + This class verifies the exact conditions under which THREAD_STATUS_HAS_EXCEPTION + is set. Each test covers a specific scenario: + + 1. except_block: Thread inside except block + -> SHOULD have HAS_EXCEPTION (exc_info->exc_value is set) + + 2. finally_propagating: Exception propagating through finally block + -> SHOULD have HAS_EXCEPTION (current_exception is set) + + 3. finally_after_except: Finally block after except handled exception + -> Should NOT have HAS_EXCEPTION (exc_info cleared after except) + + 4. finally_no_exception: Finally block with no exception raised + -> Should NOT have HAS_EXCEPTION (no exception state) + """ + + def _make_single_scenario_script(self, port, scenario): + """Create script for a single exception scenario.""" + scenarios = { + "except_block": f"""\ +import socket +import threading +import time + +def target_thread(): + '''Inside except block - exception info is present''' + conn = socket.create_connection(("localhost", {port})) + conn.sendall(b"ready:" + str(threading.get_native_id()).encode()) + try: + raise ValueError("test") + except ValueError: + while True: + time.sleep(0.01) + +t = threading.Thread(target=target_thread) +t.start() +t.join() +""", + "finally_propagating": f"""\ +import socket +import threading +import time + +def target_thread(): + '''Exception propagating through finally - current_exception is set''' + conn = socket.create_connection(("localhost", {port})) + conn.sendall(b"ready:" + str(threading.get_native_id()).encode()) + try: + try: + raise ValueError("propagating") + finally: + # Exception is propagating through here + while True: + time.sleep(0.01) + except: + pass # Never reached due to infinite loop + +t = threading.Thread(target=target_thread) +t.start() +t.join() +""", + "finally_after_except": f"""\ +import socket +import threading +import time + +def target_thread(): + '''Finally runs after except handled - exc_info is cleared''' + conn = socket.create_connection(("localhost", {port})) + conn.sendall(b"ready:" + str(threading.get_native_id()).encode()) + try: + raise ValueError("test") + except ValueError: + pass # Exception caught and handled + finally: + while True: + time.sleep(0.01) + +t = threading.Thread(target=target_thread) +t.start() +t.join() +""", + "finally_no_exception": f"""\ +import socket +import threading +import time + +def target_thread(): + '''Finally with no exception at all''' + conn = socket.create_connection(("localhost", {port})) + conn.sendall(b"ready:" + str(threading.get_native_id()).encode()) + try: + pass # No exception + finally: + while True: + time.sleep(0.01) + +t = threading.Thread(target=target_thread) +t.start() +t.join() +""", + } + + return scenarios[scenario] + + @contextmanager + def _run_scenario_process(self, scenario): + """Context manager to run a single scenario and yield thread ID and process.""" + port = find_unused_port() + script = self._make_single_scenario_script(port, scenario) + + with os_helper.temp_dir() as tmp_dir: + script_file = make_script(tmp_dir, "script", script) + server_socket = _create_server_socket(port, backlog=1) + client_socket = None + + try: + with _managed_subprocess([sys.executable, script_file]) as p: + thread_tid = None + + client_socket, _ = server_socket.accept() + line = client_socket.recv(1024) + if line and line.startswith(b"ready:"): + try: + thread_tid = int(line.split(b":")[-1]) + except (ValueError, IndexError): + pass + + server_socket.close() + server_socket = None + + yield p, thread_tid + finally: + _cleanup_sockets(client_socket, server_socket) + + def _check_thread_status( + self, p, thread_tid, condition, condition_name="condition" + ): + """Helper to check thread status with a custom condition. + + This waits until we see 3 consecutive samples where the condition + returns True, which confirms the thread has reached and is stable + in the expected state. Samples that don't match are ignored (the + thread may not have reached the expected state yet). + + Args: + p: Process object with pid attribute + thread_tid: Thread ID to check + condition: Callable(statuses, thread_tid) -> bool that returns + True when the thread is in the expected state + condition_name: Description of condition for error messages + """ + unwinder = RemoteUnwinder( + p.pid, + all_threads=True, + mode=PROFILING_MODE_ALL, + skip_non_matching_threads=False, + ) + + # Wait for 3 consecutive samples matching expected state + matching_samples = 0 + for _ in busy_retry(SHORT_TIMEOUT): + with contextlib.suppress(*TRANSIENT_ERRORS): + traces = unwinder.get_stack_trace() + statuses = self._get_thread_statuses(traces) + + if thread_tid in statuses: + if condition(statuses, thread_tid): + matching_samples += 1 + if matching_samples >= 3: + return # Success - confirmed stable in expected state + else: + # Thread not yet in expected state, reset counter + matching_samples = 0 + + self.fail( + f"Thread did not stabilize in expected state " + f"({condition_name}) within timeout" + ) + + def _check_exception_status(self, p, thread_tid, expect_exception): + """Helper to check if thread has expected exception status.""" + def condition(statuses, tid): + has_exc = bool(statuses[tid] & THREAD_STATUS_HAS_EXCEPTION) + return has_exc == expect_exception + + self._check_thread_status( + p, thread_tid, condition, + condition_name=f"expect_exception={expect_exception}" + ) + + @unittest.skipIf( + sys.platform not in ("linux", "darwin", "win32"), + "Test only runs on supported platforms (Linux, macOS, or Windows)", + ) + @unittest.skipIf( + sys.platform == "android", "Android raises Linux-specific exception" + ) + def test_except_block_has_exception(self): + """Test that thread inside except block has HAS_EXCEPTION flag. + + When a thread is executing inside an except block, exc_info->exc_value + is set, so THREAD_STATUS_HAS_EXCEPTION should be True. + """ + with self._run_scenario_process("except_block") as (p, thread_tid): + self.assertIsNotNone(thread_tid, "Thread ID not received") + self._check_exception_status(p, thread_tid, expect_exception=True) + + @unittest.skipIf( + sys.platform not in ("linux", "darwin", "win32"), + "Test only runs on supported platforms (Linux, macOS, or Windows)", + ) + @unittest.skipIf( + sys.platform == "android", "Android raises Linux-specific exception" + ) + def test_finally_propagating_has_exception(self): + """Test that finally block with propagating exception has HAS_EXCEPTION flag. + + When an exception is propagating through a finally block (not yet caught), + current_exception is set, so THREAD_STATUS_HAS_EXCEPTION should be True. + """ + with self._run_scenario_process("finally_propagating") as (p, thread_tid): + self.assertIsNotNone(thread_tid, "Thread ID not received") + self._check_exception_status(p, thread_tid, expect_exception=True) + + @unittest.skipIf( + sys.platform not in ("linux", "darwin", "win32"), + "Test only runs on supported platforms (Linux, macOS, or Windows)", + ) + @unittest.skipIf( + sys.platform == "android", "Android raises Linux-specific exception" + ) + def test_finally_after_except_no_exception(self): + """Test that finally block after except has NO HAS_EXCEPTION flag. + + When a finally block runs after an except block has handled the exception, + Python clears exc_info before entering finally, so THREAD_STATUS_HAS_EXCEPTION + should be False. + """ + with self._run_scenario_process("finally_after_except") as (p, thread_tid): + self.assertIsNotNone(thread_tid, "Thread ID not received") + self._check_exception_status(p, thread_tid, expect_exception=False) + + @unittest.skipIf( + sys.platform not in ("linux", "darwin", "win32"), + "Test only runs on supported platforms (Linux, macOS, or Windows)", + ) + @unittest.skipIf( + sys.platform == "android", "Android raises Linux-specific exception" + ) + def test_finally_no_exception_no_flag(self): + """Test that finally block with no exception has NO HAS_EXCEPTION flag. + + When a finally block runs during normal execution (no exception raised), + there is no exception state, so THREAD_STATUS_HAS_EXCEPTION should be False. + """ + with self._run_scenario_process("finally_no_exception") as (p, thread_tid): + self.assertIsNotNone(thread_tid, "Thread ID not received") + self._check_exception_status(p, thread_tid, expect_exception=False) + + +@requires_remote_subprocess_debugging() +class TestFrameCaching(RemoteInspectionTestBase): + """Test that frame caching produces correct results. + + Uses socket-based synchronization for deterministic testing. + All tests verify cache reuse via object identity checks (assertIs). + """ + + @contextmanager + def _target_process(self, script_body): + """Context manager for running a target process with socket sync.""" + port = find_unused_port() + script = f"""\ +import socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.connect(('localhost', {port})) +{textwrap.dedent(script_body)} +""" + + with os_helper.temp_dir() as work_dir: + script_dir = os.path.join(work_dir, "script_pkg") + os.mkdir(script_dir) + + server_socket = _create_server_socket(port) + script_name = _make_test_script(script_dir, "script", script) + client_socket = None + + try: + with _managed_subprocess([sys.executable, script_name]) as p: + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None + + def make_unwinder(cache_frames=True): + return RemoteUnwinder( + p.pid, all_threads=True, cache_frames=cache_frames + ) + + yield p, client_socket, make_unwinder + finally: + _cleanup_sockets(client_socket, server_socket) + + def _get_frames_with_retry(self, unwinder, required_funcs): + """Get frames containing required_funcs, with retry for transient errors.""" + for _ in range(MAX_TRIES): + with contextlib.suppress(*TRANSIENT_ERRORS): + traces = unwinder.get_stack_trace() + for interp in traces: + for thread in interp.threads: + funcs = {f.funcname for f in thread.frame_info} + if required_funcs.issubset(funcs): + return thread.frame_info + time.sleep(RETRY_DELAY) + return None + + def _sample_frames( + self, + client_socket, + unwinder, + wait_signal, + send_ack, + required_funcs, + expected_frames=1, + ): + """Wait for signal, sample frames with retry until required funcs present, send ack.""" + _wait_for_signal(client_socket, wait_signal) + frames = None + for _ in range(MAX_TRIES): + frames = self._get_frames_with_retry(unwinder, required_funcs) + if frames and len(frames) >= expected_frames: + break + time.sleep(RETRY_DELAY) + client_socket.sendall(send_ack) + return frames + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_cache_hit_same_stack(self): + """Test that consecutive samples reuse cached parent frame objects. + + The current frame (index 0) is always re-read from memory to get + updated line numbers, so it may be a different object. Parent frames + (index 1+) should be identical objects from cache. + """ + script_body = """\ + def level3(): + sock.sendall(b"sync1") + sock.recv(16) + sock.sendall(b"sync2") + sock.recv(16) + sock.sendall(b"sync3") + sock.recv(16) + + def level2(): + level3() + + def level1(): + level2() + + level1() + """ + + with self._target_process(script_body) as ( + p, + client_socket, + make_unwinder, + ): + unwinder = make_unwinder(cache_frames=True) + expected = {"level1", "level2", "level3"} + + frames1 = self._sample_frames( + client_socket, unwinder, b"sync1", b"ack", expected + ) + frames2 = self._sample_frames( + client_socket, unwinder, b"sync2", b"ack", expected + ) + frames3 = self._sample_frames( + client_socket, unwinder, b"sync3", b"done", expected + ) + + self.assertIsNotNone(frames1) + self.assertIsNotNone(frames2) + self.assertIsNotNone(frames3) + self.assertEqual(len(frames1), len(frames2)) + self.assertEqual(len(frames2), len(frames3)) + + # Current frame (index 0) is always re-read, so check value equality + self.assertEqual(frames1[0].funcname, frames2[0].funcname) + self.assertEqual(frames2[0].funcname, frames3[0].funcname) + + # Parent frames (index 1+) must be identical objects (cache reuse) + for i in range(1, len(frames1)): + f1, f2, f3 = frames1[i], frames2[i], frames3[i] + self.assertIs( + f1, f2, f"Frame {i}: samples 1-2 must be same object" + ) + self.assertIs( + f2, f3, f"Frame {i}: samples 2-3 must be same object" + ) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_line_number_updates_in_same_frame(self): + """Test that line numbers are correctly updated when execution moves within a function. + + When the profiler samples at different points within the same function, + it must report the correct line number for each sample, not stale cached values. + """ + script_body = """\ + def outer(): + inner() + + def inner(): + sock.sendall(b"line_a"); sock.recv(16) + sock.sendall(b"line_b"); sock.recv(16) + sock.sendall(b"line_c"); sock.recv(16) + sock.sendall(b"line_d"); sock.recv(16) + + outer() + """ + + with self._target_process(script_body) as ( + p, + client_socket, + make_unwinder, + ): + unwinder = make_unwinder(cache_frames=True) + + frames_a = self._sample_frames( + client_socket, unwinder, b"line_a", b"ack", {"inner"} + ) + frames_b = self._sample_frames( + client_socket, unwinder, b"line_b", b"ack", {"inner"} + ) + frames_c = self._sample_frames( + client_socket, unwinder, b"line_c", b"ack", {"inner"} + ) + frames_d = self._sample_frames( + client_socket, unwinder, b"line_d", b"done", {"inner"} + ) + + self.assertIsNotNone(frames_a) + self.assertIsNotNone(frames_b) + self.assertIsNotNone(frames_c) + self.assertIsNotNone(frames_d) + + # Get the 'inner' frame from each sample (should be index 0) + inner_a = frames_a[0] + inner_b = frames_b[0] + inner_c = frames_c[0] + inner_d = frames_d[0] + + self.assertEqual(inner_a.funcname, "inner") + self.assertEqual(inner_b.funcname, "inner") + self.assertEqual(inner_c.funcname, "inner") + self.assertEqual(inner_d.funcname, "inner") + + # Line numbers must be different and increasing (execution moves forward) + self.assertLess( + inner_a.location.lineno, inner_b.location.lineno, "Line B should be after line A" + ) + self.assertLess( + inner_b.location.lineno, inner_c.location.lineno, "Line C should be after line B" + ) + self.assertLess( + inner_c.location.lineno, inner_d.location.lineno, "Line D should be after line C" + ) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_cache_invalidation_on_return(self): + """Test cache invalidation when stack shrinks (function returns).""" + script_body = """\ + def inner(): + sock.sendall(b"at_inner") + sock.recv(16) + + def outer(): + inner() + sock.sendall(b"at_outer") + sock.recv(16) + + outer() + """ + + with self._target_process(script_body) as ( + p, + client_socket, + make_unwinder, + ): + unwinder = make_unwinder(cache_frames=True) + + frames_deep = self._sample_frames( + client_socket, + unwinder, + b"at_inner", + b"ack", + {"inner", "outer"}, + ) + frames_shallow = self._sample_frames( + client_socket, unwinder, b"at_outer", b"done", {"outer"} + ) + + self.assertIsNotNone(frames_deep) + self.assertIsNotNone(frames_shallow) + + funcs_deep = [f.funcname for f in frames_deep] + funcs_shallow = [f.funcname for f in frames_shallow] + + self.assertIn("inner", funcs_deep) + self.assertIn("outer", funcs_deep) + self.assertNotIn("inner", funcs_shallow) + self.assertIn("outer", funcs_shallow) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_cache_invalidation_on_call(self): + """Test cache invalidation when stack grows (new function called).""" + script_body = """\ + def deeper(): + sock.sendall(b"at_deeper") + sock.recv(16) + + def middle(): + sock.sendall(b"at_middle") + sock.recv(16) + deeper() + + def top(): + middle() + + top() + """ + + with self._target_process(script_body) as ( + p, + client_socket, + make_unwinder, + ): + unwinder = make_unwinder(cache_frames=True) + + frames_before = self._sample_frames( + client_socket, + unwinder, + b"at_middle", + b"ack", + {"middle", "top"}, + ) + frames_after = self._sample_frames( + client_socket, + unwinder, + b"at_deeper", + b"done", + {"deeper", "middle", "top"}, + ) + + self.assertIsNotNone(frames_before) + self.assertIsNotNone(frames_after) + + funcs_before = [f.funcname for f in frames_before] + funcs_after = [f.funcname for f in frames_after] + + self.assertIn("middle", funcs_before) + self.assertIn("top", funcs_before) + self.assertNotIn("deeper", funcs_before) + + self.assertIn("deeper", funcs_after) + self.assertIn("middle", funcs_after) + self.assertIn("top", funcs_after) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_partial_stack_reuse(self): + """Test that unchanged parent frames are reused from cache when top frame moves.""" + script_body = """\ + def level4(): + sock.sendall(b"sync1") + sock.recv(16) + sock.sendall(b"sync2") + sock.recv(16) + + def level3(): + level4() + + def level2(): + level3() + + def level1(): + level2() + + level1() + """ + + with self._target_process(script_body) as ( + p, + client_socket, + make_unwinder, + ): + unwinder = make_unwinder(cache_frames=True) + + # Sample 1: level4 at first sendall + frames1 = self._sample_frames( + client_socket, + unwinder, + b"sync1", + b"ack", + {"level1", "level2", "level3", "level4"}, + ) + # Sample 2: level4 at second sendall (same stack, different line) + frames2 = self._sample_frames( + client_socket, + unwinder, + b"sync2", + b"done", + {"level1", "level2", "level3", "level4"}, + ) + + self.assertIsNotNone(frames1) + self.assertIsNotNone(frames2) + + def find_frame(frames, funcname): + for f in frames: + if f.funcname == funcname: + return f + return None + + # level4 should have different line numbers (it moved) + l4_1 = find_frame(frames1, "level4") + l4_2 = find_frame(frames2, "level4") + self.assertIsNotNone(l4_1) + self.assertIsNotNone(l4_2) + self.assertNotEqual( + l4_1.location.lineno, + l4_2.location.lineno, + "level4 should be at different lines", + ) + + # Parent frames (level1, level2, level3) should be reused from cache + for name in ["level1", "level2", "level3"]: + f1 = find_frame(frames1, name) + f2 = find_frame(frames2, name) + self.assertIsNotNone(f1, f"{name} missing from sample 1") + self.assertIsNotNone(f2, f"{name} missing from sample 2") + self.assertIs(f1, f2, f"{name} should be reused from cache") + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_recursive_frames(self): + """Test caching with same function appearing multiple times (recursion).""" + script_body = """\ + def recurse(n): + if n <= 0: + sock.sendall(b"sync1") + sock.recv(16) + sock.sendall(b"sync2") + sock.recv(16) + else: + recurse(n - 1) + + recurse(5) + """ + + with self._target_process(script_body) as ( + p, + client_socket, + make_unwinder, + ): + unwinder = make_unwinder(cache_frames=True) + + frames1 = self._sample_frames( + client_socket, unwinder, b"sync1", b"ack", {"recurse"} + ) + frames2 = self._sample_frames( + client_socket, unwinder, b"sync2", b"done", {"recurse"} + ) + + self.assertIsNotNone(frames1) + self.assertIsNotNone(frames2) + + # Should have multiple "recurse" frames (6 total: recurse(5) down to recurse(0)) + recurse_count = sum(1 for f in frames1 if f.funcname == "recurse") + self.assertEqual(recurse_count, 6, "Should have 6 recursive frames") + + self.assertEqual(len(frames1), len(frames2)) + + # Current frame (index 0) is re-read, check value equality + self.assertEqual(frames1[0].funcname, frames2[0].funcname) + + # Parent frames (index 1+) should be identical objects (cache reuse) + for i in range(1, len(frames1)): + self.assertIs( + frames1[i], + frames2[i], + f"Frame {i}: recursive frames must be same object", + ) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_cache_vs_no_cache_equivalence(self): + """Test that cache_frames=True and cache_frames=False produce equivalent results.""" + script_body = """\ + def level3(): + sock.sendall(b"ready"); sock.recv(16) + + def level2(): + level3() + + def level1(): + level2() + + level1() + """ + + with self._target_process(script_body) as ( + p, + client_socket, + make_unwinder, + ): + _wait_for_signal(client_socket, b"ready") + + # Sample with cache + unwinder_cache = make_unwinder(cache_frames=True) + frames_cached = self._get_frames_with_retry( + unwinder_cache, {"level1", "level2", "level3"} + ) + + # Sample without cache + unwinder_no_cache = make_unwinder(cache_frames=False) + frames_no_cache = self._get_frames_with_retry( + unwinder_no_cache, {"level1", "level2", "level3"} + ) + + client_socket.sendall(b"done") + + self.assertIsNotNone(frames_cached) + self.assertIsNotNone(frames_no_cache) + + # Same number of frames + self.assertEqual(len(frames_cached), len(frames_no_cache)) + + # Same function names in same order + funcs_cached = [f.funcname for f in frames_cached] + funcs_no_cache = [f.funcname for f in frames_no_cache] + self.assertEqual(funcs_cached, funcs_no_cache) + + # For level3 (leaf frame), due to timing races we can be at either + # sock.sendall() or sock.recv() - both are valid. For parent frames, + # the locations should match exactly. + # Valid locations for level3: line 3 has two statements + # sock.sendall(b"ready") -> col 4-26 + # sock.recv(16) -> col 28-41 + level3_valid_cols = {(4, 26), (28, 41)} + + for i in range(len(frames_cached)): + loc_cached = frames_cached[i].location + loc_no_cache = frames_no_cache[i].location + + if frames_cached[i].funcname == "level3": + # Leaf frame: can be at either statement + self.assertIn( + (loc_cached.col_offset, loc_cached.end_col_offset), + level3_valid_cols, + ) + self.assertIn( + (loc_no_cache.col_offset, loc_no_cache.end_col_offset), + level3_valid_cols, + ) + else: + # Parent frames: must match exactly + self.assertEqual(loc_cached, loc_no_cache) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_cache_per_thread_isolation(self): + """Test that frame cache is per-thread and cache invalidation works independently.""" + script_body = """\ + import threading + + lock = threading.Lock() + + def sync(msg): + with lock: + sock.sendall(msg + b"\\n") + sock.recv(1) + + # Thread 1 functions + def baz1(): + sync(b"t1:baz1") + + def bar1(): + baz1() + + def blech1(): + sync(b"t1:blech1") + + def foo1(): + bar1() # Goes down to baz1, syncs + blech1() # Returns up, goes down to blech1, syncs + + # Thread 2 functions + def baz2(): + sync(b"t2:baz2") + + def bar2(): + baz2() + + def blech2(): + sync(b"t2:blech2") + + def foo2(): + bar2() # Goes down to baz2, syncs + blech2() # Returns up, goes down to blech2, syncs + + t1 = threading.Thread(target=foo1) + t2 = threading.Thread(target=foo2) + t1.start() + t2.start() + t1.join() + t2.join() + """ + + with self._target_process(script_body) as ( + p, + client_socket, + make_unwinder, + ): + unwinder = make_unwinder(cache_frames=True) + + # Message dispatch table: signal -> required functions for that thread + dispatch = { + b"t1:baz1": {"baz1", "bar1", "foo1"}, + b"t2:baz2": {"baz2", "bar2", "foo2"}, + b"t1:blech1": {"blech1", "foo1"}, + b"t2:blech2": {"blech2", "foo2"}, + } + + # Track results for each sync point + results = {} + + # Process 4 sync points (order depends on thread scheduling) + buffer = _wait_for_signal(client_socket, b"\n") + for i in range(4): + # Extract first message from buffer + msg, sep, buffer = buffer.partition(b"\n") + self.assertIn(msg, dispatch, f"Unexpected message: {msg!r}") + + # Sample frames for the thread at this sync point + required_funcs = dispatch[msg] + frames = self._get_frames_with_retry(unwinder, required_funcs) + self.assertIsNotNone(frames, f"Thread not found for {msg!r}") + results[msg] = [f.funcname for f in frames] + + # Release thread and wait for next message (if not last) + client_socket.sendall(b"k") + if i < 3: + buffer += _wait_for_signal(client_socket, b"\n") + + # Validate Phase 1: baz snapshots + t1_baz = results.get(b"t1:baz1") + t2_baz = results.get(b"t2:baz2") + self.assertIsNotNone(t1_baz, "Missing t1:baz1 snapshot") + self.assertIsNotNone(t2_baz, "Missing t2:baz2 snapshot") + + # Thread 1 at baz1: should have foo1->bar1->baz1 + self.assertIn("baz1", t1_baz) + self.assertIn("bar1", t1_baz) + self.assertIn("foo1", t1_baz) + self.assertNotIn("blech1", t1_baz) + # No cross-contamination + self.assertNotIn("baz2", t1_baz) + self.assertNotIn("bar2", t1_baz) + self.assertNotIn("foo2", t1_baz) + + # Thread 2 at baz2: should have foo2->bar2->baz2 + self.assertIn("baz2", t2_baz) + self.assertIn("bar2", t2_baz) + self.assertIn("foo2", t2_baz) + self.assertNotIn("blech2", t2_baz) + # No cross-contamination + self.assertNotIn("baz1", t2_baz) + self.assertNotIn("bar1", t2_baz) + self.assertNotIn("foo1", t2_baz) + + # Validate Phase 2: blech snapshots (cache invalidation test) + t1_blech = results.get(b"t1:blech1") + t2_blech = results.get(b"t2:blech2") + self.assertIsNotNone(t1_blech, "Missing t1:blech1 snapshot") + self.assertIsNotNone(t2_blech, "Missing t2:blech2 snapshot") + + # Thread 1 at blech1: bar1/baz1 should be GONE (cache invalidated) + self.assertIn("blech1", t1_blech) + self.assertIn("foo1", t1_blech) + self.assertNotIn( + "bar1", t1_blech, "Cache not invalidated: bar1 still present" + ) + self.assertNotIn( + "baz1", t1_blech, "Cache not invalidated: baz1 still present" + ) + # No cross-contamination + self.assertNotIn("blech2", t1_blech) + + # Thread 2 at blech2: bar2/baz2 should be GONE (cache invalidated) + self.assertIn("blech2", t2_blech) + self.assertIn("foo2", t2_blech) + self.assertNotIn( + "bar2", t2_blech, "Cache not invalidated: bar2 still present" + ) + self.assertNotIn( + "baz2", t2_blech, "Cache not invalidated: baz2 still present" + ) + # No cross-contamination + self.assertNotIn("blech1", t2_blech) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_new_unwinder_with_stale_last_profiled_frame(self): + """Test that a new unwinder returns complete stack when cache lookup misses.""" + script_body = """\ + def level4(): + sock.sendall(b"sync1") + sock.recv(16) + sock.sendall(b"sync2") + sock.recv(16) + + def level3(): + level4() + + def level2(): + level3() + + def level1(): + level2() + + level1() + """ + + with self._target_process(script_body) as ( + p, + client_socket, + make_unwinder, + ): + expected = {"level1", "level2", "level3", "level4"} + + # First unwinder samples - this sets last_profiled_frame in target + unwinder1 = make_unwinder(cache_frames=True) + frames1 = self._sample_frames( + client_socket, unwinder1, b"sync1", b"ack", expected + ) + + # Create NEW unwinder (empty cache) and sample + # The target still has last_profiled_frame set from unwinder1 + unwinder2 = make_unwinder(cache_frames=True) + frames2 = self._sample_frames( + client_socket, unwinder2, b"sync2", b"done", expected + ) + + self.assertIsNotNone(frames1) + self.assertIsNotNone(frames2) + + funcs1 = [f.funcname for f in frames1] + funcs2 = [f.funcname for f in frames2] + + # Both should have all levels + for level in ["level1", "level2", "level3", "level4"]: + self.assertIn(level, funcs1, f"{level} missing from first sample") + self.assertIn(level, funcs2, f"{level} missing from second sample") + + # Should have same stack depth + self.assertEqual( + len(frames1), + len(frames2), + "New unwinder should return complete stack despite stale last_profiled_frame", + ) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_cache_exhaustion(self): + """Test cache works when frame limit (1024) is exceeded. + + FRAME_CACHE_MAX_FRAMES=1024. With 1100 recursive frames, + the cache can't store all of them but should still work. + """ + # Use 1100 to exceed FRAME_CACHE_MAX_FRAMES=1024 + depth = 1100 + script_body = f"""\ +import sys +sys.setrecursionlimit(2000) + +def recurse(n): + if n <= 0: + sock.sendall(b"ready") + sock.recv(16) # wait for ack + sock.sendall(b"ready2") + sock.recv(16) # wait for done + return + recurse(n - 1) + +recurse({depth}) +""" + + with self._target_process(script_body) as ( + p, + client_socket, + make_unwinder, + ): + unwinder_cache = make_unwinder(cache_frames=True) + unwinder_no_cache = make_unwinder(cache_frames=False) + + frames_cached = self._sample_frames( + client_socket, + unwinder_cache, + b"ready", + b"ack", + {"recurse"}, + expected_frames=1102, + ) + # Sample again with no cache for comparison + frames_no_cache = self._sample_frames( + client_socket, + unwinder_no_cache, + b"ready2", + b"done", + {"recurse"}, + expected_frames=1102, + ) + + self.assertIsNotNone(frames_cached) + self.assertIsNotNone(frames_no_cache) + + # Both should have many recurse frames (> 1024 limit) + cached_count = [f.funcname for f in frames_cached].count("recurse") + no_cache_count = [f.funcname for f in frames_no_cache].count("recurse") + + self.assertGreater( + cached_count, 1000, "Should have >1000 recurse frames" + ) + self.assertGreater( + no_cache_count, 1000, "Should have >1000 recurse frames" + ) + + # Both modes should produce same frame count + self.assertEqual( + len(frames_cached), + len(frames_no_cache), + "Cache exhaustion should not affect stack completeness", + ) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_get_stats(self): + """Test that get_stats() returns statistics when stats=True.""" + script_body = """\ + sock.sendall(b"ready") + sock.recv(16) + """ + + with self._target_process(script_body) as (p, client_socket, _): + unwinder = RemoteUnwinder(p.pid, all_threads=True, stats=True) + _wait_for_signal(client_socket, b"ready") + + # Take a sample + _get_stack_trace_with_retry(unwinder) + + stats = unwinder.get_stats() + client_socket.sendall(b"done") + + # Verify expected keys exist + expected_keys = [ + "total_samples", + "frame_cache_hits", + "frame_cache_misses", + "frame_cache_partial_hits", + "frames_read_from_cache", + "frames_read_from_memory", + "frame_cache_hit_rate", + "batched_read_attempts", + "batched_read_successes", + "batched_read_misses", + "batched_read_segments_requested", + "batched_read_segments_completed", + "batched_read_success_rate", + "batched_read_segment_completion_rate", + ] + for key in expected_keys: + self.assertIn(key, stats) + + self.assertEqual(stats["total_samples"], 1) + + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_get_stats_disabled_raises(self): + """Test that get_stats() raises RuntimeError when stats=False.""" + script_body = """\ + sock.sendall(b"ready") + sock.recv(16) + """ + + with self._target_process(script_body) as (p, client_socket, _): + unwinder = RemoteUnwinder( + p.pid, all_threads=True + ) # stats=False by default + _wait_for_signal(client_socket, b"ready") + + with self.assertRaises(RuntimeError): + unwinder.get_stats() + + client_socket.sendall(b"done") + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 2fb963f52e5138..5a493a4fd95680 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -33,6 +33,11 @@ CURRENT_THREAD_HEADER = fr'{CURRENT_THREAD_ID} \(most recent call first\):' +def skip_if_sanitizer_signal(signame): + return support.skip_if_sanitizer(f"TSAN/UBSan itercepts {signame}", + thread=True, ub=True) + + def expected_traceback(lineno1, lineno2, header, min_count=1): regex = header regex += ' File "", line %s in func\n' % lineno1 @@ -224,7 +229,7 @@ def test_fatal_error_c_thread(self): func='faulthandler_fatal_error_thread', py_fatal_error=True) - @support.skip_if_sanitizer("TSAN itercepts SIGABRT", thread=True) + @skip_if_sanitizer_signal("SIGABRT") def test_sigabrt(self): self.check_fatal_error(""" import faulthandler @@ -236,7 +241,7 @@ def test_sigabrt(self): @unittest.skipIf(sys.platform == 'win32', "SIGFPE cannot be caught on Windows") - @support.skip_if_sanitizer("TSAN itercepts SIGFPE", thread=True) + @skip_if_sanitizer_signal("SIGFPE") def test_sigfpe(self): self.check_fatal_error(""" import faulthandler @@ -248,7 +253,7 @@ def test_sigfpe(self): @unittest.skipIf(_testcapi is None, 'need _testcapi') @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS') - @support.skip_if_sanitizer("TSAN itercepts SIGBUS", thread=True) + @skip_if_sanitizer_signal("SIGBUS") @skip_segfault_on_android def test_sigbus(self): self.check_fatal_error(""" @@ -263,7 +268,7 @@ def test_sigbus(self): @unittest.skipIf(_testcapi is None, 'need _testcapi') @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL') - @support.skip_if_sanitizer("TSAN itercepts SIGILL", thread=True) + @skip_if_sanitizer_signal("SIGILL") @skip_segfault_on_android def test_sigill(self): self.check_fatal_error(""" @@ -360,6 +365,17 @@ def test_enable_single_thread(self): 'Segmentation fault', all_threads=False) + @skip_segfault_on_android + def test_enable_without_c_stack(self): + self.check_fatal_error(""" + import faulthandler + faulthandler.enable(c_stack=False) + faulthandler._sigsegv() + """, + 3, + 'Segmentation fault', + c_stack=False) + @skip_segfault_on_android def test_disable(self): code = """ @@ -377,10 +393,11 @@ def test_disable(self): @skip_segfault_on_android def test_dump_ext_modules(self): + # Don't filter stdlib module names: disable sys.stdlib_module_names code = """ import faulthandler import sys - # Don't filter stdlib module names + import math sys.stdlib_module_names = frozenset() faulthandler.enable() faulthandler._sigsegv() @@ -392,9 +409,21 @@ def test_dump_ext_modules(self): if not match: self.fail(f"Cannot find 'Extension modules:' in {stderr!r}") modules = set(match.group(1).strip().split(', ')) - for name in ('sys', 'faulthandler'): + for name in ('sys', 'faulthandler', 'math'): self.assertIn(name, modules) + # Ignore "math.integer" sub-module if "math" package is + # in sys.stdlib_module_names + code = """ + import faulthandler + import math.integer + faulthandler.enable() + faulthandler._sigsegv() + """ + stderr, exitcode = self.get_output(code) + stderr = '\n'.join(stderr) + self.assertNotIn('Extension modules:', stderr) + def test_is_enabled(self): orig_stderr = sys.stderr try: @@ -695,6 +724,76 @@ def test_dump_traceback_later_fd(self): def test_dump_traceback_later_twice(self): self.check_dump_traceback_later(loops=2) + def test_dump_traceback_max_threads(self): + # max_threads caps the dump and writes "...\n" when truncated. + # Spawn N worker threads, dump with cap < N, and verify the + # marker is present and exactly CAP thread headers are written. + code = dedent(""" + import faulthandler + import sys + import threading + + NTHREADS = 6 + CAP = 3 + + ready = threading.Barrier(NTHREADS + 1) + stop = threading.Event() + + def worker(): + ready.wait() + stop.wait() + + threads = [threading.Thread(target=worker) for _ in range(NTHREADS)] + for t in threads: + t.start() + ready.wait() + try: + faulthandler.dump_traceback(file=sys.stderr, max_threads=CAP) + finally: + stop.set() + for t in threads: + t.join() + """).strip() + proc = script_helper.assert_python_ok('-c', code) + output = proc.err + # Truncation marker is written on its own line when the cap is hit. + self.assertIn(b"\n...\n", output) + # Cap of 3 means exactly 3 thread headers in the dump. + self.assertEqual(output.count(b"Thread 0x"), 3) + + @skip_segfault_on_android + @unittest.skipIf(support.Py_GIL_DISABLED, + "fatal-signal handler only dumps the current thread " + "when the GIL is disabled") + def test_enable_max_threads(self): + # enable(max_threads=N) caps the thread dump produced when a + # fatal signal fires. + code = dedent(""" + import faulthandler + import threading + + NTHREADS = 6 + CAP = 3 + + ready = threading.Barrier(NTHREADS + 1) + stop = threading.Event() + + def worker(): + ready.wait() + stop.wait() + + for _ in range(NTHREADS): + threading.Thread(target=worker, daemon=True).start() + ready.wait() + faulthandler.enable(max_threads=CAP) + faulthandler._sigsegv() + """).strip() + output, exitcode = self.get_output(code) + output = '\n'.join(output) + # Cap of 3 means the dump is truncated with "..." on its own line. + self.assertIn("\n...\n", output) + self.assertNotEqual(exitcode, 0) + @unittest.skipIf(not hasattr(faulthandler, "register"), "need faulthandler.register") def check_register(self, filename=False, all_threads=False, @@ -801,6 +900,46 @@ def test_register_threads(self): def test_register_chain(self): self.check_register(chain=True) + @unittest.skipIf(not hasattr(faulthandler, "register"), + "need faulthandler.register") + def test_register_max_threads(self): + # register(max_threads=N) caps the thread dump produced when + # the registered signal fires. + code = dedent(""" + import faulthandler + import signal + import threading + + NTHREADS = 6 + CAP = 3 + + ready = threading.Barrier(NTHREADS + 1) + stop = threading.Event() + + def worker(): + ready.wait() + stop.wait() + + threads = [threading.Thread(target=worker) for _ in range(NTHREADS)] + for t in threads: + t.start() + ready.wait() + try: + faulthandler.register(signal.SIGUSR1, all_threads=True, + max_threads=CAP) + signal.raise_signal(signal.SIGUSR1) + finally: + stop.set() + for t in threads: + t.join() + """).strip() + proc = script_helper.assert_python_ok('-c', code) + output = proc.err + # Cap of 3 means the dump is truncated with "..." on its own line. + self.assertIn(b"\n...\n", output) + # Cap of 3 means exactly 3 thread headers in the dump. + self.assertEqual(output.count(b"Thread 0x"), 3) + @contextmanager def check_stderr_none(self): stderr = sys.stderr diff --git a/Lib/test/test_finalization.py b/Lib/test/test_finalization.py index 42871f8a09b16b..9dd68cf8d57413 100644 --- a/Lib/test/test_finalization.py +++ b/Lib/test/test_finalization.py @@ -174,7 +174,7 @@ def test_simple(self): gc.collect() self.assert_del_calls(ids) self.assert_survivors([]) - self.assertIs(wr(), None) + self.assertIsNone(wr()) gc.collect() self.assert_del_calls(ids) self.assert_survivors([]) @@ -188,12 +188,12 @@ def test_simple_resurrect(self): gc.collect() self.assert_del_calls(ids) self.assert_survivors(ids) - self.assertIsNot(wr(), None) + self.assertIsNotNone(wr()) self.clear_survivors() gc.collect() self.assert_del_calls(ids) self.assert_survivors([]) - self.assertIs(wr(), None) + self.assertIsNone(wr()) @support.cpython_only def test_non_gc(self): @@ -265,7 +265,7 @@ def test_simple(self): gc.collect() self.assert_del_calls(ids) self.assert_survivors([]) - self.assertIs(wr(), None) + self.assertIsNone(wr()) gc.collect() self.assert_del_calls(ids) self.assert_survivors([]) @@ -276,19 +276,24 @@ def test_simple_resurrect(self): s = SelfCycleResurrector() ids = [id(s)] wr = weakref.ref(s) + wrc = weakref.ref(s, lambda x: None) del s gc.collect() self.assert_del_calls(ids) self.assert_survivors(ids) - # XXX is this desirable? - self.assertIs(wr(), None) + # This used to be None because weakrefs were cleared before + # calling finalizers. Now they are cleared after. + self.assertIsNotNone(wr()) + # A weakref with a callback is still cleared before calling + # finalizers. + self.assertIsNone(wrc()) # When trying to destroy the object a second time, __del__ # isn't called anymore (and the object isn't resurrected). self.clear_survivors() gc.collect() self.assert_del_calls(ids) self.assert_survivors([]) - self.assertIs(wr(), None) + self.assertIsNone(wr()) def test_simple_suicide(self): # Test the GC is able to deal with an object that kills its last @@ -301,11 +306,11 @@ def test_simple_suicide(self): gc.collect() self.assert_del_calls(ids) self.assert_survivors([]) - self.assertIs(wr(), None) + self.assertIsNone(wr()) gc.collect() self.assert_del_calls(ids) self.assert_survivors([]) - self.assertIs(wr(), None) + self.assertIsNone(wr()) class ChainedBase: @@ -378,18 +383,27 @@ def check_non_resurrecting_chain(self, classes): def check_resurrecting_chain(self, classes): N = len(classes) + def dummy_callback(ref): + pass with SimpleBase.test(): nodes = self.build_chain(classes) N = len(nodes) ids = [id(s) for s in nodes] survivor_ids = [id(s) for s in nodes if isinstance(s, SimpleResurrector)] wrs = [weakref.ref(s) for s in nodes] + wrcs = [weakref.ref(s, dummy_callback) for s in nodes] del nodes gc.collect() self.assert_del_calls(ids) self.assert_survivors(survivor_ids) - # XXX desirable? - self.assertEqual([wr() for wr in wrs], [None] * N) + for wr in wrs: + # These values used to be None because weakrefs were cleared + # before calling finalizers. Now they are cleared after. + self.assertIsNotNone(wr()) + for wr in wrcs: + # Weakrefs with callbacks are still cleared before calling + # finalizers. + self.assertIsNone(wr()) self.clear_survivors() gc.collect() self.assert_del_calls(ids) @@ -491,7 +505,7 @@ def test_legacy(self): self.assert_del_calls(ids) self.assert_tp_del_calls(ids) self.assert_survivors([]) - self.assertIs(wr(), None) + self.assertIsNone(wr()) gc.collect() self.assert_del_calls(ids) self.assert_tp_del_calls(ids) @@ -507,13 +521,13 @@ def test_legacy_resurrect(self): self.assert_tp_del_calls(ids) self.assert_survivors(ids) # weakrefs are cleared before tp_del is called. - self.assertIs(wr(), None) + self.assertIsNone(wr()) self.clear_survivors() gc.collect() self.assert_del_calls(ids) self.assert_tp_del_calls(ids * 2) self.assert_survivors(ids) - self.assertIs(wr(), None) + self.assertIsNone(wr()) def test_legacy_self_cycle(self): # Self-cycles with legacy finalizers end up in gc.garbage. @@ -527,11 +541,11 @@ def test_legacy_self_cycle(self): self.assert_tp_del_calls([]) self.assert_survivors([]) self.assert_garbage(ids) - self.assertIsNot(wr(), None) + self.assertIsNotNone(wr()) # Break the cycle to allow collection gc.garbage[0].ref = None self.assert_garbage([]) - self.assertIs(wr(), None) + self.assertIsNone(wr()) if __name__ == "__main__": diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 00518abcb11b46..c03b0a09f71889 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -651,6 +651,24 @@ class F(float, H): value = F('nan') self.assertEqual(hash(value), object.__hash__(value)) + def test_issue_gh143006(self): + # When comparing negative non-integer float and int with the + # same number of bits in the integer part, __neg__() in the + # int subclass returning not an int caused an assertion error. + class EvilInt(int): + def __neg__(self): + return "" + + i = -1 << 50 + f = float(i) - 0.5 + i = EvilInt(i) + self.assertFalse(f == i) + self.assertTrue(f != i) + self.assertTrue(f < i) + self.assertTrue(f <= i) + self.assertFalse(f > i) + self.assertFalse(f >= i) + @unittest.skipUnless(hasattr(float, "__getformat__"), "requires __getformat__") class FormatFunctionsTestCase(unittest.TestCase): diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py index a6523bbc518176..550faa8a174ec6 100644 --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -11,6 +11,7 @@ from test.fork_wait import ForkWait from test import support +from test.support import warnings_helper # Skip test if fork does not exist. @@ -19,6 +20,7 @@ class ForkTest(ForkWait): + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_threaded_import_lock_fork(self): """Check fork() in main thread works while a subthread is doing an import""" import_started = threading.Event() @@ -61,7 +63,7 @@ def importer(): except OSError: pass - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_nested_import_lock_fork(self): """Check fork() in main thread works while the main thread is doing an import""" exitcode = 42 diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py index 1f626d87fa6c7a..5d322cb444cfb6 100644 --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -57,10 +57,6 @@ def testcommon(formatstr, args, output=None, limit=None, overflowok=False): else: b_format = formatstr ba_format = bytearray(b_format) - b_args = [] - if not isinstance(args, tuple): - args = (args, ) - b_args = tuple(args) if output is None: b_output = ba_output = None else: @@ -69,33 +65,23 @@ def testcommon(formatstr, args, output=None, limit=None, overflowok=False): else: b_output = output ba_output = bytearray(b_output) - testformat(b_format, b_args, b_output, limit, overflowok) - testformat(ba_format, b_args, ba_output, limit, overflowok) + testformat(b_format, args, b_output, limit, overflowok) + testformat(ba_format, args, ba_output, limit, overflowok) -def test_exc(formatstr, args, exception, excmsg): - try: - testformat(formatstr, args) - except exception as exc: - if str(exc) == excmsg: - if verbose: - print("yes") - else: - if verbose: print('no') - print('Unexpected ', exception, ':', repr(str(exc))) - except: - if verbose: print('no') - print('Unexpected exception') - raise - else: - raise TestFailed('did not get expected exception: %s' % excmsg) - -def test_exc_common(formatstr, args, exception, excmsg): - # test str and bytes - test_exc(formatstr, args, exception, excmsg) - test_exc(formatstr.encode('ascii'), args, exception, excmsg) class FormatTest(unittest.TestCase): + def check_exc(self, formatstr, args, exception, excmsg): + with self.assertRaisesRegex(exception, re.escape(excmsg)): + testformat(formatstr, args) + + def check_exc_common(self, formatstr, args, exception, excmsg): + # test str and bytes + self.check_exc(formatstr, args, exception, excmsg) + if isinstance(args, dict): + args = {k.encode('ascii'): v for k, v in args.items()} + self.check_exc(formatstr.encode('ascii'), args, exception, excmsg) + def test_common_format(self): # test the format identifiers that work the same across # str, bytes, and bytearrays (integer, float, oct, hex) @@ -272,45 +258,156 @@ def test_common_format(self): if verbose: print('Testing exceptions') - test_exc_common('%', (), ValueError, "incomplete format") - test_exc_common('% %s', 1, ValueError, - "unsupported format character '%' (0x25) at index 2") + test_exc_common = self.check_exc_common + test_exc_common('abc %', (), ValueError, "stray % at position 4") + test_exc_common('abc % %s', 1, ValueError, + "stray % at position 4 or unexpected format character '%' at position 6") + test_exc_common('abc %z', 1, ValueError, + "unsupported format %z at position 4") + test_exc_common("abc %Id", 1, ValueError, + "unsupported format %I at position 4") + test_exc_common("abc %'d", 1, ValueError, + "stray % at position 4 or unexpected format character \"'\" at position 5") + test_exc_common("abc %1 d", 1, ValueError, + "stray % at position 4 or unexpected format character ' ' at position 6") + test_exc_common('abc % (x)r', {}, ValueError, + "stray % at position 4 or unexpected format character '(' at position 6") + test_exc_common('abc %((x)r', {}, ValueError, + "stray % or incomplete format key at position 4") + test_exc_common('%r %r', 1, TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%r %r', (1,), TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%r', (), TypeError, + "not enough arguments for format string (got 0)") + test_exc_common('abc %' + '9'*50 + 'r', 1, ValueError, + "width too big at position 4") + test_exc_common('abc %.' + '9'*50 + 'r', 1, ValueError, + "precision too big at position 4") + test_exc_common('%r %*r', 1, TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%r %*r', (1,), TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%*r', (1,), TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%*r', (), TypeError, + "not enough arguments for format string (got 0)") + test_exc_common('%r %.*r', 1, TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%r %.*r', (1,), TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%.*r', (1,), TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%.*r', (), TypeError, + "not enough arguments for format string (got 0)") + test_exc_common('%(x)r', 1, TypeError, + "format requires a mapping, not int") + test_exc_common('%*r', 1, TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%*r', 3.14, TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%*r', (3.14, 1), TypeError, + "format argument 1: * requires int, not float") + test_exc_common('%.*r', 1, TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%.*r', 3.14, TypeError, + "not enough arguments for format string (got 1)") + test_exc_common('%.*r', (3.14, 1), TypeError, + "format argument 1: * requires int, not float") + test_exc_common('%*r', (2**1000, 1), OverflowError, + "format argument 1: too big for width") + test_exc_common('%*r', (-2**1000, 1), OverflowError, + "format argument 1: too big for width") + test_exc_common('%.*r', (2**1000, 1), OverflowError, + "format argument 1: too big for precision") + test_exc_common('%.*r', (-2**1000, 1), OverflowError, + "format argument 1: too big for precision") test_exc_common('%d', '1', TypeError, - "%d format: a real number is required, not str") + "format argument: %d requires a real number, not str") test_exc_common('%d', b'1', TypeError, - "%d format: a real number is required, not bytes") + "format argument: %d requires a real number, not bytes") + test_exc_common('%d', ('1',), TypeError, + "format argument 1: %d requires a real number, not str") test_exc_common('%x', '1', TypeError, - "%x format: an integer is required, not str") + "format argument: %x requires an integer, not str") test_exc_common('%x', 3.14, TypeError, - "%x format: an integer is required, not float") + "format argument: %x requires an integer, not float") + test_exc_common('%x', ('1',), TypeError, + "format argument 1: %x requires an integer, not str") test_exc_common('%i', '1', TypeError, - "%i format: a real number is required, not str") + "format argument: %i requires a real number, not str") test_exc_common('%i', b'1', TypeError, - "%i format: a real number is required, not bytes") + "format argument: %i requires a real number, not bytes") + test_exc_common('%g', '1', TypeError, + "format argument: %g requires a real number, not str") + test_exc_common('%g', ('1',), TypeError, + "format argument 1: %g requires a real number, not str") def test_str_format(self): testformat("%r", "\u0378", "'\\u0378'") # non printable testformat("%a", "\u0378", "'\\u0378'") # non printable testformat("%r", "\u0374", "'\u0374'") # printable testformat("%a", "\u0374", "'\\u0374'") # printable + testformat('%(x)r', {'x': 1}, '1') # Test exception for unknown format characters, etc. if verbose: print('Testing exceptions') + test_exc = self.check_exc test_exc('abc %b', 1, ValueError, - "unsupported format character 'b' (0x62) at index 5") - #test_exc(unicode('abc %\u3000','raw-unicode-escape'), 1, ValueError, - # "unsupported format character '?' (0x3000) at index 5") - test_exc('%g', '1', TypeError, "must be real number, not str") + "unsupported format %b at position 4") + test_exc("abc %\nd", 1, ValueError, + "stray % at position 4 or unexpected format character U+000A at position 5") + test_exc("abc %\x1fd", 1, ValueError, + "stray % at position 4 or unexpected format character U+001F at position 5") + test_exc("abc %\x7fd", 1, ValueError, + "stray % at position 4 or unexpected format character U+007F at position 5") + test_exc("abc %\x80d", 1, ValueError, + "stray % at position 4 or unexpected format character U+0080 at position 5") + test_exc('abc %äd', 1, ValueError, + "stray % at position 4 or unexpected format character 'ä' (U+00E4) at position 5") + test_exc('abc %€d', 1, ValueError, + "stray % at position 4 or unexpected format character '€' (U+20AC) at position 5") test_exc('no format', '1', TypeError, - "not all arguments converted during string formatting") - test_exc('%c', -1, OverflowError, "%c arg not in range(0x110000)") + "not all arguments converted during string formatting (required 0, got 1)") + test_exc('%r', (1, 2), TypeError, + "not all arguments converted during string formatting (required 1, got 2)") + test_exc('%(x)r %r', {'x': 1}, ValueError, + "format requires a parenthesised mapping key at position 6") + test_exc('%(x)*r', {'x': 1}, ValueError, + "* cannot be used with a parenthesised mapping key at position 0") + test_exc('%(x).*r', {'x': 1}, ValueError, + "* cannot be used with a parenthesised mapping key at position 0") + test_exc('%(x)d', {'x': '1'}, TypeError, + "format argument 'x': %d requires a real number, not str") + test_exc('%(x)x', {'x': '1'}, TypeError, + "format argument 'x': %x requires an integer, not str") + test_exc('%(x)g', {'x': '1'}, TypeError, + "format argument 'x': %g requires a real number, not str") + test_exc('%c', -1, OverflowError, + "format argument: %c argument not in range(0x110000)") + test_exc('%c', (-1,), OverflowError, + "format argument 1: %c argument not in range(0x110000)") + test_exc('%(x)c', {'x': -1}, OverflowError, + "format argument 'x': %c argument not in range(0x110000)") test_exc('%c', sys.maxunicode+1, OverflowError, - "%c arg not in range(0x110000)") - #test_exc('%c', 2**128, OverflowError, "%c arg not in range(0x110000)") - test_exc('%c', 3.14, TypeError, "%c requires an int or a unicode character, not float") - test_exc('%c', 'ab', TypeError, "%c requires an int or a unicode character, not a string of length 2") - test_exc('%c', b'x', TypeError, "%c requires an int or a unicode character, not bytes") + "format argument: %c argument not in range(0x110000)") + test_exc('%c', 2**128, OverflowError, + "format argument: %c argument not in range(0x110000)") + test_exc('%c', 3.14, TypeError, + "format argument: %c requires an integer or a unicode character, not float") + test_exc('%c', (3.14,), TypeError, + "format argument 1: %c requires an integer or a unicode character, not float") + test_exc('%(x)c', {'x': 3.14}, TypeError, + "format argument 'x': %c requires an integer or a unicode character, not float") + test_exc('%c', 'ab', TypeError, + "format argument: %c requires an integer or a unicode character, not a string of length 2") + test_exc('%c', ('ab',), TypeError, + "format argument 1: %c requires an integer or a unicode character, not a string of length 2") + test_exc('%(x)c', {'x': 'ab'}, TypeError, + "format argument 'x': %c requires an integer or a unicode character, not a string of length 2") + test_exc('%c', b'x', TypeError, + "format argument: %c requires an integer or a unicode character, not bytes") if maxsize == 2**31-1: # crashes 2.2.1 and earlier: @@ -355,36 +452,84 @@ def __bytes__(self): testcommon(b"%r", b"ghi", b"b'ghi'") testcommon(b"%r", "jkl", b"'jkl'") testcommon(b"%r", "\u0544", b"'\\u0544'") + testcommon(b'%(x)r', {b'x': 1}, b'1') # Test exception for unknown format characters, etc. if verbose: print('Testing exceptions') - test_exc(b'%g', '1', TypeError, "float argument required, not str") - test_exc(b'%g', b'1', TypeError, "float argument required, not bytes") + test_exc = self.check_exc + test_exc(b"abc %\nd", 1, ValueError, + "stray % at position 4 or unexpected format character with code 0x0a at position 5") + test_exc(b"abc %'d", 1, ValueError, + "stray % at position 4 or unexpected format character \"'\" at position 5") + test_exc(b"abc %\x1fd", 1, ValueError, + "stray % at position 4 or unexpected format character with code 0x1f at position 5") + test_exc(b"abc %\x7fd", 1, ValueError, + "stray % at position 4 or unexpected format character with code 0x7f at position 5") + test_exc(b"abc %\x80d", 1, ValueError, + "stray % at position 4 or unexpected format character with code 0x80 at position 5") test_exc(b'no format', 7, TypeError, - "not all arguments converted during bytes formatting") + "not all arguments converted during bytes formatting (required 0, got 1)") test_exc(b'no format', b'1', TypeError, - "not all arguments converted during bytes formatting") + "not all arguments converted during bytes formatting (required 0, got 1)") test_exc(b'no format', bytearray(b'1'), TypeError, - "not all arguments converted during bytes formatting") + "not all arguments converted during bytes formatting (required 0, got 1)") + test_exc(b'%r', (1, 2), TypeError, + "not all arguments converted during bytes formatting (required 1, got 2)") + test_exc(b'%(x)r %r', {b'x': 1}, ValueError, + "format requires a parenthesised mapping key at position 6") + test_exc(b'%(x)*r', {b'x': 1}, ValueError, + "* cannot be used with a parenthesised mapping key at position 0") + test_exc(b'%(x).*r', {b'x': 1}, ValueError, + "* cannot be used with a parenthesised mapping key at position 0") + test_exc(b'%(x)d', {b'x': '1'}, TypeError, + "format argument b'x': %d requires a real number, not str") + test_exc(b'%(x)x', {b'x': '1'}, TypeError, + "format argument b'x': %x requires an integer, not str") + test_exc(b'%(x)g', {b'x': '1'}, TypeError, + "format argument b'x': %g requires a real number, not str") test_exc(b"%c", -1, OverflowError, - "%c arg not in range(256)") + "format argument: %c argument not in range(256)") + test_exc(b"%c", (-1,), OverflowError, + "format argument 1: %c argument not in range(256)") + test_exc(b"%(x)c", {b'x': -1}, OverflowError, + "format argument b'x': %c argument not in range(256)") test_exc(b"%c", 256, OverflowError, - "%c arg not in range(256)") + "format argument: %c argument not in range(256)") test_exc(b"%c", 2**128, OverflowError, - "%c arg not in range(256)") + "format argument: %c argument not in range(256)") test_exc(b"%c", b"Za", TypeError, - "%c requires an integer in range(256) or a single byte, not a bytes object of length 2") + "format argument: %c requires an integer in range(256) or a single byte, not a bytes object of length 2") + test_exc(b"%c", (b"Za",), TypeError, + "format argument 1: %c requires an integer in range(256) or a single byte, not a bytes object of length 2") + test_exc(b"%(x)c", {b'x': b"Za"}, TypeError, + "format argument b'x': %c requires an integer in range(256) or a single byte, not a bytes object of length 2") + test_exc(b"%c", bytearray(b"Za"), TypeError, + "format argument: %c requires an integer in range(256) or a single byte, not a bytearray object of length 2") + test_exc(b"%c", (bytearray(b"Za"),), TypeError, + "format argument 1: %c requires an integer in range(256) or a single byte, not a bytearray object of length 2") + test_exc(b"%(x)c", {b'x': bytearray(b"Za")}, TypeError, + "format argument b'x': %c requires an integer in range(256) or a single byte, not a bytearray object of length 2") test_exc(b"%c", "Y", TypeError, - "%c requires an integer in range(256) or a single byte, not str") + "format argument: %c requires an integer in range(256) or a single byte, not str") test_exc(b"%c", 3.14, TypeError, - "%c requires an integer in range(256) or a single byte, not float") + "format argument: %c requires an integer in range(256) or a single byte, not float") + test_exc(b"%c", (3.14,), TypeError, + "format argument 1: %c requires an integer in range(256) or a single byte, not float") + test_exc(b"%(x)c", {b'x': 3.14}, TypeError, + "format argument b'x': %c requires an integer in range(256) or a single byte, not float") test_exc(b"%b", "Xc", TypeError, - "%b requires a bytes-like object, " - "or an object that implements __bytes__, not 'str'") + "format argument: %b requires a bytes-like object, " + "or an object that implements __bytes__, not str") + test_exc(b"%b", ("Xc",), TypeError, + "format argument 1: %b requires a bytes-like object, " + "or an object that implements __bytes__, not str") + test_exc(b"%(x)b", {b'x': "Xc"}, TypeError, + "format argument b'x': %b requires a bytes-like object, " + "or an object that implements __bytes__, not str") test_exc(b"%s", "Wd", TypeError, - "%b requires a bytes-like object, " - "or an object that implements __bytes__, not 'str'") + "format argument: %b requires a bytes-like object, " + "or an object that implements __bytes__, not str") if maxsize == 2**31-1: # crashes 2.2.1 and earlier: @@ -626,7 +771,7 @@ def test_specifier_z_error(self): with self.assertRaisesRegex(ValueError, error_msg): f"{'x':zs}" # can't apply to string - error_msg = re.escape("unsupported format character 'z'") + error_msg = re.escape("unsupported format %z at position 0") with self.assertRaisesRegex(ValueError, error_msg): "%z.1f" % 0 # not allowed in old style string interpolation with self.assertRaisesRegex(ValueError, error_msg): diff --git a/Lib/test/test_free_threading/test_bisect.py b/Lib/test/test_free_threading/test_bisect.py new file mode 100644 index 00000000000000..bd7732da035257 --- /dev/null +++ b/Lib/test/test_free_threading/test_bisect.py @@ -0,0 +1,56 @@ +import unittest +from test.support import import_helper, threading_helper +import random + +py_bisect = import_helper.import_fresh_module('bisect', blocked=['_bisect']) +c_bisect = import_helper.import_fresh_module('bisect', fresh=['_bisect']) + + +NTHREADS = 4 +OBJECT_COUNT = 500 + + +class TestBase: + def do_racing_insort(self, insert_method): + def insert(data): + for _ in range(OBJECT_COUNT): + x = random.randint(-OBJECT_COUNT, OBJECT_COUNT) + insert_method(data, x) + + data = list(range(OBJECT_COUNT)) + threading_helper.run_concurrently( + worker_func=insert, args=(data,), nthreads=NTHREADS + ) + if False: + # These functions are not thread-safe and so the list can become + # unsorted. However, we don't want Python to crash if these + # functions are used concurrently on the same sequence. This + # should also not produce any TSAN warnings. + self.assertTrue(self.is_sorted_ascending(data)) + + def test_racing_insert_right(self): + self.do_racing_insort(self.mod.insort_right) + + def test_racing_insert_left(self): + self.do_racing_insort(self.mod.insort_left) + + @staticmethod + def is_sorted_ascending(lst): + """ + Check if the list is sorted in ascending order (non-decreasing). + """ + return all(lst[i - 1] <= lst[i] for i in range(1, len(lst))) + + +@threading_helper.requires_working_threading() +class TestPyBisect(unittest.TestCase, TestBase): + mod = py_bisect + + +@threading_helper.requires_working_threading() +class TestCBisect(unittest.TestCase, TestBase): + mod = c_bisect + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_bz2.py b/Lib/test/test_free_threading/test_bz2.py new file mode 100644 index 00000000000000..3bb531ec04c6c9 --- /dev/null +++ b/Lib/test/test_free_threading/test_bz2.py @@ -0,0 +1,64 @@ +import unittest + +from test.support import import_helper, threading_helper +from test.support.threading_helper import run_concurrently + +bz2 = import_helper.import_module("bz2") +from bz2 import BZ2Compressor, BZ2Decompressor + +from test.test_bz2 import ext_decompress, BaseTest + + +NTHREADS = 10 +TEXT = BaseTest.TEXT + + +@threading_helper.requires_working_threading() +class TestBZ2(unittest.TestCase): + def test_compressor(self): + bz2c = BZ2Compressor() + + def worker(): + # it should return empty bytes as it buffers data internally + data = bz2c.compress(TEXT) + self.assertEqual(data, b"") + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + data = bz2c.flush() + # The decompressed data should be TEXT repeated NTHREADS times + decompressed = ext_decompress(data) + self.assertEqual(decompressed, TEXT * NTHREADS) + + def test_decompressor(self): + chunk_size = 128 + chunks = [bytes([ord("a") + i]) * chunk_size for i in range(NTHREADS)] + input_data = b"".join(chunks) + compressed = bz2.compress(input_data) + + bz2d = BZ2Decompressor() + output = [] + + def worker(): + data = bz2d.decompress(compressed, chunk_size) + self.assertEqual(len(data), chunk_size) + output.append(data) + # Read attributes concurrently with other threads decompressing + self.assertIsInstance(bz2d.eof, bool) + self.assertIsInstance(bz2d.needs_input, bool) + self.assertIsInstance(bz2d.unused_data, bytes) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + self.assertEqual(len(output), NTHREADS) + # Verify the expected chunks (order doesn't matter due to append race) + self.assertEqual(set(output), set(chunks)) + self.assertTrue(bz2d.eof) + self.assertFalse(bz2d.needs_input) + # Each thread added full compressed data to the buffer, but only 1 copy + # is consumed to produce the output. The rest remains as unused_data. + self.assertEqual( + len(bz2d.unused_data), len(compressed) * (NTHREADS - 1) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_capi.py b/Lib/test/test_free_threading/test_capi.py new file mode 100644 index 00000000000000..146d7cfc97adb7 --- /dev/null +++ b/Lib/test/test_free_threading/test_capi.py @@ -0,0 +1,47 @@ +import ctypes +import sys +import unittest + +from test.support import threading_helper +from test.support.threading_helper import run_concurrently + + +_PyImport_AddModuleRef = ctypes.pythonapi.PyImport_AddModuleRef +_PyImport_AddModuleRef.argtypes = (ctypes.c_char_p,) +_PyImport_AddModuleRef.restype = ctypes.py_object + + +@threading_helper.requires_working_threading() +class TestImportCAPI(unittest.TestCase): + def test_pyimport_addmoduleref_thread_safe(self): + # gh-137422: Concurrent calls to PyImport_AddModuleRef with the same + # module name must return the same module object. + + NUM_ITERS = 10 + NTHREADS = 4 + + module_name = f"test_free_threading_addmoduleref_{id(self)}" + module_name_bytes = module_name.encode() + sys.modules.pop(module_name, None) + results = [] + + def worker(): + module = _PyImport_AddModuleRef(module_name_bytes) + results.append(module) + + for _ in range(NUM_ITERS): + try: + run_concurrently(worker_func=worker, nthreads=NTHREADS) + self.assertEqual(len(results), NTHREADS) + reference = results[0] + for module in results[1:]: + self.assertIs(module, reference) + self.assertIn(module_name, sys.modules) + self.assertIs(sys.modules[module_name], reference) + finally: + results.clear() + sys.modules.pop(module_name, None) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_code.py b/Lib/test/test_free_threading/test_code.py index a5136a3ba4edc7..2fc5eea3773c39 100644 --- a/Lib/test/test_free_threading/test_code.py +++ b/Lib/test/test_free_threading/test_code.py @@ -1,9 +1,41 @@ import unittest +try: + import ctypes +except ImportError: + ctypes = None + from threading import Thread from unittest import TestCase from test.support import threading_helper +from test.support.threading_helper import run_concurrently + +if ctypes is not None: + capi = ctypes.pythonapi + + freefunc = ctypes.CFUNCTYPE(None, ctypes.c_voidp) + + RequestCodeExtraIndex = capi.PyUnstable_Eval_RequestCodeExtraIndex + RequestCodeExtraIndex.argtypes = (freefunc,) + RequestCodeExtraIndex.restype = ctypes.c_ssize_t + + SetExtra = capi.PyUnstable_Code_SetExtra + SetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp) + SetExtra.restype = ctypes.c_int + + GetExtra = capi.PyUnstable_Code_GetExtra + GetExtra.argtypes = ( + ctypes.py_object, + ctypes.c_ssize_t, + ctypes.POINTER(ctypes.c_voidp), + ) + GetExtra.restype = ctypes.c_int + +# Note: each call to RequestCodeExtraIndex permanently allocates a slot +# (the counter is monotonically increasing), up to MAX_CO_EXTRA_USERS (255). +NTHREADS = 20 + @threading_helper.requires_working_threading() class TestCode(TestCase): @@ -25,6 +57,83 @@ def run_in_thread(): for thread in threads: thread.join() + @unittest.skipUnless(ctypes, "ctypes is required") + def test_request_code_extra_index_concurrent(self): + """Test concurrent calls to RequestCodeExtraIndex""" + results = [] + + def worker(): + idx = RequestCodeExtraIndex(freefunc(0)) + self.assertGreaterEqual(idx, 0) + results.append(idx) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + + # Every thread must get a unique index. + self.assertEqual(len(results), NTHREADS) + self.assertEqual(len(set(results)), NTHREADS) + + @unittest.skipUnless(ctypes, "ctypes is required") + def test_code_extra_all_ops_concurrent(self): + """Test concurrent RequestCodeExtraIndex + SetExtra + GetExtra""" + LOOP = 100 + + def f(): + pass + + code = f.__code__ + + def worker(): + idx = RequestCodeExtraIndex(freefunc(0)) + self.assertGreaterEqual(idx, 0) + + for i in range(LOOP): + ret = SetExtra(code, idx, ctypes.c_voidp(i + 1)) + self.assertEqual(ret, 0) + + for _ in range(LOOP): + extra = ctypes.c_voidp() + ret = GetExtra(code, idx, extra) + self.assertEqual(ret, 0) + # The slot was set by this thread, so the value must + # be the last one written. + self.assertEqual(extra.value, LOOP) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + + @unittest.skipUnless(ctypes, "ctypes is required") + def test_code_extra_set_get_concurrent(self): + """Test concurrent SetExtra + GetExtra on a shared index""" + LOOP = 100 + + def f(): + pass + + code = f.__code__ + + idx = RequestCodeExtraIndex(freefunc(0)) + self.assertGreaterEqual(idx, 0) + + def worker(): + for i in range(LOOP): + ret = SetExtra(code, idx, ctypes.c_voidp(i + 1)) + self.assertEqual(ret, 0) + + for _ in range(LOOP): + extra = ctypes.c_voidp() + ret = GetExtra(code, idx, extra) + self.assertEqual(ret, 0) + # Value is set by any writer thread. + self.assertTrue(1 <= extra.value <= LOOP) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + + # Every thread's last write is LOOP, so the final value must be LOOP. + extra = ctypes.c_voidp() + ret = GetExtra(code, idx, extra) + self.assertEqual(ret, 0) + self.assertEqual(extra.value, LOOP) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_free_threading/test_collections.py b/Lib/test/test_free_threading/test_collections.py new file mode 100644 index 00000000000000..849b0480e232fc --- /dev/null +++ b/Lib/test/test_free_threading/test_collections.py @@ -0,0 +1,53 @@ +import unittest +from collections import deque +from copy import copy +from test.support import threading_helper + +threading_helper.requires_working_threading(module=True) + + +class TestDeque(unittest.TestCase): + def test_copy_race(self): + # gh-144809: Test that deque copy is thread safe. It previously + # could raise a "deque mutated during iteration" error. + d = deque(range(100)) + + def mutate(): + for i in range(1000): + d.append(i) + if len(d) > 200: + d.popleft() + + def copy_loop(): + for _ in range(1000): + copy(d) + + threading_helper.run_concurrently([mutate, copy_loop]) + + def test_index_race_in_ac(self): + # gh-150750: There was a c_default specified as `Py_SIZE(self)`, + # it was used without a critical section. + + d = deque(range(100)) + + def index(): + for _ in range(10000): + try: + d.index(50) + except ValueError: + pass + + def mutate(): + for _ in range(10000): + d.append(0) + d.clear() + d.extend(range(100)) + d.appendleft(-1) + + threading_helper.run_concurrently( + [index, *[mutate for _ in range(3)]], + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_cprofile.py b/Lib/test/test_free_threading/test_cprofile.py new file mode 100644 index 00000000000000..361b800d6b9029 --- /dev/null +++ b/Lib/test/test_free_threading/test_cprofile.py @@ -0,0 +1,43 @@ +import unittest + +from test.support import threading_helper + +import cProfile +import pstats + + +NTHREADS = 10 +INSERT_PER_THREAD = 1000 + + +@threading_helper.requires_working_threading() +class TestCProfile(unittest.TestCase): + def test_cprofile_racing_list_insert(self): + def list_insert(lst): + for i in range(INSERT_PER_THREAD): + lst.insert(0, i) + + lst = [] + + with cProfile.Profile() as pr: + threading_helper.run_concurrently( + worker_func=list_insert, nthreads=NTHREADS, args=(lst,) + ) + pr.create_stats() + ps = pstats.Stats(pr) + stats_profile = ps.get_stats_profile() + list_insert_profile = stats_profile.func_profiles[ + "" + ] + # Even though there is no explicit recursive call to insert, + # cProfile may record some calls as recursive due to limitations + # in its handling of multithreaded programs. This issue is not + # directly related to FT Python itself; however, it tends to be + # more noticeable when using FT Python. Therefore, consider only + # the calls section and disregard the recursive part. + list_insert_ncalls = list_insert_profile.ncalls.split("/")[0] + self.assertEqual( + int(list_insert_ncalls), NTHREADS * INSERT_PER_THREAD + ) + + self.assertEqual(len(lst), NTHREADS * INSERT_PER_THREAD) diff --git a/Lib/test/test_free_threading/test_csv.py b/Lib/test/test_free_threading/test_csv.py new file mode 100644 index 00000000000000..beb4510a1281b8 --- /dev/null +++ b/Lib/test/test_free_threading/test_csv.py @@ -0,0 +1,50 @@ +import csv +import io +import unittest + +from test.support import threading_helper +from test.support.threading_helper import run_concurrently + + +NTHREADS = 10 + + +@threading_helper.requires_working_threading() +class TestCSV(unittest.TestCase): + def test_concurrent_reader_next(self): + input_rows = [f"{i},{i},{i}" for i in range(50)] + input_stream = io.StringIO("\n".join(input_rows)) + reader = csv.reader(input_stream) + output_rows = [] + + def read_row(): + for row in reader: + self.assertEqual(len(row), 3) + output_rows.append(",".join(row)) + + run_concurrently(worker_func=read_row, nthreads=NTHREADS) + self.assertSetEqual(set(input_rows), set(output_rows)) + + def test_concurrent_writer_writerow(self): + output_stream = io.StringIO() + writer = csv.writer(output_stream) + row_per_thread = 10 + expected_rows = [] + + def write_row(): + for i in range(row_per_thread): + writer.writerow([i, i, i]) + expected_rows.append(f"{i},{i},{i}") + + run_concurrently(worker_func=write_row, nthreads=NTHREADS) + + # Rewind to the start of the stream and parse the rows + output_stream.seek(0) + output_rows = [line.strip() for line in output_stream.readlines()] + + self.assertEqual(len(output_rows), NTHREADS * row_per_thread) + self.assertListEqual(sorted(output_rows), sorted(expected_rows)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_dbm_gnu.py b/Lib/test/test_free_threading/test_dbm_gnu.py new file mode 100644 index 00000000000000..d2d7b78be712f7 --- /dev/null +++ b/Lib/test/test_free_threading/test_dbm_gnu.py @@ -0,0 +1,79 @@ +import unittest + +from test.support import import_helper, os_helper, threading_helper +from test.support.threading_helper import run_concurrently + +import threading + +gdbm = import_helper.import_module("dbm.gnu") + +NTHREADS = 10 +KEY_PER_THREAD = 1000 + +gdbm_filename = "test_gdbm_file" + + +@threading_helper.requires_working_threading() +class TestGdbm(unittest.TestCase): + def test_racing_dbm_gnu(self): + def gdbm_multi_op_worker(db): + # Each thread sets, gets, and iterates + tid = threading.get_ident() + + # Insert keys + for i in range(KEY_PER_THREAD): + db[f"key_{tid}_{i}"] = f"value_{tid}_{i}" + + for i in range(KEY_PER_THREAD): + # Keys and values are stored as bytes; encode values for + # comparison + key = f"key_{tid}_{i}" + value = f"value_{tid}_{i}".encode() + self.assertIn(key, db) + self.assertEqual(db[key], value) + self.assertEqual(db.get(key), value) + self.assertIsNone(db.get("not_exist")) + with self.assertRaises(KeyError): + db["not_exist"] + + # Iterate over the database keys and verify only those belonging + # to this thread. Other threads may concurrently delete their keys. + key_prefix = f"key_{tid}".encode() + key = db.firstkey() + key_count = 0 + while key: + if key.startswith(key_prefix): + self.assertIn(key, db) + key_count += 1 + key = db.nextkey(key) + + # Can't assert key_count == KEY_PER_THREAD because concurrent + # threads may insert or delete keys during iteration. This can + # cause keys to be skipped or counted multiple times, making the + # count unreliable. + # See: https://www.gnu.org.ua/software/gdbm/manual/Sequential.html + # self.assertEqual(key_count, KEY_PER_THREAD) + + # Delete this thread's keys + for i in range(KEY_PER_THREAD): + key = f"key_{tid}_{i}" + del db[key] + self.assertNotIn(key, db) + with self.assertRaises(KeyError): + del db["not_exist"] + + # Re-insert keys + for i in range(KEY_PER_THREAD): + db[f"key_{tid}_{i}"] = f"value_{tid}_{i}" + + with os_helper.temp_dir() as tmpdirname: + db = gdbm.open(f"{tmpdirname}/{gdbm_filename}", "c") + run_concurrently( + worker_func=gdbm_multi_op_worker, nthreads=NTHREADS, args=(db,) + ) + self.assertEqual(len(db), NTHREADS * KEY_PER_THREAD) + db.close() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_dict.py b/Lib/test/test_free_threading/test_dict.py index 5d5d4e226caa40..ad23290a92ab34 100644 --- a/Lib/test/test_free_threading/test_dict.py +++ b/Lib/test/test_free_threading/test_dict.py @@ -245,5 +245,96 @@ def reader(): with threading_helper.start_threads([t1, t2]): pass + @unittest.skipIf(_testcapi is None, "requires _testcapi") + def test_racing_watch_unwatch_dict(self): + # gh-148393: race between PyDict_Watch / PyDict_Unwatch + # and concurrent dict mutation reading _ma_watcher_tag. + wid = _testcapi.add_dict_watcher(0) + try: + d = {} + ITERS = 1000 + + def writer(): + for i in range(ITERS): + d[i] = i + del d[i] + + def watcher(): + for _ in range(ITERS): + _testcapi.watch_dict(wid, d) + _testcapi.unwatch_dict(wid, d) + + threading_helper.run_concurrently([writer, watcher]) + finally: + _testcapi.clear_dict_watcher(wid) + + def test_racing_split_dict_clear_and_lookup(self): + class C: + pass + + keys = [f"a{i}" for i in range(16)] + + def make_split_nonembedded(): + inst = C() + for key in keys: + setattr(inst, key, keys.index(key)) + # dict.copy() of a split instance dict yields a split table + # with non-embedded values + return inst.__dict__.copy() + + d = make_split_nonembedded() + + def clearer(): + for _ in range(1000): + d.clear() + d.update(make_split_nonembedded()) + + def reader(): + for _ in range(1000): + for k in keys: + d.get(k) + + threading_helper.run_concurrently([clearer, reader, reader]) + + def test_racing_embedded_values_clear_and_lookup(self): + class C: + pass + + obj = C() + def writer(): + for _ in range(1000): + obj.x = 1 + obj.y = 2 + obj.z = 3 + obj.__dict__.clear() + + def reader(): + for _ in range(1000): + obj.__dict__.get('x') + + threading_helper.run_concurrently([writer, reader, reader]) + + def test_racing_dict_update_and_method_lookup(self): + # gh-144295: test race between dict modifications and method lookups. + # Uses BytesIO because the race requires a type without Py_TPFLAGS_INLINE_VALUES + # for the _PyDict_GetMethodStackRef code path. + import io + obj = io.BytesIO() + + def writer(): + for _ in range(10000): + obj.x = 1 + del obj.x + + def reader(): + for _ in range(10000): + obj.getvalue() + + t1 = Thread(target=writer) + t2 = Thread(target=reader) + + with threading_helper.start_threads([t1, t2]): + pass + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_free_threading/test_dict_watcher.py b/Lib/test/test_free_threading/test_dict_watcher.py new file mode 100644 index 00000000000000..6a6843f9344f64 --- /dev/null +++ b/Lib/test/test_free_threading/test_dict_watcher.py @@ -0,0 +1,89 @@ +import unittest + +from test.support import import_helper, threading_helper + +_testcapi = import_helper.import_module("_testcapi") + +ITERS = 100 +NTHREADS = 20 + + +@threading_helper.requires_working_threading() +class TestDictWatcherThreadSafety(unittest.TestCase): + # Watcher kinds from _testcapi + EVENTS = 0 # appends dict events as strings to global event list + + def test_concurrent_add_clear_watchers(self): + """Race AddWatcher and ClearWatcher from multiple threads. + + Uses more threads than available watcher slots (5 user slots out + of DICT_MAX_WATCHERS=8). + """ + results = [] + + def worker(): + for _ in range(ITERS): + try: + wid = _testcapi.add_dict_watcher(self.EVENTS) + except RuntimeError: + continue # All slots taken + self.assertGreaterEqual(wid, 0) + results.append(wid) + _testcapi.clear_dict_watcher(wid) + + threading_helper.run_concurrently(worker, NTHREADS) + + # Verify at least some watchers were successfully added + self.assertGreater(len(results), 0) + + def test_concurrent_watch_unwatch(self): + """Race Watch and Unwatch on the same dict from multiple threads.""" + wid = _testcapi.add_dict_watcher(self.EVENTS) + dicts = [{} for _ in range(10)] + + def worker(): + for _ in range(ITERS): + for d in dicts: + _testcapi.watch_dict(wid, d) + for d in dicts: + _testcapi.unwatch_dict(wid, d) + + try: + threading_helper.run_concurrently(worker, NTHREADS) + + # Verify watching still works after concurrent watch/unwatch + _testcapi.watch_dict(wid, dicts[0]) + dicts[0]["key"] = "value" + events = _testcapi.get_dict_watcher_events() + self.assertIn("new:key:value", events) + finally: + _testcapi.clear_dict_watcher(wid) + + def test_concurrent_modify_watched_dict(self): + """Race dict mutations (triggering callbacks) with watch/unwatch.""" + wid = _testcapi.add_dict_watcher(self.EVENTS) + d = {} + _testcapi.watch_dict(wid, d) + + def mutator(): + for i in range(ITERS): + d[f"key_{i}"] = i + d.pop(f"key_{i}", None) + + def toggler(): + for i in range(ITERS): + _testcapi.watch_dict(wid, d) + d[f"toggler_{i}"] = i + _testcapi.unwatch_dict(wid, d) + + workers = [mutator, toggler] * (NTHREADS // 2) + try: + threading_helper.run_concurrently(workers) + events = _testcapi.get_dict_watcher_events() + self.assertGreater(len(events), 0) + finally: + _testcapi.clear_dict_watcher(wid) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_exceptions.py b/Lib/test/test_free_threading/test_exceptions.py new file mode 100644 index 00000000000000..61146f29c7868e --- /dev/null +++ b/Lib/test/test_free_threading/test_exceptions.py @@ -0,0 +1,16 @@ +import unittest +import copy + +from test.support import threading_helper + +threading_helper.requires_working_threading(module=True) +class ExceptionTests(unittest.TestCase): + def test_setstate_data_race(self): + E = Exception() + + def func(): + for i in range(100): + setattr(E, 'x', i) + copy.copy(E) + + threading_helper.run_concurrently(func, nthreads=4) diff --git a/Lib/test/test_free_threading/test_frame.py b/Lib/test/test_free_threading/test_frame.py new file mode 100644 index 00000000000000..bea49df557aa2c --- /dev/null +++ b/Lib/test/test_free_threading/test_frame.py @@ -0,0 +1,151 @@ +import functools +import sys +import threading +import unittest + +from test.support import threading_helper + +threading_helper.requires_working_threading(module=True) + + +def run_with_frame(funcs, runner=None, iters=10): + """Run funcs with a frame from another thread that is currently executing. + + Args: + funcs: A function or list of functions that take a frame argument + runner: Optional function to run in the executor thread. If provided, + it will be called and should return eventually. The frame + passed to funcs will be the runner's frame. + iters: Number of iterations each func should run + """ + if not isinstance(funcs, list): + funcs = [funcs] + + frame_var = None + e = threading.Event() + b = threading.Barrier(len(funcs) + 1) + + if runner is None: + def runner(): + j = 0 + for i in range(100): + j += i + + def executor(): + nonlocal frame_var + frame_var = sys._getframe() + e.set() + b.wait() + runner() + + def func_wrapper(func): + e.wait() + frame = frame_var + b.wait() + for _ in range(iters): + func(frame) + + test_funcs = [functools.partial(func_wrapper, f) for f in funcs] + threading_helper.run_concurrently([executor] + test_funcs) + + +class TestFrameRaces(unittest.TestCase): + def test_concurrent_f_lasti(self): + run_with_frame(lambda frame: frame.f_lasti) + + def test_concurrent_f_lineno(self): + run_with_frame(lambda frame: frame.f_lineno) + + def test_concurrent_f_code(self): + run_with_frame(lambda frame: frame.f_code) + + def test_concurrent_f_back(self): + run_with_frame(lambda frame: frame.f_back) + + def test_concurrent_f_globals(self): + run_with_frame(lambda frame: frame.f_globals) + + def test_concurrent_f_builtins(self): + run_with_frame(lambda frame: frame.f_builtins) + + def test_concurrent_f_locals(self): + run_with_frame(lambda frame: frame.f_locals) + + def test_concurrent_f_trace_read(self): + run_with_frame(lambda frame: frame.f_trace) + + def test_concurrent_f_trace_opcodes_read(self): + run_with_frame(lambda frame: frame.f_trace_opcodes) + + def test_concurrent_repr(self): + run_with_frame(lambda frame: repr(frame)) + + def test_concurrent_f_trace_write(self): + def trace_func(frame, event, arg): + return trace_func + + def writer(frame): + frame.f_trace = trace_func + frame.f_trace = None + + run_with_frame(writer) + + def test_concurrent_f_trace_read_write(self): + # Test concurrent reads and writes of f_trace on a live frame. + def trace_func(frame, event, arg): + return trace_func + + def reader(frame): + _ = frame.f_trace + + def writer(frame): + frame.f_trace = trace_func + frame.f_trace = None + + run_with_frame([reader, writer, reader, writer]) + + def test_concurrent_f_trace_opcodes_write(self): + def writer(frame): + frame.f_trace_opcodes = True + frame.f_trace_opcodes = False + + run_with_frame(writer) + + def test_concurrent_f_trace_opcodes_read_write(self): + # Test concurrent reads and writes of f_trace_opcodes on a live frame. + def reader(frame): + _ = frame.f_trace_opcodes + + def writer(frame): + frame.f_trace_opcodes = True + frame.f_trace_opcodes = False + + run_with_frame([reader, writer, reader, writer]) + + def test_concurrent_frame_clear(self): + # Test race between frame.clear() and attribute reads. + def create_frame(): + x = 1 + y = 2 + return sys._getframe() + + frame = create_frame() + + def reader(): + for _ in range(10): + try: + _ = frame.f_locals + _ = frame.f_code + _ = frame.f_lineno + except ValueError: + # Frame may be cleared + pass + + def clearer(): + frame.clear() + + threading_helper.run_concurrently([reader, reader, clearer]) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_gc.py b/Lib/test/test_free_threading/test_gc.py index 3b83e0431efa6b..39901023450940 100644 --- a/Lib/test/test_free_threading/test_gc.py +++ b/Lib/test/test_free_threading/test_gc.py @@ -62,6 +62,97 @@ def mutator_thread(): with threading_helper.start_threads(gcs + mutators): pass + def test_freeze_object_in_brc_queue(self): + # GH-142975: Freezing objects in the BRC queue could result in some + # objects having a zero refcount without being deallocated. + + class Weird: + # We need a destructor to trigger the check for object resurrection + def __del__(self): + pass + + # This is owned by the main thread, so the subthread will have to increment + # this object's reference count. + weird = Weird() + + def evil(): + gc.freeze() + + # Decrement the reference count from this thread, which will trigger the + # slow path during resurrection and add our weird object to the BRC queue. + nonlocal weird + del weird + + # Collection will merge the object's reference count and make it zero. + gc.collect() + + # Unfreeze the object, making it visible to the GC. + gc.unfreeze() + gc.collect() + + thread = Thread(target=evil) + thread.start() + thread.join() + + def test_set_threshold(self): + # GH-148613: Setting the GC threshold from another thread could cause a + # race between the `gc_should_collect` and `gc_set_threshold` functions. + NUM_THREADS = 8 + NUM_ITERS = 100_000 + barrier = threading.Barrier(NUM_THREADS) + + class CyclicReference: + def __init__(self): + self.r = self + + def allocator(): + barrier.wait() + for _ in range(NUM_ITERS): + CyclicReference() + + def setter(): + barrier.wait() + for i in range(NUM_ITERS): + gc.set_threshold(100 + (i % 100), 10 + (i % 10), 10 + (i % 10)) + + current_threshold = gc.get_threshold() + try: + threads = [Thread(target=allocator) for _ in range(NUM_THREADS - 1)] + threads.append(Thread(target=setter)) + with threading_helper.start_threads(threads): + pass + finally: + gc.set_threshold(*current_threshold) + + def test_get_count(self): + class CyclicReference: + def __init__(self): + self.ref = self + + NUM_ALLOCATORS = 7 + NUM_READERS = 1 + NUM_THREADS = NUM_ALLOCATORS + NUM_READERS + NUM_ITERS = 1000 + + barrier = threading.Barrier(NUM_THREADS) + + def allocator(): + barrier.wait() + for _ in range(NUM_ITERS): + CyclicReference() + + + def reader(): + barrier.wait() + for _ in range(NUM_ITERS): + gc.get_count() + + threads = [Thread(target=allocator) for _ in range(NUM_ALLOCATORS)] + threads.extend(Thread(target=reader) for _ in range(NUM_READERS)) + + with threading_helper.start_threads(threads): + pass + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_free_threading/test_generators.py b/Lib/test/test_free_threading/test_generators.py index d01675eb38b370..2b41e28896f5a8 100644 --- a/Lib/test/test_free_threading/test_generators.py +++ b/Lib/test/test_free_threading/test_generators.py @@ -1,4 +1,6 @@ import concurrent.futures +import itertools +import threading import unittest from threading import Barrier from unittest import TestCase @@ -49,3 +51,109 @@ def test_concurrent_write(self): self.concurrent_write_with_func(func=set_gen_name) with self.subTest(func=set_gen_qualname): self.concurrent_write_with_func(func=set_gen_qualname) + + def test_concurrent_send(self): + def gen(): + yield 1 + yield 2 + yield 3 + yield 4 + yield 5 + + def run_test(drive_generator): + g = gen() + values = [] + threading_helper.run_concurrently(drive_generator, self.NUM_THREADS, args=(g, values,)) + self.assertEqual(sorted(values), [1, 2, 3, 4, 5]) + + def call_next(g, values): + while True: + try: + values.append(next(g)) + except ValueError: + continue + except StopIteration: + break + + with self.subTest(method='next'): + run_test(call_next) + + def call_send(g, values): + while True: + try: + values.append(g.send(None)) + except ValueError: + continue + except StopIteration: + break + + with self.subTest(method='send'): + run_test(call_send) + + def for_iter_gen(g, values): + while True: + try: + for value in g: + values.append(value) + else: + break + except ValueError: + continue + + with self.subTest(method='for'): + run_test(for_iter_gen) + + def test_concurrent_close(self): + def gen(): + for i in range(10): + yield i + time.sleep(0.001) + + def drive_generator(g): + while True: + try: + for value in g: + if value == 5: + g.close() + else: + return + except ValueError as e: + self.assertEqual(e.args[0], "generator already executing") + + g = gen() + threading_helper.run_concurrently(drive_generator, self.NUM_THREADS, args=(g,)) + + def test_concurrent_gi_yieldfrom(self): + def gen_yield_from(): + yield from itertools.count() + + g = gen_yield_from() + next(g) # Put in FRAME_SUSPENDED_YIELD_FROM state + + def read_yieldfrom(gen): + for _ in range(10000): + self.assertIsNotNone(gen.gi_yieldfrom) + + threading_helper.run_concurrently(read_yieldfrom, self.NUM_THREADS, args=(g,)) + + def test_gi_yieldfrom_close_race(self): + def gen_yield_from(): + yield from itertools.count() + + g = gen_yield_from() + next(g) + + done = threading.Event() + + def reader(): + while not done.is_set(): + g.gi_yieldfrom + + def closer(): + try: + g.close() + except ValueError: + pass + done.set() + + threading_helper.run_concurrently([reader, closer]) diff --git a/Lib/test/test_free_threading/test_io.py b/Lib/test/test_free_threading/test_io.py index 41d89e04da8716..8a0ad30c4bc770 100644 --- a/Lib/test/test_free_threading/test_io.py +++ b/Lib/test/test_free_threading/test_io.py @@ -1,11 +1,15 @@ +import codecs import io import _pyio as pyio import threading from unittest import TestCase from test.support import threading_helper +from test.support.threading_helper import run_concurrently from random import randint from sys import getsizeof +threading_helper.requires_working_threading(module=True) + class ThreadSafetyMixin: # Test pretty much everything that can break under free-threading. @@ -41,7 +45,7 @@ def writelines(barrier, b, *ignore): def truncate(barrier, b, *ignore): barrier.wait() try: b.truncate(0) - except: BufferError # ignore exported buffer + except BufferError: pass # ignore exported buffer def read(barrier, b, *ignore): barrier.wait() @@ -115,3 +119,54 @@ class CBytesIOTest(ThreadSafetyMixin, TestCase): class PyBytesIOTest(ThreadSafetyMixin, TestCase): ioclass = pyio.BytesIO + + +class IncrementalNewlineDecoderTest(TestCase): + def make_decoder(self): + utf8_decoder = codecs.getincrementaldecoder('utf-8')() + return io.IncrementalNewlineDecoder(utf8_decoder, translate=True) + + def test_concurrent_reset(self): + decoder = self.make_decoder() + + def worker(): + for _ in range(100): + decoder.reset() + + run_concurrently(worker_func=worker, nthreads=2) + + def test_concurrent_decode(self): + decoder = self.make_decoder() + + def worker(): + for _ in range(100): + decoder.decode(b"line\r\n", final=False) + + run_concurrently(worker_func=worker, nthreads=2) + + def test_concurrent_getstate_setstate(self): + decoder = self.make_decoder() + state = decoder.getstate() + + def getstate_worker(): + for _ in range(100): + decoder.getstate() + + def setstate_worker(): + for _ in range(100): + decoder.setstate(state) + + run_concurrently([getstate_worker] * 2 + [setstate_worker] * 2) + + def test_concurrent_decode_and_reset(self): + decoder = self.make_decoder() + + def decode_worker(): + for _ in range(100): + decoder.decode(b"line\r\n", final=False) + + def reset_worker(): + for _ in range(100): + decoder.reset() + + run_concurrently([decode_worker] * 2 + [reset_worker] * 2) diff --git a/Lib/test/test_free_threading/test_iteration.py b/Lib/test/test_free_threading/test_iteration.py index a51ad0cf83a006..44d3e9ccfdd14e 100644 --- a/Lib/test/test_free_threading/test_iteration.py +++ b/Lib/test/test_free_threading/test_iteration.py @@ -12,7 +12,7 @@ NUMITEMS = 1000 NUMTHREADS = 2 else: - NUMITEMS = 100000 + NUMITEMS = 5000 NUMTHREADS = 5 NUMMUTATORS = 2 diff --git a/Lib/test/test_free_threading/test_itertools.py b/Lib/test/test_free_threading/test_itertools.py index 9d366041917bb3..670d4ca8835e0d 100644 --- a/Lib/test/test_free_threading/test_itertools.py +++ b/Lib/test/test_free_threading/test_itertools.py @@ -1,94 +1,73 @@ import unittest -from threading import Thread, Barrier -from itertools import batched, chain, cycle +from itertools import accumulate, batched, chain, combinations_with_replacement, cycle, permutations, zip_longest from test.support import threading_helper threading_helper.requires_working_threading(module=True) + +def work_iterator(it): + while True: + try: + next(it) + except StopIteration: + break + + class ItertoolsThreading(unittest.TestCase): @threading_helper.reap_threads - def test_batched(self): - number_of_threads = 10 - number_of_iterations = 20 - barrier = Barrier(number_of_threads) - def work(it): - barrier.wait() - while True: - try: - next(it) - except StopIteration: - break - - data = tuple(range(1000)) - for it in range(number_of_iterations): - batch_iterator = batched(data, 2) - worker_threads = [] - for ii in range(number_of_threads): - worker_threads.append( - Thread(target=work, args=[batch_iterator])) - - with threading_helper.start_threads(worker_threads): - pass - - barrier.reset() + def test_accumulate(self): + number_of_iterations = 10 + for _ in range(number_of_iterations): + it = accumulate(tuple(range(40))) + threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it]) @threading_helper.reap_threads - def test_cycle(self): - number_of_threads = 6 + def test_batched(self): number_of_iterations = 10 - number_of_cycles = 400 + for _ in range(number_of_iterations): + it = batched(tuple(range(1000)), 2) + threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it]) - barrier = Barrier(number_of_threads) + @threading_helper.reap_threads + def test_cycle(self): def work(it): - barrier.wait() - for _ in range(number_of_cycles): - try: - next(it) - except StopIteration: - pass - - data = (1, 2, 3, 4) - for it in range(number_of_iterations): - cycle_iterator = cycle(data) - worker_threads = [] - for ii in range(number_of_threads): - worker_threads.append( - Thread(target=work, args=[cycle_iterator])) - - with threading_helper.start_threads(worker_threads): - pass - - barrier.reset() + for _ in range(400): + next(it) + + number_of_iterations = 6 + for _ in range(number_of_iterations): + it = cycle((1, 2, 3, 4)) + threading_helper.run_concurrently(work, nthreads=6, args=[it]) @threading_helper.reap_threads def test_chain(self): - number_of_threads = 6 - number_of_iterations = 20 + number_of_iterations = 10 + for _ in range(number_of_iterations): + it = chain(*[(1,)] * 200) + threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it]) - barrier = Barrier(number_of_threads) - def work(it): - barrier.wait() - while True: - try: - next(it) - except StopIteration: - break - - data = [(1, )] * 200 - for it in range(number_of_iterations): - chain_iterator = chain(*data) - worker_threads = [] - for ii in range(number_of_threads): - worker_threads.append( - Thread(target=work, args=[chain_iterator])) - - with threading_helper.start_threads(worker_threads): - pass - - barrier.reset() + @threading_helper.reap_threads + def test_combinations_with_replacement(self): + number_of_iterations = 6 + for _ in range(number_of_iterations): + it = combinations_with_replacement(tuple(range(2)), 2) + threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it]) + @threading_helper.reap_threads + def test_permutations(self): + number_of_iterations = 6 + for _ in range(number_of_iterations): + it = permutations(tuple(range(4)), 2) + threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it]) + + @threading_helper.reap_threads + def test_zip_longest(self): + number_of_iterations = 10 + for _ in range(number_of_iterations): + it = zip_longest(list(range(4)), list(range(8)), fillvalue=0) + threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it]) if __name__ == "__main__": diff --git a/Lib/test/test_free_threading/test_json.py b/Lib/test/test_free_threading/test_json.py new file mode 100644 index 00000000000000..010eb322a15b84 --- /dev/null +++ b/Lib/test/test_free_threading/test_json.py @@ -0,0 +1,79 @@ +from threading import Barrier, Thread +from test.test_json import CTest +from test.support import threading_helper + + +def encode_json_helper( + json, worker, data, number_of_threads=12, number_of_json_encodings=100 +): + worker_threads = [] + barrier = Barrier(number_of_threads) + for index in range(number_of_threads): + worker_threads.append( + Thread(target=worker, args=[barrier, data, index]) + ) + for t in worker_threads: + t.start() + for ii in range(number_of_json_encodings): + json.dumps(data) + data.clear() + for t in worker_threads: + t.join() + + +class MyMapping(dict): + def __init__(self): + self.mapping = [] + + def items(self): + return self.mapping + + +@threading_helper.reap_threads +@threading_helper.requires_working_threading() +class TestJsonEncoding(CTest): + # Test encoding json with concurrent threads modifying the data cannot + # corrupt the interpreter + + def test_json_mutating_list(self): + def worker(barrier, data, index): + barrier.wait() + while data: + for d in data: + if len(d) > 5: + d.clear() + else: + d.append(index) + + data = [[], []] + encode_json_helper(self.json, worker, data) + + def test_json_mutating_exact_dict(self): + def worker(barrier, data, index): + barrier.wait() + while data: + for d in data: + if len(d) > 5: + try: + key = list(d)[0] + d.pop(key) + except (KeyError, IndexError): + pass + else: + d[index] = index + + data = [{}, {}] + encode_json_helper(self.json, worker, data) + + def test_json_mutating_mapping(self): + def worker(barrier, data, index): + barrier.wait() + while data: + for d in data: + if len(d.mapping) > 3: + d.mapping.clear() + else: + d.mapping.append((index, index)) + + data = [MyMapping(), MyMapping()] + encode_json_helper(self.json, worker, data) diff --git a/Lib/test/test_free_threading/test_list.py b/Lib/test/test_free_threading/test_list.py index 44c0ac74e02aa3..0ede4df103f728 100644 --- a/Lib/test/test_free_threading/test_list.py +++ b/Lib/test/test_free_threading/test_list.py @@ -91,6 +91,85 @@ def copy_back_and_forth(b, l): with threading_helper.start_threads(threads): pass + def test_reverse(self): + def reverse_list(b, l): + b.wait() + for _ in range(100): + l.reverse() + + def reader_list(b, l): + b.wait() + for _ in range(100): + for i in range(10): + self.assertTrue(0 <= l[i] < 10) + + l = list(range(10)) + barrier = Barrier(2) + threads = [Thread(target=reverse_list, args=(barrier, l)), + Thread(target=reader_list, args=(barrier, l))] + with threading_helper.start_threads(threads): + pass + + def test_slice_assignment1(self): + def assign_slice(b, l): + b.wait() + for _ in range(100): + l[2:5] = [7, 8, 9] + + def reader_list(b, l): + b.wait() + for _ in range(100): + self.assertIn(l[2], (2, 7)) + self.assertIn(l[3], (3, 8)) + self.assertIn(l[4], (4, 9)) + + l = list(range(10)) + barrier = Barrier(2) + threads = [Thread(target=assign_slice, args=(barrier, l)), + Thread(target=reader_list, args=(barrier, l))] + with threading_helper.start_threads(threads): + pass + + def test_slice_assignment2(self): + def assign_slice(b, l): + b.wait() + for _ in range(100): + l[::2] = [10, 11, 12, 13, 14] + + def reader_list(b, l): + b.wait() + for _ in range(100): + for i in range(0, 10, 2): + self.assertIn(l[i], (i, 10 + i // 2)) + + l = list(range(10)) + barrier = Barrier(2) + threads = [Thread(target=assign_slice, args=(barrier, l)), + Thread(target=reader_list, args=(barrier, l))] + with threading_helper.start_threads(threads): + pass + + # gh-145036: race condition with list.__sizeof__() + def test_list_sizeof_free_threaded_build(self): + L = [] + + def mutate_function(): + for _ in range(100): + L.append(1) + L.pop() + + def size_function(): + for _ in range(100): + L.__sizeof__() + + threads = [] + for _ in range(4): + threads.append(Thread(target=mutate_function)) + threads.append(Thread(target=size_function)) + + with threading_helper.start_threads(threads): + pass + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_free_threading/test_lzma.py b/Lib/test/test_free_threading/test_lzma.py new file mode 100644 index 00000000000000..3beacf38576017 --- /dev/null +++ b/Lib/test/test_free_threading/test_lzma.py @@ -0,0 +1,69 @@ +import unittest + +from test.support import import_helper, threading_helper +from test.support.threading_helper import run_concurrently + +lzma = import_helper.import_module("lzma") +from lzma import LZMACompressor, LZMADecompressor + +from test.test_lzma import INPUT + + +NTHREADS = 10 + + +@threading_helper.requires_working_threading() +class TestLZMA(unittest.TestCase): + def test_compressor(self): + lzc = LZMACompressor() + + # First compress() outputs LZMA header + header = lzc.compress(INPUT) + self.assertGreater(len(header), 0) + + def worker(): + # it should return empty bytes as it buffers data internally + data = lzc.compress(INPUT) + self.assertEqual(data, b"") + + run_concurrently(worker_func=worker, nthreads=NTHREADS - 1) + full_compressed = header + lzc.flush() + decompressed = lzma.decompress(full_compressed) + # The decompressed data should be INPUT repeated NTHREADS times + self.assertEqual(decompressed, INPUT * NTHREADS) + + def test_decompressor(self): + chunk_size = 128 + chunks = [bytes([ord("a") + i]) * chunk_size for i in range(NTHREADS)] + input_data = b"".join(chunks) + compressed = lzma.compress(input_data) + + lzd = LZMADecompressor() + output = [] + + def worker(): + data = lzd.decompress(compressed, chunk_size) + self.assertEqual(len(data), chunk_size) + output.append(data) + # Read attributes concurrently with other threads decompressing + self.assertEqual(lzd.check, lzma.CHECK_CRC64) + self.assertIsInstance(lzd.eof, bool) + self.assertIsInstance(lzd.needs_input, bool) + self.assertIsInstance(lzd.unused_data, bytes) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + self.assertEqual(len(output), NTHREADS) + # Verify the expected chunks (order doesn't matter due to append race) + self.assertSetEqual(set(output), set(chunks)) + self.assertEqual(lzd.check, lzma.CHECK_CRC64) + self.assertTrue(lzd.eof) + self.assertFalse(lzd.needs_input) + # Each thread added full compressed data to the buffer, but only 1 copy + # is consumed to produce the output. The rest remains as unused_data. + self.assertEqual( + len(lzd.unused_data), len(compressed) * (NTHREADS - 1) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_mmap.py b/Lib/test/test_free_threading/test_mmap.py new file mode 100644 index 00000000000000..ece13d33f3b79b --- /dev/null +++ b/Lib/test/test_free_threading/test_mmap.py @@ -0,0 +1,315 @@ +import unittest + +from test.support import import_helper, threading_helper +from test.support.threading_helper import run_concurrently + +import os +import string +import tempfile +import threading + +from collections import Counter + +mmap = import_helper.import_module("mmap") + +NTHREADS = 10 +ANONYMOUS_MEM = -1 + + +@threading_helper.requires_working_threading() +class MmapTests(unittest.TestCase): + def test_read_and_read_byte(self): + ascii_uppercase = string.ascii_uppercase.encode() + # Choose a total mmap size that evenly divides across threads and the + # read pattern (3 bytes per loop). + mmap_size = 3 * NTHREADS * len(ascii_uppercase) + num_bytes_to_read_per_thread = mmap_size // NTHREADS + bytes_read_from_mmap = [] + + def read(mm_obj): + nread = 0 + while nread < num_bytes_to_read_per_thread: + b = mm_obj.read_byte() + bytes_read_from_mmap.append(b) + b = mm_obj.read(2) + bytes_read_from_mmap.extend(b) + nread += 3 + + with mmap.mmap(ANONYMOUS_MEM, mmap_size) as mm_obj: + for i in range(mmap_size // len(ascii_uppercase)): + mm_obj.write(ascii_uppercase) + + mm_obj.seek(0) + run_concurrently( + worker_func=read, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + self.assertEqual(len(bytes_read_from_mmap), mmap_size) + # Count each letter/byte to verify read correctness + counter = Counter(bytes_read_from_mmap) + self.assertEqual(len(counter), len(ascii_uppercase)) + # Each letter/byte should be read (3 * NTHREADS) times + for letter in ascii_uppercase: + self.assertEqual(counter[letter], 3 * NTHREADS) + + def test_readline(self): + num_lines = 1000 + lines_read_from_mmap = [] + expected_lines = [] + + def readline(mm_obj): + for i in range(num_lines // NTHREADS): + line = mm_obj.readline() + lines_read_from_mmap.append(line) + + # Allocate mmap enough for num_lines (max line 5 bytes including NL) + with mmap.mmap(ANONYMOUS_MEM, num_lines * 5) as mm_obj: + for i in range(num_lines): + line = b"%d\n" % i + mm_obj.write(line) + expected_lines.append(line) + + mm_obj.seek(0) + run_concurrently( + worker_func=readline, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + self.assertEqual(len(lines_read_from_mmap), num_lines) + # Every line should be read once by threads; order is non-deterministic + # Sort numerically by integer value + lines_read_from_mmap.sort(key=lambda x: int(x)) + self.assertEqual(lines_read_from_mmap, expected_lines) + + def test_write_and_write_byte(self): + thread_letters = list(string.ascii_uppercase) + self.assertLessEqual(NTHREADS, len(thread_letters)) + per_thread_write_loop = 100 + + def write(mm_obj): + # Each thread picks a unique letter to write + thread_letter = thread_letters.pop(0) + thread_bytes = (thread_letter * 2).encode() + for _ in range(per_thread_write_loop): + mm_obj.write_byte(thread_bytes[0]) + mm_obj.write(thread_bytes) + + with mmap.mmap( + ANONYMOUS_MEM, per_thread_write_loop * 3 * NTHREADS + ) as mm_obj: + run_concurrently( + worker_func=write, + args=(mm_obj,), + nthreads=NTHREADS, + ) + mm_obj.seek(0) + data = mm_obj.read() + self.assertEqual(len(data), NTHREADS * per_thread_write_loop * 3) + counter = Counter(data) + self.assertEqual(len(counter), NTHREADS) + # Each thread letter should be written `per_thread_write_loop` * 3 + for letter in counter: + self.assertEqual(counter[letter], per_thread_write_loop * 3) + + def test_move(self): + ascii_uppercase = string.ascii_uppercase.encode() + num_letters = len(ascii_uppercase) + + def move(mm_obj): + for i in range(num_letters): + # Move 1 byte from the first half to the second half + mm_obj.move(0 + i, num_letters + i, 1) + + with mmap.mmap(ANONYMOUS_MEM, 2 * num_letters) as mm_obj: + mm_obj.write(ascii_uppercase) + run_concurrently( + worker_func=move, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + def test_seek_and_tell(self): + seek_per_thread = 10 + + def seek(mm_obj): + self.assertTrue(mm_obj.seekable()) + for _ in range(seek_per_thread): + before_seek = mm_obj.tell() + mm_obj.seek(1, os.SEEK_CUR) + self.assertLess(before_seek, mm_obj.tell()) + + with mmap.mmap(ANONYMOUS_MEM, 1024) as mm_obj: + run_concurrently( + worker_func=seek, + args=(mm_obj,), + nthreads=NTHREADS, + ) + # Each thread seeks from current position, the end position should + # be the sum of all seeks from all threads. + self.assertEqual(mm_obj.tell(), NTHREADS * seek_per_thread) + + def test_slice_update_and_slice_read(self): + thread_letters = list(string.ascii_uppercase) + self.assertLessEqual(NTHREADS, len(thread_letters)) + + def slice_update_and_slice_read(mm_obj): + # Each thread picks a unique letter to write + thread_letter = thread_letters.pop(0) + thread_bytes = (thread_letter * 1024).encode() + for _ in range(100): + mm_obj[:] = thread_bytes + read_bytes = mm_obj[:] + # Read bytes should be all the same letter, showing no + # interleaving + self.assertTrue(all_same(read_bytes)) + + with mmap.mmap(ANONYMOUS_MEM, 1024) as mm_obj: + run_concurrently( + worker_func=slice_update_and_slice_read, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + def test_item_update_and_item_read(self): + thread_indexes = [i for i in range(NTHREADS)] + + def item_update_and_item_read(mm_obj): + # Each thread picks a unique index to write + thread_index = thread_indexes.pop() + for i in range(100): + mm_obj[thread_index] = i + self.assertEqual(mm_obj[thread_index], i) + + # Read values set by other threads, all values + # should be less than '100' + for val in mm_obj: + self.assertLess(int.from_bytes(val), 100) + + with mmap.mmap(ANONYMOUS_MEM, NTHREADS + 1) as mm_obj: + run_concurrently( + worker_func=item_update_and_item_read, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + @unittest.skipUnless(os.name == "posix", "requires Posix") + @unittest.skipUnless(hasattr(mmap.mmap, "resize"), "requires mmap.resize") + def test_resize_and_size(self): + thread_indexes = [i for i in range(NTHREADS)] + + def resize_and_item_update(mm_obj): + # Each thread picks a unique index to write + thread_index = thread_indexes.pop() + mm_obj.resize(2048) + self.assertEqual(mm_obj.size(), 2048) + for i in range(100): + mm_obj[thread_index] = i + self.assertEqual(mm_obj[thread_index], i) + + with mmap.mmap(ANONYMOUS_MEM, 1024, flags=mmap.MAP_PRIVATE) as mm_obj: + run_concurrently( + worker_func=resize_and_item_update, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + def test_close_and_closed(self): + def close_mmap(mm_obj): + mm_obj.close() + self.assertTrue(mm_obj.closed) + + with mmap.mmap(ANONYMOUS_MEM, 1) as mm_obj: + run_concurrently( + worker_func=close_mmap, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + def test_find_and_rfind(self): + per_thread_loop = 10 + + def find_and_rfind(mm_obj): + pattern = b'Thread-Ident:"%d"' % threading.get_ident() + mm_obj.write(pattern) + for _ in range(per_thread_loop): + found_at = mm_obj.find(pattern, 0) + self.assertNotEqual(found_at, -1) + # Should not find it after the `found_at` + self.assertEqual(mm_obj.find(pattern, found_at + 1), -1) + found_at_rev = mm_obj.rfind(pattern, 0) + self.assertEqual(found_at, found_at_rev) + # Should not find it after the `found_at` + self.assertEqual(mm_obj.rfind(pattern, found_at + 1), -1) + + with mmap.mmap(ANONYMOUS_MEM, 1024) as mm_obj: + run_concurrently( + worker_func=find_and_rfind, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + @unittest.skipUnless(os.name == "posix", "requires Posix") + @unittest.skipUnless(hasattr(mmap.mmap, "resize"), "requires mmap.resize") + def test_flush(self): + mmap_filename = "test_mmap_file" + resize_to = 1024 + + def resize_and_flush(mm_obj): + mm_obj.resize(resize_to) + mm_obj.flush() + + with tempfile.TemporaryDirectory() as tmpdirname: + file_path = f"{tmpdirname}/{mmap_filename}" + with open(file_path, "wb+") as file: + file.write(b"CPython") + file.flush() + with mmap.mmap(file.fileno(), 1) as mm_obj: + run_concurrently( + worker_func=resize_and_flush, + args=(mm_obj,), + nthreads=NTHREADS, + ) + + self.assertEqual(os.path.getsize(file_path), resize_to) + + def test_mmap_export_as_memoryview(self): + """ + Each thread creates a memoryview and updates the internal state of the + mmap object. + """ + buffer_size = 42 + + def create_memoryview_from_mmap(mm_obj): + memoryviews = [] + for _ in range(100): + mv = memoryview(mm_obj) + memoryviews.append(mv) + self.assertEqual(len(mv), buffer_size) + self.assertEqual(mv[:7], b"CPython") + + # Cannot close the mmap while it is exported as buffers + with self.assertRaisesRegex( + BufferError, "cannot close exported pointers exist" + ): + mm_obj.close() + + with mmap.mmap(ANONYMOUS_MEM, 42) as mm_obj: + mm_obj.write(b"CPython") + run_concurrently( + worker_func=create_memoryview_from_mmap, + args=(mm_obj,), + nthreads=NTHREADS, + ) + # Implicit mm_obj.close() verifies all exports (memoryviews) are + # properly freed. + + +def all_same(lst): + return all(item == lst[0] for item in lst) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_monitoring.py b/Lib/test/test_free_threading/test_monitoring.py index c3d0a2bcea5c17..2cd6e7b035ecb4 100644 --- a/Lib/test/test_free_threading/test_monitoring.py +++ b/Lib/test/test_free_threading/test_monitoring.py @@ -35,10 +35,10 @@ def work(self, n, funcs): return n return self.work(n - 1, funcs) + self.work(n - 2, funcs) - def start_work(self, n, funcs): + def start_work(self, n, funcs, barrier): # With the GIL builds we need to make sure that the hooks have # a chance to run as it's possible to run w/o releasing the GIL. - time.sleep(0.1) + barrier.wait() self.work(n, funcs) def after_test(self): @@ -53,14 +53,16 @@ def test_instrumentation(self): exec("def f(): pass", x) funcs.append(x["f"]) + barrier = Barrier(self.thread_count + 1) threads = [] for i in range(self.thread_count): # Each thread gets a copy of the func list to avoid contention - t = Thread(target=self.start_work, args=(self.fib, list(funcs))) + t = Thread(target=self.start_work, args=(self.fib, list(funcs), barrier)) t.start() threads.append(t) self.after_threads() + barrier.wait() while True: any_alive = False @@ -73,6 +75,9 @@ def test_instrumentation(self): break self.during_threads() + # Sleep to avoid setting monitoring events too rapidly and + # overflowing the global version counter + time.sleep(0.0001) self.after_test() @@ -117,7 +122,6 @@ class MonitoringMultiThreaded( def setUp(self): super().setUp() self.set = False - self.called = False monitoring.register_callback( self.tool_id, monitoring.events.LINE, self.callback ) @@ -127,10 +131,7 @@ def tearDown(self): super().tearDown() def callback(self, *args): - self.called = True - - def after_test(self): - self.assertTrue(self.called) + pass def during_threads(self): if self.set: @@ -148,16 +149,11 @@ class SetTraceMultiThreaded(InstrumentationMultiThreadedMixin, TestCase): def setUp(self): self.set = False - self.called = False - - def after_test(self): - self.assertTrue(self.called) def tearDown(self): sys.settrace(None) def trace_func(self, frame, event, arg): - self.called = True return self.trace_func def during_threads(self): @@ -174,16 +170,11 @@ class SetProfileMultiThreaded(InstrumentationMultiThreadedMixin, TestCase): def setUp(self): self.set = False - self.called = False - - def after_test(self): - self.assertTrue(self.called) def tearDown(self): sys.setprofile(None) def trace_func(self, frame, event, arg): - self.called = True return self.trace_func def during_threads(self): @@ -194,6 +185,60 @@ def during_threads(self): self.set = not self.set +@threading_helper.requires_working_threading() +class SetProfileAllThreadsMultiThreaded(InstrumentationMultiThreadedMixin, TestCase): + """Uses threading.setprofile_all_threads and repeatedly toggles instrumentation on and off""" + + def setUp(self): + self.set = False + + def tearDown(self): + threading.setprofile_all_threads(None) + + def trace_func(self, frame, event, arg): + return self.trace_func + + def during_threads(self): + if self.set: + threading.setprofile_all_threads(self.trace_func) + else: + threading.setprofile_all_threads(None) + self.set = not self.set + + +class SetProfileAllMultiThreaded(TestCase): + def test_profile_all_threads(self): + done = threading.Event() + + def func(): + pass + + def bg_thread(): + while not done.is_set(): + func() + func() + func() + func() + func() + + def my_profile(frame, event, arg): + return None + + bg_threads = [] + for i in range(10): + t = threading.Thread(target=bg_thread) + t.start() + bg_threads.append(t) + + for i in range(100): + threading.setprofile_all_threads(my_profile) + threading.setprofile_all_threads(None) + + done.set() + for t in bg_threads: + t.join() + + class TraceBuf: def __init__(self): self.traces = [] @@ -387,6 +432,38 @@ def noop(): self.observe_threads(noop, buf) + def test_trace_concurrent(self): + # Test calling a function concurrently from a tracing and a non-tracing + # thread + b = threading.Barrier(2) + + def func(): + for _ in range(100): + pass + + def noop(): + pass + + def bg_thread(): + b.wait() + func() # this may instrument `func` + + def tracefunc(frame, event, arg): + # These calls run under tracing can race with the background thread + for _ in range(10): + func() + return tracefunc + + t = Thread(target=bg_thread) + t.start() + try: + sys.settrace(tracefunc) + b.wait() + noop() + finally: + sys.settrace(None) + t.join() + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_free_threading/test_pickle.py b/Lib/test/test_free_threading/test_pickle.py new file mode 100644 index 00000000000000..45ea1bf5f26465 --- /dev/null +++ b/Lib/test/test_free_threading/test_pickle.py @@ -0,0 +1,78 @@ +import pickle +import threading +import unittest + +from test.support import threading_helper + + +@threading_helper.requires_working_threading() +class TestPickleFreeThreading(unittest.TestCase): + + def test_pickle_dumps_with_concurrent_dict_mutation(self): + # gh-146452: Pickling a dict while another thread mutates it + # used to segfault. batch_dict_exact() iterated dict items via + # PyDict_Next() which returns borrowed references, and a + # concurrent pop/replace could free the value before Py_INCREF + # got to it. + shared = {str(i): list(range(20)) for i in range(50)} + + def dumper(): + for _ in range(1000): + try: + pickle.dumps(shared) + except RuntimeError: + # "dictionary changed size during iteration" is expected + pass + + def mutator(): + for j in range(1000): + key = str(j % 50) + shared[key] = list(range(j % 20)) + if j % 10 == 0: + shared.pop(key, None) + shared[key] = [j] + + threads = [] + for _ in range(10): + threads.append(threading.Thread(target=dumper)) + threads.append(threading.Thread(target=mutator)) + + with threading_helper.start_threads(threads): + pass + + def test_pickle_dumps_with_concurrent_list_mutations(self): + # gh-149816: Pickling a list while another thread mutates it + # used to be a UAF in free-threaded mode. batch_list_exact() + # used PyList_GET_ITEM (borrowed) followed by Py_INCREF, and a + # concurrent replace/pop could free the item between those two + # operations. + shared = [list(range(20)) for _ in range(50)] + + def dumper(): + for _ in range(1000): + try: + pickle.dumps(shared) + except (RuntimeError, IndexError): + pass + + def mutator(): + for i in range(1000): + idx = i % 50 + shared[idx] = list(range(i % 20)) + if i % 10 == 0: + try: + shared.pop() + except IndexError: + pass + shared.append([i]) + + threads = [] + for _ in range(10): + threads.append(threading.Thread(target=dumper)) + threads.append(threading.Thread(target=mutator)) + + with threading_helper.start_threads(threads): + pass + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_re.py b/Lib/test/test_free_threading/test_re.py new file mode 100644 index 00000000000000..56f25045d1bf8e --- /dev/null +++ b/Lib/test/test_free_threading/test_re.py @@ -0,0 +1,62 @@ +import re +import unittest + +from test.support import threading_helper +from test.support.threading_helper import run_concurrently + + +NTHREADS = 10 + + +@threading_helper.requires_working_threading() +class TestRe(unittest.TestCase): + def test_pattern_sub(self): + """Pattern substitution should work across threads""" + pattern = re.compile(r"\w+@\w+\.\w+") + text = "e-mail: test@python.org or user@pycon.org. " * 5 + results = [] + + def worker(): + substituted = pattern.sub("(redacted)", text) + results.append(substituted.count("(redacted)")) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + self.assertEqual(results, [2 * 5] * NTHREADS) + + def test_pattern_search(self): + """Pattern search should work across threads.""" + emails = ["alice@python.org", "bob@pycon.org"] * 10 + pattern = re.compile(r"\w+@\w+\.\w+") + results = [] + + def worker(): + matches = [pattern.search(e).group() for e in emails] + results.append(len(matches)) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + self.assertEqual(results, [2 * 10] * NTHREADS) + + def test_scanner_concurrent_access(self): + """Shared scanner should reject concurrent access.""" + pattern = re.compile(r"\w+") + scanner = pattern.scanner("word " * 10) + + def worker(): + for _ in range(100): + try: + scanner.search() + except ValueError as e: + if "already executing" in str(e): + pass + else: + raise + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + # This test has no assertions. Its purpose is to catch crashes and + # enable thread sanitizer to detect race conditions. While "already + # executing" errors are very likely, they're not guaranteed due to + # non-deterministic thread scheduling, so we can't assert errors > 0. + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_resource.py b/Lib/test/test_free_threading/test_resource.py new file mode 100644 index 00000000000000..ecd0e535c442b4 --- /dev/null +++ b/Lib/test/test_free_threading/test_resource.py @@ -0,0 +1,41 @@ +import unittest +from test.support import import_helper, threading_helper + +resource = import_helper.import_module("resource") + + +NTHREADS = 10 +LOOP_PER_THREAD = 1000 + + +@threading_helper.requires_working_threading() +class ResourceTest(unittest.TestCase): + @unittest.skipUnless(hasattr(resource, "getrusage"), "needs getrusage") + @unittest.skipUnless( + hasattr(resource, "RUSAGE_THREAD"), "needs RUSAGE_THREAD" + ) + def test_getrusage(self): + ru_utime_lst = [] + + def dummy_work(ru_utime_lst): + for _ in range(LOOP_PER_THREAD): + pass + + usage_process = resource.getrusage(resource.RUSAGE_SELF) + usage_thread = resource.getrusage(resource.RUSAGE_THREAD) + # Process user time should be greater than thread user time + self.assertGreater(usage_process.ru_utime, usage_thread.ru_utime) + ru_utime_lst.append(usage_thread.ru_utime) + + threading_helper.run_concurrently( + worker_func=dummy_work, args=(ru_utime_lst,), nthreads=NTHREADS + ) + + usage_process = resource.getrusage(resource.RUSAGE_SELF) + self.assertEqual(len(ru_utime_lst), NTHREADS) + # Process user time should be greater than sum of all thread user times + self.assertGreater(usage_process.ru_utime, sum(ru_utime_lst)) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_set.py b/Lib/test/test_free_threading/test_set.py index a66e03bcc4c9d1..9dd3d68d5dad13 100644 --- a/Lib/test/test_free_threading/test_set.py +++ b/Lib/test/test_free_threading/test_set.py @@ -1,18 +1,14 @@ import unittest - -from threading import Thread, Barrier -from unittest import TestCase - +from threading import Thread, Barrier, Event from test.support import threading_helper -@threading_helper.requires_working_threading() -class TestSet(TestCase): +class TestSetRepr(unittest.TestCase): def test_repr_clear(self): """Test repr() of a set while another thread is calling clear()""" NUM_ITERS = 10 NUM_REPR_THREADS = 10 - barrier = Barrier(NUM_REPR_THREADS + 1) + barrier = Barrier(NUM_REPR_THREADS + 1, timeout=2) s = {1, 2, 3, 4, 5, 6, 7, 8} def clear_set(): @@ -37,5 +33,131 @@ def repr_set(): self.assertIn(set_repr, ("set()", "{1, 2, 3, 4, 5, 6, 7, 8}")) +class RaceTestBase: + def test_contains_mutate(self): + """Test set contains operation combined with mutation.""" + barrier = Barrier(2, timeout=2) + s = set() + done = Event() + + NUM_LOOPS = 1000 + + def read_set(): + barrier.wait() + while not done.is_set(): + for i in range(self.SET_SIZE): + item = i >> 1 + result = item in s + + def mutate_set(): + barrier.wait() + for i in range(NUM_LOOPS): + s.clear() + for j in range(self.SET_SIZE): + s.add(j) + for j in range(self.SET_SIZE): + s.discard(j) + # executes the set_swap_bodies() function + s.__iand__(set(k for k in range(10, 20))) + done.set() + + threads = [Thread(target=read_set), Thread(target=mutate_set)] + for t in threads: + t.start() + for t in threads: + t.join() + + def test_contains_frozenset(self): + barrier = Barrier(3, timeout=2) + done = Event() + + NUM_LOOPS = 1_000 + + # This mutates the key used for contains test, not the container + # itself. This works because frozenset allows the key to be a set(). + s = set() + + def mutate_set(): + barrier.wait() + while not done.is_set(): + s.add(0) + s.add(1) + s.clear() + + def read_set(): + barrier.wait() + container = frozenset([frozenset([0])]) + self.assertTrue(set([0]) in container) + for _ in range(NUM_LOOPS): + # Will return True when {0} is the key and False otherwise + result = s in container + done.set() + + threads = [ + Thread(target=read_set), + Thread(target=read_set), + Thread(target=mutate_set), + ] + for t in threads: + t.start() + for t in threads: + t.join() + + def test_contains_hash_mutate(self): + """Test set contains operation with mutating hash method.""" + barrier = Barrier(2, timeout=2) + + NUM_LOOPS = 1_000 + SET_SIZE = self.SET_SIZE + + s = set(range(SET_SIZE)) + + class Key: + def __init__(self): + self.count = 0 + self.value = 0 + + def __hash__(self): + self.count += 1 + # This intends to trigger the SET_LOOKKEY_CHANGED case + # of set_lookkey_threadsafe() since calling clear() + # will cause the 'table' pointer to change. + if self.count % 2 == 0: + s.clear() + else: + s.update(range(SET_SIZE)) + return hash(self.value) + + def __eq__(self, other): + return self.value == other + + key = Key() + self.assertTrue(key in s) + self.assertFalse(key in s) + self.assertTrue(key in s) + self.assertFalse(key in s) + + def read_set(): + barrier.wait() + for i in range(NUM_LOOPS): + result = key in s + + threads = [Thread(target=read_set), Thread(target=read_set)] + for t in threads: + t.start() + for t in threads: + t.join() + + +@threading_helper.requires_working_threading() +class SmallSetTest(RaceTestBase, unittest.TestCase): + SET_SIZE = 6 # smaller than PySet_MINSIZE + + +@threading_helper.requires_working_threading() +class LargeSetTest(RaceTestBase, unittest.TestCase): + SET_SIZE = 20 # larger than PySet_MINSIZE + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_free_threading/test_slots.py b/Lib/test/test_free_threading/test_slots.py index a3b9f4b0175ae7..a73525e1bebfb4 100644 --- a/Lib/test/test_free_threading/test_slots.py +++ b/Lib/test/test_free_threading/test_slots.py @@ -16,18 +16,19 @@ def run_in_threads(targets): thread.join() +class Spam: + __slots__ = [ + "eggs", + ] + + def __init__(self, initial_value): + self.eggs = initial_value + + @threading_helper.requires_working_threading() class TestSlots(TestCase): def test_object(self): - class Spam: - __slots__ = [ - "eggs", - ] - - def __init__(self, initial_value): - self.eggs = initial_value - spam = Spam(0) iters = 20_000 @@ -43,6 +44,24 @@ def reader(): run_in_threads([writer, reader, reader, reader]) + def test_del_object_is_atomic(self): + # Testing whether the implementation of `del slots_object.attribute` + # removes the attribute atomically, thus avoiding non-sequentially- + # consistent behaviors. + # https://github.com/python/cpython/issues/146270 + def deleter(spam, successes): + try: + del spam.eggs + successes.append(True) + except AttributeError: + successes.append(False) + + for _ in range(10): + spam = Spam(0) + successes = [] + threading_helper.run_concurrently(deleter, nthreads=4, args=(spam, successes)) + self.assertEqual(sum(successes), 1) + def test_T_BOOL(self): spam_old = _testcapi._test_structmembersType_OldAPI() spam_new = _testcapi._test_structmembersType_NewAPI() diff --git a/Lib/test/test_free_threading/test_str.py b/Lib/test/test_free_threading/test_str.py index 72044e979b0f48..11e04009956db1 100644 --- a/Lib/test/test_free_threading/test_str.py +++ b/Lib/test/test_free_threading/test_str.py @@ -1,7 +1,9 @@ +import sys +import threading import unittest from itertools import cycle -from threading import Event, Thread +from threading import Barrier, Event, Thread from unittest import TestCase from test.support import threading_helper @@ -69,6 +71,40 @@ def reader_func(): for reader in readers: reader.join() + def test_intern_unowned_string(self): + # Test interning strings owned by various threads. + strings = [f"intern_race_owner_{i}" for i in range(50)] + + NUM_THREADS = 5 + b = Barrier(NUM_THREADS) + + def interner(): + tid = threading.get_ident() + for i in range(20): + strings.append(f"intern_{tid}_{i}") + b.wait() + for s in strings: + r = sys.intern(s) + self.assertTrue(sys._is_interned(r)) + + threading_helper.run_concurrently(interner, nthreads=NUM_THREADS) + + def test_maketrans_dict_concurrent_modification(self): + for _ in range(5): + d = {2000: 'a'} + + def work(dct): + for i in range(100): + str.maketrans(dct) + dct[2000 + i] = chr(i % 16) + dct.pop(2000 + i, None) + + threading_helper.run_concurrently( + work, + nthreads=5, + args=(d,), + ) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_free_threading/test_suggestions.py b/Lib/test/test_free_threading/test_suggestions.py new file mode 100755 index 00000000000000..2c10a511b86595 --- /dev/null +++ b/Lib/test/test_free_threading/test_suggestions.py @@ -0,0 +1,24 @@ +import unittest + +from test.support import import_helper, threading_helper +from test.support.threading_helper import run_concurrently + +suggestions = import_helper.import_module("_suggestions") + +NTHREADS = 10 + + +@threading_helper.requires_working_threading() +class SuggestionsTests(unittest.TestCase): + def test_generate_suggestions(self): + candidates = [str(i) for i in range(100)] + + def worker(): + _ = suggestions._generate_suggestions(candidates, "42") + candidates.clear() + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_type.py b/Lib/test/test_free_threading/test_type.py index ae996e7db3c7fd..1255d842dbff48 100644 --- a/Lib/test/test_free_threading/test_type.py +++ b/Lib/test/test_free_threading/test_type.py @@ -127,6 +127,39 @@ class ClassB(Base): obj.__class__ = ClassB + def test_name_change(self): + class Foo: + pass + + def writer(): + for _ in range(1000): + Foo.__name__ = 'Bar' + + def reader(): + for _ in range(1000): + Foo.__name__ + + self.run_one(writer, reader) + + def test_bases_change(self): + class BaseA: + pass + + class Derived(BaseA): + pass + + def writer(): + for _ in range(1000): + class BaseB: + pass + Derived.__bases__ = (BaseB,) + + def reader(): + for _ in range(1000): + Derived.__base__ + + self.run_one(writer, reader) + def run_one(self, writer_func, reader_func): barrier = threading.Barrier(NTHREADS) diff --git a/Lib/test/test_free_threading/test_uuid.py b/Lib/test/test_free_threading/test_uuid.py new file mode 100755 index 00000000000000..d794afc552a652 --- /dev/null +++ b/Lib/test/test_free_threading/test_uuid.py @@ -0,0 +1,60 @@ +import os +import unittest + +from test.support import import_helper, threading_helper +from test.support.threading_helper import run_concurrently +from uuid import SafeUUID + +c_uuid = import_helper.import_module("_uuid") + +NTHREADS = 10 +UUID_PER_THREAD = 1000 + + +@threading_helper.requires_working_threading() +class UUIDTests(unittest.TestCase): + @unittest.skipUnless(os.name == "posix", "POSIX only") + def test_generate_time_safe(self): + uuids = [] + + def worker(): + local_uuids = [] + for _ in range(UUID_PER_THREAD): + uuid, is_safe = c_uuid.generate_time_safe() + self.assertIs(type(uuid), bytes) + self.assertEqual(len(uuid), 16) + # Collect the UUID only if it is safe. If not, we cannot ensure + # UUID uniqueness. According to uuid_generate_time_safe() man + # page, it is theoretically possible for two concurrently + # running processes to generate the same UUID(s) if the return + # value is not 0. + if is_safe == SafeUUID.safe: + local_uuids.append(uuid) + + # Merge all safe uuids + uuids.extend(local_uuids) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + self.assertEqual(len(uuids), len(set(uuids))) + + @unittest.skipUnless(os.name == "nt", "Windows only") + def test_UuidCreate(self): + uuids = [] + + def worker(): + local_uuids = [] + for _ in range(UUID_PER_THREAD): + uuid = c_uuid.UuidCreate() + self.assertIs(type(uuid), bytes) + self.assertEqual(len(uuid), 16) + local_uuids.append(uuid) + + # Merge all uuids + uuids.extend(local_uuids) + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + self.assertEqual(len(uuids), len(set(uuids))) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_free_threading/test_zlib.py b/Lib/test/test_free_threading/test_zlib.py new file mode 100644 index 00000000000000..7c4ed04f4a7c9e --- /dev/null +++ b/Lib/test/test_free_threading/test_zlib.py @@ -0,0 +1,80 @@ +import itertools +import unittest + +from test.support import import_helper, threading_helper +from test.support.threading_helper import run_concurrently + +zlib = import_helper.import_module("zlib") + +from test.test_zlib import HAMLET_SCENE + + +NTHREADS = 10 + + +@threading_helper.requires_working_threading() +class TestZlib(unittest.TestCase): + def test_compressor(self): + comp = zlib.compressobj() + + # First compress() outputs zlib header + header = comp.compress(HAMLET_SCENE) + self.assertGreater(len(header), 0) + + def worker(): + # it should return empty bytes as it buffers data internally + data = comp.compress(HAMLET_SCENE) + self.assertEqual(data, b"") + + run_concurrently(worker_func=worker, nthreads=NTHREADS - 1) + full_compressed = header + comp.flush() + decompressed = zlib.decompress(full_compressed) + # The decompressed data should be HAMLET_SCENE repeated NTHREADS times + self.assertEqual(decompressed, HAMLET_SCENE * NTHREADS) + + def test_decompressor_concurrent_attribute_reads(self): + input_data = HAMLET_SCENE * NTHREADS + compressed = zlib.compress(input_data) + + decomp = zlib.decompressobj() + decomp_size_per_loop = len(input_data) // 1000 + decompressed_parts = [] + + def decomp_worker(): + # Decompress in chunks, which updates eof, unused_data, unconsumed_tail + decompressed_parts.append( + decomp.decompress(compressed, decomp_size_per_loop) + ) + while decomp.unconsumed_tail: + decompressed_parts.append( + decomp.decompress( + decomp.unconsumed_tail, decomp_size_per_loop + ) + ) + + def decomp_attr_reader(): + # Read attributes concurrently while another thread decompresses + for _ in range(1000): + _ = decomp.unused_data + _ = decomp.unconsumed_tail + _ = decomp.eof + + counter = itertools.count() + + def worker(): + # First thread decompresses, others read attributes + if next(counter) == 0: + decomp_worker() + else: + decomp_attr_reader() + + run_concurrently(worker_func=worker, nthreads=NTHREADS) + + self.assertTrue(decomp.eof) + self.assertEqual(decomp.unused_data, b"") + decompressed = b"".join(decompressed_parts) + self.assertEqual(decompressed, HAMLET_SCENE * NTHREADS) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index b41e02c3a16379..5cc02c30ec2ba3 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -593,6 +593,17 @@ def test_compile_time_concat_errors(self): r"""b'' f''""", ]) + def test_concat_decode_failure_does_not_crash(self): + script = r''' +import builtins +builtins.__import__ = builtins # Breaks warning machinery so _get_resized_exprs returns NULL +try: + compile('"x"f"\]"b""', '', 'exec') +except Exception: + pass +''' + assert_python_ok('-c', script) + def test_literal(self): self.assertEqual(f'', '') self.assertEqual(f'a', 'a') @@ -1831,6 +1842,41 @@ def test_newlines_in_format_specifiers(self): for case in valid_cases: compile(case, "", "exec") + def test_raw_fstring_format_spec(self): + # Test raw f-string format spec behavior (Issue #137314). + # + # Raw f-strings should preserve literal backslashes in format specifications, + # not interpret them as escape sequences. + class UnchangedFormat: + """Test helper that returns the format spec unchanged.""" + def __format__(self, format): + return format + + # Test basic escape sequences + self.assertEqual(f"{UnchangedFormat():\xFF}", 'ÿ') + self.assertEqual(rf"{UnchangedFormat():\xFF}", '\\xFF') + + # Test nested expressions with raw/non-raw combinations + self.assertEqual(rf"{UnchangedFormat():{'\xFF'}}", 'ÿ') + self.assertEqual(f"{UnchangedFormat():{r'\xFF'}}", '\\xFF') + self.assertEqual(rf"{UnchangedFormat():{r'\xFF'}}", '\\xFF') + + # Test continuation character in format specs + self.assertEqual(f"""{UnchangedFormat():{'a'\ + 'b'}}""", 'ab') + self.assertEqual(rf"""{UnchangedFormat():{'a'\ + 'b'}}""", 'ab') + + # Test multiple format specs in same raw f-string + self.assertEqual(rf"{UnchangedFormat():\xFF} {UnchangedFormat():\n}", '\\xFF \\n') + + def test_gh139516(self): + with temp_cwd(): + script = 'script.py' + with open(script, 'wb') as f: + f.write('''def f(a): pass\nf"{f(a=lambda: 'à'\n)}"'''.encode()) + assert_python_ok(script) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py index c864d401f9ed67..983a8b92cf6384 100644 --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -8,6 +8,7 @@ import io import errno import os +import sys import threading import time import unittest @@ -16,7 +17,7 @@ except ImportError: ssl = None -from unittest import TestCase, skipUnless +from unittest import mock, TestCase, skipUnless from test import support from test.support import requires_subprocess from test.support import threading_helper @@ -590,6 +591,8 @@ def test_quit(self): # Ensure the connection gets closed; sock attribute should be None self.assertEqual(self.client.sock, None) + @unittest.skipIf(sys.platform == 'cygwin', + "MSG_OOB doesn't work properly on Cygwin") def test_abort(self): self.client.abort() @@ -1145,6 +1148,40 @@ def testTimeoutDirectAccess(self): ftp.close() +class TestFtpcpSecurity(TestCase): + """ftpcp() must not trust the host a source server advertises in PASV. + + A malicious source server can otherwise redirect the target server's + data connection to an arbitrary host:port (SSRF), so ftpcp() uses the + source server's actual peer address instead, the same as FTP.makepasv(). + """ + + def _make_pair(self, *, advertised_host, real_host, trust=False): + source = mock.Mock(spec=ftplib.FTP) + source.trust_server_pasv_ipv4_address = trust + source.sock.getpeername.return_value = (real_host, 21) + # PASV replies give the host as comma-separated octets, not dotted. + advertised = advertised_host.replace('.', ',') + source.sendcmd.side_effect = lambda cmd: ( + f'227 Entering Passive Mode ({advertised},1,2).' + if cmd == 'PASV' else '150 ok') + target = mock.Mock(spec=ftplib.FTP) + target.sendcmd.return_value = '150 ok' + return source, target + + def test_ftpcp_ignores_untrusted_pasv_host(self): + source, target = self._make_pair(advertised_host='10.0.0.5', + real_host='198.51.100.7') + ftplib.ftpcp(source, 'a', target, 'b') + target.sendport.assert_called_once_with('198.51.100.7', 258) + + def test_ftpcp_trust_server_pasv_ipv4_address(self): + source, target = self._make_pair(advertised_host='10.0.0.5', + real_host='198.51.100.7', trust=True) + ftplib.ftpcp(source, 'a', target, 'b') + target.sendport.assert_called_once_with('10.0.0.5', 258) + + class MiscTestCase(TestCase): def test__all__(self): not_exported = { diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 375f456dfde834..fe14e7cb342c9e 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -459,6 +459,24 @@ class BuiltinFunctionPropertiesTest(unittest.TestCase): # XXX Not sure where this should really go since I can't find a # test module specifically for builtin_function_or_method. + def test_builtin__module__(self): + import math + + # builtin function: + self.assertEqual(len.__module__, 'builtins') + self.assertEqual(math.sin.__module__, 'math') + + # instance method: + self.assertRaises(AttributeError, getattr, int.to_bytes, '__module__') + self.assertEqual(int.to_bytes.__objclass__.__module__, 'builtins') + + # builtin classmethod: + self.assertEqual(int.from_bytes.__module__, None) + self.assertEqual(int.from_bytes.__self__.__module__, 'builtins') + + # builtin staticmethod: + self.assertEqual(bytes.maketrans.__module__, 'builtins') + def test_builtin__qualname__(self): import time diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index f7e09fd771eaf2..c30386afe41849 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -406,6 +406,7 @@ def test_setstate(self): def test_setstate_errors(self): f = self.partial(signature) + self.assertRaises(TypeError, f.__setstate__, (capture, (), {})) self.assertRaises(TypeError, f.__setstate__, (capture, (), {}, {}, None)) self.assertRaises(TypeError, f.__setstate__, [capture, (), {}, None]) @@ -413,6 +414,8 @@ def test_setstate_errors(self): self.assertRaises(TypeError, f.__setstate__, (capture, None, {}, None)) self.assertRaises(TypeError, f.__setstate__, (capture, [], {}, None)) self.assertRaises(TypeError, f.__setstate__, (capture, (), [], None)) + self.assertRaises(TypeError, f.__setstate__, (capture, (), {}, ())) + self.assertRaises(TypeError, f.__setstate__, (capture, (), {}, 'test')) def test_setstate_subclasses(self): f = self.partial(signature) @@ -435,6 +438,7 @@ def test_setstate_subclasses(self): self.assertIs(type(r[0]), tuple) @support.skip_if_sanitizer("thread sanitizer crashes in __tsan::FuncEntry", thread=True) + @support.skip_if_unlimited_stack_size @support.skip_emscripten_stack_overflow() def test_recursive_pickle(self): with replaced_module('functools', self.module): @@ -510,6 +514,70 @@ def test_partial_genericalias(self): self.assertEqual(alias.__args__, (int,)) self.assertEqual(alias.__parameters__, ()) + # GH-144475: Tests that the partial object does not change until repr finishes + def test_repr_safety_against_reentrant_mutation(self): + g_partial = None + + class Function: + def __init__(self, name): + self.name = name + + def __call__(self): + return None + + def __repr__(self): + return f"Function({self.name})" + + class EvilObject: + def __init__(self): + self.triggered = False + + def __repr__(self): + if not self.triggered and g_partial is not None: + self.triggered = True + new_args_tuple = (None,) + new_keywords_dict = {"keyword": None} + new_tuple_state = (Function("new_function"), new_args_tuple, new_keywords_dict, None) + g_partial.__setstate__(new_tuple_state) + gc.collect() + return f"EvilObject" + + trigger = EvilObject() + func = Function("old_function") + + g_partial = functools.partial(func, None, trigger=trigger) + self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), None, trigger=EvilObject)") + + trigger.triggered = False + g_partial = functools.partial(func, trigger, arg=None) + self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), EvilObject, arg=None)") + + + trigger.triggered = False + g_partial = functools.partial(func, trigger, None) + self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), EvilObject, None)") + + trigger.triggered = False + g_partial = functools.partial(func, trigger=trigger, arg=None) + self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), trigger=EvilObject, arg=None)") + + trigger.triggered = False + g_partial = functools.partial(func, trigger, None, None, None, None, arg=None) + self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), EvilObject, None, None, None, None, arg=None)") + + def test_str_subclass_error(self): + class BadStr(str): + def __eq__(self, other): + raise RuntimeError + def __hash__(self): + return str.__hash__(self) + + def f(**kwargs): + return kwargs + + p = functools.partial(f, poison="") + with self.assertRaises(RuntimeError): + result = p(**{BadStr("poison"): "new_value"}) @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestPartialC(TestPartial, unittest.TestCase): @@ -1066,6 +1134,14 @@ def add(x, y): self.assertRaises(TypeError, self.reduce, add, [0, 1], initial="") self.assertEqual(self.reduce(42, "", initial="1"), "1") # func is never called with one item + def test_reduce_with_kwargs(self): + with self.assertRaises(TypeError): + self.reduce(function=lambda x, y: (x or 1) + y, sequence=[1, 2, 3, 4, 5]) + with self.assertRaises(TypeError): + self.reduce(function=lambda x, y: x + y, sequence=[1, 2, 3, 4, 5], initial=1) + with self.assertRaises(TypeError): + self.reduce(lambda x, y: x + y, sequence=[1, 2, 3, 4, 5], initial=1) + @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestReduceC(TestReduce, unittest.TestCase): @@ -1076,12 +1152,6 @@ class TestReduceC(TestReduce, unittest.TestCase): class TestReducePy(TestReduce, unittest.TestCase): reduce = staticmethod(py_functools.reduce) - def test_reduce_with_kwargs(self): - with self.assertWarns(DeprecationWarning): - self.reduce(function=lambda x, y: x + y, sequence=[1, 2, 3, 4, 5], initial=1) - with self.assertWarns(DeprecationWarning): - self.reduce(lambda x, y: x + y, sequence=[1, 2, 3, 4, 5], initial=1) - class TestCmpToKey: @@ -2136,6 +2206,7 @@ def orig(a: int) -> nonexistent: ... @support.skip_on_s390x @unittest.skipIf(support.is_wasi, "WASI has limited C stack") @support.skip_if_sanitizer("requires deep stack", ub=True, thread=True) + @support.skip_if_unlimited_stack_size @support.skip_emscripten_stack_overflow() def test_lru_recursion(self): @@ -2152,6 +2223,13 @@ def fib(n): with self.assertRaises(RecursionError): fib(support.exceeds_recursion_limit()) + def test_lru_checks_arg_is_callable(self): + with self.assertRaisesRegex( + TypeError, + "the first argument must be callable", + ): + self.module.lru_cache(1)('hello') + @py_functools.lru_cache() def py_cached_func(x, y): @@ -2782,7 +2860,7 @@ class Slot: @functools.singledispatchmethod @classmethod def go(cls, item, arg): - pass + return item - arg @go.register @classmethod @@ -2791,7 +2869,9 @@ def _(cls, item: int, arg): s = Slot() self.assertEqual(s.go(1, 1), 2) + self.assertEqual(s.go(1.5, 1), 0.5) self.assertEqual(Slot.go(1, 1), 2) + self.assertEqual(Slot.go(1.5, 1), 0.5) def test_staticmethod_slotted_class(self): class A: @@ -2991,6 +3071,57 @@ def static_func(arg: int) -> str: self.assertEqual(A.static_func.__name__, 'static_func') self.assertEqual(A().static_func.__name__, 'static_func') + def test_method_classlevel_calls(self): + """Regression test for GH-143535.""" + class C: + @functools.singledispatchmethod + def generic(self, x: object): + return "generic" + + @generic.register + def special1(self, x: int): + return "special1" + + @generic.register + @classmethod + def special2(self, x: float): + return "special2" + + @generic.register + @staticmethod + def special3(x: complex): + return "special3" + + def special4(self, x): + return "special4" + + class D1: + def __get__(self, _, owner): + return lambda inst, x: owner.special4(inst, x) + + generic.register(D1, D1()) + + def special5(self, x): + return "special5" + + class D2: + def __get__(self, inst, owner): + # Different instance bound to the returned method + # doesn't cause it to receive the original instance + # as a separate argument. + # To work around this, wrap the returned bound method + # with `functools.partial`. + return C().special5 + + generic.register(D2, D2()) + + self.assertEqual(C.generic(C(), "foo"), "generic") + self.assertEqual(C.generic(C(), 1), "special1") + self.assertEqual(C.generic(C(), 2.0), "special2") + self.assertEqual(C.generic(C(), 3j), "special3") + self.assertEqual(C.generic(C(), C.D1()), "special4") + self.assertEqual(C.generic(C(), C.D2()), "special5") + def test_method_repr(self): class Callable: def __call__(self, *args): @@ -3229,6 +3360,21 @@ def t(self, *args, **kwargs): with self.assertRaisesRegex(TypeError, msg): A().t(a=1) + def test_positional_only_argument(self): + @functools.singledispatch + def f(arg, /, extra): + return "base" + @f.register + def f_int(arg: int, /, extra: str): + return "int" + @f.register + def f_str(arg: str, /, extra: int): + return "str" + + self.assertEqual(f(None, "extra"), "base") + self.assertEqual(f(1, "extra"), "int") + self.assertEqual(f("s", "extra"), "str") + def test_union(self): @functools.singledispatch def f(arg): @@ -3443,16 +3589,11 @@ def _(item: int, arg: bytes) -> str: def test_method_signatures(self): class A: - def m(self, item, arg: int) -> str: - return str(item) - @classmethod - def cm(cls, item, arg: int) -> str: - return str(item) @functools.singledispatchmethod def func(self, item, arg: int) -> str: return str(item) @func.register - def _(self, item, arg: bytes) -> str: + def _(self, item: int, arg: bytes) -> str: return str(item) @functools.singledispatchmethod @@ -3461,7 +3602,7 @@ def cls_func(cls, item, arg: int) -> str: return str(arg) @func.register @classmethod - def _(cls, item, arg: bytes) -> str: + def _(cls, item: int, arg: bytes) -> str: return str(item) @functools.singledispatchmethod @@ -3470,7 +3611,7 @@ def static_func(item, arg: int) -> str: return str(arg) @func.register @staticmethod - def _(item, arg: bytes) -> str: + def _(item: int, arg: bytes) -> str: return str(item) self.assertEqual(str(Signature.from_callable(A.func)), @@ -3482,6 +3623,37 @@ def _(item, arg: bytes) -> str: self.assertEqual(str(Signature.from_callable(A.static_func)), '(item, arg: int) -> str') + def test_method_non_descriptor(self): + class Callable: + def __init__(self, value): + self.value = value + def __call__(self, arg): + return self.value, arg + + class A: + t = functools.singledispatchmethod(Callable('general')) + t.register(int, Callable('special')) + + @functools.singledispatchmethod + def u(self, arg): + return 'general', arg + u.register(int, Callable('special')) + + v = functools.singledispatchmethod(Callable('general')) + @v.register(int) + def _(self, arg): + return 'special', arg + + a = A() + self.assertEqual(a.t(0), ('special', 0)) + self.assertEqual(a.t(2.5), ('general', 2.5)) + self.assertEqual(A.t(0), ('special', 0)) + self.assertEqual(A.t(2.5), ('general', 2.5)) + self.assertEqual(a.u(0), ('special', 0)) + self.assertEqual(a.u(2.5), ('general', 2.5)) + self.assertEqual(a.v(0), ('special', 0)) + self.assertEqual(a.v(2.5), ('general', 2.5)) + class CachedCostItem: _cost = 1 diff --git a/Lib/test/test_future_stmt/test_future.py b/Lib/test/test_future_stmt/test_future.py index 71f1e616116d81..faa3a2bfe121dc 100644 --- a/Lib/test/test_future_stmt/test_future.py +++ b/Lib/test/test_future_stmt/test_future.py @@ -349,6 +349,8 @@ def test_annotations(self): eq("(i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3))") eq("{i: 0 for i in (1, 2, 3)}") eq("{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}") + eq("{**x for x in ()}") + eq("[*x for x in ()]") eq("[(x, y) for x, y in (a, b)]") eq("[(x,) for x, in (a,)]") eq("Python3 > Python2 > COBOL") diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 96ebb23f73de9d..3fc084ea6e9c6e 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -7,7 +7,7 @@ Py_GIL_DISABLED) from test.support.import_helper import import_module from test.support.os_helper import temp_dir, TESTFN, unlink -from test.support.script_helper import assert_python_ok, make_script, run_test_script +from test.support.script_helper import assert_python_ok, make_script from test.support import threading_helper, gc_threshold import gc @@ -309,6 +309,26 @@ def func(): self.assertRegex(stdout, rb"""\A\s*func=None""") self.assertFalse(stderr) + def test_datetime_weakref_cycle(self): + # https://github.com/python/cpython/issues/132413 + # If the weakref used by the datetime extension gets cleared by the GC (due to being + # in an unreachable cycle) then datetime functions would crash (get_module_state() + # was returning a NULL pointer). This bug is fixed by clearing weakrefs without + # callbacks *after* running finalizers. + code = """if 1: + import _datetime + class C: + def __del__(self): + print('__del__ called') + _datetime.timedelta(days=1) # crash? + + l = [C()] + l.append(l) + """ + rc, stdout, stderr = assert_python_ok("-c", code) + self.assertEqual(rc, 0) + self.assertEqual(stdout.strip(), b'__del__ called') + @refcount_test def test_frame(self): def f(): @@ -399,11 +419,19 @@ def test_collect_generations(self): # each call to collect(N) x = [] gc.collect(0) - # x is now in the old gen + # x is now in gen 1 a, b, c = gc.get_count() - # We don't check a since its exact values depends on + gc.collect(1) + # x is now in gen 2 + d, e, f = gc.get_count() + gc.collect(2) + # x is now in gen 3 + g, h, i = gc.get_count() + # We don't check a, d, g since their exact values depends on # internal implementation details of the interpreter. self.assertEqual((b, c), (1, 0)) + self.assertEqual((e, f), (0, 1)) + self.assertEqual((h, i), (0, 0)) def test_trashcan(self): class Ouch: @@ -658,9 +686,8 @@ def callback(ignored): gc.collect() self.assertEqual(len(ouch), 2) # else the callbacks didn't run for x in ouch: - # If the callback resurrected one of these guys, the instance - # would be damaged, with an empty __dict__. - self.assertEqual(x, None) + # The weakref should be cleared before executing the callback. + self.assertIsNone(x) def test_bug21435(self): # This is a poor test - its only virtue is that it happened to @@ -782,6 +809,32 @@ def __del__(self): rc, out, err = assert_python_ok('-c', code) self.assertEqual(out.strip(), b'__del__ called') + @unittest.skipIf(Py_GIL_DISABLED, "requires GC generations or increments") + def test_gc_debug_stats(self): + # Checks that debug information is printed to stderr + # when DEBUG_STATS is set. + code = """if 1: + import gc + gc.set_debug(%s) + gc.collect() + """ + _, _, err = assert_python_ok("-c", code % "gc.DEBUG_STATS") + self.assertRegex(err, b"gc: collecting generation [0-9]+") + self.assertRegex( + err, + b"gc: objects in each generation: [0-9]+ [0-9]+ [0-9]+", + ) + self.assertRegex( + err, b"gc: objects in permanent generation: [0-9]+" + ) + self.assertRegex( + err, + b"gc: done, .* unreachable, .* uncollectable, .* elapsed", + ) + + _, _, err = assert_python_ok("-c", code % "0") + self.assertNotIn(b"elapsed", err) + def test_global_del_SystemExit(self): code = """if 1: class ClassWithDel: @@ -801,11 +854,15 @@ def test_get_stats(self): self.assertEqual(len(stats), 3) for st in stats: self.assertIsInstance(st, dict) - self.assertEqual(set(st), - {"collected", "collections", "uncollectable"}) + self.assertEqual( + set(st), + {"collected", "collections", "uncollectable", "candidates", "duration"} + ) self.assertGreaterEqual(st["collected"], 0) self.assertGreaterEqual(st["collections"], 0) self.assertGreaterEqual(st["uncollectable"], 0) + self.assertGreaterEqual(st["candidates"], 0) + self.assertGreaterEqual(st["duration"], 0) # Check that collection counts are incremented correctly if gc.isenabled(): self.addCleanup(gc.enable) @@ -816,11 +873,25 @@ def test_get_stats(self): self.assertEqual(new[0]["collections"], old[0]["collections"] + 1) self.assertEqual(new[1]["collections"], old[1]["collections"]) self.assertEqual(new[2]["collections"], old[2]["collections"]) + self.assertGreater(new[0]["duration"], old[0]["duration"]) + self.assertEqual(new[1]["duration"], old[1]["duration"]) + self.assertEqual(new[2]["duration"], old[2]["duration"]) + for stat in ["collected", "uncollectable", "candidates"]: + self.assertGreaterEqual(new[0][stat], old[0][stat]) + self.assertEqual(new[1][stat], old[1][stat]) + self.assertEqual(new[2][stat], old[2][stat]) gc.collect(2) - new = gc.get_stats() - self.assertEqual(new[0]["collections"], old[0]["collections"] + 1) + old, new = new, gc.get_stats() + self.assertEqual(new[0]["collections"], old[0]["collections"]) self.assertEqual(new[1]["collections"], old[1]["collections"]) self.assertEqual(new[2]["collections"], old[2]["collections"] + 1) + self.assertEqual(new[0]["duration"], old[0]["duration"]) + self.assertEqual(new[1]["duration"], old[1]["duration"]) + self.assertGreater(new[2]["duration"], old[2]["duration"]) + for stat in ["collected", "uncollectable", "candidates"]: + self.assertEqual(new[0][stat], old[0][stat]) + self.assertEqual(new[1][stat], old[1][stat]) + self.assertGreaterEqual(new[2][stat], old[2][stat]) def test_freeze(self): gc.freeze() @@ -844,10 +915,42 @@ def test_get_objects_generations(self): self.assertTrue( any(l is element for element in gc.get_objects(generation=0)) ) - gc.collect() + self.assertFalse( + any(l is element for element in gc.get_objects(generation=1)) + ) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=2)) + ) + gc.collect(generation=0) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=0)) + ) + self.assertTrue( + any(l is element for element in gc.get_objects(generation=1)) + ) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=2)) + ) + gc.collect(generation=1) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=0)) + ) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=1)) + ) + self.assertTrue( + any(l is element for element in gc.get_objects(generation=2)) + ) + gc.collect(generation=2) self.assertFalse( any(l is element for element in gc.get_objects(generation=0)) ) + self.assertFalse( + any(l is element for element in gc.get_objects(generation=1)) + ) + self.assertTrue( + any(l is element for element in gc.get_objects(generation=2)) + ) del l gc.collect() @@ -1136,16 +1239,63 @@ def test_something(self): """) assert_python_ok("-c", source) + def test_do_not_cleanup_type_subclasses_before_finalization(self): + # See https://github.com/python/cpython/issues/135552 + # If we cleanup weakrefs for tp_subclasses before calling + # the finalizer (__del__) then the line `fail = BaseNode.next.next` + # should fail because we are trying to access a subclass + # attribute. But subclass type cache was not properly invalidated. + code = """ + class BaseNode: + def __del__(self): + BaseNode.next = BaseNode.next.next + fail = BaseNode.next.next + + class Node(BaseNode): + pass + + BaseNode.next = Node() + BaseNode.next.next = Node() + """ + # this test checks garbage collection while interp + # finalization + assert_python_ok("-c", textwrap.dedent(code)) + + code_inside_function = textwrap.dedent(F""" + def test(): + {textwrap.indent(code, ' ')} + + test() + """) + # this test checks regular garbage collection + assert_python_ok("-c", code_inside_function) + + + @unittest.skipUnless(Py_GIL_DISABLED, "requires free-threaded GC") + @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") + def test_tuple_untrack_counts(self): + # This ensures that the free-threaded GC is counting untracked tuples + # in the "long_lived_total" count. This is required to avoid + # performance issues from running the GC too frequently. See + # GH-142531 as an example. + gc.collect() + count = _testinternalcapi.get_long_lived_total() + n = 20_000 + tuples = [(x,) for x in range(n)] + gc.collect() + new_count = _testinternalcapi.get_long_lived_total() + self.assertFalse(gc.is_tracked(tuples[0])) + # Use n // 2 just in case some other objects were collected. + self.assertTrue(new_count - count > (n // 2)) -class IncrementalGCTests(unittest.TestCase): + @requires_gil_enabled('need generational GC') @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") - @requires_gil_enabled("Free threading does not support incremental GC") - def test_incremental_gc_handles_fast_cycle_creation(self): - # Run this test in a fresh process. The number of alive objects (which can - # be from unit tests run before this one) can influence how quickly cyclic - # garbage is found. - script = support.findfile("_test_gc_fast_cycles.py") - run_test_script(script) + def test_heap_size(self): + count = _testinternalcapi.get_tracked_heap_size() + l = [] + self.assertEqual(count + 1, _testinternalcapi.get_tracked_heap_size()) + del l + self.assertEqual(count, _testinternalcapi.get_tracked_heap_size()) class GCCallbackTests(unittest.TestCase): @@ -1222,9 +1372,11 @@ def test_collect(self): # Check that we got the right info dict for all callbacks for v in self.visit: info = v[2] - self.assertTrue("generation" in info) - self.assertTrue("collected" in info) - self.assertTrue("uncollectable" in info) + self.assertIn("generation", info) + self.assertIn("collected", info) + self.assertIn("uncollectable", info) + self.assertIn("candidates", info) + self.assertIn("duration", info) def test_collect_generation(self): self.preclean() @@ -1335,6 +1487,7 @@ def setUp(self): def tearDown(self): gc.disable() + @unittest.skipIf(Py_GIL_DISABLED, "requires GC generations or increments") def test_bug1055820c(self): # Corresponds to temp2c.py in the bug report. This is pretty # elaborate. @@ -1396,6 +1549,7 @@ def callback(ignored): # The free-threaded build doesn't have multiple generations, so # just trigger a GC manually. gc.collect() + assert not detector.gc_happened while not detector.gc_happened: i += 1 if i > 10000: @@ -1410,6 +1564,7 @@ def callback(ignored): self.assertEqual(x, None) @gc_threshold(1000, 0, 0) + @unittest.skipIf(Py_GIL_DISABLED, "requires GC generations or increments") def test_bug1055820d(self): # Corresponds to temp2d.py in the bug report. This is very much like # test_bug1055820c, but uses a __del__ method instead of a weakref @@ -1507,6 +1662,20 @@ def test_indirect_calls_with_gc_disabled(self): finally: gc.enable() + # Ensure that setting *threshold0* to zero disables collection. + @gc_threshold(0) + def test_threshold_zero(self): + junk = [] + i = 0 + detector = GC_Detector() + while not detector.gc_happened: + i += 1 + if i > 50000: + break + junk.append([]) # this may eventually trigger gc (if it is enabled) + + self.assertEqual(i, 50001) + class PythonFinalizationTests(unittest.TestCase): def test_ast_fini(self): @@ -1528,6 +1697,19 @@ def test_ast_fini(self): """) assert_python_ok("-c", code) + def test_warnings_fini(self): + # See https://github.com/python/cpython/issues/137384 + code = textwrap.dedent(''' + import asyncio + from contextvars import ContextVar + + context_loop = ContextVar("context_loop", default=None) + loop = asyncio.new_event_loop() + context_loop.set(loop) + ''') + + assert_python_ok("-c", code) + def setUpModule(): global enabled, debug diff --git a/Lib/test/test_gc_stats.py b/Lib/test/test_gc_stats.py new file mode 100644 index 00000000000000..bd75924397e76e --- /dev/null +++ b/Lib/test/test_gc_stats.py @@ -0,0 +1,350 @@ +import gc +import os +import textwrap +import time +import unittest + +from test.support import ( + Py_GIL_DISABLED, + import_helper, + requires_gil_enabled, + requires_remote_subprocess_debugging, +) +from test.test_profiling.test_sampling_profiler.helpers import test_subprocess + +try: + import _remote_debugging # noqa: F401 +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) + + +GC_STATS_FIELDS = ( + "gen", "iid", "ts_start", "ts_stop", "collections", "collected", + "uncollectable", "candidates", "heap_size", "duration") + + +def get_interpreter_identifiers(gc_stats) -> tuple[int,...]: + return tuple(sorted({s.iid for s in gc_stats})) + + +def get_generations(gc_stats) -> tuple[int,int,int]: + generations = set() + for s in gc_stats: + generations.add(s.gen) + + return tuple(sorted(generations)) + + +def get_last_item(gc_stats, generation: int, iid: int): + item = None + for s in gc_stats: + if s.gen == generation and s.iid == iid: + if item is None or item.ts_start < s.ts_start: + item = s + + return item + + +def has_local_process_debugging(): + try: + return _remote_debugging.is_python_process(os.getpid()) + except Exception: + return False + + +def check_gc_stats_fields(testcase, stats): + testcase.assertIsInstance(stats, list) + testcase.assertGreater(len(stats), 0) + for item in stats: + testcase.assertIsInstance(item, _remote_debugging.GCStatsInfo) + testcase.assertEqual(type(item).__match_args__, GC_STATS_FIELDS) + testcase.assertEqual(len(item), len(GC_STATS_FIELDS)) + + +def gc_stats_counters_advanced(before_stats, after_stats, generations, iid): + for generation in generations: + before = get_last_item(before_stats, generation, iid) + after = get_last_item(after_stats, generation, iid) + if after is None or before is None: + return False + if after.duration <= before.duration: + return False + if after.candidates <= before.candidates: + return False + return True + + +@unittest.skipUnless( + has_local_process_debugging(), "requires local process debugging") +class TestLocalGCStats(unittest.TestCase): + + _main_iid = 0 # main interpreter ID + + def test_gc_stats_fields(self): + monitor = _remote_debugging.GCMonitor(os.getpid(), debug=True) + stats = monitor.get_gc_stats(all_interpreters=False) + check_gc_stats_fields(self, stats) + + def test_module_get_gc_stats_fields(self): + stats = _remote_debugging.get_gc_stats( + os.getpid(), all_interpreters=False) + check_gc_stats_fields(self, stats) + + def test_all_interpreters_filter_for_local_process(self): + interpreters = import_helper.import_module("concurrent.interpreters") + source = """ + import gc + objects = [] + obj = [] + obj.append(obj) + objects.append(obj) + gc.collect(0) + gc.collect(1) + gc.collect(2) + """ + interp = interpreters.create() + try: + interp.exec(textwrap.dedent(source)) + for generation in range(3): + gc.collect(generation) + + main_stats = _remote_debugging.get_gc_stats( + os.getpid(), all_interpreters=False) + all_stats = _remote_debugging.get_gc_stats( + os.getpid(), all_interpreters=True) + finally: + interp.close() + + self.assertEqual(get_interpreter_identifiers(main_stats), (0,)) + self.assertIn(0, get_interpreter_identifiers(all_stats)) + self.assertGreater(len(get_interpreter_identifiers(all_stats)), 1) + self.assertEqual(get_generations(main_stats), (0, 1, 2)) + self.assertEqual(get_generations(all_stats), (0, 1, 2)) + for iid in get_interpreter_identifiers(all_stats): + for generation in range(3): + self.assertIsNotNone(get_last_item(all_stats, generation, iid)) + + @unittest.skipUnless(Py_GIL_DISABLED, "requires free-threaded GC") + def test_gc_stats_counters_for_main_interpreter_free_threaded(self): + generations = (0, 1, 2) + before_stats = _remote_debugging.get_gc_stats( + os.getpid(), all_interpreters=False) + for generation in generations: + self.assertIsNotNone( + get_last_item(before_stats, generation, self._main_iid)) + + objects = [] + for _ in range(1000): + obj = [] + obj.append(obj) + objects.append(obj) + for generation in generations: + gc.collect(generation) + + after_stats = _remote_debugging.get_gc_stats( + os.getpid(), all_interpreters=False) + self.assertTrue( + gc_stats_counters_advanced( + before_stats, after_stats, generations, self._main_iid), + (before_stats, after_stats) + ) + + +@requires_remote_subprocess_debugging() +class TestGCStats(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls._main_iid = 0 # main interpreter ID + cls._main_interpreter_script = ''' + import gc + import time + + gc.collect(0) + gc.collect(1) + gc.collect(2) + + _test_sock.sendall(b"working") + objects = [] + while True: + if len(objects) > 100: + objects = [] + + obj = [] + obj.append(obj) + objects.append(obj) + + time.sleep(0.1) + gc.collect(0) + gc.collect(1) + gc.collect(2) + ''' + cls._script = ''' + import concurrent.interpreters as interpreters + import gc + import time + + source = """if True: + import gc + + if "objects" not in globals(): + objects = [] + if len(objects) > 100: + objects = [] + + obj = [] + obj.append(obj) + objects.append(obj) + + gc.collect(0) + gc.collect(1) + gc.collect(2) + """ + + if {0}: + interp = interpreters.create() + interp.exec(source) + + gc.collect(0) + gc.collect(1) + gc.collect(2) + + _test_sock.sendall(b"working") + objects = [] + while True: + if len(objects) > 100: + objects = [] + + obj = [] + obj.append(obj) + objects.append(obj) + + time.sleep(0.1) + if {0}: + interp.exec(source) + gc.collect(0) + gc.collect(1) + gc.collect(2) + ''' + + def _gc_stats_advanced(self, before_stats, after_stats, generations): + for generation in generations: + before = get_last_item(before_stats, generation, self._main_iid) + after = get_last_item(after_stats, generation, self._main_iid) + if after is None or before is None: + return False + if after.ts_stop <= before.ts_stop: + return False + return True + + def _collect_gc_stats(self, script: str, all_interpreters: bool, + generations=(2,)): + with (test_subprocess(script, wait_for_working=True) as subproc): + monitor = _remote_debugging.GCMonitor(subproc.process.pid, debug=True) + before_stats = monitor.get_gc_stats(all_interpreters=all_interpreters) + for generation in generations: + before = get_last_item(before_stats, generation, self._main_iid) + self.assertIsNotNone(before) + + after_stats = before_stats + for _ in range(10): + time.sleep(0.5) + after_stats = monitor.get_gc_stats(all_interpreters=all_interpreters) + if self._gc_stats_advanced(before_stats, after_stats, generations): + break + else: + self.fail( + f"GC stats for generations {generations!r} did not " + f"advance: {before_stats!r} -> {after_stats!r}" + ) + + return before_stats, after_stats + + def _check_gc_stats(self, before, after): + self.assertIsNotNone(before) + self.assertIsNotNone(after) + + self.assertGreater(after.collections, before.collections, (before, after)) + self.assertGreater(after.ts_start, before.ts_start, (before, after)) + self.assertGreater(after.ts_stop, before.ts_stop, (before, after)) + self.assertGreater(after.duration, before.duration, (before, after)) + + self.assertGreater(after.candidates, before.candidates, (before, after)) + + # may not grow + self.assertGreaterEqual(after.collected, before.collected, (before, after)) + self.assertGreaterEqual(after.uncollectable, before.uncollectable, (before, after)) + + def _check_interpreter_gc_stats(self, before_stats, after_stats): + before_iids = get_interpreter_identifiers(before_stats) + after_iids = get_interpreter_identifiers(after_stats) + + self.assertEqual(before_iids, after_iids) + + self.assertEqual(get_generations(before_stats), (0, 1, 2)) + self.assertEqual(get_generations(after_stats), (0, 1, 2)) + + for iid in after_iids: + with self.subTest(f"interpreter id={iid}"): + before_last_items = (get_last_item(before_stats, 0, iid), + get_last_item(before_stats, 1, iid), + get_last_item(before_stats, 2, iid)) + + after_last_items = (get_last_item(after_stats, 0, iid), + get_last_item(after_stats, 1, iid), + get_last_item(after_stats, 2, iid)) + + for before, after in zip(before_last_items, after_last_items): + self._check_gc_stats(before, after) + + def test_gc_stats_timestamps_for_main_interpreter(self): + script = textwrap.dedent(self._main_interpreter_script) + before_stats, after_stats = self._collect_gc_stats( + script, False, generations=(0, 1, 2)) + + for generation in range(3): + with self.subTest(generation=generation): + before = get_last_item(before_stats, generation, self._main_iid) + after = get_last_item(after_stats, generation, self._main_iid) + + self.assertIsNotNone(before) + self.assertIsNotNone(after) + self.assertGreater( + after.collections, before.collections, + (before, after)) + self.assertGreater( + after.ts_start, before.ts_start, + (before, after)) + self.assertGreater( + after.ts_stop, before.ts_stop, + (before, after)) + + @requires_gil_enabled() + def test_gc_stats_for_main_interpreter(self): + script = textwrap.dedent(self._script.format(False)) + before_stats, after_stats = self._collect_gc_stats(script, False) + + self._check_interpreter_gc_stats(before_stats, after_stats) + + @requires_gil_enabled() + def test_gc_stats_for_main_interpreter_if_subinterpreter_exists(self): + script = textwrap.dedent(self._script.format(True)) + before_stats, after_stats = self._collect_gc_stats(script, False) + + self._check_interpreter_gc_stats(before_stats, after_stats) + + @requires_gil_enabled() + def test_gc_stats_for_all_interpreters(self): + script = textwrap.dedent(self._script.format(True)) + before_stats, after_stats = self._collect_gc_stats(script, True) + + before_iids = get_interpreter_identifiers(before_stats) + after_iids = get_interpreter_identifiers(after_stats) + + self.assertGreater(len(before_iids), 1) + self.assertGreater(len(after_iids), 1) + self.assertEqual(before_iids, after_iids) + + self._check_interpreter_gc_stats(before_stats, after_stats) diff --git a/Lib/test/test_gdb/gdb_jit_sample.py b/Lib/test/test_gdb/gdb_jit_sample.py new file mode 100644 index 00000000000000..b439e82e8b312f --- /dev/null +++ b/Lib/test/test_gdb/gdb_jit_sample.py @@ -0,0 +1,27 @@ +# Sample script for use by test_gdb.test_jit + +import _testinternalcapi +import operator + + +WARMUP_ITERATIONS = _testinternalcapi.TIER2_THRESHOLD + 10 + + +def jit_bt_hot(depth, warming_up_caller=False): + if depth == 0: + if not warming_up_caller: + id(42) + return + + for iteration in range(WARMUP_ITERATIONS): + operator.call( + jit_bt_hot, + depth - 1, + warming_up_caller or iteration + 1 != WARMUP_ITERATIONS, + ) + + +# Warm the shared shim once without hitting builtin_id so the real run uses +# the steady-state shim path when GDB breaks inside id(42). +jit_bt_hot(1, warming_up_caller=True) +jit_bt_hot(1) diff --git a/Lib/test/test_gdb/test_jit.py b/Lib/test/test_gdb/test_jit.py new file mode 100644 index 00000000000000..7cb604f8aee8c3 --- /dev/null +++ b/Lib/test/test_gdb/test_jit.py @@ -0,0 +1,210 @@ +import os +import platform +import re +import sys +import unittest + +from test.support import import_helper + +from .util import setup_module, DebuggerTests + + +_testinternalcapi = import_helper.import_module("_testinternalcapi") +NATIVE_JIT_ENABLED = ( + hasattr(sys, "_jit") + and sys._jit.is_enabled() + and _testinternalcapi.get_jit_backend() == "jit" +) + +JIT_SAMPLE_SCRIPT = os.path.join(os.path.dirname(__file__), "gdb_jit_sample.py") +# In batch GDB, break in builtin_id() while it is running under JIT, +# then repeatedly "finish" until the selected frame is the JIT executor. +# That gives a deterministic backtrace starting with py::jit:executor. +# +# builtin_id() sits only a few helper frames above the JIT entry on this path. +# This bound is just a generous upper limit so the test fails clearly if the +# expected stack shape changes. +MAX_FINISH_STEPS = 20 +# After landing on the JIT entry frame, single-step a bounded number of +# instructions further into the blob so the backtrace is taken from JIT code +# itself rather than the immediate helper-return site. The exact number of +# steps is not significant: each step is cross-checked against the selected +# frame's symbol so the test fails loudly if stepping escapes the registered +# JIT region, instead of asserting against a misleading backtrace. +MAX_JIT_ENTRY_STEPS = 4 +EVAL_FRAME_RE = r"(_PyEval_EvalFrameDefault|_PyEval_Vector)" +JIT_EXECUTOR_FRAME = "py::jit:executor" +JIT_ENTRY_SYMBOL = "_PyJIT_Entry" +BACKTRACE_FRAME_RE = re.compile(r"^#\d+\s+.*$", re.MULTILINE) + +FINISH_TO_JIT_EXECUTOR = ( + "python exec(\"import gdb\\n" + f"target = {JIT_EXECUTOR_FRAME!r}\\n" + f"for _ in range({MAX_FINISH_STEPS}):\\n" + " frame = gdb.selected_frame()\\n" + " if frame is not None and frame.name() == target:\\n" + " break\\n" + " gdb.execute('finish')\\n" + "else:\\n" + " raise RuntimeError('did not reach %s' % target)\\n\")" +) +STEP_INSIDE_JIT_EXECUTOR = ( + "python exec(\"import gdb\\n" + f"target = {JIT_EXECUTOR_FRAME!r}\\n" + f"for _ in range({MAX_JIT_ENTRY_STEPS}):\\n" + " frame = gdb.selected_frame()\\n" + " if frame is None or frame.name() != target:\\n" + " raise RuntimeError('left JIT region during stepping: '\\n" + " + repr(frame and frame.name()))\\n" + " gdb.execute('si')\\n" + "frame = gdb.selected_frame()\\n" + "if frame is None or frame.name() != target:\\n" + " raise RuntimeError('stepped out of JIT region after si')\\n\")" +) + + +def setUpModule(): + setup_module() + + +# The GDB JIT interface registration is gated on __linux__ && __ELF__ in +# Python/jit_unwind.c, and the synthetic EH-frame is only implemented for +# x86_64 and AArch64 (a #error fires otherwise). Skip cleanly on other +# platforms or architectures instead of producing timeouts / empty backtraces. +# sys._jit.is_enabled() is true for --enable-experimental-jit=interpreter, +# but these tests need native JIT code and a py::jit:executor frame. +@unittest.skipUnless(sys.platform == "linux", + "GDB JIT interface is only implemented for Linux + ELF") +@unittest.skipUnless(platform.machine() in ("x86_64", "aarch64"), + "GDB JIT CFI emitter only supports x86_64 and AArch64") +@unittest.skipUnless(NATIVE_JIT_ENABLED, + "requires native JIT execution active") +class JitBacktraceTests(DebuggerTests): + def get_stack_trace(self, **kwargs): + # These tests validate the JIT-relevant part of the backtrace via + # _assert_jit_backtrace_shape, so an unrelated "?? ()" frame below + # the JIT/eval segment (e.g. libc without debug info) is tolerable. + kwargs.setdefault("skip_on_truncation", False) + return super().get_stack_trace(**kwargs) + + def _extract_backtrace_frames(self, gdb_output): + frames = BACKTRACE_FRAME_RE.findall(gdb_output) + self.assertGreater( + len(frames), 0, + f"expected at least one GDB backtrace frame in output:\n{gdb_output}", + ) + return frames + + def _assert_jit_backtrace_shape(self, gdb_output, *, anchor_at_top): + # Shape assertions applied to every JIT backtrace we produce: + # 1. The synthetic JIT symbol appears exactly once. A second + # py::jit:executor frame would mean the unwinder is + # materializing two native frames for a single logical JIT + # region, or failing to unwind out of the region entirely. + # 2. The unwinder must climb directly back out of the JIT region + # into the eval loop. _PyJIT_Entry only exists to establish the + # physical frame; the synthetic executor FDE collapses it away. + # 3. For tests that assert a specific entry PC, the JIT frame + # is also at #0. + frames = self._extract_backtrace_frames(gdb_output) + backtrace = "\n".join(frames) + + jit_frames = [frame for frame in frames if JIT_EXECUTOR_FRAME in frame] + jit_count = len(jit_frames) + self.assertEqual( + jit_count, 1, + f"expected exactly 1 {JIT_EXECUTOR_FRAME} frame, got {jit_count}\n" + f"backtrace:\n{backtrace}", + ) + eval_frames = [frame for frame in frames if re.search(EVAL_FRAME_RE, frame)] + eval_count = len(eval_frames) + self.assertGreaterEqual( + eval_count, 1, + f"expected at least one _PyEval_* frame, got {eval_count}\n" + f"backtrace:\n{backtrace}", + ) + jit_frame_index = next( + i for i, frame in enumerate(frames) if JIT_EXECUTOR_FRAME in frame + ) + frames_after_jit = frames[jit_frame_index + 1:] + first_eval_offset = next( + ( + i for i, frame in enumerate(frames_after_jit) + if re.search(EVAL_FRAME_RE, frame) + ), + None, + ) + self.assertIsNotNone( + first_eval_offset, + f"expected an eval frame after the JIT frame\n" + f"backtrace:\n{backtrace}", + ) + unexpected_between = frames_after_jit[:first_eval_offset] + self.assertFalse( + unexpected_between, + "expected the executor frame to unwind directly into eval\n" + f"backtrace:\n{backtrace}", + ) + relevant_end = max( + i + for i, frame in enumerate(frames) + if ( + JIT_EXECUTOR_FRAME in frame + or re.search(EVAL_FRAME_RE, frame) + ) + ) + truncated_frames = [ + frame for frame in frames[: relevant_end + 1] + if " ?? ()" in frame + ] + self.assertFalse( + truncated_frames, + "unexpected truncated frame before the validated JIT/eval segment\n" + f"backtrace:\n{backtrace}", + ) + if anchor_at_top: + self.assertRegex( + frames[0], + re.compile(rf"^#0\s+{re.escape(JIT_EXECUTOR_FRAME)}"), + ) + + def test_bt_unwinds_through_jit_frames(self): + gdb_output = self.get_stack_trace( + script=JIT_SAMPLE_SCRIPT, + cmds_after_breakpoint=["bt"], + PYTHON_JIT="1", + ) + # The executor should appear as a named JIT frame and unwind back into + # the eval loop. + self._assert_jit_backtrace_shape(gdb_output, anchor_at_top=False) + + def test_bt_handoff_from_jit_entry_to_executor(self): + gdb_output = self.get_stack_trace( + script=JIT_SAMPLE_SCRIPT, + breakpoint=JIT_ENTRY_SYMBOL, + cmds_after_breakpoint=[ + "delete 1", + "tbreak builtin_id", + "continue", + "bt", + ], + PYTHON_JIT="1", + ) + # If we stop first in the shim and then continue into the real JIT + # workload, the final backtrace should match the architecture's + # executor unwind contract. + self._assert_jit_backtrace_shape(gdb_output, anchor_at_top=False) + + def test_bt_unwinds_from_inside_jit_executor(self): + gdb_output = self.get_stack_trace( + script=JIT_SAMPLE_SCRIPT, + cmds_after_breakpoint=[ + FINISH_TO_JIT_EXECUTOR, + STEP_INSIDE_JIT_EXECUTOR, + "bt", + ], + PYTHON_JIT="1", + ) + # Once the selected PC is inside the JIT executor, we require that GDB + # identifies the JIT frame at #0 and keeps unwinding into _PyEval_*. + self._assert_jit_backtrace_shape(gdb_output, anchor_at_top=True) diff --git a/Lib/test/test_gdb/test_pretty_print.py b/Lib/test/test_gdb/test_pretty_print.py index dfc77d65ab16a4..db3064e3df54c2 100644 --- a/Lib/test/test_gdb/test_pretty_print.py +++ b/Lib/test/test_gdb/test_pretty_print.py @@ -82,7 +82,14 @@ def test_dicts(self): self.assertGdbRepr({}) self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}") # Python preserves insertion order since 3.6 - self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'foo': 'bar', 'douglas': 42}") + self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, + "{'foo': 'bar', 'douglas': 42}") + + # frozendict + self.assertGdbRepr(frozendict(), + "frozendict({})") + self.assertGdbRepr(frozendict({'foo': 'bar', 'douglas': 42}), + "frozendict({'foo': 'bar', 'douglas': 42})") def test_lists(self): 'Verify the pretty-printing of lists' diff --git a/Lib/test/test_gdb/util.py b/Lib/test/test_gdb/util.py index 8097fd52ababe6..d903adcf2903f3 100644 --- a/Lib/test/test_gdb/util.py +++ b/Lib/test/test_gdb/util.py @@ -20,6 +20,27 @@ PYTHONHASHSEED = '123' +# gh-91960, bpo-40019: gdb reports these when the optimizer has dropped +# python-frame debug info; the test can't read what's not there. +_OPTIMIZED_OUT_PATTERNS = ( + '(frame information optimized out)', + 'Unable to read information on python frame', + '(unable to read python frame information)', +) +# gdb prints this when the unwinder genuinely failed to walk a frame — +# i.e. the CFI (ours or a library's) is wrong. Treat as a hard failure, +# not a skip, so regressions in our own unwind info don't hide. +_UNWIND_FAILURE_PATTERNS = ( + 'Backtrace stopped: frame did not save the PC', +) +# gh-104736: " ?? ()" in the bt usually means the unwinder bailed early, +# but can also be unrelated frames without debug info (e.g. libc). Tests +# that validate the JIT-relevant part of the backtrace themselves can +# opt out via skip_on_truncation=False. +_TRUNCATED_BACKTRACE_PATTERNS = ( + ' ?? ()', +) + def clean_environment(): # Remove PYTHON* environment variables such as PYTHONHOME @@ -160,7 +181,9 @@ def get_stack_trace(self, source=None, script=None, breakpoint=BREAKPOINT_FN, cmds_after_breakpoint=None, import_site=False, - ignore_stderr=False): + ignore_stderr=False, + skip_on_truncation=True, + **env_vars): ''' Run 'python -c SOURCE' under gdb with a breakpoint. @@ -239,7 +262,7 @@ def get_stack_trace(self, source=None, script=None, args += [script] # Use "args" to invoke gdb, capturing stdout, stderr: - out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED) + out, err = run_gdb(*args, PYTHONHASHSEED=PYTHONHASHSEED, **env_vars) if not ignore_stderr: for line in err.splitlines(): @@ -255,26 +278,20 @@ def get_stack_trace(self, source=None, script=None, " because the Program Counter is" " not present") + for pattern in _UNWIND_FAILURE_PATTERNS: + if pattern in out: + raise AssertionError( + f"gdb unwinder failed ({pattern!r}) — CFI bug in our " + f"generated code or in a linked library.\n" + f"Full gdb output:\n{out}" + ) + # bpo-40019: Skip the test if gdb failed to read debug information # because the Python binary is optimized. - for pattern in ( - '(frame information optimized out)', - 'Unable to read information on python frame', - - # gh-91960: On Python built with "clang -Og", gdb gets - # "frame=" for _PyEval_EvalFrameDefault() parameter - '(unable to read python frame information)', - - # gh-104736: On Python built with "clang -Og" on ppc64le, - # "py-bt" displays a truncated or not traceback, but "where" - # logs this error message: - 'Backtrace stopped: frame did not save the PC', - - # gh-104736: When "bt" command displays something like: - # "#1 0x0000000000000000 in ?? ()", the traceback is likely - # truncated or wrong. - ' ?? ()', - ): + patterns = _OPTIMIZED_OUT_PATTERNS + if skip_on_truncation: + patterns = patterns + _TRUNCATED_BACKTRACE_PATTERNS + for pattern in patterns: if pattern in out: raise unittest.SkipTest(f"{pattern!r} found in gdb output") diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index ec44a0f9ce3fb3..0cc44ff95b59d8 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -29,12 +29,13 @@ def skip_if_different_mount_drives(): test_tools.skip_if_missing("cases_generator") with test_tools.imports_under_tool("cases_generator"): - from analyzer import StackItem + from analyzer import StackItem, analyze_files from cwriter import CWriter import parser from stack import Local, Stack import tier1_generator import optimizer_generator + import record_function_generator def handle_stderr(): @@ -132,7 +133,7 @@ def test_inst_no_args(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -154,7 +155,7 @@ def test_inst_one_pop(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -165,7 +166,7 @@ def test_inst_one_pop(self): value = stack_pointer[-1]; SPAM(value); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -179,7 +180,7 @@ def test_inst_one_push(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -190,7 +191,7 @@ def test_inst_one_push(self): res = SPAM(); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -205,7 +206,7 @@ def test_inst_one_push_one_pop(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -232,7 +233,7 @@ def test_binary_op(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -247,7 +248,7 @@ def test_binary_op(self): res = SPAM(left, right); stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -262,7 +263,7 @@ def test_overlap(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -296,7 +297,7 @@ def test_predictions(self): """ output = """ TARGET(OP1) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP1; (void)(opcode); #endif @@ -313,7 +314,7 @@ def test_predictions(self): } TARGET(OP3) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP3; (void)(opcode); #endif @@ -355,7 +356,7 @@ def test_sync_sp(self): """ output = """ TARGET(A) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = A; (void)(opcode); #endif @@ -366,19 +367,19 @@ def test_sync_sp(self): _PyStackRef res; arg = stack_pointer[-1]; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); escaping_call(); stack_pointer = _PyFrame_GetStackPointer(frame); res = Py_None; stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(B) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = B; (void)(opcode); #endif @@ -421,7 +422,7 @@ def test_error_if_plain(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -444,7 +445,7 @@ def test_error_if_plain_with_comment(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -470,7 +471,7 @@ def test_error_if_pop(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -489,7 +490,7 @@ def test_error_if_pop(self): res = 0; stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -505,7 +506,7 @@ def test_error_if_pop_with_result(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -523,7 +524,7 @@ def test_error_if_pop_with_result(self): } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -537,7 +538,7 @@ def test_cache_effect(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -553,7 +554,7 @@ def test_cache_effect(self): uint32_t extra = read_u32(&this_instr[2].cache); (void)extra; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -570,7 +571,7 @@ def test_suppress_dispatch(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -604,7 +605,7 @@ def test_macro_instruction(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -640,12 +641,12 @@ def test_macro_instruction(self): } stack_pointer[-3] = res; stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(OP1) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP1; (void)(opcode); #endif @@ -667,7 +668,7 @@ def test_macro_instruction(self): } TARGET(OP3) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP3; (void)(opcode); #endif @@ -688,7 +689,7 @@ def test_macro_instruction(self): stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer[-3] = res; stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -702,7 +703,7 @@ def test_unused_caches(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -728,7 +729,7 @@ def test_pseudo_instruction_no_flags(self): """ output = """ TARGET(OP1) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP1; (void)(opcode); #endif @@ -751,7 +752,7 @@ def test_pseudo_instruction_with_flags(self): """ output = """ TARGET(OP1) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP1; (void)(opcode); #endif @@ -777,7 +778,7 @@ def test_pseudo_instruction_as_sequence(self): """ output = """ TARGET(OP1) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP1; (void)(opcode); #endif @@ -788,7 +789,7 @@ def test_pseudo_instruction_as_sequence(self): } TARGET(OP2) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP2; (void)(opcode); #endif @@ -812,7 +813,7 @@ def test_array_input(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -827,7 +828,7 @@ def test_array_input(self): below = stack_pointer[-2 - oparg*2]; SPAM(values, oparg); stack_pointer += -2 - oparg*2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -843,7 +844,7 @@ def test_array_output(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -860,7 +861,7 @@ def test_array_output(self): stack_pointer[-2] = below; stack_pointer[-1 + oparg*3] = above; stack_pointer += oparg*3; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -875,7 +876,7 @@ def test_array_input_output(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -889,7 +890,7 @@ def test_array_input_output(self): above = 0; stack_pointer[0] = above; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -905,7 +906,7 @@ def test_array_error_if(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -918,11 +919,11 @@ def test_array_error_if(self): extra = stack_pointer[-1 - oparg]; if (oparg == 0) { stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -940,7 +941,7 @@ def test_macro_push_push(self): """ output = """ TARGET(M) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = M; (void)(opcode); #endif @@ -960,7 +961,7 @@ def test_macro_push_push(self): stack_pointer[0] = val1; stack_pointer[1] = val2; stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -977,7 +978,7 @@ def test_override_inst(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -1002,7 +1003,7 @@ def test_override_op(self): """ output = """ TARGET(M) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = M; (void)(opcode); #endif @@ -1023,7 +1024,7 @@ def test_annotated_inst(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -1045,7 +1046,7 @@ def test_annotated_op(self): """ output = """ TARGET(M) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = M; (void)(opcode); #endif @@ -1086,7 +1087,7 @@ def test_array_of_one(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -1128,7 +1129,7 @@ def test_unused_named_values(self): """ output = """ TARGET(INST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INST; (void)(opcode); #endif @@ -1158,7 +1159,7 @@ def test_used_unused_used(self): """ output = """ TARGET(TEST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TEST; (void)(opcode); #endif @@ -1202,7 +1203,7 @@ def test_unused_used_used(self): """ output = """ TARGET(TEST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TEST; (void)(opcode); #endif @@ -1245,7 +1246,7 @@ def test_flush(self): """ output = """ TARGET(TEST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TEST; (void)(opcode); #endif @@ -1263,13 +1264,13 @@ def test_flush(self): stack_pointer[0] = a; stack_pointer[1] = b; stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); // SECOND { USE(a, b); } stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -1297,7 +1298,7 @@ def test_pop_on_error_peeks(self): """ output = """ TARGET(TEST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TEST; (void)(opcode); #endif @@ -1325,7 +1326,7 @@ def test_pop_on_error_peeks(self): } } stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -1348,7 +1349,7 @@ def test_push_then_error(self): output = """ TARGET(TEST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TEST; (void)(opcode); #endif @@ -1368,14 +1369,14 @@ def test_push_then_error(self): stack_pointer[0] = a; stack_pointer[1] = b; stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } } stack_pointer[0] = a; stack_pointer[1] = b; stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -1393,7 +1394,7 @@ def test_error_if_true(self): """ output = """ TARGET(OP1) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP1; (void)(opcode); #endif @@ -1404,7 +1405,7 @@ def test_error_if_true(self): } TARGET(OP2) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP2; (void)(opcode); #endif @@ -1466,7 +1467,7 @@ def test_stack_save_reload(self): output = """ TARGET(BALANCED) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BALANCED; (void)(opcode); #endif @@ -1492,7 +1493,7 @@ def test_stack_save_reload_paired(self): output = """ TARGET(BALANCED) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BALANCED; (void)(opcode); #endif @@ -1514,7 +1515,7 @@ def test_stack_reload_only(self): output = """ TARGET(BALANCED) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BALANCED; (void)(opcode); #endif @@ -1539,7 +1540,7 @@ def test_stack_save_only(self): output = """ TARGET(BALANCED) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BALANCED; (void)(opcode); #endif @@ -1565,14 +1566,14 @@ def test_instruction_size_macro(self): output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(OP); - frame->return_offset = 1 ; + frame->return_offset = 1u ; DISPATCH(); } """ @@ -1604,7 +1605,7 @@ def test_escaping_call_next_to_cmacro(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -1642,7 +1643,7 @@ def test_pystackref_frompyobject_new_next_to_cmacro(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -1661,7 +1662,7 @@ def test_pystackref_frompyobject_new_next_to_cmacro(self): stack_pointer[0] = out1; stack_pointer[1] = out2; stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } """ @@ -1840,7 +1841,7 @@ def test_reassigning_live_inputs(self): output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -1867,7 +1868,7 @@ def test_reassigning_dead_inputs(self): """ output = """ TARGET(OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = OP; (void)(opcode); #endif @@ -1881,7 +1882,7 @@ def test_reassigning_dead_inputs(self): stack_pointer = _PyFrame_GetStackPointer(frame); in = temp; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(in); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -1890,6 +1891,515 @@ def test_reassigning_dead_inputs(self): """ self.run_cases_test(input, output) + def test_recording_after_specializing_with_cache(self): + input = """ + specializing op(SPEC, (counter/1 --)) { + spam; + } + + tier2 op(REC, (--)) { + RECORD_VALUE(0); + } + + op(BODY, (--)) { + ham; + } + + macro(OP) = SPEC + unused/2 + REC + BODY; + """ + output = """ + TARGET(OP) { + #if _Py_TAIL_CALL_INTERP + int opcode = OP; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(OP); + // SPEC + { + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + spam; + } + /* Skip 2 cache entries */ + // BODY + { + ham; + } + DISPATCH(); + } + """ + self.run_cases_test(input, output) + + def test_recording_after_non_specializing(self): + input = """ + op(REGULAR, (--)) { + spam; + } + + tier2 op(REC, (--)) { + RECORD_VALUE(0); + } + + macro(OP) = REGULAR + REC; + """ + with self.assertRaisesRegex(SyntaxError, "Recording uop"): + self.run_cases_test(input, "") + + def test_multiple_consecutive_recording_uops(self): + """Multiple consecutive recording uops at the start of a macro are legal.""" + input = """ + tier2 op(_RECORD_A, (a, b -- a, b)) { + RECORD_VALUE(a); + } + tier2 op(_RECORD_B, (a, b -- a, b)) { + RECORD_VALUE(b); + } + op(_DO_STUFF, (a, b -- res)) { + res = a; + INPUTS_DEAD(); + } + macro(OP) = _RECORD_A + _RECORD_B + _DO_STUFF; + """ + output = """ + TARGET(OP) { + #if _Py_TAIL_CALL_INTERP + int opcode = OP; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(OP); + _PyStackRef a; + _PyStackRef res; + // _DO_STUFF + { + a = stack_pointer[-2]; + res = a; + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + """ + self.run_cases_test(input, output) + + def test_multiple_recording_uops_after_specializing(self): + """Multiple recording uops after a specializing uop are legal.""" + input = """ + specializing op(_SPECIALIZE_OP, (counter/1, a, b -- a, b)) { + SPAM(); + } + tier2 op(_RECORD_A, (a, b -- a, b)) { + RECORD_VALUE(a); + } + tier2 op(_RECORD_B, (a, b -- a, b)) { + RECORD_VALUE(b); + } + op(_DO_STUFF, (a, b -- res)) { + res = a; + INPUTS_DEAD(); + } + macro(OP) = _SPECIALIZE_OP + _RECORD_A + _RECORD_B + unused/2 + _DO_STUFF; + """ + output = """ + TARGET(OP) { + #if _Py_TAIL_CALL_INTERP + int opcode = OP; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(OP); + _PyStackRef a; + _PyStackRef res; + // _SPECIALIZE_OP + { + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + SPAM(); + } + /* Skip 2 cache entries */ + // _DO_STUFF + { + a = stack_pointer[-2]; + res = a; + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + """ + self.run_cases_test(input, output) + + def test_recording_uop_between_real_uops_rejected(self): + """A recording uop sandwiched between real uops is rejected.""" + input = """ + tier2 op(_RECORD_A, (a, b -- a, b)) { + RECORD_VALUE(a); + } + op(_FIRST, (a, b -- a, b)) { + first(a); + } + tier2 op(_RECORD_B, (a, b -- a, b)) { + RECORD_VALUE(b); + } + macro(OP) = _RECORD_A + _FIRST + _RECORD_B; + """ + with self.assertRaisesRegex(SyntaxError, + "must precede all " + "non-recording, non-specializing uops"): + self.run_cases_test(input, "") + + +class TestRecorderTableGeneration(unittest.TestCase): + + def setUp(self) -> None: + super().setUp() + self.maxDiff = None + self.temp_dir = tempfile.gettempdir() + self.temp_input_filename = os.path.join(self.temp_dir, "input.txt") + + def tearDown(self) -> None: + try: + os.remove(self.temp_input_filename) + except FileNotFoundError: + pass + super().tearDown() + + def analyze_input(self, input: str): + with open(self.temp_input_filename, "w+") as f: + f.write(parser.BEGIN_MARKER) + f.write(input) + f.write(parser.END_MARKER) + with handle_stderr(): + return analyze_files([self.temp_input_filename]) + + def generate_tables(self, input: str) -> str: + import io + analysis = self.analyze_input(input) + buf = io.StringIO() + out = CWriter(buf, 0, False) + record_function_generator.generate_recorder_tables(analysis, out) + return buf.getvalue() + + def get_slot_map_section(self, output: str) -> str: + return output.split( + "const _PyOpcodeRecordSlotMap _PyOpcode_RecordSlotMaps[256] = {\n", + 1, + )[1].split("};\n\n", 1)[0] + + def assert_slot_map_lines(self, output: str, *lines: str) -> None: + slot_map_section = self.get_slot_map_section(output) + for line in lines: + self.assertIn(line, slot_map_section) + + def test_single_recording_uop_generates_count(self): + input = """ + tier2 op(_RECORD_TOS, (value -- value)) { + RECORD_VALUE(value); + } + op(_DO_STUFF, (value -- res)) { + res = value; + } + macro(OP) = _RECORD_TOS + _DO_STUFF; + """ + output = self.generate_tables(input) + self.assertIn("_RECORD_TOS_INDEX", output) + self.assertIn("[OP] = {1, {_RECORD_TOS_INDEX}}", output) + + def test_three_recording_uops_generate_count_3_in_order(self): + input = """ + tier2 op(_RECORD_X, (a, b, c -- a, b, c)) { + RECORD_VALUE(a); + } + tier2 op(_RECORD_Y, (a, b, c -- a, b, c)) { + RECORD_VALUE(b); + } + tier2 op(_RECORD_Z, (a, b, c -- a, b, c)) { + RECORD_VALUE(c); + } + op(_DO_STUFF, (a, b, c -- res)) { + res = a; + } + macro(OP) = _RECORD_X + _RECORD_Y + _RECORD_Z + _DO_STUFF; + """ + output = self.generate_tables(input) + self.assertIn( + "[OP] = {3, {_RECORD_X_INDEX, _RECORD_Y_INDEX, _RECORD_Z_INDEX}}", + output, + ) + + def test_four_recording_uops_rejected(self): + input = """ + tier2 op(_RECORD_A, (a, b, c, d -- a, b, c, d)) { + RECORD_VALUE(a); + } + tier2 op(_RECORD_B, (a, b, c, d -- a, b, c, d)) { + RECORD_VALUE(b); + } + tier2 op(_RECORD_C, (a, b, c, d -- a, b, c, d)) { + RECORD_VALUE(c); + } + tier2 op(_RECORD_D, (a, b, c, d -- a, b, c, d)) { + RECORD_VALUE(d); + } + op(_DO_STUFF, (a, b, c, d -- res)) { + res = a; + } + macro(OP) = _RECORD_A + _RECORD_B + _RECORD_C + _RECORD_D + _DO_STUFF; + """ + with self.assertRaisesRegex(ValueError, "exceeds MAX_RECORDED_VALUES"): + self.generate_tables(input) + + def test_family_member_needs_transform_only_when_shape_changes(self): + input = """ + tier2 op(_RECORD_TOS, (value -- value)) { + RECORD_VALUE(value); + } + tier2 op(_RECORD_TOS_TYPE, (value -- value)) { + RECORD_VALUE(Py_TYPE(value)); + } + op(_DO_STUFF, (value -- res)) { + res = value; + } + macro(OP_RAW) = _RECORD_TOS + _DO_STUFF; + macro(OP_RAW_SPECIALIZED) = _RECORD_TOS_TYPE + _DO_STUFF; + family(OP_RAW, INLINE_CACHE_ENTRIES_OP_RAW) = { OP_RAW_SPECIALIZED }; + + macro(OP_TYPED) = _RECORD_TOS_TYPE + _DO_STUFF; + macro(OP_TYPED_SPECIALIZED) = _RECORD_TOS_TYPE + _DO_STUFF; + family(OP_TYPED, INLINE_CACHE_ENTRIES_OP_TYPED) = { OP_TYPED_SPECIALIZED }; + """ + output = self.generate_tables(input) + self.assert_slot_map_lines( + output, + "[OP_RAW] = {1, 0, {0}}", + "[OP_RAW_SPECIALIZED] = {1, 1, {0}}", + "[OP_TYPED] = {1, 0, {0}}", + "[OP_TYPED_SPECIALIZED] = {1, 0, {0}}", + ) + + def test_record_transform_generated_from_recording_uop(self): + input = """ + tier2 op(_RECORD_TOS, (tos -- tos)) { + RECORD_VALUE(PyStackRef_AsPyObjectBorrow(tos)); + } + tier2 op(_RECORD_TOS_TYPE, (tos -- tos)) { + RECORD_VALUE(Py_TYPE(PyStackRef_AsPyObjectBorrow(tos))); + } + op(_DO_STUFF, (tos -- res)) { + res = tos; + } + macro(OP) = _RECORD_TOS + _DO_STUFF; + macro(OP_SPECIALIZED) = _RECORD_TOS_TYPE + _DO_STUFF; + family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED }; + """ + output = self.generate_tables(input) + self.assertIn("_PyOpcode_RecordTransform_TOS_TYPE", output) + self.assertIn("tos = PyStackRef_FromPyObjectBorrow(recorded_value);", output) + self.assertIn( + "transformed_value = (PyObject *)Py_TYPE(PyStackRef_AsPyObjectBorrow(tos));", + output, + ) + self.assertIn("return _PyOpcode_RecordTransform_TOS_TYPE(value);", output) + self.assertNotIn("record_trace_transform_to_type", output) + + def test_record_transform_generated_when_only_specialization_records(self): + input = """ + tier2 op(_RECORD_TOS_TYPE, (tos -- tos)) { + RECORD_VALUE(Py_TYPE(PyStackRef_AsPyObjectBorrow(tos))); + } + op(_DO_STUFF, (tos -- res)) { + res = tos; + } + macro(OP) = _DO_STUFF; + macro(OP_SPECIALIZED) = _RECORD_TOS_TYPE + _DO_STUFF; + family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED }; + """ + output = self.generate_tables(input) + # Family head must adopt the specialization's recorder. + self.assertIn("[OP] = {1, {_RECORD_TOS_TYPE_INDEX}}", output) + self.assertIn("[OP_SPECIALIZED] = {1, {_RECORD_TOS_TYPE_INDEX}}", output) + # Specialization consumes the slot directly (mask 0), no transform. + self.assert_slot_map_lines(output, "[OP_SPECIALIZED] = {1, 0, {0}}") + self.assertNotIn("_PyOpcode_RecordTransform_TOS_TYPE(", output) + + def test_no_record_transform_when_only_base_records(self): + input = """ + tier2 op(_RECORD_TOS, (tos -- tos)) { + RECORD_VALUE(PyStackRef_AsPyObjectBorrow(tos)); + } + op(_DO_STUFF, (tos -- res)) { + res = tos; + } + macro(OP) = _RECORD_TOS + _DO_STUFF; + macro(OP_SPECIALIZED) = _DO_STUFF; + family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED }; + """ + output = self.generate_tables(input) + # Family head records via _RECORD_TOS. + self.assertIn("[OP] = {1, {_RECORD_TOS_INDEX}}", output) + self.assertIn("[OP_SPECIALIZED] = {1, {_RECORD_TOS_INDEX}}", output) + # Specialization has no consumer slot map entry (it doesn't read it). + self.assertNotIn( + "[OP_SPECIALIZED] = {1,", self.get_slot_map_section(output) + ) + # No transform helpers are generated. + self.assertNotIn("_PyOpcode_RecordTransform_TOS(", output) + self.assertNotIn("_PyOpcode_RecordTransform_TOS_TYPE", output) + + def test_family_member_maps_positional_recorders_to_family_slots(self): + input = """ + tier2 op(_RECORD_TOS, (sub -- sub)) { + RECORD_VALUE(sub); + } + tier2 op(_RECORD_NOS, (container, sub -- container, sub)) { + RECORD_VALUE(container); + } + op(_DO_STUFF, (container, sub -- res)) { + res = container; + } + macro(OP) = _RECORD_TOS + _RECORD_NOS + _DO_STUFF; + macro(OP_SPECIALIZED) = _RECORD_NOS + _DO_STUFF; + family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED }; + """ + output = self.generate_tables(input) + self.assert_slot_map_lines( + output, + "[OP] = {2, 0, {1, 0}}", + "[OP_SPECIALIZED] = {1, 0, {0}}", + ) + + def test_family_member_maps_non_positional_recorders_by_stack_shape(self): + input = """ + tier2 op(_RECORD_CALLABLE, (callable, self, args[oparg] -- callable, self, args[oparg])) { + RECORD_VALUE(callable); + } + tier2 op(_RECORD_BOUND_METHOD, (callable, self, args[oparg] -- callable, self, args[oparg])) { + RECORD_VALUE(callable); + } + op(_DO_STUFF, (callable, self, args[oparg] -- res)) { + res = callable; + } + macro(OP) = _RECORD_CALLABLE + _DO_STUFF; + macro(OP_SPECIALIZED) = _RECORD_BOUND_METHOD + _DO_STUFF; + family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED }; + """ + output = self.generate_tables(input) + self.assert_slot_map_lines( + output, + "[OP] = {1, 0, {0}}", + "[OP_SPECIALIZED] = {1, 1, {0}}", + ) + + def test_family_head_records_union_of_member_recorders(self): + input = """ + tier2 op(_RECORD_TOS, (value -- value)) { + RECORD_VALUE(value); + } + op(_DO_STUFF, (value -- res)) { + res = value; + } + macro(OP) = _DO_STUFF; + macro(OP_SPECIALIZED) = _RECORD_TOS + _DO_STUFF; + family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED }; + """ + analysis = self.analyze_input(input) + output = self.generate_tables(input) + self.assertEqual( + analysis.families["OP"].get_member_record_names(), + ("_RECORD_TOS",), + ) + self.assertIn("[OP] = {1, {_RECORD_TOS_INDEX}}", output) + self.assertIn("[OP_SPECIALIZED] = {1, {_RECORD_TOS_INDEX}}", output) + self.assert_slot_map_lines(output, "[OP_SPECIALIZED] = {1, 0, {0}}") + + def test_family_detects_base_and_specialized_recording_difference(self): + input = """ + tier2 op(_RECORD_TOS, (value -- value)) { + RECORD_VALUE(value); + } + tier2 op(_RECORD_TOS_TYPE, (value -- value)) { + RECORD_VALUE(Py_TYPE(value)); + } + op(_DO_STUFF, (value -- res)) { + res = value; + } + macro(OP) = _RECORD_TOS + _DO_STUFF; + macro(OP_SPECIALIZED) = _RECORD_TOS_TYPE + _DO_STUFF; + family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED }; + """ + analysis = self.analyze_input(input) + output = self.generate_tables(input) + self.assertEqual( + record_function_generator.get_instruction_record_names( + analysis.instructions["OP"] + ), + ["_RECORD_TOS"], + ) + self.assertEqual( + record_function_generator.get_instruction_record_names( + analysis.instructions["OP_SPECIALIZED"] + ), + ["_RECORD_TOS_TYPE"], + ) + self.assertIn("[OP] = {1, {_RECORD_TOS_INDEX}}", output) + self.assertIn("[OP_SPECIALIZED] = {1, {_RECORD_TOS_INDEX}}", output) + self.assert_slot_map_lines( + output, + "[OP] = {1, 0, {0}}", + "[OP_SPECIALIZED] = {1, 1, {0}}", + ) + + def test_family_head_falls_back_for_missing_member_slots(self): + input = """ + tier2 op(_RECORD_TOS, (value -- value)) { + RECORD_VALUE(value); + } + op(_DO_STUFF, (value -- res)) { + res = value; + } + macro(OP) = _RECORD_TOS + _DO_STUFF; + macro(OP_SPECIALIZED) = _DO_STUFF; + family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED }; + """ + output = self.generate_tables(input) + self.assertIn("[OP] = {1, {_RECORD_TOS_INDEX}}", output) + self.assertIn("[OP_SPECIALIZED] = {1, {_RECORD_TOS_INDEX}}", output) + + def test_family_mixed_slots_only_transform_changed_recorders(self): + input = """ + tier2 op(_RECORD_TOS_TYPE, (left, right -- left, right)) { + RECORD_VALUE(Py_TYPE(right)); + } + tier2 op(_RECORD_NOS_TYPE, (left, right -- left, right)) { + RECORD_VALUE(Py_TYPE(left)); + } + tier2 op(_RECORD_NOS, (left, right -- left, right)) { + RECORD_VALUE(left); + } + op(_DO_STUFF, (left, right -- res)) { + res = left; + } + macro(OP) = _RECORD_TOS_TYPE + _RECORD_NOS_TYPE + _DO_STUFF; + macro(OP_SPECIALIZED) = _RECORD_NOS + _DO_STUFF; + family(OP, INLINE_CACHE_ENTRIES_OP) = { OP_SPECIALIZED }; + """ + output = self.generate_tables(input) + self.assertIn("[OP] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}", output) + self.assert_slot_map_lines( + output, + "[OP] = {2, 2, {1, 0}}", + "[OP_SPECIALIZED] = {1, 0, {0}}", + ) class TestGeneratedAbstractCases(unittest.TestCase): def setUp(self) -> None: @@ -2115,8 +2625,9 @@ def test_validate_uop_unused_input(self): """ output = """ case OP: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } """ @@ -2132,8 +2643,9 @@ def test_validate_uop_unused_input(self): """ output = """ case OP: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } """ @@ -2153,9 +2665,10 @@ def test_validate_uop_unused_output(self): case OP: { JitOptRef foo; foo = NULL; + CHECK_STACK_BOUNDS(1); stack_pointer[0] = foo; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } """ @@ -2172,8 +2685,9 @@ def test_validate_uop_unused_output(self): """ output = """ case OP: { + CHECK_STACK_BOUNDS(1); stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } """ @@ -2234,7 +2748,7 @@ def test_pure_uop_body_copied_in(self): """ input2 = """ op(OP, (foo -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(foo); + REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res); res = sym_new_known(ctx, foo); } """ @@ -2274,7 +2788,7 @@ def test_pure_uop_body_copied_in_deopt(self): """ input2 = """ op(OP, (foo -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(foo); + REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res); res = foo; } """ @@ -2318,7 +2832,7 @@ def test_pure_uop_body_copied_in_error_if(self): """ input2 = """ op(OP, (foo -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(foo); + REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res); res = foo; } """ @@ -2364,7 +2878,7 @@ def test_replace_opcode_uop_body_copied_in_complex(self): """ input2 = """ op(OP, (foo -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(foo); + REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res); res = sym_new_known(ctx, foo); } """ @@ -2411,7 +2925,7 @@ def test_replace_opcode_escaping_uop_body_copied_in_complex(self): """ input2 = """ op(OP, (foo -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(foo); + REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res); res = sym_new_known(ctx, foo); } """ @@ -2445,6 +2959,172 @@ def test_replace_opcode_escaping_uop_body_copied_in_complex(self): """ self.run_cases_test(input, input2, output) + def test_replace_opcode_binop_one_output(self): + input = """ + pure op(OP, (left, right -- res)) { + res = foo(left, right); + } + """ + input2 = """ + op(OP, (left, right -- res)) { + res = sym_new_non_null(ctx, foo); + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res); + } + """ + output = """ + case OP: { + JitOptRef right; + JitOptRef left; + JitOptRef res; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + res = sym_new_non_null(ctx, foo); + if ( + sym_is_safe_const(ctx, left) && + sym_is_safe_const(ctx, right) + ) { + JitOptRef left_sym = left; + JitOptRef right_sym = right; + _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); + _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); + _PyStackRef res_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + res_stackref = foo(left, right); + /* End of uop copied from bytecodes for constant evaluation */ + res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + CHECK_STACK_BOUNDS(-1); + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + CHECK_STACK_BOUNDS(-1); + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + """ + self.run_cases_test(input, input2, output) + + def test_replace_opcode_binop_one_output_insert(self): + input = """ + pure op(OP, (left, right -- res, l, r)) { + res = foo(left, right); + l = left; + r = right; + } + """ + input2 = """ + op(OP, (left, right -- res, l, r)) { + res = sym_new_non_null(ctx, foo); + l = left; + r = right; + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res); + } + """ + output = """ + case OP: { + JitOptRef right; + JitOptRef left; + JitOptRef res; + JitOptRef l; + JitOptRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + res = sym_new_non_null(ctx, foo); + l = left; + r = right; + if ( + sym_is_safe_const(ctx, left) && + sym_is_safe_const(ctx, right) + ) { + JitOptRef left_sym = left; + JitOptRef right_sym = right; + _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); + _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); + _PyStackRef res_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + res_stackref = foo(left, right); + l_stackref = left; + r_stackref = right; + /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; + res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + """ + self.run_cases_test(input, input2, output) + + def test_replace_opcode_unaryop_one_output_insert(self): + input = """ + pure op(OP, (left -- res, l)) { + res = foo(left); + l = left; + } + """ + input2 = """ + op(OP, (left -- res, l)) { + res = sym_new_non_null(ctx, foo); + l = left; + REPLACE_OPCODE_IF_EVALUATES_PURE(left, res); + } + """ + output = """ + case OP: { + JitOptRef left; + JitOptRef res; + JitOptRef l; + left = stack_pointer[-1]; + res = sym_new_non_null(ctx, foo); + l = left; + if ( + sym_is_safe_const(ctx, left) + ) { + JitOptRef left_sym = left; + _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); + _PyStackRef res_stackref; + _PyStackRef l_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + res_stackref = foo(left); + l_stackref = left; + /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + CHECK_STACK_BOUNDS(1); + stack_pointer[-1] = res; + stack_pointer[0] = l; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-1] = res; + stack_pointer[0] = l; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + """ + self.run_cases_test(input, input2, output) + def test_replace_opocode_uop_reject_array_effects(self): input = """ pure op(OP, (foo[2] -- res)) { @@ -2458,7 +3138,7 @@ def test_replace_opocode_uop_reject_array_effects(self): """ input2 = """ op(OP, (foo[2] -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(foo); + REPLACE_OPCODE_IF_EVALUATES_PURE(foo, res); res = sym_new_unknown(ctx); } """ @@ -2468,5 +3148,30 @@ def test_replace_opocode_uop_reject_array_effects(self): "Pure evaluation cannot take array-like inputs"): self.run_cases_test(input, input2, output) + def test_overridden_abstract_with_multiple_caches(self): + input = """ + op(OP, (version/1, unused/1, index/1, value -- res)) { + res = SPAM(version, index, value); + } + """ + input2 = """ + op(OP, (value -- res)) { + res = eggs(version, index, value); + } + """ + output = """ + case OP: { + JitOptRef value; + JitOptRef res; + value = stack_pointer[-1]; + uint16_t version = (uint16_t)this_instr->operand0; + uint16_t index = (uint16_t)this_instr->operand1; + res = eggs(version, index, value); + stack_pointer[-1] = res; + break; + } + """ + self.run_cases_test(input, input2, output) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 3e41c7b9663491..6eb25960101a9e 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -134,6 +134,18 @@ def gen(): self.assertEqual(len(resurrected), 1) self.assertIsInstance(resurrected[0].gi_code, types.CodeType) + def test_exhausted_generator_frame_cycle(self): + def g(): + yield + + generator = g() + frame = generator.gi_frame + self.assertIsNone(frame.f_back) + next(generator) + self.assertIsNone(frame.f_back) + next(generator, None) + self.assertIsNone(frame.f_back) + class GeneratorTest(unittest.TestCase): @@ -290,6 +302,33 @@ def __iter__(self): self.assertEqual([1,2], list(i for i in C())) + def test_close_clears_frame(self): + # gh-142766: Test that closing a generator clears its frame + class DetectDelete: + def __init__(self): + DetectDelete.deleted = False + + def __del__(self): + DetectDelete.deleted = True + + def generator(arg): + yield + + # Test a freshly created generator (not suspended) + g = generator(DetectDelete()) + g.close() + self.assertTrue(DetectDelete.deleted) + + # Test a suspended generator + g = generator(DetectDelete()) + next(g) + g.close() + self.assertTrue(DetectDelete.deleted) + + # Clear via gi_frame.clear() + g = generator(DetectDelete()) + g.gi_frame.clear() + self.assertTrue(DetectDelete.deleted) class ModifyUnderlyingIterableTest(unittest.TestCase): iterables = [ @@ -318,21 +357,25 @@ def gen(it): yield x return gen(range(10)) - def process_tests(self, get_generator): - for obj in self.iterables: - g_obj = get_generator(obj) - with self.subTest(g_obj=g_obj, obj=obj): - self.assertListEqual(list(g_obj), list(obj)) + def process_tests(self, get_generator, changes_iterable): + if changes_iterable: + for obj in self.iterables: + g_obj = get_generator(obj) + with self.subTest(g_obj=g_obj, obj=obj): + self.assertListEqual(list(g_obj), list(obj)) - g_iter = get_generator(iter(obj)) - with self.subTest(g_iter=g_iter, obj=obj): - self.assertListEqual(list(g_iter), list(obj)) + g_iter = get_generator(iter(obj)) + with self.subTest(g_iter=g_iter, obj=obj): + self.assertListEqual(list(g_iter), list(obj)) err_regex = "'.*' object is not iterable" for obj in self.non_iterables: g_obj = get_generator(obj) with self.subTest(g_obj=g_obj): - self.assertRaisesRegex(TypeError, err_regex, list, g_obj) + if changes_iterable: + self.assertRaisesRegex(TypeError, err_regex, list, g_obj) + else: + next(g_obj) def test_modify_f_locals(self): def modify_f_locals(g, local, obj): @@ -345,22 +388,22 @@ def get_generator_genexpr(obj): def get_generator_genfunc(obj): return modify_f_locals(self.genfunc(), 'it', obj) - self.process_tests(get_generator_genexpr) - self.process_tests(get_generator_genfunc) + self.process_tests(get_generator_genexpr, False) + self.process_tests(get_generator_genfunc, True) def test_new_gen_from_gi_code(self): def new_gen_from_gi_code(g, obj): generator_func = types.FunctionType(g.gi_code, {}) return generator_func(obj) - def get_generator_genexpr(obj): - return new_gen_from_gi_code(self.genexpr(), obj) + for obj in self.non_iterables: + with self.assertRaises(TypeError): + new_gen_from_gi_code(self.genexpr(), obj) def get_generator_genfunc(obj): return new_gen_from_gi_code(self.genfunc(), obj) - self.process_tests(get_generator_genexpr) - self.process_tests(get_generator_genfunc) + self.process_tests(get_generator_genfunc, True) class ExceptionTest(unittest.TestCase): @@ -1323,7 +1366,7 @@ def b(): >>> type(i) >>> [s for s in dir(i) if not s.startswith('_')] -['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw'] +['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_state', 'gi_suspended', 'gi_yieldfrom', 'send', 'throw'] >>> from test.support import HAVE_DOCSTRINGS >>> print(i.__next__.__doc__ if HAVE_DOCSTRINGS else 'Implement next(self).') Implement next(self). diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index 7601cb00ff6685..7816775620bc01 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -17,7 +17,7 @@ from functools import partial, partialmethod, cached_property from graphlib import TopologicalSorter from logging import LoggerAdapter, StreamHandler -from mailbox import Mailbox, _PartialFile +from mailbox import Mailbox try: import ctypes except ImportError: @@ -55,15 +55,14 @@ from unittest.case import _AssertRaisesContext from queue import Queue, SimpleQueue from weakref import WeakSet, ReferenceType, ref -import typing -from typing import Unpack try: from tkinter import Event except ImportError: Event = None from string.templatelib import Template, Interpolation -from typing import TypeVar +import typing +from typing import TypeVar, Unpack T = TypeVar('T') K = TypeVar('K') V = TypeVar('V') @@ -101,7 +100,9 @@ class BaseTest(unittest.TestCase): """Test basics.""" - generic_types = [type, tuple, list, dict, set, frozenset, enumerate, memoryview, + generic_types = [type, tuple, list, dict, frozendict, + set, frozenset, enumerate, memoryview, + slice, defaultdict, deque, SequenceMatcher, dircmp, @@ -117,7 +118,7 @@ class BaseTest(unittest.TestCase): Iterable, Iterator, Reversible, Container, Collection, - Mailbox, _PartialFile, + Mailbox, ContextVar, Token, Field, Set, MutableSet, @@ -244,6 +245,56 @@ class MyGeneric: self.assertEndsWith(repr(MyGeneric[[]]), 'MyGeneric[[]]') self.assertEndsWith(repr(MyGeneric[[int, str]]), 'MyGeneric[[int, str]]') + def test_evil_repr1(self): + # gh-143635 + class Zap: + def __init__(self, container): + self.container = container + def __getattr__(self, name): + if name == "__origin__": + self.container.clear() + return None + if name == "__args__": + return () + raise AttributeError + + params = [] + params.append(Zap(params)) + alias = GenericAlias(list, (params,)) + repr_str = repr(alias) + self.assertTrue(repr_str.startswith("list[["), repr_str) + + def test_evil_repr2(self): + class Zap: + def __init__(self, container): + self.container = container + def __getattr__(self, name): + if name == "__qualname__": + self.container.clear() + return "abcd" + if name == "__module__": + return None + raise AttributeError + + params = [] + params.append(Zap(params)) + alias = GenericAlias(list, (params,)) + repr_str = repr(alias) + self.assertTrue(repr_str.startswith("list[["), repr_str) + + def test_evil_repr3(self): + # gh-143823 + lst = [] + class X: + def __repr__(self): + lst.clear() + return "x" + + lst += [X(), 1] + ga = GenericAlias(int, lst) + with self.assertRaises(IndexError): + repr(ga) + def test_exposed_type(self): import types a = types.GenericAlias(list, int) @@ -402,7 +453,10 @@ def __deepcopy__(self, memo): aliases = [ GenericAlias(list, T), GenericAlias(deque, T), - GenericAlias(X, T) + GenericAlias(X, T), + X[T], + list[T], + deque[T], ] + _UNPACKED_TUPLES for alias in aliases: with self.subTest(alias=alias): @@ -432,10 +486,26 @@ def test_union_generic(self): self.assertEqual(a.__parameters__, (T,)) def test_dir(self): - dir_of_gen_alias = set(dir(list[int])) + ga = list[int] + dir_of_gen_alias = set(dir(ga)) self.assertTrue(dir_of_gen_alias.issuperset(dir(list))) - for generic_alias_property in ("__origin__", "__args__", "__parameters__"): - self.assertIn(generic_alias_property, dir_of_gen_alias) + for generic_alias_property in ( + "__origin__", "__args__", "__parameters__", + "__unpacked__", + ): + with self.subTest(generic_alias_property=generic_alias_property): + self.assertIn(generic_alias_property, dir_of_gen_alias) + for blocked in ( + "__bases__", + "__copy__", + "__deepcopy__", + ): + with self.subTest(blocked=blocked): + self.assertNotIn(blocked, dir_of_gen_alias) + + for entry in dir_of_gen_alias: + with self.subTest(entry=entry): + getattr(ga, entry) # must not raise `AttributeError` def test_weakref(self): for t in self.generic_types: @@ -550,6 +620,14 @@ def test_nested_paramspec_specialization(self): self.assertEqual(deeply_nested_specialized.__args__, ([str, [float], int], float)) self.assertEqual(deeply_nested_specialized.__parameters__, ()) + def test_gh150146(self): + # It used to crash: + for container in [memoryview, list, tuple]: + with self.subTest(container=container): + x = container[TypeVar("")] + with self.assertRaises(TypeError): + x[*typing.Mapping[..., ...]] + class TypeIterationTests(unittest.TestCase): _UNITERABLE_TYPES = (list, tuple) diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index 16c3268fefb034..10d3f409d883c5 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -2,14 +2,16 @@ Tests common to genericpath, ntpath and posixpath """ +import copy import genericpath import os +import pickle import sys import unittest import warnings -from test.support import ( - is_apple, os_helper, warnings_helper -) +from test import support +from test.support import os_helper +from test.support import warnings_helper from test.support.script_helper import assert_python_ok from test.support.os_helper import FakePath @@ -32,6 +34,10 @@ def test_no_argument(self): .format(self.pathmodule.__name__, attr)) def test_commonprefix(self): + with warnings_helper.check_warnings((".*commonpath().*", DeprecationWarning)): + self.do_test_commonprefix() + + def do_test_commonprefix(self): commonprefix = self.pathmodule.commonprefix self.assertEqual( commonprefix([]), @@ -320,6 +326,21 @@ def test_sameopenfile(self): fd2 = fp2.fileno() self.assertTrue(self.pathmodule.sameopenfile(fd1, fd2)) + def test_realpath_mode_values(self): + for name in 'ALL_BUT_LAST', 'ALLOW_MISSING': + with self.subTest(name): + mode = getattr(self.pathmodule, name) + self.assertEqual(repr(mode), 'os.path.' + name) + self.assertEqual(str(mode), 'os.path.' + name) + self.assertTrue(mode) + self.assertIs(copy.copy(mode), mode) + self.assertIs(copy.deepcopy(mode), mode) + for proto in range(pickle.HIGHEST_PROTOCOL+1): + with self.subTest(protocol=proto): + pickled = pickle.dumps(mode, proto) + unpickled = pickle.loads(pickled) + self.assertIs(unpickled, mode) + class TestGenericTest(GenericTest, unittest.TestCase): # Issue 16852: GenericTest can't inherit from unittest.TestCase @@ -445,6 +466,19 @@ def check(value, expected): os.fsencode('$bar%s bar' % nonascii)) check(b'$spam}bar', os.fsencode('%s}bar' % nonascii)) + @support.requires_resource('cpu') + def test_expandvars_large(self): + expandvars = self.pathmodule.expandvars + with os_helper.EnvironmentVarGuard() as env: + env.clear() + env["A"] = "B" + n = 100_000 + self.assertEqual(expandvars('$A'*n), 'B'*n) + self.assertEqual(expandvars('${A}'*n), 'B'*n) + self.assertEqual(expandvars('$A!'*n), 'B!'*n) + self.assertEqual(expandvars('${A}A'*n), 'BA'*n) + self.assertEqual(expandvars('${'*10*n), '${'*10*n) + def test_abspath(self): self.assertIn("foo", self.pathmodule.abspath("foo")) with warnings.catch_warnings(): @@ -502,7 +536,7 @@ def test_nonascii_abspath(self): # directory (when the bytes name is used). and sys.platform not in { "win32", "emscripten", "wasi" - } and not is_apple + } and not support.is_apple ): name = os_helper.TESTFN_UNDECODABLE elif os_helper.TESTFN_NONASCII: @@ -576,8 +610,9 @@ def test_path_isdir(self): self.assertPathEqual(os.path.isdir) def test_path_commonprefix(self): - self.assertEqual(os.path.commonprefix([self.file_path, self.file_name]), - self.file_name) + with warnings_helper.check_warnings((".*commonpath().*", DeprecationWarning)): + self.assertEqual(os.path.commonprefix([self.file_path, self.file_name]), + self.file_name) def test_path_getsize(self): self.assertPathEqual(os.path.getsize) diff --git a/Lib/test/test_getpass.py b/Lib/test/test_getpass.py index ab36535a1cfa8a..272414a6204856 100644 --- a/Lib/test/test_getpass.py +++ b/Lib/test/test_getpass.py @@ -88,6 +88,147 @@ def test_trims_trailing_newline(self): input = StringIO('test\n') self.assertEqual('test', getpass._raw_input(input=input)) + def check_raw_input(self, inputs, expect_result, prompt='Password: '): + mock_input = StringIO(inputs) + mock_output = StringIO() + result = getpass._raw_input(prompt, mock_output, mock_input, '*') + self.assertEqual(result, expect_result) + return mock_output.getvalue() + + def test_null_char(self): + self.check_raw_input('pass\x00word\n', 'password') + + def test_raw_input_with_echo_char(self): + output = self.check_raw_input('my1pa$$word!\n', 'my1pa$$word!') + self.assertEqual('Password: ************', output) + + def test_control_chars_with_echo_char(self): + output = self.check_raw_input('pass\twd\b\n', 'pass\tw') + # After backspace: refresh rewrites prompt + 6 echo chars + self.assertEqual( + 'Password: *******' # initial prompt + 7 echo chars + '\r' + ' ' * 17 + '\r' # clear line (10 prompt + 7 prev) + 'Password: ******', # rewrite prompt + 6 echo chars + output + ) + + def test_kill_ctrl_u_with_echo_char(self): + # Ctrl+U (KILL) should clear the entire line + output = self.check_raw_input('foo\x15bar\n', 'bar') + # Should show "***" then refresh to clear, then show "***" for "bar" + self.assertIn('***', output) + # Display refresh uses \r to rewrite the line including prompt + self.assertIn('\r', output) + + def test_werase_ctrl_w_with_echo_char(self): + # Ctrl+W (WERASE) should delete the previous word + self.check_raw_input('hello world\x17end\n', 'hello end') + + def test_ctrl_w_display_preserves_prompt(self): + # Reproducer from gh-138577: type "hello world", Ctrl+W + # Display must show "Password: ******" not "******rd: ***********" + output = self.check_raw_input('hello world\x17\n', 'hello ') + # The final visible state should be "Password: ******" + # Verify prompt is rewritten during refresh, not overwritten by stars + self.assertEndsWith(output, 'Password: ******') + + def test_ctrl_a_insert_display_preserves_prompt(self): + # Reproducer from gh-138577: type "abc", Ctrl+A, type "x" + # Display must show "Password: ****" not "****word: ***" + output = self.check_raw_input('abc\x01x\n', 'xabc') + # The final visible state should be "Password: ****" + self.assertEndsWith(output, 'Password: ****\x08\x08\x08') + + def test_lnext_ctrl_v_with_echo_char(self): + # Ctrl+V (LNEXT) should insert the next character literally + self.check_raw_input('test\x16\x15more\n', 'test\x15more') + + def test_ctrl_a_move_to_start_with_echo_char(self): + # Ctrl+A should move cursor to start + self.check_raw_input('end\x01start\n', 'startend') + + def test_ctrl_a_cursor_position(self): + # After Ctrl+A, cursor is at position 0. + # Refresh writes backspaces to move cursor from end to start. + output = self.check_raw_input('abc\x01\n', 'abc') + self.assertEndsWith(output, 'Password: ***\x08\x08\x08') + + def test_ctrl_a_on_empty(self): + # Ctrl+A on empty line should be a no-op + self.check_raw_input('\x01hello\n', 'hello') + + def test_ctrl_a_already_at_start(self): + # Double Ctrl+A should be same as single Ctrl+A + self.check_raw_input('abc\x01\x01start\n', 'startabc') + + def test_ctrl_a_then_backspace(self): + # Backspace after Ctrl+A should do nothing (cursor at 0) + self.check_raw_input('abc\x01\x7f\n', 'abc') + + def test_ctrl_e_move_to_end_with_echo_char(self): + # Ctrl+E should move cursor to end + self.check_raw_input('start\x01X\x05end\n', 'Xstartend') + + def test_ctrl_e_cursor_position(self): + # After Ctrl+A then Ctrl+E, cursor is back at end. + # Refresh has no backspaces since cursor is at end. + output = self.check_raw_input('abc\x01\x05\n', 'abc') + self.assertEndsWith(output, 'Password: ***') + + def test_ctrl_e_on_empty(self): + # Ctrl+E on empty line should be a no-op + self.check_raw_input('\x05hello\n', 'hello') + + def test_ctrl_e_already_at_end(self): + # Ctrl+E when already at end should be a no-op + self.check_raw_input('abc\x05more\n', 'abcmore') + + def test_ctrl_a_then_ctrl_e(self): + # Ctrl+A then Ctrl+E should return cursor to end, typing appends + self.check_raw_input('abc\x01\x05def\n', 'abcdef') + + def test_ctrl_k_kill_forward_with_echo_char(self): + # Ctrl+K should kill from cursor to end + self.check_raw_input('delete\x01\x0bkeep\n', 'keep') + + def test_ctrl_c_interrupt_with_echo_char(self): + # Ctrl+C should raise KeyboardInterrupt + with self.assertRaises(KeyboardInterrupt): + self.check_raw_input('test\x03more', '') + + def test_ctrl_d_eof_with_echo_char(self): + # Ctrl+D twice should cause EOF + self.check_raw_input('test\x04\x04', 'test') + + def test_backspace_at_start_with_echo_char(self): + # Backspace at start should do nothing + self.check_raw_input('\x7fhello\n', 'hello') + + def test_ctrl_k_at_end_with_echo_char(self): + # Ctrl+K at end should do nothing + self.check_raw_input('hello\x0b\n', 'hello') + + def test_ctrl_w_on_empty_with_echo_char(self): + # Ctrl+W on empty line should do nothing + self.check_raw_input('\x17hello\n', 'hello') + + def test_ctrl_u_on_empty_with_echo_char(self): + # Ctrl+U on empty line should do nothing + self.check_raw_input('\x15hello\n', 'hello') + + def test_multiple_ctrl_operations_with_echo_char(self): + # Test combination: type, move, insert, delete + # "world", Ctrl+A, "hello ", Ctrl+E, "!", Ctrl+A, Ctrl+K, "start" + self.check_raw_input('world\x01hello \x05!\x01\x0bstart\n', 'start') + + def test_ctrl_w_multiple_words_with_echo_char(self): + # Ctrl+W should delete only the last word + self.check_raw_input('one two three\x17\n', 'one two ') + + def test_ctrl_v_then_ctrl_c_with_echo_char(self): + # Ctrl+V should make Ctrl+C literal (not interrupt) + self.check_raw_input('test\x16\x03end\n', 'test\x03end') + # Some of these tests are a bit white-box. The functional requirement is that # the password input be taken directly from the tty, and that it not be echoed @@ -174,31 +315,44 @@ def test_echo_char_replaces_input_with_asterisks(self): result = getpass.unix_getpass(echo_char='*') mock_input.assert_called_once_with('Password: ', textio(), - input=textio(), echo_char='*') + input=textio(), echo_char='*', + term_ctrl_chars=mock.ANY) self.assertEqual(result, mock_result) - def test_raw_input_with_echo_char(self): - passwd = 'my1pa$$word!' - mock_input = StringIO(f'{passwd}\n') - mock_output = StringIO() - with mock.patch('sys.stdin', mock_input), \ - mock.patch('sys.stdout', mock_output): - result = getpass._raw_input('Password: ', mock_output, mock_input, - '*') - self.assertEqual(result, passwd) - self.assertEqual('Password: ************', mock_output.getvalue()) +class GetpassEchoCharTest(unittest.TestCase): - def test_control_chars_with_echo_char(self): - passwd = 'pass\twd\b' - expect_result = 'pass\tw' - mock_input = StringIO(f'{passwd}\n') - mock_output = StringIO() - with mock.patch('sys.stdin', mock_input), \ - mock.patch('sys.stdout', mock_output): - result = getpass._raw_input('Password: ', mock_output, mock_input, - '*') - self.assertEqual(result, expect_result) - self.assertEqual('Password: *******\x08 \x08', mock_output.getvalue()) + def test_accept_none(self): + getpass._check_echo_char(None) + + @support.subTests('echo_char', ["*", "A", " "]) + def test_accept_single_printable_ascii(self, echo_char): + getpass._check_echo_char(echo_char) + + def test_reject_empty_string(self): + self.assertRaises(ValueError, getpass.getpass, echo_char="") + + @support.subTests('echo_char', ["***", "AA", "aA*!"]) + def test_reject_multi_character_strings(self, echo_char): + self.assertRaises(ValueError, getpass.getpass, echo_char=echo_char) + + @support.subTests('echo_char', [ + '\N{LATIN CAPITAL LETTER AE}', # non-ASCII single character + '\N{HEAVY BLACK HEART}', # non-ASCII multibyte character + ]) + def test_reject_non_ascii(self, echo_char): + self.assertRaises(ValueError, getpass.getpass, echo_char=echo_char) + + @support.subTests('echo_char', [ + ch for ch in map(chr, range(0, 128)) + if not ch.isprintable() + ]) + def test_reject_non_printable_characters(self, echo_char): + self.assertRaises(ValueError, getpass.getpass, echo_char=echo_char) + + # TypeError Rejection + @support.subTests('echo_char', [b"*", 0, 0.0, [], {}]) + def test_reject_non_string(self, echo_char): + self.assertRaises(TypeError, getpass.getpass, echo_char=echo_char) if __name__ == "__main__": diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index d0ed5129253cdf..9e4e82b2542c15 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -4,7 +4,6 @@ import shutil import sys import unittest -import warnings from test.support import is_wasi, Py_DEBUG from test.support.os_helper import (TESTFN, skip_unless_symlink, @@ -393,36 +392,6 @@ def test_glob_many_open_files(self): for it in iters: self.assertEqual(next(it), p) - def test_glob0(self): - with self.assertWarns(DeprecationWarning): - glob.glob0(self.tempdir, 'a') - - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - eq = self.assertSequencesEqual_noorder - eq(glob.glob0(self.tempdir, 'a'), ['a']) - eq(glob.glob0(self.tempdir, '.bb'), ['.bb']) - eq(glob.glob0(self.tempdir, '.b*'), []) - eq(glob.glob0(self.tempdir, 'b'), []) - eq(glob.glob0(self.tempdir, '?'), []) - eq(glob.glob0(self.tempdir, '*a'), []) - eq(glob.glob0(self.tempdir, 'a*'), []) - - def test_glob1(self): - with self.assertWarns(DeprecationWarning): - glob.glob1(self.tempdir, 'a') - - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - eq = self.assertSequencesEqual_noorder - eq(glob.glob1(self.tempdir, 'a'), ['a']) - eq(glob.glob1(self.tempdir, '.bb'), ['.bb']) - eq(glob.glob1(self.tempdir, '.b*'), ['.bb']) - eq(glob.glob1(self.tempdir, 'b'), []) - eq(glob.glob1(self.tempdir, '?'), ['a']) - eq(glob.glob1(self.tempdir, '*a'), ['a', 'aaa']) - eq(glob.glob1(self.tempdir, 'a*'), ['a', 'aaa', 'aab']) - def test_translate_matching(self): match = re.compile(glob.translate('*')).match self.assertIsNotNone(match('foo')) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 7f5d48b9c63ab7..ebcd98a0a37776 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -1,8 +1,6 @@ # Python test set -- part 1, grammar. # This just tests whether the parser accepts them all. -from test.support import check_syntax_error, skip_wasi_stack_overflow -from test.support import import_helper import annotationlib import inspect import unittest @@ -18,6 +16,12 @@ import typing from test.typinganndata import ann_module2 import test +from test.support import ( + check_syntax_error, + import_helper, + skip_emscripten_stack_overflow, + skip_wasi_stack_overflow, +) from test.support.numbers import ( VALID_UNDERSCORE_LITERALS, INVALID_UNDERSCORE_LITERALS, @@ -250,6 +254,7 @@ def test_eof_error(self): self.assertIn("was never closed", str(cm.exception)) @skip_wasi_stack_overflow() + @skip_emscripten_stack_overflow() def test_max_level(self): # Macro defined in Parser/lexer/state.h MAXLEVEL = 200 @@ -1554,6 +1559,8 @@ def check(test): check('[None [i, j]]') check('[True [i, j]]') check('[... [i, j]]') + check('[t"{x}" [i, j]]') + check('[t"x={x}" [i, j]]') msg=r'indices must be integers or slices, not tuple; perhaps you missed a comma\?' check('[(1, 2) [i, j]]') @@ -1564,8 +1571,6 @@ def check(test): check('[f"x={x}" [i, j]]') check('["abc" [i, j]]') check('[b"abc" [i, j]]') - check('[t"{x}" [i, j]]') - check('[t"x={x}" [i, j]]') msg=r'indices must be integers or slices, not tuple;' check('[[1, 2] [3, 4]]') @@ -1586,6 +1591,7 @@ def check(test): check('[[1, 2] [f"{x}"]]') check('[[1, 2] [f"x={x}"]]') check('[[1, 2] ["abc"]]') + msg=r'indices must be integers or slices, not string.templatelib.Template;' check('[[1, 2] [t"{x}"]]') check('[[1, 2] [t"x={x}"]]') msg=r'indices must be integers or slices, not' diff --git a/Lib/test/test_grp.py b/Lib/test/test_grp.py index e52e17b8dc7366..f08b9328a9ff04 100644 --- a/Lib/test/test_grp.py +++ b/Lib/test/test_grp.py @@ -1,5 +1,8 @@ """Test script for the grp module.""" +import random +import string +import sys import unittest from test.support import import_helper @@ -33,7 +36,15 @@ def test_values_extended(self): self.skipTest('huge group file, extended test skipped') for e in entries: - e2 = grp.getgrgid(e.gr_gid) + try: + e2 = grp.getgrgid(e.gr_gid) + except KeyError: + # On Cygwin, some groups returned by getgrall() cannot be + # retrieved by getgrgid() + if sys.platform == 'cygwin': + continue + raise + self.check_value(e2) self.assertEqual(e2.gr_gid, e.gr_gid) name = e.gr_name @@ -50,61 +61,51 @@ def test_values_extended(self): def test_errors(self): self.assertRaises(TypeError, grp.getgrgid) self.assertRaises(TypeError, grp.getgrgid, 3.14) + self.assertRaises(TypeError, grp.getgrgid, 0.0) + self.assertRaises(TypeError, grp.getgrgid, 0, 0) + # should be out of gid_t range + self.assertRaises(OverflowError, grp.getgrgid, 2**128) + self.assertRaises(OverflowError, grp.getgrgid, -2**128) self.assertRaises(TypeError, grp.getgrnam) self.assertRaises(TypeError, grp.getgrnam, 42) - self.assertRaises(TypeError, grp.getgrall, 42) + self.assertRaises(TypeError, grp.getgrnam, b'root') + self.assertRaises(TypeError, grp.getgrnam, 'root', 0) # embedded null character self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'a\x00b') + self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'root\x00') + self.assertRaises(UnicodeEncodeError, grp.getgrnam, 'roo\udc74') + self.assertRaises(KeyError, grp.getgrnam, '') + self.assertRaises(TypeError, grp.getgrall, 42) - # try to get some errors - bynames = {} - bygids = {} - for (n, p, g, mem) in grp.getgrall(): - if not n or n == '+': - continue # skip NIS entries etc. - bynames[n] = g - bygids[g] = n - - allnames = list(bynames.keys()) - namei = 0 - fakename = allnames[namei] - while fakename in bynames: - chars = list(fakename) - for i in range(len(chars)): - if chars[i] == 'z': - chars[i] = 'A' - break - elif chars[i] == 'Z': - continue + # Find a non-existent group name. + # getgrall() will not necessarily report all existing groups + # (typical for LDAP based directories in big organizations). + for _ in range(30): + fakename = ''.join(random.choices(string.ascii_lowercase, k=6)) + try: + grp.getgrnam(fakename) + except KeyError: + break + else: + self.fail('Cannot find non-existent group name') + + # Find a non-existent gid. + maxgid = 2**31 + for _ in range(30): + fakegid = random.randrange(maxgid) + try: + grp.getgrgid(fakegid) + except KeyError: + break + except OverflowError: + if maxgid == 2**31: + maxgid = 2**16-1 + elif maxgid == 2**16-1: + maxgid = 2**15 else: - chars[i] = chr(ord(chars[i]) + 1) - break - else: - namei = namei + 1 - try: - fakename = allnames[namei] - except IndexError: - # should never happen... if so, just forget it - break - fakename = ''.join(chars) - - self.assertRaises(KeyError, grp.getgrnam, fakename) - - # Choose a non-existent gid. - fakegid = 4127 - while fakegid in bygids: - fakegid = (fakegid * 3) % 0x10000 - - self.assertRaises(KeyError, grp.getgrgid, fakegid) - - def test_noninteger_gid(self): - entries = grp.getgrall() - if not entries: - self.skipTest('no groups') - # Choose an existent gid. - gid = entries[0][2] - self.assertRaises(TypeError, grp.getgrgid, float(gid)) - self.assertRaises(TypeError, grp.getgrgid, str(gid)) + raise + else: + self.fail('Cannot find non-existent gid') if __name__ == "__main__": diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py index 9a2e1dd248fe94..8bc8e507683cb5 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -10,8 +10,9 @@ import sys import unittest from subprocess import PIPE, Popen +from unittest import mock from test.support import catch_unraisable_exception -from test.support import import_helper +from test.support import force_not_colorized_test_class, import_helper from test.support import os_helper from test.support import _4G, bigmemtest, requires_subprocess from test.support.script_helper import assert_python_ok, assert_python_failure @@ -350,6 +351,37 @@ def test_mtime(self): self.assertEqual(dataRead, data1) self.assertEqual(fRead.mtime, mtime) + def test_mtime_with_open(self): + mtime = 123456789 + with gzip.open(self.filename, "wb", mtime=mtime) as fWrite: + fWrite.write(data1) + with gzip.open(self.filename, "rb") as fRead: + self.assertTrue(hasattr(fRead, 'mtime')) + self.assertIsNone(fRead.mtime) + dataRead = fRead.read() + self.assertEqual(dataRead, data1) + self.assertEqual(fRead.mtime, mtime) + + def test_mtime_out_of_range(self): + for mtime in (-1, 2**32): + with gzip.GzipFile(self.filename, 'w', mtime=mtime) as fWrite: + fWrite.write(data1) + with gzip.GzipFile(self.filename) as fRead: + fRead.read(1) + self.assertEqual(fRead.mtime, 0) + datac = gzip.compress(data1, mtime=mtime) + with gzip.GzipFile(fileobj=io.BytesIO(datac)) as fRead: + fRead.read(1) + self.assertEqual(fRead.mtime, 0) + + for mtime in (-1, 2**32): + with mock.patch('time.time', return_value=float(mtime)): + with gzip.GzipFile(self.filename, 'w') as fWrite: + fWrite.write(data1) + with gzip.GzipFile(self.filename) as fRead: + fRead.read(1) + self.assertEqual(fRead.mtime, 0) + def test_metadata(self): mtime = 123456789 @@ -639,7 +671,7 @@ def test_fileobj_mode(self): with open(self.filename, mode) as f: with gzip.GzipFile(fileobj=f) as g: self.assertEqual(g.mode, gzip.READ) - for mode in "wb", "ab", "xb": + for mode in "wb", "ab", "xb", "wb+", "ab+", "xb+": if "x" in mode: os_helper.unlink(self.filename) with open(self.filename, mode) as f: @@ -795,6 +827,35 @@ def test_decompress_missing_trailer(self): compressed_data = gzip.compress(data1) self.assertRaises(EOFError, gzip.decompress, compressed_data[:-8]) + def test_truncated_header(self): + truncated_headers = [ + b"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00", # Missing OS byte + b"\x1f\x8b\x08\x02\x00\x00\x00\x00\x00\xff", # FHRC, but no checksum + b"\x1f\x8b\x08\x04\x00\x00\x00\x00\x00\xff", # FEXTRA, but no xlen + b"\x1f\x8b\x08\x04\x00\x00\x00\x00\x00\xff\xaa\x00", # FEXTRA, xlen, but no data + b"\x1f\x8b\x08\x08\x00\x00\x00\x00\x00\xff", # FNAME but no fname + b"\x1f\x8b\x08\x10\x00\x00\x00\x00\x00\xff", # FCOMMENT, but no fcomment + ] + for header in truncated_headers: + with self.subTest(header=header): + with self.assertRaises(EOFError): + gzip.decompress(header) + + def test_corrupted_gzip_header(self): + header = (b"\x1f\x8b\x08\x1f\x00\x00\x00\x00\x00\xff" # All flags set + b"\x05\x00" # Xlen = 5 + b"extra" + b"name\x00" + b"comment\x00") + true_crc = zlib.crc32(header) & 0xFFFF + corrupted_crc = true_crc ^ 0xFFFF + corrupted_header = header + corrupted_crc.to_bytes(2, "little") + with self.assertRaises(gzip.BadGzipFile) as err: + gzip.decompress(corrupted_header) + self.assertEqual(str(err.exception), + f"Corrupted gzip header. Checksums do not " + f"match: {true_crc:04x} != {corrupted_crc:04x}") + def test_read_truncated(self): data = data1*50 # Drop the CRC (4 bytes) and file size (4 bytes). @@ -1057,6 +1118,7 @@ def wrapper(*args, **kwargs): return decorator +@force_not_colorized_test_class class TestCommandLine(unittest.TestCase): data = b'This is a simple test with gzip' diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index b2b64a76a9f0f6..f0e2d527af2615 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -27,32 +27,28 @@ from http.client import HTTPException -default_builtin_hashes = {'md5', 'sha1', 'sha256', 'sha512', 'sha3', 'blake2'} +default_builtin_hashes = {'md5', 'sha1', 'sha2', 'sha3', 'blake2'} # --with-builtin-hashlib-hashes override builtin_hashes = sysconfig.get_config_var("PY_BUILTIN_HASHLIB_HASHES") if builtin_hashes is None: builtin_hashes = default_builtin_hashes else: - builtin_hashes = { - m.strip() for m in builtin_hashes.strip('"').lower().split(",") - } + builtin_hash_names = builtin_hashes.strip('"').lower().split(",") + builtin_hashes = set(map(str.strip, builtin_hash_names)) -# hashlib with and without OpenSSL backend for PBKDF2 -# only import builtin_hashlib when all builtin hashes are available. -# Otherwise import prints noise on stderr +# Public 'hashlib' module with OpenSSL backend for PBKDF2. openssl_hashlib = import_fresh_module('hashlib', fresh=['_hashlib']) -if builtin_hashes == default_builtin_hashes: - builtin_hashlib = import_fresh_module('hashlib', blocked=['_hashlib']) -else: - builtin_hashlib = None try: - from _hashlib import HASH, HASHXOF, openssl_md_meth_names, get_fips_mode + import _hashlib except ImportError: - HASH = None - HASHXOF = None - openssl_md_meth_names = frozenset() - + _hashlib = None +# The extension module may exist but only define some of these. gh-141907 +HASH = getattr(_hashlib, 'HASH', None) +HASHXOF = getattr(_hashlib, 'HASHXOF', None) +openssl_md_meth_names = getattr(_hashlib, 'openssl_md_meth_names', frozenset()) +get_fips_mode = getattr(_hashlib, 'get_fips_mode', None) +if not get_fips_mode: def get_fips_mode(): return 0 @@ -138,8 +134,11 @@ def __init__(self, *args, **kwargs): algorithms.add(algorithm.lower()) _blake2 = self._conditional_import_module('_blake2') + blake2_hashes = {'blake2b', 'blake2s'} if _blake2: - algorithms.update({'blake2b', 'blake2s'}) + algorithms.update(blake2_hashes) + else: + algorithms.difference_update(blake2_hashes) self.constructors_to_test = {} for algorithm in algorithms: @@ -236,7 +235,12 @@ def test_algorithms_available(self): # all available algorithms must be loadable, bpo-47101 self.assertNotIn("undefined", hashlib.algorithms_available) for name in hashlib.algorithms_available: - digest = hashlib.new(name, usedforsecurity=False) + with self.subTest(name): + try: + _ = hashlib.new(name, usedforsecurity=False) + except ValueError as exc: + self.skip_if_blake2_not_builtin(name, exc) + raise def test_usedforsecurity_true(self): hashlib.new("sha256", usedforsecurity=True) @@ -508,6 +512,7 @@ def test_sha3_256_update_over_4gb(self): self.assertEqual(h.hexdigest(), "e2d4535e3b613135c14f2fe4e026d7ad8d569db44901740beffa30d430acb038") @requires_resource('cpu') + @requires_blake2 def test_blake2_update_over_4gb(self): # blake2s or blake2b doesn't matter based on how our C code is structured, this tests the # common loop macro logic. @@ -638,9 +643,14 @@ def check_sha3(self, name, capacity, rate, suffix): constructors = self.constructors_to_test[name] for hash_object_constructor in constructors: m = hash_object_constructor() - if HASH is not None and isinstance(m, HASH): - # _hashopenssl's variant does not have extra SHA3 attributes - continue + if name.startswith('shake_'): + if HASHXOF is not None and isinstance(m, HASHXOF): + # _hashopenssl's variant does not have extra SHA3 attributes + continue + else: + if HASH is not None and isinstance(m, HASH): + # _hashopenssl's variant does not have extra SHA3 attributes + continue self.assertEqual(capacity + rate, 1600) self.assertEqual(m._capacity_bits, capacity) self.assertEqual(m._rate_bits, rate) @@ -797,6 +807,12 @@ def test_case_sha512_3(self): "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb"+ "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b") + def skip_if_blake2_not_builtin(self, name, skip_reason): + # blake2 builtins may be absent if python built with + # a subset of --with-builtin-hashlib-hashes or none. + if "blake2" in name and "blake2" not in builtin_hashes: + self.skipTest(skip_reason) + def check_blake2(self, constructor, salt_size, person_size, key_size, digest_size, max_offset): self.assertEqual(constructor.SALT_SIZE, salt_size) @@ -1079,10 +1095,16 @@ def test_sha256_gil(self): def test_threaded_hashing_fast(self): # Same as test_threaded_hashing_slow() but only tests some functions # since otherwise test_hashlib.py becomes too slow during development. - for name in ['md5', 'sha1', 'sha256', 'sha3_256', 'blake2s']: + algos = ['md5', 'sha1', 'sha256', 'sha3_256', 'blake2s'] + for name in algos: if constructor := getattr(hashlib, name, None): with self.subTest(name): - self.do_test_threaded_hashing(constructor, is_shake=False) + try: + self.do_test_threaded_hashing(constructor, is_shake=False) + except ValueError as exc: + self.skip_if_blake2_not_builtin(name, exc) + raise + if shake_128 := getattr(hashlib, 'shake_128', None): self.do_test_threaded_hashing(shake_128, is_shake=True) @@ -1163,7 +1185,8 @@ def test_disallow_instantiation(self): def test_hash_disallow_instantiation(self): # internal types like _hashlib.HASH are not constructable support.check_disallow_instantiation(self, HASH) - support.check_disallow_instantiation(self, HASHXOF) + if HASHXOF is not None: + support.check_disallow_instantiation(self, HASHXOF) def test_readonly_types(self): for algorithm, constructors in self.constructors_to_test.items(): diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py index 02ded86678343f..1ea182fec4ff18 100644 --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -21,20 +21,21 @@ import hmac import hashlib import random -import test.support.hashlib_helper as hashlib_helper -import types import unittest -import unittest.mock as mock import warnings from _operator import _compare_digest as operator_compare_digest +from test import support +from test.support import _4G, bigmemtest from test.support import check_disallow_instantiation +from test.support import hashlib_helper, import_helper from test.support.hashlib_helper import ( BuiltinHashFunctionsTrait, HashFunctionsTrait, NamedHashFunctionsTrait, OpenSSLHashFunctionsTrait, ) -from test.support.import_helper import import_fresh_module, import_module +from test.support.import_helper import import_fresh_module +from unittest.mock import patch try: import _hashlib @@ -160,7 +161,7 @@ def hmac_digest(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL): return _call_digest_func(self.hmac.digest, key, msg, digestmod) -@hashlib_helper.requires_hashlib() +@hashlib_helper.requires_openssl_hashlib() class ThroughOpenSSLAPIMixin(CreatorMixin, DigestMixin): """Mixin delegating to _hashlib.hmac_new() and _hashlib.hmac_digest().""" @@ -302,7 +303,7 @@ def assert_hmac_new_by_name( def check_hmac_new( self, key, msg, hexdigest, hashname, digest_size, block_size, - hmac_new_func, hmac_new_kwds=types.MappingProxyType({}), + hmac_new_func, hmac_new_kwds=frozendict(), ): """Check that HMAC(key, msg) == digest. @@ -348,7 +349,7 @@ def assert_hmac_hexdigest_by_name( def check_hmac_hexdigest( self, key, msg, hexdigest, digest_size, - hmac_digest_func, hmac_digest_kwds=types.MappingProxyType({}), + hmac_digest_func, hmac_digest_kwds=frozendict(), ): """Check and return a HMAC digest computed by hmac_digest_func(). @@ -727,7 +728,7 @@ def setUpClass(cls): super().setUpClass() for meth in ['_init_openssl_hmac', '_init_builtin_hmac']: fn = getattr(cls.hmac.HMAC, meth) - cm = mock.patch.object(cls.hmac.HMAC, meth, autospec=True, wraps=fn) + cm = patch.object(cls.hmac.HMAC, meth, autospec=True, wraps=fn) cls.enterClassContext(cm) @classmethod @@ -949,7 +950,11 @@ class PyConstructorTestCase(ThroughObjectMixin, PyConstructorBaseMixin, class PyModuleConstructorTestCase(ThroughModuleAPIMixin, PyConstructorBaseMixin, unittest.TestCase): - """Test the hmac.new() and hmac.digest() functions.""" + """Test the hmac.new() and hmac.digest() functions. + + Note that "self.hmac" is imported by blocking "_hashlib" and "_hmac". + For testing functions in "hmac", extend PyMiscellaneousTests instead. + """ def test_hmac_digest_digestmod_parameter(self): func = self.hmac_digest @@ -1020,6 +1025,13 @@ def test_hmac_digest_digestmod_parameter(self): ): self.hmac_digest(b'key', b'msg', value) + @support.subTests("xof_name", ("shake_128", "shake_256")) + def test_hmac_new_xof_digestmod(self, xof_name): + # gh-145200: XOF digests (SHAKE) are not supported by HMAC. + # Verify that the error path does not leak the EVP_MAC_CTX. + with self.assertRaises(_hashlib.UnsupportedDigestmodError): + self.hmac_new(b'key', digestmod=xof_name) + class BuiltinConstructorTestCase(ThroughBuiltinAPIMixin, ExtensionConstructorTestCaseMixin, @@ -1071,6 +1083,15 @@ def test_properties(self): self.assertEqual(h.digest_size, self.digest_size) self.assertEqual(h.block_size, self.block_size) + def test_copy(self): + # Test a generic copy() and the attributes it exposes. + # See https://github.com/python/cpython/issues/142451. + h1 = self.hmac_new(b"my secret key", digestmod=self.digestname) + h2 = h1.copy() + self.assertEqual(h1.name, h2.name) + self.assertEqual(h1.digest_size, h2.digest_size) + self.assertEqual(h1.block_size, h2.block_size) + def test_repr(self): # HMAC object representation may differ across implementations raise NotImplementedError @@ -1426,7 +1447,7 @@ def test_compare_digest_func(self): self.assertIs(self.compare_digest, operator_compare_digest) -@hashlib_helper.requires_hashlib() +@hashlib_helper.requires_openssl_hashlib() class OpenSSLCompareDigestTestCase(CompareDigestMixin, unittest.TestCase): compare_digest = openssl_compare_digest @@ -1445,9 +1466,8 @@ def test_hmac_constructor_uses_builtin(self): hmac = import_fresh_module("hmac", blocked=["_hashlib"]) def watch_method(cls, name): - return mock.patch.object( - cls, name, autospec=True, wraps=getattr(cls, name) - ) + wraps = getattr(cls, name) + return patch.object(cls, name, autospec=True, wraps=wraps) with ( watch_method(hmac.HMAC, '_init_openssl_hmac') as f, @@ -1499,6 +1519,48 @@ def test_with_fallback(self): finally: cache.pop('foo') + @hashlib_helper.requires_openssl_hashdigest("md5") + @bigmemtest(size=_4G + 5, memuse=2, dry_run=False) + def test_hmac_digest_overflow_error_openssl_only(self, size): + hmac = import_fresh_module("hmac", blocked=["_hmac"]) + self.do_test_hmac_digest_overflow_error_switch_to_slow(hmac, size) + + @hashlib_helper.requires_builtin_hashdigest("md5") + @bigmemtest(size=_4G + 5, memuse=2, dry_run=False) + def test_hmac_digest_overflow_error_builtin_only(self, size): + hmac = import_fresh_module("hmac", blocked=["_hashlib"]) + self.do_test_hmac_digest_overflow_error_switch_to_slow(hmac, size) + + def do_test_hmac_digest_overflow_error_switch_to_slow(self, hmac, size): + """Check that hmac.digest() falls back to pure Python. + + The *hmac* argument implements the HMAC module interface. + The *size* argument is a large key size or message size that would + trigger an OverflowError in the C implementation(s) of hmac.digest(). + """ + + bigkey = b'K' * size + bigmsg = b'M' * size + + with patch.object(hmac, "_compute_digest_fallback") as slow: + hmac.digest(bigkey, b'm', "md5") + slow.assert_called_once() + + with patch.object(hmac, "_compute_digest_fallback") as slow: + hmac.digest(b'k', bigmsg, "md5") + slow.assert_called_once() + + @hashlib_helper.requires_hashdigest("md5", openssl=True) + @bigmemtest(size=_4G + 5, memuse=2, dry_run=False) + def test_hmac_digest_no_overflow_error_in_fallback(self, size): + hmac = import_fresh_module("hmac", blocked=["_hashlib", "_hmac"]) + + for key, msg in [(b'K' * size, b'm'), (b'k', b'M' * size)]: + with self.subTest(keysize=len(key), msgsize=len(msg)): + with patch.object(hmac, "_compute_digest_fallback") as slow: + hmac.digest(key, msg, "md5") + slow.assert_called_once() + class BuiltinMiscellaneousTests(BuiltinModuleMixin, unittest.TestCase): """HMAC-BLAKE2 is not standardized as BLAKE2 is a keyed hash function. @@ -1511,7 +1573,7 @@ class BuiltinMiscellaneousTests(BuiltinModuleMixin, unittest.TestCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.blake2 = import_module("_blake2") + cls.blake2 = import_helper.import_module("_blake2") cls.blake2b = cls.blake2.blake2b cls.blake2s = cls.blake2.blake2s diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py index 380bbe40177ec5..e4eff1ea17a670 100644 --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -8,12 +8,27 @@ from test import support +SAMPLE_RCDATA = ( + '' + "" + '' + '' + '' + '\u2603' +) + +SAMPLE_RAWTEXT = SAMPLE_RCDATA + '&☺' + + class EventCollector(html.parser.HTMLParser): - def __init__(self, *args, **kw): + def __init__(self, *args, autocdata=False, **kw): + self.autocdata = autocdata self.events = [] self.append = self.events.append html.parser.HTMLParser.__init__(self, *args, **kw) + if autocdata: + self._set_support_cdata(False) def get_events(self): # Normalize the list of events so that buffer artefacts don't @@ -34,12 +49,16 @@ def get_events(self): def handle_starttag(self, tag, attrs): self.append(("starttag", tag, attrs)) + if self.autocdata and tag == 'svg': + self._set_support_cdata(True) def handle_startendtag(self, tag, attrs): self.append(("startendtag", tag, attrs)) def handle_endtag(self, tag): self.append(("endtag", tag)) + if self.autocdata and tag == 'svg': + self._set_support_cdata(False) # all other markup @@ -90,12 +109,13 @@ def get_events(self): class TestCaseBase(unittest.TestCase): - def get_collector(self): - return EventCollector(convert_charrefs=False) + def get_collector(self, convert_charrefs=False): + return EventCollector(convert_charrefs=convert_charrefs) - def _run_check(self, source, expected_events, collector=None): + def _run_check(self, source, expected_events, + *, collector=None, convert_charrefs=False): if collector is None: - collector = self.get_collector() + collector = self.get_collector(convert_charrefs=convert_charrefs) parser = collector for s in source: parser.feed(s) @@ -109,7 +129,7 @@ def _run_check(self, source, expected_events, collector=None): def _run_check_extra(self, source, events): self._run_check(source, events, - EventCollectorExtra(convert_charrefs=False)) + collector=EventCollectorExtra(convert_charrefs=False)) class HTMLParserTestCase(TestCaseBase): @@ -168,10 +188,87 @@ def test_malformatted_charref(self): ]) def test_unclosed_entityref(self): - self._run_check("&entityref foo", [ - ("entityref", "entityref"), - ("data", " foo"), - ]) + self._run_check('> <', [('entityref', 'gt'), ('data', ' '), ('entityref', 'lt')], + convert_charrefs=False) + self._run_check('> <', [('data', '> <')], convert_charrefs=True) + + self._run_check('&undefined <', + [('entityref', 'undefined'), ('data', ' '), ('entityref', 'lt')], + convert_charrefs=False) + self._run_check('&undefined <', [('data', '&undefined <')], + convert_charrefs=True) + + self._run_check('>undefined <', + [('entityref', 'gtundefined'), ('data', ' '), ('entityref', 'lt')], + convert_charrefs=False) + self._run_check('>undefined <', [('data', '>undefined <')], + convert_charrefs=True) + + self._run_check('& <', [('data', '& '), ('entityref', 'lt')], + convert_charrefs=False) + self._run_check('& <', [('data', '& <')], convert_charrefs=True) + + def test_eof_in_entityref(self): + self._run_check('>', [('entityref', 'gt')], convert_charrefs=False) + self._run_check('>', [('data', '>')], convert_charrefs=True) + + self._run_check('&g', [('entityref', 'g')], convert_charrefs=False) + self._run_check('&g', [('data', '&g')], convert_charrefs=True) + + self._run_check('&undefined', [('entityref', 'undefined')], + convert_charrefs=False) + self._run_check('&undefined', [('data', '&undefined')], + convert_charrefs=True) + + self._run_check('>undefined', [('entityref', 'gtundefined')], + convert_charrefs=False) + self._run_check('>undefined', [('data', '>undefined')], + convert_charrefs=True) + + self._run_check('&', [('data', '&')], convert_charrefs=False) + self._run_check('&', [('data', '&')], convert_charrefs=True) + + def test_unclosed_charref(self): + self._run_check('{ <', [('charref', '123'), ('data', ' '), ('entityref', 'lt')], + convert_charrefs=False) + self._run_check('{ <', [('data', '{ <')], convert_charrefs=True) + self._run_check('« <', [('charref', 'xab'), ('data', ' '), ('entityref', 'lt')], + convert_charrefs=False) + self._run_check('« <', [('data', '\xab <')], convert_charrefs=True) + + self._run_check('� <', + [('charref', '123456789'), ('data', ' '), ('entityref', 'lt')], + convert_charrefs=False) + self._run_check('� <', [('data', '\ufffd <')], + convert_charrefs=True) + self._run_check('� <', + [('charref', 'x123456789'), ('data', ' '), ('entityref', 'lt')], + convert_charrefs=False) + self._run_check('� <', [('data', '\ufffd <')], + convert_charrefs=True) + + self._run_check('&# <', [('data', '&# '), ('entityref', 'lt')], convert_charrefs=False) + self._run_check('&# <', [('data', '&# <')], convert_charrefs=True) + self._run_check('&#x <', [('data', '&#x '), ('entityref', 'lt')], convert_charrefs=False) + self._run_check('&#x <', [('data', '&#x <')], convert_charrefs=True) + + def test_eof_in_charref(self): + self._run_check('{', [('charref', '123')], convert_charrefs=False) + self._run_check('{', [('data', '{')], convert_charrefs=True) + self._run_check('«', [('charref', 'xab')], convert_charrefs=False) + self._run_check('«', [('data', '\xab')], convert_charrefs=True) + + self._run_check('�', [('charref', '123456789')], + convert_charrefs=False) + self._run_check('�', [('data', '\ufffd')], convert_charrefs=True) + self._run_check('�', [('charref', 'x123456789')], + convert_charrefs=False) + self._run_check('�', [('data', '\ufffd')], convert_charrefs=True) + + self._run_check('&#', [('data', '&#')], convert_charrefs=False) + self._run_check('&#', [('data', '&#')], convert_charrefs=True) + self._run_check('&#x', [('data', '&#x')], convert_charrefs=False) + self._run_check('&#x', [('data', '&#x')], convert_charrefs=True) def test_bad_nesting(self): # Strangely, this *is* supposed to test that overlapping @@ -286,30 +383,20 @@ def test_get_starttag_text(self): 'Date().getTime()+\'"><\\/s\'+\'cript>\');\n//]]>'), '\n\n', '', - 'foo = ""', - 'foo = ""', - 'foo = ""', - 'foo = ""', - 'foo = ""', - 'foo = ""', ]) def test_script_content(self, content): s = f'' - self._run_check(s, [("starttag", "script", []), - ("data", content), - ("endtag", "script")]) + self._run_check(s, [ + ("starttag", "script", []), + ("data", content), + ("endtag", "script"), + ]) @support.subTests('content', [ 'a::before { content: ""; }', 'a::before { content: "¬-an-entity-ref;"; }', 'a::before { content: ""; }', 'a::before { content: "\u2603"; }', - 'a::before { content: "< /style>"; }', - 'a::before { content: ""; }', - 'a::before { content: ""; }', - 'a::before { content: ""; }', - 'a::before { content: ""; }', - 'a::before { content: ""; }', ]) def test_style_content(self, content): s = f'' @@ -317,47 +404,59 @@ def test_style_content(self, content): ("data", content), ("endtag", "style")]) - @support.subTests('content', [ - '', - "", - '', - '', - '', - '\u2603', - '< /title>', - '', - '', - '', - '', - '', + @support.subTests('tag', ['title', 'textarea']) + def test_rcdata_content(self, tag): + source = f"<{tag}>{SAMPLE_RCDATA}" + self._run_check(source, [ + ("starttag", tag, []), + ("data", SAMPLE_RCDATA), + ("endtag", tag), ]) - def test_title_content(self, content): - source = f"{content}" + source = f"<{tag}>&" self._run_check(source, [ - ("starttag", "title", []), - ("data", content), - ("endtag", "title"), + ("starttag", tag, []), + ('entityref', 'amp'), + ("endtag", tag), ]) - @support.subTests('content', [ - '', - "", - '', - '', - '', - '\u2603', - '< /textarea>', - '', - '', - '', - '', + @support.subTests('tag', + ['style', 'xmp', 'iframe', 'noembed', 'noframes', 'script']) + def test_rawtext_content(self, tag): + source = f"<{tag}>{SAMPLE_RAWTEXT}" + self._run_check(source, [ + ("starttag", tag, []), + ("data", SAMPLE_RAWTEXT), + ("endtag", tag), + ]) + + def test_noscript_content(self): + source = f"" + # scripting=False -- normal mode + self._run_check(source, [ + ('starttag', 'noscript', []), + ('comment', ' not a comment '), + ('starttag', 'not', [('a', 'start tag')]), + ('unknown decl', 'CDATA[not a cdata'), + ('comment', 'not a bogus comment'), + ('endtag', 'not'), + ('data', '☃'), + ('entityref', 'amp'), + ('charref', '9786'), + ('endtag', 'noscript'), ]) - def test_textarea_content(self, content): - source = f"" + # scripting=True -- RAWTEXT mode self._run_check(source, [ - ("starttag", "textarea", []), + ("starttag", "noscript", []), + ("data", SAMPLE_RAWTEXT), + ("endtag", "noscript"), + ], collector=EventCollector(scripting=True)) + + def test_plaintext_content(self): + content = SAMPLE_RAWTEXT + '' # not closing + source = f"{content}" + self._run_check(source, [ + ("starttag", "plaintext", []), ("data", content), - ("endtag", "textarea"), ]) @support.subTests('endtag', ['script', 'SCRIPT', 'script ', 'script\n', @@ -374,52 +473,65 @@ def test_script_closing_tag(self, endtag): ("endtag", "script")], collector=EventCollectorNoNormalize(convert_charrefs=False)) - @support.subTests('endtag', ['style', 'STYLE', 'style ', 'style\n', - 'style/', 'style foo=bar', 'style foo=">"']) - def test_style_closing_tag(self, endtag): - content = """ - b::before { content: "<!-- not a comment -->"; } - p::before { content: "&not-an-entity-ref;"; } - a::before { content: "<i>"; } - a::after { content: "</i>"; } - """ - s = f'<StyLE>{content}</{endtag}>' - self._run_check(s, [("starttag", "style", []), - ("data", content), - ("endtag", "style")], - collector=EventCollectorNoNormalize(convert_charrefs=False)) - - @support.subTests('endtag', ['title', 'TITLE', 'title ', 'title\n', - 'title/', 'title foo=bar', 'title foo=">"']) - def test_title_closing_tag(self, endtag): - content = "<!-- not a comment --><i>Egg &amp; Spam</i>" - s = f'<TitLe>{content}</{endtag}>' - self._run_check(s, [("starttag", "title", []), - ('data', '<!-- not a comment --><i>Egg & Spam</i>'), - ("endtag", "title")], - collector=EventCollectorNoNormalize(convert_charrefs=True)) - self._run_check(s, [("starttag", "title", []), - ('data', '<!-- not a comment --><i>Egg '), - ('entityref', 'amp'), - ('data', ' Spam</i>'), - ("endtag", "title")], - collector=EventCollectorNoNormalize(convert_charrefs=False)) - - @support.subTests('endtag', ['textarea', 'TEXTAREA', 'textarea ', 'textarea\n', - 'textarea/', 'textarea foo=bar', 'textarea foo=">"']) - def test_textarea_closing_tag(self, endtag): - content = "<!-- not a comment --><i>Egg &amp; Spam</i>" - s = f'<TexTarEa>{content}</{endtag}>' - self._run_check(s, [("starttag", "textarea", []), - ('data', '<!-- not a comment --><i>Egg & Spam</i>'), - ("endtag", "textarea")], - collector=EventCollectorNoNormalize(convert_charrefs=True)) - self._run_check(s, [("starttag", "textarea", []), - ('data', '<!-- not a comment --><i>Egg '), - ('entityref', 'amp'), - ('data', ' Spam</i>'), - ("endtag", "textarea")], - collector=EventCollectorNoNormalize(convert_charrefs=False)) + @support.subTests('tag', [ + 'script', 'style', 'xmp', 'iframe', 'noembed', 'noframes', + 'textarea', 'title', 'noscript', + ]) + def test_closing_tag(self, tag): + for endtag in [tag, tag.upper(), f'{tag} ', f'{tag}\n', + f'{tag}/', f'{tag} foo=bar', f'{tag} foo=">"']: + content = "<!-- not a comment --><i>Spam</i>" + s = f'<{tag.upper()}>{content}</{endtag}>' + self._run_check(s, [ + ("starttag", tag, []), + ('data', content), + ("endtag", tag), + ], collector=EventCollectorNoNormalize(convert_charrefs=False, scripting=True)) + + @support.subTests('tag', [ + 'script', 'style', 'xmp', 'iframe', 'noembed', 'noframes', + 'textarea', 'title', 'noscript', + ]) + def test_invalid_closing_tag(self, tag): + content = ( + f'< /{tag}>' + f'</ {tag}>' + f'</{tag}x>' + f'</{tag}\v>' + f'</{tag}\xa0>' + ) + source = f"<{tag}>{content}</{tag}>" + self._run_check(source, [ + ("starttag", tag, []), + ("data", content), + ("endtag", tag), + ], collector=EventCollector(convert_charrefs=False, scripting=True)) + + @support.subTests('tag,endtag', [ + ('title', 'tıtle'), + ('style', 'ſtyle'), + ('style', 'ſtyle'), + ('style', 'style'), + ('iframe', 'ıframe'), + ('noframes', 'noframeſ'), + ('noscript', 'noſcript'), + ('noscript', 'noscrıpt'), + ('script', 'ſcript'), + ('script', 'scrıpt'), + ]) + def test_invalid_nonascii_closing_tag(self, tag, endtag): + content = f"<br></{endtag}>" + source = f"<{tag}>{content}" + self._run_check(source, [ + ("starttag", tag, []), + ("data", content), + ], collector=EventCollector(convert_charrefs=False, scripting=True)) + source = f"<{tag}>{content}</{tag}>" + self._run_check(source, [ + ("starttag", tag, []), + ("data", content), + ("endtag", tag), + ], collector=EventCollector(convert_charrefs=False, scripting=True)) @support.subTests('tail,end', [ ('', False), @@ -728,20 +840,6 @@ def test_correct_detection_of_start_tags(self): ] self._run_check(html, expected) - def test_EOF_in_charref(self): - # see #17802 - # This test checks that the UnboundLocalError reported in the issue - # is not raised, however I'm not sure the returned values are correct. - # Maybe HTMLParser should use self.unescape for these - data = [ - ('a&', [('data', 'a&')]), - ('a&b', [('data', 'ab')]), - ('a&b ', [('data', 'a'), ('entityref', 'b'), ('data', ' ')]), - ('a&b;', [('data', 'a'), ('entityref', 'b')]), - ] - for html, expected in data: - self._run_check(html, expected) - def test_eof_in_comments(self): data = [ ('<!--', [('comment', '')]), @@ -767,10 +865,6 @@ def test_eof_in_declarations(self): ('<!', [('comment', '')]), ('<!-', [('comment', '-')]), ('<![', [('comment', '[')]), - ('<![CDATA[', [('unknown decl', 'CDATA[')]), - ('<![CDATA[x', [('unknown decl', 'CDATA[x')]), - ('<![CDATA[x]', [('unknown decl', 'CDATA[x]')]), - ('<![CDATA[x]]', [('unknown decl', 'CDATA[x]]')]), ('<!DOCTYPE', [('decl', 'DOCTYPE')]), ('<!DOCTYPE ', [('decl', 'DOCTYPE ')]), ('<!DOCTYPE html', [('decl', 'DOCTYPE html')]), @@ -783,6 +877,18 @@ def test_eof_in_declarations(self): for html, expected in data: self._run_check(html, expected) + @support.subTests('content', ['', 'x', 'x]', 'x]]']) + def test_eof_in_cdata(self, content): + self._run_check('<![CDATA[' + content, + [('unknown decl', 'CDATA[' + content)]) + self._run_check('<![CDATA[' + content, + [('comment', '[CDATA[' + content)], + collector=EventCollector(autocdata=True)) + self._run_check('<svg><text y="100"><![CDATA[' + content, + [('starttag', 'svg', []), + ('starttag', 'text', [('y', '100')]), + ('unknown decl', 'CDATA[' + content)]) + def test_bogus_comments(self): html = ('<!ELEMENT br EMPTY>' '<! not really a comment >' @@ -845,28 +951,53 @@ def test_broken_condcoms(self): ] self._run_check(html, expected) - def test_cdata_declarations(self): - # More tests should be added. See also "8.2.4.42. Markup - # declaration open state", "8.2.4.69. CDATA section state", - # and issue 32876 - html = ('<![CDATA[just some plain text]]>') - expected = [('unknown decl', 'CDATA[just some plain text')] + @support.subTests('content', [ + 'just some plain text', + '<!-- not a comment -->', + '&not-an-entity-ref;', + "<not a='start tag'>", + '', + '[[I have many brackets]]', + 'I have a > in the middle', + 'I have a ]] in the middle', + '] ]>', + ']] >', + ('\n' + ' if (a < b && a > b) {\n' + ' printf("[<marquee>How?</marquee>]");\n' + ' }\n'), + ]) + def test_cdata_section_content(self, content): + # See "13.2.5.42 Markup declaration open state", + # "13.2.5.69 CDATA section state", and issue bpo-32876. + html = f'<svg><text y="100"><![CDATA[{content}]]></text></svg>' + expected = [ + ('starttag', 'svg', []), + ('starttag', 'text', [('y', '100')]), + ('unknown decl', 'CDATA[' + content), + ('endtag', 'text'), + ('endtag', 'svg'), + ] self._run_check(html, expected) + self._run_check(html, expected, collector=EventCollector(autocdata=True)) - def test_cdata_declarations_multiline(self): - html = ('<code><![CDATA[' - ' if (a < b && a > b) {' - ' printf("[<marquee>How?</marquee>]");' - ' }' - ']]></code>') + def test_cdata_section(self): + # See "13.2.5.42 Markup declaration open state". + html = ('<![CDATA[foo<br>bar]]>' + '<svg><text y="100"><![CDATA[foo<br>bar]]></text></svg>' + '<![CDATA[foo<br>bar]]>') expected = [ - ('starttag', 'code', []), - ('unknown decl', - 'CDATA[ if (a < b && a > b) { ' - 'printf("[<marquee>How?</marquee>]"); }'), - ('endtag', 'code') + ('comment', '[CDATA[foo<br'), + ('data', 'bar]]>'), + ('starttag', 'svg', []), + ('starttag', 'text', [('y', '100')]), + ('unknown decl', 'CDATA[foo<br>bar'), + ('endtag', 'text'), + ('endtag', 'svg'), + ('comment', '[CDATA[foo<br'), + ('data', 'bar]]>'), ] - self._run_check(html, expected) + self._run_check(html, expected, collector=EventCollector(autocdata=True)) def test_convert_charrefs_dropped_text(self): # #23144: make sure that all the events are triggered when diff --git a/Lib/test/test_http_cookies.py b/Lib/test/test_http_cookies.py index 2fbc142de2fd34..d1df2ec42f0d14 100644 --- a/Lib/test/test_http_cookies.py +++ b/Lib/test/test_http_cookies.py @@ -1,11 +1,11 @@ # Simple test suite for http/cookies.py - import copy import unittest import doctest from http import cookies import pickle from test import support +import urllib.parse class CookieTests(unittest.TestCase): @@ -17,10 +17,10 @@ def test_basic(self): 'repr': "<SimpleCookie: chips='ahoy' vienna='finger'>", 'output': 'Set-Cookie: chips=ahoy\nSet-Cookie: vienna=finger'}, - {'data': 'keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"', - 'dict': {'keebler' : 'E=mc2; L="Loves"; fudge=\012;'}, - 'repr': '''<SimpleCookie: keebler='E=mc2; L="Loves"; fudge=\\n;'>''', - 'output': 'Set-Cookie: keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"'}, + {'data': 'keebler="E=mc2; L=\\"Loves\\"; fudge=;"', + 'dict': {'keebler' : 'E=mc2; L="Loves"; fudge=;'}, + 'repr': '''<SimpleCookie: keebler='E=mc2; L="Loves"; fudge=;'>''', + 'output': 'Set-Cookie: keebler="E=mc2; L=\\"Loves\\"; fudge=;"'}, # Check illegal cookies that have an '=' char in an unquoted value {'data': 'keebler=E=mc2', @@ -152,17 +152,21 @@ def test_load(self): self.assertEqual(C.output(['path']), 'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme') - self.assertEqual(C.js_output(), r""" + cookie_encoded = urllib.parse.quote('Customer="WILE_E_COYOTE"; Path=/acme; Version=1', safe='', encoding='utf-8') + with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"): + self.assertEqual(C.js_output(), fr""" <script type="text/javascript"> <!-- begin hiding - document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1"; + document.cookie = decodeURIComponent("{cookie_encoded}"); // end hiding --> </script> """) - self.assertEqual(C.js_output(['path']), r""" + cookie_encoded = urllib.parse.quote('Customer="WILE_E_COYOTE"; Path=/acme', safe='', encoding='utf-8') + with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"): + self.assertEqual(C.js_output(['path']), fr""" <script type="text/javascript"> <!-- begin hiding - document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme"; + document.cookie = decodeURIComponent("{cookie_encoded}"); // end hiding --> </script> """) @@ -267,17 +271,21 @@ def test_quoted_meta(self): self.assertEqual(C.output(['path']), 'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme') - self.assertEqual(C.js_output(), r""" + expected_encoded_cookie = urllib.parse.quote('Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1', safe='', encoding='utf-8') + with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"): + self.assertEqual(C.js_output(), fr""" <script type="text/javascript"> <!-- begin hiding - document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1"; + document.cookie = decodeURIComponent("{expected_encoded_cookie}"); // end hiding --> </script> """) - self.assertEqual(C.js_output(['path']), r""" + expected_encoded_cookie = urllib.parse.quote('Customer=\"WILE_E_COYOTE\"; Path=/acme', safe='', encoding='utf-8') + with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"): + self.assertEqual(C.js_output(['path']), fr""" <script type="text/javascript"> <!-- begin hiding - document.cookie = "Customer=\"WILE_E_COYOTE\"; Path=/acme"; + document.cookie = decodeURIComponent("{expected_encoded_cookie}"); // end hiding --> </script> """) @@ -368,14 +376,19 @@ def test_setter(self): self.assertEqual( M.output(), "Set-Cookie: %s=%s; Path=/foo" % (i, "%s_coded_val" % i)) + expected_encoded_cookie = urllib.parse.quote( + "%s=%s; Path=/foo" % (i, "%s_coded_val" % i), + safe='', encoding='utf-8', + ) expected_js_output = """ <script type="text/javascript"> <!-- begin hiding - document.cookie = "%s=%s; Path=/foo"; + document.cookie = decodeURIComponent("%s"); // end hiding --> </script> - """ % (i, "%s_coded_val" % i) - self.assertEqual(M.js_output(), expected_js_output) + """ % (expected_encoded_cookie,) + with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output"): + self.assertEqual(M.js_output(), expected_js_output) for i in ["foo bar", "foo@bar"]: # Try some illegal characters self.assertRaises(cookies.CookieError, @@ -571,6 +584,110 @@ def test_repr(self): r'Set-Cookie: key=coded_val; ' r'expires=\w+, \d+ \w+ \d+ \d+:\d+:\d+ \w+') + def test_control_characters(self): + for c0 in support.control_characters_c0(): + morsel = cookies.Morsel() + + # .__setitem__() + with self.assertRaises(cookies.CookieError): + morsel[c0] = "val" + with self.assertRaises(cookies.CookieError): + morsel["path"] = c0 + + # .__setstate__() + with self.assertRaises(cookies.CookieError): + morsel.__setstate__({'key': c0, 'value': 'val', 'coded_value': 'coded'}) + with self.assertRaises(cookies.CookieError): + morsel.__setstate__({'key': 'key', 'value': c0, 'coded_value': 'coded'}) + with self.assertRaises(cookies.CookieError): + morsel.__setstate__({'key': 'key', 'value': 'val', 'coded_value': c0}) + + # .setdefault() + with self.assertRaises(cookies.CookieError): + morsel.setdefault("path", c0) + with self.assertRaises(cookies.CookieError): + morsel.setdefault(c0, "val") + + # .set() + with self.assertRaises(cookies.CookieError): + morsel.set(c0, "val", "coded-value") + with self.assertRaises(cookies.CookieError): + morsel.set("path", c0, "coded-value") + with self.assertRaises(cookies.CookieError): + morsel.set("path", "val", c0) + + # .update() + with self.assertRaises(cookies.CookieError): + morsel.update({"path": c0}) + with self.assertRaises(cookies.CookieError): + morsel.update({c0: "val"}) + + # .__ior__() + with self.assertRaises(cookies.CookieError): + morsel |= {"path": c0} + with self.assertRaises(cookies.CookieError): + morsel |= {c0: "val"} + + def test_control_characters_output(self): + # Tests that even if the internals of Morsel are modified + # that a call to .output() has control character safeguards. + for c0 in support.control_characters_c0(): + morsel = cookies.Morsel() + morsel.set("key", "value", "coded-value") + morsel._key = c0 # Override private variable. + cookie = cookies.SimpleCookie() + cookie["cookie"] = morsel + with self.assertRaises(cookies.CookieError): + cookie.output() + + morsel = cookies.Morsel() + morsel.set("key", "value", "coded-value") + morsel._coded_value = c0 # Override private variable. + cookie = cookies.SimpleCookie() + cookie["cookie"] = morsel + with self.assertRaises(cookies.CookieError): + cookie.output() + + # Tests that .js_output() also has control character safeguards. + for c0 in support.control_characters_c0(): + morsel = cookies.Morsel() + morsel.set("key", "value", "coded-value") + morsel._key = c0 # Override private variable. + cookie = cookies.SimpleCookie() + cookie["cookie"] = morsel + with self.assertRaises(cookies.CookieError): + with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output"): + cookie.js_output() + + morsel = cookies.Morsel() + morsel.set("key", "value", "coded-value") + morsel._coded_value = c0 # Override private variable. + cookie = cookies.SimpleCookie() + cookie["cookie"] = morsel + with self.assertRaises(cookies.CookieError): + with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output"): + cookie.js_output() + + def test_morsel_js_output_deprecated(self): + morsel = cookies.Morsel() + morsel.set("key", "value", "value") + with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output") as cm: + result = morsel.js_output() + self.assertEqual(cm.filename, __file__) + self.assertIn("document.cookie", result) + + + def test_basecookie_js_output_warns_once(self): + C = cookies.SimpleCookie() + C["key"] = "value" + with self.assertWarns(DeprecationWarning) as cm: + C.js_output() + deprecation_warnings = [ + w for w in cm.warnings if issubclass(w.category, DeprecationWarning) + ] + self.assertEqual(len(deprecation_warnings), 1) + self.assertRegex(str(deprecation_warnings[0].message), r"BaseCookie\.js_output") + self.assertEqual(cm.filename, __file__) def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite(cookies)) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 47e3914d1dd62e..f771fc48dada36 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -369,6 +369,51 @@ def test_invalid_headers(self): with self.assertRaisesRegex(ValueError, 'Invalid header'): conn.putheader(name, value) + def test_invalid_tunnel_headers(self): + cases = ( + ('Invalid\r\nName', 'ValidValue'), + ('Invalid\rName', 'ValidValue'), + ('Invalid\nName', 'ValidValue'), + ('\r\nInvalidName', 'ValidValue'), + ('\rInvalidName', 'ValidValue'), + ('\nInvalidName', 'ValidValue'), + (' InvalidName', 'ValidValue'), + ('\tInvalidName', 'ValidValue'), + ('Invalid:Name', 'ValidValue'), + (':InvalidName', 'ValidValue'), + ('ValidName', 'Invalid\r\nValue'), + ('ValidName', 'Invalid\rValue'), + ('ValidName', 'Invalid\nValue'), + ('ValidName', 'InvalidValue\r\n'), + ('ValidName', 'InvalidValue\r'), + ('ValidName', 'InvalidValue\n'), + ) + for name, value in cases: + with self.subTest((name, value)): + conn = client.HTTPConnection('example.com') + conn.set_tunnel('tunnel', headers={ + name: value + }) + conn.sock = FakeSocket('') + with self.assertRaisesRegex(ValueError, 'Invalid header'): + conn._tunnel() # Called in .connect() + + def test_invalid_tunnel_host(self): + cases = ( + 'invalid\r.host', + '\ninvalid.host', + 'invalid.host\r\n', + 'invalid.host\x00', + 'invalid host', + ) + for tunnel_host in cases: + with self.subTest(tunnel_host): + conn = client.HTTPConnection('example.com') + conn.set_tunnel(tunnel_host) + conn.sock = FakeSocket('') + with self.assertRaisesRegex(ValueError, 'Tunnel host can\'t contain control characters'): + conn._tunnel() # Called in .connect() + def test_headers_debuglevel(self): body = ( b'HTTP/1.1 200 OK\r\n' @@ -1511,6 +1556,72 @@ def run_server(): thread.join() self.assertEqual(result, b"proxied data\n") + def test_large_content_length(self): + serv = socket.create_server((HOST, 0)) + self.addCleanup(serv.close) + + def run_server(): + [conn, address] = serv.accept() + with conn: + while conn.recv(1024): + conn.sendall( + b"HTTP/1.1 200 Ok\r\n" + b"Content-Length: %d\r\n" + b"\r\n" % size) + conn.sendall(b'A' * (size//3)) + conn.sendall(b'B' * (size - size//3)) + + thread = threading.Thread(target=run_server) + thread.start() + self.addCleanup(thread.join, 1.0) + + conn = client.HTTPConnection(*serv.getsockname()) + try: + for w in range(15, 27): + size = 1 << w + conn.request("GET", "/") + with conn.getresponse() as response: + self.assertEqual(len(response.read()), size) + finally: + conn.close() + thread.join(1.0) + + def test_large_content_length_truncated(self): + serv = socket.create_server((HOST, 0)) + self.addCleanup(serv.close) + + def run_server(): + while True: + [conn, address] = serv.accept() + with conn: + conn.recv(1024) + if not size: + break + conn.sendall( + b"HTTP/1.1 200 Ok\r\n" + b"Content-Length: %d\r\n" + b"\r\n" + b"Text" % size) + + thread = threading.Thread(target=run_server) + thread.start() + self.addCleanup(thread.join, 1.0) + + conn = client.HTTPConnection(*serv.getsockname()) + try: + for w in range(18, 65): + size = 1 << w + conn.request("GET", "/") + with conn.getresponse() as response: + self.assertRaises(client.IncompleteRead, response.read) + conn.close() + finally: + conn.close() + size = 0 + conn.request("GET", "/") + conn.close() + thread.join(1.0) + def test_putrequest_override_domain_validation(self): """ It should be possible to override the default validation diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 2548a7c5f292f0..d4ae032610a91e 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -28,10 +28,12 @@ import threading from unittest import mock from io import BytesIO, StringIO +from _colorize import get_theme import unittest from test import support from test.support import ( + force_not_colorized, is_apple, import_helper, os_helper, threading_helper ) from test.support.script_helper import kill_python, spawn_python @@ -362,6 +364,44 @@ def test_head_via_send_error(self): self.assertEqual(b'', data) +class HTTP09ServerTestCase(BaseTestCase): + + class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler): + """Request handler for HTTP/0.9 server.""" + + def do_GET(self): + self.wfile.write(f'OK: here is {self.path}\r\n'.encode()) + + def setUp(self): + super().setUp() + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock = self.enterContext(self.sock) + self.sock.connect((self.HOST, self.PORT)) + + def test_simple_get(self): + self.sock.send(b'GET /index.html\r\n') + res = self.sock.recv(1024) + self.assertEqual(res, b"OK: here is /index.html\r\n") + + def test_invalid_request(self): + self.sock.send(b'POST /index.html\r\n') + res = self.sock.recv(1024) + self.assertIn(b"Bad HTTP/0.9 request type ('POST')", res) + + def test_single_request(self): + self.sock.send(b'GET /foo.html\r\n') + res = self.sock.recv(1024) + self.assertEqual(res, b"OK: here is /foo.html\r\n") + + # Ignore errors if the connection is already closed, + # as this is the expected behavior of HTTP/0.9. + with contextlib.suppress(OSError): + self.sock.send(b'GET /bar.html\r\n') + res = self.sock.recv(1024) + # The server should not process our request. + self.assertEqual(res, b'') + + def certdata_file(*path): return os.path.join(os.path.dirname(__file__), "certdata", *path) @@ -442,6 +482,7 @@ def do_GET(self): def do_ERROR(self): self.send_error(HTTPStatus.NOT_FOUND, 'File not found') + @force_not_colorized def test_get(self): self.con = http.client.HTTPConnection(self.HOST, self.PORT) self.con.connect() @@ -452,6 +493,7 @@ def test_get(self): self.assertEndsWith(err.getvalue(), '"GET / HTTP/1.1" 200 -\n') + @force_not_colorized def test_err(self): self.con = http.client.HTTPConnection(self.HOST, self.PORT) self.con.connect() @@ -465,8 +507,49 @@ def test_err(self): self.assertEndsWith(lines[1], '"ERROR / HTTP/1.1" 404 -') +@support.force_colorized_test_class +class RequestHandlerColorizedLoggingTestCase(RequestHandlerLoggingTestCase): + + def test_get(self): + t = get_theme(force_color=True).http_server + self.con = http.client.HTTPConnection(self.HOST, self.PORT) + self.con.connect() + + with support.captured_stderr() as err: + self.con.request("GET", "/") + self.con.getresponse() + + output = err.getvalue() + self.assertIn(f"{t.path}/{t.reset}", output) + self.assertIn(f"{t.status_ok}200", output) + self.assertIn(t.reset, output) + + def test_err(self): + t = get_theme(force_color=True).http_server + self.con = http.client.HTTPConnection(self.HOST, self.PORT) + self.con.connect() + + with support.captured_stderr() as err: + self.con.request("ERROR", "/") + self.con.getresponse() + + lines = err.getvalue().split("\n") + self.assertIn( + f"{t.error}code 404, message File not found{t.reset}", lines[0] + ) + self.assertIn(f"{t.status_client_error}404", lines[1]) + + +class CustomHeaderSimpleHTTPRequestHandler(SimpleHTTPRequestHandler): + extra_response_headers = None + + def __init__(self, *args, **kwargs): + kwargs.setdefault('extra_response_headers', self.extra_response_headers) + super().__init__(*args, **kwargs) + + class SimpleHTTPServerTestCase(BaseTestCase): - class request_handler(NoLogRequestHandler, SimpleHTTPRequestHandler): + class request_handler(NoLogRequestHandler, CustomHeaderSimpleHTTPRequestHandler): pass def setUp(self): @@ -493,12 +576,12 @@ def setUp(self): def tearDown(self): try: os.chdir(self.cwd) - try: - shutil.rmtree(self.tempdir) - except: - pass finally: super().tearDown() + try: + shutil.rmtree(self.tempdir) + except: + pass def check_status_and_reason(self, response, status, data=None): def close_conn(): @@ -525,6 +608,15 @@ def close_conn(): reader.close() return body + def check_status_and_headers(self, response, status, headers=None): + # Drain the body so the server-side handler can close the file + # before tearDown removes the tempdir (matters on Windows). + response.read() + self.assertEqual(response.status, status) + if headers: + for name, value in headers.items(): + self.assertEqual(response.getheader(name), value) + def check_list_dir_dirname(self, dirname, quotedname=None): fullpath = os.path.join(self.tempdir, dirname) try: @@ -732,7 +824,7 @@ def test_get(self): # chmod() doesn't work as expected on Windows, and filesystem # permissions are ignored by root on Unix. - if os.name == 'posix' and os.geteuid() != 0: + if os.name == 'posix' and os.geteuid() != 0 and sys.platform != 'cygwin': os.chmod(self.tempdir, 0) try: response = self.request(self.base_url + '/') @@ -823,6 +915,67 @@ def test_path_without_leading_slash(self): self.assertEqual(response.getheader("Location"), self.tempdir_name + "/?hi=1") + def test_extra_response_headers_list_dir(self): + with mock.patch.object(self.request_handler, 'extra_response_headers', [ + ('X-Test1', 'test1'), + ('X-Test2', 'test2'), + ]): + response = self.request(self.base_url + '/') + self.check_status_and_headers(response, HTTPStatus.OK, { + "X-Test1": "test1", + "X-Test2": "test2", + }) + + def test_extra_response_headers_get_file(self): + with mock.patch.object(self.request_handler, 'extra_response_headers', [ + ('Set-Cookie', 'test1=value1'), + ('Set-Cookie', 'test2=value2'), + ('X-Test1', 'value3'), + ]): + data = b"Dummy index file\r\n" + with open(os.path.join(self.tempdir_name, 'index.html'), 'wb') as f: + f.write(data) + response = self.request(self.base_url + '/') + self.check_status_and_headers(response, HTTPStatus.OK, { + "Set-Cookie": "test1=value1, test2=value2", + "X-Test1": "value3", + }) + + def test_extra_response_headers_missing_on_404(self): + with mock.patch.object(self.request_handler, 'extra_response_headers', [ + ('X-Test1', 'value'), + ]): + response = self.request(self.base_url + '/missing.html') + self.check_status_and_headers(response, HTTPStatus.NOT_FOUND, { + "X-Test1": None, + }) + + def test_extra_response_headers_dont_overwrite_default_headers(self): + with mock.patch.object(self.request_handler, 'extra_response_headers', [ + ('Content-Type', 'test/not_allowed'), + ('Server', 'not_allowed'), + ('Set-Cookie', 'test=allowed'), + ]): + # The Content-Type header should not be overwritten by the extra_response_headers + # But cookies in the extra_allowed_duplicate_headers are allowed, + # including Set-Cookie + response = self.request(self.base_url + '/') + self.check_status_and_headers(response, HTTPStatus.OK, { + "Set-Cookie": "test=allowed", + }) + self.assertNotEqual(response.getheader("Content-Type"), 'test/not_allowed') + self.assertNotEqual(response.getheader("Server"), 'not_allowed') + + def test_multiple_requests_dont_duplicate_extra_response_headers(self): + with mock.patch.object(self.request_handler, 'extra_response_headers', [ + ('x-test', 'test-value'), + ]): + for _ in range(2): + response = self.request(self.base_url + '/') + self.check_status_and_headers(response, HTTPStatus.OK, { + "x-test": "test-value", + }) + class SocketlessRequestHandler(SimpleHTTPRequestHandler): def __init__(self, directory=None): @@ -897,6 +1050,7 @@ def verify_http_server_response(self, response): match = self.HTTPResponseMatch.search(response) self.assertIsNotNone(match) + @force_not_colorized def test_unprintable_not_logged(self): # We call the method from the class directly as our Socketless # Handler subclass overrode it... nice for everything BUT this test. @@ -1303,6 +1457,7 @@ class CommandLineTestCase(unittest.TestCase): 'protocol': default_protocol, 'port': default_port, 'bind': default_bind, + 'content_type': 'application/octet-stream', 'tls_cert': None, 'tls_key': None, 'tls_password': None, @@ -1371,6 +1526,31 @@ def test_protocol_flag(self, mock_func): mock_func.assert_called_once_with(**call_args) mock_func.reset_mock() + @mock.patch('http.server.test') + def test_content_type_flag(self, mock_func): + content_types = ['text/html', 'text/plain', 'application/json'] + for content_type in content_types: + with self.subTest(content_type=content_type): + self.invoke_httpd('--content-type', content_type) + call_args = self.args | dict(content_type=content_type) + mock_func.assert_called_once_with(**call_args) + mock_func.reset_mock() + + @mock.patch('http.server.test') + def test_header_flag(self, mock_func): + call_args = self.args + self.invoke_httpd('--header', 'h1', 'v1', '-H', 'h2', 'v2') + mock_func.assert_called_once_with(**call_args) + mock_func.reset_mock() + + def test_extra_header_flag_too_few_args(self): + with self.assertRaises(SystemExit): + self.invoke_httpd('--header', 'h1') + + def test_extra_header_flag_too_many_args(self): + with self.assertRaises(SystemExit): + self.invoke_httpd('--header', 'h1', 'v1', 'h2') + @unittest.skipIf(ssl is None, "requires ssl") @mock.patch('http.server.test') def test_tls_cert_and_key_flags(self, mock_func): @@ -1454,6 +1634,30 @@ def test_unknown_flag(self, _): self.assertEqual(stdout.getvalue(), '') self.assertIn('error', stderr.getvalue()) + @mock.patch('http.server.test') + def test_extra_response_headers_arg(self, mock_test): + # Call the main function with extra response headers cli args + server._main( + ['-H', 'Set-Cookie', 'k=v', '-H', 'Set-Cookie', 'k2=v2:v3 v4', '8080'] + ) + # Get the ServerClass (DualStackServerMixin subclass) that _main() + # passed to test(), and verify its finish_request passes + # extra_response_headers to the handler. + _, kwargs = mock_test.call_args + server_class = kwargs['ServerClass'] + + mock_handler_class = mock.MagicMock() + mock_server = mock.Mock() + mock_server.RequestHandlerClass = mock_handler_class + server_class.finish_request(mock_server, mock.Mock(), '127.0.0.1') + mock_handler_class.assert_called_once_with( + mock.ANY, mock.ANY, mock_server, + directory=mock.ANY, + extra_response_headers=[ + ['Set-Cookie', 'k=v'], ['Set-Cookie', 'k2=v2:v3 v4'] + ] + ) + class CommandLineRunTimeTestCase(unittest.TestCase): served_data = os.urandom(32) @@ -1522,6 +1726,16 @@ def test_https_client(self): self.assertEqual(res, self.served_data) +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(http.server, "__version__") + self.assertEqual(cm.filename, __file__) + + def setUpModule(): unittest.addModuleCleanup(os.chdir, os.getcwd()) diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index a13ee58d650e1b..fb256fb7cbcd34 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -12,8 +12,7 @@ import socket from test.support import verbose, run_with_tz, run_with_locale, cpython_only -from test.support import hashlib_helper -from test.support import threading_helper +from test.support import hashlib_helper, threading_helper import unittest from unittest import mock from datetime import datetime, timezone, timedelta @@ -256,7 +255,20 @@ def cmd_IDLE(self, tag, args): self._send_tagged(tag, 'BAD', 'Expected DONE') -class NewIMAPTestsMixin(): +class AuthHandler_CRAM_MD5(SimpleIMAPHandler): + capabilities = 'LOGINDISABLED AUTH=CRAM-MD5' + def cmd_AUTHENTICATE(self, tag, args): + self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm' + 'VzdG9uLm1jaS5uZXQ=') + r = yield + if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT' + b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'): + self._send_tagged(tag, 'OK', 'CRAM-MD5 successful') + else: + self._send_tagged(tag, 'NO', 'No access') + + +class NewIMAPTestsMixin: client = None def _setup(self, imap_handler, connect=True): @@ -360,7 +372,11 @@ def cmd_AUTHENTICATE(self, tag, args): self._send_tagged(tag, 'OK', 'FAKEAUTH successful') def cmd_APPEND(self, tag, args): self._send_textline('+') - self.server.response = yield + self.server.response = args + literal = yield + self.server.response.append(literal) + literal = yield + self.server.response.append(literal) self._send_tagged(tag, 'OK', 'okay') client, server = self._setup(UTF8AppendServer) self.assertEqual(client._encoding, 'ascii') @@ -371,10 +387,13 @@ def cmd_APPEND(self, tag, args): self.assertEqual(code, 'OK') self.assertEqual(client._encoding, 'utf-8') msg_string = 'Subject: üñí©öðé' - typ, data = client.append(None, None, None, msg_string.encode('utf-8')) + typ, data = client.append( + None, None, None, (msg_string + '\n').encode('utf-8')) self.assertEqual(typ, 'OK') self.assertEqual(server.response, - ('UTF8 (%s)\r\n' % msg_string).encode('utf-8')) + ['INBOX', 'UTF8', + '(~{25}', ('%s\r\n' % msg_string).encode('utf-8'), + b')\r\n' ]) def test_search_disallows_charset_in_utf8_mode(self): class UTF8Server(SimpleIMAPHandler): @@ -415,6 +434,16 @@ def cmd_AUTHENTICATE(self, tag, args): r'\[AUTHENTICATIONFAILED\] invalid'): client.authenticate('MYAUTH', lambda x: b'fake') + def test_invalid_login(self): + class MyServer(SimpleIMAPHandler): + def cmd_LOGIN(self, tag, args): + self.server.logged = args[0] + self._send_tagged(tag, 'NO', '[LOGIN] failed') + client, _ = self._setup(MyServer) + with self.assertRaisesRegex(imaplib.IMAP4.error, + r'\[LOGIN\] failed'): + client.login('user', 'wrongpass') + def test_valid_authentication_bytes(self): class MyServer(SimpleIMAPHandler): def cmd_AUTHENTICATE(self, tag, args): @@ -439,40 +468,26 @@ def cmd_AUTHENTICATE(self, tag, args): @hashlib_helper.requires_hashdigest('md5', openssl=True) def test_login_cram_md5_bytes(self): - class AuthHandler(SimpleIMAPHandler): - capabilities = 'LOGINDISABLED AUTH=CRAM-MD5' - def cmd_AUTHENTICATE(self, tag, args): - self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm' - 'VzdG9uLm1jaS5uZXQ=') - r = yield - if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT' - b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'): - self._send_tagged(tag, 'OK', 'CRAM-MD5 successful') - else: - self._send_tagged(tag, 'NO', 'No access') - client, _ = self._setup(AuthHandler) - self.assertTrue('AUTH=CRAM-MD5' in client.capabilities) + client, _ = self._setup(AuthHandler_CRAM_MD5) + self.assertIn('AUTH=CRAM-MD5', client.capabilities) ret, _ = client.login_cram_md5("tim", b"tanstaaftanstaaf") self.assertEqual(ret, "OK") @hashlib_helper.requires_hashdigest('md5', openssl=True) def test_login_cram_md5_plain_text(self): - class AuthHandler(SimpleIMAPHandler): - capabilities = 'LOGINDISABLED AUTH=CRAM-MD5' - def cmd_AUTHENTICATE(self, tag, args): - self._send_textline('+ PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2Uucm' - 'VzdG9uLm1jaS5uZXQ=') - r = yield - if (r == b'dGltIGYxY2E2YmU0NjRiOWVmYT' - b'FjY2E2ZmZkNmNmMmQ5ZjMy\r\n'): - self._send_tagged(tag, 'OK', 'CRAM-MD5 successful') - else: - self._send_tagged(tag, 'NO', 'No access') - client, _ = self._setup(AuthHandler) - self.assertTrue('AUTH=CRAM-MD5' in client.capabilities) + client, _ = self._setup(AuthHandler_CRAM_MD5) + self.assertIn('AUTH=CRAM-MD5', client.capabilities) ret, _ = client.login_cram_md5("tim", "tanstaaftanstaaf") self.assertEqual(ret, "OK") + @hashlib_helper.block_algorithm("md5") + def test_login_cram_md5_blocked(self): + client, _ = self._setup(AuthHandler_CRAM_MD5) + self.assertIn('AUTH=CRAM-MD5', client.capabilities) + msg = re.escape("CRAM-MD5 authentication is not supported") + with self.assertRaisesRegex(imaplib.IMAP4.error, msg): + client.login_cram_md5("tim", b"tanstaaftanstaaf") + def test_aborted_authentication(self): class MyServer(SimpleIMAPHandler): def cmd_AUTHENTICATE(self, tag, args): @@ -652,13 +667,41 @@ def test_unselect(self): self.assertEqual(data[0], b'Returned to authenticated state. (Success)') self.assertEqual(client.state, 'AUTH') + def test_control_characters(self): + client, _ = self._setup(SimpleIMAPHandler) + for c0 in support.control_characters_c0(): + with self.assertRaises(ValueError): + client.login(f'user{c0}', 'pass') + # property tests - def test_file_property_should_not_be_accessed(self): + def test_file_property_getter(self): + client, _ = self._setup(SimpleIMAPHandler) + with self.assertWarns(DeprecationWarning): + self.assertIsInstance(client.file.raw, socket.SocketIO) + + def test_file_property_setter(self): client, _ = self._setup(SimpleIMAPHandler) - # the 'file' property replaced a private attribute that is now unsafe - with self.assertWarns(RuntimeWarning): - client.file + with self.assertWarns(DeprecationWarning): + # ensure that the caller closes the existing file + client.file.close() + for new_file in [mock.Mock(), None]: + with self.assertWarns(DeprecationWarning): + client.file = new_file + with self.assertWarns(DeprecationWarning): + self.assertIs(client.file, new_file) + + def test_file_property_setter_should_not_close_previous_file(self): + client, _ = self._setup(SimpleIMAPHandler) + with mock.patch.object(client, "_imaplib_file", mock.Mock()) as f: + f.close.assert_not_called() + with self.assertWarns(DeprecationWarning): + self.assertIs(client.file, f) + with self.assertWarns(DeprecationWarning): + client.file = None + with self.assertWarns(DeprecationWarning): + self.assertIsNone(client.file) + f.close.assert_not_called() class NewIMAPTests(NewIMAPTestsMixin, unittest.TestCase): @@ -883,7 +926,11 @@ def test_enable_UTF8_True_append(self): class UTF8AppendServer(self.UTF8Server): def cmd_APPEND(self, tag, args): self._send_textline('+') - self.server.response = yield + self.server.response = args + literal = yield + self.server.response.append(literal) + literal = yield + self.server.response.append(literal) self._send_tagged(tag, 'OK', 'okay') with self.reaped_pair(UTF8AppendServer) as (server, client): @@ -897,12 +944,12 @@ def cmd_APPEND(self, tag, args): self.assertEqual(client._encoding, 'utf-8') msg_string = 'Subject: üñí©öðé' typ, data = client.append( - None, None, None, msg_string.encode('utf-8')) + None, None, None, (msg_string + '\n').encode('utf-8')) self.assertEqual(typ, 'OK') - self.assertEqual( - server.response, - ('UTF8 (%s)\r\n' % msg_string).encode('utf-8') - ) + self.assertEqual(server.response, + ['INBOX', 'UTF8', + '(~{25}', ('%s\r\n' % msg_string).encode('utf-8'), + b')\r\n' ]) # XXX also need a test that makes sure that the Literal and Untagged_status # regexes uses unicode in UTF8 mode instead of the default ASCII. @@ -1108,5 +1155,15 @@ def test_ssl_verified(self): client.shutdown() +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(imaplib, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index abbd5f1ed5f12f..9f3df8010d3233 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -15,6 +15,7 @@ import os import py_compile import random +import re import shutil import stat import subprocess @@ -23,6 +24,7 @@ import threading import time import types +import warnings import unittest from unittest import mock import _imp @@ -43,6 +45,7 @@ Py_GIL_DISABLED, no_rerun, force_not_colorized_test_class, + catch_unraisable_exception ) from test.support.import_helper import ( forget, make_legacy_pyc, unlink, unload, ready_to_import, @@ -51,7 +54,7 @@ TESTFN, rmtree, temp_umask, TESTFN_UNENCODABLE) from test.support import script_helper from test.support import threading_helper -from test.test_importlib.util import uncache +from test.test_importlib.util import uncache, temporary_pycache_prefix from types import ModuleType try: import _testsinglephase @@ -63,8 +66,10 @@ _testmultiphase = None try: import _interpreters + import concurrent.interpreters except ModuleNotFoundError: _interpreters = None + concurrent = None try: import _testinternalcapi except ImportError: @@ -72,7 +77,7 @@ skip_if_dont_write_bytecode = unittest.skipIf( - sys.dont_write_bytecode, + sys.dont_write_bytecode or sys.implementation.cache_tag is None, "test meaningful only when writing bytecode") @@ -359,6 +364,15 @@ def test_import_raises_ModuleNotFoundError(self): with self.assertRaises(ModuleNotFoundError): import something_that_should_not_exist_anywhere + def test_import_null_byte_in_name_raises_ModuleNotFoundError(self): + # gh-150633: module names containing null bytes should not + # lead to duplicates in sys.modules + before = set(sys.modules) + with self.assertRaises(ModuleNotFoundError): + __import__('zipimport\x00junk') + + self.assertEqual(set(sys.modules), before) + def test_from_import_missing_module_raises_ModuleNotFoundError(self): with self.assertRaises(ModuleNotFoundError): from something_that_should_not_exist_anywhere import blah @@ -412,7 +426,6 @@ def test_from_import_missing_attr_path_is_canonical(self): self.assertIsNotNone(cm.exception) def test_from_import_star_invalid_type(self): - import re with ready_to_import() as (name, path): with open(path, 'w', encoding='utf-8') as f: f.write("__all__ = [b'invalid_type']") @@ -501,7 +514,7 @@ def test_module_with_large_stack(self, module='longlist'): try: # Compile & remove .py file; we only need .pyc. # Bytecode must be relocated from the PEP 3147 bytecode-only location. - py_compile.compile(filename) + make_legacy_pyc(filename, allow_compile=True) finally: unlink(filename) @@ -511,7 +524,6 @@ def test_module_with_large_stack(self, module='longlist'): namespace = {} try: - make_legacy_pyc(filename) # This used to crash. exec('import ' + module, None, namespace) finally: @@ -1187,6 +1199,7 @@ class substr(str): @unittest.skipIf(sys.platform == 'win32', 'Cannot delete cwd on Windows') @unittest.skipIf(sys.platform == 'sunos5', 'Cannot delete cwd on Solaris/Illumos') + @unittest.skipIf(sys.platform.startswith('aix'), 'Cannot delete cwd on AIX') def test_script_shadowing_stdlib_cwd_failure(self): with os_helper.temp_dir() as tmp: subtmp = os.path.join(tmp, "subtmp") @@ -1249,6 +1262,44 @@ class Spec2: origin = "a\x00b" _imp.create_dynamic(Spec2()) + def test_create_builtin(self): + class Spec: + pass + spec = Spec() + + spec.name = "sys" + self.assertIs(_imp.create_builtin(spec), sys) + + spec.name = None + with self.assertRaisesRegex(TypeError, 'name must be string, not NoneType'): + _imp.create_builtin(spec) + + # gh-142029 + spec.name = "nonexistent_lib" + with self.assertRaises(ModuleNotFoundError): + _imp.create_builtin(spec) + + # gh-142029 + spec.name = "" + with self.assertRaisesRegex(ValueError, 'name must not be empty'): + _imp.create_builtin(spec) + + def test_filter_syntax_warnings_by_module(self): + module_re = r'test\.test_import\.data\.syntax_warnings\z' + unload('test.test_import.data.syntax_warnings') + with (os_helper.temp_dir() as tmpdir, + temporary_pycache_prefix(tmpdir), + warnings.catch_warnings(record=True) as wlog): + warnings.simplefilter('error') + warnings.filterwarnings('always', module=module_re) + warnings.filterwarnings('error', module='syntax_warnings') + import test.test_import.data.syntax_warnings + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21]) + filename = test.test_import.data.syntax_warnings.__file__ + for wm in wlog: + self.assertEqual(wm.filename, filename) + self.assertIs(wm.category, SyntaxWarning) + @skip_if_dont_write_bytecode class FilePermissionTests(unittest.TestCase): @@ -1357,7 +1408,10 @@ def func(): """ dir_name = os.path.abspath(TESTFN) file_name = os.path.join(dir_name, module_name) + os.extsep + "py" - compiled_name = importlib.util.cache_from_source(file_name) + try: + compiled_name = importlib.util.cache_from_source(file_name) + except NotImplementedError: + compiled_name = None def setUp(self): self.sys_path = sys.path[:] @@ -1375,7 +1429,8 @@ def tearDown(self): else: unload(self.module_name) unlink(self.file_name) - unlink(self.compiled_name) + if self.compiled_name: + unlink(self.compiled_name) rmtree(self.dir_name) def import_module(self): @@ -1394,6 +1449,8 @@ def test_basics(self): self.assertEqual(mod.code_filename, self.file_name) self.assertEqual(mod.func_filename, self.file_name) + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag is not None') def test_incorrect_code_name(self): py_compile.compile(self.file_name, dfile="another_module.py") mod = self.import_module() @@ -1403,9 +1460,9 @@ def test_incorrect_code_name(self): def test_module_without_source(self): target = "another_module.py" - py_compile.compile(self.file_name, dfile=target) + pyc_file = self.file_name + 'c' + py_compile.compile(self.file_name, pyc_file, dfile=target) os.remove(self.file_name) - pyc_file = make_legacy_pyc(self.file_name) importlib.invalidate_caches() mod = self.import_module() self.assertEqual(mod.module_filename, pyc_file) @@ -1413,8 +1470,9 @@ def test_module_without_source(self): self.assertEqual(mod.func_filename, target) def test_foreign_code(self): - py_compile.compile(self.file_name) - with open(self.compiled_name, "rb") as f: + compiled_name = self.compiled_name or (self.file_name + 'c') + py_compile.compile(self.file_name, compiled_name) + with open(compiled_name, "rb") as f: header = f.read(16) code = marshal.load(f) constants = list(code.co_consts) @@ -1422,9 +1480,11 @@ def test_foreign_code(self): pos = constants.index(1000) constants[pos] = foreign_code code = code.replace(co_consts=tuple(constants)) - with open(self.compiled_name, "wb") as f: + with open(compiled_name, "wb") as f: f.write(header) marshal.dump(code, f) + if not self.compiled_name: + os.remove(self.file_name) mod = self.import_module() self.assertEqual(mod.constant.co_filename, foreign_code.co_filename) @@ -1679,78 +1739,6 @@ def test_missing_source_legacy(self): finally: os.remove(pyc_file) - def test___cached__(self): - # Modules now also have an __cached__ that points to the pyc file. - m = __import__(TESTFN) - pyc_file = importlib.util.cache_from_source(TESTFN + '.py') - self.assertEqual(m.__cached__, os.path.join(os.getcwd(), pyc_file)) - - @skip_if_dont_write_bytecode - def test___cached___legacy_pyc(self): - # Like test___cached__() except that for backward compatibility, - # when the pyc file lives where the py file would have been (and named - # without the tag), it is importable. The __cached__ of the imported - # module is the pyc location. - __import__(TESTFN) - # pyc_file gets removed in _clean() via tearDown(). - pyc_file = make_legacy_pyc(self.source) - os.remove(self.source) - unload(TESTFN) - importlib.invalidate_caches() - m = __import__(TESTFN) - self.assertEqual(m.__cached__, - os.path.join(os.getcwd(), os.path.relpath(pyc_file))) - - @skip_if_dont_write_bytecode - def test_package___cached__(self): - # Like test___cached__ but for packages. - def cleanup(): - rmtree('pep3147') - unload('pep3147.foo') - unload('pep3147') - os.mkdir('pep3147') - self.addCleanup(cleanup) - # Touch the __init__.py - with open(os.path.join('pep3147', '__init__.py'), 'wb'): - pass - with open(os.path.join('pep3147', 'foo.py'), 'wb'): - pass - importlib.invalidate_caches() - m = __import__('pep3147.foo') - init_pyc = importlib.util.cache_from_source( - os.path.join('pep3147', '__init__.py')) - self.assertEqual(m.__cached__, os.path.join(os.getcwd(), init_pyc)) - foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py')) - self.assertEqual(sys.modules['pep3147.foo'].__cached__, - os.path.join(os.getcwd(), foo_pyc)) - - def test_package___cached___from_pyc(self): - # Like test___cached__ but ensuring __cached__ when imported from a - # PEP 3147 pyc file. - def cleanup(): - rmtree('pep3147') - unload('pep3147.foo') - unload('pep3147') - os.mkdir('pep3147') - self.addCleanup(cleanup) - # Touch the __init__.py - with open(os.path.join('pep3147', '__init__.py'), 'wb'): - pass - with open(os.path.join('pep3147', 'foo.py'), 'wb'): - pass - importlib.invalidate_caches() - m = __import__('pep3147.foo') - unload('pep3147.foo') - unload('pep3147') - importlib.invalidate_caches() - m = __import__('pep3147.foo') - init_pyc = importlib.util.cache_from_source( - os.path.join('pep3147', '__init__.py')) - self.assertEqual(m.__cached__, os.path.join(os.getcwd(), init_pyc)) - foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py')) - self.assertEqual(sys.modules['pep3147.foo'].__cached__, - os.path.join(os.getcwd(), foo_pyc)) - def test_recompute_pyc_same_second(self): # Even when the source file doesn't change timestamp, a change in # source size is enough to trigger recomputation of the pyc file. @@ -2019,10 +2007,6 @@ def test_import_bug(self): # away from the traceback. self.create_module("foo", "") importlib = sys.modules['_frozen_importlib_external'] - if 'load_module' in vars(importlib.SourceLoader): - old_exec_module = importlib.SourceLoader.exec_module - else: - old_exec_module = None try: def exec_module(*args): 1/0 @@ -2035,10 +2019,7 @@ def exec_module(*args): self.fail("ZeroDivisionError should have been raised") self.assert_traceback(tb, [__file__, '<frozen importlib', __file__]) finally: - if old_exec_module is None: - del importlib.SourceLoader.exec_module - else: - importlib.SourceLoader.exec_module = old_exec_module + del importlib.SourceLoader.exec_module @unittest.skipUnless(TESTFN_UNENCODABLE, 'need TESTFN_UNENCODABLE') def test_unencodable_filename(self): @@ -2466,6 +2447,21 @@ def test_multi_init_extension_per_interpreter_gil_compat(self): self.check_compatible_here( modname, filename, strict=False, isolated=False) + @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") + def test_testmultiphase_exec_multiple(self): + modname = '_testmultiphase_exec_multiple' + filename = _testmultiphase.__file__ + module = import_extension_from_file(modname, filename, + put_in_sys_modules=False) + # All three exec's were called. + self.assertEqual(module.a, 1) + self.assertEqual(module.b, 2) + self.assertEqual(module.c, 3) + # They were called in order. + keys = list(module.__dict__) + self.assertLess(keys.index('a'), keys.index('b')) + self.assertLess(keys.index('b'), keys.index('c')) + @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") def test_python_compat(self): module = 'threading' @@ -2539,6 +2535,32 @@ def test_disallowed_reimport(self): excsnap = _interpreters.run_string(interpid, script) self.assertIsNot(excsnap, None) + @requires_subinterpreters + def test_pyinit_function_raises_exception(self): + # gh-144601: PyInit functions that raised exceptions would cause a + # crash when imported from a subinterpreter. + import _testsinglephase + filename = _testsinglephase.__file__ + script = f"""if True: + from test.test_import import import_extension_from_file + + import_extension_from_file('_testsinglephase_raise_exception', {filename!r})""" + + interp = _interpreters.create() + try: + with catch_unraisable_exception() as cm: + exception = _interpreters.run_string(interp, script) + unraisable = cm.unraisable + finally: + _interpreters.destroy(interp) + + self.assertIsNotNone(exception) + self.assertIsNotNone(exception.type.__name__, "ImportError") + self.assertIsNotNone(exception.msg, "failed to import from subinterpreter due to exception") + self.assertIsNotNone(unraisable) + self.assertIs(unraisable.exc_type, RuntimeError) + self.assertEqual(str(unraisable.exc_value), "evil") + class TestSinglePhaseSnapshot(ModuleSnapshot): """A representation of a single-phase init module for testing. @@ -2743,9 +2765,6 @@ def _load_dynamic(self, name, path): # This is essentially copied from the old imp module. from importlib._bootstrap import _load loader = self.LOADER(name, path) - - # Issue bpo-24748: Skip the sys.modules check in _load_module_shim; - # always load new extension. spec = importlib.util.spec_from_file_location(name, path, loader=loader) return _load(spec) @@ -3159,6 +3178,7 @@ def test_check_state_first(self): # Also, we test with a single-phase module that has global state, # which is shared by all interpreters. + @no_rerun(reason="module state is not cleared (see gh-140657)") @requires_subinterpreters def test_basic_multiple_interpreters_main_no_reset(self): # without resetting; already loaded in main interpreter @@ -3227,6 +3247,7 @@ def test_basic_multiple_interpreters_main_no_reset(self): # * m_copy was copied from interp2 (was from interp1) # * module's global state was updated, not reset + @unittest.skip("gh-131229: This is suddenly very flaky") @no_rerun(reason="rerun not possible; module state is never cleared (see gh-102251)") @requires_subinterpreters def test_basic_multiple_interpreters_deleted_no_reset(self): @@ -3362,6 +3383,158 @@ def test_basic_multiple_interpreters_reset_each(self): # * module's global state was initialized, not reset +@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") +class ModexportTests(unittest.TestCase): + def test_from_modexport(self): + modname = '_test_from_modexport' + filename = _testmultiphase.__file__ + module = import_extension_from_file(modname, filename, + put_in_sys_modules=False) + + self.assertEqual(module.__name__, modname) + + @requires_subinterpreters + def test_from_modexport_gil_used(self): + # Test that a module with Py_MOD_GIL_USED (re-)enables the GIL. + # Do this in a new interpreter to avoid interfering with global state. + modname = '_test_from_modexport_gil_used' + filename = _testmultiphase.__file__ + interp = concurrent.interpreters.create() + self.addCleanup(interp.close) + queue = concurrent.interpreters.create_queue() + interp.prepare_main( + modname=modname, + filename=filename, + queue=queue, + ) + enabled_before = sys._is_gil_enabled() + interp.exec(f"""if True: + import sys + from test.support.warnings_helper import check_warnings + from {__name__} import import_extension_from_file + with check_warnings((".*GIL..has been enabled.*", RuntimeWarning), + quiet=True): + module = import_extension_from_file(modname, filename, + put_in_sys_modules=False) + queue.put(module.__name__) + queue.put(sys._is_gil_enabled()) + """) + + self.assertEqual(queue.get(), modname) + self.assertEqual(queue.get(), True) + self.assertTrue(queue.empty()) + + self.assertEqual(enabled_before, sys._is_gil_enabled()) + + def test_from_modexport_null(self): + modname = '_test_from_modexport_null' + filename = _testmultiphase.__file__ + with self.assertRaises(SystemError): + import_extension_from_file(modname, filename, + put_in_sys_modules=False) + + def test_from_modexport_exception(self): + modname = '_test_from_modexport_exception' + filename = _testmultiphase.__file__ + with self.assertRaises(ValueError): + import_extension_from_file(modname, filename, + put_in_sys_modules=False) + + def test_from_modexport_create_nonmodule(self): + modname = '_test_from_modexport_create_nonmodule' + filename = _testmultiphase.__file__ + module = import_extension_from_file(modname, filename, + put_in_sys_modules=False) + self.assertIsInstance(module, str) + + @requires_subinterpreters + def test_from_modexport_create_nonmodule_gil_used(self): + # Test that a module with Py_MOD_GIL_USED (re-)enables the GIL. + # Do this in a new interpreter to avoid interfering with global state. + modname = '_test_from_modexport_create_nonmodule_gil_used' + filename = _testmultiphase.__file__ + interp = concurrent.interpreters.create() + self.addCleanup(interp.close) + queue = concurrent.interpreters.create_queue() + interp.prepare_main( + modname=modname, + filename=filename, + queue=queue, + ) + enabled_before = sys._is_gil_enabled() + interp.exec(f"""if True: + import sys + from test.support.warnings_helper import check_warnings + from {__name__} import import_extension_from_file + with check_warnings((".*GIL..has been enabled.*", RuntimeWarning), + quiet=True): + module = import_extension_from_file(modname, filename, + put_in_sys_modules=False) + queue.put(module) + queue.put(sys._is_gil_enabled()) + """) + + self.assertIsInstance(queue.get(), str) + self.assertEqual(queue.get(), True) + self.assertTrue(queue.empty()) + + self.assertEqual(enabled_before, sys._is_gil_enabled()) + + def test_from_modexport_smoke(self): + # General positive test for sundry features + # (PyModule_FromSlotsAndSpec tests exercise these more carefully) + modname = '_test_from_modexport_smoke' + filename = _testmultiphase.__file__ + module = import_extension_from_file(modname, filename, + put_in_sys_modules=False) + self.assertEqual(module.__doc__, "the expected docstring") + self.assertEqual(module.number, 147) + self.assertEqual(module.get_state_int(), 258) + self.assertGreater(module.get_test_token(), 0) + + def test_from_modexport_smoke_token(self): + _testcapi = import_module("_testcapi") + + modname = '_test_from_modexport_smoke' + filename = _testmultiphase.__file__ + module = import_extension_from_file(modname, filename, + put_in_sys_modules=False) + token = module.get_test_token() + self.assertEqual(_testcapi.pymodule_get_token(module), token) + + tp = module.Example + self.assertEqual(_testcapi.pytype_getmodulebytoken(tp, token), module) + class Sub(tp): + pass + self.assertEqual(_testcapi.pytype_getmodulebytoken(Sub, token), module) + + def test_from_modexport_empty_slots(self): + # Module to test that Py_mod_abi is mandatory for PyModExport + modname = '_test_from_modexport_empty_slots' + filename = _testmultiphase.__file__ + with self.assertRaises(SystemError): + import_extension_from_file( + modname, filename, put_in_sys_modules=False) + + @requires_gil_enabled("this module re-enables GIL") + def test_from_modexport_minimal_slots(self): + # Module to test that: + # - no slots except Py_mod_abi is mandatory for PyModExport + # - the slots array is used as the default token + modname = '_test_from_modexport_minimal_slots' + filename = _testmultiphase.__file__ + module = import_extension_from_file( + modname, filename, put_in_sys_modules=False) + + self.assertEqual(module.__name__, modname) + self.assertEqual(module.__doc__, None) + + _testcapi = import_module("_testcapi") + smoke_mod = import_extension_from_file( + '_test_from_modexport_smoke', filename, put_in_sys_modules=False) + self.assertEqual(_testcapi.pymodule_get_token(module), + smoke_mod.get_modexport_minimal_slots()) + @cpython_only class TestMagicNumber(unittest.TestCase): def test_magic_number_endianness(self): diff --git a/Lib/test/test_import/data/syntax_warnings.py b/Lib/test/test_import/data/syntax_warnings.py new file mode 100644 index 00000000000000..103f07b6187603 --- /dev/null +++ b/Lib/test/test_import/data/syntax_warnings.py @@ -0,0 +1,21 @@ +# Syntax warnings emitted in different parts of the Python compiler. + +# Parser/lexer/lexer.c +x = 1or 0 # line 4 + +# Parser/tokenizer/helpers.c +'\z' # line 7 + +# Parser/string_parser.c +'\400' # line 10 + +# _PyCompile_Warn() in Python/codegen.c +assert(x, 'message') # line 13 +x is 1 # line 14 + +# _PyErr_EmitSyntaxWarning() in Python/ast_preprocess.c +def f(): + try: + pass + finally: + return 42 # line 21 diff --git a/Lib/test/test_import/data/unwritable/__init__.py b/Lib/test/test_import/data/unwritable/__init__.py index da4ddb3d027c34..1d61ff348d8d15 100644 --- a/Lib/test/test_import/data/unwritable/__init__.py +++ b/Lib/test/test_import/data/unwritable/__init__.py @@ -1,9 +1,9 @@ import sys class MyMod(object): - __slots__ = ['__builtins__', '__cached__', '__doc__', - '__file__', '__loader__', '__name__', - '__package__', '__path__', '__spec__'] + __slots__ = ['__builtins__', '__doc__', '__file__', + '__loader__', '__name__', '__package__', + '__path__', '__spec__'] def __init__(self): for attr in self.__slots__: setattr(self, attr, globals()[attr]) diff --git a/Lib/test/test_importlib/abc.py b/Lib/test/test_importlib/abc.py index 5d4b958767633e..0da98e0c99e39e 100644 --- a/Lib/test/test_importlib/abc.py +++ b/Lib/test/test_importlib/abc.py @@ -40,22 +40,6 @@ def test_failure(self): class LoaderTests(metaclass=abc.ABCMeta): - @abc.abstractmethod - def test_module(self): - """A module should load without issue. - - After the loader returns the module should be in sys.modules. - - Attributes to verify: - - * __file__ - * __loader__ - * __name__ - * No __path__ - - """ - pass - @abc.abstractmethod def test_package(self): """Loading a package should work. diff --git a/Lib/test/test_importlib/builtin/test_loader.py b/Lib/test/test_importlib/builtin/test_loader.py index 7e9d1b1960fdd7..2a5c2a8f39ee13 100644 --- a/Lib/test/test_importlib/builtin/test_loader.py +++ b/Lib/test/test_importlib/builtin/test_loader.py @@ -7,70 +7,6 @@ import unittest import warnings -@unittest.skipIf(util.BUILTINS.good_name is None, 'no reasonable builtin module') -class LoaderTests(abc.LoaderTests): - - """Test load_module() for built-in modules.""" - - def setUp(self): - self.verification = {'__name__': 'errno', '__package__': '', - '__loader__': self.machinery.BuiltinImporter} - - def verify(self, module): - """Verify that the module matches against what it should have.""" - self.assertIsInstance(module, types.ModuleType) - for attr, value in self.verification.items(): - self.assertEqual(getattr(module, attr), value) - self.assertIn(module.__name__, sys.modules) - - def load_module(self, name): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - return self.machinery.BuiltinImporter.load_module(name) - - def test_module(self): - # Common case. - with util.uncache(util.BUILTINS.good_name): - module = self.load_module(util.BUILTINS.good_name) - self.verify(module) - - # Built-in modules cannot be a package. - test_package = test_lacking_parent = None - - # No way to force an import failure. - test_state_after_failure = None - - def test_module_reuse(self): - # Test that the same module is used in a reload. - with util.uncache(util.BUILTINS.good_name): - module1 = self.load_module(util.BUILTINS.good_name) - module2 = self.load_module(util.BUILTINS.good_name) - self.assertIs(module1, module2) - - def test_unloadable(self): - name = 'dssdsdfff' - assert name not in sys.builtin_module_names - with self.assertRaises(ImportError) as cm: - self.load_module(name) - self.assertEqual(cm.exception.name, name) - - def test_already_imported(self): - # Using the name of a module already imported but not a built-in should - # still fail. - module_name = 'builtin_reload_test' - assert module_name not in sys.builtin_module_names - with util.uncache(module_name): - module = types.ModuleType(module_name) - sys.modules[module_name] = module - with self.assertRaises(ImportError) as cm: - self.load_module(module_name) - self.assertEqual(cm.exception.name, module_name) - - -(Frozen_LoaderTests, - Source_LoaderTests - ) = util.test_both(LoaderTests, machinery=machinery) - @unittest.skipIf(util.BUILTINS.good_name is None, 'no reasonable builtin module') class InspectLoaderTests: diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py index cdc8884d668a66..dc77fa78a203fd 100644 --- a/Lib/test/test_importlib/extension/test_finder.py +++ b/Lib/test/test_importlib/extension/test_finder.py @@ -1,4 +1,4 @@ -from test.support import is_apple_mobile +from test.support import is_apple_mobile, Py_GIL_DISABLED from test.test_importlib import abc, util machinery = util.import_importlib('importlib.machinery') @@ -59,6 +59,20 @@ def test_module(self): def test_failure(self): self.assertIsNone(self.find_spec('asdfjkl;')) + def test_abi3_extension_suffixes(self): + suffixes = self.machinery.EXTENSION_SUFFIXES + if 'win32' in sys.platform: + # Either "_d.pyd" or ".pyd" must be in suffixes + self.assertTrue({"_d.pyd", ".pyd"}.intersection(suffixes)) + elif 'cygwin' in sys.platform: + pass + else: + if Py_GIL_DISABLED: + self.assertNotIn(".abi3.so", suffixes) + else: + self.assertIn(".abi3.so", suffixes) + self.assertIn(".abi3t.so", suffixes) + (Frozen_FinderTests, Source_FinderTests diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py index 0dd21e079eba22..15d5a7d4752cc8 100644 --- a/Lib/test/test_importlib/extension/test_loader.py +++ b/Lib/test/test_importlib/extension/test_loader.py @@ -35,11 +35,6 @@ def setUp(self): self.loader = self.LoaderClass(util.EXTENSIONS.name, util.EXTENSIONS.file_path) - def load_module(self, fullname): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - return self.loader.load_module(fullname) - def test_equality(self): other = self.LoaderClass(util.EXTENSIONS.name, util.EXTENSIONS.file_path) self.assertEqual(self.loader, other) @@ -48,25 +43,6 @@ def test_inequality(self): other = self.LoaderClass('_' + util.EXTENSIONS.name, util.EXTENSIONS.file_path) self.assertNotEqual(self.loader, other) - def test_load_module_API(self): - # Test the default argument for load_module(). - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self.loader.load_module() - self.loader.load_module(None) - with self.assertRaises(ImportError): - self.load_module('XXX') - - def test_module(self): - with util.uncache(util.EXTENSIONS.name): - module = self.load_module(util.EXTENSIONS.name) - for attr, value in [('__name__', util.EXTENSIONS.name), - ('__file__', util.EXTENSIONS.file_path), - ('__package__', '')]: - self.assertEqual(getattr(module, attr), value) - self.assertIn(util.EXTENSIONS.name, sys.modules) - self.assertIsInstance(module.__loader__, self.LoaderClass) - # No extension module as __init__ available for testing. test_package = None @@ -76,18 +52,6 @@ def test_module(self): # No easy way to trigger a failure after a successful import. test_state_after_failure = None - def test_unloadable(self): - name = 'asdfjkl;' - with self.assertRaises(ImportError) as cm: - self.load_module(name) - self.assertEqual(cm.exception.name, name) - - def test_module_reuse(self): - with util.uncache(util.EXTENSIONS.name): - module1 = self.load_module(util.EXTENSIONS.name) - module2 = self.load_module(util.EXTENSIONS.name) - self.assertIs(module1, module2) - def test_is_package(self): self.assertFalse(self.loader.is_package(util.EXTENSIONS.name)) for suffix in self.machinery.EXTENSION_SUFFIXES: @@ -126,11 +90,6 @@ def setUp(self): self.loader = self.LoaderClass(self.name, self.spec.origin) - def load_module(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - return self.loader.load_module(self.name) - def load_module_by_name(self, fullname): # Load a module from the test extension by name. origin = self.spec.origin @@ -140,19 +99,6 @@ def load_module_by_name(self, fullname): loader.exec_module(module) return module - def test_module(self): - # Test loading an extension module. - with util.uncache(self.name): - module = self.load_module() - for attr, value in [('__name__', self.name), - ('__file__', self.spec.origin), - ('__package__', '')]: - self.assertEqual(getattr(module, attr), value) - with self.assertRaises(AttributeError): - module.__path__ - self.assertIs(module, sys.modules[self.name]) - self.assertIsInstance(module.__loader__, self.LoaderClass) - # No extension module as __init__ available for testing. test_package = None @@ -213,12 +159,6 @@ def setUp(self): assert self.spec self.loader = self.LoaderClass(self.name, self.spec.origin) - def load_module(self): - # Load the module from the test extension. - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - return self.loader.load_module(self.name) - def load_module_by_name(self, fullname): # Load a module from the test extension by name. origin = self.spec.origin @@ -237,60 +177,6 @@ def load_module_by_name(self, fullname): # Handling failure on reload is the up to the module. test_state_after_failure = None - def test_module(self): - # Test loading an extension module. - with util.uncache(self.name): - module = self.load_module() - for attr, value in [('__name__', self.name), - ('__file__', self.spec.origin), - ('__package__', '')]: - self.assertEqual(getattr(module, attr), value) - with self.assertRaises(AttributeError): - module.__path__ - self.assertIs(module, sys.modules[self.name]) - self.assertIsInstance(module.__loader__, self.LoaderClass) - - def test_functionality(self): - # Test basic functionality of stuff defined in an extension module. - with util.uncache(self.name): - module = self.load_module() - self.assertIsInstance(module, types.ModuleType) - ex = module.Example() - self.assertEqual(ex.demo('abcd'), 'abcd') - self.assertEqual(ex.demo(), None) - with self.assertRaises(AttributeError): - ex.abc - ex.abc = 0 - self.assertEqual(ex.abc, 0) - self.assertEqual(module.foo(9, 9), 18) - self.assertIsInstance(module.Str(), str) - self.assertEqual(module.Str(1) + '23', '123') - with self.assertRaises(module.error): - raise module.error() - self.assertEqual(module.int_const, 1969) - self.assertEqual(module.str_const, 'something different') - - def test_reload(self): - # Test that reload didn't re-set the module's attributes. - with util.uncache(self.name): - module = self.load_module() - ex_class = module.Example - importlib.reload(module) - self.assertIs(ex_class, module.Example) - - def test_try_registration(self): - # Assert that the PyState_{Find,Add,Remove}Module C API doesn't work. - with util.uncache(self.name): - module = self.load_module() - with self.subTest('PyState_FindModule'): - self.assertEqual(module.call_state_registration_func(0), None) - with self.subTest('PyState_AddModule'): - with self.assertRaises(SystemError): - module.call_state_registration_func(1) - with self.subTest('PyState_RemoveModule'): - with self.assertRaises(SystemError): - module.call_state_registration_func(2) - def test_load_submodule(self): # Test loading a simulated submodule. module = self.load_module_by_name('pkg.' + self.name) diff --git a/Lib/test/test_importlib/import_/test_api.py b/Lib/test/test_importlib/import_/test_api.py index d6ad590b3d46a0..ea1e29a4e5f278 100644 --- a/Lib/test/test_importlib/import_/test_api.py +++ b/Lib/test/test_importlib/import_/test_api.py @@ -27,13 +27,6 @@ def exec_module(module): raise ImportError('I cannot be loaded!') -class BadLoaderFinder: - @classmethod - def load_module(cls, fullname): - if fullname == SUBMOD_NAME: - raise ImportError('I cannot be loaded!') - - class APITest: """Test API-specific details for __import__ (e.g. raising the right @@ -93,45 +86,6 @@ def test_blocked_fromlist(self): self.assertEqual(cm.exception.name, SUBMOD_NAME) -class OldAPITests(APITest): - bad_finder_loader = BadLoaderFinder - - def test_raises_ModuleNotFoundError(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - super().test_raises_ModuleNotFoundError() - - def test_name_requires_rparition(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - super().test_name_requires_rparition() - - def test_negative_level(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - super().test_negative_level() - - def test_nonexistent_fromlist_entry(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - super().test_nonexistent_fromlist_entry() - - def test_fromlist_load_error_propagates(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - super().test_fromlist_load_error_propagates - - def test_blocked_fromlist(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - super().test_blocked_fromlist() - - -(Frozen_OldAPITests, - Source_OldAPITests - ) = util.test_both(OldAPITests, __import__=util.__import__) - - class SpecAPITests(APITest): bad_finder_loader = BadSpecFinderLoader diff --git a/Lib/test/test_importlib/import_/test_helpers.py b/Lib/test/test_importlib/import_/test_helpers.py index 550f88d1d7a651..7587276a41e953 100644 --- a/Lib/test/test_importlib/import_/test_helpers.py +++ b/Lib/test/test_importlib/import_/test_helpers.py @@ -19,8 +19,7 @@ def test_no_loader_but_spec(self): ns = {"__spec__": spec} _bootstrap_external._fix_up_module(ns, name, path) - expected = {"__spec__": spec, "__loader__": loader, "__file__": path, - "__cached__": None} + expected = {"__spec__": spec, "__loader__": loader, "__file__": path} self.assertEqual(ns, expected) def test_no_loader_no_spec_but_sourceless(self): @@ -29,7 +28,7 @@ def test_no_loader_no_spec_but_sourceless(self): ns = {} _bootstrap_external._fix_up_module(ns, name, path, path) - expected = {"__file__": path, "__cached__": path} + expected = {"__file__": path} for key, val in expected.items(): with self.subTest(f"{key}: {val}"): @@ -51,7 +50,7 @@ def test_no_loader_no_spec_but_source(self): ns = {} _bootstrap_external._fix_up_module(ns, name, path) - expected = {"__file__": path, "__cached__": None} + expected = {"__file__": path} for key, val in expected.items(): with self.subTest(f"{key}: {val}"): diff --git a/Lib/test/test_importlib/metadata/_path.py b/Lib/test/test_importlib/metadata/_path.py index b3cfb9cd549d6c..e63d889f96bf13 100644 --- a/Lib/test/test_importlib/metadata/_path.py +++ b/Lib/test/test_importlib/metadata/_path.py @@ -1,9 +1,14 @@ -# from jaraco.path 3.7 +# from jaraco.path 3.7.2 + +from __future__ import annotations import functools import pathlib -from typing import Dict, Protocol, Union -from typing import runtime_checkable +from collections.abc import Mapping +from typing import TYPE_CHECKING, Protocol, Union, runtime_checkable + +if TYPE_CHECKING: + from typing_extensions import Self class Symlink(str): @@ -12,29 +17,25 @@ class Symlink(str): """ -FilesSpec = Dict[str, Union[str, bytes, Symlink, 'FilesSpec']] # type: ignore +FilesSpec = Mapping[str, Union[str, bytes, Symlink, 'FilesSpec']] @runtime_checkable class TreeMaker(Protocol): - def __truediv__(self, *args, **kwargs): ... # pragma: no cover - - def mkdir(self, **kwargs): ... # pragma: no cover - - def write_text(self, content, **kwargs): ... # pragma: no cover - - def write_bytes(self, content): ... # pragma: no cover - - def symlink_to(self, target): ... # pragma: no cover + def __truediv__(self, other, /) -> Self: ... + def mkdir(self, *, exist_ok) -> object: ... + def write_text(self, content, /, *, encoding) -> object: ... + def write_bytes(self, content, /) -> object: ... + def symlink_to(self, target, /) -> object: ... -def _ensure_tree_maker(obj: Union[str, TreeMaker]) -> TreeMaker: - return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj) # type: ignore +def _ensure_tree_maker(obj: str | TreeMaker) -> TreeMaker: + return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj) def build( spec: FilesSpec, - prefix: Union[str, TreeMaker] = pathlib.Path(), # type: ignore + prefix: str | TreeMaker = pathlib.Path(), ): """ Build a set of files/directories, as described by the spec. @@ -66,23 +67,24 @@ def build( @functools.singledispatch -def create(content: Union[str, bytes, FilesSpec], path): +def create(content: str | bytes | FilesSpec, path: TreeMaker) -> None: path.mkdir(exist_ok=True) - build(content, prefix=path) # type: ignore + # Mypy only looks at the signature of the main singledispatch method. So it must contain the complete Union + build(content, prefix=path) # type: ignore[arg-type] # python/mypy#11727 @create.register -def _(content: bytes, path): +def _(content: bytes, path: TreeMaker) -> None: path.write_bytes(content) @create.register -def _(content: str, path): +def _(content: str, path: TreeMaker) -> None: path.write_text(content, encoding='utf-8') @create.register -def _(content: Symlink, path): +def _(content: Symlink, path: TreeMaker) -> None: path.symlink_to(content) diff --git a/Lib/test/test_importlib/metadata/fixtures.py b/Lib/test/test_importlib/metadata/fixtures.py index 826b1b3259b4cd..3283697d418188 100644 --- a/Lib/test/test_importlib/metadata/fixtures.py +++ b/Lib/test/test_importlib/metadata/fixtures.py @@ -1,11 +1,12 @@ -import sys +import contextlib import copy +import functools import json -import shutil import pathlib +import shutil +import sys import textwrap -import functools -import contextlib +from importlib import resources from test.support import import_helper from test.support import os_helper @@ -15,15 +16,6 @@ from ._path import FilesSpec -try: - from importlib import resources # type: ignore - - getattr(resources, 'files') - getattr(resources, 'as_file') -except (ImportError, AttributeError): - import importlib_resources as resources # type: ignore - - @contextlib.contextmanager def tmp_path(): """ diff --git a/Lib/test/test_importlib/metadata/test_api.py b/Lib/test/test_importlib/metadata/test_api.py index 2256e0c502e46f..5449f0484492fb 100644 --- a/Lib/test/test_importlib/metadata/test_api.py +++ b/Lib/test/test_importlib/metadata/test_api.py @@ -1,14 +1,12 @@ +import importlib import re import textwrap import unittest -import warnings -import importlib -import contextlib -from . import fixtures from importlib.metadata import ( Distribution, PackageNotFoundError, + Prepared, distribution, entry_points, files, @@ -17,12 +15,7 @@ version, ) - -@contextlib.contextmanager -def suppress_known_deprecation(): - with warnings.catch_warnings(record=True) as ctx: - warnings.simplefilter('default', category=DeprecationWarning) - yield ctx +from . import fixtures class APITests( @@ -153,13 +146,13 @@ def test_metadata_for_this_package(self): classifiers = md.get_all('Classifier') assert 'Topic :: Software Development :: Libraries' in classifiers - def test_missing_key_legacy(self): + def test_missing_key(self): """ - Requesting a missing key will still return None, but warn. + Requesting a missing key raises KeyError. """ md = metadata('distinfo-pkg') - with suppress_known_deprecation(): - assert md['does-not-exist'] is None + with self.assertRaises(KeyError): + md['does-not-exist'] def test_get_key(self): """ @@ -321,3 +314,34 @@ class InvalidateCache(unittest.TestCase): def test_invalidate_cache(self): # No externally observable behavior, but ensures test coverage... importlib.invalidate_caches() + + +class PreparedTests(unittest.TestCase): + @fixtures.parameterize( + # Simple + dict(input='sample', expected='sample'), + # Mixed case + dict(input='Sample', expected='sample'), + dict(input='SAMPLE', expected='sample'), + dict(input='SaMpLe', expected='sample'), + # Separator conversions + dict(input='sample-pkg', expected='sample_pkg'), + dict(input='sample.pkg', expected='sample_pkg'), + dict(input='sample_pkg', expected='sample_pkg'), + # Multiple separators + dict(input='sample---pkg', expected='sample_pkg'), + dict(input='sample___pkg', expected='sample_pkg'), + dict(input='sample...pkg', expected='sample_pkg'), + # Mixed separators + dict(input='sample-._pkg', expected='sample_pkg'), + dict(input='sample_.-pkg', expected='sample_pkg'), + # Complex + dict(input='Sample__Pkg-name.foo', expected='sample_pkg_name_foo'), + dict(input='Sample__Pkg.name__foo', expected='sample_pkg_name_foo'), + # Uppercase with separators + dict(input='SAMPLE-PKG', expected='sample_pkg'), + dict(input='Sample.Pkg', expected='sample_pkg'), + dict(input='SAMPLE_PKG', expected='sample_pkg'), + ) + def test_normalize(self, input, expected): + self.assertEqual(Prepared.normalize(input), expected) diff --git a/Lib/test/test_importlib/metadata/test_main.py b/Lib/test/test_importlib/metadata/test_main.py index e4218076f8cb0e..aae052160d9763 100644 --- a/Lib/test/test_importlib/metadata/test_main.py +++ b/Lib/test/test_importlib/metadata/test_main.py @@ -1,23 +1,18 @@ -import re +import importlib import pickle +import re import unittest -import warnings -import importlib -import importlib.metadata -import contextlib -from test.support import os_helper try: import pyfakefs.fake_filesystem_unittest as ffs except ImportError: from .stubs import fake_filesystem_unittest as ffs +from test.support import os_helper -from . import fixtures -from ._context import suppress -from ._path import Symlink from importlib.metadata import ( Distribution, EntryPoint, + MetadataNotFound, PackageNotFoundError, _unique, distributions, @@ -27,12 +22,8 @@ version, ) - -@contextlib.contextmanager -def suppress_known_deprecation(): - with warnings.catch_warnings(record=True) as ctx: - warnings.simplefilter('default', category=DeprecationWarning) - yield ctx +from . import fixtures +from ._path import Symlink class BasicTests(fixtures.DistInfoPkg, unittest.TestCase): @@ -59,9 +50,6 @@ def test_package_not_found_mentions_metadata(self): assert "metadata" in str(ctx.exception) - # expected to fail until ABC is enforced - @suppress(AssertionError) - @suppress_known_deprecation() def test_abc_enforced(self): with self.assertRaises(TypeError): type('DistributionSubclass', (Distribution,), {})() @@ -146,6 +134,43 @@ def test_unique_distributions(self): assert len(after) == len(before) +class InvalidMetadataTests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): + @staticmethod + def make_pkg(name, files=dict(METADATA="VERSION: 1.0")): + """ + Create metadata for a dist-info package with name and files. + """ + return { + f'{name}.dist-info': files, + } + + def test_valid_dists_preferred(self): + """ + Dists with metadata should be preferred when discovered by name. + + Ref python/importlib_metadata#489. + """ + # create three dists with the valid one in the middle (lexicographically) + # such that on most file systems, the valid one is never naturally first. + fixtures.build_files(self.make_pkg('foo-4.0', files={}), self.site_dir) + fixtures.build_files(self.make_pkg('foo-4.1'), self.site_dir) + fixtures.build_files(self.make_pkg('foo-4.2', files={}), self.site_dir) + dist = Distribution.from_name('foo') + assert dist.version == "1.0" + + def test_missing_metadata(self): + """ + Dists with a missing metadata file should raise ``MetadataNotFound``. + + Ref python/importlib_metadata#493 and python/cpython#143387. + """ + fixtures.build_files(self.make_pkg('foo-4.3', files={}), self.site_dir) + with self.assertRaises(MetadataNotFound): + Distribution.from_name('foo').metadata + with self.assertRaises(MetadataNotFound): + metadata('foo') + + class NonASCIITests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): @staticmethod def pkg_with_non_ascii_description(site_dir): diff --git a/Lib/test/test_importlib/metadata/test_zip.py b/Lib/test/test_importlib/metadata/test_zip.py index 276f6288c91598..97168549667de3 100644 --- a/Lib/test/test_importlib/metadata/test_zip.py +++ b/Lib/test/test_importlib/metadata/test_zip.py @@ -1,8 +1,12 @@ +import multiprocessing +import os import sys import unittest -from . import fixtures +from test.support import requires_fork, warnings_helper + from importlib.metadata import ( + FastPath, PackageNotFoundError, distribution, distributions, @@ -11,6 +15,8 @@ version, ) +from . import fixtures + class TestZip(fixtures.ZipFixtures, unittest.TestCase): def setUp(self): @@ -46,6 +52,39 @@ def test_one_distribution(self): dists = list(distributions(path=sys.path[:1])) assert len(dists) == 1 + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() + @requires_fork() + @unittest.skipUnless( + hasattr(os, 'register_at_fork') + and 'fork' in multiprocessing.get_all_start_methods(), + 'requires fork-based multiprocessing support', + ) + def test_fastpath_cache_cleared_in_forked_child(self): + zip_path = sys.path[0] + + FastPath(zip_path) + assert FastPath.__new__.cache_info().currsize >= 1 + + ctx = multiprocessing.get_context('fork') + parent_conn, child_conn = ctx.Pipe() + + def child(conn, root): + try: + before = FastPath.__new__.cache_info().currsize + FastPath(root) + after = FastPath.__new__.cache_info().currsize + conn.send((before, after)) + finally: + conn.close() + + proc = ctx.Process(target=child, args=(child_conn, zip_path)) + proc.start() + child_conn.close() + cache_sizes = parent_conn.recv() + proc.join() + + self.assertEqual(cache_sizes, (0, 1)) + class TestEgg(TestZip): def setUp(self): diff --git a/Lib/test/test_importlib/namespace_pkgs/foo/README.md b/Lib/test/test_importlib/namespace_pkgs/foo/README.md new file mode 100644 index 00000000000000..9f6bc74941d0ce --- /dev/null +++ b/Lib/test/test_importlib/namespace_pkgs/foo/README.md @@ -0,0 +1,2 @@ +This directory should not be a package, but should share a name with an +unrelated subpackage. diff --git a/Lib/test/test_importlib/resources/_path.py b/Lib/test/test_importlib/resources/_path.py index b144628cb73c77..3720af7c5085d7 100644 --- a/Lib/test/test_importlib/resources/_path.py +++ b/Lib/test/test_importlib/resources/_path.py @@ -1,10 +1,6 @@ -import pathlib import functools - -from typing import Dict, Union -from typing import runtime_checkable -from typing import Protocol - +import pathlib +from typing import Protocol, Union, runtime_checkable #### # from jaraco.path 3.7.1 @@ -16,7 +12,7 @@ class Symlink(str): """ -FilesSpec = Dict[str, Union[str, bytes, Symlink, 'FilesSpec']] +FilesSpec = dict[str, Union[str, bytes, Symlink, 'FilesSpec']] @runtime_checkable @@ -32,13 +28,13 @@ def write_bytes(self, content): ... # pragma: no cover def symlink_to(self, target): ... # pragma: no cover -def _ensure_tree_maker(obj: Union[str, TreeMaker]) -> TreeMaker: +def _ensure_tree_maker(obj: str | TreeMaker) -> TreeMaker: return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj) # type: ignore[return-value] def build( spec: FilesSpec, - prefix: Union[str, TreeMaker] = pathlib.Path(), # type: ignore[assignment] + prefix: str | TreeMaker = pathlib.Path(), # type: ignore[assignment] ): """ Build a set of files/directories, as described by the spec. @@ -70,7 +66,7 @@ def build( @functools.singledispatch -def create(content: Union[str, bytes, FilesSpec], path): +def create(content: str | bytes | FilesSpec, path): path.mkdir(exist_ok=True) build(content, prefix=path) # type: ignore[arg-type] diff --git a/Lib/test/test_importlib/resources/test_compatibilty_files.py b/Lib/test/test_importlib/resources/test_compatibilty_files.py index bcf608d9e2cbdf..2fd39bedf258d1 100644 --- a/Lib/test/test_importlib/resources/test_compatibilty_files.py +++ b/Lib/test/test_importlib/resources/test_compatibilty_files.py @@ -1,8 +1,6 @@ +import importlib.resources as resources import io import unittest - -from importlib import resources - from importlib.resources._adapters import ( CompatibilityFiles, wrap_spec, @@ -26,51 +24,46 @@ def files(self): return resources.files(self.package) def test_spec_path_iter(self): - self.assertEqual( - sorted(path.name for path in self.files.iterdir()), - ['a', 'b', 'c'], - ) + assert sorted(path.name for path in self.files.iterdir()) == ['a', 'b', 'c'] def test_child_path_iter(self): - self.assertEqual(list((self.files / 'a').iterdir()), []) + assert list((self.files / 'a').iterdir()) == [] def test_orphan_path_iter(self): - self.assertEqual(list((self.files / 'a' / 'a').iterdir()), []) - self.assertEqual(list((self.files / 'a' / 'a' / 'a').iterdir()), []) + assert list((self.files / 'a' / 'a').iterdir()) == [] + assert list((self.files / 'a' / 'a' / 'a').iterdir()) == [] def test_spec_path_is(self): - self.assertFalse(self.files.is_file()) - self.assertFalse(self.files.is_dir()) + assert not self.files.is_file() + assert not self.files.is_dir() def test_child_path_is(self): - self.assertTrue((self.files / 'a').is_file()) - self.assertFalse((self.files / 'a').is_dir()) + assert (self.files / 'a').is_file() + assert not (self.files / 'a').is_dir() def test_orphan_path_is(self): - self.assertFalse((self.files / 'a' / 'a').is_file()) - self.assertFalse((self.files / 'a' / 'a').is_dir()) - self.assertFalse((self.files / 'a' / 'a' / 'a').is_file()) - self.assertFalse((self.files / 'a' / 'a' / 'a').is_dir()) + assert not (self.files / 'a' / 'a').is_file() + assert not (self.files / 'a' / 'a').is_dir() + assert not (self.files / 'a' / 'a' / 'a').is_file() + assert not (self.files / 'a' / 'a' / 'a').is_dir() def test_spec_path_name(self): - self.assertEqual(self.files.name, 'testingpackage') + assert self.files.name == 'testingpackage' def test_child_path_name(self): - self.assertEqual((self.files / 'a').name, 'a') + assert (self.files / 'a').name == 'a' def test_orphan_path_name(self): - self.assertEqual((self.files / 'a' / 'b').name, 'b') - self.assertEqual((self.files / 'a' / 'b' / 'c').name, 'c') + assert (self.files / 'a' / 'b').name == 'b' + assert (self.files / 'a' / 'b' / 'c').name == 'c' def test_spec_path_open(self): - self.assertEqual(self.files.read_bytes(), b'Hello, world!') - self.assertEqual(self.files.read_text(encoding='utf-8'), 'Hello, world!') + assert self.files.read_bytes() == b'Hello, world!' + assert self.files.read_text(encoding='utf-8') == 'Hello, world!' def test_child_path_open(self): - self.assertEqual((self.files / 'a').read_bytes(), b'Hello, world!') - self.assertEqual( - (self.files / 'a').read_text(encoding='utf-8'), 'Hello, world!' - ) + assert (self.files / 'a').read_bytes() == b'Hello, world!' + assert (self.files / 'a').read_text(encoding='utf-8') == 'Hello, world!' def test_orphan_path_open(self): with self.assertRaises(FileNotFoundError): @@ -88,7 +81,7 @@ def test_orphan_path_invalid(self): def test_wrap_spec(self): spec = wrap_spec(self.package) - self.assertIsInstance(spec.loader.get_resource_reader(None), CompatibilityFiles) + assert isinstance(spec.loader.get_resource_reader(None), CompatibilityFiles) class CompatibilityFilesNoReaderTests(unittest.TestCase): @@ -101,4 +94,4 @@ def files(self): return resources.files(self.package) def test_spec_path_joinpath(self): - self.assertIsInstance(self.files / 'a', CompatibilityFiles.OrphanPath) + assert isinstance(self.files / 'a', CompatibilityFiles.OrphanPath) diff --git a/Lib/test/test_importlib/resources/test_contents.py b/Lib/test/test_importlib/resources/test_contents.py index 4e4e0e9c337f23..bdc158d85a239f 100644 --- a/Lib/test/test_importlib/resources/test_contents.py +++ b/Lib/test/test_importlib/resources/test_contents.py @@ -1,5 +1,5 @@ +import importlib.resources as resources import unittest -from importlib import resources from . import util diff --git a/Lib/test/test_importlib/resources/test_custom.py b/Lib/test/test_importlib/resources/test_custom.py index 640f90fc0dd91a..a7fc6bc35c5ece 100644 --- a/Lib/test/test_importlib/resources/test_custom.py +++ b/Lib/test/test_importlib/resources/test_custom.py @@ -1,12 +1,12 @@ -import unittest import contextlib +import importlib.resources as resources import pathlib +import unittest +from importlib.resources import abc +from importlib.resources.abc import ResourceReader, TraversableResources from test.support import os_helper -from importlib import resources -from importlib.resources import abc -from importlib.resources.abc import TraversableResources, ResourceReader from . import util diff --git a/Lib/test/test_importlib/resources/test_files.py b/Lib/test/test_importlib/resources/test_files.py index 3ce44999f98ee5..c922d32cedc307 100644 --- a/Lib/test/test_importlib/resources/test_files.py +++ b/Lib/test/test_importlib/resources/test_files.py @@ -1,15 +1,16 @@ +import contextlib +import importlib +import importlib.resources as resources import pathlib import py_compile import textwrap import unittest import warnings -import importlib -import contextlib - -from importlib import resources from importlib.resources.abc import Traversable + +from test.support import import_helper, os_helper + from . import util -from test.support import os_helper, import_helper @contextlib.contextmanager @@ -36,15 +37,7 @@ def test_traversable(self): def test_joinpath_with_multiple_args(self): files = resources.files(self.data) binfile = files.joinpath('subdirectory', 'binary.file') - self.assertTrue(binfile.is_file()) - - def test_old_parameter(self): - """ - Files used to take a 'package' parameter. Make sure anyone - passing by name is still supported. - """ - with suppress_known_deprecation(): - resources.files(package=self.data) + assert binfile.is_file() class OpenDiskTests(FilesTests, util.DiskSetup, unittest.TestCase): @@ -70,7 +63,7 @@ def test_non_paths_in_dunder_path(self): to cause the ``PathEntryFinder`` to be called when searching for packages. In that case, resources should still be loadable. """ - import namespacedata01 + import namespacedata01 # type: ignore[import-not-found] namespacedata01.__path__.append( '__editable__.sample_namespace-1.0.finder.__path_hook__' @@ -161,7 +154,9 @@ def _compile_importlib(self): sources = pathlib.Path(resources.__file__).parent for source_path in sources.glob('**/*.py'): - c_path = c_resources.joinpath(source_path.relative_to(sources)).with_suffix('.pyc') + c_path = c_resources.joinpath(source_path.relative_to(sources)).with_suffix( + '.pyc' + ) py_compile.compile(source_path, c_path) self.fixtures.enter_context(import_helper.DirsOnSysPath(bin_site)) diff --git a/Lib/test/test_importlib/resources/test_functional.py b/Lib/test/test_importlib/resources/test_functional.py index e8d25fa4d9faf0..9cec6af9a5d051 100644 --- a/Lib/test/test_importlib/resources/test_functional.py +++ b/Lib/test/test_importlib/resources/test_functional.py @@ -1,17 +1,12 @@ -import unittest -import os import importlib +import importlib.resources as resources +import os +import unittest from test.support import warnings_helper -from importlib import resources - from . import util -# Since the functional API forwards to Traversable, we only test -# filesystem resources here -- not zip files, namespace packages etc. -# We do test for two kinds of Anchor, though. - class StringAnchorMixin: anchor01 = 'data01' @@ -28,7 +23,7 @@ def anchor02(self): return importlib.import_module('data02') -class FunctionalAPIBase(util.DiskSetup): +class FunctionalAPIBase: def setUp(self): super().setUp() self.load_fixture('data02') @@ -43,83 +38,90 @@ def _gen_resourcetxt_path_parts(self): with self.subTest(path_parts=path_parts): yield path_parts + @staticmethod + def remove_utf16_bom(string): + """Remove an architecture-specific UTF-16 BOM prefix when present. + + Some platforms surface UTF-16 BOM bytes as escaped text when the + fixture is intentionally decoded as UTF-8 with ``errors='backslashreplace'``. + Strip that prefix so assertions validate content consistently.""" + for bom in ('\\xff\\xfe', '\\xfe\\xff', '\ufeff'): + if string.startswith(bom): + return string[len(bom) :] + return string + def test_read_text(self): - self.assertEqual( - resources.read_text(self.anchor01, 'utf-8.file'), - 'Hello, UTF-8 world!\n', + assert ( + resources.read_text(self.anchor01, 'utf-8.file') == 'Hello, UTF-8 world!\n' ) - self.assertEqual( + assert ( resources.read_text( self.anchor02, 'subdirectory', 'subsubdir', 'resource.txt', encoding='utf-8', - ), - 'a resource', + ) + == 'a resource' ) for path_parts in self._gen_resourcetxt_path_parts(): - self.assertEqual( + assert ( resources.read_text( self.anchor02, *path_parts, encoding='utf-8', - ), - 'a resource', + ) + == 'a resource' ) # Use generic OSError, since e.g. attempting to read a directory can # fail with PermissionError rather than IsADirectoryError with self.assertRaises(OSError): resources.read_text(self.anchor01) - with self.assertRaises(OSError): + with self.assertRaises((OSError, resources.abc.TraversalError)): resources.read_text(self.anchor01, 'no-such-file') with self.assertRaises(UnicodeDecodeError): resources.read_text(self.anchor01, 'utf-16.file') - self.assertEqual( + assert ( resources.read_text( self.anchor01, 'binary.file', encoding='latin1', - ), - '\x00\x01\x02\x03', + ) + == '\x00\x01\x02\x03' ) - self.assertEndsWith( # ignore the BOM + assert self.remove_utf16_bom( resources.read_text( self.anchor01, 'utf-16.file', errors='backslashreplace', ), - 'Hello, UTF-16 world!\n'.encode('utf-16-le').decode( - errors='backslashreplace', - ), + ) == 'Hello, UTF-16 world!\n'.encode('utf-16-le').decode( + errors='backslashreplace', ) def test_read_binary(self): - self.assertEqual( - resources.read_binary(self.anchor01, 'utf-8.file'), - b'Hello, UTF-8 world!\n', + assert ( + resources.read_binary(self.anchor01, 'utf-8.file') + == b'Hello, UTF-8 world!\n' ) for path_parts in self._gen_resourcetxt_path_parts(): - self.assertEqual( - resources.read_binary(self.anchor02, *path_parts), - b'a resource', - ) + assert resources.read_binary(self.anchor02, *path_parts) == b'a resource' def test_open_text(self): with resources.open_text(self.anchor01, 'utf-8.file') as f: - self.assertEqual(f.read(), 'Hello, UTF-8 world!\n') + assert f.read() == 'Hello, UTF-8 world!\n' for path_parts in self._gen_resourcetxt_path_parts(): with resources.open_text( self.anchor02, *path_parts, encoding='utf-8', ) as f: - self.assertEqual(f.read(), 'a resource') + assert f.read() == 'a resource' # Use generic OSError, since e.g. attempting to read a directory can # fail with PermissionError rather than IsADirectoryError with self.assertRaises(OSError): resources.open_text(self.anchor01) - with self.assertRaises(OSError): + with self.assertRaises((OSError, resources.abc.TraversalError)): resources.open_text(self.anchor01, 'no-such-file') with resources.open_text(self.anchor01, 'utf-16.file') as f: with self.assertRaises(UnicodeDecodeError): @@ -129,71 +131,70 @@ def test_open_text(self): 'binary.file', encoding='latin1', ) as f: - self.assertEqual(f.read(), '\x00\x01\x02\x03') + assert f.read() == '\x00\x01\x02\x03' with resources.open_text( self.anchor01, 'utf-16.file', errors='backslashreplace', ) as f: - self.assertEndsWith( # ignore the BOM - f.read(), - 'Hello, UTF-16 world!\n'.encode('utf-16-le').decode( - errors='backslashreplace', - ), + assert self.remove_utf16_bom(f.read()) == 'Hello, UTF-16 world!\n'.encode( + 'utf-16-le' + ).decode( + errors='backslashreplace', ) def test_open_binary(self): with resources.open_binary(self.anchor01, 'utf-8.file') as f: - self.assertEqual(f.read(), b'Hello, UTF-8 world!\n') + assert f.read() == b'Hello, UTF-8 world!\n' for path_parts in self._gen_resourcetxt_path_parts(): with resources.open_binary( self.anchor02, *path_parts, ) as f: - self.assertEqual(f.read(), b'a resource') + assert f.read() == b'a resource' def test_path(self): with resources.path(self.anchor01, 'utf-8.file') as path: with open(str(path), encoding='utf-8') as f: - self.assertEqual(f.read(), 'Hello, UTF-8 world!\n') + assert f.read() == 'Hello, UTF-8 world!\n' with resources.path(self.anchor01) as path: with open(os.path.join(path, 'utf-8.file'), encoding='utf-8') as f: - self.assertEqual(f.read(), 'Hello, UTF-8 world!\n') + assert f.read() == 'Hello, UTF-8 world!\n' def test_is_resource(self): is_resource = resources.is_resource - self.assertTrue(is_resource(self.anchor01, 'utf-8.file')) - self.assertFalse(is_resource(self.anchor01, 'no_such_file')) - self.assertFalse(is_resource(self.anchor01)) - self.assertFalse(is_resource(self.anchor01, 'subdirectory')) + assert is_resource(self.anchor01, 'utf-8.file') + assert not is_resource(self.anchor01, 'no_such_file') + assert not is_resource(self.anchor01) + assert not is_resource(self.anchor01, 'subdirectory') for path_parts in self._gen_resourcetxt_path_parts(): - self.assertTrue(is_resource(self.anchor02, *path_parts)) + assert is_resource(self.anchor02, *path_parts) def test_contents(self): with warnings_helper.check_warnings((".*contents.*", DeprecationWarning)): c = resources.contents(self.anchor01) - self.assertGreaterEqual( - set(c), - {'utf-8.file', 'utf-16.file', 'binary.file', 'subdirectory'}, - ) - with self.assertRaises(OSError), warnings_helper.check_warnings(( - ".*contents.*", - DeprecationWarning, - )): + assert set(c) >= {'utf-8.file', 'utf-16.file', 'binary.file', 'subdirectory'} + with ( + self.assertRaises(OSError), + warnings_helper.check_warnings(( + ".*contents.*", + DeprecationWarning, + )), + ): list(resources.contents(self.anchor01, 'utf-8.file')) for path_parts in self._gen_resourcetxt_path_parts(): - with self.assertRaises(OSError), warnings_helper.check_warnings(( - ".*contents.*", - DeprecationWarning, - )): + with ( + self.assertRaises((OSError, resources.abc.TraversalError)), + warnings_helper.check_warnings(( + ".*contents.*", + DeprecationWarning, + )), + ): list(resources.contents(self.anchor01, *path_parts)) with warnings_helper.check_warnings((".*contents.*", DeprecationWarning)): c = resources.contents(self.anchor01, 'subdirectory') - self.assertGreaterEqual( - set(c), - {'binary.file'}, - ) + assert set(c) >= {'binary.file'} @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_common_errors(self): @@ -233,17 +234,28 @@ def test_text_errors(self): ) -class FunctionalAPITest_StringAnchor( +class FunctionalAPITest_StringAnchor_Disk( StringAnchorMixin, FunctionalAPIBase, + util.DiskSetup, unittest.TestCase, ): pass -class FunctionalAPITest_ModuleAnchor( +class FunctionalAPITest_ModuleAnchor_Disk( ModuleAnchorMixin, FunctionalAPIBase, + util.DiskSetup, + unittest.TestCase, +): + pass + + +class FunctionalAPITest_StringAnchor_Memory( + StringAnchorMixin, + FunctionalAPIBase, + util.MemorySetup, unittest.TestCase, ): pass diff --git a/Lib/test/test_importlib/resources/test_open.py b/Lib/test/test_importlib/resources/test_open.py index 8c00378ad3cc9c..950f71db05a018 100644 --- a/Lib/test/test_importlib/resources/test_open.py +++ b/Lib/test/test_importlib/resources/test_open.py @@ -1,6 +1,6 @@ +import importlib.resources as resources import unittest -from importlib import resources from . import util @@ -23,19 +23,19 @@ def test_open_binary(self): target = resources.files(self.data) / 'binary.file' with target.open('rb') as fp: result = fp.read() - self.assertEqual(result, bytes(range(4))) + assert result == bytes(range(4)) def test_open_text_default_encoding(self): target = resources.files(self.data) / 'utf-8.file' with target.open(encoding='utf-8') as fp: result = fp.read() - self.assertEqual(result, 'Hello, UTF-8 world!\n') + assert result == 'Hello, UTF-8 world!\n' def test_open_text_given_encoding(self): target = resources.files(self.data) / 'utf-16.file' with target.open(encoding='utf-16', errors='strict') as fp: result = fp.read() - self.assertEqual(result, 'Hello, UTF-16 world!\n') + assert result == 'Hello, UTF-16 world!\n' def test_open_text_with_errors(self): """ @@ -46,11 +46,10 @@ def test_open_text_with_errors(self): self.assertRaises(UnicodeError, fp.read) with target.open(encoding='utf-8', errors='ignore') as fp: result = fp.read() - self.assertEqual( - result, + assert result == ( 'H\x00e\x00l\x00l\x00o\x00,\x00 ' '\x00U\x00T\x00F\x00-\x001\x006\x00 ' - '\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00', + '\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00' ) def test_open_binary_FileNotFoundError(self): diff --git a/Lib/test/test_importlib/resources/test_path.py b/Lib/test/test_importlib/resources/test_path.py index 903911f57b3306..162344e5d83749 100644 --- a/Lib/test/test_importlib/resources/test_path.py +++ b/Lib/test/test_importlib/resources/test_path.py @@ -1,8 +1,8 @@ +import importlib.resources as resources import io import pathlib import unittest -from importlib import resources from . import util @@ -19,9 +19,9 @@ def test_reading(self): """ target = resources.files(self.data) / 'utf-8.file' with resources.as_file(target) as path: - self.assertIsInstance(path, pathlib.Path) - self.assertEndsWith(path.name, "utf-8.file") - self.assertEqual('Hello, UTF-8 world!\n', path.read_text(encoding='utf-8')) + assert isinstance(path, pathlib.Path) + assert path.name.endswith("utf-8.file"), repr(path) + assert 'Hello, UTF-8 world!\n' == path.read_text(encoding='utf-8') class PathDiskTests(PathTests, util.DiskSetup, unittest.TestCase): diff --git a/Lib/test/test_importlib/resources/test_read.py b/Lib/test/test_importlib/resources/test_read.py index 59c237d964121e..4085a64b0eec57 100644 --- a/Lib/test/test_importlib/resources/test_read.py +++ b/Lib/test/test_importlib/resources/test_read.py @@ -1,6 +1,6 @@ +import importlib.resources as resources import unittest - -from importlib import import_module, resources +from importlib import import_module from . import util @@ -18,23 +18,25 @@ def execute(self, package, path): class ReadTests: def test_read_bytes(self): result = resources.files(self.data).joinpath('binary.file').read_bytes() - self.assertEqual(result, bytes(range(4))) + assert result == bytes(range(4)) def test_read_text_default_encoding(self): result = ( - resources.files(self.data) + resources + .files(self.data) .joinpath('utf-8.file') .read_text(encoding='utf-8') ) - self.assertEqual(result, 'Hello, UTF-8 world!\n') + assert result == 'Hello, UTF-8 world!\n' def test_read_text_given_encoding(self): result = ( - resources.files(self.data) + resources + .files(self.data) .joinpath('utf-16.file') .read_text(encoding='utf-16') ) - self.assertEqual(result, 'Hello, UTF-16 world!\n') + assert result == 'Hello, UTF-16 world!\n' def test_read_text_with_errors(self): """ @@ -43,11 +45,10 @@ def test_read_text_with_errors(self): target = resources.files(self.data) / 'utf-16.file' self.assertRaises(UnicodeError, target.read_text, encoding='utf-8') result = target.read_text(encoding='utf-8', errors='ignore') - self.assertEqual( - result, + assert result == ( 'H\x00e\x00l\x00l\x00o\x00,\x00 ' '\x00U\x00T\x00F\x00-\x001\x006\x00 ' - '\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00', + '\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00' ) @@ -59,13 +60,13 @@ class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase): def test_read_submodule_resource(self): submodule = import_module('data01.subdirectory') result = resources.files(submodule).joinpath('binary.file').read_bytes() - self.assertEqual(result, bytes(range(4, 8))) + assert result == bytes(range(4, 8)) def test_read_submodule_resource_by_name(self): result = ( resources.files('data01.subdirectory').joinpath('binary.file').read_bytes() ) - self.assertEqual(result, bytes(range(4, 8))) + assert result == bytes(range(4, 8)) class ReadNamespaceTests(ReadTests, util.DiskSetup, unittest.TestCase): @@ -78,15 +79,16 @@ class ReadNamespaceZipTests(ReadTests, util.ZipSetup, unittest.TestCase): def test_read_submodule_resource(self): submodule = import_module('namespacedata01.subdirectory') result = resources.files(submodule).joinpath('binary.file').read_bytes() - self.assertEqual(result, bytes(range(12, 16))) + assert result == bytes(range(12, 16)) def test_read_submodule_resource_by_name(self): result = ( - resources.files('namespacedata01.subdirectory') + resources + .files('namespacedata01.subdirectory') .joinpath('binary.file') .read_bytes() ) - self.assertEqual(result, bytes(range(12, 16))) + assert result == bytes(range(12, 16)) if __name__ == '__main__': diff --git a/Lib/test/test_importlib/resources/test_reader.py b/Lib/test/test_importlib/resources/test_reader.py index ed5693ab416798..691a78bb060b39 100644 --- a/Lib/test/test_importlib/resources/test_reader.py +++ b/Lib/test/test_importlib/resources/test_reader.py @@ -1,9 +1,8 @@ import os.path import pathlib import unittest - from importlib import import_module -from importlib.readers import MultiplexedPath, NamespaceReader +from importlib.resources.readers import MultiplexedPath, NamespaceReader from . import util @@ -31,9 +30,7 @@ def test_iterdir(self): contents.remove('__pycache__') except (KeyError, ValueError): pass - self.assertEqual( - contents, {'subdirectory', 'binary.file', 'utf-16.file', 'utf-8.file'} - ) + assert contents == {'subdirectory', 'binary.file', 'utf-16.file', 'utf-8.file'} def test_iterdir_duplicate(self): contents = { @@ -44,16 +41,19 @@ def test_iterdir_duplicate(self): contents.remove(remove) except (KeyError, ValueError): pass - self.assertEqual( - contents, - {'__init__.py', 'binary.file', 'subdirectory', 'utf-16.file', 'utf-8.file'}, - ) + assert contents == { + '__init__.py', + 'binary.file', + 'subdirectory', + 'utf-16.file', + 'utf-8.file', + } def test_is_dir(self): - self.assertEqual(MultiplexedPath(self.folder).is_dir(), True) + assert MultiplexedPath(self.folder).is_dir() def test_is_file(self): - self.assertEqual(MultiplexedPath(self.folder).is_file(), False) + assert not MultiplexedPath(self.folder).is_file() def test_open_file(self): path = MultiplexedPath(self.folder) @@ -67,19 +67,17 @@ def test_open_file(self): def test_join_path(self): prefix = str(self.folder.parent) path = MultiplexedPath(self.folder, self.data01) - self.assertEqual( - str(path.joinpath('binary.file'))[len(prefix) + 1 :], - os.path.join('namespacedata01', 'binary.file'), + assert str(path.joinpath('binary.file'))[len(prefix) + 1 :] == os.path.join( + 'namespacedata01', 'binary.file' ) sub = path.joinpath('subdirectory') assert isinstance(sub, MultiplexedPath) assert 'namespacedata01' in str(sub) assert 'data01' in str(sub) - self.assertEqual( - str(path.joinpath('imaginary'))[len(prefix) + 1 :], - os.path.join('namespacedata01', 'imaginary'), + assert str(path.joinpath('imaginary'))[len(prefix) + 1 :] == os.path.join( + 'namespacedata01', 'imaginary' ) - self.assertEqual(path.joinpath(), path) + assert path.joinpath() == path def test_join_path_compound(self): path = MultiplexedPath(self.folder) @@ -88,23 +86,16 @@ def test_join_path_compound(self): def test_join_path_common_subdir(self): prefix = str(self.data02.parent) path = MultiplexedPath(self.data01, self.data02) - self.assertIsInstance(path.joinpath('subdirectory'), MultiplexedPath) - self.assertEqual( - str(path.joinpath('subdirectory', 'subsubdir'))[len(prefix) + 1 :], - os.path.join('data02', 'subdirectory', 'subsubdir'), + assert isinstance(path.joinpath('subdirectory'), MultiplexedPath) + assert str(path.joinpath('subdirectory', 'subsubdir'))[len(prefix) + 1 :] == ( + os.path.join('data02', 'subdirectory', 'subsubdir') ) def test_repr(self): - self.assertEqual( - repr(MultiplexedPath(self.folder)), - f"MultiplexedPath('{self.folder}')", - ) + assert repr(MultiplexedPath(self.folder)) == f"MultiplexedPath('{self.folder}')" def test_name(self): - self.assertEqual( - MultiplexedPath(self.folder).name, - os.path.basename(self.folder), - ) + assert MultiplexedPath(self.folder).name == os.path.basename(self.folder) class NamespaceReaderTest(util.DiskSetup, unittest.TestCase): @@ -119,18 +110,14 @@ def test_resource_path(self): reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations) root = self.data.__path__[0] - self.assertEqual( - reader.resource_path('binary.file'), os.path.join(root, 'binary.file') - ) - self.assertEqual( - reader.resource_path('imaginary'), os.path.join(root, 'imaginary') - ) + assert reader.resource_path('binary.file') == os.path.join(root, 'binary.file') + assert reader.resource_path('imaginary') == os.path.join(root, 'imaginary') def test_files(self): reader = NamespaceReader(self.data.__spec__.submodule_search_locations) root = self.data.__path__[0] - self.assertIsInstance(reader.files(), MultiplexedPath) - self.assertEqual(repr(reader.files()), f"MultiplexedPath('{root}')") + assert isinstance(reader.files(), MultiplexedPath) + assert repr(reader.files()) == f"MultiplexedPath('{root}')" if __name__ == '__main__': diff --git a/Lib/test/test_importlib/resources/test_resource.py b/Lib/test/test_importlib/resources/test_resource.py index fcede14b891a84..b114b1f0d80fbf 100644 --- a/Lib/test/test_importlib/resources/test_resource.py +++ b/Lib/test/test_importlib/resources/test_resource.py @@ -1,7 +1,9 @@ +import importlib.resources as resources +import types import unittest +from importlib import import_module from . import util -from importlib import resources, import_module class ResourceTests: @@ -9,16 +11,16 @@ class ResourceTests: def test_is_file_exists(self): target = resources.files(self.data) / 'binary.file' - self.assertTrue(target.is_file()) + assert target.is_file() def test_is_file_missing(self): target = resources.files(self.data) / 'not-a-file' - self.assertFalse(target.is_file()) + assert not target.is_file() def test_is_dir(self): target = resources.files(self.data) / 'subdirectory' - self.assertFalse(target.is_file()) - self.assertTrue(target.is_dir()) + assert not target.is_file() + assert target.is_dir() class ResourceDiskTests(ResourceTests, util.DiskSetup, unittest.TestCase): @@ -38,7 +40,7 @@ def test_resource_contents(self): package = util.create_package( file=self.data, path=self.data.__file__, contents=['A', 'B', 'C'] ) - self.assertEqual(names(resources.files(package)), {'A', 'B', 'C'}) + assert names(resources.files(package)) == {'A', 'B', 'C'} def test_is_file(self): package = util.create_package( @@ -46,7 +48,7 @@ def test_is_file(self): path=self.data.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F'], ) - self.assertTrue(resources.files(package).joinpath('B').is_file()) + assert resources.files(package).joinpath('B').is_file() def test_is_dir(self): package = util.create_package( @@ -54,7 +56,7 @@ def test_is_dir(self): path=self.data.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F'], ) - self.assertTrue(resources.files(package).joinpath('D').is_dir()) + assert resources.files(package).joinpath('D').is_dir() def test_resource_missing(self): package = util.create_package( @@ -62,7 +64,7 @@ def test_resource_missing(self): path=self.data.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F'], ) - self.assertFalse(resources.files(package).joinpath('Z').is_file()) + assert not resources.files(package).joinpath('Z').is_file() class ResourceCornerCaseTests(util.DiskSetup, unittest.TestCase): @@ -82,30 +84,26 @@ def test_package_has_no_reader_fallback(self): module.__file__ = '/path/which/shall/not/be/named' module.__spec__.loader = module.__loader__ module.__spec__.origin = module.__file__ - self.assertFalse(resources.files(module).joinpath('A').is_file()) + assert not resources.files(module).joinpath('A').is_file() class ResourceFromZipsTest01(util.ZipSetup, unittest.TestCase): def test_is_submodule_resource(self): submodule = import_module('data01.subdirectory') - self.assertTrue(resources.files(submodule).joinpath('binary.file').is_file()) + assert resources.files(submodule).joinpath('binary.file').is_file() def test_read_submodule_resource_by_name(self): - self.assertTrue( - resources.files('data01.subdirectory').joinpath('binary.file').is_file() - ) + assert resources.files('data01.subdirectory').joinpath('binary.file').is_file() def test_submodule_contents(self): submodule = import_module('data01.subdirectory') - self.assertEqual( - names(resources.files(submodule)), {'__init__.py', 'binary.file'} - ) + assert names(resources.files(submodule)) == {'__init__.py', 'binary.file'} def test_submodule_contents_by_name(self): - self.assertEqual( - names(resources.files('data01.subdirectory')), - {'__init__.py', 'binary.file'}, - ) + assert names(resources.files('data01.subdirectory')) == { + '__init__.py', + 'binary.file', + } def test_as_file_directory(self): with resources.as_file(resources.files('data01')) as data: @@ -124,14 +122,8 @@ def test_unrelated_contents(self): Test thata zip with two unrelated subpackages return distinct resources. Ref python/importlib_resources#44. """ - self.assertEqual( - names(resources.files('data02.one')), - {'__init__.py', 'resource1.txt'}, - ) - self.assertEqual( - names(resources.files('data02.two')), - {'__init__.py', 'resource2.txt'}, - ) + assert names(resources.files('data02.one')) == {'__init__.py', 'resource1.txt'} + assert names(resources.files('data02.two')) == {'__init__.py', 'resource2.txt'} class DeletingZipsTest(util.ZipSetup, unittest.TestCase): @@ -168,16 +160,15 @@ def test_read_text_does_not_keep_open(self): class ResourceFromNamespaceTests: def test_is_submodule_resource(self): - self.assertTrue( - resources.files(import_module('namespacedata01')) + assert ( + resources + .files(import_module('namespacedata01')) .joinpath('binary.file') .is_file() ) def test_read_submodule_resource_by_name(self): - self.assertTrue( - resources.files('namespacedata01').joinpath('binary.file').is_file() - ) + assert resources.files('namespacedata01').joinpath('binary.file').is_file() def test_submodule_contents(self): contents = names(resources.files(import_module('namespacedata01'))) @@ -185,9 +176,7 @@ def test_submodule_contents(self): contents.remove('__pycache__') except KeyError: pass - self.assertEqual( - contents, {'subdirectory', 'binary.file', 'utf-8.file', 'utf-16.file'} - ) + assert contents == {'subdirectory', 'binary.file', 'utf-8.file', 'utf-16.file'} def test_submodule_contents_by_name(self): contents = names(resources.files('namespacedata01')) @@ -195,9 +184,7 @@ def test_submodule_contents_by_name(self): contents.remove('__pycache__') except KeyError: pass - self.assertEqual( - contents, {'subdirectory', 'binary.file', 'utf-8.file', 'utf-16.file'} - ) + assert contents == {'subdirectory', 'binary.file', 'utf-8.file', 'utf-16.file'} def test_submodule_sub_contents(self): contents = names(resources.files(import_module('namespacedata01.subdirectory'))) @@ -205,7 +192,7 @@ def test_submodule_sub_contents(self): contents.remove('__pycache__') except KeyError: pass - self.assertEqual(contents, {'binary.file'}) + assert contents == {'binary.file'} def test_submodule_sub_contents_by_name(self): contents = names(resources.files('namespacedata01.subdirectory')) @@ -213,7 +200,7 @@ def test_submodule_sub_contents_by_name(self): contents.remove('__pycache__') except KeyError: pass - self.assertEqual(contents, {'binary.file'}) + assert contents == {'binary.file'} class ResourceFromNamespaceDiskTests( @@ -232,5 +219,24 @@ class ResourceFromNamespaceZipTests( MODULE = 'namespacedata01' +class MainModuleTests(unittest.TestCase): + def test_main_module_with_none_spec(self): + """ + __main__ module with no spec should raise TypeError (for clarity). + + See python/cpython#138531 for details. + """ + # construct a __main__ module with no __spec__. + mainmodule = types.ModuleType("__main__") + + assert mainmodule.__spec__ is None + + with self.assertRaises( + TypeError, + msg="Cannot access resources for '__main__' as it does not appear to correspond to an importable module (its __spec__ is None).", + ): + resources.files(mainmodule) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_importlib/resources/test_util.py b/Lib/test/test_importlib/resources/test_util.py new file mode 100644 index 00000000000000..de304b6f3510a6 --- /dev/null +++ b/Lib/test/test_importlib/resources/test_util.py @@ -0,0 +1,29 @@ +import unittest + +from .util import MemorySetup, Traversable + + +class TestMemoryTraversableImplementation(unittest.TestCase): + def test_concrete_methods_are_not_overridden(self): + """`MemoryTraversable` must not override `Traversable` concrete methods. + + This test is not an attempt to enforce a particular `Traversable` protocol; + it merely catches changes in the `Traversable` abstract/concrete methods + that have not been mirrored in the `MemoryTraversable` subclass. + """ + + traversable_concrete_methods = { + method + for method, value in Traversable.__dict__.items() + if callable(value) and method not in Traversable.__abstractmethods__ + } + memory_traversable_concrete_methods = { + method + for method, value in MemorySetup.MemoryTraversable.__dict__.items() + if callable(value) and not method.startswith("__") + } + overridden_methods = ( + memory_traversable_concrete_methods & traversable_concrete_methods + ) + + assert not overridden_methods diff --git a/Lib/test/test_importlib/resources/util.py b/Lib/test/test_importlib/resources/util.py index e2d995f596317d..85b5c61518de44 100644 --- a/Lib/test/test_importlib/resources/util.py +++ b/Lib/test/test_importlib/resources/util.py @@ -1,18 +1,18 @@ import abc +import contextlib +import functools import importlib import io +import pathlib import sys import types -import pathlib -import contextlib +from importlib.machinery import ModuleSpec +from importlib.resources.abc import ResourceReader, Traversable, TraversableResources -from importlib.resources.abc import ResourceReader from test.support import import_helper, os_helper -from . import zip as zip_ -from . import _path - -from importlib.machinery import ModuleSpec +from . import _path +from . import zip as zip_ class Reader(ResourceReader): @@ -122,7 +122,7 @@ def test_missing_path(self): bytes_data = io.BytesIO(b'Hello, world!') package = create_package(file=bytes_data, path=FileNotFoundError()) self.execute(package, 'utf-8.file') - self.assertEqual(package.__loader__._path, 'utf-8.file') + assert package.__loader__._path == 'utf-8.file' def test_extant_path(self): # Attempting to open or read or request the path when the @@ -133,7 +133,7 @@ def test_extant_path(self): path = __file__ package = create_package(file=bytes_data, path=path) self.execute(package, 'utf-8.file') - self.assertEqual(package.__loader__._path, 'utf-8.file') + assert package.__loader__._path == 'utf-8.file' def test_useless_loader(self): package = create_package(file=FileNotFoundError(), path=FileNotFoundError()) @@ -202,5 +202,108 @@ def tree_on_path(self, spec): self.fixtures.enter_context(import_helper.DirsOnSysPath(temp_dir)) +class MemorySetup(ModuleSetup): + """Support loading a module in memory.""" + + MODULE = 'data01' + + def load_fixture(self, module): + self.fixtures.enter_context(self.augment_sys_metapath(module)) + return importlib.import_module(module) + + @contextlib.contextmanager + def augment_sys_metapath(self, module): + finder_instance = self.MemoryFinder(module) + sys.meta_path.append(finder_instance) + yield + sys.meta_path.remove(finder_instance) + + class MemoryFinder(importlib.abc.MetaPathFinder): + def __init__(self, module): + self._module = module + + def find_spec(self, fullname, path, target=None): + if fullname != self._module: + return None + + return importlib.machinery.ModuleSpec( + name=fullname, + loader=MemorySetup.MemoryLoader(self._module), + is_package=True, + ) + + class MemoryLoader(importlib.abc.Loader): + def __init__(self, module): + self._module = module + + def exec_module(self, module): + pass + + def get_resource_reader(self, fullname): + return MemorySetup.MemoryTraversableResources(self._module, fullname) + + class MemoryTraversableResources(TraversableResources): + def __init__(self, module, fullname): + self._module = module + self._fullname = fullname + + def files(self): + return MemorySetup.MemoryTraversable(self._module, self._fullname) + + class MemoryTraversable(Traversable): + """Implement only the abstract methods of `Traversable`. + + Besides `.__init__()`, no other methods may be implemented or overridden. + This is critical for validating the concrete `Traversable` implementations. + """ + + def __init__(self, module, fullname): + self._module = module + self._fullname = fullname + + def _resolve(self): + """ + Fully traverse the `fixtures` dictionary. + + This should be wrapped in a `try/except KeyError` + but it is not currently needed and lowers the code coverage numbers. + """ + path = pathlib.PurePosixPath(self._fullname) + return functools.reduce(lambda d, p: d[p], path.parts, fixtures) + + def iterdir(self): + directory = self._resolve() + if not isinstance(directory, dict): + # Filesystem openers raise OSError, and that exception is mirrored here. + raise OSError(f"{self._fullname} is not a directory") + for path in directory: + yield MemorySetup.MemoryTraversable( + self._module, f"{self._fullname}/{path}" + ) + + def is_dir(self) -> bool: + return isinstance(self._resolve(), dict) + + def is_file(self) -> bool: + return not self.is_dir() + + def open(self, mode='r', encoding=None, errors=None, *_, **__): + contents = self._resolve() + if isinstance(contents, dict): + # Filesystem openers raise OSError when attempting to open a directory, + # and that exception is mirrored here. + raise OSError(f"{self._fullname} is a directory") + if isinstance(contents, str): + contents = contents.encode("utf-8") + result = io.BytesIO(contents) + if "b" in mode: + return result + return io.TextIOWrapper(result, encoding=encoding, errors=errors) + + @property + def name(self): + return pathlib.PurePosixPath(self._fullname).name + + class CommonTests(DiskSetup, CommonTestsBase): pass diff --git a/Lib/test/test_importlib/source/test_file_loader.py b/Lib/test/test_importlib/source/test_file_loader.py index f35adec1a8e800..e4bd850f3514ff 100644 --- a/Lib/test/test_importlib/source/test_file_loader.py +++ b/Lib/test/test_importlib/source/test_file_loader.py @@ -22,7 +22,7 @@ from test.test_py_compile import SourceDateEpochTestMeta -class SimpleTest(abc.LoaderTests): +class SimpleTest: """Should have no issue importing a source module [basic]. And if there is a syntax error, it should raise a SyntaxError [syntax error]. @@ -34,17 +34,6 @@ def setUp(self): self.filepath = os.path.join('ham', self.name + '.py') self.loader = self.machinery.SourceFileLoader(self.name, self.filepath) - def test_load_module_API(self): - class Tester(self.abc.FileLoader): - def get_source(self, _): return 'attr = 42' - def is_package(self, _): return False - - loader = Tester('blah', 'blah.py') - self.addCleanup(unload, 'blah') - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - module = loader.load_module() # Should not raise an exception. - def test_get_filename_API(self): # If fullname is not set then assume self.path is desired. class Tester(self.abc.FileLoader): @@ -69,173 +58,11 @@ def test_inequality(self): other = self.machinery.SourceFileLoader('_' + self.name, self.filepath) self.assertNotEqual(self.loader, other) - # [basic] - def test_module(self): - with util.create_modules('_temp') as mapping: - loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - module = loader.load_module('_temp') - self.assertIn('_temp', sys.modules) - check = {'__name__': '_temp', '__file__': mapping['_temp'], - '__package__': ''} - for attr, value in check.items(): - self.assertEqual(getattr(module, attr), value) - - def test_package(self): - with util.create_modules('_pkg.__init__') as mapping: - loader = self.machinery.SourceFileLoader('_pkg', - mapping['_pkg.__init__']) - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - module = loader.load_module('_pkg') - self.assertIn('_pkg', sys.modules) - check = {'__name__': '_pkg', '__file__': mapping['_pkg.__init__'], - '__path__': [os.path.dirname(mapping['_pkg.__init__'])], - '__package__': '_pkg'} - for attr, value in check.items(): - self.assertEqual(getattr(module, attr), value) - - - def test_lacking_parent(self): - with util.create_modules('_pkg.__init__', '_pkg.mod')as mapping: - loader = self.machinery.SourceFileLoader('_pkg.mod', - mapping['_pkg.mod']) - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - module = loader.load_module('_pkg.mod') - self.assertIn('_pkg.mod', sys.modules) - check = {'__name__': '_pkg.mod', '__file__': mapping['_pkg.mod'], - '__package__': '_pkg'} - for attr, value in check.items(): - self.assertEqual(getattr(module, attr), value) def fake_mtime(self, fxn): """Fake mtime to always be higher than expected.""" return lambda name: fxn(name) + 1 - def test_module_reuse(self): - with util.create_modules('_temp') as mapping: - loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - module = loader.load_module('_temp') - module_id = id(module) - module_dict_id = id(module.__dict__) - with open(mapping['_temp'], 'w', encoding='utf-8') as file: - file.write("testing_var = 42\n") - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - module = loader.load_module('_temp') - self.assertIn('testing_var', module.__dict__, - "'testing_var' not in " - "{0}".format(list(module.__dict__.keys()))) - self.assertEqual(module, sys.modules['_temp']) - self.assertEqual(id(module), module_id) - self.assertEqual(id(module.__dict__), module_dict_id) - - def test_state_after_failure(self): - # A failed reload should leave the original module intact. - attributes = ('__file__', '__path__', '__package__') - value = '<test>' - name = '_temp' - with util.create_modules(name) as mapping: - orig_module = types.ModuleType(name) - for attr in attributes: - setattr(orig_module, attr, value) - with open(mapping[name], 'w', encoding='utf-8') as file: - file.write('+++ bad syntax +++') - loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) - with self.assertRaises(SyntaxError): - loader.exec_module(orig_module) - for attr in attributes: - self.assertEqual(getattr(orig_module, attr), value) - with self.assertRaises(SyntaxError): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - loader.load_module(name) - for attr in attributes: - self.assertEqual(getattr(orig_module, attr), value) - - # [syntax error] - def test_bad_syntax(self): - with util.create_modules('_temp') as mapping: - with open(mapping['_temp'], 'w', encoding='utf-8') as file: - file.write('=') - loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) - with self.assertRaises(SyntaxError): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - loader.load_module('_temp') - self.assertNotIn('_temp', sys.modules) - - def test_file_from_empty_string_dir(self): - # Loading a module found from an empty string entry on sys.path should - # not only work, but keep all attributes relative. - file_path = '_temp.py' - with open(file_path, 'w', encoding='utf-8') as file: - file.write("# test file for importlib") - try: - with util.uncache('_temp'): - loader = self.machinery.SourceFileLoader('_temp', file_path) - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - mod = loader.load_module('_temp') - self.assertEqual(file_path, mod.__file__) - self.assertEqual(self.util.cache_from_source(file_path), - mod.__cached__) - finally: - os.unlink(file_path) - pycache = os.path.dirname(self.util.cache_from_source(file_path)) - if os.path.exists(pycache): - shutil.rmtree(pycache) - - @util.writes_bytecode_files - def test_timestamp_overflow(self): - # When a modification timestamp is larger than 2**32, it should be - # truncated rather than raise an OverflowError. - with util.create_modules('_temp') as mapping: - source = mapping['_temp'] - compiled = self.util.cache_from_source(source) - with open(source, 'w', encoding='utf-8') as f: - f.write("x = 5") - try: - os.utime(source, (2 ** 33 - 5, 2 ** 33 - 5)) - except OverflowError: - self.skipTest("cannot set modification time to large integer") - except OSError as e: - if e.errno != getattr(errno, 'EOVERFLOW', None): - raise - self.skipTest("cannot set modification time to large integer ({})".format(e)) - loader = self.machinery.SourceFileLoader('_temp', mapping['_temp']) - # PEP 451 - module = types.ModuleType('_temp') - module.__spec__ = self.util.spec_from_loader('_temp', loader) - loader.exec_module(module) - self.assertEqual(module.x, 5) - self.assertTrue(os.path.exists(compiled)) - os.unlink(compiled) - # PEP 302 - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - mod = loader.load_module('_temp') - # Sanity checks. - self.assertEqual(mod.__cached__, compiled) - self.assertEqual(mod.x, 5) - # The pyc file was created. - self.assertTrue(os.path.exists(compiled)) - - def test_unloadable(self): - loader = self.machinery.SourceFileLoader('good name', {}) - module = types.ModuleType('bad name') - module.__spec__ = self.machinery.ModuleSpec('bad name', loader) - with self.assertRaises(ImportError): - loader.exec_module(module) - with self.assertRaises(ImportError): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - loader.load_module('bad name') - @util.writes_bytecode_files def test_checked_hash_based_pyc(self): with util.create_modules('_temp') as mapping: @@ -386,12 +213,21 @@ def manipulate_bytecode(self, del sys.modules['_temp'] except KeyError: pass - py_compile.compile(mapping[name], invalidation_mode=invalidation_mode) - if not del_source: - bytecode_path = self.util.cache_from_source(mapping[name]) + if sys.implementation.cache_tag is None: + if del_source: + bytecode_path = mapping[name] + 'c' + py_compile.compile(mapping[name], bytecode_path, + invalidation_mode=invalidation_mode) + os.unlink(mapping[name]) + else: + raise unittest.SkipTest('requires sys.implementation.cache_tag') else: - os.unlink(mapping[name]) - bytecode_path = make_legacy_pyc(mapping[name]) + py_compile.compile(mapping[name], invalidation_mode=invalidation_mode) + if not del_source: + bytecode_path = self.util.cache_from_source(mapping[name]) + else: + os.unlink(mapping[name]) + bytecode_path = make_legacy_pyc(mapping[name]) if manipulator: with open(bytecode_path, 'rb') as file: bc = file.read() @@ -511,16 +347,6 @@ def import_(self, file, module_name): loader.exec_module(module) -class BadBytecodeTestPEP302(BadBytecodeTest): - - def import_(self, file, module_name): - loader = self.loader(module_name, file) - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - module = loader.load_module(module_name) - self.assertIn(module_name, sys.modules) - - class SourceLoaderBadBytecodeTest: @classmethod @@ -681,18 +507,6 @@ class SourceLoaderBadBytecodeTestPEP451( util=importlib_util) -class SourceLoaderBadBytecodeTestPEP302( - SourceLoaderBadBytecodeTest, BadBytecodeTestPEP302): - pass - - -(Frozen_SourceBadBytecodePEP302, - Source_SourceBadBytecodePEP302 - ) = util.test_both(SourceLoaderBadBytecodeTestPEP302, importlib=importlib, - machinery=machinery, abc=importlib_abc, - util=importlib_util) - - class SourcelessLoaderBadBytecodeTest: @classmethod @@ -779,17 +593,5 @@ class SourcelessLoaderBadBytecodeTestPEP451(SourcelessLoaderBadBytecodeTest, util=importlib_util) -class SourcelessLoaderBadBytecodeTestPEP302(SourcelessLoaderBadBytecodeTest, - BadBytecodeTestPEP302): - pass - - -(Frozen_SourcelessBadBytecodePEP302, - Source_SourcelessBadBytecodePEP302 - ) = util.test_both(SourcelessLoaderBadBytecodeTestPEP302, importlib=importlib, - machinery=machinery, abc=importlib_abc, - util=importlib_util) - - if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_importlib/source/test_finder.py b/Lib/test/test_importlib/source/test_finder.py index 4de736a6bf3b2d..91865b997ad364 100644 --- a/Lib/test/test_importlib/source/test_finder.py +++ b/Lib/test/test_importlib/source/test_finder.py @@ -57,6 +57,8 @@ def run_test(self, test, create=None, *, compile_=None, unlink=None): """ if create is None: create = {test} + if (compile_ or unlink) and sys.implementation.cache_tag is None: + raise unittest.SkipTest('requires sys.implementation.cache_tag') with util.create_modules(*create) as mapping: if compile_: for name in compile_: @@ -73,7 +75,7 @@ def run_test(self, test, create=None, *, compile_=None, unlink=None): if error.errno != errno.ENOENT: raise loader = self.import_(mapping['.root'], test) - self.assertHasAttr(loader, 'load_module') + self.assertHasAttr(loader, 'exec_module') return loader def test_module(self): @@ -100,7 +102,7 @@ def test_module_in_package(self): with util.create_modules('pkg.__init__', 'pkg.sub') as mapping: pkg_dir = os.path.dirname(mapping['pkg.__init__']) loader = self.import_(pkg_dir, 'pkg.sub') - self.assertHasAttr(loader, 'load_module') + self.assertHasAttr(loader, 'exec_module') # [sub package] def test_package_in_package(self): @@ -108,7 +110,7 @@ def test_package_in_package(self): with context as mapping: pkg_dir = os.path.dirname(mapping['pkg.__init__']) loader = self.import_(pkg_dir, 'pkg.sub') - self.assertHasAttr(loader, 'load_module') + self.assertHasAttr(loader, 'exec_module') # [package over modules] def test_package_over_module(self): @@ -129,7 +131,7 @@ def test_empty_string_for_dir(self): file.write("# test file for importlib") try: loader = self._find(finder, 'mod', loader_only=True) - self.assertHasAttr(loader, 'load_module') + self.assertHasAttr(loader, 'exec_module') finally: os.unlink('mod.py') diff --git a/Lib/test/test_importlib/source/test_source_encoding.py b/Lib/test/test_importlib/source/test_source_encoding.py index c09c9aa12b862e..fada07877fc125 100644 --- a/Lib/test/test_importlib/source/test_source_encoding.py +++ b/Lib/test/test_importlib/source/test_source_encoding.py @@ -102,19 +102,6 @@ def load(self, loader): ) = util.test_both(EncodingTestPEP451, machinery=machinery) -class EncodingTestPEP302(EncodingTest): - - def load(self, loader): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - return loader.load_module(self.module_name) - - -(Frozen_EncodingTestPEP302, - Source_EncodingTestPEP302 - ) = util.test_both(EncodingTestPEP302, machinery=machinery) - - class LineEndingTest: r"""Source written with the three types of line endings (\n, \r\n, \r) @@ -158,18 +145,5 @@ def load(self, loader, module_name): ) = util.test_both(LineEndingTestPEP451, machinery=machinery) -class LineEndingTestPEP302(LineEndingTest): - - def load(self, loader, module_name): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - return loader.load_module(module_name) - - -(Frozen_LineEndingTestPEP302, - Source_LineEndingTestPEP302 - ) = util.test_both(LineEndingTestPEP302, machinery=machinery) - - if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py index 070920d0da7e19..b24c576646842f 100644 --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -194,10 +194,6 @@ def test_create_module(self): spec = 'a spec' self.assertIsNone(self.ins.create_module(spec)) - def test_load_module(self): - with self.assertRaises(ImportError): - self.ins.load_module('something') - def test_module_repr(self): mod = types.ModuleType('blah') with warnings.catch_warnings(): @@ -224,15 +220,7 @@ class ResourceLoaderDefaultsTests(ABCTestHarness): SPLIT = make_abc_subclasses(ResourceLoader) def test_get_data(self): - with ( - self.assertRaises(IOError), - self.assertWarnsRegex( - DeprecationWarning, - r"importlib\.abc\.ResourceLoader is deprecated in favour of " - r"supporting resource loading through importlib\.resources" - r"\.abc\.TraversableResources.", - ), - ): + with self.assertRaises(IOError): self.ins.get_data('/some/path') @@ -366,38 +354,6 @@ def is_package(self, fullname): return SpecLoader() - def test_fresh(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - loader = self.loader() - name = 'blah' - with test_util.uncache(name): - loader.load_module(name) - module = loader.found - self.assertIs(sys.modules[name], module) - self.assertEqual(loader, module.__loader__) - self.assertEqual(loader, module.__spec__.loader) - self.assertEqual(name, module.__name__) - self.assertEqual(name, module.__spec__.name) - self.assertIsNotNone(module.__path__) - self.assertIsNotNone(module.__path__, - module.__spec__.submodule_search_locations) - - def test_reload(self): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - name = 'blah' - loader = self.loader() - module = types.ModuleType(name) - module.__spec__ = self.util.spec_from_loader(name, loader) - module.__loader__ = loader - with test_util.uncache(name): - sys.modules[name] = module - loader.load_module(name) - found = loader.found - self.assertIs(found, sys.modules[name]) - self.assertIs(module, sys.modules[name]) - (Frozen_LoaderLoadModuleTests, Source_LoaderLoadModuleTests @@ -486,59 +442,6 @@ def test_get_code_source_not_found(self): InspectLoaderSubclass=SPLIT_IL) -class InspectLoaderLoadModuleTests: - - """Test InspectLoader.load_module().""" - - module_name = 'blah' - - def setUp(self): - import_helper.unload(self.module_name) - self.addCleanup(import_helper.unload, self.module_name) - - def load(self, loader): - spec = self.util.spec_from_loader(self.module_name, loader) - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - return self.init._bootstrap._load_unlocked(spec) - - def mock_get_code(self): - return mock.patch.object(self.InspectLoaderSubclass, 'get_code') - - def test_get_code_ImportError(self): - # If get_code() raises ImportError, it should propagate. - with self.mock_get_code() as mocked_get_code: - mocked_get_code.side_effect = ImportError - with self.assertRaises(ImportError): - loader = self.InspectLoaderSubclass() - self.load(loader) - - def test_get_code_None(self): - # If get_code() returns None, raise ImportError. - with self.mock_get_code() as mocked_get_code: - mocked_get_code.return_value = None - with self.assertRaises(ImportError): - loader = self.InspectLoaderSubclass() - self.load(loader) - - def test_module_returned(self): - # The loaded module should be returned. - code = compile('attr = 42', '<string>', 'exec') - with self.mock_get_code() as mocked_get_code: - mocked_get_code.return_value = code - loader = self.InspectLoaderSubclass() - module = self.load(loader) - self.assertEqual(module, sys.modules[self.module_name]) - - -(Frozen_ILLoadModuleTests, - Source_ILLoadModuleTests - ) = test_util.test_both(InspectLoaderLoadModuleTests, - InspectLoaderSubclass=SPLIT_IL, - init=init, - util=util) - - ##### ExecutionLoader concrete methods ######################################### class ExecutionLoaderGetCodeTests: @@ -607,8 +510,7 @@ def test_get_code_no_path(self): class SourceOnlyLoader: # Globals that should be defined for all modules. - source = (b"_ = '::'.join([__name__, __file__, __cached__, __package__, " - b"repr(__loader__)])") + source = (b"_ = '::'.join([__name__, __file__, __package__, repr(__loader__)])") def __init__(self, path): self.path = path @@ -631,7 +533,10 @@ class SourceLoader(SourceOnlyLoader): def __init__(self, path, magic=None): super().__init__(path) - self.bytecode_path = self.util.cache_from_source(self.path) + try: + self.bytecode_path = self.util.cache_from_source(self.path) + except NotImplementedError: + self.bytecode_path = None self.source_size = len(self.source) if magic is None: magic = self.util.MAGIC_NUMBER @@ -677,26 +582,26 @@ def setUp(self, *, is_package=True, **kwargs): module_name = 'mod' self.path = os.path.join(self.package, '.'.join(['mod', 'py'])) self.name = '.'.join([self.package, module_name]) - self.cached = self.util.cache_from_source(self.path) + try: + self.cached = self.util.cache_from_source(self.path) + except NotImplementedError: + self.cached = None self.loader = self.loader_mock(self.path, **kwargs) def verify_module(self, module): self.assertEqual(module.__name__, self.name) self.assertEqual(module.__file__, self.path) - self.assertEqual(module.__cached__, self.cached) self.assertEqual(module.__package__, self.package) self.assertEqual(module.__loader__, self.loader) values = module._.split('::') self.assertEqual(values[0], self.name) self.assertEqual(values[1], self.path) - self.assertEqual(values[2], self.cached) - self.assertEqual(values[3], self.package) - self.assertEqual(values[4], repr(self.loader)) + self.assertEqual(values[2], self.package) + self.assertEqual(values[3], repr(self.loader)) def verify_code(self, code_object): module = types.ModuleType(self.name) module.__file__ = self.path - module.__cached__ = self.cached module.__package__ = self.package module.__loader__ = self.loader module.__path__ = [] @@ -737,34 +642,6 @@ def test_source_to_code(self): code = self.loader.source_to_code(self.loader.source, self.path) self.verify_code(code) - def test_load_module(self): - # Loading a module should set __name__, __loader__, __package__, - # __path__ (for packages), __file__, and __cached__. - # The module should also be put into sys.modules. - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - with test_util.uncache(self.name): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - module = self.loader.load_module(self.name) - self.verify_module(module) - self.assertEqual(module.__path__, [os.path.dirname(self.path)]) - self.assertIn(self.name, sys.modules) - - def test_package_settings(self): - # __package__ needs to be set, while __path__ is set on if the module - # is a package. - # Testing the values for a package are covered by test_load_module. - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - self.setUp(is_package=False) - with test_util.uncache(self.name): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - module = self.loader.load_module(self.name) - self.verify_module(module) - self.assertNotHasAttr(module, '__path__') - def test_get_source_encoding(self): # Source is considered encoded in UTF-8 by default unless otherwise # specified by an encoding line. @@ -785,6 +662,8 @@ def test_get_source_encoding(self): @unittest.skipIf(sys.dont_write_bytecode, "sys.dont_write_bytecode is true") +@unittest.skipIf(sys.implementation.cache_tag is None, + "sys.implementation.cache_tag is None") class SourceLoaderBytecodeTests(SourceLoaderTestHarness): """Test importlib.abc.SourceLoader's use of bytecode. @@ -912,7 +791,7 @@ def test_universal_newlines(self): mock = self.SourceOnlyLoaderMock('mod.file') source = "x = 42\r\ny = -13\r\n" mock.source = source.encode('utf-8') - expect = io.IncrementalNewlineDecoder(None, True).decode(source) + expect = io.StringIO(source, newline=None).getvalue() self.assertEqual(mock.get_source(name), expect) @@ -936,13 +815,8 @@ def get_filename(self, fullname): def path_stats(self, path): return {'mtime': 1} - with self.assertWarnsRegex( - DeprecationWarning, - r"importlib\.abc\.ResourceLoader is deprecated in favour of " - r"supporting resource loading through importlib\.resources" - r"\.abc\.TraversableResources.", - ): - loader = DummySourceLoader() + + loader = DummySourceLoader() with self.assertWarnsRegex( DeprecationWarning, @@ -952,17 +826,5 @@ def path_stats(self, path): loader.path_mtime('foo.py') -class ResourceLoaderDeprecationWarningsTests(unittest.TestCase): - """Tests ResourceLoader deprecation warnings.""" - - def test_deprecated_resource_loader(self): - from importlib.abc import ResourceLoader - class DummyLoader(ResourceLoader): - def get_data(self, path): - return b'' - - with self.assertWarns(DeprecationWarning): - DummyLoader() - if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_importlib/test_api.py b/Lib/test/test_importlib/test_api.py index 1bc531a2fe34e7..70d93d693ae593 100644 --- a/Lib/test/test_importlib/test_api.py +++ b/Lib/test/test_importlib/test_api.py @@ -231,11 +231,9 @@ def test_reload_location_changed(self): # Start as a plain module. self.init.invalidate_caches() path = os.path.join(cwd, name + '.py') - cached = self.util.cache_from_source(path) expected = {'__name__': name, '__package__': '', '__file__': path, - '__cached__': cached, '__doc__': None, } os_helper.create_empty_file(path) @@ -252,11 +250,9 @@ def test_reload_location_changed(self): # Change to a package. self.init.invalidate_caches() init_path = os.path.join(cwd, name, '__init__.py') - cached = self.util.cache_from_source(init_path) expected = {'__name__': name, '__package__': name, '__file__': init_path, - '__cached__': cached, '__path__': [os.path.dirname(init_path)], '__doc__': None, } @@ -283,7 +279,6 @@ def test_reload_namespace_changed(self): # Start as a namespace package. self.init.invalidate_caches() bad_path = os.path.join(cwd, name, '__init.py') - cached = self.util.cache_from_source(bad_path) expected = {'__name__': name, '__package__': name, '__doc__': None, @@ -312,11 +307,9 @@ def test_reload_namespace_changed(self): # Change to a regular package. self.init.invalidate_caches() init_path = os.path.join(cwd, name, '__init__.py') - cached = self.util.cache_from_source(init_path) expected = {'__name__': name, '__package__': name, '__file__': init_path, - '__cached__': cached, '__path__': [os.path.dirname(init_path)], '__doc__': None, 'eggs': None, diff --git a/Lib/test/test_importlib/test_discover.py b/Lib/test/test_importlib/test_discover.py new file mode 100644 index 00000000000000..c4ab7b6982e3ba --- /dev/null +++ b/Lib/test/test_importlib/test_discover.py @@ -0,0 +1,121 @@ +from unittest.mock import Mock + +from test.test_importlib import util + +importlib = util.import_importlib('importlib') +machinery = util.import_importlib('importlib.machinery') + + +class DiscoverableFinder: + def __init__(self, discover=[]): + self._discovered_values = discover + + def find_spec(self, fullname, path=None, target=None): + raise NotImplementedError + + def discover(self, parent=None): + yield from self._discovered_values + + +class TestPathFinder: + """PathFinder implements MetaPathFinder, which uses the PathEntryFinder(s) + registered in sys.path_hooks (and sys.path_importer_cache) to search + sys.path or the parent's __path__. + + PathFinder.discover() should redirect to the .discover() method of the + PathEntryFinder for each path entry. + """ + + def test_search_path_hooks_top_level(self): + modules = [ + self.machinery.ModuleSpec(name='example1', loader=None), + self.machinery.ModuleSpec(name='example2', loader=None), + self.machinery.ModuleSpec(name='example3', loader=None), + ] + + with util.import_state( + path_importer_cache={ + 'discoverable': DiscoverableFinder(discover=modules), + }, + path=['discoverable'], + ): + discovered = list(self.machinery.PathFinder.discover()) + + self.assertEqual(discovered, modules) + + + def test_search_path_hooks_parent(self): + parent = self.machinery.ModuleSpec(name='example', loader=None, is_package=True) + parent.submodule_search_locations.append('discoverable') + + children = [ + self.machinery.ModuleSpec(name='example.child1', loader=None), + self.machinery.ModuleSpec(name='example.child2', loader=None), + self.machinery.ModuleSpec(name='example.child3', loader=None), + ] + + with util.import_state( + path_importer_cache={ + 'discoverable': DiscoverableFinder(discover=children) + }, + path=[], + ): + discovered = list(self.machinery.PathFinder.discover(parent)) + + self.assertEqual(discovered, children) + + def test_invalid_parent(self): + parent = self.machinery.ModuleSpec(name='example', loader=None) + with self.assertRaises(ValueError): + list(self.machinery.PathFinder.discover(parent)) + + +( + Frozen_TestPathFinder, + Source_TestPathFinder, +) = util.test_both(TestPathFinder, importlib=importlib, machinery=machinery) + + +class TestFileFinder: + """FileFinder implements PathEntryFinder and provides the base finder + implementation to search the file system. + """ + + def get_finder(self, path): + loader_details = [ + (self.machinery.SourceFileLoader, self.machinery.SOURCE_SUFFIXES), + (self.machinery.SourcelessFileLoader, self.machinery.BYTECODE_SUFFIXES), + ] + return self.machinery.FileFinder(path, *loader_details) + + def test_discover_top_level(self): + modules = {'example1', 'example2', 'example3'} + with util.create_modules(*modules) as mapping: + finder = self.get_finder(mapping['.root']) + discovered = list(finder.discover()) + self.assertEqual({spec.name for spec in discovered}, modules) + + def test_discover_parent(self): + modules = { + 'example.child1', + 'example.child2', + 'example.child3', + } + with util.create_modules(*modules) as mapping: + example = self.get_finder(mapping['.root']).find_spec('example') + finder = self.get_finder(example.submodule_search_locations[0]) + discovered = list(finder.discover(example)) + self.assertEqual({spec.name for spec in discovered}, modules) + + def test_invalid_parent(self): + with util.create_modules('example') as mapping: + finder = self.get_finder(mapping['.root']) + example = finder.find_spec('example') + with self.assertRaises(ValueError): + list(finder.discover(example)) + + +( + Frozen_TestFileFinder, + Source_TestFileFinder, +) = util.test_both(TestFileFinder, importlib=importlib, machinery=machinery) diff --git a/Lib/test/test_importlib/test_namespace_pkgs.py b/Lib/test/test_importlib/test_namespace_pkgs.py index 6ca0978f9bca69..522ed9fa43f5f9 100644 --- a/Lib/test/test_importlib/test_namespace_pkgs.py +++ b/Lib/test/test_importlib/test_namespace_pkgs.py @@ -318,6 +318,17 @@ def test_module_before_namespace_package(self): self.assertEqual(a_test.attr, 'in module') +class NamespaceSubpackageSameName(NamespacePackageTest): + paths = [''] + + def test_namespace_subpackage_shares_name_with_directory(self): + submodule_path = 'project4.foo' + with self.assertRaises(ModuleNotFoundError) as cm: + importlib.machinery.PathFinder.find_spec(submodule_path) + + self.assertEqual(cm.exception.name, 'project4') + + class ReloadTests(NamespacePackageTest): paths = ['portion1'] diff --git a/Lib/test/test_importlib/test_pkg_import.py b/Lib/test/test_importlib/test_pkg_import.py index 5ffae6222bacb8..287684efc85a91 100644 --- a/Lib/test/test_importlib/test_pkg_import.py +++ b/Lib/test/test_importlib/test_pkg_import.py @@ -39,9 +39,12 @@ def tearDown(self): self.remove_modules() def rewrite_file(self, contents): - compiled_path = cache_from_source(self.module_path) - if os.path.exists(compiled_path): - os.remove(compiled_path) + try: + compiled_path = cache_from_source(self.module_path) + if os.path.exists(compiled_path): + os.remove(compiled_path) + except NotImplementedError: + pass with open(self.module_path, 'w', encoding='utf-8') as f: f.write(contents) diff --git a/Lib/test/test_importlib/test_spec.py b/Lib/test/test_importlib/test_spec.py index aebeabaf83f75d..77b6228940c3b9 100644 --- a/Lib/test/test_importlib/test_spec.py +++ b/Lib/test/test_importlib/test_spec.py @@ -52,7 +52,10 @@ class ModuleSpecTests: def setUp(self): self.name = 'spam' self.path = 'spam.py' - self.cached = self.util.cache_from_source(self.path) + try: + self.cached = self.util.cache_from_source(self.path) + except NotImplementedError: + self.cached = None self.loader = TestLoader() self.spec = self.machinery.ModuleSpec(self.name, self.loader) self.loc_spec = self.machinery.ModuleSpec(self.name, self.loader, @@ -184,6 +187,8 @@ def test_cached_with_origin_not_location(self): self.assertIs(spec.cached, None) + @unittest.skipIf(sys.implementation.cache_tag is None, + "sys.implementation.cache_tag is None") def test_cached_source(self): expected = self.util.cache_from_source(self.path) @@ -224,7 +229,10 @@ def bootstrap(self): def setUp(self): self.name = 'spam' self.path = 'spam.py' - self.cached = self.util.cache_from_source(self.path) + try: + self.cached = self.util.cache_from_source(self.path) + except NotImplementedError: + self.cached = None self.loader = TestLoader() self.spec = self.machinery.ModuleSpec(self.name, self.loader) self.loc_spec = self.machinery.ModuleSpec(self.name, self.loader, @@ -287,20 +295,6 @@ def exec_module(self, module): loaded = self.bootstrap._load(self.spec) self.assertNotIn(self.spec.name, sys.modules) - def test_load_legacy_attributes_immutable(self): - module = object() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", ImportWarning) - class ImmutableLoader(TestLoader): - def load_module(self, name): - sys.modules[name] = module - return module - self.spec.loader = ImmutableLoader() - with CleanImport(self.spec.name): - loaded = self.bootstrap._load(self.spec) - - self.assertIs(sys.modules[self.spec.name], module) - # reload() def test_reload(self): @@ -350,7 +344,6 @@ def test_reload_init_module_attrs(self): self.assertIs(loaded.__spec__, self.spec) self.assertNotHasAttr(loaded, '__path__') self.assertNotHasAttr(loaded, '__file__') - self.assertNotHasAttr(loaded, '__cached__') (Frozen_ModuleSpecMethodsTests, @@ -364,7 +357,10 @@ class FactoryTests: def setUp(self): self.name = 'spam' self.path = os.path.abspath('spam.py') - self.cached = self.util.cache_from_source(self.path) + try: + self.cached = self.util.cache_from_source(self.path) + except NotImplementedError: + self.cached = None self.loader = TestLoader() self.fileloader = TestLoader(self.path) self.pkgloader = TestLoader(self.path, True) diff --git a/Lib/test/test_importlib/test_threaded_import.py b/Lib/test/test_importlib/test_threaded_import.py index f78dc399720c86..6875fdca9c8528 100644 --- a/Lib/test/test_importlib/test_threaded_import.py +++ b/Lib/test/test_importlib/test_threaded_import.py @@ -259,6 +259,208 @@ def test_multiprocessing_pool_circular_import(self, size): 'partial', 'pool_in_threads.py') script_helper.assert_python_ok(fn) + def test_import_failure_race_condition(self): + # Regression test for race condition where a thread could receive + # a partially-initialized module when another thread's import fails. + # The race occurs when: + # 1. Thread 1 starts importing, adds module to sys.modules + # 2. Thread 2 sees the module in sys.modules + # 3. Thread 1's import fails, removes module from sys.modules + # 4. Thread 2 should NOT return the stale module reference + os.mkdir(TESTFN) + self.addCleanup(shutil.rmtree, TESTFN) + sys.path.insert(0, TESTFN) + self.addCleanup(sys.path.remove, TESTFN) + + # Create a module that partially initializes then fails + modname = 'failing_import_module' + with open(os.path.join(TESTFN, modname + '.py'), 'w') as f: + f.write(''' +import time +PARTIAL_ATTR = 'initialized' +time.sleep(0.05) # Widen race window +raise RuntimeError("Intentional import failure") +''') + self.addCleanup(forget, modname) + importlib.invalidate_caches() + + errors = [] + results = [] + + def do_import(delay=0): + time.sleep(delay) + try: + mod = __import__(modname) + # If we got a module, verify it's in sys.modules + if modname not in sys.modules: + errors.append( + f"Got module {mod!r} but {modname!r} not in sys.modules" + ) + elif sys.modules[modname] is not mod: + errors.append( + f"Got different module than sys.modules[{modname!r}]" + ) + else: + results.append(('success', mod)) + except RuntimeError: + results.append(('RuntimeError',)) + except Exception as e: + errors.append(f"Unexpected exception: {e}") + + # Run multiple iterations to increase chance of hitting the race + for _ in range(10): + errors.clear() + results.clear() + if modname in sys.modules: + del sys.modules[modname] + + t1 = threading.Thread(target=do_import, args=(0,)) + t2 = threading.Thread(target=do_import, args=(0.01,)) + t1.start() + t2.start() + t1.join() + t2.join() + + # Neither thread should have errors about stale modules + self.assertEqual(errors, [], f"Race condition detected: {errors}") + + def test_hierarchical_import_deadlock(self): + # Regression test for bpo-38884 / gh-83065 + # Tests that concurrent imports at different hierarchy levels + # don't deadlock when parent imports child in __init__.py + + # Create package structure: + # package/__init__.py: from package import subpackage + # package/subpackage/__init__.py: from package.subpackage.module import * + # package/subpackage/module.py: class SomeClass: pass + + pkg_dir = os.path.join(TESTFN, 'hier_deadlock_pkg') + os.makedirs(pkg_dir) + self.addCleanup(shutil.rmtree, TESTFN) + + subpkg_dir = os.path.join(pkg_dir, 'subpackage') + os.makedirs(subpkg_dir) + + with open(os.path.join(pkg_dir, "__init__.py"), "w") as f: + f.write("from hier_deadlock_pkg import subpackage\n") + + with open(os.path.join(subpkg_dir, "__init__.py"), "w") as f: + f.write("from hier_deadlock_pkg.subpackage.module import *\n") + + with open(os.path.join(subpkg_dir, "module.py"), "w") as f: + f.write("class SomeClass:\n pass\n") + + sys.path.insert(0, TESTFN) + self.addCleanup(sys.path.remove, TESTFN) + self.addCleanup(forget, 'hier_deadlock_pkg') + self.addCleanup(forget, 'hier_deadlock_pkg.subpackage') + self.addCleanup(forget, 'hier_deadlock_pkg.subpackage.module') + + importlib.invalidate_caches() + + errors = [] + results = [] + barrier = threading.Barrier(2) + + def t1(): + barrier.wait() + try: + import hier_deadlock_pkg.subpackage + results.append('t1_success') + except Exception as e: + errors.append(('t1', type(e).__name__, str(e))) + + def t2(): + barrier.wait() + try: + import hier_deadlock_pkg.subpackage.module + results.append('t2_success') + except Exception as e: + errors.append(('t2', type(e).__name__, str(e))) + + # Run multiple times to increase chance of hitting race condition + for i in range(10): + for mod in ['hier_deadlock_pkg', 'hier_deadlock_pkg.subpackage', + 'hier_deadlock_pkg.subpackage.module']: + sys.modules.pop(mod, None) + + errors.clear() + results.clear() + barrier.reset() + + thread1 = threading.Thread(target=t1) + thread2 = threading.Thread(target=t2) + + thread1.start() + thread2.start() + + thread1.join(timeout=5) + thread2.join(timeout=5) + + if thread1.is_alive() or thread2.is_alive(): + self.fail(f"Threads deadlocked on iteration {i}") + + self.assertEqual( + errors, [], + f"Import(s) failed on iteration {i}: {errors}") + self.assertEqual( + sorted(results), ['t1_success', 't2_success'], + f"Not all imports succeeded on iteration {i}: {results}") + + def test_cross_package_circular_import(self): + # Two packages whose __init__.py each import a submodule of the + # other. Concurrent imports of submodules of each must not raise + # _DeadlockError; the import system accepts a partially-initialised + # parent in this case (see _lock_unlock_module). + os.makedirs(os.path.join(TESTFN, "circ_a")) + os.makedirs(os.path.join(TESTFN, "circ_b")) + self.addCleanup(shutil.rmtree, TESTFN) + with open(os.path.join(TESTFN, "circ_a", "__init__.py"), "w") as f: + f.write("import time; time.sleep(0.03)\nimport circ_b.other\n") + with open(os.path.join(TESTFN, "circ_b", "__init__.py"), "w") as f: + f.write("import time; time.sleep(0.03)\nimport circ_a.other\n") + for pkg in ("circ_a", "circ_b"): + for mod in ("sub.py", "other.py"): + with open(os.path.join(TESTFN, pkg, mod), "w") as f: + f.write("X = 1\n") + + sys.path.insert(0, TESTFN) + self.addCleanup(sys.path.remove, TESTFN) + for mod in ("circ_a", "circ_a.sub", "circ_a.other", + "circ_b", "circ_b.sub", "circ_b.other"): + self.addCleanup(forget, mod) + importlib.invalidate_caches() + + errors = [] + barrier = threading.Barrier(2) + + def do_import(name): + barrier.wait() + try: + importlib.import_module(name) + except Exception as e: + errors.append((name, type(e).__name__, str(e))) + + for i in range(10): + for mod in ("circ_a", "circ_a.sub", "circ_a.other", + "circ_b", "circ_b.sub", "circ_b.other"): + sys.modules.pop(mod, None) + errors.clear() + barrier.reset() + + thread1 = threading.Thread(target=do_import, args=("circ_a.sub",)) + thread2 = threading.Thread(target=do_import, args=("circ_b.sub",)) + thread1.start() + thread2.start() + thread1.join(timeout=5) + thread2.join(timeout=5) + + if thread1.is_alive() or thread2.is_alive(): + self.fail(f"Threads deadlocked on iteration {i}") + self.assertEqual( + errors, [], + f"Import(s) failed on iteration {i}: {errors}") + def setUpModule(): thread_info = threading_helper.threading_setup() diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index 6d6d5f96aab4a8..a926a7a4d408af 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -124,12 +124,6 @@ def test___file__(self): module = self.util.module_from_spec(spec) self.assertEqual(module.__file__, spec.origin) - def test___cached__(self): - spec = self.machinery.ModuleSpec('test', object()) - spec.cached = 'some/path' - spec.has_location = True - module = self.util.module_from_spec(spec) - self.assertEqual(module.__cached__, spec.cached) (Frozen_ModuleFromSpecTests, Source_ModuleFromSpecTests @@ -351,6 +345,8 @@ def test_cache_from_source_no_cache_tag(self): with self.assertRaises(NotImplementedError): self.util.cache_from_source('whatever.py') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_cache_from_source_no_dot(self): # Directory with a dot, filename without dot. path = os.path.join('foo.bar', 'file') @@ -359,47 +355,16 @@ def test_cache_from_source_no_dot(self): self.assertEqual(self.util.cache_from_source(path, optimization=''), expect) - def test_cache_from_source_debug_override(self): - # Given the path to a .py file, return the path to its PEP 3147/PEP 488 - # defined .pyc file (i.e. under __pycache__). - path = os.path.join('foo', 'bar', 'baz', 'qux.py') - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - self.assertEqual(self.util.cache_from_source(path, False), - self.util.cache_from_source(path, optimization=1)) - self.assertEqual(self.util.cache_from_source(path, True), - self.util.cache_from_source(path, optimization='')) - with warnings.catch_warnings(): - warnings.simplefilter('error') - with self.assertRaises(DeprecationWarning): - self.util.cache_from_source(path, False) - with self.assertRaises(DeprecationWarning): - self.util.cache_from_source(path, True) - + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_cache_from_source_cwd(self): path = 'foo.py' expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag)) self.assertEqual(self.util.cache_from_source(path, optimization=''), expect) - def test_cache_from_source_override(self): - # When debug_override is not None, it can be any true-ish or false-ish - # value. - path = os.path.join('foo', 'bar', 'baz.py') - # However if the bool-ishness can't be determined, the exception - # propagates. - class Bearish: - def __bool__(self): raise RuntimeError - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - self.assertEqual(self.util.cache_from_source(path, []), - self.util.cache_from_source(path, optimization=1)) - self.assertEqual(self.util.cache_from_source(path, [17]), - self.util.cache_from_source(path, optimization='')) - with self.assertRaises(RuntimeError): - self.util.cache_from_source('/foo/bar/baz.py', Bearish()) - - + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_cache_from_source_optimization_empty_string(self): # Setting 'optimization' to '' leads to no optimization tag (PEP 488). path = 'foo.py' @@ -407,6 +372,8 @@ def test_cache_from_source_optimization_empty_string(self): self.assertEqual(self.util.cache_from_source(path, optimization=''), expect) + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_cache_from_source_optimization_None(self): # Setting 'optimization' to None uses the interpreter's optimization. # (PEP 488) @@ -423,6 +390,8 @@ def test_cache_from_source_optimization_None(self): self.assertEqual(self.util.cache_from_source(path, optimization=None), expect) + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_cache_from_source_optimization_set(self): # The 'optimization' parameter accepts anything that has a string repr # that passes str.alnum(). @@ -440,6 +409,8 @@ def test_cache_from_source_optimization_set(self): with self.assertRaises(ValueError): self.util.cache_from_source(path, optimization='path/is/bad') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_cache_from_source_debug_override_optimization_both_set(self): # Can only set one of the optimization-related parameters. with warnings.catch_warnings(): @@ -449,6 +420,8 @@ def test_cache_from_source_debug_override_optimization_both_set(self): @unittest.skipUnless(os.sep == '\\' and os.altsep == '/', 'test meaningful only where os.altsep is defined') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_sep_altsep_and_sep_cache_from_source(self): # Windows path and PEP 3147 where sep is right of altsep. self.assertEqual( @@ -481,44 +454,60 @@ def test_source_from_cache_no_cache_tag(self): with self.assertRaises(NotImplementedError): self.util.source_from_cache(path) + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_source_from_cache_bad_path(self): # When the path to a pyc file is not in PEP 3147 format, a ValueError # is raised. self.assertRaises( ValueError, self.util.source_from_cache, '/foo/bar/bazqux.pyc') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_source_from_cache_no_slash(self): # No slashes at all in path -> ValueError self.assertRaises( ValueError, self.util.source_from_cache, 'foo.cpython-32.pyc') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_source_from_cache_too_few_dots(self): # Too few dots in final path component -> ValueError self.assertRaises( ValueError, self.util.source_from_cache, '__pycache__/foo.pyc') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_source_from_cache_too_many_dots(self): with self.assertRaises(ValueError): self.util.source_from_cache( '__pycache__/foo.cpython-32.opt-1.foo.pyc') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_source_from_cache_not_opt(self): # Non-`opt-` path component -> ValueError self.assertRaises( ValueError, self.util.source_from_cache, '__pycache__/foo.cpython-32.foo.pyc') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_source_from_cache_no__pycache__(self): # Another problem with the path -> ValueError self.assertRaises( ValueError, self.util.source_from_cache, '/foo/bar/foo.cpython-32.foo.pyc') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_source_from_cache_optimized_bytecode(self): # Optimized bytecode is not an issue. path = os.path.join('__pycache__', 'foo.{}.opt-1.pyc'.format(self.tag)) self.assertEqual(self.util.source_from_cache(path), 'foo.py') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') def test_source_from_cache_missing_optimization(self): # An empty optimization level is a no-no. path = os.path.join('__pycache__', 'foo.{}.opt-.pyc'.format(self.tag)) @@ -580,6 +569,18 @@ def test_cache_from_source_respects_pycache_prefix_relative(self): self.util.cache_from_source(path, optimization=''), os.path.normpath(expect)) + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag to not be None') + def test_cache_from_source_in_root_with_pycache_prefix(self): + # Regression test for gh-82916 + pycache_prefix = os.path.join(os.path.sep, 'tmp', 'bytecode') + path = 'qux.py' + expect = os.path.join(os.path.sep, 'tmp', 'bytecode', + f'qux.{self.tag}.pyc') + with util.temporary_pycache_prefix(pycache_prefix): + with os_helper.change_cwd('/'): + self.assertEqual(self.util.cache_from_source(path), expect) + @unittest.skipIf(sys.implementation.cache_tag is None, 'requires sys.implementation.cache_tag to not be None') def test_source_from_cache_inside_pycache_prefix(self): @@ -776,31 +777,70 @@ def test_complete_multi_phase_init_module(self): self.run_with_own_gil(script) -class MiscTests(unittest.TestCase): - def test_atomic_write_should_notice_incomplete_writes(self): +class PatchAtomicWrites: + def __init__(self, truncate_at_length, never_complete=False): + self.truncate_at_length = truncate_at_length + self.never_complete = never_complete + self.seen_write = False + self._children = [] + + def __enter__(self): import _pyio oldwrite = os.write - seen_write = False - - truncate_at_length = 100 # Emulate an os.write that only writes partial data. def write(fd, data): - nonlocal seen_write - seen_write = True - return oldwrite(fd, data[:truncate_at_length]) + if self.seen_write and self.never_complete: + return None + self.seen_write = True + return oldwrite(fd, data[:self.truncate_at_length]) # Need to patch _io to be _pyio, so that io.FileIO is affected by the # os.write patch. - with (support.swap_attr(_bootstrap_external, '_io', _pyio), - support.swap_attr(os, 'write', write)): - with self.assertRaises(OSError): - # Make sure we write something longer than the point where we - # truncate. - content = b'x' * (truncate_at_length * 2) - _bootstrap_external._write_atomic(os_helper.TESTFN, content) - assert seen_write + self.children = [ + support.swap_attr(_bootstrap_external, '_io', _pyio), + support.swap_attr(os, 'write', write) + ] + for child in self.children: + child.__enter__() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + for child in self.children: + child.__exit__(exc_type, exc_val, exc_tb) + + +class MiscTests(unittest.TestCase): + + def test_atomic_write_retries_incomplete_writes(self): + truncate_at_length = 100 + length = truncate_at_length * 2 + + with PatchAtomicWrites(truncate_at_length=truncate_at_length) as cm: + # Make sure we write something longer than the point where we + # truncate. + content = b'x' * length + _bootstrap_external._write_atomic(os_helper.TESTFN, content) + self.assertTrue(cm.seen_write) + + self.assertEqual(os.stat(support.os_helper.TESTFN).st_size, length) + os.unlink(support.os_helper.TESTFN) + + def test_atomic_write_errors_if_unable_to_complete(self): + truncate_at_length = 100 + + with ( + PatchAtomicWrites( + truncate_at_length=truncate_at_length, never_complete=True, + ) as cm, + self.assertRaises(OSError) + ): + # Make sure we write something longer than the point where we + # truncate. + content = b'x' * (truncate_at_length * 2) + _bootstrap_external._write_atomic(os_helper.TESTFN, content) + self.assertTrue(cm.seen_write) with self.assertRaises(OSError): os.stat(support.os_helper.TESTFN) # Check that the file did not get written. diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py index edbe78545a2536..6399f952f9e912 100644 --- a/Lib/test/test_importlib/util.py +++ b/Lib/test/test_importlib/util.py @@ -15,7 +15,8 @@ import tempfile import types -_testsinglephase = import_helper.import_module("_testsinglephase") +# gh-116303: Skip test module dependent tests if test modules are unavailable +import_helper.import_module("_testmultiphase") BUILTINS = types.SimpleNamespace() @@ -291,6 +292,9 @@ def writes_bytecode_files(fxn): tests that require it to be set to False.""" if sys.dont_write_bytecode: return unittest.skip("relies on writing bytecode")(fxn) + if sys.implementation.cache_tag is None: + return unittest.skip("requires sys.implementation.cache_tag to not be None")(fxn) + @functools.wraps(fxn) def wrapper(*args, **kwargs): original = sys.dont_write_bytecode diff --git a/Lib/test/test_inspect/inspect_fodder2.py b/Lib/test/test_inspect/inspect_fodder2.py index 43fda6622537fc..157e12167b5d27 100644 --- a/Lib/test/test_inspect/inspect_fodder2.py +++ b/Lib/test/test_inspect/inspect_fodder2.py @@ -369,3 +369,35 @@ class dc364: # line 369 dc370 = dataclasses.make_dataclass('dc370', (('x', int), ('y', int))) dc371 = dataclasses.make_dataclass('dc370', (('x', int), ('y', int)), module=__name__) + +import inspect +import itertools + +# line 376 +ge377 = ( + inspect.currentframe() + for i in itertools.count() +) + +# line 382 +def func383(): + # line 384 + ge385 = ( + inspect.currentframe() + for i in itertools.count() + ) + return ge385 + +# line 391 +@decorator +# comment +def func394(): + return 395 + +# line 397 +@decorator + +def func400(): + return 401 + +pass # end of file diff --git a/Lib/test/test_inspect/inspect_fodder3.py b/Lib/test/test_inspect/inspect_fodder3.py new file mode 100644 index 00000000000000..ea2481edf938c2 --- /dev/null +++ b/Lib/test/test_inspect/inspect_fodder3.py @@ -0,0 +1,39 @@ +from functools import cached_property + +# docstring in parent, inherited in child +class ParentInheritDoc: + @cached_property + def foo(self): + """docstring for foo defined in parent""" + +class ChildInheritDoc(ParentInheritDoc): + pass + +class ChildInheritDefineDoc(ParentInheritDoc): + @cached_property + def foo(self): + pass + +# Redefine foo as something other than cached_property +class ChildPropertyFoo(ParentInheritDoc): + @property + def foo(self): + """docstring for the property foo""" + +class ChildMethodFoo(ParentInheritDoc): + def foo(self): + """docstring for the method foo""" + +# docstring in child but not parent +class ParentNoDoc: + @cached_property + def foo(self): + pass + +class ChildNoDoc(ParentNoDoc): + pass + +class ChildDefineDoc(ParentNoDoc): + @cached_property + def foo(self): + """docstring for foo defined in child""" diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 30e01b8cd87a75..7351f97fd9a4b5 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -46,6 +46,7 @@ from test.test_inspect import inspect_fodder as mod from test.test_inspect import inspect_fodder2 as mod2 +from test.test_inspect import inspect_fodder3 as mod3 from test.test_inspect import inspect_stringized_annotations from test.test_inspect import inspect_deferred_annotations @@ -148,6 +149,38 @@ def meth_self_o(self, object, /): pass def meth_type_noargs(type, /): pass def meth_type_o(type, object, /): pass +# Decorator decorator that returns a simple wrapped function +def identity_wrapper(func): + @functools.wraps(func) + def wrapped(*args, **kwargs): + return func(*args, **kwargs) + return wrapped + +# Original signature of the simple wrapped function returned by +# identity_wrapper(). +varargs_signature = ( + (('args', ..., ..., 'var_positional'), + ('kwargs', ..., ..., 'var_keyword')), + ..., +) + +# Decorator decorator that returns a simple descriptor +class custom_descriptor: + def __init__(self, func): + self.func = func + + def __get__(self, instance, owner): + return self.func.__get__(instance, owner) + + +class TestImportTime(unittest.TestCase): + + @cpython_only + def test_lazy_import(self): + import_helper.ensure_lazy_imports( + "inspect", {"re", "tokenize"} + ) + class TestPredicates(IsTestBase): @@ -665,10 +698,56 @@ def test_getdoc_inherited(self): self.assertEqual(inspect.getdoc(mod.FesteringGob.contradiction), 'The automatic gainsaying.') + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + def test_getdoc_inherited_class_doc(self): + class A: + """Common base class""" + class B(A): + pass + + a = A() + self.assertEqual(inspect.getdoc(A), 'Common base class') + self.assertEqual(inspect.getdoc(A, inherit_class_doc=False), + 'Common base class') + self.assertEqual(inspect.getdoc(a), 'Common base class') + self.assertIsNone(inspect.getdoc(a, fallback_to_class_doc=False)) + a.__doc__ = 'Instance' + self.assertEqual(inspect.getdoc(a, fallback_to_class_doc=False), + 'Instance') + + b = B() + self.assertEqual(inspect.getdoc(B), 'Common base class') + self.assertIsNone(inspect.getdoc(B, inherit_class_doc=False)) + self.assertIsNone(inspect.getdoc(b)) + self.assertIsNone(inspect.getdoc(b, fallback_to_class_doc=False)) + b.__doc__ = 'Instance' + self.assertEqual(inspect.getdoc(b, fallback_to_class_doc=False), 'Instance') + + def test_getdoc_inherited_cached_property(self): + doc = inspect.getdoc(mod3.ParentInheritDoc.foo) + self.assertEqual(doc, 'docstring for foo defined in parent') + self.assertEqual(inspect.getdoc(mod3.ChildInheritDoc.foo), doc) + self.assertEqual(inspect.getdoc(mod3.ChildInheritDefineDoc.foo), doc) + + def test_getdoc_redefine_cached_property_as_other(self): + self.assertEqual(inspect.getdoc(mod3.ChildPropertyFoo.foo), + 'docstring for the property foo') + self.assertEqual(inspect.getdoc(mod3.ChildMethodFoo.foo), + 'docstring for the method foo') + + def test_getdoc_define_cached_property(self): + self.assertEqual(inspect.getdoc(mod3.ChildDefineDoc.foo), + 'docstring for foo defined in child') + + def test_getdoc_nodoc_inherited(self): + self.assertIsNone(inspect.getdoc(mod3.ChildNoDoc.foo)) + @unittest.skipIf(MISSING_C_DOCSTRINGS, "test requires docstrings") def test_finddoc(self): finddoc = inspect._finddoc self.assertEqual(finddoc(int), int.__doc__) + self.assertIsNone(finddoc(int, search_in_class=False)) self.assertEqual(finddoc(int.to_bytes), int.to_bytes.__doc__) self.assertEqual(finddoc(int().to_bytes), int.to_bytes.__doc__) self.assertEqual(finddoc(int.from_bytes), int.from_bytes.__doc__) @@ -1189,12 +1268,22 @@ def test_nested_class_definition_inside_async_function(self): self.assertSourceEqual(run(mod2.func225), 226, 227) self.assertSourceEqual(mod2.cls226, 231, 235) + self.assertSourceEqual(mod2.cls226.func232, 232, 235) self.assertSourceEqual(run(mod2.cls226().func232), 233, 234) def test_class_definition_same_name_diff_methods(self): self.assertSourceEqual(mod2.cls296, 296, 298) self.assertSourceEqual(mod2.cls310, 310, 312) + def test_generator_expression(self): + self.assertSourceEqual(next(mod2.ge377), 377, 380) + self.assertSourceEqual(next(mod2.func383()), 385, 388) + + def test_comment_or_empty_line_after_decorator(self): + self.assertSourceEqual(mod2.func394, 392, 395) + self.assertSourceEqual(mod2.func400, 398, 401) + + class TestNoEOL(GetSourceBase): def setUp(self): self.tempdir = TESTFN + '_dir' @@ -1266,9 +1355,10 @@ def assertFullArgSpecEquals(self, routine, args_e, varargs_e=None, varkw_e=None, defaults_e=None, posonlyargs_e=[], kwonlyargs_e=[], kwonlydefaults_e=None, - ann_e={}): + ann_e={}, + annotation_format=Format.VALUE): args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \ - inspect.getfullargspec(routine) + inspect.getfullargspec(routine, annotation_format=annotation_format) self.assertEqual(args, args_e) self.assertEqual(varargs, varargs_e) self.assertEqual(varkw, varkw_e) @@ -1301,6 +1391,19 @@ def test_getfullargspec(self): kwonlyargs_e=['e', 'f'], kwonlydefaults_e={'e': 4, 'f': 5}) + def get_getfullargspec_with_undefined_names_in_annotations(self): + def my_func(a: undefined_name): + pass + + with self.assertRaises(NameError): + inspect.getfullargspec(my_func) + + self.assertFullArgSpecEquals(my_func, ['a'], ann_e={'a': 'undefined_name'}, + annotation_format=Format.STRING) + + arg_spec = inspect.getfullargspec(my_func, annotation_format=Format.FORWARDREF) + self.assertIsInstance(arg_spec.annotations['a'], ForwardRef) + def test_argspec_api_ignores_wrapped(self): # Issue 20684: low level introspection API must ignore __wrapped__ @functools.wraps(mod.spam) @@ -1749,14 +1852,55 @@ class C(metaclass=M): class TestFormatAnnotation(unittest.TestCase): def test_typing_replacement(self): - from test.typinganndata.ann_module9 import ann, ann1 + from test.typinganndata.ann_module9 import A, ann, ann1 self.assertEqual(inspect.formatannotation(ann), 'List[str] | int') self.assertEqual(inspect.formatannotation(ann1), 'List[testModule.typing.A] | int') + self.assertEqual(inspect.formatannotation(A, 'testModule.typing'), 'A') + self.assertEqual(inspect.formatannotation(A, 'other'), 'testModule.typing.A') + self.assertEqual( + inspect.formatannotation(ann1, 'testModule.typing'), + 'List[testModule.typing.A] | int', + ) + def test_forwardref(self): fwdref = ForwardRef('fwdref') self.assertEqual(inspect.formatannotation(fwdref), 'fwdref') + def test_formatannotationrelativeto(self): + from test.typinganndata.ann_module9 import A, ann1 + + # Builtin types: + self.assertEqual( + inspect.formatannotationrelativeto(object)(type), + 'type', + ) + + # Custom types: + self.assertEqual( + inspect.formatannotationrelativeto(None)(A), + 'testModule.typing.A', + ) + + class B: ... + B.__module__ = 'testModule.typing' + + self.assertEqual( + inspect.formatannotationrelativeto(B)(A), + 'A', + ) + + self.assertEqual( + inspect.formatannotationrelativeto(object)(A), + 'testModule.typing.A', + ) + + # Not an instance of "type": + self.assertEqual( + inspect.formatannotationrelativeto(A)(ann1), + 'List[testModule.typing.A] | int', + ) + class TestIsMethodDescriptor(unittest.TestCase): @@ -2683,6 +2827,30 @@ def running_check_generator(): # Running after the first yield next(self.generator) + def test_types_coroutine_wrapper_state(self): + def gen(): + yield 1 + yield 2 + + @types.coroutine + def wrapped_generator_coro(): + # return a generator iterator so types.coroutine + # wraps it into types._GeneratorWrapper. + return gen() + + g = wrapped_generator_coro() + self.addCleanup(g.close) + self.assertIs(type(g), types._GeneratorWrapper) + + # _GeneratorWrapper must provide gi_suspended/cr_suspended + # so inspect.get*state() doesn't raise AttributeError. + self.assertEqual(inspect.getgeneratorstate(g), inspect.GEN_CREATED) + self.assertEqual(inspect.getcoroutinestate(g), inspect.CORO_CREATED) + + next(g) + self.assertEqual(inspect.getgeneratorstate(g), inspect.GEN_SUSPENDED) + self.assertEqual(inspect.getcoroutinestate(g), inspect.CORO_SUSPENDED) + def test_easy_debugging(self): # repr() and str() of a generator state should contain the state name names = 'GEN_CREATED GEN_RUNNING GEN_SUSPENDED GEN_CLOSED'.split() @@ -4021,44 +4189,272 @@ def __init__(self, b): ('bar', 2, ..., "keyword_only")), ...)) - def test_signature_on_class_with_decorated_new(self): - def identity(func): - @functools.wraps(func) - def wrapped(*args, **kwargs): - return func(*args, **kwargs) - return wrapped - - class Foo: - @identity - def __new__(cls, a, b): + def test_signature_on_class_with_wrapped_metaclass_call(self): + class CM(type): + @identity_wrapper + def __call__(cls, a): + pass + class C(metaclass=CM): + def __init__(self, b): pass - self.assertEqual(self.signature(Foo), - ((('a', ..., ..., "positional_or_keyword"), - ('b', ..., ..., "positional_or_keyword")), + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), ...)) - self.assertEqual(self.signature(Foo.__new__), - ((('cls', ..., ..., "positional_or_keyword"), - ('a', ..., ..., "positional_or_keyword"), - ('b', ..., ..., "positional_or_keyword")), - ...)) + with self.subTest('classmethod'): + class CM(type): + @classmethod + @identity_wrapper + def __call__(cls, a): + return a + class C(metaclass=CM): + def __init__(self, b): + pass - class Bar: - __new__ = identity(object.__new__) + self.assertEqual(C(1), 1) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) - varargs_signature = ( - (('args', ..., ..., 'var_positional'), - ('kwargs', ..., ..., 'var_keyword')), - ..., - ) + with self.subTest('staticmethod'): + class CM(type): + @staticmethod + @identity_wrapper + def __call__(a): + return a + class C(metaclass=CM): + def __init__(self, b): + pass + + self.assertEqual(C(1), 1) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('MethodType'): + class A: + @identity_wrapper + def call(self, a): + return a + class CM(type): + __call__ = A().call + class C(metaclass=CM): + def __init__(self, b): + pass + + self.assertEqual(C(1), 1) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('descriptor'): + class CM(type): + @custom_descriptor + @identity_wrapper + def __call__(self, a): + return a + class C(metaclass=CM): + def __init__(self, b): + pass + + self.assertEqual(C(1), 1) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + self.assertEqual(self.signature(C.__call__), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + self.assertEqual(self.signature(C, follow_wrapped=False), + varargs_signature) + self.assertEqual(self.signature(C.__call__, follow_wrapped=False), + varargs_signature) + + def test_signature_on_class_with_wrapped_init(self): + class C: + @identity_wrapper + def __init__(self, b): + pass + + C(1) # does not raise + self.assertEqual(self.signature(C), + ((('b', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('classmethod'): + class C: + @classmethod + @identity_wrapper + def __init__(cls, b): + pass + + C(1) # does not raise + self.assertEqual(self.signature(C), + ((('b', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('staticmethod'): + class C: + @staticmethod + @identity_wrapper + def __init__(b): + pass + + C(1) # does not raise + self.assertEqual(self.signature(C), + ((('b', ..., ..., "positional_or_keyword"),), + ...)) - self.assertEqual(self.signature(Bar), ((), ...)) - self.assertEqual(self.signature(Bar.__new__), varargs_signature) - self.assertEqual(self.signature(Bar, follow_wrapped=False), - varargs_signature) - self.assertEqual(self.signature(Bar.__new__, follow_wrapped=False), - varargs_signature) + with self.subTest('MethodType'): + class A: + @identity_wrapper + def call(self, a): + pass + + class C: + __init__ = A().call + + C(1) # does not raise + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('partial'): + class C: + __init__ = functools.partial(identity_wrapper(lambda x, a, b: None), 2) + + C(1) # does not raise + self.assertEqual(self.signature(C), + ((('b', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('partialmethod'): + class C: + @identity_wrapper + def _init(self, x, a): + self.a = (x, a) + __init__ = functools.partialmethod(_init, 2) + + self.assertEqual(C(1).a, (2, 1)) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('descriptor'): + class C: + @custom_descriptor + @identity_wrapper + def __init__(self, a): + pass + + C(1) # does not raise + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + self.assertEqual(self.signature(C.__init__), + ((('self', ..., ..., "positional_or_keyword"), + ('a', ..., ..., "positional_or_keyword")), + ...)) + + self.assertEqual(self.signature(C, follow_wrapped=False), + varargs_signature) + if support.MISSING_C_DOCSTRINGS: + self.assertRaisesRegex( + ValueError, "no signature found", + self.signature, C.__new__, follow_wrapped=False, + ) + else: + self.assertEqual(self.signature(C.__new__, follow_wrapped=False), + varargs_signature) + + def test_signature_on_class_with_wrapped_new(self): + with self.subTest('FunctionType'): + class C: + @identity_wrapper + def __new__(cls, a): + return a + + self.assertEqual(C(1), 1) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('classmethod'): + class C: + @classmethod + @identity_wrapper + def __new__(cls, cls2, a): + return a + + self.assertEqual(C(1), 1) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('staticmethod'): + class C: + @staticmethod + @identity_wrapper + def __new__(cls, a): + return a + + self.assertEqual(C(1), 1) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('MethodType'): + class A: + @identity_wrapper + def call(self, cls, a): + return a + class C: + __new__ = A().call + + self.assertEqual(C(1), 1) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('partial'): + class C: + __new__ = functools.partial(identity_wrapper(lambda x, cls, a: (x, a)), 2) + + self.assertEqual(C(1), (2, 1)) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('partialmethod'): + class C: + __new__ = functools.partialmethod(identity_wrapper(lambda cls, x, a: (x, a)), 2) + + self.assertEqual(C(1), (2, 1)) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + + with self.subTest('descriptor'): + class C: + @custom_descriptor + @identity_wrapper + def __new__(cls, a): + return a + + self.assertEqual(C(1), 1) + self.assertEqual(self.signature(C), + ((('a', ..., ..., "positional_or_keyword"),), + ...)) + self.assertEqual(self.signature(C.__new__), + ((('cls', ..., ..., "positional_or_keyword"), + ('a', ..., ..., "positional_or_keyword")), + ...)) + + self.assertEqual(self.signature(C, follow_wrapped=False), + varargs_signature) + self.assertEqual(self.signature(C.__new__, follow_wrapped=False), + varargs_signature) def test_signature_on_class_with_init(self): class C: @@ -5754,7 +6150,8 @@ def _test_builtin_methods_have_signatures(self, cls, no_signature, unsupported_s self.assertRaises(ValueError, inspect.signature, getattr(cls, name)) def test_builtins_have_signatures(self): - no_signature = {'type', 'super', 'bytearray', 'bytes', 'dict', 'int', 'str'} + no_signature = {'type', 'super', 'bytearray', 'bytes', + 'dict', 'frozendict', 'int', 'str'} # These need PEP 457 groups needs_groups = {"range", "slice", "dir", "getattr", "next", "iter", "vars"} @@ -5831,6 +6228,21 @@ def test_collections_abc_module_has_signatures(self): import collections.abc self._test_module_has_signatures(collections.abc) + def test_datetime_module_has_signatures(self): + # Only test if the C implementation is available. + import_helper.import_module('_datetime') + import datetime + no_signature = {'tzinfo'} + unsupported_signature = {'timezone'} + methods_unsupported_signature = { + 'date': {'replace'}, + 'time': {'replace'}, + 'datetime': {'replace', 'combine'}, + } + self._test_module_has_signatures(datetime, + no_signature, unsupported_signature, + methods_unsupported_signature=methods_unsupported_signature) + def test_errno_module_has_signatures(self): import errno self._test_module_has_signatures(errno) @@ -5843,8 +6255,7 @@ def test_faulthandler_module_has_signatures(self): self._test_module_has_signatures(faulthandler, unsupported_signature=unsupported_signature) def test_functools_module_has_signatures(self): - unsupported_signature = {"reduce"} - self._test_module_has_signatures(functools, unsupported_signature=unsupported_signature) + self._test_module_has_signatures(functools) def test_gc_module_has_signatures(self): import gc @@ -5878,8 +6289,7 @@ def test_operator_module_has_signatures(self): def test_os_module_has_signatures(self): unsupported_signature = {'chmod', 'utime'} unsupported_signature |= {name for name in - ['get_terminal_size', 'link', 'posix_spawn', 'posix_spawnp', - 'register_at_fork', 'startfile'] + ['get_terminal_size', 'link', 'register_at_fork', 'startfile'] if hasattr(os, name)} self._test_module_has_signatures(os, unsupported_signature=unsupported_signature) @@ -5889,7 +6299,10 @@ def test_pwd_module_has_signatures(self): def test_re_module_has_signatures(self): import re - methods_no_signature = {'Match': {'group'}} + methods_no_signature = { + 'Match': {'group'}, + 'Pattern': {'match'}, # It is now an alias for prefixmatch + } self._test_module_has_signatures(re, methods_no_signature=methods_no_signature, good_exceptions={'error', 'PatternError'}) @@ -5902,6 +6315,10 @@ def test_stat_module_has_signatures(self): import stat self._test_module_has_signatures(stat) + def test_struct_module_has_signatures(self): + import struct + self._test_module_has_signatures(struct) + def test_string_module_has_signatures(self): import string self._test_module_has_signatures(string) @@ -6089,8 +6506,19 @@ def test_wrapped_descriptor(self): self.assertIs(inspect.unwrap(staticmethod(classmethod)), classmethod) self.assertIs(inspect.unwrap(classmethod(staticmethod)), staticmethod) +def _clean_object_ids(text): + # Helper to handle "<obj at 0x...>" details in CLI output checks + import re + detect = r"object at 0x([0-9A-Fa-f]+)>" + replace = "object at 0x...>" + return re.sub(detect, replace, text) + +class TestModuleCLI(unittest.TestCase): + + BUILTIN_ERROR = "No source code available for builtin module" + NO_SOURCE_ERROR = "No source code available for defining module" + NO_SOURCE_TARGET_ERROR = "Failed to retrieve source code for given target" -class TestMain(unittest.TestCase): def test_only_source(self): module = importlib.import_module('unittest') rc, out, err = assert_python_ok('-m', 'inspect', @@ -6118,27 +6546,223 @@ def test_qualname_source(self): inspect.getsource(ThreadPoolExecutor).splitlines()) self.assertEqual(err, b'') - def test_builtins(self): + def test_error_builtins(self): _, out, err = assert_python_failure('-m', 'inspect', 'sys') lines = err.decode().splitlines() - self.assertEqual(lines, ["Can't get info for builtin modules."]) + self.assertEqual(lines, [self.BUILTIN_ERROR]) - def test_details(self): - module = importlib.import_module('unittest') + def test_error_extension(self): + module_name = "_testcapi" + if module_name in sys.builtin_module_names: + # WASI test environment has even _testcapi as a builtin module + expected_error = self.BUILTIN_ERROR + else: + expected_error = self.NO_SOURCE_ERROR + _, out, err = assert_python_failure('-m', 'inspect', + module_name) + lines = err.decode().splitlines() + self.assertEqual(lines, [expected_error]) + + def test_error_data(self): + _, out, err = assert_python_failure('-m', 'inspect', + 'importlib.machinery:SOURCE_SUFFIXES') + lines = err.decode().splitlines() + self.assertEqual(lines, [self.NO_SOURCE_TARGET_ERROR]) + + def test_details_option_with_package(self): + module_name = 'unittest' + module = importlib.import_module(module_name) args = support.optim_args_from_interpreter_flags() rc, out, err = assert_python_ok(*args, '-m', 'inspect', - 'unittest', '--details') - output = out.decode() - # Just a quick sanity check on the output - self.assertIn(module.__spec__.name, output) - self.assertIn(module.__name__, output) - self.assertIn(module.__spec__.origin, output) - self.assertIn(module.__file__, output) - self.assertIn(module.__spec__.cached, output) - self.assertIn(module.__cached__, output) + module_name, '--details') + # Full rendering check on the expected output + expected_lines = [ + f"Target: {module.__name__}", # No aliasing + f"Origin: {module.__spec__.origin}", + f"Source: {module.__file__}", + f"Cached: {module.__spec__.cached}", # None is still displayed + f"Loader: {_clean_object_ids(repr(module.__spec__.loader))}", + f"Submodule search paths: {module.__path__}", + "", + ] + output_lines = _clean_object_ids(out.decode()).splitlines() + self.assertEqual(output_lines, expected_lines) self.assertEqual(err, b'') + def test_details_option_with_builtin_module(self): + # Also an end-to-end test of non-package lookups + module_name = 'sys' + module = importlib.import_module(module_name) + args = support.optim_args_from_interpreter_flags() + rc, out, err = assert_python_ok(*args, '-m', 'inspect', + module_name, '--details') + # Full rendering check on the expected output + # No error is reported when just fetching the module details + expected_lines = [ + f"Target: {module.__name__}", # No aliasing + f"Origin: {module.__spec__.origin}", + "Source: None", + "Cached: None", + f"Loader: {_clean_object_ids(repr(module.__spec__.loader))}", + "", + ] + output_lines = _clean_object_ids(out.decode()).splitlines() + self.assertEqual(output_lines, expected_lines) + self.assertEqual(err, b'') + + def test_details_option_with_data_target(self): + # Also an end-to-end test of non-module lookups without aliasing + module_name = 'importlib.machinery' + cli_target = f"{module_name}:SOURCE_SUFFIXES" + module = importlib.import_module(module_name) + args = support.optim_args_from_interpreter_flags() + rc, out, err = assert_python_ok(*args, '-m', 'inspect', + cli_target, '--details') + # Full rendering check on the expected output + # The error is only informational when reading source details + expected_lines = [ + f"Target: {cli_target}", # No aliasing + f"Origin: {module.__spec__.origin}", + f"Source: {module.__file__}", + f"Cached: {module.__spec__.cached}", # None is still displayed + self.NO_SOURCE_TARGET_ERROR, + "", + ] + output_lines = out.decode().splitlines() + self.assertEqual(output_lines, expected_lines) + self.assertEqual(err, b'') + + @unittest.skipIf(not os.path.exists(os.path.__file__), "Needs frozen source file") + def test_details_option_with_aliased_target(self): + # Also an end-to-end test of successful non-module lookups + module = importlib.import_module("os.path") + target = module.join + cli_target = "os:path.join" # Defining module is os.path, not os + defining_target = f"{target.__module__}:{target.__qualname__}" + + args = support.optim_args_from_interpreter_flags() + rc, out, err = assert_python_ok(*args, '-m', 'inspect', + cli_target, '--details') + # Full rendering check on the expected output + expected_lines = [ + f'Target: {defining_target} (looked up as "{cli_target}")', + f"Origin: {module.__spec__.origin}", + f"Source: {module.__file__}", + f"Cached: {module.__spec__.cached}", # None is still displayed + f"Line: {inspect.findsource(target)[1]}", + "", + ] + output_lines = out.decode().splitlines() + self.assertEqual(output_lines, expected_lines) + self.assertEqual(err, b'') + + def _check_details(self, module, details, other_expected_keys=(), *, alias=None, error=None): + expected_keys = {"target", "origin", "source", "cached"} + if other_expected_keys: + expected_keys |= other_expected_keys + if alias is not None: + expected_keys.add("alias") + if error is not None: + expected_keys.add("error") + self.assertEqual(set(details.keys()), expected_keys) + self.assertEqual(module.__spec__.origin, details["origin"]) + try: + expected_source = inspect.getsourcefile(module) + except Exception: + expected_source = None + if expected_source and expected_source.startswith("<frozen"): + # Check special case for frozen modules + expected_source = module.__file__ + self.assertEqual(expected_source, details["source"]) + self.assertEqual(module.__spec__.cached, details["cached"]) + if "loader" in other_expected_keys: + self.assertEqual(repr(module.__spec__.loader), details["loader"]) + if "submodule_paths" in other_expected_keys: + self.assertEqual(repr(module.__path__), details["submodule_paths"]) + if alias is not None: + self.assertEqual(details["alias"], alias) + self.assertNotEqual(details["target"], alias) + if error is not None: + self.assertEqual(details["error"], error) + + def test_get_cli_details_for_source_module(self): + module_name = "inspect" + module = importlib.import_module(module_name) + details = inspect._get_details_for_cli(module, module_name, module) + self._check_details(module, details, {"loader"}) + target = module.signature + nominal_target = f"{module_name}:{target.__qualname__}" + details = inspect._get_details_for_cli(module, nominal_target, target) + self._check_details(module, details, {"lineno"}) + self.assertEqual(inspect.findsource(target)[1], details["lineno"]) + + def test_get_cli_details_for_source_package(self): + module_name = "importlib" + module = importlib.import_module(module_name) + details = inspect._get_details_for_cli(module, module_name, module) + self._check_details(module, details, {"loader", "submodule_paths"}) + target = module.import_module # Assumes this is not re-exported + nominal_target = f"{module_name}:{target.__qualname__}" + details = inspect._get_details_for_cli(module, nominal_target, target) + self._check_details(module, details, {"lineno"}) + self.assertEqual(inspect.findsource(target)[1], details["lineno"]) + + def test_get_cli_details_for_builtin_module(self): + expected_error = self.BUILTIN_ERROR + module_name = "sys" + module = importlib.import_module(module_name) + details = inspect._get_details_for_cli(module, module_name, module) + self._check_details(module, details, {"loader"}, error=expected_error) + target = module.exit + nominal_target = f"{module_name}:{target.__qualname__}" + details = inspect._get_details_for_cli(module, nominal_target, target) + self._check_details(module, details, error=expected_error) + + def test_get_cli_details_for_frozen_module(self): + # Source is actually available for this frozen module, as + # __file__ refers to the location of importlib._bootstrap + module_name = "_frozen_importlib" + module = importlib.import_module(module_name) + details = inspect._get_details_for_cli(module, module_name, module) + self._check_details(module, details, {"loader"}, alias=module_name) + target = module.__import__ + nominal_target = f"{module_name}:{target.__qualname__}" + details = inspect._get_details_for_cli(module, nominal_target, target) + self._check_details(module, details, {"lineno"}, alias=nominal_target) + self.assertEqual(inspect.findsource(target)[1], details["lineno"]) + + def test_get_cli_details_for_extension_module(self): + module_name = "_testcapi" + if module_name in sys.builtin_module_names: + # WASI test environment has even _testcapi as a builtin module + expected_error = self.BUILTIN_ERROR + else: + expected_error = self.NO_SOURCE_ERROR + module = importlib.import_module(module_name) + details = inspect._get_details_for_cli(module, module_name, module) + self._check_details(module, details, {"loader"}, error=expected_error) + target = module.fatal_error + nominal_target = f"{module_name}:{target.__qualname__}" + details = inspect._get_details_for_cli(module, nominal_target, target) + self._check_details(module, details, error=expected_error) + + @unittest.skipIf(not os.path.exists(os.path.__file__), "Needs frozen source file") + def test_get_cli_details_for_aliased_module(self): + # os.path is an alias for a platform dependent implementation module + # Test is skipped if the source file is missing (as the output changes), + # which may happen if running the test suite after deployment. + module_name = "os.path" + module = importlib.import_module(module_name) + details = inspect._get_details_for_cli(module, module_name, module) + self._check_details(module, details, {"loader"}, alias=module_name) + nominal_module = importlib.import_module("os") + nominal_target = "os:path.join" + target = module.join + details = inspect._get_details_for_cli(nominal_module, nominal_target, target) + self._check_details(module, details, {"lineno"}, alias=nominal_target) + self.assertEqual(inspect.findsource(target)[1], details["lineno"]) + class TestReload(unittest.TestCase): @@ -6231,7 +6855,5 @@ def f(): self.assertIn(expected, output) - - if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index 7a7cb73f673787..fa675f626cbe10 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -778,6 +778,18 @@ def test_pylong_int_divmod(self): a, b = divmod(n*3 + 1, n) assert a == 3 and b == 1 + @support.cpython_only # tests implementation details of CPython. + @unittest.skipUnless(_pylong, "_pylong module required") + def test_pylong_int_divmod_crash(self): + # Regression test for https://github.com/python/cpython/issues/142554. + bad_int_divmod = lambda a, b: (1,) + # 'k' chosen such that divmod(2**(2*k), 2**k) uses _pylong.int_divmod() + k = 10_000 + a, b = (1 << (2 * k)), (1 << k) + with mock.patch.object(_pylong, "int_divmod", wraps=bad_int_divmod): + msg = r"tuple of length 2 is required from int_divmod\(\)" + self.assertRaisesRegex(ValueError, msg, divmod, a, b) + def test_pylong_str_to_int(self): v1 = 1 << 100_000 s = str(v1) diff --git a/Lib/test/test_interpreters/test_api.py b/Lib/test/test_interpreters/test_api.py index a34b20beaca7a3..13d23af5aceb47 100644 --- a/Lib/test/test_interpreters/test_api.py +++ b/Lib/test/test_interpreters/test_api.py @@ -1,6 +1,7 @@ import contextlib import os import pickle +import signal import sys from textwrap import dedent import threading @@ -11,6 +12,7 @@ from test.support import os_helper from test.support import script_helper from test.support import import_helper +from test.support.script_helper import assert_python_ok, spawn_python # Raise SkipTest if subinterpreters not supported. _interpreters = import_helper.import_module('_interpreters') from concurrent import interpreters @@ -418,6 +420,52 @@ def test_pickle(self): unpickled = pickle.loads(data) self.assertEqual(unpickled, interp) + @support.requires_subprocess() + @force_not_colorized + def test_cleanup_in_repl(self): + # GH-135729: Using a subinterpreter in the REPL would lead to an unraisable + # exception during finalization + repl = script_helper.spawn_python("-i") + script = b"""if True: + from concurrent import interpreters + interpreters.create() + exit()""" + stdout, stderr = repl.communicate(script) + self.assertIsNone(stderr) + self.assertIn(b"Interpreter.close()", stdout) + self.assertNotIn(b"Traceback", stdout) + + @support.requires_subprocess() + @unittest.skipIf(os.name == 'nt', "signals don't work well on windows") + def test_keyboard_interrupt_in_thread_running_interp(self): + import subprocess + source = f"""if True: + from concurrent import interpreters + from threading import Thread + + def test(): + import time + print('a', flush=True, end='') + time.sleep(10) + + interp = interpreters.create() + interp.call_in_thread(test) + """ + + with spawn_python("-c", source, stderr=subprocess.PIPE) as proc: + self.assertEqual(proc.stdout.read(1), b'a') + proc.send_signal(signal.SIGINT) + proc.stderr.flush() + error = proc.stderr.read() + self.assertIn(b"KeyboardInterrupt", error) + retcode = proc.wait() + # Sometimes we send the SIGINT after the subthread yields the GIL to + # the main thread, which results in the main thread getting the + # KeyboardInterrupt before finalization is reached. There's not + # any great way to protect against that, so we just allow a -2 + # return code as well. + self.assertIn(retcode, (0, -signal.SIGINT)) + class TestInterpreterIsRunning(TestBase): @@ -707,6 +755,68 @@ def test_created_with_capi(self): self.interp_exists(interpid)) + def test_remaining_threads(self): + r_interp, w_interp = self.pipe() + + FINISHED = b'F' + + # It's unlikely, but technically speaking, it's possible + # that the thread could've finished before interp.close() is + # reached, so this test might not properly exercise the case. + # However, it's quite unlikely and probably not worth bothering about. + interp = interpreters.create() + interp.exec(f"""if True: + import os + import threading + import time + + def task(): + time.sleep(1) + os.write({w_interp}, {FINISHED!r}) + + threads = (threading.Thread(target=task) for _ in range(3)) + for t in threads: + t.start() + """) + interp.close() + + self.assertEqual(os.read(r_interp, 1), FINISHED) + + def test_remaining_daemon_threads(self): + # Daemon threads leak reference by nature, because they hang threads + # without allowing them to do cleanup (i.e., release refs). + # To prevent that from messing up the refleak hunter and whatnot, we + # run this in a subprocess. + code = '''if True: + import _interpreters + import types + interp = _interpreters.create( + types.SimpleNamespace( + use_main_obmalloc=False, + allow_fork=False, + allow_exec=False, + allow_threads=True, + allow_daemon_threads=True, + check_multi_interp_extensions=True, + gil='own', + ) + ) + _interpreters.exec(interp, f"""if True: + import threading + import time + + def task(): + time.sleep(3) + + threads = (threading.Thread(target=task, daemon=True) for _ in range(3)) + for t in threads: + t.start() + """) + _interpreters.destroy(interp) + ''' + assert_python_ok('-c', code) + + class TestInterpreterPrepareMain(TestBase): def test_empty(self): @@ -776,7 +886,7 @@ def test_created_with_capi(self): with self.assertRaisesRegex(InterpreterError, 'unrecognized'): interp.prepare_main({'spam': True}) with self.assertRaisesRegex(ExecutionFailed, 'NameError'): - self.run_from_capi(interpid, 'assert spam is True') + self.run_from_capi(interpid, 'spam') class TestInterpreterExec(TestBase): @@ -815,7 +925,10 @@ def script(): spam.eggs() interp = interpreters.create() - interp.exec(script) + try: + interp.exec(script) + finally: + interp.close() """) stdout, stderr = self.assert_python_failure(scriptfile) @@ -824,7 +937,7 @@ def script(): # File "{interpreters.__file__}", line 179, in exec self.assertEqual(stderr, dedent(f"""\ Traceback (most recent call last): - File "{scriptfile}", line 9, in <module> + File "{scriptfile}", line 10, in <module> interp.exec(script) ~~~~~~~~~~~^^^^^^^^ {interpmod_line.strip()} @@ -2204,6 +2317,16 @@ def test_whence(self): whence = eval(text) self.assertEqual(whence, _interpreters.WHENCE_LEGACY_CAPI) + def test_contextvars_missing(self): + script = f""" + import contextvars + print(getattr(contextvars.Token, "MISSING", "'doesn't exist'")) + """ + + orig = _interpreters.create() + text = self.run_and_capture(orig, script) + self.assertEqual(text.strip(), "<Token.MISSING>") + def test_is_running(self): def check(interpid, expected): with self.assertRaisesRegex(InterpreterError, 'unrecognized'): diff --git a/Lib/test/test_interpreters/test_channels.py b/Lib/test/test_interpreters/test_channels.py index 52827357078b85..5437792b5a7014 100644 --- a/Lib/test/test_interpreters/test_channels.py +++ b/Lib/test/test_interpreters/test_channels.py @@ -47,6 +47,12 @@ def test_list_all(self): after = set(channels.list_all()) self.assertEqual(after, created) + def test_list_all_closed(self): + created = [channels.create() for _ in range(3)] + rch, sch = created.pop(1) + rch.close() + self.assertEqual(set(channels.list_all()), set(created)) + def test_shareable(self): interp = interpreters.create() rch, sch = channels.create() diff --git a/Lib/test/test_interpreters/test_lifecycle.py b/Lib/test/test_interpreters/test_lifecycle.py index 15537ac6cc8f82..39c1441b67c133 100644 --- a/Lib/test/test_interpreters/test_lifecycle.py +++ b/Lib/test/test_interpreters/test_lifecycle.py @@ -132,6 +132,7 @@ def test_sys_path_0(self): 'sub': sys.path[0], }}, indent=4), flush=True) """) + interp.close() ''' # <tmp>/ # pkg/ @@ -172,7 +173,10 @@ def test_gh_109793(self): argv = [sys.executable, '-c', '''if True: from concurrent import interpreters interp = interpreters.create() - raise Exception + try: + raise Exception + finally: + interp.close() '''] proc = subprocess.run(argv, capture_output=True, text=True) self.assertIn('Traceback', proc.stderr) diff --git a/Lib/test/test_interpreters/test_queues.py b/Lib/test/test_interpreters/test_queues.py index 5451c6654acb47..77334aea3836b9 100644 --- a/Lib/test/test_interpreters/test_queues.py +++ b/Lib/test/test_interpreters/test_queues.py @@ -11,7 +11,7 @@ from concurrent.interpreters import _queues as queues, _crossinterp from .utils import _run_output, TestBase as _TestBase - +HUGE_TIMEOUT = 3600 REPLACE = _crossinterp._UNBOUND_CONSTANT_TO_FLAG[_crossinterp.UNBOUND] @@ -306,6 +306,8 @@ def test_put_timeout(self): queue.put(None) with self.assertRaises(queues.QueueFull): queue.put(None, timeout=0.1) + with self.assertRaises(queues.QueueFull): + queue.put(None, HUGE_TIMEOUT, 0.1) queue.get() queue.put(None) @@ -315,6 +317,10 @@ def test_put_nowait(self): queue.put_nowait(None) with self.assertRaises(queues.QueueFull): queue.put_nowait(None) + with self.assertRaises(queues.QueueFull): + queue.put(None, False) + with self.assertRaises(queues.QueueFull): + queue.put(None, False, timeout=HUGE_TIMEOUT) queue.get() queue.put_nowait(None) @@ -345,11 +351,17 @@ def test_get_timeout(self): queue = queues.create() with self.assertRaises(queues.QueueEmpty): queue.get(timeout=0.1) + with self.assertRaises(queues.QueueEmpty): + queue.get(HUGE_TIMEOUT, 0.1) def test_get_nowait(self): queue = queues.create() with self.assertRaises(queues.QueueEmpty): queue.get_nowait() + with self.assertRaises(queues.QueueEmpty): + queue.get(False) + with self.assertRaises(queues.QueueEmpty): + queue.get(False, timeout=HUGE_TIMEOUT) def test_put_get_full_fallback(self): expected = list(range(20)) diff --git a/Lib/test/test_interpreters/test_stress.py b/Lib/test/test_interpreters/test_stress.py index e25e67a0d4f445..6b40a536bd3c31 100644 --- a/Lib/test/test_interpreters/test_stress.py +++ b/Lib/test/test_interpreters/test_stress.py @@ -7,6 +7,7 @@ # Raise SkipTest if subinterpreters not supported. import_helper.import_module('_interpreters') from concurrent import interpreters +from concurrent.interpreters import InterpreterError from .utils import TestBase @@ -74,6 +75,14 @@ def run(): start.set() support.gc_collect() + def test_create_interpreter_no_memory(self): + import _interpreters + _testcapi = import_helper.import_module("_testcapi") + + with self.assertRaises(InterpreterError): + _testcapi.set_nomemory(0, 1) + _interpreters.create() + if __name__ == '__main__': # Test needs to be a package, so we can do relative imports. diff --git a/Lib/test/test_interpreters/utils.py b/Lib/test/test_interpreters/utils.py index ae09aa457b48c7..bb6da52727c212 100644 --- a/Lib/test/test_interpreters/utils.py +++ b/Lib/test/test_interpreters/utils.py @@ -1,5 +1,6 @@ from collections import namedtuple import contextlib +import errno import json import logging import os @@ -51,7 +52,7 @@ def _close_file(file): else: os.close(file) except OSError as exc: - if exc.errno != 9: + if exc.errno != errno.EBADF: raise # re-raise # It was closed already. diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py deleted file mode 100644 index b487bcabf01ca4..00000000000000 --- a/Lib/test/test_io.py +++ /dev/null @@ -1,5066 +0,0 @@ -"""Unit tests for the io module.""" - -# Tests of io are scattered over the test suite: -# * test_bufio - tests file buffering -# * test_memoryio - tests BytesIO and StringIO -# * test_fileio - tests FileIO -# * test_file - tests the file interface -# * test_io - tests everything else in the io module -# * test_univnewlines - tests universal newline support -# * test_largefile - tests operations on a file greater than 2**32 bytes -# (only enabled with -ulargefile) -# * test_free_threading/test_io - tests thread safety of io objects - -################################################################################ -# ATTENTION TEST WRITERS!!! -################################################################################ -# When writing tests for io, it's important to test both the C and Python -# implementations. This is usually done by writing a base test that refers to -# the type it is testing as an attribute. Then it provides custom subclasses to -# test both implementations. This file has lots of examples. -################################################################################ - -import abc -import array -import errno -import locale -import os -import pickle -import random -import signal -import sys -import textwrap -import threading -import time -import unittest -import warnings -import weakref -from collections import deque, UserList -from itertools import cycle, count -from test import support -from test.support.script_helper import ( - assert_python_ok, assert_python_failure, run_python_until_end) -from test.support import ( - import_helper, is_apple, os_helper, threading_helper, warnings_helper, -) -from test.support.os_helper import FakePath - -import codecs -import io # C implementation of io -import _pyio as pyio # Python implementation of io - -try: - import ctypes -except ImportError: - def byteslike(*pos, **kw): - return array.array("b", bytes(*pos, **kw)) -else: - def byteslike(*pos, **kw): - """Create a bytes-like object having no string or sequence methods""" - data = bytes(*pos, **kw) - obj = EmptyStruct() - ctypes.resize(obj, len(data)) - memoryview(obj).cast("B")[:] = data - return obj - class EmptyStruct(ctypes.Structure): - pass - - -def _default_chunk_size(): - """Get the default TextIOWrapper chunk size""" - with open(__file__, "r", encoding="latin-1") as f: - return f._CHUNK_SIZE - -requires_alarm = unittest.skipUnless( - hasattr(signal, "alarm"), "test requires signal.alarm()" -) - - -class BadIndex: - def __index__(self): - 1/0 - -class MockRawIOWithoutRead: - """A RawIO implementation without read(), so as to exercise the default - RawIO.read() which calls readinto().""" - - def __init__(self, read_stack=()): - self._read_stack = list(read_stack) - self._write_stack = [] - self._reads = 0 - self._extraneous_reads = 0 - - def write(self, b): - self._write_stack.append(bytes(b)) - return len(b) - - def writable(self): - return True - - def fileno(self): - return 42 - - def readable(self): - return True - - def seekable(self): - return True - - def seek(self, pos, whence): - return 0 # wrong but we gotta return something - - def tell(self): - return 0 # same comment as above - - def readinto(self, buf): - self._reads += 1 - max_len = len(buf) - try: - data = self._read_stack[0] - except IndexError: - self._extraneous_reads += 1 - return 0 - if data is None: - del self._read_stack[0] - return None - n = len(data) - if len(data) <= max_len: - del self._read_stack[0] - buf[:n] = data - return n - else: - buf[:] = data[:max_len] - self._read_stack[0] = data[max_len:] - return max_len - - def truncate(self, pos=None): - return pos - -class CMockRawIOWithoutRead(MockRawIOWithoutRead, io.RawIOBase): - pass - -class PyMockRawIOWithoutRead(MockRawIOWithoutRead, pyio.RawIOBase): - pass - - -class MockRawIO(MockRawIOWithoutRead): - - def read(self, n=None): - self._reads += 1 - try: - return self._read_stack.pop(0) - except: - self._extraneous_reads += 1 - return b"" - -class CMockRawIO(MockRawIO, io.RawIOBase): - pass - -class PyMockRawIO(MockRawIO, pyio.RawIOBase): - pass - - -class MisbehavedRawIO(MockRawIO): - def write(self, b): - return super().write(b) * 2 - - def read(self, n=None): - return super().read(n) * 2 - - def seek(self, pos, whence): - return -123 - - def tell(self): - return -456 - - def readinto(self, buf): - super().readinto(buf) - return len(buf) * 5 - -class CMisbehavedRawIO(MisbehavedRawIO, io.RawIOBase): - pass - -class PyMisbehavedRawIO(MisbehavedRawIO, pyio.RawIOBase): - pass - - -class SlowFlushRawIO(MockRawIO): - def __init__(self): - super().__init__() - self.in_flush = threading.Event() - - def flush(self): - self.in_flush.set() - time.sleep(0.25) - -class CSlowFlushRawIO(SlowFlushRawIO, io.RawIOBase): - pass - -class PySlowFlushRawIO(SlowFlushRawIO, pyio.RawIOBase): - pass - - -class CloseFailureIO(MockRawIO): - closed = 0 - - def close(self): - if not self.closed: - self.closed = 1 - raise OSError - -class CCloseFailureIO(CloseFailureIO, io.RawIOBase): - pass - -class PyCloseFailureIO(CloseFailureIO, pyio.RawIOBase): - pass - - -class MockFileIO: - - def __init__(self, data): - self.read_history = [] - super().__init__(data) - - def read(self, n=None): - res = super().read(n) - self.read_history.append(None if res is None else len(res)) - return res - - def readinto(self, b): - res = super().readinto(b) - self.read_history.append(res) - return res - -class CMockFileIO(MockFileIO, io.BytesIO): - pass - -class PyMockFileIO(MockFileIO, pyio.BytesIO): - pass - - -class MockUnseekableIO: - def seekable(self): - return False - - def seek(self, *args): - raise self.UnsupportedOperation("not seekable") - - def tell(self, *args): - raise self.UnsupportedOperation("not seekable") - - def truncate(self, *args): - raise self.UnsupportedOperation("not seekable") - -class CMockUnseekableIO(MockUnseekableIO, io.BytesIO): - UnsupportedOperation = io.UnsupportedOperation - -class PyMockUnseekableIO(MockUnseekableIO, pyio.BytesIO): - UnsupportedOperation = pyio.UnsupportedOperation - - -class MockCharPseudoDevFileIO(MockFileIO): - # GH-95782 - # ftruncate() does not work on these special files (and CPython then raises - # appropriate exceptions), so truncate() does not have to be accounted for - # here. - def __init__(self, data): - super().__init__(data) - - def seek(self, *args): - return 0 - - def tell(self, *args): - return 0 - -class CMockCharPseudoDevFileIO(MockCharPseudoDevFileIO, io.BytesIO): - pass - -class PyMockCharPseudoDevFileIO(MockCharPseudoDevFileIO, pyio.BytesIO): - pass - - -class MockNonBlockWriterIO: - - def __init__(self): - self._write_stack = [] - self._blocker_char = None - - def pop_written(self): - s = b"".join(self._write_stack) - self._write_stack[:] = [] - return s - - def block_on(self, char): - """Block when a given char is encountered.""" - self._blocker_char = char - - def readable(self): - return True - - def seekable(self): - return True - - def seek(self, pos, whence=0): - # naive implementation, enough for tests - return 0 - - def writable(self): - return True - - def write(self, b): - b = bytes(b) - n = -1 - if self._blocker_char: - try: - n = b.index(self._blocker_char) - except ValueError: - pass - else: - if n > 0: - # write data up to the first blocker - self._write_stack.append(b[:n]) - return n - else: - # cancel blocker and indicate would block - self._blocker_char = None - return None - self._write_stack.append(b) - return len(b) - -class CMockNonBlockWriterIO(MockNonBlockWriterIO, io.RawIOBase): - BlockingIOError = io.BlockingIOError - -class PyMockNonBlockWriterIO(MockNonBlockWriterIO, pyio.RawIOBase): - BlockingIOError = pyio.BlockingIOError - - -class IOTest(unittest.TestCase): - - def setUp(self): - os_helper.unlink(os_helper.TESTFN) - - def tearDown(self): - os_helper.unlink(os_helper.TESTFN) - - def write_ops(self, f): - self.assertEqual(f.write(b"blah."), 5) - f.truncate(0) - self.assertEqual(f.tell(), 5) - f.seek(0) - - self.assertEqual(f.write(b"blah."), 5) - self.assertEqual(f.seek(0), 0) - self.assertEqual(f.write(b"Hello."), 6) - self.assertEqual(f.tell(), 6) - self.assertEqual(f.seek(-1, 1), 5) - self.assertEqual(f.tell(), 5) - buffer = bytearray(b" world\n\n\n") - self.assertEqual(f.write(buffer), 9) - buffer[:] = b"*" * 9 # Overwrite our copy of the data - self.assertEqual(f.seek(0), 0) - self.assertEqual(f.write(b"h"), 1) - self.assertEqual(f.seek(-1, 2), 13) - self.assertEqual(f.tell(), 13) - - self.assertEqual(f.truncate(12), 12) - self.assertEqual(f.tell(), 13) - self.assertRaises(TypeError, f.seek, 0.0) - - def read_ops(self, f, buffered=False): - data = f.read(5) - self.assertEqual(data, b"hello") - data = byteslike(data) - self.assertEqual(f.readinto(data), 5) - self.assertEqual(bytes(data), b" worl") - data = bytearray(5) - self.assertEqual(f.readinto(data), 2) - self.assertEqual(len(data), 5) - self.assertEqual(data[:2], b"d\n") - self.assertEqual(f.seek(0), 0) - self.assertEqual(f.read(20), b"hello world\n") - self.assertEqual(f.read(1), b"") - self.assertEqual(f.readinto(byteslike(b"x")), 0) - self.assertEqual(f.seek(-6, 2), 6) - self.assertEqual(f.read(5), b"world") - self.assertEqual(f.read(0), b"") - self.assertEqual(f.readinto(byteslike()), 0) - self.assertEqual(f.seek(-6, 1), 5) - self.assertEqual(f.read(5), b" worl") - self.assertEqual(f.tell(), 10) - self.assertRaises(TypeError, f.seek, 0.0) - if buffered: - f.seek(0) - self.assertEqual(f.read(), b"hello world\n") - f.seek(6) - self.assertEqual(f.read(), b"world\n") - self.assertEqual(f.read(), b"") - f.seek(0) - data = byteslike(5) - self.assertEqual(f.readinto1(data), 5) - self.assertEqual(bytes(data), b"hello") - - LARGE = 2**31 - - def large_file_ops(self, f): - assert f.readable() - assert f.writable() - try: - self.assertEqual(f.seek(self.LARGE), self.LARGE) - except (OverflowError, ValueError): - self.skipTest("no largefile support") - self.assertEqual(f.tell(), self.LARGE) - self.assertEqual(f.write(b"xxx"), 3) - self.assertEqual(f.tell(), self.LARGE + 3) - self.assertEqual(f.seek(-1, 1), self.LARGE + 2) - self.assertEqual(f.truncate(), self.LARGE + 2) - self.assertEqual(f.tell(), self.LARGE + 2) - self.assertEqual(f.seek(0, 2), self.LARGE + 2) - self.assertEqual(f.truncate(self.LARGE + 1), self.LARGE + 1) - self.assertEqual(f.tell(), self.LARGE + 2) - self.assertEqual(f.seek(0, 2), self.LARGE + 1) - self.assertEqual(f.seek(-1, 2), self.LARGE) - self.assertEqual(f.read(2), b"x") - - def test_invalid_operations(self): - # Try writing on a file opened in read mode and vice-versa. - exc = self.UnsupportedOperation - with self.open(os_helper.TESTFN, "w", encoding="utf-8") as fp: - self.assertRaises(exc, fp.read) - self.assertRaises(exc, fp.readline) - with self.open(os_helper.TESTFN, "wb") as fp: - self.assertRaises(exc, fp.read) - self.assertRaises(exc, fp.readline) - with self.open(os_helper.TESTFN, "wb", buffering=0) as fp: - self.assertRaises(exc, fp.read) - self.assertRaises(exc, fp.readline) - with self.open(os_helper.TESTFN, "rb", buffering=0) as fp: - self.assertRaises(exc, fp.write, b"blah") - self.assertRaises(exc, fp.writelines, [b"blah\n"]) - with self.open(os_helper.TESTFN, "rb") as fp: - self.assertRaises(exc, fp.write, b"blah") - self.assertRaises(exc, fp.writelines, [b"blah\n"]) - with self.open(os_helper.TESTFN, "r", encoding="utf-8") as fp: - self.assertRaises(exc, fp.write, "blah") - self.assertRaises(exc, fp.writelines, ["blah\n"]) - # Non-zero seeking from current or end pos - self.assertRaises(exc, fp.seek, 1, self.SEEK_CUR) - self.assertRaises(exc, fp.seek, -1, self.SEEK_END) - - @support.cpython_only - def test_startup_optimization(self): - # gh-132952: Test that `io` is not imported at startup and that the - # __module__ of UnsupportedOperation is set to "io". - assert_python_ok("-S", "-c", textwrap.dedent( - """ - import sys - assert "io" not in sys.modules - try: - sys.stdin.truncate() - except Exception as e: - typ = type(e) - assert typ.__module__ == "io", (typ, typ.__module__) - assert typ.__name__ == "UnsupportedOperation", (typ, typ.__name__) - else: - raise AssertionError("Expected UnsupportedOperation") - """ - )) - - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_optional_abilities(self): - # Test for OSError when optional APIs are not supported - # The purpose of this test is to try fileno(), reading, writing and - # seeking operations with various objects that indicate they do not - # support these operations. - - def pipe_reader(): - [r, w] = os.pipe() - os.close(w) # So that read() is harmless - return self.FileIO(r, "r") - - def pipe_writer(): - [r, w] = os.pipe() - self.addCleanup(os.close, r) - # Guarantee that we can write into the pipe without blocking - thread = threading.Thread(target=os.read, args=(r, 100)) - thread.start() - self.addCleanup(thread.join) - return self.FileIO(w, "w") - - def buffered_reader(): - return self.BufferedReader(self.MockUnseekableIO()) - - def buffered_writer(): - return self.BufferedWriter(self.MockUnseekableIO()) - - def buffered_random(): - return self.BufferedRandom(self.BytesIO()) - - def buffered_rw_pair(): - return self.BufferedRWPair(self.MockUnseekableIO(), - self.MockUnseekableIO()) - - def text_reader(): - class UnseekableReader(self.MockUnseekableIO): - writable = self.BufferedIOBase.writable - write = self.BufferedIOBase.write - return self.TextIOWrapper(UnseekableReader(), "ascii") - - def text_writer(): - class UnseekableWriter(self.MockUnseekableIO): - readable = self.BufferedIOBase.readable - read = self.BufferedIOBase.read - return self.TextIOWrapper(UnseekableWriter(), "ascii") - - tests = ( - (pipe_reader, "fr"), (pipe_writer, "fw"), - (buffered_reader, "r"), (buffered_writer, "w"), - (buffered_random, "rws"), (buffered_rw_pair, "rw"), - (text_reader, "r"), (text_writer, "w"), - (self.BytesIO, "rws"), (self.StringIO, "rws"), - ) - - def do_test(test, obj, abilities): - readable = "r" in abilities - self.assertEqual(obj.readable(), readable) - writable = "w" in abilities - self.assertEqual(obj.writable(), writable) - - if isinstance(obj, self.TextIOBase): - data = "3" - elif isinstance(obj, (self.BufferedIOBase, self.RawIOBase)): - data = b"3" - else: - self.fail("Unknown base class") - - if "f" in abilities: - obj.fileno() - else: - self.assertRaises(OSError, obj.fileno) - - if readable: - obj.read(1) - obj.read() - else: - self.assertRaises(OSError, obj.read, 1) - self.assertRaises(OSError, obj.read) - - if writable: - obj.write(data) - else: - self.assertRaises(OSError, obj.write, data) - - if sys.platform.startswith("win") and test in ( - pipe_reader, pipe_writer): - # Pipes seem to appear as seekable on Windows - return - seekable = "s" in abilities - self.assertEqual(obj.seekable(), seekable) - - if seekable: - obj.tell() - obj.seek(0) - else: - self.assertRaises(OSError, obj.tell) - self.assertRaises(OSError, obj.seek, 0) - - if writable and seekable: - obj.truncate() - obj.truncate(0) - else: - self.assertRaises(OSError, obj.truncate) - self.assertRaises(OSError, obj.truncate, 0) - - for [test, abilities] in tests: - with self.subTest(test): - if test == pipe_writer and not threading_helper.can_start_thread: - self.skipTest("Need threads") - with test() as obj: - do_test(test, obj, abilities) - - - def test_open_handles_NUL_chars(self): - fn_with_NUL = 'foo\0bar' - self.assertRaises(ValueError, self.open, fn_with_NUL, 'w', encoding="utf-8") - - bytes_fn = bytes(fn_with_NUL, 'ascii') - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self.assertRaises(ValueError, self.open, bytes_fn, 'w', encoding="utf-8") - - def test_raw_file_io(self): - with self.open(os_helper.TESTFN, "wb", buffering=0) as f: - self.assertEqual(f.readable(), False) - self.assertEqual(f.writable(), True) - self.assertEqual(f.seekable(), True) - self.write_ops(f) - with self.open(os_helper.TESTFN, "rb", buffering=0) as f: - self.assertEqual(f.readable(), True) - self.assertEqual(f.writable(), False) - self.assertEqual(f.seekable(), True) - self.read_ops(f) - - def test_buffered_file_io(self): - with self.open(os_helper.TESTFN, "wb") as f: - self.assertEqual(f.readable(), False) - self.assertEqual(f.writable(), True) - self.assertEqual(f.seekable(), True) - self.write_ops(f) - with self.open(os_helper.TESTFN, "rb") as f: - self.assertEqual(f.readable(), True) - self.assertEqual(f.writable(), False) - self.assertEqual(f.seekable(), True) - self.read_ops(f, True) - - def test_readline(self): - with self.open(os_helper.TESTFN, "wb") as f: - f.write(b"abc\ndef\nxyzzy\nfoo\x00bar\nanother line") - with self.open(os_helper.TESTFN, "rb") as f: - self.assertEqual(f.readline(), b"abc\n") - self.assertEqual(f.readline(10), b"def\n") - self.assertEqual(f.readline(2), b"xy") - self.assertEqual(f.readline(4), b"zzy\n") - self.assertEqual(f.readline(), b"foo\x00bar\n") - self.assertEqual(f.readline(None), b"another line") - self.assertRaises(TypeError, f.readline, 5.3) - with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: - self.assertRaises(TypeError, f.readline, 5.3) - - def test_readline_nonsizeable(self): - # Issue #30061 - # Crash when readline() returns an object without __len__ - class R(self.IOBase): - def readline(self): - return None - self.assertRaises((TypeError, StopIteration), next, R()) - - def test_next_nonsizeable(self): - # Issue #30061 - # Crash when __next__() returns an object without __len__ - class R(self.IOBase): - def __next__(self): - return None - self.assertRaises(TypeError, R().readlines, 1) - - def test_raw_bytes_io(self): - f = self.BytesIO() - self.write_ops(f) - data = f.getvalue() - self.assertEqual(data, b"hello world\n") - f = self.BytesIO(data) - self.read_ops(f, True) - - def test_large_file_ops(self): - # On Windows and Apple platforms this test consumes large resources; It - # takes a long time to build the >2 GiB file and takes >2 GiB of disk - # space therefore the resource must be enabled to run this test. - if sys.platform[:3] == 'win' or is_apple: - support.requires( - 'largefile', - 'test requires %s bytes and a long time to run' % self.LARGE) - with self.open(os_helper.TESTFN, "w+b", 0) as f: - self.large_file_ops(f) - with self.open(os_helper.TESTFN, "w+b") as f: - self.large_file_ops(f) - - def test_with_open(self): - for bufsize in (0, 100): - with self.open(os_helper.TESTFN, "wb", bufsize) as f: - f.write(b"xxx") - self.assertEqual(f.closed, True) - try: - with self.open(os_helper.TESTFN, "wb", bufsize) as f: - 1/0 - except ZeroDivisionError: - self.assertEqual(f.closed, True) - else: - self.fail("1/0 didn't raise an exception") - - # issue 5008 - def test_append_mode_tell(self): - with self.open(os_helper.TESTFN, "wb") as f: - f.write(b"xxx") - with self.open(os_helper.TESTFN, "ab", buffering=0) as f: - self.assertEqual(f.tell(), 3) - with self.open(os_helper.TESTFN, "ab") as f: - self.assertEqual(f.tell(), 3) - with self.open(os_helper.TESTFN, "a", encoding="utf-8") as f: - self.assertGreater(f.tell(), 0) - - def test_destructor(self): - record = [] - class MyFileIO(self.FileIO): - def __del__(self): - record.append(1) - try: - f = super().__del__ - except AttributeError: - pass - else: - f() - def close(self): - record.append(2) - super().close() - def flush(self): - record.append(3) - super().flush() - with warnings_helper.check_warnings(('', ResourceWarning)): - f = MyFileIO(os_helper.TESTFN, "wb") - f.write(b"xxx") - del f - support.gc_collect() - self.assertEqual(record, [1, 2, 3]) - with self.open(os_helper.TESTFN, "rb") as f: - self.assertEqual(f.read(), b"xxx") - - def _check_base_destructor(self, base): - record = [] - class MyIO(base): - def __init__(self): - # This exercises the availability of attributes on object - # destruction. - # (in the C version, close() is called by the tp_dealloc - # function, not by __del__) - self.on_del = 1 - self.on_close = 2 - self.on_flush = 3 - def __del__(self): - record.append(self.on_del) - try: - f = super().__del__ - except AttributeError: - pass - else: - f() - def close(self): - record.append(self.on_close) - super().close() - def flush(self): - record.append(self.on_flush) - super().flush() - f = MyIO() - del f - support.gc_collect() - self.assertEqual(record, [1, 2, 3]) - - def test_IOBase_destructor(self): - self._check_base_destructor(self.IOBase) - - def test_RawIOBase_destructor(self): - self._check_base_destructor(self.RawIOBase) - - def test_BufferedIOBase_destructor(self): - self._check_base_destructor(self.BufferedIOBase) - - def test_TextIOBase_destructor(self): - self._check_base_destructor(self.TextIOBase) - - def test_close_flushes(self): - with self.open(os_helper.TESTFN, "wb") as f: - f.write(b"xxx") - with self.open(os_helper.TESTFN, "rb") as f: - self.assertEqual(f.read(), b"xxx") - - def test_array_writes(self): - a = array.array('i', range(10)) - n = len(a.tobytes()) - def check(f): - with f: - self.assertEqual(f.write(a), n) - f.writelines((a,)) - check(self.BytesIO()) - check(self.FileIO(os_helper.TESTFN, "w")) - check(self.BufferedWriter(self.MockRawIO())) - check(self.BufferedRandom(self.MockRawIO())) - check(self.BufferedRWPair(self.MockRawIO(), self.MockRawIO())) - - def test_closefd(self): - self.assertRaises(ValueError, self.open, os_helper.TESTFN, 'w', - encoding="utf-8", closefd=False) - - def test_read_closed(self): - with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f: - f.write("egg\n") - with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: - file = self.open(f.fileno(), "r", encoding="utf-8", closefd=False) - self.assertEqual(file.read(), "egg\n") - file.seek(0) - file.close() - self.assertRaises(ValueError, file.read) - with self.open(os_helper.TESTFN, "rb") as f: - file = self.open(f.fileno(), "rb", closefd=False) - self.assertEqual(file.read()[:3], b"egg") - file.close() - self.assertRaises(ValueError, file.readinto, bytearray(1)) - - def test_no_closefd_with_filename(self): - # can't use closefd in combination with a file name - self.assertRaises(ValueError, self.open, os_helper.TESTFN, "r", - encoding="utf-8", closefd=False) - - def test_closefd_attr(self): - with self.open(os_helper.TESTFN, "wb") as f: - f.write(b"egg\n") - with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: - self.assertEqual(f.buffer.raw.closefd, True) - file = self.open(f.fileno(), "r", encoding="utf-8", closefd=False) - self.assertEqual(file.buffer.raw.closefd, False) - - def test_garbage_collection(self): - # FileIO objects are collected, and collecting them flushes - # all data to disk. - with warnings_helper.check_warnings(('', ResourceWarning)): - f = self.FileIO(os_helper.TESTFN, "wb") - f.write(b"abcxxx") - f.f = f - wr = weakref.ref(f) - del f - support.gc_collect() - self.assertIsNone(wr(), wr) - with self.open(os_helper.TESTFN, "rb") as f: - self.assertEqual(f.read(), b"abcxxx") - - def test_unbounded_file(self): - # Issue #1174606: reading from an unbounded stream such as /dev/zero. - zero = "/dev/zero" - if not os.path.exists(zero): - self.skipTest("{0} does not exist".format(zero)) - if sys.maxsize > 0x7FFFFFFF: - self.skipTest("test can only run in a 32-bit address space") - if support.real_max_memuse < support._2G: - self.skipTest("test requires at least 2 GiB of memory") - with self.open(zero, "rb", buffering=0) as f: - self.assertRaises(OverflowError, f.read) - with self.open(zero, "rb") as f: - self.assertRaises(OverflowError, f.read) - with self.open(zero, "r") as f: - self.assertRaises(OverflowError, f.read) - - def check_flush_error_on_close(self, *args, **kwargs): - # Test that the file is closed despite failed flush - # and that flush() is called before file closed. - f = self.open(*args, **kwargs) - closed = [] - def bad_flush(): - closed[:] = [f.closed] - raise OSError() - f.flush = bad_flush - self.assertRaises(OSError, f.close) # exception not swallowed - self.assertTrue(f.closed) - self.assertTrue(closed) # flush() called - self.assertFalse(closed[0]) # flush() called before file closed - f.flush = lambda: None # break reference loop - - def test_flush_error_on_close(self): - # raw file - # Issue #5700: io.FileIO calls flush() after file closed - self.check_flush_error_on_close(os_helper.TESTFN, 'wb', buffering=0) - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) - self.check_flush_error_on_close(fd, 'wb', buffering=0) - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) - self.check_flush_error_on_close(fd, 'wb', buffering=0, closefd=False) - os.close(fd) - # buffered io - self.check_flush_error_on_close(os_helper.TESTFN, 'wb') - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) - self.check_flush_error_on_close(fd, 'wb') - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) - self.check_flush_error_on_close(fd, 'wb', closefd=False) - os.close(fd) - # text io - self.check_flush_error_on_close(os_helper.TESTFN, 'w', encoding="utf-8") - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) - self.check_flush_error_on_close(fd, 'w', encoding="utf-8") - fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) - self.check_flush_error_on_close(fd, 'w', encoding="utf-8", closefd=False) - os.close(fd) - - def test_multi_close(self): - f = self.open(os_helper.TESTFN, "wb", buffering=0) - f.close() - f.close() - f.close() - self.assertRaises(ValueError, f.flush) - - def test_RawIOBase_read(self): - # Exercise the default limited RawIOBase.read(n) implementation (which - # calls readinto() internally). - rawio = self.MockRawIOWithoutRead((b"abc", b"d", None, b"efg", None)) - self.assertEqual(rawio.read(2), b"ab") - self.assertEqual(rawio.read(2), b"c") - self.assertEqual(rawio.read(2), b"d") - self.assertEqual(rawio.read(2), None) - self.assertEqual(rawio.read(2), b"ef") - self.assertEqual(rawio.read(2), b"g") - self.assertEqual(rawio.read(2), None) - self.assertEqual(rawio.read(2), b"") - - def test_types_have_dict(self): - test = ( - self.IOBase(), - self.RawIOBase(), - self.TextIOBase(), - self.StringIO(), - self.BytesIO() - ) - for obj in test: - self.assertHasAttr(obj, "__dict__") - - def test_opener(self): - with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f: - f.write("egg\n") - fd = os.open(os_helper.TESTFN, os.O_RDONLY) - def opener(path, flags): - return fd - with self.open("non-existent", "r", encoding="utf-8", opener=opener) as f: - self.assertEqual(f.read(), "egg\n") - - def test_bad_opener_negative_1(self): - # Issue #27066. - def badopener(fname, flags): - return -1 - with self.assertRaises(ValueError) as cm: - self.open('non-existent', 'r', opener=badopener) - self.assertEqual(str(cm.exception), 'opener returned -1') - - def test_bad_opener_other_negative(self): - # Issue #27066. - def badopener(fname, flags): - return -2 - with self.assertRaises(ValueError) as cm: - self.open('non-existent', 'r', opener=badopener) - self.assertEqual(str(cm.exception), 'opener returned -2') - - def test_opener_invalid_fd(self): - # Check that OSError is raised with error code EBADF if the - # opener returns an invalid file descriptor (see gh-82212). - fd = os_helper.make_bad_fd() - with self.assertRaises(OSError) as cm: - self.open('foo', opener=lambda name, flags: fd) - self.assertEqual(cm.exception.errno, errno.EBADF) - - def test_fileio_closefd(self): - # Issue #4841 - with self.open(__file__, 'rb') as f1, \ - self.open(__file__, 'rb') as f2: - fileio = self.FileIO(f1.fileno(), closefd=False) - # .__init__() must not close f1 - fileio.__init__(f2.fileno(), closefd=False) - f1.readline() - # .close() must not close f2 - fileio.close() - f2.readline() - - def test_nonbuffered_textio(self): - with warnings_helper.check_no_resource_warning(self): - with self.assertRaises(ValueError): - self.open(os_helper.TESTFN, 'w', encoding="utf-8", buffering=0) - - def test_invalid_newline(self): - with warnings_helper.check_no_resource_warning(self): - with self.assertRaises(ValueError): - self.open(os_helper.TESTFN, 'w', encoding="utf-8", newline='invalid') - - def test_buffered_readinto_mixin(self): - # Test the implementation provided by BufferedIOBase - class Stream(self.BufferedIOBase): - def read(self, size): - return b"12345" - read1 = read - stream = Stream() - for method in ("readinto", "readinto1"): - with self.subTest(method): - buffer = byteslike(5) - self.assertEqual(getattr(stream, method)(buffer), 5) - self.assertEqual(bytes(buffer), b"12345") - - def test_fspath_support(self): - def check_path_succeeds(path): - with self.open(path, "w", encoding="utf-8") as f: - f.write("egg\n") - - with self.open(path, "r", encoding="utf-8") as f: - self.assertEqual(f.read(), "egg\n") - - check_path_succeeds(FakePath(os_helper.TESTFN)) - check_path_succeeds(FakePath(os.fsencode(os_helper.TESTFN))) - - with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f: - bad_path = FakePath(f.fileno()) - with self.assertRaises(TypeError): - self.open(bad_path, 'w', encoding="utf-8") - - bad_path = FakePath(None) - with self.assertRaises(TypeError): - self.open(bad_path, 'w', encoding="utf-8") - - bad_path = FakePath(FloatingPointError) - with self.assertRaises(FloatingPointError): - self.open(bad_path, 'w', encoding="utf-8") - - # ensure that refcounting is correct with some error conditions - with self.assertRaisesRegex(ValueError, 'read/write/append mode'): - self.open(FakePath(os_helper.TESTFN), 'rwxa', encoding="utf-8") - - def test_RawIOBase_readall(self): - # Exercise the default unlimited RawIOBase.read() and readall() - # implementations. - rawio = self.MockRawIOWithoutRead((b"abc", b"d", b"efg")) - self.assertEqual(rawio.read(), b"abcdefg") - rawio = self.MockRawIOWithoutRead((b"abc", b"d", b"efg")) - self.assertEqual(rawio.readall(), b"abcdefg") - - def test_BufferedIOBase_readinto(self): - # Exercise the default BufferedIOBase.readinto() and readinto1() - # implementations (which call read() or read1() internally). - class Reader(self.BufferedIOBase): - def __init__(self, avail): - self.avail = avail - def read(self, size): - result = self.avail[:size] - self.avail = self.avail[size:] - return result - def read1(self, size): - """Returns no more than 5 bytes at once""" - return self.read(min(size, 5)) - tests = ( - # (test method, total data available, read buffer size, expected - # read size) - ("readinto", 10, 5, 5), - ("readinto", 10, 6, 6), # More than read1() can return - ("readinto", 5, 6, 5), # Buffer larger than total available - ("readinto", 6, 7, 6), - ("readinto", 10, 0, 0), # Empty buffer - ("readinto1", 10, 5, 5), # Result limited to single read1() call - ("readinto1", 10, 6, 5), # Buffer larger than read1() can return - ("readinto1", 5, 6, 5), # Buffer larger than total available - ("readinto1", 6, 7, 5), - ("readinto1", 10, 0, 0), # Empty buffer - ) - UNUSED_BYTE = 0x81 - for test in tests: - with self.subTest(test): - method, avail, request, result = test - reader = Reader(bytes(range(avail))) - buffer = bytearray((UNUSED_BYTE,) * request) - method = getattr(reader, method) - self.assertEqual(method(buffer), result) - self.assertEqual(len(buffer), request) - self.assertSequenceEqual(buffer[:result], range(result)) - unused = (UNUSED_BYTE,) * (request - result) - self.assertSequenceEqual(buffer[result:], unused) - self.assertEqual(len(reader.avail), avail - result) - - def test_close_assert(self): - class R(self.IOBase): - def __setattr__(self, name, value): - pass - def flush(self): - raise OSError() - f = R() - # This would cause an assertion failure. - self.assertRaises(OSError, f.close) - - # Silence destructor error - R.flush = lambda self: None - - @threading_helper.requires_working_threading() - def test_write_readline_races(self): - # gh-134908: Concurrent iteration over a file caused races - thread_count = 2 - write_count = 100 - read_count = 100 - - def writer(file, barrier): - barrier.wait() - for _ in range(write_count): - file.write("x") - - def reader(file, barrier): - barrier.wait() - for _ in range(read_count): - for line in file: - self.assertEqual(line, "") - - with self.open(os_helper.TESTFN, "w+") as f: - barrier = threading.Barrier(thread_count + 1) - reader = threading.Thread(target=reader, args=(f, barrier)) - writers = [threading.Thread(target=writer, args=(f, barrier)) - for _ in range(thread_count)] - with threading_helper.catch_threading_exception() as cm: - with threading_helper.start_threads(writers + [reader]): - pass - self.assertIsNone(cm.exc_type) - - self.assertEqual(os.stat(os_helper.TESTFN).st_size, - write_count * thread_count) - - -class CIOTest(IOTest): - - def test_IOBase_finalize(self): - # Issue #12149: segmentation fault on _PyIOBase_finalize when both a - # class which inherits IOBase and an object of this class are caught - # in a reference cycle and close() is already in the method cache. - class MyIO(self.IOBase): - def close(self): - pass - - # create an instance to populate the method cache - MyIO() - obj = MyIO() - obj.obj = obj - wr = weakref.ref(obj) - del MyIO - del obj - support.gc_collect() - self.assertIsNone(wr(), wr) - -@support.cpython_only -class TestIOCTypes(unittest.TestCase): - def setUp(self): - _io = import_helper.import_module("_io") - self.types = [ - _io.BufferedRWPair, - _io.BufferedRandom, - _io.BufferedReader, - _io.BufferedWriter, - _io.BytesIO, - _io.FileIO, - _io.IncrementalNewlineDecoder, - _io.StringIO, - _io.TextIOWrapper, - _io._BufferedIOBase, - _io._BytesIOBuffer, - _io._IOBase, - _io._RawIOBase, - _io._TextIOBase, - ] - if sys.platform == "win32": - self.types.append(_io._WindowsConsoleIO) - self._io = _io - - def test_immutable_types(self): - for tp in self.types: - with self.subTest(tp=tp): - with self.assertRaisesRegex(TypeError, "immutable"): - tp.foo = "bar" - - def test_class_hierarchy(self): - def check_subs(types, base): - for tp in types: - with self.subTest(tp=tp, base=base): - self.assertIsSubclass(tp, base) - - def recursive_check(d): - for k, v in d.items(): - if isinstance(v, dict): - recursive_check(v) - elif isinstance(v, set): - check_subs(v, k) - else: - self.fail("corrupt test dataset") - - _io = self._io - hierarchy = { - _io._IOBase: { - _io._BufferedIOBase: { - _io.BufferedRWPair, - _io.BufferedRandom, - _io.BufferedReader, - _io.BufferedWriter, - _io.BytesIO, - }, - _io._RawIOBase: { - _io.FileIO, - }, - _io._TextIOBase: { - _io.StringIO, - _io.TextIOWrapper, - }, - }, - } - if sys.platform == "win32": - hierarchy[_io._IOBase][_io._RawIOBase].add(_io._WindowsConsoleIO) - - recursive_check(hierarchy) - - def test_subclassing(self): - _io = self._io - dataset = {k: True for k in self.types} - dataset[_io._BytesIOBuffer] = False - - for tp, is_basetype in dataset.items(): - with self.subTest(tp=tp, is_basetype=is_basetype): - name = f"{tp.__name__}_subclass" - bases = (tp,) - if is_basetype: - _ = type(name, bases, {}) - else: - msg = "not an acceptable base type" - with self.assertRaisesRegex(TypeError, msg): - _ = type(name, bases, {}) - - def test_disallow_instantiation(self): - _io = self._io - support.check_disallow_instantiation(self, _io._BytesIOBuffer) - - def test_stringio_setstate(self): - # gh-127182: Calling __setstate__() with invalid arguments must not crash - obj = self._io.StringIO() - with self.assertRaisesRegex( - TypeError, - 'initial_value must be str or None, not int', - ): - obj.__setstate__((1, '', 0, {})) - - obj.__setstate__((None, '', 0, {})) # should not crash - self.assertEqual(obj.getvalue(), '') - - obj.__setstate__(('', '', 0, {})) - self.assertEqual(obj.getvalue(), '') - -class PyIOTest(IOTest): - pass - - -@support.cpython_only -class APIMismatchTest(unittest.TestCase): - - def test_RawIOBase_io_in_pyio_match(self): - """Test that pyio RawIOBase class has all c RawIOBase methods""" - mismatch = support.detect_api_mismatch(pyio.RawIOBase, io.RawIOBase, - ignore=('__weakref__', '__static_attributes__')) - self.assertEqual(mismatch, set(), msg='Python RawIOBase does not have all C RawIOBase methods') - - def test_RawIOBase_pyio_in_io_match(self): - """Test that c RawIOBase class has all pyio RawIOBase methods""" - mismatch = support.detect_api_mismatch(io.RawIOBase, pyio.RawIOBase) - self.assertEqual(mismatch, set(), msg='C RawIOBase does not have all Python RawIOBase methods') - - -class CommonBufferedTests: - # Tests common to BufferedReader, BufferedWriter and BufferedRandom - - def test_detach(self): - raw = self.MockRawIO() - buf = self.tp(raw) - self.assertIs(buf.detach(), raw) - self.assertRaises(ValueError, buf.detach) - - repr(buf) # Should still work - - def test_fileno(self): - rawio = self.MockRawIO() - bufio = self.tp(rawio) - - self.assertEqual(42, bufio.fileno()) - - def test_invalid_args(self): - rawio = self.MockRawIO() - bufio = self.tp(rawio) - # Invalid whence - self.assertRaises(ValueError, bufio.seek, 0, -1) - self.assertRaises(ValueError, bufio.seek, 0, 9) - - def test_override_destructor(self): - tp = self.tp - record = [] - class MyBufferedIO(tp): - def __del__(self): - record.append(1) - try: - f = super().__del__ - except AttributeError: - pass - else: - f() - def close(self): - record.append(2) - super().close() - def flush(self): - record.append(3) - super().flush() - rawio = self.MockRawIO() - bufio = MyBufferedIO(rawio) - del bufio - support.gc_collect() - self.assertEqual(record, [1, 2, 3]) - - def test_context_manager(self): - # Test usability as a context manager - rawio = self.MockRawIO() - bufio = self.tp(rawio) - def _with(): - with bufio: - pass - _with() - # bufio should now be closed, and using it a second time should raise - # a ValueError. - self.assertRaises(ValueError, _with) - - def test_error_through_destructor(self): - # Test that the exception state is not modified by a destructor, - # even if close() fails. - rawio = self.CloseFailureIO() - with support.catch_unraisable_exception() as cm: - with self.assertRaises(AttributeError): - self.tp(rawio).xyzzy - - self.assertEqual(cm.unraisable.exc_type, OSError) - - def test_repr(self): - raw = self.MockRawIO() - b = self.tp(raw) - clsname = r"(%s\.)?%s" % (self.tp.__module__, self.tp.__qualname__) - self.assertRegex(repr(b), "<%s>" % clsname) - raw.name = "dummy" - self.assertRegex(repr(b), "<%s name='dummy'>" % clsname) - raw.name = b"dummy" - self.assertRegex(repr(b), "<%s name=b'dummy'>" % clsname) - - def test_recursive_repr(self): - # Issue #25455 - raw = self.MockRawIO() - b = self.tp(raw) - with support.swap_attr(raw, 'name', b), support.infinite_recursion(25): - with self.assertRaises(RuntimeError): - repr(b) # Should not crash - - def test_flush_error_on_close(self): - # Test that buffered file is closed despite failed flush - # and that flush() is called before file closed. - raw = self.MockRawIO() - closed = [] - def bad_flush(): - closed[:] = [b.closed, raw.closed] - raise OSError() - raw.flush = bad_flush - b = self.tp(raw) - self.assertRaises(OSError, b.close) # exception not swallowed - self.assertTrue(b.closed) - self.assertTrue(raw.closed) - self.assertTrue(closed) # flush() called - self.assertFalse(closed[0]) # flush() called before file closed - self.assertFalse(closed[1]) - raw.flush = lambda: None # break reference loop - - def test_close_error_on_close(self): - raw = self.MockRawIO() - def bad_flush(): - raise OSError('flush') - def bad_close(): - raise OSError('close') - raw.close = bad_close - b = self.tp(raw) - b.flush = bad_flush - with self.assertRaises(OSError) as err: # exception not swallowed - b.close() - self.assertEqual(err.exception.args, ('close',)) - self.assertIsInstance(err.exception.__context__, OSError) - self.assertEqual(err.exception.__context__.args, ('flush',)) - self.assertFalse(b.closed) - - # Silence destructor error - raw.close = lambda: None - b.flush = lambda: None - - def test_nonnormalized_close_error_on_close(self): - # Issue #21677 - raw = self.MockRawIO() - def bad_flush(): - raise non_existing_flush - def bad_close(): - raise non_existing_close - raw.close = bad_close - b = self.tp(raw) - b.flush = bad_flush - with self.assertRaises(NameError) as err: # exception not swallowed - b.close() - self.assertIn('non_existing_close', str(err.exception)) - self.assertIsInstance(err.exception.__context__, NameError) - self.assertIn('non_existing_flush', str(err.exception.__context__)) - self.assertFalse(b.closed) - - # Silence destructor error - b.flush = lambda: None - raw.close = lambda: None - - def test_multi_close(self): - raw = self.MockRawIO() - b = self.tp(raw) - b.close() - b.close() - b.close() - self.assertRaises(ValueError, b.flush) - - def test_unseekable(self): - bufio = self.tp(self.MockUnseekableIO(b"A" * 10)) - self.assertRaises(self.UnsupportedOperation, bufio.tell) - self.assertRaises(self.UnsupportedOperation, bufio.seek, 0) - - def test_readonly_attributes(self): - raw = self.MockRawIO() - buf = self.tp(raw) - x = self.MockRawIO() - with self.assertRaises(AttributeError): - buf.raw = x - - def test_pickling_subclass(self): - global MyBufferedIO - class MyBufferedIO(self.tp): - def __init__(self, raw, tag): - super().__init__(raw) - self.tag = tag - def __getstate__(self): - return self.tag, self.raw.getvalue() - def __setstate__(slf, state): - tag, value = state - slf.__init__(self.BytesIO(value), tag) - - raw = self.BytesIO(b'data') - buf = MyBufferedIO(raw, tag='ham') - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - with self.subTest(protocol=proto): - pickled = pickle.dumps(buf, proto) - newbuf = pickle.loads(pickled) - self.assertEqual(newbuf.raw.getvalue(), b'data') - self.assertEqual(newbuf.tag, 'ham') - del MyBufferedIO - - -class SizeofTest: - - @support.cpython_only - def test_sizeof(self): - bufsize1 = 4096 - bufsize2 = 8192 - rawio = self.MockRawIO() - bufio = self.tp(rawio, buffer_size=bufsize1) - size = sys.getsizeof(bufio) - bufsize1 - rawio = self.MockRawIO() - bufio = self.tp(rawio, buffer_size=bufsize2) - self.assertEqual(sys.getsizeof(bufio), size + bufsize2) - - @support.cpython_only - def test_buffer_freeing(self) : - bufsize = 4096 - rawio = self.MockRawIO() - bufio = self.tp(rawio, buffer_size=bufsize) - size = sys.getsizeof(bufio) - bufsize - bufio.close() - self.assertEqual(sys.getsizeof(bufio), size) - -class BufferedReaderTest(unittest.TestCase, CommonBufferedTests): - read_mode = "rb" - - def test_constructor(self): - rawio = self.MockRawIO([b"abc"]) - bufio = self.tp(rawio) - bufio.__init__(rawio) - bufio.__init__(rawio, buffer_size=1024) - bufio.__init__(rawio, buffer_size=16) - self.assertEqual(b"abc", bufio.read()) - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=0) - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-16) - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1) - rawio = self.MockRawIO([b"abc"]) - bufio.__init__(rawio) - self.assertEqual(b"abc", bufio.read()) - - def test_uninitialized(self): - bufio = self.tp.__new__(self.tp) - del bufio - bufio = self.tp.__new__(self.tp) - self.assertRaisesRegex((ValueError, AttributeError), - 'uninitialized|has no attribute', - bufio.read, 0) - bufio.__init__(self.MockRawIO()) - self.assertEqual(bufio.read(0), b'') - - def test_read(self): - for arg in (None, 7): - rawio = self.MockRawIO((b"abc", b"d", b"efg")) - bufio = self.tp(rawio) - self.assertEqual(b"abcdefg", bufio.read(arg)) - # Invalid args - self.assertRaises(ValueError, bufio.read, -2) - - def test_read1(self): - rawio = self.MockRawIO((b"abc", b"d", b"efg")) - bufio = self.tp(rawio) - self.assertEqual(b"a", bufio.read(1)) - self.assertEqual(b"b", bufio.read1(1)) - self.assertEqual(rawio._reads, 1) - self.assertEqual(b"", bufio.read1(0)) - self.assertEqual(b"c", bufio.read1(100)) - self.assertEqual(rawio._reads, 1) - self.assertEqual(b"d", bufio.read1(100)) - self.assertEqual(rawio._reads, 2) - self.assertEqual(b"efg", bufio.read1(100)) - self.assertEqual(rawio._reads, 3) - self.assertEqual(b"", bufio.read1(100)) - self.assertEqual(rawio._reads, 4) - - def test_read1_arbitrary(self): - rawio = self.MockRawIO((b"abc", b"d", b"efg")) - bufio = self.tp(rawio) - self.assertEqual(b"a", bufio.read(1)) - self.assertEqual(b"bc", bufio.read1()) - self.assertEqual(b"d", bufio.read1()) - self.assertEqual(b"efg", bufio.read1(-1)) - self.assertEqual(rawio._reads, 3) - self.assertEqual(b"", bufio.read1()) - self.assertEqual(rawio._reads, 4) - - def test_readinto(self): - rawio = self.MockRawIO((b"abc", b"d", b"efg")) - bufio = self.tp(rawio) - b = bytearray(2) - self.assertEqual(bufio.readinto(b), 2) - self.assertEqual(b, b"ab") - self.assertEqual(bufio.readinto(b), 2) - self.assertEqual(b, b"cd") - self.assertEqual(bufio.readinto(b), 2) - self.assertEqual(b, b"ef") - self.assertEqual(bufio.readinto(b), 1) - self.assertEqual(b, b"gf") - self.assertEqual(bufio.readinto(b), 0) - self.assertEqual(b, b"gf") - rawio = self.MockRawIO((b"abc", None)) - bufio = self.tp(rawio) - self.assertEqual(bufio.readinto(b), 2) - self.assertEqual(b, b"ab") - self.assertEqual(bufio.readinto(b), 1) - self.assertEqual(b, b"cb") - - def test_readinto1(self): - buffer_size = 10 - rawio = self.MockRawIO((b"abc", b"de", b"fgh", b"jkl")) - bufio = self.tp(rawio, buffer_size=buffer_size) - b = bytearray(2) - self.assertEqual(bufio.peek(3), b'abc') - self.assertEqual(rawio._reads, 1) - self.assertEqual(bufio.readinto1(b), 2) - self.assertEqual(b, b"ab") - self.assertEqual(rawio._reads, 1) - self.assertEqual(bufio.readinto1(b), 1) - self.assertEqual(b[:1], b"c") - self.assertEqual(rawio._reads, 1) - self.assertEqual(bufio.readinto1(b), 2) - self.assertEqual(b, b"de") - self.assertEqual(rawio._reads, 2) - b = bytearray(2*buffer_size) - self.assertEqual(bufio.peek(3), b'fgh') - self.assertEqual(rawio._reads, 3) - self.assertEqual(bufio.readinto1(b), 6) - self.assertEqual(b[:6], b"fghjkl") - self.assertEqual(rawio._reads, 4) - - def test_readinto_array(self): - buffer_size = 60 - data = b"a" * 26 - rawio = self.MockRawIO((data,)) - bufio = self.tp(rawio, buffer_size=buffer_size) - - # Create an array with element size > 1 byte - b = array.array('i', b'x' * 32) - assert len(b) != 16 - - # Read into it. We should get as many *bytes* as we can fit into b - # (which is more than the number of elements) - n = bufio.readinto(b) - self.assertGreater(n, len(b)) - - # Check that old contents of b are preserved - bm = memoryview(b).cast('B') - self.assertLess(n, len(bm)) - self.assertEqual(bm[:n], data[:n]) - self.assertEqual(bm[n:], b'x' * (len(bm[n:]))) - - def test_readinto1_array(self): - buffer_size = 60 - data = b"a" * 26 - rawio = self.MockRawIO((data,)) - bufio = self.tp(rawio, buffer_size=buffer_size) - - # Create an array with element size > 1 byte - b = array.array('i', b'x' * 32) - assert len(b) != 16 - - # Read into it. We should get as many *bytes* as we can fit into b - # (which is more than the number of elements) - n = bufio.readinto1(b) - self.assertGreater(n, len(b)) - - # Check that old contents of b are preserved - bm = memoryview(b).cast('B') - self.assertLess(n, len(bm)) - self.assertEqual(bm[:n], data[:n]) - self.assertEqual(bm[n:], b'x' * (len(bm[n:]))) - - def test_readlines(self): - def bufio(): - rawio = self.MockRawIO((b"abc\n", b"d\n", b"ef")) - return self.tp(rawio) - self.assertEqual(bufio().readlines(), [b"abc\n", b"d\n", b"ef"]) - self.assertEqual(bufio().readlines(5), [b"abc\n", b"d\n"]) - self.assertEqual(bufio().readlines(None), [b"abc\n", b"d\n", b"ef"]) - - def test_buffering(self): - data = b"abcdefghi" - dlen = len(data) - - tests = [ - [ 100, [ 3, 1, 4, 8 ], [ dlen, 0 ] ], - [ 100, [ 3, 3, 3], [ dlen ] ], - [ 4, [ 1, 2, 4, 2 ], [ 4, 4, 1 ] ], - ] - - for bufsize, buf_read_sizes, raw_read_sizes in tests: - rawio = self.MockFileIO(data) - bufio = self.tp(rawio, buffer_size=bufsize) - pos = 0 - for nbytes in buf_read_sizes: - self.assertEqual(bufio.read(nbytes), data[pos:pos+nbytes]) - pos += nbytes - # this is mildly implementation-dependent - self.assertEqual(rawio.read_history, raw_read_sizes) - - def test_read_non_blocking(self): - # Inject some None's in there to simulate EWOULDBLOCK - rawio = self.MockRawIO((b"abc", b"d", None, b"efg", None, None, None)) - bufio = self.tp(rawio) - self.assertEqual(b"abcd", bufio.read(6)) - self.assertEqual(b"e", bufio.read(1)) - self.assertEqual(b"fg", bufio.read()) - self.assertEqual(b"", bufio.peek(1)) - self.assertIsNone(bufio.read()) - self.assertEqual(b"", bufio.read()) - - rawio = self.MockRawIO((b"a", None, None)) - self.assertEqual(b"a", rawio.readall()) - self.assertIsNone(rawio.readall()) - - def test_read_past_eof(self): - rawio = self.MockRawIO((b"abc", b"d", b"efg")) - bufio = self.tp(rawio) - - self.assertEqual(b"abcdefg", bufio.read(9000)) - - def test_read_all(self): - rawio = self.MockRawIO((b"abc", b"d", b"efg")) - bufio = self.tp(rawio) - - self.assertEqual(b"abcdefg", bufio.read()) - - @threading_helper.requires_working_threading() - @support.requires_resource('cpu') - def test_threads(self): - try: - # Write out many bytes with exactly the same number of 0's, - # 1's... 255's. This will help us check that concurrent reading - # doesn't duplicate or forget contents. - N = 1000 - l = list(range(256)) * N - random.shuffle(l) - s = bytes(bytearray(l)) - with self.open(os_helper.TESTFN, "wb") as f: - f.write(s) - with self.open(os_helper.TESTFN, self.read_mode, buffering=0) as raw: - bufio = self.tp(raw, 8) - errors = [] - results = [] - def f(): - try: - # Intra-buffer read then buffer-flushing read - for n in cycle([1, 19]): - s = bufio.read(n) - if not s: - break - # list.append() is atomic - results.append(s) - except Exception as e: - errors.append(e) - raise - threads = [threading.Thread(target=f) for x in range(20)] - with threading_helper.start_threads(threads): - time.sleep(0.02) # yield - self.assertFalse(errors, - "the following exceptions were caught: %r" % errors) - s = b''.join(results) - for i in range(256): - c = bytes(bytearray([i])) - self.assertEqual(s.count(c), N) - finally: - os_helper.unlink(os_helper.TESTFN) - - def test_unseekable(self): - bufio = self.tp(self.MockUnseekableIO(b"A" * 10)) - self.assertRaises(self.UnsupportedOperation, bufio.tell) - self.assertRaises(self.UnsupportedOperation, bufio.seek, 0) - bufio.read(1) - self.assertRaises(self.UnsupportedOperation, bufio.seek, 0) - self.assertRaises(self.UnsupportedOperation, bufio.tell) - - def test_misbehaved_io(self): - rawio = self.MisbehavedRawIO((b"abc", b"d", b"efg")) - bufio = self.tp(rawio) - self.assertRaises(OSError, bufio.seek, 0) - self.assertRaises(OSError, bufio.tell) - - # Silence destructor error - bufio.close = lambda: None - - def test_no_extraneous_read(self): - # Issue #9550; when the raw IO object has satisfied the read request, - # we should not issue any additional reads, otherwise it may block - # (e.g. socket). - bufsize = 16 - for n in (2, bufsize - 1, bufsize, bufsize + 1, bufsize * 2): - rawio = self.MockRawIO([b"x" * n]) - bufio = self.tp(rawio, bufsize) - self.assertEqual(bufio.read(n), b"x" * n) - # Simple case: one raw read is enough to satisfy the request. - self.assertEqual(rawio._extraneous_reads, 0, - "failed for {}: {} != 0".format(n, rawio._extraneous_reads)) - # A more complex case where two raw reads are needed to satisfy - # the request. - rawio = self.MockRawIO([b"x" * (n - 1), b"x"]) - bufio = self.tp(rawio, bufsize) - self.assertEqual(bufio.read(n), b"x" * n) - self.assertEqual(rawio._extraneous_reads, 0, - "failed for {}: {} != 0".format(n, rawio._extraneous_reads)) - - def test_read_on_closed(self): - # Issue #23796 - b = self.BufferedReader(self.BytesIO(b"12")) - b.read(1) - b.close() - with self.subTest('peek'): - self.assertRaises(ValueError, b.peek) - with self.subTest('read1'): - self.assertRaises(ValueError, b.read1, 1) - with self.subTest('read'): - self.assertRaises(ValueError, b.read) - with self.subTest('readinto'): - self.assertRaises(ValueError, b.readinto, bytearray()) - with self.subTest('readinto1'): - self.assertRaises(ValueError, b.readinto1, bytearray()) - with self.subTest('flush'): - self.assertRaises(ValueError, b.flush) - with self.subTest('truncate'): - self.assertRaises(ValueError, b.truncate) - with self.subTest('seek'): - self.assertRaises(ValueError, b.seek, 0) - - def test_truncate_on_read_only(self): - rawio = self.MockFileIO(b"abc") - bufio = self.tp(rawio) - self.assertFalse(bufio.writable()) - self.assertRaises(self.UnsupportedOperation, bufio.truncate) - self.assertRaises(self.UnsupportedOperation, bufio.truncate, 0) - - def test_tell_character_device_file(self): - # GH-95782 - # For the (former) bug in BufferedIO to manifest, the wrapped IO obj - # must be able to produce at least 2 bytes. - raw = self.MockCharPseudoDevFileIO(b"12") - buf = self.tp(raw) - self.assertEqual(buf.tell(), 0) - self.assertEqual(buf.read(1), b"1") - self.assertEqual(buf.tell(), 0) - - def test_seek_character_device_file(self): - raw = self.MockCharPseudoDevFileIO(b"12") - buf = self.tp(raw) - self.assertEqual(buf.seek(0, io.SEEK_CUR), 0) - self.assertEqual(buf.seek(1, io.SEEK_SET), 0) - self.assertEqual(buf.seek(0, io.SEEK_CUR), 0) - self.assertEqual(buf.read(1), b"1") - - # In the C implementation, tell() sets the BufferedIO's abs_pos to 0, - # which means that the next seek() could return a negative offset if it - # does not sanity-check: - self.assertEqual(buf.tell(), 0) - self.assertEqual(buf.seek(0, io.SEEK_CUR), 0) - - -class CBufferedReaderTest(BufferedReaderTest, SizeofTest): - tp = io.BufferedReader - - def test_initialization(self): - rawio = self.MockRawIO([b"abc"]) - bufio = self.tp(rawio) - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=0) - self.assertRaises(ValueError, bufio.read) - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-16) - self.assertRaises(ValueError, bufio.read) - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1) - self.assertRaises(ValueError, bufio.read) - - def test_misbehaved_io_read(self): - rawio = self.MisbehavedRawIO((b"abc", b"d", b"efg")) - bufio = self.tp(rawio) - # _pyio.BufferedReader seems to implement reading different, so that - # checking this is not so easy. - self.assertRaises(OSError, bufio.read, 10) - - def test_garbage_collection(self): - # C BufferedReader objects are collected. - # The Python version has __del__, so it ends into gc.garbage instead - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with warnings_helper.check_warnings(('', ResourceWarning)): - rawio = self.FileIO(os_helper.TESTFN, "w+b") - f = self.tp(rawio) - f.f = f - wr = weakref.ref(f) - del f - support.gc_collect() - self.assertIsNone(wr(), wr) - - def test_args_error(self): - # Issue #17275 - with self.assertRaisesRegex(TypeError, "BufferedReader"): - self.tp(self.BytesIO(), 1024, 1024, 1024) - - def test_bad_readinto_value(self): - rawio = self.tp(self.BytesIO(b"12")) - rawio.readinto = lambda buf: -1 - bufio = self.tp(rawio) - with self.assertRaises(OSError) as cm: - bufio.readline() - self.assertIsNone(cm.exception.__cause__) - - def test_bad_readinto_type(self): - rawio = self.tp(self.BytesIO(b"12")) - rawio.readinto = lambda buf: b'' - bufio = self.tp(rawio) - with self.assertRaises(OSError) as cm: - bufio.readline() - self.assertIsInstance(cm.exception.__cause__, TypeError) - - -class PyBufferedReaderTest(BufferedReaderTest): - tp = pyio.BufferedReader - - -class BufferedWriterTest(unittest.TestCase, CommonBufferedTests): - write_mode = "wb" - - def test_constructor(self): - rawio = self.MockRawIO() - bufio = self.tp(rawio) - bufio.__init__(rawio) - bufio.__init__(rawio, buffer_size=1024) - bufio.__init__(rawio, buffer_size=16) - self.assertEqual(3, bufio.write(b"abc")) - bufio.flush() - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=0) - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-16) - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1) - bufio.__init__(rawio) - self.assertEqual(3, bufio.write(b"ghi")) - bufio.flush() - self.assertEqual(b"".join(rawio._write_stack), b"abcghi") - - def test_uninitialized(self): - bufio = self.tp.__new__(self.tp) - del bufio - bufio = self.tp.__new__(self.tp) - self.assertRaisesRegex((ValueError, AttributeError), - 'uninitialized|has no attribute', - bufio.write, b'') - bufio.__init__(self.MockRawIO()) - self.assertEqual(bufio.write(b''), 0) - - def test_detach_flush(self): - raw = self.MockRawIO() - buf = self.tp(raw) - buf.write(b"howdy!") - self.assertFalse(raw._write_stack) - buf.detach() - self.assertEqual(raw._write_stack, [b"howdy!"]) - - def test_write(self): - # Write to the buffered IO but don't overflow the buffer. - writer = self.MockRawIO() - bufio = self.tp(writer, 8) - bufio.write(b"abc") - self.assertFalse(writer._write_stack) - buffer = bytearray(b"def") - bufio.write(buffer) - buffer[:] = b"***" # Overwrite our copy of the data - bufio.flush() - self.assertEqual(b"".join(writer._write_stack), b"abcdef") - - def test_write_overflow(self): - writer = self.MockRawIO() - bufio = self.tp(writer, 8) - contents = b"abcdefghijklmnop" - for n in range(0, len(contents), 3): - bufio.write(contents[n:n+3]) - flushed = b"".join(writer._write_stack) - # At least (total - 8) bytes were implicitly flushed, perhaps more - # depending on the implementation. - self.assertStartsWith(flushed, contents[:-8]) - - def check_writes(self, intermediate_func): - # Lots of writes, test the flushed output is as expected. - contents = bytes(range(256)) * 1000 - n = 0 - writer = self.MockRawIO() - bufio = self.tp(writer, 13) - # Generator of write sizes: repeat each N 15 times then proceed to N+1 - def gen_sizes(): - for size in count(1): - for i in range(15): - yield size - sizes = gen_sizes() - while n < len(contents): - size = min(next(sizes), len(contents) - n) - self.assertEqual(bufio.write(contents[n:n+size]), size) - intermediate_func(bufio) - n += size - bufio.flush() - self.assertEqual(contents, b"".join(writer._write_stack)) - - def test_writes(self): - self.check_writes(lambda bufio: None) - - def test_writes_and_flushes(self): - self.check_writes(lambda bufio: bufio.flush()) - - def test_writes_and_seeks(self): - def _seekabs(bufio): - pos = bufio.tell() - bufio.seek(pos + 1, 0) - bufio.seek(pos - 1, 0) - bufio.seek(pos, 0) - self.check_writes(_seekabs) - def _seekrel(bufio): - pos = bufio.seek(0, 1) - bufio.seek(+1, 1) - bufio.seek(-1, 1) - bufio.seek(pos, 0) - self.check_writes(_seekrel) - - def test_writes_and_truncates(self): - self.check_writes(lambda bufio: bufio.truncate(bufio.tell())) - - def test_write_non_blocking(self): - raw = self.MockNonBlockWriterIO() - bufio = self.tp(raw, 8) - - self.assertEqual(bufio.write(b"abcd"), 4) - self.assertEqual(bufio.write(b"efghi"), 5) - # 1 byte will be written, the rest will be buffered - raw.block_on(b"k") - self.assertEqual(bufio.write(b"jklmn"), 5) - - # 8 bytes will be written, 8 will be buffered and the rest will be lost - raw.block_on(b"0") - try: - bufio.write(b"opqrwxyz0123456789") - except self.BlockingIOError as e: - written = e.characters_written - else: - self.fail("BlockingIOError should have been raised") - self.assertEqual(written, 16) - self.assertEqual(raw.pop_written(), - b"abcdefghijklmnopqrwxyz") - - self.assertEqual(bufio.write(b"ABCDEFGHI"), 9) - s = raw.pop_written() - # Previously buffered bytes were flushed - self.assertStartsWith(s, b"01234567A") - - def test_write_and_rewind(self): - raw = self.BytesIO() - bufio = self.tp(raw, 4) - self.assertEqual(bufio.write(b"abcdef"), 6) - self.assertEqual(bufio.tell(), 6) - bufio.seek(0, 0) - self.assertEqual(bufio.write(b"XY"), 2) - bufio.seek(6, 0) - self.assertEqual(raw.getvalue(), b"XYcdef") - self.assertEqual(bufio.write(b"123456"), 6) - bufio.flush() - self.assertEqual(raw.getvalue(), b"XYcdef123456") - - def test_flush(self): - writer = self.MockRawIO() - bufio = self.tp(writer, 8) - bufio.write(b"abc") - bufio.flush() - self.assertEqual(b"abc", writer._write_stack[0]) - - def test_writelines(self): - l = [b'ab', b'cd', b'ef'] - writer = self.MockRawIO() - bufio = self.tp(writer, 8) - bufio.writelines(l) - bufio.flush() - self.assertEqual(b''.join(writer._write_stack), b'abcdef') - - def test_writelines_userlist(self): - l = UserList([b'ab', b'cd', b'ef']) - writer = self.MockRawIO() - bufio = self.tp(writer, 8) - bufio.writelines(l) - bufio.flush() - self.assertEqual(b''.join(writer._write_stack), b'abcdef') - - def test_writelines_error(self): - writer = self.MockRawIO() - bufio = self.tp(writer, 8) - self.assertRaises(TypeError, bufio.writelines, [1, 2, 3]) - self.assertRaises(TypeError, bufio.writelines, None) - self.assertRaises(TypeError, bufio.writelines, 'abc') - - def test_destructor(self): - writer = self.MockRawIO() - bufio = self.tp(writer, 8) - bufio.write(b"abc") - del bufio - support.gc_collect() - self.assertEqual(b"abc", writer._write_stack[0]) - - def test_truncate(self): - # Truncate implicitly flushes the buffer. - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with self.open(os_helper.TESTFN, self.write_mode, buffering=0) as raw: - bufio = self.tp(raw, 8) - bufio.write(b"abcdef") - self.assertEqual(bufio.truncate(3), 3) - self.assertEqual(bufio.tell(), 6) - with self.open(os_helper.TESTFN, "rb", buffering=0) as f: - self.assertEqual(f.read(), b"abc") - - def test_truncate_after_write(self): - # Ensure that truncate preserves the file position after - # writes longer than the buffer size. - # Issue: https://bugs.python.org/issue32228 - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with self.open(os_helper.TESTFN, "wb") as f: - # Fill with some buffer - f.write(b'\x00' * 10000) - buffer_sizes = [8192, 4096, 200] - for buffer_size in buffer_sizes: - with self.open(os_helper.TESTFN, "r+b", buffering=buffer_size) as f: - f.write(b'\x00' * (buffer_size + 1)) - # After write write_pos and write_end are set to 0 - f.read(1) - # read operation makes sure that pos != raw_pos - f.truncate() - self.assertEqual(f.tell(), buffer_size + 2) - - @threading_helper.requires_working_threading() - @support.requires_resource('cpu') - def test_threads(self): - try: - # Write out many bytes from many threads and test they were - # all flushed. - N = 1000 - contents = bytes(range(256)) * N - sizes = cycle([1, 19]) - n = 0 - queue = deque() - while n < len(contents): - size = next(sizes) - queue.append(contents[n:n+size]) - n += size - del contents - # We use a real file object because it allows us to - # exercise situations where the GIL is released before - # writing the buffer to the raw streams. This is in addition - # to concurrency issues due to switching threads in the middle - # of Python code. - with self.open(os_helper.TESTFN, self.write_mode, buffering=0) as raw: - bufio = self.tp(raw, 8) - errors = [] - def f(): - try: - while True: - try: - s = queue.popleft() - except IndexError: - return - bufio.write(s) - except Exception as e: - errors.append(e) - raise - threads = [threading.Thread(target=f) for x in range(20)] - with threading_helper.start_threads(threads): - time.sleep(0.02) # yield - self.assertFalse(errors, - "the following exceptions were caught: %r" % errors) - bufio.close() - with self.open(os_helper.TESTFN, "rb") as f: - s = f.read() - for i in range(256): - self.assertEqual(s.count(bytes([i])), N) - finally: - os_helper.unlink(os_helper.TESTFN) - - def test_misbehaved_io(self): - rawio = self.MisbehavedRawIO() - bufio = self.tp(rawio, 5) - self.assertRaises(OSError, bufio.seek, 0) - self.assertRaises(OSError, bufio.tell) - self.assertRaises(OSError, bufio.write, b"abcdef") - - # Silence destructor error - bufio.close = lambda: None - - def test_max_buffer_size_removal(self): - with self.assertRaises(TypeError): - self.tp(self.MockRawIO(), 8, 12) - - def test_write_error_on_close(self): - raw = self.MockRawIO() - def bad_write(b): - raise OSError() - raw.write = bad_write - b = self.tp(raw) - b.write(b'spam') - self.assertRaises(OSError, b.close) # exception not swallowed - self.assertTrue(b.closed) - - @threading_helper.requires_working_threading() - def test_slow_close_from_thread(self): - # Issue #31976 - rawio = self.SlowFlushRawIO() - bufio = self.tp(rawio, 8) - t = threading.Thread(target=bufio.close) - t.start() - rawio.in_flush.wait() - self.assertRaises(ValueError, bufio.write, b'spam') - self.assertTrue(bufio.closed) - t.join() - - - -class CBufferedWriterTest(BufferedWriterTest, SizeofTest): - tp = io.BufferedWriter - - def test_initialization(self): - rawio = self.MockRawIO() - bufio = self.tp(rawio) - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=0) - self.assertRaises(ValueError, bufio.write, b"def") - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-16) - self.assertRaises(ValueError, bufio.write, b"def") - self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1) - self.assertRaises(ValueError, bufio.write, b"def") - - def test_garbage_collection(self): - # C BufferedWriter objects are collected, and collecting them flushes - # all data to disk. - # The Python version has __del__, so it ends into gc.garbage instead - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - with warnings_helper.check_warnings(('', ResourceWarning)): - rawio = self.FileIO(os_helper.TESTFN, "w+b") - f = self.tp(rawio) - f.write(b"123xxx") - f.x = f - wr = weakref.ref(f) - del f - support.gc_collect() - self.assertIsNone(wr(), wr) - with self.open(os_helper.TESTFN, "rb") as f: - self.assertEqual(f.read(), b"123xxx") - - def test_args_error(self): - # Issue #17275 - with self.assertRaisesRegex(TypeError, "BufferedWriter"): - self.tp(self.BytesIO(), 1024, 1024, 1024) - - -class PyBufferedWriterTest(BufferedWriterTest): - tp = pyio.BufferedWriter - -class BufferedRWPairTest(unittest.TestCase): - - def test_constructor(self): - pair = self.tp(self.MockRawIO(), self.MockRawIO()) - self.assertFalse(pair.closed) - - def test_uninitialized(self): - pair = self.tp.__new__(self.tp) - del pair - pair = self.tp.__new__(self.tp) - self.assertRaisesRegex((ValueError, AttributeError), - 'uninitialized|has no attribute', - pair.read, 0) - self.assertRaisesRegex((ValueError, AttributeError), - 'uninitialized|has no attribute', - pair.write, b'') - pair.__init__(self.MockRawIO(), self.MockRawIO()) - self.assertEqual(pair.read(0), b'') - self.assertEqual(pair.write(b''), 0) - - def test_detach(self): - pair = self.tp(self.MockRawIO(), self.MockRawIO()) - self.assertRaises(self.UnsupportedOperation, pair.detach) - - def test_constructor_max_buffer_size_removal(self): - with self.assertRaises(TypeError): - self.tp(self.MockRawIO(), self.MockRawIO(), 8, 12) - - def test_constructor_with_not_readable(self): - class NotReadable(MockRawIO): - def readable(self): - return False - - self.assertRaises(OSError, self.tp, NotReadable(), self.MockRawIO()) - - def test_constructor_with_not_writeable(self): - class NotWriteable(MockRawIO): - def writable(self): - return False - - self.assertRaises(OSError, self.tp, self.MockRawIO(), NotWriteable()) - - def test_read(self): - pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO()) - - self.assertEqual(pair.read(3), b"abc") - self.assertEqual(pair.read(1), b"d") - self.assertEqual(pair.read(), b"ef") - pair = self.tp(self.BytesIO(b"abc"), self.MockRawIO()) - self.assertEqual(pair.read(None), b"abc") - - def test_readlines(self): - pair = lambda: self.tp(self.BytesIO(b"abc\ndef\nh"), self.MockRawIO()) - self.assertEqual(pair().readlines(), [b"abc\n", b"def\n", b"h"]) - self.assertEqual(pair().readlines(), [b"abc\n", b"def\n", b"h"]) - self.assertEqual(pair().readlines(5), [b"abc\n", b"def\n"]) - - def test_read1(self): - # .read1() is delegated to the underlying reader object, so this test - # can be shallow. - pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO()) - - self.assertEqual(pair.read1(3), b"abc") - self.assertEqual(pair.read1(), b"def") - - def test_readinto(self): - for method in ("readinto", "readinto1"): - with self.subTest(method): - pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO()) - - data = byteslike(b'\0' * 5) - self.assertEqual(getattr(pair, method)(data), 5) - self.assertEqual(bytes(data), b"abcde") - - def test_write(self): - w = self.MockRawIO() - pair = self.tp(self.MockRawIO(), w) - - pair.write(b"abc") - pair.flush() - buffer = bytearray(b"def") - pair.write(buffer) - buffer[:] = b"***" # Overwrite our copy of the data - pair.flush() - self.assertEqual(w._write_stack, [b"abc", b"def"]) - - def test_peek(self): - pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO()) - - self.assertStartsWith(pair.peek(3), b"abc") - self.assertEqual(pair.read(3), b"abc") - - def test_readable(self): - pair = self.tp(self.MockRawIO(), self.MockRawIO()) - self.assertTrue(pair.readable()) - - def test_writeable(self): - pair = self.tp(self.MockRawIO(), self.MockRawIO()) - self.assertTrue(pair.writable()) - - def test_seekable(self): - # BufferedRWPairs are never seekable, even if their readers and writers - # are. - pair = self.tp(self.MockRawIO(), self.MockRawIO()) - self.assertFalse(pair.seekable()) - - # .flush() is delegated to the underlying writer object and has been - # tested in the test_write method. - - def test_close_and_closed(self): - pair = self.tp(self.MockRawIO(), self.MockRawIO()) - self.assertFalse(pair.closed) - pair.close() - self.assertTrue(pair.closed) - - def test_reader_close_error_on_close(self): - def reader_close(): - reader_non_existing - reader = self.MockRawIO() - reader.close = reader_close - writer = self.MockRawIO() - pair = self.tp(reader, writer) - with self.assertRaises(NameError) as err: - pair.close() - self.assertIn('reader_non_existing', str(err.exception)) - self.assertTrue(pair.closed) - self.assertFalse(reader.closed) - self.assertTrue(writer.closed) - - # Silence destructor error - reader.close = lambda: None - - def test_writer_close_error_on_close(self): - def writer_close(): - writer_non_existing - reader = self.MockRawIO() - writer = self.MockRawIO() - writer.close = writer_close - pair = self.tp(reader, writer) - with self.assertRaises(NameError) as err: - pair.close() - self.assertIn('writer_non_existing', str(err.exception)) - self.assertFalse(pair.closed) - self.assertTrue(reader.closed) - self.assertFalse(writer.closed) - - # Silence destructor error - writer.close = lambda: None - writer = None - - # Ignore BufferedWriter (of the BufferedRWPair) unraisable exception - with support.catch_unraisable_exception(): - # Ignore BufferedRWPair unraisable exception - with support.catch_unraisable_exception(): - pair = None - support.gc_collect() - support.gc_collect() - - def test_reader_writer_close_error_on_close(self): - def reader_close(): - reader_non_existing - def writer_close(): - writer_non_existing - reader = self.MockRawIO() - reader.close = reader_close - writer = self.MockRawIO() - writer.close = writer_close - pair = self.tp(reader, writer) - with self.assertRaises(NameError) as err: - pair.close() - self.assertIn('reader_non_existing', str(err.exception)) - self.assertIsInstance(err.exception.__context__, NameError) - self.assertIn('writer_non_existing', str(err.exception.__context__)) - self.assertFalse(pair.closed) - self.assertFalse(reader.closed) - self.assertFalse(writer.closed) - - # Silence destructor error - reader.close = lambda: None - writer.close = lambda: None - - def test_isatty(self): - class SelectableIsAtty(MockRawIO): - def __init__(self, isatty): - MockRawIO.__init__(self) - self._isatty = isatty - - def isatty(self): - return self._isatty - - pair = self.tp(SelectableIsAtty(False), SelectableIsAtty(False)) - self.assertFalse(pair.isatty()) - - pair = self.tp(SelectableIsAtty(True), SelectableIsAtty(False)) - self.assertTrue(pair.isatty()) - - pair = self.tp(SelectableIsAtty(False), SelectableIsAtty(True)) - self.assertTrue(pair.isatty()) - - pair = self.tp(SelectableIsAtty(True), SelectableIsAtty(True)) - self.assertTrue(pair.isatty()) - - def test_weakref_clearing(self): - brw = self.tp(self.MockRawIO(), self.MockRawIO()) - ref = weakref.ref(brw) - brw = None - ref = None # Shouldn't segfault. - -class CBufferedRWPairTest(BufferedRWPairTest): - tp = io.BufferedRWPair - -class PyBufferedRWPairTest(BufferedRWPairTest): - tp = pyio.BufferedRWPair - - -class BufferedRandomTest(BufferedReaderTest, BufferedWriterTest): - read_mode = "rb+" - write_mode = "wb+" - - def test_constructor(self): - BufferedReaderTest.test_constructor(self) - BufferedWriterTest.test_constructor(self) - - def test_uninitialized(self): - BufferedReaderTest.test_uninitialized(self) - BufferedWriterTest.test_uninitialized(self) - - def test_read_and_write(self): - raw = self.MockRawIO((b"asdf", b"ghjk")) - rw = self.tp(raw, 8) - - self.assertEqual(b"as", rw.read(2)) - rw.write(b"ddd") - rw.write(b"eee") - self.assertFalse(raw._write_stack) # Buffer writes - self.assertEqual(b"ghjk", rw.read()) - self.assertEqual(b"dddeee", raw._write_stack[0]) - - def test_seek_and_tell(self): - raw = self.BytesIO(b"asdfghjkl") - rw = self.tp(raw) - - self.assertEqual(b"as", rw.read(2)) - self.assertEqual(2, rw.tell()) - rw.seek(0, 0) - self.assertEqual(b"asdf", rw.read(4)) - - rw.write(b"123f") - rw.seek(0, 0) - self.assertEqual(b"asdf123fl", rw.read()) - self.assertEqual(9, rw.tell()) - rw.seek(-4, 2) - self.assertEqual(5, rw.tell()) - rw.seek(2, 1) - self.assertEqual(7, rw.tell()) - self.assertEqual(b"fl", rw.read(11)) - rw.flush() - self.assertEqual(b"asdf123fl", raw.getvalue()) - - self.assertRaises(TypeError, rw.seek, 0.0) - - def check_flush_and_read(self, read_func): - raw = self.BytesIO(b"abcdefghi") - bufio = self.tp(raw) - - self.assertEqual(b"ab", read_func(bufio, 2)) - bufio.write(b"12") - self.assertEqual(b"ef", read_func(bufio, 2)) - self.assertEqual(6, bufio.tell()) - bufio.flush() - self.assertEqual(6, bufio.tell()) - self.assertEqual(b"ghi", read_func(bufio)) - raw.seek(0, 0) - raw.write(b"XYZ") - # flush() resets the read buffer - bufio.flush() - bufio.seek(0, 0) - self.assertEqual(b"XYZ", read_func(bufio, 3)) - - def test_flush_and_read(self): - self.check_flush_and_read(lambda bufio, *args: bufio.read(*args)) - - def test_flush_and_readinto(self): - def _readinto(bufio, n=-1): - b = bytearray(n if n >= 0 else 9999) - n = bufio.readinto(b) - return bytes(b[:n]) - self.check_flush_and_read(_readinto) - - def test_flush_and_peek(self): - def _peek(bufio, n=-1): - # This relies on the fact that the buffer can contain the whole - # raw stream, otherwise peek() can return less. - b = bufio.peek(n) - if n != -1: - b = b[:n] - bufio.seek(len(b), 1) - return b - self.check_flush_and_read(_peek) - - def test_flush_and_write(self): - raw = self.BytesIO(b"abcdefghi") - bufio = self.tp(raw) - - bufio.write(b"123") - bufio.flush() - bufio.write(b"45") - bufio.flush() - bufio.seek(0, 0) - self.assertEqual(b"12345fghi", raw.getvalue()) - self.assertEqual(b"12345fghi", bufio.read()) - - def test_threads(self): - BufferedReaderTest.test_threads(self) - BufferedWriterTest.test_threads(self) - - def test_writes_and_peek(self): - def _peek(bufio): - bufio.peek(1) - self.check_writes(_peek) - def _peek(bufio): - pos = bufio.tell() - bufio.seek(-1, 1) - bufio.peek(1) - bufio.seek(pos, 0) - self.check_writes(_peek) - - def test_writes_and_reads(self): - def _read(bufio): - bufio.seek(-1, 1) - bufio.read(1) - self.check_writes(_read) - - def test_writes_and_read1s(self): - def _read1(bufio): - bufio.seek(-1, 1) - bufio.read1(1) - self.check_writes(_read1) - - def test_writes_and_readintos(self): - def _read(bufio): - bufio.seek(-1, 1) - bufio.readinto(bytearray(1)) - self.check_writes(_read) - - def test_write_after_readahead(self): - # Issue #6629: writing after the buffer was filled by readahead should - # first rewind the raw stream. - for overwrite_size in [1, 5]: - raw = self.BytesIO(b"A" * 10) - bufio = self.tp(raw, 4) - # Trigger readahead - self.assertEqual(bufio.read(1), b"A") - self.assertEqual(bufio.tell(), 1) - # Overwriting should rewind the raw stream if it needs so - bufio.write(b"B" * overwrite_size) - self.assertEqual(bufio.tell(), overwrite_size + 1) - # If the write size was smaller than the buffer size, flush() and - # check that rewind happens. - bufio.flush() - self.assertEqual(bufio.tell(), overwrite_size + 1) - s = raw.getvalue() - self.assertEqual(s, - b"A" + b"B" * overwrite_size + b"A" * (9 - overwrite_size)) - - def test_write_rewind_write(self): - # Various combinations of reading / writing / seeking backwards / writing again - def mutate(bufio, pos1, pos2): - assert pos2 >= pos1 - # Fill the buffer - bufio.seek(pos1) - bufio.read(pos2 - pos1) - bufio.write(b'\x02') - # This writes earlier than the previous write, but still inside - # the buffer. - bufio.seek(pos1) - bufio.write(b'\x01') - - b = b"\x80\x81\x82\x83\x84" - for i in range(0, len(b)): - for j in range(i, len(b)): - raw = self.BytesIO(b) - bufio = self.tp(raw, 100) - mutate(bufio, i, j) - bufio.flush() - expected = bytearray(b) - expected[j] = 2 - expected[i] = 1 - self.assertEqual(raw.getvalue(), expected, - "failed result for i=%d, j=%d" % (i, j)) - - def test_truncate_after_read_or_write(self): - raw = self.BytesIO(b"A" * 10) - bufio = self.tp(raw, 100) - self.assertEqual(bufio.read(2), b"AA") # the read buffer gets filled - self.assertEqual(bufio.truncate(), 2) - self.assertEqual(bufio.write(b"BB"), 2) # the write buffer increases - self.assertEqual(bufio.truncate(), 4) - - def test_misbehaved_io(self): - BufferedReaderTest.test_misbehaved_io(self) - BufferedWriterTest.test_misbehaved_io(self) - - def test_interleaved_read_write(self): - # Test for issue #12213 - with self.BytesIO(b'abcdefgh') as raw: - with self.tp(raw, 100) as f: - f.write(b"1") - self.assertEqual(f.read(1), b'b') - f.write(b'2') - self.assertEqual(f.read1(1), b'd') - f.write(b'3') - buf = bytearray(1) - f.readinto(buf) - self.assertEqual(buf, b'f') - f.write(b'4') - self.assertEqual(f.peek(1), b'h') - f.flush() - self.assertEqual(raw.getvalue(), b'1b2d3f4h') - - with self.BytesIO(b'abc') as raw: - with self.tp(raw, 100) as f: - self.assertEqual(f.read(1), b'a') - f.write(b"2") - self.assertEqual(f.read(1), b'c') - f.flush() - self.assertEqual(raw.getvalue(), b'a2c') - - def test_read1_after_write(self): - with self.BytesIO(b'abcdef') as raw: - with self.tp(raw, 3) as f: - f.write(b"1") - self.assertEqual(f.read1(1), b'b') - f.flush() - self.assertEqual(raw.getvalue(), b'1bcdef') - with self.BytesIO(b'abcdef') as raw: - with self.tp(raw, 3) as f: - f.write(b"1") - self.assertEqual(f.read1(), b'bcd') - f.flush() - self.assertEqual(raw.getvalue(), b'1bcdef') - with self.BytesIO(b'abcdef') as raw: - with self.tp(raw, 3) as f: - f.write(b"1") - # XXX: read(100) returns different numbers of bytes - # in Python and C implementations. - self.assertEqual(f.read1(100)[:3], b'bcd') - f.flush() - self.assertEqual(raw.getvalue(), b'1bcdef') - - def test_interleaved_readline_write(self): - with self.BytesIO(b'ab\ncdef\ng\n') as raw: - with self.tp(raw) as f: - f.write(b'1') - self.assertEqual(f.readline(), b'b\n') - f.write(b'2') - self.assertEqual(f.readline(), b'def\n') - f.write(b'3') - self.assertEqual(f.readline(), b'\n') - f.flush() - self.assertEqual(raw.getvalue(), b'1b\n2def\n3\n') - - # You can't construct a BufferedRandom over a non-seekable stream. - test_unseekable = None - - # writable() returns True, so there's no point to test it over - # a writable stream. - test_truncate_on_read_only = None - - -class CBufferedRandomTest(BufferedRandomTest, SizeofTest): - tp = io.BufferedRandom - - def test_garbage_collection(self): - CBufferedReaderTest.test_garbage_collection(self) - CBufferedWriterTest.test_garbage_collection(self) - - def test_args_error(self): - # Issue #17275 - with self.assertRaisesRegex(TypeError, "BufferedRandom"): - self.tp(self.BytesIO(), 1024, 1024, 1024) - - -class PyBufferedRandomTest(BufferedRandomTest): - tp = pyio.BufferedRandom - - -# To fully exercise seek/tell, the StatefulIncrementalDecoder has these -# properties: -# - A single output character can correspond to many bytes of input. -# - The number of input bytes to complete the character can be -# undetermined until the last input byte is received. -# - The number of input bytes can vary depending on previous input. -# - A single input byte can correspond to many characters of output. -# - The number of output characters can be undetermined until the -# last input byte is received. -# - The number of output characters can vary depending on previous input. - -class StatefulIncrementalDecoder(codecs.IncrementalDecoder): - """ - For testing seek/tell behavior with a stateful, buffering decoder. - - Input is a sequence of words. Words may be fixed-length (length set - by input) or variable-length (period-terminated). In variable-length - mode, extra periods are ignored. Possible words are: - - 'i' followed by a number sets the input length, I (maximum 99). - When I is set to 0, words are space-terminated. - - 'o' followed by a number sets the output length, O (maximum 99). - - Any other word is converted into a word followed by a period on - the output. The output word consists of the input word truncated - or padded out with hyphens to make its length equal to O. If O - is 0, the word is output verbatim without truncating or padding. - I and O are initially set to 1. When I changes, any buffered input is - re-scanned according to the new I. EOF also terminates the last word. - """ - - def __init__(self, errors='strict'): - codecs.IncrementalDecoder.__init__(self, errors) - self.reset() - - def __repr__(self): - return '<SID %x>' % id(self) - - def reset(self): - self.i = 1 - self.o = 1 - self.buffer = bytearray() - - def getstate(self): - i, o = self.i ^ 1, self.o ^ 1 # so that flags = 0 after reset() - return bytes(self.buffer), i*100 + o - - def setstate(self, state): - buffer, io = state - self.buffer = bytearray(buffer) - i, o = divmod(io, 100) - self.i, self.o = i ^ 1, o ^ 1 - - def decode(self, input, final=False): - output = '' - for b in input: - if self.i == 0: # variable-length, terminated with period - if b == ord('.'): - if self.buffer: - output += self.process_word() - else: - self.buffer.append(b) - else: # fixed-length, terminate after self.i bytes - self.buffer.append(b) - if len(self.buffer) == self.i: - output += self.process_word() - if final and self.buffer: # EOF terminates the last word - output += self.process_word() - return output - - def process_word(self): - output = '' - if self.buffer[0] == ord('i'): - self.i = min(99, int(self.buffer[1:] or 0)) # set input length - elif self.buffer[0] == ord('o'): - self.o = min(99, int(self.buffer[1:] or 0)) # set output length - else: - output = self.buffer.decode('ascii') - if len(output) < self.o: - output += '-'*self.o # pad out with hyphens - if self.o: - output = output[:self.o] # truncate to output length - output += '.' - self.buffer = bytearray() - return output - - codecEnabled = False - - -# bpo-41919: This method is separated from StatefulIncrementalDecoder to avoid a resource leak -# when registering codecs and cleanup functions. -def lookupTestDecoder(name): - if StatefulIncrementalDecoder.codecEnabled and name == 'test_decoder': - latin1 = codecs.lookup('latin-1') - return codecs.CodecInfo( - name='test_decoder', encode=latin1.encode, decode=None, - incrementalencoder=None, - streamreader=None, streamwriter=None, - incrementaldecoder=StatefulIncrementalDecoder) - - -class StatefulIncrementalDecoderTest(unittest.TestCase): - """ - Make sure the StatefulIncrementalDecoder actually works. - """ - - test_cases = [ - # I=1, O=1 (fixed-length input == fixed-length output) - (b'abcd', False, 'a.b.c.d.'), - # I=0, O=0 (variable-length input, variable-length output) - (b'oiabcd', True, 'abcd.'), - # I=0, O=0 (should ignore extra periods) - (b'oi...abcd...', True, 'abcd.'), - # I=0, O=6 (variable-length input, fixed-length output) - (b'i.o6.x.xyz.toolongtofit.', False, 'x-----.xyz---.toolon.'), - # I=2, O=6 (fixed-length input < fixed-length output) - (b'i.i2.o6xyz', True, 'xy----.z-----.'), - # I=6, O=3 (fixed-length input > fixed-length output) - (b'i.o3.i6.abcdefghijklmnop', True, 'abc.ghi.mno.'), - # I=0, then 3; O=29, then 15 (with longer output) - (b'i.o29.a.b.cde.o15.abcdefghijabcdefghij.i3.a.b.c.d.ei00k.l.m', True, - 'a----------------------------.' + - 'b----------------------------.' + - 'cde--------------------------.' + - 'abcdefghijabcde.' + - 'a.b------------.' + - '.c.------------.' + - 'd.e------------.' + - 'k--------------.' + - 'l--------------.' + - 'm--------------.') - ] - - def test_decoder(self): - # Try a few one-shot test cases. - for input, eof, output in self.test_cases: - d = StatefulIncrementalDecoder() - self.assertEqual(d.decode(input, eof), output) - - # Also test an unfinished decode, followed by forcing EOF. - d = StatefulIncrementalDecoder() - self.assertEqual(d.decode(b'oiabcd'), '') - self.assertEqual(d.decode(b'', 1), 'abcd.') - -class TextIOWrapperTest(unittest.TestCase): - - def setUp(self): - self.testdata = b"AAA\r\nBBB\rCCC\r\nDDD\nEEE\r\n" - self.normalized = b"AAA\nBBB\nCCC\nDDD\nEEE\n".decode("ascii") - os_helper.unlink(os_helper.TESTFN) - codecs.register(lookupTestDecoder) - self.addCleanup(codecs.unregister, lookupTestDecoder) - - def tearDown(self): - os_helper.unlink(os_helper.TESTFN) - - def test_constructor(self): - r = self.BytesIO(b"\xc3\xa9\n\n") - b = self.BufferedReader(r, 1000) - t = self.TextIOWrapper(b, encoding="utf-8") - t.__init__(b, encoding="latin-1", newline="\r\n") - self.assertEqual(t.encoding, "latin-1") - self.assertEqual(t.line_buffering, False) - t.__init__(b, encoding="utf-8", line_buffering=True) - self.assertEqual(t.encoding, "utf-8") - self.assertEqual(t.line_buffering, True) - self.assertEqual("\xe9\n", t.readline()) - invalid_type = TypeError if self.is_C else ValueError - with self.assertRaises(invalid_type): - t.__init__(b, encoding=42) - with self.assertRaises(UnicodeEncodeError): - t.__init__(b, encoding='\udcfe') - with self.assertRaises(ValueError): - t.__init__(b, encoding='utf-8\0') - with self.assertRaises(invalid_type): - t.__init__(b, encoding="utf-8", errors=42) - if support.Py_DEBUG or sys.flags.dev_mode or self.is_C: - with self.assertRaises(UnicodeEncodeError): - t.__init__(b, encoding="utf-8", errors='\udcfe') - if support.Py_DEBUG or sys.flags.dev_mode or self.is_C: - with self.assertRaises(ValueError): - t.__init__(b, encoding="utf-8", errors='replace\0') - with self.assertRaises(TypeError): - t.__init__(b, encoding="utf-8", newline=42) - with self.assertRaises(ValueError): - t.__init__(b, encoding="utf-8", newline='\udcfe') - with self.assertRaises(ValueError): - t.__init__(b, encoding="utf-8", newline='\n\0') - with self.assertRaises(ValueError): - t.__init__(b, encoding="utf-8", newline='xyzzy') - - def test_uninitialized(self): - t = self.TextIOWrapper.__new__(self.TextIOWrapper) - del t - t = self.TextIOWrapper.__new__(self.TextIOWrapper) - self.assertRaises(Exception, repr, t) - self.assertRaisesRegex((ValueError, AttributeError), - 'uninitialized|has no attribute', - t.read, 0) - t.__init__(self.MockRawIO(), encoding="utf-8") - self.assertEqual(t.read(0), '') - - def test_non_text_encoding_codecs_are_rejected(self): - # Ensure the constructor complains if passed a codec that isn't - # marked as a text encoding - # http://bugs.python.org/issue20404 - r = self.BytesIO() - b = self.BufferedWriter(r) - with self.assertRaisesRegex(LookupError, "is not a text encoding"): - self.TextIOWrapper(b, encoding="hex") - - def test_detach(self): - r = self.BytesIO() - b = self.BufferedWriter(r) - t = self.TextIOWrapper(b, encoding="ascii") - self.assertIs(t.detach(), b) - - t = self.TextIOWrapper(b, encoding="ascii") - t.write("howdy") - self.assertFalse(r.getvalue()) - t.detach() - self.assertEqual(r.getvalue(), b"howdy") - self.assertRaises(ValueError, t.detach) - - # Operations independent of the detached stream should still work - repr(t) - self.assertEqual(t.encoding, "ascii") - self.assertEqual(t.errors, "strict") - self.assertFalse(t.line_buffering) - self.assertFalse(t.write_through) - - def test_repr(self): - raw = self.BytesIO("hello".encode("utf-8")) - b = self.BufferedReader(raw) - t = self.TextIOWrapper(b, encoding="utf-8") - modname = self.TextIOWrapper.__module__ - self.assertRegex(repr(t), - r"<(%s\.)?TextIOWrapper encoding='utf-8'>" % modname) - raw.name = "dummy" - self.assertRegex(repr(t), - r"<(%s\.)?TextIOWrapper name='dummy' encoding='utf-8'>" % modname) - t.mode = "r" - self.assertRegex(repr(t), - r"<(%s\.)?TextIOWrapper name='dummy' mode='r' encoding='utf-8'>" % modname) - raw.name = b"dummy" - self.assertRegex(repr(t), - r"<(%s\.)?TextIOWrapper name=b'dummy' mode='r' encoding='utf-8'>" % modname) - - t.buffer.detach() - repr(t) # Should not raise an exception - - def test_recursive_repr(self): - # Issue #25455 - raw = self.BytesIO() - t = self.TextIOWrapper(raw, encoding="utf-8") - with support.swap_attr(raw, 'name', t), support.infinite_recursion(25): - with self.assertRaises(RuntimeError): - repr(t) # Should not crash - - def test_subclass_repr(self): - class TestSubclass(self.TextIOWrapper): - pass - - f = TestSubclass(self.StringIO()) - self.assertIn(TestSubclass.__name__, repr(f)) - - def test_line_buffering(self): - r = self.BytesIO() - b = self.BufferedWriter(r, 1000) - t = self.TextIOWrapper(b, encoding="utf-8", newline="\n", line_buffering=True) - t.write("X") - self.assertEqual(r.getvalue(), b"") # No flush happened - t.write("Y\nZ") - self.assertEqual(r.getvalue(), b"XY\nZ") # All got flushed - t.write("A\rB") - self.assertEqual(r.getvalue(), b"XY\nZA\rB") - - def test_reconfigure_line_buffering(self): - r = self.BytesIO() - b = self.BufferedWriter(r, 1000) - t = self.TextIOWrapper(b, encoding="utf-8", newline="\n", line_buffering=False) - t.write("AB\nC") - self.assertEqual(r.getvalue(), b"") - - t.reconfigure(line_buffering=True) # implicit flush - self.assertEqual(r.getvalue(), b"AB\nC") - t.write("DEF\nG") - self.assertEqual(r.getvalue(), b"AB\nCDEF\nG") - t.write("H") - self.assertEqual(r.getvalue(), b"AB\nCDEF\nG") - t.reconfigure(line_buffering=False) # implicit flush - self.assertEqual(r.getvalue(), b"AB\nCDEF\nGH") - t.write("IJ") - self.assertEqual(r.getvalue(), b"AB\nCDEF\nGH") - - # Keeping default value - t.reconfigure() - t.reconfigure(line_buffering=None) - self.assertEqual(t.line_buffering, False) - t.reconfigure(line_buffering=True) - t.reconfigure() - t.reconfigure(line_buffering=None) - self.assertEqual(t.line_buffering, True) - - @unittest.skipIf(sys.flags.utf8_mode, "utf-8 mode is enabled") - def test_default_encoding(self): - with os_helper.EnvironmentVarGuard() as env: - # try to get a user preferred encoding different than the current - # locale encoding to check that TextIOWrapper() uses the current - # locale encoding and not the user preferred encoding - env.unset('LC_ALL', 'LANG', 'LC_CTYPE') - - current_locale_encoding = locale.getencoding() - b = self.BytesIO() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", EncodingWarning) - t = self.TextIOWrapper(b) - self.assertEqual(t.encoding, current_locale_encoding) - - def test_encoding(self): - # Check the encoding attribute is always set, and valid - b = self.BytesIO() - t = self.TextIOWrapper(b, encoding="utf-8") - self.assertEqual(t.encoding, "utf-8") - with warnings.catch_warnings(): - warnings.simplefilter("ignore", EncodingWarning) - t = self.TextIOWrapper(b) - self.assertIsNotNone(t.encoding) - codecs.lookup(t.encoding) - - def test_encoding_errors_reading(self): - # (1) default - b = self.BytesIO(b"abc\n\xff\n") - t = self.TextIOWrapper(b, encoding="ascii") - self.assertRaises(UnicodeError, t.read) - # (2) explicit strict - b = self.BytesIO(b"abc\n\xff\n") - t = self.TextIOWrapper(b, encoding="ascii", errors="strict") - self.assertRaises(UnicodeError, t.read) - # (3) ignore - b = self.BytesIO(b"abc\n\xff\n") - t = self.TextIOWrapper(b, encoding="ascii", errors="ignore") - self.assertEqual(t.read(), "abc\n\n") - # (4) replace - b = self.BytesIO(b"abc\n\xff\n") - t = self.TextIOWrapper(b, encoding="ascii", errors="replace") - self.assertEqual(t.read(), "abc\n\ufffd\n") - - def test_encoding_errors_writing(self): - # (1) default - b = self.BytesIO() - t = self.TextIOWrapper(b, encoding="ascii") - self.assertRaises(UnicodeError, t.write, "\xff") - # (2) explicit strict - b = self.BytesIO() - t = self.TextIOWrapper(b, encoding="ascii", errors="strict") - self.assertRaises(UnicodeError, t.write, "\xff") - # (3) ignore - b = self.BytesIO() - t = self.TextIOWrapper(b, encoding="ascii", errors="ignore", - newline="\n") - t.write("abc\xffdef\n") - t.flush() - self.assertEqual(b.getvalue(), b"abcdef\n") - # (4) replace - b = self.BytesIO() - t = self.TextIOWrapper(b, encoding="ascii", errors="replace", - newline="\n") - t.write("abc\xffdef\n") - t.flush() - self.assertEqual(b.getvalue(), b"abc?def\n") - - def test_newlines(self): - input_lines = [ "unix\n", "windows\r\n", "os9\r", "last\n", "nonl" ] - - tests = [ - [ None, [ 'unix\n', 'windows\n', 'os9\n', 'last\n', 'nonl' ] ], - [ '', input_lines ], - [ '\n', [ "unix\n", "windows\r\n", "os9\rlast\n", "nonl" ] ], - [ '\r\n', [ "unix\nwindows\r\n", "os9\rlast\nnonl" ] ], - [ '\r', [ "unix\nwindows\r", "\nos9\r", "last\nnonl" ] ], - ] - encodings = ( - 'utf-8', 'latin-1', - 'utf-16', 'utf-16-le', 'utf-16-be', - 'utf-32', 'utf-32-le', 'utf-32-be', - ) - - # Try a range of buffer sizes to test the case where \r is the last - # character in TextIOWrapper._pending_line. - for encoding in encodings: - # XXX: str.encode() should return bytes - data = bytes(''.join(input_lines).encode(encoding)) - for do_reads in (False, True): - for bufsize in range(1, 10): - for newline, exp_lines in tests: - bufio = self.BufferedReader(self.BytesIO(data), bufsize) - textio = self.TextIOWrapper(bufio, newline=newline, - encoding=encoding) - if do_reads: - got_lines = [] - while True: - c2 = textio.read(2) - if c2 == '': - break - self.assertEqual(len(c2), 2) - got_lines.append(c2 + textio.readline()) - else: - got_lines = list(textio) - - for got_line, exp_line in zip(got_lines, exp_lines): - self.assertEqual(got_line, exp_line) - self.assertEqual(len(got_lines), len(exp_lines)) - - def test_newlines_input(self): - testdata = b"AAA\nBB\x00B\nCCC\rDDD\rEEE\r\nFFF\r\nGGG" - normalized = testdata.replace(b"\r\n", b"\n").replace(b"\r", b"\n") - for newline, expected in [ - (None, normalized.decode("ascii").splitlines(keepends=True)), - ("", testdata.decode("ascii").splitlines(keepends=True)), - ("\n", ["AAA\n", "BB\x00B\n", "CCC\rDDD\rEEE\r\n", "FFF\r\n", "GGG"]), - ("\r\n", ["AAA\nBB\x00B\nCCC\rDDD\rEEE\r\n", "FFF\r\n", "GGG"]), - ("\r", ["AAA\nBB\x00B\nCCC\r", "DDD\r", "EEE\r", "\nFFF\r", "\nGGG"]), - ]: - buf = self.BytesIO(testdata) - txt = self.TextIOWrapper(buf, encoding="ascii", newline=newline) - self.assertEqual(txt.readlines(), expected) - txt.seek(0) - self.assertEqual(txt.read(), "".join(expected)) - - def test_newlines_output(self): - testdict = { - "": b"AAA\nBBB\nCCC\nX\rY\r\nZ", - "\n": b"AAA\nBBB\nCCC\nX\rY\r\nZ", - "\r": b"AAA\rBBB\rCCC\rX\rY\r\rZ", - "\r\n": b"AAA\r\nBBB\r\nCCC\r\nX\rY\r\r\nZ", - } - tests = [(None, testdict[os.linesep])] + sorted(testdict.items()) - for newline, expected in tests: - buf = self.BytesIO() - txt = self.TextIOWrapper(buf, encoding="ascii", newline=newline) - txt.write("AAA\nB") - txt.write("BB\nCCC\n") - txt.write("X\rY\r\nZ") - txt.flush() - self.assertEqual(buf.closed, False) - self.assertEqual(buf.getvalue(), expected) - - def test_destructor(self): - l = [] - base = self.BytesIO - class MyBytesIO(base): - def close(self): - l.append(self.getvalue()) - base.close(self) - b = MyBytesIO() - t = self.TextIOWrapper(b, encoding="ascii") - t.write("abc") - del t - support.gc_collect() - self.assertEqual([b"abc"], l) - - def test_override_destructor(self): - record = [] - class MyTextIO(self.TextIOWrapper): - def __del__(self): - record.append(1) - try: - f = super().__del__ - except AttributeError: - pass - else: - f() - def close(self): - record.append(2) - super().close() - def flush(self): - record.append(3) - super().flush() - b = self.BytesIO() - t = MyTextIO(b, encoding="ascii") - del t - support.gc_collect() - self.assertEqual(record, [1, 2, 3]) - - def test_error_through_destructor(self): - # Test that the exception state is not modified by a destructor, - # even if close() fails. - rawio = self.CloseFailureIO() - with support.catch_unraisable_exception() as cm: - with self.assertRaises(AttributeError): - self.TextIOWrapper(rawio, encoding="utf-8").xyzzy - - self.assertEqual(cm.unraisable.exc_type, OSError) - - # Systematic tests of the text I/O API - - def test_basic_io(self): - for chunksize in (1, 2, 3, 4, 5, 15, 16, 17, 31, 32, 33, 63, 64, 65): - for enc in "ascii", "latin-1", "utf-8" :# , "utf-16-be", "utf-16-le": - f = self.open(os_helper.TESTFN, "w+", encoding=enc) - f._CHUNK_SIZE = chunksize - self.assertEqual(f.write("abc"), 3) - f.close() - f = self.open(os_helper.TESTFN, "r+", encoding=enc) - f._CHUNK_SIZE = chunksize - self.assertEqual(f.tell(), 0) - self.assertEqual(f.read(), "abc") - cookie = f.tell() - self.assertEqual(f.seek(0), 0) - self.assertEqual(f.read(None), "abc") - f.seek(0) - self.assertEqual(f.read(2), "ab") - self.assertEqual(f.read(1), "c") - self.assertEqual(f.read(1), "") - self.assertEqual(f.read(), "") - self.assertEqual(f.tell(), cookie) - self.assertEqual(f.seek(0), 0) - self.assertEqual(f.seek(0, 2), cookie) - self.assertEqual(f.write("def"), 3) - self.assertEqual(f.seek(cookie), cookie) - self.assertEqual(f.read(), "def") - if enc.startswith("utf"): - self.multi_line_test(f, enc) - f.close() - - def multi_line_test(self, f, enc): - f.seek(0) - f.truncate() - sample = "s\xff\u0fff\uffff" - wlines = [] - for size in (0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 62, 63, 64, 65, 1000): - chars = [] - for i in range(size): - chars.append(sample[i % len(sample)]) - line = "".join(chars) + "\n" - wlines.append((f.tell(), line)) - f.write(line) - f.seek(0) - rlines = [] - while True: - pos = f.tell() - line = f.readline() - if not line: - break - rlines.append((pos, line)) - self.assertEqual(rlines, wlines) - - def test_telling(self): - f = self.open(os_helper.TESTFN, "w+", encoding="utf-8") - p0 = f.tell() - f.write("\xff\n") - p1 = f.tell() - f.write("\xff\n") - p2 = f.tell() - f.seek(0) - self.assertEqual(f.tell(), p0) - self.assertEqual(f.readline(), "\xff\n") - self.assertEqual(f.tell(), p1) - self.assertEqual(f.readline(), "\xff\n") - self.assertEqual(f.tell(), p2) - f.seek(0) - for line in f: - self.assertEqual(line, "\xff\n") - self.assertRaises(OSError, f.tell) - self.assertEqual(f.tell(), p2) - f.close() - - def test_seeking(self): - chunk_size = _default_chunk_size() - prefix_size = chunk_size - 2 - u_prefix = "a" * prefix_size - prefix = bytes(u_prefix.encode("utf-8")) - self.assertEqual(len(u_prefix), len(prefix)) - u_suffix = "\u8888\n" - suffix = bytes(u_suffix.encode("utf-8")) - line = prefix + suffix - with self.open(os_helper.TESTFN, "wb") as f: - f.write(line*2) - with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: - s = f.read(prefix_size) - self.assertEqual(s, str(prefix, "ascii")) - self.assertEqual(f.tell(), prefix_size) - self.assertEqual(f.readline(), u_suffix) - - def test_seeking_too(self): - # Regression test for a specific bug - data = b'\xe0\xbf\xbf\n' - with self.open(os_helper.TESTFN, "wb") as f: - f.write(data) - with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: - f._CHUNK_SIZE # Just test that it exists - f._CHUNK_SIZE = 2 - f.readline() - f.tell() - - def test_seek_and_tell(self): - #Test seek/tell using the StatefulIncrementalDecoder. - # Make test faster by doing smaller seeks - CHUNK_SIZE = 128 - - def test_seek_and_tell_with_data(data, min_pos=0): - """Tell/seek to various points within a data stream and ensure - that the decoded data returned by read() is consistent.""" - f = self.open(os_helper.TESTFN, 'wb') - f.write(data) - f.close() - f = self.open(os_helper.TESTFN, encoding='test_decoder') - f._CHUNK_SIZE = CHUNK_SIZE - decoded = f.read() - f.close() - - for i in range(min_pos, len(decoded) + 1): # seek positions - for j in [1, 5, len(decoded) - i]: # read lengths - f = self.open(os_helper.TESTFN, encoding='test_decoder') - self.assertEqual(f.read(i), decoded[:i]) - cookie = f.tell() - self.assertEqual(f.read(j), decoded[i:i + j]) - f.seek(cookie) - self.assertEqual(f.read(), decoded[i:]) - f.close() - - # Enable the test decoder. - StatefulIncrementalDecoder.codecEnabled = 1 - - # Run the tests. - try: - # Try each test case. - for input, _, _ in StatefulIncrementalDecoderTest.test_cases: - test_seek_and_tell_with_data(input) - - # Position each test case so that it crosses a chunk boundary. - for input, _, _ in StatefulIncrementalDecoderTest.test_cases: - offset = CHUNK_SIZE - len(input)//2 - prefix = b'.'*offset - # Don't bother seeking into the prefix (takes too long). - min_pos = offset*2 - test_seek_and_tell_with_data(prefix + input, min_pos) - - # Ensure our test decoder won't interfere with subsequent tests. - finally: - StatefulIncrementalDecoder.codecEnabled = 0 - - def test_multibyte_seek_and_tell(self): - f = self.open(os_helper.TESTFN, "w", encoding="euc_jp") - f.write("AB\n\u3046\u3048\n") - f.close() - - f = self.open(os_helper.TESTFN, "r", encoding="euc_jp") - self.assertEqual(f.readline(), "AB\n") - p0 = f.tell() - self.assertEqual(f.readline(), "\u3046\u3048\n") - p1 = f.tell() - f.seek(p0) - self.assertEqual(f.readline(), "\u3046\u3048\n") - self.assertEqual(f.tell(), p1) - f.close() - - def test_seek_with_encoder_state(self): - f = self.open(os_helper.TESTFN, "w", encoding="euc_jis_2004") - f.write("\u00e6\u0300") - p0 = f.tell() - f.write("\u00e6") - f.seek(p0) - f.write("\u0300") - f.close() - - f = self.open(os_helper.TESTFN, "r", encoding="euc_jis_2004") - self.assertEqual(f.readline(), "\u00e6\u0300\u0300") - f.close() - - def test_encoded_writes(self): - data = "1234567890" - tests = ("utf-16", - "utf-16-le", - "utf-16-be", - "utf-32", - "utf-32-le", - "utf-32-be") - for encoding in tests: - buf = self.BytesIO() - f = self.TextIOWrapper(buf, encoding=encoding) - # Check if the BOM is written only once (see issue1753). - f.write(data) - f.write(data) - f.seek(0) - self.assertEqual(f.read(), data * 2) - f.seek(0) - self.assertEqual(f.read(), data * 2) - self.assertEqual(buf.getvalue(), (data * 2).encode(encoding)) - - def test_unreadable(self): - class UnReadable(self.BytesIO): - def readable(self): - return False - txt = self.TextIOWrapper(UnReadable(), encoding="utf-8") - self.assertRaises(OSError, txt.read) - - def test_read_one_by_one(self): - txt = self.TextIOWrapper(self.BytesIO(b"AA\r\nBB"), encoding="utf-8") - reads = "" - while True: - c = txt.read(1) - if not c: - break - reads += c - self.assertEqual(reads, "AA\nBB") - - def test_readlines(self): - txt = self.TextIOWrapper(self.BytesIO(b"AA\nBB\nCC"), encoding="utf-8") - self.assertEqual(txt.readlines(), ["AA\n", "BB\n", "CC"]) - txt.seek(0) - self.assertEqual(txt.readlines(None), ["AA\n", "BB\n", "CC"]) - txt.seek(0) - self.assertEqual(txt.readlines(5), ["AA\n", "BB\n"]) - - # read in amounts equal to TextIOWrapper._CHUNK_SIZE which is 128. - def test_read_by_chunk(self): - # make sure "\r\n" straddles 128 char boundary. - txt = self.TextIOWrapper(self.BytesIO(b"A" * 127 + b"\r\nB"), encoding="utf-8") - reads = "" - while True: - c = txt.read(128) - if not c: - break - reads += c - self.assertEqual(reads, "A"*127+"\nB") - - def test_writelines(self): - l = ['ab', 'cd', 'ef'] - buf = self.BytesIO() - txt = self.TextIOWrapper(buf, encoding="utf-8") - txt.writelines(l) - txt.flush() - self.assertEqual(buf.getvalue(), b'abcdef') - - def test_writelines_userlist(self): - l = UserList(['ab', 'cd', 'ef']) - buf = self.BytesIO() - txt = self.TextIOWrapper(buf, encoding="utf-8") - txt.writelines(l) - txt.flush() - self.assertEqual(buf.getvalue(), b'abcdef') - - def test_writelines_error(self): - txt = self.TextIOWrapper(self.BytesIO(), encoding="utf-8") - self.assertRaises(TypeError, txt.writelines, [1, 2, 3]) - self.assertRaises(TypeError, txt.writelines, None) - self.assertRaises(TypeError, txt.writelines, b'abc') - - def test_issue1395_1(self): - txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") - - # read one char at a time - reads = "" - while True: - c = txt.read(1) - if not c: - break - reads += c - self.assertEqual(reads, self.normalized) - - def test_issue1395_2(self): - txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") - txt._CHUNK_SIZE = 4 - - reads = "" - while True: - c = txt.read(4) - if not c: - break - reads += c - self.assertEqual(reads, self.normalized) - - def test_issue1395_3(self): - txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") - txt._CHUNK_SIZE = 4 - - reads = txt.read(4) - reads += txt.read(4) - reads += txt.readline() - reads += txt.readline() - reads += txt.readline() - self.assertEqual(reads, self.normalized) - - def test_issue1395_4(self): - txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") - txt._CHUNK_SIZE = 4 - - reads = txt.read(4) - reads += txt.read() - self.assertEqual(reads, self.normalized) - - def test_issue1395_5(self): - txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") - txt._CHUNK_SIZE = 4 - - reads = txt.read(4) - pos = txt.tell() - txt.seek(0) - txt.seek(pos) - self.assertEqual(txt.read(4), "BBB\n") - - def test_issue2282(self): - buffer = self.BytesIO(self.testdata) - txt = self.TextIOWrapper(buffer, encoding="ascii") - - self.assertEqual(buffer.seekable(), txt.seekable()) - - def test_append_bom(self): - # The BOM is not written again when appending to a non-empty file - filename = os_helper.TESTFN - for charset in ('utf-8-sig', 'utf-16', 'utf-32'): - with self.open(filename, 'w', encoding=charset) as f: - f.write('aaa') - pos = f.tell() - with self.open(filename, 'rb') as f: - self.assertEqual(f.read(), 'aaa'.encode(charset)) - - with self.open(filename, 'a', encoding=charset) as f: - f.write('xxx') - with self.open(filename, 'rb') as f: - self.assertEqual(f.read(), 'aaaxxx'.encode(charset)) - - def test_seek_bom(self): - # Same test, but when seeking manually - filename = os_helper.TESTFN - for charset in ('utf-8-sig', 'utf-16', 'utf-32'): - with self.open(filename, 'w', encoding=charset) as f: - f.write('aaa') - pos = f.tell() - with self.open(filename, 'r+', encoding=charset) as f: - f.seek(pos) - f.write('zzz') - f.seek(0) - f.write('bbb') - with self.open(filename, 'rb') as f: - self.assertEqual(f.read(), 'bbbzzz'.encode(charset)) - - def test_seek_append_bom(self): - # Same test, but first seek to the start and then to the end - filename = os_helper.TESTFN - for charset in ('utf-8-sig', 'utf-16', 'utf-32'): - with self.open(filename, 'w', encoding=charset) as f: - f.write('aaa') - with self.open(filename, 'a', encoding=charset) as f: - f.seek(0) - f.seek(0, self.SEEK_END) - f.write('xxx') - with self.open(filename, 'rb') as f: - self.assertEqual(f.read(), 'aaaxxx'.encode(charset)) - - def test_errors_property(self): - with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f: - self.assertEqual(f.errors, "strict") - with self.open(os_helper.TESTFN, "w", encoding="utf-8", errors="replace") as f: - self.assertEqual(f.errors, "replace") - - @support.no_tracing - @threading_helper.requires_working_threading() - def test_threads_write(self): - # Issue6750: concurrent writes could duplicate data - event = threading.Event() - with self.open(os_helper.TESTFN, "w", encoding="utf-8", buffering=1) as f: - def run(n): - text = "Thread%03d\n" % n - event.wait() - f.write(text) - threads = [threading.Thread(target=run, args=(x,)) - for x in range(20)] - with threading_helper.start_threads(threads, event.set): - time.sleep(0.02) - with self.open(os_helper.TESTFN, encoding="utf-8") as f: - content = f.read() - for n in range(20): - self.assertEqual(content.count("Thread%03d\n" % n), 1) - - def test_flush_error_on_close(self): - # Test that text file is closed despite failed flush - # and that flush() is called before file closed. - txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") - closed = [] - def bad_flush(): - closed[:] = [txt.closed, txt.buffer.closed] - raise OSError() - txt.flush = bad_flush - self.assertRaises(OSError, txt.close) # exception not swallowed - self.assertTrue(txt.closed) - self.assertTrue(txt.buffer.closed) - self.assertTrue(closed) # flush() called - self.assertFalse(closed[0]) # flush() called before file closed - self.assertFalse(closed[1]) - txt.flush = lambda: None # break reference loop - - def test_close_error_on_close(self): - buffer = self.BytesIO(self.testdata) - def bad_flush(): - raise OSError('flush') - def bad_close(): - raise OSError('close') - buffer.close = bad_close - txt = self.TextIOWrapper(buffer, encoding="ascii") - txt.flush = bad_flush - with self.assertRaises(OSError) as err: # exception not swallowed - txt.close() - self.assertEqual(err.exception.args, ('close',)) - self.assertIsInstance(err.exception.__context__, OSError) - self.assertEqual(err.exception.__context__.args, ('flush',)) - self.assertFalse(txt.closed) - - # Silence destructor error - buffer.close = lambda: None - txt.flush = lambda: None - - def test_nonnormalized_close_error_on_close(self): - # Issue #21677 - buffer = self.BytesIO(self.testdata) - def bad_flush(): - raise non_existing_flush - def bad_close(): - raise non_existing_close - buffer.close = bad_close - txt = self.TextIOWrapper(buffer, encoding="ascii") - txt.flush = bad_flush - with self.assertRaises(NameError) as err: # exception not swallowed - txt.close() - self.assertIn('non_existing_close', str(err.exception)) - self.assertIsInstance(err.exception.__context__, NameError) - self.assertIn('non_existing_flush', str(err.exception.__context__)) - self.assertFalse(txt.closed) - - # Silence destructor error - buffer.close = lambda: None - txt.flush = lambda: None - - def test_multi_close(self): - txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") - txt.close() - txt.close() - txt.close() - self.assertRaises(ValueError, txt.flush) - - def test_unseekable(self): - txt = self.TextIOWrapper(self.MockUnseekableIO(self.testdata), encoding="utf-8") - self.assertRaises(self.UnsupportedOperation, txt.tell) - self.assertRaises(self.UnsupportedOperation, txt.seek, 0) - - def test_readonly_attributes(self): - txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") - buf = self.BytesIO(self.testdata) - with self.assertRaises(AttributeError): - txt.buffer = buf - - def test_rawio(self): - # Issue #12591: TextIOWrapper must work with raw I/O objects, so - # that subprocess.Popen() can have the required unbuffered - # semantics with universal_newlines=True. - raw = self.MockRawIO([b'abc', b'def', b'ghi\njkl\nopq\n']) - txt = self.TextIOWrapper(raw, encoding='ascii', newline='\n') - # Reads - self.assertEqual(txt.read(4), 'abcd') - self.assertEqual(txt.readline(), 'efghi\n') - self.assertEqual(list(txt), ['jkl\n', 'opq\n']) - - def test_rawio_write_through(self): - # Issue #12591: with write_through=True, writes don't need a flush - raw = self.MockRawIO([b'abc', b'def', b'ghi\njkl\nopq\n']) - txt = self.TextIOWrapper(raw, encoding='ascii', newline='\n', - write_through=True) - txt.write('1') - txt.write('23\n4') - txt.write('5') - self.assertEqual(b''.join(raw._write_stack), b'123\n45') - - def test_bufio_write_through(self): - # Issue #21396: write_through=True doesn't force a flush() - # on the underlying binary buffered object. - flush_called, write_called = [], [] - class BufferedWriter(self.BufferedWriter): - def flush(self, *args, **kwargs): - flush_called.append(True) - return super().flush(*args, **kwargs) - def write(self, *args, **kwargs): - write_called.append(True) - return super().write(*args, **kwargs) - - rawio = self.BytesIO() - data = b"a" - bufio = BufferedWriter(rawio, len(data)*2) - textio = self.TextIOWrapper(bufio, encoding='ascii', - write_through=True) - # write to the buffered io but don't overflow the buffer - text = data.decode('ascii') - textio.write(text) - - # buffer.flush is not called with write_through=True - self.assertFalse(flush_called) - # buffer.write *is* called with write_through=True - self.assertTrue(write_called) - self.assertEqual(rawio.getvalue(), b"") # no flush - - write_called = [] # reset - textio.write(text * 10) # total content is larger than bufio buffer - self.assertTrue(write_called) - self.assertEqual(rawio.getvalue(), data * 11) # all flushed - - def test_reconfigure_write_through(self): - raw = self.MockRawIO([]) - t = self.TextIOWrapper(raw, encoding='ascii', newline='\n') - t.write('1') - t.reconfigure(write_through=True) # implied flush - self.assertEqual(t.write_through, True) - self.assertEqual(b''.join(raw._write_stack), b'1') - t.write('23') - self.assertEqual(b''.join(raw._write_stack), b'123') - t.reconfigure(write_through=False) - self.assertEqual(t.write_through, False) - t.write('45') - t.flush() - self.assertEqual(b''.join(raw._write_stack), b'12345') - # Keeping default value - t.reconfigure() - t.reconfigure(write_through=None) - self.assertEqual(t.write_through, False) - t.reconfigure(write_through=True) - t.reconfigure() - t.reconfigure(write_through=None) - self.assertEqual(t.write_through, True) - - def test_read_nonbytes(self): - # Issue #17106 - # Crash when underlying read() returns non-bytes - t = self.TextIOWrapper(self.StringIO('a'), encoding="utf-8") - self.assertRaises(TypeError, t.read, 1) - t = self.TextIOWrapper(self.StringIO('a'), encoding="utf-8") - self.assertRaises(TypeError, t.readline) - t = self.TextIOWrapper(self.StringIO('a'), encoding="utf-8") - self.assertRaises(TypeError, t.read) - - def test_illegal_encoder(self): - # Issue 31271: Calling write() while the return value of encoder's - # encode() is invalid shouldn't cause an assertion failure. - rot13 = codecs.lookup("rot13") - with support.swap_attr(rot13, '_is_text_encoding', True): - t = self.TextIOWrapper(self.BytesIO(b'foo'), encoding="rot13") - self.assertRaises(TypeError, t.write, 'bar') - - def test_illegal_decoder(self): - # Issue #17106 - # Bypass the early encoding check added in issue 20404 - def _make_illegal_wrapper(): - quopri = codecs.lookup("quopri") - quopri._is_text_encoding = True - try: - t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), - newline='\n', encoding="quopri") - finally: - quopri._is_text_encoding = False - return t - # Crash when decoder returns non-string - t = _make_illegal_wrapper() - self.assertRaises(TypeError, t.read, 1) - t = _make_illegal_wrapper() - self.assertRaises(TypeError, t.readline) - t = _make_illegal_wrapper() - self.assertRaises(TypeError, t.read) - - # Issue 31243: calling read() while the return value of decoder's - # getstate() is invalid should neither crash the interpreter nor - # raise a SystemError. - def _make_very_illegal_wrapper(getstate_ret_val): - class BadDecoder: - def getstate(self): - return getstate_ret_val - def _get_bad_decoder(dummy): - return BadDecoder() - quopri = codecs.lookup("quopri") - with support.swap_attr(quopri, 'incrementaldecoder', - _get_bad_decoder): - return _make_illegal_wrapper() - t = _make_very_illegal_wrapper(42) - self.assertRaises(TypeError, t.read, 42) - t = _make_very_illegal_wrapper(()) - self.assertRaises(TypeError, t.read, 42) - t = _make_very_illegal_wrapper((1, 2)) - self.assertRaises(TypeError, t.read, 42) - - def _check_create_at_shutdown(self, **kwargs): - # Issue #20037: creating a TextIOWrapper at shutdown - # shouldn't crash the interpreter. - iomod = self.io.__name__ - code = """if 1: - import codecs - import {iomod} as io - - # Avoid looking up codecs at shutdown - codecs.lookup('utf-8') - - class C: - def __del__(self): - io.TextIOWrapper(io.BytesIO(), **{kwargs}) - print("ok") - c = C() - """.format(iomod=iomod, kwargs=kwargs) - return assert_python_ok("-c", code) - - def test_create_at_shutdown_without_encoding(self): - rc, out, err = self._check_create_at_shutdown() - if err: - # Can error out with a RuntimeError if the module state - # isn't found. - self.assertIn(self.shutdown_error, err.decode()) - else: - self.assertEqual("ok", out.decode().strip()) - - def test_create_at_shutdown_with_encoding(self): - rc, out, err = self._check_create_at_shutdown(encoding='utf-8', - errors='strict') - self.assertFalse(err) - self.assertEqual("ok", out.decode().strip()) - - def test_read_byteslike(self): - r = MemviewBytesIO(b'Just some random string\n') - t = self.TextIOWrapper(r, 'utf-8') - - # TextIOwrapper will not read the full string, because - # we truncate it to a multiple of the native int size - # so that we can construct a more complex memoryview. - bytes_val = _to_memoryview(r.getvalue()).tobytes() - - self.assertEqual(t.read(200), bytes_val.decode('utf-8')) - - def test_issue22849(self): - class F(object): - def readable(self): return True - def writable(self): return True - def seekable(self): return True - - for i in range(10): - try: - self.TextIOWrapper(F(), encoding='utf-8') - except Exception: - pass - - F.tell = lambda x: 0 - t = self.TextIOWrapper(F(), encoding='utf-8') - - def test_reconfigure_locale(self): - wrapper = self.TextIOWrapper(self.BytesIO(b"test")) - wrapper.reconfigure(encoding="locale") - - def test_reconfigure_encoding_read(self): - # latin1 -> utf8 - # (latin1 can decode utf-8 encoded string) - data = 'abc\xe9\n'.encode('latin1') + 'd\xe9f\n'.encode('utf8') - raw = self.BytesIO(data) - txt = self.TextIOWrapper(raw, encoding='latin1', newline='\n') - self.assertEqual(txt.readline(), 'abc\xe9\n') - with self.assertRaises(self.UnsupportedOperation): - txt.reconfigure(encoding='utf-8') - with self.assertRaises(self.UnsupportedOperation): - txt.reconfigure(newline=None) - - def test_reconfigure_write_fromascii(self): - # ascii has a specific encodefunc in the C implementation, - # but utf-8-sig has not. Make sure that we get rid of the - # cached encodefunc when we switch encoders. - raw = self.BytesIO() - txt = self.TextIOWrapper(raw, encoding='ascii', newline='\n') - txt.write('foo\n') - txt.reconfigure(encoding='utf-8-sig') - txt.write('\xe9\n') - txt.flush() - self.assertEqual(raw.getvalue(), b'foo\n\xc3\xa9\n') - - def test_reconfigure_write(self): - # latin -> utf8 - raw = self.BytesIO() - txt = self.TextIOWrapper(raw, encoding='latin1', newline='\n') - txt.write('abc\xe9\n') - txt.reconfigure(encoding='utf-8') - self.assertEqual(raw.getvalue(), b'abc\xe9\n') - txt.write('d\xe9f\n') - txt.flush() - self.assertEqual(raw.getvalue(), b'abc\xe9\nd\xc3\xa9f\n') - - # ascii -> utf-8-sig: ensure that no BOM is written in the middle of - # the file - raw = self.BytesIO() - txt = self.TextIOWrapper(raw, encoding='ascii', newline='\n') - txt.write('abc\n') - txt.reconfigure(encoding='utf-8-sig') - txt.write('d\xe9f\n') - txt.flush() - self.assertEqual(raw.getvalue(), b'abc\nd\xc3\xa9f\n') - - def test_reconfigure_write_non_seekable(self): - raw = self.BytesIO() - raw.seekable = lambda: False - raw.seek = None - txt = self.TextIOWrapper(raw, encoding='ascii', newline='\n') - txt.write('abc\n') - txt.reconfigure(encoding='utf-8-sig') - txt.write('d\xe9f\n') - txt.flush() - - # If the raw stream is not seekable, there'll be a BOM - self.assertEqual(raw.getvalue(), b'abc\n\xef\xbb\xbfd\xc3\xa9f\n') - - def test_reconfigure_defaults(self): - txt = self.TextIOWrapper(self.BytesIO(), 'ascii', 'replace', '\n') - txt.reconfigure(encoding=None) - self.assertEqual(txt.encoding, 'ascii') - self.assertEqual(txt.errors, 'replace') - txt.write('LF\n') - - txt.reconfigure(newline='\r\n') - self.assertEqual(txt.encoding, 'ascii') - self.assertEqual(txt.errors, 'replace') - - txt.reconfigure(errors='ignore') - self.assertEqual(txt.encoding, 'ascii') - self.assertEqual(txt.errors, 'ignore') - txt.write('CRLF\n') - - txt.reconfigure(encoding='utf-8', newline=None) - self.assertEqual(txt.errors, 'strict') - txt.seek(0) - self.assertEqual(txt.read(), 'LF\nCRLF\n') - - self.assertEqual(txt.detach().getvalue(), b'LF\nCRLF\r\n') - - def test_reconfigure_errors(self): - txt = self.TextIOWrapper(self.BytesIO(), 'ascii', 'replace', '\r') - with self.assertRaises(TypeError): # there was a crash - txt.reconfigure(encoding=42) - if self.is_C: - with self.assertRaises(UnicodeEncodeError): - txt.reconfigure(encoding='\udcfe') - with self.assertRaises(LookupError): - txt.reconfigure(encoding='locale\0') - # TODO: txt.reconfigure(encoding='utf-8\0') - # TODO: txt.reconfigure(encoding='nonexisting') - with self.assertRaises(TypeError): - txt.reconfigure(errors=42) - if self.is_C: - with self.assertRaises(UnicodeEncodeError): - txt.reconfigure(errors='\udcfe') - # TODO: txt.reconfigure(errors='ignore\0') - # TODO: txt.reconfigure(errors='nonexisting') - with self.assertRaises(TypeError): - txt.reconfigure(newline=42) - with self.assertRaises(ValueError): - txt.reconfigure(newline='\udcfe') - with self.assertRaises(ValueError): - txt.reconfigure(newline='xyz') - if not self.is_C: - # TODO: Should fail in C too. - with self.assertRaises(ValueError): - txt.reconfigure(newline='\n\0') - if self.is_C: - # TODO: Use __bool__(), not __index__(). - with self.assertRaises(ZeroDivisionError): - txt.reconfigure(line_buffering=BadIndex()) - with self.assertRaises(OverflowError): - txt.reconfigure(line_buffering=2**1000) - with self.assertRaises(ZeroDivisionError): - txt.reconfigure(write_through=BadIndex()) - with self.assertRaises(OverflowError): - txt.reconfigure(write_through=2**1000) - with self.assertRaises(ZeroDivisionError): # there was a crash - txt.reconfigure(line_buffering=BadIndex(), - write_through=BadIndex()) - self.assertEqual(txt.encoding, 'ascii') - self.assertEqual(txt.errors, 'replace') - self.assertIs(txt.line_buffering, False) - self.assertIs(txt.write_through, False) - - txt.reconfigure(encoding='latin1', errors='ignore', newline='\r\n', - line_buffering=True, write_through=True) - self.assertEqual(txt.encoding, 'latin1') - self.assertEqual(txt.errors, 'ignore') - self.assertIs(txt.line_buffering, True) - self.assertIs(txt.write_through, True) - - def test_reconfigure_newline(self): - raw = self.BytesIO(b'CR\rEOF') - txt = self.TextIOWrapper(raw, 'ascii', newline='\n') - txt.reconfigure(newline=None) - self.assertEqual(txt.readline(), 'CR\n') - raw = self.BytesIO(b'CR\rEOF') - txt = self.TextIOWrapper(raw, 'ascii', newline='\n') - txt.reconfigure(newline='') - self.assertEqual(txt.readline(), 'CR\r') - raw = self.BytesIO(b'CR\rLF\nEOF') - txt = self.TextIOWrapper(raw, 'ascii', newline='\r') - txt.reconfigure(newline='\n') - self.assertEqual(txt.readline(), 'CR\rLF\n') - raw = self.BytesIO(b'LF\nCR\rEOF') - txt = self.TextIOWrapper(raw, 'ascii', newline='\n') - txt.reconfigure(newline='\r') - self.assertEqual(txt.readline(), 'LF\nCR\r') - raw = self.BytesIO(b'CR\rCRLF\r\nEOF') - txt = self.TextIOWrapper(raw, 'ascii', newline='\r') - txt.reconfigure(newline='\r\n') - self.assertEqual(txt.readline(), 'CR\rCRLF\r\n') - - txt = self.TextIOWrapper(self.BytesIO(), 'ascii', newline='\r') - txt.reconfigure(newline=None) - txt.write('linesep\n') - txt.reconfigure(newline='') - txt.write('LF\n') - txt.reconfigure(newline='\n') - txt.write('LF\n') - txt.reconfigure(newline='\r') - txt.write('CR\n') - txt.reconfigure(newline='\r\n') - txt.write('CRLF\n') - expected = 'linesep' + os.linesep + 'LF\nLF\nCR\rCRLF\r\n' - self.assertEqual(txt.detach().getvalue().decode('ascii'), expected) - - def test_issue25862(self): - # Assertion failures occurred in tell() after read() and write(). - t = self.TextIOWrapper(self.BytesIO(b'test'), encoding='ascii') - t.read(1) - t.read() - t.tell() - t = self.TextIOWrapper(self.BytesIO(b'test'), encoding='ascii') - t.read(1) - t.write('x') - t.tell() - - def test_issue35928(self): - p = self.BufferedRWPair(self.BytesIO(b'foo\nbar\n'), self.BytesIO()) - f = self.TextIOWrapper(p) - res = f.readline() - self.assertEqual(res, 'foo\n') - f.write(res) - self.assertEqual(res + f.readline(), 'foo\nbar\n') - - def test_pickling_subclass(self): - global MyTextIO - class MyTextIO(self.TextIOWrapper): - def __init__(self, raw, tag): - super().__init__(raw) - self.tag = tag - def __getstate__(self): - return self.tag, self.buffer.getvalue() - def __setstate__(slf, state): - tag, value = state - slf.__init__(self.BytesIO(value), tag) - - raw = self.BytesIO(b'data') - txt = MyTextIO(raw, 'ham') - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - with self.subTest(protocol=proto): - pickled = pickle.dumps(txt, proto) - newtxt = pickle.loads(pickled) - self.assertEqual(newtxt.buffer.getvalue(), b'data') - self.assertEqual(newtxt.tag, 'ham') - del MyTextIO - - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_read_non_blocking(self): - import os - r, w = os.pipe() - try: - os.set_blocking(r, False) - with self.io.open(r, 'rt') as textfile: - r = None - # Nothing has been written so a non-blocking read raises a BlockingIOError exception. - with self.assertRaises(BlockingIOError): - textfile.read() - finally: - if r is not None: - os.close(r) - os.close(w) - - -class MemviewBytesIO(io.BytesIO): - '''A BytesIO object whose read method returns memoryviews - rather than bytes''' - - def read1(self, len_): - return _to_memoryview(super().read1(len_)) - - def read(self, len_): - return _to_memoryview(super().read(len_)) - -def _to_memoryview(buf): - '''Convert bytes-object *buf* to a non-trivial memoryview''' - - arr = array.array('i') - idx = len(buf) - len(buf) % arr.itemsize - arr.frombytes(buf[:idx]) - return memoryview(arr) - - -class CTextIOWrapperTest(TextIOWrapperTest): - io = io - shutdown_error = "LookupError: unknown encoding: ascii" - - def test_initialization(self): - r = self.BytesIO(b"\xc3\xa9\n\n") - b = self.BufferedReader(r, 1000) - t = self.TextIOWrapper(b, encoding="utf-8") - self.assertRaises(ValueError, t.__init__, b, encoding="utf-8", newline='xyzzy') - self.assertRaises(ValueError, t.read) - - t = self.TextIOWrapper.__new__(self.TextIOWrapper) - self.assertRaises(Exception, repr, t) - - def test_garbage_collection(self): - # C TextIOWrapper objects are collected, and collecting them flushes - # all data to disk. - # The Python version has __del__, so it ends in gc.garbage instead. - with warnings_helper.check_warnings(('', ResourceWarning)): - rawio = self.FileIO(os_helper.TESTFN, "wb") - b = self.BufferedWriter(rawio) - t = self.TextIOWrapper(b, encoding="ascii") - t.write("456def") - t.x = t - wr = weakref.ref(t) - del t - support.gc_collect() - self.assertIsNone(wr(), wr) - with self.open(os_helper.TESTFN, "rb") as f: - self.assertEqual(f.read(), b"456def") - - def test_rwpair_cleared_before_textio(self): - # Issue 13070: TextIOWrapper's finalization would crash when called - # after the reference to the underlying BufferedRWPair's writer got - # cleared by the GC. - for i in range(1000): - b1 = self.BufferedRWPair(self.MockRawIO(), self.MockRawIO()) - t1 = self.TextIOWrapper(b1, encoding="ascii") - b2 = self.BufferedRWPair(self.MockRawIO(), self.MockRawIO()) - t2 = self.TextIOWrapper(b2, encoding="ascii") - # circular references - t1.buddy = t2 - t2.buddy = t1 - support.gc_collect() - - def test_del__CHUNK_SIZE_SystemError(self): - t = self.TextIOWrapper(self.BytesIO(), encoding='ascii') - with self.assertRaises(AttributeError): - del t._CHUNK_SIZE - - def test_internal_buffer_size(self): - # bpo-43260: TextIOWrapper's internal buffer should not store - # data larger than chunk size. - chunk_size = 8192 # default chunk size, updated later - - class MockIO(self.MockRawIO): - def write(self, data): - if len(data) > chunk_size: - raise RuntimeError - return super().write(data) - - buf = MockIO() - t = self.TextIOWrapper(buf, encoding="ascii") - chunk_size = t._CHUNK_SIZE - t.write("abc") - t.write("def") - # default chunk size is 8192 bytes so t don't write data to buf. - self.assertEqual([], buf._write_stack) - - with self.assertRaises(RuntimeError): - t.write("x"*(chunk_size+1)) - - self.assertEqual([b"abcdef"], buf._write_stack) - t.write("ghi") - t.write("x"*chunk_size) - self.assertEqual([b"abcdef", b"ghi", b"x"*chunk_size], buf._write_stack) - - def test_issue119506(self): - chunk_size = 8192 - - class MockIO(self.MockRawIO): - written = False - def write(self, data): - if not self.written: - self.written = True - t.write("middle") - return super().write(data) - - buf = MockIO() - t = self.TextIOWrapper(buf) - t.write("abc") - t.write("def") - # writing data which size >= chunk_size cause flushing buffer before write. - t.write("g" * chunk_size) - t.flush() - - self.assertEqual([b"abcdef", b"middle", b"g"*chunk_size], - buf._write_stack) - - -class PyTextIOWrapperTest(TextIOWrapperTest): - io = pyio - shutdown_error = "LookupError: unknown encoding: ascii" - - -class IncrementalNewlineDecoderTest(unittest.TestCase): - - def check_newline_decoding_utf8(self, decoder): - # UTF-8 specific tests for a newline decoder - def _check_decode(b, s, **kwargs): - # We exercise getstate() / setstate() as well as decode() - state = decoder.getstate() - self.assertEqual(decoder.decode(b, **kwargs), s) - decoder.setstate(state) - self.assertEqual(decoder.decode(b, **kwargs), s) - - _check_decode(b'\xe8\xa2\x88', "\u8888") - - _check_decode(b'\xe8', "") - _check_decode(b'\xa2', "") - _check_decode(b'\x88', "\u8888") - - _check_decode(b'\xe8', "") - _check_decode(b'\xa2', "") - _check_decode(b'\x88', "\u8888") - - _check_decode(b'\xe8', "") - self.assertRaises(UnicodeDecodeError, decoder.decode, b'', final=True) - - decoder.reset() - _check_decode(b'\n', "\n") - _check_decode(b'\r', "") - _check_decode(b'', "\n", final=True) - _check_decode(b'\r', "\n", final=True) - - _check_decode(b'\r', "") - _check_decode(b'a', "\na") - - _check_decode(b'\r\r\n', "\n\n") - _check_decode(b'\r', "") - _check_decode(b'\r', "\n") - _check_decode(b'\na', "\na") - - _check_decode(b'\xe8\xa2\x88\r\n', "\u8888\n") - _check_decode(b'\xe8\xa2\x88', "\u8888") - _check_decode(b'\n', "\n") - _check_decode(b'\xe8\xa2\x88\r', "\u8888") - _check_decode(b'\n', "\n") - - def check_newline_decoding(self, decoder, encoding): - result = [] - if encoding is not None: - encoder = codecs.getincrementalencoder(encoding)() - def _decode_bytewise(s): - # Decode one byte at a time - for b in encoder.encode(s): - result.append(decoder.decode(bytes([b]))) - else: - encoder = None - def _decode_bytewise(s): - # Decode one char at a time - for c in s: - result.append(decoder.decode(c)) - self.assertEqual(decoder.newlines, None) - _decode_bytewise("abc\n\r") - self.assertEqual(decoder.newlines, '\n') - _decode_bytewise("\nabc") - self.assertEqual(decoder.newlines, ('\n', '\r\n')) - _decode_bytewise("abc\r") - self.assertEqual(decoder.newlines, ('\n', '\r\n')) - _decode_bytewise("abc") - self.assertEqual(decoder.newlines, ('\r', '\n', '\r\n')) - _decode_bytewise("abc\r") - self.assertEqual("".join(result), "abc\n\nabcabc\nabcabc") - decoder.reset() - input = "abc" - if encoder is not None: - encoder.reset() - input = encoder.encode(input) - self.assertEqual(decoder.decode(input), "abc") - self.assertEqual(decoder.newlines, None) - - def test_newline_decoder(self): - encodings = ( - # None meaning the IncrementalNewlineDecoder takes unicode input - # rather than bytes input - None, 'utf-8', 'latin-1', - 'utf-16', 'utf-16-le', 'utf-16-be', - 'utf-32', 'utf-32-le', 'utf-32-be', - ) - for enc in encodings: - decoder = enc and codecs.getincrementaldecoder(enc)() - decoder = self.IncrementalNewlineDecoder(decoder, translate=True) - self.check_newline_decoding(decoder, enc) - decoder = codecs.getincrementaldecoder("utf-8")() - decoder = self.IncrementalNewlineDecoder(decoder, translate=True) - self.check_newline_decoding_utf8(decoder) - self.assertRaises(TypeError, decoder.setstate, 42) - - def test_newline_bytes(self): - # Issue 5433: Excessive optimization in IncrementalNewlineDecoder - def _check(dec): - self.assertEqual(dec.newlines, None) - self.assertEqual(dec.decode("\u0D00"), "\u0D00") - self.assertEqual(dec.newlines, None) - self.assertEqual(dec.decode("\u0A00"), "\u0A00") - self.assertEqual(dec.newlines, None) - dec = self.IncrementalNewlineDecoder(None, translate=False) - _check(dec) - dec = self.IncrementalNewlineDecoder(None, translate=True) - _check(dec) - - def test_translate(self): - # issue 35062 - for translate in (-2, -1, 1, 2): - decoder = codecs.getincrementaldecoder("utf-8")() - decoder = self.IncrementalNewlineDecoder(decoder, translate) - self.check_newline_decoding_utf8(decoder) - decoder = codecs.getincrementaldecoder("utf-8")() - decoder = self.IncrementalNewlineDecoder(decoder, translate=0) - self.assertEqual(decoder.decode(b"\r\r\n"), "\r\r\n") - -class CIncrementalNewlineDecoderTest(IncrementalNewlineDecoderTest): - @support.cpython_only - def test_uninitialized(self): - uninitialized = self.IncrementalNewlineDecoder.__new__( - self.IncrementalNewlineDecoder) - self.assertRaises(ValueError, uninitialized.decode, b'bar') - self.assertRaises(ValueError, uninitialized.getstate) - self.assertRaises(ValueError, uninitialized.setstate, (b'foo', 0)) - self.assertRaises(ValueError, uninitialized.reset) - - -class PyIncrementalNewlineDecoderTest(IncrementalNewlineDecoderTest): - pass - - -# XXX Tests for open() - -class MiscIOTest(unittest.TestCase): - - # for test__all__, actual values are set in subclasses - name_of_module = None - extra_exported = () - not_exported = () - - def tearDown(self): - os_helper.unlink(os_helper.TESTFN) - - def test___all__(self): - support.check__all__(self, self.io, self.name_of_module, - extra=self.extra_exported, - not_exported=self.not_exported) - - def test_attributes(self): - f = self.open(os_helper.TESTFN, "wb", buffering=0) - self.assertEqual(f.mode, "wb") - f.close() - - f = self.open(os_helper.TESTFN, "w+", encoding="utf-8") - self.assertEqual(f.mode, "w+") - self.assertEqual(f.buffer.mode, "rb+") # Does it really matter? - self.assertEqual(f.buffer.raw.mode, "rb+") - - g = self.open(f.fileno(), "wb", closefd=False) - self.assertEqual(g.mode, "wb") - self.assertEqual(g.raw.mode, "wb") - self.assertEqual(g.name, f.fileno()) - self.assertEqual(g.raw.name, f.fileno()) - f.close() - g.close() - - def test_removed_u_mode(self): - # bpo-37330: The "U" mode has been removed in Python 3.11 - for mode in ("U", "rU", "r+U"): - with self.assertRaises(ValueError) as cm: - self.open(os_helper.TESTFN, mode) - self.assertIn('invalid mode', str(cm.exception)) - - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_open_pipe_with_append(self): - # bpo-27805: Ignore ESPIPE from lseek() in open(). - r, w = os.pipe() - self.addCleanup(os.close, r) - f = self.open(w, 'a', encoding="utf-8") - self.addCleanup(f.close) - # Check that the file is marked non-seekable. On Windows, however, lseek - # somehow succeeds on pipes. - if sys.platform != 'win32': - self.assertFalse(f.seekable()) - - def test_io_after_close(self): - for kwargs in [ - {"mode": "w"}, - {"mode": "wb"}, - {"mode": "w", "buffering": 1}, - {"mode": "w", "buffering": 2}, - {"mode": "wb", "buffering": 0}, - {"mode": "r"}, - {"mode": "rb"}, - {"mode": "r", "buffering": 1}, - {"mode": "r", "buffering": 2}, - {"mode": "rb", "buffering": 0}, - {"mode": "w+"}, - {"mode": "w+b"}, - {"mode": "w+", "buffering": 1}, - {"mode": "w+", "buffering": 2}, - {"mode": "w+b", "buffering": 0}, - ]: - if "b" not in kwargs["mode"]: - kwargs["encoding"] = "utf-8" - f = self.open(os_helper.TESTFN, **kwargs) - f.close() - self.assertRaises(ValueError, f.flush) - self.assertRaises(ValueError, f.fileno) - self.assertRaises(ValueError, f.isatty) - self.assertRaises(ValueError, f.__iter__) - if hasattr(f, "peek"): - self.assertRaises(ValueError, f.peek, 1) - self.assertRaises(ValueError, f.read) - if hasattr(f, "read1"): - self.assertRaises(ValueError, f.read1, 1024) - self.assertRaises(ValueError, f.read1) - if hasattr(f, "readall"): - self.assertRaises(ValueError, f.readall) - if hasattr(f, "readinto"): - self.assertRaises(ValueError, f.readinto, bytearray(1024)) - if hasattr(f, "readinto1"): - self.assertRaises(ValueError, f.readinto1, bytearray(1024)) - self.assertRaises(ValueError, f.readline) - self.assertRaises(ValueError, f.readlines) - self.assertRaises(ValueError, f.readlines, 1) - self.assertRaises(ValueError, f.seek, 0) - self.assertRaises(ValueError, f.tell) - self.assertRaises(ValueError, f.truncate) - self.assertRaises(ValueError, f.write, - b"" if "b" in kwargs['mode'] else "") - self.assertRaises(ValueError, f.writelines, []) - self.assertRaises(ValueError, next, f) - - def test_blockingioerror(self): - # Various BlockingIOError issues - class C(str): - pass - c = C("") - b = self.BlockingIOError(1, c) - c.b = b - b.c = c - wr = weakref.ref(c) - del c, b - support.gc_collect() - self.assertIsNone(wr(), wr) - - def test_abcs(self): - # Test the visible base classes are ABCs. - self.assertIsInstance(self.IOBase, abc.ABCMeta) - self.assertIsInstance(self.RawIOBase, abc.ABCMeta) - self.assertIsInstance(self.BufferedIOBase, abc.ABCMeta) - self.assertIsInstance(self.TextIOBase, abc.ABCMeta) - - def _check_abc_inheritance(self, abcmodule): - with self.open(os_helper.TESTFN, "wb", buffering=0) as f: - self.assertIsInstance(f, abcmodule.IOBase) - self.assertIsInstance(f, abcmodule.RawIOBase) - self.assertNotIsInstance(f, abcmodule.BufferedIOBase) - self.assertNotIsInstance(f, abcmodule.TextIOBase) - with self.open(os_helper.TESTFN, "wb") as f: - self.assertIsInstance(f, abcmodule.IOBase) - self.assertNotIsInstance(f, abcmodule.RawIOBase) - self.assertIsInstance(f, abcmodule.BufferedIOBase) - self.assertNotIsInstance(f, abcmodule.TextIOBase) - with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f: - self.assertIsInstance(f, abcmodule.IOBase) - self.assertNotIsInstance(f, abcmodule.RawIOBase) - self.assertNotIsInstance(f, abcmodule.BufferedIOBase) - self.assertIsInstance(f, abcmodule.TextIOBase) - - def test_abc_inheritance(self): - # Test implementations inherit from their respective ABCs - self._check_abc_inheritance(self) - - def test_abc_inheritance_official(self): - # Test implementations inherit from the official ABCs of the - # baseline "io" module. - self._check_abc_inheritance(io) - - def _check_warn_on_dealloc(self, *args, **kwargs): - f = self.open(*args, **kwargs) - r = repr(f) - with self.assertWarns(ResourceWarning) as cm: - f = None - support.gc_collect() - self.assertIn(r, str(cm.warning.args[0])) - - def test_warn_on_dealloc(self): - self._check_warn_on_dealloc(os_helper.TESTFN, "wb", buffering=0) - self._check_warn_on_dealloc(os_helper.TESTFN, "wb") - self._check_warn_on_dealloc(os_helper.TESTFN, "w", encoding="utf-8") - - def _check_warn_on_dealloc_fd(self, *args, **kwargs): - fds = [] - def cleanup_fds(): - for fd in fds: - try: - os.close(fd) - except OSError as e: - if e.errno != errno.EBADF: - raise - self.addCleanup(cleanup_fds) - r, w = os.pipe() - fds += r, w - self._check_warn_on_dealloc(r, *args, **kwargs) - # When using closefd=False, there's no warning - r, w = os.pipe() - fds += r, w - with warnings_helper.check_no_resource_warning(self): - self.open(r, *args, closefd=False, **kwargs) - - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_warn_on_dealloc_fd(self): - self._check_warn_on_dealloc_fd("rb", buffering=0) - self._check_warn_on_dealloc_fd("rb") - self._check_warn_on_dealloc_fd("r", encoding="utf-8") - - - def test_pickling(self): - # Pickling file objects is forbidden - msg = "cannot pickle" - for kwargs in [ - {"mode": "w"}, - {"mode": "wb"}, - {"mode": "wb", "buffering": 0}, - {"mode": "r"}, - {"mode": "rb"}, - {"mode": "rb", "buffering": 0}, - {"mode": "w+"}, - {"mode": "w+b"}, - {"mode": "w+b", "buffering": 0}, - ]: - if "b" not in kwargs["mode"]: - kwargs["encoding"] = "utf-8" - for protocol in range(pickle.HIGHEST_PROTOCOL + 1): - with self.subTest(protocol=protocol, kwargs=kwargs): - with self.open(os_helper.TESTFN, **kwargs) as f: - with self.assertRaisesRegex(TypeError, msg): - pickle.dumps(f, protocol) - - @unittest.skipIf(support.is_emscripten, "Emscripten corrupts memory when writing to nonblocking fd") - def test_nonblock_pipe_write_bigbuf(self): - self._test_nonblock_pipe_write(16*1024) - - @unittest.skipIf(support.is_emscripten, "Emscripten corrupts memory when writing to nonblocking fd") - def test_nonblock_pipe_write_smallbuf(self): - self._test_nonblock_pipe_write(1024) - - @unittest.skipUnless(hasattr(os, 'set_blocking'), - 'os.set_blocking() required for this test') - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def _test_nonblock_pipe_write(self, bufsize): - sent = [] - received = [] - r, w = os.pipe() - os.set_blocking(r, False) - os.set_blocking(w, False) - - # To exercise all code paths in the C implementation we need - # to play with buffer sizes. For instance, if we choose a - # buffer size less than or equal to _PIPE_BUF (4096 on Linux) - # then we will never get a partial write of the buffer. - rf = self.open(r, mode='rb', closefd=True, buffering=bufsize) - wf = self.open(w, mode='wb', closefd=True, buffering=bufsize) - - with rf, wf: - for N in 9999, 73, 7574: - try: - i = 0 - while True: - msg = bytes([i % 26 + 97]) * N - sent.append(msg) - wf.write(msg) - i += 1 - - except self.BlockingIOError as e: - self.assertEqual(e.args[0], errno.EAGAIN) - self.assertEqual(e.args[2], e.characters_written) - sent[-1] = sent[-1][:e.characters_written] - received.append(rf.read()) - msg = b'BLOCKED' - wf.write(msg) - sent.append(msg) - - while True: - try: - wf.flush() - break - except self.BlockingIOError as e: - self.assertEqual(e.args[0], errno.EAGAIN) - self.assertEqual(e.args[2], e.characters_written) - self.assertEqual(e.characters_written, 0) - received.append(rf.read()) - - received += iter(rf.read, None) - - sent, received = b''.join(sent), b''.join(received) - self.assertEqual(sent, received) - self.assertTrue(wf.closed) - self.assertTrue(rf.closed) - - def test_create_fail(self): - # 'x' mode fails if file is existing - with self.open(os_helper.TESTFN, 'w', encoding="utf-8"): - pass - self.assertRaises(FileExistsError, self.open, os_helper.TESTFN, 'x', encoding="utf-8") - - def test_create_writes(self): - # 'x' mode opens for writing - with self.open(os_helper.TESTFN, 'xb') as f: - f.write(b"spam") - with self.open(os_helper.TESTFN, 'rb') as f: - self.assertEqual(b"spam", f.read()) - - def test_open_allargs(self): - # there used to be a buffer overflow in the parser for rawmode - self.assertRaises(ValueError, self.open, os_helper.TESTFN, 'rwax+', encoding="utf-8") - - def test_check_encoding_errors(self): - # bpo-37388: open() and TextIOWrapper must check encoding and errors - # arguments in dev mode - mod = self.io.__name__ - filename = __file__ - invalid = 'Boom, Shaka Laka, Boom!' - code = textwrap.dedent(f''' - import sys - from {mod} import open, TextIOWrapper - - try: - open({filename!r}, encoding={invalid!r}) - except LookupError: - pass - else: - sys.exit(21) - - try: - open({filename!r}, errors={invalid!r}) - except LookupError: - pass - else: - sys.exit(22) - - fp = open({filename!r}, "rb") - with fp: - try: - TextIOWrapper(fp, encoding={invalid!r}) - except LookupError: - pass - else: - sys.exit(23) - - try: - TextIOWrapper(fp, errors={invalid!r}) - except LookupError: - pass - else: - sys.exit(24) - - sys.exit(10) - ''') - proc = assert_python_failure('-X', 'dev', '-c', code) - self.assertEqual(proc.rc, 10, proc) - - def test_check_encoding_warning(self): - # PEP 597: Raise warning when encoding is not specified - # and sys.flags.warn_default_encoding is set. - mod = self.io.__name__ - filename = __file__ - code = textwrap.dedent(f'''\ - import sys - from {mod} import open, TextIOWrapper - import pathlib - - with open({filename!r}) as f: # line 5 - pass - - pathlib.Path({filename!r}).read_text() # line 8 - ''') - proc = assert_python_ok('-X', 'warn_default_encoding', '-c', code) - warnings = proc.err.splitlines() - self.assertEqual(len(warnings), 2) - self.assertStartsWith(warnings[0], b"<string>:5: EncodingWarning: ") - self.assertStartsWith(warnings[1], b"<string>:8: EncodingWarning: ") - - def test_text_encoding(self): - # PEP 597, bpo-47000. io.text_encoding() returns "locale" or "utf-8" - # based on sys.flags.utf8_mode - code = "import io; print(io.text_encoding(None))" - - proc = assert_python_ok('-X', 'utf8=0', '-c', code) - self.assertEqual(b"locale", proc.out.strip()) - - proc = assert_python_ok('-X', 'utf8=1', '-c', code) - self.assertEqual(b"utf-8", proc.out.strip()) - - -class CMiscIOTest(MiscIOTest): - io = io - name_of_module = "io", "_io" - extra_exported = "BlockingIOError", - - def test_readinto_buffer_overflow(self): - # Issue #18025 - class BadReader(self.io.BufferedIOBase): - def read(self, n=-1): - return b'x' * 10**6 - bufio = BadReader() - b = bytearray(2) - self.assertRaises(ValueError, bufio.readinto, b) - - def check_daemon_threads_shutdown_deadlock(self, stream_name): - # Issue #23309: deadlocks at shutdown should be avoided when a - # daemon thread and the main thread both write to a file. - code = """if 1: - import sys - import time - import threading - from test.support import SuppressCrashReport - - file = sys.{stream_name} - - def run(): - while True: - file.write('.') - file.flush() - - crash = SuppressCrashReport() - crash.__enter__() - # don't call __exit__(): the crash occurs at Python shutdown - - thread = threading.Thread(target=run) - thread.daemon = True - thread.start() - - time.sleep(0.5) - file.write('!') - file.flush() - """.format_map(locals()) - res, _ = run_python_until_end("-c", code) - err = res.err.decode() - if res.rc != 0: - # Failure: should be a fatal error - pattern = (r"Fatal Python error: _enter_buffered_busy: " - r"could not acquire lock " - r"for <(_io\.)?BufferedWriter name='<{stream_name}>'> " - r"at interpreter shutdown, possibly due to " - r"daemon threads".format_map(locals())) - self.assertRegex(err, pattern) - else: - self.assertFalse(err.strip('.!')) - - @threading_helper.requires_working_threading() - @support.requires_resource('walltime') - def test_daemon_threads_shutdown_stdout_deadlock(self): - self.check_daemon_threads_shutdown_deadlock('stdout') - - @threading_helper.requires_working_threading() - @support.requires_resource('walltime') - def test_daemon_threads_shutdown_stderr_deadlock(self): - self.check_daemon_threads_shutdown_deadlock('stderr') - - -class PyMiscIOTest(MiscIOTest): - io = pyio - name_of_module = "_pyio", "io" - extra_exported = "BlockingIOError", "open_code", - not_exported = "valid_seek_flags", - - -@unittest.skipIf(os.name == 'nt', 'POSIX signals required for this test.') -class SignalsTest(unittest.TestCase): - - def setUp(self): - self.oldalrm = signal.signal(signal.SIGALRM, self.alarm_interrupt) - - def tearDown(self): - signal.signal(signal.SIGALRM, self.oldalrm) - - def alarm_interrupt(self, sig, frame): - 1/0 - - def check_interrupted_write(self, item, bytes, **fdopen_kwargs): - """Check that a partial write, when it gets interrupted, properly - invokes the signal handler, and bubbles up the exception raised - in the latter.""" - - # XXX This test has three flaws that appear when objects are - # XXX not reference counted. - - # - if wio.write() happens to trigger a garbage collection, - # the signal exception may be raised when some __del__ - # method is running; it will not reach the assertRaises() - # call. - - # - more subtle, if the wio object is not destroyed at once - # and survives this function, the next opened file is likely - # to have the same fileno (since the file descriptor was - # actively closed). When wio.__del__ is finally called, it - # will close the other's test file... To trigger this with - # CPython, try adding "global wio" in this function. - - # - This happens only for streams created by the _pyio module, - # because a wio.close() that fails still consider that the - # file needs to be closed again. You can try adding an - # "assert wio.closed" at the end of the function. - - # Fortunately, a little gc.collect() seems to be enough to - # work around all these issues. - support.gc_collect() # For PyPy or other GCs. - - read_results = [] - def _read(): - s = os.read(r, 1) - read_results.append(s) - - t = threading.Thread(target=_read) - t.daemon = True - r, w = os.pipe() - fdopen_kwargs["closefd"] = False - large_data = item * (support.PIPE_MAX_SIZE // len(item) + 1) - try: - wio = self.io.open(w, **fdopen_kwargs) - if hasattr(signal, 'pthread_sigmask'): - # create the thread with SIGALRM signal blocked - signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGALRM]) - t.start() - signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGALRM]) - else: - t.start() - - # Fill the pipe enough that the write will be blocking. - # It will be interrupted by the timer armed above. Since the - # other thread has read one byte, the low-level write will - # return with a successful (partial) result rather than an EINTR. - # The buffered IO layer must check for pending signal - # handlers, which in this case will invoke alarm_interrupt(). - signal.alarm(1) - try: - self.assertRaises(ZeroDivisionError, wio.write, large_data) - finally: - signal.alarm(0) - t.join() - # We got one byte, get another one and check that it isn't a - # repeat of the first one. - read_results.append(os.read(r, 1)) - self.assertEqual(read_results, [bytes[0:1], bytes[1:2]]) - finally: - os.close(w) - os.close(r) - # This is deliberate. If we didn't close the file descriptor - # before closing wio, wio would try to flush its internal - # buffer, and block again. - try: - wio.close() - except OSError as e: - if e.errno != errno.EBADF: - raise - - @requires_alarm - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_interrupted_write_unbuffered(self): - self.check_interrupted_write(b"xy", b"xy", mode="wb", buffering=0) - - @requires_alarm - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_interrupted_write_buffered(self): - self.check_interrupted_write(b"xy", b"xy", mode="wb") - - @requires_alarm - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_interrupted_write_text(self): - self.check_interrupted_write("xy", b"xy", mode="w", encoding="ascii") - - @support.no_tracing - def check_reentrant_write(self, data, **fdopen_kwargs): - def on_alarm(*args): - # Will be called reentrantly from the same thread - wio.write(data) - 1/0 - signal.signal(signal.SIGALRM, on_alarm) - r, w = os.pipe() - wio = self.io.open(w, **fdopen_kwargs) - try: - signal.alarm(1) - # Either the reentrant call to wio.write() fails with RuntimeError, - # or the signal handler raises ZeroDivisionError. - with self.assertRaises((ZeroDivisionError, RuntimeError)) as cm: - while 1: - for i in range(100): - wio.write(data) - wio.flush() - # Make sure the buffer doesn't fill up and block further writes - os.read(r, len(data) * 100) - exc = cm.exception - if isinstance(exc, RuntimeError): - self.assertStartsWith(str(exc), "reentrant call") - finally: - signal.alarm(0) - wio.close() - os.close(r) - - @requires_alarm - def test_reentrant_write_buffered(self): - self.check_reentrant_write(b"xy", mode="wb") - - @requires_alarm - def test_reentrant_write_text(self): - self.check_reentrant_write("xy", mode="w", encoding="ascii") - - def check_interrupted_read_retry(self, decode, **fdopen_kwargs): - """Check that a buffered read, when it gets interrupted (either - returning a partial result or EINTR), properly invokes the signal - handler and retries if the latter returned successfully.""" - r, w = os.pipe() - fdopen_kwargs["closefd"] = False - def alarm_handler(sig, frame): - os.write(w, b"bar") - signal.signal(signal.SIGALRM, alarm_handler) - try: - rio = self.io.open(r, **fdopen_kwargs) - os.write(w, b"foo") - signal.alarm(1) - # Expected behaviour: - # - first raw read() returns partial b"foo" - # - second raw read() returns EINTR - # - third raw read() returns b"bar" - self.assertEqual(decode(rio.read(6)), "foobar") - finally: - signal.alarm(0) - rio.close() - os.close(w) - os.close(r) - - @requires_alarm - @support.requires_resource('walltime') - def test_interrupted_read_retry_buffered(self): - self.check_interrupted_read_retry(lambda x: x.decode('latin1'), - mode="rb") - - @requires_alarm - @support.requires_resource('walltime') - def test_interrupted_read_retry_text(self): - self.check_interrupted_read_retry(lambda x: x, - mode="r", encoding="latin1") - - def check_interrupted_write_retry(self, item, **fdopen_kwargs): - """Check that a buffered write, when it gets interrupted (either - returning a partial result or EINTR), properly invokes the signal - handler and retries if the latter returned successfully.""" - select = import_helper.import_module("select") - - # A quantity that exceeds the buffer size of an anonymous pipe's - # write end. - N = support.PIPE_MAX_SIZE - r, w = os.pipe() - fdopen_kwargs["closefd"] = False - - # We need a separate thread to read from the pipe and allow the - # write() to finish. This thread is started after the SIGALRM is - # received (forcing a first EINTR in write()). - read_results = [] - write_finished = False - error = None - def _read(): - try: - while not write_finished: - while r in select.select([r], [], [], 1.0)[0]: - s = os.read(r, 1024) - read_results.append(s) - except BaseException as exc: - nonlocal error - error = exc - t = threading.Thread(target=_read) - t.daemon = True - def alarm1(sig, frame): - signal.signal(signal.SIGALRM, alarm2) - signal.alarm(1) - def alarm2(sig, frame): - t.start() - - large_data = item * N - signal.signal(signal.SIGALRM, alarm1) - try: - wio = self.io.open(w, **fdopen_kwargs) - signal.alarm(1) - # Expected behaviour: - # - first raw write() is partial (because of the limited pipe buffer - # and the first alarm) - # - second raw write() returns EINTR (because of the second alarm) - # - subsequent write()s are successful (either partial or complete) - written = wio.write(large_data) - self.assertEqual(N, written) - - wio.flush() - write_finished = True - t.join() - - self.assertIsNone(error) - self.assertEqual(N, sum(len(x) for x in read_results)) - finally: - signal.alarm(0) - write_finished = True - os.close(w) - os.close(r) - # This is deliberate. If we didn't close the file descriptor - # before closing wio, wio would try to flush its internal - # buffer, and could block (in case of failure). - try: - wio.close() - except OSError as e: - if e.errno != errno.EBADF: - raise - - @requires_alarm - @support.requires_resource('walltime') - def test_interrupted_write_retry_buffered(self): - self.check_interrupted_write_retry(b"x", mode="wb") - - @requires_alarm - @support.requires_resource('walltime') - def test_interrupted_write_retry_text(self): - self.check_interrupted_write_retry("x", mode="w", encoding="latin1") - - -class CSignalsTest(SignalsTest): - io = io - -class PySignalsTest(SignalsTest): - io = pyio - - # Handling reentrancy issues would slow down _pyio even more, so the - # tests are disabled. - test_reentrant_write_buffered = None - test_reentrant_write_text = None - - -class ProtocolsTest(unittest.TestCase): - class MyReader: - def read(self, sz=-1): - return b"" - - class MyWriter: - def write(self, b: bytes): - pass - - def test_reader_subclass(self): - self.assertIsSubclass(MyReader, io.Reader[bytes]) - self.assertNotIsSubclass(str, io.Reader[bytes]) - - def test_writer_subclass(self): - self.assertIsSubclass(MyWriter, io.Writer[bytes]) - self.assertNotIsSubclass(str, io.Writer[bytes]) - - -def load_tests(loader, tests, pattern): - tests = (CIOTest, PyIOTest, APIMismatchTest, - CBufferedReaderTest, PyBufferedReaderTest, - CBufferedWriterTest, PyBufferedWriterTest, - CBufferedRWPairTest, PyBufferedRWPairTest, - CBufferedRandomTest, PyBufferedRandomTest, - StatefulIncrementalDecoderTest, - CIncrementalNewlineDecoderTest, PyIncrementalNewlineDecoderTest, - CTextIOWrapperTest, PyTextIOWrapperTest, - CMiscIOTest, PyMiscIOTest, - CSignalsTest, PySignalsTest, TestIOCTypes, - ) - - # Put the namespaces of the IO module we are testing and some useful mock - # classes in the __dict__ of each test. - mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO, - MockNonBlockWriterIO, MockUnseekableIO, MockRawIOWithoutRead, - SlowFlushRawIO, MockCharPseudoDevFileIO) - all_members = io.__all__ - c_io_ns = {name : getattr(io, name) for name in all_members} - py_io_ns = {name : getattr(pyio, name) for name in all_members} - globs = globals() - c_io_ns.update((x.__name__, globs["C" + x.__name__]) for x in mocks) - py_io_ns.update((x.__name__, globs["Py" + x.__name__]) for x in mocks) - for test in tests: - if test.__name__.startswith("C"): - for name, obj in c_io_ns.items(): - setattr(test, name, obj) - test.is_C = True - elif test.__name__.startswith("Py"): - for name, obj in py_io_ns.items(): - setattr(test, name, obj) - test.is_C = False - - suite = loader.suiteClass() - for test in tests: - suite.addTest(loader.loadTestsFromTestCase(test)) - return suite - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_io/__init__.py b/Lib/test/test_io/__init__.py new file mode 100644 index 00000000000000..c94fad3e779381 --- /dev/null +++ b/Lib/test/test_io/__init__.py @@ -0,0 +1,28 @@ +"""Tests for the io module and its implementations (_io and _pyio) + +Tests are split across multiple files to increase +parallelism and focus on specific implementation pieces. + +* test_io + * test_bufferedio - tests file buffering + * test_memoryio - tests BytesIO and StringIO + * test_fileio - tests FileIO + * test_file - tests the file interface + * test_general - tests everything else in the io module + * test_univnewlines - tests universal newline support + * test_largefile - tests operations on a file greater than 2**32 bytes + (only enabled with -ulargefile) +* test_free_threading/test_io - tests thread safety of io objects + +.. attention:: + When writing tests for io, it's important to test both the C and Python + implementations. This is usually done by writing a base test that refers to + the type it is testing as an attribute. Then it provides custom subclasses to + test both implementations. This directory contains lots of examples. +""" + +import os +from test.support import load_package_tests + +def load_tests(*args): + return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_io/__main__.py b/Lib/test/test_io/__main__.py new file mode 100644 index 00000000000000..40a23a297ec2b4 --- /dev/null +++ b/Lib/test/test_io/__main__.py @@ -0,0 +1,4 @@ +from . import load_tests +import unittest + +unittest.main() diff --git a/Lib/test/test_io/test_bufferedio.py b/Lib/test/test_io/test_bufferedio.py new file mode 100644 index 00000000000000..e83dd0d4e28d00 --- /dev/null +++ b/Lib/test/test_io/test_bufferedio.py @@ -0,0 +1,1567 @@ +import array +import pickle +import random +import sys +import threading +import time +import unittest +import warnings +import weakref +from collections import deque, UserList +from itertools import cycle, count +from test import support +from test.support import check_sanitizer, os_helper, threading_helper +from .utils import byteslike, CTestCase, PyTestCase + + +import io # C implementation. +import _pyio as pyio # Python implementation. + + +class CommonBufferedTests: + # Tests common to BufferedReader, BufferedWriter and BufferedRandom + + def test_detach(self): + raw = self.MockRawIO() + buf = self.tp(raw) + self.assertIs(buf.detach(), raw) + self.assertRaises(ValueError, buf.detach) + + repr(buf) # Should still work + + def test_fileno(self): + rawio = self.MockRawIO() + bufio = self.tp(rawio) + + self.assertEqual(42, bufio.fileno()) + + def test_invalid_args(self): + rawio = self.MockRawIO() + bufio = self.tp(rawio) + # Invalid whence + self.assertRaises(ValueError, bufio.seek, 0, -1) + self.assertRaises(ValueError, bufio.seek, 0, 9) + + def test_override_destructor(self): + tp = self.tp + record = [] + class MyBufferedIO(tp): + def __del__(self): + record.append(1) + try: + f = super().__del__ + except AttributeError: + pass + else: + f() + def close(self): + record.append(2) + super().close() + def flush(self): + record.append(3) + super().flush() + rawio = self.MockRawIO() + bufio = MyBufferedIO(rawio) + del bufio + support.gc_collect() + self.assertEqual(record, [1, 2, 3]) + + def test_context_manager(self): + # Test usability as a context manager + rawio = self.MockRawIO() + bufio = self.tp(rawio) + def _with(): + with bufio: + pass + _with() + # bufio should now be closed, and using it a second time should raise + # a ValueError. + self.assertRaises(ValueError, _with) + + def test_error_through_destructor(self): + # Test that the exception state is not modified by a destructor, + # even if close() fails. + rawio = self.CloseFailureIO() + with support.catch_unraisable_exception() as cm: + with self.assertRaises(AttributeError): + self.tp(rawio).xyzzy + + self.assertEqual(cm.unraisable.exc_type, OSError) + + def test_repr(self): + raw = self.MockRawIO() + b = self.tp(raw) + clsname = r"(%s\.)?%s" % (self.tp.__module__, self.tp.__qualname__) + self.assertRegex(repr(b), "<%s>" % clsname) + raw.name = "dummy" + self.assertRegex(repr(b), "<%s name='dummy'>" % clsname) + raw.name = b"dummy" + self.assertRegex(repr(b), "<%s name=b'dummy'>" % clsname) + + def test_recursive_repr(self): + # Issue #25455 + raw = self.MockRawIO() + b = self.tp(raw) + with support.swap_attr(raw, 'name', b), support.infinite_recursion(25): + with self.assertRaises(RuntimeError): + repr(b) # Should not crash + + def test_flush_error_on_close(self): + # Test that buffered file is closed despite failed flush + # and that flush() is called before file closed. + raw = self.MockRawIO() + closed = [] + def bad_flush(): + closed[:] = [b.closed, raw.closed] + raise OSError() + raw.flush = bad_flush + b = self.tp(raw) + self.assertRaises(OSError, b.close) # exception not swallowed + self.assertTrue(b.closed) + self.assertTrue(raw.closed) + self.assertTrue(closed) # flush() called + self.assertFalse(closed[0]) # flush() called before file closed + self.assertFalse(closed[1]) + raw.flush = lambda: None # break reference loop + + def test_close_error_on_close(self): + raw = self.MockRawIO() + def bad_flush(): + raise OSError('flush') + def bad_close(): + raise OSError('close') + raw.close = bad_close + b = self.tp(raw) + b.flush = bad_flush + with self.assertRaises(OSError) as err: # exception not swallowed + b.close() + self.assertEqual(err.exception.args, ('close',)) + self.assertIsInstance(err.exception.__context__, OSError) + self.assertEqual(err.exception.__context__.args, ('flush',)) + self.assertFalse(b.closed) + + # Silence destructor error + raw.close = lambda: None + b.flush = lambda: None + + def test_nonnormalized_close_error_on_close(self): + # Issue #21677 + raw = self.MockRawIO() + def bad_flush(): + raise non_existing_flush + def bad_close(): + raise non_existing_close + raw.close = bad_close + b = self.tp(raw) + b.flush = bad_flush + with self.assertRaises(NameError) as err: # exception not swallowed + b.close() + self.assertIn('non_existing_close', str(err.exception)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertIn('non_existing_flush', str(err.exception.__context__)) + self.assertFalse(b.closed) + + # Silence destructor error + b.flush = lambda: None + raw.close = lambda: None + + def test_multi_close(self): + raw = self.MockRawIO() + b = self.tp(raw) + b.close() + b.close() + b.close() + self.assertRaises(ValueError, b.flush) + + def test_unseekable(self): + bufio = self.tp(self.MockUnseekableIO(b"A" * 10)) + self.assertRaises(self.UnsupportedOperation, bufio.tell) + self.assertRaises(self.UnsupportedOperation, bufio.seek, 0) + + def test_readonly_attributes(self): + raw = self.MockRawIO() + buf = self.tp(raw) + x = self.MockRawIO() + with self.assertRaises(AttributeError): + buf.raw = x + + def test_pickling_subclass(self): + global MyBufferedIO + class MyBufferedIO(self.tp): + def __init__(self, raw, tag): + super().__init__(raw) + self.tag = tag + def __getstate__(self): + return self.tag, self.raw.getvalue() + def __setstate__(slf, state): + tag, value = state + slf.__init__(self.BytesIO(value), tag) + + raw = self.BytesIO(b'data') + buf = MyBufferedIO(raw, tag='ham') + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + pickled = pickle.dumps(buf, proto) + newbuf = pickle.loads(pickled) + self.assertEqual(newbuf.raw.getvalue(), b'data') + self.assertEqual(newbuf.tag, 'ham') + del MyBufferedIO + + +class SizeofTest: + + @support.cpython_only + def test_sizeof(self): + bufsize1 = 4096 + bufsize2 = 8192 + rawio = self.MockRawIO() + bufio = self.tp(rawio, buffer_size=bufsize1) + size = sys.getsizeof(bufio) - bufsize1 + rawio = self.MockRawIO() + bufio = self.tp(rawio, buffer_size=bufsize2) + self.assertEqual(sys.getsizeof(bufio), size + bufsize2) + + @support.cpython_only + def test_buffer_freeing(self) : + bufsize = 4096 + rawio = self.MockRawIO() + bufio = self.tp(rawio, buffer_size=bufsize) + size = sys.getsizeof(bufio) - bufsize + bufio.close() + self.assertEqual(sys.getsizeof(bufio), size) + +class BufferedReaderTest(CommonBufferedTests): + read_mode = "rb" + + def test_constructor(self): + rawio = self.MockRawIO([b"abc"]) + bufio = self.tp(rawio) + bufio.__init__(rawio) + bufio.__init__(rawio, buffer_size=1024) + bufio.__init__(rawio, buffer_size=16) + self.assertEqual(b"abc", bufio.read()) + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=0) + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-16) + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1) + rawio = self.MockRawIO([b"abc"]) + bufio.__init__(rawio) + self.assertEqual(b"abc", bufio.read()) + + def test_uninitialized(self): + bufio = self.tp.__new__(self.tp) + del bufio + bufio = self.tp.__new__(self.tp) + self.assertRaisesRegex((ValueError, AttributeError), + 'uninitialized|has no attribute', + bufio.read, 0) + bufio.__init__(self.MockRawIO()) + self.assertEqual(bufio.read(0), b'') + + def test_read(self): + for arg in (None, 7): + rawio = self.MockRawIO((b"abc", b"d", b"efg")) + bufio = self.tp(rawio) + self.assertEqual(b"abcdefg", bufio.read(arg)) + # Invalid args + self.assertRaises(ValueError, bufio.read, -2) + + def test_read1(self): + rawio = self.MockRawIO((b"abc", b"d", b"efg")) + bufio = self.tp(rawio) + self.assertEqual(b"a", bufio.read(1)) + self.assertEqual(b"b", bufio.read1(1)) + self.assertEqual(rawio._reads, 1) + self.assertEqual(b"", bufio.read1(0)) + self.assertEqual(b"c", bufio.read1(100)) + self.assertEqual(rawio._reads, 1) + self.assertEqual(b"d", bufio.read1(100)) + self.assertEqual(rawio._reads, 2) + self.assertEqual(b"efg", bufio.read1(100)) + self.assertEqual(rawio._reads, 3) + self.assertEqual(b"", bufio.read1(100)) + self.assertEqual(rawio._reads, 4) + + def test_read1_arbitrary(self): + rawio = self.MockRawIO((b"abc", b"d", b"efg")) + bufio = self.tp(rawio) + self.assertEqual(b"a", bufio.read(1)) + self.assertEqual(b"bc", bufio.read1()) + self.assertEqual(b"d", bufio.read1()) + self.assertEqual(b"efg", bufio.read1(-1)) + self.assertEqual(rawio._reads, 3) + self.assertEqual(b"", bufio.read1()) + self.assertEqual(rawio._reads, 4) + + def test_readinto(self): + rawio = self.MockRawIO((b"abc", b"d", b"efg")) + bufio = self.tp(rawio) + b = bytearray(2) + self.assertEqual(bufio.readinto(b), 2) + self.assertEqual(b, b"ab") + self.assertEqual(bufio.readinto(b), 2) + self.assertEqual(b, b"cd") + self.assertEqual(bufio.readinto(b), 2) + self.assertEqual(b, b"ef") + self.assertEqual(bufio.readinto(b), 1) + self.assertEqual(b, b"gf") + self.assertEqual(bufio.readinto(b), 0) + self.assertEqual(b, b"gf") + rawio = self.MockRawIO((b"abc", None)) + bufio = self.tp(rawio) + self.assertEqual(bufio.readinto(b), 2) + self.assertEqual(b, b"ab") + self.assertEqual(bufio.readinto(b), 1) + self.assertEqual(b, b"cb") + + def test_readinto1(self): + buffer_size = 10 + rawio = self.MockRawIO((b"abc", b"de", b"fgh", b"jkl")) + bufio = self.tp(rawio, buffer_size=buffer_size) + b = bytearray(2) + self.assertEqual(bufio.peek(3), b'abc') + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 2) + self.assertEqual(b, b"ab") + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 1) + self.assertEqual(b[:1], b"c") + self.assertEqual(rawio._reads, 1) + self.assertEqual(bufio.readinto1(b), 2) + self.assertEqual(b, b"de") + self.assertEqual(rawio._reads, 2) + b = bytearray(2*buffer_size) + self.assertEqual(bufio.peek(3), b'fgh') + self.assertEqual(rawio._reads, 3) + self.assertEqual(bufio.readinto1(b), 6) + self.assertEqual(b[:6], b"fghjkl") + self.assertEqual(rawio._reads, 4) + + def test_readinto_array(self): + buffer_size = 60 + data = b"a" * 26 + rawio = self.MockRawIO((data,)) + bufio = self.tp(rawio, buffer_size=buffer_size) + + # Create an array with element size > 1 byte + b = array.array('i', b'x' * 32) + assert len(b) != 16 + + # Read into it. We should get as many *bytes* as we can fit into b + # (which is more than the number of elements) + n = bufio.readinto(b) + self.assertGreater(n, len(b)) + + # Check that old contents of b are preserved + bm = memoryview(b).cast('B') + self.assertLess(n, len(bm)) + self.assertEqual(bm[:n], data[:n]) + self.assertEqual(bm[n:], b'x' * (len(bm[n:]))) + + def test_readinto1_array(self): + buffer_size = 60 + data = b"a" * 26 + rawio = self.MockRawIO((data,)) + bufio = self.tp(rawio, buffer_size=buffer_size) + + # Create an array with element size > 1 byte + b = array.array('i', b'x' * 32) + assert len(b) != 16 + + # Read into it. We should get as many *bytes* as we can fit into b + # (which is more than the number of elements) + n = bufio.readinto1(b) + self.assertGreater(n, len(b)) + + # Check that old contents of b are preserved + bm = memoryview(b).cast('B') + self.assertLess(n, len(bm)) + self.assertEqual(bm[:n], data[:n]) + self.assertEqual(bm[n:], b'x' * (len(bm[n:]))) + + def test_readlines(self): + def bufio(): + rawio = self.MockRawIO((b"abc\n", b"d\n", b"ef")) + return self.tp(rawio) + self.assertEqual(bufio().readlines(), [b"abc\n", b"d\n", b"ef"]) + self.assertEqual(bufio().readlines(5), [b"abc\n", b"d\n"]) + self.assertEqual(bufio().readlines(None), [b"abc\n", b"d\n", b"ef"]) + + def test_buffering(self): + data = b"abcdefghi" + dlen = len(data) + + tests = [ + [ 100, [ 3, 1, 4, 8 ], [ dlen, 0 ] ], + [ 100, [ 3, 3, 3], [ dlen ] ], + [ 4, [ 1, 2, 4, 2 ], [ 4, 4, 1 ] ], + ] + + for bufsize, buf_read_sizes, raw_read_sizes in tests: + rawio = self.MockFileIO(data) + bufio = self.tp(rawio, buffer_size=bufsize) + pos = 0 + for nbytes in buf_read_sizes: + self.assertEqual(bufio.read(nbytes), data[pos:pos+nbytes]) + pos += nbytes + # this is mildly implementation-dependent + self.assertEqual(rawio.read_history, raw_read_sizes) + + def test_read_non_blocking(self): + # Inject some None's in there to simulate EWOULDBLOCK + rawio = self.MockRawIO((b"abc", b"d", None, b"efg", None, None, None)) + bufio = self.tp(rawio) + self.assertEqual(b"abcd", bufio.read(6)) + self.assertEqual(b"e", bufio.read(1)) + self.assertEqual(b"fg", bufio.read()) + self.assertEqual(b"", bufio.peek(1)) + self.assertIsNone(bufio.read()) + self.assertEqual(b"", bufio.read()) + + rawio = self.MockRawIO((b"a", None, None)) + self.assertEqual(b"a", rawio.readall()) + self.assertIsNone(rawio.readall()) + + def test_read_past_eof(self): + rawio = self.MockRawIO((b"abc", b"d", b"efg")) + bufio = self.tp(rawio) + + self.assertEqual(b"abcdefg", bufio.read(9000)) + + def test_read_all(self): + rawio = self.MockRawIO((b"abc", b"d", b"efg")) + bufio = self.tp(rawio) + + self.assertEqual(b"abcdefg", bufio.read()) + + @threading_helper.requires_working_threading() + @support.requires_resource('cpu') + def test_threads(self): + try: + # Write out many bytes with exactly the same number of 0's, + # 1's... 255's. This will help us check that concurrent reading + # doesn't duplicate or forget contents. + N = 1000 + l = list(range(256)) * N + random.shuffle(l) + s = bytes(bytearray(l)) + with self.open(os_helper.TESTFN, "wb") as f: + f.write(s) + with self.open(os_helper.TESTFN, self.read_mode, buffering=0) as raw: + bufio = self.tp(raw, 8) + errors = [] + results = [] + def f(): + try: + # Intra-buffer read then buffer-flushing read + for n in cycle([1, 19]): + s = bufio.read(n) + if not s: + break + # list.append() is atomic + results.append(s) + except Exception as e: + errors.append(e) + raise + threads = [threading.Thread(target=f) for x in range(20)] + with threading_helper.start_threads(threads): + time.sleep(0.02) # yield + self.assertFalse(errors, + "the following exceptions were caught: %r" % errors) + s = b''.join(results) + for i in range(256): + c = bytes(bytearray([i])) + self.assertEqual(s.count(c), N) + finally: + os_helper.unlink(os_helper.TESTFN) + + def test_unseekable(self): + bufio = self.tp(self.MockUnseekableIO(b"A" * 10)) + self.assertRaises(self.UnsupportedOperation, bufio.tell) + self.assertRaises(self.UnsupportedOperation, bufio.seek, 0) + bufio.read(1) + self.assertRaises(self.UnsupportedOperation, bufio.seek, 0) + self.assertRaises(self.UnsupportedOperation, bufio.tell) + + def test_misbehaved_io(self): + rawio = self.MisbehavedRawIO((b"abc", b"d", b"efg")) + bufio = self.tp(rawio) + self.assertRaises(OSError, bufio.seek, 0) + self.assertRaises(OSError, bufio.tell) + + # Silence destructor error + bufio.close = lambda: None + + def test_no_extraneous_read(self): + # Issue #9550; when the raw IO object has satisfied the read request, + # we should not issue any additional reads, otherwise it may block + # (e.g. socket). + bufsize = 16 + for n in (2, bufsize - 1, bufsize, bufsize + 1, bufsize * 2): + rawio = self.MockRawIO([b"x" * n]) + bufio = self.tp(rawio, bufsize) + self.assertEqual(bufio.read(n), b"x" * n) + # Simple case: one raw read is enough to satisfy the request. + self.assertEqual(rawio._extraneous_reads, 0, + "failed for {}: {} != 0".format(n, rawio._extraneous_reads)) + # A more complex case where two raw reads are needed to satisfy + # the request. + rawio = self.MockRawIO([b"x" * (n - 1), b"x"]) + bufio = self.tp(rawio, bufsize) + self.assertEqual(bufio.read(n), b"x" * n) + self.assertEqual(rawio._extraneous_reads, 0, + "failed for {}: {} != 0".format(n, rawio._extraneous_reads)) + + def test_read_on_closed(self): + # Issue #23796 + b = self.BufferedReader(self.BytesIO(b"12")) + b.read(1) + b.close() + with self.subTest('peek'): + self.assertRaises(ValueError, b.peek) + with self.subTest('read1'): + self.assertRaises(ValueError, b.read1, 1) + with self.subTest('read'): + self.assertRaises(ValueError, b.read) + with self.subTest('readinto'): + self.assertRaises(ValueError, b.readinto, bytearray()) + with self.subTest('readinto1'): + self.assertRaises(ValueError, b.readinto1, bytearray()) + with self.subTest('flush'): + self.assertRaises(ValueError, b.flush) + with self.subTest('truncate'): + self.assertRaises(ValueError, b.truncate) + with self.subTest('seek'): + self.assertRaises(ValueError, b.seek, 0) + + def test_truncate_on_read_only(self): + rawio = self.MockFileIO(b"abc") + bufio = self.tp(rawio) + self.assertFalse(bufio.writable()) + self.assertRaises(self.UnsupportedOperation, bufio.truncate) + self.assertRaises(self.UnsupportedOperation, bufio.truncate, 0) + + def test_tell_character_device_file(self): + # GH-95782 + # For the (former) bug in BufferedIO to manifest, the wrapped IO obj + # must be able to produce at least 2 bytes. + raw = self.MockCharPseudoDevFileIO(b"12") + buf = self.tp(raw) + self.assertEqual(buf.tell(), 0) + self.assertEqual(buf.read(1), b"1") + self.assertEqual(buf.tell(), 0) + + def test_seek_character_device_file(self): + raw = self.MockCharPseudoDevFileIO(b"12") + buf = self.tp(raw) + self.assertEqual(buf.seek(0, io.SEEK_CUR), 0) + self.assertEqual(buf.seek(1, io.SEEK_SET), 0) + self.assertEqual(buf.seek(0, io.SEEK_CUR), 0) + self.assertEqual(buf.read(1), b"1") + + # In the C implementation, tell() sets the BufferedIO's abs_pos to 0, + # which means that the next seek() could return a negative offset if it + # does not sanity-check: + self.assertEqual(buf.tell(), 0) + self.assertEqual(buf.seek(0, io.SEEK_CUR), 0) + + +class CBufferedReaderTest(BufferedReaderTest, SizeofTest, CTestCase): + tp = io.BufferedReader + + def test_initialization(self): + rawio = self.MockRawIO([b"abc"]) + bufio = self.tp(rawio) + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=0) + self.assertRaises(ValueError, bufio.read) + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-16) + self.assertRaises(ValueError, bufio.read) + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1) + self.assertRaises(ValueError, bufio.read) + + def test_misbehaved_io_read(self): + rawio = self.MisbehavedRawIO((b"abc", b"d", b"efg")) + bufio = self.tp(rawio) + # _pyio.BufferedReader seems to implement reading different, so that + # checking this is not so easy. + self.assertRaises(OSError, bufio.read, 10) + + def test_garbage_collection(self): + # C BufferedReader objects are collected. + # The Python version has __del__, so it ends into gc.garbage instead + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + # Note that using warnings_helper.check_warnings() will keep the + # file alive due to the `source` argument to warn(). So, use + # catch_warnings() instead. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ResourceWarning) + rawio = self.FileIO(os_helper.TESTFN, "w+b") + f = self.tp(rawio) + f.f = f + wr = weakref.ref(f) + del f + support.gc_collect() + self.assertIsNone(wr(), wr) + + def test_args_error(self): + # Issue #17275 + with self.assertRaisesRegex(TypeError, "BufferedReader"): + self.tp(self.BytesIO(), 1024, 1024, 1024) + + def test_bad_readinto_value(self): + rawio = self.tp(self.BytesIO(b"12")) + rawio.readinto = lambda buf: -1 + bufio = self.tp(rawio) + with self.assertRaises(OSError) as cm: + bufio.readline() + self.assertIsNone(cm.exception.__cause__) + + def test_bad_readinto_type(self): + rawio = self.tp(self.BytesIO(b"12")) + rawio.readinto = lambda buf: b'' + bufio = self.tp(rawio) + with self.assertRaises(OSError) as cm: + bufio.readline() + self.assertIsInstance(cm.exception.__cause__, TypeError) + + @unittest.skipUnless(sys.maxsize > 2**32, 'requires 64bit platform') + @unittest.skipIf(check_sanitizer(thread=True), + 'ThreadSanitizer aborts on huge allocations (exit code 66).') + def test_read1_error_does_not_cause_reentrant_failure(self): + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with self.open(os_helper.TESTFN, "wb") as f: + f.write(b"hello") + + with self.open(os_helper.TESTFN, "rb", buffering=0) as raw: + bufio = self.tp(raw, buffer_size=8) + # To request a size that is far too huge to ever be satisfied, + # so that the internal buffer allocation reliably fails with MemoryError. + huge = sys.maxsize // 2 + 1 + with self.assertRaises(MemoryError): + bufio.read1(huge) + + # Used to crash before gh-143689: + self.assertEqual(bufio.read1(1), b"h") + + +class PyBufferedReaderTest(BufferedReaderTest, PyTestCase): + tp = pyio.BufferedReader + + +class BufferedWriterTest(CommonBufferedTests): + write_mode = "wb" + + def test_constructor(self): + rawio = self.MockRawIO() + bufio = self.tp(rawio) + bufio.__init__(rawio) + bufio.__init__(rawio, buffer_size=1024) + bufio.__init__(rawio, buffer_size=16) + self.assertEqual(3, bufio.write(b"abc")) + bufio.flush() + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=0) + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-16) + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1) + bufio.__init__(rawio) + self.assertEqual(3, bufio.write(b"ghi")) + bufio.flush() + self.assertEqual(b"".join(rawio._write_stack), b"abcghi") + + def test_uninitialized(self): + bufio = self.tp.__new__(self.tp) + del bufio + bufio = self.tp.__new__(self.tp) + self.assertRaisesRegex((ValueError, AttributeError), + 'uninitialized|has no attribute', + bufio.write, b'') + bufio.__init__(self.MockRawIO()) + self.assertEqual(bufio.write(b''), 0) + + def test_detach_flush(self): + raw = self.MockRawIO() + buf = self.tp(raw) + buf.write(b"howdy!") + self.assertFalse(raw._write_stack) + buf.detach() + self.assertEqual(raw._write_stack, [b"howdy!"]) + + def test_write(self): + # Write to the buffered IO but don't overflow the buffer. + writer = self.MockRawIO() + bufio = self.tp(writer, 8) + bufio.write(b"abc") + self.assertFalse(writer._write_stack) + buffer = bytearray(b"def") + bufio.write(buffer) + buffer[:] = b"***" # Overwrite our copy of the data + bufio.flush() + self.assertEqual(b"".join(writer._write_stack), b"abcdef") + + def test_write_overflow(self): + writer = self.MockRawIO() + bufio = self.tp(writer, 8) + contents = b"abcdefghijklmnop" + for n in range(0, len(contents), 3): + bufio.write(contents[n:n+3]) + flushed = b"".join(writer._write_stack) + # At least (total - 8) bytes were implicitly flushed, perhaps more + # depending on the implementation. + self.assertStartsWith(flushed, contents[:-8]) + + def check_writes(self, intermediate_func): + # Lots of writes, test the flushed output is as expected. + contents = bytes(range(256)) * 1000 + n = 0 + writer = self.MockRawIO() + bufio = self.tp(writer, 13) + # Generator of write sizes: repeat each N 15 times then proceed to N+1 + def gen_sizes(): + for size in count(1): + for i in range(15): + yield size + sizes = gen_sizes() + while n < len(contents): + size = min(next(sizes), len(contents) - n) + self.assertEqual(bufio.write(contents[n:n+size]), size) + intermediate_func(bufio) + n += size + bufio.flush() + self.assertEqual(contents, b"".join(writer._write_stack)) + + def test_writes(self): + self.check_writes(lambda bufio: None) + + def test_writes_and_flushes(self): + self.check_writes(lambda bufio: bufio.flush()) + + def test_writes_and_seeks(self): + def _seekabs(bufio): + pos = bufio.tell() + bufio.seek(pos + 1, 0) + bufio.seek(pos - 1, 0) + bufio.seek(pos, 0) + self.check_writes(_seekabs) + def _seekrel(bufio): + pos = bufio.seek(0, 1) + bufio.seek(+1, 1) + bufio.seek(-1, 1) + bufio.seek(pos, 0) + self.check_writes(_seekrel) + + def test_writes_and_truncates(self): + self.check_writes(lambda bufio: bufio.truncate(bufio.tell())) + + def test_write_non_blocking(self): + raw = self.MockNonBlockWriterIO() + bufio = self.tp(raw, 8) + + self.assertEqual(bufio.write(b"abcd"), 4) + self.assertEqual(bufio.write(b"efghi"), 5) + # 1 byte will be written, the rest will be buffered + raw.block_on(b"k") + self.assertEqual(bufio.write(b"jklmn"), 5) + + # 8 bytes will be written, 8 will be buffered and the rest will be lost + raw.block_on(b"0") + try: + bufio.write(b"opqrwxyz0123456789") + except self.BlockingIOError as e: + written = e.characters_written + else: + self.fail("BlockingIOError should have been raised") + self.assertEqual(written, 16) + self.assertEqual(raw.pop_written(), + b"abcdefghijklmnopqrwxyz") + + self.assertEqual(bufio.write(b"ABCDEFGHI"), 9) + s = raw.pop_written() + # Previously buffered bytes were flushed + self.assertStartsWith(s, b"01234567A") + + def test_write_and_rewind(self): + raw = self.BytesIO() + bufio = self.tp(raw, 4) + self.assertEqual(bufio.write(b"abcdef"), 6) + self.assertEqual(bufio.tell(), 6) + bufio.seek(0, 0) + self.assertEqual(bufio.write(b"XY"), 2) + bufio.seek(6, 0) + self.assertEqual(raw.getvalue(), b"XYcdef") + self.assertEqual(bufio.write(b"123456"), 6) + bufio.flush() + self.assertEqual(raw.getvalue(), b"XYcdef123456") + + def test_flush(self): + writer = self.MockRawIO() + bufio = self.tp(writer, 8) + bufio.write(b"abc") + bufio.flush() + self.assertEqual(b"abc", writer._write_stack[0]) + + def test_writelines(self): + l = [b'ab', b'cd', b'ef'] + writer = self.MockRawIO() + bufio = self.tp(writer, 8) + bufio.writelines(l) + bufio.flush() + self.assertEqual(b''.join(writer._write_stack), b'abcdef') + + def test_writelines_userlist(self): + l = UserList([b'ab', b'cd', b'ef']) + writer = self.MockRawIO() + bufio = self.tp(writer, 8) + bufio.writelines(l) + bufio.flush() + self.assertEqual(b''.join(writer._write_stack), b'abcdef') + + def test_writelines_error(self): + writer = self.MockRawIO() + bufio = self.tp(writer, 8) + self.assertRaises(TypeError, bufio.writelines, [1, 2, 3]) + self.assertRaises(TypeError, bufio.writelines, None) + self.assertRaises(TypeError, bufio.writelines, 'abc') + + def test_destructor(self): + writer = self.MockRawIO() + bufio = self.tp(writer, 8) + bufio.write(b"abc") + del bufio + support.gc_collect() + self.assertEqual(b"abc", writer._write_stack[0]) + + def test_truncate(self): + # Truncate implicitly flushes the buffer. + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with self.open(os_helper.TESTFN, self.write_mode, buffering=0) as raw: + bufio = self.tp(raw, 8) + bufio.write(b"abcdef") + self.assertEqual(bufio.truncate(3), 3) + self.assertEqual(bufio.tell(), 6) + with self.open(os_helper.TESTFN, "rb", buffering=0) as f: + self.assertEqual(f.read(), b"abc") + + def test_truncate_after_write(self): + # Ensure that truncate preserves the file position after + # writes longer than the buffer size. + # Issue: https://bugs.python.org/issue32228 + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + with self.open(os_helper.TESTFN, "wb") as f: + # Fill with some buffer + f.write(b'\x00' * 10000) + buffer_sizes = [8192, 4096, 200] + for buffer_size in buffer_sizes: + with self.open(os_helper.TESTFN, "r+b", buffering=buffer_size) as f: + f.write(b'\x00' * (buffer_size + 1)) + # After write write_pos and write_end are set to 0 + f.read(1) + # read operation makes sure that pos != raw_pos + f.truncate() + self.assertEqual(f.tell(), buffer_size + 2) + + @threading_helper.requires_working_threading() + @support.requires_resource('cpu') + def test_threads(self): + try: + # Write out many bytes from many threads and test they were + # all flushed. + N = 1000 + contents = bytes(range(256)) * N + sizes = cycle([1, 19]) + n = 0 + queue = deque() + while n < len(contents): + size = next(sizes) + queue.append(contents[n:n+size]) + n += size + del contents + # We use a real file object because it allows us to + # exercise situations where the GIL is released before + # writing the buffer to the raw streams. This is in addition + # to concurrency issues due to switching threads in the middle + # of Python code. + with self.open(os_helper.TESTFN, self.write_mode, buffering=0) as raw: + bufio = self.tp(raw, 8) + errors = [] + def f(): + try: + while True: + try: + s = queue.popleft() + except IndexError: + return + bufio.write(s) + except Exception as e: + errors.append(e) + raise + threads = [threading.Thread(target=f) for x in range(20)] + with threading_helper.start_threads(threads): + time.sleep(0.02) # yield + self.assertFalse(errors, + "the following exceptions were caught: %r" % errors) + bufio.close() + with self.open(os_helper.TESTFN, "rb") as f: + s = f.read() + for i in range(256): + self.assertEqual(s.count(bytes([i])), N) + finally: + os_helper.unlink(os_helper.TESTFN) + + def test_misbehaved_io(self): + rawio = self.MisbehavedRawIO() + bufio = self.tp(rawio, 5) + self.assertRaises(OSError, bufio.seek, 0) + self.assertRaises(OSError, bufio.tell) + self.assertRaises(OSError, bufio.write, b"abcdef") + + # Silence destructor error + bufio.close = lambda: None + + def test_max_buffer_size_removal(self): + with self.assertRaises(TypeError): + self.tp(self.MockRawIO(), 8, 12) + + def test_write_error_on_close(self): + raw = self.MockRawIO() + def bad_write(b): + raise OSError() + raw.write = bad_write + b = self.tp(raw) + b.write(b'spam') + self.assertRaises(OSError, b.close) # exception not swallowed + self.assertTrue(b.closed) + + @threading_helper.requires_working_threading() + def test_slow_close_from_thread(self): + # Issue #31976 + rawio = self.SlowFlushRawIO() + bufio = self.tp(rawio, 8) + t = threading.Thread(target=bufio.close) + t.start() + rawio.in_flush.wait() + self.assertRaises(ValueError, bufio.write, b'spam') + self.assertTrue(bufio.closed) + t.join() + + +class CBufferedWriterTest(BufferedWriterTest, SizeofTest, CTestCase): + tp = io.BufferedWriter + + def test_initialization(self): + rawio = self.MockRawIO() + bufio = self.tp(rawio) + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=0) + self.assertRaises(ValueError, bufio.write, b"def") + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-16) + self.assertRaises(ValueError, bufio.write, b"def") + self.assertRaises(ValueError, bufio.__init__, rawio, buffer_size=-1) + self.assertRaises(ValueError, bufio.write, b"def") + + def test_garbage_collection(self): + # C BufferedWriter objects are collected, and collecting them flushes + # all data to disk. + # The Python version has __del__, so it ends into gc.garbage instead + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + # Note that using warnings_helper.check_warnings() will keep the + # file alive due to the `source` argument to warn(). So, use + # catch_warnings() instead. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ResourceWarning) + rawio = self.FileIO(os_helper.TESTFN, "w+b") + f = self.tp(rawio) + f.write(b"123xxx") + f.x = f + wr = weakref.ref(f) + del f + support.gc_collect() + self.assertIsNone(wr(), wr) + with self.open(os_helper.TESTFN, "rb") as f: + self.assertEqual(f.read(), b"123xxx") + + def test_args_error(self): + # Issue #17275 + with self.assertRaisesRegex(TypeError, "BufferedWriter"): + self.tp(self.BytesIO(), 1024, 1024, 1024) + + def test_non_boolean_closed_attr(self): + # gh-140650: check TypeError is raised + class MockRawIOWithoutClosed(self.MockRawIO): + closed = NotImplemented + + bufio = self.tp(MockRawIOWithoutClosed()) + self.assertRaises(TypeError, bufio.write, b"") + self.assertRaises(TypeError, bufio.flush) + self.assertRaises(TypeError, bufio.close) + + def test_closed_attr_raises(self): + class MockRawIOClosedRaises(self.MockRawIO): + @property + def closed(self): + raise ValueError("test") + + bufio = self.tp(MockRawIOClosedRaises()) + self.assertRaisesRegex(ValueError, "test", bufio.write, b"") + self.assertRaisesRegex(ValueError, "test", bufio.flush) + self.assertRaisesRegex(ValueError, "test", bufio.close) + + +class PyBufferedWriterTest(BufferedWriterTest, PyTestCase): + tp = pyio.BufferedWriter + +class BufferedRWPairTest: + + def test_constructor(self): + pair = self.tp(self.MockRawIO(), self.MockRawIO()) + self.assertFalse(pair.closed) + + def test_uninitialized(self): + pair = self.tp.__new__(self.tp) + del pair + pair = self.tp.__new__(self.tp) + self.assertRaisesRegex((ValueError, AttributeError), + 'uninitialized|has no attribute', + pair.read, 0) + self.assertRaisesRegex((ValueError, AttributeError), + 'uninitialized|has no attribute', + pair.write, b'') + pair.__init__(self.MockRawIO(), self.MockRawIO()) + self.assertEqual(pair.read(0), b'') + self.assertEqual(pair.write(b''), 0) + + def test_detach(self): + pair = self.tp(self.MockRawIO(), self.MockRawIO()) + self.assertRaises(self.UnsupportedOperation, pair.detach) + + def test_constructor_max_buffer_size_removal(self): + with self.assertRaises(TypeError): + self.tp(self.MockRawIO(), self.MockRawIO(), 8, 12) + + def test_constructor_with_not_readable(self): + class NotReadable(self.MockRawIO): + def readable(self): + return False + + self.assertRaises(OSError, self.tp, NotReadable(), self.MockRawIO()) + + def test_constructor_with_not_writeable(self): + class NotWriteable(self.MockRawIO): + def writable(self): + return False + + self.assertRaises(OSError, self.tp, self.MockRawIO(), NotWriteable()) + + def test_read(self): + pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO()) + + self.assertEqual(pair.read(3), b"abc") + self.assertEqual(pair.read(1), b"d") + self.assertEqual(pair.read(), b"ef") + pair = self.tp(self.BytesIO(b"abc"), self.MockRawIO()) + self.assertEqual(pair.read(None), b"abc") + + def test_readlines(self): + pair = lambda: self.tp(self.BytesIO(b"abc\ndef\nh"), self.MockRawIO()) + self.assertEqual(pair().readlines(), [b"abc\n", b"def\n", b"h"]) + self.assertEqual(pair().readlines(), [b"abc\n", b"def\n", b"h"]) + self.assertEqual(pair().readlines(5), [b"abc\n", b"def\n"]) + + def test_read1(self): + # .read1() is delegated to the underlying reader object, so this test + # can be shallow. + pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO()) + + self.assertEqual(pair.read1(3), b"abc") + self.assertEqual(pair.read1(), b"def") + + def test_readinto(self): + for method in ("readinto", "readinto1"): + with self.subTest(method): + pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO()) + + data = byteslike(b'\0' * 5) + self.assertEqual(getattr(pair, method)(data), 5) + self.assertEqual(bytes(data), b"abcde") + + # gh-138720: C BufferedRWPair would destruct in a bad order resulting in + # an unraisable exception. + support.gc_collect() + + def test_write(self): + w = self.MockRawIO() + pair = self.tp(self.MockRawIO(), w) + + pair.write(b"abc") + pair.flush() + buffer = bytearray(b"def") + pair.write(buffer) + buffer[:] = b"***" # Overwrite our copy of the data + pair.flush() + self.assertEqual(w._write_stack, [b"abc", b"def"]) + + def test_peek(self): + pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO()) + + self.assertStartsWith(pair.peek(3), b"abc") + self.assertEqual(pair.read(3), b"abc") + + def test_readable(self): + pair = self.tp(self.MockRawIO(), self.MockRawIO()) + self.assertTrue(pair.readable()) + + def test_writeable(self): + pair = self.tp(self.MockRawIO(), self.MockRawIO()) + self.assertTrue(pair.writable()) + + def test_seekable(self): + # BufferedRWPairs are never seekable, even if their readers and writers + # are. + pair = self.tp(self.MockRawIO(), self.MockRawIO()) + self.assertFalse(pair.seekable()) + + # .flush() is delegated to the underlying writer object and has been + # tested in the test_write method. + + def test_close_and_closed(self): + pair = self.tp(self.MockRawIO(), self.MockRawIO()) + self.assertFalse(pair.closed) + pair.close() + self.assertTrue(pair.closed) + + def test_reader_close_error_on_close(self): + def reader_close(): + reader_non_existing + reader = self.MockRawIO() + reader.close = reader_close + writer = self.MockRawIO() + pair = self.tp(reader, writer) + with self.assertRaises(NameError) as err: + pair.close() + self.assertIn('reader_non_existing', str(err.exception)) + self.assertTrue(pair.closed) + self.assertFalse(reader.closed) + self.assertTrue(writer.closed) + + # Silence destructor error + reader.close = lambda: None + + def test_writer_close_error_on_close(self): + def writer_close(): + writer_non_existing + reader = self.MockRawIO() + writer = self.MockRawIO() + writer.close = writer_close + pair = self.tp(reader, writer) + with self.assertRaises(NameError) as err: + pair.close() + self.assertIn('writer_non_existing', str(err.exception)) + self.assertFalse(pair.closed) + self.assertTrue(reader.closed) + self.assertFalse(writer.closed) + + # Silence destructor error + writer.close = lambda: None + writer = None + + # Ignore BufferedWriter (of the BufferedRWPair) unraisable exception + with support.catch_unraisable_exception(): + # Ignore BufferedRWPair unraisable exception + with support.catch_unraisable_exception(): + pair = None + support.gc_collect() + support.gc_collect() + + def test_reader_writer_close_error_on_close(self): + def reader_close(): + reader_non_existing + def writer_close(): + writer_non_existing + reader = self.MockRawIO() + reader.close = reader_close + writer = self.MockRawIO() + writer.close = writer_close + pair = self.tp(reader, writer) + with self.assertRaises(NameError) as err: + pair.close() + self.assertIn('reader_non_existing', str(err.exception)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertIn('writer_non_existing', str(err.exception.__context__)) + self.assertFalse(pair.closed) + self.assertFalse(reader.closed) + self.assertFalse(writer.closed) + + # Silence destructor error + reader.close = lambda: None + writer.close = lambda: None + + def test_isatty(self): + class SelectableIsAtty(self.MockRawIO): + def __init__(self, isatty): + super().__init__() + self._isatty = isatty + + def isatty(self): + return self._isatty + + pair = self.tp(SelectableIsAtty(False), SelectableIsAtty(False)) + self.assertFalse(pair.isatty()) + + pair = self.tp(SelectableIsAtty(True), SelectableIsAtty(False)) + self.assertTrue(pair.isatty()) + + pair = self.tp(SelectableIsAtty(False), SelectableIsAtty(True)) + self.assertTrue(pair.isatty()) + + pair = self.tp(SelectableIsAtty(True), SelectableIsAtty(True)) + self.assertTrue(pair.isatty()) + + def test_weakref_clearing(self): + brw = self.tp(self.MockRawIO(), self.MockRawIO()) + ref = weakref.ref(brw) + brw = None + ref = None # Shouldn't segfault. + +class CBufferedRWPairTest(BufferedRWPairTest, CTestCase): + tp = io.BufferedRWPair + +class PyBufferedRWPairTest(BufferedRWPairTest, PyTestCase): + tp = pyio.BufferedRWPair + + +class BufferedRandomTest(BufferedReaderTest, BufferedWriterTest): + read_mode = "rb+" + write_mode = "wb+" + + def test_constructor(self): + BufferedReaderTest.test_constructor(self) + BufferedWriterTest.test_constructor(self) + + def test_uninitialized(self): + BufferedReaderTest.test_uninitialized(self) + BufferedWriterTest.test_uninitialized(self) + + def test_read_and_write(self): + raw = self.MockRawIO((b"asdf", b"ghjk")) + rw = self.tp(raw, 8) + + self.assertEqual(b"as", rw.read(2)) + rw.write(b"ddd") + rw.write(b"eee") + self.assertFalse(raw._write_stack) # Buffer writes + self.assertEqual(b"ghjk", rw.read()) + self.assertEqual(b"dddeee", raw._write_stack[0]) + + def test_seek_and_tell(self): + raw = self.BytesIO(b"asdfghjkl") + rw = self.tp(raw) + + self.assertEqual(b"as", rw.read(2)) + self.assertEqual(2, rw.tell()) + rw.seek(0, 0) + self.assertEqual(b"asdf", rw.read(4)) + + rw.write(b"123f") + rw.seek(0, 0) + self.assertEqual(b"asdf123fl", rw.read()) + self.assertEqual(9, rw.tell()) + rw.seek(-4, 2) + self.assertEqual(5, rw.tell()) + rw.seek(2, 1) + self.assertEqual(7, rw.tell()) + self.assertEqual(b"fl", rw.read(11)) + rw.flush() + self.assertEqual(b"asdf123fl", raw.getvalue()) + + self.assertRaises(TypeError, rw.seek, 0.0) + + def check_flush_and_read(self, read_func): + raw = self.BytesIO(b"abcdefghi") + bufio = self.tp(raw) + + self.assertEqual(b"ab", read_func(bufio, 2)) + bufio.write(b"12") + self.assertEqual(b"ef", read_func(bufio, 2)) + self.assertEqual(6, bufio.tell()) + bufio.flush() + self.assertEqual(6, bufio.tell()) + self.assertEqual(b"ghi", read_func(bufio)) + raw.seek(0, 0) + raw.write(b"XYZ") + # flush() resets the read buffer + bufio.flush() + bufio.seek(0, 0) + self.assertEqual(b"XYZ", read_func(bufio, 3)) + + def test_flush_and_read(self): + self.check_flush_and_read(lambda bufio, *args: bufio.read(*args)) + + def test_flush_and_readinto(self): + def _readinto(bufio, n=-1): + b = bytearray(n if n >= 0 else 9999) + n = bufio.readinto(b) + b.resize(n) + return b.take_bytes() + self.check_flush_and_read(_readinto) + + def test_flush_and_peek(self): + def _peek(bufio, n=-1): + # This relies on the fact that the buffer can contain the whole + # raw stream, otherwise peek() can return less. + b = bufio.peek(n) + if n != -1: + b = b[:n] + bufio.seek(len(b), 1) + return b + self.check_flush_and_read(_peek) + + def test_flush_and_write(self): + raw = self.BytesIO(b"abcdefghi") + bufio = self.tp(raw) + + bufio.write(b"123") + bufio.flush() + bufio.write(b"45") + bufio.flush() + bufio.seek(0, 0) + self.assertEqual(b"12345fghi", raw.getvalue()) + self.assertEqual(b"12345fghi", bufio.read()) + + def test_threads(self): + BufferedReaderTest.test_threads(self) + BufferedWriterTest.test_threads(self) + + def test_writes_and_peek(self): + def _peek(bufio): + bufio.peek(1) + self.check_writes(_peek) + def _peek(bufio): + pos = bufio.tell() + bufio.seek(-1, 1) + bufio.peek(1) + bufio.seek(pos, 0) + self.check_writes(_peek) + + def test_writes_and_reads(self): + def _read(bufio): + bufio.seek(-1, 1) + bufio.read(1) + self.check_writes(_read) + + def test_writes_and_read1s(self): + def _read1(bufio): + bufio.seek(-1, 1) + bufio.read1(1) + self.check_writes(_read1) + + def test_writes_and_readintos(self): + def _read(bufio): + bufio.seek(-1, 1) + bufio.readinto(bytearray(1)) + self.check_writes(_read) + + def test_write_after_readahead(self): + # Issue #6629: writing after the buffer was filled by readahead should + # first rewind the raw stream. + for overwrite_size in [1, 5]: + raw = self.BytesIO(b"A" * 10) + bufio = self.tp(raw, 4) + # Trigger readahead + self.assertEqual(bufio.read(1), b"A") + self.assertEqual(bufio.tell(), 1) + # Overwriting should rewind the raw stream if it needs so + bufio.write(b"B" * overwrite_size) + self.assertEqual(bufio.tell(), overwrite_size + 1) + # If the write size was smaller than the buffer size, flush() and + # check that rewind happens. + bufio.flush() + self.assertEqual(bufio.tell(), overwrite_size + 1) + s = raw.getvalue() + self.assertEqual(s, + b"A" + b"B" * overwrite_size + b"A" * (9 - overwrite_size)) + + def test_write_rewind_write(self): + # Various combinations of reading / writing / seeking backwards / writing again + def mutate(bufio, pos1, pos2): + assert pos2 >= pos1 + # Fill the buffer + bufio.seek(pos1) + bufio.read(pos2 - pos1) + bufio.write(b'\x02') + # This writes earlier than the previous write, but still inside + # the buffer. + bufio.seek(pos1) + bufio.write(b'\x01') + + b = b"\x80\x81\x82\x83\x84" + for i in range(0, len(b)): + for j in range(i, len(b)): + raw = self.BytesIO(b) + bufio = self.tp(raw, 100) + mutate(bufio, i, j) + bufio.flush() + expected = bytearray(b) + expected[j] = 2 + expected[i] = 1 + self.assertEqual(raw.getvalue(), expected, + "failed result for i=%d, j=%d" % (i, j)) + + def test_truncate_after_read_or_write(self): + raw = self.BytesIO(b"A" * 10) + bufio = self.tp(raw, 100) + self.assertEqual(bufio.read(2), b"AA") # the read buffer gets filled + self.assertEqual(bufio.truncate(), 2) + self.assertEqual(bufio.write(b"BB"), 2) # the write buffer increases + self.assertEqual(bufio.truncate(), 4) + + def test_misbehaved_io(self): + BufferedReaderTest.test_misbehaved_io(self) + BufferedWriterTest.test_misbehaved_io(self) + + def test_interleaved_read_write(self): + # Test for issue #12213 + with self.BytesIO(b'abcdefgh') as raw: + with self.tp(raw, 100) as f: + f.write(b"1") + self.assertEqual(f.read(1), b'b') + f.write(b'2') + self.assertEqual(f.read1(1), b'd') + f.write(b'3') + buf = bytearray(1) + f.readinto(buf) + self.assertEqual(buf, b'f') + f.write(b'4') + self.assertEqual(f.peek(1), b'h') + f.flush() + self.assertEqual(raw.getvalue(), b'1b2d3f4h') + + with self.BytesIO(b'abc') as raw: + with self.tp(raw, 100) as f: + self.assertEqual(f.read(1), b'a') + f.write(b"2") + self.assertEqual(f.read(1), b'c') + f.flush() + self.assertEqual(raw.getvalue(), b'a2c') + + def test_read1_after_write(self): + with self.BytesIO(b'abcdef') as raw: + with self.tp(raw, 3) as f: + f.write(b"1") + self.assertEqual(f.read1(1), b'b') + f.flush() + self.assertEqual(raw.getvalue(), b'1bcdef') + with self.BytesIO(b'abcdef') as raw: + with self.tp(raw, 3) as f: + f.write(b"1") + self.assertEqual(f.read1(), b'bcd') + f.flush() + self.assertEqual(raw.getvalue(), b'1bcdef') + with self.BytesIO(b'abcdef') as raw: + with self.tp(raw, 3) as f: + f.write(b"1") + # XXX: read(100) returns different numbers of bytes + # in Python and C implementations. + self.assertEqual(f.read1(100)[:3], b'bcd') + f.flush() + self.assertEqual(raw.getvalue(), b'1bcdef') + + def test_interleaved_readline_write(self): + with self.BytesIO(b'ab\ncdef\ng\n') as raw: + with self.tp(raw) as f: + f.write(b'1') + self.assertEqual(f.readline(), b'b\n') + f.write(b'2') + self.assertEqual(f.readline(), b'def\n') + f.write(b'3') + self.assertEqual(f.readline(), b'\n') + f.flush() + self.assertEqual(raw.getvalue(), b'1b\n2def\n3\n') + + # You can't construct a BufferedRandom over a non-seekable stream. + test_unseekable = None + + # writable() returns True, so there's no point to test it over + # a writable stream. + test_truncate_on_read_only = None + + +class CBufferedRandomTest(BufferedRandomTest, SizeofTest, CTestCase): + tp = io.BufferedRandom + + def test_garbage_collection(self): + CBufferedReaderTest.test_garbage_collection(self) + CBufferedWriterTest.test_garbage_collection(self) + + def test_args_error(self): + # Issue #17275 + with self.assertRaisesRegex(TypeError, "BufferedRandom"): + self.tp(self.BytesIO(), 1024, 1024, 1024) + + +class PyBufferedRandomTest(BufferedRandomTest, PyTestCase): + tp = pyio.BufferedRandom + + +# Simple test to ensure that optimizations in the IO library deliver the +# expected results. For best testing, run this under a debug-build Python too +# (to exercise asserts in the C code). + +lengths = list(range(1, 257)) + [512, 1000, 1024, 2048, 4096, 8192, 10000, + 16384, 32768, 65536, 1000000] + +class BufferSizeTest: + def try_one(self, s): + # Write s + "\n" + s to file, then open it and ensure that successive + # .readline()s deliver what we wrote. + + # Ensure we can open TESTFN for writing. + os_helper.unlink(os_helper.TESTFN) + + # Since C doesn't guarantee we can write/read arbitrary bytes in text + # files, use binary mode. + f = self.open(os_helper.TESTFN, "wb") + try: + # write once with \n and once without + f.write(s) + f.write(b"\n") + f.write(s) + f.close() + f = self.open(os_helper.TESTFN, "rb") + line = f.readline() + self.assertEqual(line, s + b"\n") + line = f.readline() + self.assertEqual(line, s) + line = f.readline() + self.assertFalse(line) # Must be at EOF + f.close() + finally: + os_helper.unlink(os_helper.TESTFN) + + def drive_one(self, pattern): + for length in lengths: + # Repeat string 'pattern' as often as needed to reach total length + # 'length'. Then call try_one with that string, a string one larger + # than that, and a string one smaller than that. Try this with all + # small sizes and various powers of 2, so we exercise all likely + # stdio buffer sizes, and "off by one" errors on both sides. + q, r = divmod(length, len(pattern)) + teststring = pattern * q + pattern[:r] + self.assertEqual(len(teststring), length) + self.try_one(teststring) + self.try_one(teststring + b"x") + self.try_one(teststring[:-1]) + + def test_primepat(self): + # A pattern with prime length, to avoid simple relationships with + # stdio buffer sizes. + self.drive_one(b"1234567890\00\01\02\03\04\05\06") + + def test_nullpat(self): + self.drive_one(b'\0' * 1000) + + +class CBufferSizeTest(BufferSizeTest, unittest.TestCase): + open = io.open + +class PyBufferSizeTest(BufferSizeTest, unittest.TestCase): + open = staticmethod(pyio.open) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_file.py b/Lib/test/test_io/test_file.py similarity index 100% rename from Lib/test/test_file.py rename to Lib/test/test_io/test_file.py diff --git a/Lib/test/test_fileio.py b/Lib/test/test_io/test_fileio.py similarity index 99% rename from Lib/test/test_fileio.py rename to Lib/test/test_io/test_fileio.py index e3d54f6315aade..e53c4749f58cf2 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_io/test_fileio.py @@ -567,8 +567,8 @@ def testModeStrings(self): # test that the mode attribute is correct for various mode strings # given as init args try: - for modes in [('w', 'wb'), ('wb', 'wb'), ('wb+', 'rb+'), - ('w+b', 'rb+'), ('a', 'ab'), ('ab', 'ab'), + for modes in [('w', 'wb'), ('wb', 'wb'), ('wb+', 'wb+'), + ('w+b', 'wb+'), ('a', 'ab'), ('ab', 'ab'), ('ab+', 'ab+'), ('a+b', 'ab+'), ('r', 'rb'), ('rb', 'rb'), ('rb+', 'rb+'), ('r+b', 'rb+')]: # read modes are last so that TESTFN will exist first diff --git a/Lib/test/test_io/test_general.py b/Lib/test/test_io/test_general.py new file mode 100644 index 00000000000000..085ed3ea6a95ee --- /dev/null +++ b/Lib/test/test_io/test_general.py @@ -0,0 +1,1431 @@ +"""General tests for the io module. + +New tests should go in more specific modules; see test_io/__init__.py +""" + +import abc +import array +import errno +import os +import pickle +import sys +import textwrap +import threading +import unittest +import warnings +import weakref +from test import support +from test.support.script_helper import ( + assert_python_ok, assert_python_failure, run_python_until_end) +from test.support import ( + import_helper, is_apple, os_helper, threading_helper, warnings_helper, +) +from test.support.os_helper import FakePath +from .utils import byteslike, CTestCase, PyTestCase + +import io # C implementation of io +import _pyio as pyio # Python implementation of io + + +class IOTest: + + def setUp(self): + os_helper.unlink(os_helper.TESTFN) + + def tearDown(self): + os_helper.unlink(os_helper.TESTFN) + + def write_ops(self, f): + self.assertEqual(f.write(b"blah."), 5) + f.truncate(0) + self.assertEqual(f.tell(), 5) + f.seek(0) + + self.assertEqual(f.write(b"blah."), 5) + self.assertEqual(f.seek(0), 0) + self.assertEqual(f.write(b"Hello."), 6) + self.assertEqual(f.tell(), 6) + self.assertEqual(f.seek(-1, 1), 5) + self.assertEqual(f.tell(), 5) + buffer = bytearray(b" world\n\n\n") + self.assertEqual(f.write(buffer), 9) + buffer[:] = b"*" * 9 # Overwrite our copy of the data + self.assertEqual(f.seek(0), 0) + self.assertEqual(f.write(b"h"), 1) + self.assertEqual(f.seek(-1, 2), 13) + self.assertEqual(f.tell(), 13) + + self.assertEqual(f.truncate(12), 12) + self.assertEqual(f.tell(), 13) + self.assertRaises(TypeError, f.seek, 0.0) + + def read_ops(self, f, buffered=False): + data = f.read(5) + self.assertEqual(data, b"hello") + data = byteslike(data) + self.assertEqual(f.readinto(data), 5) + self.assertEqual(bytes(data), b" worl") + data = bytearray(5) + self.assertEqual(f.readinto(data), 2) + self.assertEqual(len(data), 5) + self.assertEqual(data[:2], b"d\n") + self.assertEqual(f.seek(0), 0) + self.assertEqual(f.read(20), b"hello world\n") + self.assertEqual(f.read(1), b"") + self.assertEqual(f.readinto(byteslike(b"x")), 0) + self.assertEqual(f.seek(-6, 2), 6) + self.assertEqual(f.read(5), b"world") + self.assertEqual(f.read(0), b"") + self.assertEqual(f.readinto(byteslike()), 0) + self.assertEqual(f.seek(-6, 1), 5) + self.assertEqual(f.read(5), b" worl") + self.assertEqual(f.tell(), 10) + self.assertRaises(TypeError, f.seek, 0.0) + if buffered: + f.seek(0) + self.assertEqual(f.read(), b"hello world\n") + f.seek(6) + self.assertEqual(f.read(), b"world\n") + self.assertEqual(f.read(), b"") + f.seek(0) + data = byteslike(5) + self.assertEqual(f.readinto1(data), 5) + self.assertEqual(bytes(data), b"hello") + + LARGE = 2**31 + + def large_file_ops(self, f): + assert f.readable() + assert f.writable() + try: + self.assertEqual(f.seek(self.LARGE), self.LARGE) + except (OverflowError, ValueError): + self.skipTest("no largefile support") + self.assertEqual(f.tell(), self.LARGE) + self.assertEqual(f.write(b"xxx"), 3) + self.assertEqual(f.tell(), self.LARGE + 3) + self.assertEqual(f.seek(-1, 1), self.LARGE + 2) + self.assertEqual(f.truncate(), self.LARGE + 2) + self.assertEqual(f.tell(), self.LARGE + 2) + self.assertEqual(f.seek(0, 2), self.LARGE + 2) + self.assertEqual(f.truncate(self.LARGE + 1), self.LARGE + 1) + self.assertEqual(f.tell(), self.LARGE + 2) + self.assertEqual(f.seek(0, 2), self.LARGE + 1) + self.assertEqual(f.seek(-1, 2), self.LARGE) + self.assertEqual(f.read(2), b"x") + + def test_invalid_operations(self): + # Try writing on a file opened in read mode and vice-versa. + exc = self.UnsupportedOperation + with self.open(os_helper.TESTFN, "w", encoding="utf-8") as fp: + self.assertRaises(exc, fp.read) + self.assertRaises(exc, fp.readline) + with self.open(os_helper.TESTFN, "wb") as fp: + self.assertRaises(exc, fp.read) + self.assertRaises(exc, fp.readline) + with self.open(os_helper.TESTFN, "wb", buffering=0) as fp: + self.assertRaises(exc, fp.read) + self.assertRaises(exc, fp.readall) + self.assertRaises(exc, fp.readline) + with self.open(os_helper.TESTFN, "rb", buffering=0) as fp: + self.assertRaises(exc, fp.write, b"blah") + self.assertRaises(exc, fp.writelines, [b"blah\n"]) + with self.open(os_helper.TESTFN, "rb") as fp: + self.assertRaises(exc, fp.write, b"blah") + self.assertRaises(exc, fp.writelines, [b"blah\n"]) + with self.open(os_helper.TESTFN, "r", encoding="utf-8") as fp: + self.assertRaises(exc, fp.write, "blah") + self.assertRaises(exc, fp.writelines, ["blah\n"]) + # Non-zero seeking from current or end pos + self.assertRaises(exc, fp.seek, 1, self.SEEK_CUR) + self.assertRaises(exc, fp.seek, -1, self.SEEK_END) + + @support.cpython_only + def test_startup_optimization(self): + # gh-132952: Test that `io` is not imported at startup and that the + # __module__ of UnsupportedOperation is set to "io". + assert_python_ok("-S", "-c", textwrap.dedent( + """ + import sys + assert "io" not in sys.modules + try: + sys.stdin.truncate() + except Exception as e: + typ = type(e) + assert typ.__module__ == "io", (typ, typ.__module__) + assert typ.__name__ == "UnsupportedOperation", (typ, typ.__name__) + else: + raise AssertionError("Expected UnsupportedOperation") + """ + )) + + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_optional_abilities(self): + # Test for OSError when optional APIs are not supported + # The purpose of this test is to try fileno(), reading, writing and + # seeking operations with various objects that indicate they do not + # support these operations. + + def pipe_reader(): + [r, w] = os.pipe() + os.close(w) # So that read() is harmless + return self.FileIO(r, "r") + + def pipe_writer(): + [r, w] = os.pipe() + self.addCleanup(os.close, r) + # Guarantee that we can write into the pipe without blocking + thread = threading.Thread(target=os.read, args=(r, 100)) + thread.start() + self.addCleanup(thread.join) + return self.FileIO(w, "w") + + def buffered_reader(): + return self.BufferedReader(self.MockUnseekableIO()) + + def buffered_writer(): + return self.BufferedWriter(self.MockUnseekableIO()) + + def buffered_random(): + return self.BufferedRandom(self.BytesIO()) + + def buffered_rw_pair(): + return self.BufferedRWPair(self.MockUnseekableIO(), + self.MockUnseekableIO()) + + def text_reader(): + class UnseekableReader(self.MockUnseekableIO): + writable = self.BufferedIOBase.writable + write = self.BufferedIOBase.write + return self.TextIOWrapper(UnseekableReader(), "ascii") + + def text_writer(): + class UnseekableWriter(self.MockUnseekableIO): + readable = self.BufferedIOBase.readable + read = self.BufferedIOBase.read + return self.TextIOWrapper(UnseekableWriter(), "ascii") + + tests = ( + (pipe_reader, "fr"), (pipe_writer, "fw"), + (buffered_reader, "r"), (buffered_writer, "w"), + (buffered_random, "rws"), (buffered_rw_pair, "rw"), + (text_reader, "r"), (text_writer, "w"), + (self.BytesIO, "rws"), (self.StringIO, "rws"), + ) + + def do_test(test, obj, abilities): + readable = "r" in abilities + self.assertEqual(obj.readable(), readable) + writable = "w" in abilities + self.assertEqual(obj.writable(), writable) + + if isinstance(obj, self.TextIOBase): + data = "3" + elif isinstance(obj, (self.BufferedIOBase, self.RawIOBase)): + data = b"3" + else: + self.fail("Unknown base class") + + if "f" in abilities: + obj.fileno() + else: + self.assertRaises(OSError, obj.fileno) + + if readable: + obj.read(1) + obj.read() + else: + self.assertRaises(OSError, obj.read, 1) + self.assertRaises(OSError, obj.read) + + if writable: + obj.write(data) + else: + self.assertRaises(OSError, obj.write, data) + + if sys.platform.startswith("win") and test in ( + pipe_reader, pipe_writer): + # Pipes seem to appear as seekable on Windows + return + seekable = "s" in abilities + self.assertEqual(obj.seekable(), seekable) + + if seekable: + obj.tell() + obj.seek(0) + else: + self.assertRaises(OSError, obj.tell) + self.assertRaises(OSError, obj.seek, 0) + + if writable and seekable: + obj.truncate() + obj.truncate(0) + else: + self.assertRaises(OSError, obj.truncate) + self.assertRaises(OSError, obj.truncate, 0) + + for [test, abilities] in tests: + with self.subTest(test): + if test == pipe_writer and not threading_helper.can_start_thread: + self.skipTest("Need threads") + with test() as obj: + do_test(test, obj, abilities) + + + def test_open_handles_NUL_chars(self): + fn_with_NUL = 'foo\0bar' + self.assertRaises(ValueError, self.open, fn_with_NUL, 'w', encoding="utf-8") + + bytes_fn = bytes(fn_with_NUL, 'ascii') + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + self.assertRaises(ValueError, self.open, bytes_fn, 'w', encoding="utf-8") + + def test_raw_file_io(self): + with self.open(os_helper.TESTFN, "wb", buffering=0) as f: + self.assertEqual(f.readable(), False) + self.assertEqual(f.writable(), True) + self.assertEqual(f.seekable(), True) + self.write_ops(f) + with self.open(os_helper.TESTFN, "rb", buffering=0) as f: + self.assertEqual(f.readable(), True) + self.assertEqual(f.writable(), False) + self.assertEqual(f.seekable(), True) + self.read_ops(f) + + def test_buffered_file_io(self): + with self.open(os_helper.TESTFN, "wb") as f: + self.assertEqual(f.readable(), False) + self.assertEqual(f.writable(), True) + self.assertEqual(f.seekable(), True) + self.write_ops(f) + with self.open(os_helper.TESTFN, "rb") as f: + self.assertEqual(f.readable(), True) + self.assertEqual(f.writable(), False) + self.assertEqual(f.seekable(), True) + self.read_ops(f, True) + + def test_readline(self): + with self.open(os_helper.TESTFN, "wb") as f: + f.write(b"abc\ndef\nxyzzy\nfoo\x00bar\nanother line") + with self.open(os_helper.TESTFN, "rb") as f: + self.assertEqual(f.readline(), b"abc\n") + self.assertEqual(f.readline(10), b"def\n") + self.assertEqual(f.readline(2), b"xy") + self.assertEqual(f.readline(4), b"zzy\n") + self.assertEqual(f.readline(), b"foo\x00bar\n") + self.assertEqual(f.readline(None), b"another line") + self.assertRaises(TypeError, f.readline, 5.3) + with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: + self.assertRaises(TypeError, f.readline, 5.3) + + def test_readline_nonsizeable(self): + # Issue #30061 + # Crash when readline() returns an object without __len__ + class R(self.IOBase): + def readline(self): + return None + self.assertRaises((TypeError, StopIteration), next, R()) + + def test_next_nonsizeable(self): + # Issue #30061 + # Crash when __next__() returns an object without __len__ + class R(self.IOBase): + def __next__(self): + return None + self.assertRaises(TypeError, R().readlines, 1) + + def test_raw_bytes_io(self): + f = self.BytesIO() + self.write_ops(f) + data = f.getvalue() + self.assertEqual(data, b"hello world\n") + f = self.BytesIO(data) + self.read_ops(f, True) + + def test_large_file_ops(self): + # On Windows and Apple platforms this test consumes large resources; It + # takes a long time to build the >2 GiB file and takes >2 GiB of disk + # space therefore the resource must be enabled to run this test. + if sys.platform[:3] == 'win' or is_apple: + support.requires( + 'largefile', + 'test requires %s bytes and a long time to run' % self.LARGE) + with self.open(os_helper.TESTFN, "w+b", 0) as f: + self.large_file_ops(f) + with self.open(os_helper.TESTFN, "w+b") as f: + self.large_file_ops(f) + + def test_with_open(self): + for bufsize in (0, 100): + with self.open(os_helper.TESTFN, "wb", bufsize) as f: + f.write(b"xxx") + self.assertEqual(f.closed, True) + try: + with self.open(os_helper.TESTFN, "wb", bufsize) as f: + 1/0 + except ZeroDivisionError: + self.assertEqual(f.closed, True) + else: + self.fail("1/0 didn't raise an exception") + + # issue 5008 + def test_append_mode_tell(self): + with self.open(os_helper.TESTFN, "wb") as f: + f.write(b"xxx") + with self.open(os_helper.TESTFN, "ab", buffering=0) as f: + self.assertEqual(f.tell(), 3) + with self.open(os_helper.TESTFN, "ab") as f: + self.assertEqual(f.tell(), 3) + with self.open(os_helper.TESTFN, "a", encoding="utf-8") as f: + self.assertGreater(f.tell(), 0) + + def test_destructor(self): + record = [] + class MyFileIO(self.FileIO): + def __del__(self): + record.append(1) + try: + f = super().__del__ + except AttributeError: + pass + else: + f() + def close(self): + record.append(2) + super().close() + def flush(self): + record.append(3) + super().flush() + with warnings_helper.check_warnings(('', ResourceWarning)): + f = MyFileIO(os_helper.TESTFN, "wb") + f.write(b"xxx") + del f + support.gc_collect() + self.assertEqual(record, [1, 2, 3]) + with self.open(os_helper.TESTFN, "rb") as f: + self.assertEqual(f.read(), b"xxx") + + def _check_base_destructor(self, base): + record = [] + class MyIO(base): + def __init__(self): + # This exercises the availability of attributes on object + # destruction. + # (in the C version, close() is called by the tp_dealloc + # function, not by __del__) + self.on_del = 1 + self.on_close = 2 + self.on_flush = 3 + def __del__(self): + record.append(self.on_del) + try: + f = super().__del__ + except AttributeError: + pass + else: + f() + def close(self): + record.append(self.on_close) + super().close() + def flush(self): + record.append(self.on_flush) + super().flush() + f = MyIO() + del f + support.gc_collect() + self.assertEqual(record, [1, 2, 3]) + + def test_IOBase_destructor(self): + self._check_base_destructor(self.IOBase) + + def test_RawIOBase_destructor(self): + self._check_base_destructor(self.RawIOBase) + + def test_BufferedIOBase_destructor(self): + self._check_base_destructor(self.BufferedIOBase) + + def test_TextIOBase_destructor(self): + self._check_base_destructor(self.TextIOBase) + + def test_close_flushes(self): + with self.open(os_helper.TESTFN, "wb") as f: + f.write(b"xxx") + with self.open(os_helper.TESTFN, "rb") as f: + self.assertEqual(f.read(), b"xxx") + + def test_array_writes(self): + a = array.array('i', range(10)) + n = len(a.tobytes()) + def check(f): + with f: + self.assertEqual(f.write(a), n) + f.writelines((a,)) + check(self.BytesIO()) + check(self.FileIO(os_helper.TESTFN, "w")) + check(self.BufferedWriter(self.MockRawIO())) + check(self.BufferedRandom(self.MockRawIO())) + check(self.BufferedRWPair(self.MockRawIO(), self.MockRawIO())) + + def test_closefd(self): + self.assertRaises(ValueError, self.open, os_helper.TESTFN, 'w', + encoding="utf-8", closefd=False) + + def test_read_closed(self): + with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f: + f.write("egg\n") + with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: + file = self.open(f.fileno(), "r", encoding="utf-8", closefd=False) + self.assertEqual(file.read(), "egg\n") + file.seek(0) + file.close() + self.assertRaises(ValueError, file.read) + with self.open(os_helper.TESTFN, "rb") as f: + file = self.open(f.fileno(), "rb", closefd=False) + self.assertEqual(file.read()[:3], b"egg") + file.close() + self.assertRaises(ValueError, file.readinto, bytearray(1)) + + def test_no_closefd_with_filename(self): + # can't use closefd in combination with a file name + self.assertRaises(ValueError, self.open, os_helper.TESTFN, "r", + encoding="utf-8", closefd=False) + + def test_closefd_attr(self): + with self.open(os_helper.TESTFN, "wb") as f: + f.write(b"egg\n") + with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: + self.assertEqual(f.buffer.raw.closefd, True) + file = self.open(f.fileno(), "r", encoding="utf-8", closefd=False) + self.assertEqual(file.buffer.raw.closefd, False) + + def test_garbage_collection(self): + # FileIO objects are collected, and collecting them flushes + # all data to disk. + # + # Note that using warnings_helper.check_warnings() will keep the + # file alive due to the `source` argument to warn(). So, use + # catch_warnings() instead. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ResourceWarning) + f = self.FileIO(os_helper.TESTFN, "wb") + f.write(b"abcxxx") + f.f = f + wr = weakref.ref(f) + del f + support.gc_collect() + self.assertIsNone(wr(), wr) + with self.open(os_helper.TESTFN, "rb") as f: + self.assertEqual(f.read(), b"abcxxx") + + def test_unbounded_file(self): + # Issue #1174606: reading from an unbounded stream such as /dev/zero. + zero = "/dev/zero" + if not os.path.exists(zero): + self.skipTest("{0} does not exist".format(zero)) + if sys.maxsize > 0x7FFFFFFF: + self.skipTest("test can only run in a 32-bit address space") + if support.real_max_memuse < support._2G: + self.skipTest("test requires at least 2 GiB of memory") + with self.open(zero, "rb", buffering=0) as f: + self.assertRaises(OverflowError, f.read) + with self.open(zero, "rb") as f: + self.assertRaises(OverflowError, f.read) + with self.open(zero, "r") as f: + self.assertRaises(OverflowError, f.read) + + def check_flush_error_on_close(self, *args, **kwargs): + # Test that the file is closed despite failed flush + # and that flush() is called before file closed. + f = self.open(*args, **kwargs) + closed = [] + def bad_flush(): + closed[:] = [f.closed] + raise OSError() + f.flush = bad_flush + self.assertRaises(OSError, f.close) # exception not swallowed + self.assertTrue(f.closed) + self.assertTrue(closed) # flush() called + self.assertFalse(closed[0]) # flush() called before file closed + f.flush = lambda: None # break reference loop + + def test_flush_error_on_close(self): + # raw file + # Issue #5700: io.FileIO calls flush() after file closed + self.check_flush_error_on_close(os_helper.TESTFN, 'wb', buffering=0) + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(fd, 'wb', buffering=0) + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(fd, 'wb', buffering=0, closefd=False) + os.close(fd) + # buffered io + self.check_flush_error_on_close(os_helper.TESTFN, 'wb') + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(fd, 'wb') + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(fd, 'wb', closefd=False) + os.close(fd) + # text io + self.check_flush_error_on_close(os_helper.TESTFN, 'w', encoding="utf-8") + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(fd, 'w', encoding="utf-8") + fd = os.open(os_helper.TESTFN, os.O_WRONLY|os.O_CREAT) + self.check_flush_error_on_close(fd, 'w', encoding="utf-8", closefd=False) + os.close(fd) + + def test_multi_close(self): + f = self.open(os_helper.TESTFN, "wb", buffering=0) + f.close() + f.close() + f.close() + self.assertRaises(ValueError, f.flush) + + def test_RawIOBase_read(self): + # Exercise the default limited RawIOBase.read(n) implementation (which + # calls readinto() internally). + rawio = self.MockRawIOWithoutRead((b"abc", b"d", None, b"efg", None)) + self.assertEqual(rawio.read(2), b"ab") + self.assertEqual(rawio.read(2), b"c") + self.assertEqual(rawio.read(2), b"d") + self.assertEqual(rawio.read(2), None) + self.assertEqual(rawio.read(2), b"ef") + self.assertEqual(rawio.read(2), b"g") + self.assertEqual(rawio.read(2), None) + self.assertEqual(rawio.read(2), b"") + + def test_RawIOBase_read_bounds_checking(self): + # Make sure a `.readinto` call which returns a value outside + # (0, len(buffer)) raises. + class Misbehaved(self.io.RawIOBase): + def __init__(self, readinto_return) -> None: + self._readinto_return = readinto_return + def readinto(self, b): + return self._readinto_return + + with self.assertRaises(ValueError) as cm: + Misbehaved(2).read(1) + self.assertEqual(str(cm.exception), "readinto returned 2 outside buffer size 1") + for bad_size in (2147483647, sys.maxsize, -1, -1000): + with self.assertRaises(ValueError): + Misbehaved(bad_size).read() + + def test_RawIOBase_read_gh60107(self): + # gh-60107: Ensure a "Raw I/O" which keeps a reference to the + # mutable memory doesn't allow making a mutable bytes. + class RawIOKeepsReference(self.MockRawIOWithoutRead): + def __init__(self, *args, **kwargs): + self.buf = None + super().__init__(*args, **kwargs) + + def readinto(self, buf): + # buf is the bytearray so keeping a reference to it doesn't keep + # the memory alive; a memoryview does. + self.buf = memoryview(buf) + buf[0:4] = self._read_stack.pop() + return 3 + + with self.assertRaises(BufferError): + rawio = RawIOKeepsReference([b"1234"]) + rawio.read(4) + + def test_types_have_dict(self): + test = ( + self.IOBase(), + self.RawIOBase(), + self.TextIOBase(), + self.StringIO(), + self.BytesIO() + ) + for obj in test: + self.assertHasAttr(obj, "__dict__") + + def test_opener(self): + with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f: + f.write("egg\n") + fd = os.open(os_helper.TESTFN, os.O_RDONLY) + def opener(path, flags): + return fd + with self.open("non-existent", "r", encoding="utf-8", opener=opener) as f: + self.assertEqual(f.read(), "egg\n") + + def test_bad_opener_negative_1(self): + # Issue #27066. + def badopener(fname, flags): + return -1 + with self.assertRaises(ValueError) as cm: + self.open('non-existent', 'r', opener=badopener) + self.assertEqual(str(cm.exception), 'opener returned -1') + + def test_bad_opener_other_negative(self): + # Issue #27066. + def badopener(fname, flags): + return -2 + with self.assertRaises(ValueError) as cm: + self.open('non-existent', 'r', opener=badopener) + self.assertEqual(str(cm.exception), 'opener returned -2') + + def test_opener_invalid_fd(self): + # Check that OSError is raised with error code EBADF if the + # opener returns an invalid file descriptor (see gh-82212). + fd = os_helper.make_bad_fd() + with self.assertRaises(OSError) as cm: + self.open('foo', opener=lambda name, flags: fd) + self.assertEqual(cm.exception.errno, errno.EBADF) + + def test_fileio_closefd(self): + # Issue #4841 + with self.open(__file__, 'rb') as f1, \ + self.open(__file__, 'rb') as f2: + fileio = self.FileIO(f1.fileno(), closefd=False) + # .__init__() must not close f1 + fileio.__init__(f2.fileno(), closefd=False) + f1.readline() + # .close() must not close f2 + fileio.close() + f2.readline() + + def test_nonbuffered_textio(self): + with warnings_helper.check_no_resource_warning(self): + with self.assertRaises(ValueError): + self.open(os_helper.TESTFN, 'w', encoding="utf-8", buffering=0) + + def test_invalid_newline(self): + with warnings_helper.check_no_resource_warning(self): + with self.assertRaises(ValueError): + self.open(os_helper.TESTFN, 'w', encoding="utf-8", newline='invalid') + + def test_buffered_readinto_mixin(self): + # Test the implementation provided by BufferedIOBase + class Stream(self.BufferedIOBase): + def read(self, size): + return b"12345" + read1 = read + stream = Stream() + for method in ("readinto", "readinto1"): + with self.subTest(method): + buffer = byteslike(5) + self.assertEqual(getattr(stream, method)(buffer), 5) + self.assertEqual(bytes(buffer), b"12345") + + def test_fspath_support(self): + def check_path_succeeds(path): + with self.open(path, "w", encoding="utf-8") as f: + f.write("egg\n") + + with self.open(path, "r", encoding="utf-8") as f: + self.assertEqual(f.read(), "egg\n") + + check_path_succeeds(FakePath(os_helper.TESTFN)) + check_path_succeeds(FakePath(os.fsencode(os_helper.TESTFN))) + + with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f: + bad_path = FakePath(f.fileno()) + with self.assertRaises(TypeError): + self.open(bad_path, 'w', encoding="utf-8") + + bad_path = FakePath(None) + with self.assertRaises(TypeError): + self.open(bad_path, 'w', encoding="utf-8") + + bad_path = FakePath(FloatingPointError) + with self.assertRaises(FloatingPointError): + self.open(bad_path, 'w', encoding="utf-8") + + # ensure that refcounting is correct with some error conditions + with self.assertRaisesRegex(ValueError, 'read/write/append mode'): + self.open(FakePath(os_helper.TESTFN), 'rwxa', encoding="utf-8") + + def test_RawIOBase_readall(self): + # Exercise the default unlimited RawIOBase.read() and readall() + # implementations. + rawio = self.MockRawIOWithoutRead((b"abc", b"d", b"efg")) + self.assertEqual(rawio.read(), b"abcdefg") + rawio = self.MockRawIOWithoutRead((b"abc", b"d", b"efg")) + self.assertEqual(rawio.readall(), b"abcdefg") + + def test_BufferedIOBase_readinto(self): + # Exercise the default BufferedIOBase.readinto() and readinto1() + # implementations (which call read() or read1() internally). + class Reader(self.BufferedIOBase): + def __init__(self, avail): + self.avail = avail + def read(self, size): + result = self.avail[:size] + self.avail = self.avail[size:] + return result + def read1(self, size): + """Returns no more than 5 bytes at once""" + return self.read(min(size, 5)) + tests = ( + # (test method, total data available, read buffer size, expected + # read size) + ("readinto", 10, 5, 5), + ("readinto", 10, 6, 6), # More than read1() can return + ("readinto", 5, 6, 5), # Buffer larger than total available + ("readinto", 6, 7, 6), + ("readinto", 10, 0, 0), # Empty buffer + ("readinto1", 10, 5, 5), # Result limited to single read1() call + ("readinto1", 10, 6, 5), # Buffer larger than read1() can return + ("readinto1", 5, 6, 5), # Buffer larger than total available + ("readinto1", 6, 7, 5), + ("readinto1", 10, 0, 0), # Empty buffer + ) + UNUSED_BYTE = 0x81 + for test in tests: + with self.subTest(test): + method, avail, request, result = test + reader = Reader(bytes(range(avail))) + buffer = bytearray((UNUSED_BYTE,) * request) + method = getattr(reader, method) + self.assertEqual(method(buffer), result) + self.assertEqual(len(buffer), request) + self.assertSequenceEqual(buffer[:result], range(result)) + unused = (UNUSED_BYTE,) * (request - result) + self.assertSequenceEqual(buffer[result:], unused) + self.assertEqual(len(reader.avail), avail - result) + + def test_close_assert(self): + class R(self.IOBase): + def __setattr__(self, name, value): + pass + def flush(self): + raise OSError() + f = R() + # This would cause an assertion failure. + self.assertRaises(OSError, f.close) + + # Silence destructor error + R.flush = lambda self: None + + @threading_helper.requires_working_threading() + def test_write_readline_races(self): + # gh-134908: Concurrent iteration over a file caused races + thread_count = 2 + write_count = 100 + read_count = 100 + + def writer(file, barrier): + barrier.wait() + for _ in range(write_count): + file.write("x") + + def reader(file, barrier): + barrier.wait() + for _ in range(read_count): + for line in file: + self.assertEqual(line, "") + + with self.open(os_helper.TESTFN, "w+") as f: + barrier = threading.Barrier(thread_count + 1) + reader = threading.Thread(target=reader, args=(f, barrier)) + writers = [threading.Thread(target=writer, args=(f, barrier)) + for _ in range(thread_count)] + with threading_helper.catch_threading_exception() as cm: + with threading_helper.start_threads(writers + [reader]): + pass + self.assertIsNone(cm.exc_type) + + self.assertEqual(os.stat(os_helper.TESTFN).st_size, + write_count * thread_count) + + +class CIOTest(IOTest, CTestCase): + + def test_IOBase_finalize(self): + # Issue #12149: segmentation fault on _PyIOBase_finalize when both a + # class which inherits IOBase and an object of this class are caught + # in a reference cycle and close() is already in the method cache. + class MyIO(self.IOBase): + def close(self): + pass + + # create an instance to populate the method cache + MyIO() + obj = MyIO() + obj.obj = obj + wr = weakref.ref(obj) + del MyIO + del obj + support.gc_collect() + self.assertIsNone(wr(), wr) + +@support.cpython_only +class TestIOCTypes(unittest.TestCase): + def setUp(self): + _io = import_helper.import_module("_io") + self.types = [ + _io.BufferedRWPair, + _io.BufferedRandom, + _io.BufferedReader, + _io.BufferedWriter, + _io.BytesIO, + _io.FileIO, + _io.IncrementalNewlineDecoder, + _io.StringIO, + _io.TextIOWrapper, + _io._BufferedIOBase, + _io._BytesIOBuffer, + _io._IOBase, + _io._RawIOBase, + _io._TextIOBase, + ] + if sys.platform == "win32": + self.types.append(_io._WindowsConsoleIO) + self._io = _io + + def test_immutable_types(self): + for tp in self.types: + with self.subTest(tp=tp): + with self.assertRaisesRegex(TypeError, "immutable"): + tp.foo = "bar" + + def test_class_hierarchy(self): + def check_subs(types, base): + for tp in types: + with self.subTest(tp=tp, base=base): + self.assertIsSubclass(tp, base) + + def recursive_check(d): + for k, v in d.items(): + if isinstance(v, dict): + recursive_check(v) + elif isinstance(v, set): + check_subs(v, k) + else: + self.fail("corrupt test dataset") + + _io = self._io + hierarchy = { + _io._IOBase: { + _io._BufferedIOBase: { + _io.BufferedRWPair, + _io.BufferedRandom, + _io.BufferedReader, + _io.BufferedWriter, + _io.BytesIO, + }, + _io._RawIOBase: { + _io.FileIO, + }, + _io._TextIOBase: { + _io.StringIO, + _io.TextIOWrapper, + }, + }, + } + if sys.platform == "win32": + hierarchy[_io._IOBase][_io._RawIOBase].add(_io._WindowsConsoleIO) + + recursive_check(hierarchy) + + def test_subclassing(self): + _io = self._io + dataset = {k: True for k in self.types} + dataset[_io._BytesIOBuffer] = False + + for tp, is_basetype in dataset.items(): + with self.subTest(tp=tp, is_basetype=is_basetype): + name = f"{tp.__name__}_subclass" + bases = (tp,) + if is_basetype: + _ = type(name, bases, {}) + else: + msg = "not an acceptable base type" + with self.assertRaisesRegex(TypeError, msg): + _ = type(name, bases, {}) + + def test_disallow_instantiation(self): + _io = self._io + support.check_disallow_instantiation(self, _io._BytesIOBuffer) + + def test_stringio_setstate(self): + # gh-127182: Calling __setstate__() with invalid arguments must not crash + obj = self._io.StringIO() + with self.assertRaisesRegex( + TypeError, + 'initial_value must be str or None, not int', + ): + obj.__setstate__((1, '', 0, {})) + + obj.__setstate__((None, '', 0, {})) # should not crash + self.assertEqual(obj.getvalue(), '') + + obj.__setstate__(('', '', 0, {})) + self.assertEqual(obj.getvalue(), '') + +class PyIOTest(IOTest, PyTestCase): + pass + + +@support.cpython_only +class APIMismatchTest(unittest.TestCase): + + def test_RawIOBase_io_in_pyio_match(self): + """Test that pyio RawIOBase class has all c RawIOBase methods""" + mismatch = support.detect_api_mismatch(pyio.RawIOBase, io.RawIOBase, + ignore=('__weakref__', '__static_attributes__')) + self.assertEqual(mismatch, set(), msg='Python RawIOBase does not have all C RawIOBase methods') + + def test_RawIOBase_pyio_in_io_match(self): + """Test that c RawIOBase class has all pyio RawIOBase methods""" + mismatch = support.detect_api_mismatch(io.RawIOBase, pyio.RawIOBase) + self.assertEqual(mismatch, set(), msg='C RawIOBase does not have all Python RawIOBase methods') + + +# XXX Tests for open() + +class MiscIOTest: + + # for test__all__, actual values are set in subclasses + name_of_module = None + extra_exported = () + not_exported = () + + def tearDown(self): + os_helper.unlink(os_helper.TESTFN) + + def test___all__(self): + support.check__all__(self, self.io, self.name_of_module, + extra=self.extra_exported, + not_exported=self.not_exported) + + def test_attributes(self): + f = self.open(os_helper.TESTFN, "wb", buffering=0) + self.assertEqual(f.mode, "wb") + f.close() + + f = self.open(os_helper.TESTFN, "w+", encoding="utf-8") + self.assertEqual(f.mode, "w+") + self.assertEqual(f.buffer.mode, "wb+") + self.assertEqual(f.buffer.raw.mode, "wb+") + + g = self.open(f.fileno(), "wb", closefd=False) + self.assertEqual(g.mode, "wb") + self.assertEqual(g.raw.mode, "wb") + self.assertEqual(g.name, f.fileno()) + self.assertEqual(g.raw.name, f.fileno()) + f.close() + g.close() + + def test_removed_u_mode(self): + # bpo-37330: The "U" mode has been removed in Python 3.11 + for mode in ("U", "rU", "r+U"): + with self.assertRaises(ValueError) as cm: + self.open(os_helper.TESTFN, mode) + self.assertIn('invalid mode', str(cm.exception)) + + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_open_pipe_with_append(self): + # bpo-27805: Ignore ESPIPE from lseek() in open(). + r, w = os.pipe() + self.addCleanup(os.close, r) + f = self.open(w, 'a', encoding="utf-8") + self.addCleanup(f.close) + # Check that the file is marked non-seekable. On Windows, however, lseek + # somehow succeeds on pipes. + if sys.platform != 'win32': + self.assertFalse(f.seekable()) + + def test_io_after_close(self): + for kwargs in [ + {"mode": "w"}, + {"mode": "wb"}, + {"mode": "w", "buffering": 1}, + {"mode": "w", "buffering": 2}, + {"mode": "wb", "buffering": 0}, + {"mode": "r"}, + {"mode": "rb"}, + {"mode": "r", "buffering": 1}, + {"mode": "r", "buffering": 2}, + {"mode": "rb", "buffering": 0}, + {"mode": "w+"}, + {"mode": "w+b"}, + {"mode": "w+", "buffering": 1}, + {"mode": "w+", "buffering": 2}, + {"mode": "w+b", "buffering": 0}, + ]: + if "b" not in kwargs["mode"]: + kwargs["encoding"] = "utf-8" + f = self.open(os_helper.TESTFN, **kwargs) + f.close() + self.assertRaises(ValueError, f.flush) + self.assertRaises(ValueError, f.fileno) + self.assertRaises(ValueError, f.isatty) + self.assertRaises(ValueError, f.__iter__) + if hasattr(f, "peek"): + self.assertRaises(ValueError, f.peek, 1) + self.assertRaises(ValueError, f.read) + if hasattr(f, "read1"): + self.assertRaises(ValueError, f.read1, 1024) + self.assertRaises(ValueError, f.read1) + if hasattr(f, "readall"): + self.assertRaises(ValueError, f.readall) + if hasattr(f, "readinto"): + self.assertRaises(ValueError, f.readinto, bytearray(1024)) + if hasattr(f, "readinto1"): + self.assertRaises(ValueError, f.readinto1, bytearray(1024)) + self.assertRaises(ValueError, f.readline) + self.assertRaises(ValueError, f.readlines) + self.assertRaises(ValueError, f.readlines, 1) + self.assertRaises(ValueError, f.seek, 0) + self.assertRaises(ValueError, f.tell) + self.assertRaises(ValueError, f.truncate) + self.assertRaises(ValueError, f.write, + b"" if "b" in kwargs['mode'] else "") + self.assertRaises(ValueError, f.writelines, []) + self.assertRaises(ValueError, next, f) + + def test_blockingioerror(self): + # Various BlockingIOError issues + class C(str): + pass + c = C("") + b = self.BlockingIOError(1, c) + c.b = b + b.c = c + wr = weakref.ref(c) + del c, b + support.gc_collect() + self.assertIsNone(wr(), wr) + + def test_abcs(self): + # Test the visible base classes are ABCs. + self.assertIsInstance(self.IOBase, abc.ABCMeta) + self.assertIsInstance(self.RawIOBase, abc.ABCMeta) + self.assertIsInstance(self.BufferedIOBase, abc.ABCMeta) + self.assertIsInstance(self.TextIOBase, abc.ABCMeta) + + def _check_abc_inheritance(self, abcmodule): + with self.open(os_helper.TESTFN, "wb", buffering=0) as f: + self.assertIsInstance(f, abcmodule.IOBase) + self.assertIsInstance(f, abcmodule.RawIOBase) + self.assertNotIsInstance(f, abcmodule.BufferedIOBase) + self.assertNotIsInstance(f, abcmodule.TextIOBase) + with self.open(os_helper.TESTFN, "wb") as f: + self.assertIsInstance(f, abcmodule.IOBase) + self.assertNotIsInstance(f, abcmodule.RawIOBase) + self.assertIsInstance(f, abcmodule.BufferedIOBase) + self.assertNotIsInstance(f, abcmodule.TextIOBase) + with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f: + self.assertIsInstance(f, abcmodule.IOBase) + self.assertNotIsInstance(f, abcmodule.RawIOBase) + self.assertNotIsInstance(f, abcmodule.BufferedIOBase) + self.assertIsInstance(f, abcmodule.TextIOBase) + + def test_abc_inheritance(self): + # Test implementations inherit from their respective ABCs + self._check_abc_inheritance(self) + + def test_abc_inheritance_official(self): + # Test implementations inherit from the official ABCs of the + # baseline "io" module. + self._check_abc_inheritance(io) + + def _check_warn_on_dealloc(self, *args, **kwargs): + f = self.open(*args, **kwargs) + r = repr(f) + with self.assertWarns(ResourceWarning) as cm: + f = None + support.gc_collect() + self.assertIn(r, str(cm.warning.args[0])) + + def test_warn_on_dealloc(self): + self._check_warn_on_dealloc(os_helper.TESTFN, "wb", buffering=0) + self._check_warn_on_dealloc(os_helper.TESTFN, "wb") + self._check_warn_on_dealloc(os_helper.TESTFN, "w", encoding="utf-8") + + def _check_warn_on_dealloc_fd(self, *args, **kwargs): + fds = [] + def cleanup_fds(): + for fd in fds: + try: + os.close(fd) + except OSError as e: + if e.errno != errno.EBADF: + raise + self.addCleanup(cleanup_fds) + r, w = os.pipe() + fds += r, w + self._check_warn_on_dealloc(r, *args, **kwargs) + # When using closefd=False, there's no warning + r, w = os.pipe() + fds += r, w + with warnings_helper.check_no_resource_warning(self): + self.open(r, *args, closefd=False, **kwargs) + + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_warn_on_dealloc_fd(self): + self._check_warn_on_dealloc_fd("rb", buffering=0) + self._check_warn_on_dealloc_fd("rb") + self._check_warn_on_dealloc_fd("r", encoding="utf-8") + + + def test_pickling(self): + # Pickling file objects is forbidden + msg = "cannot pickle" + for kwargs in [ + {"mode": "w"}, + {"mode": "wb"}, + {"mode": "wb", "buffering": 0}, + {"mode": "r"}, + {"mode": "rb"}, + {"mode": "rb", "buffering": 0}, + {"mode": "w+"}, + {"mode": "w+b"}, + {"mode": "w+b", "buffering": 0}, + ]: + if "b" not in kwargs["mode"]: + kwargs["encoding"] = "utf-8" + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=protocol, kwargs=kwargs): + with self.open(os_helper.TESTFN, **kwargs) as f: + with self.assertRaisesRegex(TypeError, msg): + pickle.dumps(f, protocol) + + @unittest.skipIf(support.is_emscripten, "Emscripten corrupts memory when writing to nonblocking fd") + def test_nonblock_pipe_write_bigbuf(self): + self._test_nonblock_pipe_write(16*1024) + + @unittest.skipIf(support.is_emscripten, "Emscripten corrupts memory when writing to nonblocking fd") + def test_nonblock_pipe_write_smallbuf(self): + self._test_nonblock_pipe_write(1024) + + @unittest.skipUnless(hasattr(os, 'set_blocking'), + 'os.set_blocking() required for this test') + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def _test_nonblock_pipe_write(self, bufsize): + sent = [] + received = [] + r, w = os.pipe() + os.set_blocking(r, False) + os.set_blocking(w, False) + + # To exercise all code paths in the C implementation we need + # to play with buffer sizes. For instance, if we choose a + # buffer size less than or equal to _PIPE_BUF (4096 on Linux) + # then we will never get a partial write of the buffer. + rf = self.open(r, mode='rb', closefd=True, buffering=bufsize) + wf = self.open(w, mode='wb', closefd=True, buffering=bufsize) + + with rf, wf: + for N in 9999, 73, 7574: + try: + i = 0 + while True: + msg = bytes([i % 26 + 97]) * N + sent.append(msg) + wf.write(msg) + i += 1 + + except self.BlockingIOError as e: + self.assertEqual(e.args[0], errno.EAGAIN) + self.assertEqual(e.args[2], e.characters_written) + sent[-1] = sent[-1][:e.characters_written] + received.append(rf.read()) + msg = b'BLOCKED' + wf.write(msg) + sent.append(msg) + + while True: + try: + wf.flush() + break + except self.BlockingIOError as e: + self.assertEqual(e.args[0], errno.EAGAIN) + self.assertEqual(e.args[2], e.characters_written) + self.assertEqual(e.characters_written, 0) + received.append(rf.read()) + + received += iter(rf.read, None) + + sent, received = b''.join(sent), b''.join(received) + self.assertEqual(sent, received) + self.assertTrue(wf.closed) + self.assertTrue(rf.closed) + + def test_create_fail(self): + # 'x' mode fails if file is existing + with self.open(os_helper.TESTFN, 'w', encoding="utf-8"): + pass + self.assertRaises(FileExistsError, self.open, os_helper.TESTFN, 'x', encoding="utf-8") + + def test_create_writes(self): + # 'x' mode opens for writing + with self.open(os_helper.TESTFN, 'xb') as f: + f.write(b"spam") + with self.open(os_helper.TESTFN, 'rb') as f: + self.assertEqual(b"spam", f.read()) + + def test_open_allargs(self): + # there used to be a buffer overflow in the parser for rawmode + self.assertRaises(ValueError, self.open, os_helper.TESTFN, 'rwax+', encoding="utf-8") + + def test_check_encoding_errors(self): + # bpo-37388: open() and TextIOWrapper must check encoding and errors + # arguments in dev mode + mod = self.io.__name__ + filename = __file__ + invalid = 'Boom, Shaka Laka, Boom!' + code = textwrap.dedent(f''' + import sys + from {mod} import open, TextIOWrapper + + try: + open({filename!r}, encoding={invalid!r}) + except LookupError: + pass + else: + sys.exit(21) + + try: + open({filename!r}, errors={invalid!r}) + except LookupError: + pass + else: + sys.exit(22) + + fp = open({filename!r}, "rb") + with fp: + try: + TextIOWrapper(fp, encoding={invalid!r}) + except LookupError: + pass + else: + sys.exit(23) + + try: + TextIOWrapper(fp, errors={invalid!r}) + except LookupError: + pass + else: + sys.exit(24) + + sys.exit(10) + ''') + proc = assert_python_failure('-X', 'dev', '-c', code) + self.assertEqual(proc.rc, 10, proc) + + def test_check_encoding_warning(self): + # PEP 597: Raise warning when encoding is not specified + # and sys.flags.warn_default_encoding is set. + mod = self.io.__name__ + filename = __file__ + code = textwrap.dedent(f'''\ + import sys + from {mod} import open, TextIOWrapper + import pathlib + + with open({filename!r}) as f: # line 5 + pass + + pathlib.Path({filename!r}).read_text() # line 8 + ''') + proc = assert_python_ok('-X', 'warn_default_encoding', '-c', code) + warnings = proc.err.splitlines() + self.assertEqual(len(warnings), 2) + self.assertStartsWith(warnings[0], b"<string>:5: EncodingWarning: ") + self.assertStartsWith(warnings[1], b"<string>:8: EncodingWarning: ") + + def test_text_encoding(self): + # PEP 597, bpo-47000. io.text_encoding() returns "locale" or "utf-8" + # based on sys.flags.utf8_mode + code = "import io; print(io.text_encoding(None))" + + proc = assert_python_ok('-X', 'utf8=0', '-c', code) + self.assertEqual(b"locale", proc.out.strip()) + + proc = assert_python_ok('-X', 'utf8=1', '-c', code) + self.assertEqual(b"utf-8", proc.out.strip()) + + +class CMiscIOTest(MiscIOTest, CTestCase): + name_of_module = "io", "_io" + extra_exported = "BlockingIOError", + + def test_readinto_buffer_overflow(self): + # Issue #18025 + class BadReader(self.io.BufferedIOBase): + def read(self, n=-1): + return b'x' * 10**6 + bufio = BadReader() + b = bytearray(2) + self.assertRaises(ValueError, bufio.readinto, b) + + def check_daemon_threads_shutdown_deadlock(self, stream_name): + # Issue #23309: deadlocks at shutdown should be avoided when a + # daemon thread and the main thread both write to a file. + code = """if 1: + import sys + import time + import threading + from test.support import SuppressCrashReport + + file = sys.{stream_name} + + def run(): + while True: + file.write('.') + file.flush() + + crash = SuppressCrashReport() + crash.__enter__() + # don't call __exit__(): the crash occurs at Python shutdown + + thread = threading.Thread(target=run) + thread.daemon = True + thread.start() + + time.sleep(0.5) + file.write('!') + file.flush() + """.format_map(locals()) + res, _ = run_python_until_end("-c", code) + err = res.err.decode() + if res.rc != 0: + # Failure: should be a fatal error + pattern = (r"Fatal Python error: _enter_buffered_busy: " + r"could not acquire lock " + r"for <(_io\.)?BufferedWriter name='<{stream_name}>'> " + r"at interpreter shutdown, possibly due to " + r"daemon threads".format_map(locals())) + self.assertRegex(err, pattern) + else: + self.assertFalse(err.strip('.!')) + + @threading_helper.requires_working_threading() + @support.requires_resource('walltime') + def test_daemon_threads_shutdown_stdout_deadlock(self): + self.check_daemon_threads_shutdown_deadlock('stdout') + + @threading_helper.requires_working_threading() + @support.requires_resource('walltime') + def test_daemon_threads_shutdown_stderr_deadlock(self): + self.check_daemon_threads_shutdown_deadlock('stderr') + + +class PyMiscIOTest(MiscIOTest, PyTestCase): + name_of_module = "_pyio", "io" + extra_exported = "BlockingIOError", "open_code", + not_exported = "valid_seek_flags", + + +class ProtocolsTest(unittest.TestCase): + class MyReader: + def read(self, sz=-1): + return b"" + + class MyWriter: + def write(self, b: bytes): + pass + + def test_reader_subclass(self): + self.assertIsSubclass(self.MyReader, io.Reader) + self.assertNotIsSubclass(str, io.Reader) + + def test_writer_subclass(self): + self.assertIsSubclass(self.MyWriter, io.Writer) + self.assertNotIsSubclass(str, io.Writer) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_largefile.py b/Lib/test/test_io/test_largefile.py similarity index 98% rename from Lib/test/test_largefile.py rename to Lib/test/test_io/test_largefile.py index 41f7b70e5cfe81..438a90a92ed588 100644 --- a/Lib/test/test_largefile.py +++ b/Lib/test/test_io/test_largefile.py @@ -56,9 +56,7 @@ class TestFileMethods(LargeFileTest): (i.e. > 2 GiB) files. """ - # _pyio.FileIO.readall() uses a temporary bytearray then casted to bytes, - # so memuse=2 is needed - @bigmemtest(size=size, memuse=2, dry_run=False) + @bigmemtest(size=size, memuse=1, dry_run=False) def test_large_read(self, _size): # bpo-24658: Test that a read greater than 2GB does not fail. with self.open(TESTFN, "rb") as f: @@ -154,7 +152,7 @@ def test_seekable(self): f.seek(pos) self.assertTrue(f.seekable()) - @bigmemtest(size=size, memuse=2, dry_run=False) + @bigmemtest(size=size, memuse=1, dry_run=False) def test_seek_readall(self, _size): # Seek which doesn't change position should readall successfully. with self.open(TESTFN, 'rb') as f: diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_io/test_memoryio.py similarity index 90% rename from Lib/test/test_memoryio.py rename to Lib/test/test_io/test_memoryio.py index 63998a86c45b53..3669ac0b038b71 100644 --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_io/test_memoryio.py @@ -54,6 +54,12 @@ def testSeek(self): self.assertEqual(buf[3:], bytesIo.read()) self.assertRaises(TypeError, bytesIo.seek, 0.0) + self.assertEqual(sys.maxsize, bytesIo.seek(sys.maxsize)) + self.assertEqual(self.EOF, bytesIo.read(4)) + + self.assertEqual(sys.maxsize - 2, bytesIo.seek(sys.maxsize - 2)) + self.assertEqual(self.EOF, bytesIo.read(4)) + def testTell(self): buf = self.buftype("1234567890") bytesIo = self.ioclass(buf) @@ -552,6 +558,14 @@ def test_relative_seek(self): memio.seek(1, 1) self.assertEqual(memio.read(), buf[1:]) + def test_issue141311(self): + memio = self.ioclass() + # Seek allows PY_SSIZE_T_MAX, read should handle that. + # Past end of buffer read should always return 0 (EOF). + self.assertEqual(sys.maxsize, memio.seek(sys.maxsize)) + buf = bytearray(2) + self.assertEqual(0, memio.readinto(buf)) + def test_unicode(self): memio = self.ioclass() @@ -573,6 +587,70 @@ def test_issue5449(self): self.ioclass(initial_bytes=buf) self.assertRaises(TypeError, self.ioclass, buf, foo=None) + def test_write_concurrent_close(self): + class B: + def __buffer__(self, flags): + memio.close() + return memoryview(b"A") + + memio = self.ioclass() + self.assertRaises(ValueError, memio.write, B()) + + # Prevent crashes when memio.write() or memio.writelines() + # concurrently mutates (e.g., closes or exports) 'memio'. + # See: https://github.com/python/cpython/issues/143378. + + def test_writelines_concurrent_close(self): + class B: + def __buffer__(self, flags): + memio.close() + return memoryview(b"A") + + memio = self.ioclass() + self.assertRaises(ValueError, memio.writelines, [B()]) + + def test_write_concurrent_export(self): + class B: + buf = None + def __buffer__(self, flags): + self.buf = memio.getbuffer() + return memoryview(b"A") + + memio = self.ioclass() + self.assertRaises(BufferError, memio.write, B()) + + def test_writelines_concurrent_export(self): + class B: + buf = None + def __buffer__(self, flags): + self.buf = memio.getbuffer() + return memoryview(b"A") + + memio = self.ioclass() + self.assertRaises(BufferError, memio.writelines, [B()]) + + def test_write_mutating_buffer(self): + # Test that buffer is exported only once during write(). + # See: https://github.com/python/cpython/issues/143602. + class B: + count = 0 + def __buffer__(self, flags): + self.count += 1 + if self.count == 1: + return memoryview(b"AAA") + else: + return memoryview(b"BBBBBBBBB") + + memio = self.ioclass(b'0123456789') + memio.seek(2) + b = B() + n = memio.write(b) + + self.assertEqual(b.count, 1) + self.assertEqual(n, 3) + self.assertEqual(memio.getvalue(), b"01AAA56789") + self.assertEqual(memio.tell(), 5) + class TextIOTestMixin: @@ -889,6 +967,25 @@ def test_setstate(self): memio.close() self.assertRaises(ValueError, memio.__setstate__, ("closed", "", 0, None)) + def test_write_str_subclass(self): + # Writing a str subclass should use the subclass's unicode data + # directly, not call __str__ on it (which may return a different + # value). gh-149047 + class MyStr(str): + def __str__(self): + return "WRONG" + + s = MyStr("correct") + memio = self.ioclass() + memio.write(s) + self.assertEqual(memio.getvalue(), "correct") + + # Also test the fast path where pos == string_size (STATE_ACCUMULATING) + memio2 = self.ioclass() + memio2.write(MyStr("hello ")) + memio2.write(MyStr("world")) + self.assertEqual(memio2.getvalue(), "hello world") + class CStringIOPickleTest(PyStringIOPickleTest): UnsupportedOperation = io.UnsupportedOperation diff --git a/Lib/test/test_io/test_signals.py b/Lib/test/test_io/test_signals.py new file mode 100644 index 00000000000000..03f1da1eb1cfb0 --- /dev/null +++ b/Lib/test/test_io/test_signals.py @@ -0,0 +1,280 @@ +import errno +import os +import signal +import threading +import unittest +from test import support +from test.support import import_helper +from .utils import PyTestCase, CTestCase + + +requires_alarm = unittest.skipUnless( + hasattr(signal, "alarm"), "test requires signal.alarm()" +) + + +@unittest.skipIf(os.name == 'nt', 'POSIX signals required for this test.') +class SignalsTest: + + def setUp(self): + self.oldalrm = signal.signal(signal.SIGALRM, self.alarm_interrupt) + + def tearDown(self): + signal.signal(signal.SIGALRM, self.oldalrm) + + def alarm_interrupt(self, sig, frame): + 1/0 + + def check_interrupted_write(self, item, bytes, **fdopen_kwargs): + """Check that a partial write, when it gets interrupted, properly + invokes the signal handler, and bubbles up the exception raised + in the latter.""" + + # XXX This test has three flaws that appear when objects are + # XXX not reference counted. + + # - if wio.write() happens to trigger a garbage collection, + # the signal exception may be raised when some __del__ + # method is running; it will not reach the assertRaises() + # call. + + # - more subtle, if the wio object is not destroyed at once + # and survives this function, the next opened file is likely + # to have the same fileno (since the file descriptor was + # actively closed). When wio.__del__ is finally called, it + # will close the other's test file... To trigger this with + # CPython, try adding "global wio" in this function. + + # - This happens only for streams created by the _pyio module, + # because a wio.close() that fails still consider that the + # file needs to be closed again. You can try adding an + # "assert wio.closed" at the end of the function. + + # Fortunately, a little gc.collect() seems to be enough to + # work around all these issues. + support.gc_collect() # For PyPy or other GCs. + + read_results = [] + def _read(): + s = os.read(r, 1) + read_results.append(s) + + t = threading.Thread(target=_read) + t.daemon = True + r, w = os.pipe() + fdopen_kwargs["closefd"] = False + large_data = item * (support.PIPE_MAX_SIZE // len(item) + 1) + try: + wio = self.io.open(w, **fdopen_kwargs) + if hasattr(signal, 'pthread_sigmask'): + # create the thread with SIGALRM signal blocked + signal.pthread_sigmask(signal.SIG_BLOCK, [signal.SIGALRM]) + t.start() + signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGALRM]) + else: + t.start() + + # Fill the pipe enough that the write will be blocking. + # It will be interrupted by the timer armed above. Since the + # other thread has read one byte, the low-level write will + # return with a successful (partial) result rather than an EINTR. + # The buffered IO layer must check for pending signal + # handlers, which in this case will invoke alarm_interrupt(). + signal.alarm(1) + try: + self.assertRaises(ZeroDivisionError, wio.write, large_data) + finally: + signal.alarm(0) + t.join() + # We got one byte, get another one and check that it isn't a + # repeat of the first one. + read_results.append(os.read(r, 1)) + self.assertEqual(read_results, [bytes[0:1], bytes[1:2]]) + finally: + os.close(w) + os.close(r) + # This is deliberate. If we didn't close the file descriptor + # before closing wio, wio would try to flush its internal + # buffer, and block again. + try: + wio.close() + except OSError as e: + if e.errno != errno.EBADF: + raise + + @requires_alarm + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_interrupted_write_unbuffered(self): + self.check_interrupted_write(b"xy", b"xy", mode="wb", buffering=0) + + @requires_alarm + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_interrupted_write_buffered(self): + self.check_interrupted_write(b"xy", b"xy", mode="wb") + + @requires_alarm + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_interrupted_write_text(self): + self.check_interrupted_write("xy", b"xy", mode="w", encoding="ascii") + + @support.no_tracing + def check_reentrant_write(self, data, **fdopen_kwargs): + def on_alarm(*args): + # Will be called reentrantly from the same thread + wio.write(data) + 1/0 + signal.signal(signal.SIGALRM, on_alarm) + r, w = os.pipe() + wio = self.io.open(w, **fdopen_kwargs) + try: + signal.alarm(1) + # Either the reentrant call to wio.write() fails with RuntimeError, + # or the signal handler raises ZeroDivisionError. + with self.assertRaises((ZeroDivisionError, RuntimeError)) as cm: + while 1: + for i in range(100): + wio.write(data) + wio.flush() + # Make sure the buffer doesn't fill up and block further writes + os.read(r, len(data) * 100) + exc = cm.exception + if isinstance(exc, RuntimeError): + self.assertStartsWith(str(exc), "reentrant call") + finally: + signal.alarm(0) + wio.close() + os.close(r) + + @requires_alarm + def test_reentrant_write_buffered(self): + self.check_reentrant_write(b"xy", mode="wb") + + @requires_alarm + def test_reentrant_write_text(self): + self.check_reentrant_write("xy", mode="w", encoding="ascii") + + def check_interrupted_read_retry(self, decode, **fdopen_kwargs): + """Check that a buffered read, when it gets interrupted (either + returning a partial result or EINTR), properly invokes the signal + handler and retries if the latter returned successfully.""" + r, w = os.pipe() + fdopen_kwargs["closefd"] = False + def alarm_handler(sig, frame): + os.write(w, b"bar") + signal.signal(signal.SIGALRM, alarm_handler) + try: + rio = self.io.open(r, **fdopen_kwargs) + os.write(w, b"foo") + signal.alarm(1) + # Expected behaviour: + # - first raw read() returns partial b"foo" + # - second raw read() returns EINTR + # - third raw read() returns b"bar" + self.assertEqual(decode(rio.read(6)), "foobar") + finally: + signal.alarm(0) + rio.close() + os.close(w) + os.close(r) + + @requires_alarm + @support.requires_resource('walltime') + def test_interrupted_read_retry_buffered(self): + self.check_interrupted_read_retry(lambda x: x.decode('latin1'), + mode="rb") + + @requires_alarm + @support.requires_resource('walltime') + def test_interrupted_read_retry_text(self): + self.check_interrupted_read_retry(lambda x: x, + mode="r", encoding="latin1") + + def check_interrupted_write_retry(self, item, **fdopen_kwargs): + """Check that a buffered write, when it gets interrupted (either + returning a partial result or EINTR), properly invokes the signal + handler and retries if the latter returned successfully.""" + select = import_helper.import_module("select") + + # A quantity that exceeds the buffer size of an anonymous pipe's + # write end. + N = support.PIPE_MAX_SIZE + r, w = os.pipe() + fdopen_kwargs["closefd"] = False + + # We need a separate thread to read from the pipe and allow the + # write() to finish. This thread is started after the SIGALRM is + # received (forcing a first EINTR in write()). + read_results = [] + write_finished = False + error = None + def _read(): + try: + while not write_finished: + while r in select.select([r], [], [], 1.0)[0]: + s = os.read(r, 1024) + read_results.append(s) + except BaseException as exc: + nonlocal error + error = exc + t = threading.Thread(target=_read) + t.daemon = True + def alarm1(sig, frame): + signal.signal(signal.SIGALRM, alarm2) + signal.alarm(1) + def alarm2(sig, frame): + t.start() + + large_data = item * N + signal.signal(signal.SIGALRM, alarm1) + try: + wio = self.io.open(w, **fdopen_kwargs) + signal.alarm(1) + # Expected behaviour: + # - first raw write() is partial (because of the limited pipe buffer + # and the first alarm) + # - second raw write() returns EINTR (because of the second alarm) + # - subsequent write()s are successful (either partial or complete) + written = wio.write(large_data) + self.assertEqual(N, written) + + wio.flush() + write_finished = True + t.join() + + self.assertIsNone(error) + self.assertEqual(N, sum(len(x) for x in read_results)) + finally: + signal.alarm(0) + write_finished = True + os.close(w) + os.close(r) + # This is deliberate. If we didn't close the file descriptor + # before closing wio, wio would try to flush its internal + # buffer, and could block (in case of failure). + try: + wio.close() + except OSError as e: + if e.errno != errno.EBADF: + raise + + @requires_alarm + @support.requires_resource('walltime') + def test_interrupted_write_retry_buffered(self): + self.check_interrupted_write_retry(b"x", mode="wb") + + @requires_alarm + @support.requires_resource('walltime') + def test_interrupted_write_retry_text(self): + self.check_interrupted_write_retry("x", mode="w", encoding="latin1") + + +class CSignalsTest(SignalsTest, CTestCase): + pass + +class PySignalsTest(SignalsTest, PyTestCase): + pass + + # Handling reentrancy issues would slow down _pyio even more, so the + # tests are disabled. + test_reentrant_write_buffered = None + test_reentrant_write_text = None diff --git a/Lib/test/test_io/test_textio.py b/Lib/test/test_io/test_textio.py new file mode 100644 index 00000000000000..82096ab0987395 --- /dev/null +++ b/Lib/test/test_io/test_textio.py @@ -0,0 +1,1749 @@ +import array +import codecs +import locale +import os +import pickle +import sys +import threading +import time +import unittest +import warnings +import weakref +from collections import UserList +from test import support +from test.support import os_helper, threading_helper +from test.support.script_helper import assert_python_ok +from .utils import CTestCase, PyTestCase + +import io # C implementation of io +import _pyio as pyio # Python implementation of io + + +def _default_chunk_size(): + """Get the default TextIOWrapper chunk size""" + with open(__file__, "r", encoding="latin-1") as f: + return f._CHUNK_SIZE + + +class BadIndex: + def __index__(self): + 1/0 + + +# To fully exercise seek/tell, the StatefulIncrementalDecoder has these +# properties: +# - A single output character can correspond to many bytes of input. +# - The number of input bytes to complete the character can be +# undetermined until the last input byte is received. +# - The number of input bytes can vary depending on previous input. +# - A single input byte can correspond to many characters of output. +# - The number of output characters can be undetermined until the +# last input byte is received. +# - The number of output characters can vary depending on previous input. + +class StatefulIncrementalDecoder(codecs.IncrementalDecoder): + """ + For testing seek/tell behavior with a stateful, buffering decoder. + + Input is a sequence of words. Words may be fixed-length (length set + by input) or variable-length (period-terminated). In variable-length + mode, extra periods are ignored. Possible words are: + - 'i' followed by a number sets the input length, I (maximum 99). + When I is set to 0, words are space-terminated. + - 'o' followed by a number sets the output length, O (maximum 99). + - Any other word is converted into a word followed by a period on + the output. The output word consists of the input word truncated + or padded out with hyphens to make its length equal to O. If O + is 0, the word is output verbatim without truncating or padding. + I and O are initially set to 1. When I changes, any buffered input is + re-scanned according to the new I. EOF also terminates the last word. + """ + + def __init__(self, errors='strict'): + codecs.IncrementalDecoder.__init__(self, errors) + self.reset() + + def __repr__(self): + return '<SID %x>' % id(self) + + def reset(self): + self.i = 1 + self.o = 1 + self.buffer = bytearray() + + def getstate(self): + i, o = self.i ^ 1, self.o ^ 1 # so that flags = 0 after reset() + return bytes(self.buffer), i*100 + o + + def setstate(self, state): + buffer, io = state + self.buffer = bytearray(buffer) + i, o = divmod(io, 100) + self.i, self.o = i ^ 1, o ^ 1 + + def decode(self, input, final=False): + output = '' + for b in input: + if self.i == 0: # variable-length, terminated with period + if b == ord('.'): + if self.buffer: + output += self.process_word() + else: + self.buffer.append(b) + else: # fixed-length, terminate after self.i bytes + self.buffer.append(b) + if len(self.buffer) == self.i: + output += self.process_word() + if final and self.buffer: # EOF terminates the last word + output += self.process_word() + return output + + def process_word(self): + output = '' + if self.buffer[0] == ord('i'): + self.i = min(99, int(self.buffer[1:] or 0)) # set input length + elif self.buffer[0] == ord('o'): + self.o = min(99, int(self.buffer[1:] or 0)) # set output length + else: + output = self.buffer.decode('ascii') + if len(output) < self.o: + output += '-'*self.o # pad out with hyphens + if self.o: + output = output[:self.o] # truncate to output length + output += '.' + self.buffer = bytearray() + return output + + codecEnabled = False + + +# bpo-41919: This method is separated from StatefulIncrementalDecoder to avoid a resource leak +# when registering codecs and cleanup functions. +def lookupTestDecoder(name): + if StatefulIncrementalDecoder.codecEnabled and name == 'test_decoder': + latin1 = codecs.lookup('latin-1') + return codecs.CodecInfo( + name='test_decoder', encode=latin1.encode, decode=None, + incrementalencoder=None, + streamreader=None, streamwriter=None, + incrementaldecoder=StatefulIncrementalDecoder) + + +class StatefulIncrementalDecoderTest(unittest.TestCase): + """ + Make sure the StatefulIncrementalDecoder actually works. + """ + + test_cases = [ + # I=1, O=1 (fixed-length input == fixed-length output) + (b'abcd', False, 'a.b.c.d.'), + # I=0, O=0 (variable-length input, variable-length output) + (b'oiabcd', True, 'abcd.'), + # I=0, O=0 (should ignore extra periods) + (b'oi...abcd...', True, 'abcd.'), + # I=0, O=6 (variable-length input, fixed-length output) + (b'i.o6.x.xyz.toolongtofit.', False, 'x-----.xyz---.toolon.'), + # I=2, O=6 (fixed-length input < fixed-length output) + (b'i.i2.o6xyz', True, 'xy----.z-----.'), + # I=6, O=3 (fixed-length input > fixed-length output) + (b'i.o3.i6.abcdefghijklmnop', True, 'abc.ghi.mno.'), + # I=0, then 3; O=29, then 15 (with longer output) + (b'i.o29.a.b.cde.o15.abcdefghijabcdefghij.i3.a.b.c.d.ei00k.l.m', True, + 'a----------------------------.' + + 'b----------------------------.' + + 'cde--------------------------.' + + 'abcdefghijabcde.' + + 'a.b------------.' + + '.c.------------.' + + 'd.e------------.' + + 'k--------------.' + + 'l--------------.' + + 'm--------------.') + ] + + def test_decoder(self): + # Try a few one-shot test cases. + for input, eof, output in self.test_cases: + d = StatefulIncrementalDecoder() + self.assertEqual(d.decode(input, eof), output) + + # Also test an unfinished decode, followed by forcing EOF. + d = StatefulIncrementalDecoder() + self.assertEqual(d.decode(b'oiabcd'), '') + self.assertEqual(d.decode(b'', 1), 'abcd.') + +class TextIOWrapperTest: + + def setUp(self): + self.testdata = b"AAA\r\nBBB\rCCC\r\nDDD\nEEE\r\n" + self.normalized = b"AAA\nBBB\nCCC\nDDD\nEEE\n".decode("ascii") + os_helper.unlink(os_helper.TESTFN) + codecs.register(lookupTestDecoder) + self.addCleanup(codecs.unregister, lookupTestDecoder) + + def tearDown(self): + os_helper.unlink(os_helper.TESTFN) + + def test_constructor(self): + r = self.BytesIO(b"\xc3\xa9\n\n") + b = self.BufferedReader(r, 1000) + t = self.TextIOWrapper(b, encoding="utf-8") + t.__init__(b, encoding="latin-1", newline="\r\n") + self.assertEqual(t.encoding, "latin-1") + self.assertEqual(t.line_buffering, False) + t.__init__(b, encoding="utf-8", line_buffering=True) + self.assertEqual(t.encoding, "utf-8") + self.assertEqual(t.line_buffering, True) + self.assertEqual("\xe9\n", t.readline()) + invalid_type = TypeError if self.is_C else ValueError + with self.assertRaises(invalid_type): + t.__init__(b, encoding=42) + with self.assertRaises(UnicodeEncodeError): + t.__init__(b, encoding='\udcfe') + with self.assertRaises(ValueError): + t.__init__(b, encoding='utf-8\0') + with self.assertRaises(invalid_type): + t.__init__(b, encoding="utf-8", errors=42) + if support.Py_DEBUG or sys.flags.dev_mode or self.is_C: + with self.assertRaises(UnicodeEncodeError): + t.__init__(b, encoding="utf-8", errors='\udcfe') + if support.Py_DEBUG or sys.flags.dev_mode or self.is_C: + with self.assertRaises(ValueError): + t.__init__(b, encoding="utf-8", errors='replace\0') + with self.assertRaises(TypeError): + t.__init__(b, encoding="utf-8", newline=42) + with self.assertRaises(ValueError): + t.__init__(b, encoding="utf-8", newline='\udcfe') + with self.assertRaises(ValueError): + t.__init__(b, encoding="utf-8", newline='\n\0') + with self.assertRaises(ValueError): + t.__init__(b, encoding="utf-8", newline='xyzzy') + + def test_uninitialized(self): + t = self.TextIOWrapper.__new__(self.TextIOWrapper) + del t + t = self.TextIOWrapper.__new__(self.TextIOWrapper) + self.assertRaises(Exception, repr, t) + self.assertRaisesRegex((ValueError, AttributeError), + 'uninitialized|has no attribute', + t.read, 0) + t.__init__(self.MockRawIO(), encoding="utf-8") + self.assertEqual(t.read(0), '') + + def test_non_text_encoding_codecs_are_rejected(self): + # Ensure the constructor complains if passed a codec that isn't + # marked as a text encoding + # http://bugs.python.org/issue20404 + r = self.BytesIO() + b = self.BufferedWriter(r) + with self.assertRaisesRegex(LookupError, "is not a text encoding"): + self.TextIOWrapper(b, encoding="hex") + + def test_detach(self): + r = self.BytesIO() + b = self.BufferedWriter(r) + t = self.TextIOWrapper(b, encoding="ascii") + self.assertIs(t.detach(), b) + + t = self.TextIOWrapper(b, encoding="ascii") + t.write("howdy") + self.assertFalse(r.getvalue()) + t.detach() + self.assertEqual(r.getvalue(), b"howdy") + self.assertRaises(ValueError, t.detach) + + # Operations independent of the detached stream should still work + repr(t) + self.assertEqual(t.encoding, "ascii") + self.assertEqual(t.errors, "strict") + self.assertFalse(t.line_buffering) + self.assertFalse(t.write_through) + + def test_repr(self): + raw = self.BytesIO("hello".encode("utf-8")) + b = self.BufferedReader(raw) + t = self.TextIOWrapper(b, encoding="utf-8") + modname = self.TextIOWrapper.__module__ + self.assertRegex(repr(t), + r"<(%s\.)?TextIOWrapper encoding='utf-8'>" % modname) + raw.name = "dummy" + self.assertRegex(repr(t), + r"<(%s\.)?TextIOWrapper name='dummy' encoding='utf-8'>" % modname) + t.mode = "r" + self.assertRegex(repr(t), + r"<(%s\.)?TextIOWrapper name='dummy' mode='r' encoding='utf-8'>" % modname) + raw.name = b"dummy" + self.assertRegex(repr(t), + r"<(%s\.)?TextIOWrapper name=b'dummy' mode='r' encoding='utf-8'>" % modname) + + t.buffer.detach() + repr(t) # Should not raise an exception + + def test_recursive_repr(self): + # Issue #25455 + raw = self.BytesIO() + t = self.TextIOWrapper(raw, encoding="utf-8") + with support.swap_attr(raw, 'name', t), support.infinite_recursion(25): + with self.assertRaises(RuntimeError): + repr(t) # Should not crash + + def test_subclass_repr(self): + class TestSubclass(self.TextIOWrapper): + pass + + f = TestSubclass(self.StringIO()) + self.assertIn(TestSubclass.__name__, repr(f)) + + def test_line_buffering(self): + r = self.BytesIO() + b = self.BufferedWriter(r, 1000) + t = self.TextIOWrapper(b, encoding="utf-8", newline="\n", line_buffering=True) + t.write("X") + self.assertEqual(r.getvalue(), b"") # No flush happened + t.write("Y\nZ") + self.assertEqual(r.getvalue(), b"XY\nZ") # All got flushed + t.write("A\rB") + self.assertEqual(r.getvalue(), b"XY\nZA\rB") + + def test_reconfigure_line_buffering(self): + r = self.BytesIO() + b = self.BufferedWriter(r, 1000) + t = self.TextIOWrapper(b, encoding="utf-8", newline="\n", line_buffering=False) + t.write("AB\nC") + self.assertEqual(r.getvalue(), b"") + + t.reconfigure(line_buffering=True) # implicit flush + self.assertEqual(r.getvalue(), b"AB\nC") + t.write("DEF\nG") + self.assertEqual(r.getvalue(), b"AB\nCDEF\nG") + t.write("H") + self.assertEqual(r.getvalue(), b"AB\nCDEF\nG") + t.reconfigure(line_buffering=False) # implicit flush + self.assertEqual(r.getvalue(), b"AB\nCDEF\nGH") + t.write("IJ") + self.assertEqual(r.getvalue(), b"AB\nCDEF\nGH") + + # Keeping default value + t.reconfigure() + t.reconfigure(line_buffering=None) + self.assertEqual(t.line_buffering, False) + t.reconfigure(line_buffering=True) + t.reconfigure() + t.reconfigure(line_buffering=None) + self.assertEqual(t.line_buffering, True) + + @unittest.skipIf(sys.flags.utf8_mode, "utf-8 mode is enabled") + def test_default_encoding(self): + with os_helper.EnvironmentVarGuard() as env: + # try to get a user preferred encoding different than the current + # locale encoding to check that TextIOWrapper() uses the current + # locale encoding and not the user preferred encoding + env.unset('LC_ALL', 'LANG', 'LC_CTYPE') + + current_locale_encoding = locale.getencoding() + b = self.BytesIO() + with warnings.catch_warnings(): + warnings.simplefilter("ignore", EncodingWarning) + t = self.TextIOWrapper(b) + self.assertEqual(t.encoding, current_locale_encoding) + + def test_encoding(self): + # Check the encoding attribute is always set, and valid + b = self.BytesIO() + t = self.TextIOWrapper(b, encoding="utf-8") + self.assertEqual(t.encoding, "utf-8") + with warnings.catch_warnings(): + warnings.simplefilter("ignore", EncodingWarning) + t = self.TextIOWrapper(b) + self.assertIsNotNone(t.encoding) + codecs.lookup(t.encoding) + + def test_encoding_errors_reading(self): + # (1) default + b = self.BytesIO(b"abc\n\xff\n") + t = self.TextIOWrapper(b, encoding="ascii") + self.assertRaises(UnicodeError, t.read) + # (2) explicit strict + b = self.BytesIO(b"abc\n\xff\n") + t = self.TextIOWrapper(b, encoding="ascii", errors="strict") + self.assertRaises(UnicodeError, t.read) + # (3) ignore + b = self.BytesIO(b"abc\n\xff\n") + t = self.TextIOWrapper(b, encoding="ascii", errors="ignore") + self.assertEqual(t.read(), "abc\n\n") + # (4) replace + b = self.BytesIO(b"abc\n\xff\n") + t = self.TextIOWrapper(b, encoding="ascii", errors="replace") + self.assertEqual(t.read(), "abc\n\ufffd\n") + + def test_encoding_errors_writing(self): + # (1) default + b = self.BytesIO() + t = self.TextIOWrapper(b, encoding="ascii") + self.assertRaises(UnicodeError, t.write, "\xff") + # (2) explicit strict + b = self.BytesIO() + t = self.TextIOWrapper(b, encoding="ascii", errors="strict") + self.assertRaises(UnicodeError, t.write, "\xff") + # (3) ignore + b = self.BytesIO() + t = self.TextIOWrapper(b, encoding="ascii", errors="ignore", + newline="\n") + t.write("abc\xffdef\n") + t.flush() + self.assertEqual(b.getvalue(), b"abcdef\n") + # (4) replace + b = self.BytesIO() + t = self.TextIOWrapper(b, encoding="ascii", errors="replace", + newline="\n") + t.write("abc\xffdef\n") + t.flush() + self.assertEqual(b.getvalue(), b"abc?def\n") + + def test_newlines(self): + input_lines = [ "unix\n", "windows\r\n", "os9\r", "last\n", "nonl" ] + + tests = [ + [ None, [ 'unix\n', 'windows\n', 'os9\n', 'last\n', 'nonl' ] ], + [ '', input_lines ], + [ '\n', [ "unix\n", "windows\r\n", "os9\rlast\n", "nonl" ] ], + [ '\r\n', [ "unix\nwindows\r\n", "os9\rlast\nnonl" ] ], + [ '\r', [ "unix\nwindows\r", "\nos9\r", "last\nnonl" ] ], + ] + encodings = ( + 'utf-8', 'latin-1', + 'utf-16', 'utf-16-le', 'utf-16-be', + 'utf-32', 'utf-32-le', 'utf-32-be', + ) + + # Try a range of buffer sizes to test the case where \r is the last + # character in TextIOWrapper._pending_line. + for encoding in encodings: + # XXX: str.encode() should return bytes + data = bytes(''.join(input_lines).encode(encoding)) + for do_reads in (False, True): + for bufsize in range(1, 10): + for newline, exp_lines in tests: + bufio = self.BufferedReader(self.BytesIO(data), bufsize) + textio = self.TextIOWrapper(bufio, newline=newline, + encoding=encoding) + if do_reads: + got_lines = [] + while True: + c2 = textio.read(2) + if c2 == '': + break + self.assertEqual(len(c2), 2) + got_lines.append(c2 + textio.readline()) + else: + got_lines = list(textio) + + for got_line, exp_line in zip(got_lines, exp_lines): + self.assertEqual(got_line, exp_line) + self.assertEqual(len(got_lines), len(exp_lines)) + + def test_newlines_input(self): + testdata = b"AAA\nBB\x00B\nCCC\rDDD\rEEE\r\nFFF\r\nGGG" + normalized = testdata.replace(b"\r\n", b"\n").replace(b"\r", b"\n") + for newline, expected in [ + (None, normalized.decode("ascii").splitlines(keepends=True)), + ("", testdata.decode("ascii").splitlines(keepends=True)), + ("\n", ["AAA\n", "BB\x00B\n", "CCC\rDDD\rEEE\r\n", "FFF\r\n", "GGG"]), + ("\r\n", ["AAA\nBB\x00B\nCCC\rDDD\rEEE\r\n", "FFF\r\n", "GGG"]), + ("\r", ["AAA\nBB\x00B\nCCC\r", "DDD\r", "EEE\r", "\nFFF\r", "\nGGG"]), + ]: + buf = self.BytesIO(testdata) + txt = self.TextIOWrapper(buf, encoding="ascii", newline=newline) + self.assertEqual(txt.readlines(), expected) + txt.seek(0) + self.assertEqual(txt.read(), "".join(expected)) + + def test_newlines_output(self): + testdict = { + "": b"AAA\nBBB\nCCC\nX\rY\r\nZ", + "\n": b"AAA\nBBB\nCCC\nX\rY\r\nZ", + "\r": b"AAA\rBBB\rCCC\rX\rY\r\rZ", + "\r\n": b"AAA\r\nBBB\r\nCCC\r\nX\rY\r\r\nZ", + } + tests = [(None, testdict[os.linesep])] + sorted(testdict.items()) + for newline, expected in tests: + buf = self.BytesIO() + txt = self.TextIOWrapper(buf, encoding="ascii", newline=newline) + txt.write("AAA\nB") + txt.write("BB\nCCC\n") + txt.write("X\rY\r\nZ") + txt.flush() + self.assertEqual(buf.closed, False) + self.assertEqual(buf.getvalue(), expected) + + def test_destructor(self): + l = [] + base = self.BytesIO + class MyBytesIO(base): + def close(self): + l.append(self.getvalue()) + base.close(self) + b = MyBytesIO() + t = self.TextIOWrapper(b, encoding="ascii") + t.write("abc") + del t + support.gc_collect() + self.assertEqual([b"abc"], l) + + def test_override_destructor(self): + record = [] + class MyTextIO(self.TextIOWrapper): + def __del__(self): + record.append(1) + try: + f = super().__del__ + except AttributeError: + pass + else: + f() + def close(self): + record.append(2) + super().close() + def flush(self): + record.append(3) + super().flush() + b = self.BytesIO() + t = MyTextIO(b, encoding="ascii") + del t + support.gc_collect() + self.assertEqual(record, [1, 2, 3]) + + def test_error_through_destructor(self): + # Test that the exception state is not modified by a destructor, + # even if close() fails. + rawio = self.CloseFailureIO() + with support.catch_unraisable_exception() as cm: + with self.assertRaises(AttributeError): + self.TextIOWrapper(rawio, encoding="utf-8").xyzzy + + self.assertEqual(cm.unraisable.exc_type, OSError) + + # Systematic tests of the text I/O API + + def test_basic_io(self): + for chunksize in (1, 2, 3, 4, 5, 15, 16, 17, 31, 32, 33, 63, 64, 65): + for enc in "ascii", "latin-1", "utf-8" :# , "utf-16-be", "utf-16-le": + f = self.open(os_helper.TESTFN, "w+", encoding=enc) + f._CHUNK_SIZE = chunksize + self.assertEqual(f.write("abc"), 3) + f.close() + f = self.open(os_helper.TESTFN, "r+", encoding=enc) + f._CHUNK_SIZE = chunksize + self.assertEqual(f.tell(), 0) + self.assertEqual(f.read(), "abc") + cookie = f.tell() + self.assertEqual(f.seek(0), 0) + self.assertEqual(f.read(None), "abc") + f.seek(0) + self.assertEqual(f.read(2), "ab") + self.assertEqual(f.read(1), "c") + self.assertEqual(f.read(1), "") + self.assertEqual(f.read(), "") + self.assertEqual(f.tell(), cookie) + self.assertEqual(f.seek(0), 0) + self.assertEqual(f.seek(0, 2), cookie) + self.assertEqual(f.write("def"), 3) + self.assertEqual(f.seek(cookie), cookie) + self.assertEqual(f.read(), "def") + if enc.startswith("utf"): + self.multi_line_test(f, enc) + f.close() + + def multi_line_test(self, f, enc): + f.seek(0) + f.truncate() + sample = "s\xff\u0fff\uffff" + wlines = [] + for size in (0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 62, 63, 64, 65, 1000): + chars = [] + for i in range(size): + chars.append(sample[i % len(sample)]) + line = "".join(chars) + "\n" + wlines.append((f.tell(), line)) + f.write(line) + f.seek(0) + rlines = [] + while True: + pos = f.tell() + line = f.readline() + if not line: + break + rlines.append((pos, line)) + self.assertEqual(rlines, wlines) + + def test_telling(self): + f = self.open(os_helper.TESTFN, "w+", encoding="utf-8") + p0 = f.tell() + f.write("\xff\n") + p1 = f.tell() + f.write("\xff\n") + p2 = f.tell() + f.seek(0) + self.assertEqual(f.tell(), p0) + self.assertEqual(f.readline(), "\xff\n") + self.assertEqual(f.tell(), p1) + self.assertEqual(f.readline(), "\xff\n") + self.assertEqual(f.tell(), p2) + f.seek(0) + for line in f: + self.assertEqual(line, "\xff\n") + self.assertRaises(OSError, f.tell) + self.assertEqual(f.tell(), p2) + f.close() + + def test_seeking(self): + chunk_size = _default_chunk_size() + prefix_size = chunk_size - 2 + u_prefix = "a" * prefix_size + prefix = bytes(u_prefix.encode("utf-8")) + self.assertEqual(len(u_prefix), len(prefix)) + u_suffix = "\u8888\n" + suffix = bytes(u_suffix.encode("utf-8")) + line = prefix + suffix + with self.open(os_helper.TESTFN, "wb") as f: + f.write(line*2) + with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: + s = f.read(prefix_size) + self.assertEqual(s, str(prefix, "ascii")) + self.assertEqual(f.tell(), prefix_size) + self.assertEqual(f.readline(), u_suffix) + + def test_seeking_too(self): + # Regression test for a specific bug + data = b'\xe0\xbf\xbf\n' + with self.open(os_helper.TESTFN, "wb") as f: + f.write(data) + with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: + f._CHUNK_SIZE # Just test that it exists + f._CHUNK_SIZE = 2 + f.readline() + f.tell() + + def test_seek_and_tell(self): + #Test seek/tell using the StatefulIncrementalDecoder. + # Make test faster by doing smaller seeks + CHUNK_SIZE = 128 + + def test_seek_and_tell_with_data(data, min_pos=0): + """Tell/seek to various points within a data stream and ensure + that the decoded data returned by read() is consistent.""" + f = self.open(os_helper.TESTFN, 'wb') + f.write(data) + f.close() + f = self.open(os_helper.TESTFN, encoding='test_decoder') + f._CHUNK_SIZE = CHUNK_SIZE + decoded = f.read() + f.close() + + for i in range(min_pos, len(decoded) + 1): # seek positions + for j in [1, 5, len(decoded) - i]: # read lengths + f = self.open(os_helper.TESTFN, encoding='test_decoder') + self.assertEqual(f.read(i), decoded[:i]) + cookie = f.tell() + self.assertEqual(f.read(j), decoded[i:i + j]) + f.seek(cookie) + self.assertEqual(f.read(), decoded[i:]) + f.close() + + # Enable the test decoder. + StatefulIncrementalDecoder.codecEnabled = 1 + + # Run the tests. + try: + # Try each test case. + for input, _, _ in StatefulIncrementalDecoderTest.test_cases: + test_seek_and_tell_with_data(input) + + # Position each test case so that it crosses a chunk boundary. + for input, _, _ in StatefulIncrementalDecoderTest.test_cases: + offset = CHUNK_SIZE - len(input)//2 + prefix = b'.'*offset + # Don't bother seeking into the prefix (takes too long). + min_pos = offset*2 + test_seek_and_tell_with_data(prefix + input, min_pos) + + # Ensure our test decoder won't interfere with subsequent tests. + finally: + StatefulIncrementalDecoder.codecEnabled = 0 + + def test_multibyte_seek_and_tell(self): + f = self.open(os_helper.TESTFN, "w", encoding="euc_jp") + f.write("AB\n\u3046\u3048\n") + f.close() + + f = self.open(os_helper.TESTFN, "r", encoding="euc_jp") + self.assertEqual(f.readline(), "AB\n") + p0 = f.tell() + self.assertEqual(f.readline(), "\u3046\u3048\n") + p1 = f.tell() + f.seek(p0) + self.assertEqual(f.readline(), "\u3046\u3048\n") + self.assertEqual(f.tell(), p1) + f.close() + + def test_tell_after_readline_with_cr(self): + # Test for gh-141314: TextIOWrapper.tell() assertion failure + # when dealing with standalone carriage returns + data = b'line1\r' + with self.open(os_helper.TESTFN, "wb") as f: + f.write(data) + + with self.open(os_helper.TESTFN, "r") as f: + # Read line that ends with \r + line = f.readline() + self.assertEqual(line, "line1\n") + # This should not cause an assertion failure + pos = f.tell() + # Verify we can seek back to this position + f.seek(pos) + remaining = f.read() + self.assertEqual(remaining, "") + + + def test_seek_with_encoder_state(self): + f = self.open(os_helper.TESTFN, "w", encoding="euc_jis_2004") + f.write("\u00e6\u0300") + p0 = f.tell() + f.write("\u00e6") + f.seek(p0) + f.write("\u0300") + f.close() + + f = self.open(os_helper.TESTFN, "r", encoding="euc_jis_2004") + self.assertEqual(f.readline(), "\u00e6\u0300\u0300") + f.close() + + def test_encoded_writes(self): + data = "1234567890" + tests = ("utf-16", + "utf-16-le", + "utf-16-be", + "utf-32", + "utf-32-le", + "utf-32-be") + for encoding in tests: + buf = self.BytesIO() + f = self.TextIOWrapper(buf, encoding=encoding) + # Check if the BOM is written only once (see issue1753). + f.write(data) + f.write(data) + f.seek(0) + self.assertEqual(f.read(), data * 2) + f.seek(0) + self.assertEqual(f.read(), data * 2) + self.assertEqual(buf.getvalue(), (data * 2).encode(encoding)) + + def test_unreadable(self): + class UnReadable(self.BytesIO): + def readable(self): + return False + txt = self.TextIOWrapper(UnReadable(), encoding="utf-8") + self.assertRaises(OSError, txt.read) + + def test_read_one_by_one(self): + txt = self.TextIOWrapper(self.BytesIO(b"AA\r\nBB"), encoding="utf-8") + reads = "" + while True: + c = txt.read(1) + if not c: + break + reads += c + self.assertEqual(reads, "AA\nBB") + + def test_readlines(self): + txt = self.TextIOWrapper(self.BytesIO(b"AA\nBB\nCC"), encoding="utf-8") + self.assertEqual(txt.readlines(), ["AA\n", "BB\n", "CC"]) + txt.seek(0) + self.assertEqual(txt.readlines(None), ["AA\n", "BB\n", "CC"]) + txt.seek(0) + self.assertEqual(txt.readlines(5), ["AA\n", "BB\n"]) + + # read in amounts equal to TextIOWrapper._CHUNK_SIZE which is 128. + def test_read_by_chunk(self): + # make sure "\r\n" straddles 128 char boundary. + txt = self.TextIOWrapper(self.BytesIO(b"A" * 127 + b"\r\nB"), encoding="utf-8") + reads = "" + while True: + c = txt.read(128) + if not c: + break + reads += c + self.assertEqual(reads, "A"*127+"\nB") + + def test_writelines(self): + l = ['ab', 'cd', 'ef'] + buf = self.BytesIO() + txt = self.TextIOWrapper(buf, encoding="utf-8") + txt.writelines(l) + txt.flush() + self.assertEqual(buf.getvalue(), b'abcdef') + + def test_writelines_userlist(self): + l = UserList(['ab', 'cd', 'ef']) + buf = self.BytesIO() + txt = self.TextIOWrapper(buf, encoding="utf-8") + txt.writelines(l) + txt.flush() + self.assertEqual(buf.getvalue(), b'abcdef') + + def test_writelines_error(self): + txt = self.TextIOWrapper(self.BytesIO(), encoding="utf-8") + self.assertRaises(TypeError, txt.writelines, [1, 2, 3]) + self.assertRaises(TypeError, txt.writelines, None) + self.assertRaises(TypeError, txt.writelines, b'abc') + + def test_issue1395_1(self): + txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") + + # read one char at a time + reads = "" + while True: + c = txt.read(1) + if not c: + break + reads += c + self.assertEqual(reads, self.normalized) + + def test_issue1395_2(self): + txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") + txt._CHUNK_SIZE = 4 + + reads = "" + while True: + c = txt.read(4) + if not c: + break + reads += c + self.assertEqual(reads, self.normalized) + + def test_issue1395_3(self): + txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") + txt._CHUNK_SIZE = 4 + + reads = txt.read(4) + reads += txt.read(4) + reads += txt.readline() + reads += txt.readline() + reads += txt.readline() + self.assertEqual(reads, self.normalized) + + def test_issue1395_4(self): + txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") + txt._CHUNK_SIZE = 4 + + reads = txt.read(4) + reads += txt.read() + self.assertEqual(reads, self.normalized) + + def test_issue1395_5(self): + txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") + txt._CHUNK_SIZE = 4 + + reads = txt.read(4) + pos = txt.tell() + txt.seek(0) + txt.seek(pos) + self.assertEqual(txt.read(4), "BBB\n") + + def test_issue2282(self): + buffer = self.BytesIO(self.testdata) + txt = self.TextIOWrapper(buffer, encoding="ascii") + + self.assertEqual(buffer.seekable(), txt.seekable()) + + def test_append_bom(self): + # The BOM is not written again when appending to a non-empty file + filename = os_helper.TESTFN + for charset in ('utf-8-sig', 'utf-16', 'utf-32'): + with self.open(filename, 'w', encoding=charset) as f: + f.write('aaa') + pos = f.tell() + with self.open(filename, 'rb') as f: + self.assertEqual(f.read(), 'aaa'.encode(charset)) + + with self.open(filename, 'a', encoding=charset) as f: + f.write('xxx') + with self.open(filename, 'rb') as f: + self.assertEqual(f.read(), 'aaaxxx'.encode(charset)) + + def test_seek_bom(self): + # Same test, but when seeking manually + filename = os_helper.TESTFN + for charset in ('utf-8-sig', 'utf-16', 'utf-32'): + with self.open(filename, 'w', encoding=charset) as f: + f.write('aaa') + pos = f.tell() + with self.open(filename, 'r+', encoding=charset) as f: + f.seek(pos) + f.write('zzz') + f.seek(0) + f.write('bbb') + with self.open(filename, 'rb') as f: + self.assertEqual(f.read(), 'bbbzzz'.encode(charset)) + + def test_seek_append_bom(self): + # Same test, but first seek to the start and then to the end + filename = os_helper.TESTFN + for charset in ('utf-8-sig', 'utf-16', 'utf-32'): + with self.open(filename, 'w', encoding=charset) as f: + f.write('aaa') + with self.open(filename, 'a', encoding=charset) as f: + f.seek(0) + f.seek(0, self.SEEK_END) + f.write('xxx') + with self.open(filename, 'rb') as f: + self.assertEqual(f.read(), 'aaaxxx'.encode(charset)) + + def test_errors_property(self): + with self.open(os_helper.TESTFN, "w", encoding="utf-8") as f: + self.assertEqual(f.errors, "strict") + with self.open(os_helper.TESTFN, "w", encoding="utf-8", errors="replace") as f: + self.assertEqual(f.errors, "replace") + + @support.no_tracing + @threading_helper.requires_working_threading() + def test_threads_write(self): + # Issue6750: concurrent writes could duplicate data + event = threading.Event() + with self.open(os_helper.TESTFN, "w", encoding="utf-8", buffering=1) as f: + def run(n): + text = "Thread%03d\n" % n + event.wait() + f.write(text) + threads = [threading.Thread(target=run, args=(x,)) + for x in range(20)] + with threading_helper.start_threads(threads, event.set): + time.sleep(0.02) + with self.open(os_helper.TESTFN, encoding="utf-8") as f: + content = f.read() + for n in range(20): + self.assertEqual(content.count("Thread%03d\n" % n), 1) + + def test_flush_error_on_close(self): + # Test that text file is closed despite failed flush + # and that flush() is called before file closed. + txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") + closed = [] + def bad_flush(): + closed[:] = [txt.closed, txt.buffer.closed] + raise OSError() + txt.flush = bad_flush + self.assertRaises(OSError, txt.close) # exception not swallowed + self.assertTrue(txt.closed) + self.assertTrue(txt.buffer.closed) + self.assertTrue(closed) # flush() called + self.assertFalse(closed[0]) # flush() called before file closed + self.assertFalse(closed[1]) + txt.flush = lambda: None # break reference loop + + def test_close_error_on_close(self): + buffer = self.BytesIO(self.testdata) + def bad_flush(): + raise OSError('flush') + def bad_close(): + raise OSError('close') + buffer.close = bad_close + txt = self.TextIOWrapper(buffer, encoding="ascii") + txt.flush = bad_flush + with self.assertRaises(OSError) as err: # exception not swallowed + txt.close() + self.assertEqual(err.exception.args, ('close',)) + self.assertIsInstance(err.exception.__context__, OSError) + self.assertEqual(err.exception.__context__.args, ('flush',)) + self.assertFalse(txt.closed) + + # Silence destructor error + buffer.close = lambda: None + txt.flush = lambda: None + + def test_nonnormalized_close_error_on_close(self): + # Issue #21677 + buffer = self.BytesIO(self.testdata) + def bad_flush(): + raise non_existing_flush + def bad_close(): + raise non_existing_close + buffer.close = bad_close + txt = self.TextIOWrapper(buffer, encoding="ascii") + txt.flush = bad_flush + with self.assertRaises(NameError) as err: # exception not swallowed + txt.close() + self.assertIn('non_existing_close', str(err.exception)) + self.assertIsInstance(err.exception.__context__, NameError) + self.assertIn('non_existing_flush', str(err.exception.__context__)) + self.assertFalse(txt.closed) + + # Silence destructor error + buffer.close = lambda: None + txt.flush = lambda: None + + def test_multi_close(self): + txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") + txt.close() + txt.close() + txt.close() + self.assertRaises(ValueError, txt.flush) + + def test_unseekable(self): + txt = self.TextIOWrapper(self.MockUnseekableIO(self.testdata), encoding="utf-8") + self.assertRaises(self.UnsupportedOperation, txt.tell) + self.assertRaises(self.UnsupportedOperation, txt.seek, 0) + + def test_readonly_attributes(self): + txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii") + buf = self.BytesIO(self.testdata) + with self.assertRaises(AttributeError): + txt.buffer = buf + + def test_rawio(self): + # Issue #12591: TextIOWrapper must work with raw I/O objects, so + # that subprocess.Popen() can have the required unbuffered + # semantics with universal_newlines=True. + raw = self.MockRawIO([b'abc', b'def', b'ghi\njkl\nopq\n']) + txt = self.TextIOWrapper(raw, encoding='ascii', newline='\n') + # Reads + self.assertEqual(txt.read(4), 'abcd') + self.assertEqual(txt.readline(), 'efghi\n') + self.assertEqual(list(txt), ['jkl\n', 'opq\n']) + + def test_rawio_write_through(self): + # Issue #12591: with write_through=True, writes don't need a flush + raw = self.MockRawIO([b'abc', b'def', b'ghi\njkl\nopq\n']) + txt = self.TextIOWrapper(raw, encoding='ascii', newline='\n', + write_through=True) + txt.write('1') + txt.write('23\n4') + txt.write('5') + self.assertEqual(b''.join(raw._write_stack), b'123\n45') + + def test_bufio_write_through(self): + # Issue #21396: write_through=True doesn't force a flush() + # on the underlying binary buffered object. + flush_called, write_called = [], [] + class BufferedWriter(self.BufferedWriter): + def flush(self, *args, **kwargs): + flush_called.append(True) + return super().flush(*args, **kwargs) + def write(self, *args, **kwargs): + write_called.append(True) + return super().write(*args, **kwargs) + + rawio = self.BytesIO() + data = b"a" + bufio = BufferedWriter(rawio, len(data)*2) + textio = self.TextIOWrapper(bufio, encoding='ascii', + write_through=True) + # write to the buffered io but don't overflow the buffer + text = data.decode('ascii') + textio.write(text) + + # buffer.flush is not called with write_through=True + self.assertFalse(flush_called) + # buffer.write *is* called with write_through=True + self.assertTrue(write_called) + self.assertEqual(rawio.getvalue(), b"") # no flush + + write_called = [] # reset + textio.write(text * 10) # total content is larger than bufio buffer + self.assertTrue(write_called) + self.assertEqual(rawio.getvalue(), data * 11) # all flushed + + def test_reconfigure_write_through(self): + raw = self.MockRawIO([]) + t = self.TextIOWrapper(raw, encoding='ascii', newline='\n') + t.write('1') + t.reconfigure(write_through=True) # implied flush + self.assertEqual(t.write_through, True) + self.assertEqual(b''.join(raw._write_stack), b'1') + t.write('23') + self.assertEqual(b''.join(raw._write_stack), b'123') + t.reconfigure(write_through=False) + self.assertEqual(t.write_through, False) + t.write('45') + t.flush() + self.assertEqual(b''.join(raw._write_stack), b'12345') + # Keeping default value + t.reconfigure() + t.reconfigure(write_through=None) + self.assertEqual(t.write_through, False) + t.reconfigure(write_through=True) + t.reconfigure() + t.reconfigure(write_through=None) + self.assertEqual(t.write_through, True) + + def test_read_nonbytes(self): + # Issue #17106 + # Crash when underlying read() returns non-bytes + t = self.TextIOWrapper(self.StringIO('a'), encoding="utf-8") + self.assertRaises(TypeError, t.read, 1) + t = self.TextIOWrapper(self.StringIO('a'), encoding="utf-8") + self.assertRaises(TypeError, t.readline) + t = self.TextIOWrapper(self.StringIO('a'), encoding="utf-8") + self.assertRaises(TypeError, t.read) + + def test_illegal_encoder(self): + # Issue 31271: Calling write() while the return value of encoder's + # encode() is invalid shouldn't cause an assertion failure. + rot13 = codecs.lookup("rot13") + with support.swap_attr(rot13, '_is_text_encoding', True): + t = self.TextIOWrapper(self.BytesIO(b'foo'), encoding="rot13") + self.assertRaises(TypeError, t.write, 'bar') + + def test_illegal_decoder(self): + # Issue #17106 + # Bypass the early encoding check added in issue 20404 + def _make_illegal_wrapper(): + quopri = codecs.lookup("quopri") + quopri._is_text_encoding = True + try: + t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), + newline='\n', encoding="quopri") + finally: + quopri._is_text_encoding = False + return t + # Crash when decoder returns non-string + t = _make_illegal_wrapper() + self.assertRaises(TypeError, t.read, 1) + t = _make_illegal_wrapper() + self.assertRaises(TypeError, t.readline) + t = _make_illegal_wrapper() + self.assertRaises(TypeError, t.read) + + # Issue 31243: calling read() while the return value of decoder's + # getstate() is invalid should neither crash the interpreter nor + # raise a SystemError. + def _make_very_illegal_wrapper(getstate_ret_val): + class BadDecoder: + def getstate(self): + return getstate_ret_val + def _get_bad_decoder(dummy): + return BadDecoder() + quopri = codecs.lookup("quopri") + with support.swap_attr(quopri, 'incrementaldecoder', + _get_bad_decoder): + return _make_illegal_wrapper() + t = _make_very_illegal_wrapper(42) + self.assertRaises(TypeError, t.read, 42) + t = _make_very_illegal_wrapper(()) + self.assertRaises(TypeError, t.read, 42) + t = _make_very_illegal_wrapper((1, 2)) + self.assertRaises(TypeError, t.read, 42) + + def _check_create_at_shutdown(self, **kwargs): + # Issue #20037: creating a TextIOWrapper at shutdown + # shouldn't crash the interpreter. + iomod = self.io.__name__ + code = """if 1: + import codecs + import {iomod} as io + + # Avoid looking up codecs at shutdown + codecs.lookup('utf-8') + + class C: + def __del__(self): + io.TextIOWrapper(io.BytesIO(), **{kwargs}) + print("ok") + c = C() + """.format(iomod=iomod, kwargs=kwargs) + return assert_python_ok("-c", code) + + def test_create_at_shutdown_without_encoding(self): + rc, out, err = self._check_create_at_shutdown() + if err: + # Can error out with a RuntimeError if the module state + # isn't found. + self.assertIn(self.shutdown_error, err.decode()) + else: + self.assertEqual("ok", out.decode().strip()) + + def test_create_at_shutdown_with_encoding(self): + rc, out, err = self._check_create_at_shutdown(encoding='utf-8', + errors='strict') + self.assertFalse(err) + self.assertEqual("ok", out.decode().strip()) + + def test_read_byteslike(self): + r = MemviewBytesIO(b'Just some random string\n') + t = self.TextIOWrapper(r, 'utf-8') + + # TextIOwrapper will not read the full string, because + # we truncate it to a multiple of the native int size + # so that we can construct a more complex memoryview. + bytes_val = _to_memoryview(r.getvalue()).tobytes() + + self.assertEqual(t.read(200), bytes_val.decode('utf-8')) + + def test_issue22849(self): + class F(object): + def readable(self): return True + def writable(self): return True + def seekable(self): return True + + for i in range(10): + try: + self.TextIOWrapper(F(), encoding='utf-8') + except Exception: + pass + + F.tell = lambda x: 0 + t = self.TextIOWrapper(F(), encoding='utf-8') + + def test_reconfigure_locale(self): + wrapper = self.TextIOWrapper(self.BytesIO(b"test")) + wrapper.reconfigure(encoding="locale") + + def test_reconfigure_encoding_read(self): + # latin1 -> utf8 + # (latin1 can decode utf-8 encoded string) + data = 'abc\xe9\n'.encode('latin1') + 'd\xe9f\n'.encode('utf8') + raw = self.BytesIO(data) + txt = self.TextIOWrapper(raw, encoding='latin1', newline='\n') + self.assertEqual(txt.readline(), 'abc\xe9\n') + with self.assertRaises(self.UnsupportedOperation): + txt.reconfigure(encoding='utf-8') + with self.assertRaises(self.UnsupportedOperation): + txt.reconfigure(newline=None) + + def test_reconfigure_write_fromascii(self): + # ascii has a specific encodefunc in the C implementation, + # but utf-8-sig has not. Make sure that we get rid of the + # cached encodefunc when we switch encoders. + raw = self.BytesIO() + txt = self.TextIOWrapper(raw, encoding='ascii', newline='\n') + txt.write('foo\n') + txt.reconfigure(encoding='utf-8-sig') + txt.write('\xe9\n') + txt.flush() + self.assertEqual(raw.getvalue(), b'foo\n\xc3\xa9\n') + + def test_reconfigure_write(self): + # latin -> utf8 + raw = self.BytesIO() + txt = self.TextIOWrapper(raw, encoding='latin1', newline='\n') + txt.write('abc\xe9\n') + txt.reconfigure(encoding='utf-8') + self.assertEqual(raw.getvalue(), b'abc\xe9\n') + txt.write('d\xe9f\n') + txt.flush() + self.assertEqual(raw.getvalue(), b'abc\xe9\nd\xc3\xa9f\n') + + # ascii -> utf-8-sig: ensure that no BOM is written in the middle of + # the file + raw = self.BytesIO() + txt = self.TextIOWrapper(raw, encoding='ascii', newline='\n') + txt.write('abc\n') + txt.reconfigure(encoding='utf-8-sig') + txt.write('d\xe9f\n') + txt.flush() + self.assertEqual(raw.getvalue(), b'abc\nd\xc3\xa9f\n') + + def test_reconfigure_write_non_seekable(self): + raw = self.BytesIO() + raw.seekable = lambda: False + raw.seek = None + txt = self.TextIOWrapper(raw, encoding='ascii', newline='\n') + txt.write('abc\n') + txt.reconfigure(encoding='utf-8-sig') + txt.write('d\xe9f\n') + txt.flush() + + # If the raw stream is not seekable, there'll be a BOM + self.assertEqual(raw.getvalue(), b'abc\n\xef\xbb\xbfd\xc3\xa9f\n') + + def test_reconfigure_defaults(self): + txt = self.TextIOWrapper(self.BytesIO(), 'ascii', 'replace', '\n') + txt.reconfigure(encoding=None) + self.assertEqual(txt.encoding, 'ascii') + self.assertEqual(txt.errors, 'replace') + txt.write('LF\n') + + txt.reconfigure(newline='\r\n') + self.assertEqual(txt.encoding, 'ascii') + self.assertEqual(txt.errors, 'replace') + + txt.reconfigure(errors='ignore') + self.assertEqual(txt.encoding, 'ascii') + self.assertEqual(txt.errors, 'ignore') + txt.write('CRLF\n') + + txt.reconfigure(encoding='utf-8', newline=None) + self.assertEqual(txt.errors, 'strict') + txt.seek(0) + self.assertEqual(txt.read(), 'LF\nCRLF\n') + + self.assertEqual(txt.detach().getvalue(), b'LF\nCRLF\r\n') + + def test_reconfigure_errors(self): + txt = self.TextIOWrapper(self.BytesIO(), 'ascii', 'replace', '\r') + with self.assertRaises(TypeError): # there was a crash + txt.reconfigure(encoding=42) + if self.is_C: + with self.assertRaises(UnicodeEncodeError): + txt.reconfigure(encoding='\udcfe') + with self.assertRaises(LookupError): + txt.reconfigure(encoding='locale\0') + # TODO: txt.reconfigure(encoding='utf-8\0') + # TODO: txt.reconfigure(encoding='nonexisting') + with self.assertRaises(TypeError): + txt.reconfigure(errors=42) + if self.is_C: + with self.assertRaises(UnicodeEncodeError): + txt.reconfigure(errors='\udcfe') + # TODO: txt.reconfigure(errors='ignore\0') + # TODO: txt.reconfigure(errors='nonexisting') + with self.assertRaises(TypeError): + txt.reconfigure(newline=42) + with self.assertRaises(ValueError): + txt.reconfigure(newline='\udcfe') + with self.assertRaises(ValueError): + txt.reconfigure(newline='xyz') + if not self.is_C: + # TODO: Should fail in C too. + with self.assertRaises(ValueError): + txt.reconfigure(newline='\n\0') + if self.is_C: + # TODO: Use __bool__(), not __index__(). + with self.assertRaises(ZeroDivisionError): + txt.reconfigure(line_buffering=BadIndex()) + with self.assertRaises(OverflowError): + txt.reconfigure(line_buffering=2**1000) + with self.assertRaises(ZeroDivisionError): + txt.reconfigure(write_through=BadIndex()) + with self.assertRaises(OverflowError): + txt.reconfigure(write_through=2**1000) + with self.assertRaises(ZeroDivisionError): # there was a crash + txt.reconfigure(line_buffering=BadIndex(), + write_through=BadIndex()) + self.assertEqual(txt.encoding, 'ascii') + self.assertEqual(txt.errors, 'replace') + self.assertIs(txt.line_buffering, False) + self.assertIs(txt.write_through, False) + + txt.reconfigure(encoding='latin1', errors='ignore', newline='\r\n', + line_buffering=True, write_through=True) + self.assertEqual(txt.encoding, 'latin1') + self.assertEqual(txt.errors, 'ignore') + self.assertIs(txt.line_buffering, True) + self.assertIs(txt.write_through, True) + + def test_reconfigure_newline(self): + raw = self.BytesIO(b'CR\rEOF') + txt = self.TextIOWrapper(raw, 'ascii', newline='\n') + txt.reconfigure(newline=None) + self.assertEqual(txt.readline(), 'CR\n') + raw = self.BytesIO(b'CR\rEOF') + txt = self.TextIOWrapper(raw, 'ascii', newline='\n') + txt.reconfigure(newline='') + self.assertEqual(txt.readline(), 'CR\r') + raw = self.BytesIO(b'CR\rLF\nEOF') + txt = self.TextIOWrapper(raw, 'ascii', newline='\r') + txt.reconfigure(newline='\n') + self.assertEqual(txt.readline(), 'CR\rLF\n') + raw = self.BytesIO(b'LF\nCR\rEOF') + txt = self.TextIOWrapper(raw, 'ascii', newline='\n') + txt.reconfigure(newline='\r') + self.assertEqual(txt.readline(), 'LF\nCR\r') + raw = self.BytesIO(b'CR\rCRLF\r\nEOF') + txt = self.TextIOWrapper(raw, 'ascii', newline='\r') + txt.reconfigure(newline='\r\n') + self.assertEqual(txt.readline(), 'CR\rCRLF\r\n') + + txt = self.TextIOWrapper(self.BytesIO(), 'ascii', newline='\r') + txt.reconfigure(newline=None) + txt.write('linesep\n') + txt.reconfigure(newline='') + txt.write('LF\n') + txt.reconfigure(newline='\n') + txt.write('LF\n') + txt.reconfigure(newline='\r') + txt.write('CR\n') + txt.reconfigure(newline='\r\n') + txt.write('CRLF\n') + expected = 'linesep' + os.linesep + 'LF\nLF\nCR\rCRLF\r\n' + self.assertEqual(txt.detach().getvalue().decode('ascii'), expected) + + def test_issue25862(self): + # Assertion failures occurred in tell() after read() and write(). + t = self.TextIOWrapper(self.BytesIO(b'test'), encoding='ascii') + t.read(1) + t.read() + t.tell() + t = self.TextIOWrapper(self.BytesIO(b'test'), encoding='ascii') + t.read(1) + t.write('x') + t.tell() + + def test_issue35928(self): + p = self.BufferedRWPair(self.BytesIO(b'foo\nbar\n'), self.BytesIO()) + f = self.TextIOWrapper(p) + res = f.readline() + self.assertEqual(res, 'foo\n') + f.write(res) + self.assertEqual(res + f.readline(), 'foo\nbar\n') + + def test_pickling_subclass(self): + global MyTextIO + class MyTextIO(self.TextIOWrapper): + def __init__(self, raw, tag): + super().__init__(raw) + self.tag = tag + def __getstate__(self): + return self.tag, self.buffer.getvalue() + def __setstate__(slf, state): + tag, value = state + slf.__init__(self.BytesIO(value), tag) + + raw = self.BytesIO(b'data') + txt = MyTextIO(raw, 'ham') + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + pickled = pickle.dumps(txt, proto) + newtxt = pickle.loads(pickled) + self.assertEqual(newtxt.buffer.getvalue(), b'data') + self.assertEqual(newtxt.tag, 'ham') + del MyTextIO + + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_read_non_blocking(self): + import os + r, w = os.pipe() + try: + os.set_blocking(r, False) + with self.io.open(r, 'rt') as textfile: + r = None + # Nothing has been written so a non-blocking read raises a BlockingIOError exception. + with self.assertRaises(BlockingIOError): + textfile.read() + finally: + if r is not None: + os.close(r) + os.close(w) + + +class MemviewBytesIO(io.BytesIO): + '''A BytesIO object whose read method returns memoryviews + rather than bytes''' + + def read1(self, len_): + return _to_memoryview(super().read1(len_)) + + def read(self, len_): + return _to_memoryview(super().read(len_)) + +def _to_memoryview(buf): + '''Convert bytes-object *buf* to a non-trivial memoryview''' + + arr = array.array('i') + idx = len(buf) - len(buf) % arr.itemsize + arr.frombytes(buf[:idx]) + return memoryview(arr) + + +class CTextIOWrapperTest(TextIOWrapperTest, CTestCase): + shutdown_error = "LookupError: unknown encoding: ascii" + + def test_initialization(self): + r = self.BytesIO(b"\xc3\xa9\n\n") + b = self.BufferedReader(r, 1000) + t = self.TextIOWrapper(b, encoding="utf-8") + self.assertRaises(ValueError, t.__init__, b, encoding="utf-8", newline='xyzzy') + self.assertRaises(ValueError, t.read) + + t = self.TextIOWrapper.__new__(self.TextIOWrapper) + self.assertRaises(Exception, repr, t) + + def test_garbage_collection(self): + # C TextIOWrapper objects are collected, and collecting them flushes + # all data to disk. + # The Python version has __del__, so it ends in gc.garbage instead. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ResourceWarning) + rawio = self.FileIO(os_helper.TESTFN, "wb") + b = self.BufferedWriter(rawio) + t = self.TextIOWrapper(b, encoding="ascii") + t.write("456def") + t.x = t + wr = weakref.ref(t) + del t + support.gc_collect() + self.assertIsNone(wr(), wr) + with self.open(os_helper.TESTFN, "rb") as f: + self.assertEqual(f.read(), b"456def") + + def test_rwpair_cleared_before_textio(self): + # Issue 13070: TextIOWrapper's finalization would crash when called + # after the reference to the underlying BufferedRWPair's writer got + # cleared by the GC. + for i in range(1000): + b1 = self.BufferedRWPair(self.MockRawIO(), self.MockRawIO()) + t1 = self.TextIOWrapper(b1, encoding="ascii") + b2 = self.BufferedRWPair(self.MockRawIO(), self.MockRawIO()) + t2 = self.TextIOWrapper(b2, encoding="ascii") + # circular references + t1.buddy = t2 + t2.buddy = t1 + support.gc_collect() + + def test_del__CHUNK_SIZE_SystemError(self): + t = self.TextIOWrapper(self.BytesIO(), encoding='ascii') + with self.assertRaises(AttributeError): + del t._CHUNK_SIZE + + def test_internal_buffer_size(self): + # bpo-43260: TextIOWrapper's internal buffer should not store + # data larger than chunk size. + chunk_size = 8192 # default chunk size, updated later + + class MockIO(self.MockRawIO): + def write(self, data): + if len(data) > chunk_size: + raise RuntimeError + return super().write(data) + + buf = MockIO() + t = self.TextIOWrapper(buf, encoding="ascii") + chunk_size = t._CHUNK_SIZE + t.write("abc") + t.write("def") + # default chunk size is 8192 bytes so t don't write data to buf. + self.assertEqual([], buf._write_stack) + + with self.assertRaises(RuntimeError): + t.write("x"*(chunk_size+1)) + + self.assertEqual([b"abcdef"], buf._write_stack) + t.write("ghi") + t.write("x"*chunk_size) + self.assertEqual([b"abcdef", b"ghi", b"x"*chunk_size], buf._write_stack) + + def test_issue119506(self): + chunk_size = 8192 + + class MockIO(self.MockRawIO): + written = False + def write(self, data): + if not self.written: + self.written = True + t.write("middle") + return super().write(data) + + buf = MockIO() + t = self.TextIOWrapper(buf) + t.write("abc") + t.write("def") + # writing data which size >= chunk_size cause flushing buffer before write. + t.write("g" * chunk_size) + t.flush() + + self.assertEqual([b"abcdef", b"middle", b"g"*chunk_size], + buf._write_stack) + + def test_issue142594(self): + wrapper = None + detached = False + class ReentrantRawIO(self.RawIOBase): + @property + def closed(self): + nonlocal detached + if wrapper is not None and not detached: + detached = True + wrapper.detach() + return False + + raw = ReentrantRawIO() + wrapper = self.TextIOWrapper(raw) + wrapper.close() # should not crash + + def test_reentrant_detach_during_flush(self): + # gh-143008: Reentrant detach() during flush should not crash. + + class DetachOnce(self.BufferedRandom): + wrapper = None + + def detach_once(self): + original = self.wrapper + self.wrapper = None + if original is not None: + original.detach() + original.flush() + + class DetachOnFlush(DetachOnce): + def flush(self): + self.detach_once() + + class DetachOnWrite(DetachOnce): + def write(self, b): + self.detach_once() + return len(b) + + # Separate reference for after detach_once. + wrapper = None + + def make_text(buffer): + nonlocal wrapper + buffer.wrapper = self.TextIOWrapper(buffer, encoding='utf-8') + wrapper = buffer.wrapper + + # Many calls could result in the same null self->buffer crash. + tests = [ + ('truncate', lambda: wrapper.truncate(0)), + ('close', lambda: wrapper.close()), + ('detach', lambda: wrapper.detach()), + ('seek', lambda: wrapper.seek(0)), + ('tell', lambda: wrapper.tell()), + ('reconfigure', lambda: wrapper.reconfigure(line_buffering=True)), + ] + for name, method in tests: + with self.subTest(name): + make_text(DetachOnFlush(self.MockRawIO())) + self.assertRaisesRegex(ValueError, "detached", method) + + # Should not crash. + with self.subTest('read via writeflush'): + make_text(DetachOnWrite(self.MockRawIO())) + wrapper.write('x') + self.assertRaisesRegex(ValueError, "detached", wrapper.read) + + +class PyTextIOWrapperTest(TextIOWrapperTest, PyTestCase): + shutdown_error = "LookupError: unknown encoding: ascii" + + +class IncrementalNewlineDecoderTest: + + def check_newline_decoding_utf8(self, decoder): + # UTF-8 specific tests for a newline decoder + def _check_decode(b, s, **kwargs): + # We exercise getstate() / setstate() as well as decode() + state = decoder.getstate() + self.assertEqual(decoder.decode(b, **kwargs), s) + decoder.setstate(state) + self.assertEqual(decoder.decode(b, **kwargs), s) + + _check_decode(b'\xe8\xa2\x88', "\u8888") + + _check_decode(b'\xe8', "") + _check_decode(b'\xa2', "") + _check_decode(b'\x88', "\u8888") + + _check_decode(b'\xe8', "") + _check_decode(b'\xa2', "") + _check_decode(b'\x88', "\u8888") + + _check_decode(b'\xe8', "") + self.assertRaises(UnicodeDecodeError, decoder.decode, b'', final=True) + + decoder.reset() + _check_decode(b'\n', "\n") + _check_decode(b'\r', "") + _check_decode(b'', "\n", final=True) + _check_decode(b'\r', "\n", final=True) + + _check_decode(b'\r', "") + _check_decode(b'a', "\na") + + _check_decode(b'\r\r\n', "\n\n") + _check_decode(b'\r', "") + _check_decode(b'\r', "\n") + _check_decode(b'\na', "\na") + + _check_decode(b'\xe8\xa2\x88\r\n', "\u8888\n") + _check_decode(b'\xe8\xa2\x88', "\u8888") + _check_decode(b'\n', "\n") + _check_decode(b'\xe8\xa2\x88\r', "\u8888") + _check_decode(b'\n', "\n") + + def check_newline_decoding(self, decoder, encoding): + result = [] + if encoding is not None: + encoder = codecs.getincrementalencoder(encoding)() + def _decode_bytewise(s): + # Decode one byte at a time + for b in encoder.encode(s): + result.append(decoder.decode(bytes([b]))) + else: + encoder = None + def _decode_bytewise(s): + # Decode one char at a time + for c in s: + result.append(decoder.decode(c)) + self.assertEqual(decoder.newlines, None) + _decode_bytewise("abc\n\r") + self.assertEqual(decoder.newlines, '\n') + _decode_bytewise("\nabc") + self.assertEqual(decoder.newlines, ('\n', '\r\n')) + _decode_bytewise("abc\r") + self.assertEqual(decoder.newlines, ('\n', '\r\n')) + _decode_bytewise("abc") + self.assertEqual(decoder.newlines, ('\r', '\n', '\r\n')) + _decode_bytewise("abc\r") + self.assertEqual("".join(result), "abc\n\nabcabc\nabcabc") + decoder.reset() + input = "abc" + if encoder is not None: + encoder.reset() + input = encoder.encode(input) + self.assertEqual(decoder.decode(input), "abc") + self.assertEqual(decoder.newlines, None) + + def test_newline_decoder(self): + encodings = ( + # None meaning the IncrementalNewlineDecoder takes unicode input + # rather than bytes input + None, 'utf-8', 'latin-1', + 'utf-16', 'utf-16-le', 'utf-16-be', + 'utf-32', 'utf-32-le', 'utf-32-be', + ) + for enc in encodings: + decoder = enc and codecs.getincrementaldecoder(enc)() + decoder = self.IncrementalNewlineDecoder(decoder, translate=True) + self.check_newline_decoding(decoder, enc) + decoder = codecs.getincrementaldecoder("utf-8")() + decoder = self.IncrementalNewlineDecoder(decoder, translate=True) + self.check_newline_decoding_utf8(decoder) + self.assertRaises(TypeError, decoder.setstate, 42) + + def test_newline_bytes(self): + # Issue 5433: Excessive optimization in IncrementalNewlineDecoder + def _check(dec): + self.assertEqual(dec.newlines, None) + self.assertEqual(dec.decode("\u0D00"), "\u0D00") + self.assertEqual(dec.newlines, None) + self.assertEqual(dec.decode("\u0A00"), "\u0A00") + self.assertEqual(dec.newlines, None) + dec = self.IncrementalNewlineDecoder(None, translate=False) + _check(dec) + dec = self.IncrementalNewlineDecoder(None, translate=True) + _check(dec) + + def test_translate(self): + # issue 35062 + for translate in (-2, -1, 1, 2): + decoder = codecs.getincrementaldecoder("utf-8")() + decoder = self.IncrementalNewlineDecoder(decoder, translate) + self.check_newline_decoding_utf8(decoder) + decoder = codecs.getincrementaldecoder("utf-8")() + decoder = self.IncrementalNewlineDecoder(decoder, translate=0) + self.assertEqual(decoder.decode(b"\r\r\n"), "\r\r\n") + +class CIncrementalNewlineDecoderTest(IncrementalNewlineDecoderTest, unittest.TestCase): + IncrementalNewlineDecoder = io.IncrementalNewlineDecoder + + @support.cpython_only + def test_uninitialized(self): + uninitialized = self.IncrementalNewlineDecoder.__new__( + self.IncrementalNewlineDecoder) + self.assertRaises(ValueError, uninitialized.decode, b'bar') + self.assertRaises(ValueError, uninitialized.getstate) + self.assertRaises(ValueError, uninitialized.setstate, (b'foo', 0)) + self.assertRaises(ValueError, uninitialized.reset) + + +class PyIncrementalNewlineDecoderTest(IncrementalNewlineDecoderTest, unittest.TestCase): + IncrementalNewlineDecoder = pyio.IncrementalNewlineDecoder diff --git a/Lib/test/test_univnewlines.py b/Lib/test/test_io/test_univnewlines.py similarity index 100% rename from Lib/test/test_univnewlines.py rename to Lib/test/test_io/test_univnewlines.py diff --git a/Lib/test/test_io/utils.py b/Lib/test/test_io/utils.py new file mode 100644 index 00000000000000..3b1faec2140fbc --- /dev/null +++ b/Lib/test/test_io/utils.py @@ -0,0 +1,318 @@ +import array +import threading +import time +import unittest + +import io # C implementation of io +import _pyio as pyio # Python implementation of io + + +try: + import ctypes +except ImportError: + def byteslike(*pos, **kw): + return array.array("b", bytes(*pos, **kw)) +else: + class EmptyStruct(ctypes.Structure): + pass + + def byteslike(*pos, **kw): + """Create a bytes-like object having no string or sequence methods""" + data = bytes(*pos, **kw) + obj = EmptyStruct() + ctypes.resize(obj, len(data)) + memoryview(obj).cast("B")[:] = data + return obj + + +class MockRawIOWithoutRead: + """A RawIO implementation without read(), so as to exercise the default + RawIO.read() which calls readinto().""" + + def __init__(self, read_stack=()): + self._read_stack = list(read_stack) + self._write_stack = [] + self._reads = 0 + self._extraneous_reads = 0 + + def write(self, b): + self._write_stack.append(bytes(b)) + return len(b) + + def writable(self): + return True + + def fileno(self): + return 42 + + def readable(self): + return True + + def seekable(self): + return True + + def seek(self, pos, whence): + return 0 # wrong but we gotta return something + + def tell(self): + return 0 # same comment as above + + def readinto(self, buf): + self._reads += 1 + max_len = len(buf) + try: + data = self._read_stack[0] + except IndexError: + self._extraneous_reads += 1 + return 0 + if data is None: + del self._read_stack[0] + return None + n = len(data) + if len(data) <= max_len: + del self._read_stack[0] + buf[:n] = data + return n + else: + buf[:] = data[:max_len] + self._read_stack[0] = data[max_len:] + return max_len + + def truncate(self, pos=None): + return pos + +class CMockRawIOWithoutRead(MockRawIOWithoutRead, io.RawIOBase): + pass + +class PyMockRawIOWithoutRead(MockRawIOWithoutRead, pyio.RawIOBase): + pass + + +class MockRawIO(MockRawIOWithoutRead): + + def read(self, n=None): + self._reads += 1 + try: + return self._read_stack.pop(0) + except: + self._extraneous_reads += 1 + return b"" + +class CMockRawIO(MockRawIO, io.RawIOBase): + pass + +class PyMockRawIO(MockRawIO, pyio.RawIOBase): + pass + + +class MisbehavedRawIO(MockRawIO): + def write(self, b): + return super().write(b) * 2 + + def read(self, n=None): + return super().read(n) * 2 + + def seek(self, pos, whence): + return -123 + + def tell(self): + return -456 + + def readinto(self, buf): + super().readinto(buf) + return len(buf) * 5 + +class CMisbehavedRawIO(MisbehavedRawIO, io.RawIOBase): + pass + +class PyMisbehavedRawIO(MisbehavedRawIO, pyio.RawIOBase): + pass + + +class SlowFlushRawIO(MockRawIO): + def __init__(self): + super().__init__() + self.in_flush = threading.Event() + + def flush(self): + self.in_flush.set() + time.sleep(0.25) + +class CSlowFlushRawIO(SlowFlushRawIO, io.RawIOBase): + pass + +class PySlowFlushRawIO(SlowFlushRawIO, pyio.RawIOBase): + pass + + +class CloseFailureIO(MockRawIO): + closed = 0 + + def close(self): + if not self.closed: + self.closed = 1 + raise OSError + +class CCloseFailureIO(CloseFailureIO, io.RawIOBase): + pass + +class PyCloseFailureIO(CloseFailureIO, pyio.RawIOBase): + pass + + +class MockFileIO: + + def __init__(self, data): + self.read_history = [] + super().__init__(data) + + def read(self, n=None): + res = super().read(n) + self.read_history.append(None if res is None else len(res)) + return res + + def readinto(self, b): + res = super().readinto(b) + self.read_history.append(res) + return res + +class CMockFileIO(MockFileIO, io.BytesIO): + pass + +class PyMockFileIO(MockFileIO, pyio.BytesIO): + pass + + +class MockUnseekableIO: + def seekable(self): + return False + + def seek(self, *args): + raise self.UnsupportedOperation("not seekable") + + def tell(self, *args): + raise self.UnsupportedOperation("not seekable") + + def truncate(self, *args): + raise self.UnsupportedOperation("not seekable") + +class CMockUnseekableIO(MockUnseekableIO, io.BytesIO): + UnsupportedOperation = io.UnsupportedOperation + +class PyMockUnseekableIO(MockUnseekableIO, pyio.BytesIO): + UnsupportedOperation = pyio.UnsupportedOperation + + +class MockCharPseudoDevFileIO(MockFileIO): + # GH-95782 + # ftruncate() does not work on these special files (and CPython then raises + # appropriate exceptions), so truncate() does not have to be accounted for + # here. + def __init__(self, data): + super().__init__(data) + + def seek(self, *args): + return 0 + + def tell(self, *args): + return 0 + +class CMockCharPseudoDevFileIO(MockCharPseudoDevFileIO, io.BytesIO): + pass + +class PyMockCharPseudoDevFileIO(MockCharPseudoDevFileIO, pyio.BytesIO): + pass + + +class MockNonBlockWriterIO: + + def __init__(self): + self._write_stack = [] + self._blocker_char = None + + def pop_written(self): + s = b"".join(self._write_stack) + self._write_stack[:] = [] + return s + + def block_on(self, char): + """Block when a given char is encountered.""" + self._blocker_char = char + + def readable(self): + return True + + def seekable(self): + return True + + def seek(self, pos, whence=0): + # naive implementation, enough for tests + return 0 + + def writable(self): + return True + + def write(self, b): + b = bytes(b) + n = -1 + if self._blocker_char: + try: + n = b.index(self._blocker_char) + except ValueError: + pass + else: + if n > 0: + # write data up to the first blocker + self._write_stack.append(b[:n]) + return n + else: + # cancel blocker and indicate would block + self._blocker_char = None + return None + self._write_stack.append(b) + return len(b) + +class CMockNonBlockWriterIO(MockNonBlockWriterIO, io.RawIOBase): + BlockingIOError = io.BlockingIOError + +class PyMockNonBlockWriterIO(MockNonBlockWriterIO, pyio.RawIOBase): + BlockingIOError = pyio.BlockingIOError + + +# Build classes which point to all the right mocks per io implementation +class CTestCase(unittest.TestCase): + io = io + is_C = True + + MockRawIO = CMockRawIO + MisbehavedRawIO = CMisbehavedRawIO + MockFileIO = CMockFileIO + CloseFailureIO = CCloseFailureIO + MockNonBlockWriterIO = CMockNonBlockWriterIO + MockUnseekableIO = CMockUnseekableIO + MockRawIOWithoutRead = CMockRawIOWithoutRead + SlowFlushRawIO = CSlowFlushRawIO + MockCharPseudoDevFileIO = CMockCharPseudoDevFileIO + + # Use the class as a proxy to the io module members. + def __getattr__(self, name): + return getattr(io, name) + + +class PyTestCase(unittest.TestCase): + io = pyio + is_C = False + + MockRawIO = PyMockRawIO + MisbehavedRawIO = PyMisbehavedRawIO + MockFileIO = PyMockFileIO + CloseFailureIO = PyCloseFailureIO + MockNonBlockWriterIO = PyMockNonBlockWriterIO + MockUnseekableIO = PyMockUnseekableIO + MockRawIOWithoutRead = PyMockRawIOWithoutRead + SlowFlushRawIO = PySlowFlushRawIO + MockCharPseudoDevFileIO = PyMockCharPseudoDevFileIO + + # Use the class as a proxy to the _pyio module members. + def __getattr__(self, name): + return getattr(pyio, name) diff --git a/Lib/test/test_ioctl.py b/Lib/test/test_ioctl.py index 277d2fc99eaec6..0bcabd579a1b30 100644 --- a/Lib/test/test_ioctl.py +++ b/Lib/test/test_ioctl.py @@ -139,6 +139,7 @@ def setUp(self): self.addCleanup(os.close, self.master_fd) @unittest.skipUnless(hasattr(termios, 'TCFLSH'), 'requires termios.TCFLSH') + @unittest.skipIf(sys.platform == 'cygwin', 'test failed on Cygwin') def test_ioctl_clear_input_or_output(self): wfd = self.slave_fd rfd = self.master_fd diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index db1c38243e2268..3f017b97dc28a3 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -12,6 +12,7 @@ import pickle import ipaddress import weakref +from collections.abc import Iterator from test.support import LARGEST, SMALLEST @@ -1472,18 +1473,27 @@ def testGetSupernet4(self): self.ipv6_scoped_network.supernet(new_prefix=62)) def testHosts(self): + hosts = self.ipv4_network.hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual(ipaddress.IPv4Address('1.2.3.1'), next(hosts)) hosts = list(self.ipv4_network.hosts()) self.assertEqual(254, len(hosts)) self.assertEqual(ipaddress.IPv4Address('1.2.3.1'), hosts[0]) self.assertEqual(ipaddress.IPv4Address('1.2.3.254'), hosts[-1]) ipv6_network = ipaddress.IPv6Network('2001:658:22a:cafe::/120') + hosts = ipv6_network.hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::1'), next(hosts)) hosts = list(ipv6_network.hosts()) self.assertEqual(255, len(hosts)) self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::1'), hosts[0]) self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::ff'), hosts[-1]) ipv6_scoped_network = ipaddress.IPv6Network('2001:658:22a:cafe::%scope/120') + hosts = ipv6_scoped_network.hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual((ipaddress.IPv6Address('2001:658:22a:cafe::1')), next(hosts)) hosts = list(ipv6_scoped_network.hosts()) self.assertEqual(255, len(hosts)) self.assertEqual(ipaddress.IPv6Address('2001:658:22a:cafe::1'), hosts[0]) @@ -1494,6 +1504,12 @@ def testHosts(self): ipaddress.IPv4Address('2.0.0.1')] str_args = '2.0.0.0/31' tpl_args = ('2.0.0.0', 31) + hosts = ipaddress.ip_network(str_args).hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual(next(hosts), addrs[0]) + hosts = ipaddress.ip_network(tpl_args).hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual(next(hosts), addrs[0]) self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts())) self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts())) self.assertEqual(list(ipaddress.ip_network(str_args).hosts()), @@ -1503,6 +1519,12 @@ def testHosts(self): addrs = [ipaddress.IPv4Address('1.2.3.4')] str_args = '1.2.3.4/32' tpl_args = ('1.2.3.4', 32) + hosts = ipaddress.ip_network(str_args).hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual(next(hosts), addrs[0]) + hosts = ipaddress.ip_network(tpl_args).hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual(next(hosts), addrs[0]) self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts())) self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts())) self.assertEqual(list(ipaddress.ip_network(str_args).hosts()), @@ -1512,6 +1534,12 @@ def testHosts(self): ipaddress.IPv6Address('2001:658:22a:cafe::1')] str_args = '2001:658:22a:cafe::/127' tpl_args = ('2001:658:22a:cafe::', 127) + hosts = ipaddress.ip_network(str_args).hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual(next(hosts), addrs[0]) + hosts = ipaddress.ip_network(tpl_args).hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual(next(hosts), addrs[0]) self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts())) self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts())) self.assertEqual(list(ipaddress.ip_network(str_args).hosts()), @@ -1520,6 +1548,12 @@ def testHosts(self): addrs = [ipaddress.IPv6Address('2001:658:22a:cafe::1'), ] str_args = '2001:658:22a:cafe::1/128' tpl_args = ('2001:658:22a:cafe::1', 128) + hosts = ipaddress.ip_network(str_args).hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual(next(hosts), addrs[0]) + hosts = ipaddress.ip_network(tpl_args).hosts() + self.assertIsInstance(hosts, Iterator) + self.assertEqual(next(hosts), addrs[0]) self.assertEqual(addrs, list(ipaddress.ip_network(str_args).hosts())) self.assertEqual(addrs, list(ipaddress.ip_network(tpl_args).hosts())) self.assertEqual(list(ipaddress.ip_network(str_args).hosts()), @@ -2269,6 +2303,10 @@ def testReservedIpv4(self): self.assertEqual(False, ipaddress.ip_network('240.0.0.0').is_multicast) self.assertEqual(True, ipaddress.ip_network('240.0.0.0').is_reserved) + self.assertTrue(ipaddress.ip_interface('0.0.0.0/32').is_unspecified) + self.assertFalse(ipaddress.ip_interface('0.0.0.0/31').is_unspecified) + self.assertFalse(ipaddress.ip_interface('1.2.3.4/32').is_unspecified) + self.assertEqual(True, ipaddress.ip_interface( '192.168.1.1/17').is_private) self.assertEqual(False, ipaddress.ip_network('192.169.0.0').is_private) @@ -2817,5 +2855,15 @@ def testNetworkV6HashCollisions(self): ) +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(ipaddress, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py index f440fc28ee7b7d..d97535ba46e677 100644 --- a/Lib/test/test_isinstance.py +++ b/Lib/test/test_isinstance.py @@ -317,6 +317,7 @@ def __bases__(self): self.assertRaises(RecursionError, issubclass, int, X()) self.assertRaises(RecursionError, isinstance, 1, X()) + @support.skip_if_unlimited_stack_size @support.skip_emscripten_stack_overflow() @support.skip_wasi_stack_overflow() def test_infinite_recursion_via_bases_tuple(self): @@ -328,6 +329,7 @@ def __getattr__(self, attr): with self.assertRaises(RecursionError): issubclass(Failure(), int) + @support.skip_if_unlimited_stack_size @support.skip_emscripten_stack_overflow() @support.skip_wasi_stack_overflow() def test_infinite_cycle_in_bases(self): diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 61bea9dba07fec..cf579d4da4e0df 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -733,6 +733,59 @@ def keyfunc(obj): keyfunc.skip = 1 self.assertRaises(ExpectedError, gulp, [None, None], keyfunc) + def test_groupby_reentrant_eq_does_not_crash(self): + # regression test for gh-143543 + class Key: + def __init__(self, do_advance): + self.do_advance = do_advance + + def __eq__(self, other): + if self.do_advance: + self.do_advance = False + next(g) + return NotImplemented + return False + + def keys(): + yield Key(True) + yield Key(False) + + g = itertools.groupby([None, None], keys().send) + next(g) + next(g) # must pass with address sanitizer + + def test_grouper_reentrant_eq_does_not_crash(self): + # regression test for gh-146613 + grouper_iter = None + + class Key: + __hash__ = None + + def __init__(self, do_advance): + self.do_advance = do_advance + + def __eq__(self, other): + nonlocal grouper_iter + if self.do_advance: + self.do_advance = False + if grouper_iter is not None: + try: + next(grouper_iter) + except StopIteration: + pass + return NotImplemented + return True + + def keyfunc(element): + if element == 0: + return Key(do_advance=True) + return Key(do_advance=False) + + g = itertools.groupby(range(4), keyfunc) + key, grouper_iter = next(g) + items = list(grouper_iter) + self.assertEqual(len(items), 1) + def test_filter(self): self.assertEqual(list(filter(isEven, range(6))), [0,2,4]) self.assertEqual(list(filter(None, [0,1,0,2,0])), [1,2]) diff --git a/Lib/test/test_json/__init__.py b/Lib/test/test_json/__init__.py index 74b64ed86a3183..e58fbee41af3e6 100644 --- a/Lib/test/test_json/__init__.py +++ b/Lib/test/test_json/__init__.py @@ -47,6 +47,16 @@ def test_cjson(self): '_json') +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(json, "__version__") + self.assertEqual(cm.filename, __file__) + + def load_tests(loader, _, pattern): suite = unittest.TestSuite() for mod in (json, json.encoder, json.decoder): diff --git a/Lib/test/test_json/json_lines.jsonl b/Lib/test/test_json/json_lines.jsonl new file mode 100644 index 00000000000000..d2f292111956f3 --- /dev/null +++ b/Lib/test/test_json/json_lines.jsonl @@ -0,0 +1,2 @@ +{"ingredients":["frog", "water", "chocolate", "glucose"]} +{"ingredients":["chocolate","steel bolts"]} diff --git a/Lib/test/test_json/test_decode.py b/Lib/test/test_json/test_decode.py index 2250af964c022b..1d51fb2de0e69e 100644 --- a/Lib/test/test_json/test_decode.py +++ b/Lib/test/test_json/test_decode.py @@ -69,6 +69,31 @@ def test_object_pairs_hook(self): object_pairs_hook=OrderedDict), OrderedDict([('empty', OrderedDict())])) + def test_array_hook(self): + s = '[1, 2, 3]' + t = self.loads(s, array_hook=tuple) + self.assertEqual(t, (1, 2, 3)) + self.assertEqual(type(t), tuple) + + # Nested array in inner structure with object_hook + s = '{"xkd": [[1], [2], [3]]}' + p = frozendict(xkd=((1,), (2,), (3,))) + data = self.loads(s, object_hook=frozendict, array_hook=tuple) + self.assertEqual(data, p) + self.assertEqual(type(data), frozendict) + self.assertEqual(type(data["xkd"]), tuple) + for item in data["xkd"]: + self.assertEqual(type(item), tuple) + + self.assertEqual(self.loads('[]', array_hook=tuple), ()) + + def test_load_array_hook(self): + # json.load must forward array_hook to loads + fp = StringIO('[10, 20, 30]') + result = self.json.load(fp, array_hook=tuple) + self.assertEqual(result, (10, 20, 30)) + self.assertEqual(type(result), tuple) + def test_decoder_optimizations(self): # Several optimizations were made that skip over calls to # the whitespace regex, so this test is designed to try and diff --git a/Lib/test/test_json/test_dump.py b/Lib/test/test_json/test_dump.py index 39470754003bb6..5bc03085e60a3d 100644 --- a/Lib/test/test_json/test_dump.py +++ b/Lib/test/test_json/test_dump.py @@ -12,6 +12,18 @@ def test_dump(self): def test_dumps(self): self.assertEqual(self.dumps({}), '{}') + def test_dumps_dict(self): + self.assertEqual(self.dumps({'x': 1, 'y': 2}), + '{"x": 1, "y": 2}') + self.assertEqual(self.dumps(frozendict({'x': 1, 'y': 2})), + '{"x": 1, "y": 2}') + lst = [{'x': 1}, frozendict(y=2)] + self.assertEqual(self.dumps(lst), + '[{"x": 1}, {"y": 2}]') + data = {'x': dict(a=1), 'y': frozendict(b=2)} + self.assertEqual(self.dumps(data), + '{"x": {"a": 1}, "y": {"b": 2}}') + def test_dump_skipkeys(self): v = {b'invalid_key': False, 'valid_key': True} with self.assertRaises(TypeError): @@ -65,6 +77,59 @@ def __lt__(self, o): d[1337] = "true.dat" self.assertEqual(self.dumps(d, sort_keys=True), '{"1337": "true.dat"}') + # gh-145244: UAF on borrowed key when default callback mutates dict + def test_default_clears_dict_key_uaf(self): + class Evil: + pass + + class AlsoEvil: + pass + + # Use a non-interned string key so it can actually be freed + key = "A" * 100 + target = {key: Evil()} + del key + + def evil_default(obj): + if isinstance(obj, Evil): + target.clear() + return AlsoEvil() + raise TypeError("not serializable") + + with self.assertRaises(TypeError): + self.json.dumps(target, default=evil_default, + check_circular=False) + + def test_dumps_str_subclass(self): + # Don't call obj.__str__() on str subclasses + + # str subclass which returns a different string on str(obj) + class StrSubclass(str): + def __str__(self): + return "StrSubclass" + + obj = StrSubclass('ascii') + self.assertEqual(self.dumps(obj), '"ascii"') + self.assertEqual(self.dumps([obj]), '["ascii"]') + self.assertEqual(self.dumps({'key': obj}), '{"key": "ascii"}') + + obj = StrSubclass('escape\n') + self.assertEqual(self.dumps(obj), '"escape\\n"') + self.assertEqual(self.dumps([obj]), '["escape\\n"]') + self.assertEqual(self.dumps({'key': obj}), '{"key": "escape\\n"}') + + obj = StrSubclass('nonascii:é') + self.assertEqual(self.dumps(obj, ensure_ascii=False), + '"nonascii:é"') + self.assertEqual(self.dumps([obj], ensure_ascii=False), + '["nonascii:é"]') + self.assertEqual(self.dumps({'key': obj}, ensure_ascii=False), + '{"key": "nonascii:é"}') + self.assertEqual(self.dumps(obj), '"nonascii:\\u00e9"') + self.assertEqual(self.dumps([obj]), '["nonascii:\\u00e9"]') + self.assertEqual(self.dumps({'key': obj}), + '{"key": "nonascii:\\u00e9"}') + class TestPyDump(TestDump, PyTest): pass diff --git a/Lib/test/test_json/test_encode_basestring_ascii.py b/Lib/test/test_json/test_encode_basestring_ascii.py index 6a39b72a09df35..1b5dfcfde01d11 100644 --- a/Lib/test/test_json/test_encode_basestring_ascii.py +++ b/Lib/test/test_json/test_encode_basestring_ascii.py @@ -3,18 +3,24 @@ from test.support import bigaddrspacetest +# str subclass which returns a different string on str(obj) +class StrSubclass(str): + def __str__(self): + return "StrSubclass" + CASES = [ ('/\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\x08\x0c\n\r\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?', '"/\\\\\\"\\ucafe\\ubabe\\uab98\\ufcde\\ubcda\\uef4a\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?"'), ('\u0123\u4567\u89ab\ucdef\uabcd\uef4a', '"\\u0123\\u4567\\u89ab\\ucdef\\uabcd\\uef4a"'), ('controls', '"controls"'), ('\x08\x0c\n\r\t', '"\\b\\f\\n\\r\\t"'), + ('\x00\x1f\x7f', '"\\u0000\\u001f\\u007f"'), ('{"object with 1 member":["array with 1 element"]}', '"{\\"object with 1 member\\":[\\"array with 1 element\\"]}"'), (' s p a c e d ', '" s p a c e d "'), ('\U0001d120', '"\\ud834\\udd20"'), ('\u03b1\u03a9', '"\\u03b1\\u03a9"'), ("`1~!@#$%^&*()_+-={':[,]}|;.</>?", '"`1~!@#$%^&*()_+-={\':[,]}|;.</>?"'), - ('\x08\x0c\n\r\t', '"\\b\\f\\n\\r\\t"'), - ('\u0123\u4567\u89ab\ucdef\uabcd\uef4a', '"\\u0123\\u4567\\u89ab\\ucdef\\uabcd\\uef4a"'), + # Don't call obj.__str__() on str subclasses + (StrSubclass('ascii'), '"ascii"'), ] class TestEncodeBasestringAscii: diff --git a/Lib/test/test_json/test_enum.py b/Lib/test/test_json/test_enum.py index 10f414898b8935..518c3e11200659 100644 --- a/Lib/test/test_json/test_enum.py +++ b/Lib/test/test_json/test_enum.py @@ -26,11 +26,14 @@ class FloatNum(float, Enum): NEG_INF = float('-inf') NAN = float('nan') -class WierdNum(float, Enum): +class WeirdNum(float, Enum): inf = INF neg_inf = NEG_INF nan = NAN +class StringEnum(str, Enum): + COLOR = "color" + class TestEnum: def test_floats(self): @@ -40,7 +43,7 @@ def test_floats(self): self.assertEqual(self.loads(self.dumps(enum)), enum) def test_weird_floats(self): - for enum, expected in zip(WierdNum, ('Infinity', '-Infinity', 'NaN')): + for enum, expected in zip(WeirdNum, ('Infinity', '-Infinity', 'NaN')): self.assertEqual(self.dumps(enum), expected) if not isnan(enum): self.assertEqual(float(self.dumps(enum)), enum) @@ -64,16 +67,16 @@ def test_list(self): str([E, PI, TAU])) self.assertEqual(self.loads(self.dumps(list(FloatNum))), list(FloatNum)) - self.assertEqual(self.dumps(list(WierdNum)), + self.assertEqual(self.dumps(list(WeirdNum)), '[Infinity, -Infinity, NaN]') - self.assertEqual(self.loads(self.dumps(list(WierdNum)))[:2], - list(WierdNum)[:2]) - self.assertTrue(isnan(self.loads(self.dumps(list(WierdNum)))[2])) + self.assertEqual(self.loads(self.dumps(list(WeirdNum)))[:2], + list(WeirdNum)[:2]) + self.assertTrue(isnan(self.loads(self.dumps(list(WeirdNum)))[2])) def test_dict_keys(self): s, b, h, r = BigNum e, p, t = FloatNum - i, j, n = WierdNum + i, j, n = WeirdNum d = { s:'tiny', b:'large', h:'larger', r:'largest', e:"Euler's number", p:'pi', t:'tau', @@ -100,9 +103,9 @@ def test_dict_values(self): e=FloatNum.e, pi=FloatNum.pi, tau=FloatNum.tau, - i=WierdNum.inf, - j=WierdNum.neg_inf, - n=WierdNum.nan, + i=WeirdNum.inf, + j=WeirdNum.neg_inf, + n=WeirdNum.nan, ) nd = self.loads(self.dumps(d)) self.assertEqual(nd['tiny'], SMALL) @@ -116,5 +119,11 @@ def test_dict_values(self): self.assertEqual(nd['j'], NEG_INF) self.assertTrue(isnan(nd['n'])) + def test_str_enum(self): + obj = StringEnum.COLOR + self.assertEqual(self.dumps(obj), '"color"') + self.assertEqual(self.dumps([obj]), '["color"]') + self.assertEqual(self.dumps({'key': obj}), '{"key": "color"}') + class TestPyEnum(TestEnum, PyTest): pass class TestCEnum(TestEnum, CTest): pass diff --git a/Lib/test/test_json/test_recursion.py b/Lib/test/test_json/test_recursion.py index 5d7b56ff9ad285..d732fc80cf1cf3 100644 --- a/Lib/test/test_json/test_recursion.py +++ b/Lib/test/test_json/test_recursion.py @@ -68,10 +68,12 @@ def default(self, o): self.fail("didn't raise ValueError on default recursion") + @support.skip_if_pgo_task # fails during PGO training w/ some stack sizes + @support.skip_if_unlimited_stack_size @support.skip_emscripten_stack_overflow() @support.skip_wasi_stack_overflow() def test_highly_nested_objects_decoding(self): - very_deep = 200000 + very_deep = 500_000 # test that loading highly-nested objects doesn't segfault when C # accelerations are used. See #12017 with self.assertRaises(RecursionError): @@ -84,13 +86,14 @@ def test_highly_nested_objects_decoding(self): with support.infinite_recursion(): self.loads('[' * very_deep + '1' + ']' * very_deep) + @support.skip_if_unlimited_stack_size @support.skip_wasi_stack_overflow() @support.skip_emscripten_stack_overflow() @support.requires_resource('cpu') def test_highly_nested_objects_encoding(self): # See #12051 l, d = [], {} - for x in range(200_000): + for x in range(500_000): l, d = [l], {'k':d} with self.assertRaises(RecursionError): with support.infinite_recursion(5000): @@ -99,6 +102,7 @@ def test_highly_nested_objects_encoding(self): with support.infinite_recursion(5000): self.dumps(d) + @support.skip_if_unlimited_stack_size @support.skip_emscripten_stack_overflow() @support.skip_wasi_stack_overflow() def test_endless_recursion(self): diff --git a/Lib/test/test_json/test_scanstring.py b/Lib/test/test_json/test_scanstring.py index cca556a3b95bab..9a6cdfe12d266c 100644 --- a/Lib/test/test_json/test_scanstring.py +++ b/Lib/test/test_json/test_scanstring.py @@ -144,7 +144,7 @@ def test_bad_escapes(self): def test_overflow(self): with self.assertRaises(OverflowError): - self.json.decoder.scanstring(b"xxx", sys.maxsize+1) + self.json.decoder.scanstring("xxx", sys.maxsize+1) class TestPyScanstring(TestScanstring, PyTest): pass diff --git a/Lib/test/test_json/test_speedups.py b/Lib/test/test_json/test_speedups.py index 682014cfd5b344..0b22a0bf4b9538 100644 --- a/Lib/test/test_json/test_speedups.py +++ b/Lib/test/test_json/test_speedups.py @@ -1,4 +1,5 @@ from test.test_json import CTest +from test.support import gc_collect class BadBool: @@ -80,3 +81,94 @@ def test(name): def test_unsortable_keys(self): with self.assertRaises(TypeError): self.json.encoder.JSONEncoder(sort_keys=True).encode({'a': 1, 1: 'a'}) + + def test_current_indent_level(self): + enc = self.json.encoder.c_make_encoder( + markers=None, + default=str, + encoder=self.json.encoder.c_encode_basestring, + indent='\t', + key_separator=': ', + item_separator=', ', + sort_keys=False, + skipkeys=False, + allow_nan=False) + expected = ( + '[\n' + '\t"spam", \n' + '\t{\n' + '\t\t"ham": "eggs"\n' + '\t}\n' + ']') + self.assertEqual(enc(['spam', {'ham': 'eggs'}], 0)[0], expected) + self.assertEqual(enc(['spam', {'ham': 'eggs'}], -3)[0], expected) + expected2 = ( + '[\n' + '\t\t\t\t"spam", \n' + '\t\t\t\t{\n' + '\t\t\t\t\t"ham": "eggs"\n' + '\t\t\t\t}\n' + '\t\t\t]') + self.assertEqual(enc(['spam', {'ham': 'eggs'}], 3)[0], expected2) + self.assertRaises(TypeError, enc, ['spam', {'ham': 'eggs'}], 3.0) + self.assertRaises(TypeError, enc, ['spam', {'ham': 'eggs'}]) + + def test_mutate_dict_items_during_encode(self): + # gh-142831: Clearing the items list via a re-entrant key encoder + # must not cause a use-after-free. BadDict.items() returns a + # mutable list; encode_str clears it while iterating. + items = None + + class BadDict(dict): + def items(self): + nonlocal items + items = [("boom", object())] + return items + + cleared = False + def encode_str(obj): + nonlocal items, cleared + if items is not None: + items.clear() + items = None + cleared = True + gc_collect() + return '"x"' + + encoder = self.json.encoder.c_make_encoder( + None, lambda o: "null", + encode_str, None, + ": ", ", ", False, + False, True + ) + + # Must not crash (use-after-free under ASan before fix) + encoder(BadDict(real=1), 0) + self.assertTrue(cleared) + + def test_mutate_list_during_encode(self): + # gh-142831: Clearing a list mid-iteration via the default + # callback must not cause a use-after-free. + call_count = 0 + lst = [object() for _ in range(10)] + + def default(obj): + nonlocal call_count + call_count += 1 + if call_count == 3: + lst.clear() + gc_collect() + return None + + encoder = self.json.encoder.c_make_encoder( + None, default, + self.json.encoder.c_encode_basestring, None, + ": ", ", ", False, + False, True + ) + + # Must not crash (use-after-free under ASan before fix) + encoder(lst, 0) + # Verify the mutation path was actually hit and the loop + # stopped iterating after the list was cleared. + self.assertEqual(call_count, 3) diff --git a/Lib/test/test_json/test_tool.py b/Lib/test/test_json/test_tool.py index 30f9bb3331605c..0a96b318b15b1c 100644 --- a/Lib/test/test_json/test_tool.py +++ b/Lib/test/test_json/test_tool.py @@ -1,4 +1,5 @@ import errno +import pathlib import os import sys import textwrap @@ -13,6 +14,7 @@ @support.requires_subprocess() +@support.skip_if_pgo_task class TestMain(unittest.TestCase): data = """ @@ -156,6 +158,14 @@ def test_jsonlines(self): self.assertEqual(process.stdout, self.jsonlines_expect) self.assertEqual(process.stderr, '') + @force_not_colorized + def test_jsonlines_from_file(self): + jsonl = pathlib.Path(__file__).parent / 'json_lines.jsonl' + args = sys.executable, '-m', self.module, '--json-lines', jsonl + process = subprocess.run(args, capture_output=True, text=True, check=True) + self.assertEqual(process.stdout, self.jsonlines_expect) + self.assertEqual(process.stderr, '') + def test_help_flag(self): rc, out, err = assert_python_ok('-m', self.module, '-h', PYTHON_COLORS='0') @@ -319,6 +329,7 @@ def test_colors(self): @support.requires_subprocess() +@support.skip_if_pgo_task class TestTool(TestMain): module = 'json.tool' diff --git a/Lib/test/test_json/test_unicode.py b/Lib/test/test_json/test_unicode.py index 68629cceeb9be9..1aa9546dc46306 100644 --- a/Lib/test/test_json/test_unicode.py +++ b/Lib/test/test_json/test_unicode.py @@ -32,6 +32,29 @@ def test_encoding7(self): j = self.dumps(u + "\n", ensure_ascii=False) self.assertEqual(j, f'"{u}\\n"') + def test_ascii_non_printable_encode(self): + u = '\b\t\n\f\r\x00\x1f\x7f' + self.assertEqual(self.dumps(u), + '"\\b\\t\\n\\f\\r\\u0000\\u001f\\u007f"') + self.assertEqual(self.dumps(u, ensure_ascii=False), + '"\\b\\t\\n\\f\\r\\u0000\\u001f\x7f"') + + def test_ascii_non_printable_decode(self): + self.assertEqual(self.loads('"\\b\\t\\n\\f\\r"'), + '\b\t\n\f\r') + s = ''.join(map(chr, range(32))) + for c in s: + self.assertRaises(self.JSONDecodeError, self.loads, f'"{c}"') + self.assertEqual(self.loads(f'"{s}"', strict=False), s) + self.assertEqual(self.loads('"\x7f"'), '\x7f') + + def test_escaped_decode(self): + self.assertEqual(self.loads('"\\b\\t\\n\\f\\r"'), '\b\t\n\f\r') + self.assertEqual(self.loads('"\\"\\\\\\/"'), '"\\/') + for c in set(map(chr, range(0x100))) - set('"\\/bfnrt'): + self.assertRaises(self.JSONDecodeError, self.loads, f'"\\{c}"') + self.assertRaises(self.JSONDecodeError, self.loads, f'"\\{c}"', strict=False) + def test_big_unicode_encode(self): u = '\U0001d120' self.assertEqual(self.dumps(u), '"\\ud834\\udd20"') @@ -48,6 +71,18 @@ def test_unicode_decode(self): s = f'"\\u{i:04x}"' self.assertEqual(self.loads(s), u) + def test_single_surrogate_encode(self): + self.assertEqual(self.dumps('\uD83D'), '"\\ud83d"') + self.assertEqual(self.dumps('\uD83D', ensure_ascii=False), '"\ud83d"') + self.assertEqual(self.dumps('\uDC0D'), '"\\udc0d"') + self.assertEqual(self.dumps('\uDC0D', ensure_ascii=False), '"\udc0d"') + + def test_single_surrogate_decode(self): + self.assertEqual(self.loads('"\uD83D"'), '\ud83d') + self.assertEqual(self.loads('"\\uD83D"'), '\ud83d') + self.assertEqual(self.loads('"\udc0d"'), '\udc0d') + self.assertEqual(self.loads('"\\udc0d"'), '\udc0d') + def test_unicode_preservation(self): self.assertEqual(type(self.loads('""')), str) self.assertEqual(type(self.loads('"a"')), str) diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py index e94edcbc107ba9..2cf99be9e2c3ba 100644 --- a/Lib/test/test_kqueue.py +++ b/Lib/test/test_kqueue.py @@ -9,6 +9,8 @@ import time import unittest +from test.support import warnings_helper + if not hasattr(select, "kqueue"): raise unittest.SkipTest("test works only on BSD") @@ -252,11 +254,21 @@ def test_close(self): # operations must fail with ValueError("I/O operation on closed ...") self.assertRaises(ValueError, kqueue.control, None, 4) + def test_close_error(self): + # gh-146205: close() should raise OSError if underlying fd is invalid + kqueue = select.kqueue() + fd = kqueue.fileno() + os.close(fd) + with self.assertRaises(OSError) as cm: + kqueue.close() + self.assertEqual(cm.exception.errno, errno.EBADF) + def test_fd_non_inheritable(self): kqueue = select.kqueue() self.addCleanup(kqueue.close) self.assertEqual(os.get_inheritable(kqueue.fileno()), False) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() def test_fork(self): # gh-110395: kqueue objects must be closed after fork diff --git a/Lib/test/test_launcher.py b/Lib/test/test_launcher.py index caa1603c78eb01..c522bc1c2c093c 100644 --- a/Lib/test/test_launcher.py +++ b/Lib/test/test_launcher.py @@ -227,6 +227,8 @@ def run_py(self, args, env=None, allow_fail=False, expect_returncode=0, argv=Non "PYLAUNCHER_LIMIT_TO_COMPANY": "", **{k.upper(): v for k, v in (env or {}).items()}, } + if ini_dir := getattr(self, '_ini_dir', None): + env.setdefault("_PYLAUNCHER_INIDIR", ini_dir) if not argv: argv = [self.py_exe, *args] with subprocess.Popen( @@ -262,11 +264,14 @@ def run_py(self, args, env=None, allow_fail=False, expect_returncode=0, argv=Non return data def py_ini(self, content): - local_appdata = os.environ.get("LOCALAPPDATA") - if not local_appdata: - raise unittest.SkipTest("LOCALAPPDATA environment variable is " - "missing or empty") - return PreservePyIni(Path(local_appdata) / "py.ini", content) + ini_dir = getattr(self, '_ini_dir', None) + if not ini_dir: + local_appdata = os.environ.get("LOCALAPPDATA") + if not local_appdata: + raise unittest.SkipTest("LOCALAPPDATA environment variable is " + "missing or empty") + ini_dir = local_appdata + return PreservePyIni(Path(ini_dir) / "py.ini", content) @contextlib.contextmanager def script(self, content, encoding="utf-8"): @@ -302,6 +307,8 @@ def setUpClass(cls): p = subprocess.check_output("reg query HKCU\\Software\\Python /s") #print(p.decode('mbcs')) + cls._ini_dir = tempfile.mkdtemp() + cls.addClassCleanup(shutil.rmtree, cls._ini_dir, ignore_errors=True) @classmethod def tearDownClass(cls): diff --git a/Lib/test/test_lazy_import/__init__.py b/Lib/test/test_lazy_import/__init__.py new file mode 100644 index 00000000000000..1724beb8ce6951 --- /dev/null +++ b/Lib/test/test_lazy_import/__init__.py @@ -0,0 +1,1954 @@ +"""Tests for PEP 810 lazy imports.""" + +import io +import dis +import subprocess +import sys +import textwrap +import threading +import types +import unittest +import tempfile +import os +import contextlib + +from test import support +from test.support.script_helper import assert_python_ok + +try: + import _testcapi +except ImportError: + _testcapi = None + + +class LazyImportTestCase(unittest.TestCase): + def setUp(self): + self.lazy_imports_filter = sys.get_lazy_imports_filter() + self.lazy_imports = sys.get_lazy_imports() + + def tearDown(self): + """Clean up any test modules from sys.modules.""" + for key in list(sys.modules.keys()): + if key.startswith('test.test_lazy_import.data'): + del sys.modules[key] + + sys.set_lazy_imports_filter(self.lazy_imports_filter) + sys.set_lazy_imports(self.lazy_imports) + sys.lazy_modules.clear() + + +class LazyImportTests(LazyImportTestCase): + """Tests for basic lazy import functionality.""" + + def test_basic_unused(self): + """Lazy imported module should not be loaded if never accessed.""" + import test.test_lazy_import.data.basic_unused + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + self.assertIn("test.test_lazy_import.data.basic2", sys.lazy_modules) + + def test_sys_lazy_modules(self): + try: + import test.test_lazy_import.data.basic_from_unused + except ImportError as e: + self.fail('lazy import failed') + + self.assertFalse("test.test_lazy_import.data.basic2" in sys.modules) + self.assertIn("test.test_lazy_import.data", sys.lazy_modules) + self.assertIn("test.test_lazy_import.data.basic2", sys.lazy_modules) + test.test_lazy_import.data.basic_from_unused.basic2 + self.assertNotIn("test.test_import.data", sys.lazy_modules) + + def test_basic_unused_use_externally(self): + """Lazy import should load module when accessed from outside.""" + from test.test_lazy_import.data import basic_unused + + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + x = basic_unused.test.test_lazy_import.data.basic2 + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_basic_from_unused_use_externally(self): + """Lazy 'from' import should load when accessed from outside.""" + from test.test_lazy_import.data import basic_from_unused + + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + x = basic_from_unused.basic2 + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_basic_unused_dir(self): + """dir() on module should not trigger lazy import reification.""" + import test.test_lazy_import.data.basic_unused + + x = dir(test.test_lazy_import.data.basic_unused) + self.assertIn("test", x) + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_basic_dir(self): + """dir() at module scope should not trigger lazy import reification.""" + from test.test_lazy_import.data import basic_dir + + self.assertIn("test", basic_dir.x) + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_basic_used(self): + """Lazy import should load when accessed within the module.""" + import test.test_lazy_import.data.basic_used + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + @support.requires_subprocess() + def test_from_import_with_module_getattr(self): + """Lazy from import should respect module-level __getattr__.""" + code = textwrap.dedent(""" + lazy from test.test_lazy_import.data.module_with_getattr import dynamic_attr + assert dynamic_attr == "from_getattr" + """) + assert_python_ok("-c", code) + + @support.requires_subprocess() + def test_from_import_with_module_getattr_raising(self): + """Lazy from import should respect module-level __getattr__.""" + code = textwrap.dedent(""" + lazy from test.test_lazy_import.data.module_with_getattr import raising_attr + + try: + raising_attr + except ValueError as exc: + assert str(exc) == 'from_getattr', exc + else: + assert False, f'ValueError is not raised: {raising_attr}' + """) + assert_python_ok("-c", code) + + @support.requires_subprocess() + def test_from_import_with_module_getattr_missing(self): + """Lazy from import should respect module-level __getattr__.""" + for attr in ("missing_attr", "import_error_attr"): + with self.subTest(attr=attr): + code = textwrap.dedent(f""" + lazy from test.test_lazy_import.data.module_with_getattr import {attr} + + try: + {attr} + except ImportError as exc: + assert '{attr}' in str(exc), exc + assert exc.__cause__ is not None + else: + assert False, ('ImportError is not raised', {attr}) + """) + assert_python_ok("-c", code) + + @support.requires_subprocess() + def test_from_import_with_module_getattr_warning(self): + """Lazy from import should respect module-level __getattr__.""" + code = textwrap.dedent(""" + import warnings + + with warnings.catch_warnings(record=True) as log: + lazy from test.test_lazy_import.data.module_with_getattr import warning_attr + + assert log == [] + + with warnings.catch_warnings(record=True) as log: + warning_attr + assert warning_attr == 'from_warning_attr', warning_attr + assert len(log) == 1, log + assert isinstance(log[0].message, UserWarning), log + assert str(log[0].message) == 'from_getattr', log + """) + assert_python_ok("-c", code) + + @support.requires_subprocess() + def test_from_import_with_imported_module_getattr(self): + """Lazy from import should not shadow an imported module's __getattr__.""" + code = textwrap.dedent(""" + import test.test_lazy_import.data.module_with_getattr as mod + lazy from test.test_lazy_import.data.module_with_getattr import dynamic_attr + assert dynamic_attr == "from_getattr" + assert mod.dynamic_attr == "from_getattr" + """) + assert_python_ok("-c", code) + + +class GlobalLazyImportModeTests(LazyImportTestCase): + """Tests for sys.set_lazy_imports() global mode control.""" + + def test_global_off_rejected(self): + """Mode 'none' is not supported.""" + with self.assertRaises(ValueError): + sys.set_lazy_imports("none") + + def test_global_on(self): + """Mode 'all' should make regular imports lazy.""" + import test.test_lazy_import.data.global_on + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_global_filter(self): + """Filter returning False should prevent lazy loading.""" + import test.test_lazy_import.data.global_filter + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_global_filter_true(self): + """Filter returning True should allow lazy loading.""" + import test.test_lazy_import.data.global_filter_true + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_global_filter_from(self): + """Filter should work with 'from' imports.""" + import test.test_lazy_import.data.global_filter_from + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_global_filter_from_true(self): + """Filter returning True should allow lazy 'from' imports.""" + import test.test_lazy_import.data.global_filter_from_true + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + +class CompatibilityModeTests(LazyImportTestCase): + """Tests for __lazy_modules__ compatibility mode.""" + + def test_compatibility_mode(self): + """__lazy_modules__ should enable lazy imports for listed modules.""" + import test.test_lazy_import.data.basic_compatibility_mode + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_compatibility_mode_used(self): + """Using a lazy import from __lazy_modules__ should load the module.""" + import test.test_lazy_import.data.basic_compatibility_mode_used + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_compatibility_mode_func(self): + """Imports inside functions should be eager even in compatibility mode.""" + import test.test_lazy_import.data.compatibility_mode_func + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_compatibility_mode_try_except(self): + """Imports in try/except should be eager even in compatibility mode.""" + import test.test_lazy_import.data.compatibility_mode_try_except + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_compatibility_mode_relative(self): + """__lazy_modules__ should work with relative imports.""" + import test.test_lazy_import.data.basic_compatibility_mode_relative + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + +class ModuleIntrospectionTests(LazyImportTestCase): + """Tests for module dict and getattr behavior with lazy imports.""" + + def test_modules_dict(self): + """Accessing module.__dict__ should not trigger reification.""" + import test.test_lazy_import.data.modules_dict + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_modules_getattr(self): + """Module __getattr__ for lazy import name should trigger reification.""" + import test.test_lazy_import.data.modules_getattr + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_modules_getattr_other(self): + """Module __getattr__ for other names should not trigger reification.""" + import test.test_lazy_import.data.modules_getattr_other + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + +class LazyImportTypeTests(LazyImportTestCase): + """Tests for the LazyImportType and its resolve() method.""" + + def test_lazy_value_resolve(self): + """resolve() method should force the lazy import to load.""" + import test.test_lazy_import.data.lazy_get_value + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_lazy_import_type_exposed(self): + """LazyImportType should be exposed in types module.""" + self.assertHasAttr(types, 'LazyImportType') + self.assertEqual(types.LazyImportType.__name__, 'lazy_import') + + def test_lazy_import_type_cant_construct(self): + """LazyImportType should not be directly constructible.""" + self.assertRaises(TypeError, types.LazyImportType, {}, "module") + + @support.requires_subprocess() + def test_lazy_import_type_attributes_accessible(self): + """Check that static PyLazyImport_Type is initialized at startup.""" + code = textwrap.dedent(""" + lazy import json + print(globals()["json"].resolve) + """) + proc = assert_python_ok("-c", code) + self.assertIn(b"<built-in method resolve of lazy_import object at", proc.out) + + +class SyntaxRestrictionTests(LazyImportTestCase): + """Tests for syntax restrictions on lazy imports.""" + + def test_lazy_try_except(self): + """lazy import inside try/except should raise SyntaxError.""" + with self.assertRaises(SyntaxError): + import test.test_lazy_import.data.badsyntax.lazy_try_except + + def test_lazy_try_except_from(self): + """lazy from import inside try/except should raise SyntaxError.""" + with self.assertRaises(SyntaxError): + import test.test_lazy_import.data.badsyntax.lazy_try_except_from + + def test_lazy_try_except_from_star(self): + """lazy from import * should raise SyntaxError.""" + with self.assertRaises(SyntaxError): + import test.test_lazy_import.data.badsyntax.lazy_try_except_from_star + + def test_lazy_future_import(self): + """lazy from __future__ import should raise SyntaxError.""" + with self.assertRaises(SyntaxError) as cm: + import test.test_lazy_import.data.badsyntax.lazy_future_import + # Check we highlight 'lazy' (column offset 0, end offset 4) + self.assertEqual(cm.exception.offset, 1) + self.assertEqual(cm.exception.end_offset, 5) + + def test_lazy_import_func(self): + """lazy import inside function should raise SyntaxError.""" + with self.assertRaises(SyntaxError): + import test.test_lazy_import.data.badsyntax.lazy_import_func + + def test_lazy_import_exec_in_function(self): + """lazy import via exec() inside a function should raise SyntaxError.""" + # exec() inside a function creates a non-module-level context + # where lazy imports are not allowed + def f(): + exec("lazy import json") + + with self.assertRaises(SyntaxError) as cm: + f() + self.assertIn("only allowed at module level", str(cm.exception)) + + def test_lazy_import_exec_in_class(self): + """lazy import via exec() inside a class should raise SyntaxError.""" + # exec() inside a class body also has non-module-level locals. + with self.assertRaises(SyntaxError) as cm: + class C: + exec("lazy import json") + + self.assertIn("only allowed at module level", str(cm.exception)) + + @support.requires_subprocess() + def test_lazy_import_exec_at_module_level(self): + """lazy import via exec() at module level should work.""" + # exec() at module level (globals == locals) should allow lazy imports + code = textwrap.dedent(""" + import sys + exec("lazy import json") + # Should be lazy - not loaded yet + assert 'json' not in sys.modules + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + +class EagerImportInLazyModeTests(LazyImportTestCase): + """Tests for imports that should remain eager even in lazy mode.""" + + def test_try_except_eager(self): + """Imports in try/except should be eager even with mode='all'.""" + sys.set_lazy_imports("all") + import test.test_lazy_import.data.try_except_eager + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_try_except_eager_from(self): + """From imports in try/except should be eager even with mode='all'.""" + sys.set_lazy_imports("all") + import test.test_lazy_import.data.try_except_eager_from + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_eager_import_func(self): + """Imports inside functions should return modules, not proxies.""" + sys.set_lazy_imports("all") + import test.test_lazy_import.data.eager_import_func + + f = test.test_lazy_import.data.eager_import_func.f + self.assertEqual(type(f()), type(sys)) + + def test_exec_import_func(self): + """Implicit lazy imports via exec() inside functions should be eager.""" + sys.set_lazy_imports("all") + + def f(): + exec("import test.test_lazy_import.data.basic2") + + f() + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_exec_import_func_with_lazy_modules(self): + """__lazy_modules__ should not make exec() imports lazy inside functions.""" + globals()["__lazy_modules__"] = ["test.test_lazy_import.data.basic2"] + try: + def f(): + exec("import test.test_lazy_import.data.basic2") + + f() + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + finally: + del globals()["__lazy_modules__"] + + def test_exec_import_class(self): + """Implicit lazy imports via exec() inside classes should be eager.""" + sys.set_lazy_imports("all") + + class C: + exec("import test.test_lazy_import.data.basic2") + + self.assertIsNotNone(C) + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_exec_import_class_with_lazy_modules(self): + """__lazy_modules__ should not make exec() imports lazy inside classes.""" + globals()["__lazy_modules__"] = ["test.test_lazy_import.data.basic2"] + try: + class C: + exec("import test.test_lazy_import.data.basic2") + + self.assertIsNotNone(C) + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + finally: + del globals()["__lazy_modules__"] + + +class WithStatementTests(LazyImportTestCase): + """Tests for lazy imports in with statement context.""" + + def test_lazy_with(self): + """lazy import with 'with' statement should work.""" + import test.test_lazy_import.data.lazy_with + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_lazy_with_from(self): + """lazy from import with 'with' statement should work.""" + import test.test_lazy_import.data.lazy_with_from + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + +class PackageTests(LazyImportTestCase): + """Tests for lazy imports with packages.""" + + def test_lazy_import_pkg(self): + """lazy import of package submodule should load the package.""" + out = io.StringIO() + + with contextlib.redirect_stdout(out): + import test.test_lazy_import.data.lazy_import_pkg + + self.assertIn("test.test_lazy_import.data.pkg", sys.modules) + self.assertIn("test.test_lazy_import.data.pkg.bar", sys.modules) + self.assertIn("BAR_MODULE_LOADED", out.getvalue()) + + def test_lazy_submodule_stored_in_parent_dict(self): + """Accessing a lazy submodule should store it in the parent's __dict__.""" + out = io.StringIO() + + with contextlib.redirect_stdout(out): + import test.test_lazy_import.data.lazy_import_pkg + + pkg = sys.modules["test.test_lazy_import.data.pkg"] + self.assertIn("bar", pkg.__dict__) + self.assertIs(pkg.__dict__["bar"], sys.modules["test.test_lazy_import.data.pkg.bar"]) + self.assertIn("BAR_MODULE_LOADED", out.getvalue()) + + def test_lazy_import_pkg_cross_import(self): + """Cross-imports within package should preserve lazy imports.""" + import test.test_lazy_import.data.pkg.c + + self.assertIn("test.test_lazy_import.data.pkg", sys.modules) + self.assertIn("test.test_lazy_import.data.pkg.c", sys.modules) + self.assertNotIn("test.test_lazy_import.data.pkg.b", sys.modules) + + g = test.test_lazy_import.data.pkg.c.get_globals() + self.assertEqual(type(g["x"]), int) + self.assertEqual(type(g["b"]), types.LazyImportType) + + @support.requires_subprocess() + def test_lazy_from_import_does_not_pollute_parent(self): + """Lazy from import should not add the name to the parent module's dict.""" + code = textwrap.dedent(""" + lazy from json import nonexistent_attr + import json + assert "nonexistent_attr" not in json.__dict__, ( + "lazy from import should not publish attributes on the parent module" + ) + """) + assert_python_ok("-c", code) + + @support.requires_subprocess() + def test_package_from_import_with_module_getattr_raising(self): + """Lazy from import should respect a package's __getattr__.""" + code = textwrap.dedent(""" + lazy from test.test_lazy_import.data.pkg import raising_attr + + try: + raising_attr + except ValueError as exc: + assert str(exc) == 'from_getattr', exc + else: + assert False, f'ValueError is not raised: {raising_attr}' + """) + assert_python_ok("-c", code) + + @support.requires_subprocess() + def test_package_from_import_with_module_getattr_missing(self): + """Lazy from import should respect package's __getattr__.""" + for attr in ("missing_attr", "import_error_attr"): + with self.subTest(attr=attr): + code = textwrap.dedent(f""" + lazy from test.test_lazy_import.data.pkg import {attr} + + try: + {attr} + except ImportError as exc: + assert '{attr}' in str(exc), exc + assert exc.__cause__ is not None + else: + assert False, ('ImportError is not raised', {attr}) + """) + assert_python_ok("-c", code) + + @support.requires_subprocess() + def test_from_import_with_module_getattr_warning(self): + """Lazy from import should respect package's __getattr__.""" + code = textwrap.dedent(""" + import warnings + + with warnings.catch_warnings(record=True) as log: + lazy from test.test_lazy_import.data.pkg import warning_attr + + assert log == [] + + with warnings.catch_warnings(record=True) as log: + warning_attr + assert warning_attr == 'from_warning_attr', warning_attr + assert len(log) == 1, log + assert isinstance(log[0].message, UserWarning), log + assert str(log[0].message) == 'from_getattr', log + """) + assert_python_ok("-c", code) + + @support.requires_subprocess() + def test_package_from_import_with_module_getattr(self): + """Lazy from import should respect a package's __getattr__.""" + code = textwrap.dedent(""" + import test.test_lazy_import.data.pkg as pkg + lazy from test.test_lazy_import.data.pkg import dynamic_attr + assert dynamic_attr == "from_getattr" + assert pkg.dynamic_attr == "from_getattr" + """) + assert_python_ok("-c", code) + + +class DunderLazyImportTests(LazyImportTestCase): + """Tests for __lazy_import__ builtin function.""" + + def test_dunder_lazy_import(self): + """__lazy_import__ should create lazy import proxy.""" + import test.test_lazy_import.data.dunder_lazy_import + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_dunder_lazy_import_with_custom_filter(self): + sys.set_lazy_imports_filter(lambda importer, imported, fromlist: False) + import test.test_lazy_import.data.dunder_lazy_import + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_dunder_lazy_import_used(self): + """Using __lazy_import__ result should trigger module load.""" + import test.test_lazy_import.data.dunder_lazy_import_used + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_dunder_lazy_import_invalid_arguments(self): + """__lazy_import__ should reject invalid arguments.""" + for invalid_name in (b"", 123, None): + with self.assertRaises(TypeError): + __lazy_import__(invalid_name) + + with self.assertRaises(ValueError): + __lazy_import__("sys", level=-1) + with self.assertRaises(TypeError): + __lazy_import__("sys", globals=1) + + def test_dunder_lazy_import_builtins(self): + """__lazy_import__ should use module's __builtins__ for __import__.""" + from test.test_lazy_import.data import dunder_lazy_import_builtins + + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + self.assertEqual(dunder_lazy_import_builtins.basic.basic2, 42) + + +class SysLazyImportsAPITests(LazyImportTestCase): + """Tests for sys lazy imports API functions.""" + + def test_set_lazy_imports_requires_string(self): + """set_lazy_imports should reject non-string arguments.""" + with self.assertRaises(TypeError): + sys.set_lazy_imports(True) + with self.assertRaises(TypeError): + sys.set_lazy_imports(None) + with self.assertRaises(TypeError): + sys.set_lazy_imports(1) + + def test_set_lazy_imports_rejects_invalid_mode(self): + """set_lazy_imports should reject invalid mode strings.""" + with self.assertRaises(ValueError): + sys.set_lazy_imports("invalid") + with self.assertRaises(ValueError): + sys.set_lazy_imports("on") + with self.assertRaises(ValueError): + sys.set_lazy_imports("off") + + def test_get_lazy_imports_returns_string(self): + """get_lazy_imports should return string modes.""" + sys.set_lazy_imports("normal") + self.assertEqual(sys.get_lazy_imports(), "normal") + + sys.set_lazy_imports("all") + self.assertEqual(sys.get_lazy_imports(), "all") + + def test_get_lazy_imports_filter_default(self): + """get_lazy_imports_filter should return None by default.""" + sys.set_lazy_imports_filter(None) + self.assertIsNone(sys.get_lazy_imports_filter()) + + def test_set_and_get_lazy_imports_filter(self): + """set/get_lazy_imports_filter should round-trip filter function.""" + def my_filter(name): + return name.startswith("test.") + + sys.set_lazy_imports_filter(my_filter) + self.assertIs(sys.get_lazy_imports_filter(), my_filter) + + def test_lazy_modules_attribute_is_dict(self): + """sys.lazy_modules should be a set per PEP 810.""" + self.assertIsInstance(sys.lazy_modules, set) + + @support.requires_subprocess() + def test_lazy_modules_tracks_lazy_imports(self): + """sys.lazy_modules should track lazily imported module names.""" + code = textwrap.dedent(""" + import sys + initial_count = len(sys.lazy_modules) + import test.test_lazy_import.data.basic_unused + assert "test.test_lazy_import.data.basic2" in sys.lazy_modules + assert len(sys.lazy_modules) > initial_count + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + +@support.requires_subprocess() +class ErrorHandlingTests(LazyImportTestCase): + """Tests for error handling during lazy import reification. + + PEP 810: Errors during reification should show exception chaining with + both the lazy import definition location and the access location. + """ + + def test_import_error_shows_chained_traceback(self): + """Accessing a nonexistent lazy submodule via parent attr raises AttributeError.""" + code = textwrap.dedent(""" + import sys + lazy import test.test_lazy_import.data.nonexistent_module + + try: + x = test.test_lazy_import.data.nonexistent_module + except AttributeError as e: + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_attribute_error_on_from_import_shows_chained_traceback(self): + """Accessing missing attribute from lazy from-import should chain errors.""" + # Tests 'lazy from module import nonexistent' behavior + code = textwrap.dedent(""" + import sys + lazy from test.test_lazy_import.data.basic2 import nonexistent_name + + try: + x = nonexistent_name + except ImportError as e: + # PEP 810: Enhanced error reporting through exception chaining + assert e.__cause__ is not None, "Expected chained exception" + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_reification_retries_on_failure(self): + """Failed reification should allow retry on subsequent access. + + PEP 810: "If reification fails, the lazy object is not reified or replaced. + Subsequent uses of the lazy object will re-try the reification." + """ + code = textwrap.dedent(""" + import sys + import types + + lazy import test.test_lazy_import.data.broken_module + + # First access - should fail + try: + x = test.test_lazy_import.data.broken_module + except AttributeError: + pass + + # The lazy object should still be a lazy proxy (not reified) + g = globals() + lazy_obj = g['test'] + # The root 'test' binding should still allow retry + # Second access - should also fail (retry the import) + try: + x = test.test_lazy_import.data.broken_module + except AttributeError: + print("OK - retry worked") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_error_during_module_execution_propagates(self): + """Errors in module code during reification should propagate correctly.""" + code = textwrap.dedent(""" + import sys + lazy import test.test_lazy_import.data.broken_module + + try: + _ = test.test_lazy_import.data.broken_module + print("FAIL - should have raised") + except AttributeError: + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_circular_lazy_import_does_not_crash_for_gh_144727(self): + with tempfile.TemporaryDirectory() as tmpdir: + a_path = os.path.join(tmpdir, "a.py") + b_path = os.path.join(tmpdir, "b.py") + + with open(a_path, "w") as f: + f.write(textwrap.dedent("""\ + lazy import b + + def something(): + b.hello() + + something() + """)) + + with open(b_path, "w") as f: + f.write(textwrap.dedent("""\ + lazy import a + + def hello(): + print(a) + """)) + + result = subprocess.run( + [sys.executable, a_path], + capture_output=True, + text=True, + cwd=tmpdir, + ) + # Should get a proper Python error, not a crash + self.assertEqual(result.returncode, 1) + self.assertIn("Error", result.stderr) + + +@support.requires_subprocess() +class GlobalsAndDictTests(LazyImportTestCase): + """Tests for globals() and __dict__ behavior with lazy imports. + + PEP 810: "Calling globals() or accessing a module's __dict__ does not trigger + reification – they return the module's dictionary, and accessing lazy objects + through that dictionary still returns lazy proxy objects." + """ + + def test_globals_returns_lazy_proxy_when_accessed_from_function(self): + """globals() accessed from a function should return lazy proxy without reification. + + Note: At module level, accessing globals()['name'] triggers LOAD_NAME which + automatically resolves lazy imports. Inside a function, accessing globals()['name'] + uses BINARY_SUBSCR which returns the lazy proxy without resolution. + """ + code = textwrap.dedent(""" + import sys + import types + + lazy from test.test_lazy_import.data.basic2 import x + + # Check that module is not yet loaded + assert 'test.test_lazy_import.data.basic2' not in sys.modules + + def check_lazy(): + # Access through globals() from inside a function + g = globals() + lazy_obj = g['x'] + return type(lazy_obj) is types.LazyImportType + + # Inside function, should get lazy proxy + is_lazy = check_lazy() + assert is_lazy, "Expected LazyImportType from function scope" + + # Module should STILL not be loaded + assert 'test.test_lazy_import.data.basic2' not in sys.modules + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_globals_dict_access_returns_lazy_proxy_inline(self): + """Accessing globals()['name'] inline should return lazy proxy. + + Note: Assigning g['name'] to a local variable at module level triggers + reification due to STORE_NAME bytecode. Inline access preserves laziness. + """ + code = textwrap.dedent(""" + import sys + import types + lazy import json + # Inline access without assignment to local variable preserves lazy proxy + assert type(globals()['json']) is types.LazyImportType + assert 'json' not in sys.modules + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_module_dict_returns_lazy_proxy_without_reifying(self): + """module.__dict__ access should not trigger reification.""" + import test.test_lazy_import.data.globals_access + + # Module not loaded yet via direct dict access + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + # Access via get_from_globals should return lazy proxy + lazy_obj = test.test_lazy_import.data.globals_access.get_from_globals() + self.assertEqual(type(lazy_obj), types.LazyImportType) + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_direct_access_triggers_reification(self): + """Direct name access (not through globals()) should trigger reification.""" + import test.test_lazy_import.data.globals_access + + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + # Direct access should reify + result = test.test_lazy_import.data.globals_access.get_direct() + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_resolve_method_forces_reification(self): + """Calling resolve() on lazy proxy should force reification. + + Note: Must access lazy proxy from within a function to avoid automatic + reification by LOAD_NAME at module level. + """ + code = textwrap.dedent(""" + import sys + import types + + lazy from test.test_lazy_import.data.basic2 import x + + assert 'test.test_lazy_import.data.basic2' not in sys.modules + + def test_resolve(): + g = globals() + lazy_obj = g['x'] + assert type(lazy_obj) is types.LazyImportType, f"Expected lazy proxy, got {type(lazy_obj)}" + + resolved = lazy_obj.resolve() + + # Now module should be loaded + assert 'test.test_lazy_import.data.basic2' in sys.modules + assert resolved == 42 # x is 42 in basic2.py + return True + + assert test_resolve() + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_add_lazy_to_globals(self): + code = textwrap.dedent(""" + import sys + import types + + lazy from test.test_lazy_import.data import basic2 + + assert 'test.test_lazy_import.data.basic2' not in sys.modules + + class C: pass + sneaky = C() + sneaky.x = 1 + + def f(): + t = 0 + for _ in range(5): + t += sneaky.x + return t + + f() + globals()["sneaky"] = globals()["basic2"] + assert f() == 210 + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + +@support.requires_subprocess() +class MultipleNameFromImportTests(LazyImportTestCase): + """Tests for lazy from ... import with multiple names. + + PEP 810: "When using lazy from ... import, each imported name is bound to a + lazy proxy object. The first access to any of these names triggers loading + of the entire module and reifies only that specific name to its actual value. + Other names remain as lazy proxies until they are accessed." + """ + + def test_accessing_one_name_leaves_others_as_proxies(self): + """Accessing one name from multi-name import should leave others lazy.""" + code = textwrap.dedent(""" + import sys + import types + + lazy from test.test_lazy_import.data.basic2 import f, x + + # Neither should be loaded yet + assert 'test.test_lazy_import.data.basic2' not in sys.modules + + g = globals() + assert type(g['f']) is types.LazyImportType + assert type(g['x']) is types.LazyImportType + + # Access 'x' - this loads the module and reifies only 'x' + value = x + assert value == 42 + + # Module is now loaded + assert 'test.test_lazy_import.data.basic2' in sys.modules + + # 'x' should be reified (int), 'f' should still be lazy proxy + assert type(g['x']) is int, f"Expected int, got {type(g['x'])}" + assert type(g['f']) is types.LazyImportType, f"Expected LazyImportType, got {type(g['f'])}" + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_all_names_reified_after_all_accessed(self): + """All names should be reified after each is accessed.""" + code = textwrap.dedent(""" + import sys + import types + + lazy from test.test_lazy_import.data.basic2 import f, x + + g = globals() + + # Access both + _ = x + _ = f + + # Both should be reified now + assert type(g['x']) is int + assert callable(g['f']) + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + +@support.requires_subprocess() +class SysLazyModulesTrackingTests(LazyImportTestCase): + """Tests for sys.lazy_modules tracking behavior. + + PEP 810: "When the module is reified, it's removed from sys.lazy_modules" + """ + + def test_module_added_to_lazy_modules_on_lazy_import(self): + """Module should be added to sys.lazy_modules when lazily imported.""" + # PEP 810 states lazy_modules tracks modules that have been lazily imported + # Note: The current implementation keeps modules in lazy_modules even after + # reification (primarily for diagnostics and introspection) + code = textwrap.dedent(""" + import sys + + initial_count = len(sys.lazy_modules) + + lazy import test.test_lazy_import.data.basic2 + + # Should be in lazy_modules after lazy import + assert "test.test_lazy_import.data.basic2" in sys.lazy_modules + assert len(sys.lazy_modules) > initial_count + + # Trigger reification + _ = test.test_lazy_import.data.basic2.x + + # Module should still be tracked (for diagnostics per PEP 810) + assert "test.test_lazy_import.data.basic2" not in sys.lazy_modules + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_lazy_modules_is_per_interpreter(self): + """Each interpreter should have independent sys.lazy_modules.""" + # Basic test that sys.lazy_modules exists and is a set + self.assertIsInstance(sys.lazy_modules, set) + + def test_lazy_module_without_children_is_tracked(self): + code = textwrap.dedent(""" + import sys + lazy import json + assert "json" in sys.lazy_modules, ( + f"expected 'json' in sys.lazy_modules, got {set(sys.lazy_modules)}" + ) + print("OK") + """) + assert_python_ok("-c", code) + + +@support.requires_subprocess() +class CommandLineAndEnvVarTests(unittest.TestCase): + """Tests for command-line and environment variable control. + + PEP 810: The global lazy imports flag can be controlled through: + - The -X lazy_imports=<mode> command-line option + - The PYTHON_LAZY_IMPORTS=<mode> environment variable + """ + + def test_cli_lazy_imports_all_makes_regular_imports_lazy(self): + """-X lazy_imports=all should make all imports potentially lazy.""" + code = textwrap.dedent(""" + import sys + # In 'all' mode, regular imports become lazy + import json + # json should not be in sys.modules yet (lazy) + # Actually accessing it triggers reification + if 'json' not in sys.modules: + print("LAZY") + else: + print("EAGER") + """) + result = subprocess.run( + [sys.executable, "-X", "lazy_imports=all", "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}") + self.assertIn("LAZY", result.stdout) + + def test_cli_lazy_imports_none_is_rejected(self): + """-X lazy_imports=none should be rejected.""" + result = subprocess.run( + [sys.executable, "-X", "lazy_imports=none", "-c", "pass"], + capture_output=True, + text=True + ) + self.assertNotEqual(result.returncode, 0) + self.assertIn("-X lazy_imports: invalid value", result.stderr) + self.assertIn("expected 'all' or 'normal'", result.stderr) + + def test_cli_lazy_imports_normal_respects_lazy_keyword_only(self): + """-X lazy_imports=normal should respect lazy keyword only.""" + # Note: Use test modules instead of stdlib modules to avoid + # modules already loaded by the interpreter startup + code = textwrap.dedent(""" + import sys + import test.test_lazy_import.data.basic2 # Should be eager + lazy import test.test_lazy_import.data.pkg.b # Should be lazy + + eager_loaded = 'test.test_lazy_import.data.basic2' in sys.modules + lazy_loaded = 'test.test_lazy_import.data.pkg.b' in sys.modules + + if eager_loaded and not lazy_loaded: + print("OK") + else: + print(f"FAIL: eager={eager_loaded}, lazy={lazy_loaded}") + """) + result = subprocess.run( + [sys.executable, "-X", "lazy_imports=normal", "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_env_var_lazy_imports_all_enables_global_lazy(self): + """PYTHON_LAZY_IMPORTS=all should enable global lazy imports.""" + code = textwrap.dedent(""" + import sys + import json + if 'json' not in sys.modules: + print("LAZY") + else: + print("EAGER") + """) + import os + env = os.environ.copy() + env["PYTHON_LAZY_IMPORTS"] = "all" + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True, + env=env + ) + self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}") + self.assertIn("LAZY", result.stdout) + + def test_env_var_lazy_imports_none_is_rejected(self): + """PYTHON_LAZY_IMPORTS=none should be rejected.""" + import os + env = os.environ.copy() + env["PYTHON_LAZY_IMPORTS"] = "none" + result = subprocess.run( + [sys.executable, "-c", "pass"], + capture_output=True, + text=True, + env=env + ) + self.assertNotEqual(result.returncode, 0) + self.assertIn("PYTHON_LAZY_IMPORTS: invalid value", result.stderr) + self.assertIn("expected 'all' or 'normal'", result.stderr) + + def test_cli_overrides_env_var(self): + """Command-line option should take precedence over environment variable.""" + # PEP 810: -X lazy_imports takes precedence over PYTHON_LAZY_IMPORTS + code = textwrap.dedent(""" + import sys + import json + if 'json' in sys.modules: + print("EAGER") + else: + print("LAZY") + """) + import os + env = os.environ.copy() + env["PYTHON_LAZY_IMPORTS"] = "all" # env says all imports are lazy + result = subprocess.run( + [sys.executable, "-X", "lazy_imports=normal", "-c", code], + capture_output=True, + text=True, + env=env + ) + self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}") + # CLI should win, so a regular import should stay eager. + self.assertIn("EAGER", result.stdout) + + def test_sys_set_lazy_imports_overrides_cli(self): + """sys.set_lazy_imports() should take precedence over CLI option.""" + code = textwrap.dedent(""" + import sys + sys.set_lazy_imports("normal") # Override CLI + import json + if 'json' in sys.modules: + print("EAGER") + else: + print("LAZY") + """) + result = subprocess.run( + [sys.executable, "-X", "lazy_imports=all", "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}") + self.assertIn("EAGER", result.stdout) + + +@support.requires_subprocess() +class FilterFunctionSignatureTests(LazyImportTestCase): + """Tests for the filter function signature per PEP 810. + + PEP 810: func(importer: str, name: str, fromlist: tuple[str, ...] | None) -> bool + """ + + def _run_subprocess_with_modules(self, code, files): + with tempfile.TemporaryDirectory() as tmpdir: + for relpath, contents in files.items(): + path = os.path.join(tmpdir, relpath) + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w", encoding="utf-8") as file: + file.write(textwrap.dedent(contents)) + + env = os.environ.copy() + env["PYTHONPATH"] = os.pathsep.join( + entry for entry in (tmpdir, env.get("PYTHONPATH")) if entry + ) + env["PYTHON_LAZY_IMPORTS"] = "normal" + + result = subprocess.run( + [sys.executable, "-c", textwrap.dedent(code)], + capture_output=True, + cwd=tmpdir, + env=env, + text=True, + ) + return result + + def _assert_subprocess_ok(self, code, files): + result = self._run_subprocess_with_modules(code, files) + self.assertEqual( + result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}" + ) + return result + + def test_filter_receives_correct_arguments_for_import(self): + """Filter should receive (importer, name, fromlist=None) for 'import x'.""" + code = textwrap.dedent(""" + import sys + + received_args = [] + + def my_filter(importer, name, fromlist): + received_args.append((importer, name, fromlist)) + return True + + sys.set_lazy_imports_filter(my_filter) + + lazy import json + + assert len(received_args) == 1, f"Expected 1 call, got {len(received_args)}" + importer, name, fromlist = received_args[0] + assert name == "json", f"Expected name='json', got {name!r}" + assert fromlist is None, f"Expected fromlist=None, got {fromlist!r}" + assert isinstance(importer, str), f"Expected str importer, got {type(importer)}" + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_filter_receives_fromlist_for_from_import(self): + """Filter should receive fromlist tuple for 'from x import y, z'.""" + code = textwrap.dedent(""" + import sys + + received_args = [] + + def my_filter(importer, name, fromlist): + received_args.append((importer, name, fromlist)) + return True + + sys.set_lazy_imports_filter(my_filter) + + lazy from json import dumps, loads + + assert len(received_args) == 1, f"Expected 1 call, got {len(received_args)}" + importer, name, fromlist = received_args[0] + assert name == "json", f"Expected name='json', got {name!r}" + assert fromlist == ("dumps", "loads"), f"Expected ('dumps', 'loads'), got {fromlist!r}" + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_filter_returning_false_forces_eager_import(self): + """Filter returning False should make import eager.""" + code = textwrap.dedent(""" + import sys + + def deny_filter(importer, name, fromlist): + return False + + sys.set_lazy_imports_filter(deny_filter) + + lazy import json + + # Should be eager due to filter + if 'json' in sys.modules: + print("EAGER") + else: + print("LAZY") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}") + self.assertIn("EAGER", result.stdout) + + def test_filter_distinguishes_absolute_and_relative_from_imports(self): + """Relative imports should pass resolved module names to the filter.""" + files = { + "target.py": """ + VALUE = "absolute" + """, + "pkg/__init__.py": "", + "pkg/target.py": """ + VALUE = "relative" + """, + "pkg/runner.py": """ + import sys + + seen = [] + + def my_filter(importer, name, fromlist): + seen.append((importer, name, fromlist)) + return True + + sys.set_lazy_imports_filter(my_filter) + + lazy from target import VALUE as absolute_value + lazy from .target import VALUE as relative_value + + assert seen == [ + (__name__, "target", ("VALUE",)), + (__name__, "pkg.target", ("VALUE",)), + ], seen + """, + } + + result = self._assert_subprocess_ok( + """ + import pkg.runner + print("OK") + """, + files, + ) + self.assertIn("OK", result.stdout) + + def test_filter_receives_resolved_name_for_relative_package_import(self): + """'lazy from . import x' should report the resolved package name.""" + files = { + "pkg/__init__.py": "", + "pkg/sibling.py": """ + VALUE = 1 + """, + "pkg/runner.py": """ + import sys + + seen = [] + + def my_filter(importer, name, fromlist): + seen.append((importer, name, fromlist)) + return True + + sys.set_lazy_imports_filter(my_filter) + + lazy from . import sibling + + assert seen == [ + (__name__, "pkg", ("sibling",)), + ], seen + """, + } + + result = self._assert_subprocess_ok( + """ + import pkg.runner + print("OK") + """, + files, + ) + self.assertIn("OK", result.stdout) + + def test_filter_receives_resolved_name_for_parent_relative_import(self): + """Parent relative imports should also use the resolved module name.""" + files = { + "pkg/__init__.py": "", + "pkg/target.py": """ + VALUE = 1 + """, + "pkg/sub/__init__.py": "", + "pkg/sub/runner.py": """ + import sys + + seen = [] + + def my_filter(importer, name, fromlist): + seen.append((importer, name, fromlist)) + return True + + sys.set_lazy_imports_filter(my_filter) + + lazy from ..target import VALUE + + assert seen == [ + (__name__, "pkg.target", ("VALUE",)), + ], seen + """, + } + + result = self._assert_subprocess_ok( + """ + import pkg.sub.runner + print("OK") + """, + files, + ) + self.assertIn("OK", result.stdout) + + def test_filter_can_force_eager_only_for_resolved_relative_target(self): + """Resolved names should let filters treat relative and absolute imports differently.""" + files = { + "target.py": """ + VALUE = "absolute" + """, + "pkg/__init__.py": "", + "pkg/target.py": """ + VALUE = "relative" + """, + "pkg/runner.py": """ + import sys + + def my_filter(importer, name, fromlist): + return name != "pkg.target" + + sys.set_lazy_imports_filter(my_filter) + + lazy from target import VALUE as absolute_value + lazy from .target import VALUE as relative_value + + assert "pkg.target" in sys.modules, sorted( + name for name in sys.modules + if name in {"target", "pkg.target"} + ) + assert "target" not in sys.modules, sorted( + name for name in sys.modules + if name in {"target", "pkg.target"} + ) + assert relative_value == "relative", relative_value + """, + } + + result = self._assert_subprocess_ok( + """ + import pkg.runner + print("OK") + """, + files, + ) + self.assertIn("OK", result.stdout) + + +class AdditionalSyntaxRestrictionTests(LazyImportTestCase): + """Additional syntax restriction tests per PEP 810.""" + + def test_lazy_import_inside_class_raises_syntax_error(self): + """lazy import inside class body should raise SyntaxError.""" + # PEP 810: "The soft keyword is only allowed at the global (module) level, + # not inside functions, class bodies, try blocks, or import *" + with self.assertRaises(SyntaxError): + import test.test_lazy_import.data.badsyntax.lazy_class_body + + +@support.requires_subprocess() +class MixedLazyEagerImportTests(LazyImportTestCase): + """Tests for mixing lazy and eager imports of the same module. + + PEP 810: "If module foo is imported both lazily and eagerly in the same + program, the eager import takes precedence and both bindings resolve to + the same module object." + """ + + def test_eager_import_before_lazy_resolves_to_same_module(self): + """Eager import before lazy should make lazy resolve to same module.""" + code = textwrap.dedent(""" + import sys + import json # Eager import first + + lazy import json as lazy_json # Lazy import same module + + # lazy_json should resolve to the same object + assert json is lazy_json, "Lazy and eager imports should resolve to same module" + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_lazy_import_before_eager_resolves_to_same_module(self): + """Lazy import followed by eager should both point to same module.""" + code = textwrap.dedent(""" + import sys + + lazy import json as lazy_json + + # Lazy not loaded yet + assert 'json' not in sys.modules + + import json # Eager import triggers load + + # Both should be the same object + assert json is lazy_json + print("OK") + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + +class RelativeImportTests(LazyImportTestCase): + """Tests for relative imports with lazy keyword.""" + + def test_relative_lazy_import(self): + """lazy from . import submodule should work.""" + from test.test_lazy_import.data import relative_lazy + + # basic2 should not be loaded yet + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + # Access triggers reification + result = relative_lazy.get_basic2() + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + def test_relative_lazy_from_import(self): + """lazy from .module import name should work.""" + from test.test_lazy_import.data import relative_lazy_from + + # basic2 should not be loaded yet + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + # Access triggers reification + result = relative_lazy_from.get_x() + self.assertEqual(result, 42) + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + +class LazyModulesCompatibilityFromImportTests(LazyImportTestCase): + """Tests for __lazy_modules__ with from imports. + + PEP 810: "When a module is made lazy this way, from-imports using that + module are also lazy" + """ + + def test_lazy_modules_makes_from_imports_lazy(self): + """__lazy_modules__ should make from imports of listed modules lazy.""" + from test.test_lazy_import.data import lazy_compat_from + + # basic2 should not be loaded yet because it's in __lazy_modules__ + self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules) + + # Access triggers reification + result = lazy_compat_from.get_x() + self.assertEqual(result, 42) + self.assertIn("test.test_lazy_import.data.basic2", sys.modules) + + +@support.requires_subprocess() +class ImportStateAtReificationTests(LazyImportTestCase): + """Tests for import system state at reification time. + + PEP 810: "Reification still calls __import__ to resolve the import, which uses + the state of the import system (e.g. sys.path, sys.meta_path, sys.path_hooks + and __import__) at reification time, not the state when the lazy import + statement was evaluated." + """ + + def test_sys_path_at_reification_time_is_used(self): + """sys.path changes after lazy import should affect reification.""" + code = textwrap.dedent(""" + import sys + import tempfile + import os + + # Create a temporary module + with tempfile.TemporaryDirectory() as tmpdir: + mod_path = os.path.join(tmpdir, "dynamic_test_module.py") + with open(mod_path, "w") as f: + f.write("VALUE = 'from_temp_dir'\\n") + + # Lazy import before adding to path + lazy import dynamic_test_module + + # Module cannot be found yet + try: + _ = dynamic_test_module + print("FAIL - should not find module") + except ModuleNotFoundError: + pass + + # Now add temp dir to path + sys.path.insert(0, tmpdir) + + # Now reification should succeed using current sys.path + assert dynamic_test_module.VALUE == 'from_temp_dir' + print("OK") + + sys.path.remove(tmpdir) + """) + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + +@support.requires_subprocess() +class ThreadSafetyTests(LazyImportTestCase): + """Tests for thread-safety of lazy imports.""" + + def test_concurrent_lazy_import_reification(self): + """Multiple threads racing to reify the same lazy import should succeed.""" + from test.test_lazy_import.data import basic_unused + + num_threads = 10 + results = [None] * num_threads + errors = [] + barrier = threading.Barrier(num_threads) + + def access_lazy_import(idx): + try: + barrier.wait() + module = basic_unused.test.test_lazy_import.data.basic2 + results[idx] = module + except Exception as e: + errors.append((idx, e)) + + threads = [ + threading.Thread(target=access_lazy_import, args=(i,)) + for i in range(num_threads) + ] + + for t in threads: + t.start() + for t in threads: + t.join() + + self.assertEqual(errors, [], f"Errors occurred: {errors}") + self.assertTrue(all(r is not None for r in results)) + first_module = results[0] + for r in results[1:]: + self.assertIs(r, first_module) + + def test_concurrent_reification_multiple_modules(self): + """Multiple threads reifying different lazy imports concurrently.""" + code = textwrap.dedent(""" + import sys + import threading + + sys.set_lazy_imports("all") + + lazy import json + lazy import os + lazy import io + lazy import re + + num_threads = 8 + results = {} + errors = [] + barrier = threading.Barrier(num_threads) + + def access_modules(idx): + try: + barrier.wait() + mods = [json, os, io, re] + results[idx] = [type(m).__name__ for m in mods] + except Exception as e: + errors.append((idx, e)) + + threads = [ + threading.Thread(target=access_modules, args=(i,)) + for i in range(num_threads) + ] + + for t in threads: + t.start() + for t in threads: + t.join() + + assert not errors, f"Errors: {errors}" + for idx, mods in results.items(): + assert all(m == 'module' for m in mods), f"Thread {idx} got: {mods}" + + print("OK") + """) + + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_concurrent_lazy_modules_dict_updates(self): + """Multiple threads creating lazy imports should safely update sys.lazy_modules.""" + code = textwrap.dedent(""" + import sys + import threading + + num_threads = 16 + iterations = 50 + errors = [] + barrier = threading.Barrier(num_threads) + + def create_lazy_imports(idx): + try: + barrier.wait() + for i in range(iterations): + exec(f"lazy import json as json_{idx}_{i}", globals()) + exec(f"lazy import os as os_{idx}_{i}", globals()) + except Exception as e: + errors.append((idx, e)) + + threads = [ + threading.Thread(target=create_lazy_imports, args=(i,)) + for i in range(num_threads) + ] + + for t in threads: + t.start() + for t in threads: + t.join() + + assert not errors, f"Errors: {errors}" + assert isinstance(sys.lazy_modules, set), "sys.lazy_modules is not a dict" + print("OK") + """) + + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_concurrent_reification_same_module_high_contention(self): + """High contention: many threads reifying the exact same lazy import.""" + code = textwrap.dedent(""" + import sys + import threading + import types + + sys.set_lazy_imports("all") + + lazy import json + + num_threads = 20 + results = [None] * num_threads + errors = [] + barrier = threading.Barrier(num_threads) + + def access_json(idx): + try: + barrier.wait() + for _ in range(100): + _ = json.dumps + _ = json.loads + results[idx] = json + except Exception as e: + errors.append((idx, e)) + + threads = [ + threading.Thread(target=access_json, args=(i,)) + for i in range(num_threads) + ] + + for t in threads: + t.start() + for t in threads: + t.join() + + assert not errors, f"Errors: {errors}" + assert all(r is not None for r in results), "Some threads got None" + first = results[0] + assert all(r is first for r in results), "Inconsistent module objects" + assert not isinstance(first, types.LazyImportType), "Got lazy import instead of module" + print("OK") + """) + + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + def test_concurrent_reification_with_module_attribute_access(self): + """Threads racing to reify and immediately access module attributes.""" + code = textwrap.dedent(""" + import sys + import threading + + sys.set_lazy_imports("all") + + lazy import collections + lazy import functools + lazy import itertools + + num_threads = 12 + results = {} + errors = [] + barrier = threading.Barrier(num_threads) + + def stress_lazy_imports(idx): + try: + barrier.wait() + for _ in range(50): + _ = collections.OrderedDict + _ = functools.partial + _ = itertools.chain + _ = collections.defaultdict + _ = functools.lru_cache + _ = itertools.islice + results[idx] = ( + type(collections).__name__, + type(functools).__name__, + type(itertools).__name__, + ) + except Exception as e: + errors.append((idx, e)) + + threads = [ + threading.Thread(target=stress_lazy_imports, args=(i,)) + for i in range(num_threads) + ] + + for t in threads: + t.start() + for t in threads: + t.join() + + assert not errors, f"Errors: {errors}" + for idx, types_tuple in results.items(): + assert all(t == 'module' for t in types_tuple), f"Thread {idx}: {types_tuple}" + print("OK") + """) + + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True + ) + self.assertEqual(result.returncode, 0, f"stdout: {result.stdout}, stderr: {result.stderr}") + self.assertIn("OK", result.stdout) + + +class LazyImportDisTests(unittest.TestCase): + def test_lazy_import_dis(self): + """dis should properly show lazy import""" + code = compile("lazy import foo", "exec", "exec") + f = io.StringIO() + dis.dis(code, file=f) + self.assertIn("foo + lazy", f.getvalue()) + + def test_normal_import_dis(self): + """non lazy imports should just show the name""" + code = compile("import foo", "exec", "exec") + f = io.StringIO() + dis.dis(code, file=f) + for line in f.getvalue().split('\n'): + if "IMPORT_NAME" in line: + self.assertIn("(foo)", line) + break + else: + self.assertFail("IMPORT_NAME not found") + + +@unittest.skipIf(_testcapi is None, 'need the _testcapi module') +class LazyCApiTests(LazyImportTestCase): + def test_set_matches_sys(self): + self.assertEqual(_testcapi.PyImport_GetLazyImportsMode(), sys.get_lazy_imports()) + for mode in ("normal", "all"): + _testcapi.PyImport_SetLazyImportsMode(mode) + self.assertEqual(_testcapi.PyImport_GetLazyImportsMode(), sys.get_lazy_imports()) + self.assertRaises(ValueError, _testcapi.PyImport_SetLazyImportsMode, "none") + + def test_filter_matches_sys(self): + self.assertEqual(_testcapi.PyImport_GetLazyImportsFilter(), sys.get_lazy_imports_filter()) + + def filter(*args): + pass + + _testcapi.PyImport_SetLazyImportsFilter(filter) + self.assertEqual(_testcapi.PyImport_GetLazyImportsFilter(), sys.get_lazy_imports_filter()) + + def test_set_bad_filter(self): + self.assertRaises(ValueError, _testcapi.PyImport_SetLazyImportsFilter, 42) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_lazy_import/__main__.py b/Lib/test/test_lazy_import/__main__.py new file mode 100644 index 00000000000000..d6c94efaf30833 --- /dev/null +++ b/Lib/test/test_lazy_import/__main__.py @@ -0,0 +1,3 @@ +import unittest + +unittest.main('test.test_lazy_import') diff --git a/Lib/test/test_lazy_import/data/badsyntax/lazy_class_body.py b/Lib/test/test_lazy_import/data/badsyntax/lazy_class_body.py new file mode 100644 index 00000000000000..e154b78f6cdc13 --- /dev/null +++ b/Lib/test/test_lazy_import/data/badsyntax/lazy_class_body.py @@ -0,0 +1,3 @@ +# SyntaxError: lazy import inside class body is not allowed +class Foo: + lazy import json diff --git a/Lib/test/test_lazy_import/data/badsyntax/lazy_future_import.py b/Lib/test/test_lazy_import/data/badsyntax/lazy_future_import.py new file mode 100644 index 00000000000000..8bd258b76b4cbd --- /dev/null +++ b/Lib/test/test_lazy_import/data/badsyntax/lazy_future_import.py @@ -0,0 +1 @@ +lazy from __future__ import annotations diff --git a/Lib/test/test_lazy_import/data/badsyntax/lazy_import_func.py b/Lib/test/test_lazy_import/data/badsyntax/lazy_import_func.py new file mode 100644 index 00000000000000..af758b51127686 --- /dev/null +++ b/Lib/test/test_lazy_import/data/badsyntax/lazy_import_func.py @@ -0,0 +1,2 @@ +def f(): + lazy import foo diff --git a/Lib/test/test_lazy_import/data/badsyntax/lazy_try_except.py b/Lib/test/test_lazy_import/data/badsyntax/lazy_try_except.py new file mode 100644 index 00000000000000..e58d1f929cae18 --- /dev/null +++ b/Lib/test/test_lazy_import/data/badsyntax/lazy_try_except.py @@ -0,0 +1,4 @@ +try: + lazy import foo +except: + pass diff --git a/Lib/test/test_lazy_import/data/badsyntax/lazy_try_except_from.py b/Lib/test/test_lazy_import/data/badsyntax/lazy_try_except_from.py new file mode 100644 index 00000000000000..8c97553f486cef --- /dev/null +++ b/Lib/test/test_lazy_import/data/badsyntax/lazy_try_except_from.py @@ -0,0 +1,4 @@ +try: + lazy from foo import bar +except: + pass diff --git a/Lib/test/test_lazy_import/data/badsyntax/lazy_try_except_from_star.py b/Lib/test/test_lazy_import/data/badsyntax/lazy_try_except_from_star.py new file mode 100644 index 00000000000000..b2370eaae771c1 --- /dev/null +++ b/Lib/test/test_lazy_import/data/badsyntax/lazy_try_except_from_star.py @@ -0,0 +1 @@ +lazy from foo import * diff --git a/Lib/test/test_lazy_import/data/basic2.py b/Lib/test/test_lazy_import/data/basic2.py new file mode 100644 index 00000000000000..f93ec89d5abfb6 --- /dev/null +++ b/Lib/test/test_lazy_import/data/basic2.py @@ -0,0 +1,4 @@ +def f(): + pass + +x = 42 diff --git a/Lib/test/test_lazy_import/data/basic_compatibility_mode.py b/Lib/test/test_lazy_import/data/basic_compatibility_mode.py new file mode 100644 index 00000000000000..33eeab8879c394 --- /dev/null +++ b/Lib/test/test_lazy_import/data/basic_compatibility_mode.py @@ -0,0 +1,2 @@ +__lazy_modules__ = ['test.test_lazy_import.data.basic2'] +import test.test_lazy_import.data.basic2 diff --git a/Lib/test/test_lazy_import/data/basic_compatibility_mode_relative.py b/Lib/test/test_lazy_import/data/basic_compatibility_mode_relative.py new file mode 100644 index 00000000000000..15c1ea77630b31 --- /dev/null +++ b/Lib/test/test_lazy_import/data/basic_compatibility_mode_relative.py @@ -0,0 +1,2 @@ +__lazy_modules__ = ['test.test_lazy_import.data.basic2'] +lazy from .basic2 import f diff --git a/Lib/test/test_lazy_import/data/basic_compatibility_mode_used.py b/Lib/test/test_lazy_import/data/basic_compatibility_mode_used.py new file mode 100644 index 00000000000000..78bb4ffa52314b --- /dev/null +++ b/Lib/test/test_lazy_import/data/basic_compatibility_mode_used.py @@ -0,0 +1,3 @@ +__lazy_modules__ = ['test.test_lazy_import.data.basic2'] +import test.test_lazy_import.data.basic2 +test.test_lazy_import.data.basic2.f() diff --git a/Lib/test/test_lazy_import/data/basic_dir.py b/Lib/test/test_lazy_import/data/basic_dir.py new file mode 100644 index 00000000000000..a88c8ed80a1dcf --- /dev/null +++ b/Lib/test/test_lazy_import/data/basic_dir.py @@ -0,0 +1,2 @@ +lazy import test.test_lazy_import.data.basic2 +x = dir() diff --git a/Lib/test/test_lazy_import/data/basic_from_unused.py b/Lib/test/test_lazy_import/data/basic_from_unused.py new file mode 100644 index 00000000000000..62fa04bc8faefd --- /dev/null +++ b/Lib/test/test_lazy_import/data/basic_from_unused.py @@ -0,0 +1 @@ +lazy from test.test_lazy_import.data import basic2 diff --git a/Lib/test/test_lazy_import/data/basic_unused.py b/Lib/test/test_lazy_import/data/basic_unused.py new file mode 100644 index 00000000000000..f3e502c59e7cdf --- /dev/null +++ b/Lib/test/test_lazy_import/data/basic_unused.py @@ -0,0 +1 @@ +lazy import test.test_lazy_import.data.basic2 diff --git a/Lib/test/test_lazy_import/data/basic_used.py b/Lib/test/test_lazy_import/data/basic_used.py new file mode 100644 index 00000000000000..7234bd2fe02a80 --- /dev/null +++ b/Lib/test/test_lazy_import/data/basic_used.py @@ -0,0 +1,3 @@ +lazy import test.test_lazy_import.data.basic2 as basic2 + +basic2.f() diff --git a/Lib/test/test_lazy_import/data/broken_attr_module.py b/Lib/test/test_lazy_import/data/broken_attr_module.py new file mode 100644 index 00000000000000..a60bca2bad02ee --- /dev/null +++ b/Lib/test/test_lazy_import/data/broken_attr_module.py @@ -0,0 +1,3 @@ +# Module that exists but doesn't have expected attributes +x = 42 +# No 'nonexistent_attr' here diff --git a/Lib/test/test_lazy_import/data/broken_module.py b/Lib/test/test_lazy_import/data/broken_module.py new file mode 100644 index 00000000000000..b49d4a4a15f1d4 --- /dev/null +++ b/Lib/test/test_lazy_import/data/broken_module.py @@ -0,0 +1,2 @@ +# Module that raises an error during import +raise ValueError("This module always fails to import") diff --git a/Lib/test/test_lazy_import/data/compatibility_mode_func.py b/Lib/test/test_lazy_import/data/compatibility_mode_func.py new file mode 100644 index 00000000000000..9405d2f832b8ee --- /dev/null +++ b/Lib/test/test_lazy_import/data/compatibility_mode_func.py @@ -0,0 +1,5 @@ +__lazy_modules__ = ['test.test_lazy_import.data.basic2'] +def f(): + import test.test_lazy_import.data.basic2 + +f() diff --git a/Lib/test/test_lazy_import/data/compatibility_mode_try_except.py b/Lib/test/test_lazy_import/data/compatibility_mode_try_except.py new file mode 100644 index 00000000000000..a746c4a34a50c9 --- /dev/null +++ b/Lib/test/test_lazy_import/data/compatibility_mode_try_except.py @@ -0,0 +1,5 @@ +__lazy_modules__ = ['test.test_lazy_import.data.basic2'] +try: + import test.test_lazy_import.data.basic2 +except: + pass diff --git a/Lib/test/test_lazy_import/data/dunder_lazy_import.py b/Lib/test/test_lazy_import/data/dunder_lazy_import.py new file mode 100644 index 00000000000000..c4f4d61623f956 --- /dev/null +++ b/Lib/test/test_lazy_import/data/dunder_lazy_import.py @@ -0,0 +1 @@ +basic = __lazy_import__('test.test_lazy_import.data.basic2') diff --git a/Lib/test/test_lazy_import/data/dunder_lazy_import_builtins.py b/Lib/test/test_lazy_import/data/dunder_lazy_import_builtins.py new file mode 100644 index 00000000000000..8b6dbd7e3f684c --- /dev/null +++ b/Lib/test/test_lazy_import/data/dunder_lazy_import_builtins.py @@ -0,0 +1,14 @@ +import sys + +def myimport(*args): + return sys.modules[__name__] + + +new_globals = dict(globals()) +new_globals["__builtins__"] = { + "__import__": myimport, +} +basic2 = 42 +basic = __lazy_import__("test.test_lazy_import.data.basic2", + globals=new_globals) +basic diff --git a/Lib/test/test_lazy_import/data/dunder_lazy_import_used.py b/Lib/test/test_lazy_import/data/dunder_lazy_import_used.py new file mode 100644 index 00000000000000..42c026994b1094 --- /dev/null +++ b/Lib/test/test_lazy_import/data/dunder_lazy_import_used.py @@ -0,0 +1,3 @@ +basic = __lazy_import__('test.test_lazy_import.data', + fromlist=("basic2", )) +basic diff --git a/Lib/test/test_lazy_import/data/eager_import_func.py b/Lib/test/test_lazy_import/data/eager_import_func.py new file mode 100644 index 00000000000000..1f7130209d4b56 --- /dev/null +++ b/Lib/test/test_lazy_import/data/eager_import_func.py @@ -0,0 +1,3 @@ +def f(): + import test.test_lazy_import.data.basic2 as basic2 + return basic2 diff --git a/Lib/test/test_lazy_import/data/global_filter.py b/Lib/test/test_lazy_import/data/global_filter.py new file mode 100644 index 00000000000000..0e2311842dfcbd --- /dev/null +++ b/Lib/test/test_lazy_import/data/global_filter.py @@ -0,0 +1,10 @@ +import sys + +def filter(module_name, imported_name, from_list): + assert module_name == __name__ + assert imported_name == "test.test_lazy_import.data.basic2" + return False + +sys.set_lazy_imports_filter(filter) + +lazy import test.test_lazy_import.data.basic2 as basic2 diff --git a/Lib/test/test_lazy_import/data/global_filter_from.py b/Lib/test/test_lazy_import/data/global_filter_from.py new file mode 100644 index 00000000000000..da9d22e87277f8 --- /dev/null +++ b/Lib/test/test_lazy_import/data/global_filter_from.py @@ -0,0 +1,11 @@ +import sys + +def filter(module_name, imported_name, from_list): + assert module_name == __name__ + assert imported_name == "test.test_lazy_import.data.basic2" + assert from_list == ('f',) + return False + +sys.set_lazy_imports_filter(filter) + +lazy from test.test_lazy_import.data.basic2 import f diff --git a/Lib/test/test_lazy_import/data/global_filter_from_true.py b/Lib/test/test_lazy_import/data/global_filter_from_true.py new file mode 100644 index 00000000000000..2d8b1de4c7c400 --- /dev/null +++ b/Lib/test/test_lazy_import/data/global_filter_from_true.py @@ -0,0 +1,12 @@ +import sys + +def filter(module_name, imported_name, from_list): + assert module_name == __name__ + assert imported_name == "test.test_lazy_import.data.basic2" + assert from_list == ('f',) + return True + +sys.set_lazy_imports("normal") +sys.set_lazy_imports_filter(filter) + +lazy from test.test_lazy_import.data.basic2 import f diff --git a/Lib/test/test_lazy_import/data/global_filter_true.py b/Lib/test/test_lazy_import/data/global_filter_true.py new file mode 100644 index 00000000000000..da4abeacf87cda --- /dev/null +++ b/Lib/test/test_lazy_import/data/global_filter_true.py @@ -0,0 +1,11 @@ +import sys + +def filter(module_name, imported_name, from_list): + assert module_name == __name__ + assert imported_name == "test.test_lazy_import.data.basic2" + return True + +sys.set_lazy_imports("normal") +sys.set_lazy_imports_filter(filter) + +lazy import test.test_lazy_import.data.basic2 as basic2 diff --git a/Lib/test/test_lazy_import/data/global_on.py b/Lib/test/test_lazy_import/data/global_on.py new file mode 100644 index 00000000000000..ddacab5468bab9 --- /dev/null +++ b/Lib/test/test_lazy_import/data/global_on.py @@ -0,0 +1,5 @@ +import sys + +sys.set_lazy_imports("all") + +import test.test_lazy_import.data.basic2 as basic2 diff --git a/Lib/test/test_lazy_import/data/globals_access.py b/Lib/test/test_lazy_import/data/globals_access.py new file mode 100644 index 00000000000000..af6fc40115b21c --- /dev/null +++ b/Lib/test/test_lazy_import/data/globals_access.py @@ -0,0 +1,9 @@ +# Test that globals() returns lazy proxy objects without reifying +lazy import test.test_lazy_import.data.basic2 as basic2 + +def get_from_globals(): + g = globals() + return g['basic2'] + +def get_direct(): + return basic2 diff --git a/Lib/test/test_lazy_import/data/lazy_compat_from.py b/Lib/test/test_lazy_import/data/lazy_compat_from.py new file mode 100644 index 00000000000000..d6f7ed9a78538b --- /dev/null +++ b/Lib/test/test_lazy_import/data/lazy_compat_from.py @@ -0,0 +1,6 @@ +# Test __lazy_modules__ with from imports +__lazy_modules__ = ['test.test_lazy_import.data.basic2'] +from test.test_lazy_import.data.basic2 import x, f + +def get_x(): + return x diff --git a/Lib/test/test_lazy_import/data/lazy_get_value.py b/Lib/test/test_lazy_import/data/lazy_get_value.py new file mode 100644 index 00000000000000..f216dc52f292fa --- /dev/null +++ b/Lib/test/test_lazy_import/data/lazy_get_value.py @@ -0,0 +1,7 @@ +lazy import test.test_lazy_import.data.basic2 as basic2 + +def f(): + x = globals() + return x['basic2'].resolve() + +f() diff --git a/Lib/test/test_lazy_import/data/lazy_import_pkg.py b/Lib/test/test_lazy_import/data/lazy_import_pkg.py new file mode 100644 index 00000000000000..f237b50fd422ff --- /dev/null +++ b/Lib/test/test_lazy_import/data/lazy_import_pkg.py @@ -0,0 +1,2 @@ +lazy import test.test_lazy_import.data.pkg.bar +x = test.test_lazy_import.data.pkg.bar.f diff --git a/Lib/test/test_lazy_import/data/lazy_with.py b/Lib/test/test_lazy_import/data/lazy_with.py new file mode 100644 index 00000000000000..30cf9d377624da --- /dev/null +++ b/Lib/test/test_lazy_import/data/lazy_with.py @@ -0,0 +1,3 @@ +import contextlib +with contextlib.nullcontext(): + lazy import test.test_lazy_import.data.basic2 diff --git a/Lib/test/test_lazy_import/data/lazy_with_from.py b/Lib/test/test_lazy_import/data/lazy_with_from.py new file mode 100644 index 00000000000000..794ba33f828596 --- /dev/null +++ b/Lib/test/test_lazy_import/data/lazy_with_from.py @@ -0,0 +1,3 @@ +import contextlib +with contextlib.nullcontext(): + lazy import test.test_lazy_import.data.basic2 as basic2 diff --git a/Lib/test/test_lazy_import/data/module_with_getattr.py b/Lib/test/test_lazy_import/data/module_with_getattr.py new file mode 100644 index 00000000000000..db3a2301075c2e --- /dev/null +++ b/Lib/test/test_lazy_import/data/module_with_getattr.py @@ -0,0 +1,12 @@ +def __getattr__(name): + if name == "dynamic_attr": + return "from_getattr" + elif name == "raising_attr": + raise ValueError("from_getattr") + elif name == "import_error_attr": + raise ImportError(name) + elif name == "warning_attr": + import warnings + warnings.warn("from_getattr", category=UserWarning) + return "from_warning_attr" + raise AttributeError(name) diff --git a/Lib/test/test_lazy_import/data/modules_dict.py b/Lib/test/test_lazy_import/data/modules_dict.py new file mode 100644 index 00000000000000..562a3b9a62f8d3 --- /dev/null +++ b/Lib/test/test_lazy_import/data/modules_dict.py @@ -0,0 +1,5 @@ +lazy import test.test_lazy_import.data.basic2 as basic2 + +import sys +mod = sys.modules[__name__] +x = mod.__dict__ diff --git a/Lib/test/test_lazy_import/data/modules_getattr.py b/Lib/test/test_lazy_import/data/modules_getattr.py new file mode 100644 index 00000000000000..f0b8e2bd0613b8 --- /dev/null +++ b/Lib/test/test_lazy_import/data/modules_getattr.py @@ -0,0 +1,5 @@ +lazy import test.test_lazy_import.data.basic2 as basic2 + +import sys +mod = sys.modules[__name__] +x = mod.basic2 diff --git a/Lib/test/test_lazy_import/data/modules_getattr_other.py b/Lib/test/test_lazy_import/data/modules_getattr_other.py new file mode 100644 index 00000000000000..642840c28722b3 --- /dev/null +++ b/Lib/test/test_lazy_import/data/modules_getattr_other.py @@ -0,0 +1,5 @@ +lazy import test.test_lazy_import.data.basic2 as basic2 + +import sys +mod = sys.modules[__name__] +x = mod.__name__ diff --git a/Lib/test/test_lazy_import/data/multi_from_import.py b/Lib/test/test_lazy_import/data/multi_from_import.py new file mode 100644 index 00000000000000..dfd875975dd2f7 --- /dev/null +++ b/Lib/test/test_lazy_import/data/multi_from_import.py @@ -0,0 +1,5 @@ +# Test that lazy from import with multiple names only reifies accessed names +lazy from test.test_lazy_import.data.basic2 import f, x + +def get_globals(): + return globals() diff --git a/Lib/test/test_lazy_import/data/pkg/__init__.py b/Lib/test/test_lazy_import/data/pkg/__init__.py new file mode 100644 index 00000000000000..5f7b8662596cac --- /dev/null +++ b/Lib/test/test_lazy_import/data/pkg/__init__.py @@ -0,0 +1,14 @@ +x = 42 + +def __getattr__(name): + if name == "dynamic_attr": + return "from_getattr" + elif name == "raising_attr": + raise ValueError("from_getattr") + elif name == "import_error_attr": + raise ImportError(name) + elif name == "warning_attr": + import warnings + warnings.warn("from_getattr", category=UserWarning) + return "from_warning_attr" + raise AttributeError(name) diff --git a/Lib/test/test_lazy_import/data/pkg/b.py b/Lib/test/test_lazy_import/data/pkg/b.py new file mode 100644 index 00000000000000..a266b7c7c0d165 --- /dev/null +++ b/Lib/test/test_lazy_import/data/pkg/b.py @@ -0,0 +1,2 @@ +def foo(): + return 'foo' diff --git a/Lib/test/test_lazy_import/data/pkg/bar.py b/Lib/test/test_lazy_import/data/pkg/bar.py new file mode 100644 index 00000000000000..b8d8b60b886b88 --- /dev/null +++ b/Lib/test/test_lazy_import/data/pkg/bar.py @@ -0,0 +1,2 @@ +print("BAR_MODULE_LOADED") +def f(): pass diff --git a/Lib/test/test_lazy_import/data/pkg/c.py b/Lib/test/test_lazy_import/data/pkg/c.py new file mode 100644 index 00000000000000..0bb031198648ab --- /dev/null +++ b/Lib/test/test_lazy_import/data/pkg/c.py @@ -0,0 +1,4 @@ +lazy from . import b, x + +def get_globals(): + return globals() diff --git a/Lib/test/test_lazy_import/data/relative_lazy.py b/Lib/test/test_lazy_import/data/relative_lazy.py new file mode 100644 index 00000000000000..6273d3883abf88 --- /dev/null +++ b/Lib/test/test_lazy_import/data/relative_lazy.py @@ -0,0 +1,5 @@ +# Test relative imports with lazy keyword +lazy from . import basic2 + +def get_basic2(): + return basic2 diff --git a/Lib/test/test_lazy_import/data/relative_lazy_from.py b/Lib/test/test_lazy_import/data/relative_lazy_from.py new file mode 100644 index 00000000000000..1bae2d6853dfcf --- /dev/null +++ b/Lib/test/test_lazy_import/data/relative_lazy_from.py @@ -0,0 +1,8 @@ +# Test relative from imports with lazy keyword +lazy from .basic2 import x, f + +def get_x(): + return x + +def get_f(): + return f diff --git a/Lib/test/test_lazy_import/data/try_except_eager.py b/Lib/test/test_lazy_import/data/try_except_eager.py new file mode 100644 index 00000000000000..90b4bc62898d2a --- /dev/null +++ b/Lib/test/test_lazy_import/data/try_except_eager.py @@ -0,0 +1,4 @@ +try: + import test.test_lazy_import.data.basic2 +except: + pass diff --git a/Lib/test/test_lazy_import/data/try_except_eager_from.py b/Lib/test/test_lazy_import/data/try_except_eager_from.py new file mode 100644 index 00000000000000..1e6c650d0d1c7d --- /dev/null +++ b/Lib/test/test_lazy_import/data/try_except_eager_from.py @@ -0,0 +1,4 @@ +try: + from test.test_lazy_import.data.basic2 import f +except: + pass diff --git a/Lib/test/test_linecache.py b/Lib/test/test_linecache.py index 02f65338428c8f..fcd94edc611fac 100644 --- a/Lib/test/test_linecache.py +++ b/Lib/test/test_linecache.py @@ -259,22 +259,44 @@ def raise_memoryerror(*args, **kwargs): def test_loader(self): filename = 'scheme://path' - for loader in (None, object(), NoSourceLoader()): + linecache.clearcache() + module_globals = {'__name__': 'a.b.c', '__loader__': None} + self.assertEqual(linecache.getlines(filename, module_globals), []) + + for loader in object(), NoSourceLoader(): linecache.clearcache() module_globals = {'__name__': 'a.b.c', '__loader__': loader} - self.assertEqual(linecache.getlines(filename, module_globals), []) + with self.assertWarns(DeprecationWarning) as w: + self.assertEqual(linecache.getlines(filename, module_globals), []) + self.assertEqual(str(w.warning), + 'Module globals is missing a __spec__.loader') linecache.clearcache() module_globals = {'__name__': 'a.b.c', '__loader__': FakeLoader()} - self.assertEqual(linecache.getlines(filename, module_globals), - ['source for a.b.c\n']) + with self.assertWarns(DeprecationWarning) as w: + self.assertEqual(linecache.getlines(filename, module_globals), + ['source for a.b.c\n']) + self.assertEqual(str(w.warning), + 'Module globals is missing a __spec__.loader') - for spec in (None, object(), ModuleSpec('', FakeLoader())): + for spec in None, object(): linecache.clearcache() module_globals = {'__name__': 'a.b.c', '__loader__': FakeLoader(), '__spec__': spec} + with self.assertWarns(DeprecationWarning) as w: + self.assertEqual(linecache.getlines(filename, module_globals), + ['source for a.b.c\n']) + self.assertEqual(str(w.warning), + 'Module globals is missing a __spec__.loader') + + linecache.clearcache() + module_globals = {'__name__': 'a.b.c', '__loader__': FakeLoader(), + '__spec__': ModuleSpec('', FakeLoader())} + with self.assertWarns(DeprecationWarning) as w: self.assertEqual(linecache.getlines(filename, module_globals), ['source for a.b.c\n']) + self.assertEqual(str(w.warning), + 'Module globals; __loader__ != __spec__.loader') linecache.clearcache() spec = ModuleSpec('x.y.z', FakeLoader()) diff --git a/Lib/test/test_list.py b/Lib/test/test_list.py index 223f34fb696a6e..642b54d34849da 100644 --- a/Lib/test/test_list.py +++ b/Lib/test/test_list.py @@ -349,10 +349,12 @@ def test_deopt_from_append_list(self): # gh-132011: it used to crash, because # of `CALL_LIST_APPEND` specialization failure. code = textwrap.dedent(""" + import _testinternalcapi + l = [] def lappend(l, x, y): l.append((x, y)) - for x in range(3): + for x in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): lappend(l, None, None) try: lappend(list, None, None) diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py index 70148dc30fc957..cf3796d9480801 100644 --- a/Lib/test/test_listcomps.py +++ b/Lib/test/test_listcomps.py @@ -171,6 +171,17 @@ def test_references___class__(self): """ self._check_in_scopes(code, raises=NameError) + def test_references___class___nested(self): + code = """ + res = [(lambda: __class__)() for _ in [1]] + """ + self._check_in_scopes(code, raises=NameError) + + def test_references___class___nested_used(self): + class _C: + res = [lambda: __class__ for _ in [1]] + self.assertIs(_C.res[0](), _C) + def test_references___class___defined(self): code = """ __class__ = 2 @@ -180,6 +191,38 @@ def test_references___class___defined(self): code, outputs={"res": [2]}, scopes=["module", "function"]) self._check_in_scopes(code, raises=NameError, scopes=["class"]) + def test_references___class___defined_nested(self): + code = """ + __class__ = 2 + res = [(lambda: __class__)() for x in [1]] + """ + self._check_in_scopes( + code, outputs={"res": [2]}, scopes=["module", "function"]) + self._check_in_scopes(code, raises=NameError, scopes=["class"]) + + def test_references___classdict__(self): + code = """ + class i: [__classdict__ for x in y] + """ + self._check_in_scopes(code, raises=NameError) + + def test_references___classdict___nested(self): + class _C: + res = [(lambda: __classdict__)() for _ in [1]] + self.assertIn("res", _C.res[0]) + + def test_references___conditional_annotations__(self): + code = """ + class i: [__conditional_annotations__ for x in y] + """ + self._check_in_scopes(code, raises=NameError) + + def test_references___conditional_annotations___nested(self): + code = """ + class i: [lambda: __conditional_annotations__ for x in y] + """ + self._check_in_scopes(code, raises=NameError) + def test_references___class___enclosing(self): code = """ __class__ = 2 diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index 55b502e52ca454..8057c35f540841 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -1,13 +1,23 @@ from decimal import Decimal +from test import support from test.support import cpython_only, verbose, is_android, linked_to_musl, os_helper -from test.support.warnings_helper import check_warnings from test.support.import_helper import ensure_lazy_imports, import_fresh_module from unittest import mock import unittest import locale +import os import sys import codecs + +class MiscTestCase(unittest.TestCase): + maxDiff = None + def test__all__(self): + extra = ["localeconv", "strcoll", "strxfrm", "getencoding", + "Error"] + not_exported = ["locale_encoding_alias", "locale_alias", "windows_locale"] + support.check__all__(self, locale, extra=extra, not_exported=not_exported) + class LazyImportTest(unittest.TestCase): @cpython_only def test_lazy_import(self): @@ -349,8 +359,7 @@ def setUp(self): enc = codecs.lookup(locale.getencoding() or 'ascii').name if enc not in ('utf-8', 'iso8859-1', 'cp1252'): raise unittest.SkipTest('encoding not suitable') - if enc != 'iso8859-1' and (sys.platform == 'darwin' or is_android or - sys.platform.startswith('freebsd')): + if enc != 'iso8859-1' and is_android: raise unittest.SkipTest('wcscoll/wcsxfrm have known bugs') BaseLocalizedTest.setUp(self) @@ -424,8 +433,8 @@ def test_hyphenated_encoding(self): self.check('cs_CZ.ISO8859-2', 'cs_CZ.ISO8859-2') def test_euro_modifier(self): - self.check('de_DE@euro', 'de_DE.ISO8859-15') - self.check('en_US.ISO8859-15@euro', 'en_US.ISO8859-15') + self.check('de_DE@euro', 'de_DE.ISO8859-15@euro') + self.check('en_US.ISO8859-15@euro', 'en_US.ISO8859-15@euro') self.check('de_DE.utf8@euro', 'de_DE.UTF-8') def test_latin_modifier(self): @@ -486,6 +495,153 @@ def test_japanese(self): self.check('jp_jp', 'ja_JP.eucJP') +class TestRealLocales(unittest.TestCase): + def setUp(self): + oldlocale = locale.setlocale(locale.LC_CTYPE) + self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale) + + def test_getsetlocale_issue1813(self): + # Issue #1813: setting and getting the locale under a Turkish locale + try: + locale.setlocale(locale.LC_CTYPE, 'tr_TR') + except locale.Error: + # Unsupported locale on this system + self.skipTest('test needs Turkish locale') + loc = locale.getlocale(locale.LC_CTYPE) + if verbose: + print('testing with %a' % (loc,), end=' ', flush=True) + try: + locale.setlocale(locale.LC_CTYPE, loc) + except locale.Error as exc: + # bpo-37945: setlocale(LC_CTYPE) fails with getlocale(LC_CTYPE) + # and the tr_TR locale on Windows. getlocale() builds a locale + # which is not recognize by setlocale(). + self.skipTest(f"setlocale(LC_CTYPE, {loc!r}) failed: {exc!r}") + self.assertEqual(loc, locale.getlocale(locale.LC_CTYPE)) + + @unittest.skipUnless(os.name == 'nt', 'requires Windows') + def test_setlocale_long_encoding(self): + with self.assertRaises(locale.Error): + locale.setlocale(locale.LC_CTYPE, 'English.%016d' % 1252) + locale.setlocale(locale.LC_CTYPE, 'English.%015d' % 1252) + loc = locale.setlocale(locale.LC_ALL) + self.assertIn('.1252', loc) + loc2 = loc.replace('.1252', '.%016d' % 1252, 1) + with self.assertRaises(locale.Error): + locale.setlocale(locale.LC_ALL, loc2) + loc2 = loc.replace('.1252', '.%015d' % 1252, 1) + locale.setlocale(locale.LC_ALL, loc2) + + # gh-137273: Debug assertion failure on Windows for long encoding. + with self.assertRaises(locale.Error): + locale.setlocale(locale.LC_CTYPE, 'en_US.' + 'x'*16) + locale.setlocale(locale.LC_CTYPE, 'en_US.UTF-8') + loc = locale.setlocale(locale.LC_ALL) + self.assertIn('.UTF-8', loc) + loc2 = loc.replace('.UTF-8', '.' + 'x'*16, 1) + with self.assertRaises(locale.Error): + locale.setlocale(locale.LC_ALL, loc2) + + @support.subTests('localename,localetuple', [ + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'iso885915')), + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'iso88591')), + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'ISO8859-15')), + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'ISO8859-1')), + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', None)), + ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'iso885915')), + ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'iso88591')), + ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'ISO8859-15')), + ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'ISO8859-1')), + ('de_DE.ISO8859-15@euro', ('de_DE@euro', None)), + ('el_GR.ISO8859-7@euro', ('el_GR@euro', 'iso88597')), + ('el_GR.ISO8859-7@euro', ('el_GR@euro', 'ISO8859-7')), + ('el_GR.ISO8859-7@euro', ('el_GR@euro', None)), + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'iso885915')), + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'iso88591')), + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'ISO8859-15')), + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'ISO8859-1')), + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', None)), + ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', 'utf8')), + ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', 'UTF-8')), + ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', None)), + ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', 'utf8')), + ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', 'UTF-8')), + ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', None)), + ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', 'utf8')), + ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', 'UTF-8')), + ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', None)), + ('be_BY.UTF-8@latin', ('be_BY@latin', 'utf8')), + ('be_BY.UTF-8@latin', ('be_BY@latin', 'UTF-8')), + ('be_BY.UTF-8@latin', ('be_BY@latin', None)), + ('sr_RS.UTF-8@latin', ('sr_RS@latin', 'utf8')), + ('sr_RS.UTF-8@latin', ('sr_RS@latin', 'UTF-8')), + ('sr_RS.UTF-8@latin', ('sr_RS@latin', None)), + ('ug_CN.UTF-8@latin', ('ug_CN@latin', 'utf8')), + ('ug_CN.UTF-8@latin', ('ug_CN@latin', 'UTF-8')), + ('ug_CN.UTF-8@latin', ('ug_CN@latin', None)), + ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', 'utf8')), + ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')), + ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', None)), + ]) + def test_setlocale_with_modifier(self, localename, localetuple): + try: + locale.setlocale(locale.LC_CTYPE, localename) + except locale.Error as exc: + self.skipTest(str(exc)) + loc = locale.setlocale(locale.LC_CTYPE, localetuple) + self.assertEqual(loc, localename) + + loctuple = locale.getlocale(locale.LC_CTYPE) + loc = locale.setlocale(locale.LC_CTYPE, loctuple) + self.assertEqual(loc, localename) + + @support.subTests('localename,localetuple', [ + ('fr_FR.iso885915@euro', ('fr_FR@euro', 'ISO8859-15')), + ('fr_FR.ISO8859-15@euro', ('fr_FR@euro', 'ISO8859-15')), + ('fr_FR@euro', ('fr_FR@euro', 'ISO8859-15')), + ('de_DE.iso885915@euro', ('de_DE@euro', 'ISO8859-15')), + ('de_DE.ISO8859-15@euro', ('de_DE@euro', 'ISO8859-15')), + ('de_DE@euro', ('de_DE@euro', 'ISO8859-15')), + ('el_GR.iso88597@euro', ('el_GR@euro', 'ISO8859-7')), + ('el_GR.ISO8859-7@euro', ('el_GR@euro', 'ISO8859-7')), + ('el_GR@euro', ('el_GR@euro', 'ISO8859-7')), + ('ca_ES.iso885915@euro', ('ca_ES@euro', 'ISO8859-15')), + ('ca_ES.ISO8859-15@euro', ('ca_ES@euro', 'ISO8859-15')), + ('ca_ES@euro', ('ca_ES@euro', 'ISO8859-15')), + ('ca_ES.utf8@valencia', ('ca_ES@valencia', 'UTF-8')), + ('ca_ES.UTF-8@valencia', ('ca_ES@valencia', 'UTF-8')), + ('ca_ES@valencia', ('ca_ES@valencia', 'UTF-8')), + ('ks_IN.utf8@devanagari', ('ks_IN@devanagari', 'UTF-8')), + ('ks_IN.UTF-8@devanagari', ('ks_IN@devanagari', 'UTF-8')), + ('ks_IN@devanagari', ('ks_IN@devanagari', 'UTF-8')), + ('sd_IN.utf8@devanagari', ('sd_IN@devanagari', 'UTF-8')), + ('sd_IN.UTF-8@devanagari', ('sd_IN@devanagari', 'UTF-8')), + ('sd_IN@devanagari', ('sd_IN@devanagari', 'UTF-8')), + ('be_BY.utf8@latin', ('be_BY@latin', 'UTF-8')), + ('be_BY.UTF-8@latin', ('be_BY@latin', 'UTF-8')), + ('be_BY@latin', ('be_BY@latin', 'UTF-8')), + ('sr_RS.utf8@latin', ('sr_RS@latin', 'UTF-8')), + ('sr_RS.UTF-8@latin', ('sr_RS@latin', 'UTF-8')), + ('sr_RS@latin', ('sr_RS@latin', 'UTF-8')), + ('ug_CN.utf8@latin', ('ug_CN@latin', 'UTF-8')), + ('ug_CN.UTF-8@latin', ('ug_CN@latin', 'UTF-8')), + ('ug_CN@latin', ('ug_CN@latin', 'UTF-8')), + ('uz_UZ.utf8@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')), + ('uz_UZ.UTF-8@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')), + ('uz_UZ@cyrillic', ('uz_UZ@cyrillic', 'UTF-8')), + ]) + def test_getlocale_with_modifier(self, localename, localetuple): + try: + locale.setlocale(locale.LC_CTYPE, localename) + except locale.Error as exc: + self.skipTest(str(exc)) + loctuple = locale.getlocale(locale.LC_CTYPE) + self.assertEqual(loctuple, localetuple) + + locale.setlocale(locale.LC_CTYPE, loctuple) + self.assertEqual(locale.getlocale(locale.LC_CTYPE), localetuple) + + class TestMiscellaneous(unittest.TestCase): def test_defaults_UTF8(self): # Issue #18378: on (at least) macOS setting LC_CTYPE to "UTF-8" is @@ -506,8 +662,7 @@ def test_defaults_UTF8(self): env.unset('LC_ALL', 'LC_CTYPE', 'LANG', 'LANGUAGE') env.set('LC_CTYPE', 'UTF-8') - with check_warnings(('', DeprecationWarning)): - self.assertEqual(locale.getdefaultlocale(), (None, 'UTF-8')) + self.assertEqual(locale.getdefaultlocale(), (None, 'UTF-8')) finally: if orig_getlocale is not None: _locale._getdefaultlocale = orig_getlocale @@ -552,27 +707,6 @@ def test_setlocale_category(self): # crasher from bug #7419 self.assertRaises(locale.Error, locale.setlocale, 12345) - def test_getsetlocale_issue1813(self): - # Issue #1813: setting and getting the locale under a Turkish locale - oldlocale = locale.setlocale(locale.LC_CTYPE) - self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale) - try: - locale.setlocale(locale.LC_CTYPE, 'tr_TR') - except locale.Error: - # Unsupported locale on this system - self.skipTest('test needs Turkish locale') - loc = locale.getlocale(locale.LC_CTYPE) - if verbose: - print('testing with %a' % (loc,), end=' ', flush=True) - try: - locale.setlocale(locale.LC_CTYPE, loc) - except locale.Error as exc: - # bpo-37945: setlocale(LC_CTYPE) fails with getlocale(LC_CTYPE) - # and the tr_TR locale on Windows. getlocale() builds a locale - # which is not recognize by setlocale(). - self.skipTest(f"setlocale(LC_CTYPE, {loc!r}) failed: {exc!r}") - self.assertEqual(loc, locale.getlocale(locale.LC_CTYPE)) - def test_invalid_locale_format_in_localetuple(self): with self.assertRaises(TypeError): locale.setlocale(locale.LC_ALL, b'fi_FI') diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 275f7ce47d09b5..cc2e9b782a3502 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -25,6 +25,7 @@ import codecs import configparser +import contextlib import copy import datetime import pathlib @@ -403,6 +404,20 @@ def test_empty_filter(self): r = logging.makeLogRecord({'name': 'spam.eggs'}) self.assertTrue(f.filter(r)) + def test_filter_repr(self): + f = logging.Filter('myapp') + self.assertEqual(repr(f), '<Filter (myapp)>') + + def test_filter_repr_empty(self): + f = logging.Filter() + self.assertEqual(repr(f), '<Filter ()>') + + def test_filter_repr_subclass(self): + class MyFilter(logging.Filter): + pass + f = MyFilter('myapp') + self.assertEqual(repr(f), '<MyFilter (myapp)>') + # # First, we define our levels. There can be as many as you want - the only # limitations are that they should be integers, the lowest should be > 0 and @@ -730,6 +745,7 @@ def remove_loop(fname, tries): # based on os.fork existing because that is what users and this test use. # This helps ensure that when fork exists (the important concept) that the # register_at_fork mechanism is also present and used. + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() @threading_helper.requires_working_threading() @skip_if_asan_fork @@ -3281,12 +3297,11 @@ def format(self, record): } } - # Remove when deprecation ends. - class DeprecatedStrmHandler(logging.StreamHandler): + class StrmHandler(logging.StreamHandler): def __init__(self, strm=None): super().__init__(stream=strm) - config_custom_handler_with_deprecated_strm_arg = { + config_custom_handler_with_removed_strm_arg = { "version": 1, "formatters": { "form1": { @@ -3295,7 +3310,7 @@ def __init__(self, strm=None): }, "handlers": { "hand1": { - "class": DeprecatedStrmHandler, + "class": StrmHandler, "formatter": "form1", "level": "NOTSET", "stream": "ext://sys.stdout", @@ -3401,14 +3416,9 @@ def test_config5_ok(self): self.test_config1_ok(config=self.config5) self.check_handler('hand1', CustomHandler) - def test_deprecation_warning_custom_handler_with_strm_arg(self): - msg = ( - "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." - ) - with self.assertWarnsRegex(DeprecationWarning, msg): - self.test_config1_ok(config=self.config_custom_handler_with_deprecated_strm_arg) + def test_removed_strm_arg(self): + with self.assertRaisesRegex(ValueError, 'hand1'): + self.apply_config(self.config_custom_handler_with_removed_strm_arg) def test_config6_failure(self): self.assertRaises(Exception, self.apply_config, self.config6) @@ -4045,6 +4055,7 @@ def test_config_queue_handler_invalid_config_does_not_create_multiprocessing_man self._apply_simple_queue_listener_configuration(qspec) manager.assert_not_called() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @skip_if_tsan_fork @support.requires_subprocess() @unittest.skipUnless(support.Py_DEBUG, "requires a debug build for testing" @@ -4054,11 +4065,7 @@ def test_config_reject_simple_queue_handler_multiprocessing_context(self): # and thus cannot be used as a queue-like object (gh-124653) import multiprocessing - - if support.MS_WINDOWS: - start_methods = ['spawn'] - else: - start_methods = ['spawn', 'fork', 'forkserver'] + start_methods = multiprocessing.get_all_start_methods() for start_method in start_methods: with self.subTest(start_method=start_method): @@ -4067,16 +4074,15 @@ def test_config_reject_simple_queue_handler_multiprocessing_context(self): with self.assertRaises(ValueError): self._apply_simple_queue_listener_configuration(qspec) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @skip_if_tsan_fork @support.requires_subprocess() @unittest.skipUnless(support.Py_DEBUG, "requires a debug build for testing" " assertions in multiprocessing") def test_config_queue_handler_multiprocessing_context(self): # regression test for gh-121723 - if support.MS_WINDOWS: - start_methods = ['spawn'] - else: - start_methods = ['spawn', 'fork', 'forkserver'] + import multiprocessing + start_methods = multiprocessing.get_all_start_methods() for start_method in start_methods: with self.subTest(start_method=start_method): ctx = multiprocessing.get_context(start_method) @@ -4107,6 +4113,7 @@ def _mpinit_issue121723(qspec, message_to_log): # log a message (this creates a record put in the queue) logging.getLogger().info(message_to_log) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @skip_if_tsan_fork @support.requires_subprocess() def test_multiprocessing_queues(self): @@ -4166,6 +4173,30 @@ def test_90195(self): # Logger should be enabled, since explicitly mentioned self.assertFalse(logger.disabled) + def test_disable_existing_loggers_preserves_children(self): + parent = logging.getLogger('many') + child = logging.getLogger('many.child') + child.setLevel(logging.CRITICAL) + self.assertFalse(child.isEnabledFor(logging.INFO)) + cousin = logging.getLogger('many-child') + for i in range(20): + logging.getLogger(f'many-sibling-{i}') + + self.apply_config({ + 'version': 1, + 'loggers': { + 'many': { + 'level': 'INFO', + }, + }, + }) + + self.assertFalse(parent.disabled) + self.assertFalse(child.disabled) + self.assertEqual(child.level, logging.NOTSET) + self.assertTrue(child.isEnabledFor(logging.INFO)) + self.assertTrue(cousin.disabled) + def test_111615(self): # See gh-111615 import_helper.import_module('_multiprocessing') # see gh-113692 @@ -4238,6 +4269,43 @@ def test_set_log_record_factory(self): man.setLogRecordFactory(expected) self.assertEqual(man.logRecordFactory, expected) + @threading_helper.requires_working_threading() + def test_getLogger_fast_path_never_returns_unwired_logger(self): + # getLogger()'s lock-free fast path returns a logger straight out of + # loggerDict, so a logger must be published there only after + # _fixupParents() has set its parent; otherwise a concurrent caller + # observes it detached from the hierarchy (gh-150818 follow-up). + manager = logging.Manager(logging.RootLogger(logging.WARNING)) + name = 'a.b.c' + + paused = threading.Event() + seen = [] + real_fixup = manager._fixupParents + + # Pause the creating thread between publishing rv and wiring its + # parent, then read loggerDict the way the fast path does and snapshot + # the parent at that instant (rv is wired in place soon after). + def fixup(alogger): + paused.set() + reader.join() + real_fixup(alogger) + + def read(): + paused.wait() + rv = manager.loggerDict.get(name) + if rv is not None and not isinstance(rv, logging.PlaceHolder): + seen.append(rv.parent) + + reader = threading.Thread(target=read) + manager._fixupParents = fixup + try: + reader.start() + manager.getLogger(name) + finally: + manager._fixupParents = real_fixup + + self.assertNotIn(None, seen) + class ChildLoggerTest(BaseTest): def test_child_loggers(self): r = logging.getLogger() @@ -4909,6 +4977,20 @@ def test_relativeCreated_has_higher_precision(self): # After PR gh-102412, precision (places) increases from 3 to 7 self.assertAlmostEqual(relativeCreated, offset_ns / 1e6, places=7) + def test_formatter_repr(self): + f = logging.Formatter('%(message)s') + self.assertEqual(repr(f), '<Formatter (%(message)s)>') + + def test_formatter_repr_default(self): + f = logging.Formatter() + self.assertEqual(repr(f), '<Formatter (%(message)s)>') + + def test_formatter_repr_subclass(self): + class MyFormatter(logging.Formatter): + pass + f = MyFormatter('%(message)s') + self.assertEqual(repr(f), '<MyFormatter (%(message)s)>') + class TestBufferingFormatter(logging.BufferingFormatter): def formatHeader(self, records): @@ -5337,6 +5419,7 @@ def _extract_logrecord_process_name(key, logMultiprocessing, conn=None): else: return results + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @skip_if_tsan_fork def test_multiprocessing(self): support.skip_if_broken_multiprocessing_synchronize() @@ -5821,7 +5904,7 @@ def cleanup(): self.addCleanup(cleanup) self.addCleanup(logging.shutdown) - self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None) + self.adapter = logging.LoggerAdapter(logger=self.logger) def test_exception(self): msg = 'testing exception: %r' @@ -5992,6 +6075,18 @@ def test_extra_merged(self): self.assertEqual(record.foo, '1') self.assertEqual(record.bar, '2') + self.adapter.critical('no extra') # should not fail + self.assertEqual(len(self.recording.records), 2) + record = self.recording.records[-1] + self.assertEqual(record.foo, '1') + self.assertNotHasAttr(record, 'bar') + + self.adapter.critical('none extra', extra=None) # should not fail + self.assertEqual(len(self.recording.records), 3) + record = self.recording.records[-1] + self.assertEqual(record.foo, '1') + self.assertNotHasAttr(record, 'bar') + def test_extra_merged_log_call_has_precedence(self): self.adapter = logging.LoggerAdapter(logger=self.logger, extra={'foo': '1'}, @@ -6003,6 +6098,25 @@ def test_extra_merged_log_call_has_precedence(self): self.assertHasAttr(record, 'foo') self.assertEqual(record.foo, '2') + def test_extra_merged_without_extra(self): + self.adapter = logging.LoggerAdapter(logger=self.logger, + merge_extra=True) + + self.adapter.critical('foo should be here', extra={'foo': '1'}) + self.assertEqual(len(self.recording.records), 1) + record = self.recording.records[-1] + self.assertEqual(record.foo, '1') + + self.adapter.critical('no extra') # should not fail + self.assertEqual(len(self.recording.records), 2) + record = self.recording.records[-1] + self.assertNotHasAttr(record, 'foo') + + self.adapter.critical('none extra', extra=None) # should not fail + self.assertEqual(len(self.recording.records), 3) + record = self.recording.records[-1] + self.assertNotHasAttr(record, 'foo') + class PrefixAdapter(logging.LoggerAdapter): prefix = 'Adapter' @@ -6333,6 +6447,32 @@ def test_should_not_rollover_non_file(self): self.assertFalse(rh.shouldRollover(self.next_rec())) rh.close() + @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()') + def test_should_not_rollover_named_pipe(self): + # gh-143237 - test with non-seekable special file (named pipe) + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) + try: + os.mkfifo(filename) + except PermissionError as e: + self.skipTest('os.mkfifo(): %s' % e) + + data = 'not read' + def other_side(): + nonlocal data + with open(filename, 'rb') as f: + data = f.read() + + thread = threading.Thread(target=other_side) + with threading_helper.start_threads([thread]): + rh = logging.handlers.RotatingFileHandler( + filename, encoding="utf-8", maxBytes=1) + with contextlib.closing(rh): + m = self.next_rec() + self.assertFalse(rh.shouldRollover(m)) + rh.emit(m) + self.assertEqual(data.decode(), m.msg + os.linesep) + def test_should_rollover(self): with open(self.fn, 'wb') as f: f.write(b'\n') @@ -6512,6 +6652,57 @@ def test_rollover(self): print(tf.read()) self.assertTrue(found, msg=msg) + @unittest.skipUnless(hasattr(os.stat_result, 'st_birthtime') or hasattr(os, 'statx'), + "st_birthtime and statx() not available or supported by Python on this OS") + @support.requires_resource('walltime') + def test_rollover_based_on_st_birthtime_only(self): + def add_record(message: str) -> None: + fh = logging.handlers.TimedRotatingFileHandler( + self.fn, when='S', interval=4, encoding="utf-8", backupCount=1) + fmt = logging.Formatter('%(asctime)s %(message)s') + fh.setFormatter(fmt) + record = logging.makeLogRecord({'msg': message}) + fh.emit(record) + fh.close() + + add_record('testing - initial') + self.assertLogFile(self.fn) + # Sleep a little over the half of rollover time - and this value + # must be over 2 seconds, since this is the mtime resolution on + # FAT32 filesystems. + time.sleep(2.1) + add_record('testing - update before rollover to renew the st_mtime') + time.sleep(2.1) # a little over the half of rollover time + add_record('testing - new record supposedly in the new file after rollover') + + # At this point, the log file should be rotated if the rotation + # is based on creation time but should be not if it's based on + # modification time. + found = False + now = datetime.datetime.now() + GO_BACK = 5 # seconds + for secs in range(GO_BACK + 1): + prev = now - datetime.timedelta(seconds=secs) + fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S") + found = os.path.exists(fn) + if found: + self.rmfiles.append(fn) + break + msg = 'No rotated files found, went back %d seconds' % GO_BACK + if not found: + # print additional diagnostics + dn, fn = os.path.split(self.fn) + files = [f for f in os.listdir(dn) if f.startswith(fn)] + print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr) + print('The only matching files are: %s' % files, file=sys.stderr) + for f in files: + print('Contents of %s:' % f) + path = os.path.join(dn, f) + print(os.stat(path)) + with open(path, 'r') as tf: + print(tf.read()) + self.assertTrue(found, msg=msg) + def test_rollover_at_midnight(self, weekly=False): os_helper.unlink(self.fn) now = datetime.datetime.now() @@ -7185,8 +7376,8 @@ def test_compute_rollover(self, when=when, interval=interval, exp=exp): setattr(TimedRotatingFileHandlerTest, name, test_compute_rollover) -@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.') class NTEventLogHandlerTest(BaseTest): + @unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.') def test_basic(self): logtype = 'Application' elh = win32evtlog.OpenEventLog(None, logtype) @@ -7220,6 +7411,17 @@ def test_basic(self): msg = 'Record not found in event log, went back %d records' % GO_BACK self.assertTrue(found, msg=msg) + @unittest.skipUnless(sys.platform == "win32", "Windows required for this test") + def test_without_pywin32(self): + h = logging.handlers.NTEventLogHandler('python_test') + self.addCleanup(h.close) + + # Verify that the handler uses _winapi module + self.assertIsNotNone(h._winapi, "_winapi module should be available") + + r = logging.makeLogRecord({'msg': 'Hello!'}) + h.emit(r) + class MiscTestCase(unittest.TestCase): def test__all__(self): @@ -7231,6 +7433,16 @@ def test__all__(self): support.check__all__(self, logging, not_exported=not_exported) +class TestModule(unittest.TestCase): + def test_deprecated__version__and__date__(self): + msg = "is deprecated and slated for removal in Python 3.20" + for attr in ("__version__", "__date__"): + with self.subTest(attr=attr): + with self.assertWarnsRegex(DeprecationWarning, msg) as cm: + getattr(logging, attr) + self.assertEqual(cm.filename, __file__) + + # Set the locale to the platform-dependent default. I have no idea # why the test does this, but in any case we save the current locale # first and restore it at the end. diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index f336d49fa4f008..b48a8812a1a2d1 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1374,17 +1374,22 @@ def equivalent_python(n, length, byteorder, signed=False): check(tests4, 'little', signed=False) self.assertRaises(OverflowError, (256).to_bytes, 1, 'big', signed=False) - self.assertRaises(OverflowError, (256).to_bytes, 1, 'big', signed=True) self.assertRaises(OverflowError, (256).to_bytes, 1, 'little', signed=False) - self.assertRaises(OverflowError, (256).to_bytes, 1, 'little', signed=True) + self.assertRaises(OverflowError, (128).to_bytes, 1, 'big', signed=True) + self.assertRaises(OverflowError, (128).to_bytes, 1, 'little', signed=True) + self.assertRaises(OverflowError, (-129).to_bytes, 1, 'big', signed=True) + self.assertRaises(OverflowError, (-129).to_bytes, 1, 'little', signed=True) self.assertRaises(OverflowError, (-1).to_bytes, 2, 'big', signed=False) self.assertRaises(OverflowError, (-1).to_bytes, 2, 'little', signed=False) self.assertEqual((0).to_bytes(0, 'big'), b'') + self.assertEqual((0).to_bytes(0, 'big', signed=True), b'') self.assertEqual((1).to_bytes(5, 'big'), b'\x00\x00\x00\x00\x01') self.assertEqual((0).to_bytes(5, 'big'), b'\x00\x00\x00\x00\x00') self.assertEqual((-1).to_bytes(5, 'big', signed=True), b'\xff\xff\xff\xff\xff') self.assertRaises(OverflowError, (1).to_bytes, 0, 'big') + self.assertRaises(OverflowError, (-1).to_bytes, 0, 'big', signed=True) + self.assertRaises(OverflowError, (-1).to_bytes, 0, 'little', signed=True) # gh-98783 class SubStr(str): @@ -1693,5 +1698,21 @@ class MyInt(int): # GH-117195 -- This shouldn't crash object.__sizeof__(1) + def test_hash(self): + # gh-136599 + self.assertEqual(hash(-1), -2) + self.assertEqual(hash(0), 0) + self.assertEqual(hash(10), 10) + + self.assertEqual(hash(sys.hash_info.modulus - 2), sys.hash_info.modulus - 2) + self.assertEqual(hash(sys.hash_info.modulus - 1), sys.hash_info.modulus - 1) + self.assertEqual(hash(sys.hash_info.modulus), 0) + self.assertEqual(hash(sys.hash_info.modulus + 1), 1) + + self.assertEqual(hash(-sys.hash_info.modulus - 2), -2) + self.assertEqual(hash(-sys.hash_info.modulus - 1), -2) + self.assertEqual(hash(-sys.hash_info.modulus), 0) + self.assertEqual(hash(-sys.hash_info.modulus + 1), -sys.hash_info.modulus + 1) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index e93c3c37354e27..d4f954b34c1252 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -383,6 +383,12 @@ def test_uninitialized_LZMADecompressor_crash(self): self.assertEqual(LZMADecompressor.__new__(LZMADecompressor). decompress(bytes()), b'') + def test_riscv_filter_constant_exists(self): + self.assertTrue(lzma.FILTER_RISCV) + + def test_arm64_filter_constant_exists(self): + self.assertTrue(lzma.FILTER_ARM64) + class CompressDecompressFunctionTestCase(unittest.TestCase): diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 0169948e453438..019c699bff55c4 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -8,9 +8,10 @@ import io import tempfile from test import support -from test.support import import_helper +from test.support import import_helper, warnings_helper from test.support import os_helper from test.support import refleak_helper +from test.support import requires_root_user from test.support import socket_helper import unittest import textwrap @@ -542,6 +543,11 @@ def _test_flush_or_close(self, method, should_call_close): self.assertIn(self._box.get_string(key), contents) oldbox.close() + def test_use_context_manager(self): + # Mailboxes are usable as a context manager + with self._box as box: + self.assertIs(self._box, box) + def test_dump_message(self): # Write message representations to disk for input in (email.message_from_string(_sample_message), @@ -1081,6 +1087,7 @@ def test_permissions_after_flush(self): self.assertEqual(os.stat(self._path).st_mode, mode) + @requires_root_user @unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown') def test_ownership_after_flush(self): # See issue gh-117467 @@ -1103,10 +1110,7 @@ def test_ownership_after_flush(self): else: self.skipTest("test needs more than one group") - try: - os.chown(self._path, other_uid, other_gid) - except OSError: - self.skipTest('test needs root privilege') + os.chown(self._path, other_uid, other_gid) # Change permissions as in test_permissions_after_flush. mode = st.st_mode | 0o666 os.chmod(self._path, mode) @@ -1122,6 +1126,16 @@ def test_ownership_after_flush(self): self.assertEqual(st.st_gid, other_gid) self.assertEqual(st.st_mode, mode) + def test_context_manager_locks_and_closes(self): + # Context manager locks/unlocks and closes. + # (This test uses an implementation detail to get the state.) + self.assertFalse(self._box._locked) + with self._box as context_object: + self.assertIs(self._box, context_object) + self.assertTrue(self._box._locked) + self.assertFalse(self._box._file.closed) + self.assertFalse(self._box._locked) + self.assertTrue(self._box._file.closed) class _TestMboxMMDF(_TestSingleFile): @@ -1212,6 +1226,7 @@ def test_add_and_close(self): self.assertEqual(contents, f.read()) self._box = self._factory(self._path) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() @unittest.skipUnless(hasattr(socket, 'socketpair'), "Test needs socketpair().") def test_lock_conflict(self): diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 8b1fb0eba1f8b6..9c4d91c456dc5d 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -43,6 +43,11 @@ def test_ints(self): for expected in (-n, n): self.helper(expected) n = n >> 1 + n = 1 << 100 + while n: + for expected in (-n, -n+1, n-1, n): + self.helper(expected) + n = n >> 1 def test_int64(self): # Simulate int marshaling with TYPE_INT64. @@ -312,6 +317,138 @@ def test_recursion_limit(self): last.append([0]) self.assertRaises(ValueError, marshal.dumps, head) + def test_reference_loop_list(self): + a = [] + a.append(a) + for v in range(3): + self.assertRaises(ValueError, marshal.dumps, a, v) + for v in range(3, marshal.version + 1): + d = marshal.dumps(a, v) + b = marshal.loads(d) + self.assertIsInstance(b, list) + self.assertIs(b[0], b) + + def test_reference_loop_dict(self): + a = {} + a[None] = a + for v in range(3): + self.assertRaises(ValueError, marshal.dumps, a, v) + for v in range(3, marshal.version + 1): + d = marshal.dumps(a, v) + b = marshal.loads(d) + self.assertIsInstance(b, dict) + self.assertIs(b[None], b) + + def test_reference_loop_tuple(self): + a = ([],) + a[0].append(a) + for v in range(3): + self.assertRaises(ValueError, marshal.dumps, a, v) + for v in range(3, marshal.version + 1): + d = marshal.dumps(a, v) + b = marshal.loads(d) + self.assertIsInstance(b, tuple) + self.assertIsInstance(b[0], list) + self.assertIs(b[0][0], b) + + def test_reference_loop_code(self): + def f(): + return 1234.5 + code = f.__code__ + a = [] + code = code.replace(co_consts=code.co_consts + (a,)) + # This test creates a reference loop which leads to reference leaks, + # so we need to break the loop manually. See gh-148722. + self.addCleanup(a.clear) + a.append(code) + for v in range(marshal.version + 1): + self.assertRaises(ValueError, marshal.dumps, code, v) + + def test_reference_loop_slice(self): + a = slice([], None) + a.start.append(a) + for v in range(marshal.version + 1): + self.assertRaises(ValueError, marshal.dumps, a, v) + + a = slice(None, []) + a.stop.append(a) + for v in range(marshal.version + 1): + self.assertRaises(ValueError, marshal.dumps, a, v) + + a = slice(None, None, []) + a.step.append(a) + for v in range(marshal.version + 1): + self.assertRaises(ValueError, marshal.dumps, a, v) + + def test_reference_loop_frozendict(self): + a = frozendict({None: []}) + a[None].append(a) + for v in range(marshal.version + 1): + self.assertRaises(ValueError, marshal.dumps, a, v) + + def test_loads_reference_loop_list(self): + data = b'\xdb\x01\x00\x00\x00r\x00\x00\x00\x00' # [<R>] + a = marshal.loads(data) + self.assertIsInstance(a, list) + self.assertIs(a[0], a) + + def test_loads_reference_loop_dict(self): + data = b'\xfbNr\x00\x00\x00\x000' # {None: <R>} + a = marshal.loads(data) + self.assertIsInstance(a, dict) + self.assertIs(a[None], a) + + def test_loads_abnormal_reference_loops(self): + # Indirect self-references of tuples. + data = b'\xa8\x01\x00\x00\x00[\x01\x00\x00\x00r\x00\x00\x00\x00' # ([<R>],) + a = marshal.loads(data) + self.assertIsInstance(a, tuple) + self.assertIsInstance(a[0], list) + self.assertIs(a[0][0], a) + + data = b'\xa8\x01\x00\x00\x00{Nr\x00\x00\x00\x000' # ({None: <R>},) + a = marshal.loads(data) + self.assertIsInstance(a, tuple) + self.assertIsInstance(a[0], dict) + self.assertIs(a[0][None], a) + + # Direct self-reference which cannot be created in Python. + # This creates a reference loop which cannot be collected. + if False: + data = b'\xa8\x01\x00\x00\x00r\x00\x00\x00\x00' # (<R>,) + a = marshal.loads(data) + self.assertIsInstance(a, tuple) + self.assertIs(a[0], a) + + # Direct self-references which cannot be created in Python + # because of unhashability. + data = b'\xfbr\x00\x00\x00\x00N0' # {<R>: None} + self.assertRaises(TypeError, marshal.loads, data) + data = b'\xbc\x01\x00\x00\x00r\x00\x00\x00\x00' # {<R>} + self.assertRaises(TypeError, marshal.loads, data) + + for data in [ + # Indirect self-references of immutable objects. + b'\xba[\x01\x00\x00\x00r\x00\x00\x00\x00NN', # slice([<R>], None) + b'\xbaN[\x01\x00\x00\x00r\x00\x00\x00\x00N', # slice(None, [<R>]) + b'\xbaNN[\x01\x00\x00\x00r\x00\x00\x00\x00', # slice(None, None, [<R>]) + b'\xba{Nr\x00\x00\x00\x000NN', # slice({None: <R>}, None) + b'\xbaN{Nr\x00\x00\x00\x000N', # slice(None, {None: <R>}) + b'\xbaNN{Nr\x00\x00\x00\x000', # slice(None, None, {None: <R>}) + b'\xfdN[\x01\x00\x00\x00r\x00\x00\x00\x000', # frozendict({None: [<R>]}) + b'\xfdN{Nr\x00\x00\x00\x0000', # frozendict({None: {None: <R>}) + + # Direct self-references which cannot be created in Python. + b'\xbe\x01\x00\x00\x00r\x00\x00\x00\x00', # frozenset({<R>}) + b'\xfdNr\x00\x00\x00\x000', # frozendict({None: <R>}) + b'\xfdr\x00\x00\x00\x00N0', # frozendict({<R>: None}) + b'\xbar\x00\x00\x00\x00NN', # slice(<R>, None) + b'\xbaNr\x00\x00\x00\x00N', # slice(None, <R>) + b'\xbaNNr\x00\x00\x00\x00', # slice(None, None, <R>) + ]: + with self.subTest(data=data): + self.assertRaises(ValueError, marshal.loads, data) + def test_exact_type_match(self): # Former bug: # >>> class Int(int): pass @@ -408,6 +545,26 @@ def test_deterministic_sets(self): _, dump_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1") self.assertEqual(dump_0, dump_1) + def test_unmarshallable(self): + # Check no crash after encountering unmarshallable objects. + # See https://github.com/python/cpython/issues/106287. + fset = frozenset([int]) + code = compile("a = 1", "<string>", "exec") + code = code.replace(co_consts=(1, fset, None)) + cases = (('tuple', (fset,)), + ('list', [fset]), + ('set', fset), + ('dict key', {fset: 'x'}), + ('dict value', {'x': fset}), + ('dict key & value', {fset: fset}), + ('slice', slice(fset, fset)), + ('code', code)) + for name, arg in cases: + with self.subTest(name, arg=arg): + with self.assertRaisesRegex(ValueError, "unmarshallable object"): + marshal.dumps((arg, memoryview(b''))) + + LARGE_SIZE = 2**31 pointer_size = 8 if sys.maxsize > 0xFFFFFFFF else 4 @@ -545,6 +702,15 @@ def testDict(self): self.helper(dictobj) self.helper3(dictobj) + def testFrozenDict(self): + for obj in self.keys: + dictobj = frozendict({"hello": obj, "goodbye": obj, obj: "hello"}) + self.helper(dictobj) + + for version in range(6): + with self.assertRaises(ValueError): + marshal.dumps(dictobj, version) + def testModule(self): with open(__file__, "rb") as f: code = f.read() @@ -610,7 +776,7 @@ def test_slice(self): with self.subTest(obj=str(obj)): self.helper(obj) - for version in range(4): + for version in range(5): with self.assertRaises(ValueError): marshal.dumps(obj, version) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index e3b0d4fa9eeeb3..cc37697189af3e 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1,7 +1,8 @@ # Python test set -- math module # XXXX Should not do tests around zero only -from test.support import verbose, requires_IEEE_754 +from test.support import (verbose, requires_IEEE_754, + skip_if_double_rounding) from test import support import unittest import fractions @@ -23,11 +24,6 @@ FLOAT_MAX = sys.float_info.max FLOAT_MIN = sys.float_info.min -# detect evidence of double-rounding: fsum is not always correctly -# rounded on machines that suffer from double rounding. -x, y = 1e16, 2.9999 # use temporary values to defeat peephole optimizer -HAVE_DOUBLE_ROUNDING = (x + y == 1e16 + 4) - # locate file with test values if __name__ == '__main__': file = sys.argv[0] @@ -55,56 +51,6 @@ def to_ulps(x): return n -# Here's a pure Python version of the math.factorial algorithm, for -# documentation and comparison purposes. -# -# Formula: -# -# factorial(n) = factorial_odd_part(n) << (n - count_set_bits(n)) -# -# where -# -# factorial_odd_part(n) = product_{i >= 0} product_{0 < j <= n >> i; j odd} j -# -# The outer product above is an infinite product, but once i >= n.bit_length, -# (n >> i) < 1 and the corresponding term of the product is empty. So only the -# finitely many terms for 0 <= i < n.bit_length() contribute anything. -# -# We iterate downwards from i == n.bit_length() - 1 to i == 0. The inner -# product in the formula above starts at 1 for i == n.bit_length(); for each i -# < n.bit_length() we get the inner product for i from that for i + 1 by -# multiplying by all j in {n >> i+1 < j <= n >> i; j odd}. In Python terms, -# this set is range((n >> i+1) + 1 | 1, (n >> i) + 1 | 1, 2). - -def count_set_bits(n): - """Number of '1' bits in binary expansion of a nonnnegative integer.""" - return 1 + count_set_bits(n & n - 1) if n else 0 - -def partial_product(start, stop): - """Product of integers in range(start, stop, 2), computed recursively. - start and stop should both be odd, with start <= stop. - - """ - numfactors = (stop - start) >> 1 - if not numfactors: - return 1 - elif numfactors == 1: - return start - else: - mid = (start + numfactors) | 1 - return partial_product(start, mid) * partial_product(mid, stop) - -def py_factorial(n): - """Factorial of nonnegative integer n, via "Binary Split Factorial Formula" - described at http://www.luschny.de/math/factorial/binarysplitfact.html - - """ - inner = outer = 1 - for i in reversed(range(n.bit_length())): - inner *= partial_product((n >> i + 1) + 1 | 1, (n >> i) + 1 | 1) - outer *= inner - return outer << (n - count_set_bits(n)) - def ulp_abs_check(expected, got, ulp_tol, abs_tol): """Given finite floats `expected` and `got`, check that they're approximately equal to within the given number of ulps or the @@ -129,7 +75,7 @@ def parse_mtestfile(fname): -- starts a comment blank lines, or lines containing only a comment, are ignored other lines are expected to have the form - id fn arg -> expected [flag]* + id fn arg... -> expected [flag]* """ with open(fname, encoding="utf-8") as fp: @@ -141,12 +87,12 @@ def parse_mtestfile(fname): continue lhs, rhs = line.split('->') - id, fn, arg = lhs.split() + id, fn, *args = lhs.split() rhs_pieces = rhs.split() exp = rhs_pieces[0] flags = rhs_pieces[1:] - yield (id, fn, float(arg), float(exp), flags) + yield (id, fn, [float(arg) for arg in args], float(exp), flags) def parse_testfile(fname): @@ -239,6 +185,22 @@ def __init__(self, value): def __index__(self): return self.value +class IndexableFloatLike: + def __init__(self, float_value, index_value): + self.float_value = float_value + self.index_value = index_value + + def __float__(self): + if isinstance(self.float_value, BaseException): + raise self.float_value + return self.float_value + + def __index__(self): + if isinstance(self.index_value, BaseException): + raise self.index_value + return self.index_value + + class BadDescr: def __get__(self, obj, objtype=None): raise ValueError @@ -325,6 +287,8 @@ def testAtanh(self): self.assertRaises(ValueError, math.atanh, NINF) self.assertTrue(math.isnan(math.atanh(NAN))) + @unittest.skipIf(sys.platform.startswith("sunos"), + "skipping, see gh-138573") def testAtan2(self): self.assertRaises(TypeError, math.atan2) self.ftest('atan2(-1, 0)', math.atan2(-1, 0), -math.pi/2) @@ -547,33 +511,6 @@ def testFabs(self): self.ftest('fabs(0)', math.fabs(0), 0) self.ftest('fabs(1)', math.fabs(1), 1) - def testFactorial(self): - self.assertEqual(math.factorial(0), 1) - total = 1 - for i in range(1, 1000): - total *= i - self.assertEqual(math.factorial(i), total) - self.assertEqual(math.factorial(i), py_factorial(i)) - self.assertRaises(ValueError, math.factorial, -1) - self.assertRaises(ValueError, math.factorial, -10**100) - - def testFactorialNonIntegers(self): - self.assertRaises(TypeError, math.factorial, 5.0) - self.assertRaises(TypeError, math.factorial, 5.2) - self.assertRaises(TypeError, math.factorial, -1.0) - self.assertRaises(TypeError, math.factorial, -1e100) - self.assertRaises(TypeError, math.factorial, decimal.Decimal('5')) - self.assertRaises(TypeError, math.factorial, decimal.Decimal('5.2')) - self.assertRaises(TypeError, math.factorial, "5") - - # Other implementations may place different upper bounds. - @support.cpython_only - def testFactorialHugeInputs(self): - # Currently raises OverflowError for inputs that are too large - # to fit into a C long. - self.assertRaises(OverflowError, math.factorial, 10**100) - self.assertRaises(TypeError, math.factorial, 1e100) - def testFloor(self): self.assertRaises(TypeError, math.floor) self.assertEqual(int, type(math.floor(0.5))) @@ -742,8 +679,7 @@ def testfrexp(name, result, expected): self.assertTrue(math.isnan(math.frexp(NAN)[0])) @requires_IEEE_754 - @unittest.skipIf(HAVE_DOUBLE_ROUNDING, - "fsum is not exact on machines with double rounding") + @skip_if_double_rounding def testFsum(self): # math.fsum relies on exact rounding for correct operation. # There's a known problem with IA32 floating-point that causes @@ -979,8 +915,8 @@ def testHypot(self): self.assertRaises(TypeError, math.hypot, *([1.0]*18), 'spam') @requires_IEEE_754 - @unittest.skipIf(HAVE_DOUBLE_ROUNDING, - "hypot() loses accuracy on machines with double rounding") + @skip_if_double_rounding + @support.skip_on_newlib def testHypotAccuracy(self): # Verify improved accuracy in cases that were known to be inaccurate. # @@ -1175,68 +1111,6 @@ def test_math_dist_leak(self): with self.assertRaises(ValueError): math.dist([1, 2], [3, 4, 5]) - def testIsqrt(self): - # Test a variety of inputs, large and small. - test_values = ( - list(range(1000)) - + list(range(10**6 - 1000, 10**6 + 1000)) - + [2**e + i for e in range(60, 200) for i in range(-40, 40)] - + [3**9999, 10**5001] - ) - - for value in test_values: - with self.subTest(value=value): - s = math.isqrt(value) - self.assertIs(type(s), int) - self.assertLessEqual(s*s, value) - self.assertLess(value, (s+1)*(s+1)) - - # Negative values - with self.assertRaises(ValueError): - math.isqrt(-1) - - # Integer-like things - s = math.isqrt(True) - self.assertIs(type(s), int) - self.assertEqual(s, 1) - - s = math.isqrt(False) - self.assertIs(type(s), int) - self.assertEqual(s, 0) - - class IntegerLike(object): - def __init__(self, value): - self.value = value - - def __index__(self): - return self.value - - s = math.isqrt(IntegerLike(1729)) - self.assertIs(type(s), int) - self.assertEqual(s, 41) - - with self.assertRaises(ValueError): - math.isqrt(IntegerLike(-3)) - - # Non-integer-like things - bad_values = [ - 3.5, "a string", decimal.Decimal("3.5"), 3.5j, - 100.0, -4.0, - ] - for value in bad_values: - with self.subTest(value=value): - with self.assertRaises(TypeError): - math.isqrt(value) - - @support.bigmemtest(2**32, memuse=0.85) - def test_isqrt_huge(self, size): - if size & 1: - size += 1 - v = 1 << size - w = math.isqrt(v) - self.assertEqual(w.bit_length(), size // 2 + 1) - self.assertEqual(w.bit_count(), 1) - def test_lcm(self): lcm = math.lcm self.assertEqual(lcm(0, 0), 0) @@ -1331,13 +1205,32 @@ def testLog(self): self.ftest('log(10**40, 10**20)', math.log(10**40, 10**20), 2) self.ftest('log(10**1000)', math.log(10**1000), 2302.5850929940457) + self.ftest('log(10**2000, 10**1000)', math.log(10**2000, 10**1000), 2) + self.ftest('log(MyIndexable(32), MyIndexable(2))', + math.log(MyIndexable(32), MyIndexable(2)), 5) + self.ftest('log(MyIndexable(10**1000))', + math.log(MyIndexable(10**1000)), + 2302.5850929940457) + self.ftest('log(MyIndexable(10**2000), MyIndexable(10**1000))', + math.log(MyIndexable(10**2000), MyIndexable(10**1000)), + 2) + self.assertRaises(ValueError, math.log, 0.0) + self.assertRaises(ValueError, math.log, 0) + self.assertRaises(ValueError, math.log, MyIndexable(0)) self.assertRaises(ValueError, math.log, -1.5) + self.assertRaises(ValueError, math.log, -1) + self.assertRaises(ValueError, math.log, MyIndexable(-1)) self.assertRaises(ValueError, math.log, -10**1000) + self.assertRaises(ValueError, math.log, MyIndexable(-10**1000)) self.assertRaises(ValueError, math.log, 10, -10) self.assertRaises(ValueError, math.log, NINF) self.assertEqual(math.log(INF), INF) self.assertTrue(math.isnan(math.log(NAN))) + self.assertEqual(math.log(IndexableFloatLike(math.e, 10**1000)), 1.0) + self.assertAlmostEqual(math.log(IndexableFloatLike(OverflowError(), 10**1000)), + 2302.5850929940457) + def testLog1p(self): self.assertRaises(TypeError, math.log1p) for n in [2, 2**90, 2**300]: @@ -1353,37 +1246,62 @@ def testLog2(self): self.assertEqual(math.log2(1), 0.0) self.assertEqual(math.log2(2), 1.0) self.assertEqual(math.log2(4), 2.0) + self.assertEqual(math.log2(MyIndexable(4)), 2.0) - # Large integer values - self.assertEqual(math.log2(2**1023), 1023.0) - self.assertEqual(math.log2(2**1024), 1024.0) - self.assertEqual(math.log2(2**2000), 2000.0) - + self.assertRaises(ValueError, math.log2, 0.0) + self.assertRaises(ValueError, math.log2, 0) + self.assertRaises(ValueError, math.log2, MyIndexable(0)) self.assertRaises(ValueError, math.log2, -1.5) + self.assertRaises(ValueError, math.log2, -1) + self.assertRaises(ValueError, math.log2, MyIndexable(-1)) + self.assertRaises(ValueError, math.log2, -2**2000) + self.assertRaises(ValueError, math.log2, MyIndexable(-2**2000)) self.assertRaises(ValueError, math.log2, NINF) self.assertTrue(math.isnan(math.log2(NAN))) + self.assertEqual(math.log2(IndexableFloatLike(8.0, 2**2000)), 3.0) + self.assertEqual(math.log2(IndexableFloatLike(OverflowError(), 2**2000)), 2000.0) + @requires_IEEE_754 # log2() is not accurate enough on Mac OS X Tiger (10.4) @support.requires_mac_ver(10, 5) + @support.skip_on_newlib def testLog2Exact(self): # Check that we get exact equality for log2 of powers of 2. actual = [math.log2(math.ldexp(1.0, n)) for n in range(-1074, 1024)] expected = [float(n) for n in range(-1074, 1024)] self.assertEqual(actual, expected) + # Large integer values + self.assertEqual(math.log2(2**1023), 1023.0) + self.assertEqual(math.log2(2**1024), 1024.0) + self.assertEqual(math.log2(2**2000), 2000.0) + self.assertEqual(math.log2(MyIndexable(2**2000)), 2000.0) + def testLog10(self): self.assertRaises(TypeError, math.log10) self.ftest('log10(0.1)', math.log10(0.1), -1) self.ftest('log10(1)', math.log10(1), 0) self.ftest('log10(10)', math.log10(10), 1) self.ftest('log10(10**1000)', math.log10(10**1000), 1000.0) + self.ftest('log10(MyIndexable(10))', math.log10(MyIndexable(10)), 1) + self.ftest('log10(MyIndexable(10**1000))', + math.log10(MyIndexable(10**1000)), 1000.0) + self.assertRaises(ValueError, math.log10, 0.0) + self.assertRaises(ValueError, math.log10, 0) + self.assertRaises(ValueError, math.log10, MyIndexable(0)) self.assertRaises(ValueError, math.log10, -1.5) + self.assertRaises(ValueError, math.log10, -1) + self.assertRaises(ValueError, math.log10, MyIndexable(-1)) self.assertRaises(ValueError, math.log10, -10**1000) + self.assertRaises(ValueError, math.log10, MyIndexable(-10**1000)) self.assertRaises(ValueError, math.log10, NINF) self.assertEqual(math.log(INF), INF) self.assertTrue(math.isnan(math.log10(NAN))) + self.assertEqual(math.log10(IndexableFloatLike(100.0, 10**1000)), 2.0) + self.assertEqual(math.log10(IndexableFloatLike(OverflowError(), 10**1000)), 1000.0) + @support.bigmemtest(2**32, memuse=0.2) def test_log_huge_integer(self, size): v = 1 << size @@ -1488,8 +1406,7 @@ def __rmul__(self, other): self.assertEqual(sumprod(*args), 0.0) @requires_IEEE_754 - @unittest.skipIf(HAVE_DOUBLE_ROUNDING, - "sumprod() accuracy not guaranteed on machines with double rounding") + @skip_if_double_rounding @support.cpython_only # Other implementations may choose a different algorithm def test_sumprod_accuracy(self): sumprod = math.sumprod @@ -1574,8 +1491,7 @@ def run(func, *args): ) @requires_IEEE_754 - @unittest.skipIf(HAVE_DOUBLE_ROUNDING, - "sumprod() accuracy not guaranteed on machines with double rounding") + @skip_if_double_rounding @support.cpython_only # Other implementations may choose a different algorithm @support.requires_resource('cpu') def test_sumprod_extended_precision_accuracy(self): @@ -2225,10 +2141,10 @@ def test_testfile(self): @requires_IEEE_754 def test_mtestfile(self): - fail_fmt = "{}: {}({!r}): {}" + fail_fmt = "{}: {}{!r}: {}" failures = [] - for id, fn, arg, expected, flags in parse_mtestfile(math_testcases): + for id, fn, args, expected, flags in parse_mtestfile(math_testcases): func = getattr(math, fn) if 'invalid' in flags or 'divide-by-zero' in flags: @@ -2237,7 +2153,7 @@ def test_mtestfile(self): expected = 'OverflowError' try: - got = func(arg) + got = func(*args) except ValueError: got = 'ValueError' except OverflowError: @@ -2260,7 +2176,7 @@ def test_mtestfile(self): # general. abs_tol = 1e-15 - elif fn == 'erfc' and arg >= 0.0: + elif fn == 'erfc' and (arg := args[0]) >= 0.0: # erfc has less-than-ideal accuracy for large # arguments (x ~ 25 or so), mainly due to the # error involved in computing exp(-x*x). @@ -2283,7 +2199,7 @@ def test_mtestfile(self): if failure is None: continue - msg = fail_fmt.format(id, fn, arg, failure) + msg = fail_fmt.format(id, fn, args, failure) failures.append(msg) if failures: @@ -2392,140 +2308,6 @@ def _naive_prod(iterable, start=1): self.assertEqual(type(prod([1, decimal.Decimal(2.0), 3, 4, 5, 6])), decimal.Decimal) - def testPerm(self): - perm = math.perm - factorial = math.factorial - # Test if factorial definition is satisfied - for n in range(500): - for k in (range(n + 1) if n < 100 else range(30) if n < 200 else range(10)): - self.assertEqual(perm(n, k), - factorial(n) // factorial(n - k)) - - # Test for Pascal's identity - for n in range(1, 100): - for k in range(1, n): - self.assertEqual(perm(n, k), perm(n - 1, k - 1) * k + perm(n - 1, k)) - - # Test corner cases - for n in range(1, 100): - self.assertEqual(perm(n, 0), 1) - self.assertEqual(perm(n, 1), n) - self.assertEqual(perm(n, n), factorial(n)) - - # Test one argument form - for n in range(20): - self.assertEqual(perm(n), factorial(n)) - self.assertEqual(perm(n, None), factorial(n)) - - # Raises TypeError if any argument is non-integer or argument count is - # not 1 or 2 - self.assertRaises(TypeError, perm, 10, 1.0) - self.assertRaises(TypeError, perm, 10, decimal.Decimal(1.0)) - self.assertRaises(TypeError, perm, 10, "1") - self.assertRaises(TypeError, perm, 10.0, 1) - self.assertRaises(TypeError, perm, decimal.Decimal(10.0), 1) - self.assertRaises(TypeError, perm, "10", 1) - - self.assertRaises(TypeError, perm) - self.assertRaises(TypeError, perm, 10, 1, 3) - self.assertRaises(TypeError, perm) - - # Raises Value error if not k or n are negative numbers - self.assertRaises(ValueError, perm, -1, 1) - self.assertRaises(ValueError, perm, -2**1000, 1) - self.assertRaises(ValueError, perm, 1, -1) - self.assertRaises(ValueError, perm, 1, -2**1000) - - # Returns zero if k is greater than n - self.assertEqual(perm(1, 2), 0) - self.assertEqual(perm(1, 2**1000), 0) - - n = 2**1000 - self.assertEqual(perm(n, 0), 1) - self.assertEqual(perm(n, 1), n) - self.assertEqual(perm(n, 2), n * (n-1)) - if support.check_impl_detail(cpython=True): - self.assertRaises(OverflowError, perm, n, n) - - for n, k in (True, True), (True, False), (False, False): - self.assertEqual(perm(n, k), 1) - self.assertIs(type(perm(n, k)), int) - self.assertEqual(perm(IntSubclass(5), IntSubclass(2)), 20) - self.assertEqual(perm(MyIndexable(5), MyIndexable(2)), 20) - for k in range(3): - self.assertIs(type(perm(IntSubclass(5), IntSubclass(k))), int) - self.assertIs(type(perm(MyIndexable(5), MyIndexable(k))), int) - - def testComb(self): - comb = math.comb - factorial = math.factorial - # Test if factorial definition is satisfied - for n in range(500): - for k in (range(n + 1) if n < 100 else range(30) if n < 200 else range(10)): - self.assertEqual(comb(n, k), factorial(n) - // (factorial(k) * factorial(n - k))) - - # Test for Pascal's identity - for n in range(1, 100): - for k in range(1, n): - self.assertEqual(comb(n, k), comb(n - 1, k - 1) + comb(n - 1, k)) - - # Test corner cases - for n in range(100): - self.assertEqual(comb(n, 0), 1) - self.assertEqual(comb(n, n), 1) - - for n in range(1, 100): - self.assertEqual(comb(n, 1), n) - self.assertEqual(comb(n, n - 1), n) - - # Test Symmetry - for n in range(100): - for k in range(n // 2): - self.assertEqual(comb(n, k), comb(n, n - k)) - - # Raises TypeError if any argument is non-integer or argument count is - # not 2 - self.assertRaises(TypeError, comb, 10, 1.0) - self.assertRaises(TypeError, comb, 10, decimal.Decimal(1.0)) - self.assertRaises(TypeError, comb, 10, "1") - self.assertRaises(TypeError, comb, 10.0, 1) - self.assertRaises(TypeError, comb, decimal.Decimal(10.0), 1) - self.assertRaises(TypeError, comb, "10", 1) - - self.assertRaises(TypeError, comb, 10) - self.assertRaises(TypeError, comb, 10, 1, 3) - self.assertRaises(TypeError, comb) - - # Raises Value error if not k or n are negative numbers - self.assertRaises(ValueError, comb, -1, 1) - self.assertRaises(ValueError, comb, -2**1000, 1) - self.assertRaises(ValueError, comb, 1, -1) - self.assertRaises(ValueError, comb, 1, -2**1000) - - # Returns zero if k is greater than n - self.assertEqual(comb(1, 2), 0) - self.assertEqual(comb(1, 2**1000), 0) - - n = 2**1000 - self.assertEqual(comb(n, 0), 1) - self.assertEqual(comb(n, 1), n) - self.assertEqual(comb(n, 2), n * (n-1) // 2) - self.assertEqual(comb(n, n), 1) - self.assertEqual(comb(n, n-1), n) - self.assertEqual(comb(n, n-2), n * (n-1) // 2) - if support.check_impl_detail(cpython=True): - self.assertRaises(OverflowError, comb, n, n//2) - - for n, k in (True, True), (True, False), (False, False): - self.assertEqual(comb(n, k), 1) - self.assertIs(type(comb(n, k)), int) - self.assertEqual(comb(IntSubclass(5), IntSubclass(2)), 10) - self.assertEqual(comb(MyIndexable(5), MyIndexable(2)), 10) - for k in range(3): - self.assertIs(type(comb(IntSubclass(5), IntSubclass(k))), int) - self.assertIs(type(comb(MyIndexable(5), MyIndexable(k))), int) - @requires_IEEE_754 def test_nextafter(self): # around 2^52 and 2^63 @@ -2827,6 +2609,7 @@ def test_fma_nan_results(self): self.assertIsNaN(math.fma(a, math.nan, b)) self.assertIsNaN(math.fma(a, b, math.nan)) + @support.skip_on_newlib def test_fma_infinities(self): # Cases involving infinite inputs or results. positives = [1e-300, 2.3, 1e300, math.inf] @@ -2897,7 +2680,7 @@ def test_fma_infinities(self): # gh-73468: On some platforms, libc fma() doesn't implement IEE 754-2008 # properly: it doesn't use the right sign when the result is zero. @unittest.skipIf( - sys.platform.startswith(("freebsd", "wasi", "netbsd", "emscripten")) + sys.platform.startswith(("freebsd", "wasi", "netbsd", "emscripten", "cygwin")) or (sys.platform == "android" and platform.machine() == "x86_64") or support.linked_to_musl(), # gh-131032 f"this platform doesn't implement IEE 754-2008 properly") @@ -2955,6 +2738,7 @@ def test_fma_zero_result(self): self.assertIsNegativeZero(math.fma(y-x, -(x+y), -z)) self.assertIsPositiveZero(math.fma(x-y, -(x+y), z)) + @support.skip_on_newlib def test_fma_overflow(self): a = b = float.fromhex('0x1p512') c = float.fromhex('0x1p1023') @@ -2988,11 +2772,13 @@ def test_fma_overflow(self): c = float.fromhex('0x1.fffffffffffffp+1023') self.assertEqual(math.fma(a, b, -c), c) + @support.skip_on_newlib def test_fma_single_round(self): a = float.fromhex('0x1p-50') self.assertEqual(math.fma(a - 1.0, a + 1.0, 1.0), a*a) - def test_random(self): + @support.skip_on_newlib + def test_fma_random(self): # A collection of randomly generated inputs for which the naive FMA # (with two rounds) gives a different result from a singly-rounded FMA. diff --git a/Lib/test/test_math_integer.py b/Lib/test/test_math_integer.py new file mode 100644 index 00000000000000..09a98d93bd636c --- /dev/null +++ b/Lib/test/test_math_integer.py @@ -0,0 +1,403 @@ +from decimal import Decimal +from fractions import Fraction +import unittest +from test import support + + +class IntSubclass(int): + pass + +# Class providing an __index__ method. +class MyIndexable(object): + def __init__(self, value): + self.value = value + + def __index__(self): + return self.value + +# Here's a pure Python version of the math.integer.factorial algorithm, for +# documentation and comparison purposes. +# +# Formula: +# +# factorial(n) = factorial_odd_part(n) << (n - count_set_bits(n)) +# +# where +# +# factorial_odd_part(n) = product_{i >= 0} product_{0 < j <= n >> i; j odd} j +# +# The outer product above is an infinite product, but once i >= n.bit_length, +# (n >> i) < 1 and the corresponding term of the product is empty. So only the +# finitely many terms for 0 <= i < n.bit_length() contribute anything. +# +# We iterate downwards from i == n.bit_length() - 1 to i == 0. The inner +# product in the formula above starts at 1 for i == n.bit_length(); for each i +# < n.bit_length() we get the inner product for i from that for i + 1 by +# multiplying by all j in {n >> i+1 < j <= n >> i; j odd}. In Python terms, +# this set is range((n >> i+1) + 1 | 1, (n >> i) + 1 | 1, 2). + +def count_set_bits(n): + """Number of '1' bits in binary expansion of a nonnnegative integer.""" + return 1 + count_set_bits(n & n - 1) if n else 0 + +def partial_product(start, stop): + """Product of integers in range(start, stop, 2), computed recursively. + start and stop should both be odd, with start <= stop. + + """ + numfactors = (stop - start) >> 1 + if not numfactors: + return 1 + elif numfactors == 1: + return start + else: + mid = (start + numfactors) | 1 + return partial_product(start, mid) * partial_product(mid, stop) + +def py_factorial(n): + """Factorial of nonnegative integer n, via "Binary Split Factorial Formula" + described at http://www.luschny.de/math/factorial/binarysplitfact.html + + """ + inner = outer = 1 + for i in reversed(range(n.bit_length())): + inner *= partial_product((n >> i + 1) + 1 | 1, (n >> i) + 1 | 1) + outer *= inner + return outer << (n - count_set_bits(n)) + + +class IntMathTests(unittest.TestCase): + import math.integer as module + + def assertIntEqual(self, actual, expected): + self.assertEqual(actual, expected) + self.assertIs(type(actual), int) + + def test_factorial(self): + factorial = self.module.factorial + self.assertEqual(factorial(0), 1) + total = 1 + for i in range(1, 1000): + total *= i + self.assertEqual(factorial(i), total) + self.assertEqual(factorial(i), py_factorial(i)) + + self.assertIntEqual(factorial(False), 1) + self.assertIntEqual(factorial(True), 1) + for i in range(3): + expected = factorial(i) + self.assertIntEqual(factorial(IntSubclass(i)), expected) + self.assertIntEqual(factorial(MyIndexable(i)), expected) + + self.assertRaises(ValueError, factorial, -1) + self.assertRaises(ValueError, factorial, -10**1000) + + def test_factorial_non_integers(self): + factorial = self.module.factorial + self.assertRaises(TypeError, factorial, 5.0) + self.assertRaises(TypeError, factorial, 5.2) + self.assertRaises(TypeError, factorial, -1.0) + self.assertRaises(TypeError, factorial, -1e100) + self.assertRaises(TypeError, factorial, Decimal('5')) + self.assertRaises(TypeError, factorial, Decimal('5.2')) + self.assertRaises(TypeError, factorial, Fraction(5, 1)) + self.assertRaises(TypeError, factorial, "5") + + # Other implementations may place different upper bounds. + @support.cpython_only + def test_factorial_huge_inputs(self): + factorial = self.module.factorial + # Currently raises OverflowError for inputs that are too large + # to fit into a C long. + self.assertRaises(OverflowError, factorial, 10**100) + self.assertRaises(TypeError, factorial, 1e100) + + def test_gcd(self): + gcd = self.module.gcd + self.assertEqual(gcd(0, 0), 0) + self.assertEqual(gcd(1, 0), 1) + self.assertEqual(gcd(-1, 0), 1) + self.assertEqual(gcd(0, 1), 1) + self.assertEqual(gcd(0, -1), 1) + self.assertEqual(gcd(7, 1), 1) + self.assertEqual(gcd(7, -1), 1) + self.assertEqual(gcd(-23, 15), 1) + self.assertEqual(gcd(120, 84), 12) + self.assertEqual(gcd(84, -120), 12) + self.assertEqual(gcd(1216342683557601535506311712, + 436522681849110124616458784), 32) + c = 652560 + x = 434610456570399902378880679233098819019853229470286994367836600566 + y = 1064502245825115327754847244914921553977 + a = x * c + b = y * c + self.assertEqual(gcd(a, b), c) + self.assertEqual(gcd(b, a), c) + self.assertEqual(gcd(-a, b), c) + self.assertEqual(gcd(b, -a), c) + self.assertEqual(gcd(a, -b), c) + self.assertEqual(gcd(-b, a), c) + self.assertEqual(gcd(-a, -b), c) + self.assertEqual(gcd(-b, -a), c) + c = 576559230871654959816130551884856912003141446781646602790216406874 + a = x * c + b = y * c + self.assertEqual(gcd(a, b), c) + self.assertEqual(gcd(b, a), c) + self.assertEqual(gcd(-a, b), c) + self.assertEqual(gcd(b, -a), c) + self.assertEqual(gcd(a, -b), c) + self.assertEqual(gcd(-b, a), c) + self.assertEqual(gcd(-a, -b), c) + self.assertEqual(gcd(-b, -a), c) + + self.assertRaises(TypeError, gcd, 120.0, 84) + self.assertRaises(TypeError, gcd, 120, 84.0) + self.assertIntEqual(gcd(IntSubclass(120), IntSubclass(84)), 12) + self.assertIntEqual(gcd(MyIndexable(120), MyIndexable(84)), 12) + + def test_lcm(self): + lcm = self.module.lcm + self.assertEqual(lcm(0, 0), 0) + self.assertEqual(lcm(1, 0), 0) + self.assertEqual(lcm(-1, 0), 0) + self.assertEqual(lcm(0, 1), 0) + self.assertEqual(lcm(0, -1), 0) + self.assertEqual(lcm(7, 1), 7) + self.assertEqual(lcm(7, -1), 7) + self.assertEqual(lcm(-23, 15), 345) + self.assertEqual(lcm(120, 84), 840) + self.assertEqual(lcm(84, -120), 840) + self.assertEqual(lcm(1216342683557601535506311712, + 436522681849110124616458784), + 16592536571065866494401400422922201534178938447014944) + + x = 43461045657039990237 + y = 10645022458251153277 + for c in (652560, + 57655923087165495981): + a = x * c + b = y * c + d = x * y * c + self.assertEqual(lcm(a, b), d) + self.assertEqual(lcm(b, a), d) + self.assertEqual(lcm(-a, b), d) + self.assertEqual(lcm(b, -a), d) + self.assertEqual(lcm(a, -b), d) + self.assertEqual(lcm(-b, a), d) + self.assertEqual(lcm(-a, -b), d) + self.assertEqual(lcm(-b, -a), d) + + self.assertEqual(lcm(), 1) + self.assertEqual(lcm(120), 120) + self.assertEqual(lcm(-120), 120) + self.assertEqual(lcm(120, 84, 102), 14280) + self.assertEqual(lcm(120, 0, 84), 0) + + self.assertRaises(TypeError, lcm, 120.0) + self.assertRaises(TypeError, lcm, 120.0, 84) + self.assertRaises(TypeError, lcm, 120, 84.0) + self.assertRaises(TypeError, lcm, 120, 0, 84.0) + self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840) + + def test_isqrt(self): + isqrt = self.module.isqrt + # Test a variety of inputs, large and small. + test_values = ( + list(range(1000)) + + list(range(10**6 - 1000, 10**6 + 1000)) + + [2**e + i for e in range(60, 200) for i in range(-40, 40)] + + [3**9999, 10**5001] + ) + + for value in test_values: + with self.subTest(value=value): + s = isqrt(value) + self.assertIs(type(s), int) + self.assertLessEqual(s*s, value) + self.assertLess(value, (s+1)*(s+1)) + + # Negative values + with self.assertRaises(ValueError): + isqrt(-1) + + # Integer-like things + self.assertIntEqual(isqrt(True), 1) + self.assertIntEqual(isqrt(False), 0) + self.assertIntEqual(isqrt(MyIndexable(1729)), 41) + + with self.assertRaises(ValueError): + isqrt(MyIndexable(-3)) + + # Non-integer-like things + bad_values = [ + 3.5, "a string", Decimal("3.5"), 3.5j, + 100.0, -4.0, + ] + for value in bad_values: + with self.subTest(value=value): + with self.assertRaises(TypeError): + isqrt(value) + + @support.bigmemtest(2**32, memuse=0.85) + def test_isqrt_huge(self, size): + isqrt = self.module.isqrt + if size & 1: + size += 1 + v = 1 << size + w = isqrt(v) + self.assertEqual(w.bit_length(), size // 2 + 1) + self.assertEqual(w.bit_count(), 1) + + def test_perm(self): + perm = self.module.perm + factorial = self.module.factorial + # Test if factorial definition is satisfied + for n in range(500): + for k in (range(n + 1) if n < 100 else range(30) if n < 200 else range(10)): + self.assertEqual(perm(n, k), + factorial(n) // factorial(n - k)) + + # Test for Pascal's identity + for n in range(1, 100): + for k in range(1, n): + self.assertEqual(perm(n, k), perm(n - 1, k - 1) * k + perm(n - 1, k)) + + # Test corner cases + for n in range(1, 100): + self.assertEqual(perm(n, 0), 1) + self.assertEqual(perm(n, 1), n) + self.assertEqual(perm(n, n), factorial(n)) + + # Test one argument form + for n in range(20): + self.assertEqual(perm(n), factorial(n)) + self.assertEqual(perm(n, None), factorial(n)) + + # Raises TypeError if any argument is non-integer or argument count is + # not 1 or 2 + self.assertRaises(TypeError, perm, 10, 1.0) + self.assertRaises(TypeError, perm, 10, Decimal(1.0)) + self.assertRaises(TypeError, perm, 10, Fraction(1, 1)) + self.assertRaises(TypeError, perm, 10, "1") + self.assertRaises(TypeError, perm, 10.0, 1) + self.assertRaises(TypeError, perm, Decimal(10.0), 1) + self.assertRaises(TypeError, perm, Fraction(10, 1), 1) + self.assertRaises(TypeError, perm, "10", 1) + + self.assertRaises(TypeError, perm) + self.assertRaises(TypeError, perm, 10, 1, 3) + self.assertRaises(TypeError, perm) + + # Raises Value error if not k or n are negative numbers + self.assertRaises(ValueError, perm, -1, 1) + self.assertRaises(ValueError, perm, -2**1000, 1) + self.assertRaises(ValueError, perm, 1, -1) + self.assertRaises(ValueError, perm, 1, -2**1000) + + # Returns zero if k is greater than n + self.assertEqual(perm(1, 2), 0) + self.assertEqual(perm(1, 2**1000), 0) + + n = 2**1000 + self.assertEqual(perm(n, 0), 1) + self.assertEqual(perm(n, 1), n) + self.assertEqual(perm(n, 2), n * (n-1)) + if support.check_impl_detail(cpython=True): + self.assertRaises(OverflowError, perm, n, n) + + for n, k in (True, True), (True, False), (False, False): + self.assertIntEqual(perm(n, k), 1) + self.assertEqual(perm(IntSubclass(5), IntSubclass(2)), 20) + self.assertEqual(perm(MyIndexable(5), MyIndexable(2)), 20) + for k in range(3): + self.assertIs(type(perm(IntSubclass(5), IntSubclass(k))), int) + self.assertIs(type(perm(MyIndexable(5), MyIndexable(k))), int) + + def test_comb(self): + comb = self.module.comb + factorial = self.module.factorial + # Test if factorial definition is satisfied + for n in range(500): + for k in (range(n + 1) if n < 100 else range(30) if n < 200 else range(10)): + self.assertEqual(comb(n, k), factorial(n) + // (factorial(k) * factorial(n - k))) + + # Test for Pascal's identity + for n in range(1, 100): + for k in range(1, n): + self.assertEqual(comb(n, k), comb(n - 1, k - 1) + comb(n - 1, k)) + + # Test corner cases + for n in range(100): + self.assertEqual(comb(n, 0), 1) + self.assertEqual(comb(n, n), 1) + + for n in range(1, 100): + self.assertEqual(comb(n, 1), n) + self.assertEqual(comb(n, n - 1), n) + + # Test Symmetry + for n in range(100): + for k in range(n // 2): + self.assertEqual(comb(n, k), comb(n, n - k)) + + # Raises TypeError if any argument is non-integer or argument count is + # not 2 + self.assertRaises(TypeError, comb, 10, 1.0) + self.assertRaises(TypeError, comb, 10, Decimal(1.0)) + self.assertRaises(TypeError, comb, 10, "1") + self.assertRaises(TypeError, comb, 10.0, 1) + self.assertRaises(TypeError, comb, Decimal(10.0), 1) + self.assertRaises(TypeError, comb, "10", 1) + + self.assertRaises(TypeError, comb, 10) + self.assertRaises(TypeError, comb, 10, 1, 3) + self.assertRaises(TypeError, comb) + + # Raises Value error if not k or n are negative numbers + self.assertRaises(ValueError, comb, -1, 1) + self.assertRaises(ValueError, comb, -2**1000, 1) + self.assertRaises(ValueError, comb, 1, -1) + self.assertRaises(ValueError, comb, 1, -2**1000) + + # Returns zero if k is greater than n + self.assertEqual(comb(1, 2), 0) + self.assertEqual(comb(1, 2**1000), 0) + + n = 2**1000 + self.assertEqual(comb(n, 0), 1) + self.assertEqual(comb(n, 1), n) + self.assertEqual(comb(n, 2), n * (n-1) // 2) + self.assertEqual(comb(n, n), 1) + self.assertEqual(comb(n, n-1), n) + self.assertEqual(comb(n, n-2), n * (n-1) // 2) + if support.check_impl_detail(cpython=True): + self.assertRaises(OverflowError, comb, n, n//2) + + for n, k in (True, True), (True, False), (False, False): + self.assertIntEqual(comb(n, k), 1) + self.assertEqual(comb(IntSubclass(5), IntSubclass(2)), 10) + self.assertEqual(comb(MyIndexable(5), MyIndexable(2)), 10) + for k in range(3): + self.assertIs(type(comb(IntSubclass(5), IntSubclass(k))), int) + self.assertIs(type(comb(MyIndexable(5), MyIndexable(k))), int) + + +class MathTests(IntMathTests): + import math as module + + +class MiscTests(unittest.TestCase): + + def test_module_name(self): + import math.integer + self.assertEqual(math.integer.__name__, 'math.integer') + for name in dir(math.integer): + if not name.startswith('_'): + obj = getattr(math.integer, name) + self.assertEqual(obj.__module__, 'math.integer') + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 64f440f180bbf0..f71b6f53486509 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -387,6 +387,20 @@ def test_hash_writable(self): m = self._view(b) self.assertRaises(ValueError, hash, m) + def test_hash_use_after_free(self): + # Prevent crash in memoryview(v).__hash__ with re-entrant v.__hash__. + # Regression test for https://github.com/python/cpython/issues/142664. + class E(array.array): + def __hash__(self): + mv.release() + self.clear() + return 123 + + v = E('B', b'A' * 4096) + mv = memoryview(v).toreadonly() # must be read-only for hash() + self.assertRaises(BufferError, hash, mv) + self.assertRaises(BufferError, mv.__hash__) + def test_weakref(self): # Check memoryviews are weakrefable for tp in self._types: @@ -442,6 +456,20 @@ def test_issue22668(self): self.assertEqual(c.format, "H") self.assertEqual(d.format, "H") + def test_hex_use_after_free(self): + # Prevent UAF in memoryview.hex(sep) with re-entrant sep.__len__. + # Regression test for https://github.com/python/cpython/issues/143195. + ba = bytearray(b'A' * 1024) + mv = memoryview(ba) + + class S(bytes): + def __len__(self): + mv.release() + ba.clear() + return 1 + + self.assertRaises(BufferError, mv.hex, S(b':')) + # Variations on source objects for the buffer: bytes-like objects, then arrays # with itemsize > 1. @@ -547,6 +575,101 @@ def test_array_assign(self): m[:] = new_a self.assertEqual(a, new_a) + def test_compare_equal(self): + # A memoryview is equal to itself: there is no need to compare + # individual values. This is not true for float values since they can + # be NaN, and NaN is not equal to itself. + + def check_equal(view, is_equal): + self.assertEqual(view == view, is_equal) + self.assertEqual(view != view, not is_equal) + + # Comparison with a different memoryview doesn't use + # the optimization and should give the same result. + view2 = memoryview(view) + self.assertEqual(view2 == view, is_equal) + self.assertEqual(view2 != view2, not is_equal) + + # Test integer formats + for int_format in 'bBhHiIlLqQ': + with self.subTest(format=int_format): + a = array.array(int_format, [1, 2, 3]) + m = memoryview(a) + check_equal(m, True) + + if int_format in 'bB': + m2 = m.cast('@' + m.format) + check_equal(m2, True) + + # Test 'c' format + a = array.array('B', [1, 2, 3]) + m = memoryview(a.tobytes()).cast('c') + check_equal(m, True) + + # Test 'n' and 'N' formats + if struct.calcsize('L') == struct.calcsize('N'): + int_format = 'L' + elif struct.calcsize('Q') == struct.calcsize('N'): + int_format = 'Q' + else: + int_format = None + if int_format: + a = array.array(int_format, [1, 2, 3]) + m = memoryview(a.tobytes()).cast('N') + check_equal(m, True) + m = memoryview(a.tobytes()).cast('n') + check_equal(m, True) + + # Test '?' format + m = memoryview(b'\0\1\2').cast('?') + check_equal(m, True) + + # Test float formats + for float_format in 'fd': + with self.subTest(format=float_format): + a = array.array(float_format, [1.0, 2.0, float('nan')]) + m = memoryview(a) + # nan is not equal to nan + check_equal(m, False) + + a = array.array(float_format, [1.0, 2.0, 3.0]) + m = memoryview(a) + check_equal(m, True) + + # Test complex formats + for complex_format in ('Zf', 'Zd'): + with self.subTest(format=complex_format): + data = struct.pack(complex_format * 3, 1.0, 2.0, float('nan')) + m = memoryview(data).cast(complex_format) + # nan is not equal to nan + check_equal(m, False) + + data = struct.pack(complex_format * 3, 1.0, 2.0, 3.0) + m = memoryview(data).cast(complex_format) + check_equal(m, True) + + def test_boolean_format(self): + # Test '?' format (keep all the checks below for UBSan) + # See github.com/python/cpython/issues/148390. + + # m1a and m1b are equivalent to [False, True, False] + m1a = memoryview(b'\0\2\0').cast('?') + self.assertEqual(m1a.tolist(), [False, True, False]) + m1b = memoryview(b'\0\4\0').cast('?') + self.assertEqual(m1b.tolist(), [False, True, False]) + self.assertEqual(m1a, m1b) + + # m2a and m2b are equivalent to [True, True, True] + m2a = memoryview(b'\1\3\5').cast('?') + self.assertEqual(m2a.tolist(), [True, True, True]) + m2b = memoryview(b'\2\4\6').cast('?') + self.assertEqual(m2b.tolist(), [True, True, True]) + self.assertEqual(m2a, m2b) + + allbytes = bytes(range(256)) + allbytes = memoryview(allbytes).cast('?') + self.assertEqual(allbytes.tolist(), [False] + [True] * 255) + class BytesMemorySliceTest(unittest.TestCase, BaseMemorySliceTests, BaseBytesMemoryTests): @@ -593,6 +716,14 @@ def test_half_float(self): self.assertEqual(half_view.nbytes * 2, float_view.nbytes) self.assertListEqual(half_view.tolist(), float_view.tolist()) + def test_complex_types(self): + float_complex_data = struct.pack('FFF', 0.0, -1.5j, 1+2j) + double_complex_data = struct.pack('DDD', 0.0, -1.5j, 1+2j) + float_complex_view = memoryview(float_complex_data).cast('Zf') + double_complex_view = memoryview(double_complex_data).cast('Zd') + self.assertEqual(float_complex_view.nbytes * 2, double_complex_view.nbytes) + self.assertListEqual(float_complex_view.tolist(), double_complex_view.tolist()) + def test_memoryview_hex(self): # Issue #9951: memoryview.hex() segfaults with non-contiguous buffers. x = b'0' * 200000 @@ -600,6 +731,25 @@ def test_memoryview_hex(self): m2 = m1[::-1] self.assertEqual(m2.hex(), '30' * 200000) + def test_memoryview_hex_separator(self): + x = bytes(range(97, 102)) + m1 = memoryview(x) + m2 = m1[::-1] + self.assertEqual(m2.hex(':'), '65:64:63:62:61') + self.assertEqual(m2.hex(':', 2), '65:6463:6261') + self.assertEqual(m2.hex(':', -2), '6564:6362:61') + self.assertEqual(m2.hex(sep=':', bytes_per_sep=2), '65:6463:6261') + self.assertEqual(m2.hex(sep=':', bytes_per_sep=-2), '6564:6362:61') + for bytes_per_sep in 5, -5, sys.maxsize, -sys.maxsize: + with self.subTest(bytes_per_sep=bytes_per_sep): + self.assertEqual(m2.hex(':', bytes_per_sep), '6564636261') + for bytes_per_sep in sys.maxsize+1, -sys.maxsize-1, 2**1000, -2**1000: + with self.subTest(bytes_per_sep=bytes_per_sep): + try: + self.assertEqual(m2.hex(':', bytes_per_sep), '6564636261') + except OverflowError: + pass + def test_copy(self): m = memoryview(b'abc') with self.assertRaises(TypeError): diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index fb57d5e5544c12..1a3b49b87b121f 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -6,8 +6,9 @@ import unittest.mock from platform import win32_edition from test import support -from test.support import cpython_only, force_not_colorized, os_helper +from test.support import cpython_only, force_not_colorized, os_helper, requires_subprocess from test.support.import_helper import ensure_lazy_imports +from test.support.script_helper import assert_python_ok, assert_python_failure try: import _winapi @@ -30,50 +31,11 @@ def tearDownModule(): mimetypes.knownfiles = knownfiles -class MimeTypesTestCase(unittest.TestCase): +class MimeTypesModuleTestCase(unittest.TestCase): def setUp(self): - self.db = mimetypes.MimeTypes() - - def test_case_sensitivity(self): - eq = self.assertEqual - eq(self.db.guess_file_type("foobar.html"), ("text/html", None)) - eq(self.db.guess_type("scheme:foobar.html"), ("text/html", None)) - eq(self.db.guess_file_type("foobar.HTML"), ("text/html", None)) - eq(self.db.guess_type("scheme:foobar.HTML"), ("text/html", None)) - eq(self.db.guess_file_type("foobar.tgz"), ("application/x-tar", "gzip")) - eq(self.db.guess_type("scheme:foobar.tgz"), ("application/x-tar", "gzip")) - eq(self.db.guess_file_type("foobar.TGZ"), ("application/x-tar", "gzip")) - eq(self.db.guess_type("scheme:foobar.TGZ"), ("application/x-tar", "gzip")) - eq(self.db.guess_file_type("foobar.tar.Z"), ("application/x-tar", "compress")) - eq(self.db.guess_type("scheme:foobar.tar.Z"), ("application/x-tar", "compress")) - eq(self.db.guess_file_type("foobar.tar.z"), (None, None)) - eq(self.db.guess_type("scheme:foobar.tar.z"), (None, None)) - - def test_default_data(self): - eq = self.assertEqual - eq(self.db.guess_file_type("foo.html"), ("text/html", None)) - eq(self.db.guess_file_type("foo.HTML"), ("text/html", None)) - eq(self.db.guess_file_type("foo.tgz"), ("application/x-tar", "gzip")) - eq(self.db.guess_file_type("foo.tar.gz"), ("application/x-tar", "gzip")) - eq(self.db.guess_file_type("foo.tar.Z"), ("application/x-tar", "compress")) - eq(self.db.guess_file_type("foo.tar.bz2"), ("application/x-tar", "bzip2")) - eq(self.db.guess_file_type("foo.tar.xz"), ("application/x-tar", "xz")) - - def test_data_urls(self): - eq = self.assertEqual - guess_type = self.db.guess_type - eq(guess_type("data:invalidDataWithoutComma"), (None, None)) - eq(guess_type("data:,thisIsTextPlain"), ("text/plain", None)) - eq(guess_type("data:;base64,thisIsTextPlain"), ("text/plain", None)) - eq(guess_type("data:text/x-foo,thisIsTextXFoo"), ("text/x-foo", None)) - - def test_file_parsing(self): - eq = self.assertEqual - sio = io.StringIO("x-application/x-unittest pyunit\n") - self.db.readfp(sio) - eq(self.db.guess_file_type("foo.pyunit"), - ("x-application/x-unittest", None)) - eq(self.db.guess_extension("x-application/x-unittest"), ".pyunit") + mimetypes.inited = False + mimetypes._default_mime_types() + mimetypes._db = None def test_read_mime_types(self): eq = self.assertEqual @@ -108,101 +70,6 @@ def test_read_mime_types(self): mock_open.assert_called_with(filename, encoding='utf-8') eq(mime_dict[".Français"], "application/no-mans-land") - def test_non_standard_types(self): - eq = self.assertEqual - # First try strict - eq(self.db.guess_file_type('foo.xul', strict=True), (None, None)) - eq(self.db.guess_extension('image/jpg', strict=True), None) - # And then non-strict - eq(self.db.guess_file_type('foo.xul', strict=False), ('text/xul', None)) - eq(self.db.guess_file_type('foo.XUL', strict=False), ('text/xul', None)) - eq(self.db.guess_file_type('foo.invalid', strict=False), (None, None)) - eq(self.db.guess_extension('image/jpg', strict=False), '.jpg') - eq(self.db.guess_extension('image/JPG', strict=False), '.jpg') - - def test_filename_with_url_delimiters(self): - # bpo-38449: URL delimiters cases should be handled also. - # They would have different mime types if interpreted as URL as - # compared to when interpreted as filename because of the semicolon. - eq = self.assertEqual - gzip_expected = ('application/x-tar', 'gzip') - for name in ( - ';1.tar.gz', - '?1.tar.gz', - '#1.tar.gz', - '#1#.tar.gz', - ';1#.tar.gz', - ';&1=123;?.tar.gz', - '?k1=v1&k2=v2.tar.gz', - ): - for prefix in ('', '/', '\\', - 'c:', 'c:/', 'c:\\', 'c:/d/', 'c:\\d\\', - '//share/server/', '\\\\share\\server\\'): - path = prefix + name - with self.subTest(path=path): - eq(self.db.guess_file_type(path), gzip_expected) - eq(self.db.guess_type(path), gzip_expected) - expected = (None, None) if os.name == 'nt' else gzip_expected - for prefix in ('//', '\\\\', '//share/', '\\\\share\\'): - path = prefix + name - with self.subTest(path=path): - eq(self.db.guess_file_type(path), expected) - eq(self.db.guess_type(path), expected) - eq(self.db.guess_file_type(r" \"\`;b&b&c |.tar.gz"), gzip_expected) - eq(self.db.guess_type(r" \"\`;b&b&c |.tar.gz"), gzip_expected) - - eq(self.db.guess_file_type(r'foo/.tar.gz'), (None, 'gzip')) - eq(self.db.guess_type(r'foo/.tar.gz'), (None, 'gzip')) - expected = (None, 'gzip') if os.name == 'nt' else gzip_expected - eq(self.db.guess_file_type(r'foo\.tar.gz'), expected) - eq(self.db.guess_type(r'foo\.tar.gz'), expected) - eq(self.db.guess_type(r'scheme:foo\.tar.gz'), gzip_expected) - - def test_url(self): - result = self.db.guess_type('http://example.com/host.html') - result = self.db.guess_type('http://host.html') - msg = 'URL only has a host name, not a file' - self.assertSequenceEqual(result, (None, None), msg) - result = self.db.guess_type('http://example.com/host.html') - msg = 'Should be text/html' - self.assertSequenceEqual(result, ('text/html', None), msg) - result = self.db.guess_type('http://example.com/host.html#x.tar') - self.assertSequenceEqual(result, ('text/html', None)) - result = self.db.guess_type('http://example.com/host.html?q=x.tar') - self.assertSequenceEqual(result, ('text/html', None)) - - def test_guess_all_types(self): - # First try strict. Use a set here for testing the results because if - # test_urllib2 is run before test_mimetypes, global state is modified - # such that the 'all' set will have more items in it. - all = self.db.guess_all_extensions('text/plain', strict=True) - self.assertTrue(set(all) >= {'.bat', '.c', '.h', '.ksh', '.pl', '.txt'}) - self.assertEqual(len(set(all)), len(all)) # no duplicates - # And now non-strict - all = self.db.guess_all_extensions('image/jpg', strict=False) - self.assertEqual(all, ['.jpg']) - # And now for no hits - all = self.db.guess_all_extensions('image/jpg', strict=True) - self.assertEqual(all, []) - # And now for type existing in both strict and non-strict mappings. - self.db.add_type('test-type', '.strict-ext') - self.db.add_type('test-type', '.non-strict-ext', strict=False) - all = self.db.guess_all_extensions('test-type', strict=False) - self.assertEqual(all, ['.strict-ext', '.non-strict-ext']) - all = self.db.guess_all_extensions('test-type') - self.assertEqual(all, ['.strict-ext']) - # Test that changing the result list does not affect the global state - all.append('.no-such-ext') - all = self.db.guess_all_extensions('test-type') - self.assertNotIn('.no-such-ext', all) - - def test_encoding(self): - filename = support.findfile("mime.types") - mimes = mimetypes.MimeTypes([filename]) - exts = mimes.guess_all_extensions('application/vnd.geocube+xml', - strict=True) - self.assertEqual(exts, ['.g3', '.g\xb3']) - def test_init_reinitializes(self): # Issue 4936: make sure an init starts clean # First, put some poison into the types table @@ -227,9 +94,15 @@ def check_extensions(): for mime_type, ext in ( ("application/epub+zip", ".epub"), ("application/octet-stream", ".bin"), + ("application/dicom", ".dcm"), ("application/gzip", ".gz"), ("application/ogg", ".ogx"), + ("application/pdf", ".pdf"), ("application/postscript", ".ps"), + ("application/rtf", ".rtf"), + ("application/sql", ".sql"), + ("application/texinfo", ".texi"), + ("application/toml", ".toml"), ("application/vnd.apple.mpegurl", ".m3u"), ("application/vnd.ms-excel", ".xls"), ("application/vnd.ms-fontobject", ".eot"), @@ -242,11 +115,11 @@ def check_extensions(): ("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".xlsx"), ("application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".docx"), ("application/vnd.rar", ".rar"), + ("application/vnd.sqlite3", ".sqlite3"), ("application/x-7z-compressed", ".7z"), ("application/x-debian-package", ".deb"), ("application/x-httpd-php", ".php"), ("application/x-rpm", ".rpm"), - ("application/x-texinfo", ".texi"), ("application/x-troff", ".roff"), ("application/xml", ".xsl"), ("application/yaml", ".yaml"), @@ -268,6 +141,7 @@ def check_extensions(): ("image/jp2", ".jp2"), ("image/jpeg", ".jpg"), ("image/jpm", ".jpm"), + ("image/jxl", ".jxl"), ("image/t38", ".t38"), ("image/tiff", ".tiff"), ("image/tiff-fx", ".tfx"), @@ -279,7 +153,6 @@ def check_extensions(): ("model/stl", ".stl"), ("text/html", ".html"), ("text/plain", ".txt"), - ("text/rtf", ".rtf"), ("text/x-rst", ".rst"), ("video/matroska", ".mkv"), ("video/matroska-3d", ".mk3d"), @@ -335,6 +208,205 @@ def test_init_stability(self): self.assertEqual(types_map, mimetypes.types_map) self.assertEqual(common_types, mimetypes.common_types) + def test_init_files(self): + guess_file_type = mimetypes.guess_file_type + self.assertEqual(guess_file_type("file.test2")[0], None) + + filename = support.findfile("mime.types2") + mimetypes.init([filename]) + self.assertEqual(guess_file_type("file.test2")[0], "testing/test2") + + mimetypes.init() + self.assertEqual(guess_file_type("file.test2")[0], None) + + def test_init_knownfiles(self): + guess_file_type = mimetypes.guess_file_type + self.assertEqual(guess_file_type("file.test2")[0], None) + + filename = support.findfile("mime.types2") + mimetypes.knownfiles = [filename, "non-existent"] + self.addCleanup(mimetypes.knownfiles.clear) + + mimetypes.init() + self.assertEqual(guess_file_type("file.test2")[0], "testing/test2") + + def test_added_types_are_used(self): + mimetypes.add_type('testing/default-type', '') + mime_type, _ = mimetypes.guess_type('') + self.assertEqual(mime_type, 'testing/default-type') + + mime_type, _ = mimetypes.guess_type('test.myext') + self.assertEqual(mime_type, None) + + mimetypes.add_type('testing/type', '.myext') + mime_type, _ = mimetypes.guess_type('test.myext') + self.assertEqual(mime_type, 'testing/type') + + def test_add_type_with_undotted_extension_not_supported(self): + msg = "Extension 'undotted' must start with '.'" + with self.assertRaisesRegex(ValueError, msg): + mimetypes.add_type("testing/type", "undotted") + with self.assertRaisesRegex(ValueError, msg): + mimetypes.add_type("", "undotted") + + +class MimeTypesClassTestCase(unittest.TestCase): + def setUp(self): + self.db = mimetypes.MimeTypes() + + def test_init_files(self): + guess_file_type = self.db.guess_file_type + self.assertEqual(guess_file_type("file.test2")[0], None) + + filename = support.findfile("mime.types2") + db = mimetypes.MimeTypes([filename]) + guess_file_type = db.guess_file_type + self.assertEqual(guess_file_type("file.test2")[0], "testing/test2") + + def test_init_knownfiles(self): + filename = support.findfile("mime.types2") + mimetypes.knownfiles = [filename, "non-existent"] + self.addCleanup(mimetypes.knownfiles.clear) + + db = mimetypes.MimeTypes() + guess_file_type = db.guess_file_type + self.assertEqual(guess_file_type("file.test2")[0], None) + + def test_case_sensitivity(self): + eq = self.assertEqual + eq(self.db.guess_file_type("foobar.html"), ("text/html", None)) + eq(self.db.guess_type("scheme:foobar.html"), ("text/html", None)) + eq(self.db.guess_file_type("foobar.HTML"), ("text/html", None)) + eq(self.db.guess_type("scheme:foobar.HTML"), ("text/html", None)) + eq(self.db.guess_file_type("foobar.tgz"), ("application/x-tar", "gzip")) + eq(self.db.guess_type("scheme:foobar.tgz"), ("application/x-tar", "gzip")) + eq(self.db.guess_file_type("foobar.TGZ"), ("application/x-tar", "gzip")) + eq(self.db.guess_type("scheme:foobar.TGZ"), ("application/x-tar", "gzip")) + eq(self.db.guess_file_type("foobar.tar.Z"), ("application/x-tar", "compress")) + eq(self.db.guess_type("scheme:foobar.tar.Z"), ("application/x-tar", "compress")) + eq(self.db.guess_file_type("foobar.tar.z"), (None, None)) + eq(self.db.guess_type("scheme:foobar.tar.z"), (None, None)) + + def test_default_data(self): + eq = self.assertEqual + eq(self.db.guess_file_type("foo.html"), ("text/html", None)) + eq(self.db.guess_file_type("foo.HTML"), ("text/html", None)) + eq(self.db.guess_file_type("foo.tgz"), ("application/x-tar", "gzip")) + eq(self.db.guess_file_type("foo.tar.gz"), ("application/x-tar", "gzip")) + eq(self.db.guess_file_type("foo.tar.Z"), ("application/x-tar", "compress")) + eq(self.db.guess_file_type("foo.tar.bz2"), ("application/x-tar", "bzip2")) + eq(self.db.guess_file_type("foo.tar.xz"), ("application/x-tar", "xz")) + + def test_data_urls(self): + eq = self.assertEqual + guess_type = self.db.guess_type + eq(guess_type("data:invalidDataWithoutComma"), (None, None)) + eq(guess_type("data:,thisIsTextPlain"), ("text/plain", None)) + eq(guess_type("data:;base64,thisIsTextPlain"), ("text/plain", None)) + eq(guess_type("data:text/x-foo,thisIsTextXFoo"), ("text/x-foo", None)) + + def test_file_parsing(self): + eq = self.assertEqual + sio = io.StringIO("x-application/x-unittest pyunit\n") + self.db.readfp(sio) + eq(self.db.guess_file_type("foo.pyunit"), + ("x-application/x-unittest", None)) + eq(self.db.guess_extension("x-application/x-unittest"), ".pyunit") + + def test_non_standard_types(self): + eq = self.assertEqual + # First try strict + eq(self.db.guess_file_type('foo.xul', strict=True), (None, None)) + # And then non-strict + eq(self.db.guess_file_type('foo.xul', strict=False), ('text/xul', None)) + eq(self.db.guess_file_type('foo.XUL', strict=False), ('text/xul', None)) + eq(self.db.guess_file_type('foo.invalid', strict=False), (None, None)) + eq(self.db.guess_extension('image/jpeg', strict=False), '.jpg') + eq(self.db.guess_extension('image/JPEG', strict=False), '.jpg') + + def test_filename_with_url_delimiters(self): + # bpo-38449: URL delimiters cases should be handled also. + # They would have different mime types if interpreted as URL as + # compared to when interpreted as filename because of the semicolon. + eq = self.assertEqual + gzip_expected = ('application/x-tar', 'gzip') + for name in ( + ';1.tar.gz', + '?1.tar.gz', + '#1.tar.gz', + '#1#.tar.gz', + ';1#.tar.gz', + ';&1=123;?.tar.gz', + '?k1=v1&k2=v2.tar.gz', + ): + for prefix in ('', '/', '\\', + 'c:', 'c:/', 'c:\\', 'c:/d/', 'c:\\d\\', + '//share/server/', '\\\\share\\server\\'): + path = prefix + name + with self.subTest(path=path): + eq(self.db.guess_file_type(path), gzip_expected) + eq(self.db.guess_type(path), gzip_expected) + expected = (None, None) if os.name == 'nt' else gzip_expected + for prefix in ('//', '\\\\', '//share/', '\\\\share\\'): + path = prefix + name + with self.subTest(path=path): + eq(self.db.guess_file_type(path), expected) + eq(self.db.guess_type(path), expected) + eq(self.db.guess_file_type(r" \"\`;b&b&c |.tar.gz"), gzip_expected) + eq(self.db.guess_type(r" \"\`;b&b&c |.tar.gz"), gzip_expected) + + eq(self.db.guess_file_type(r'foo/.tar.gz'), (None, 'gzip')) + eq(self.db.guess_type(r'foo/.tar.gz'), (None, 'gzip')) + expected = (None, 'gzip') if os.name == 'nt' else gzip_expected + eq(self.db.guess_file_type(r'foo\.tar.gz'), expected) + eq(self.db.guess_type(r'foo\.tar.gz'), expected) + eq(self.db.guess_type(r'scheme:foo\.tar.gz'), gzip_expected) + + def test_url(self): + result = self.db.guess_type('http://example.com/host.html') + result = self.db.guess_type('http://host.html') + msg = 'URL only has a host name, not a file' + self.assertSequenceEqual(result, (None, None), msg) + result = self.db.guess_type('http://example.com/host.html') + msg = 'Should be text/html' + self.assertSequenceEqual(result, ('text/html', None), msg) + result = self.db.guess_type('http://example.com/host.html#x.tar') + self.assertSequenceEqual(result, ('text/html', None)) + result = self.db.guess_type('http://example.com/host.html?q=x.tar') + self.assertSequenceEqual(result, ('text/html', None)) + + def test_guess_all_types(self): + # First try strict. Use a set here for testing the results because if + # test_urllib2 is run before test_mimetypes, global state is modified + # such that the 'all' set will have more items in it. + all = self.db.guess_all_extensions('text/plain', strict=True) + self.assertTrue(set(all) >= {'.bat', '.c', '.h', '.ksh', '.pl', '.txt'}) + self.assertEqual(len(set(all)), len(all)) # no duplicates + # And now non-strict + all = self.db.guess_all_extensions('image/jpeg', strict=False) + self.assertEqual(all, ['.jpg', '.jpe', '.jpeg']) + # And now for no hits + all = self.db.guess_all_extensions('image/jpg', strict=True) + self.assertEqual(all, []) + # And now for type existing in both strict and non-strict mappings. + self.db.add_type('test-type', '.strict-ext') + self.db.add_type('test-type', '.non-strict-ext', strict=False) + all = self.db.guess_all_extensions('test-type', strict=False) + self.assertEqual(all, ['.strict-ext', '.non-strict-ext']) + all = self.db.guess_all_extensions('test-type') + self.assertEqual(all, ['.strict-ext']) + # Test that changing the result list does not affect the global state + all.append('.no-such-ext') + all = self.db.guess_all_extensions('test-type') + self.assertNotIn('.no-such-ext', all) + + def test_encoding(self): + filename = support.findfile("mime.types") + mimes = mimetypes.MimeTypes([filename]) + exts = mimes.guess_all_extensions('application/vnd.geocube+xml', + strict=True) + self.assertEqual(exts, ['.g3', '.g\xb3']) + def test_path_like_ob(self): filename = "LICENSE.txt" filepath = os_helper.FakePath(filename) @@ -370,25 +442,7 @@ def test_keywords_args_api(self): self.assertEqual(self.db.guess_type( url="scheme:foo.html", strict=True), ("text/html", None)) self.assertEqual(self.db.guess_all_extensions( - type='image/jpg', strict=True), []) - self.assertEqual(self.db.guess_extension( - type='image/jpg', strict=False), '.jpg') - - def test_added_types_are_used(self): - mimetypes.add_type('testing/default-type', '') - mime_type, _ = mimetypes.guess_type('') - self.assertEqual(mime_type, 'testing/default-type') - - mime_type, _ = mimetypes.guess_type('test.myext') - self.assertEqual(mime_type, None) - - mimetypes.add_type('testing/type', '.myext') - mime_type, _ = mimetypes.guess_type('test.myext') - self.assertEqual(mime_type, 'testing/type') - - def test_add_type_with_undotted_extension_deprecated(self): - with self.assertWarns(DeprecationWarning): - mimetypes.add_type("testing/type", "undotted") + type='image/jpeg', strict=True), ['.jpg', '.jpe', '.jpeg']) @unittest.skipUnless(sys.platform.startswith("win"), "Windows only") @@ -450,15 +504,15 @@ def test_parse_args(self): args, help_text = mimetypes._parse_args("--invalid") self.assertTrue(help_text.startswith("usage: ")) - args, _ = mimetypes._parse_args(shlex.split("-l -e image/jpg")) + args, _ = mimetypes._parse_args(shlex.split("-l -e image/jpeg")) self.assertTrue(args.extension) self.assertTrue(args.lenient) - self.assertEqual(args.type, ["image/jpg"]) + self.assertEqual(args.type, ["image/jpeg"]) - args, _ = mimetypes._parse_args(shlex.split("-e image/jpg")) + args, _ = mimetypes._parse_args(shlex.split("-e image/jpeg")) self.assertTrue(args.extension) self.assertFalse(args.lenient) - self.assertEqual(args.type, ["image/jpg"]) + self.assertEqual(args.type, ["image/jpeg"]) args, _ = mimetypes._parse_args(shlex.split("-l foo.webp")) self.assertFalse(args.extension) @@ -470,13 +524,30 @@ def test_parse_args(self): self.assertFalse(args.lenient) self.assertEqual(args.type, ["foo.pic"]) + def test_multiple_inputs(self): + result = "\n".join(mimetypes._main(shlex.split("foo.pdf foo.png"))) + self.assertEqual( + result, + "type: application/pdf encoding: None\n" + "type: image/png encoding: None" + ) + + def test_multiple_inputs_error(self): + result = "\n".join(mimetypes._main(shlex.split("foo.pdf foo.bar_ext"))) + self.assertEqual( + result, + "type: application/pdf encoding: None\n" + "error: media type unknown for foo.bar_ext" + ) + + def test_invocation(self): for command, expected in [ - ("-l -e image/jpg", ".jpg"), ("-e image/jpeg", ".jpg"), ("-l foo.webp", "type: image/webp encoding: None"), ]: - self.assertEqual(mimetypes._main(shlex.split(command)), expected) + result = "\n".join(mimetypes._main(shlex.split(command))) + self.assertEqual(result, expected) def test_invocation_error(self): for command, expected in [ @@ -484,8 +555,62 @@ def test_invocation_error(self): ("foo.bar_ext", "error: media type unknown for foo.bar_ext"), ]: with self.subTest(command=command): - with self.assertRaisesRegex(SystemExit, expected): - mimetypes._main(shlex.split(command)) + result = "\n".join(mimetypes._main(shlex.split(command))) + self.assertEqual(result, expected) + + +@requires_subprocess() +class CommandLineSubprocessTest(unittest.TestCase): + def test_help(self): + rc, stdout, stderr = assert_python_ok('-m', 'mimetypes', '--help') + self.assertIn(b'mimetypes', stdout) + self.assertIn(b'--extension', stdout) + self.assertIn(b'--lenient', stdout) + + def test_type_lookup(self): + rc, stdout, stderr = assert_python_ok('-m', 'mimetypes', 'foo.pdf') + self.assertEqual(stdout.strip(), b'type: application/pdf encoding: None') + self.assertEqual(stderr, b'') + + def test_type_lookup_unknown(self): + rc, stdout, stderr = assert_python_failure('-m', 'mimetypes', 'foo.unknownext12345') + self.assertEqual(stdout.strip(), b'error: media type unknown for foo.unknownext12345') + self.assertEqual(stderr, b'') + + def test_extension_flag(self): + rc, stdout, stderr = assert_python_ok('-m', 'mimetypes', '-e', 'image/jpeg') + self.assertEqual(stdout.strip(), b'.jpg') + self.assertEqual(stderr, b'') + + def test_extension_flag_unknown(self): + rc, stdout, stderr = assert_python_failure('-m', 'mimetypes', '-e', 'image/unknowntype12345') + self.assertEqual(stdout.strip(), b'error: unknown type image/unknowntype12345') + self.assertEqual(stderr, b'') + + def test_lenient_flag(self): + rc, stdout, stderr = assert_python_ok('-m', 'mimetypes', '-e', '--lenient', 'text/xul') + self.assertIn(b'.xul', stdout) + self.assertEqual(stderr, b'') + + def test_multiple_inputs(self): + rc, stdout, stderr = assert_python_ok('-m', 'mimetypes', 'foo.pdf', 'foo.png') + self.assertIn(b'type: application/pdf encoding: None', stdout) + self.assertIn(b'type: image/png encoding: None', stdout) + self.assertEqual(stderr, b'') + + def test_multiple_inputs_with_error(self): + rc, stdout, stderr = assert_python_failure( + '-m', 'mimetypes', 'foo.pdf', 'foo.unknownext12345' + ) + self.assertIn(b'type: application/pdf encoding: None', stdout) + self.assertIn(b'error: media type unknown for foo.unknownext12345', stdout) + self.assertEqual(stderr, b'') + + @force_not_colorized + def test_unknown_flag(self): + rc, stdout, stderr = assert_python_failure('-m', 'mimetypes', '--unknown-flag', 'foo.pdf') + self.assertEqual(stdout, b'') + self.assertIn(b'error: unrecognized arguments: --unknown-flag', stderr) if __name__ == "__main__": diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 4f25e9c2a03cb4..46249e5138aed5 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -8,7 +8,7 @@ import xml.dom.minidom -from xml.dom.minidom import parse, Attr, Node, Document, parseString +from xml.dom.minidom import parse, Attr, Node, Document, Element, parseString from xml.dom.minidom import getDOMImplementation from xml.parsers.expat import ExpatError @@ -173,6 +173,49 @@ def testAppendChild(self): self.assertEqual(dom.documentElement.childNodes[-1].data, "Hello") dom.unlink() + @support.requires_resource('cpu') + def testAppendChildNoQuadraticComplexity(self): + impl = getDOMImplementation() + + def work(n): + doc = impl.createDocument(None, "some_tag", None) + element = doc.documentElement + total_calls = 0 + + # Count attribute accesses as a proxy for work done + def getattribute_counter(self, attr): + nonlocal total_calls + total_calls += 1 + return object.__getattribute__(self, attr) + + with support.swap_attr(Element, "__getattribute__", getattribute_counter): + for _ in range(n): + child = doc.createElement("child") + element.appendChild(child) + element = child + return total_calls + + # Doubling N should not ~quadruple the work. + w1 = work(1024) + w2 = work(2048) + w3 = work(4096) + + self.assertGreater(w1, 0) + r1 = w2 / w1 + r2 = w3 / w2 + self.assertLess( + max(r1, r2), 3.2, + msg=f"Possible quadratic behavior: work={w1,w2,w3} ratios={r1,r2}" + ) + + def testSetAttributeNodeWithoutOwnerDocument(self): + # regression test for gh-142754 + elem = Element("test") + attr = Attr("id") + attr.value = "test-id" + elem.setAttributeNode(attr) + self.assertEqual(elem.getAttribute("id"), "test-id") + def testAppendChildFragment(self): dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() dom.documentElement.appendChild(frag) diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index b2a299ed172967..2e2ac147968dd4 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -1,3 +1,4 @@ +from test import support from test.support import ( requires, _2G, _4G, gc_collect, cpython_only, is_emscripten, is_apple, in_systemd_nspawn_sync_suppressed, @@ -5,8 +6,8 @@ from test.support.import_helper import import_module from test.support.os_helper import TESTFN, unlink from test.support.script_helper import assert_python_ok -import unittest import errno +import unittest import os import re import itertools @@ -57,6 +58,7 @@ def test_basic(self): f.write(b'\0'* (PAGESIZE-3) ) f.flush() m = mmap.mmap(f.fileno(), 2 * PAGESIZE) + self.addCleanup(m.close) finally: f.close() @@ -114,31 +116,28 @@ def test_basic(self): # Try to seek to negative position... self.assertRaises(ValueError, m.seek, -len(m)-1, 2) + @unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize') + def test_resize(self): + # Create a file to be mmap'ed. + with open(TESTFN, 'bw+') as f: + # Write 2 pages worth of data to the file + f.write(b'\0'* 2 * PAGESIZE) + f.flush() + m = mmap.mmap(f.fileno(), 2 * PAGESIZE) + self.addCleanup(m.close) + # Try resizing map - try: - m.resize(512) - except SystemError: - # resize() not supported - # No messages are printed, since the output of this test suite - # would then be different across platforms. - pass - else: - # resize() is supported - self.assertEqual(len(m), 512) - # Check that we can no longer seek beyond the new size. - self.assertRaises(ValueError, m.seek, 513, 0) - - # Check that the underlying file is truncated too - # (bug #728515) - f = open(TESTFN, 'rb') - try: - f.seek(0, 2) - self.assertEqual(f.tell(), 512) - finally: - f.close() - self.assertEqual(m.size(), 512) + m.resize(512) + self.assertEqual(len(m), 512) + # Check that we can no longer seek beyond the new size. + self.assertRaises(ValueError, m.seek, 513, 0) - m.close() + # Check that the underlying file is truncated too + # (bug #728515) + with open(TESTFN, 'rb') as f: + f.seek(0, 2) + self.assertEqual(f.tell(), 512) + self.assertEqual(m.size(), 512) def test_access_parameter(self): # Test for "access" keyword parameter @@ -183,15 +182,10 @@ def test_access_parameter(self): else: self.fail("Able to write to readonly memory map") - # Ensuring that readonly mmap can't be resized - try: - m.resize(2*mapsize) - except SystemError: # resize is not universally supported - pass - except TypeError: - pass - else: - self.fail("Able to resize readonly memory map") + if hasattr(m, 'resize'): + # Ensuring that readonly mmap can't be resized + with self.assertRaises(TypeError): + m.resize(2 * mapsize) with open(TESTFN, "rb") as fp: self.assertEqual(fp.read(), b'a'*mapsize, "Readonly memory map data file was modified") @@ -242,8 +236,9 @@ def test_access_parameter(self): with open(TESTFN, "rb") as fp: self.assertEqual(fp.read(), b'c'*mapsize, "Copy-on-write test data file should not be modified.") - # Ensuring copy-on-write maps cannot be resized - self.assertRaises(TypeError, m.resize, 2*mapsize) + if hasattr(m, 'resize'): + # Ensuring copy-on-write maps cannot be resized + self.assertRaises(TypeError, m.resize, 2 * mapsize) m.close() # Ensuring invalid access parameter raises exception @@ -270,62 +265,57 @@ def test_access_parameter(self): self.assertRaises(TypeError, m.write_byte, 0) m.close() - @unittest.skipIf(os.name == 'nt', 'trackfd not present on Windows') - def test_trackfd_parameter(self): + @support.subTests('close_original_fd', (True, False)) + def test_trackfd_parameter(self, close_original_fd): size = 64 with open(TESTFN, "wb") as f: f.write(b"a"*size) - for close_original_fd in True, False: - with self.subTest(close_original_fd=close_original_fd): - with open(TESTFN, "r+b") as f: - with mmap.mmap(f.fileno(), size, trackfd=False) as m: - if close_original_fd: - f.close() - self.assertEqual(len(m), size) - with self.assertRaises(OSError) as err_cm: - m.size() - self.assertEqual(err_cm.exception.errno, errno.EBADF) - with self.assertRaises(ValueError): - m.resize(size * 2) - with self.assertRaises(ValueError): - m.resize(size // 2) - self.assertEqual(m.closed, False) - - # Smoke-test other API - m.write_byte(ord('X')) - m[2] = ord('Y') - m.flush() - with open(TESTFN, "rb") as f: - self.assertEqual(f.read(4), b'XaYa') - self.assertEqual(m.tell(), 1) - m.seek(0) - self.assertEqual(m.tell(), 0) - self.assertEqual(m.read_byte(), ord('X')) - - self.assertEqual(m.closed, True) - self.assertEqual(os.stat(TESTFN).st_size, size) - - @unittest.skipIf(os.name == 'nt', 'trackfd not present on Windows') + with open(TESTFN, "r+b") as f: + with mmap.mmap(f.fileno(), size, trackfd=False) as m: + if close_original_fd: + f.close() + self.assertEqual(len(m), size) + with self.assertRaises(ValueError): + m.size() + if hasattr(m, 'resize'): + with self.assertRaises(ValueError): + m.resize(size * 2) + with self.assertRaises(ValueError): + m.resize(size // 2) + self.assertIs(m.closed, False) + + # Smoke-test other API + m.write_byte(ord('X')) + m[2] = ord('Y') + m.flush() + with open(TESTFN, "rb") as f: + self.assertEqual(f.read(4), b'XaYa') + self.assertEqual(m.tell(), 1) + m.seek(0) + self.assertEqual(m.tell(), 0) + self.assertEqual(m.read_byte(), ord('X')) + + if os.name == 'nt' and not close_original_fd: + self.assertRaises(PermissionError, os.rename, TESTFN, TESTFN+'1') + else: + os.rename(TESTFN, TESTFN+'1') + os.rename(TESTFN+'1', TESTFN) + + self.assertIs(m.closed, True) + self.assertEqual(os.stat(TESTFN).st_size, size) + def test_trackfd_neg1(self): size = 64 with mmap.mmap(-1, size, trackfd=False) as m: - with self.assertRaises(OSError): - m.size() with self.assertRaises(ValueError): - m.resize(size // 2) + m.size() + if hasattr(m, 'resize'): + with self.assertRaises(ValueError): + m.resize(size // 2) self.assertEqual(len(m), size) m[0] = ord('a') assert m[0] == ord('a') - @unittest.skipIf(os.name != 'nt', 'trackfd only fails on Windows') - def test_no_trackfd_parameter_on_windows(self): - # 'trackffd' is an invalid keyword argument for this function - size = 64 - with self.assertRaises(TypeError): - mmap.mmap(-1, size, trackfd=True) - with self.assertRaises(TypeError): - mmap.mmap(-1, size, trackfd=False) - def test_bad_file_desc(self): # Try opening a bad file descriptor... self.assertRaises(OSError, mmap.mmap, -2, 4096) @@ -364,6 +354,8 @@ def test_find_end(self): self.assertEqual(m.find(b'one', 1, -1), 8) self.assertEqual(m.find(b'one', 1, -2), -1) self.assertEqual(m.find(bytearray(b'one')), 0) + self.assertEqual(m.find(b'', n + 1), -1) + self.assertEqual(m.rfind(b'', n + 1), -1) for i in range(-n-1, n+1): for j in range(-n-1, n+1): @@ -505,6 +497,7 @@ def test_anonymous(self): b = x & 0xff m[x] = b self.assertEqual(m[x], b) + self.assertEqual(m.size(), PAGESIZE) def test_read_all(self): m = mmap.mmap(-1, 16) @@ -614,13 +607,9 @@ def test_offset (self): self.assertEqual(m[0:3], b'foo') f.close() - # Try resizing map - try: + if hasattr(m, 'resize'): + # Try resizing map m.resize(512) - except SystemError: - pass - else: - # resize() is supported self.assertEqual(len(m), 512) # Check that we can no longer seek beyond the new size. self.assertRaises(ValueError, m.seek, 513, 0) @@ -812,14 +801,12 @@ def test_write_returning_the_number_of_bytes_written(self): self.assertEqual(mm.write(b"yz"), 2) self.assertEqual(mm.write(b"python"), 6) + @unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize') def test_resize_past_pos(self): m = mmap.mmap(-1, 8192) self.addCleanup(m.close) m.read(5000) - try: - m.resize(4096) - except SystemError: - self.skipTest("resizing not supported") + m.resize(4096) self.assertEqual(m.read(14), b'') self.assertRaises(ValueError, m.read_byte) self.assertRaises(ValueError, m.write_byte, 42) @@ -887,6 +874,10 @@ def test_madvise(self): size = 2 * PAGESIZE m = mmap.mmap(-1, size) + class Number: + def __index__(self): + return 2 + with self.assertRaisesRegex(ValueError, "madvise start out of bounds"): m.madvise(mmap.MADV_NORMAL, size) with self.assertRaisesRegex(ValueError, "madvise start out of bounds"): @@ -895,41 +886,70 @@ def test_madvise(self): m.madvise(mmap.MADV_NORMAL, 0, -1) with self.assertRaisesRegex(OverflowError, "madvise length too large"): m.madvise(mmap.MADV_NORMAL, PAGESIZE, sys.maxsize) + with self.assertRaisesRegex( + TypeError, "'str' object cannot be interpreted as an integer"): + m.madvise(mmap.MADV_NORMAL, PAGESIZE, "Not a Number") self.assertEqual(m.madvise(mmap.MADV_NORMAL), None) self.assertEqual(m.madvise(mmap.MADV_NORMAL, PAGESIZE), None) self.assertEqual(m.madvise(mmap.MADV_NORMAL, PAGESIZE, size), None) self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, 2), None) + self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, Number()), None) self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, size), None) - @unittest.skipUnless(os.name == 'nt', 'requires Windows') - def test_resize_up_when_mapped_to_pagefile(self): + @unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize') + def test_resize_up_anonymous_mapping(self): """If the mmap is backed by the pagefile ensure a resize up can happen and that the original data is still in place """ start_size = PAGESIZE new_size = 2 * start_size - data = bytes(random.getrandbits(8) for _ in range(start_size)) + data = random.randbytes(start_size) - m = mmap.mmap(-1, start_size) - m[:] = data - m.resize(new_size) - self.assertEqual(len(m), new_size) - self.assertEqual(m[:start_size], data[:start_size]) + with mmap.mmap(-1, start_size) as m: + m[:] = data + if sys.platform.startswith(('linux', 'android')): + # Can't expand a shared anonymous mapping on Linux. + # See https://bugzilla.kernel.org/show_bug.cgi?id=8691 + with self.assertRaises(ValueError): + m.resize(new_size) + else: + m.resize(new_size) + self.assertEqual(len(m), new_size) + self.assertEqual(m[:start_size], data) + self.assertEqual(m[start_size:], b'\0' * (new_size - start_size)) + + @unittest.skipUnless(os.name == 'posix', 'requires Posix') + @unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize') + def test_resize_up_private_anonymous_mapping(self): + start_size = PAGESIZE + new_size = 2 * start_size + data = random.randbytes(start_size) - @unittest.skipUnless(os.name == 'nt', 'requires Windows') - def test_resize_down_when_mapped_to_pagefile(self): + with mmap.mmap(-1, start_size, flags=mmap.MAP_PRIVATE) as m: + m[:] = data + m.resize(new_size) + self.assertEqual(len(m), new_size) + self.assertEqual(m[:start_size], data) + self.assertEqual(m[start_size:], b'\0' * (new_size - start_size)) + + @unittest.skipUnless(hasattr(mmap.mmap, 'resize'), 'requires mmap.resize') + def test_resize_down_anonymous_mapping(self): """If the mmap is backed by the pagefile ensure a resize down up can happen and that a truncated form of the original data is still in place """ - start_size = PAGESIZE + start_size = 2 * PAGESIZE new_size = start_size // 2 - data = bytes(random.getrandbits(8) for _ in range(start_size)) - - m = mmap.mmap(-1, start_size) - m[:] = data - m.resize(new_size) - self.assertEqual(len(m), new_size) - self.assertEqual(m[:new_size], data[:new_size]) + data = random.randbytes(start_size) + + with mmap.mmap(-1, start_size) as m: + m[:] = data + m.resize(new_size) + self.assertEqual(len(m), new_size) + self.assertEqual(m[:], data[:new_size]) + if sys.platform.startswith(('linux', 'android')): + # Can't expand to its original size. + with self.assertRaises(ValueError): + m.resize(start_size) @unittest.skipUnless(os.name == 'nt', 'requires Windows') def test_resize_fails_if_mapping_held_elsewhere(self): @@ -1136,6 +1156,74 @@ def test_access_violations(self): self.assertEqual(stdout.strip(), b'') self.assertEqual(stderr.strip(), b'') + def test_flush_parameters(self): + with open(TESTFN, 'wb+') as f: + f.write(b'x' * PAGESIZE * 3) + f.flush() + + m = mmap.mmap(f.fileno(), PAGESIZE * 3) + self.addCleanup(m.close) + + m.flush() + m.flush(PAGESIZE) + m.flush(PAGESIZE, PAGESIZE) + + if hasattr(mmap, 'MS_SYNC'): + m.flush(0, PAGESIZE, flags=mmap.MS_SYNC) + if hasattr(mmap, 'MS_ASYNC'): + m.flush(flags=mmap.MS_ASYNC) + if hasattr(mmap, 'MS_INVALIDATE'): + m.flush(PAGESIZE * 2, flags=mmap.MS_INVALIDATE) + if hasattr(mmap, 'MS_ASYNC') and hasattr(mmap, 'MS_INVALIDATE'): + if sys.platform == 'freebsd': + # FreeBSD doesn't support this combination + with self.assertRaises(OSError) as cm: + m.flush(0, PAGESIZE, flags=mmap.MS_ASYNC | mmap.MS_INVALIDATE) + self.assertEqual(cm.exception.errno, errno.EINVAL) + else: + m.flush(0, PAGESIZE, flags=mmap.MS_ASYNC | mmap.MS_INVALIDATE) + + @unittest.skipUnless(sys.platform == 'linux', 'Linux only') + @support.requires_linux_version(5, 17, 0) + @unittest.skipIf(support.linked_to_musl(), "musl libc issue, gh-143632") + def test_set_name(self): + # Test setting name on anonymous mmap + m = mmap.mmap(-1, PAGESIZE) + self.addCleanup(m.close) + try: + result = m.set_name('test_mapping') + except OSError as exc: + if exc.errno == errno.EINVAL: + # gh-142419: On Fedora, prctl(PR_SET_VMA_ANON_NAME) fails with + # EINVAL because the kernel option CONFIG_ANON_VMA_NAME is + # disabled. + # See: https://bugzilla.redhat.com/show_bug.cgi?id=2302746 + self.skipTest("prctl() failed with EINVAL") + else: + raise + self.assertIsNone(result) + + # Test name length limit (80 chars including prefix "cpython:mmap:" and '\0') + # Prefix is 13 chars, so max name is 66 chars + long_name = 'x' * 66 + result = m.set_name(long_name) + self.assertIsNone(result) + + # Test name too long + too_long_name = 'x' * 67 + with self.assertRaises(ValueError): + m.set_name(too_long_name) + + # Test that file-backed mmap raises error + with open(TESTFN, 'wb+') as f: + f.write(b'x' * PAGESIZE) + f.flush() + m2 = mmap.mmap(f.fileno(), PAGESIZE) + self.addCleanup(m2.close) + + with self.assertRaises(ValueError): + m2.set_name('should_fail') + class LargeMmapTests(unittest.TestCase): diff --git a/Lib/test/test_modulefinder.py b/Lib/test/test_modulefinder.py index b64e684f80599f..703bfbf94d7bff 100644 --- a/Lib/test/test_modulefinder.py +++ b/Lib/test/test_modulefinder.py @@ -76,6 +76,18 @@ from sys import version_info """] +namespace_package_test = [ + "module", + ["a", "module"], + ["a.c", "blahblah"], [], + """\ +module.py + import a + import a.c + import blahblah +a/b.py +"""] + absolute_import_test = [ "a.module", ["a", "a.module", @@ -353,6 +365,9 @@ def _do_test(self, info, report=False, debug=0, replace_paths=[], modulefinder_c def test_package(self): self._do_test(package_test) + def test_namespace_package(self): + self._do_test(namespace_package_test) + def test_maybe(self): self._do_test(maybe_test) diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index a932ac80117d27..b8861d09e1564b 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -3,6 +3,7 @@ import collections import dis import functools +import inspect import math import operator import sys @@ -11,10 +12,10 @@ import unittest import test.support -from test.support import requires_specialization_ft, script_helper +from test.support import import_helper, requires_specialization, script_helper -_testcapi = test.support.import_helper.import_module("_testcapi") -_testinternalcapi = test.support.import_helper.import_module("_testinternalcapi") +_testcapi = import_helper.import_module("_testcapi") +_testinternalcapi = import_helper.import_module("_testinternalcapi") PAIR = (0,1) @@ -195,13 +196,10 @@ def test_c_return_count(self): (E.BRANCH, "branch"), ] -EXCEPT_EVENTS = [ +SIMPLE_EVENTS = INSTRUMENTED_EVENTS + [ (E.RAISE, "raise"), - (E.PY_UNWIND, "unwind"), (E.EXCEPTION_HANDLED, "exception_handled"), -] - -SIMPLE_EVENTS = INSTRUMENTED_EVENTS + EXCEPT_EVENTS + [ + (E.PY_UNWIND, "unwind"), (E.C_RAISE, "c_raise"), (E.C_RETURN, "c_return"), ] @@ -737,18 +735,6 @@ def test_disable_legal_events(self): sys.monitoring.register_callback(TEST_TOOL, event, None) - def test_disable_illegal_events(self): - for event, name in EXCEPT_EVENTS: - try: - counter = CounterWithDisable() - counter.disable = True - sys.monitoring.register_callback(TEST_TOOL, event, counter) - sys.monitoring.set_events(TEST_TOOL, event) - with self.assertRaises(ValueError): - self.raise_handle_reraise() - finally: - sys.monitoring.set_events(TEST_TOOL, 0) - sys.monitoring.register_callback(TEST_TOOL, event, None) class ExceptionRecorder: @@ -1046,7 +1032,7 @@ def func(): ) self.assertEqual(events[0], ("throw", IndexError)) - @requires_specialization_ft + @requires_specialization def test_no_unwind_for_shim_frame(self): class ValueErrorRaiser: def __init__(self): @@ -1078,6 +1064,25 @@ def f(): self.assertEqual(events, expected) + # gh-140373 + def test_gen_unwind(self): + def gen(): + yield 1 + + def f(): + g = gen() + next(g) + g.close() + + recorders = ( + UnwindRecorder, + ) + events = self.get_events(f, TEST_TOOL, recorders) + expected = [ + ("unwind", GeneratorExit, "gen"), + ] + self.assertEqual(events, expected) + class LineRecorder: event_type = E.LINE @@ -1194,19 +1199,20 @@ def func1(): line3 = 3 self.check_events(func1, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [ - ('line', 'get_events', 10), - ('line', 'func1', 1), - ('instruction', 'func1', 2), - ('instruction', 'func1', 4), - ('line', 'func1', 2), - ('instruction', 'func1', 6), - ('instruction', 'func1', 8), - ('line', 'func1', 3), - ('instruction', 'func1', 10), - ('instruction', 'func1', 12), - ('instruction', 'func1', 14), - ('instruction', 'func1', 16), - ('line', 'get_events', 11)]) + ("line", "get_events", 10), + ("line", "func1", 1), + ("instruction", "func1", 4), + ("instruction", "func1", 6), + ("line", "func1", 2), + ("instruction", "func1", 8), + ("instruction", "func1", 10), + ("line", "func1", 3), + ("instruction", "func1", 12), + ("instruction", "func1", 14), + ("instruction", "func1", 16), + ("instruction", "func1", 18), + ("line", "get_events", 11), + ]) def test_c_call(self): @@ -1216,22 +1222,23 @@ def func2(): line3 = 3 self.check_events(func2, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [ - ('line', 'get_events', 10), - ('line', 'func2', 1), - ('instruction', 'func2', 2), - ('instruction', 'func2', 4), - ('line', 'func2', 2), - ('instruction', 'func2', 6), - ('instruction', 'func2', 8), - ('instruction', 'func2', 28), - ('instruction', 'func2', 30), - ('instruction', 'func2', 38), - ('line', 'func2', 3), - ('instruction', 'func2', 40), - ('instruction', 'func2', 42), - ('instruction', 'func2', 44), - ('instruction', 'func2', 46), - ('line', 'get_events', 11)]) + ("line", "get_events", 10), + ("line", "func2", 1), + ("instruction", "func2", 4), + ("instruction", "func2", 6), + ("line", "func2", 2), + ("instruction", "func2", 8), + ("instruction", "func2", 10), + ("instruction", "func2", 30), + ("instruction", "func2", 32), + ("instruction", "func2", 40), + ("line", "func2", 3), + ("instruction", "func2", 42), + ("instruction", "func2", 44), + ("instruction", "func2", 46), + ("instruction", "func2", 48), + ("line", "get_events", 11), + ]) def test_try_except(self): @@ -1244,28 +1251,29 @@ def func3(): line = 6 self.check_events(func3, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [ - ('line', 'get_events', 10), - ('line', 'func3', 1), - ('instruction', 'func3', 2), - ('line', 'func3', 2), - ('instruction', 'func3', 4), - ('instruction', 'func3', 6), - ('line', 'func3', 3), - ('instruction', 'func3', 8), - ('instruction', 'func3', 18), - ('instruction', 'func3', 20), - ('line', 'func3', 4), - ('instruction', 'func3', 22), - ('line', 'func3', 5), - ('instruction', 'func3', 24), - ('instruction', 'func3', 26), - ('instruction', 'func3', 28), - ('line', 'func3', 6), - ('instruction', 'func3', 30), - ('instruction', 'func3', 32), - ('instruction', 'func3', 34), - ('instruction', 'func3', 36), - ('line', 'get_events', 11)]) + ("line", "get_events", 10), + ("line", "func3", 1), + ("instruction", "func3", 4), + ("line", "func3", 2), + ("instruction", "func3", 6), + ("instruction", "func3", 8), + ("line", "func3", 3), + ("instruction", "func3", 10), + ("instruction", "func3", 20), + ("instruction", "func3", 22), + ("line", "func3", 4), + ("instruction", "func3", 24), + ("line", "func3", 5), + ("instruction", "func3", 26), + ("instruction", "func3", 28), + ("instruction", "func3", 30), + ("line", "func3", 6), + ("instruction", "func3", 32), + ("instruction", "func3", 34), + ("instruction", "func3", 36), + ("instruction", "func3", 38), + ("line", "get_events", 11), + ]) def test_with_restart(self): def func1(): @@ -1276,16 +1284,16 @@ def func1(): self.check_events(func1, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [ ('line', 'get_events', 10), ('line', 'func1', 1), - ('instruction', 'func1', 2), ('instruction', 'func1', 4), - ('line', 'func1', 2), ('instruction', 'func1', 6), + ('line', 'func1', 2), ('instruction', 'func1', 8), - ('line', 'func1', 3), ('instruction', 'func1', 10), + ('line', 'func1', 3), ('instruction', 'func1', 12), ('instruction', 'func1', 14), ('instruction', 'func1', 16), + ('instruction', 'func1', 18), ('line', 'get_events', 11)]) sys.monitoring.restart_events() @@ -1293,16 +1301,16 @@ def func1(): self.check_events(func1, recorders = LINE_AND_INSTRUCTION_RECORDERS, expected = [ ('line', 'get_events', 10), ('line', 'func1', 1), - ('instruction', 'func1', 2), ('instruction', 'func1', 4), - ('line', 'func1', 2), ('instruction', 'func1', 6), + ('line', 'func1', 2), ('instruction', 'func1', 8), - ('line', 'func1', 3), ('instruction', 'func1', 10), + ('line', 'func1', 3), ('instruction', 'func1', 12), ('instruction', 'func1', 14), ('instruction', 'func1', 16), + ('instruction', 'func1', 18), ('line', 'get_events', 11)]) def test_turn_off_only_instruction(self): @@ -1350,10 +1358,10 @@ def func1(): line1 = 1 MUST_INCLUDE_LI = [ - ('instruction', 'func1', 2), - ('line', 'func1', 2), ('instruction', 'func1', 4), - ('instruction', 'func1', 6)] + ('line', 'func1', 2), + ('instruction', 'func1', 6), + ('instruction', 'func1', 8)] def test_line_then_instruction(self): recorders = [ LineRecorder, InstructionRecorder ] @@ -1370,11 +1378,11 @@ def func2(): len(()) MUST_INCLUDE_CI = [ - ('instruction', 'func2', 2), + ('instruction', 'func2', 4), ('call', 'func2', sys.monitoring.MISSING), ('call', 'len', ()), - ('instruction', 'func2', 12), - ('instruction', 'func2', 14)] + ('instruction', 'func2', 14), + ('instruction', 'func2', 16)] @@ -1458,8 +1466,334 @@ def func3(): ('line', 'func3', 6)]) def test_set_non_local_event(self): + # C_RETURN/C_RAISE are ancillary (derived) events — not settable as local with self.assertRaises(ValueError): - sys.monitoring.set_local_events(TEST_TOOL, just_call.__code__, E.RAISE) + sys.monitoring.set_local_events(TEST_TOOL, just_call.__code__, E.C_RETURN) + + def test_local_reraise(self): + """RERAISE fires as a local event only for the instrumented code object.""" + + def foo(): + try: + raise RuntimeError("test") + except RuntimeError: + raise + + def bar(): + try: + raise RuntimeError("test") + except RuntimeError: + raise + + events = set() + + def callback(code, offset, exc): + events.add(code.co_name) + + try: + sys.monitoring.register_callback(TEST_TOOL, E.RERAISE, callback) + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.RERAISE) + try: + foo() + except RuntimeError: + pass + try: + bar() # should NOT trigger the callback + except RuntimeError: + pass + self.assertEqual(events, {'foo'}) + finally: + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0) + sys.monitoring.register_callback(TEST_TOOL, E.RERAISE, None) + + def test_local_reraise_disable(self): + """Returning DISABLE from a RERAISE callback disables it for that code object.""" + + call_count = 0 + + def foo(): + try: + raise RuntimeError("test") + except RuntimeError: + raise + + def callback(code, offset, exc): + nonlocal call_count + call_count += 1 + return sys.monitoring.DISABLE + + try: + sys.monitoring.register_callback(TEST_TOOL, E.RERAISE, callback) + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.RERAISE) + try: + foo() + except RuntimeError: + pass + self.assertEqual(call_count, 1) + try: + foo() + except RuntimeError: + pass + self.assertEqual(call_count, 1) # not fired again — disabled + finally: + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0) + sys.monitoring.register_callback(TEST_TOOL, E.RERAISE, None) + + def test_local_py_throw(self): + """PY_THROW fires as a local event only for the instrumented code object.""" + + def gen_foo(): + yield 1 + yield 2 + + def gen_bar(): + yield 1 + yield 2 + + events = [] + + def callback(code, offset, exc): + events.append(code.co_name) + + try: + sys.monitoring.register_callback(TEST_TOOL, E.PY_THROW, callback) + sys.monitoring.set_local_events(TEST_TOOL, gen_foo.__code__, E.PY_THROW) + + g = gen_foo() + next(g) + try: + g.throw(RuntimeError("test")) + except RuntimeError: + pass + + h = gen_bar() + next(h) + try: + h.throw(RuntimeError("test")) # should NOT trigger the callback + except RuntimeError: + pass + + self.assertEqual(events, ['gen_foo']) + finally: + sys.monitoring.set_local_events(TEST_TOOL, gen_foo.__code__, 0) + sys.monitoring.register_callback(TEST_TOOL, E.PY_THROW, None) + + def test_local_py_throw_disable(self): + """Returning DISABLE from a PY_THROW callback disables it for that code object.""" + + call_count = 0 + + def gen_foo(): + yield 1 + yield 2 + + def callback(code, offset, exc): + nonlocal call_count + call_count += 1 + return sys.monitoring.DISABLE + + try: + sys.monitoring.register_callback(TEST_TOOL, E.PY_THROW, callback) + sys.monitoring.set_local_events(TEST_TOOL, gen_foo.__code__, E.PY_THROW) + + g = gen_foo() + next(g) + try: + g.throw(RuntimeError("test")) + except RuntimeError: + pass + self.assertEqual(call_count, 1) + + g2 = gen_foo() + next(g2) + try: + g2.throw(RuntimeError("test")) + except RuntimeError: + pass + self.assertEqual(call_count, 1) # not fired again — disabled + finally: + sys.monitoring.set_local_events(TEST_TOOL, gen_foo.__code__, 0) + sys.monitoring.register_callback(TEST_TOOL, E.PY_THROW, None) + + def test_local_raise(self): + """RAISE fires as a local event only for the instrumented code object.""" + + def foo(): + try: + raise RuntimeError("test") + except RuntimeError: + pass + + def bar(): + try: + raise RuntimeError("test") + except RuntimeError: + pass + + events = [] + + def callback(code, offset, exc): + events.append(code.co_name) + + try: + sys.monitoring.register_callback(TEST_TOOL, E.RAISE, callback) + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.RAISE) + foo() + bar() # should NOT trigger the callback + self.assertEqual(events, ['foo']) + finally: + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0) + sys.monitoring.register_callback(TEST_TOOL, E.RAISE, None) + + def test_local_raise_disable(self): + """Returning DISABLE from a RAISE callback disables it for that code object.""" + + call_count = 0 + + def foo(): + try: + raise RuntimeError("test") + except RuntimeError: + pass + + def callback(code, offset, exc): + nonlocal call_count + call_count += 1 + return sys.monitoring.DISABLE + + try: + sys.monitoring.register_callback(TEST_TOOL, E.RAISE, callback) + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.RAISE) + foo() + self.assertEqual(call_count, 1) + foo() + self.assertEqual(call_count, 1) # not fired again — disabled + finally: + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0) + sys.monitoring.register_callback(TEST_TOOL, E.RAISE, None) + + def test_local_exception_handled(self): + """EXCEPTION_HANDLED fires as a local event only for the instrumented code object.""" + + def foo(): + try: + raise RuntimeError("test") + except RuntimeError: + pass + + def bar(): + try: + raise RuntimeError("test") + except RuntimeError: + pass + + events = [] + + def callback(code, offset, exc): + events.append(code.co_name) + + try: + sys.monitoring.register_callback(TEST_TOOL, E.EXCEPTION_HANDLED, callback) + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.EXCEPTION_HANDLED) + foo() + bar() # should NOT trigger the callback + self.assertEqual(events, ['foo']) + finally: + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0) + sys.monitoring.register_callback(TEST_TOOL, E.EXCEPTION_HANDLED, None) + + def test_local_exception_handled_disable(self): + """Returning DISABLE from an EXCEPTION_HANDLED callback disables it for that code object.""" + + call_count = 0 + + def foo(): + try: + raise RuntimeError("test") + except RuntimeError: + pass + + def callback(code, offset, exc): + nonlocal call_count + call_count += 1 + return sys.monitoring.DISABLE + + try: + sys.monitoring.register_callback(TEST_TOOL, E.EXCEPTION_HANDLED, callback) + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.EXCEPTION_HANDLED) + foo() + self.assertEqual(call_count, 1) + foo() + self.assertEqual(call_count, 1) # not fired again — disabled + finally: + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0) + sys.monitoring.register_callback(TEST_TOOL, E.EXCEPTION_HANDLED, None) + + def test_local_py_unwind(self): + """PY_UNWIND fires as a local event only for the instrumented code object.""" + + def foo(): + raise RuntimeError("test") + + def bar(): + raise RuntimeError("test") + + events = [] + + def callback(code, offset, exc): + events.append(code.co_name) + + try: + sys.monitoring.register_callback(TEST_TOOL, E.PY_UNWIND, callback) + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.PY_UNWIND) + + try: + foo() + except RuntimeError: + pass + + try: + bar() # should NOT trigger the callback + except RuntimeError: + pass + + self.assertEqual(events, ['foo']) + finally: + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0) + sys.monitoring.register_callback(TEST_TOOL, E.PY_UNWIND, None) + + def test_local_py_unwind_disable(self): + """Returning DISABLE from a PY_UNWIND callback disables it for that code object.""" + + call_count = 0 + + def foo(): + raise RuntimeError("test") + + def callback(code, offset, exc): + nonlocal call_count + call_count += 1 + return sys.monitoring.DISABLE + + try: + sys.monitoring.register_callback(TEST_TOOL, E.PY_UNWIND, callback) + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, E.PY_UNWIND) + + try: + foo() + except RuntimeError: + pass + self.assertEqual(call_count, 1) # fired once + + try: + foo() + except RuntimeError: + pass + self.assertEqual(call_count, 1) # not fired again — disabled by DISABLE return + + finally: + sys.monitoring.set_local_events(TEST_TOOL, foo.__code__, 0) + sys.monitoring.register_callback(TEST_TOOL, E.PY_UNWIND, None) def line_from_offset(code, offset): for start, end, line in code.co_lines(): @@ -1589,11 +1923,11 @@ def whilefunc(n=0): ('branch right', 'whilefunc', 1, 3)]) self.check_events(func, recorders = BRANCH_OFFSET_RECORDERS, expected = [ - ('branch left', 'func', 28, 32), - ('branch right', 'func', 44, 58), - ('branch left', 'func', 28, 32), - ('branch left', 'func', 44, 50), - ('branch right', 'func', 28, 70)]) + ('branch left', 'func', 32, 36), + ('branch right', 'func', 48, 62), + ('branch left', 'func', 32, 36), + ('branch left', 'func', 48, 54), + ('branch right', 'func', 32, 74)]) def test_except_star(self): @@ -1620,8 +1954,8 @@ def func(): ('branch', 'func', 4, 4), ('line', 'func', 5), ('line', 'meth', 1), - ('jump', 'func', 5, '[offset=120]'), - ('branch', 'func', '[offset=124]', '[offset=130]'), + ('jump', 'func', 5, '[offset=122]'), + ('branch', 'func', '[offset=126]', '[offset=132]'), ('line', 'get_events', 11)]) self.check_events(func, recorders = FLOW_AND_LINE_RECORDERS, expected = [ @@ -1635,8 +1969,8 @@ def func(): ('line', 'func', 5), ('line', 'meth', 1), ('return', 'meth', None), - ('jump', 'func', 5, '[offset=120]'), - ('branch', 'func', '[offset=124]', '[offset=130]'), + ('jump', 'func', 5, '[offset=122]'), + ('branch', 'func', '[offset=126]', '[offset=132]'), ('return', 'func', None), ('line', 'get_events', 11)]) @@ -1648,8 +1982,8 @@ def foo(n=0): n += 1 return None - in_loop = ('branch left', 'foo', 10, 16) - exit_loop = ('branch right', 'foo', 10, 40) + in_loop = ('branch left', 'foo', 12, 18) + exit_loop = ('branch right', 'foo', 12, 42) self.check_events(foo, recorders = BRANCH_OFFSET_RECORDERS, expected = [ in_loop, in_loop, @@ -1709,6 +2043,27 @@ def func(v=1): ('branch right', 'func', 6, 8), ('branch right', 'func', 2, 10)]) + def test_callback_set_frame_lineno(self): + def func(s: str) -> int: + if s.startswith("t"): + return 1 + else: + return 0 + + def callback(code, from_, to): + # try set frame.f_lineno + frame = inspect.currentframe() + while frame and frame.f_code is not code: + frame = frame.f_back + + self.assertIsNotNone(frame) + frame.f_lineno = frame.f_lineno + 1 # run next instruction + + sys.monitoring.set_local_events(TEST_TOOL, func.__code__, E.BRANCH_LEFT) + sys.monitoring.register_callback(TEST_TOOL, E.BRANCH_LEFT, callback) + + self.assertEqual(func("true"), 1) + class TestBranchConsistency(MonitoringTestBase, unittest.TestCase): diff --git a/Lib/test/test_msvcrt.py b/Lib/test/test_msvcrt.py index 1c6905bd1ee586..fef86ce323e54d 100644 --- a/Lib/test/test_msvcrt.py +++ b/Lib/test/test_msvcrt.py @@ -4,6 +4,7 @@ import unittest from textwrap import dedent +from test import support from test.support import os_helper, requires_resource from test.support.os_helper import TESTFN, TESTFN_ASCII @@ -67,8 +68,12 @@ def run_in_separated_process(self, code): # Run test in a separated process to avoid stdin conflicts. # See: gh-110147 cmd = [sys.executable, '-c', code] - subprocess.run(cmd, check=True, capture_output=True, - creationflags=subprocess.CREATE_NEW_CONSOLE) + try: + subprocess.run(cmd, check=True, capture_output=True, + creationflags=subprocess.CREATE_NEW_CONSOLE) + except subprocess.CalledProcessError as exc: + support.skip_on_low_desktop_heap_memory_subprocess(exc.returncode) + raise def test_kbhit(self): code = dedent(''' diff --git a/Lib/test/test_multiprocessing_forkserver/__init__.py b/Lib/test/test_multiprocessing_forkserver/__init__.py index d91715a344dfa7..c58375e2861c62 100644 --- a/Lib/test/test_multiprocessing_forkserver/__init__.py +++ b/Lib/test/test_multiprocessing_forkserver/__init__.py @@ -6,8 +6,11 @@ if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") -if sys.platform == "win32": +if sys.platform in ("win32", "cygwin"): raise unittest.SkipTest("forkserver is not available on Windows") +if not support.has_fork_support: + raise unittest.SkipTest("requires working os.fork()") + def load_tests(*args): return support.load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_multiprocessing_forkserver/test_preload.py b/Lib/test/test_multiprocessing_forkserver/test_preload.py new file mode 100644 index 00000000000000..f8e119aa367637 --- /dev/null +++ b/Lib/test/test_multiprocessing_forkserver/test_preload.py @@ -0,0 +1,230 @@ +"""Tests for forkserver preload functionality.""" + +import contextlib +import multiprocessing +import os +import shutil +import sys +import tempfile +import unittest +from multiprocessing import forkserver, spawn + + +class TestForkserverPreload(unittest.TestCase): + """Tests for forkserver preload functionality.""" + + def setUp(self): + self._saved_warnoptions = sys.warnoptions.copy() + # Remove warning options that would convert ImportWarning to errors: + # - 'error' converts all warnings to errors + # - 'error::ImportWarning' specifically converts ImportWarning + # Keep other specific options like 'error::BytesWarning' that + # subprocess's _args_from_interpreter_flags() expects to remove + sys.warnoptions[:] = [ + opt for opt in sys.warnoptions + if opt not in ('error', 'error::ImportWarning') + ] + self.ctx = multiprocessing.get_context('forkserver') + forkserver._forkserver._stop() + + def tearDown(self): + sys.warnoptions[:] = self._saved_warnoptions + forkserver._forkserver._stop() + + @staticmethod + def _send_value(conn, value): + """Send value through connection. Static method to be picklable as Process target.""" + conn.send(value) + + @contextlib.contextmanager + def capture_forkserver_stderr(self): + """Capture stderr from forkserver by preloading a module that redirects it. + + Yields (module_name, capture_file_path). The capture file can be read + after the forkserver has processed preloads. This works because + forkserver.main() calls util._flush_std_streams() after preloading, + ensuring captured output is written before we read it. + """ + tmpdir = tempfile.mkdtemp() + capture_module = os.path.join(tmpdir, '_capture_stderr.py') + capture_file = os.path.join(tmpdir, 'stderr.txt') + try: + with open(capture_module, 'w') as f: + # Use line buffering (buffering=1) to ensure warnings are written. + # Enable ImportWarning since it's ignored by default. + f.write( + f'import sys, warnings; ' + f'sys.stderr = open({capture_file!r}, "w", buffering=1); ' + f'warnings.filterwarnings("always", category=ImportWarning)\n' + ) + sys.path.insert(0, tmpdir) + yield '_capture_stderr', capture_file + finally: + sys.path.remove(tmpdir) + shutil.rmtree(tmpdir, ignore_errors=True) + + def test_preload_on_error_ignore_default(self): + """Test that invalid modules are silently ignored by default.""" + self.ctx.set_forkserver_preload(['nonexistent_module_xyz']) + + r, w = self.ctx.Pipe(duplex=False) + p = self.ctx.Process(target=self._send_value, args=(w, 42)) + p.start() + w.close() + result = r.recv() + r.close() + p.join() + + self.assertEqual(result, 42) + self.assertEqual(p.exitcode, 0) + + def test_preload_on_error_ignore_explicit(self): + """Test that invalid modules are silently ignored with on_error='ignore'.""" + self.ctx.set_forkserver_preload(['nonexistent_module_xyz'], on_error='ignore') + + r, w = self.ctx.Pipe(duplex=False) + p = self.ctx.Process(target=self._send_value, args=(w, 99)) + p.start() + w.close() + result = r.recv() + r.close() + p.join() + + self.assertEqual(result, 99) + self.assertEqual(p.exitcode, 0) + + def test_preload_on_error_warn(self): + """Test that invalid modules emit warnings with on_error='warn'.""" + with self.capture_forkserver_stderr() as (capture_mod, stderr_file): + self.ctx.set_forkserver_preload( + [capture_mod, 'nonexistent_module_xyz'], on_error='warn') + + r, w = self.ctx.Pipe(duplex=False) + p = self.ctx.Process(target=self._send_value, args=(w, 123)) + p.start() + w.close() + result = r.recv() + r.close() + p.join() + + self.assertEqual(result, 123) + self.assertEqual(p.exitcode, 0) + + with open(stderr_file) as f: + stderr_output = f.read() + self.assertIn('nonexistent_module_xyz', stderr_output) + self.assertIn('ImportWarning', stderr_output) + + def test_preload_on_error_fail_breaks_context(self): + """Test that invalid modules with on_error='fail' breaks the forkserver.""" + with self.capture_forkserver_stderr() as (capture_mod, stderr_file): + self.ctx.set_forkserver_preload( + [capture_mod, 'nonexistent_module_xyz'], on_error='fail') + + r, w = self.ctx.Pipe(duplex=False) + try: + p = self.ctx.Process(target=self._send_value, args=(w, 42)) + with self.assertRaises((EOFError, ConnectionError, BrokenPipeError)) as cm: + p.start() + notes = getattr(cm.exception, '__notes__', []) + self.assertTrue(notes, "Expected exception to have __notes__") + self.assertIn('Forkserver process may have crashed', notes[0]) + + with open(stderr_file) as f: + stderr_output = f.read() + self.assertIn('nonexistent_module_xyz', stderr_output) + self.assertIn('ModuleNotFoundError', stderr_output) + finally: + w.close() + r.close() + + def test_preload_valid_modules_with_on_error_fail(self): + """Test that valid modules work fine with on_error='fail'.""" + self.ctx.set_forkserver_preload(['os', 'sys'], on_error='fail') + + r, w = self.ctx.Pipe(duplex=False) + p = self.ctx.Process(target=self._send_value, args=(w, 'success')) + p.start() + w.close() + result = r.recv() + r.close() + p.join() + + self.assertEqual(result, 'success') + self.assertEqual(p.exitcode, 0) + + def test_preload_invalid_on_error_value(self): + """Test that invalid on_error values raise ValueError.""" + with self.assertRaises(ValueError) as cm: + self.ctx.set_forkserver_preload(['os'], on_error='invalid') + self.assertIn("on_error must be 'ignore', 'warn', or 'fail'", str(cm.exception)) + + +class TestHandlePreload(unittest.TestCase): + """Unit tests for _handle_preload() function.""" + + def setUp(self): + self._saved_main = sys.modules['__main__'] + + def tearDown(self): + spawn.old_main_modules.clear() + sys.modules['__main__'] = self._saved_main + + def test_handle_preload_main_on_error_fail(self): + """Test that __main__ import failures raise with on_error='fail'.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.py') as f: + f.write('raise RuntimeError("test error in __main__")\n') + f.flush() + with self.assertRaises(RuntimeError) as cm: + forkserver._handle_preload(['__main__'], main_path=f.name, on_error='fail') + self.assertIn("test error in __main__", str(cm.exception)) + + def test_handle_preload_main_on_error_warn(self): + """Test that __main__ import failures warn with on_error='warn'.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.py') as f: + f.write('raise ImportError("test import error")\n') + f.flush() + with self.assertWarns(ImportWarning) as cm: + forkserver._handle_preload(['__main__'], main_path=f.name, on_error='warn') + self.assertIn("Failed to preload __main__", str(cm.warning)) + self.assertIn("test import error", str(cm.warning)) + + def test_handle_preload_main_on_error_ignore(self): + """Test that __main__ import failures are ignored with on_error='ignore'.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.py') as f: + f.write('raise ImportError("test import error")\n') + f.flush() + forkserver._handle_preload(['__main__'], main_path=f.name, on_error='ignore') + + def test_handle_preload_main_valid(self): + """Test that valid __main__ preload works.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.py') as f: + f.write('test_var = 42\n') + f.flush() + forkserver._handle_preload(['__main__'], main_path=f.name, on_error='fail') + + def test_handle_preload_module_on_error_fail(self): + """Test that module import failures raise with on_error='fail'.""" + with self.assertRaises(ModuleNotFoundError): + forkserver._handle_preload(['nonexistent_test_module_xyz'], on_error='fail') + + def test_handle_preload_module_on_error_warn(self): + """Test that module import failures warn with on_error='warn'.""" + with self.assertWarns(ImportWarning) as cm: + forkserver._handle_preload(['nonexistent_test_module_xyz'], on_error='warn') + self.assertIn("Failed to preload module", str(cm.warning)) + + def test_handle_preload_module_on_error_ignore(self): + """Test that module import failures are ignored with on_error='ignore'.""" + forkserver._handle_preload(['nonexistent_test_module_xyz'], on_error='ignore') + + def test_handle_preload_combined(self): + """Test preloading both __main__ and modules.""" + with tempfile.NamedTemporaryFile(mode='w', suffix='.py') as f: + f.write('import sys\n') + f.flush() + forkserver._handle_preload(['__main__', 'os', 'sys'], main_path=f.name, on_error='fail') + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_multiprocessing_main_handling.py b/Lib/test/test_multiprocessing_main_handling.py index 6b30a89316703b..fbd44fb0d3d5eb 100644 --- a/Lib/test/test_multiprocessing_main_handling.py +++ b/Lib/test/test_multiprocessing_main_handling.py @@ -196,9 +196,8 @@ def test_ipython_workaround(self): def test_script_compiled(self): with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, 'script') - py_compile.compile(script_name, doraise=True) + pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True) os.remove(script_name) - pyc_file = import_helper.make_legacy_pyc(script_name) self._check_script(pyc_file) def test_directory(self): @@ -213,9 +212,8 @@ def test_directory_compiled(self): with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__', source=source) - py_compile.compile(script_name, doraise=True) + pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True) os.remove(script_name) - pyc_file = import_helper.make_legacy_pyc(script_name) self._check_script(script_dir) def test_zipfile(self): @@ -231,7 +229,8 @@ def test_zipfile_compiled(self): with os_helper.temp_dir() as script_dir: script_name = _make_test_script(script_dir, '__main__', source=source) - compiled_name = py_compile.compile(script_name, doraise=True) + compiled_name = script_name + 'c' + py_compile.compile(script_name, compiled_name, doraise=True) zip_name, run_name = make_zip_script(script_dir, 'test_zip', compiled_name) self._check_script(zip_name) @@ -273,9 +272,8 @@ def test_package_compiled(self): make_pkg(pkg_dir) script_name = _make_test_script(pkg_dir, '__main__', source=source) - compiled_name = py_compile.compile(script_name, doraise=True) + pyc_file = import_helper.make_legacy_pyc(script_name, allow_compile=True) os.remove(script_name) - pyc_file = import_helper.make_legacy_pyc(script_name) launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg') self._check_script(launch_name) diff --git a/Lib/test/test_netrc.py b/Lib/test/test_netrc.py index 9d720f627102e3..354081e96213a6 100644 --- a/Lib/test/test_netrc.py +++ b/Lib/test/test_netrc.py @@ -1,6 +1,9 @@ import netrc, os, unittest, sys, textwrap +from pathlib import Path from test import support from test.support import os_helper +from unittest.mock import patch + temp_filename = os_helper.TESTFN @@ -309,6 +312,26 @@ def test_security(self): self.assertEqual(nrc.hosts['foo.domain.com'], ('anonymous', '', 'pass')) + @unittest.skipUnless(os.name == 'posix', 'POSIX only test') + @unittest.skipUnless(hasattr(os, 'getuid'), "os.getuid is required") + @os_helper.skip_unless_working_chmod + def test_security_only_once(self): + # Make sure security check is only run once per parse when multiple + # entries are found. + with patch.object(netrc.netrc, "_security_check") as mock: + with os_helper.temp_dir() as tmp_dir: + netrc_path = Path(tmp_dir) / '.netrc' + netrc_path.write_text("""\ + machine foo.domain.com login bar password pass + machine bar.domain.com login foo password pass + """) + netrc_path.chmod(0o600) + with os_helper.EnvironmentVarGuard() as environ: + environ.set('HOME', tmp_dir) + netrc.netrc() + + mock.assert_called_once() + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 22f6403d482bc4..a3728b58335e63 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1,3 +1,4 @@ +import errno import inspect import ntpath import os @@ -6,9 +7,10 @@ import sys import unittest import warnings -from ntpath import ALLOW_MISSING +from ntpath import ALL_BUT_LAST, ALLOW_MISSING from test import support -from test.support import TestFailed, cpython_only, os_helper +from test.support import os_helper +from test.support import warnings_helper from test.support.os_helper import FakePath from test import test_genericpath from tempfile import TemporaryFile @@ -58,7 +60,7 @@ def tester(fn, wantResult): fn = fn.replace("\\", "\\\\") gotResult = eval(fn) if wantResult != gotResult and _norm(wantResult) != _norm(gotResult): - raise TestFailed("%s should return: %s but returned: %s" \ + raise support.TestFailed("%s should return: %s but returned: %s" \ %(str(fn), str(wantResult), str(gotResult))) # then with bytes @@ -74,7 +76,7 @@ def tester(fn, wantResult): warnings.simplefilter("ignore", DeprecationWarning) gotResult = eval(fn) if _norm(wantResult) != _norm(gotResult): - raise TestFailed("%s should return: %s but returned: %s" \ + raise support.TestFailed("%s should return: %s but returned: %s" \ %(str(fn), str(wantResult), repr(gotResult))) @@ -297,6 +299,10 @@ def test_isabs(self): tester('ntpath.isabs("\\\\.\\C:")', 1) def test_commonprefix(self): + with warnings_helper.check_warnings((".*commonpath().*", DeprecationWarning)): + self.do_test_commonprefix() + + def do_test_commonprefix(self): tester('ntpath.commonprefix(["/home/swenson/spam", "/home/swen/spam"])', "/home/swen") tester('ntpath.commonprefix(["\\home\\swen\\spam", "\\home\\swen\\eggs"])', @@ -587,59 +593,63 @@ def test_realpath_invalid_paths(self): # gh-106242: Embedded nulls and non-strict fallback to abspath self.assertEqual(realpath(path, strict=False), path) # gh-106242: Embedded nulls should raise OSError (not ValueError) + self.assertRaises(OSError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(OSError, realpath, path, strict=True) self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) path = ABSTFNb + b'\x00' self.assertEqual(realpath(path, strict=False), path) + self.assertRaises(OSError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(OSError, realpath, path, strict=True) self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) path = ABSTFN + '\\nonexistent\\x\x00' self.assertEqual(realpath(path, strict=False), path) + self.assertRaises(OSError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(OSError, realpath, path, strict=True) self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) path = ABSTFNb + b'\\nonexistent\\x\x00' self.assertEqual(realpath(path, strict=False), path) + self.assertRaises(OSError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(OSError, realpath, path, strict=True) self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) path = ABSTFN + '\x00\\..' self.assertEqual(realpath(path, strict=False), os.getcwd()) + self.assertEqual(realpath(path, strict=ALL_BUT_LAST), os.getcwd()) self.assertEqual(realpath(path, strict=True), os.getcwd()) self.assertEqual(realpath(path, strict=ALLOW_MISSING), os.getcwd()) path = ABSTFNb + b'\x00\\..' self.assertEqual(realpath(path, strict=False), os.getcwdb()) + self.assertEqual(realpath(path, strict=ALL_BUT_LAST), os.getcwdb()) self.assertEqual(realpath(path, strict=True), os.getcwdb()) self.assertEqual(realpath(path, strict=ALLOW_MISSING), os.getcwdb()) path = ABSTFN + '\\nonexistent\\x\x00\\..' self.assertEqual(realpath(path, strict=False), ABSTFN + '\\nonexistent') + self.assertRaises(OSError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(OSError, realpath, path, strict=True) self.assertEqual(realpath(path, strict=ALLOW_MISSING), ABSTFN + '\\nonexistent') path = ABSTFNb + b'\\nonexistent\\x\x00\\..' self.assertEqual(realpath(path, strict=False), ABSTFNb + b'\\nonexistent') + self.assertRaises(OSError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(OSError, realpath, path, strict=True) self.assertEqual(realpath(path, strict=ALLOW_MISSING), ABSTFNb + b'\\nonexistent') @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') - @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_invalid_unicode_paths(self, kwargs): realpath = ntpath.realpath ABSTFN = ntpath.abspath(os_helper.TESTFN) ABSTFNb = os.fsencode(ABSTFN) path = ABSTFNb + b'\xff' self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) - self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) path = ABSTFNb + b'\\nonexistent\\\xff' self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) - self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) path = ABSTFNb + b'\xff\\..' self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) - self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) path = ABSTFNb + b'\\nonexistent\\\xff\\..' self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) - self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) @os_helper.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') - @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_relative(self, kwargs): ABSTFN = ntpath.abspath(os_helper.TESTFN) open(ABSTFN, "wb").close() @@ -766,34 +776,53 @@ def test_realpath_symlink_loops_strict(self): self.addCleanup(os_helper.unlink, ABSTFN + "a") os.symlink(ABSTFN, ABSTFN) + self.assertRaises(OSError, ntpath.realpath, ABSTFN, strict=ALL_BUT_LAST) self.assertRaises(OSError, ntpath.realpath, ABSTFN, strict=True) os.symlink(ABSTFN + "1", ABSTFN + "2") os.symlink(ABSTFN + "2", ABSTFN + "1") + self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1", strict=ALL_BUT_LAST) self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1", strict=True) + self.assertRaises(OSError, ntpath.realpath, ABSTFN + "2", strict=ALL_BUT_LAST) self.assertRaises(OSError, ntpath.realpath, ABSTFN + "2", strict=True) + self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\x", strict=ALL_BUT_LAST) self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\x", strict=True) # Windows eliminates '..' components before resolving links, so the # following call is not expected to raise. + self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..", strict=ALL_BUT_LAST), + ntpath.dirname(ABSTFN)) self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..", strict=True), ntpath.dirname(ABSTFN)) + self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\x", strict=ALL_BUT_LAST), + ntpath.dirname(ABSTFN) + "\\x") self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\..\\x", strict=True) os.symlink(ABSTFN + "x", ABSTFN + "y") + self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\" + + ntpath.basename(ABSTFN) + "y", + strict=ALL_BUT_LAST), + ABSTFN + "x") self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\..\\" + ntpath.basename(ABSTFN) + "y", strict=True) + self.assertRaises(OSError, ntpath.realpath, + ABSTFN + "1\\..\\" + ntpath.basename(ABSTFN) + "1", + strict=ALL_BUT_LAST) self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\..\\" + ntpath.basename(ABSTFN) + "1", strict=True) os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a") + self.assertRaises(OSError, ntpath.realpath, ABSTFN + "a", strict=ALL_BUT_LAST) self.assertRaises(OSError, ntpath.realpath, ABSTFN + "a", strict=True) os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN)) + "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c") + self.assertRaises(OSError, ntpath.realpath, ABSTFN + "c", strict=ALL_BUT_LAST) self.assertRaises(OSError, ntpath.realpath, ABSTFN + "c", strict=True) # Test using relative path as well. + self.assertRaises(OSError, ntpath.realpath, ntpath.basename(ABSTFN), + strict=ALL_BUT_LAST) self.assertRaises(OSError, ntpath.realpath, ntpath.basename(ABSTFN), strict=True) @@ -853,7 +882,7 @@ def test_realpath_symlink_loops_raise(self): @os_helper.skip_unless_symlink @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') - @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_symlink_prefix(self, kwargs): ABSTFN = ntpath.abspath(os_helper.TESTFN) self.addCleanup(os_helper.unlink, ABSTFN + "3") @@ -891,6 +920,7 @@ def test_realpath_nul(self): tester("ntpath.realpath('NUL')", r'\\.\NUL') tester("ntpath.realpath('NUL', strict=False)", r'\\.\NUL') tester("ntpath.realpath('NUL', strict=True)", r'\\.\NUL') + tester("ntpath.realpath('NUL', strict=ALL_BUT_LAST)", r'\\.\NUL') tester("ntpath.realpath('NUL', strict=ALLOW_MISSING)", r'\\.\NUL') @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') @@ -915,7 +945,7 @@ def test_realpath_cwd(self): self.assertPathEqual(test_file_long, ntpath.realpath(test_file_short)) - for kwargs in {}, {'strict': True}, {'strict': ALLOW_MISSING}: + for kwargs in {}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}: with self.subTest(**kwargs): with os_helper.change_cwd(test_dir_long): self.assertPathEqual( @@ -975,6 +1005,93 @@ def test_realpath_permission(self): self.assertPathEqual(test_file, ntpath.realpath(test_file_short)) + @os_helper.skip_unless_symlink + @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') + def test_realpath_mode(self): + realpath = ntpath.realpath + ABSTFN = ntpath.abspath(os_helper.TESTFN) + self.addCleanup(os_helper.rmdir, ABSTFN) + self.addCleanup(os_helper.rmdir, ABSTFN + "/dir") + self.addCleanup(os_helper.unlink, ABSTFN + "/file") + self.addCleanup(os_helper.unlink, ABSTFN + "/dir/file2") + self.addCleanup(os_helper.unlink, ABSTFN + "/link") + self.addCleanup(os_helper.unlink, ABSTFN + "/link2") + self.addCleanup(os_helper.unlink, ABSTFN + "/broken") + self.addCleanup(os_helper.unlink, ABSTFN + "/cycle") + + os.mkdir(ABSTFN) + os.mkdir(ABSTFN + "\\dir") + open(ABSTFN + "\\file", "wb").close() + open(ABSTFN + "\\dir\\file2", "wb").close() + os.symlink("file", ABSTFN + "\\link") + os.symlink("dir", ABSTFN + "\\link2") + os.symlink("nonexistent", ABSTFN + "\\broken") + os.symlink("cycle", ABSTFN + "\\cycle") + def check(path, modes, expected, errno=None): + path = path.replace('/', '\\') + if isinstance(expected, str): + assert errno is None + expected = expected.replace('/', os.sep) + for mode in modes: + with self.subTest(mode=mode): + self.assertEqual(realpath(path, strict=mode), + ABSTFN + expected) + else: + for mode in modes: + with self.subTest(mode=mode): + with self.assertRaises(expected) as cm: + realpath(path, strict=mode) + if errno is not None: + self.assertEqual(cm.exception.errno, errno) + + self.enterContext(os_helper.change_cwd(ABSTFN)) + all_modes = [False, ALLOW_MISSING, ALL_BUT_LAST, True] + check("file", all_modes, "/file") + check("file/", all_modes, "/file") + check("file/file2", [False, ALLOW_MISSING], "/file/file2") + check("file/file2", [ALL_BUT_LAST, True], FileNotFoundError) + check("file/.", all_modes, "/file") + check("file/../link2", all_modes, "/dir") + + check("dir", all_modes, "/dir") + check("dir/", all_modes, "/dir") + check("dir/file2", all_modes, "/dir/file2") + + check("link", all_modes, "/file") + check("link/", all_modes, "/file") + check("link/file2", [False, ALLOW_MISSING], "/file/file2") + check("link/file2", [ALL_BUT_LAST, True], FileNotFoundError) + check("link/.", all_modes, "/file") + check("link/../link", all_modes, "/file") + + check("link2", all_modes, "/dir") + check("link2/", all_modes, "/dir") + check("link2/file2", all_modes, "/dir/file2") + + check("nonexistent", [False, ALLOW_MISSING, ALL_BUT_LAST], "/nonexistent") + check("nonexistent", [True], FileNotFoundError) + check("nonexistent/", [False, ALLOW_MISSING, ALL_BUT_LAST], "/nonexistent") + check("nonexistent/", [True], FileNotFoundError) + check("nonexistent/file", [False, ALLOW_MISSING], "/nonexistent/file") + check("nonexistent/file", [ALL_BUT_LAST, True], FileNotFoundError) + check("nonexistent/../link", all_modes, "/file") + + check("broken", [False, ALLOW_MISSING, ALL_BUT_LAST], "/nonexistent") + check("broken", [True], FileNotFoundError) + check("broken/", [False, ALLOW_MISSING, ALL_BUT_LAST], "/nonexistent") + check("broken/", [True], FileNotFoundError) + check("broken/file", [False, ALLOW_MISSING], "/nonexistent/file") + check("broken/file", [ALL_BUT_LAST, True], FileNotFoundError) + check("broken/../link", all_modes, "/file") + + check("cycle", [False], "/cycle") + check("cycle", [ALLOW_MISSING, ALL_BUT_LAST, True], OSError, errno.EINVAL) + check("cycle/", [False], "/cycle") + check("cycle/", [ALLOW_MISSING, ALL_BUT_LAST, True], OSError, errno.EINVAL) + check("cycle/file", [False], "/cycle/file") + check("cycle/file", [ALLOW_MISSING, ALL_BUT_LAST, True], OSError, errno.EINVAL) + check("cycle/../link", all_modes, "/file") + def test_expandvars(self): with os_helper.EnvironmentVarGuard() as env: env.clear() @@ -1021,6 +1138,19 @@ def check(value, expected): check('%spam%bar', '%sbar' % nonascii) check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii) + @support.requires_resource('cpu') + def test_expandvars_large(self): + expandvars = ntpath.expandvars + with os_helper.EnvironmentVarGuard() as env: + env.clear() + env["A"] = "B" + n = 100_000 + self.assertEqual(expandvars('%A%'*n), 'B'*n) + self.assertEqual(expandvars('%A%A'*n), 'BA'*n) + self.assertEqual(expandvars("''"*n + '%%'), "''"*n + '%') + self.assertEqual(expandvars("%%"*n), "%"*n) + self.assertEqual(expandvars("$$"*n), "$"*n) + def test_expanduser(self): tester('ntpath.expanduser("test")', 'test') @@ -1438,7 +1568,7 @@ def test_con_device(self): self.assertTrue(os.path.exists(r"\\.\CON")) @unittest.skipIf(sys.platform != 'win32', "Fast paths are only for win32") - @cpython_only + @support.cpython_only def test_fast_paths_in_use(self): # There are fast paths of these functions implemented in posixmodule.c. # Confirm that they are being used, and not the Python fallbacks in diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index 30baa09048616c..7946550ec0db63 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1,3 +1,4 @@ +import collections import copy import pickle import dis @@ -5,7 +6,7 @@ import types import unittest from test.support import (threading_helper, check_impl_detail, - requires_specialization, requires_specialization_ft, + requires_specialization, cpython_only, requires_jit_disabled, reset_code) from test.support.import_helper import import_module @@ -524,7 +525,7 @@ def f(x, y): f() @requires_jit_disabled - @requires_specialization_ft + @requires_specialization def test_assign_init_code(self): class MyClass: def __init__(self): @@ -547,7 +548,7 @@ def count_args(self, *args): instantiate() @requires_jit_disabled - @requires_specialization_ft + @requires_specialization def test_push_init_frame_fails(self): def instantiate(): return InitTakesArg() @@ -567,6 +568,47 @@ def test(default=None): with self.assertRaises(RecursionError): test() + def test_dont_specialize_custom_vectorcall(self): + def f(): + raise Exception("no way") + + _testinternalcapi.set_vectorcall_nop(f) + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + f() + + @requires_jit_disabled + @requires_specialization + def test_specialize_call_function_ex_py(self): + def ex_py(*args, **kwargs): + return 1 + + def instantiate(): + args = (1, 2, 3) + kwargs = {} + return ex_py(*args, **kwargs) + + # Trigger specialization + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + instantiate() + self.assert_specialized(instantiate, "CALL_EX_PY") + + @requires_jit_disabled + @requires_specialization + def test_specialize_call_function_ex_py_fail(self): + def ex_py(*args, **kwargs): + return 1 + + def instantiate(): + args = (1, 2, 3) + kwargs = {} + return ex_py(*args, **kwargs) + + _testinternalcapi.set_vectorcall_nop(ex_py) + # Trigger specialization + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + instantiate() + self.assert_no_opcode(instantiate, "CALL_EX_PY") + self.assert_specialized(instantiate, "CALL_EX_NON_PY_GENERAL") def make_deferred_ref_count_obj(): """Create an object that uses deferred reference counting. @@ -582,7 +624,7 @@ def make_deferred_ref_count_obj(): class TestRacesDoNotCrash(TestBase): # Careful with these. Bigger numbers have a higher chance of catching bugs, # but you can also burn through a *ton* of type/dict/function versions: - ITEMS = 1000 + ITEMS = 1400 LOOPS = 4 WRITERS = 2 @@ -619,7 +661,7 @@ def assert_races_do_not_crash( for writer in writers: writer.join() - @requires_specialization_ft + @requires_specialization def test_binary_subscr_getitem(self): def get_items(): class C: @@ -649,7 +691,7 @@ def write(items): opname = "BINARY_OP_SUBSCR_GETITEM" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_binary_subscr_list_int(self): def get_items(): items = [] @@ -727,7 +769,7 @@ def write(items): opname = "FOR_ITER_LIST" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_class(self): def get_items(): class C: @@ -757,7 +799,7 @@ def write(items): opname = "LOAD_ATTR_CLASS" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_class_with_metaclass_check(self): def get_items(): class Meta(type): @@ -790,7 +832,7 @@ def write(items): opname = "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_getattribute_overridden(self): def get_items(): class C: @@ -820,7 +862,7 @@ def write(items): opname = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_instance_value(self): def get_items(): class C: @@ -844,7 +886,7 @@ def write(items): opname = "LOAD_ATTR_INSTANCE_VALUE" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_method_lazy_dict(self): def get_items(): class C(Exception): @@ -874,7 +916,7 @@ def write(items): opname = "LOAD_ATTR_METHOD_LAZY_DICT" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_method_no_dict(self): def get_items(): class C: @@ -905,7 +947,7 @@ def write(items): opname = "LOAD_ATTR_METHOD_NO_DICT" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_method_with_values(self): def get_items(): class C: @@ -935,7 +977,7 @@ def write(items): opname = "LOAD_ATTR_METHOD_WITH_VALUES" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_module(self): def get_items(): items = [] @@ -960,7 +1002,7 @@ def write(items): opname = "LOAD_ATTR_MODULE" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_property(self): def get_items(): class C: @@ -990,7 +1032,7 @@ def write(items): opname = "LOAD_ATTR_PROPERTY" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_slot(self): def get_items(): class C: @@ -1017,7 +1059,7 @@ def write(items): opname = "LOAD_ATTR_SLOT" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_attr_with_hint(self): def get_items(): class C: @@ -1044,7 +1086,7 @@ def write(items): opname = "LOAD_ATTR_WITH_HINT" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_load_global_module(self): if not have_dict_key_versions(): raise unittest.SkipTest("Low on dict key versions") @@ -1117,7 +1159,7 @@ def write(items): opname = "STORE_ATTR_WITH_HINT" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_store_subscr_list_int(self): def get_items(): items = [] @@ -1141,7 +1183,7 @@ def write(items): opname = "STORE_SUBSCR_LIST_INT" self.assert_races_do_not_crash(opname, get_items, read, write) - @requires_specialization_ft + @requires_specialization def test_unpack_sequence_list(self): def get_items(): items = [] @@ -1321,7 +1363,7 @@ def f(o, n): class TestSpecializer(TestBase): @cpython_only - @requires_specialization_ft + @requires_specialization def test_binary_op(self): def binary_op_add_int(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): @@ -1333,6 +1375,21 @@ def binary_op_add_int(): self.assert_specialized(binary_op_add_int, "BINARY_OP_ADD_INT") self.assert_no_opcode(binary_op_add_int, "BINARY_OP") + def binary_op_int_non_compact(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a, b = 10000000000, 1 + c = a + b + self.assertEqual(c, 10000000001) + c = a - b + self.assertEqual(c, 9999999999) + c = a * b + self.assertEqual(c, 10000000000) + + binary_op_int_non_compact() + self.assert_no_opcode(binary_op_int_non_compact, "BINARY_OP_ADD_INT") + self.assert_no_opcode(binary_op_int_non_compact, "BINARY_OP_SUBTRACT_INT") + self.assert_no_opcode(binary_op_int_non_compact, "BINARY_OP_MULTIPLY_INT") + def binary_op_add_unicode(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): a, b = "foo", "bar" @@ -1362,11 +1419,50 @@ def binary_op_add_extend(): self.assertEqual(c, 2.0) c = b / a self.assertEqual(c, 0.5) + c = a + c += b + self.assertEqual(c, 9.0) + c = b + c += a + self.assertEqual(c, 9.0) + c = a + c -= b + self.assertEqual(c, 3.0) + c = b + c -= a + self.assertEqual(c, -3.0) + c = a + c *= b + self.assertEqual(c, 18.0) + c = b + c *= a + self.assertEqual(c, 18.0) + c = a + c /= b + self.assertEqual(c, 2.0) + c = b + c /= a + self.assertEqual(c, 0.5) binary_op_add_extend() self.assert_specialized(binary_op_add_extend, "BINARY_OP_EXTEND") self.assert_no_opcode(binary_op_add_extend, "BINARY_OP") + def binary_op_add_extend_sequences(): + l1 = [1, 2] + l2 = [None] + t1 = (1, 2) + t2 = (None,) + for _ in range(100): + list_sum = l1 + l2 + self.assertEqual(list_sum, [1, 2, None]) + tuple_sum = t1 + t2 + self.assertEqual(tuple_sum, (1, 2, None)) + + binary_op_add_extend_sequences() + self.assert_specialized(binary_op_add_extend_sequences, "BINARY_OP_EXTEND") + self.assert_no_opcode(binary_op_add_extend_sequences, "BINARY_OP") + def binary_op_zero_division(): def compactlong_lhs(arg): 42 / arg @@ -1441,7 +1537,7 @@ def binary_op_bitwise_extend(): self.assert_no_opcode(binary_op_bitwise_extend, "BINARY_OP") @cpython_only - @requires_specialization_ft + @requires_specialization def test_load_super_attr(self): """Ensure that LOAD_SUPER_ATTR is specialized as expected.""" @@ -1480,7 +1576,7 @@ def init(self): self.assert_no_opcode(A.__init__, "LOAD_SUPER_ATTR_METHOD") @cpython_only - @requires_specialization_ft + @requires_specialization def test_contain_op(self): def contains_op_dict(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): @@ -1492,6 +1588,27 @@ def contains_op_dict(): self.assert_specialized(contains_op_dict, "CONTAINS_OP_DICT") self.assert_no_opcode(contains_op_dict, "CONTAINS_OP") + def contains_op_frozen_dict(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a, b = 1, frozendict({1: 2, 2: 5}) + self.assertTrue(a in b) + self.assertFalse(3 in b) + + contains_op_frozen_dict() + self.assert_specialized(contains_op_frozen_dict, "CONTAINS_OP_DICT") + self.assert_no_opcode(contains_op_frozen_dict, "CONTAINS_OP") + + def contains_op_frozen_dict_subclass(): + class MyFrozenDict(frozendict): + pass + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a, b = 1, MyFrozenDict({1: 2, 2: 5}) + self.assertTrue(a in b) + self.assertFalse(3 in b) + + contains_op_frozen_dict_subclass() + self.assert_no_opcode(contains_op_frozen_dict_subclass, "CONTAINS_OP_DICT") + def contains_op_set(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): a, b = 1, {1, 2} @@ -1503,7 +1620,7 @@ def contains_op_set(): self.assert_no_opcode(contains_op_set, "CONTAINS_OP") @cpython_only - @requires_specialization_ft + @requires_specialization def test_send_with(self): def run_async(coro): while True: @@ -1530,7 +1647,7 @@ async def send_with(): self.assert_specialized(send_with, "SEND_GEN") @cpython_only - @requires_specialization_ft + @requires_specialization def test_send_yield_from(self): def g(): yield None @@ -1545,7 +1662,38 @@ def send_yield_from(): self.assert_no_opcode(send_yield_from, "SEND") @cpython_only - @requires_specialization_ft + @requires_specialization + def test_send_yield_from_iter(self): + L = list(range(100)) + def send_yield_from(): + yield from L + + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + list(send_yield_from()) + + self.assert_specialized(send_yield_from, "SEND_VIRTUAL") + self.assert_no_opcode(send_yield_from, "SEND") + + @cpython_only + @requires_specialization + def test_send_async_for(self): + async def g(): + yield None + + async def send_for(): + async for _ in g(): + break + + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + try: + send_for().send(None) + except StopIteration: + pass + self.assert_specialized(send_for, "SEND_ASYNC_GEN") + self.assert_no_opcode(send_for, "SEND") + + @cpython_only + @requires_specialization def test_store_attr_slot(self): class C: __slots__ = ['x'] @@ -1566,7 +1714,7 @@ def set_slot(n): self.assert_no_opcode(set_slot, "STORE_ATTR_SLOT") @cpython_only - @requires_specialization_ft + @requires_specialization def test_store_attr_instance_value(self): class C: pass @@ -1588,7 +1736,7 @@ def set_value(n): self.assert_no_opcode(set_value, "STORE_ATTR_INSTANCE_VALUE") @cpython_only - @requires_specialization_ft + @requires_specialization def test_store_attr_with_hint(self): class C: pass @@ -1613,7 +1761,7 @@ def set_value(n): self.assert_no_opcode(set_value, "STORE_ATTR_WITH_HINT") @cpython_only - @requires_specialization_ft + @requires_specialization def test_to_bool(self): def to_bool_bool(): true_cnt, false_cnt = 0, 0 @@ -1681,7 +1829,7 @@ def to_bool_str(): self.assert_no_opcode(to_bool_str, "TO_BOOL") @cpython_only - @requires_specialization_ft + @requires_specialization def test_unpack_sequence(self): def unpack_sequence_two_tuple(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): @@ -1718,7 +1866,7 @@ def unpack_sequence_list(): self.assert_no_opcode(unpack_sequence_list, "UNPACK_SEQUENCE") @cpython_only - @requires_specialization_ft + @requires_specialization def test_binary_subscr(self): def binary_subscr_list_int(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): @@ -1752,6 +1900,63 @@ def binary_subscr_dict(): self.assert_specialized(binary_subscr_dict, "BINARY_OP_SUBSCR_DICT") self.assert_no_opcode(binary_subscr_dict, "BINARY_OP") + def binary_subscr_frozen_dict(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = frozendict({1: 2, 2: 3}) + self.assertEqual(a[1], 2) + self.assertEqual(a[2], 3) + + binary_subscr_frozen_dict() + self.assert_specialized(binary_subscr_frozen_dict, "BINARY_OP_SUBSCR_DICT") + self.assert_no_opcode(binary_subscr_frozen_dict, "BINARY_OP") + + def binary_subscr_frozen_dict_subclass(): + class MyFrozenDict(frozendict): + pass + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = MyFrozenDict({1: 2, 2: 3}) + self.assertEqual(a[1], 2) + self.assertEqual(a[2], 3) + + binary_subscr_frozen_dict_subclass() + self.assert_specialized(binary_subscr_frozen_dict_subclass, "BINARY_OP_SUBSCR_DICT") + self.assert_no_opcode(binary_subscr_frozen_dict_subclass, "BINARY_OP") + + def binary_subscr_defaultdict(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = collections.defaultdict(lambda: 42, {1: 2, 2: 3}) + self.assertEqual(a[1], 2) + self.assertEqual(a[2], 3) + self.assertEqual(a[7], 42) + + binary_subscr_defaultdict() + self.assert_specialized(binary_subscr_defaultdict, "BINARY_OP_SUBSCR_DICT") + self.assert_no_opcode(binary_subscr_defaultdict, "BINARY_OP") + + def binary_subscr_counter(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = collections.Counter('abcdeabcdabcaba') + self.assertEqual(a['a'], 5) + self.assertEqual(a['b'], 4) + self.assertEqual(a['m'], 0) + + binary_subscr_counter() + self.assert_specialized(binary_subscr_counter, "BINARY_OP_SUBSCR_DICT") + self.assert_no_opcode(binary_subscr_counter, "BINARY_OP") + + def binary_subscr_dict_subclass_override(): + class MyDict(dict): + def __getitem__(self, key): + return 42 + + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = MyDict() + self.assertEqual(a['a'], 42) + self.assertEqual(a['b'], 42) + + binary_subscr_dict_subclass_override() + self.assert_no_opcode(binary_subscr_dict_subclass_override, "BINARY_OP_SUBSCR_DICT") + def binary_subscr_str_int(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): a = "foobar" @@ -1762,6 +1967,16 @@ def binary_subscr_str_int(): self.assert_specialized(binary_subscr_str_int, "BINARY_OP_SUBSCR_STR_INT") self.assert_no_opcode(binary_subscr_str_int, "BINARY_OP") + def binary_subscr_str_int_non_compact(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = "바이트코드_특수화" + for idx, expected in enumerate(a): + self.assertEqual(a[idx], expected) + + binary_subscr_str_int_non_compact() + self.assert_specialized(binary_subscr_str_int_non_compact, "BINARY_OP_SUBSCR_USTR_INT") + self.assert_no_opcode(binary_subscr_str_int_non_compact, "BINARY_OP_SUBSCR_STR_INT") + def binary_subscr_getitems(): class C: def __init__(self, val): @@ -1778,7 +1993,54 @@ def __getitem__(self, item): self.assert_no_opcode(binary_subscr_getitems, "BINARY_OP") @cpython_only - @requires_specialization_ft + @requires_specialization + def test_store_subscr(self): + def store_subscr_dict(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = {1: 2, 2: 3} + a[1] = 4 + self.assertEqual(a[1], 4) + + store_subscr_dict() + self.assert_specialized(store_subscr_dict, "STORE_SUBSCR_DICT") + self.assert_no_opcode(store_subscr_dict, "STORE_SUBSCR") + + def store_subscr_frozen_dict(): + dicts = [{1: 2}, frozendict({1: 2})] + for i in range(_testinternalcapi.SPECIALIZATION_THRESHOLD + 1): + d = dicts[i == _testinternalcapi.SPECIALIZATION_THRESHOLD] + d[1] = 3 + + with self.assertRaises(TypeError): + store_subscr_frozen_dict() + self.assert_specialized(store_subscr_frozen_dict, "STORE_SUBSCR_DICT") + self.assert_no_opcode(store_subscr_frozen_dict, "STORE_SUBSCR") + + def store_subscr_defaultdict(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = collections.defaultdict(int) + a[1] = 4 + self.assertEqual(a[1], 4) + + store_subscr_defaultdict() + self.assert_specialized(store_subscr_defaultdict, "STORE_SUBSCR_DICT") + self.assert_no_opcode(store_subscr_defaultdict, "STORE_SUBSCR") + + def store_subscr_dict_subclass_override(): + class MyDict(dict): + def __setitem__(self, key, value): + super().__setitem__(key, value * 2) + + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + a = MyDict() + a['x'] = 5 + self.assertEqual(a['x'], 10) + + store_subscr_dict_subclass_override() + self.assert_no_opcode(store_subscr_dict_subclass_override, "STORE_SUBSCR_DICT") + + @cpython_only + @requires_specialization def test_compare_op(self): def compare_op_int(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): @@ -1812,7 +2074,7 @@ def compare_op_str(): @cpython_only - @requires_specialization_ft + @requires_specialization def test_for_iter(self): L = list(range(10)) def for_iter_list(): @@ -1832,6 +2094,15 @@ def for_iter_tuple(): self.assert_specialized(for_iter_tuple, "FOR_ITER_TUPLE") self.assert_no_opcode(for_iter_tuple, "FOR_ITER") + s = "abcdefghij" + def for_iter_str(): + for i in s: + self.assertIn(i, s) + + for_iter_str() + self.assert_specialized(for_iter_str, "FOR_ITER_VIRTUAL") + self.assert_no_opcode(for_iter_str, "FOR_ITER") + r = range(10) def for_iter_range(): for i in r: @@ -1849,6 +2120,126 @@ def for_iter_generator(): self.assert_specialized(for_iter_generator, "FOR_ITER_GEN") self.assert_no_opcode(for_iter_generator, "FOR_ITER") + @cpython_only + @requires_specialization + def test_get_iter(self): + L = list(range(10)) + def get_iter_list(): + n = 10 + while n: + n -= 1 + for i in L: + break + get_iter_list() + self.assert_specialized(get_iter_list, "GET_ITER_VIRTUAL") + self.assert_no_opcode(get_iter_list, "GET_ITER") + + def get_iter_gen(): + n = 10 + while n: + n -= 1 + for i in (i for i in range(10)): + break + get_iter_gen() + self.assert_specialized(get_iter_gen, "GET_ITER_SELF") + self.assert_no_opcode(get_iter_gen, "GET_ITER") + + @cpython_only + @requires_specialization + def test_call_list_append(self): + # gh-141367: only exact lists should use + # CALL_LIST_APPEND instruction after specialization. + + r = range(_testinternalcapi.SPECIALIZATION_THRESHOLD) + + def list_append(l): + for _ in r: + l.append(1) + + list_append([]) + self.assert_specialized(list_append, "CALL_LIST_APPEND") + self.assert_no_opcode(list_append, "CALL_METHOD_DESCRIPTOR_O") + self.assert_no_opcode(list_append, "CALL") + + def my_list_append(l): + for _ in r: + l.append(1) + + class MyList(list): pass + my_list_append(MyList()) + self.assert_specialized(my_list_append, "CALL_METHOD_DESCRIPTOR_O") + self.assert_no_opcode(my_list_append, "CALL_LIST_APPEND") + self.assert_no_opcode(my_list_append, "CALL") + + @cpython_only + @requires_specialization + def test_load_attr_module_with_getattr(self): + module = types.ModuleType("test_module_with_getattr") + module.__dict__["some_attr"] = "foo" + + def module_getattr(name): + if name == "missing_attr": + return 42 + raise AttributeError(f"module has no attribute {name}") + + module.__dict__["__getattr__"] = module_getattr + + import sys + sys.modules.pop("test_module_with_getattr", None) + sys.modules["test_module_with_getattr"] = module + try: + def load_module_attr_present(): + import test_module_with_getattr + return test_module_with_getattr.some_attr + + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + self.assertEqual(load_module_attr_present(), "foo") + + self.assert_specialized(load_module_attr_present, "LOAD_ATTR_MODULE") + self.assert_no_opcode(load_module_attr_present, "LOAD_ATTR") + + def load_module_attr_missing(): + import test_module_with_getattr + return test_module_with_getattr.missing_attr + + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + self.assertEqual(load_module_attr_missing(), 42) + + self.assert_no_opcode(load_module_attr_missing, "LOAD_ATTR_MODULE") + finally: + sys.modules.pop("test_module_with_getattr", None) + + @cpython_only + @requires_specialization + def test_specialized_iter_doesnt_skip_send_check(self): + def gen_func(seq): + yield from seq + gen = gen_func(list(range(10))) + for _ in range(3): + gen.send(None) + with self.assertRaises(AttributeError): + gen.send(1) + + + @cpython_only + @requires_specialization + def test_load_attr_enum(self): + import enum + + class Color(enum.IntEnum): + RED = 1 + GREEN = 2 + BLUE = 3 + + def load_enum_member(): + for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): + x = Color.RED + assert x == 1 + + load_enum_member() + self.assert_specialized(load_enum_member, + "LOAD_ATTR_CLASS_WITH_METACLASS_CHECK") + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index e476e4727803e5..fc8ef9520b3c0f 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -1666,6 +1666,16 @@ def test_translations(self): self.assertMsgidsEqual(optparse) +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(optparse, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == '__main__': # To regenerate translation snapshots if len(sys.argv) > 1 and sys.argv[1] == '--snapshot-update': diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 4204a6a47d2a81..642c2722711c7c 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -698,6 +698,7 @@ def test_merge_operator(self): d |= list(b.items()) expected = OrderedDict({0: 0, 1: 1, 2: 2, 3: 3}) self.assertEqual(a | dict(b), expected) + self.assertEqual(a | frozendict(b), expected) self.assertEqual(a | b, expected) self.assertEqual(c, expected) self.assertEqual(d, expected) @@ -706,12 +707,17 @@ def test_merge_operator(self): c |= a expected = OrderedDict({1: 1, 2: 1, 3: 3, 0: 0}) self.assertEqual(dict(b) | a, expected) + self.assertEqual(frozendict(b) | a, expected) + self.assertEqual(a.__ror__(frozendict(b)), expected) self.assertEqual(b | a, expected) self.assertEqual(c, expected) self.assertIs(type(a | b), OrderedDict) self.assertIs(type(dict(a) | b), OrderedDict) + self.assertIs(type(frozendict(a) | b), frozendict) + self.assertIs(type(b.__ror__(frozendict(a))), OrderedDict) self.assertIs(type(a | dict(b)), OrderedDict) + self.assertIs(type(a | frozendict(b)), OrderedDict) expected = a.copy() a |= () diff --git a/Lib/test/test_os/__init__.py b/Lib/test/test_os/__init__.py new file mode 100644 index 00000000000000..bc502ef32d2916 --- /dev/null +++ b/Lib/test/test_os/__init__.py @@ -0,0 +1,6 @@ +import os.path +from test.support import load_package_tests + + +def load_tests(*args): + return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_os.py b/Lib/test/test_os/test_os.py similarity index 87% rename from Lib/test/test_os.py rename to Lib/test/test_os/test_os.py index de3a17fe893170..b88388e091312a 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -7,7 +7,6 @@ import contextlib import decimal import errno -import fnmatch import fractions import itertools import locale @@ -31,12 +30,14 @@ import uuid import warnings from test import support -from test.support import import_helper from test.support import os_helper from test.support import socket_helper from test.support import infinite_recursion +from test.support import requires_root_user +from test.support import requires_non_root_user from test.support import warnings_helper from platform import win32_is_iot +from .utils import create_file try: import resource @@ -46,10 +47,6 @@ import fcntl except ImportError: fcntl = None -try: - import _winapi -except ImportError: - _winapi = None try: import pwd all_users = [u.pw_uid for u in pwd.getpwall()] @@ -72,10 +69,6 @@ from test.support.os_helper import FakePath -root_in_posix = False -if hasattr(os, 'geteuid'): - root_in_posix = (os.geteuid() == 0) - # Detect whether we're on a Linux system that uses the (now outdated # and unmaintained) linuxthreads threading library. There's an issue # when combining linuxthreads with a failed execv call: see @@ -93,11 +86,6 @@ def requires_os_func(name): return unittest.skipUnless(hasattr(os, name), 'requires os.%s' % name) -def create_file(filename, content=b'content'): - with open(filename, "xb", 0) as fp: - fp.write(content) - - # bpo-41625: On AIX, splice() only works with a socket, not with a pipe. requires_splice_pipe = unittest.skipIf(sys.platform.startswith("aix"), 'on AIX, splice() only accepts sockets') @@ -174,6 +162,20 @@ def test_getcwdb(self): self.assertIsInstance(cwd, bytes) self.assertEqual(os.fsdecode(cwd), os.getcwd()) + def test_type_fqdn(self): + def fqdn(obj): + return (obj.__module__, obj.__qualname__) + + native = os.name + self.assertEqual(fqdn(os.stat_result), ("os", "stat_result")) + self.assertEqual(fqdn(os.times_result), (native, "times_result")) + if hasattr(os, "statvfs_result"): + self.assertEqual(fqdn(os.statvfs_result), ("os", "statvfs_result")) + if hasattr(os, "sched_param"): + self.assertEqual(fqdn(os.sched_param), (native, "sched_param")) + if hasattr(os, "waitid_result"): + self.assertEqual(fqdn(os.waitid_result), (native, "waitid_result")) + # Tests creating TESTFN class FileTests(unittest.TestCase): @@ -640,6 +642,15 @@ def setUp(self): self.addCleanup(os_helper.unlink, self.fname) create_file(self.fname, b"ABC") + def check_timestamp_agreement(self, result, names): + # Make sure that the st_?time and st_?time_ns fields roughly agree + # (they should always agree up to around tens-of-microseconds) + for name in names: + with self.subTest(name=name): + floaty = int(getattr(result, name) * 100_000) + nanosecondy = getattr(result, name + "_ns") // 10_000 + self.assertAlmostEqual(floaty, nanosecondy, delta=2, msg=name) + def check_stat_attributes(self, fname): result = os.stat(fname) @@ -660,21 +671,15 @@ def trunc(x): return x result[getattr(stat, name)]) self.assertIn(attr, members) - # Make sure that the st_?time and st_?time_ns fields roughly agree - # (they should always agree up to around tens-of-microseconds) - for name in 'st_atime st_mtime st_ctime'.split(): - floaty = int(getattr(result, name) * 100000) - nanosecondy = getattr(result, name + "_ns") // 10000 - self.assertAlmostEqual(floaty, nanosecondy, delta=2) - - # Ensure both birthtime and birthtime_ns roughly agree, if present + time_attributes = ['st_atime', 'st_mtime', 'st_ctime'] try: - floaty = int(result.st_birthtime * 100000) - nanosecondy = result.st_birthtime_ns // 10000 + result.st_birthtime + result.st_birthtime_ns except AttributeError: pass else: - self.assertAlmostEqual(floaty, nanosecondy, delta=2) + time_attributes.append('st_birthtime') + self.check_timestamp_agreement(result, time_attributes) try: result[200] @@ -735,6 +740,160 @@ def test_stat_result_pickle(self): unpickled = pickle.loads(p) self.assertEqual(result, unpickled) + def check_statx_attributes(self, filename): + maximal_mask = 0 + for name in dir(os): + if name.startswith('STATX_'): + maximal_mask |= getattr(os, name) + result = os.statx(filename, maximal_mask) + stat_result = os.stat(filename) + + time_attributes = ('stx_atime', 'stx_btime', 'stx_ctime', 'stx_mtime') + # gh-83714: stx_btime can be None on tmpfs even if STATX_BTIME mask + # is used + time_attributes = [name for name in time_attributes + if getattr(result, name) is not None] + self.check_timestamp_agreement(result, time_attributes) + + def getmask(name): + return getattr(os, name, 0) + + requirements = ( + ('stx_atime', os.STATX_ATIME), + ('stx_atime_ns', os.STATX_ATIME), + ('stx_atomic_write_segments_max', getmask('STATX_WRITE_ATOMIC')), + ('stx_atomic_write_unit_max', getmask('STATX_WRITE_ATOMIC')), + ('stx_atomic_write_unit_max_opt', getmask('STATX_WRITE_ATOMIC')), + ('stx_atomic_write_unit_min', getmask('STATX_WRITE_ATOMIC')), + ('stx_attributes', 0), + ('stx_attributes_mask', 0), + ('stx_blksize', 0), + ('stx_blocks', os.STATX_BLOCKS), + ('stx_btime', os.STATX_BTIME), + ('stx_btime_ns', os.STATX_BTIME), + ('stx_ctime', os.STATX_CTIME), + ('stx_ctime_ns', os.STATX_CTIME), + ('stx_dev', 0), + ('stx_dev_major', 0), + ('stx_dev_minor', 0), + ('stx_dio_mem_align', getmask('STATX_DIOALIGN')), + ('stx_dio_offset_align', getmask('STATX_DIOALIGN')), + ('stx_dio_read_offset_align', getmask('STATX_DIO_READ_ALIGN')), + ('stx_gid', os.STATX_GID), + ('stx_ino', os.STATX_INO), + ('stx_mask', 0), + ('stx_mnt_id', getmask('STATX_MNT_ID')), + ('stx_mode', os.STATX_TYPE | os.STATX_MODE), + ('stx_mtime', os.STATX_MTIME), + ('stx_mtime_ns', os.STATX_MTIME), + ('stx_nlink', os.STATX_NLINK), + ('stx_rdev', 0), + ('stx_rdev_major', 0), + ('stx_rdev_minor', 0), + ('stx_size', os.STATX_SIZE), + ('stx_subvol', getmask('STATX_SUBVOL')), + ('stx_uid', os.STATX_UID), + ) + optional_members = { + 'stx_atomic_write_segments_max', + 'stx_atomic_write_unit_max', + 'stx_atomic_write_unit_max_opt', + 'stx_atomic_write_unit_min', + 'stx_dio_mem_align', + 'stx_dio_offset_align', + 'stx_dio_read_offset_align', + 'stx_mnt_id', + 'stx_subvol', + } + float_type = { + 'stx_atime', + 'stx_btime', + 'stx_ctime', + 'stx_mtime', + } + + members = set(name for name in dir(result) + if name.startswith('stx_')) + tested = set(name for name, mask in requirements) + if members - tested: + raise ValueError(f"statx members not tested: {members - tested}") + + for name, mask in requirements: + with self.subTest(name=name): + try: + x = getattr(result, name) + except AttributeError: + if name in optional_members: + continue + else: + raise + + if not(result.stx_mask & mask == mask): + self.assertIsNone(x) + continue + + if name in float_type: + self.assertIsInstance(x, float) + else: + self.assertIsInstance(x, int) + + # Compare with stat_result + try: + b = getattr(stat_result, "st_" + name[4:]) + except AttributeError: + pass + else: + self.assertEqual(type(x), type(b)) + if isinstance(x, float): + self.assertAlmostEqual(x, b) + else: + self.assertEqual(x, b) + + self.assertEqual(result.stx_rdev_major, os.major(result.stx_rdev)) + self.assertEqual(result.stx_rdev_minor, os.minor(result.stx_rdev)) + self.assertEqual(result.stx_dev_major, os.major(result.stx_dev)) + self.assertEqual(result.stx_dev_minor, os.minor(result.stx_dev)) + + self.assertEqual(result.stx_attributes & result.stx_attributes_mask, + result.stx_attributes) + + @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()') + def test_statx_attributes(self): + self.check_statx_attributes(self.fname) + + @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()') + def test_statx_attributes_bytes(self): + try: + fname = self.fname.encode(sys.getfilesystemencoding()) + except UnicodeEncodeError: + self.skipTest("cannot encode %a for the filesystem" % self.fname) + self.check_statx_attributes(fname) + + @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()') + def test_statx_attributes_pathlike(self): + self.check_statx_attributes(FakePath(self.fname)) + + @unittest.skipUnless(hasattr(os, 'statx'), 'test needs os.statx()') + def test_statx_result(self): + result = os.statx(self.fname, os.STATX_BASIC_STATS) + + # Check that attributes are read-only + members = [name for name in dir(result) + if name.startswith('stx_')] + for name in members: + try: + setattr(result, name, 1) + except AttributeError: + pass + else: + self.fail("No exception raised") + + # statx_result is not a tuple or tuple-like object. + with self.assertRaisesRegex(TypeError, 'not subscriptable'): + result[0] + with self.assertRaisesRegex(TypeError, 'cannot unpack'): + _, _ = result + @unittest.skipUnless(hasattr(os, 'statvfs'), 'test needs os.statvfs()') def test_statvfs_attributes(self): result = os.statvfs(self.fname) @@ -948,6 +1107,20 @@ def ns_to_sec(ns): # issue, os.utime() rounds towards minus infinity. return (ns * 1e-9) + 0.5e-9 + @staticmethod + def ns_to_sec_decimal(ns): + # Convert a number of nanosecond (int) to a number of seconds (Decimal). + # Round towards infinity by adding 0.5 nanosecond to avoid rounding + # issue, os.utime() rounds towards minus infinity. + return decimal.Decimal('1e-9') * ns + decimal.Decimal('0.5e-9') + + @staticmethod + def ns_to_sec_fraction(ns): + # Convert a number of nanosecond (int) to a number of seconds (Fraction). + # Round towards infinity by adding 0.5 nanosecond to avoid rounding + # issue, os.utime() rounds towards minus infinity. + return fractions.Fraction(ns, 10**9) + fractions.Fraction(1, 2*10**9) + def test_utime_by_indexed(self): # pass times as floating-point seconds as the second indexed parameter def set_time(filename, ns): @@ -968,6 +1141,24 @@ def set_time(filename, ns): os.utime(filename, times=(atime, mtime)) self._test_utime(set_time) + def test_utime_decimal(self): + # pass times as Decimal seconds + def set_time(filename, ns): + atime_ns, mtime_ns = ns + atime = self.ns_to_sec_decimal(atime_ns) + mtime = self.ns_to_sec_decimal(mtime_ns) + os.utime(filename, (atime, mtime)) + self._test_utime(set_time) + + def test_utime_fraction(self): + # pass times as Fraction seconds + def set_time(filename, ns): + atime_ns, mtime_ns = ns + atime = self.ns_to_sec_fraction(atime_ns) + mtime = self.ns_to_sec_fraction(mtime_ns) + os.utime(filename, (atime, mtime)) + self._test_utime(set_time) + @unittest.skipUnless(os.utime in os.supports_follow_symlinks, "follow_symlinks support for utime required " "for this test.") @@ -1064,9 +1255,15 @@ def test_large_time(self): if self.get_file_system(self.dirname) != "NTFS": self.skipTest("requires NTFS") - large = 5000000000 # some day in 2128 - os.utime(self.fname, (large, large)) - self.assertEqual(os.stat(self.fname).st_mtime, large) + times = ( + 5000000000, # some day in 2128 + # boundaries of the fast path cutoff in posixmodule.c:fill_time + -9223372037, -9223372036, 9223372035, 9223372036, + ) + for large in times: + with self.subTest(large=large): + os.utime(self.fname, (large, large)) + self.assertEqual(os.stat(self.fname).st_mtime, large) def test_utime_invalid_arguments(self): # seconds and nanoseconds parameters are mutually exclusive @@ -1466,6 +1663,14 @@ def test_reload_environ(self): self.assertNotIn(b'test_env', os.environb) self.assertNotIn('test_env', os.environ) + def test_clearenv(self): + os.environ['REMOVEME'] = '1' + os.environ.clear() + self.assertEqual(os.environ, {}) + + self.assertRaises(TypeError, os.environ.clear, None) + + class WalkTests(unittest.TestCase): """Tests for os.walk().""" is_fwalk = False @@ -1662,7 +1867,9 @@ def test_walk_bad_dir2(self): walk_it = self.walk(self.tmp1_path, follow_symlinks=True) if self.is_fwalk: - self.assertRaises(NotADirectoryError, next, walk_it) + with self.assertRaises(OSError) as cm: + next(walk_it) + self.assertIn(cm.exception.errno, (errno.ENOTDIR, errno.EINVAL)) self.assertRaises(StopIteration, next, walk_it) @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()') @@ -1932,6 +2139,94 @@ def test_mode(self): self.assertEqual(os.stat(path).st_mode & 0o777, 0o555) self.assertEqual(os.stat(parent).st_mode & 0o777, 0o775) + @unittest.skipIf( + support.is_emscripten or support.is_wasi, + "umask is not implemented on Emscripten/WASI." + ) + @unittest.skipIf( + sys.platform == "android", + "Android filesystem may not honor requested permissions." + ) + def test_mode_with_parent_mode(self): + # Test the parent_mode parameter + parent = os.path.join(os_helper.TESTFN, 'dir1') + path = os.path.join(parent, 'dir2') + with os_helper.temp_umask(0o002): + # Specify mode for both leaf and parent directories + os.makedirs(path, 0o770, parent_mode=0o750) + self.assertTrue(os.path.exists(path)) + self.assertTrue(os.path.isdir(path)) + if os.name != 'nt': + # Leaf directory gets the mode parameter + self.assertEqual(os.stat(path).st_mode & 0o777, 0o770) + # Parent directory gets the parent_mode parameter + self.assertEqual(os.stat(parent).st_mode & 0o777, 0o750) + + @unittest.skipIf( + support.is_emscripten or support.is_wasi, + "umask is not implemented on Emscripten/WASI." + ) + @unittest.skipIf( + sys.platform == "android", + "Android filesystem may not honor requested permissions." + ) + def test_parent_mode_deep_hierarchy(self): + # Test parent_mode with deep directory hierarchy + base = os.path.join(os_helper.TESTFN, 'dir1', 'dir2', 'dir3') + with os_helper.temp_umask(0o002): + os.makedirs(base, 0o755, parent_mode=0o700) + self.assertTrue(os.path.exists(base)) + if os.name != 'nt': + # Check that all parent directories have parent_mode + level1 = os.path.join(os_helper.TESTFN, 'dir1') + level2 = os.path.join(level1, 'dir2') + self.assertEqual(os.stat(level1).st_mode & 0o777, 0o700) + self.assertEqual(os.stat(level2).st_mode & 0o777, 0o700) + # Leaf directory has the regular mode + self.assertEqual(os.stat(base).st_mode & 0o777, 0o755) + + @unittest.skipIf( + support.is_emscripten or support.is_wasi, + "umask is not implemented on Emscripten/WASI." + ) + @unittest.skipIf( + sys.platform == "android", + "Android filesystem may not honor requested permissions." + ) + def test_parent_mode_same_as_mode(self): + # Test emulating Python 3.6 behavior by setting parent_mode=mode + parent = os.path.join(os_helper.TESTFN, 'dir1') + path = os.path.join(parent, 'dir2') + with os_helper.temp_umask(0o002): + os.makedirs(path, 0o705, parent_mode=0o705) + self.assertTrue(os.path.exists(path)) + if os.name != 'nt': + # Both directories should have the same mode + self.assertEqual(os.stat(path).st_mode & 0o777, 0o705) + self.assertEqual(os.stat(parent).st_mode & 0o777, 0o705) + + @unittest.skipIf( + support.is_emscripten or support.is_wasi, + "umask is not implemented on Emscripten/WASI." + ) + @unittest.skipIf( + sys.platform == "android", + "Android filesystem may not honor requested permissions." + ) + def test_parent_mode_combined_with_umask(self): + # parent_mode, like mode, is combined with the process umask; it does + # not bypass it. + parent = os.path.join(os_helper.TESTFN, 'dir1') + path = os.path.join(parent, 'dir2') + with os_helper.temp_umask(0o022): + os.makedirs(path, 0o777, parent_mode=0o777) + self.assertTrue(os.path.isdir(path)) + if os.name != 'nt': + # 0o777 is masked down to 0o755 by the 0o022 umask, for both + # the leaf (mode) and the parent (parent_mode). + self.assertEqual(os.stat(path).st_mode & 0o777, 0o755) + self.assertEqual(os.stat(parent).st_mode & 0o777, 0o755) + @unittest.skipIf( support.is_wasi, "WASI's umask is a stub." @@ -2005,15 +2300,9 @@ def test_win32_mkdir_700(self): ) def tearDown(self): - path = os.path.join(os_helper.TESTFN, 'dir1', 'dir2', 'dir3', - 'dir4', 'dir5', 'dir6') - # If the tests failed, the bottom-most directory ('../dir6') - # may not have been created, so we look for the outermost directory - # that exists. - while not os.path.exists(path) and path != os_helper.TESTFN: - path = os.path.dirname(path) - - os.removedirs(path) + # Remove the whole tree regardless of which sub-directories a test + # created and regardless of their permission bits. + os_helper.rmtree(os_helper.TESTFN) @unittest.skipUnless(hasattr(os, "chown"), "requires os.chown()") @@ -2050,8 +2339,8 @@ def test_chown_gid(self): gid = os.stat(os_helper.TESTFN).st_gid self.assertEqual(gid, gid_2) - @unittest.skipUnless(root_in_posix and len(all_users) > 1, - "test needs root privilege and more than one user") + @requires_root_user + @unittest.skipUnless(len(all_users) > 1, "test needs more than one user") def test_chown_with_root(self): uid_1, uid_2 = all_users[:2] gid = os.stat(os_helper.TESTFN).st_gid @@ -2062,8 +2351,10 @@ def test_chown_with_root(self): uid = os.stat(os_helper.TESTFN).st_uid self.assertEqual(uid, uid_2) - @unittest.skipUnless(not root_in_posix and len(all_users) > 1, - "test needs non-root account and more than one user") + @requires_non_root_user + @unittest.skipUnless(len(all_users) > 1, "test needs and more than one user") + @unittest.skipIf(sys.platform == 'cygwin', + 'chown() can set any uid on Cygwin') def test_chown_without_permission(self): uid_1, uid_2 = all_users[:2] gid = os.stat(os_helper.TESTFN).st_gid @@ -2417,6 +2708,45 @@ def test_execve_invalid_env(self): with self.assertRaises(ValueError): os.execve(args[0], args, newenv) + # See https://github.com/python/cpython/issues/137934 and the other + # related issues for the reason why we cannot test this on Windows. + @unittest.skipIf(os.name == "nt", "POSIX-specific test") + @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), + "requires a shell") + def test_execve_env_concurrent_mutation_with_fspath_posix(self): + # Prevent crash when mutating environment during parsing. + # Regression test for https://github.com/python/cpython/issues/143309. + + message = "hello from execve" + code = """ + import os, sys + + class MyPath: + def __fspath__(self): + mutated.clear() + return b"pwn" + + mutated = KEYS = VALUES = [MyPath()] + + class MyEnv: + def __getitem__(self): raise RuntimeError("must not be called") + def __len__(self): return 1 + def keys(self): return KEYS + def values(self): return VALUES + + args = [{unix_shell!r}, '-c', 'echo \"{message!s}\"'] + os.execve(args[0], args, MyEnv()) + """.format(unix_shell=unix_shell, message=message) + + # Make sure to forward "LD_*" variables so that assert_python_ok() + # can run correctly. + minimal = {k: v for k, v in os.environ.items() if k.startswith("LD_")} + with os_helper.EnvironmentVarGuard() as env: + env.clear() + env.update(minimal) + _, out, _ = assert_python_ok('-c', code, **env) + self.assertIn(bytes(message, "ascii"), out) + @unittest.skipUnless(sys.platform == "win32", "Win32-specific test") def test_execve_with_empty_path(self): # bpo-32890: Check GetLastError() misuse @@ -2428,42 +2758,6 @@ def test_execve_with_empty_path(self): self.fail('No OSError raised') -@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") -class Win32ErrorTests(unittest.TestCase): - def setUp(self): - try: - os.stat(os_helper.TESTFN) - except FileNotFoundError: - exists = False - except OSError as exc: - exists = True - self.fail("file %s must not exist; os.stat failed with %s" - % (os_helper.TESTFN, exc)) - else: - self.fail("file %s must not exist" % os_helper.TESTFN) - - def test_rename(self): - self.assertRaises(OSError, os.rename, os_helper.TESTFN, os_helper.TESTFN+".bak") - - def test_remove(self): - self.assertRaises(OSError, os.remove, os_helper.TESTFN) - - def test_chdir(self): - self.assertRaises(OSError, os.chdir, os_helper.TESTFN) - - def test_mkdir(self): - self.addCleanup(os_helper.unlink, os_helper.TESTFN) - - with open(os_helper.TESTFN, "x") as f: - self.assertRaises(OSError, os.mkdir, os_helper.TESTFN) - - def test_utime(self): - self.assertRaises(OSError, os.utime, os_helper.TESTFN, None) - - def test_chmod(self): - self.assertRaises(OSError, os.chmod, os_helper.TESTFN, 0) - - @unittest.skipIf(support.is_wasi, "Cannot create invalid FD on WASI.") class TestInvalidFD(unittest.TestCase): singles = ["fchdir", "dup", "fstat", "fstatvfs", "tcgetpgrp", "ttyname"] @@ -2568,6 +2862,68 @@ def test_fpathconf_bad_fd(self): self.check(os.pathconf, "PC_NAME_MAX") self.check(os.fpathconf, "PC_NAME_MAX") + @unittest.skipUnless(hasattr(os, 'pathconf'), 'test needs os.pathconf()') + @unittest.skipIf( + support.linked_to_musl(), + 'musl fpathconf ignores the file descriptor and returns a constant', + ) + def test_pathconf_negative_fd_uses_fd_semantics(self): + if os.pathconf not in os.supports_fd: + self.skipTest('needs fpathconf()') + + with self.assertRaises(OSError) as ctx: + os.pathconf(-1, 1) + self.assertEqual(ctx.exception.errno, errno.EBADF) + + @support.subTests("fd", [-1, -5]) + def test_negative_fd_ebadf(self, fd): + tests = [(os.stat, fd)] + if hasattr(os, "statx"): + tests.append((os.statx, fd, 0)) + if os.chdir in os.supports_fd: + tests.append((os.chdir, fd)) + if os.chmod in os.supports_fd: + tests.append((os.chmod, fd, 0o777)) + if hasattr(os, "chown") and os.chown in os.supports_fd: + tests.append((os.chown, fd, 0, 0)) + if os.listdir in os.supports_fd: + tests.append((os.listdir, fd)) + if os.utime in os.supports_fd: + tests.append((os.utime, fd, (0, 0))) + if hasattr(os, "truncate") and os.truncate in os.supports_fd: + tests.append((os.truncate, fd, 0)) + if hasattr(os, 'statvfs') and os.statvfs in os.supports_fd: + tests.append((os.statvfs, fd)) + if hasattr(os, "setxattr"): + tests.append((os.getxattr, fd, b"user.test")) + tests.append((os.setxattr, fd, b"user.test", b"1")) + tests.append((os.removexattr, fd, b"user.test")) + tests.append((os.listxattr, fd)) + if os.scandir in os.supports_fd: + tests.append((os.scandir, fd)) + + for func, *args in tests: + with self.subTest(func=func, args=args): + with self.assertRaises(OSError) as ctx: + func(*args) + self.assertEqual(ctx.exception.errno, errno.EBADF) + + if (hasattr(os, "execve") and os.execve in os.supports_fd + and support.has_subprocess_support): + # glibc fails with EINVAL, musl fails with EBADF + with self.assertRaises(OSError) as ctx: + os.execve(fd, [sys.executable, "-c", "pass"], os.environ) + self.assertIn(ctx.exception.errno, (errno.EBADF, errno.EINVAL)) + + if support.MS_WINDOWS: + import nt + self.assertFalse(nt._path_exists(fd)) + self.assertFalse(nt._path_lexists(fd)) + self.assertFalse(nt._path_isdir(fd)) + self.assertFalse(nt._path_isfile(fd)) + self.assertFalse(nt._path_islink(fd)) + self.assertFalse(nt._path_isjunction(fd)) + @unittest.skipUnless(hasattr(os, 'ftruncate'), 'test needs os.ftruncate()') def test_ftruncate(self): self.check(os.truncate, 0) @@ -2798,224 +3154,6 @@ def test_stat(self): for fn in self.unicodefn: os.stat(os.path.join(self.dir, fn)) -@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") -class Win32KillTests(unittest.TestCase): - def _kill(self, sig): - # Start sys.executable as a subprocess and communicate from the - # subprocess to the parent that the interpreter is ready. When it - # becomes ready, send *sig* via os.kill to the subprocess and check - # that the return code is equal to *sig*. - import ctypes - from ctypes import wintypes - import msvcrt - - # Since we can't access the contents of the process' stdout until the - # process has exited, use PeekNamedPipe to see what's inside stdout - # without waiting. This is done so we can tell that the interpreter - # is started and running at a point where it could handle a signal. - PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe - PeekNamedPipe.restype = wintypes.BOOL - PeekNamedPipe.argtypes = (wintypes.HANDLE, # Pipe handle - ctypes.POINTER(ctypes.c_char), # stdout buf - wintypes.DWORD, # Buffer size - ctypes.POINTER(wintypes.DWORD), # bytes read - ctypes.POINTER(wintypes.DWORD), # bytes avail - ctypes.POINTER(wintypes.DWORD)) # bytes left - msg = "running" - proc = subprocess.Popen([sys.executable, "-c", - "import sys;" - "sys.stdout.write('{}');" - "sys.stdout.flush();" - "input()".format(msg)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE) - self.addCleanup(proc.stdout.close) - self.addCleanup(proc.stderr.close) - self.addCleanup(proc.stdin.close) - - count, max = 0, 100 - while count < max and proc.poll() is None: - # Create a string buffer to store the result of stdout from the pipe - buf = ctypes.create_string_buffer(len(msg)) - # Obtain the text currently in proc.stdout - # Bytes read/avail/left are left as NULL and unused - rslt = PeekNamedPipe(msvcrt.get_osfhandle(proc.stdout.fileno()), - buf, ctypes.sizeof(buf), None, None, None) - self.assertNotEqual(rslt, 0, "PeekNamedPipe failed") - if buf.value: - self.assertEqual(msg, buf.value.decode()) - break - time.sleep(0.1) - count += 1 - else: - self.fail("Did not receive communication from the subprocess") - - os.kill(proc.pid, sig) - self.assertEqual(proc.wait(), sig) - - def test_kill_sigterm(self): - # SIGTERM doesn't mean anything special, but make sure it works - self._kill(signal.SIGTERM) - - def test_kill_int(self): - # os.kill on Windows can take an int which gets set as the exit code - self._kill(100) - - @unittest.skipIf(mmap is None, "requires mmap") - def _kill_with_event(self, event, name): - tagname = "test_os_%s" % uuid.uuid1() - m = mmap.mmap(-1, 1, tagname) - m[0] = 0 - - # Run a script which has console control handling enabled. - script = os.path.join(os.path.dirname(__file__), - "win_console_handler.py") - cmd = [sys.executable, script, tagname] - proc = subprocess.Popen(cmd, - creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) - - with proc: - # Let the interpreter startup before we send signals. See #3137. - for _ in support.sleeping_retry(support.SHORT_TIMEOUT): - if proc.poll() is None: - break - else: - # Forcefully kill the process if we weren't able to signal it. - proc.kill() - self.fail("Subprocess didn't finish initialization") - - os.kill(proc.pid, event) - - try: - # proc.send_signal(event) could also be done here. - # Allow time for the signal to be passed and the process to exit. - proc.wait(timeout=support.SHORT_TIMEOUT) - except subprocess.TimeoutExpired: - # Forcefully kill the process if we weren't able to signal it. - proc.kill() - self.fail("subprocess did not stop on {}".format(name)) - - @unittest.skip("subprocesses aren't inheriting Ctrl+C property") - @support.requires_subprocess() - def test_CTRL_C_EVENT(self): - from ctypes import wintypes - import ctypes - - # Make a NULL value by creating a pointer with no argument. - NULL = ctypes.POINTER(ctypes.c_int)() - SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler - SetConsoleCtrlHandler.argtypes = (ctypes.POINTER(ctypes.c_int), - wintypes.BOOL) - SetConsoleCtrlHandler.restype = wintypes.BOOL - - # Calling this with NULL and FALSE causes the calling process to - # handle Ctrl+C, rather than ignore it. This property is inherited - # by subprocesses. - SetConsoleCtrlHandler(NULL, 0) - - self._kill_with_event(signal.CTRL_C_EVENT, "CTRL_C_EVENT") - - @support.requires_subprocess() - def test_CTRL_BREAK_EVENT(self): - self._kill_with_event(signal.CTRL_BREAK_EVENT, "CTRL_BREAK_EVENT") - - -@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") -class Win32ListdirTests(unittest.TestCase): - """Test listdir on Windows.""" - - def setUp(self): - self.created_paths = [] - for i in range(2): - dir_name = 'SUB%d' % i - dir_path = os.path.join(os_helper.TESTFN, dir_name) - file_name = 'FILE%d' % i - file_path = os.path.join(os_helper.TESTFN, file_name) - os.makedirs(dir_path) - with open(file_path, 'w', encoding='utf-8') as f: - f.write("I'm %s and proud of it. Blame test_os.\n" % file_path) - self.created_paths.extend([dir_name, file_name]) - self.created_paths.sort() - - def tearDown(self): - shutil.rmtree(os_helper.TESTFN) - - def test_listdir_no_extended_path(self): - """Test when the path is not an "extended" path.""" - # unicode - self.assertEqual( - sorted(os.listdir(os_helper.TESTFN)), - self.created_paths) - - # bytes - self.assertEqual( - sorted(os.listdir(os.fsencode(os_helper.TESTFN))), - [os.fsencode(path) for path in self.created_paths]) - - def test_listdir_extended_path(self): - """Test when the path starts with '\\\\?\\'.""" - # See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath - # unicode - path = '\\\\?\\' + os.path.abspath(os_helper.TESTFN) - self.assertEqual( - sorted(os.listdir(path)), - self.created_paths) - - # bytes - path = b'\\\\?\\' + os.fsencode(os.path.abspath(os_helper.TESTFN)) - self.assertEqual( - sorted(os.listdir(path)), - [os.fsencode(path) for path in self.created_paths]) - - -@unittest.skipUnless(os.name == "nt", "NT specific tests") -class Win32ListdriveTests(unittest.TestCase): - """Test listdrive, listmounts and listvolume on Windows.""" - - def setUp(self): - # Get drives and volumes from fsutil - out = subprocess.check_output( - ["fsutil.exe", "volume", "list"], - cwd=os.path.join(os.getenv("SystemRoot", "\\Windows"), "System32"), - encoding="mbcs", - errors="ignore", - ) - lines = out.splitlines() - self.known_volumes = {l for l in lines if l.startswith('\\\\?\\')} - self.known_drives = {l for l in lines if l[1:] == ':\\'} - self.known_mounts = {l for l in lines if l[1:3] == ':\\'} - - def test_listdrives(self): - drives = os.listdrives() - self.assertIsInstance(drives, list) - self.assertSetEqual( - self.known_drives, - self.known_drives & set(drives), - ) - - def test_listvolumes(self): - volumes = os.listvolumes() - self.assertIsInstance(volumes, list) - self.assertSetEqual( - self.known_volumes, - self.known_volumes & set(volumes), - ) - - def test_listmounts(self): - for volume in os.listvolumes(): - try: - mounts = os.listmounts(volume) - except OSError as ex: - if support.verbose: - print("Skipping", volume, "because of", ex) - else: - self.assertIsInstance(mounts, list) - self.assertSetEqual( - set(mounts), - self.known_mounts & set(mounts), - ) - @unittest.skipUnless(hasattr(os, 'readlink'), 'needs os.readlink()') class ReadlinkTests(unittest.TestCase): @@ -3078,370 +3216,6 @@ def test_bytes(self): self.assertIsInstance(path, bytes) -@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") -@os_helper.skip_unless_symlink -class Win32SymlinkTests(unittest.TestCase): - filelink = 'filelinktest' - filelink_target = os.path.abspath(__file__) - dirlink = 'dirlinktest' - dirlink_target = os.path.dirname(filelink_target) - missing_link = 'missing link' - - def setUp(self): - assert os.path.exists(self.dirlink_target) - assert os.path.exists(self.filelink_target) - assert not os.path.exists(self.dirlink) - assert not os.path.exists(self.filelink) - assert not os.path.exists(self.missing_link) - - def tearDown(self): - if os.path.exists(self.filelink): - os.remove(self.filelink) - if os.path.exists(self.dirlink): - os.rmdir(self.dirlink) - if os.path.lexists(self.missing_link): - os.remove(self.missing_link) - - def test_directory_link(self): - os.symlink(self.dirlink_target, self.dirlink) - self.assertTrue(os.path.exists(self.dirlink)) - self.assertTrue(os.path.isdir(self.dirlink)) - self.assertTrue(os.path.islink(self.dirlink)) - self.check_stat(self.dirlink, self.dirlink_target) - - def test_file_link(self): - os.symlink(self.filelink_target, self.filelink) - self.assertTrue(os.path.exists(self.filelink)) - self.assertTrue(os.path.isfile(self.filelink)) - self.assertTrue(os.path.islink(self.filelink)) - self.check_stat(self.filelink, self.filelink_target) - - def _create_missing_dir_link(self): - 'Create a "directory" link to a non-existent target' - linkname = self.missing_link - if os.path.lexists(linkname): - os.remove(linkname) - target = r'c:\\target does not exist.29r3c740' - assert not os.path.exists(target) - target_is_dir = True - os.symlink(target, linkname, target_is_dir) - - def test_remove_directory_link_to_missing_target(self): - self._create_missing_dir_link() - # For compatibility with Unix, os.remove will check the - # directory status and call RemoveDirectory if the symlink - # was created with target_is_dir==True. - os.remove(self.missing_link) - - def test_isdir_on_directory_link_to_missing_target(self): - self._create_missing_dir_link() - self.assertFalse(os.path.isdir(self.missing_link)) - - def test_rmdir_on_directory_link_to_missing_target(self): - self._create_missing_dir_link() - os.rmdir(self.missing_link) - - def check_stat(self, link, target): - self.assertEqual(os.stat(link), os.stat(target)) - self.assertNotEqual(os.lstat(link), os.stat(link)) - - bytes_link = os.fsencode(link) - self.assertEqual(os.stat(bytes_link), os.stat(target)) - self.assertNotEqual(os.lstat(bytes_link), os.stat(bytes_link)) - - def test_12084(self): - level1 = os.path.abspath(os_helper.TESTFN) - level2 = os.path.join(level1, "level2") - level3 = os.path.join(level2, "level3") - self.addCleanup(os_helper.rmtree, level1) - - os.mkdir(level1) - os.mkdir(level2) - os.mkdir(level3) - - file1 = os.path.abspath(os.path.join(level1, "file1")) - create_file(file1) - - orig_dir = os.getcwd() - try: - os.chdir(level2) - link = os.path.join(level2, "link") - os.symlink(os.path.relpath(file1), "link") - self.assertIn("link", os.listdir(os.getcwd())) - - # Check os.stat calls from the same dir as the link - self.assertEqual(os.stat(file1), os.stat("link")) - - # Check os.stat calls from a dir below the link - os.chdir(level1) - self.assertEqual(os.stat(file1), - os.stat(os.path.relpath(link))) - - # Check os.stat calls from a dir above the link - os.chdir(level3) - self.assertEqual(os.stat(file1), - os.stat(os.path.relpath(link))) - finally: - os.chdir(orig_dir) - - @unittest.skipUnless(os.path.lexists(r'C:\Users\All Users') - and os.path.exists(r'C:\ProgramData'), - 'Test directories not found') - def test_29248(self): - # os.symlink() calls CreateSymbolicLink, which creates - # the reparse data buffer with the print name stored - # first, so the offset is always 0. CreateSymbolicLink - # stores the "PrintName" DOS path (e.g. "C:\") first, - # with an offset of 0, followed by the "SubstituteName" - # NT path (e.g. "\??\C:\"). The "All Users" link, on - # the other hand, seems to have been created manually - # with an inverted order. - target = os.readlink(r'C:\Users\All Users') - self.assertTrue(os.path.samefile(target, r'C:\ProgramData')) - - def test_buffer_overflow(self): - # Older versions would have a buffer overflow when detecting - # whether a link source was a directory. This test ensures we - # no longer crash, but does not otherwise validate the behavior - segment = 'X' * 27 - path = os.path.join(*[segment] * 10) - test_cases = [ - # overflow with absolute src - ('\\' + path, segment), - # overflow dest with relative src - (segment, path), - # overflow when joining src - (path[:180], path[:180]), - ] - for src, dest in test_cases: - try: - os.symlink(src, dest) - except FileNotFoundError: - pass - else: - try: - os.remove(dest) - except OSError: - pass - # Also test with bytes, since that is a separate code path. - try: - os.symlink(os.fsencode(src), os.fsencode(dest)) - except FileNotFoundError: - pass - else: - try: - os.remove(dest) - except OSError: - pass - - def test_appexeclink(self): - root = os.path.expandvars(r'%LOCALAPPDATA%\Microsoft\WindowsApps') - if not os.path.isdir(root): - self.skipTest("test requires a WindowsApps directory") - - aliases = [os.path.join(root, a) - for a in fnmatch.filter(os.listdir(root), '*.exe')] - - for alias in aliases: - if support.verbose: - print() - print("Testing with", alias) - st = os.lstat(alias) - self.assertEqual(st, os.stat(alias)) - self.assertFalse(stat.S_ISLNK(st.st_mode)) - self.assertEqual(st.st_reparse_tag, stat.IO_REPARSE_TAG_APPEXECLINK) - self.assertTrue(os.path.isfile(alias)) - # testing the first one we see is sufficient - break - else: - self.skipTest("test requires an app execution alias") - -@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") -class Win32JunctionTests(unittest.TestCase): - junction = 'junctiontest' - junction_target = os.path.dirname(os.path.abspath(__file__)) - - def setUp(self): - assert os.path.exists(self.junction_target) - assert not os.path.lexists(self.junction) - - def tearDown(self): - if os.path.lexists(self.junction): - os.unlink(self.junction) - - def test_create_junction(self): - _winapi.CreateJunction(self.junction_target, self.junction) - self.assertTrue(os.path.lexists(self.junction)) - self.assertTrue(os.path.exists(self.junction)) - self.assertTrue(os.path.isdir(self.junction)) - self.assertNotEqual(os.stat(self.junction), os.lstat(self.junction)) - self.assertEqual(os.stat(self.junction), os.stat(self.junction_target)) - - # bpo-37834: Junctions are not recognized as links. - self.assertFalse(os.path.islink(self.junction)) - self.assertEqual(os.path.normcase("\\\\?\\" + self.junction_target), - os.path.normcase(os.readlink(self.junction))) - - def test_unlink_removes_junction(self): - _winapi.CreateJunction(self.junction_target, self.junction) - self.assertTrue(os.path.exists(self.junction)) - self.assertTrue(os.path.lexists(self.junction)) - - os.unlink(self.junction) - self.assertFalse(os.path.exists(self.junction)) - -@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") -class Win32NtTests(unittest.TestCase): - def test_getfinalpathname_handles(self): - nt = import_helper.import_module('nt') - ctypes = import_helper.import_module('ctypes') - # Ruff false positive -- it thinks we're redefining `ctypes` here - import ctypes.wintypes # noqa: F811 - - kernel = ctypes.WinDLL('Kernel32.dll', use_last_error=True) - kernel.GetCurrentProcess.restype = ctypes.wintypes.HANDLE - - kernel.GetProcessHandleCount.restype = ctypes.wintypes.BOOL - kernel.GetProcessHandleCount.argtypes = (ctypes.wintypes.HANDLE, - ctypes.wintypes.LPDWORD) - - # This is a pseudo-handle that doesn't need to be closed - hproc = kernel.GetCurrentProcess() - - handle_count = ctypes.wintypes.DWORD() - ok = kernel.GetProcessHandleCount(hproc, ctypes.byref(handle_count)) - self.assertEqual(1, ok) - - before_count = handle_count.value - - # The first two test the error path, __file__ tests the success path - filenames = [ - r'\\?\C:', - r'\\?\NUL', - r'\\?\CONIN', - __file__, - ] - - for _ in range(10): - for name in filenames: - try: - nt._getfinalpathname(name) - except Exception: - # Failure is expected - pass - try: - os.stat(name) - except Exception: - pass - - ok = kernel.GetProcessHandleCount(hproc, ctypes.byref(handle_count)) - self.assertEqual(1, ok) - - handle_delta = handle_count.value - before_count - - self.assertEqual(0, handle_delta) - - @support.requires_subprocess() - def test_stat_unlink_race(self): - # bpo-46785: the implementation of os.stat() falls back to reading - # the parent directory if CreateFileW() fails with a permission - # error. If reading the parent directory fails because the file or - # directory are subsequently unlinked, or because the volume or - # share are no longer available, then the original permission error - # should not be restored. - filename = os_helper.TESTFN - self.addCleanup(os_helper.unlink, filename) - deadline = time.time() + 5 - command = textwrap.dedent("""\ - import os - import sys - import time - - filename = sys.argv[1] - deadline = float(sys.argv[2]) - - while time.time() < deadline: - try: - with open(filename, "w") as f: - pass - except OSError: - pass - try: - os.remove(filename) - except OSError: - pass - """) - - with subprocess.Popen([sys.executable, '-c', command, filename, str(deadline)]) as proc: - while time.time() < deadline: - try: - os.stat(filename) - except FileNotFoundError as e: - assert e.winerror == 2 # ERROR_FILE_NOT_FOUND - try: - proc.wait(1) - except subprocess.TimeoutExpired: - proc.terminate() - - @support.requires_subprocess() - def test_stat_inaccessible_file(self): - filename = os_helper.TESTFN - ICACLS = os.path.expandvars(r"%SystemRoot%\System32\icacls.exe") - - with open(filename, "wb") as f: - f.write(b'Test data') - - stat1 = os.stat(filename) - - try: - # Remove all permissions from the file - subprocess.check_output([ICACLS, filename, "/inheritance:r"], - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as ex: - if support.verbose: - print(ICACLS, filename, "/inheritance:r", "failed.") - print(ex.stdout.decode("oem", "replace").rstrip()) - try: - os.unlink(filename) - except OSError: - pass - self.skipTest("Unable to create inaccessible file") - - def cleanup(): - # Give delete permission to the owner (us) - subprocess.check_output([ICACLS, filename, "/grant", "*WD:(D)"], - stderr=subprocess.STDOUT) - os.unlink(filename) - - self.addCleanup(cleanup) - - if support.verbose: - print("File:", filename) - print("stat with access:", stat1) - - # First test - we shouldn't raise here, because we still have access to - # the directory and can extract enough information from its metadata. - stat2 = os.stat(filename) - - if support.verbose: - print(" without access:", stat2) - - # We may not get st_dev/st_ino, so ensure those are 0 or match - self.assertIn(stat2.st_dev, (0, stat1.st_dev)) - self.assertIn(stat2.st_ino, (0, stat1.st_ino)) - - # st_mode and st_size should match (for a normal file, at least) - self.assertEqual(stat1.st_mode, stat2.st_mode) - self.assertEqual(stat1.st_size, stat2.st_size) - - # st_ctime and st_mtime should be the same - self.assertEqual(stat1.st_ctime, stat2.st_ctime) - self.assertEqual(stat1.st_mtime, stat2.st_mtime) - - # st_atime should be the same or later - self.assertGreaterEqual(stat1.st_atime, stat2.st_atime) - - @os_helper.skip_unless_symlink class NonLocalSymlinkTests(unittest.TestCase): @@ -3518,6 +3292,7 @@ def test_getppid(self): self.assertEqual(error, b'') self.assertEqual(int(stdout), os.getpid()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def check_waitpid(self, code, exitcode, callback=None): if sys.platform == 'win32': # On Windows, os.spawnv() simply joins arguments with spaces: @@ -3620,30 +3395,35 @@ def create_args(self, *, with_env=False, use_bytes=False): return program, args + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnl') def test_spawnl(self): program, args = self.create_args() exitcode = os.spawnl(os.P_WAIT, program, *args) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnle') def test_spawnle(self): program, args = self.create_args(with_env=True) exitcode = os.spawnle(os.P_WAIT, program, *args, self.env) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnlp') def test_spawnlp(self): program, args = self.create_args() exitcode = os.spawnlp(os.P_WAIT, program, *args) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnlpe') def test_spawnlpe(self): program, args = self.create_args(with_env=True) exitcode = os.spawnlpe(os.P_WAIT, program, *args, self.env) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnv') def test_spawnv(self): program, args = self.create_args() @@ -3654,30 +3434,35 @@ def test_spawnv(self): exitcode = os.spawnv(os.P_WAIT, FakePath(program), args) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnve') def test_spawnve(self): program, args = self.create_args(with_env=True) exitcode = os.spawnve(os.P_WAIT, program, args, self.env) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnvp') def test_spawnvp(self): program, args = self.create_args() exitcode = os.spawnvp(os.P_WAIT, program, args) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnvpe') def test_spawnvpe(self): program, args = self.create_args(with_env=True) exitcode = os.spawnvpe(os.P_WAIT, program, args, self.env) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnv') def test_nowait(self): program, args = self.create_args() pid = os.spawnv(os.P_NOWAIT, program, args) support.wait_process(pid, exitcode=self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnve') def test_spawnve_bytes(self): # Test bytes handling in parse_arglist and parse_envlist (#28114) @@ -3685,18 +3470,21 @@ def test_spawnve_bytes(self): exitcode = os.spawnve(os.P_WAIT, program, args, self.env) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnl') def test_spawnl_noargs(self): program, __ = self.create_args() self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, program) self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, program, '') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnle') def test_spawnle_noargs(self): program, __ = self.create_args() self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, program, {}) self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, program, '', {}) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnv') def test_spawnv_noargs(self): program, __ = self.create_args() @@ -3705,6 +3493,7 @@ def test_spawnv_noargs(self): self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, ('',)) self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, ['']) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnve') def test_spawnve_noargs(self): program, __ = self.create_args() @@ -3761,22 +3550,28 @@ def _test_invalid_env(self, spawn): exitcode = spawn(os.P_WAIT, program, args, newenv) self.assertEqual(exitcode, 0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnve') def test_spawnve_invalid_env(self): self._test_invalid_env(os.spawnve) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnvpe') def test_spawnvpe_invalid_env(self): self._test_invalid_env(os.spawnvpe) -# The introduction of this TestCase caused at least two different errors on -# *nix buildbots. Temporarily skip this to let the buildbots move along. -@unittest.skip("Skip due to platform/environment differences on *NIX buildbots") @unittest.skipUnless(hasattr(os, 'getlogin'), "test needs os.getlogin") class LoginTests(unittest.TestCase): def test_getlogin(self): - user_name = os.getlogin() + try: + user_name = os.getlogin() + except OSError as exc: + # See https://man7.org/linux/man-pages/man3/getlogin.3.html#ERRORS. + if exc.errno in (errno.ENXIO, errno.ENOENT, errno.ENOTTY): + self.skipTest(str(exc)) + else: + raise self.assertNotEqual(len(user_name), 0) @@ -3940,6 +3735,11 @@ async def test_invalid_offset(self): await self.async_sendfile(self.sockno, self.fileno, -1, 4096) self.assertEqual(cm.exception.errno, errno.EINVAL) + async def test_invalid_count(self): + with self.assertRaises(ValueError, msg="count cannot be negative"): + await self.sendfile_wrapper(self.sockno, self.fileno, offset=0, + count=-1) + async def test_keywords(self): # Keyword arguments should be supported await self.async_sendfile(out_fd=self.sockno, in_fd=self.fileno, @@ -3993,7 +3793,6 @@ async def test_trailers(self): @requires_headers_trailers @requires_32b async def test_headers_overflow_32bits(self): - self.server.handler_instance.accumulate = False with self.assertRaises(OSError) as cm: await self.async_sendfile(self.sockno, self.fileno, 0, 0, headers=[b"x" * 2**16] * 2**15) @@ -4002,7 +3801,6 @@ async def test_headers_overflow_32bits(self): @requires_headers_trailers @requires_32b async def test_trailers_overflow_32bits(self): - self.server.handler_instance.accumulate = False with self.assertRaises(OSError) as cm: await self.async_sendfile(self.sockno, self.fileno, 0, 0, trailers=[b"x" * 2**16] * 2**15) @@ -4337,10 +4135,11 @@ def test_timerfd_non_blocking(self): initial_expiration = 0.1 os.timerfd_settime(fd, initial=initial_expiration, interval=0) - # read() raises OSError with errno is EAGAIN for non-blocking timer. - with self.assertRaises(OSError) as ctx: - self.read_count_signaled(fd) - self.assertEqual(ctx.exception.errno, errno.EAGAIN) + if sys.platform != 'cygwin': + # read() raises OSError with errno is EAGAIN for non-blocking timer. + with self.assertRaises(OSError) as ctx: + self.read_count_signaled(fd) + self.assertEqual(ctx.exception.errno, errno.EAGAIN) # Wait more than 0.1 seconds time.sleep(initial_expiration + 0.1) @@ -4521,12 +4320,19 @@ def test_timerfd_ns_initval(self): # 2nd call next_expiration_ns, interval_ns2 = os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns) - self.assertEqual(interval_ns2, interval_ns) + CYGWIN = (sys.platform == 'cygwin') + if not CYGWIN: + self.assertEqual(interval_ns2, interval_ns) + else: + self.assertEqual(interval_ns2, 0) self.assertEqual(next_expiration_ns, initial_expiration_ns) # timerfd_gettime next_expiration_ns, interval_ns2 = os.timerfd_gettime_ns(fd) - self.assertEqual(interval_ns2, interval_ns) + if not CYGWIN: + self.assertEqual(interval_ns2, interval_ns) + else: + self.assertEqual(interval_ns2, 0) self.assertLessEqual(next_expiration_ns, initial_expiration_ns) self.assertAlmostEqual(next_expiration_ns, initial_expiration_ns, delta=limit_error) @@ -4648,6 +4454,7 @@ def test_oserror_filename(self): (self.filenames, os.listdir,), (self.filenames, os.rename, "dst"), (self.filenames, os.replace, "dst"), + (self.filenames, os.utime, None), ] if os_helper.can_chmod(): funcs.append((self.filenames, os.chmod, 0o777)) @@ -4688,6 +4495,19 @@ def test_oserror_filename(self): else: self.fail(f"No exception thrown by {func}") + def test_mkdir(self): + filename = os_helper.TESTFN + subdir = os.path.join(filename, 'subdir') + self.assertRaises(FileNotFoundError, os.mkdir, subdir) + + self.addCleanup(os_helper.unlink, filename) + create_file(filename) + self.assertRaises(FileExistsError, os.mkdir, filename) + + self.assertRaises((NotADirectoryError, FileNotFoundError), + os.mkdir, subdir) + + class CPUCountTests(unittest.TestCase): def check_cpu_count(self, cpus): if cpus is None: @@ -4881,6 +4701,7 @@ def test_posix_pty_functions(self): self.addCleanup(os.close, son_fd) self.assertEqual(os.ptsname(mother_fd), os.ttyname(son_fd)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(hasattr(os, 'spawnl'), "need os.spawnl()") @support.requires_subprocess() def test_pipe_spawnl(self): diff --git a/Lib/test/test_posix.py b/Lib/test/test_os/test_posix.py similarity index 92% rename from Lib/test/test_posix.py rename to Lib/test/test_os/test_posix.py index 628920e34b586f..1395156539a163 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_os/test_posix.py @@ -142,8 +142,8 @@ def test_initgroups(self): self.assertRaises(TypeError, posix.initgroups, "foo", 3, object()) # If a non-privileged user invokes it, it should fail with OSError - # EPERM. - if os.getuid() != 0: + # EPERM. On Cygwin, initgroups(name, 13) does not fail. + if os.getuid() != 0 and sys.platform != 'cygwin': try: name = pwd.getpwuid(posix.getuid()).pw_name except KeyError: @@ -597,7 +597,9 @@ def test_sysconf(self): posix.sysconf(1.23) arg_max = posix.sysconf("SC_ARG_MAX") - self.assertGreater(arg_max, 0) + # SC_ARG_MAX is -1 on Cygwin + if sys.platform != 'cygwin': + self.assertGreater(arg_max, 0) self.assertEqual( posix.sysconf(posix.sysconf_names["SC_ARG_MAX"]), arg_max) @@ -668,22 +670,77 @@ def test_fstat(self): finally: fp.close() - def test_stat(self): - self.assertTrue(posix.stat(os_helper.TESTFN)) - self.assertTrue(posix.stat(os.fsencode(os_helper.TESTFN))) + @unittest.skipUnless(hasattr(posix, 'stat'), + 'test needs posix.stat()') + @unittest.skipUnless(os.stat in os.supports_follow_symlinks, + 'test needs follow_symlinks support in os.stat()') + def test_stat_fd_zero_follow_symlinks(self): + with self.assertRaisesRegex(ValueError, + 'cannot use fd and follow_symlinks together'): + posix.stat(0, follow_symlinks=False) + with self.assertRaisesRegex(ValueError, + 'cannot use fd and follow_symlinks together'): + posix.stat(1, follow_symlinks=False) + + def check_statlike_path(self, func): + self.assertTrue(func(os_helper.TESTFN)) + self.assertTrue(func(os.fsencode(os_helper.TESTFN))) + self.assertTrue(func(os_helper.FakePath(os_helper.TESTFN))) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, bytearray(os.fsencode(os_helper.TESTFN))) + func, bytearray(os.fsencode(os_helper.TESTFN))) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, None) + func, None) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, list(os_helper.TESTFN)) + func, list(os_helper.TESTFN)) self.assertRaisesRegex(TypeError, 'should be string, bytes, os.PathLike or integer, not', - posix.stat, list(os.fsencode(os_helper.TESTFN))) + func, list(os.fsencode(os_helper.TESTFN))) + + def test_stat(self): + self.check_statlike_path(posix.stat) + + @unittest.skipUnless(hasattr(posix, 'statx'), 'test needs posix.statx()') + def test_statx(self): + def func(path, **kwargs): + return posix.statx(path, posix.STATX_BASIC_STATS, **kwargs) + self.check_statlike_path(func) + + @unittest.skipUnless(hasattr(posix, 'statx'), 'test needs posix.statx()') + def test_statx_flags(self): + # glibc's fallback implementation of statx via the stat family fails + # with EINVAL on the (nonzero) sync flags. If you see this failure, + # update your kernel and/or seccomp syscall filter. + valid_flag_names = ('AT_NO_AUTOMOUNT', 'AT_STATX_SYNC_AS_STAT', + 'AT_STATX_FORCE_SYNC', 'AT_STATX_DONT_SYNC') + for flag_name in valid_flag_names: + flag = getattr(posix, flag_name) + with self.subTest(msg=flag_name, flags=flag): + posix.statx(os_helper.TESTFN, posix.STATX_BASIC_STATS, + flags=flag) + + # These flags are not exposed to Python because their functionality is + # implemented via kwargs instead. + kwarg_equivalent_flags = ( + (0x0100, 'AT_SYMLINK_NOFOLLOW', 'follow_symlinks'), + (0x0400, 'AT_SYMLINK_FOLLOW', 'follow_symlinks'), + (0x1000, 'AT_EMPTY_PATH', 'dir_fd'), + ) + for flag, flag_name, kwarg_name in kwarg_equivalent_flags: + with self.subTest(msg=flag_name, flags=flag): + with self.assertRaisesRegex(ValueError, kwarg_name): + posix.statx(os_helper.TESTFN, posix.STATX_BASIC_STATS, + flags=flag) + + with self.subTest(msg="AT_STATX_FORCE_SYNC | AT_STATX_DONT_SYNC"): + with self.assertRaises(OSError) as ctx: + flags = posix.AT_STATX_FORCE_SYNC | posix.AT_STATX_DONT_SYNC + posix.statx(os_helper.TESTFN, posix.STATX_BASIC_STATS, + flags=flags) + self.assertEqual(ctx.exception.errno, errno.EINVAL) @unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()") def test_mkfifo(self): @@ -757,12 +814,30 @@ def test_makedev(self): self.assertRaises((ValueError, OverflowError), posix.makedev, x, minor) self.assertRaises((ValueError, OverflowError), posix.makedev, major, x) - if sys.platform == 'linux': - NODEV = -1 + # The following tests are needed to test functions accepting or + # returning the special value NODEV (if it is defined). major(), minor() + # and makefile() are the only easily reproducible examples, but that + # behavior is platform specific -- on some platforms their code has + # a special case for NODEV, on others this is just an implementation + # artifact. + if (hasattr(posix, 'NODEV') and + sys.platform.startswith(('linux', 'macos', 'freebsd', 'dragonfly', + 'sunos'))): + NODEV = posix.NODEV self.assertEqual(posix.major(NODEV), NODEV) self.assertEqual(posix.minor(NODEV), NODEV) self.assertEqual(posix.makedev(NODEV, NODEV), NODEV) + def test_nodev(self): + # NODEV is not a part of Posix, but is defined on many systems. + if (not hasattr(posix, 'NODEV') + and (not sys.platform.startswith(('linux', 'macos', 'freebsd', + 'dragonfly', 'netbsd', 'openbsd', + 'sunos')) + or support.linked_to_musl())): + self.skipTest('not defined on this platform') + self.assertHasAttr(posix, 'NODEV') + def _test_all_chown_common(self, chown_func, first_param, stat_func): """Common code for chown, fchown and lchown tests.""" def check_stat(uid, gid): @@ -1366,6 +1441,14 @@ def test_sched_param(self): self.assertNotEqual(newparam, param) self.assertEqual(newparam.sched_priority, 0) + @requires_sched + def test_bug_140634(self): + sched_priority = float('inf') # any new reference + param = posix.sched_param(sched_priority) + param.__reduce__() + del sched_priority, param # should not crash + support.gc_collect() # just to be sure + @unittest.skipUnless(hasattr(posix, "sched_rr_get_interval"), "no function") def test_sched_rr_get_interval(self): try: @@ -1521,6 +1604,34 @@ def test_pidfd_open(self): self.assertEqual(cm.exception.errno, errno.EINVAL) os.close(os.pidfd_open(os.getpid(), 0)) + @unittest.skipUnless(hasattr(os, "pidfd_getfd"), "pidfd_getfd unavailable") + def test_pidfd_getfd(self): + fd = os.open(__file__, os.O_RDONLY) + self.addCleanup(os.close, fd) + pidfd = os.pidfd_open(os.getpid(), 0) + self.addCleanup(os.close, pidfd) + try: + dupfd = os.pidfd_getfd(pidfd, fd) + except OSError as exc: + if exc.errno == errno.ENOSYS: + self.skipTest("system does not support pidfd_getfd") + if isinstance(exc, PermissionError): + self.skipTest(f"pidfd_getfd syscall blocked: {exc!r}") + raise + self.addCleanup(os.close, dupfd) + + self.assertFalse(os.get_inheritable(dupfd)) # PEP 446 + self.assertEqual(os.fstat(fd), os.fstat(dupfd)) + + with self.assertRaises(OSError) as cm: + os.pidfd_getfd(-1, 0) + self.assertEqual(cm.exception.errno, errno.EBADF) + + with self.assertRaises(OSError) as cm: + bad_fd = os_helper.make_bad_fd() + os.pidfd_getfd(pidfd, bad_fd) + self.assertEqual(cm.exception.errno, errno.EBADF) + @os_helper.skip_unless_hardlink @os_helper.skip_unless_symlink def test_link_follow_symlinks(self): @@ -1611,33 +1722,47 @@ def test_chown_dir_fd(self): with self.prepare_file() as (dir_fd, name, fullname): posix.chown(name, os.getuid(), os.getgid(), dir_fd=dir_fd) - @unittest.skipUnless(os.stat in os.supports_dir_fd, "test needs dir_fd support in os.stat()") - def test_stat_dir_fd(self): + def check_statlike_dir_fd(self, func, prefix): with self.prepare() as (dir_fd, name, fullname): with open(fullname, 'w') as outfile: outfile.write("testline\n") self.addCleanup(posix.unlink, fullname) - s1 = posix.stat(fullname) - s2 = posix.stat(name, dir_fd=dir_fd) - self.assertEqual(s1, s2) - s2 = posix.stat(fullname, dir_fd=None) - self.assertEqual(s1, s2) + def get(result, attr): + return getattr(result, prefix + attr) + + s1 = func(fullname) + s2 = func(name, dir_fd=dir_fd) + self.assertEqual((get(s1, "dev"), get(s1, "ino")), + (get(s2, "dev"), get(s2, "ino"))) + s2 = func(fullname, dir_fd=None) + self.assertEqual((get(s1, "dev"), get(s1, "ino")), + (get(s2, "dev"), get(s2, "ino"))) self.assertRaisesRegex(TypeError, 'should be integer or None, not', - posix.stat, name, dir_fd=posix.getcwd()) + func, name, dir_fd=posix.getcwd()) self.assertRaisesRegex(TypeError, 'should be integer or None, not', - posix.stat, name, dir_fd=float(dir_fd)) + func, name, dir_fd=float(dir_fd)) self.assertRaises(OverflowError, - posix.stat, name, dir_fd=10**20) + func, name, dir_fd=10**20) for fd in False, True: with self.assertWarnsRegex(RuntimeWarning, 'bool is used as a file descriptor') as cm: with self.assertRaises(OSError): - posix.stat('nonexisting', dir_fd=fd) + func('nonexisting', dir_fd=fd) self.assertEqual(cm.filename, __file__) + @unittest.skipUnless(os.stat in os.supports_dir_fd, "test needs dir_fd support in os.stat()") + def test_stat_dir_fd(self): + self.check_statlike_dir_fd(posix.stat, prefix="st_") + + @unittest.skipUnless(hasattr(posix, 'statx'), "test needs os.statx()") + def test_statx_dir_fd(self): + def func(path, **kwargs): + return posix.statx(path, os.STATX_INO, **kwargs) + self.check_statlike_dir_fd(func, prefix="stx_") + @unittest.skipUnless(os.utime in os.supports_dir_fd, "test needs dir_fd support in os.utime()") def test_utime_dir_fd(self): with self.prepare_file() as (dir_fd, name, fullname): @@ -1848,6 +1973,14 @@ def test_no_such_executable(self): # directories in the $PATH that are not accessible. except (FileNotFoundError, PermissionError) as exc: self.assertEqual(exc.filename, no_such_executable) + + # On Cygwin, os.posix_spawn() creates a child process even if the + # executable doesn't exist. We have to reap this process. + if sys.platform == 'cygwin': + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + pid, status = os.waitpid(-1, os.WNOHANG) + if pid != 0: + break else: pid2, status = os.waitpid(pid, 0) self.assertEqual(pid2, pid) @@ -1913,6 +2046,11 @@ def test_setpgroup(self): ) support.wait_process(pid, exitcode=0) + def test_setpgroup_allow_none(self): + path, args = self.NOOP_PROGRAM[0], self.NOOP_PROGRAM + pid = self.spawn_func(path, args, os.environ, setpgroup=None) + support.wait_process(pid, exitcode=0) + def test_setpgroup_wrong_type(self): with self.assertRaises(TypeError): self.spawn_func(sys.executable, @@ -2013,6 +2151,20 @@ def test_setsigdef_wrong_type(self): [sys.executable, "-c", "pass"], os.environ, setsigdef=[signal.NSIG, signal.NSIG+1]) + def test_scheduler_allow_none(self): + path, args = self.NOOP_PROGRAM[0], self.NOOP_PROGRAM + pid = self.spawn_func(path, args, os.environ, scheduler=None) + support.wait_process(pid, exitcode=0) + + @support.subTests("scheduler", [object(), 1, [1, 2]]) + def test_scheduler_wrong_type(self, scheduler): + path, args = self.NOOP_PROGRAM[0], self.NOOP_PROGRAM + with self.assertRaisesRegex( + TypeError, + "scheduler must be a tuple or None", + ): + self.spawn_func(path, args, os.environ, scheduler=scheduler) + @requires_sched @unittest.skipIf(sys.platform.startswith(('freebsd', 'netbsd')), "bpo-34685: test can fail on BSD") @@ -2036,6 +2188,12 @@ def test_setscheduler_only_param(self): @requires_sched @unittest.skipIf(sys.platform.startswith(('freebsd', 'netbsd')), "bpo-34685: test can fail on BSD") + @unittest.skipIf(platform.libc_ver()[0] == 'glibc' and + os.sched_getscheduler(0) in [ + os.SCHED_BATCH, + os.SCHED_IDLE, + os.SCHED_DEADLINE], + "Skip test due to glibc posix_spawn policy") def test_setscheduler_with_policy(self): policy = os.sched_getscheduler(0) priority = os.sched_get_priority_min(policy) diff --git a/Lib/test/test_os/test_windows.py b/Lib/test/test_os/test_windows.py new file mode 100644 index 00000000000000..f1c6283f60d35e --- /dev/null +++ b/Lib/test/test_os/test_windows.py @@ -0,0 +1,605 @@ +import sys +import unittest + +if sys.platform != "win32": + raise unittest.SkipTest("Win32 specific tests") + +import _winapi +import fnmatch +import mmap +import os +import shutil +import signal +import stat +import subprocess +import textwrap +import time +import uuid +from test import support +from test.support import import_helper +from test.support import os_helper +from .utils import create_file + + +class Win32KillTests(unittest.TestCase): + def _kill(self, sig): + # Start sys.executable as a subprocess and communicate from the + # subprocess to the parent that the interpreter is ready. When it + # becomes ready, send *sig* via os.kill to the subprocess and check + # that the return code is equal to *sig*. + import ctypes + from ctypes import wintypes + import msvcrt + + # Since we can't access the contents of the process' stdout until the + # process has exited, use PeekNamedPipe to see what's inside stdout + # without waiting. This is done so we can tell that the interpreter + # is started and running at a point where it could handle a signal. + PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe + PeekNamedPipe.restype = wintypes.BOOL + PeekNamedPipe.argtypes = (wintypes.HANDLE, # Pipe handle + ctypes.POINTER(ctypes.c_char), # stdout buf + wintypes.DWORD, # Buffer size + ctypes.POINTER(wintypes.DWORD), # bytes read + ctypes.POINTER(wintypes.DWORD), # bytes avail + ctypes.POINTER(wintypes.DWORD)) # bytes left + msg = "running" + proc = subprocess.Popen([sys.executable, "-c", + "import sys;" + "sys.stdout.write('{}');" + "sys.stdout.flush();" + "input()".format(msg)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE) + self.addCleanup(proc.stdout.close) + self.addCleanup(proc.stderr.close) + self.addCleanup(proc.stdin.close) + + count, max = 0, 100 + while count < max and proc.poll() is None: + # Create a string buffer to store the result of stdout from the pipe + buf = ctypes.create_string_buffer(len(msg)) + # Obtain the text currently in proc.stdout + # Bytes read/avail/left are left as NULL and unused + rslt = PeekNamedPipe(msvcrt.get_osfhandle(proc.stdout.fileno()), + buf, ctypes.sizeof(buf), None, None, None) + self.assertNotEqual(rslt, 0, "PeekNamedPipe failed") + if buf.value: + self.assertEqual(msg, buf.value.decode()) + break + time.sleep(0.1) + count += 1 + else: + self.fail("Did not receive communication from the subprocess") + + os.kill(proc.pid, sig) + self.assertEqual(proc.wait(), sig) + + def test_kill_sigterm(self): + # SIGTERM doesn't mean anything special, but make sure it works + self._kill(signal.SIGTERM) + + def test_kill_int(self): + # os.kill on Windows can take an int which gets set as the exit code + self._kill(100) + + @unittest.skipIf(mmap is None, "requires mmap") + def _kill_with_event(self, event, name): + tagname = "test_os_%s" % uuid.uuid1() + m = mmap.mmap(-1, 1, tagname) + m[0] = 0 + + # Run a script which has console control handling enabled. + script = os.path.join(os.path.dirname(__file__), + "win_console_handler.py") + cmd = [sys.executable, script, tagname] + proc = subprocess.Popen(cmd, + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) + + with proc: + # Let the interpreter startup before we send signals. See #3137. + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if proc.poll() is None: + break + else: + # Forcefully kill the process if we weren't able to signal it. + proc.kill() + self.fail("Subprocess didn't finish initialization") + + os.kill(proc.pid, event) + + try: + # proc.send_signal(event) could also be done here. + # Allow time for the signal to be passed and the process to exit. + proc.wait(timeout=support.SHORT_TIMEOUT) + except subprocess.TimeoutExpired: + # Forcefully kill the process if we weren't able to signal it. + proc.kill() + self.fail("subprocess did not stop on {}".format(name)) + + @unittest.skip("subprocesses aren't inheriting Ctrl+C property") + @support.requires_subprocess() + def test_CTRL_C_EVENT(self): + from ctypes import wintypes + import ctypes + + # Make a NULL value by creating a pointer with no argument. + NULL = ctypes.POINTER(ctypes.c_int)() + SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler + SetConsoleCtrlHandler.argtypes = (ctypes.POINTER(ctypes.c_int), + wintypes.BOOL) + SetConsoleCtrlHandler.restype = wintypes.BOOL + + # Calling this with NULL and FALSE causes the calling process to + # handle Ctrl+C, rather than ignore it. This property is inherited + # by subprocesses. + SetConsoleCtrlHandler(NULL, 0) + + self._kill_with_event(signal.CTRL_C_EVENT, "CTRL_C_EVENT") + + @support.requires_subprocess() + def test_CTRL_BREAK_EVENT(self): + self._kill_with_event(signal.CTRL_BREAK_EVENT, "CTRL_BREAK_EVENT") + + +class Win32ListdirTests(unittest.TestCase): + """Test listdir on Windows.""" + + def setUp(self): + self.created_paths = [] + for i in range(2): + dir_name = 'SUB%d' % i + dir_path = os.path.join(os_helper.TESTFN, dir_name) + file_name = 'FILE%d' % i + file_path = os.path.join(os_helper.TESTFN, file_name) + os.makedirs(dir_path) + with open(file_path, 'w', encoding='utf-8') as f: + f.write("I'm %s and proud of it. Blame test_os.\n" % file_path) + self.created_paths.extend([dir_name, file_name]) + self.created_paths.sort() + + def tearDown(self): + shutil.rmtree(os_helper.TESTFN) + + def test_listdir_no_extended_path(self): + """Test when the path is not an "extended" path.""" + # unicode + self.assertEqual( + sorted(os.listdir(os_helper.TESTFN)), + self.created_paths) + + # bytes + self.assertEqual( + sorted(os.listdir(os.fsencode(os_helper.TESTFN))), + [os.fsencode(path) for path in self.created_paths]) + + def test_listdir_extended_path(self): + """Test when the path starts with '\\\\?\\'.""" + # See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath + # unicode + path = '\\\\?\\' + os.path.abspath(os_helper.TESTFN) + self.assertEqual( + sorted(os.listdir(path)), + self.created_paths) + + # bytes + path = b'\\\\?\\' + os.fsencode(os.path.abspath(os_helper.TESTFN)) + self.assertEqual( + sorted(os.listdir(path)), + [os.fsencode(path) for path in self.created_paths]) + + +@unittest.skipUnless(os.name == "nt", "NT specific tests") +class Win32ListdriveTests(unittest.TestCase): + """Test listdrive, listmounts and listvolume on Windows.""" + + def setUp(self): + # Get drives and volumes from fsutil + out = subprocess.check_output( + ["fsutil.exe", "volume", "list"], + cwd=os.path.join(os.getenv("SystemRoot", "\\Windows"), "System32"), + encoding="mbcs", + errors="ignore", + ) + lines = out.splitlines() + self.known_volumes = {l for l in lines if l.startswith('\\\\?\\')} + self.known_drives = {l for l in lines if l[1:] == ':\\'} + self.known_mounts = {l for l in lines if l[1:3] == ':\\'} + + def test_listdrives(self): + drives = os.listdrives() + self.assertIsInstance(drives, list) + self.assertSetEqual( + self.known_drives, + self.known_drives & set(drives), + ) + + def test_listvolumes(self): + volumes = os.listvolumes() + self.assertIsInstance(volumes, list) + self.assertSetEqual( + self.known_volumes, + self.known_volumes & set(volumes), + ) + + def test_listmounts(self): + for volume in os.listvolumes(): + try: + mounts = os.listmounts(volume) + except OSError as ex: + if support.verbose: + print("Skipping", volume, "because of", ex) + else: + self.assertIsInstance(mounts, list) + self.assertSetEqual( + set(mounts), + self.known_mounts & set(mounts), + ) + + +@os_helper.skip_unless_symlink +class Win32SymlinkTests(unittest.TestCase): + filelink = 'filelinktest' + filelink_target = os.path.abspath(__file__) + dirlink = 'dirlinktest' + dirlink_target = os.path.dirname(filelink_target) + missing_link = 'missing link' + + def setUp(self): + assert os.path.exists(self.dirlink_target) + assert os.path.exists(self.filelink_target) + assert not os.path.exists(self.dirlink) + assert not os.path.exists(self.filelink) + assert not os.path.exists(self.missing_link) + + def tearDown(self): + if os.path.exists(self.filelink): + os.remove(self.filelink) + if os.path.exists(self.dirlink): + os.rmdir(self.dirlink) + if os.path.lexists(self.missing_link): + os.remove(self.missing_link) + + def test_directory_link(self): + os.symlink(self.dirlink_target, self.dirlink) + self.assertTrue(os.path.exists(self.dirlink)) + self.assertTrue(os.path.isdir(self.dirlink)) + self.assertTrue(os.path.islink(self.dirlink)) + self.check_stat(self.dirlink, self.dirlink_target) + + def test_file_link(self): + os.symlink(self.filelink_target, self.filelink) + self.assertTrue(os.path.exists(self.filelink)) + self.assertTrue(os.path.isfile(self.filelink)) + self.assertTrue(os.path.islink(self.filelink)) + self.check_stat(self.filelink, self.filelink_target) + + def _create_missing_dir_link(self): + 'Create a "directory" link to a non-existent target' + linkname = self.missing_link + if os.path.lexists(linkname): + os.remove(linkname) + target = r'c:\\target does not exist.29r3c740' + assert not os.path.exists(target) + target_is_dir = True + os.symlink(target, linkname, target_is_dir) + + def test_remove_directory_link_to_missing_target(self): + self._create_missing_dir_link() + # For compatibility with Unix, os.remove will check the + # directory status and call RemoveDirectory if the symlink + # was created with target_is_dir==True. + os.remove(self.missing_link) + + def test_isdir_on_directory_link_to_missing_target(self): + self._create_missing_dir_link() + self.assertFalse(os.path.isdir(self.missing_link)) + + def test_rmdir_on_directory_link_to_missing_target(self): + self._create_missing_dir_link() + os.rmdir(self.missing_link) + + def check_stat(self, link, target): + self.assertEqual(os.stat(link), os.stat(target)) + self.assertNotEqual(os.lstat(link), os.stat(link)) + + bytes_link = os.fsencode(link) + self.assertEqual(os.stat(bytes_link), os.stat(target)) + self.assertNotEqual(os.lstat(bytes_link), os.stat(bytes_link)) + + def test_12084(self): + level1 = os.path.abspath(os_helper.TESTFN) + level2 = os.path.join(level1, "level2") + level3 = os.path.join(level2, "level3") + self.addCleanup(os_helper.rmtree, level1) + + os.mkdir(level1) + os.mkdir(level2) + os.mkdir(level3) + + file1 = os.path.abspath(os.path.join(level1, "file1")) + create_file(file1) + + orig_dir = os.getcwd() + try: + os.chdir(level2) + link = os.path.join(level2, "link") + os.symlink(os.path.relpath(file1), "link") + self.assertIn("link", os.listdir(os.getcwd())) + + # Check os.stat calls from the same dir as the link + self.assertEqual(os.stat(file1), os.stat("link")) + + # Check os.stat calls from a dir below the link + os.chdir(level1) + self.assertEqual(os.stat(file1), + os.stat(os.path.relpath(link))) + + # Check os.stat calls from a dir above the link + os.chdir(level3) + self.assertEqual(os.stat(file1), + os.stat(os.path.relpath(link))) + finally: + os.chdir(orig_dir) + + @unittest.skipUnless(os.path.lexists(r'C:\Users\All Users') + and os.path.exists(r'C:\ProgramData'), + 'Test directories not found') + def test_29248(self): + # os.symlink() calls CreateSymbolicLink, which creates + # the reparse data buffer with the print name stored + # first, so the offset is always 0. CreateSymbolicLink + # stores the "PrintName" DOS path (e.g. "C:\") first, + # with an offset of 0, followed by the "SubstituteName" + # NT path (e.g. "\??\C:\"). The "All Users" link, on + # the other hand, seems to have been created manually + # with an inverted order. + target = os.readlink(r'C:\Users\All Users') + self.assertTrue(os.path.samefile(target, r'C:\ProgramData')) + + def test_buffer_overflow(self): + # Older versions would have a buffer overflow when detecting + # whether a link source was a directory. This test ensures we + # no longer crash, but does not otherwise validate the behavior + segment = 'X' * 27 + path = os.path.join(*[segment] * 10) + test_cases = [ + # overflow with absolute src + ('\\' + path, segment), + # overflow dest with relative src + (segment, path), + # overflow when joining src + (path[:180], path[:180]), + ] + for src, dest in test_cases: + try: + os.symlink(src, dest) + except FileNotFoundError: + pass + else: + try: + os.remove(dest) + except OSError: + pass + # Also test with bytes, since that is a separate code path. + try: + os.symlink(os.fsencode(src), os.fsencode(dest)) + except FileNotFoundError: + pass + else: + try: + os.remove(dest) + except OSError: + pass + + def test_appexeclink(self): + root = os.path.expandvars(r'%LOCALAPPDATA%\Microsoft\WindowsApps') + if not os.path.isdir(root): + self.skipTest("test requires a WindowsApps directory") + + aliases = [os.path.join(root, a) + for a in fnmatch.filter(os.listdir(root), '*.exe')] + + for alias in aliases: + if support.verbose: + print() + print("Testing with", alias) + st = os.lstat(alias) + self.assertEqual(st, os.stat(alias)) + self.assertFalse(stat.S_ISLNK(st.st_mode)) + self.assertEqual(st.st_reparse_tag, stat.IO_REPARSE_TAG_APPEXECLINK) + self.assertTrue(os.path.isfile(alias)) + # testing the first one we see is sufficient + break + else: + self.skipTest("test requires an app execution alias") + + +class Win32JunctionTests(unittest.TestCase): + junction = 'junctiontest' + junction_target = os.path.dirname(os.path.abspath(__file__)) + + def setUp(self): + assert os.path.exists(self.junction_target) + assert not os.path.lexists(self.junction) + + def tearDown(self): + if os.path.lexists(self.junction): + os.unlink(self.junction) + + def test_create_junction(self): + _winapi.CreateJunction(self.junction_target, self.junction) + self.assertTrue(os.path.lexists(self.junction)) + self.assertTrue(os.path.exists(self.junction)) + self.assertTrue(os.path.isdir(self.junction)) + self.assertNotEqual(os.stat(self.junction), os.lstat(self.junction)) + self.assertEqual(os.stat(self.junction), os.stat(self.junction_target)) + + # bpo-37834: Junctions are not recognized as links. + self.assertFalse(os.path.islink(self.junction)) + self.assertEqual(os.path.normcase("\\\\?\\" + self.junction_target), + os.path.normcase(os.readlink(self.junction))) + + def test_unlink_removes_junction(self): + _winapi.CreateJunction(self.junction_target, self.junction) + self.assertTrue(os.path.exists(self.junction)) + self.assertTrue(os.path.lexists(self.junction)) + + os.unlink(self.junction) + self.assertFalse(os.path.exists(self.junction)) + + +class Win32NtTests(unittest.TestCase): + def test_getfinalpathname_handles(self): + nt = import_helper.import_module('nt') + ctypes = import_helper.import_module('ctypes') + # Ruff false positive -- it thinks we're redefining `ctypes` here + import ctypes.wintypes # noqa: F811 + + kernel = ctypes.WinDLL('Kernel32.dll', use_last_error=True) + kernel.GetCurrentProcess.restype = ctypes.wintypes.HANDLE + + kernel.GetProcessHandleCount.restype = ctypes.wintypes.BOOL + kernel.GetProcessHandleCount.argtypes = (ctypes.wintypes.HANDLE, + ctypes.wintypes.LPDWORD) + + # This is a pseudo-handle that doesn't need to be closed + hproc = kernel.GetCurrentProcess() + + handle_count = ctypes.wintypes.DWORD() + ok = kernel.GetProcessHandleCount(hproc, ctypes.byref(handle_count)) + self.assertEqual(1, ok) + + before_count = handle_count.value + + # The first two test the error path, __file__ tests the success path + filenames = [ + r'\\?\C:', + r'\\?\NUL', + r'\\?\CONIN', + __file__, + ] + + for _ in range(10): + for name in filenames: + try: + nt._getfinalpathname(name) + except Exception: + # Failure is expected + pass + try: + os.stat(name) + except Exception: + pass + + ok = kernel.GetProcessHandleCount(hproc, ctypes.byref(handle_count)) + self.assertEqual(1, ok) + + handle_delta = handle_count.value - before_count + + self.assertEqual(0, handle_delta) + + @support.requires_subprocess() + def test_stat_unlink_race(self): + # bpo-46785: the implementation of os.stat() falls back to reading + # the parent directory if CreateFileW() fails with a permission + # error. If reading the parent directory fails because the file or + # directory are subsequently unlinked, or because the volume or + # share are no longer available, then the original permission error + # should not be restored. + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) + deadline = time.time() + 5 + command = textwrap.dedent("""\ + import os + import sys + import time + + filename = sys.argv[1] + deadline = float(sys.argv[2]) + + while time.time() < deadline: + try: + with open(filename, "w") as f: + pass + except OSError: + pass + try: + os.remove(filename) + except OSError: + pass + """) + + with subprocess.Popen([sys.executable, '-c', command, filename, str(deadline)]) as proc: + while time.time() < deadline: + try: + os.stat(filename) + except FileNotFoundError as e: + assert e.winerror == 2 # ERROR_FILE_NOT_FOUND + try: + proc.wait(1) + except subprocess.TimeoutExpired: + proc.terminate() + + @support.requires_subprocess() + def test_stat_inaccessible_file(self): + filename = os_helper.TESTFN + ICACLS = os.path.expandvars(r"%SystemRoot%\System32\icacls.exe") + + with open(filename, "wb") as f: + f.write(b'Test data') + + stat1 = os.stat(filename) + + try: + # Remove all permissions from the file + subprocess.check_output([ICACLS, filename, "/inheritance:r"], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as ex: + if support.verbose: + print(ICACLS, filename, "/inheritance:r", "failed.") + print(ex.stdout.decode("oem", "replace").rstrip()) + try: + os.unlink(filename) + except OSError: + pass + self.skipTest("Unable to create inaccessible file") + + def cleanup(): + # Give delete permission to the owner (us) + subprocess.check_output([ICACLS, filename, "/grant", "*WD:(D)"], + stderr=subprocess.STDOUT) + os.unlink(filename) + + self.addCleanup(cleanup) + + if support.verbose: + print("File:", filename) + print("stat with access:", stat1) + + # First test - we shouldn't raise here, because we still have access to + # the directory and can extract enough information from its metadata. + stat2 = os.stat(filename) + + if support.verbose: + print(" without access:", stat2) + + # We may not get st_dev/st_ino, so ensure those are 0 or match + self.assertIn(stat2.st_dev, (0, stat1.st_dev)) + self.assertIn(stat2.st_ino, (0, stat1.st_ino)) + + # st_mode and st_size should match (for a normal file, at least) + self.assertEqual(stat1.st_mode, stat2.st_mode) + self.assertEqual(stat1.st_size, stat2.st_size) + + # st_ctime and st_mtime should be the same + self.assertEqual(stat1.st_ctime, stat2.st_ctime) + self.assertEqual(stat1.st_mtime, stat2.st_mtime) + + # st_atime should be the same or later + self.assertGreaterEqual(stat1.st_atime, stat2.st_atime) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_os/utils.py b/Lib/test/test_os/utils.py new file mode 100644 index 00000000000000..e0c39598c316c8 --- /dev/null +++ b/Lib/test/test_os/utils.py @@ -0,0 +1,3 @@ +def create_file(filename, content=b'content'): + with open(filename, "xb", 0) as fp: + fp.write(content) diff --git a/Lib/test/test_pathlib/support/local_path.py b/Lib/test/test_pathlib/support/local_path.py index c1423c545bfd00..ddfd6fd419533c 100644 --- a/Lib/test/test_pathlib/support/local_path.py +++ b/Lib/test/test_pathlib/support/local_path.py @@ -145,7 +145,7 @@ def __init__(self, *pathsegments): super().__init__(*pathsegments) self.info = LocalPathInfo(self) - def __open_rb__(self, buffering=-1): + def __open_reader__(self): return open(self, 'rb') def iterdir(self): @@ -163,8 +163,8 @@ class WritableLocalPath(_WritablePath, LexicalPath): __slots__ = () __fspath__ = LexicalPath.__vfspath__ - def __open_wb__(self, buffering=-1): - return open(self, 'wb') + def __open_writer__(self, mode): + return open(self, f'{mode}b') def mkdir(self, mode=0o777): os.mkdir(self, mode) diff --git a/Lib/test/test_pathlib/support/zip_path.py b/Lib/test/test_pathlib/support/zip_path.py index 21e1d07423aff5..90b939b6a59010 100644 --- a/Lib/test/test_pathlib/support/zip_path.py +++ b/Lib/test/test_pathlib/support/zip_path.py @@ -264,13 +264,13 @@ def info(self): tree = self.zip_file.filelist.tree return tree.resolve(vfspath(self), follow_symlinks=False) - def __open_rb__(self, buffering=-1): + def __open_reader__(self): info = self.info.resolve() if not info.exists(): raise FileNotFoundError(errno.ENOENT, "File not found", self) elif info.is_dir(): raise IsADirectoryError(errno.EISDIR, "Is a directory", self) - return self.zip_file.open(info.zip_info, 'r') + return self.zip_file.open(info.zip_info) def iterdir(self): info = self.info.resolve() @@ -320,8 +320,8 @@ def __repr__(self): def with_segments(self, *pathsegments): return type(self)(*pathsegments, zip_file=self.zip_file) - def __open_wb__(self, buffering=-1): - return self.zip_file.open(vfspath(self), 'w') + def __open_writer__(self, mode): + return self.zip_file.open(vfspath(self), mode) def mkdir(self, mode=0o777): zinfo = zipfile.ZipInfo(vfspath(self) + '/') @@ -334,4 +334,4 @@ def symlink_to(self, target, target_is_directory=False): zinfo.external_attr = stat.S_IFLNK << 16 if target_is_directory: zinfo.external_attr |= 0x10 - self.zip_file.writestr(zinfo, vfspath(target)) + self.zip_file.writestr(zinfo, target) diff --git a/Lib/test/test_pathlib/test_join.py b/Lib/test/test_pathlib/test_join.py index f1a24204b4c30a..4630210e492291 100644 --- a/Lib/test/test_pathlib/test_join.py +++ b/Lib/test/test_pathlib/test_join.py @@ -3,6 +3,8 @@ """ import unittest +import threading +from test.support import threading_helper from .support import is_pypi from .support.lexical_path import LexicalPath @@ -158,6 +160,26 @@ def test_parts(self): parts = p.parts self.assertEqual(parts, (sep, 'a', 'b')) + @threading_helper.requires_working_threading() + def test_parts_multithreaded(self): + P = self.cls + + NUM_THREADS = 10 + NUM_ITERS = 10 + + for _ in range(NUM_ITERS): + b = threading.Barrier(NUM_THREADS) + path = P('a') / 'b' / 'c' / 'd' / 'e' + expected = ('a', 'b', 'c', 'd', 'e') + + def check_parts(): + b.wait() + self.assertEqual(path.parts, expected) + + threads = [threading.Thread(target=check_parts) for _ in range(NUM_THREADS)] + with threading_helper.start_threads(threads): + pass + def test_parent(self): # Relative P = self.cls @@ -354,6 +376,61 @@ def test_with_suffix(self): self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.') self.assertRaises(TypeError, P('a/b').with_suffix, None) + def test_relative_to(self): + P = self.cls + p = P('a/b') + self.assertEqual(p.relative_to(P('')), P('a', 'b')) + self.assertEqual(p.relative_to(P('a')), P('b')) + self.assertEqual(p.relative_to(P('a/b')), P('')) + self.assertEqual(p.relative_to(P(''), walk_up=True), P('a', 'b')) + self.assertEqual(p.relative_to(P('a'), walk_up=True), P('b')) + self.assertEqual(p.relative_to(P('a/b'), walk_up=True), P('')) + self.assertEqual(p.relative_to(P('a/c'), walk_up=True), P('..', 'b')) + self.assertEqual(p.relative_to(P('a/b/c'), walk_up=True), P('..')) + self.assertEqual(p.relative_to(P('c'), walk_up=True), P('..', 'a', 'b')) + self.assertRaises(ValueError, p.relative_to, P('c')) + self.assertRaises(ValueError, p.relative_to, P('a/b/c')) + self.assertRaises(ValueError, p.relative_to, P('a/c')) + self.assertRaises(ValueError, p.relative_to, P('/a')) + self.assertRaises(ValueError, p.relative_to, P('../a')) + self.assertRaises(ValueError, p.relative_to, P('a/..')) + self.assertRaises(ValueError, p.relative_to, P('/a/..')) + self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('/a'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('../a'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('a/..'), walk_up=True) + self.assertRaises(ValueError, p.relative_to, P('/a/..'), walk_up=True) + class Q(self.cls): + __eq__ = object.__eq__ + __hash__ = object.__hash__ + q = Q('a/b') + self.assertTrue(q.relative_to(q)) + self.assertRaises(ValueError, q.relative_to, Q('')) + self.assertRaises(ValueError, q.relative_to, Q('a')) + self.assertRaises(ValueError, q.relative_to, Q('a'), walk_up=True) + self.assertRaises(ValueError, q.relative_to, Q('a/b')) + self.assertRaises(ValueError, q.relative_to, Q('c')) + + def test_is_relative_to(self): + P = self.cls + p = P('a/b') + self.assertTrue(p.is_relative_to(P(''))) + self.assertTrue(p.is_relative_to(P('a'))) + self.assertTrue(p.is_relative_to(P('a/b'))) + self.assertFalse(p.is_relative_to(P('c'))) + self.assertFalse(p.is_relative_to(P('a/b/c'))) + self.assertFalse(p.is_relative_to(P('a/c'))) + self.assertFalse(p.is_relative_to(P('/a'))) + class Q(self.cls): + __eq__ = object.__eq__ + __hash__ = object.__hash__ + q = Q('a/b') + self.assertTrue(q.is_relative_to(q)) + self.assertFalse(q.is_relative_to(Q(''))) + self.assertFalse(q.is_relative_to(Q('a'))) + self.assertFalse(q.is_relative_to(Q('a/b'))) + self.assertFalse(q.is_relative_to(Q('c'))) + class LexicalPathJoinTest(JoinTestBase, unittest.TestCase): cls = LexicalPath diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index a1105aae6351b6..2cb4876f5c6400 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -20,6 +20,8 @@ from test.support import is_emscripten, is_wasi, is_wasm32 from test.support import infinite_recursion from test.support import os_helper +from test.support import requires_root_user +from test.support import requires_non_root_user from test.support.os_helper import TESTFN, FS_NONASCII, FakePath try: import fcntl @@ -35,11 +37,6 @@ posix = None -root_in_posix = False -if hasattr(os, 'geteuid'): - root_in_posix = (os.geteuid() == 0) - - def patch_replace(old_test): def new_replace(self, target): raise OSError(errno.EXDEV, "Cross-device link", self, target) @@ -84,7 +81,7 @@ def test_is_notimplemented(self): class LazyImportTest(unittest.TestCase): @cpython_only def test_lazy_import(self): - import_helper.ensure_lazy_imports("pathlib", {"shutil"}) + import_helper.ensure_lazy_imports("pathlib", {"glob", "shutil"}) # @@ -293,6 +290,12 @@ def test_pickling_common(self): self.assertEqual(hash(pp), hash(p)) self.assertEqual(str(pp), str(p)) + def test_unpicking_3_13(self): + data = (b"\x80\x04\x95'\x00\x00\x00\x00\x00\x00\x00\x8c\x0e" + b"pathlib._local\x94\x8c\rPurePosixPath\x94\x93\x94)R\x94.") + p = pickle.loads(data) + self.assertIsInstance(p, pathlib.PurePosixPath) + def test_repr_common(self): for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): with self.subTest(pathstr=pathstr): @@ -1548,7 +1551,7 @@ def raiser(*args, **kwargs): self.assertRaises(FileNotFoundError, source.copy, target) @unittest.skipIf(sys.platform == "win32" or sys.platform == "wasi", "directories are always readable on Windows and WASI") - @unittest.skipIf(root_in_posix, "test fails with root privilege") + @requires_non_root_user def test_copy_dir_no_read_permission(self): base = self.cls(self.base) source = base / 'dirE' @@ -2021,7 +2024,7 @@ def test_owner(self): self.assertEqual(expected_name, p.owner()) @unittest.skipUnless(pwd, "the pwd module is needed for this test") - @unittest.skipUnless(root_in_posix, "test needs root privilege") + @requires_root_user def test_owner_no_follow_symlinks(self): all_users = [u.pw_uid for u in pwd.getpwall()] if len(all_users) < 2: @@ -2056,7 +2059,7 @@ def test_group(self): self.assertEqual(expected_name, p.group()) @unittest.skipUnless(grp, "the grp module is needed for this test") - @unittest.skipUnless(root_in_posix, "test needs root privilege") + @requires_root_user def test_group_no_follow_symlinks(self): all_groups = [g.gr_gid for g in grp.getgrall()] if len(all_groups) < 2: @@ -2489,6 +2492,116 @@ def my_mkdir(path, mode=0o777): self.assertNotIn(str(p12), concurrently_created) self.assertTrue(p.exists()) + @unittest.skipIf( + is_emscripten or is_wasi, + "umask is not implemented on Emscripten/WASI." + ) + @unittest.skipIf( + sys.platform == "android", + "Android filesystem may not honor requested permissions." + ) + def test_mkdir_parents_umask(self): + # Test that parent directories respect umask when parent_mode is not set + p = self.cls(self.base, 'umasktest', 'child') + self.assertFalse(p.exists()) + if os.name != 'nt': + with os_helper.temp_umask(0o002): + p.mkdir(0o755, parents=True) + self.assertTrue(p.exists()) + # Leaf directory gets the specified mode + self.assertEqual(p.stat().st_mode & 0o777, 0o755) + # Parent directory respects umask (0o777 & ~0o002 = 0o775) + self.assertEqual(p.parent.stat().st_mode & 0o777, 0o775) + + @unittest.skipIf( + is_emscripten or is_wasi, + "umask is not implemented on Emscripten/WASI." + ) + @unittest.skipIf( + sys.platform == "android", + "Android filesystem may not honor requested permissions." + ) + def test_mkdir_with_parent_mode(self): + # Test the parent_mode parameter + p = self.cls(self.base, 'newdirPM', 'subdirPM') + self.assertFalse(p.exists()) + if os.name != 'nt': + with os_helper.temp_umask(0o022): + # Specify different modes for parent and leaf directories + p.mkdir(0o755, parents=True, parent_mode=0o750) + self.assertTrue(p.exists()) + self.assertTrue(p.is_dir()) + # Leaf directory gets the mode parameter + self.assertEqual(p.stat().st_mode & 0o777, 0o755) + # Parent directory gets the parent_mode parameter + self.assertEqual(p.parent.stat().st_mode & 0o777, 0o750) + + @unittest.skipIf( + is_emscripten or is_wasi, + "umask is not implemented on Emscripten/WASI." + ) + @unittest.skipIf( + sys.platform == "android", + "Android filesystem may not honor requested permissions." + ) + def test_mkdir_parent_mode_deep_hierarchy(self): + # Test parent_mode with deep directory hierarchy + p = self.cls(self.base, 'level1PM', 'level2PM', 'level3PM') + self.assertFalse(p.exists()) + if os.name != 'nt': + with os_helper.temp_umask(0o022): + p.mkdir(0o755, parents=True, parent_mode=0o700) + self.assertTrue(p.exists()) + # Check that all parent directories have parent_mode + level1 = self.cls(self.base, 'level1PM') + level2 = level1 / 'level2PM' + self.assertEqual(level1.stat().st_mode & 0o777, 0o700) + self.assertEqual(level2.stat().st_mode & 0o777, 0o700) + # Leaf directory has the regular mode + self.assertEqual(p.stat().st_mode & 0o777, 0o755) + + @unittest.skipIf( + is_emscripten or is_wasi, + "umask is not implemented on Emscripten/WASI." + ) + @unittest.skipIf( + sys.platform == "android", + "Android filesystem may not honor requested permissions." + ) + def test_mkdir_parent_mode_combined_with_umask(self): + # parent_mode, like mode, is combined with the process umask; it does + # not bypass it. + p = self.cls(self.base, 'umaskPM', 'child') + self.assertFalse(p.exists()) + if os.name != 'nt': + with os_helper.temp_umask(0o022): + p.mkdir(0o777, parents=True, parent_mode=0o777) + self.assertTrue(p.exists()) + # 0o777 is masked down to 0o755 by the 0o022 umask, for both + # the leaf (mode) and the parent (parent_mode). + self.assertEqual(p.stat().st_mode & 0o777, 0o755) + self.assertEqual(p.parent.stat().st_mode & 0o777, 0o755) + + @unittest.skipIf( + is_emscripten or is_wasi, + "umask is not implemented on Emscripten/WASI." + ) + @unittest.skipIf( + sys.platform == "android", + "Android filesystem may not honor requested permissions." + ) + def test_mkdir_parent_mode_same_as_mode(self): + # Test setting parent_mode same as mode + p = self.cls(self.base, 'samedirPM', 'subdirPM') + self.assertFalse(p.exists()) + if os.name != 'nt': + with os_helper.temp_umask(0o022): + p.mkdir(0o705, parents=True, parent_mode=0o705) + self.assertTrue(p.exists()) + # Both directories should have the same mode + self.assertEqual(p.stat().st_mode & 0o777, 0o705) + self.assertEqual(p.parent.stat().st_mode & 0o777, 0o705) + @needs_symlinks def test_symlink_to(self): P = self.cls(self.base) diff --git a/Lib/test/test_pathlib/test_read.py b/Lib/test/test_pathlib/test_read.py index 482203c290a3c4..16fb555b2aee05 100644 --- a/Lib/test/test_pathlib/test_read.py +++ b/Lib/test/test_pathlib/test_read.py @@ -13,10 +13,10 @@ if is_pypi: from pathlib_abc import PathInfo, _ReadablePath - from pathlib_abc._os import magic_open + from pathlib_abc._os import vfsopen else: from pathlib.types import PathInfo, _ReadablePath - from pathlib._os import magic_open + from pathlib._os import vfsopen class ReadTestBase: @@ -32,10 +32,16 @@ def test_is_readable(self): def test_open_r(self): p = self.root / 'fileA' - with magic_open(p, 'r', encoding='utf-8') as f: + with vfsopen(p, 'r', encoding='utf-8') as f: self.assertIsInstance(f, io.TextIOBase) self.assertEqual(f.read(), 'this is file A\n') + def test_open_r_buffering_error(self): + p = self.root / 'fileA' + self.assertRaises(ValueError, vfsopen, p, 'r', buffering=0) + self.assertRaises(ValueError, vfsopen, p, 'r', buffering=1) + self.assertRaises(ValueError, vfsopen, p, 'r', buffering=1024) + @unittest.skipIf( not getattr(sys.flags, 'warn_default_encoding', 0), "Requires warn_default_encoding", @@ -43,17 +49,17 @@ def test_open_r(self): def test_open_r_encoding_warning(self): p = self.root / 'fileA' with self.assertWarns(EncodingWarning) as wc: - with magic_open(p, 'r'): + with vfsopen(p, 'r'): pass self.assertEqual(wc.filename, __file__) def test_open_rb(self): p = self.root / 'fileA' - with magic_open(p, 'rb') as f: + with vfsopen(p, 'rb') as f: self.assertEqual(f.read(), b'this is file A\n') - self.assertRaises(ValueError, magic_open, p, 'rb', encoding='utf8') - self.assertRaises(ValueError, magic_open, p, 'rb', errors='strict') - self.assertRaises(ValueError, magic_open, p, 'rb', newline='') + self.assertRaises(ValueError, vfsopen, p, 'rb', encoding='utf8') + self.assertRaises(ValueError, vfsopen, p, 'rb', errors='strict') + self.assertRaises(ValueError, vfsopen, p, 'rb', newline='') def test_read_bytes(self): p = self.root / 'fileA' diff --git a/Lib/test/test_pathlib/test_write.py b/Lib/test/test_pathlib/test_write.py index b958490d0a834f..fa2a81a47b33dc 100644 --- a/Lib/test/test_pathlib/test_write.py +++ b/Lib/test/test_pathlib/test_write.py @@ -13,10 +13,10 @@ if is_pypi: from pathlib_abc import _WritablePath - from pathlib_abc._os import magic_open + from pathlib_abc._os import vfsopen else: from pathlib.types import _WritablePath - from pathlib._os import magic_open + from pathlib._os import vfsopen class WriteTestBase: @@ -31,11 +31,17 @@ def test_is_writable(self): def test_open_w(self): p = self.root / 'fileA' - with magic_open(p, 'w', encoding='utf-8') as f: + with vfsopen(p, 'w', encoding='utf-8') as f: self.assertIsInstance(f, io.TextIOBase) f.write('this is file A\n') self.assertEqual(self.ground.readtext(p), 'this is file A\n') + def test_open_w_buffering_error(self): + p = self.root / 'fileA' + self.assertRaises(ValueError, vfsopen, p, 'w', buffering=0) + self.assertRaises(ValueError, vfsopen, p, 'w', buffering=1) + self.assertRaises(ValueError, vfsopen, p, 'w', buffering=1024) + @unittest.skipIf( not getattr(sys.flags, 'warn_default_encoding', 0), "Requires warn_default_encoding", @@ -43,31 +49,33 @@ def test_open_w(self): def test_open_w_encoding_warning(self): p = self.root / 'fileA' with self.assertWarns(EncodingWarning) as wc: - with magic_open(p, 'w'): + with vfsopen(p, 'w'): pass self.assertEqual(wc.filename, __file__) def test_open_wb(self): p = self.root / 'fileA' - with magic_open(p, 'wb') as f: + with vfsopen(p, 'wb') as f: #self.assertIsInstance(f, io.BufferedWriter) f.write(b'this is file A\n') self.assertEqual(self.ground.readbytes(p), b'this is file A\n') - self.assertRaises(ValueError, magic_open, p, 'wb', encoding='utf8') - self.assertRaises(ValueError, magic_open, p, 'wb', errors='strict') - self.assertRaises(ValueError, magic_open, p, 'wb', newline='') + self.assertRaises(ValueError, vfsopen, p, 'wb', encoding='utf8') + self.assertRaises(ValueError, vfsopen, p, 'wb', errors='strict') + self.assertRaises(ValueError, vfsopen, p, 'wb', newline='') def test_write_bytes(self): p = self.root / 'fileA' - p.write_bytes(b'abcdefg') - self.assertEqual(self.ground.readbytes(p), b'abcdefg') + data = b'abcdefg' + self.assertEqual(len(data), p.write_bytes(data)) + self.assertEqual(self.ground.readbytes(p), data) # Check that trying to write str does not truncate the file. self.assertRaises(TypeError, p.write_bytes, 'somestr') - self.assertEqual(self.ground.readbytes(p), b'abcdefg') + self.assertEqual(self.ground.readbytes(p), data) def test_write_text(self): p = self.root / 'fileA' - p.write_text('äbcdefg', encoding='latin-1') + data = 'äbcdefg' + self.assertEqual(len(data), p.write_text(data, encoding='latin-1')) self.assertEqual(self.ground.readbytes(p), b'\xe4bcdefg') # Check that trying to write bytes does not truncate the file. self.assertRaises(TypeError, p.write_text, b'somebytes', encoding='utf-8') diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py index 5d0857b059ea23..29cce4ee6d271f 100644 --- a/Lib/test/test_patma.py +++ b/Lib/test/test_patma.py @@ -2762,6 +2762,96 @@ def test_patma_255(self): self.assertEqual(y, 1) self.assertIs(z, x) + def test_patma_256(self): + x = 0 + match x: + case +0: + y = 0 + self.assertEqual(x, 0) + self.assertEqual(y, 0) + + def test_patma_257(self): + x = 0 + match x: + case +0.0: + y = 0 + self.assertEqual(x, 0) + self.assertEqual(y, 0) + + def test_patma_258(self): + x = 0 + match x: + case +0j: + y = 0 + self.assertEqual(x, 0) + self.assertEqual(y, 0) + + def test_patma_259(self): + x = 0 + match x: + case +0.0j: + y = 0 + self.assertEqual(x, 0) + self.assertEqual(y, 0) + + def test_patma_260(self): + x = 1 + match x: + case +1: + y = 0 + self.assertEqual(x, 1) + self.assertEqual(y, 0) + + def test_patma_261(self): + x = 1.5 + match x: + case +1.5: + y = 0 + self.assertEqual(x, 1.5) + self.assertEqual(y, 0) + + def test_patma_262(self): + x = 1j + match x: + case +1j: + y = 0 + self.assertEqual(x, 1j) + self.assertEqual(y, 0) + + def test_patma_263(self): + x = 1.5j + match x: + case +1.5j: + y = 0 + self.assertEqual(x, 1.5j) + self.assertEqual(y, 0) + + def test_patma_264(self): + x = 0.25 + 1.75j + match x: + case +0.25 + 1.75j: + y = 0 + self.assertEqual(x, 0.25 + 1.75j) + self.assertEqual(y, 0) + + def test_patma_265(self): + x = 0.25 - 1.75j + match x: + case 0.25 - +1.75j: + y = 0 + self.assertEqual(x, 0.25 - 1.75j) + self.assertEqual(y, 0) + + def test_patma_266(self): + x = 0 + match x: + case +1e1000: + y = 0 + case 0: + y = 1 + self.assertEqual(x, 0) + self.assertEqual(y, 1) + def test_patma_runtime_checkable_protocol(self): # Runtime-checkable protocol from typing import Protocol, runtime_checkable diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 6b74e21ad73d1a..410f1436ed4d20 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -6,6 +6,7 @@ import io import os import pdb +import re import sys import types import codecs @@ -3232,6 +3233,37 @@ def test_pdb_issue_gh_127321(): """ +def test_pdb_issue_gh_136057(): + """See GH-136057 + "step" and "next" commands should be able to get over list comprehensions + >>> def test_function(): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... lst = [i for i in range(10)] + ... for i in lst: pass + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE + ... 'next', + ... 'next', + ... 'step', + ... 'continue', + ... ]): + ... test_function() + > <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(2)test_function() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) next + > <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(3)test_function() + -> lst = [i for i in range(10)] + (Pdb) next + > <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(4)test_function() + -> for i in lst: pass + (Pdb) step + --Return-- + > <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(4)test_function()->None + -> for i in lst: pass + (Pdb) continue + """ + + def test_pdb_issue_gh_80731(): """See GH-80731 @@ -3447,6 +3479,49 @@ def test_pdb_issue_gh_65052(): (Pdb) continue """ +def test_pdb_commands_last_breakpoint(): + """See GH-142834 + + >>> def test_function(): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... foo = 1 + ... bar = 2 + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE + ... 'break 4', + ... 'break 3', + ... 'clear 2', + ... 'commands', + ... 'p "success"', + ... 'end', + ... 'continue', + ... 'clear 1', + ... 'commands', + ... 'continue', + ... ]): + ... test_function() + > <doctest test.test_pdb.test_pdb_commands_last_breakpoint[0]>(2)test_function() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) break 4 + Breakpoint 1 at <doctest test.test_pdb.test_pdb_commands_last_breakpoint[0]>:4 + (Pdb) break 3 + Breakpoint 2 at <doctest test.test_pdb.test_pdb_commands_last_breakpoint[0]>:3 + (Pdb) clear 2 + Deleted breakpoint 2 at <doctest test.test_pdb.test_pdb_commands_last_breakpoint[0]>:3 + (Pdb) commands + (com) p "success" + (com) end + (Pdb) continue + 'success' + > <doctest test.test_pdb.test_pdb_commands_last_breakpoint[0]>(4)test_function() + -> bar = 2 + (Pdb) clear 1 + Deleted breakpoint 1 at <doctest test.test_pdb.test_pdb_commands_last_breakpoint[0]>:4 + (Pdb) commands + *** cannot set commands: no existing breakpoint + (Pdb) continue + """ + @support.force_not_colorized_test_class @support.requires_subprocess() @@ -3530,6 +3605,35 @@ def _assert_find_function(self, file_content, func_name, expected): self.assertEqual( expected, pdb.find_function(func_name, os_helper.TESTFN)) + def _fd_dir_for_pipe_targets(self): + """Return a directory exposing live file descriptors, if any.""" + return self._proc_fd_dir() or self._dev_fd_dir() + + def _proc_fd_dir(self): + """Return /proc-backed fd dir when it can be used for pipes.""" + # GH-142836: Opening /proc/self/fd entries for pipes raises EACCES on + # Solaris, so prefer other mechanisms there. + if sys.platform.startswith("sunos"): + return None + + proc_fd = "/proc/self/fd" + if os.path.isdir(proc_fd) and os.path.exists(os.path.join(proc_fd, '0')): + return proc_fd + return None + + def _dev_fd_dir(self): + """Return /dev-backed fd dir when usable.""" + dev_fd = "/dev/fd" + if os.path.isdir(dev_fd) and os.path.exists(os.path.join(dev_fd, '0')): + if sys.platform.startswith("freebsd"): + try: + if os.stat("/dev").st_dev == os.stat(dev_fd).st_dev: + return None + except FileNotFoundError: + return None + return dev_fd + return None + def test_find_function_empty_file(self): self._assert_find_function(b'', 'foo', None) @@ -3549,6 +3653,20 @@ def quux(): ('bœr', 5), ) + def test_print_stack_entry_uses_dynamic_line_prefix(self): + """Test that pdb.line_prefix binding is dynamic (gh-141781).""" + stdout = io.StringIO() + p = pdb.Pdb(stdout=stdout) + + # Get the current frame to use for printing + frame = sys._getframe() + + with support.swap_attr(pdb, 'line_prefix', 'CUSTOM_PREFIX> '): + p.print_stack_entry((frame, frame.f_lineno)) + + # Check if the custom prefix appeared in the output + self.assertIn('CUSTOM_PREFIX> ', stdout.getvalue()) + def test_find_function_found_with_encoding_cookie(self): self._assert_find_function( """\ @@ -3588,6 +3706,47 @@ def test_spec(self): stdout, _ = self.run_pdb_script(script, commands) self.assertIn('None', stdout) + def test_script_target_anonymous_pipe(self): + """ + _ScriptTarget doesn't fail on an anonymous pipe. + + GH-142315 + """ + fd_dir = self._fd_dir_for_pipe_targets() + if fd_dir is None: + self.skipTest('anonymous pipe targets require /proc/self/fd or /dev/fd') + + read_fd, write_fd = os.pipe() + + def safe_close(fd): + try: + os.close(fd) + except OSError: + pass + + self.addCleanup(safe_close, read_fd) + self.addCleanup(safe_close, write_fd) + + pipe_path = os.path.join(fd_dir, str(read_fd)) + if not os.path.exists(pipe_path): + self.skipTest('fd directory does not expose anonymous pipes') + + script_source = 'marker = "via_pipe"\n' + os.write(write_fd, script_source.encode('utf-8')) + os.close(write_fd) + + original_path0 = sys.path[0] + self.addCleanup(sys.path.__setitem__, 0, original_path0) + + target = pdb._ScriptTarget(pipe_path) + code_text = target.code + namespace = target.namespace + exec(code_text, namespace) + + self.assertEqual(namespace['marker'], 'via_pipe') + self.assertEqual(namespace['__file__'], target.filename) + self.assertIsNone(namespace['__spec__']) + def test_find_function_first_executable_line(self): code = textwrap.dedent("""\ def foo(): pass @@ -3935,6 +4094,23 @@ def test_readrc_homedir(self): f.write("invalid") self.assertEqual(pdb.Pdb().rcLines[0], "invalid") + def test_readrc_current_dir(self): + with os_helper.temp_cwd() as cwd: + rc_path = os.path.join(cwd, ".pdbrc") + with open(rc_path, "w") as f: + f.write("invalid") + self.assertEqual(pdb.Pdb().rcLines[-1], "invalid") + + def test_readrc_cwd_is_home(self): + with os_helper.EnvironmentVarGuard() as env: + env.unset("HOME") + with os_helper.temp_cwd() as cwd, patch("os.path.expanduser"): + rc_path = os.path.join(cwd, ".pdbrc") + os.path.expanduser.return_value = rc_path + with open(rc_path, "w") as f: + f.write("invalid") + self.assertEqual(pdb.Pdb().rcLines, ["invalid"]) + def test_header(self): stdout = StringIO() header = 'Nobody expects... blah, blah, blah' @@ -3974,7 +4150,10 @@ def test_run_module_with_args(self): commands = """ continue """ - self._run_pdb(["calendar", "-m"], commands, expected_returncode=2) + self._run_pdb(["calendar", "-m"], commands, expected_returncode=1) + + _, stderr = self._run_pdb(["-m", "calendar", "-p", "1"], commands) + self.assertIn("unrecognized arguments: -p", stderr) stdout, _ = self._run_pdb(["-m", "calendar", "1"], commands) self.assertIn("December", stdout) @@ -4539,6 +4718,72 @@ def bar(): ])) self.assertIn('break in bar', stdout) + def test_end_of_options_separator(self): + # gh-148615: Test parsing when '--' separator is used + script = "import sys; print(f'ARGS: {sys.argv[1:]}')" + with open(os_helper.TESTFN, 'w', encoding='utf-8') as f: + f.write(script) + stdout, _ = self._run_pdb(['--', os_helper.TESTFN, '-foo'], 'c\nq') + self.assertIn("ARGS: ['-foo']", stdout) + stdout, _ = self._run_pdb(['-c', 'continue', '--', os_helper.TESTFN, '-c', 'foo'], 'q') + self.assertIn("ARGS: ['-c', 'foo']", stdout) + stdout, stderr = self._run_pdb(['--'], 'q', expected_returncode=2) + self.assertIn("missing script or module to run", stderr) + stdout, stderr = self._run_pdb(['-x', '--', os_helper.TESTFN], 'q', expected_returncode=2) + self.assertIn("unrecognized arguments: -x", stderr) + stdout, _ = self._run_pdb([os_helper.TESTFN, '--', 'arg'], 'c\nq') + self.assertIn("ARGS: ['--', 'arg']", stdout) + with os_helper.temp_cwd(): + with open('mymod.py', 'w', encoding='utf-8') as f: + f.write(script) + stdout, _ = self._run_pdb(['-m', 'mymod', '--', 'arg'], 'c\nq') + self.assertIn("ARGS: ['--', 'arg']", stdout) + + @unittest.skipIf(SKIP_CORO_TESTS, "Coroutine tests are skipped") + def test_async_break(self): + script = """ + import asyncio + + async def main(): + pass + + asyncio.run(main()) + """ + commands = """ + break main + continue + quit + """ + stdout, stderr = self.run_pdb_script(script, commands) + self.assertRegex(stdout, r"Breakpoint 1 at .*main\.py:5") + self.assertIn("pass", stdout) + + def test_issue_59000(self): + script = """ + def foo(): + pass + + class C: + def foo(self): + pass + """ + commands = """ + break C.foo + quit + """ + stdout, stderr = self.run_pdb_script(script, commands) + self.assertIn("The specified object 'C.foo' is not a function", stdout) + + def test_pyrepl_available(self): + with patch.dict(os.environ, {"PYTHON_BASIC_REPL": "1"}): + self.assertFalse(pdb._pyrepl_available()) + + with patch.dict(os.environ, {}, clear=True): + mod = types.ModuleType("_pyrepl.main") + mod.CAN_USE_PYREPL = True + with patch.dict("sys.modules", {"_pyrepl.main": mod}): + self.assertTrue(pdb._pyrepl_available()) + class ChecklineTests(unittest.TestCase): def setUp(self): @@ -4688,6 +4933,41 @@ def foo(): stdout, _ = self._run_script(script, commands) self.assertIn("42", stdout) + def test_readline_not_imported(self): + """GH-138860 + Directly or indirectly importing readline might deadlock a subprocess + if it's launched with process_group=0 or preexec_fn=setpgrp + + It's also a pattern that readline is never imported with just import pdb. + + This test is to ensure that readline is not imported for import pdb. + It's possible that we have a good reason to do that in the future. + """ + + script = textwrap.dedent(""" + import sys + import pdb + if "readline" in sys.modules: + print("readline imported") + """) + commands = "" + stdout, stderr = self._run_script(script, commands) + self.assertNotIn("readline imported", stdout) + self.assertEqual(stderr, "") + + def test_alternate_stdin(self): + script = textwrap.dedent(""" + import pdb + import io + + input_data = io.StringIO("p 40 + 2\\nc\\n") + pdb.Pdb(stdin=input_data).set_trace() + """) + commands = "" + stdout, stderr = self._run_script(script, commands) + self.assertIn("42", stdout) + self.assertEqual(stderr, "") + @support.force_colorized_test_class class PdbTestColorize(unittest.TestCase): @@ -4722,6 +5002,29 @@ def test_stack_entry(self): p.set_trace(commands=['w', 'c']) self.assertIn("\x1b", output.getvalue()) + @unittest.skipIf(not pdb._pyrepl_available(), "pyrepl is not available") + def test_gen_colors(self): + p = pdb.Pdb() + gen_colors = p.pyrepl_input.gen_colors + + test_cases = [ + ("longlist", [((0, 7), "soft_keyword")]), + ("!longlist", [((0, 0), "op")]), + ("list", [((0, 3), "soft_keyword")]), + ("list(", [((0, 3), "builtin"), ((4, 4), "op")]), + ("a = 1", [ + ((0, 0), "soft_keyword"), + ((2, 2), "op"), + ((4, 4), "number"), + ]) + ] + + for buffer, expected in test_cases: + for color_span, ((start, end), tag) in zip(gen_colors(buffer), expected, strict=True): + self.assertEqual(color_span.span.start, start) + self.assertEqual(color_span.span.end, end) + self.assertEqual(color_span.tag, tag) + @support.force_not_colorized_test_class @support.requires_subprocess() @@ -4758,6 +5061,20 @@ def setUpClass(cls): if readline.backend == "editline": raise unittest.SkipTest("libedit readline is not supported for pdb") + def _run_pty(self, script, input, env=None): + if env is None: + # By default, we use basic repl for the test. + # Subclass can overwrite this method and set env to use advanced REPL + env = os.environ | {'PYTHON_BASIC_REPL': '1'} + output = run_pty(script, input, env=env) + # filter all control characters + # Strip ANSI CSI sequences (good enough for most REPL/prompt output) + output = re.sub(r"\x1b\[[0-?]*[ -/]*[@-~]", "", output.decode("utf-8")) + return output + + def _pyrepl_available(self): + return pdb._pyrepl_available() + def test_basic_completion(self): script = textwrap.dedent(""" import pdb; pdb.Pdb().set_trace() @@ -4769,12 +5086,12 @@ def test_basic_completion(self): # then add ntin and complete 'contin' to 'continue' input = b"co\t\tntin\t\n" - output = run_pty(script, input) + output = self._run_pty(script, input) - self.assertIn(b'commands', output) - self.assertIn(b'condition', output) - self.assertIn(b'continue', output) - self.assertIn(b'hello!', output) + self.assertIn('commands', output) + self.assertIn('condition', output) + self.assertIn('continue', output) + self.assertIn('hello!', output) def test_expression_completion(self): script = textwrap.dedent(""" @@ -4791,11 +5108,11 @@ def test_expression_completion(self): # Continue input += b"c\n" - output = run_pty(script, input) + output = self._run_pty(script, input) - self.assertIn(b'special', output) - self.assertIn(b'species', output) - self.assertIn(b'$_frame', output) + self.assertIn('special', output) + self.assertIn('species', output) + self.assertIn('$_frame', output) def test_builtin_completion(self): script = textwrap.dedent(""" @@ -4809,9 +5126,9 @@ def test_builtin_completion(self): # Continue input += b"c\n" - output = run_pty(script, input) + output = self._run_pty(script, input) - self.assertIn(b'special', output) + self.assertIn('special', output) def test_convvar_completion(self): script = textwrap.dedent(""" @@ -4827,10 +5144,10 @@ def test_convvar_completion(self): # Continue input += b"c\n" - output = run_pty(script, input) + output = self._run_pty(script, input) - self.assertIn(b'<frame at 0x', output) - self.assertIn(b'102', output) + self.assertIn('<frame at 0x', output) + self.assertIn('102', output) def test_local_namespace(self): script = textwrap.dedent(""" @@ -4846,9 +5163,9 @@ def f(): # Continue input += b"c\n" - output = run_pty(script, input) + output = self._run_pty(script, input) - self.assertIn(b'I love Python', output) + self.assertIn('I love Python', output) @unittest.skipIf(sys.platform.startswith('freebsd'), '\\x08 is not interpreted as backspace on FreeBSD') @@ -4868,9 +5185,9 @@ def test_multiline_auto_indent(self): input += b"f(-21-21)\n" input += b"c\n" - output = run_pty(script, input) + output = self._run_pty(script, input) - self.assertIn(b'42', output) + self.assertIn('42', output) def test_multiline_completion(self): script = textwrap.dedent(""" @@ -4886,9 +5203,9 @@ def test_multiline_completion(self): input += b"fun\t()\n" input += b"c\n" - output = run_pty(script, input) + output = self._run_pty(script, input) - self.assertIn(b'42', output) + self.assertIn('42', output) @unittest.skipIf(sys.platform.startswith('freebsd'), '\\x08 is not interpreted as backspace on FreeBSD') @@ -4914,10 +5231,10 @@ def func(): c """).encode() - output = run_pty(script, input) + output = self._run_pty(script, input) - self.assertIn(b'5', output) - self.assertNotIn(b'Error', output) + self.assertIn('5', output) + self.assertNotIn('Error', output) def test_interact_completion(self): script = textwrap.dedent(""" @@ -4941,11 +5258,45 @@ def test_interact_completion(self): # continue input += b"c\n" - output = run_pty(script, input) + output = self._run_pty(script, input) + + self.assertIn("'disp' is not defined", output) + self.assertIn('special', output) + self.assertIn('84', output) + + +@unittest.skipIf(not pdb._pyrepl_available(), "pyrepl is not available") +class PdbTestReadlinePyREPL(PdbTestReadline): + def _run_pty(self, script, input): + # Override the env to make sure pyrepl is used in this test class + return super()._run_pty(script, input, env={**os.environ}) + + def test_pyrepl_used(self): + script = textwrap.dedent(""" + import pdb + db = pdb.Pdb() + print(db.pyrepl_input) + """) + input = b"" + output = self._run_pty(script, input) + self.assertIn('PdbPyReplInput', output) + + def test_pyrepl_multiline_change(self): + script = textwrap.dedent(""" + import pdb; pdb.Pdb().set_trace() + """) + + input = b"def f():\n" + # Auto-indent should work here + input += b"return x" + # The following command tries to add the argument x in f() + # up, left, left (in the parenthesis now), "x", down, down (at the end) + input += b"\x1bOA\x1bOD\x1bODx\x1bOB\x1bOB\n\n" + input += b"f(40 + 2)\n" + input += b"c\n" - self.assertIn(b"'disp' is not defined", output) - self.assertIn(b'special', output) - self.assertIn(b'84', output) + output = self._run_pty(script, input) + self.assertIn('42', output) def load_tests(loader, tests, pattern): diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 98629df457460e..28748009f731bc 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1,3 +1,4 @@ +import ast import dis import gc from itertools import combinations, product @@ -105,7 +106,7 @@ def test_elim_inversion_of_is_or_in(self): self.check_lnotab(code) def test_global_as_constant(self): - # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False + # LOAD_GLOBAL None/True/False --> LOAD_COMMON_CONSTANT None/True/False def f(): x = None x = None @@ -120,7 +121,7 @@ def h(): for func, elem in ((f, None), (g, True), (h, False)): with self.subTest(func=func): self.assertNotInBytecode(func, 'LOAD_GLOBAL') - self.assertInBytecode(func, 'LOAD_CONST', elem) + self.assertInBytecode(func, 'LOAD_COMMON_CONSTANT', elem) self.check_lnotab(func) def f(): @@ -128,7 +129,7 @@ def f(): return None self.assertNotInBytecode(f, 'LOAD_GLOBAL') - self.assertInBytecode(f, 'LOAD_CONST', None) + self.assertInBytecode(f, 'LOAD_COMMON_CONSTANT', None) self.check_lnotab(f) def test_while_one(self): @@ -145,13 +146,14 @@ def f(): def test_pack_unpack(self): for line, elem in ( - ('a, = a,', 'LOAD_CONST',), + ('a, = a,', None), ('a, b = a, b', 'SWAP',), ('a, b, c = a, b, c', 'SWAP',), ): with self.subTest(line=line): code = compile(line,'','single') - self.assertInBytecode(code, elem) + if elem is not None: + self.assertInBytecode(code, elem) self.assertNotInBytecode(code, 'BUILD_TUPLE') self.assertNotInBytecode(code, 'UNPACK_SEQUENCE') self.check_lnotab(code) @@ -173,10 +175,10 @@ def test_constant_folding_tuples_of_constants(self): # Long tuples should be folded too. code = compile(repr(tuple(range(10000))),'','single') self.assertNotInBytecode(code, 'BUILD_TUPLE') - # One LOAD_CONST for the tuple, one for the None return value + # One LOAD_CONST for the tuple; None return value uses LOAD_COMMON_CONSTANT load_consts = [instr for instr in dis.get_instructions(code) if instr.opname == 'LOAD_CONST'] - self.assertEqual(len(load_consts), 2) + self.assertEqual(len(load_consts), 1) self.check_lnotab(code) # Bug 1053819: Tuple of constants misidentified when presented with: @@ -282,11 +284,11 @@ def test_constant_folding_unaryop(self): ('-0.0', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.0), ('-(1.0-1.0)', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.0), ('-0.5', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.5), - ('---1', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -1), + ('---1', 'UNARY_NEGATIVE', None, True, 'LOAD_COMMON_CONSTANT', -1), ('---""', 'UNARY_NEGATIVE', None, False, None, None), ('~~~1', 'UNARY_INVERT', None, True, 'LOAD_CONST', -2), ('~~~""', 'UNARY_INVERT', None, False, None, None), - ('not not True', 'UNARY_NOT', None, True, 'LOAD_CONST', True), + ('not not True', 'UNARY_NOT', None, True, 'LOAD_COMMON_CONSTANT', True), ('not not x', 'UNARY_NOT', None, True, 'LOAD_NAME', 'x'), # this should be optimized regardless of constant or not ('+++1', 'CALL_INTRINSIC_1', intrinsic_positive, True, 'LOAD_SMALL_INT', 1), ('---x', 'UNARY_NEGATIVE', None, False, None, None), @@ -325,7 +327,7 @@ def test_constant_folding_binop(self): ('1 + 2', 'NB_ADD', True, 'LOAD_SMALL_INT', 3), ('1 + 2 + 3', 'NB_ADD', True, 'LOAD_SMALL_INT', 6), ('1 + ""', 'NB_ADD', False, None, None), - ('1 - 2', 'NB_SUBTRACT', True, 'LOAD_CONST', -1), + ('1 - 2', 'NB_SUBTRACT', True, 'LOAD_COMMON_CONSTANT', -1), ('1 - 2 - 3', 'NB_SUBTRACT', True, 'LOAD_CONST', -4), ('1 - ""', 'NB_SUBTRACT', False, None, None), ('2 * 2', 'NB_MULTIPLY', True, 'LOAD_SMALL_INT', 4), @@ -733,22 +735,27 @@ def test_format_errors(self): with self.assertRaisesRegex(TypeError, 'not all arguments converted during string formatting'): eval("'%s' % (x, y)", {'x': 1, 'y': 2}) - with self.assertRaisesRegex(ValueError, 'incomplete format'): + with self.assertRaisesRegex(ValueError, 'stray % at position 2'): eval("'%s%' % (x,)", {'x': 1234}) - with self.assertRaisesRegex(ValueError, 'incomplete format'): + with self.assertRaisesRegex(ValueError, 'stray % at position 4'): eval("'%s%%%' % (x,)", {'x': 1234}) with self.assertRaisesRegex(TypeError, 'not enough arguments for format string'): eval("'%s%z' % (x,)", {'x': 1234}) - with self.assertRaisesRegex(ValueError, 'unsupported format character'): + with self.assertRaisesRegex(ValueError, + 'unsupported format %z at position 2'): eval("'%s%z' % (x, 5)", {'x': 1234}) - with self.assertRaisesRegex(TypeError, 'a real number is required, not str'): + with self.assertRaisesRegex(TypeError, + 'format argument 1: %d requires a real number, not str'): eval("'%d' % (x,)", {'x': '1234'}) - with self.assertRaisesRegex(TypeError, 'an integer is required, not float'): + with self.assertRaisesRegex(TypeError, + 'format argument 1: %x requires an integer, not float'): eval("'%x' % (x,)", {'x': 1234.56}) - with self.assertRaisesRegex(TypeError, 'an integer is required, not str'): + with self.assertRaisesRegex(TypeError, + 'format argument 1: %x requires an integer, not str'): eval("'%x' % (x,)", {'x': '1234'}) - with self.assertRaisesRegex(TypeError, 'must be real number, not str'): + with self.assertRaisesRegex(TypeError, + 'format argument 1: %f requires a real number, not str'): eval("'%f' % (x,)", {'x': '1234'}) with self.assertRaisesRegex(TypeError, 'not enough arguments for format string'): @@ -1116,9 +1123,63 @@ def trace(frame, event, arg): self.assertInBytecode(f, "LOAD_FAST_BORROW") self.assertNotInBytecode(f, "LOAD_FAST_CHECK") + def test_import_from_doesnt_clobber_load_fast_borrow(self): + def f(self): + if x: pass + self.x + from heapq import heapify_max + print(heapify_max) + self.assertInBytecode(f, "LOAD_FAST_BORROW", "self") class DirectCfgOptimizerTests(CfgOptimizationTestCase): + def test_optimize_cfg_const_index_out_of_range(self): + insts = [ + ('LOAD_CONST', 2, 0), + ('RETURN_VALUE', None, 0), + ] + seq = self.seq_from_insts(insts) + with self.assertRaisesRegex(ValueError, "out of range"): + _testinternalcapi.optimize_cfg(seq, [0, 1], 0) + + def test_optimize_cfg_consts_must_be_list(self): + insts = [ + ('LOAD_CONST', 0, 0), + ('RETURN_VALUE', None, 0), + ] + seq = self.seq_from_insts(insts) + with self.assertRaisesRegex(TypeError, "consts must be a list"): + _testinternalcapi.optimize_cfg(seq, (0,), 0) + + def test_compiler_codegen_metadata_consts_roundtrips_optimize_cfg(self): + tree = ast.parse("x = (1, 2)", mode="exec", optimize=1) + insts, meta = _testinternalcapi.compiler_codegen(tree, "<s>", 0) + consts = meta["consts"] + self.assertIsInstance(consts, list) + _testinternalcapi.optimize_cfg(insts, consts, 0) + + def test_compiler_codegen_consts_include_none_required_for_implicit_return(self): + # Module "pass" only needs the const table entry for None once + # _PyCodegen_AddReturnAtEnd runs. If metadata["consts"] were taken + # before that, the list would not match LOAD_CONST opargs (here: 0 + # for None), and optimize_cfg would read out of range. + tree = ast.parse("pass", mode="exec", optimize=1) + insts, meta = _testinternalcapi.compiler_codegen(tree, "<s>", 0) + consts = meta["consts"] + self.assertEqual(consts, [None]) + + load_const = opcode.opmap["LOAD_CONST"] + self.assertEqual( + [t[1] for t in insts.get_instructions() if t[0] == load_const], + [0], + ) + + # As if consts were snapshotted before AddReturnAtEnd: still LOAD_CONST 0, no row. + with self.assertRaisesRegex(ValueError, "out of range"): + _testinternalcapi.optimize_cfg(insts, [], 0) + + _testinternalcapi.optimize_cfg(insts, list(consts), 0) + def cfg_optimization_test(self, insts, expected_insts, consts=None, expected_consts=None, nlocals=0): @@ -1246,10 +1307,10 @@ def test_build_empty_tuple(self): ('RETURN_VALUE', None, 0), ] after = [ - ('LOAD_CONST', 0, 0), + ('LOAD_COMMON_CONSTANT', opcode._common_constants.index(()), 0), ('RETURN_VALUE', None, 0), ] - self.cfg_optimization_test(before, after, consts=[], expected_consts=[()]) + self.cfg_optimization_test(before, after, consts=[], expected_consts=[]) def test_fold_tuple_of_constants(self): before = [ @@ -1304,10 +1365,10 @@ def test_fold_constant_intrinsic_list_to_tuple(self): ('RETURN_VALUE', None, 0) ] after = [ - ('LOAD_CONST', 0, 0), + ('LOAD_COMMON_CONSTANT', opcode._common_constants.index(()), 0), ('RETURN_VALUE', None, 0) ] - self.cfg_optimization_test(before, after, consts=[], expected_consts=[()]) + self.cfg_optimization_test(before, after, consts=[], expected_consts=[]) # multiple BUILD_LIST 0: ([], 1, [], 2) same = [ @@ -1458,7 +1519,7 @@ def test_optimize_literal_list_for_iter(self): ('LOAD_SMALL_INT', 1, 0), ('LOAD_SMALL_INT', 2, 0), ('BUILD_LIST', 2, 0), - ('GET_ITER', None, 0), + ('GET_ITER', 0, 0), start := self.Label(), ('FOR_ITER', end := self.Label(), 0), ('STORE_FAST', 0, 0), @@ -1471,7 +1532,7 @@ def test_optimize_literal_list_for_iter(self): ] after = [ ('LOAD_CONST', 1, 0), - ('GET_ITER', None, 0), + ('GET_ITER', 0, 0), start := self.Label(), ('FOR_ITER', end := self.Label(), 0), ('STORE_FAST', 0, 0), @@ -1479,7 +1540,7 @@ def test_optimize_literal_list_for_iter(self): end, ('END_FOR', None, 0), ('POP_ITER', None, 0), - ('LOAD_CONST', 0, 0), + ('LOAD_COMMON_CONSTANT', 7, 0), ('RETURN_VALUE', None, 0), ] self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, (1, 2)]) @@ -1489,7 +1550,7 @@ def test_optimize_literal_list_for_iter(self): ('LOAD_SMALL_INT', 1, 0), ('LOAD_NAME', 0, 0), ('BUILD_LIST', 2, 0), - ('GET_ITER', None, 0), + ('GET_ITER', 0, 0), start := self.Label(), ('FOR_ITER', end := self.Label(), 0), ('STORE_FAST', 0, 0), @@ -1504,7 +1565,7 @@ def test_optimize_literal_list_for_iter(self): ('LOAD_SMALL_INT', 1, 0), ('LOAD_NAME', 0, 0), ('BUILD_TUPLE', 2, 0), - ('GET_ITER', None, 0), + ('GET_ITER', 0, 0), start := self.Label(), ('FOR_ITER', end := self.Label(), 0), ('STORE_FAST', 0, 0), @@ -1512,7 +1573,7 @@ def test_optimize_literal_list_for_iter(self): end, ('END_FOR', None, 0), ('POP_ITER', None, 0), - ('LOAD_CONST', 0, 0), + ('LOAD_COMMON_CONSTANT', 7, 0), ('RETURN_VALUE', None, 0), ] self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None]) @@ -1523,7 +1584,7 @@ def test_optimize_literal_set_for_iter(self): ('LOAD_SMALL_INT', 1, 0), ('LOAD_SMALL_INT', 2, 0), ('BUILD_SET', 2, 0), - ('GET_ITER', None, 0), + ('GET_ITER', 0, 0), start := self.Label(), ('FOR_ITER', end := self.Label(), 0), ('STORE_FAST', 0, 0), @@ -1536,7 +1597,7 @@ def test_optimize_literal_set_for_iter(self): ] after = [ ('LOAD_CONST', 1, 0), - ('GET_ITER', None, 0), + ('GET_ITER', 0, 0), start := self.Label(), ('FOR_ITER', end := self.Label(), 0), ('STORE_FAST', 0, 0), @@ -1544,7 +1605,7 @@ def test_optimize_literal_set_for_iter(self): end, ('END_FOR', None, 0), ('POP_ITER', None, 0), - ('LOAD_CONST', 0, 0), + ('LOAD_COMMON_CONSTANT', 7, 0), ('RETURN_VALUE', None, 0), ] self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, frozenset({1, 2})]) @@ -1555,7 +1616,7 @@ def test_optimize_literal_set_for_iter(self): ('LOAD_SMALL_INT', 1, 0), ('LOAD_NAME', 0, 0), ('BUILD_SET', 2, 0), - ('GET_ITER', None, 0), + ('GET_ITER', 0, 0), start := self.Label(), ('FOR_ITER', end := self.Label(), 0), ('STORE_FAST', 0, 0), @@ -1566,7 +1627,22 @@ def test_optimize_literal_set_for_iter(self): ('LOAD_CONST', 0, 0), ('RETURN_VALUE', None, 0), ] - self.cfg_optimization_test(same, same, consts=[None], expected_consts=[None]) + expected = [ + ('LOAD_SMALL_INT', 1, 0), + ('LOAD_NAME', 0, 0), + ('BUILD_SET', 2, 0), + ('GET_ITER', 0, 0), + start := self.Label(), + ('FOR_ITER', end := self.Label(), 0), + ('STORE_FAST', 0, 0), + ('JUMP', start, 0), + end, + ('END_FOR', None, 0), + ('POP_ITER', None, 0), + ('LOAD_COMMON_CONSTANT', 7, 0), + ('RETURN_VALUE', None, 0), + ] + self.cfg_optimization_test(same, expected, consts=[None], expected_consts=[None]) def test_optimize_literal_list_contains(self): # x in [1, 2] ==> x in (1, 2) @@ -1585,7 +1661,7 @@ def test_optimize_literal_list_contains(self): ('LOAD_CONST', 1, 0), ('CONTAINS_OP', 0, 0), ('POP_TOP', None, 0), - ('LOAD_CONST', 0, 0), + ('LOAD_COMMON_CONSTANT', 7, 0), ('RETURN_VALUE', None, 0), ] self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, (1, 2)]) @@ -1608,7 +1684,7 @@ def test_optimize_literal_list_contains(self): ('BUILD_TUPLE', 2, 0), ('CONTAINS_OP', 0, 0), ('POP_TOP', None, 0), - ('LOAD_CONST', 0, 0), + ('LOAD_COMMON_CONSTANT', 7, 0), ('RETURN_VALUE', None, 0), ] self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None]) @@ -1630,7 +1706,7 @@ def test_optimize_literal_set_contains(self): ('LOAD_CONST', 1, 0), ('CONTAINS_OP', 0, 0), ('POP_TOP', None, 0), - ('LOAD_CONST', 0, 0), + ('LOAD_COMMON_CONSTANT', 7, 0), ('RETURN_VALUE', None, 0), ] self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, frozenset({1, 2})]) @@ -1647,7 +1723,17 @@ def test_optimize_literal_set_contains(self): ('LOAD_CONST', 0, 0), ('RETURN_VALUE', None, 0), ] - self.cfg_optimization_test(same, same, consts=[None], expected_consts=[None]) + expected = [ + ('LOAD_NAME', 0, 0), + ('LOAD_SMALL_INT', 1, 0), + ('LOAD_NAME', 1, 0), + ('BUILD_SET', 2, 0), + ('CONTAINS_OP', 0, 0), + ('POP_TOP', None, 0), + ('LOAD_COMMON_CONSTANT', 7, 0), + ('RETURN_VALUE', None, 0), + ] + self.cfg_optimization_test(same, expected, consts=[None], expected_consts=[None]) def test_optimize_unary_not(self): # test folding @@ -1658,10 +1744,10 @@ def test_optimize_unary_not(self): ('RETURN_VALUE', None, 0), ] after = [ - ('LOAD_CONST', 1, 0), + ('LOAD_COMMON_CONSTANT', 10, 0), ('RETURN_VALUE', None, 0), ] - self.cfg_optimization_test(before, after, consts=[], expected_consts=[True, False]) + self.cfg_optimization_test(before, after, consts=[], expected_consts=[True]) # test cancel out before = [ @@ -1709,7 +1795,7 @@ def test_optimize_unary_not(self): ('RETURN_VALUE', None, 0), ] after = [ - ('LOAD_CONST', 0, 0), + ('LOAD_COMMON_CONSTANT', 9, 0), ('RETURN_VALUE', None, 0), ] self.cfg_optimization_test(before, after, consts=[], expected_consts=[True]) @@ -1725,10 +1811,10 @@ def test_optimize_unary_not(self): ('RETURN_VALUE', None, 0), ] after = [ - ('LOAD_CONST', 1, 0), + ('LOAD_COMMON_CONSTANT', 10, 0), ('RETURN_VALUE', None, 0), ] - self.cfg_optimization_test(before, after, consts=[], expected_consts=[True, False]) + self.cfg_optimization_test(before, after, consts=[], expected_consts=[True]) # test cancel out & eliminate to bool (to bool stays as we are not iterating to a fixed point) before = [ @@ -2344,7 +2430,7 @@ def test_list_to_tuple_get_iter(self): ("LOAD_FAST", 1, 4), ("LIST_EXTEND", 1, 5), ("CALL_INTRINSIC_1", INTRINSIC_LIST_TO_TUPLE, 6), - ("GET_ITER", None, 7), + ("GET_ITER", 0, 7), top := self.Label(), ("FOR_ITER", end := self.Label(), 8), ("STORE_FAST", 2, 9), @@ -2362,7 +2448,7 @@ def test_list_to_tuple_get_iter(self): ("LOAD_FAST_BORROW", 1, 4), ("LIST_EXTEND", 1, 5), ("NOP", None, 6), # ("CALL_INTRINSIC_1", INTRINSIC_LIST_TO_TUPLE, 6), - ("GET_ITER", None, 7), + ("GET_ITER", 0, 7), top := self.Label(), ("FOR_ITER", end := self.Label(), 8), ("STORE_FAST", 2, 9), @@ -2370,7 +2456,7 @@ def test_list_to_tuple_get_iter(self): end, ("END_FOR", None, 11), ("POP_TOP", None, 12), - ("LOAD_CONST", 0, 13), + ("LOAD_COMMON_CONSTANT", 7, 13), ("RETURN_VALUE", None, 14), ] self.cfg_optimization_test(insts, expected_insts, consts=[None]) @@ -2394,7 +2480,7 @@ def make_bb(self, insts): maxconst = max(maxconst, arg) consts = [None for _ in range(maxconst + 1)] return insts + [ - ("LOAD_CONST", 0, last_loc + 1), + ("LOAD_COMMON_CONSTANT", 7, last_loc + 1), ("RETURN_VALUE", None, last_loc + 2), ], consts @@ -2425,7 +2511,7 @@ def test_optimized(self): ] expected = [ ("LOAD_FAST_BORROW", 0, 1), - ("LOAD_CONST", 1, 2), + ("LOAD_COMMON_CONSTANT", 7, 2), ("SWAP", 2, 3), ("POP_TOP", None, 4), ] @@ -2463,7 +2549,13 @@ def test_unoptimized_if_support_killed(self): ("STORE_FAST", 0, 3), ("POP_TOP", None, 4), ] - self.check(insts, insts) + expected = [ + ("LOAD_FAST", 0, 1), + ("LOAD_COMMON_CONSTANT", 7, 2), + ("STORE_FAST", 0, 3), + ("POP_TOP", None, 4), + ] + self.check(insts, expected) insts = [ ("LOAD_FAST", 0, 1), @@ -2472,7 +2564,14 @@ def test_unoptimized_if_support_killed(self): ("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4), ("POP_TOP", None, 5), ] - self.check(insts, insts) + expected = [ + ("LOAD_FAST", 0, 1), + ("LOAD_COMMON_CONSTANT", 7, 2), + ("LOAD_COMMON_CONSTANT", 7, 3), + ("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4), + ("POP_TOP", None, 5), + ] + self.check(insts, expected) insts = [ ("LOAD_FAST", 0, 1), @@ -2493,7 +2592,12 @@ def test_unoptimized_if_aliased(self): ("LOAD_CONST", 0, 3), ("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4), ] - self.check(insts, insts) + expected = [ + ("LOAD_FAST", 0, 1), + ("LOAD_COMMON_CONSTANT", 7, 3), + ("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4), + ] + self.check(insts, expected) def test_consume_no_inputs(self): insts = [ @@ -2538,7 +2642,19 @@ def test_for_iter(self): ("LOAD_CONST", 0, 7), ("RETURN_VALUE", None, 8), ] - self.cfg_optimization_test(insts, insts, consts=[None]) + expected = [ + ("LOAD_FAST", 0, 1), + top := self.Label(), + ("FOR_ITER", end := self.Label(), 2), + ("STORE_FAST", 2, 3), + ("JUMP", top, 4), + end, + ("END_FOR", None, 5), + ("POP_TOP", None, 6), + ("LOAD_COMMON_CONSTANT", 7, 7), + ("RETURN_VALUE", None, 8), + ] + self.cfg_optimization_test(insts, expected, consts=[None]) def test_load_attr(self): insts = [ @@ -2607,10 +2723,10 @@ def test_send(self): ("LOAD_FAST", 0, 1), ("LOAD_FAST_BORROW", 1, 2), ("SEND", end := self.Label(), 3), - ("LOAD_CONST", 0, 4), + ("LOAD_COMMON_CONSTANT", 7, 4), ("RETURN_VALUE", None, 5), end, - ("LOAD_CONST", 0, 6), + ("LOAD_COMMON_CONSTANT", 7, 6), ("RETURN_VALUE", None, 7) ] self.cfg_optimization_test(insts, expected, consts=[None]) @@ -2648,7 +2764,15 @@ def test_set_function_attribute(self): ("LOAD_CONST", 0, 5), ("RETURN_VALUE", None, 6) ] - self.cfg_optimization_test(insts, insts, consts=[None]) + expected = [ + ("LOAD_COMMON_CONSTANT", 7, 1), + ("LOAD_FAST", 0, 2), + ("SET_FUNCTION_ATTRIBUTE", 2, 3), + ("STORE_FAST", 1, 4), + ("LOAD_COMMON_CONSTANT", 7, 5), + ("RETURN_VALUE", None, 6) + ] + self.cfg_optimization_test(insts, expected, consts=[None]) insts = [ ("LOAD_CONST", 0, 1), @@ -2657,7 +2781,7 @@ def test_set_function_attribute(self): ("RETURN_VALUE", None, 4) ] expected = [ - ("LOAD_CONST", 0, 1), + ("LOAD_COMMON_CONSTANT", 7, 1), ("LOAD_FAST_BORROW", 0, 2), ("SET_FUNCTION_ATTRIBUTE", 2, 3), ("RETURN_VALUE", None, 4) @@ -2665,22 +2789,37 @@ def test_set_function_attribute(self): self.cfg_optimization_test(insts, expected, consts=[None]) def test_get_yield_from_iter(self): - # GET_YIELD_FROM_ITER may leave its operand on the stack insts = [ ("LOAD_FAST", 0, 1), - ("GET_YIELD_FROM_ITER", None, 2), - ("LOAD_CONST", 0, 3), + ("GET_ITER", 1, 2), + ("PUSH_NULL", None, 3), + ("LOAD_CONST", 0, 4), send := self.Label(), - ("SEND", end := self.Label(), 5), - ("YIELD_VALUE", 1, 6), - ("RESUME", 2, 7), - ("JUMP", send, 8), + ("SEND", end := self.Label(), 6), + ("YIELD_VALUE", 1, 7), + ("RESUME", 2, 8), + ("JUMP", send, 9), end, - ("END_SEND", None, 9), - ("LOAD_CONST", 0, 10), - ("RETURN_VALUE", None, 11), + ("END_SEND", None, 10), + ("LOAD_CONST", 0, 11), + ("RETURN_VALUE", None, 12), ] - self.cfg_optimization_test(insts, insts, consts=[None]) + expected = [ + ("LOAD_FAST", 0, 1), + ("GET_ITER", 1, 2), + ("PUSH_NULL", None, 3), + ("LOAD_COMMON_CONSTANT", 7, 4), + send := self.Label(), + ("SEND", end := self.Label(), 6), + ("YIELD_VALUE", 1, 7), + ("RESUME", 2, 8), + ("JUMP", send, 9), + end, + ("END_SEND", None, 10), + ("LOAD_COMMON_CONSTANT", 7, 11), + ("RETURN_VALUE", None, 12), + ] + self.cfg_optimization_test(insts, expected, consts=[None]) def test_push_exc_info(self): insts = [ diff --git a/Lib/test/test_peg_generator/test_c_parser.py b/Lib/test/test_peg_generator/test_c_parser.py index aa01a9b8f7ed87..3500f229b1b386 100644 --- a/Lib/test/test_peg_generator/test_c_parser.py +++ b/Lib/test/test_peg_generator/test_c_parser.py @@ -100,11 +100,15 @@ def setUpClass(cls): with contextlib.ExitStack() as stack: python_exe = stack.enter_context(support.setup_venv_with_pip_setuptools("venv")) - sitepackages = subprocess.check_output( + platlib_path = subprocess.check_output( [python_exe, "-c", "import sysconfig; print(sysconfig.get_path('platlib'))"], text=True, ).strip() - stack.enter_context(import_helper.DirsOnSysPath(sitepackages)) + purelib_path = subprocess.check_output( + [python_exe, "-c", "import sysconfig; print(sysconfig.get_path('purelib'))"], + text=True, + ).strip() + stack.enter_context(import_helper.DirsOnSysPath(platlib_path, purelib_path)) cls.addClassCleanup(stack.pop_all().close) @support.requires_venv_with_pip() @@ -352,9 +356,9 @@ def test_same_name_different_types(self) -> None: grammar_source = """ start[mod_ty]: a[asdl_stmt_seq*]=import_from+ NEWLINE ENDMARKER { _PyAST_Module(a, NULL, p->arena)} import_from[stmt_ty]: ( a='from' !'import' c=simple_name 'import' d=import_as_names_from { - _PyAST_ImportFrom(c->v.Name.id, d, 0, EXTRA) } + _PyAST_ImportFrom(c->v.Name.id, d, 0, 0, EXTRA) } | a='from' '.' 'import' c=import_as_names_from { - _PyAST_ImportFrom(NULL, c, 1, EXTRA) } + _PyAST_ImportFrom(NULL, c, 1, 0, EXTRA) } ) simple_name[expr_ty]: NAME import_as_names_from[asdl_alias_seq*]: a[asdl_alias_seq*]=','.import_as_name_from+ { a } diff --git a/Lib/test/test_peg_generator/test_grammar_validator.py b/Lib/test/test_peg_generator/test_grammar_validator.py index c7f20e1de802ce..857aced8ae5dcf 100644 --- a/Lib/test/test_peg_generator/test_grammar_validator.py +++ b/Lib/test/test_peg_generator/test_grammar_validator.py @@ -4,7 +4,8 @@ test_tools.skip_if_missing("peg_generator") with test_tools.imports_under_tool("peg_generator"): from pegen.grammar_parser import GeneratedParser as GrammarParser - from pegen.validator import SubRuleValidator, ValidationError, RaiseRuleValidator + from pegen.validator import SubRuleValidator, ValidationError + from pegen.validator import RaiseRuleValidator, CutValidator from pegen.testutil import parse_string from pegen.grammar import Grammar @@ -59,3 +60,18 @@ def test_raising_valid_rule(self) -> None: with self.assertRaises(ValidationError): for rule_name, rule in grammar.rules.items(): validator.validate_rule(rule_name, rule) + + def test_cut_validator(self) -> None: + grammar_source = """ + star: (OP ~ OP)* + plus: (OP ~ OP)+ + bracket: [OP ~ OP] + gather: OP.(OP ~ OP)+ + nested: [OP | NAME ~ OP] + """ + grammar: Grammar = parse_string(grammar_source, GrammarParser) + validator = CutValidator(grammar) + for rule_name, rule in grammar.rules.items(): + with self.subTest(rule_name): + with self.assertRaises(ValidationError): + validator.validate_rule(rule_name, rule) diff --git a/Lib/test/test_peg_generator/test_pegen.py b/Lib/test/test_peg_generator/test_pegen.py index d912c55812397d..f39fcc2e0d8daf 100644 --- a/Lib/test/test_peg_generator/test_pegen.py +++ b/Lib/test/test_peg_generator/test_pegen.py @@ -755,6 +755,30 @@ def test_cut(self) -> None: ], ) + def test_cut_is_local_in_rule(self) -> None: + grammar = """ + start: + | inner + | 'x' { "ok" } + inner: + | 'x' ~ 'y' + | 'x' + """ + parser_class = make_parser(grammar) + node = parse_string("x", parser_class) + self.assertEqual(node, 'ok') + + def test_cut_is_local_in_parens(self) -> None: + # we currently don't guarantee this behavior, see gh-143054 + grammar = """ + start: + | ('x' ~ 'y' | 'x') + | 'x' { "ok" } + """ + parser_class = make_parser(grammar) + node = parse_string("x", parser_class) + self.assertEqual(node, 'ok') + def test_dangling_reference(self) -> None: grammar = """ start: foo ENDMARKER @@ -1106,3 +1130,53 @@ def test_deep_nested_rule(self) -> None: ) self.assertEqual(output, expected_output) + + def test_rule_flags(self) -> None: + """Test the new rule flags syntax that accepts arbitrary lists of flags.""" + # Test grammar with various flag combinations + grammar_source = """ + start: simple_rule + + simple_rule (memo): + | "hello" + + multi_flag_rule (memo, custom, test): + | "world" + + single_custom_flag (custom): + | "test" + + no_flags_rule: + | "plain" + """ + + grammar: Grammar = parse_string(grammar_source, GrammarParser) + rules = grammar.rules + + # Test memo-only rule + simple_rule = rules['simple_rule'] + self.assertTrue('memo' in simple_rule.flags, + "simple_rule should have memo") + self.assertEqual(simple_rule.flags, frozenset(['memo']), + f"simple_rule flags should be {'memo'}, got {simple_rule.flags}") + + # Test multi-flag rule + multi_flag_rule = rules['multi_flag_rule'] + self.assertTrue('memo' in simple_rule.flags, + "multi_flag_rule should have memo") + self.assertEqual(multi_flag_rule.flags, frozenset({'memo', 'custom', 'test'}), + f"multi_flag_rule flags should contain memo, custom, test, got {multi_flag_rule.flags}") + + # Test single custom flag rule + single_custom_rule = rules['single_custom_flag'] + self.assertFalse('memo' not in simple_rule.flags, + "single_custom_flag should not have memo") + self.assertEqual(single_custom_rule.flags, frozenset(['custom']), + f"single_custom_flag flags should be {'custom'}, got {single_custom_rule.flags}") + + # Test no flags rule + no_flags_rule = rules['no_flags_rule'] + self.assertFalse('memo' not in simple_rule.flags, + "no_flags_rule should not have memo") + self.assertEqual(no_flags_rule.flags, frozenset(), + f"no_flags_rule flags should be the empty set, got {no_flags_rule.flags}") diff --git a/Lib/test/test_perf_profiler.py b/Lib/test/test_perf_profiler.py index 7529c853f9c152..425c76dd01ed7c 100644 --- a/Lib/test/test_perf_profiler.py +++ b/Lib/test/test_perf_profiler.py @@ -34,6 +34,21 @@ def supports_trampoline_profiling(): raise unittest.SkipTest("perf trampoline profiling not supported") +def _perf_env(**env_vars): + env = os.environ.copy() + # Keep perf's output stable regardless of the builder's perf config. + env.update( + { + "DEBUGINFOD_URLS": "", + "PERF_CONFIG": os.devnull, + } + ) + if env_vars: + env.update(env_vars) + env["PYTHON_JIT"] = "0" + return env + + class TestPerfTrampoline(unittest.TestCase): def setUp(self): super().setUp() @@ -63,13 +78,12 @@ def baz(): """ with temp_dir() as script_dir: script = make_script(script_dir, "perftest", code) - env = {**os.environ, "PYTHON_JIT": "0"} with subprocess.Popen( [sys.executable, "-Xperf", script], text=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, - env=env, + env=_perf_env(), ) as process: stdout, stderr = process.communicate() @@ -132,13 +146,12 @@ def baz(): """ with temp_dir() as script_dir: script = make_script(script_dir, "perftest", code) - env = {**os.environ, "PYTHON_JIT": "0"} with subprocess.Popen( [sys.executable, "-Xperf", script], text=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, - env=env, + env=_perf_env(), ) as process: stdout, stderr = process.communicate() @@ -160,50 +173,106 @@ def baz(): self.assertIn(f"py::bar_fork:{script}", child_perf_file_contents) self.assertIn(f"py::baz_fork:{script}", child_perf_file_contents) - @unittest.skipIf(support.check_bolt_optimized(), "fails on BOLT instrumented binaries") - def test_sys_api(self): - code = """if 1: - import sys - def foo(): - pass - - def spam(): - pass - - def bar(): - sys.deactivate_stack_trampoline() - foo() - sys.activate_stack_trampoline("perf") - spam() + # The parent's map should not contain the child's symbols. + self.assertNotIn(f"py::foo_fork:{script}", perf_file_contents) + self.assertNotIn(f"py::bar_fork:{script}", perf_file_contents) + self.assertNotIn(f"py::baz_fork:{script}", perf_file_contents) - def baz(): - bar() + # The child's map should not contain the parent's symbols. + self.assertNotIn(f"py::foo:{script}", child_perf_file_contents) + self.assertNotIn(f"py::bar:{script}", child_perf_file_contents) + self.assertNotIn(f"py::baz:{script}", child_perf_file_contents) - sys.activate_stack_trampoline("perf") - baz() + @unittest.skipIf(support.check_bolt_optimized(), "fails on BOLT instrumented binaries") + def test_trampoline_works_after_fork_with_many_code_objects(self): + code = """if 1: + import gc, os, sys, signal + + # Create many code objects so trampoline_refcount > 1 + for i in range(50): + exec(compile(f"def _dummy_{i}(): pass", f"<test{i}>", "exec")) + + pid = os.fork() + if pid == 0: + # Child: create and destroy new code objects, + # then collect garbage. If the old code watcher + # survived the fork, the double-decrement of + # trampoline_refcount will cause a SIGSEGV. + for i in range(50): + exec(compile(f"def _child_{i}(): pass", f"<child{i}>", "exec")) + gc.collect() + os._exit(0) + else: + _, status = os.waitpid(pid, 0) + if os.WIFSIGNALED(status): + print(f"FAIL: child killed by signal {os.WTERMSIG(status)}", file=sys.stderr) + sys.exit(1) + sys.exit(os.WEXITSTATUS(status)) """ with temp_dir() as script_dir: script = make_script(script_dir, "perftest", code) - env = {**os.environ, "PYTHON_JIT": "0"} with subprocess.Popen( - [sys.executable, script], + [sys.executable, "-Xperf", script], text=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, - env=env, + env=_perf_env(), ) as process: stdout, stderr = process.communicate() + self.assertEqual(process.returncode, 0, stderr) self.assertEqual(stderr, "") - self.assertEqual(stdout, "") - perf_file = pathlib.Path(f"/tmp/perf-{process.pid}.map") - self.assertTrue(perf_file.exists()) - perf_file_contents = perf_file.read_text() - self.assertNotIn(f"py::foo:{script}", perf_file_contents) - self.assertIn(f"py::spam:{script}", perf_file_contents) - self.assertIn(f"py::bar:{script}", perf_file_contents) - self.assertIn(f"py::baz:{script}", perf_file_contents) + @unittest.skipIf(support.check_bolt_optimized(), "fails on BOLT instrumented binaries") + def test_sys_api(self): + for define_eval_hook in (False, True): + code = """if 1: + import sys + def foo(): + pass + + def spam(): + pass + + def bar(): + sys.deactivate_stack_trampoline() + foo() + sys.activate_stack_trampoline("perf") + spam() + + def baz(): + bar() + + sys.activate_stack_trampoline("perf") + baz() + """ + if define_eval_hook: + set_eval_hook = """if 1: + import _testinternalcapi + _testinternalcapi.set_eval_frame_record([]) +""" + code = set_eval_hook + code + with temp_dir() as script_dir: + script = make_script(script_dir, "perftest", code) + with subprocess.Popen( + [sys.executable, script], + text=True, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + env=_perf_env(), + ) as process: + stdout, stderr = process.communicate() + + self.assertEqual(stderr, "") + self.assertEqual(stdout, "") + + perf_file = pathlib.Path(f"/tmp/perf-{process.pid}.map") + self.assertTrue(perf_file.exists()) + perf_file_contents = perf_file.read_text() + self.assertNotIn(f"py::foo:{script}", perf_file_contents) + self.assertIn(f"py::spam:{script}", perf_file_contents) + self.assertIn(f"py::bar:{script}", perf_file_contents) + self.assertIn(f"py::baz:{script}", perf_file_contents) def test_sys_api_with_existing_trampoline(self): code = """if 1: @@ -231,6 +300,24 @@ def test_sys_api_get_status(self): """ assert_python_ok("-c", code, PYTHON_JIT="0") + def test_sys_api_perf_jit_backend(self): + code = """if 1: + import sys + sys.activate_stack_trampoline("perf_jit") + assert sys.is_stack_trampoline_active() is True + sys.deactivate_stack_trampoline() + assert sys.is_stack_trampoline_active() is False + """ + assert_python_ok("-c", code, PYTHON_JIT="0") + + def test_sys_api_with_existing_perf_jit_trampoline(self): + code = """if 1: + import sys + sys.activate_stack_trampoline("perf_jit") + sys.activate_stack_trampoline("perf_jit") + """ + assert_python_ok("-c", code, PYTHON_JIT="0") + def is_unwinding_reliable_with_frame_pointers(): cflags = sysconfig.get_config_var("PY_CORE_CFLAGS") @@ -269,9 +356,12 @@ def perf_command_works(): "-c", 'print("hello")', ) - env = {**os.environ, "PYTHON_JIT": "0"} stdout = subprocess.check_output( - cmd, cwd=script_dir, text=True, stderr=subprocess.STDOUT, env=env + cmd, + cwd=script_dir, + text=True, + stderr=subprocess.STDOUT, + env=_perf_env(), ) except (subprocess.SubprocessError, OSError): return False @@ -283,42 +373,49 @@ def perf_command_works(): def run_perf(cwd, *args, use_jit=False, **env_vars): - env = os.environ.copy() - if env_vars: - env.update(env_vars) - env["PYTHON_JIT"] = "0" + env = _perf_env(**env_vars) output_file = cwd + "/perf_output.perf" - if not use_jit: - base_cmd = ( - "perf", - "record", - "--no-buildid", - "--no-buildid-cache", - "-g", - "--call-graph=fp", - "-o", output_file, - "--" - ) + base_cmd = [ + "perf", + "record", + "--no-buildid", + "--no-buildid-cache", + "-g", + "--call-graph=dwarf,65528" if use_jit else "--call-graph=fp", + ] + if use_jit: + perf_commands = [] + # Some builders have low perf_event_mlock_kb limits. + mmap_sizes = ("4M", "2M", "1M", "512K", "256K", "128K", None) + for mmap_size in mmap_sizes: + command = base_cmd.copy() + if mmap_size is not None: + command += ["-F99", "-k1", "-m", mmap_size] + else: + command += ["-F99", "-k1"] + command += ["-o", output_file, "--"] + perf_commands.append(command) else: - base_cmd = ( - "perf", - "record", - "--no-buildid", - "--no-buildid-cache", - "-g", - "--call-graph=dwarf,65528", - "-F99", - "-k1", - "-o", - output_file, - "--", + perf_commands = [base_cmd + ["-o", output_file, "--"]] + + mmap_pages_error = "try again with a smaller value of -m/--mmap_pages" + for index, base_cmd in enumerate(perf_commands): + proc = subprocess.run( + base_cmd + list(args), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + text=True, ) - proc = subprocess.run( - base_cmd + args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=env, - ) + if ( + proc.returncode + and use_jit + and index != len(perf_commands) - 1 + and mmap_pages_error in proc.stderr + ): + continue + break + if proc.returncode: print(proc.stderr, file=sys.stderr) raise ValueError(f"Perf failed with return code {proc.returncode}") @@ -327,10 +424,10 @@ def run_perf(cwd, *args, use_jit=False, **env_vars): jit_output_file = cwd + "/jit_output.dump" command = ("perf", "inject", "-j", "-i", output_file, "-o", jit_output_file) proc = subprocess.run( - command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, env=env + command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, env=env, text=True ) if proc.returncode: - print(proc.stderr) + print(proc.stderr, file=sys.stderr) raise ValueError(f"Perf failed with return code {proc.returncode}") # Copy the jit_output_file to the output_file os.rename(jit_output_file, output_file) @@ -342,23 +439,40 @@ def run_perf(cwd, *args, use_jit=False, **env_vars): stderr=subprocess.PIPE, env=env, check=True, + text=True, ) - return proc.stdout.decode("utf-8", "replace"), proc.stderr.decode( - "utf-8", "replace" - ) + return proc.stdout, proc.stderr class TestPerfProfilerMixin: - def run_perf(self, script_dir, perf_mode, script): + PERF_CAPTURE_ATTEMPTS = 3 + + def run_perf(self, script_dir, script, activate_trampoline=True): raise NotImplementedError() + def run_perf_with_retries( + self, script_dir, script, expected_symbols=(), activate_trampoline=True + ): + stdout = stderr = "" + for _ in range(self.PERF_CAPTURE_ATTEMPTS): + stdout, stderr = self.run_perf( + script_dir, script, activate_trampoline=activate_trampoline + ) + if activate_trampoline and any( + symbol not in stdout for symbol in expected_symbols + ): + continue + break + return stdout, stderr + def test_python_calls_appear_in_the_stack_if_perf_activated(self): with temp_dir() as script_dir: code = """if 1: + from itertools import repeat + def foo(n): - x = 0 - for i in range(n): - x += i + for _ in repeat(None, n): + pass def bar(n): foo(n) @@ -366,23 +480,29 @@ def bar(n): def baz(n): bar(n) - baz(10000000) + baz(40000000) """ script = make_script(script_dir, "perftest", code) - stdout, stderr = self.run_perf(script_dir, script) - self.assertEqual(stderr, "") + expected_symbols = [ + f"py::foo:{script}", + f"py::bar:{script}", + f"py::baz:{script}", + ] + stdout, _ = self.run_perf_with_retries( + script_dir, script, expected_symbols + ) - self.assertIn(f"py::foo:{script}", stdout) - self.assertIn(f"py::bar:{script}", stdout) - self.assertIn(f"py::baz:{script}", stdout) + for expected_symbol in expected_symbols: + self.assertIn(expected_symbol, stdout) def test_python_calls_do_not_appear_in_the_stack_if_perf_deactivated(self): with temp_dir() as script_dir: code = """if 1: + from itertools import repeat + def foo(n): - x = 0 - for i in range(n): - x += i + for _ in repeat(None, n): + pass def bar(n): foo(n) @@ -390,13 +510,12 @@ def bar(n): def baz(n): bar(n) - baz(10000000) + baz(40000000) """ script = make_script(script_dir, "perftest", code) - stdout, stderr = self.run_perf( + stdout, _ = self.run_perf_with_retries( script_dir, script, activate_trampoline=False ) - self.assertEqual(stderr, "") self.assertNotIn(f"py::foo:{script}", stdout) self.assertNotIn(f"py::bar:{script}", stdout) @@ -466,13 +585,12 @@ def compile_trampolines_for_all_functions(): with temp_dir() as script_dir: script = make_script(script_dir, "perftest", code) - env = {**os.environ, "PYTHON_JIT": "0"} with subprocess.Popen( [sys.executable, "-Xperf", script], universal_newlines=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, - env=env, + env=_perf_env(), ) as process: stdout, stderr = process.communicate() diff --git a/Lib/test/test_perfmaps.py b/Lib/test/test_perfmaps.py index 647c32656abd6d..ee4eb50033c470 100644 --- a/Lib/test/test_perfmaps.py +++ b/Lib/test/test_perfmaps.py @@ -1,4 +1,5 @@ import os +import sys import sysconfig import unittest @@ -17,6 +18,9 @@ def supports_trampoline_profiling(): raise unittest.SkipTest("perf trampoline profiling not supported") class TestPerfMapWriting(unittest.TestCase): + def tearDown(self): + perf_map_state_teardown() + def test_write_perf_map_entry(self): self.assertEqual(write_perf_map_entry(0x1234, 5678, "entry1"), 0) self.assertEqual(write_perf_map_entry(0x2345, 6789, "entry2"), 0) @@ -24,4 +28,15 @@ def test_write_perf_map_entry(self): perf_file_contents = f.read() self.assertIn("1234 162e entry1", perf_file_contents) self.assertIn("2345 1a85 entry2", perf_file_contents) - perf_map_state_teardown() + + @unittest.skipIf(sys.maxsize <= 2**32, "requires size_t wider than unsigned int") + def test_write_perf_map_entry_large_size(self): + code_addr = 0x3456 + code_size = 1 << 33 + entry_name = "entry_big" + + self.assertEqual(write_perf_map_entry(code_addr, code_size, entry_name), 0) + with open(f"/tmp/perf-{os.getpid()}.map") as f: + perf_file_contents = f.read() + self.assertIn(f"{code_addr:x} {code_size:x} {entry_name}", + perf_file_contents) diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py index e2384b33345a45..48375cf459ea0b 100644 --- a/Lib/test/test_pickle.py +++ b/Lib/test/test_pickle.py @@ -59,6 +59,8 @@ class PyUnpicklerTests(AbstractUnpickleTests, unittest.TestCase): truncated_errors = (pickle.UnpicklingError, EOFError, AttributeError, ValueError, struct.error, IndexError, ImportError) + truncated_data_error = (EOFError, '') + size_overflow_error = (pickle.UnpicklingError, 'exceeds') def loads(self, buf, **kwds): f = io.BytesIO(buf) @@ -103,6 +105,8 @@ class InMemoryPickleTests(AbstractPickleTests, AbstractUnpickleTests, truncated_errors = (pickle.UnpicklingError, EOFError, AttributeError, ValueError, struct.error, IndexError, ImportError) + truncated_data_error = ((pickle.UnpicklingError, EOFError), '') + size_overflow_error = ((OverflowError, pickle.UnpicklingError), 'exceeds') def dumps(self, arg, protocol=None, **kwargs): return pickle.dumps(arg, protocol, **kwargs) @@ -375,6 +379,8 @@ class CUnpicklerTests(PyUnpicklerTests): unpickler = _pickle.Unpickler bad_stack_errors = (pickle.UnpicklingError,) truncated_errors = (pickle.UnpicklingError,) + truncated_data_error = (pickle.UnpicklingError, 'truncated') + size_overflow_error = (OverflowError, 'exceeds') class CPicklingErrorTests(PyPicklingErrorTests): pickler = _pickle.Pickler @@ -413,6 +419,46 @@ def test_issue18339(self): unpickler.memo = {-1: None} unpickler.memo = {1: None} + def test_concurrent_pickler_dump(self): + f = io.BytesIO() + pickler = self.pickler_class(f) + class X: + def __reduce__(slf): + self.assertRaises(RuntimeError, pickler.dump, 42) + return list, () + pickler.dump(X()) # should not crash + self.assertEqual(pickle.loads(f.getvalue()), []) + + def test_concurrent_pickler_dump_and_init(self): + f = io.BytesIO() + pickler = self.pickler_class(f) + class X: + def __reduce__(slf): + self.assertRaises(RuntimeError, pickler.__init__, f) + return list, () + pickler.dump([X()]) # should not fail + self.assertEqual(pickle.loads(f.getvalue()), [[]]) + + def test_concurrent_unpickler_load(self): + global reducer + def reducer(): + self.assertRaises(RuntimeError, unpickler.load) + return 42 + f = io.BytesIO(b'(c%b\nreducer\n(tRl.' % (__name__.encode(),)) + unpickler = self.unpickler_class(f) + unpickled = unpickler.load() # should not fail + self.assertEqual(unpickled, [42]) + + def test_concurrent_unpickler_load_and_init(self): + global reducer + def reducer(): + self.assertRaises(RuntimeError, unpickler.__init__, f) + return 42 + f = io.BytesIO(b'(c%b\nreducer\n(tRl.' % (__name__.encode(),)) + unpickler = self.unpickler_class(f) + unpickled = unpickler.load() # should not crash + self.assertEqual(unpickled, [42]) + class CDispatchTableTests(AbstractDispatchTableTests, unittest.TestCase): pickler_class = pickle.Pickler def get_dispatch_table(self): @@ -461,7 +507,7 @@ class SizeofTests(unittest.TestCase): check_sizeof = support.check_sizeof def test_pickler(self): - basesize = support.calcobjsize('7P2n3i2n3i2P') + basesize = support.calcobjsize('7P2n3i2n4i2P') p = _pickle.Pickler(io.BytesIO()) self.assertEqual(object.__sizeof__(p), basesize) MT_size = struct.calcsize('3nP0n') @@ -478,7 +524,7 @@ def test_pickler(self): 0) # Write buffer is cleared after every dump(). def test_unpickler(self): - basesize = support.calcobjsize('2P2n2P 2P2n2i5P 2P3n8P2n2i') + basesize = support.calcobjsize('2P2n3P 2P2n2i5P 2P3n8P2n3i') unpickler = _pickle.Unpickler P = struct.calcsize('P') # Size of memo table entry. n = struct.calcsize('n') # Size of mark table entry. diff --git a/Lib/test/test_pickletools.py b/Lib/test/test_pickletools.py index a178d3353eecdf..caf2d7ba6bfd8f 100644 --- a/Lib/test/test_pickletools.py +++ b/Lib/test/test_pickletools.py @@ -1,7 +1,11 @@ import io +import itertools import pickle import pickletools +import tempfile +import textwrap from test import support +from test.support import os_helper from test.pickletester import AbstractPickleTests import doctest import unittest @@ -156,6 +160,7 @@ def test_unknown_opcode_without_pos(self): next(it) +@support.force_not_colorized_test_class class DisTests(unittest.TestCase): maxDiff = None @@ -384,13 +389,13 @@ def test_string_without_quotes(self): self.check_dis_error(b'Sabc"\n.', '', "no string quotes around b'abc\"'") self.check_dis_error(b"S'abc\n.", '', - '''strinq quote b"'" not found at both ends of b"'abc"''') + '''string quote b"'" not found at both ends of b"'abc"''') self.check_dis_error(b'S"abc\n.', '', - r"""strinq quote b'"' not found at both ends of b'"abc'""") + r"""string quote b'"' not found at both ends of b'"abc'""") self.check_dis_error(b"S'abc\"\n.", '', - r"""strinq quote b"'" not found at both ends of b'\\'abc"'""") + r"""string quote b"'" not found at both ends of b'\\'abc"'""") self.check_dis_error(b"S\"abc'\n.", '', - r"""strinq quote b'"' not found at both ends of b'"abc\\''""") + r"""string quote b'"' not found at both ends of b'"abc\\''""") def test_binstring(self): self.check_dis(b"T\x03\x00\x00\x00abc.", '''\ @@ -514,6 +519,171 @@ def test__all__(self): support.check__all__(self, pickletools, not_exported=not_exported) +@support.force_not_colorized_test_class +class CommandLineTest(unittest.TestCase): + def setUp(self): + self.filename = tempfile.mktemp() + self.addCleanup(os_helper.unlink, self.filename) + + @staticmethod + def text_normalize(string): + return textwrap.dedent(string).strip() + + def set_pickle_data(self, data): + with open(self.filename, 'wb') as f: + pickle.dump(data, f) + + def invoke_pickletools(self, *flags): + with ( + support.captured_stdout() as stdout, + support.captured_stderr() as stderr, + ): + pickletools._main(args=[*flags, self.filename]) + self.assertEqual(stderr.getvalue(), '') + return self.text_normalize(stdout.getvalue()) + + def check_output(self, data, expect, *flags): + with self.subTest(data=data, flags=flags): + self.set_pickle_data(data) + res = self.invoke_pickletools(*flags) + expect = self.text_normalize(expect) + self.assertListEqual(res.splitlines(), expect.splitlines()) + + def test_invocation(self): + # test various combinations of parameters + output_file = tempfile.mktemp() + self.addCleanup(os_helper.unlink, output_file) + base_flags = [ + (f'-o={output_file}', f'--output={output_file}'), + ('-m', '--memo'), + ('-l=2', '--indentlevel=2'), + ('-a', '--annotate'), + ('-p="Another:"', '--preamble="Another:"'), + ] + data = { 'a', 'b', 'c' } + + self.set_pickle_data(data) + + for r in range(1, len(base_flags) + 1): + for choices in itertools.combinations(base_flags, r=r): + for args in itertools.product(*choices): + with self.subTest(args=args[1:]): + self.invoke_pickletools(*args) + + def test_unknown_flag(self): + with self.assertRaises(SystemExit): + with support.captured_stderr() as stderr: + pickletools._main(args=['--unknown']) + self.assertStartsWith(stderr.getvalue(), 'usage: ') + + def test_output_flag(self): + # test 'python -m pickletools -o/--output' + output_file = tempfile.mktemp() + self.addCleanup(os_helper.unlink, output_file) + data = ('fake_data',) + expect = r''' + 0: \x80 PROTO 5 + 2: \x95 FRAME 15 + 11: \x8c SHORT_BINUNICODE 'fake_data' + 22: \x94 MEMOIZE (as 0) + 23: \x85 TUPLE1 + 24: \x94 MEMOIZE (as 1) + 25: . STOP + highest protocol among opcodes = 4 + ''' + for flag in [f'-o={output_file}', f'--output={output_file}']: + with self.subTest(data=data, flags=flag): + self.set_pickle_data(data) + res = self.invoke_pickletools(flag) + with open(output_file, 'r') as f: + res_from_file = self.text_normalize(f.read()) + expect = self.text_normalize(expect) + + self.assertListEqual(res.splitlines(), []) + self.assertListEqual(res_from_file.splitlines(), + expect.splitlines()) + + def test_memo_flag(self): + # test 'python -m pickletools -m/--memo' + data = ('fake_data',) + expect = r''' + 0: \x80 PROTO 5 + 2: \x95 FRAME 15 + 11: \x8c SHORT_BINUNICODE 'fake_data' + 22: \x94 MEMOIZE (as 0) + 23: \x85 TUPLE1 + 24: \x94 MEMOIZE (as 1) + 25: . STOP + highest protocol among opcodes = 4 + ''' + for flag in ['-m', '--memo']: + self.check_output(data, expect, flag) + + def test_indentlevel_flag(self): + # test 'python -m pickletools -l/--indentlevel' + data = ('fake_data',) + expect = r''' + 0: \x80 PROTO 5 + 2: \x95 FRAME 15 + 11: \x8c SHORT_BINUNICODE 'fake_data' + 22: \x94 MEMOIZE (as 0) + 23: \x85 TUPLE1 + 24: \x94 MEMOIZE (as 1) + 25: . STOP + highest protocol among opcodes = 4 + ''' + for flag in ['-l=2', '--indentlevel=2']: + self.check_output(data, expect, flag) + + def test_annotate_flag(self): + # test 'python -m pickletools -a/--annotate' + data = ('fake_data',) + expect = r''' + 0: \x80 PROTO 5 Protocol version indicator. + 2: \x95 FRAME 15 Indicate the beginning of a new frame. + 11: \x8c SHORT_BINUNICODE 'fake_data' Push a Python Unicode string object. + 22: \x94 MEMOIZE (as 0) Store the stack top into the memo. The stack is not popped. + 23: \x85 TUPLE1 Build a one-tuple out of the topmost item on the stack. + 24: \x94 MEMOIZE (as 1) Store the stack top into the memo. The stack is not popped. + 25: . STOP Stop the unpickling machine. + highest protocol among opcodes = 4 + ''' + for flag in ['-a', '--annotate']: + self.check_output(data, expect, flag) + + def test_preamble_flag(self): + # test 'python -m pickletools -p/--preamble' + data = ('fake_data',) + expect = r''' + Another: + 0: \x80 PROTO 5 + 2: \x95 FRAME 15 + 11: \x8c SHORT_BINUNICODE 'fake_data' + 22: \x94 MEMOIZE (as 0) + 23: \x85 TUPLE1 + 24: \x94 MEMOIZE (as 1) + 25: . STOP + highest protocol among opcodes = 4 + Another: + 0: \x80 PROTO 5 + 2: \x95 FRAME 15 + 11: \x8c SHORT_BINUNICODE 'fake_data' + 22: \x94 MEMOIZE (as 0) + 23: \x85 TUPLE1 + 24: \x94 MEMOIZE (as 1) + 25: . STOP + highest protocol among opcodes = 4 + ''' + for flag in ['-p=Another:', '--preamble=Another:']: + with self.subTest(data=data, flags=flag): + self.set_pickle_data(data) + with support.captured_stdout() as stdout: + pickletools._main(args=[flag, self.filename, self.filename]) + res = self.text_normalize(stdout.getvalue()) + expect = self.text_normalize(expect) + self.assertListEqual(res.splitlines(), expect.splitlines()) + + def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite(pickletools)) return tests diff --git a/Lib/test/test_pkg.py b/Lib/test/test_pkg.py index d2b724db40d3e9..0a366e2a5bb2d1 100644 --- a/Lib/test/test_pkg.py +++ b/Lib/test/test_pkg.py @@ -198,15 +198,15 @@ def test_5(self): import t5 self.assertEqual(fixdir(dir(t5)), - ['__cached__', '__doc__', '__file__', '__loader__', - '__name__', '__package__', '__path__', '__spec__', - 'foo', 'string', 't5']) + ['__doc__', '__file__', '__loader__', '__name__', + '__package__', '__path__', '__spec__', 'foo', + 'string', 't5']) self.assertEqual(fixdir(dir(t5.foo)), - ['__cached__', '__doc__', '__file__', '__loader__', - '__name__', '__package__', '__spec__', 'string']) + ['__doc__', '__file__', '__loader__', '__name__', + '__package__', '__spec__', 'string']) self.assertEqual(fixdir(dir(t5.string)), - ['__cached__', '__doc__', '__file__', '__loader__', - '__name__', '__package__', '__spec__', 'spam']) + ['__doc__', '__file__', '__loader__', '__name__', + '__package__', '__spec__', 'spam']) def test_6(self): hier = [ @@ -221,14 +221,13 @@ def test_6(self): import t6 self.assertEqual(fixdir(dir(t6)), - ['__all__', '__cached__', '__doc__', '__file__', - '__loader__', '__name__', '__package__', '__path__', - '__spec__']) + ['__all__', '__doc__', '__file__', '__loader__', + '__name__', '__package__', '__path__', '__spec__']) s = """ import t6 from t6 import * self.assertEqual(fixdir(dir(t6)), - ['__all__', '__cached__', '__doc__', '__file__', + ['__all__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'eggs', 'ham', 'spam']) self.assertEqual(dir(), ['eggs', 'ham', 'self', 'spam', 't6']) @@ -256,20 +255,19 @@ def test_7(self): t7, sub, subsub = None, None, None import t7 as tas self.assertEqual(fixdir(dir(tas)), - ['__cached__', '__doc__', '__file__', '__loader__', - '__name__', '__package__', '__path__', '__spec__']) + ['__doc__', '__file__', '__loader__', '__name__', + '__package__', '__path__', '__spec__']) self.assertFalse(t7) from t7 import sub as subpar self.assertEqual(fixdir(dir(subpar)), - ['__cached__', '__doc__', '__file__', '__loader__', - '__name__', '__package__', '__path__', '__spec__']) + ['__doc__', '__file__', '__loader__', '__name__', + '__package__', '__path__', '__spec__']) self.assertFalse(t7) self.assertFalse(sub) from t7.sub import subsub as subsubsub self.assertEqual(fixdir(dir(subsubsub)), - ['__cached__', '__doc__', '__file__', '__loader__', - '__name__', '__package__', '__path__', '__spec__', - 'spam']) + ['__doc__', '__file__', '__loader__', '__name__', + '__package__', '__path__', '__spec__', 'spam']) self.assertFalse(t7) self.assertFalse(sub) self.assertFalse(subsub) diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py index d4faaaeca00457..4623b7eb4434b0 100644 --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -1,3 +1,5 @@ +import logging +import logging.handlers from pathlib import Path from test.support.import_helper import unload from test.support.warnings_helper import check_warnings @@ -232,9 +234,6 @@ def test_walk_packages_raises_on_string_or_bytes_input(self): list(pkgutil.walk_packages(bytes_input)) def test_name_resolution(self): - import logging - import logging.handlers - success_cases = ( ('os', os), ('os.path', os.path), @@ -322,6 +321,53 @@ def test_name_resolution(self): with self.assertRaises(exc): pkgutil.resolve_name(s) + def test_name_resolution_strict(self): + # PEP 829: strict=True accepts only the pkg.mod:callable form + # (W(.W)*:W(.W)*) -- both the colon and the callable are required. + success_cases = ( + ('os.path:pathsep', os.path.pathsep), + ('logging.handlers:SysLogHandler', + logging.handlers.SysLogHandler), + ('logging.handlers:SysLogHandler.LOG_ALERT', + logging.handlers.SysLogHandler.LOG_ALERT), + ('builtins:int', int), + ('builtins:int.from_bytes', int.from_bytes), + ('os:path', os.path), + ) + + # All of these are accepted under strict=False but must be + # rejected under strict=True. + failure_cases = ( + 'os', # no colon (non-strict form) + 'os.path', # no colon + 'logging:', # colon, empty callable + 'os.foo:', # colon, empty callable + ':int', # empty package + 'os.path:join:extra', # extra colon + 'os.path.9abc:join', # invalid identifier in package + 'os.path:9abc', # invalid identifier in callable + '', # empty + '?abc:foo', # invalid character + ) + + for s, expected in success_cases: + with self.subTest(s=s): + self.assertEqual( + pkgutil.resolve_name(s, strict=True), expected) + + for s in failure_cases: + with self.subTest(s=s): + with self.assertRaises(ValueError): + pkgutil.resolve_name(s, strict=True) + + # Cache independence: a strict=True call must not poison + # strict=False (and vice versa). Exercise both orderings. + self.assertEqual( + pkgutil.resolve_name('os:path', strict=True), os.path) + self.assertEqual(pkgutil.resolve_name('os.path'), os.path) + self.assertEqual( + pkgutil.resolve_name('os:path', strict=True), os.path) + def test_name_resolution_import_rebinding(self): # The same data is also used for testing import in test_import and # mock.patch in test_unittest. diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 479649053abc01..63c130813ec497 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -11,7 +11,7 @@ from unittest import mock from test import support -from test.support import os_helper +from test.support import os_helper, warnings_helper try: # Some of the iOS tests need ctypes to operate. @@ -465,7 +465,7 @@ def test_mac_ver(self): else: self.assertEqual(res[2], 'PowerPC') - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(sys.platform == 'darwin', "OSX only test") def test_mac_ver_with_fork(self): # Issue7895: platform.mac_ver() crashes when using fork without exec @@ -532,8 +532,10 @@ def test_ios_ver(self): self.assertEqual(override.model, "Whiz") self.assertTrue(override.is_simulator) - @unittest.skipIf(support.is_emscripten, "Does not apply to Emscripten") def test_libc_ver(self): + if support.is_emscripten: + assert platform.libc_ver() == ("emscripten", "4.0.19") + return # check that libc_ver(executable) doesn't raise an exception if os.path.isdir(sys.executable) and \ os.path.exists(sys.executable+'.exe'): @@ -565,6 +567,10 @@ def test_libc_ver(self): # musl uses semver, but we accept some variations anyway: (b'/aports/main/musl/src/musl-12.5', ('musl', '12.5')), (b'/aports/main/musl/src/musl-1.2.5.7', ('musl', '1.2.5.7')), + (b'libc.musl.so.1', ('musl', '1')), + (b'libc.musl-x86_64.so.1.2.5', ('musl', '1.2.5')), + (b'ld-musl.so.1', ('musl', '1')), + (b'ld-musl-x86_64.so.1.2.5', ('musl', '1.2.5')), (b'', ('', '')), ): with open(filename, 'wb') as fp: @@ -583,6 +589,14 @@ def test_libc_ver(self): (b'GLIBC_1.23.4\0GLIBC_1.9\0GLIBC_1.21\0', ('glibc', '1.23.4')), (b'libc.so.2.4\0libc.so.9\0libc.so.23.1\0', ('libc', '23.1')), (b'musl-1.4.1\0musl-2.1.1\0musl-2.0.1\0', ('musl', '2.1.1')), + ( + b'libc.musl-x86_64.so.1.4.1\0libc.musl-x86_64.so.2.1.1\0libc.musl-x86_64.so.2.0.1', + ('musl', '2.1.1'), + ), + ( + b'ld-musl-x86_64.so.1.4.1\0ld-musl-x86_64.so.2.1.1\0ld-musl-x86_64.so.2.0.1', + ('musl', '2.1.1'), + ), (b'no match here, so defaults are used', ('test', '100.1.0')), ): with open(filename, 'wb') as f: @@ -762,13 +776,14 @@ def invoke_platform(self, *flags): platform._main(args=flags) return output.getvalue() + @support.force_not_colorized def test_unknown_flag(self): + output = io.StringIO() with self.assertRaises(SystemExit): - output = io.StringIO() # suppress argparse error message with contextlib.redirect_stderr(output): _ = self.invoke_platform('--unknown') - self.assertStartsWith(output, "usage: ") + self.assertStartsWith(output.getvalue(), "usage: ") def test_invocation(self): flags = ( diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index a0c76e5dec5ebe..b9c261310bb567 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -509,6 +509,69 @@ def test_bytes(self): data2 = plistlib.dumps(pl2) self.assertEqual(data, data2) + def test_bytes_indent(self): + header = ( + b'<?xml version="1.0" encoding="UTF-8"?>\n' + b'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' + b'<plist version="1.0">\n') + data = [{'bytes': bytes(range(50))}] + pl = plistlib.dumps(data) + self.assertEqual(pl, header + + b'<array>\n' + b'\t<dict>\n' + b'\t\t<key>bytes</key>\n' + b'\t\t<data>\n' + b'\t\tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss\n' + b'\t\tLS4vMDE=\n' + b'\t\t</data>\n' + b'\t</dict>\n' + b'</array>\n' + b'</plist>\n') + + def dumps_with_indent(data, indent): + fp = BytesIO() + writer = plistlib._PlistWriter(fp, indent=indent) + writer.write(data) + return fp.getvalue() + + pl = dumps_with_indent(data, b' ') + self.assertEqual(pl, header + + b'<array>\n' + b' <dict>\n' + b' <key>bytes</key>\n' + b' <data>\n' + b' AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDE=\n' + b' </data>\n' + b' </dict>\n' + b'</array>\n' + b'</plist>\n') + + pl = dumps_with_indent(data, b' \t') + self.assertEqual(pl, header + + b'<array>\n' + b' \t<dict>\n' + b' \t \t<key>bytes</key>\n' + b' \t \t<data>\n' + b' \t \tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss\n' + b' \t \tLS4vMDE=\n' + b' \t \t</data>\n' + b' \t</dict>\n' + b'</array>\n' + b'</plist>\n') + + pl = dumps_with_indent(data, b'\t ') + self.assertEqual(pl, header + + b'<array>\n' + b'\t <dict>\n' + b'\t \t <key>bytes</key>\n' + b'\t \t <data>\n' + b'\t \t AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygp\n' + b'\t \t KissLS4vMDE=\n' + b'\t \t </data>\n' + b'\t </dict>\n' + b'</array>\n' + b'</plist>\n') + def test_loads_str_with_xml_fmt(self): pl = self._create() b = plistlib.dumps(pl) @@ -581,7 +644,6 @@ def test_appleformatting(self): self.assertEqual(data, TESTDATA[fmt], "generated data was not identical to Apple's output") - def test_appleformattingfromliteral(self): self.maxDiff = None for fmt in ALL_FORMATS: @@ -730,6 +792,25 @@ def test_dict_members(self): }) self.assertIsNot(pl2['first'], pl2['second']) + def test_frozendict(self): + pl = frozendict( + aString="Doodah", + anInt=728, + aDict=frozendict( + anotherString="hello", + aTrueValue=True, + ), + aList=["A", "B", 12], + ) + + for fmt in ALL_FORMATS: + with self.subTest(fmt=fmt): + data = plistlib.dumps(pl, fmt=fmt) + pl2 = plistlib.loads(data) + self.assertEqual(pl2, dict(pl)) + self.assertIsInstance(pl2, dict) + self.assertIsInstance(pl2['aDict'], dict) + def test_controlcharacters(self): for i in range(128): c = chr(i) @@ -903,8 +984,7 @@ def test_dump_naive_datetime_with_aware_datetime_option(self): class TestBinaryPlistlib(unittest.TestCase): - @staticmethod - def decode(*objects, offset_size=1, ref_size=1): + def build(self, *objects, offset_size=1, ref_size=1): data = [b'bplist00'] offset = 8 offsets = [] @@ -916,7 +996,11 @@ def decode(*objects, offset_size=1, ref_size=1): len(objects), 0, offset) data.extend(offsets) data.append(tail) - return plistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY) + return b''.join(data) + + def decode(self, *objects, offset_size=1, ref_size=1): + data = self.build(*objects, offset_size=offset_size, ref_size=ref_size) + return plistlib.loads(data, fmt=plistlib.FMT_BINARY) def test_nonstandard_refs_size(self): # Issue #21538: Refs and offsets are 24-bit integers @@ -1024,6 +1108,34 @@ def test_invalid_binary(self): with self.assertRaises(plistlib.InvalidFileException): plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY) + def test_truncated_large_data(self): + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + def check(data): + with open(os_helper.TESTFN, 'wb') as f: + f.write(data) + # buffered file + with open(os_helper.TESTFN, 'rb') as f: + with self.assertRaises(plistlib.InvalidFileException): + plistlib.load(f, fmt=plistlib.FMT_BINARY) + # unbuffered file + with open(os_helper.TESTFN, 'rb', buffering=0) as f: + with self.assertRaises(plistlib.InvalidFileException): + plistlib.load(f, fmt=plistlib.FMT_BINARY) + for w in range(20, 64): + s = 1 << w + # data + check(self.build(b'\x4f\x13' + s.to_bytes(8, 'big'))) + # ascii string + check(self.build(b'\x5f\x13' + s.to_bytes(8, 'big'))) + # unicode string + check(self.build(b'\x6f\x13' + s.to_bytes(8, 'big'))) + # array + check(self.build(b'\xaf\x13' + s.to_bytes(8, 'big'))) + # dict + check(self.build(b'\xdf\x13' + s.to_bytes(8, 'big'))) + # number of objects + check(b'bplist00' + struct.pack('>6xBBQQQ', 1, 1, s, 0, 8)) + def test_load_aware_datetime(self): data = (b'bplist003B\x04>\xd0d\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00' b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00' diff --git a/Lib/test/test_poll.py b/Lib/test/test_poll.py index 5675db8d1cab6e..a15612b91458fa 100644 --- a/Lib/test/test_poll.py +++ b/Lib/test/test_poll.py @@ -173,17 +173,25 @@ def test_poll3(self): @cpython_only def test_poll_c_limits(self): try: + import _testcapi from _testcapi import USHRT_MAX, INT_MAX, UINT_MAX + HAVE_PPOLL = getattr(_testcapi, 'HAVE_PPOLL', False) except ImportError: raise unittest.SkipTest("requires _testcapi") + pollster = select.poll() pollster.register(1) # Issues #15989, #17919 self.assertRaises(OverflowError, pollster.register, 0, USHRT_MAX + 1) self.assertRaises(OverflowError, pollster.modify, 1, USHRT_MAX + 1) - self.assertRaises(OverflowError, pollster.poll, INT_MAX + 1) - self.assertRaises(OverflowError, pollster.poll, UINT_MAX + 1) + if HAVE_PPOLL: + MS_TO_NS = 1_000_000 + tmax = _testcapi.INT64_MAX // MS_TO_NS + self.assertRaises(OverflowError, pollster.poll, tmax + 1) + else: + self.assertRaises(OverflowError, pollster.poll, INT_MAX + 1) + self.assertRaises(OverflowError, pollster.poll, UINT_MAX + 1) @threading_helper.reap_threads def test_threaded_poll(self): diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py index ef2da97f86734a..f08b945c8f57e5 100644 --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -17,6 +17,7 @@ from test.support import threading_helper from test.support import asynchat from test.support import asyncore +from test.support import control_characters_c0 test_support.requires_working_socket(module=True) @@ -184,7 +185,8 @@ def _do_tls_handshake(self): elif err.args[0] == ssl.SSL_ERROR_EOF: return self.handle_close() # TODO: SSLError does not expose alert information - elif ("SSLV3_ALERT_BAD_CERTIFICATE" in err.args[1] or + elif ("TLS_ALERT_BAD_CERTIFICATE" in err.args[1] or + "SSLV3_ALERT_BAD_CERTIFICATE" in err.args[1] or "SSLV3_ALERT_CERTIFICATE_UNKNOWN" in err.args[1]): return self.handle_close() raise @@ -395,6 +397,13 @@ def test_quit(self): self.assertIsNone(self.client.sock) self.assertIsNone(self.client.file) + def test_control_characters(self): + for c0 in control_characters_c0(): + with self.assertRaises(ValueError): + self.client.user(f'user{c0}') + with self.assertRaises(ValueError): + self.client.pass_(f'{c0}pass') + @requires_ssl def test_stls_capa(self): capa = self.client.capa() @@ -415,6 +424,7 @@ def test_stls_context(self): self.assertEqual(ctx.check_hostname, True) with self.assertRaises(ssl.CertificateError): resp = self.client.stls(context=ctx) + self.client = poplib.POP3("localhost", self.server.port, timeout=test_support.LOOPBACK_TIMEOUT) resp = self.client.stls(context=ctx) diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 21f06712548d88..a9975b75f7c22b 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -1,3 +1,4 @@ +import errno import inspect import os import posixpath @@ -5,7 +6,7 @@ import sys import unittest from functools import partial -from posixpath import realpath, abspath, dirname, basename, ALLOW_MISSING +from posixpath import realpath, abspath, dirname, basename, ALL_BUT_LAST, ALLOW_MISSING from test import support from test import test_genericpath from test.support import import_helper @@ -448,7 +449,7 @@ def test_normpath(self): self.assertEqual(result, expected) @skip_if_ABSTFN_contains_backslash - @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_curdir(self, kwargs): self.assertEqual(realpath('.', **kwargs), os.getcwd()) self.assertEqual(realpath('./.', **kwargs), os.getcwd()) @@ -459,7 +460,7 @@ def test_realpath_curdir(self, kwargs): self.assertEqual(realpath(b'/'.join([b'.'] * 100), **kwargs), os.getcwdb()) @skip_if_ABSTFN_contains_backslash - @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_pardir(self, kwargs): self.assertEqual(realpath('..', **kwargs), dirname(os.getcwd())) self.assertEqual(realpath('../..', **kwargs), dirname(dirname(os.getcwd()))) @@ -495,35 +496,43 @@ def test_realpath_strict(self): def test_realpath_invalid_paths(self): path = '/\x00' self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(ValueError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(ValueError, realpath, path, strict=True) self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) path = b'/\x00' self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(ValueError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(ValueError, realpath, path, strict=True) self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) path = '/nonexistent/x\x00' self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(FileNotFoundError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(FileNotFoundError, realpath, path, strict=True) self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) path = b'/nonexistent/x\x00' self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(FileNotFoundError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(FileNotFoundError, realpath, path, strict=True) self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) path = '/\x00/..' self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(ValueError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(ValueError, realpath, path, strict=True) self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) path = b'/\x00/..' self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(ValueError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(ValueError, realpath, path, strict=True) self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) path = '/nonexistent/x\x00/..' self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(FileNotFoundError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(FileNotFoundError, realpath, path, strict=True) self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) path = b'/nonexistent/x\x00/..' self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(FileNotFoundError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(FileNotFoundError, realpath, path, strict=True) self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) @@ -534,6 +543,7 @@ def test_realpath_invalid_paths(self): self.assertEqual(realpath(path, strict=ALLOW_MISSING), path) else: self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) + self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(UnicodeEncodeError, realpath, path, strict=True) self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) path = '/nonexistent/\udfff' @@ -543,6 +553,7 @@ def test_realpath_invalid_paths(self): else: self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) + self.assertRaises(FileNotFoundError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(FileNotFoundError, realpath, path, strict=True) path = '/\udfff/..' if sys.platform == 'win32': @@ -551,6 +562,7 @@ def test_realpath_invalid_paths(self): self.assertEqual(realpath(path, strict=ALLOW_MISSING), '/') else: self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) + self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(UnicodeEncodeError, realpath, path, strict=True) self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) path = '/nonexistent/\udfff/..' @@ -560,6 +572,7 @@ def test_realpath_invalid_paths(self): else: self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) + self.assertRaises(FileNotFoundError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(FileNotFoundError, realpath, path, strict=True) path = b'/\xff' @@ -570,9 +583,11 @@ def test_realpath_invalid_paths(self): else: self.assertEqual(realpath(path, strict=False), path) if support.is_wasi: + self.assertRaises(OSError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(OSError, realpath, path, strict=True) self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) else: + self.assertEqual(realpath(path, strict=ALL_BUT_LAST), path) self.assertRaises(FileNotFoundError, realpath, path, strict=True) self.assertEqual(realpath(path, strict=ALLOW_MISSING), path) path = b'/nonexistent/\xff' @@ -582,14 +597,16 @@ def test_realpath_invalid_paths(self): else: self.assertEqual(realpath(path, strict=False), path) if support.is_wasi: + self.assertRaises(OSError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(OSError, realpath, path, strict=True) self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) else: + self.assertRaises(FileNotFoundError, realpath, path, strict=ALL_BUT_LAST) self.assertRaises(FileNotFoundError, realpath, path, strict=True) @os_helper.skip_unless_symlink @skip_if_ABSTFN_contains_backslash - @_parameterize({}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_relative(self, kwargs): try: os.symlink(posixpath.relpath(ABSTFN+"1"), ABSTFN) @@ -599,12 +616,14 @@ def test_realpath_relative(self, kwargs): @os_helper.skip_unless_symlink @skip_if_ABSTFN_contains_backslash - @_parameterize({}, {'strict': ALLOW_MISSING}) - def test_realpath_missing_pardir(self, kwargs): + def test_realpath_missing_pardir(self): try: os.symlink(TESTFN + "1", TESTFN) - self.assertEqual( - realpath("nonexistent/../" + TESTFN, **kwargs), ABSTFN + "1") + path = "nonexistent/../" + TESTFN + self.assertEqual(realpath(path), ABSTFN + "1") + self.assertEqual(realpath(path, strict=ALLOW_MISSING), ABSTFN + "1") + self.assertRaises(FileNotFoundError, realpath, path, strict=ALL_BUT_LAST) + self.assertRaises(FileNotFoundError, realpath, path, strict=True) finally: os_helper.unlink(TESTFN) @@ -651,7 +670,7 @@ def test_realpath_symlink_loops(self): @os_helper.skip_unless_symlink @skip_if_ABSTFN_contains_backslash - @_parameterize({'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_symlink_loops_strict(self, kwargs): # Bug #43757, raise OSError if we get into an infinite symlink loop in # the strict modes. @@ -693,7 +712,7 @@ def test_realpath_symlink_loops_strict(self, kwargs): @os_helper.skip_unless_symlink @skip_if_ABSTFN_contains_backslash - @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_repeated_indirect_symlinks(self, kwargs): # Issue #6975. try: @@ -708,7 +727,7 @@ def test_realpath_repeated_indirect_symlinks(self, kwargs): @os_helper.skip_unless_symlink @skip_if_ABSTFN_contains_backslash - @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_deep_recursion(self, kwargs): depth = 10 try: @@ -728,7 +747,7 @@ def test_realpath_deep_recursion(self, kwargs): @os_helper.skip_unless_symlink @skip_if_ABSTFN_contains_backslash - @_parameterize({}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_resolve_parents(self, kwargs): # We also need to resolve any symlinks in the parents of a relative # path passed to realpath. E.g.: current working directory is @@ -749,7 +768,7 @@ def test_realpath_resolve_parents(self, kwargs): @os_helper.skip_unless_symlink @skip_if_ABSTFN_contains_backslash - @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_resolve_before_normalizing(self, kwargs): # Bug #990669: Symbolic links should be resolved before we # normalize the path. E.g.: if we have directories 'a', 'k' and 'y' @@ -778,7 +797,7 @@ def test_realpath_resolve_before_normalizing(self, kwargs): @os_helper.skip_unless_symlink @skip_if_ABSTFN_contains_backslash - @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_resolve_first(self, kwargs): # Bug #1213894: The first component of the path, if not absolute, # must be resolved too. @@ -816,7 +835,7 @@ def test_realpath_unreadable_symlink(self): @skip_if_ABSTFN_contains_backslash @unittest.skipIf(os.chmod not in os.supports_follow_symlinks, "Can't set symlink permissions") @unittest.skipIf(sys.platform != "darwin", "only macOS requires read permission to readlink()") - @_parameterize({'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_realpath_unreadable_symlink_strict(self, kwargs): try: os.symlink(ABSTFN+"1", ABSTFN) @@ -842,6 +861,7 @@ def test_realpath_unreadable_directory(self): os.chmod(ABSTFN, 0o000) self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN) self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN) + self.assertEqual(realpath(ABSTFN, strict=ALL_BUT_LAST), ABSTFN) self.assertEqual(realpath(ABSTFN, strict=ALLOW_MISSING), ABSTFN) try: @@ -855,6 +875,8 @@ def test_realpath_unreadable_directory(self): ABSTFN + '/k') self.assertRaises(PermissionError, realpath, ABSTFN + '/k', strict=True) + self.assertRaises(PermissionError, realpath, ABSTFN + '/k', + strict=ALL_BUT_LAST) self.assertRaises(PermissionError, realpath, ABSTFN + '/k', strict=ALLOW_MISSING) @@ -862,6 +884,8 @@ def test_realpath_unreadable_directory(self): ABSTFN + '/missing') self.assertRaises(PermissionError, realpath, ABSTFN + '/missing', strict=True) + self.assertRaises(PermissionError, realpath, ABSTFN + '/missing', + strict=ALL_BUT_LAST) self.assertRaises(PermissionError, realpath, ABSTFN + '/missing', strict=ALLOW_MISSING) finally: @@ -875,25 +899,30 @@ def test_realpath_nonterminal_file(self): with open(ABSTFN, 'w') as f: f.write('test_posixpath wuz ere') self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN) + self.assertEqual(realpath(ABSTFN, strict=ALL_BUT_LAST), ABSTFN) self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN) self.assertEqual(realpath(ABSTFN, strict=ALLOW_MISSING), ABSTFN) self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=ALLOW_MISSING) self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=ALLOW_MISSING) self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN)) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=ALLOW_MISSING) self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "/subdir") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=ALLOW_MISSING) @@ -908,25 +937,30 @@ def test_realpath_nonterminal_symlink_to_file(self): f.write('test_posixpath wuz ere') os.symlink(ABSTFN + "1", ABSTFN) self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN + "1") + self.assertEqual(realpath(ABSTFN, strict=ALL_BUT_LAST), ABSTFN + "1") self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "1") self.assertEqual(realpath(ABSTFN, strict=ALLOW_MISSING), ABSTFN + "1") self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN + "1") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=ALLOW_MISSING) self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN + "1") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=ALLOW_MISSING) self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN)) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=ALLOW_MISSING) self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "1/subdir") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=ALLOW_MISSING) @@ -943,25 +977,30 @@ def test_realpath_nonterminal_symlink_to_symlinks_to_file(self): os.symlink(ABSTFN + "2", ABSTFN + "1") os.symlink(ABSTFN + "1", ABSTFN) self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN + "2") + self.assertEqual(realpath(ABSTFN, strict=ALL_BUT_LAST), ABSTFN + "2") self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "2") self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "2") self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN + "2") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=ALLOW_MISSING) self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN + "2") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=ALLOW_MISSING) self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN)) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=ALLOW_MISSING) self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "2/subdir") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=ALL_BUT_LAST) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True) self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=ALLOW_MISSING) @@ -970,6 +1009,98 @@ def test_realpath_nonterminal_symlink_to_symlinks_to_file(self): os_helper.unlink(ABSTFN + "1") os_helper.unlink(ABSTFN + "2") + @os_helper.skip_unless_symlink + def test_realpath_mode(self): + self.addCleanup(os_helper.rmdir, ABSTFN) + self.addCleanup(os_helper.rmdir, ABSTFN + "/dir") + self.addCleanup(os_helper.unlink, ABSTFN + "/file") + self.addCleanup(os_helper.unlink, ABSTFN + "/dir/file2") + self.addCleanup(os_helper.unlink, ABSTFN + "/link") + self.addCleanup(os_helper.unlink, ABSTFN + "/link2") + self.addCleanup(os_helper.unlink, ABSTFN + "/broken") + self.addCleanup(os_helper.unlink, ABSTFN + "/cycle") + + os.mkdir(ABSTFN) + os.mkdir(ABSTFN + "/dir") + open(ABSTFN + "/file", "wb").close() + open(ABSTFN + "/dir/file2", "wb").close() + os.symlink("file", ABSTFN + "/link") + os.symlink("dir", ABSTFN + "/link2") + os.symlink("nonexistent", ABSTFN + "/broken") + os.symlink("cycle", ABSTFN + "/cycle") + def check(path, modes, expected, errno=None): + if isinstance(expected, str): + assert errno is None + expected = expected.replace('/', os.sep) + for mode in modes: + with self.subTest(mode=mode): + self.assertEqual(realpath(path, strict=mode).replace('/', os.sep), + ABSTFN.replace('/', os.sep) + expected) + else: + for mode in modes: + with self.subTest(mode=mode): + with self.assertRaises(expected) as cm: + realpath(path, strict=mode) + if errno is not None: + self.assertEqual(cm.exception.errno, errno) + + self.enterContext(os_helper.change_cwd(ABSTFN)) + all_modes = [False, ALLOW_MISSING, ALL_BUT_LAST, True] + check("file", all_modes, "/file") + check("file/", [False], "/file") + check("file/", [ALLOW_MISSING, ALL_BUT_LAST, True], NotADirectoryError) + check("file/file2", [False], "/file/file2") + check("file/file2", [ALLOW_MISSING, ALL_BUT_LAST, True], NotADirectoryError) + check("file/.", [False], "/file") + check("file/.", [ALLOW_MISSING, ALL_BUT_LAST, True], NotADirectoryError) + check("file/../link2", [False], "/dir") + check("file/../link2", [ALLOW_MISSING, ALL_BUT_LAST, True], NotADirectoryError) + + check("dir", all_modes, "/dir") + check("dir/", all_modes, "/dir") + check("dir/file2", all_modes, "/dir/file2") + + check("link", all_modes, "/file") + check("link/", [False], "/file") + check("link/", [ALLOW_MISSING, ALL_BUT_LAST, True], NotADirectoryError) + check("link/file2", [False], "/file/file2") + check("link/file2", [ALLOW_MISSING, ALL_BUT_LAST, True], NotADirectoryError) + check("link/.", [False], "/file") + check("link/.", [ALLOW_MISSING, ALL_BUT_LAST, True], NotADirectoryError) + check("link/../link", [False], "/file") + check("link/../link", [ALLOW_MISSING, ALL_BUT_LAST, True], NotADirectoryError) + + check("link2", all_modes, "/dir") + check("link2/", all_modes, "/dir") + check("link2/file2", all_modes, "/dir/file2") + + check("nonexistent", [False, ALLOW_MISSING, ALL_BUT_LAST], "/nonexistent") + check("nonexistent", [True], FileNotFoundError) + check("nonexistent/", [False, ALLOW_MISSING, ALL_BUT_LAST], "/nonexistent") + check("nonexistent/", [True], FileNotFoundError) + check("nonexistent/file", [False, ALLOW_MISSING], "/nonexistent/file") + check("nonexistent/file", [ALL_BUT_LAST, True], FileNotFoundError) + check("nonexistent/../link", [False, ALLOW_MISSING], "/file") + check("nonexistent/../link", [ALL_BUT_LAST, True], FileNotFoundError) + + check("broken", [False, ALLOW_MISSING, ALL_BUT_LAST], "/nonexistent") + check("broken", [True], FileNotFoundError) + check("broken/", [False, ALLOW_MISSING, ALL_BUT_LAST], "/nonexistent") + check("broken/", [True], FileNotFoundError) + check("broken/file", [False, ALLOW_MISSING], "/nonexistent/file") + check("broken/file", [ALL_BUT_LAST, True], FileNotFoundError) + check("broken/../link", [False, ALLOW_MISSING], "/file") + check("broken/../link", [ALL_BUT_LAST, True], FileNotFoundError) + + check("cycle", [False], "/cycle") + check("cycle", [ALLOW_MISSING, ALL_BUT_LAST, True], OSError, errno.ELOOP) + check("cycle/", [False], "/cycle") + check("cycle/", [ALLOW_MISSING, ALL_BUT_LAST, True], OSError, errno.ELOOP) + check("cycle/file", [False], "/cycle/file") + check("cycle/file", [ALLOW_MISSING, ALL_BUT_LAST, True], OSError, errno.ELOOP) + check("cycle/../link", [False], "/file") + check("cycle/../link", [ALLOW_MISSING, ALL_BUT_LAST, True], OSError, errno.ELOOP) + def test_relpath(self): (real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar") try: @@ -1152,7 +1283,7 @@ def test_path_normpath(self): def test_path_abspath(self): self.assertPathEqual(self.path.abspath) - @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) + @_parameterize({}, {'strict': True}, {'strict': ALL_BUT_LAST}, {'strict': ALLOW_MISSING}) def test_path_realpath(self, kwargs): self.assertPathEqual(self.path.realpath) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 41c337ade7eca1..041c2072b9e253 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -67,6 +67,13 @@ class dict3(dict): def __repr__(self): return dict.__repr__(self) +class frozendict2(frozendict): + pass + +class frozendict3(frozendict): + def __repr__(self): + return frozendict.__repr__(self) + class dict_custom_repr(dict): def __repr__(self): return '*'*len(dict.__repr__(self)) @@ -157,6 +164,7 @@ def test_init(self): self.assertRaises(ValueError, pprint.PrettyPrinter, depth=0) self.assertRaises(ValueError, pprint.PrettyPrinter, depth=-1) self.assertRaises(ValueError, pprint.PrettyPrinter, width=0) + self.assertRaises(ValueError, pprint.PrettyPrinter, compact=True, expand=True) def test_basic(self): # Verify .isrecursive() and .isreadable() w/o recursion @@ -254,18 +262,22 @@ def test_same_as_repr(self): set(), set2(), set3(), frozenset(), frozenset2(), frozenset3(), {}, dict2(), dict3(), + frozendict(), frozendict2(), frozendict3(), {}.keys(), {}.values(), {}.items(), MappingView({}), KeysView({}), ItemsView({}), ValuesView({}), self.assertTrue, pprint, -6, -6, -6-6j, -1.5, "x", b"x", bytearray(b"x"), (3,), [3], {3: 6}, - (1,2), [3,4], {5: 6}, + (1,2), [3,4], tuple2((1,2)), tuple3((1,2)), tuple3(range(100)), [3,4], list2([3,4]), list3([3,4]), list3(range(100)), set({7}), set2({7}), set3({7}), frozenset({8}), frozenset2({8}), frozenset3({8}), - dict2({5: 6}), dict3({5: 6}), + {5: 6}, dict2({5: 6}), dict3({5: 6}), + frozendict({5: 6}), frozendict2({5: 6}), frozendict3({5: 6}), {5: 6}.keys(), {5: 6}.values(), {5: 6}.items(), + frozendict({5: 6}).keys(), frozendict({5: 6}).values(), + frozendict({5: 6}).items(), MappingView({5: 6}), KeysView({5: 6}), ItemsView({5: 6}), ValuesView({5: 6}), range(10, -11, -1), @@ -330,20 +342,45 @@ def test_basic_line_wrap(self): for type in [dict, dict2]: self.assertEqual(pprint.pformat(type(o)), exp) + exp = """\ +frozendict({'RPM_cal': 0, + 'RPM_cal2': 48059, + 'Speed_cal': 0, + 'controldesk_runtime_us': 0, + 'main_code_runtime_us': 0, + 'read_io_runtime_us': 0, + 'write_io_runtime_us': 43690})""" + self.assertEqual(pprint.pformat(frozendict(o)), exp) + exp = """\ +frozendict2({'RPM_cal': 0, + 'RPM_cal2': 48059, + 'Speed_cal': 0, + 'controldesk_runtime_us': 0, + 'main_code_runtime_us': 0, + 'read_io_runtime_us': 0, + 'write_io_runtime_us': 43690})""" + self.assertEqual(pprint.pformat(frozendict2(o)), exp) + o = range(100) exp = 'dict_keys([%s])' % ',\n '.join(map(str, o)) keys = dict.fromkeys(o).keys() self.assertEqual(pprint.pformat(keys), exp) + keys = frozendict.fromkeys(o).keys() + self.assertEqual(pprint.pformat(keys), exp) o = range(100) exp = 'dict_values([%s])' % ',\n '.join(map(str, o)) values = {v: v for v in o}.values() self.assertEqual(pprint.pformat(values), exp) + values = frozendict({v: v for v in o}).values() + self.assertEqual(pprint.pformat(values), exp) o = range(100) exp = 'dict_items([%s])' % ',\n '.join("(%s, %s)" % (i, i) for i in o) items = {v: v for v in o}.items() self.assertEqual(pprint.pformat(items), exp) + items = frozendict({v: v for v in o}).items() + self.assertEqual(pprint.pformat(items), exp) o = range(100) exp = 'odict_keys([%s])' % ',\n '.join(map(str, o)) @@ -1262,6 +1299,12 @@ def test_counter(self): 'e': 4, 'n': 2, 'l': 1})""") + self.assertEqual(pprint.pformat(d, indent=2, width=1), +"""\ +Counter({ 's': 6, + 'e': 4, + 'n': 2, + 'l': 1})""") def test_chainmap(self): d = collections.ChainMap() @@ -1472,6 +1515,537 @@ def test_user_string(self): 'jumped over a ' 'lazy dog'}""") + def test_template(self): + d = t"" + self.assertEqual(pprint.pformat(d), + "Template(strings=('',), interpolations=())") + self.assertEqual(pprint.pformat(d), repr(d)) + self.assertEqual(pprint.pformat(d, width=1), +"""\ +Template(strings=('',), + interpolations=())""") + name = "World" + d = t"Hello {name}" + self.assertEqual(pprint.pformat(d), +"""\ +Template(strings=('Hello ', ''), + interpolations=(Interpolation('World', 'name', None, ''),))""") + ver = {3.13: False, 3.14: True} + d = t"Hello { {"name": "Python", "version": ver}!s:z}!" + self.assertEqual(pprint.pformat(d, width=1), +"""\ +Template(strings=('Hello ', + '!'), + interpolations=(Interpolation({'name': 'Python', + 'version': {3.13: False, + 3.14: True}}, + ' ' + '{"name": ' + '"Python", ' + '"version": ' + 'ver}', + 's', + 'z'),))""") + + def test_expand_template(self): + d = t"" + self.assertEqual( + pprint.pformat(d, expand=True), + "Template(strings=('',), interpolations=())", + ) + name = "World" + d = t"Hello {name}" + self.assertEqual( + pprint.pformat(d, width=40, indent=4, expand=True), + """\ +Template( + strings=('Hello ', ''), + interpolations=( + Interpolation( + value='World', + expression='name', + conversion=None, + format_spec='', + ), + ), +)""", + ) + ver = {3.13: False, 3.14: True} + d = t"Hello { {"name": "Python", "version": ver}!s:z}!" + self.assertEqual( + pprint.pformat(d, width=40, indent=4, expand=True), + """\ +Template( + strings=('Hello ', '!'), + interpolations=( + Interpolation( + value={ + 'name': 'Python', + 'version': { + 3.13: False, + 3.14: True, + }, + }, + expression=' {"name": "Python", ' + '"version": ver}', + conversion='s', + format_spec='z', + ), + ), +)""", + ) + + def test_expand_dataclass(self): + @dataclasses.dataclass + class DummyDataclass: + foo: str + bar: float + baz: bool + qux: dict = dataclasses.field(default_factory=dict) + quux: list = dataclasses.field(default_factory=list) + corge: int = 1 + garply: tuple = (1, 2, 3, 4) + dummy_dataclass = DummyDataclass( + foo="foo", + bar=1.2, + baz=False, + qux={"foo": "bar", "baz": 123}, + quux=["foo", "bar", "baz"], + corge=7, + garply=(1, 2, 3, 4), + ) + self.assertEqual(pprint.pformat(dummy_dataclass, width=40, indent=4, + expand=True), +"""\ +DummyDataclass( + foo='foo', + bar=1.2, + baz=False, + qux={'baz': 123, 'foo': 'bar'}, + quux=['foo', 'bar', 'baz'], + corge=7, + garply=(1, 2, 3, 4), +)""") + + def test_expand_dict(self): + dummy_dict = { + "foo": "bar", + "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7, + } + self.assertEqual(pprint.pformat(dummy_dict, width=40, indent=4, + expand=True, sort_dicts=False), +"""\ +{ + 'foo': 'bar', + 'baz': 123, + 'qux': {'foo': 'bar', 'baz': 123}, + 'quux': ['foo', 'bar', 'baz'], + 'corge': 7, +}""") + + def test_expand_ordered_dict(self): + dummy_ordered_dict = collections.OrderedDict( + [ + ("foo", 1), + ("bar", 12), + ("baz", 123), + ] + ) + self.assertEqual(pprint.pformat(dummy_ordered_dict, width=20, indent=4, + expand=True), +"""\ +OrderedDict([ + ('foo', 1), + ('bar', 12), + ('baz', 123), +])""") + + def test_expand_list(self): + dummy_list = [ + "foo", + "bar", + "baz", + "qux", + ] + self.assertEqual(pprint.pformat(dummy_list, width=20, indent=4, + expand=True), +"""\ +[ + 'foo', + 'bar', + 'baz', + 'qux', +]""") + + def test_expand_tuple(self): + dummy_tuple = ( + "foo", + "bar", + "baz", + 4, + 5, + 6, + ) + self.assertEqual(pprint.pformat(dummy_tuple, width=20, indent=4, + expand=True), +"""\ +( + 'foo', + 'bar', + 'baz', + 4, + 5, + 6, +)""") + + def test_expand_single_element_tuple(self): + self.assertEqual( + pprint.pformat((1,), width=1, indent=4, expand=True), + """\ +( + 1, +)""") + + def test_expand_set(self): + dummy_set = { + "foo", + "bar", + "baz", + "qux", + (1, 2, 3), + } + self.assertEqual(pprint.pformat(dummy_set, width=20, indent=4, + expand=True), +"""\ +{ + 'bar', + 'baz', + 'foo', + 'qux', + (1, 2, 3), +}""") + + def test_expand_frozenset(self): + dummy_set = { + (1, 2, 3), + } + dummy_frozenset = frozenset( + { + "foo", + "bar", + "baz", + (1, 2, 3), + frozenset(dummy_set), + } + ) + self.assertEqual(pprint.pformat(dummy_frozenset, width=40, indent=4, + expand=True), +"""\ +frozenset({ + frozenset({(1, 2, 3)}), + 'bar', + 'baz', + 'foo', + (1, 2, 3), +})""") + + def test_expand_frozendict(self): + dummy_frozendict = frozendict( + {"foo": "bar", "baz": 123, "qux": [1, 2]} + ) + self.assertEqual( + pprint.pformat(dummy_frozendict, width=20, indent=4, expand=True), + """\ +frozendict({ + 'baz': 123, + 'foo': 'bar', + 'qux': [1, 2], +})""", + ) + + def test_expand_bytes(self): + dummy_bytes = b"Hello world! foo bar baz 123 456 789" + self.assertEqual(pprint.pformat(dummy_bytes, width=20, indent=4, + expand=True), +"""\ +( + b'Hello world!' + b' foo bar baz' + b' 123 456 789' +)""") + + def test_expand_bytearray(self): + dummy_bytes = b"Hello world! foo bar baz 123 456 789" + dummy_byte_array = bytearray(dummy_bytes) + self.assertEqual(pprint.pformat(dummy_byte_array, width=40, indent=4, + expand=True), +"""\ +bytearray( + b'Hello world! foo bar baz 123 456' + b' 789' +)""") + + def test_expand_mappingproxy(self): + dummy_dict = { + "foo": "bar", + "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7, + } + dummy_mappingproxy = types.MappingProxyType(dummy_dict) + self.assertEqual(pprint.pformat(dummy_mappingproxy, width=40, indent=4, + expand=True), +"""\ +mappingproxy({ + 'baz': 123, + 'corge': 7, + 'foo': 'bar', + 'quux': ['foo', 'bar', 'baz'], + 'qux': {'baz': 123, 'foo': 'bar'}, +})""") + + def test_expand_namespace(self): + dummy_namespace = types.SimpleNamespace( + foo="bar", + bar=42, + baz=types.SimpleNamespace( + x=321, + y="string", + d={"foo": True, "bar": "baz"}, + ), + ) + + self.assertEqual(pprint.pformat(dummy_namespace, width=40, indent=4, + expand=True), +"""\ +namespace( + foo='bar', + bar=42, + baz=namespace( + x=321, + y='string', + d={'bar': 'baz', 'foo': True}, + ), +)""") + + def test_expand_defaultdict(self): + dummy_defaultdict = collections.defaultdict(list) + dummy_defaultdict["foo"].append("bar") + dummy_defaultdict["foo"].append("baz") + dummy_defaultdict["foo"].append("qux") + dummy_defaultdict["bar"] = {"foo": "bar", "baz": None} + self.assertEqual(pprint.pformat(dummy_defaultdict, width=40, indent=4, + expand=True), +"""\ +defaultdict(<class 'list'>, { + 'bar': {'baz': None, 'foo': 'bar'}, + 'foo': ['bar', 'baz', 'qux'], +})""") + + def test_expand_counter(self): + dummy_counter = collections.Counter("abcdeabcdabcaba") + expected = """\ +Counter({ + 'a': 5, + 'b': 4, + 'c': 3, + 'd': 2, + 'e': 1, +})""" + self.assertEqual(pprint.pformat(dummy_counter, width=40, indent=4, + expand=True), expected) + + expected2 = """\ +Counter({ + 'a': 5, + 'b': 4, + 'c': 3, + 'd': 2, + 'e': 1, +})""" + self.assertEqual(pprint.pformat(dummy_counter, width=20, indent=2, + expand=True), expected2) + + def test_expand_chainmap(self): + dummy_dict = { + "foo": "bar", + "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7, + } + dummy_chainmap = collections.ChainMap( + {"foo": "bar"}, + {"baz": "qux"}, + {"corge": dummy_dict}, + ) + dummy_chainmap.maps.append({"garply": "waldo"}) + self.assertEqual(pprint.pformat(dummy_chainmap, width=40, indent=4, + expand=True), +"""\ +ChainMap( + {'foo': 'bar'}, + {'baz': 'qux'}, + { + 'corge': { + 'baz': 123, + 'corge': 7, + 'foo': 'bar', + 'quux': ['foo', 'bar', 'baz'], + 'qux': { + 'baz': 123, + 'foo': 'bar', + }, + }, + }, + {'garply': 'waldo'}, +)""") + + def test_expand_deque(self): + dummy_dict = { + "foo": "bar", + "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7, + } + dummy_list = [ + "foo", + "bar", + "baz", + ] + dummy_set = { + (1, 2, 3), + } + dummy_deque = collections.deque(maxlen=10) + dummy_deque.append("foo") + dummy_deque.append(123) + dummy_deque.append(dummy_dict) + dummy_deque.extend(dummy_list) + dummy_deque.appendleft(dummy_set) + self.assertEqual(pprint.pformat(dummy_deque, width=40, indent=4, + expand=True), +"""\ +deque([ + {(1, 2, 3)}, + 'foo', + 123, + { + 'baz': 123, + 'corge': 7, + 'foo': 'bar', + 'quux': ['foo', 'bar', 'baz'], + 'qux': {'baz': 123, 'foo': 'bar'}, + }, + 'foo', + 'bar', + 'baz', +], maxlen=10)""") + + def test_expand_userdict(self): + class DummyUserDict(collections.UserDict): + """A custom UserDict with some extra attributes""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.access_count = 0 + dummy_userdict = DummyUserDict({ "foo": "bar", "baz": 123, + "qux": {"foo": "bar", "baz": 123}, + "quux": ["foo", "bar", "baz"], + "corge": 7 }) + dummy_userdict.access_count = 5 + + self.assertEqual(pprint.pformat(dummy_userdict, width=40, indent=4, + expand=True), +"""\ +{ + 'baz': 123, + 'corge': 7, + 'foo': 'bar', + 'quux': ['foo', 'bar', 'baz'], + 'qux': {'baz': 123, 'foo': 'bar'}, +}""") + + def test_expand_userlist(self): + class DummyUserList(collections.UserList): + """A custom UserList with some extra attributes""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.description = "foo" + dummy_userlist = DummyUserList(["first", 2, {"key": "value"}, + [4, 5, 6]]) + + self.assertEqual(pprint.pformat(dummy_userlist, width=40, indent=4, + expand=True), +"""\ +[ + 'first', + 2, + {'key': 'value'}, + [4, 5, 6], +]""") + + def test_expand_dict_keys(self): + d = {"foo": 1, "bar": 2, "baz": 3, "qux": 4, "quux": 5} + self.assertEqual( + pprint.pformat(d.keys(), width=20, indent=4, expand=True), + """\ +dict_keys([ + 'bar', + 'baz', + 'foo', + 'quux', + 'qux', +])""", + ) + + def test_expand_dict_values(self): + d = {"foo": 1, "bar": 2, "baz": 3, "qux": 4, "quux": 5} + self.assertEqual( + pprint.pformat(d.values(), width=20, indent=4, expand=True), + """\ +dict_values([ + 1, + 2, + 3, + 4, + 5, +])""", + ) + + def test_expand_dict_items(self): + d = {"foo": 1, "bar": 2, "baz": 3, "qux": 4, "quux": 5} + self.assertEqual( + pprint.pformat(d.items(), width=20, indent=4, expand=True), + """\ +dict_items([ + ('bar', 2), + ('baz', 3), + ('foo', 1), + ('quux', 5), + ('qux', 4), +])""", + ) + + def test_expand_str(self): + s = "The quick brown fox jumped over the lazy dog " * 3 + self.assertEqual( + pprint.pformat(s, width=40, indent=4, expand=True), + """\ +( + 'The quick brown fox jumped over ' + 'the lazy dog The quick brown fox ' + 'jumped over the lazy dog The ' + 'quick brown fox jumped over the ' + 'lazy dog ' +)""", + ) + class DottedPrettyPrinter(pprint.PrettyPrinter): diff --git a/Lib/test/test_profile.py b/Lib/test/test_profile.py index 0f16b92334999c..c4b2d7ca05c0a6 100644 --- a/Lib/test/test_profile.py +++ b/Lib/test/test_profile.py @@ -4,12 +4,16 @@ import pstats import unittest import os +import warnings from difflib import unified_diff from io import StringIO from test.support.os_helper import TESTFN, unlink, temp_dir, change_cwd from contextlib import contextmanager, redirect_stdout -import profile +# Suppress deprecation warning for profile module (PEP 799) +with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + import profile from test.profilee import testfunc, timer from test.support.script_helper import assert_python_failure, assert_python_ok diff --git a/Lib/test/test_profiling/__init__.py b/Lib/test/test_profiling/__init__.py new file mode 100644 index 00000000000000..4b16ecc31156a5 --- /dev/null +++ b/Lib/test/test_profiling/__init__.py @@ -0,0 +1,5 @@ +import os +from test.support import load_package_tests + +def load_tests(*args): + return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_profiling/__main__.py b/Lib/test/test_profiling/__main__.py new file mode 100644 index 00000000000000..40a23a297ec2b4 --- /dev/null +++ b/Lib/test/test_profiling/__main__.py @@ -0,0 +1,4 @@ +from . import load_tests +import unittest + +unittest.main() diff --git a/Lib/test/test_profiling/test_heatmap.py b/Lib/test/test_profiling/test_heatmap.py new file mode 100644 index 00000000000000..ee27fdd3fa3053 --- /dev/null +++ b/Lib/test/test_profiling/test_heatmap.py @@ -0,0 +1,862 @@ +"""Tests for the heatmap collector (profiling.sampling).""" + +import os +import shutil +import tempfile +import unittest +from collections import namedtuple +from pathlib import Path + +# Matches the C structseq LocationInfo from _remote_debugging +LocationInfo = namedtuple('LocationInfo', ['lineno', 'end_lineno', 'col_offset', 'end_col_offset']) + +from profiling.sampling.heatmap_collector import ( + HeatmapCollector, + get_python_path_info, + extract_module_name, +) + +from test.support import captured_stdout, captured_stderr + + +# ============================================================================= +# Unit Tests for Public Helper Functions +# ============================================================================= + +class TestPathInfoFunctions(unittest.TestCase): + """Test public helper functions for path information.""" + + def test_get_python_path_info_returns_dict(self): + """Test that get_python_path_info returns a dictionary with expected keys.""" + path_info = get_python_path_info() + + self.assertIsInstance(path_info, dict) + self.assertIn('stdlib', path_info) + self.assertIn('site_packages', path_info) + self.assertIn('sys_path', path_info) + + def test_get_python_path_info_stdlib_is_path_or_none(self): + """Test that stdlib is either a Path object or None.""" + path_info = get_python_path_info() + + if path_info['stdlib'] is not None: + self.assertIsInstance(path_info['stdlib'], Path) + + def test_get_python_path_info_site_packages_is_list(self): + """Test that site_packages is a list.""" + path_info = get_python_path_info() + + self.assertIsInstance(path_info['site_packages'], list) + for item in path_info['site_packages']: + self.assertIsInstance(item, Path) + + def test_get_python_path_info_sys_path_is_list(self): + """Test that sys_path is a list of Path objects.""" + path_info = get_python_path_info() + + self.assertIsInstance(path_info['sys_path'], list) + for item in path_info['sys_path']: + self.assertIsInstance(item, Path) + + def test_extract_module_name_with_none(self): + """Test extract_module_name with None filename.""" + path_info = get_python_path_info() + module_name, module_type = extract_module_name(None, path_info) + + self.assertEqual(module_name, 'unknown') + self.assertEqual(module_type, 'other') + + def test_extract_module_name_with_empty_string(self): + """Test extract_module_name with empty filename.""" + path_info = get_python_path_info() + module_name, module_type = extract_module_name('', path_info) + + self.assertEqual(module_name, 'unknown') + self.assertEqual(module_type, 'other') + + def test_extract_module_name_with_stdlib_file(self): + """Test extract_module_name with a standard library file.""" + path_info = get_python_path_info() + + # Use os module as a known stdlib file + if path_info['stdlib']: + stdlib_file = str(path_info['stdlib'] / 'os.py') + module_name, module_type = extract_module_name(stdlib_file, path_info) + + self.assertEqual(module_type, 'stdlib') + self.assertIn('os', module_name) + + def test_extract_module_name_with_project_file(self): + """Test extract_module_name with a project file.""" + path_info = get_python_path_info() + + # Create a mock project file path + if path_info['sys_path']: + # Use current directory as project path + project_file = '/some/project/path/mymodule.py' + module_name, module_type = extract_module_name(project_file, path_info) + + # Should classify as 'other' if not in sys.path + self.assertIn(module_type, ['project', 'other']) + + def test_extract_module_name_removes_py_extension(self): + """Test that .py extension is removed from module names.""" + path_info = get_python_path_info() + + # Test with a simple .py file + module_name, module_type = extract_module_name('/path/to/test.py', path_info) + + # Module name should not contain .py + self.assertNotIn('.py', module_name) + + def test_extract_module_name_with_special_files(self): + """Test extract_module_name with special filenames like <string>.""" + path_info = get_python_path_info() + + special_files = ['<string>', '<stdin>', '[eval]'] + for special_file in special_files: + module_name, module_type = extract_module_name(special_file, path_info) + self.assertEqual(module_type, 'other') + + +# ============================================================================= +# Unit Tests for HeatmapCollector Public API +# ============================================================================= + +class TestHeatmapCollectorInit(unittest.TestCase): + """Test HeatmapCollector initialization.""" + + def test_init_creates_empty_data_structures(self): + """Test that __init__ creates empty data structures.""" + collector = HeatmapCollector(sample_interval_usec=100) + + # Check that data structures are initialized + self.assertIsInstance(collector.line_samples, dict) + self.assertIsInstance(collector.file_samples, dict) + self.assertIsInstance(collector.line_self_samples, dict) + self.assertIsInstance(collector.file_self_samples, dict) + self.assertIsInstance(collector.call_graph, dict) + self.assertIsInstance(collector.callers_graph, dict) + self.assertIsInstance(collector.function_definitions, dict) + self.assertIsInstance(collector.edge_samples, dict) + + # Check that they're empty + self.assertEqual(len(collector.line_samples), 0) + self.assertEqual(len(collector.file_samples), 0) + self.assertEqual(len(collector.line_self_samples), 0) + self.assertEqual(len(collector.file_self_samples), 0) + + def test_init_sets_total_samples_to_zero(self): + """Test that total samples starts at zero.""" + collector = HeatmapCollector(sample_interval_usec=100) + self.assertEqual(collector._total_samples, 0) + + def test_init_gets_path_info(self): + """Test that path info is retrieved during init.""" + collector = HeatmapCollector(sample_interval_usec=100) + self.assertIsNotNone(collector._path_info) + self.assertIn('stdlib', collector._path_info) + + +class TestHeatmapCollectorSetStats(unittest.TestCase): + """Test HeatmapCollector.set_stats() method.""" + + def test_set_stats_stores_all_parameters(self): + """Test that set_stats stores all provided parameters.""" + collector = HeatmapCollector(sample_interval_usec=100) + + collector.set_stats( + sample_interval_usec=500, + duration_sec=10.5, + sample_rate=99.5, + error_rate=0.5 + ) + + self.assertEqual(collector.stats['sample_interval_usec'], 500) + self.assertEqual(collector.stats['duration_sec'], 10.5) + self.assertEqual(collector.stats['sample_rate'], 99.5) + self.assertEqual(collector.stats['error_rate'], 0.5) + + def test_set_stats_includes_system_info(self): + """Test that set_stats includes Python and platform info.""" + collector = HeatmapCollector(sample_interval_usec=100) + collector.set_stats(sample_interval_usec=100, duration_sec=1.0, sample_rate=100.0) + + self.assertIn('python_version', collector.stats) + self.assertIn('python_implementation', collector.stats) + self.assertIn('platform', collector.stats) + + def test_set_stats_accepts_kwargs(self): + """Test that set_stats accepts additional kwargs.""" + collector = HeatmapCollector(sample_interval_usec=100) + + collector.set_stats( + sample_interval_usec=100, + duration_sec=1.0, + sample_rate=100.0, + custom_key='custom_value', + another_key=42 + ) + + self.assertEqual(collector.stats['custom_key'], 'custom_value') + self.assertEqual(collector.stats['another_key'], 42) + + def test_set_stats_with_none_error_rate(self): + """Test set_stats with error_rate=None.""" + collector = HeatmapCollector(sample_interval_usec=100) + collector.set_stats(sample_interval_usec=100, duration_sec=1.0, sample_rate=100.0) + + self.assertIn('error_rate', collector.stats) + self.assertIsNone(collector.stats['error_rate']) + + +class TestHeatmapCollectorProcessFrames(unittest.TestCase): + """Test HeatmapCollector.process_frames() method.""" + + def test_process_frames_increments_total_samples(self): + """Test that process_frames increments total samples count.""" + collector = HeatmapCollector(sample_interval_usec=100) + + initial_count = collector._total_samples + frames = [('file.py', (10, 10, -1, -1), 'func', None)] + collector.process_frames(frames, thread_id=1) + + self.assertEqual(collector._total_samples, initial_count + 1) + + def test_process_frames_records_line_samples(self): + """Test that process_frames records line samples.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [('test.py', (5, 5, -1, -1), 'test_func', None)] + collector.process_frames(frames, thread_id=1) + + # Check that line was recorded + self.assertIn(('test.py', 5), collector.line_samples) + self.assertEqual(collector.line_samples[('test.py', 5)], 1) + + def test_process_frames_records_multiple_lines_in_stack(self): + """Test that process_frames records all lines in a stack.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [ + ('file1.py', (10, 10, -1, -1), 'func1', None), + ('file2.py', (20, 20, -1, -1), 'func2', None), + ('file3.py', (30, 30, -1, -1), 'func3', None) + ] + collector.process_frames(frames, thread_id=1) + + # All frames should be recorded + self.assertIn(('file1.py', 10), collector.line_samples) + self.assertIn(('file2.py', 20), collector.line_samples) + self.assertIn(('file3.py', 30), collector.line_samples) + + def test_process_frames_distinguishes_self_samples(self): + """Test that process_frames distinguishes self (leaf) samples.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [ + ('leaf.py', (5, 5, -1, -1), 'leaf_func', None), # This is the leaf (top of stack) + ('caller.py', (10, 10, -1, -1), 'caller_func', None) + ] + collector.process_frames(frames, thread_id=1) + + # Leaf should have self sample + self.assertIn(('leaf.py', 5), collector.line_self_samples) + self.assertEqual(collector.line_self_samples[('leaf.py', 5)], 1) + + # Caller should NOT have self sample + self.assertNotIn(('caller.py', 10), collector.line_self_samples) + + def test_process_frames_accumulates_samples(self): + """Test that multiple calls accumulate samples.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [('file.py', (10, 10, -1, -1), 'func', None)] + + collector.process_frames(frames, thread_id=1) + collector.process_frames(frames, thread_id=1) + collector.process_frames(frames, thread_id=1) + + self.assertEqual(collector.line_samples[('file.py', 10)], 3) + self.assertEqual(collector._total_samples, 3) + + def test_process_frames_ignores_invalid_frames(self): + """Test that process_frames ignores invalid frames.""" + collector = HeatmapCollector(sample_interval_usec=100) + + # These should be ignored + invalid_frames = [ + ('<string>', (1, 1, -1, -1), 'test', None), + ('[eval]', (1, 1, -1, -1), 'test', None), + ('', (1, 1, -1, -1), 'test', None), + (None, (1, 1, -1, -1), 'test', None), + ('__init__', (0, 0, -1, -1), 'test', None), # Special invalid frame + ] + + for frame in invalid_frames: + collector.process_frames([frame], thread_id=1) + + # Should not record these invalid frames + for frame in invalid_frames: + if frame[0]: + self.assertNotIn((frame[0], frame[1][0]), collector.line_samples) + + def test_process_frames_builds_call_graph(self): + """Test that process_frames builds call graph relationships.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [ + ('callee.py', (5, 5, -1, -1), 'callee_func', None), + ('caller.py', (10, 10, -1, -1), 'caller_func', None) + ] + collector.process_frames(frames, thread_id=1) + + # Check that call relationship was recorded + caller_key = ('caller.py', 10) + self.assertIn(caller_key, collector.call_graph) + + # Check callers graph + callee_key = ('callee.py', 5) + self.assertIn(callee_key, collector.callers_graph) + + def test_process_frames_records_function_definitions(self): + """Test that process_frames records function definition locations.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [('module.py', (42, 42, -1, -1), 'my_function', None)] + collector.process_frames(frames, thread_id=1) + + self.assertIn(('module.py', 'my_function'), collector.function_definitions) + self.assertEqual(collector.function_definitions[('module.py', 'my_function')], 42) + + def test_process_frames_tracks_edge_samples(self): + """Test that process_frames tracks edge sample counts.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [ + ('callee.py', (5, 5, -1, -1), 'callee', None), + ('caller.py', (10, 10, -1, -1), 'caller', None) + ] + + # Process same call stack multiple times + collector.process_frames(frames, thread_id=1) + collector.process_frames(frames, thread_id=1) + + # Check that edge count is tracked + self.assertGreater(len(collector.edge_samples), 0) + + def test_process_frames_weight_applies_to_identical_samples(self): + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [ + ('callee.py', (5, 5, -1, -1), 'callee', None), + ('caller.py', (10, 10, -1, -1), 'caller', None), + ] + + collector.process_frames(frames, thread_id=1, weight=5) + + edge_key = (('caller.py', 10), ('callee.py', 5)) + self.assertEqual(collector.edge_samples[edge_key], 5) + self.assertEqual(collector.line_samples[('callee.py', 5)], 5) + self.assertEqual(collector.line_samples[('caller.py', 10)], 5) + + def test_process_frames_handles_empty_frames(self): + """Test that process_frames handles empty frame list.""" + collector = HeatmapCollector(sample_interval_usec=100) + + initial_count = collector._total_samples + collector.process_frames([], thread_id=1) + + # Should still increment total samples + self.assertEqual(collector._total_samples, initial_count + 1) + + def test_process_frames_with_file_samples_dict(self): + """Test that file_samples dict is properly populated.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [('test.py', (10, 10, -1, -1), 'func', None)] + collector.process_frames(frames, thread_id=1) + + self.assertIn('test.py', collector.file_samples) + self.assertIn(10, collector.file_samples['test.py']) + self.assertEqual(collector.file_samples['test.py'][10], 1) + + +def frame(filename, line, func): + """Create a frame tuple: (filename, location, funcname, opcode).""" + return (filename, (line, line, -1, -1), func, None) + + +class TestHeatmapCollectorNavigationButtons(unittest.TestCase): + """Test navigation button behavior for caller/callee relationships. + + For every call stack: + - Root frames (entry points): only DOWN arrow (callees) + - Middle frames: both UP and DOWN arrows + - Leaf frames: only UP arrow (callers) + """ + + def collect(self, *stacks): + """Create collector and process frame stacks.""" + collector = HeatmapCollector(sample_interval_usec=100) + for stack in stacks: + collector.process_frames(stack, thread_id=1) + return collector + + def test_deep_call_stack_relationships(self): + """Test root/middle/leaf navigation in a 5-level call stack.""" + # Stack: root -> A -> B -> C -> leaf + stack = [ + frame('leaf.py', 5, 'leaf'), + frame('c.py', 10, 'func_c'), + frame('b.py', 15, 'func_b'), + frame('a.py', 20, 'func_a'), + frame('root.py', 25, 'root'), + ] + c = self.collect(stack) + + # Root: only callees (no one calls it) + self.assertIn(('root.py', 25), c.call_graph) + self.assertNotIn(('root.py', 25), c.callers_graph) + + # Middle frames: both callers and callees + for key in [('a.py', 20), ('b.py', 15), ('c.py', 10)]: + self.assertIn(key, c.call_graph) + self.assertIn(key, c.callers_graph) + + # Leaf: only callers (doesn't call anyone) + self.assertNotIn(('leaf.py', 5), c.call_graph) + self.assertIn(('leaf.py', 5), c.callers_graph) + + def test_all_lines_in_function_see_callers(self): + """Test that interior lines map to their function for caller lookup.""" + # Same function sampled at different lines (12, 15, 10) + c = self.collect( + [frame('mod.py', 12, 'my_func'), frame('caller.py', 100, 'caller')], + [frame('mod.py', 15, 'my_func'), frame('caller.py', 100, 'caller')], + [frame('mod.py', 10, 'my_func'), frame('caller.py', 100, 'caller')], + ) + + # All lines should map to same function + for line in [10, 12, 15]: + self.assertEqual(c.line_to_function[('mod.py', line)], 'my_func') + + # Function definition line should have callers + func_def = c.function_definitions[('mod.py', 'my_func')] + self.assertIn(('mod.py', func_def), c.callers_graph) + + def test_multiple_callers_and_callees(self): + """Test multiple callers/callees are recorded correctly.""" + # Two callers -> target, and caller -> two callees + c = self.collect( + [frame('target.py', 10, 'target'), frame('caller1.py', 20, 'c1')], + [frame('target.py', 10, 'target'), frame('caller2.py', 30, 'c2')], + [frame('callee1.py', 5, 'f1'), frame('dispatcher.py', 40, 'dispatch')], + [frame('callee2.py', 6, 'f2'), frame('dispatcher.py', 40, 'dispatch')], + ) + + # Target has 2 callers + callers = c.callers_graph[('target.py', 10)] + self.assertEqual({x[0] for x in callers}, {'caller1.py', 'caller2.py'}) + + # Dispatcher has 2 callees + callees = c.call_graph[('dispatcher.py', 40)] + self.assertEqual({x[0] for x in callees}, {'callee1.py', 'callee2.py'}) + + def test_edge_samples_counted(self): + """Test that repeated calls accumulate edge counts.""" + stack = [frame('callee.py', 10, 'callee'), frame('caller.py', 20, 'caller')] + c = self.collect(stack, stack, stack) + + edge_key = (('caller.py', 20), ('callee.py', 10)) + self.assertEqual(c.edge_samples[edge_key], 3) + + +class TestHeatmapCollectorExport(unittest.TestCase): + """Test HeatmapCollector.export() method.""" + + def setUp(self): + """Set up test directory.""" + self.test_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, self.test_dir) + + def test_export_creates_output_directory(self): + """Test that export creates the output directory.""" + collector = HeatmapCollector(sample_interval_usec=100) + + # Add some data + frames = [('test.py', (10, 10, -1, -1), 'func', None)] + collector.process_frames(frames, thread_id=1) + + output_path = os.path.join(self.test_dir, 'heatmap_output') + + with captured_stdout(), captured_stderr(): + collector.export(output_path) + + self.assertTrue(os.path.exists(output_path)) + self.assertTrue(os.path.isdir(output_path)) + + def test_export_creates_index_html(self): + """Test that export creates index.html.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [('test.py', (10, 10, -1, -1), 'func', None)] + collector.process_frames(frames, thread_id=1) + + output_path = os.path.join(self.test_dir, 'heatmap_output') + + with captured_stdout(), captured_stderr(): + collector.export(output_path) + + index_path = os.path.join(output_path, 'index.html') + self.assertTrue(os.path.exists(index_path)) + + def test_export_creates_file_htmls(self): + """Test that export creates individual file HTMLs.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [('test.py', (10, 10, -1, -1), 'func', None)] + collector.process_frames(frames, thread_id=1) + + output_path = os.path.join(self.test_dir, 'heatmap_output') + + with captured_stdout(), captured_stderr(): + collector.export(output_path) + + # Check for file_XXXX.html files + html_files = [f for f in os.listdir(output_path) + if f.startswith('file_') and f.endswith('.html')] + self.assertGreater(len(html_files), 0) + + def test_export_with_empty_data(self): + """Test export with no data collected.""" + collector = HeatmapCollector(sample_interval_usec=100) + + output_path = os.path.join(self.test_dir, 'empty_output') + + # Should handle empty data gracefully + with captured_stdout(), captured_stderr(): + collector.export(output_path) + + def test_export_handles_html_suffix(self): + """Test that export handles .html suffix in output path.""" + collector = HeatmapCollector(sample_interval_usec=100) + + frames = [('test.py', (10, 10, -1, -1), 'func', None)] + collector.process_frames(frames, thread_id=1) + + # Path with .html suffix should be stripped + output_path = os.path.join(self.test_dir, 'output.html') + + with captured_stdout(), captured_stderr(): + collector.export(output_path) + + # Should create directory without .html + expected_dir = os.path.join(self.test_dir, 'output') + self.assertTrue(os.path.exists(expected_dir)) + + def test_export_with_multiple_files(self): + """Test export with multiple files.""" + collector = HeatmapCollector(sample_interval_usec=100) + + # Add samples for multiple files + collector.process_frames([('file1.py', (10, 10, -1, -1), 'func1', None)], thread_id=1) + collector.process_frames([('file2.py', (20, 20, -1, -1), 'func2', None)], thread_id=1) + collector.process_frames([('file3.py', (30, 30, -1, -1), 'func3', None)], thread_id=1) + + output_path = os.path.join(self.test_dir, 'multi_file') + + with captured_stdout(), captured_stderr(): + collector.export(output_path) + + # Should create HTML for each file + html_files = [f for f in os.listdir(output_path) + if f.startswith('file_') and f.endswith('.html')] + self.assertGreaterEqual(len(html_files), 3) + + def test_export_index_contains_file_references(self): + """Test that index.html contains references to profiled files.""" + collector = HeatmapCollector(sample_interval_usec=100) + collector.set_stats(sample_interval_usec=100, duration_sec=1.0, sample_rate=100.0) + + frames = [('mytest.py', (10, 10, -1, -1), 'my_func', None)] + collector.process_frames(frames, thread_id=1) + + output_path = os.path.join(self.test_dir, 'test_output') + + with captured_stdout(), captured_stderr(): + collector.export(output_path) + + index_path = os.path.join(output_path, 'index.html') + with open(index_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Should contain reference to the file + self.assertIn('mytest', content) + + def test_export_file_html_has_line_numbers(self): + """Test that exported file HTML contains line numbers.""" + collector = HeatmapCollector(sample_interval_usec=100) + + # Create a temporary Python file + temp_file = os.path.join(self.test_dir, 'temp_source.py') + with open(temp_file, 'w') as f: + f.write('def test():\n pass\n') + + frames = [(temp_file, (1, 1, -1, -1), 'test', None)] + collector.process_frames(frames, thread_id=1) + + output_path = os.path.join(self.test_dir, 'line_test') + + with captured_stdout(), captured_stderr(): + collector.export(output_path) + + # Find the generated file HTML + html_files = [f for f in os.listdir(output_path) + if f.startswith('file_') and f.endswith('.html')] + + if html_files: + with open(os.path.join(output_path, html_files[0]), 'r', encoding='utf-8') as f: + content = f.read() + + # Should have line-related content + self.assertIn('line-', content) + + +class MockFrameInfo: + """Mock FrameInfo for testing. + + Frame format: (filename, location, funcname, opcode) where: + - location is a tuple (lineno, end_lineno, col_offset, end_col_offset) + - opcode is an int or None + """ + + def __init__(self, filename, lineno, funcname, opcode=None): + self.filename = filename + self.funcname = funcname + self.opcode = opcode + self.location = (lineno, lineno, -1, -1) + + def __iter__(self): + return iter((self.filename, self.location, self.funcname, self.opcode)) + + def __getitem__(self, index): + return (self.filename, self.location, self.funcname, self.opcode)[index] + + def __len__(self): + return 4 + + def __repr__(self): + return f"MockFrameInfo('{self.filename}', {self.location}, '{self.funcname}', {self.opcode})" + + +class MockThreadInfo: + """Mock ThreadInfo for testing since the real one isn't accessible.""" + + def __init__(self, thread_id, frame_info, status=0): + self.thread_id = thread_id + self.frame_info = frame_info + self.status = status # Thread status flags + + def __repr__(self): + return f"MockThreadInfo(thread_id={self.thread_id}, frame_info={self.frame_info})" + + +class MockInterpreterInfo: + """Mock InterpreterInfo for testing since the real one isn't accessible.""" + + def __init__(self, interpreter_id, threads): + self.interpreter_id = interpreter_id + self.threads = threads + + def __repr__(self): + return f"MockInterpreterInfo(interpreter_id={self.interpreter_id}, threads={self.threads})" + + +class TestHeatmapCollector(unittest.TestCase): + """Tests for HeatmapCollector functionality.""" + + def test_heatmap_collector_basic(self): + """Test basic HeatmapCollector functionality.""" + collector = HeatmapCollector(sample_interval_usec=100) + + # Test empty state + self.assertEqual(len(collector.file_samples), 0) + self.assertEqual(len(collector.line_samples), 0) + + # Test collecting sample data - frames are 4-tuples: (filename, location, funcname, opcode) + test_frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo( + 1, + [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")], + )] + ) + ] + collector.collect(test_frames) + + # Should have recorded samples for the file + self.assertGreater(len(collector.line_samples), 0) + self.assertIn("file.py", collector.file_samples) + + # Check that line samples were recorded + file_data = collector.file_samples["file.py"] + self.assertGreater(len(file_data), 0) + + def test_heatmap_collector_export(self): + """Test heatmap HTML export functionality.""" + heatmap_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, heatmap_dir) + + collector = HeatmapCollector(sample_interval_usec=100) + + # Create test data with multiple files using MockFrameInfo + test_frames1 = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")])], + ) + ] + test_frames2 = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")])], + ) + ] # Same stack + test_frames3 = [ + MockInterpreterInfo(0, [MockThreadInfo(1, [MockFrameInfo("other.py", 5, "other_func")])]) + ] + + collector.collect(test_frames1) + collector.collect(test_frames2) + collector.collect(test_frames3) + + # Export heatmap + with (captured_stdout(), captured_stderr()): + collector.export(heatmap_dir) + + # Verify index.html was created + index_path = os.path.join(heatmap_dir, "index.html") + self.assertTrue(os.path.exists(index_path)) + self.assertGreater(os.path.getsize(index_path), 0) + + # Check index contains HTML content + with open(index_path, "r", encoding="utf-8") as f: + content = f.read() + + # Should be valid HTML + self.assertIn("<!doctype html>", content.lower()) + self.assertIn("<html", content) + self.assertIn("Tachyon Profiler", content) + + # Should contain file references + self.assertIn("file.py", content) + self.assertIn("other.py", content) + + # Verify individual file HTMLs were created + file_htmls = [f for f in os.listdir(heatmap_dir) if f.startswith("file_") and f.endswith(".html")] + self.assertGreater(len(file_htmls), 0) + + # Check one of the file HTMLs + file_html_path = os.path.join(heatmap_dir, file_htmls[0]) + with open(file_html_path, "r", encoding="utf-8") as f: + file_content = f.read() + + # Should contain heatmap styling and JavaScript + self.assertIn("line-sample", file_content) + self.assertIn("nav-btn", file_content) + + +class TestHeatmapCollectorLocation(unittest.TestCase): + """Tests for HeatmapCollector location handling.""" + + def test_heatmap_with_full_location_info(self): + """Test HeatmapCollector uses full location tuple.""" + collector = HeatmapCollector(sample_interval_usec=1000) + + # Frame with full location: (lineno, end_lineno, col_offset, end_col_offset) + frame = MockFrameInfo("test.py", 10, "func") + # Override with full location info + frame.location = LocationInfo(10, 15, 4, 20) + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame])] + ) + ] + collector.collect(frames) + + # Verify data was collected with location info + # HeatmapCollector uses file_samples dict with filename -> Counter of linenos + self.assertIn("test.py", collector.file_samples) + # Line 10 should have samples + self.assertIn(10, collector.file_samples["test.py"]) + + def test_heatmap_with_none_location(self): + """Test HeatmapCollector handles None location gracefully.""" + collector = HeatmapCollector(sample_interval_usec=1000) + + # Synthetic frame with None location + frame = MockFrameInfo("~", 0, "<native>") + frame.location = None + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame])] + ) + ] + # Should not raise + collector.collect(frames) + + def test_heatmap_export_with_location_data(self): + """Test HeatmapCollector export includes location info.""" + tmp_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, tmp_dir) + + collector = HeatmapCollector(sample_interval_usec=1000) + + frame = MockFrameInfo("test.py", 10, "process") + frame.location = LocationInfo(10, 12, 0, 30) + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame])] + ) + ] + collector.collect(frames) + + # Export should work + with (captured_stdout(), captured_stderr()): + collector.export(tmp_dir) + self.assertTrue(os.path.exists(os.path.join(tmp_dir, "index.html"))) + + def test_heatmap_collector_frame_format(self): + """Test HeatmapCollector with 4-element frame format.""" + collector = HeatmapCollector(sample_interval_usec=1000) + + frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("app.py", 100, "main", opcode=90), + MockFrameInfo("utils.py", 50, "helper", opcode=100), + MockFrameInfo("lib.py", 25, "process", opcode=None), + ], + ) + ], + ) + ] + collector.collect(frames) + + # Should have recorded data for the files + self.assertIn("app.py", collector.file_samples) + self.assertIn("utils.py", collector.file_samples) + self.assertIn("lib.py", collector.file_samples) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_profiling/test_sampling_profiler/__init__.py b/Lib/test/test_profiling/test_sampling_profiler/__init__.py new file mode 100644 index 00000000000000..616ae5b49f03ef --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/__init__.py @@ -0,0 +1,9 @@ +"""Tests for the sampling profiler (profiling.sampling).""" + +import os +from test.support import load_package_tests + + +def load_tests(*args): + """Load all tests from this subpackage.""" + return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_profiling/test_sampling_profiler/_live_collector_helpers.py b/Lib/test/test_profiling/test_sampling_profiler/_live_collector_helpers.py new file mode 100644 index 00000000000000..2c672895099140 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/_live_collector_helpers.py @@ -0,0 +1,62 @@ +"""Common test helpers and mocks for live collector tests.""" + +from collections import namedtuple + +from profiling.sampling.constants import ( + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, +) + + +# Matches the C structseq LocationInfo from _remote_debugging +LocationInfo = namedtuple('LocationInfo', ['lineno', 'end_lineno', 'col_offset', 'end_col_offset']) + + +class MockFrameInfo: + """Mock FrameInfo for testing. + + Frame format: (filename, location, funcname, opcode) where: + - location is a tuple (lineno, end_lineno, col_offset, end_col_offset) + - opcode is an int or None + """ + + def __init__(self, filename, lineno, funcname, opcode=None): + self.filename = filename + self.funcname = funcname + self.opcode = opcode + self.location = LocationInfo(lineno, lineno, -1, -1) + + def __iter__(self): + return iter((self.filename, self.location, self.funcname, self.opcode)) + + def __getitem__(self, index): + return (self.filename, self.location, self.funcname, self.opcode)[index] + + def __len__(self): + return 4 + + def __repr__(self): + return f"MockFrameInfo('{self.filename}', {self.location}, '{self.funcname}', {self.opcode})" + + +class MockThreadInfo: + """Mock ThreadInfo for testing.""" + + def __init__(self, thread_id, frame_info, status=THREAD_STATUS_HAS_GIL | THREAD_STATUS_ON_CPU): + self.thread_id = thread_id + self.frame_info = frame_info + self.status = status + + def __repr__(self): + return f"MockThreadInfo(thread_id={self.thread_id}, frame_info={self.frame_info}, status={self.status})" + + +class MockInterpreterInfo: + """Mock InterpreterInfo for testing.""" + + def __init__(self, interpreter_id, threads): + self.interpreter_id = interpreter_id + self.threads = threads + + def __repr__(self): + return f"MockInterpreterInfo(interpreter_id={self.interpreter_id}, threads={self.threads})" diff --git a/Lib/test/test_profiling/test_sampling_profiler/helpers.py b/Lib/test/test_profiling/test_sampling_profiler/helpers.py new file mode 100644 index 00000000000000..b07776d415bb29 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/helpers.py @@ -0,0 +1,202 @@ +"""Helper utilities for sampling profiler tests.""" + +import contextlib +import socket +import subprocess +import sys +import unittest +from collections import namedtuple + +from test.support import SHORT_TIMEOUT +from test.support.socket_helper import find_unused_port +from test.support.os_helper import unlink + + +PROCESS_VM_READV_SUPPORTED = False + +try: + from _remote_debugging import PROCESS_VM_READV_SUPPORTED # noqa: F401 + import _remote_debugging # noqa: F401 +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) +else: + import profiling.sampling # noqa: F401 + from profiling.sampling.sample import SampleProfiler # noqa: F401 + + +skip_if_not_supported = unittest.skipIf( + ( + sys.platform != "darwin" + and sys.platform != "linux" + and sys.platform != "win32" + ), + "Test only runs on Linux, Windows and MacOS", +) + +SubprocessInfo = namedtuple("SubprocessInfo", ["process", "socket"]) + + +def _wait_for_signal(sock, expected_signals, timeout=SHORT_TIMEOUT): + """ + Wait for expected signal(s) from a socket with proper timeout and EOF handling. + + Args: + sock: Connected socket to read from + expected_signals: Single bytes object or list of bytes objects to wait for + timeout: Socket timeout in seconds + + Returns: + bytes: Complete accumulated response buffer + + Raises: + RuntimeError: If connection closed before signal received or timeout + """ + if isinstance(expected_signals, bytes): + expected_signals = [expected_signals] + + sock.settimeout(timeout) + buffer = b"" + + while True: + # Check if all expected signals are in buffer + if all(sig in buffer for sig in expected_signals): + return buffer + + try: + chunk = sock.recv(4096) + if not chunk: + raise RuntimeError( + f"Connection closed before receiving expected signals. " + f"Expected: {expected_signals}, Got: {buffer[-200:]!r}" + ) + buffer += chunk + except socket.timeout: + raise RuntimeError( + f"Timeout waiting for signals. " + f"Expected: {expected_signals}, Got: {buffer[-200:]!r}" + ) from None + except OSError as e: + raise RuntimeError( + f"Socket error while waiting for signals: {e}. " + f"Expected: {expected_signals}, Got: {buffer[-200:]!r}" + ) from None + + +def _cleanup_sockets(*sockets): + """Safely close multiple sockets, ignoring errors.""" + for sock in sockets: + if sock is not None: + try: + sock.close() + except OSError: + pass + + +def _cleanup_process(proc, timeout=SHORT_TIMEOUT): + """Terminate a process gracefully, escalating to kill if needed.""" + if proc.poll() is not None: + return + proc.terminate() + try: + proc.wait(timeout=timeout) + return + except subprocess.TimeoutExpired: + pass + proc.kill() + try: + proc.wait(timeout=timeout) + except subprocess.TimeoutExpired: + pass # Process refuses to die, nothing more we can do + + +@contextlib.contextmanager +def test_subprocess(script, wait_for_working=False): + """Context manager to create a test subprocess with socket synchronization. + + Args: + script: Python code to execute in the subprocess. If wait_for_working + is True, script should send b"working" after starting work. + wait_for_working: If True, wait for both "ready" and "working" signals. + Default False for backward compatibility. + + Yields: + SubprocessInfo: Named tuple with process and socket objects + """ + # Find an unused port for socket communication + port = find_unused_port() + + # Inject socket connection code at the beginning of the script + socket_code = f""" +import socket +_test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +_test_sock.connect(('localhost', {port})) +_test_sock.sendall(b"ready") +""" + + # Combine socket code with user script + full_script = socket_code + script + + # Create server socket to wait for process to be ready + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server_socket.bind(("localhost", port)) + server_socket.settimeout(SHORT_TIMEOUT) + server_socket.listen(1) + + proc = subprocess.Popen( + [sys.executable, "-c", full_script], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + client_socket = None + try: + # Wait for process to connect and send ready signal + client_socket, _ = server_socket.accept() + server_socket.close() + server_socket = None + + # Wait for ready signal, and optionally working signal + if wait_for_working: + _wait_for_signal(client_socket, [b"ready", b"working"]) + else: + _wait_for_signal(client_socket, b"ready") + + yield SubprocessInfo(proc, client_socket) + finally: + _cleanup_sockets(client_socket, server_socket) + _cleanup_process(proc) + + +def close_and_unlink(file): + """Close a file and unlink it from the filesystem.""" + file.close() + unlink(file.name) + + +def jsonl_tables(records): + """Extract the canonical sections of a parsed JSONL profile. + + Returns ``(meta, str_defs, frame_defs, agg, end)`` where ``str_defs`` is a + ``{str_id: value}`` dict, ``frame_defs`` is a flat list of all frame + definitions across chunks, and ``agg`` is the first agg record (sufficient + for tests that only emit one chunk). + """ + meta = next(record for record in records if record["type"] == "meta") + end = next(record for record in records if record["type"] == "end") + agg = next(record for record in records if record["type"] == "agg") + str_defs = { + item["str_id"]: item["value"] + for record in records + if record["type"] == "string_table" + for item in record["strings"] + } + frame_defs = [ + item + for record in records + if record["type"] == "frame_table" + for item in record["frames"] + ] + return meta, str_defs, frame_defs, agg, end diff --git a/Lib/test/test_profiling/test_sampling_profiler/mocks.py b/Lib/test/test_profiling/test_sampling_profiler/mocks.py new file mode 100644 index 00000000000000..6ac2d08e898d81 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/mocks.py @@ -0,0 +1,112 @@ +"""Mock classes for sampling profiler tests.""" + +from collections import namedtuple + +# Matches the C structseq LocationInfo from _remote_debugging +LocationInfo = namedtuple('LocationInfo', ['lineno', 'end_lineno', 'col_offset', 'end_col_offset']) + + +class MockFrameInfo: + """Mock FrameInfo for testing. + + Frame format: (filename, location, funcname, opcode) where: + - location is a tuple (lineno, end_lineno, col_offset, end_col_offset) + - opcode is an int or None + """ + + def __init__(self, filename, lineno, funcname, opcode=None): + self.filename = filename + self.funcname = funcname + self.opcode = opcode + self.location = LocationInfo(lineno, lineno, -1, -1) + + def __iter__(self): + return iter((self.filename, self.location, self.funcname, self.opcode)) + + def __getitem__(self, index): + return (self.filename, self.location, self.funcname, self.opcode)[index] + + def __len__(self): + return 4 + + def __repr__(self): + return f"MockFrameInfo('{self.filename}', {self.location}, '{self.funcname}', {self.opcode})" + + +class MockThreadInfo: + """Mock ThreadInfo for testing since the real one isn't accessible.""" + + def __init__( + self, thread_id, frame_info, status=0 + ): # Default to THREAD_STATE_RUNNING (0) + self.thread_id = thread_id + self.frame_info = frame_info + self.status = status + + def __repr__(self): + return f"MockThreadInfo(thread_id={self.thread_id}, frame_info={self.frame_info}, status={self.status})" + + +class MockInterpreterInfo: + """Mock InterpreterInfo for testing since the real one isn't accessible.""" + + def __init__(self, interpreter_id, threads): + self.interpreter_id = interpreter_id + self.threads = threads + + def __repr__(self): + return f"MockInterpreterInfo(interpreter_id={self.interpreter_id}, threads={self.threads})" + + +class MockCoroInfo: + """Mock CoroInfo for testing async tasks.""" + + def __init__(self, task_name, call_stack): + self.task_name = task_name # In reality, this is the parent task ID + self.call_stack = call_stack + + def __repr__(self): + return f"MockCoroInfo(task_name={self.task_name}, call_stack={self.call_stack})" + + +class MockTaskInfo: + """Mock TaskInfo for testing async tasks.""" + + def __init__(self, task_id, task_name, coroutine_stack, awaited_by=None): + self.task_id = task_id + self.task_name = task_name + self.coroutine_stack = coroutine_stack # List of CoroInfo objects + self.awaited_by = awaited_by or [] # List of CoroInfo objects (parents) + + def __repr__(self): + return f"MockTaskInfo(task_id={self.task_id}, task_name={self.task_name})" + + +class MockAwaitedInfo: + """Mock AwaitedInfo for testing async tasks.""" + + def __init__(self, thread_id, awaited_by): + self.thread_id = thread_id + self.awaited_by = awaited_by # List of TaskInfo objects + + def __repr__(self): + return f"MockAwaitedInfo(thread_id={self.thread_id}, awaited_by={len(self.awaited_by)} tasks)" + + +def make_diff_collector_with_mock_baseline(baseline_samples): + """Create a DiffFlamegraphCollector with baseline injected directly, + skipping the binary round-trip that _load_baseline normally does.""" + from profiling.sampling.stack_collector import ( + DiffFlamegraphCollector, + FlamegraphCollector, + ) + + baseline = FlamegraphCollector(1000) + for sample in baseline_samples: + baseline.collect(sample) + + # Path is unused since we inject _baseline_collector directly; + # use __file__ as a dummy path that passes the existence check. + diff = DiffFlamegraphCollector(1000, baseline_binary_path=__file__) + diff._baseline_collector = baseline + return diff diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_advanced.py b/Lib/test/test_profiling/test_sampling_profiler/test_advanced.py new file mode 100644 index 00000000000000..11b1ad84242fd4 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_advanced.py @@ -0,0 +1,236 @@ +"""Tests for advanced sampling profiler features (GC tracking, native frames, ProcessPoolExecutor support).""" + +import io +import os +import subprocess +import tempfile +import unittest +from unittest import mock + +try: + import _remote_debugging # noqa: F401 + import profiling.sampling + import profiling.sampling.sample + from profiling.sampling.pstats_collector import PstatsCollector + from profiling.sampling.stack_collector import CollapsedStackCollector +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) + +from test.support import ( + SHORT_TIMEOUT, + SuppressCrashReport, + os_helper, + requires_remote_subprocess_debugging, + script_helper, +) + +from .helpers import close_and_unlink, test_subprocess + + +@requires_remote_subprocess_debugging() +class TestGCFrameTracking(unittest.TestCase): + """Tests for GC frame tracking in the sampling profiler.""" + + @classmethod + def setUpClass(cls): + """Create a static test script with GC frames and CPU-intensive work.""" + cls.gc_test_script = ''' +import gc + +class ExpensiveGarbage: + def __init__(self): + self.cycle = self + + def __del__(self): + result = 0 + for i in range(100000): + result += i * i + if i % 1000 == 0: + result = result % 1000000 + +_test_sock.sendall(b"working") +while True: + ExpensiveGarbage() + gc.collect() +''' + + def test_gc_frames_enabled(self): + """Test that GC frames appear when gc tracking is enabled.""" + with ( + test_subprocess(self.gc_test_script, wait_for_working=True) as subproc, + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=5000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=1, + native=False, + gc=True, + ) + collector.print_stats(show_summary=False) + + output = captured_output.getvalue() + + # Should capture samples + self.assertIn("Captured", output) + self.assertIn("samples", output) + + # GC frames should be present + self.assertIn("<GC>", output) + + def test_gc_frames_disabled(self): + """Test that GC frames do not appear when gc tracking is disabled.""" + with ( + test_subprocess(self.gc_test_script, wait_for_working=True) as subproc, + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=5000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=1, + native=False, + gc=False, + ) + collector.print_stats(show_summary=False) + + output = captured_output.getvalue() + + # Should capture samples + self.assertIn("Captured", output) + self.assertIn("samples", output) + + # GC frames should NOT be present + self.assertNotIn("<GC>", output) + + +@requires_remote_subprocess_debugging() +class TestNativeFrameTracking(unittest.TestCase): + """Tests for native frame tracking in the sampling profiler.""" + + @classmethod + def setUpClass(cls): + """Create a static test script with native frames and CPU-intensive work.""" + cls.native_test_script = """ +import operator + +def inner(): + for _ in range(1_000_0000): + pass + +_test_sock.sendall(b"working") +while True: + operator.call(inner) +""" + + def test_native_frames_enabled(self): + """Test that native frames appear when native tracking is enabled.""" + collapsed_file = tempfile.NamedTemporaryFile( + suffix=".txt", delete=False + ) + self.addCleanup(close_and_unlink, collapsed_file) + + with test_subprocess(self.native_test_script, wait_for_working=True) as subproc: + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = CollapsedStackCollector(1000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=1, + native=True, + ) + collector.export(collapsed_file.name) + + # Verify file was created and contains valid data + self.assertTrue(os.path.exists(collapsed_file.name)) + self.assertGreater(os.path.getsize(collapsed_file.name), 0) + + # Check file format + with open(collapsed_file.name, "r") as f: + content = f.read() + + lines = content.strip().split("\n") + self.assertGreater(len(lines), 0) + + stacks = [line.rsplit(" ", 1)[0] for line in lines] + + # Most samples should have native code in the middle of the stack: + self.assertTrue(any(";<native>;" in stack for stack in stacks)) + + # No samples should have native code at the top of the stack: + self.assertFalse(any(stack.endswith(";<native>") for stack in stacks)) + + def test_native_frames_disabled(self): + """Test that native frames do not appear when native tracking is disabled.""" + with ( + test_subprocess(self.native_test_script, wait_for_working=True) as subproc, + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=5000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=1, + ) + collector.print_stats(show_summary=False) + output = captured_output.getvalue() + # Native frames should NOT be present: + self.assertNotIn("<native>", output) + + +@requires_remote_subprocess_debugging() +class TestProcessPoolExecutorSupport(unittest.TestCase): + """ + Test that ProcessPoolExecutor works correctly with profiling.sampling. + """ + + def test_process_pool_executor_pickle(self): + # gh-140729: test use ProcessPoolExecutor.map() can sampling + test_script = """ +import concurrent.futures + +def worker(x): + return x * 2 + +if __name__ == "__main__": + with concurrent.futures.ProcessPoolExecutor() as executor: + results = list(executor.map(worker, [1, 2, 3])) + print(f"Results: {results}") +""" + with os_helper.temp_dir() as temp_dir: + script = script_helper.make_script( + temp_dir, "test_process_pool_executor_pickle", test_script + ) + with SuppressCrashReport(): + with script_helper.spawn_python( + "-m", + "profiling.sampling", + "run", + "-d", + "5", + "-r", + "10", + script, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) as proc: + try: + stdout, stderr = proc.communicate( + timeout=SHORT_TIMEOUT + ) + except subprocess.TimeoutExpired: + proc.kill() + stdout, stderr = proc.communicate() + + self.assertIn("Results: [2, 4, 6]", stdout) + self.assertNotIn("Can't pickle", stderr) diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_async.py b/Lib/test/test_profiling/test_sampling_profiler/test_async.py new file mode 100644 index 00000000000000..1f5685717b6273 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_async.py @@ -0,0 +1,787 @@ +"""Tests for async stack reconstruction in the sampling profiler. + +Each test covers a distinct algorithm path or edge case: +1. Graph building: _build_task_graph() +2. Leaf identification: _find_leaf_tasks() +3. Stack traversal: _build_linear_stacks() with BFS +""" + +import inspect +import unittest + +try: + import _remote_debugging # noqa: F401 + from profiling.sampling.pstats_collector import PstatsCollector + from profiling.sampling.stack_collector import FlamegraphCollector + from profiling.sampling.sample import sample, sample_live, SampleProfiler +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) + +from .mocks import MockFrameInfo, MockCoroInfo, MockTaskInfo, MockAwaitedInfo + + +class TestAsyncStackReconstruction(unittest.TestCase): + """Test async task tree linear stack reconstruction algorithm.""" + + def test_empty_input(self): + """Test _build_task_graph with empty awaited_info_list.""" + collector = PstatsCollector(sample_interval_usec=1000) + stacks = list(collector._iter_async_frames([])) + self.assertEqual(len(stacks), 0) + + def test_single_root_task(self): + """Test _find_leaf_tasks: root task with no parents is its own leaf.""" + collector = PstatsCollector(sample_interval_usec=1000) + + root = MockTaskInfo( + task_id=123, + task_name="Task-1", + coroutine_stack=[ + MockCoroInfo( + task_name="Task-1", + call_stack=[MockFrameInfo("main.py", 10, "main")] + ) + ], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=100, awaited_by=[root])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + # Single root is both leaf and root + self.assertEqual(len(stacks), 1) + frames, thread_id, leaf_id = stacks[0] + self.assertEqual(leaf_id, 123) + self.assertEqual(thread_id, 100) + + def test_parent_child_chain(self): + """Test _build_linear_stacks: BFS follows parent links from leaf to root. + + Task graph: + + Parent (id=1) + | + Child (id=2) + """ + collector = PstatsCollector(sample_interval_usec=1000) + + child = MockTaskInfo( + task_id=2, + task_name="Child", + coroutine_stack=[ + MockCoroInfo(task_name="Child", call_stack=[MockFrameInfo("c.py", 5, "child_fn")]) + ], + awaited_by=[ + MockCoroInfo(task_name=1, call_stack=[MockFrameInfo("p.py", 10, "parent_await")]) + ] + ) + + parent = MockTaskInfo( + task_id=1, + task_name="Parent", + coroutine_stack=[ + MockCoroInfo(task_name="Parent", call_stack=[MockFrameInfo("p.py", 15, "parent_fn")]) + ], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=200, awaited_by=[child, parent])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + # Leaf is child, traverses to parent + self.assertEqual(len(stacks), 1) + frames, thread_id, leaf_id = stacks[0] + self.assertEqual(leaf_id, 2) + + # Verify both child and parent frames present + func_names = [f.funcname for f in frames] + self.assertIn("child_fn", func_names) + self.assertIn("parent_fn", func_names) + + def test_multiple_leaf_tasks(self): + """Test _find_leaf_tasks: identifies multiple leaves correctly. + + Task graph (fan-out from root): + + Root (id=1) + / \ + Leaf1 (id=10) Leaf2 (id=20) + + Expected: 2 stacks (one for each leaf). + """ + collector = PstatsCollector(sample_interval_usec=1000) + leaf1 = MockTaskInfo( + task_id=10, + task_name="Leaf1", + coroutine_stack=[MockCoroInfo(task_name="Leaf1", call_stack=[MockFrameInfo("l1.py", 1, "f1")])], + awaited_by=[MockCoroInfo(task_name=1, call_stack=[MockFrameInfo("r.py", 5, "root")])] + ) + + leaf2 = MockTaskInfo( + task_id=20, + task_name="Leaf2", + coroutine_stack=[MockCoroInfo(task_name="Leaf2", call_stack=[MockFrameInfo("l2.py", 2, "f2")])], + awaited_by=[MockCoroInfo(task_name=1, call_stack=[MockFrameInfo("r.py", 5, "root")])] + ) + + root = MockTaskInfo( + task_id=1, + task_name="Root", + coroutine_stack=[MockCoroInfo(task_name="Root", call_stack=[MockFrameInfo("r.py", 10, "main")])], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=300, awaited_by=[leaf1, leaf2, root])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + # Two leaves = two stacks + self.assertEqual(len(stacks), 2) + leaf_ids = {leaf_id for _, _, leaf_id in stacks} + self.assertEqual(leaf_ids, {10, 20}) + + def test_cycle_detection(self): + """Test _build_linear_stacks: cycle detection prevents infinite loops. + + Task graph (cyclic dependency): + + A (id=1) <---> B (id=2) + + Neither task is a leaf (both have parents), so no stacks are produced. + """ + collector = PstatsCollector(sample_interval_usec=1000) + task_a = MockTaskInfo( + task_id=1, + task_name="A", + coroutine_stack=[MockCoroInfo(task_name="A", call_stack=[MockFrameInfo("a.py", 1, "a")])], + awaited_by=[MockCoroInfo(task_name=2, call_stack=[MockFrameInfo("b.py", 5, "b")])] + ) + + task_b = MockTaskInfo( + task_id=2, + task_name="B", + coroutine_stack=[MockCoroInfo(task_name="B", call_stack=[MockFrameInfo("b.py", 10, "b")])], + awaited_by=[MockCoroInfo(task_name=1, call_stack=[MockFrameInfo("a.py", 15, "a")])] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=400, awaited_by=[task_a, task_b])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + # No leaves (both have parents), should return empty + self.assertEqual(len(stacks), 0) + + def test_orphaned_parent_reference(self): + """Test _build_linear_stacks: handles parent ID not in task_map.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Task references non-existent parent + orphan = MockTaskInfo( + task_id=5, + task_name="Orphan", + coroutine_stack=[MockCoroInfo(task_name="Orphan", call_stack=[MockFrameInfo("o.py", 1, "orphan")])], + awaited_by=[MockCoroInfo(task_name=999, call_stack=[])] # 999 doesn't exist + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=500, awaited_by=[orphan])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + # Stops at missing parent, yields what it has + self.assertEqual(len(stacks), 1) + frames, _, leaf_id = stacks[0] + self.assertEqual(leaf_id, 5) + + def test_multiple_coroutines_per_task(self): + """Test _build_linear_stacks: collects frames from all coroutines in task.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Task with multiple coroutines (e.g., nested async generators) + task = MockTaskInfo( + task_id=7, + task_name="Multi", + coroutine_stack=[ + MockCoroInfo(task_name="Multi", call_stack=[MockFrameInfo("g.py", 5, "gen1")]), + MockCoroInfo(task_name="Multi", call_stack=[MockFrameInfo("g.py", 10, "gen2")]), + ], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=600, awaited_by=[task])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + self.assertEqual(len(stacks), 1) + frames, _, _ = stacks[0] + + # Both coroutine frames should be present + func_names = [f.funcname for f in frames] + self.assertIn("gen1", func_names) + self.assertIn("gen2", func_names) + + def test_multiple_threads(self): + """Test _build_task_graph: handles multiple AwaitedInfo (different threads).""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Two threads with separate task trees + thread1_task = MockTaskInfo( + task_id=100, + task_name="T1", + coroutine_stack=[MockCoroInfo(task_name="T1", call_stack=[MockFrameInfo("t1.py", 1, "t1")])], + awaited_by=[] + ) + + thread2_task = MockTaskInfo( + task_id=200, + task_name="T2", + coroutine_stack=[MockCoroInfo(task_name="T2", call_stack=[MockFrameInfo("t2.py", 1, "t2")])], + awaited_by=[] + ) + + awaited_info_list = [ + MockAwaitedInfo(thread_id=1, awaited_by=[thread1_task]), + MockAwaitedInfo(thread_id=2, awaited_by=[thread2_task]), + ] + + stacks = list(collector._iter_async_frames(awaited_info_list)) + + # Two threads = two stacks + self.assertEqual(len(stacks), 2) + + # Verify thread IDs preserved + thread_ids = {thread_id for _, thread_id, _ in stacks} + self.assertEqual(thread_ids, {1, 2}) + + def test_collect_public_interface(self): + """Test collect() method correctly routes to async frame processing.""" + collector = PstatsCollector(sample_interval_usec=1000) + + child = MockTaskInfo( + task_id=50, + task_name="Child", + coroutine_stack=[MockCoroInfo(task_name="Child", call_stack=[MockFrameInfo("c.py", 1, "child")])], + awaited_by=[MockCoroInfo(task_name=51, call_stack=[])] + ) + + parent = MockTaskInfo( + task_id=51, + task_name="Parent", + coroutine_stack=[MockCoroInfo(task_name="Parent", call_stack=[MockFrameInfo("p.py", 1, "parent")])], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=999, awaited_by=[child, parent])] + + # Public interface: collect() + collector.collect(awaited_info_list) + + # Verify stats collected + self.assertGreater(len(collector.result), 0) + func_names = [loc[2] for loc in collector.result.keys()] + self.assertIn("child", func_names) + self.assertIn("parent", func_names) + + def test_diamond_pattern_multiple_parents(self): + """Test _build_linear_stacks: task with 2+ parents picks one deterministically. + + CRITICAL: Tests that when a task has multiple parents, we pick one parent + deterministically (sorted, first one) and annotate the task name with parent count. + """ + collector = PstatsCollector(sample_interval_usec=1000) + + # Diamond pattern: Root spawns A and B, both await Child + # + # Root (id=1) + # / \ + # A (id=2) B (id=3) + # \ / + # Child (id=4) + # + + child = MockTaskInfo( + task_id=4, + task_name="Child", + coroutine_stack=[MockCoroInfo(task_name="Child", call_stack=[MockFrameInfo("c.py", 1, "child_work")])], + awaited_by=[ + MockCoroInfo(task_name=2, call_stack=[MockFrameInfo("a.py", 5, "a_await")]), # Parent A + MockCoroInfo(task_name=3, call_stack=[MockFrameInfo("b.py", 5, "b_await")]), # Parent B + ] + ) + + parent_a = MockTaskInfo( + task_id=2, + task_name="A", + coroutine_stack=[MockCoroInfo(task_name="A", call_stack=[MockFrameInfo("a.py", 10, "a_work")])], + awaited_by=[MockCoroInfo(task_name=1, call_stack=[MockFrameInfo("root.py", 5, "root_spawn")])] + ) + + parent_b = MockTaskInfo( + task_id=3, + task_name="B", + coroutine_stack=[MockCoroInfo(task_name="B", call_stack=[MockFrameInfo("b.py", 10, "b_work")])], + awaited_by=[MockCoroInfo(task_name=1, call_stack=[MockFrameInfo("root.py", 5, "root_spawn")])] + ) + + root = MockTaskInfo( + task_id=1, + task_name="Root", + coroutine_stack=[MockCoroInfo(task_name="Root", call_stack=[MockFrameInfo("root.py", 20, "main")])], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=777, awaited_by=[child, parent_a, parent_b, root])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + # Should get 1 stack: Child->A->Root (picks parent with lowest ID: 2) + self.assertEqual(len(stacks), 1, "Diamond should create only 1 path, picking first sorted parent") + + # Verify the single stack + frames, thread_id, leaf_id = stacks[0] + self.assertEqual(leaf_id, 4) + self.assertEqual(thread_id, 777) + + func_names = [f.funcname for f in frames] + # Stack should contain child, parent A (id=2, first when sorted), and root + self.assertIn("child_work", func_names) + self.assertIn("a_work", func_names, "Should use parent A (id=2, first when sorted)") + self.assertNotIn("b_work", func_names, "Should not include parent B") + self.assertIn("main", func_names) + + # Verify Child task is annotated with parent count + self.assertIn("Child (2 parents)", func_names, "Child task should be annotated with parent count") + + def test_empty_coroutine_stack(self): + """Test _build_linear_stacks: handles empty coroutine_stack (line 109 condition false).""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Task with no coroutine_stack + task = MockTaskInfo( + task_id=99, + task_name="EmptyStack", + coroutine_stack=[], # Empty! + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=111, awaited_by=[task])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + self.assertEqual(len(stacks), 1) + frames, _, _ = stacks[0] + + # Should only have task marker, no function frames + func_names = [f.funcname for f in frames] + self.assertEqual(len(func_names), 1, "Should only have task marker") + self.assertIn("EmptyStack", func_names) + + def test_orphaned_parent_with_no_frames_collected(self): + """Test _build_linear_stacks: orphaned parent at start with empty frames (line 94-96).""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Leaf that doesn't exist in task_map (should not happen normally, but test robustness) + # We'll create a scenario where the leaf_id is present but empty + + # Task references non-existent parent, and has no coroutine_stack + orphan = MockTaskInfo( + task_id=88, + task_name="Orphan", + coroutine_stack=[], # No frames + awaited_by=[MockCoroInfo(task_name=999, call_stack=[])] # Parent doesn't exist + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=222, awaited_by=[orphan])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + # Should yield because we have the task marker even with no function frames + self.assertEqual(len(stacks), 1) + frames, _, leaf_id = stacks[0] + self.assertEqual(leaf_id, 88) + # Has task marker but no function frames + self.assertGreater(len(frames), 0, "Should have at least task marker") + + def test_frame_ordering(self): + """Test _build_linear_stacks: frames are collected in correct order (leaf->root). + + Task graph (3-level chain): + + Root (id=1) <- root_bottom, root_top + | + Middle (id=2) <- mid_bottom, mid_top + | + Leaf (id=3) <- leaf_bottom, leaf_top + + Expected frame order: leaf_bottom, leaf_top, mid_bottom, mid_top, root_bottom, root_top + (stack is built bottom-up: leaf frames first, then parent frames). + """ + collector = PstatsCollector(sample_interval_usec=1000) + leaf = MockTaskInfo( + task_id=3, + task_name="Leaf", + coroutine_stack=[ + MockCoroInfo(task_name="Leaf", call_stack=[ + MockFrameInfo("leaf.py", 1, "leaf_bottom"), + MockFrameInfo("leaf.py", 2, "leaf_top"), + ]) + ], + awaited_by=[MockCoroInfo(task_name=2, call_stack=[])] + ) + + middle = MockTaskInfo( + task_id=2, + task_name="Middle", + coroutine_stack=[ + MockCoroInfo(task_name="Middle", call_stack=[ + MockFrameInfo("mid.py", 1, "mid_bottom"), + MockFrameInfo("mid.py", 2, "mid_top"), + ]) + ], + awaited_by=[MockCoroInfo(task_name=1, call_stack=[])] + ) + + root = MockTaskInfo( + task_id=1, + task_name="Root", + coroutine_stack=[ + MockCoroInfo(task_name="Root", call_stack=[ + MockFrameInfo("root.py", 1, "root_bottom"), + MockFrameInfo("root.py", 2, "root_top"), + ]) + ], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=333, awaited_by=[leaf, middle, root])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + self.assertEqual(len(stacks), 1) + frames, _, _ = stacks[0] + + func_names = [f.funcname for f in frames] + + # Order should be: leaf frames, leaf marker, middle frames, middle marker, root frames, root marker + leaf_bottom_idx = func_names.index("leaf_bottom") + leaf_top_idx = func_names.index("leaf_top") + mid_bottom_idx = func_names.index("mid_bottom") + root_bottom_idx = func_names.index("root_bottom") + + # Verify leaf comes before middle comes before root + self.assertLess(leaf_bottom_idx, leaf_top_idx, "Leaf frames in order") + self.assertLess(leaf_top_idx, mid_bottom_idx, "Leaf before middle") + self.assertLess(mid_bottom_idx, root_bottom_idx, "Middle before root") + + def test_complex_multi_parent_convergence(self): + """Test _build_linear_stacks: multiple leaves with same parents pick deterministically. + + Tests that when multiple leaves have multiple parents, each leaf picks the same + parent (sorted, first one) and all leaves are annotated with parent count. + + Task graph structure (both leaves awaited by both A and B):: + + Root (id=1) + / \\ + A (id=2) B (id=3) + | \\ / | + | \\ / | + | \\/ | + | /\\ | + | / \\ | + LeafX (id=4) LeafY (id=5) + + Expected behavior: Both leaves pick parent A (lowest id=2) for their stack path. + Result: 2 stacks, both going through A -> Root (B is skipped). + """ + collector = PstatsCollector(sample_interval_usec=1000) + + leaf_x = MockTaskInfo( + task_id=4, + task_name="LeafX", + coroutine_stack=[MockCoroInfo(task_name="LeafX", call_stack=[MockFrameInfo("x.py", 1, "x")])], + awaited_by=[ + MockCoroInfo(task_name=2, call_stack=[]), + MockCoroInfo(task_name=3, call_stack=[]), + ] + ) + + leaf_y = MockTaskInfo( + task_id=5, + task_name="LeafY", + coroutine_stack=[MockCoroInfo(task_name="LeafY", call_stack=[MockFrameInfo("y.py", 1, "y")])], + awaited_by=[ + MockCoroInfo(task_name=2, call_stack=[]), + MockCoroInfo(task_name=3, call_stack=[]), + ] + ) + + parent_a = MockTaskInfo( + task_id=2, + task_name="A", + coroutine_stack=[MockCoroInfo(task_name="A", call_stack=[MockFrameInfo("a.py", 1, "a")])], + awaited_by=[MockCoroInfo(task_name=1, call_stack=[])] + ) + + parent_b = MockTaskInfo( + task_id=3, + task_name="B", + coroutine_stack=[MockCoroInfo(task_name="B", call_stack=[MockFrameInfo("b.py", 1, "b")])], + awaited_by=[MockCoroInfo(task_name=1, call_stack=[])] + ) + + root = MockTaskInfo( + task_id=1, + task_name="Root", + coroutine_stack=[MockCoroInfo(task_name="Root", call_stack=[MockFrameInfo("r.py", 1, "root")])], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=444, awaited_by=[leaf_x, leaf_y, parent_a, parent_b, root])] + stacks = list(collector._iter_async_frames(awaited_info_list)) + + # 2 leaves, each picks same parent (A, id=2) = 2 paths + self.assertEqual(len(stacks), 2, "Should create 2 paths: X->A->Root, Y->A->Root") + + # Verify both leaves pick parent A (id=2, first when sorted) + leaf_ids_seen = set() + for frames, _, leaf_id in stacks: + leaf_ids_seen.add(leaf_id) + func_names = [f.funcname for f in frames] + + # Both stacks should go through parent A only + self.assertIn("a", func_names, "Should use parent A (id=2, first when sorted)") + self.assertNotIn("b", func_names, "Should not include parent B") + self.assertIn("root", func_names, "Should reach root") + + # Check for parent count annotation on the leaf + if leaf_id == 4: + self.assertIn("x", func_names) + self.assertIn("LeafX (2 parents)", func_names, "LeafX should be annotated with parent count") + elif leaf_id == 5: + self.assertIn("y", func_names) + self.assertIn("LeafY (2 parents)", func_names, "LeafY should be annotated with parent count") + + # Both leaves should be represented + self.assertEqual(leaf_ids_seen, {4, 5}, "Both LeafX and LeafY should have paths") + + +class TestFlamegraphCollectorAsync(unittest.TestCase): + """Test FlamegraphCollector with async frames.""" + + def test_flamegraph_with_async_frames(self): + """Test FlamegraphCollector correctly processes async task frames.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + # Build async task tree: Root -> Child + child = MockTaskInfo( + task_id=2, + task_name="ChildTask", + coroutine_stack=[ + MockCoroInfo( + task_name="ChildTask", + call_stack=[MockFrameInfo("child.py", 10, "child_work")] + ) + ], + awaited_by=[MockCoroInfo(task_name=1, call_stack=[])] + ) + + root = MockTaskInfo( + task_id=1, + task_name="RootTask", + coroutine_stack=[ + MockCoroInfo( + task_name="RootTask", + call_stack=[MockFrameInfo("root.py", 20, "root_work")] + ) + ], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=100, awaited_by=[child, root])] + + # Collect async frames + collector.collect(awaited_info_list) + + # Verify samples were collected + self.assertGreater(collector._total_samples, 0) + + # Verify the flamegraph tree structure contains our functions + root_node = collector._root + self.assertGreater(root_node["samples"], 0) + + # Check that thread ID was tracked + self.assertIn(100, collector._all_threads) + + def test_flamegraph_with_task_markers(self): + """Test FlamegraphCollector includes <task> boundary markers.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + task = MockTaskInfo( + task_id=42, + task_name="MyTask", + coroutine_stack=[ + MockCoroInfo( + task_name="MyTask", + call_stack=[MockFrameInfo("work.py", 5, "do_work")] + ) + ], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=200, awaited_by=[task])] + collector.collect(awaited_info_list) + + # Find <task> marker in the tree + def find_task_marker(node, depth=0): + for func, child in node.get("children", {}).items(): + if func[0] == "<task>": + return func + result = find_task_marker(child, depth + 1) + if result: + return result + return None + + task_marker = find_task_marker(collector._root) + self.assertIsNotNone(task_marker, "Should have <task> marker in tree") + self.assertEqual(task_marker[0], "<task>") + self.assertIn("MyTask", task_marker[2]) + + def test_flamegraph_multiple_async_samples(self): + """Test FlamegraphCollector aggregates multiple async samples correctly.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + task = MockTaskInfo( + task_id=1, + task_name="Task", + coroutine_stack=[ + MockCoroInfo( + task_name="Task", + call_stack=[MockFrameInfo("work.py", 10, "work")] + ) + ], + awaited_by=[] + ) + + awaited_info_list = [MockAwaitedInfo(thread_id=300, awaited_by=[task])] + + # Collect multiple samples + for _ in range(5): + collector.collect(awaited_info_list) + + # Verify sample count + self.assertEqual(collector._sample_count, 5) + self.assertEqual(collector._total_samples, 5) + + +class TestAsyncAwareParameterFlow(unittest.TestCase): + """Integration tests for async_aware parameter flow from CLI to unwinder.""" + + def test_sample_function_accepts_async_aware(self): + """Test that sample() function accepts async_aware parameter.""" + sig = inspect.signature(sample) + self.assertIn("async_aware", sig.parameters) + + def test_sample_live_function_accepts_async_aware(self): + """Test that sample_live() function accepts async_aware parameter.""" + sig = inspect.signature(sample_live) + self.assertIn("async_aware", sig.parameters) + + def test_sample_profiler_sample_accepts_async_aware(self): + """Test that SampleProfiler.sample() accepts async_aware parameter.""" + sig = inspect.signature(SampleProfiler.sample) + self.assertIn("async_aware", sig.parameters) + + def test_async_aware_all_sees_sleeping_and_running_tasks(self): + """Test async_aware='all' captures both sleeping and CPU-running tasks.""" + # Sleeping task (awaiting) + sleeping_task = MockTaskInfo( + task_id=1, + task_name="SleepingTask", + coroutine_stack=[ + MockCoroInfo( + task_name="SleepingTask", + call_stack=[MockFrameInfo("sleeper.py", 10, "sleep_work")] + ) + ], + awaited_by=[] + ) + + # CPU-running task (active) + running_task = MockTaskInfo( + task_id=2, + task_name="RunningTask", + coroutine_stack=[ + MockCoroInfo( + task_name="RunningTask", + call_stack=[MockFrameInfo("runner.py", 20, "cpu_work")] + ) + ], + awaited_by=[] + ) + + # Both tasks returned by get_all_awaited_by + awaited_info_list = [MockAwaitedInfo(thread_id=100, awaited_by=[sleeping_task, running_task])] + + collector = PstatsCollector(sample_interval_usec=1000) + collector.collect(awaited_info_list) + collector.create_stats() + + # Both tasks should be visible + sleeping_key = ("sleeper.py", 10, "sleep_work") + running_key = ("runner.py", 20, "cpu_work") + + self.assertIn(sleeping_key, collector.stats) + self.assertIn(running_key, collector.stats) + + # Task markers should also be present + task_keys = [k for k in collector.stats if k[0] == "<task>"] + self.assertGreater(len(task_keys), 0, "Should have <task> markers in stats") + + # Verify task names are in the markers + task_names = [k[2] for k in task_keys] + self.assertTrue( + any("SleepingTask" in name for name in task_names), + "SleepingTask should be in task markers" + ) + self.assertTrue( + any("RunningTask" in name for name in task_names), + "RunningTask should be in task markers" + ) + + def test_async_aware_running_sees_only_running_task(self): + """Test async_aware='running' only shows the currently running task stack.""" + # Only the running task's stack is returned by get_async_stack_trace + running_task = MockTaskInfo( + task_id=2, + task_name="RunningTask", + coroutine_stack=[ + MockCoroInfo( + task_name="RunningTask", + call_stack=[MockFrameInfo("runner.py", 20, "cpu_work")] + ) + ], + awaited_by=[] + ) + + # get_async_stack_trace only returns the running task + awaited_info_list = [MockAwaitedInfo(thread_id=100, awaited_by=[running_task])] + + collector = PstatsCollector(sample_interval_usec=1000) + collector.collect(awaited_info_list) + collector.create_stats() + + # Only running task should be visible + running_key = ("runner.py", 20, "cpu_work") + self.assertIn(running_key, collector.stats) + + # Verify we don't see the sleeping task (it wasn't in the input) + sleeping_key = ("sleeper.py", 10, "sleep_work") + self.assertNotIn(sleeping_key, collector.stats) + + # Task marker for running task should be present + task_keys = [k for k in collector.stats if k[0] == "<task>"] + self.assertGreater(len(task_keys), 0, "Should have <task> markers in stats") + + task_names = [k[2] for k in task_keys] + self.assertTrue( + any("RunningTask" in name for name in task_names), + "RunningTask should be in task markers" + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py b/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py new file mode 100644 index 00000000000000..5efc60a9211175 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py @@ -0,0 +1,1516 @@ +"""Tests for binary format round-trip functionality.""" + +import json +import os +import pathlib +import random +import struct +import tempfile +import unittest +from collections import defaultdict + +try: + import _remote_debugging + from _remote_debugging import ( + InterpreterInfo, + ThreadInfo, + FrameInfo, + LocationInfo, + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, + THREAD_STATUS_UNKNOWN, + THREAD_STATUS_GIL_REQUESTED, + THREAD_STATUS_HAS_EXCEPTION, + THREAD_STATUS_MAIN_THREAD, + ) + from profiling.sampling.binary_collector import BinaryCollector + from profiling.sampling.binary_reader import BinaryReader, convert_binary_to_format + from profiling.sampling.gecko_collector import GeckoCollector + + ZSTD_AVAILABLE = _remote_debugging.zstd_available() +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) + +from .helpers import jsonl_tables + + +def make_frame(filename, lineno, funcname, end_lineno=None, column=None, + end_column=None, opcode=None): + """Create a FrameInfo struct sequence with full location info and opcode.""" + if end_lineno is None: + end_lineno = lineno + if column is None: + column = 0 + if end_column is None: + end_column = 0 + location = LocationInfo((lineno, end_lineno, column, end_column)) + return FrameInfo((filename, location, funcname, opcode)) + + +def make_thread(thread_id, frames, status=0): + """Create a ThreadInfo struct sequence.""" + return ThreadInfo((thread_id, status, frames)) + + +def make_interpreter(interp_id, threads): + """Create an InterpreterInfo struct sequence.""" + return InterpreterInfo((interp_id, threads)) + + +def extract_lineno(location): + """Extract line number from location (tuple or int or None).""" + if location is None: + return 0 # Treat None as 0 + if isinstance(location, tuple): + return location[0] if location[0] is not None else 0 + return location + + +def extract_location(location): + """Extract full location info as dict from location tuple or None.""" + if location is None: + return {"lineno": 0, "end_lineno": 0, "column": 0, "end_column": 0} + if isinstance(location, tuple) and len(location) >= 4: + return { + "lineno": location[0] if location[0] is not None else 0, + "end_lineno": location[1] if location[1] is not None else 0, + "column": location[2] if location[2] is not None else 0, + "end_column": location[3] if location[3] is not None else 0, + } + # Fallback for old-style location + lineno = location[0] if isinstance(location, tuple) else location + return {"lineno": lineno or 0, "end_lineno": lineno or 0, "column": 0, "end_column": 0} + + +def frame_to_dict(frame): + """Convert a FrameInfo to a dict.""" + loc = extract_location(frame.location) + return { + "filename": frame.filename, + "funcname": frame.funcname, + "lineno": loc["lineno"], + "end_lineno": loc["end_lineno"], + "column": loc["column"], + "end_column": loc["end_column"], + "opcode": frame.opcode, + } + + +class RawCollector: + """Collector that captures all raw data grouped by thread.""" + + def __init__(self): + # Key: (interpreter_id, thread_id) -> list of samples for that thread + self.by_thread = defaultdict(list) + self.total_count = 0 + + def collect(self, stack_frames, timestamps_us): + """Capture the raw sample data.""" + # timestamps_us is a list; add one sample per timestamp + count = len(timestamps_us) + for interp in stack_frames: + for thread in interp.threads: + frames = [frame_to_dict(f) for f in thread.frame_info] + key = (interp.interpreter_id, thread.thread_id) + sample = {"status": thread.status, "frames": frames} + for _ in range(count): + self.by_thread[key].append(sample) + self.total_count += count + + def export(self, filename): + pass + + +def samples_to_by_thread(samples): + """Convert input samples to by-thread format for comparison.""" + by_thread = defaultdict(list) + for sample in samples: + for interp in sample: + for thread in interp.threads: + frames = [frame_to_dict(f) for f in thread.frame_info] + key = (interp.interpreter_id, thread.thread_id) + by_thread[key].append( + { + "status": thread.status, + "frames": frames, + } + ) + return by_thread + + +class BinaryFormatTestBase(unittest.TestCase): + """Base class with common setup/teardown for binary format tests.""" + + def setUp(self): + self.temp_files = [] + + def tearDown(self): + for f in self.temp_files: + if os.path.exists(f): + os.unlink(f) + + def create_binary_file(self, samples, interval=1000, compression="none"): + """Create a test binary file and track it for cleanup.""" + filename, _ = self.write_binary_file(samples, interval, compression) + return filename + + def write_binary_file(self, samples, interval=1000, compression="none"): + """Like create_binary_file but also returns the writer collector.""" + with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f: + filename = f.name + self.temp_files.append(filename) + + collector = BinaryCollector( + filename, interval, compression=compression + ) + for sample in samples: + collector.collect(sample) + collector.export(None) + return filename, collector + + def roundtrip(self, samples, interval=1000, compression="none"): + """Write samples to binary and read back.""" + filename = self.create_binary_file(samples, interval, compression) + collector = RawCollector() + with BinaryReader(filename) as reader: + count = reader.replay_samples(collector) + return collector, count + + def assert_samples_equal(self, expected_samples, collector): + """Assert that roundtripped samples match input exactly, per-thread.""" + expected = samples_to_by_thread(expected_samples) + + # Same threads present + self.assertEqual( + set(expected.keys()), + set(collector.by_thread.keys()), + "Thread set mismatch", + ) + + # For each thread, samples match in order + for key in expected: + exp_samples = expected[key] + act_samples = collector.by_thread[key] + interp_id, thread_id = key + + self.assertEqual( + len(exp_samples), + len(act_samples), + f"Thread ({interp_id}, {thread_id}): sample count mismatch " + f"(expected {len(exp_samples)}, got {len(act_samples)})", + ) + + for i, (exp, act) in enumerate(zip(exp_samples, act_samples)): + self.assertEqual( + exp["status"], + act["status"], + f"Thread ({interp_id}, {thread_id}), sample {i}: " + f"status mismatch (expected {exp['status']}, got {act['status']})", + ) + + self.assertEqual( + len(exp["frames"]), + len(act["frames"]), + f"Thread ({interp_id}, {thread_id}), sample {i}: " + f"frame count mismatch", + ) + + for j, (exp_frame, act_frame) in enumerate( + zip(exp["frames"], act["frames"]) + ): + for field in ("filename", "funcname", "lineno", "end_lineno", + "column", "end_column", "opcode"): + self.assertEqual( + exp_frame[field], + act_frame[field], + f"Thread ({interp_id}, {thread_id}), sample {i}, " + f"frame {j}: {field} mismatch " + f"(expected {exp_frame[field]!r}, got {act_frame[field]!r})", + ) + + +class TestBinaryRoundTrip(BinaryFormatTestBase): + """Tests for exact binary format round-trip.""" + + def test_single_sample_single_frame(self): + """Single sample with one frame roundtrips exactly.""" + samples = [ + [ + make_interpreter( + 0, + [ + make_thread( + 12345, [make_frame("test.py", 42, "myfunc")] + ) + ], + ) + ] + ] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_single_sample_multi_frame(self): + """Single sample with call stack roundtrips exactly.""" + frames = [ + make_frame("inner.py", 10, "inner"), + make_frame("middle.py", 20, "middle"), + make_frame("outer.py", 30, "outer"), + ] + samples = [[make_interpreter(0, [make_thread(100, frames)])]] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_multiple_samples_same_stack(self): + """Multiple identical samples roundtrip exactly (tests RLE).""" + frame = make_frame("hot.py", 99, "hot_func") + samples = [ + [make_interpreter(0, [make_thread(1, [frame])])] + for _ in range(100) + ] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 100) + self.assert_samples_equal(samples, collector) + + def test_multiple_samples_varying_stacks(self): + """Multiple samples with varying stacks roundtrip exactly.""" + samples = [] + for i in range(20): + depth = i % 5 + 1 + frames = [ + make_frame(f"f{j}.py", j * 10 + i, f"func{j}") + for j in range(depth) + ] + samples.append([make_interpreter(0, [make_thread(1, frames)])]) + collector, count = self.roundtrip(samples) + self.assertEqual(count, 20) + self.assert_samples_equal(samples, collector) + + def test_thread_ids_preserved(self): + """Thread IDs are preserved exactly.""" + thread_ids = [1, 12345, 0x7FFF12345678, 999999] + samples = [] + for tid in thread_ids: + samples.append( + [ + make_interpreter( + 0, [make_thread(tid, [make_frame("t.py", 10, "f")])] + ) + ] + ) + collector, count = self.roundtrip(samples) + self.assertEqual(count, len(thread_ids)) + self.assert_samples_equal(samples, collector) + + def test_interpreter_ids_preserved(self): + """Interpreter IDs are preserved exactly.""" + interp_ids = [0, 1, 5, 100] + samples = [] + for iid in interp_ids: + samples.append( + [ + make_interpreter( + iid, [make_thread(1, [make_frame("i.py", 10, "f")])] + ) + ] + ) + collector, count = self.roundtrip(samples) + self.assertEqual(count, len(interp_ids)) + self.assert_samples_equal(samples, collector) + + def test_status_flags_preserved(self): + """All thread status flags are preserved exactly.""" + statuses = [ + 0, + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, + THREAD_STATUS_UNKNOWN, + THREAD_STATUS_GIL_REQUESTED, + THREAD_STATUS_HAS_EXCEPTION, + THREAD_STATUS_MAIN_THREAD, + THREAD_STATUS_HAS_GIL | THREAD_STATUS_ON_CPU, + THREAD_STATUS_HAS_GIL | THREAD_STATUS_HAS_EXCEPTION, + THREAD_STATUS_HAS_GIL + | THREAD_STATUS_ON_CPU + | THREAD_STATUS_GIL_REQUESTED, + ] + samples = [] + for i, status in enumerate(statuses): + samples.append( + [ + make_interpreter( + 0, + [ + make_thread( + 1, [make_frame("s.py", 10 + i, "f")], status + ) + ], + ) + ] + ) + collector, count = self.roundtrip(samples) + self.assertEqual(count, len(statuses)) + self.assert_samples_equal(samples, collector) + + def test_binary_replay_preserves_main_thread_for_gecko(self): + """Binary replay preserves main thread identity for GeckoCollector.""" + samples = [ + [ + make_interpreter( + 0, + [ + make_thread( + 1, + [make_frame("main.py", 10, "main")], + THREAD_STATUS_MAIN_THREAD, + ), + make_thread(2, [make_frame("worker.py", 20, "worker")]), + ], + ) + ] + ] + filename = self.create_binary_file(samples) + collector = GeckoCollector(1000) + + with BinaryReader(filename) as reader: + count = reader.replay_samples(collector) + + self.assertEqual(count, 2) + profile = collector._build_profile() + threads = {thread["tid"]: thread for thread in profile["threads"]} + self.assertTrue(threads[1]["isMainThread"]) + self.assertFalse(threads[2]["isMainThread"]) + + def test_multiple_threads_per_sample(self): + """Multiple threads in one sample roundtrip exactly.""" + threads = [ + make_thread( + 1, [make_frame("t1.py", 10, "t1")], THREAD_STATUS_HAS_GIL + ), + make_thread( + 2, [make_frame("t2.py", 20, "t2")], THREAD_STATUS_ON_CPU + ), + make_thread(3, [make_frame("t3.py", 30, "t3")], 0), + ] + samples = [[make_interpreter(0, threads)] for _ in range(10)] + collector, count = self.roundtrip(samples) + # 10 samples × 3 threads = 30 thread-samples + self.assertEqual(count, 30) + self.assert_samples_equal(samples, collector) + + def test_multiple_interpreters_per_sample(self): + """Multiple interpreters in one sample roundtrip exactly.""" + samples = [ + [ + make_interpreter( + 0, [make_thread(1, [make_frame("i0.py", 10, "i0")])] + ), + make_interpreter( + 1, [make_thread(2, [make_frame("i1.py", 20, "i1")])] + ), + ] + for _ in range(5) + ] + collector, count = self.roundtrip(samples) + # 5 samples × 2 interpreters × 1 thread = 10 thread-samples + self.assertEqual(count, 10) + self.assert_samples_equal(samples, collector) + + def test_same_thread_id_different_interpreters(self): + """Same thread_id in different interpreters must be tracked separately.""" + # This test catches bugs where thread state is keyed only by thread_id + # without considering interpreter_id + samples = [] + # Interleave samples from interpreter 0 and 1, both using thread_id=1 + for i in range(20): + interp_id = i % 2 # Alternate between interpreter 0 and 1 + frame = make_frame( + f"interp{interp_id}.py", 10 + i, f"func{interp_id}" + ) + samples.append( + [make_interpreter(interp_id, [make_thread(1, [frame])])] + ) + + collector, count = self.roundtrip(samples) + self.assertEqual(count, 20) + self.assert_samples_equal(samples, collector) + + # Verify both interpreters are present + keys = set(collector.by_thread.keys()) + self.assertIn((0, 1), keys) # interpreter 0, thread 1 + self.assertIn((1, 1), keys) # interpreter 1, thread 1 + + # Verify each interpreter got 10 samples + self.assertEqual(len(collector.by_thread[(0, 1)]), 10) + self.assertEqual(len(collector.by_thread[(1, 1)]), 10) + + # Verify the samples are in the right order for each interpreter + for i, sample in enumerate(collector.by_thread[(0, 1)]): + expected_lineno = 10 + i * 2 # 10, 12, 14, ... + self.assertEqual(sample["frames"][0]["lineno"], expected_lineno) + self.assertEqual(sample["frames"][0]["filename"], "interp0.py") + + for i, sample in enumerate(collector.by_thread[(1, 1)]): + expected_lineno = 11 + i * 2 # 11, 13, 15, ... + self.assertEqual(sample["frames"][0]["lineno"], expected_lineno) + self.assertEqual(sample["frames"][0]["filename"], "interp1.py") + + def test_deep_call_stack(self): + """Deep call stack roundtrips exactly.""" + depth = 100 + frames = [ + make_frame(f"f{i}.py", i + 1, f"func{i}") for i in range(depth) + ] + samples = [[make_interpreter(0, [make_thread(1, frames)])]] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_line_numbers_preserved(self): + """Various line numbers are preserved exactly.""" + linenos = [1, 100, 1000, 65535, 100000] + samples = [] + for lineno in linenos: + samples.append( + [ + make_interpreter( + 0, [make_thread(1, [make_frame("l.py", lineno, "f")])] + ) + ] + ) + collector, count = self.roundtrip(samples) + self.assertEqual(count, len(linenos)) + self.assert_samples_equal(samples, collector) + + @unittest.skipUnless(ZSTD_AVAILABLE, "zstd compression not available") + def test_zstd_compression_roundtrip(self): + """Zstd compressed data roundtrips exactly.""" + samples = [] + for i in range(200): + frames = [ + make_frame(f"z{j}.py", j * 10 + i + 1, f"zfunc{j}") + for j in range(3) + ] + samples.append([make_interpreter(0, [make_thread(1, frames)])]) + collector, count = self.roundtrip(samples, compression="zstd") + self.assertEqual(count, 200) + self.assert_samples_equal(samples, collector) + + def test_sample_interval_preserved(self): + """Sample interval is preserved in file metadata.""" + intervals = [100, 500, 1000, 5000, 10000] + for interval in intervals: + with self.subTest(interval=interval): + samples = [ + [ + make_interpreter( + 0, [make_thread(1, [make_frame("i.py", 1, "f")])] + ) + ] + ] + filename = self.create_binary_file(samples, interval=interval) + with BinaryReader(filename) as reader: + info = reader.get_info() + self.assertEqual(info["sample_interval_us"], interval) + + def test_threads_interleaved_samples(self): + """Multiple threads with interleaved varying samples.""" + samples = [] + for i in range(30): + threads = [ + make_thread( + 1, + [make_frame("t1.py", 10 + i, "t1")], + THREAD_STATUS_HAS_GIL if i % 2 == 0 else 0, + ), + make_thread( + 2, + [make_frame("t2.py", 20 + i, "t2")], + THREAD_STATUS_ON_CPU if i % 3 == 0 else 0, + ), + ] + samples.append([make_interpreter(0, threads)]) + collector, count = self.roundtrip(samples) + self.assertEqual(count, 60) + self.assert_samples_equal(samples, collector) + + def test_full_location_roundtrip(self): + """Full source location (end_lineno, column, end_column) roundtrips.""" + frames = [ + make_frame("test.py", 10, "func1", end_lineno=12, column=4, end_column=20), + make_frame("test.py", 20, "func2", end_lineno=20, column=8, end_column=45), + make_frame("test.py", 30, "func3", end_lineno=35, column=0, end_column=100), + ] + samples = [[make_interpreter(0, [make_thread(1, frames)])]] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_opcode_roundtrip(self): + """Opcode values roundtrip exactly.""" + opcodes = [0, 1, 50, 100, 150, 200, 254] # Valid Python opcodes + samples = [] + for opcode in opcodes: + frame = make_frame("test.py", 10, "func", opcode=opcode) + samples.append([make_interpreter(0, [make_thread(1, [frame])])]) + collector, count = self.roundtrip(samples) + self.assertEqual(count, len(opcodes)) + self.assert_samples_equal(samples, collector) + + def test_opcode_none_roundtrip(self): + """Opcode=None (sentinel 255) roundtrips as None.""" + frame = make_frame("test.py", 10, "func", opcode=None) + samples = [[make_interpreter(0, [make_thread(1, [frame])])]] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_mixed_location_and_opcode(self): + """Mixed full location and opcode data roundtrips.""" + frames = [ + make_frame("a.py", 10, "a", end_lineno=15, column=4, end_column=30, opcode=100), + make_frame("b.py", 20, "b", end_lineno=20, column=0, end_column=50, opcode=None), + make_frame("c.py", 30, "c", end_lineno=32, column=8, end_column=25, opcode=50), + ] + samples = [[make_interpreter(0, [make_thread(1, frames)])]] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_delta_encoding_multiline(self): + """Multi-line spans (large end_lineno delta) roundtrip correctly.""" + # This tests the delta encoding: end_lineno = lineno + delta + frames = [ + make_frame("test.py", 1, "small", end_lineno=1, column=0, end_column=10), + make_frame("test.py", 100, "medium", end_lineno=110, column=0, end_column=50), + make_frame("test.py", 1000, "large", end_lineno=1500, column=0, end_column=200), + ] + samples = [[make_interpreter(0, [make_thread(1, frames)])]] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_column_positions_preserved(self): + """Various column positions are preserved exactly.""" + columns = [(0, 10), (4, 50), (8, 100), (100, 200)] + samples = [] + for col, end_col in columns: + frame = make_frame("test.py", 10, "func", column=col, end_column=end_col) + samples.append([make_interpreter(0, [make_thread(1, [frame])])]) + collector, count = self.roundtrip(samples) + self.assertEqual(count, len(columns)) + self.assert_samples_equal(samples, collector) + + def test_same_line_different_opcodes(self): + """Same line with different opcodes creates distinct frames.""" + # This tests that opcode is part of the frame key + frames = [ + make_frame("test.py", 10, "func", opcode=100), + make_frame("test.py", 10, "func", opcode=101), + make_frame("test.py", 10, "func", opcode=102), + ] + samples = [[make_interpreter(0, [make_thread(1, [f])]) for f in frames]] + collector, count = self.roundtrip(samples) + # Verify all three opcodes are preserved distinctly + self.assertEqual(count, 3) + + def test_same_line_different_columns(self): + """Same line with different columns creates distinct frames.""" + frames = [ + make_frame("test.py", 10, "func", column=0, end_column=10), + make_frame("test.py", 10, "func", column=15, end_column=25), + make_frame("test.py", 10, "func", column=30, end_column=40), + ] + samples = [[make_interpreter(0, [make_thread(1, [f])]) for f in frames]] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 3) + + +class TestBinaryEdgeCases(BinaryFormatTestBase): + """Tests for edge cases in binary format.""" + + def test_unicode_filenames(self): + """Unicode filenames roundtrip exactly.""" + filenames = [ + "/путь/файл.py", + "/路径/文件.py", + "/パス/ファイル.py", + "/chemin/café.py", + ] + for fname in filenames: + with self.subTest(filename=fname): + samples = [ + [ + make_interpreter( + 0, [make_thread(1, [make_frame(fname, 1, "func")])] + ) + ] + ] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_unicode_funcnames(self): + """Unicode function names roundtrip exactly.""" + funcnames = [ + "функция", + "函数", + "関数", + "función", + ] + for funcname in funcnames: + with self.subTest(funcname=funcname): + samples = [ + [ + make_interpreter( + 0, + [ + make_thread( + 1, [make_frame("test.py", 1, funcname)] + ) + ], + ) + ] + ] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_special_char_filenames(self): + """Filenames with special characters roundtrip exactly.""" + filenames = [ + "/path/with spaces/file.py", + "/path/with\ttab/file.py", + "/path/with'quote/file.py", + '/path/with"double/file.py', + "/path/with\\backslash/file.py", + ] + for fname in filenames: + with self.subTest(filename=fname): + samples = [ + [ + make_interpreter( + 0, [make_thread(1, [make_frame(fname, 1, "func")])] + ) + ] + ] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_special_funcnames(self): + """Function names with special characters roundtrip exactly.""" + funcnames = [ + "<lambda>", + "<listcomp>", + "<genexpr>", + "<module>", + "__init__", + "func.inner", + ] + for funcname in funcnames: + with self.subTest(funcname=funcname): + samples = [ + [ + make_interpreter( + 0, + [ + make_thread( + 1, [make_frame("test.py", 1, funcname)] + ) + ], + ) + ] + ] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_long_filename(self): + """Long filename roundtrips exactly.""" + long_file = "/very/long/path/" + "sub/" * 50 + "file.py" + samples = [ + [ + make_interpreter( + 0, [make_thread(1, [make_frame(long_file, 1, "func")])] + ) + ] + ] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_long_funcname(self): + """Long function name roundtrips exactly.""" + long_func = "very_long_function_name_" + "x" * 200 + samples = [ + [ + make_interpreter( + 0, [make_thread(1, [make_frame("test.py", 1, long_func)])] + ) + ] + ] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_empty_funcname(self): + """Empty function name roundtrips exactly.""" + samples = [ + [ + make_interpreter( + 0, [make_thread(1, [make_frame("test.py", 1, "")])] + ) + ] + ] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + @unittest.skipUnless(ZSTD_AVAILABLE, "zstd compression not available") + def test_large_sample_count(self): + """Large number of samples roundtrips exactly.""" + num = 5000 + samples = [ + [ + make_interpreter( + 0, + [ + make_thread( + 1, [make_frame("test.py", (i % 100) + 1, "func")] + ) + ], + ) + ] + for i in range(num) + ] + collector, count = self.roundtrip(samples, compression="zstd") + self.assertEqual(count, num) + self.assert_samples_equal(samples, collector) + + def test_context_manager_cleanup(self): + """Reader cleans up on context exit.""" + samples = [ + [ + make_interpreter( + 0, [make_thread(1, [make_frame("t.py", 1, "f")])] + ) + ] + ] + filename = self.create_binary_file(samples) + reader = BinaryReader(filename) + with reader: + collector = RawCollector() + count = reader.replay_samples(collector) + self.assertEqual(count, 1) + with self.assertRaises(RuntimeError): + reader.replay_samples(collector) + + def test_invalid_file_path(self): + """Invalid file path raises appropriate error.""" + with self.assertRaises((FileNotFoundError, OSError, ValueError)): + with BinaryReader("/nonexistent/path/file.bin") as reader: + reader.replay_samples(RawCollector()) + + def test_path_arguments_round_trip(self): + """Reader and writer accept str, bytes or os.PathLike.""" + with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f: + filename = f.name + self.temp_files.append(filename) + + for path_arg in (filename, os.fsencode(filename), pathlib.Path(filename)): + with self.subTest(path_type=type(path_arg).__name__): + writer = _remote_debugging.BinaryWriter(path_arg, 1000, 0) + writer.finalize() + reader = _remote_debugging.BinaryReader(path_arg) + info = reader.get_info() + reader.close() + self.assertEqual(info["sample_count"], 0) + + def test_rejects_non_pathlike(self): + """Reader and writer raise TypeError on non-path-like filenames.""" + with self.assertRaises(TypeError): + _remote_debugging.BinaryWriter(123, 1000, 0) + with self.assertRaises(TypeError): + _remote_debugging.BinaryReader(123) + + def test_invalid_path_error_preserves_pathlib(self): + """Missing path: OSError carries the original path object, not a string.""" + missing = pathlib.Path("/i/do/not/exist") + with self.assertRaises(FileNotFoundError) as cm: + _remote_debugging.BinaryReader(missing) + self.assertEqual(os.fspath(cm.exception.filename), os.fspath(missing)) + + def test_writer_handles_empty_stack_first_sample(self): + """BinaryWriter.write_sample tolerates an empty stack on a fresh thread. + + Regression test for the C-level RLE bug in process_thread_sample: a + freshly-created ThreadEntry has prev_stack_depth == 0, so an empty + curr_stack compares as STACK_REPEAT against the zero-initialized + previous stack. Before the fix, this fell through the + `&& !is_new_thread` guard into write_sample_with_encoding, which had + no handler for STACK_REPEAT and raised + RuntimeError("Invalid stack encoding type"). Goes through + BinaryWriter.write_sample directly so the test cannot be masked by + any Python-level filtering. + """ + with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f: + filename = f.name + self.temp_files.append(filename) + + writer = _remote_debugging.BinaryWriter(filename, 1000, 0, compression=0) + empty_sample = [ + make_interpreter( + 0, [make_thread(99, [], status=THREAD_STATUS_UNKNOWN)] + ) + ] + # First sample for a fresh thread has empty frame_info — the exact + # scenario that exposes the bug. + writer.write_sample(empty_sample, 1000) + writer.write_sample(empty_sample, 2000) + # Mix in a real sample to exercise the transition out of the + # empty-stack RLE buffer. + real_sample = [ + make_interpreter(0, [make_thread(1, [make_frame("a.py", 1, "f")])]) + ] + writer.write_sample(real_sample, 3000) + writer.finalize() + + reader_collector = RawCollector() + with BinaryReader(filename) as reader: + count = reader.replay_samples(reader_collector) + # Empty-stack samples are recorded as STACK_REPEAT records with + # depth-0 stacks; the file must replay all three samples. + self.assertEqual(count, 3) + + def test_writer_handles_mixed_empty_and_real_first_sample(self): + """First sample with one empty + one real thread roundtrips through C.""" + with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f: + filename = f.name + self.temp_files.append(filename) + + writer = _remote_debugging.BinaryWriter(filename, 1000, 0, compression=0) + sample = [ + make_interpreter( + 0, + [ + make_thread(1, [make_frame("a.py", 1, "f")]), + make_thread(99, [], status=THREAD_STATUS_UNKNOWN), + ], + ) + ] + # Two samples so RLE state is exercised. + writer.write_sample(sample, 1000) + writer.write_sample(sample, 2000) + writer.finalize() + + # Replay must succeed without raising RuntimeError, and the real + # thread's frames must round-trip. + reader_collector = RawCollector() + with BinaryReader(filename) as reader: + reader.replay_samples(reader_collector) + self.assertIn((0, 1), reader_collector.by_thread) + self.assertEqual(len(reader_collector.by_thread[(0, 1)]), 2) + + def test_writer_total_samples_after_finalize_matches_reader(self): + """BinaryWriter.total_samples after finalize() matches the reader's count.""" + # Five IDENTICAL samples force every sample beyond the first into the + # per-thread RLE buffer. Regression for the cached_total_samples + # ordering bug: capturing the cache BEFORE binary_writer_finalize() + # missed the buffered samples that flush_pending_rle() counts. Keep + # the samples identical to preserve coverage. See gh-149342. + samples = [ + [make_interpreter(0, [make_thread(1, [make_frame("a.py", 1, "f")])])] + ] * 5 + filename, writer_collector = self.write_binary_file(samples) + reader_collector = RawCollector() + with BinaryReader(filename) as reader: + replayed = reader.replay_samples(reader_collector) + self.assertEqual(writer_collector.total_samples, len(samples)) + self.assertEqual(writer_collector.total_samples, replayed) + + def test_writer_total_samples_after_context_manager_matches_reader(self): + """total_samples after `with BinaryWriter(...)` matches the reader's count. + + Regression for the asymmetry between finalize() and __exit__ in + module.c: __exit__ also calls binary_writer_finalize and must + preserve cached_total_samples like finalize() does, otherwise the + getter returns 0 once self->writer is NULL. + """ + with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f: + filename = f.name + self.temp_files.append(filename) + + sample = [ + make_interpreter(0, [make_thread(1, [make_frame("a.py", 1, "f")])]) + ] + with _remote_debugging.BinaryWriter(filename, 1000, 0, compression=0) as w: + for i in range(5): + w.write_sample(sample, i * 1000) + self.assertEqual(w.total_samples, 5) + + reader_collector = RawCollector() + with BinaryReader(filename) as reader: + self.assertEqual(reader.replay_samples(reader_collector), 5) + + def test_writer_total_samples_after_close_returns_zero(self): + """close() discards data; total_samples reflects no cached count.""" + with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f: + filename = f.name + self.temp_files.append(filename) + + w = _remote_debugging.BinaryWriter(filename, 1000, 0, compression=0) + sample = [ + make_interpreter(0, [make_thread(1, [make_frame("a.py", 1, "f")])]) + ] + for i in range(5): + w.write_sample(sample, i * 1000) + w.close() + self.assertEqual(w.total_samples, 0) + + +class TestBinaryFormatValidation(BinaryFormatTestBase): + """Tests for malformed binary files.""" + + HDR_OFF_SAMPLES = 28 + HDR_OFF_THREADS = 32 + HDR_OFF_STR_TABLE = 36 + HDR_OFF_FRAME_TABLE = 44 + FILE_HEADER_PLACEHOLDER_SIZE = 64 + + def test_replay_rejects_more_threads_than_declared(self): + """Replay rejects files with more unique threads than the header declares.""" + threads = [ + make_thread(1, [make_frame("t1.py", 10, "t1")]), + make_thread(2, [make_frame("t2.py", 20, "t2")]), + ] + samples = [[make_interpreter(0, threads)]] + filename = self.create_binary_file(samples, compression="none") + + with open(filename, "r+b") as raw: + raw.seek(self.HDR_OFF_THREADS) + raw.write(struct.pack("=I", 1)) + + with BinaryReader(filename) as reader: + self.assertEqual(reader.get_info()["thread_count"], 1) + with self.assertRaises(ValueError) as cm: + reader.replay_samples(RawCollector()) + self.assertEqual( + str(cm.exception), + "Invalid thread count: sample data contains more unique " + "threads than declared in header (declared 1, found at least 2)", + ) + + def test_replay_rejects_sample_count_mismatch(self): + """Replay rejects files whose decoded samples disagree with the header.""" + samples = [[make_interpreter(0, [ + make_thread(1, [make_frame("sample.py", 10, "sample")]) + ])]] + filename = self.create_binary_file(samples, compression="none") + + with open(filename, "r+b") as raw: + raw.seek(self.HDR_OFF_SAMPLES) + raw.write(struct.pack("=I", 2)) + + with BinaryReader(filename) as reader: + self.assertEqual(reader.get_info()["sample_count"], 2) + with self.assertRaises(ValueError) as cm: + reader.replay_samples(RawCollector()) + self.assertEqual( + str(cm.exception), + "Sample count mismatch: header declares 2 samples " + "but replay decoded 1", + ) + + def test_replay_rejects_trailing_partial_sample_header(self): + """Replay rejects partial sample bytes instead of silently stopping.""" + filename = self.create_binary_file([], compression="none") + sample_data_end = self.FILE_HEADER_PLACEHOLDER_SIZE + 1 + + with open(filename, "r+b") as raw: + raw.seek(self.HDR_OFF_STR_TABLE) + raw.write(struct.pack("=Q", sample_data_end)) + raw.seek(self.HDR_OFF_FRAME_TABLE) + raw.write(struct.pack("=Q", sample_data_end)) + + with BinaryReader(filename) as reader: + with self.assertRaises(ValueError) as cm: + reader.replay_samples(RawCollector()) + self.assertEqual(str(cm.exception), "Truncated sample data: 1 trailing bytes") + + +class TestBinaryEncodings(BinaryFormatTestBase): + """Tests specifically targeting different stack encodings.""" + + def test_stack_full_encoding(self): + """First sample uses STACK_FULL encoding and roundtrips.""" + frames = [make_frame(f"f{i}.py", i + 1, f"func{i}") for i in range(5)] + samples = [[make_interpreter(0, [make_thread(1, frames)])]] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1) + self.assert_samples_equal(samples, collector) + + def test_stack_repeat_encoding(self): + """Identical consecutive samples use RLE and roundtrip.""" + frame = make_frame("repeat.py", 42, "repeat_func") + samples = [ + [make_interpreter(0, [make_thread(1, [frame])])] + for _ in range(1000) + ] + collector, count = self.roundtrip(samples) + self.assertEqual(count, 1000) + self.assert_samples_equal(samples, collector) + + def test_stack_suffix_encoding(self): + """Samples sharing suffix use STACK_SUFFIX and roundtrip.""" + samples = [] + for i in range(10): + frames = [make_frame(f"new{i}.py", i + 1, f"new{i}")] + frames.extend( + [ + make_frame(f"shared{j}.py", j + 1, f"shared{j}") + for j in range(5) + ] + ) + samples.append([make_interpreter(0, [make_thread(1, frames)])]) + collector, count = self.roundtrip(samples) + self.assertEqual(count, 10) + self.assert_samples_equal(samples, collector) + + def test_stack_pop_push_encoding(self): + """Samples with pop+push pattern roundtrip.""" + samples = [] + base_frames = [make_frame("base.py", 10, "base")] + + # Call deeper + samples.append([make_interpreter(0, [make_thread(1, base_frames)])]) + samples.append( + [ + make_interpreter( + 0, + [ + make_thread( + 1, + [make_frame("call1.py", 20, "call1")] + + base_frames, + ) + ], + ) + ] + ) + samples.append( + [ + make_interpreter( + 0, + [ + make_thread( + 1, + [ + make_frame("call2.py", 30, "call2"), + make_frame("call1.py", 20, "call1"), + ] + + base_frames, + ) + ], + ) + ] + ) + # Return + samples.append( + [ + make_interpreter( + 0, + [ + make_thread( + 1, + [make_frame("call1.py", 25, "call1")] + + base_frames, + ) + ], + ) + ] + ) + samples.append([make_interpreter(0, [make_thread(1, base_frames)])]) + + collector, count = self.roundtrip(samples) + self.assertEqual(count, 5) + self.assert_samples_equal(samples, collector) + + def test_mixed_encodings(self): + """Mix of different encoding patterns roundtrips.""" + samples = [] + # Some repeated samples (RLE) + frame1 = make_frame("hot.py", 1, "hot") + for _ in range(20): + samples.append([make_interpreter(0, [make_thread(1, [frame1])])]) + # Some varying samples + for i in range(20): + frames = [make_frame(f"vary{i}.py", i + 1, f"vary{i}")] + samples.append([make_interpreter(0, [make_thread(1, frames)])]) + # More repeated + for _ in range(20): + samples.append([make_interpreter(0, [make_thread(1, [frame1])])]) + + collector, count = self.roundtrip(samples) + self.assertEqual(count, 60) + self.assert_samples_equal(samples, collector) + + def test_alternating_threads_status_changes(self): + """Alternating thread status changes roundtrip correctly.""" + samples = [] + for i in range(50): + status1 = THREAD_STATUS_HAS_GIL if i % 2 == 0 else 0 + status2 = ( + THREAD_STATUS_ON_CPU if i % 3 == 0 else THREAD_STATUS_HAS_GIL + ) + threads = [ + make_thread(1, [make_frame("t1.py", 10, "t1")], status1), + make_thread(2, [make_frame("t2.py", 20, "t2")], status2), + ] + samples.append([make_interpreter(0, threads)]) + collector, count = self.roundtrip(samples) + self.assertEqual(count, 100) + self.assert_samples_equal(samples, collector) + + +class TestBinaryStress(BinaryFormatTestBase): + """Randomized stress tests for binary format.""" + + @unittest.skipUnless(ZSTD_AVAILABLE, "zstd compression not available") + def test_random_samples_stress(self): + """Stress test with random samples - exercises hash table resizing.""" + random.seed(42) # Reproducible + + # Large pools to force hash table resizing (exceeds initial 8192/4096 sizes) + filenames = [f"file{i}.py" for i in range(200)] + funcnames = [f"func{i}" for i in range(300)] + thread_ids = list(range(1, 50)) + interp_ids = list(range(10)) + statuses = [ + 0, + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, + THREAD_STATUS_HAS_GIL | THREAD_STATUS_ON_CPU, + THREAD_STATUS_HAS_EXCEPTION, + ] + + samples = [] + for _ in range(1000): + num_interps = random.randint(1, 3) + interps = [] + for _ in range(num_interps): + iid = random.choice(interp_ids) + num_threads = random.randint(1, 5) + threads = [] + for _ in range(num_threads): + tid = random.choice(thread_ids) + status = random.choice(statuses) + depth = random.randint(1, 15) + frames = [] + for _ in range(depth): + fname = random.choice(filenames) + func = random.choice(funcnames) + # Wide line number range to create many unique frames + lineno = random.randint(1, 5000) + frames.append(make_frame(fname, lineno, func)) + threads.append(make_thread(tid, frames, status)) + interps.append(make_interpreter(iid, threads)) + samples.append(interps) + + collector, count = self.roundtrip(samples, compression="zstd") + self.assertGreater(count, 0) + self.assert_samples_equal(samples, collector) + + def test_rle_stress(self): + """Stress test RLE encoding with identical samples.""" + random.seed(123) + + # Create a few distinct stacks + stacks = [] + for i in range(5): + depth = random.randint(1, 8) + frames = [ + make_frame(f"rle{j}.py", j * 10, f"rle{j}") + for j in range(depth) + ] + stacks.append(frames) + + # Generate samples with repeated stacks (should trigger RLE) + samples = [] + for _ in range(100): + stack = random.choice(stacks) + repeat = random.randint(1, 50) + for _ in range(repeat): + samples.append([make_interpreter(0, [make_thread(1, stack)])]) + + collector, count = self.roundtrip(samples) + self.assertEqual(count, len(samples)) + self.assert_samples_equal(samples, collector) + + @unittest.skipUnless(ZSTD_AVAILABLE, "zstd compression not available") + def test_multi_thread_stress(self): + """Stress test with many threads and interleaved samples.""" + random.seed(456) + + thread_ids = list(range(1, 20)) + samples = [] + + for i in range(300): + # Randomly select 1-5 threads for this sample + num_threads = random.randint(1, 5) + selected = random.sample(thread_ids, num_threads) + threads = [] + for tid in selected: + status = random.choice( + [0, THREAD_STATUS_HAS_GIL, THREAD_STATUS_ON_CPU] + ) + depth = random.randint(1, 5) + frames = [ + make_frame(f"mt{tid}_{j}.py", i + j, f"f{j}") + for j in range(depth) + ] + threads.append(make_thread(tid, frames, status)) + samples.append([make_interpreter(0, threads)]) + + collector, count = self.roundtrip(samples, compression="zstd") + self.assertGreater(count, 0) + self.assert_samples_equal(samples, collector) + + def test_encoding_transitions_stress(self): + """Stress test stack encoding transitions.""" + random.seed(789) + + base_frames = [ + make_frame(f"base{i}.py", i, f"base{i}") for i in range(5) + ] + samples = [] + + for i in range(200): + choice = random.randint(0, 4) + if choice == 0: + # Full new stack + depth = random.randint(1, 8) + frames = [ + make_frame(f"new{i}_{j}.py", j, f"new{j}") + for j in range(depth) + ] + elif choice == 1: + # Repeat previous (will use RLE if identical) + frames = base_frames[: random.randint(1, 5)] + elif choice == 2: + # Add frames on top (suffix encoding) + extra = random.randint(1, 3) + frames = [ + make_frame(f"top{i}_{j}.py", j, f"top{j}") + for j in range(extra) + ] + frames.extend(base_frames[: random.randint(2, 4)]) + else: + # Pop and push (pop-push encoding) + keep = random.randint(1, 3) + push = random.randint(0, 2) + frames = [ + make_frame(f"push{i}_{j}.py", j, f"push{j}") + for j in range(push) + ] + frames.extend(base_frames[:keep]) + + samples.append([make_interpreter(0, [make_thread(1, frames)])]) + + collector, count = self.roundtrip(samples) + self.assertEqual(count, len(samples)) + self.assert_samples_equal(samples, collector) + + @unittest.skipUnless(ZSTD_AVAILABLE, "zstd compression not available") + def test_same_thread_id_multiple_interpreters_stress(self): + """Stress test: same thread_id across multiple interpreters with interleaved samples. + + This test catches bugs where thread state is keyed only by thread_id + without considering interpreter_id (both in writer and reader). + """ + random.seed(999) + + # Multiple interpreters, each with overlapping thread_ids + interp_ids = [0, 1, 2, 3] + # Same thread_ids used across all interpreters + shared_thread_ids = [1, 2, 3] + + filenames = [f"file{i}.py" for i in range(10)] + funcnames = [f"func{i}" for i in range(15)] + statuses = [0, THREAD_STATUS_HAS_GIL, THREAD_STATUS_ON_CPU] + + samples = [] + for i in range(1000): + # Randomly pick an interpreter + iid = random.choice(interp_ids) + # Randomly pick 1-3 threads (from shared pool) + num_threads = random.randint(1, 3) + selected_tids = random.sample(shared_thread_ids, num_threads) + + threads = [] + for tid in selected_tids: + status = random.choice(statuses) + depth = random.randint(1, 6) + frames = [] + for d in range(depth): + # Include interpreter and thread info in frame data for verification + fname = f"i{iid}_t{tid}_{random.choice(filenames)}" + func = random.choice(funcnames) + lineno = i * 10 + d + 1 # Unique per sample + frames.append(make_frame(fname, lineno, func)) + threads.append(make_thread(tid, frames, status)) + + samples.append([make_interpreter(iid, threads)]) + + collector, count = self.roundtrip(samples, compression="zstd") + self.assertGreater(count, 0) + self.assert_samples_equal(samples, collector) + + # Verify that we have samples from multiple (interpreter, thread) combinations + # with the same thread_id + keys = set(collector.by_thread.keys()) + # Should have samples for same thread_id in different interpreters + for tid in shared_thread_ids: + interps_with_tid = [iid for (iid, t) in keys if t == tid] + self.assertGreater( + len(interps_with_tid), + 1, + f"Thread {tid} should appear in multiple interpreters", + ) + + +class TimestampCollector: + """Collector that captures timestamps for verification.""" + + def __init__(self): + self.all_timestamps = [] + + def collect(self, stack_frames, timestamps_us=None): + if timestamps_us is not None: + self.all_timestamps.extend(timestamps_us) + + def export(self, filename): + pass + + +class TestTimestampPreservation(BinaryFormatTestBase): + """Tests for timestamp preservation during binary round-trip.""" + + def test_timestamp_preservation(self): + """Timestamps are preserved during round-trip.""" + frame = make_frame("test.py", 10, "func") + timestamps = [1000000, 2000000, 3000000] + + with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f: + filename = f.name + self.temp_files.append(filename) + + collector = BinaryCollector(filename, 1000, compression="none") + for ts in timestamps: + sample = [make_interpreter(0, [make_thread(1, [frame])])] + collector.collect(sample, timestamp_us=ts) + collector.export(None) + + ts_collector = TimestampCollector() + with BinaryReader(filename) as reader: + count = reader.replay_samples(ts_collector) + + self.assertEqual(count, 3) + self.assertEqual(ts_collector.all_timestamps, timestamps) + + def test_timestamp_preservation_with_rle(self): + """RLE-batched samples preserve individual timestamps.""" + frame = make_frame("rle.py", 42, "rle_func") + + with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f: + filename = f.name + self.temp_files.append(filename) + + # Identical samples (triggers RLE) with different timestamps + collector = BinaryCollector(filename, 1000, compression="none") + expected_timestamps = [] + for i in range(50): + ts = 1000000 + i * 100 + expected_timestamps.append(ts) + sample = [make_interpreter(0, [make_thread(1, [frame])])] + collector.collect(sample, timestamp_us=ts) + collector.export(None) + + ts_collector = TimestampCollector() + with BinaryReader(filename) as reader: + count = reader.replay_samples(ts_collector) + + self.assertEqual(count, 50) + self.assertEqual(ts_collector.all_timestamps, expected_timestamps) + + +class TestBinaryReplayToJsonl(BinaryFormatTestBase): + """Tests for binary -> JSONL replay via convert_binary_to_format.""" + + def _replay_to_jsonl(self, samples, interval=1000): + bin_path = self.create_binary_file(samples, interval=interval) + with tempfile.NamedTemporaryFile(suffix=".jsonl", delete=False) as f: + jsonl_path = f.name + self.temp_files.append(jsonl_path) + + convert_binary_to_format(bin_path, jsonl_path, "jsonl") + + with open(jsonl_path, "r", encoding="utf-8") as f: + return [json.loads(line) for line in f] + + def test_binary_replay_to_jsonl_basic(self): + """Replay a small .bin to JSONL: meta/end shape, samples_total, run_id.""" + frame = make_frame("hot.py", 99, "hot_func") + samples = [ + [make_interpreter(0, [make_thread(1, [frame])])] + for _ in range(5) + ] + records = self._replay_to_jsonl(samples, interval=2000) + meta, _, frame_defs, _, end = jsonl_tables(records) + + self.assertEqual(meta["sample_interval_usec"], 2000) + self.assertEqual(end["samples_total"], 5) + + run_ids = {r["run_id"] for r in records} + self.assertEqual(len(run_ids), 1) + self.assertRegex(next(iter(run_ids)), r"^[0-9a-f]{32}$") + + self.assertEqual(len(frame_defs), 1) + self.assertEqual(frame_defs[0]["line"], 99) + + def test_binary_replay_to_jsonl_rle_weight_propagation(self): + """RLE-batched identical samples land as a single agg entry with the right total.""" + frame = make_frame("rle.py", 42, "rle_func") + samples = [ + [make_interpreter(0, [make_thread(1, [frame])])] + for _ in range(50) + ] + records = self._replay_to_jsonl(samples) + _, _, _, agg, end = jsonl_tables(records) + + self.assertEqual(end["samples_total"], 50) + self.assertEqual(agg["entries"], [ + {"frame_id": 0, "self": 50, "cumulative": 50}, + ]) + + def test_binary_replay_to_jsonl_omits_unavailable_columns(self): + """Columns the binary recorder did not capture are omitted, not 0.""" + # make_frame defaults column/end_column to 0; pass column=-1 / end_column=-1 + # so the binary side records LOCATION_NOT_AVAILABLE. + frame = make_frame("nocol.py", 7, "no_col", column=-1, end_column=-1) + samples = [[make_interpreter(0, [make_thread(1, [frame])])]] + records = self._replay_to_jsonl(samples) + _, _, frame_defs, _, _ = jsonl_tables(records) + + self.assertEqual(len(frame_defs), 1) + fd = frame_defs[0] + self.assertEqual(fd["line"], 7) + self.assertNotIn("col", fd) + self.assertNotIn("end_col", fd) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_blocking.py b/Lib/test/test_profiling/test_sampling_profiler/test_blocking.py new file mode 100644 index 00000000000000..1f4b6da3281056 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_blocking.py @@ -0,0 +1,160 @@ +"""Tests for blocking mode sampling profiler.""" + +import io +import textwrap +import unittest +from unittest import mock + +try: + import _remote_debugging # noqa: F401 + import profiling.sampling + import profiling.sampling.sample + from profiling.sampling.stack_collector import CollapsedStackCollector +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) + +from test.support import requires_remote_subprocess_debugging + +from .helpers import test_subprocess + +# Duration for profiling in tests +PROFILING_DURATION_SEC = 1 + + +@requires_remote_subprocess_debugging() +class TestBlockingModeStackAccuracy(unittest.TestCase): + """Test that blocking mode produces accurate stack traces. + + When using blocking mode, the target process is stopped during sampling. + This ensures that we see accurate stack traces where functions appear + in the correct caller/callee relationship. + + These tests verify that generator functions are correctly shown at the + top of the stack when they are actively executing, and not incorrectly + shown under their caller's code. + """ + + @classmethod + def setUpClass(cls): + # Test script that uses a generator consumed in a loop. + # When consume_generator is the executing leaf frame on the arithmetic + # lines (temp1, temp2, etc.), fibonacci_generator should NOT be in the + # stack at all. + # Line numbers are important here - see ARITHMETIC_LINES below. + cls.generator_script = textwrap.dedent(''' + def fibonacci_generator(n): + a, b = 0, 1 + for _ in range(n): + yield a + a, b = b, a + b + + def consume_generator(): + gen = fibonacci_generator(10000) + for value in gen: + temp1 = value + 1 + temp2 = value * 2 + temp3 = value - 1 + result = temp1 + temp2 + temp3 + + def main(): + while True: + consume_generator() + + _test_sock.sendall(b"working") + main() + ''') + # Line numbers of the arithmetic operations in consume_generator. + # These are the lines where fibonacci_generator should NOT be in the + # stack when consume_generator is the executing leaf frame. They account + # for the socket prelude added by test_subprocess(). + # temp1 = value + 1 -> line 16 + # temp2 = value * 2 -> line 17 + # temp3 = value - 1 -> line 18 + # result = ... -> line 19 + cls.ARITHMETIC_LINES = {16, 17, 18, 19} + + def test_generator_not_under_consumer_arithmetic(self): + """Test that fibonacci_generator doesn't appear when consume_generator does arithmetic. + + When consume_generator is the leaf frame on arithmetic lines (temp1, + temp2, etc.), fibonacci_generator should NOT be anywhere in the stack - + it's not being called at that point. Non-leaf frame line numbers are + caller/resume metadata, not proof that the frame is executing. + + Valid stacks: + - fibonacci_generator at the top (generator is executing), with + consume_generator below it + - consume_generator at arithmetic lines WITHOUT fibonacci_generator + (we're just doing math, not calling the generator) + + Invalid stacks (indicate torn/inconsistent reads): + - consume_generator leaf frame at arithmetic lines WITH + fibonacci_generator + anywhere in the stack + + Note: call_tree is ordered from bottom (index 0) to top (index -1). + """ + with test_subprocess(self.generator_script, wait_for_working=True) as subproc: + collector = CollapsedStackCollector(sample_interval_usec=100, skip_idle=False) + + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=PROFILING_DURATION_SEC, + blocking=True, + ) + + # Analyze collected stacks + total_samples = 0 + invalid_stacks = 0 + arithmetic_samples = 0 + generator_samples = 0 + generator_not_leaf_samples = 0 + + for (call_tree, _thread_id), count in collector.stack_counter.items(): + total_samples += count + + if not call_tree: + continue + + # Non-leaf frame line numbers can point at resume locations while + # a callee is the executing leaf frame. + _, lineno, funcname = call_tree[-1] + func_names = [frame[2] for frame in call_tree] + + if "fibonacci_generator" in func_names: + generator_samples += count + if funcname != "fibonacci_generator": + generator_not_leaf_samples += count + + if funcname == "consume_generator" and lineno in self.ARITHMETIC_LINES: + arithmetic_samples += count + # Check if fibonacci_generator appears anywhere in this stack. + if "fibonacci_generator" in func_names: + invalid_stacks += count + + self.assertGreater(total_samples, 10, + f"Expected at least 10 samples, got {total_samples}") + + # We should have some samples on the arithmetic lines + self.assertGreater(arithmetic_samples, 0, + f"Expected some samples on arithmetic lines, got {arithmetic_samples}") + + self.assertGreater(generator_samples, 0, + f"Expected some samples in fibonacci_generator, got {generator_samples}") + + self.assertEqual(generator_not_leaf_samples, 0, + f"Found {generator_not_leaf_samples}/{generator_samples} stacks where " + f"fibonacci_generator appears but is not the leaf frame.") + + self.assertEqual(invalid_stacks, 0, + f"Found {invalid_stacks}/{arithmetic_samples} invalid stacks where " + f"fibonacci_generator appears in the stack when consume_generator " + f"is the leaf frame on an arithmetic line. This indicates " + f"torn/inconsistent stack traces are being captured.") diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_children.py b/Lib/test/test_profiling/test_sampling_profiler/test_children.py new file mode 100644 index 00000000000000..e64d917eedde56 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_children.py @@ -0,0 +1,1082 @@ +"""Tests for --subprocesses subprocess profiling support.""" + +import argparse +import io +import os +import signal +import subprocess +import sys +import tempfile +import threading +import time +import unittest +from unittest.mock import MagicMock, patch + +from test.support import ( + SHORT_TIMEOUT, + reap_children, + requires_remote_subprocess_debugging, +) + +# Guard imports that require _remote_debugging module. +# This module is not available on all platforms (e.g., WASI). +try: + from profiling.sampling._child_monitor import ( + get_child_pids, + ChildProcessMonitor, + is_python_process, + _MAX_CHILD_PROFILERS, + _CLEANUP_INTERVAL_CYCLES, + ) +except ImportError: + # Module will be skipped via @requires_remote_subprocess_debugging decorators + get_child_pids = None + ChildProcessMonitor = None + is_python_process = None + _MAX_CHILD_PROFILERS = None + _CLEANUP_INTERVAL_CYCLES = None + +try: + from profiling.sampling.cli import ( + _add_sampling_options, + _validate_args, + _build_child_profiler_args, + _build_output_pattern, + _setup_child_monitor, + ) +except ImportError: + # cli module imports sample module which requires _remote_debugging + _add_sampling_options = None + _validate_args = None + _build_child_profiler_args = None + _build_output_pattern = None + _setup_child_monitor = None + +from .helpers import _cleanup_process + +# String to check for in stderr when profiler lacks permissions (e.g., macOS) +_PERMISSION_ERROR_MSG = "Permission Error" + + +def _readline_with_timeout(file_obj, timeout): + # Thread-based readline with timeout - works across all platforms + # including Windows where select() doesn't work with pipes. + # Returns the line read, or None if timeout occurred. + result = [None] + exception = [None] + + def reader(): + try: + result[0] = file_obj.readline() + except Exception as e: + exception[0] = e + + thread = threading.Thread(target=reader, daemon=True) + thread.start() + thread.join(timeout=timeout) + + if thread.is_alive(): + return None + + if exception[0] is not None: + raise exception[0] + + return result[0] + + +def _wait_for_process_ready(proc, timeout): + # Wait for a subprocess to be ready using polling instead of fixed sleep. + # Returns True if process is ready, False if it exited or timeout. + deadline = time.time() + timeout + poll_interval = 0.01 + + while time.time() < deadline: + if proc.poll() is not None: + return False + + try: + if sys.platform == "linux": + if os.path.exists(f"/proc/{proc.pid}/exe"): + return True + else: + return True + except OSError: + pass + + time.sleep(poll_interval) + poll_interval = min(poll_interval * 2, 0.1) + + return proc.poll() is None + + +@unittest.skipIf( + _build_child_profiler_args is None, + "profiling.sampling.cli unavailable", +) +class TestChildProfilerArgBuilder(unittest.TestCase): + """Tests for child profiler CLI argument construction.""" + + def test_build_child_profiler_args_diff_flamegraph(self): + """Test child args use the real --diff-flamegraph flag.""" + args = argparse.Namespace( + sample_interval_usec=1000, + duration=None, + all_threads=False, + realtime_stats=False, + native=False, + gc=True, + opcodes=False, + async_aware=False, + mode="wall", + format="diff_flamegraph", + diff_baseline="baseline.bin", + ) + + child_args = _build_child_profiler_args(args) + + self.assertIn("--diff-flamegraph", child_args) + self.assertNotIn("--diff_flamegraph", child_args) + + flag_index = child_args.index("--diff-flamegraph") + self.assertGreater(len(child_args), flag_index + 1) + self.assertEqual(child_args[flag_index + 1], "baseline.bin") + + +@requires_remote_subprocess_debugging() +class TestGetChildPids(unittest.TestCase): + """Tests for the get_child_pids function.""" + + def setUp(self): + reap_children() + + def tearDown(self): + reap_children() + + def test_get_child_pids_from_remote_debugging(self): + """Test get_child_pids from _remote_debugging module.""" + try: + import _remote_debugging + + # Test that the function exists + self.assertTrue(hasattr(_remote_debugging, "get_child_pids")) + + # Test with current process (should return empty or have children if any) + result = _remote_debugging.get_child_pids(os.getpid()) + self.assertIsInstance(result, list) + except (ImportError, AttributeError): + self.skipTest("_remote_debugging.get_child_pids not available") + + def test_get_child_pids_fallback(self): + """Test the fallback implementation for get_child_pids.""" + # Test with current process + result = get_child_pids(os.getpid()) + self.assertIsInstance(result, list) + + @unittest.skipUnless(sys.platform == "linux", "Linux only") + def test_discover_child_process_linux(self): + """Test that we can discover child processes on Linux.""" + # Create a child process + proc = subprocess.Popen( + [sys.executable, "-c", "import time; time.sleep(10)"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + try: + # Poll until child appears + deadline = time.time() + SHORT_TIMEOUT + children = [] + while time.time() < deadline: + children = get_child_pids(os.getpid()) + if proc.pid in children: + break + time.sleep(0.05) + + self.assertIn( + proc.pid, + children, + f"Child PID {proc.pid} not discovered within {SHORT_TIMEOUT}s. " + f"Found PIDs: {children}", + ) + finally: + _cleanup_process(proc) + + def test_recursive_child_discovery(self): + """Test that recursive=True finds grandchildren.""" + # Create a child that spawns a grandchild and keeps a reference to it + # so we can clean it up via the child process + code = """ +import subprocess +import sys +import threading +grandchild = subprocess.Popen([sys.executable, '-c', 'import time; time.sleep(60)']) +print(grandchild.pid, flush=True) +# Wait for parent to send signal byte (cross-platform) +# Using threading with timeout so test doesn't hang if something goes wrong +# Timeout is 60s (2x test timeout) to ensure child outlives test in worst case +def wait_for_signal(): + try: + sys.stdin.buffer.read(1) + except: + pass +t = threading.Thread(target=wait_for_signal, daemon=True) +t.start() +t.join(timeout=60) +# Clean up grandchild before exiting +if grandchild.poll() is None: + grandchild.terminate() + try: + grandchild.wait(timeout=2) + except subprocess.TimeoutExpired: + grandchild.kill() + try: + grandchild.wait(timeout=2) + except subprocess.TimeoutExpired: + grandchild.wait() +""" + proc = subprocess.Popen( + [sys.executable, "-c", code], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + ) + + grandchild_pid = None + try: + # Read grandchild PID with thread-based timeout + # This prevents indefinite blocking on all platforms + grandchild_pid_line = _readline_with_timeout( + proc.stdout, SHORT_TIMEOUT + ) + if grandchild_pid_line is None: + self.fail( + f"Timeout waiting for grandchild PID from child process " + f"(child PID: {proc.pid})" + ) + if not grandchild_pid_line: + self.fail( + f"Child process {proc.pid} closed stdout without printing " + f"grandchild PID" + ) + grandchild_pid = int(grandchild_pid_line.strip()) + + # Poll until grandchild is visible + deadline = time.time() + SHORT_TIMEOUT + pids_recursive = [] + while time.time() < deadline: + pids_recursive = get_child_pids(os.getpid(), recursive=True) + if grandchild_pid in pids_recursive: + break + time.sleep(0.05) + + self.assertIn( + proc.pid, + pids_recursive, + f"Child PID {proc.pid} not found in recursive discovery. " + f"Found: {pids_recursive}", + ) + self.assertIn( + grandchild_pid, + pids_recursive, + f"Grandchild PID {grandchild_pid} not found in recursive discovery. " + f"Found: {pids_recursive}", + ) + + # Non-recursive should find only direct child + pids_direct = get_child_pids(os.getpid(), recursive=False) + self.assertIn( + proc.pid, + pids_direct, + f"Child PID {proc.pid} not found in non-recursive discovery. " + f"Found: {pids_direct}", + ) + self.assertNotIn( + grandchild_pid, + pids_direct, + f"Grandchild PID {grandchild_pid} should NOT be in non-recursive " + f"discovery. Found: {pids_direct}", + ) + finally: + # Send signal byte to child to trigger cleanup, then close stdin + try: + proc.stdin.write(b"x") + proc.stdin.flush() + proc.stdin.close() + except OSError: + pass + proc.stdout.close() + _cleanup_process(proc) + # The grandchild may not have been cleaned up by the child process + # (e.g., if the child was killed). Explicitly terminate the + # grandchild to prevent PermissionError on Windows when removing + # temp directories. + if grandchild_pid is not None: + try: + os.kill(grandchild_pid, signal.SIGTERM) + except (OSError, ProcessLookupError): + pass # Process already exited + + def test_nonexistent_pid_returns_empty(self): + """Test that nonexistent PID returns empty list.""" + # Use a very high PID that's unlikely to exist + result = get_child_pids(999999999) + self.assertEqual(result, []) + + +@requires_remote_subprocess_debugging() +class TestChildProcessMonitor(unittest.TestCase): + """Tests for the ChildProcessMonitor class.""" + + def setUp(self): + reap_children() + + def tearDown(self): + reap_children() + + def test_monitor_creation(self): + """Test that ChildProcessMonitor can be created.""" + monitor = ChildProcessMonitor( + pid=os.getpid(), + cli_args=["-r", "10khz", "-d", "5"], + output_pattern="test_{pid}.pstats", + ) + self.assertEqual(monitor.parent_pid, os.getpid()) + self.assertEqual(monitor.cli_args, ["-r", "10khz", "-d", "5"]) + self.assertEqual(monitor.output_pattern, "test_{pid}.pstats") + + def test_monitor_lifecycle(self): + """Test monitor lifecycle via context manager.""" + monitor = ChildProcessMonitor( + pid=os.getpid(), cli_args=[], output_pattern=None + ) + + # Before entering context, thread should not exist + self.assertIsNone(monitor._monitor_thread) + + with monitor: + # Inside context, thread should be running + self.assertIsNotNone(monitor._monitor_thread) + self.assertTrue(monitor._monitor_thread.is_alive()) + + # After exiting context, thread should be stopped + self.assertFalse(monitor._monitor_thread.is_alive()) + + def test_spawned_profilers_property(self): + """Test that spawned_profilers returns a copy of the list.""" + monitor = ChildProcessMonitor( + pid=os.getpid(), cli_args=[], output_pattern=None + ) + + # Should return empty list initially + profilers = monitor.spawned_profilers + self.assertEqual(profilers, []) + self.assertIsNot(profilers, monitor._spawned_profilers) + + def test_context_manager(self): + """Test that ChildProcessMonitor works as a context manager.""" + with ChildProcessMonitor( + pid=os.getpid(), cli_args=[], output_pattern=None + ) as monitor: + self.assertIsNotNone(monitor._monitor_thread) + self.assertTrue(monitor._monitor_thread.is_alive()) + + # After exiting context, thread should be stopped + self.assertFalse(monitor._monitor_thread.is_alive()) + + +@requires_remote_subprocess_debugging() +class TestCLIChildrenFlag(unittest.TestCase): + """Tests for the --subprocesses CLI flag.""" + + def setUp(self): + reap_children() + + def tearDown(self): + reap_children() + + def test_subprocesses_flag_parsed(self): + """Test that --subprocesses flag is recognized.""" + parser = argparse.ArgumentParser() + _add_sampling_options(parser) + + # Parse with --subprocesses + args = parser.parse_args(["--subprocesses"]) + self.assertTrue(args.subprocesses) + + # Parse without --subprocesses + args = parser.parse_args([]) + self.assertFalse(args.subprocesses) + + def test_subprocesses_incompatible_with_live(self): + """Test that --subprocesses is incompatible with --live.""" + # Create mock args with both subprocesses and live + args = argparse.Namespace( + subprocesses=True, + live=True, + async_aware=False, + format="pstats", + mode="wall", + sort=None, + limit=None, + no_summary=False, + opcodes=False, + blocking=False, + interval=1000, + ) + + parser = argparse.ArgumentParser() + + with self.assertRaises(SystemExit): + _validate_args(args, parser) + + def test_build_child_profiler_args(self): + """Test building CLI args for child profilers.""" + args = argparse.Namespace( + sample_interval_usec=200, + duration=15, + all_threads=True, + realtime_stats=False, + native=True, + gc=True, + opcodes=False, + async_aware=False, + mode="cpu", + format="flamegraph", + ) + + child_args = _build_child_profiler_args(args) + + # Verify flag-value pairs are correctly paired (flag followed by value) + def assert_flag_value_pair(flag, value): + self.assertIn( + flag, + child_args, + f"Flag '{flag}' not found in args: {child_args}", + ) + flag_index = child_args.index(flag) + self.assertGreater( + len(child_args), + flag_index + 1, + f"No value after flag '{flag}' in args: {child_args}", + ) + self.assertEqual( + child_args[flag_index + 1], + str(value), + f"Flag '{flag}' should be followed by '{value}', got " + f"'{child_args[flag_index + 1]}' in args: {child_args}", + ) + + assert_flag_value_pair("-r", 5000) + assert_flag_value_pair("-d", 15) + assert_flag_value_pair("--mode", "cpu") + + # Verify standalone flags are present + self.assertIn( + "-a", child_args, f"Flag '-a' not found in args: {child_args}" + ) + self.assertIn( + "--native", + child_args, + f"Flag '--native' not found in args: {child_args}", + ) + self.assertIn( + "--flamegraph", + child_args, + f"Flag '--flamegraph' not found in args: {child_args}", + ) + self.assertNotIn( + "--browser", + child_args, + f"Flag '--browser' should not be in child args: {child_args}", + ) + + def test_build_child_profiler_args_no_gc(self): + """Test building CLI args with --no-gc.""" + args = argparse.Namespace( + sample_interval_usec=100, + duration=5, + all_threads=False, + realtime_stats=False, + native=False, + gc=False, # Explicitly disabled + opcodes=False, + async_aware=False, + mode="wall", + format="pstats", + ) + + child_args = _build_child_profiler_args(args) + + self.assertIn( + "--no-gc", + child_args, + f"Flag '--no-gc' not found when gc=False. Args: {child_args}", + ) + + def test_build_output_pattern_with_outfile(self): + """Test output pattern generation with user-specified output.""" + # With extension + args = argparse.Namespace(outfile="output.html", format="flamegraph") + pattern = _build_output_pattern(args) + self.assertEqual(pattern, "output_{pid}.html") + + # Without extension + args = argparse.Namespace(outfile="output", format="pstats") + pattern = _build_output_pattern(args) + self.assertEqual(pattern, "output_{pid}") + + def test_build_output_pattern_default(self): + """Test output pattern generation with default output.""" + # Flamegraph format + args = argparse.Namespace(outfile=None, format="flamegraph") + pattern = _build_output_pattern(args) + self.assertIn("{pid}", pattern) + self.assertIn("flamegraph", pattern) + self.assertTrue(pattern.endswith(".html")) + + # Heatmap format + args = argparse.Namespace(outfile=None, format="heatmap") + pattern = _build_output_pattern(args) + self.assertEqual(pattern, "heatmap_{pid}") + + +@requires_remote_subprocess_debugging() +class TestChildrenIntegration(unittest.TestCase): + """Integration tests for --subprocesses functionality.""" + + def setUp(self): + reap_children() + + def tearDown(self): + reap_children() + + def test_setup_child_monitor(self): + """Test setting up a child monitor from args.""" + args = argparse.Namespace( + sample_interval_usec=100, + duration=5, + all_threads=False, + realtime_stats=False, + native=False, + gc=True, + opcodes=False, + async_aware=False, + mode="wall", + format="pstats", + outfile=None, + ) + + monitor = _setup_child_monitor(args, os.getpid()) + # Use addCleanup to ensure monitor is properly cleaned up even if + # assertions fail + self.addCleanup(monitor.__exit__, None, None, None) + + self.assertIsNotNone(monitor) + self.assertEqual( + monitor.parent_pid, + os.getpid(), + f"Monitor parent_pid should be {os.getpid()}, got {monitor.parent_pid}", + ) + + +@requires_remote_subprocess_debugging() +class TestIsPythonProcess(unittest.TestCase): + """Tests for the is_python_process function.""" + + def setUp(self): + reap_children() + + def tearDown(self): + reap_children() + + def test_is_python_process_current_process(self): + """Test that current process is detected as Python.""" + # Current process should be Python + result = is_python_process(os.getpid()) + self.assertTrue( + result, + f"Current process (PID {os.getpid()}) should be detected as Python", + ) + + def test_is_python_process_python_subprocess(self): + """Test that a Python subprocess is detected as Python.""" + # Start a Python subprocess + proc = subprocess.Popen( + [sys.executable, "-c", "import time; time.sleep(10)"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + try: + # Poll until Python runtime structures are initialized + # (is_python_process probes for runtime structures which take + # time to initialize after process start) + deadline = time.time() + SHORT_TIMEOUT + detected = False + while time.time() < deadline: + if proc.poll() is not None: + self.fail(f"Process {proc.pid} exited unexpectedly") + if is_python_process(proc.pid): + detected = True + break + time.sleep(0.05) + + self.assertTrue( + detected, + f"Python subprocess (PID {proc.pid}) should be detected as Python " + f"within {SHORT_TIMEOUT}s", + ) + finally: + _cleanup_process(proc) + + @unittest.skipUnless(sys.platform == "linux", "Linux only test") + def test_is_python_process_non_python_subprocess(self): + """Test that a non-Python subprocess is not detected as Python.""" + # Start a non-Python subprocess (sleep command) + proc = subprocess.Popen( + ["sleep", "10"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + try: + # Wait for process to be ready using polling + self.assertTrue( + _wait_for_process_ready(proc, SHORT_TIMEOUT), + f"Process {proc.pid} should be ready within {SHORT_TIMEOUT}s", + ) + + self.assertFalse( + is_python_process(proc.pid), + f"Non-Python subprocess 'sleep' (PID {proc.pid}) should NOT be " + f"detected as Python", + ) + finally: + _cleanup_process(proc) + + def test_is_python_process_nonexistent_pid(self): + """Test that nonexistent PID returns False.""" + # Use a very high PID that's unlikely to exist + result = is_python_process(999999999) + self.assertFalse( + result, + "Nonexistent PID 999999999 should return False", + ) + + def test_is_python_process_exited_process(self): + """Test handling of a process that exits quickly.""" + # Start a process that exits immediately + proc = subprocess.Popen( + [sys.executable, "-c", "pass"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + # Wait for it to exit + proc.wait(timeout=SHORT_TIMEOUT) + + # Should return False for exited process (not raise) + result = is_python_process(proc.pid) + self.assertFalse( + result, f"Exited process (PID {proc.pid}) should return False" + ) + + +@requires_remote_subprocess_debugging() +class TestMaxChildProfilersLimit(unittest.TestCase): + """Tests for the _MAX_CHILD_PROFILERS limit.""" + + def setUp(self): + reap_children() + + def tearDown(self): + reap_children() + + def test_max_profilers_constant_exists(self): + """Test that _MAX_CHILD_PROFILERS constant is defined.""" + self.assertEqual( + _MAX_CHILD_PROFILERS, + 100, + f"_MAX_CHILD_PROFILERS should be 100, got {_MAX_CHILD_PROFILERS}", + ) + + def test_cleanup_interval_constant_exists(self): + """Test that _CLEANUP_INTERVAL_CYCLES constant is defined.""" + self.assertEqual( + _CLEANUP_INTERVAL_CYCLES, + 10, + f"_CLEANUP_INTERVAL_CYCLES should be 10, got {_CLEANUP_INTERVAL_CYCLES}", + ) + + def test_monitor_respects_max_limit(self): + """Test that monitor refuses to spawn more than _MAX_CHILD_PROFILERS.""" + # Create a monitor + monitor = ChildProcessMonitor( + pid=os.getpid(), + cli_args=["-r", "10khz", "-d", "5"], + output_pattern="test_{pid}.pstats", + ) + + # Manually fill up the profilers list to the limit + mock_profilers = [MagicMock() for _ in range(_MAX_CHILD_PROFILERS)] + for mock_proc in mock_profilers: + mock_proc.poll.return_value = None # Simulate running process + monitor._spawned_profilers = mock_profilers + + # Try to spawn another profiler - should be rejected + stderr_capture = io.StringIO() + with patch("sys.stderr", stderr_capture): + monitor._spawn_profiler_for_child(99999) + + # Verify warning was printed + stderr_output = stderr_capture.getvalue() + self.assertIn( + "Max child profilers", + stderr_output, + f"Expected warning about max profilers, got: {stderr_output}", + ) + self.assertIn( + str(_MAX_CHILD_PROFILERS), + stderr_output, + f"Warning should mention limit ({_MAX_CHILD_PROFILERS}): {stderr_output}", + ) + + # Verify no new profiler was added + self.assertEqual( + len(monitor._spawned_profilers), + _MAX_CHILD_PROFILERS, + f"Should still have {_MAX_CHILD_PROFILERS} profilers, got " + f"{len(monitor._spawned_profilers)}", + ) + + +@requires_remote_subprocess_debugging() +class TestWaitForProfilers(unittest.TestCase): + """Tests for the wait_for_profilers method.""" + + def setUp(self): + reap_children() + + def tearDown(self): + reap_children() + + def test_wait_for_profilers_empty_list(self): + """Test that wait_for_profilers returns immediately with no profilers.""" + monitor = ChildProcessMonitor( + pid=os.getpid(), cli_args=[], output_pattern=None + ) + + # Should return immediately without printing anything + stderr_capture = io.StringIO() + with unittest.mock.patch("sys.stderr", stderr_capture): + start = time.time() + monitor.wait_for_profilers(timeout=10.0) + elapsed = time.time() - start + + # Should complete very quickly (less than 1 second) + self.assertLess( + elapsed, + 1.0, + f"wait_for_profilers with empty list took {elapsed:.2f}s, expected < 1s", + ) + # No "Waiting for..." message should be printed + self.assertNotIn( + "Waiting for", + stderr_capture.getvalue(), + "Should not print waiting message when no profilers", + ) + + def test_wait_for_profilers_with_completed_process(self): + """Test waiting for profilers that complete quickly.""" + monitor = ChildProcessMonitor( + pid=os.getpid(), cli_args=[], output_pattern=None + ) + + # Start a process that exits quickly + proc = subprocess.Popen( + [sys.executable, "-c", "pass"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + # Add to spawned profilers + monitor._spawned_profilers.append(proc) + + try: + stderr_capture = io.StringIO() + with unittest.mock.patch("sys.stderr", stderr_capture): + start = time.time() + monitor.wait_for_profilers(timeout=SHORT_TIMEOUT) + elapsed = time.time() - start + + # Should complete quickly since process exits fast + self.assertLess( + elapsed, + 5.0, + f"wait_for_profilers took {elapsed:.2f}s for quick process", + ) + # Should print waiting message + self.assertIn( + "Waiting for 1 child profiler", + stderr_capture.getvalue(), + "Should print waiting message", + ) + finally: + _cleanup_process(proc) + + def test_wait_for_profilers_timeout(self): + """Test that wait_for_profilers respects timeout.""" + monitor = ChildProcessMonitor( + pid=os.getpid(), cli_args=[], output_pattern=None + ) + + # Start a process that runs for a long time + proc = subprocess.Popen( + [sys.executable, "-c", "import time; time.sleep(60)"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + # Add to spawned profilers + monitor._spawned_profilers.append(proc) + + try: + stderr_capture = io.StringIO() + with unittest.mock.patch("sys.stderr", stderr_capture): + start = time.time() + # Use short timeout + monitor.wait_for_profilers(timeout=0.5) + elapsed = time.time() - start + + # Should timeout after approximately 0.5 seconds + self.assertGreater( + elapsed, + 0.4, + f"wait_for_profilers returned too quickly ({elapsed:.2f}s)", + ) + self.assertLess( + elapsed, + 2.0, + f"wait_for_profilers took too long ({elapsed:.2f}s), timeout not respected", + ) + finally: + _cleanup_process(proc) + + def test_wait_for_profilers_multiple(self): + """Test waiting for multiple profilers.""" + monitor = ChildProcessMonitor( + pid=os.getpid(), cli_args=[], output_pattern=None + ) + + # Start multiple processes + procs = [] + for _ in range(3): + proc = subprocess.Popen( + [sys.executable, "-c", "import time; time.sleep(0.1)"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + procs.append(proc) + monitor._spawned_profilers.append(proc) + + try: + stderr_capture = io.StringIO() + with unittest.mock.patch("sys.stderr", stderr_capture): + monitor.wait_for_profilers(timeout=SHORT_TIMEOUT) + + # Should report correct count + self.assertIn( + "Waiting for 3 child profiler", + stderr_capture.getvalue(), + "Should report correct profiler count", + ) + finally: + for proc in procs: + _cleanup_process(proc) + + +@requires_remote_subprocess_debugging() +class TestEndToEndChildrenCLI(unittest.TestCase): + """End-to-end tests for --subprocesses CLI flag.""" + + def setUp(self): + reap_children() + + def tearDown(self): + reap_children() + + def test_subprocesses_flag_spawns_child_and_creates_output(self): + """Test that --subprocesses flag works end-to-end with actual subprocesses.""" + # Create a temporary directory for output files + with tempfile.TemporaryDirectory() as tmpdir: + # Create a script that spawns a child Python process + parent_script = f""" +import subprocess +import sys +import time + +# Spawn a child that does some work +child = subprocess.Popen([ + sys.executable, '-c', + 'import time; [i**2 for i in range(1000)]; time.sleep(2)' +]) +# Do some work in parent +for i in range(1000): + _ = i ** 2 +time.sleep(2) +child.wait() +""" + script_file = os.path.join(tmpdir, "parent_script.py") + with open(script_file, "w") as f: + f.write(parent_script) + + output_file = os.path.join(tmpdir, "profile.pstats") + + # Run the profiler with --subprocesses flag + result = subprocess.run( + [ + sys.executable, + "-m", + "profiling.sampling", + "run", + "--subprocesses", + "-d", + "3", + "-r", + "100", + "-o", + output_file, + script_file, + ], + capture_output=True, + text=True, + timeout=SHORT_TIMEOUT, + ) + + # Check that parent output file was created + self.assertTrue( + os.path.exists(output_file), + f"Parent profile output not created. " + f"stdout: {result.stdout}, stderr: {result.stderr}", + ) + + # Check for child profiler output files (pattern: profile_{pid}.pstats) + output_files = os.listdir(tmpdir) + child_profiles = [ + f + for f in output_files + if f.startswith("profile_") and f.endswith(".pstats") + ] + + # Note: Child profiling is best-effort; the child may exit before + # profiler attaches, or the process may not be detected as Python. + # We just verify the mechanism doesn't crash. + if result.returncode != 0: + self.fail( + f"Profiler exited with code {result.returncode}. " + f"stdout: {result.stdout}, stderr: {result.stderr}" + ) + + def test_subprocesses_flag_with_flamegraph_output(self): + """Test --subprocesses with flamegraph output format.""" + with tempfile.TemporaryDirectory() as tmpdir: + # Simple parent that spawns a child + parent_script = f""" +import subprocess +import sys +import time +child = subprocess.Popen([sys.executable, '-c', 'import time; time.sleep(1)']) +time.sleep(1) +child.wait() +""" + script_file = os.path.join(tmpdir, "parent.py") + with open(script_file, "w") as f: + f.write(parent_script) + + output_file = os.path.join(tmpdir, "flame.html") + + result = subprocess.run( + [ + sys.executable, + "-m", + "profiling.sampling", + "run", + "--subprocesses", + "-d", + "2", + "-r", + "100", + "--flamegraph", + "-o", + output_file, + script_file, + ], + capture_output=True, + text=True, + timeout=SHORT_TIMEOUT, + ) + + self.assertTrue( + os.path.exists(output_file), + f"Flamegraph output not created. stderr: {result.stderr}", + ) + + # Verify it's valid HTML + with open(output_file, "r") as f: + content = f.read() + self.assertIn( + "<html", + content.lower(), + "Flamegraph output should be HTML", + ) + + def test_subprocesses_flag_no_crash_on_quick_child(self): + """Test that --subprocesses doesn't crash when child exits quickly.""" + with tempfile.TemporaryDirectory() as tmpdir: + # Parent spawns a child that exits immediately + parent_script = f""" +import subprocess +import sys +import time +# Child exits immediately +child = subprocess.Popen([sys.executable, '-c', 'pass']) +child.wait() +time.sleep(1) +""" + script_file = os.path.join(tmpdir, "parent.py") + with open(script_file, "w") as f: + f.write(parent_script) + + output_file = os.path.join(tmpdir, "profile.pstats") + + result = subprocess.run( + [ + sys.executable, + "-m", + "profiling.sampling", + "run", + "--subprocesses", + "-d", + "2", + "-r", + "100", + "-o", + output_file, + script_file, + ], + capture_output=True, + text=True, + timeout=SHORT_TIMEOUT, + ) + + # Should not crash - exit code 0 + self.assertEqual( + result.returncode, + 0, + f"Profiler crashed with quick-exit child. " + f"stderr: {result.stderr}", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_cli.py b/Lib/test/test_profiling/test_sampling_profiler/test_cli.py new file mode 100644 index 00000000000000..9c0734ac804e1b --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_cli.py @@ -0,0 +1,987 @@ +"""Tests for sampling profiler CLI argument parsing and functionality.""" + +import io +import json +import os +import subprocess +import sys +import tempfile +import unittest +from unittest import mock + +try: + import _remote_debugging # noqa: F401 +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) + +from test.support import ( + force_not_colorized, + is_emscripten, + requires_remote_subprocess_debugging, +) + +from profiling.sampling.cli import ( + FORMAT_EXTENSIONS, + _create_collector, + _generate_output_filename, + main, +) +from profiling.sampling.constants import ( + PROFILING_MODE_ALL, + PROFILING_MODE_CPU, + PROFILING_MODE_WALL, +) +from profiling.sampling.errors import SamplingScriptNotFoundError, SamplingModuleNotFoundError, SamplingUnknownProcessError +from profiling.sampling.jsonl_collector import JsonlCollector + +class TestSampleProfilerCLI(unittest.TestCase): + def _setup_sync_mocks(self, mock_socket, mock_popen): + """Helper to set up socket and process mocks for coordinator tests.""" + # Mock the sync socket with context manager support + mock_sock_instance = mock.MagicMock() + mock_sock_instance.getsockname.return_value = ("127.0.0.1", 12345) + + # Mock the connection with context manager support + mock_conn = mock.MagicMock() + mock_conn.recv.return_value = b"ready" + mock_conn.__enter__.return_value = mock_conn + mock_conn.__exit__.return_value = None + + # Mock accept() to return (connection, address) and support indexing + mock_accept_result = mock.MagicMock() + mock_accept_result.__getitem__.return_value = ( + mock_conn # [0] returns the connection + ) + mock_sock_instance.accept.return_value = mock_accept_result + + # Mock socket with context manager support + mock_sock_instance.__enter__.return_value = mock_sock_instance + mock_sock_instance.__exit__.return_value = None + mock_socket.return_value = mock_sock_instance + + # Mock the subprocess + mock_process = mock.MagicMock() + mock_process.pid = 12345 + mock_process.poll.return_value = None + mock_popen.return_value = mock_process + return mock_process + + def _verify_coordinator_command(self, mock_popen, expected_target_args): + """Helper to verify the coordinator command was called correctly.""" + args, kwargs = mock_popen.call_args + coordinator_cmd = args[0] + self.assertEqual(coordinator_cmd[0], sys.executable) + self.assertEqual(coordinator_cmd[1], "-m") + self.assertEqual( + coordinator_cmd[2], "profiling.sampling._sync_coordinator" + ) + self.assertEqual(coordinator_cmd[3], "12345") # port + # cwd is coordinator_cmd[4] + self.assertEqual(coordinator_cmd[5:], expected_target_args) + + @unittest.skipIf(is_emscripten, "socket.SO_REUSEADDR does not exist") + @requires_remote_subprocess_debugging() + def test_cli_module_argument_parsing(self): + test_args = ["profiling.sampling.cli", "run", "-m", "mymodule"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + mock.patch("subprocess.Popen") as mock_popen, + mock.patch("socket.socket") as mock_socket, + mock.patch("profiling.sampling.cli._wait_for_ready_signal"), + mock.patch("importlib.util.find_spec", return_value=True), + ): + self._setup_sync_mocks(mock_socket, mock_popen) + main() + + self._verify_coordinator_command(mock_popen, ("-m", "mymodule")) + # Verify sample was called once (exact arguments will vary with the new API) + mock_sample.assert_called_once() + + @unittest.skipIf(is_emscripten, "socket.SO_REUSEADDR does not exist") + @requires_remote_subprocess_debugging() + def test_cli_module_with_arguments(self): + test_args = [ + "profiling.sampling.cli", + "run", + "-m", + "mymodule", + "arg1", + "arg2", + "--flag", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + mock.patch("subprocess.Popen") as mock_popen, + mock.patch("socket.socket") as mock_socket, + mock.patch("profiling.sampling.cli._wait_for_ready_signal"), + mock.patch("importlib.util.find_spec", return_value=True), + ): + self._setup_sync_mocks(mock_socket, mock_popen) + main() + + self._verify_coordinator_command( + mock_popen, ("-m", "mymodule", "arg1", "arg2", "--flag") + ) + mock_sample.assert_called_once() + + @unittest.skipIf(is_emscripten, "socket.SO_REUSEADDR does not exist") + def test_cli_script_argument_parsing(self): + test_args = ["profiling.sampling.cli", "run", "myscript.py"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + mock.patch("subprocess.Popen") as mock_popen, + mock.patch("socket.socket") as mock_socket, + mock.patch("profiling.sampling.cli._wait_for_ready_signal"), + mock.patch("os.path.exists", return_value=True), + ): + self._setup_sync_mocks(mock_socket, mock_popen) + main() + + self._verify_coordinator_command(mock_popen, ("myscript.py",)) + mock_sample.assert_called_once() + + @unittest.skipIf(is_emscripten, "socket.SO_REUSEADDR does not exist") + def test_cli_script_with_arguments(self): + test_args = [ + "profiling.sampling.cli", + "run", + "myscript.py", + "arg1", + "arg2", + "--flag", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + mock.patch("subprocess.Popen") as mock_popen, + mock.patch("socket.socket") as mock_socket, + mock.patch("profiling.sampling.cli._wait_for_ready_signal"), + mock.patch("os.path.exists", return_value=True), + ): + # Use the helper to set up mocks consistently + mock_process = self._setup_sync_mocks(mock_socket, mock_popen) + # Override specific behavior for this test + mock_process.wait.side_effect = [ + subprocess.TimeoutExpired(test_args, 0.1), + None, + ] + + main() + + # Verify the coordinator command was called + args, kwargs = mock_popen.call_args + coordinator_cmd = args[0] + self.assertEqual(coordinator_cmd[0], sys.executable) + self.assertEqual(coordinator_cmd[1], "-m") + self.assertEqual( + coordinator_cmd[2], "profiling.sampling._sync_coordinator" + ) + self.assertEqual(coordinator_cmd[3], "12345") # port + # cwd is coordinator_cmd[4] + self.assertEqual( + coordinator_cmd[5:], ("myscript.py", "arg1", "arg2", "--flag") + ) + + def test_cli_mutually_exclusive_pid_module(self): + # In new CLI, attach and run are separate subcommands, so this test + # verifies that mixing them causes an error + test_args = [ + "profiling.sampling.cli", + "attach", # attach subcommand uses PID + "12345", + "-m", # -m is only for run subcommand + "mymodule", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + error_msg = mock_stderr.getvalue() + self.assertIn("unrecognized arguments", error_msg) + + def test_cli_mutually_exclusive_pid_script(self): + # In new CLI, you can't mix attach (PID) with run (script) + # This would be caught by providing a PID to run subcommand + test_args = ["profiling.sampling.cli", "run", "12345"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SamplingScriptNotFoundError) as cm, + ): + main() + + # Verify the error is about the non-existent script + self.assertIn("12345", str(cm.exception)) + + def test_cli_no_target_specified(self): + # In new CLI, must specify a subcommand + test_args = ["profiling.sampling.cli", "-d", "5"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + error_msg = mock_stderr.getvalue() + self.assertIn("invalid choice", error_msg) + + @unittest.skipIf(is_emscripten, "socket.SO_REUSEADDR does not exist") + @requires_remote_subprocess_debugging() + def test_cli_module_with_profiler_options(self): + test_args = [ + "profiling.sampling.cli", + "run", + "-r", + "1000", + "-d", + "30", + "-a", + "--sort", + "tottime", + "-l", + "20", + "-m", + "mymodule", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + mock.patch("subprocess.Popen") as mock_popen, + mock.patch("socket.socket") as mock_socket, + mock.patch("profiling.sampling.cli._wait_for_ready_signal"), + mock.patch("importlib.util.find_spec", return_value=True), + ): + self._setup_sync_mocks(mock_socket, mock_popen) + main() + + self._verify_coordinator_command(mock_popen, ("-m", "mymodule")) + mock_sample.assert_called_once() + + @unittest.skipIf(is_emscripten, "socket.SO_REUSEADDR does not exist") + def test_cli_script_with_profiler_options(self): + """Test script with various profiler options.""" + test_args = [ + "profiling.sampling.cli", + "run", + "-r", + "500", + "-d", + "60", + "--collapsed", + "-o", + "output.txt", + "myscript.py", + "scriptarg", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + mock.patch("subprocess.Popen") as mock_popen, + mock.patch("socket.socket") as mock_socket, + mock.patch("profiling.sampling.cli._wait_for_ready_signal"), + mock.patch("os.path.exists", return_value=True), + ): + self._setup_sync_mocks(mock_socket, mock_popen) + main() + + self._verify_coordinator_command( + mock_popen, ("myscript.py", "scriptarg") + ) + # Verify profiler was called + mock_sample.assert_called_once() + + def test_cli_empty_module_name(self): + test_args = ["profiling.sampling.cli", "run", "-m"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + error_msg = mock_stderr.getvalue() + self.assertIn("required: target", error_msg) # argparse error for missing positional arg + + @unittest.skipIf(is_emscripten, "socket.SO_REUSEADDR does not exist") + @requires_remote_subprocess_debugging() + def test_cli_long_module_option(self): + test_args = [ + "profiling.sampling.cli", + "run", + "-m", + "mymodule", + "arg1", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + mock.patch("subprocess.Popen") as mock_popen, + mock.patch("socket.socket") as mock_socket, + mock.patch("profiling.sampling.cli._wait_for_ready_signal"), + mock.patch("importlib.util.find_spec", return_value=True), + ): + self._setup_sync_mocks(mock_socket, mock_popen) + main() + + self._verify_coordinator_command( + mock_popen, ("-m", "mymodule", "arg1") + ) + + def test_cli_complex_script_arguments(self): + test_args = [ + "profiling.sampling.cli", + "run", + "script.py", + "--input", + "file.txt", + "-v", + "--output=/tmp/out", + "positional", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + mock.patch( + "profiling.sampling.cli._run_with_sync" + ) as mock_run_with_sync, + mock.patch("os.path.exists", return_value=True), + ): + mock_process = mock.MagicMock() + mock_process.pid = 12345 + mock_process.wait.side_effect = [ + subprocess.TimeoutExpired(test_args, 0.1), + None, + ] + mock_process.poll.return_value = None + mock_run_with_sync.return_value = mock_process + + main() + + mock_run_with_sync.assert_called_once_with( + ( + sys.executable, + "script.py", + "--input", + "file.txt", + "-v", + "--output=/tmp/out", + "positional", + ), + suppress_output=False + ) + + def test_cli_collapsed_format_validation(self): + """Test that CLI properly validates incompatible options with collapsed format.""" + test_cases = [ + # Test sort option is invalid with collapsed + ( + [ + "profiling.sampling.cli", + "attach", + "12345", + "--collapsed", + "--sort", + "tottime", # Changed from nsamples (default) to trigger validation + ], + "sort", + ), + # Test limit option is invalid with collapsed + ( + [ + "profiling.sampling.cli", + "attach", + "12345", + "--collapsed", + "-l", + "20", + ], + "limit", + ), + # Test no-summary option is invalid with collapsed + ( + [ + "profiling.sampling.cli", + "attach", + "12345", + "--collapsed", + "--no-summary", + ], + "summary", + ), + ] + + for test_args, expected_error_keyword in test_cases: + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + mock.patch("profiling.sampling.cli.sample"), # Prevent actual profiling + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error code + error_msg = mock_stderr.getvalue() + self.assertIn("error:", error_msg) + self.assertIn("only valid with --pstats", error_msg) + + def test_cli_default_collapsed_filename(self): + """Test that collapsed format gets a default filename when not specified.""" + test_args = ["profiling.sampling.cli", "attach", "12345", "--collapsed"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + main() + + # Check that sample was called (exact filename depends on implementation) + mock_sample.assert_called_once() + + def test_cli_custom_output_filenames(self): + """Test custom output filenames for both formats.""" + test_cases = [ + ( + [ + "profiling.sampling.cli", + "attach", + "12345", + "--pstats", + "-o", + "custom.pstats", + ], + "custom.pstats", + "pstats", + ), + ( + [ + "profiling.sampling.cli", + "attach", + "12345", + "--collapsed", + "-o", + "custom.txt", + ], + "custom.txt", + "collapsed", + ), + ] + + for test_args, expected_filename, expected_format in test_cases: + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + main() + + mock_sample.assert_called_once() + + def test_cli_missing_required_arguments(self): + """Test that CLI requires subcommand.""" + with ( + mock.patch("sys.argv", ["profiling.sampling.cli"]), + mock.patch("sys.stderr", io.StringIO()), + ): + with self.assertRaises(SystemExit): + main() + + def test_cli_mutually_exclusive_format_options(self): + """Test that pstats and collapsed options are mutually exclusive.""" + with ( + mock.patch( + "sys.argv", + [ + "profiling.sampling.cli", + "attach", + "12345", + "--pstats", + "--collapsed", + ], + ), + mock.patch("sys.stderr", io.StringIO()), + ): + with self.assertRaises(SystemExit): + main() + + def test_argument_parsing_basic(self): + test_args = ["profiling.sampling.cli", "attach", "12345"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + main() + + mock_sample.assert_called_once() + + def _run_dump_cli( + self, + *cli_args, + dump_return=None, + dump_side_effect=None, + process_running=True, + ): + """Run main() for a `dump` invocation, returning (mock_dump, mock_print).""" + argv = ["profiling.sampling.cli", "dump", *cli_args] + dump_kwargs = {} + if dump_side_effect is not None: + dump_kwargs["side_effect"] = dump_side_effect + else: + dump_kwargs["return_value"] = [] if dump_return is None else dump_return + with ( + mock.patch("sys.argv", argv), + mock.patch( + "profiling.sampling.cli._is_process_running", + return_value=process_running, + ), + mock.patch("profiling.sampling.cli.dump_stack", **dump_kwargs) as mock_dump_stack, + mock.patch("profiling.sampling.cli.print_stack_dump") as mock_print_stack_dump, + ): + main() + return mock_dump_stack, mock_print_stack_dump + + def _run_dump_cli_expecting_exit(self, *cli_args, capture="stderr"): + """Run main() for a `dump` invocation expected to SystemExit, return (cm, captured_text).""" + argv = ["profiling.sampling.cli", "dump", *cli_args] + buf = io.StringIO() + stream = "sys.stderr" if capture == "stderr" else "sys.stdout" + with ( + mock.patch("sys.argv", argv), + mock.patch(stream, buf), + self.assertRaises(SystemExit) as cm, + ): + main() + return cm, buf.getvalue() + + def test_cli_dump_subcommand(self): + stack_frames = [mock.sentinel.stack_frames] + mock_dump_stack, mock_print_stack_dump = self._run_dump_cli( + "12345", dump_return=stack_frames + ) + + mock_dump_stack.assert_called_once() + self.assertEqual(mock_dump_stack.call_args.args, (12345,)) + call_kwargs = mock_dump_stack.call_args.kwargs + self.assertFalse(call_kwargs["all_threads"]) + self.assertIsNone(call_kwargs["async_aware"]) + self.assertFalse(call_kwargs["native"]) + self.assertTrue(call_kwargs["gc"]) + self.assertFalse(call_kwargs["opcodes"]) + self.assertFalse(call_kwargs["blocking"]) + self.assertEqual(call_kwargs["mode"], PROFILING_MODE_ALL) + mock_print_stack_dump.assert_called_once_with(stack_frames, pid=12345) + + def test_cli_dump_subcommand_options(self): + mock_dump_stack, _ = self._run_dump_cli( + "-a", "--native", "--no-gc", "--opcodes", "--blocking", "12345" + ) + + call_kwargs = mock_dump_stack.call_args.kwargs + self.assertTrue(call_kwargs["all_threads"]) + self.assertTrue(call_kwargs["native"]) + self.assertFalse(call_kwargs["gc"]) + self.assertTrue(call_kwargs["opcodes"]) + self.assertTrue(call_kwargs["blocking"]) + self.assertEqual(call_kwargs["mode"], PROFILING_MODE_ALL) + + def test_cli_dump_rejects_mode_option(self): + cm, stderr = self._run_dump_cli_expecting_exit("12345", "--mode", "cpu") + self.assertEqual(cm.exception.code, 2) + self.assertIn("unrecognized arguments: --mode", stderr) + + def test_cli_dump_async_aware_defaults_to_all(self): + mock_dump_stack, _ = self._run_dump_cli("--async-aware", "12345") + call_kwargs = mock_dump_stack.call_args.kwargs + self.assertEqual(call_kwargs["async_aware"], "all") + self.assertEqual(call_kwargs["mode"], PROFILING_MODE_WALL) + + def test_cli_dump_async_mode_all_is_forwarded(self): + mock_dump_stack, _ = self._run_dump_cli( + "--async-aware", "--async-mode", "all", "12345" + ) + call_kwargs = mock_dump_stack.call_args.kwargs + self.assertEqual(call_kwargs["async_aware"], "all") + self.assertEqual(call_kwargs["mode"], PROFILING_MODE_WALL) + + def test_cli_dump_async_mode_running_is_forwarded(self): + mock_dump_stack, _ = self._run_dump_cli( + "--async-aware", "--async-mode", "running", "12345" + ) + call_kwargs = mock_dump_stack.call_args.kwargs + self.assertEqual(call_kwargs["async_aware"], "running") + self.assertEqual(call_kwargs["mode"], PROFILING_MODE_WALL) + + def test_cli_dump_async_mode_requires_async_aware(self): + cm, stderr = self._run_dump_cli_expecting_exit( + "--async-mode", "running", "12345" + ) + self.assertEqual(cm.exception.code, 2) + self.assertIn("--async-mode requires --async-aware", stderr) + + def test_cli_dump_rejects_async_aware_with_all_threads(self): + cm, stderr = self._run_dump_cli_expecting_exit( + "--async-aware", "-a", "12345" + ) + self.assertEqual(cm.exception.code, 2) + self.assertIn("--all-threads", stderr) + self.assertIn("incompatible with --async-aware", stderr) + + def test_cli_dump_rejects_async_aware_with_native(self): + cm, stderr = self._run_dump_cli_expecting_exit( + "--async-aware", "--native", "12345" + ) + self.assertEqual(cm.exception.code, 2) + self.assertIn("--native", stderr) + self.assertIn("incompatible with --async-aware", stderr) + + def test_cli_dump_rejects_async_aware_with_no_gc(self): + cm, stderr = self._run_dump_cli_expecting_exit( + "--async-aware", "--no-gc", "12345" + ) + self.assertEqual(cm.exception.code, 2) + self.assertIn("--no-gc", stderr) + self.assertIn("incompatible with --async-aware", stderr) + + def test_cli_dump_unknown_process(self): + with self.assertRaises(SamplingUnknownProcessError): + self._run_dump_cli("12345", process_running=False) + + def test_cli_dump_process_exits_before_snapshot(self): + with self.assertRaises(SystemExit) as cm: + self._run_dump_cli("12345", dump_side_effect=ProcessLookupError) + self.assertIn( + "No stack dump collected - process 12345 exited", + str(cm.exception.code), + ) + + @force_not_colorized + def test_cli_dump_help_lists_dump_options_without_mode(self): + cm, stdout = self._run_dump_cli_expecting_exit("--help", capture="stdout") + self.assertEqual(cm.exception.code, 0) + self.assertIn("--async-mode {running,all}", stdout) + self.assertIn("--opcodes", stdout) + self.assertNotIn("--mode {wall,cpu,gil,exception}", stdout) + + def test_sort_options(self): + sort_options = [ + ("nsamples", 0), + ("tottime", 1), + ("cumtime", 2), + ("sample-pct", 3), + ("cumul-pct", 4), + ("name", -1), + ] + + for option, expected_sort_value in sort_options: + test_args = ["profiling.sampling.cli", "attach", "12345", "--sort", option] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + main() + + mock_sample.assert_called_once() + mock_sample.reset_mock() + + def test_async_aware_flag_defaults_to_running(self): + """Test --async-aware flag enables async profiling with default 'running' mode.""" + test_args = ["profiling.sampling.cli", "attach", "12345", "--async-aware"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + main() + + mock_sample.assert_called_once() + # Verify async_aware was passed with default "running" mode + call_kwargs = mock_sample.call_args[1] + self.assertEqual(call_kwargs.get("async_aware"), "running") + + def test_async_aware_with_async_mode_all(self): + """Test --async-aware with --async-mode all.""" + test_args = ["profiling.sampling.cli", "attach", "12345", "--async-aware", "--async-mode", "all"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + main() + + mock_sample.assert_called_once() + call_kwargs = mock_sample.call_args[1] + self.assertEqual(call_kwargs.get("async_aware"), "all") + + def test_async_aware_default_is_none(self): + """Test async_aware defaults to None when --async-aware not specified.""" + test_args = ["profiling.sampling.cli", "attach", "12345"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + main() + + mock_sample.assert_called_once() + call_kwargs = mock_sample.call_args[1] + self.assertIsNone(call_kwargs.get("async_aware")) + + def test_async_mode_invalid_choice(self): + """Test --async-mode with invalid choice raises error.""" + test_args = ["profiling.sampling.cli", "attach", "12345", "--async-aware", "--async-mode", "invalid"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()), + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + + def test_async_mode_requires_async_aware(self): + """Test --async-mode without --async-aware raises error.""" + test_args = ["profiling.sampling.cli", "attach", "12345", "--async-mode", "all"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + error_msg = mock_stderr.getvalue() + self.assertIn("--async-mode requires --async-aware", error_msg) + + def test_async_aware_incompatible_with_native(self): + """Test --async-aware is incompatible with --native.""" + test_args = ["profiling.sampling.cli", "attach", "12345", "--async-aware", "--native"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + error_msg = mock_stderr.getvalue() + self.assertIn("--native", error_msg) + self.assertIn("incompatible with --async-aware", error_msg) + + def test_async_aware_incompatible_with_no_gc(self): + """Test --async-aware is incompatible with --no-gc.""" + test_args = ["profiling.sampling.cli", "attach", "12345", "--async-aware", "--no-gc"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + error_msg = mock_stderr.getvalue() + self.assertIn("--no-gc", error_msg) + self.assertIn("incompatible with --async-aware", error_msg) + + def test_async_aware_incompatible_with_both_native_and_no_gc(self): + """Test --async-aware is incompatible with both --native and --no-gc.""" + test_args = ["profiling.sampling.cli", "attach", "12345", "--async-aware", "--native", "--no-gc"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + error_msg = mock_stderr.getvalue() + self.assertIn("--native", error_msg) + self.assertIn("--no-gc", error_msg) + self.assertIn("incompatible with --async-aware", error_msg) + + def test_async_aware_incompatible_with_mode(self): + """Test --async-aware is incompatible with --mode (non-wall).""" + test_args = ["profiling.sampling.cli", "attach", "12345", "--async-aware", "--mode", "cpu"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + error_msg = mock_stderr.getvalue() + self.assertIn("--mode=cpu", error_msg) + self.assertIn("incompatible with --async-aware", error_msg) + + def test_async_aware_incompatible_with_all_threads(self): + """Test --async-aware is incompatible with --all-threads.""" + test_args = ["profiling.sampling.cli", "attach", "12345", "--async-aware", "--all-threads"] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + error_msg = mock_stderr.getvalue() + self.assertIn("--all-threads", error_msg) + self.assertIn("incompatible with --async-aware", error_msg) + + @unittest.skipIf(is_emscripten, "subprocess not available") + def test_run_nonexistent_script_exits_cleanly(self): + """Test that running a non-existent script exits with a clean error.""" + with mock.patch("sys.argv", ["profiling.sampling.cli", "run", "/nonexistent/script.py"]): + with self.assertRaisesRegex(SamplingScriptNotFoundError, "Script '[\\w/.]+' not found."): + main() + + @unittest.skipIf(is_emscripten, "subprocess not available") + def test_run_nonexistent_module_exits_cleanly(self): + """Test that running a non-existent module exits with a clean error.""" + with mock.patch("sys.argv", ["profiling.sampling.cli", "run", "-m", "nonexistent_module_xyz"]): + with self.assertRaisesRegex(SamplingModuleNotFoundError, "Module '[\\w/.]+' not found."): + main() + + @unittest.skipIf(is_emscripten, "subprocess not available") + def test_cli_attach_nonexistent_pid(self): + fake_pid = "99999" + with mock.patch("sys.argv", ["profiling.sampling.cli", "attach", fake_pid]): + with self.assertRaises(SamplingUnknownProcessError) as cm: + main() + + self.assertIn(fake_pid, str(cm.exception)) + + def test_cli_replay_rejects_non_binary_profile(self): + with tempfile.TemporaryDirectory() as tempdir: + profile = os.path.join(tempdir, "output.prof") + with open(profile, "wb") as file: + file.write(b"not a binary sampling profile") + + with mock.patch("sys.argv", ["profiling.sampling.cli", "replay", profile]): + with self.assertRaises(SystemExit) as cm: + main() + + error = str(cm.exception) + self.assertIn("not a binary sampling profile", error) + self.assertIn("--binary", error) + + def test_cli_replay_reader_errors_exit_cleanly(self): + with tempfile.TemporaryDirectory() as tempdir: + profile = os.path.join(tempdir, "output.bin") + with open(profile, "wb") as file: + file.write(b"HCAT" + (b"\0" * 60)) + + with ( + mock.patch("sys.argv", ["profiling.sampling.cli", "replay", profile]), + mock.patch( + "profiling.sampling.cli.BinaryReader", + side_effect=ValueError("Unsupported format version 2"), + ), + ): + with self.assertRaises(SystemExit) as cm: + main() + + self.assertEqual( + str(cm.exception), + "Error: Unsupported format version 2", + ) + + def test_cli_jsonl_format_mutually_exclusive_with_pstats(self): + """--jsonl and --pstats cannot be combined (mutually exclusive group).""" + with ( + mock.patch( + "sys.argv", + [ + "profiling.sampling.cli", + "attach", + "12345", + "--jsonl", + "--pstats", + ], + ), + mock.patch("sys.stderr", io.StringIO()), + ): + with self.assertRaises(SystemExit): + main() + + def test_cli_jsonl_extension_in_format_extensions(self): + """FORMAT_EXTENSIONS maps 'jsonl' -> 'jsonl' so default filenames work.""" + self.assertEqual(FORMAT_EXTENSIONS["jsonl"], "jsonl") + self.assertEqual(_generate_output_filename("jsonl", 12345), "jsonl_12345.jsonl") + + def test_cli_jsonl_create_collector_propagates_mode(self): + """_create_collector('jsonl', ..., mode=X) lands X in the meta record.""" + collector = _create_collector( + "jsonl", + sample_interval_usec=1000, + skip_idle=False, + mode=PROFILING_MODE_CPU, + ) + self.assertIsInstance(collector, JsonlCollector) + + with tempfile.NamedTemporaryFile(suffix=".jsonl", delete=False) as f: + jsonl_path = f.name + self.addCleanup(os.unlink, jsonl_path) + collector.export(jsonl_path) + with open(jsonl_path, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + meta = next(r for r in records if r["type"] == "meta") + self.assertEqual(meta["mode"], "cpu") + + def test_cli_jsonl_rejects_opcodes_combination(self): + """--opcodes is incompatible with --jsonl per opcodes_compatible_formats.""" + test_args = [ + "profiling.sampling.cli", + "attach", + "12345", + "--jsonl", + "--opcodes", + ] + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + mock.patch("profiling.sampling.cli.sample"), + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) + self.assertIn("--opcodes", mock_stderr.getvalue()) diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py b/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py new file mode 100644 index 00000000000000..1ab31af67fec52 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_collectors.py @@ -0,0 +1,3138 @@ +"""Tests for sampling profiler collector components.""" + +import json +import marshal +import opcode +import os +import tempfile +import unittest + +from test.support import is_emscripten + +try: + import _remote_debugging # noqa: F401 + from profiling.sampling import gecko_collector + from profiling.sampling.pstats_collector import PstatsCollector + from profiling.sampling.stack_collector import ( + CollapsedStackCollector, + FlamegraphCollector, + ) + from profiling.sampling.jsonl_collector import JsonlCollector + from profiling.sampling.gecko_collector import GeckoCollector + from profiling.sampling.heatmap_collector import _TemplateLoader + from profiling.sampling.collector import extract_lineno, normalize_location + from profiling.sampling.opcode_utils import get_opcode_info, format_opcode + from profiling.sampling.constants import ( + PROFILING_MODE_WALL, + PROFILING_MODE_CPU, + DEFAULT_LOCATION, + ) + from _remote_debugging import ( + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, + THREAD_STATUS_GIL_REQUESTED, + THREAD_STATUS_MAIN_THREAD, + ) +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) + +from test.support import captured_stdout, captured_stderr + +from .mocks import MockFrameInfo, MockThreadInfo, MockInterpreterInfo, LocationInfo, make_diff_collector_with_mock_baseline +from .helpers import close_and_unlink, jsonl_tables + + +def resolve_name(node, strings): + """Resolve a flamegraph node's name from the string table.""" + idx = node.get("name", 0) + if isinstance(idx, int) and 0 <= idx < len(strings): + return strings[idx] + return str(idx) + + +def find_child_by_name(children, strings, substr): + """Find a child node whose resolved name contains substr.""" + for child in children: + if substr in resolve_name(child, strings): + return child + return None + + +def export_gecko_profile(testcase, collector): + gecko_out = tempfile.NamedTemporaryFile(suffix=".json", delete=False) + testcase.addCleanup(close_and_unlink, gecko_out) + # We cannot overwrite an open file on Windows. + gecko_out.close() + + with captured_stdout(), captured_stderr(): + collector.export(gecko_out.name) + + testcase.assertGreater(os.path.getsize(gecko_out.name), 0) + with open(gecko_out.name, encoding="utf-8") as file: + return json.load(file) + + +def assert_gecko_column_lengths(testcase, table, columns): + expected = table["length"] + for column in columns: + testcase.assertEqual( + len(table[column]), expected, + f"{column!r} has wrong length", + ) + + +def gecko_marker_names(profile, markers): + string_array = profile["shared"]["stringArray"] + return [string_array[idx] for idx in markers["name"]] + + +def gecko_opcode_marker_data(profile): + markers = profile["threads"][0]["markers"] + return [ + data for data in markers["data"] + if data.get("type") == "Opcode" + ] + + +class TestSampleProfilerComponents(unittest.TestCase): + """Unit tests for individual profiler components.""" + + def test_mock_frame_info_with_empty_and_unicode_values(self): + """Test MockFrameInfo handles empty strings, unicode characters, and very long names correctly.""" + # Test with empty strings + frame = MockFrameInfo("", 0, "") + self.assertEqual(frame.filename, "") + self.assertEqual(frame.location.lineno, 0) + self.assertEqual(frame.funcname, "") + + # Test with unicode characters + frame = MockFrameInfo("文件.py", 42, "函数名") + self.assertEqual(frame.filename, "文件.py") + self.assertEqual(frame.funcname, "函数名") + + # Test with very long names + long_filename = "x" * 1000 + ".py" + long_funcname = "func_" + "x" * 1000 + frame = MockFrameInfo(long_filename, 999999, long_funcname) + self.assertEqual(frame.filename, long_filename) + self.assertEqual(frame.location.lineno, 999999) + self.assertEqual(frame.funcname, long_funcname) + + def test_heatmap_navigation_restarts_line_highlight(self): + """Test heatmap navigation can replay target line highlights.""" + loader = _TemplateLoader() + + self.assertIn(".code-line:target", loader.file_css) + self.assertIn("function restartLineHighlight(target)", loader.file_js) + self.assertIn("target.style.animation = 'none'", loader.file_js) + self.assertIn("void target.offsetWidth", loader.file_js) + self.assertIn("url.href === window.location.href", loader.file_js) + self.assertIn("navigateToLine(JSON.parse(navData).link)", loader.file_js) + self.assertIn("navigateToLine(linkData.link)", loader.file_js) + + def test_pstats_collector_with_extreme_intervals_and_empty_data(self): + """Test PstatsCollector handles zero/large intervals, empty frames, None thread IDs, and duplicate frames.""" + # Test with zero interval + collector = PstatsCollector(sample_interval_usec=0) + self.assertEqual(collector.sample_interval_usec, 0) + + # Test with very large interval + collector = PstatsCollector(sample_interval_usec=1000000000) + self.assertEqual(collector.sample_interval_usec, 1000000000) + + # Test collecting empty frames list + collector = PstatsCollector(sample_interval_usec=1000) + collector.collect([]) + self.assertEqual(len(collector.result), 0) + + # Test collecting frames with None thread id + test_frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(None, [MockFrameInfo("file.py", 10, "func", None)])], + ) + ] + collector.collect(test_frames) + # Should still process the frames + self.assertEqual(len(collector.result), 1) + + # Test collecting duplicate frames in same sample (recursive function) + test_frames = [ + MockInterpreterInfo( + 0, # interpreter_id + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 10, "func1"), # Duplicate (recursion) + ], + ) + ], + ) + ] + collector = PstatsCollector(sample_interval_usec=1000) + collector.collect(test_frames) + # Should count only once per sample to avoid over-counting recursive functions + self.assertEqual( + collector.result[("file.py", 10, "func1")]["cumulative_calls"], 1 + ) + + def test_pstats_collector_single_frame_stacks(self): + """Test PstatsCollector with single-frame call stacks to trigger len(frames) <= 1 branch.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Test with exactly one frame (should trigger the <= 1 condition) + single_frame = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("single.py", 10, "single_func")] + ) + ], + ) + ] + collector.collect(single_frame) + + # Should record the single frame with inline call + self.assertEqual(len(collector.result), 1) + single_key = ("single.py", 10, "single_func") + self.assertIn(single_key, collector.result) + self.assertEqual(collector.result[single_key]["direct_calls"], 1) + self.assertEqual(collector.result[single_key]["cumulative_calls"], 1) + + # Test with empty frames (should also trigger <= 1 condition) + empty_frames = [MockInterpreterInfo(0, [MockThreadInfo(1, [])])] + collector.collect(empty_frames) + + # Should not add any new entries + self.assertEqual( + len(collector.result), 1 + ) # Still just the single frame + + # Test mixed single and multi-frame stacks + mixed_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("single2.py", 20, "single_func2")], + ), # Single frame + MockThreadInfo( + 2, + [ # Multi-frame stack + MockFrameInfo("multi.py", 30, "multi_func1"), + MockFrameInfo("multi.py", 40, "multi_func2"), + ], + ), + ], + ), + ] + collector.collect(mixed_frames) + + # Should have recorded all functions + self.assertEqual( + len(collector.result), 4 + ) # single + single2 + multi1 + multi2 + + # Verify single frame handling + single2_key = ("single2.py", 20, "single_func2") + self.assertIn(single2_key, collector.result) + self.assertEqual(collector.result[single2_key]["direct_calls"], 1) + self.assertEqual(collector.result[single2_key]["cumulative_calls"], 1) + + # Verify multi-frame handling still works + multi1_key = ("multi.py", 30, "multi_func1") + multi2_key = ("multi.py", 40, "multi_func2") + self.assertIn(multi1_key, collector.result) + self.assertIn(multi2_key, collector.result) + self.assertEqual(collector.result[multi1_key]["direct_calls"], 1) + self.assertEqual( + collector.result[multi2_key]["cumulative_calls"], 1 + ) # Called from multi1 + + def test_collapsed_stack_collector_with_empty_and_deep_stacks(self): + """Test CollapsedStackCollector handles empty frames, single-frame stacks, and very deep call stacks.""" + collector = CollapsedStackCollector(1000) + + # Test with empty frames + collector.collect([]) + self.assertEqual(len(collector.stack_counter), 0) + + # Test with single frame stack + test_frames = [ + MockInterpreterInfo( + 0, [MockThreadInfo(1, [MockFrameInfo("file.py", 10, "func")])] + ) + ] + collector.collect(test_frames) + self.assertEqual(len(collector.stack_counter), 1) + (((path, thread_id), count),) = collector.stack_counter.items() + self.assertEqual(path, (("file.py", 10, "func"),)) + self.assertEqual(thread_id, 1) + self.assertEqual(count, 1) + + # Test with very deep stack + deep_stack = [MockFrameInfo(f"file{i}.py", i, f"func{i}") for i in range(100)] + test_frames = [MockInterpreterInfo(0, [MockThreadInfo(1, deep_stack)])] + collector = CollapsedStackCollector(1000) + collector.collect(test_frames) + # One aggregated path with 100 frames (reversed) + (((path_tuple, thread_id),),) = (collector.stack_counter.keys(),) + self.assertEqual(len(path_tuple), 100) + self.assertEqual(path_tuple[0], ("file99.py", 99, "func99")) + self.assertEqual(path_tuple[-1], ("file0.py", 0, "func0")) + self.assertEqual(thread_id, 1) + + def test_pstats_collector_basic(self): + """Test basic PstatsCollector functionality.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Test empty state + self.assertEqual(len(collector.result), 0) + self.assertEqual(len(collector.stats), 0) + + # Test collecting sample data + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ], + ) + ], + ) + ] + collector.collect(test_frames) + + # Should have recorded calls for both functions + self.assertEqual(len(collector.result), 2) + self.assertIn(("file.py", 10, "func1"), collector.result) + self.assertIn(("file.py", 20, "func2"), collector.result) + + # Top-level function should have direct call + self.assertEqual( + collector.result[("file.py", 10, "func1")]["direct_calls"], 1 + ) + self.assertEqual( + collector.result[("file.py", 10, "func1")]["cumulative_calls"], 1 + ) + + # Calling function should have cumulative call but no direct calls + self.assertEqual( + collector.result[("file.py", 20, "func2")]["cumulative_calls"], 1 + ) + self.assertEqual( + collector.result[("file.py", 20, "func2")]["direct_calls"], 0 + ) + + def test_pstats_collector_create_stats(self): + """Test PstatsCollector stats creation.""" + collector = PstatsCollector( + sample_interval_usec=1000000 + ) # 1 second intervals + + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ], + ) + ], + ) + ] + collector.collect(test_frames) + collector.collect(test_frames) # Collect twice + + collector.create_stats() + + # Check stats format: (direct_calls, cumulative_calls, tt, ct, callers) + func1_stats = collector.stats[("file.py", 10, "func1")] + self.assertEqual(func1_stats[0], 2) # direct_calls (top of stack) + self.assertEqual(func1_stats[1], 2) # cumulative_calls + self.assertEqual( + func1_stats[2], 2.0 + ) # tt (total time - 2 samples * 1 sec) + self.assertEqual(func1_stats[3], 2.0) # ct (cumulative time) + + func2_stats = collector.stats[("file.py", 20, "func2")] + self.assertEqual( + func2_stats[0], 0 + ) # direct_calls (never top of stack) + self.assertEqual( + func2_stats[1], 2 + ) # cumulative_calls (appears in stack) + self.assertEqual(func2_stats[2], 0.0) # tt (no direct calls) + self.assertEqual(func2_stats[3], 2.0) # ct (cumulative time) + + def test_collapsed_stack_collector_basic(self): + collector = CollapsedStackCollector(1000) + + # Test empty state + self.assertEqual(len(collector.stack_counter), 0) + + # Test collecting sample data + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")] + ) + ], + ) + ] + collector.collect(test_frames) + + # Should store one reversed path + self.assertEqual(len(collector.stack_counter), 1) + (((path, thread_id), count),) = collector.stack_counter.items() + expected_tree = (("file.py", 20, "func2"), ("file.py", 10, "func1")) + self.assertEqual(path, expected_tree) + self.assertEqual(thread_id, 1) + self.assertEqual(count, 1) + + def test_collapsed_stack_collector_export(self): + collapsed_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, collapsed_out) + + collector = CollapsedStackCollector(1000) + + test_frames1 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")] + ) + ], + ) + ] + test_frames2 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")] + ) + ], + ) + ] # Same stack + test_frames3 = [ + MockInterpreterInfo( + 0, [MockThreadInfo(1, [MockFrameInfo("other.py", 5, "other_func")])] + ) + ] + + collector.collect(test_frames1) + collector.collect(test_frames2) + collector.collect(test_frames3) + + with captured_stdout(), captured_stderr(): + collector.export(collapsed_out.name) + # Check file contents + with open(collapsed_out.name, "r") as f: + content = f.read() + + lines = content.strip().split("\n") + self.assertEqual(len(lines), 2) # Two unique stacks + + # Check collapsed format: tid:X;file:func:line;file:func:line count + stack1_expected = "tid:1;file.py:func2:20;file.py:func1:10 2" + stack2_expected = "tid:1;other.py:other_func:5 1" + + self.assertIn(stack1_expected, lines) + self.assertIn(stack2_expected, lines) + + def test_flamegraph_collector_basic(self): + """Test basic FlamegraphCollector functionality.""" + collector = FlamegraphCollector(1000) + + # Empty collector should produce 'No Data' + data = collector._convert_to_flamegraph_format() + # With string table, name is now an index - resolve it using the strings array + strings = data.get("strings", []) + self.assertIn(resolve_name(data, strings), ("No Data", "No significant data")) + + # Test collecting sample data + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")] + ) + ], + ) + ] + collector.collect(test_frames) + + # Convert and verify structure: func2 -> func1 with counts = 1 + data = collector._convert_to_flamegraph_format() + # Expect promotion: root is the single child (func2), with func1 as its only child + strings = data.get("strings", []) + name = resolve_name(data, strings) + self.assertTrue(name.startswith("Program Root: ")) + self.assertIn("func2 (file.py:20)", name) + label = strings[data["label"]] + self.assertTrue(label.startswith("Program Root: ")) + self.assertEqual(data["self"], 0) # non-leaf: no self time + children = data.get("children", []) + self.assertEqual(len(children), 1) + child = children[0] + self.assertIn("func1 (file.py:10)", resolve_name(child, strings)) + self.assertEqual(child["value"], 1) + self.assertEqual(child["self"], 1) # leaf: all time is self + + def test_flamegraph_collector_export(self): + """Test flamegraph HTML export functionality.""" + flamegraph_out = tempfile.NamedTemporaryFile( + suffix=".html", delete=False + ) + self.addCleanup(close_and_unlink, flamegraph_out) + + collector = FlamegraphCollector(1000) + + # Create some test data (use Interpreter/Thread objects like runtime) + test_frames1 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")] + ) + ], + ) + ] + test_frames2 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")] + ) + ], + ) + ] # Same stack + test_frames3 = [ + MockInterpreterInfo( + 0, [MockThreadInfo(1, [MockFrameInfo("other.py", 5, "other_func")])] + ) + ] + + collector.collect(test_frames1) + collector.collect(test_frames2) + collector.collect(test_frames3) + + # Export flamegraph + with captured_stdout(), captured_stderr(): + collector.export(flamegraph_out.name) + + # Verify file was created and contains valid data + self.assertTrue(os.path.exists(flamegraph_out.name)) + self.assertGreater(os.path.getsize(flamegraph_out.name), 0) + + # Check file contains HTML content + with open(flamegraph_out.name, "r", encoding="utf-8") as f: + content = f.read() + + # Should be valid HTML + self.assertIn("<!doctype html>", content.lower()) + self.assertIn("<html", content) + self.assertIn("Tachyon Profiler - Flamegraph", content) + self.assertIn("d3-flame-graph", content) + + # Should contain the data + self.assertIn('"name":', content) + self.assertIn('"value":', content) + self.assertIn('"children":', content) + + def test_gecko_collector_basic(self): + """Test basic GeckoCollector functionality.""" + collector = GeckoCollector(1000) + + # Test empty state + self.assertEqual(len(collector.threads), 0) + self.assertEqual(collector.sample_count, 0) + self.assertEqual(len(collector.global_strings), 1) # "(root)" + + # Test collecting sample data + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")], + status=THREAD_STATUS_MAIN_THREAD, + ) + ], + ) + ] + collector.collect(test_frames) + + # Should have recorded one thread and one sample + self.assertEqual(len(collector.threads), 1) + self.assertEqual(collector.sample_count, 1) + self.assertIn(1, collector.threads) + + profile_data = collector._build_profile() + + # Verify profile structure + self.assertIn("meta", profile_data) + self.assertIn("threads", profile_data) + self.assertIn("shared", profile_data) + + # Check shared string table + shared = profile_data["shared"] + self.assertIn("stringArray", shared) + string_array = shared["stringArray"] + self.assertGreater(len(string_array), 0) + + # Should contain our functions in the string array + self.assertIn("func1", string_array) + self.assertIn("func2", string_array) + + # Check thread data structure + threads = profile_data["threads"] + self.assertEqual(len(threads), 1) + thread_data = threads[0] + self.assertTrue(thread_data["isMainThread"]) + + # Verify thread structure + self.assertIn("samples", thread_data) + self.assertIn("funcTable", thread_data) + self.assertIn("frameTable", thread_data) + self.assertIn("stackTable", thread_data) + + # Verify samples + samples = thread_data["samples"] + self.assertEqual(samples["length"], 1) + assert_gecko_column_lengths( + self, samples, ("stack", "time", "eventDelay") + ) + + # Verify function table structure and content + func_table = thread_data["funcTable"] + self.assertIn("name", func_table) + self.assertIn("fileName", func_table) + self.assertIn("lineNumber", func_table) + self.assertEqual(func_table["length"], 2) # Should have 2 functions + + # Verify actual function content through string array indices + func_names = [] + for idx in func_table["name"]: + func_name = ( + string_array[idx] + if isinstance(idx, int) and 0 <= idx < len(string_array) + else str(idx) + ) + func_names.append(func_name) + + self.assertIn("func1", func_names, f"func1 not found in {func_names}") + self.assertIn("func2", func_names, f"func2 not found in {func_names}") + + # Verify frame table + frame_table = thread_data["frameTable"] + self.assertEqual( + frame_table["length"], 2 + ) # Should have frames for both functions + self.assertEqual(len(frame_table["func"]), 2) + + # Verify stack structure + stack_table = thread_data["stackTable"] + self.assertGreater(stack_table["length"], 0) + self.assertGreater(len(stack_table["frame"]), 0) + + @unittest.skipIf(is_emscripten, "threads not available") + def test_gecko_collector_export(self): + """Test Gecko profile export functionality.""" + collector = GeckoCollector(1000) + + test_frames1 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")] + ) + ], + ) + ] + test_frames2 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("file.py", 10, "func1"), MockFrameInfo("file.py", 20, "func2")] + ) + ], + ) + ] # Same stack + test_frames3 = [ + MockInterpreterInfo( + 0, [MockThreadInfo(1, [MockFrameInfo("other.py", 5, "other_func")])] + ) + ] + + collector.collect(test_frames1) + collector.collect(test_frames2) + collector.collect(test_frames3) + + profile_data = export_gecko_profile(self, collector) + + # Should be valid Gecko profile format + self.assertIn("meta", profile_data) + self.assertIn("threads", profile_data) + self.assertIn("shared", profile_data) + + # Check meta information + self.assertIn("categories", profile_data["meta"]) + self.assertIn("interval", profile_data["meta"]) + + # Check shared string table + self.assertIn("stringArray", profile_data["shared"]) + self.assertGreater(len(profile_data["shared"]["stringArray"]), 0) + + # Should contain our functions + string_array = profile_data["shared"]["stringArray"] + self.assertIn("func1", string_array) + self.assertIn("func2", string_array) + self.assertIn("other_func", string_array) + + thread_data = profile_data["threads"][0] + assert_gecko_column_lengths( + self, thread_data["samples"], ("stack", "time", "eventDelay") + ) + + @unittest.skipIf(is_emscripten, "threads not available") + def test_gecko_collector_export_after_spill_flush(self): + """Test Gecko profile export after spill buffers flush to disk.""" + old_buffer_bytes = gecko_collector.DEFAULT_SPILL_BUFFER_BYTES + gecko_collector.DEFAULT_SPILL_BUFFER_BYTES = 1 + self.addCleanup( + setattr, gecko_collector, "DEFAULT_SPILL_BUFFER_BYTES", + old_buffer_bytes + ) + + collector = GeckoCollector(1000) + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("file.py", 10, "func")], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + collector.collect(test_frames, timestamps_us=[1000, 2000, 3000]) + + profile_data = export_gecko_profile(self, collector) + samples = profile_data["threads"][0]["samples"] + self.assertEqual(samples["length"], 3) + assert_gecko_column_lengths( + self, samples, ("stack", "time", "eventDelay") + ) + + @unittest.skipIf(is_emscripten, "threads not available") + def test_gecko_collector_rejects_collect_after_export(self): + collector = GeckoCollector(1000) + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("file.py", 10, "func")], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + collector.collect(test_frames) + export_gecko_profile(self, collector) + + with self.assertRaisesRegex(RuntimeError, "after export"): + collector.collect(test_frames) + + @unittest.skipIf(is_emscripten, "threads not available") + def test_gecko_collector_export_failure_keeps_existing_file(self): + collector = GeckoCollector(1000) + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("file.py", 10, "func")], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + collector.collect(test_frames) + + with tempfile.TemporaryDirectory() as temp_dir: + filename = os.path.join(temp_dir, "profile.json") + with open(filename, "w", encoding="utf-8") as file: + file.write("existing") + + before = set(os.listdir(temp_dir)) + + def fail(file): + raise OSError("boom") + + collector._stream_profile = fail + with captured_stdout(), captured_stderr(): + with self.assertRaisesRegex(OSError, "boom"): + collector.export(filename) + + with open(filename, encoding="utf-8") as file: + self.assertEqual(file.read(), "existing") + self.assertEqual(set(os.listdir(temp_dir)), before) + + def test_gecko_collector_markers(self): + """Test Gecko profile markers for GIL and CPU state tracking.""" + collector = GeckoCollector(1000) + + # Status combinations for different thread states + HAS_GIL_ON_CPU = ( + THREAD_STATUS_HAS_GIL | THREAD_STATUS_ON_CPU + ) # Running Python code + NO_GIL_ON_CPU = THREAD_STATUS_ON_CPU # Running native code + WAITING_FOR_GIL = THREAD_STATUS_GIL_REQUESTED # Waiting for GIL + + # Simulate thread state transitions + collector.collect( + [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("test.py", 10, "python_func")], + status=HAS_GIL_ON_CPU, + ) + ], + ) + ] + ) + + collector.collect( + [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("test.py", 15, "wait_func")], + status=WAITING_FOR_GIL, + ) + ], + ) + ] + ) + + collector.collect( + [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("test.py", 20, "python_func2")], + status=HAS_GIL_ON_CPU, + ) + ], + ) + ] + ) + + collector.collect( + [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("native.c", 100, "native_func")], + status=NO_GIL_ON_CPU, + ) + ], + ) + ] + ) + + profile_data = collector._build_profile() + + # Verify we have threads with markers + self.assertIn("threads", profile_data) + self.assertEqual(len(profile_data["threads"]), 1) + thread_data = profile_data["threads"][0] + + # Check markers exist + self.assertIn("markers", thread_data) + markers = thread_data["markers"] + + self.assertGreater( + markers["length"], 0, "Should have generated markers" + ) + assert_gecko_column_lengths( + self, markers, + ("data", "name", "startTime", "endTime", "phase", "category"), + ) + + # Verify we have different marker types + marker_name_set = set(gecko_marker_names(profile_data, markers)) + + # Should have "Has GIL" markers (when thread had GIL) + self.assertIn( + "Has GIL", marker_name_set, "Should have 'Has GIL' markers" + ) + + # Should have "No GIL" markers (when thread didn't have GIL) + self.assertIn( + "No GIL", marker_name_set, "Should have 'No GIL' markers" + ) + + # Should have "On CPU" markers (when thread was on CPU) + self.assertIn( + "On CPU", marker_name_set, "Should have 'On CPU' markers" + ) + + # Should have "Waiting for GIL" markers (when thread was waiting) + self.assertIn( + "Waiting for GIL", + marker_name_set, + "Should have 'Waiting for GIL' markers", + ) + + # Verify marker structure + for i in range(markers["length"]): + # All markers should be interval markers (phase = 1) + self.assertEqual( + markers["phase"][i], 1, f"Marker {i} should be interval marker" + ) + + # All markers should have valid time range + start_time = markers["startTime"][i] + end_time = markers["endTime"][i] + self.assertLessEqual( + start_time, + end_time, + f"Marker {i} should have valid time range", + ) + + # All markers should have valid category + self.assertGreaterEqual( + markers["category"][i], + 0, + f"Marker {i} should have valid category", + ) + + def test_pstats_collector_export(self): + collector = PstatsCollector( + sample_interval_usec=1000000 + ) # 1 second intervals + + test_frames1 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ], + ) + ], + ) + ] + test_frames2 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ], + ) + ], + ) + ] # Same stack + test_frames3 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("other.py", 5, "other_func")] + ) + ], + ) + ] + + collector.collect(test_frames1) + collector.collect(test_frames2) + collector.collect(test_frames3) + + pstats_out = tempfile.NamedTemporaryFile( + suffix=".pstats", delete=False + ) + self.addCleanup(close_and_unlink, pstats_out) + collector.export(pstats_out.name) + + # Check file can be loaded with marshal + with open(pstats_out.name, "rb") as f: + stats_data = marshal.load(f) + + # Should be a dictionary with the sampled marker + self.assertIsInstance(stats_data, dict) + self.assertIn(("__sampled__",), stats_data) + self.assertTrue(stats_data[("__sampled__",)]) + + # Should have function data + function_entries = [ + k for k in stats_data.keys() if k != ("__sampled__",) + ] + self.assertGreater(len(function_entries), 0) + + # Check specific function stats format: (cc, nc, tt, ct, callers) + func1_key = ("file.py", 10, "func1") + func2_key = ("file.py", 20, "func2") + other_key = ("other.py", 5, "other_func") + + self.assertIn(func1_key, stats_data) + self.assertIn(func2_key, stats_data) + self.assertIn(other_key, stats_data) + + # Check func1 stats (should have 2 samples) + func1_stats = stats_data[func1_key] + self.assertEqual(func1_stats[0], 2) # total_calls + self.assertEqual(func1_stats[1], 2) # nc (non-recursive calls) + self.assertEqual(func1_stats[2], 2.0) # tt (total time) + self.assertEqual(func1_stats[3], 2.0) # ct (cumulative time) + + def test_flamegraph_collector_stats_accumulation(self): + """Test that FlamegraphCollector accumulates stats across samples.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + # First sample + stack_frames_1 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("a.py", 1, "func_a")], status=THREAD_STATUS_HAS_GIL), + MockThreadInfo(2, [MockFrameInfo("b.py", 2, "func_b")], status=THREAD_STATUS_ON_CPU), + ], + ) + ] + collector.collect(stack_frames_1) + self.assertEqual(collector.thread_status_counts["has_gil"], 1) + self.assertEqual(collector.thread_status_counts["on_cpu"], 1) + self.assertEqual(collector.thread_status_counts["total"], 2) + + # Second sample + stack_frames_2 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("a.py", 1, "func_a")], status=THREAD_STATUS_GIL_REQUESTED), + MockThreadInfo(2, [MockFrameInfo("b.py", 2, "func_b")], status=THREAD_STATUS_HAS_GIL), + MockThreadInfo(3, [MockFrameInfo("c.py", 3, "func_c")], status=THREAD_STATUS_ON_CPU), + ], + ) + ] + collector.collect(stack_frames_2) + + # Should accumulate + self.assertEqual(collector.thread_status_counts["has_gil"], 2) # 1 + 1 + self.assertEqual(collector.thread_status_counts["on_cpu"], 2) # 1 + 1 + self.assertEqual(collector.thread_status_counts["gil_requested"], 1) # 0 + 1 + self.assertEqual(collector.thread_status_counts["total"], 5) # 2 + 3 + + # Test GC sample tracking + stack_frames_gc = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("~", 0, "<GC>")], status=THREAD_STATUS_HAS_GIL), + ], + ) + ] + collector.collect(stack_frames_gc) + self.assertEqual(collector.samples_with_gc_frames, 1) + + # Another sample without GC + collector.collect(stack_frames_1) + self.assertEqual(collector.samples_with_gc_frames, 1) # Still 1 + + # Another GC sample + collector.collect(stack_frames_gc) + self.assertEqual(collector.samples_with_gc_frames, 2) + + def test_flamegraph_collector_per_thread_stats(self): + """Test per-thread statistics tracking in FlamegraphCollector.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + # Multiple threads with different states + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("a.py", 1, "func_a")], status=THREAD_STATUS_HAS_GIL), + MockThreadInfo(2, [MockFrameInfo("b.py", 2, "func_b")], status=THREAD_STATUS_ON_CPU), + MockThreadInfo(3, [MockFrameInfo("c.py", 3, "func_c")], status=THREAD_STATUS_GIL_REQUESTED), + ], + ) + ] + collector.collect(stack_frames) + + # Check per-thread stats + self.assertIn(1, collector.per_thread_stats) + self.assertIn(2, collector.per_thread_stats) + self.assertIn(3, collector.per_thread_stats) + + # Thread 1: has GIL + self.assertEqual(collector.per_thread_stats[1]["has_gil"], 1) + self.assertEqual(collector.per_thread_stats[1]["on_cpu"], 0) + self.assertEqual(collector.per_thread_stats[1]["total"], 1) + + # Thread 2: on CPU + self.assertEqual(collector.per_thread_stats[2]["has_gil"], 0) + self.assertEqual(collector.per_thread_stats[2]["on_cpu"], 1) + self.assertEqual(collector.per_thread_stats[2]["total"], 1) + + # Thread 3: waiting + self.assertEqual(collector.per_thread_stats[3]["gil_requested"], 1) + self.assertEqual(collector.per_thread_stats[3]["total"], 1) + + # Test accumulation across samples + stack_frames_2 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("a.py", 2, "func_b")], status=THREAD_STATUS_ON_CPU), + ], + ) + ] + collector.collect(stack_frames_2) + + self.assertEqual(collector.per_thread_stats[1]["has_gil"], 1) + self.assertEqual(collector.per_thread_stats[1]["on_cpu"], 1) + self.assertEqual(collector.per_thread_stats[1]["total"], 2) + + def test_flamegraph_collector_percentage_calculations(self): + """Test that percentage calculations are correct in exported data.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + # Create scenario: 60% GIL held, 40% not held + for i in range(6): + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("a.py", 1, "func")], status=THREAD_STATUS_HAS_GIL), + ], + ) + ] + collector.collect(stack_frames) + + for i in range(4): + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("a.py", 1, "func")], status=THREAD_STATUS_ON_CPU), + ], + ) + ] + collector.collect(stack_frames) + + # Export to get calculated percentages + data = collector._convert_to_flamegraph_format() + thread_stats = data["stats"]["thread_stats"] + + self.assertAlmostEqual(thread_stats["has_gil_pct"], 60.0, places=1) + self.assertAlmostEqual(thread_stats["on_cpu_pct"], 40.0, places=1) + self.assertEqual(thread_stats["total"], 10) + + def test_flamegraph_collector_mode_handling(self): + """Test that profiling mode is correctly passed through to exported data.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + # Collect some data + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("a.py", 1, "func")], status=THREAD_STATUS_HAS_GIL), + ], + ) + ] + collector.collect(stack_frames) + + # Set stats with mode + collector.set_stats( + sample_interval_usec=1000, + duration_sec=1.0, + sample_rate=1000.0, + mode=PROFILING_MODE_CPU + ) + + data = collector._convert_to_flamegraph_format() + self.assertEqual(data["stats"]["mode"], PROFILING_MODE_CPU) + + def test_flamegraph_collector_zero_samples_edge_case(self): + """Test that collector handles zero samples gracefully.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + # Export without collecting any samples + data = collector._convert_to_flamegraph_format() + + # Should return a valid structure with no data + self.assertIn("name", data) + self.assertEqual(data["value"], 0) + self.assertIn("children", data) + self.assertEqual(len(data["children"]), 0) + + def test_flamegraph_collector_json_structure_includes_stats(self): + """Test that exported JSON includes thread_stats and per_thread_stats.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + # Collect some data with multiple threads + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("a.py", 1, "func_a")], status=THREAD_STATUS_HAS_GIL), + MockThreadInfo(2, [MockFrameInfo("b.py", 2, "func_b")], status=THREAD_STATUS_ON_CPU), + ], + ) + ] + collector.collect(stack_frames) + + # Set stats + collector.set_stats( + sample_interval_usec=1000, + duration_sec=1.0, + sample_rate=1000.0, + mode=PROFILING_MODE_WALL + ) + + # Export and verify structure + data = collector._convert_to_flamegraph_format() + + # Check that stats object exists and contains expected fields + self.assertIn("stats", data) + stats = data["stats"] + + # Verify thread_stats exists and has expected structure + self.assertIn("thread_stats", stats) + thread_stats = stats["thread_stats"] + self.assertIn("has_gil_pct", thread_stats) + self.assertIn("on_cpu_pct", thread_stats) + self.assertIn("gil_requested_pct", thread_stats) + self.assertIn("gc_pct", thread_stats) + self.assertIn("total", thread_stats) + + # Verify per_thread_stats exists and has data for both threads + self.assertIn("per_thread_stats", stats) + per_thread_stats = stats["per_thread_stats"] + self.assertIn(1, per_thread_stats) + self.assertIn(2, per_thread_stats) + + # Check per-thread structure + for thread_id in [1, 2]: + thread_data = per_thread_stats[thread_id] + self.assertIn("has_gil_pct", thread_data) + self.assertIn("on_cpu_pct", thread_data) + self.assertIn("gil_requested_pct", thread_data) + self.assertIn("gc_pct", thread_data) + self.assertIn("total", thread_data) + + def test_flamegraph_collector_per_thread_gc_percentage(self): + """Test that per-thread GC percentage uses total samples as denominator.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + # Create 10 samples total: + # - Thread 1 appears in all 10 samples, has GC in 2 of them + # - Thread 2 appears in only 5 samples, has GC in 1 of them + + # First 5 samples: both threads, thread 1 has GC in 2 + for i in range(5): + has_gc = i < 2 # First 2 samples have GC for thread 1 + frames_1 = [MockFrameInfo("~", 0, "<GC>")] if has_gc else [MockFrameInfo("a.py", 1, "func_a")] + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, frames_1, status=THREAD_STATUS_HAS_GIL), + MockThreadInfo(2, [MockFrameInfo("b.py", 2, "func_b")], status=THREAD_STATUS_ON_CPU), + ], + ) + ] + collector.collect(stack_frames) + + # Next 5 samples: only thread 1, thread 2 appears in first of these with GC + for i in range(5): + if i == 0: + # Thread 2 appears in this sample with GC + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("a.py", 1, "func_a")], status=THREAD_STATUS_HAS_GIL), + MockThreadInfo(2, [MockFrameInfo("~", 0, "<GC>")], status=THREAD_STATUS_ON_CPU), + ], + ) + ] + else: + # Only thread 1 + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo(1, [MockFrameInfo("a.py", 1, "func_a")], status=THREAD_STATUS_HAS_GIL), + ], + ) + ] + collector.collect(stack_frames) + + # Set stats and export + collector.set_stats( + sample_interval_usec=1000, + duration_sec=1.0, + sample_rate=1000.0, + mode=PROFILING_MODE_WALL + ) + + data = collector._convert_to_flamegraph_format() + per_thread_stats = data["stats"]["per_thread_stats"] + + # Thread 1: appeared in 10 samples, had GC in 2 + # GC percentage should be 2/10 = 20% (using total samples, not thread appearances) + self.assertEqual(collector.per_thread_stats[1]["gc_samples"], 2) + self.assertEqual(collector.per_thread_stats[1]["total"], 10) + self.assertAlmostEqual(per_thread_stats[1]["gc_pct"], 20.0, places=1) + + # Thread 2: appeared in 6 samples, had GC in 1 + # GC percentage should be 1/10 = 10% (using total samples, not thread appearances) + self.assertEqual(collector.per_thread_stats[2]["gc_samples"], 1) + self.assertEqual(collector.per_thread_stats[2]["total"], 6) + self.assertAlmostEqual(per_thread_stats[2]["gc_pct"], 10.0, places=1) + + def test_diff_flamegraph_identical_profiles(self): + """When baseline and current are identical, diff should be ~0.""" + test_frames = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ]) + ]) + ] + + diff = make_diff_collector_with_mock_baseline([test_frames] * 3) + for _ in range(3): + diff.collect(test_frames) + + data = diff._convert_to_flamegraph_format() + strings = data.get("strings", []) + + self.assertTrue(data["stats"]["is_differential"]) + self.assertEqual(data["stats"]["baseline_samples"], 3) + self.assertEqual(data["stats"]["current_samples"], 3) + self.assertAlmostEqual(data["stats"]["baseline_scale"], 1.0) + + children = data.get("children", []) + self.assertEqual(len(children), 1) + child = children[0] + self.assertIn("func1", resolve_name(child, strings)) + self.assertEqual(child["self_time"], 3) + self.assertAlmostEqual(child["baseline"], 3.0) + self.assertAlmostEqual(child["diff"], 0.0, places=1) + self.assertAlmostEqual(child["diff_pct"], 0.0, places=1) + + self.assertEqual(data["stats"]["elided_count"], 0) + self.assertNotIn("elided_flamegraph", data["stats"]) + + def test_diff_flamegraph_new_function(self): + """A function only in current should have diff_pct=100 and baseline=0.""" + baseline_frames = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ]) + ]) + ] + + diff = make_diff_collector_with_mock_baseline([baseline_frames]) + diff.collect([ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 30, "new_func"), + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ]) + ]) + ]) + + data = diff._convert_to_flamegraph_format() + strings = data.get("strings", []) + + children = data.get("children", []) + self.assertEqual(len(children), 1) + func1_node = children[0] + self.assertIn("func1", resolve_name(func1_node, strings)) + + func1_children = func1_node.get("children", []) + self.assertEqual(len(func1_children), 1) + new_func_node = func1_children[0] + self.assertIn("new_func", resolve_name(new_func_node, strings)) + self.assertEqual(new_func_node["baseline"], 0) + self.assertGreater(new_func_node["self_time"], 0) + self.assertEqual(new_func_node["diff"], new_func_node["self_time"]) + self.assertAlmostEqual(new_func_node["diff_pct"], 100.0) + + def test_diff_flamegraph_changed_functions(self): + """Functions with different sample counts should have correct diff and diff_pct.""" + hot_leaf_sample = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "hot_leaf"), + MockFrameInfo("file.py", 20, "caller"), + ]) + ]) + ] + cold_leaf_sample = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 30, "cold_leaf"), + MockFrameInfo("file.py", 20, "caller"), + ]) + ]) + ] + + # Baseline: 2 samples, current: 4, scale = 2.0 + diff = make_diff_collector_with_mock_baseline( + [hot_leaf_sample, cold_leaf_sample] + ) + for _ in range(3): + diff.collect(hot_leaf_sample) + diff.collect(cold_leaf_sample) + + data = diff._convert_to_flamegraph_format() + strings = data.get("strings", []) + self.assertAlmostEqual(data["stats"]["baseline_scale"], 2.0) + + children = data.get("children", []) + hot_node = find_child_by_name(children, strings, "hot_leaf") + cold_node = find_child_by_name(children, strings, "cold_leaf") + self.assertIsNotNone(hot_node) + self.assertIsNotNone(cold_node) + + # hot_leaf regressed (+50%) + self.assertAlmostEqual(hot_node["baseline"], 2.0) + self.assertEqual(hot_node["self_time"], 3) + self.assertAlmostEqual(hot_node["diff"], 1.0) + self.assertAlmostEqual(hot_node["diff_pct"], 50.0) + + # cold_leaf improved (-50%) + self.assertAlmostEqual(cold_node["baseline"], 2.0) + self.assertEqual(cold_node["self_time"], 1) + self.assertAlmostEqual(cold_node["diff"], -1.0) + self.assertAlmostEqual(cold_node["diff_pct"], -50.0) + + def test_diff_flamegraph_scale_factor(self): + """Scale factor adjusts when sample counts differ.""" + baseline_frames = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ]) + ]) + ] + + diff = make_diff_collector_with_mock_baseline([baseline_frames]) + for _ in range(4): + diff.collect(baseline_frames) + + data = diff._convert_to_flamegraph_format() + self.assertAlmostEqual(data["stats"]["baseline_scale"], 4.0) + + children = data.get("children", []) + self.assertEqual(len(children), 1) + func1_node = children[0] + self.assertEqual(func1_node["self_time"], 4) + self.assertAlmostEqual(func1_node["baseline"], 4.0) + self.assertAlmostEqual(func1_node["diff"], 0.0) + self.assertAlmostEqual(func1_node["diff_pct"], 0.0) + + def test_diff_flamegraph_elided_stacks(self): + """Paths in baseline but not current produce elided stacks.""" + baseline_frames_1 = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ]) + ]) + ] + baseline_frames_2 = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 30, "old_func"), + MockFrameInfo("file.py", 20, "func2"), + ]) + ]) + ] + + diff = make_diff_collector_with_mock_baseline([baseline_frames_1, baseline_frames_2]) + for _ in range(2): + diff.collect(baseline_frames_1) + + data = diff._convert_to_flamegraph_format() + + self.assertGreater(data["stats"]["elided_count"], 0) + self.assertIn("elided_flamegraph", data["stats"]) + elided = data["stats"]["elided_flamegraph"] + self.assertTrue(elided["stats"]["is_differential"]) + self.assertIn("strings", elided) + + elided_strings = elided.get("strings", []) + children = elided.get("children", []) + self.assertEqual(len(children), 1) + child = children[0] + self.assertIn("old_func", resolve_name(child, elided_strings)) + self.assertEqual(child["self_time"], 0) + self.assertAlmostEqual(child["diff_pct"], -100.0) + self.assertGreater(child["baseline"], 0) + self.assertAlmostEqual(child["diff"], -child["baseline"]) + + def test_diff_flamegraph_elided_top_level_root(self): + """Elided top-level roots do not crash metadata generation.""" + baseline_frames_1 = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "kept_leaf"), + MockFrameInfo("file.py", 20, "kept_root"), + ]) + ]) + ] + baseline_frames_2 = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 30, "old_leaf"), + MockFrameInfo("file.py", 40, "old_root"), + ]) + ]) + ] + + diff = make_diff_collector_with_mock_baseline([ + baseline_frames_1, + baseline_frames_2, + ]) + diff.collect(baseline_frames_1) + + data = diff._convert_to_flamegraph_format() + elided = data["stats"]["elided_flamegraph"] + elided_strings = elided.get("strings", []) + children = elided.get("children", []) + + self.assertEqual(len(children), 1) + self.assertIn("old_root", resolve_name(children[0], elided_strings)) + + def test_diff_flamegraph_function_matched_despite_line_change(self): + """Functions match by (filename, funcname), ignoring lineno.""" + baseline_frames = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ]) + ]) + ] + + diff = make_diff_collector_with_mock_baseline([baseline_frames]) + # Same functions but different line numbers + diff.collect([ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 99, "func1"), + MockFrameInfo("file.py", 55, "func2"), + ]) + ]) + ]) + + data = diff._convert_to_flamegraph_format() + strings = data.get("strings", []) + + children = data.get("children", []) + self.assertEqual(len(children), 1) + child = children[0] + self.assertIn("func1", resolve_name(child, strings)) + self.assertGreater(child["baseline"], 0) + self.assertGreater(child["self_time"], 0) + self.assertAlmostEqual(child["diff"], 0.0, places=1) + self.assertAlmostEqual(child["diff_pct"], 0.0, places=1) + + def test_diff_flamegraph_empty_current(self): + """Empty current profile still produces differential metadata and elided paths.""" + baseline_frames = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [MockFrameInfo("file.py", 10, "func1")]) + ]) + ] + + diff = make_diff_collector_with_mock_baseline([baseline_frames]) + # Don't collect anything in current + + data = diff._convert_to_flamegraph_format() + self.assertIn("name", data) + self.assertEqual(data["value"], 0) + # Differential metadata should still be populated + self.assertTrue(data["stats"]["is_differential"]) + # All baseline paths should be elided since current is empty + self.assertGreater(data["stats"]["elided_count"], 0) + + def test_diff_flamegraph_empty_baseline(self): + """Empty baseline with non-empty current uses scale=1.0 fallback.""" + diff = make_diff_collector_with_mock_baseline([]) + diff.collect([ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ]) + ]) + ]) + + data = diff._convert_to_flamegraph_format() + strings = data.get("strings", []) + + self.assertTrue(data["stats"]["is_differential"]) + self.assertEqual(data["stats"]["baseline_samples"], 0) + self.assertEqual(data["stats"]["current_samples"], 1) + self.assertAlmostEqual(data["stats"]["baseline_scale"], 1.0) + self.assertEqual(data["stats"]["elided_count"], 0) + + children = data.get("children", []) + self.assertEqual(len(children), 1) + child = children[0] + self.assertIn("func1", resolve_name(child, strings)) + self.assertEqual(child["self_time"], 1) + self.assertAlmostEqual(child["baseline"], 0.0) + self.assertAlmostEqual(child["diff"], 1.0) + self.assertAlmostEqual(child["diff_pct"], 100.0) + + def test_diff_flamegraph_export(self): + """DiffFlamegraphCollector export produces differential HTML.""" + test_frames = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ]) + ]) + ] + + diff = make_diff_collector_with_mock_baseline([test_frames]) + diff.collect(test_frames) + + flamegraph_out = tempfile.NamedTemporaryFile( + suffix=".html", delete=False + ) + self.addCleanup(close_and_unlink, flamegraph_out) + + with captured_stdout(), captured_stderr(): + diff.export(flamegraph_out.name) + + self.assertTrue(os.path.exists(flamegraph_out.name)) + self.assertGreater(os.path.getsize(flamegraph_out.name), 0) + + with open(flamegraph_out.name, "r", encoding="utf-8") as f: + content = f.read() + + self.assertIn("<!doctype html>", content.lower()) + self.assertIn("Differential Flamegraph", content) + self.assertIn('"is_differential": true', content) + self.assertIn("d3-flame-graph", content) + self.assertIn('id="diff-legend-section"', content) + self.assertIn("Differential Colors", content) + + def test_diff_flamegraph_preserves_metadata(self): + """Differential mode preserves threads and opcodes metadata.""" + test_frames = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [MockFrameInfo("a.py", 10, "func_a", opcode=100)]), + MockThreadInfo(2, [MockFrameInfo("b.py", 20, "func_b", opcode=200)]), + ]) + ] + + diff = make_diff_collector_with_mock_baseline([test_frames]) + diff.collect(test_frames) + + data = diff._convert_to_flamegraph_format() + strings = data.get("strings", []) + + self.assertTrue(data["stats"]["is_differential"]) + + self.assertIn("threads", data) + self.assertEqual(len(data["threads"]), 2) + + children = data.get("children", []) + self.assertEqual(len(children), 2) + + opcodes_found = set() + for child in children: + self.assertIn("diff", child) + self.assertIn("diff_pct", child) + self.assertIn("baseline", child) + self.assertIn("self_time", child) + self.assertIn("threads", child) + + if "opcodes" in child: + opcodes_found.update(child["opcodes"].keys()) + + self.assertIn(100, opcodes_found) + self.assertIn(200, opcodes_found) + + self.assertIn("per_thread_stats", data["stats"]) + per_thread_stats = data["stats"]["per_thread_stats"] + self.assertIn(1, per_thread_stats) + self.assertIn(2, per_thread_stats) + + def test_diff_flamegraph_elided_preserves_metadata(self): + """Elided flamegraph preserves thread_stats, per_thread_stats, and opcodes.""" + baseline_frames_1 = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "func1", opcode=100), + MockFrameInfo("file.py", 20, "func2", opcode=101), + ], status=THREAD_STATUS_HAS_GIL) + ]) + ] + baseline_frames_2 = [ + MockInterpreterInfo(0, [ + MockThreadInfo(1, [ + MockFrameInfo("file.py", 30, "old_func", opcode=200), + MockFrameInfo("file.py", 20, "func2", opcode=101), + ], status=THREAD_STATUS_HAS_GIL) + ]) + ] + + diff = make_diff_collector_with_mock_baseline([baseline_frames_1, baseline_frames_2]) + for _ in range(2): + diff.collect(baseline_frames_1) + + data = diff._convert_to_flamegraph_format() + elided = data["stats"]["elided_flamegraph"] + + self.assertTrue(elided["stats"]["is_differential"]) + self.assertIn("thread_stats", elided["stats"]) + self.assertIn("per_thread_stats", elided["stats"]) + self.assertIn("baseline_samples", elided["stats"]) + self.assertIn("current_samples", elided["stats"]) + self.assertIn("strings", elided) + + elided_strings = elided.get("strings", []) + children = elided.get("children", []) + self.assertEqual(len(children), 1) + old_func_node = children[0] + if "opcodes" in old_func_node: + self.assertIn(200, old_func_node["opcodes"]) + self.assertEqual(old_func_node["self_time"], 0) + self.assertAlmostEqual(old_func_node["diff_pct"], -100.0) + + def test_diff_flamegraph_load_baseline(self): + """Diff annotations work when baseline is loaded from a binary file.""" + from profiling.sampling.binary_collector import BinaryCollector + from profiling.sampling.stack_collector import DiffFlamegraphCollector + from .test_binary_format import make_frame, make_thread, make_interpreter + + hot_sample = [make_interpreter(0, [make_thread(1, [ + make_frame("file.py", 10, "hot_leaf"), + make_frame("file.py", 20, "caller"), + ])])] + cold_sample = [make_interpreter(0, [make_thread(1, [ + make_frame("file.py", 30, "cold_leaf"), + make_frame("file.py", 20, "caller"), + ])])] + + # Baseline: 2 samples, current: 4, scale = 2.0 + bin_file = tempfile.NamedTemporaryFile(suffix=".bin", delete=False) + self.addCleanup(close_and_unlink, bin_file) + + writer = BinaryCollector( + bin_file.name, sample_interval_usec=1000, compression='none' + ) + writer.collect(hot_sample) + writer.collect(cold_sample) + writer.export(None) + + diff = DiffFlamegraphCollector( + 1000, baseline_binary_path=bin_file.name + ) + hot_mock = [MockInterpreterInfo(0, [MockThreadInfo(1, [ + MockFrameInfo("file.py", 10, "hot_leaf"), + MockFrameInfo("file.py", 20, "caller"), + ])])] + cold_mock = [MockInterpreterInfo(0, [MockThreadInfo(1, [ + MockFrameInfo("file.py", 30, "cold_leaf"), + MockFrameInfo("file.py", 20, "caller"), + ])])] + for _ in range(3): + diff.collect(hot_mock) + diff.collect(cold_mock) + + data = diff._convert_to_flamegraph_format() + strings = data.get("strings", []) + + self.assertTrue(data["stats"]["is_differential"]) + self.assertAlmostEqual(data["stats"]["baseline_scale"], 2.0) + + children = data.get("children", []) + hot_node = find_child_by_name(children, strings, "hot_leaf") + cold_node = find_child_by_name(children, strings, "cold_leaf") + self.assertIsNotNone(hot_node) + self.assertIsNotNone(cold_node) + + # hot_leaf regressed (+50%) + self.assertAlmostEqual(hot_node["baseline"], 2.0) + self.assertEqual(hot_node["self_time"], 3) + self.assertAlmostEqual(hot_node["diff"], 1.0) + self.assertAlmostEqual(hot_node["diff_pct"], 50.0) + + # cold_leaf improved (-50%) + self.assertAlmostEqual(cold_node["baseline"], 2.0) + self.assertEqual(cold_node["self_time"], 1) + self.assertAlmostEqual(cold_node["diff"], -1.0) + self.assertAlmostEqual(cold_node["diff_pct"], -50.0) + + def test_jsonl_collector_export_exact_output(self): + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(1000) + collector.run_id = "run-123" + + test_frames1 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ], + ) + ], + ) + ] + test_frames2 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("file.py", 10, "func1"), + MockFrameInfo("file.py", 20, "func2"), + ], + ) + ], + ) + ] # Same stack + test_frames3 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("other.py", 5, "other_func")] + ) + ], + ) + ] + + collector.collect(test_frames1) + collector.collect(test_frames2) + collector.collect(test_frames3) + + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + content = f.read() + + self.assertEqual( + content, + ( + '{"type":"meta","v":0,"run_id":"run-123","sample_interval_usec":1000}\n' + '{"type":"string_table","v":0,"run_id":"run-123","strings":[{"str_id":0,"value":"func1"},{"str_id":1,"value":"file.py"},{"str_id":2,"value":"func2"},{"str_id":3,"value":"other_func"},{"str_id":4,"value":"other.py"}]}\n' + '{"type":"frame_table","v":0,"run_id":"run-123","frames":[{"frame_id":0,"path_str_id":1,"func_str_id":0,"line":10,"end_line":10},{"frame_id":1,"path_str_id":1,"func_str_id":2,"line":20,"end_line":20},{"frame_id":2,"path_str_id":4,"func_str_id":3,"line":5,"end_line":5}]}\n' + '{"type":"agg","v":0,"run_id":"run-123","kind":"frame","scope":"final","samples_total":3,"entries":[{"frame_id":0,"self":2,"cumulative":2},{"frame_id":1,"self":0,"cumulative":2},{"frame_id":2,"self":1,"cumulative":1}]}\n' + '{"type":"end","v":0,"run_id":"run-123","samples_total":3}\n' + ), + ) + + def test_jsonl_collector_export_includes_mode_in_meta(self): + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(1000, mode=PROFILING_MODE_CPU) + collector.collect( + [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, [MockFrameInfo("file.py", 10, "func")] + ) + ], + ) + ] + ) + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + meta_record = next( + record for record in records if record["type"] == "meta" + ) + self.assertEqual(meta_record["mode"], "cpu") + + def test_jsonl_collector_export_empty_profile(self): + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(1000) + collector.run_id = "run-123" + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + self.assertEqual( + [record["type"] for record in records], ["meta", "end"] + ) + self.assertEqual(records[0]["sample_interval_usec"], 1000) + self.assertEqual(records[0]["run_id"], "run-123") + self.assertEqual(records[1]["samples_total"], 0) + self.assertEqual(records[1]["run_id"], "run-123") + + def test_jsonl_collector_recursive_frames_counted_once_per_sample(self): + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(1000) + collector.collect( + [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo( + "recursive.py", 10, "recursive_func" + ), + MockFrameInfo( + "recursive.py", 10, "recursive_func" + ), + MockFrameInfo( + "recursive.py", 10, "recursive_func" + ), + ], + ) + ], + ) + ] + ) + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + _, _, frame_defs, agg_record, end_record = jsonl_tables(records) + self.assertEqual(len(frame_defs), 1) + self.assertEqual( + agg_record["entries"], + [ + { + "frame_id": frame_defs[0]["frame_id"], + "self": 1, + "cumulative": 1, + } + ], + ) + self.assertEqual(agg_record["samples_total"], 1) + self.assertEqual(end_record["samples_total"], 1) + + def test_jsonl_collector_skip_idle_filters_threads(self): + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + active_status = THREAD_STATUS_HAS_GIL | THREAD_STATUS_ON_CPU + frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("active1.py", 10, "active_func1")], + status=active_status, + ), + MockThreadInfo( + 2, + [MockFrameInfo("idle.py", 20, "idle_func")], + status=0, + ), + MockThreadInfo( + 3, + [MockFrameInfo("active2.py", 30, "active_func2")], + status=active_status, + ), + ], + ) + ] + + def export_summary(skip_idle): + collector = JsonlCollector(1000, skip_idle=skip_idle) + collector.collect(frames) + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + _, str_defs, frame_defs, agg_record, _ = jsonl_tables(records) + paths = {str_defs[item["path_str_id"]] for item in frame_defs} + funcs = {str_defs[item["func_str_id"]] for item in frame_defs} + return paths, funcs, agg_record["samples_total"] + + paths, funcs, samples_total = export_summary(skip_idle=True) + self.assertEqual(paths, {"active1.py", "active2.py"}) + self.assertEqual(funcs, {"active_func1", "active_func2"}) + self.assertEqual(samples_total, 2) + + paths, funcs, samples_total = export_summary(skip_idle=False) + self.assertEqual(paths, {"active1.py", "idle.py", "active2.py"}) + self.assertEqual(funcs, {"active_func1", "idle_func", "active_func2"}) + self.assertEqual(samples_total, 3) + + def test_jsonl_collector_splits_large_exports_into_chunks(self): + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(1000) + + for i in range(257): + collector.collect( + [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo( + f"file{i}.py", i + 1, f"func{i}" + ) + ], + ) + ], + ) + ] + ) + + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + run_ids = {record["run_id"] for record in records} + self.assertEqual(len(run_ids), 1) + self.assertRegex(next(iter(run_ids)), r"^[0-9a-f]{32}$") + + _, str_defs, frame_defs, agg_record, end_record = jsonl_tables( + records + ) + str_chunks = [ + record for record in records if record["type"] == "string_table" + ] + frame_chunks = [ + record for record in records if record["type"] == "frame_table" + ] + agg_chunks = [record for record in records if record["type"] == "agg"] + + self.assertEqual( + [len(record["strings"]) for record in str_chunks], + [256, 256, 2], + ) + self.assertEqual( + [len(record["frames"]) for record in frame_chunks], [256, 1] + ) + self.assertEqual( + [len(record["entries"]) for record in agg_chunks], [256, 1] + ) + self.assertEqual(len(str_defs), 514) + self.assertEqual(len(frame_defs), 257) + self.assertEqual(agg_record["samples_total"], 257) + self.assertEqual(end_record["samples_total"], 257) + + def test_jsonl_collector_respects_weight_for_rle_batched_samples(self): + """weight>1 (from binary replay RLE) is honored in self/cumulative.""" + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(1000) + leaf = MockFrameInfo("file.py", 10, "leaf") + non_leaf = MockFrameInfo("file.py", 20, "non_leaf") + + collector.process_frames([leaf, non_leaf], _thread_id=1, weight=5) + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + _, str_defs, frame_defs, agg, end = jsonl_tables(records) + self.assertEqual(end["samples_total"], 5) + self.assertEqual(agg["samples_total"], 5) + self.assertEqual( + {str_defs[fd["func_str_id"]]: fd["frame_id"] for fd in frame_defs}, + {"leaf": 0, "non_leaf": 1}, + ) + self.assertEqual(agg["entries"], [ + {"frame_id": 0, "self": 5, "cumulative": 5}, + {"frame_id": 1, "self": 0, "cumulative": 5}, + ]) + + def test_jsonl_collector_recursion_with_weight(self): + """Recursion dedup respects weight, not occurrence count.""" + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(1000) + recursive = MockFrameInfo("rec.py", 10, "f") + + collector.process_frames([recursive] * 3, _thread_id=1, weight=3) + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + _, _, frame_defs, agg, _ = jsonl_tables(records) + self.assertEqual(len(frame_defs), 1) + self.assertEqual(agg["entries"], [ + {"frame_id": 0, "self": 3, "cumulative": 3}, + ]) + + def test_jsonl_collector_emits_col_and_end_col_when_present(self): + """All four location fields are emitted when col/end_col are >= 0.""" + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(1000) + frame = MockFrameInfo("test.py", 0, "f") + frame.location = LocationInfo(42, 45, 4, 12) + frames = [ + MockInterpreterInfo( + 0, [MockThreadInfo(1, [frame], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames) + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + _, str_defs, frame_defs, _, _ = jsonl_tables(records) + self.assertEqual(frame_defs, [ + { + "frame_id": 0, + "path_str_id": 1, + "func_str_id": 0, + "line": 42, + "end_line": 45, + "col": 4, + "end_col": 12, + }, + ]) + self.assertEqual(str_defs, {0: "f", 1: "test.py"}) + + def test_jsonl_collector_partial_location_elision(self): + """Negative col/end_col/end_line fields are individually elided.""" + # _get_or_create_frame_id interns funcname before filename, so + # func_str_id=0 ("f") and path_str_id=1 ("test.py"). + common = {"frame_id": 0, "path_str_id": 1, "func_str_id": 0} + cases = [ + (LocationInfo(42, 45, -1, 12), + {**common, "line": 42, "end_line": 45, "end_col": 12}), + (LocationInfo(42, 45, 4, -1), + {**common, "line": 42, "end_line": 45, "col": 4}), + (LocationInfo(42, 0, 4, 8), + {**common, "line": 42, "col": 4, "end_col": 8}), + ] + for loc, expected_frame_def in cases: + with self.subTest(location=loc): + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(1000) + frame = MockFrameInfo("test.py", 0, "f") + frame.location = loc + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame], status=THREAD_STATUS_HAS_GIL)], + ) + ] + collector.collect(frames) + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + _, _, frame_defs, _, _ = jsonl_tables(records) + self.assertEqual(frame_defs, [expected_frame_def]) + + +class TestRecursiveFunctionHandling(unittest.TestCase): + """Tests for correct handling of recursive functions in cumulative stats.""" + + def test_pstats_collector_recursive_function_single_sample(self): + """Test that recursive functions are counted once per sample, not per occurrence.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Simulate a recursive function appearing 5 times in one sample + recursive_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + ], + ) + ], + ) + ] + collector.collect(recursive_frames) + + location = ("test.py", 10, "recursive_func") + # Should count as 1 cumulative call (present in 1 sample), not 5 + self.assertEqual(collector.result[location]["cumulative_calls"], 1) + # Direct calls should be 1 (top of stack) + self.assertEqual(collector.result[location]["direct_calls"], 1) + + def test_pstats_collector_recursive_function_multiple_samples(self): + """Test cumulative counting across multiple samples with recursion.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Sample 1: recursive function at depth 3 + sample1 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + ], + ) + ], + ) + ] + # Sample 2: recursive function at depth 2 + sample2 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + ], + ) + ], + ) + ] + # Sample 3: recursive function at depth 4 + sample3 = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + ], + ) + ], + ) + ] + + collector.collect(sample1) + collector.collect(sample2) + collector.collect(sample3) + + location = ("test.py", 10, "recursive_func") + # Should count as 3 cumulative calls (present in 3 samples) + # Not 3+2+4=9 which would be the buggy behavior + self.assertEqual(collector.result[location]["cumulative_calls"], 3) + self.assertEqual(collector.result[location]["direct_calls"], 3) + + def test_pstats_collector_mixed_recursive_and_nonrecursive(self): + """Test a call stack with both recursive and non-recursive functions.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Stack: main -> foo (recursive x3) -> bar + frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("test.py", 50, "bar"), # top of stack + MockFrameInfo("test.py", 20, "foo"), # recursive + MockFrameInfo("test.py", 20, "foo"), # recursive + MockFrameInfo("test.py", 20, "foo"), # recursive + MockFrameInfo("test.py", 10, "main"), # bottom + ], + ) + ], + ) + ] + collector.collect(frames) + + # bar: 1 cumulative (in stack), 1 direct (top) + self.assertEqual(collector.result[("test.py", 50, "bar")]["cumulative_calls"], 1) + self.assertEqual(collector.result[("test.py", 50, "bar")]["direct_calls"], 1) + + # foo: 1 cumulative (counted once despite 3 occurrences), 0 direct + self.assertEqual(collector.result[("test.py", 20, "foo")]["cumulative_calls"], 1) + self.assertEqual(collector.result[("test.py", 20, "foo")]["direct_calls"], 0) + + # main: 1 cumulative, 0 direct + self.assertEqual(collector.result[("test.py", 10, "main")]["cumulative_calls"], 1) + self.assertEqual(collector.result[("test.py", 10, "main")]["direct_calls"], 0) + + def test_pstats_collector_cumulative_percentage_cannot_exceed_100(self): + """Test that cumulative percentage stays <= 100% even with deep recursion.""" + collector = PstatsCollector(sample_interval_usec=1000000) # 1 second for easy math + + # Collect 10 samples, each with recursive function at depth 100 + for _ in range(10): + frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [MockFrameInfo("test.py", 10, "deep_recursive")] * 100, + ) + ], + ) + ] + collector.collect(frames) + + location = ("test.py", 10, "deep_recursive") + # Cumulative calls should be 10 (number of samples), not 1000 + self.assertEqual(collector.result[location]["cumulative_calls"], 10) + + # Verify stats calculation gives correct percentage + collector.create_stats() + stats = collector.stats[location] + # stats format: (direct_calls, cumulative_calls, total_time, cumulative_time, callers) + cumulative_calls = stats[1] + self.assertEqual(cumulative_calls, 10) + + def test_pstats_collector_different_lines_same_function_counted_separately(self): + """Test that different line numbers in same function are tracked separately.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Function with multiple line numbers (e.g., different call sites within recursion) + frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("test.py", 15, "func"), # line 15 + MockFrameInfo("test.py", 12, "func"), # line 12 + MockFrameInfo("test.py", 15, "func"), # line 15 again + MockFrameInfo("test.py", 10, "func"), # line 10 + ], + ) + ], + ) + ] + collector.collect(frames) + + # Each unique (file, line, func) should be counted once + self.assertEqual(collector.result[("test.py", 15, "func")]["cumulative_calls"], 1) + self.assertEqual(collector.result[("test.py", 12, "func")]["cumulative_calls"], 1) + self.assertEqual(collector.result[("test.py", 10, "func")]["cumulative_calls"], 1) + + +class TestLocationHelpers(unittest.TestCase): + """Tests for location handling helper functions.""" + + def test_extract_lineno_from_location_info(self): + """Test extracting lineno from LocationInfo namedtuple.""" + loc = LocationInfo(42, 45, 0, 10) + self.assertEqual(extract_lineno(loc), 42) + + def test_extract_lineno_from_tuple(self): + """Test extracting lineno from plain tuple.""" + loc = (100, 105, 5, 20) + self.assertEqual(extract_lineno(loc), 100) + + def test_extract_lineno_from_none(self): + """Test extracting lineno from None (synthetic frames).""" + self.assertEqual(extract_lineno(None), 0) + + def test_extract_lineno_from_int(self): + """Test extracting lineno from a bare integer line number. + + Mirrors normalize_location's int contract so callers like the + collapsed/flamegraph collectors do not crash on a bare-int location. + """ + self.assertEqual(extract_lineno(42), 42) + self.assertEqual(extract_lineno(0), 0) + + def test_normalize_location_with_int(self): + """Test normalize_location expands a legacy integer line number.""" + result = normalize_location(42) + self.assertEqual(result, (42, 42, -1, -1)) + + def test_normalize_location_with_location_info(self): + """Test normalize_location passes through LocationInfo.""" + loc = LocationInfo(10, 15, 0, 5) + result = normalize_location(loc) + self.assertEqual(result, loc) + + def test_normalize_location_with_tuple(self): + """Test normalize_location passes through tuple.""" + loc = (10, 15, 0, 5) + result = normalize_location(loc) + self.assertEqual(result, loc) + + def test_normalize_location_with_none(self): + """Test normalize_location returns DEFAULT_LOCATION for None.""" + result = normalize_location(None) + self.assertEqual(result, DEFAULT_LOCATION) + self.assertEqual(result, (0, 0, -1, -1)) + + +class TestOpcodeFormatting(unittest.TestCase): + """Tests for opcode formatting utilities.""" + + def test_get_opcode_info_standard_opcode(self): + """Test get_opcode_info for a standard opcode.""" + # LOAD_CONST is a standard opcode + load_const = opcode.opmap.get('LOAD_CONST') + if load_const is not None: + info = get_opcode_info(load_const) + self.assertEqual(info['opname'], 'LOAD_CONST') + self.assertEqual(info['base_opname'], 'LOAD_CONST') + self.assertFalse(info['is_specialized']) + + def test_get_opcode_info_unknown_opcode(self): + """Test get_opcode_info for an unknown opcode.""" + info = get_opcode_info(999) + self.assertEqual(info['opname'], '<999>') + self.assertEqual(info['base_opname'], '<999>') + self.assertFalse(info['is_specialized']) + + def test_format_opcode_standard(self): + """Test format_opcode for a standard opcode.""" + load_const = opcode.opmap.get('LOAD_CONST') + if load_const is not None: + formatted = format_opcode(load_const) + self.assertEqual(formatted, 'LOAD_CONST') + + def test_format_opcode_specialized(self): + """Test format_opcode for a specialized opcode shows base in parens.""" + if not hasattr(opcode, '_specialized_opmap'): + self.skipTest("No specialized opcodes in this Python version") + if not hasattr(opcode, '_specializations'): + self.skipTest("No specialization info in this Python version") + + # Find any specialized opcode to test + for base_name, variants in opcode._specializations.items(): + if not variants: + continue + variant_name = variants[0] + variant_opcode = opcode._specialized_opmap.get(variant_name) + if variant_opcode is None: + continue + formatted = format_opcode(variant_opcode) + # Should show: VARIANT_NAME (BASE_NAME) + self.assertIn(variant_name, formatted) + self.assertIn(f'({base_name})', formatted) + return + + self.skipTest("No specialized opcodes found") + + def test_format_opcode_unknown(self): + """Test format_opcode for an unknown opcode.""" + formatted = format_opcode(999) + self.assertEqual(formatted, '<999>') + + +class TestLocationInCollectors(unittest.TestCase): + """Tests for location tuple handling in each collector.""" + + def _make_frames_with_location(self, location, opcode=None): + """Create test frames with a specific location.""" + frame = MockFrameInfo("test.py", 0, "test_func", opcode) + # Override the location + frame.location = location + return [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame], status=THREAD_STATUS_HAS_GIL)] + ) + ] + + def test_pstats_collector_with_location_info(self): + """Test PstatsCollector handles LocationInfo properly.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Frame with LocationInfo + frame = MockFrameInfo("test.py", 42, "my_function") + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames) + + # Should extract lineno from location + key = ("test.py", 42, "my_function") + self.assertIn(key, collector.result) + self.assertEqual(collector.result[key]["direct_calls"], 1) + + def test_pstats_collector_with_none_location(self): + """Test PstatsCollector handles None location (synthetic frames).""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Create frame with None location (like GC frame) + frame = MockFrameInfo("~", 0, "<GC>") + frame.location = None # Synthetic frame has no location + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames) + + # Should use lineno=0 for None location + key = ("~", 0, "<GC>") + self.assertIn(key, collector.result) + + def test_collapsed_stack_with_location_info(self): + """Test CollapsedStackCollector handles LocationInfo properly.""" + collector = CollapsedStackCollector(1000) + + frame1 = MockFrameInfo("main.py", 10, "main") + frame2 = MockFrameInfo("utils.py", 25, "helper") + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame1, frame2], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames) + + # Check that linenos were extracted correctly + self.assertEqual(len(collector.stack_counter), 1) + (path, _), count = list(collector.stack_counter.items())[0] + # Reversed order: helper at top, main at bottom + self.assertEqual(path[0], ("utils.py", 25, "helper")) + self.assertEqual(path[1], ("main.py", 10, "main")) + + def test_flamegraph_collector_with_location_info(self): + """Test FlamegraphCollector handles LocationInfo properly.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + frame = MockFrameInfo("app.py", 100, "process_data") + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames) + + data = collector._convert_to_flamegraph_format() + # Verify the function name includes lineno from location + strings = data.get("strings", []) + name_found = any("process_data" in s and "100" in s for s in strings if isinstance(s, str)) + self.assertTrue(name_found, f"Expected to find 'process_data' with line 100 in {strings}") + + def test_gecko_collector_with_location_info(self): + """Test GeckoCollector handles LocationInfo properly.""" + collector = GeckoCollector(sample_interval_usec=1000) + + frame = MockFrameInfo("server.py", 50, "handle_request") + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames) + + profile = collector._build_profile() + # Check that the function was recorded + self.assertEqual(len(profile["threads"]), 1) + thread_data = profile["threads"][0] + string_array = profile["shared"]["stringArray"] + + # Verify function name is in string table + self.assertIn("handle_request", string_array) + + def test_jsonl_collector_with_location_info(self): + """Test JsonlCollector handles LocationInfo properly.""" + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(sample_interval_usec=1000) + + # Frame with LocationInfo + frame = MockFrameInfo("test.py", 42, "my_function") + frames = [ + MockInterpreterInfo( + 0, [MockThreadInfo(1, [frame], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames) + + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + meta, str_defs, frame_defs, agg, end = jsonl_tables(records) + self.assertEqual(meta["sample_interval_usec"], 1000) + self.assertEqual(agg["samples_total"], 1) + self.assertEqual(end["samples_total"], 1) + self.assertEqual(len(frame_defs), 1) + self.assertEqual(str_defs[frame_defs[0]["path_str_id"]], "test.py") + self.assertEqual(str_defs[frame_defs[0]["func_str_id"]], "my_function") + self.assertEqual( + frame_defs[0], + { + "frame_id": 0, + "path_str_id": frame_defs[0]["path_str_id"], + "func_str_id": frame_defs[0]["func_str_id"], + "line": 42, + "end_line": 42, + }, + ) + + def test_jsonl_collector_with_none_location(self): + """Test JsonlCollector handles None location (synthetic frames).""" + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + collector = JsonlCollector(sample_interval_usec=1000) + + # Create frame with None location (like GC frame) + frame = MockFrameInfo("~", 0, "<GC>") + frame.location = None # Synthetic frame has no location + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames) + + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + meta, str_defs, frame_defs, agg, end = jsonl_tables(records) + self.assertEqual(meta["sample_interval_usec"], 1000) + self.assertEqual(agg["samples_total"], 1) + self.assertEqual(end["samples_total"], 1) + self.assertEqual(len(frame_defs), 1) + self.assertEqual(str_defs[frame_defs[0]["path_str_id"]], "~") + self.assertEqual(str_defs[frame_defs[0]["func_str_id"]], "<GC>") + self.assertEqual( + frame_defs[0], + { + "frame_id": 0, + "path_str_id": frame_defs[0]["path_str_id"], + "func_str_id": frame_defs[0]["func_str_id"], + "line": 0, + }, + ) + + +class TestOpcodeHandling(unittest.TestCase): + """Tests for opcode field handling in collectors.""" + + def test_frame_with_opcode(self): + """Test MockFrameInfo properly stores opcode.""" + frame = MockFrameInfo("test.py", 10, "my_func", opcode=90) + self.assertEqual(frame.opcode, 90) + # Verify tuple representation includes opcode + self.assertEqual(frame[3], 90) + self.assertEqual(len(frame), 4) + + def test_frame_without_opcode(self): + """Test MockFrameInfo with no opcode defaults to None.""" + frame = MockFrameInfo("test.py", 10, "my_func") + self.assertIsNone(frame.opcode) + self.assertIsNone(frame[3]) + + def test_collectors_ignore_opcode_for_key_generation(self): + """Test that collectors use (filename, lineno, funcname) as key, not opcode.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Same function, different opcodes + frame1 = MockFrameInfo("test.py", 10, "func", opcode=90) + frame2 = MockFrameInfo("test.py", 10, "func", opcode=100) + + frames1 = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame1], status=THREAD_STATUS_HAS_GIL)] + ) + ] + frames2 = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame2], status=THREAD_STATUS_HAS_GIL)] + ) + ] + + collector.collect(frames1) + collector.collect(frames2) + + # Should be counted as same function (opcode not in key) + key = ("test.py", 10, "func") + self.assertIn(key, collector.result) + self.assertEqual(collector.result[key]["direct_calls"], 2) + + +class TestGeckoOpcodeMarkers(unittest.TestCase): + """Tests for GeckoCollector opcode interval markers.""" + + def test_gecko_collector_opcodes_disabled_by_default(self): + """Test that opcode tracking is disabled by default.""" + collector = GeckoCollector(sample_interval_usec=1000) + self.assertFalse(collector.opcodes_enabled) + + def test_gecko_collector_opcodes_enabled(self): + """Test that opcode tracking can be enabled.""" + collector = GeckoCollector(sample_interval_usec=1000, opcodes=True) + self.assertTrue(collector.opcodes_enabled) + + def test_gecko_opcode_state_tracking(self): + """Test that GeckoCollector tracks opcode state changes.""" + collector = GeckoCollector(sample_interval_usec=1000, opcodes=True) + self.addCleanup(collector._cleanup_spills) + + # First sample with opcode 90 (RAISE_VARARGS) + frame1 = MockFrameInfo("test.py", 10, "func", opcode=90) + frames1 = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame1], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames1) + + # Should start tracking this opcode state + self.assertIn(1, collector.opcode_state) + state = collector.opcode_state[1] + self.assertEqual(state[0], 90) # opcode + self.assertEqual(state[1], 10) # lineno + self.assertEqual(state[3], "func") # funcname + + def test_gecko_opcode_state_change_emits_marker(self): + """Test that opcode state change emits an interval marker.""" + collector = GeckoCollector(sample_interval_usec=1000, opcodes=True) + + # First sample: opcode 90 + frame1 = MockFrameInfo("test.py", 10, "func", opcode=90) + frames1 = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame1], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames1) + + # Second sample: different opcode 100 + frame2 = MockFrameInfo("test.py", 10, "func", opcode=100) + frames2 = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame2], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames2) + + # Should have emitted a marker for the first opcode + profile = collector._build_profile() + markers = profile["threads"][0]["markers"] + assert_gecko_column_lengths( + self, markers, + ("data", "name", "startTime", "endTime", "phase", "category"), + ) + opcode_markers = gecko_opcode_marker_data(profile) + self.assertIn( + { + "opcode": 90, + "line": 10, + "function": "func", + }, + [ + { + "opcode": marker["opcode"], + "line": marker["line"], + "function": marker["function"], + } + for marker in opcode_markers + ], + ) + + def test_gecko_opcode_markers_not_emitted_when_disabled(self): + """Test that no opcode markers when opcodes=False.""" + collector = GeckoCollector(sample_interval_usec=1000, opcodes=False) + + frame1 = MockFrameInfo("test.py", 10, "func", opcode=90) + frames1 = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame1], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames1) + + frame2 = MockFrameInfo("test.py", 10, "func", opcode=100) + frames2 = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame2], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames2) + + profile = collector._build_profile() + self.assertEqual(gecko_opcode_marker_data(profile), []) + self.assertEqual(profile["meta"]["markerSchema"], []) + + def test_gecko_opcode_with_none_opcode(self): + """Test that None opcode doesn't cause issues.""" + collector = GeckoCollector(sample_interval_usec=1000, opcodes=True) + + # Frame with no opcode (None) + frame = MockFrameInfo("test.py", 10, "func", opcode=None) + frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(1, [frame], status=THREAD_STATUS_HAS_GIL)] + ) + ] + collector.collect(frames) + + profile = collector._build_profile() + self.assertEqual(gecko_opcode_marker_data(profile), []) + + +class TestCollectorFrameFormat(unittest.TestCase): + """Tests verifying all collectors handle the 4-element frame format.""" + + def _make_sample_frames(self): + """Create sample frames with full format: (filename, location, funcname, opcode).""" + return [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("app.py", 100, "main", opcode=90), + MockFrameInfo("utils.py", 50, "helper", opcode=100), + MockFrameInfo("lib.py", 25, "process", opcode=None), + ], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + + def test_pstats_collector_frame_format(self): + """Test PstatsCollector with 4-element frame format.""" + collector = PstatsCollector(sample_interval_usec=1000) + collector.collect(self._make_sample_frames()) + + # All three functions should be recorded + self.assertEqual(len(collector.result), 3) + self.assertIn(("app.py", 100, "main"), collector.result) + self.assertIn(("utils.py", 50, "helper"), collector.result) + self.assertIn(("lib.py", 25, "process"), collector.result) + + def test_collapsed_stack_frame_format(self): + """Test CollapsedStackCollector with 4-element frame format.""" + collector = CollapsedStackCollector(sample_interval_usec=1000) + collector.collect(self._make_sample_frames()) + + self.assertEqual(len(collector.stack_counter), 1) + (path, _), _ = list(collector.stack_counter.items())[0] + # 3 frames in the path (reversed order) + self.assertEqual(len(path), 3) + + def test_flamegraph_collector_frame_format(self): + """Test FlamegraphCollector with 4-element frame format.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + collector.collect(self._make_sample_frames()) + + data = collector._convert_to_flamegraph_format() + # Should have processed the frames + self.assertIn("children", data) + + def test_gecko_collector_frame_format(self): + """Test GeckoCollector with 4-element frame format.""" + collector = GeckoCollector(sample_interval_usec=1000) + collector.collect(self._make_sample_frames()) + + profile = collector._build_profile() + # Should have one thread with the frames + self.assertEqual(len(profile["threads"]), 1) + thread = profile["threads"][0] + # Should have recorded 3 functions + self.assertEqual(thread["funcTable"]["length"], 3) + + def test_jsonl_collector_frame_format(self): + """Test JsonlCollector with 4-element frame format.""" + collector = JsonlCollector(sample_interval_usec=1000) + collector.collect(self._make_sample_frames()) + + with tempfile.NamedTemporaryFile(delete=False) as f: + self.addClassCleanup(close_and_unlink, f) + collector.export(f.name) + + with open(f.name, "r", encoding="utf-8") as fp: + records = [json.loads(line) for line in fp] + + _, str_defs, frame_defs, _, _ = jsonl_tables(records) + + self.assertEqual(len(frame_defs), 3) + + paths = {str_defs[item["path_str_id"]] for item in frame_defs} + funcs = {str_defs[item["func_str_id"]] for item in frame_defs} + + self.assertEqual(paths, {"app.py", "utils.py", "lib.py"}) + self.assertEqual(funcs, {"main", "helper", "process"}) + + +class TestInternalFrameFiltering(unittest.TestCase): + """Tests for filtering internal profiler frames from output.""" + + def test_filter_internal_frames(self): + """Test that _sync_coordinator frames are filtered from anywhere in stack.""" + from profiling.sampling.collector import filter_internal_frames + + # Stack with _sync_coordinator in the middle (realistic scenario) + frames = [ + MockFrameInfo("user_script.py", 10, "user_func"), + MockFrameInfo("/path/to/_sync_coordinator.py", 100, "main"), + MockFrameInfo("<frozen runpy>", 87, "_run_code"), + ] + + filtered = filter_internal_frames(frames) + self.assertEqual(len(filtered), 2) + self.assertEqual(filtered[0].filename, "user_script.py") + self.assertEqual(filtered[1].filename, "<frozen runpy>") + + def test_pstats_collector_filters_internal_frames(self): + """Test that PstatsCollector filters out internal frames.""" + collector = PstatsCollector(sample_interval_usec=1000) + + frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("user_script.py", 10, "user_func"), + MockFrameInfo("/path/to/_sync_coordinator.py", 100, "main"), + MockFrameInfo("<frozen runpy>", 87, "_run_code"), + ], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + collector.collect(frames) + + self.assertEqual(len(collector.result), 2) + self.assertIn(("user_script.py", 10, "user_func"), collector.result) + self.assertIn(("<frozen runpy>", 87, "_run_code"), collector.result) + + def test_gecko_collector_filters_internal_frames(self): + """Test that GeckoCollector filters out internal frames.""" + collector = GeckoCollector(sample_interval_usec=1000) + + frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("app.py", 50, "run"), + MockFrameInfo("/lib/_sync_coordinator.py", 100, "main"), + ], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + collector.collect(frames) + + profile = collector._build_profile() + string_array = profile["shared"]["stringArray"] + + # Should not contain _sync_coordinator functions + for s in string_array: + self.assertNotIn("_sync_coordinator", s) + + def test_flamegraph_collector_filters_internal_frames(self): + """Test that FlamegraphCollector filters out internal frames.""" + collector = FlamegraphCollector(sample_interval_usec=1000) + + frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("app.py", 50, "run"), + MockFrameInfo("/lib/_sync_coordinator.py", 100, "main"), + MockFrameInfo("<frozen runpy>", 87, "_run_code"), + ], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + collector.collect(frames) + + data = collector._convert_to_flamegraph_format() + strings = data.get("strings", []) + + for s in strings: + self.assertNotIn("_sync_coordinator", s) + + def test_collapsed_stack_collector_filters_internal_frames(self): + """Test that CollapsedStackCollector filters out internal frames.""" + collector = CollapsedStackCollector(sample_interval_usec=1000) + + frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("app.py", 50, "run"), + MockFrameInfo("/lib/_sync_coordinator.py", 100, "main"), + ], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + collector.collect(frames) + + # Check that no stack contains _sync_coordinator + for (call_tree, _), _ in collector.stack_counter.items(): + for filename, _, _ in call_tree: + self.assertNotIn("_sync_coordinator", filename) + + def test_jsonl_collector_filters_internal_frames(self): + """Test that JsonlCollector filters out internal frames.""" + jsonl_out = tempfile.NamedTemporaryFile(delete=False) + self.addCleanup(close_and_unlink, jsonl_out) + + frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("app.py", 50, "run"), + MockFrameInfo("/lib/_sync_coordinator.py", 100, "main"), + MockFrameInfo("<frozen runpy>", 87, "_run_code"), + ], + status=THREAD_STATUS_HAS_GIL, + ) + ], + ) + ] + + collector = JsonlCollector(sample_interval_usec=1000) + collector.collect(frames) + collector.export(jsonl_out.name) + + with open(jsonl_out.name, "r", encoding="utf-8") as f: + records = [json.loads(line) for line in f] + + _, str_defs, frame_defs, _, _ = jsonl_tables(records) + + paths = {str_defs[item["path_str_id"]] for item in frame_defs} + + self.assertIn("app.py", paths) + self.assertIn("<frozen runpy>", paths) + + for path in paths: + self.assertNotIn("_sync_coordinator", path) diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_dump.py b/Lib/test/test_profiling/test_sampling_profiler/test_dump.py new file mode 100644 index 00000000000000..7a0f8df38a8533 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_dump.py @@ -0,0 +1,454 @@ +"""Tests for one-shot sampling profiler stack dumps.""" + +from collections import namedtuple +import os +import opcode +import tempfile +import unittest + +import _colorize + +from profiling.sampling.constants import ( + THREAD_STATUS_GIL_REQUESTED, + THREAD_STATUS_HAS_EXCEPTION, + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_MAIN_THREAD, + THREAD_STATUS_ON_CPU, + THREAD_STATUS_UNKNOWN, +) +from profiling.sampling.dump import format_stack_dump + +from .mocks import ( + LocationInfo as StructseqLocationInfo, + MockAwaitedInfo, + MockCoroInfo, + MockFrameInfo, + MockInterpreterInfo, + MockTaskInfo, + MockThreadInfo, +) + +try: + import _remote_debugging # noqa: F401 +except ImportError: + _remote_debugging = None + + +StructseqInterpreterInfo = namedtuple( + "StructseqInterpreterInfo", + ["interpreter_id", "threads"], +) +StructseqThreadInfo = namedtuple( + "StructseqThreadInfo", + ["thread_id", "status", "frame_info"], +) +StructseqFrameInfo = namedtuple( + "StructseqFrameInfo", + ["filename", "location", "funcname", "opcode"], +) + + +class TestStackDumpFormatting(unittest.TestCase): + def test_format_stack_dump_single_thread(self): + frames = [ + MockFrameInfo("leaf.py", 10, "leaf"), + MockFrameInfo("root.py", 1, "root"), + ] + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 123, + frames, + status=( + THREAD_STATUS_MAIN_THREAD + | THREAD_STATUS_HAS_GIL + | THREAD_STATUS_ON_CPU + ), + ) + ], + ) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn( + "Stack dump for PID 42, thread 123 " + "(main thread, has GIL, on CPU; most recent call last):", + output, + ) + self.assertNotIn("Interpreter 0", output) + self.assertLess( + output.index('File "root.py", line 1, in root'), + output.index('File "leaf.py", line 10, in leaf'), + ) + self.assertIn(' File "root.py", line 1, in root', output) + self.assertIn(' File "leaf.py", line 10, in leaf', output) + self.assertNotIn("\x1b[", output) + + def test_format_stack_dump_with_structseq_tuples(self): + stack_frames = [ + StructseqInterpreterInfo( + 0, + [ + StructseqThreadInfo( + 123, + THREAD_STATUS_HAS_GIL, + [ + StructseqFrameInfo( + "file.py", + StructseqLocationInfo(5, 5, -1, -1), + "func", + None, + ) + ], + ) + ], + ) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn( + "Stack dump for PID 42, thread 123 " + "(has GIL; most recent call last):", + output, + ) + self.assertIn(' File "file.py", line 5, in func', output) + + def test_format_stack_dump_shows_interpreter_ids_when_multiple(self): + stack_frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(100, [MockFrameInfo("main.py", 1, "main")])], + ), + MockInterpreterInfo( + 1, + [MockThreadInfo(200, [MockFrameInfo("sub.py", 2, "sub")])], + ), + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn( + "Stack dump for PID 42, thread 100 " + "(interpreter 0; idle; most recent call last):", + output, + ) + self.assertIn( + "Stack dump, thread 200 " + "(interpreter 1; idle; most recent call last):", + output, + ) + + def test_format_stack_dump_omits_unknown_status(self): + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 123, + [MockFrameInfo("file.py", 5, "func")], + status=THREAD_STATUS_UNKNOWN, + ) + ], + ) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn( + "Stack dump for PID 42, thread 123 (most recent call last):", + output, + ) + self.assertNotIn("unknown", output) + + def test_format_stack_dump_omits_unknown_when_other_status_exists(self): + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 123, + [MockFrameInfo("file.py", 5, "func")], + status=THREAD_STATUS_UNKNOWN | THREAD_STATUS_ON_CPU, + ) + ], + ) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn( + "Stack dump for PID 42, thread 123 " + "(on CPU; most recent call last):", + output, + ) + self.assertNotIn("unknown", output) + + def test_format_stack_dump_labels_known_idle_status(self): + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 123, + [MockFrameInfo("file.py", 5, "func")], + status=0, + ) + ], + ) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn( + "Stack dump for PID 42, thread 123 " + "(idle; most recent call last):", + output, + ) + + def test_format_stack_dump_status_does_not_add_idle_to_waiting_thread(self): + status = ( + THREAD_STATUS_MAIN_THREAD + | THREAD_STATUS_GIL_REQUESTED + | THREAD_STATUS_HAS_EXCEPTION + ) + stack_frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(123, [MockFrameInfo("file.py", 5, "func")], status=status)], + ) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn( + "Stack dump for PID 42, thread 123 " + "(main thread, waiting for GIL, has exception; most recent call last):", + output, + ) + self.assertNotIn("idle", output) + + def test_format_stack_dump_formats_opcode_name(self): + load_const = opcode.opmap["LOAD_CONST"] + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 123, + [ + StructseqFrameInfo( + "file.py", + StructseqLocationInfo(5, 5, -1, -1), + "func", + load_const, + ) + ], + ) + ], + ) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn("opcode=LOAD_CONST", output) + self.assertNotIn(f"opcode={load_const}", output) + + def test_format_stack_dump_formats_unknown_opcode(self): + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 123, + [ + StructseqFrameInfo( + "file.py", + StructseqLocationInfo(5, 5, -1, -1), + "func", + 999, + ) + ], + ) + ], + ) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn("opcode=<999>", output) + + def test_format_stack_dump_prefers_qualname_attribute(self): + class Frame: + filename = "file.py" + location = StructseqLocationInfo(5, 5, -1, -1) + funcname = "inner" + qualname = "Outer.inner" + opcode = None + + stack_frames = [ + MockInterpreterInfo(0, [MockThreadInfo(123, [Frame()])]) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn(' File "file.py", line 5, in Outer.inner', output) + self.assertNotIn("in inner", output) + + def test_format_stack_dump_filters_internal_frames(self): + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 123, + [ + MockFrameInfo("user.py", 10, "user"), + MockFrameInfo("_sync_coordinator.py", 20, "internal"), + ], + ) + ], + ) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn(' File "user.py", line 10, in user', output) + self.assertNotIn("_sync_coordinator.py", output) + + @unittest.skipIf(_remote_debugging is None, "requires _remote_debugging") + def test_format_stack_dump_async_task(self): + task = MockTaskInfo( + task_id=1, + task_name="Task-1", + coroutine_stack=[ + MockCoroInfo( + "Task-1", + [MockFrameInfo("task.py", 5, "waiter")], + ) + ], + ) + stack_frames = [MockAwaitedInfo(thread_id=123, awaited_by=[task])] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn( + "Stack dump for PID 42, thread 123 " + "(task 1; most recent call last):", + output, + ) + self.assertIn(' File "task.py", line 5, in waiter', output) + + def test_format_stack_dump_strips_source_like_traceback(self): + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 123, + [ + StructseqFrameInfo( + __file__, + StructseqLocationInfo(1, 1, -1, -1), + "<module>", + None, + ) + ], + ) + ], + ) + ] + + output = format_stack_dump(stack_frames, pid=42, colorize=False) + + self.assertIn(' File "', output) + self.assertIn('", line 1, in <module>', output) + self.assertIn('"""Tests for one-shot sampling profiler stack dumps."""', output) + self.assertNotIn("^^^^^^^^", output) + + def test_format_stack_dump_highlights_source_range(self): + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 123, + [ + StructseqFrameInfo( + __file__, + StructseqLocationInfo(1, 1, 0, 3), + "<module>", + None, + ) + ], + ) + ], + ) + ] + theme = _colorize.get_theme(force_color=True).profiler_dump + + output = format_stack_dump(stack_frames, pid=42, colorize=True) + + self.assertIn( + f"{theme.source_highlight}\"\"\"{theme.reset}", + output, + ) + self.assertNotIn("^^^^^^^^", _colorize.decolor(output)) + + def test_format_stack_dump_highlights_source_range_after_trimming(self): + with tempfile.TemporaryDirectory() as tmp_dir: + filename = os.path.join(tmp_dir, "target.py") + with open(filename, "w", encoding="utf-8") as file: + file.write(" result = call(arg)\n") + stack_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 123, + [ + StructseqFrameInfo( + filename, + StructseqLocationInfo(1, 1, 13, 17), + "<module>", + None, + ) + ], + ) + ], + ) + ] + theme = _colorize.get_theme(force_color=True).profiler_dump + + output = format_stack_dump(stack_frames, pid=42, colorize=True) + + self.assertIn(f"{theme.source_highlight}call{theme.reset}", output) + self.assertIn("\n result = call(arg)\n", _colorize.decolor(output)) + self.assertNotIn("\n result = call(arg)\n", _colorize.decolor(output)) + + def test_format_stack_dump_empty(self): + output = format_stack_dump([], pid=42, colorize=False) + + self.assertEqual("No Python stacks found for PID 42\n", output) + + def test_format_stack_dump_colorized(self): + stack_frames = [ + MockInterpreterInfo( + 0, + [MockThreadInfo(123, [MockFrameInfo("file.py", 5, "func")])], + ) + ] + theme = _colorize.get_theme(force_color=True).profiler_dump + + output = format_stack_dump(stack_frames, pid=42, colorize=True) + + self.assertIn(theme.header, output) + self.assertIn(theme.filename, output) + self.assertIn(theme.reset, output) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_integration.py b/Lib/test/test_profiling/test_sampling_profiler/test_integration.py new file mode 100644 index 00000000000000..3487647b76683e --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_integration.py @@ -0,0 +1,953 @@ +"""Tests for sampling profiler integration and error handling.""" + +import contextlib +import io +import marshal +import os +import shutil +import subprocess +import sys +import tempfile +import unittest +from unittest import mock + +try: + import _remote_debugging + import profiling.sampling + import profiling.sampling.sample + from profiling.sampling.pstats_collector import PstatsCollector + from profiling.sampling.stack_collector import CollapsedStackCollector + from profiling.sampling.sample import SampleProfiler, _is_process_running + from profiling.sampling.cli import main +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) + +from test.support import ( + requires_remote_subprocess_debugging, + SHORT_TIMEOUT, +) + +from .helpers import ( + test_subprocess, + close_and_unlink, +) +from .mocks import MockFrameInfo, MockThreadInfo, MockInterpreterInfo + +# Duration for profiling tests - long enough for process to complete naturally +PROFILING_TIMEOUT = str(int(SHORT_TIMEOUT)) + +# Duration for profiling in tests - short enough to complete quickly +PROFILING_DURATION_SEC = 2 + + +@requires_remote_subprocess_debugging() +class TestRecursiveFunctionProfiling(unittest.TestCase): + """Test profiling of recursive functions and complex call patterns.""" + + def test_recursive_function_call_counting(self): + """Test that recursive function calls are counted correctly.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Simulate a recursive call pattern: fibonacci(5) calling itself + recursive_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ # First sample: deep in recursion + MockFrameInfo("fib.py", 10, "fibonacci"), + MockFrameInfo( + "fib.py", 10, "fibonacci" + ), # recursive call + MockFrameInfo( + "fib.py", 10, "fibonacci" + ), # deeper recursion + MockFrameInfo( + "fib.py", 10, "fibonacci" + ), # even deeper + MockFrameInfo("main.py", 5, "main"), # main caller + ], + ) + ], + ), + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ # Second sample: different recursion depth + MockFrameInfo("fib.py", 10, "fibonacci"), + MockFrameInfo( + "fib.py", 10, "fibonacci" + ), # recursive call + MockFrameInfo("main.py", 5, "main"), # main caller + ], + ) + ], + ), + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ # Third sample: back to deeper recursion + MockFrameInfo("fib.py", 10, "fibonacci"), + MockFrameInfo("fib.py", 10, "fibonacci"), + MockFrameInfo("fib.py", 10, "fibonacci"), + MockFrameInfo("main.py", 5, "main"), + ], + ) + ], + ), + ] + + for frames in recursive_frames: + collector.collect([frames]) + + collector.create_stats() + + # Check that recursive calls are counted properly + fib_key = ("fib.py", 10, "fibonacci") + main_key = ("main.py", 5, "main") + + self.assertIn(fib_key, collector.stats) + self.assertIn(main_key, collector.stats) + + # Fibonacci: counted once per sample, not per occurrence + fib_stats = collector.stats[fib_key] + direct_calls, cumulative_calls, tt, ct, callers = fib_stats + + # Should count 3 (present in 3 samples), not 9 (total occurrences) + self.assertEqual(cumulative_calls, 3) + self.assertEqual(direct_calls, 3) # Top of stack in all samples + self.assertGreater(tt, 0) + self.assertGreater(ct, 0) + + # Main should also have 3 cumulative calls (in all 3 samples) + main_stats = collector.stats[main_key] + main_direct_calls, main_cumulative_calls = main_stats[0], main_stats[1] + self.assertEqual(main_direct_calls, 0) # Never directly executing + self.assertEqual(main_cumulative_calls, 3) # Appears in all 3 samples + + def test_nested_function_hierarchy(self): + """Test profiling of deeply nested function calls.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Simulate a deep call hierarchy + deep_call_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("level1.py", 10, "level1_func"), + MockFrameInfo("level2.py", 20, "level2_func"), + MockFrameInfo("level3.py", 30, "level3_func"), + MockFrameInfo("level4.py", 40, "level4_func"), + MockFrameInfo("level5.py", 50, "level5_func"), + MockFrameInfo("main.py", 5, "main"), + ], + ) + ], + ), + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ # Same hierarchy sampled again + MockFrameInfo("level1.py", 10, "level1_func"), + MockFrameInfo("level2.py", 20, "level2_func"), + MockFrameInfo("level3.py", 30, "level3_func"), + MockFrameInfo("level4.py", 40, "level4_func"), + MockFrameInfo("level5.py", 50, "level5_func"), + MockFrameInfo("main.py", 5, "main"), + ], + ) + ], + ), + ] + + for frames in deep_call_frames: + collector.collect([frames]) + + collector.create_stats() + + # All levels should be recorded + for level in range(1, 6): + key = (f"level{level}.py", level * 10, f"level{level}_func") + self.assertIn(key, collector.stats) + + stats = collector.stats[key] + direct_calls, cumulative_calls, tt, ct, callers = stats + + # Each level should appear in stack twice (2 samples) + self.assertEqual(cumulative_calls, 2) + + # Only level1 (deepest) should have direct calls + if level == 1: + self.assertEqual(direct_calls, 2) + else: + self.assertEqual(direct_calls, 0) + + # Deeper levels should have lower cumulative time than higher levels + # (since they don't include time from functions they call) + if level == 1: # Deepest level with most time + self.assertGreater(ct, 0) + + def test_alternating_call_patterns(self): + """Test profiling with alternating call patterns.""" + collector = PstatsCollector(sample_interval_usec=1000) + + # Simulate alternating execution paths + pattern_frames = [ + # Pattern A: path through func_a + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("module.py", 10, "func_a"), + MockFrameInfo("module.py", 30, "shared_func"), + MockFrameInfo("main.py", 5, "main"), + ], + ) + ], + ), + # Pattern B: path through func_b + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("module.py", 20, "func_b"), + MockFrameInfo("module.py", 30, "shared_func"), + MockFrameInfo("main.py", 5, "main"), + ], + ) + ], + ), + # Pattern A again + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("module.py", 10, "func_a"), + MockFrameInfo("module.py", 30, "shared_func"), + MockFrameInfo("main.py", 5, "main"), + ], + ) + ], + ), + # Pattern B again + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("module.py", 20, "func_b"), + MockFrameInfo("module.py", 30, "shared_func"), + MockFrameInfo("main.py", 5, "main"), + ], + ) + ], + ), + ] + + for frames in pattern_frames: + collector.collect([frames]) + + collector.create_stats() + + # Check that both paths are recorded equally + func_a_key = ("module.py", 10, "func_a") + func_b_key = ("module.py", 20, "func_b") + shared_key = ("module.py", 30, "shared_func") + main_key = ("main.py", 5, "main") + + # func_a and func_b should each be directly executing twice + self.assertEqual(collector.stats[func_a_key][0], 2) # direct_calls + self.assertEqual(collector.stats[func_a_key][1], 2) # cumulative_calls + self.assertEqual(collector.stats[func_b_key][0], 2) # direct_calls + self.assertEqual(collector.stats[func_b_key][1], 2) # cumulative_calls + + # shared_func should appear in all samples (4 times) but never directly executing + self.assertEqual(collector.stats[shared_key][0], 0) # direct_calls + self.assertEqual(collector.stats[shared_key][1], 4) # cumulative_calls + + # main should appear in all samples but never directly executing + self.assertEqual(collector.stats[main_key][0], 0) # direct_calls + self.assertEqual(collector.stats[main_key][1], 4) # cumulative_calls + + def test_collapsed_stack_with_recursion(self): + """Test collapsed stack collector with recursive patterns.""" + collector = CollapsedStackCollector(1000) + + # Recursive call pattern + recursive_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("factorial.py", 10, "factorial"), + MockFrameInfo("factorial.py", 10, "factorial"), # recursive + MockFrameInfo("factorial.py", 10, "factorial"), # deeper + MockFrameInfo("main.py", 5, "main"), + ], + ) + ], + ), + MockInterpreterInfo( + 0, + [ + MockThreadInfo( + 1, + [ + MockFrameInfo("factorial.py", 10, "factorial"), + MockFrameInfo("factorial.py", 10, "factorial"), # different depth + MockFrameInfo("main.py", 5, "main"), + ], + ) + ], + ), + ] + + for frames in recursive_frames: + collector.collect([frames]) + + # Should capture both call paths + self.assertEqual(len(collector.stack_counter), 2) + + # First path should be longer (deeper recursion) than the second + path_tuples = list(collector.stack_counter.keys()) + paths = [p[0] for p in path_tuples] # Extract just the call paths + lengths = [len(p) for p in paths] + self.assertNotEqual(lengths[0], lengths[1]) + + # Both should contain factorial calls + self.assertTrue( + any(any(f[2] == "factorial" for f in p) for p in paths) + ) + + # Verify total occurrences via aggregation + factorial_key = ("factorial.py", 10, "factorial") + main_key = ("main.py", 5, "main") + + def total_occurrences(func): + total = 0 + for (path, thread_id), count in collector.stack_counter.items(): + total += sum(1 for f in path if f == func) * count + return total + + self.assertEqual(total_occurrences(factorial_key), 5) + self.assertEqual(total_occurrences(main_key), 2) + + +# Shared workload functions for test scripts +_WORKLOAD_FUNCTIONS = ''' +def slow_fibonacci(n): + if n <= 1: + return n + return slow_fibonacci(n-1) + slow_fibonacci(n-2) + +def cpu_intensive_work(): + result = 0 + for i in range(10000): + result += i * i + if i % 100 == 0: + result = result % 1000000 + return result + +def do_work(): + iteration = 0 + while True: + if iteration % 2 == 0: + slow_fibonacci(15) + else: + cpu_intensive_work() + iteration += 1 +''' + + +@requires_remote_subprocess_debugging() +class TestSampleProfilerIntegration(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Test script for use with test_subprocess() - signals when work starts + cls.test_script = _WORKLOAD_FUNCTIONS + ''' +_test_sock.sendall(b"working") +do_work() +''' + # CLI test script - runs for fixed duration (no socket sync) + cls.cli_test_script = ''' +import time +''' + _WORKLOAD_FUNCTIONS.replace( + 'while True:', 'end_time = time.time() + 30\n while time.time() < end_time:' +) + ''' +do_work() +''' + + def test_sampling_basic_functionality(self): + with ( + test_subprocess(self.test_script, wait_for_working=True) as subproc, + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=1000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=PROFILING_DURATION_SEC, + ) + collector.print_stats(show_summary=False) + + output = captured_output.getvalue() + + # Basic checks on output + self.assertIn("Captured", output) + self.assertIn("samples", output) + self.assertIn("Profile Stats", output) + + # Should see some of our test functions + self.assertIn("slow_fibonacci", output) + + def test_sampling_with_pstats_export(self): + pstats_out = tempfile.NamedTemporaryFile( + suffix=".pstats", delete=False + ) + self.addCleanup(close_and_unlink, pstats_out) + + with test_subprocess(self.test_script, wait_for_working=True) as subproc: + # Suppress profiler output when testing file export + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=10000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=PROFILING_DURATION_SEC, + ) + collector.export(pstats_out.name) + + # Verify file was created and contains valid data + self.assertTrue(os.path.exists(pstats_out.name)) + self.assertGreater(os.path.getsize(pstats_out.name), 0) + + # Try to load the stats file + with open(pstats_out.name, "rb") as f: + stats_data = marshal.load(f) + + # Should be a dictionary with the sampled marker + self.assertIsInstance(stats_data, dict) + self.assertIn(("__sampled__",), stats_data) + self.assertTrue(stats_data[("__sampled__",)]) + + # Should have some function data + function_entries = [ + k for k in stats_data.keys() if k != ("__sampled__",) + ] + self.assertGreater(len(function_entries), 0) + + def test_sampling_with_collapsed_export(self): + collapsed_file = tempfile.NamedTemporaryFile( + suffix=".txt", delete=False + ) + self.addCleanup(close_and_unlink, collapsed_file) + + with ( + test_subprocess(self.test_script, wait_for_working=True) as subproc, + ): + # Suppress profiler output when testing file export + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = CollapsedStackCollector(1000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=PROFILING_DURATION_SEC, + ) + collector.export(collapsed_file.name) + + # Verify file was created and contains valid data + self.assertTrue(os.path.exists(collapsed_file.name)) + self.assertGreater(os.path.getsize(collapsed_file.name), 0) + + # Check file format + with open(collapsed_file.name, "r") as f: + content = f.read() + + lines = content.strip().split("\n") + self.assertGreater(len(lines), 0) + + # Each line should have format: stack_trace count + for line in lines: + parts = line.rsplit(" ", 1) + self.assertEqual(len(parts), 2) + + stack_trace, count_str = parts + self.assertGreater(len(stack_trace), 0) + self.assertTrue(count_str.isdigit()) + self.assertGreater(int(count_str), 0) + + # Stack trace should contain semicolon-separated entries + if ";" in stack_trace: + stack_parts = stack_trace.split(";") + for part in stack_parts: + # Each part should be file:function:line + self.assertIn(":", part) + + def test_sampling_all_threads(self): + with ( + test_subprocess(self.test_script, wait_for_working=True) as subproc, + # Suppress profiler output + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=10000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=PROFILING_DURATION_SEC, + all_threads=True, + ) + collector.print_stats(show_summary=False) + + # Just verify that sampling completed without error + # We're not testing output format here + + def test_sample_target_script(self): + script_file = tempfile.NamedTemporaryFile(delete=False) + script_file.write(self.cli_test_script.encode("utf-8")) + script_file.flush() + self.addCleanup(close_and_unlink, script_file) + + # Sample for PROFILING_DURATION_SEC seconds + test_args = [ + "profiling.sampling.sample", "run", + "-d", str(PROFILING_DURATION_SEC), + script_file.name + ] + + with ( + mock.patch("sys.argv", test_args), + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + main() + + output = captured_output.getvalue() + + # Basic checks on output + self.assertIn("Captured", output) + self.assertIn("samples", output) + self.assertIn("Profile Stats", output) + + # Should see some of our test functions + self.assertIn("slow_fibonacci", output) + + def test_sample_target_module(self): + tempdir = tempfile.TemporaryDirectory(delete=False) + self.addCleanup(lambda x: shutil.rmtree(x), tempdir.name) + + module_path = os.path.join(tempdir.name, "test_module.py") + + with open(module_path, "w") as f: + f.write(self.cli_test_script) + + test_args = [ + "profiling.sampling.cli", + "run", + "-d", + str(PROFILING_DURATION_SEC), + "-m", + "test_module", + ] + + with ( + mock.patch("sys.argv", test_args), + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + # Change to temp directory so subprocess can find the module + contextlib.chdir(tempdir.name), + ): + main() + + output = captured_output.getvalue() + + # Basic checks on output + self.assertIn("Captured", output) + self.assertIn("samples", output) + self.assertIn("Profile Stats", output) + + # Should see some of our test functions + self.assertIn("slow_fibonacci", output) + + +@requires_remote_subprocess_debugging() +class TestSampleProfilerErrorHandling(unittest.TestCase): + def test_invalid_pid(self): + with self.assertRaises((SystemExit, PermissionError)): + collector = PstatsCollector(sample_interval_usec=100, skip_idle=False) + profiling.sampling.sample.sample(-1, collector, duration_sec=1) + + def test_process_dies_during_sampling(self): + # Use wait_for_working=False since this simple script doesn't send "working" + with test_subprocess( + "import time; time.sleep(0.5); exit()", + wait_for_working=False + ) as subproc: + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=50000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=2, # Longer than process lifetime + ) + + output = captured_output.getvalue() + + self.assertIn("Error rate", output) + + def test_is_process_running(self): + # Use wait_for_working=False since this simple script doesn't send "working" + with test_subprocess( + "import time; time.sleep(1000)", + wait_for_working=False + ) as subproc: + profiler = SampleProfiler( + pid=subproc.process.pid, + sample_interval_usec=1000, + all_threads=False, + ) + self.assertTrue(_is_process_running(profiler.pid)) + self.assertIsNotNone(profiler.unwinder.get_stack_trace()) + subproc.process.kill() + subproc.process.wait() + self.assertRaises( + ProcessLookupError, profiler.unwinder.get_stack_trace + ) + + # Exit the context manager to ensure the process is terminated + self.assertFalse(_is_process_running(profiler.pid)) + self.assertRaises( + ProcessLookupError, profiler.unwinder.get_stack_trace + ) + + @unittest.skipUnless(sys.platform == "linux", "Only valid on Linux") + def test_esrch_signal_handling(self): + # Use wait_for_working=False since this simple script doesn't send "working" + with test_subprocess( + "import time; time.sleep(1000)", + wait_for_working=False + ) as subproc: + unwinder = _remote_debugging.RemoteUnwinder( + subproc.process.pid + ) + initial_trace = unwinder.get_stack_trace() + self.assertIsNotNone(initial_trace) + + subproc.process.kill() + + # Wait for the process to die and try to get another trace + subproc.process.wait() + + with self.assertRaises(ProcessLookupError): + unwinder.get_stack_trace() + + def test_script_error_treatment(self): + script_file = tempfile.NamedTemporaryFile( + "w", delete=False, suffix=".py" + ) + script_file.write("open('nonexistent_file.txt')\n") + script_file.close() + self.addCleanup(os.unlink, script_file.name) + + result = subprocess.run( + [ + sys.executable, + "-m", + "profiling.sampling.cli", + "run", + script_file.name, + ], + capture_output=True, + text=True, + ) + output = result.stdout + result.stderr + + self.assertNotIn("Script file not found", output) + self.assertIn( + "No such file or directory: 'nonexistent_file.txt'", output + ) + + def test_live_incompatible_with_pstats_options(self): + """Test that --live is incompatible with individual pstats options.""" + test_cases = [ + (["--sort", "tottime"], "--sort"), + (["--limit", "30"], "--limit"), + (["--no-summary"], "--no-summary"), + ] + + for args, expected_flag in test_cases: + with self.subTest(args=args): + test_args = ["profiling.sampling.cli", "run", "--live"] + args + ["test.py"] + with mock.patch("sys.argv", test_args): + with self.assertRaises(SystemExit) as cm: + main() + self.assertNotEqual(cm.exception.code, 0) + + def test_live_incompatible_with_multiple_pstats_options(self): + """Test that --live is incompatible with multiple pstats options.""" + test_args = [ + "profiling.sampling.cli", "run", "--live", + "--sort", "cumtime", "--limit", "25", "--no-summary", "test.py" + ] + + with mock.patch("sys.argv", test_args): + with self.assertRaises(SystemExit) as cm: + main() + self.assertNotEqual(cm.exception.code, 0) + + def test_live_incompatible_with_pstats_default_values(self): + """Test that --live blocks pstats options even with default values.""" + # Test with --sort=nsamples (the default value) + test_args = ["profiling.sampling.cli", "run", "--live", "--sort=nsamples", "test.py"] + + with mock.patch("sys.argv", test_args): + with self.assertRaises(SystemExit) as cm: + main() + self.assertNotEqual(cm.exception.code, 0) + + # Test with --limit=15 (the default value) + test_args = ["profiling.sampling.cli", "run", "--live", "--limit=15", "test.py"] + + with mock.patch("sys.argv", test_args): + with self.assertRaises(SystemExit) as cm: + main() + self.assertNotEqual(cm.exception.code, 0) + + +@requires_remote_subprocess_debugging() +class TestAsyncAwareProfilingIntegration(unittest.TestCase): + """Integration tests for async-aware profiling mode.""" + + @classmethod + def setUpClass(cls): + # Async test script that runs indefinitely until killed. + # Sends "working" signal AFTER tasks are created and scheduled. + cls.async_script = ''' +import asyncio + +async def sleeping_leaf(): + while True: + await asyncio.sleep(0.02) + +async def cpu_leaf(): + total = 0 + while True: + for i in range(10000): + total += i * i + await asyncio.sleep(0) + +async def supervisor(): + tasks = [ + asyncio.create_task(sleeping_leaf(), name="Sleeper-0"), + asyncio.create_task(sleeping_leaf(), name="Sleeper-1"), + asyncio.create_task(sleeping_leaf(), name="Sleeper-2"), + asyncio.create_task(cpu_leaf(), name="Worker"), + ] + await asyncio.sleep(0) # Let tasks get scheduled + _test_sock.sendall(b"working") + await asyncio.gather(*tasks) + +asyncio.run(supervisor()) +''' + + def _collect_async_samples(self, async_aware_mode): + """Helper to collect samples and count function occurrences. + + Returns a dict mapping function names to their sample counts. + """ + with test_subprocess(self.async_script, wait_for_working=True) as subproc: + collector = CollapsedStackCollector(1000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=PROFILING_DURATION_SEC, + async_aware=async_aware_mode, + ) + + # Count samples per function from collapsed stacks + # stack_counter keys are (call_tree, thread_id) where call_tree + # is a tuple of (file, line, func) tuples + func_samples = {} + total = 0 + for (call_tree, _thread_id), count in collector.stack_counter.items(): + total += count + for _file, _line, func in call_tree: + func_samples[func] = func_samples.get(func, 0) + count + + func_samples["_total"] = total + return func_samples + + def test_async_aware_all_sees_sleeping_and_running_tasks(self): + """Test that async_aware='all' captures both sleeping and CPU-running tasks. + + Task tree structure: + main + └── supervisor + ├── Sleeper-0 (sleeping_leaf) + ├── Sleeper-1 (sleeping_leaf) + ├── Sleeper-2 (sleeping_leaf) + └── Worker (cpu_leaf) + + async_aware='all' should see ALL 4 leaf tasks in the output. + """ + samples = self._collect_async_samples("all") + + self.assertGreater(samples["_total"], 0, "Should have collected samples") + self.assertIn("sleeping_leaf", samples) + self.assertIn("cpu_leaf", samples) + self.assertIn("supervisor", samples) + + def test_async_aware_running_sees_only_cpu_task(self): + """Test that async_aware='running' only captures the actively running task. + + Task tree structure: + main + └── supervisor + ├── Sleeper-0 (sleeping_leaf) - NOT visible in 'running' + ├── Sleeper-1 (sleeping_leaf) - NOT visible in 'running' + ├── Sleeper-2 (sleeping_leaf) - NOT visible in 'running' + └── Worker (cpu_leaf) - VISIBLE in 'running' + + async_aware='running' should only see the Worker task doing CPU work. + """ + samples = self._collect_async_samples("running") + + total = samples["_total"] + cpu_leaf_samples = samples.get("cpu_leaf", 0) + + self.assertGreater(total, 0, "Should have collected some samples") + self.assertGreater(cpu_leaf_samples, 0, "cpu_leaf should appear in samples") + + # cpu_leaf should have at least 90% of samples (typically 99%+) + # sleeping_leaf may occasionally appear with very few samples (< 1%) + # when tasks briefly wake up to check sleep timers + cpu_percentage = (cpu_leaf_samples / total) * 100 + self.assertGreater(cpu_percentage, 90.0, + f"cpu_leaf should dominate samples in 'running' mode, " + f"got {cpu_percentage:.1f}% ({cpu_leaf_samples}/{total})") + + +def _generate_deep_generators_script(chain_depth=20, recurse_depth=150): + """Generate a script with deep nested generators for stress testing.""" + lines = [ + 'import sys', + 'sys.setrecursionlimit(5000)', + '', + ] + # Generate chain of yield-from functions + for i in range(chain_depth - 1): + lines.extend([ + f'def deep_yield_chain_{i}(n):', + f' yield ("L{i}", n)', + f' yield from deep_yield_chain_{i + 1}(n)', + '', + ]) + # Last chain function calls recursive_diver + lines.extend([ + f'def deep_yield_chain_{chain_depth - 1}(n):', + f' yield ("L{chain_depth - 1}", n)', + f' yield from recursive_diver(n, {chain_depth})', + '', + 'def recursive_diver(n, depth):', + ' yield (f"DIVE_{depth}", n)', + f' if depth < {recurse_depth}:', + ' yield from recursive_diver(n, depth + 1)', + ' else:', + ' for i in range(5):', + ' yield (f"BOTTOM_{depth}", i)', + '', + 'def oscillating_generator(iterations=1000):', + ' for i in range(iterations):', + ' yield ("OSCILLATE", i)', + ' yield from deep_yield_chain_0(i)', + '', + 'def run_forever():', + ' while True:', + ' for _ in oscillating_generator(10):', + ' pass', + '', + '_test_sock.sendall(b"working")', + 'run_forever()', + ]) + return '\n'.join(lines) + + +@requires_remote_subprocess_debugging() +class TestDeepGeneratorFrameCache(unittest.TestCase): + """Test frame cache consistency with deep oscillating generator stacks.""" + + def test_all_stacks_share_same_base_frame(self): + """Verify all sampled stacks reach the entry point function. + + When profiling deep generators that oscillate up and down the call + stack, every sample should include the entry point function + (run_forever) in its call chain. If the frame cache stores + incomplete stacks, some samples will be missing this base function, + causing broken flamegraphs. + """ + script = _generate_deep_generators_script() + with test_subprocess(script, wait_for_working=True) as subproc: + collector = CollapsedStackCollector(sample_interval_usec=1, skip_idle=False) + + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=2, + ) + + samples_with_entry_point = 0 + samples_without_entry_point = 0 + total_samples = 0 + + for (call_tree, _thread_id), count in collector.stack_counter.items(): + total_samples += count + if call_tree: + has_entry_point = call_tree and call_tree[0][2] == "<module>" + if has_entry_point: + samples_with_entry_point += count + else: + samples_without_entry_point += count + + self.assertGreater(total_samples, 100, + f"Expected at least 100 samples, got {total_samples}") + + self.assertEqual(samples_without_entry_point, 0, + f"Found {samples_without_entry_point}/{total_samples} samples " + f"missing the entry point function 'run_forever'. This indicates " + f"incomplete stacks are being returned, likely due to frame cache " + f"storing partial stack traces.") diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_core.py b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_core.py new file mode 100644 index 00000000000000..f67a900822c853 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_core.py @@ -0,0 +1,698 @@ +"""Core functionality tests for LiveStatsCollector. + +Tests for path simplification, frame processing, collect method, +statistics building, sorting, and formatting. +""" + +import os +import unittest +from test.support import requires +from test.support.import_helper import import_module + +# Only run these tests if curses is available +requires("curses") +curses = import_module("curses") + +from profiling.sampling.live_collector import LiveStatsCollector, MockDisplay +from profiling.sampling.constants import ( + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, +) +from ._live_collector_helpers import ( + MockFrameInfo, + MockThreadInfo, + MockInterpreterInfo, +) + + +class TestLiveStatsCollectorPathSimplification(unittest.TestCase): + """Tests for path simplification functionality.""" + + def test_simplify_stdlib_path(self): + """Test simplification of standard library paths.""" + collector = LiveStatsCollector(1000) + # Get actual os module path + os_file = os.__file__ + if os_file: + stdlib_dir = os.path.dirname(os.path.abspath(os_file)) + test_path = os.path.join(stdlib_dir, "json", "decoder.py") + simplified = collector.simplify_path(test_path) + # Should remove the stdlib prefix + self.assertNotIn(stdlib_dir, simplified) + self.assertIn("json", simplified) + + def test_simplify_unknown_path(self): + """Test that unknown paths are returned unchanged.""" + collector = LiveStatsCollector(1000) + test_path = "/some/unknown/path/file.py" + simplified = collector.simplify_path(test_path) + self.assertEqual(simplified, test_path) + + +class TestLiveStatsCollectorFrameProcessing(unittest.TestCase): + """Tests for frame processing functionality.""" + + def test_process_single_frame(self): + """Test processing a single frame.""" + collector = LiveStatsCollector(1000) + frames = [MockFrameInfo("test.py", 10, "test_func")] + collector.process_frames(frames) + + location = ("test.py", 10, "test_func") + self.assertEqual(collector.result[location]["direct_calls"], 1) + self.assertEqual(collector.result[location]["cumulative_calls"], 1) + + def test_process_multiple_frames(self): + """Test processing a stack of multiple frames.""" + collector = LiveStatsCollector(1000) + frames = [ + MockFrameInfo("test.py", 10, "inner_func"), + MockFrameInfo("test.py", 20, "middle_func"), + MockFrameInfo("test.py", 30, "outer_func"), + ] + collector.process_frames(frames) + + # Top frame (inner_func) should have both direct and cumulative + inner_loc = ("test.py", 10, "inner_func") + self.assertEqual(collector.result[inner_loc]["direct_calls"], 1) + self.assertEqual(collector.result[inner_loc]["cumulative_calls"], 1) + + # Other frames should only have cumulative + middle_loc = ("test.py", 20, "middle_func") + self.assertEqual(collector.result[middle_loc]["direct_calls"], 0) + self.assertEqual(collector.result[middle_loc]["cumulative_calls"], 1) + + outer_loc = ("test.py", 30, "outer_func") + self.assertEqual(collector.result[outer_loc]["direct_calls"], 0) + self.assertEqual(collector.result[outer_loc]["cumulative_calls"], 1) + + def test_process_empty_frames(self): + """Test processing empty frames list.""" + collector = LiveStatsCollector(1000) + collector.process_frames([]) + # Should not raise an error and result should remain empty + self.assertEqual(len(collector.result), 0) + + def test_process_frames_accumulation(self): + """Test that multiple calls accumulate correctly.""" + collector = LiveStatsCollector(1000) + frames = [MockFrameInfo("test.py", 10, "test_func")] + + collector.process_frames(frames) + collector.process_frames(frames) + collector.process_frames(frames) + + location = ("test.py", 10, "test_func") + self.assertEqual(collector.result[location]["direct_calls"], 3) + self.assertEqual(collector.result[location]["cumulative_calls"], 3) + + def test_process_frames_with_thread_id(self): + """Test processing frames with per-thread tracking.""" + collector = LiveStatsCollector(1000) + frames = [MockFrameInfo("test.py", 10, "test_func")] + + # Process frames with thread_id + collector.process_frames(frames, thread_id=123) + + # Check aggregated result + location = ("test.py", 10, "test_func") + self.assertEqual(collector.result[location]["direct_calls"], 1) + self.assertEqual(collector.result[location]["cumulative_calls"], 1) + + # Check per-thread result + self.assertIn(123, collector.per_thread_data) + self.assertEqual( + collector.per_thread_data[123].result[location]["direct_calls"], 1 + ) + self.assertEqual( + collector.per_thread_data[123].result[location]["cumulative_calls"], 1 + ) + + def test_process_frames_multiple_threads(self): + """Test processing frames from multiple threads.""" + collector = LiveStatsCollector(1000) + frames1 = [MockFrameInfo("test.py", 10, "test_func")] + frames2 = [MockFrameInfo("test.py", 20, "other_func")] + + # Process frames from different threads + collector.process_frames(frames1, thread_id=123) + collector.process_frames(frames2, thread_id=456) + + # Check that both threads have their own data + self.assertIn(123, collector.per_thread_data) + self.assertIn(456, collector.per_thread_data) + + loc1 = ("test.py", 10, "test_func") + loc2 = ("test.py", 20, "other_func") + + # Thread 123 should only have func1 + self.assertEqual( + collector.per_thread_data[123].result[loc1]["direct_calls"], 1 + ) + self.assertNotIn(loc2, collector.per_thread_data[123].result) + + # Thread 456 should only have func2 + self.assertEqual( + collector.per_thread_data[456].result[loc2]["direct_calls"], 1 + ) + self.assertNotIn(loc1, collector.per_thread_data[456].result) + + def test_process_recursive_frames_counted_once(self): + """Test that recursive functions are counted once per sample.""" + collector = LiveStatsCollector(1000) + # Simulate recursive function appearing 5 times in stack + frames = [ + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + ] + collector.process_frames(frames) + + location = ("test.py", 10, "recursive_func") + # Should count as 1 cumulative (present in 1 sample), not 5 + self.assertEqual(collector.result[location]["cumulative_calls"], 1) + self.assertEqual(collector.result[location]["direct_calls"], 1) + + def test_process_recursive_frames_multiple_samples(self): + """Test cumulative counting across multiple samples with recursion.""" + collector = LiveStatsCollector(1000) + + # Sample 1: depth 3 + frames1 = [ + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + ] + # Sample 2: depth 2 + frames2 = [ + MockFrameInfo("test.py", 10, "recursive_func"), + MockFrameInfo("test.py", 10, "recursive_func"), + ] + + collector.process_frames(frames1) + collector.process_frames(frames2) + + location = ("test.py", 10, "recursive_func") + # Should count as 2 (present in 2 samples), not 5 + self.assertEqual(collector.result[location]["cumulative_calls"], 2) + self.assertEqual(collector.result[location]["direct_calls"], 2) + + def test_process_mixed_recursive_nonrecursive(self): + """Test stack with both recursive and non-recursive functions.""" + collector = LiveStatsCollector(1000) + + # Stack: main -> foo (recursive x3) -> bar + frames = [ + MockFrameInfo("test.py", 50, "bar"), + MockFrameInfo("test.py", 20, "foo"), + MockFrameInfo("test.py", 20, "foo"), + MockFrameInfo("test.py", 20, "foo"), + MockFrameInfo("test.py", 10, "main"), + ] + collector.process_frames(frames) + + # foo: 1 cumulative despite 3 occurrences + self.assertEqual(collector.result[("test.py", 20, "foo")]["cumulative_calls"], 1) + self.assertEqual(collector.result[("test.py", 20, "foo")]["direct_calls"], 0) + + # bar and main: 1 cumulative each + self.assertEqual(collector.result[("test.py", 50, "bar")]["cumulative_calls"], 1) + self.assertEqual(collector.result[("test.py", 10, "main")]["cumulative_calls"], 1) + + +class TestLiveStatsCollectorCollect(unittest.TestCase): + """Tests for the collect method.""" + + def test_collect_initializes_start_time(self): + """Test that collect initializes start_time on first call.""" + collector = LiveStatsCollector(1000) + self.assertIsNone(collector.start_time) + + # Create mock stack frames + thread_info = MockThreadInfo(123, []) + interpreter_info = MockInterpreterInfo(0, [thread_info]) + stack_frames = [interpreter_info] + + collector.collect(stack_frames) + self.assertIsNotNone(collector.start_time) + + def test_collect_increments_sample_count(self): + """Test that collect increments total_samples.""" + collector = LiveStatsCollector(1000) + thread_info = MockThreadInfo(123, []) + interpreter_info = MockInterpreterInfo(0, [thread_info]) + stack_frames = [interpreter_info] + + self.assertEqual(collector.total_samples, 0) + collector.collect(stack_frames) + self.assertEqual(collector.total_samples, 1) + collector.collect(stack_frames) + self.assertEqual(collector.total_samples, 2) + + def test_collect_with_frames(self): + """Test collect with actual frame data.""" + collector = LiveStatsCollector(1000) + frames = [MockFrameInfo("test.py", 10, "test_func")] + thread_info = MockThreadInfo(123, frames) + interpreter_info = MockInterpreterInfo(0, [thread_info]) + stack_frames = [interpreter_info] + + collector.collect(stack_frames) + + location = ("test.py", 10, "test_func") + self.assertEqual(collector.result[location]["direct_calls"], 1) + self.assertEqual(collector.successful_samples, 1) + self.assertEqual(collector.failed_samples, 0) + + def test_collect_with_empty_frames(self): + """Test collect with empty frames counts as successful. + + A sample is considered successful if the profiler could read from the + target process, even if no frames matched the current filter (e.g., + --mode exception when no thread has an active exception). The sample + itself worked; it just didn't produce frame data. + """ + collector = LiveStatsCollector(1000) + thread_info = MockThreadInfo(123, []) + interpreter_info = MockInterpreterInfo(0, [thread_info]) + stack_frames = [interpreter_info] + + collector.collect(stack_frames) + + # Empty frames still count as successful - the sample worked even + # though no frames matched the filter + self.assertEqual(collector.successful_samples, 1) + self.assertEqual(collector.total_samples, 1) + self.assertEqual(collector.failed_samples, 0) + + def test_sample_counts_invariant(self): + """Test that total_samples == successful_samples + failed_samples. + + Empty frame data (e.g., from --mode exception with no active exception) + still counts as successful since the profiler could read process state. + """ + collector = LiveStatsCollector(1000) + + # Mix of samples with and without frame data + frames = [MockFrameInfo("test.py", 10, "func")] + thread_with_frames = MockThreadInfo(123, frames) + thread_empty = MockThreadInfo(456, []) + interp_with_frames = MockInterpreterInfo(0, [thread_with_frames]) + interp_empty = MockInterpreterInfo(0, [thread_empty]) + + # Collect various samples + collector.collect([interp_with_frames]) # Has frames + collector.collect([interp_empty]) # No frames (filtered) + collector.collect([interp_with_frames]) # Has frames + collector.collect([interp_empty]) # No frames (filtered) + collector.collect([interp_empty]) # No frames (filtered) + + # All 5 samples are successful (profiler could read process state) + self.assertEqual(collector.total_samples, 5) + self.assertEqual(collector.successful_samples, 5) + self.assertEqual(collector.failed_samples, 0) + + # Invariant must hold + self.assertEqual( + collector.total_samples, + collector.successful_samples + collector.failed_samples + ) + + def test_collect_skip_idle_threads(self): + """Test that idle threads are skipped when skip_idle=True.""" + collector = LiveStatsCollector(1000, skip_idle=True) + + frames = [MockFrameInfo("test.py", 10, "test_func")] + running_thread = MockThreadInfo( + 123, frames, status=THREAD_STATUS_HAS_GIL | THREAD_STATUS_ON_CPU + ) + idle_thread = MockThreadInfo(124, frames, status=0) # No flags = idle + interpreter_info = MockInterpreterInfo( + 0, [running_thread, idle_thread] + ) + stack_frames = [interpreter_info] + + collector.collect(stack_frames) + + # Only one thread should be processed + location = ("test.py", 10, "test_func") + self.assertEqual(collector.result[location]["direct_calls"], 1) + + def test_collect_multiple_threads(self): + """Test collect with multiple threads.""" + collector = LiveStatsCollector(1000) + + frames1 = [MockFrameInfo("test1.py", 10, "func1")] + frames2 = [MockFrameInfo("test2.py", 20, "func2")] + thread1 = MockThreadInfo(123, frames1) + thread2 = MockThreadInfo(124, frames2) + interpreter_info = MockInterpreterInfo(0, [thread1, thread2]) + stack_frames = [interpreter_info] + + collector.collect(stack_frames) + + loc1 = ("test1.py", 10, "func1") + loc2 = ("test2.py", 20, "func2") + self.assertEqual(collector.result[loc1]["direct_calls"], 1) + self.assertEqual(collector.result[loc2]["direct_calls"], 1) + + # Check thread IDs are tracked + self.assertIn(123, collector.thread_ids) + self.assertIn(124, collector.thread_ids) + + def test_collect_filtered_mode_percentage_calculation(self): + """Test that percentages use successful_samples, not total_samples. + + With the current behavior, all samples are considered successful + (the profiler could read from the process), even when filters result + in no frame data. This means percentages are relative to all sampling + attempts that succeeded in reading process state. + """ + collector = LiveStatsCollector(1000) + + # Simulate 10 samples where only 2 had matching data (e.g., exception mode) + frames_with_data = [MockFrameInfo("test.py", 10, "exception_handler")] + thread_with_data = MockThreadInfo(123, frames_with_data) + interpreter_with_data = MockInterpreterInfo(0, [thread_with_data]) + + # Empty thread simulates filtered-out data at C level + thread_empty = MockThreadInfo(456, []) + interpreter_empty = MockInterpreterInfo(0, [thread_empty]) + + # 2 samples with data + collector.collect([interpreter_with_data]) + collector.collect([interpreter_with_data]) + + # 8 samples without data (filtered out at C level, but sample still succeeded) + for _ in range(8): + collector.collect([interpreter_empty]) + + # All 10 samples are successful - the profiler could read from the process + self.assertEqual(collector.total_samples, 10) + self.assertEqual(collector.successful_samples, 10) + + # Build stats and check percentage + stats_list = collector.build_stats_list() + self.assertEqual(len(stats_list), 1) + + # The function appeared in 2 out of 10 successful samples = 20% + location = ("test.py", 10, "exception_handler") + self.assertEqual(collector.result[location]["direct_calls"], 2) + + def test_percentage_values_use_successful_samples(self): + """Test that percentages are calculated from successful_samples. + + This verifies the fix where percentages use successful_samples (samples with + frame data) instead of total_samples (all sampling attempts). Critical for + filtered modes like --mode exception. + """ + collector = LiveStatsCollector(1000) + + # Simulate scenario: 100 total samples, only 20 had frame data + collector.total_samples = 100 + collector.successful_samples = 20 + + # Function appeared in 10 out of 20 successful samples + collector.result[("test.py", 10, "handler")] = { + "direct_calls": 10, + "cumulative_calls": 15, + "total_rec_calls": 0, + } + + stats_list = collector.build_stats_list() + self.assertEqual(len(stats_list), 1) + + stat = stats_list[0] + # Calculate expected percentages using successful_samples + expected_sample_pct = stat["direct_calls"] / collector.successful_samples * 100 + expected_cumul_pct = stat["cumulative_calls"] / collector.successful_samples * 100 + + # Percentage should be 10/20 * 100 = 50%, NOT 10/100 * 100 = 10% + self.assertAlmostEqual(expected_sample_pct, 50.0) + # Cumulative percentage should be 15/20 * 100 = 75%, NOT 15/100 * 100 = 15% + self.assertAlmostEqual(expected_cumul_pct, 75.0) + + # Verify sorting by percentage works correctly + collector.result[("test.py", 20, "other")] = { + "direct_calls": 5, # 25% of successful samples + "cumulative_calls": 8, + "total_rec_calls": 0, + } + collector.sort_by = "sample_pct" + stats_list = collector.build_stats_list() + # handler (50%) should come before other (25%) + self.assertEqual(stats_list[0]["func"][2], "handler") + self.assertEqual(stats_list[1]["func"][2], "other") + + def test_build_stats_list_zero_successful_samples(self): + """Test build_stats_list handles zero successful_samples without division by zero. + + When all samples are filtered out (e.g., exception mode with no exceptions), + percentage calculations should return 0 without raising ZeroDivisionError. + """ + collector = LiveStatsCollector(1000) + + # Edge case: data exists but no successful samples + collector.result[("test.py", 10, "func")] = { + "direct_calls": 10, + "cumulative_calls": 10, + "total_rec_calls": 0, + } + collector.total_samples = 100 + collector.successful_samples = 0 # All samples filtered out + + # Should not raise ZeroDivisionError + stats_list = collector.build_stats_list() + self.assertEqual(len(stats_list), 1) + + # Verify percentage-based sorting also works with zero successful_samples + collector.sort_by = "sample_pct" + stats_list = collector.build_stats_list() + self.assertEqual(len(stats_list), 1) + + collector.sort_by = "cumul_pct" + stats_list = collector.build_stats_list() + self.assertEqual(len(stats_list), 1) + + +class TestLiveStatsCollectorStatisticsBuilding(unittest.TestCase): + """Tests for statistics building and sorting.""" + + def setUp(self): + """Set up test fixtures.""" + self.collector = LiveStatsCollector(1000) + # Add some test data + self.collector.result[("file1.py", 10, "func1")] = { + "direct_calls": 100, + "cumulative_calls": 150, + "total_rec_calls": 0, + } + self.collector.result[("file2.py", 20, "func2")] = { + "direct_calls": 50, + "cumulative_calls": 200, + "total_rec_calls": 0, + } + self.collector.result[("file3.py", 30, "func3")] = { + "direct_calls": 75, + "cumulative_calls": 75, + "total_rec_calls": 0, + } + self.collector.total_samples = 300 + # successful_samples is used for percentage calculations + self.collector.successful_samples = 300 + + def test_build_stats_list(self): + """Test that stats list is built correctly.""" + stats_list = self.collector.build_stats_list() + self.assertEqual(len(stats_list), 3) + + # Check that all expected keys are present + for stat in stats_list: + self.assertIn("func", stat) + self.assertIn("direct_calls", stat) + self.assertIn("cumulative_calls", stat) + self.assertIn("total_time", stat) + self.assertIn("cumulative_time", stat) + + def test_sort_by_nsamples(self): + """Test sorting by number of samples.""" + self.collector.sort_by = "nsamples" + stats_list = self.collector.build_stats_list() + + # Should be sorted by direct_calls descending + self.assertEqual(stats_list[0]["func"][2], "func1") # 100 samples + self.assertEqual(stats_list[1]["func"][2], "func3") # 75 samples + self.assertEqual(stats_list[2]["func"][2], "func2") # 50 samples + + def test_sort_by_tottime(self): + """Test sorting by total time.""" + self.collector.sort_by = "tottime" + stats_list = self.collector.build_stats_list() + + # Should be sorted by total_time descending + # total_time = direct_calls * sample_interval_sec + self.assertEqual(stats_list[0]["func"][2], "func1") + self.assertEqual(stats_list[1]["func"][2], "func3") + self.assertEqual(stats_list[2]["func"][2], "func2") + + def test_sort_by_cumtime(self): + """Test sorting by cumulative time.""" + self.collector.sort_by = "cumtime" + stats_list = self.collector.build_stats_list() + + # Should be sorted by cumulative_time descending + self.assertEqual(stats_list[0]["func"][2], "func2") # 200 cumulative + self.assertEqual(stats_list[1]["func"][2], "func1") # 150 cumulative + self.assertEqual(stats_list[2]["func"][2], "func3") # 75 cumulative + + def test_sort_by_sample_pct(self): + """Test sorting by sample percentage.""" + self.collector.sort_by = "sample_pct" + stats_list = self.collector.build_stats_list() + + # Should be sorted by percentage of direct_calls + self.assertEqual(stats_list[0]["func"][2], "func1") # 33.3% + self.assertEqual(stats_list[1]["func"][2], "func3") # 25% + self.assertEqual(stats_list[2]["func"][2], "func2") # 16.7% + + def test_sort_by_cumul_pct(self): + """Test sorting by cumulative percentage.""" + self.collector.sort_by = "cumul_pct" + stats_list = self.collector.build_stats_list() + + # Should be sorted by percentage of cumulative_calls + self.assertEqual(stats_list[0]["func"][2], "func2") # 66.7% + self.assertEqual(stats_list[1]["func"][2], "func1") # 50% + self.assertEqual(stats_list[2]["func"][2], "func3") # 25% + + +class TestLiveStatsCollectorSortCycle(unittest.TestCase): + """Tests for sort mode cycling.""" + + def test_cycle_sort_from_nsamples(self): + """Test cycling from nsamples.""" + collector = LiveStatsCollector(1000, sort_by="nsamples") + collector._cycle_sort() + self.assertEqual(collector.sort_by, "sample_pct") + + def test_cycle_sort_from_sample_pct(self): + """Test cycling from sample_pct.""" + collector = LiveStatsCollector(1000, sort_by="sample_pct") + collector._cycle_sort() + self.assertEqual(collector.sort_by, "tottime") + + def test_cycle_sort_from_tottime(self): + """Test cycling from tottime.""" + collector = LiveStatsCollector(1000, sort_by="tottime") + collector._cycle_sort() + self.assertEqual(collector.sort_by, "cumul_pct") + + def test_cycle_sort_from_cumul_pct(self): + """Test cycling from cumul_pct.""" + collector = LiveStatsCollector(1000, sort_by="cumul_pct") + collector._cycle_sort() + self.assertEqual(collector.sort_by, "cumtime") + + def test_cycle_sort_from_cumtime(self): + """Test cycling from cumtime back to nsamples.""" + collector = LiveStatsCollector(1000, sort_by="cumtime") + collector._cycle_sort() + self.assertEqual(collector.sort_by, "nsamples") + + def test_cycle_sort_invalid_mode(self): + """Test cycling from invalid mode resets to nsamples.""" + collector = LiveStatsCollector(1000) + collector.sort_by = "invalid_mode" + collector._cycle_sort() + self.assertEqual(collector.sort_by, "nsamples") + + def test_cycle_sort_backward_from_nsamples(self): + """Test cycling backward from nsamples goes to cumtime.""" + collector = LiveStatsCollector(1000, sort_by="nsamples") + collector._cycle_sort(reverse=True) + self.assertEqual(collector.sort_by, "cumtime") + + def test_cycle_sort_backward_from_cumtime(self): + """Test cycling backward from cumtime goes to cumul_pct.""" + collector = LiveStatsCollector(1000, sort_by="cumtime") + collector._cycle_sort(reverse=True) + self.assertEqual(collector.sort_by, "cumul_pct") + + def test_cycle_sort_backward_from_sample_pct(self): + """Test cycling backward from sample_pct goes to nsamples.""" + collector = LiveStatsCollector(1000, sort_by="sample_pct") + collector._cycle_sort(reverse=True) + self.assertEqual(collector.sort_by, "nsamples") + + def test_input_lowercase_s_cycles_forward(self): + """Test that lowercase 's' cycles forward.""" + display = MockDisplay() + collector = LiveStatsCollector( + 1000, sort_by="nsamples", display=display + ) + + display.simulate_input(ord("s")) + collector._handle_input() + + self.assertEqual(collector.sort_by, "sample_pct") + + def test_input_uppercase_s_cycles_backward(self): + """Test that uppercase 'S' cycles backward.""" + display = MockDisplay() + collector = LiveStatsCollector( + 1000, sort_by="nsamples", display=display + ) + + display.simulate_input(ord("S")) + collector._handle_input() + + self.assertEqual(collector.sort_by, "cumtime") + + +class TestLiveStatsCollectorFormatting(unittest.TestCase): + """Tests for formatting methods.""" + + def test_format_uptime_seconds(self): + """Test uptime formatting for seconds only.""" + collector = LiveStatsCollector(1000, display=MockDisplay()) + colors = collector._setup_colors() + collector._initialize_widgets(colors) + self.assertEqual(collector.header_widget.format_uptime(45), "0m45s") + + def test_format_uptime_minutes(self): + """Test uptime formatting for minutes.""" + collector = LiveStatsCollector(1000, display=MockDisplay()) + colors = collector._setup_colors() + collector._initialize_widgets(colors) + self.assertEqual(collector.header_widget.format_uptime(125), "2m05s") + + def test_format_uptime_hours(self): + """Test uptime formatting for hours.""" + collector = LiveStatsCollector(1000, display=MockDisplay()) + colors = collector._setup_colors() + collector._initialize_widgets(colors) + self.assertEqual( + collector.header_widget.format_uptime(3661), "1h01m01s" + ) + + def test_format_uptime_large_values(self): + """Test uptime formatting for large time values.""" + collector = LiveStatsCollector(1000, display=MockDisplay()) + colors = collector._setup_colors() + collector._initialize_widgets(colors) + self.assertEqual( + collector.header_widget.format_uptime(86400), "24h00m00s" + ) + + def test_format_uptime_zero(self): + """Test uptime formatting for zero.""" + collector = LiveStatsCollector(1000, display=MockDisplay()) + colors = collector._setup_colors() + collector._initialize_widgets(colors) + self.assertEqual(collector.header_widget.format_uptime(0), "0m00s") + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_interaction.py b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_interaction.py new file mode 100644 index 00000000000000..baf478133fc665 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_interaction.py @@ -0,0 +1,1232 @@ +"""Interactive controls tests for LiveStatsCollector. + +Tests for interactive controls, filtering, filter input, and thread navigation. +""" + +import time +import unittest +from test.support import requires +from test.support.import_helper import import_module + +# Only run these tests if curses is available +requires("curses") +curses = import_module("curses") + +from profiling.sampling.live_collector import LiveStatsCollector, MockDisplay +from profiling.sampling.constants import ( + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, +) +from ._live_collector_helpers import ( + MockFrameInfo, + MockThreadInfo, + MockInterpreterInfo, +) + + +class TestLiveCollectorInteractiveControls(unittest.TestCase): + """Tests for interactive control features.""" + + def setUp(self): + """Set up collector with mock display.""" + self.display = MockDisplay(height=40, width=160) + self.collector = LiveStatsCollector( + 1000, pid=12345, display=self.display + ) + self.collector.start_time = time.perf_counter() + # Set a consistent display update interval for tests + self.collector.display_update_interval_sec = 0.1 + + def tearDown(self): + """Clean up after test.""" + pass + + def test_pause_functionality(self): + """Test pause/resume functionality.""" + self.assertFalse(self.collector.paused) + + # Simulate 'p' key press + self.display.simulate_input(ord("p")) + self.collector._handle_input() + + self.assertTrue(self.collector.paused) + + # Press 'p' again to resume + self.display.simulate_input(ord("p")) + self.collector._handle_input() + + self.assertFalse(self.collector.paused) + + def test_pause_stops_ui_updates(self): + """Test that pausing stops UI updates but profiling continues.""" + # Add some data + self.collector.total_samples = 10 + self.collector.result[("test.py", 1, "func")] = { + "direct_calls": 5, + "cumulative_calls": 10, + "total_rec_calls": 0, + } + + # Pause + self.collector.paused = True + + # Simulate a collect call (profiling continues) + thread_info = MockThreadInfo(123, []) + interpreter_info = MockInterpreterInfo(0, [thread_info]) + stack_frames = [interpreter_info] + + initial_samples = self.collector.total_samples + self.collector.collect(stack_frames) + + # Samples should still increment + self.assertEqual(self.collector.total_samples, initial_samples + 1) + + # But display should not have been updated (buffer stays clear) + self.display.cleared = False + self.collector.collect(stack_frames) + self.assertFalse( + self.display.cleared, "Display should not update when paused" + ) + + def test_reset_stats(self): + """Test reset statistics functionality.""" + # Add some stats + self.collector.total_samples = 100 + self.collector.successful_samples = 90 + self.collector.failed_samples = 10 + self.collector.result[("test.py", 1, "func")] = { + "direct_calls": 50, + "cumulative_calls": 75, + "total_rec_calls": 0, + } + + # Reset + self.collector.reset_stats() + + self.assertEqual(self.collector.total_samples, 0) + self.assertEqual(self.collector.successful_samples, 0) + self.assertEqual(self.collector.failed_samples, 0) + self.assertEqual(len(self.collector.result), 0) + + def test_increase_refresh_rate(self): + """Test increasing refresh rate (faster updates).""" + initial_interval = self.collector.display_update_interval_sec + + # Simulate '+' key press (faster = smaller interval) + self.display.simulate_input(ord("+")) + self.collector._handle_input() + + self.assertLess(self.collector.display_update_interval_sec, initial_interval) + + def test_decrease_refresh_rate(self): + """Test decreasing refresh rate (slower updates).""" + initial_interval = self.collector.display_update_interval_sec + + # Simulate '-' key press (slower = larger interval) + self.display.simulate_input(ord("-")) + self.collector._handle_input() + + self.assertGreater(self.collector.display_update_interval_sec, initial_interval) + + def test_refresh_rate_minimum(self): + """Test that refresh rate has a minimum (max speed).""" + self.collector.display_update_interval_sec = 0.05 # Set to minimum + + # Try to go faster + self.display.simulate_input(ord("+")) + self.collector._handle_input() + + # Should stay at minimum + self.assertEqual(self.collector.display_update_interval_sec, 0.05) + + def test_refresh_rate_maximum(self): + """Test that refresh rate has a maximum (min speed).""" + self.collector.display_update_interval_sec = 1.0 # Set to maximum + + # Try to go slower + self.display.simulate_input(ord("-")) + self.collector._handle_input() + + # Should stay at maximum + self.assertEqual(self.collector.display_update_interval_sec, 1.0) + + def test_help_toggle(self): + """Test help screen toggle.""" + self.assertFalse(self.collector.show_help) + + # Show help + self.display.simulate_input(ord("h")) + self.collector._handle_input() + + self.assertTrue(self.collector.show_help) + + # Pressing any key closes help + self.display.simulate_input(ord("x")) + self.collector._handle_input() + + self.assertFalse(self.collector.show_help) + + def test_help_with_question_mark(self): + """Test help screen with '?' key.""" + self.display.simulate_input(ord("?")) + self.collector._handle_input() + + self.assertTrue(self.collector.show_help) + + def test_help_dismiss_with_q_does_not_quit(self): + """Test that pressing 'q' while help is shown only closes help, not quit""" + self.assertFalse(self.collector.show_help) + self.display.simulate_input(ord("h")) + self.collector._handle_input() + self.assertTrue(self.collector.show_help) + + self.display.simulate_input(ord("q")) + self.collector._handle_input() + + self.assertFalse(self.collector.show_help) + self.assertTrue(self.collector.running) + + def test_filter_clear(self): + """Test clearing filter.""" + self.collector.filter_pattern = "test" + + # Clear filter + self.display.simulate_input(ord("c")) + self.collector._handle_input() + + self.assertIsNone(self.collector.filter_pattern) + + def test_filter_clear_when_none(self): + """Test clearing filter when no filter is set.""" + self.assertIsNone(self.collector.filter_pattern) + + # Should not crash + self.display.simulate_input(ord("c")) + self.collector._handle_input() + + self.assertIsNone(self.collector.filter_pattern) + + def test_paused_status_in_footer(self): + """Test that paused status appears in footer.""" + self.collector.total_samples = 10 + self.collector.paused = True + + self.collector._update_display() + + # Check that PAUSED appears in display + self.assertTrue(self.display.contains_text("PAUSED")) + + def test_filter_status_in_footer(self): + """Test that filter status appears in footer.""" + self.collector.total_samples = 10 + self.collector.filter_pattern = "mytest" + + self.collector._update_display() + + # Check that filter info appears + self.assertTrue(self.display.contains_text("Filter")) + + def test_help_screen_display(self): + """Test that help screen is displayed.""" + self.collector.show_help = True + + self.collector._update_display() + + # Check for help content + self.assertTrue(self.display.contains_text("Interactive Commands")) + + def test_pause_uppercase(self): + """Test pause with uppercase 'P' key.""" + self.assertFalse(self.collector.paused) + + self.display.simulate_input(ord("P")) + self.collector._handle_input() + + self.assertTrue(self.collector.paused) + + def test_help_uppercase(self): + """Test help with uppercase 'H' key.""" + self.assertFalse(self.collector.show_help) + + self.display.simulate_input(ord("H")) + self.collector._handle_input() + + self.assertTrue(self.collector.show_help) + + def test_reset_lowercase(self): + """Test reset with lowercase 'r' key.""" + # Add some stats + self.collector.total_samples = 100 + self.collector.result[("test.py", 1, "func")] = { + "direct_calls": 50, + "cumulative_calls": 75, + "total_rec_calls": 0, + } + + self.display.simulate_input(ord("r")) + self.collector._handle_input() + + self.assertEqual(self.collector.total_samples, 0) + self.assertEqual(len(self.collector.result), 0) + + def test_reset_uppercase(self): + """Test reset with uppercase 'R' key.""" + self.collector.total_samples = 100 + + self.display.simulate_input(ord("R")) + self.collector._handle_input() + + self.assertEqual(self.collector.total_samples, 0) + + def test_filter_clear_uppercase(self): + """Test clearing filter with uppercase 'C' key.""" + self.collector.filter_pattern = "test" + + self.display.simulate_input(ord("C")) + self.collector._handle_input() + + self.assertIsNone(self.collector.filter_pattern) + + def test_increase_refresh_rate_with_equals(self): + """Test increasing refresh rate with '=' key.""" + initial_interval = self.collector.display_update_interval_sec + + # Simulate '=' key press (alternative to '+') + self.display.simulate_input(ord("=")) + self.collector._handle_input() + + self.assertLess(self.collector.display_update_interval_sec, initial_interval) + + def test_decrease_refresh_rate_with_underscore(self): + """Test decreasing refresh rate with '_' key.""" + initial_interval = self.collector.display_update_interval_sec + + # Simulate '_' key press (alternative to '-') + self.display.simulate_input(ord("_")) + self.collector._handle_input() + + self.assertGreater(self.collector.display_update_interval_sec, initial_interval) + + def test_finished_state_displays_banner(self): + """Test that finished state shows prominent banner.""" + # Add some sample data + thread_info = MockThreadInfo( + 123, + [ + MockFrameInfo("test.py", 10, "work"), + MockFrameInfo("test.py", 20, "main"), + ], + ) + interpreter_info = MockInterpreterInfo(0, [thread_info]) + stack_frames = [interpreter_info] + self.collector.collect(stack_frames) + + # Mark as finished + self.collector.mark_finished() + + # Check that finished flag is set + self.assertTrue(self.collector.finished) + + # Check that the banner message is displayed + self.assertTrue(self.display.contains_text("PROFILING COMPLETE")) + self.assertTrue(self.display.contains_text("Press 'q' to Quit")) + + def test_finished_state_allows_ui_controls(self): + """Test that finished state allows UI controls but prioritizes quit.""" + self.collector.finished = True + self.collector.running = True + + # Try pressing 's' (sort) - should work and trigger display update + original_sort = self.collector.sort_by + self.display.simulate_input(ord("s")) + self.collector._handle_input() + self.assertTrue(self.collector.running) # Still running + self.assertNotEqual(self.collector.sort_by, original_sort) # Sort changed + + # Try pressing 'p' (pause) - should work + self.display.simulate_input(ord("p")) + self.collector._handle_input() + self.assertTrue(self.collector.running) # Still running + self.assertTrue(self.collector.paused) # Now paused + + # Try pressing 'r' (reset) - should be ignored when finished + self.collector.total_samples = 100 + self.display.simulate_input(ord("r")) + self.collector._handle_input() + self.assertTrue(self.collector.running) # Still running + self.assertEqual(self.collector.total_samples, 100) # NOT reset when finished + + # Press 'q' - should stop + self.display.simulate_input(ord("q")) + self.collector._handle_input() + self.assertFalse(self.collector.running) # Stopped + + def test_finished_state_footer_message(self): + """Test that footer shows appropriate message when finished.""" + # Add some sample data + thread_info = MockThreadInfo( + 123, + [ + MockFrameInfo("test.py", 10, "work"), + MockFrameInfo("test.py", 20, "main"), + ], + ) + interpreter_info = MockInterpreterInfo(0, [thread_info]) + stack_frames = [interpreter_info] + self.collector.collect(stack_frames) + + # Mark as finished + self.collector.mark_finished() + + # Check that footer contains finished message + self.assertTrue(self.display.contains_text("PROFILING FINISHED")) + + def test_finished_state_freezes_time(self): + """Test that time displays are frozen when finished.""" + + # Set up collector with known start time + self.collector.start_time = time.perf_counter() - 10.0 # 10 seconds ago + + # Mark as finished - this should freeze the time + self.collector.mark_finished() + + # Get the frozen elapsed time + frozen_elapsed = self.collector.elapsed_time + frozen_time_display = self.collector.current_time_display + + # Wait a bit to ensure time would advance + time.sleep(0.1) + + # Time should remain frozen + self.assertEqual(self.collector.elapsed_time, frozen_elapsed) + self.assertEqual(self.collector.current_time_display, frozen_time_display) + + # Verify finish timestamp was set + self.assertIsNotNone(self.collector.finish_timestamp) + + # Reset should clear the frozen state + self.collector.reset_stats() + self.assertFalse(self.collector.finished) + self.assertIsNone(self.collector.finish_timestamp) + + +class TestLiveCollectorFiltering(unittest.TestCase): + """Tests for filtering functionality.""" + + def setUp(self): + """Set up collector with test data.""" + self.display = MockDisplay(height=40, width=160) + self.collector = LiveStatsCollector( + 1000, pid=12345, display=self.display + ) + self.collector.start_time = time.perf_counter() + self.collector.total_samples = 100 + + # Add test data + self.collector.result[("app/models.py", 10, "save")] = { + "direct_calls": 50, + "cumulative_calls": 75, + "total_rec_calls": 0, + } + self.collector.result[("app/views.py", 20, "render")] = { + "direct_calls": 30, + "cumulative_calls": 40, + "total_rec_calls": 0, + } + self.collector.result[("lib/utils.py", 30, "helper")] = { + "direct_calls": 20, + "cumulative_calls": 25, + "total_rec_calls": 0, + } + + def test_filter_by_filename(self): + """Test filtering by filename pattern.""" + self.collector.filter_pattern = "models" + + stats_list = self.collector.build_stats_list() + + # Only models.py should be included + self.assertEqual(len(stats_list), 1) + self.assertIn("models.py", stats_list[0]["func"][0]) + + def test_filter_by_function_name(self): + """Test filtering by function name.""" + self.collector.filter_pattern = "render" + + stats_list = self.collector.build_stats_list() + + self.assertEqual(len(stats_list), 1) + self.assertEqual(stats_list[0]["func"][2], "render") + + def test_filter_case_insensitive(self): + """Test that filtering is case-insensitive.""" + self.collector.filter_pattern = "MODELS" + + stats_list = self.collector.build_stats_list() + + # Should still match models.py + self.assertEqual(len(stats_list), 1) + + def test_filter_substring_matching(self): + """Test substring filtering.""" + self.collector.filter_pattern = "app/" + + stats_list = self.collector.build_stats_list() + + # Should match both app files + self.assertEqual(len(stats_list), 2) + + def test_no_filter(self): + """Test with no filter applied.""" + self.collector.filter_pattern = None + + stats_list = self.collector.build_stats_list() + + # All items should be included + self.assertEqual(len(stats_list), 3) + + def test_filter_partial_function_name(self): + """Test filtering by partial function name.""" + self.collector.filter_pattern = "save" + + stats_list = self.collector.build_stats_list() + + self.assertEqual(len(stats_list), 1) + self.assertEqual(stats_list[0]["func"][2], "save") + + def test_filter_combined_filename_funcname(self): + """Test filtering matches filename:funcname pattern.""" + self.collector.filter_pattern = "views.py:render" + + stats_list = self.collector.build_stats_list() + + # Should match the combined pattern + self.assertEqual(len(stats_list), 1) + self.assertEqual(stats_list[0]["func"][2], "render") + + def test_filter_no_matches(self): + """Test filter that matches nothing.""" + self.collector.filter_pattern = "nonexistent" + + stats_list = self.collector.build_stats_list() + + self.assertEqual(len(stats_list), 0) + + +class TestLiveCollectorFilterInput(unittest.TestCase): + """Tests for filter input mode.""" + + def setUp(self): + """Set up collector with mock display.""" + self.display = MockDisplay(height=40, width=160) + self.collector = LiveStatsCollector( + 1000, pid=12345, display=self.display + ) + self.collector.start_time = time.perf_counter() + + def test_enter_filter_mode(self): + """Test entering filter input mode.""" + self.assertFalse(self.collector.filter_input_mode) + + # Press '/' to enter filter mode + self.display.simulate_input(ord("/")) + self.collector._handle_input() + + self.assertTrue(self.collector.filter_input_mode) + + def test_filter_input_typing(self): + """Test typing characters in filter input mode.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "" + + # Type 't', 'e', 's', 't' + for ch in "test": + self.display.simulate_input(ord(ch)) + self.collector._handle_input() + + self.assertEqual(self.collector.filter_input_buffer, "test") + + def test_filter_input_backspace(self): + """Test backspace in filter input mode.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "test" + + # Press backspace (127) + self.display.simulate_input(127) + self.collector._handle_input() + + self.assertEqual(self.collector.filter_input_buffer, "tes") + + def test_filter_input_backspace_alt(self): + """Test alternative backspace key (263) in filter input mode.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "test" + + # Press backspace (263) + self.display.simulate_input(263) + self.collector._handle_input() + + self.assertEqual(self.collector.filter_input_buffer, "tes") + + def test_filter_input_backspace_empty(self): + """Test backspace on empty buffer.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "" + + # Press backspace - should not crash + self.display.simulate_input(127) + self.collector._handle_input() + + self.assertEqual(self.collector.filter_input_buffer, "") + + def test_filter_input_enter_applies_filter(self): + """Test pressing Enter applies the filter.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "myfilter" + + # Press Enter (10) + self.display.simulate_input(10) + self.collector._handle_input() + + self.assertFalse(self.collector.filter_input_mode) + self.assertEqual(self.collector.filter_pattern, "myfilter") + self.assertEqual(self.collector.filter_input_buffer, "") + + def test_filter_input_enter_alt(self): + """Test alternative Enter key (13) applies filter.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "myfilter" + + # Press Enter (13) + self.display.simulate_input(13) + self.collector._handle_input() + + self.assertFalse(self.collector.filter_input_mode) + self.assertEqual(self.collector.filter_pattern, "myfilter") + + def test_filter_input_enter_empty_clears_filter(self): + """Test pressing Enter with empty buffer clears filter.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "" + self.collector.filter_pattern = "oldfilter" + + # Press Enter + self.display.simulate_input(10) + self.collector._handle_input() + + self.assertFalse(self.collector.filter_input_mode) + self.assertIsNone(self.collector.filter_pattern) + + def test_filter_input_escape_cancels(self): + """Test pressing ESC cancels filter input.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "newfilter" + self.collector.filter_pattern = "oldfilter" + + # Press ESC (27) + self.display.simulate_input(27) + self.collector._handle_input() + + self.assertFalse(self.collector.filter_input_mode) + self.assertEqual( + self.collector.filter_pattern, "oldfilter" + ) # Unchanged + self.assertEqual(self.collector.filter_input_buffer, "") + + def test_filter_input_start_with_existing_filter(self): + """Test entering filter mode with existing filter pre-fills buffer.""" + self.collector.filter_pattern = "existing" + + # Enter filter mode + self.display.simulate_input(ord("/")) + self.collector._handle_input() + + # Buffer should be pre-filled with existing pattern + self.assertEqual(self.collector.filter_input_buffer, "existing") + + def test_filter_input_start_without_filter(self): + """Test entering filter mode with no existing filter.""" + self.collector.filter_pattern = None + + # Enter filter mode + self.display.simulate_input(ord("/")) + self.collector._handle_input() + + # Buffer should be empty + self.assertEqual(self.collector.filter_input_buffer, "") + + def test_filter_input_mode_blocks_other_commands(self): + """Test that filter input mode blocks other commands.""" + self.collector.filter_input_mode = True + initial_sort = self.collector.sort_by + + # Try to press 's' (sort) - should be captured as input + self.display.simulate_input(ord("s")) + self.collector._handle_input() + + # Sort should not change, 's' should be in buffer + self.assertEqual(self.collector.sort_by, initial_sort) + self.assertEqual(self.collector.filter_input_buffer, "s") + + def test_filter_input_non_printable_ignored(self): + """Test that non-printable characters are ignored.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "test" + + # Try to input a control character (< 32) + self.display.simulate_input(1) # Ctrl-A + self.collector._handle_input() + + # Buffer should be unchanged + self.assertEqual(self.collector.filter_input_buffer, "test") + + def test_filter_input_high_ascii_ignored(self): + """Test that high ASCII characters (>= 127, except backspace) are ignored.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "test" + + # Try to input high ASCII (128) + self.display.simulate_input(128) + self.collector._handle_input() + + # Buffer should be unchanged + self.assertEqual(self.collector.filter_input_buffer, "test") + + def test_filter_prompt_displayed(self): + """Test that filter prompt is displayed when in input mode.""" + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "myfilter" + self.collector.total_samples = 10 + + self.collector._update_display() + + # Should show the filter prompt + self.assertTrue(self.display.contains_text("Function filter")) + self.assertTrue(self.display.contains_text("myfilter")) + + +class TestLiveCollectorThreadNavigation(unittest.TestCase): + """Tests for thread navigation functionality.""" + + def setUp(self): + """Set up collector with mock display and multiple threads.""" + self.mock_display = MockDisplay(height=40, width=160) + self.collector = LiveStatsCollector( + 1000, pid=12345, display=self.mock_display + ) + self.collector.start_time = time.perf_counter() + + # Simulate data from multiple threads + frames1 = [MockFrameInfo("file1.py", 10, "func1")] + frames2 = [MockFrameInfo("file2.py", 20, "func2")] + frames3 = [MockFrameInfo("file3.py", 30, "func3")] + + thread1 = MockThreadInfo(111, frames1) + thread2 = MockThreadInfo(222, frames2) + thread3 = MockThreadInfo(333, frames3) + + interpreter_info = MockInterpreterInfo(0, [thread1, thread2, thread3]) + stack_frames = [interpreter_info] + + # Collect data to populate thread IDs + self.collector.collect(stack_frames) + + def test_initial_view_mode_is_all(self): + """Test that collector starts in ALL mode.""" + self.assertEqual(self.collector.view_mode, "ALL") + self.assertEqual(self.collector.current_thread_index, 0) + + def test_thread_ids_are_tracked(self): + """Test that thread IDs are tracked during collection.""" + self.assertIn(111, self.collector.thread_ids) + self.assertIn(222, self.collector.thread_ids) + self.assertIn(333, self.collector.thread_ids) + self.assertEqual(len(self.collector.thread_ids), 3) + + def test_toggle_to_per_thread_mode(self): + """Test toggling from ALL to PER_THREAD mode with 't' key.""" + self.assertEqual(self.collector.view_mode, "ALL") + + self.mock_display.simulate_input(ord("t")) + self.collector._handle_input() + + self.assertEqual(self.collector.view_mode, "PER_THREAD") + self.assertEqual(self.collector.current_thread_index, 0) + + def test_toggle_back_to_all_mode(self): + """Test toggling back from PER_THREAD to ALL mode.""" + # Switch to PER_THREAD + self.mock_display.simulate_input(ord("t")) + self.collector._handle_input() + self.assertEqual(self.collector.view_mode, "PER_THREAD") + + # Switch back to ALL + self.mock_display.simulate_input(ord("T")) + self.collector._handle_input() + self.assertEqual(self.collector.view_mode, "ALL") + + def test_arrow_right_navigates_threads_in_per_thread_mode(self): + """Test that arrow keys navigate threads in PER_THREAD mode.""" + # Switch to PER_THREAD mode + self.mock_display.simulate_input(ord("t")) + self.collector._handle_input() + + # Navigate forward + self.assertEqual(self.collector.current_thread_index, 0) + + self.mock_display.simulate_input(curses.KEY_RIGHT) + self.collector._handle_input() + self.assertEqual(self.collector.current_thread_index, 1) + + self.mock_display.simulate_input(curses.KEY_RIGHT) + self.collector._handle_input() + self.assertEqual(self.collector.current_thread_index, 2) + + def test_arrow_left_navigates_threads_backward(self): + """Test that left arrow navigates threads backward.""" + # Switch to PER_THREAD mode + self.mock_display.simulate_input(ord("t")) + self.collector._handle_input() + + # Navigate backward (should wrap around) + self.mock_display.simulate_input(curses.KEY_LEFT) + self.collector._handle_input() + self.assertEqual( + self.collector.current_thread_index, 2 + ) # Wrapped to last + + self.mock_display.simulate_input(curses.KEY_LEFT) + self.collector._handle_input() + self.assertEqual(self.collector.current_thread_index, 1) + + def test_arrow_down_navigates_like_right(self): + """Test that down arrow works like right arrow.""" + # Switch to PER_THREAD mode + self.mock_display.simulate_input(ord("t")) + self.collector._handle_input() + + self.mock_display.simulate_input(curses.KEY_DOWN) + self.collector._handle_input() + self.assertEqual(self.collector.current_thread_index, 1) + + def test_arrow_up_navigates_like_left(self): + """Test that up arrow works like left arrow.""" + # Switch to PER_THREAD mode + self.mock_display.simulate_input(ord("t")) + self.collector._handle_input() + + self.mock_display.simulate_input(curses.KEY_UP) + self.collector._handle_input() + self.assertEqual(self.collector.current_thread_index, 2) # Wrapped + + def test_arrow_keys_switch_to_per_thread_mode(self): + """Test that arrow keys switch from ALL mode to PER_THREAD mode.""" + self.assertEqual(self.collector.view_mode, "ALL") + + self.mock_display.simulate_input(curses.KEY_RIGHT) + self.collector._handle_input() + self.assertEqual(self.collector.view_mode, "PER_THREAD") + self.assertEqual(self.collector.current_thread_index, 0) + + def test_stats_list_in_all_mode(self): + """Test that stats list uses aggregated data in ALL mode.""" + stats_list = self.collector.build_stats_list() + + # Should have all 3 functions + self.assertEqual(len(stats_list), 3) + func_names = {stat["func"][2] for stat in stats_list} + self.assertEqual(func_names, {"func1", "func2", "func3"}) + + def test_stats_list_in_per_thread_mode(self): + """Test that stats list filters by thread in PER_THREAD mode.""" + # Switch to PER_THREAD mode + self.collector.view_mode = "PER_THREAD" + self.collector.current_thread_index = 0 # First thread (111) + + stats_list = self.collector.build_stats_list() + + # Should only have func1 from thread 111 + self.assertEqual(len(stats_list), 1) + self.assertEqual(stats_list[0]["func"][2], "func1") + + def test_stats_list_switches_with_thread_navigation(self): + """Test that stats list updates when navigating threads.""" + self.collector.view_mode = "PER_THREAD" + + # Thread 0 (111) -> func1 + self.collector.current_thread_index = 0 + stats_list = self.collector.build_stats_list() + self.assertEqual(len(stats_list), 1) + self.assertEqual(stats_list[0]["func"][2], "func1") + + # Thread 1 (222) -> func2 + self.collector.current_thread_index = 1 + stats_list = self.collector.build_stats_list() + self.assertEqual(len(stats_list), 1) + self.assertEqual(stats_list[0]["func"][2], "func2") + + # Thread 2 (333) -> func3 + self.collector.current_thread_index = 2 + stats_list = self.collector.build_stats_list() + self.assertEqual(len(stats_list), 1) + self.assertEqual(stats_list[0]["func"][2], "func3") + + def test_reset_stats_clears_thread_data(self): + """Test that reset_stats clears thread tracking data.""" + self.assertGreater(len(self.collector.thread_ids), 0) + self.assertGreater(len(self.collector.per_thread_data), 0) + + self.collector.reset_stats() + + self.assertEqual(len(self.collector.thread_ids), 0) + self.assertEqual(len(self.collector.per_thread_data), 0) + self.assertEqual(self.collector.view_mode, "ALL") + self.assertEqual(self.collector.current_thread_index, 0) + + def test_toggle_with_no_threads_stays_in_all_mode(self): + """Test that toggle does nothing when no threads exist.""" + collector = LiveStatsCollector(1000, display=MockDisplay()) + self.assertEqual(len(collector.thread_ids), 0) + + collector.display.simulate_input(ord("t")) + collector._handle_input() + + # Should remain in ALL mode since no threads + self.assertEqual(collector.view_mode, "ALL") + + def test_per_thread_data_isolation(self): + """Test that per-thread data is properly isolated.""" + # Check that each thread has its own isolated data + self.assertIn(111, self.collector.per_thread_data) + self.assertIn(222, self.collector.per_thread_data) + self.assertIn(333, self.collector.per_thread_data) + + # Thread 111 should only have func1 + thread1_funcs = list(self.collector.per_thread_data[111].result.keys()) + self.assertEqual(len(thread1_funcs), 1) + self.assertEqual(thread1_funcs[0][2], "func1") + + # Thread 222 should only have func2 + thread2_funcs = list(self.collector.per_thread_data[222].result.keys()) + self.assertEqual(len(thread2_funcs), 1) + self.assertEqual(thread2_funcs[0][2], "func2") + + def test_aggregated_data_sums_all_threads(self): + """Test that ALL mode shows aggregated data from all threads.""" + # All three functions should be in the aggregated result + self.assertEqual(len(self.collector.result), 3) + + # Each function should have 1 direct call + for func_location, counts in self.collector.result.items(): + self.assertEqual(counts["direct_calls"], 1) + + def test_per_thread_status_tracking(self): + """Test that per-thread status statistics are tracked.""" + # Each thread should have status counts + self.assertIn(111, self.collector.per_thread_data) + self.assertIn(222, self.collector.per_thread_data) + self.assertIn(333, self.collector.per_thread_data) + + # Each thread should have the expected attributes + for thread_id in [111, 222, 333]: + thread_data = self.collector.per_thread_data[thread_id] + self.assertIsNotNone(thread_data.has_gil) + self.assertIsNotNone(thread_data.on_cpu) + self.assertIsNotNone(thread_data.gil_requested) + self.assertIsNotNone(thread_data.unknown) + self.assertIsNotNone(thread_data.total) + # Each thread was sampled once + self.assertEqual(thread_data.total, 1) + + def test_reset_stats_clears_thread_status(self): + """Test that reset_stats clears per-thread status data.""" + self.assertGreater(len(self.collector.per_thread_data), 0) + + self.collector.reset_stats() + + self.assertEqual(len(self.collector.per_thread_data), 0) + + def test_per_thread_sample_counts(self): + """Test that per-thread sample counts are tracked correctly.""" + # Each thread should have exactly 1 sample (we collected once) + for thread_id in [111, 222, 333]: + self.assertIn(thread_id, self.collector.per_thread_data) + self.assertEqual(self.collector.per_thread_data[thread_id].sample_count, 1) + + def test_per_thread_gc_samples(self): + """Test that per-thread GC samples are tracked correctly.""" + # Initially no threads have GC frames + for thread_id in [111, 222, 333]: + self.assertIn(thread_id, self.collector.per_thread_data) + self.assertEqual( + self.collector.per_thread_data[thread_id].gc_frame_samples, 0 + ) + + # Now collect a sample with a GC frame in thread 222 + gc_frames = [MockFrameInfo("gc.py", 100, "gc_collect")] + thread_with_gc = MockThreadInfo(222, gc_frames) + interpreter_info = MockInterpreterInfo(0, [thread_with_gc]) + stack_frames = [interpreter_info] + + self.collector.collect(stack_frames) + + # Thread 222 should now have 1 GC sample + self.assertEqual(self.collector.per_thread_data[222].gc_frame_samples, 1) + # Other threads should still have 0 + self.assertEqual(self.collector.per_thread_data[111].gc_frame_samples, 0) + self.assertEqual(self.collector.per_thread_data[333].gc_frame_samples, 0) + + def test_only_threads_with_frames_are_tracked(self): + """Test that only threads with actual frame data are added to thread_ids.""" + # Create a new collector + collector = LiveStatsCollector(1000, display=MockDisplay()) + + # Create threads: one with frames, one without + frames = [MockFrameInfo("test.py", 10, "test_func")] + thread_with_frames = MockThreadInfo(111, frames) + thread_without_frames = MockThreadInfo(222, None) # No frames + interpreter_info = MockInterpreterInfo( + 0, [thread_with_frames, thread_without_frames] + ) + stack_frames = [interpreter_info] + + collector.collect(stack_frames) + + # Only thread 111 should be tracked (it has frames) + self.assertIn(111, collector.thread_ids) + self.assertNotIn(222, collector.thread_ids) + + def test_per_thread_status_isolation(self): + """Test that per-thread status counts are isolated per thread.""" + # Create threads with different status flags + + frames1 = [MockFrameInfo("file1.py", 10, "func1")] + frames2 = [MockFrameInfo("file2.py", 20, "func2")] + + # Thread 444: has GIL but not on CPU + thread1 = MockThreadInfo(444, frames1, status=THREAD_STATUS_HAS_GIL) + # Thread 555: on CPU but not has GIL + thread2 = MockThreadInfo(555, frames2, status=THREAD_STATUS_ON_CPU) + + interpreter_info = MockInterpreterInfo(0, [thread1, thread2]) + stack_frames = [interpreter_info] + + collector = LiveStatsCollector(1000, display=MockDisplay()) + collector.collect(stack_frames) + + # Check thread 444 status + self.assertEqual(collector.per_thread_data[444].has_gil, 1) + self.assertEqual(collector.per_thread_data[444].on_cpu, 0) + + # Check thread 555 status + self.assertEqual(collector.per_thread_data[555].has_gil, 0) + self.assertEqual(collector.per_thread_data[555].on_cpu, 1) + + def test_display_uses_per_thread_stats_in_per_thread_mode(self): + """Test that display widget uses per-thread stats when in PER_THREAD mode.""" + + # Create collector with mock display + collector = LiveStatsCollector(1000, display=MockDisplay()) + collector.start_time = time.perf_counter() + + # Create 2 threads with different characteristics + # Thread 111: always has GIL (10 samples) + # Thread 222: never has GIL (10 samples) + for _ in range(10): + frames1 = [MockFrameInfo("file1.py", 10, "func1")] + frames2 = [MockFrameInfo("file2.py", 20, "func2")] + thread1 = MockThreadInfo( + 111, frames1, status=THREAD_STATUS_HAS_GIL + ) + thread2 = MockThreadInfo(222, frames2, status=0) # No flags + interpreter_info = MockInterpreterInfo(0, [thread1, thread2]) + collector.collect([interpreter_info]) + + # In ALL mode, should show mixed stats (50% on GIL, 50% off GIL) + self.assertEqual(collector.view_mode, "ALL") + total_has_gil = collector.thread_status_counts["has_gil"] + total_threads = collector.thread_status_counts["total"] + self.assertEqual(total_has_gil, 10) # Only thread 111 has GIL + self.assertEqual(total_threads, 20) # 10 samples * 2 threads + + # Switch to PER_THREAD mode and select thread 111 + collector.view_mode = "PER_THREAD" + collector.current_thread_index = 0 # Thread 111 + + # Thread 111 should show 100% on GIL + thread_111_data = collector.per_thread_data[111] + self.assertEqual(thread_111_data.has_gil, 10) + self.assertEqual(thread_111_data.total, 10) + + # Switch to thread 222 + collector.current_thread_index = 1 # Thread 222 + + # Thread 222 should show 0% on GIL + thread_222_data = collector.per_thread_data[222] + self.assertEqual(thread_222_data.has_gil, 0) + self.assertEqual(thread_222_data.total, 10) + + def test_display_uses_per_thread_gc_stats_in_per_thread_mode(self): + """Test that GC percentage uses per-thread data in PER_THREAD mode.""" + # Create collector with mock display + collector = LiveStatsCollector(1000, display=MockDisplay()) + collector.start_time = time.perf_counter() + + # Thread 111: 5 samples, 2 with GC + # Thread 222: 5 samples, 0 with GC + for i in range(5): + if i < 2: + # First 2 samples for thread 111 have GC + frames1 = [MockFrameInfo("gc.py", 100, "gc_collect")] + else: + frames1 = [MockFrameInfo("file1.py", 10, "func1")] + + frames2 = [MockFrameInfo("file2.py", 20, "func2")] # No GC + + thread1 = MockThreadInfo(111, frames1) + thread2 = MockThreadInfo(222, frames2) + interpreter_info = MockInterpreterInfo(0, [thread1, thread2]) + collector.collect([interpreter_info]) + + # Check aggregated GC stats (ALL mode) + # 2 GC samples out of 10 total = 20% + self.assertEqual(collector.gc_frame_samples, 2) + self.assertEqual(collector.total_samples, 5) # 5 collect() calls + + # Check per-thread GC stats + # Thread 111: 2 GC samples out of 5 = 40% + self.assertEqual(collector.per_thread_data[111].gc_frame_samples, 2) + self.assertEqual(collector.per_thread_data[111].sample_count, 5) + + # Thread 222: 0 GC samples out of 5 = 0% + self.assertEqual(collector.per_thread_data[222].gc_frame_samples, 0) + self.assertEqual(collector.per_thread_data[222].sample_count, 5) + + # Now verify the display would use the correct stats + collector.view_mode = "PER_THREAD" + + # For thread 111 + collector.current_thread_index = 0 + thread_id = collector.thread_ids[0] + self.assertEqual(thread_id, 111) + thread_gc_pct = ( + collector.per_thread_data[111].gc_frame_samples + / collector.per_thread_data[111].sample_count + ) * 100 + self.assertEqual(thread_gc_pct, 40.0) + + # For thread 222 + collector.current_thread_index = 1 + thread_id = collector.thread_ids[1] + self.assertEqual(thread_id, 222) + thread_gc_pct = ( + collector.per_thread_data[222].gc_frame_samples + / collector.per_thread_data[222].sample_count + ) * 100 + self.assertEqual(thread_gc_pct, 0.0) + + def test_function_counts_are_per_thread_in_per_thread_mode(self): + """Test that function counts (total/exec/stack) are per-thread in PER_THREAD mode.""" + # Create collector with mock display + collector = LiveStatsCollector(1000, display=MockDisplay()) + collector.start_time = time.perf_counter() + + # Thread 111: calls func1, func2, func3 (3 functions) + # Thread 222: calls func4, func5 (2 functions) + frames1 = [ + MockFrameInfo("file1.py", 10, "func1"), + MockFrameInfo("file1.py", 20, "func2"), + MockFrameInfo("file1.py", 30, "func3"), + ] + frames2 = [ + MockFrameInfo("file2.py", 40, "func4"), + MockFrameInfo("file2.py", 50, "func5"), + ] + + thread1 = MockThreadInfo(111, frames1) + thread2 = MockThreadInfo(222, frames2) + interpreter_info = MockInterpreterInfo(0, [thread1, thread2]) + collector.collect([interpreter_info]) + + # In ALL mode, should have 5 total functions + self.assertEqual(len(collector.result), 5) + + # In PER_THREAD mode for thread 111, should have 3 functions + collector.view_mode = "PER_THREAD" + collector.current_thread_index = 0 # Thread 111 + thread_111_result = collector.per_thread_data[111].result + self.assertEqual(len(thread_111_result), 3) + + # Verify the functions are the right ones + thread_111_funcs = {loc[2] for loc in thread_111_result.keys()} + self.assertEqual(thread_111_funcs, {"func1", "func2", "func3"}) + + # In PER_THREAD mode for thread 222, should have 2 functions + collector.current_thread_index = 1 # Thread 222 + thread_222_result = collector.per_thread_data[222].result + self.assertEqual(len(thread_222_result), 2) + + # Verify the functions are the right ones + thread_222_funcs = {loc[2] for loc in thread_222_result.keys()} + self.assertEqual(thread_222_funcs, {"func4", "func5"}) + + +class TestLiveCollectorNewFeatures(unittest.TestCase): + """Tests for new features added to live collector.""" + + def setUp(self): + """Set up test fixtures.""" + self.display = MockDisplay() + self.collector = LiveStatsCollector(1000, display=self.display) + self.collector.start_time = time.perf_counter() + + def test_filter_input_takes_precedence_over_commands(self): + """Test that filter input mode blocks command keys like 'h' and 'p'.""" + # Enter filter input mode + self.collector.filter_input_mode = True + self.collector.filter_input_buffer = "" + + # Press 'h' - should add to filter buffer, not show help + self.display.simulate_input(ord("h")) + self.collector._handle_input() + + self.assertFalse(self.collector.show_help) # Help not triggered + self.assertEqual(self.collector.filter_input_buffer, "h") # Added to filter + self.assertTrue(self.collector.filter_input_mode) # Still in filter mode + + def test_reset_blocked_when_finished(self): + """Test that reset command is blocked when profiling is finished.""" + # Set up some sample data and mark as finished + self.collector.total_samples = 100 + self.collector.finished = True + + # Press 'r' for reset + self.display.simulate_input(ord("r")) + self.collector._handle_input() + + # Should NOT have been reset + self.assertEqual(self.collector.total_samples, 100) + self.assertTrue(self.collector.finished) + + def test_time_display_fix_when_finished(self): + """Test that time display shows correct frozen time when finished.""" + + # Mark as finished to freeze time + self.collector.mark_finished() + + # Should have set both timestamps correctly + self.assertIsNotNone(self.collector.finish_timestamp) + self.assertIsNotNone(self.collector.finish_wall_time) + + # Get the frozen time display + frozen_time = self.collector.current_time_display + + # Wait a bit + time.sleep(0.1) + + # Should still show the same frozen time (not jump to wrong time) + self.assertEqual(self.collector.current_time_display, frozen_time) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py new file mode 100644 index 00000000000000..59373a8d00c03c --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_live_collector_ui.py @@ -0,0 +1,889 @@ +"""UI and display tests for LiveStatsCollector. + +Tests for MockDisplay, curses integration, display methods, +edge cases, update display, and display helpers. +""" + +import functools +import io +import sys +import tempfile +import time +import unittest +from unittest import mock +from test.support import requires, requires_remote_subprocess_debugging +from test.support.import_helper import import_module + +# Only run these tests if curses is available +requires("curses") +curses = import_module("curses") + +from profiling.sampling.live_collector import LiveStatsCollector, MockDisplay +from profiling.sampling.cli import main +from ._live_collector_helpers import ( + MockThreadInfo, + MockInterpreterInfo, +) +from .helpers import close_and_unlink + + +class TestLiveStatsCollectorWithMockDisplay(unittest.TestCase): + """Tests for display functionality using MockDisplay.""" + + def setUp(self): + """Set up collector with mock display.""" + self.mock_display = MockDisplay(height=40, width=160) + self.collector = LiveStatsCollector( + 1000, pid=12345, display=self.mock_display + ) + self.collector.start_time = time.perf_counter() + + def test_update_display_with_mock(self): + """Test that update_display works with MockDisplay.""" + self.collector.total_samples = 100 + self.collector.result[("test.py", 10, "test_func")] = { + "direct_calls": 50, + "cumulative_calls": 75, + "total_rec_calls": 0, + } + + self.collector._update_display() + + # Verify display operations were called + self.assertTrue(self.mock_display.cleared) + self.assertTrue(self.mock_display.refreshed) + self.assertTrue(self.mock_display.redrawn) + + # Verify some content was written + self.assertGreater(len(self.mock_display.buffer), 0) + + def test_handle_input_quit(self): + """Test that 'q' input stops the collector.""" + self.mock_display.simulate_input(ord("q")) + self.collector._handle_input() + self.assertFalse(self.collector.running) + + def test_handle_input_sort_cycle(self): + """Test that 's' input cycles sort mode.""" + self.collector.sort_by = "tottime" + self.mock_display.simulate_input(ord("s")) + self.collector._handle_input() + self.assertEqual(self.collector.sort_by, "cumul_pct") + + def test_draw_methods_with_mock_display(self): + """Test that draw methods write to mock display.""" + self.collector.total_samples = 500 + self.collector.successful_samples = 450 + self.collector.failed_samples = 50 + + colors = self.collector._setup_colors() + self.collector._initialize_widgets(colors) + + # Test individual widget methods + line = self.collector.header_widget.draw_header_info(0, 160, 100.5) + self.assertEqual(line, 2) # Title + header info line + self.assertGreater(len(self.mock_display.buffer), 0) + + # Clear buffer and test next method + self.mock_display.buffer.clear() + line = self.collector.header_widget.draw_sample_stats(0, 160, 10.0) + self.assertEqual(line, 1) + self.assertGreater(len(self.mock_display.buffer), 0) + + def test_terminal_too_small_message(self): + """Test terminal too small warning.""" + small_display = MockDisplay(height=10, width=50) + self.collector.display = small_display + + self.collector._show_terminal_too_small(10, 50) + + # Should have written warning message + text = small_display.get_text_at(3, 15) # Approximate center + self.assertIsNotNone(text) + + def test_full_display_rendering_with_data(self): + """Test complete display rendering with realistic data.""" + # Add multiple functions with different call counts + self.collector.total_samples = 1000 + self.collector.successful_samples = 950 + self.collector.failed_samples = 50 + + self.collector.result[("app.py", 10, "main")] = { + "direct_calls": 100, + "cumulative_calls": 500, + "total_rec_calls": 0, + } + self.collector.result[("utils.py", 20, "helper")] = { + "direct_calls": 300, + "cumulative_calls": 400, + "total_rec_calls": 0, + } + self.collector.result[("db.py", 30, "query")] = { + "direct_calls": 50, + "cumulative_calls": 100, + "total_rec_calls": 0, + } + + self.collector._update_display() + + # Verify the display has content + self.assertGreater(len(self.mock_display.buffer), 10) + + # Verify PID is shown + found_pid = False + for (line, col), (text, attr) in self.mock_display.buffer.items(): + if "12345" in text: + found_pid = True + break + self.assertTrue(found_pid, "PID should be displayed") + + def test_efficiency_bar_visualization(self): + """Test that efficiency bar shows correct proportions.""" + self.collector.total_samples = 100 + self.collector.successful_samples = 75 + self.collector.failed_samples = 25 + + colors = self.collector._setup_colors() + self.collector._initialize_widgets(colors) + self.collector.header_widget.draw_efficiency_bar(0, 160) + + # Check that something was drawn to the display + self.assertGreater(len(self.mock_display.buffer), 0) + + def test_stats_display_with_different_sort_modes(self): + """Test that stats are displayed correctly with different sort modes.""" + self.collector.total_samples = 100 + self.collector.successful_samples = 100 # For percentage calculations + self.collector.result[("a.py", 1, "func_a")] = { + "direct_calls": 10, + "cumulative_calls": 20, + "total_rec_calls": 0, + } + self.collector.result[("b.py", 2, "func_b")] = { + "direct_calls": 30, + "cumulative_calls": 40, + "total_rec_calls": 0, + } + + # Test each sort mode + for sort_mode in [ + "nsamples", + "tottime", + "cumtime", + "sample_pct", + "cumul_pct", + ]: + self.mock_display.buffer.clear() + self.collector.sort_by = sort_mode + + stats_list = self.collector.build_stats_list() + self.assertEqual(len(stats_list), 2) + + # Verify sorting worked (func_b should be first for most modes) + if sort_mode in ["nsamples", "tottime", "sample_pct"]: + self.assertEqual(stats_list[0]["func"][2], "func_b") + + def test_narrow_terminal_column_hiding(self): + """Test that columns are hidden on narrow terminals.""" + narrow_display = MockDisplay(height=40, width=70) + collector = LiveStatsCollector(1000, pid=12345, display=narrow_display) + collector.start_time = time.perf_counter() + + colors = collector._setup_colors() + collector._initialize_widgets(colors) + line, show_sample_pct, show_tottime, show_cumul_pct, show_cumtime = ( + collector.table_widget.draw_column_headers(0, 70) + ) + + # On narrow terminal, some columns should be hidden + self.assertFalse( + show_cumul_pct or show_cumtime, + "Some columns should be hidden on narrow terminal", + ) + + def test_very_narrow_terminal_minimal_columns(self): + """Test minimal display on very narrow terminal.""" + very_narrow = MockDisplay(height=40, width=60) + collector = LiveStatsCollector(1000, pid=12345, display=very_narrow) + collector.start_time = time.perf_counter() + + colors = collector._setup_colors() + collector._initialize_widgets(colors) + line, show_sample_pct, show_tottime, show_cumul_pct, show_cumtime = ( + collector.table_widget.draw_column_headers(0, 60) + ) + + # Very narrow should hide even more columns + self.assertFalse( + show_sample_pct, + "Sample % should be hidden on very narrow terminal", + ) + + def test_display_updates_only_at_interval(self): + """Test that display updates respect the update interval.""" + # Create collector with display + collector = LiveStatsCollector(1000, display=self.mock_display) + + # Simulate multiple rapid collections + thread_info = MockThreadInfo(123, []) + interpreter_info = MockInterpreterInfo(0, [thread_info]) + stack_frames = [interpreter_info] + + # First collect should update display + collector.collect(stack_frames) + first_cleared = self.mock_display.cleared + + # Reset flags + self.mock_display.cleared = False + self.mock_display.refreshed = False + + # Immediate second collect should NOT update display (too soon) + collector.collect(stack_frames) + self.assertFalse( + self.mock_display.cleared, + "Display should not update too frequently", + ) + + def test_top_functions_display(self): + """Test that top functions are highlighted correctly.""" + self.collector.total_samples = 1000 + + # Create functions with different sample counts + for i in range(10): + self.collector.result[(f"file{i}.py", i * 10, f"func{i}")] = { + "direct_calls": (10 - i) * 10, # Decreasing counts + "cumulative_calls": (10 - i) * 20, + "total_rec_calls": 0, + } + + colors = self.collector._setup_colors() + self.collector._initialize_widgets(colors) + stats_list = self.collector.build_stats_list() + + self.collector.header_widget.draw_top_functions(0, 160, stats_list) + + # Top functions section should have written something + self.assertGreater(len(self.mock_display.buffer), 0) + + +class TestLiveStatsCollectorCursesIntegration(unittest.TestCase): + """Tests for curses-related functionality using mocks.""" + + def setUp(self): + """Set up mock curses screen.""" + self.mock_stdscr = mock.MagicMock() + self.mock_stdscr.getmaxyx.return_value = (40, 160) # height, width + self.mock_stdscr.getch.return_value = -1 # No input + # Save original stdout/stderr + self._orig_stdout = sys.stdout + self._orig_stderr = sys.stderr + + def tearDown(self): + """Restore stdout/stderr if changed.""" + sys.stdout = self._orig_stdout + sys.stderr = self._orig_stderr + + def test_init_curses(self): + """Test curses initialization.""" + collector = LiveStatsCollector(1000) + + with ( + mock.patch("curses.curs_set"), + mock.patch("curses.has_colors", return_value=True), + mock.patch("curses.start_color"), + mock.patch("curses.use_default_colors"), + mock.patch("builtins.open", mock.mock_open()) as mock_open_func, + ): + collector.init_curses(self.mock_stdscr) + + self.assertIsNotNone(collector.stdscr) + self.mock_stdscr.nodelay.assert_called_with(True) + self.mock_stdscr.scrollok.assert_called_with(False) + + # Clean up properly + if collector._devnull: + collector._devnull.close() + collector._saved_stdout = None + collector._saved_stderr = None + + def test_cleanup_curses(self): + """Test curses cleanup.""" + mock_display = MockDisplay() + collector = LiveStatsCollector(1000, display=mock_display) + collector.stdscr = self.mock_stdscr + + # Mock devnull file to avoid resource warnings + mock_devnull = mock.MagicMock() + mock_saved_stdout = mock.MagicMock() + mock_saved_stderr = mock.MagicMock() + + collector._devnull = mock_devnull + collector._saved_stdout = mock_saved_stdout + collector._saved_stderr = mock_saved_stderr + + with mock.patch("curses.curs_set"): + collector.cleanup_curses() + + mock_devnull.close.assert_called_once() + # Verify stdout/stderr were set back to the saved values + self.assertEqual(sys.stdout, mock_saved_stdout) + self.assertEqual(sys.stderr, mock_saved_stderr) + # Verify the saved values were cleared + self.assertIsNone(collector._saved_stdout) + self.assertIsNone(collector._saved_stderr) + self.assertIsNone(collector._devnull) + + def test_add_str_with_mock_display(self): + """Test safe_addstr with MockDisplay.""" + mock_display = MockDisplay(height=40, width=160) + collector = LiveStatsCollector(1000, display=mock_display) + colors = collector._setup_colors() + collector._initialize_widgets(colors) + + collector.header_widget.add_str(5, 10, "Test", 0) + # Verify it was added to the buffer + self.assertIn((5, 10), mock_display.buffer) + + def test_setup_colors_with_color_support(self): + """Test color setup when colors are supported.""" + mock_display = MockDisplay(height=40, width=160) + mock_display.colors_supported = True + collector = LiveStatsCollector(1000, display=mock_display) + + colors = collector._setup_colors() + + self.assertIn("header", colors) + self.assertIn("cyan", colors) + self.assertIn("yellow", colors) + self.assertIn("green", colors) + self.assertIn("magenta", colors) + self.assertIn("red", colors) + + def test_setup_colors_without_color_support(self): + """Test color setup when colors are not supported.""" + mock_display = MockDisplay(height=40, width=160) + mock_display.colors_supported = False + collector = LiveStatsCollector(1000, display=mock_display) + + colors = collector._setup_colors() + + # Should still have all keys but with fallback values + self.assertIn("header", colors) + self.assertIn("cyan", colors) + + def test_handle_input_quit(self): + """Test handling 'q' key to quit.""" + mock_display = MockDisplay() + mock_display.simulate_input(ord("q")) + collector = LiveStatsCollector(1000, display=mock_display) + + self.assertTrue(collector.running) + collector._handle_input() + self.assertFalse(collector.running) + + def test_handle_input_quit_uppercase(self): + """Test handling 'Q' key to quit.""" + mock_display = MockDisplay() + mock_display.simulate_input(ord("Q")) + collector = LiveStatsCollector(1000, display=mock_display) + + self.assertTrue(collector.running) + collector._handle_input() + self.assertFalse(collector.running) + + def test_handle_input_cycle_sort(self): + """Test handling 's' key to cycle sort.""" + mock_display = MockDisplay() + mock_display.simulate_input(ord("s")) + collector = LiveStatsCollector( + 1000, sort_by="nsamples", display=mock_display + ) + + collector._handle_input() + self.assertEqual(collector.sort_by, "sample_pct") + + def test_handle_input_cycle_sort_uppercase(self): + """Test handling 'S' key to cycle sort backward.""" + mock_display = MockDisplay() + mock_display.simulate_input(ord("S")) + collector = LiveStatsCollector( + 1000, sort_by="nsamples", display=mock_display + ) + + collector._handle_input() + self.assertEqual(collector.sort_by, "cumtime") + + def test_handle_input_no_key(self): + """Test handling when no key is pressed.""" + mock_display = MockDisplay() + collector = LiveStatsCollector(1000, display=mock_display) + + collector._handle_input() + # Should not change state + self.assertTrue(collector.running) + + +class TestLiveStatsCollectorDisplayMethods(unittest.TestCase): + """Tests for display-related methods.""" + + def setUp(self): + """Set up collector with mock display.""" + self.mock_display = MockDisplay(height=40, width=160) + self.collector = LiveStatsCollector( + 1000, pid=12345, display=self.mock_display + ) + self.collector.start_time = time.perf_counter() + + def test_show_terminal_too_small(self): + """Test terminal too small message display.""" + self.collector._show_terminal_too_small(10, 50) + # Should have written some content to the display buffer + self.assertGreater(len(self.mock_display.buffer), 0) + + def test_draw_header_info(self): + """Test drawing header information.""" + colors = { + "cyan": curses.A_BOLD, + "green": curses.A_BOLD, + "yellow": curses.A_BOLD, + "magenta": curses.A_BOLD, + } + self.collector._initialize_widgets(colors) + + line = self.collector.header_widget.draw_header_info(0, 160, 100.5) + self.assertEqual(line, 2) # Title + header info line + + def test_draw_sample_stats(self): + """Test drawing sample statistics.""" + self.collector.total_samples = 1000 + colors = {"cyan": curses.A_BOLD, "green": curses.A_BOLD} + self.collector._initialize_widgets(colors) + + line = self.collector.header_widget.draw_sample_stats(0, 160, 10.0) + self.assertEqual(line, 1) + self.assertGreater(self.collector.max_sample_rate, 0) + + def test_progress_bar_uses_target_rate(self): + """Test that progress bar uses target rate instead of max rate.""" + # Set up collector with specific sampling interval + collector = LiveStatsCollector( + 10000, pid=12345, display=self.mock_display + ) # 10ms = 100Hz target + collector.start_time = time.perf_counter() + collector.total_samples = 500 + collector.max_sample_rate = ( + 150 # Higher than target to test we don't use this + ) + + colors = {"cyan": curses.A_BOLD, "green": curses.A_BOLD} + collector._initialize_widgets(colors) + + # Clear the display buffer to capture only our progress bar content + self.mock_display.buffer.clear() + + # Draw sample stats with a known elapsed time that gives us a specific sample rate + elapsed = 10.0 # 500 samples in 10 seconds = 50 samples/second + line = collector.header_widget.draw_sample_stats(0, 160, elapsed) + + # Verify display was updated + self.assertEqual(line, 1) + self.assertGreater(len(self.mock_display.buffer), 0) + + # Verify the label shows current/target format with units instead of "max" + found_current_target_label = False + found_max_label = False + for (line_num, col), (text, attr) in self.mock_display.buffer.items(): + # Should show "50.0Hz/100.0Hz (50.0%)" since we're at 50% of target (50/100) + if "50.0Hz/100.0Hz" in text and "50.0%" in text: + found_current_target_label = True + if "max:" in text: + found_max_label = True + + self.assertTrue( + found_current_target_label, + "Should display current/target rate with percentage", + ) + self.assertFalse(found_max_label, "Should not display max rate label") + + def test_progress_bar_different_intervals(self): + """Test that progress bar adapts to different sampling intervals.""" + test_cases = [ + ( + 1000, + "1.0KHz", + "100.0Hz", + ), # 1ms interval -> 1000Hz target (1.0KHz), 100Hz current + ( + 5000, + "200.0Hz", + "100.0Hz", + ), # 5ms interval -> 200Hz target, 100Hz current + ( + 20000, + "50.0Hz", + "100.0Hz", + ), # 20ms interval -> 50Hz target, 100Hz current + ( + 100000, + "10.0Hz", + "100.0Hz", + ), # 100ms interval -> 10Hz target, 100Hz current + ] + + for ( + interval_usec, + expected_target_formatted, + expected_current_formatted, + ) in test_cases: + with self.subTest(interval=interval_usec): + collector = LiveStatsCollector( + interval_usec, display=MockDisplay() + ) + collector.start_time = time.perf_counter() + collector.total_samples = 100 + + colors = {"cyan": curses.A_BOLD, "green": curses.A_BOLD} + collector._initialize_widgets(colors) + + # Clear buffer + collector.display.buffer.clear() + + # Draw with 1 second elapsed time (gives us current rate of 100Hz) + collector.header_widget.draw_sample_stats(0, 160, 1.0) + + # Check that the current/target format appears in the display with proper units + found_current_target_format = False + for (line_num, col), ( + text, + attr, + ) in collector.display.buffer.items(): + # Looking for format like "100.0Hz/1.0KHz" or "100.0Hz/200.0Hz" + expected_format = f"{expected_current_formatted}/{expected_target_formatted}" + if expected_format in text and "%" in text: + found_current_target_format = True + break + + self.assertTrue( + found_current_target_format, + f"Should display current/target rate format with units for {interval_usec}µs interval", + ) + + def test_draw_efficiency_bar(self): + """Test drawing efficiency bar.""" + self.collector.successful_samples = 900 + self.collector.failed_samples = 100 + self.collector.total_samples = 1000 + colors = {"green": curses.A_BOLD, "red": curses.A_BOLD} + self.collector._initialize_widgets(colors) + + line = self.collector.header_widget.draw_efficiency_bar(0, 160) + self.assertEqual(line, 1) + + def test_draw_function_stats(self): + """Test drawing function statistics.""" + self.collector.result[("test.py", 10, "func1")] = { + "direct_calls": 100, + "cumulative_calls": 150, + "total_rec_calls": 0, + } + self.collector.result[("test.py", 20, "func2")] = { + "direct_calls": 0, + "cumulative_calls": 50, + "total_rec_calls": 0, + } + + stats_list = self.collector.build_stats_list() + colors = { + "cyan": curses.A_BOLD, + "green": curses.A_BOLD, + "yellow": curses.A_BOLD, + "magenta": curses.A_BOLD, + } + self.collector._initialize_widgets(colors) + + line = self.collector.header_widget.draw_function_stats( + 0, 160, stats_list + ) + self.assertEqual(line, 1) + + def test_draw_top_functions(self): + """Test drawing top functions.""" + self.collector.total_samples = 300 + self.collector.result[("test.py", 10, "hot_func")] = { + "direct_calls": 100, + "cumulative_calls": 150, + "total_rec_calls": 0, + } + + stats_list = self.collector.build_stats_list() + colors = { + "red": curses.A_BOLD, + "yellow": curses.A_BOLD, + "green": curses.A_BOLD, + } + self.collector._initialize_widgets(colors) + + line = self.collector.header_widget.draw_top_functions( + 0, 160, stats_list + ) + self.assertEqual(line, 1) + + def test_draw_column_headers(self): + """Test drawing column headers.""" + colors = { + "sorted_header": curses.A_BOLD, + "normal_header": curses.A_NORMAL, + } + self.collector._initialize_widgets(colors) + + ( + line, + show_sample_pct, + show_tottime, + show_cumul_pct, + show_cumtime, + ) = self.collector.table_widget.draw_column_headers(0, 160) + self.assertEqual(line, 1) + self.assertTrue(show_sample_pct) + self.assertTrue(show_tottime) + self.assertTrue(show_cumul_pct) + self.assertTrue(show_cumtime) + + def test_draw_column_headers_narrow_terminal(self): + """Test column headers adapt to narrow terminal.""" + colors = { + "sorted_header": curses.A_BOLD, + "normal_header": curses.A_NORMAL, + } + self.collector._initialize_widgets(colors) + + ( + line, + show_sample_pct, + show_tottime, + show_cumul_pct, + show_cumtime, + ) = self.collector.table_widget.draw_column_headers(0, 70) + self.assertEqual(line, 1) + # Some columns should be hidden on narrow terminal + self.assertFalse(show_cumul_pct) + + def test_draw_footer(self): + """Test drawing footer.""" + colors = self.collector._setup_colors() + self.collector._initialize_widgets(colors) + self.collector.footer_widget.render(38, 160) + # Should have written some content to the display buffer + self.assertGreater(len(self.mock_display.buffer), 0) + + def test_draw_progress_bar(self): + """Test progress bar drawing.""" + colors = self.collector._setup_colors() + self.collector._initialize_widgets(colors) + bar, length = self.collector.header_widget.progress_bar.render_bar( + 50, 100, 30 + ) + + self.assertIn("[", bar) + self.assertIn("]", bar) + self.assertGreater(length, 0) + # Should be roughly 50% filled + self.assertIn("█", bar) + self.assertIn("░", bar) + + +class TestLiveStatsCollectorEdgeCases(unittest.TestCase): + """Tests for edge cases and error handling.""" + + def test_very_long_function_name(self): + """Test handling of very long function names.""" + collector = LiveStatsCollector(1000) + long_name = "x" * 200 + collector.result[("test.py", 10, long_name)] = { + "direct_calls": 10, + "cumulative_calls": 20, + "total_rec_calls": 0, + } + + stats_list = collector.build_stats_list() + self.assertEqual(len(stats_list), 1) + self.assertEqual(stats_list[0]["func"][2], long_name) + + +class TestLiveStatsCollectorUpdateDisplay(unittest.TestCase): + """Tests for the _update_display method.""" + + def setUp(self): + """Set up collector with mock display.""" + self.mock_display = MockDisplay(height=40, width=160) + self.collector = LiveStatsCollector( + 1000, pid=12345, display=self.mock_display + ) + self.collector.start_time = time.perf_counter() + + def test_update_display_terminal_too_small(self): + """Test update_display when terminal is too small.""" + small_display = MockDisplay(height=10, width=50) + self.collector.display = small_display + + with mock.patch.object( + self.collector, "_show_terminal_too_small" + ) as mock_show: + self.collector._update_display() + mock_show.assert_called_once() + + def test_update_display_normal(self): + """Test normal update_display operation.""" + self.collector.total_samples = 100 + self.collector.successful_samples = 90 + self.collector.failed_samples = 10 + self.collector.result[("test.py", 10, "func")] = { + "direct_calls": 50, + "cumulative_calls": 75, + "total_rec_calls": 0, + } + + self.collector._update_display() + + self.assertTrue(self.mock_display.cleared) + self.assertTrue(self.mock_display.refreshed) + + def test_update_display_handles_exception(self): + """Test that update_display handles exceptions gracefully.""" + # Make one of the methods raise an exception + with mock.patch.object( + self.collector, + "_prepare_display_data", + side_effect=Exception("Test error"), + ): + # Should not raise an exception (it catches and logs via trace_exception) + try: + self.collector._update_display() + except Exception: + self.fail( + "_update_display should handle exceptions gracefully" + ) + + +class TestLiveCollectorWithMockDisplayHelpers(unittest.TestCase): + """Tests using the new MockDisplay helper methods.""" + + def test_verify_pid_display_with_contains(self): + """Test verifying PID is displayed using contains_text helper.""" + display = MockDisplay(height=40, width=160) + collector = LiveStatsCollector(1000, pid=99999, display=display) + collector.start_time = time.perf_counter() + collector.total_samples = 10 + + collector._update_display() + + # Use the helper method + self.assertTrue( + display.contains_text("99999"), "PID should be visible in display" + ) + + def test_verify_function_names_displayed(self): + """Test verifying function names appear in display.""" + display = MockDisplay(height=40, width=160) + collector = LiveStatsCollector(1000, pid=12345, display=display) + collector.start_time = time.perf_counter() + + collector.total_samples = 100 + collector.result[("mymodule.py", 42, "my_special_function")] = { + "direct_calls": 50, + "cumulative_calls": 75, + "total_rec_calls": 0, + } + + collector._update_display() + + # Verify function name appears + self.assertTrue( + display.contains_text("my_special_function"), + "Function name should be visible", + ) + + def test_get_all_lines_full_display(self): + """Test getting all lines from a full display render.""" + display = MockDisplay(height=40, width=160) + collector = LiveStatsCollector(1000, pid=12345, display=display) + collector.start_time = time.perf_counter() + collector.total_samples = 100 + + collector._update_display() + + lines = display.get_all_lines() + + # Should have multiple lines of content + self.assertGreater(len(lines), 5) + + # Should have header content + self.assertTrue(any("PID" in line for line in lines)) + + +@requires_remote_subprocess_debugging() +class TestLiveModeErrors(unittest.TestCase): + """Tests running error commands in the live mode fails gracefully.""" + + def mock_curses_wrapper(self, func): + func(mock.MagicMock()) + + def mock_init_curses_side_effect(self, n_times, mock_self, stdscr): + mock_self.display = MockDisplay() + # Allow the loop to run for a bit (approx 0.5s) before quitting + # This ensures we don't exit too early while the subprocess is + # still failing + for _ in range(n_times): + mock_self.display.simulate_input(-1) + mock_self.display.simulate_input(ord('q')) + + def test_run_failed_module_live(self): + """Test that running a existing module that fails exits with clean error.""" + + args = [ + "profiling.sampling.cli", "run", "--live", "-m", "test", + "test_asdasd" + ] + + with ( + mock.patch( + 'profiling.sampling.live_collector.collector.LiveStatsCollector.init_curses', + autospec=True, + side_effect=functools.partial(self.mock_init_curses_side_effect, 1000) + ), + mock.patch('curses.wrapper', side_effect=self.mock_curses_wrapper), + mock.patch("sys.argv", args), + mock.patch('sys.stderr', new=io.StringIO()) as fake_stderr + ): + main() + self.assertIn( + 'test test_asdasd crashed -- Traceback (most recent call last):', + fake_stderr.getvalue() + ) + + def test_run_failed_script_live(self): + """Test that running a failing script exits with clean error.""" + script = tempfile.NamedTemporaryFile(suffix=".py") + self.addCleanup(close_and_unlink, script) + script.write(b'1/0\n') + script.seek(0) + + args = ["profiling.sampling.cli", "run", "--live", script.name] + + with ( + mock.patch( + 'profiling.sampling.live_collector.collector.LiveStatsCollector.init_curses', + autospec=True, + side_effect=functools.partial(self.mock_init_curses_side_effect, 200) + ), + mock.patch('curses.wrapper', side_effect=self.mock_curses_wrapper), + mock.patch("sys.argv", args), + mock.patch('sys.stderr', new=io.StringIO()) as fake_stderr + ): + main() + stderr = fake_stderr.getvalue() + self.assertIn('ZeroDivisionError', stderr) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_modes.py b/Lib/test/test_profiling/test_sampling_profiler/test_modes.py new file mode 100644 index 00000000000000..0b38fb4ad4bcf6 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_modes.py @@ -0,0 +1,587 @@ +"""Tests for sampling profiler mode filtering (CPU and GIL modes).""" + +import io +import unittest +from unittest import mock + +try: + import _remote_debugging # noqa: F401 + import profiling.sampling + import profiling.sampling.sample + from profiling.sampling.pstats_collector import PstatsCollector + from profiling.sampling.cli import main, _parse_mode + from profiling.sampling.constants import PROFILING_MODE_EXCEPTION + from _remote_debugging import ( + THREAD_STATUS_HAS_GIL, + THREAD_STATUS_ON_CPU, + ) +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) + +from test.support import requires_remote_subprocess_debugging + +from .helpers import test_subprocess +from .mocks import MockFrameInfo, MockInterpreterInfo + + +@requires_remote_subprocess_debugging() +class TestCpuModeFiltering(unittest.TestCase): + """Test CPU mode filtering functionality (--mode=cpu).""" + + def test_mode_validation(self): + """Test that CLI validates mode choices correctly.""" + # Invalid mode choice should raise SystemExit + test_args = [ + "profiling.sampling.cli", + "attach", + "12345", + "--mode", + "invalid", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("sys.stderr", io.StringIO()) as mock_stderr, + self.assertRaises(SystemExit) as cm, + ): + main() + + self.assertEqual(cm.exception.code, 2) # argparse error + error_msg = mock_stderr.getvalue() + self.assertIn("invalid choice", error_msg) + + def test_frames_filtered_with_skip_idle(self): + """Test that frames are actually filtered when skip_idle=True.""" + # Create mock frames with different thread statuses + class MockThreadInfoWithStatus: + def __init__(self, thread_id, frame_info, status): + self.thread_id = thread_id + self.frame_info = frame_info + self.status = status + + # Create test data: active thread (HAS_GIL | ON_CPU), idle thread (neither), and another active thread + ACTIVE_STATUS = ( + THREAD_STATUS_HAS_GIL | THREAD_STATUS_ON_CPU + ) # Has GIL and on CPU + IDLE_STATUS = 0 # Neither has GIL nor on CPU + + test_frames = [ + MockInterpreterInfo( + 0, + [ + MockThreadInfoWithStatus( + 1, + [MockFrameInfo("active1.py", 10, "active_func1")], + ACTIVE_STATUS, + ), + MockThreadInfoWithStatus( + 2, + [MockFrameInfo("idle.py", 20, "idle_func")], + IDLE_STATUS, + ), + MockThreadInfoWithStatus( + 3, + [MockFrameInfo("active2.py", 30, "active_func2")], + ACTIVE_STATUS, + ), + ], + ) + ] + + # Test with skip_idle=True - should only process running threads + collector_skip = PstatsCollector( + sample_interval_usec=1000, skip_idle=True + ) + collector_skip.collect(test_frames) + + # Should only have functions from running threads (status 0) + active1_key = ("active1.py", 10, "active_func1") + active2_key = ("active2.py", 30, "active_func2") + idle_key = ("idle.py", 20, "idle_func") + + self.assertIn(active1_key, collector_skip.result) + self.assertIn(active2_key, collector_skip.result) + self.assertNotIn( + idle_key, collector_skip.result + ) # Idle thread should be filtered out + + # Test with skip_idle=False - should process all threads + collector_no_skip = PstatsCollector( + sample_interval_usec=1000, skip_idle=False + ) + collector_no_skip.collect(test_frames) + + # Should have functions from all threads + self.assertIn(active1_key, collector_no_skip.result) + self.assertIn(active2_key, collector_no_skip.result) + self.assertIn( + idle_key, collector_no_skip.result + ) # Idle thread should be included + + def test_cpu_mode_integration_filtering(self): + """Integration test: CPU mode should only capture active threads, not idle ones.""" + # Script with one mostly-idle thread and one CPU-active thread + cpu_vs_idle_script = """ +import time +import threading + +cpu_ready = threading.Event() + +def idle_worker(): + time.sleep(999999) + +def cpu_active_worker(): + cpu_ready.set() + x = 1 + while True: + x += 1 + +idle_thread = threading.Thread(target=idle_worker) +cpu_thread = threading.Thread(target=cpu_active_worker) +idle_thread.start() +cpu_thread.start() +cpu_ready.wait() +_test_sock.sendall(b"working") +idle_thread.join() +cpu_thread.join() +""" + with test_subprocess(cpu_vs_idle_script, wait_for_working=True) as subproc: + + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=5000, skip_idle=True) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=2.0, + mode=1, # CPU mode + all_threads=True, + ) + collector.print_stats(show_summary=False, mode=1) + + cpu_mode_output = captured_output.getvalue() + + # Test wall-clock mode (mode=0) - should capture both functions + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=5000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=2.0, + mode=0, # Wall-clock mode + all_threads=True, + ) + collector.print_stats(show_summary=False) + + wall_mode_output = captured_output.getvalue() + + # Verify both modes captured samples + self.assertIn("Captured", cpu_mode_output) + self.assertIn("samples", cpu_mode_output) + self.assertIn("Captured", wall_mode_output) + self.assertIn("samples", wall_mode_output) + + # CPU mode should strongly favor cpu_active_worker over mostly_idle_worker + self.assertIn("cpu_active_worker", cpu_mode_output) + self.assertNotIn("idle_worker", cpu_mode_output) + + # Wall-clock mode should capture both types of work + self.assertIn("cpu_active_worker", wall_mode_output) + self.assertIn("idle_worker", wall_mode_output) + + def test_cpu_mode_with_no_samples(self): + """Test that CPU mode handles no samples gracefully when no samples are collected.""" + # Mock a collector that returns empty stats + mock_collector = PstatsCollector(sample_interval_usec=5000, skip_idle=True) + mock_collector.stats = {} + + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + mock.patch( + "profiling.sampling.sample.SampleProfiler" + ) as mock_profiler_class, + ): + mock_profiler = mock.MagicMock() + mock_profiler_class.return_value = mock_profiler + + profiling.sampling.sample.sample( + 12345, # dummy PID + mock_collector, + duration_sec=0.5, + mode=1, # CPU mode + all_threads=True, + ) + + mock_collector.print_stats(show_summary=False, mode=1) + + output = captured_output.getvalue() + + # Should see the "No samples were collected" message + self.assertIn("No samples were collected", output) + self.assertIn("CPU mode", output) + + +@requires_remote_subprocess_debugging() +class TestGilModeFiltering(unittest.TestCase): + """Test GIL mode filtering functionality (--mode=gil).""" + + def test_gil_mode_validation(self): + """Test that CLI accepts gil mode choice correctly.""" + + test_args = [ + "profiling.sampling.cli", + "attach", + "12345", + "--mode", + "gil", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + try: + main() + except (SystemExit, OSError, RuntimeError): + pass # Expected due to invalid PID + + # Should have attempted to call sample with mode=2 (GIL mode) + mock_sample.assert_called_once() + call_args = mock_sample.call_args + # Check the mode parameter (should be in kwargs) + self.assertEqual(call_args.kwargs.get("mode"), 2) # PROFILING_MODE_GIL + + def test_gil_mode_sample_function_call(self): + """Test that sample() function correctly uses GIL mode.""" + with ( + mock.patch( + "profiling.sampling.sample.SampleProfiler" + ) as mock_profiler, + ): + # Mock the profiler instance + mock_instance = mock.Mock() + mock_profiler.return_value = mock_instance + + # Create a real collector instance + collector = PstatsCollector(sample_interval_usec=1000, skip_idle=True) + + # Call sample with GIL mode + profiling.sampling.sample.sample( + 12345, + collector, + mode=2, # PROFILING_MODE_GIL + duration_sec=1, + ) + + # Verify SampleProfiler was created with correct mode + mock_profiler.assert_called_once() + call_args = mock_profiler.call_args + self.assertEqual(call_args[1]["mode"], 2) # mode parameter + + # Verify profiler.sample was called + mock_instance.sample.assert_called_once() + + def test_gil_mode_cli_argument_parsing(self): + """Test CLI argument parsing for GIL mode with various options.""" + + test_args = [ + "profiling.sampling.cli", + "attach", + "12345", + "--mode", + "gil", + "-r", + "2000", + "-d", + "5", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + try: + main() + except (SystemExit, OSError, RuntimeError): + pass # Expected due to invalid PID + + # Verify all arguments were parsed correctly + mock_sample.assert_called_once() + call_args = mock_sample.call_args + self.assertEqual(call_args.kwargs.get("mode"), 2) # GIL mode + self.assertEqual(call_args.kwargs.get("duration_sec"), 5) + + def test_gil_mode_integration_behavior(self): + """Integration test: GIL mode should capture GIL-holding threads.""" + # Create a test script with GIL-releasing operations + gil_test_script = """ +import time +import threading + +gil_ready = threading.Event() + +def gil_releasing_work(): + time.sleep(999999) + +def gil_holding_work(): + gil_ready.set() + x = 1 + while True: + x += 1 + +idle_thread = threading.Thread(target=gil_releasing_work) +cpu_thread = threading.Thread(target=gil_holding_work) +idle_thread.start() +cpu_thread.start() +gil_ready.wait() +_test_sock.sendall(b"working") +idle_thread.join() +cpu_thread.join() +""" + with test_subprocess(gil_test_script, wait_for_working=True) as subproc: + + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=5000, skip_idle=True) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=2.0, + mode=2, # GIL mode + all_threads=True, + ) + collector.print_stats(show_summary=False) + + gil_mode_output = captured_output.getvalue() + + # Test wall-clock mode for comparison + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=5000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=0.5, + mode=0, # Wall-clock mode + all_threads=True, + ) + collector.print_stats(show_summary=False) + + wall_mode_output = captured_output.getvalue() + + # GIL mode should primarily capture GIL-holding work + # (Note: actual behavior depends on threading implementation) + self.assertIn("gil_holding_work", gil_mode_output) + + # Wall-clock mode should capture both types of work + self.assertIn("gil_holding_work", wall_mode_output) + + def test_mode_constants_are_defined(self): + """Test that all profiling mode constants are properly defined.""" + self.assertEqual(profiling.sampling.sample.PROFILING_MODE_WALL, 0) + self.assertEqual(profiling.sampling.sample.PROFILING_MODE_CPU, 1) + self.assertEqual(profiling.sampling.sample.PROFILING_MODE_GIL, 2) + + def test_parse_mode_function(self): + """Test the _parse_mode function with all valid modes.""" + self.assertEqual(_parse_mode("wall"), 0) + self.assertEqual(_parse_mode("cpu"), 1) + self.assertEqual(_parse_mode("gil"), 2) + self.assertEqual(_parse_mode("exception"), 4) + + # Test invalid mode raises KeyError + with self.assertRaises(KeyError): + _parse_mode("invalid") + + +@requires_remote_subprocess_debugging() +class TestExceptionModeFiltering(unittest.TestCase): + """Test exception mode filtering functionality (--mode=exception).""" + + def test_exception_mode_validation(self): + """Test that CLI accepts exception mode choice correctly.""" + + test_args = [ + "profiling.sampling.cli", + "attach", + "12345", + "--mode", + "exception", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + try: + main() + except (SystemExit, OSError, RuntimeError): + pass # Expected due to invalid PID + + # Should have attempted to call sample with mode=4 (exception mode) + mock_sample.assert_called_once() + call_args = mock_sample.call_args + # Check the mode parameter (should be in kwargs) + self.assertEqual(call_args.kwargs.get("mode"), 4) # PROFILING_MODE_EXCEPTION + + def test_exception_mode_sample_function_call(self): + """Test that sample() function correctly uses exception mode.""" + with ( + mock.patch( + "profiling.sampling.sample.SampleProfiler" + ) as mock_profiler, + ): + # Mock the profiler instance + mock_instance = mock.Mock() + mock_profiler.return_value = mock_instance + + # Create a real collector instance + collector = PstatsCollector(sample_interval_usec=1000, skip_idle=True) + + # Call sample with exception mode + profiling.sampling.sample.sample( + 12345, + collector, + mode=4, # PROFILING_MODE_EXCEPTION + duration_sec=1, + ) + + # Verify SampleProfiler was created with correct mode + mock_profiler.assert_called_once() + call_args = mock_profiler.call_args + self.assertEqual(call_args[1]["mode"], 4) # mode parameter + + # Verify profiler.sample was called + mock_instance.sample.assert_called_once() + + def test_exception_mode_cli_argument_parsing(self): + """Test CLI argument parsing for exception mode with various options.""" + + test_args = [ + "profiling.sampling.cli", + "attach", + "12345", + "--mode", + "exception", + "-r", + "2000", + "-d", + "5", + ] + + with ( + mock.patch("sys.argv", test_args), + mock.patch("profiling.sampling.cli._is_process_running", return_value=True), + mock.patch("profiling.sampling.cli.sample") as mock_sample, + ): + try: + main() + except (SystemExit, OSError, RuntimeError): + pass # Expected due to invalid PID + + # Verify all arguments were parsed correctly + mock_sample.assert_called_once() + call_args = mock_sample.call_args + self.assertEqual(call_args.kwargs.get("mode"), 4) # exception mode + self.assertEqual(call_args.kwargs.get("duration_sec"), 5) + + def test_exception_mode_constants_are_defined(self): + """Test that exception mode constant is properly defined.""" + self.assertEqual(PROFILING_MODE_EXCEPTION, 4) + + def test_exception_mode_integration_filtering(self): + """Integration test: Exception mode should only capture threads with active exceptions.""" + # Script with one thread handling an exception and one normal thread + exception_vs_normal_script = """ +import time +import threading + +exception_ready = threading.Event() + +def normal_worker(): + x = 0 + while True: + x += 1 + +def exception_handling_worker(): + try: + raise ValueError("test exception") + except ValueError: + # Signal AFTER entering except block, then do CPU work + exception_ready.set() + x = 0 + while True: + x += 1 + +normal_thread = threading.Thread(target=normal_worker) +exception_thread = threading.Thread(target=exception_handling_worker) +normal_thread.start() +exception_thread.start() +exception_ready.wait() +_test_sock.sendall(b"working") +normal_thread.join() +exception_thread.join() +""" + with test_subprocess(exception_vs_normal_script, wait_for_working=True) as subproc: + + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=5000, skip_idle=True) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=2.0, + mode=4, # Exception mode + all_threads=True, + ) + collector.print_stats(show_summary=False, mode=4) + + exception_mode_output = captured_output.getvalue() + + # Test wall-clock mode (mode=0) - should capture both functions + with ( + io.StringIO() as captured_output, + mock.patch("sys.stdout", captured_output), + ): + collector = PstatsCollector(sample_interval_usec=5000, skip_idle=False) + profiling.sampling.sample.sample( + subproc.process.pid, + collector, + duration_sec=2.0, + mode=0, # Wall-clock mode + all_threads=True, + ) + collector.print_stats(show_summary=False) + + wall_mode_output = captured_output.getvalue() + + # Verify both modes captured samples + self.assertIn("Captured", exception_mode_output) + self.assertIn("samples", exception_mode_output) + self.assertIn("Captured", wall_mode_output) + self.assertIn("samples", wall_mode_output) + + # Exception mode should strongly favor exception_handling_worker over normal_worker + self.assertIn("exception_handling_worker", exception_mode_output) + self.assertNotIn("normal_worker", exception_mode_output) + + # Wall-clock mode should capture both types of work + self.assertIn("exception_handling_worker", wall_mode_output) + self.assertIn("normal_worker", wall_mode_output) diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_profiler.py b/Lib/test/test_profiling/test_sampling_profiler/test_profiler.py new file mode 100644 index 00000000000000..2f5a5e27328659 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_profiler.py @@ -0,0 +1,878 @@ +"""Tests for sampling profiler core functionality.""" + +import contextlib +import io +import re +import types +from unittest import mock +import unittest + +try: + import _remote_debugging # noqa: F401 + from profiling.sampling.constants import PROFILING_MODE_ALL, PROFILING_MODE_WALL + from profiling.sampling.sample import SampleProfiler, dump_stack + from profiling.sampling.pstats_collector import PstatsCollector +except ImportError: + raise unittest.SkipTest( + "Test only runs when _remote_debugging is available" + ) + +from test.support import force_not_colorized_test_class + + +def print_sampled_stats(stats, sort=-1, limit=None, show_summary=True, sample_interval_usec=100): + """Helper function to maintain compatibility with old test API. + + This wraps the new PstatsCollector.print_stats() API to work with the + existing test infrastructure. + """ + # Create a mock collector that populates stats correctly + collector = PstatsCollector(sample_interval_usec=sample_interval_usec) + + # Override create_stats to populate self.stats with the provided stats + def mock_create_stats(): + collector.stats = stats.stats + collector.create_stats = mock_create_stats + + # Call the new print_stats method + collector.print_stats(sort=sort, limit=limit, show_summary=show_summary) + + +class TestSampleProfiler(unittest.TestCase): + """Test the SampleProfiler class.""" + + def test_sample_profiler_initialization(self): + """Test SampleProfiler initialization with various parameters.""" + + # Mock RemoteUnwinder to avoid permission issues + with mock.patch( + "_remote_debugging.RemoteUnwinder" + ) as mock_unwinder_class: + mock_unwinder_class.return_value = mock.MagicMock() + + # Test basic initialization + profiler = SampleProfiler( + pid=12345, sample_interval_usec=1000, all_threads=False + ) + self.assertEqual(profiler.pid, 12345) + self.assertEqual(profiler.sample_interval_usec, 1000) + self.assertEqual(profiler.all_threads, False) + + # Test with all_threads=True + profiler = SampleProfiler( + pid=54321, sample_interval_usec=5000, all_threads=True + ) + self.assertEqual(profiler.pid, 54321) + self.assertEqual(profiler.sample_interval_usec, 5000) + self.assertEqual(profiler.all_threads, True) + + @staticmethod + @contextlib.contextmanager + def _patched_unwinder(): + """Yield a namespace exposing the mock unwinder ``instance`` and ``cls``.""" + instance = mock.MagicMock() + with mock.patch( + "_remote_debugging.RemoteUnwinder", return_value=instance + ) as cls: + yield types.SimpleNamespace(instance=instance, cls=cls) + + def test_dump_stack_uses_single_unwinder_snapshot(self): + stack_frames = [mock.sentinel.stack_frames] + with self._patched_unwinder() as u: + u.instance.get_stack_trace.return_value = stack_frames + result = dump_stack(12345) + + self.assertIs(result, stack_frames) + self.assertEqual(u.cls.call_args.kwargs["mode"], PROFILING_MODE_ALL) + u.instance.get_stack_trace.assert_called_once_with() + + def test_dump_stack_returns_empty_async_snapshot(self): + with self._patched_unwinder() as u: + u.instance.get_async_stack_trace.return_value = [] + result = dump_stack(12345, async_aware="running") + + self.assertEqual(result, []) + u.instance.get_async_stack_trace.assert_called_once_with() + u.instance.get_stack_trace.assert_not_called() + + def test_dump_stack_async_all_uses_all_awaited_by(self): + stack_frames = [mock.sentinel.awaited_info] + with self._patched_unwinder() as u: + u.instance.get_all_awaited_by.return_value = stack_frames + result = dump_stack(12345, async_aware="all") + + self.assertIs(result, stack_frames) + u.instance.get_all_awaited_by.assert_called_once_with() + u.instance.get_stack_trace.assert_not_called() + u.instance.get_async_stack_trace.assert_not_called() + + def test_dump_stack_passes_unwinder_options(self): + with self._patched_unwinder() as u: + u.instance.get_stack_trace.return_value = [] + dump_stack( + 12345, + all_threads=True, + native=True, + gc=False, + opcodes=True, + ) + + call_kwargs = u.cls.call_args.kwargs + self.assertTrue(call_kwargs["all_threads"]) + self.assertEqual(call_kwargs["mode"], PROFILING_MODE_ALL) + self.assertTrue(call_kwargs["native"]) + self.assertFalse(call_kwargs["gc"]) + self.assertTrue(call_kwargs["opcodes"]) + self.assertFalse(call_kwargs["skip_non_matching_threads"]) + self.assertTrue(call_kwargs["cache_frames"]) + + def test_dump_stack_wall_mode_skips_non_matching_threads(self): + with self._patched_unwinder() as u: + u.instance.get_stack_trace.return_value = [] + dump_stack(12345, mode=PROFILING_MODE_WALL) + + self.assertTrue(u.cls.call_args.kwargs["skip_non_matching_threads"]) + + def test_dump_stack_blocking_pauses_and_resumes_threads(self): + stack_frames = [mock.sentinel.stack_frames] + with self._patched_unwinder() as u: + u.instance.get_stack_trace.return_value = stack_frames + result = dump_stack(12345, blocking=True) + + self.assertIs(result, stack_frames) + u.instance.pause_threads.assert_called_once_with() + u.instance.resume_threads.assert_called_once_with() + + def test_dump_stack_blocking_resumes_threads_after_failure(self): + with self._patched_unwinder() as u: + u.instance.get_stack_trace.side_effect = RuntimeError("boom") + with self.assertRaises(RuntimeError): + dump_stack(12345, blocking=True) + + u.instance.pause_threads.assert_called_once_with() + u.instance.resume_threads.assert_called_once_with() + + def test_sample_profiler_sample_method_timing(self): + """Test that the sample method respects duration and handles timing correctly.""" + + # Mock the unwinder to avoid needing a real process + mock_unwinder = mock.MagicMock() + mock_unwinder.get_stack_trace.return_value = [ + ( + 1, + [ + mock.MagicMock( + filename="test.py", lineno=10, funcname="test_func" + ) + ], + ) + ] + + with mock.patch( + "_remote_debugging.RemoteUnwinder" + ) as mock_unwinder_class: + mock_unwinder_class.return_value = mock_unwinder + + profiler = SampleProfiler( + pid=12345, sample_interval_usec=100000, all_threads=False + ) # 100ms interval + + # Mock collector + mock_collector = mock.MagicMock() + + # Mock time to control the sampling loop + start_time = 1000.0 + times = [ + start_time + i * 0.1 for i in range(12) + ] # 0, 0.1, 0.2, ..., 1.1 seconds + + with mock.patch("time.perf_counter", side_effect=times): + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + profiler.sample(mock_collector, duration_sec=1) + + result = output.getvalue() + + # Should have captured approximately 10 samples (1 second / 0.1 second interval) + self.assertIn("Captured", result) + self.assertIn("samples", result) + + # Verify collector was called multiple times + total_weight = sum( + len(c.kwargs.get("timestamps_us") or [None]) + for c in mock_collector.collect.call_args_list + ) + self.assertGreaterEqual(total_weight, 5) + self.assertLessEqual(total_weight, 11) + + def test_sample_profiler_does_not_buffer_non_aggregating_collectors(self): + """Test that non-aggregating collectors get each sample immediately.""" + + stack_frames = [mock.sentinel.stack_frames] + mock_collector = mock.MagicMock() + mock_collector.aggregating = False + + with self._patched_unwinder() as u: + u.instance.get_stack_trace.return_value = stack_frames + + manager = mock.Mock() + manager.attach_mock(u.instance.get_stack_trace, "unwind") + manager.attach_mock(mock_collector.collect, "collect") + + profiler = SampleProfiler( + pid=12345, sample_interval_usec=10000, all_threads=False + ) + + times = [0.0, 0.01, 0.011, 0.02, 0.03] + with mock.patch("time.perf_counter", side_effect=times): + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + profiler.sample(mock_collector, duration_sec=0.025) + + self.assertEqual( + manager.mock_calls, + [ + mock.call.unwind(), + mock.call.collect(stack_frames), + mock.call.unwind(), + mock.call.collect(stack_frames), + ], + ) + + def test_sample_profiler_flushes_aggregated_batches_at_limit(self): + """Test that aggregating collectors flush after MAX_PENDING_SAMPLES samples.""" + + stack_frames = [mock.sentinel.stack_frames] + mock_collector = mock.MagicMock() + mock_collector.aggregating = True + + with self._patched_unwinder() as u: + u.instance.get_stack_trace.return_value = stack_frames + + profiler = SampleProfiler( + pid=12345, sample_interval_usec=10000, all_threads=False + ) + + times = [ + 0.0, + 0.01, 0.011, + 0.02, 0.021, + 0.03, 0.031, + 0.04, 0.041, + 0.05, 0.051, + ] + with mock.patch("profiling.sampling.sample.MAX_PENDING_SAMPLES", 2): + with mock.patch("time.perf_counter", side_effect=times): + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + profiler.sample(mock_collector, duration_sec=0.045) + + batches = [ + (c.args[0], len(c.kwargs["timestamps_us"])) + for c in mock_collector.collect.call_args_list + ] + self.assertEqual( + batches, + [(stack_frames, 2), (stack_frames, 2), (stack_frames, 1)], + ) + + def test_sample_profiler_error_handling(self): + """Test that the sample method handles errors gracefully.""" + + # Mock unwinder that raises errors + mock_unwinder = mock.MagicMock() + error_sequence = [ + RuntimeError("Process died"), + [ + ( + 1, + [ + mock.MagicMock( + filename="test.py", lineno=10, funcname="test_func" + ) + ], + ) + ], + UnicodeDecodeError("utf-8", b"", 0, 1, "invalid"), + [ + ( + 1, + [ + mock.MagicMock( + filename="test.py", + lineno=20, + funcname="test_func2", + ) + ], + ) + ], + OSError("Permission denied"), + ] + mock_unwinder.get_stack_trace.side_effect = error_sequence + + with mock.patch( + "_remote_debugging.RemoteUnwinder" + ) as mock_unwinder_class: + mock_unwinder_class.return_value = mock_unwinder + + profiler = SampleProfiler( + pid=12345, sample_interval_usec=10000, all_threads=False + ) + + mock_collector = mock.MagicMock() + + # Control timing to run exactly 5 samples + times = [0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06] + + with mock.patch("time.perf_counter", side_effect=times): + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + profiler.sample(mock_collector, duration_sec=0.05) + + result = output.getvalue() + + # Should report error rate + self.assertIn("Error rate:", result) + self.assertIn("%", result) + + # Collector should have been called only for successful samples (should be > 0) + self.assertGreater(mock_collector.collect.call_count, 0) + self.assertLessEqual(mock_collector.collect.call_count, 3) + + def test_sample_profiler_missed_samples_warning(self): + """Test that the profiler warns about missed samples when sampling is too slow.""" + + mock_unwinder = mock.MagicMock() + mock_unwinder.get_stack_trace.return_value = [ + ( + 1, + [ + mock.MagicMock( + filename="test.py", lineno=10, funcname="test_func" + ) + ], + ) + ] + + with mock.patch( + "_remote_debugging.RemoteUnwinder" + ) as mock_unwinder_class: + mock_unwinder_class.return_value = mock_unwinder + + # Use very short interval that we'll miss + profiler = SampleProfiler( + pid=12345, sample_interval_usec=1000, all_threads=False + ) # 1ms interval + + mock_collector = mock.MagicMock() + + # Simulate slow sampling where we miss many samples + times = [ + 0.0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + ] # Extra time points to avoid StopIteration + + with mock.patch("time.perf_counter", side_effect=times): + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + profiler.sample(mock_collector, duration_sec=0.5) + + result = output.getvalue() + + # Should warn about missed samples + self.assertIn("Warning: missed", result) + self.assertIn("samples from the expected total", result) + + def test_sample_profiler_keyboard_interrupt(self): + mock_unwinder = mock.MagicMock() + mock_unwinder.get_stack_trace.side_effect = [ + [ + ( + 1, + [ + mock.MagicMock( + filename="test.py", lineno=10, funcname="test_func" + ) + ], + ) + ], + KeyboardInterrupt(), + ] + + with mock.patch( + "_remote_debugging.RemoteUnwinder" + ) as mock_unwinder_class: + mock_unwinder_class.return_value = mock_unwinder + profiler = SampleProfiler( + pid=12345, sample_interval_usec=10000, all_threads=False + ) + mock_collector = mock.MagicMock() + times = [0.0, 0.01, 0.02, 0.03, 0.04] + with mock.patch("time.perf_counter", side_effect=times): + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + try: + profiler.sample(mock_collector, duration_sec=1.0) + except KeyboardInterrupt: + self.fail( + "KeyboardInterrupt was not handled by the profiler" + ) + result = output.getvalue() + self.assertIn("Interrupted by user.", result) + self.assertIn("Captured", result) + self.assertIn("samples", result) + self.assertNotIn("Warning: missed", result) + + +@force_not_colorized_test_class +class TestPrintSampledStats(unittest.TestCase): + """Test the print_sampled_stats function.""" + + def setUp(self): + """Set up test data.""" + # Mock stats data + self.mock_stats = mock.MagicMock() + self.mock_stats.stats = { + ("file1.py", 10, "func1"): ( + 100, + 100, + 0.5, + 0.5, + {}, + ), # cc, nc, tt, ct, callers + ("file2.py", 20, "func2"): (50, 50, 0.25, 0.3, {}), + ("file3.py", 30, "func3"): (200, 200, 1.5, 2.0, {}), + ("file4.py", 40, "func4"): ( + 10, + 10, + 0.001, + 0.001, + {}, + ), # millisecond range + ("file5.py", 50, "func5"): ( + 5, + 5, + 0.000001, + 0.000002, + {}, + ), # microsecond range + } + + def test_print_sampled_stats_basic(self): + """Test basic print_sampled_stats functionality.""" + + # Capture output + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats(self.mock_stats, sample_interval_usec=100) + + result = output.getvalue() + + # Check header is present + self.assertIn("Profile Stats:", result) + self.assertIn("nsamples", result) + self.assertIn("tottime", result) + self.assertIn("cumtime", result) + + # Check functions are present + self.assertIn("func1", result) + self.assertIn("func2", result) + self.assertIn("func3", result) + + def test_print_sampled_stats_sorting(self): + """Test different sorting options.""" + + # Test sort by calls + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats( + self.mock_stats, sort=0, sample_interval_usec=100 + ) + + result = output.getvalue() + lines = result.strip().split("\n") + + # Find the data lines (skip header) + data_lines = [l for l in lines if "file" in l and ".py" in l] + # func3 should be first (200 calls) + self.assertIn("func3", data_lines[0]) + + # Test sort by time + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats( + self.mock_stats, sort=1, sample_interval_usec=100 + ) + + result = output.getvalue() + lines = result.strip().split("\n") + + data_lines = [l for l in lines if "file" in l and ".py" in l] + # func3 should be first (1.5s time) + self.assertIn("func3", data_lines[0]) + + def test_print_sampled_stats_limit(self): + """Test limiting output rows.""" + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats( + self.mock_stats, limit=2, sample_interval_usec=100 + ) + + result = output.getvalue() + + # Count function entries in the main stats section (not in summary) + lines = result.split("\n") + # Find where the main stats section ends (before summary) + main_section_lines = [] + for line in lines: + if "Summary of Interesting Functions:" in line: + break + main_section_lines.append(line) + + # Count function entries only in main section + func_count = sum( + 1 + for line in main_section_lines + if "func" in line and ".py" in line + ) + self.assertEqual(func_count, 2) + + def test_print_sampled_stats_time_units(self): + """Test proper time unit selection.""" + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats(self.mock_stats, sample_interval_usec=100) + + result = output.getvalue() + + # Should use seconds for the header since max time is > 1s + self.assertIn("tottime (s)", result) + self.assertIn("cumtime (s)", result) + + # Test with only microsecond-range times + micro_stats = mock.MagicMock() + micro_stats.stats = { + ("file1.py", 10, "func1"): (100, 100, 0.000005, 0.000010, {}), + } + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats(micro_stats, sample_interval_usec=100) + + result = output.getvalue() + + # Should use microseconds + self.assertIn("tottime (μs)", result) + self.assertIn("cumtime (μs)", result) + + def test_print_sampled_stats_summary(self): + """Test summary section generation.""" + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats( + self.mock_stats, + show_summary=True, + sample_interval_usec=100, + ) + + result = output.getvalue() + + # Check summary sections are present + self.assertIn("Summary of Interesting Functions:", result) + self.assertIn( + "Functions with Highest Direct/Cumulative Ratio (Hot Spots):", + result, + ) + self.assertIn( + "Functions with Highest Call Frequency (Indirect Calls):", result + ) + self.assertIn( + "Functions with Highest Call Magnification (Cumulative/Direct):", + result, + ) + + def test_print_sampled_stats_no_summary(self): + """Test disabling summary output.""" + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats( + self.mock_stats, + show_summary=False, + sample_interval_usec=100, + ) + + result = output.getvalue() + + # Summary should not be present + self.assertNotIn("Summary of Interesting Functions:", result) + + def test_print_sampled_stats_empty_stats(self): + """Test with empty stats.""" + + empty_stats = mock.MagicMock() + empty_stats.stats = {} + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats(empty_stats, sample_interval_usec=100) + + result = output.getvalue() + + # Should print message about no samples + self.assertIn("No samples were collected.", result) + + def test_print_sampled_stats_sample_percentage_sorting(self): + """Test sample percentage sorting options.""" + + # Add a function with high sample percentage (more direct calls than func3's 200) + self.mock_stats.stats[("expensive.py", 60, "expensive_func")] = ( + 300, # direct calls (higher than func3's 200) + 300, # cumulative calls + 1.0, # total time + 1.0, # cumulative time + {}, + ) + + # Test sort by sample percentage + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats( + self.mock_stats, sort=3, sample_interval_usec=100 + ) # sample percentage + + result = output.getvalue() + lines = result.strip().split("\n") + + data_lines = [l for l in lines if ".py" in l and "func" in l] + # expensive_func should be first (highest sample percentage) + self.assertIn("expensive_func", data_lines[0]) + + def test_print_sampled_stats_with_recursive_calls(self): + """Test print_sampled_stats with recursive calls where nc != cc.""" + + # Create stats with recursive calls (nc != cc) + recursive_stats = mock.MagicMock() + recursive_stats.stats = { + # (direct_calls, cumulative_calls, tt, ct, callers) - recursive function + ("recursive.py", 10, "factorial"): ( + 5, # direct_calls + 10, # cumulative_calls (appears more times in stack due to recursion) + 0.5, + 0.6, + {}, + ), + ("normal.py", 20, "normal_func"): ( + 3, # direct_calls + 3, # cumulative_calls (same as direct for non-recursive) + 0.2, + 0.2, + {}, + ), + } + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats(recursive_stats, sample_interval_usec=100) + + result = output.getvalue() + + # Should display recursive calls as "5/10" format + self.assertIn("5/10", result) # nc/cc format for recursive calls + self.assertIn("3", result) # just nc for non-recursive calls + self.assertIn("factorial", result) + self.assertIn("normal_func", result) + + def test_print_sampled_stats_with_zero_call_counts(self): + """Test print_sampled_stats with zero call counts to trigger division protection.""" + + # Create stats with zero call counts + zero_stats = mock.MagicMock() + zero_stats.stats = { + ("file.py", 10, "zero_calls"): (0, 0, 0.0, 0.0, {}), # Zero calls + ("file.py", 20, "normal_func"): ( + 5, + 5, + 0.1, + 0.1, + {}, + ), # Normal function + } + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats(zero_stats, sample_interval_usec=100) + + result = output.getvalue() + + # Should handle zero call counts gracefully + self.assertIn("zero_calls", result) + self.assertIn("zero_calls", result) + self.assertIn("normal_func", result) + + def test_print_sampled_stats_sort_by_name(self): + """Test sort by function name option.""" + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats( + self.mock_stats, sort=-1, sample_interval_usec=100 + ) # sort by name + + result = output.getvalue() + lines = result.strip().split("\n") + + # Find the data lines (skip header and summary) + # Data lines start with whitespace and numbers, and contain filename:lineno(function) + data_lines = [] + for line in lines: + # Skip header lines and summary sections + if ( + line.startswith(" ") + and "(" in line + and ")" in line + and not line.startswith( + " 1." + ) # Skip summary lines that start with times + and not line.startswith( + " 0." + ) # Skip summary lines that start with times + and not "per call" in line # Skip summary lines + and not "calls" in line # Skip summary lines + and not "total time" in line # Skip summary lines + and not "cumulative time" in line + ): # Skip summary lines + data_lines.append(line) + + # Extract just the function names for comparison + func_names = [] + + for line in data_lines: + # Function name is between the last ( and ), accounting for ANSI color codes + match = re.search(r"\(([^)]+)\)$", line) + if match: + func_name = match.group(1) + # Remove ANSI color codes + func_name = re.sub(r"\x1b\[[0-9;]*m", "", func_name) + func_names.append(func_name) + + # Verify we extracted function names and they are sorted + self.assertGreater( + len(func_names), 0, "Should have extracted some function names" + ) + self.assertEqual( + func_names, + sorted(func_names), + f"Function names {func_names} should be sorted alphabetically", + ) + + def test_print_sampled_stats_with_zero_time_functions(self): + """Test summary sections with functions that have zero time.""" + + # Create stats with zero-time functions + zero_time_stats = mock.MagicMock() + zero_time_stats.stats = { + ("file1.py", 10, "zero_time_func"): ( + 5, + 5, + 0.0, + 0.0, + {}, + ), # Zero time + ("file2.py", 20, "normal_func"): ( + 3, + 3, + 0.1, + 0.1, + {}, + ), # Normal time + } + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats( + zero_time_stats, + show_summary=True, + sample_interval_usec=100, + ) + + result = output.getvalue() + + # Should handle zero-time functions gracefully in summary + self.assertIn("Summary of Interesting Functions:", result) + self.assertIn("zero_time_func", result) + self.assertIn("normal_func", result) + + def test_print_sampled_stats_with_malformed_qualified_names(self): + """Test summary generation with function names that don't contain colons.""" + + # Create stats with function names that would create malformed qualified names + malformed_stats = mock.MagicMock() + malformed_stats.stats = { + # Function name without clear module separation + ("no_colon_func", 10, "func"): (3, 3, 0.1, 0.1, {}), + ("", 20, "empty_filename_func"): (2, 2, 0.05, 0.05, {}), + ("normal.py", 30, "normal_func"): (5, 5, 0.2, 0.2, {}), + } + + with io.StringIO() as output: + with mock.patch("sys.stdout", output): + print_sampled_stats( + malformed_stats, + show_summary=True, + sample_interval_usec=100, + ) + + result = output.getvalue() + + # Should handle malformed names gracefully in summary aggregation + self.assertIn("Summary of Interesting Functions:", result) + # All function names should appear somewhere in the output + self.assertIn("func", result) + self.assertIn("empty_filename_func", result) + self.assertIn("normal_func", result) + + def test_print_sampled_stats_with_recursive_call_stats_creation(self): + """Test create_stats with recursive call data to trigger total_rec_calls branch.""" + collector = PstatsCollector(sample_interval_usec=1000000) # 1 second + + # Simulate recursive function data where total_rec_calls would be set + # We need to manually manipulate the collector result to test this branch + collector.result = { + ("recursive.py", 10, "factorial"): { + "total_rec_calls": 3, # Non-zero recursive calls + "direct_calls": 5, + "cumulative_calls": 10, + }, + ("normal.py", 20, "normal_func"): { + "total_rec_calls": 0, # Zero recursive calls + "direct_calls": 2, + "cumulative_calls": 5, + }, + } + + collector.create_stats() + + # Check that recursive calls are handled differently from non-recursive + factorial_stats = collector.stats[("recursive.py", 10, "factorial")] + normal_stats = collector.stats[("normal.py", 20, "normal_func")] + + # factorial should use cumulative_calls (10) as nc + self.assertEqual( + factorial_stats[1], 10 + ) # nc should be cumulative_calls + self.assertEqual(factorial_stats[0], 5) # cc should be direct_calls + + # normal_func should use cumulative_calls as nc + self.assertEqual(normal_stats[1], 5) # nc should be cumulative_calls + self.assertEqual(normal_stats[0], 2) # cc should be direct_calls diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_trend_tracker.py b/Lib/test/test_profiling/test_sampling_profiler/test_trend_tracker.py new file mode 100644 index 00000000000000..43b23be0fe3da2 --- /dev/null +++ b/Lib/test/test_profiling/test_sampling_profiler/test_trend_tracker.py @@ -0,0 +1,97 @@ +"""Simple unit tests for TrendTracker.""" + +import unittest +from test.support import requires +from test.support.import_helper import import_module + +# Only run these tests if curses is available +requires("curses") +curses = import_module("curses") + +from profiling.sampling.live_collector.trend_tracker import TrendTracker + + +class TestTrendTracker(unittest.TestCase): + """Tests for TrendTracker class.""" + + def setUp(self): + """Set up test fixtures.""" + self.colors = { + "trend_up": curses.A_BOLD, + "trend_down": curses.A_REVERSE, + "trend_stable": curses.A_NORMAL, + } + + def test_basic_trend_detection(self): + """Test basic up/down/stable trend detection.""" + tracker = TrendTracker(self.colors, enabled=True) + + # First value is always stable + self.assertEqual(tracker.update("func1", "nsamples", 10), "stable") + + # Increasing value + self.assertEqual(tracker.update("func1", "nsamples", 20), "up") + + # Decreasing value + self.assertEqual(tracker.update("func1", "nsamples", 15), "down") + + # Small change (within threshold) is stable + self.assertEqual(tracker.update("func1", "nsamples", 15.0001), "stable") + + def test_multiple_metrics(self): + """Test tracking multiple metrics simultaneously.""" + tracker = TrendTracker(self.colors, enabled=True) + + trends = tracker.update_metrics("func1", { + "nsamples": 10, + "tottime": 5.0, + }) + + self.assertEqual(trends["nsamples"], "stable") + self.assertEqual(trends["tottime"], "stable") + + # Update with changes + trends = tracker.update_metrics("func1", { + "nsamples": 15, + "tottime": 3.0, + }) + + self.assertEqual(trends["nsamples"], "up") + self.assertEqual(trends["tottime"], "down") + + def test_toggle_enabled(self): + """Test enable/disable toggle.""" + tracker = TrendTracker(self.colors, enabled=True) + self.assertTrue(tracker.enabled) + + tracker.toggle() + self.assertFalse(tracker.enabled) + + # When disabled, should return A_NORMAL + self.assertEqual(tracker.get_color("up"), curses.A_NORMAL) + + def test_get_color(self): + """Test color selection for trends.""" + tracker = TrendTracker(self.colors, enabled=True) + + self.assertEqual(tracker.get_color("up"), curses.A_BOLD) + self.assertEqual(tracker.get_color("down"), curses.A_REVERSE) + self.assertEqual(tracker.get_color("stable"), curses.A_NORMAL) + + def test_clear(self): + """Test clearing tracked values.""" + tracker = TrendTracker(self.colors, enabled=True) + + # Add some data + tracker.update("func1", "nsamples", 10) + tracker.update("func1", "nsamples", 20) + + # Clear + tracker.clear() + + # After clear, first update should be stable + self.assertEqual(tracker.update("func1", "nsamples", 30), "stable") + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_profiling/test_tracing_profiler.py b/Lib/test/test_profiling/test_tracing_profiler.py new file mode 100644 index 00000000000000..3668e24e073ce4 --- /dev/null +++ b/Lib/test/test_profiling/test_tracing_profiler.py @@ -0,0 +1,252 @@ +"""Test suite for the cProfile module.""" + +import sys +import unittest + +# rip off all interesting stuff from test_profile +import profiling.tracing as cProfile +import tempfile +import textwrap +from test.test_profile import ProfileTest, regenerate_expected_output +from test.support.script_helper import assert_python_failure, assert_python_ok +from test import support + + +class CProfileTest(ProfileTest): + profilerclass = cProfile.Profile + profilermodule = cProfile + expected_max_output = "{built-in method builtins.max}" + + def get_expected_output(self): + return _ProfileOutput + + def test_bad_counter_during_dealloc(self): + # bpo-3895 + import _lsprof + + with support.catch_unraisable_exception() as cm: + obj = _lsprof.Profiler(lambda: int) + obj.enable() + obj.disable() + obj.clear() + + self.assertEqual(cm.unraisable.exc_type, TypeError) + + def test_crash_with_not_enough_args(self): + # gh-126220 + import _lsprof + + for profile in [_lsprof.Profiler(), cProfile.Profile()]: + for method in [ + "_pystart_callback", + "_pyreturn_callback", + "_ccall_callback", + "_creturn_callback", + ]: + with self.subTest(profile=profile, method=method): + method_obj = getattr(profile, method) + with self.assertRaises(TypeError): + method_obj() # should not crash + + def test_evil_external_timer(self): + # gh-120289 + # Disabling profiler in external timer should not crash + import _lsprof + class EvilTimer(): + def __init__(self, disable_count): + self.count = 0 + self.disable_count = disable_count + + def __call__(self): + self.count += 1 + if self.count == self.disable_count: + profiler_with_evil_timer.disable() + return self.count + + # this will trigger external timer to disable profiler at + # call event - in initContext in _lsprof.c + with support.catch_unraisable_exception() as cm: + profiler_with_evil_timer = _lsprof.Profiler(EvilTimer(1)) + profiler_with_evil_timer.enable() + # Make a call to trigger timer + (lambda: None)() + profiler_with_evil_timer.disable() + profiler_with_evil_timer.clear() + self.assertEqual(cm.unraisable.exc_type, RuntimeError) + + # this will trigger external timer to disable profiler at + # return event - in Stop in _lsprof.c + with support.catch_unraisable_exception() as cm: + profiler_with_evil_timer = _lsprof.Profiler(EvilTimer(2)) + profiler_with_evil_timer.enable() + # Make a call to trigger timer + (lambda: None)() + profiler_with_evil_timer.disable() + profiler_with_evil_timer.clear() + self.assertEqual(cm.unraisable.exc_type, RuntimeError) + + def test_profile_enable_disable(self): + prof = self.profilerclass() + # Make sure we clean ourselves up if the test fails for some reason. + self.addCleanup(prof.disable) + + prof.enable() + self.assertEqual( + sys.monitoring.get_tool(sys.monitoring.PROFILER_ID), "cProfile") + + prof.disable() + self.assertIs(sys.monitoring.get_tool(sys.monitoring.PROFILER_ID), None) + + def test_profile_as_context_manager(self): + prof = self.profilerclass() + # Make sure we clean ourselves up if the test fails for some reason. + self.addCleanup(prof.disable) + + with prof as __enter__return_value: + # profile.__enter__ should return itself. + self.assertIs(prof, __enter__return_value) + + # profile should be set as the global profiler inside the + # with-block + self.assertEqual( + sys.monitoring.get_tool(sys.monitoring.PROFILER_ID), "cProfile") + + # profile shouldn't be set once we leave the with-block. + self.assertIs(sys.monitoring.get_tool(sys.monitoring.PROFILER_ID), None) + + def test_second_profiler(self): + pr = self.profilerclass() + pr2 = self.profilerclass() + pr.enable() + self.assertRaises(ValueError, pr2.enable) + pr.disable() + + def test_throw(self): + """ + gh-106152 + generator.throw() should trigger a call in cProfile + """ + + def gen(): + yield + + pr = self.profilerclass() + pr.enable() + g = gen() + try: + g.throw(SyntaxError) + except SyntaxError: + pass + pr.disable() + pr.create_stats() + + self.assertTrue(any("throw" in func[2] for func in pr.stats.keys())), + + def test_clear_with_nested_calls(self): + # Calling clear() during nested profiled calls should not leak + # ProfilerContexts. clearEntries() must walk the entire linked list, + # not just free the top context. + import _lsprof + + def level3(profiler): + profiler.clear() + + def level2(profiler): + level3(profiler) + + def level1(profiler): + level2(profiler) + + profiler = _lsprof.Profiler() + profiler.enable() + for _ in range(100): + level1(profiler) + profiler.disable() + profiler.clear() + + def test_bad_descriptor(self): + # gh-132250 + # cProfile should not crash when the profiler callback fails to locate + # the actual function of a method. + with self.profilerclass() as prof: + with self.assertRaises(TypeError): + bytes.find(str()) + + +class TestCommandLine(unittest.TestCase): + def test_sort(self): + rc, out, err = assert_python_failure('-m', 'cProfile', '-s', 'demo') + self.assertGreater(rc, 0) + self.assertIn(b"option -s: invalid choice: 'demo'", err) + + def test_profile_script_importing_main(self): + """Check that scripts that reference __main__ see their own namespace + when being profiled.""" + with tempfile.NamedTemporaryFile("w+", delete_on_close=False) as f: + f.write(textwrap.dedent("""\ + class Foo: + pass + import __main__ + assert Foo == __main__.Foo + """)) + f.close() + assert_python_ok('-m', "cProfile", f.name) + + +def main(): + if '-r' not in sys.argv: + unittest.main() + else: + regenerate_expected_output(__file__, CProfileTest) + + +# Don't remove this comment. Everything below it is auto-generated. +#--cut-------------------------------------------------------------------------- +_ProfileOutput = {} +_ProfileOutput['print_stats'] = """\ + 28 0.028 0.001 0.028 0.001 profilee.py:110(__getattr__) + 1 0.270 0.270 1.000 1.000 profilee.py:25(testfunc) + 23/3 0.150 0.007 0.170 0.057 profilee.py:35(factorial) + 20 0.020 0.001 0.020 0.001 profilee.py:48(mul) + 2 0.040 0.020 0.600 0.300 profilee.py:55(helper) + 4 0.116 0.029 0.120 0.030 profilee.py:73(helper1) + 2 0.000 0.000 0.140 0.070 profilee.py:84(helper2_indirect) + 8 0.312 0.039 0.400 0.050 profilee.py:88(helper2) + 8 0.064 0.008 0.080 0.010 profilee.py:98(subhelper)""" +_ProfileOutput['print_callers'] = """\ +profilee.py:110(__getattr__) <- 16 0.016 0.016 profilee.py:98(subhelper) +profilee.py:25(testfunc) <- 1 0.270 1.000 <string>:1(<module>) +profilee.py:35(factorial) <- 1 0.014 0.130 profilee.py:25(testfunc) + 20/3 0.130 0.147 profilee.py:35(factorial) + 2 0.006 0.040 profilee.py:84(helper2_indirect) +profilee.py:48(mul) <- 20 0.020 0.020 profilee.py:35(factorial) +profilee.py:55(helper) <- 2 0.040 0.600 profilee.py:25(testfunc) +profilee.py:73(helper1) <- 4 0.116 0.120 profilee.py:55(helper) +profilee.py:84(helper2_indirect) <- 2 0.000 0.140 profilee.py:55(helper) +profilee.py:88(helper2) <- 6 0.234 0.300 profilee.py:55(helper) + 2 0.078 0.100 profilee.py:84(helper2_indirect) +profilee.py:98(subhelper) <- 8 0.064 0.080 profilee.py:88(helper2) +{built-in method builtins.hasattr} <- 4 0.000 0.004 profilee.py:73(helper1) + 8 0.000 0.008 profilee.py:88(helper2) +{built-in method sys.exception} <- 4 0.000 0.000 profilee.py:73(helper1) +{method 'append' of 'list' objects} <- 4 0.000 0.000 profilee.py:73(helper1)""" +_ProfileOutput['print_callees'] = """\ +<string>:1(<module>) -> 1 0.270 1.000 profilee.py:25(testfunc) +profilee.py:110(__getattr__) -> +profilee.py:25(testfunc) -> 1 0.014 0.130 profilee.py:35(factorial) + 2 0.040 0.600 profilee.py:55(helper) +profilee.py:35(factorial) -> 20/3 0.130 0.147 profilee.py:35(factorial) + 20 0.020 0.020 profilee.py:48(mul) +profilee.py:48(mul) -> +profilee.py:55(helper) -> 4 0.116 0.120 profilee.py:73(helper1) + 2 0.000 0.140 profilee.py:84(helper2_indirect) + 6 0.234 0.300 profilee.py:88(helper2) +profilee.py:73(helper1) -> 4 0.000 0.004 {built-in method builtins.hasattr} +profilee.py:84(helper2_indirect) -> 2 0.006 0.040 profilee.py:35(factorial) + 2 0.078 0.100 profilee.py:88(helper2) +profilee.py:88(helper2) -> 8 0.064 0.080 profilee.py:98(subhelper) +profilee.py:98(subhelper) -> 16 0.016 0.016 profilee.py:110(__getattr__) +{built-in method builtins.hasattr} -> 12 0.012 0.012 profilee.py:110(__getattr__)""" + +if __name__ == "__main__": + main() diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index ed4fe8a140879d..7e4f4828ce0f8d 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -1,9 +1,8 @@ import unittest from test.support import ( - is_android, is_apple_mobile, is_wasm32, reap_children, verbose + is_android, is_apple_mobile, is_wasm32, reap_children, verbose, warnings_helper ) from test.support.import_helper import import_module -from test.support.os_helper import TESTFN, unlink # Skip these tests if termios is not available import_module('termios') @@ -194,6 +193,7 @@ def test_openpty(self): s2 = _readline(master_fd) self.assertEqual(b'For my pet fish, Eric.\n', normalize_output(s2)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_fork(self): debug("calling pty.fork()") pid, master_fd = pty.fork() @@ -229,6 +229,7 @@ def test_fork(self): os._exit(2) os._exit(4) else: + self.assertFalse(os.get_inheritable(master_fd)) debug("Waiting for child (%d) to finish." % pid) # In verbose mode, we have to consume the debug output from the # child or the child will block, causing this test to hang in the @@ -295,27 +296,29 @@ def test_master_read(self): self.assertEqual(data, b"") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_spawn_doesnt_hang(self): - self.addCleanup(unlink, TESTFN) - with open(TESTFN, 'wb') as f: - STDOUT_FILENO = 1 - dup_stdout = os.dup(STDOUT_FILENO) - os.dup2(f.fileno(), STDOUT_FILENO) - buf = b'' - def master_read(fd): - nonlocal buf - data = os.read(fd, 1024) - buf += data - return data + # gh-140482: Do the test in a pty.fork() child to avoid messing + # with the interactive test runner's terminal settings. + pid, fd = pty.fork() + if pid == pty.CHILD: + pty.spawn([sys.executable, '-c', 'print("hi there")']) + os._exit(0) + + try: + buf = bytearray() try: - pty.spawn([sys.executable, '-c', 'print("hi there")'], - master_read) - finally: - os.dup2(dup_stdout, STDOUT_FILENO) - os.close(dup_stdout) - self.assertEqual(buf, b'hi there\r\n') - with open(TESTFN, 'rb') as f: - self.assertEqual(f.read(), b'hi there\r\n') + while (data := os.read(fd, 1024)) != b'': + buf.extend(data) + except OSError as e: + if e.errno != errno.EIO: + raise + + (pid, status) = os.waitpid(pid, 0) + self.assertEqual(status, 0) + self.assertEqual(buf.take_bytes(), b"hi there\r\n") + finally: + os.close(fd) class SmallPtyTests(unittest.TestCase): """These tests don't spawn children or hang.""" diff --git a/Lib/test/test_pwd.py b/Lib/test/test_pwd.py index aa090b464a7222..bdf57776c82be1 100644 --- a/Lib/test/test_pwd.py +++ b/Lib/test/test_pwd.py @@ -1,3 +1,5 @@ +import random +import string import sys import unittest from test.support import import_helper @@ -56,59 +58,57 @@ def test_values_extended(self): def test_errors(self): self.assertRaises(TypeError, pwd.getpwuid) self.assertRaises(TypeError, pwd.getpwuid, 3.14) + self.assertRaises(TypeError, pwd.getpwuid, 0.0) + self.assertRaises(TypeError, pwd.getpwuid, 0, 0) + # should be out of uid_t range + self.assertRaises(KeyError, pwd.getpwuid, 2**128) + self.assertRaises(KeyError, pwd.getpwuid, -2**128) self.assertRaises(TypeError, pwd.getpwnam) self.assertRaises(TypeError, pwd.getpwnam, 42) - self.assertRaises(TypeError, pwd.getpwall, 42) + self.assertRaises(TypeError, pwd.getpwnam, b'root') + self.assertRaises(TypeError, pwd.getpwnam, 'root', 0) # embedded null character self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'a\x00b') + self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'root\x00') + self.assertRaises(UnicodeEncodeError, pwd.getpwnam, 'roo\udc74') + self.assertRaises(KeyError, pwd.getpwnam, '') + self.assertRaises(TypeError, pwd.getpwall, 42) - # try to get some errors - bynames = {} - byuids = {} - for (n, p, u, g, gecos, d, s) in pwd.getpwall(): - bynames[n] = u - byuids[u] = n - - allnames = list(bynames.keys()) - namei = 0 - fakename = allnames[namei] if allnames else "invaliduser" - while fakename in bynames: - chars = list(fakename) - for i in range(len(chars)): - if chars[i] == 'z': - chars[i] = 'A' - break - elif chars[i] == 'Z': - continue - else: - chars[i] = chr(ord(chars[i]) + 1) - break - else: - namei = namei + 1 - try: - fakename = allnames[namei] - except IndexError: - # should never happen... if so, just forget it - break - fakename = ''.join(chars) - - self.assertRaises(KeyError, pwd.getpwnam, fakename) - - # In some cases, byuids isn't a complete list of all users in the - # system, so if we try to pick a value not in byuids (via a perturbing - # loop, say), pwd.getpwuid() might still be able to find data for that - # uid. Using sys.maxint may provoke the same problems, but hopefully - # it will be a more repeatable failure. - fakeuid = sys.maxsize - self.assertNotIn(fakeuid, byuids) - self.assertRaises(KeyError, pwd.getpwuid, fakeuid) + # Find a non-existent user name. + # getpwall() will not necessarily report all existing users + # (typical for LDAP based directories in big organizations). + for _ in range(30): + fakename = ''.join(random.choices(string.ascii_lowercase, k=6)) + try: + pwd.getpwnam(fakename) + except KeyError: + break + else: + self.fail('Cannot find non-existent user name') + + # Find a non-existent uid. + maxuid = max(e.pw_uid for e in pwd.getpwall()) + if maxuid < 2**15: + maxuid = 2**15 + elif maxuid < 2**16: + maxuid = 2**16-1 + else: + maxuid = 2**31 + for _ in range(30): + fakeuid = random.randrange(maxuid) + try: + pwd.getpwuid(fakeuid) + except KeyError: + break + else: + self.fail('Cannot find non-existent uid') + + # On Cygwin, getpwuid(-1) returns 'Unknown+User' user + if sys.platform != 'cygwin': + # -1 shouldn't be a valid uid because it has a special meaning in many + # uid-related functions + self.assertRaises(KeyError, pwd.getpwuid, -1) - # -1 shouldn't be a valid uid because it has a special meaning in many - # uid-related functions - self.assertRaises(KeyError, pwd.getpwuid, -1) - # should be out of uid_t range - self.assertRaises(KeyError, pwd.getpwuid, 2**128) - self.assertRaises(KeyError, pwd.getpwuid, -2**128) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py index 64387296e84621..da2d630d7ace7b 100644 --- a/Lib/test/test_py_compile.py +++ b/Lib/test/test_py_compile.py @@ -56,7 +56,10 @@ def setUp(self): self.directory = tempfile.mkdtemp(dir=os.getcwd()) self.source_path = os.path.join(self.directory, '_test.py') self.pyc_path = self.source_path + 'c' - self.cache_path = importlib.util.cache_from_source(self.source_path) + try: + self.cache_path = importlib.util.cache_from_source(self.source_path) + except NotImplementedError: + self.cache_path = None self.cwd_drive = os.path.splitdrive(os.getcwd())[0] # In these tests we compute relative paths. When using Windows, the # current working directory path and the 'self.source_path' might be @@ -73,10 +76,31 @@ def tearDown(self): if self.cwd_drive: os.chdir(self.cwd_drive) + def assert_cache_path_exists(self, should_exist=True): + if self.cache_path: + if should_exist: + self.assertTrue(os.path.exists(self.cache_path)) + else: + self.assertFalse(os.path.exists(self.cache_path)) + return + cache_dir = os.path.join(self.directory, '__pycache__') + if not os.path.isdir(cache_dir): + if should_exist: + self.fail('no __pycache__ directory exists') + return + for f in os.listdir(cache_dir): + if f.startswith('_test.') and f.endswith('.pyc'): + if should_exist: + return + self.fail(f'__pycache__/{f} was created') + else: + if should_exist: + self.fail('no __pycache__/_test.*.pyc file exists') + def test_absolute_path(self): py_compile.compile(self.source_path, self.pyc_path) self.assertTrue(os.path.exists(self.pyc_path)) - self.assertFalse(os.path.exists(self.cache_path)) + self.assert_cache_path_exists(False) def test_do_not_overwrite_symlinks(self): # In the face of a cfile argument being a symlink, bail out. @@ -98,22 +122,24 @@ def test_do_not_overwrite_nonregular_files(self): with self.assertRaises(FileExistsError): py_compile.compile(self.source_path, os.devnull) + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag is not None') def test_cache_path(self): py_compile.compile(self.source_path) - self.assertTrue(os.path.exists(self.cache_path)) + self.assert_cache_path_exists(True) def test_cwd(self): with os_helper.change_cwd(self.directory): py_compile.compile(os.path.basename(self.source_path), os.path.basename(self.pyc_path)) self.assertTrue(os.path.exists(self.pyc_path)) - self.assertFalse(os.path.exists(self.cache_path)) + self.assert_cache_path_exists(False) def test_relative_path(self): py_compile.compile(os.path.relpath(self.source_path), os.path.relpath(self.pyc_path)) self.assertTrue(os.path.exists(self.pyc_path)) - self.assertFalse(os.path.exists(self.cache_path)) + self.assert_cache_path_exists(False) @os_helper.skip_if_dac_override @unittest.skipIf(os.name == 'nt', @@ -136,14 +162,14 @@ def test_bad_coding(self): 'tokenizedata', 'bad_coding2.py') with support.captured_stderr(): - self.assertIsNone(py_compile.compile(bad_coding, doraise=False)) - self.assertFalse(os.path.exists( - importlib.util.cache_from_source(bad_coding))) + self.assertIsNone(py_compile.compile(bad_coding, self.pyc_path, + doraise=False)) + self.assertFalse(os.path.exists(self.pyc_path)) def test_source_date_epoch(self): py_compile.compile(self.source_path, self.pyc_path) self.assertTrue(os.path.exists(self.pyc_path)) - self.assertFalse(os.path.exists(self.cache_path)) + self.assert_cache_path_exists(False) with open(self.pyc_path, 'rb') as fp: flags = importlib._bootstrap_external._classify_pyc( fp.read(), 'test', {}) @@ -155,6 +181,8 @@ def test_source_date_epoch(self): self.assertEqual(flags, expected_flags) @unittest.skipIf(sys.flags.optimize > 0, 'test does not work with -O') + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag is not None') def test_double_dot_no_clobber(self): # http://bugs.python.org/issue22966 # py_compile foo.bar.py -> __pycache__/foo.cpython-34.pyc @@ -174,6 +202,8 @@ def test_double_dot_no_clobber(self): self.assertTrue(os.path.exists(cache_path)) self.assertFalse(os.path.exists(pyc_path)) + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag is not None') def test_optimization_path(self): # Specifying optimized bytecode should lead to a path reflecting that. self.assertIn('opt-2', py_compile.compile(self.source_path, optimize=2)) @@ -181,17 +211,19 @@ def test_optimization_path(self): def test_invalidation_mode(self): py_compile.compile( self.source_path, + self.pyc_path, invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, ) - with open(self.cache_path, 'rb') as fp: + with open(self.pyc_path, 'rb') as fp: flags = importlib._bootstrap_external._classify_pyc( fp.read(), 'test', {}) self.assertEqual(flags, 0b11) py_compile.compile( self.source_path, + self.pyc_path, invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH, ) - with open(self.cache_path, 'rb') as fp: + with open(self.pyc_path, 'rb') as fp: flags = importlib._bootstrap_external._classify_pyc( fp.read(), 'test', {}) self.assertEqual(flags, 0b1) @@ -201,11 +233,19 @@ def test_quiet(self): 'tokenizedata', 'bad_coding2.py') with support.captured_stderr() as stderr: - self.assertIsNone(py_compile.compile(bad_coding, doraise=False, quiet=2)) - self.assertIsNone(py_compile.compile(bad_coding, doraise=True, quiet=2)) + self.assertIsNone(py_compile.compile(bad_coding, self.pyc_path, doraise=False, quiet=2)) + self.assertIsNone(py_compile.compile(bad_coding, self.pyc_path, doraise=True, quiet=2)) self.assertEqual(stderr.getvalue(), '') with self.assertRaises(py_compile.PyCompileError): - py_compile.compile(bad_coding, doraise=True, quiet=1) + py_compile.compile(bad_coding, self.pyc_path, doraise=True, quiet=1) + + def test_utf7_decoded_cr_compiles(self): + with open(self.source_path, 'wb') as file: + file.write(b"#coding=U7+AA0''\n") + + pyc_path = py_compile.compile(self.source_path, self.pyc_path, doraise=True) + self.assertEqual(pyc_path, self.pyc_path) + self.assertTrue(os.path.exists(self.pyc_path)) class PyCompileTestsWithSourceEpoch(PyCompileTestsBase, @@ -227,8 +267,12 @@ class PyCompileCLITestCase(unittest.TestCase): def setUp(self): self.directory = tempfile.mkdtemp() self.source_path = os.path.join(self.directory, '_test.py') - self.cache_path = importlib.util.cache_from_source(self.source_path, - optimization='' if __debug__ else 1) + try: + self.cache_path = importlib.util.cache_from_source(self.source_path, + optimization='' if __debug__ else 1) + except NotImplementedError: + # py_compile.main() assumes legacy pyc path if there is no cache_tag + self.cache_path = self.source_path + 'c' with open(self.source_path, 'w') as file: file.write('x = 123\n') diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index bce68e6cd7a57e..b5ec41b17f793b 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -250,9 +250,10 @@ def test_others(self): 'pdb', # pyclbr does not handle elegantly `typing` or properties ignore=('Union', '_ModuleTarget', '_ScriptTarget', '_ZipTarget', 'curframe_locals', - '_InteractState'), + '_InteractState', 'rlcompleter'), ) - cm('pydoc', ignore=('input', 'output',)) # properties + cm('pydoc', ignore=('input', 'output', # properties + 'getpager', 'plainpager', )) # aliases # Tests for modules inside packages cm('email.parser') diff --git a/Lib/test/test_pydoc/longsummary.py b/Lib/test/test_pydoc/longsummary.py new file mode 100644 index 00000000000000..0e566d33454119 --- /dev/null +++ b/Lib/test/test_pydoc/longsummary.py @@ -0,0 +1,29 @@ +class C: + """This is a class summary that consists of a very long single line, exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + """ + def meth(self): + """This is a method summary that consists of a very long single line, exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + """ + + @property + def prop(self): + """This is a property summary that consists of a very long single line, exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + """ + +def func(self): + """This is a function summary that consists of a very long single line, exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + """ + +data = C() +data.__doc__ = """This is a data summary that consists of a very long single line, exceeding the recommended PEP 8 limit. + +The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. +""" diff --git a/Lib/test/test_pydoc/pydocfodder.py b/Lib/test/test_pydoc/pydocfodder.py index 3cc2d5bd57fe5b..412aa3743e430b 100644 --- a/Lib/test/test_pydoc/pydocfodder.py +++ b/Lib/test/test_pydoc/pydocfodder.py @@ -87,6 +87,8 @@ def B_classmethod(cls, x): object_repr = object.__repr__ get = {}.get # same name dict_get = {}.get + from math import sin + B.B_classmethod_ref = B.B_classmethod @@ -186,3 +188,4 @@ def __call__(self, inst): object_repr = object.__repr__ get = {}.get # same name dict_get = {}.get +from math import sin # noqa: F401 diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 3b50ead00bdd31..5543c664528e6c 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -11,7 +11,6 @@ import _pickle import pkgutil import re -import stat import tempfile import test.support import time @@ -474,6 +473,32 @@ def test_issue8225(self): result, doc_loc = get_pydoc_text(xml.etree) self.assertEqual(doc_loc, "", "MODULE DOCS incorrectly includes a link") + def test_online_docs_link(self): + import encodings.idna + import importlib._bootstrap + + module_docs = { + 'encodings': 'codecs#module-encodings', + 'encodings.idna': 'codecs#module-encodings.idna', + } + + with unittest.mock.patch('pydoc_data.module_docs.module_docs', module_docs): + doc = pydoc.TextDoc() + + basedir = os.path.dirname(encodings.__file__) + doc_link = doc.getdocloc(encodings, basedir=basedir) + self.assertIsNotNone(doc_link) + self.assertIn('codecs#module-encodings', doc_link) + self.assertNotIn('encodings.html', doc_link) + + doc_link = doc.getdocloc(encodings.idna, basedir=basedir) + self.assertIsNotNone(doc_link) + self.assertIn('codecs#module-encodings.idna', doc_link) + self.assertNotIn('encodings.idna.html', doc_link) + + doc_link = doc.getdocloc(importlib._bootstrap, basedir=basedir) + self.assertIsNone(doc_link) + def test_getpager_with_stdin_none(self): previous_stdin = sys.stdin try: @@ -981,6 +1006,8 @@ def test_synopsis_sourceless(self): os = import_helper.import_fresh_module('os') expected = os.__doc__.splitlines()[0] filename = os.__spec__.cached + if not filename: + raise unittest.SkipTest('requires .pyc files') synopsis = pydoc.synopsis(filename) self.assertEqual(synopsis, expected) @@ -988,10 +1015,10 @@ def test_synopsis_sourceless(self): def test_synopsis_sourceless_empty_doc(self): with os_helper.temp_cwd() as test_dir: init_path = os.path.join(test_dir, 'foomod42.py') - cached_path = importlib.util.cache_from_source(init_path) + cached_path = init_path + 'c' with open(init_path, 'w') as fobj: fobj.write("foo = 1") - py_compile.compile(init_path) + py_compile.compile(init_path, cached_path) synopsis = pydoc.synopsis(init_path, {}) self.assertIsNone(synopsis) synopsis_cached = pydoc.synopsis(cached_path, {}) @@ -1218,6 +1245,71 @@ def function_with_really_long_name_so_annotations_can_be_rather_small( <lambda> lambda very_long_parameter_name_that_should_not_fit_into_a_single_line, second_very_long_parameter_name ''' % __name__) + @requires_docstrings + def test_long_summaries(self): + from . import longsummary + doc = pydoc.render_doc(longsummary) + doc = clean_text(doc) + self.assertEqual(doc, '''Python Library Documentation: module test.test_pydoc.longsummary in test.test_pydoc + +NAME + test.test_pydoc.longsummary + +CLASSES + builtins.object + C + + class C(builtins.object) + | This is a class summary that consists of a very long single line, + | exceeding the recommended PEP 8 limit. + | + | The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + | + | Methods defined here: + | + | meth(self) + | This is a method summary that consists of a very long single line, + | exceeding the recommended PEP 8 limit. + | + | The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + | + | ---------------------------------------------------------------------- + | Readonly properties defined here: + | + | prop + | This is a property summary that consists of a very long single line, + | exceeding the recommended PEP 8 limit. + | + | The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + | + | ---------------------------------------------------------------------- + | Data descriptors defined here: + | + | __dict__ + | dictionary for instance variables + | + | __weakref__ + | list of weak references to the object + +FUNCTIONS + func(self) + This is a function summary that consists of a very long single line, + exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + +DATA + data = <test.test_pydoc.longsummary.C object> + This is a data summary that consists of a very long single line, + exceeding the recommended PEP 8 limit. + + The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines. + +FILE + %s + +''' % inspect.getabsfile(longsummary)) + def test__future__imports(self): # __future__ features are excluded from module help, # except when it's the __future__ module itself @@ -1300,27 +1392,16 @@ def test_apropos_with_unreadable_dir(self): self.assertEqual(out.getvalue(), '') self.assertEqual(err.getvalue(), '') - @os_helper.skip_unless_working_chmod def test_apropos_empty_doc(self): pkgdir = os.path.join(TESTFN, 'walkpkg') - if support.is_emscripten: - # Emscripten's readdir implementation is buggy on directories - # with read permission but no execute permission. - old_umask = os.umask(0) - self.addCleanup(os.umask, old_umask) os.mkdir(pkgdir) self.addCleanup(rmtree, pkgdir) init_path = os.path.join(pkgdir, '__init__.py') with open(init_path, 'w') as fobj: fobj.write("foo = 1") - current_mode = stat.S_IMODE(os.stat(pkgdir).st_mode) - try: - os.chmod(pkgdir, current_mode & ~stat.S_IEXEC) - with self.restrict_walk_packages(path=[TESTFN]), captured_stdout() as stdout: - pydoc.apropos('') - self.assertIn('walkpkg', stdout.getvalue()) - finally: - os.chmod(pkgdir, current_mode) + with self.restrict_walk_packages(path=[TESTFN]), captured_stdout() as stdout: + pydoc.apropos('') + self.assertIn('walkpkg', stdout.getvalue()) def test_url_search_package_error(self): # URL handler search should cope with packages that raise exceptions @@ -1346,47 +1427,6 @@ def test_url_search_package_error(self): finally: sys.path[:] = saved_paths - @unittest.skip('causes undesirable side-effects (#20128)') - def test_modules(self): - # See Helper.listmodules(). - num_header_lines = 2 - num_module_lines_min = 5 # Playing it safe. - num_footer_lines = 3 - expected = num_header_lines + num_module_lines_min + num_footer_lines - - output = StringIO() - helper = pydoc.Helper(output=output) - helper('modules') - result = output.getvalue().strip() - num_lines = len(result.splitlines()) - - self.assertGreaterEqual(num_lines, expected) - - @unittest.skip('causes undesirable side-effects (#20128)') - def test_modules_search(self): - # See Helper.listmodules(). - expected = 'pydoc - ' - - output = StringIO() - helper = pydoc.Helper(output=output) - with captured_stdout() as help_io: - helper('modules pydoc') - result = help_io.getvalue() - - self.assertIn(expected, result) - - @unittest.skip('some buildbots are not cooperating (#20128)') - def test_modules_search_builtin(self): - expected = 'gc - ' - - output = StringIO() - helper = pydoc.Helper(output=output) - with captured_stdout() as help_io: - helper('modules garbage') - result = help_io.getvalue() - - self.assertStartsWith(result, expected) - def test_importfile(self): try: loaded_pydoc = pydoc.importfile(pydoc.__file__) @@ -1466,7 +1506,7 @@ def test_special_form(self): self.assertIn('NoReturn = typing.NoReturn', doc) self.assertIn(typing.NoReturn.__doc__.strip().splitlines()[0], doc) else: - self.assertIn('NoReturn = class _SpecialForm(_Final)', doc) + self.assertIn('NoReturn = class _SpecialForm(_Final, _NotIterable)', doc) def test_typing_pydoc(self): def foo(data: typing.List[typing.Any], @@ -1951,9 +1991,11 @@ def test_text_doc_routines_in_class(self, cls=pydocfodder.B): if not support.MISSING_C_DOCSTRINGS: self.assertIn(' | get(key, default=None, /) method of builtins.dict instance', lines) self.assertIn(' | dict_get = get(key, default=None, /) method of builtins.dict instance', lines) + self.assertIn(' | sin(x, /)', lines) else: self.assertIn(' | get(...) method of builtins.dict instance', lines) self.assertIn(' | dict_get = get(...) method of builtins.dict instance', lines) + self.assertIn(' | sin(object, /)', lines) lines = self.getsection(result, f' | Class methods {where}:', ' | ' + '-'*70) self.assertIn(' | B_classmethod(x)', lines) @@ -2039,6 +2081,11 @@ def test_text_doc_routines_in_module(self): self.assertIn(' __repr__(...) unbound builtins.object method', lines) self.assertIn(' object_repr = __repr__(...) unbound builtins.object method', lines) + # builtin functions + if not support.MISSING_C_DOCSTRINGS: + self.assertIn(' sin(x, /)', lines) + else: + self.assertIn(' sin(object, /)', lines) def test_html_doc_routines_in_module(self): doc = pydoc.HTMLDoc() @@ -2079,6 +2126,12 @@ def test_html_doc_routines_in_module(self): self.assertIn(' __repr__(...) unbound builtins.object method', lines) self.assertIn(' object_repr = __repr__(...) unbound builtins.object method', lines) + # builtin functions + if not support.MISSING_C_DOCSTRINGS: + self.assertIn(' sin(x, /)', lines) + else: + self.assertIn(' sin(object, /)', lines) + @unittest.skipIf( is_wasm32, @@ -2161,9 +2214,46 @@ def test_url_requests(self): class TestHelper(unittest.TestCase): + def mock_interactive_session(self, inputs): + """ + Given a list of inputs, run an interactive help session. Returns a string + of what would be shown on screen. + """ + input_iter = iter(inputs) + + def mock_getline(prompt): + output.write(prompt) + next_input = next(input_iter) + output.write(next_input + os.linesep) + return next_input + + with captured_stdout() as output: + helper = pydoc.Helper(output=output) + with unittest.mock.patch.object(helper, "getline", mock_getline): + helper.interact() + + # handle different line endings across platforms consistently + return output.getvalue().strip().splitlines(keepends=False) + def test_keywords(self): self.assertEqual(sorted(pydoc.Helper.keywords), - sorted(keyword.kwlist)) + sorted(keyword.kwlist + ['lazy'])) + + def test_interact_empty_line_continues(self): + # gh-138568: test pressing Enter without input should continue in help session + self.assertEqual( + self.mock_interactive_session(["", " ", "quit"]), + ["help> ", "help> ", "help> quit"], + ) + + def test_interact_quit_commands_exit(self): + quit_commands = ["quit", "q", "exit"] + for quit_cmd in quit_commands: + with self.subTest(quit_command=quit_cmd): + self.assertEqual( + self.mock_interactive_session([quit_cmd]), + [f"help> {quit_cmd}"], + ) class PydocWithMetaClasses(unittest.TestCase): @@ -2345,6 +2435,32 @@ def test_sys_path_adjustment_when_curdir_already_included(self): trailing_argv0dir = trailing_curdir + [self.argv0dir] self.assertIsNone(self._get_revised_path(trailing_argv0dir)) + def test__get_version(self): + import json + import warnings + + class MyModule: + __name__ = 'my_module' + + @property + def __version__(self): + warnings._deprecated("__version__", remove=(3, 20)) + return "1.2.3" + + module = MyModule() + doc = pydoc.Doc() + with warnings.catch_warnings(record=True) as w: # TODO: remove in 3.20 + warnings.simplefilter("always") + version = doc._get_version(json) + self.assertEqual(version, "2.0.9") + self.assertEqual(len(w), 0) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + version = doc._get_version(module) + self.assertEqual(version, "1.2.3") + self.assertEqual(len(w), 1) + def setUpModule(): thread_info = threading_helper.threading_setup() diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index d4b4f60be980a5..42309dd8f6c190 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -1,14 +1,18 @@ # XXX TypeErrors on calling handlers, or on bad return values from a # handler, are obscure and unhelpful. +import abc +import functools import os +import re import sys import sysconfig +import textwrap import unittest import traceback from io import BytesIO from test import support -from test.support import os_helper +from test.support import import_helper, os_helper from test.support import sortdict from unittest import mock from xml.parsers import expat @@ -223,8 +227,7 @@ def _verify_parse_output(self, operations): "Character data: '\xb5'", "End element: 'root'", ] - for operation, expected_operation in zip(operations, expected_operations): - self.assertEqual(operation, expected_operation) + self.assertEqual(operations, expected_operations) def test_parse_bytes(self): out = self.Outputter() @@ -272,6 +275,168 @@ def test_parse_again(self): self.assertEqual(expat.ErrorString(cm.exception.code), expat.errors.XML_ERROR_FINISHED) + @support.subTests('encoding', [ + # built-in Expat encodings + 'iso-8859-1', 'utf-8', 'utf-16', 'utf-16be', 'utf-16le', + # 8-bit Python encodings + 'iso8859-1', 'iso8859-2', 'iso8859-3', 'iso8859-4', 'iso8859-5', + 'iso8859-6', 'iso8859-7', 'iso8859-8', 'iso8859-9', 'iso8859-10', + 'iso8859-13', 'iso8859-14', 'iso8859-15', 'iso8859-16', + 'cp437', 'cp720', 'cp737', 'cp775', 'cp850', 'cp852', + 'cp855', 'cp856', 'cp857', 'cp858', 'cp860', 'cp861', 'cp862', + 'cp863', 'cp865', 'cp866', 'cp869', 'cp874', 'cp1006', 'cp1125', + 'cp1250', 'cp1251', 'cp1252', 'cp1253', 'cp1254', 'cp1255', + 'cp1256', 'cp1257', 'cp1258', + 'mac-cyrillic', 'mac-greek', 'mac-iceland', 'mac-latin2', + 'mac-roman', 'mac-turkish', + 'koi8-r', 'koi8-t', 'koi8-u', 'kz1048', 'ptcp154', + # multi-byte Python encodings + "cp932", "cp949", "cp950", + "Big5","EUC-JP", "GB2312", "GBK", "johab", "Shift_JIS", + 'UTF8', 'utf-8-sig', + "Big5-HKSCS", "EUC_JIS-2004", "EUC_JISX0213", + "Shift_JIS-2004", "Shift_JISX0213", + ]) + def test_supported_encodings(self, encoding): + out = self.Outputter() + parser = expat.ParserCreate() + self._hookup_callbacks(parser, out) + c = 'éπя\u05d0\u060c€'.encode(encoding, 'ignore').decode(encoding)[0] + data = (f'<?xml version="1.0" encoding="{encoding}"?>\n' + f'<root>{c}</root>').encode(encoding) + parser.Parse(data, True) + self.assertEqual(out.out, [ + ('XML declaration', ('1.0', encoding, -1)), + "Start element: 'root' {}", + f'Character data: {c!r}', + "End element: 'root'", + ]) + + @support.subTests('encoding', [ + 'UTF-8', 'utf-8', 'utf8', 'utf-16', 'utf-16le', 'utf-16be', + 'koi8-u', 'cp1125', 'cp1251', 'iso8859-5', 'mac-cyrillic', + ]) + def test_supported_encodings2(self, encoding): + out = self.Outputter() + parser = expat.ParserCreate() + self._hookup_callbacks(parser, out) + data = (f'<?xml version="1.0" encoding="{encoding}"?>\n' + '<!-- коментар -->' + '<корінь атрибут="значення">зміст</корінь>').encode(encoding) + parser.Parse(data, True) + self.assertEqual(out.out, [ + ('XML declaration', ('1.0', encoding, -1)), + "Comment: ' коментар '", + "Start element: 'корінь' {'атрибут': 'значення'}", + "Character data: 'зміст'", + "End element: 'корінь'", + ]) + + @support.subTests('encoding', [ + 'utf-8', 'utf-16', 'utf-16be', 'utf-16le', + ]) + def test_supported_non_bmp(self, encoding): + out = self.Outputter() + parser = expat.ParserCreate() + self._hookup_callbacks(parser, out) + c = '\U00020e6d\U00028e36' + data = (f'<?xml version="1.0" encoding="{encoding}"?>\n' + f'<root>{c}</root>').encode(encoding) + parser.Parse(data, True) + self.assertEqual(out.out, [ + ('XML declaration', ('1.0', encoding, -1)), + "Start element: 'root' {}", + f'Character data: {c!r}', + "End element: 'root'", + ]) + + @support.subTests('encoding', [ + 'UTF8', 'utf-8-sig', + "Big5-HKSCS", "EUC_JIS-2004", "EUC_JISX0213", + "Shift_JIS-2004", "Shift_JISX0213", + ]) + def test_unsupported_non_bmp(self, encoding): + parser = expat.ParserCreate() + c = '\U00020e6d\U00028e36' + data = (f'<?xml version="1.0" encoding="{encoding}"?>\n' + f'<root>{c}</root>').encode(encoding) + with self.assertRaises(expat.ExpatError): + parser.Parse(data, True) + + @support.subTests('encoding', [ + 'UTF-7', + "unicode-escape", "raw-unicode-escape", + "EUC-KR", + "GB18030", + "HZ-GB-2312", + "ISO-2022-JP", "ISO-2022-JP-1", "ISO-2022-JP-2004", + "ISO-2022-JP-2", "ISO-2022-JP-3", "ISO-2022-JP-EXT", + "ISO-2022-KR", + ]) + def test_unsupported_encodings(self, encoding): + parser = expat.ParserCreate() + data = (f'<?xml version="1.0" encoding="{encoding}"?>\n' + '<root></root>').encode(encoding) + with self.assertRaises(ValueError): + parser.Parse(data, True) + + parser = expat.ParserCreate() + data = (f'<?xml version="1.0" encoding="{encoding}"?>\n' + '<root></root>').encode() + with self.assertRaises(ValueError): + parser.Parse(data, True) + + @support.subTests('encoding', [ + 'cp037', 'cp273', 'cp424', 'cp500', 'cp864', 'cp875', + 'cp1026', 'cp1140', + 'mac_arabic', 'mac_farsi', + ]) + def test_incompatible_encodings(self, encoding): + parser = expat.ParserCreate() + data = (f'<?xml version="1.0" encoding="{encoding}"?>\n' + '<root></root>').encode(encoding) + with self.assertRaises(expat.ExpatError): + parser.Parse(data, True) + + parser = expat.ParserCreate() + data = (f'<?xml version="1.0" encoding="{encoding}"?>\n' + '<root></root>').encode() + with self.assertRaisesRegex(expat.ExpatError, 'unknown encoding'): + parser.Parse(data, True) + + @support.subTests('encoding', [ + 'hex_codec', 'rot_13', + ]) + def test_non_text_encodings(self, encoding): + parser = expat.ParserCreate() + data = (f'<?xml version="1.0" encoding="{encoding}"?>\n' + '<root></root>').encode() + with self.assertRaises(LookupError): + parser.Parse(data, True) + + def test_undefined_encoding(self): + parser = expat.ParserCreate() + data = b'<?xml version="1.0" encoding="undefined"?>\n<root></root>' + with self.assertRaises(UnicodeError): + parser.Parse(data, True) + + def test_unknown_encoding(self): + parser = expat.ParserCreate() + data = b'<?xml version="1.0" encoding="xyz"?>\n<root></root>' + with self.assertRaises(LookupError): + parser.Parse(data, True) + + @support.subTests('sample,exception', [ + (b'<x> \xa1</x>', UnicodeDecodeError), # crashed + (b'<x> \xa1</x', UnicodeDecodeError), # crashed + (b'<x> \xa1', expat.ExpatError), + ]) + def test_multibyte_encoding_errors(self, sample, exception): + parser = expat.ParserCreate() + data = b'<?xml version="1.0" encoding="EUC-JP"?>\n' + sample + with self.assertRaises(exception): + parser.Parse(data, True) + class NamespaceSeparatorTest(unittest.TestCase): def test_legal(self): # Tests that make sure we get errors when the namespace_separator value @@ -506,6 +671,34 @@ def _test_exception(self, have_source): self.assertIn('call_with_frame("StartElement"', entries[1].line) + def test_invalid_NotStandalone(self): + parser = expat.ParserCreate() + parser.NotStandaloneHandler = mock.Mock(return_value="bad value") + parser.ElementDeclHandler = lambda _1, _2: None + + payload = b"""\ +<!DOCTYPE quotations SYSTEM "quotations.dtd" [<!ELEMENT root ANY>]><root/> +""" + with self.assertRaises(TypeError) as cm: + parser.Parse(payload, True) + parser.NotStandaloneHandler.assert_called_once() + + notes = ["invalid 'NotStandalone' event handler return value"] + self.assertEqual(cm.exception.__notes__, notes) + + def test_invalid_ExternalEntityRefHandler(self): + parser = expat.ParserCreate() + parser.UseForeignDTD() + parser.SetParamEntityParsing(expat.XML_PARAM_ENTITY_PARSING_ALWAYS) + parser.ExternalEntityRefHandler = mock.Mock(return_value=None) + + with self.assertRaises(TypeError) as cm: + parser.Parse(b"<?xml version='1.0'?><element/>", True) + parser.ExternalEntityRefHandler.assert_called_once() + + notes = ["invalid 'ExternalEntityRef' event handler return value"] + self.assertEqual(cm.exception.__notes__, notes) + # Test Current* members: class PositionTest(unittest.TestCase): @@ -680,6 +873,56 @@ def test_change_size_2(self): parser.Parse(xml2, True) self.assertEqual(self.n, 4) + @support.requires_resource('cpu') + @support.requires_resource('walltime') + @support.bigmemtest(size=2**31, memuse=4, dry_run=False) + def test_large_character_data_does_not_crash(self, size): + # See https://github.com/python/cpython/issues/148441 + parser = expat.ParserCreate() + parser.buffer_text = True + parser.buffer_size = 2**31 - 1 # INT_MAX + N = 2049 * (1 << 20) - 3 # Character data greater than INT_MAX + self.assertGreater(N, parser.buffer_size) + parser.CharacterDataHandler = lambda text: None + xml_data = b"<r>" + b"A" * N + b"</r>" + self.assertEqual(parser.Parse(xml_data, True), 1) + +class ElementDeclHandlerTest(unittest.TestCase): + def test_trigger_leak(self): + # Unfixed, this test would leak the memory of the so-called + # "content model" in function ``my_ElementDeclHandler`` of pyexpat. + # See https://github.com/python/cpython/issues/140593. + data = textwrap.dedent('''\ + <!DOCTYPE quotations SYSTEM "quotations.dtd" [ + <!ELEMENT root ANY> + ]> + <root/> + ''').encode('UTF-8') + + parser = expat.ParserCreate() + parser.NotStandaloneHandler = lambda: 1.234 # arbitrary float + parser.ElementDeclHandler = lambda _1, _2: None + self.assertRaises(TypeError, parser.Parse, data, True) + + @support.skip_if_unlimited_stack_size + @support.skip_emscripten_stack_overflow() + @support.skip_wasi_stack_overflow() + def test_deeply_nested_content_model(self): + # This should raise a RecursionError and not crash. + # See https://github.com/python/cpython/issues/145986. + N = 800_000 + data = ( + b'<!DOCTYPE root [\n<!ELEMENT root ' + + b'(a, ' * N + b'a' + b')' * N + + b'>\n]>\n<root/>\n' + ) + + parser = expat.ParserCreate() + parser.ElementDeclHandler = lambda _1, _2: None + with support.infinite_recursion(): + with self.assertRaises(RecursionError): + parser.Parse(data) + class MalformedInputTest(unittest.TestCase): def test1(self): xml = b"\0\r\n" @@ -767,6 +1010,81 @@ def resolve_entity(context, base, system_id, public_id): self.assertEqual(handler_call_args, [("bar", "baz")]) +class ParentParserLifetimeTest(unittest.TestCase): + """ + Subparsers make use of their parent XML_Parser inside of Expat. + As a result, parent parsers need to outlive subparsers. + + See https://github.com/python/cpython/issues/139400. + """ + + def test_parent_parser_outlives_its_subparsers__single(self): + parser = expat.ParserCreate() + subparser = parser.ExternalEntityParserCreate(None) + + # Now try to cause garbage collection of the parent parser + # while it's still being referenced by a related subparser. + del parser + + def test_parent_parser_outlives_its_subparsers__multiple(self): + parser = expat.ParserCreate() + subparser_one = parser.ExternalEntityParserCreate(None) + subparser_two = parser.ExternalEntityParserCreate(None) + + # Now try to cause garbage collection of the parent parser + # while it's still being referenced by a related subparser. + del parser + + def test_parent_parser_outlives_its_subparsers__chain(self): + parser = expat.ParserCreate() + subparser = parser.ExternalEntityParserCreate(None) + subsubparser = subparser.ExternalEntityParserCreate(None) + + # Now try to cause garbage collection of the parent parsers + # while they are still being referenced by a related subparser. + del parser + del subparser + + +class ExternalEntityParserCreateErrorTest(unittest.TestCase): + """ExternalEntityParserCreate error paths should not crash or leak + refcounts on the parent parser. + + See https://github.com/python/cpython/issues/144984. + """ + + @classmethod + def setUpClass(cls): + cls.testcapi = import_helper.import_module('_testcapi') + + @unittest.skipIf(support.Py_TRACE_REFS, + 'Py_TRACE_REFS conflicts with testcapi.set_nomemory') + def test_error_path_no_crash(self): + # When an allocation inside ExternalEntityParserCreate fails, + # the partially-initialized subparser is deallocated. This + # must not dereference NULL handlers or double-decrement the + # parent parser's refcount. + parser = expat.ParserCreate() + parser.buffer_text = True + rc_before = sys.getrefcount(parser) + + # We avoid self.assertRaises(MemoryError) here because the + # context manager itself needs memory allocations that fail + # while the nomemory hook is active. + self.testcapi.set_nomemory(1, 10) + raised = False + try: + parser.ExternalEntityParserCreate(None) + except MemoryError: + raised = True + finally: + self.testcapi.remove_mem_hooks() + self.assertTrue(raised, "MemoryError not raised") + + rc_after = sys.getrefcount(parser) + self.assertEqual(rc_after, rc_before) + + class ReparseDeferralTest(unittest.TestCase): def test_getter_setter_round_trip(self): parser = expat.ParserCreate() @@ -821,5 +1139,259 @@ def start_element(name, _): self.assertEqual(started, ['doc']) +class AttackProtectionTestBase(abc.ABC): + """ + Base class for testing protections against XML payloads with + disproportionate amplification. + + The protections being tested should detect and prevent attacks + that leverage disproportionate amplification from small inputs. + """ + + @staticmethod + def exponential_expansion_payload(*, nrows, ncols, text='.'): + """Create a billion laughs attack payload. + + Be careful: the number of total items is pow(n, k), thereby + requiring at least pow(ncols, nrows) * sizeof(text) memory! + """ + template = textwrap.dedent(f"""\ + <?xml version="1.0"?> + <!DOCTYPE doc [ + <!ENTITY row0 "{text}"> + <!ELEMENT doc (#PCDATA)> + {{body}} + ]> + <doc>&row{nrows};</doc> + """).rstrip() + + body = '\n'.join( + f'<!ENTITY row{i + 1} "{f"&row{i};" * ncols}">' + for i in range(nrows) + ) + body = textwrap.indent(body, ' ' * 4) + return template.format(body=body) + + def test_payload_generation(self): + # self-test for exponential_expansion_payload() + payload = self.exponential_expansion_payload(nrows=2, ncols=3) + self.assertEqual(payload, textwrap.dedent("""\ + <?xml version="1.0"?> + <!DOCTYPE doc [ + <!ENTITY row0 "."> + <!ELEMENT doc (#PCDATA)> + <!ENTITY row1 "&row0;&row0;&row0;"> + <!ENTITY row2 "&row1;&row1;&row1;"> + ]> + <doc>&row2;</doc> + """).rstrip()) + + def assert_root_parser_failure(self, func, /, *args, **kwargs): + """Check that func(*args, **kwargs) is invalid for a sub-parser.""" + msg = "parser must be a root parser" + self.assertRaisesRegex(expat.ExpatError, msg, func, *args, **kwargs) + + @abc.abstractmethod + def assert_rejected(self, func, /, *args, **kwargs): + """Assert that func(*args, **kwargs) triggers the attack protection. + + Note: this method must ensure that the attack protection being tested + is the one that is actually triggered at runtime, e.g., by matching + the exact error message. + """ + + @abc.abstractmethod + def set_activation_threshold(self, parser, threshold): + """Set the activation threshold for the tested protection.""" + + @abc.abstractmethod + def set_maximum_amplification(self, parser, max_factor): + """Set the maximum amplification factor for the tested protection.""" + + @abc.abstractmethod + def test_set_activation_threshold__threshold_reached(self): + """Test when the activation threshold is exceeded.""" + + @abc.abstractmethod + def test_set_activation_threshold__threshold_not_reached(self): + """Test when the activation threshold is not exceeded.""" + + def test_set_activation_threshold__invalid_threshold_type(self): + parser = expat.ParserCreate() + setter = functools.partial(self.set_activation_threshold, parser) + + self.assertRaises(TypeError, setter, 1.0) + self.assertRaises(TypeError, setter, -1.5) + self.assertRaises(ValueError, setter, -5) + + def test_set_activation_threshold__invalid_threshold_range(self): + _testcapi = import_helper.import_module("_testcapi") + parser = expat.ParserCreate() + setter = functools.partial(self.set_activation_threshold, parser) + + self.assertRaises(OverflowError, setter, _testcapi.ULLONG_MAX + 1) + + def test_set_activation_threshold__fail_for_subparser(self): + parser = expat.ParserCreate() + subparser = parser.ExternalEntityParserCreate(None) + setter = functools.partial(self.set_activation_threshold, subparser) + self.assert_root_parser_failure(setter, 12345) + + @abc.abstractmethod + def test_set_maximum_amplification__amplification_exceeded(self): + """Test when the amplification factor is exceeded.""" + + @abc.abstractmethod + def test_set_maximum_amplification__amplification_not_exceeded(self): + """Test when the amplification factor is not exceeded.""" + + def test_set_maximum_amplification__infinity(self): + inf = float('inf') # an 'inf' threshold is allowed by Expat + parser = expat.ParserCreate() + self.assertIsNone(self.set_maximum_amplification(parser, inf)) + + def test_set_maximum_amplification__invalid_max_factor_type(self): + parser = expat.ParserCreate() + setter = functools.partial(self.set_maximum_amplification, parser) + + self.assertRaises(TypeError, setter, None) + self.assertRaises(TypeError, setter, 'abc') + + def test_set_maximum_amplification__invalid_max_factor_range(self): + parser = expat.ParserCreate() + setter = functools.partial(self.set_maximum_amplification, parser) + + msg = re.escape("'max_factor' must be at least 1.0") + self.assertRaisesRegex(expat.ExpatError, msg, setter, float('nan')) + self.assertRaisesRegex(expat.ExpatError, msg, setter, 0.99) + + def test_set_maximum_amplification__fail_for_subparser(self): + parser = expat.ParserCreate() + subparser = parser.ExternalEntityParserCreate(None) + setter = functools.partial(self.set_maximum_amplification, subparser) + self.assert_root_parser_failure(setter, 123.45) + + +@unittest.skipIf(expat.version_info < (2, 4, 0), "requires Expat >= 2.4.0") +class ExpansionProtectionTest(AttackProtectionTestBase, unittest.TestCase): + + def assert_rejected(self, func, /, *args, **kwargs): + """Check that func(*args, **kwargs) hits the allocation limit.""" + msg = ( + r"limit on input amplification factor \(from DTD and entities\) " + r"breached: line \d+, column \d+" + ) + self.assertRaisesRegex(expat.ExpatError, msg, func, *args, **kwargs) + + def set_activation_threshold(self, parser, threshold): + return parser.SetBillionLaughsAttackProtectionActivationThreshold(threshold) + + def set_maximum_amplification(self, parser, max_factor): + return parser.SetBillionLaughsAttackProtectionMaximumAmplification(max_factor) + + def test_set_activation_threshold__threshold_reached(self): + parser = expat.ParserCreate() + # Choose a threshold expected to be always reached. + self.set_activation_threshold(parser, 3) + # Check that the threshold is reached by choosing a small factor + # and a payload whose peak amplification factor exceeds it. + self.assertIsNone(self.set_maximum_amplification(parser, 1.0)) + payload = self.exponential_expansion_payload(ncols=10, nrows=4) + self.assert_rejected(parser.Parse, payload, True) + + def test_set_activation_threshold__threshold_not_reached(self): + parser = expat.ParserCreate() + # Choose a threshold expected to be never reached. + self.set_activation_threshold(parser, pow(10, 5)) + # Check that the threshold is reached by choosing a small factor + # and a payload whose peak amplification factor exceeds it. + self.assertIsNone(self.set_maximum_amplification(parser, 1.0)) + payload = self.exponential_expansion_payload(ncols=10, nrows=4) + self.assertIsNotNone(parser.Parse(payload, True)) + + def test_set_maximum_amplification__amplification_exceeded(self): + parser = expat.ParserCreate() + # Unconditionally enable maximum activation factor. + self.set_activation_threshold(parser, 0) + # Choose a max amplification factor expected to always be exceeded. + self.assertIsNone(self.set_maximum_amplification(parser, 1.0)) + # Craft a payload for which the peak amplification factor is > 1.0. + payload = self.exponential_expansion_payload(ncols=1, nrows=2) + self.assert_rejected(parser.Parse, payload, True) + + def test_set_maximum_amplification__amplification_not_exceeded(self): + parser = expat.ParserCreate() + # Unconditionally enable maximum activation factor. + self.set_activation_threshold(parser, 0) + # Choose a max amplification factor expected to never be exceeded. + self.assertIsNone(self.set_maximum_amplification(parser, 1e4)) + # Craft a payload for which the peak amplification factor is < 1e4. + payload = self.exponential_expansion_payload(ncols=1, nrows=2) + self.assertIsNotNone(parser.Parse(payload, True)) + + +@unittest.skipIf(not hasattr(expat.XMLParserType, + "SetAllocTrackerMaximumAmplification"), + "requires Python compiled with Expat >= 2.7.2") +class MemoryProtectionTest(AttackProtectionTestBase, unittest.TestCase): + + # NOTE: with the default Expat configuration, the billion laughs protection + # may hit before the allocation limiter if exponential_expansion_payload() + # is not carefully parametrized. As such, the payloads should be chosen so + # that either the allocation limiter is hit before other protections are + # triggered or no protection at all is triggered. + + def assert_rejected(self, func, /, *args, **kwargs): + """Check that func(*args, **kwargs) hits the allocation limit.""" + msg = r"out of memory: line \d+, column \d+" + self.assertRaisesRegex(expat.ExpatError, msg, func, *args, **kwargs) + + def set_activation_threshold(self, parser, threshold): + return parser.SetAllocTrackerActivationThreshold(threshold) + + def set_maximum_amplification(self, parser, max_factor): + return parser.SetAllocTrackerMaximumAmplification(max_factor) + + def test_set_activation_threshold__threshold_reached(self): + parser = expat.ParserCreate() + # Choose a threshold expected to be always reached. + self.set_activation_threshold(parser, 3) + # Check that the threshold is reached by choosing a small factor + # and a payload whose peak amplification factor exceeds it. + self.assertIsNone(self.set_maximum_amplification(parser, 1.0)) + payload = self.exponential_expansion_payload(ncols=10, nrows=4) + self.assert_rejected(parser.Parse, payload, True) + + def test_set_activation_threshold__threshold_not_reached(self): + parser = expat.ParserCreate() + # Choose a threshold expected to be never reached. + self.set_activation_threshold(parser, pow(10, 5)) + # Check that the threshold is reached by choosing a small factor + # and a payload whose peak amplification factor exceeds it. + self.assertIsNone(self.set_maximum_amplification(parser, 1.0)) + payload = self.exponential_expansion_payload(ncols=10, nrows=4) + self.assertIsNotNone(parser.Parse(payload, True)) + + def test_set_maximum_amplification__amplification_exceeded(self): + parser = expat.ParserCreate() + # Unconditionally enable maximum activation factor. + self.set_activation_threshold(parser, 0) + # Choose a max amplification factor expected to always be exceeded. + self.assertIsNone(self.set_maximum_amplification(parser, 1.0)) + # Craft a payload for which the peak amplification factor is > 1.0. + payload = self.exponential_expansion_payload(ncols=1, nrows=2) + self.assert_rejected(parser.Parse, payload, True) + + def test_set_maximum_amplification__amplification_not_exceeded(self): + parser = expat.ParserCreate() + # Unconditionally enable maximum activation factor. + self.set_activation_threshold(parser, 0) + # Choose a max amplification factor expected to never be exceeded. + self.assertIsNone(self.set_maximum_amplification(parser, 1e4)) + # Craft a payload for which the peak amplification factor is < 1e4. + payload = self.exponential_expansion_payload(ncols=1, nrows=2) + self.assertIsNotNone(parser.Parse(payload, True)) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_pyrepl/__init__.py b/Lib/test/test_pyrepl/__init__.py index 8ef472eb0cffaf..1534d63352cc55 100644 --- a/Lib/test/test_pyrepl/__init__.py +++ b/Lib/test/test_pyrepl/__init__.py @@ -1,14 +1,13 @@ import os -from test.support import load_package_tests -import unittest +import sys +from test.support import import_helper, load_package_tests -try: - import termios -except ImportError: - raise unittest.SkipTest("termios required") -else: - del termios +import_helper.import_module("_pyrepl") + + +if sys.platform != "win32": + import_helper.import_module("termios") def load_tests(*args): diff --git a/Lib/test/test_pyrepl/support.py b/Lib/test/test_pyrepl/support.py index 4f7f9d77933336..c879a2f93b6313 100644 --- a/Lib/test/test_pyrepl/support.py +++ b/Lib/test/test_pyrepl/support.py @@ -4,6 +4,7 @@ from unittest.mock import MagicMock from _pyrepl.console import Console, Event +from _pyrepl.render import RenderLine, RenderedScreen from _pyrepl.readline import ReadlineAlikeReader, ReadlineConfig from _pyrepl.simple_interact import _strip_final_indent from _pyrepl.utils import unbracket, ANSI_ESCAPE_SEQUENCE @@ -15,7 +16,13 @@ def assert_screen_equal( ): actual = clean_screen(reader) if clean else reader.screen expected = expected.split("\n") - self.assertListEqual(actual, expected) + if clean: + self.assertListEqual(actual, expected) + return + + actual_lines = [RenderLine.from_rendered_text(line) for line in actual] + expected_lines = [RenderLine.from_rendered_text(line) for line in expected] + self.assertListEqual(actual_lines, expected_lines) def multiline_input(reader: ReadlineAlikeReader, namespace: dict | None = None): @@ -88,6 +95,8 @@ def prepare_console(events: Iterable[Event], **kwargs) -> MagicMock | Console: console.get_event.side_effect = events console.height = 100 console.width = 80 + console.getheightwidth = MagicMock(side_effect=lambda: (console.height, console.width)) + for key, val in kwargs.items(): setattr(console, key, val) return console @@ -118,9 +127,11 @@ class FakeConsole(Console): def __init__(self, events, encoding="utf-8") -> None: self.events = iter(events) self.encoding = encoding - self.screen = [] + self._rendered_screen = RenderedScreen.empty() self.height = 100 self.width = 80 + self.posxy = (0, 0) + self._redraw_visual_cycle = 0 def get_event(self, block: bool = True) -> Event | None: return next(self.events) @@ -131,7 +142,7 @@ def getpending(self) -> Event: def getheightwidth(self) -> tuple[int, int]: return self.height, self.width - def refresh(self, screen: list[str], xy: tuple[int, int]) -> None: + def refresh(self, rendered_screen: RenderedScreen) -> None: pass def prepare(self) -> None: diff --git a/Lib/test/test_pyrepl/test_fancycompleter.py b/Lib/test/test_pyrepl/test_fancycompleter.py new file mode 100644 index 00000000000000..0ffc1ed97b557a --- /dev/null +++ b/Lib/test/test_pyrepl/test_fancycompleter.py @@ -0,0 +1,250 @@ +import importlib +import inspect +import os +import types +import unittest + +from _colorize import ANSIColors, get_theme +from _pyrepl.completing_reader import stripcolor +from _pyrepl.fancycompleter import ( + Completer, + colorize_matches, + commonprefix, + _color_for_obj, +) +from test.support.import_helper import ready_to_import + +class MockPatch: + def __init__(self): + self.original_values = {} + + def setattr(self, obj, name, value): + if obj not in self.original_values: + self.original_values[obj] = {} + if name not in self.original_values[obj]: + self.original_values[obj][name] = getattr(obj, name) + setattr(obj, name, value) + + def restore_all(self): + for obj, attrs in self.original_values.items(): + for name, value in attrs.items(): + setattr(obj, name, value) + +class FancyCompleterTests(unittest.TestCase): + def setUp(self): + self.mock_patch = MockPatch() + + def tearDown(self): + self.mock_patch.restore_all() + + def test_commonprefix(self): + self.assertEqual(commonprefix(['isalpha', 'isdigit', 'foo']), '') + self.assertEqual(commonprefix(['isalpha', 'isdigit']), 'is') + self.assertEqual(commonprefix([]), '') + + def test_colorize_matches_signature(self): + signature = inspect.signature(colorize_matches) + + self.assertEqual(list(signature.parameters), ["names", "values", "theme"]) + + def test_complete_attribute(self): + compl = Completer({'a': None}, use_colors=False) + self.assertEqual(compl.attr_matches('a.'), ['a.__']) + matches = compl.attr_matches('a.__') + self.assertNotIn('__class__', matches) + self.assertIn('a.__class__', matches) + match = compl.attr_matches('a.__class') + self.assertEqual(len(match), 1) + self.assertTrue(match[0].startswith('a.__class__')) + + def test_complete_attribute_prefix(self): + class C(object): + attr = 1 + _attr = 2 + __attr__attr = 3 + compl = Completer({'a': C}, use_colors=False) + self.assertEqual(compl.attr_matches('a.'), ['a.attr', 'a.mro']) + self.assertEqual( + compl.attr_matches('a._'), + ['a._C__attr__attr', 'a._attr'], + ) + matches = compl.attr_matches('a.__') + self.assertNotIn('__class__', matches) + self.assertIn('a.__class__', matches) + match = compl.attr_matches('a.__class') + self.assertEqual(len(match), 1) + self.assertTrue(match[0].startswith('a.__class__')) + + compl = Completer({'a': None}, use_colors=False) + self.assertEqual(compl.attr_matches('a._'), ['a.__']) + + def test_complete_attribute_colored(self): + theme = get_theme() + compl = Completer({'a': 42}, use_colors=True) + matches = compl.attr_matches('a.__') + self.assertGreater(len(matches), 2) + expected_color = theme.fancycompleter.type + expected_part = f'{expected_color}a.__class__{ANSIColors.RESET}' + for match in matches: + if expected_part in match: + break + else: + self.assertFalse(True, matches) + self.assertNotIn(' ', matches) + + def test_preserves_callable_postfix_for_single_attribute_match(self): + compl = Completer({'os': os}, use_colors=False) + self.assertEqual(compl.attr_matches('os.getpid'), ['os.getpid()']) + + def test_property_method_not_called(self): + class Foo: + property_called = False + + @property + def bar(self): + self.property_called = True + return 1 + + foo = Foo() + compl = Completer({'foo': foo}, use_colors=False) + self.assertEqual(compl.attr_matches('foo.b'), ['foo.bar']) + self.assertFalse(foo.property_called) + + def test_excessive_getattr(self): + class Foo: + calls = 0 + bar = '' + + def __getattribute__(self, name): + if name == 'bar': + self.calls += 1 + return None + return super().__getattribute__(name) + + foo = Foo() + compl = Completer({'foo': foo}, use_colors=False) + self.assertEqual(compl.complete('foo.b', 0), 'foo.bar') + self.assertEqual(foo.calls, 1) + + def test_uncreated_attr(self): + class Foo: + __slots__ = ('bar',) + + compl = Completer({'foo': Foo()}, use_colors=False) + self.assertEqual(compl.complete('foo.', 0), 'foo.bar') + + def test_module_attributes_do_not_reify_lazy_imports(self): + with ready_to_import("test_pyrepl_lazy_mod", "lazy import json\n") as (name, _): + module = importlib.import_module(name) + self.assertIs(type(module.__dict__["json"]), types.LazyImportType) + + compl = Completer({name: module}, use_colors=False) + self.assertEqual(compl.attr_matches(f"{name}.j"), [f"{name}.json"]) + self.assertIs(type(module.__dict__["json"]), types.LazyImportType) + + def test_complete_colored_single_match(self): + """No coloring, via commonprefix.""" + compl = Completer({'foobar': 42}, use_colors=True) + matches = compl.global_matches('foob') + self.assertEqual(matches, ['foobar']) + + def test_does_not_color_single_match(self): + class obj: + msgs = [] + + compl = Completer({'obj': obj}, use_colors=True) + matches = compl.attr_matches('obj.msgs') + self.assertEqual(matches, ['obj.msgs']) + + def test_complete_global(self): + compl = Completer({'foobar': 1, 'foobazzz': 2}, use_colors=False) + self.assertEqual(compl.global_matches('foo'), ['fooba']) + matches = compl.global_matches('fooba') + self.assertEqual(set(matches), set(['foobar', 'foobazzz'])) + self.assertEqual(compl.global_matches('foobaz'), ['foobazzz']) + self.assertEqual(compl.global_matches('nothing'), []) + + def test_complete_global_colored(self): + theme = get_theme() + compl = Completer({'foobar': 1, 'foobazzz': 2}, use_colors=True) + self.assertEqual(compl.global_matches('foo'), ['fooba']) + matches = compl.global_matches('fooba') + + int_color = theme.fancycompleter.int + self.assertEqual(matches, [ + f'{int_color}foobar{ANSIColors.RESET}', + f'{int_color}foobazzz{ANSIColors.RESET}', + ]) + self.assertEqual(compl.global_matches('foobaz'), ['foobazzz']) + self.assertEqual(compl.global_matches('nothing'), []) + + def test_colorized_match_is_stripped(self): + theme = get_theme() + match = _color_for_obj('spam', 1, theme) + self.assertEqual(stripcolor(match), 'spam') + + def test_complete_with_indexer(self): + compl = Completer({'lst': [None, 2, 3]}, use_colors=False) + self.assertEqual(compl.attr_matches('lst[0].'), ['lst[0].__']) + matches = compl.attr_matches('lst[0].__') + self.assertNotIn('__class__', matches) + self.assertIn('lst[0].__class__', matches) + match = compl.attr_matches('lst[0].__class') + self.assertEqual(len(match), 1) + self.assertTrue(match[0].startswith('lst[0].__class__')) + + def test_autocomplete(self): + class A: + aaa = None + abc_1 = None + abc_2 = None + abc_3 = None + bbb = None + compl = Completer({'A': A}, use_colors=False) + # + # In this case, we want to display all attributes which start with + # 'a'. + matches = compl.attr_matches('A.a') + self.assertEqual( + sorted(matches), + ['A.aaa', 'A.abc_1', 'A.abc_2', 'A.abc_3'], + ) + # + # If there is an actual common prefix, we return just it, so that readline + # will insert it into place + matches = compl.attr_matches('A.ab') + self.assertEqual(matches, ['A.abc_']) + # + # Finally, at the next tab, we display again all the completions + # available for this common prefix. + matches = compl.attr_matches('A.abc_') + self.assertEqual( + sorted(matches), + ['A.abc_1', 'A.abc_2', 'A.abc_3'], + ) + + def test_complete_exception(self): + compl = Completer({}, use_colors=False) + self.assertEqual(compl.attr_matches('xxx.'), []) + + def test_complete_invalid_attr(self): + compl = Completer({'str': str}, use_colors=False) + self.assertEqual(compl.attr_matches('str.xx'), []) + + def test_complete_function_skipped(self): + compl = Completer({'str': str}, use_colors=False) + self.assertEqual(compl.attr_matches('str.split().'), []) + + def test_unicode_in___dir__(self): + class Foo(object): + def __dir__(self): + return ['hello', 'world'] + + compl = Completer({'a': Foo()}, use_colors=False) + matches = compl.attr_matches('a.') + self.assertEqual(matches, ['a.hello', 'a.world']) + self.assertIs(type(matches[0]), str) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_pyrepl/test_interact.py b/Lib/test/test_pyrepl/test_interact.py index 8c0eeab6dcae96..fd4530ebc004aa 100644 --- a/Lib/test/test_pyrepl/test_interact.py +++ b/Lib/test/test_pyrepl/test_interact.py @@ -1,7 +1,7 @@ import contextlib import io -import unittest import warnings +import unittest from unittest.mock import patch from textwrap import dedent @@ -293,7 +293,7 @@ def f(): """) with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("default") + warnings.simplefilter("always") console.runsource(code) count = sum("'return' in a 'finally' block" in str(w.message) diff --git a/Lib/test/test_pyrepl/test_layout.py b/Lib/test/test_pyrepl/test_layout.py new file mode 100644 index 00000000000000..6825659b772d21 --- /dev/null +++ b/Lib/test/test_pyrepl/test_layout.py @@ -0,0 +1,309 @@ +from unittest import TestCase +from _pyrepl.content import ( + ContentFragment, + ContentLine, + PromptContent, + SourceLine, +) +from _pyrepl.layout import ( + LayoutMap, + LayoutRow, + layout_content_lines, +) + + +def _source(text, lineno=0, start_offset=0, has_newline=True, cursor_index=None): + return SourceLine(lineno, text, start_offset, has_newline, cursor_index) + + +def _prompt(text=">>> ", width=4): + return PromptContent((), text, width) + + +def _body_from_text(text): + return tuple(ContentFragment(ch, 1) for ch in text) + + +def _content_line(text, prompt=None, has_newline=True, start_offset=0): + if prompt is None: + prompt = _prompt() + body = _body_from_text(text) + source = _source(text, start_offset=start_offset, has_newline=has_newline) + return ContentLine(source, prompt, body) + + +class TestLayoutRow(TestCase): + def test_width_basic(self): + row = LayoutRow(4, (1, 1, 1)) + self.assertEqual(row.width, 7) + + def test_width_with_suffix(self): + row = LayoutRow(4, (1, 1), suffix_width=1) + self.assertEqual(row.width, 7) + + def test_screeninfo_without_suffix(self): + row = LayoutRow(4, (1, 1, 1)) + prompt_w, widths = row.screeninfo + self.assertEqual(prompt_w, 4) + self.assertEqual(widths, [1, 1, 1]) + + def test_screeninfo_with_suffix(self): + row = LayoutRow(4, (1, 1), suffix_width=1) + prompt_w, widths = row.screeninfo + self.assertEqual(prompt_w, 4) + self.assertEqual(widths, [1, 1, 1]) + + +class TestLayoutMap(TestCase): + def test_empty(self): + lm = LayoutMap.empty() + self.assertEqual(len(lm.rows), 1) + self.assertEqual(lm.max_row(), 0) + self.assertEqual(lm.max_column(0), 0) + + def test_screeninfo(self): + lm = LayoutMap(( + LayoutRow(4, (1, 1, 1)), + LayoutRow(0, (1, 1)), + )) + info = lm.screeninfo + self.assertEqual(len(info), 2) + self.assertEqual(info[0], (4, [1, 1, 1])) + self.assertEqual(info[1], (0, [1, 1])) + + def test_max_column(self): + lm = LayoutMap((LayoutRow(4, (1, 1, 1)),)) + self.assertEqual(lm.max_column(0), 7) + + def test_max_row(self): + lm = LayoutMap((LayoutRow(0, ()), LayoutRow(0, ()))) + self.assertEqual(lm.max_row(), 1) + + def test_pos_to_xy_empty_rows(self): + lm = LayoutMap(()) + self.assertEqual(lm.pos_to_xy(0), (0, 0)) + + def test_pos_to_xy_single_row(self): + lm = LayoutMap((LayoutRow(4, (1, 1, 1), buffer_advance=3),)) + self.assertEqual(lm.pos_to_xy(0), (4, 0)) + self.assertEqual(lm.pos_to_xy(1), (5, 0)) + self.assertEqual(lm.pos_to_xy(2), (6, 0)) + self.assertEqual(lm.pos_to_xy(3), (7, 0)) + + def test_pos_to_xy_multi_row(self): + lm = LayoutMap(( + LayoutRow(4, (1, 1, 1), buffer_advance=4), + LayoutRow(4, (1, 1), buffer_advance=2), + )) + # First row: pos 0-3 + self.assertEqual(lm.pos_to_xy(0), (4, 0)) + self.assertEqual(lm.pos_to_xy(3), (7, 0)) + # Second row: pos 4-5 + self.assertEqual(lm.pos_to_xy(4), (4, 1)) + self.assertEqual(lm.pos_to_xy(5), (5, 1)) + + def test_pos_to_xy_past_end_clamps(self): + lm = LayoutMap((LayoutRow(4, (1, 1), buffer_advance=2),)) + self.assertEqual(lm.pos_to_xy(99), (6, 0)) + + def test_pos_to_xy_skips_prompt_only_leading_rows(self): + lm = LayoutMap(( + LayoutRow(0, (), buffer_advance=0), # leading prompt-only row + LayoutRow(4, (1, 1), buffer_advance=3), + )) + # pos 0 should skip the leading row and land on the real row + self.assertEqual(lm.pos_to_xy(0), (4, 1)) + + def test_pos_to_xy_with_suffix(self): + lm = LayoutMap(( + LayoutRow(4, (1, 1), suffix_width=1, buffer_advance=2), + LayoutRow(0, (1,), buffer_advance=2), + )) + # pos=2 fits within first row's char_widths (len=2), cursor at end + self.assertEqual(lm.pos_to_xy(2), (6, 0)) + # pos=3 exceeds first row, lands on second row + self.assertEqual(lm.pos_to_xy(3), (1, 1)) + + def test_xy_to_pos_empty_rows(self): + lm = LayoutMap(()) + self.assertEqual(lm.xy_to_pos(0, 0), 0) + + def test_xy_to_pos_single_row(self): + lm = LayoutMap((LayoutRow(4, (1, 1, 1), buffer_advance=3),)) + self.assertEqual(lm.xy_to_pos(4, 0), 0) + self.assertEqual(lm.xy_to_pos(5, 0), 1) + self.assertEqual(lm.xy_to_pos(6, 0), 2) + self.assertEqual(lm.xy_to_pos(7, 0), 3) + + def test_xy_to_pos_multi_row(self): + lm = LayoutMap(( + LayoutRow(4, (1, 1, 1), buffer_advance=4), + LayoutRow(4, (1, 1), buffer_advance=2), + )) + self.assertEqual(lm.xy_to_pos(4, 0), 0) + self.assertEqual(lm.xy_to_pos(4, 1), 4) + self.assertEqual(lm.xy_to_pos(5, 1), 5) + + def test_xy_to_pos_before_prompt_returns_zero(self): + lm = LayoutMap((LayoutRow(4, (1, 1), buffer_advance=2),)) + self.assertEqual(lm.xy_to_pos(0, 0), 0) + + def test_xy_to_pos_with_zero_width_chars(self): + # Simulates combining characters (zero-width) after a base char + lm = LayoutMap((LayoutRow(4, (1, 0, 1), buffer_advance=3),)) + # x=5 is past the first char; trailing zero-width combining is included + self.assertEqual(lm.xy_to_pos(5, 0), 2) + + def test_xy_to_pos_zero_width_skipped(self): + lm = LayoutMap((LayoutRow(0, (0, 1, 1), buffer_advance=3),)) + # x=0: the zero-width char at index 0 is skipped, pos advances + self.assertEqual(lm.xy_to_pos(0, 0), 1) + + def test_xy_to_pos_zero_width_before_target(self): + # Zero-width char between two normal chars; target x is past it + lm = LayoutMap((LayoutRow(0, (1, 0, 1), buffer_advance=3),)) + # x=2: passes char at x=0 (w=1), skips zero-width at x=1, lands at x=2 + self.assertEqual(lm.xy_to_pos(2, 0), 3) + + def test_xy_to_pos_trailing_all_zero_width(self): + # All remaining chars from cursor position are zero-width + lm = LayoutMap((LayoutRow(0, (1, 0), buffer_advance=2),)) + # x=1: past first char, trailing loop exhausts (all remaining are 0-width) + self.assertEqual(lm.xy_to_pos(1, 0), 2) + + +class TestLayoutContentLines(TestCase): + def test_zero_width_returns_empty(self): + result = layout_content_lines((), 0, 0) + self.assertEqual(result.wrapped_rows, ()) + self.assertEqual(result.layout_map.rows, ()) + + def test_negative_width_returns_empty(self): + result = layout_content_lines((), -1, 0) + self.assertEqual(result.wrapped_rows, ()) + + def test_single_short_line(self): + line = _content_line("abc") + result = layout_content_lines((line,), 80, 0) + + self.assertEqual(len(result.wrapped_rows), 1) + row = result.wrapped_rows[0] + self.assertEqual(row.prompt_text, ">>> ") + self.assertEqual(row.prompt_width, 4) + self.assertEqual(row.suffix, "") + self.assertEqual(row.buffer_advance, 4) # 3 chars + newline + + def test_single_line_no_newline(self): + line = _content_line("ab", has_newline=False) + result = layout_content_lines((line,), 80, 0) + + self.assertEqual(len(result.wrapped_rows), 1) + self.assertEqual(result.wrapped_rows[0].buffer_advance, 2) + + def test_empty_body(self): + source = _source("", has_newline=True) + prompt = _prompt() + line = ContentLine(source, prompt, ()) + result = layout_content_lines((line,), 80, 0) + + self.assertEqual(len(result.wrapped_rows), 1) + self.assertEqual(result.wrapped_rows[0].buffer_advance, 1) # just newline + + def test_line_wraps(self): + # prompt ">>> " is 4 wide, terminal is 10 wide, so 6 chars fit per row + line = _content_line("abcdefgh") # 8 chars, needs 2 rows + result = layout_content_lines((line,), 10, 0) + + self.assertEqual(len(result.wrapped_rows), 2) + first, second = result.wrapped_rows + self.assertEqual(first.prompt_text, ">>> ") + self.assertEqual(first.suffix, "\\") + self.assertEqual(first.suffix_width, 1) + # First row: 10 - 4(prompt) - 1(suffix) = 5 chars + self.assertEqual(first.buffer_advance, 5) + # Second row: continuation with no prompt + self.assertEqual(second.prompt_text, "") + self.assertEqual(second.prompt_width, 0) + self.assertEqual(second.buffer_advance, 4) # remaining 3 + newline + self.assertEqual(second.suffix, "") + + def test_wrapping_forces_progress(self): + # When a single character is wider than available space, force 1 char + prompt = _prompt("P", 1) + body = (ContentFragment("W", 1),) + source = _source("W", has_newline=False) + line = ContentLine(source, prompt, body) + # width=2 means prompt(1) + char(1) = 2, which fits (< width would be + # false for width=2 since 1+1 >= 2), so it wraps but forces progress + result = layout_content_lines((line,), 2, 0) + + self.assertEqual(len(result.wrapped_rows), 1) + self.assertEqual(result.wrapped_rows[0].buffer_advance, 1) + + def test_layout_map_matches_wrapped_rows(self): + line = _content_line("abc") + result = layout_content_lines((line,), 80, 0) + + self.assertEqual(len(result.layout_map.rows), len(result.wrapped_rows)) + self.assertEqual(result.layout_map.rows[0].prompt_width, 4) + self.assertEqual(result.layout_map.rows[0].char_widths, (1, 1, 1)) + + def test_line_end_offsets(self): + line1 = _content_line("ab") + line2 = _content_line("cd") + result = layout_content_lines((line1, line2), 80, 0) + + self.assertEqual(len(result.line_end_offsets), 2) + # line1: 2 chars + 1 newline = offset 3 + self.assertEqual(result.line_end_offsets[0], 3) + # line2: offset 3 + 2 chars + 1 newline = 6 + self.assertEqual(result.line_end_offsets[1], 6) + + def test_start_offset_shifts_offsets(self): + line = _content_line("ab") + result = layout_content_lines((line,), 80, 10) + + self.assertEqual(result.line_end_offsets[0], 13) + + def test_multiple_lines(self): + line1 = _content_line("abc") + line2 = _content_line("de") + result = layout_content_lines((line1, line2), 80, 0) + + self.assertEqual(len(result.wrapped_rows), 2) + self.assertEqual(result.wrapped_rows[0].buffer_advance, 4) # abc + \n + self.assertEqual(result.wrapped_rows[1].buffer_advance, 3) # de + \n + + def test_leading_prompt_lines(self): + leading = (ContentFragment("header", 6),) + prompt = PromptContent(leading, ">>> ", 4) + body = _body_from_text("x") + source = _source("x", has_newline=False) + line = ContentLine(source, prompt, body) + result = layout_content_lines((line,), 80, 0) + + # Leading line + body line + self.assertEqual(len(result.wrapped_rows), 2) + # Leading row has the fragment but no prompt + self.assertEqual(result.wrapped_rows[0].fragments, leading) + # Body row has prompt and content + self.assertEqual(result.wrapped_rows[1].prompt_text, ">>> ") + + def test_wrapped_line_layout_rows_have_suffix(self): + line = _content_line("abcdefgh") + result = layout_content_lines((line,), 10, 0) + + first_layout = result.layout_map.rows[0] + self.assertEqual(first_layout.suffix_width, 1) + second_layout = result.layout_map.rows[1] + self.assertEqual(second_layout.suffix_width, 0) + + def test_pos_to_xy_through_layout(self): + line = _content_line("abc") + result = layout_content_lines((line,), 80, 0) + lm = result.layout_map + + self.assertEqual(lm.pos_to_xy(0), (4, 0)) # after prompt + self.assertEqual(lm.pos_to_xy(1), (5, 0)) + self.assertEqual(lm.pos_to_xy(2), (6, 0)) + self.assertEqual(lm.pos_to_xy(3), (7, 0)) # end of line diff --git a/Lib/test/test_pyrepl/test_pyrepl.py b/Lib/test/test_pyrepl/test_pyrepl.py index 2588a5ba27d776..4240a3c3174959 100644 --- a/Lib/test/test_pyrepl/test_pyrepl.py +++ b/Lib/test/test_pyrepl/test_pyrepl.py @@ -1,17 +1,28 @@ +import contextlib +import importlib import io import itertools import os import pathlib +import pkgutil import re import rlcompleter import select import subprocess import sys import tempfile +from functools import partial from pkgutil import ModuleInfo from unittest import TestCase, skipUnless, skipIf, SkipTest -from unittest.mock import patch -from test.support import force_not_colorized, make_clean_env, Py_DEBUG +from unittest.mock import Mock, patch +import warnings +from test.support import ( + captured_stdout, + captured_stderr, + force_not_colorized, + make_clean_env, + Py_DEBUG, +) from test.support import has_subprocess_support, SHORT_TIMEOUT, STDLIB_DIR from test.support.import_helper import import_module from test.support.os_helper import EnvironmentVarGuard, unlink @@ -25,16 +36,35 @@ multiline_input, code_to_events, ) +from _colorize import ANSIColors, get_theme from _pyrepl.console import Event -from _pyrepl._module_completer import ImportParser, ModuleCompleter -from _pyrepl.readline import (ReadlineAlikeReader, ReadlineConfig, - _ReadlineWrapper) +from _pyrepl.completing_reader import stripcolor +from _pyrepl._module_completer import ( + ImportParser, + ModuleCompleter, + HARDCODED_SUBMODULES, +) +from _pyrepl.fancycompleter import Completer as FancyCompleter, colorize_matches +import _pyrepl.readline as pyrepl_readline +from _pyrepl.readline import ( + ReadlineAlikeReader, + ReadlineConfig, + _ReadlineWrapper, +) from _pyrepl.readline import multiline_input as readline_multiline_input try: import pty except ImportError: pty = None +try: + import readline as readline_module +except ImportError: + readline_module = None +try: + import tkinter +except ImportError: + tkinter = None class ReplTestCase(TestCase): @@ -51,6 +81,7 @@ def run_repl( cwd: str | None = None, skip: bool = False, timeout: float = SHORT_TIMEOUT, + exit_on_output: str | None = None, ) -> tuple[str, int]: temp_dir = None if cwd is None: @@ -64,6 +95,7 @@ def run_repl( cwd=cwd, skip=skip, timeout=timeout, + exit_on_output=exit_on_output, ) finally: if temp_dir is not None: @@ -78,6 +110,7 @@ def _run_repl( cwd: str, skip: bool, timeout: float, + exit_on_output: str | None, ) -> tuple[str, int]: assert pty master_fd, slave_fd = pty.openpty() @@ -123,6 +156,11 @@ def _run_repl( except OSError: break output.append(data) + if exit_on_output is not None: + output = ["".join(output)] + if exit_on_output in output[0]: + process.kill() + break else: os.close(master_fd) process.kill() @@ -735,6 +773,64 @@ def test_history_with_multiline_entries(self): self.assert_screen_equal(reader, expected, clean=True) self.assertEqual(output, expected) + def test_up_arrow_stays_within_recalled_multiline_entry(self): + code = ( + "def fo():\n" + "...\n" + "...\n" + "a = 1\n" + "b = 2\n" + "x = 1\n" + "\n" + "def fo():\n" + "...\n" + "...\n" + "a = 1\n" + "b = 2\n" + "x = 1\n" + "z = 2\n" + "\n" + ) + events = list(itertools.chain( + code_to_events(code), + [ + Event(evt="key", data="up", raw=bytearray(b"\x1bOA")), + Event(evt="key", data="up", raw=bytearray(b"\x1bOA")), + ] + )) + + reader = self.prepare_reader(events) + multiline_input(reader) + multiline_input(reader) + + expected = ( + "def fo():\n" + " ...\n" + " ...\n" + " a = 1\n" + " b = 2\n" + " x = 1\n" + " z = 2" + ) + reader.more_lines = partial(more_lines, namespace=None) + reader.ps1 = reader.ps2 = ">>> " + reader.ps3 = reader.ps4 = "... " + try: + reader.prepare() + reader.refresh() + + reader.handle1() + self.assertEqual(reader.historyi, 1) + self.assertEqual(reader.get_unicode(), expected) + first_cxy = reader.cxy + + reader.handle1() + self.assertEqual(reader.historyi, 1) + self.assertEqual(reader.get_unicode(), expected) + self.assertLess(reader.cxy[1], first_cxy[1]) + finally: + reader.restore() + def test_history_navigation_with_down_arrow(self): events = itertools.chain( @@ -780,12 +876,28 @@ def test_history_search(self): self.assertEqual(output, "1+1") self.assert_screen_equal(reader, "1+1", clean=True) + def test_history_file_embedded_nuls_are_sanitized(self): + reader = self.prepare_reader([]) + wrapper = _ReadlineWrapper(reader=reader, f_in=0, f_out=1) + with tempfile.NamedTemporaryFile("wb", delete=False) as history_file: + history_file.write(b"good\n") + history_file.write(b"ba\0d\n") + history_file.write(b"line1\r\nline2\0\n") + filename = history_file.name + + try: + wrapper.read_history_file(filename) + finally: + unlink(filename) + + self.assertEqual(reader.history, ["good", "bad", "line1\nline2"]) + def test_control_character(self): events = code_to_events("c\x1d\n") reader = self.prepare_reader(events) output = multiline_input(reader) self.assertEqual(output, "c\x1d") - self.assert_screen_equal(reader, "c\x1d", clean=True) + self.assert_screen_equal(reader, "c^]", clean=True) def test_history_search_backward(self): # Test <page up> history search backward with "imp" input @@ -920,9 +1032,117 @@ def test_func(self): self.assertEqual(mock_stderr.getvalue(), "") +class TestPyReplFancyCompleter(TestCase): + def prepare_reader(self, events, namespace, *, use_colors): + console = FakeConsole(events) + config = ReadlineConfig() + config.readline_completer = FancyCompleter( + namespace, use_colors=use_colors + ).complete + reader = ReadlineAlikeReader(console=console, config=config) + return reader + + def test_simple_completion_preserves_callable_postfix(self): + events = code_to_events("os.getpid\t\n") + + namespace = {"os": os} + reader = self.prepare_reader(events, namespace, use_colors=False) + + output = multiline_input(reader, namespace) + self.assertEqual(output, "os.getpid()") + + def test_attribute_menu_tracks_typed_stem(self): + class Obj: + apple = 1 + apricot = 2 + banana = 3 + + namespace = {"obj": Obj} + reader = self.prepare_reader( + code_to_events("obj.\t\ta"), + namespace, + use_colors=True, + ) + + with self.assertRaises(StopIteration): + while True: + reader.handle1() + + self.assertEqual("".join(reader.buffer), "obj.a") + self.assertTrue(reader.cmpltn_menu_visible) + menu = "\n".join(reader.cmpltn_menu) + self.assertIn("apple", menu) + self.assertIn("apricot", menu) + self.assertNotIn("banana", menu) + self.assertNotIn("mro", menu) + + def test_get_completions_sorts_colored_matches_by_visible_text(self): + console = FakeConsole(iter(())) + config = ReadlineConfig() + config.readline_completer = FancyCompleter( + { + "foo_str": "value", + "foo_int": 1, + "foo_none": None, + }, + use_colors=True, + ).complete + reader = ReadlineAlikeReader(console=console, config=config) + + matches, action = reader.get_completions("foo_") + + self.assertIsNone(action) + self.assertEqual( + [stripcolor(match) for match in matches], + ["foo_int", "foo_none", "foo_str"], + ) + + +class TestPyReplReadlineSetup(TestCase): + def test_setup_ignores_basic_completer_env_when_env_is_disabled(self): + class FakeFancyCompleter: + def __init__(self, namespace): + self.namespace = namespace + self.use_colors = Mock() + self.theme = Mock() + + def complete(self, text, state): + return None + + class FakeBasicCompleter(FakeFancyCompleter): + pass + + wrapper = Mock() + wrapper.config = ReadlineConfig() + stdin = Mock() + stdout = Mock() + stdin.fileno.return_value = 0 + stdout.fileno.return_value = 1 + + with ( + patch.object(pyrepl_readline, "_wrapper", wrapper), + patch.object(pyrepl_readline, "raw_input", None), + patch.object(pyrepl_readline, "FancyCompleter", FakeFancyCompleter), + patch.object(pyrepl_readline, "RLCompleter", FakeBasicCompleter), + patch.object(pyrepl_readline.sys, "stdin", stdin), + patch.object(pyrepl_readline.sys, "stdout", stdout), + patch.object(pyrepl_readline.sys, "flags", Mock(ignore_environment=True)), + patch.object(pyrepl_readline.os, "isatty", return_value=True), + patch.object(pyrepl_readline.os, "getenv") as mock_getenv, + patch("builtins.input", lambda prompt="": prompt), + ): + mock_getenv.return_value = "1" + pyrepl_readline._setup({}) + + self.assertIsInstance( + wrapper.config.readline_completer.__self__, + FakeFancyCompleter, + ) + mock_getenv.assert_not_called() + + class TestPyReplModuleCompleter(TestCase): def setUp(self): - import importlib # Make iter_modules() search only the standard library. # This makes the test more reliable in case there are # other user packages/scripts on PYTHONPATH which can @@ -942,7 +1162,9 @@ def prepare_reader(self, events, namespace): reader = ReadlineAlikeReader(console=console, config=config) return reader - def test_import_completions(self): + @patch.dict(sys.modules, + {"importlib.resources": object()}) # don't propose to import it + def test_completions(self): cases = ( ("import path\t\n", "import pathlib"), ("import importlib.\t\tres\t\n", "import importlib.resources"), @@ -956,6 +1178,7 @@ def test_import_completions(self): ("from importlib import mac\t\n", "from importlib import machinery"), ("from importlib import res\t\n", "from importlib import resources"), ("from importlib.res\t import a\t\n", "from importlib.resources import abc"), + ("from __phello__ import s\t\n", "from __phello__ import spam"), # frozen module ) for code, expected in cases: with self.subTest(code=code): @@ -995,7 +1218,7 @@ def test_sub_module_private_completions(self): # Return public methods by default ("from foo import \t\n", "from foo import public"), # Return private methods if explicitly specified - ("from foo import _\t\n", "from foo import _private"), + ("from foo import _p\t\n", "from foo import _private"), ) for code, expected in cases: with self.subTest(code=code): @@ -1005,14 +1228,6 @@ def test_sub_module_private_completions(self): self.assertEqual(output, expected) def test_builtin_completion_top_level(self): - import importlib - # Make iter_modules() search only the standard library. - # This makes the test more reliable in case there are - # other user packages/scripts on PYTHONPATH which can - # intefere with the completions. - lib_path = os.path.dirname(importlib.__path__[0]) - sys.path = [lib_path] - cases = ( ("import bui\t\n", "import builtins"), ("from bui\t\n", "from builtins"), @@ -1024,12 +1239,15 @@ def test_builtin_completion_top_level(self): output = reader.readline() self.assertEqual(output, expected) - def test_relative_import_completions(self): + def test_relative_completions(self): cases = ( (None, "from .readl\t\n", "from .readl"), (None, "from . import readl\t\n", "from . import readl"), ("_pyrepl", "from .readl\t\n", "from .readline"), ("_pyrepl", "from . import readl\t\n", "from . import readline"), + ("_pyrepl", "from .readline import mul\t\n", "from .readline import multiline_input"), + ("_pyrepl", "from .. import toodeep\t\n", "from .. import toodeep"), + ("concurrent", "from .futures.i\t\n", "from .futures.interpreter"), ) for package, code, expected in cases: with self.subTest(code=code): @@ -1059,7 +1277,7 @@ def test_no_fallback_on_regular_completion(self): cases = ( ("import pri\t\n", "import pri"), ("from pri\t\n", "from pri"), - ("from typing import Na\t\n", "from typing import Na"), + ("from typong import Na\t\n", "from typong import Na"), ) for code, expected in cases: with self.subTest(code=code): @@ -1068,6 +1286,244 @@ def test_no_fallback_on_regular_completion(self): output = reader.readline() self.assertEqual(output, expected) + def test_global_cache(self): + with (tempfile.TemporaryDirectory() as _dir1, + patch.object(sys, "path", [_dir1, *sys.path])): + dir1 = pathlib.Path(_dir1) + (dir1 / "mod_aa.py").touch() + (dir1 / "mod_bb.py").touch() + events = code_to_events("import mod_a\t\nimport mod_b\t\n") + reader = self.prepare_reader(events, namespace={}) + output_1, output_2 = reader.readline(), reader.readline() + self.assertEqual(output_1, "import mod_aa") + self.assertEqual(output_2, "import mod_bb") + + def test_hardcoded_stdlib_submodules(self): + cases = ( + ("import collections.\t\n", "import collections.abc"), + ("import os.\t\n", "import os.path"), + ("import math.\t\n", "import math.integer"), + ("import xml.parsers.expat.\t\te\t\n\n", "import xml.parsers.expat.errors"), + ("from xml.parsers.expat import \t\tm\t\n\n", "from xml.parsers.expat import model"), + ) + for code, expected in cases: + with self.subTest(code=code): + events = code_to_events(code) + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertEqual(output, expected) + + def test_hardcoded_stdlib_submodules_not_proposed_if_local_import(self): + with (tempfile.TemporaryDirectory() as _dir, + patch.object(sys, "modules", {})): # hide imported module + dir = pathlib.Path(_dir) + (dir / "collections").mkdir() + (dir / "collections" / "__init__.py").touch() + (dir / "collections" / "foo.py").touch() + with patch.object(sys, "path", [_dir, *sys.path]): + events = code_to_events("import collections.\t\n") + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertEqual(output, "import collections.foo") + + def test_already_imported_stdlib_module_no_other_suggestions(self): + with (tempfile.TemporaryDirectory() as _dir, + patch.object(sys, "path", [_dir, *sys.path])): + dir = pathlib.Path(_dir) + (dir / "collections").mkdir() + (dir / "collections" / "__init__.py").touch() + (dir / "collections" / "foo.py").touch() + + # collections found in dir, but was already imported + # from stdlib at startup -> suggest stdlib submodules only + events = code_to_events("import collections.\t\n") + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertEqual(output, "import collections.abc") + + def test_already_imported_custom_module_no_suggestions(self): + with (tempfile.TemporaryDirectory() as _dir1, + tempfile.TemporaryDirectory() as _dir2, + patch.object(sys, "path", [_dir2, _dir1, *sys.path])): + dir1 = pathlib.Path(_dir1) + (dir1 / "mymodule").mkdir() + (dir1 / "mymodule" / "__init__.py").touch() + (dir1 / "mymodule" / "foo.py").touch() + importlib.import_module("mymodule") + + dir2 = pathlib.Path(_dir2) + (dir2 / "mymodule").mkdir() + (dir2 / "mymodule" / "__init__.py").touch() + (dir2 / "mymodule" / "bar.py").touch() + # Purge FileFinder cache after adding files + pkgutil.get_importer(_dir2).invalidate_caches() + # mymodule found in dir2 before dir1, but it was already imported + # from dir1 -> do not suggest dir2 submodules + events = code_to_events("import mymodule.\t\n") + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertEqual(output, "import mymodule.") + + del sys.modules["mymodule"] + # mymodule not imported anymore -> suggest dir2 submodules + events = code_to_events("import mymodule.\t\n") + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertEqual(output, "import mymodule.bar") + + def test_already_imported_custom_file_no_suggestions(self): + # Same as before, but mymodule from dir1 has no submodules + # -> propose nothing + with (tempfile.TemporaryDirectory() as _dir1, + tempfile.TemporaryDirectory() as _dir2, + patch.object(sys, "path", [_dir2, _dir1, *sys.path])): + dir1 = pathlib.Path(_dir1) + (dir1 / "mymodule").mkdir() + (dir1 / "mymodule.py").touch() + importlib.import_module("mymodule") + + dir2 = pathlib.Path(_dir2) + (dir2 / "mymodule").mkdir() + (dir2 / "mymodule" / "__init__.py").touch() + (dir2 / "mymodule" / "bar.py").touch() + events = code_to_events("import mymodule.\t\n") + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertEqual(output, "import mymodule.") + del sys.modules["mymodule"] + + def test_already_imported_module_without_origin_or_spec(self): + with (tempfile.TemporaryDirectory() as _dir1, + patch.object(sys, "path", [_dir1, *sys.path])): + dir1 = pathlib.Path(_dir1) + for mod in ("no_origin", "not_has_location", "no_spec"): + (dir1 / mod).mkdir() + (dir1 / mod / "__init__.py").touch() + (dir1 / mod / "foo.py").touch() + pkgutil.get_importer(_dir1).invalidate_caches() + module = importlib.import_module(mod) + assert module.__spec__ + if mod == "no_origin": + module.__spec__.origin = None + elif mod == "not_has_location": + module.__spec__.has_location = False + else: + module.__spec__ = None + events = code_to_events(f"import {mod}.\t\n") + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertEqual(output, f"import {mod}.") + del sys.modules[mod] + + @patch.dict(sys.modules) + def test_attribute_completion(self): + with tempfile.TemporaryDirectory() as _dir: + dir = pathlib.Path(_dir) + (dir / "foo.py").write_text("bar = 42") + (dir / "bar.py").write_text("baz = 42") + (dir / "pack").mkdir() + (dir / "pack" / "__init__.py").write_text("attr = 42") + (dir / "pack" / "foo.py").touch() + (dir / "pack" / "bar.py").touch() + (dir / "pack" / "baz.py").touch() + sys.modules.pop("graphlib", None) # test modules may have been imported by previous tests + sys.modules.pop("antigravity", None) + sys.modules.pop("unittest.__main__", None) + with patch.object(sys, "path", [_dir, *sys.path]): + pkgutil.get_importer(_dir).invalidate_caches() + importlib.import_module("bar") + cases = ( + # needs 2 tabs to import (show prompt, then import) + ("from foo import \t\n", "from foo import ", set()), + ("from foo import \t\t\n", "from foo import bar", {"foo"}), + ("from foo import ba\t\n", "from foo import ba", set()), + ("from foo import ba\t\t\n", "from foo import bar", {"foo"}), + # reset if a character is inserted between tabs + ("from foo import \tb\ta\t\n", "from foo import ba", set()), + # packages: needs 3 tabs ([ not unique ], prompt, import) + ("from pack import \t\t\n", "from pack import ", set()), + ("from pack import \t\t\t\n", "from pack import ", {"pack"}), + ("from pack import \t\t\ta\t\n", "from pack import attr", {"pack"}), + # one match: needs 2 tabs (insert + show prompt, import) + ("from pack import f\t\n", "from pack import foo", set()), + ("from pack import f\t\t\n", "from pack import foo", {"pack"}), + # common prefix: needs 3 tabs (insert + [ not unique ], prompt, import) + ("from pack import b\t\n", "from pack import ba", set()), + ("from pack import b\t\t\n", "from pack import ba", set()), + ("from pack import b\t\t\t\n", "from pack import ba", {"pack"}), + # module already imported + ("from bar import b\t\n", "from bar import baz", set()), + # stdlib modules are automatically imported + ("from graphlib import T\t\n", "from graphlib import TopologicalSorter", {"graphlib"}), + # except those with known side-effects + ("from antigravity import g\t\n", "from antigravity import g", set()), + ("from unittest.__main__ import \t\n", "from unittest.__main__ import ", set()), + ) + for code, expected, expected_imports in cases: + with self.subTest(code=code), patch.dict(sys.modules): + _imported = set(sys.modules.keys()) + events = code_to_events(code) + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertEqual(output, expected) + new_imports = sys.modules.keys() - _imported + self.assertEqual(new_imports, expected_imports) + + @patch.dict(sys.modules) + def test_attribute_completion_error_on_import(self): + with tempfile.TemporaryDirectory() as _dir: + dir = pathlib.Path(_dir) + (dir / "foo.py").write_text("bar = 42") + (dir / "boom.py").write_text("1 <> 2") + with patch.object(sys, "path", [_dir, *sys.path]): + cases = ( + ("from boom import \t\t\n", "from boom import "), + ("from foo import \t\t\n", "from foo import bar"), # still working + ) + for code, expected in cases: + with self.subTest(code=code): + events = code_to_events(code) + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertEqual(output, expected) + self.assertNotIn("boom", sys.modules) + + @patch.dict(sys.modules) + def test_attribute_completion_error_on_attributes_access(self): + with tempfile.TemporaryDirectory() as _dir: + dir = pathlib.Path(_dir) + (dir / "boom").mkdir() + (dir / "boom"/"__init__.py").write_text("def __dir__(): raise ValueError()") + (dir / "boom"/"submodule.py").touch() + with patch.object(sys, "path", [_dir, *sys.path]): + events = code_to_events("from boom import \t\t\n") # trigger import + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertIn("boom", sys.modules) + # ignore attributes, just propose submodule + self.assertEqual(output, "from boom import submodule") + + @patch.dict(sys.modules) + def test_attribute_completion_private_and_invalid_names(self): + with tempfile.TemporaryDirectory() as _dir: + dir = pathlib.Path(_dir) + (dir / "foo.py").write_text("_secret = 'bar'") + with patch.object(sys, "path", [_dir, *sys.path]): + mod = importlib.import_module("foo") + mod.__dict__["invalid-identifier"] = "baz" + cases = ( + ("from foo import \t\n", "from foo import "), + ("from foo import _s\t\n", "from foo import _secret"), + ("from foo import inv\t\n", "from foo import inv"), + ) + for code, expected in cases: + with self.subTest(code=code): + events = code_to_events(code) + reader = self.prepare_reader(events, namespace={}) + output = reader.readline() + self.assertEqual(output, expected) + + def test_get_path_and_prefix(self): cases = ( ('', ('', '')), @@ -1170,6 +1626,7 @@ def test_parse_error(self): 'import ..foo', 'import .foo.bar', 'import foo; x = 1', + 'import foo; 1,', 'import a.; x = 1', 'import a.b; x = 1', 'import a.b.; x = 1', @@ -1189,6 +1646,8 @@ def test_parse_error(self): 'from foo import import', 'from foo import from', 'from foo import as', + 'from \\x', # _tokenize SyntaxError -> tokenize TokenError + 'if 1:\n pass\n\tpass', # _tokenize TabError -> tokenize TabError ) for code in cases: parser = ImportParser(code) @@ -1196,6 +1655,174 @@ def test_parse_error(self): with self.subTest(code=code): self.assertEqual(actual, None) + @patch.dict(sys.modules) + def test_suggestions_and_messages(self) -> None: + # more unitary tests checking the exact suggestions provided + # (sorting, de-duplication, import action...) + _prompt = ("[ module not imported, press again to import it " + "and propose attributes ]") + _error = "[ error during import: division by zero ]" + with tempfile.TemporaryDirectory() as _dir: + dir = pathlib.Path(_dir) + (dir / "foo.py").write_text("bar = 42") + (dir / "boom.py").write_text("1/0") + (dir / "pack").mkdir() + (dir / "pack" / "__init__.py").write_text("foo = 1; bar = 2;") + (dir / "pack" / "bar.py").touch() + sys.modules.pop("graphlib", None) # test modules may have been imported by previous tests + sys.modules.pop("string.templatelib", None) + with patch.object(sys, "path", [_dir, *sys.path]): + pkgutil.get_importer(_dir).invalidate_caches() + # NOTE: Cases are intentionally sequential and share completer + # state. Earlier cases may import modules that later cases + # depend on. Do NOT reorder without understanding dependencies. + cases = ( + # no match != not an import + ("import nope", ([], None), set()), + ("improt nope", None, set()), + # names sorting + ("import col", (["collections", "colorsys"], None), set()), + # module auto-import + ("import fo", (["foo"], None), set()), + ("from foo import ", ([], (_prompt, None)), {"foo"}), + ("from foo import ", (["bar"], None), set()), # now imported + ("from foo import ba", (["bar"], None), set()), + # error during import + ("from boom import ", ([], (_prompt, _error)), set()), + ("from boom import ", ([], None), set()), # do not retry + # packages + ("from collections import a", (["abc"], None), set()), + ("from pack import ", (["bar"], (_prompt, None)), {"pack"}), + ("from pack import ", (["bar", "foo"], None), set()), + ("from pack.bar import ", ([], (_prompt, None)), {"pack.bar"}), + ("from pack.bar import ", ([], None), set()), + # stdlib = auto-imported + ("from graphlib import T", (["TopologicalSorter"], None), {"graphlib"}), + ("from string.templatelib import c", (["convert"], None), {"string.templatelib"}), + ) + completer = ModuleCompleter() + for i, (code, expected, expected_imports) in enumerate(cases): + with self.subTest(code=code, i=i): + _imported = set(sys.modules.keys()) + result = completer.get_completions(code) + self.assertEqual(result is None, expected is None) + if result: + compl, _values, act = result + self.assertEqual(compl, expected[0]) + self.assertEqual(act is None, expected[1] is None) + if act: + msg, func = act + self.assertEqual(msg, expected[1][0]) + act_result = func() + self.assertEqual(act_result, expected[1][1]) + + new_imports = sys.modules.keys() - _imported + self.assertSetEqual(new_imports, expected_imports) + + def test_colorize_import_completions(self) -> None: + theme = get_theme() + type_color = theme.fancycompleter.type + module_color = theme.fancycompleter.module + R = ANSIColors.RESET + + colorize = lambda names, values: colorize_matches(names, values, theme) + config = ReadlineConfig(colorize_completions=colorize) + reader = ReadlineAlikeReader( + console=FakeConsole(events=[]), + config=config, + ) + + # "from collections import de" -> defaultdict (type) and deque (type) + reader.buffer = list("from collections import de") + reader.pos = len(reader.buffer) + names, action = reader.get_module_completions() + self.assertEqual(names, [ + f"{type_color}defaultdict{R}", + f"{type_color}deque{R}", + ]) + self.assertIsNone(action) + + # "from importlib.m" has submodule completions colored as modules + reader.buffer = list("from importlib.m") + reader.pos = len(reader.buffer) + names, action = reader.get_module_completions() + self.assertEqual(names, [ + f"{module_color}importlib.machinery{R}", + f"{module_color}importlib.metadata{R}", + ]) + self.assertIsNone(action) + + # Make sure attributes take precedence over submodules when both exist + # Here we're using `unittest.main` which happens to be both a module and an attribute + reader.buffer = list("from unittest import m") + reader.pos = len(reader.buffer) + names, action = reader.get_module_completions() + self.assertEqual(names, [ + f"{type_color}main{R}", # Ensure that `main` is colored as an attribute (class in this case) + f"{module_color}mock{R}", + ]) + self.assertIsNone(action) + + +# Audit hook used to check for stdlib modules import side-effects +# Defined globally to avoid adding one hook per test run (refleak) +_audit_events: set[str] | None = None + + +def _hook(name: str, _args: tuple): + if _audit_events is not None: # No-op when not activated + _audit_events.add(name) +sys.addaudithook(_hook) + + +@contextlib.contextmanager +def _capture_audit_events(): + global _audit_events + _audit_events = set() + try: + yield _audit_events + finally: + _audit_events = None + + +class TestModuleCompleterAutomaticImports(TestCase): + def test_no_side_effects(self): + from test.test___all__ import AllTest # TODO: extract to a helper? + + completer = ModuleCompleter() + for _, modname in AllTest().walk_modules(completer._stdlib_path, ""): + with self.subTest(modname=modname): + with (captured_stdout() as out, + captured_stderr() as err, + _capture_audit_events() as audit_events, + (patch("tkinter._tkinter.create") if tkinter + else contextlib.nullcontext()) as tk_mock, + warnings.catch_warnings(action="ignore")): + completer._maybe_import_module(modname) + # Test no module is imported that + # 1. prints any text + self.assertEqual(out.getvalue(), "") + self.assertEqual(err.getvalue(), "") + # 2. spawn any subprocess (eg. webbrowser.open) + self.assertNotIn("subprocess.Popen", audit_events) + # 3. launch a Tk window + if tk_mock is not None: + tk_mock.assert_not_called() + + +class TestHardcodedSubmodules(TestCase): + @patch.dict(sys.modules) + def test_hardcoded_stdlib_submodules_are_importable(self): + for parent_path, submodules in HARDCODED_SUBMODULES.items(): + for module_name in submodules: + path = f"{parent_path}.{module_name}" + with self.subTest(path=path): + # We can't use importlib.util.find_spec here, + # since some hardcoded submodules parents are + # not proper packages + importlib.import_module(path) + + class TestPasteEvent(TestCase): def prepare_reader(self, events): console = FakeConsole(events) @@ -1360,6 +1987,9 @@ class TestDumbTerminal(ReplTestCase): def test_dumb_terminal_exits_cleanly(self): env = os.environ.copy() env.pop('PYTHON_BASIC_REPL', None) + # Ignore PYTHONSTARTUP to not pollute the output + # with an unrelated traceback. See GH-137568. + env.pop('PYTHONSTARTUP', None) env.update({"TERM": "dumb"}) output, exit_code = self.run_repl("exit()\n", env=env) self.assertEqual(exit_code, 0) @@ -1394,10 +2024,10 @@ def test_exposed_globals_in_repl(self): case2 = f"{pre}, '__doc__', '__file__', {post}" in output # if `__main__` is a cached .pyc file and the .py source exists - case3 = f"{pre}, '__cached__', '__doc__', '__file__', {post}" in output + case3 = f"{pre}, '__doc__', '__file__', {post}" in output # if `__main__` is a cached .pyc file but there's no .py source file - case4 = f"{pre}, '__cached__', '__doc__', {post}" in output + case4 = f"{pre}, '__doc__', {post}" in output self.assertTrue(case1 or case2 or case3 or case4, output) @@ -1718,18 +2348,24 @@ def test_history_survive_crash(self): commands = "1\n2\n3\nexit()\n" output, exit_code = self.run_repl(commands, env=env, skip=True) + self.assertEqual(exit_code, 0) - commands = "spam\nimport time\ntime.sleep(1000)\nquit\n" - try: - self.run_repl(commands, env=env, timeout=3) - except AssertionError: - pass + # Run until "0xcafe" is printed (as "51966") and then kill the + # process to simulate a crash. Note that the output also includes + # the echoed input commands. + commands = "spam\nimport time\n0xcafe\ntime.sleep(1000)\nquit\n" + output, exit_code = self.run_repl(commands, env=env, + exit_on_output="51966") + self.assertNotEqual(exit_code, 0) history = pathlib.Path(hfile.name).read_text() self.assertIn("2", history) self.assertIn("exit()", history) self.assertIn("spam", history) self.assertIn("import time", history) + # History is written after each command's output is printed to the + # console, so depending on how quickly the process is killed, + # the last command may or may not be written to the history file. self.assertNotIn("sleep", history) self.assertNotIn("quit", history) @@ -1773,3 +2409,223 @@ def test_detect_pip_usage_in_repl(self): " outside of the Python REPL" ) self.assertIn(hint, output) + + @force_not_colorized + def test_no_newline(self): + env = os.environ.copy() + env.pop("PYTHON_BASIC_REPL", "") + env["PYTHON_BASIC_REPL"] = "1" + + commands = "print('Something pretty long', end='')\nexit()\n" + expected_output_sequence = "Something pretty long>>> exit()" + + # gh-143394: The basic REPL needs the readline module to turn off + # ECHO terminal attribute. + if readline_module is not None: + basic_output, basic_exit_code = self.run_repl(commands, env=env) + self.assertEqual(basic_exit_code, 0) + self.assertIn(expected_output_sequence, basic_output) + + output, exit_code = self.run_repl(commands) + self.assertEqual(exit_code, 0) + + # Build patterns for escape sequences that don't affect cursor position + # or visual output. Use terminfo to get platform-specific sequences, + # falling back to hard-coded patterns for capabilities not in terminfo. + from _pyrepl.terminfo import TermInfo + ti = TermInfo(os.environ.get("TERM", "")) + + safe_patterns = [] + + # smkx/rmkx - application cursor keys and keypad mode + smkx = ti.get("smkx") + rmkx = ti.get("rmkx") + if smkx: + safe_patterns.append(re.escape(smkx.decode("ascii"))) + if rmkx: + safe_patterns.append(re.escape(rmkx.decode("ascii"))) + if not smkx and not rmkx: + safe_patterns.append(r'\x1b\[\?1[hl]') # application cursor keys + safe_patterns.append(r'\x1b[=>]') # application keypad mode + + # ich1 - insert character (only safe form that inserts exactly 1 char) + ich1 = ti.get("ich1") + if ich1: + safe_patterns.append(re.escape(ich1.decode("ascii")) + r'(?=[ -~])') + else: + safe_patterns.append(r'\x1b\[(?:1)?@(?=[ -~])') + + # civis/cnorm - cursor visibility (may include cursor blinking control) + civis = ti.get("civis") + cnorm = ti.get("cnorm") + if civis: + safe_patterns.append(re.escape(civis.decode("ascii"))) + if cnorm: + safe_patterns.append(re.escape(cnorm.decode("ascii"))) + if not civis and not cnorm: + safe_patterns.append(r'\x1b\[\?25[hl]') # cursor visibility + safe_patterns.append(r'\x1b\[\?12[hl]') # cursor blinking + + # rmam / smam - automatic margins + rmam = ti.get("rmam") + smam = ti.get("smam") + if rmam: + safe_patterns.append(re.escape(rmam.decode("ascii"))) + if smam: + safe_patterns.append(re.escape(smam.decode("ascii"))) + if not rmam and not smam: + safe_patterns.append(r'\x1b\[\?7l') # turn off automatic margins + safe_patterns.append(r'\x1b\[\?7h') # turn on automatic margins + + # Modern extensions not in standard terminfo - always use patterns + safe_patterns.append(r'\x1b\[\?2004[hl]') # bracketed paste mode + safe_patterns.append(r'\x1b\[\?12[hl]') # cursor blinking (may be separate) + safe_patterns.append(r'\x1b\[\?[01]c') # device attributes + + safe_escapes = re.compile('|'.join(safe_patterns)) + cleaned_output = safe_escapes.sub('', output) + self.assertIn(expected_output_sequence, cleaned_output) + + +@skipUnless(sys.platform == "darwin", "macOS only") +class TestMainAppleTerminal(TestMain): + """Test the REPL with Apple Terminal's TERM_PROGRAM set.""" + + def run_repl(self, repl_input, env=None, **kwargs): + if env is None: + env = os.environ.copy() + env["TERM_PROGRAM"] = "Apple_Terminal" + return super().run_repl(repl_input, env=env, **kwargs) + + +class TestPyReplCtrlD(TestCase): + """Test Ctrl+D behavior in _pyrepl to match old pre-3.13 REPL behavior. + + Ctrl+D should: + - Exit on empty buffer (raises EOFError) + - Delete character when cursor is in middle of line + - Perform no operation when cursor is at end of line without newline + - Exit multiline mode when cursor is at end with trailing newline + - Run code up to that point when pressed on blank line with preceding lines + """ + def prepare_reader(self, events): + console = FakeConsole(events) + config = ReadlineConfig(readline_completer=None) + reader = ReadlineAlikeReader(console=console, config=config) + return reader + + def test_ctrl_d_empty_line(self): + """Test that pressing Ctrl+D on empty line exits the program""" + events = [ + Event(evt="key", data="\x04", raw=bytearray(b"\x04")), # Ctrl+D + ] + reader = self.prepare_reader(events) + with self.assertRaises(EOFError): + multiline_input(reader) + + def test_ctrl_d_multiline_with_new_line(self): + """Test that pressing Ctrl+D in multiline mode with trailing newline exits multiline mode""" + events = itertools.chain( + code_to_events("def f():\n pass\n"), # Enter multiline mode with trailing newline + [ + Event(evt="key", data="\x04", raw=bytearray(b"\x04")), # Ctrl+D + ], + ) + reader, _ = handle_all_events(events) + self.assertTrue(reader.finished) + self.assertEqual("def f():\n pass\n", "".join(reader.buffer)) + + def test_ctrl_d_multiline_middle_of_line(self): + """Test that pressing Ctrl+D in multiline mode with cursor in middle deletes character""" + events = itertools.chain( + code_to_events("def f():\n hello world"), # Enter multiline mode + [ + Event(evt="key", data="left", raw=bytearray(b"\x1bOD")) + ] * 5, # move cursor to 'w' in "world" + [ + Event(evt="key", data="\x04", raw=bytearray(b"\x04")) + ], # Ctrl+D should delete 'w' + ) + reader, _ = handle_all_events(events) + self.assertFalse(reader.finished) + self.assertEqual("def f():\n hello orld", "".join(reader.buffer)) + + def test_ctrl_d_multiline_end_of_line_no_newline(self): + """Test that pressing Ctrl+D at end of line without newline performs no operation""" + events = itertools.chain( + code_to_events("def f():\n hello"), # Enter multiline mode, no trailing newline + [ + Event(evt="key", data="\x04", raw=bytearray(b"\x04")) + ], # Ctrl+D should be no-op + ) + reader, _ = handle_all_events(events) + self.assertFalse(reader.finished) + self.assertEqual("def f():\n hello", "".join(reader.buffer)) + + def test_ctrl_d_single_line_middle_of_line(self): + """Test that pressing Ctrl+D in single line mode deletes current character""" + events = itertools.chain( + code_to_events("hello"), + [Event(evt="key", data="left", raw=bytearray(b"\x1bOD"))], # move left + [Event(evt="key", data="\x04", raw=bytearray(b"\x04"))], # Ctrl+D + ) + reader, _ = handle_all_events(events) + self.assertEqual("hell", "".join(reader.buffer)) + + def test_ctrl_d_single_line_end_no_newline(self): + """Test that pressing Ctrl+D at end of single line without newline does nothing""" + events = itertools.chain( + code_to_events("hello"), # cursor at end of line + [Event(evt="key", data="\x04", raw=bytearray(b"\x04"))], # Ctrl+D + ) + reader, _ = handle_all_events(events) + self.assertEqual("hello", "".join(reader.buffer)) + + +@skipUnless(sys.platform == "win32", "windows console only") +class TestWindowsConsoleEolWrap(TestCase): + def _make_mock_console(self, width=80): + from _pyrepl import windows_console as wc + from _pyrepl.render import RenderedScreen + + console = object.__new__(wc.WindowsConsole) + + console.width = width + console.posxy = (0, 0) + console._rendered_screen = RenderedScreen.from_screen_lines([""], (0, 0)) + + console._hide_cursor = Mock() + console._show_cursor = Mock() + console._erase_to_end = Mock() + console._move_relative = Mock() + console.move_cursor = Mock() + console._WindowsConsole__write = Mock() + + return console, wc + + def _apply_changed_line(self, console, wc, y, old_line, new_line, px=0): + from _pyrepl.render import RenderLine + + old_render = RenderLine.from_rendered_text(old_line) + new_render = RenderLine.from_rendered_text(new_line) + update = wc.WindowsConsole._WindowsConsole__plan_changed_line( + console, y, old_render, new_render, px + ) + if update is not None: + wc.WindowsConsole._WindowsConsole__apply_line_update( + console, update + ) + + def test_short_line_sets_posxy_normally(self): + width = 10 + y = 3 + console, wc = self._make_mock_console(width=width) + self._apply_changed_line(console, wc, y, "", "a" * 3) + self.assertEqual(console.posxy, (3, y)) + + def test_exact_width_line_does_not_wrap(self): + width = 10 + y = 3 + console, wc = self._make_mock_console(width=width) + self._apply_changed_line(console, wc, y, "", "a" * width) + self.assertEqual(console.posxy, (width - 1, y)) diff --git a/Lib/test/test_pyrepl/test_reader.py b/Lib/test/test_pyrepl/test_reader.py index 9a02dff7387563..0b32ead357c2c0 100644 --- a/Lib/test/test_pyrepl/test_reader.py +++ b/Lib/test/test_pyrepl/test_reader.py @@ -10,6 +10,8 @@ from .support import ScreenEqualMixin, code_to_events from .support import prepare_reader, prepare_console from _pyrepl.console import Event +from _pyrepl.layout import LayoutMap +from _pyrepl.readline import ReadlineAlikeReader, ReadlineConfig from _pyrepl.reader import Reader from _colorize import default_theme @@ -18,8 +20,48 @@ colors = {overrides.get(k, k[0].lower()): v for k, v in default_theme.syntax.items()} +def prepare_reader_multiline_prompt(*args, **kwargs): + reader = prepare_reader(*args, **kwargs) + del reader.get_prompt + reader.ps1 = "Python 3.15\n>>> " + reader.ps2 = "Python 3.15\n>>> " + reader.ps3 = "Python 3.15\n... " + reader.ps4 = "Python 3.15\n... " + reader.can_colorize = False + reader.paste_mode = False + return reader + + @force_not_colorized_test_class class TestReader(ScreenEqualMixin, TestCase): + def assert_multiline_prompt_screen(self, code, expected_screen, expected_cxy): + reader, _ = handle_all_events( + code_to_events(code), + prepare_reader=prepare_reader_multiline_prompt, + ) + + self.assertEqual(reader.screen, expected_screen) + self.assertEqual(reader.cxy, expected_cxy) + + def test_multiline_prompt_does_not_duplicate_leading_lines(self): + self.assert_multiline_prompt_screen( + "abc", + ["Python 3.15", ">>> abc"], + (7, 1), + ) + + def test_multiline_prompt_does_not_duplicate_leading_lines_across_buffer_lines(self): + self.assert_multiline_prompt_screen( + "if x:\n y", + [ + "Python 3.15", + ">>> if x:", + "Python 3.15", + "... y", + ], + (13, 3), + ) + def test_calc_screen_wrap_simple(self): events = code_to_events(10 * "a") reader, _ = handle_events_narrow_console(events) @@ -102,6 +144,22 @@ def test_calc_screen_backspace(self): reader, _ = handle_all_events(events) self.assert_screen_equal(reader, "aa") + def test_refresh_escapes_control_bytes_in_buffer(self): + console = prepare_console(()) + config = ReadlineConfig(readline_completer=None) + reader = ReadlineAlikeReader(console=console, config=config) + reader.can_colorize = False + reader.ps1 = reader.ps2 = ">>> " + reader.ps3 = reader.ps4 = "... " + reader.buffer = ["\x00", "\x1b"] + reader.pos = len(reader.buffer) + reader.invalidate_full() + + reader.refresh() + + self.assert_screen_equal(reader, ">>> ^@^[") + self.assertEqual(reader.cxy, (8, 0)) + def test_calc_screen_wrap_removes_after_backspace(self): events = itertools.chain( code_to_events(10 * "a"), @@ -176,7 +234,7 @@ def test_up_arrow_after_ctrl_r(self): ) reader, _ = handle_all_events(events) - self.assert_screen_equal(reader, "") + self.assertIn(reader.screen, ([], [""])) def test_newline_within_block_trailing_whitespace(self): # fmt: off @@ -228,6 +286,7 @@ def _prepare_console(events): console.get_event.side_effect = events console.height = 100 console.width = 80 + console.getheightwidth = MagicMock(side_effect=lambda: (console.height, console.width)) console.input_hook = input_hook return console @@ -300,6 +359,21 @@ def test_prompt_length(self): self.assertEqual(prompt, "\033[0;32m樂>\033[0m> ") self.assertEqual(l, 5) + def test_prepare_with_zero_width_does_not_crash(self): + console = prepare_console([], width=0) + reader = ReadlineAlikeReader(console=console, config=ReadlineConfig()) + reader.ps1 = ">>> " + reader.ps2 = ">>> " + reader.ps3 = "... " + reader.ps4 = "" + reader.can_colorize = False + reader.paste_mode = False + + reader.prepare() + + self.assertEqual(reader.cxy, (0, 0)) + self.assertEqual(reader.screen, []) + def test_completions_updated_on_key_press(self): namespace = {"itertools": itertools} code = "itertools." @@ -346,8 +420,7 @@ def test_key_press_on_tab_press_once(self): def test_pos2xy_with_no_columns(self): console = prepare_console([]) reader = prepare_reader(console) - # Simulate a resize to 0 columns - reader.screeninfo = [] + reader.layout = LayoutMap(()) self.assertEqual(reader.pos2xy(), (0, 0)) def test_setpos_from_xy_for_non_printing_char(self): @@ -378,6 +451,7 @@ def funct(case: str = sys.platform) -> None: case "ios" | "android": print("on the phone") case _: print('arms around', match.group(1)) + type type = type[type] """ ) expected = dedent( @@ -397,6 +471,7 @@ def funct(case: str = sys.platform) -> None: {K}case{z} {s}"ios"{z} {o}|{z} {s}"android"{z}{o}:{z} {b}print{z}{o}({z}{s}"on the phone"{z}{o}){z} {K}case{z} {K}_{z}{o}:{z} {b}print{z}{o}({z}{s}'arms around'{z}{o},{z} match{o}.{z}group{o}({z}{n}1{z}{o}){z}{o}){z} + {K}type{z} {b}type{z} {o}={z} {b}type{z}{o}[{z}{b}type{z}{o}]{z} """ ) expected_sync = expected.format(a="", **colors) @@ -404,14 +479,14 @@ def funct(case: str = sys.platform) -> None: reader, _ = handle_all_events(events) self.assert_screen_equal(reader, code, clean=True) self.assert_screen_equal(reader, expected_sync) - self.assertEqual(reader.pos, 396) - self.assertEqual(reader.cxy, (0, 15)) + self.assertEqual(reader.pos, 419) + self.assertEqual(reader.cxy, (0, 16)) async_msg = "{k}async{z} ".format(**colors) expected_async = expected.format(a=async_msg, **colors) more_events = itertools.chain( code_to_events(code), - [Event(evt="key", data="up", raw=bytearray(b"\x1bOA"))] * 14, + [Event(evt="key", data="up", raw=bytearray(b"\x1bOA"))] * 15, code_to_events("async "), ) reader, _ = handle_all_events(more_events) diff --git a/Lib/test/test_pyrepl/test_render.py b/Lib/test/test_pyrepl/test_render.py new file mode 100644 index 00000000000000..5479f2eb4bae39 --- /dev/null +++ b/Lib/test/test_pyrepl/test_render.py @@ -0,0 +1,311 @@ +from unittest import TestCase +from _pyrepl.render import ( + LineUpdate, + RenderCell, + RenderLine, + RenderedScreen, + ScreenOverlay, + StyleRef, + diff_render_lines, + render_cells, + requires_cursor_resync, +) + + +class TestRenderLine(TestCase): + def test_from_rendered_text_parses_style_state(self): + line = RenderLine.from_rendered_text("\x1b[31ma\x1b[0mb") + + self.assertEqual(line.width, 2) + self.assertEqual([cell.text for cell in line.cells], ["a", "b"]) + self.assertEqual([cell.style.sgr for cell in line.cells], ["\x1b[31m", ""]) + self.assertEqual([cell.controls for cell in line.cells], [(), ()]) + + def test_from_rendered_text_round_trips_trailing_reset(self): + line = RenderLine.from_rendered_text("\x1b[31ma\x1b[0m") + + self.assertEqual([cell.text for cell in line.cells], ["a"]) + self.assertEqual([cell.style.sgr for cell in line.cells], ["\x1b[31m"]) + self.assertEqual(line.text, "\x1b[31ma\x1b[0m") + + def test_from_parts_accepts_style_refs(self): + line = RenderLine.from_parts( + ["d", "e", "f", " "], + [1, 1, 1, 1], + [StyleRef.from_sgr("\x1b[1;34m")] * 3 + [None], + ) + + self.assertEqual([cell.text for cell in line.cells], ["d", "e", "f", " "]) + self.assertEqual( + [cell.style.sgr for cell in line.cells], + ["\x1b[1;34m", "\x1b[1;34m", "\x1b[1;34m", ""], + ) + self.assertEqual(line.text, "\x1b[1;34mdef\x1b[0m ") + + def test_from_parts_without_styles(self): + line = RenderLine.from_parts(["a", "b"], [1, 1]) + + self.assertEqual(line.text, "ab") + self.assertEqual(line.width, 2) + + def test_from_rendered_text_empty_string(self): + line = RenderLine.from_rendered_text("") + + self.assertEqual(line.cells, ()) + self.assertEqual(line.text, "") + self.assertEqual(line.width, 0) + + def test_from_rendered_text_with_non_sgr_controls(self): + # \x1b[H is a cursor-home control (not SGR since it doesn't end with 'm') + line = RenderLine.from_rendered_text("\x1b[Hx") + + self.assertEqual(len(line.cells), 2) + self.assertEqual(line.cells[0].controls, ("\x1b[H",)) + self.assertEqual(line.cells[0].text, "") + self.assertEqual(line.cells[1].text, "x") + + def test_from_rendered_text_trailing_non_sgr_control(self): + line = RenderLine.from_rendered_text("a\x1b[K") + + self.assertEqual(len(line.cells), 2) + self.assertEqual(line.cells[0].text, "a") + self.assertEqual(line.cells[1].controls, ("\x1b[K",)) + self.assertEqual(line.cells[1].text, "") + + def test_from_rendered_text_non_sgr_before_text(self): + # Non-SGR control immediately before text should produce a control cell + # then text cells. + line = RenderLine.from_rendered_text("\x1b[Kab") + + texts = [c.text for c in line.cells] + self.assertEqual(texts, ["", "a", "b"]) + self.assertEqual(line.cells[0].controls, ("\x1b[K",)) + + +class TestLineDiff(TestCase): + def test_diff_render_lines_ignores_unchanged_ansi_prefix(self): + old = RenderLine.from_rendered_text("\x1b[31ma\x1b[0mb") + new = RenderLine.from_rendered_text("\x1b[31ma\x1b[0mc") + + diff = diff_render_lines(old, new) + + self.assertIsNotNone(diff) + assert diff is not None + self.assertEqual(diff.start_x, 1) + self.assertEqual(diff.old_text, "b") + self.assertEqual(diff.new_text, "c") + + def test_diff_render_lines_detects_single_cell_insertion(self): + old = RenderLine.from_rendered_text("ab") + new = RenderLine.from_rendered_text("acb") + + diff = diff_render_lines(old, new) + + self.assertIsNotNone(diff) + assert diff is not None + self.assertEqual(diff.start_x, 1) + self.assertEqual(diff.old_text, "") + self.assertEqual(diff.new_text, "c") + + def test_colored_append_only_emits_new_character_and_reset(self): + old = RenderLine.from_rendered_text("\x1b[1mabc\x1b[0m") + new = RenderLine.from_rendered_text("\x1b[1mabcd\x1b[0m") + + diff = diff_render_lines(old, new) + + self.assertIsNotNone(diff) + assert diff is not None + self.assertEqual(diff.start_x, 3) + self.assertEqual(render_cells(diff.new_cells), "\x1b[1md\x1b[0m") + + def test_keyword_space_inserts_only_space_after_reset(self): + old = RenderLine.from_parts( + ["d", "e", "f"], + [1, 1, 1], + ["keyword", "keyword", "keyword"], + ) + new = RenderLine.from_parts( + ["d", "e", "f", " "], + [1, 1, 1, 1], + ["keyword", "keyword", "keyword", None], + ) + + diff = diff_render_lines(old, new) + + self.assertIsNotNone(diff) + assert diff is not None + self.assertEqual(diff.start_x, 3) + self.assertEqual(render_cells(diff.new_cells), " ") + + def test_diff_render_lines_returns_none_for_identical(self): + line = RenderLine.from_rendered_text("abc") + self.assertIsNone(diff_render_lines(line, line)) + + def test_diff_render_lines_breaks_on_controls_in_prefix(self): + old = RenderLine.from_cells([ + RenderCell("a", 1), + RenderCell("", 0, controls=("\x1b[K",)), + RenderCell("b", 1), + ]) + new = RenderLine.from_cells([ + RenderCell("a", 1), + RenderCell("", 0, controls=("\x1b[K",)), + RenderCell("c", 1), + ]) + + diff = diff_render_lines(old, new) + + self.assertIsNotNone(diff) + assert diff is not None + # Prefix scan stops at control cell, so diff starts at cell 1 + self.assertEqual(diff.start_cell, 1) + self.assertEqual(diff.start_x, 1) + + def test_diff_render_lines_extends_past_combining_chars(self): + # \u0301 is a combining acute accent (zero-width) + old = RenderLine.from_parts(["a", "b", "\u0301"], [1, 1, 0]) + new = RenderLine.from_parts(["a", "c", "\u0301"], [1, 1, 0]) + + diff = diff_render_lines(old, new) + + self.assertIsNotNone(diff) + assert diff is not None + # The combining char is included since it's zero-width + self.assertEqual(len(diff.new_cells), 2) + + def test_diff_old_and_new_changed_width(self): + old = RenderLine.from_rendered_text("ab") + new = RenderLine.from_rendered_text("acd") + + diff = diff_render_lines(old, new) + + self.assertIsNotNone(diff) + assert diff is not None + self.assertEqual(diff.old_changed_width, 1) + self.assertEqual(diff.new_changed_width, 2) + + def test_rendered_screen_round_trips_screen_lines(self): + screen = RenderedScreen.from_screen_lines( + ["a", "\x1b[31mb\x1b[0m"], + (0, 1), + ) + + self.assertEqual(screen.screen_lines, ("a", "\x1b[31mb\x1b[0m")) + + +class TestRenderedScreen(TestCase): + def test_empty(self): + screen = RenderedScreen.empty() + + self.assertEqual(screen.lines, ()) + self.assertEqual(screen.cursor, (0, 0)) + self.assertEqual(screen.overlays, ()) + self.assertEqual(screen.composed_lines, ()) + + def test_with_overlay(self): + screen = RenderedScreen.from_screen_lines(["aaa", "bbb"], (0, 0)) + overlay_line = RenderLine.from_rendered_text("xxx") + result = screen.with_overlay(1, [overlay_line]) + + self.assertEqual(len(result.overlays), 1) + self.assertEqual(result.composed_lines[0].text, "aaa") + self.assertEqual(result.composed_lines[1].text, "xxx") + + def test_compose_replace_overlay(self): + base = RenderedScreen.from_screen_lines(["aa", "bb", "cc"], (0, 0)) + overlay_line = RenderLine.from_rendered_text("XX") + screen = RenderedScreen( + base.lines, + (0, 0), + (ScreenOverlay(y=1, lines=(overlay_line,)),), + ) + + self.assertEqual(screen.composed_lines[0].text, "aa") + self.assertEqual(screen.composed_lines[1].text, "XX") + self.assertEqual(screen.composed_lines[2].text, "cc") + + def test_compose_insert_overlay(self): + base = RenderedScreen.from_screen_lines(["aa", "bb"], (0, 0)) + overlay_line = RenderLine.from_rendered_text("INS") + screen = RenderedScreen( + base.lines, + (0, 0), + (ScreenOverlay(y=1, lines=(overlay_line,), insert=True),), + ) + + self.assertEqual(len(screen.composed_lines), 3) + self.assertEqual(screen.composed_lines[0].text, "aa") + self.assertEqual(screen.composed_lines[1].text, "INS") + self.assertEqual(screen.composed_lines[2].text, "bb") + + def test_compose_replace_extends_beyond_lines(self): + base = RenderedScreen.from_screen_lines(["aa"], (0, 0)) + overlay_line = RenderLine.from_rendered_text("XX") + screen = RenderedScreen( + base.lines, + (0, 0), + (ScreenOverlay(y=1, lines=(overlay_line,)),), + ) + + self.assertEqual(len(screen.composed_lines), 2) + self.assertEqual(screen.composed_lines[1].text, "XX") + + +class TestRenderCell(TestCase): + def test_terminal_text(self): + cell = RenderCell("x", 1, style=StyleRef.from_sgr("\x1b[32m")) + self.assertEqual(cell.terminal_text, "\x1b[32mx\x1b[0m") + + def test_terminal_text_plain(self): + cell = RenderCell("y", 1) + self.assertEqual(cell.terminal_text, "y") + + +class TestRenderCells(TestCase): + def test_render_cells_with_controls(self): + cells = [ + RenderCell("", 0, controls=("\x1b[K",)), + RenderCell("a", 1), + ] + result = render_cells(cells) + self.assertEqual(result, "\x1b[Ka") + + def test_render_cells_skips_empty_text(self): + cells = [ + RenderCell("", 0), + RenderCell("a", 1), + ] + result = render_cells(cells) + self.assertEqual(result, "a") + + def test_render_cells_with_visual_style(self): + cells = [RenderCell("a", 1)] + result = render_cells(cells, visual_style="\x1b[7m") + self.assertEqual(result, "\x1b[7ma\x1b[0m") + + +class TestLineUpdate(TestCase): + def test_post_init_renders_text(self): + cells = (RenderCell("a", 1), RenderCell("b", 1)) + update = LineUpdate( + kind="insert_char", + y=0, + start_cell=0, + start_x=0, + cells=cells, + ) + self.assertEqual(update.text, "ab") + + +class TestRequiresCursorResync(TestCase): + def test_no_controls(self): + cells = [RenderCell("a", 1)] + self.assertFalse(requires_cursor_resync(cells)) + + def test_sgr_only_does_not_require_resync(self): + cells = [RenderCell("", 0, controls=("\x1b[31m",))] + self.assertFalse(requires_cursor_resync(cells)) + + def test_non_sgr_requires_resync(self): + cells = [RenderCell("", 0, controls=("\x1b[H",))] + self.assertTrue(requires_cursor_resync(cells)) diff --git a/Lib/test/test_pyrepl/test_unix_console.py b/Lib/test/test_pyrepl/test_unix_console.py index ab1236768cfb3e..71b2e17e334151 100644 --- a/Lib/test/test_pyrepl/test_unix_console.py +++ b/Lib/test/test_pyrepl/test_unix_console.py @@ -1,14 +1,18 @@ +import errno import itertools import os +import signal import sys +import threading import unittest from functools import partial -from test.support import os_helper, force_not_colorized_test_class +from test.support import force_color, os_helper, force_not_colorized_test_class +from test.support import threading_helper from unittest import TestCase -from unittest.mock import MagicMock, call, patch, ANY +from unittest.mock import MagicMock, call, patch, ANY, Mock -from .support import handle_all_events, code_to_events +from .support import handle_all_events, code_to_events, more_lines try: from _pyrepl.console import Event @@ -96,6 +100,60 @@ def unix_console(events, **kwargs): @patch("os.write") @force_not_colorized_test_class class TestConsole(TestCase): + @staticmethod + def _prepare_reader_with_prompts(console, **kwargs): + from _pyrepl.readline import ReadlineAlikeReader, ReadlineConfig + + config = ReadlineConfig( + readline_completer=kwargs.pop("readline_completer", None) + ) + reader = ReadlineAlikeReader(console=console, config=config) + reader.paste_mode = False + for key, val in kwargs.items(): + setattr(reader, key, val) + return reader + + def test_colorized_multiline_typing_does_not_redraw_previous_line(self, _os_write): + def prepare_reader_with_prompts(console, **kwargs): + reader = self._prepare_reader_with_prompts(console, **kwargs) + reader.more_lines = partial(more_lines, namespace=None) + return reader + + with force_color(True): + events = itertools.chain( + code_to_events("def foo():"), + [Event(evt="key", data="\n", raw=bytearray(b"\n"))], + code_to_events("x = 1"), + [Event(evt="key", data="\n", raw=bytearray(b"\n"))], + code_to_events("y"), + ) + _, con = handle_all_events( + events, + prepare_console=unix_console, + prepare_reader=prepare_reader_with_prompts, + ) + con.restore() + + self.assertNotIn( + call(ANY, b" \x1b[0m x \x1b[0m=\x1b[0m "), + _os_write.mock_calls, + ) + self.assertIn(call(ANY, b"y"), _os_write.mock_calls) + + def test_no_newline(self, _os_write): + code = "1" + events = code_to_events(code) + _, con = handle_events_unix_console(events) + self.assertNotIn(call(ANY, b'\n'), _os_write.mock_calls) + con.restore() + + def test_newline(self, _os_write): + code = "\n" + events = code_to_events(code) + _, con = handle_events_unix_console(events) + _os_write.assert_any_call(ANY, b"\n") + con.restore() + def test_simple_addition(self, _os_write): code = "12+34" events = code_to_events(code) @@ -232,8 +290,7 @@ def test_resize_bigger_on_multiline_function(self, _os_write): events = itertools.chain(code_to_events(code)) reader, console = handle_events_short_unix_console(events) - console.height = 2 - console.getheightwidth = MagicMock(lambda _: (2, 80)) + console.getheightwidth = MagicMock(side_effect=lambda: (2, 80)) def same_reader(_): return reader @@ -268,8 +325,7 @@ def test_resize_smaller_on_multiline_function(self, _os_write): events = itertools.chain(code_to_events(code)) reader, console = handle_events_unix_console_height_3(events) - console.height = 1 - console.getheightwidth = MagicMock(lambda _: (1, 80)) + console.getheightwidth = MagicMock(side_effect=lambda: (1, 80)) def same_reader(_): return reader @@ -303,3 +359,49 @@ def test_getheightwidth_with_invalid_environ(self, _os_write): self.assertIsInstance(console.getheightwidth(), tuple) os.environ = [] self.assertIsInstance(console.getheightwidth(), tuple) + + @unittest.skipUnless(sys.platform == "darwin", "requires macOS") + def test_restore_with_invalid_environ_on_macos(self, _os_write): + # gh-128636 for macOS + console = UnixConsole(term="xterm") + with os_helper.EnvironmentVarGuard(): + os.environ = [] + console.prepare() # needed to call restore() + console.restore() # this should succeed + + @threading_helper.reap_threads + @threading_helper.requires_working_threading() + def test_restore_in_thread(self, _os_write): + # gh-139391: ensure that console.restore() silently suppresses + # exceptions when calling signal.signal() from a non-main thread. + console = unix_console([]) + console.old_sigwinch = signal.SIG_DFL + thread = threading.Thread(target=console.restore) + thread.start() + thread.join() # this should not raise + + +@unittest.skipIf(sys.platform == "win32", "No Unix console on Windows") +class TestUnixConsoleEIOHandling(TestCase): + + @patch('_pyrepl.unix_console.tcsetattr') + @patch('_pyrepl.unix_console.tcgetattr') + def test_eio_error_handling_in_restore(self, mock_tcgetattr, mock_tcsetattr): + + import termios + mock_termios = Mock() + mock_termios.iflag = 0 + mock_termios.oflag = 0 + mock_termios.cflag = 0 + mock_termios.lflag = 0 + mock_termios.cc = [0] * 32 + mock_termios.copy.return_value = mock_termios + mock_tcgetattr.return_value = mock_termios + + console = UnixConsole(term="xterm") + console.prepare() + + mock_tcsetattr.side_effect = termios.error(errno.EIO, "Input/output error") + + # EIO error should be handled gracefully in restore() + console.restore() diff --git a/Lib/test/test_pyrepl/test_utils.py b/Lib/test/test_pyrepl/test_utils.py index 8ce1e5371386f0..0b873d32b62b5d 100644 --- a/Lib/test/test_pyrepl/test_utils.py +++ b/Lib/test/test_pyrepl/test_utils.py @@ -1,14 +1,33 @@ from unittest import TestCase -from _pyrepl.utils import str_width, wlen, prev_next_window +from _pyrepl.utils import str_width, wlen, prev_next_window, gen_colors class TestUtils(TestCase): def test_str_width(self): - characters = ['a', '1', '_', '!', '\x1a', '\u263A', '\uffb9'] + characters = [ + 'a', + '1', + '_', + '!', + '\x1a', + '\u263A', + '\uffb9', + '\N{LATIN SMALL LETTER E WITH ACUTE}', # é + '\N{LATIN SMALL LETTER E WITH CEDILLA}', # ȩ + '\u00ad', + ] for c in characters: self.assertEqual(str_width(c), 1) + zero_width_characters = [ + '\N{COMBINING ACUTE ACCENT}', + '\N{ZERO WIDTH JOINER}', + ] + for c in zero_width_characters: + with self.subTest(character=c): + self.assertEqual(str_width(c), 0) + characters = [chr(99989), chr(99999)] for c in characters: self.assertEqual(str_width(c), 2) @@ -25,6 +44,8 @@ def test_wlen(self): self.assertEqual(wlen('hello'), 5) self.assertEqual(wlen('hello' + '\x1a'), 7) + self.assertEqual(wlen('e\N{COMBINING ACUTE ACCENT}'), 1) + self.assertEqual(wlen('a\N{ZERO WIDTH JOINER}b'), 2) def test_prev_next_window(self): def gen_normal(): @@ -60,3 +81,65 @@ def gen_raise(): self.assertEqual(next(pnw), (3, 4, None)) with self.assertRaises(ZeroDivisionError): next(pnw) + + def test_gen_colors_keyword_highlighting(self): + cases = [ + # no highlights + ("a.set", [(".", "op")]), + ("obj.list", [(".", "op")]), + ("obj.match", [(".", "op")]), + ("b. \\\n format", [(".", "op")]), + ("lazy", []), + ("lazy()", [('(', 'op'), (')', 'op')]), + # highlights + ("set", [("set", "builtin")]), + ("list", [("list", "builtin")]), + (" \n dict", [("dict", "builtin")]), + ( + " lazy import", + [("lazy", "soft_keyword"), ("import", "keyword")], + ), + ( + "lazy from cool_people import pablo", + [ + ("lazy", "soft_keyword"), + ("from", "keyword"), + ("import", "keyword"), + ], + ), + ( + "if sad: lazy import happy", + [ + ("if", "keyword"), + (":", "op"), + ("lazy", "soft_keyword"), + ("import", "keyword"), + ], + ), + ( + "pass; lazy import z", + [ + ("pass", "keyword"), + (";", "op"), + ("lazy", "soft_keyword"), + ("import", "keyword"), + ], + ), + ( + "case +1", + [ + ("case", "soft_keyword"), + ("+", "op"), + ("1", "number"), + ], + ), + ] + for code, expected_highlights in cases: + with self.subTest(code=code): + colors = list(gen_colors(code)) + # Extract (text, tag) pairs for comparison + actual_highlights = [] + for color in colors: + span_text = code[color.span.start:color.span.end + 1] + actual_highlights.append((span_text, color.tag)) + self.assertEqual(actual_highlights, expected_highlights) diff --git a/Lib/test/test_pyrepl/test_windows_console.py b/Lib/test/test_pyrepl/test_windows_console.py index f9607e02c604ff..2b6075b3274c05 100644 --- a/Lib/test/test_pyrepl/test_windows_console.py +++ b/Lib/test/test_pyrepl/test_windows_console.py @@ -10,7 +10,7 @@ from test.support import force_not_colorized_test_class from typing import Iterable from unittest import TestCase -from unittest.mock import MagicMock, call +from unittest.mock import MagicMock, call, patch from .support import handle_all_events, code_to_events from .support import prepare_reader as default_prepare_reader @@ -30,7 +30,20 @@ pass +def _mock_console_init(self, f_in=0, f_out=1, term="", encoding="utf-8"): + """Mock __init__ to avoid real Windows API calls in headless environments.""" + super(WindowsConsole, self).__init__(f_in, f_out, term, encoding) + self.width = 80 + self.height = 25 + self._WindowsConsole__offset = 0 + self.posxy = (0, 0) + self._WindowsConsole__vt_support = False + self._WindowsConsole_original_input_mode = 0 + self.event_queue = wc.EventQueue('utf-8') + + @force_not_colorized_test_class +@patch.object(WindowsConsole, '__init__', _mock_console_init) class WindowsConsoleTests(TestCase): def console(self, events, **kwargs) -> Console: console = WindowsConsole() @@ -72,6 +85,20 @@ def handle_events_short(self, events, **kwargs): def handle_events_height_3(self, events): return self.handle_events(events, height=3) + def test_no_newline(self): + code = "1" + events = code_to_events(code) + _, con = self.handle_events(events) + self.assertNotIn(call(b'\n'), con.out.write.mock_calls) + con.restore() + + def test_newline(self): + code = "\n" + events = code_to_events(code) + _, con = self.handle_events(events) + con.out.write.assert_any_call(b"\n") + con.restore() + def test_simple_addition(self): code = "12+34" events = code_to_events(code) @@ -101,9 +128,7 @@ def test_resize_wider(self): events = code_to_events(code) reader, console = self.handle_events_narrow(events) - console.height = 20 - console.width = 80 - console.getheightwidth = MagicMock(lambda _: (20, 80)) + console.getheightwidth = MagicMock(side_effect=lambda: (20, 80)) def same_reader(_): return reader @@ -129,9 +154,7 @@ def test_resize_narrower(self): events = code_to_events(code) reader, console = self.handle_events(events) - console.height = 20 - console.width = 4 - console.getheightwidth = MagicMock(lambda _: (20, 4)) + console.getheightwidth = MagicMock(side_effect=lambda: (20, 4)) def same_reader(_): return reader @@ -264,8 +287,7 @@ def test_resize_bigger_on_multiline_function(self): events = itertools.chain(code_to_events(code)) reader, console = self.handle_events_short(events) - console.height = 2 - console.getheightwidth = MagicMock(lambda _: (2, 80)) + console.getheightwidth = MagicMock(side_effect=lambda: (2, 80)) def same_reader(_): return reader @@ -284,7 +306,8 @@ def same_console(events): call(self.move_left(5)), call(self.move_up()), call(b"def f():"), - call(self.move_left(3)), + call(self.move_left(8)), + call(self.move_right(5)), call(self.move_down()), ] ) @@ -302,8 +325,7 @@ def test_resize_smaller_on_multiline_function(self): events = itertools.chain(code_to_events(code)) reader, console = self.handle_events_height_3(events) - console.height = 1 - console.getheightwidth = MagicMock(lambda _: (1, 80)) + console.getheightwidth = MagicMock(side_effect=lambda: (1, 80)) def same_reader(_): return reader @@ -359,6 +381,7 @@ def test_multiline_ctrl_z(self): con.restore() +@patch.object(WindowsConsole, '__init__', _mock_console_init) class WindowsConsoleGetEventTests(TestCase): # Virtual-Key Codes: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes VK_BACK = 0x08 @@ -576,6 +599,32 @@ def test_up_vt(self): Event(evt='key', data='up', raw=bytearray(b'\x1b[A'))) self.assertEqual(self.mock.call_count, 3) + # All tests above assume that there is always keyboard data to read, + # because for simplicity we just use + # self.console.wait = MagicMock(return_value=True) + def test_wait_empty(self): + console = WindowsConsole(encoding='utf-8') + console.wait_for_event = MagicMock(return_value=True) + self.assertTrue(console.event_queue.empty()) + timeout = 2.0 + self.assertTrue(console.wait(timeout)) + self.assertEqual(console.wait_for_event.call_count, 1) + self.assertEqual(console.wait_for_event.mock_calls[0], call(timeout)) + + timeout = 1.1 + console.wait_for_event = MagicMock(return_value=False) + self.assertFalse(console.wait(timeout)) + self.assertEqual(console.wait_for_event.call_count, 1) + self.assertEqual(console.wait_for_event.mock_calls[0], call(timeout)) + + def test_wait_not_empty(self): + console = WindowsConsole(encoding='utf-8') + console.wait_for_event = MagicMock(return_value=True) + console.event_queue.push(b"a") + self.assertFalse(console.event_queue.empty()) + self.assertTrue(console.wait(0.0)) + self.assertEqual(console.wait_for_event.call_count, 0) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_pystats.py b/Lib/test/test_pystats.py new file mode 100644 index 00000000000000..c50cecfcfddfe6 --- /dev/null +++ b/Lib/test/test_pystats.py @@ -0,0 +1,215 @@ +import sys +import textwrap +import unittest +from test.support import script_helper + +# This function is available for the --enable-pystats config. +HAVE_PYSTATS = hasattr(sys, '_stats_on') + +TEST_TEMPLATE = """ + import sys + import threading + import time + + THREADS = 2 + + class A: + pass + + class B: + pass + + def modify_class(): + # This is used as a rare event we can assume doesn't happen unless we do it. + # It increments the "Rare event (set_class)" count. + a = A() + a.__class__ = B + + TURNED_ON = False + def stats_on(): + global TURNED_ON + sys._stats_on() + TURNED_ON = True + + TURNED_OFF = False + def stats_off(): + global TURNED_OFF + sys._stats_off() + TURNED_OFF = True + + CLEARED = False + def stats_clear(): + global CLEARED + sys._stats_clear() + CLEARED = True + + def func_start(): + pass + + def func_end(): + pass + + def func_test(thread_id): + pass + + _TEST_CODE_ + + func_start() + threads = [] + for i in range(THREADS): + t = threading.Thread(target=func_test, args=(i,)) + threads.append(t) + t.start() + for t in threads: + t.join() + func_end() + """ + + +def run_test_code( + test_code, + args=[], + env_vars=None, +): + """Run test code and return the value of the "set_class" stats counter. + """ + code = textwrap.dedent(TEST_TEMPLATE) + code = code.replace('_TEST_CODE_', textwrap.dedent(test_code)) + script_args = args + ['-c', code] + env_vars = env_vars or {} + res, _ = script_helper.run_python_until_end(*script_args, **env_vars) + stderr = res.err.decode("ascii", "backslashreplace") + for line in stderr.split('\n'): + if 'Rare event (set_class)' in line: + label, _, value = line.partition(':') + return value.strip() + return '' + + +@unittest.skipUnless(HAVE_PYSTATS, "requires pystats build option") +class TestPyStats(unittest.TestCase): + """Tests for pystats functionality (requires --enable-pystats build + option). + """ + + def test_stats_toggle_on(self): + """Check the toggle on functionality. + """ + code = """ + def func_start(): + modify_class() + """ + + # If turned on with command line flag, should get one count. + stat_count = run_test_code(code, args=['-X', 'pystats']) + self.assertEqual(stat_count, '1') + + # If turned on with env var, should get one count. + stat_count = run_test_code(code, env_vars={'PYTHONSTATS': '1'}) + self.assertEqual(stat_count, '1') + + # If not turned on, should be no counts. + stat_count = run_test_code(code) + self.assertEqual(stat_count, '') + + code = """ + def func_start(): + modify_class() + sys._stats_on() + modify_class() + """ + # Not initially turned on but enabled by sys._stats_on(), should get + # one count. + stat_count = run_test_code(code) + self.assertEqual(stat_count, '1') + + def test_stats_toggle_on_thread(self): + """Check the toggle on functionality when threads are used. + """ + code = """ + def func_test(thread_id): + if thread_id == 0: + modify_class() + stats_on() + modify_class() + else: + while not TURNED_ON: + pass + modify_class() + """ + # Turning on in one thread will count in other thread. + stat_count = run_test_code(code) + self.assertEqual(stat_count, '2') + + code = """ + def func_test(thread_id): + if thread_id == 0: + modify_class() + stats_off() + modify_class() + else: + while not TURNED_OFF: + pass + modify_class() + """ + # Turning off in one thread will not count in other threads. + stat_count = run_test_code(code, args=['-X', 'pystats']) + self.assertEqual(stat_count, '1') + + def test_thread_exit_merge(self): + """Check that per-thread stats (when free-threading enabled) are merged. + """ + code = """ + def func_test(thread_id): + modify_class() + if thread_id == 0: + raise SystemExit + """ + # Stats from a thread exiting early should still be counted. + stat_count = run_test_code(code, args=['-X', 'pystats']) + self.assertEqual(stat_count, '2') + + def test_stats_dump(self): + """Check that sys._stats_dump() works. + """ + code = """ + def func_test(thread_id): + if thread_id == 0: + stats_on() + else: + while not TURNED_ON: + pass + modify_class() + sys._stats_dump() + stats_off() + """ + # Stats from a thread exiting early should still be counted. + stat_count = run_test_code(code) + self.assertEqual(stat_count, '1') + + def test_stats_clear(self): + """Check that sys._stats_clear() works. + """ + code = """ + ready = False + def func_test(thread_id): + global ready + if thread_id == 0: + stats_on() + modify_class() + while not ready: + pass # wait until other thread has called modify_class() + stats_clear() # clears stats for all threads + else: + while not TURNED_ON: + pass + modify_class() + ready = True + """ + # Clearing stats will clear for all threads + stat_count = run_test_code(code) + self.assertEqual(stat_count, '0') + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_queue.py b/Lib/test/test_queue.py index c855fb8fe2b05a..f2898de469e349 100644 --- a/Lib/test/test_queue.py +++ b/Lib/test/test_queue.py @@ -2,6 +2,7 @@ # to ensure the Queue locks remain stable. import itertools import random +import struct import threading import time import unittest @@ -9,6 +10,7 @@ from test.support import gc_collect, bigmemtest from test.support import import_helper from test.support import threading_helper +from test import support # queue module depends on threading primitives threading_helper.requires_working_threading(module=True) @@ -1031,6 +1033,14 @@ def test_is_default(self): self.assertIs(self.type2test, self.queue.SimpleQueue) self.assertIs(self.type2test, self.queue.SimpleQueue) + def test_simplequeue_sizeof(self): + q = self.type2test() + basesize = support.calcobjsize('?nnPnnP') + support.check_sizeof(self, q, basesize + struct.calcsize(8 * 'P')) + for _ in range(1000): + q.put(object()) + support.check_sizeof(self, q, basesize + struct.calcsize(1024 * 'P')) + def test_reentrancy(self): # bpo-14976: put() may be called reentrantly in an asynchronous # callback. diff --git a/Lib/test/test_raise.py b/Lib/test/test_raise.py index dcf0753bc828f3..645ef291a58499 100644 --- a/Lib/test/test_raise.py +++ b/Lib/test/test_raise.py @@ -186,18 +186,14 @@ def test_class_cause(self): self.fail("No exception raised") def test_class_cause_nonexception_result(self): - class ConstructsNone(BaseException): - @classmethod + # See https://github.com/python/cpython/issues/140530. + class ConstructMortal(BaseException): def __new__(*args, **kwargs): - return None - try: - raise IndexError from ConstructsNone - except TypeError as e: - self.assertIn("should have returned an instance of BaseException", str(e)) - except IndexError: - self.fail("Wrong kind of exception raised") - else: - self.fail("No exception raised") + return ["mortal value"] + + msg = ".*should have returned an instance of BaseException.*" + with self.assertRaisesRegex(TypeError, msg): + raise IndexError from ConstructMortal def test_instance_cause(self): cause = KeyError() diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 0217ebd132b110..dbd3b855f536a0 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -14,6 +14,8 @@ from fractions import Fraction from collections import abc, Counter +from test.support import warnings_helper + class MyIndex: def __init__(self, value): @@ -1073,6 +1075,12 @@ def test_avg_std(self): msg='%s%r' % (variate.__name__, args)) self.assertAlmostEqual(s2/(N-1), sigmasqrd, places=2, msg='%s%r' % (variate.__name__, args)) + def test_binomialvariate_log_zero(self): + # gh-149222: Variety random() return 0.0 no input Error + with unittest.mock.patch.object(random.Random, 'random', side_effect= [0.0] + [0.5] * 20): + result = random.binomialvariate(10, 0.5) + self.assertIsInstance(result, int) + self.assertIn(result, range(11)) def test_constant(self): g = random.Random() @@ -1399,6 +1407,7 @@ def test__all__(self): # tests validity but not completeness of the __all__ list self.assertTrue(set(random.__all__) <= set(dir(random))) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @test.support.requires_fork() def test_after_fork(self): # Test the global Random instance gets reseeded in child diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 3870b153688b25..2c9c290e8906b7 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -470,6 +470,16 @@ def test_iterator_setstate(self): it.__setstate__(2**64 - 7) self.assertEqual(list(it), [12, 10]) + def test_iterator_invalid_setstate(self): + for invalid_value in (1.0, ""): + ranges = (('rangeiter', range(10, 100, 2)), + ('longrangeiter', range(10, 2**65, 2))) + for rng_name, rng in ranges: + with self.subTest(invalid_value=invalid_value, range=rng_name): + it = iter(rng) + with self.assertRaises(TypeError): + it.__setstate__(invalid_value) + def test_odd_bug(self): # This used to raise a "SystemError: NULL result without error" # because the range validation step was eating the exception diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 993652d2e882cc..69d730c49387be 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1,10 +1,11 @@ from test.support import (gc_collect, bigmemtest, _2G, cpython_only, captured_stdout, check_disallow_instantiation, linked_to_musl, - warnings_helper, SHORT_TIMEOUT, Stopwatch, requires_resource) + SHORT_TIMEOUT, Stopwatch, requires_resource) import locale import re import string +import sys import unittest import warnings from re import Scanner @@ -89,10 +90,13 @@ def test_search_star_plus(self): self.assertEqual(re.search('x+', 'axx').span(), (1, 3)) self.assertIsNone(re.search('x', 'aaa')) self.assertEqual(re.match('a*', 'xxx').span(0), (0, 0)) + self.assertEqual(re.prefixmatch('a*', 'xxx').span(0), (0, 0)) self.assertEqual(re.match('a*', 'xxx').span(), (0, 0)) self.assertEqual(re.match('x*', 'xxxa').span(0), (0, 3)) + self.assertEqual(re.prefixmatch('x*', 'xxxa').span(0), (0, 3)) self.assertEqual(re.match('x*', 'xxxa').span(), (0, 3)) self.assertIsNone(re.match('a+', 'xxx')) + self.assertIsNone(re.prefixmatch('a+', 'xxx')) def test_branching(self): """Test Branching @@ -179,6 +183,7 @@ def test_bug_449000(self): def test_bug_1661(self): # Verify that flags do not get silently ignored with compiled patterns pattern = re.compile('.') + self.assertRaises(ValueError, re.prefixmatch, pattern, 'A', re.I) self.assertRaises(ValueError, re.match, pattern, 'A', re.I) self.assertRaises(ValueError, re.search, pattern, 'A', re.I) self.assertRaises(ValueError, re.findall, pattern, 'A', re.I) @@ -516,6 +521,8 @@ def test_re_match(self): self.assertEqual(re.match(b'(a)', string).group(0), b'a') self.assertEqual(re.match(b'(a)', string).group(1), b'a') self.assertEqual(re.match(b'(a)', string).group(1, 1), (b'a', b'a')) + self.assertEqual(re.prefixmatch(b'(a)', string).group(1, 1), + (b'a', b'a')) for a in ("\xe0", "\u0430", "\U0001d49c"): self.assertEqual(re.match(a, a).groups(), ()) self.assertEqual(re.match('(%s)' % a, a).groups(), (a,)) @@ -557,10 +564,8 @@ def __index__(self): self.assertEqual(m.group(2, 1), ('b', 'a')) self.assertEqual(m.group(Index(2), Index(1)), ('b', 'a')) - def test_match_getitem(self): - pat = re.compile('(?:(?P<a1>a)|(?P<b2>b))(?P<c3>c)?') - - m = pat.match('a') + def do_test_match_getitem(self, match_fn): + m = match_fn('a') self.assertEqual(m['a1'], 'a') self.assertEqual(m['b2'], None) self.assertEqual(m['c3'], None) @@ -584,7 +589,7 @@ def test_match_getitem(self): with self.assertRaisesRegex(IndexError, 'no such group'): 'a1={a2}'.format_map(m) - m = pat.match('ac') + m = match_fn('ac') self.assertEqual(m['a1'], 'a') self.assertEqual(m['b2'], None) self.assertEqual(m['c3'], 'c') @@ -601,6 +606,14 @@ def test_match_getitem(self): # No len(). self.assertRaises(TypeError, len, m) + def test_match_getitem(self): + pat = re.compile('(?:(?P<a1>a)|(?P<b2>b))(?P<c3>c)?') + self.do_test_match_getitem(pat.match) + + def test_prefixmatch_getitem(self): + pat = re.compile('(?:(?P<a1>a)|(?P<b2>b))(?P<c3>c)?') + self.do_test_match_getitem(pat.prefixmatch) + def test_re_fullmatch(self): # Issue 16203: Proposal: add re.fullmatch() method. self.assertEqual(re.fullmatch(r"a", "a").span(), (0, 1)) @@ -1638,6 +1651,24 @@ def s_int(scanner, token): return int(token) (['sum', 'op=', 3, 'op*', 'foo', 'op+', 312.5, 'op+', 'bar'], '')) + def test_bug_gh140797(self): + # gh140797: Capturing groups are not allowed in re.Scanner + + msg = r"Cannot use capturing groups in re\.Scanner" + # Capturing group throws an error + with self.assertRaisesRegex(ValueError, msg): + Scanner([("(a)b", None)]) + + # Named Group + with self.assertRaisesRegex(ValueError, msg): + Scanner([("(?P<name>a)", None)]) + + # Non-capturing groups should pass normally + s = Scanner([("(?:a)b", lambda scanner, token: token)]) + result, rem = s.scan("ab") + self.assertEqual(result,['ab']) + self.assertEqual(rem,'') + def test_bug_448951(self): # bug 448951 (similar to 429357, but with single char match) # (Also test greedy matches.) @@ -1749,11 +1780,10 @@ def test_bug_6561(self): for x in not_decimal_digits: self.assertIsNone(re.match(r'^\d$', x)) - @warnings_helper.ignore_warnings(category=DeprecationWarning) # gh-80480 array('u') def test_empty_array(self): # SF buf 1647541 import array - for typecode in 'bBhuwHiIlLfd': + for typecode in array.typecodes: a = array.array(typecode) self.assertIsNone(re.compile(b"bla").match(a)) self.assertEqual(re.compile(b"").match(a).groups(), ()) @@ -2177,6 +2207,8 @@ def test_bug_20998(self): self.assertEqual(re.fullmatch('[a-c]+', 'ABC', re.I).span(), (0, 3)) @unittest.skipIf(linked_to_musl(), "musl libc issue, bpo-46390") + @unittest.skipIf(sys.platform.startswith("sunos"), + "test doesn't work on Solaris, gh-91214") def test_locale_caching(self): # Issue #22410 oldlocale = locale.setlocale(locale.LC_CTYPE) @@ -2214,6 +2246,8 @@ def check_en_US_utf8(self): self.assertIsNone(re.match(b'(?Li)\xe5', b'\xc5')) @unittest.skipIf(linked_to_musl(), "musl libc issue, bpo-46390") + @unittest.skipIf(sys.platform.startswith("sunos"), + "test doesn't work on Solaris, gh-91214") def test_locale_compiled(self): oldlocale = locale.setlocale(locale.LC_CTYPE) self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale) @@ -3111,5 +3145,15 @@ def test_re_tests(self): self.assertTrue(obj.search(s)) +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(re, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py index 45192fe508270d..3982686dd10aec 100644 --- a/Lib/test/test_readline.py +++ b/Lib/test/test_readline.py @@ -413,6 +413,24 @@ def test_write_read_limited_history(self): # So, we've only tested that the read did not fail. # See TestHistoryManipulation for the full test. + @unittest.skipUnless(hasattr(readline, "get_pre_input_hook"), + "get_pre_input_hook not available") + def test_get_pre_input_hook(self): + # Save and restore the original hook to avoid side effects + original_hook = readline.get_pre_input_hook() + self.addCleanup(readline.set_pre_input_hook, original_hook) + + # Test that get_pre_input_hook returns None when no hook is set + readline.set_pre_input_hook(None) + self.assertIsNone(readline.get_pre_input_hook()) + + # Set a hook and verify we can retrieve it + def my_hook(): + pass + + readline.set_pre_input_hook(my_hook) + self.assertIs(readline.get_pre_input_hook(), my_hook) + @unittest.skipUnless(support.Py_GIL_DISABLED, 'these tests can only possibly fail with GIL disabled') class FreeThreadingTest(unittest.TestCase): diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 5bc3c5924b07fb..6d30d267cd5ad2 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -41,7 +41,11 @@ ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..') ROOT_DIR = os.path.abspath(os.path.normpath(ROOT_DIR)) -LOG_PREFIX = r'[0-9]+:[0-9]+:[0-9]+ (?:load avg: [0-9]+\.[0-9]{2} )?' +LOG_PREFIX = ( + r'[0-9]+:[0-9]+:[0-9]+ ' + r'(?:load avg: [0-9]+\.[0-9]{2} )?' + r'(?:mem: [0-9]+\.[0-9] (?:MiB|GiB) )?' +) RESULT_REGEX = ( 'passed', 'failed', @@ -182,6 +186,22 @@ def test_randomize(self): self.assertTrue(regrtest.randomize) self.assertIsInstance(regrtest.random_seed, int) + def test_no_randomize(self): + ns = self.parse_args([]) + self.assertIs(ns.randomize, False) + + ns = self.parse_args(["--randomize"]) + self.assertIs(ns.randomize, True) + + ns = self.parse_args(["--no-randomize"]) + self.assertIs(ns.randomize, False) + + ns = self.parse_args(["--randomize", "--no-randomize"]) + self.assertIs(ns.randomize, False) + + ns = self.parse_args(["--no-randomize", "--randomize"]) + self.assertIs(ns.randomize, False) + def test_randseed(self): ns = self.parse_args(['--randseed', '12345']) self.assertEqual(ns.random_seed, 12345) @@ -189,6 +209,10 @@ def test_randseed(self): self.checkError(['--randseed'], 'expected one argument') self.checkError(['--randseed', 'foo'], 'invalid int value') + ns = self.parse_args(['--randseed', '12345', '--no-randomize']) + self.assertEqual(ns.random_seed, 12345) + self.assertFalse(ns.randomize) + def test_fromfile(self): for opt in '-f', '--fromfile': with self.subTest(opt=opt): @@ -259,26 +283,56 @@ def test_use(self): for opt in '-u', '--use': with self.subTest(opt=opt): ns = self.parse_args([opt, 'gui,network']) - self.assertEqual(ns.use_resources, ['gui', 'network']) + self.assertEqual(ns.use_resources, {'gui': None, 'network': None}) + ns = self.parse_args([opt, 'gui', opt, 'network']) + self.assertEqual(ns.use_resources, {'gui': None, 'network': None}) ns = self.parse_args([opt, 'gui,none,network']) - self.assertEqual(ns.use_resources, ['network']) + self.assertEqual(ns.use_resources, {'network': None}) + ns = self.parse_args([opt, 'gui', opt, 'none', opt, 'network']) + self.assertEqual(ns.use_resources, {'network': None}) - expected = list(cmdline.ALL_RESOURCES) - expected.remove('gui') + expected = dict.fromkeys(cmdline.ALL_RESOURCES) + del expected['gui'] ns = self.parse_args([opt, 'all,-gui']) self.assertEqual(ns.use_resources, expected) + self.checkError([opt], 'expected one argument') self.checkError([opt, 'foo'], 'invalid resource') # all + a resource not part of "all" + expected = dict.fromkeys(cmdline.ALL_RESOURCES) + expected['tzdata'] = None ns = self.parse_args([opt, 'all,tzdata']) - self.assertEqual(ns.use_resources, - list(cmdline.ALL_RESOURCES) + ['tzdata']) + self.assertEqual(ns.use_resources, expected) + ns = self.parse_args([opt, 'all', opt, 'tzdata']) + self.assertEqual(ns.use_resources, expected) # test another resource which is not part of "all" ns = self.parse_args([opt, 'extralargefile']) - self.assertEqual(ns.use_resources, ['extralargefile']) + self.assertEqual(ns.use_resources, {'extralargefile': None}) + + # test resource with value + ns = self.parse_args([opt, 'xpickle=2.7']) + self.assertEqual(ns.use_resources, {'xpickle': '2.7'}) + ns = self.parse_args([opt, 'xpickle=2.7,xpickle=3.3']) + self.assertEqual(ns.use_resources, {'xpickle': '3.3'}) + ns = self.parse_args([opt, 'xpickle=2.7,none']) + self.assertEqual(ns.use_resources, {}) + ns = self.parse_args([opt, 'xpickle=2.7,-xpickle']) + self.assertEqual(ns.use_resources, {}) + + expected = dict.fromkeys(cmdline.ALL_RESOURCES) + expected['xpickle'] = '2.7' + ns = self.parse_args([opt, 'all,xpickle=2.7']) + self.assertEqual(ns.use_resources, expected) + ns = self.parse_args([opt, 'all', opt, 'xpickle=2.7']) + self.assertEqual(ns.use_resources, expected) + + # test invalid resources with value + self.checkError([opt, 'all=0'], 'invalid resource: all=0') + self.checkError([opt, 'none=0'], 'invalid resource: none=0') + self.checkError([opt, 'all,-gui=0'], 'invalid resource: -gui=0') def test_memlimit(self): for opt in '-M', '--memlimit': @@ -428,29 +482,31 @@ def create_regrtest(self, args): return regrtest - def check_ci_mode(self, args, use_resources, rerun=True): + def check_ci_mode(self, args, use_resources, + *, rerun=True, randomize=True, output_on_failure=True): regrtest = self.create_regrtest(args) self.assertEqual(regrtest.num_workers, -1) self.assertEqual(regrtest.want_rerun, rerun) - self.assertTrue(regrtest.randomize) + self.assertEqual(regrtest.fail_rerun, False) + self.assertEqual(regrtest.randomize, randomize) self.assertIsInstance(regrtest.random_seed, int) self.assertTrue(regrtest.fail_env_changed) self.assertTrue(regrtest.print_slowest) - self.assertTrue(regrtest.output_on_failure) - self.assertEqual(sorted(regrtest.use_resources), sorted(use_resources)) + self.assertEqual(regrtest.output_on_failure, output_on_failure) + self.assertEqual(regrtest.use_resources, use_resources) return regrtest def test_fast_ci(self): args = ['--fast-ci'] - use_resources = sorted(cmdline.ALL_RESOURCES) - use_resources.remove('cpu') + use_resources = dict.fromkeys(cmdline.ALL_RESOURCES) + del use_resources['cpu'] regrtest = self.check_ci_mode(args, use_resources) self.assertEqual(regrtest.timeout, 10 * 60) def test_fast_ci_python_cmd(self): args = ['--fast-ci', '--python', 'python -X dev'] - use_resources = sorted(cmdline.ALL_RESOURCES) - use_resources.remove('cpu') + use_resources = dict.fromkeys(cmdline.ALL_RESOURCES) + del use_resources['cpu'] regrtest = self.check_ci_mode(args, use_resources, rerun=False) self.assertEqual(regrtest.timeout, 10 * 60) self.assertEqual(regrtest.python_cmd, ('python', '-X', 'dev')) @@ -458,17 +514,35 @@ def test_fast_ci_python_cmd(self): def test_fast_ci_resource(self): # it should be possible to override resources individually args = ['--fast-ci', '-u-network'] - use_resources = sorted(cmdline.ALL_RESOURCES) - use_resources.remove('cpu') - use_resources.remove('network') + use_resources = dict.fromkeys(cmdline.ALL_RESOURCES) + del use_resources['cpu'] + del use_resources['network'] self.check_ci_mode(args, use_resources) + def test_fast_ci_verbose(self): + args = ['--fast-ci', '--verbose'] + use_resources = dict.fromkeys(cmdline.ALL_RESOURCES) + del use_resources['cpu'] + regrtest = self.check_ci_mode(args, use_resources, + output_on_failure=False) + self.assertEqual(regrtest.verbose, True) + def test_slow_ci(self): args = ['--slow-ci'] - use_resources = sorted(cmdline.ALL_RESOURCES) + use_resources = dict.fromkeys(cmdline.ALL_RESOURCES) regrtest = self.check_ci_mode(args, use_resources) self.assertEqual(regrtest.timeout, 20 * 60) + def test_ci_no_randomize(self): + use_resources = dict.fromkeys(cmdline.ALL_RESOURCES) + self.check_ci_mode( + ["--slow-ci", "--no-randomize"], use_resources, randomize=False + ) + del use_resources['cpu'] + self.check_ci_mode( + ["--fast-ci", "--no-randomize"], use_resources, randomize=False + ) + def test_dont_add_python_opts(self): args = ['--dont-add-python-opts'] ns = cmdline._parse_args(args) @@ -501,6 +575,13 @@ def test_single_process(self): self.assertEqual(regrtest.num_workers, 0) self.assertTrue(regrtest.single_process) + def test_pythoninfo(self): + ns = self.parse_args([]) + self.assertFalse(ns.pythoninfo) + + ns = self.parse_args(['--pythoninfo']) + self.assertTrue(ns.pythoninfo) + @dataclasses.dataclass(slots=True) class Rerun: @@ -2153,10 +2234,7 @@ def test_unload_tests(self): self.check_executed_tests(output, tests, stats=3) def check_add_python_opts(self, option): - # --fast-ci and --slow-ci add "-u -W default -bb -E" options to Python - - # Skip test if _testinternalcapi is missing - import_helper.import_module('_testinternalcapi') + # --fast-ci and --slow-ci add "-u -W error -bb -E" options to Python code = textwrap.dedent(r""" import sys @@ -2171,25 +2249,26 @@ def check_add_python_opts(self, option): use_environment = (support.is_emscripten or support.is_wasi) class WorkerTests(unittest.TestCase): - @unittest.skipUnless(config_get is None, 'need config_get()') + @unittest.skipIf(config_get is None, 'need config_get()') def test_config(self): - config = config_get() # -u option self.assertEqual(config_get('buffered_stdio'), 0) - # -W default option - self.assertTrue(config_get('warnoptions'), ['default']) + # -W error option + self.assertEqual(config_get('warnoptions'), + ['error', 'error::BytesWarning']) # -bb option - self.assertTrue(config_get('bytes_warning'), 2) + self.assertEqual(config_get('bytes_warning'), 2) # -E option - self.assertTrue(config_get('use_environment'), use_environment) + self.assertEqual(config_get('use_environment'), use_environment) def test_python_opts(self): # -u option self.assertTrue(sys.__stdout__.write_through) self.assertTrue(sys.__stderr__.write_through) - # -W default option - self.assertTrue(sys.warnoptions, ['default']) + # -W error option + self.assertEqual(sys.warnoptions, + ['error', 'error::BytesWarning']) # -bb option self.assertEqual(sys.flags.bytes_warning, 2) @@ -2208,7 +2287,8 @@ def test_python_opts(self): proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - text=True) + text=True, + env=support.make_clean_env()) self.assertEqual(proc.returncode, 0, proc) def test_add_python_opts(self): @@ -2357,6 +2437,11 @@ def test_pgo_exclude(self): self.assertNotIn('test_re', tests) self.assertEqual(len(tests), len(pgo_tests) - 1) + def test_pythoninfo(self): + testname = self.create_test() + output = self.run_tests('--pythoninfo', testname) + self.assertIn("Python build information", output) + class TestUtils(unittest.TestCase): def test_format_duration(self): @@ -2396,20 +2481,20 @@ def test_format_resources(self): format_resources = utils.format_resources ALL_RESOURCES = utils.ALL_RESOURCES self.assertEqual( - format_resources(("network",)), + format_resources({"network": None}), 'resources (1): network') self.assertEqual( - format_resources(("audio", "decimal", "network")), + format_resources(dict.fromkeys(("audio", "decimal", "network"))), 'resources (3): audio,decimal,network') self.assertEqual( - format_resources(ALL_RESOURCES), + format_resources(dict.fromkeys(ALL_RESOURCES)), 'resources: all') self.assertEqual( - format_resources(tuple(name for name in ALL_RESOURCES - if name != "cpu")), + format_resources({name: None for name in ALL_RESOURCES + if name != "cpu"}), 'resources: all,-cpu') self.assertEqual( - format_resources((*ALL_RESOURCES, "tzdata")), + format_resources({**dict.fromkeys(ALL_RESOURCES), "tzdata": None}), 'resources: all,tzdata') def test_match_test(self): diff --git a/Lib/test/test_remote_pdb.py b/Lib/test/test_remote_pdb.py index 280e2444ef7d34..d26d63faa61ddb 100644 --- a/Lib/test/test_remote_pdb.py +++ b/Lib/test/test_remote_pdb.py @@ -1441,6 +1441,34 @@ def test_multi_line_commands(self): self.assertIn("Function returned: 42", stdout) self.assertEqual(process.returncode, 0) + def test_exec_in_closure_result_uses_pdb_stdout(self): + """ + Expression results executed via _exec_in_closure() should be written + to the debugger output stream (pdb stdout), not to sys.stdout. + """ + self._create_script() + process, client_file = self._connect_and_get_client_file() + + with kill_on_error(process): + self._read_until_prompt(client_file) + + self._send_command(client_file, "(lambda: 123)()") + messages = self._read_until_prompt(client_file) + result_msg = "".join(msg.get("message", "") for msg in messages) + self.assertIn("123", result_msg) + + self._send_command(client_file, "sum(i for i in (1, 2, 3))") + messages = self._read_until_prompt(client_file) + result_msg = "".join(msg.get("message", "") for msg in messages) + self.assertIn("6", result_msg) + + self._send_command(client_file, "c") + stdout, _ = process.communicate(timeout=SHORT_TIMEOUT) + + self.assertNotIn("\n123\n", stdout) + self.assertNotIn("\n6\n", stdout) + self.assertEqual(process.returncode, 0) + def _supports_remote_attaching(): PROCESS_VM_READV_SUPPORTED = False @@ -1539,6 +1567,9 @@ def do_integration_test(self, client_stdin): redirect_stdout(client_stdout), redirect_stderr(client_stderr), unittest.mock.patch("sys.argv", ["pdb", "-p", str(process.pid)]), + unittest.mock.patch( + "pdb.exit_with_permission_help_text", side_effect=PermissionError + ), ): try: pdb.main() @@ -1587,5 +1618,17 @@ def test_attach_to_process_with_colors(self): self.assertNotIn("while x == 1", output["client"]["stdout"]) self.assertIn("while x == 1", re.sub("\x1b[^m]*m", "", output["client"]["stdout"])) + def test_attach_to_non_existent_process(self): + with force_color(False): + result = subprocess.run([sys.executable, "-m", "pdb", "-p", "999999"], text=True, capture_output=True) + self.assertNotEqual(result.returncode, 0) + if sys.platform == "darwin": + # On MacOS, attaching to a non-existent process gives PermissionError + error = "The specified process cannot be attached to due to insufficient permissions" + else: + error = "Cannot attach to pid 999999, please make sure that the process exists" + self.assertIn(error, result.stdout) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index f4a4634fc62f8a..850cb66a89ba84 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -5,6 +5,8 @@ import subprocess import sys import unittest +from contextlib import contextmanager +from functools import partial from textwrap import dedent from test import support from test.support import ( @@ -27,7 +29,7 @@ raise unittest.SkipTest("test module requires subprocess") -def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw): +def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, custom=False, isolated=True, **kw): """Run the Python REPL with the given arguments. kw is extra keyword args to pass to subprocess.Popen. Returns a Popen @@ -41,7 +43,14 @@ def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw): # path may be used by PyConfig_Get("module_search_paths") to build the # default module search path. stdin_fname = os.path.join(os.path.dirname(sys.executable), "<stdin>") - cmd_line = [stdin_fname, '-I', '-i'] + cmd_line = [stdin_fname] + # Isolated mode implies -EPs and ignores PYTHON* variables. + if isolated: + cmd_line.append('-I') + # Don't re-run the built-in REPL from interactive mode + # if we're testing a custom REPL (such as the asyncio REPL). + if not custom: + cmd_line.append('-i') cmd_line.extend(args) # Set TERM=vt100, for the rationale see the comments in spawn_python() of @@ -55,6 +64,23 @@ def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw): stdout=stdout, stderr=stderr, **kw) + +spawn_asyncio_repl = partial(spawn_repl, "-m", "asyncio", custom=True) + + +@contextmanager +def temp_pythonstartup(*, source: str, histfile: str = ".pythonhist"): + """Create environment variables for a PYTHONSTARTUP script in a temporary directory.""" + with os_helper.temp_dir() as tmpdir: + filename = os.path.join(tmpdir, "pythonstartup.py") + with open(filename, "w") as f: + f.write(source) + yield { + "PYTHONSTARTUP": filename, + "PYTHON_HISTORY": os.path.join(tmpdir, histfile) + } + + def run_on_interactive_mode(source): """Spawn a new Python interpreter, pass the given input source code from the stdin and return the @@ -131,6 +157,22 @@ def test_multiline_string_parsing(self): output = kill_python(p) self.assertEqual(p.returncode, 0) + @cpython_only + def test_lexer_buffer_realloc_with_null_start(self): + # gh-144759: NULL pointer arithmetic in the lexer when start and + # multi_line_start are NULL (uninitialized in tok_mode_stack[0]) + # and the lexer buffer is reallocated while parsing long input. + long_value = "a" * 2000 + user_input = dedent(f"""\ + x = f'{{{long_value!r}}}' + print(x) + """) + p = spawn_repl() + p.stdin.write(user_input) + output = kill_python(p) + self.assertEqual(p.returncode, 0) + self.assertIn(long_value, output) + def test_close_stdin(self): user_input = dedent(''' import os @@ -188,6 +230,66 @@ def foo(x): ] self.assertEqual(traceback_lines, expected_lines) + def test_pythonstartup_error_reporting(self): + # errors based on https://github.com/python/cpython/issues/137576 + + def make_repl(env): + return subprocess.Popen( + [os.path.join(os.path.dirname(sys.executable), '<stdin>'), "-i"], + executable=sys.executable, + text=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=env, + ) + + # case 1: error in user input, but PYTHONSTARTUP is fine + with os_helper.temp_dir() as tmpdir: + script = os.path.join(tmpdir, "pythonstartup.py") + with open(script, "w") as f: + f.write("print('from pythonstartup')\n") + + env = os.environ.copy() + env['PYTHONSTARTUP'] = script + env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".pythonhist") + p = make_repl(env) + p.stdin.write("1/0") + output = kill_python(p) + expected = dedent(""" + Traceback (most recent call last): + File "<stdin>", line 1, in <module> + 1/0 + ~^~ + ZeroDivisionError: division by zero + """) + self.assertIn("from pythonstartup", output) + self.assertIn(expected, output) + + # case 2: error in PYTHONSTARTUP triggered by user input + with os_helper.temp_dir() as tmpdir: + script = os.path.join(tmpdir, "pythonstartup.py") + with open(script, "w") as f: + f.write("def foo():\n 1/0\n") + + env = os.environ.copy() + env['PYTHONSTARTUP'] = script + env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".pythonhist") + p = make_repl(env) + p.stdin.write('foo()') + output = kill_python(p) + expected = dedent(""" + Traceback (most recent call last): + File "<stdin>", line 1, in <module> + foo() + ~~~^^ + File "%s", line 2, in foo + 1/0 + ~^~ + ZeroDivisionError: division by zero + """) % script + self.assertIn(expected, output) + def test_runsource_show_syntax_error_location(self): user_input = dedent("""def f(x, x): ... """) @@ -225,19 +327,27 @@ def test_asyncio_repl_reaches_python_startup_script(self): with os_helper.temp_dir() as tmpdir: script = os.path.join(tmpdir, "pythonstartup.py") with open(script, "w") as f: - f.write("print('pythonstartup done!')" + os.linesep) - f.write("exit(0)" + os.linesep) + f.write("print('pythonstartup done!')\n") + env = os.environ.copy() + env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".asyncio_history") + env["PYTHONSTARTUP"] = script + p = spawn_asyncio_repl(isolated=False, env=env) + output = kill_python(p) + self.assertEqual(p.returncode, 0) + self.assertIn("pythonstartup done!", output) + def test_asyncio_repl_respects_isolated_mode(self): + with os_helper.temp_dir() as tmpdir: + script = os.path.join(tmpdir, "pythonstartup.py") + with open(script, "w") as f: + f.write("print('should not print')\n") env = os.environ.copy() env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".asyncio_history") env["PYTHONSTARTUP"] = script - subprocess.check_call( - [sys.executable, "-m", "asyncio"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=env, - timeout=SHORT_TIMEOUT, - ) + p = spawn_asyncio_repl(isolated=True, env=env) + output = kill_python(p) + self.assertEqual(p.returncode, 0) + self.assertNotIn("should not print", output) @unittest.skipUnless(pty, "requires pty") def test_asyncio_repl_is_ok(self): @@ -297,7 +407,7 @@ def f(): class TestAsyncioREPL(unittest.TestCase): def test_multiple_statements_fail_early(self): user_input = "1 / 0; print(f'afterwards: {1+1}')" - p = spawn_repl("-m", "asyncio") + p = spawn_asyncio_repl() p.stdin.write(user_input) output = kill_python(p) self.assertIn("ZeroDivisionError", output) @@ -309,7 +419,7 @@ def test_toplevel_contextvars_sync(self): var = ContextVar("var", default="failed") var.set("ok") """) - p = spawn_repl("-m", "asyncio") + p = spawn_asyncio_repl() p.stdin.write(user_input) user_input2 = dedent(""" print(f"toplevel contextvar test: {var.get()}") @@ -325,9 +435,16 @@ def test_toplevel_contextvars_async(self): from contextvars import ContextVar var = ContextVar('var', default='failed') """) - p = spawn_repl("-m", "asyncio") + p = spawn_asyncio_repl() p.stdin.write(user_input) user_input2 = "async def set_var(): var.set('ok')\n" + try: + import _pyrepl # noqa: F401 + except ModuleNotFoundError: + # If we're going to be forced into the regular REPL, then we need an + # extra newline here. Omit it by default to catch any breakage to + # the new REPL's behavior. + user_input2 += "\n" p.stdin.write(user_input2) user_input3 = "await set_var()\n" p.stdin.write(user_input3) @@ -338,6 +455,39 @@ def test_toplevel_contextvars_async(self): expected = "toplevel contextvar test: ok" self.assertIn(expected, output, expected) + def test_quiet_mode(self): + p = spawn_repl("-q", "-m", "asyncio", custom=True) + output = kill_python(p) + self.assertEqual(p.returncode, 0) + self.assertEqual(output[:3], ">>>") + + @support.force_not_colorized + @support.subTests( + ("startup_code", "expected_error"), + [ + ("some invalid syntax\n", "SyntaxError: invalid syntax"), + ("1/0\n", "ZeroDivisionError: division by zero"), + ], + ) + def test_pythonstartup_failure(self, startup_code, expected_error): + startup_env = self.enterContext( + temp_pythonstartup(source=startup_code, histfile=".asyncio_history")) + + p = spawn_repl( + "-qm", "asyncio", + env=os.environ | startup_env, + isolated=False, + custom=True) + p.stdin.write("print('user code', 'executed')\n") + output = kill_python(p) + self.assertEqual(p.returncode, 0) + + tb_hint = f'File "{startup_env["PYTHONSTARTUP"]}", line 1' + self.assertIn(tb_hint, output) + self.assertIn(expected_error, output) + + self.assertIn("user code executed", output) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py index 22a55b57c076eb..5a95a05c6496a2 100644 --- a/Lib/test/test_reprlib.py +++ b/Lib/test/test_reprlib.py @@ -695,8 +695,11 @@ def _check_path_limitations(self, module_name): source_path_len += 2 * (len(self.longname) + 1) # a path separator + `module_name` + ".py" source_path_len += len(module_name) + 1 + len(".py") - cached_path_len = (source_path_len + - len(importlib.util.cache_from_source("x.py")) - len("x.py")) + try: + cached_path_len = (source_path_len + + len(importlib.util.cache_from_source("x.py")) - len("x.py")) + except NotImplementedError: + cached_path_len = source_path_len if os.name == 'nt' and cached_path_len >= 258: # Under Windows, the max path len is 260 including C's terminating # NUL character. diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py index d23d3623235f38..6ea27c463f3148 100644 --- a/Lib/test/test_resource.py +++ b/Lib/test/test_resource.py @@ -4,7 +4,6 @@ from test import support from test.support import import_helper from test.support import os_helper -import time resource = import_helper.import_module('resource') @@ -14,89 +13,138 @@ class ResourceTest(unittest.TestCase): def test_args(self): self.assertRaises(TypeError, resource.getrlimit) - self.assertRaises(TypeError, resource.getrlimit, 42, 42) + self.assertRaises(TypeError, resource.getrlimit, 0, 42) + self.assertRaises(OverflowError, resource.getrlimit, 2**1000) + self.assertRaises(OverflowError, resource.getrlimit, -2**1000) + self.assertRaises(TypeError, resource.getrlimit, '0') self.assertRaises(TypeError, resource.setrlimit) - self.assertRaises(TypeError, resource.setrlimit, 42, 42, 42) + self.assertRaises(TypeError, resource.setrlimit, 0) + self.assertRaises(TypeError, resource.setrlimit, 0, 42) + self.assertRaises(TypeError, resource.setrlimit, 0, 42, 42) + self.assertRaises(OverflowError, resource.setrlimit, 2**1000, (42, 42)) + self.assertRaises(OverflowError, resource.setrlimit, -2**1000, (42, 42)) + self.assertRaises(ValueError, resource.setrlimit, 0, (42,)) + self.assertRaises(ValueError, resource.setrlimit, 0, (42, 42, 42)) + self.assertRaises(TypeError, resource.setrlimit, '0', (42, 42)) + self.assertRaises(TypeError, resource.setrlimit, 0, ('42', 42)) + self.assertRaises(TypeError, resource.setrlimit, 0, (42, '42')) @unittest.skipIf(sys.platform == "vxworks", "setting RLIMIT_FSIZE is not supported on VxWorks") + @unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE') def test_fsize_ismax(self): - try: - (cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE) - except AttributeError: - pass - else: - # RLIMIT_FSIZE should be RLIM_INFINITY, which will be a really big - # number on a platform with large file support. On these platforms, - # we need to test that the get/setrlimit functions properly convert - # the number to a C long long and that the conversion doesn't raise - # an error. - self.assertEqual(resource.RLIM_INFINITY, max) - resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max)) - + (cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE) + # RLIMIT_FSIZE should be RLIM_INFINITY, which will be a really big + # number on a platform with large file support. On these platforms, + # we need to test that the get/setrlimit functions properly convert + # the number to a C long long and that the conversion doesn't raise + # an error. + self.assertGreater(resource.RLIM_INFINITY, 0) + self.assertGreaterEqual(max, 0) + self.assertLessEqual(cur, max) + resource.setrlimit(resource.RLIMIT_FSIZE, (max, max)) + resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max)) + + @unittest.skipIf(sys.platform in ("vxworks", "cygwin"), + f"setting RLIMIT_FSIZE is not supported on {sys.platform}") + @unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE') def test_fsize_enforced(self): + self.addCleanup(os_helper.unlink, os_helper.TESTFN) try: - (cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE) - except AttributeError: - pass - else: - # Check to see what happens when the RLIMIT_FSIZE is small. Some - # versions of Python were terminated by an uncaught SIGXFSZ, but - # pythonrun.c has been fixed to ignore that exception. If so, the - # write() should return EFBIG when the limit is exceeded. - - # At least one platform has an unlimited RLIMIT_FSIZE and attempts - # to change it raise ValueError instead. + (cur, max_lim) = resource.getrlimit(resource.RLIMIT_FSIZE) + except OSError as e: + self.skipTest(f"getrlimit(RLIMIT_FSIZE) failed: {e}") + if max_lim != resource.RLIM_INFINITY and max_lim < 1025: + self.skipTest(f"system RLIMIT_FSIZE hard limit ({max_lim}) is too small for this test") + with open(os_helper.TESTFN, "wb", buffering=0) as f: try: - try: - resource.setrlimit(resource.RLIMIT_FSIZE, (1024, max)) - limit_set = True - except ValueError: - limit_set = False - f = open(os_helper.TESTFN, "wb") - try: - f.write(b"X" * 1024) - try: - f.write(b"Y") - f.flush() - # On some systems (e.g., Ubuntu on hppa) the flush() - # doesn't always cause the exception, but the close() - # does eventually. Try flushing several times in - # an attempt to ensure the file is really synced and - # the exception raised. - for i in range(5): - time.sleep(.1) - f.flush() - except OSError: - if not limit_set: - raise - if limit_set: - # Close will attempt to flush the byte we wrote - # Restore limit first to avoid getting a spurious error - resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max)) - finally: - f.close() + resource.setrlimit(resource.RLIMIT_FSIZE, (1024, max_lim)) + f.write(b"X" * 1024) + with self.assertRaises(OSError, msg="f.write() did not raise OSError when exceeding RLIMIT_FSIZE"): + f.write(b"Y") + f.flush() finally: - if limit_set: - resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max)) - os_helper.unlink(os_helper.TESTFN) + # Close will attempt to flush the byte we wrote + # Restore limit first to avoid getting a spurious error + resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max_lim)) - def test_fsize_toobig(self): + @unittest.skipIf(sys.platform == "vxworks", + "setting RLIMIT_FSIZE is not supported on VxWorks") + @unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE') + def test_fsize_too_big(self): # Be sure that setrlimit is checking for really large values too_big = 10**50 + (cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE) + try: + resource.setrlimit(resource.RLIMIT_FSIZE, (too_big, max)) + except (OverflowError, ValueError): + pass + try: + resource.setrlimit(resource.RLIMIT_FSIZE, (max, too_big)) + except (OverflowError, ValueError): + pass + + @unittest.skipIf(sys.platform in ("vxworks", "cygwin"), + f"setting RLIMIT_FSIZE is not supported on {sys.platform}") + @unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE') + def test_fsize_not_too_big(self): + (cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE) + self.addCleanup(resource.setrlimit, resource.RLIMIT_FSIZE, (cur, max)) + + def expected(cur): + # The glibc wrapper functions use a 64-bit rlim_t data type, + # even on 32-bit platforms. If a program tried to set a resource + # limit to a value larger than can be represented in a 32-bit + # unsigned long, then the glibc setrlimit() wrapper function + # silently converted the limit value to RLIM_INFINITY. + if sys.maxsize < 2**32 <= cur <= resource.RLIM_INFINITY: + return [(resource.RLIM_INFINITY, max), (cur, max)] + return [(min(cur, resource.RLIM_INFINITY), max)] + + resource.setrlimit(resource.RLIMIT_FSIZE, (2**31-5, max)) + self.assertEqual(resource.getrlimit(resource.RLIMIT_FSIZE), (2**31-5, max)) + resource.setrlimit(resource.RLIMIT_FSIZE, (2**31, max)) + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**31)) + resource.setrlimit(resource.RLIMIT_FSIZE, (2**32-5, max)) + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32-5)) + try: - (cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE) - except AttributeError: + resource.setrlimit(resource.RLIMIT_FSIZE, (2**32, max)) + except OverflowError: pass else: + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**32)) + + resource.setrlimit(resource.RLIMIT_FSIZE, (2**63-5, max)) + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63-5)) try: - resource.setrlimit(resource.RLIMIT_FSIZE, (too_big, max)) - except (OverflowError, ValueError): - pass - try: - resource.setrlimit(resource.RLIMIT_FSIZE, (max, too_big)) - except (OverflowError, ValueError): + resource.setrlimit(resource.RLIMIT_FSIZE, (2**63, max)) + except ValueError: + # There is a hard limit on macOS. pass + else: + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**63)) + resource.setrlimit(resource.RLIMIT_FSIZE, (2**64-5, max)) + self.assertIn(resource.getrlimit(resource.RLIMIT_FSIZE), expected(2**64-5)) + + @unittest.skipIf(sys.platform == "vxworks", + "setting RLIMIT_FSIZE is not supported on VxWorks") + @unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE') + def test_fsize_negative(self): + self.assertGreater(resource.RLIM_INFINITY, 0) + (cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE) + for value in -5, -2**31, -2**32-5, -2**63, -2**64-5, -2**1000: + with self.subTest(value=value): + self.assertRaises(ValueError, resource.setrlimit, resource.RLIMIT_FSIZE, (value, max)) + self.assertRaises(ValueError, resource.setrlimit, resource.RLIMIT_FSIZE, (cur, value)) + + if resource.RLIM_INFINITY in (2**32-3, 2**32-1, 2**64-3, 2**64-1): + value = (resource.RLIM_INFINITY & 0xffff) - 0x10000 + with self.assertWarnsRegex(DeprecationWarning, "RLIM_INFINITY"): + resource.setrlimit(resource.RLIMIT_FSIZE, (value, max)) + with self.assertWarnsRegex(DeprecationWarning, "RLIM_INFINITY"): + resource.setrlimit(resource.RLIMIT_FSIZE, (cur, value)) + @unittest.skipUnless(hasattr(resource, "getrusage"), "needs getrusage") def test_getrusage(self): @@ -115,29 +163,35 @@ def test_getrusage(self): pass # Issue 6083: Reference counting bug - @unittest.skipIf(sys.platform == "vxworks", - "setting RLIMIT_CPU is not supported on VxWorks") + @unittest.skipIf(sys.platform in ("vxworks", "cygwin"), + f"setting RLIMIT_CPU is not supported on {sys.platform}") + @unittest.skipUnless(hasattr(resource, 'RLIMIT_CPU'), 'requires resource.RLIMIT_CPU') def test_setrusage_refcount(self): - try: - limits = resource.getrlimit(resource.RLIMIT_CPU) - except AttributeError: - pass - else: - class BadSequence: - def __len__(self): - return 2 - def __getitem__(self, key): - if key in (0, 1): - return len(tuple(range(1000000))) - raise IndexError + limits = resource.getrlimit(resource.RLIMIT_CPU) + class BadSequence: + def __len__(self): + return 2 + def __getitem__(self, key): + if key in (0, 1): + return len(tuple(range(1000000))) + raise IndexError - resource.setrlimit(resource.RLIMIT_CPU, BadSequence()) + resource.setrlimit(resource.RLIMIT_CPU, BadSequence()) def test_pagesize(self): pagesize = resource.getpagesize() self.assertIsInstance(pagesize, int) self.assertGreaterEqual(pagesize, 0) + def test_contants(self): + self.assertIsInstance(resource.RLIM_INFINITY, int) + if sys.platform.startswith(('freebsd', 'solaris', 'sunos', 'aix')): + self.assertHasAttr(resource, 'RLIM_SAVED_CUR') + self.assertHasAttr(resource, 'RLIM_SAVED_MAX') + if hasattr(resource, 'RLIM_SAVED_CUR'): + self.assertIsInstance(resource.RLIM_SAVED_CUR, int) + self.assertIsInstance(resource.RLIM_SAVED_MAX, int) + @unittest.skipUnless(sys.platform in ('linux', 'android'), 'Linux only') def test_linux_constants(self): for attr in ['MSGQUEUE', 'NICE', 'RTPRIO', 'RTTIME', 'SIGPENDING']: @@ -145,7 +199,7 @@ def test_linux_constants(self): self.assertIsInstance(getattr(resource, 'RLIMIT_' + attr), int) def test_freebsd_contants(self): - for attr in ['SWAP', 'SBSIZE', 'NPTS']: + for attr in ['SWAP', 'SBSIZE', 'NPTS', 'UMTXP', 'VMEM', 'PIPEBUF']: with contextlib.suppress(AttributeError): self.assertIsInstance(getattr(resource, 'RLIMIT_' + attr), int) @@ -168,7 +222,8 @@ class BadSeq: def __len__(self): return 2 def __getitem__(self, key): - return limits[key] - 1 # new reference + lim = limits[key] + return lim - 1 if lim > 0 else lim + sys.maxsize*2 # new reference limits = resource.getrlimit(resource.RLIMIT_AS) self.assertEqual(resource.prlimit(0, resource.RLIMIT_AS, BadSeq()), diff --git a/Lib/test/test_rlcompleter.py b/Lib/test/test_rlcompleter.py index a8914953ce9eb4..c0b5a4da8cb256 100644 --- a/Lib/test/test_rlcompleter.py +++ b/Lib/test/test_rlcompleter.py @@ -1,6 +1,7 @@ import unittest from unittest.mock import patch import builtins +import types import rlcompleter from test.support import MISSING_C_DOCSTRINGS @@ -106,19 +107,35 @@ def test_excessive_getattr(self): # we use __dir__ and __getattr__ in class Foo to create a "magic" # class attribute 'bar'. This forces `getattr` to call __getattr__ # (which is doesn't necessarily do). - class Foo: + # Test 1: Attribute returns None + class FooReturnsNone: calls = 0 - bar = '' + bar = None def __getattribute__(self, name): if name == 'bar': self.calls += 1 return None return super().__getattribute__(name) - f = Foo() - completer = rlcompleter.Completer(dict(f=f)) - self.assertEqual(completer.complete('f.b', 0), 'f.bar') - self.assertEqual(f.calls, 1) + f1 = FooReturnsNone() + completer1 = rlcompleter.Completer(dict(f=f1)) + self.assertEqual(completer1.complete('f.b', 0), 'f.bar') + self.assertEqual(f1.calls, 1) + + # Test 2: Attribute returns non-None value + class FooReturnsValue: + calls = 0 + bar = '' + def __getattribute__(self, name): + if name == 'bar': + self.calls += 1 + return '' + return super().__getattribute__(name) + + f2 = FooReturnsValue() + completer2 = rlcompleter.Completer(dict(f=f2)) + self.assertEqual(completer2.complete('f.b', 0), 'f.bar') + self.assertEqual(f2.calls, 1) def test_property_method_not_called(self): class Foo: @@ -135,6 +152,57 @@ def bar(self): self.assertEqual(completer.complete('f.b', 0), 'f.bar') self.assertFalse(f.property_called) + def test_released_memoryview_completion_works(self): + mv = memoryview(b"abc") + mv.release() + + self.assertIsInstance(type(mv).shape, types.GetSetDescriptorType) + self.assertIsInstance(type(mv).strides, types.GetSetDescriptorType) + + completer = rlcompleter.Completer(dict(mv=mv)) + matches = completer.attr_matches('mv.') + + # These are getset descriptors on memoryview and should be completed + # without evaluating the released-memoryview getters. + self.assertIn('mv.shape', matches) + self.assertIn('mv.strides', matches) + + def test_member_descriptor_not_evaluated(self): + class Foo: + __slots__ = ("boom",) + boom_accesses = 0 + + def __getattribute__(self, name): + if name == "boom": + type(self).boom_accesses += 1 + raise RuntimeError("boom access should be skipped") + return super().__getattribute__(name) + + self.assertIsInstance(Foo.boom, types.MemberDescriptorType) + + completer = rlcompleter.Completer(dict(f=Foo())) + matches = completer.attr_matches('f.') + self.assertIn('f.boom', matches) + self.assertEqual(Foo.boom_accesses, 0) + + def test_raising_descriptor_completion_works(self): + class ExplodingDescriptor: + def __init__(self): + self.instance_get_calls = 0 + + def __get__(self, obj, owner): + if obj is None: + return self + self.instance_get_calls += 1 + raise RuntimeError("descriptor getter exploded") + + class Foo: + boom = ExplodingDescriptor() + + completer = rlcompleter.Completer(dict(f=Foo())) + matches = completer.attr_matches('f.') + self.assertIn('f.boom', matches) + self.assertEqual(Foo.boom.instance_get_calls, 0) def test_uncreated_attr(self): # Attributes like properties and slots should be completed even when @@ -144,6 +212,7 @@ class Foo: completer = rlcompleter.Completer(dict(f=Foo())) self.assertEqual(completer.complete('f.', 0), 'f.bar') + @unittest.mock.patch('rlcompleter._readline_available', False) def test_complete(self): completer = rlcompleter.Completer() diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py index 8d89e2a8224452..cd1477037e94b7 100644 --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -15,6 +15,18 @@ class BaseRobotTest: good = [] bad = [] site_maps = None + expected_output = None + + def __init_subclass__(cls): + super().__init_subclass__() + # Remove tests that do nothing. + if issubclass(cls, unittest.TestCase): + if not cls.good: + cls.test_good_urls = None + if not cls.bad: + cls.test_bad_urls = None + if cls.expected_output is None: + cls.test_string_formatting = None def setUp(self): lines = io.StringIO(self.robots_txt).readlines() @@ -42,6 +54,8 @@ def test_bad_urls(self): def test_site_maps(self): self.assertEqual(self.parser.site_maps(), self.site_maps) + def test_string_formatting(self): + self.assertEqual(str(self.parser), self.expected_output) class UserAgentWildcardTest(BaseRobotTest, unittest.TestCase): robots_txt = """\ @@ -53,6 +67,56 @@ class UserAgentWildcardTest(BaseRobotTest, unittest.TestCase): good = ['/', '/test.html'] bad = ['/cyberworld/map/index.html', '/tmp/xxx', '/foo.html'] +class SimpleExampleTest(BaseRobotTest, unittest.TestCase): + # Example from RFC 9309, section 5.1. + robots_txt = """\ +User-Agent: * +Disallow: *.gif$ +Disallow: /example/ +Allow: /publications/ + +User-Agent: foobot +Disallow:/ +Allow:/example/page.html +Allow:/example/allowed.gif + +User-Agent: barbot +User-Agent: bazbot +Disallow: /example/page.html + +User-Agent: quxbot + """ + good = [ + '/', '/publications/', + ('foobot', '/example/page.html'), ('foobot', '/example/allowed.gif'), + ('barbot', '/'), ('barbot', '/example/'), + ('barbot', '/example/allowed.gif'), + ('barbot', '/example/disallowed.gif'), + ('barbot', '/publications/'), + ('barbot', '/publications/allowed.gif'), + ('bazbot', '/'), ('bazbot', '/example/'), + ('bazbot', '/example/allowed.gif'), + ('bazbot', '/example/disallowed.gif'), + ('bazbot', '/publications/'), + ('bazbot', '/publications/allowed.gif'), + ('quxbot', '/'), ('quxbot', '/example/'), + ('quxbot', '/example/page.html'), ('quxbot', '/example/allowed.gif'), + ('quxbot', '/example/disallowed.gif'), + ('quxbot', '/publications/'), + ('quxbot', '/publications/allowed.gif'), + ] + bad = [ + '/example/', '/example/page.html', '/example/allowed.gif', + '/example/disallowed.gif', + '/publications/allowed.gif', + ('foobot', '/'), ('foobot', '/example/'), + ('foobot', '/example/disallowed.gif'), + ('foobot', '/publications/'), + ('foobot', '/publications/allowed.gif'), + ('barbot', '/example/page.html'), + ('bazbot', '/example/page.html'), + ] + class CrawlDelayAndCustomAgentTest(BaseRobotTest, unittest.TestCase): robots_txt = """\ @@ -94,7 +158,7 @@ class RejectAllRobotsTest(BaseRobotTest, unittest.TestCase): User-agent: * Disallow: / """ - good = [] + good = ['/robots.txt'] bad = ['/cyberworld/map/index.html', '/', '/tmp/'] @@ -129,6 +193,7 @@ def test_request_rate(self): class EmptyFileTest(BaseRequestRateTest, unittest.TestCase): robots_txt = '' good = ['/foo'] + expected_output = '' class CrawlDelayAndRequestRateTest(BaseRequestRateTest, unittest.TestCase): @@ -195,69 +260,302 @@ class AnotherInvalidRequestRateTest(BaseRobotTest, unittest.TestCase): class UserAgentOrderingTest(BaseRobotTest, unittest.TestCase): - # the order of User-agent should be correct. note - # that this file is incorrect because "Googlebot" is a - # substring of "Googlebot-Mobile" + # the order of User-agent should not matter robots_txt = """\ User-agent: Googlebot Disallow: / +Allow: /folder1/ User-agent: Googlebot-Mobile Allow: / +Disallow: /folder1/ """ agent = 'Googlebot' bad = ['/something.jpg'] + good = ['/folder1/myfile.html'] class UserAgentGoogleMobileTest(UserAgentOrderingTest): - agent = 'Googlebot-Mobile' + agent = 'Googlebot-mobile' + bad = ['/folder1/myfile.html'] + good = ['/something.jpg'] -class GoogleURLOrderingTest(BaseRobotTest, unittest.TestCase): - # Google also got the order wrong. You need - # to specify the URLs from more specific to more general +class LongestMatchTest(BaseRobotTest, unittest.TestCase): + # Based on example from RFC 9309, section 5.2. robots_txt = """\ -User-agent: Googlebot -Allow: /folder1/myfile.html -Disallow: /folder1/ +User-agent: * +Allow: /example/page/ +Disallow: /example/page/disallowed.gif +Allow: /example/ """ - agent = 'googlebot' - good = ['/folder1/myfile.html'] - bad = ['/folder1/anotherfile.html'] + good = ['/example/', '/example/page/'] + bad = ['/example/page/disallowed.gif'] -class DisallowQueryStringTest(BaseRobotTest, unittest.TestCase): - # see issue #6325 for details +class LongestMatchWildcardTest(BaseRobotTest, unittest.TestCase): robots_txt = """\ User-agent: * -Disallow: /some/path?name=value +Allow: /example/page/ +Disallow: *.gif +Allow: /example/ + """ + good = ['/example/', '/example/page/'] + bad = ['/example/page/disallowed.gif', '/x.gif'] + + +class AllowWinsEqualMatchTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ +User-agent: * +Disallow: /spam +Allow: /spam +Disallow: /spam """ - good = ['/some/path'] - bad = ['/some/path?name=value'] + good = ['/spam', '/spam/'] -class UseFirstUserAgentWildcardTest(BaseRobotTest, unittest.TestCase): - # obey first * entry (#4108) +class AllowWinsEqualFullMatchTest(BaseRobotTest, unittest.TestCase): robots_txt = """\ User-agent: * +Disallow: /spam +Allow: /spam$ +Disallow: /spam +Disallow: /eggs$ +Allow: /eggs +Disallow: /eggs$ + """ + good = ['/spam', '/eggs', '/eggs/'] + bad = ['/spam/'] + + +class AllowWinsEqualMatchWildcardTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ +User-agent: * +Disallow: /spam +Allow: *am +Disallow: /spam +Disallow: *gs +Allow: /eggs +Disallow: *gs + """ + good = ['/spam', '/eggs', '/spam/', '/eggs/'] + + +class MergeGroupsTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ +User-agent: spambot +Disallow: /some/path + +User-agent: spambot +Disallow: /another/path + """ + agent = 'spambot' + bad = ['/some/path', '/another/path'] + + +class UserAgentStartsGroupTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ +User-agent: spambot +Disallow: /some/path +User-agent: eggsbot +Disallow: /another/path + """ + good = [('spambot', '/'), ('spambot', '/another/path'), + ('eggsbot', '/'), ('eggsbot', '/some/path')] + bad = [('spambot', '/some/path'), ('eggsbot', '/another/path')] + expected_output = """\ +User-agent: spambot +Disallow: /some/path + +User-agent: eggsbot +Disallow: /another/path\ +""" + +class IgnoreEmptyLinesTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ +User-agent: spambot + +User-agent: eggsbot +Disallow: /some/path + +Disallow: /another/path + """ + good = [('spambot', '/'), ('eggsbot', '/')] + bad = [ + ('spambot', '/some/path'), ('spambot', '/another/path'), + ('eggsbot', '/some/path'), ('eggsbot', '/another/path'), + ] + expected_output = """\ +User-agent: spambot +User-agent: eggsbot +Disallow: /some/path +Disallow: /another/path\ +""" + + +class IgnoreRulesWithoutUserAgentTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ Disallow: /some/path User-agent: * Disallow: /another/path """ - good = ['/another/path'] - bad = ['/some/path'] + good = ['/', '/some/path'] + bad = ['/another/path'] + expected_output = """\ +User-agent: * +Disallow: /another/path\ +""" -class EmptyQueryStringTest(BaseRobotTest, unittest.TestCase): - # normalize the URL first (#17403) +class EmptyGroupTest(BaseRobotTest, unittest.TestCase): robots_txt = """\ User-agent: * -Allow: /some/path? +Disallow: /some/path + +User-agent: spambot + """ + agent = 'spambot' + good = ['/', '/some/path'] + expected_output = """\ +User-agent: * +Disallow: /some/path + +User-agent: spambot +Allow:\ +""" + + +class WeirdPathTest(BaseRobotTest, unittest.TestCase): + robots_txt = f"""\ +User-agent: * +Disallow: /a$$$ +Disallow: /b$z +Disallow: /c*** +Disallow: /d***z +Disallow: /e*$**$$ +Disallow: /f*$**$$z +Disallow: /g$*$$** +Disallow: /h$*$$**z + """ + good = ['/ax', '/a$$', '/b', '/bz', '/b$z', '/d', '/f', '/fz', + '/f$$$z', '/fx$y$$z', '/gx', '/g$$$', '/g$x$$y', '/h', '/hz', + '/h$$$z', '/h$x$$yz'] + bad = ['/a', '/c', '/cxy', '/dz', '/dxyz', '/dxzy', '/e', '/exy', + '/e$$', '/ex$y$', '/g'] + expected_output = """\ +User-agent: * +Disallow: /a$ +Disallow: /c* +Disallow: /d*z +Disallow: /e*$ +Disallow: /g$\ +""" + + +class PathWithManyWildcardsTest(BaseRobotTest, unittest.TestCase): + # This test would take many years if use naive translation to regular + # expression (* -> .*). + N = 50 + robots_txt = f"""\ +User-agent: * +Disallow: /{'*a'*N}*b + """ + good = ['/' + 'a'*N + 'a'] + bad = ['/' + 'a'*N + 'b'] + + +class DisallowQueryStringTest(BaseRobotTest, unittest.TestCase): + # see issue #6325 for details + robots_txt = """\ +User-agent: * +Disallow: /some/path?name=value Disallow: /another/path? +Disallow: /yet/one/path?name=value&more """ - good = ['/some/path?'] - bad = ['/another/path?'] + good = ['/some/path', '/some/path?', + '/some/path%3Fname=value', '/some/path?name%3Dvalue', + '/another/path', '/another/path%3F', + '/yet/one/path?name=value%26more', + '/some/pathxname=value'] + bad = ['/some/path?name=value' + '/another/path?', '/another/path?name=value', + '/yet/one/path?name=value&more'] + + +class PercentEncodingTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ +User-agent: * +Disallow: /a1/Z-._~ # unreserved characters +Disallow: /a2/%5A%2D%2E%5F%7E # percent-encoded unreserved characters +Disallow: /u1/%F0%9F%90%8D # percent-encoded ASCII Unicode character +Disallow: /u2/%f0%9f%90%8d +Disallow: /u3/\U0001f40d # raw non-ASCII Unicode character +Disallow: /v1/%F0 # percent-encoded non-ASCII octet +Disallow: /v2/%f0 +Disallow: /v3/\udcf0 # raw non-ASCII octet +Disallow: /p1%xy # raw percent +Disallow: /p2% +Disallow: /p3%25xy # percent-encoded percent +Disallow: /p4%2525xy # double percent-encoded percent +Disallow: /john%20smith # space +Disallow: /john doe +Disallow: /trailingspace%20 +Disallow: /question%3Fq=v # not query +Disallow: /hash%23f # not fragment +Disallow: /dollar%24 +Disallow: /asterisk%2A +Disallow: /sub/dir +Disallow: /slash%2F +Disallow: /query/question?q=%3F +Disallow: /query/raw/question?q=? +Disallow: /query/eq?q%3Dv +Disallow: /query/amp?q=v%26a +""" + good = [ + '/u1/%F0', '/u1/%f0', + '/u2/%F0', '/u2/%f0', + '/u3/%F0', '/u3/%f0', + '/p1%2525xy', '/p2%f0', '/p3%2525xy', '/p4%xy', '/p4%25xy', + '/question?q=v', + '/dollar', '/asterisk', + '/query/eq?q=v', + '/query/amp?q=v&a', + ] + bad = [ + '/a1/Z-._~', '/a1/%5A%2D%2E%5F%7E', + '/a2/Z-._~', '/a2/%5A%2D%2E%5F%7E', + '/u1/%F0%9F%90%8D', '/u1/%f0%9f%90%8d', '/u1/\U0001f40d', + '/u2/%F0%9F%90%8D', '/u2/%f0%9f%90%8d', '/u2/\U0001f40d', + '/u3/%F0%9F%90%8D', '/u3/%f0%9f%90%8d', '/u3/\U0001f40d', + '/v1/%F0', '/v1/%f0', '/v1/\udcf0', '/v1/\U0001f40d', + '/v2/%F0', '/v2/%f0', '/v2/\udcf0', '/v2/\U0001f40d', + '/v3/%F0', '/v3/%f0', '/v3/\udcf0', '/v3/\U0001f40d', + '/p1%xy', '/p1%25xy', + '/p2%', '/p2%25', '/p2%2525', '/p2%xy', + '/p3%xy', '/p3%25xy', + '/p4%2525xy', + '/john%20smith', '/john smith', + '/john%20doe', '/john doe', + '/trailingspace%20', '/trailingspace ', + '/question%3Fq=v', + '/hash#f', '/hash%23f', + '/dollar$', '/dollar%24', + '/asterisk*', '/asterisk%2A', + '/sub/dir', '/sub%2Fdir', + '/slash%2F', '/slash/', + '/query/question?q=?', '/query/question?q=%3F', + '/query/raw/question?q=?', '/query/raw/question?q=%3F', + '/query/eq?q%3Dv', + '/query/amp?q=v%26a', + ] + # other reserved characters + for c in ":/#[]@!$&'()*+,;=": + robots_txt += f'Disallow: /raw{c}\nDisallow: /pc%{ord(c):02X}\n' + bad.append(f'/raw{c}') + bad.append(f'/raw%{ord(c):02X}') + bad.append(f'/pc{c}') + bad.append(f'/pc%{ord(c):02X}') class DefaultEntryTest(BaseRequestRateTest, unittest.TestCase): @@ -286,64 +584,193 @@ class StringFormattingTest(BaseRobotTest, unittest.TestCase): """ expected_output = """\ -User-agent: cybermapper -Disallow: /some/path - User-agent: * Crawl-delay: 1 Request-rate: 3/15 -Disallow: /cyberworld/map/\ -""" - - def test_string_formatting(self): - self.assertEqual(str(self.parser), self.expected_output) +Disallow: /cyberworld/map/ +User-agent: cybermapper +Disallow: /some/path\ +""" -class RobotHandler(BaseHTTPRequestHandler): - def do_GET(self): - self.send_error(403, "Forbidden access") +class ConstructedStringFormattingTest(unittest.TestCase): + def test_empty(self): + parser = urllib.robotparser.RobotFileParser() + self.assertEqual(str(parser), '') - def log_message(self, format, *args): - pass + def test_group_without_rules(self): + parser = urllib.robotparser.RobotFileParser() + entry = urllib.robotparser.Entry() + entry.useragents = ['spambot'] + parser._add_entry(entry) + entry = urllib.robotparser.Entry() + entry.useragents = ['hambot'] + entry.rulelines = [urllib.robotparser.RuleLine('/ham', False)] + parser._add_entry(entry) + entry = urllib.robotparser.Entry() + entry.useragents = ['eggsbot'] + parser._add_entry(entry) + self.assertEqual(str(parser), """\ +User-agent: spambot +Allow: + +User-agent: hambot +Disallow: /ham + +User-agent: eggsbot +Allow:\ +""") + + def test_group_without_user_agent(self): + parser = urllib.robotparser.RobotFileParser() + entry = urllib.robotparser.Entry() + entry.rulelines = [urllib.robotparser.RuleLine('/ham', False)] + parser._add_entry(entry) + entry = urllib.robotparser.Entry() + entry.useragents = ['spambot'] + entry.rulelines = [urllib.robotparser.RuleLine('/spam', False)] + parser._add_entry(entry) + entry = urllib.robotparser.Entry() + entry.rulelines = [urllib.robotparser.RuleLine('/eggs', False)] + parser._add_entry(entry) + self.assertEqual(str(parser), """\ +User-agent: spambot +Disallow: /spam\ +""") @unittest.skipUnless( support.has_socket_support, "Socket server requires working socket." ) -class PasswordProtectedSiteTestCase(unittest.TestCase): +class BaseLocalNetworkTestCase: - def setUp(self): + @classmethod + def setUpClass(cls): # clear _opener global variable - self.addCleanup(urllib.request.urlcleanup) + cls.addClassCleanup(urllib.request.urlcleanup) - self.server = HTTPServer((socket_helper.HOST, 0), RobotHandler) + cls.server = HTTPServer((socket_helper.HOST, 0), cls.RobotHandler) + cls.addClassCleanup(cls.server.server_close) - self.t = threading.Thread( + t = threading.Thread( name='HTTPServer serving', - target=self.server.serve_forever, + target=cls.server.serve_forever, # Short poll interval to make the test finish quickly. # Time between requests is short enough that we won't wake # up spuriously too many times. kwargs={'poll_interval':0.01}) - self.t.daemon = True # In case this function raises. - self.t.start() + cls.enterClassContext(threading_helper.start_threads([t])) + cls.addClassCleanup(cls.server.shutdown) + + +SAMPLE_ROBOTS_TXT = b'''\ +User-agent: test_robotparser +Disallow: /utf8/\xf0\x9f\x90\x8d +Disallow: /non-utf8/\xf0 +Disallow: //[spam]/path +''' + - def tearDown(self): - self.server.shutdown() - self.t.join() - self.server.server_close() +class LocalNetworkTestCase(BaseLocalNetworkTestCase, unittest.TestCase): + class RobotHandler(BaseHTTPRequestHandler): + + def do_GET(self): + self.send_response(200) + self.end_headers() + self.wfile.write(SAMPLE_ROBOTS_TXT) + + def log_message(self, format, *args): + pass + + def testRead(self): + # Test that reading a weird robots.txt doesn't fail. + addr = self.server.server_address + url = f'http://{socket_helper.HOST}:{addr[1]}' + robots_url = url + '/robots.txt' + parser = urllib.robotparser.RobotFileParser() + parser.set_url(robots_url) + parser.read() + # And it can even interpret the weird paths in some reasonable way. + agent = 'test_robotparser' + self.assertTrue(parser.can_fetch(agent, robots_url)) + self.assertTrue(parser.can_fetch(agent, url + '/utf8/')) + self.assertFalse(parser.can_fetch(agent, url + '/utf8/\U0001f40d')) + self.assertFalse(parser.can_fetch(agent, url + '/utf8/%F0%9F%90%8D')) + self.assertTrue(parser.can_fetch(agent, url + '/non-utf8/')) + self.assertFalse(parser.can_fetch(agent, url + '/non-utf8/%F0')) + self.assertFalse(parser.can_fetch(agent, url + '/non-utf8/\U0001f40d')) + self.assertFalse(parser.can_fetch(agent, url + '/%2F[spam]/path')) + + +class HttpErrorsTestCase(BaseLocalNetworkTestCase, unittest.TestCase): + class RobotHandler(BaseHTTPRequestHandler): + + def do_GET(self): + self.send_error(self.server.return_code) + + def log_message(self, format, *args): + pass + + def setUp(self): + # Make sure that a valid code is set in the test. + self.server.return_code = None + + def testUnauthorized(self): + self.server.return_code = 401 + addr = self.server.server_address + url = f'http://{socket_helper.HOST}:{addr[1]}' + robots_url = url + "/robots.txt" + parser = urllib.robotparser.RobotFileParser() + parser.set_url(url) + parser.read() + self.assertFalse(parser.can_fetch("*", robots_url)) + self.assertFalse(parser.can_fetch("*", url + '/some/file.html')) + + def testForbidden(self): + self.server.return_code = 403 + addr = self.server.server_address + url = f'http://{socket_helper.HOST}:{addr[1]}' + robots_url = url + "/robots.txt" + parser = urllib.robotparser.RobotFileParser() + parser.set_url(url) + parser.read() + self.assertFalse(parser.can_fetch("*", robots_url)) + self.assertFalse(parser.can_fetch("*", url + '/some/file.html')) + + def testNotFound(self): + self.server.return_code = 404 + addr = self.server.server_address + url = f'http://{socket_helper.HOST}:{addr[1]}' + robots_url = url + "/robots.txt" + parser = urllib.robotparser.RobotFileParser() + parser.set_url(url) + parser.read() + self.assertTrue(parser.can_fetch("*", robots_url)) + self.assertTrue(parser.can_fetch("*", url + '/path/file.html')) + + def testTeapot(self): + self.server.return_code = 418 + addr = self.server.server_address + url = f'http://{socket_helper.HOST}:{addr[1]}' + robots_url = url + "/robots.txt" + parser = urllib.robotparser.RobotFileParser() + parser.set_url(url) + parser.read() + self.assertTrue(parser.can_fetch("*", robots_url)) + self.assertTrue(parser.can_fetch("*", url + '/pot-1?milk-type=Cream')) - @threading_helper.reap_threads - def testPasswordProtectedSite(self): + def testServiceUnavailable(self): + self.server.return_code = 503 addr = self.server.server_address - url = 'http://' + socket_helper.HOST + ':' + str(addr[1]) + url = f'http://{socket_helper.HOST}:{addr[1]}' robots_url = url + "/robots.txt" parser = urllib.robotparser.RobotFileParser() parser.set_url(url) parser.read() self.assertFalse(parser.can_fetch("*", robots_url)) + self.assertFalse(parser.can_fetch("*", url + '/path/file.html')) @support.requires_working_socket() @@ -355,6 +782,7 @@ class NetworkTestCase(unittest.TestCase): @classmethod def setUpClass(cls): support.requires('network') + cls.addClassCleanup(urllib.request.urlcleanup) with socket_helper.transient_internet(cls.base_url): cls.parser = urllib.robotparser.RobotFileParser(cls.robots_txt) cls.parser.read() @@ -374,7 +802,7 @@ def test_basic(self): def test_can_fetch(self): self.assertTrue(self.parser.can_fetch('*', self.url('elsewhere'))) self.assertFalse(self.parser.can_fetch('Nutch', self.base_url)) - self.assertFalse(self.parser.can_fetch('Nutch', self.url('brian'))) + self.assertTrue(self.parser.can_fetch('Nutch', self.url('brian'))) self.assertFalse(self.parser.can_fetch('Nutch', self.url('webstats'))) self.assertFalse(self.parser.can_fetch('*', self.url('webstats'))) self.assertTrue(self.parser.can_fetch('*', self.base_url)) diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py index a2a07c04f58ef2..55b9673ef6c91c 100644 --- a/Lib/test/test_runpy.py +++ b/Lib/test/test_runpy.py @@ -20,9 +20,11 @@ requires_subprocess, verbose, ) +from test import support from test.support.import_helper import forget, make_legacy_pyc, unload from test.support.os_helper import create_empty_file, temp_dir, FakePath from test.support.script_helper import make_script, make_zip_script +from test.test_importlib.util import temporary_pycache_prefix import runpy @@ -55,7 +57,6 @@ def f(): implicit_namespace = { "__name__": None, "__file__": None, - "__cached__": None, "__package__": None, "__doc__": None, "__spec__": None @@ -216,6 +217,25 @@ def test_invalid_names(self): # Package without __main__.py self.expect_import_error("multiprocessing") + def test_invalid_names_set_name_attribute(self): + cases = [ + # (mod_name, expected_name) -- comment indicates raise site + ("nonexistent_runpy_test_module", + "nonexistent_runpy_test_module"), # spec is None + ("sys.imp.eric", "sys.imp.eric"), # find_spec error + (".relative_name", ".relative_name"), # relative name rejected + ("sys", "sys"), # builtin: no code object + ("multiprocessing", "multiprocessing"), # package without __main__ + ] + for mod_name, expected_name in cases: + with self.subTest(mod_name=mod_name): + try: + run_module(mod_name) + except ImportError as exc: + self.assertEqual(exc.name, expected_name) + else: + self.fail("Expected ImportError for %r" % mod_name) + def test_library_module(self): self.assertEqual(run_module("runpy")["__name__"], "runpy") @@ -284,7 +304,6 @@ def _del_pkg(self, top): def _fix_ns_for_legacy_pyc(self, ns, alter_sys): char_to_add = "c" ns["__file__"] += char_to_add - ns["__cached__"] = ns["__file__"] spec = ns["__spec__"] new_spec = importlib.util.spec_from_file_location(spec.name, ns["__file__"]) @@ -304,7 +323,6 @@ def _check_module(self, depth, alter_sys=False, expected_ns.update({ "__name__": mod_name, "__file__": mod_fname, - "__cached__": mod_spec.cached, "__package__": mod_name.rpartition(".")[0], "__spec__": mod_spec, }) @@ -321,14 +339,16 @@ def create_ns(init_globals): self.check_code_execution(create_ns, expected_ns) importlib.invalidate_caches() __import__(mod_name) - os.remove(mod_fname) if not sys.dont_write_bytecode: - make_legacy_pyc(mod_fname) + make_legacy_pyc(mod_fname, allow_compile=True) unload(mod_name) # In case loader caches paths + os.remove(mod_fname) importlib.invalidate_caches() if verbose > 1: print("Running from compiled:", mod_name) self._fix_ns_for_legacy_pyc(expected_ns, alter_sys) self.check_code_execution(create_ns, expected_ns) + else: + os.remove(mod_fname) finally: self._del_pkg(pkg_dir) if verbose > 1: print("Module executed successfully") @@ -345,7 +365,6 @@ def _check_package(self, depth, alter_sys=False, expected_ns.update({ "__name__": mod_name, "__file__": mod_fname, - "__cached__": importlib.util.cache_from_source(mod_fname), "__package__": pkg_name, "__spec__": mod_spec, }) @@ -362,14 +381,16 @@ def create_ns(init_globals): self.check_code_execution(create_ns, expected_ns) importlib.invalidate_caches() __import__(mod_name) - os.remove(mod_fname) if not sys.dont_write_bytecode: - make_legacy_pyc(mod_fname) + make_legacy_pyc(mod_fname, allow_compile=True) unload(mod_name) # In case loader caches paths + os.remove(mod_fname) if verbose > 1: print("Running from compiled:", pkg_name) importlib.invalidate_caches() self._fix_ns_for_legacy_pyc(expected_ns, alter_sys) self.check_code_execution(create_ns, expected_ns) + else: + os.remove(mod_fname) finally: self._del_pkg(pkg_dir) if verbose > 1: print("Package executed successfully") @@ -422,7 +443,7 @@ def _check_relative_imports(self, depth, run_name=None): importlib.invalidate_caches() __import__(mod_name) os.remove(mod_fname) - if not sys.dont_write_bytecode: + if not sys.dont_write_bytecode and sys.implementation.cache_tag: make_legacy_pyc(mod_fname) unload(mod_name) # In case the loader caches paths if verbose > 1: print("Running from compiled:", mod_name) @@ -550,7 +571,6 @@ def test_run_name(self): expected_ns.update({ "__name__": run_name, "__file__": mod_fname, - "__cached__": importlib.util.cache_from_source(mod_fname), "__package__": mod_name.rpartition(".")[0], "__spec__": mod_spec, }) @@ -630,7 +650,6 @@ def create_ns(init_globals): expected_ns.update({ "__name__": expected_name, "__file__": expected_file, - "__cached__": mod_cached, "__package__": "", "__spec__": mod_spec, "run_argv0": expected_argv0, @@ -680,6 +699,8 @@ def test_basic_script_no_suffix(self): self._check_script(script_name, "<run_path>", script_name, script_name, expect_spec=False) + @unittest.skipIf(sys.implementation.cache_tag is None, + 'requires sys.implementation.cache_tag') def test_script_compiled(self): with temp_dir() as script_dir: mod_name = 'script' @@ -700,12 +721,10 @@ def test_directory_compiled(self): with temp_dir() as script_dir: mod_name = '__main__' script_name = self._make_test_script(script_dir, mod_name) - compiled_name = py_compile.compile(script_name, doraise=True) + legacy_pyc = make_legacy_pyc(script_name, allow_compile=True) os.remove(script_name) - if not sys.dont_write_bytecode: - legacy_pyc = make_legacy_pyc(script_name) - self._check_script(script_dir, "<run_path>", legacy_pyc, - script_dir, mod_name=mod_name) + self._check_script(script_dir, "<run_path>", legacy_pyc, + script_dir, mod_name=mod_name) def test_directory_error(self): with temp_dir() as script_dir: @@ -714,6 +733,17 @@ def test_directory_error(self): msg = "can't find '__main__' module in %r" % script_dir self._check_import_error(script_dir, msg) + def test_directory_error_sets_name_attribute(self): + with temp_dir() as script_dir: + self._make_test_script(script_dir, 'not_main') + try: + run_path(script_dir) + except ImportError as exc: + self.assertEqual(exc.name, '__main__') + else: + self.fail("Expected ImportError for directory without " + "__main__.py") + def test_zipfile(self): with temp_dir() as script_dir: mod_name = '__main__' @@ -726,7 +756,8 @@ def test_zipfile_compiled(self): with temp_dir() as script_dir: mod_name = '__main__' script_name = self._make_test_script(script_dir, mod_name) - compiled_name = py_compile.compile(script_name, doraise=True) + compiled_name = script_name + 'c' + py_compile.compile(script_name, compiled_name, doraise=True) zip_name, fname = make_zip_script(script_dir, 'test_zip', compiled_name) self._check_script(zip_name, "<run_path>", fname, zip_name, @@ -763,6 +794,47 @@ def test_encoding(self): result = run_path(filename) self.assertEqual(result['s'], "non-ASCII: h\xe9") + def test_run_module_filter_syntax_warnings_by_module(self): + module_re = r'test\.test_import\.data\.syntax_warnings\z' + with (temp_dir() as tmpdir, + temporary_pycache_prefix(tmpdir), + warnings.catch_warnings(record=True) as wlog): + warnings.simplefilter('error') + warnings.filterwarnings('always', module=module_re) + warnings.filterwarnings('error', module='syntax_warnings') + ns = run_module('test.test_import.data.syntax_warnings') + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21]) + filename = ns['__file__'] + for wm in wlog: + self.assertEqual(wm.filename, filename) + self.assertIs(wm.category, SyntaxWarning) + + def test_run_path_filter_syntax_warnings_by_module(self): + filename = support.findfile('test_import/data/syntax_warnings.py') + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=r'<run_path>\z') + warnings.filterwarnings('error', module='test') + warnings.filterwarnings('error', module='syntax_warnings') + warnings.filterwarnings('error', + module=r'test\.test_import\.data\.syntax_warnings') + run_path(filename) + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21]) + for wm in wlog: + self.assertEqual(wm.filename, filename) + self.assertIs(wm.category, SyntaxWarning) + + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=r'package\.script\z') + warnings.filterwarnings('error', module='<run_path>') + warnings.filterwarnings('error', module='test') + warnings.filterwarnings('error', module='syntax_warnings') + warnings.filterwarnings('error', + module=r'test\.test_import\.data\.syntax_warnings') + run_path(filename, run_name='package.script') + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21]) + @force_not_colorized_test_class class TestExit(unittest.TestCase): diff --git a/Lib/test/test_sample_profiler.py b/Lib/test/test_sample_profiler.py deleted file mode 100644 index 2c7fa1cba712c9..00000000000000 --- a/Lib/test/test_sample_profiler.py +++ /dev/null @@ -1,1877 +0,0 @@ -"""Tests for the sampling profiler (profile.sample).""" - -import contextlib -import io -import marshal -import os -import socket -import subprocess -import sys -import tempfile -import unittest -from unittest import mock - -from profile.pstats_collector import PstatsCollector -from profile.stack_collector import ( - CollapsedStackCollector, -) - -from test.support.os_helper import unlink -from test.support import force_not_colorized_test_class, SHORT_TIMEOUT -from test.support.socket_helper import find_unused_port -from test.support import requires_subprocess - -PROCESS_VM_READV_SUPPORTED = False - -try: - from _remote_debugging import PROCESS_VM_READV_SUPPORTED - import _remote_debugging -except ImportError: - raise unittest.SkipTest( - "Test only runs when _remote_debugging is available" - ) -else: - import profile.sample - from profile.sample import SampleProfiler - - - -class MockFrameInfo: - """Mock FrameInfo for testing since the real one isn't accessible.""" - - def __init__(self, filename, lineno, funcname): - self.filename = filename - self.lineno = lineno - self.funcname = funcname - - def __repr__(self): - return f"MockFrameInfo(filename='{self.filename}', lineno={self.lineno}, funcname='{self.funcname}')" - - -skip_if_not_supported = unittest.skipIf( - ( - sys.platform != "darwin" - and sys.platform != "linux" - and sys.platform != "win32" - ), - "Test only runs on Linux, Windows and MacOS", -) - - -@contextlib.contextmanager -def test_subprocess(script): - # Find an unused port for socket communication - port = find_unused_port() - - # Inject socket connection code at the beginning of the script - socket_code = f''' -import socket -_test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -_test_sock.connect(('localhost', {port})) -_test_sock.sendall(b"ready") -''' - - # Combine socket code with user script - full_script = socket_code + script - - # Create server socket to wait for process to be ready - server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - server_socket.bind(("localhost", port)) - server_socket.settimeout(SHORT_TIMEOUT) - server_socket.listen(1) - - proc = subprocess.Popen( - [sys.executable, "-c", full_script], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - - client_socket = None - try: - # Wait for process to connect and send ready signal - client_socket, _ = server_socket.accept() - server_socket.close() - response = client_socket.recv(1024) - if response != b"ready": - raise RuntimeError(f"Unexpected response from subprocess: {response}") - - yield proc - finally: - if client_socket is not None: - client_socket.close() - if proc.poll() is None: - proc.kill() - proc.wait() - - -def close_and_unlink(file): - file.close() - unlink(file.name) - - -class TestSampleProfilerComponents(unittest.TestCase): - """Unit tests for individual profiler components.""" - - def test_mock_frame_info_with_empty_and_unicode_values(self): - """Test MockFrameInfo handles empty strings, unicode characters, and very long names correctly.""" - # Test with empty strings - frame = MockFrameInfo("", 0, "") - self.assertEqual(frame.filename, "") - self.assertEqual(frame.lineno, 0) - self.assertEqual(frame.funcname, "") - self.assertIn("filename=''", repr(frame)) - - # Test with unicode characters - frame = MockFrameInfo("文件.py", 42, "函数名") - self.assertEqual(frame.filename, "文件.py") - self.assertEqual(frame.funcname, "函数名") - - # Test with very long names - long_filename = "x" * 1000 + ".py" - long_funcname = "func_" + "x" * 1000 - frame = MockFrameInfo(long_filename, 999999, long_funcname) - self.assertEqual(frame.filename, long_filename) - self.assertEqual(frame.lineno, 999999) - self.assertEqual(frame.funcname, long_funcname) - - def test_pstats_collector_with_extreme_intervals_and_empty_data(self): - """Test PstatsCollector handles zero/large intervals, empty frames, None thread IDs, and duplicate frames.""" - # Test with zero interval - collector = PstatsCollector(sample_interval_usec=0) - self.assertEqual(collector.sample_interval_usec, 0) - - # Test with very large interval - collector = PstatsCollector(sample_interval_usec=1000000000) - self.assertEqual(collector.sample_interval_usec, 1000000000) - - # Test collecting empty frames list - collector = PstatsCollector(sample_interval_usec=1000) - collector.collect([]) - self.assertEqual(len(collector.result), 0) - - # Test collecting frames with None thread id - test_frames = [(None, [MockFrameInfo("file.py", 10, "func")])] - collector.collect(test_frames) - # Should still process the frames - self.assertEqual(len(collector.result), 1) - - # Test collecting duplicate frames in same sample - test_frames = [ - ( - 1, - [ - MockFrameInfo("file.py", 10, "func1"), - MockFrameInfo("file.py", 10, "func1"), # Duplicate - ], - ) - ] - collector = PstatsCollector(sample_interval_usec=1000) - collector.collect(test_frames) - # Should count both occurrences - self.assertEqual( - collector.result[("file.py", 10, "func1")]["cumulative_calls"], 2 - ) - - def test_pstats_collector_single_frame_stacks(self): - """Test PstatsCollector with single-frame call stacks to trigger len(frames) <= 1 branch.""" - collector = PstatsCollector(sample_interval_usec=1000) - - # Test with exactly one frame (should trigger the <= 1 condition) - single_frame = [(1, [MockFrameInfo("single.py", 10, "single_func")])] - collector.collect(single_frame) - - # Should record the single frame with inline call - self.assertEqual(len(collector.result), 1) - single_key = ("single.py", 10, "single_func") - self.assertIn(single_key, collector.result) - self.assertEqual(collector.result[single_key]["direct_calls"], 1) - self.assertEqual(collector.result[single_key]["cumulative_calls"], 1) - - # Test with empty frames (should also trigger <= 1 condition) - empty_frames = [(1, [])] - collector.collect(empty_frames) - - # Should not add any new entries - self.assertEqual( - len(collector.result), 1 - ) # Still just the single frame - - # Test mixed single and multi-frame stacks - mixed_frames = [ - ( - 1, - [MockFrameInfo("single2.py", 20, "single_func2")], - ), # Single frame - ( - 2, - [ # Multi-frame stack - MockFrameInfo("multi.py", 30, "multi_func1"), - MockFrameInfo("multi.py", 40, "multi_func2"), - ], - ), - ] - collector.collect(mixed_frames) - - # Should have recorded all functions - self.assertEqual( - len(collector.result), 4 - ) # single + single2 + multi1 + multi2 - - # Verify single frame handling - single2_key = ("single2.py", 20, "single_func2") - self.assertIn(single2_key, collector.result) - self.assertEqual(collector.result[single2_key]["direct_calls"], 1) - self.assertEqual(collector.result[single2_key]["cumulative_calls"], 1) - - # Verify multi-frame handling still works - multi1_key = ("multi.py", 30, "multi_func1") - multi2_key = ("multi.py", 40, "multi_func2") - self.assertIn(multi1_key, collector.result) - self.assertIn(multi2_key, collector.result) - self.assertEqual(collector.result[multi1_key]["direct_calls"], 1) - self.assertEqual( - collector.result[multi2_key]["cumulative_calls"], 1 - ) # Called from multi1 - - def test_collapsed_stack_collector_with_empty_and_deep_stacks(self): - """Test CollapsedStackCollector handles empty frames, single-frame stacks, and very deep call stacks.""" - collector = CollapsedStackCollector() - - # Test with empty frames - collector.collect([]) - self.assertEqual(len(collector.call_trees), 0) - - # Test with single frame stack - test_frames = [(1, [("file.py", 10, "func")])] - collector.collect(test_frames) - self.assertEqual(len(collector.call_trees), 1) - self.assertEqual(collector.call_trees[0], [("file.py", 10, "func")]) - - # Test with very deep stack - deep_stack = [(f"file{i}.py", i, f"func{i}") for i in range(100)] - test_frames = [(1, deep_stack)] - collector = CollapsedStackCollector() - collector.collect(test_frames) - self.assertEqual(len(collector.call_trees[0]), 100) - # Check it's properly reversed - self.assertEqual( - collector.call_trees[0][0], ("file99.py", 99, "func99") - ) - self.assertEqual(collector.call_trees[0][-1], ("file0.py", 0, "func0")) - - def test_pstats_collector_basic(self): - """Test basic PstatsCollector functionality.""" - collector = PstatsCollector(sample_interval_usec=1000) - - # Test empty state - self.assertEqual(len(collector.result), 0) - self.assertEqual(len(collector.stats), 0) - - # Test collecting sample data - test_frames = [ - ( - 1, - [ - MockFrameInfo("file.py", 10, "func1"), - MockFrameInfo("file.py", 20, "func2"), - ], - ) - ] - collector.collect(test_frames) - - # Should have recorded calls for both functions - self.assertEqual(len(collector.result), 2) - self.assertIn(("file.py", 10, "func1"), collector.result) - self.assertIn(("file.py", 20, "func2"), collector.result) - - # Top-level function should have direct call - self.assertEqual( - collector.result[("file.py", 10, "func1")]["direct_calls"], 1 - ) - self.assertEqual( - collector.result[("file.py", 10, "func1")]["cumulative_calls"], 1 - ) - - # Calling function should have cumulative call but no direct calls - self.assertEqual( - collector.result[("file.py", 20, "func2")]["cumulative_calls"], 1 - ) - self.assertEqual( - collector.result[("file.py", 20, "func2")]["direct_calls"], 0 - ) - - def test_pstats_collector_create_stats(self): - """Test PstatsCollector stats creation.""" - collector = PstatsCollector( - sample_interval_usec=1000000 - ) # 1 second intervals - - test_frames = [ - ( - 1, - [ - MockFrameInfo("file.py", 10, "func1"), - MockFrameInfo("file.py", 20, "func2"), - ], - ) - ] - collector.collect(test_frames) - collector.collect(test_frames) # Collect twice - - collector.create_stats() - - # Check stats format: (direct_calls, cumulative_calls, tt, ct, callers) - func1_stats = collector.stats[("file.py", 10, "func1")] - self.assertEqual(func1_stats[0], 2) # direct_calls (top of stack) - self.assertEqual(func1_stats[1], 2) # cumulative_calls - self.assertEqual( - func1_stats[2], 2.0 - ) # tt (total time - 2 samples * 1 sec) - self.assertEqual(func1_stats[3], 2.0) # ct (cumulative time) - - func2_stats = collector.stats[("file.py", 20, "func2")] - self.assertEqual( - func2_stats[0], 0 - ) # direct_calls (never top of stack) - self.assertEqual( - func2_stats[1], 2 - ) # cumulative_calls (appears in stack) - self.assertEqual(func2_stats[2], 0.0) # tt (no direct calls) - self.assertEqual(func2_stats[3], 2.0) # ct (cumulative time) - - def test_collapsed_stack_collector_basic(self): - collector = CollapsedStackCollector() - - # Test empty state - self.assertEqual(len(collector.call_trees), 0) - self.assertEqual(len(collector.function_samples), 0) - - # Test collecting sample data - test_frames = [ - (1, [("file.py", 10, "func1"), ("file.py", 20, "func2")]) - ] - collector.collect(test_frames) - - # Should store call tree (reversed) - self.assertEqual(len(collector.call_trees), 1) - expected_tree = [("file.py", 20, "func2"), ("file.py", 10, "func1")] - self.assertEqual(collector.call_trees[0], expected_tree) - - # Should count function samples - self.assertEqual( - collector.function_samples[("file.py", 10, "func1")], 1 - ) - self.assertEqual( - collector.function_samples[("file.py", 20, "func2")], 1 - ) - - def test_collapsed_stack_collector_export(self): - collapsed_out = tempfile.NamedTemporaryFile(delete=False) - self.addCleanup(close_and_unlink, collapsed_out) - - collector = CollapsedStackCollector() - - test_frames1 = [ - (1, [("file.py", 10, "func1"), ("file.py", 20, "func2")]) - ] - test_frames2 = [ - (1, [("file.py", 10, "func1"), ("file.py", 20, "func2")]) - ] # Same stack - test_frames3 = [(1, [("other.py", 5, "other_func")])] - - collector.collect(test_frames1) - collector.collect(test_frames2) - collector.collect(test_frames3) - - collector.export(collapsed_out.name) - # Check file contents - with open(collapsed_out.name, "r") as f: - content = f.read() - - lines = content.strip().split("\n") - self.assertEqual(len(lines), 2) # Two unique stacks - - # Check collapsed format: file:func:line;file:func:line count - stack1_expected = "file.py:func2:20;file.py:func1:10 2" - stack2_expected = "other.py:other_func:5 1" - - self.assertIn(stack1_expected, lines) - self.assertIn(stack2_expected, lines) - - def test_pstats_collector_export(self): - collector = PstatsCollector( - sample_interval_usec=1000000 - ) # 1 second intervals - - test_frames1 = [ - ( - 1, - [ - MockFrameInfo("file.py", 10, "func1"), - MockFrameInfo("file.py", 20, "func2"), - ], - ) - ] - test_frames2 = [ - ( - 1, - [ - MockFrameInfo("file.py", 10, "func1"), - MockFrameInfo("file.py", 20, "func2"), - ], - ) - ] # Same stack - test_frames3 = [(1, [MockFrameInfo("other.py", 5, "other_func")])] - - collector.collect(test_frames1) - collector.collect(test_frames2) - collector.collect(test_frames3) - - pstats_out = tempfile.NamedTemporaryFile( - suffix=".pstats", delete=False - ) - self.addCleanup(close_and_unlink, pstats_out) - collector.export(pstats_out.name) - - # Check file can be loaded with marshal - with open(pstats_out.name, "rb") as f: - stats_data = marshal.load(f) - - # Should be a dictionary with the sampled marker - self.assertIsInstance(stats_data, dict) - self.assertIn(("__sampled__",), stats_data) - self.assertTrue(stats_data[("__sampled__",)]) - - # Should have function data - function_entries = [ - k for k in stats_data.keys() if k != ("__sampled__",) - ] - self.assertGreater(len(function_entries), 0) - - # Check specific function stats format: (cc, nc, tt, ct, callers) - func1_key = ("file.py", 10, "func1") - func2_key = ("file.py", 20, "func2") - other_key = ("other.py", 5, "other_func") - - self.assertIn(func1_key, stats_data) - self.assertIn(func2_key, stats_data) - self.assertIn(other_key, stats_data) - - # Check func1 stats (should have 2 samples) - func1_stats = stats_data[func1_key] - self.assertEqual(func1_stats[0], 2) # total_calls - self.assertEqual(func1_stats[1], 2) # nc (non-recursive calls) - self.assertEqual(func1_stats[2], 2.0) # tt (total time) - self.assertEqual(func1_stats[3], 2.0) # ct (cumulative time) - - -class TestSampleProfiler(unittest.TestCase): - """Test the SampleProfiler class.""" - - def test_sample_profiler_initialization(self): - """Test SampleProfiler initialization with various parameters.""" - from profile.sample import SampleProfiler - - # Mock RemoteUnwinder to avoid permission issues - with mock.patch( - "_remote_debugging.RemoteUnwinder" - ) as mock_unwinder_class: - mock_unwinder_class.return_value = mock.MagicMock() - - # Test basic initialization - profiler = SampleProfiler( - pid=12345, sample_interval_usec=1000, all_threads=False - ) - self.assertEqual(profiler.pid, 12345) - self.assertEqual(profiler.sample_interval_usec, 1000) - self.assertEqual(profiler.all_threads, False) - - # Test with all_threads=True - profiler = SampleProfiler( - pid=54321, sample_interval_usec=5000, all_threads=True - ) - self.assertEqual(profiler.pid, 54321) - self.assertEqual(profiler.sample_interval_usec, 5000) - self.assertEqual(profiler.all_threads, True) - - def test_sample_profiler_sample_method_timing(self): - """Test that the sample method respects duration and handles timing correctly.""" - from profile.sample import SampleProfiler - - # Mock the unwinder to avoid needing a real process - mock_unwinder = mock.MagicMock() - mock_unwinder.get_stack_trace.return_value = [ - ( - 1, - [ - mock.MagicMock( - filename="test.py", lineno=10, funcname="test_func" - ) - ], - ) - ] - - with mock.patch( - "_remote_debugging.RemoteUnwinder" - ) as mock_unwinder_class: - mock_unwinder_class.return_value = mock_unwinder - - profiler = SampleProfiler( - pid=12345, sample_interval_usec=100000, all_threads=False - ) # 100ms interval - - # Mock collector - mock_collector = mock.MagicMock() - - # Mock time to control the sampling loop - start_time = 1000.0 - times = [ - start_time + i * 0.1 for i in range(12) - ] # 0, 0.1, 0.2, ..., 1.1 seconds - - with mock.patch("time.perf_counter", side_effect=times): - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - profiler.sample(mock_collector, duration_sec=1) - - result = output.getvalue() - - # Should have captured approximately 10 samples (1 second / 0.1 second interval) - self.assertIn("Captured", result) - self.assertIn("samples", result) - - # Verify collector was called multiple times - self.assertGreaterEqual(mock_collector.collect.call_count, 5) - self.assertLessEqual(mock_collector.collect.call_count, 11) - - def test_sample_profiler_error_handling(self): - """Test that the sample method handles errors gracefully.""" - from profile.sample import SampleProfiler - - # Mock unwinder that raises errors - mock_unwinder = mock.MagicMock() - error_sequence = [ - RuntimeError("Process died"), - [ - ( - 1, - [ - mock.MagicMock( - filename="test.py", lineno=10, funcname="test_func" - ) - ], - ) - ], - UnicodeDecodeError("utf-8", b"", 0, 1, "invalid"), - [ - ( - 1, - [ - mock.MagicMock( - filename="test.py", - lineno=20, - funcname="test_func2", - ) - ], - ) - ], - OSError("Permission denied"), - ] - mock_unwinder.get_stack_trace.side_effect = error_sequence - - with mock.patch( - "_remote_debugging.RemoteUnwinder" - ) as mock_unwinder_class: - mock_unwinder_class.return_value = mock_unwinder - - profiler = SampleProfiler( - pid=12345, sample_interval_usec=10000, all_threads=False - ) - - mock_collector = mock.MagicMock() - - # Control timing to run exactly 5 samples - times = [0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06] - - with mock.patch("time.perf_counter", side_effect=times): - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - profiler.sample(mock_collector, duration_sec=0.05) - - result = output.getvalue() - - # Should report error rate - self.assertIn("Error rate:", result) - self.assertIn("%", result) - - # Collector should have been called only for successful samples (should be > 0) - self.assertGreater(mock_collector.collect.call_count, 0) - self.assertLessEqual(mock_collector.collect.call_count, 3) - - def test_sample_profiler_missed_samples_warning(self): - """Test that the profiler warns about missed samples when sampling is too slow.""" - from profile.sample import SampleProfiler - - mock_unwinder = mock.MagicMock() - mock_unwinder.get_stack_trace.return_value = [ - ( - 1, - [ - mock.MagicMock( - filename="test.py", lineno=10, funcname="test_func" - ) - ], - ) - ] - - with mock.patch( - "_remote_debugging.RemoteUnwinder" - ) as mock_unwinder_class: - mock_unwinder_class.return_value = mock_unwinder - - # Use very short interval that we'll miss - profiler = SampleProfiler( - pid=12345, sample_interval_usec=1000, all_threads=False - ) # 1ms interval - - mock_collector = mock.MagicMock() - - # Simulate slow sampling where we miss many samples - times = [ - 0.0, - 0.1, - 0.2, - 0.3, - 0.4, - 0.5, - 0.6, - 0.7, - ] # Extra time points to avoid StopIteration - - with mock.patch("time.perf_counter", side_effect=times): - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - profiler.sample(mock_collector, duration_sec=0.5) - - result = output.getvalue() - - # Should warn about missed samples - self.assertIn("Warning: missed", result) - self.assertIn("samples from the expected total", result) - - -@force_not_colorized_test_class -class TestPrintSampledStats(unittest.TestCase): - """Test the print_sampled_stats function.""" - - def setUp(self): - """Set up test data.""" - # Mock stats data - self.mock_stats = mock.MagicMock() - self.mock_stats.stats = { - ("file1.py", 10, "func1"): ( - 100, - 100, - 0.5, - 0.5, - {}, - ), # cc, nc, tt, ct, callers - ("file2.py", 20, "func2"): (50, 50, 0.25, 0.3, {}), - ("file3.py", 30, "func3"): (200, 200, 1.5, 2.0, {}), - ("file4.py", 40, "func4"): ( - 10, - 10, - 0.001, - 0.001, - {}, - ), # millisecond range - ("file5.py", 50, "func5"): ( - 5, - 5, - 0.000001, - 0.000002, - {}, - ), # microsecond range - } - - def test_print_sampled_stats_basic(self): - """Test basic print_sampled_stats functionality.""" - from profile.sample import print_sampled_stats - - # Capture output - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats(self.mock_stats, sample_interval_usec=100) - - result = output.getvalue() - - # Check header is present - self.assertIn("Profile Stats:", result) - self.assertIn("nsamples", result) - self.assertIn("tottime", result) - self.assertIn("cumtime", result) - - # Check functions are present - self.assertIn("func1", result) - self.assertIn("func2", result) - self.assertIn("func3", result) - - def test_print_sampled_stats_sorting(self): - """Test different sorting options.""" - from profile.sample import print_sampled_stats - - # Test sort by calls - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats( - self.mock_stats, sort=0, sample_interval_usec=100 - ) - - result = output.getvalue() - lines = result.strip().split("\n") - - # Find the data lines (skip header) - data_lines = [l for l in lines if "file" in l and ".py" in l] - # func3 should be first (200 calls) - self.assertIn("func3", data_lines[0]) - - # Test sort by time - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats( - self.mock_stats, sort=1, sample_interval_usec=100 - ) - - result = output.getvalue() - lines = result.strip().split("\n") - - data_lines = [l for l in lines if "file" in l and ".py" in l] - # func3 should be first (1.5s time) - self.assertIn("func3", data_lines[0]) - - def test_print_sampled_stats_limit(self): - """Test limiting output rows.""" - from profile.sample import print_sampled_stats - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats( - self.mock_stats, limit=2, sample_interval_usec=100 - ) - - result = output.getvalue() - - # Count function entries in the main stats section (not in summary) - lines = result.split("\n") - # Find where the main stats section ends (before summary) - main_section_lines = [] - for line in lines: - if "Summary of Interesting Functions:" in line: - break - main_section_lines.append(line) - - # Count function entries only in main section - func_count = sum( - 1 - for line in main_section_lines - if "func" in line and ".py" in line - ) - self.assertEqual(func_count, 2) - - def test_print_sampled_stats_time_units(self): - """Test proper time unit selection.""" - from profile.sample import print_sampled_stats - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats(self.mock_stats, sample_interval_usec=100) - - result = output.getvalue() - - # Should use seconds for the header since max time is > 1s - self.assertIn("tottime (s)", result) - self.assertIn("cumtime (s)", result) - - # Test with only microsecond-range times - micro_stats = mock.MagicMock() - micro_stats.stats = { - ("file1.py", 10, "func1"): (100, 100, 0.000005, 0.000010, {}), - } - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats(micro_stats, sample_interval_usec=100) - - result = output.getvalue() - - # Should use microseconds - self.assertIn("tottime (μs)", result) - self.assertIn("cumtime (μs)", result) - - def test_print_sampled_stats_summary(self): - """Test summary section generation.""" - from profile.sample import print_sampled_stats - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats( - self.mock_stats, - show_summary=True, - sample_interval_usec=100, - ) - - result = output.getvalue() - - # Check summary sections are present - self.assertIn("Summary of Interesting Functions:", result) - self.assertIn( - "Functions with Highest Direct/Cumulative Ratio (Hot Spots):", - result, - ) - self.assertIn( - "Functions with Highest Call Frequency (Indirect Calls):", result - ) - self.assertIn( - "Functions with Highest Call Magnification (Cumulative/Direct):", - result, - ) - - def test_print_sampled_stats_no_summary(self): - """Test disabling summary output.""" - from profile.sample import print_sampled_stats - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats( - self.mock_stats, - show_summary=False, - sample_interval_usec=100, - ) - - result = output.getvalue() - - # Summary should not be present - self.assertNotIn("Summary of Interesting Functions:", result) - - def test_print_sampled_stats_empty_stats(self): - """Test with empty stats.""" - from profile.sample import print_sampled_stats - - empty_stats = mock.MagicMock() - empty_stats.stats = {} - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats(empty_stats, sample_interval_usec=100) - - result = output.getvalue() - - # Should still print header - self.assertIn("Profile Stats:", result) - - def test_print_sampled_stats_sample_percentage_sorting(self): - """Test sample percentage sorting options.""" - from profile.sample import print_sampled_stats - - # Add a function with high sample percentage (more direct calls than func3's 200) - self.mock_stats.stats[("expensive.py", 60, "expensive_func")] = ( - 300, # direct calls (higher than func3's 200) - 300, # cumulative calls - 1.0, # total time - 1.0, # cumulative time - {}, - ) - - # Test sort by sample percentage - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats( - self.mock_stats, sort=3, sample_interval_usec=100 - ) # sample percentage - - result = output.getvalue() - lines = result.strip().split("\n") - - data_lines = [l for l in lines if ".py" in l and "func" in l] - # expensive_func should be first (highest sample percentage) - self.assertIn("expensive_func", data_lines[0]) - - def test_print_sampled_stats_with_recursive_calls(self): - """Test print_sampled_stats with recursive calls where nc != cc.""" - from profile.sample import print_sampled_stats - - # Create stats with recursive calls (nc != cc) - recursive_stats = mock.MagicMock() - recursive_stats.stats = { - # (direct_calls, cumulative_calls, tt, ct, callers) - recursive function - ("recursive.py", 10, "factorial"): ( - 5, # direct_calls - 10, # cumulative_calls (appears more times in stack due to recursion) - 0.5, - 0.6, - {}, - ), - ("normal.py", 20, "normal_func"): ( - 3, # direct_calls - 3, # cumulative_calls (same as direct for non-recursive) - 0.2, - 0.2, - {}, - ), - } - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats(recursive_stats, sample_interval_usec=100) - - result = output.getvalue() - - # Should display recursive calls as "5/10" format - self.assertIn("5/10", result) # nc/cc format for recursive calls - self.assertIn("3", result) # just nc for non-recursive calls - self.assertIn("factorial", result) - self.assertIn("normal_func", result) - - def test_print_sampled_stats_with_zero_call_counts(self): - """Test print_sampled_stats with zero call counts to trigger division protection.""" - from profile.sample import print_sampled_stats - - # Create stats with zero call counts - zero_stats = mock.MagicMock() - zero_stats.stats = { - ("file.py", 10, "zero_calls"): (0, 0, 0.0, 0.0, {}), # Zero calls - ("file.py", 20, "normal_func"): ( - 5, - 5, - 0.1, - 0.1, - {}, - ), # Normal function - } - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats(zero_stats, sample_interval_usec=100) - - result = output.getvalue() - - # Should handle zero call counts gracefully - self.assertIn("zero_calls", result) - self.assertIn("zero_calls", result) - self.assertIn("normal_func", result) - - def test_print_sampled_stats_sort_by_name(self): - """Test sort by function name option.""" - from profile.sample import print_sampled_stats - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats( - self.mock_stats, sort=-1, sample_interval_usec=100 - ) # sort by name - - result = output.getvalue() - lines = result.strip().split("\n") - - # Find the data lines (skip header and summary) - # Data lines start with whitespace and numbers, and contain filename:lineno(function) - data_lines = [] - for line in lines: - # Skip header lines and summary sections - if ( - line.startswith(" ") - and "(" in line - and ")" in line - and not line.startswith( - " 1." - ) # Skip summary lines that start with times - and not line.startswith( - " 0." - ) # Skip summary lines that start with times - and not "per call" in line # Skip summary lines - and not "calls" in line # Skip summary lines - and not "total time" in line # Skip summary lines - and not "cumulative time" in line - ): # Skip summary lines - data_lines.append(line) - - # Extract just the function names for comparison - func_names = [] - import re - - for line in data_lines: - # Function name is between the last ( and ), accounting for ANSI color codes - match = re.search(r"\(([^)]+)\)$", line) - if match: - func_name = match.group(1) - # Remove ANSI color codes - func_name = re.sub(r"\x1b\[[0-9;]*m", "", func_name) - func_names.append(func_name) - - # Verify we extracted function names and they are sorted - self.assertGreater( - len(func_names), 0, "Should have extracted some function names" - ) - self.assertEqual( - func_names, - sorted(func_names), - f"Function names {func_names} should be sorted alphabetically", - ) - - def test_print_sampled_stats_with_zero_time_functions(self): - """Test summary sections with functions that have zero time.""" - from profile.sample import print_sampled_stats - - # Create stats with zero-time functions - zero_time_stats = mock.MagicMock() - zero_time_stats.stats = { - ("file1.py", 10, "zero_time_func"): ( - 5, - 5, - 0.0, - 0.0, - {}, - ), # Zero time - ("file2.py", 20, "normal_func"): ( - 3, - 3, - 0.1, - 0.1, - {}, - ), # Normal time - } - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats( - zero_time_stats, - show_summary=True, - sample_interval_usec=100, - ) - - result = output.getvalue() - - # Should handle zero-time functions gracefully in summary - self.assertIn("Summary of Interesting Functions:", result) - self.assertIn("zero_time_func", result) - self.assertIn("normal_func", result) - - def test_print_sampled_stats_with_malformed_qualified_names(self): - """Test summary generation with function names that don't contain colons.""" - from profile.sample import print_sampled_stats - - # Create stats with function names that would create malformed qualified names - malformed_stats = mock.MagicMock() - malformed_stats.stats = { - # Function name without clear module separation - ("no_colon_func", 10, "func"): (3, 3, 0.1, 0.1, {}), - ("", 20, "empty_filename_func"): (2, 2, 0.05, 0.05, {}), - ("normal.py", 30, "normal_func"): (5, 5, 0.2, 0.2, {}), - } - - with io.StringIO() as output: - with mock.patch("sys.stdout", output): - print_sampled_stats( - malformed_stats, - show_summary=True, - sample_interval_usec=100, - ) - - result = output.getvalue() - - # Should handle malformed names gracefully in summary aggregation - self.assertIn("Summary of Interesting Functions:", result) - # All function names should appear somewhere in the output - self.assertIn("func", result) - self.assertIn("empty_filename_func", result) - self.assertIn("normal_func", result) - - def test_print_sampled_stats_with_recursive_call_stats_creation(self): - """Test create_stats with recursive call data to trigger total_rec_calls branch.""" - collector = PstatsCollector(sample_interval_usec=1000000) # 1 second - - # Simulate recursive function data where total_rec_calls would be set - # We need to manually manipulate the collector result to test this branch - collector.result = { - ("recursive.py", 10, "factorial"): { - "total_rec_calls": 3, # Non-zero recursive calls - "direct_calls": 5, - "cumulative_calls": 10, - }, - ("normal.py", 20, "normal_func"): { - "total_rec_calls": 0, # Zero recursive calls - "direct_calls": 2, - "cumulative_calls": 5, - }, - } - - collector.create_stats() - - # Check that recursive calls are handled differently from non-recursive - factorial_stats = collector.stats[("recursive.py", 10, "factorial")] - normal_stats = collector.stats[("normal.py", 20, "normal_func")] - - # factorial should use cumulative_calls (10) as nc - self.assertEqual( - factorial_stats[1], 10 - ) # nc should be cumulative_calls - self.assertEqual(factorial_stats[0], 5) # cc should be direct_calls - - # normal_func should use cumulative_calls as nc - self.assertEqual(normal_stats[1], 5) # nc should be cumulative_calls - self.assertEqual(normal_stats[0], 2) # cc should be direct_calls - - -@skip_if_not_supported -@unittest.skipIf( - sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, - "Test only runs on Linux with process_vm_readv support", -) -class TestRecursiveFunctionProfiling(unittest.TestCase): - """Test profiling of recursive functions and complex call patterns.""" - - def test_recursive_function_call_counting(self): - """Test that recursive function calls are counted correctly.""" - collector = PstatsCollector(sample_interval_usec=1000) - - # Simulate a recursive call pattern: fibonacci(5) calling itself - recursive_frames = [ - ( - 1, - [ # First sample: deep in recursion - MockFrameInfo("fib.py", 10, "fibonacci"), - MockFrameInfo("fib.py", 10, "fibonacci"), # recursive call - MockFrameInfo( - "fib.py", 10, "fibonacci" - ), # deeper recursion - MockFrameInfo("fib.py", 10, "fibonacci"), # even deeper - MockFrameInfo("main.py", 5, "main"), # main caller - ], - ), - ( - 1, - [ # Second sample: different recursion depth - MockFrameInfo("fib.py", 10, "fibonacci"), - MockFrameInfo("fib.py", 10, "fibonacci"), # recursive call - MockFrameInfo("main.py", 5, "main"), # main caller - ], - ), - ( - 1, - [ # Third sample: back to deeper recursion - MockFrameInfo("fib.py", 10, "fibonacci"), - MockFrameInfo("fib.py", 10, "fibonacci"), - MockFrameInfo("fib.py", 10, "fibonacci"), - MockFrameInfo("main.py", 5, "main"), - ], - ), - ] - - for frames in recursive_frames: - collector.collect([frames]) - - collector.create_stats() - - # Check that recursive calls are counted properly - fib_key = ("fib.py", 10, "fibonacci") - main_key = ("main.py", 5, "main") - - self.assertIn(fib_key, collector.stats) - self.assertIn(main_key, collector.stats) - - # Fibonacci should have many calls due to recursion - fib_stats = collector.stats[fib_key] - direct_calls, cumulative_calls, tt, ct, callers = fib_stats - - # Should have recorded multiple calls (9 total appearances in samples) - self.assertEqual(cumulative_calls, 9) - self.assertGreater(tt, 0) # Should have some total time - self.assertGreater(ct, 0) # Should have some cumulative time - - # Main should have fewer calls - main_stats = collector.stats[main_key] - main_direct_calls, main_cumulative_calls = main_stats[0], main_stats[1] - self.assertEqual(main_direct_calls, 0) # Never directly executing - self.assertEqual(main_cumulative_calls, 3) # Appears in all 3 samples - - def test_nested_function_hierarchy(self): - """Test profiling of deeply nested function calls.""" - collector = PstatsCollector(sample_interval_usec=1000) - - # Simulate a deep call hierarchy - deep_call_frames = [ - ( - 1, - [ - MockFrameInfo("level1.py", 10, "level1_func"), - MockFrameInfo("level2.py", 20, "level2_func"), - MockFrameInfo("level3.py", 30, "level3_func"), - MockFrameInfo("level4.py", 40, "level4_func"), - MockFrameInfo("level5.py", 50, "level5_func"), - MockFrameInfo("main.py", 5, "main"), - ], - ), - ( - 1, - [ # Same hierarchy sampled again - MockFrameInfo("level1.py", 10, "level1_func"), - MockFrameInfo("level2.py", 20, "level2_func"), - MockFrameInfo("level3.py", 30, "level3_func"), - MockFrameInfo("level4.py", 40, "level4_func"), - MockFrameInfo("level5.py", 50, "level5_func"), - MockFrameInfo("main.py", 5, "main"), - ], - ), - ] - - for frames in deep_call_frames: - collector.collect([frames]) - - collector.create_stats() - - # All levels should be recorded - for level in range(1, 6): - key = (f"level{level}.py", level * 10, f"level{level}_func") - self.assertIn(key, collector.stats) - - stats = collector.stats[key] - direct_calls, cumulative_calls, tt, ct, callers = stats - - # Each level should appear in stack twice (2 samples) - self.assertEqual(cumulative_calls, 2) - - # Only level1 (deepest) should have direct calls - if level == 1: - self.assertEqual(direct_calls, 2) - else: - self.assertEqual(direct_calls, 0) - - # Deeper levels should have lower cumulative time than higher levels - # (since they don't include time from functions they call) - if level == 1: # Deepest level with most time - self.assertGreater(ct, 0) - - def test_alternating_call_patterns(self): - """Test profiling with alternating call patterns.""" - collector = PstatsCollector(sample_interval_usec=1000) - - # Simulate alternating execution paths - pattern_frames = [ - # Pattern A: path through func_a - ( - 1, - [ - MockFrameInfo("module.py", 10, "func_a"), - MockFrameInfo("module.py", 30, "shared_func"), - MockFrameInfo("main.py", 5, "main"), - ], - ), - # Pattern B: path through func_b - ( - 1, - [ - MockFrameInfo("module.py", 20, "func_b"), - MockFrameInfo("module.py", 30, "shared_func"), - MockFrameInfo("main.py", 5, "main"), - ], - ), - # Pattern A again - ( - 1, - [ - MockFrameInfo("module.py", 10, "func_a"), - MockFrameInfo("module.py", 30, "shared_func"), - MockFrameInfo("main.py", 5, "main"), - ], - ), - # Pattern B again - ( - 1, - [ - MockFrameInfo("module.py", 20, "func_b"), - MockFrameInfo("module.py", 30, "shared_func"), - MockFrameInfo("main.py", 5, "main"), - ], - ), - ] - - for frames in pattern_frames: - collector.collect([frames]) - - collector.create_stats() - - # Check that both paths are recorded equally - func_a_key = ("module.py", 10, "func_a") - func_b_key = ("module.py", 20, "func_b") - shared_key = ("module.py", 30, "shared_func") - main_key = ("main.py", 5, "main") - - # func_a and func_b should each be directly executing twice - self.assertEqual(collector.stats[func_a_key][0], 2) # direct_calls - self.assertEqual(collector.stats[func_a_key][1], 2) # cumulative_calls - self.assertEqual(collector.stats[func_b_key][0], 2) # direct_calls - self.assertEqual(collector.stats[func_b_key][1], 2) # cumulative_calls - - # shared_func should appear in all samples (4 times) but never directly executing - self.assertEqual(collector.stats[shared_key][0], 0) # direct_calls - self.assertEqual(collector.stats[shared_key][1], 4) # cumulative_calls - - # main should appear in all samples but never directly executing - self.assertEqual(collector.stats[main_key][0], 0) # direct_calls - self.assertEqual(collector.stats[main_key][1], 4) # cumulative_calls - - def test_collapsed_stack_with_recursion(self): - """Test collapsed stack collector with recursive patterns.""" - collector = CollapsedStackCollector() - - # Recursive call pattern - recursive_frames = [ - ( - 1, - [ - ("factorial.py", 10, "factorial"), - ("factorial.py", 10, "factorial"), # recursive - ("factorial.py", 10, "factorial"), # deeper - ("main.py", 5, "main"), - ], - ), - ( - 1, - [ - ("factorial.py", 10, "factorial"), - ("factorial.py", 10, "factorial"), # different depth - ("main.py", 5, "main"), - ], - ), - ] - - for frames in recursive_frames: - collector.collect([frames]) - - # Should capture both call trees - self.assertEqual(len(collector.call_trees), 2) - - # First tree should be longer (deeper recursion) - tree1 = collector.call_trees[0] - tree2 = collector.call_trees[1] - - # Trees should be different lengths due to different recursion depths - self.assertNotEqual(len(tree1), len(tree2)) - - # Both should contain factorial calls - self.assertTrue(any("factorial" in str(frame) for frame in tree1)) - self.assertTrue(any("factorial" in str(frame) for frame in tree2)) - - # Function samples should count all occurrences - factorial_key = ("factorial.py", 10, "factorial") - main_key = ("main.py", 5, "main") - - # factorial appears 5 times total (3 + 2) - self.assertEqual(collector.function_samples[factorial_key], 5) - # main appears 2 times total - self.assertEqual(collector.function_samples[main_key], 2) - - -@requires_subprocess() -@skip_if_not_supported -class TestSampleProfilerIntegration(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.test_script = ''' -import time -import os - -def slow_fibonacci(n): - """Recursive fibonacci - should show up prominently in profiler.""" - if n <= 1: - return n - return slow_fibonacci(n-1) + slow_fibonacci(n-2) - -def cpu_intensive_work(): - """CPU intensive work that should show in profiler.""" - result = 0 - for i in range(10000): - result += i * i - if i % 100 == 0: - result = result % 1000000 - return result - -def medium_computation(): - """Medium complexity function.""" - result = 0 - for i in range(100): - result += i * i - return result - -def fast_loop(): - """Fast simple loop.""" - total = 0 - for i in range(50): - total += i - return total - -def nested_calls(): - """Test nested function calls.""" - def level1(): - def level2(): - return medium_computation() - return level2() - return level1() - -def main_loop(): - """Main test loop with different execution paths.""" - iteration = 0 - - while True: - iteration += 1 - - # Different execution paths - focus on CPU intensive work - if iteration % 3 == 0: - # Very CPU intensive - result = cpu_intensive_work() - elif iteration % 5 == 0: - # Expensive recursive operation - result = slow_fibonacci(12) - else: - # Medium operation - result = nested_calls() - - # No sleep - keep CPU busy - -if __name__ == "__main__": - main_loop() -''' - - def test_sampling_basic_functionality(self): - with ( - test_subprocess(self.test_script) as proc, - io.StringIO() as captured_output, - mock.patch("sys.stdout", captured_output), - ): - try: - profile.sample.sample( - proc.pid, - duration_sec=2, - sample_interval_usec=1000, # 1ms - show_summary=False, - ) - except PermissionError: - self.skipTest("Insufficient permissions for remote profiling") - - output = captured_output.getvalue() - - # Basic checks on output - self.assertIn("Captured", output) - self.assertIn("samples", output) - self.assertIn("Profile Stats", output) - - # Should see some of our test functions - self.assertIn("slow_fibonacci", output) - - def test_sampling_with_pstats_export(self): - pstats_out = tempfile.NamedTemporaryFile( - suffix=".pstats", delete=False - ) - self.addCleanup(close_and_unlink, pstats_out) - - with test_subprocess(self.test_script) as proc: - # Suppress profiler output when testing file export - with ( - io.StringIO() as captured_output, - mock.patch("sys.stdout", captured_output), - ): - try: - profile.sample.sample( - proc.pid, - duration_sec=1, - filename=pstats_out.name, - sample_interval_usec=10000, - ) - except PermissionError: - self.skipTest( - "Insufficient permissions for remote profiling" - ) - - # Verify file was created and contains valid data - self.assertTrue(os.path.exists(pstats_out.name)) - self.assertGreater(os.path.getsize(pstats_out.name), 0) - - # Try to load the stats file - with open(pstats_out.name, "rb") as f: - stats_data = marshal.load(f) - - # Should be a dictionary with the sampled marker - self.assertIsInstance(stats_data, dict) - self.assertIn(("__sampled__",), stats_data) - self.assertTrue(stats_data[("__sampled__",)]) - - # Should have some function data - function_entries = [ - k for k in stats_data.keys() if k != ("__sampled__",) - ] - self.assertGreater(len(function_entries), 0) - - def test_sampling_with_collapsed_export(self): - collapsed_file = tempfile.NamedTemporaryFile( - suffix=".txt", delete=False - ) - self.addCleanup(close_and_unlink, collapsed_file) - - with ( - test_subprocess(self.test_script) as proc, - ): - # Suppress profiler output when testing file export - with ( - io.StringIO() as captured_output, - mock.patch("sys.stdout", captured_output), - ): - try: - profile.sample.sample( - proc.pid, - duration_sec=1, - filename=collapsed_file.name, - output_format="collapsed", - sample_interval_usec=10000, - ) - except PermissionError: - self.skipTest( - "Insufficient permissions for remote profiling" - ) - - # Verify file was created and contains valid data - self.assertTrue(os.path.exists(collapsed_file.name)) - self.assertGreater(os.path.getsize(collapsed_file.name), 0) - - # Check file format - with open(collapsed_file.name, "r") as f: - content = f.read() - - lines = content.strip().split("\n") - self.assertGreater(len(lines), 0) - - # Each line should have format: stack_trace count - for line in lines: - parts = line.rsplit(" ", 1) - self.assertEqual(len(parts), 2) - - stack_trace, count_str = parts - self.assertGreater(len(stack_trace), 0) - self.assertTrue(count_str.isdigit()) - self.assertGreater(int(count_str), 0) - - # Stack trace should contain semicolon-separated entries - if ";" in stack_trace: - stack_parts = stack_trace.split(";") - for part in stack_parts: - # Each part should be file:function:line - self.assertIn(":", part) - - def test_sampling_all_threads(self): - with ( - test_subprocess(self.test_script) as proc, - # Suppress profiler output - io.StringIO() as captured_output, - mock.patch("sys.stdout", captured_output), - ): - try: - profile.sample.sample( - proc.pid, - duration_sec=1, - all_threads=True, - sample_interval_usec=10000, - show_summary=False, - ) - except PermissionError: - self.skipTest("Insufficient permissions for remote profiling") - - # Just verify that sampling completed without error - # We're not testing output format here - - -@skip_if_not_supported -@unittest.skipIf( - sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, - "Test only runs on Linux with process_vm_readv support", -) -class TestSampleProfilerErrorHandling(unittest.TestCase): - def test_invalid_pid(self): - with self.assertRaises((OSError, RuntimeError)): - profile.sample.sample(-1, duration_sec=1) - - def test_process_dies_during_sampling(self): - with test_subprocess("import time; time.sleep(0.5); exit()") as proc: - with ( - io.StringIO() as captured_output, - mock.patch("sys.stdout", captured_output), - ): - try: - profile.sample.sample( - proc.pid, - duration_sec=2, # Longer than process lifetime - sample_interval_usec=50000, - ) - except PermissionError: - self.skipTest( - "Insufficient permissions for remote profiling" - ) - - output = captured_output.getvalue() - - self.assertIn("Error rate", output) - - def test_invalid_output_format(self): - with self.assertRaises(ValueError): - profile.sample.sample( - os.getpid(), - duration_sec=1, - output_format="invalid_format", - ) - - def test_invalid_output_format_with_mocked_profiler(self): - """Test invalid output format with proper mocking to avoid permission issues.""" - with mock.patch( - "profile.sample.SampleProfiler" - ) as mock_profiler_class: - mock_profiler = mock.MagicMock() - mock_profiler_class.return_value = mock_profiler - - with self.assertRaises(ValueError) as cm: - profile.sample.sample( - 12345, - duration_sec=1, - output_format="unknown_format", - ) - - # Should raise ValueError with the invalid format name - self.assertIn( - "Invalid output format: unknown_format", str(cm.exception) - ) - - def test_is_process_running(self): - with test_subprocess("import time; time.sleep(1000)") as proc: - try: - profiler = SampleProfiler(pid=proc.pid, sample_interval_usec=1000, all_threads=False) - except PermissionError: - self.skipTest( - "Insufficient permissions to read the stack trace" - ) - self.assertTrue(profiler._is_process_running()) - self.assertIsNotNone(profiler.unwinder.get_stack_trace()) - proc.kill() - proc.wait() - # ValueError on MacOS (yeah I know), ProcessLookupError on Linux and Windows - self.assertRaises((ValueError, ProcessLookupError), profiler.unwinder.get_stack_trace) - - # Exit the context manager to ensure the process is terminated - self.assertFalse(profiler._is_process_running()) - self.assertRaises((ValueError, ProcessLookupError), profiler.unwinder.get_stack_trace) - - @unittest.skipUnless(sys.platform == "linux", "Only valid on Linux") - def test_esrch_signal_handling(self): - with test_subprocess("import time; time.sleep(1000)") as proc: - try: - unwinder = _remote_debugging.RemoteUnwinder(proc.pid) - except PermissionError: - self.skipTest( - "Insufficient permissions to read the stack trace" - ) - initial_trace = unwinder.get_stack_trace() - self.assertIsNotNone(initial_trace) - - proc.kill() - - # Wait for the process to die and try to get another trace - proc.wait() - - with self.assertRaises(ProcessLookupError): - unwinder.get_stack_trace() - - - -class TestSampleProfilerCLI(unittest.TestCase): - def test_cli_collapsed_format_validation(self): - """Test that CLI properly validates incompatible options with collapsed format.""" - test_cases = [ - # Test sort options are invalid with collapsed - ( - ["profile.sample", "--collapsed", "--sort-nsamples", "12345"], - "sort", - ), - ( - ["profile.sample", "--collapsed", "--sort-tottime", "12345"], - "sort", - ), - ( - [ - "profile.sample", - "--collapsed", - "--sort-cumtime", - "12345", - ], - "sort", - ), - ( - [ - "profile.sample", - "--collapsed", - "--sort-sample-pct", - "12345", - ], - "sort", - ), - ( - [ - "profile.sample", - "--collapsed", - "--sort-cumul-pct", - "12345", - ], - "sort", - ), - ( - ["profile.sample", "--collapsed", "--sort-name", "12345"], - "sort", - ), - # Test limit option is invalid with collapsed - (["profile.sample", "--collapsed", "-l", "20", "12345"], "limit"), - ( - ["profile.sample", "--collapsed", "--limit", "20", "12345"], - "limit", - ), - # Test no-summary option is invalid with collapsed - ( - ["profile.sample", "--collapsed", "--no-summary", "12345"], - "summary", - ), - ] - - for test_args, expected_error_keyword in test_cases: - with ( - mock.patch("sys.argv", test_args), - mock.patch("sys.stderr", io.StringIO()) as mock_stderr, - self.assertRaises(SystemExit) as cm, - ): - profile.sample.main() - - self.assertEqual(cm.exception.code, 2) # argparse error code - error_msg = mock_stderr.getvalue() - self.assertIn("error:", error_msg) - self.assertIn("--pstats format", error_msg) - - def test_cli_default_collapsed_filename(self): - """Test that collapsed format gets a default filename when not specified.""" - test_args = ["profile.sample", "--collapsed", "12345"] - - with ( - mock.patch("sys.argv", test_args), - mock.patch("profile.sample.sample") as mock_sample, - ): - profile.sample.main() - - # Check that filename was set to default collapsed format - mock_sample.assert_called_once() - call_args = mock_sample.call_args[1] - self.assertEqual(call_args["output_format"], "collapsed") - self.assertEqual(call_args["filename"], "collapsed.12345.txt") - - def test_cli_custom_output_filenames(self): - """Test custom output filenames for both formats.""" - test_cases = [ - ( - ["profile.sample", "--pstats", "-o", "custom.pstats", "12345"], - "custom.pstats", - "pstats", - ), - ( - ["profile.sample", "--collapsed", "-o", "custom.txt", "12345"], - "custom.txt", - "collapsed", - ), - ] - - for test_args, expected_filename, expected_format in test_cases: - with ( - mock.patch("sys.argv", test_args), - mock.patch("profile.sample.sample") as mock_sample, - ): - profile.sample.main() - - mock_sample.assert_called_once() - call_args = mock_sample.call_args[1] - self.assertEqual(call_args["filename"], expected_filename) - self.assertEqual(call_args["output_format"], expected_format) - - def test_cli_missing_required_arguments(self): - """Test that CLI requires PID argument.""" - with ( - mock.patch("sys.argv", ["profile.sample"]), - mock.patch("sys.stderr", io.StringIO()), - ): - with self.assertRaises(SystemExit): - profile.sample.main() - - def test_cli_mutually_exclusive_format_options(self): - """Test that pstats and collapsed options are mutually exclusive.""" - with ( - mock.patch( - "sys.argv", - ["profile.sample", "--pstats", "--collapsed", "12345"], - ), - mock.patch("sys.stderr", io.StringIO()), - ): - with self.assertRaises(SystemExit): - profile.sample.main() - - def test_argument_parsing_basic(self): - test_args = ["profile.sample", "12345"] - - with ( - mock.patch("sys.argv", test_args), - mock.patch("profile.sample.sample") as mock_sample, - ): - profile.sample.main() - - mock_sample.assert_called_once_with( - 12345, - sample_interval_usec=100, - duration_sec=10, - filename=None, - all_threads=False, - limit=15, - sort=2, - show_summary=True, - output_format="pstats", - realtime_stats=False, - ) - - def test_sort_options(self): - sort_options = [ - ("--sort-nsamples", 0), - ("--sort-tottime", 1), - ("--sort-cumtime", 2), - ("--sort-sample-pct", 3), - ("--sort-cumul-pct", 4), - ("--sort-name", -1), - ] - - for option, expected_sort_value in sort_options: - test_args = ["profile.sample", option, "12345"] - - with ( - mock.patch("sys.argv", test_args), - mock.patch("profile.sample.sample") as mock_sample, - ): - profile.sample.main() - - mock_sample.assert_called_once() - call_args = mock_sample.call_args[1] - self.assertEqual( - call_args["sort"], - expected_sort_value, - ) - mock_sample.reset_mock() - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_samply_profiler.py b/Lib/test/test_samply_profiler.py index ec0ed37ffd047b..f9ab9207c3c23d 100644 --- a/Lib/test/test_samply_profiler.py +++ b/Lib/test/test_samply_profiler.py @@ -240,5 +240,29 @@ def compile_trampolines_for_all_functions(): self.assertIn(line, child_perf_file_contents) +@unittest.skipUnless(samply_command_works(), "samply command doesn't work") +class TestSamplyProfilerWithJitDump(unittest.TestCase, TestSamplyProfilerMixin): + # Regression test for gh-150723: exercises the binary jitdump backend + # (-Xperf_jit) end to end through samply, unlike TestSamplyProfiler which + # uses the textual perf-map backend (-Xperf). + def run_samply(self, script_dir, script, activate_trampoline=True): + if activate_trampoline: + return run_samply(script_dir, sys.executable, "-Xperf_jit", script) + return run_samply(script_dir, sys.executable, script) + + def setUp(self): + super().setUp() + self.jit_files = set(pathlib.Path("/tmp/").glob("jit-*.dump")) + self.jit_files |= set(pathlib.Path("/tmp/").glob("jitted-*.so")) + + def tearDown(self) -> None: + super().tearDown() + files_to_delete = set(pathlib.Path("/tmp/").glob("jit-*.dump")) + files_to_delete |= set(pathlib.Path("/tmp/").glob("jitted-*.so")) + files_to_delete -= self.jit_files + for file in files_to_delete: + file.unlink() + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py index 5c10bcedc69bc6..29babd7bf6996a 100644 --- a/Lib/test/test_sax.py +++ b/Lib/test/test_sax.py @@ -1573,5 +1573,17 @@ def test_all(self): check__all__(self, sax, extra=extra) +class TestModule(unittest.TestCase): + def test_deprecated__version__and__date__(self): + for module in (sax.expatreader, sax.handler): + with self.subTest(module=module): + with self.assertWarnsRegex( + DeprecationWarning, + "'version' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(module, "version") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index c0df9507bd7f5e..9bfd4bc7d63669 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -20,6 +20,14 @@ def check_pass_thru(): raise PassThru yield 1 +class CustomHash: + def __init__(self, hash): + self.hash = hash + def __hash__(self): + return self.hash + def __repr__(self): + return f'<CustomHash {self.hash} at {id(self):#x}>' + class BadCmp: def __hash__(self): return 1 @@ -180,7 +188,10 @@ def test_symmetric_difference(self): self.assertEqual(type(i), self.basetype) self.assertRaises(PassThru, self.s.symmetric_difference, check_pass_thru()) self.assertRaises(TypeError, self.s.symmetric_difference, [[]]) - for C in set, frozenset, dict.fromkeys, str, list, tuple: + constructors = (set, frozenset, + dict.fromkeys, frozendict.fromkeys, + str, list, tuple) + for C in constructors: self.assertEqual(self.thetype('abcba').symmetric_difference(C('cdc')), set('abd')) self.assertEqual(self.thetype('abcba').symmetric_difference(C('efgfe')), set('abcefg')) self.assertEqual(self.thetype('abcba').symmetric_difference(C('ccb')), set('a')) @@ -661,7 +672,7 @@ def check_unhashable_element(): with check_unhashable_element(): myset.discard(elem) - # Only TypeError exception is overriden, + # Only TypeError exception is overridden, # other exceptions are left unchanged. class HashError: def __hash__(self): @@ -675,6 +686,28 @@ def __hash__(self): with self.assertRaises(KeyError): myset.discard(elem2) + def test_hash_collision_remove_add(self): + self.maxDiff = None + # There should be enough space, so all elements with unique hash + # will be placed in corresponding cells without collision. + n = 64 + elems = [CustomHash(h) for h in range(n)] + # Elements with hash collision. + a = CustomHash(n) + b = CustomHash(n) + elems += [a, b] + s = self.thetype(elems) + self.assertEqual(len(s), len(elems), s) + s.remove(a) + # "a" has been replaced with a dummy. + del elems[n] + self.assertEqual(len(s), len(elems), s) + self.assertEqual(s, set(elems)) + s.add(b) + # "b" should not replace the dummy. + self.assertEqual(len(s), len(elems), s) + self.assertEqual(s, set(elems)) + class SetSubclass(set): pass @@ -1561,6 +1594,14 @@ def setUp(self): #------------------------------------------------------------------------------ +class TestOnlySetsFrozenDict(TestOnlySetsInBinaryOps, unittest.TestCase): + def setUp(self): + self.set = set((1, 2, 3)) + self.other = frozendict({1:2, 3:4}) + self.otherIsIterable = True + +#------------------------------------------------------------------------------ + class TestOnlySetsOperator(TestOnlySetsInBinaryOps, unittest.TestCase): def setUp(self): self.set = set((1, 2, 3)) @@ -1853,6 +1894,7 @@ def test_iter_and_mutate(self): list(si) def test_merge_and_mutate(self): + # gh-141805 class X: def __hash__(self): return hash(0) @@ -1865,6 +1907,33 @@ def __eq__(self, o): s = {0} s.update(other) + def test_hash_collision_concurrent_add(self): + class X: + def __hash__(self): + return 0 + class Y: + flag = False + def __hash__(self): + return 0 + def __eq__(self, other): + if not self.flag: + self.flag = True + s.add(X()) + return self is other + + a = X() + s = set() + s.add(a) + s.add(X()) + s.remove(a) + # Now the set contains a dummy entry followed by an entry + # for an object with hash 0. + s.add(Y()) + # The following operations should not crash. + repr(s) + list(s) + set() | s + class TestOperationsMutating: """Regression test for bpo-46615""" diff --git a/Lib/test/test_shelve.py b/Lib/test/test_shelve.py index 64609ab9dd9a62..5f6a030e018f96 100644 --- a/Lib/test/test_shelve.py +++ b/Lib/test/test_shelve.py @@ -5,7 +5,7 @@ import pickle import os -from test.support import import_helper, os_helper +from test.support import import_helper, os_helper, subTests from collections.abc import MutableMapping from test.test_dbm import dbm_iterator @@ -173,6 +173,8 @@ def test_custom_serializer_and_deserializer(self): def serializer(obj, protocol): if isinstance(obj, (bytes, bytearray, str)): if protocol == 5: + if isinstance(obj, bytearray): + return bytes(obj) # DBM backends expect bytes return obj return type(obj).__name__ elif isinstance(obj, array.array): @@ -222,22 +224,31 @@ def deserializer(data): s["array_data"], array_data.tobytes().decode() ) - def test_custom_incomplete_serializer_and_deserializer(self): - dbm_sqlite3 = import_helper.import_module("dbm.sqlite3") - os.mkdir(self.dirname) - self.addCleanup(os_helper.rmtree, self.dirname) + @subTests("serialized", [None, ["invalid type"]]) + def test_custom_invalid_serializer(self, serialized): + test_dir = f"{self.dirname}_{id(serialized)}" + os.mkdir(test_dir) + self.addCleanup(os_helper.rmtree, test_dir) + test_fn = os.path.join(test_dir, "shelftemp.db") - with self.assertRaises(dbm_sqlite3.error): - def serializer(obj, protocol=None): - pass + def serializer(obj, protocol=None): + return serialized - def deserializer(data): - return data.decode("utf-8") + def deserializer(data): + return data.decode("utf-8") - with shelve.open(self.fn, serializer=serializer, + # Since the serializer returns an invalid type or None, + # dbm.error is raised by dbm.sqlite3 and TypeError is raised + # by other backends. + with self.assertRaises((TypeError, dbm.error)): + with shelve.open(test_fn, serializer=serializer, deserializer=deserializer) as s: s["foo"] = "bar" + def test_custom_incomplete_deserializer(self): + os.mkdir(self.dirname) + self.addCleanup(os_helper.rmtree, self.dirname) + def serializer(obj, protocol=None): return type(obj).__name__.encode("utf-8") @@ -352,7 +363,7 @@ def type_name_len(obj): self.assertEqual(s["bytearray_data"], "bytearray") self.assertEqual(s["array_data"], "array") - def test_custom_incomplete_serializer_and_deserializer_bsd_db_shelf(self): + def test_custom_incomplete_deserializer_bsd_db_shelf(self): berkeleydb = import_helper.import_module("berkeleydb") os.mkdir(self.dirname) self.addCleanup(os_helper.rmtree, self.dirname) @@ -370,6 +381,11 @@ def deserializer(data): self.assertIsNone(s["foo"]) self.assertNotEqual(s["foo"], "bar") + def test_custom_incomplete_serializer_bsd_db_shelf(self): + berkeleydb = import_helper.import_module("berkeleydb") + os.mkdir(self.dirname) + self.addCleanup(os_helper.rmtree, self.dirname) + def serializer(obj, protocol=None): pass diff --git a/Lib/test/test_shlex.py b/Lib/test/test_shlex.py index a13ddcb76b7bcb..2adaee81b06308 100644 --- a/Lib/test/test_shlex.py +++ b/Lib/test/test_shlex.py @@ -330,6 +330,7 @@ def testQuote(self): unsafe = '"`$\\!' + unicode_sample self.assertEqual(shlex.quote(''), "''") + self.assertEqual(shlex.quote(None), "''") self.assertEqual(shlex.quote(safeunquoted), safeunquoted) self.assertEqual(shlex.quote('test file name'), "'test file name'") for u in unsafe: @@ -338,6 +339,16 @@ def testQuote(self): for u in unsafe: self.assertEqual(shlex.quote("test%s'name'" % u), "'test%s'\"'\"'name'\"'\"''" % u) + self.assertRaises(TypeError, shlex.quote, 42) + self.assertRaises(TypeError, shlex.quote, b"abc") + + def testForceQuote(self): + self.assertEqual(shlex.quote("spam"), "spam") + self.assertEqual(shlex.quote("spam", force=False), "spam") + self.assertEqual(shlex.quote("spam", force=True), "'spam'") + self.assertEqual(shlex.quote("spam eggs", force=False), "'spam eggs'") + self.assertEqual(shlex.quote("spam eggs", force=True), "'spam eggs'") + self.assertEqual(shlex.quote("two's-complement", force=False), "'two'\"'\"'s-complement'") def testJoin(self): for split_command, command in [ diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index ebb6cf88336249..bb901220fb408c 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -2042,6 +2042,32 @@ def test_make_zipfile_in_curdir(self): self.assertEqual(make_archive('test', 'zip'), 'test.zip') self.assertTrue(os.path.isfile('test.zip')) + def test_make_archive_pathlike_cwd_default(self): + called_args = [] + def archiver(base_name, base_dir, **kw): + called_args.append((base_name, kw.get('root_dir'))) + + register_archive_format('xxx', archiver, [], 'xxx file') + self.addCleanup(unregister_archive_format, 'xxx') + with no_chdir: + make_archive(FakePath('basename'), 'xxx') + self.assertEqual(called_args, [('basename', None)]) + + def test_make_archive_pathlike_cwd_supports_root_dir(self): + root_dir = self.mkdtemp() + called_args = [] + def archiver(base_name, base_dir, **kw): + called_args.append((base_name, base_dir, kw.get('root_dir'))) + archiver.supports_root_dir = True + + register_archive_format('xxx', archiver, [], 'xxx file') + self.addCleanup(unregister_archive_format, 'xxx') + with no_chdir: + make_archive(FakePath('basename'), 'xxx', + root_dir=FakePath(root_dir), + base_dir=FakePath('basedir')) + self.assertEqual(called_args, [('basename', 'basedir', root_dir)]) + def test_register_archive_format(self): self.assertRaises(TypeError, register_archive_format, 'xxx', 1) @@ -2110,8 +2136,6 @@ def test_make_zipfile_rootdir_nodir(self): def check_unpack_archive(self, format, **kwargs): self.check_unpack_archive_with_converter( format, lambda path: path, **kwargs) - self.check_unpack_archive_with_converter( - format, FakePath, **kwargs) self.check_unpack_archive_with_converter(format, FakePath, **kwargs) def check_unpack_archive_with_converter(self, format, converter, **kwargs): @@ -2168,6 +2192,71 @@ def test_unpack_archive_zip(self): with self.assertRaises(TypeError): self.check_unpack_archive('zip', filter='data') + def test_unpack_archive_zip_badpaths(self): + srcdir = self.mkdtemp() + zipname = os.path.join(srcdir, 'test.zip') + abspath = os.path.join(srcdir, 'abspath') + with zipfile.ZipFile(zipname, 'w') as zf: + zf.writestr(abspath, 'badfile') + zf.writestr(os.sep + abspath, 'badfile') + zf.writestr('/abspath', 'badfile') + zf.writestr('C:/abspath', 'badfile') + zf.writestr('D:\\abspath', 'badfile') + zf.writestr('E:abspath', 'badfile') + zf.writestr('F:/G:/abspath', 'badfile') + zf.writestr('//server/share/abspath', 'badfile') + zf.writestr('\\\\server2\\share\\abspath', 'badfile') + zf.writestr('../relpath', 'badfile') + zf.writestr(os.pardir + os.sep + 'relpath2', 'badfile') + zf.writestr('good/file', 'goodfile') + zf.writestr('good..file', 'goodfile') + + dstdir = os.path.join(self.mkdtemp(), 'dst') + unpack_archive(zipname, dstdir) + self.assertTrue(os.path.isfile(os.path.join(dstdir, 'good', 'file'))) + self.assertTrue(os.path.isfile(os.path.join(dstdir, 'good..file'))) + self.assertFalse(os.path.exists(abspath)) + self.assertFalse(os.path.exists(os.path.join(dstdir, 'abspath'))) + self.assertFalse(os.path.exists(os.path.join(dstdir, 'G_'))) + self.assertFalse(os.path.exists(os.path.join(dstdir, 'server'))) + if os.name != 'nt': + self.assertTrue(os.path.isfile(os.path.join(dstdir, 'C:', 'abspath'))) + self.assertTrue(os.path.isfile(os.path.join(dstdir, 'D:\\abspath'))) + self.assertTrue(os.path.isfile(os.path.join(dstdir, 'E:abspath'))) + self.assertTrue(os.path.isfile(os.path.join(dstdir, 'F:', 'G:', 'abspath'))) + self.assertTrue(os.path.isfile(os.path.join(dstdir, '\\\\server2\\share\\abspath'))) + if os.pardir == '..': + self.assertFalse(os.path.exists(os.path.join(dstdir, '..', 'relpath'))) + self.assertFalse(os.path.exists(os.path.join(dstdir, 'relpath'))) + else: + self.assertTrue(os.path.isfile(os.path.join(dstdir, '..', 'relpath'))) + self.assertFalse(os.path.exists(os.path.join(dstdir, os.pardir, 'relpath2'))) + self.assertFalse(os.path.exists(os.path.join(dstdir, 'relpath2'))) + + dstdir2 = os.path.join(self.mkdtemp(), 'dst') + os.mkdir(dstdir2) + with os_helper.change_cwd(dstdir2): + unpack_archive(zipname, '') + self.assertTrue(os.path.isfile(os.path.join('good', 'file'))) + self.assertTrue(os.path.isfile('good..file')) + self.assertFalse(os.path.exists(abspath)) + self.assertFalse(os.path.exists('abspath')) + self.assertFalse(os.path.exists('C_')) + self.assertFalse(os.path.exists('server')) + if os.name != 'nt': + self.assertTrue(os.path.isfile(os.path.join('C:', 'abspath'))) + self.assertTrue(os.path.isfile('D:\\abspath')) + self.assertTrue(os.path.isfile('E:abspath')) + self.assertTrue(os.path.isfile(os.path.join('F:', 'G:', 'abspath'))) + self.assertTrue(os.path.isfile('\\\\server2\\share\\abspath')) + if os.pardir == '..': + self.assertFalse(os.path.exists(os.path.join('..', 'relpath'))) + self.assertFalse(os.path.exists('relpath')) + else: + self.assertTrue(os.path.isfile(os.path.join('..', 'relpath'))) + self.assertFalse(os.path.exists(os.path.join(os.pardir, 'relpath2'))) + self.assertFalse(os.path.exists('relpath2')) + def test_unpack_registry(self): formats = get_unpack_formats() @@ -2825,6 +2914,23 @@ def test_destinsrc_false_positive(self): finally: os_helper.rmtree(TESTFN) + @os_helper.skip_unless_symlink + def test_destinsrc_symlink_bypass(self): + tmp = self.mkdtemp() + src = os.path.join(tmp, 'src') + os.makedirs(src) + # tmp/link -> tmp (one level up) + link = os.path.join(tmp, 'link') + os.symlink(tmp, link) + # raw path: tmp/link/src/sub - no src prefix in string space + # real path: tmp/src/sub - physically inside src + dst = os.path.join(link, 'src', 'sub') + self.assertTrue( + shutil._destinsrc(src, dst), + msg='_destinsrc failed to detect dst inside src via symlink ' + '(dst=%s, src=%s)' % (dst, src), + ) + @os_helper.skip_unless_symlink @mock_rename def test_move_file_symlink(self): @@ -3491,8 +3597,6 @@ def test_module_all_attribute(self): if hasattr(os, 'statvfs') or os.name == 'nt': target_api.append('disk_usage') self.assertEqual(set(shutil.__all__), set(target_api)) - with self.assertWarns(DeprecationWarning): - from shutil import ExecError # noqa: F401 if __name__ == '__main__': diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py index 6d62d6119255a8..2cad18a69ff7c0 100644 --- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -14,7 +14,7 @@ import unittest from test import support from test.support import ( - is_apple, is_apple_mobile, os_helper, threading_helper + force_not_colorized, is_apple, is_apple_mobile, os_helper, threading_helper ) from test.support.script_helper import assert_python_ok, spawn_python try: @@ -353,6 +353,7 @@ def check_signum(signals): @unittest.skipIf(_testcapi is None, 'need _testcapi') @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + @force_not_colorized def test_wakeup_write_error(self): # Issue #16105: write() errors in the C signal handler should not # pass silently. @@ -832,6 +833,8 @@ def test_itimer_real(self): # Issue 3864, unknown if this affects earlier versions of freebsd also @unittest.skipIf(sys.platform in ('netbsd5',) or is_apple_mobile, 'itimer not reliable (does not mix well with threading) on some BSDs.') + @unittest.skipIf(sys.platform == 'cygwin', + "Cygwin doesn't support ITIMER_VIRTUAL") def test_itimer_virtual(self): self.itimer = signal.ITIMER_VIRTUAL signal.signal(signal.SIGVTALRM, self.sig_vtalrm) @@ -849,6 +852,8 @@ def test_itimer_virtual(self): # and the handler should have been called self.assertEqual(self.hndl_called, True) + @unittest.skipIf(sys.platform == 'cygwin', + "Cygwin doesn't support ITIMER_PROF") def test_itimer_prof(self): self.itimer = signal.ITIMER_PROF signal.signal(signal.SIGPROF, self.sig_prof) diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index d0e3294263557e..5fd65ad999210e 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -13,8 +13,10 @@ from test.support import socket_helper from test.support import captured_stderr from test.support.os_helper import TESTFN, EnvironmentVarGuard +from test.support.script_helper import spawn_python, kill_python import ast import builtins +import contextlib import glob import io import os @@ -25,6 +27,8 @@ import sys import sysconfig import tempfile +from textwrap import dedent +from types import SimpleNamespace import urllib.error import urllib.request from unittest import mock @@ -121,14 +125,11 @@ def test_addpackage(self): # comment or import that is a valid directory name for where the .pth # file resides; invalid directories are not added pth_file = PthFile() - pth_file.cleanup(prep=True) # to make sure that nothing is - # pre-existing that shouldn't be - try: - pth_file.create() + # Ensure we have a clean slate. + pth_file.cleanup(prep=True) + with pth_file.create(): site.addpackage(pth_file.base_dir, pth_file.filename, set()) self.pth_file_tests(pth_file) - finally: - pth_file.cleanup() def make_pth(self, contents, pth_dir='.', pth_name=TESTFN): # Create a .pth file and return its (abspath, basename). @@ -148,9 +149,6 @@ def test_addpackage_import_bad_syntax(self): self.assertRegex(err_out.getvalue(), "line 1") self.assertRegex(err_out.getvalue(), re.escape(os.path.join(pth_dir, pth_fn))) - # XXX: the previous two should be independent checks so that the - # order doesn't matter. The next three could be a single check - # but my regex foo isn't good enough to write it. self.assertRegex(err_out.getvalue(), 'Traceback') self.assertRegex(err_out.getvalue(), r'import bad-syntax') self.assertRegex(err_out.getvalue(), 'SyntaxError') @@ -160,10 +158,8 @@ def test_addpackage_import_bad_exec(self): pth_dir, pth_fn = self.make_pth("randompath\nimport nosuchmodule\n") with captured_stderr() as err_out: site.addpackage(pth_dir, pth_fn, set()) - self.assertRegex(err_out.getvalue(), "line 2") self.assertRegex(err_out.getvalue(), re.escape(os.path.join(pth_dir, pth_fn))) - # XXX: ditto previous XXX comment. self.assertRegex(err_out.getvalue(), 'Traceback') self.assertRegex(err_out.getvalue(), 'ModuleNotFoundError') @@ -176,65 +172,66 @@ def test_addpackage_empty_lines(self): def test_addpackage_import_bad_pth_file(self): # Issue 5258 pth_dir, pth_fn = self.make_pth("abc\x00def\n") - with captured_stderr() as err_out: - self.assertFalse(site.addpackage(pth_dir, pth_fn, set())) - self.maxDiff = None - self.assertEqual(err_out.getvalue(), "") for path in sys.path: if isinstance(path, str): self.assertNotIn("abc\x00def", path) def test_addsitedir(self): - # Same tests for test_addpackage since addsitedir() essentially just - # calls addpackage() for every .pth file in the directory + # addsitedir() reads .pth files and, when called standalone + # (known_paths=None), flushes paths and import lines immediately. pth_file = PthFile() - pth_file.cleanup(prep=True) # Make sure that nothing is pre-existing - # that is tested for - try: - pth_file.create() - site.addsitedir(pth_file.base_dir, set()) + # Ensure we have a clean slate. + pth_file.cleanup(prep=True) + with pth_file.create(): + site.addsitedir(pth_file.base_dir) + self.pth_file_tests(pth_file) + + def test_addsitedir_explicit_flush(self): + # StartupState.addsitedir() reads .pth files and accumulates pending + # state without flushing. A subsequent state.process() call then + # applies the paths and runs the import lines. + pth_file = PthFile() + # Ensure we have a clean slate. + pth_file.cleanup(prep=True) + with pth_file.create(): + state = site.StartupState(known_paths=set()) + state.addsitedir(pth_file.base_dir) + self.assertNotIn(pth_file.imported, sys.modules) + state.process() self.pth_file_tests(pth_file) - finally: - pth_file.cleanup() def test_addsitedir_dotfile(self): pth_file = PthFile('.dotfile') + # Ensure we have a clean slate. pth_file.cleanup(prep=True) - try: - pth_file.create() - site.addsitedir(pth_file.base_dir, set()) + with pth_file.create(): + site.addsitedir(pth_file.base_dir) self.assertNotIn(site.makepath(pth_file.good_dir_path)[0], sys.path) self.assertIn(pth_file.base_dir, sys.path) - finally: - pth_file.cleanup() @unittest.skipUnless(hasattr(os, 'chflags'), 'test needs os.chflags()') def test_addsitedir_hidden_flags(self): pth_file = PthFile() + # Ensure we have a clean slate. pth_file.cleanup(prep=True) - try: - pth_file.create() + with pth_file.create(): st = os.stat(pth_file.file_path) os.chflags(pth_file.file_path, st.st_flags | stat.UF_HIDDEN) - site.addsitedir(pth_file.base_dir, set()) + site.addsitedir(pth_file.base_dir) self.assertNotIn(site.makepath(pth_file.good_dir_path)[0], sys.path) self.assertIn(pth_file.base_dir, sys.path) - finally: - pth_file.cleanup() @unittest.skipUnless(sys.platform == 'win32', 'test needs Windows') @support.requires_subprocess() def test_addsitedir_hidden_file_attribute(self): pth_file = PthFile() + # Ensure we have a clean slate. pth_file.cleanup(prep=True) - try: - pth_file.create() + with pth_file.create(): subprocess.check_call(['attrib', '+H', pth_file.file_path]) - site.addsitedir(pth_file.base_dir, set()) + site.addsitedir(pth_file.base_dir) self.assertNotIn(site.makepath(pth_file.good_dir_path)[0], sys.path) self.assertIn(pth_file.base_dir, sys.path) - finally: - pth_file.cleanup() # This tests _getuserbase, hence the double underline # to distinguish from a test for getuserbase @@ -398,7 +395,7 @@ def test_trace(self): self.assertEqual(sys.stderr.getvalue(), out) -class PthFile(object): +class PthFile: """Helper class for handling testing of .pth files""" def __init__(self, filename_base=TESTFN, imported="time", @@ -413,6 +410,7 @@ def __init__(self, filename_base=TESTFN, imported="time", self.good_dir_path = os.path.join(self.base_dir, self.good_dirname) self.bad_dir_path = os.path.join(self.base_dir, self.bad_dirname) + @contextlib.contextmanager def create(self): """Create a .pth file with a comment, blank lines, an ``import <self.imported>``, a line with self.good_dirname, and a line with @@ -421,19 +419,21 @@ def create(self): Creation of the directory for self.good_dir_path (based off of self.good_dirname) is also performed. - Make sure to call self.cleanup() to undo anything done by this method. - + Used as a context manager: self.cleanup() is called on exit. """ - FILE = open(self.file_path, 'w') + with open(self.file_path, 'w') as fp: + print(f"""\ +#import @bad module name +import {self.imported} +{self.good_dirname} +{self.bad_dirname} +""", file=fp) + + os.mkdir(self.good_dir_path) try: - print("#import @bad module name", file=FILE) - print("\n", file=FILE) - print("import %s" % self.imported, file=FILE) - print(self.good_dirname, file=FILE) - print(self.bad_dirname, file=FILE) + yield self finally: - FILE.close() - os.mkdir(self.good_dir_path) + self.cleanup() def cleanup(self, prep=False): """Make sure that the .pth file is deleted, self.imported is not in @@ -453,6 +453,7 @@ def cleanup(self, prep=False): if os.path.exists(self.bad_dir_path): os.rmdir(self.bad_dir_path) + class ImportSideEffectTests(unittest.TestCase): """Test side-effects from importing 'site'.""" @@ -464,17 +465,6 @@ def tearDown(self): """Restore sys.path""" sys.path[:] = self.sys_path - def test_abs_paths_cached_None(self): - """Test for __cached__ is None. - - Regarding to PEP 3147, __cached__ can be None. - - See also: https://bugs.python.org/issue30167 - """ - sys.modules['test'].__cached__ = None - site.abs_paths() - self.assertIsNone(sys.modules['test'].__cached__) - def test_no_duplicate_paths(self): # No duplicate paths should exist in sys.path # Handled by removeduppaths() @@ -553,7 +543,6 @@ def test_customization_modules_on_startup(self): output = subprocess.check_output([sys.executable, '-s', '-c', '""']) self.assertNotIn(eyecatcher, output.decode('utf-8')) - @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"), 'need SSL support to download license') @test.support.requires_resource('network') @@ -802,6 +791,952 @@ def test_underpth_dll_file(self): )], env=env) self.assertTrue(rc, "sys.path is incorrect") + @support.requires_subprocess() + def test_underpth_no_user_site(self): + pth_lines = [test.support.STDLIB_DIR, 'import site'] + exe_file = self._create_underpth_exe(pth_lines) + p = subprocess.run([exe_file, '-X', 'utf8', '-c', + 'import sys; ' + 'sys.exit(not sys.flags.no_user_site)']) + self.assertEqual(p.returncode, 0, "sys.flags.no_user_site was 0") + + +class CommandLineTests(unittest.TestCase): + def exists(self, path): + if path is not None and os.path.isdir(path): + return "exists" + else: + return "doesn't exist" + + def get_excepted_output(self, *args): + if len(args) == 0: + user_base = site.getuserbase() + user_site = site.getusersitepackages() + output = io.StringIO() + output.write("sys.path = [\n") + for dir in sys.path: + output.write(" %r,\n" % (dir,)) + output.write("]\n") + output.write(f"USER_BASE: {user_base} ({self.exists(user_base)})\n") + output.write(f"USER_SITE: {user_site} ({self.exists(user_site)})\n") + output.write(f"ENABLE_USER_SITE: {site.ENABLE_USER_SITE}\n") + return 0, dedent(output.getvalue()).strip() + + buffer = [] + if '--user-base' in args: + buffer.append(site.getuserbase()) + if '--user-site' in args: + buffer.append(site.getusersitepackages()) + + if buffer: + return_code = 3 + if site.ENABLE_USER_SITE: + return_code = 0 + elif site.ENABLE_USER_SITE is False: + return_code = 1 + elif site.ENABLE_USER_SITE is None: + return_code = 2 + output = os.pathsep.join(buffer) + return return_code, os.path.normpath(dedent(output).strip()) + else: + return 10, None + + def invoke_command_line(self, *args): + cmd_args = [] + if sys.flags.no_user_site: + cmd_args.append("-s") + cmd_args.extend(["-m", "site", *args]) + + with EnvironmentVarGuard() as env: + env["PYTHONUTF8"] = "1" + env["PYTHONIOENCODING"] = "utf-8" + proc = spawn_python(*cmd_args, text=True, env=env, + encoding='utf-8', errors='replace') + + output = kill_python(proc) + return_code = proc.returncode + return return_code, os.path.normpath(dedent(output).strip()) + + @support.requires_subprocess() + def test_no_args(self): + return_code, output = self.invoke_command_line() + excepted_return_code, _ = self.get_excepted_output() + self.assertEqual(return_code, excepted_return_code) + lines = output.splitlines() + self.assertEqual(lines[0], "sys.path = [") + self.assertEqual(lines[-4], "]") + excepted_base = f"USER_BASE: '{site.getuserbase()}'" +\ + f" ({self.exists(site.getuserbase())})" + self.assertEqual(lines[-3], excepted_base) + excepted_site = f"USER_SITE: '{site.getusersitepackages()}'" +\ + f" ({self.exists(site.getusersitepackages())})" + self.assertEqual(lines[-2], excepted_site) + self.assertEqual(lines[-1], f"ENABLE_USER_SITE: {site.ENABLE_USER_SITE}") + + @support.requires_subprocess() + def test_unknown_args(self): + return_code, output = self.invoke_command_line("--unknown-arg") + excepted_return_code, _ = self.get_excepted_output("--unknown-arg") + self.assertEqual(return_code, excepted_return_code) + self.assertIn('[--user-base] [--user-site]', output) + + @support.requires_subprocess() + def test_base_arg(self): + return_code, output = self.invoke_command_line("--user-base") + excepted = self.get_excepted_output("--user-base") + excepted_return_code, excepted_output = excepted + self.assertEqual(return_code, excepted_return_code) + self.assertEqual(output, excepted_output) + + @support.requires_subprocess() + def test_site_arg(self): + return_code, output = self.invoke_command_line("--user-site") + excepted = self.get_excepted_output("--user-site") + excepted_return_code, excepted_output = excepted + self.assertEqual(return_code, excepted_return_code) + self.assertEqual(output, excepted_output) + + @support.requires_subprocess() + def test_both_args(self): + return_code, output = self.invoke_command_line("--user-base", + "--user-site") + excepted = self.get_excepted_output("--user-base", "--user-site") + excepted_return_code, excepted_output = excepted + self.assertEqual(return_code, excepted_return_code) + self.assertEqual(output, excepted_output) + + +class StartFileTests(unittest.TestCase): + """Tests for .start file processing (PEP 829).""" + + def setUp(self): + self.enterContext(import_helper.DirsOnSysPath()) + self.tmpdir = self.sitedir = self.enterContext(os_helper.temp_dir()) + # Each test gets its own StartupState to batch the parsing and + # explicitly invoke the processing. Seed with an empty known_paths + # so dedup is not influenced by the current sys.path. + self.state = site.StartupState(known_paths=set()) + + def _make_start(self, content, name='testpkg', basedir=None): + """Write a <name>.start file and return its basename. + + ``basedir`` defaults to ``self.tmpdir``. Pass an explicit directory + when the .start file needs to live somewhere other than the test's + primary tmpdir (e.g. a nested user-site). + """ + basename = f"{name}.start" + filepath = os.path.join(self.tmpdir if basedir is None else basedir, basename) + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + return basename + + def _make_pth(self, content, name='testpkg', basedir=None): + """Write a <name>.pth file and return its basename. + + ``basedir`` defaults to ``self.tmpdir``. Pass an explicit directory + when the .pth file needs to live somewhere other than the test's + primary tmpdir (e.g. a nested user-site). + """ + basename = f"{name}.pth" + filepath = os.path.join(self.tmpdir if basedir is None else basedir, basename) + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + return basename + + def _make_mod(self, contents, name='mod', *, package=False, on_path=False): + """Write an importable module (or package), returning its parent dir.""" + extdir = os.path.join(self.sitedir, 'extdir') + os.makedirs(extdir, exist_ok=True) + + # Put the code in a package's dunder-init or flat module. + if package: + pkgdir = os.path.join(extdir, name) + os.mkdir(pkgdir) + modpath = os.path.join(pkgdir, '__init__.py') + else: + modpath = os.path.join(extdir, f'{name}.py') + + with open(modpath, 'w') as fp: + fp.write(contents) + + self.addCleanup(sys.modules.pop, name, None) + if on_path: + # Don't worry, DirsOnSysPath() in setUp() will clean this up. + sys.path.insert(0, extdir) + return extdir + + def _all_entrypoints(self, state=None): + """Flatten state._entrypoints into a list of (filename, entry) tuples.""" + result = [] + state = self.state if state is None else state + for filename, entries in state._entrypoints.items(): + for entry in entries: + result.append((filename, entry)) + return result + + def _just_entrypoints(self, state=None): + return [entry for filename, entry in self._all_entrypoints(state)] + + # There are two classes of tests here. Tests that start with `test_impl_` + # know details about the implementation and they access non-public methods + # and data structures to perform focused functional tests. + # + # Tests that start with `test_addsitedir_` are end-to-end tests that ensure + # integration semantics and functionality as a caller of the public + # surfaces would see. + + # --- Basic StartupState implementation tests --- + + def test_impl_startupstate_defaults_to_sys_path(self): + sys.path.insert(0, self.sitedir) + state = site.StartupState() + self.assertIn(site.makepath(self.sitedir)[1], state._known_paths) + + def test_impl_startupstate_uses_supplied_known_paths(self): + known_paths = set() + state = site.StartupState(known_paths) + state.addsitedir(self.sitedir) + self.assertIs(state._known_paths, known_paths) + self.assertIn(site.makepath(self.sitedir)[1], known_paths) + + # --- StartupState._read_start_file tests --- + + def test_impl_read_start_file_basic(self): + self._make_start("os.path:join\n", name='foo') + self.state._read_start_file(self.sitedir, 'foo.start') + fullname = os.path.join(self.sitedir, 'foo.start') + self.assertEqual( + self.state._entrypoints[fullname], ['os.path:join'] + ) + + def test_impl_read_start_file_multiple_entries(self): + self._make_start("os.path:join\nos.path:exists\n", name='foo') + self.state._read_start_file(self.sitedir, 'foo.start') + fullname = os.path.join(self.sitedir, 'foo.start') + self.assertEqual( + self.state._entrypoints[fullname], + ['os.path:join', 'os.path:exists'], + ) + + def test_impl_read_start_file_comments_and_blanks(self): + self._make_start("# a comment\n\nos.path:join\n \n", name='foo') + self.state._read_start_file(self.sitedir, 'foo.start') + fullname = os.path.join(self.sitedir, 'foo.start') + self.assertEqual( + self.state._entrypoints[fullname], ['os.path:join'] + ) + + def test_impl_read_start_file_accepts_all_non_blank_lines(self): + # Syntax validation is deferred to entry-point execution time + # (where pkgutil.resolve_name(strict=True) enforces the strict + # pkg.mod:callable form), so parsing accepts every non-blank, + # non-comment line, including syntactically invalid ones. + content = ( + "os.path\n" # no colon + "pkg.mod:\n" # empty callable + ":callable\n" # empty module + "pkg.mod:callable:extra\n" # multiple colons + "os.path:join\n" # valid + ) + self._make_start(content, name='foo') + self.state._read_start_file(self.sitedir, 'foo.start') + fullname = os.path.join(self.sitedir, 'foo.start') + self.assertEqual(self.state._entrypoints[fullname], [ + 'os.path', + 'pkg.mod:', + ':callable', + 'pkg.mod:callable:extra', + 'os.path:join', + ]) + + def test_impl_read_start_file_empty(self): + # PEP 829: an empty .start file is still registered as present + # (with an empty entry point list) so that it suppresses `import` + # lines in any matching .pth file. + self._make_start("", name='foo') + self.state._read_start_file(self.sitedir, 'foo.start') + fullname = os.path.join(self.sitedir, 'foo.start') + self.assertEqual(self.state._entrypoints, {fullname: []}) + + def test_impl_read_start_file_comments_only(self): + # As with an empty file, a comments-only .start file is registered + # as present so it can suppress matching .pth `import` lines. + self._make_start("# just a comment\n# another\n", name='foo') + self.state._read_start_file(self.sitedir, 'foo.start') + fullname = os.path.join(self.sitedir, 'foo.start') + self.assertEqual(self.state._entrypoints, {fullname: []}) + + def test_impl_read_start_file_nonexistent(self): + with captured_stderr(): + self.state._read_start_file(self.tmpdir, 'nonexistent.start') + self.assertEqual(self.state._entrypoints, {}) + + @unittest.skipUnless(hasattr(os, 'chflags'), 'test needs os.chflags()') + def test_impl_read_start_file_hidden_flags(self): + self._make_start("os.path:join\n", name='foo') + filepath = os.path.join(self.tmpdir, 'foo.start') + st = os.stat(filepath) + os.chflags(filepath, st.st_flags | stat.UF_HIDDEN) + self.state._read_start_file(self.sitedir, 'foo.start') + self.assertEqual(self.state._entrypoints, {}) + + def test_impl_one_start_file_with_duplicates_not_deduplicated(self): + # PEP 829: duplicate entry points are NOT deduplicated. + self._make_start("os.path:join\nos.path:join\n", name='foo') + self.state._read_start_file(self.sitedir, 'foo.start') + fullname = os.path.join(self.sitedir, 'foo.start') + self.assertEqual( + self.state._entrypoints[fullname], + ['os.path:join', 'os.path:join'], + ) + + def test_impl_two_start_files_with_duplicates_not_deduplicated(self): + self._make_start("os.path:join", name="foo") + self._make_start("os.path:join", name="bar") + self.state._read_start_file(self.sitedir, 'foo.start') + self.state._read_start_file(self.sitedir, 'bar.start') + self.assertEqual( + self._just_entrypoints(), + ['os.path:join', 'os.path:join'], + ) + + def test_impl_read_start_file_accepts_utf8_bom(self): + # PEP 829: .start files MUST be utf-8-sig (UTF-8 with optional BOM). + filepath = os.path.join(self.tmpdir, 'foo.start') + with open(filepath, 'wb') as f: + f.write(b'\xef\xbb\xbf' + b'os.path:join\n') + self.state._read_start_file(self.sitedir, 'foo.start') + fullname = os.path.join(self.sitedir, 'foo.start') + self.assertEqual( + self.state._entrypoints[fullname], ['os.path:join'] + ) + + def test_impl_read_start_file_invalid_utf8_silently_skipped(self): + # PEP 829: .start files MUST be utf-8-sig. Unlike .pth files, there + # is no locale-encoding fallback. A .start file that is not valid + # UTF-8 is silently skipped, with no key registered in + # state._entrypoints and no output to stderr (parsing errors are + # reported only under -v). + filepath = os.path.join(self.tmpdir, 'foo.start') + with open(filepath, 'wb') as f: + # Bare continuation byte -- invalid as a UTF-8 start byte. + f.write(b'\x80\x80\x80\n') + with captured_stderr() as err: + self.state._read_start_file(self.sitedir, 'foo.start') + self.assertEqual(self.state._entrypoints, {}) + self.assertEqual(err.getvalue(), "") + + # --- StartupState._read_pth_file tests --- + + def test_impl_read_pth_file_paths(self): + subdir = os.path.join(self.sitedir, 'mylib') + os.mkdir(subdir) + self._make_pth("mylib\n", name='foo') + self.state._read_pth_file(self.sitedir, 'foo.pth') + fullname = os.path.join(self.sitedir, 'foo.pth') + self.assertIn((fullname, subdir), self.state._path_entries) + + def test_impl_read_pth_file_imports_collected(self): + self._make_pth("import sys\n", name='foo') + self.state._read_pth_file(self.sitedir, 'foo.pth') + fullname = os.path.join(self.sitedir, 'foo.pth') + self.assertEqual( + self.state._importexecs[fullname], ['import sys'] + ) + + def test_impl_read_pth_file_comments_and_blanks(self): + self._make_pth("# comment\n\n \n", name='foo') + self.state._read_pth_file(self.sitedir, 'foo.pth') + self.assertEqual(self.state._path_entries, []) + self.assertEqual(self.state._importexecs, {}) + + def test_impl_read_pth_file_deduplication(self): + subdir = os.path.join(self.sitedir, 'mylib') + os.mkdir(subdir) + # self.state._known_paths acts as the deduplication ledger across + # both reads. + self._make_pth("mylib\n", name='a') + self._make_pth("mylib\n", name='b') + self.state._read_pth_file(self.sitedir, 'a.pth') + self.state._read_pth_file(self.sitedir, 'b.pth') + # There is only one entry across both files. + all_dirs = [dir_ for filename, dir_ in self.state._path_entries] + self.assertEqual(all_dirs, [subdir]) + + def test_impl_read_pth_file_bad_line_continues(self): + # PEP 829: errors on individual lines don't abort processing the file. + subdir = os.path.join(self.sitedir, 'goodpath') + os.mkdir(subdir) + self._make_pth("abc\x00def\ngoodpath\n", name='foo') + with captured_stderr(): + self.state._read_pth_file(self.sitedir, 'foo.pth') + fullname = os.path.join(self.sitedir, 'foo.pth') + self.assertIn((fullname, subdir), self.state._path_entries) + + def _flags_with_verbose(self, verbose): + # Build a sys.flags clone with verbose overridden but every + # other field preserved, so unrelated reads like + # sys.flags.optimize during io.open_code() continue to work. + attrs = { + name: getattr(sys.flags, name) + for name in sys.flags.__match_args__ + } + attrs['verbose'] = verbose + return SimpleNamespace(**attrs) + + def test_impl_read_pth_file_parse_error_silent_by_default(self): + # PEP 829: parse-time errors are silent unless -v is given. + # Force the error path by making makepath() raise an exception. + self._make_pth("badline\n", name='foo') + with ( + mock.patch('site.makepath', side_effect=ValueError("boom")), + mock.patch('sys.flags', self._flags_with_verbose(False)), + captured_stderr() as err, + ): + self.state._read_pth_file(self.sitedir, 'foo.pth') + self.assertEqual(err.getvalue(), "") + + def test_impl_read_pth_file_parse_error_reported_under_verbose(self): + # PEP 829: parse-time errors are reported when -v is given. + self._make_pth("badline\n", name='foo') + with ( + mock.patch('site.makepath', side_effect=ValueError("boom")), + mock.patch('sys.flags', self._flags_with_verbose(True)), + captured_stderr() as err, + ): + self.state._read_pth_file(self.sitedir, 'foo.pth') + out = err.getvalue() + self.assertIn('Error in', out) + self.assertIn('foo.pth', out) + + def test_impl_read_pth_file_locale_fallback(self): + # PEP 829: .pth files that fail UTF-8 decoding fall back to the + # locale encoding for backward compatibility (deprecated in + # 3.15, to be removed in 3.20). Mock locale.getencoding() so + # the test does not depend on the host's actual locale. + subdir = os.path.join(self.sitedir, 'mylib') + os.mkdir(subdir) + filepath = os.path.join(self.tmpdir, 'foo.pth') + # \xe9 is invalid UTF-8 but valid in latin-1. + with open(filepath, 'wb') as f: + f.write(b'# caf\xe9 comment\nmylib\n') + with ( + mock.patch('locale.getencoding', return_value='latin-1'), + captured_stderr(), + ): + self.state._read_pth_file(self.sitedir, 'foo.pth') + fullname = os.path.join(self.sitedir, 'foo.pth') + self.assertIn((fullname, subdir), self.state._path_entries) + + # --- StartupState._execute_start_entrypoints tests --- + + def test_impl_execute_entrypoints_with_callable(self): + # An entry point with a callable. + self._make_mod("""\ +called = False +def startup(): + global called + called = True +""", name='epmod', package=True, on_path=True) + fullname = os.path.join(self.sitedir, 'epmod.start') + self.state._entrypoints[fullname] = ['epmod:startup'] + self.state._execute_start_entrypoints() + import epmod + self.assertTrue(epmod.called) + + def test_impl_execute_entrypoints_import_error(self): + # Import errors print a traceback and continue. + fullname = os.path.join(self.sitedir, 'bad.start') + self.state._entrypoints[fullname] = [ + 'nosuchmodule_xyz:func', 'os.path:join', + ] + with captured_stderr() as err: + self.state._execute_start_entrypoints() + self.assertIn('nosuchmodule_xyz', err.getvalue()) + # os.path:join should still have been called (no exception for it) + + def test_impl_execute_entrypoints_strict_syntax_rejection(self): + # PEP 829: only the strict pkg.mod:callable form is valid. At entry + # point execution time, pkgutil.resolve_name(strict=True) raises a + # ValueError for the invalid syntax. The invalid entry is reported + # and execution continues with the next one. + fullname = os.path.join(self.sitedir, 'bad.start') + self.state._entrypoints[fullname] = [ + 'os.path', # no colon + 'pkg.mod:', # empty callable + ':callable', # empty module + 'pkg.mod:callable:extra', # multiple colons + ] + with captured_stderr() as err: + self.state._execute_start_entrypoints() + out = err.getvalue() + self.assertIn('Invalid entry point syntax', out) + for bad in ( + 'os.path', + 'pkg.mod:', + ':callable', + 'pkg.mod:callable:extra', + ): + self.assertIn(bad, out) + + def test_impl_execute_entrypoints_callable_error(self): + # A callable that errors prints a traceback but continues. + self._make_mod("""\ +def fail(): + raise RuntimeError("boom") +""", name='badmod', package=True, on_path=True) + fullname = os.path.join(self.sitedir, 'badmod.start') + self.state._entrypoints[fullname] = ['badmod:fail'] + with captured_stderr() as err: + self.state._execute_start_entrypoints() + self.assertIn('RuntimeError', err.getvalue()) + self.assertIn('boom', err.getvalue()) + + def test_impl_execute_entrypoints_duplicates_called_twice(self): + # PEP 829: duplicate entry points execute multiple times. + self._make_mod("""\ +call_count = 0 +def bump(): + global call_count + call_count += 1 +""", name='countmod', package=False, on_path=True) + fullname = os.path.join(self.sitedir, 'countmod.start') + self.state._entrypoints[fullname] = [ + 'countmod:bump', 'countmod:bump', + ] + self.state._execute_start_entrypoints() + import countmod + self.assertEqual(countmod.call_count, 2) + + # --- StartupState._exec_imports tests --- + + def test_impl_exec_imports_suppressed_by_matching_start(self): + # Import lines from foo.pth are suppressed when foo.start exists. + self._make_mod("""\ +call_count = 0 +def bump(incr=2): + global call_count + call_count += incr +""", name='countmod', package=False, on_path=True) + self._make_start("countmod:bump\n", name='foo') + self._make_pth("import countmod; countmod.bump(1)\n", name='foo') + self.state._read_pth_file(self.sitedir, 'foo.pth') + self.state._read_start_file(self.sitedir, 'foo.start') + self.state._exec_imports() + self.state._execute_start_entrypoints() + import countmod + # This will be 2 because the entry point is called with no + # arguments, and the .pth import line is never exec'd. + self.assertEqual(countmod.call_count, 2) + + def test_impl_exec_imports_not_suppressed_by_different_start(self): + # Import lines from foo.pth are NOT suppressed by bar.start. + self._make_mod("""\ +call_count = 0 +def bump(): + global call_count + call_count += 1 +""", name='countmod', package=False, on_path=True) + pth_fullname = os.path.join(self.sitedir, 'foo.pth') + start_fullname = os.path.join(self.sitedir, 'bar.start') + self.state._importexecs[pth_fullname] = ['import countmod; countmod.bump()'] + self.state._entrypoints[start_fullname] = ['os.path:join'] + self.state._exec_imports() + import countmod + self.assertEqual(countmod.call_count, 1) + + def test_impl_exec_imports_suppressed_by_empty_matching_start(self): + self._make_start("", name='foo') + self._make_pth("import epmod; epmod.startup()", name='foo') + self._make_mod("""\ +called = False +def startup(): + global called + called = True +""", name='epmod', package=True, on_path=True) + self.state._read_pth_file(self.sitedir, 'foo.pth') + self.state._read_start_file(self.sitedir, 'foo.start') + self.state._exec_imports() + import epmod + self.assertFalse(epmod.called) + + # --- StartupState._extend_syspath tests --- + + def test_impl_extend_syspath_existing_dir(self): + subdir = os.path.join(self.sitedir, 'extlib') + os.mkdir(subdir) + self.state._path_entries.append(('test.pth', subdir)) + self.state._extend_syspath() + self.assertIn(subdir, sys.path) + + def test_impl_extend_syspath_nonexistent_dir(self): + nonesuch = os.path.join(self.sitedir, 'nosuchdir') + self.state._path_entries.append(('test.pth', nonesuch)) + with captured_stderr() as err: + self.state._extend_syspath() + self.assertNotIn(nonesuch, sys.path) + self.assertIn('does not exist', err.getvalue()) + + # --- addsitedir integration tests --- + + def test_addsitedir_pth_import_skipped_when_matching_start_exists(self): + # PEP 829: an empty .start file disables the matching .pth's import + # lines, even when the .start has no entry points of its own. + self._make_mod("flag = False\n", name='suppressed', on_path=True) + self._make_start("", name='foo') + self._make_pth( + "import suppressed; suppressed.flag = True\n", + name='foo') + site.addsitedir(self.sitedir, set()) + import suppressed + self.assertFalse( + suppressed.flag, + "import line in foo.pth should be suppressed by foo.start") + + def test_addsitedir_dotfile_start_entrypoint_not_executed(self): + # .start files starting with '.' are skipped, so their entry + # points must not run. + self._make_mod("""\ +called = False +def hook(): + global called + called = True +""", + name='dotted', on_path=True) + self._make_start("dotted:hook\n", name='.hidden') + site.addsitedir(self.sitedir, set()) + import dotted + self.assertFalse(dotted.called) + + def test_addsitedir_dedups_paths_across_pth_files(self): + # PEP 829: when multiple .pth files reference the same path within + # a single addsitedir() invocation, the path is appended to + # sys.path exactly once. + subdir = os.path.join(self.sitedir, 'shared') + os.mkdir(subdir) + self._make_pth("shared\n", name='a') + self._make_pth("shared\n", name='b') + before = sys.path.count(subdir) + site.addsitedir(self.sitedir, set()) + self.assertEqual(sys.path.count(subdir), before + 1) + + def test_addsitedir_discovers_start_files(self): + # addsitedir() should discover .start files and accumulate entries. + self._make_start("os.path:join\n", name='foo') + state = site.StartupState(known_paths=set()) + state.addsitedir(self.sitedir) + fullname = os.path.join(self.sitedir, 'foo.start') + self.assertIn( + 'os.path:join', state._entrypoints[fullname] + ) + + def test_addsitedir_pth_paths_still_work_with_start(self): + # Path lines in .pth files still work even when a .start file exists. + subdir = os.path.join(self.sitedir, 'mylib') + os.mkdir(subdir) + self._make_start("os.path:join\n", name='foo') + self._make_pth("mylib\n", name='foo') + state = site.StartupState(known_paths=set()) + state.addsitedir(self.sitedir) + fullname = os.path.join(self.sitedir, 'foo.pth') + self.assertIn((fullname, subdir), state._path_entries) + + def test_addsitedir_start_alphabetical_order(self): + # Multiple .start files are discovered alphabetically. + self._make_start("os.path:join\n", name='zzz') + self._make_start("os.path:exists\n", name='aaa') + state = site.StartupState(known_paths=set()) + state.addsitedir(self.sitedir) + entries = self._just_entrypoints(state) + idx_a = entries.index('os.path:exists') + idx_z = entries.index('os.path:join') + self.assertLess(idx_a, idx_z) + + def test_addsitedir_pth_and_start(self): + # Create a .pth and .start with the same basename; verify both the + # .pth data and .start data is collected. + subdir = os.path.join(self.sitedir, 'mylib') + os.mkdir(subdir) + self._make_pth("mylib\n", name='foo') + self._make_start("os.path:join\n", name='foo') + state = site.StartupState(known_paths=set()) + state.addsitedir(self.sitedir) + # Both should be collected. + pth_fullname = os.path.join(self.sitedir, 'foo.pth') + start_fullname = os.path.join(self.sitedir, 'foo.start') + self.assertIn((pth_fullname, subdir), state._path_entries) + self.assertIn( + 'os.path:join', + state._entrypoints.get(start_fullname, []), + ) + + def test_impl_addsitedir_skips_dotfile_start(self): + # .start files starting with '.' are skipped. + # This will create `.hidden.start`. + self._make_start("os.path:join\n", name='.hidden') + state = site.StartupState(known_paths=set()) + state.addsitedir(self.sitedir) + self.assertEqual(state._entrypoints, {}) + + def test_addsitedir_standalone_flushes(self): + # Standalone addsitedir creates a per-call StartupState and processes + # it before returning, so the caller sees the effect immediately. + subdir = os.path.join(self.sitedir, 'flushlib') + os.mkdir(subdir) + self._make_pth("flushlib\n", name='foo') + # No arguments means state is implied and processing is eager. + site.addsitedir(self.sitedir) + self.assertIn(subdir, sys.path) + + def test_addsitedir_explicit_startup_state_does_not_flush(self): + # With an explicit StartupState, addsitedir accumulates pending state + # but does not flush it; sys.path is updated only when process() is + # called explicitly. + subdir = os.path.join(self.sitedir, 'acclib') + os.mkdir(subdir) + self._make_pth("acclib\n", name='foo') + state = site.StartupState(known_paths=set()) + state.addsitedir(self.sitedir) + # Path is pending, not yet on sys.path. + self.assertNotIn(subdir, sys.path) + fullname = os.path.join(self.sitedir, 'foo.pth') + self.assertIn((fullname, subdir), state._path_entries) + + def test_addsitedir_startup_state_preserves_site_relative_order(self): + # As pointed out by @ncoghlan in + # https://github.com/python/cpython/issues/150228#issuecomment-4528614952 + # a subtle ordering change was inadvertently introduced where the + # interspersing of the sitedirs with the sys.path extensions they defined + # was lost during batch mode. You'd see all the sitedirs, then all path + # extensions. This test ensures that the old interspersing behavior + # has been restored. + # + # Let's start by creating two sitedirs, each with an extension directory + # which will be added to sys.path by .pth files in the respective sitedirs. + sitedir2 = self.enterContext(os_helper.temp_dir()) + extdir1 = os.path.join(self.sitedir, 'ext1') + extdir2 = os.path.join(sitedir2, 'ext2') + os.mkdir(extdir1) + os.mkdir(extdir2) + self._make_pth(extdir1 + "\n", name='one') + self._make_pth(extdir2 + "\n", name='two', basedir=sitedir2) + # Now create an explicit batch, add each sitedir, then process the + # entire batch. + state = site.StartupState(known_paths=set()) + state.addsitedir(self.sitedir) + state.addsitedir(sitedir2) + state.process() + # Ensure that on sys.path we see this interspersed order: + # [sitedir1, extdir1, sitedir2, extdir2] + indexes = [ + sys.path.index(path) + for path in (self.sitedir, extdir1, sitedir2, extdir2) + ] + # If the index ordering is the same, we preserved the intersperse. + self.assertEqual(indexes, sorted(indexes)) + + def test_addsitedir_startup_state_paths_before_entrypoints(self): + # Ensure that sys.path extensions are available by the time + # .start file entry points are called. + extdir = self._make_mod("""\ +called = False +def hook(): + global called + called = True +""") + self.assertNotIn(extdir, sys.path) + self._make_pth("extdir\n", name='extlib') + self._make_start("mod:hook\n", name='extlib') + # Before the startup state is explicitly processed, neither + # the path extension is added, nor the entry point called. + state = site.StartupState(known_paths=set()) + state.addsitedir(self.sitedir) + self.assertNotIn(extdir, sys.path) + self.assertNotIn('mod', sys.modules) + # After processing the batch, sys.path is extended and + # the entry point was called. + state.process() + self.assertIn(extdir, sys.path) + import mod + self.assertTrue(mod.called) + + def test_pth_path_is_available_to_start_entrypoint(self): + # Core PEP 829 invariant: all .pth path extensions are applied to + # sys.path *before* any .start entry point runs, so an entry + # point may live in a module reachable only via a .pth-extended + # path. If the flush phases were inverted, resolving the entry + # point would fail with ModuleNotFoundError. + extdir = self._make_mod("""\ +called = False +def hook(): + global called + called = True +""") + # extdir is not on sys.path; only the .pth file makes it so. + self.assertNotIn(extdir, sys.path) + self._make_pth("extdir\n", name='extlib') + self._make_start("mod:hook\n", name='extlib') + + # Standalone addsitedir() triggers the full flush sequence. + site.addsitedir(self.sitedir) + + self.assertIn(extdir, sys.path) + import mod + self.assertTrue( + mod.called, + "entry point did not run; .pth path was likely not applied " + "before .start entry-point execution") + + # --- bugs --- + + # gh-75723 + def test_addsitdir_idempotent_pth(self): + # Adding the same sitedir twice with a known_paths, should not + # process .pth files twice. + extdir = self._make_mod("""\ +_pth_count = 0 +""") + self._make_pth(f"""\ +{extdir} +import mod; mod._pth_count += 1 +""") + dirs = set() + dirs = site.addsitedir(self.sitedir, dirs) + dirs = site.addsitedir(self.sitedir, dirs) + import mod + self.assertEqual(mod._pth_count, 1) + + def test_addsitdir_idempotent_start(self): + # Adding the same sitedir twice with a known_paths, should not + # process .pth files twice. + extdir = self._make_mod("""\ +_pth_count = 0 +def increment(): + global _pth_count + _pth_count += 1 +""") + self._make_pth(f"""\ +{extdir} +""") + self._make_start("""\ +mod:increment +""") + dirs = set() + dirs = site.addsitedir(self.sitedir, dirs) + dirs = site.addsitedir(self.sitedir, dirs) + import mod + self.assertEqual(mod._pth_count, 1) + + # gh-149504 + def test_reentrant_addsitedir_pth(self): + # An import line in a .pth file that calls site.addsitedir() + # must not crash or re-execute outer entries while the outer + # call is still processing its pending startup state. + overlay = self.enterContext(os_helper.temp_dir()) + overlay_pth = os.path.join(overlay, 'overlay.pth') + pkgdir = self.enterContext(os_helper.temp_dir()) + with open(overlay_pth, 'w', encoding='utf-8') as fp: + print(pkgdir, file=fp) + self._make_pth(f"import site; site.addsitedir({overlay!r})\n") + site.addsitedir(self.sitedir, set()) + self.assertIn(overlay, sys.path) + self.assertIn(pkgdir, sys.path) + + # gh-149504 + def test_reentrant_addsitedir_start(self): + # As above, but the re-entry happens from a .start entry point + # instead of a .pth import line. The entry point execution + # phase is vulnerable to the same class of bug. + overlay = self.enterContext(os_helper.temp_dir()) + overlay_pth = os.path.join(overlay, 'overlay.pth') + pkgdir = self.enterContext(os_helper.temp_dir()) + with open(overlay_pth, 'w', encoding='utf-8') as fp: + print(pkgdir, file=fp) + self._make_mod(f"""\ +import site +def bootstrap(): + site.addsitedir({overlay!r}) +""", + name='reenter_helper', on_path=True) + self._make_start("reenter_helper:bootstrap\n") + site.addsitedir(self.sitedir, set()) + self.assertIn(overlay, sys.path) + self.assertIn(pkgdir, sys.path) + + # gh-149819 + @unittest.skipUnless(site.ENABLE_USER_SITE, "requires user-site") + @support.requires_subprocess() + def test_pth_processed_when_sitedir_already_on_path(self): + # A .pth file in a site-packages directory must still be processed by + # site.main() when that directory is already on sys.path at + # interpreter start up, for example in a subprocess that inherits + # PYTHONPATH from its parent. Before the fix, main() seeded + # known_paths with all entries derived from removeduppaths(), and + # addsitedir() then skipped .pth processing for any directory already + # in known_paths. + user_base = self.tmpdir + user_site = site._get_path(user_base) + os.makedirs(user_site) + sentinel = "GH149819_PTH_RAN" + # Writing some text to stderr is the simplest observable side effect. + self._make_pth(f"""\ +import sys; sys.stderr.write({sentinel!r}); sys.stderr.flush() +""", + name='gh149819', + basedir=user_site) + with EnvironmentVarGuard() as env: + # PYTHONUSERBASE points USER_SITE at our temp directory so + # site.main() will call addsitedir() on it, rather than on the + # host interpreter's real user-site. + env['PYTHONUSERBASE'] = user_base + # PYTHONPATH puts that same directory on sys.path before + # site.main() runs in the subprocess. This is what triggers the + # bug: removeduppaths() records it in known_paths, and the unfixed + # addsitedir() then skips .pth processing. + env['PYTHONPATH'] = user_site + result = subprocess.run( + [sys.executable, '-c', ''], + capture_output=True, + check=True, + ) + self.assertIn(sentinel.encode(), result.stderr) + + @unittest.skipUnless(site.ENABLE_USER_SITE, "requires user-site") + @support.requires_subprocess() + def test_start_processed_when_sitedir_already_on_path(self): + # Companion to test_pth_processed_when_sitedir_already_on_path: + # the same dedup-guard skip in addsitedir() suppressed both .pth + # and .start file processing, so verify .start entry points also + # run for a site-packages directory inherited via PYTHONPATH. + user_base = self.tmpdir + user_site = site._get_path(user_base) + os.makedirs(user_site) + sentinel = "GH149819_START_RAN" + # The .start entry point resolves to a callable, so we write a + # tiny importable module that outputs the sentinel text. It lands in + # <self.sitedir>/extdir. That path is added to PYTHONPATH below so + # the subprocess can import it. + extdir = self._make_mod(f"""\ +import sys +def run(): + sys.stderr.write({sentinel!r}) + sys.stderr.flush() +""", name='gh149819mod') + self._make_start( + 'gh149819mod:run\n', name='gh149819', basedir=user_site + ) + with EnvironmentVarGuard() as env: + # See above for details. + env['PYTHONUSERBASE'] = user_base + env['PYTHONPATH'] = os.pathsep.join([user_site, extdir]) + result = subprocess.run( + [sys.executable, '-c', ''], + capture_output=True, + check=True, + ) + self.assertIn(sentinel.encode(), result.stderr) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py index 4c9fc14bd43f54..b8aac8c20202a2 100644 --- a/Lib/test/test_smtplib.py +++ b/Lib/test/test_smtplib.py @@ -17,6 +17,7 @@ import threading import unittest +import unittest.mock as mock from test import support, mock_socket from test.support import hashlib_helper from test.support import socket_helper @@ -926,11 +927,14 @@ def _auth_cram_md5(self, arg=None): except ValueError as e: self.push('535 Splitting response {!r} into user and password ' 'failed: {}'.format(logpass, e)) - return False - valid_hashed_pass = hmac.HMAC( - sim_auth[1].encode('ascii'), - self._decode_base64(sim_cram_md5_challenge).encode('ascii'), - 'md5').hexdigest() + return + pwd = sim_auth[1].encode('ascii') + msg = self._decode_base64(sim_cram_md5_challenge).encode('ascii') + try: + valid_hashed_pass = hmac.HMAC(pwd, msg, 'md5').hexdigest() + except ValueError: + self.push('504 CRAM-MD5 is not supported') + return self._authenticated(user, hashed_pass == valid_hashed_pass) # end AUTH related stuff. @@ -1181,6 +1185,39 @@ def testAUTH_CRAM_MD5(self): self.assertEqual(resp, (235, b'Authentication Succeeded')) smtp.close() + @hashlib_helper.block_algorithm('md5') + @mock.patch("smtplib._have_cram_md5_support", False) + def testAUTH_CRAM_MD5_blocked(self): + # CRAM-MD5 is the only "known" method by the server, + # but it is not supported by the client. In particular, + # no challenge will ever be sent. + self.serv.add_feature("AUTH CRAM-MD5") + smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', + timeout=support.LOOPBACK_TIMEOUT) + self.addCleanup(smtp.close) + msg = re.escape("No suitable authentication method found.") + with self.assertRaisesRegex(smtplib.SMTPException, msg): + smtp.login(sim_auth[0], sim_auth[1]) + + @hashlib_helper.block_algorithm('md5') + @mock.patch("smtplib._have_cram_md5_support", False) + def testAUTH_CRAM_MD5_blocked_and_fallback(self): + # Test that PLAIN is tried after CRAM-MD5 failed + self.serv.add_feature("AUTH CRAM-MD5 PLAIN") + smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', + timeout=support.LOOPBACK_TIMEOUT) + self.addCleanup(smtp.close) + with ( + mock.patch.object(smtp, "auth_cram_md5") as smtp_auth_cram_md5, + mock.patch.object( + smtp, "auth_plain", wraps=smtp.auth_plain + ) as smtp_auth_plain + ): + resp = smtp.login(sim_auth[0], sim_auth[1]) + smtp_auth_plain.assert_called_once() + smtp_auth_cram_md5.assert_not_called() + self.assertEqual(resp, (235, b'Authentication Succeeded')) + @hashlib_helper.requires_hashdigest('md5', openssl=True) def testAUTH_multiple(self): # Test that multiple authentication methods are tried. diff --git a/Lib/test/test_smtpnet.py b/Lib/test/test_smtpnet.py index d765746987bc4b..861e7e6a725c40 100644 --- a/Lib/test/test_smtpnet.py +++ b/Lib/test/test_smtpnet.py @@ -45,6 +45,59 @@ def test_connect_starttls(self): server.ehlo() server.quit() + def test_connect_host_port_starttls(self): + support.get_attribute(smtplib, 'SMTP_SSL') + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + with socket_helper.transient_internet(self.testServer): + server = smtplib.SMTP(f'{self.testServer}:{self.remotePort}') + try: + server.starttls(context=context) + except smtplib.SMTPException as e: + if e.args[0] == 'STARTTLS extension not supported by server.': + unittest.skip(e.args[0]) + else: + raise + server.ehlo() + server.quit() + + def test_explicit_connect_starttls(self): + support.get_attribute(smtplib, 'SMTP_SSL') + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + with socket_helper.transient_internet(self.testServer): + server = smtplib.SMTP() + server.connect(self.testServer, self.remotePort) + try: + server.starttls(context=context) + except smtplib.SMTPException as e: + if e.args[0] == 'STARTTLS extension not supported by server.': + unittest.skip(e.args[0]) + else: + raise + server.ehlo() + server.quit() + + def test_explicit_connect_host_port_starttls(self): + support.get_attribute(smtplib, 'SMTP_SSL') + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + with socket_helper.transient_internet(self.testServer): + server = smtplib.SMTP() + server.connect(f'{self.testServer}:{self.remotePort}') + try: + server.starttls(context=context) + except smtplib.SMTPException as e: + if e.args[0] == 'STARTTLS extension not supported by server.': + unittest.skip(e.args[0]) + else: + raise + server.ehlo() + server.quit() + class SmtpSSLTest(unittest.TestCase): testServer = SMTP_TEST_SERVER diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 3dd67b2a2aba97..63c465e3bfea16 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -8,7 +8,9 @@ import _thread as thread import array import contextlib +import decimal import errno +import fractions import gc import io import itertools @@ -52,6 +54,7 @@ VSOCKPORT = 1234 AIX = platform.system() == "AIX" +SOLARIS = sys.platform.startswith("sunos") WSL = "microsoft-standard-WSL" in platform.release() try: @@ -202,6 +205,25 @@ def _have_socket_hyperv(): return True +def _have_udp_lite(): + if not hasattr(socket, "IPPROTO_UDPLITE"): + return False + # Older Android versions block UDPLITE with SELinux. + if support.is_android and platform.android_ver().api_level < 29: + return False + + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDPLITE) + except OSError as exc: + # Linux 7.1 removed UDP Lite support + if exc.errno == errno.EPROTONOSUPPORT: + return False + raise + sock.close() + + return True + + @contextlib.contextmanager def socket_setdefaulttimeout(timeout): old_timeout = socket.getdefaulttimeout() @@ -244,10 +266,7 @@ def downgrade_malformed_data_warning(): HAVE_SOCKET_VSOCK = _have_socket_vsock() -# Older Android versions block UDPLITE with SELinux. -HAVE_SOCKET_UDPLITE = ( - hasattr(socket, "IPPROTO_UDPLITE") - and not (support.is_android and platform.android_ver().api_level < 29)) +HAVE_SOCKET_UDPLITE = _have_udp_lite() HAVE_SOCKET_BLUETOOTH = _have_socket_bluetooth() @@ -560,8 +579,8 @@ def clientTearDown(self): @unittest.skipIf(WSL, 'VSOCK does not work on Microsoft WSL') @unittest.skipUnless(HAVE_SOCKET_VSOCK, 'VSOCK sockets required for this test.') -@unittest.skipUnless(get_cid() != 2, # VMADDR_CID_HOST - "This test can only be run on a virtual guest.") +@unittest.skipIf(get_cid() == getattr(socket, 'VMADDR_CID_HOST', 2), + "This test can only be run on a virtual guest.") class ThreadedVSOCKSocketStreamTest(unittest.TestCase, ThreadableTest): def __init__(self, methodName='runTest'): @@ -571,7 +590,16 @@ def __init__(self, methodName='runTest'): def setUp(self): self.serv = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) self.addCleanup(self.serv.close) - self.serv.bind((socket.VMADDR_CID_ANY, VSOCKPORT)) + cid = get_cid() + if cid in (socket.VMADDR_CID_HOST, socket.VMADDR_CID_ANY): + cid = socket.VMADDR_CID_LOCAL + try: + self.serv.bind((cid, VSOCKPORT)) + except OSError as exc: + if exc.errno == errno.EADDRNOTAVAIL: + self.skipTest(f"bind() failed with {exc!r}") + else: + raise self.serv.listen() self.serverExplicitReady() self.serv.settimeout(support.LOOPBACK_TIMEOUT) @@ -1173,7 +1201,10 @@ def testInterfaceNameIndex(self): 'socket.if_indextoname() not available.') @support.skip_android_selinux('if_indextoname') def testInvalidInterfaceIndexToName(self): - self.assertRaises(OSError, socket.if_indextoname, 0) + with self.assertRaises(OSError) as cm: + socket.if_indextoname(0) + self.assertIsNotNone(cm.exception.errno) + self.assertRaises(ValueError, socket.if_indextoname, -1) self.assertRaises(OverflowError, socket.if_indextoname, 2**1000) self.assertRaises(TypeError, socket.if_indextoname, '_DEADBEEF') @@ -1193,8 +1224,11 @@ def testInvalidInterfaceIndexToName(self): 'socket.if_nametoindex() not available.') @support.skip_android_selinux('if_nametoindex') def testInvalidInterfaceNameToIndex(self): + with self.assertRaises(OSError) as cm: + socket.if_nametoindex("_DEADBEEF") + self.assertIsNotNone(cm.exception.errno) + self.assertRaises(TypeError, socket.if_nametoindex, 0) - self.assertRaises(OSError, socket.if_nametoindex, '_DEADBEEF') @unittest.skipUnless(hasattr(sys, 'getrefcount'), 'test needs sys.getrefcount()') @@ -1309,10 +1343,20 @@ def testDefaultTimeout(self): self.assertEqual(s.gettimeout(), None) # Set the default timeout to 10, and see if it propagates - with socket_setdefaulttimeout(10): - self.assertEqual(socket.getdefaulttimeout(), 10) + with socket_setdefaulttimeout(10.125): + self.assertEqual(socket.getdefaulttimeout(), 10.125) with socket.socket() as sock: - self.assertEqual(sock.gettimeout(), 10) + self.assertEqual(sock.gettimeout(), 10.125) + + socket.setdefaulttimeout(decimal.Decimal('11.125')) + self.assertEqual(socket.getdefaulttimeout(), 11.125) + with socket.socket() as sock: + self.assertEqual(sock.gettimeout(), 11.125) + + socket.setdefaulttimeout(fractions.Fraction(97, 8)) + self.assertEqual(socket.getdefaulttimeout(), 12.125) + with socket.socket() as sock: + self.assertEqual(sock.gettimeout(), 12.125) # Reset the default timeout to None, and see if it propagates socket.setdefaulttimeout(None) @@ -1410,7 +1454,7 @@ def testIPv6toString(self): assertInvalid('1:2:3:4:5:6:') assertInvalid('1:2:3:4:5:6:7:8:0') # bpo-29972: inet_pton() doesn't fail on AIX - if not AIX: + if not AIX and sys.platform != 'cygwin': assertInvalid('1:2:3:4:5:6:7:8:') self.assertEqual(b'\x00' * 12 + b'\xfe\x2a\x17\x40', @@ -1538,6 +1582,40 @@ def testSetSockOpt(self): reuse = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) self.assertFalse(reuse == 0, "failed to set reuse mode") + def test_setsockopt_errors(self): + # See issue #107546. + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.addCleanup(sock.close) + + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # No error expected. + + with self.assertRaises(OverflowError): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 2 ** 100) + + with self.assertRaises(OverflowError): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, - 2 ** 100) + + with self.assertRaises(OverflowError): + sock.setsockopt(socket.SOL_SOCKET, 2 ** 100, 1) + + with self.assertRaises(OverflowError): + sock.setsockopt(2 ** 100, socket.SO_REUSEADDR, 1) + + with self.assertRaisesRegex(TypeError, "socket option should be int, bytes-like object or None"): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, dict()) + + with self.assertRaisesRegex(TypeError, "requires 4 arguments when the third argument is None"): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, None) + + with self.assertRaisesRegex(TypeError, "only takes 4 arguments when the third argument is None"): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1, 2) + + with self.assertRaisesRegex(TypeError, "takes at least 3 arguments"): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) + + with self.assertRaisesRegex(TypeError, "takes at most 4 arguments"): + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1, 2, 3) + def testSendAfterClose(self): # testing send() after close() with timeout with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: @@ -1923,7 +2001,8 @@ def test_getfqdn_filter_localhost(self): self.assertEqual(socket.getfqdn(), socket.getfqdn("::")) @unittest.skipUnless(socket_helper.IPV6_ENABLED, 'IPv6 required for this test.') - @unittest.skipIf(sys.platform == 'win32', 'does not work on Windows') + @unittest.skipIf(sys.platform in ('win32', 'cygwin'), + 'does not work on Windows') @unittest.skipIf(AIX, 'Symbolic scope id does not work') @unittest.skipUnless(hasattr(socket, 'if_nameindex'), "test needs socket.if_nameindex()") @support.skip_android_selinux('if_nameindex') @@ -1957,7 +2036,7 @@ def test_getaddrinfo_ipv6_scopeid_numeric(self): self.assertEqual(sockaddr, ('ff02::1de:c0:face:8d', 1234, 0, ifindex)) @unittest.skipUnless(socket_helper.IPV6_ENABLED, 'IPv6 required for this test.') - @unittest.skipIf(sys.platform == 'win32', 'does not work on Windows') + @unittest.skipIf(sys.platform in ('win32', 'cygwin'), 'does not work on Windows') @unittest.skipIf(AIX, 'Symbolic scope id does not work') @unittest.skipUnless(hasattr(socket, 'if_nameindex'), "test needs socket.if_nameindex()") @support.skip_android_selinux('if_nameindex') @@ -2169,6 +2248,24 @@ def test_addressinfo_enum(self): source=_socket) enum._test_simple_enum(CheckedAddressInfo, socket.AddressInfo) + @unittest.skipUnless(hasattr(socket.socket, "sendmsg"),"sendmsg not supported") + def test_sendmsg_reentrant_ancillary_mutation(self): + + class Mut: + def __index__(self): + seq.clear() + return socket.SCM_RIGHTS + + seq = [ + (socket.SOL_SOCKET, Mut(), b'xxxx'), + (socket.SOL_SOCKET, socket.SCM_RIGHTS, b'xxxx'), + ] + + left, right = socket.socketpair() + self.addCleanup(left.close) + self.addCleanup(right.close) + self.assertRaises(OSError, left.sendmsg, [b'x'], seq) + @unittest.skipUnless(HAVE_SOCKET_CAN, 'SocketCan required for this test.') class BasicCANTest(unittest.TestCase): @@ -2358,6 +2455,45 @@ def testCrucialConstants(self): socket.CAN_ISOTP socket.SOCK_DGRAM + @unittest.skipUnless(hasattr(socket, "SOL_CAN_ISOTP"), + "missing <linux/can/isotp.h>") + def testISOTP(self): + socket.SOL_CAN_ISOTP + + socket.CAN_ISOTP_OPTS + socket.CAN_ISOTP_RECV_FC + + socket.CAN_ISOTP_TX_STMIN + socket.CAN_ISOTP_RX_STMIN + socket.CAN_ISOTP_LL_OPTS + + socket.CAN_ISOTP_LISTEN_MODE + socket.CAN_ISOTP_EXTEND_ADDR + socket.CAN_ISOTP_TX_PADDING + socket.CAN_ISOTP_RX_PADDING + socket.CAN_ISOTP_CHK_PAD_LEN + socket.CAN_ISOTP_CHK_PAD_DATA + socket.CAN_ISOTP_HALF_DUPLEX + socket.CAN_ISOTP_FORCE_TXSTMIN + socket.CAN_ISOTP_FORCE_RXSTMIN + socket.CAN_ISOTP_RX_EXT_ADDR + socket.CAN_ISOTP_WAIT_TX_DONE + # This constant is not always available + # socket.CAN_ISOTP_SF_BROADCAST + + socket.CAN_ISOTP_DEFAULT_FLAGS + socket.CAN_ISOTP_DEFAULT_EXT_ADDRESS + socket.CAN_ISOTP_DEFAULT_PAD_CONTENT + socket.CAN_ISOTP_DEFAULT_FRAME_TXTIME + socket.CAN_ISOTP_DEFAULT_RECV_BS + socket.CAN_ISOTP_DEFAULT_EXT_ADDRESS + socket.CAN_ISOTP_DEFAULT_RECV_STMIN + socket.CAN_ISOTP_DEFAULT_RECV_WFTMAX + + socket.CAN_ISOTP_DEFAULT_LL_MTU + socket.CAN_ISOTP_DEFAULT_LL_TX_DL + socket.CAN_ISOTP_DEFAULT_LL_TX_FLAGS + def testCreateSocket(self): with socket.socket(socket.PF_CAN, socket.SOCK_RAW, socket.CAN_RAW) as s: pass @@ -3888,6 +4024,10 @@ def testCMSG_SPACE(self): # Test CMSG_SPACE() with various valid and invalid values, # checking the assumptions used by sendmsg(). toobig = self.socklen_t_limit - socket.CMSG_SPACE(1) + 1 + if SOLARIS and platform.processor() == "sparc": + # On Solaris SPARC, number of bytes returned by socket.CMSG_SPACE + # increases at different lengths; see gh-91214. + toobig -= 3 values = list(range(257)) + list(range(toobig - 257, toobig)) last = socket.CMSG_SPACE(0) @@ -4034,6 +4174,7 @@ def _testFDPassCMSG_LEN(self): self.createAndSendFDs(1) @unittest.skipIf(is_apple, "skipping, see issue #12958") + @unittest.skipIf(SOLARIS, "skipping, see gh-91214") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparate(self): @@ -4045,6 +4186,7 @@ def testFDPassSeparate(self): @testFDPassSeparate.client_skip @unittest.skipIf(is_apple, "skipping, see issue #12958") + @unittest.skipIf(SOLARIS, "skipping, see gh-91214") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparate(self): fd0, fd1 = self.newFDs(2) @@ -4058,6 +4200,7 @@ def _testFDPassSeparate(self): len(MSG)) @unittest.skipIf(is_apple, "skipping, see issue #12958") + @unittest.skipIf(SOLARIS, "skipping, see gh-91214") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparateMinSpace(self): @@ -4072,6 +4215,7 @@ def testFDPassSeparateMinSpace(self): @testFDPassSeparateMinSpace.client_skip @unittest.skipIf(is_apple, "skipping, see issue #12958") + @unittest.skipIf(SOLARIS, "skipping, see gh-91214") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparateMinSpace(self): fd0, fd1 = self.newFDs(2) @@ -7045,7 +7189,7 @@ def test_aes_cbc(self): self.assertEqual(len(dec), msglen * multiplier) self.assertEqual(dec, msg * multiplier) - @support.requires_linux_version(4, 9) # see issue29324 + @support.requires_linux_version(4, 9) # see gh-73510 def test_aead_aes_gcm(self): key = bytes.fromhex('c939cc13397c1d37de6ae0e1cb7c423c') iv = bytes.fromhex('b3d8cc017cbb89b39e0f67e2') @@ -7385,6 +7529,62 @@ def detach(): pass +class ReentrantMutationTests(unittest.TestCase): + """Regression tests for re-entrant mutation in sendmsg/recvmsg_into. + + These tests verify that mutating sequences during argument parsing + via __buffer__ protocol does not cause crashes. + + See: https://github.com/python/cpython/issues/143988 + """ + + @unittest.skipUnless(hasattr(socket.socket, "sendmsg"), + "sendmsg not supported") + def test_sendmsg_reentrant_data_mutation(self): + seq = [] + + class MutBuffer: + def __init__(self): + self.tripped = False + + def __buffer__(self, flags): + if not self.tripped: + self.tripped = True + seq.clear() + return memoryview(b'Hello') + + seq = [MutBuffer(), b'World', b'Test'] + + left, right = socket.socketpair() + with left, right: + left.sendmsg(seq) + self.assertEqual(right.recv(1024), b'HelloWorldTest') + + @unittest.skipUnless(hasattr(socket.socket, "recvmsg_into"), + "recvmsg_into not supported") + def test_recvmsg_into_reentrant_buffer_mutation(self): + seq = [] + buf1 = bytearray(100) + + class MutBuffer: + def __init__(self): + self.tripped = False + + def __buffer__(self, flags): + if not self.tripped: + self.tripped = True + seq.clear() + return memoryview(buf1) + + seq = [MutBuffer(), bytearray(100), bytearray(100)] + + left, right = socket.socketpair() + with left, right: + left.send(b'Hello World!') + right.recvmsg_into(seq) + self.assertEqual(buf1, b'Hello World!'.ljust(100, b'\x00')) + + def setUpModule(): thread_info = threading_helper.threading_setup() unittest.addModuleCleanup(threading_helper.threading_cleanup, *thread_info) diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 0f62f9eb200e42..ca33a9a6dac8d3 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -17,6 +17,7 @@ from test.support import os_helper from test.support import socket_helper from test.support import threading_helper +from test.support import warnings_helper test.support.requires("network") @@ -43,6 +44,7 @@ def receive(sock, n, timeout=test.support.SHORT_TIMEOUT): raise RuntimeError("timed out on %r" % (sock,)) +@warnings_helper.ignore_fork_in_thread_deprecation_warnings() @test.support.requires_fork() @contextlib.contextmanager def simple_subprocess(testcase): @@ -173,6 +175,7 @@ def test_ThreadingTCPServer(self): socketserver.StreamRequestHandler, self.stream_examine) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_forking def test_ForkingTCPServer(self): with simple_subprocess(self): @@ -192,6 +195,7 @@ def test_ThreadingUnixStreamServer(self): socketserver.StreamRequestHandler, self.stream_examine) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_unix_sockets @requires_forking def test_ForkingUnixStreamServer(self): @@ -210,6 +214,7 @@ def test_ThreadingUDPServer(self): socketserver.DatagramRequestHandler, self.dgram_examine) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_forking def test_ForkingUDPServer(self): with simple_subprocess(self): @@ -218,17 +223,22 @@ def test_ForkingUDPServer(self): self.dgram_examine) @requires_unix_sockets + @unittest.skipIf(test.support.is_apple_mobile and test.support.on_github_actions, + "gh-140702: Test fails regularly on iOS simulator on GitHub Actions") def test_UnixDatagramServer(self): self.run_server(socketserver.UnixDatagramServer, socketserver.DatagramRequestHandler, self.dgram_examine) @requires_unix_sockets + @unittest.skipIf(test.support.is_apple_mobile and test.support.on_github_actions, + "gh-140702: Test fails regularly on iOS simulator on GitHub Actions") def test_ThreadingUnixDatagramServer(self): self.run_server(socketserver.ThreadingUnixDatagramServer, socketserver.DatagramRequestHandler, self.dgram_examine) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_unix_sockets @requires_forking def test_ForkingUnixDatagramServer(self): @@ -314,11 +324,13 @@ def test_threading_not_handled(self): self.assertIs(cm.exc_type, SystemExit) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_forking def test_forking_handled(self): ForkingErrorTestServer(ValueError) self.check_result(handled=True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_forking def test_forking_not_handled(self): ForkingErrorTestServer(SystemExit) @@ -503,5 +515,15 @@ class MyServer(socketserver.ThreadingMixIn, socketserver.TCPServer): server.server_close() +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(socketserver, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_source_encoding.py b/Lib/test/test_source_encoding.py index 1399f3fcd2d393..53fffe7cfb56d0 100644 --- a/Lib/test/test_source_encoding.py +++ b/Lib/test/test_source_encoding.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- import unittest -from test.support import script_helper, captured_stdout, requires_subprocess, requires_resource +from test import support +from test.support import script_helper from test.support.os_helper import TESTFN, unlink, rmtree from test.support.import_helper import unload import importlib @@ -64,7 +65,24 @@ def test_issue7820(self): # two bytes in common with the UTF-8 BOM self.assertRaises(SyntaxError, eval, b'\xef\xbb\x20') - @requires_subprocess() + def test_truncated_utf8_at_eof(self): + # Regression test for https://issues.oss-fuzz.com/issues/451112368 + # Truncated multi-byte UTF-8 sequences at end of input caused an + # out-of-bounds read in Parser/tokenizer/helpers.c:valid_utf8(). + truncated = [ + b'\xc2', # 2-byte lead, missing 1 continuation + b'\xdf', # 2-byte lead, missing 1 continuation + b'\xe0', # 3-byte lead, missing 2 continuations + b'\xe0\xa0', # 3-byte lead, missing 1 continuation + b'\xf0\x90', # 4-byte lead, missing 2 continuations + b'\xf0\x90\x80', # 4-byte lead, missing 1 continuation + b'\xf3', # 4-byte lead, missing 3 (the oss-fuzz reproducer) + ] + for seq in truncated: + with self.subTest(seq=seq): + self.assertRaises(SyntaxError, compile, seq, '<test>', 'exec') + + @support.requires_subprocess() def test_20731(self): sub = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), @@ -172,6 +190,8 @@ def test_tokenizer_fstring_warning_in_first_line(self): os.unlink(TESTFN) +BUFSIZ = 2**13 + class AbstractSourceEncodingTest: def test_default_coding(self): @@ -184,14 +204,20 @@ def test_first_coding_line(self): self.check_script_output(src, br"'\xc3\u20ac'") def test_second_coding_line(self): - src = (b'#\n' + src = (b'#!/usr/bin/python\n' + b'#coding:iso8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_second_coding_line_empty_first_line(self): + src = (b'\n' b'#coding:iso8859-15\n' b'print(ascii("\xc3\xa4"))\n') self.check_script_output(src, br"'\xc3\u20ac'") def test_third_coding_line(self): # Only first two lines are tested for a magic comment. - src = (b'#\n' + src = (b'#!/usr/bin/python\n' b'#\n' b'#coding:iso8859-15\n' b'print(ascii("\xc3\xa4"))\n') @@ -209,48 +235,196 @@ def test_double_coding_same_line(self): b'print(ascii("\xc3\xa4"))\n') self.check_script_output(src, br"'\xc3\u20ac'") + def test_double_coding_utf8(self): + src = (b'#coding:utf-8\n' + b'#coding:latin1\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xe4'") + + def test_long_first_coding_line(self): + src = (b'#' + b' '*BUFSIZ + b'coding:iso8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_long_second_coding_line(self): + src = (b'#!/usr/bin/python\n' + b'#' + b' '*BUFSIZ + b'coding:iso8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_long_coding_line(self): + src = (b'#coding:iso-8859-15' + b' '*BUFSIZ + b'\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_long_coding_name(self): + src = (b'#coding:iso-8859-1-' + b'x'*BUFSIZ + b'\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\xa4'") + + def test_long_first_utf8_line(self): + src = b'#' + b'\xc3\xa4'*(BUFSIZ//2) + b'\n' + self.check_script_output(src, b'') + src = b'# ' + b'\xc3\xa4'*(BUFSIZ//2) + b'\n' + self.check_script_output(src, b'') + + def test_long_second_utf8_line(self): + src = b'\n#' + b'\xc3\xa4'*(BUFSIZ//2) + b'\n' + self.check_script_output(src, b'') + src = b'\n# ' + b'\xc3\xa4'*(BUFSIZ//2) + b'\n' + self.check_script_output(src, b'') + def test_first_non_utf8_coding_line(self): src = (b'#coding:iso-8859-15 \xa4\n' b'print(ascii("\xc3\xa4"))\n') self.check_script_output(src, br"'\xc3\u20ac'") def test_second_non_utf8_coding_line(self): - src = (b'\n' + src = (b'#!/usr/bin/python\n' b'#coding:iso-8859-15 \xa4\n' b'print(ascii("\xc3\xa4"))\n') self.check_script_output(src, br"'\xc3\u20ac'") + def test_first_utf8_coding_line_error(self): + src = (b'#coding:ascii \xc3\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"(\(unicode error\) )?'ascii' codec can't decode byte") + + def test_second_utf8_coding_line_error(self): + src = (b'#!/usr/bin/python\n' + b'#coding:ascii \xc3\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"(\(unicode error\) )?'ascii' codec can't decode byte") + def test_utf8_bom(self): src = (b'\xef\xbb\xbfprint(ascii("\xc3\xa4"))\n') self.check_script_output(src, br"'\xe4'") + def test_utf8_bom_utf8_comments(self): + src = (b'\xef\xbb\xbf#\xc3\xa4\n' + b'#\xc3\xa4\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xe4'") + def test_utf8_bom_and_utf8_coding_line(self): src = (b'\xef\xbb\xbf#coding:utf-8\n' b'print(ascii("\xc3\xa4"))\n') self.check_script_output(src, br"'\xe4'") + def test_utf8_bom_and_non_utf8_first_coding_line(self): + src = (b'\xef\xbb\xbf#coding:iso-8859-15\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"encoding problem: iso-8859-15 with BOM", + lineno=1) + + def test_utf8_bom_and_non_utf8_second_coding_line(self): + src = (b'\xef\xbb\xbf#first\n' + b'#coding:iso-8859-15\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"encoding problem: iso-8859-15 with BOM", + lineno=2) + + def test_non_utf8_shebang(self): + src = (b'#!/home/\xa4/bin/python\n' + b'#coding:iso-8859-15\n' + b'print(ascii("\xc3\xa4"))\n') + self.check_script_output(src, br"'\xc3\u20ac'") + + def test_utf8_shebang_error(self): + src = (b'#!/home/\xc3\xa4/bin/python\n' + b'#coding:ascii\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"(\(unicode error\) )?'ascii' codec can't decode byte") + + def test_non_utf8_shebang_error(self): + src = (b'#!/home/\xa4/bin/python\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"Non-UTF-8 code starting with .* on line 1", + lineno=1) + + def test_non_utf8_second_line_error(self): + src = (b'#first\n' + b'#second\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"Non-UTF-8 code starting with .* on line 2", + lineno=2) + + def test_non_utf8_third_line_error(self): + src = (b'#first\n' + b'#second\n' + b'#third\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"Non-UTF-8 code starting with .* on line 3", + lineno=3) + + def test_utf8_bom_non_utf8_third_line_error(self): + src = (b'\xef\xbb\xbf#first\n' + b'#second\n' + b'#third\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"Non-UTF-8 code starting with .* on line 3|" + br"'utf-8' codec can't decode byte", + lineno=3) + + def test_utf_8_non_utf8_third_line_error(self): + src = (b'#coding: utf-8\n' + b'#second\n' + b'#third\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"Non-UTF-8 code starting with .* on line 3|" + br"'utf-8' codec can't decode byte", + lineno=3) + + def test_utf8_non_utf8_third_line_error(self): + src = (b'#coding: utf8\n' + b'#second\n' + b'#third\xa4\n' + b'raise RuntimeError\n') + self.check_script_error(src, + br"'utf-8' codec can't decode byte") + def test_crlf(self): src = (b'print(ascii("""\r\n"""))\n') - out = self.check_script_output(src, br"'\n'") + self.check_script_output(src, br"'\n'") def test_crcrlf(self): src = (b'print(ascii("""\r\r\n"""))\n') - out = self.check_script_output(src, br"'\n\n'") + self.check_script_output(src, br"'\n\n'") def test_crcrcrlf(self): src = (b'print(ascii("""\r\r\r\n"""))\n') - out = self.check_script_output(src, br"'\n\n\n'") + self.check_script_output(src, br"'\n\n\n'") def test_crcrcrlf2(self): src = (b'#coding:iso-8859-1\n' b'print(ascii("""\r\r\r\n"""))\n') - out = self.check_script_output(src, br"'\n\n\n'") + self.check_script_output(src, br"'\n\n\n'") + + def test_nul_in_first_coding_line(self): + src = (b'#coding:iso8859-15\x00\n' + b'\n' + b'\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"source code (string )?cannot contain null bytes") + + def test_nul_in_second_coding_line(self): + src = (b'#!/usr/bin/python\n' + b'#coding:iso8859-15\x00\n' + b'\n' + b'raise RuntimeError\n') + self.check_script_error(src, br"source code (string )?cannot contain null bytes") class UTF8ValidatorTest(unittest.TestCase): @unittest.skipIf(not sys.platform.startswith("linux"), "Too slow to run on non-Linux platforms") - @requires_resource('cpu') + @support.requires_resource('cpu') def test_invalid_utf8(self): # This is a port of test_utf8_decode_invalid_sequences in # test_unicode.py to exercise the separate utf8 validator in @@ -316,15 +490,29 @@ def check(content): check(b'\xF4'+cb+b'\xBF\xBF') +@support.force_not_colorized_test_class class BytesSourceEncodingTest(AbstractSourceEncodingTest, unittest.TestCase): def check_script_output(self, src, expected): - with captured_stdout() as stdout: + with support.captured_stdout() as stdout: exec(src) out = stdout.getvalue().encode('latin1') self.assertEqual(out.rstrip(), expected) + def check_script_error(self, src, expected, lineno=...): + with self.assertRaises(SyntaxError) as cm: + exec(src) + exc = cm.exception + self.assertRegex(str(exc), expected.decode()) + if lineno is not ...: + self.assertEqual(exc.lineno, lineno) + line = src.splitlines()[lineno-1].decode(errors='replace') + if lineno == 1: + line = line.removeprefix('\ufeff') + self.assertEqual(line, exc.text) + +@support.force_not_colorized_test_class class FileSourceEncodingTest(AbstractSourceEncodingTest, unittest.TestCase): def check_script_output(self, src, expected): @@ -335,6 +523,37 @@ def check_script_output(self, src, expected): res = script_helper.assert_python_ok(fn) self.assertEqual(res.out.rstrip(), expected) + def check_script_error(self, src, expected, lineno=...): + with tempfile.TemporaryDirectory() as tmpd: + fn = os.path.join(tmpd, 'test.py') + with open(fn, 'wb') as fp: + fp.write(src) + res = script_helper.assert_python_failure(fn) + err = res.err.rstrip() + self.assertRegex(err.splitlines()[-1], b'SyntaxError: ' + expected) + if lineno is not ...: + self.assertIn(f', line {lineno}\n'.encode(), + err.replace(os.linesep.encode(), b'\n')) + line = src.splitlines()[lineno-1].decode(errors='replace') + if lineno == 1: + line = line.removeprefix('\ufeff') + self.assertIn(line.encode(), err) + + def test_coding_spec_unknown_encoding(self): + src = (b'# coding: c1252\n' + b'print("Hi!")\n') + self.check_script_error(src, br"unknown encoding: c1252") + + def test_coding_spec_decode_error(self): + src = (b'# coding: shift-jis\n' + b'print("\xc4\x85")\n') + self.check_script_error(src, br"'shift_jis' codec can't decode byte") + + def test_coding_spec_non_text_encoding(self): + src = (b'# coding: hex_codec\n' + b'print("eggs")\n') + self.check_script_error(src, br"'hex_codec' is not a text encoding") + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_sqlite3/__init__.py b/Lib/test/test_sqlite3/__init__.py index d777fca82da4b0..145f3b80024829 100644 --- a/Lib/test/test_sqlite3/__init__.py +++ b/Lib/test/test_sqlite3/__init__.py @@ -6,10 +6,14 @@ import os import sqlite3 +# make sure only print once +_printed_version = False + # Implement the unittest "load tests" protocol. -def load_tests(*args): +def load_tests(loader, tests, pattern): + global _printed_version + if verbose and not _printed_version: + print(f"test_sqlite3: testing with SQLite version {sqlite3.sqlite_version}") + _printed_version = True pkg_dir = os.path.dirname(__file__) - return load_package_tests(pkg_dir, *args) - -if verbose: - print(f"test_sqlite3: testing with SQLite version {sqlite3.sqlite_version}") + return load_package_tests(pkg_dir, loader, tests, pattern) diff --git a/Lib/test/test_sqlite3/test_cli.py b/Lib/test/test_sqlite3/test_cli.py index 720fa3c4c1ea8b..1fc0236780fa8b 100644 --- a/Lib/test/test_sqlite3/test_cli.py +++ b/Lib/test/test_sqlite3/test_cli.py @@ -80,8 +80,8 @@ def test_cli_on_disk_db(self): @force_not_colorized_test_class class InteractiveSession(unittest.TestCase): MEMORY_DB_MSG = "Connected to a transient in-memory database" - PS1 = "sqlite> " - PS2 = "... " + PS1 = "\001\002sqlite> \001\002" + PS2 = "\001\002 ... \001\002" def run_cli(self, *args, commands=()): with ( @@ -202,8 +202,8 @@ def test_interact_on_disk_file(self): def test_color(self): with unittest.mock.patch("_colorize.can_colorize", return_value=True): out, err = self.run_cli(commands="TEXT\n") - self.assertIn("\x1b[1;35msqlite> \x1b[0m", out) - self.assertIn("\x1b[1;35m ... \x1b[0m\x1b", out) + self.assertIn("\x01\x1b[1;35m\x02sqlite> \x01\x1b[0m\x02", out) + self.assertIn("\x01\x1b[1;35m\x02 ... \x01\x1b[0m\x02\x01\x1b", out) out, err = self.run_cli(commands=("sel;",)) self.assertIn('\x1b[1;35mOperationalError (SQLITE_ERROR)\x1b[0m: ' '\x1b[35mnear "sel": syntax error\x1b[0m', err) @@ -212,14 +212,14 @@ def test_color(self): @requires_subprocess() @force_not_colorized_test_class class Completion(unittest.TestCase): + # run_pty() creates a real terminal environment, where sqlite3 CLI + # SqliteInteractiveConsole invokes GNU Readline for input. Readline's + # _rl_strip_prompt() strips \001 and \002 from the output, so test + # assertions use the plain prompt. PS1 = "sqlite> " @classmethod def setUpClass(cls): - _sqlite3 = import_module("_sqlite3") - if not hasattr(_sqlite3, "SQLITE_KEYWORDS"): - raise unittest.SkipTest("unable to determine SQLite keywords") - readline = import_module("readline") if readline.backend == "editline": raise unittest.SkipTest("libedit readline is not supported") @@ -229,12 +229,24 @@ def write_input(self, input_, env=None): import readline from sqlite3.__main__ import main + # Configure readline to ...: + # - hide control sequences surrounding each candidate + # - hide "Display all xxx possibilities? (y or n)" + # - show candidates one per line readline.parse_and_bind("set colored-completion-prefix off") + readline.parse_and_bind("set completion-query-items 0") + readline.parse_and_bind("set page-completions off") + readline.parse_and_bind("set completion-display-width 0") + main() """) return run_pty(script, input_, env) def test_complete_sql_keywords(self): + _sqlite3 = import_module("_sqlite3") + if not hasattr(_sqlite3, "SQLITE_KEYWORDS"): + raise unittest.SkipTest("unable to determine SQLite keywords") + # List candidates starting with 'S', there should be multiple matches. input_ = b"S\t\tEL\t 1;\n.quit\n" output = self.write_input(input_) @@ -249,6 +261,123 @@ def test_complete_sql_keywords(self): self.assertIn(b"SELECT", output) self.assertIn(b"(1,)", output) + # .commands are completed without changing case + input_ = b".ver\t\n.quit\n" + output = self.write_input(input_) + self.assertIn(b".version", output) + + def test_complete_table_indexes_triggers_views(self): + input_ = textwrap.dedent("""\ + CREATE TABLE _Table (id); + CREATE INDEX _Index ON _table (id); + CREATE TRIGGER _Trigger BEFORE INSERT + ON _Table BEGIN SELECT 1; END; + CREATE VIEW _View AS SELECT 1; + + CREATE TEMP TABLE _Temp_table (id); + CREATE INDEX temp._Temp_index ON _Temp_table (id); + CREATE TEMP TRIGGER _Temp_trigger BEFORE INSERT + ON _Table BEGIN SELECT 1; END; + CREATE TEMP VIEW _Temp_view AS SELECT 1; + + ATTACH ':memory:' AS attached; + CREATE TABLE attached._Attached_table (id); + CREATE INDEX attached._Attached_index ON _Attached_table (id); + CREATE TRIGGER attached._Attached_trigger BEFORE INSERT + ON _Attached_table BEGIN SELECT 1; END; + CREATE VIEW attached._Attached_view AS SELECT 1; + + SELECT id FROM _\t\tta\t; + .quit\n""").encode() + output = self.write_input(input_) + lines = output.decode().splitlines() + indices = [i for i, line in enumerate(lines) + if line.startswith(self.PS1)] + start, end = indices[-3], indices[-2] + candidates = [l.strip() for l in lines[start+1:end]] + self.assertEqual(candidates, + [ + "_Attached_index", + "_Attached_table", + "_Attached_trigger", + "_Attached_view", + "_Index", + "_Table", + "_Temp_index", + "_Temp_table", + "_Temp_trigger", + "_Temp_view", + "_Trigger", + "_View", + ], + ) + start, end = indices[-2], indices[-1] + # direct match with '_Table' completed, no candidates displayed + candidates = [l.strip() for l in lines[start+1:end]] + self.assertEqual(len(candidates), 0) + + @unittest.skipIf(sqlite3.sqlite_version_info < (3, 16, 0), + "PRAGMA table-valued function is not available until " + "SQLite 3.16.0") + def test_complete_columns(self): + input_ = textwrap.dedent("""\ + CREATE TABLE _table (_col_table); + CREATE TEMP TABLE _temp_table (_col_temp); + ATTACH ':memory:' AS attached; + CREATE TABLE attached._attached_table (_col_attached); + + SELECT _col_\t\tta\tFROM _table; + .quit\n""").encode() + output = self.write_input(input_) + lines = output.decode().splitlines() + indices = [ + i for i, line in enumerate(lines) if line.startswith(self.PS1) + ] + start, end = indices[-3], indices[-2] + candidates = [l.strip() for l in lines[start+1:end]] + + self.assertEqual( + candidates, ["_col_attached", "_col_table", "_col_temp"] + ) + + @unittest.skipIf(sqlite3.sqlite_version_info < (3, 30, 0), + "PRAGMA function_list is not available until " + "SQLite 3.30.0") + def test_complete_functions(self): + input_ = b"SELECT AV\t1);\n.quit\n" + output = self.write_input(input_) + self.assertIn(b"AVG(1);", output) + self.assertIn(b"(1.0,)", output) + + # Functions are completed in upper case for even lower case user input. + input_ = b"SELECT av\t1);\n.quit\n" + output = self.write_input(input_) + self.assertIn(b"AVG(1);", output) + self.assertIn(b"(1.0,)", output) + + def test_complete_schemata(self): + input_ = textwrap.dedent("""\ + ATTACH ':memory:' AS MixedCase; + -- Test '_' is escaped in Like pattern filtering + ATTACH ':memory:' AS _underscore; + -- Let database_list pragma have a 'temp' schema entry + CREATE TEMP TABLE _table (id); + + SELECT * FROM \t\tmIX\t.sqlite_master; + SELECT * FROM _und\t.sqlite_master; + .quit\n""").encode() + output = self.write_input(input_) + lines = output.decode().splitlines() + indices = [ + i for i, line in enumerate(lines) if line.startswith(self.PS1) + ] + start, end = indices[-4], indices[-3] + candidates = [l.strip() for l in lines[start+1:end]] + self.assertIn("MixedCase", candidates) + self.assertIn("_underscore", candidates) + self.assertIn("main", candidates) + self.assertIn("temp", candidates) + @unittest.skipIf(sys.platform.startswith("freebsd"), "Two actual tabs are inserted when there are no matching" " completions in the pseudo-terminal opened by run_pty()" @@ -269,8 +398,6 @@ def test_complete_no_match(self): self.assertEqual(line_num, len(lines)) def test_complete_no_input(self): - from _sqlite3 import SQLITE_KEYWORDS - script = textwrap.dedent(""" import readline from sqlite3.__main__ import main @@ -301,7 +428,7 @@ def test_complete_no_input(self): self.assertEqual(len(indices), 2) start, end = indices candidates = [l.strip() for l in lines[start+1:end]] - self.assertEqual(candidates, sorted(SQLITE_KEYWORDS)) + self.assertEqual(candidates, sorted(candidates)) except: if verbose: print(' PTY output: '.center(30, '-')) diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 3602726437d8cf..5f6cb527955ca1 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -21,6 +21,7 @@ # 3. This notice may not be removed or altered from any source distribution. import contextlib +import functools import os import sqlite3 as sqlite import subprocess @@ -630,6 +631,14 @@ def test_deserialize_corrupt_database(self): class OpenTests(unittest.TestCase): _sql = "create table test(id integer)" + def test_open_with_bytes_path(self): + path = os.fsencode(TESTFN) + self.addCleanup(unlink, path) + self.assertFalse(os.path.exists(path)) + with contextlib.closing(sqlite.connect(path)) as cx: + self.assertTrue(os.path.exists(path)) + cx.execute(self._sql) + def test_open_with_path_like_object(self): """ Checks that we can successfully connect to a database using an object that is PathLike, i.e. has __fspath__(). """ @@ -1052,7 +1061,7 @@ def test_array_size(self): # now set to 2 self.cu.arraysize = 2 - # now make the query return 3 rows + # now make the query return 2 rows from a table of 3 rows self.cu.execute("delete from test") self.cu.execute("insert into test(name) values ('A')") self.cu.execute("insert into test(name) values ('B')") @@ -1062,13 +1071,50 @@ def test_array_size(self): self.assertEqual(len(res), 2) + def test_invalid_array_size(self): + UINT32_MAX = (1 << 32) - 1 + setter = functools.partial(setattr, self.cu, 'arraysize') + + self.assertRaises(TypeError, setter, 1.0) + self.assertRaises(ValueError, setter, -3) + self.assertRaises(OverflowError, setter, UINT32_MAX + 1) + def test_fetchmany(self): + # no active SQL statement + res = self.cu.fetchmany() + self.assertEqual(res, []) + res = self.cu.fetchmany(1000) + self.assertEqual(res, []) + + # test default parameter + self.cu.execute("select name from test") + res = self.cu.fetchmany() + self.assertEqual(len(res), 1) + + # test when the number of requested rows exceeds the actual count self.cu.execute("select name from test") res = self.cu.fetchmany(100) self.assertEqual(len(res), 1) res = self.cu.fetchmany(100) self.assertEqual(res, []) + # test when size = 0 + self.cu.execute("select name from test") + res = self.cu.fetchmany(0) + self.assertEqual(res, []) + res = self.cu.fetchmany(100) + self.assertEqual(len(res), 1) + res = self.cu.fetchmany(100) + self.assertEqual(res, []) + + def test_invalid_fetchmany(self): + UINT32_MAX = (1 << 32) - 1 + fetchmany = self.cu.fetchmany + + self.assertRaises(TypeError, fetchmany, 1.0) + self.assertRaises(ValueError, fetchmany, -3) + self.assertRaises(OverflowError, fetchmany, UINT32_MAX + 1) + def test_fetchmany_kw_arg(self): """Checks if fetchmany works with keyword arguments""" self.cu.execute("select name from test") @@ -1333,6 +1379,11 @@ def test_blob_get_slice(self): def test_blob_get_empty_slice(self): self.assertEqual(self.blob[5:5], b"") + def test_blob_get_empty_slice_oob_indices(self): + self.cx.execute("insert into test(b) values (?)", (b"abc",)) + with self.cx.blobopen("test", "b", 2) as blob: + self.assertEqual(blob[5:-5], b"") + def test_blob_get_slice_negative_index(self): self.assertEqual(self.blob[5:-5], self.data[5:-5]) @@ -1349,6 +1400,18 @@ def test_blob_set_empty_slice(self): self.blob[0:0] = b"" self.assertEqual(self.blob[:], self.data) + def test_blob_set_empty_slice_wrong_type(self): + with self.assertRaises(TypeError): + self.blob[5:5] = None + + def test_blob_set_empty_slice_wrong_size(self): + with self.assertRaisesRegex(IndexError, "wrong size"): + self.blob[5:5] = b"123" + + def test_blob_set_empty_slice_correct(self): + self.blob[5:5] = b"" + self.assertEqual(self.blob[:], self.data) + def test_blob_set_slice_with_skip(self): self.blob[0:10:2] = b"12345" actual = self.cx.execute("select b from test").fetchone()[0] diff --git a/Lib/test/test_sqlite3/test_factory.py b/Lib/test/test_sqlite3/test_factory.py index 776659e3b16108..a9abeab3193688 100644 --- a/Lib/test/test_sqlite3/test_factory.py +++ b/Lib/test/test_sqlite3/test_factory.py @@ -146,6 +146,16 @@ def test_sqlite_row_index(self): with self.assertRaises(IndexError): row[complex()] # index must be int or string + def test_delete_connection_row_factory(self): + # gh-149738: deleting row_factory should raise an exception + with self.assertRaises(AttributeError): + del self.con.row_factory + + def test_delete_connection_text_factory(self): + # gh-149738: deleting text_factory should raise an exception + with self.assertRaises(AttributeError): + del self.con.text_factory + def test_sqlite_row_index_unicode(self): row = self.con.execute("select 1 as \xff").fetchone() self.assertEqual(row["\xff"], 1) diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index 2b907e35131d06..7e5117771b379f 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -24,11 +24,15 @@ import sqlite3 as sqlite import unittest +from test.support import import_helper from test.support.os_helper import TESTFN, unlink from .util import memory_database, cx_limit, with_tracebacks from .util import MemoryDatabaseMixin +# TODO(picnixz): increase test coverage for other callbacks +# such as 'func', 'step', 'finalize', and 'collation'. + class CollationTests(MemoryDatabaseMixin, unittest.TestCase): @@ -116,6 +120,21 @@ def test_collation_register_twice(self): self.assertEqual(result[0][0], 'b') self.assertEqual(result[1][0], 'a') + def test_collation_register_when_busy(self): + # See https://github.com/python/cpython/issues/146090. + con = self.con + con.create_collation("mycoll", lambda x, y: (x > y) - (x < y)) + con.execute("CREATE TABLE t(x TEXT)") + con.execute("INSERT INTO t VALUES (?)", ("a",)) + con.execute("INSERT INTO t VALUES (?)", ("b",)) + con.commit() + + cursor = self.con.execute("SELECT x FROM t ORDER BY x COLLATE mycoll") + next(cursor) + # Replace the collation while the statement is active -> SQLITE_BUSY. + with self.assertRaises(sqlite.OperationalError) as cm: + self.con.create_collation("mycoll", lambda a, b: 0) + def test_deregister_collation(self): """ Register a collation, then deregister it. Make sure an error is raised if we try @@ -129,8 +148,55 @@ def test_deregister_collation(self): self.assertEqual(str(cm.exception), 'no such collation sequence: mycoll') +class AuthorizerTests(MemoryDatabaseMixin, unittest.TestCase): + + def assert_not_authorized(self, func, /, *args, **kwargs): + with self.assertRaisesRegex(sqlite.DatabaseError, "not authorized"): + func(*args, **kwargs) + + # When a handler has an invalid signature, the exception raised is + # the same that would be raised if the handler "negatively" replied. + + def test_authorizer_invalid_signature(self): + self.cx.execute("create table if not exists test(a number)") + self.cx.set_authorizer(lambda: None) + self.assert_not_authorized(self.cx.execute, "select * from test") + + # Tests for checking that callback context mutations do not crash. + # Regression tests for https://github.com/python/cpython/issues/142830. + + @with_tracebacks(ZeroDivisionError, regex="hello world") + def test_authorizer_concurrent_mutation_in_call(self): + self.cx.execute("create table if not exists test(a number)") + + def handler(*a, **kw): + self.cx.set_authorizer(None) + raise ZeroDivisionError("hello world") + + self.cx.set_authorizer(handler) + self.assert_not_authorized(self.cx.execute, "select * from test") + + @with_tracebacks(OverflowError) + def test_authorizer_concurrent_mutation_with_overflown_value(self): + _testcapi = import_helper.import_module("_testcapi") + self.cx.execute("create table if not exists test(a number)") + + def handler(*a, **kw): + self.cx.set_authorizer(None) + # We expect 'int' at the C level, so this one will raise + # when converting via PyLong_Int(). + return _testcapi.INT_MAX + 1 + + self.cx.set_authorizer(handler) + self.assert_not_authorized(self.cx.execute, "select * from test") + + class ProgressTests(MemoryDatabaseMixin, unittest.TestCase): + def assert_interrupted(self, func, /, *args, **kwargs): + with self.assertRaisesRegex(sqlite.OperationalError, "interrupted"): + func(*args, **kwargs) + def test_progress_handler_used(self): """ Test that the progress handler is invoked once it is set. @@ -219,11 +285,48 @@ def bad_progress(): create table foo(a, b) """) - def test_progress_handler_keyword_args(self): + def test_set_progress_handler_keyword_args(self): with self.assertRaisesRegex(TypeError, 'takes at least 1 positional argument'): self.con.set_progress_handler(progress_handler=lambda: None, n=1) + # When a handler has an invalid signature, the exception raised is + # the same that would be raised if the handler "negatively" replied. + + def test_progress_handler_invalid_signature(self): + self.cx.execute("create table if not exists test(a number)") + self.cx.set_progress_handler(lambda x: None, 1) + self.assert_interrupted(self.cx.execute, "select * from test") + + # Tests for checking that callback context mutations do not crash. + # Regression tests for https://github.com/python/cpython/issues/142830. + + @with_tracebacks(ZeroDivisionError, regex="hello world") + def test_progress_handler_concurrent_mutation_in_call(self): + self.cx.execute("create table if not exists test(a number)") + + def handler(*a, **kw): + self.cx.set_progress_handler(None, 1) + raise ZeroDivisionError("hello world") + + self.cx.set_progress_handler(handler, 1) + self.assert_interrupted(self.cx.execute, "select * from test") + + def test_progress_handler_concurrent_mutation_in_conversion(self): + self.cx.execute("create table if not exists test(a number)") + + class Handler: + def __bool__(_): + # clear the progress handler + self.cx.set_progress_handler(None, 1) + raise ValueError # force PyObject_True() to fail + + self.cx.set_progress_handler(Handler.__init__, 1) + self.assert_interrupted(self.cx.execute, "select * from test") + + # Running with tracebacks makes the second execution of this + # function raise another exception because of a database change. + class TraceCallbackTests(MemoryDatabaseMixin, unittest.TestCase): @@ -345,11 +448,40 @@ def test_trace_bad_handler(self): cx.set_trace_callback(lambda stmt: 5/0) cx.execute("select 1") - def test_trace_keyword_args(self): + def test_set_trace_callback_keyword_args(self): with self.assertRaisesRegex(TypeError, 'takes exactly 1 positional argument'): self.con.set_trace_callback(trace_callback=lambda: None) + # When a handler has an invalid signature, the exception raised is + # the same that would be raised if the handler "negatively" replied, + # but for the trace handler, exceptions are never re-raised (only + # printed when needed). + + @with_tracebacks( + TypeError, + regex=r".*<lambda>\(\) missing 6 required positional arguments", + ) + def test_trace_handler_invalid_signature(self): + self.cx.execute("create table if not exists test(a number)") + self.cx.set_trace_callback(lambda x, y, z, t, a, b, c: None) + self.cx.execute("select * from test") + + # Tests for checking that callback context mutations do not crash. + # Regression tests for https://github.com/python/cpython/issues/142830. + + @with_tracebacks(ZeroDivisionError, regex="hello world") + def test_trace_callback_concurrent_mutation_in_call(self): + self.cx.execute("create table if not exists test(a number)") + + def handler(statement): + # clear the progress handler + self.cx.set_trace_callback(None) + raise ZeroDivisionError("hello world") + + self.cx.set_trace_callback(handler) + self.cx.execute("select * from test") + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 9e519537ca5ed3..40111d41007795 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -1,5 +1,6 @@ # Test the support for SSL and sockets +import contextlib import sys import unittest import unittest.mock @@ -47,9 +48,24 @@ PROTOCOLS = sorted(ssl._PROTOCOL_NAMES) HOST = socket_helper.HOST +IS_AWS_LC = "AWS-LC" in ssl.OPENSSL_VERSION IS_OPENSSL_3_0_0 = ssl.OPENSSL_VERSION_INFO >= (3, 0, 0) +CAN_GET_SELECTED_OPENSSL_GROUP = ssl.OPENSSL_VERSION_INFO >= (3, 2) +CAN_IGNORE_UNKNOWN_OPENSSL_GROUPS = ssl.OPENSSL_VERSION_INFO >= (3, 3) +CAN_GET_AVAILABLE_OPENSSL_GROUPS = ssl.OPENSSL_VERSION_INFO >= (3, 5) +CAN_GET_AVAILABLE_OPENSSL_SIGALGS = ssl.OPENSSL_VERSION_INFO >= (3, 4) +CAN_SET_CLIENT_SIGALGS = not IS_AWS_LC +CAN_IGNORE_UNKNOWN_OPENSSL_SIGALGS = ssl.OPENSSL_VERSION_INFO >= (3, 3) +CAN_GET_SELECTED_OPENSSL_SIGALG = ssl.OPENSSL_VERSION_INFO >= (3, 5) PY_SSL_DEFAULT_CIPHERS = sysconfig.get_config_var('PY_SSL_DEFAULT_CIPHERS') +CAN_SET_KEYLOG = (os.name != "nt") +requires_keylog_setter = unittest.skipUnless( + CAN_SET_KEYLOG, + "cannot set 'keylog_filename' on Windows" +) + + PROTOCOL_TO_TLS_VERSION = {} for proto, ver in ( ("PROTOCOL_SSLv3", "SSLv3"), @@ -124,6 +140,7 @@ def data_file(*name): EMPTYCERT = data_file("nullcert.pem") BADCERT = data_file("badcert.pem") NONEXISTINGCERT = data_file("XXXnonexisting.pem") +NONEXISTINGKEY = data_file("XXXnonexistingkey.pem") BADKEY = data_file("badkey.pem") NOKIACERT = data_file("nokia.pem") NULLBYTECERT = data_file("nullbytecert.pem") @@ -258,32 +275,76 @@ def utc_offset(): #NOTE: ignore issues like #1647654 ) -def test_wrap_socket(sock, *, - cert_reqs=ssl.CERT_NONE, ca_certs=None, - ciphers=None, certfile=None, keyfile=None, - **kwargs): - if not kwargs.get("server_side"): - kwargs["server_hostname"] = SIGNED_CERTFILE_HOSTNAME - context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - else: +def make_test_context( + *, + server_side=False, + check_hostname=None, + cert_reqs=ssl.CERT_NONE, + ca_certs=None, certfile=None, keyfile=None, + ciphers=None, ciphersuites=None, + min_version=None, max_version=None, +): + if server_side: context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) - if cert_reqs is not None: + else: + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + + if check_hostname is None: if cert_reqs == ssl.CERT_NONE: context.check_hostname = False + else: + context.check_hostname = check_hostname + + if cert_reqs is not None: context.verify_mode = cert_reqs + if ca_certs is not None: context.load_verify_locations(ca_certs) if certfile is not None or keyfile is not None: context.load_cert_chain(certfile, keyfile) + if ciphers is not None: context.set_ciphers(ciphers) - return context.wrap_socket(sock, **kwargs) + if ciphersuites is not None: + context.set_ciphersuites(ciphersuites) + + if min_version is not None: + context.minimum_version = min_version + if max_version is not None: + context.maximum_version = max_version + + return context + + +def test_wrap_socket( + sock, + *, + server_side=False, + check_hostname=None, + cert_reqs=ssl.CERT_NONE, + ca_certs=None, certfile=None, keyfile=None, + ciphers=None, ciphersuites=None, + min_version=None, max_version=None, + **kwargs, +): + context = make_test_context( + server_side=server_side, + check_hostname=check_hostname, + cert_reqs=cert_reqs, + ca_certs=ca_certs, certfile=certfile, keyfile=keyfile, + ciphers=ciphers, ciphersuites=ciphersuites, + min_version=min_version, max_version=max_version, + ) + if not server_side: + kwargs.setdefault("server_hostname", SIGNED_CERTFILE_HOSTNAME) + return context.wrap_socket(sock, server_side=server_side, **kwargs) USE_SAME_TEST_CONTEXT = False _TEST_CONTEXT = None -def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True): +def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True, + client_cert=None): """Create context client_context, server_context, hostname = testing_context() @@ -310,6 +371,10 @@ def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True): if server_chain: server_context.load_verify_locations(SIGNING_CA) + if client_cert: + client_context.load_cert_chain(client_cert) + server_context.verify_mode = ssl.CERT_REQUIRED + if USE_SAME_TEST_CONTEXT: if _TEST_CONTEXT is not None: _TEST_CONTEXT = client_context, server_context, hostname @@ -317,6 +382,20 @@ def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True): return client_context, server_context, hostname +def do_ssl_object_handshake(sslobject, outgoing, max_retry=25): + """Call do_handshake() on the sslobject and return the sent data. + + If do_handshake() fails more than *max_retry* times, return None. + """ + data, attempt = None, 0 + while not data and attempt < max_retry: + with contextlib.suppress(ssl.SSLWantReadError): + sslobject.do_handshake() + data = outgoing.read() + attempt += 1 + return data + + class BasicSocketTests(unittest.TestCase): def test_constants(self): @@ -329,7 +408,7 @@ def test_constants(self): ssl.OP_NO_COMPRESSION self.assertEqual(ssl.HAS_SNI, True) self.assertEqual(ssl.HAS_ECDH, True) - self.assertEqual(ssl.HAS_TLSv1_2, True) + self.assertIsInstance(ssl.HAS_TLSv1_2, bool) self.assertEqual(ssl.HAS_TLSv1_3, True) ssl.OP_NO_SSLv2 ssl.OP_NO_SSLv3 @@ -520,11 +599,11 @@ def test_openssl_version(self): # Some sanity checks follow # >= 1.1.1 self.assertGreaterEqual(n, 0x10101000) - # < 4.0 - self.assertLess(n, 0x40000000) + # < 5.0 + self.assertLess(n, 0x50000000) major, minor, fix, patch, status = t self.assertGreaterEqual(major, 1) - self.assertLess(major, 4) + self.assertLess(major, 5) self.assertGreaterEqual(minor, 0) self.assertLess(minor, 256) self.assertGreaterEqual(fix, 0) @@ -556,6 +635,7 @@ def test_refcycle(self): del ss self.assertEqual(wr(), None) + @unittest.skipIf(sys.platform == 'cygwin', 'test hangs on Cygwin') def test_wrapped_unconnected(self): # Methods on an unconnected SSLSocket propagate the original # OSError raise by the underlying socket object. @@ -590,12 +670,14 @@ def test_openssl111_deprecations(self): ssl.OP_NO_TLSv1_2, ssl.OP_NO_TLSv1_3 ] - protocols = [ - ssl.PROTOCOL_TLSv1, - ssl.PROTOCOL_TLSv1_1, - ssl.PROTOCOL_TLSv1_2, - ssl.PROTOCOL_TLS - ] + protocols = [] + if hasattr(ssl, 'PROTOCOL_TLSv1'): + protocols.append(ssl.PROTOCOL_TLSv1) + if hasattr(ssl, 'PROTOCOL_TLSv1_1'): + protocols.append(ssl.PROTOCOL_TLSv1_1) + if hasattr(ssl, 'PROTOCOL_TLSv1_2'): + protocols.append(ssl.PROTOCOL_TLSv1_2) + protocols.append(ssl.PROTOCOL_TLS) versions = [ ssl.TLSVersion.SSLv3, ssl.TLSVersion.TLSv1, @@ -960,6 +1042,56 @@ def test_get_ciphers(self): len(intersection), 2, f"\ngot: {sorted(names)}\nexpected: {sorted(expected)}" ) + def test_set_groups(self): + ctx = ssl.create_default_context() + # We use P-256 and P-384 (FIPS 186-4) that are alloed by OpenSSL + # even if FIPS module is enabled. Ignoring unknown groups is only + # supported since OpenSSL 3.3. + self.assertIsNone(ctx.set_groups('P-256:P-384')) + + self.assertRaises(ssl.SSLError, ctx.set_groups, 'P-256:foo') + if CAN_IGNORE_UNKNOWN_OPENSSL_GROUPS: + self.assertIsNone(ctx.set_groups('P-256:?foo')) + + @unittest.skipUnless(CAN_GET_AVAILABLE_OPENSSL_GROUPS, + "OpenSSL version doesn't support getting groups") + def test_get_groups(self): + ctx = ssl.create_default_context() + # By default, only return official IANA names. + self.assertNotIn('P-256', ctx.get_groups()) + self.assertIn('P-256', ctx.get_groups(include_aliases=True)) + + @unittest.skipUnless(CAN_GET_AVAILABLE_OPENSSL_SIGALGS, + "SSL library doesn't support getting sigalgs") + def test_get_sigalgs(self): + self.assertIn('rsa_pss_rsae_sha256', ssl.get_sigalgs()) + + @unittest.skipUnless(CAN_SET_CLIENT_SIGALGS, + "SSL library doesn't support setting client sigalgs") + def test_set_client_sigalgs(self): + ctx = ssl.create_default_context() + + self.assertIsNone(ctx.set_client_sigalgs('rsa_pss_rsae_sha256')) + + self.assertRaises(ssl.SSLError, ctx.set_client_sigalgs, + 'rsa_pss_rsae_sha256:foo') + + # Ignoring unknown sigalgs is only supported since OpenSSL 3.3. + if CAN_IGNORE_UNKNOWN_OPENSSL_SIGALGS: + self.assertIsNone(ctx.set_client_sigalgs('rsa_pss_rsae_sha256:?foo')) + + def test_set_server_sigalgs(self): + ctx = ssl.create_default_context() + + self.assertIsNone(ctx.set_server_sigalgs('rsa_pss_rsae_sha256')) + + self.assertRaises(ssl.SSLError, ctx.set_server_sigalgs, + 'rsa_pss_rsae_sha256:foo') + + # Ignoring unknown sigalgs is only supported since OpenSSL 3.3. + if CAN_IGNORE_UNKNOWN_OPENSSL_SIGALGS: + self.assertIsNone(ctx.set_server_sigalgs('rsa_pss_rsae_sha256:?foo')) + def test_options(self): # Test default SSLContext options ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) @@ -1085,7 +1217,13 @@ def test_min_max_version(self): ctx.maximum_version = ssl.TLSVersion.MINIMUM_SUPPORTED self.assertIn( ctx.maximum_version, - {ssl.TLSVersion.TLSv1, ssl.TLSVersion.TLSv1_1, ssl.TLSVersion.SSLv3} + { + ssl.TLSVersion.TLSv1, + ssl.TLSVersion.TLSv1_1, + ssl.TLSVersion.TLSv1_2, + ssl.TLSVersion.TLSv1_3, + ssl.TLSVersion.SSLv3, + } ) ctx.minimum_version = ssl.TLSVersion.MAXIMUM_SUPPORTED @@ -1097,7 +1235,7 @@ def test_min_max_version(self): with self.assertRaises(ValueError): ctx.minimum_version = 42 - if has_tls_protocol(ssl.PROTOCOL_TLSv1_1): + if has_tls_protocol('PROTOCOL_TLSv1_1'): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_1) self.assertIn( @@ -1159,6 +1297,11 @@ def test_load_cert_chain(self): with self.assertRaises(OSError) as cm: ctx.load_cert_chain(NONEXISTINGCERT) self.assertEqual(cm.exception.errno, errno.ENOENT) + self.assertEqual(cm.exception.filename, NONEXISTINGCERT) + with self.assertRaises(OSError) as cm: + ctx.load_cert_chain(CERTFILE, keyfile=NONEXISTINGKEY) + self.assertEqual(cm.exception.errno, errno.ENOENT) + self.assertEqual(cm.exception.filename, NONEXISTINGKEY) with self.assertRaisesRegex(ssl.SSLError, "PEM (lib|routines)"): ctx.load_cert_chain(BADCERT) with self.assertRaisesRegex(ssl.SSLError, "PEM (lib|routines)"): @@ -1239,6 +1382,25 @@ def getpass(self): # Make sure the password function isn't called if it isn't needed ctx.load_cert_chain(CERTFILE, password=getpass_exception) + @threading_helper.requires_working_threading() + def test_load_cert_chain_thread_safety(self): + # gh-134698: _ssl detaches the thread state (and as such, + # releases the GIL and critical sections) around expensive + # OpenSSL calls. Unfortunately, OpenSSL structures aren't + # thread-safe, so executing these calls concurrently led + # to crashes. + ctx = ssl.create_default_context() + + def race(): + ctx.load_cert_chain(CERTFILE) + + threads = [threading.Thread(target=race) for _ in range(8)] + with threading_helper.catch_threading_exception() as cm: + with threading_helper.start_threads(threads): + pass + + self.assertIsNone(cm.exc_value) + def test_load_verify_locations(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ctx.load_verify_locations(CERTFILE) @@ -1387,6 +1549,49 @@ def dummycallback(sock, servername, ctx): ctx.set_servername_callback(None) ctx.set_servername_callback(dummycallback) + def test_sni_callback_on_dead_references(self): + # See https://github.com/python/cpython/issues/146080. + c_ctx = make_test_context() + c_inc, c_out = ssl.MemoryBIO(), ssl.MemoryBIO() + client = c_ctx.wrap_bio(c_inc, c_out, server_hostname=SIGNED_CERTFILE_HOSTNAME) + + def sni_callback(sock, servername, ctx): pass + sni_callback = unittest.mock.Mock(wraps=sni_callback) + s_ctx = make_test_context(server_side=True, certfile=SIGNED_CERTFILE) + s_ctx.set_servername_callback(sni_callback) + + s_inc, s_out = ssl.MemoryBIO(), ssl.MemoryBIO() + server = s_ctx.wrap_bio(s_inc, s_out, server_side=True) + server_impl = server._sslobj + + # Perform the handshake on the client side first. + data = do_ssl_object_handshake(client, c_out) + sni_callback.assert_not_called() + if data is None: + self.skipTest("cannot establish a handshake from the client") + s_inc.write(data) + sni_callback.assert_not_called() + # Delete the server object before it starts doing its handshake + # and ensure that we did not call the SNI callback yet. + del server + gc.collect() + # Try to continue the server's handshake by directly using + # the internal SSL object. The latter is a weak reference + # stored in the server context and has now a dead owner. + with self.assertRaises(ssl.SSLError) as cm: + server_impl.do_handshake() + # The SNI C callback raised an exception before calling our callback. + sni_callback.assert_not_called() + + # In AWS-LC, any handshake failures reports SSL_R_PARSE_TLSEXT, + # while OpenSSL uses SSL_R_CALLBACK_FAILED on SNI callback failures. + if IS_AWS_LC: + libssl_error_reason = "PARSE_TLSEXT" + else: + libssl_error_reason = "callback failed" + self.assertIn(libssl_error_reason, str(cm.exception)) + self.assertEqual(cm.exception.errno, ssl.SSL_ERROR_SSL) + def test_sni_callback_refcycle(self): # Reference cycles through the servername callback are detected # and cleared. @@ -1399,6 +1604,59 @@ def dummycallback(sock, servername, ctx, cycle=ctx): gc.collect() self.assertIs(wr(), None) + @unittest.skipUnless(support.Py_GIL_DISABLED, + "test is only useful if the GIL is disabled") + @threading_helper.requires_working_threading() + def test_sni_callback_race(self): + # Replacing sni_callback while handshakes are in-flight must not + # crash (use-after-free on the callback in free-threaded builds). + client_ctx, server_ctx, hostname = testing_context() + + server_ctx.sni_callback = lambda *a: None + done = threading.Event() + + def do_handshakes(): + while not done.is_set(): + c_in = ssl.MemoryBIO() + c_out = ssl.MemoryBIO() + s_in = ssl.MemoryBIO() + s_out = ssl.MemoryBIO() + client = client_ctx.wrap_bio( + c_in, c_out, server_hostname=hostname) + server = server_ctx.wrap_bio(s_in, s_out, server_side=True) + for _ in range(50): + try: + client.do_handshake() + except ssl.SSLWantReadError: + pass + except ssl.SSLError: + break + if c_out.pending: + s_in.write(c_out.read()) + try: + server.do_handshake() + except ssl.SSLWantReadError: + pass + except ssl.SSLError: + break + if s_out.pending: + c_in.write(s_out.read()) + + def toggle_callback(): + while not done.is_set(): + server_ctx.sni_callback = lambda *a: None + server_ctx.sni_callback = None + + workers = max(4, (os.cpu_count() or 4) * 2) + threads = [threading.Thread(target=do_handshakes) + for _ in range(workers)] + threads.append(threading.Thread(target=toggle_callback)) + + with threading_helper.catch_threading_exception() as cm: + with threading_helper.start_threads(threads): + done.set() + self.assertIsNone(cm.exc_value) + def test_cert_store_stats(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) self.assertEqual(ctx.cert_store_stats(), @@ -1530,23 +1788,24 @@ def test__create_stdlib_context(self): self.assertFalse(ctx.check_hostname) self._assert_context_options(ctx) - if has_tls_protocol(ssl.PROTOCOL_TLSv1): + if has_tls_protocol('PROTOCOL_TLSv1'): with warnings_helper.check_warnings(): ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1) self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) self._assert_context_options(ctx) - with warnings_helper.check_warnings(): - ctx = ssl._create_stdlib_context( - ssl.PROTOCOL_TLSv1_2, - cert_reqs=ssl.CERT_REQUIRED, - check_hostname=True - ) - self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1_2) - self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) - self.assertTrue(ctx.check_hostname) - self._assert_context_options(ctx) + if has_tls_protocol('PROTOCOL_TLSv1_2'): + with warnings_helper.check_warnings(): + ctx = ssl._create_stdlib_context( + ssl.PROTOCOL_TLSv1_2, + cert_reqs=ssl.CERT_REQUIRED, + check_hostname=True + ) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1_2) + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + self.assertTrue(ctx.check_hostname) + self._assert_context_options(ctx) ctx = ssl._create_stdlib_context(purpose=ssl.Purpose.CLIENT_AUTH) self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLS_SERVER) @@ -1641,6 +1900,39 @@ def test_num_tickest(self): with self.assertRaises(ValueError): ctx.num_tickets = 1 + @support.cpython_only + def test_refcycle_msg_callback(self): + # See https://github.com/python/cpython/issues/142516. + ctx = make_test_context() + def msg_callback(*args, _=ctx, **kwargs): ... + ctx._msg_callback = msg_callback + + @support.cpython_only + @requires_keylog_setter + def test_refcycle_keylog_filename(self): + # See https://github.com/python/cpython/issues/142516. + self.addCleanup(os_helper.unlink, os_helper.TESTFN) + ctx = make_test_context() + class KeylogFilename(str): ... + ctx.keylog_filename = KeylogFilename(os_helper.TESTFN) + ctx.keylog_filename._ = ctx + + @support.cpython_only + @unittest.skipUnless(ssl.HAS_PSK, 'requires TLS-PSK') + def test_refcycle_psk_client_callback(self): + # See https://github.com/python/cpython/issues/142516. + ctx = make_test_context() + def psk_client_callback(*args, _=ctx, **kwargs): ... + ctx.set_psk_client_callback(psk_client_callback) + + @support.cpython_only + @unittest.skipUnless(ssl.HAS_PSK, 'requires TLS-PSK') + def test_refcycle_psk_server_callback(self): + # See https://github.com/python/cpython/issues/142516. + ctx = make_test_context(server_side=True) + def psk_server_callback(*args, _=ctx, **kwargs): ... + ctx.set_psk_server_callback(psk_server_callback) + class SSLErrorTests(unittest.TestCase): @@ -2197,6 +2489,68 @@ def test_transport_eof(self): self.assertRaises(ssl.SSLEOFError, sslobj.read) +@unittest.skipUnless(has_tls_version('TLSv1_3'), "TLS 1.3 is not available") +class SimpleBackgroundTestsTLS_1_3(unittest.TestCase): + """Tests that connect to a simple server running in the background.""" + + def setUp(self): + server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ciphers = [cipher['name'] for cipher in server_ctx.get_ciphers() + if cipher['protocol'] == 'TLSv1.3'] + + if not ciphers: + self.skipTest("No cipher supports TLSv1.3") + + self.matching_cipher = ciphers[0] + # Some tests need at least two ciphers, and are responsible + # to skip themselves if matching_cipher == mismatched_cipher. + self.mismatched_cipher = ciphers[-1] + + server_ctx.set_ciphersuites(self.matching_cipher) + server_ctx.load_cert_chain(SIGNED_CERTFILE) + server = ThreadedEchoServer(context=server_ctx) + self.enterContext(server) + self.server_addr = (HOST, server.port) + + def test_ciphersuites(self): + # Test unrecognized TLS 1.3 cipher suite name + with ( + socket.socket(socket.AF_INET) as sock, + self.assertRaisesRegex(ssl.SSLError, + "No cipher suite can be selected") + ): + test_wrap_socket(sock, cert_reqs=ssl.CERT_NONE, + ciphersuites="XXX", + min_version=ssl.TLSVersion.TLSv1_3) + + # Test successful TLS 1.3 handshake + with test_wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE, + ciphersuites=self.matching_cipher, + min_version=ssl.TLSVersion.TLSv1_3) as s: + s.connect(self.server_addr) + self.assertEqual(s.cipher()[0], self.matching_cipher) + + def test_ciphersuite_downgrade(self): + with test_wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE, + ciphersuites=self.matching_cipher, + min_version=ssl.TLSVersion.TLSv1_2, + max_version=ssl.TLSVersion.TLSv1_2) as s: + s.connect(self.server_addr) + self.assertEqual(s.cipher()[1], 'TLSv1.2') + + def test_ciphersuite_mismatch(self): + if self.matching_cipher == self.mismatched_cipher: + self.skipTest("Multiple TLS 1.3 ciphers are not available") + + with test_wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE, + ciphersuites=self.mismatched_cipher, + min_version=ssl.TLSVersion.TLSv1_3) as s: + self.assertRaises(ssl.SSLError, s.connect, self.server_addr) + + @support.requires_resource('network') class NetworkedTests(unittest.TestCase): @@ -2540,6 +2894,36 @@ def close(self): def stop(self): self.active = False +class TestEOFServer(threading.Thread): + def __init__(self): + super().__init__() + self.listening = threading.Event() + self.address = None + + def run(self): + context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + context.load_cert_chain(CERTFILE) + server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + with server_sock: + server_sock.settimeout(support.SHORT_TIMEOUT) + server_sock.bind((HOST, 0)) + server_sock.listen(5) + + self.address = server_sock.getsockname() + self.listening.set() + + sock, addr = server_sock.accept() + sslconn = context.wrap_socket(sock, server_side=True) + with sslconn: + request = b'' + while chunk := sslconn.recv(1024): + request += chunk + if b'\n' in chunk: + break + + sslconn.sendall(b'server\n') + sslconn.shutdown(socket.SHUT_WR) + class AsyncoreEchoServer(threading.Thread): # this one's based on asyncore.dispatcher @@ -2701,6 +3085,11 @@ def server_params_test(client_context, server_context, indata=b"FOO\n", 'session_reused': s.session_reused, 'session': s.session, }) + if CAN_GET_SELECTED_OPENSSL_GROUP: + stats.update({'group': s.group()}) + if CAN_GET_SELECTED_OPENSSL_SIGALG: + stats.update({'client_sigalg': s.client_sigalg()}) + stats.update({'server_sigalg': s.server_sigalg()}) s.close() stats['server_alpn_protocols'] = server.selected_alpn_protocols stats['server_shared_ciphers'] = server.shared_ciphers @@ -3240,7 +3629,7 @@ def test_wrong_cert_tls13(self): OSError, 'alert unknown ca|EOF occurred|TLSV1_ALERT_UNKNOWN_CA|' 'closed by the remote host|Connection reset by peer|' - 'Broken pipe' + 'Broken pipe|Software caused connection abort' ): # TLS 1.3 perform client cert exchange after handshake s.write(b'data') @@ -3409,10 +3798,10 @@ def test_protocol_tlsv1_2(self): client_options=ssl.OP_NO_TLSv1_2) try_protocol_combo(ssl.PROTOCOL_TLS, ssl.PROTOCOL_TLSv1_2, 'TLSv1.2') - if has_tls_protocol(ssl.PROTOCOL_TLSv1): + if has_tls_protocol('PROTOCOL_TLSv1'): try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1, False) try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_2, False) - if has_tls_protocol(ssl.PROTOCOL_TLSv1_1): + if has_tls_protocol('PROTOCOL_TLSv1_1'): try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_1, False) try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_2, False) @@ -3851,6 +4240,8 @@ def test_no_shared_ciphers(self): with self.assertRaises(OSError): s.connect((HOST, server.port)) self.assertIn("NO_SHARED_CIPHER", server.conn_errors[0]) + self.assertIsNone(s.cipher()) + self.assertIsNone(s.group()) def test_version_basic(self): """ @@ -4126,6 +4517,111 @@ def test_ecdh_curve(self): chatty=True, connectionchatty=True, sni_name=hostname) + def test_groups(self): + # server secp384r1, client auto + client_context, server_context, hostname = testing_context() + + server_context.set_groups("secp384r1") + server_context.minimum_version = ssl.TLSVersion.TLSv1_3 + stats = server_params_test(client_context, server_context, + chatty=True, connectionchatty=True, + sni_name=hostname) + if CAN_GET_SELECTED_OPENSSL_GROUP: + self.assertEqual(stats['group'], "secp384r1") + + # server auto, client secp384r1 + client_context, server_context, hostname = testing_context() + client_context.set_groups("secp384r1") + server_context.minimum_version = ssl.TLSVersion.TLSv1_3 + stats = server_params_test(client_context, server_context, + chatty=True, connectionchatty=True, + sni_name=hostname) + if CAN_GET_SELECTED_OPENSSL_GROUP: + self.assertEqual(stats['group'], "secp384r1") + + # server / client curve mismatch + client_context, server_context, hostname = testing_context() + client_context.set_groups("prime256v1") + server_context.set_groups("secp384r1") + server_context.minimum_version = ssl.TLSVersion.TLSv1_3 + with self.assertRaises(ssl.SSLError): + server_params_test(client_context, server_context, + chatty=True, connectionchatty=True, + sni_name=hostname) + + @unittest.skipUnless(CAN_SET_CLIENT_SIGALGS, + "SSL library doesn't support setting client sigalgs") + def test_client_sigalgs(self): + # no mutual auth, so cient_sigalg should be None + client_context, server_context, hostname = testing_context() + stats = server_params_test(client_context, server_context, + chatty=True, connectionchatty=True, + sni_name=hostname) + if CAN_GET_SELECTED_OPENSSL_SIGALG: + self.assertIsNone(stats['client_sigalg']) + + # server auto, client rsa_pss_rsae_sha384 + sigalg = "rsa_pss_rsae_sha384" + client_context, server_context, hostname = \ + testing_context(client_cert=SIGNED_CERTFILE) + client_context.set_client_sigalgs(sigalg) + stats = server_params_test(client_context, server_context, + chatty=True, connectionchatty=True, + sni_name=hostname) + if CAN_GET_SELECTED_OPENSSL_SIGALG: + self.assertEqual(stats['client_sigalg'], sigalg) + + @unittest.skipUnless(CAN_SET_CLIENT_SIGALGS, + "SSL library doesn't support setting client sigalgs") + def test_client_sigalgs_mismatch(self): + client_context, server_context, hostname = \ + testing_context(client_cert=SIGNED_CERTFILE) + client_context.set_client_sigalgs("rsa_pss_rsae_sha256") + server_context.set_client_sigalgs("rsa_pss_rsae_sha384") + + with self.assertRaises(( + ssl.SSLError, + # On handshake failures, some systems raise a ConnectionResetError. + ConnectionResetError, + # On handshake failures, Cygwin raises ConnectionAbortedError. + ConnectionAbortedError, + # On handshake failures, macOS may raise a BrokenPipeError. + # See https://github.com/python/cpython/issues/139504. + BrokenPipeError, + )): + server_params_test(client_context, server_context, + chatty=True, connectionchatty=True, + sni_name=hostname) + + def test_server_sigalgs(self): + # server rsa_pss_rsae_sha384, client auto + sigalg = "rsa_pss_rsae_sha384" + client_context, server_context, hostname = testing_context() + server_context.set_server_sigalgs(sigalg) + stats = server_params_test(client_context, server_context, + chatty=True, connectionchatty=True, + sni_name=hostname) + if CAN_GET_SELECTED_OPENSSL_SIGALG: + self.assertEqual(stats['server_sigalg'], sigalg) + + # server auto, client rsa_pss_rsae_sha384 + client_context, server_context, hostname = testing_context() + client_context.set_server_sigalgs(sigalg) + stats = server_params_test(client_context, server_context, + chatty=True, connectionchatty=True, + sni_name=hostname) + if CAN_GET_SELECTED_OPENSSL_SIGALG: + self.assertEqual(stats['server_sigalg'], sigalg) + + def test_server_sigalgs_mismatch(self): + client_context, server_context, hostname = testing_context() + client_context.set_server_sigalgs("rsa_pss_rsae_sha256") + server_context.set_server_sigalgs("rsa_pss_rsae_sha384") + with self.assertRaises(ssl.SSLError): + server_params_test(client_context, server_context, + chatty=True, connectionchatty=True, + sni_name=hostname) + def test_selected_alpn_protocol(self): # selected_alpn_protocol() is None unless ALPN is used. client_context, server_context, hostname = testing_context() @@ -4261,7 +4757,9 @@ def cb_raising(ssl_sock, server_name, initial_context): sni_name='supermessage') # Allow for flexible libssl error messages. - regex = "(SSLV3_ALERT_HANDSHAKE_FAILURE|NO_PRIVATE_VALUE)" + regex = ("(TLS_ALERT_HANDSHAKE_FAILURE" + "|SSLV3_ALERT_HANDSHAKE_FAILURE" + "|NO_PRIVATE_VALUE)") self.assertRegex(cm.exception.reason, regex) self.assertEqual(catch.unraisable.exc_type, ZeroDivisionError) @@ -4550,6 +5048,86 @@ def server_callback(identity): with client_context.wrap_socket(socket.socket()) as s: s.connect((HOST, server.port)) + def test_thread_recv_while_main_thread_sends(self): + # GH-137583: Locking was added to calls to send() and recv() on SSL + # socket objects. This seemed fine at the surface level because those + # calls weren't re-entrant, but recv() calls would implicitly mimick + # holding a lock by blocking until it received data. This means that + # if a thread started to infinitely block until data was received, calls + # to send() would deadlock, because it would wait forever on the lock + # that the recv() call held. + data = b"1" * 1024 + event = threading.Event() + def background(sock): + event.set() + received = sock.recv(len(data)) + self.assertEqual(received, data) + + client_context, server_context, hostname = testing_context() + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket(), + server_hostname=hostname) as sock: + sock.connect((HOST, server.port)) + sock.settimeout(1) + sock.setblocking(1) + # Ensure that the server is ready to accept requests + sock.sendall(b"123") + self.assertEqual(sock.recv(3), b"123") + with threading_helper.catch_threading_exception() as cm: + thread = threading.Thread(target=background, + args=(sock,), daemon=True) + thread.start() + event.wait() + sock.sendall(data) + thread.join() + if cm.exc_value is not None: + raise cm.exc_value + + def test_got_eof(self): + # gh-148292: Test that _ssl._SSLSocket behaves the same on all OpenSSL + # versions on calling methods after EOF (after the first SSLEOFError). + + server = TestEOFServer() + server.start() + if not server.listening.wait(support.SHORT_TIMEOUT): + raise RuntimeError("server took too long") + self.addCleanup(server.join) + + context = ssl.create_default_context(cafile=CERTFILE) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(support.SHORT_TIMEOUT) + sock.connect(server.address) + sslsock = context.wrap_socket(sock, server_hostname='localhost') + with sslsock: + sslsock.sendall(b'client\n') + # test the _ssl._SSLSocket object, not ssl.SSLSocket + sslobj = sslsock._sslobj + + data = sslobj.read(1024) + self.assertEqual(data, b'server\n') + + # The second read gets EOF error and sets got_eof_error to 1 + with self.assertRaises(ssl.SSLEOFError): + sslobj.read(1024) + + # Following read(), sendfile(), write() and do_handshake() calls + # must raise SSLEOFError + with self.assertRaises(ssl.SSLEOFError): + # The _SSLSocket remembers the previous EOF error + # and raises again SSLEOFError + sslobj.read(1024) + if hasattr(sslobj, 'sendfile'): + with open(__file__, "rb") as fp: + with self.assertRaises(ssl.SSLEOFError): + sslobj.sendfile(fp.fileno(), 0, 1) + with self.assertRaises(ssl.SSLEOFError): + sslobj.write(b'client2\n') + with self.assertRaises(ssl.SSLEOFError): + sslsock.do_handshake() + + self.assertEqual(sslsock.pending(), 0) + @unittest.skipUnless(has_tls_version('TLSv1_3') and ssl.HAS_PHA, "Test needs TLS 1.3 PHA") @@ -4866,17 +5444,12 @@ def test_internal_chain_server(self): self.assertEqual(res, b'\x02\n') -HAS_KEYLOG = hasattr(ssl.SSLContext, 'keylog_filename') -requires_keylog = unittest.skipUnless( - HAS_KEYLOG, 'test requires OpenSSL 1.1.1 with keylog callback') - class TestSSLDebug(unittest.TestCase): def keylog_lines(self, fname=os_helper.TESTFN): with open(fname) as f: return len(list(f)) - @requires_keylog def test_keylog_defaults(self): self.addCleanup(os_helper.unlink, os_helper.TESTFN) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) @@ -4904,7 +5477,6 @@ def test_keylog_defaults(self): with self.assertRaises(TypeError): ctx.keylog_filename = 1 - @requires_keylog def test_keylog_filename(self): self.addCleanup(os_helper.unlink, os_helper.TESTFN) client_context, server_context, hostname = testing_context() @@ -4945,7 +5517,6 @@ def test_keylog_filename(self): client_context.keylog_filename = None server_context.keylog_filename = None - @requires_keylog @unittest.skipIf(sys.flags.ignore_environment, "test is not compatible with ignore_environment") def test_keylog_env(self): @@ -4980,6 +5551,20 @@ def msg_cb(conn, direction, version, content_type, msg_type, data): with self.assertRaises(TypeError): client_context._msg_callback = object() + def test_msg_callback_exception(self): + client_context, server_context, hostname = testing_context() + + def msg_cb(conn, direction, version, content_type, msg_type, data): + raise RuntimeError("msg_cb exception") + + client_context._msg_callback = msg_cb + server = ThreadedEchoServer(context=server_context, chatty=False) + with server: + with client_context.wrap_socket(socket.socket(), + server_hostname=hostname) as s: + with self.assertRaisesRegex(RuntimeError, "msg_cb exception"): + s.connect((HOST, server.port)) + def test_msg_callback_tls12(self): client_context, server_context, hostname = testing_context() client_context.maximum_version = ssl.TLSVersion.TLSv1_2 @@ -5105,17 +5690,29 @@ def run(self): def non_linux_skip_if_other_okay_error(self, err): if sys.platform in ("linux", "android"): return # Expect the full test setup to always work on Linux. - if (isinstance(err, ConnectionResetError) or + if (isinstance(err, (ConnectionResetError, ConnectionAbortedError)) or (isinstance(err, OSError) and err.errno == errno.EINVAL) or - re.search('wrong.version.number', str(getattr(err, "reason", "")), re.I)): + re.search( + # Matches the following error messages: + # '[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123)' + # '[SSL: RECORD_LAYER_FAILURE] record layer failure (_ssl.c:1109)' + # '[SSL: HTTP_REQUEST] http request (_ssl.c:1143)' + r'wrong.version.number|record.layer.failure|http.request', + str(getattr(err, "reason", "")), + re.IGNORECASE, + ) + ): # On Windows the TCP RST leads to a ConnectionResetError # (ECONNRESET) which Linux doesn't appear to surface to userspace. # If wrap_socket() winds up on the "if connected:" path and doing - # the actual wrapping... we get an SSLError from OpenSSL. Typically - # WRONG_VERSION_NUMBER. While appropriate, neither is the scenario - # we're specifically trying to test. The way this test is written - # is known to work on Linux. We'll skip it anywhere else that it - # does not present as doing so. + # the actual wrapping... we get an SSLError from OpenSSL. This is + # typically WRONG_VERSION_NUMBER. The same happens on iOS, but + # RECORD_LAYER_FAILURE or HTTP_REQUEST is the error. + # + # While appropriate, these scenarios aren't what we're specifically + # trying to test. The way this test is written is known to work on + # Linux. We'll skip it anywhere else that it does not present as + # doing so. try: self.skipTest(f"Could not recreate conditions on {sys.platform}:" f" {err=}") @@ -5316,7 +5913,7 @@ def test_tlsalerttype(self): class Checked_TLSAlertType(enum.IntEnum): """Alert types for TLSContentType.ALERT messages - See RFC 8466, section B.2 + See RFC 8446, section B.2 """ CLOSE_NOTIFY = 0 UNEXPECTED_MESSAGE = 10 diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 5a6ba9de337904..09ee2d53f98f58 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -45,6 +45,7 @@ def test_windows_feature_macros(self): SYMBOL_NAMES = ( + "PyABIInfo_Check", "PyAIter_Check", "PyArg_Parse", "PyArg_ParseTuple", @@ -133,6 +134,10 @@ def test_windows_feature_macros(self): "PyComplex_ImagAsDouble", "PyComplex_RealAsDouble", "PyComplex_Type", + "PyCriticalSection2_Begin", + "PyCriticalSection2_End", + "PyCriticalSection_Begin", + "PyCriticalSection_End", "PyDescr_NewClassMethod", "PyDescr_NewGetSet", "PyDescr_NewMember", @@ -164,6 +169,7 @@ def test_windows_feature_macros(self): "PyDict_MergeFromSeq2", "PyDict_New", "PyDict_Next", + "PyDict_SetDefaultRef", "PyDict_SetItem", "PyDict_SetItemString", "PyDict_Size", @@ -363,12 +369,18 @@ def test_windows_feature_macros(self): "PyImport_ImportModuleNoBlock", "PyImport_ReloadModule", "PyIndex_Check", + "PyInterpreterGuard_Close", + "PyInterpreterGuard_FromCurrent", + "PyInterpreterGuard_FromView", "PyInterpreterState_Clear", "PyInterpreterState_Delete", "PyInterpreterState_Get", "PyInterpreterState_GetDict", "PyInterpreterState_GetID", "PyInterpreterState_New", + "PyInterpreterView_Close", + "PyInterpreterView_FromCurrent", + "PyInterpreterView_FromMain", "PyIter_Check", "PyIter_Next", "PyIter_NextItem", @@ -389,6 +401,9 @@ def test_windows_feature_macros(self): "PyList_Sort", "PyList_Type", "PyLongRangeIter_Type", + "PyLongWriter_Create", + "PyLongWriter_Discard", + "PyLongWriter_Finish", "PyLong_AsDouble", "PyLong_AsInt", "PyLong_AsInt32", @@ -407,6 +422,8 @@ def test_windows_feature_macros(self): "PyLong_AsUnsignedLongLongMask", "PyLong_AsUnsignedLongMask", "PyLong_AsVoidPtr", + "PyLong_Export", + "PyLong_FreeExport", "PyLong_FromDouble", "PyLong_FromInt32", "PyLong_FromInt64", @@ -423,6 +440,7 @@ def test_windows_feature_macros(self): "PyLong_FromUnsignedNativeBytes", "PyLong_FromVoidPtr", "PyLong_GetInfo", + "PyLong_GetNativeLayout", "PyLong_Type", "PyMap_Type", "PyMapping_Check", @@ -468,8 +486,10 @@ def test_windows_feature_macros(self): "PyModule_AddStringConstant", "PyModule_AddType", "PyModule_Create2", + "PyModule_Exec", "PyModule_ExecDef", "PyModule_FromDefAndSpec2", + "PyModule_FromSlotsAndSpec", "PyModule_GetDef", "PyModule_GetDict", "PyModule_GetFilename", @@ -477,6 +497,10 @@ def test_windows_feature_macros(self): "PyModule_GetName", "PyModule_GetNameObject", "PyModule_GetState", + "PyModule_GetStateSize", + "PyModule_GetState_DuringGC", + "PyModule_GetToken", + "PyModule_GetToken_DuringGC", "PyModule_New", "PyModule_NewObject", "PyModule_SetDocString", @@ -538,6 +562,7 @@ def test_windows_feature_macros(self): "PyObject_AsWriteBuffer", "PyObject_Bytes", "PyObject_Call", + "PyObject_CallFinalizerFromDealloc", "PyObject_CallFunction", "PyObject_CallFunctionObjArgs", "PyObject_CallMethod", @@ -574,6 +599,7 @@ def test_windows_feature_macros(self): "PyObject_GetOptionalAttr", "PyObject_GetOptionalAttrString", "PyObject_GetTypeData", + "PyObject_GetTypeData_DuringGC", "PyObject_HasAttr", "PyObject_HasAttrString", "PyObject_HasAttrStringWithError", @@ -675,12 +701,15 @@ def test_windows_feature_macros(self): "PyThreadState_Clear", "PyThreadState_Delete", "PyThreadState_DeleteCurrent", + "PyThreadState_Ensure", + "PyThreadState_EnsureFromView", "PyThreadState_Get", "PyThreadState_GetDict", "PyThreadState_GetFrame", "PyThreadState_GetID", "PyThreadState_GetInterpreter", "PyThreadState_New", + "PyThreadState_Release", "PyThreadState_SetAsyncExc", "PyThreadState_Swap", "PyThread_GetInfo", @@ -723,17 +752,23 @@ def test_windows_feature_macros(self): "PyType_Freeze", "PyType_FromMetaclass", "PyType_FromModuleAndSpec", + "PyType_FromSlots", "PyType_FromSpec", "PyType_FromSpecWithBases", "PyType_GenericAlloc", "PyType_GenericNew", "PyType_GetBaseByToken", + "PyType_GetBaseByToken_DuringGC", "PyType_GetFlags", "PyType_GetFullyQualifiedName", "PyType_GetModule", "PyType_GetModuleByDef", + "PyType_GetModuleByToken", + "PyType_GetModuleByToken_DuringGC", "PyType_GetModuleName", "PyType_GetModuleState", + "PyType_GetModuleState_DuringGC", + "PyType_GetModule_DuringGC", "PyType_GetName", "PyType_GetQualName", "PyType_GetSlot", @@ -894,6 +929,7 @@ def test_windows_feature_macros(self): "Py_GetRecursionLimit", "Py_GetVersion", "Py_HasFileSystemDefaultEncoding", + "Py_IS_TYPE", "Py_IncRef", "Py_Initialize", "Py_InitializeEx", @@ -913,6 +949,8 @@ def test_windows_feature_macros(self): "Py_REFCNT", "Py_ReprEnter", "Py_ReprLeave", + "Py_SET_SIZE", + "Py_SIZE", "Py_SetPath", "Py_SetProgramName", "Py_SetPythonHome", diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py index 5fd25d5012c425..a83f7d076f027e 100644 --- a/Lib/test/test_stat.py +++ b/Lib/test/test_stat.py @@ -163,6 +163,11 @@ def test_mode(self): self.statmod.S_IFREG) self.assertEqual(self.statmod.S_IMODE(st_mode), 0o666) + def test_filemode_does_not_misclassify_random_bits(self): + # gh-144050 regression test + self.assertEqual(self.statmod.filemode(0o77777)[0], "?") + self.assertEqual(self.statmod.filemode(0o177777)[0], "?") + @os_helper.skip_unless_working_chmod def test_directory(self): os.mkdir(TESTFN) diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 8250b0aef09aec..700c5ac304f717 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -16,7 +16,8 @@ import sys import unittest from test import support -from test.support import import_helper, requires_IEEE_754 +from test.support import (import_helper, requires_IEEE_754, + skip_if_double_rounding, skip_on_newlib) from decimal import Decimal from fractions import Fraction @@ -28,12 +29,6 @@ # === Helper functions and class === -# Test copied from Lib/test/test_math.py -# detect evidence of double-rounding: fsum is not always correctly -# rounded on machines that suffer from double rounding. -x, y = 1e16, 2.9999 # use temporary values to defeat peephole optimizer -HAVE_DOUBLE_ROUNDING = (x + y == 1e16 + 4) - def sign(x): """Return -1.0 for negatives, including -0.0, otherwise +1.0.""" return math.copysign(1, x) @@ -2005,7 +2000,6 @@ def test_iter_list_same(self): expected = self.func(data) self.assertEqual(self.func(iter(data)), expected) - class TestPVariance(VarianceStdevMixin, NumericTestCase, UnivariateTypeMixin): # Tests for population variance. def setUp(self): @@ -2113,6 +2107,14 @@ def test_center_not_at_mean(self): self.assertEqual(self.func(data), 2.5) self.assertEqual(self.func(data, mu=0.5), 6.5) + def test_gh_140938(self): + # Inputs with inf/nan should raise a ValueError + with self.assertRaises(ValueError): + self.func([1.0, math.inf]) + with self.assertRaises(ValueError): + self.func([1.0, math.nan]) + + class TestSqrtHelpers(unittest.TestCase): def test_integer_sqrt_of_frac_rto(self): @@ -2789,9 +2791,9 @@ def test_sqrtprod_helper_function_fundamentals(self): self.assertEqual(sign(actual), sign(expected)) @requires_IEEE_754 - @unittest.skipIf(HAVE_DOUBLE_ROUNDING, - "accuracy not guaranteed on machines with double rounding") + @skip_if_double_rounding @support.cpython_only # Allow for a weaker sumprod() implementation + @skip_on_newlib def test_sqrtprod_helper_function_improved_accuracy(self): # Test a known example where accuracy is improved x, y, target = 0.8035720646477457, 0.7957468097636939, 0.7996498651651661 diff --git a/Lib/test/test_str.py b/Lib/test/test_str.py index 2584fbf72d3fa6..4f57499af70f4d 100644 --- a/Lib/test/test_str.py +++ b/Lib/test/test_str.py @@ -454,6 +454,13 @@ def test_maketrans_translate(self): self.assertEqual("[a\xe9]".translate(str.maketrans({'a': '<\u20ac>'})), "[<\u20ac>\xe9]") + # with frozendict + tbl = self.type2test.maketrans(frozendict({'s': 'S', 'T': 't'})) + self.assertEqual(tbl, {ord('s'): 'S', ord('T'): 't'}) + self.assertEqual('sTan'.translate(tbl), 'Stan') + tbl = self.type2test.maketrans(frozendict({'a': None, 'b': '<i>'})) + self.checkequalnofix('<i><i><i>c', 'abababc', 'translate', tbl) + # invalid Unicode characters invalid_char = 0x10ffff+1 for before in "a\xe9\u20ac\U0010ffff": @@ -1578,17 +1585,40 @@ def __int__(self): self.assertEqual('%X' % letter_m, '6D') self.assertEqual('%o' % letter_m, '155') self.assertEqual('%c' % letter_m, 'm') - self.assertRaisesRegex(TypeError, '%x format: an integer is required, not float', operator.mod, '%x', 3.14) - self.assertRaisesRegex(TypeError, '%X format: an integer is required, not float', operator.mod, '%X', 2.11) - self.assertRaisesRegex(TypeError, '%o format: an integer is required, not float', operator.mod, '%o', 1.79) - self.assertRaisesRegex(TypeError, '%x format: an integer is required, not PseudoFloat', operator.mod, '%x', pi) - self.assertRaisesRegex(TypeError, '%x format: an integer is required, not complex', operator.mod, '%x', 3j) - self.assertRaisesRegex(TypeError, '%X format: an integer is required, not complex', operator.mod, '%X', 2j) - self.assertRaisesRegex(TypeError, '%o format: an integer is required, not complex', operator.mod, '%o', 1j) - self.assertRaisesRegex(TypeError, '%u format: a real number is required, not complex', operator.mod, '%u', 3j) - self.assertRaisesRegex(TypeError, '%i format: a real number is required, not complex', operator.mod, '%i', 2j) - self.assertRaisesRegex(TypeError, '%d format: a real number is required, not complex', operator.mod, '%d', 1j) - self.assertRaisesRegex(TypeError, r'%c requires an int or a unicode character, not .*\.PseudoFloat', operator.mod, '%c', pi) + with self.assertRaisesRegex(TypeError, + 'format argument: %x requires an integer, not float'): + '%x' % 3.14 + with self.assertRaisesRegex(TypeError, + 'format argument: %X requires an integer, not float'): + '%X' % 2.11 + with self.assertRaisesRegex(TypeError, + 'format argument: %o requires an integer, not float'): + '%o' % 1.79 + with self.assertRaisesRegex(TypeError, + r'format argument: %x requires an integer, not .*\.PseudoFloat'): + '%x' % pi + with self.assertRaisesRegex(TypeError, + 'format argument: %x requires an integer, not complex'): + '%x' % 3j + with self.assertRaisesRegex(TypeError, + 'format argument: %X requires an integer, not complex'): + '%X' % 2j + with self.assertRaisesRegex(TypeError, + 'format argument: %o requires an integer, not complex'): + '%o' % 1j + with self.assertRaisesRegex(TypeError, + 'format argument: %u requires a real number, not complex'): + '%u' % 3j + with self.assertRaisesRegex(TypeError, + 'format argument: %i requires a real number, not complex'): + '%i' % 2j + with self.assertRaisesRegex(TypeError, + 'format argument: %d requires a real number, not complex'): + '%d' % 1j + with self.assertRaisesRegex(TypeError, + r'format argument: %c requires an integer or a unicode character, ' + r'not .*\.PseudoFloat'): + '%c' % pi class RaisingNumber: def __int__(self): diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 0241e543cd7dde..e95cc6db170e24 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -8,7 +8,6 @@ import platform import sys from test import support -from test.support import warnings_helper from test.support import skip_if_buggy_ucrt_strfptime, run_with_locales from datetime import date as datetime_date @@ -20,6 +19,13 @@ else: glibc_ver = None +def skip_cygwin_locale(): + if sys.platform != 'cygwin': + return + loc = locale.getlocale(locale.LC_TIME)[0] + if loc in ('my_MM', 'or_IN'): + raise unittest.SkipTest('test fails on Cygwin') + class getlang_Tests(unittest.TestCase): """Test _getlang""" @@ -286,7 +292,7 @@ def test_ValueError(self): def test_strptime_exception_context(self): # check that this doesn't chain exceptions needlessly (see #17572) with self.assertRaises(ValueError) as e: - _strptime._strptime_time('', '%D') + _strptime._strptime_time('', '%!') self.assertTrue(e.exception.__suppress_context__) # additional check for stray % branch with self.assertRaises(ValueError) as e: @@ -406,37 +412,50 @@ def test_offset(self): (*_, offset), _, offset_fraction = _strptime._strptime("-013030.000001", "%z") self.assertEqual(offset, -(one_hour + half_hour + half_minute)) self.assertEqual(offset_fraction, -1) - (*_, offset), _, offset_fraction = _strptime._strptime("+01:00", "%z") - self.assertEqual(offset, one_hour) - self.assertEqual(offset_fraction, 0) - (*_, offset), _, offset_fraction = _strptime._strptime("-01:30", "%z") - self.assertEqual(offset, -(one_hour + half_hour)) - self.assertEqual(offset_fraction, 0) - (*_, offset), _, offset_fraction = _strptime._strptime("-01:30:30", "%z") - self.assertEqual(offset, -(one_hour + half_hour + half_minute)) - self.assertEqual(offset_fraction, 0) - (*_, offset), _, offset_fraction = _strptime._strptime("-01:30:30.000001", "%z") - self.assertEqual(offset, -(one_hour + half_hour + half_minute)) - self.assertEqual(offset_fraction, -1) - (*_, offset), _, offset_fraction = _strptime._strptime("+01:30:30.001", "%z") - self.assertEqual(offset, one_hour + half_hour + half_minute) - self.assertEqual(offset_fraction, 1000) - (*_, offset), _, offset_fraction = _strptime._strptime("Z", "%z") - self.assertEqual(offset, 0) - self.assertEqual(offset_fraction, 0) + + cases = [ + ("+01:00", one_hour, 0), + ("-01:30", -(one_hour + half_hour), 0), + ("-01:30:30", -(one_hour + half_hour + half_minute), 0), + ("-01:30:30.000001", -(one_hour + half_hour + half_minute), -1), + ("+01:30:30.001", +(one_hour + half_hour + half_minute), 1000), + ("Z", 0, 0), + ] + for directive in ("%z", "%:z"): + for offset_str, expected_offset, expected_fraction in cases: + with self.subTest(offset_str=offset_str, directive=directive): + (*_, offset), _, offset_fraction = _strptime._strptime( + offset_str, directive + ) + self.assertEqual(offset, expected_offset) + self.assertEqual(offset_fraction, expected_fraction) def test_bad_offset(self): - with self.assertRaises(ValueError): - _strptime._strptime("-01:30:30.", "%z") - with self.assertRaises(ValueError): - _strptime._strptime("-0130:30", "%z") - with self.assertRaises(ValueError): - _strptime._strptime("-01:30:30.1234567", "%z") - with self.assertRaises(ValueError): - _strptime._strptime("-01:30:30:123456", "%z") + error_cases_any_z = [ + "-01:30:30.", # Decimal point not followed with digits + "-01:30:30.1234567", # Too many digits after decimal point + "-01:30:30:123456", # Colon as decimal separator + "-0130:30", # Incorrect use of colons + ] + for directive in ("%z", "%:z"): + for timestr in error_cases_any_z: + with self.subTest(timestr=timestr, directive=directive): + with self.assertRaises(ValueError): + _strptime._strptime(timestr, directive) + + required_colons_cases = ["-013030", "+0130", "-01:3030.123456"] + for timestr in required_colons_cases: + with self.subTest(timestr=timestr): + with self.assertRaises(ValueError): + _strptime._strptime(timestr, "%:z") + with self.assertRaises(ValueError) as err: _strptime._strptime("-01:3030", "%z") self.assertEqual("Inconsistent use of : in -01:3030", str(err.exception)) + with self.assertRaises(ValueError) as err: + _strptime._strptime("-01:3030", "%:z") + self.assertEqual("Missing colon in %:z before '30', got '-01:3030'", + str(err.exception)) @skip_if_buggy_ucrt_strfptime def test_timezone(self): @@ -497,6 +516,8 @@ def test_bad_timezone(self): 'my_MM', 'or_IN', 'shn_MM', 'az_IR', 'byn_ER', 'wal_ET', 'lzh_TW') def test_date_time_locale(self): + skip_cygwin_locale() + # Test %c directive loc = locale.getlocale(locale.LC_TIME)[0] if glibc_ver and glibc_ver < (2, 31) and loc == 'br_FR': @@ -524,6 +545,8 @@ def test_date_time_locale(self): 'csb_PL', 'br_FR', 'gez_ET', 'brx_IN', 'my_MM', 'shn_MM') def test_date_time_locale2(self): + skip_cygwin_locale() + # Test %c directive loc = locale.getlocale(locale.LC_TIME)[0] if sys.platform.startswith('sunos'): @@ -538,6 +561,8 @@ def test_date_time_locale2(self): 'he_IL', 'eu_ES', 'ar_AE', 'az_IR', 'my_MM', 'or_IN', 'shn_MM', 'lzh_TW') def test_date_locale(self): + skip_cygwin_locale() + # Test %x directive now = time.time() self.roundtrip('%x', slice(0, 3), time.localtime(now)) @@ -555,9 +580,11 @@ def test_date_locale(self): @run_with_locales('LC_TIME', 'en_US', 'fr_FR', 'de_DE', 'ja_JP', 'eu_ES', 'ar_AE', 'my_MM', 'shn_MM', 'lzh_TW') def test_date_locale2(self): + skip_cygwin_locale() + # Test %x directive loc = locale.getlocale(locale.LC_TIME)[0] - if sys.platform.startswith('sunos'): + if sys.platform.startswith(('sunos', 'aix')): if loc in ('en_US', 'de_DE', 'ar_AE'): self.skipTest(f'locale {loc!r} may not work on this platform') self.roundtrip('%x', slice(0, 3), (1900, 1, 1, 0, 0, 0, 0, 1, 0)) @@ -575,6 +602,8 @@ def test_date_locale2(self): 'ti_ET', 'tig_ER', 'wal_ET', 'lzh_TW', 'ar_SA', 'bg_BG') def test_time_locale(self): + skip_cygwin_locale() + # Test %X directive loc = locale.getlocale(locale.LC_TIME)[0] pos = slice(3, 6) @@ -626,15 +655,49 @@ def test_escaping(self): need_escaping = r".^$*+?{}\[]|)(" self.assertTrue(_strptime._strptime_time(need_escaping, need_escaping)) - @warnings_helper.ignore_warnings(category=DeprecationWarning) # gh-70647 def test_feb29_on_leap_year_without_year(self): - time.strptime("Feb 29", "%b %d") + with self.assertRaises(ValueError): + time.strptime("Feb 29", "%b %d") + with self.assertRaises(ValueError): + time.strptime("Mar 1", "%b %d") + + def test_strptime_F_format(self): + test_date = "2025-10-26" + self.assertEqual( + time.strptime(test_date, "%F"), + time.strptime(test_date, "%Y-%m-%d") + ) + + def test_strptime_T_format(self): + test_time = "15:00:00" + self.assertEqual( + time.strptime(test_time, "%T"), + time.strptime(test_time, "%H:%M:%S") + ) + + def test_strptime_D_format(self): + test_date = "11/28/25" + self.assertEqual( + time.strptime(test_date, "%D"), + time.strptime(test_date, "%m/%d/%y") + ) - @warnings_helper.ignore_warnings(category=DeprecationWarning) # gh-70647 - def test_mar1_comes_after_feb29_even_when_omitting_the_year(self): - self.assertLess( - time.strptime("Feb 29", "%b %d"), - time.strptime("Mar 1", "%b %d")) + def test_strptime_n_and_t_format(self): + format_directives = ('%n', '%t', '%n%t', '%t%n') + whitespaces = ('', ' ', '\t', '\r', '\v', '\n', '\f') + for fd in format_directives: + for ws in (*whitespaces, ''.join(whitespaces)): + with self.subTest(format_directive=fd, whitespace=ws): + self.assertEqual( + time.strptime( + f"2026{ws}02{ws}03", + f"%Y{fd}%m{fd}%d", + ), + time.strptime( + f'2026-02-03', + "%Y-%m-%d", + ), + ) class Strptime12AMPMTests(unittest.TestCase): """Test a _strptime regression in '%I %p' at 12 noon (12 PM)""" diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 7df01f28f09118..edd85df633fc3b 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -397,6 +397,21 @@ def test_705836(self): big = (1 << 25) - 1 big = math.ldexp(big, 127 - 24) self.assertRaises(OverflowError, struct.pack, ">f", big) + self.assertRaises(OverflowError, struct.pack, "<f", big) + # same for native format, see gh-145633 + self.assertRaises(OverflowError, struct.pack, "f", big) + + # And for half-floats + big = (1 << 11) - 1 + big = math.ldexp(big, 15 - 10) + packed = struct.pack(">e", big) + unpacked = struct.unpack(">e", packed)[0] + self.assertEqual(big, unpacked) + big = (1 << 12) - 1 + big = math.ldexp(big, 15 - 11) + self.assertRaises(OverflowError, struct.pack, ">e", big) + self.assertRaises(OverflowError, struct.pack, "<e", big) + self.assertRaises(OverflowError, struct.pack, "e", big) def test_1530559(self): for code, byteorder in iter_integer_formats(): @@ -433,56 +448,63 @@ def test_unpack_from(self): self.assertEqual(s.unpack_from(buffer=test_string, offset=2), (b'cd01',)) - def test_pack_into(self): + def _test_pack_into(self, pack_into): test_string = b'Reykjavik rocks, eow!' - writable_buf = array.array('b', b' '*100) - fmt = '21s' - s = struct.Struct(fmt) + writable_buf = memoryview(array.array('b', b' '*100)) # Test without offset - s.pack_into(writable_buf, 0, test_string) + pack_into(writable_buf, 0, test_string) from_buf = writable_buf.tobytes()[:len(test_string)] self.assertEqual(from_buf, test_string) # Test with offset. - s.pack_into(writable_buf, 10, test_string) + pack_into(writable_buf, 10, test_string) from_buf = writable_buf.tobytes()[:len(test_string)+10] self.assertEqual(from_buf, test_string[:10] + test_string) + # Test with negative offset. + pack_into(writable_buf, -30, test_string) + from_buf = writable_buf.tobytes()[-30:-30+len(test_string)] + self.assertEqual(from_buf, test_string) + # Go beyond boundaries. small_buf = array.array('b', b' '*10) - self.assertRaises((ValueError, struct.error), s.pack_into, small_buf, 0, - test_string) - self.assertRaises((ValueError, struct.error), s.pack_into, small_buf, 2, - test_string) + with self.assertRaises((ValueError, struct.error)): + pack_into(small_buf, 0, test_string) + with self.assertRaises((ValueError, struct.error)): + pack_into(writable_buf, 90, test_string) + with self.assertRaises((ValueError, struct.error)): + pack_into(writable_buf, -10, test_string) + with self.assertRaises((ValueError, struct.error)): + pack_into(writable_buf, 150, test_string) + with self.assertRaises((ValueError, struct.error)): + pack_into(writable_buf, -150, test_string) + + # Test invalid buffer. + self.assertRaises(TypeError, pack_into, b' '*100, 0, test_string) + self.assertRaises(TypeError, pack_into, ' '*100, 0, test_string) + self.assertRaises(TypeError, pack_into, [0]*100, 0, test_string) + self.assertRaises(TypeError, pack_into, None, 0, test_string) + self.assertRaises(TypeError, pack_into, writable_buf[::2], 0, test_string) + self.assertRaises(TypeError, pack_into, writable_buf[::-1], 0, test_string) + + # Test bogus offset (issue bpo-3694) + with self.assertRaises(TypeError): + pack_into(writable_buf, None, test_string) + with self.assertRaises(TypeError): + pack_into(writable_buf, 0.0, test_string) + with self.assertRaises(OverflowError): + pack_into(writable_buf, 2**1000, test_string) + with self.assertRaises(OverflowError): + pack_into(writable_buf, -2**1000, test_string) - # Test bogus offset (issue 3694) - sb = small_buf - self.assertRaises((TypeError, struct.error), struct.pack_into, b'', sb, - None) + def test_pack_into(self): + s = struct.Struct('21s') + self._test_pack_into(s.pack_into) def test_pack_into_fn(self): - test_string = b'Reykjavik rocks, eow!' - writable_buf = array.array('b', b' '*100) - fmt = '21s' - pack_into = lambda *args: struct.pack_into(fmt, *args) - - # Test without offset. - pack_into(writable_buf, 0, test_string) - from_buf = writable_buf.tobytes()[:len(test_string)] - self.assertEqual(from_buf, test_string) - - # Test with offset. - pack_into(writable_buf, 10, test_string) - from_buf = writable_buf.tobytes()[:len(test_string)+10] - self.assertEqual(from_buf, test_string[:10] + test_string) - - # Go beyond boundaries. - small_buf = array.array('b', b' '*10) - self.assertRaises((ValueError, struct.error), pack_into, small_buf, 0, - test_string) - self.assertRaises((ValueError, struct.error), pack_into, small_buf, 2, - test_string) + pack_into = lambda *args: struct.pack_into('21s', *args) + self._test_pack_into(pack_into) def test_unpack_with_buffer(self): # SF bug 1563759: struct.unpack doesn't support buffer protocol objects @@ -545,6 +567,15 @@ def test_count_overflow(self): hugecount2 = '{}b{}H'.format(sys.maxsize//2, sys.maxsize//2) self.assertRaises(struct.error, struct.calcsize, hugecount2) + hugecount3 = '{}i{}q'.format(sys.maxsize // 4, sys.maxsize // 8) + self.assertRaises(struct.error, struct.calcsize, hugecount3) + + hugecount4 = '{}?s'.format(sys.maxsize) + self.assertRaises(struct.error, struct.calcsize, hugecount4) + + hugecount5 = '{}?p'.format(sys.maxsize) + self.assertRaises(struct.error, struct.calcsize, hugecount5) + def test_trailing_counter(self): store = array.array('b', b' '*100) @@ -574,12 +605,37 @@ def test_Struct_reinitialization(self): # Issue 9422: there was a memory leak when reinitializing a # Struct instance. This test can be used to detect the leak # when running with regrtest -L. - s = struct.Struct('i') - s.__init__('ii') + s = struct.Struct('>h') + msg = 'Re-initialization .* will not work' + with self.assertWarnsRegex(FutureWarning, msg): + s.__init__('>hh') + self.assertEqual(s.format, '>hh') + packed = b'\x00\x01\x00\x02' + self.assertEqual(s.pack(1, 2), packed) + self.assertEqual(s.unpack(packed), (1, 2)) + + s.__init__('>hh') # same format + self.assertEqual(s.format, '>hh') + self.assertEqual(s.pack(1, 2), packed) + self.assertEqual(s.unpack(packed), (1, 2)) + + with self.assertWarnsRegex(FutureWarning, msg): + with self.assertRaises(ValueError): + s.__init__('\udc00') + self.assertEqual(s.format, '>hh') + self.assertEqual(s.pack(1, 2), packed) + self.assertEqual(s.unpack(packed), (1, 2)) + + with self.assertWarnsRegex(FutureWarning, msg): + with self.assertRaises(struct.error): + s.__init__('$') + self.assertEqual(s.format, '>hh') + self.assertEqual(s.pack(1, 2), packed) + self.assertEqual(s.unpack(packed), (1, 2)) def check_sizeof(self, format_str, number_of_codes): # The size of 'PyStructObject' - totalsize = support.calcobjsize('2n3P') + totalsize = support.calcobjsize('2n3P?0P') # The size taken up by the 'formatcode' dynamic array totalsize += struct.calcsize('P3n0P') * (number_of_codes + 1) support.check_sizeof(self, struct.Struct(format_str), totalsize) @@ -777,15 +833,160 @@ def test_error_propagation(fmt_str): test_error_propagation('N') test_error_propagation('n') - def test_struct_subclass_instantiation(self): + def test_custom_struct_init(self): # Regression test for https://github.com/python/cpython/issues/112358 class MyStruct(struct.Struct): - def __init__(self): + def __init__(self, *args, **kwargs): super().__init__('>h') + my_struct = MyStruct('>h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + my_struct = MyStruct(format='>h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + + warnmsg = r"Different format arguments for __new__\(\) and __init__\(\) methods of Struct" + with self.assertWarnsRegex(FutureWarning, warnmsg): + my_struct = MyStruct('<h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertWarnsRegex(FutureWarning, warnmsg): + my_struct = MyStruct(format='<h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + + warnmsg = r"Struct\(\) missing required argument 'format' \(pos 1\)" + with self.assertWarnsRegex(DeprecationWarning, warnmsg): + my_struct = MyStruct() + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertWarnsRegex(DeprecationWarning, warnmsg): + my_struct = MyStruct(arg='>h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + + warnmsg = r"Struct\(\) takes at most 1 argument \(2 given\)" + with self.assertWarnsRegex(DeprecationWarning, warnmsg): + my_struct = MyStruct('>h', 42) + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertWarnsRegex(DeprecationWarning, warnmsg): + my_struct = MyStruct('>h', arg=42) + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertWarnsRegex(DeprecationWarning, warnmsg): + my_struct = MyStruct('>h', format=42) + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertWarnsRegex(DeprecationWarning, warnmsg): + my_struct = MyStruct(format='>h', arg=42) + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + + warnmsg = r"Invalid 'format' argument for Struct\.__new__\(\): " + with self.assertWarnsRegex(DeprecationWarning, warnmsg + '.*must be'): + my_struct = MyStruct(42) + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertWarnsRegex(DeprecationWarning, warnmsg + '.*must be'): + my_struct = MyStruct(format=42) + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertWarnsRegex(DeprecationWarning, warnmsg + 'bad char'): + my_struct = MyStruct('$') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertWarnsRegex(DeprecationWarning, warnmsg + 'bad char'): + my_struct = MyStruct(format='$') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertWarnsRegex(DeprecationWarning, warnmsg + "non-ASCII"): + my_struct = MyStruct('\udc00') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertWarnsRegex(DeprecationWarning, warnmsg + "non-ASCII"): + my_struct = MyStruct(format='\udc00') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + + def test_custom_struct_new(self): + # New way, no warnings: + class MyStruct(struct.Struct): + def __new__(cls, *args, **kwargs): + return super().__new__(cls, '>h') + + for format in '>h', '<h', 42, '$', '\u20ac', '\udc00', b'\xa4': + with self.subTest(format=format): + my_struct = MyStruct(format) + self.assertEqual(my_struct.format, '>h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + my_struct = MyStruct(format='<h') + self.assertEqual(my_struct.format, '>h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') my_struct = MyStruct() + self.assertEqual(my_struct.format, '>h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + my_struct = MyStruct('<h', 42) + self.assertEqual(my_struct.format, '>h') self.assertEqual(my_struct.pack(12345), b'\x30\x39') + def test_custom_struct_new_and_init(self): + # New way, no warnings: + class MyStruct(struct.Struct): + def __new__(cls, newargs, initargs): + return super().__new__(cls, *newargs) + def __init__(self, newargs, initargs): + if initargs is not None: + super().__init__(*initargs) + + my_struct = MyStruct(('>h',), ('>h',)) + self.assertEqual(my_struct.format, '>h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertRaises(TypeError): + MyStruct((), ()) + with self.assertRaises(TypeError): + MyStruct(('>h',), ()) + with self.assertRaises(TypeError): + MyStruct((), ('>h',)) + with self.assertRaises(TypeError): + MyStruct((42,), ('>h',)) + with self.assertWarns(FutureWarning): + with self.assertRaises(TypeError): + MyStruct(('>h',), (42,)) + with self.assertRaises(struct.error): + MyStruct(('$',), ('>h',)) + with self.assertWarns(FutureWarning): + with self.assertRaises(struct.error): + MyStruct(('>h',), ('$',)) + with self.assertRaises(ValueError): + MyStruct(('\udc00',), ('>h',)) + with self.assertRaises(ValueError): + MyStruct((b'\xa4',), ('>h',)) + with self.assertWarns(FutureWarning): + with self.assertRaises(ValueError): + MyStruct(('>h',), ('\udc00',)) + with self.assertWarns(FutureWarning): + with self.assertRaises(ValueError): + MyStruct(('>h',), (b'\xa4',)) + with self.assertWarns(FutureWarning): + my_struct = MyStruct(('>h',), ('<h',)) + self.assertEqual(my_struct.format, '<h') + self.assertEqual(my_struct.pack(12345), b'\x39\x30') + + def test_no_custom_struct_new_or_init(self): + class MyStruct(struct.Struct): + pass + + my_struct = MyStruct('>h') + self.assertEqual(my_struct.format, '>h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + my_struct = MyStruct(format='>h') + self.assertEqual(my_struct.format, '>h') + self.assertEqual(my_struct.pack(12345), b'\x30\x39') + with self.assertRaises(TypeError): + MyStruct() + with self.assertRaises(TypeError): + MyStruct(42) + with self.assertRaises(struct.error): + MyStruct('$') + with self.assertRaises(ValueError): + MyStruct('\udc00') + with self.assertRaises(ValueError): + MyStruct(b'\xa4') + with self.assertRaises(TypeError): + MyStruct('>h', 42) + with self.assertRaises(TypeError): + MyStruct('>h', arg=42) + with self.assertRaises(TypeError): + MyStruct(arg=42) + with self.assertRaises(TypeError): + MyStruct('>h', format='>h') + def test_repr(self): s = struct.Struct('=i2H') self.assertEqual(repr(s), f'Struct({s.format!r})') @@ -794,11 +995,64 @@ def test_c_complex_round_trip(self): values = [complex(*_) for _ in combinations([1, -1, 0.0, -0.0, 2, -3, INF, -INF, NAN], 2)] for z in values: - for f in ['F', 'D', '>F', '>D', '<F', '<D']: + for f in [ + 'F', 'D', 'Zf', 'Zd', + '>F', '>D', '>Zf', '>Zd', + '<F', '<D', '<Zf', '<Zd', + ]: with self.subTest(z=z, format=f): round_trip = struct.unpack(f, struct.pack(f, z))[0] self.assertComplexesAreIdentical(z, round_trip) + @unittest.skipIf( + support.is_android or support.is_apple_mobile, + "Subinterpreters are not supported on Android and iOS" + ) + def test_endian_table_init_subinterpreters(self): + # Verify that the _struct extension module can be initialized + # concurrently in subinterpreters (gh-140260). + try: + from concurrent.futures import InterpreterPoolExecutor + except ImportError: + raise unittest.SkipTest("InterpreterPoolExecutor not available") + + code = "import struct" + with InterpreterPoolExecutor(max_workers=5) as executor: + results = executor.map(exec, [code] * 5) + self.assertListEqual(list(results), [None] * 5) + + def test_operations_on_half_initialized_Struct(self): + S = struct.Struct.__new__(struct.Struct) + + spam = array.array('b', b' ') + self.assertRaises(RuntimeError, S.iter_unpack, spam) + self.assertRaises(RuntimeError, S.pack, 1) + self.assertRaises(RuntimeError, S.pack_into, spam, 1) + self.assertRaises(RuntimeError, S.unpack, spam) + self.assertRaises(RuntimeError, S.unpack_from, spam) + self.assertRaises(AttributeError, getattr, S, 'format') + self.assertRaises(RuntimeError, S.__sizeof__) + self.assertRaises(RuntimeError, repr, S) + self.assertEqual(S.size, -1) + + def test_float_round_trip(self): + for format in ( + "f", "<f", ">f", + "d", "<d", ">d", + "e", "<e", ">e", + ): + with self.subTest(format=format): + f = struct.unpack(format, struct.pack(format, 1.5))[0] + self.assertEqual(f, 1.5) + f = struct.unpack(format, struct.pack(format, NAN))[0] + self.assertTrue(math.isnan(f), f) + f = struct.unpack(format, struct.pack(format, INF))[0] + self.assertTrue(math.isinf(f), f) + self.assertEqual(math.copysign(1.0, f), 1.0) + f = struct.unpack(format, struct.pack(format, -INF))[0] + self.assertTrue(math.isinf(f), f) + self.assertEqual(math.copysign(1.0, f), -1.0) + class UnpackIteratorTest(unittest.TestCase): """ @@ -873,6 +1127,7 @@ def test_module_func(self): self.assertRaises(StopIteration, next, it) def test_half_float(self): + _testcapi = import_helper.import_module('_testcapi') # Little-endian examples from: # http://en.wikipedia.org/wiki/Half_precision_floating-point_format format_bits_float__cleanRoundtrip_list = [ @@ -917,10 +1172,17 @@ def test_half_float(self): # Check that packing produces a bit pattern representing a quiet NaN: # all exponent bits and the msb of the fraction should all be 1. + if _testcapi.nan_msb_is_signaling: + # HP PA RISC and some MIPS CPUs use 0 for quiet, see: + # https://en.wikipedia.org/wiki/NaN#Encoding + expected = 0x7c + else: + expected = 0x7e + packed = struct.pack('<e', math.nan) - self.assertEqual(packed[1] & 0x7e, 0x7e) + self.assertEqual(packed[1] & 0x7e, expected) packed = struct.pack('<e', -math.nan) - self.assertEqual(packed[1] & 0x7e, 0x7e) + self.assertEqual(packed[1] & 0x7e, expected) # Checks for round-to-even behavior format_bits_float__rounding_list = [ diff --git a/Lib/test/test_structseq.py b/Lib/test/test_structseq.py index 9622151143cd78..74506fc54de50e 100644 --- a/Lib/test/test_structseq.py +++ b/Lib/test/test_structseq.py @@ -1,4 +1,5 @@ import copy +import gc import os import pickle import re @@ -355,6 +356,14 @@ def test_reference_cycle(self): type(t).refcyle = t """)) + def test_replace_gc_tracked(self): + # Verify that __replace__ results are properly GC-tracked + time_struct = time.gmtime(0) + lst = [] + replaced_struct = time_struct.__replace__(tm_year=lst) + lst.append(replaced_struct) + + self.assertTrue(gc.is_tracked(replaced_struct)) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index f0e350c71f60ea..d41cb1294a3daf 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -22,6 +22,7 @@ import sysconfig import select import shutil +import socket import threading import gc import textwrap @@ -957,6 +958,48 @@ def test_communicate(self): self.assertEqual(stdout, b"banana") self.assertEqual(stderr, b"pineapple") + def test_communicate_memoryview_input(self): + # Test memoryview input with byte elements + test_data = b"Hello, memoryview!" + mv = memoryview(test_data) + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.stdout.write(sys.stdin.read())'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + self.addCleanup(p.stdout.close) + self.addCleanup(p.stdin.close) + (stdout, stderr) = p.communicate(mv) + self.assertEqual(stdout, test_data) + self.assertIsNone(stderr) + + def test_communicate_memoryview_input_nonbyte(self): + # Test memoryview input with non-byte elements (e.g., int32) + # This tests the fix for gh-134453 where non-byte memoryviews + # had incorrect length tracking on POSIX + import array + # Create an array of 32-bit integers that's large enough to trigger + # the chunked writing behavior (> PIPE_BUF) + pipe_buf = getattr(select, 'PIPE_BUF', 512) + # Each 'i' element is 4 bytes, so we need more than pipe_buf/4 elements + # Add some extra to ensure we exceed the buffer size + num_elements = pipe_buf + 1 + test_array = array.array('i', [0x64306f66 for _ in range(num_elements)]) + expected_bytes = test_array.tobytes() + mv = memoryview(test_array) + + p = subprocess.Popen([sys.executable, "-c", + 'import sys; ' + 'data = sys.stdin.buffer.read(); ' + 'sys.stdout.buffer.write(data)'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + self.addCleanup(p.stdout.close) + self.addCleanup(p.stdin.close) + (stdout, stderr) = p.communicate(mv) + self.assertEqual(stdout, expected_bytes, + msg=f"{len(stdout)=} =? {len(expected_bytes)=}") + self.assertIsNone(stderr) + def test_communicate_timeout(self): p = subprocess.Popen([sys.executable, "-c", 'import sys,os,time;' @@ -992,6 +1035,134 @@ def test_communicate_timeout_large_output(self): (stdout, _) = p.communicate() self.assertEqual(len(stdout), 4 * 64 * 1024) + def test_communicate_timeout_large_input(self): + # Test that timeout is enforced when writing large input to a + # slow-to-read subprocess, and that partial input is preserved + # for continuation after timeout (gh-141473). + # + # This is a regression test for Windows matching POSIX behavior. + # On POSIX, select() is used to multiplex I/O with timeout checking. + # On Windows, stdin writing must also honor the timeout rather than + # blocking indefinitely when the pipe buffer fills. + + input_data = b"x" * (128 * 1024) # > typical pipe buffer + + # Cross-platform wake mechanism: the slow reader connects to a + # loopback TCP socket and blocks in select() on it (capped at 9s + # as a safety net we don't expect to hit). After phase 1 raises + # TimeoutExpired, the parent sends a byte to release the child so + # it drains stdin. A socket (rather than a raw pipe) is required + # because Windows select() only supports sockets, not arbitrary + # file descriptors. + server = socket.create_server(('127.0.0.1', 0), backlog=1) + server.settimeout(10) # bound the accept() if the child fails to start + port = server.getsockname()[1] + # The child sends one byte (low byte of its PID) first so the parent + # can detect the rare case of an unrelated process on the same host + # connecting to our ephemeral port before our child does. A single + # byte gives 1/256 collision odds, which is plenty for flake-prevention. + slow_reader = ( + "import os, socket, sys, select; " + f"s = socket.create_connection(('127.0.0.1', {port}), timeout=9); " + "s.sendall(bytes([os.getpid() & 0xff])); " + "select.select([s], [], [], 9); " + "sys.stdout.buffer.write(sys.stdin.buffer.read())" + ) + + p = subprocess.Popen( + [sys.executable, "-c", slow_reader], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + conn = None + try: + conn, _ = server.accept() + server.close() + server = None + + conn.settimeout(5) + peer_byte = conn.recv(1) + conn.settimeout(None) + self.assertEqual(peer_byte, bytes([p.pid & 0xff]), + f"loopback handshake byte {peer_byte!r} != " + f"low byte of child PID {p.pid} ({p.pid & 0xff:#x})") + + timeout = 0.2 + start = time.monotonic() + try: + p.communicate(input_data, timeout=timeout) + # If we get here without TimeoutExpired, the timeout was ignored + elapsed = time.monotonic() - start + self.fail( + f"TimeoutExpired not raised. communicate() completed in " + f"{elapsed:.2f}s, but slow reader stalls for up to 9s. " + "Stdin writing blocked without enforcing timeout.") + except subprocess.TimeoutExpired: + elapsed = time.monotonic() - start + + # Timeout should occur close to the specified timeout value, + # not after waiting for the subprocess to finish sleeping. + # Allow generous margin for slow CI, but must be well under + # the slow-reader's stall cap. + self.assertLess(elapsed, 5.0, + f"TimeoutExpired raised after {elapsed:.2f}s; expected ~{timeout}s. " + "Stdin writing blocked without checking timeout.") + + # Release the slow reader so it stops blocking and drains stdin. + conn.sendall(b'go') + conn.close() + conn = None + + # After timeout, continue communication. The remaining input + # should be sent and we should receive all data back. + stdout, stderr = p.communicate() + + # Verify all input was eventually received by the subprocess + self.assertEqual(len(stdout), len(input_data), + f"Expected {len(input_data)} bytes output but got {len(stdout)}") + self.assertEqual(stdout, input_data) + finally: + if conn is not None: + conn.close() + if server is not None: + server.close() + p.kill() + p.wait() + + def test_communicate_timeout_resume_partial_write(self): + """Resume writing input after a partial-write TimeoutExpired. + + Exercises the _input_offset bookkeeping across the + _communicate_io_posix factoring: a first communicate() must time out + mid-write, and a subsequent communicate() must finish delivering the + remaining bytes so the child receives the full input intact. + """ + # 1 MiB easily exceeds typical pipe buffers (~64 KiB) so writing + # blocks once the buffer fills before the child starts reading. + input_data = bytes(range(256)) * 4096 # 1 MiB, distinctive pattern + self.assertEqual(len(input_data), 1024 * 1024) + + p = subprocess.Popen( + [sys.executable, "-c", + "import sys, time; " + "time.sleep(0.5); " + "sys.stdout.buffer.write(sys.stdin.buffer.read())"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + try: + with self.assertRaises(subprocess.TimeoutExpired): + p.communicate(input_data, timeout=0.05) + + # Resume: no new input, generous timeout to avoid CI flakes. + stdout, stderr = p.communicate(timeout=support.LONG_TIMEOUT) + self.assertEqual(len(stdout), len(input_data)) + self.assertEqual(stdout, input_data) + finally: + p.kill() + p.wait() + # Test for the fd leak reported in http://bugs.python.org/issue2791. def test_communicate_pipe_fd_leak(self): for stdin_pipe in (False, True): @@ -1062,6 +1233,19 @@ def test_writes_before_communicate(self): self.assertEqual(stdout, b"bananasplit") self.assertEqual(stderr, b"") + def test_communicate_stdin_closed_before_call(self): + # gh-70560, gh-74389: stdin.close() before communicate() + # should not raise ValueError from stdin.flush() + with subprocess.Popen([sys.executable, "-c", + 'import sys; sys.exit(0)'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) as p: + p.stdin.close() # Close stdin before communicate + # This should not raise ValueError + (stdout, stderr) = p.communicate() + self.assertEqual(p.returncode, 0) + def test_universal_newlines_and_text(self): args = [ sys.executable, "-c", @@ -1312,6 +1496,8 @@ def test_wait(self): def test_wait_timeout(self): p = subprocess.Popen([sys.executable, "-c", "import time; time.sleep(0.3)"]) + with self.assertRaises(subprocess.TimeoutExpired) as c: + p.wait(timeout=0) with self.assertRaises(subprocess.TimeoutExpired) as c: p.wait(timeout=0.0001) self.assertIn("0.0001", str(c.exception)) # For coverage of __str__. @@ -1643,6 +1829,40 @@ def test_wait_negative_timeout(self): self.assertEqual(proc.wait(), 0) + def test_post_timeout_communicate_sends_input(self): + """GH-141473 regression test; the stdin pipe must close""" + with subprocess.Popen( + [sys.executable, "-uc", """\ +import sys +while c := sys.stdin.read(512): + sys.stdout.write(c) +print() +"""], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) as proc: + try: + data = f"spam{'#'*4096}beans" + proc.communicate( + input=data, + timeout=0, + ) + except subprocess.TimeoutExpired as exc: + pass + # Prior to the bugfix, this would hang as the stdin + # pipe to the child had not been closed. + try: + stdout, stderr = proc.communicate(timeout=15) + except subprocess.TimeoutExpired as exc: + self.fail("communicate() hung waiting on child process that should have seen its stdin pipe close and exit") + self.assertEqual( + proc.returncode, 0, + msg=f"STDERR:\n{stderr}\nSTDOUT:\n{stdout}") + self.assertStartsWith(stdout, "spam") + self.assertIn("beans", stdout) + class RunFuncTestCase(BaseTestCase): def run_python(self, code, **kwargs): @@ -3499,8 +3719,9 @@ def test_startupinfo(self): # Since Python is a console process, it won't be affected # by wShowWindow, but the argument should be silently # ignored - subprocess.call(ZERO_RETURN_CMD, - startupinfo=startupinfo) + rc = subprocess.call(ZERO_RETURN_CMD, + startupinfo=startupinfo) + self.assertEqual(rc, 0) def test_startupinfo_keywords(self): # startupinfo argument @@ -3515,8 +3736,9 @@ def test_startupinfo_keywords(self): # Since Python is a console process, it won't be affected # by wShowWindow, but the argument should be silently # ignored - subprocess.call(ZERO_RETURN_CMD, - startupinfo=startupinfo) + rc = subprocess.call(ZERO_RETURN_CMD, + startupinfo=startupinfo) + self.assertEqual(rc, 0) def test_startupinfo_copy(self): # bpo-34044: Popen must not modify input STARTUPINFO structure @@ -3545,13 +3767,17 @@ def test_startupinfo_copy(self): self.assertEqual(startupinfo.wShowWindow, subprocess.SW_HIDE) self.assertEqual(startupinfo.lpAttributeList, {"handle_list": []}) + # CREATE_NEW_CONSOLE creates a "popup" window. + @support.requires_resource('gui') def test_creationflags(self): # creationflags argument CREATE_NEW_CONSOLE = 16 sys.stderr.write(" a DOS box should flash briefly ...\n") - subprocess.call(sys.executable + - ' -c "import time; time.sleep(0.25)"', - creationflags=CREATE_NEW_CONSOLE) + rc = subprocess.call(sys.executable + + ' -c "import time; time.sleep(0.25)"', + creationflags=CREATE_NEW_CONSOLE) + support.skip_on_low_desktop_heap_memory_subprocess(rc) + self.assertEqual(rc, 0) def test_invalid_args(self): # invalid arguments should raise ValueError @@ -3629,14 +3855,16 @@ def test_close_fds_with_stdio(self): def test_empty_attribute_list(self): startupinfo = subprocess.STARTUPINFO() startupinfo.lpAttributeList = {} - subprocess.call(ZERO_RETURN_CMD, - startupinfo=startupinfo) + rc = subprocess.call(ZERO_RETURN_CMD, + startupinfo=startupinfo) + self.assertEqual(rc, 0) def test_empty_handle_list(self): startupinfo = subprocess.STARTUPINFO() startupinfo.lpAttributeList = {"handle_list": []} - subprocess.call(ZERO_RETURN_CMD, - startupinfo=startupinfo) + rc = subprocess.call(ZERO_RETURN_CMD, + startupinfo=startupinfo) + self.assertEqual(rc, 0) def test_shell_sequence(self): # Run command through the shell (sequence) @@ -3647,6 +3875,8 @@ def test_shell_sequence(self): env=newenv) with p: self.assertIn(b"physalis", p.stdout.read()) + p.communicate() + self.assertEqual(p.returncode, 0) def test_shell_string(self): # Run command through the shell (string) @@ -3657,6 +3887,8 @@ def test_shell_string(self): env=newenv) with p: self.assertIn(b"physalis", p.stdout.read()) + p.communicate() + self.assertEqual(p.returncode, 0) def test_shell_encodings(self): # Run command through the shell (string) @@ -3669,6 +3901,8 @@ def test_shell_encodings(self): encoding=enc) with p: self.assertIn("physalis", p.stdout.read(), enc) + p.communicate() + self.assertEqual(p.returncode, 0) def test_call_string(self): # call() function with string argument on Windows @@ -3949,5 +4183,122 @@ def test_broken_pipe_cleanup(self): self.assertTrue(proc.stdin.closed) + +class FastWaitTestCase(BaseTestCase): + """Tests for efficient (pidfd_open() + poll() / kqueue()) process + waiting in subprocess.Popen.wait(). + """ + CAN_USE_PIDFD_OPEN = subprocess._CAN_USE_PIDFD_OPEN + CAN_USE_KQUEUE = subprocess._CAN_USE_KQUEUE + COMMAND = [sys.executable, "-c", "import time; time.sleep(0.3)"] + WAIT_TIMEOUT = 0.0001 # 0.1 ms + + def assert_fast_waitpid_error(self, patch_point): + # Emulate a case where pidfd_open() or kqueue() fails. + # Busy-poll wait should be used as fallback. + exc = OSError(errno.EMFILE, os.strerror(errno.EMFILE)) + with mock.patch(patch_point, side_effect=exc) as m: + p = subprocess.Popen(self.COMMAND) + with self.assertRaises(subprocess.TimeoutExpired): + p.wait(self.WAIT_TIMEOUT) + self.assertEqual(p.wait(timeout=support.SHORT_TIMEOUT), 0) + self.assertTrue(m.called) + + @unittest.skipIf(not CAN_USE_PIDFD_OPEN, reason="needs pidfd_open()") + def test_wait_pidfd_open_error(self): + self.assert_fast_waitpid_error("os.pidfd_open") + + @unittest.skipIf(not CAN_USE_KQUEUE, reason="needs kqueue() for proc") + def test_wait_kqueue_error(self): + self.assert_fast_waitpid_error("select.kqueue") + + @unittest.skipIf(not CAN_USE_KQUEUE, reason="needs kqueue() for proc") + def test_kqueue_control_error(self): + # Emulate a case where kqueue.control() fails. Busy-poll wait + # should be used as fallback. + p = subprocess.Popen(self.COMMAND) + kq_mock = mock.Mock() + kq_mock.control.side_effect = OSError( + errno.EPERM, os.strerror(errno.EPERM) + ) + kq_mock.close = mock.Mock() + + with mock.patch("select.kqueue", return_value=kq_mock) as m: + with self.assertRaises(subprocess.TimeoutExpired): + p.wait(self.WAIT_TIMEOUT) + self.assertEqual(p.wait(timeout=support.SHORT_TIMEOUT), 0) + self.assertTrue(m.called) + + def assert_wait_race_condition(self, patch_target, real_func): + # Call pidfd_open() / kqueue(), then terminate the process. + # Make sure that the wait call (poll() / kqueue.control()) + # still works for a terminated PID. + p = subprocess.Popen(self.COMMAND) + + def wrapper(*args, **kwargs): + ret = real_func(*args, **kwargs) + try: + os.kill(p.pid, signal.SIGTERM) + os.waitpid(p.pid, 0) + except OSError: + pass + return ret + + with mock.patch(patch_target, side_effect=wrapper) as m: + status = p.wait(timeout=support.SHORT_TIMEOUT) + self.assertTrue(m.called) + self.assertEqual(status, 0) + + @unittest.skipIf(not CAN_USE_PIDFD_OPEN, reason="needs pidfd_open()") + def test_pidfd_open_race(self): + self.assert_wait_race_condition("os.pidfd_open", os.pidfd_open) + + @unittest.skipIf(not CAN_USE_KQUEUE, reason="needs kqueue() for proc") + def test_kqueue_race(self): + self.assert_wait_race_condition("select.kqueue", select.kqueue) + + def assert_notification_without_immediate_reap(self, patch_target): + # Verify fallback to busy polling when poll() / kqueue() + # succeeds, but waitpid(pid, WNOHANG) returns (0, 0). + def waitpid_wrapper(pid, flags): + nonlocal ncalls + ncalls += 1 + if ncalls == 1: + return (0, 0) + return real_waitpid(pid, flags) + + ncalls = 0 + real_waitpid = os.waitpid + with mock.patch.object(subprocess.Popen, patch_target, return_value=True) as m1: + with mock.patch("os.waitpid", side_effect=waitpid_wrapper) as m2: + p = subprocess.Popen(self.COMMAND) + with self.assertRaises(subprocess.TimeoutExpired): + p.wait(self.WAIT_TIMEOUT) + self.assertEqual(p.wait(timeout=support.SHORT_TIMEOUT), 0) + self.assertTrue(m1.called) + self.assertTrue(m2.called) + + @unittest.skipIf(not CAN_USE_PIDFD_OPEN, reason="needs pidfd_open()") + def test_pidfd_open_notification_without_immediate_reap(self): + self.assert_notification_without_immediate_reap("_wait_pidfd") + + @unittest.skipIf(not CAN_USE_KQUEUE, reason="needs kqueue() for proc") + def test_kqueue_notification_without_immediate_reap(self): + self.assert_notification_without_immediate_reap("_wait_kqueue") + + @unittest.skipUnless( + CAN_USE_PIDFD_OPEN or CAN_USE_KQUEUE, + "fast wait mechanism not available" + ) + def test_fast_path_avoid_busy_loop(self): + # assert that the busy loop is not called as long as the fast + # wait is available + with mock.patch('time.sleep') as m: + p = subprocess.Popen(self.COMMAND) + with self.assertRaises(subprocess.TimeoutExpired): + p.wait(self.WAIT_TIMEOUT) + self.assertEqual(p.wait(timeout=support.LONG_TIMEOUT), 0) + self.assertFalse(m.called) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 9ec382afb65fe4..d556f96bc532ed 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -96,9 +96,15 @@ def test_get_attribute(self): self.test_get_attribute) self.assertRaises(unittest.SkipTest, support.get_attribute, self, "foo") - @unittest.skip("failing buildbots") + @unittest.skipIf(support.is_android or support.is_apple_mobile, + 'Mobile platforms redirect stdout to system log') def test_get_original_stdout(self): - self.assertEqual(support.get_original_stdout(), sys.stdout) + if isinstance(sys.stdout, io.StringIO): + # gh-55258: When --junit-xml is used, stdout is a StringIO: + # use sys.__stdout__ in this case. + self.assertEqual(support.get_original_stdout(), sys.__stdout__) + else: + self.assertEqual(support.get_original_stdout(), sys.stdout) def test_unload(self): import sched # noqa: F401 @@ -485,6 +491,7 @@ def test_check__all__(self): self.assertRaises(AssertionError, support.check__all__, self, unittest) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG'), 'need os.waitpid() and os.WNOHANG') @support.requires_fork() @@ -562,16 +569,28 @@ def test_args_from_interpreter_flags(self): # -X options ['-X', 'dev'], ['-Wignore', '-X', 'dev'], + ['-X', 'cpu_count=4'], + ['-X', 'disable-remote-debug'], ['-X', 'faulthandler'], ['-X', 'importtime'], ['-X', 'importtime=2'], + ['-X', 'int_max_str_digits=1000'], + ['-X', 'lazy_imports=all'], + ['-X', 'no_debug_ranges'], ['-X', 'showrefcount'], ['-X', 'tracemalloc'], ['-X', 'tracemalloc=3'], + ['-X', 'warn_default_encoding'], ): with self.subTest(opts=opts): self.check_options(opts, 'args_from_interpreter_flags') + with os_helper.temp_dir() as temp_path: + prefix = os.path.join(temp_path, 'pycache') + opts = ['-X', f'pycache_prefix={prefix}'] + with self.subTest(opts=opts): + self.check_options(opts, 'args_from_interpreter_flags') + self.check_options(['-I', '-E', '-s', '-P'], 'args_from_interpreter_flags', ['-I']) @@ -665,6 +684,7 @@ def test_recursive(depth, limit): """) script_helper.assert_python_ok("-c", code) + @support.skip_if_unlimited_stack_size def test_recursion(self): # Test infinite_recursion() and get_recursion_available() functions. def recursive_function(depth): @@ -780,6 +800,7 @@ def test_get_signal_name(self): (128 + int(signal.SIGABRT), 'SIGABRT'), (3221225477, "STATUS_ACCESS_VIOLATION"), (0xC00000FD, "STATUS_STACK_OVERFLOW"), + (0xC0000906, "0xC0000906"), ): self.assertEqual(support.get_signal_name(exitcode), expected, exitcode) @@ -791,10 +812,10 @@ def test_linked_to_musl(self): self.assertTrue(linked) # The value is cached, so make sure it returns the same value again. self.assertIs(linked, support.linked_to_musl()) - # The unlike libc, the musl version is a triple. + # The musl version is either triple or just a major version number. if linked: self.assertIsInstance(linked, tuple) - self.assertEqual(3, len(linked)) + self.assertIn(len(linked), (1, 3)) for v in linked: self.assertIsInstance(v, int) @@ -865,22 +886,22 @@ def try_import_attribute(self, fullname, default=None): return default def fetch_hash_function(self, name, implementation): - info = hashlib_helper.get_hash_info(name) - match implementation: - case "hashlib": - assert info.hashlib is not None, info - return getattr(self.hashlib, info.hashlib) - case "openssl": - try: - return getattr(self._hashlib, info.openssl, None) - except TypeError: - return None - fullname = info.fullname(implementation) + info = hashlib_helper.get_hash_func_info(name) + match hashlib_helper.Implementation(implementation): + case hashlib_helper.Implementation.hashlib: + method_name = info.hashlib.member_name + assert isinstance(method_name, str), method_name + return getattr(self.hashlib, method_name) + case hashlib_helper.Implementation.openssl: + method_name = info.openssl.member_name + assert isinstance(method_name, str | None), method_name + return getattr(self._hashlib, method_name or "", None) + fullname = info[implementation].fullname return self.try_import_attribute(fullname) def fetch_hmac_function(self, name): - fullname = hashlib_helper._EXPLICIT_HMAC_CONSTRUCTORS[name] - return self.try_import_attribute(fullname) + target = hashlib_helper.get_hmac_item_info(name) + return target.import_member() def check_openssl_hash(self, name, *, disabled=True): """Check that OpenSSL HASH interface is enabled/disabled.""" diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index 26c75fcbc2bd7f..8c03420c4c5e4b 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -2,9 +2,8 @@ Test the API of the symtable module. """ -import re -import textwrap import symtable +import warnings import unittest from test import support @@ -254,6 +253,7 @@ def test_function_info(self): self.assertEqual(sorted(func.get_locals()), expected) self.assertEqual(sorted(func.get_globals()), ["bar", "glob", "some_assigned_global_var"]) self.assertEqual(self.internal.get_frees(), ("x",)) + self.assertEqual(self.spam.get_cells(), ("some_var", "x",)) def test_globals(self): self.assertTrue(self.spam.lookup("glob").is_global()) @@ -283,6 +283,9 @@ def test_local(self): def test_free(self): self.assertTrue(self.internal.lookup("x").is_free()) + def test_cells(self): + self.assertTrue(self.spam.lookup("x").is_cell()) + def test_referenced(self): self.assertTrue(self.internal.lookup("x").is_referenced()) self.assertTrue(self.spam.lookup("internal").is_referenced()) @@ -359,87 +362,6 @@ def test_name(self): self.assertEqual(self.spam.lookup("x").get_name(), "x") self.assertEqual(self.Mine.get_name(), "Mine") - def test_class_get_methods(self): - deprecation_mess = ( - re.escape('symtable.Class.get_methods() is deprecated ' - 'and will be removed in Python 3.16.') - ) - - with self.assertWarnsRegex(DeprecationWarning, deprecation_mess): - self.assertEqual(self.Mine.get_methods(), ('a_method',)) - - top = symtable.symtable(TEST_COMPLEX_CLASS_CODE, "?", "exec") - this = find_block(top, "ComplexClass") - - with self.assertWarnsRegex(DeprecationWarning, deprecation_mess): - self.assertEqual(this.get_methods(), ( - 'a_method', 'a_method_pep_695', - 'an_async_method', 'an_async_method_pep_695', - 'a_classmethod', 'a_classmethod_pep_695', - 'an_async_classmethod', 'an_async_classmethod_pep_695', - 'a_staticmethod', 'a_staticmethod_pep_695', - 'an_async_staticmethod', 'an_async_staticmethod_pep_695', - 'a_fakemethod', 'a_fakemethod_pep_695', - 'an_async_fakemethod', 'an_async_fakemethod_pep_695', - 'glob_unassigned_meth', 'glob_unassigned_meth_pep_695', - 'glob_unassigned_async_meth', 'glob_unassigned_async_meth_pep_695', - 'glob_assigned_meth', 'glob_assigned_meth_pep_695', - 'glob_assigned_async_meth', 'glob_assigned_async_meth_pep_695', - )) - - # Test generator expressions that are of type TYPE_FUNCTION - # but will not be reported by get_methods() since they are - # not functions per se. - # - # Other kind of comprehensions such as list, set or dict - # expressions do not have the TYPE_FUNCTION type. - - def check_body(body, expected_methods): - indented = textwrap.indent(body, ' ' * 4) - top = symtable.symtable(f"class A:\n{indented}", "?", "exec") - this = find_block(top, "A") - with self.assertWarnsRegex(DeprecationWarning, deprecation_mess): - self.assertEqual(this.get_methods(), expected_methods) - - # statements with 'genexpr' inside it - GENEXPRS = ( - 'x = (x for x in [])', - 'x = (x async for x in [])', - 'type x[genexpr = (x for x in [])] = (x for x in [])', - 'type x[genexpr = (x async for x in [])] = (x async for x in [])', - 'genexpr = (x for x in [])', - 'genexpr = (x async for x in [])', - 'type genexpr[genexpr = (x for x in [])] = (x for x in [])', - 'type genexpr[genexpr = (x async for x in [])] = (x async for x in [])', - ) - - for gen in GENEXPRS: - # test generator expression - with self.subTest(gen=gen): - check_body(gen, ()) - - # test generator expression + variable named 'genexpr' - with self.subTest(gen=gen, isvar=True): - check_body('\n'.join((gen, 'genexpr = 1')), ()) - check_body('\n'.join(('genexpr = 1', gen)), ()) - - for paramlist in ('()', '(x)', '(x, y)', '(z: T)'): - for func in ( - f'def genexpr{paramlist}:pass', - f'async def genexpr{paramlist}:pass', - f'def genexpr[T]{paramlist}:pass', - f'async def genexpr[T]{paramlist}:pass', - ): - with self.subTest(func=func): - # test function named 'genexpr' - check_body(func, ('genexpr',)) - - for gen in GENEXPRS: - with self.subTest(gen=gen, func=func): - # test generator expression + function named 'genexpr' - check_body('\n'.join((gen, func)), ('genexpr',)) - check_body('\n'.join((func, gen)), ('genexpr',)) - def test_filename_correct(self): ### Bug tickler: SyntaxError file name correct whether error raised ### while parsing or building symbol table. @@ -579,6 +501,37 @@ def test_nested_genexpr(self): self.assertEqual(sorted(st.get_identifiers()), [".0", "y"]) self.assertEqual(st.get_children(), []) + def test__symtable_refleak(self): + # Regression test for reference leak in PyUnicode_FSDecoder. + # See https://github.com/python/cpython/issues/139748. + mortal_str = 'this is a mortal string' + # check error path when 'compile_type' AC conversion failed + self.assertRaises(TypeError, symtable.symtable, '', mortal_str, 1) + + def test_filter_syntax_warnings_by_module(self): + filename = support.findfile('test_import/data/syntax_warnings.py') + with open(filename, 'rb') as f: + source = f.read() + module_re = r'test\.test_import\.data\.syntax_warnings\z' + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=module_re) + symtable.symtable(source, filename, 'exec') + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10]) + for wm in wlog: + self.assertEqual(wm.filename, filename) + self.assertIs(wm.category, SyntaxWarning) + + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=r'package\.module\z') + warnings.filterwarnings('error', module=module_re) + symtable.symtable(source, filename, 'exec', module='package.module') + self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10]) + for wm in wlog: + self.assertEqual(wm.filename, filename) + self.assertIs(wm.category, SyntaxWarning) + class ComprehensionTests(unittest.TestCase): def get_identifiers_recursive(self, st, res): diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index c52d24219410c2..a3d485c998ac91 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -371,16 +371,46 @@ SyntaxError: invalid syntax >>> match ...: -... case {**rest, "key": value}: +... case {**_}: ... ... Traceback (most recent call last): SyntaxError: invalid syntax ->>> match ...: -... case {**_}: -... ... +# Check incorrect "case" placement with specialized error messages + +>>> case "pattern": ... Traceback (most recent call last): -SyntaxError: invalid syntax +SyntaxError: case statement must be inside match statement + +>>> case 1 | 2: ... +Traceback (most recent call last): +SyntaxError: case statement must be inside match statement + +>>> case klass(attr=1) | {}: ... +Traceback (most recent call last): +SyntaxError: case statement must be inside match statement + +>>> case [] if x > 1: ... +Traceback (most recent call last): +SyntaxError: case statement must be inside match statement + +>>> case match: ... +Traceback (most recent call last): +SyntaxError: case statement must be inside match statement + +>>> case case: ... +Traceback (most recent call last): +SyntaxError: case statement must be inside match statement + +>>> if some: +... case 1: ... +Traceback (most recent call last): +SyntaxError: case statement must be inside match statement + +>>> case some: +... case 1: ... +Traceback (most recent call last): +SyntaxError: case statement must be inside match statement # But prefixes of soft keywords should # still raise specialized errors @@ -2031,6 +2061,30 @@ Traceback (most recent call last): SyntaxError: trailing comma not allowed without surrounding parentheses +>>> with item,: pass +Traceback (most recent call last): +SyntaxError: the last 'with' item has a trailing comma + +>>> with item as x,: pass +Traceback (most recent call last): +SyntaxError: the last 'with' item has a trailing comma + +>>> with item1, item2,: pass +Traceback (most recent call last): +SyntaxError: the last 'with' item has a trailing comma + +>>> with item1 as x, item2,: pass +Traceback (most recent call last): +SyntaxError: the last 'with' item has a trailing comma + +>>> with item1 as x, item2 as y,: pass +Traceback (most recent call last): +SyntaxError: the last 'with' item has a trailing comma + +>>> with item1, item2 as y,: pass +Traceback (most recent call last): +SyntaxError: the last 'with' item has a trailing comma + >>> import a from b Traceback (most recent call last): SyntaxError: Did you mean to use 'from ... import ...' instead? @@ -2133,6 +2187,25 @@ Traceback (most recent call last): SyntaxError: cannot use subscript as import target +# Check that we don't raise a "cannot use name as import target" error +# if there is an error in an unrelated statement after ';' + +>>> import a as b; None = 1 +Traceback (most recent call last): +SyntaxError: cannot assign to None + +>>> import a, b as c; d = 1; None = 1 +Traceback (most recent call last): +SyntaxError: cannot assign to None + +>>> from a import b as c; None = 1 +Traceback (most recent call last): +SyntaxError: cannot assign to None + +>>> from a import b, c as d; e = 1; None = 1 +Traceback (most recent call last): +SyntaxError: cannot assign to None + # Check that we dont raise the "trailing comma" error if there is more # input to the left of the valid part that we parsed. @@ -2240,7 +2313,7 @@ Traceback (most recent call last): SyntaxError: invalid character '£' (U+00A3) - Invalid pattern matching constructs: +Invalid pattern matching constructs: >>> match ...: ... case 42 as _: @@ -2296,12 +2369,36 @@ Traceback (most recent call last): SyntaxError: positional patterns follow keyword patterns + >>> match ...: + ... case Foo(y=1, x=2, y=3): + ... ... + Traceback (most recent call last): + SyntaxError: attribute name repeated in class pattern: y + >>> match ...: ... case C(a=b, c, d=e, f, g=h, i, j=k, ...): ... ... Traceback (most recent call last): SyntaxError: positional patterns follow keyword patterns + >>> match ...: + ... case {**double_star, "spam": "eggs"}: + ... ... + Traceback (most recent call last): + SyntaxError: double star pattern must be the last (right-most) subpattern in the mapping pattern + + >>> match ...: + ... case {"foo": 1, **double_star, "spam": "eggs"}: + ... ... + Traceback (most recent call last): + SyntaxError: double star pattern must be the last (right-most) subpattern in the mapping pattern + + >>> match ...: + ... case {"spam": "eggs", "b": {**d, "ham": "bacon"}}: + ... ... + Traceback (most recent call last): + SyntaxError: double star pattern must be the last (right-most) subpattern in the mapping pattern + Uses of the star operator which should fail: A[:*b] @@ -2686,6 +2783,76 @@ def f(x: *b) >>> f(x = 5, *:) Traceback (most recent call last): SyntaxError: Invalid star expression + +Asserts: + + >>> assert (a := 1) # ok + >>> assert 1, (a := 1) # ok + + >>> assert a := 1 + Traceback (most recent call last): + SyntaxError: cannot use named expression without parentheses here + + >>> assert 1, a := 1 + Traceback (most recent call last): + SyntaxError: cannot use named expression without parentheses here + + >>> assert 1 = 2 = 3 + Traceback (most recent call last): + SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? + + >>> assert 1 = 2 + Traceback (most recent call last): + SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? + + >>> assert (1 = 2) + Traceback (most recent call last): + SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? + + >>> assert 'a' = a + Traceback (most recent call last): + SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? + + >>> assert x[0] = 1 + Traceback (most recent call last): + SyntaxError: cannot assign to subscript here. Maybe you meant '==' instead of '='? + + >>> assert (yield a) = 2 + Traceback (most recent call last): + SyntaxError: cannot assign to yield expression here. Maybe you meant '==' instead of '='? + + >>> assert a = 2 + Traceback (most recent call last): + SyntaxError: cannot assign to name here. Maybe you meant '==' instead of '='? + + >>> assert (a = 2) + Traceback (most recent call last): + SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='? + + >>> assert a = b + Traceback (most recent call last): + SyntaxError: cannot assign to name here. Maybe you meant '==' instead of '='? + + >>> assert 1, 1 = b + Traceback (most recent call last): + SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? + + >>> assert 1, (1 = b) + Traceback (most recent call last): + SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? + + >>> assert 1, a = 1 + Traceback (most recent call last): + SyntaxError: cannot assign to name here. Maybe you meant '==' instead of '='? + + >>> assert 1, (a = 1) + Traceback (most recent call last): + SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='? + + >>> assert 1 = a, a = 1 + Traceback (most recent call last): + SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? + """ import re @@ -3199,6 +3366,20 @@ def test_multiline_compiler_error_points_to_the_end(self): lineno=3 ) + def test_multiline_string_concat_missing_comma_points_to_last_string(self): + # gh-142236: For multi-line string concatenations with a missing comma, + # the error should point to the last string, not the first. + self._check_error( + "print(\n" + ' "line1"\n' + ' "line2"\n' + ' "line3"\n' + " x=1\n" + ")", + "Perhaps you forgot a comma", + lineno=4, # Points to "line3", the last string + ) + @support.cpython_only def test_syntax_error_on_deeply_nested_blocks(self): # This raises a SyntaxError, it used to raise a SystemError. Context @@ -3324,6 +3505,128 @@ def test_ifexp_body_stmt_else_stmt(self): ]: self._check_error(f"x = {lhs_stmt} if 1 else {rhs_stmt}", msg) + +class LazyImportRestrictionTestCase(SyntaxErrorTestCase): + """Test syntax restrictions for lazy imports.""" + + def test_lazy_import_in_try_block(self): + """Test that lazy imports are not allowed inside try blocks.""" + self._check_error("""\ +try: + lazy import os +except: + pass +""", "lazy import not allowed inside try/except blocks") + + self._check_error("""\ +try: + lazy from sys import path +except ImportError: + pass +""", "lazy from ... import not allowed inside try/except blocks") + + def test_lazy_import_in_trystar_block(self): + """Test that lazy imports are not allowed inside try* blocks.""" + self._check_error("""\ +try: + lazy import json +except* Exception: + pass +""", "lazy import not allowed inside try/except blocks") + + self._check_error("""\ +try: + lazy from collections import defaultdict +except* ImportError: + pass +""", "lazy from ... import not allowed inside try/except blocks") + + def test_lazy_import_in_except_block(self): + """Test that lazy imports are not allowed inside except blocks.""" + self._check_error("""\ +try: + sys.modules # trigger the except block +except* Exception: + lazy import sys +""", "lazy import not allowed inside try/except blocks") + + def test_lazy_import_in_function(self): + """Test that lazy imports are not allowed inside functions.""" + self._check_error("""\ +def func(): + lazy import math +""", "lazy import not allowed inside functions") + + self._check_error("""\ +def func(): + lazy from datetime import datetime +""", "lazy from ... import not allowed inside functions") + + def test_lazy_import_in_async_function(self): + """Test that lazy imports are not allowed inside async functions.""" + self._check_error("""\ +async def async_func(): + lazy import asyncio +""", "lazy import not allowed inside functions") + + self._check_error("""\ +async def async_func(): + lazy from json import loads +""", "lazy from ... import not allowed inside functions") + + def test_lazy_import_in_class(self): + """Test that lazy imports are not allowed inside classes.""" + self._check_error("""\ +class MyClass: + lazy import typing +""", "lazy import not allowed inside classes") + + self._check_error("""\ +class MyClass: + lazy from abc import ABC +""", "lazy from ... import not allowed inside classes") + + def test_lazy_import_star_forbidden(self): + """Test that 'lazy from ... import *' is forbidden everywhere.""" + # At module level should also be forbidden + self._check_error("lazy from os import *", + "lazy from ... import \\* is not allowed") + + # Inside function should give lazy function error first + self._check_error("""\ +def func(): + lazy from sys import * +""", "lazy from ... import not allowed inside functions") + + def test_lazy_import_nested_scopes(self): + """Test lazy imports in nested scopes.""" + self._check_error("""\ +class Outer: + def method(self): + lazy import sys +""", "lazy import not allowed inside functions") + + self._check_error("""\ +def outer(): + class Inner: + lazy import json +""", "lazy import not allowed inside classes") + + self._check_error("""\ +def outer(): + def inner(): + lazy from collections import deque +""", "lazy from ... import not allowed inside functions") + + def test_lazy_import_valid_cases(self): + """Test that lazy imports work at module level.""" + # These should compile without errors + compile("lazy import os", "<test>", "exec") + compile("lazy from sys import path", "<test>", "exec") + compile("lazy import json as j", "<test>", "exec") + compile("lazy from datetime import datetime as dt", "<test>", "exec") + + def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite()) return tests diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 486bf10a0b5647..02c70403185f60 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1,6 +1,5 @@ import builtins import codecs -import _datetime import gc import io import locale @@ -494,7 +493,7 @@ def test_getframemodulename(self): self.assertIs(f, f2) self.assertIsNone(sys._getframemodulename(i)) - # sys._current_frames() is a CPython-only gimmick. + @support.cpython_only # sys._current_frames() is a CPython-only gimmick. @threading_helper.reap_threads @threading_helper.requires_working_threading() def test_current_frames(self): @@ -730,14 +729,24 @@ def test_thread_info(self): self.assertEqual(len(info), 3) self.assertIn(info.name, ('nt', 'pthread', 'pthread-stubs', 'solaris', None)) self.assertIn(info.lock, ('pymutex', None)) - if sys.platform.startswith(("linux", "android", "freebsd")): + if sys.platform.startswith(("linux", "android", "freebsd", "wasi")): self.assertEqual(info.name, "pthread") elif sys.platform == "win32": self.assertEqual(info.name, "nt") elif sys.platform == "emscripten": self.assertIn(info.name, {"pthread", "pthread-stubs"}) - elif sys.platform == "wasi": - self.assertEqual(info.name, "pthread-stubs") + + def test_abi_info(self): + info = sys.abi_info + info_keys = {'pointer_bits', 'free_threaded', 'debug', 'byteorder'} + self.assertEqual(set(vars(info)), info_keys) + pointer_bits = 64 if sys.maxsize > 2**32 else 32 + self.assertEqual(info.pointer_bits, pointer_bits) + self.assertEqual(info.free_threaded, + bool(sysconfig.get_config_var('Py_GIL_DISABLED'))) + self.assertEqual(info.debug, + bool(sysconfig.get_config_var('Py_DEBUG'))) + self.assertEqual(info.byteorder, sys.byteorder) @unittest.skipUnless(support.is_emscripten, "only available on Emscripten") def test_emscripten_info(self): @@ -849,23 +858,35 @@ def test_subinterp_intern_singleton(self): ''')) self.assertTrue(sys._is_interned(s)) - def test_sys_flags(self): + def test_sys_flags_indexable_attributes(self): self.assertTrue(sys.flags) - attrs = ("debug", + # We've stopped assigning sequence indices to new sys.flags attributes: + # https://github.com/python/cpython/issues/122575#issuecomment-2416497086 + indexable_attrs = ("debug", "inspect", "interactive", "optimize", "dont_write_bytecode", "no_user_site", "no_site", "ignore_environment", "verbose", "bytes_warning", "quiet", "hash_randomization", "isolated", "dev_mode", "utf8_mode", "warn_default_encoding", "safe_path", "int_max_str_digits") - for attr in attrs: + for attr_idx, attr in enumerate(indexable_attrs): self.assertHasAttr(sys.flags, attr) attr_type = bool if attr in ("dev_mode", "safe_path") else int self.assertEqual(type(getattr(sys.flags, attr)), attr_type, attr) + attr_value = getattr(sys.flags, attr) + self.assertEqual(sys.flags[attr_idx], attr_value, + msg=f"sys.flags .{attr} vs [{attr_idx}]") self.assertTrue(repr(sys.flags)) - self.assertEqual(len(sys.flags), len(attrs)) + self.assertEqual(len(sys.flags), 18, msg="Do not increase, see GH-122575") self.assertIn(sys.flags.utf8_mode, {0, 1, 2}) + def test_sys_flags_name_only_attributes(self): + # non-tuple sequence fields (name only sys.flags attributes) + self.assertIsInstance(sys.flags.gil, int|type(None)) + self.assertIsInstance(sys.flags.thread_inherit_context, int|type(None)) + self.assertIsInstance(sys.flags.context_aware_warnings, int|type(None)) + self.assertIsInstance(sys.flags.lazy_imports, int|type(None)) + def assert_raise_on_new_sys_type(self, sys_attr): # Users are intentionally prevented from creating new instances of # sys.flags, sys.version_info, and sys.getwindowsversion. @@ -1262,16 +1283,6 @@ def check(tracebacklimit, expected): def test_no_duplicates_in_meta_path(self): self.assertEqual(len(sys.meta_path), len(set(sys.meta_path))) - @unittest.skipUnless(hasattr(sys, "_enablelegacywindowsfsencoding"), - 'needs sys._enablelegacywindowsfsencoding()') - def test__enablelegacywindowsfsencoding(self): - code = ('import sys', - 'sys._enablelegacywindowsfsencoding()', - 'print(sys.getfilesystemencoding(), sys.getfilesystemencodeerrors())') - rc, out, err = assert_python_ok('-c', '; '.join(code)) - out = out.decode('ascii', 'replace').rstrip() - self.assertEqual(out, 'mbcs replace') - @support.requires_subprocess() def test_orig_argv(self): code = textwrap.dedent(''' @@ -1340,6 +1351,7 @@ def test_disable_gil_abi(self): @test.support.cpython_only +@test.support.force_not_colorized_test_class class UnraisableHookTest(unittest.TestCase): def test_original_unraisablehook(self): _testcapi = import_helper.import_module('_testcapi') @@ -1481,6 +1493,7 @@ def hook_func(args): def test_custom_unraisablehook_fail(self): _testcapi = import_helper.import_module('_testcapi') from _testcapi import err_writeunraisable + def hook_func(*args): raise Exception("hook_func failed") @@ -1570,7 +1583,7 @@ def test_objecttypes(self): samples = [b'', b'u'*100000] for sample in samples: x = bytearray(sample) - check(x, vsize('n2Pi') + x.__alloc__()) + check(x, vsize('n2PiP') + x.__alloc__()) # bytearray_iterator check(iter(bytearray()), size('nP')) # bytes @@ -1712,9 +1725,10 @@ def get_gen(): yield 1 check(int(PyLong_BASE**2), vsize('') + 3*self.longdigit) # module if support.Py_GIL_DISABLED: - check(unittest, size('PPPPPP')) + md_gil = '?' else: - check(unittest, size('PPPPP')) + md_gil = '' + check(unittest, size('PPPP?' + md_gil + 'NPPPPP')) # None check(None, size('')) # NotImplementedType @@ -1729,7 +1743,12 @@ def delx(self): del self.__x x = property(getx, setx, delx, "") check(x, size('5Pi')) # PyCapsule - check(_datetime.datetime_CAPI, size('6P')) + try: + import _datetime + except ModuleNotFoundError: + pass + else: + check(_datetime.datetime_CAPI, size('6P')) # rangeiterator check(iter(range(1)), size('3l')) check(iter(range(2**65)), size('3P')) @@ -1769,7 +1788,7 @@ def delx(self): del self.__x check((1,2,3), vsize('') + self.P + 3*self.P) # type # static type: PyTypeObject - fmt = 'P2nPI13Pl4Pn9Pn12PIPc' + fmt = 'P2nPI13Pl4Pn9Pn12PI2Pc' s = vsize(fmt) check(int, s) typeid = 'n' if support.Py_GIL_DISABLED else '' @@ -1863,7 +1882,10 @@ class S(set): check(S(), set(), '3P') class FS(frozenset): __slots__ = 'a', 'b', 'c' - check(FS(), frozenset(), '3P') + + class mytuple(tuple): + pass + check(FS([mytuple()]), frozenset([mytuple()]), '3P') from collections import OrderedDict class OD(OrderedDict): __slots__ = 'a', 'b', 'c' @@ -1876,7 +1898,7 @@ def test_pythontypes(self): check = self.check_sizeof # _ast.AST import _ast - check(_ast.AST(), size('P')) + check(_ast.Module(), size('3P')) try: raise TypeError except TypeError as e: @@ -1887,10 +1909,16 @@ def test_pythontypes(self): # symtable entry # XXX # sys.flags - # FIXME: The +3 is for the 'gil', 'thread_inherit_context' and - # 'context_aware_warnings' flags and will not be necessary once - # gh-122575 is fixed - check(sys.flags, vsize('') + self.P + self.P * (3 + len(sys.flags))) + # FIXME: The non_sequence_fields adjustment is for these flags: + # - 'gil' + # - 'thread_inherit_context' + # - 'context_aware_warnings' + # - 'lazy_imports' + # Not needing to increment this every time we add a new field + # per GH-122575 would be nice... + # Q: What is the actual point of this sys.flags C size derived from PyStructSequence_Field array assertion? + non_sequence_fields = 4 + check(sys.flags, vsize('') + self.P + self.P * (non_sequence_fields + len(sys.flags))) def test_asyncgen_hooks(self): old = sys.get_asyncgen_hooks() @@ -2239,9 +2267,10 @@ def frame_2_jit(expected: bool) -> None: def frame_3_jit() -> None: # JITs just before the last loop: - for i in range(_testinternalcapi.TIER2_THRESHOLD + 1): + # 1 extra iteration for tracing. + for i in range(_testinternalcapi.TIER2_THRESHOLD + 2): # Careful, doing this in the reverse order breaks tracing: - expected = {enabled} and i == _testinternalcapi.TIER2_THRESHOLD + expected = {enabled} and i >= _testinternalcapi.TIER2_THRESHOLD assert sys._jit.is_active() is expected frame_2_jit(expected) assert sys._jit.is_active() is expected diff --git a/Lib/test/test_sys_setprofile.py b/Lib/test/test_sys_setprofile.py index 345c022bd2374c..a22b728b569419 100644 --- a/Lib/test/test_sys_setprofile.py +++ b/Lib/test/test_sys_setprofile.py @@ -272,6 +272,8 @@ def g(p): self.check_events(g, [(1, 'call', g_ident, None), (2, 'call', f_ident, None), (2, 'return', f_ident, 0), + (2, 'call', f_ident, None), + (2, 'return', f_ident, None), (1, 'return', g_ident, None), ], check_args=True) diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index b3685a91c57ee7..a5e7bbbfad5ffc 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -360,6 +360,8 @@ class TraceTestCase(unittest.TestCase): # Disable gc collection when tracing, otherwise the # deallocators may be traced as well. def setUp(self): + if os.environ.get('PYTHON_UOPS_OPTIMIZE') == '0': + self.skipTest("Line tracing behavior differs when JIT optimizer is disabled") self.using_gc = gc.isenabled() gc.disable() self.addCleanup(sys.settrace, sys.gettrace()) @@ -664,21 +666,18 @@ async def f(): (-2, 'line'), (-1, 'line'), (-1, 'return'), - (1, 'exception'), (2, 'line'), (1, 'line'), (-1, 'call'), (-2, 'line'), (-1, 'line'), (-1, 'return'), - (1, 'exception'), (2, 'line'), (1, 'line'), (-1, 'call'), (-2, 'line'), (-1, 'line'), (-1, 'return'), - (1, 'exception'), (2, 'line'), (1, 'line'), (-1, 'call'), diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 4aaef5b142931e..e6f99581f0b7a6 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -7,6 +7,7 @@ import shutil import json import textwrap +from unittest.mock import patch from copy import copy from test import support @@ -20,7 +21,7 @@ ) from test.support.import_helper import import_module from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink, - change_cwd) + change_cwd, EnvironmentVarGuard) from test.support.venv import VirtualEnvironmentMixin import sysconfig @@ -247,19 +248,15 @@ def test_get_platform(self): self.assertIsInstance(actual_platform, str) self.assertTrue(actual_platform) - # windows XP, 32bits + # Windows os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Intel)]') - sys.platform = 'win32' - self.assertEqual(get_platform(), 'win32') - - # windows XP, amd64 - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Amd64)]') - sys.platform = 'win32' - self.assertEqual(get_platform(), 'win-amd64') + with patch('_sysconfig.get_platform', create=True, return_value='win32'): + self.assertEqual(get_platform(), 'win32') + with patch('_sysconfig.get_platform', create=True, return_value='win-amd64'): + self.assertEqual(get_platform(), 'win-amd64') + sys.platform = 'test-plaform' + with patch('_sysconfig.get_platform', create=True, return_value=None): + self.assertEqual(get_platform(), 'test-plaform') # macbook os.name = 'posix' @@ -352,6 +349,13 @@ def test_get_platform(self): self.assertEqual(get_platform(), 'macosx-10.4-%s' % arch) + for macver in range(11, 16): + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-fno-strict-overflow -Wsign-compare -Wunreachable-code' + '-arch arm64 -fno-common -dynamic -DNDEBUG -g -O3 -Wall') + get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = f"{macver}.0" + self.assertEqual(get_platform(), 'macosx-%d.0-arm64' % macver) + # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' @@ -367,10 +371,12 @@ def test_get_platform(self): sys.platform = 'android' get_config_vars()['ANDROID_API_LEVEL'] = 9 for machine, abi in { - 'x86_64': 'x86_64', - 'i686': 'x86', 'aarch64': 'arm64_v8a', + 'arm': 'armeabi_v7a', 'armv7l': 'armeabi_v7a', + 'armv8l': 'armeabi_v7a', + 'i686': 'x86', + 'x86_64': 'x86_64', }.items(): with self.subTest(machine): self._set_uname(('Linux', 'localhost', '3.18.91+', @@ -570,15 +576,23 @@ def test_linux_ext_suffix(self): expected_suffixes = 'x86_64-linux-gnu.so', 'x86_64-linux-musl.so' self.assertEndsWith(suffix, expected_suffixes) + @unittest.skipIf(sysconfig.get_config_var('PY_BUILTIN_HASHLIB_HASHES') is None, + 'PY_BUILTIN_HASHLIB_HASHES required for this test') + def test_PY_BUILTIN_HASHLIB_HASHES_in_vars(self): + vars = sysconfig.get_config_vars() + self.assertFalse(vars['PY_BUILTIN_HASHLIB_HASHES'].startswith('"')) + @unittest.skipUnless(sys.platform == 'android', 'Android-specific test') def test_android_ext_suffix(self): machine = platform.machine() suffix = sysconfig.get_config_var('EXT_SUFFIX') expected_triplet = { - "x86_64": "x86_64-linux-android", - "i686": "i686-linux-android", "aarch64": "aarch64-linux-android", + "arm": "arm-linux-androideabi", "armv7l": "arm-linux-androideabi", + "armv8l": "arm-linux-androideabi", + "i686": "i686-linux-android", + "x86_64": "x86_64-linux-android", }[machine] self.assertEndsWith(suffix, f"-{expected_triplet}.so") @@ -757,8 +771,13 @@ def test_parse_makefile(self): print("var3=42", file=makefile) print("var4=$/invalid", file=makefile) print("var5=dollar$$5", file=makefile) - print("var6=${var3}/lib/python3.5/config-$(VAR2)$(var5)" + print("var6=${var7}/lib/python3.5/config-$(VAR2)$(var5)" "-x86_64-linux-gnu", file=makefile) + print("var7=${var3}", file=makefile) + print("var8=$$(var3)", file=makefile) + print("var9=$(var10)(var3)", file=makefile) + print("var10=$$", file=makefile) + print("var11=$${ORIGIN}${var5}", file=makefile) vars = _parse_makefile(TESTFN) self.assertEqual(vars, { 'var1': 'ab42', @@ -767,6 +786,74 @@ def test_parse_makefile(self): 'var4': '$/invalid', 'var5': 'dollar$5', 'var6': '42/lib/python3.5/config-b42dollar$5-x86_64-linux-gnu', + 'var7': 42, + 'var8': '$(var3)', + 'var9': '$(var3)', + 'var10': '$', + 'var11': '${ORIGIN}dollar$5', + }) + + def _test_parse_makefile_recursion(self): + self.addCleanup(unlink, TESTFN) + with open(TESTFN, "w") as makefile: + print("var1=var1=$(var1)", file=makefile) + print("var2=var3=$(var3)", file=makefile) + print("var3=var2=$(var2)", file=makefile) + vars = _parse_makefile(TESTFN) + self.assertEqual(vars, { + 'var1': 'var1=', + 'var2': 'var3=var2=', + 'var3': 'var2=', + }) + + def test_parse_makefile_renamed_vars(self): + self.addCleanup(unlink, TESTFN) + with open(TESTFN, "w") as makefile: + print("var1=$(CFLAGS)", file=makefile) + print("PY_CFLAGS=-Wall $(CPPFLAGS)", file=makefile) + print("PY_LDFLAGS=-lm", file=makefile) + print("var2=$(LDFLAGS)", file=makefile) + print("var3=$(CPPFLAGS)", file=makefile) + with EnvironmentVarGuard() as env: + env.clear() + vars = _parse_makefile(TESTFN) + self.assertEqual(vars, { + 'var1': '-Wall', + 'CFLAGS': '-Wall', + 'PY_CFLAGS': '-Wall', + 'LDFLAGS': '-lm', + 'PY_LDFLAGS': '-lm', + 'var2': '-lm', + 'var3': '', + }) + + def test_parse_makefile_keep_unresolved(self): + self.addCleanup(unlink, TESTFN) + with open(TESTFN, "w") as makefile: + print("var1=value", file=makefile) + print("var2=$/", file=makefile) + print("var3=$/$(var1)", file=makefile) + print("var4=var5=$(var5)", file=makefile) + print("var5=$/$(var1)", file=makefile) + print("var6=$(var1)$/", file=makefile) + print("var7=var8=$(var8)", file=makefile) + print("var8=$(var1)$/", file=makefile) + vars = _parse_makefile(TESTFN) + self.assertEqual(vars, { + 'var1': 'value', + 'var2': '$/', + 'var3': '$/value', + 'var4': 'var5=$/value', + 'var5': '$/value', + 'var6': 'value$/', + 'var7': 'var8=value$/', + 'var8': 'value$/', + }) + vars = _parse_makefile(TESTFN, keep_unresolved=False) + self.assertEqual(vars, { + 'var1': 'value', + 'var4': 'var5=', + 'var7': 'var8=', }) diff --git a/Lib/test/test_tabnanny.py b/Lib/test/test_tabnanny.py index 30dcb3e3c4f4f9..e575aac037e917 100644 --- a/Lib/test/test_tabnanny.py +++ b/Lib/test/test_tabnanny.py @@ -3,7 +3,8 @@ Glossary: * errored : Whitespace related problems present in file. """ -from unittest import TestCase, mock + +from unittest import TestCase, main, mock import errno import os import tabnanny @@ -352,3 +353,17 @@ def test_double_verbose_mode(self): "offending line: '\\tprint(\"world\")'" ).strip() self.validate_cmd("-vv", path, stdout=stdout, partial=True) + + +class TestModule(TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(tabnanny, "__version__") + self.assertEqual(cm.filename, __file__) + + +if __name__ == "__main__": + main() diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 7055e1ed147a9e..62a262740a7efa 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -10,6 +10,7 @@ import re import warnings import stat +import time import unittest import unittest.mock @@ -55,6 +56,7 @@ def sha256sum(data): zstname = os.path.join(TEMPDIR, "testtar.tar.zst") tmpname = os.path.join(TEMPDIR, "tmp.tar") dotlessname = os.path.join(TEMPDIR, "testtar") +SPACE = b" " sha256_regtype = ( "e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce" @@ -840,10 +842,90 @@ def test_next_on_empty_tarfile(self): with tarfile.open(fileobj=fd, mode="r") as tf: self.assertEqual(tf.next(), None) + def _setup_symlink_to_target(self, temp_dirpath): + target_filepath = os.path.join(temp_dirpath, "target") + ustar_dirpath = os.path.join(temp_dirpath, "ustar") + hardlink_filepath = os.path.join(ustar_dirpath, "lnktype") + with open(target_filepath, "wb") as f: + f.write(b"target") + os.makedirs(ustar_dirpath) + os.symlink(target_filepath, hardlink_filepath) + return target_filepath, hardlink_filepath + + def _assert_on_file_content(self, filepath, digest): + with open(filepath, "rb") as f: + data = f.read() + self.assertEqual(sha256sum(data), digest) + + @unittest.skipUnless( + hasattr(os, "link"), "Missing hardlink implementation" + ) + @os_helper.skip_unless_symlink + def test_extract_hardlink_on_symlink(self): + """ + This test verifies that extracting a hardlink will not follow an + existing symlink after a FileExistsError on os.link. + """ + with os_helper.temp_dir() as DIR: + target_filepath, hardlink_filepath = self._setup_symlink_to_target(DIR) + with tarfile.open(tarname, encoding="iso8859-1") as tar: + tar.extract("ustar/regtype", DIR, filter="data") + tar.extract("ustar/lnktype", DIR, filter="data") + self._assert_on_file_content(target_filepath, sha256sum(b"target")) + self._assert_on_file_content(hardlink_filepath, sha256_regtype) + + @unittest.skipUnless( + hasattr(os, "link"), "Missing hardlink implementation" + ) + @os_helper.skip_unless_symlink + def test_extractall_hardlink_on_symlink(self): + """ + This test verifies that extracting a hardlink will not follow an + existing symlink after a FileExistsError on os.link. + """ + with os_helper.temp_dir() as DIR: + target_filepath, hardlink_filepath = self._setup_symlink_to_target(DIR) + with tarfile.open(tarname, encoding="iso8859-1") as tar: + tar.extractall( + DIR, members=["ustar/regtype", "ustar/lnktype"], filter="data", + ) + self._assert_on_file_content(target_filepath, sha256sum(b"target")) + self._assert_on_file_content(hardlink_filepath, sha256_regtype) + + +class GzipReadTestBase: + + def test_read_with_extra_field(self): + with open(self.tarname, 'rb') as f: + data = bytearray(f.read()) + flags = data[3] + self.assertEqual(flags, 8) + data[3] = flags | 4 + data[10:10] = b'\x05\x00extra' + with open(tmpname, 'wb') as f: + f.write(data) + print(self.mode) + with tarfile.open(tmpname, mode=self.mode): + pass + + def test_read_with_file_comment(self): + with open(self.tarname, 'rb') as f: + data = bytearray(f.read()) + flags = data[3] + self.assertEqual(flags, 8) + data[3] = flags | 16 + i = data.index(0, 10) + 1 + data[i:i] = b'comment\x00' + with open(tmpname, 'wb') as f: + f.write(data) + with tarfile.open(tmpname, mode=self.mode): + pass + + class MiscReadTest(MiscReadTestBase, unittest.TestCase): test_fail_comp = None -class GzipMiscReadTest(GzipTest, MiscReadTestBase, unittest.TestCase): +class GzipMiscReadTest(GzipTest, GzipReadTestBase, MiscReadTestBase, unittest.TestCase): pass class Bz2MiscReadTest(Bz2Test, MiscReadTestBase, unittest.TestCase): @@ -917,7 +999,7 @@ def test_compare_members(self): finally: tar1.close() -class GzipStreamReadTest(GzipTest, StreamReadTest): +class GzipStreamReadTest(GzipTest, GzipReadTestBase, StreamReadTest): pass class Bz2StreamReadTest(Bz2Test, StreamReadTest): @@ -1182,6 +1264,25 @@ def test_longname_directory(self): self.assertIsNotNone(tar.getmember(longdir)) self.assertIsNotNone(tar.getmember(longdir.removesuffix('/'))) + def test_longname_file_not_directory(self): + # Test reading a longname file and ensure it is not handled as a directory + # Issue #141707 + buf = io.BytesIO() + with tarfile.open(mode='w', fileobj=buf, format=self.format) as tar: + ti = tarfile.TarInfo() + ti.type = tarfile.AREGTYPE + ti.name = ('a' * 99) + '/' + ('b' * 3) + tar.addfile(ti) + + expected = {t.name: t.type for t in tar.getmembers()} + + buf.seek(0) + with tarfile.open(mode='r', fileobj=buf) as tar: + actual = {t.name: t.type for t in tar.getmembers()} + + self.assertEqual(expected, actual) + + class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase): subdir = "gnu" @@ -1241,6 +1342,27 @@ def _fs_supports_holes(): else: return False + def test_gnulong_dirname_strips_all_trailing_slashes(self): + # gh-149980: _proc_gnulong must normalize trailing slashes the same + # way _frombuf and _proc_builtin do (rstrip, not removesuffix), so + # a GNU long-name directory entry agrees with a short-name one. + long_name = "a" * 120 + "///" # > 100 bytes => GNUTYPE_LONGNAME + short_name = "b" * 20 + "///" + + buf = io.BytesIO() + with tarfile.open(fileobj=buf, mode="w", + format=tarfile.GNU_FORMAT) as tar: + for name in (short_name, long_name): + info = tarfile.TarInfo(name=name) + info.type = tarfile.DIRTYPE + tar.addfile(info) + + buf.seek(0) + with tarfile.open(fileobj=buf, mode="r") as tar: + names = [m.name for m in tar.getmembers()] + + self.assertEqual(names, ["b" * 20, "a" * 120]) + class PaxReadTest(LongnameTest, ReadTest, unittest.TestCase): @@ -1736,6 +1858,16 @@ def test_file_mode(self): finally: os.umask(original_umask) + def test_pathlike_name(self): + expected_name = os.path.abspath(tmpname) + tarpath = os_helper.FakePath(tmpname) + + for func in (tarfile.open, tarfile.TarFile.open): + with self.subTest(): + with func(tarpath, self.mode) as tar: + self.assertEqual(tar.name, expected_name) + os_helper.unlink(tmpname) + class GzipStreamWriteTest(GzipTest, StreamWriteTest): def test_source_directory_not_leaked(self): @@ -1747,6 +1879,19 @@ def test_source_directory_not_leaked(self): payload = pathlib.Path(tmpname).read_text(encoding='latin-1') assert os.path.dirname(tmpname) not in payload + def test_create_with_mtime(self): + tarfile.open(tmpname, self.mode, mtime=0).close() + with self.open(tmpname, 'r') as fobj: + fobj.read() + self.assertEqual(fobj.mtime, 0) + + def test_create_without_mtime(self): + before = int(time.time()) + tarfile.open(tmpname, self.mode).close() + after = int(time.time()) + with self.open(tmpname, 'r') as fobj: + fobj.read() + self.assertTrue(before <= fobj.mtime <= after) class Bz2StreamWriteTest(Bz2Test, StreamWriteTest): decompressor = bz2.BZ2Decompressor if bz2 else None @@ -2053,6 +2198,19 @@ def test_create_with_compresslevel(self): with tarfile.open(tmpname, 'r:gz', compresslevel=1) as tobj: pass + def test_create_with_mtime(self): + tarfile.open(tmpname, self.mode, mtime=0).close() + with self.open(tmpname, 'rb') as fobj: + fobj.read() + self.assertEqual(fobj.mtime, 0) + + def test_create_without_mtime(self): + before = int(time.time()) + tarfile.open(tmpname, self.mode).close() + after = int(time.time()) + with self.open(tmpname, 'r') as fobj: + fobj.read() + self.assertTrue(before <= fobj.mtime <= after) class Bz2CreateTest(Bz2Test, CreateTest): @@ -2715,7 +2873,7 @@ def test_useful_error_message_when_modules_missing(self): str(excinfo.exception), ) - @unittest.skipUnless(os_helper.can_symlink(), 'requires symlink support') + @os_helper.skip_unless_symlink @unittest.skipUnless(hasattr(os, 'chmod'), "missing os.chmod") @unittest.mock.patch('os.chmod') def test_deferred_directory_attributes_update(self, mock_chmod): @@ -3097,7 +3255,11 @@ def root_is_uid_gid_0(): import pwd, grp except ImportError: return False - if pwd.getpwuid(0)[0] != 'root': + try: + if pwd.getpwuid(0)[0] != 'root': + return False + except KeyError: + # On Cygwin, there is no root user (uid 0) return False if grp.getgrgid(0)[0] != 'root': return False @@ -3601,6 +3763,39 @@ class TestExtractionFilters(unittest.TestCase): # The destination for the extraction, within `outerdir` destdir = outerdir / 'dest' + @classmethod + def setUpClass(cls): + # Posix and Windows have different pathname resolution: + # either symlink or a '..' component resolve first. + # Let's see which we are on. + if os_helper.can_symlink(): + testpath = os.path.join(TEMPDIR, 'resolution_test') + os.mkdir(testpath) + + # testpath/current links to `.` which is all of: + # - `testpath` + # - `testpath/current` + # - `testpath/current/current` + # - etc. + os.symlink('.', os.path.join(testpath, 'current')) + + # we'll test where `testpath/current/../file` ends up + with open(os.path.join(testpath, 'current', '..', 'file'), 'w'): + pass + + if os.path.exists(os.path.join(testpath, 'file')): + # Windows collapses 'current\..' to '.' first, leaving + # 'testpath\file' + cls.dotdot_resolves_early = True + elif os.path.exists(os.path.join(testpath, '..', 'file')): + # Posix resolves 'current' to '.' first, leaving + # 'testpath/../file' + cls.dotdot_resolves_early = False + else: + raise AssertionError('Could not determine link resolution') + else: + cls.dotdot_resolves_early = False + @contextmanager def check_context(self, tar, filter, *, check_flag=True): """Extracts `tar` to `self.destdir` and allows checking the result @@ -3747,23 +3942,21 @@ def test_parent_symlink(self): arc.add('current', symlink_to='.') # effectively points to ./../ - arc.add('parent', symlink_to='current/..') + if self.dotdot_resolves_early and os_helper.can_symlink(): + arc.add('parent', symlink_to='current/../..') + else: + arc.add('parent', symlink_to='current/..') arc.add('parent/evil') if os_helper.can_symlink(): with self.check_context(arc.open(), 'fully_trusted'): - if self.raised_exception is not None: - # Windows will refuse to create a file that's a symlink to itself - # (and tarfile doesn't swallow that exception) - self.expect_exception(FileExistsError) - # The other cases will fail with this error too. - # Skip the rest of this test. - return + self.expect_file('current', symlink_to='.') + if self.dotdot_resolves_early: + self.expect_file('parent', symlink_to='current/../..') else: - self.expect_file('current', symlink_to='.') self.expect_file('parent', symlink_to='current/..') - self.expect_file('../evil') + self.expect_file('../evil') with self.check_context(arc.open(), 'tar'): self.expect_exception( @@ -3772,10 +3965,19 @@ def test_parent_symlink(self): + "which is outside the destination") with self.check_context(arc.open(), 'data'): - self.expect_exception( - tarfile.LinkOutsideDestinationError, - """'parent' would link to ['"].*outerdir['"], """ - + "which is outside the destination") + if self.dotdot_resolves_early: + # 'current/../..' normalises to '..', which is rejected. + self.expect_exception( + tarfile.LinkOutsideDestinationError, + """'parent' would link to ['"].*outerdir['"], """ + + "which is outside the destination") + else: + # 'current/..' normalises to '.'; the rewritten link is + # created and 'parent/evil' lands harmlessly inside the + # destination. + self.expect_file('current', symlink_to='.') + self.expect_file('parent', symlink_to='.') + self.expect_file('evil') else: # No symlink support. The symlinks are ignored. @@ -3837,6 +4039,9 @@ def test_realpath_limit_attack(self): check_flag=False)): if sys.platform == 'win32': self.expect_exception((FileNotFoundError, FileExistsError)) + elif sys.platform == 'cygwin': + exc = self.expect_exception(OSError) + self.assertEqual(exc.errno, errno.ELOOP) elif self.raised_exception: # Cannot symlink/hardlink: tarfile falls back to getmember() self.expect_exception(KeyError) @@ -3858,42 +4063,14 @@ def test_realpath_limit_attack(self): # 206: ERROR_FILENAME_EXCED_RANGE self.assertIn(exc.winerror, (3, 5, 206)) else: - self.assertEqual(exc.errno, errno.ENAMETOOLONG) + self.assertIn(exc.errno, + (errno.ENAMETOOLONG, errno.ELOOP)) @symlink_test def test_parent_symlink2(self): # Test interplaying symlinks # Inspired by 'dirsymlink2b' in jwilk/traversal-archives - # Posix and Windows have different pathname resolution: - # either symlink or a '..' component resolve first. - # Let's see which we are on. - if os_helper.can_symlink(): - testpath = os.path.join(TEMPDIR, 'resolution_test') - os.mkdir(testpath) - - # testpath/current links to `.` which is all of: - # - `testpath` - # - `testpath/current` - # - `testpath/current/current` - # - etc. - os.symlink('.', os.path.join(testpath, 'current')) - - # we'll test where `testpath/current/../file` ends up - with open(os.path.join(testpath, 'current', '..', 'file'), 'w'): - pass - - if os.path.exists(os.path.join(testpath, 'file')): - # Windows collapses 'current\..' to '.' first, leaving - # 'testpath\file' - dotdot_resolves_early = True - elif os.path.exists(os.path.join(testpath, '..', 'file')): - # Posix resolves 'current' to '.' first, leaving - # 'testpath/../file' - dotdot_resolves_early = False - else: - raise AssertionError('Could not determine link resolution') - with ArchiveMaker() as arc: # `current` links to `.` which is both the destination directory @@ -3929,7 +4106,7 @@ def test_parent_symlink2(self): with self.check_context(arc.open(), 'data'): if os_helper.can_symlink(): - if dotdot_resolves_early: + if self.dotdot_resolves_early: # Fail when extracting a file outside destination self.expect_exception( tarfile.OutsideDestinationError, @@ -3977,6 +4154,21 @@ def test_absolute_symlink(self): tarfile.AbsoluteLinkError, "'parent' is a link to an absolute path") + @symlink_test + @os_helper.skip_unless_symlink + def test_symlink_target_seperator_rewrite_on_windows(self): + with ArchiveMaker() as arc: + arc.add('link', symlink_to="relative/test/path") + + with self.check_context(arc.open(), 'fully_trusted'): + self.expect_file('link', type=tarfile.SYMTYPE) + link_path = os.path.normpath(self.destdir / "link") + link_target = os.readlink(link_path) + if os.name == "nt": + self.assertEqual(link_target, "relative\\test\\path") + else: + self.assertEqual(link_target, "relative/test/path") + def test_absolute_hardlink(self): # Test hardlink to an absolute path # Inspired by 'dirsymlink' in https://github.com/jwilk/traversal-archives @@ -4049,6 +4241,76 @@ def test_sly_relative2(self): + """['"].*moo['"], which is outside the """ + "destination") + @symlink_test + @os_helper.skip_unless_symlink + def test_normpath_realpath_mismatch(self): + # The link-target check must validate the value that will actually + # be written to disk (the normalised linkname), not the original. + # Here 'a' is a symlink to a deep nonexistent path, so realpath() + # of 'a/../../...' stays inside the destination while normpath() + # collapses 'a/..' lexically and escapes. + depth = len(self.destdir.parts) + 5 + deep = '/'.join(f'p{i}' for i in range(depth)) + sneaky = 'a/' + '../' * depth + 'flag' + for kind in 'symlink_to', 'hardlink_to': + with self.subTest(kind): + with ArchiveMaker() as arc: + arc.add('a', symlink_to=deep) + arc.add('escape', **{kind: sneaky}) + with self.check_context(arc.open(), 'data'): + self.expect_exception( + tarfile.LinkOutsideDestinationError) + + @symlink_test + @os_helper.skip_unless_symlink + def test_symlink_trailing_slash(self): + # A trailing slash on a symlink member's name must not cause the + # link target to be resolved relative to the wrong directory. + with ArchiveMaker() as arc: + t = tarfile.TarInfo('x/') + t.type = tarfile.SYMTYPE + t.linkname = '..' + arc.tar_w.addfile(t) + arc.add('x/escaped', content='hi') + + with self.check_context(arc.open(), 'data'): + self.expect_exception(tarfile.LinkOutsideDestinationError) + + @symlink_test + @os_helper.skip_unless_symlink + def test_link_at_destination(self): + # A link member whose name resolves to the destination directory + # itself must be rejected: otherwise the destination is replaced + # by a symlink and later members can be redirected through it. + for name in '', '.', './': + with ArchiveMaker() as arc: + t = tarfile.TarInfo(name) + t.type = tarfile.SYMTYPE + t.linkname = '.' + arc.tar_w.addfile(t) + + with self.check_context(arc.open(), 'data'): + self.expect_exception(tarfile.OutsideDestinationError) + + @symlink_test + @os_helper.skip_unless_symlink + def test_empty_name_symlink_chain(self): + # Regression test for a chain of empty-named symlinks that + # incrementally redirects the destination outwards. + with ArchiveMaker() as arc: + for name, target in [('', ''), ('a/', '..'), + ('', 'dummy'), ('', 'a'), + ('b/', '..'), + ('', 'dummy'), ('', 'a/b')]: + t = tarfile.TarInfo(name) + t.type = tarfile.SYMTYPE + t.linkname = target + arc.tar_w.addfile(t) + arc.add('escaped', content='hi') + + with self.check_context(arc.open(), 'data'): + self.expect_exception(tarfile.FilterError) + @symlink_test def test_deep_symlink(self): # Test that symlinks and hardlinks inside a directory @@ -4602,6 +4864,171 @@ def extractall(self, ar): ar.extractall(self.testdir, filter='fully_trusted') +class OffsetValidationTests(unittest.TestCase): + tarname = tmpname + invalid_posix_header = ( + # name: 100 bytes + tarfile.NUL * tarfile.LENGTH_NAME + # mode, space, null terminator: 8 bytes + + b"000755" + SPACE + tarfile.NUL + # uid, space, null terminator: 8 bytes + + b"000001" + SPACE + tarfile.NUL + # gid, space, null terminator: 8 bytes + + b"000001" + SPACE + tarfile.NUL + # size, space: 12 bytes + + b"\xff" * 11 + SPACE + # mtime, space: 12 bytes + + tarfile.NUL * 11 + SPACE + # chksum: 8 bytes + + b"0011407" + tarfile.NUL + # type: 1 byte + + tarfile.REGTYPE + # linkname: 100 bytes + + tarfile.NUL * tarfile.LENGTH_LINK + # magic: 6 bytes, version: 2 bytes + + tarfile.POSIX_MAGIC + # uname: 32 bytes + + tarfile.NUL * 32 + # gname: 32 bytes + + tarfile.NUL * 32 + # devmajor, space, null terminator: 8 bytes + + tarfile.NUL * 6 + SPACE + tarfile.NUL + # devminor, space, null terminator: 8 bytes + + tarfile.NUL * 6 + SPACE + tarfile.NUL + # prefix: 155 bytes + + tarfile.NUL * tarfile.LENGTH_PREFIX + # padding: 12 bytes + + tarfile.NUL * 12 + ) + invalid_gnu_header = ( + # name: 100 bytes + tarfile.NUL * tarfile.LENGTH_NAME + # mode, null terminator: 8 bytes + + b"0000755" + tarfile.NUL + # uid, null terminator: 8 bytes + + b"0000001" + tarfile.NUL + # gid, space, null terminator: 8 bytes + + b"0000001" + tarfile.NUL + # size, space: 12 bytes + + b"\xff" * 11 + SPACE + # mtime, space: 12 bytes + + tarfile.NUL * 11 + SPACE + # chksum: 8 bytes + + b"0011327" + tarfile.NUL + # type: 1 byte + + tarfile.REGTYPE + # linkname: 100 bytes + + tarfile.NUL * tarfile.LENGTH_LINK + # magic: 8 bytes + + tarfile.GNU_MAGIC + # uname: 32 bytes + + tarfile.NUL * 32 + # gname: 32 bytes + + tarfile.NUL * 32 + # devmajor, null terminator: 8 bytes + + tarfile.NUL * 8 + # devminor, null terminator: 8 bytes + + tarfile.NUL * 8 + # padding: 167 bytes + + tarfile.NUL * 167 + ) + invalid_v7_header = ( + # name: 100 bytes + tarfile.NUL * tarfile.LENGTH_NAME + # mode, space, null terminator: 8 bytes + + b"000755" + SPACE + tarfile.NUL + # uid, space, null terminator: 8 bytes + + b"000001" + SPACE + tarfile.NUL + # gid, space, null terminator: 8 bytes + + b"000001" + SPACE + tarfile.NUL + # size, space: 12 bytes + + b"\xff" * 11 + SPACE + # mtime, space: 12 bytes + + tarfile.NUL * 11 + SPACE + # chksum: 8 bytes + + b"0010070" + tarfile.NUL + # type: 1 byte + + tarfile.REGTYPE + # linkname: 100 bytes + + tarfile.NUL * tarfile.LENGTH_LINK + # padding: 255 bytes + + tarfile.NUL * 255 + ) + valid_gnu_header = tarfile.TarInfo("filename").tobuf(tarfile.GNU_FORMAT) + data_block = b"\xff" * tarfile.BLOCKSIZE + + def _write_buffer(self, buffer): + with open(self.tarname, "wb") as f: + f.write(buffer) + + def _get_members(self, ignore_zeros=None): + with open(self.tarname, "rb") as f: + with tarfile.open( + mode="r", fileobj=f, ignore_zeros=ignore_zeros + ) as tar: + return tar.getmembers() + + def _assert_raises_read_error_exception(self): + with self.assertRaisesRegex( + tarfile.ReadError, "file could not be opened successfully" + ): + self._get_members() + + def test_invalid_offset_header_validations(self): + for tar_format, invalid_header in ( + ("posix", self.invalid_posix_header), + ("gnu", self.invalid_gnu_header), + ("v7", self.invalid_v7_header), + ): + with self.subTest(format=tar_format): + self._write_buffer(invalid_header) + self._assert_raises_read_error_exception() + + def test_early_stop_at_invalid_offset_header(self): + buffer = self.valid_gnu_header + self.invalid_gnu_header + self.valid_gnu_header + self._write_buffer(buffer) + members = self._get_members() + self.assertEqual(len(members), 1) + self.assertEqual(members[0].name, "filename") + self.assertEqual(members[0].offset, 0) + + def test_ignore_invalid_archive(self): + # 3 invalid headers with their respective data + buffer = (self.invalid_gnu_header + self.data_block) * 3 + self._write_buffer(buffer) + members = self._get_members(ignore_zeros=True) + self.assertEqual(len(members), 0) + + def test_ignore_invalid_offset_headers(self): + for first_block, second_block, expected_offset in ( + ( + (self.valid_gnu_header), + (self.invalid_gnu_header + self.data_block), + 0, + ), + ( + (self.invalid_gnu_header + self.data_block), + (self.valid_gnu_header), + 1024, + ), + ): + self._write_buffer(first_block + second_block) + members = self._get_members(ignore_zeros=True) + self.assertEqual(len(members), 1) + self.assertEqual(members[0].name, "filename") + self.assertEqual(members[0].offset, expected_offset) + + +class TestModule(unittest.TestCase): + def test_deprecated_version(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'version' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(tarfile, "version") + self.assertEqual(cm.filename, __file__) + + def setUpModule(): os_helper.unlink(TEMPDIR) os.makedirs(TEMPDIR) diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py index d479f7d7515d9b..70731d3222ced9 100644 --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -40,6 +40,9 @@ def setUp(self): self.interp = Tcl() self.wantobjects = self.interp.tk.wantobjects() + def passValue(self, value): + return self.interp.call('set', '_', value) + def testEval(self): tcl = self.interp tcl.eval('set a 1') @@ -51,7 +54,11 @@ def test_eval_null_in_result(self): def test_eval_surrogates_in_result(self): tcl = self.interp - self.assertEqual(tcl.eval(r'set a "<\ud83d\udcbb>"'), '<\U0001f4bb>') + result = tcl.eval(r'set a "<\ud83d\udcbb>"') + if sys.platform == 'win32' and tcl_version >= (9, 0): + self.assertEqual('<\ud83d\udcbb>', result) + else: + self.assertEqual('<\U0001f4bb>', result) def testEvalException(self): tcl = self.interp @@ -286,7 +293,11 @@ def test_evalfile_surrogates_in_result(self): set b "<\\ud83d\\udcbb>" """) tcl.evalfile(filename) - self.assertEqual(tcl.eval('set b'), '<\U0001f4bb>') + result = tcl.eval('set b') + if sys.platform == 'win32' and tcl_version >= (9, 0): + self.assertEqual('<\ud83d\udcbb>', result) + else: + self.assertEqual('<\U0001f4bb>', result) def testEvalFileException(self): tcl = self.interp @@ -490,8 +501,7 @@ def test_expr_bignum(self): self.assertIsInstance(result, str) def test_passing_values(self): - def passValue(value): - return self.interp.call('set', '_', value) + passValue = self.passValue self.assertEqual(passValue(True), True if self.wantobjects else '1') self.assertEqual(passValue(False), False if self.wantobjects else '0') @@ -537,6 +547,24 @@ def passValue(value): self.assertEqual(passValue(['a', ['b', 'c']]), ('a', ('b', 'c')) if self.wantobjects else 'a {b c}') + def test_set_object_concurrent_mutation_in_sequence_conversion(self): + # Prevent SIGSEV when the object to convert is concurrently mutated. + # See: https://github.com/python/cpython/issues/143310. + + string = "value" + + class Value: + def __str__(self): + values.clear() + return string + + class List(list): + pass + + expect = (string, "pad") if self.wantobjects else f"{string} pad" + self.assertEqual(self.passValue(values := [Value(), "pad"]), expect) + self.assertEqual(self.passValue(values := List([Value(), "pad"])), expect) + def test_user_command(self): result = None def testfunc(arg): @@ -797,6 +825,10 @@ def test_huge_string_builtins2(self, size): def setUpModule(): + wantobjects = support.get_resource_value('wantobjects') + if wantobjects is not None: + unittest.enterModuleContext( + support.swap_attr(tkinter, 'wantobjects', int(wantobjects))) if support.verbose: tcl = Tcl() print('patchlevel =', tcl.call('info', 'patchlevel'), flush=True) diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index 52b13b98cbcce5..3b081ecd4a3aa5 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -330,17 +330,40 @@ def _mock_candidate_names(*names): class TestBadTempdir: def test_read_only_directory(self): with _inside_empty_temp_dir(): - oldmode = mode = os.stat(tempfile.tempdir).st_mode - mode &= ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) - os.chmod(tempfile.tempdir, mode) + probe = os.path.join(tempfile.tempdir, 'probe') + if os.name == 'nt': + # Use security identifier *S-1-1-0 instead + # of localized "Everyone" to not depend on the locale. + cmd = ['icacls', tempfile.tempdir, '/deny', '*S-1-1-0:(W)'] + stdout = None if support.verbose > 1 else subprocess.DEVNULL + subprocess.run(cmd, check=True, stdout=stdout) + else: + oldmode = mode = os.stat(tempfile.tempdir).st_mode + mode &= ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) + mode = stat.S_IREAD + os.chmod(tempfile.tempdir, mode) try: - if os.access(tempfile.tempdir, os.W_OK): + # Check that the directory is read-only. + try: + os.mkdir(probe) + except PermissionError: + pass + else: + os.rmdir(probe) self.skipTest("can't set the directory read-only") + # gh-66305: Now it takes a split second, but previously + # it took about 10 days on Windows. with self.assertRaises(PermissionError): self.make_temp() - self.assertEqual(os.listdir(tempfile.tempdir), []) finally: - os.chmod(tempfile.tempdir, oldmode) + if os.name == 'nt': + # Use security identifier *S-1-1-0 instead + # of localized "Everyone" to not depend on the locale. + cmd = ['icacls', tempfile.tempdir, '/grant:r', '*S-1-1-0:(M)'] + subprocess.run(cmd, check=True, stdout=stdout) + else: + os.chmod(tempfile.tempdir, oldmode) + self.assertEqual(os.listdir(tempfile.tempdir), []) def test_nonexisting_directory(self): with _inside_empty_temp_dir(): @@ -492,6 +515,8 @@ def test_noinherit(self): self.assertFalse(retval > 0, "child process reports failure %d"%retval) @unittest.skipUnless(has_textmode, "text mode not available") + @unittest.skipIf(sys.platform == "cygwin", + "truncate text mode is not supported on Cygwin") def test_textmode(self): # _mkstemp_inner can create files in text mode @@ -1386,7 +1411,7 @@ def test_properties(self): f.write(b'x') self.assertTrue(f._rolled) - self.assertEqual(f.mode, 'rb+') + self.assertEqual(f.mode, 'wb+') self.assertIsNotNone(f.name) with self.assertRaises(AttributeError): f.newlines diff --git a/Lib/test/test_termios.py b/Lib/test/test_termios.py index ce8392a6ccdbd6..3dcf5bd13791dc 100644 --- a/Lib/test/test_termios.py +++ b/Lib/test/test_termios.py @@ -1,3 +1,4 @@ +import contextlib import errno import os import sys @@ -11,6 +12,19 @@ termios = import_module('termios') +# Skip the test on ENOTTY error +@contextlib.contextmanager +def skip_enotty_error(testcase, func, platforms): + try: + yield + except termios.error as exc: + if (exc.args[0] == errno.ENOTTY + and sys.platform.startswith(platforms)): + testcase.skipTest(f'termios.{func}() is not supported ' + f'with pseudo-terminals (?) on {sys.platform}') + raise + + @unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()") class TestFunctions(unittest.TestCase): @@ -90,7 +104,8 @@ def test_tcsetattr_errors(self): self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, attrs2) self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW, object()) self.assertRaises(TypeError, termios.tcsetattr, self.fd, termios.TCSANOW) - self.assertRaisesTermiosError(errno.EINVAL, termios.tcsetattr, self.fd, -1, attrs) + if sys.platform != 'cygwin': + self.assertRaisesTermiosError(errno.EINVAL, termios.tcsetattr, self.fd, -1, attrs) self.assertRaises(OverflowError, termios.tcsetattr, self.fd, 2**1000, attrs) self.assertRaises(TypeError, termios.tcsetattr, self.fd, object(), attrs) self.assertRaisesTermiosError(errno.ENOTTY, termios.tcsetattr, self.bad_fd, termios.TCSANOW, attrs) @@ -101,14 +116,10 @@ def test_tcsetattr_errors(self): @support.skip_android_selinux('tcsendbreak') def test_tcsendbreak(self): - try: + with skip_enotty_error(self, 'tcsendbreak', + ('freebsd', 'netbsd', 'cygwin')): termios.tcsendbreak(self.fd, 1) - except termios.error as exc: - if exc.args[0] == errno.ENOTTY and sys.platform.startswith(('freebsd', "netbsd")): - self.skipTest('termios.tcsendbreak() is not supported ' - 'with pseudo-terminals (?) on this platform') - raise - termios.tcsendbreak(self.stream, 1) + termios.tcsendbreak(self.stream, 1) @support.skip_android_selinux('tcsendbreak') def test_tcsendbreak_errors(self): @@ -123,8 +134,9 @@ def test_tcsendbreak_errors(self): @support.skip_android_selinux('tcdrain') def test_tcdrain(self): - termios.tcdrain(self.fd) - termios.tcdrain(self.stream) + with skip_enotty_error(self, 'tcdrain', ('cygwin',)): + termios.tcdrain(self.fd) + termios.tcdrain(self.stream) @support.skip_android_selinux('tcdrain') def test_tcdrain_errors(self): @@ -149,6 +161,7 @@ def test_tcflush_errors(self): self.assertRaises(TypeError, termios.tcflush, object(), termios.TCIFLUSH) self.assertRaises(TypeError, termios.tcflush, self.fd) + @unittest.skipIf(sys.platform == 'cygwin', 'test fails on Cygwin') def test_tcflush_clear_input_or_output(self): wfd = self.fd rfd = self.master_fd @@ -176,14 +189,16 @@ def test_tcflush_clear_input_or_output(self): @support.skip_android_selinux('tcflow') def test_tcflow(self): - termios.tcflow(self.fd, termios.TCOOFF) - termios.tcflow(self.fd, termios.TCOON) - termios.tcflow(self.fd, termios.TCIOFF) - termios.tcflow(self.fd, termios.TCION) + with skip_enotty_error(self, 'tcflow', ('cygwin',)): + termios.tcflow(self.fd, termios.TCOOFF) + termios.tcflow(self.fd, termios.TCOON) + termios.tcflow(self.fd, termios.TCIOFF) + termios.tcflow(self.fd, termios.TCION) @support.skip_android_selinux('tcflow') def test_tcflow_errors(self): - self.assertRaisesTermiosError(errno.EINVAL, termios.tcflow, self.fd, -1) + if sys.platform != 'cygwin': + self.assertRaisesTermiosError(errno.EINVAL, termios.tcflow, self.fd, -1) self.assertRaises(OverflowError, termios.tcflow, self.fd, 2**1000) self.assertRaises(TypeError, termios.tcflow, self.fd, object()) self.assertRaisesTermiosError(errno.ENOTTY, termios.tcflow, self.bad_fd, termios.TCOON) diff --git a/Lib/test/test_textwrap.py b/Lib/test/test_textwrap.py index cbd383ea4e2656..aca1f427656bb5 100644 --- a/Lib/test/test_textwrap.py +++ b/Lib/test/test_textwrap.py @@ -605,7 +605,7 @@ def test_break_long(self): # bug 1146. Prevent a long word to be wrongly wrapped when the # preceding word is exactly one character shorter than the width self.check_wrap(self.text, 12, - ['Did you say ', + ['Did you say', '"supercalifr', 'agilisticexp', 'ialidocious?', @@ -633,7 +633,7 @@ def test_nobreak_long(self): def test_max_lines_long(self): self.check_wrap(self.text, 12, - ['Did you say ', + ['Did you say', '"supercalifr', 'agilisticexp', '[...]'], diff --git a/Lib/test/test_thread.py b/Lib/test/test_thread.py index d94e04250c9307..ac924728febc99 100644 --- a/Lib/test/test_thread.py +++ b/Lib/test/test_thread.py @@ -76,6 +76,14 @@ def test_stack_size(self): thread.stack_size(0) self.assertEqual(thread.stack_size(), 0, "stack_size not reset to default") + with self.assertRaises(ValueError): + # 123 bytes is too small + thread.stack_size(123) + + with self.assertRaises(ValueError): + # size must be positive + thread.stack_size(-4096) + @unittest.skipIf(os.name not in ("nt", "posix"), 'test meant for nt and posix') def test_nt_and_posix_stack_size(self): try: diff --git a/Lib/test/test_thread_local_bytecode.py b/Lib/test/test_thread_local_bytecode.py index d5c56db8d5da58..6a1ae4064b5daf 100644 --- a/Lib/test/test_thread_local_bytecode.py +++ b/Lib/test/test_thread_local_bytecode.py @@ -3,7 +3,7 @@ import unittest from test import support -from test.support import cpython_only, import_helper, requires_specialization_ft +from test.support import cpython_only, import_helper, requires_specialization from test.support.script_helper import assert_python_ok from test.support.threading_helper import requires_working_threading @@ -15,7 +15,7 @@ @requires_working_threading() @unittest.skipUnless(support.Py_GIL_DISABLED, "only in free-threaded builds") class TLBCTests(unittest.TestCase): - @requires_specialization_ft + @requires_specialization def test_new_threads_start_with_unspecialized_code(self): code = textwrap.dedent(""" import dis @@ -46,7 +46,7 @@ def f(a, b, q=None): """) assert_python_ok("-X", "tlbc=1", "-c", code) - @requires_specialization_ft + @requires_specialization def test_threads_specialize_independently(self): code = textwrap.dedent(""" import dis diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 00a3037c3e1e01..3d01804513bde9 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -6,7 +6,7 @@ from test.support import threading_helper, requires_subprocess, requires_gil_enabled from test.support import verbose, cpython_only, os_helper from test.support.import_helper import ensure_lazy_imports, import_module -from test.support.script_helper import assert_python_ok, assert_python_failure +from test.support.script_helper import assert_python_ok, assert_python_failure, spawn_python from test.support import force_not_colorized import random @@ -323,6 +323,7 @@ def f(mutex): # PyThreadState_SetAsyncExc() is a CPython-only gimmick, not (currently) # exposed at the Python level. This test relies on ctypes to get at it. + @cpython_only def test_PyThreadState_SetAsyncExc(self): ctypes = import_module("ctypes") @@ -411,6 +412,53 @@ def run(self): t.join() # else the thread is still running, and we have no way to kill it + @cpython_only + @unittest.skipUnless(hasattr(signal, "pthread_kill"), "need pthread_kill") + @unittest.skipUnless(hasattr(signal, "SIGUSR1"), "need SIGUSR1") + def test_PyThreadState_SetAsyncExc_interrupts_sleep(self): + _testcapi = import_module("_testlimitedcapi") + + worker_started = threading.Event() + + class InjectedException(Exception): + """Custom exception for testing""" + + caught_exception = None + + def catch_exception(): + nonlocal caught_exception + day_as_seconds = 60 * 60 * 24 + try: + worker_started.set() + time.sleep(day_as_seconds) + except InjectedException as exc: + caught_exception = exc + + thread = threading.Thread(target=catch_exception) + thread.start() + worker_started.wait() + + signal.signal(signal.SIGUSR1, lambda sig, frame: None) + + result = _testcapi.threadstate_set_async_exc( + thread.ident, InjectedException) + self.assertEqual(result, 1) + + for _ in support.sleeping_retry(support.SHORT_TIMEOUT): + if not thread.is_alive(): + break + try: + signal.pthread_kill(thread.ident, signal.SIGUSR1) + except OSError: + # The thread might have terminated between the is_alive check + # and the pthread_kill + break + + thread.join() + signal.signal(signal.SIGUSR1, signal.SIG_DFL) + + self.assertIsInstance(caught_exception, InjectedException) + def test_limbo_cleanup(self): # Issue 7481: Failure to start thread should cleanup the limbo map. def fail_new_thread(*args, **kwargs): @@ -1430,6 +1478,33 @@ def test_native_id_after_fork(self): self.assertEqual(len(native_ids), 2) self.assertNotEqual(native_ids[0], native_ids[1]) + def test_stop_the_world_during_finalization(self): + # gh-137433: Test functions that trigger a stop-the-world in the free + # threading build concurrent with interpreter finalization. + script = """if True: + import gc + import sys + import threading + NUM_THREADS = 5 + b = threading.Barrier(NUM_THREADS + 1) + def run_in_bg(): + b.wait() + while True: + sys.setprofile(None) + gc.collect() + + for _ in range(NUM_THREADS): + t = threading.Thread(target=run_in_bg, daemon=True) + t.start() + + b.wait() + print("Exiting...") + """ + rc, out, err = assert_python_ok('-c', script) + self.assertEqual(rc, 0) + self.assertEqual(err, b"") + self.assertEqual(out.strip(), b"Exiting...") + class ThreadJoinOnShutdown(BaseTestCase): def _run_and_join(self, script): @@ -1749,6 +1824,7 @@ def task(): self.assertEqual(os.read(r_interp, 1), DONE) @cpython_only + @support.skip_if_sanitizer(thread=True, memory=True) def test_daemon_threads_fatal_error(self): import_module("_testcapi") subinterp_code = f"""if 1: @@ -1767,10 +1843,7 @@ def f(): _testcapi.run_in_subinterp(%r) """ % (subinterp_code,) - with test.support.SuppressCrashReport(): - rc, out, err = assert_python_failure("-c", script) - self.assertIn("Fatal Python error: Py_EndInterpreter: " - "not the last thread", err.decode()) + assert_python_ok("-c", script) def _check_allowed(self, before_start='', *, allowed=True, @@ -2042,6 +2115,49 @@ def modify_file(): t.start() t.join() + def test_dummy_thread_on_interpreter_shutdown(self): + # GH-130522: When `threading` held a reference to itself and then a + # _DummyThread() object was created, destruction of the dummy thread + # would emit an unraisable exception at shutdown, due to a lock being + # destroyed. + code = """if True: + import sys + import threading + + threading.x = sys.modules[__name__] + x = threading._DummyThread() + """ + rc, out, err = assert_python_ok("-c", code) + self.assertEqual(rc, 0) + self.assertEqual(out, b"") + self.assertEqual(err, b"") + + @requires_subprocess() + @unittest.skipIf(os.name == 'nt', "signals don't work well on windows") + def test_keyboard_interrupt_during_threading_shutdown(self): + import subprocess + source = f""" + from threading import Thread + import time + import os + + + def test(): + print('a', flush=True, end='') + time.sleep(10) + + + for _ in range(3): + Thread(target=test).start() + """ + + with spawn_python("-c", source, stderr=subprocess.PIPE) as proc: + self.assertEqual(proc.stdout.read(3), b'aaa') + proc.send_signal(signal.SIGINT) + proc.stderr.flush() + error = proc.stderr.read() + self.assertIn(b"KeyboardInterrupt", error) + class ThreadRunFail(threading.Thread): def run(self): @@ -2252,6 +2368,231 @@ class BarrierTests(lock_tests.BarrierTests): barriertype = staticmethod(threading.Barrier) +## Test Synchronization tools for iterators ################ + +class ThreadingIteratorToolsTests(BaseTestCase): + def test_serialize_serializes_concurrent_iteration(self): + limit = 10_000 + workers_count = 10 + result = 0 + result_lock = threading.Lock() + start = threading.Event() + + def producer(limit): + for x in range(limit): + yield x + + def consumer(iterator): + nonlocal result + start.wait() + total = 0 + for x in iterator: + total += x + with result_lock: + result += total + + iterator = threading.serialize_iterator(producer(limit)) + workers = [ + threading.Thread(target=consumer, args=(iterator,)) + for _ in range(workers_count) + ] + with threading_helper.wait_threads_exit(): + for worker in workers: + worker.start() + for worker in workers: + # Wait for the worker thread to actually start. + while worker.ident is None: + time.sleep(0.1) + start.set() + for worker in workers: + worker.join() + + self.assertEqual(result, limit * (limit - 1) // 2) + + def test_serialize_generator_methods(self): + # A generator that yields and receives + def echo(): + try: + while True: + val = yield "ready" + yield f"received {val}" + except ValueError: + yield "caught" + + it = threading.serialize_iterator(echo()) + + # Test __next__ + self.assertEqual(next(it), "ready") + + # Test send() + self.assertEqual(it.send("hello"), "received hello") + self.assertEqual(next(it), "ready") + + # Test throw() + self.assertEqual(it.throw(ValueError), "caught") + + # Test close() + it.close() + with self.assertRaises(StopIteration): + next(it) + + def test_serialize_methods_attribute_error(self): + # A standard iterator that does not have send/throw/close + # should raise AttributeError when called. + standard_it = threading.serialize_iterator([1, 2, 3]) + + with self.assertRaises(AttributeError): + standard_it.send("foo") + + with self.assertRaises(AttributeError): + standard_it.throw(ValueError) + + with self.assertRaises(AttributeError): + standard_it.close() + + def test_serialize_generator_methods_locking(self): + # Verifies that generator methods also acquire the lock. + # We can test this by checking if the lock is held during the call. + + class LockCheckingGenerator: + def __init__(self, lock): + self.lock = lock + def __iter__(self): + return self + def send(self, value): + if not self.lock.locked(): + raise RuntimeError("Lock not held during send()") + return value + def throw(self, *args): + if not self.lock.locked(): + raise RuntimeError("Lock not held during throw()") + def close(self): + if not self.lock.locked(): + raise RuntimeError("Lock not held during close()") + + # Manually create the serialize object to inspect the lock + it = threading.serialize_iterator([]) + mock_gen = LockCheckingGenerator(it._lock) + it._iterator = mock_gen + + # These should not raise RuntimeError + it.send(1) + it.throw(ValueError) + it.close() + + def test_serialize_next_exception(self): + # Verify exception pass through for calls to next() + + def f(): + raise RuntimeError + yield None + + g = threading.serialize_iterator(f()) + with self.assertRaises(RuntimeError): + next(g) + + def test_synchronized_serializes_generator_instances(self): + unique = 10 + repetitions = 5 + limit = 100 + start = threading.Event() + + @threading.synchronized_iterator + def atomic_counter(): + # The sleep widens the race window that would exist without + # synchronization between yielding a value and advancing state. + i = 0 + while True: + yield i + time.sleep(0.0005) + i += 1 + + def consumer(counter): + start.wait() + for _ in range(limit): + next(counter) + + unique_counters = [atomic_counter() for _ in range(unique)] + counters = unique_counters * repetitions + workers = [ + threading.Thread(target=consumer, args=(counter,)) + for counter in counters + ] + with threading_helper.wait_threads_exit(): + for worker in workers: + worker.start() + start.set() + for worker in workers: + worker.join() + + self.assertEqual( + {next(counter) for counter in unique_counters}, + {limit * repetitions}, + ) + + def test_synchronized_preserves_wrapped_metadata(self): + def gen(): + yield 1 + + wrapped = threading.synchronized_iterator(gen) + + self.assertEqual(wrapped.__name__, gen.__name__) + self.assertIs(wrapped.__wrapped__, gen) + self.assertEqual(list(wrapped()), [1]) + + def test_concurrent_tee_supports_concurrent_consumers(self): + limit = 5_000 + num_threads = 25 + successes = 0 + failures = [] + result_lock = threading.Lock() + start = threading.Event() + expected = list(range(limit)) + + def producer(limit): + for x in range(limit): + yield x + + def consumer(iterator): + nonlocal successes + start.wait() + items = list(iterator) + with result_lock: + if items == expected: + successes += 1 + else: + failures.append(items[:20]) + + tees = threading.concurrent_tee(producer(limit), n=num_threads) + workers = [ + threading.Thread(target=consumer, args=(iterator,)) + for iterator in tees + ] + with threading_helper.wait_threads_exit(): + for worker in workers: + worker.start() + start.set() + for worker in workers: + worker.join() + + self.assertEqual(failures, []) + self.assertEqual(successes, len(tees)) + + # Verify that locks are shared + self.assertEqual(len({id(t_obj.lock) for t_obj in tees}), 1) + + def test_concurrent_tee_zero_iterators(self): + self.assertEqual(threading.concurrent_tee(range(10), n=0), ()) + + def test_concurrent_tee_negative_n(self): + with self.assertRaises(ValueError): + threading.concurrent_tee(range(10), n=-1) + + +################# + + + class MiscTestCase(unittest.TestCase): def test__all__(self): restore_default_excepthook(self) @@ -2264,6 +2605,9 @@ def test__all__(self): @unittest.skipUnless(hasattr(_thread, 'set_name'), "missing _thread.set_name") @unittest.skipUnless(hasattr(_thread, '_get_name'), "missing _thread._get_name") def test_set_name(self): + # Ensure main thread name is restored after test + self.addCleanup(_thread.set_name, _thread._get_name()) + # set_name() limit in bytes truncate = getattr(_thread, "_NAME_MAXLEN", None) limit = truncate or 100 @@ -2303,7 +2647,8 @@ def test_set_name(self): tests.append(os_helper.TESTFN_UNENCODABLE) if sys.platform.startswith("sunos"): - encoding = "utf-8" + # Use ASCII encoding on Solaris/Illumos/OpenIndiana + encoding = "ascii" else: encoding = sys.getfilesystemencoding() @@ -2319,7 +2664,7 @@ def work(): if truncate is not None: encoded = encoded[:truncate] if sys.platform.startswith("sunos"): - expected = encoded.decode("utf-8", "surrogateescape") + expected = encoded.decode("ascii", "surrogateescape") else: expected = os.fsdecode(encoded) else: @@ -2338,7 +2683,11 @@ def work(): if '\0' in expected: expected = expected.split('\0', 1)[0] - with self.subTest(name=name, expected=expected): + with self.subTest(name=name, expected=expected, thread="main"): + _thread.set_name(name) + self.assertEqual(_thread._get_name(), expected) + + with self.subTest(name=name, expected=expected, thread="worker"): work_name = None thread = threading.Thread(target=work, name=name) thread.start() @@ -2477,6 +2826,7 @@ def test_atexit_called_once(self): self.assertFalse(err) + @force_not_colorized def test_atexit_after_shutdown(self): # The only way to do this is by registering an atexit within # an atexit, which is intended to raise an exception. diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 5312faa50774ec..1850f053aaffd6 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -2,6 +2,7 @@ from test.support import warnings_helper import decimal import enum +import fractions import math import platform import sys @@ -170,10 +171,12 @@ def test_sleep_exceptions(self): # Improved exception #81267 with self.assertRaises(TypeError) as errmsg: time.sleep([]) - self.assertIn("integer or float", str(errmsg.exception)) + self.assertIn("real number", str(errmsg.exception)) def test_sleep(self): - for value in [-0.0, 0, 0.0, 1e-100, 1e-9, 1e-6, 1, 1.2]: + for value in [-0.0, 0, 0.0, 1e-100, 1e-9, 1e-6, 1, 1.2, + decimal.Decimal('0.02'), + fractions.Fraction(1, 50)]: with self.subTest(value=value): time.sleep(value) @@ -184,6 +187,38 @@ def test_epoch(self): # Only test the date and time, ignore other gmtime() members self.assertEqual(tuple(epoch)[:6], (1970, 1, 1, 0, 0, 0), epoch) + def test_gmtime(self): + # expected format: + # (tm_year, tm_mon, tm_mday, + # tm_hour, tm_min, tm_sec, + # tm_wday, tm_yday) + tests = [ + (-13262400, (1969, 7, 31, 12, 0, 0, 3, 212)), + (-6177600, (1969, 10, 21, 12, 0, 0, 1, 294)), + # leap years (pre epoch) + (-2077660800, (1904, 3, 1, 0, 0, 0, 1, 61)), + (-2077833600, (1904, 2, 28, 0, 0, 0, 6, 59)), + ] + + try: + from _testinternalcapi import SIZEOF_TIME_T + except ImportError: + pass + else: + if SIZEOF_TIME_T >= 8: + tests.extend(( + # non-leap years (pre epoch) + (-2203891200, (1900, 3, 1, 0, 0, 0, 3, 60)), + (-2203977600, (1900, 2, 28, 0, 0, 0, 2, 59)), + (-5359564800, (1800, 3, 1, 0, 0, 0, 5, 60)), + (-5359651200, (1800, 2, 28, 0, 0, 0, 4, 59)), + )) + + for t, expected in tests: + with self.subTest(t=t, expected=expected): + res = time.gmtime(t) + self.assertEqual(tuple(res)[:8], expected, res) + def test_strftime(self): tt = time.gmtime(self.t) for directive in ('a', 'A', 'b', 'B', 'c', 'd', 'H', 'I', @@ -323,11 +358,11 @@ def test_strptime(self): # Should be able to go round-trip from strftime to strptime without # raising an exception. tt = time.gmtime(self.t) - for directive in ('a', 'A', 'b', 'B', 'c', 'd', 'H', 'I', - 'j', 'm', 'M', 'p', 'S', + for directive in ('a', 'A', 'b', 'B', 'c', 'd', 'D', 'e', 'F', 'H', 'I', + 'j', 'm', 'M', 'n', 'p', 'S', 't', 'T', 'U', 'w', 'W', 'x', 'X', 'y', 'Y', 'Z', '%'): format = '%' + directive - if directive == 'd': + if directive in ('d', 'e'): format += ',%Y' # Avoid GH-70647. strf_output = time.strftime(format, tt) try: @@ -344,7 +379,7 @@ def test_strptime_bytes(self): def test_strptime_exception_context(self): # check that this doesn't chain exceptions needlessly (see #17572) with self.assertRaises(ValueError) as e: - time.strptime('', '%D') + time.strptime('', '%!') self.assertTrue(e.exception.__suppress_context__) # additional check for stray % branch with self.assertRaises(ValueError) as e: @@ -352,10 +387,13 @@ def test_strptime_exception_context(self): self.assertTrue(e.exception.__suppress_context__) def test_strptime_leap_year(self): - # GH-70647: warns if parsing a format with a day and no year. + # GH-70647: %d errors if parsing a format with a day and no year. + with self.assertRaises(ValueError): + time.strptime('02-07 18:28', '%m-%d %H:%M') + # %e without a year is deprecated, scheduled for removal in 3.17. with self.assertWarnsRegex(DeprecationWarning, r'.*day of month without a year.*'): - time.strptime('02-07 18:28', '%m-%d %H:%M') + time.strptime('02-07 18:28', '%m-%e %H:%M') def test_asctime(self): time.asctime(time.gmtime(self.t)) @@ -498,12 +536,13 @@ def test_localtime_without_arg(self): def test_mktime(self): # Issue #1726687 for t in (-2, -1, 0, 1): + t_struct = time.localtime(t) try: - tt = time.localtime(t) + t1 = time.mktime(t_struct) except (OverflowError, OSError): pass else: - self.assertEqual(time.mktime(tt), t) + self.assertEqual(t1, t) # Issue #13309: passing extreme values to mktime() or localtime() # borks the glibc's internal timezone data. @@ -577,11 +616,10 @@ def test_thread_time(self): # thread_time() should not include time spend during a sleep start = time.thread_time() - time.sleep(0.100) + time.sleep(0.200) stop = time.thread_time() - # use 20 ms because thread_time() has usually a resolution of 15 ms - # on Windows - self.assertLess(stop - start, 0.020) + # gh-143528: use 100 ms to support slow CI + self.assertLess(stop - start, 0.100) info = time.get_clock_info('thread_time') self.assertTrue(info.monotonic) @@ -756,7 +794,6 @@ class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear, unittest.TestCase): class TestPytime(unittest.TestCase): @skip_if_buggy_ucrt_strfptime - @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") def test_localtime_timezone(self): # Get the localtime and examine it for the offset and zone. @@ -791,14 +828,12 @@ def test_localtime_timezone(self): self.assertEqual(new_lt.tm_gmtoff, lt.tm_gmtoff) self.assertEqual(new_lt9.tm_zone, lt.tm_zone) - @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") def test_strptime_timezone(self): t = time.strptime("UTC", "%Z") self.assertEqual(t.tm_zone, 'UTC') t = time.strptime("+0500", "%z") self.assertEqual(t.tm_gmtoff, 5 * 3600) - @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") def test_short_times(self): import pickle diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py index 2aeebea9f93d43..a2a09f9de61490 100644 --- a/Lib/test/test_timeit.py +++ b/Lib/test/test_timeit.py @@ -4,8 +4,14 @@ import io from textwrap import dedent -from test.support import captured_stdout -from test.support import captured_stderr +from test.support import ( + captured_stderr, + captured_stdout, + force_colorized, + force_not_colorized_test_class, +) + +from _colorize import get_theme # timeit's default number of iterations. DEFAULT_NUMBER = 1000000 @@ -41,6 +47,7 @@ def wrap_timer(self, timer): self.saved_timer = timer return self +@force_not_colorized_test_class class TestTimeit(unittest.TestCase): def tearDown(self): @@ -252,11 +259,12 @@ def run_main(self, seconds_per_increment=1.0, switches=None, timer=None): return s.getvalue() def test_main_bad_switch(self): - s = self.run_main(switches=['--bad-switch']) - self.assertEqual(s, dedent("""\ - option --bad-switch not recognized - use -h/--help for command line help - """)) + with captured_stderr() as error_stringio: + s = self.run_main(switches=["--bad-switch"]) + self.assertEqual(s, "") + self.assertIn( + "unrecognized arguments: --bad-switch", error_stringio.getvalue() + ) def test_main_seconds(self): s = self.run_main(seconds_per_increment=5.5) @@ -294,15 +302,16 @@ def test_main_negative_reps(self): s = self.run_main(seconds_per_increment=60.0, switches=['-r-5']) self.assertEqual(s, "1 loop, best of 1: 60 sec per loop\n") - @unittest.skipIf(sys.flags.optimize >= 2, "need __doc__") def test_main_help(self): s = self.run_main(switches=['-h']) - self.assertEqual(s, timeit.__doc__) + self.assertIn("Tool for measuring execution time", s) + self.assertIn("-n", s) + self.assertIn("--number", s) def test_main_verbose(self): s = self.run_main(switches=['-v']) self.assertEqual(s, dedent("""\ - 1 loop -> 1 secs + 1 loop -> 1 sec raw times: 1 sec, 1 sec, 1 sec, 1 sec, 1 sec @@ -312,19 +321,19 @@ def test_main_verbose(self): def test_main_very_verbose(self): s = self.run_main(seconds_per_increment=0.000_030, switches=['-vv']) self.assertEqual(s, dedent("""\ - 1 loop -> 3e-05 secs - 2 loops -> 6e-05 secs - 5 loops -> 0.00015 secs - 10 loops -> 0.0003 secs - 20 loops -> 0.0006 secs - 50 loops -> 0.0015 secs - 100 loops -> 0.003 secs - 200 loops -> 0.006 secs - 500 loops -> 0.015 secs - 1000 loops -> 0.03 secs - 2000 loops -> 0.06 secs - 5000 loops -> 0.15 secs - 10000 loops -> 0.3 secs + 1 loop -> 3e-05 sec + 2 loops -> 6e-05 sec + 5 loops -> 0.00015 sec + 10 loops -> 0.0003 sec + 20 loops -> 0.0006 sec + 50 loops -> 0.0015 sec + 100 loops -> 0.003 sec + 200 loops -> 0.006 sec + 500 loops -> 0.015 sec + 1000 loops -> 0.03 sec + 2000 loops -> 0.06 sec + 5000 loops -> 0.15 sec + 10000 loops -> 0.3 sec raw times: 300 msec, 300 msec, 300 msec, 300 msec, 300 msec @@ -346,10 +355,12 @@ def test_main_with_time_unit(self): "100 loops, best of 5: 3e+03 usec per loop\n") # Test invalid unit input with captured_stderr() as error_stringio: - invalid = self.run_main(seconds_per_increment=0.003, - switches=['-u', 'parsec']) - self.assertEqual(error_stringio.getvalue(), - "Unrecognized unit. Please select nsec, usec, msec, or sec.\n") + invalid = self.run_main( + seconds_per_increment=0.003, switches=["-u", "parsec"] + ) + self.assertIn( + "choose from 'nsec', 'usec', 'msec', 'sec'", error_stringio.getvalue() + ) def test_main_exception(self): with captured_stderr() as error_stringio: @@ -361,10 +372,10 @@ def test_main_exception_fixed_reps(self): s = self.run_main(switches=['-n1', '1/0']) self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError') - def autorange(self, seconds_per_increment=1/1024, callback=None): + def autorange(self, seconds_per_increment=1/1024, callback=None, target_time=0.2): timer = FakeTimer(seconds_per_increment=seconds_per_increment) t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer) - return t.autorange(callback) + return t.autorange(callback, target_time=target_time) def test_autorange(self): num_loops, time_taken = self.autorange() @@ -376,6 +387,11 @@ def test_autorange_second(self): self.assertEqual(num_loops, 1) self.assertEqual(time_taken, 1.0) + def test_autorange_with_target_time(self): + num_loops, time_taken = self.autorange(target_time=1.0) + self.assertEqual(num_loops, 2000) + self.assertEqual(time_taken, 2000/1024) + def test_autorange_with_callback(self): def callback(a, b): print("{} {:.3f}".format(a, b)) @@ -395,5 +411,39 @@ def callback(a, b): self.assertEqual(s.getvalue(), expected) -if __name__ == '__main__': +class TestTimeitColor(unittest.TestCase): + + fake_stmt = TestTimeit.fake_stmt + run_main = TestTimeit.run_main + + @force_colorized + def test_main_colorized(self): + t = get_theme(force_color=True).timeit + s = self.run_main(seconds_per_increment=5.5) + self.assertEqual( + s, + "1 loop, best of 5: " + f"{t.best}5.5 sec{t.reset} " + f"{t.per_loop}per loop{t.reset}\n", + ) + + @force_colorized + def test_main_verbose_colorized(self): + t = get_theme(force_color=True).timeit + s = self.run_main(switches=["-v"]) + self.assertEqual( + s, + f"1 loop {t.punctuation}-> {t.timing}1 sec{t.reset}\n\n" + "raw times: " + f"{t.timing}1 sec{t.punctuation}, " + f"{t.timing}1 sec{t.punctuation}, " + f"{t.timing}1 sec{t.punctuation}, " + f"{t.timing}1 sec{t.punctuation}, " + f"{t.timing}1 sec{t.reset}\n\n" + f"1 loop, best of 5: {t.best}1 sec{t.reset} " + f"{t.per_loop}per loop{t.reset}\n", + ) + + +if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_tkinter/__init__.py b/Lib/test/test_tkinter/__init__.py index aa196c12c804ac..62890c705a6ca6 100644 --- a/Lib/test/test_tkinter/__init__.py +++ b/Lib/test/test_tkinter/__init__.py @@ -22,3 +22,9 @@ def load_tests(*args): return load_package_tests(os.path.dirname(__file__), *args) + +def setUpModule(): + wantobjects = support.get_resource_value('wantobjects') + if wantobjects is not None: + unittest.enterModuleContext( + support.swap_attr(tkinter, 'wantobjects', int(wantobjects))) diff --git a/Lib/test/test_tkinter/support.py b/Lib/test/test_tkinter/support.py index 46b01e6f131290..7fb0b217fd24ec 100644 --- a/Lib/test/test_tkinter/support.py +++ b/Lib/test/test_tkinter/support.py @@ -1,5 +1,14 @@ import functools import tkinter +import unittest +from test import support + + +def setUpModule(): + wantobjects = support.get_resource_value('wantobjects') + if wantobjects is not None: + unittest.enterModuleContext( + support.swap_attr(tkinter, 'wantobjects', int(wantobjects))) class AbstractTkTest: @@ -10,6 +19,8 @@ def setUpClass(cls): tkinter.NoDefaultRoot() cls.root = tkinter.Tk() cls.wantobjects = cls.root.wantobjects() + if support.is_resource_enabled('wantobjects'): + assert cls.wantobjects == int(support.get_resource_value('wantobjects')) # De-maximize main window. # Some window managers can maximize new windows. cls.root.wm_state('normal') diff --git a/Lib/test/test_tkinter/test_colorchooser.py b/Lib/test/test_tkinter/test_colorchooser.py index 9bba21392d8d14..8a7e97f207a41f 100644 --- a/Lib/test/test_tkinter/test_colorchooser.py +++ b/Lib/test/test_tkinter/test_colorchooser.py @@ -1,6 +1,7 @@ import unittest import tkinter from test.support import requires, swap_attr +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractDefaultRootTest, AbstractTkTest from tkinter import colorchooser from tkinter.colorchooser import askcolor diff --git a/Lib/test/test_tkinter/test_font.py b/Lib/test/test_tkinter/test_font.py index 563707ddd2fa9b..ee147710fbfc85 100644 --- a/Lib/test/test_tkinter/test_font.py +++ b/Lib/test/test_tkinter/test_font.py @@ -1,7 +1,9 @@ +import collections.abc import unittest import tkinter from tkinter import font from test.support import requires, gc_collect, ALWAYS_EQ +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest requires('gui') @@ -118,6 +120,16 @@ def test_repr(self): repr(self.font), f'<tkinter.font.Font object {fontname!r}>' ) + def test_iterable_protocol(self): + self.assertNotIsSubclass(font.Font, collections.abc.Iterable) + self.assertNotIsSubclass(font.Font, collections.abc.Container) + self.assertNotIsInstance(self.font, collections.abc.Iterable) + self.assertNotIsInstance(self.font, collections.abc.Container) + with self.assertRaisesRegex(TypeError, 'is not iterable'): + iter(self.font) + with self.assertRaisesRegex(TypeError, 'is not a container or iterable'): + self.font in self.font + class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): @@ -159,5 +171,15 @@ def test_nametofont(self): self.assertRaises(RuntimeError, font.nametofont, fontname) +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(font, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_tkinter/test_geometry_managers.py b/Lib/test/test_tkinter/test_geometry_managers.py index d71a634a767310..3dcdadd1aacf10 100644 --- a/Lib/test/test_tkinter/test_geometry_managers.py +++ b/Lib/test/test_tkinter/test_geometry_managers.py @@ -4,6 +4,7 @@ from tkinter import TclError from test.support import requires +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import pixels_conv from test.test_tkinter.widget_tests import AbstractWidgetTest @@ -39,11 +40,11 @@ def test_pack_configure_after(self): b.pack_configure(side='top') c.pack_configure(side='top') d.pack_configure(side='top') - self.assertEqual(pack.pack_slaves(), [a, b, c, d]) + self.assertEqual(pack.pack_content(), [a, b, c, d]) a.pack_configure(after=b) - self.assertEqual(pack.pack_slaves(), [b, a, c, d]) + self.assertEqual(pack.pack_content(), [b, a, c, d]) a.pack_configure(after=a) - self.assertEqual(pack.pack_slaves(), [b, a, c, d]) + self.assertEqual(pack.pack_content(), [b, a, c, d]) def test_pack_configure_anchor(self): pack, a, b, c, d = self.create2() @@ -72,11 +73,11 @@ def test_pack_configure_before(self): b.pack_configure(side='top') c.pack_configure(side='top') d.pack_configure(side='top') - self.assertEqual(pack.pack_slaves(), [a, b, c, d]) + self.assertEqual(pack.pack_content(), [a, b, c, d]) a.pack_configure(before=d) - self.assertEqual(pack.pack_slaves(), [b, c, a, d]) + self.assertEqual(pack.pack_content(), [b, c, a, d]) a.pack_configure(before=a) - self.assertEqual(pack.pack_slaves(), [b, c, a, d]) + self.assertEqual(pack.pack_content(), [b, c, a, d]) def test_pack_configure_expand(self): pack, a, b, c, d = self.create2() @@ -109,10 +110,10 @@ def test_pack_configure_in(self): c.pack_configure(side='top') d.pack_configure(side='top') a.pack_configure(in_=pack) - self.assertEqual(pack.pack_slaves(), [b, c, d, a]) + self.assertEqual(pack.pack_content(), [b, c, d, a]) a.pack_configure(in_=c) - self.assertEqual(pack.pack_slaves(), [b, c, d]) - self.assertEqual(c.pack_slaves(), [a]) + self.assertEqual(pack.pack_content(), [b, c, d]) + self.assertEqual(c.pack_content(), [a]) with self.assertRaisesRegex( TclError, """can't pack "?%s"? inside itself""" % (a,)): a.pack_configure(in_=a) @@ -222,11 +223,11 @@ def test_pack_forget(self): a.pack_configure() b.pack_configure() c.pack_configure() - self.assertEqual(pack.pack_slaves(), [a, b, c]) + self.assertEqual(pack.pack_content(), [a, b, c]) b.pack_forget() - self.assertEqual(pack.pack_slaves(), [a, c]) + self.assertEqual(pack.pack_content(), [a, c]) b.pack_forget() - self.assertEqual(pack.pack_slaves(), [a, c]) + self.assertEqual(pack.pack_content(), [a, c]) d.pack_forget() def test_pack_info(self): @@ -272,6 +273,14 @@ def test_pack_propagate(self): self.assertEqual(pack.winfo_reqwidth(), 20) self.assertEqual(pack.winfo_reqheight(), 40) + def test_pack_content(self): + pack, a, b, c, d = self.create2() + self.assertEqual(pack.pack_content(), []) + a.pack_configure() + self.assertEqual(pack.pack_content(), [a]) + b.pack_configure() + self.assertEqual(pack.pack_content(), [a, b]) + def test_pack_slaves(self): pack, a, b, c, d = self.create2() self.assertEqual(pack.pack_slaves(), []) @@ -476,6 +485,15 @@ def test_place_info(self): with self.assertRaises(TypeError): f2.place_info(0) + def test_place_content(self): + foo = tkinter.Frame(self.root) + bar = tkinter.Frame(self.root) + self.assertEqual(foo.place_content(), []) + bar.place_configure(in_=foo) + self.assertEqual(foo.place_content(), [bar]) + with self.assertRaises(TypeError): + foo.place_content(0) + def test_place_slaves(self): foo = tkinter.Frame(self.root) bar = tkinter.Frame(self.root) @@ -728,10 +746,10 @@ def test_grid_forget(self): c = tkinter.Button(self.root) b.grid_configure(row=2, column=2, rowspan=2, columnspan=2, padx=3, pady=4, sticky='ns') - self.assertEqual(self.root.grid_slaves(), [b]) + self.assertEqual(self.root.grid_content(), [b]) b.grid_forget() c.grid_forget() - self.assertEqual(self.root.grid_slaves(), []) + self.assertEqual(self.root.grid_content(), []) self.assertEqual(b.grid_info(), {}) b.grid_configure(row=0, column=0) info = b.grid_info() @@ -748,10 +766,10 @@ def test_grid_remove(self): c = tkinter.Button(self.root) b.grid_configure(row=2, column=2, rowspan=2, columnspan=2, padx=3, pady=4, sticky='ns') - self.assertEqual(self.root.grid_slaves(), [b]) + self.assertEqual(self.root.grid_content(), [b]) b.grid_remove() c.grid_remove() - self.assertEqual(self.root.grid_slaves(), []) + self.assertEqual(self.root.grid_content(), []) self.assertEqual(b.grid_info(), {}) b.grid_configure(row=0, column=0) info = b.grid_info() @@ -886,6 +904,23 @@ def test_grid_size(self): f.grid_configure(row=4, column=5) self.assertEqual(self.root.grid_size(), (6, 5)) + def test_grid_content(self): + self.assertEqual(self.root.grid_content(), []) + a = tkinter.Label(self.root) + a.grid_configure(row=0, column=1) + b = tkinter.Label(self.root) + b.grid_configure(row=1, column=0) + c = tkinter.Label(self.root) + c.grid_configure(row=1, column=1) + d = tkinter.Label(self.root) + d.grid_configure(row=1, column=1) + self.assertEqual(self.root.grid_content(), [d, c, b, a]) + self.assertEqual(self.root.grid_content(row=0), [a]) + self.assertEqual(self.root.grid_content(row=1), [d, c, b]) + self.assertEqual(self.root.grid_content(column=0), [b]) + self.assertEqual(self.root.grid_content(column=1), [d, c, a]) + self.assertEqual(self.root.grid_content(row=1, column=1), [d, c]) + def test_grid_slaves(self): self.assertEqual(self.root.grid_slaves(), []) a = tkinter.Label(self.root) diff --git a/Lib/test/test_tkinter/test_images.py b/Lib/test/test_tkinter/test_images.py index 38371fe00d6eb5..3aca9515a33b24 100644 --- a/Lib/test/test_tkinter/test_images.py +++ b/Lib/test/test_tkinter/test_images.py @@ -1,7 +1,9 @@ +import collections.abc import unittest import tkinter from test import support from test.support import os_helper +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest, requires_tk support.requires('gui') @@ -61,7 +63,33 @@ def test_image_create_photo(self): self.assertRaises(RuntimeError, tkinter.PhotoImage) -class BitmapImageTest(AbstractTkTest, unittest.TestCase): +class BaseImageTest: + def create(self): + return self.image_class('::img::test', master=self.root, + file=self.testfile) + + def test_bug_100814(self): + # gh-100814: Passing a callable option value causes AttributeError. + with self.assertRaises(tkinter.TclError): + self.image_class('::img::test', master=self.root, spam=print) + image = self.image_class('::img::test', master=self.root) + with self.assertRaises(tkinter.TclError): + image.configure(spam=print) + + def test_iterable_protocol(self): + image = self.create() + self.assertNotIsSubclass(self.image_class, collections.abc.Iterable) + self.assertNotIsSubclass(self.image_class, collections.abc.Container) + self.assertNotIsInstance(image, collections.abc.Iterable) + self.assertNotIsInstance(image, collections.abc.Container) + with self.assertRaisesRegex(TypeError, 'is not iterable'): + iter(image) + with self.assertRaisesRegex(TypeError, 'is not a container or iterable'): + image in image + + +class BitmapImageTest(BaseImageTest, AbstractTkTest, unittest.TestCase): + image_class = tkinter.BitmapImage @classmethod def setUpClass(cls): @@ -144,26 +172,15 @@ def test_configure_foreground(self): self.assertEqual(image['foreground'], '-foreground {} {} #000000 yellow') - def test_bug_100814(self): - # gh-100814: Passing a callable option value causes AttributeError. - with self.assertRaises(tkinter.TclError): - tkinter.BitmapImage('::img::test', master=self.root, spam=print) - image = tkinter.BitmapImage('::img::test', master=self.root) - with self.assertRaises(tkinter.TclError): - image.configure(spam=print) - -class PhotoImageTest(AbstractTkTest, unittest.TestCase): +class PhotoImageTest(BaseImageTest, AbstractTkTest, unittest.TestCase): + image_class = tkinter.PhotoImage @classmethod def setUpClass(cls): AbstractTkTest.setUpClass.__func__(cls) cls.testfile = support.findfile('python.gif', subdir='tkinterdata') - def create(self): - return tkinter.PhotoImage('::img::test', master=self.root, - file=self.testfile) - def colorlist(self, *args): if tkinter.TkVersion >= 8.6 and self.wantobjects: return args @@ -282,14 +299,6 @@ def test_configure_palette(self): image.configure(palette='3/4/2') self.assertEqual(image['palette'], '3/4/2') - def test_bug_100814(self): - # gh-100814: Passing a callable option value causes AttributeError. - with self.assertRaises(tkinter.TclError): - tkinter.PhotoImage('::img::test', master=self.root, spam=print) - image = tkinter.PhotoImage('::img::test', master=self.root) - with self.assertRaises(tkinter.TclError): - image.configure(spam=print) - def test_blank(self): image = self.create() image.blank() diff --git a/Lib/test/test_tkinter/test_loadtk.py b/Lib/test/test_tkinter/test_loadtk.py index 61b0eda2fc750a..7cff654621eb35 100644 --- a/Lib/test/test_tkinter/test_loadtk.py +++ b/Lib/test/test_tkinter/test_loadtk.py @@ -3,6 +3,7 @@ import unittest import test.support as test_support from test.support import os_helper +from test.test_tkinter.support import setUpModule # noqa: F401 from tkinter import Tcl, TclError test_support.requires('gui') diff --git a/Lib/test/test_tkinter/test_messagebox.py b/Lib/test/test_tkinter/test_messagebox.py index f41bdc98286283..f29f0c7ba59086 100644 --- a/Lib/test/test_tkinter/test_messagebox.py +++ b/Lib/test/test_tkinter/test_messagebox.py @@ -1,6 +1,7 @@ import unittest import tkinter from test.support import requires, swap_attr +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractDefaultRootTest from tkinter.commondialog import Dialog from tkinter.messagebox import showinfo diff --git a/Lib/test/test_tkinter/test_misc.py b/Lib/test/test_tkinter/test_misc.py index 0c76e07066f8a8..2dc3bdfcec4076 100644 --- a/Lib/test/test_tkinter/test_misc.py +++ b/Lib/test/test_tkinter/test_misc.py @@ -1,9 +1,13 @@ +import collections.abc import functools +import platform +import sys import unittest import tkinter from tkinter import TclError import enum from test import support +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest, requires_tk, get_tk_patchlevel) @@ -508,6 +512,17 @@ def test_embedded_null(self): widget.selection_range(0, 'end') self.assertEqual(widget.selection_get(), '\u20ac\0abc\x00def') + def test_iterable_protocol(self): + widget = tkinter.Entry(self.root) + self.assertNotIsSubclass(tkinter.Entry, collections.abc.Iterable) + self.assertNotIsSubclass(tkinter.Entry, collections.abc.Container) + self.assertNotIsInstance(widget, collections.abc.Iterable) + self.assertNotIsInstance(widget, collections.abc.Container) + with self.assertRaisesRegex(TypeError, 'is not iterable'): + iter(widget) + with self.assertRaisesRegex(TypeError, 'is not a container or iterable'): + widget in widget + class WmTest(AbstractTkTest, unittest.TestCase): @@ -565,12 +580,27 @@ def test_wm_attribute(self): def test_wm_iconbitmap(self): t = tkinter.Toplevel(self.root) + patchlevel = get_tk_patchlevel(t) + + if ( + t._windowingsystem == 'aqua' + and sys.platform == 'darwin' + and platform.machine() == 'x86_64' + and platform.mac_ver()[0].startswith('26.') + and ( + patchlevel[:3] <= (8, 6, 17) + or (9, 0) <= patchlevel[:3] <= (9, 0, 3) + ) + ): + # https://github.com/python/cpython/issues/146531 + # Tk bug 4a2070f0d3a99aa412bc582d386d575ca2f37323 + self.skipTest('wm iconbitmap hangs on macOS 26 Intel') + self.assertEqual(t.wm_iconbitmap(), '') t.wm_iconbitmap('hourglass') bug = False if t._windowingsystem == 'aqua': # Tk bug 13ac26b35dc55f7c37f70b39d59d7ef3e63017c8. - patchlevel = get_tk_patchlevel(t) if patchlevel < (8, 6, 17) or (9, 0) <= patchlevel < (9, 0, 2): bug = True if not bug: @@ -623,6 +653,8 @@ def test_focus(self): self.assertEqual(e.x_root, '??') self.assertEqual(e.y_root, '??') self.assertEqual(e.delta, 0) + self.assertEqual(e.user_data, '??') + self.assertEqual(e.detail, 'NotifyAncestor') self.assertEqual(repr(e), '<FocusIn event>') def test_configure(self): @@ -656,6 +688,8 @@ def test_configure(self): self.assertEqual(e.x_root, '??') self.assertEqual(e.y_root, '??') self.assertEqual(e.delta, 0) + self.assertEqual(e.user_data, '??') + self.assertEqual(e.detail, '??') self.assertEqual(repr(e), '<Configure event x=0 y=0 width=150 height=100>') def test_event_generate_key_press(self): @@ -692,6 +726,8 @@ def test_event_generate_key_press(self): self.assertEqual(e.x_root, -1) self.assertEqual(e.y_root, -1) self.assertEqual(e.delta, 0) + self.assertEqual(e.user_data, '??') + self.assertEqual(e.detail, '??') self.assertEqual(repr(e), f"<KeyPress event state={e.state:#x} " f"keysym=z keycode={e.keycode} char='z' x={e.x} y={e.y}>") @@ -727,8 +763,17 @@ def test_event_generate_enter(self): self.assertEqual(e.x_root, 100 + f.winfo_rootx()) self.assertEqual(e.y_root, 50 + f.winfo_rooty()) self.assertEqual(e.delta, 0) + self.assertEqual(e.user_data, '??') + self.assertEqual(e.detail, 'NotifyAncestor') self.assertEqual(repr(e), '<Enter event focus=False x=100 y=50>') + f.event_generate('<Enter>', x=100, y=50, detail='NotifyPointer') + self.assertEqual(len(events), 2, events) + e = events[1] + self.assertIs(e.type, tkinter.EventType.Enter) + self.assertEqual(e.user_data, '??') + self.assertEqual(e.detail, 'NotifyPointer') + def test_event_generate_button_press(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() @@ -761,6 +806,8 @@ def test_event_generate_button_press(self): self.assertEqual(e.x_root, f.winfo_rootx() + 100) self.assertEqual(e.y_root, f.winfo_rooty() + 50) self.assertEqual(e.delta, 0) + self.assertEqual(e.user_data, '??') + self.assertEqual(e.detail, '??') self.assertEqual(repr(e), '<ButtonPress event num=1 x=100 y=50>') def test_event_generate_motion(self): @@ -795,6 +842,8 @@ def test_event_generate_motion(self): self.assertEqual(e.x_root, f.winfo_rootx() + 100) self.assertEqual(e.y_root, f.winfo_rooty() + 50) self.assertEqual(e.delta, 0) + self.assertEqual(e.user_data, '??') + self.assertEqual(e.detail, '??') self.assertEqual(repr(e), '<Motion event state=Button1 x=100 y=50>') def test_event_generate_mouse_wheel(self): @@ -829,9 +878,11 @@ def test_event_generate_mouse_wheel(self): self.assertEqual(e.x_root, f.winfo_rootx() + 100) self.assertEqual(e.y_root, f.winfo_rooty() + 50) self.assertEqual(e.delta, -5) + self.assertEqual(e.user_data, '??') + self.assertEqual(e.detail, '??') self.assertEqual(repr(e), '<MouseWheel event delta=-5 x=100 y=50>') - def test_generate_event_virtual_event(self): + def test_event_generate_virtual_event(self): f = tkinter.Frame(self.root, width=150, height=100) f.pack() self.root.wait_visibility() # needed on Windows @@ -863,9 +914,18 @@ def test_generate_event_virtual_event(self): self.assertEqual(e.x_root, f.winfo_rootx() + 50) self.assertEqual(e.y_root, -1) self.assertEqual(e.delta, 0) + self.assertEqual(e.user_data, '') + self.assertEqual(e.detail, '??') self.assertEqual(repr(e), f"<VirtualEvent event x=50 y=0>") + f.event_generate('<<Spam>>', data='spam') + self.assertEqual(len(events), 2, events) + e = events[1] + self.assertIs(e.type, tkinter.EventType.VirtualEvent) + self.assertEqual(e.user_data, 'spam') + self.assertEqual(e.detail, '??') + class BindTest(AbstractTkTest, unittest.TestCase): diff --git a/Lib/test/test_tkinter/test_simpledialog.py b/Lib/test/test_tkinter/test_simpledialog.py index 502f7f7098a322..313ad82e0a2c0d 100644 --- a/Lib/test/test_tkinter/test_simpledialog.py +++ b/Lib/test/test_tkinter/test_simpledialog.py @@ -1,6 +1,7 @@ import unittest import tkinter from test.support import requires, swap_attr +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractDefaultRootTest from tkinter.simpledialog import Dialog, askinteger diff --git a/Lib/test/test_tkinter/test_text.py b/Lib/test/test_tkinter/test_text.py index b26956930d3402..453a4505a0a4da 100644 --- a/Lib/test/test_tkinter/test_text.py +++ b/Lib/test/test_tkinter/test_text.py @@ -1,6 +1,7 @@ import unittest import tkinter from test.support import requires +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractTkTest requires('gui') @@ -34,12 +35,116 @@ def test_search(self): # Invalid text index. self.assertRaises(tkinter.TclError, text.search, '', 0) + self.assertRaises(tkinter.TclError, text.search, '', '') + self.assertRaises(tkinter.TclError, text.search, '', 'invalid') + self.assertRaises(tkinter.TclError, text.search, '', '1.0', 0) + self.assertRaises(tkinter.TclError, text.search, '', '1.0', '') + self.assertRaises(tkinter.TclError, text.search, '', '1.0', 'invalid') - # Check if we are getting the indices as strings -- you are likely - # to get Tcl_Obj under Tk 8.5 if Tkinter doesn't convert it. - text.insert('1.0', 'hi-test') - self.assertEqual(text.search('-test', '1.0', 'end'), '1.2') - self.assertEqual(text.search('test', '1.0', 'end'), '1.3') + text.insert('1.0', + 'This is a test. This is only a test.\n' + 'Another line.\n' + 'Yet another line.\n' + '64-bit') + + self.assertEqual(text.search('test', '1.0'), '1.10') + self.assertEqual(text.search('test', '1.0', 'end'), '1.10') + self.assertEqual(text.search('test', '1.0', '1.10'), '') + self.assertEqual(text.search('test', '1.11'), '1.31') + self.assertEqual(text.search('test', '1.32', 'end'), '') + self.assertEqual(text.search('test', '1.32'), '1.10') + + self.assertEqual(text.search('', '1.0'), '1.0') # empty pattern + self.assertEqual(text.search('nonexistent', '1.0'), '') + self.assertEqual(text.search('-bit', '1.0'), '4.2') # starts with a hyphen + + self.assertEqual(text.search('line', '3.0'), '3.12') + self.assertEqual(text.search('line', '3.0', forwards=True), '3.12') + self.assertEqual(text.search('line', '3.0', backwards=True), '2.8') + self.assertEqual(text.search('line', '3.0', forwards=True, backwards=True), '2.8') + + self.assertEqual(text.search('t.', '1.0'), '1.13') + self.assertEqual(text.search('t.', '1.0', exact=True), '1.13') + self.assertEqual(text.search('t.', '1.0', regexp=True), '1.10') + self.assertEqual(text.search('t.', '1.0', exact=True, regexp=True), '1.10') + + self.assertEqual(text.search('TEST', '1.0'), '') + self.assertEqual(text.search('TEST', '1.0', nocase=True), '1.10') + + self.assertEqual(text.search('.*line', '1.0', regexp=True), '2.0') + self.assertEqual(text.search('.*line', '1.0', regexp=True, nolinestop=True), '1.0') + + self.assertEqual(text.search('test', '1.0', '1.13'), '1.10') + self.assertEqual(text.search('test', '1.0', '1.13', strictlimits=True), '') + self.assertEqual(text.search('test', '1.0', '1.14', strictlimits=True), '1.10') + + var = tkinter.Variable(self.root) + self.assertEqual(text.search('test', '1.0', count=var), '1.10') + self.assertEqual(var.get(), 4 if self.wantobjects else '4') + + # TODO: Add test for elide=True + + def test_search_all(self): + text = self.text + + # pattern and index are obligatory arguments. + self.assertRaises(tkinter.TclError, text.search_all, None, '1.0') + self.assertRaises(tkinter.TclError, text.search_all, 'a', None) + self.assertRaises(tkinter.TclError, text.search_all, None, None) + + # Keyword-only arguments + self.assertRaises(TypeError, text.search_all, 'a', '1.0', 'end', None) + + # Invalid text index. + self.assertRaises(tkinter.TclError, text.search_all, '', 0) + self.assertRaises(tkinter.TclError, text.search_all, '', '') + self.assertRaises(tkinter.TclError, text.search_all, '', 'invalid') + self.assertRaises(tkinter.TclError, text.search_all, '', '1.0', 0) + self.assertRaises(tkinter.TclError, text.search_all, '', '1.0', '') + self.assertRaises(tkinter.TclError, text.search_all, '', '1.0', 'invalid') + + def eq(res, expected): + self.assertIsInstance(res, tuple) + self.assertEqual([str(i) for i in res], expected) + + text.insert('1.0', 'ababa\naba\n64-bit') + + eq(text.search_all('aba', '1.0'), ['1.0', '2.0']) + eq(text.search_all('aba', '1.0', 'end'), ['1.0', '2.0']) + eq(text.search_all('aba', '1.1', 'end'), ['1.2', '2.0']) + eq(text.search_all('aba', '1.1'), ['1.2', '2.0', '1.0']) + + res = text.search_all('', '1.0') # empty pattern + eq(res[:5], ['1.0', '1.1', '1.2', '1.3', '1.4']) + eq(res[-5:], ['3.2', '3.3', '3.4', '3.5', '3.6']) + eq(text.search_all('nonexistent', '1.0'), []) + eq(text.search_all('-bit', '1.0'), ['3.2']) # starts with a hyphen + + eq(text.search_all('aba', '1.0', 'end', forwards=True), ['1.0', '2.0']) + eq(text.search_all('aba', 'end', '1.0', backwards=True), ['2.0', '1.2']) + + eq(text.search_all('aba', '1.0', overlap=True), ['1.0', '1.2', '2.0']) + eq(text.search_all('aba', 'end', '1.0', overlap=True, backwards=True), ['2.0', '1.2', '1.0']) + + eq(text.search_all('aba', '1.0', exact=True), ['1.0', '2.0']) + eq(text.search_all('a.a', '1.0', exact=True), []) + eq(text.search_all('a.a', '1.0', regexp=True), ['1.0', '2.0']) + + eq(text.search_all('ABA', '1.0'), []) + eq(text.search_all('ABA', '1.0', nocase=True), ['1.0', '2.0']) + + eq(text.search_all('a.a', '1.0', regexp=True), ['1.0', '2.0']) + eq(text.search_all('a.a', '1.0', regexp=True, nolinestop=True), ['1.0', '1.4']) + + eq(text.search_all('aba', '1.0', '2.2'), ['1.0', '2.0']) + eq(text.search_all('aba', '1.0', '2.2', strictlimits=True), ['1.0']) + eq(text.search_all('aba', '1.0', '2.3', strictlimits=True), ['1.0', '2.0']) + + var = tkinter.Variable(self.root) + eq(text.search_all('aba', '1.0', count=var), ['1.0', '2.0']) + self.assertEqual(var.get(), (3, 3) if self.wantobjects else '3 3') + + # TODO: Add test for elide=True def test_count(self): text = self.text diff --git a/Lib/test/test_tkinter/test_variables.py b/Lib/test/test_tkinter/test_variables.py index 75b3a6934fc0e3..8733095ffb65f4 100644 --- a/Lib/test/test_tkinter/test_variables.py +++ b/Lib/test/test_tkinter/test_variables.py @@ -6,6 +6,7 @@ from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl, TclError) from test.support import ALWAYS_EQ +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractDefaultRootTest, tcl_version diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py index ff3f92e9b5ef83..1c400e970eb02d 100644 --- a/Lib/test/test_tkinter/test_widgets.py +++ b/Lib/test/test_tkinter/test_widgets.py @@ -4,6 +4,7 @@ import os from test.support import requires +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import (requires_tk, tk_version, get_tk_patchlevel, widget_eq, AbstractDefaultRootTest) @@ -25,12 +26,8 @@ def float_round(x): return float(round(x)) class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): - if tk_version < (9, 0): - _no_round = {'padx', 'pady'} - else: - _no_round = {'borderwidth', 'height', 'highlightthickness', 'padx', - 'pady', 'width'} - if tk_version < (9, 0): + _no_round = {'padx', 'pady'} + if tk_version < (8, 7): _clipped = {'highlightthickness'} else: _clipped = {'borderwidth', 'height', 'highlightthickness', 'padx', @@ -121,11 +118,6 @@ class FrameTest(AbstractToplevelTest, unittest.TestCase): 'highlightbackground', 'highlightcolor', 'highlightthickness', 'padx', 'pady', 'relief', 'takefocus', 'tile', 'visual', 'width', ) - if tk_version < (9, 0): - _no_round = {'padx', 'pady'} - else: - _no_round = {'borderwidth', 'height', 'highlightthickness', 'padx', - 'pady', 'width'} def create(self, **kwargs): return tkinter.Frame(self.root, **kwargs) @@ -141,11 +133,6 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): 'labelanchor', 'labelwidget', 'padx', 'pady', 'relief', 'takefocus', 'text', 'visual', 'width', ) - if tk_version < (9, 0): - _no_round = {'padx', 'pady'} - else: - _no_round = {'borderwidth', 'height', 'highlightthickness', 'padx', - 'pady', 'width'} def create(self, **kwargs): return tkinter.LabelFrame(self.root, **kwargs) @@ -166,11 +153,19 @@ def test_configure_labelwidget(self): # Label, Button, Checkbutton, Radiobutton, MenuButton class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests): _rounds_pixels = False - if tk_version < (9, 0): + if tk_version < (8, 7): _clipped = {} + elif tk_version < (9, 0): + _clipped = {'borderwidth', 'height', 'highlightthickness', 'padx', 'pady', 'width'} else: - _clipped = {'borderwidth', 'insertborderwidth', 'highlightthickness', - 'padx', 'pady'} + _clipped = {'borderwidth', 'height', 'highlightthickness', + 'insertborderwidth', 'padx', 'pady', 'width'} + + def setUp(self): + super().setUp() + if tk_version[:2] == (9, 0) and get_tk_patchlevel(self.root) < (9, 0, 2): + self._clipped = self._clipped - {'height', 'width'} + @add_configure_tests(StandardOptionsTests) class LabelTest(AbstractLabelTest, unittest.TestCase): @@ -200,6 +195,11 @@ class ButtonTest(AbstractLabelTest, unittest.TestCase): 'repeatdelay', 'repeatinterval', 'state', 'takefocus', 'text', 'textvariable', 'underline', 'width', 'wraplength') + if tk_version < (8, 7): + _clipped = {} + else: + _clipped = {'borderwidth', 'height', 'highlightthickness', + 'padx', 'pady', 'width'} def create(self, **kwargs): return tkinter.Button(self.root, **kwargs) @@ -300,10 +300,17 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase): 'underline', 'width', 'wraplength', ) _rounds_pixels = (tk_version < (9, 0)) - if tk_version < (9, 0): + if tk_version < (8, 7): _clipped = {'highlightthickness', 'padx', 'pady'} + elif tk_version < (9, 0): + _clipped = {'borderwidth', 'highlightthickness', 'padx', 'pady'} else: - _clipped ={ 'insertborderwidth', 'highlightthickness', 'padx', 'pady'} + _clipped = {'borderwidth', 'highlightthickness', 'insertborderwidth', 'padx', 'pady'} + + def setUp(self): + super().setUp() + if tk_version[:2] == (9, 0) and get_tk_patchlevel(self.root) < (9, 0, 1): + self._clipped = self._clipped - {'borderwidth'} def create(self, **kwargs): return tkinter.Menubutton(self.root, **kwargs) @@ -315,13 +322,17 @@ def test_configure_direction(self): def test_configure_height(self): widget = self.create() - self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str) + if tk_version < (8, 7) or (tk_version[:2] == (9, 0) and get_tk_patchlevel(self.root) < (9, 0, 1)): + conv = str + else: + conv = False + self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=conv) def test_configure_image(self): widget = self.create() image = tkinter.PhotoImage(master=self.root, name='image1') self.checkParam(widget, 'image', image, conv=str) - if tk_version < (9, 0): + if tk_version < (8, 7): errmsg = 'image "spam" doesn\'t exist' else: errmsg = 'image "spam" does not exist' @@ -342,7 +353,11 @@ def test_configure_menu(self): def test_configure_width(self): widget = self.create() - self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str) + if tk_version < (8, 7) or (tk_version[:2] == (9, 0) and get_tk_patchlevel(self.root) < (9, 0, 1)): + conv = str + else: + conv = False + self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=conv) class OptionMenuTest(MenubuttonTest, unittest.TestCase): @@ -361,12 +376,11 @@ def test_specify_name(self): @add_configure_tests(IntegerSizeTests, StandardOptionsTests) class EntryTest(AbstractWidgetTest, unittest.TestCase): - _rounds_pixels = (tk_version < (9, 0)) - if tk_version < (9, 0): + if tk_version < (8, 7): _clipped = {'highlightthickness'} else: - _clipped = {'highlightthickness', 'borderwidth', 'insertborderwidth', - 'selectborderwidth'} + _clipped = {'borderwidth', 'highlightthickness', 'insertborderwidth', + 'insertwidth', 'selectborderwidth'} OPTIONS = ( 'background', 'borderwidth', 'cursor', @@ -392,23 +406,20 @@ def test_configure_disabledbackground(self): def test_configure_insertborderwidth(self): widget = self.create(insertwidth=100) self.checkPixelsParam(widget, 'insertborderwidth', - 0, 1.3, 2.6, 6, '10p') - self.checkParam(widget, 'insertborderwidth', -2) + 0, 1.3, 2.6, 6, -2, '10p') # insertborderwidth is bounded above by a half of insertwidth. - expected = 100 // 2 if tk_version < (9, 0) else 60 + expected = 100 // 2 if tk_version < (8, 7) else 60 self.checkParam(widget, 'insertborderwidth', 60, expected=expected) def test_configure_insertwidth(self): widget = self.create() - self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p') - if tk_version < (9, 0): + self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, 0.9, '10p') + if tk_version < (8, 7): + self.checkParam(widget, 'insertwidth', 0, expected=2) self.checkParam(widget, 'insertwidth', 0.1, expected=2) self.checkParam(widget, 'insertwidth', -2, expected=2) - self.checkParam(widget, 'insertwidth', 0.9, expected=1) else: - self.checkParam(widget, 'insertwidth', 0.1) - self.checkParam(widget, 'insertwidth', -2, expected=0) - self.checkParam(widget, 'insertwidth', 0.9) + self.checkPixelsParam(widget, 'insertwidth', 0, 0.1, -2) def test_configure_invalidcommand(self): widget = self.create() @@ -551,11 +562,22 @@ def test_configure_values(self): # XXX widget = self.create() self.assertEqual(widget['values'], '') - self.checkParam(widget, 'values', 'mon tue wed thur') + if tk_version < (8, 7) or (tk_version[:2] == (9, 0) and get_tk_patchlevel(self.root) < (9, 0, 1)): + expected = 'mon tue wed thur' + else: + expected = ('mon', 'tue', 'wed', 'thur') + self.checkParam(widget, 'values', 'mon tue wed thur', + expected=expected) self.checkParam(widget, 'values', ('mon', 'tue', 'wed', 'thur'), - expected='mon tue wed thur') + expected=expected) + + if tk_version < (8, 7) or (tk_version[:2] == (9, 0) and get_tk_patchlevel(self.root) < (9, 0, 1)): + expected = '42 3.14 {} {any string}' + else: + expected = (42, 3.14, '', 'any string') self.checkParam(widget, 'values', (42, 3.14, '', 'any string'), - expected='42 3.14 {} {any string}') + expected=expected) + self.checkParam(widget, 'values', '') def test_configure_wrap(self): @@ -618,9 +640,20 @@ class TextTest(AbstractWidgetTest, unittest.TestCase): 'tabs', 'tabstyle', 'takefocus', 'undo', 'width', 'wrap', 'xscrollcommand', 'yscrollcommand', ) - _rounds_pixels = (tk_version < (9, 0)) _no_round = {'selectborderwidth'} - _clipped = {'highlightthickness'} + if tk_version < (9, 0): + _clipped = {'highlightthickness', 'spacing1', 'spacing2', 'spacing3'} + else: + _clipped = {'borderwidth', 'height', 'highlightthickness', + 'insertborderwidth', 'insertwidth', 'padx', 'pady', + 'selectborderwidth', 'spacing1', 'spacing2', 'spacing3'} + + def setUp(self): + super().setUp() + if tk_version[:2] == (9, 0) and get_tk_patchlevel(self.root) < (9, 0, 2): + self._clipped = self._clipped - {'borderwidth', 'height', 'padx', 'pady'} + if tk_version[:2] == (9, 0) and get_tk_patchlevel(self.root) < (9, 0, 1): + self._clipped = self._clipped - {'insertborderwidth', 'insertwidth', 'selectborderwidth'} def create(self, **kwargs): return tkinter.Text(self.root, **kwargs) @@ -649,10 +682,11 @@ def test_configure_endline(self): def test_configure_height(self): widget = self.create() self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c') - self.checkParam(widget, 'height', -100, - expected=1 if tk_version < (9, 0) else -100) - self.checkParam(widget, 'height', 0, - expected=1 if tk_version < (9, 0) else 0 ) + if tk_version < (9, 0): + self.checkParam(widget, 'height', 0, expected=1) + self.checkParam(widget, 'height', -100, expected=1) + else: + self.checkPixelsParam(widget, 'height', 0, -100) def test_configure_maxundo(self): widget = self.create() @@ -668,25 +702,17 @@ def test_configure_insertunfocussed(self): self.checkEnumParam(widget, 'insertunfocussed', 'hollow', 'none', 'solid') - def test_configure_selectborderwidth(self): - widget = self.create() - self.checkPixelsParam(widget, 'selectborderwidth', - 1.3, 2.6, -2, '10p', conv=False) - def test_configure_spacing1(self): widget = self.create() - self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, '0.5c') - self.checkParam(widget, 'spacing1', -5, expected=0) + self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, -5, '0.5c') def test_configure_spacing2(self): widget = self.create() - self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, '0.1c') - self.checkParam(widget, 'spacing2', -1, expected=0) + self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, -1, '0.1c') def test_configure_spacing3(self): widget = self.create() - self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c') - self.checkParam(widget, 'spacing3', -10, expected=0) + self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, -10, '0.5c') def test_configure_startline(self): widget = self.create() @@ -759,17 +785,22 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase): 'xscrollcommand', 'xscrollincrement', 'yscrollcommand', 'yscrollincrement', 'width', ) - _rounds_pixels = True - if tk_version < (9, 0): - _noround = {} + if tk_version < (8, 7): _clipped = {'highlightthickness'} else: - _no_round = {'borderwidth', 'height', 'highlightthickness', 'width', - 'xscrollincrement', 'yscrollincrement'} - _clipped = {'borderwidth', 'height', 'highlightthickness', 'width', - 'xscrollincrement', 'yscrollincrement'} + _clipped = {'borderwidth', 'height', 'highlightthickness', + 'insertborderwidth', 'insertwidth', 'selectborderwidth', + 'width', 'xscrollincrement', 'yscrollincrement'} _stringify = True + def setUp(self): + super().setUp() + if tk_version[:2] == (9, 0) and get_tk_patchlevel(self.root) < (9, 0, 1): + self._rounds_pixels = True + self._no_round = {'borderwidth', 'height', 'highlightthickness', + 'width', 'xscrollincrement', 'yscrollincrement'} + self._clipped = self._clipped - {'insertborderwidth', 'insertwidth', 'selectborderwidth'} + def create(self, **kwargs): return tkinter.Canvas(self.root, **kwargs) @@ -916,7 +947,6 @@ def test_create_line(self): def test_create_polygon(self): c = self.create() - tk87 = tk_version >= (8, 7) # In Tk < 8.7 polygons are filled, but has no outline by default. # This affects its size, so always explicitly specify outline. i1 = c.create_polygon(20, 30, 40, 50, 60, 10, outline='red') @@ -1021,11 +1051,10 @@ class ListboxTest(AbstractWidgetTest, unittest.TestCase): 'selectmode', 'setgrid', 'state', 'takefocus', 'width', 'xscrollcommand', 'yscrollcommand', ) - _rounds_pixels = (tk_version < (9, 0)) - if tk_version < (9, 0): + if tk_version < (8, 7): _clipped = {'highlightthickness'} else: - _clipped = { 'borderwidth', 'highlightthickness', 'selectborderwidth'} + _clipped = {'borderwidth', 'highlightthickness', 'selectborderwidth'} def create(self, **kwargs): return tkinter.Listbox(self.root, **kwargs) @@ -1163,7 +1192,6 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase): 'resolution', 'showvalue', 'sliderlength', 'sliderrelief', 'state', 'takefocus', 'tickinterval', 'to', 'troughcolor', 'variable', 'width', ) - _rounds_pixels = (tk_version < (9, 0)) _clipped = {'highlightthickness'} default_orient = 'vertical' @@ -1233,14 +1261,13 @@ class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): 'repeatdelay', 'repeatinterval', 'takefocus', 'troughcolor', 'width', ) - _rounds_pixels = True - if tk_version >= (9, 0): - _no_round = {'borderwidth', 'elementborderwidth', 'highlightthickness', - 'width'} - if tk_version < (9, 0): + if tk_version < (8, 7): _clipped = {'highlightthickness'} + elif tk_version < (9, 0): + _clipped = {'borderwidth', 'elementborderwidth', 'highlightthickness'} else: - _clipped = {'borderwidth', 'highlightthickness', 'width'} + _clipped = {'borderwidth', 'elementborderwidth', 'highlightthickness', 'width'} + _clipped_to_default = {'elementborderwidth'} _stringify = True default_orient = 'vertical' @@ -1249,9 +1276,7 @@ def create(self, **kwargs): def test_configure_elementborderwidth(self): widget = self.create() - self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, '1m') - expected = self._default_pixels if tk_version >= (8, 7) else -2 - self.checkParam(widget, 'elementborderwidth', -2, expected=expected) + self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, -2, '1m') def test_configure_orient(self): widget = self.create() @@ -1278,7 +1303,7 @@ def test_set(self): self.assertRaises(TypeError, sb.set, 0.6, 0.7, 0.8) -@add_configure_tests(StandardOptionsTests) +@add_configure_tests(PixelSizeTests, StandardOptionsTests) class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'background', 'borderwidth', 'cursor', @@ -1289,14 +1314,8 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): 'sashcursor', 'sashpad', 'sashrelief', 'sashwidth', 'showhandle', 'width', ) - _rounds_pixels = True - if tk_version < (9, 0): - _no_round = {'handlesize', 'height', 'proxyborderwidth', 'sashwidth', - 'selectborderwidth', 'width'} - else: - _no_round = {'borderwidth', 'handlepad', 'handlesize', 'height', - 'proxyborderwidth', 'sashpad', 'sashwidth', - 'selectborderwidth', 'width'} + _no_round = {'handlesize', 'height', 'proxyborderwidth', 'sashwidth', + 'selectborderwidth', 'width'} _clipped = {} default_orient = 'horizontal' @@ -1309,13 +1328,7 @@ def test_configure_handlepad(self): def test_configure_handlesize(self): widget = self.create() - self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m', - conv=False) - - def test_configure_height(self): - widget = self.create() - self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i', - conv=False) + self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m') def test_configure_opaqueresize(self): widget = self.create() @@ -1330,8 +1343,7 @@ def test_configure_proxybackground(self): def test_configure_proxyborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'proxyborderwidth', - 0, 1.3, 2.9, 6, -2, '10p', - conv=False) + 0, 1.3, 2.9, 6, -2, '10p') @requires_tk(8, 6, 5) def test_configure_proxyrelief(self): @@ -1353,18 +1365,12 @@ def test_configure_sashrelief(self): def test_configure_sashwidth(self): widget = self.create() - self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m', - conv=False) + self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m') def test_configure_showhandle(self): widget = self.create() self.checkBooleanParam(widget, 'showhandle') - def test_configure_width(self): - widget = self.create() - self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i', - conv=False) - def create2(self): p = self.create() b = tkinter.Button(p) @@ -1546,12 +1552,12 @@ class MessageTest(AbstractWidgetTest, unittest.TestCase): 'justify', 'padx', 'pady', 'relief', 'takefocus', 'text', 'textvariable', 'width', ) - _rounds_pixels = (tk_version < (9, 0)) _no_round = {'padx', 'pady'} - if tk_version < (9, 0): + if tk_version < (8, 7): _clipped = {'highlightthickness'} else: - _clipped = {'borderwidth', 'highlightthickness', 'padx', 'pady'} + _clipped = {'borderwidth', 'highlightthickness', 'padx', 'pady', 'width'} + _clipped_to_default = {'padx', 'pady'} def create(self, **kwargs): return tkinter.Message(self.root, **kwargs) @@ -1560,24 +1566,6 @@ def test_configure_aspect(self): widget = self.create() self.checkIntegerParam(widget, 'aspect', 250, 0, -300) - def test_configure_padx(self): - widget = self.create() - self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m') - expected = -2 if tk_version < (9, 0) else self._default_pixels - self.checkParam(widget, 'padx', -2, expected=expected) - - def test_configure_pady(self): - widget = self.create() - self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m') - expected = -2 if tk_version < (9, 0) else self._default_pixels - self.checkParam(widget, 'pady', -2, expected=expected) - - def test_configure_width(self): - widget = self.create() - self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, 0, '5i') - expected = 0 if tk_version >= (8, 7) else -402 - self.checkParam(widget, 'width', -402, expected=expected) - class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): diff --git a/Lib/test/test_tkinter/widget_tests.py b/Lib/test/test_tkinter/widget_tests.py index f518925e994e90..94244a8b3fe244 100644 --- a/Lib/test/test_tkinter/widget_tests.py +++ b/Lib/test/test_tkinter/widget_tests.py @@ -12,11 +12,12 @@ # borderwidth = bd class AbstractWidgetTest(AbstractTkTest): - _default_pixels = '' # Value for unset pixel options. - _rounds_pixels = True # True if some pixel options are rounded. - _no_round = {} # Pixel options which are not rounded nonetheless + _default_pixels = '' if tk_version >= (9, 0) else -1 # Value for unset pixel options. + _rounds_pixels = (tk_version < (9, 0)) # True if some pixel options are rounded. + _no_round = set() # Pixel options which are not rounded nonetheless _stringify = False # Whether to convert tuples to strings _allow_empty_justify = False + _clipped_to_default = set() @property def scaling(self): @@ -43,9 +44,12 @@ def checkParam(self, widget, name, value, *, expected=_sentinel, widget[name] = value if expected is _sentinel: expected = value - if name in self._clipped: - if not isinstance(expected, str): - expected = max(expected, 0) + if name in self._clipped: + if not isinstance(expected, str) and expected < 0: + if tk_version >= (8, 7) and name in self._clipped_to_default: + expected = self._default_pixels + else: + expected = 0 if conv: expected = conv(expected) if self._stringify or not self.wantobjects: @@ -143,10 +147,10 @@ def checkEnumParam(self, widget, name, *values, self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) def checkPixelsParam(self, widget, name, *values, conv=None, **kwargs): - if not self._rounds_pixels or name in self._no_round: - conv = False - elif conv != str: - conv = round + if conv is None: + if self._rounds_pixels and name not in self._no_round: + conv = round + alow_neg = tk_version < (9, 1) for value in values: expected = _sentinel conv1 = conv @@ -156,6 +160,9 @@ def checkPixelsParam(self, widget, name, *values, conv=None, **kwargs): if conv1 and conv1 is not str: expected = pixels_conv(value) * self.scaling conv1 = round + elif not alow_neg and isinstance(value, (int, float)) and value < 0: + self.checkInvalidParam(widget, name, value) + continue self.checkParam(widget, name, value, expected=expected, conv=conv1, **kwargs) errmsg = '(bad|expected) screen distance ((or "" )?but got )?"{}"' @@ -177,7 +184,7 @@ def checkReliefParam(self, widget, name, *, allow_empty=False): def checkImageParam(self, widget, name): image = tkinter.PhotoImage(master=self.root, name='image1') self.checkParam(widget, name, image, conv=str) - if tk_version < (9, 0): + if tk_version < (8, 7): errmsg = 'image "spam" doesn\'t exist' else: errmsg = 'image "spam" does not exist' @@ -246,8 +253,8 @@ def test_configure_activeborderwidth(self): def test_configure_borderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'borderwidth', - 0, 1.3, 2.6, 6, '10p') - self.checkParam(widget, 'borderwidth', -2) + 0, 1.3, 2.6, 6, -2, '10p') + if 'bd' in self.OPTIONS: self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, '10p') self.checkParam(widget, 'bd', -2, expected=expected) @@ -255,14 +262,11 @@ def test_configure_borderwidth(self): def test_configure_highlightthickness(self): widget = self.create() self.checkPixelsParam(widget, 'highlightthickness', - 0, 1.3, 2.6, 6, '10p') - self.checkParam(widget, 'highlightthickness', -2) + 0, 1.3, 2.6, 6, -2, '10p') def test_configure_insertborderwidth(self): widget = self.create() - self.checkPixelsParam(widget, 'insertborderwidth', - 0, 1.3, 2.6, 6, '10p') - self.checkParam(widget, 'insertborderwidth', -2) + self.checkPixelsParam(widget, 'insertborderwidth', 0, 1.3, 2.6, 6, -2, '10p') def test_configure_insertwidth(self): widget = self.create() @@ -270,18 +274,17 @@ def test_configure_insertwidth(self): def test_configure_padx(self): widget = self.create() - self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m') - self.checkParam(widget, 'padx', -2) + self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m') def test_configure_pady(self): widget = self.create() - self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m') - self.checkParam(widget, 'pady', -2) + self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m') def test_configure_selectborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p') + class StandardOptionsTests(PixelOptionsTests): STANDARD_OPTIONS = ( 'activebackground', 'activeforeground', diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 865e0c5b40ddd3..ab53a20cff5539 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1216,6 +1216,23 @@ def test_multiline_non_ascii_fstring_with_expr(self): FSTRING_END "\'\'\'" (3, 1) (3, 4) """) + # gh-139516, the '\n' is explicit to ensure no trailing whitespace which would invalidate the test + self.check_tokenize('''f"{f(a=lambda: 'à'\n)}"''', """\ + FSTRING_START \'f"\' (1, 0) (1, 2) + OP '{' (1, 2) (1, 3) + NAME 'f' (1, 3) (1, 4) + OP '(' (1, 4) (1, 5) + NAME 'a' (1, 5) (1, 6) + OP '=' (1, 6) (1, 7) + NAME 'lambda' (1, 7) (1, 13) + OP ':' (1, 13) (1, 14) + STRING "\'à\'" (1, 15) (1, 18) + NL '\\n' (1, 18) (1, 19) + OP ')' (2, 0) (2, 1) + OP '}' (2, 1) (2, 2) + FSTRING_END \'"\' (2, 2) (2, 3) + """) + class GenerateTokensTest(TokenizeTest): def check_tokenize(self, s, expected): # Format the tokens in s in a table format. @@ -1346,7 +1363,8 @@ def readline(): def test_no_bom_no_encoding_cookie(self): lines = ( - b'# something\n', + b'#!/home/\xc3\xa4/bin/python\n', + b'# something \xe2\x82\xac\n', b'print(something)\n', b'do_something(else)\n' ) @@ -1354,16 +1372,54 @@ def test_no_bom_no_encoding_cookie(self): self.assertEqual(encoding, 'utf-8') self.assertEqual(consumed_lines, list(lines[:2])) + def test_no_bom_no_encoding_cookie_first_line_error(self): + lines = ( + b'#!/home/\xa4/bin/python\n\n', + b'print(something)\n', + b'do_something(else)\n' + ) + with self.assertRaises(SyntaxError): + tokenize.detect_encoding(self.get_readline(lines)) + + def test_no_bom_no_encoding_cookie_second_line_error(self): + lines = ( + b'#!/usr/bin/python\n', + b'# something \xe2\n', + b'print(something)\n', + b'do_something(else)\n' + ) + with self.assertRaises(SyntaxError): + tokenize.detect_encoding(self.get_readline(lines)) + def test_bom_no_cookie(self): lines = ( - b'\xef\xbb\xbf# something\n', + b'\xef\xbb\xbf#!/home/\xc3\xa4/bin/python\n', b'print(something)\n', b'do_something(else)\n' ) encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) self.assertEqual(encoding, 'utf-8-sig') self.assertEqual(consumed_lines, - [b'# something\n', b'print(something)\n']) + [b'#!/home/\xc3\xa4/bin/python\n', b'print(something)\n']) + + def test_bom_no_cookie_first_line_error(self): + lines = ( + b'\xef\xbb\xbf#!/home/\xa4/bin/python\n', + b'print(something)\n', + b'do_something(else)\n' + ) + with self.assertRaises(SyntaxError): + tokenize.detect_encoding(self.get_readline(lines)) + + def test_bom_no_cookie_second_line_error(self): + lines = ( + b'\xef\xbb\xbf#!/usr/bin/python\n', + b'# something \xe2\n', + b'print(something)\n', + b'do_something(else)\n' + ) + with self.assertRaises(SyntaxError): + tokenize.detect_encoding(self.get_readline(lines)) def test_cookie_first_line_no_bom(self): lines = ( @@ -1439,16 +1495,60 @@ def test_cookie_second_line_noncommented_first_line(self): expected = [b"print('\xc2\xa3')\n"] self.assertEqual(consumed_lines, expected) - def test_cookie_second_line_commented_first_line(self): + def test_first_non_utf8_coding_line(self): lines = ( - b"#print('\xc2\xa3')\n", - b'# vim: set fileencoding=iso8859-15 :\n', - b"print('\xe2\x82\xac')\n" + b'#coding:iso-8859-15 \xa4\n', + b'print(something)\n' ) encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) - self.assertEqual(encoding, 'iso8859-15') - expected = [b"#print('\xc2\xa3')\n", b'# vim: set fileencoding=iso8859-15 :\n'] - self.assertEqual(consumed_lines, expected) + self.assertEqual(encoding, 'iso-8859-15') + self.assertEqual(consumed_lines, list(lines[:1])) + + def test_first_utf8_coding_line_error(self): + lines = ( + b'#coding:ascii \xc3\xa4\n', + b'print(something)\n' + ) + with self.assertRaises(SyntaxError): + tokenize.detect_encoding(self.get_readline(lines)) + + def test_second_non_utf8_coding_line(self): + lines = ( + b'#!/usr/bin/python\n', + b'#coding:iso-8859-15 \xa4\n', + b'print(something)\n' + ) + encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) + self.assertEqual(encoding, 'iso-8859-15') + self.assertEqual(consumed_lines, list(lines[:2])) + + def test_second_utf8_coding_line_error(self): + lines = ( + b'#!/usr/bin/python\n', + b'#coding:ascii \xc3\xa4\n', + b'print(something)\n' + ) + with self.assertRaises(SyntaxError): + tokenize.detect_encoding(self.get_readline(lines)) + + def test_non_utf8_shebang(self): + lines = ( + b'#!/home/\xa4/bin/python\n', + b'#coding:iso-8859-15\n', + b'print(something)\n' + ) + encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) + self.assertEqual(encoding, 'iso-8859-15') + self.assertEqual(consumed_lines, list(lines[:2])) + + def test_utf8_shebang_error(self): + lines = ( + b'#!/home/\xc3\xa4/bin/python\n', + b'#coding:ascii\n', + b'print(something)\n' + ) + with self.assertRaises(SyntaxError): + tokenize.detect_encoding(self.get_readline(lines)) def test_cookie_second_line_empty_first_line(self): lines = ( @@ -1461,6 +1561,70 @@ def test_cookie_second_line_empty_first_line(self): expected = [b'\n', b'# vim: set fileencoding=iso8859-15 :\n'] self.assertEqual(consumed_lines, expected) + def test_cookie_third_line(self): + lines = ( + b'#!/home/\xc3\xa4/bin/python\n', + b'# something\n', + b'# vim: set fileencoding=ascii :\n', + b'print(something)\n', + b'do_something(else)\n' + ) + encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) + self.assertEqual(encoding, 'utf-8') + self.assertEqual(consumed_lines, list(lines[:2])) + + def test_double_coding_line(self): + # If the first line matches the second line is ignored. + lines = ( + b'#coding:iso8859-15\n', + b'#coding:latin1\n', + b'print(something)\n' + ) + encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) + self.assertEqual(encoding, 'iso8859-15') + self.assertEqual(consumed_lines, list(lines[:1])) + + def test_double_coding_same_line(self): + lines = ( + b'#coding:iso8859-15 coding:latin1\n', + b'print(something)\n' + ) + encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) + self.assertEqual(encoding, 'iso8859-15') + self.assertEqual(consumed_lines, list(lines[:1])) + + def test_double_coding_utf8(self): + lines = ( + b'#coding:utf-8\n', + b'#coding:latin1\n', + b'print(something)\n' + ) + encoding, consumed_lines = tokenize.detect_encoding(self.get_readline(lines)) + self.assertEqual(encoding, 'utf-8') + self.assertEqual(consumed_lines, list(lines[:1])) + + def test_nul_in_first_coding_line(self): + lines = ( + b'#coding:iso8859-15\x00\n', + b'\n', + b'\n', + b'print(something)\n' + ) + with self.assertRaisesRegex(SyntaxError, + "source code cannot contain null bytes"): + tokenize.detect_encoding(self.get_readline(lines)) + + def test_nul_in_second_coding_line(self): + lines = ( + b'#!/usr/bin/python\n', + b'#coding:iso8859-15\x00\n', + b'\n', + b'print(something)\n' + ) + with self.assertRaisesRegex(SyntaxError, + "source code cannot contain null bytes"): + tokenize.detect_encoding(self.get_readline(lines)) + def test_latin1_normalization(self): # See get_normal_name() in Parser/tokenizer/helpers.c. encodings = ("latin-1", "iso-8859-1", "iso-latin-1", "latin-1-unix", @@ -1485,7 +1649,6 @@ def test_syntaxerror_latin1(self): readline = self.get_readline(lines) self.assertRaises(SyntaxError, tokenize.detect_encoding, readline) - def test_utf8_normalization(self): # See get_normal_name() in Parser/tokenizer/helpers.c. encodings = ("utf-8", "utf-8-mac", "utf-8-unix") @@ -3020,6 +3183,7 @@ def get_tokens(string): f'__{ x:d }__'""", + " a\n\x00", ]: with self.subTest(case=case): self.assertRaises(tokenize.TokenError, get_tokens, case) @@ -3162,6 +3326,7 @@ def test_newline_at_the_end_of_buffer(self): run_test_script(file_name) +@support.force_not_colorized_test_class class CommandLineTest(unittest.TestCase): def setUp(self): self.filename = tempfile.mktemp() diff --git a/Lib/test/test_tomllib/burntsushi.py b/Lib/test/test_tomllib/burntsushi.py index 71228c65369572..0ec50eb1a98a45 100644 --- a/Lib/test/test_tomllib/burntsushi.py +++ b/Lib/test/test_tomllib/burntsushi.py @@ -7,19 +7,8 @@ import datetime from typing import Any -# Aliases for converting TOML compliance format [1] to BurntSushi format [2] -# [1] https://github.com/toml-lang/compliance/blob/db7c3211fda30ff9ddb10292f4aeda7e2e10abc4/docs/json-encoding.md # noqa: E501 -# [2] https://github.com/BurntSushi/toml-test/blob/4634fdf3a6ecd6aaea5f4cdcd98b2733c2694993/README.md # noqa: E501 -_aliases = { - "boolean": "bool", - "offset datetime": "datetime", - "local datetime": "datetime-local", - "local date": "date-local", - "local time": "time-local", -} - - -def convert(obj): # noqa: C901 + +def convert(obj): if isinstance(obj, str): return {"type": "string", "value": obj} elif isinstance(obj, bool): @@ -53,31 +42,25 @@ def convert(obj): # noqa: C901 def normalize(obj: Any) -> Any: """Normalize test objects. - This normalizes primitive values (e.g. floats), and also converts from - TOML compliance format [1] to BurntSushi format [2]. - - [1] https://github.com/toml-lang/compliance/blob/db7c3211fda30ff9ddb10292f4aeda7e2e10abc4/docs/json-encoding.md # noqa: E501 - [2] https://github.com/BurntSushi/toml-test/blob/4634fdf3a6ecd6aaea5f4cdcd98b2733c2694993/README.md # noqa: E501 - """ + This normalizes primitive values (e.g. floats).""" if isinstance(obj, list): return [normalize(item) for item in obj] if isinstance(obj, dict): if "type" in obj and "value" in obj: type_ = obj["type"] - norm_type = _aliases.get(type_, type_) value = obj["value"] - if norm_type == "float": + if type_ == "float": norm_value = _normalize_float_str(value) - elif norm_type in {"datetime", "datetime-local"}: + elif type_ in {"datetime", "datetime-local"}: norm_value = _normalize_datetime_str(value) - elif norm_type == "time-local": + elif type_ == "time-local": norm_value = _normalize_localtime_str(value) else: norm_value = value - if norm_type == "array": + if type_ == "array": return [normalize(item) for item in value] - return {"type": norm_type, "value": norm_value} + return {"type": type_, "value": norm_value} return {k: normalize(v) for k, v in obj.items()} raise AssertionError("Burntsushi fixtures should be dicts/lists only") diff --git a/Lib/test/test_tomllib/data/valid/dates-and-times/datetimes.json b/Lib/test/test_tomllib/data/valid/dates-and-times/datetimes.json index 99aca873480ec3..09a7c083d14f88 100644 --- a/Lib/test/test_tomllib/data/valid/dates-and-times/datetimes.json +++ b/Lib/test/test_tomllib/data/valid/dates-and-times/datetimes.json @@ -1,4 +1,5 @@ { "local-dt": {"type":"datetime-local","value":"1988-10-27t01:01:01"}, + "local-dt-no-seconds": {"type":"datetime-local","value":"2025-04-18t20:05:00"}, "zulu-dt": {"type":"datetime","value":"1988-10-27t01:01:01z"} } diff --git a/Lib/test/test_tomllib/data/valid/dates-and-times/datetimes.toml b/Lib/test/test_tomllib/data/valid/dates-and-times/datetimes.toml index cf84159de46fd8..5dc4b318256198 100644 --- a/Lib/test/test_tomllib/data/valid/dates-and-times/datetimes.toml +++ b/Lib/test/test_tomllib/data/valid/dates-and-times/datetimes.toml @@ -1,2 +1,3 @@ local-dt=1988-10-27t01:01:01 +local-dt-no-seconds=2025-04-18T20:05 zulu-dt=1988-10-27t01:01:01z diff --git a/Lib/test/test_tomllib/data/valid/dates-and-times/localtime.json b/Lib/test/test_tomllib/data/valid/dates-and-times/localtime.json index 4d96abcbc799e6..1f66348b237161 100644 --- a/Lib/test/test_tomllib/data/valid/dates-and-times/localtime.json +++ b/Lib/test/test_tomllib/data/valid/dates-and-times/localtime.json @@ -1,2 +1,4 @@ {"t": - {"type":"time-local","value":"00:00:00.999999"}} + {"type":"time-local","value":"00:00:00.999999"}, +"t2": + {"type":"time-local","value":"00:00:00"}} diff --git a/Lib/test/test_tomllib/data/valid/dates-and-times/localtime.toml b/Lib/test/test_tomllib/data/valid/dates-and-times/localtime.toml index 87547c1cf3bd89..6579b30c94f8d6 100644 --- a/Lib/test/test_tomllib/data/valid/dates-and-times/localtime.toml +++ b/Lib/test/test_tomllib/data/valid/dates-and-times/localtime.toml @@ -1 +1,2 @@ -t=00:00:00.99999999999999 \ No newline at end of file +t=00:00:00.99999999999999 +t2=00:00 \ No newline at end of file diff --git a/Lib/test/test_tomllib/data/valid/empty-inline-table.json b/Lib/test/test_tomllib/data/valid/inline-table/empty-inline-table.json similarity index 100% rename from Lib/test/test_tomllib/data/valid/empty-inline-table.json rename to Lib/test/test_tomllib/data/valid/inline-table/empty-inline-table.json diff --git a/Lib/test/test_tomllib/data/valid/empty-inline-table.toml b/Lib/test/test_tomllib/data/valid/inline-table/empty-inline-table.toml similarity index 100% rename from Lib/test/test_tomllib/data/valid/empty-inline-table.toml rename to Lib/test/test_tomllib/data/valid/inline-table/empty-inline-table.toml diff --git a/Lib/test/test_tomllib/data/valid/inline-table/multiline-inline-table.json b/Lib/test/test_tomllib/data/valid/inline-table/multiline-inline-table.json new file mode 100644 index 00000000000000..d253884fbac9f0 --- /dev/null +++ b/Lib/test/test_tomllib/data/valid/inline-table/multiline-inline-table.json @@ -0,0 +1,35 @@ +{ + "multiline": { + "a": { + "type": "integer", + "value": "1" + }, + "b": { + "type": "integer", + "value": "2" + }, + "c": [ + { + "type": "integer", + "value": "1" + }, + { + "type": "integer", + "value": "2" + }, + { + "type": "integer", + "value": "3" + } + ], + "d": { + "type": "integer", + "value": "3" + }, + "e": { + "type": "integer", + "value": "4" + }, + "f": {} + } +} \ No newline at end of file diff --git a/Lib/test/test_tomllib/data/valid/inline-table/multiline-inline-table.toml b/Lib/test/test_tomllib/data/valid/inline-table/multiline-inline-table.toml new file mode 100644 index 00000000000000..6a98a08a576a06 --- /dev/null +++ b/Lib/test/test_tomllib/data/valid/inline-table/multiline-inline-table.toml @@ -0,0 +1,12 @@ +multiline = { + "a" = 1, "b" = 2, + c = [ + 1, + 2, + 3, + ],# comment + d = 3, + e = 4, f = { + # comment + }, +} diff --git a/Lib/test/test_tomllib/data/valid/multiline-basic-str/replacements.json b/Lib/test/test_tomllib/data/valid/multiline-basic-str/replacements.json new file mode 100644 index 00000000000000..699f556248d880 --- /dev/null +++ b/Lib/test/test_tomllib/data/valid/multiline-basic-str/replacements.json @@ -0,0 +1,6 @@ +{ + "escape": {"type":"string","value":"\u001B"}, + "tab": {"type":"string","value":"\t"}, + "upper-j": {"type":"string","value":"J"}, + "upper-j-2": {"type":"string","value":"J"} +} diff --git a/Lib/test/test_tomllib/data/valid/multiline-basic-str/replacements.toml b/Lib/test/test_tomllib/data/valid/multiline-basic-str/replacements.toml new file mode 100644 index 00000000000000..fa5647e5938ee4 --- /dev/null +++ b/Lib/test/test_tomllib/data/valid/multiline-basic-str/replacements.toml @@ -0,0 +1,4 @@ +escape = "\e" +tab = "\x09" +upper-j = "\x4a" +upper-j-2 = "\x4A" diff --git a/Lib/test/test_tomllib/test_data.py b/Lib/test/test_tomllib/test_data.py index 3483d93022b01b..9db1a37466e7bf 100644 --- a/Lib/test/test_tomllib/test_data.py +++ b/Lib/test/test_tomllib/test_data.py @@ -8,12 +8,6 @@ from . import burntsushi, tomllib - -class MissingFile: - def __init__(self, path: Path): - self.path = path - - DATA_DIR = Path(__file__).parent / "data" VALID_FILES = tuple((DATA_DIR / "valid").glob("**/*.toml")) @@ -22,10 +16,7 @@ def __init__(self, path: Path): _expected_files = [] for p in VALID_FILES: json_path = p.with_suffix(".json") - try: - text = json.loads(json_path.read_bytes().decode()) - except FileNotFoundError: - text = MissingFile(json_path) + text = json.loads(json_path.read_bytes().decode()) _expected_files.append(text) VALID_FILES_EXPECTED = tuple(_expected_files) @@ -49,14 +40,6 @@ def test_invalid(self): def test_valid(self): for valid, expected in zip(VALID_FILES, VALID_FILES_EXPECTED): with self.subTest(msg=valid.stem): - if isinstance(expected, MissingFile): - # For a poor man's xfail, assert that this is one of the - # test cases where expected data is known to be missing. - assert valid.stem in { - "qa-array-inline-nested-1000", - "qa-table-inline-nested-1000", - } - continue toml_str = valid.read_bytes().decode() actual = tomllib.loads(toml_str) actual = burntsushi.convert(actual) diff --git a/Lib/test/test_tomllib/test_misc.py b/Lib/test/test_tomllib/test_misc.py index 59116afa1f36ad..af7ab91bba7cd1 100644 --- a/Lib/test/test_tomllib/test_misc.py +++ b/Lib/test/test_tomllib/test_misc.py @@ -9,8 +9,10 @@ from pathlib import Path import sys import tempfile +import textwrap import unittest from test import support +from test.support.script_helper import assert_python_ok from . import tomllib @@ -93,6 +95,7 @@ def test_deepcopy(self): } self.assertEqual(obj_copy, expected_obj) + @support.skip_if_unlimited_stack_size def test_inline_array_recursion_limit(self): with support.infinite_recursion(max_depth=100): available = support.get_recursion_available() @@ -104,6 +107,7 @@ def test_inline_array_recursion_limit(self): recursive_array_toml = "arr = " + nest_count * "[" + nest_count * "]" tomllib.loads(recursive_array_toml) + @support.skip_if_unlimited_stack_size def test_inline_table_recursion_limit(self): with support.infinite_recursion(max_depth=100): available = support.get_recursion_available() @@ -115,6 +119,19 @@ def test_inline_table_recursion_limit(self): recursive_table_toml = nest_count * "key = {" + nest_count * "}" tomllib.loads(recursive_table_toml) + def test_key_recursion_limit(self): + nest_count = tomllib._parser.MAX_KEY_PARTS - 2 + nested_key_toml = "a." * nest_count + "a = 1" + tomllib.loads(nested_key_toml) + + nest_count = tomllib._parser.MAX_KEY_PARTS + 2 + nested_key_toml = "a." * nest_count + "a = 1" + with self.assertRaisesRegex( + RecursionError, + r"TOML key has more than the allowed [0-9]+ parts", + ): + tomllib.loads(nested_key_toml) + def test_types_import(self): """Test that `_types` module runs. @@ -122,3 +139,20 @@ def test_types_import(self): never imported by tests. """ importlib.import_module(f"{tomllib.__name__}._types") + + def test_lazy_import(self): + # Test the TOML file can be parsed without importing regular + # expressions (tomllib._re) + code = textwrap.dedent(""" + import sys, tomllib, textwrap + document = textwrap.dedent(''' + [metadata] + key = "text" + array = ["array", "of", "text"] + booleans = [true, false] + ''') + tomllib.loads(document) + print("lazy import?", 'tomllib._re' not in sys.modules) + """) + proc = assert_python_ok("-c", code) + self.assertIn(b"lazy import? True", proc.out) diff --git a/Lib/test/test_tools/test_compute_changes.py b/Lib/test/test_tools/test_compute_changes.py new file mode 100644 index 00000000000000..eb1ecd447a7ce0 --- /dev/null +++ b/Lib/test/test_tools/test_compute_changes.py @@ -0,0 +1,148 @@ +"""Tests to cover the Tools/build/compute-changes.py script.""" + +import importlib +import os +import unittest +from pathlib import Path +from unittest.mock import patch + +from test.support import os_helper +from test.test_tools import basepath, skip_if_missing, imports_under_tool + +skip_if_missing("build") + +with patch.dict(os.environ, {"GITHUB_DEFAULT_BRANCH": "main"}): + with imports_under_tool("build"): + compute_changes = importlib.import_module("compute-changes") + +process_changed_files = compute_changes.process_changed_files +is_fuzzable_library_file = compute_changes.is_fuzzable_library_file +Outputs = compute_changes.Outputs +ANDROID_DIRS = compute_changes.ANDROID_DIRS +IOS_DIRS = compute_changes.IOS_DIRS +MACOS_DIRS = compute_changes.MACOS_DIRS +WASI_DIRS = compute_changes.WASI_DIRS +RUN_TESTS_IGNORE = compute_changes.RUN_TESTS_IGNORE +UNIX_BUILD_SYSTEM_FILE_NAMES = compute_changes.UNIX_BUILD_SYSTEM_FILE_NAMES +LIBRARY_FUZZER_PATHS = compute_changes.LIBRARY_FUZZER_PATHS + + +class TestProcessChangedFiles(unittest.TestCase): + + def test_windows(self): + f = {Path(".github/workflows/reusable-windows.yml")} + result = process_changed_files(f) + self.assertTrue(result.run_tests) + self.assertTrue(result.run_windows_tests) + + def test_docs(self): + for f in ( + ".github/workflows/reusable-docs.yml", + "Doc/library/datetime.rst", + "Doc/Makefile", + ): + with self.subTest(f=f): + result = process_changed_files({Path(f)}) + self.assertTrue(result.run_docs) + self.assertFalse(result.run_tests) + + def test_ci_fuzz_stdlib(self): + with os_helper.change_cwd(basepath): + for p in LIBRARY_FUZZER_PATHS: + with self.subTest(p=p): + if p.is_dir(): + f = p / "file" + elif p.is_file(): + f = p + else: + continue + result = process_changed_files({f}) + self.assertTrue(result.run_ci_fuzz_stdlib) + self.assertTrue(is_fuzzable_library_file(f)) + + def test_android(self): + for d in ANDROID_DIRS: + with self.subTest(d=d): + result = process_changed_files({Path(d) / "file"}) + self.assertTrue(result.run_tests) + self.assertTrue(result.run_android) + self.assertFalse(result.run_windows_tests) + + def test_ios(self): + for d in IOS_DIRS: + with self.subTest(d=d): + result = process_changed_files({Path(d) / "file"}) + self.assertTrue(result.run_tests) + self.assertTrue(result.run_ios) + self.assertFalse(result.run_windows_tests) + + def test_macos(self): + f = {Path(".github/workflows/reusable-macos.yml")} + result = process_changed_files(f) + self.assertTrue(result.run_tests) + self.assertTrue(result.run_macos) + + for d in MACOS_DIRS: + with self.subTest(d=d): + result = process_changed_files({Path(d) / "file"}) + self.assertTrue(result.run_tests) + self.assertTrue(result.run_macos) + self.assertFalse(result.run_windows_tests) + + def test_wasi(self): + f = {Path(".github/workflows/reusable-wasi.yml")} + result = process_changed_files(f) + self.assertTrue(result.run_tests) + self.assertTrue(result.run_wasi) + + for d in WASI_DIRS: + with self.subTest(d=d): + result = process_changed_files({d / "file"}) + self.assertTrue(result.run_tests) + self.assertTrue(result.run_wasi) + self.assertFalse(result.run_windows_tests) + + def test_unix(self): + for f in UNIX_BUILD_SYSTEM_FILE_NAMES: + with self.subTest(f=f): + result = process_changed_files({f}) + self.assertTrue(result.run_tests) + self.assertFalse(result.run_windows_tests) + + def test_msi(self): + for f in ( + ".github/workflows/reusable-windows-msi.yml", + "Tools/msi/build.bat", + ): + with self.subTest(f=f): + result = process_changed_files({Path(f)}) + self.assertTrue(result.run_windows_msi) + + def test_all_run(self): + for f in ( + ".github/workflows/some-new-workflow.yml", + ".github/workflows/build.yml", + ): + with self.subTest(f=f): + result = process_changed_files({Path(f)}) + self.assertTrue(result.run_tests) + self.assertTrue(result.run_android) + self.assertTrue(result.run_ios) + self.assertTrue(result.run_macos) + self.assertTrue(result.run_ubuntu) + self.assertTrue(result.run_wasi) + + def test_all_ignored(self): + for f in RUN_TESTS_IGNORE: + with self.subTest(f=f): + self.assertEqual(process_changed_files({Path(f)}), Outputs()) + + def test_wasi_and_android(self): + f = {Path(".github/workflows/reusable-wasi.yml"), Path("Android/file")} + result = process_changed_files(f) + self.assertTrue(result.run_tests) + self.assertTrue(result.run_wasi) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_tools/test_makefile.py b/Lib/test/test_tools/test_makefile.py index 4c7588d4d93fc3..31a516067394e1 100644 --- a/Lib/test/test_tools/test_makefile.py +++ b/Lib/test/test_tools/test_makefile.py @@ -48,15 +48,18 @@ def test_makefile_test_folders(self): if dirname == '__pycache__' or dirname.startswith('.'): dirs.clear() # do not process subfolders continue - # Skip empty dirs: + + # Skip empty dirs (ignoring hidden files and __pycache__): + files = [ + filename for filename in files + if not filename.startswith('.') + ] + dirs = [ + dirname for dirname in dirs + if not dirname.startswith('.') and dirname != "__pycache__" + ] if not dirs and not files: continue - # Skip dirs with hidden-only files: - if files and all( - filename.startswith('.') or filename == '__pycache__' - for filename in files - ): - continue relpath = os.path.relpath(dirpath, support.STDLIB_DIR) with self.subTest(relpath=relpath): diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index bf54c9995376d6..19eee19bdea6d5 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -142,6 +142,8 @@ def test_traced_func_linear(self): self.assertEqual(self.tracer.results().counts, expected) + @unittest.skipIf(os.environ.get('PYTHON_UOPS_OPTIMIZE') == '0', + "Line counts differ when JIT optimizer is disabled") def test_traced_func_loop(self): self.tracer.runfunc(traced_func_loop, 2, 3) @@ -166,6 +168,8 @@ def test_traced_func_importing(self): self.assertEqual(self.tracer.results().counts, expected) + @unittest.skipIf(os.environ.get('PYTHON_UOPS_OPTIMIZE') == '0', + "Line counts differ when JIT optimizer is disabled") def test_trace_func_generator(self): self.tracer.runfunc(traced_func_calling_generator) @@ -236,6 +240,8 @@ def setUp(self): self.my_py_filename = fix_ext_py(__file__) self.addCleanup(sys.settrace, sys.gettrace()) + @unittest.skipIf(os.environ.get('PYTHON_UOPS_OPTIMIZE') == '0', + "Line counts differ when JIT optimizer is disabled") def test_exec_counts(self): self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) code = r'''traced_func_loop(2, 5)''' diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 11b7f419bddbe4..2c6324a14a8e2f 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -9,18 +9,21 @@ import builtins import unittest import unittest.mock +import os import re import tempfile import random import string +import importlib.machinery +import sysconfig from test import support import shutil from test.support import (Error, captured_output, cpython_only, ALWAYS_EQ, requires_debug_ranges, has_no_debug_ranges, - requires_subprocess) -from test.support.os_helper import TESTFN, unlink -from test.support.script_helper import assert_python_ok, assert_python_failure -from test.support.import_helper import forget + requires_subprocess, os_helper) +from test.support.os_helper import TESTFN, temp_dir, unlink +from test.support.script_helper import assert_python_ok, assert_python_failure, make_script +from test.support.import_helper import ensure_lazy_imports, forget from test.support import force_not_colorized, force_not_colorized_test_class import json @@ -37,7 +40,7 @@ test_frame = namedtuple('frame', ['f_code', 'f_globals', 'f_locals']) test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next', 'tb_lasti']) -color_overrides = {"reset": "z", "filename": "fn", "error_highlight": "E"} +color_overrides = {"reset": "z", "filename": "fn", "error_highlight": "E", "note": "n"} colors = { color_overrides.get(k, k[0].lower()): v for k, v in _colorize.default_theme.traceback.items() @@ -88,6 +91,12 @@ def syntax_error_bad_indentation2(self): def tokenizer_error_with_caret_range(self): compile("blech ( ", "?", "exec") + def syntax_error_with_caret_wide_char(self): + compile("女女女=1; 女女女/", "?", "exec") + + def syntax_error_with_caret_wide_char_range(self): + compile("f(x, 女女女 for 女女女 in range(30), z)", "?", "exec") + def test_caret(self): err = self.get_exception_format(self.syntax_error_with_caret, SyntaxError) @@ -125,6 +134,20 @@ def test_caret(self): self.assertEqual(err[1].find("("), err[2].find("^")) # in the right place self.assertEqual(err[2].count("^"), 1) + def test_caret_wide_char(self): + err = self.get_exception_format(self.syntax_error_with_caret_wide_char, + SyntaxError) + self.assertIn("^", err[2]) + # "女女女=1; 女女女/" has display width 17 + self.assertEqual(err[2].find("^"), 4 + 17) + + err = self.get_exception_format(self.syntax_error_with_caret_wide_char_range, + SyntaxError) + self.assertIn("^", err[2]) + self.assertEqual(err[2].find("^"), 4 + 5) + # "女女女 for 女女女 in range(30)" has display width 30 + self.assertEqual(err[2].count("^"), 30) + def test_nocaret(self): exc = SyntaxError("error", ("x.py", 23, None, "bad syntax")) err = traceback.format_exception_only(SyntaxError, exc) @@ -183,7 +206,7 @@ def test_recursion_error_during_traceback(self): sys.setrecursionlimit(15) def f(): - ref(lambda: 0, []) + ref(lambda: 0, ord) f() try: @@ -504,6 +527,33 @@ def __del__(self): b'ZeroDivisionError: division by zero'] self.assertEqual(stderr.splitlines(), expected) + @cpython_only + def test_lost_io_open(self): + # GH-142737: Display the traceback even if io.open is lost + crasher = textwrap.dedent("""\ + import io + import traceback + # Trigger fallback mode + traceback._print_exception_bltin = None + del io.open + raise RuntimeError("should not crash") + """) + + # Create a temporary script to exercise _Py_FindSourceFile + with temp_dir() as script_dir: + script = make_script( + script_dir=script_dir, + script_basename='tb_test_no_io_open', + source=crasher) + rc, stdout, stderr = assert_python_failure(script) + + self.assertEqual(rc, 1) # Make sure it's not a crash + + expected = [b'Traceback (most recent call last):', + f' File "{script}", line 6, in <module>'.encode(), + b'RuntimeError: should not crash'] + self.assertEqual(stderr.splitlines(), expected) + def test_print_exception(self): output = StringIO() traceback.print_exception( @@ -1741,6 +1791,67 @@ def f(): self.assertEqual(result_lines, expected) +class TestKeywordTypoSuggestions(unittest.TestCase): + TYPO_CASES = [ + ("with block ad something:\n pass", "and"), + ("fur a in b:\n pass", "for"), + ("for a in b:\n pass\nelso:\n pass", "else"), + ("whille True:\n pass", "while"), + ("iff x > 5:\n pass", "if"), + ("if x:\n pass\nelseif y:\n pass", "elif"), + ("tyo:\n pass\nexcept y:\n pass", "try"), + ("classe MyClass:\n pass", "class"), + ("impor math", "import"), + ("form x import y", "from"), + ("defn calculate_sum(a, b):\n return a + b", "def"), + ("def foo():\n returm result", "return"), + ("lamda x: x ** 2", "lambda"), + ("def foo():\n yeld i", "yield"), + ("def foo():\n globel counter", "global"), + ("frum math import sqrt", "from"), + ("asynch def fetch_data():\n pass", "async"), + ("async def foo():\n awaid fetch_data()", "await"), + ('raisee ValueError("Error")', "raise"), + ("[x for x\nin range(3)\nof x]", "if"), + ("[123 fur x\nin range(3)\nif x]", "for"), + ("for x im n:\n pass", "in"), + ] + + def test_keyword_suggestions_from_file(self): + with tempfile.TemporaryDirectory() as script_dir: + for i, (code, expected_kw) in enumerate(self.TYPO_CASES): + with self.subTest(typo=expected_kw): + source = textwrap.dedent(code).strip() + script_name = make_script(script_dir, f"script_{i}", source) + rc, stdout, stderr = assert_python_failure(script_name) + stderr_text = stderr.decode('utf-8') + self.assertIn(f"Did you mean '{expected_kw}'", stderr_text) + + def test_keyword_suggestions_from_command_string(self): + for code, expected_kw in self.TYPO_CASES: + with self.subTest(typo=expected_kw): + source = textwrap.dedent(code).strip() + rc, stdout, stderr = assert_python_failure('-c', source) + stderr_text = stderr.decode('utf-8') + self.assertIn(f"Did you mean '{expected_kw}'", stderr_text) + + def test_no_keyword_suggestion_for_comma_errors(self): + # When the parser identifies a missing comma, don't suggest + # bogus keyword replacements like 'print' -> 'not' + code = '''\ +import sys +print( + "line1" + "line2" + file=sys.stderr +) +''' + source = textwrap.dedent(code).strip() + rc, stdout, stderr = assert_python_failure('-c', source) + stderr_text = stderr.decode('utf-8') + self.assertIn("Perhaps you forgot a comma", stderr_text) + self.assertNotIn("Did you mean", stderr_text) + @requires_debug_ranges() @force_not_colorized_test_class class PurePythonTracebackErrorCaretTests( @@ -4021,11 +4132,13 @@ def test_dont_swallow_subexceptions_of_falsey_exceptiongroup(self): global_for_suggestions = None -class SuggestionFormattingTestBase: +class SuggestionFormattingTestMixin: + attr_function = getattr + def get_suggestion(self, obj, attr_name=None): if attr_name is not None: def callable(): - getattr(obj, attr_name) + self.attr_function(obj, attr_name) else: callable = obj @@ -4034,7 +4147,9 @@ def callable(): ) return result_lines[0] - def test_getattr_suggestions(self): + +class BaseSuggestionTests(SuggestionFormattingTestMixin): + def test_suggestions(self): class Substitution: noise = more_noise = a = bc = None blech = None @@ -4065,53 +4180,76 @@ class CaseChangeOverSubstitution: BLuch = None for cls, suggestion in [ - (Addition, "'bluchin'?"), - (Substitution, "'blech'?"), - (Elimination, "'blch'?"), - (Addition, "'bluchin'?"), - (SubstitutionOverElimination, "'blach'?"), - (SubstitutionOverAddition, "'blach'?"), - (EliminationOverAddition, "'bluc'?"), - (CaseChangeOverSubstitution, "'BLuch'?"), + (Addition, "'.bluchin'"), + (Substitution, "'.blech'"), + (Elimination, "'.blch'"), + (Addition, "'.bluchin'"), + (SubstitutionOverElimination, "'.blach'"), + (SubstitutionOverAddition, "'.blach'"), + (EliminationOverAddition, "'.bluc'"), + (CaseChangeOverSubstitution, "'.BLuch'"), ]: actual = self.get_suggestion(cls(), 'bluch') - self.assertIn(suggestion, actual) + self.assertIn('Did you mean ' + suggestion, actual) - def test_getattr_suggestions_underscored(self): + def test_suggestions_underscored(self): class A: bluch = None - self.assertIn("'bluch'", self.get_suggestion(A(), 'blach')) - self.assertIn("'bluch'", self.get_suggestion(A(), '_luch')) - self.assertIn("'bluch'", self.get_suggestion(A(), '_bluch')) + self.assertIn("'.bluch'", self.get_suggestion(A(), 'blach')) + self.assertIn("'.bluch'", self.get_suggestion(A(), '_luch')) + self.assertIn("'.bluch'", self.get_suggestion(A(), '_bluch')) + attr_function = self.attr_function class B: _bluch = None def method(self, name): - getattr(self, name) + attr_function(self, name) + + self.assertIn("'._bluch'", self.get_suggestion(B(), '_blach')) + self.assertIn("'._bluch'", self.get_suggestion(B(), '_luch')) + self.assertNotIn("'._bluch'", self.get_suggestion(B(), 'bluch')) + + self.assertIn("'._bluch'", self.get_suggestion(partial(B().method, '_blach'))) + self.assertIn("'._bluch'", self.get_suggestion(partial(B().method, '_luch'))) + self.assertIn("'._bluch'", self.get_suggestion(partial(B().method, 'bluch'))) + + def test_suggestions_with_custom___dir__(self): + class M(type): + def __dir__(cls): + return [None, "fox"] + + class C0: + def __dir__(self): + return [..., "bluch"] + + class C1(C0, metaclass=M): + pass + + self.assertNotIn("'.bluch'", self.get_suggestion(C0, "blach")) + self.assertIn("'.bluch'", self.get_suggestion(C0(), "blach")) + + self.assertIn("'.fox'", self.get_suggestion(C1, "foo")) + self.assertNotIn("'.fox'", self.get_suggestion(C1(), "foo")) - self.assertIn("'_bluch'", self.get_suggestion(B(), '_blach')) - self.assertIn("'_bluch'", self.get_suggestion(B(), '_luch')) - self.assertNotIn("'_bluch'", self.get_suggestion(B(), 'bluch')) + self.assertNotIn("'.bluch'", self.get_suggestion(C1, "blach")) + self.assertIn("'.bluch'", self.get_suggestion(C1(), "blach")) - self.assertIn("'_bluch'", self.get_suggestion(partial(B().method, '_blach'))) - self.assertIn("'_bluch'", self.get_suggestion(partial(B().method, '_luch'))) - self.assertIn("'_bluch'", self.get_suggestion(partial(B().method, 'bluch'))) - def test_getattr_suggestions_do_not_trigger_for_long_attributes(self): + def test_do_not_trigger_for_long_attributes(self): class A: blech = None actual = self.get_suggestion(A(), 'somethingverywrong') self.assertNotIn("blech", actual) - def test_getattr_error_bad_suggestions_do_not_trigger_for_small_names(self): + def test_do_not_trigger_for_small_names(self): class MyClass: vvv = mom = w = id = pytho = None for name in ("b", "v", "m", "py"): with self.subTest(name=name): - actual = self.get_suggestion(MyClass, name) + actual = self.get_suggestion(MyClass(), name) self.assertNotIn("Did you mean", actual) self.assertNotIn("'vvv", actual) self.assertNotIn("'mom'", actual) @@ -4119,7 +4257,7 @@ class MyClass: self.assertNotIn("'w'", actual) self.assertNotIn("'pytho'", actual) - def test_getattr_suggestions_do_not_trigger_for_big_dicts(self): + def test_do_not_trigger_for_big_dicts(self): class A: blech = None # A class with a very big __dict__ will not be considered @@ -4130,7 +4268,36 @@ class A: actual = self.get_suggestion(A(), 'bluch') self.assertNotIn("blech", actual) - def test_getattr_suggestions_no_args(self): + def test_suggestions_for_same_name(self): + class A: + def __dir__(self): + return ['blech'] + actual = self.get_suggestion(A(), 'blech') + self.assertNotIn("Did you mean", actual) + + def test_suggestions_not_normalized(self): + class A: + analization = None + fiⁿₐˡᵢᶻₐᵗᵢᵒₙ = None + + suggestion = self.get_suggestion(A(), 'fiⁿₐˡᵢᶻₐᵗᵢᵒₙ') + self.assertIn("'.finalization'", suggestion) + self.assertNotIn("analization", suggestion) + + class B: + attr_a = None + attr_µ = None # attr_\xb5 + + suggestion = self.get_suggestion(B(), 'attr_\xb5') + self.assertIn( + "'.attr_\u03bc' ('attr_\\u03bc') " + "instead of '.attr_\xb5' ('attr_\\xb5')", + suggestion) + self.assertNotIn("attr_a", suggestion) + + +class GetattrSuggestionTests(BaseSuggestionTests): + def test_suggestions_no_args(self): class A: blech = None def __getattr__(self, attr): @@ -4147,7 +4314,7 @@ def __getattr__(self, attr): actual = self.get_suggestion(A(), 'bluch') self.assertIn("blech", actual) - def test_getattr_suggestions_invalid_args(self): + def test_suggestions_invalid_args(self): class NonStringifyClass: __str__ = None __repr__ = None @@ -4171,13 +4338,12 @@ def __getattr__(self, attr): actual = self.get_suggestion(cls(), 'bluch') self.assertIn("blech", actual) - def test_getattr_suggestions_for_same_name(self): - class A: - def __dir__(self): - return ['blech'] - actual = self.get_suggestion(A(), 'blech') - self.assertNotIn("Did you mean", actual) +class DelattrSuggestionTests(BaseSuggestionTests): + attr_function = delattr + + +class SuggestionFormattingTestBase(SuggestionFormattingTestMixin): def test_attribute_error_with_failing_dict(self): class T: bluch = 1 @@ -4219,6 +4385,278 @@ def __getattribute__(self, attr): self.assertIn("Did you mean", actual) self.assertIn("bluch", actual) + def test_getattr_nested_attribute_suggestions(self): + # Test that nested attributes are suggested when no direct match + class Inner: + def __init__(self): + self.value = 42 + self.data = "test" + + class Outer: + def __init__(self): + self.inner = Inner() + + # Should suggest 'inner.value' + actual = self.get_suggestion(Outer(), 'value') + self.assertIn("Did you mean '.inner.value' instead of '.value'", actual) + + # Should suggest 'inner.data' + actual = self.get_suggestion(Outer(), 'data') + self.assertIn("Did you mean '.inner.data' instead of '.data'", actual) + + def test_getattr_nested_prioritizes_direct_matches(self): + # Test that direct attribute matches are prioritized over nested ones + class Inner: + def __init__(self): + self.foo = 42 + + class Outer: + def __init__(self): + self.inner = Inner() + self.fooo = 100 # Similar to 'foo' + + # Should suggest 'fooo' (direct) not 'inner.foo' (nested) + actual = self.get_suggestion(Outer(), 'foo') + self.assertIn("Did you mean '.fooo'", actual) + self.assertNotIn("inner.foo", actual) + + def test_getattr_nested_with_property(self): + # Property suggestions should not execute the property getter. + class Inner: + @property + def computed(self): + return missing_name + + class Outer: + def __init__(self): + self.inner = Inner() + + actual = self.get_suggestion(Outer(), 'computed') + self.assertIn( + "Did you mean '.inner.computed' instead of '.computed'", + actual, + ) + + def test_getattr_nested_no_suggestion_for_deep_nesting(self): + # Test that deeply nested attributes (2+ levels) are not suggested + class Deep: + def __init__(self): + self.value = 42 + + class Middle: + def __init__(self): + self.deep = Deep() + + class Outer: + def __init__(self): + self.middle = Middle() + + # Should not suggest 'middle.deep.value' (too deep) + actual = self.get_suggestion(Outer(), 'value') + self.assertNotIn("Did you mean", actual) + + def test_getattr_nested_ignores_private_attributes(self): + # Test that nested suggestions ignore private attributes + class Inner: + def __init__(self): + self.public_value = 42 + + class Outer: + def __init__(self): + self._private_inner = Inner() + + # Should not suggest '_private_inner.public_value' + actual = self.get_suggestion(Outer(), 'public_value') + self.assertNotIn("Did you mean", actual) + + def test_getattr_nested_limits_attribute_checks(self): + # Test that nested suggestions are limited to checking first 20 non-private attributes + class Inner: + def __init__(self): + self.target_value = 42 + + class Outer: + def __init__(self): + # Add many attributes before 'inner' + for i in range(25): + setattr(self, f'attr_{i:02d}', i) + # Add the inner object after 20+ attributes + self.inner = Inner() + + obj = Outer() + # Verify that 'inner' is indeed present but after position 20 + attrs = [x for x in sorted(dir(obj)) if not x.startswith('_')] + inner_position = attrs.index('inner') + self.assertGreater(inner_position, 19, "inner should be after position 20 in sorted attributes") + + # Should not suggest 'inner.target_value' because inner is beyond the first 20 attributes checked + actual = self.get_suggestion(obj, 'target_value') + self.assertNotIn("inner.target_value", actual) + + def test_getattr_nested_returns_first_match_only(self): + # Test that only the first nested match is returned (not multiple) + class Inner1: + def __init__(self): + self.value = 1 + + class Inner2: + def __init__(self): + self.value = 2 + + class Inner3: + def __init__(self): + self.value = 3 + + class Outer: + def __init__(self): + # Multiple inner objects with same attribute + self.a_inner = Inner1() + self.b_inner = Inner2() + self.c_inner = Inner3() + + # Should suggest only the first match (alphabetically) + actual = self.get_suggestion(Outer(), 'value') + self.assertIn("'.a_inner.value'", actual) + # Verify it's a single suggestion, not multiple + self.assertEqual(actual.count("Did you mean"), 1) + + def test_getattr_nested_handles_attribute_access_exceptions(self): + # Test that exceptions raised when accessing attributes don't crash the suggestion system + class ExplodingProperty: + @property + def exploding_attr(self): + raise RuntimeError("BOOM! This property always explodes") + + def __repr__(self): + raise RuntimeError("repr also explodes") + + class SafeInner: + def __init__(self): + self.target = 42 + + class Outer: + def __init__(self): + self.exploder = ExplodingProperty() # Accessing attributes will raise + self.safe_inner = SafeInner() + + # Should still suggest '.safe_inner.target' without crashing + # even though accessing exploder.target would raise an exception + actual = self.get_suggestion(Outer(), 'target') + self.assertIn("'.safe_inner.target'", actual) + + def test_getattr_nested_handles_hasattr_exceptions(self): + # Test that exceptions in hasattr don't crash the system + class WeirdObject: + def __getattr__(self, name): + if name == 'target': + raise RuntimeError("Can't check for target attribute") + raise AttributeError(f"No attribute {name}") + + class NormalInner: + def __init__(self): + self.target = 100 + + class Outer: + def __init__(self): + self.weird = WeirdObject() # hasattr will raise for 'target' + self.normal = NormalInner() + + # Should still find 'normal.target' even though weird.target check fails + actual = self.get_suggestion(Outer(), 'target') + self.assertIn("'.normal.target'", actual) + + @force_not_colorized + def test_cross_language(self): + cases = [ + # (type, attr, hint_attr) + (list, 'push', 'append'), + (list, 'concat', 'extend'), + (list, 'addAll', 'extend'), + (str, 'toUpperCase', 'upper'), + (str, 'toLowerCase', 'lower'), + (str, 'trimStart', 'lstrip'), + (str, 'trimEnd', 'rstrip'), + (dict, 'keySet', 'keys'), + (dict, 'entrySet', 'items'), + (dict, 'entries', 'items'), + (dict, 'putAll', 'update'), + ] + for test_type, attr, hint_attr in cases: + with self.subTest(type=test_type.__name__, attr=attr): + obj = test_type() + actual = self.get_suggestion(obj, attr) + self.assertEndsWith(actual, f"Did you mean '.{hint_attr}'?") + + cases = [ + # (type, attr, hint) + (list, 'contains', "Use 'x in list'."), + (list, 'add', "Did you mean to use a 'set' object?"), + (dict, 'put', "Use d[k] = v."), + ] + for test_type, attr, expected in cases: + with self.subTest(type=test_type, attr=attr): + obj = test_type() + actual = self.get_suggestion(obj, attr) + self.assertEndsWith(actual, expected) + + @force_not_colorized + def test_cross_language_levenshtein_fallback(self): + # When no cross-language entry exists, Levenshtein still works + # (e.g., trim->strip is not in the table but Levenshtein catches it) + actual = self.get_suggestion('', 'trim') + self.assertIn("strip", actual) + + @force_not_colorized + def test_cross_language_no_hint_for_unknown_attr(self): + actual = self.get_suggestion([], 'completely_unknown_method') + self.assertNotIn("Did you mean", actual) + + @force_not_colorized + def test_cross_language_works_for_subclasses(self): + # isinstance() check means subclasses also get hints + class MyList(list): + pass + actual = self.get_suggestion(MyList(), 'push') + self.assertEndsWith(actual, "Did you mean '.append'?") + + class MyDict(dict): + pass + actual = self.get_suggestion(MyDict(), 'keySet') + self.assertEndsWith(actual, "Did you mean '.keys'?") + + @force_not_colorized + def test_cross_language_mutable_on_immutable(self): + # Mutable method on immutable type suggests the mutable counterpart + cases = [ + (tuple, 'append', "Did you mean to use a 'list' object?"), + (tuple, 'extend', "Did you mean to use a 'list' object?"), + (tuple, 'insert', "Did you mean to use a 'list' object?"), + (tuple, 'remove', "Did you mean to use a 'list' object?"), + (frozenset, 'add', "Did you mean to use a 'set' object?"), + (frozenset, 'discard', "Did you mean to use a 'set' object?"), + (frozenset, 'remove', "Did you mean to use a 'set' object?"), + (frozenset, 'update', "Did you mean to use a 'set' object?"), + (frozendict, 'update', "Did you mean to use a 'dict' object?"), + (tuple, 'clear', "Did you mean to use a 'list' object?"), + (frozenset, 'clear', "Did you mean to use a 'set' object?"), + (frozendict, 'clear', "Did you mean to use a 'dict' object?"), + ] + for test_type, attr, expected in cases: + with self.subTest(type=test_type.__name__, attr=attr): + obj = test_type() + actual = self.get_suggestion(obj, attr) + self.assertEndsWith(actual, expected) + + @force_not_colorized + def test_cross_language_float_bitwise(self): + # Bitwise operators on float suggest using int + cases = ['__or__', '__and__', '__xor__', '__lshift__', '__rshift__'] + for attr in cases: + with self.subTest(attr=attr): + actual = self.get_suggestion(1.0, attr) + self.assertIn("'int'", actual) + self.assertIn("Bitwise operators", actual) + def make_module(self, code): tmpdir = Path(tempfile.mkdtemp()) self.addCleanup(shutil.rmtree, tmpdir) @@ -4573,6 +5011,34 @@ def foo(self): actual = self.get_suggestion(instance.foo) self.assertIn("self.blech", actual) + def test_name_error_with_instance_not_normalized(self): + class A: + def __init__(self): + self.fiⁿₐˡᵢᶻₐᵗᵢᵒₙ = None + def foo(self): + analization = 1 + x = fiⁿₐˡᵢᶻₐᵗᵢᵒₙ + + instance = A() + actual = self.get_suggestion(instance.foo) + self.assertIn("self.finalization", actual) + self.assertNotIn("fiⁿₐˡᵢᶻₐᵗᵢᵒₙ", actual) + self.assertNotIn("analization", actual) + + class B: + def __init__(self): + self.attr_µ = None # attr_\xb5 + def foo(self): + attr_a = 1 + x = attr_µ # attr_\xb5 + + instance = B() + actual = self.get_suggestion(instance.foo) + self.assertIn("self.attr_\u03bc", actual) + self.assertIn(r"self.attr_\u03bc", actual) + self.assertNotIn("attr_\xb5", actual) + self.assertNotIn("attr_a", actual) + def test_unbound_local_error_with_instance(self): class A: def __init__(self): @@ -4655,6 +5121,51 @@ class CPythonSuggestionFormattingTests( """ +class PurePythonGetattrSuggestionFormattingTests( + PurePythonExceptionFormattingMixin, + GetattrSuggestionTests, + unittest.TestCase, +): + """ + Same set of tests (for attribute access) as above using the pure Python + implementation of traceback printing in traceback.py. + """ + + +class PurePythonDelattrSuggestionFormattingTests( + PurePythonExceptionFormattingMixin, + DelattrSuggestionTests, + unittest.TestCase, +): + """ + Same set of tests (for attribute deletion) as above using the pure Python + implementation of traceback printing in traceback.py. + """ + + +@cpython_only +class CPythonGetattrSuggestionFormattingTests( + CAPIExceptionFormattingMixin, + GetattrSuggestionTests, + unittest.TestCase, +): + """ + Same set of tests (for attribute access) as above but with Python's + internal traceback printing. + """ + + +@cpython_only +class CPythonDelattrSuggestionFormattingTests( + CAPIExceptionFormattingMixin, + DelattrSuggestionTests, + unittest.TestCase, +): + """ + Same set of tests (for attribute deletion) as above but with Python's + internal traceback printing. + """ + class MiscTest(unittest.TestCase): def test_all(self): @@ -4754,7 +5265,8 @@ def test_no_site_package_flavour(self): self.assertIn( (b"Site initialization is disabled, did you forget to " - b"add the site-packages directory to sys.path?"), stderr + b"add the site-packages directory to sys.path " + b"or to enable your virtual environment?"), stderr ) code = """ @@ -4766,9 +5278,91 @@ def test_no_site_package_flavour(self): self.assertNotIn( (b"Site initialization is disabled, did you forget to " - b"add the site-packages directory to sys.path?"), stderr + b"add the site-packages directory to sys.path " + b"or to enable your virtual environment?"), stderr ) + def test_missing_stdlib_module(self): + code = """ + import sys + sys.stdlib_module_names |= {'spam'} + import spam + """ + _, _, stderr = assert_python_failure('-S', '-c', code) + + self.assertIn(b"Standard library module 'spam' was not found", stderr) + + code = """ + import sys + import traceback + traceback._MISSING_STDLIB_MODULE_MESSAGES = {'spam': "Install 'spam4life' for 'spam'"} + sys.stdlib_module_names |= {'spam'} + import spam + """ + _, _, stderr = assert_python_failure('-S', '-c', code) + + self.assertIn(b"Install 'spam4life' for 'spam'", stderr) + + @unittest.skipIf(sys.platform == "win32", "Non-Windows test") + def test_windows_only_module_error(self): + try: + import msvcrt # noqa: F401 + except ModuleNotFoundError: + formatted = traceback.format_exc() + self.assertIn("Unsupported platform for Windows-only standard library module 'msvcrt'", formatted) + else: + self.fail("ModuleNotFoundError was not raised") + + @unittest.skipIf(not importlib.machinery.EXTENSION_SUFFIXES, 'Platform does not support extension modules') + def test_find_incompatible_extension_modules(self): + """_find_incompatible_extension_modules assumes the last extension in + importlib.machinery.EXTENSION_SUFFIXES (defined in Python/dynload_*.c) + is untagged (eg. .so, .pyd). + + This test exists to make sure that assumption is correct. + """ + last_extension_suffix = importlib.machinery.EXTENSION_SUFFIXES[-1] + if shlib_suffix := sysconfig.get_config_var('SHLIB_SUFFIX'): + self.assertEqual(last_extension_suffix, shlib_suffix) + else: + before_dot, *extensions = last_extension_suffix.split('.') + expected_prefixes = [''] + if os.name == 'nt': + # Windows puts the debug tag in the module file stem (eg. foo_d.pyd) + expected_prefixes.append('_d') + self.assertIn(before_dot, expected_prefixes, msg=( + f'Unexpected prefix {before_dot!r} in extension module ' + f'suffix {last_extension_suffix!r}. ' + 'traceback._find_incompatible_extension_module needs to be ' + 'updated to take this into account!' + )) + # if SHLIB_SUFFIX is not define, we assume the native + # shared library suffix only contains one extension + # (eg. '.so', bad eg. '.cpython-315-x86_64-linux-gnu.so') + self.assertEqual(len(extensions), 1, msg=( + 'The last suffix in importlib.machinery.EXTENSION_SUFFIXES ' + 'contains more than one extension, so it is probably different ' + 'than SHLIB_SUFFIX. It probably contains an ABI tag! ' + 'If this is a false positive, define SHLIB_SUFFIX in sysconfig.' + )) + + @unittest.skipIf(not importlib.machinery.EXTENSION_SUFFIXES, 'Platform does not support extension modules') + def test_incompatible_extension_modules_hint(self): + untagged_suffix = importlib.machinery.EXTENSION_SUFFIXES[-1] + with os_helper.temp_dir() as tmp: + # create a module with a incompatible ABI tag + incompatible_module = f'foo.some-abi{untagged_suffix}' + open(os.path.join(tmp, incompatible_module), "wb").close() + # try importing it + code = f''' + import sys + sys.path.insert(0, {tmp!r}) + import foo + ''' + _, _, stderr = assert_python_failure('-c', code, __cwd=tmp) + hint = f'Although a module with this name was found for a different Python version ({incompatible_module}).' + self.assertIn(hint, stderr.decode()) + class TestColorizedTraceback(unittest.TestCase): maxDiff = None @@ -4805,6 +5399,23 @@ def bar(): self.assertIn("return baz1(1,\n 2,3\n ,4)", lines) self.assertIn(red + "bar" + reset + boldr + "()" + reset, lines) + def test_colorized_exception_notes(self): + def foo(): + raise ValueError() + + try: + foo() + except Exception as e: + e.add_note("First note") + e.add_note("Second note") + exc = traceback.TracebackException.from_exception(e) + + lines = "".join(exc.format(colorize=True)) + note = colors["n"] + reset = colors["z"] + self.assertIn(note + "First note" + reset, lines) + self.assertIn(note + "Second note" + reset, lines) + def test_colorized_syntax_error(self): try: compile("a $ b", "<string>", "exec") @@ -4813,7 +5424,7 @@ def test_colorized_syntax_error(self): e, capture_locals=True ) actual = "".join(exc.format(colorize=True)) - def expected(t, m, fn, l, f, E, e, z): + def expected(t, m, fn, l, f, E, e, z, n): return "".join( [ f' File {fn}"<string>"{z}, line {l}1{z}\n', @@ -4839,7 +5450,7 @@ def foo(): actual = tbstderr.getvalue().splitlines() lno_foo = foo.__code__.co_firstlineno - def expected(t, m, fn, l, f, E, e, z): + def expected(t, m, fn, l, f, E, e, z, n): return [ 'Traceback (most recent call last):', f' File {fn}"{__file__}"{z}, ' @@ -4872,7 +5483,7 @@ def foo(): lno_foo = foo.__code__.co_firstlineno actual = "".join(exc.format(colorize=True)).splitlines() - def expected(t, m, fn, l, f, E, e, z): + def expected(t, m, fn, l, f, E, e, z, n): return [ f" + Exception Group Traceback (most recent call last):", f' | File {fn}"{__file__}"{z}, line {l}{lno_foo+9}{z}, in {f}test_colorized_traceback_from_exception_group{z}', @@ -4896,5 +5507,139 @@ def expected(t, m, fn, l, f, E, e, z): ] self.assertEqual(actual, expected(**colors)) + def test_colorized_traceback_unicode(self): + try: + 啊哈=1; 啊哈/0#### + except Exception as e: + exc = traceback.TracebackException.from_exception(e) + + actual = "".join(exc.format(colorize=True)).splitlines() + def expected(t, m, fn, l, f, E, e, z, n): + return [ + f" 啊哈=1; {e}啊哈{z}{E}/{z}{e}0{z}####", + f" {e}~~~~{z}{E}^{z}{e}~{z}", + ] + self.assertEqual(actual[2:4], expected(**colors)) + + try: + ééééé/0 + except Exception as e: + exc = traceback.TracebackException.from_exception(e) + + actual = "".join(exc.format(colorize=True)).splitlines() + def expected(t, m, fn, l, f, E, e, z, n): + return [ + f" {E}ééééé{z}/0", + f" {E}^^^^^{z}", + ] + self.assertEqual(actual[2:4], expected(**colors)) + + def test_colorized_syntax_error_ascii_display_width(self): + """Caret alignment for ASCII edge cases handled by _wlen. + + The old ASCII fast track in _display_width returned the raw character + offset for ASCII strings, which is wrong for CTRL-Z (display width 2) + and ANSI escape sequences (display width 0). + """ + E = colors["E"] + z = colors["z"] + t = colors["t"] + m = colors["m"] + fn = colors["fn"] + l = colors["l"] + + def _make_syntax_error(text, offset, end_offset): + err = SyntaxError("invalid syntax") + err.filename = "<string>" + err.lineno = 1 + err.end_lineno = 1 + err.text = text + err.offset = offset + err.end_offset = end_offset + return err + + # CTRL-Z (\x1a) is ASCII but displayed as ^Z (2 columns). + # Verify caret aligns when CTRL-Z precedes the error. + err = _make_syntax_error("a\x1a$\n", offset=3, end_offset=4) + exc = traceback.TracebackException.from_exception(err) + actual = "".join(exc.format(colorize=True)) + # 'a' (1 col) + '\x1a' (2 cols) = 3 cols before '$' + self.assertIn( + f' File {fn}"<string>"{z}, line {l}1{z}\n' + f' a\x1a{E}${z}\n' + f' {" " * 3}{E}^{z}\n' + f'{t}SyntaxError{z}: {m}invalid syntax{z}\n', + actual, + ) + + # CTRL-Z in the highlighted (error) region counts as 2 columns. + err = _make_syntax_error("$\x1a\n", offset=1, end_offset=3) + exc = traceback.TracebackException.from_exception(err) + actual = "".join(exc.format(colorize=True)) + # '$' (1 col) + '\x1a' (2 cols) = 3 columns of carets + self.assertIn( + f' {E}$\x1a{z}\n' + f' {E}{"^" * 3}{z}\n', + actual, + ) + + # ANSI escape sequences are ASCII but take 0 display columns. + err = _make_syntax_error("a\x1b[1mb$\n", offset=7, end_offset=8) + exc = traceback.TracebackException.from_exception(err) + actual = "".join(exc.format(colorize=True)) + # 'a' (1 col) + '\x1b[1m' (0 cols) + 'b' (1 col) = 2 before '$' + self.assertIn( + f' a\x1b[1mb{E}${z}\n' + f' {" " * 2}{E}^{z}\n', + actual, + ) + +class TestLazyImportSuggestions(unittest.TestCase): + """Test that lazy imports are not reified when computing AttributeError suggestions.""" + + def test_attribute_error_does_not_reify_lazy_imports(self): + """Printing an AttributeError should not trigger lazy import reification.""" + # pkg.bar prints "BAR_MODULE_LOADED" when imported. + # If lazy import is reified during suggestion computation, we'll see it. + code = textwrap.dedent(""" + lazy import test.test_lazy_import.data.pkg.bar + test.test_lazy_import.data.pkg.nonexistent + """) + rc, stdout, stderr = assert_python_failure('-c', code) + self.assertNotIn(b"BAR_MODULE_LOADED", stdout) + + def test_traceback_formatting_does_not_reify_lazy_imports(self): + """Formatting a traceback should not trigger lazy import reification.""" + code = textwrap.dedent(""" + import traceback + lazy import test.test_lazy_import.data.pkg.bar + try: + test.test_lazy_import.data.pkg.nonexistent + except AttributeError: + traceback.format_exc() + print("OK") + """) + rc, stdout, stderr = assert_python_ok('-c', code) + self.assertIn(b"OK", stdout) + self.assertNotIn(b"BAR_MODULE_LOADED", stdout) + + def test_suggestion_still_works_for_non_lazy_attributes(self): + """Suggestions should still work for non-lazy module attributes.""" + code = textwrap.dedent(""" + lazy import test.test_lazy_import.data.pkg.bar + # Typo for __name__ + test.test_lazy_import.data.pkg.__nme__ + """) + rc, stdout, stderr = assert_python_failure('-c', code) + self.assertIn(b"__name__", stderr) + self.assertNotIn(b"BAR_MODULE_LOADED", stdout) + + +class LazyImportTest(unittest.TestCase): + @support.cpython_only + def test_lazy_import(self): + ensure_lazy_imports("traceback", {"_colorize"}) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index 6dc6880ff8c356..9d3ff8a620b6f2 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -8,7 +8,7 @@ from test.support.script_helper import (assert_python_ok, assert_python_failure, interpreter_requires_environment) from test import support -from test.support import force_not_colorized +from test.support import force_not_colorized, warnings_helper from test.support import os_helper from test.support import threading_helper @@ -354,6 +354,7 @@ def fork_child(self): # everything is fine return 0 + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() def test_fork(self): # check that tracemalloc is still working after fork diff --git a/Lib/test/test_ttk/__init__.py b/Lib/test/test_ttk/__init__.py index 7ee7ffbd6d7408..7a47f44ca38221 100644 --- a/Lib/test/test_ttk/__init__.py +++ b/Lib/test/test_ttk/__init__.py @@ -19,7 +19,21 @@ from tkinter import ttk +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(ttk, "__version__") + self.assertEqual(cm.filename, __file__) + + def setUpModule(): + wantobjects = support.get_resource_value('wantobjects') + if wantobjects is not None: + unittest.enterModuleContext( + support.swap_attr(tkinter, 'wantobjects', int(wantobjects))) root = None try: root = tkinter.Tk() diff --git a/Lib/test/test_ttk/test_extensions.py b/Lib/test/test_ttk/test_extensions.py index 05bca59e703936..669a3e184eb771 100644 --- a/Lib/test/test_ttk/test_extensions.py +++ b/Lib/test/test_ttk/test_extensions.py @@ -3,6 +3,7 @@ import tkinter from tkinter import ttk from test.support import requires, gc_collect +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractTkTest, AbstractDefaultRootTest requires('gui') diff --git a/Lib/test/test_ttk/test_style.py b/Lib/test/test_ttk/test_style.py index 19918772514ad4..fdbaae1b644e4d 100644 --- a/Lib/test/test_ttk/test_style.py +++ b/Lib/test/test_ttk/test_style.py @@ -5,6 +5,7 @@ from tkinter import TclError from test import support from test.support import requires +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractTkTest, get_tk_patchlevel requires('gui') diff --git a/Lib/test/test_ttk/test_widgets.py b/Lib/test/test_ttk/test_widgets.py index f33da2a8848738..8cce9aed9d514f 100644 --- a/Lib/test/test_ttk/test_widgets.py +++ b/Lib/test/test_ttk/test_widgets.py @@ -5,6 +5,7 @@ import sys from test.test_ttk_textonly import MockTclObj +from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import ( AbstractTkTest, requires_tk, tk_version, get_tk_patchlevel, simulate_mouse_click, AbstractDefaultRootTest) @@ -182,7 +183,7 @@ def checkImageParam(self, widget, name): expected=('image1', 'active', 'image2')) self.checkParam(widget, name, 'image1 active image2', expected=('image1', 'active', 'image2')) - if tk_version < (9, 0): + if tk_version < (8, 7): errmsg = 'image "spam" doesn\'t exist' else: errmsg = 'image "spam" does not exist' @@ -1191,7 +1192,7 @@ def test_traversal(self): elif sys.platform == 'win32': focus_identify_as = 'focus' else: - focus_identify_as = 'focus' if tk_version < (9,0) else 'padding' + focus_identify_as = 'focus' if tk_version < (8, 7) else 'padding' self.assertEqual(self.nb.identify(5, 5), focus_identify_as) simulate_mouse_click(self.nb, 5, 5) self.nb.focus_force() diff --git a/Lib/test/test_tuple.py b/Lib/test/test_tuple.py index 9ce80c5e8ea009..e533392b8cae94 100644 --- a/Lib/test/test_tuple.py +++ b/Lib/test/test_tuple.py @@ -290,12 +290,18 @@ def test_repr(self): self.assertEqual(repr(a0), "()") self.assertEqual(repr(a2), "(0, 1, 2)") + # Checks that t is not tracked without any GC collections. + def _not_tracked_instantly(self, t): + self.assertFalse(gc.is_tracked(t), t) + + # Checks that t is not tracked after GC collection. def _not_tracked(self, t): # Nested tuples can take several collections to untrack gc.collect() gc.collect() self.assertFalse(gc.is_tracked(t), t) + # Checks that t continues to be tracked even after GC collection. def _tracked(self, t): self.assertTrue(gc.is_tracked(t), t) gc.collect() @@ -307,13 +313,19 @@ def test_track_literals(self): # Test GC-optimization of tuple literals x, y, z = 1.5, "a", [] - self._not_tracked(()) - self._not_tracked((1,)) - self._not_tracked((1, 2)) - self._not_tracked((1, 2, "a")) - self._not_tracked((1, 2, (None, True, False, ()), int)) - self._not_tracked((object(),)) + # We check that those objects aren't tracked at all. + # It's essential for the GC performance, see gh-139951. + self._not_tracked_instantly(()) + self._not_tracked_instantly((1,)) + self._not_tracked_instantly((1, 2)) + self._not_tracked_instantly((1, 2, "a")) + self._not_tracked_instantly((1, 2) * 5) + self._not_tracked_instantly((12, 10**10, 'a_' * 100)) + self._not_tracked_instantly((object(),)) + self._not_tracked(((1, x), y, (2, 3))) + self._not_tracked((1, 2, (None, True, False, ()), int)) + self._not_tracked((object(), ())) # Tuples with mutable elements are always tracked, even if those # elements are not tracked right now. @@ -343,6 +355,12 @@ def check_track_dynamic(self, tp, always_track): self._tracked(tp(tuple([obj]) for obj in [x, y, z])) self._tracked(tuple(tp([obj]) for obj in [x, y, z])) + t = tp([1, x, y, z]) + self.assertEqual(type(t), tp) + self._tracked(t) + self.assertEqual(type(t[:]), tuple) + self._tracked(t[:]) + @support.cpython_only def test_track_dynamic(self): # Test GC-optimization of dynamically constructed tuples. diff --git a/Lib/test/test_turtle.py b/Lib/test/test_turtle.py index d02cac284a909a..12d2eed874148c 100644 --- a/Lib/test/test_turtle.py +++ b/Lib/test/test_turtle.py @@ -60,12 +60,25 @@ def patch_screen(): We must patch the _Screen class itself instead of the _Screen instance because instantiating it requires a display. """ + # Create a mock screen that delegates color validation to the real TurtleScreen methods + mock_screen = unittest.mock.MagicMock() + mock_screen.__class__ = turtle._Screen + mock_screen.mode.return_value = "standard" + mock_screen._colormode = 1.0 + + def mock_iscolorstring(color): + valid_colors = {'red', 'green', 'blue', 'black', 'white', 'yellow', + 'orange', 'purple', 'pink', 'brown', 'gray', 'grey', + 'cyan', 'magenta'} + + return color in valid_colors or (isinstance(color, str) and color.startswith('#')) + + mock_screen._iscolorstring = mock_iscolorstring + mock_screen._colorstr = turtle._Screen._colorstr.__get__(mock_screen) + return unittest.mock.patch( "turtle._Screen.__new__", - **{ - "return_value.__class__": turtle._Screen, - "return_value.mode.return_value": "standard", - }, + return_value=mock_screen ) @@ -635,6 +648,28 @@ def test_poly_context_when_creating_poly(self): self.assertTrue(self.turtle._creatingPoly) self.assertFalse(self.turtle._creatingPoly) + def test_dot_signature(self): + self.turtle.dot() + self.turtle.dot(10) + self.turtle.dot(size=10) + self.turtle.dot((0, 0, 0)) + self.turtle.dot(size=(0, 0, 0)) + self.turtle.dot("blue") + self.turtle.dot("") + self.turtle.dot(size="blue") + self.turtle.dot(20, "blue") + self.turtle.dot(20, "blue") + self.turtle.dot(20, (0, 0, 0)) + self.turtle.dot(20, 0, 0, 0) + with self.assertRaises(TypeError): + self.turtle.dot(color="blue") + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, "_not_a_color_") + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, (0, 0, 0, 0)) + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, 0, 0, 0, 0) + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, (-1, 0, 0)) + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, -1, 0, 0) + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, (0, 257, 0)) + self.assertRaises(turtle.TurtleGraphicsError, self.turtle.dot, 0, 0, 257, 0) class TestModuleLevel(unittest.TestCase): def test_all_signatures(self): diff --git a/Lib/test/test_type_aliases.py b/Lib/test/test_type_aliases.py index ee1791bc1d0b9d..05bc6a80888a06 100644 --- a/Lib/test/test_type_aliases.py +++ b/Lib/test/test_type_aliases.py @@ -8,6 +8,11 @@ Callable, TypeAliasType, TypeVar, TypeVarTuple, ParamSpec, Unpack, get_args, ) +type GlobalTypeAlias = int + +def get_type_alias(): + type TypeAliasInFunc = str + return TypeAliasInFunc class TypeParamsInvalidTest(unittest.TestCase): def test_name_collisions(self): @@ -70,6 +75,8 @@ def inner[B](self): class TypeParamsAliasValueTest(unittest.TestCase): + type TypeAliasInClass = dict + def test_alias_value_01(self): type TA1 = int @@ -142,33 +149,67 @@ def test_subscripting(self): self.assertIs(specialized2.__origin__, VeryGeneric) self.assertEqual(specialized2.__args__, (int, str, float, [bool, range])) + def test___name__(self): + type TypeAliasLocal = GlobalTypeAlias + + self.assertEqual(GlobalTypeAlias.__name__, 'GlobalTypeAlias') + self.assertEqual(get_type_alias().__name__, 'TypeAliasInFunc') + self.assertEqual(self.TypeAliasInClass.__name__, 'TypeAliasInClass') + self.assertEqual(TypeAliasLocal.__name__, 'TypeAliasLocal') + + with self.assertRaisesRegex( + AttributeError, + "readonly attribute", + ): + setattr(TypeAliasLocal, '__name__', 'TA') + + def test___qualname__(self): + type TypeAliasLocal = GlobalTypeAlias + + self.assertEqual(GlobalTypeAlias.__qualname__, + 'GlobalTypeAlias') + self.assertEqual(get_type_alias().__qualname__, + 'get_type_alias.<locals>.TypeAliasInFunc') + self.assertEqual(self.TypeAliasInClass.__qualname__, + 'TypeParamsAliasValueTest.TypeAliasInClass') + self.assertEqual(TypeAliasLocal.__qualname__, + 'TypeParamsAliasValueTest.test___qualname__.<locals>.TypeAliasLocal') + + with self.assertRaisesRegex( + AttributeError, + "readonly attribute", + ): + setattr(TypeAliasLocal, '__qualname__', 'TA') + def test_repr(self): type Simple = int - type VeryGeneric[T, *Ts, **P] = Callable[P, tuple[T, *Ts]] + self.assertEqual(repr(Simple), Simple.__qualname__) - self.assertEqual(repr(Simple), "Simple") - self.assertEqual(repr(VeryGeneric), "VeryGeneric") + type VeryGeneric[T, *Ts, **P] = Callable[P, tuple[T, *Ts]] + self.assertEqual(repr(VeryGeneric), VeryGeneric.__qualname__) + fullname = f"{VeryGeneric.__module__}.{VeryGeneric.__qualname__}" self.assertEqual(repr(VeryGeneric[int, bytes, str, [float, object]]), - "VeryGeneric[int, bytes, str, [float, object]]") + f"{fullname}[int, bytes, str, [float, object]]") self.assertEqual(repr(VeryGeneric[int, []]), - "VeryGeneric[int, []]") + f"{fullname}[int, []]") self.assertEqual(repr(VeryGeneric[int, [VeryGeneric[int], list[str]]]), - "VeryGeneric[int, [VeryGeneric[int], list[str]]]") + f"{fullname}[int, [{fullname}[int], list[str]]]") def test_recursive_repr(self): type Recursive = Recursive - self.assertEqual(repr(Recursive), "Recursive") + self.assertEqual(repr(Recursive), Recursive.__qualname__) type X = list[Y] type Y = list[X] - self.assertEqual(repr(X), "X") - self.assertEqual(repr(Y), "Y") + self.assertEqual(repr(X), X.__qualname__) + self.assertEqual(repr(Y), Y.__qualname__) type GenericRecursive[X] = list[X | GenericRecursive[X]] - self.assertEqual(repr(GenericRecursive), "GenericRecursive") - self.assertEqual(repr(GenericRecursive[int]), "GenericRecursive[int]") + self.assertEqual(repr(GenericRecursive), GenericRecursive.__qualname__) + fullname = f"{GenericRecursive.__module__}.{GenericRecursive.__qualname__}" + self.assertEqual(repr(GenericRecursive[int]), f"{fullname}[int]") self.assertEqual(repr(GenericRecursive[GenericRecursive[int]]), - "GenericRecursive[GenericRecursive[int]]") + f"{fullname}[{fullname}[int]]") def test_raising(self): type MissingName = list[_My_X] @@ -193,15 +234,25 @@ class TypeAliasConstructorTest(unittest.TestCase): def test_basic(self): TA = TypeAliasType("TA", int) self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "TA") self.assertIs(TA.__value__, int) self.assertEqual(TA.__type_params__, ()) self.assertEqual(TA.__module__, __name__) + def test_with_qualname(self): + TA = TypeAliasType("TA", str, qualname="Class.TA") + self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "Class.TA") + self.assertIs(TA.__value__, str) + self.assertEqual(TA.__type_params__, ()) + self.assertEqual(TA.__module__, __name__) + def test_attributes_with_exec(self): ns = {} exec("type TA = int", ns, ns) TA = ns["TA"] self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "TA") self.assertIs(TA.__value__, int) self.assertEqual(TA.__type_params__, ()) self.assertIs(TA.__module__, None) @@ -210,6 +261,7 @@ def test_generic(self): T = TypeVar("T") TA = TypeAliasType("TA", list[T], type_params=(T,)) self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "TA") self.assertEqual(TA.__value__, list[T]) self.assertEqual(TA.__type_params__, (T,)) self.assertEqual(TA.__module__, __name__) @@ -218,6 +270,7 @@ def test_generic(self): def test_not_generic(self): TA = TypeAliasType("TA", list[int], type_params=()) self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "TA") self.assertEqual(TA.__value__, list[int]) self.assertEqual(TA.__type_params__, ()) self.assertEqual(TA.__module__, __name__) @@ -268,8 +321,9 @@ def test_expects_type_like(self): TypeAliasType("A", int, type_params=(T, 2)) def test_keywords(self): - TA = TypeAliasType(name="TA", value=int) + TA = TypeAliasType(name="TA", value=int, type_params=(), qualname=None) self.assertEqual(TA.__name__, "TA") + self.assertEqual(TA.__qualname__, "TA") self.assertIs(TA.__value__, int) self.assertEqual(TA.__type_params__, ()) self.assertEqual(TA.__module__, __name__) @@ -283,6 +337,8 @@ def test_errors(self): TypeAliasType("TA", list, ()) with self.assertRaises(TypeError): TypeAliasType("TA", list, type_params=42) + with self.assertRaises(TypeError): + TypeAliasType("TA", list, qualname=range(5)) class TypeAliasTypeTest(unittest.TestCase): @@ -316,6 +372,8 @@ def test_module(self): mod_generics_cache.__name__) self.assertEqual(mod_generics_cache.OldStyle.__module__, mod_generics_cache.__name__) + Alias.__module__ = "ham.spam.eggs" + self.assertEqual(Alias.__module__, "ham.spam.eggs") def test_unpack(self): type Alias = tuple[int, int] diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py index c66cb058552643..b751f825bb97d5 100644 --- a/Lib/test/test_type_annotations.py +++ b/Lib/test/test_type_annotations.py @@ -309,7 +309,7 @@ def check_annotations(self, f): print(f.__annotations__) f.__annotate__ = lambda x: 42 - with self.assertRaisesRegex(TypeError, r"__annotate__ returned non-dict of type 'int'"): + with self.assertRaisesRegex(TypeError, r"__annotate__\(\) must return a dict, not int"): print(f.__annotations__) f.__annotate__ = lambda x: {"x": x} @@ -485,6 +485,13 @@ def test_comprehension_in_annotation(self): ns = run_code("x: [y for y in range(10)]") self.assertEqual(ns["__annotate__"](1), {"x": list(range(10))}) + def test_class_annotation_dunder_classdict(self): + ns = run_code(""" + class C: + __classdict__: int + """) + self.assertEqual(ns["C"].__annotations__, {"__classdict__": int}) + def test_future_annotations(self): code = """ from __future__ import annotations @@ -835,3 +842,57 @@ def test_complex_comprehension_inlining_exec(self): genexp = annos["unique_name_2"][0] lamb = list(genexp)[0] self.assertEqual(lamb(), 42) + + def test_annotate_qualname(self): + code = """ + def f() -> None: + def nested() -> None: pass + return nested + class Outer: + x: int + def method(self, x: int): + pass + """ + ns = run_code(code) + method = ns["Outer"].method + self.assertEqual(method.__annotate__.__qualname__, "Outer.method.__annotate__") + self.assertEqual(ns["f"].__annotate__.__qualname__, "f.__annotate__") + self.assertEqual(ns["f"]().__annotate__.__qualname__, "f.<locals>.nested.__annotate__") + self.assertEqual(ns["Outer"].__annotate__.__qualname__, "Outer.__annotate__") + + # gh-138349 + def test_module_level_annotation_plus_listcomp(self): + cases = [ + """ + def report_error(): + pass + try: + [0 for name_2 in unique_name_0 if (lambda: name_2)] + except: + pass + annotated_name: 0 + """, + """ + class Generic: + pass + try: + [0 for name_2 in unique_name_0 if (0 for unique_name_1 in unique_name_2 for unique_name_3 in name_2)] + except: + pass + annotated_name: 0 + """, + """ + class Generic: + pass + annotated_name: 0 + try: + [0 for name_2 in [[0]] for unique_name_1 in unique_name_2 if (lambda: name_2)] + except: + pass + """, + ] + for code in cases: + with self.subTest(code=code): + mod = build_module(code) + annos = mod.__annotations__ + self.assertEqual(annos, {"annotated_name": 0}) diff --git a/Lib/test/test_type_cache.py b/Lib/test/test_type_cache.py index 7469a1047f81d7..849a2afd8ed798 100644 --- a/Lib/test/test_type_cache.py +++ b/Lib/test/test_type_cache.py @@ -1,9 +1,10 @@ """ Tests for the internal type cache in CPython. """ +import collections.abc import dis import unittest import warnings from test import support -from test.support import import_helper, requires_specialization, requires_specialization_ft +from test.support import import_helper, requires_specialization try: from sys import _clear_type_cache except ImportError: @@ -114,6 +115,25 @@ class HolderSub(Holder): Holder.set_value() HolderSub.value + def test_abc_register_invalidates_subclass_versions(self): + class Parent: + pass + + class Child(Parent): + pass + + type_assign_version(Parent) + type_assign_version(Child) + parent_version = type_get_version(Parent) + child_version = type_get_version(Child) + if parent_version == 0 or child_version == 0: + self.skipTest("Could not assign valid type versions") + + collections.abc.Mapping.register(Parent) + + self.assertEqual(type_get_version(Parent), 0) + self.assertEqual(type_get_version(Child), 0) + @support.cpython_only class TypeCacheWithSpecializationTests(unittest.TestCase): def tearDown(self): @@ -219,7 +239,7 @@ def store_bar_2(type_): self._check_specialization(store_bar_2, B(), "STORE_ATTR", should_specialize=False) - @requires_specialization_ft + @requires_specialization def test_class_call_specialization_user_type(self): class F: def __init__(self): diff --git a/Lib/test/test_type_comments.py b/Lib/test/test_type_comments.py index c40c45594f4d80..d827ac271085bd 100644 --- a/Lib/test/test_type_comments.py +++ b/Lib/test/test_type_comments.py @@ -1,6 +1,7 @@ import ast import sys import unittest +from test.support import import_helper funcdef = """\ @@ -391,6 +392,16 @@ def check_both_ways(source): check_both_ways("pass # type: ignorewhatever\n") check_both_ways("pass # type: ignoreé\n") + def test_non_utf8_type_comment_with_ignore_cookie(self): + _testcapi = import_helper.import_module('_testcapi') + flags = 0x0800 | 0x1000 # PyCF_IGNORE_COOKIE | PyCF_TYPE_COMMENTS + with self.assertRaises(UnicodeDecodeError): + _testcapi.Py_CompileStringExFlags( + b"a=1 # type: \x80", "<test>", 256, flags) + with self.assertRaises(UnicodeDecodeError): + _testcapi.Py_CompileStringExFlags( + b"def a(f=8, #type: \x80\n\x80", "<test>", 256, flags) + def test_func_type_input(self): def parse_func_type_input(source): diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py index 0f393def827271..84c1b954136736 100644 --- a/Lib/test/test_type_params.py +++ b/Lib/test/test_type_params.py @@ -152,6 +152,13 @@ def test_incorrect_mro_explicit_object(self): with self.assertRaisesRegex(TypeError, r"\(MRO\) for bases object, Generic"): class My[X](object): ... + def test_compile_error_in_type_param_bound(self): + # This should not crash, see gh-145187 + check_syntax_error( + self, + "if True:\n class h[l:{7for*()in 0}]:2" + ) + class TypeParamsNonlocalTest(unittest.TestCase): def test_nonlocal_disallowed_01(self): diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 9b0ae709d7968d..2084b30d71ff6c 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -41,7 +41,7 @@ def clear_typing_caches(): class TypesTests(unittest.TestCase): def test_names(self): - c_only_names = {'CapsuleType'} + c_only_names = {'CapsuleType', 'LazyImportType'} ignored = {'new_class', 'resolve_bases', 'prepare_class', 'get_original_bases', 'DynamicClassAttribute', 'coroutine'} @@ -55,10 +55,11 @@ def test_names(self): 'CoroutineType', 'EllipsisType', 'FrameType', 'FunctionType', 'FrameLocalsProxyType', 'GeneratorType', 'GenericAlias', 'GetSetDescriptorType', - 'LambdaType', 'MappingProxyType', 'MemberDescriptorType', - 'MethodDescriptorType', 'MethodType', 'MethodWrapperType', - 'ModuleType', 'NoneType', 'NotImplementedType', 'SimpleNamespace', - 'TracebackType', 'UnionType', 'WrapperDescriptorType', + 'LambdaType', 'LazyImportType', 'MappingProxyType', + 'MemberDescriptorType', 'MethodDescriptorType', 'MethodType', + 'MethodWrapperType', 'ModuleType', 'NoneType', + 'NotImplementedType', 'SimpleNamespace', 'TracebackType', + 'UnionType', 'WrapperDescriptorType', } self.assertEqual(all_names, set(c_types.__all__)) self.assertEqual(all_names - c_only_names, set(py_types.__all__)) @@ -714,7 +715,10 @@ def call(part): def test_frame_locals_proxy_type(self): self.assertIsInstance(types.FrameLocalsProxyType, type) - self.assertIsInstance(types.FrameLocalsProxyType.__doc__, str) + if MISSING_C_DOCSTRINGS: + self.assertIsNone(types.FrameLocalsProxyType.__doc__) + else: + self.assertIsInstance(types.FrameLocalsProxyType.__doc__, str) self.assertEqual(types.FrameLocalsProxyType.__module__, 'builtins') self.assertEqual(types.FrameLocalsProxyType.__name__, 'FrameLocalsProxy') @@ -2164,6 +2168,21 @@ class Spam(types.SimpleNamespace): self.assertIs(type(spam2), Spam) self.assertEqual(vars(spam2), {'ham': 5, 'eggs': 9}) + def test_replace_invalid_subtype(self): + # See https://github.com/python/cpython/issues/143636. + class MyNS(types.SimpleNamespace): + def __new__(cls, *args, **kwargs): + if created: + return 12345 + return super().__new__(cls) + + created = False + ns = MyNS() + created = True + err = (r"^expect types\.SimpleNamespace type, " + r"but .+\.MyNS\(\) returned 'int' object") + self.assertRaisesRegex(TypeError, err, copy.replace, ns) + def test_fake_namespace_compare(self): # Issue #24257: Incorrect use of PyObject_IsInstance() caused # SystemError. @@ -2291,8 +2310,8 @@ def foo(): return gen self.assertIs(wrapper.__name__, gen.__name__) # Test AttributeErrors - for name in {'gi_running', 'gi_frame', 'gi_code', 'gi_yieldfrom', - 'cr_running', 'cr_frame', 'cr_code', 'cr_await'}: + for name in {'gi_running', 'gi_frame', 'gi_code', 'gi_yieldfrom', 'gi_suspended', + 'cr_running', 'cr_frame', 'cr_code', 'cr_await', 'cr_suspended'}: with self.assertRaises(AttributeError): getattr(wrapper, name) @@ -2301,14 +2320,17 @@ def foo(): return gen gen.gi_frame = object() gen.gi_code = object() gen.gi_yieldfrom = object() + gen.gi_suspended = object() self.assertIs(wrapper.gi_running, gen.gi_running) self.assertIs(wrapper.gi_frame, gen.gi_frame) self.assertIs(wrapper.gi_code, gen.gi_code) self.assertIs(wrapper.gi_yieldfrom, gen.gi_yieldfrom) + self.assertIs(wrapper.gi_suspended, gen.gi_suspended) self.assertIs(wrapper.cr_running, gen.gi_running) self.assertIs(wrapper.cr_frame, gen.gi_frame) self.assertIs(wrapper.cr_code, gen.gi_code) self.assertIs(wrapper.cr_await, gen.gi_yieldfrom) + self.assertIs(wrapper.cr_suspended, gen.gi_suspended) wrapper.close() gen.close.assert_called_once_with() @@ -2427,7 +2449,7 @@ def foo(): return gen self.assertIs(wrapper.__await__(), gen) for name in ('__name__', '__qualname__', 'gi_code', - 'gi_running', 'gi_frame'): + 'gi_running', 'gi_frame', 'gi_suspended'): self.assertIs(getattr(foo(), name), getattr(gen, name)) self.assertIs(foo().cr_code, gen.gi_code) @@ -2490,8 +2512,8 @@ def coro(): self.assertEqual(repr(wrapper), str(wrapper)) self.assertTrue(set(dir(wrapper)).issuperset({ '__await__', '__iter__', '__next__', 'cr_code', 'cr_running', - 'cr_frame', 'gi_code', 'gi_frame', 'gi_running', 'send', - 'close', 'throw'})) + 'cr_frame', 'cr_suspended', 'gi_code', 'gi_frame', 'gi_running', + 'gi_suspended', 'send', 'close', 'throw'})) class FunctionTests(unittest.TestCase): diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index b1615bbff383c2..042604ed7c1a42 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -13,7 +13,8 @@ import pickle import re import sys -from unittest import TestCase, main, skip +import warnings +from unittest import TestCase, main from unittest.mock import patch from copy import copy, deepcopy @@ -28,20 +29,20 @@ from typing import assert_type, cast, runtime_checkable from typing import get_type_hints from typing import get_origin, get_args, get_protocol_members -from typing import override +from typing import override, disjoint_base from typing import is_typeddict, is_protocol from typing import reveal_type from typing import dataclass_transform -from typing import no_type_check, no_type_check_decorator +from typing import no_type_check from typing import Type -from typing import NamedTuple, NotRequired, Required, ReadOnly, TypedDict +from typing import NamedTuple, NotRequired, Required, ReadOnly, TypedDict, NoExtraItems from typing import IO, TextIO, BinaryIO from typing import Pattern, Match from typing import Annotated, ForwardRef from typing import Self, LiteralString from typing import TypeAlias from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs -from typing import TypeGuard, TypeIs, NoDefault +from typing import TypeForm, TypeGuard, TypeIs, NoDefault import abc import textwrap import typing @@ -50,7 +51,7 @@ from test.support import ( captured_stderr, cpython_only, requires_docstrings, import_helper, run_code, - EqualToForwardRef, + subTests, EqualToForwardRef, ) from test.typinganndata import ( ann_module695, mod_generics_cache, _typed_dict_helper, @@ -761,6 +762,16 @@ class A(Generic[T, P, U]): ... self.assertEqual(A[float, [range]].__args__, (float, (range,), float)) self.assertEqual(A[float, [range], int].__args__, (float, (range,), int)) + def test_paramspec_and_typevar_specialization_2(self): + T = TypeVar("T") + P = ParamSpec('P', default=...) + U = TypeVar("U", default=float) + self.assertEqual(P.__default__, ...) + class A(Generic[T, P, U]): ... + self.assertEqual(A[float].__args__, (float, ..., float)) + self.assertEqual(A[float, [range]].__args__, (float, (range,), float)) + self.assertEqual(A[float, [range], int].__args__, (float, (range,), int)) + def test_typevartuple_none(self): U = TypeVarTuple('U') U_None = TypeVarTuple('U_None', default=None) @@ -769,7 +780,7 @@ def test_typevartuple_none(self): self.assertIs(U_None.__default__, None) self.assertIs(U_None.has_default(), True) - class X[**Ts]: ... + class X[*Ts]: ... Ts, = X.__type_params__ self.assertIs(Ts.__default__, NoDefault) self.assertIs(Ts.has_default(), False) @@ -1277,6 +1288,57 @@ def test_cannot_call_instance(self): with self.assertRaises(TypeError): Ts() + def test_default_variance(self): + Ts = TypeVarTuple('Ts') + self.assertIs(Ts.__covariant__, False) + self.assertIs(Ts.__contravariant__, False) + self.assertIs(Ts.__infer_variance__, False) + self.assertIsNone(Ts.__bound__) + + def test_covariant(self): + Ts_co = TypeVarTuple('Ts_co', covariant=True) + self.assertIs(Ts_co.__covariant__, True) + self.assertIs(Ts_co.__contravariant__, False) + self.assertIs(Ts_co.__infer_variance__, False) + + def test_contravariant(self): + Ts_contra = TypeVarTuple('Ts_contra', contravariant=True) + self.assertIs(Ts_contra.__covariant__, False) + self.assertIs(Ts_contra.__contravariant__, True) + self.assertIs(Ts_contra.__infer_variance__, False) + + def test_infer_variance(self): + Ts = TypeVarTuple('Ts', infer_variance=True) + self.assertIs(Ts.__covariant__, False) + self.assertIs(Ts.__contravariant__, False) + self.assertIs(Ts.__infer_variance__, True) + + def test_bound(self): + Ts_bound = TypeVarTuple('Ts_bound', bound=int) + self.assertIs(Ts_bound.__bound__, int) + Ts_no_bound = TypeVarTuple('Ts_no_bound') + self.assertIsNone(Ts_no_bound.__bound__) + + def test_no_bivariant(self): + with self.assertRaises(ValueError): + TypeVarTuple('Ts', covariant=True, contravariant=True) + + def test_cannot_combine_explicit_and_infer(self): + with self.assertRaises(ValueError): + TypeVarTuple('Ts', covariant=True, infer_variance=True) + with self.assertRaises(ValueError): + TypeVarTuple('Ts', contravariant=True, infer_variance=True) + + def test_repr_with_variance(self): + Ts = TypeVarTuple('Ts') + self.assertEqual(repr(Ts), '~Ts') + Ts_co = TypeVarTuple('Ts_co', covariant=True) + self.assertEqual(repr(Ts_co), '+Ts_co') + Ts_contra = TypeVarTuple('Ts_contra', contravariant=True) + self.assertEqual(repr(Ts_contra), '-Ts_contra') + Ts_infer = TypeVarTuple('Ts_infer', infer_variance=True) + self.assertEqual(repr(Ts_infer), 'Ts_infer') + def test_unpacked_typevartuple_is_equal_to_itself(self): Ts = TypeVarTuple('Ts') self.assertEqual((*Ts,)[0], (*Ts,)[0]) @@ -1416,16 +1478,16 @@ def test_repr_is_correct(self): class G1(Generic[*Ts]): pass class G2(Generic[Unpack[Ts]]): pass - self.assertEqual(repr(Ts), 'Ts') + self.assertEqual(repr(Ts), '~Ts') - self.assertEqual(repr((*Ts,)[0]), 'typing.Unpack[Ts]') - self.assertEqual(repr(Unpack[Ts]), 'typing.Unpack[Ts]') + self.assertEqual(repr((*Ts,)[0]), 'typing.Unpack[~Ts]') + self.assertEqual(repr(Unpack[Ts]), 'typing.Unpack[~Ts]') - self.assertEqual(repr(tuple[*Ts]), 'tuple[typing.Unpack[Ts]]') - self.assertEqual(repr(Tuple[Unpack[Ts]]), 'typing.Tuple[typing.Unpack[Ts]]') + self.assertEqual(repr(tuple[*Ts]), 'tuple[typing.Unpack[~Ts]]') + self.assertEqual(repr(Tuple[Unpack[Ts]]), 'typing.Tuple[typing.Unpack[~Ts]]') - self.assertEqual(repr(*tuple[*Ts]), '*tuple[typing.Unpack[Ts]]') - self.assertEqual(repr(Unpack[Tuple[Unpack[Ts]]]), 'typing.Unpack[typing.Tuple[typing.Unpack[Ts]]]') + self.assertEqual(repr(*tuple[*Ts]), '*tuple[typing.Unpack[~Ts]]') + self.assertEqual(repr(Unpack[Tuple[Unpack[Ts]]]), 'typing.Unpack[typing.Tuple[typing.Unpack[~Ts]]]') def test_variadic_class_repr_is_correct(self): Ts = TypeVarTuple('Ts') @@ -1464,61 +1526,61 @@ def test_variadic_class_alias_repr_is_correct(self): class A(Generic[Unpack[Ts]]): pass B = A[*Ts] - self.assertEndsWith(repr(B), 'A[typing.Unpack[Ts]]') + self.assertEndsWith(repr(B), 'A[typing.Unpack[~Ts]]') self.assertEndsWith(repr(B[()]), 'A[()]') self.assertEndsWith(repr(B[float]), 'A[float]') self.assertEndsWith(repr(B[float, str]), 'A[float, str]') C = A[Unpack[Ts]] - self.assertEndsWith(repr(C), 'A[typing.Unpack[Ts]]') + self.assertEndsWith(repr(C), 'A[typing.Unpack[~Ts]]') self.assertEndsWith(repr(C[()]), 'A[()]') self.assertEndsWith(repr(C[float]), 'A[float]') self.assertEndsWith(repr(C[float, str]), 'A[float, str]') D = A[*Ts, int] - self.assertEndsWith(repr(D), 'A[typing.Unpack[Ts], int]') + self.assertEndsWith(repr(D), 'A[typing.Unpack[~Ts], int]') self.assertEndsWith(repr(D[()]), 'A[int]') self.assertEndsWith(repr(D[float]), 'A[float, int]') self.assertEndsWith(repr(D[float, str]), 'A[float, str, int]') E = A[Unpack[Ts], int] - self.assertEndsWith(repr(E), 'A[typing.Unpack[Ts], int]') + self.assertEndsWith(repr(E), 'A[typing.Unpack[~Ts], int]') self.assertEndsWith(repr(E[()]), 'A[int]') self.assertEndsWith(repr(E[float]), 'A[float, int]') self.assertEndsWith(repr(E[float, str]), 'A[float, str, int]') F = A[int, *Ts] - self.assertEndsWith(repr(F), 'A[int, typing.Unpack[Ts]]') + self.assertEndsWith(repr(F), 'A[int, typing.Unpack[~Ts]]') self.assertEndsWith(repr(F[()]), 'A[int]') self.assertEndsWith(repr(F[float]), 'A[int, float]') self.assertEndsWith(repr(F[float, str]), 'A[int, float, str]') G = A[int, Unpack[Ts]] - self.assertEndsWith(repr(G), 'A[int, typing.Unpack[Ts]]') + self.assertEndsWith(repr(G), 'A[int, typing.Unpack[~Ts]]') self.assertEndsWith(repr(G[()]), 'A[int]') self.assertEndsWith(repr(G[float]), 'A[int, float]') self.assertEndsWith(repr(G[float, str]), 'A[int, float, str]') H = A[int, *Ts, str] - self.assertEndsWith(repr(H), 'A[int, typing.Unpack[Ts], str]') + self.assertEndsWith(repr(H), 'A[int, typing.Unpack[~Ts], str]') self.assertEndsWith(repr(H[()]), 'A[int, str]') self.assertEndsWith(repr(H[float]), 'A[int, float, str]') self.assertEndsWith(repr(H[float, str]), 'A[int, float, str, str]') I = A[int, Unpack[Ts], str] - self.assertEndsWith(repr(I), 'A[int, typing.Unpack[Ts], str]') + self.assertEndsWith(repr(I), 'A[int, typing.Unpack[~Ts], str]') self.assertEndsWith(repr(I[()]), 'A[int, str]') self.assertEndsWith(repr(I[float]), 'A[int, float, str]') self.assertEndsWith(repr(I[float, str]), 'A[int, float, str, str]') J = A[*Ts, *tuple[str, ...]] - self.assertEndsWith(repr(J), 'A[typing.Unpack[Ts], *tuple[str, ...]]') + self.assertEndsWith(repr(J), 'A[typing.Unpack[~Ts], *tuple[str, ...]]') self.assertEndsWith(repr(J[()]), 'A[*tuple[str, ...]]') self.assertEndsWith(repr(J[float]), 'A[float, *tuple[str, ...]]') self.assertEndsWith(repr(J[float, str]), 'A[float, str, *tuple[str, ...]]') K = A[Unpack[Ts], Unpack[Tuple[str, ...]]] - self.assertEndsWith(repr(K), 'A[typing.Unpack[Ts], typing.Unpack[typing.Tuple[str, ...]]]') + self.assertEndsWith(repr(K), 'A[typing.Unpack[~Ts], typing.Unpack[typing.Tuple[str, ...]]]') self.assertEndsWith(repr(K[()]), 'A[typing.Unpack[typing.Tuple[str, ...]]]') self.assertEndsWith(repr(K[float]), 'A[float, typing.Unpack[typing.Tuple[str, ...]]]') self.assertEndsWith(repr(K[float, str]), 'A[float, str, typing.Unpack[typing.Tuple[str, ...]]]') @@ -1539,9 +1601,9 @@ class G(type(Unpack[Ts])): pass with self.assertRaisesRegex(TypeError, r'Cannot subclass typing\.Unpack'): class H(Unpack): pass - with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[Ts\]'): + with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[~Ts\]'): class I(*Ts): pass - with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[Ts\]'): + with self.assertRaisesRegex(TypeError, r'Cannot subclass typing.Unpack\[~Ts\]'): class J(Unpack[Ts]): pass def test_variadic_class_args_are_correct(self): @@ -2272,6 +2334,15 @@ class Ints(enum.IntEnum): self.assertEqual(Union[Literal[1], Literal[Ints.B], Literal[True]].__args__, (Literal[1], Literal[Ints.B], Literal[True])) + def test_allow_non_types_in_or(self): + # gh-140348: Test that using | with a Union object allows things that are + # not allowed by is_unionable(). + U1 = Union[int, str] + self.assertEqual(U1 | float, Union[int, str, float]) + self.assertEqual(U1 | "float", Union[int, str, "float"]) + self.assertEqual(float | U1, Union[float, int, str]) + self.assertEqual("float" | U1, Union["float", int, str]) + class TupleTests(BaseTestCase): @@ -3865,8 +3936,8 @@ def meth(self): pass self.assertIsNot(get_protocol_members(PR), P.__protocol_attrs__) acceptable_extra_attrs = { - '_is_protocol', '_is_runtime_protocol', '__parameters__', - '__init__', '__annotations__', '__subclasshook__', '__annotate__', + '_is_protocol', '_is_runtime_protocol', '__typing_is_deprecated_inherited_runtime_protocol__', + '__parameters__', '__init__', '__annotations__', '__subclasshook__', '__annotate__', '__annotations_cache__', '__annotate_func__', } self.assertLessEqual(vars(NonP).keys(), vars(C).keys() | acceptable_extra_attrs) @@ -3958,6 +4029,7 @@ class C: pass def test_defining_generic_protocols(self): T = TypeVar('T') + T2 = TypeVar('T2') S = TypeVar('S') @runtime_checkable @@ -3967,17 +4039,26 @@ def meth(self): pass class P(PR[int, T], Protocol[T]): y = 1 + self.assertEqual(P.__parameters__, (T,)) + with self.assertRaises(TypeError): PR[int] with self.assertRaises(TypeError): P[int, str] + with self.assertRaisesRegex( + TypeError, + re.escape('Some type variables (~S) are not listed in Protocol[~T, ~T2]'), + ): + class ExtraTypeVars(P[S], Protocol[T, T2]): ... class C(PR[int, T]): pass + self.assertEqual(C.__parameters__, (T,)) self.assertIsInstance(C[str](), C) def test_defining_generic_protocols_old_style(self): T = TypeVar('T') + T2 = TypeVar('T2') S = TypeVar('S') @runtime_checkable @@ -3996,9 +4077,19 @@ class P(PR[int, str], Protocol): class P1(Protocol, Generic[T]): def bar(self, x: T) -> str: ... + self.assertEqual(P1.__parameters__, (T,)) + class P2(Generic[T], Protocol): def bar(self, x: T) -> str: ... + self.assertEqual(P2.__parameters__, (T,)) + + msg = re.escape('Some type variables (~S) are not listed in Protocol[~T, ~T2]') + with self.assertRaisesRegex(TypeError, msg): + class ExtraTypeVars(P1[S], Protocol[T, T2]): ... + with self.assertRaisesRegex(TypeError, msg): + class ExtraTypeVars(P2[S], Protocol[T, T2]): ... + @runtime_checkable class PSub(P1[str], Protocol): x = 1 @@ -4011,6 +4102,28 @@ def bar(self, x: str) -> str: self.assertIsInstance(Test(), PSub) + def test_protocol_parameter_order(self): + # https://github.com/python/cpython/issues/137191 + T1 = TypeVar("T1") + T2 = TypeVar("T2", default=object) + + class A(Protocol[T1]): ... + + class B0(A[T2], Generic[T1, T2]): ... + self.assertEqual(B0.__parameters__, (T1, T2)) + + class B1(A[T2], Protocol, Generic[T1, T2]): ... + self.assertEqual(B1.__parameters__, (T1, T2)) + + class B2(A[T2], Protocol[T1, T2]): ... + self.assertEqual(B2.__parameters__, (T1, T2)) + + class B3[T1, T2](A[T2], Protocol): + @staticmethod + def get_typeparams(): + return (T1, T2) + self.assertEqual(B3.__parameters__, B3.get_typeparams()) + def test_pep695_generic_protocol_callable_members(self): @runtime_checkable class Foo[T](Protocol): @@ -4396,6 +4509,70 @@ class P(Protocol): with self.assertRaisesRegex(TypeError, "@runtime_checkable"): isinstance(1, P) + @subTests(['check_obj', 'check_func'], ([42, isinstance], [frozenset, issubclass])) + def test_inherited_runtime_protocol_deprecated(self, check_obj, check_func): + """See GH-132604.""" + + class BareProto(Protocol): + """I am not runtime-checkable.""" + + @runtime_checkable + class RCProto1(Protocol): + """I am runtime-checkable.""" + + class InheritedRCProto1(RCProto1, Protocol): + """I am accidentally runtime-checkable (by inheritance).""" + + @runtime_checkable + class RCProto2(InheritedRCProto1, Protocol): + """Explicit RC -> inherited RC -> explicit RC.""" + def spam(self): ... + + @runtime_checkable + class RCProto3(BareProto, Protocol): + """Not RC -> explicit RC.""" + + class InheritedRCProto2(RCProto3, Protocol): + """Not RC -> explicit RC -> inherited RC.""" + def eggs(self): ... + + class InheritedRCProto3(RCProto2, Protocol): + """Explicit RC -> inherited RC -> explicit RC -> inherited RC.""" + + class Concrete1(BareProto): + pass + + class Concrete2(InheritedRCProto2): + pass + + class Concrete3(InheritedRCProto3): + pass + + depr_message_re = ( + r"<class .+\.InheritedRCProto\d'> isn't explicitly decorated " + r"with @runtime_checkable but it is used in issubclass\(\) or " + r"isinstance\(\). Instance and class checks can only be used with " + r"@runtime_checkable protocols. This will raise a TypeError in Python 3.20." + ) + + for inherited_runtime_proto in InheritedRCProto1, InheritedRCProto2, InheritedRCProto3: + with self.assertWarnsRegex(DeprecationWarning, depr_message_re): + check_func(check_obj, inherited_runtime_proto) + + # Don't warn for explicitly checkable protocols and concrete implementations. + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + + for checkable in RCProto1, RCProto2, RCProto3, Concrete1, Concrete2, Concrete3: + check_func(check_obj, checkable) + + # Don't warn for uncheckable protocols. + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + + with self.assertRaises(TypeError): # Self-test. Protocol below can't be runtime-checkable. + check_func(check_obj, BareProto) + def test_super_call_init(self): class P(Protocol): x: int @@ -4679,6 +4856,34 @@ class D(Generic[T]): pass with self.assertRaises(TypeError): D[()] + def test_generic_init_subclass_not_called_error(self): + notes = ["Note: this exception may have been caused by " + r"'GenericTests.test_generic_init_subclass_not_called_error.<locals>.Base.__init_subclass__' " + "(or the '__init_subclass__' method on a superclass) not calling 'super().__init_subclass__()'"] + + class Base: + def __init_subclass__(cls) -> None: + # Oops, I forgot super().__init_subclass__()! + pass + + with self.subTest(): + class Sub(Base, Generic[T]): + pass + + with self.assertRaises(AttributeError) as cm: + Sub[int] + + self.assertEqual(cm.exception.__notes__, notes) + + with self.subTest(): + class Sub[U](Base): + pass + + with self.assertRaises(AttributeError) as cm: + Sub[int] + + self.assertEqual(cm.exception.__notes__, notes) + def test_generic_subclass_checks(self): for typ in [list[int], List[int], tuple[int, str], Tuple[int, str], @@ -5442,13 +5647,13 @@ class TsP(Generic[*Ts, P]): MyCallable[[int], bool]: "MyCallable[[int], bool]", MyCallable[[int, str], bool]: "MyCallable[[int, str], bool]", MyCallable[[int, list[int]], bool]: "MyCallable[[int, list[int]], bool]", - MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[typing.Unpack[Ts], ~P], ~T]", + MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[typing.Unpack[~Ts], ~P], ~T]", DoubleSpec[P2, P, T]: "DoubleSpec[~P2, ~P, ~T]", DoubleSpec[[int], [str], bool]: "DoubleSpec[[int], [str], bool]", DoubleSpec[[int, int], [str, str], bool]: "DoubleSpec[[int, int], [str, str], bool]", - TsP[*Ts, P]: "TsP[typing.Unpack[Ts], ~P]", + TsP[*Ts, P]: "TsP[typing.Unpack[~Ts], ~P]", TsP[int, str, list[int], []]: "TsP[int, str, list[int], []]", TsP[int, [str, list[int]]]: "TsP[int, [str, list[int]]]", @@ -5643,6 +5848,27 @@ def foo(x: T): foo(42) + def test_genericalias_instance_isclass(self): + # test against user-defined generic classes + T = TypeVar('T') + + class Node(Generic[T]): + def __init__(self, label: T, + left: 'Node[T] | None' = None, + right: 'Node[T] | None' = None): + self.label = label + self.left = left + self.right = right + + self.assertTrue(inspect.isclass(Node)) + self.assertFalse(inspect.isclass(Node[int])) + self.assertFalse(inspect.isclass(Node[str])) + + # test against standard generic classes + self.assertFalse(inspect.isclass(set[int])) + self.assertFalse(inspect.isclass(list[bytes])) + self.assertFalse(inspect.isclass(dict[str, str])) + def test_implicit_any(self): T = TypeVar('T') @@ -5736,6 +5962,7 @@ def test_subclass_special_form(self): Final[int], Literal[1, 2], Concatenate[int, ParamSpec("P")], + TypeForm[int], TypeGuard[int], TypeIs[range], ): @@ -5802,6 +6029,23 @@ class A: with self.assertRaises(TypeError): a[int] + def test_return_non_tuple_while_unpacking(self): + # GH-138497: GenericAlias objects didn't ensure that __typing_subst__ actually + # returned a tuple + class EvilTypeVar: + __typing_is_unpacked_typevartuple__ = True + def __typing_prepare_subst__(*_): + return None # any value + def __typing_subst__(*_): + return 42 # not tuple + + evil = EvilTypeVar() + # Create a dummy TypeAlias that will be given the evil generic from + # above. + type type_alias[*_] = 0 + with self.assertRaisesRegex(TypeError, ".+__typing_subst__.+tuple.+int.*"): + type_alias[evil][0] + class ClassVarTests(BaseTestCase): @@ -6278,35 +6522,6 @@ class F: for clazz in [C, D, E, F]: self.assertEqual(get_type_hints(clazz), expected_result) - def test_meta_no_type_check(self): - depr_msg = ( - "'typing.no_type_check_decorator' is deprecated " - "and slated for removal in Python 3.15" - ) - with self.assertWarnsRegex(DeprecationWarning, depr_msg): - @no_type_check_decorator - def magic_decorator(func): - return func - - self.assertEqual(magic_decorator.__name__, 'magic_decorator') - - @magic_decorator - def foo(a: 'whatevers') -> {}: - pass - - @magic_decorator - class C: - def foo(a: 'whatevers') -> {}: - pass - - self.assertEqual(foo.__name__, 'foo') - th = get_type_hints(foo) - self.assertEqual(th, {}) - cth = get_type_hints(C.foo) - self.assertEqual(cth, {}) - ith = get_type_hints(C().foo) - self.assertEqual(ith, {}) - class InternalsTests(BaseTestCase): def test_collect_parameters(self): @@ -6602,11 +6817,7 @@ def test_get_type_hints_modules(self): self.assertEqual(gth(ann_module2), {}) self.assertEqual(gth(ann_module3), {}) - @skip("known bug") def test_get_type_hints_modules_forwardref(self): - # FIXME: This currently exposes a bug in typing. Cached forward references - # don't account for the case where there are multiple types of the same - # name coming from different modules in the same program. mgc_hints = {'default_a': Optional[mod_generics_cache.A], 'default_b': Optional[mod_generics_cache.B]} self.assertEqual(gth(mod_generics_cache), mgc_hints) @@ -6694,6 +6905,24 @@ def test_get_type_hints_wrapped_decoratored_func(self): self.assertEqual(gth(ForRefExample.func), expects) self.assertEqual(gth(ForRefExample.nested), expects) + def test_get_type_hints_wrapped_cycle_self(self): + # gh-146553: __wrapped__ self-reference must raise ValueError, + # not loop forever. + def f(x: int) -> str: ... + f.__wrapped__ = f + with self.assertRaisesRegex(ValueError, 'wrapper loop'): + get_type_hints(f) + + def test_get_type_hints_wrapped_cycle_mutual(self): + # gh-146553: mutual __wrapped__ cycle (a -> b -> a) must raise + # ValueError, not loop forever. + def a(): ... + def b(): ... + a.__wrapped__ = b + b.__wrapped__ = a + with self.assertRaisesRegex(ValueError, 'wrapper loop'): + get_type_hints(a) + def test_get_type_hints_annotated(self): def foobar(x: List['X']): ... X = Annotated[int, (1, 10)] @@ -7113,6 +7342,26 @@ def func(x: MyClass['int'], y: MyClass[Annotated[int, ...]]): ... assert isinstance(get_type_hints(func)['x'], MyAlias) assert isinstance(get_type_hints(func)['y'], MyAlias) + def test_stringified_typeddict(self): + ns = run_code( + """ + from __future__ import annotations + from typing import TypedDict + class TD[UniqueT](TypedDict): + a: UniqueT + """ + ) + TD = ns['TD'] + self.assertEqual(TD.__annotations__, {'a': EqualToForwardRef('UniqueT', owner=TD, module=TD.__module__)}) + self.assertEqual(get_type_hints(TD), {'a': TD.__type_params__[0]}) + + def test_get_type_hints_order(self): + """Ensure that the order of function annotations matches the order they're defined""" + def f(positional: int, /, normal: str, *args: bytes, kwarg: list, **kwargs: bool) -> tuple: + pass + + self.assertEqual(list(gth(f)), ["positional", "normal", "args", "kwarg", "kwargs", "return"]) + class GetUtilitiesTestCase(TestCase): def test_get_origin(self): @@ -7203,6 +7452,7 @@ class C(Generic[T]): pass self.assertEqual(get_args(Required[int]), (int,)) self.assertEqual(get_args(NotRequired[int]), (int,)) self.assertEqual(get_args(TypeAlias), ()) + self.assertEqual(get_args(TypeForm[int]), (int,)) self.assertEqual(get_args(TypeGuard[int]), (int,)) self.assertEqual(get_args(TypeIs[range]), (range,)) Ts = TypeVarTuple('Ts') @@ -7427,6 +7677,25 @@ def test_mutablesequence(self): self.assertIsInstance([], typing.MutableSequence) self.assertNotIsInstance((), typing.MutableSequence) + def test_bytestring(self): + previous_typing_module = sys.modules.pop("typing", None) + self.addCleanup(sys.modules.__setitem__, "typing", previous_typing_module) + + with self.assertWarns(DeprecationWarning): + from typing import ByteString + with self.assertWarns(DeprecationWarning): + self.assertIsInstance(b'', ByteString) + with self.assertWarns(DeprecationWarning): + self.assertIsInstance(bytearray(b''), ByteString) + with self.assertWarns(DeprecationWarning): + self.assertIsSubclass(bytes, ByteString) + with self.assertWarns(DeprecationWarning): + self.assertIsSubclass(bytearray, ByteString) + with self.assertWarns(DeprecationWarning): + class Foo(ByteString): ... + with self.assertWarns(DeprecationWarning): + class Bar(ByteString, typing.Awaitable): ... + def test_list(self): self.assertIsSubclass(list, typing.List) @@ -8358,6 +8627,15 @@ class ThisWontWorkEither(NamedTuple): def name(self): return __class__.__name__ + def test_named_tuple_non_sequence_input(self): + field_names = ["x", "y"] + field_values = [int, int] + Point = NamedTuple("Point", zip(field_names, field_values)) + p = Point(1, 2) + self.assertEqual(p.x, 1) + self.assertEqual(p.y, 2) + self.assertEqual(repr(p),"Point(x=1, y=2)") + class TypedDictTests(BaseTestCase): def test_basics_functional_syntax(self): @@ -8598,8 +8876,8 @@ def _make_td(future, class_name, annos, base, extra_names=None): child = _make_td( child_future, "Child", {"child": "int"}, "Base", {"Base": base} ) - base_anno = ForwardRef("int", module="builtins") if base_future else int - child_anno = ForwardRef("int", module="builtins") if child_future else int + base_anno = ForwardRef("int", module="builtins", owner=base) if base_future else int + child_anno = ForwardRef("int", module="builtins", owner=child) if child_future else int self.assertEqual(base.__annotations__, {'base': base_anno}) self.assertEqual( child.__annotations__, {'child': child_anno, 'base': base_anno} @@ -8728,6 +9006,32 @@ class ChildWithInlineAndOptional(Untotal, Inline): class Wrong(*bases): pass + def test_closed_values(self): + class Implicit(TypedDict): ... + class ExplicitTrue(TypedDict, closed=True): ... + class ExplicitFalse(TypedDict, closed=False): ... + + self.assertIsNone(Implicit.__closed__) + self.assertIs(ExplicitTrue.__closed__, True) + self.assertIs(ExplicitFalse.__closed__, False) + + def test_extra_items_class_arg(self): + class TD(TypedDict, extra_items=int): + a: str + + self.assertIs(TD.__extra_items__, int) + self.assertEqual(TD.__annotations__, {'a': str}) + self.assertEqual(TD.__required_keys__, frozenset({'a'})) + self.assertEqual(TD.__optional_keys__, frozenset()) + + class NoExtra(TypedDict): + a: str + + self.assertIs(NoExtra.__extra_items__, NoExtraItems) + self.assertEqual(NoExtra.__annotations__, {'a': str}) + self.assertEqual(NoExtra.__required_keys__, frozenset({'a'})) + self.assertEqual(NoExtra.__optional_keys__, frozenset()) + def test_is_typeddict(self): self.assertIs(is_typeddict(Point2D), True) self.assertIs(is_typeddict(Union[str, int]), False) @@ -9055,6 +9359,71 @@ class AllTheThings(TypedDict): }, ) + def test_closed_inheritance(self): + class Base(TypedDict, extra_items=ReadOnly[Union[str, None]]): + a: int + + self.assertEqual(Base.__required_keys__, frozenset({"a"})) + self.assertEqual(Base.__optional_keys__, frozenset({})) + self.assertEqual(Base.__readonly_keys__, frozenset({})) + self.assertEqual(Base.__mutable_keys__, frozenset({"a"})) + self.assertEqual(Base.__annotations__, {"a": int}) + self.assertEqual(Base.__extra_items__, ReadOnly[Union[str, None]]) + self.assertIsNone(Base.__closed__) + + class Child(Base, extra_items=int): + a: str + + self.assertEqual(Child.__required_keys__, frozenset({'a'})) + self.assertEqual(Child.__optional_keys__, frozenset({})) + self.assertEqual(Child.__readonly_keys__, frozenset({})) + self.assertEqual(Child.__mutable_keys__, frozenset({'a'})) + self.assertEqual(Child.__annotations__, {"a": str}) + self.assertIs(Child.__extra_items__, int) + self.assertIsNone(Child.__closed__) + + class GrandChild(Child, closed=True): + a: float + + self.assertEqual(GrandChild.__required_keys__, frozenset({'a'})) + self.assertEqual(GrandChild.__optional_keys__, frozenset({})) + self.assertEqual(GrandChild.__readonly_keys__, frozenset({})) + self.assertEqual(GrandChild.__mutable_keys__, frozenset({'a'})) + self.assertEqual(GrandChild.__annotations__, {"a": float}) + self.assertIs(GrandChild.__extra_items__, NoExtraItems) + self.assertIs(GrandChild.__closed__, True) + + class GrandGrandChild(GrandChild): + ... + self.assertEqual(GrandGrandChild.__required_keys__, frozenset({'a'})) + self.assertEqual(GrandGrandChild.__optional_keys__, frozenset({})) + self.assertEqual(GrandGrandChild.__readonly_keys__, frozenset({})) + self.assertEqual(GrandGrandChild.__mutable_keys__, frozenset({'a'})) + self.assertEqual(GrandGrandChild.__annotations__, {"a": float}) + self.assertIs(GrandGrandChild.__extra_items__, NoExtraItems) + self.assertIsNone(GrandGrandChild.__closed__) + + def test_implicit_extra_items(self): + class Base(TypedDict): + a: int + + self.assertIs(Base.__extra_items__, NoExtraItems) + self.assertIsNone(Base.__closed__) + + class ChildA(Base, closed=True): + ... + + self.assertEqual(ChildA.__extra_items__, NoExtraItems) + self.assertIs(ChildA.__closed__, True) + + def test_cannot_combine_closed_and_extra_items(self): + with self.assertRaisesRegex( + TypeError, + "Cannot combine closed=True and extra_items" + ): + class TD(TypedDict, closed=True, extra_items=range): + x: str + def test_annotations(self): # _type_check is applied with self.assertRaisesRegex(TypeError, "Plain typing.Final is not valid as type argument"): @@ -9284,6 +9653,12 @@ class A(typing.Match): class B(typing.Pattern): pass + def test_typed_dict_signature(self): + self.assertListEqual( + list(inspect.signature(TypedDict).parameters), + ['typename', 'fields', 'total', 'closed', 'extra_items'] + ) + class AnnotatedTests(BaseTestCase): @@ -9746,6 +10121,19 @@ class B(str): ... self.assertIs(type(field_c2.__metadata__[0]), float) self.assertIs(type(field_c3.__metadata__[0]), bool) + def test_forwardref_partial_evaluation(self): + # Test that Annotated partially evaluates if it contains a ForwardRef + # See: https://github.com/python/cpython/issues/137706 + def f(x: Annotated[undefined, '']): pass + + ann = annotationlib.get_annotations(f, format=annotationlib.Format.FORWARDREF) + + # Test that the attributes are retrievable from the partially evaluated annotation + x_ann = ann['x'] + self.assertIs(get_origin(x_ann), Annotated) + self.assertEqual(x_ann.__origin__, EqualToForwardRef('undefined', owner=f)) + self.assertEqual(x_ann.__metadata__, ('',)) + class TypeAliasTests(BaseTestCase): def test_canonical_usage_with_variable_annotation(self): @@ -10353,6 +10741,72 @@ def test_no_isinstance(self): issubclass(int, TypeIs) +class TypeFormTests(BaseTestCase): + def test_basics(self): + TypeForm[int] # OK + self.assertEqual(TypeForm[int], TypeForm[int]) + + def foo(arg) -> TypeForm[int]: ... + self.assertEqual(gth(foo), {'return': TypeForm[int]}) + + with self.assertRaises(TypeError): + TypeForm[int, str] + + def test_repr(self): + self.assertEqual(repr(TypeForm), 'typing.TypeForm') + cv = TypeForm[int] + self.assertEqual(repr(cv), 'typing.TypeForm[int]') + cv = TypeForm[Employee] + self.assertEqual(repr(cv), 'typing.TypeForm[%s.Employee]' % __name__) + cv = TypeForm[tuple[int]] + self.assertEqual(repr(cv), 'typing.TypeForm[tuple[int]]') + + def test_cannot_subclass(self): + with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE): + class C(type(TypeForm)): + pass + with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE): + class D(type(TypeForm[int])): + pass + with self.assertRaisesRegex(TypeError, + r'Cannot subclass typing\.TypeForm'): + class E(TypeForm): + pass + with self.assertRaisesRegex(TypeError, + r'Cannot subclass typing\.TypeForm\[int\]'): + class F(TypeForm[int]): + pass + + def test_call(self): + objs = [ + 1, + "int", + int, + tuple[int, str], + Tuple[int, str], + ] + for obj in objs: + with self.subTest(obj=obj): + self.assertIs(TypeForm(obj), obj) + + with self.assertRaises(TypeError): + TypeForm() + with self.assertRaises(TypeError): + TypeForm("too", "many") + + def test_cannot_init_type(self): + with self.assertRaises(TypeError): + type(TypeForm)() + with self.assertRaises(TypeError): + type(TypeForm[Optional[int]])() + + def test_no_isinstance(self): + with self.assertRaises(TypeError): + isinstance(1, TypeForm[int]) + with self.assertRaises(TypeError): + issubclass(int, TypeForm) + + SpecialAttrsP = typing.ParamSpec('SpecialAttrsP') SpecialAttrsT = typing.TypeVar('SpecialAttrsT', int, float, complex) @@ -10360,6 +10814,10 @@ def test_no_isinstance(self): class SpecialAttrsTests(BaseTestCase): def test_special_attrs(self): + with warnings.catch_warnings( + action='ignore', category=DeprecationWarning + ): + typing_ByteString = typing.ByteString cls_to_check = { # ABC classes typing.AbstractSet: 'AbstractSet', @@ -10368,6 +10826,7 @@ def test_special_attrs(self): typing.AsyncIterable: 'AsyncIterable', typing.AsyncIterator: 'AsyncIterator', typing.Awaitable: 'Awaitable', + typing_ByteString: 'ByteString', typing.Callable: 'Callable', typing.ChainMap: 'ChainMap', typing.Collection: 'Collection', @@ -10449,6 +10908,7 @@ def test_special_attrs(self): typing.Never: 'Never', typing.Optional: 'Optional', typing.TypeAlias: 'TypeAlias', + typing.TypeForm: 'TypeForm', typing.TypeGuard: 'TypeGuard', typing.TypeIs: 'TypeIs', typing.TypeVar: 'TypeVar', @@ -10463,6 +10923,7 @@ def test_special_attrs(self): typing.Literal[1, 2]: 'Literal', typing.Literal[True, 2]: 'Literal', typing.Optional[Any]: 'Union', + typing.TypeForm[Any]: 'TypeForm', typing.TypeGuard[Any]: 'TypeGuard', typing.TypeIs[Any]: 'TypeIs', typing.Union[Any]: 'Any', @@ -10545,6 +11006,18 @@ def bar(self): self.assertNotIn('__magic__', dir_items) +class DisjointBaseTests(BaseTestCase): + def test_disjoint_base_unmodified(self): + class C: ... + self.assertIs(C, disjoint_base(C)) + + def test_dunder_disjoint_base(self): + @disjoint_base + class C: ... + + self.assertIs(C.__disjoint_base__, True) + + class RevealTypeTests(BaseTestCase): def test_reveal_type(self): obj = object() @@ -10679,6 +11152,10 @@ def test_no_attributes(self): with self.assertRaises(AttributeError): type(NoDefault).foo + def test_no_subclassing(self): + with self.assertRaises(TypeError): + class Test(type(NoDefault)): ... + class AllTests(BaseTestCase): """Tests for __all__.""" @@ -10720,7 +11197,8 @@ def test_all_exported_names(self): # there's a few types and metaclasses that aren't exported not k.endswith(('Meta', '_contra', '_co')) and not k.upper() == k and - # but export all things that have __module__ == 'typing' + k not in {"ByteString"} and + # but export all other things that have __module__ == 'typing' getattr(v, '__module__', None) == typing.__name__ ) } diff --git a/Lib/test/test_ucn.py b/Lib/test/test_ucn.py index 0e2c25aaff2fe9..5f4f1b8e52ccef 100644 --- a/Lib/test/test_ucn.py +++ b/Lib/test/test_ucn.py @@ -88,6 +88,9 @@ def test_hangul_syllables(self): self.checkletter("HANGUL SYLLABLE HWEOK", "\ud6f8") self.checkletter("HANGUL SYLLABLE HIH", "\ud7a3") + self.checkletter("haNGul SYllABle WAe", '\uc65c') + self.checkletter("HAngUL syLLabLE waE", '\uc65c') + self.assertRaises(ValueError, unicodedata.name, "\ud7a4") def test_cjk_unified_ideographs(self): @@ -103,6 +106,35 @@ def test_cjk_unified_ideographs(self): self.checkletter("CJK UNIFIED IDEOGRAPH-2B81D", "\U0002B81D") self.checkletter("CJK UNIFIED IDEOGRAPH-3134A", "\U0003134A") + self.checkletter("cjK UniFIeD idEogRAph-3aBc", "\u3abc") + self.checkletter("CJk uNIfiEd IDeOGraPH-3AbC", "\u3abc") + self.checkletter("cjK UniFIeD idEogRAph-2aBcD", "\U0002abcd") + self.checkletter("CJk uNIfiEd IDeOGraPH-2AbCd", "\U0002abcd") + + def test_tangut_ideographs(self): + self.checkletter("TANGUT IDEOGRAPH-17000", "\U00017000") + self.checkletter("TANGUT IDEOGRAPH-187FF", "\U000187ff") + self.checkletter("TANGUT IDEOGRAPH-18D00", "\U00018D00") + self.checkletter("TANGUT IDEOGRAPH-18D1E", "\U00018d1e") + self.checkletter("tangut ideograph-18d1e", "\U00018d1e") + + def test_egyptian_hieroglyphs(self): + self.checkletter("EGYPTIAN HIEROGLYPH-13460", "\U00013460") + self.checkletter("EGYPTIAN HIEROGLYPH-143FA", "\U000143fa") + self.checkletter("egyptian hieroglyph-143fa", "\U000143fa") + + def test_khitan_small_script_characters(self): + self.checkletter("KHITAN SMALL SCRIPT CHARACTER-18B00", "\U00018b00") + self.checkletter("KHITAN SMALL SCRIPT CHARACTER-18CD5", "\U00018cd5") + self.checkletter("KHITAN SMALL SCRIPT CHARACTER-18CFF", "\U00018cff") + self.checkletter("KHITAN SMALL SCRIPT CHARACTER-18CFF", "\U00018cff") + self.checkletter("khitan small script character-18cff", "\U00018cff") + + def test_nushu_characters(self): + self.checkletter("NUSHU CHARACTER-1B170", "\U0001b170") + self.checkletter("NUSHU CHARACTER-1B2FB", "\U0001b2fb") + self.checkletter("nushu character-1b2fb", "\U0001b2fb") + def test_bmp_characters(self): for code in range(0x10000): char = chr(code) diff --git a/Lib/test/test_unicodedata.py b/Lib/test/test_unicodedata.py index 8e3fef6b6fe4a0..ad25be3da8cb34 100644 --- a/Lib/test/test_unicodedata.py +++ b/Lib/test/test_unicodedata.py @@ -6,32 +6,66 @@ """ +from functools import partial import hashlib from http.client import HTTPException import sys import unicodedata import unittest +import weakref from test.support import ( + gc_collect, open_urlresource, requires_resource, script_helper, cpython_only, check_disallow_instantiation, force_not_colorized, + is_resource_enabled, + findfile, ) +quicktest = not is_resource_enabled('cpu') + +def iterallchars(): + maxunicode = 0xffff if quicktest else sys.maxunicode + return map(chr, range(maxunicode + 1)) + + +def check_version(testfile): + hdr = testfile.readline() + return unicodedata.unidata_version in hdr + + +def download_test_data_file(filename): + TESTDATAURL = f"http://www.pythontest.net/unicode/{unicodedata.unidata_version}/{filename}" + + try: + return open_urlresource(TESTDATAURL, encoding="utf-8", check=check_version) + except PermissionError: + raise unittest.SkipTest( + f"Permission error when downloading {TESTDATAURL} " + f"into the test data directory" + ) + except (OSError, HTTPException) as exc: + raise unittest.SkipTest(f"Failed to download {TESTDATAURL}: {exc}") + + class UnicodeMethodsTest(unittest.TestCase): # update this, if the database changes - expectedchecksum = '9e43ee3929471739680c0e705482b4ae1c4122e4' + expectedchecksum = ('47a99fa654ef1f50e89d2e9697b7b041fccb5a05' + if quicktest else + '8b2615a9fc627676cbc0b6fac0191177df97ef5f') - @requires_resource('cpu') def test_method_checksum(self): h = hashlib.sha1() - for i in range(sys.maxunicode + 1): - char = chr(i) - data = [ + for char in iterallchars(): + s1 = char + 'abc' + s2 = char + 'ABC' + s3 = char + '123' + data = ( # Predicates (single char) "01"[char.isalnum()], "01"[char.isalpha()], @@ -44,15 +78,15 @@ def test_method_checksum(self): "01"[char.isupper()], # Predicates (multiple chars) - "01"[(char + 'abc').isalnum()], - "01"[(char + 'abc').isalpha()], - "01"[(char + '123').isdecimal()], - "01"[(char + '123').isdigit()], - "01"[(char + 'abc').islower()], - "01"[(char + '123').isnumeric()], + "01"[s1.isalnum()], + "01"[s1.isalpha()], + "01"[s3.isdecimal()], + "01"[s3.isdigit()], + "01"[s1.islower()], + "01"[s3.isnumeric()], "01"[(char + ' \t').isspace()], - "01"[(char + 'abc').istitle()], - "01"[(char + 'ABC').isupper()], + "01"[s1.istitle()], + "01"[s2.isupper()], # Mappings (single char) char.lower(), @@ -60,54 +94,100 @@ def test_method_checksum(self): char.title(), # Mappings (multiple chars) - (char + 'abc').lower(), - (char + 'ABC').upper(), - (char + 'abc').title(), - (char + 'ABC').title(), + s1.lower(), + s2.upper(), + s1.title(), + s2.title(), - ] + ) h.update(''.join(data).encode('utf-8', 'surrogatepass')) result = h.hexdigest() self.assertEqual(result, self.expectedchecksum) -class UnicodeDatabaseTest(unittest.TestCase): - db = unicodedata -class UnicodeFunctionsTest(UnicodeDatabaseTest): +class BaseUnicodeFunctionsTest: - # Update this if the database changes. Make sure to do a full rebuild - # (e.g. 'make distclean && make') to get the correct checksum. - expectedchecksum = '23ab09ed4abdf93db23b97359108ed630dd8311d' - - @requires_resource('cpu') def test_function_checksum(self): + db = self.db data = [] h = hashlib.sha1() - for i in range(sys.maxunicode + 1): - char = chr(i) - data = [ + for char in iterallchars(): + data = "%.12g%.12g%.12g%s%s%s%s%s%s%s" % ( # Properties - format(self.db.digit(char, -1), '.12g'), - format(self.db.numeric(char, -1), '.12g'), - format(self.db.decimal(char, -1), '.12g'), - self.db.category(char), - self.db.bidirectional(char), - self.db.decomposition(char), - str(self.db.mirrored(char)), - str(self.db.combining(char)), - unicodedata.east_asian_width(char), - self.db.name(char, ""), - ] - h.update(''.join(data).encode("ascii")) + db.digit(char, -1), + db.numeric(char, -1), + db.decimal(char, -1), + db.category(char), + db.bidirectional(char), + db.decomposition(char), + db.mirrored(char), + db.combining(char), + db.east_asian_width(char), + db.name(char, ""), + ) + h.update(data.encode("ascii")) result = h.hexdigest() self.assertEqual(result, self.expectedchecksum) + def test_name(self): + name = self.db.name + self.assertRaises(ValueError, name, '\0') + self.assertRaises(ValueError, name, '\n') + self.assertRaises(ValueError, name, '\x1F') + self.assertRaises(ValueError, name, '\x7F') + self.assertRaises(ValueError, name, '\x9F') + self.assertRaises(ValueError, name, '\uFFFE') + self.assertRaises(ValueError, name, '\uFFFF') + self.assertRaises(ValueError, name, '\U0010FFFF') + self.assertEqual(name('\U0010FFFF', 42), 42) + + self.assertEqual(name(' '), 'SPACE') + self.assertEqual(name('1'), 'DIGIT ONE') + self.assertEqual(name('A'), 'LATIN CAPITAL LETTER A') + self.assertEqual(name('\xA0'), 'NO-BREAK SPACE') + self.assertEqual(name('\u0221', None), None if self.old else + 'LATIN SMALL LETTER D WITH CURL') + self.assertEqual(name('\u3400'), 'CJK UNIFIED IDEOGRAPH-3400') + self.assertEqual(name('\u9FA5'), 'CJK UNIFIED IDEOGRAPH-9FA5') + self.assertEqual(name('\uAC00'), 'HANGUL SYLLABLE GA') + self.assertEqual(name('\uD7A3'), 'HANGUL SYLLABLE HIH') + self.assertEqual(name('\uF900'), 'CJK COMPATIBILITY IDEOGRAPH-F900') + self.assertEqual(name('\uFA6A'), 'CJK COMPATIBILITY IDEOGRAPH-FA6A') + self.assertEqual(name('\uFBF9'), + 'ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ' + 'ABOVE WITH ALEF MAKSURA ISOLATED FORM') + self.assertEqual(name('\U00013460', None), None if self.old else + 'EGYPTIAN HIEROGLYPH-13460') + self.assertEqual(name('\U000143FA', None), None if self.old else + 'EGYPTIAN HIEROGLYPH-143FA') + self.assertEqual(name('\U00017000', None), None if self.old else + 'TANGUT IDEOGRAPH-17000') + self.assertEqual(name('\U00018B00', None), None if self.old else + 'KHITAN SMALL SCRIPT CHARACTER-18B00') + self.assertEqual(name('\U00018CD5', None), None if self.old else + 'KHITAN SMALL SCRIPT CHARACTER-18CD5') + self.assertEqual(name('\U00018CFF', None), None if self.old else + 'KHITAN SMALL SCRIPT CHARACTER-18CFF') + self.assertEqual(name('\U00018D1E', None), None if self.old else + 'TANGUT IDEOGRAPH-18D1E') + self.assertEqual(name('\U0001B170', None), None if self.old else + 'NUSHU CHARACTER-1B170') + self.assertEqual(name('\U0001B2FB', None), None if self.old else + 'NUSHU CHARACTER-1B2FB') + self.assertEqual(name('\U0001FBA8', None), None if self.old else + 'BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO ' + 'MIDDLE LEFT AND MIDDLE RIGHT TO LOWER CENTRE') + self.assertEqual(name('\U0002A6D6'), 'CJK UNIFIED IDEOGRAPH-2A6D6') + self.assertEqual(name('\U0002FA1D'), 'CJK COMPATIBILITY IDEOGRAPH-2FA1D') + self.assertEqual(name('\U00033479', None), None if self.old else + 'CJK UNIFIED IDEOGRAPH-33479') + @requires_resource('cpu') def test_name_inverse_lookup(self): - for i in range(sys.maxunicode + 1): - char = chr(i) - if looked_name := self.db.name(char, None): + for char in iterallchars(): + looked_name = self.db.name(char, None) + if looked_name is not None: self.assertEqual(self.db.lookup(looked_name), char) def test_no_names_in_pua(self): @@ -127,6 +207,17 @@ def test_lookup_nonexistant(self): "HANDBUG", "MODIFIER LETTER CYRILLIC SMALL QUESTION MARK", "???", + "CJK UNIFIED IDEOGRAPH-03400", + "CJK UNIFIED IDEOGRAPH-020000", + "CJK UNIFIED IDEOGRAPH-33FF", + "CJK UNIFIED IDEOGRAPH-F900", + "CJK UNIFIED IDEOGRAPH-13460", + "CJK UNIFIED IDEOGRAPH-17000", + "CJK UNIFIED IDEOGRAPH-18B00", + "CJK UNIFIED IDEOGRAPH-1B170", + "CJK COMPATIBILITY IDEOGRAPH-3400", + "TANGUT IDEOGRAPH-3400", + "HANGUL SYLLABLE AC00", ]: self.assertRaises(KeyError, self.db.lookup, nonexistent) @@ -138,6 +229,13 @@ def test_digit(self): self.assertEqual(self.db.digit('\U00020000', None), None) self.assertEqual(self.db.digit('\U0001D7FD'), 7) + # New in 13.0.0 + self.assertEqual(self.db.digit('\U0001fbf9', None), 9) + # New in 14.0.0 + self.assertEqual(self.db.digit('\U00016ac9', None), 9) + # New in 15.0.0 + self.assertEqual(self.db.digit('\U0001e4f9', None), 9) + self.assertRaises(TypeError, self.db.digit) self.assertRaises(TypeError, self.db.digit, 'xx') self.assertRaises(ValueError, self.db.digit, 'x') @@ -147,9 +245,28 @@ def test_numeric(self): self.assertEqual(self.db.numeric('9'), 9) self.assertEqual(self.db.numeric('\u215b'), 0.125) self.assertEqual(self.db.numeric('\u2468'), 9.0) - self.assertEqual(self.db.numeric('\ua627'), 7.0) self.assertEqual(self.db.numeric('\U00020000', None), None) - self.assertEqual(self.db.numeric('\U0001012A'), 9000) + + # New in 4.1.0 + self.assertEqual(self.db.numeric('\U0001012A', None), None if self.old else 9000) + # Changed in 4.1.0 + self.assertEqual(self.db.numeric('\u5793', None), 1e20 if self.old else None) + # New in 5.0.0 + self.assertEqual(self.db.numeric('\u07c0', None), None if self.old else 0.0) + # New in 5.1.0 + self.assertEqual(self.db.numeric('\ua627', None), None if self.old else 7.0) + # Changed in 5.2.0 + self.assertEqual(self.db.numeric('\u09f6'), 3.0 if self.old else 3/16) + # New in 6.0.0 + self.assertEqual(self.db.numeric('\u0b72', None), None if self.old else 0.25) + # New in 12.0.0 + self.assertEqual(self.db.numeric('\U0001ed3c', None), None if self.old else 0.5) + # New in 13.0.0 + self.assertEqual(self.db.numeric('\U0001fbf9', None), None if self.old else 9) + # New in 14.0.0 + self.assertEqual(self.db.numeric('\U00016ac9', None), None if self.old else 9) + # New in 15.0.0 + self.assertEqual(self.db.numeric('\U0001e4f9', None), None if self.old else 9) self.assertRaises(TypeError, self.db.numeric) self.assertRaises(TypeError, self.db.numeric, 'xx') @@ -163,6 +280,18 @@ def test_decimal(self): self.assertEqual(self.db.decimal('\U00020000', None), None) self.assertEqual(self.db.decimal('\U0001D7FD'), 7) + # New in 4.1.0 + self.assertEqual(self.db.decimal('\xb2', None), 2 if self.old else None) + self.assertEqual(self.db.decimal('\u1369', None), 1 if self.old else None) + # New in 5.0.0 + self.assertEqual(self.db.decimal('\u07c0', None), None if self.old else 0) + # New in 13.0.0 + self.assertEqual(self.db.decimal('\U0001fbf9', None), None if self.old else 9) + # New in 14.0.0 + self.assertEqual(self.db.decimal('\U00016ac9', None), None if self.old else 9) + # New in 15.0.0 + self.assertEqual(self.db.decimal('\U0001e4f9', None), None if self.old else 9) + self.assertRaises(TypeError, self.db.decimal) self.assertRaises(TypeError, self.db.decimal, 'xx') self.assertRaises(ValueError, self.db.decimal, 'x') @@ -172,24 +301,91 @@ def test_category(self): self.assertEqual(self.db.category('a'), 'Ll') self.assertEqual(self.db.category('A'), 'Lu') self.assertEqual(self.db.category('\U00020000'), 'Lo') - self.assertEqual(self.db.category('\U0001012A'), 'No') + + # New in 4.1.0 + self.assertEqual(self.db.category('\U0001012A'), 'Cn' if self.old else 'No') + self.assertEqual(self.db.category('\U000e01ef'), 'Cn' if self.old else 'Mn') + # New in 5.1.0 + self.assertEqual(self.db.category('\u0374'), 'Sk' if self.old else 'Lm') + # Changed in 13.0.0 + self.assertEqual(self.db.category('\u0b55'), 'Cn' if self.old else 'Mn') + self.assertEqual(self.db.category('\U0003134a'), 'Cn' if self.old else 'Lo') + # Changed in 14.0.0 + self.assertEqual(self.db.category('\u061d'), 'Cn' if self.old else 'Po') + self.assertEqual(self.db.category('\U0002b738'), 'Cn' if self.old else 'Lo') + # Changed in 15.0.0 + self.assertEqual(self.db.category('\u0cf3'), 'Cn' if self.old else 'Mc') + self.assertEqual(self.db.category('\U000323af'), 'Cn' if self.old else 'Lo') self.assertRaises(TypeError, self.db.category) self.assertRaises(TypeError, self.db.category, 'xx') def test_bidirectional(self): - self.assertEqual(self.db.bidirectional('\uFFFE'), '') + self.assertEqual(self.db.bidirectional('\uFFFE'), 'BN') self.assertEqual(self.db.bidirectional(' '), 'WS') self.assertEqual(self.db.bidirectional('A'), 'L') self.assertEqual(self.db.bidirectional('\U00020000'), 'L') + # New in 4.1.0 + self.assertEqual(self.db.bidirectional('+'), 'ET' if self.old else 'ES') + self.assertEqual(self.db.bidirectional('\u0221'), '' if self.old else 'L') + self.assertEqual(self.db.bidirectional('\U000e01ef'), '' if self.old else 'NSM') + # New in 13.0.0 + self.assertEqual(self.db.bidirectional('\u0b55'), '' if self.old else 'NSM') + self.assertEqual(self.db.bidirectional('\U0003134a'), '' if self.old else 'L') + # New in 14.0.0 + self.assertEqual(self.db.bidirectional('\u061d'), '' if self.old else 'AL') + self.assertEqual(self.db.bidirectional('\U0002b738'), '' if self.old else 'L') + # New in 15.0.0 + self.assertEqual(self.db.bidirectional('\u0cf3'), '' if self.old else 'L') + self.assertEqual(self.db.bidirectional('\U000323af'), '' if self.old else 'L') + # New in 16.0.0 + self.assertEqual(self.db.bidirectional('\u0897'), '' if self.old else 'NSM') + self.assertEqual(self.db.bidirectional('\U0001fbef'), '' if self.old else 'ON') + # New in 17.0.0 + self.assertEqual(self.db.bidirectional('\u088f'), '' if self.old else 'AL') + self.assertEqual(self.db.bidirectional('\U0001fbfa'), '' if self.old else 'ON') + self.assertRaises(TypeError, self.db.bidirectional) self.assertRaises(TypeError, self.db.bidirectional, 'xx') + def test_bidirectional_unassigned(self): + if self.old: + return + self.assertEqual(self.db.bidirectional('\u0378'), 'L') + self.assertEqual(self.db.bidirectional('\u077F'), 'AL') + self.assertEqual(self.db.bidirectional('\u20CF'), 'ET') + self.assertEqual(self.db.bidirectional('\u0590'), 'R') + self.assertEqual(self.db.bidirectional('\uFFFF'), 'BN') + self.assertEqual(self.db.bidirectional('\U0001FFFE'), 'BN') + self.assertEqual(self.db.bidirectional('\U00010D01'), 'AL') + def test_decomposition(self): self.assertEqual(self.db.decomposition('\uFFFE'),'') self.assertEqual(self.db.decomposition('\u00bc'), '<fraction> 0031 2044 0034') + # New in 4.1.0 + self.assertEqual(self.db.decomposition('\u03f9'), '' if self.old else '<compat> 03A3') + # New in 13.0.0 + self.assertEqual(self.db.decomposition('\uab69'), '' if self.old else '<super> 028D') + self.assertEqual(self.db.decomposition('\U00011938'), '' if self.old else '11935 11930') + self.assertEqual(self.db.decomposition('\U0001fbf9'), '' if self.old else '<font> 0039') + # New in 14.0.0 + self.assertEqual(self.db.decomposition('\ua7f2'), '' if self.old else '<super> 0043') + self.assertEqual(self.db.decomposition('\U000107ba'), '' if self.old else '<super> 1DF1E') + # New in 15.0.0 + self.assertEqual(self.db.decomposition('\U0001e06d'), '' if self.old else '<super> 04B1') + # New in 16.0.0 + self.assertEqual(self.db.decomposition('\U0001CCD6'), '' if self.old else '<font> 0041') + # New in 17.0.0 + self.assertEqual(self.db.decomposition('\uA7F1'), '' if self.old else '<super> 0053') + + # Hangul characters + self.assertEqual(self.db.decomposition('\uAC00'), '1100 1161') + self.assertEqual(self.db.decomposition('\uD4DB'), '1111 1171 11B6') + self.assertEqual(self.db.decomposition('\uC2F8'), '110A 1161') + self.assertEqual(self.db.decomposition('\uD7A3'), '1112 1175 11C2') + self.assertRaises(TypeError, self.db.decomposition) self.assertRaises(TypeError, self.db.decomposition, 'xx') @@ -199,6 +395,16 @@ def test_mirrored(self): self.assertEqual(self.db.mirrored('\u2201'), 1) self.assertEqual(self.db.mirrored('\U00020000'), 0) + # New in 5.0.0 + self.assertEqual(self.db.mirrored('\u0f3a'), 0 if self.old else 1) + self.assertEqual(self.db.mirrored('\U0001d7c3'), 0 if self.old else 1) + # New in 11.0.0 + self.assertEqual(self.db.mirrored('\u29a1'), 1 if self.old else 0) + # New in 14.0.0 + self.assertEqual(self.db.mirrored('\u2e5c'), 0 if self.old else 1) + # New in 16.0.0 + self.assertEqual(self.db.mirrored('\u226D'), 0 if self.old else 1) + self.assertRaises(TypeError, self.db.mirrored) self.assertRaises(TypeError, self.db.mirrored, 'xx') @@ -208,9 +414,189 @@ def test_combining(self): self.assertEqual(self.db.combining('\u20e1'), 230) self.assertEqual(self.db.combining('\U00020000'), 0) + # New in 4.1.0 + self.assertEqual(self.db.combining('\u0350'), 0 if self.old else 230) + # New in 9.0.0 + self.assertEqual(self.db.combining('\U0001e94a'), 0 if self.old else 7) + # New in 13.0.0 + self.assertEqual(self.db.combining('\u1abf'), 0 if self.old else 220) + self.assertEqual(self.db.combining('\U00016ff1'), 0 if self.old else 6) + # New in 14.0.0 + self.assertEqual(self.db.combining('\u0c3c'), 0 if self.old else 7) + self.assertEqual(self.db.combining('\U0001e2ae'), 0 if self.old else 230) + # New in 15.0.0 + self.assertEqual(self.db.combining('\U00010efd'), 0 if self.old else 220) + # New in 16.0.0 + self.assertEqual(self.db.combining('\u0897'), 0 if self.old else 230) + # New in 17.0.0 + self.assertEqual(self.db.combining('\u1ACF'), 0 if self.old else 230) + self.assertRaises(TypeError, self.db.combining) self.assertRaises(TypeError, self.db.combining, 'xx') + def test_normalization(self): + # Test normalize() and is_normalized() + def check(ch, expected): + if isinstance(expected, str): + expected = [expected]*4 + forms = ('NFC', 'NFD', 'NFKC', 'NFKD') + result = [self.db.normalize(form, ch) for form in forms] + self.assertEqual(ascii(result), ascii(list(expected))) + self.assertEqual([self.db.is_normalized(form, ch) for form in forms], + [ch == y for x, y in zip(result, expected)]) + + check('', '') + check('A', 'A') + check(' ', ' ') + check('\U0010ffff', '\U0010ffff') + check('abc', 'abc') + # Broken in 4.0.0 + check('\u0340', '\u0300') + check('\u0300', '\u0300') + check('\U0002fa1d', '\U0002a600') + check('\U0002a600', '\U0002a600') + check('\u0344', '\u0308\u0301') + check('\u0308\u0301', '\u0308\u0301') + # Broken in 4.0.0 and 4.0.1 + check('\U0001d1bc', '\U0001d1ba\U0001d165') + check('\U0001d1ba\U0001d165', '\U0001d1ba\U0001d165') + check('\ufb2c', '\u05e9\u05bc\u05c1') + check('\u05e9\u05bc\u05c1', '\u05e9\u05bc\u05c1') + check('\U0001d1c0', '\U0001d1ba\U0001d165\U0001d16f') + check('\U0001d1ba\U0001d165\U0001d16f', '\U0001d1ba\U0001d165\U0001d16f') + + # Broken in 4.0.0 + check('\xa0', ['\xa0', '\xa0', ' ', ' ']) + check('\u2003', ['\u2003', '\u2003', ' ', ' ']) + check('\U0001d7ff', ['\U0001d7ff', '\U0001d7ff', '9', '9']) + + check('\xa8', ['\xa8', '\xa8', ' \u0308', ' \u0308']) + check(' \u0308', ' \u0308') + + check('\xc0', ['\xc0', 'A\u0300']*2) + check('A\u0300', ['\xc0', 'A\u0300']*2) + + check('\ud7a3', ['\ud7a3', '\u1112\u1175\u11c2']*2) + check('\u1112\u1175\u11c2', ['\ud7a3', '\u1112\u1175\u11c2']*2) + + check('\xb4', ['\xb4', '\xb4', ' \u0301', ' \u0301']) + check('\u1ffd', ['\xb4', '\xb4', ' \u0301', ' \u0301']) + check(' \u0301', ' \u0301') + + check('\xc5', ['\xc5', 'A\u030a']*2) + check('\u212b', ['\xc5', 'A\u030a']*2) + check('A\u030a', ['\xc5', 'A\u030a']*2) + + check('\u1f71', ['\u03ac', '\u03b1\u0301']*2) + check('\u03ac', ['\u03ac', '\u03b1\u0301']*2) + check('\u03b1\u0301', ['\u03ac', '\u03b1\u0301']*2) + + check('\u01c4', ['\u01c4', '\u01c4', 'D\u017d', 'DZ\u030c']) + check('D\u017d', ['D\u017d', 'DZ\u030c']*2) + check('DZ\u030c', ['D\u017d', 'DZ\u030c']*2) + + check('\u1fed', ['\u1fed', '\xa8\u0300', ' \u0308\u0300', ' \u0308\u0300']) + check('\xa8\u0300', ['\u1fed', '\xa8\u0300', ' \u0308\u0300', ' \u0308\u0300']) + check(' \u0308\u0300', ' \u0308\u0300') + + check('\u326e', ['\u326e', '\u326e', '\uac00', '\u1100\u1161']) + check('\u320e', ['\u320e', '\u320e', '(\uac00)', '(\u1100\u1161)']) + check('(\uac00)', ['(\uac00)', '(\u1100\u1161)']*2) + check('(\u1100\u1161)', ['(\uac00)', '(\u1100\u1161)']*2) + + check('\u0385', ['\u0385', '\xa8\u0301', ' \u0308\u0301', ' \u0308\u0301']) + check('\u1fee', ['\u0385', '\xa8\u0301', ' \u0308\u0301', ' \u0308\u0301']) + check('\xa8\u0301', ['\u0385', '\xa8\u0301', ' \u0308\u0301', ' \u0308\u0301']) + check(' \u0308\u0301', ' \u0308\u0301') + + check('\u1fdf', ['\u1fdf', '\u1ffe\u0342', ' \u0314\u0342', ' \u0314\u0342']) + check('\u1ffe\u0342', ['\u1fdf', '\u1ffe\u0342', ' \u0314\u0342', ' \u0314\u0342']) + check('\u1ffe', ['\u1ffe', '\u1ffe', ' \u0314', ' \u0314']) + check(' \u0314\u0342', ' \u0314\u0342') + + check('\u03d3', ['\u03d3', '\u03d2\u0301', '\u038e', '\u03a5\u0301']) + check('\u03d2\u0301', ['\u03d3', '\u03d2\u0301', '\u038e', '\u03a5\u0301']) + check('\u038e', ['\u038e', '\u03a5\u0301']*2) + check('\u1feb', ['\u038e', '\u03a5\u0301']*2) + check('\u03a5\u0301', ['\u038e', '\u03a5\u0301']*2) + + check('\u0626', ['\u0626', '\u064a\u0654']*2) + check('\u064a\u0654', ['\u0626', '\u064a\u0654']*2) + check('\ufe89', ['\ufe89', '\ufe89', '\u0626', '\u064a\u0654']) + check('\ufe8a', ['\ufe8a', '\ufe8a', '\u0626', '\u064a\u0654']) + check('\ufe8b', ['\ufe8b', '\ufe8b', '\u0626', '\u064a\u0654']) + check('\ufe8c', ['\ufe8c', '\ufe8c', '\u0626', '\u064a\u0654']) + + check('\ufef9', ['\ufef9', '\ufef9', '\u0644\u0625', '\u0644\u0627\u0655']) + check('\ufefa', ['\ufefa', '\ufefa', '\u0644\u0625', '\u0644\u0627\u0655']) + check('\ufefb', ['\ufefb', '\ufefb', '\u0644\u0627', '\u0644\u0627']) + check('\ufefc', ['\ufefc', '\ufefc', '\u0644\u0627', '\u0644\u0627']) + check('\u0644\u0625', ['\u0644\u0625', '\u0644\u0627\u0655']*2) + check('\u0644\u0627\u0655', ['\u0644\u0625', '\u0644\u0627\u0655']*2) + check('\u0644\u0627', '\u0644\u0627') + + # Broken in 4.0.0 + check('\u327c', '\u327c' if self.old else + ['\u327c', '\u327c', '\ucc38\uace0', '\u110e\u1161\u11b7\u1100\u1169']) + check('\ucc38\uace0', ['\ucc38\uace0', '\u110e\u1161\u11b7\u1100\u1169']*2) + check('\ucc38', ['\ucc38', '\u110e\u1161\u11b7']*2) + check('\u110e\u1161\u11b7\u1100\u1169', + ['\ucc38\uace0', '\u110e\u1161\u11b7\u1100\u1169']*2) + check('\u110e\u1161\u11b7\u1100', + ['\ucc38\u1100', '\u110e\u1161\u11b7\u1100']*2) + check('\u110e\u1161\u11b7', + ['\ucc38', '\u110e\u1161\u11b7']*2) + check('\u110e\u1161', + ['\ucc28', '\u110e\u1161']*2) + check('\u110e', '\u110e') + # Broken in 4.0.0-12.0.0 + check('\U00011938', '\U00011938' if self.old else + ['\U00011938', '\U00011935\U00011930']*2) + check('\U00011935\U00011930', ['\U00011938', '\U00011935\U00011930']*2) + # New in 4.0.1 + check('\u321d', '\u321d' if self.old else + ['\u321d', '\u321d', '(\uc624\uc804)', '(\u110b\u1169\u110c\u1165\u11ab)']) + check('(\uc624\uc804)', + ['(\uc624\uc804)', '(\u110b\u1169\u110c\u1165\u11ab)']*2) + check('(\u110b\u1169\u110c\u1165\u11ab)', + ['(\uc624\uc804)', '(\u110b\u1169\u110c\u1165\u11ab)']*2) + check('\u4d57', '\u4d57') + check('\u45d7', '\u45d7' if self.old else '\u45d7') + check('\U0002f9bf', '\u4d57' if self.old else '\u45d7') + # New in 4.1.0 + check('\u03a3', '\u03a3') + check('\u03f9', '\u03f9' if self.old else + ['\u03f9', '\u03f9', '\u03a3', '\u03a3']) + # New in 5.0.0 + check('\u1b06', '\u1b06' if self.old else ['\u1b06', '\u1b05\u1b35']*2) + # New in 5.2.0 + check('\U0001f213', '\U0001f213' if self.old else + ['\U0001f213', '\U0001f213', '\u30c7', '\u30c6\u3099']) + # New in 6.1.0 + check('\ufa2e', '\ufa2e' if self.old else '\u90de') + # New in 13.0.0 + check('\U00011938', '\U00011938' if self.old else + ['\U00011938', '\U00011935\U00011930', '\U00011938', '\U00011935\U00011930']) + check('\U0001fbf9', '\U0001fbf9' if self.old else + ['\U0001fbf9', '\U0001fbf9', '9', '9']) + # New in 14.0.0 + check('\U000107ba', '\U000107ba' if self.old else + ['\U000107ba', '\U000107ba', '\U0001df1e', '\U0001df1e']) + # New in 15.0.0 + check('\U0001e06d', '\U0001e06d' if self.old else + ['\U0001e06d', '\U0001e06d', '\u04b1', '\u04b1']) + # New in 16.0.0 + check('\U0001ccd6', '\U0001ccd6' if self.old else + ['\U0001ccd6', '\U0001ccd6', 'A', 'A']) + + self.assertRaises(TypeError, self.db.normalize) + self.assertRaises(TypeError, self.db.normalize, 'NFC') + self.assertRaises(ValueError, self.db.normalize, 'SPAM', 'A') + + self.assertRaises(TypeError, self.db.is_normalized) + self.assertRaises(TypeError, self.db.is_normalized, 'NFC') + self.assertRaises(ValueError, self.db.is_normalized, 'SPAM', 'A') + def test_pr29(self): # https://www.unicode.org/review/pr-29.html # See issues #1054943 and #10254. @@ -225,10 +611,39 @@ def test_pr29(self): def test_issue10254(self): # Crash reported in #10254 + # New in 4.1.0 a = 'C\u0338' * 20 + 'C\u0327' b = 'C\u0338' * 20 + '\xC7' self.assertEqual(self.db.normalize('NFC', a), b) + def test_long_combining_mark_run(self): + # gh-149079: avoid quadratic canonical ordering. + payload = "a" + ("\u0300\u0327" * 32) + nfd = "a" + ("\u0327" * 32) + ("\u0300" * 32) + nfc = "\u00e0" + ("\u0327" * 32) + ("\u0300" * 31) + + self.assertEqual(self.db.normalize("NFD", payload), nfd) + self.assertEqual(self.db.normalize("NFKD", payload), nfd) + self.assertEqual(self.db.normalize("NFC", payload), nfc) + self.assertEqual(self.db.normalize("NFKC", payload), nfc) + + def test_combining_mark_run_fast_paths(self): + # gh-149079: cover short runs and already-sorted long runs. + short_payload = "a" + ("\u0300\u0327" * 9) + "\u0300" + short_nfd = "a" + ("\u0327" * 9) + ("\u0300" * 10) + short_nfc = "\u00e0" + ("\u0327" * 9) + ("\u0300" * 9) + long_sorted = "a" + ("\u0327" * 30) + ("\u0300" * 30) + long_sorted_nfc = "\u00e0" + ("\u0327" * 30) + ("\u0300" * 29) + + self.assertEqual(self.db.normalize("NFD", short_payload), short_nfd) + self.assertEqual(self.db.normalize("NFKD", short_payload), short_nfd) + self.assertEqual(self.db.normalize("NFC", short_payload), short_nfc) + self.assertEqual(self.db.normalize("NFKC", short_payload), short_nfc) + self.assertEqual(self.db.normalize("NFD", long_sorted), long_sorted) + self.assertEqual(self.db.normalize("NFKD", long_sorted), long_sorted) + self.assertEqual(self.db.normalize("NFC", long_sorted), long_sorted_nfc) + self.assertEqual(self.db.normalize("NFKC", long_sorted), long_sorted_nfc) + def test_issue29456(self): # Fix #29456 u1176_str_a = '\u1100\u1176\u11a8' @@ -238,6 +653,7 @@ def test_issue29456(self): u11c3_str_a = '\u1100\u1175\u11c3' u11c3_str_b = '\uae30\u11c3' self.assertEqual(self.db.normalize('NFC', u1176_str_a), u1176_str_b) + # New in 4.1.0 self.assertEqual(self.db.normalize('NFC', u11a7_str_a), u11a7_str_b) self.assertEqual(self.db.normalize('NFC', u11c3_str_a), u11c3_str_b) @@ -255,6 +671,34 @@ def test_east_asian_width(self): self.assertEqual(eaw('\u2010'), 'A') self.assertEqual(eaw('\U00020000'), 'W') + # New in 4.1.0 + self.assertEqual(eaw('\u0350'), 'N' if self.old else 'A') + self.assertEqual(eaw('\U000e01ef'), 'N' if self.old else 'A') + # New in 5.2.0 + self.assertEqual(eaw('\u115a'), 'N' if self.old else 'W') + # New in 9.0.0 + self.assertEqual(eaw('\u231a'), 'N' if self.old else 'W') + self.assertEqual(eaw('\u2614'), 'N' if self.old else 'W') + self.assertEqual(eaw('\U0001f19a'), 'N' if self.old else 'W') + self.assertEqual(eaw('\U0001f991'), 'N' if self.old else 'W') + self.assertEqual(eaw('\U0001f9c0'), 'N' if self.old else 'W') + # New in 12.0.0 + self.assertEqual(eaw('\u32ff'), 'N' if self.old else 'W') + self.assertEqual(eaw('\U0001fa95'), 'N' if self.old else 'W') + # New in 13.0.0 + self.assertEqual(eaw('\u31bb'), 'N' if self.old else 'W') + self.assertEqual(eaw('\U0003134a'), 'N' if self.old else 'W') + # New in 14.0.0 + self.assertEqual(eaw('\u9ffd'), 'N' if self.old else 'W') + self.assertEqual(eaw('\U0002b738'), 'N' if self.old else 'W') + # New in 15.0.0 + self.assertEqual(eaw('\U000323af'), 'N' if self.old else 'W') + # New in 16.0.0 + self.assertEqual(eaw('\u2630'), 'N' if self.old else 'W') + self.assertEqual(eaw('\U0001FAE9'), 'N' if self.old else 'W') + # New in 17.0.0 + self.assertEqual(eaw('\U00016FF2'), 'N' if self.old else 'W') + def test_east_asian_width_unassigned(self): eaw = self.db.east_asian_width # unassigned @@ -263,7 +707,8 @@ def test_east_asian_width_unassigned(self): self.assertIs(self.db.name(char, None), None) # unassigned but reserved for CJK - for char in '\uFA6E\uFADA\U0002A6E0\U0002FA20\U0003134B\U0003FFFD': + for char in ('\U0002A6E0\U0002FA20\U0003134B\U0003FFFD' + '\uFA6E\uFADA'): # New in 5.2.0 self.assertEqual(eaw(char), 'W') self.assertIs(self.db.name(char, None), None) @@ -272,11 +717,400 @@ def test_east_asian_width_unassigned(self): self.assertEqual(eaw(char), 'A') self.assertIs(self.db.name(char, None), None) - def test_east_asian_width_9_0_changes(self): - self.assertEqual(self.db.ucd_3_2_0.east_asian_width('\u231a'), 'N') - self.assertEqual(self.db.east_asian_width('\u231a'), 'W') +class UnicodeFunctionsTest(unittest.TestCase, BaseUnicodeFunctionsTest): + db = unicodedata + old = False + + # Update this if the database changes. Make sure to do a full rebuild + # (e.g. 'make distclean && make') to get the correct checksum. + expectedchecksum = ('00b13fa975a60b1d3f490f1fc8c126ab24990c75' + if quicktest else + 'ebfc9dd281c2226998fd435744dd2e9321899beb') + + @requires_resource('network') + def test_all_names(self): + TESTDATAFILE = "DerivedName.txt" + testdata = download_test_data_file(TESTDATAFILE) + + with testdata: + self.run_name_tests(testdata) -class UnicodeMiscTest(UnicodeDatabaseTest): + def run_name_tests(self, testdata): + names_ref = {} + + def parse_cp(s): + return int(s, 16) + + # Parse data + for line in testdata: + line = line.strip() + if not line or line.startswith("#"): + continue + raw_cp, name = line.split("; ") + # Check for a range + if ".." in raw_cp: + cp1, cp2 = map(parse_cp, raw_cp.split("..")) + # remove ‘*’ at the end + assert name[-1] == '*', (raw_cp, name) + name = name[:-1] + for cp in range(cp1, cp2 + 1): + names_ref[cp] = f"{name}{cp:04X}" + elif name[-1] == '*': + cp = parse_cp(raw_cp) + name = name[:-1] + names_ref[cp] = f"{name}{cp:04X}" + else: + assert '*' not in name, (raw_cp, name) + cp = parse_cp(raw_cp) + names_ref[cp] = name + + for cp in range(0, sys.maxunicode + 1): + self.assertEqual(self.db.name(chr(cp), None), names_ref.get(cp)) + + def test_isxidstart(self): + self.assertTrue(self.db.isxidstart('S')) + self.assertTrue(self.db.isxidstart('\u0AD0')) # GUJARATI OM + self.assertTrue(self.db.isxidstart('\u0EC6')) # LAO KO LA + self.assertTrue(self.db.isxidstart('\u17DC')) # KHMER SIGN AVAKRAHASANYA + self.assertTrue(self.db.isxidstart('\uA015')) # YI SYLLABLE WU + self.assertTrue(self.db.isxidstart('\uFE7B')) # ARABIC KASRA MEDIAL FORM + + self.assertFalse(self.db.isxidstart(' ')) + self.assertFalse(self.db.isxidstart('0')) + self.assertRaises(TypeError, self.db.isxidstart) + self.assertRaises(TypeError, self.db.isxidstart, 'xx') + + def test_isxidcontinue(self): + self.assertTrue(self.db.isxidcontinue('S')) + self.assertTrue(self.db.isxidcontinue('_')) + self.assertTrue(self.db.isxidcontinue('0')) + self.assertTrue(self.db.isxidcontinue('\u00BA')) # MASCULINE ORDINAL INDICATOR + self.assertTrue(self.db.isxidcontinue('\u0640')) # ARABIC TATWEEL + self.assertTrue(self.db.isxidcontinue('\u0710')) # SYRIAC LETTER ALAPH + self.assertTrue(self.db.isxidcontinue('\u0B3E')) # ORIYA VOWEL SIGN AA + self.assertTrue(self.db.isxidcontinue('\u17D7')) # KHMER SIGN LEK TOO + + self.assertFalse(self.db.isxidcontinue(' ')) + self.assertRaises(TypeError, self.db.isxidcontinue) + self.assertRaises(TypeError, self.db.isxidcontinue, 'xx') + + def test_grapheme_cluster_break(self): + gcb = self.db.grapheme_cluster_break + self.assertEqual(gcb(' '), 'Other') + self.assertEqual(gcb('x'), 'Other') + self.assertEqual(gcb('\U0010FFFF'), 'Other') + self.assertEqual(gcb('\r'), 'CR') + self.assertEqual(gcb('\n'), 'LF') + self.assertEqual(gcb('\0'), 'Control') + self.assertEqual(gcb('\t'), 'Control') + self.assertEqual(gcb('\x1F'), 'Control') + self.assertEqual(gcb('\x7F'), 'Control') + self.assertEqual(gcb('\x9F'), 'Control') + self.assertEqual(gcb('\U000E0001'), 'Control') + self.assertEqual(gcb('\u0300'), 'Extend') + self.assertEqual(gcb('\u200C'), 'Extend') + self.assertEqual(gcb('\U000E01EF'), 'Extend') + self.assertEqual(gcb('\u1159'), 'L') + self.assertEqual(gcb('\u11F9'), 'T') + self.assertEqual(gcb('\uD788'), 'LV') + self.assertEqual(gcb('\uD7A3'), 'LVT') + # New in 5.0.0 + self.assertEqual(gcb('\u05BA'), 'Extend') + self.assertEqual(gcb('\u20EF'), 'Extend') + # New in 5.1.0 + self.assertEqual(gcb('\u2064'), 'Control') + self.assertEqual(gcb('\uAA4D'), 'SpacingMark') + # New in 5.2.0 + self.assertEqual(gcb('\u0816'), 'Extend') + self.assertEqual(gcb('\uA97C'), 'L') + self.assertEqual(gcb('\uD7C6'), 'V') + self.assertEqual(gcb('\uD7FB'), 'T') + # New in 6.0.0 + self.assertEqual(gcb('\u093A'), 'Extend') + self.assertEqual(gcb('\U00011002'), 'SpacingMark') + # New in 6.1.0 + self.assertEqual(gcb('\U000E0FFF'), 'Control') + self.assertEqual(gcb('\U00016F7E'), 'SpacingMark') + # New in 6.2.0 + self.assertEqual(gcb('\U0001F1E6'), 'Regional_Indicator') + self.assertEqual(gcb('\U0001F1FF'), 'Regional_Indicator') + # New in 6.3.0 + self.assertEqual(gcb('\u180E'), 'Control') + self.assertEqual(gcb('\u1A1B'), 'Extend') + # New in 7.0.0 + self.assertEqual(gcb('\u0E33'), 'SpacingMark') + self.assertEqual(gcb('\u0EB3'), 'SpacingMark') + self.assertEqual(gcb('\U0001BCA3'), 'Control') + self.assertEqual(gcb('\U0001E8D6'), 'Extend') + self.assertEqual(gcb('\U0001163E'), 'SpacingMark') + # New in 8.0.0 + self.assertEqual(gcb('\u08E3'), 'Extend') + self.assertEqual(gcb('\U00011726'), 'SpacingMark') + # New in 9.0.0 + self.assertEqual(gcb('\u0600'), 'Prepend') + self.assertEqual(gcb('\U000E007F'), 'Extend') + self.assertEqual(gcb('\U00011CB4'), 'SpacingMark') + self.assertEqual(gcb('\u200D'), 'ZWJ') + # New in 10.0.0 + self.assertEqual(gcb('\U00011D46'), 'Prepend') + self.assertEqual(gcb('\U00011D47'), 'Extend') + self.assertEqual(gcb('\U00011A97'), 'SpacingMark') + # New in 11.0.0 + self.assertEqual(gcb('\U000110CD'), 'Prepend') + self.assertEqual(gcb('\u07FD'), 'Extend') + self.assertEqual(gcb('\U00011EF6'), 'SpacingMark') + # New in 12.0.0 + self.assertEqual(gcb('\U00011A84'), 'Prepend') + self.assertEqual(gcb('\U00013438'), 'Control') + self.assertEqual(gcb('\U0001E2EF'), 'Extend') + self.assertEqual(gcb('\U00016F87'), 'SpacingMark') + # New in 13.0.0 + self.assertEqual(gcb('\U00011941'), 'Prepend') + self.assertEqual(gcb('\U00016FE4'), 'Extend') + self.assertEqual(gcb('\U00011942'), 'SpacingMark') + # New in 14.0.0 + self.assertEqual(gcb('\u0891'), 'Prepend') + self.assertEqual(gcb('\U0001E2AE'), 'Extend') + # New in 15.0.0 + self.assertEqual(gcb('\U00011F02'), 'Prepend') + self.assertEqual(gcb('\U0001343F'), 'Control') + self.assertEqual(gcb('\U0001E4EF'), 'Extend') + self.assertEqual(gcb('\U00011F3F'), 'SpacingMark') + # New in 16.0.0 + self.assertEqual(gcb('\U000113D1'), 'Prepend') + self.assertEqual(gcb('\U0001E5EF'), 'Extend') + self.assertEqual(gcb('\U0001612C'), 'SpacingMark') + self.assertEqual(gcb('\U00016D63'), 'V') + # New in 17.0.0 + self.assertEqual(gcb('\u1AEB'), 'Extend') + self.assertEqual(gcb('\U00011B67'), 'SpacingMark') + + self.assertRaises(TypeError, gcb) + self.assertRaises(TypeError, gcb, b'x') + self.assertRaises(TypeError, gcb, 120) + self.assertRaises(TypeError, gcb, '') + self.assertRaises(TypeError, gcb, 'xx') + + def test_indic_conjunct_break(self): + incb = self.db.indic_conjunct_break + self.assertEqual(incb(' '), 'None') + self.assertEqual(incb('x'), 'None') + self.assertEqual(incb('\U0010FFFF'), 'None') + # New in 15.1.0 + self.assertEqual(incb('\u094D'), 'Linker') + self.assertEqual(incb('\u0D4D'), 'Linker') + self.assertEqual(incb('\u0915'), 'Consonant') + self.assertEqual(incb('\u0D3A'), 'Consonant') + self.assertEqual(incb('\u0300'), 'Extend') + self.assertEqual(incb('\U0001E94A'), 'Extend') + # New in 16.0.0 + self.assertEqual(incb('\u034F'), 'Extend') + self.assertEqual(incb('\U000E01EF'), 'Extend') + # New in 17.0.0 + self.assertEqual(incb('\u1039'), 'Linker') + self.assertEqual(incb('\U00011F42'), 'Linker') + self.assertEqual(incb('\u1000'), 'Consonant') + self.assertEqual(incb('\U00011F33'), 'Consonant') + self.assertEqual(incb('\U0001E6F5'), 'Extend') + + self.assertRaises(TypeError, incb) + self.assertRaises(TypeError, incb, b'x') + self.assertRaises(TypeError, incb, 120) + self.assertRaises(TypeError, incb, '') + self.assertRaises(TypeError, incb, 'xx') + + def test_extended_pictographic(self): + ext_pict = self.db.extended_pictographic + self.assertIs(ext_pict(' '), False) + self.assertIs(ext_pict('x'), False) + self.assertIs(ext_pict('\U0010FFFF'), False) + # New in 13.0.0 + self.assertIs(ext_pict('\xA9'), True) + self.assertIs(ext_pict('\u203C'), True) + self.assertIs(ext_pict('\U0001FAD6'), True) + self.assertIs(ext_pict('\U0001FFFD'), True) + # New in 17.0.0 + self.assertIs(ext_pict('\u2388'), False) + self.assertIs(ext_pict('\U0001FA6D'), False) + + self.assertRaises(TypeError, ext_pict) + self.assertRaises(TypeError, ext_pict, b'x') + self.assertRaises(TypeError, ext_pict, 120) + self.assertRaises(TypeError, ext_pict, '') + self.assertRaises(TypeError, ext_pict, 'xx') + + def test_grapheme_break(self): + def graphemes(*args): + return list(map(str, self.db.iter_graphemes(*args))) + + self.assertRaises(TypeError, self.db.iter_graphemes) + self.assertRaises(TypeError, self.db.iter_graphemes, b'x') + self.assertRaises(TypeError, self.db.iter_graphemes, 'x', 0, 0, 0) + + self.assertEqual(graphemes(''), []) + self.assertEqual(graphemes('abcd'), ['a', 'b', 'c', 'd']) + self.assertEqual(graphemes('abcd', 1), ['b', 'c', 'd']) + self.assertEqual(graphemes('abcd', 1, 3), ['b', 'c']) + self.assertEqual(graphemes('abcd', -3), ['b', 'c', 'd']) + self.assertEqual(graphemes('abcd', 1, -1), ['b', 'c']) + self.assertEqual(graphemes('abcd', 3, 1), []) + self.assertEqual(graphemes('abcd', 5), []) + self.assertEqual(graphemes('abcd', 0, 5), ['a', 'b', 'c', 'd']) + self.assertEqual(graphemes('abcd', -5), ['a', 'b', 'c', 'd']) + self.assertEqual(graphemes('abcd', 0, -5), []) + # GB3 + self.assertEqual(graphemes('\r\n'), ['\r\n']) + # GB4 + self.assertEqual(graphemes('\r\u0308'), ['\r', '\u0308']) + self.assertEqual(graphemes('\n\u0308'), ['\n', '\u0308']) + self.assertEqual(graphemes('\0\u0308'), ['\0', '\u0308']) + # GB5 + self.assertEqual(graphemes('\u06dd\r'), ['\u06dd', '\r']) + self.assertEqual(graphemes('\u06dd\n'), ['\u06dd', '\n']) + self.assertEqual(graphemes('\u06dd\0'), ['\u06dd', '\0']) + # GB6 + self.assertEqual(graphemes('\u1100\u1160'), ['\u1100\u1160']) + self.assertEqual(graphemes('\u1100\uAC00'), ['\u1100\uAC00']) + self.assertEqual(graphemes('\u1100\uAC01'), ['\u1100\uAC01']) + # GB7 + self.assertEqual(graphemes('\uAC00\u1160'), ['\uAC00\u1160']) + self.assertEqual(graphemes('\uAC00\u11A8'), ['\uAC00\u11A8']) + self.assertEqual(graphemes('\u1160\u1160'), ['\u1160\u1160']) + self.assertEqual(graphemes('\u1160\u11A8'), ['\u1160\u11A8']) + # GB8 + self.assertEqual(graphemes('\uAC01\u11A8'), ['\uAC01\u11A8']) + self.assertEqual(graphemes('\u11A8\u11A8'), ['\u11A8\u11A8']) + # GB9 + self.assertEqual(graphemes('a\u0300'), ['a\u0300']) + self.assertEqual(graphemes('a\u200D'), ['a\u200D']) + # GB9a + self.assertEqual(graphemes('\u0905\u0903'), ['\u0905\u0903']) + # GB9b + self.assertEqual(graphemes('\u06dd\u0661'), ['\u06dd\u0661']) + # GB9c + self.assertEqual(graphemes('\u0915\u094d\u0924'), + ['\u0915\u094d\u0924']) + self.assertEqual(graphemes('\u0915\u094D\u094D\u0924'), + ['\u0915\u094D\u094D\u0924']) + self.assertEqual(graphemes('\u0915\u094D\u0924\u094D\u092F'), + ['\u0915\u094D\u0924\u094D\u092F']) + # GB11 + self.assertEqual(graphemes( + '\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F' + '\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC'), + ['\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F' + '\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC']) + # GB12 + self.assertEqual(graphemes( + '\U0001F1FA\U0001F1E6\U0001F1FA\U0001F1F3'), + ['\U0001F1FA\U0001F1E6', '\U0001F1FA\U0001F1F3']) + # GB13 + self.assertEqual(graphemes( + 'a\U0001F1FA\U0001F1E6\U0001F1FA\U0001F1F3'), + ['a', '\U0001F1FA\U0001F1E6', '\U0001F1FA\U0001F1F3']) + + def test_block(self): + self.assertEqual(self.db.block('\u0000'), 'Basic Latin') + self.assertEqual(self.db.block('\u0041'), 'Basic Latin') + self.assertEqual(self.db.block('\u007F'), 'Basic Latin') + self.assertEqual(self.db.block('\u0080'), 'Latin-1 Supplement') + self.assertEqual(self.db.block('\u00FF'), 'Latin-1 Supplement') + self.assertEqual(self.db.block('\u1159'), 'Hangul Jamo') + self.assertEqual(self.db.block('\u11F9'), 'Hangul Jamo') + self.assertEqual(self.db.block('\uD788'), 'Hangul Syllables') + self.assertEqual(self.db.block('\uD7A3'), 'Hangul Syllables') + # New in 5.0.0 + self.assertEqual(self.db.block('\u05BA'), 'Hebrew') + self.assertEqual(self.db.block('\u20EF'), 'Combining Diacritical Marks for Symbols') + # New in 5.1.0 + self.assertEqual(self.db.block('\u2064'), 'General Punctuation') + self.assertEqual(self.db.block('\uAA4D'), 'Cham') + # New in 5.2.0 + self.assertEqual(self.db.block('\u0816'), 'Samaritan') + self.assertEqual(self.db.block('\uA97C'), 'Hangul Jamo Extended-A') + self.assertEqual(self.db.block('\uD7C6'), 'Hangul Jamo Extended-B') + self.assertEqual(self.db.block('\uD7FB'), 'Hangul Jamo Extended-B') + # New in 6.0.0 + self.assertEqual(self.db.block('\u093A'), 'Devanagari') + self.assertEqual(self.db.block('\U00011002'), 'Brahmi') + # New in 6.1.0 + self.assertEqual(self.db.block('\U000E0FFF'), 'No_Block') + self.assertEqual(self.db.block('\U00016F7E'), 'Miao') + # New in 6.2.0 + self.assertEqual(self.db.block('\U0001F1E6'), 'Enclosed Alphanumeric Supplement') + self.assertEqual(self.db.block('\U0001F1FF'), 'Enclosed Alphanumeric Supplement') + # New in 6.3.0 + self.assertEqual(self.db.block('\u180E'), 'Mongolian') + self.assertEqual(self.db.block('\u1A1B'), 'Buginese') + # New in 7.0.0 + self.assertEqual(self.db.block('\u0E33'), 'Thai') + self.assertEqual(self.db.block('\u0EB3'), 'Lao') + self.assertEqual(self.db.block('\U0001BCA3'), 'Shorthand Format Controls') + self.assertEqual(self.db.block('\U0001E8D6'), 'Mende Kikakui') + self.assertEqual(self.db.block('\U0001163E'), 'Modi') + # New in 8.0.0 + self.assertEqual(self.db.block('\u08E3'), 'Arabic Extended-A') + self.assertEqual(self.db.block('\U00011726'), 'Ahom') + # New in 9.0.0 + self.assertEqual(self.db.block('\u0600'), 'Arabic') + self.assertEqual(self.db.block('\U000E007F'), 'Tags') + self.assertEqual(self.db.block('\U00011CB4'), 'Marchen') + self.assertEqual(self.db.block('\u200D'), 'General Punctuation') + # New in 10.0.0 + self.assertEqual(self.db.block('\U00011D46'), 'Masaram Gondi') + self.assertEqual(self.db.block('\U00011D47'), 'Masaram Gondi') + self.assertEqual(self.db.block('\U00011A97'), 'Soyombo') + # New in 11.0.0 + self.assertEqual(self.db.block('\U000110CD'), 'Kaithi') + self.assertEqual(self.db.block('\u07FD'), 'NKo') + self.assertEqual(self.db.block('\U00011EF6'), 'Makasar') + # New in 12.0.0 + self.assertEqual(self.db.block('\U00011A84'), 'Soyombo') + self.assertEqual(self.db.block('\U00013438'), 'Egyptian Hieroglyph Format Controls') + self.assertEqual(self.db.block('\U0001E2EF'), 'Wancho') + self.assertEqual(self.db.block('\U00016F87'), 'Miao') + # New in 13.0.0 + self.assertEqual(self.db.block('\U00011941'), 'Dives Akuru') + self.assertEqual(self.db.block('\U00016FE4'), 'Ideographic Symbols and Punctuation') + self.assertEqual(self.db.block('\U00011942'), 'Dives Akuru') + # New in 14.0.0 + self.assertEqual(self.db.block('\u0891'), 'Arabic Extended-B') + self.assertEqual(self.db.block('\U0001E2AE'), 'Toto') + # New in 15.0.0 + self.assertEqual(self.db.block('\U00011F02'), 'Kawi') + self.assertEqual(self.db.block('\U0001343F'), 'Egyptian Hieroglyph Format Controls') + self.assertEqual(self.db.block('\U0001E4EF'), 'Nag Mundari') + self.assertEqual(self.db.block('\U00011F3F'), 'Kawi') + # New in 16.0.0 + self.assertEqual(self.db.block('\U000113D1'), 'Tulu-Tigalari') + self.assertEqual(self.db.block('\U0001E5EF'), 'Ol Onal') + self.assertEqual(self.db.block('\U0001612C'), 'Gurung Khema') + self.assertEqual(self.db.block('\U00016D63'), 'Kirat Rai') + # New in 17.0.0 + self.assertEqual(self.db.block('\u1AEB'), 'Combining Diacritical Marks Extended') + self.assertEqual(self.db.block('\U00011B67'), 'Sharada Supplement') + # Unassigned + self.assertEqual(self.db.block('\U00100000'), 'Supplementary Private Use Area-B') + self.assertEqual(self.db.block('\U0010FFFF'), 'Supplementary Private Use Area-B') + + def test_block_invalid_input(self): + self.assertRaises(TypeError, self.db.block) + self.assertRaises(TypeError, self.db.block, b'x') + self.assertRaises(TypeError, self.db.block, 120) + self.assertRaises(TypeError, self.db.block, '') + self.assertRaises(TypeError, self.db.block, 'xx') + + +class Unicode_3_2_0_FunctionsTest(unittest.TestCase, BaseUnicodeFunctionsTest): + db = unicodedata.ucd_3_2_0 + old = True + expectedchecksum = ('cb5bbbd1f55b67371e18222b90a8e21c87f16b72' + if quicktest else + '74936dffe949d99203a47e6a66565b2fc337bae7') + + +class UnicodeMiscTest(unittest.TestCase): + db = unicodedata @cpython_only def test_disallow_instantiation(self): @@ -300,37 +1134,60 @@ def test_failed_import_during_compiling(self): "(can't load unicodedata module)" self.assertIn(error, result.err.decode("ascii")) + def test_unicodedata_unload_reload(self): + # gh-149449: dropping unicodedata and running gc must not leave the + # cached _ucnhash_CAPI pointer dangling. + code = ( + "import gc, sys\n" + "assert '\\N{GRINNING FACE}'.encode(" + " 'ascii', errors='namereplace') == b'\\\\N{GRINNING FACE}'\n" + "compile(r\"x = '\\\\N{LATIN CAPITAL LETTER A}'\", '<x>', 'exec')\n" + "del sys.modules['unicodedata']\n" + "gc.collect()\n" + "assert '\\N{WINKING FACE}'.encode(" + " 'ascii', errors='namereplace') == b'\\\\N{WINKING FACE}'\n" + "compile(r\"x = '\\\\N{LATIN CAPITAL LETTER B}'\", '<x>', 'exec')\n" + ) + script_helper.assert_python_ok("-c", code) + def test_decimal_numeric_consistent(self): # Test that decimal and numeric are consistent, # i.e. if a character has a decimal value, # its numeric value should be the same. count = 0 - for i in range(0x10000): - c = chr(i) + for c in iterallchars(): dec = self.db.decimal(c, -1) if dec != -1: self.assertEqual(dec, self.db.numeric(c)) count += 1 - self.assertTrue(count >= 10) # should have tested at least the ASCII digits + self.assertTrue(count >= 10, count) # should have tested at least the ASCII digits def test_digit_numeric_consistent(self): # Test that digit and numeric are consistent, # i.e. if a character has a digit value, # its numeric value should be the same. count = 0 - for i in range(0x10000): - c = chr(i) + for c in iterallchars(): dec = self.db.digit(c, -1) if dec != -1: self.assertEqual(dec, self.db.numeric(c)) count += 1 - self.assertTrue(count >= 10) # should have tested at least the ASCII digits + self.assertTrue(count >= 10, count) # should have tested at least the ASCII digits + + def test_normalize_consistent(self): + allchars = list(iterallchars()) + for form in ('NFC', 'NFD', 'NFKC', 'NFKD'): + for c in allchars: + norm = self.db.normalize(form, c) + self.assertEqual(self.db.is_normalized(form, c), norm == c) + if norm != c: + self.assertEqual(self.db.normalize(form, norm), norm) + self.assertTrue(self.db.is_normalized(form, norm)) def test_bug_1704793(self): self.assertEqual(self.db.lookup("GOTHIC LETTER FAIHU"), '\U00010346') def test_ucd_510(self): - import unicodedata # In UCD 5.1.0, a mirrored property changed wrt. UCD 3.2.0 self.assertTrue(unicodedata.mirrored("\u0f3a")) self.assertTrue(not unicodedata.ucd_3_2_0.mirrored("\u0f3a")) @@ -346,10 +1203,10 @@ def test_bug_5828(self): # Only U+0000 should have U+0000 as its upper/lower/titlecase variant self.assertEqual( [ - c for c in range(sys.maxunicode+1) - if "\x00" in chr(c).lower()+chr(c).upper()+chr(c).title() + c for c in iterallchars() + if "\x00" in (c.lower(), c.upper(), c.title()) ], - [0] + ["\x00"] ) def test_bug_4971(self): @@ -359,22 +1216,29 @@ def test_bug_4971(self): self.assertEqual("\u01c6".title(), "\u01c5") def test_linebreak_7643(self): - for i in range(0x10000): - lines = (chr(i) + 'A').splitlines() - if i in (0x0a, 0x0b, 0x0c, 0x0d, 0x85, - 0x1c, 0x1d, 0x1e, 0x2028, 0x2029): + for c in iterallchars(): + lines = (c + 'A').splitlines() + if c in ('\x0a', '\x0b', '\x0c', '\x0d', '\x85', + '\x1c', '\x1d', '\x1e', '\u2028', '\u2029'): self.assertEqual(len(lines), 2, - r"\u%.4x should be a linebreak" % i) + r"%a should be a linebreak" % c) else: self.assertEqual(len(lines), 1, - r"\u%.4x should not be a linebreak" % i) + r"%a should not be a linebreak" % c) -class NormalizationTest(unittest.TestCase): - @staticmethod - def check_version(testfile): - hdr = testfile.readline() - return unicodedata.unidata_version in hdr + def test_segment_object(self): + segments = list(unicodedata.iter_graphemes('spa\u0300m')) + self.assertEqual(len(segments), 4, segments) + segment = segments[2] + self.assertEqual(segment.start, 2) + self.assertEqual(segment.end, 4) + self.assertEqual(str(segment), 'a\u0300') + self.assertEqual(repr(segment), '<Segment 2:4>') + self.assertRaises(TypeError, iter, segment) + self.assertRaises(TypeError, len, segment) + +class NormalizationTest(unittest.TestCase): @staticmethod def unistr(data): data = [int(x, 16) for x in data.split(" ")] @@ -384,36 +1248,26 @@ def unistr(data): @requires_resource('cpu') def test_normalization(self): TESTDATAFILE = "NormalizationTest.txt" - TESTDATAURL = f"http://www.pythontest.net/unicode/{unicodedata.unidata_version}/{TESTDATAFILE}" - - # Hit the exception early - try: - testdata = open_urlresource(TESTDATAURL, encoding="utf-8", - check=self.check_version) - except PermissionError: - self.skipTest(f"Permission error when downloading {TESTDATAURL} " - f"into the test data directory") - except (OSError, HTTPException) as exc: - self.skipTest(f"Failed to download {TESTDATAURL}: {exc}") + testdata = download_test_data_file(TESTDATAFILE) with testdata: - self.run_normalization_tests(testdata) + self.run_normalization_tests(testdata, unicodedata) - def run_normalization_tests(self, testdata): - part = None - part1_data = {} - - def NFC(str): - return unicodedata.normalize("NFC", str) - - def NFKC(str): - return unicodedata.normalize("NFKC", str) + @requires_resource('cpu') + def test_normalization_3_2_0(self): + testdatafile = findfile('NormalizationTest-3.2.0.txt') + with open(testdatafile, encoding='utf-8') as testdata: + self.run_normalization_tests(testdata, unicodedata.ucd_3_2_0) - def NFD(str): - return unicodedata.normalize("NFD", str) + def run_normalization_tests(self, testdata, ucd): + part = None + part1_data = set() - def NFKD(str): - return unicodedata.normalize("NFKD", str) + NFC = partial(ucd.normalize, "NFC") + NFKC = partial(ucd.normalize, "NFKC") + NFD = partial(ucd.normalize, "NFD") + NFKD = partial(ucd.normalize, "NFKD") + is_normalized = ucd.is_normalized for line in testdata: if '#' in line: @@ -438,25 +1292,24 @@ def NFKD(str): NFKD(c3) == NFKD(c4) == NFKD(c5), line) - self.assertTrue(unicodedata.is_normalized("NFC", c2)) - self.assertTrue(unicodedata.is_normalized("NFC", c4)) + self.assertTrue(is_normalized("NFC", c2)) + self.assertTrue(is_normalized("NFC", c4)) - self.assertTrue(unicodedata.is_normalized("NFD", c3)) - self.assertTrue(unicodedata.is_normalized("NFD", c5)) + self.assertTrue(is_normalized("NFD", c3)) + self.assertTrue(is_normalized("NFD", c5)) - self.assertTrue(unicodedata.is_normalized("NFKC", c4)) - self.assertTrue(unicodedata.is_normalized("NFKD", c5)) + self.assertTrue(is_normalized("NFKC", c4)) + self.assertTrue(is_normalized("NFKD", c5)) # Record part 1 data if part == "@Part1": - part1_data[c1] = 1 + part1_data.add(c1) # Perform tests for all other data - for c in range(sys.maxunicode+1): - X = chr(c) + for X in iterallchars(): if X in part1_data: continue - self.assertTrue(X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X), c) + self.assertTrue(X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X), ord(X)) def test_edge_cases(self): self.assertRaises(TypeError, unicodedata.normalize) @@ -491,5 +1344,68 @@ class MyStr(str): self.assertIs(type(normalize(form, MyStr(input_str))), str) +class GraphemeBreakTest(unittest.TestCase): + @requires_resource('network') + def test_grapheme_break(self): + TESTDATAFILE = "GraphemeBreakTest.txt" + testdata = download_test_data_file(TESTDATAFILE) + + with testdata: + self.run_grapheme_break_tests(testdata) + + def run_grapheme_break_tests(self, testdata): + for line in testdata: + line, _, comment = line.partition('#') + line = line.strip() + if not line: + continue + comment = comment.strip() + + chunks = [] + breaks = [] + pos = 0 + for field in line.replace('×', ' ').split(): + if field == '÷': + chunks.append('') + breaks.append(pos) + else: + chunks[-1] += chr(int(field, 16)) + pos += 1 + self.assertEqual(chunks.pop(), '', line) + input = ''.join(chunks) + with self.subTest(line): + result = list(unicodedata.iter_graphemes(input)) + self.assertEqual(list(map(str, result)), chunks, comment) + self.assertEqual([x.start for x in result], breaks[:-1], comment) + self.assertEqual([x.end for x in result], breaks[1:], comment) + for i in range(1, len(breaks) - 1): + result = list(unicodedata.iter_graphemes(input, breaks[i])) + self.assertEqual(list(map(str, result)), chunks[i:], comment) + self.assertEqual([x.start for x in result], breaks[i:-1], comment) + self.assertEqual([x.end for x in result], breaks[i+1:], comment) + + def test_reference_loops(self): + # Test that reference loops involving GraphemeBreakIterator or + # Segment can be broken by the garbage collector. + class S(str): + pass + + s = S('abc') + s.ref = unicodedata.iter_graphemes(s) + wr = weakref.ref(s) + del s + self.assertIsNotNone(wr()) + gc_collect() + self.assertIsNone(wr()) + + s = S('abc') + s.ref = next(unicodedata.iter_graphemes(s)) + wr = weakref.ref(s) + del s + self.assertIsNotNone(wr()) + gc_collect() + self.assertIsNone(wr()) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_unittest/test_assertions.py b/Lib/test/test_unittest/test_assertions.py index 1dec947ea76d23..df95e558949aee 100644 --- a/Lib/test/test_unittest/test_assertions.py +++ b/Lib/test/test_unittest/test_assertions.py @@ -406,11 +406,12 @@ def testAssertWarnsRegex(self): # test warning raised but with wrong message def raise_wrong_message(): warnings.warn('foo') - self.assertMessagesCM('assertWarnsRegex', (UserWarning, 'regex'), - raise_wrong_message, - ['^"regex" does not match "foo"$', '^oops$', - '^"regex" does not match "foo"$', - '^"regex" does not match "foo" : oops$']) + with self.assertWarnsRegex(UserWarning, 'foo'): + self.assertMessagesCM('assertWarnsRegex', (UserWarning, 'regex'), + raise_wrong_message, + ['^"regex" does not match "foo"$', '^oops$', + '^"regex" does not match "foo"$', + '^"regex" does not match "foo" : oops$']) if __name__ == "__main__": diff --git a/Lib/test/test_unittest/test_case.py b/Lib/test/test_unittest/test_case.py index cf10e956bf2bdc..83b42918e1eff6 100644 --- a/Lib/test/test_unittest/test_case.py +++ b/Lib/test/test_unittest/test_case.py @@ -1631,11 +1631,11 @@ def testAssertRaisesRegexNoExceptionType(self): self.assertRaisesRegex((ValueError, object), 'expect') def testAssertWarnsCallable(self): - def _runtime_warn(): - warnings.warn("foo", RuntimeWarning) + def _runtime_warn(categories=(RuntimeWarning,)): + for category in categories: + warnings.warn("foo", category) # Success when the right warning is triggered, even several times - self.assertWarns(RuntimeWarning, _runtime_warn) - self.assertWarns(RuntimeWarning, _runtime_warn) + self.assertWarns(RuntimeWarning, _runtime_warn, (RuntimeWarning, RuntimeWarning)) # A tuple of warning classes is accepted self.assertWarns((DeprecationWarning, RuntimeWarning), _runtime_warn) # *args and **kwargs also work @@ -1648,22 +1648,35 @@ def _runtime_warn(): with self.assertRaises(TypeError): self.assertWarns(RuntimeWarning, None) # Failure when another warning is triggered - with warnings.catch_warnings(): + with warnings.catch_warnings(record=True) as log: # Force default filter (in case tests are run with -We) warnings.simplefilter("default", RuntimeWarning) with self.assertRaises(self.failureException): - self.assertWarns(DeprecationWarning, _runtime_warn) + self.assertWarns(DeprecationWarning, _runtime_warn, + (RuntimeWarning, RuntimeWarning)) + self.assertEqual(len(log), 1, log) + self.assertIsInstance(log[0].message, RuntimeWarning) # Filters for other warnings are not modified with warnings.catch_warnings(): warnings.simplefilter("error", RuntimeWarning) with self.assertRaises(RuntimeWarning): self.assertWarns(DeprecationWarning, _runtime_warn) + # Warnings that do not match the category are not swallowed. + with self.assertWarns(RuntimeWarning): + with self.assertRaises(self.failureException): + self.assertWarns(DeprecationWarning, _runtime_warn) + with self.assertWarns(RuntimeWarning): + self.assertWarns(DeprecationWarning, _runtime_warn, + (RuntimeWarning, DeprecationWarning)) + with self.assertWarns(RuntimeWarning): + self.assertWarns(DeprecationWarning, _runtime_warn, + (DeprecationWarning, RuntimeWarning)) def testAssertWarnsContext(self): # Believe it or not, it is preferable to duplicate all tests above, # to make sure the __warningregistry__ $@ is circumvented correctly. - def _runtime_warn(): - warnings.warn("foo", RuntimeWarning) + def _runtime_warn(category=RuntimeWarning): + warnings.warn("foo", category) _runtime_warn_lineno = inspect.getsourcelines(_runtime_warn)[1] with self.assertWarns(RuntimeWarning) as cm: _runtime_warn() @@ -1694,18 +1707,58 @@ def _runtime_warn(): with self.assertWarns(RuntimeWarning, foobar=42): pass # Failure when another warning is triggered - with warnings.catch_warnings(): + with warnings.catch_warnings(record=True) as log: # Force default filter (in case tests are run with -We) warnings.simplefilter("default", RuntimeWarning) with self.assertRaises(self.failureException): with self.assertWarns(DeprecationWarning): _runtime_warn() + _runtime_warn() + self.assertEqual(len(log), 1, log) + self.assertIsInstance(log[0].message, RuntimeWarning) + with warnings.catch_warnings(record=True) as log: + # Force default filter (in case tests are run with -We) + warnings.simplefilter("error", RuntimeWarning) + warnings.filterwarnings("default", category=RuntimeWarning, + module=__name__) + with self.assertRaises(self.failureException): + with self.assertWarns(DeprecationWarning): + _runtime_warn() + _runtime_warn() + self.assertEqual(len(log), 1, log) + self.assertIsInstance(log[0].message, RuntimeWarning) # Filters for other warnings are not modified with warnings.catch_warnings(): warnings.simplefilter("error", RuntimeWarning) with self.assertRaises(RuntimeWarning): with self.assertWarns(DeprecationWarning): _runtime_warn() + # Warnings that do not match the category are not swallowed. + with self.assertWarns(RuntimeWarning): + with self.assertRaises(self.failureException): + with self.assertWarns(DeprecationWarning): + _runtime_warn() + with self.assertWarns(RuntimeWarning): + with self.assertWarns(DeprecationWarning): + _runtime_warn() + _runtime_warn(DeprecationWarning) + with self.assertWarns(RuntimeWarning): + with self.assertWarns(DeprecationWarning): + _runtime_warn(DeprecationWarning) + _runtime_warn() + # Filters by module name work for other warnings. + with warnings.catch_warnings(record=True) as log: + warnings.filterwarnings("error", category=RuntimeWarning) + warnings.filterwarnings("default", category=RuntimeWarning, + module=re.escape(__name__)) + warnings.filterwarnings("error", category=RuntimeWarning, + module='test_case') + with self.assertWarns(DeprecationWarning): + _runtime_warn(DeprecationWarning) + _runtime_warn() + _runtime_warn() + self.assertEqual(len(log), 1, log) + self.assertIsInstance(log[0].message, RuntimeWarning) def testAssertWarnsNoExceptionType(self): with self.assertRaises(TypeError): @@ -1722,8 +1775,9 @@ def testAssertWarnsNoExceptionType(self): self.assertWarns((UserWarning, Exception)) def testAssertWarnsRegexCallable(self): - def _runtime_warn(msg): - warnings.warn(msg, RuntimeWarning) + def _runtime_warn(*msgs): + for msg in msgs: + warnings.warn(msg, RuntimeWarning) self.assertWarnsRegex(RuntimeWarning, "o+", _runtime_warn, "foox") # Failure when no warning is triggered @@ -1734,16 +1788,26 @@ def _runtime_warn(msg): with self.assertRaises(TypeError): self.assertWarnsRegex(RuntimeWarning, "o+", None) # Failure when another warning is triggered - with warnings.catch_warnings(): + with warnings.catch_warnings(record=True) as log: # Force default filter (in case tests are run with -We) warnings.simplefilter("default", RuntimeWarning) with self.assertRaises(self.failureException): self.assertWarnsRegex(DeprecationWarning, "o+", - _runtime_warn, "foox") - # Failure when message doesn't match - with self.assertRaises(self.failureException): + _runtime_warn, "foox", "foox") + self.assertEqual(len(log), 1, log) + self.assertIsInstance(log[0].message, RuntimeWarning) + # Failure when message doesn't match. + # Warnings that do not match the regex are not swallowed. + with self.assertWarnsRegex(RuntimeWarning, "ar"): + with self.assertRaises(self.failureException): + self.assertWarnsRegex(RuntimeWarning, "o+", + _runtime_warn, "barz") + with self.assertWarnsRegex(RuntimeWarning, "ar"): self.assertWarnsRegex(RuntimeWarning, "o+", - _runtime_warn, "barz") + _runtime_warn, "barz", "foox") + with self.assertWarnsRegex(RuntimeWarning, "ar"): + self.assertWarnsRegex(RuntimeWarning, "o+", + _runtime_warn, "foox", "barz") # A little trickier: we ask RuntimeWarnings to be raised, and then # check for some of them. It is implementation-defined whether # non-matching RuntimeWarnings are simply re-raised, or produce a @@ -1778,15 +1842,28 @@ def _runtime_warn(msg): with self.assertWarnsRegex(RuntimeWarning, 'o+', foobar=42): pass # Failure when another warning is triggered - with warnings.catch_warnings(): + with warnings.catch_warnings(record=True) as log: # Force default filter (in case tests are run with -We) warnings.simplefilter("default", RuntimeWarning) with self.assertRaises(self.failureException): with self.assertWarnsRegex(DeprecationWarning, "o+"): _runtime_warn("foox") - # Failure when message doesn't match - with self.assertRaises(self.failureException): + _runtime_warn("foox") + self.assertEqual(len(log), 1, log) + self.assertIsInstance(log[0].message, RuntimeWarning) + # Failure when message doesn't match. + # Warnings that do not match the regex are not swallowed. + with self.assertWarnsRegex(RuntimeWarning, "ar"): + with self.assertRaises(self.failureException): + with self.assertWarnsRegex(RuntimeWarning, "o+"): + _runtime_warn("barz") + with self.assertWarnsRegex(RuntimeWarning, "ar"): + with self.assertWarnsRegex(RuntimeWarning, "o+"): + _runtime_warn("barz") + _runtime_warn("foox") + with self.assertWarnsRegex(RuntimeWarning, "ar"): with self.assertWarnsRegex(RuntimeWarning, "o+"): + _runtime_warn("foox") _runtime_warn("barz") # A little trickier: we ask RuntimeWarnings to be raised, and then # check for some of them. It is implementation-defined whether @@ -1797,6 +1874,19 @@ def _runtime_warn(msg): with self.assertRaises((RuntimeWarning, self.failureException)): with self.assertWarnsRegex(RuntimeWarning, "o+"): _runtime_warn("barz") + # Filters by module name work for warnings with other message. + with warnings.catch_warnings(record=True) as log: + warnings.filterwarnings("error", category=RuntimeWarning) + warnings.filterwarnings("default", category=RuntimeWarning, + module=re.escape(__name__)) + warnings.filterwarnings("error", category=RuntimeWarning, + module='test_case') + with self.assertWarnsRegex(RuntimeWarning, "ar"): + _runtime_warn("bar") + _runtime_warn("foox") + _runtime_warn("foox") + self.assertEqual(len(log), 1, log) + self.assertIsInstance(log[0].message, RuntimeWarning) def testAssertWarnsRegexNoExceptionType(self): with self.assertRaises(TypeError): diff --git a/Lib/test/test_unittest/test_program.py b/Lib/test/test_unittest/test_program.py index 6092ed292d8f60..8ed92373e5e984 100644 --- a/Lib/test/test_unittest/test_program.py +++ b/Lib/test/test_unittest/test_program.py @@ -75,6 +75,14 @@ def testUnexpectedSuccess(self): class Empty(unittest.TestCase): pass + class SetUpClassFailure(unittest.TestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + raise Exception + def testPass(self): + pass + class TestLoader(unittest.TestLoader): """Test loader that returns a suite containing the supplied testcase.""" @@ -191,6 +199,18 @@ def test_ExitEmptySuite(self): out = stream.getvalue() self.assertIn('\nNO TESTS RAN\n', out) + def test_ExitSetUpClassFailureSuite(self): + stream = BufferedWriter() + with self.assertRaises(SystemExit) as cm: + unittest.main( + argv=["setup_class_failure"], + testRunner=unittest.TextTestRunner(stream=stream), + testLoader=self.TestLoader(self.SetUpClassFailure)) + self.assertEqual(cm.exception.code, 1) + out = stream.getvalue() + self.assertIn("ERROR: setUpClass", out) + self.assertIn("SetUpClassFailure", out) + class InitialisableProgram(unittest.TestProgram): exit = False diff --git a/Lib/test/test_unittest/test_util.py b/Lib/test/test_unittest/test_util.py index d590a333930278..09ce09b91b7ac2 100644 --- a/Lib/test/test_unittest/test_util.py +++ b/Lib/test/test_unittest/test_util.py @@ -26,6 +26,39 @@ def test_sorted_list_difference(self): self.assertEqual(sorted_list_difference([2], [1, 1]), ([2], [1])) self.assertEqual(sorted_list_difference([1, 2], [1, 1]), ([2], [])) + def test_sorted_list_difference_tail_deduplication(self): + # Tail deduplication when one list is exhausted before the other. + # These exercise the except-IndexError path in sorted_list_difference. + self.assertEqual(sorted_list_difference([], [0, 0]), ([], [0])) + self.assertEqual(sorted_list_difference([0, 0], []), ([0], [])) + self.assertEqual(sorted_list_difference([], [1, 1, 2, 2]), ([], [1, 2])) + self.assertEqual(sorted_list_difference([1, 1, 2, 2], []), ([1, 2], [])) + # One list exhausts mid-way, leaving duplicated tail in the other. + self.assertEqual(sorted_list_difference([1], [1, 2, 2, 3, 3]), ([], [2, 3])) + self.assertEqual(sorted_list_difference([1, 2, 2, 3, 3], [1]), ([2, 3], [])) + + def test_sorted_list_difference_strings(self): + self.assertEqual( + sorted_list_difference(['a', 'b'], ['b', 'c']), + (['a'], ['c'])) + self.assertEqual( + sorted_list_difference([], ['a', 'a', 'b']), + ([], ['a', 'b'])) + self.assertEqual( + sorted_list_difference(['a', 'a', 'b'], []), + (['a', 'b'], [])) + + def test_sorted_list_difference_unhashable(self): + self.assertEqual( + sorted_list_difference([[1], [2]], [[2], [3]]), + ([[1]], [[3]])) + self.assertEqual( + sorted_list_difference([], [[0], [0]]), + ([], [[0]])) + self.assertEqual( + sorted_list_difference([[0], [0]], []), + ([[0]], [])) + def test_unorderable_list_difference(self): self.assertEqual(unorderable_list_difference([], []), ([], [])) self.assertEqual(unorderable_list_difference([1, 2], []), ([2, 1], [])) diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index 386d53bf5a5c63..764585ec5d5468 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -1743,6 +1743,13 @@ def static_method(): pass mock_method.assert_called_once_with() self.assertRaises(TypeError, mock_method, 'extra_arg') + # gh-145754 + def test_create_autospec_type_hints_typechecking(self): + def foo(x: Tuple[int, ...]) -> None: + pass + + mock.create_autospec(foo) + #Issue21238 def test_mock_unsafe(self): m = Mock() diff --git a/Lib/test/test_unittest/testmock/testthreadingmock.py b/Lib/test/test_unittest/testmock/testthreadingmock.py index a02b532ed447cd..dda4916434ec1b 100644 --- a/Lib/test/test_unittest/testmock/testthreadingmock.py +++ b/Lib/test/test_unittest/testmock/testthreadingmock.py @@ -1,8 +1,10 @@ +import sys import time import unittest +import threading import concurrent.futures -from test.support import threading_helper +from test.support import setswitchinterval, threading_helper from unittest.mock import patch, ThreadingMock @@ -196,6 +198,102 @@ def test_reset_mock_resets_wait(self): m.wait_until_any_call_with() m.assert_called_once() + def test_call_count_thread_safe(self): + # See https://github.com/python/cpython/issues/142651. + m = ThreadingMock() + LOOPS = 100 + THREADS = 10 + def test_function(): + for _ in range(LOOPS): + m() + + oldswitchinterval = sys.getswitchinterval() + setswitchinterval(1e-6) + try: + threads = [threading.Thread(target=test_function) for _ in range(THREADS)] + with threading_helper.start_threads(threads): + pass + finally: + sys.setswitchinterval(oldswitchinterval) + + self.assertEqual(m.call_count, LOOPS * THREADS) + + + def test_call_args_thread_safe(self): + m = ThreadingMock() + LOOPS = 100 + THREADS = 10 + def test_function(thread_id): + for i in range(LOOPS): + m(thread_id, i) + + oldswitchinterval = sys.getswitchinterval() + setswitchinterval(1e-6) + try: + threads = [ + threading.Thread(target=test_function, args=(thread_id,)) + for thread_id in range(THREADS) + ] + with threading_helper.start_threads(threads): + pass + finally: + sys.setswitchinterval(oldswitchinterval) + expected_calls = { + (thread_id, i) + for thread_id in range(THREADS) + for i in range(LOOPS) + } + self.assertSetEqual({call.args for call in m.call_args_list}, expected_calls) + + def test_method_calls_thread_safe(self): + m = ThreadingMock() + LOOPS = 100 + THREADS = 10 + def test_function(thread_id): + for i in range(LOOPS): + getattr(m, f"method_{thread_id}")(i) + + oldswitchinterval = sys.getswitchinterval() + setswitchinterval(1e-6) + try: + threads = [ + threading.Thread(target=test_function, args=(thread_id,)) + for thread_id in range(THREADS) + ] + with threading_helper.start_threads(threads): + pass + finally: + sys.setswitchinterval(oldswitchinterval) + for thread_id in range(THREADS): + self.assertEqual(getattr(m, f"method_{thread_id}").call_count, LOOPS) + self.assertEqual({call.args for call in getattr(m, f"method_{thread_id}").call_args_list}, + {(i,) for i in range(LOOPS)}) + + def test_mock_calls_thread_safe(self): + m = ThreadingMock() + LOOPS = 100 + THREADS = 10 + def test_function(thread_id): + for i in range(LOOPS): + m(thread_id, i) + + oldswitchinterval = sys.getswitchinterval() + setswitchinterval(1e-6) + try: + threads = [ + threading.Thread(target=test_function, args=(thread_id,)) + for thread_id in range(THREADS) + ] + with threading_helper.start_threads(threads): + pass + finally: + sys.setswitchinterval(oldswitchinterval) + expected_calls = { + (thread_id, i) + for thread_id in range(THREADS) + for i in range(LOOPS) + } + self.assertSetEqual({call.args for call in m.mock_calls}, expected_calls) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_unpack_ex.py b/Lib/test/test_unpack_ex.py index 9e2d54bd3a8c4e..33c96b84964b59 100644 --- a/Lib/test/test_unpack_ex.py +++ b/Lib/test/test_unpack_ex.py @@ -134,6 +134,22 @@ ... TypeError: 'list' object is not a mapping + >>> class OnlyKeys: + ... def keys(self): + ... return ['key'] + >>> {**OnlyKeys()} + Traceback (most recent call last): + ... + TypeError: 'OnlyKeys' object is not subscriptable + + >>> class BrokenKeys: + ... def keys(self): + ... return 1 + >>> {**BrokenKeys()} + Traceback (most recent call last): + ... + TypeError: test.test_unpack_ex.BrokenKeys.keys() must return an iterable, not int + >>> len(eval("{" + ", ".join("**{{{}: {}}}".format(i, i) ... for i in range(1000)) + "}")) 1000 @@ -141,7 +157,7 @@ >>> {0:1, **{0:2}, 0:3, 0:4} {0: 4} -List comprehension element unpacking +Comprehension element unpacking >>> a, b, c = [0, 1, 2], 3, 4 >>> [*a, b, c] @@ -149,46 +165,206 @@ >>> l = [a, (3, 4), {5}, {6: None}, (i for i in range(7, 10))] >>> [*item for item in l] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + >>> [*[0, 1] for i in range(5)] + [0, 1, 0, 1, 0, 1, 0, 1, 0, 1] + + >>> [*'a' for i in range(5)] + ['a', 'a', 'a', 'a', 'a'] + + >>> [*[] for i in range(10)] + [] + + >>> [*(x*2) for x in [[1, 2, 3], [], 'cat']] + [1, 2, 3, 1, 2, 3, 'c', 'a', 't', 'c', 'a', 't'] + + >>> {**{} for a in [1]} + {} + + >>> {**{7: i} for i in range(10)} + {7: 9} + + >>> dicts = [{1: 2}, {3: 4}, {5: 6, 7: 8}, {}, {9: 10}, {1: 0}] + >>> {**d for d in dicts} + {1: 0, 3: 4, 5: 6, 7: 8, 9: 10} + + >>> gen = (*(0, 1) for i in range(5)) + >>> next(gen) + 0 + >>> list(gen) + [1, 0, 1, 0, 1, 0, 1, 0, 1] + +Comprehension unpacking with conditionals and double loops + + >>> [*[i, i+1] for i in range(5) if i % 2 == 0] + [0, 1, 2, 3, 4, 5] + + >>> [*y for x in [[[0], [1, 2, 3], [], [4, 5]], [[6, 7]]] for y in x] + [0, 1, 2, 3, 4, 5, 6, 7] + + >>> [*y for x in [[[0], [1, 2, 3], [], [4, 5]], [[6, 7]]] for y in x if y and y[0]>0] + [1, 2, 3, 4, 5, 6, 7] + + >>> dicts = [{1: 2}, {3: 4}, {5: 6, 7: 8}, {}, {9: 10}, {1: 0}] + >>> {**d for d in dicts if len(d) != 2} + {1: 0, 3: 4, 9: 10} + +Scoping of assignment expressions in comprehensions + + >>> [*((y := i**2), 2*y) for i in range(4)] + [0, 0, 1, 2, 4, 8, 9, 18] + >>> y + 9 + + >>> [*(y := [i, i+1, i+2]) for i in range(4)] + [0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5] + >>> y + [3, 4, 5] + + >>> g = (*(z := [i, i+1, i+2]) for i in range(4)) + >>> z + Traceback (most recent call last): + ... + NameError: name 'z' is not defined + >>> next(g) + 0 + >>> z + [0, 1, 2] + >>> next(g) + 1 + >>> z + [0, 1, 2] + >>> next(g) + 2 + >>> z + [0, 1, 2] + >>> next(g) + 1 + >>> z + [1, 2, 3] + + >>> x = [1, 2, 3] + >>> y = [4, 5, 6] + >>> def f(*args): + ... print(args) + + >>> f(*x if x else y) + (1, 2, 3) + + +Malformed comperehension element unpacking + + >>> [*x for x in [1, 2, 3]] Traceback (most recent call last): ... - SyntaxError: iterable unpacking cannot be used in comprehension + [*x for x in [1, 2, 3]] + ^^ + TypeError: Value after * must be an iterable, not int + - >>> [*[0, 1] for i in range(10)] +Error messages for specific failure modes of unpacking + + >>> [*x if x else y for x in z] Traceback (most recent call last): ... - SyntaxError: iterable unpacking cannot be used in comprehension + [*x if x else y for x in z] + ^^^^^^^^^^^^^^ + SyntaxError: invalid starred expression. Did you forget to wrap the conditional expression in parentheses? - >>> [*'a' for i in range(10)] + >>> [*x if x else y] Traceback (most recent call last): ... - SyntaxError: iterable unpacking cannot be used in comprehension + [*x if x else y] + ^^^^^^^^^^^^^^ + SyntaxError: invalid starred expression. Did you forget to wrap the conditional expression in parentheses? - >>> [*[] for i in range(10)] + >>> [x if x else *y for x in z] Traceback (most recent call last): ... - SyntaxError: iterable unpacking cannot be used in comprehension + [x if x else *y for x in z] + ^ + SyntaxError: cannot unpack only part of a conditional expression - >>> {**{} for a in [1]} + >>> [x if x else *y] Traceback (most recent call last): ... - SyntaxError: dict unpacking cannot be used in dict comprehension + [x if x else *y] + ^ + SyntaxError: cannot unpack only part of a conditional expression -# Pegen is better here. -# Generator expression in function arguments + >>> {**x if x else y} + Traceback (most recent call last): + ... + {**x if x else y} + ^^^^^^^^^^^^^^^^ + SyntaxError: invalid double starred expression. Did you forget to wrap the conditional expression in parentheses? + >>> {x if x else **y} + Traceback (most recent call last): + ... + {x if x else **y} + ^^ + SyntaxError: cannot use dict unpacking on only part of a conditional expression -# >>> list(*x for x in (range(5) for i in range(3))) -# Traceback (most recent call last): -# ... -# list(*x for x in (range(5) for i in range(3))) -# ^ -# SyntaxError: invalid syntax + >>> [**x for x in [{1: 2}]] + Traceback (most recent call last): + ... + [**x for x in [{1: 2}]] + ^^^ + SyntaxError: cannot use dict unpacking in list comprehension + + >>> (**x for x in [{1:2}]) + Traceback (most recent call last): + ... + (**x for x in [{1:2}]) + ^^^ + SyntaxError: cannot use dict unpacking in generator expression >>> dict(**x for x in [{1:2}]) Traceback (most recent call last): ... dict(**x for x in [{1:2}]) - ^ - SyntaxError: invalid syntax + ^^^ + SyntaxError: cannot use dict unpacking in generator expression + + >>> {*a: b for a, b in {1: 2}.items()} + Traceback (most recent call last): + ... + {*a: b for a, b in {1: 2}.items()} + ^^ + SyntaxError: cannot use a starred expression in a dictionary key + + >>> {**a: b for a, b in {1: 2}.items()} + Traceback (most recent call last): + ... + {**a: b for a, b in {1: 2}.items()} + ^^^ + SyntaxError: cannot use dict unpacking in a dictionary key + + >>> {a: *b for a, b in {1: 2}.items()} + Traceback (most recent call last): + ... + {a: *b for a, b in {1: 2}.items()} + ^^ + SyntaxError: cannot use a starred expression in a dictionary value + + >>> {a: **b for a, b in {1: 2}.items()} + Traceback (most recent call last): + ... + {a: **b for a, b in {1: 2}.items()} + ^^^ + SyntaxError: cannot use dict unpacking in a dictionary value + + +# Generator expression in function arguments + + >>> list(*x for x in (range(5) for i in range(3))) + [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4] + + >>> def f(arg): + ... print(type(arg), list(arg), list(arg)) + >>> f(*x for x in [[1,2,3]]) + <class 'generator'> [1, 2, 3] [] Iterable argument unpacking @@ -383,13 +559,13 @@ Some size constraints (all fail.) - >>> s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range(1<<8 + 1)" + >>> s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range((1<<8) + 1)" >>> compile(s, 'test', 'exec') # doctest:+ELLIPSIS Traceback (most recent call last): ... SyntaxError: too many expressions in star-unpacking assignment - >>> s = ", ".join("a%d" % i for i in range(1<<8 + 1)) + ", *rest = range(1<<8 + 2)" + >>> s = ", ".join("a%d" % i for i in range((1<<8) + 1)) + ", *rest = range((1<<8) + 2)" >>> compile(s, 'test', 'exec') # doctest:+ELLIPSIS Traceback (most recent call last): ... @@ -400,6 +576,176 @@ """ +def test_errors_in_iter(): + """ + >>> class A: + ... def __iter__(self): + ... raise exc + ... + >>> exc = ZeroDivisionError('some error') + >>> [*A()] + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> {*A()} + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> exc = AttributeError('some error') + >>> [*A()] + Traceback (most recent call last): + ... + AttributeError: some error + + >>> {*A()} + Traceback (most recent call last): + ... + AttributeError: some error + + >>> exc = TypeError('some error') + >>> [*A()] + Traceback (most recent call last): + ... + TypeError: some error + + >>> {*A()} + Traceback (most recent call last): + ... + TypeError: some error + """ + +def test_errors_in_next(): + """ + >>> class I: + ... def __iter__(self): + ... return self + ... def __next__(self): + ... raise exc + ... + >>> class A: + ... def __iter__(self): + ... return I() + ... + + >>> exc = ZeroDivisionError('some error') + >>> [*A()] + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> {*A()} + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> exc = AttributeError('some error') + >>> [*A()] + Traceback (most recent call last): + ... + AttributeError: some error + + >>> {*A()} + Traceback (most recent call last): + ... + AttributeError: some error + + >>> exc = TypeError('some error') + >>> [*A()] + Traceback (most recent call last): + ... + TypeError: some error + + >>> {*A()} + Traceback (most recent call last): + ... + TypeError: some error + """ + +def test_errors_in_keys(): + """ + >>> class D: + ... def keys(self): + ... raise exc + ... + >>> exc = ZeroDivisionError('some error') + >>> {**D()} + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> exc = AttributeError('some error') + >>> {**D()} + Traceback (most recent call last): + ... + AttributeError: some error + + >>> exc = KeyError('some error') + >>> {**D()} + Traceback (most recent call last): + ... + KeyError: 'some error' + """ + +def test_errors_in_keys_next(): + """ + >>> class I: + ... def __iter__(self): + ... return self + ... def __next__(self): + ... raise exc + ... + >>> class D: + ... def keys(self): + ... return I() + ... + >>> exc = ZeroDivisionError('some error') + >>> {**D()} + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> exc = AttributeError('some error') + >>> {**D()} + Traceback (most recent call last): + ... + AttributeError: some error + + >>> exc = KeyError('some error') + >>> {**D()} + Traceback (most recent call last): + ... + KeyError: 'some error' + """ + +def test_errors_in_getitem(): + """ + >>> class D: + ... def keys(self): + ... return ['key'] + ... def __getitem__(self, key): + ... raise exc + ... + >>> exc = ZeroDivisionError('some error') + >>> {**D()} + Traceback (most recent call last): + ... + ZeroDivisionError: some error + + >>> exc = AttributeError('some error') + >>> {**D()} + Traceback (most recent call last): + ... + AttributeError: some error + + >>> exc = KeyError('some error') + >>> {**D()} + Traceback (most recent call last): + ... + KeyError: 'some error' + """ + __test__ = {'doctests' : doctests} def load_tests(loader, tests, pattern): diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index 0d6b05bc660b76..dcaad49ffab5d2 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -206,6 +206,97 @@ def test_tstrings(self): self.check_ast_roundtrip("t'foo'") self.check_ast_roundtrip("t'foo {bar}'") self.check_ast_roundtrip("t'foo {bar!s:.2f}'") + self.check_ast_roundtrip("t'{a + b}'") + self.check_ast_roundtrip("t'{a + b:x}'") + self.check_ast_roundtrip("t'{a + b!s}'") + self.check_ast_roundtrip("t'{ {a}}'") + self.check_ast_roundtrip("t'{ {a}=}'") + self.check_ast_roundtrip("t'{{a}}'") + self.check_ast_roundtrip("t''") + self.check_ast_roundtrip('t""') + self.check_ast_roundtrip("t'{(lambda x: x)}'") + self.check_ast_roundtrip("t'{t'{x}'}'") + + def test_tstring_with_nonsensical_str_field(self): + # `value` suggests that the original code is `t'{test1}`, but `str` suggests otherwise + self.assertEqual( + ast.unparse( + ast.TemplateStr( + values=[ + ast.Interpolation( + value=ast.Name(id="test1", ctx=ast.Load()), str="test2", conversion=-1 + ) + ] + ) + ), + "t'{test2}'", + ) + + def test_tstring_with_none_str_field(self): + self.assertEqual( + ast.unparse( + ast.TemplateStr( + [ast.Interpolation(value=ast.Name(id="test1"), str=None, conversion=-1)] + ) + ), + "t'{test1}'", + ) + self.assertEqual( + ast.unparse( + ast.TemplateStr( + [ + ast.Interpolation( + value=ast.Lambda( + args=ast.arguments(args=[ast.arg(arg="x")]), + body=ast.Name(id="x"), + ), + str=None, + conversion=-1, + ) + ] + ) + ), + "t'{(lambda x: x)}'", + ) + self.assertEqual( + ast.unparse( + ast.TemplateStr( + values=[ + ast.Interpolation( + value=ast.TemplateStr( + # `str` field kept here + [ast.Interpolation(value=ast.Name(id="x"), str="y", conversion=-1)] + ), + str=None, + conversion=-1, + ) + ] + ) + ), + '''t"{t'{y}'}"''', + ) + self.assertEqual( + ast.unparse( + ast.TemplateStr( + values=[ + ast.Interpolation( + value=ast.TemplateStr( + [ast.Interpolation(value=ast.Name(id="x"), str=None, conversion=-1)] + ), + str=None, + conversion=-1, + ) + ] + ) + ), + '''t"{t'{x}'}"''', + ) + self.assertEqual( + ast.unparse(ast.TemplateStr( + [ast.Interpolation(value=ast.Constant(value="foo"), str=None, conversion=114)] + )), + '''t"{'foo'!r}"''', + ) def test_strings(self): self.check_ast_roundtrip("u'foo'") @@ -312,6 +403,11 @@ def test_set_comprehension(self): def test_dict_comprehension(self): self.check_ast_roundtrip("{x: x*x for x in range(10)}") + def test_dict_comprehension_unpacking(self): + self.check_ast_roundtrip("{**x for x in ()}") + self.check_ast_roundtrip("{**x for x in range(10)}") + self.check_ast_roundtrip("[*x for x in ()]") + def test_class_decorators(self): self.check_ast_roundtrip(class_decorator) @@ -813,15 +909,6 @@ def test_type_params(self): self.check_ast_roundtrip("def f[T: int = int, **P = int, *Ts = *int]():\n pass") self.check_ast_roundtrip("class C[T: int = int, **P = int, *Ts = *int]():\n pass") - def test_tstr(self): - self.check_ast_roundtrip("t'{a + b}'") - self.check_ast_roundtrip("t'{a + b:x}'") - self.check_ast_roundtrip("t'{a + b!s}'") - self.check_ast_roundtrip("t'{ {a}}'") - self.check_ast_roundtrip("t'{ {a}=}'") - self.check_ast_roundtrip("t'{{a}}'") - self.check_ast_roundtrip("t''") - class ManualASTCreationTestCase(unittest.TestCase): """Test that AST nodes created without a type_params field unparse correctly.""" diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index c30fb5e27eea8a..2dd739b77b8e4d 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -10,6 +10,7 @@ from test import support from test.support import os_helper from test.support import socket_helper +from test.support import control_characters_c0 import os import socket try: @@ -590,6 +591,13 @@ def test_invalid_base64_data(self): # missing padding character self.assertRaises(ValueError,urllib.request.urlopen,'data:;base64,Cg=') + def test_invalid_mediatype(self): + for c0 in control_characters_c0(): + self.assertRaises(ValueError,urllib.request.urlopen, + f'data:text/html;{c0},data') + for c0 in control_characters_c0(): + self.assertRaises(ValueError,urllib.request.urlopen, + f'data:text/html{c0};base64,ZGF0YQ==') class urlretrieve_FileTests(unittest.TestCase): """Test urllib.urlretrieve() on local files""" @@ -1590,6 +1598,10 @@ def test_url2pathname_resolve_host(self): def test_url2pathname_win(self): fn = urllib.request.url2pathname self.assertEqual(fn('/C:/'), 'C:\\') + self.assertEqual(fn('//C:'), 'C:') + self.assertEqual(fn('//C:/'), 'C:\\') + self.assertEqual(fn('//C:\\'), 'C:\\') + self.assertEqual(fn('//C:80/'), 'C:80\\') self.assertEqual(fn("///C|"), 'C:') self.assertEqual(fn("///C:"), 'C:') self.assertEqual(fn('///C:/'), 'C:\\') diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 7d7f2fa00d35b6..3a77b9e5ab7928 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -577,6 +577,23 @@ class NonHandler(object): self.assertRaises(TypeError, OpenerDirector().add_handler, NonHandler()) + def test_no_protocol_methods(self): + # test the case that methods starts with handler type without the protocol + # like open*() or _open*(). + # These methods should be ignored + + o = OpenerDirector() + meth_spec = [ + ["open"], + ["_open"], + ["error"] + ] + + add_ordered_mock_handlers(o, meth_spec) + + self.assertEqual(len(o.handle_open), 0) + self.assertEqual(len(o.handle_error), 0) + def test_badly_named_methods(self): # test work-around for three methods that accidentally follow the # naming conventions for handler methods diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index e6a18476908495..17db686942fabe 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -1,9 +1,13 @@ +import contextlib import errno +import sysconfig import unittest +from unittest import mock from test import support from test.support import os_helper from test.support import socket_helper from test.support import ResourceDenied +from test.support.warnings_helper import check_no_resource_warning import os import socket @@ -143,6 +147,43 @@ def test_ftp(self): ] self._test_urls(urls, self._extra_handlers()) + @support.requires_resource('walltime') + @unittest.skipIf(sysconfig.get_platform() == 'linux-ppc64le', + 'leaks on PPC64LE (gh-140691)') + def test_ftp_no_leak(self): + # gh-140691: When the data connection (but not control connection) + # cannot be made established, we shouldn't leave an open socket object. + + class MockError(OSError): + pass + + orig_create_connection = socket.create_connection + def patched_create_connection(address, *args, **kwargs): + """Simulate REJECTing connections to ports other than 21""" + host, port = address + if port != 21: + raise MockError() + return orig_create_connection(address, *args, **kwargs) + + url = 'ftp://www.pythontest.net/README' + entry = url, None, urllib.error.URLError + no_cache_handlers = [urllib.request.FTPHandler()] + cache_handlers = self._extra_handlers() + with mock.patch('socket.create_connection', patched_create_connection): + with check_no_resource_warning(self): + # Try without CacheFTPHandler + self._test_urls([entry], handlers=no_cache_handlers, + retry=False) + with check_no_resource_warning(self): + # Try with CacheFTPHandler (uncached) + self._test_urls([entry], cache_handlers, retry=False) + with check_no_resource_warning(self): + # Try with CacheFTPHandler (cached) + self._test_urls([entry], cache_handlers, retry=False) + # Try without the mock: the handler should not use a closed connection + with check_no_resource_warning(self): + self._test_urls([url], cache_handlers, retry=False) + def test_file(self): TESTFN = os_helper.TESTFN f = open(TESTFN, 'w') @@ -218,27 +259,6 @@ def test_custom_headers(self): opener.open(request) self.assertEqual(request.get_header('User-agent'),'Test-Agent') - @unittest.skip('XXX: http://www.imdb.com is gone') - def test_sites_no_connection_close(self): - # Some sites do not send Connection: close header. - # Verify that those work properly. (#issue12576) - - URL = 'http://www.imdb.com' # mangles Connection:close - - with socket_helper.transient_internet(URL): - try: - with urllib.request.urlopen(URL) as res: - pass - except ValueError: - self.fail("urlopen failed for site not sending \ - Connection:close") - else: - self.assertTrue(res) - - req = urllib.request.urlopen(URL) - res = req.read() - self.assertTrue(res) - def _test_urls(self, urls, handlers, retry=True): import time import logging @@ -255,18 +275,16 @@ def _test_urls(self, urls, handlers, retry=True): else: req = expected_err = None + if expected_err: + context = self.assertRaises(expected_err) + else: + context = contextlib.nullcontext() + with socket_helper.transient_internet(url): - try: + f = None + with context: f = urlopen(url, req, support.INTERNET_TIMEOUT) - # urllib.error.URLError is a subclass of OSError - except OSError as err: - if expected_err: - msg = ("Didn't get expected error(s) %s for %s %s, got %s: %s" % - (expected_err, url, req, type(err), err)) - self.assertIsInstance(err, expected_err, msg) - else: - raise - else: + if f is not None: try: with time_out, \ socket_peer_reset, \ diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py index b2bde5a9b1d696..8e4da0e0f09919 100644 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -1,7 +1,10 @@ +import copy +import pickle import sys import unicodedata import unittest import urllib.parse +from urllib.parse import urldefrag, urlparse, urlsplit, urlunparse, urlunsplit from test import support RFC1808_BASE = "http://a/b/c/d;p?q#f" @@ -107,19 +110,46 @@ class UrlParseTestCase(unittest.TestCase): def checkRoundtrips(self, url, parsed, split, url2=None): if url2 is None: url2 = url - result = urllib.parse.urlparse(url) + self.checkRoundtrips1(url, parsed, split, missing_as_none=True) + empty = url[:0] + parsed = tuple(x or empty for x in parsed) + split = tuple(x or empty for x in split) + self.checkRoundtrips1(url, parsed, split, url2, missing_as_none=False) + + result = urlparse(url, missing_as_none=True) + self.assertEqual(urlunparse(result, keep_empty=False), url2) + self.assertEqual(urlunparse(tuple(result), keep_empty=False), url2) + result = urlparse(url, missing_as_none=False) + with self.assertRaises(ValueError): + urlunparse(result, keep_empty=True) + urlunparse(tuple(result), keep_empty=True) + + result = urlsplit(url, missing_as_none=True) + self.assertEqual(urlunsplit(result, keep_empty=False), url2) + self.assertEqual(urlunsplit(tuple(result), keep_empty=False), url2) + result = urlsplit(url, missing_as_none=False) + with self.assertRaises(ValueError): + urlunsplit(result, keep_empty=True) + urlunsplit(tuple(result), keep_empty=True) + + def checkRoundtrips1(self, url, parsed, split, url2=None, *, missing_as_none): + if url2 is None: + url2 = url + result = urlparse(url, missing_as_none=missing_as_none) self.assertSequenceEqual(result, parsed) t = (result.scheme, result.netloc, result.path, result.params, result.query, result.fragment) self.assertSequenceEqual(t, parsed) # put it back together and it should be the same - result2 = urllib.parse.urlunparse(result) - self.assertSequenceEqual(result2, url2) - self.assertSequenceEqual(result2, result.geturl()) + result2 = urlunparse(result) + self.assertEqual(result2, url2) + self.assertEqual(result2, result.geturl()) + self.assertEqual(urlunparse(result, keep_empty=missing_as_none), url2) + self.assertEqual(urlunparse(tuple(result), keep_empty=missing_as_none), result2) # the result of geturl() is a fixpoint; we can always parse it # again to get the same result: - result3 = urllib.parse.urlparse(result.geturl()) + result3 = urlparse(result.geturl(), missing_as_none=missing_as_none) self.assertEqual(result3.geturl(), result.geturl()) self.assertSequenceEqual(result3, result) self.assertEqual(result3.scheme, result.scheme) @@ -134,17 +164,18 @@ def checkRoundtrips(self, url, parsed, split, url2=None): self.assertEqual(result3.port, result.port) # check the roundtrip using urlsplit() as well - result = urllib.parse.urlsplit(url) + result = urlsplit(url, missing_as_none=missing_as_none) self.assertSequenceEqual(result, split) t = (result.scheme, result.netloc, result.path, - result.query, result.fragment) + result.query, result.fragment) self.assertSequenceEqual(t, split) - result2 = urllib.parse.urlunsplit(result) - self.assertSequenceEqual(result2, url2) - self.assertSequenceEqual(result2, result.geturl()) + result2 = urlunsplit(result) + self.assertEqual(result2, url2) + self.assertEqual(result2, result.geturl()) + self.assertEqual(urlunsplit(tuple(result), keep_empty=missing_as_none), result2) # check the fixpoint property of re-parsing the result of geturl() - result3 = urllib.parse.urlsplit(result.geturl()) + result3 = urlsplit(result.geturl(), missing_as_none=missing_as_none) self.assertEqual(result3.geturl(), result.geturl()) self.assertSequenceEqual(result3, result) self.assertEqual(result3.scheme, result.scheme) @@ -177,94 +208,94 @@ def test_qs(self, orig, expect): @support.subTests('bytes', (False, True)) @support.subTests('url,parsed,split', [ ('path/to/file', - ('', '', 'path/to/file', '', '', ''), - ('', '', 'path/to/file', '', '')), + (None, None, 'path/to/file', None, None, None), + (None, None, 'path/to/file', None, None)), ('/path/to/file', - ('', '', '/path/to/file', '', '', ''), - ('', '', '/path/to/file', '', '')), + (None, None, '/path/to/file', None, None, None), + (None, None, '/path/to/file', None, None)), ('//path/to/file', - ('', 'path', '/to/file', '', '', ''), - ('', 'path', '/to/file', '', '')), + (None, 'path', '/to/file', None, None, None), + (None, 'path', '/to/file', None, None)), ('////path/to/file', - ('', '', '//path/to/file', '', '', ''), - ('', '', '//path/to/file', '', '')), + (None, '', '//path/to/file', None, None, None), + (None, '', '//path/to/file', None, None)), ('/////path/to/file', - ('', '', '///path/to/file', '', '', ''), - ('', '', '///path/to/file', '', '')), + (None, '', '///path/to/file', None, None, None), + (None, '', '///path/to/file', None, None)), ('scheme:path/to/file', - ('scheme', '', 'path/to/file', '', '', ''), - ('scheme', '', 'path/to/file', '', '')), + ('scheme', None, 'path/to/file', None, None, None), + ('scheme', None, 'path/to/file', None, None)), ('scheme:/path/to/file', - ('scheme', '', '/path/to/file', '', '', ''), - ('scheme', '', '/path/to/file', '', '')), + ('scheme', None, '/path/to/file', None, None, None), + ('scheme', None, '/path/to/file', None, None)), ('scheme://path/to/file', - ('scheme', 'path', '/to/file', '', '', ''), - ('scheme', 'path', '/to/file', '', '')), + ('scheme', 'path', '/to/file', None, None, None), + ('scheme', 'path', '/to/file', None, None)), ('scheme:////path/to/file', - ('scheme', '', '//path/to/file', '', '', ''), - ('scheme', '', '//path/to/file', '', '')), + ('scheme', '', '//path/to/file', None, None, None), + ('scheme', '', '//path/to/file', None, None)), ('scheme://///path/to/file', - ('scheme', '', '///path/to/file', '', '', ''), - ('scheme', '', '///path/to/file', '', '')), + ('scheme', '', '///path/to/file', None, None, None), + ('scheme', '', '///path/to/file', None, None)), ('file:tmp/junk.txt', - ('file', '', 'tmp/junk.txt', '', '', ''), - ('file', '', 'tmp/junk.txt', '', '')), + ('file', None, 'tmp/junk.txt', None, None, None), + ('file', None, 'tmp/junk.txt', None, None)), ('file:///tmp/junk.txt', - ('file', '', '/tmp/junk.txt', '', '', ''), - ('file', '', '/tmp/junk.txt', '', '')), + ('file', '', '/tmp/junk.txt', None, None, None), + ('file', '', '/tmp/junk.txt', None, None)), ('file:////tmp/junk.txt', - ('file', '', '//tmp/junk.txt', '', '', ''), - ('file', '', '//tmp/junk.txt', '', '')), + ('file', '', '//tmp/junk.txt', None, None, None), + ('file', '', '//tmp/junk.txt', None, None)), ('file://///tmp/junk.txt', - ('file', '', '///tmp/junk.txt', '', '', ''), - ('file', '', '///tmp/junk.txt', '', '')), + ('file', '', '///tmp/junk.txt', None, None, None), + ('file', '', '///tmp/junk.txt', None, None)), ('http:tmp/junk.txt', - ('http', '', 'tmp/junk.txt', '', '', ''), - ('http', '', 'tmp/junk.txt', '', '')), + ('http', None, 'tmp/junk.txt', None, None, None), + ('http', None, 'tmp/junk.txt', None, None)), ('http://example.com/tmp/junk.txt', - ('http', 'example.com', '/tmp/junk.txt', '', '', ''), - ('http', 'example.com', '/tmp/junk.txt', '', '')), + ('http', 'example.com', '/tmp/junk.txt', None, None, None), + ('http', 'example.com', '/tmp/junk.txt', None, None)), ('http:///example.com/tmp/junk.txt', - ('http', '', '/example.com/tmp/junk.txt', '', '', ''), - ('http', '', '/example.com/tmp/junk.txt', '', '')), + ('http', '', '/example.com/tmp/junk.txt', None, None, None), + ('http', '', '/example.com/tmp/junk.txt', None, None)), ('http:////example.com/tmp/junk.txt', - ('http', '', '//example.com/tmp/junk.txt', '', '', ''), - ('http', '', '//example.com/tmp/junk.txt', '', '')), + ('http', '', '//example.com/tmp/junk.txt', None, None, None), + ('http', '', '//example.com/tmp/junk.txt', None, None)), ('imap://mail.python.org/mbox1', - ('imap', 'mail.python.org', '/mbox1', '', '', ''), - ('imap', 'mail.python.org', '/mbox1', '', '')), + ('imap', 'mail.python.org', '/mbox1', None, None, None), + ('imap', 'mail.python.org', '/mbox1', None, None)), ('mms://wms.sys.hinet.net/cts/Drama/09006251100.asf', ('mms', 'wms.sys.hinet.net', '/cts/Drama/09006251100.asf', - '', '', ''), + None, None, None), ('mms', 'wms.sys.hinet.net', '/cts/Drama/09006251100.asf', - '', '')), + None, None)), ('nfs://server/path/to/file.txt', - ('nfs', 'server', '/path/to/file.txt', '', '', ''), - ('nfs', 'server', '/path/to/file.txt', '', '')), + ('nfs', 'server', '/path/to/file.txt', None, None, None), + ('nfs', 'server', '/path/to/file.txt', None, None)), ('svn+ssh://svn.zope.org/repos/main/ZConfig/trunk/', ('svn+ssh', 'svn.zope.org', '/repos/main/ZConfig/trunk/', - '', '', ''), + None, None, None), ('svn+ssh', 'svn.zope.org', '/repos/main/ZConfig/trunk/', - '', '')), + None, None)), ('git+ssh://git@github.com/user/project.git', ('git+ssh', 'git@github.com','/user/project.git', - '','',''), + None,None,None), ('git+ssh', 'git@github.com','/user/project.git', - '', '')), + None, None)), ('itms-services://?action=download-manifest&url=https://example.com/app', - ('itms-services', '', '', '', - 'action=download-manifest&url=https://example.com/app', ''), + ('itms-services', '', '', None, + 'action=download-manifest&url=https://example.com/app', None), ('itms-services', '', '', - 'action=download-manifest&url=https://example.com/app', '')), + 'action=download-manifest&url=https://example.com/app', None)), ('+scheme:path/to/file', - ('', '', '+scheme:path/to/file', '', '', ''), - ('', '', '+scheme:path/to/file', '', '')), + (None, None, '+scheme:path/to/file', None, None, None), + (None, None, '+scheme:path/to/file', None, None)), ('sch_me:path/to/file', - ('', '', 'sch_me:path/to/file', '', '', ''), - ('', '', 'sch_me:path/to/file', '', '')), + (None, None, 'sch_me:path/to/file', None, None, None), + (None, None, 'sch_me:path/to/file', None, None)), ('schème:path/to/file', - ('', '', 'schème:path/to/file', '', '', ''), - ('', '', 'schème:path/to/file', '', '')), + (None, None, 'schème:path/to/file', None, None, None), + (None, None, 'schème:path/to/file', None, None)), ]) def test_roundtrips(self, bytes, url, parsed, split): if bytes: @@ -279,24 +310,24 @@ def test_roundtrips(self, bytes, url, parsed, split): @support.subTests('url,url2,parsed,split', [ ('///path/to/file', '/path/to/file', - ('', '', '/path/to/file', '', '', ''), - ('', '', '/path/to/file', '', '')), + (None, '', '/path/to/file', None, None, None), + (None, '', '/path/to/file', None, None)), ('scheme:///path/to/file', 'scheme:/path/to/file', - ('scheme', '', '/path/to/file', '', '', ''), - ('scheme', '', '/path/to/file', '', '')), + ('scheme', '', '/path/to/file', None, None, None), + ('scheme', '', '/path/to/file', None, None)), ('file:/tmp/junk.txt', 'file:///tmp/junk.txt', - ('file', '', '/tmp/junk.txt', '', '', ''), - ('file', '', '/tmp/junk.txt', '', '')), + ('file', None, '/tmp/junk.txt', None, None, None), + ('file', None, '/tmp/junk.txt', None, None)), ('http:/tmp/junk.txt', 'http:///tmp/junk.txt', - ('http', '', '/tmp/junk.txt', '', '', ''), - ('http', '', '/tmp/junk.txt', '', '')), + ('http', None, '/tmp/junk.txt', None, None, None), + ('http', None, '/tmp/junk.txt', None, None)), ('https:/tmp/junk.txt', 'https:///tmp/junk.txt', - ('https', '', '/tmp/junk.txt', '', '', ''), - ('https', '', '/tmp/junk.txt', '', '')), + ('https', None, '/tmp/junk.txt', None, None, None), + ('https', None, '/tmp/junk.txt', None, None)), ]) def test_roundtrips_normalization(self, bytes, url, url2, parsed, split): if bytes: @@ -310,17 +341,17 @@ def test_roundtrips_normalization(self, bytes, url, url2, parsed, split): @support.subTests('scheme', ('http', 'https')) @support.subTests('url,parsed,split', [ ('://www.python.org', - ('www.python.org', '', '', '', ''), - ('www.python.org', '', '', '')), + ('www.python.org', '', None, None, None), + ('www.python.org', '', None, None)), ('://www.python.org#abc', - ('www.python.org', '', '', '', 'abc'), - ('www.python.org', '', '', 'abc')), + ('www.python.org', '', None, None, 'abc'), + ('www.python.org', '', None, 'abc')), ('://www.python.org?q=abc', - ('www.python.org', '', '', 'q=abc', ''), - ('www.python.org', '', 'q=abc', '')), + ('www.python.org', '', None, 'q=abc', None), + ('www.python.org', '', 'q=abc', None)), ('://www.python.org/#abc', - ('www.python.org', '/', '', '', 'abc'), - ('www.python.org', '/', '', 'abc')), + ('www.python.org', '/', None, None, 'abc'), + ('www.python.org', '/', None, 'abc')), ('://a/b/c/d;p?q#f', ('a', '/b/c/d', 'p', 'q', 'f'), ('a', '/b/c/d;p', 'q', 'f')), @@ -342,16 +373,21 @@ def test_http_roundtrips(self, bytes, scheme, url, parsed, split): def checkJoin(self, base, relurl, expected, *, relroundtrip=True): with self.subTest(base=base, relurl=relurl): self.assertEqual(urllib.parse.urljoin(base, relurl), expected) - baseb = base.encode('ascii') - relurlb = relurl.encode('ascii') - expectedb = expected.encode('ascii') + baseb = str_encode(base) + relurlb = str_encode(relurl) + expectedb = str_encode(expected) self.assertEqual(urllib.parse.urljoin(baseb, relurlb), expectedb) if relroundtrip: - relurl = urllib.parse.urlunsplit(urllib.parse.urlsplit(relurl)) - self.assertEqual(urllib.parse.urljoin(base, relurl), expected) - relurlb = urllib.parse.urlunsplit(urllib.parse.urlsplit(relurlb)) - self.assertEqual(urllib.parse.urljoin(baseb, relurlb), expectedb) + relurl2 = urlunsplit(urlsplit(relurl)) + self.assertEqual(urllib.parse.urljoin(base, relurl2), expected) + relurlb2 = urlunsplit(urlsplit(relurlb)) + self.assertEqual(urllib.parse.urljoin(baseb, relurlb2), expectedb) + + relurl3 = urlunsplit(urlsplit(relurl, missing_as_none=True)) + self.assertEqual(urllib.parse.urljoin(base, relurl3), expected) + relurlb3 = urlunsplit(urlsplit(relurlb, missing_as_none=True)) + self.assertEqual(urllib.parse.urljoin(baseb, relurlb3), expectedb) @support.subTests('bytes', (False, True)) @support.subTests('u', ['Python', './Python','x-newscheme://foo.com/stuff','x://y','x:/y','x:/','/',]) @@ -387,7 +423,7 @@ def test_RFC1808(self): self.checkJoin(RFC1808_BASE, '../../g', 'http://a/g') # "abnormal" cases from RFC 1808: - self.checkJoin(RFC1808_BASE, '', 'http://a/b/c/d;p?q#f') + self.checkJoin(RFC1808_BASE, None, 'http://a/b/c/d;p?q#f') self.checkJoin(RFC1808_BASE, 'g.', 'http://a/b/c/g.') self.checkJoin(RFC1808_BASE, '.g', 'http://a/b/c/.g') self.checkJoin(RFC1808_BASE, 'g..', 'http://a/b/c/g..') @@ -411,8 +447,10 @@ def test_RFC1808(self): def test_RFC2368(self): # Issue 11467: path that starts with a number is not parsed correctly - self.assertEqual(urllib.parse.urlparse('mailto:1337@example.org'), + self.assertEqual(urlparse('mailto:1337@example.org'), ('mailto', '', '1337@example.org', '', '', '')) + self.assertEqual(urlparse('mailto:1337@example.org', missing_as_none=True), + ('mailto', None, '1337@example.org', None, None, None)) def test_RFC2396(self): # cases from RFC 2396 @@ -741,18 +779,18 @@ def test_RFC2732_invalid(self, bytes, invalid_url): @support.subTests('bytes', (False, True)) @support.subTests('url,defrag,frag', [ ('http://python.org#frag', 'http://python.org', 'frag'), - ('http://python.org', 'http://python.org', ''), + ('http://python.org', 'http://python.org', None), ('http://python.org/#frag', 'http://python.org/', 'frag'), - ('http://python.org/', 'http://python.org/', ''), + ('http://python.org/', 'http://python.org/', None), ('http://python.org/?q#frag', 'http://python.org/?q', 'frag'), - ('http://python.org/?q', 'http://python.org/?q', ''), + ('http://python.org/?q', 'http://python.org/?q', None), ('http://python.org/p#frag', 'http://python.org/p', 'frag'), - ('http://python.org/p?q', 'http://python.org/p?q', ''), + ('http://python.org/p?q', 'http://python.org/p?q', None), (RFC1808_BASE, 'http://a/b/c/d;p?q', 'f'), - (RFC2396_BASE, 'http://a/b/c/d;p?q', ''), + (RFC2396_BASE, 'http://a/b/c/d;p?q', None), ('http://a/b/c;p?q#f', 'http://a/b/c;p?q', 'f'), ('http://a/b/c;p?q#', 'http://a/b/c;p?q', ''), - ('http://a/b/c;p?q', 'http://a/b/c;p?q', ''), + ('http://a/b/c;p?q', 'http://a/b/c;p?q', None), ('http://a/b/c;p?#f', 'http://a/b/c;p?', 'f'), ('http://a/b/c;p#f', 'http://a/b/c;p', 'f'), ('http://a/b/c;?q#f', 'http://a/b/c;?q', 'f'), @@ -764,14 +802,19 @@ def test_RFC2732_invalid(self, bytes, invalid_url): ('//a/b/c;p?q#f', '//a/b/c;p?q', 'f'), ('://a/b/c;p?q#f', '://a/b/c;p?q', 'f'), ]) - def test_urldefrag(self, bytes, url, defrag, frag): + @support.subTests('missing_as_none', (False, True)) + def test_urldefrag(self, bytes, url, defrag, frag, missing_as_none): if bytes: url = str_encode(url) defrag = str_encode(defrag) frag = str_encode(frag) - result = urllib.parse.urldefrag(url) - hash = '#' if isinstance(url, str) else b'#' - self.assertEqual(result.geturl(), url.rstrip(hash)) + result = urllib.parse.urldefrag(url, missing_as_none=missing_as_none) + if not missing_as_none: + hash = '#' if isinstance(url, str) else b'#' + url = url.rstrip(hash) + if frag is None: + frag = url[:0] + self.assertEqual(result.geturl(), url) self.assertEqual(result, (defrag, frag)) self.assertEqual(result.url, defrag) self.assertEqual(result.fragment, frag) @@ -1001,26 +1044,27 @@ def test_attributes_bad_scheme(self, bytes, parse, scheme): if not url.isascii(): self.skipTest('non-ASCII bytes') url = url.encode("ascii") - p = parse(url) - self.assertEqual(p.scheme, b"" if bytes else "") + p = parse(url, missing_as_none=True) + self.assertIsNone(p.scheme) - def test_attributes_without_netloc(self): + @support.subTests('missing_as_none', (False, True)) + def test_attributes_without_netloc(self, missing_as_none): # This example is straight from RFC 3261. It looks like it # should allow the username, hostname, and port to be filled # in, but doesn't. Since it's a URI and doesn't use the # scheme://netloc syntax, the netloc and related attributes # should be left empty. uri = "sip:alice@atlanta.com;maddr=239.255.255.1;ttl=15" - p = urllib.parse.urlsplit(uri) - self.assertEqual(p.netloc, "") + p = urllib.parse.urlsplit(uri, missing_as_none=missing_as_none) + self.assertEqual(p.netloc, None if missing_as_none else "") self.assertEqual(p.username, None) self.assertEqual(p.password, None) self.assertEqual(p.hostname, None) self.assertEqual(p.port, None) self.assertEqual(p.geturl(), uri) - p = urllib.parse.urlparse(uri) - self.assertEqual(p.netloc, "") + p = urllib.parse.urlparse(uri, missing_as_none=missing_as_none) + self.assertEqual(p.netloc, None if missing_as_none else "") self.assertEqual(p.username, None) self.assertEqual(p.password, None) self.assertEqual(p.hostname, None) @@ -1029,16 +1073,16 @@ def test_attributes_without_netloc(self): # You guessed it, repeating the test with bytes input uri = b"sip:alice@atlanta.com;maddr=239.255.255.1;ttl=15" - p = urllib.parse.urlsplit(uri) - self.assertEqual(p.netloc, b"") + p = urllib.parse.urlsplit(uri, missing_as_none=missing_as_none) + self.assertEqual(p.netloc, None if missing_as_none else b"") self.assertEqual(p.username, None) self.assertEqual(p.password, None) self.assertEqual(p.hostname, None) self.assertEqual(p.port, None) self.assertEqual(p.geturl(), uri) - p = urllib.parse.urlparse(uri) - self.assertEqual(p.netloc, b"") + p = urllib.parse.urlparse(uri, missing_as_none=missing_as_none) + self.assertEqual(p.netloc, None if missing_as_none else b"") self.assertEqual(p.username, None) self.assertEqual(p.password, None) self.assertEqual(p.hostname, None) @@ -1052,67 +1096,86 @@ def test_noslash(self): self.assertEqual(urllib.parse.urlparse(b"http://example.com?blahblah=/foo"), (b'http', b'example.com', b'', b'', b'blahblah=/foo', b'')) - def test_withoutscheme(self): + @support.subTests('missing_as_none', (False, True)) + def test_withoutscheme(self, missing_as_none): # Test urlparse without scheme # Issue 754016: urlparse goes wrong with IP:port without scheme # RFC 1808 specifies that netloc should start with //, urlparse expects # the same, otherwise it classifies the portion of url as path. - self.assertEqual(urllib.parse.urlparse("path"), - ('','','path','','','')) - self.assertEqual(urllib.parse.urlparse("//www.python.org:80"), - ('','www.python.org:80','','','','')) - self.assertEqual(urllib.parse.urlparse("http://www.python.org:80"), - ('http','www.python.org:80','','','','')) + none = None if missing_as_none else '' + self.assertEqual(urlparse("path", missing_as_none=missing_as_none), + (none, none, 'path', none, none, none)) + self.assertEqual(urlparse("//www.python.org:80", missing_as_none=missing_as_none), + (none, 'www.python.org:80', '', none, none, none)) + self.assertEqual(urlparse("http://www.python.org:80", missing_as_none=missing_as_none), + ('http', 'www.python.org:80', '', none, none, none)) # Repeat for bytes input - self.assertEqual(urllib.parse.urlparse(b"path"), - (b'',b'',b'path',b'',b'',b'')) - self.assertEqual(urllib.parse.urlparse(b"//www.python.org:80"), - (b'',b'www.python.org:80',b'',b'',b'',b'')) - self.assertEqual(urllib.parse.urlparse(b"http://www.python.org:80"), - (b'http',b'www.python.org:80',b'',b'',b'',b'')) - - def test_portseparator(self): + none = None if missing_as_none else b'' + self.assertEqual(urlparse(b"path", missing_as_none=missing_as_none), + (none, none, b'path', none, none, none)) + self.assertEqual(urlparse(b"//www.python.org:80", missing_as_none=missing_as_none), + (none, b'www.python.org:80', b'', none, none, none)) + self.assertEqual(urlparse(b"http://www.python.org:80", missing_as_none=missing_as_none), + (b'http', b'www.python.org:80', b'', none, none, none)) + + @support.subTests('missing_as_none', (False, True)) + def test_portseparator(self, missing_as_none): # Issue 754016 makes changes for port separator ':' from scheme separator - self.assertEqual(urllib.parse.urlparse("http:80"), ('http','','80','','','')) - self.assertEqual(urllib.parse.urlparse("https:80"), ('https','','80','','','')) - self.assertEqual(urllib.parse.urlparse("path:80"), ('path','','80','','','')) - self.assertEqual(urllib.parse.urlparse("http:"),('http','','','','','')) - self.assertEqual(urllib.parse.urlparse("https:"),('https','','','','','')) - self.assertEqual(urllib.parse.urlparse("http://www.python.org:80"), - ('http','www.python.org:80','','','','')) + none = None if missing_as_none else '' + self.assertEqual(urlparse("http:80", missing_as_none=missing_as_none), + ('http', none, '80', none, none, none)) + self.assertEqual(urlparse("https:80", missing_as_none=missing_as_none), + ('https', none, '80', none, none, none)) + self.assertEqual(urlparse("path:80", missing_as_none=missing_as_none), + ('path', none, '80', none, none, none)) + self.assertEqual(urlparse("http:", missing_as_none=missing_as_none), + ('http', none, '', none, none, none)) + self.assertEqual(urlparse("https:", missing_as_none=missing_as_none), + ('https', none, '', none, none, none)) + self.assertEqual(urlparse("http://www.python.org:80", missing_as_none=missing_as_none), + ('http', 'www.python.org:80', '', none, none, none)) # As usual, need to check bytes input as well - self.assertEqual(urllib.parse.urlparse(b"http:80"), (b'http',b'',b'80',b'',b'',b'')) - self.assertEqual(urllib.parse.urlparse(b"https:80"), (b'https',b'',b'80',b'',b'',b'')) - self.assertEqual(urllib.parse.urlparse(b"path:80"), (b'path',b'',b'80',b'',b'',b'')) - self.assertEqual(urllib.parse.urlparse(b"http:"),(b'http',b'',b'',b'',b'',b'')) - self.assertEqual(urllib.parse.urlparse(b"https:"),(b'https',b'',b'',b'',b'',b'')) - self.assertEqual(urllib.parse.urlparse(b"http://www.python.org:80"), - (b'http',b'www.python.org:80',b'',b'',b'',b'')) + none = None if missing_as_none else b'' + self.assertEqual(urlparse(b"http:80", missing_as_none=missing_as_none), + (b'http', none, b'80', none, none, none)) + self.assertEqual(urlparse(b"https:80", missing_as_none=missing_as_none), + (b'https', none, b'80', none, none, none)) + self.assertEqual(urlparse(b"path:80", missing_as_none=missing_as_none), + (b'path', none, b'80', none, none, none)) + self.assertEqual(urlparse(b"http:", missing_as_none=missing_as_none), + (b'http', none, b'', none, none, none)) + self.assertEqual(urlparse(b"https:", missing_as_none=missing_as_none), + (b'https', none, b'', none, none, none)) + self.assertEqual(urlparse(b"http://www.python.org:80", missing_as_none=missing_as_none), + (b'http', b'www.python.org:80', b'', none, none, none)) def test_usingsys(self): # Issue 3314: sys module is used in the error self.assertRaises(TypeError, urllib.parse.urlencode, "foo") - def test_anyscheme(self): + @support.subTests('missing_as_none', (False, True)) + def test_anyscheme(self, missing_as_none): # Issue 7904: s3://foo.com/stuff has netloc "foo.com". - self.assertEqual(urllib.parse.urlparse("s3://foo.com/stuff"), - ('s3', 'foo.com', '/stuff', '', '', '')) - self.assertEqual(urllib.parse.urlparse("x-newscheme://foo.com/stuff"), - ('x-newscheme', 'foo.com', '/stuff', '', '', '')) - self.assertEqual(urllib.parse.urlparse("x-newscheme://foo.com/stuff?query#fragment"), - ('x-newscheme', 'foo.com', '/stuff', '', 'query', 'fragment')) - self.assertEqual(urllib.parse.urlparse("x-newscheme://foo.com/stuff?query"), - ('x-newscheme', 'foo.com', '/stuff', '', 'query', '')) + none = None if missing_as_none else '' + self.assertEqual(urlparse("s3://foo.com/stuff", missing_as_none=missing_as_none), + ('s3', 'foo.com', '/stuff', none, none, none)) + self.assertEqual(urlparse("x-newscheme://foo.com/stuff", missing_as_none=missing_as_none), + ('x-newscheme', 'foo.com', '/stuff', none, none, none)) + self.assertEqual(urlparse("x-newscheme://foo.com/stuff?query#fragment", missing_as_none=missing_as_none), + ('x-newscheme', 'foo.com', '/stuff', none, 'query', 'fragment')) + self.assertEqual(urlparse("x-newscheme://foo.com/stuff?query", missing_as_none=missing_as_none), + ('x-newscheme', 'foo.com', '/stuff', none, 'query', none)) # And for bytes... - self.assertEqual(urllib.parse.urlparse(b"s3://foo.com/stuff"), - (b's3', b'foo.com', b'/stuff', b'', b'', b'')) - self.assertEqual(urllib.parse.urlparse(b"x-newscheme://foo.com/stuff"), - (b'x-newscheme', b'foo.com', b'/stuff', b'', b'', b'')) - self.assertEqual(urllib.parse.urlparse(b"x-newscheme://foo.com/stuff?query#fragment"), - (b'x-newscheme', b'foo.com', b'/stuff', b'', b'query', b'fragment')) - self.assertEqual(urllib.parse.urlparse(b"x-newscheme://foo.com/stuff?query"), - (b'x-newscheme', b'foo.com', b'/stuff', b'', b'query', b'')) + none = None if missing_as_none else b'' + self.assertEqual(urlparse(b"s3://foo.com/stuff", missing_as_none=missing_as_none), + (b's3', b'foo.com', b'/stuff', none, none, none)) + self.assertEqual(urlparse(b"x-newscheme://foo.com/stuff", missing_as_none=missing_as_none), + (b'x-newscheme', b'foo.com', b'/stuff', none, none, none)) + self.assertEqual(urlparse(b"x-newscheme://foo.com/stuff?query#fragment", missing_as_none=missing_as_none), + (b'x-newscheme', b'foo.com', b'/stuff', none, b'query', b'fragment')) + self.assertEqual(urlparse(b"x-newscheme://foo.com/stuff?query", missing_as_none=missing_as_none), + (b'x-newscheme', b'foo.com', b'/stuff', none, b'query', none)) @support.subTests('func', (urllib.parse.urlparse, urllib.parse.urlsplit)) def test_default_scheme(self, func): @@ -1125,8 +1188,11 @@ def test_default_scheme(self, func): self.assertEqual(func("path", scheme="ftp").scheme, "ftp") self.assertEqual(func(b"path", scheme=b"ftp").scheme, b"ftp") self.assertEqual(func("path").scheme, "") + self.assertEqual(func("path", missing_as_none=True).scheme, None) self.assertEqual(func(b"path").scheme, b"") + self.assertEqual(func(b"path", missing_as_none=True).scheme, None) self.assertEqual(func(b"path", "").scheme, b"") + self.assertEqual(func(b"path", "", missing_as_none=True).scheme, b"") @support.subTests('url,attr,expected_frag', ( ("http:#frag", "path", "frag"), @@ -1151,9 +1217,16 @@ def test_parse_fragments(self, url, attr, expected_frag, func): "#" + expected_frag) self.assertEqual(func(url, "", False).fragment, "") + result = func(url, allow_fragments=False, missing_as_none=True) + self.assertIsNone(result.fragment) + self.assertTrue( + getattr(result, attr).endswith("#" + expected_frag)) + self.assertIsNone(func(url, "", False, missing_as_none=True).fragment) + result = func(url, allow_fragments=True) self.assertEqual(result.fragment, expected_frag) - self.assertNotEndsWith(getattr(result, attr), expected_frag) + self.assertFalse( + getattr(result, attr).endswith(expected_frag)) self.assertEqual(func(url, "", True).fragment, expected_frag) self.assertEqual(func(url).fragment, expected_frag) @@ -1182,19 +1255,10 @@ def test_mixed_types_rejected(self): with self.assertRaisesRegex(TypeError, "Cannot mix str"): urllib.parse.urljoin(b"http://python.org", "http://python.org") - @support.subTests('result_type', [ - urllib.parse.DefragResult, - urllib.parse.SplitResult, - urllib.parse.ParseResult, - ]) - def test_result_pairs(self, result_type): - # Check encoding and decoding between result pairs - str_type = result_type - num_args = len(str_type._fields) + def _check_result_type(self, str_type, str_args): bytes_type = str_type._encoded_counterpart self.assertIs(bytes_type._decoded_counterpart, str_type) - str_args = ('',) * num_args - bytes_args = (b'',) * num_args + bytes_args = tuple_encode(str_args) str_result = str_type(*str_args) bytes_result = bytes_type(*bytes_args) encoding = 'ascii' @@ -1213,6 +1277,169 @@ def test_result_pairs(self, result_type): self.assertEqual(str_result.encode(encoding), bytes_result) self.assertEqual(str_result.encode(encoding, errors), bytes_args) self.assertEqual(str_result.encode(encoding, errors), bytes_result) + for result in str_result, bytes_result: + self.assertEqual(copy.copy(result), result) + self.assertEqual(copy.deepcopy(result), result) + self.assertEqual(copy.replace(result), result) + self.assertEqual(result._replace(), result) + + def test_result_pairs__(self): + # Check encoding and decoding between result pairs + self._check_result_type(urllib.parse.DefragResult, ('', '')) + self._check_result_type(urllib.parse.DefragResult, ('', None)) + self._check_result_type(urllib.parse.SplitResult, ('', '', '', '', '')) + self._check_result_type(urllib.parse.SplitResult, (None, None, '', None, None)) + self._check_result_type(urllib.parse.ParseResult, ('', '', '', '', '', '')) + self._check_result_type(urllib.parse.ParseResult, (None, None, '', None, None, None)) + + def test_result_encoding_decoding(self): + def check(str_result, bytes_result): + self.assertEqual(str_result.encode(), bytes_result) + self.assertEqual(str_result.encode().geturl(), bytes_result.geturl()) + self.assertEqual(bytes_result.decode(), str_result) + self.assertEqual(bytes_result.decode().geturl(), str_result.geturl()) + + url = 'http://example.com/?#' + burl = url.encode() + for func in urldefrag, urlsplit, urlparse: + check(func(url, missing_as_none=True), func(burl, missing_as_none=True)) + check(func(url), func(burl)) + + def test_result_copying(self): + def check(result): + result2 = copy.copy(result) + self.assertEqual(result2, result) + self.assertEqual(result2.geturl(), result.geturl()) + result2 = copy.deepcopy(result) + self.assertEqual(result2, result) + self.assertEqual(result2.geturl(), result.geturl()) + result2 = copy.replace(result) + self.assertEqual(result2, result) + self.assertEqual(result2.geturl(), result.geturl()) + result2 = result._replace() + self.assertEqual(result2, result) + self.assertEqual(result2.geturl(), result.geturl()) + + url = 'http://example.com/?#' + burl = url.encode() + for func in urldefrag, urlsplit, urlparse: + check(func(url)) + check(func(url, missing_as_none=True)) + check(func(burl)) + check(func(burl, missing_as_none=True)) + + def test_result_pickling(self): + def check(result): + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + pickled = pickle.dumps(result, proto) + result2 = pickle.loads(pickled) + self.assertEqual(result2, result) + self.assertEqual(result2.geturl(), result.geturl()) + + url = 'http://example.com/?#' + burl = url.encode() + for func in urldefrag, urlsplit, urlparse: + check(func(url)) + check(func(url, missing_as_none=True)) + check(func(burl)) + check(func(burl, missing_as_none=True)) + + def test_result_compat_unpickling(self): + def check(result, pickles): + for pickled in pickles: + result2 = pickle.loads(pickled) + self.assertEqual(result2, result) + self.assertEqual(result2.geturl(), result.geturl()) + + url = 'http://example.com/?#' + burl = url.encode() + # Pre-3.15 data. + check(urldefrag(url), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nDefragResult\nc__builtin__\ntuple\n(Vhttp://example.com/?\nV\nttR.', + b'ccopy_reg\n_reconstructor\n(curlparse\nDefragResult\nc__builtin__\ntuple\n(X\x14\x00\x00\x00http://example.com/?X\x00\x00\x00\x00ttR.', + b'\x80\x02curlparse\nDefragResult\nX\x14\x00\x00\x00http://example.com/?X\x00\x00\x00\x00\x86\x81.', + b'\x80\x03curllib.parse\nDefragResult\nX\x14\x00\x00\x00http://example.com/?X\x00\x00\x00\x00\x86\x81.', + b'\x80\x04\x958\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x0cDefragResult\x93\x8c\x14http://example.com/?\x8c\x00\x86\x81.', + )) + check(urldefrag(burl), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nDefragResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\n(Vhttp://example.com/?\nVlatin1\ntRc__builtin__\nbytes\n(tRttR.', + b'ccopy_reg\n_reconstructor\n(curlparse\nDefragResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\n(X\x14\x00\x00\x00http://example.com/?X\x06\x00\x00\x00latin1tRc__builtin__\nbytes\n)RttR.', + b'\x80\x02curlparse\nDefragResultBytes\nc_codecs\nencode\nX\x14\x00\x00\x00http://example.com/?X\x06\x00\x00\x00latin1\x86Rc__builtin__\nbytes\n)R\x86\x81.', + b'\x80\x03curllib.parse\nDefragResultBytes\nC\x14http://example.com/?C\x00\x86\x81.', + b'\x80\x04\x95=\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x11DefragResultBytes\x93C\x14http://example.com/?C\x00\x86\x81.', + )) + check(urlsplit(url), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nSplitResult\nc__builtin__\ntuple\n(Vhttp\nVexample.com\nV/\nV\np0\ng0\nttR.', + b'ccopy_reg\n_reconstructor\n(curlparse\nSplitResult\nc__builtin__\ntuple\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/X\x00\x00\x00\x00q\x00h\x00ttR.', + b'\x80\x02curlparse\nSplitResult\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/X\x00\x00\x00\x00q\x00h\x00t\x81.', + b'\x80\x03curllib.parse\nSplitResult\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/X\x00\x00\x00\x00q\x00h\x00t\x81.', + b'\x80\x04\x95;\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x0bSplitResult\x93(\x8c\x04http\x8c\x0bexample.com\x8c\x01/\x8c\x00\x94h\x00t\x81.', + )) + check(urlsplit(burl), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nSplitResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\np0\n(Vhttp\nVlatin1\np1\ntRg0\n(Vexample.com\ng1\ntRg0\n(V/\ng1\ntRc__builtin__\nbytes\n(tRp2\ng2\nttR.', + b'ccopy_reg\n_reconstructor\n(curlparse\nSplitResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\nq\x00(X\x04\x00\x00\x00httpX\x06\x00\x00\x00latin1q\x01tRh\x00(X\x0b\x00\x00\x00example.comh\x01tRh\x00(X\x01\x00\x00\x00/h\x01tRc__builtin__\nbytes\n)Rq\x02h\x02ttR.', + b'\x80\x02curlparse\nSplitResultBytes\n(c_codecs\nencode\nq\x00X\x04\x00\x00\x00httpX\x06\x00\x00\x00latin1q\x01\x86Rh\x00X\x0b\x00\x00\x00example.comh\x01\x86Rh\x00X\x01\x00\x00\x00/h\x01\x86Rc__builtin__\nbytes\n)Rq\x02h\x02t\x81.', + b'\x80\x03curllib.parse\nSplitResultBytes\n(C\x04httpC\x0bexample.comC\x01/C\x00q\x00h\x00t\x81.', + b'\x80\x04\x95@\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x10SplitResultBytes\x93(C\x04httpC\x0bexample.comC\x01/C\x00\x94h\x00t\x81.', + )) + check(urlparse(url), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nParseResult\nc__builtin__\ntuple\n(Vhttp\nVexample.com\nV/\nV\np0\ng0\ng0\nttR.', + b'ccopy_reg\n_reconstructor\n(curlparse\nParseResult\nc__builtin__\ntuple\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/X\x00\x00\x00\x00q\x00h\x00h\x00ttR.', + b'\x80\x02curlparse\nParseResult\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/X\x00\x00\x00\x00q\x00h\x00h\x00t\x81.', + b'\x80\x03curllib.parse\nParseResult\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/X\x00\x00\x00\x00q\x00h\x00h\x00t\x81.', + b'\x80\x04\x95=\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x0bParseResult\x93(\x8c\x04http\x8c\x0bexample.com\x8c\x01/\x8c\x00\x94h\x00h\x00t\x81.', + )) + check(urlparse(burl), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nParseResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\np0\n(Vhttp\nVlatin1\np1\ntRg0\n(Vexample.com\ng1\ntRg0\n(V/\ng1\ntRc__builtin__\nbytes\n(tRp2\ng2\ng2\nttR.', + b'ccopy_reg\n_reconstructor\n(curlparse\nParseResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\nq\x00(X\x04\x00\x00\x00httpX\x06\x00\x00\x00latin1q\x01tRh\x00(X\x0b\x00\x00\x00example.comh\x01tRh\x00(X\x01\x00\x00\x00/h\x01tRc__builtin__\nbytes\n)Rq\x02h\x02h\x02ttR.', + b'\x80\x02curlparse\nParseResultBytes\n(c_codecs\nencode\nq\x00X\x04\x00\x00\x00httpX\x06\x00\x00\x00latin1q\x01\x86Rh\x00X\x0b\x00\x00\x00example.comh\x01\x86Rh\x00X\x01\x00\x00\x00/h\x01\x86Rc__builtin__\nbytes\n)Rq\x02h\x02h\x02t\x81.', + b'\x80\x03curllib.parse\nParseResultBytes\n(C\x04httpC\x0bexample.comC\x01/C\x00q\x00h\x00h\x00t\x81.', + b'\x80\x04\x95B\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x10ParseResultBytes\x93(C\x04httpC\x0bexample.comC\x01/C\x00\x94h\x00h\x00t\x81.', + )) + + # 3.15 data with missing_as_none=True. + check(urldefrag(url, missing_as_none=True), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nDefragResult\nc__builtin__\ntuple\n(Vhttp://example.com/?\nV\nttR(N(dV_keep_empty\nI01\nstb.', + b'ccopy_reg\n_reconstructor\n(curlparse\nDefragResult\nc__builtin__\ntuple\n(X\x14\x00\x00\x00http://example.com/?X\x00\x00\x00\x00ttR(N}X\x0b\x00\x00\x00_keep_emptyI01\nstb.', + b'\x80\x02curlparse\nDefragResult\nX\x14\x00\x00\x00http://example.com/?X\x00\x00\x00\x00\x86\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x03curllib.parse\nDefragResult\nX\x14\x00\x00\x00http://example.com/?X\x00\x00\x00\x00\x86\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x04\x95K\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x0cDefragResult\x93\x8c\x14http://example.com/?\x8c\x00\x86\x81N}\x8c\x0b_keep_empty\x88s\x86b.', + )) + check(urldefrag(burl, missing_as_none=True), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nDefragResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\n(Vhttp://example.com/?\nVlatin1\ntRc__builtin__\nbytes\n(tRttR(N(dV_keep_empty\nI01\nstb.', + b'ccopy_reg\n_reconstructor\n(curlparse\nDefragResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\n(X\x14\x00\x00\x00http://example.com/?X\x06\x00\x00\x00latin1tRc__builtin__\nbytes\n)RttR(N}X\x0b\x00\x00\x00_keep_emptyI01\nstb.', + b'\x80\x02curlparse\nDefragResultBytes\nc_codecs\nencode\nX\x14\x00\x00\x00http://example.com/?X\x06\x00\x00\x00latin1\x86Rc__builtin__\nbytes\n)R\x86\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x03curllib.parse\nDefragResultBytes\nC\x14http://example.com/?C\x00\x86\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x04\x95P\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x11DefragResultBytes\x93C\x14http://example.com/?C\x00\x86\x81N}\x8c\x0b_keep_empty\x88s\x86b.', + )) + check(urlsplit(url, missing_as_none=True), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nSplitResult\nc__builtin__\ntuple\n(Vhttp\nVexample.com\nV/\nV\np0\ng0\nttR(N(dV_keep_empty\nI01\nstb.', + b'ccopy_reg\n_reconstructor\n(curlparse\nSplitResult\nc__builtin__\ntuple\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/X\x00\x00\x00\x00q\x00h\x00ttR(N}X\x0b\x00\x00\x00_keep_emptyI01\nstb.', + b'\x80\x02curlparse\nSplitResult\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/X\x00\x00\x00\x00q\x00h\x00t\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x03curllib.parse\nSplitResult\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/X\x00\x00\x00\x00q\x00h\x00t\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x04\x95N\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x0bSplitResult\x93(\x8c\x04http\x8c\x0bexample.com\x8c\x01/\x8c\x00\x94h\x00t\x81N}\x8c\x0b_keep_empty\x88s\x86b.', + )) + check(urlsplit(burl, missing_as_none=True), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nSplitResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\np0\n(Vhttp\nVlatin1\np1\ntRg0\n(Vexample.com\ng1\ntRg0\n(V/\ng1\ntRc__builtin__\nbytes\n(tRp2\ng2\nttR(N(dV_keep_empty\nI01\nstb.', + b'ccopy_reg\n_reconstructor\n(curlparse\nSplitResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\nq\x00(X\x04\x00\x00\x00httpX\x06\x00\x00\x00latin1q\x01tRh\x00(X\x0b\x00\x00\x00example.comh\x01tRh\x00(X\x01\x00\x00\x00/h\x01tRc__builtin__\nbytes\n)Rq\x02h\x02ttR(N}X\x0b\x00\x00\x00_keep_emptyI01\nstb.', + b'\x80\x02curlparse\nSplitResultBytes\n(c_codecs\nencode\nq\x00X\x04\x00\x00\x00httpX\x06\x00\x00\x00latin1q\x01\x86Rh\x00X\x0b\x00\x00\x00example.comh\x01\x86Rh\x00X\x01\x00\x00\x00/h\x01\x86Rc__builtin__\nbytes\n)Rq\x02h\x02t\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x03curllib.parse\nSplitResultBytes\n(C\x04httpC\x0bexample.comC\x01/C\x00q\x00h\x00t\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x04\x95S\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x10SplitResultBytes\x93(C\x04httpC\x0bexample.comC\x01/C\x00\x94h\x00t\x81N}\x8c\x0b_keep_empty\x88s\x86b.', + )) + check(urlparse(url, missing_as_none=True), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nParseResult\nc__builtin__\ntuple\n(Vhttp\nVexample.com\nV/\nNV\np0\ng0\nttR(N(dV_keep_empty\nI01\nstb.', + b'ccopy_reg\n_reconstructor\n(curlparse\nParseResult\nc__builtin__\ntuple\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/NX\x00\x00\x00\x00q\x00h\x00ttR(N}X\x0b\x00\x00\x00_keep_emptyI01\nstb.', + b'\x80\x02curlparse\nParseResult\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/NX\x00\x00\x00\x00q\x00h\x00t\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x03curllib.parse\nParseResult\n(X\x04\x00\x00\x00httpX\x0b\x00\x00\x00example.comX\x01\x00\x00\x00/NX\x00\x00\x00\x00q\x00h\x00t\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x04\x95O\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x0bParseResult\x93(\x8c\x04http\x8c\x0bexample.com\x8c\x01/N\x8c\x00\x94h\x00t\x81N}\x8c\x0b_keep_empty\x88s\x86b.', + )) + check(urlparse(burl, missing_as_none=True), ( + b'ccopy_reg\n_reconstructor\n(curlparse\nParseResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\np0\n(Vhttp\nVlatin1\np1\ntRg0\n(Vexample.com\ng1\ntRg0\n(V/\ng1\ntRNc__builtin__\nbytes\n(tRp2\ng2\nttR(N(dV_keep_empty\nI01\nstb.', + b'ccopy_reg\n_reconstructor\n(curlparse\nParseResultBytes\nc__builtin__\ntuple\n(c_codecs\nencode\nq\x00(X\x04\x00\x00\x00httpX\x06\x00\x00\x00latin1q\x01tRh\x00(X\x0b\x00\x00\x00example.comh\x01tRh\x00(X\x01\x00\x00\x00/h\x01tRNc__builtin__\nbytes\n)Rq\x02h\x02ttR(N}X\x0b\x00\x00\x00_keep_emptyI01\nstb.', + b'\x80\x02curlparse\nParseResultBytes\n(c_codecs\nencode\nq\x00X\x04\x00\x00\x00httpX\x06\x00\x00\x00latin1q\x01\x86Rh\x00X\x0b\x00\x00\x00example.comh\x01\x86Rh\x00X\x01\x00\x00\x00/h\x01\x86RNc__builtin__\nbytes\n)Rq\x02h\x02t\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x03curllib.parse\nParseResultBytes\n(C\x04httpC\x0bexample.comC\x01/NC\x00q\x00h\x00t\x81N}X\x0b\x00\x00\x00_keep_empty\x88s\x86b.', + b'\x80\x04\x95T\x00\x00\x00\x00\x00\x00\x00\x8c\x0curllib.parse\x8c\x10ParseResultBytes\x93(C\x04httpC\x0bexample.comC\x01/NC\x00\x94h\x00t\x81N}\x8c\x0b_keep_empty\x88s\x86b.', + )) def test_parse_qs_encoding(self): result = urllib.parse.parse_qs("key=\u0141%E9", encoding="latin-1") @@ -1457,6 +1684,11 @@ def test_telurl_params(self): self.assertEqual(p1.path, '+1-201-555-0123') self.assertEqual(p1.params, '') + p1 = urllib.parse.urlparse('tel:+1-201-555-0123', missing_as_none=True) + self.assertEqual(p1.scheme, 'tel') + self.assertEqual(p1.path, '+1-201-555-0123') + self.assertEqual(p1.params, None) + p1 = urllib.parse.urlparse('tel:7042;phone-context=example.com') self.assertEqual(p1.scheme, 'tel') self.assertEqual(p1.path, '7042') @@ -1684,63 +1916,63 @@ def test_splittype_deprecation(self): urllib.parse.splittype('') self.assertEqual(str(cm.warning), 'urllib.parse.splittype() is deprecated as of 3.8, ' - 'use urllib.parse.urlparse() instead') + 'use urllib.parse.urlsplit() instead') def test_splithost_deprecation(self): with self.assertWarns(DeprecationWarning) as cm: urllib.parse.splithost('') self.assertEqual(str(cm.warning), 'urllib.parse.splithost() is deprecated as of 3.8, ' - 'use urllib.parse.urlparse() instead') + 'use urllib.parse.urlsplit() instead') def test_splituser_deprecation(self): with self.assertWarns(DeprecationWarning) as cm: urllib.parse.splituser('') self.assertEqual(str(cm.warning), 'urllib.parse.splituser() is deprecated as of 3.8, ' - 'use urllib.parse.urlparse() instead') + 'use urllib.parse.urlsplit() instead') def test_splitpasswd_deprecation(self): with self.assertWarns(DeprecationWarning) as cm: urllib.parse.splitpasswd('') self.assertEqual(str(cm.warning), 'urllib.parse.splitpasswd() is deprecated as of 3.8, ' - 'use urllib.parse.urlparse() instead') + 'use urllib.parse.urlsplit() instead') def test_splitport_deprecation(self): with self.assertWarns(DeprecationWarning) as cm: urllib.parse.splitport('') self.assertEqual(str(cm.warning), 'urllib.parse.splitport() is deprecated as of 3.8, ' - 'use urllib.parse.urlparse() instead') + 'use urllib.parse.urlsplit() instead') def test_splitnport_deprecation(self): with self.assertWarns(DeprecationWarning) as cm: urllib.parse.splitnport('') self.assertEqual(str(cm.warning), 'urllib.parse.splitnport() is deprecated as of 3.8, ' - 'use urllib.parse.urlparse() instead') + 'use urllib.parse.urlsplit() instead') def test_splitquery_deprecation(self): with self.assertWarns(DeprecationWarning) as cm: urllib.parse.splitquery('') self.assertEqual(str(cm.warning), 'urllib.parse.splitquery() is deprecated as of 3.8, ' - 'use urllib.parse.urlparse() instead') + 'use urllib.parse.urlsplit() instead') def test_splittag_deprecation(self): with self.assertWarns(DeprecationWarning) as cm: urllib.parse.splittag('') self.assertEqual(str(cm.warning), 'urllib.parse.splittag() is deprecated as of 3.8, ' - 'use urllib.parse.urlparse() instead') + 'use urllib.parse.urlsplit() instead') def test_splitattr_deprecation(self): with self.assertWarns(DeprecationWarning) as cm: urllib.parse.splitattr('') self.assertEqual(str(cm.warning), 'urllib.parse.splitattr() is deprecated as of 3.8, ' - 'use urllib.parse.urlparse() instead') + 'use urllib.parse.urlsplit() instead') def test_splitvalue_deprecation(self): with self.assertWarns(DeprecationWarning) as cm: @@ -1757,6 +1989,8 @@ def test_to_bytes_deprecation(self): def str_encode(s): + if s is None: + return None return s.encode('ascii') def tuple_encode(t): diff --git a/Lib/test/test_userdict.py b/Lib/test/test_userdict.py index 75de9ea252de98..c60135ca5a12a8 100644 --- a/Lib/test/test_userdict.py +++ b/Lib/test/test_userdict.py @@ -1,8 +1,18 @@ # Check every path through every method of UserDict +from collections import UserDict from test import mapping_tests import unittest import collections +import types + + +class UserDictSubclass(UserDict): + pass + +class UserDictSubclass2(UserDict): + pass + d0 = {} d1 = {"one": 1} @@ -155,6 +165,25 @@ def test_init(self): self.assertRaises(TypeError, collections.UserDict, (), ()) self.assertRaises(TypeError, collections.UserDict.__init__) + def test_data(self): + u = UserDict() + self.assertEqual(u.data, {}) + self.assertIs(type(u.data), dict) + d = {'a': 1, 'b': 2} + u = UserDict(d) + self.assertEqual(u.data, d) + self.assertIsNot(u.data, d) + self.assertIs(type(u.data), dict) + u = UserDict(u) + self.assertEqual(u.data, d) + self.assertIs(type(u.data), dict) + u = UserDict([('a', 1), ('b', 2)]) + self.assertEqual(u.data, d) + self.assertIs(type(u.data), dict) + u = UserDict(a=1, b=2) + self.assertEqual(u.data, d) + self.assertIs(type(u.data), dict) + def test_update(self): for kw in 'self', 'dict', 'other', 'iterable': d = collections.UserDict() @@ -215,6 +244,69 @@ class G(collections.UserDict): test_repr_deep = mapping_tests.TestHashMappingProtocol.test_repr_deep + def test_mixed_or(self): + for t in UserDict, dict, frozendict, types.MappingProxyType: + with self.subTest(t.__name__): + u = UserDict({0: 'a', 1: 'b'}) | t({1: 'c', 2: 'd'}) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDict) + + u = t({0: 'a', 1: 'b'}) | UserDict({1: 'c', 2: 'd'}) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDict) + + u = UserDict({0: 'a', 1: 'b'}) | UserDictSubclass({1: 'c', 2: 'd'}) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDict) + + u = UserDictSubclass({0: 'a', 1: 'b'}) | UserDict({1: 'c', 2: 'd'}) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDictSubclass) + + u = UserDictSubclass({0: 'a', 1: 'b'}) | UserDictSubclass2({1: 'c', 2: 'd'}) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDictSubclass) + + u = UserDict({1: 'c', 2: 'd'}).__ror__(UserDict({0: 'a', 1: 'b'})) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDict) + + u = UserDictSubclass({1: 'c', 2: 'd'}).__ror__(UserDictSubclass2({0: 'a', 1: 'b'})) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDictSubclass) + + def test_mixed_ior(self): + for t in UserDict, dict, frozendict, types.MappingProxyType: + with self.subTest(t.__name__): + u = u2 = UserDict({0: 'a', 1: 'b'}) + u |= t({1: 'c', 2: 'd'}) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDict) + self.assertIs(u, u2) + + u = dict({0: 'a', 1: 'b'}) + u |= UserDict({1: 'c', 2: 'd'}) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), dict) + + u = u2 = UserDict({0: 'a', 1: 'b'}) + u |= UserDictSubclass({1: 'c', 2: 'd'}) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDict) + self.assertIs(u, u2) + + u = u2 = UserDictSubclass({0: 'a', 1: 'b'}) + u |= UserDict({1: 'c', 2: 'd'}) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDictSubclass) + self.assertIs(u, u2) + + u = u2 = UserDictSubclass({0: 'a', 1: 'b'}) + u |= UserDictSubclass2({1: 'c', 2: 'd'}) + self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'}) + self.assertIs(type(u), UserDictSubclass) + self.assertIs(u, u2) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_userlist.py b/Lib/test/test_userlist.py index d3d9f4cff8da3a..3e5c5ff19458eb 100644 --- a/Lib/test/test_userlist.py +++ b/Lib/test/test_userlist.py @@ -2,12 +2,36 @@ from collections import UserList from test import list_tests +from test import support import unittest +class UserListSubclass(UserList): + pass + +class UserListSubclass2(UserList): + pass + + class UserListTest(list_tests.CommonTest): type2test = UserList + def test_data(self): + u = UserList() + self.assertEqual(u.data, []) + self.assertIs(type(u.data), list) + a = [1, 2] + u = UserList(a) + self.assertEqual(u.data, a) + self.assertIsNot(u.data, a) + self.assertIs(type(u.data), list) + u = UserList(u) + self.assertEqual(u.data, a) + self.assertIs(type(u.data), list) + u = UserList("spam") + self.assertEqual(u.data, list("spam")) + self.assertIs(type(u.data), list) + def test_getslice(self): super().test_getslice() l = [0, 1, 2, 3, 4] @@ -24,34 +48,74 @@ def test_slice_type(self): self.assertIsInstance(u[:], u.__class__) self.assertEqual(u[:],u) - def test_add_specials(self): - u = UserList("spam") - u2 = u + "eggs" - self.assertEqual(u2, list("spameggs")) + def test_mixed_add(self): + for t in UserList, list, str, tuple, iter: + with self.subTest(t.__name__): + u = UserList("spam") + t("eggs") + self.assertEqual(u, list("spameggs")) + self.assertIs(type(u), UserList) + + u = t("spam") + UserList("eggs") + self.assertEqual(u, list("spameggs")) + self.assertIs(type(u), UserList) + + u = UserList("spam") + UserListSubclass("eggs") + self.assertEqual(u, list("spameggs")) + self.assertIs(type(u), UserList) - def test_radd_specials(self): - u = UserList("eggs") - u2 = "spam" + u + u = UserListSubclass("spam") + UserList("eggs") + self.assertEqual(u, list("spameggs")) + self.assertIs(type(u), UserListSubclass) + + u = UserListSubclass("spam") + UserListSubclass2("eggs") + self.assertEqual(u, list("spameggs")) + self.assertIs(type(u), UserListSubclass) + + u2 = UserList("eggs").__radd__(UserList("spam")) self.assertEqual(u2, list("spameggs")) - u2 = u.__radd__(UserList("spam")) + self.assertIs(type(u), UserListSubclass) + + u2 = UserListSubclass("eggs").__radd__(UserListSubclass2("spam")) self.assertEqual(u2, list("spameggs")) + self.assertIs(type(u), UserListSubclass) - def test_iadd(self): - super().test_iadd() - u = [0, 1] - u += UserList([0, 1]) - self.assertEqual(u, [0, 1, 0, 1]) + def test_mixed_iadd(self): + for t in UserList, list, str, tuple, iter: + with self.subTest(t.__name__): + u = u2 = UserList("spam") + u += t("eggs") + self.assertEqual(u, list("spameggs")) + self.assertIs(type(u), UserList) + self.assertIs(u, u2) - def test_mixedcmp(self): - u = self.type2test([0, 1]) - self.assertEqual(u, [0, 1]) - self.assertNotEqual(u, [0]) - self.assertNotEqual(u, [0, 2]) + u = t("spam") + u += UserList("eggs") + self.assertEqual(u, list("spameggs")) + self.assertIs(type(u), UserList) - def test_mixedadd(self): + u = u2 = UserList("spam") + u += UserListSubclass("eggs") + self.assertEqual(u, list("spameggs")) + self.assertIs(type(u), UserList) + self.assertIs(u, u2) + + u = u2 = UserListSubclass("spam") + u += UserList("eggs") + self.assertEqual(u, list("spameggs")) + self.assertIs(type(u), UserListSubclass) + self.assertIs(u, u2) + + u = u2 = UserListSubclass("spam") + u += UserListSubclass2("eggs") + self.assertEqual(u, list("spameggs")) + self.assertIs(type(u), UserListSubclass) + self.assertIs(u, u2) + + def test_mixed_cmp(self): u = self.type2test([0, 1]) - self.assertEqual(u + [], u) - self.assertEqual(u + [2], [0, 1, 2]) + self._assert_cmp(u, [0, 1], 0) + self._assert_cmp(u, [0], 1) + self._assert_cmp(u, [0, 2], -1) def test_getitemoverwriteiter(self): # Verify that __getitem__ overrides *are* recognized by __iter__ @@ -60,6 +124,43 @@ def __getitem__(self, key): return str(key) + '!!!' self.assertEqual(next(iter(T((1,2)))), "0!!!") + def test_implementation(self): + u = UserList([1]) + with (support.swap_attr(UserList, '__len__', None), + support.swap_attr(UserList, 'insert', None)): + u.append(2) + self.assertEqual(u, [1, 2]) + with support.swap_attr(UserList, 'append', None): + u.extend([3, 4]) + self.assertEqual(u, [1, 2, 3, 4]) + with support.swap_attr(UserList, 'append', None): + u.extend(UserList([3, 4])) + self.assertEqual(u, [1, 2, 3, 4, 3, 4]) + with support.swap_attr(UserList, '__iter__', None): + c = u.count(3) + self.assertEqual(c, 2) + with (support.swap_attr(UserList, '__iter__', None), + support.swap_attr(UserList, '__getitem__', None)): + i = u.index(4) + self.assertEqual(i, 3) + with (support.swap_attr(UserList, 'index', None), + support.swap_attr(UserList, '__getitem__', None)): + u.remove(3) + self.assertEqual(u, [1, 2, 4, 3, 4]) + with (support.swap_attr(UserList, '__getitem__', None), + support.swap_attr(UserList, '__delitem__', None)): + u.pop() + self.assertEqual(u, [1, 2, 4, 3]) + with (support.swap_attr(UserList, '__len__', None), + support.swap_attr(UserList, '__getitem__', None), + support.swap_attr(UserList, '__setitem__', None)): + u.reverse() + self.assertEqual(u, [3, 4, 2, 1]) + with (support.swap_attr(UserList, '__len__', None), + support.swap_attr(UserList, 'pop', None)): + u.clear() + self.assertEqual(u, []) + def test_userlist_copy(self): u = self.type2test([6, 8, 1, 9, 1]) v = u.copy() diff --git a/Lib/test/test_userstring.py b/Lib/test/test_userstring.py index 74df52f5412af0..cc85c06bf93363 100644 --- a/Lib/test/test_userstring.py +++ b/Lib/test/test_userstring.py @@ -3,9 +3,18 @@ import unittest from test import string_tests +from test import support from collections import UserString + +class UserStringSubclass(UserString): + pass + +class UserStringSubclass2(UserString): + pass + + class UserStringTest( string_tests.StringLikeTest, unittest.TestCase @@ -40,6 +49,78 @@ def checkcall(self, object, methodname, *args): # we don't fix the arguments, because UserString can't cope with it getattr(object, methodname)(*args) + def test_data(self): + u = UserString("spam") + self.assertEqual(u.data, "spam") + self.assertIs(type(u.data), str) + u = UserString(u) + self.assertEqual(u.data, "spam") + self.assertIs(type(u.data), str) + u = UserString(42) + self.assertEqual(u.data, "42") + self.assertIs(type(u.data), str) + + def test_mixed_add(self): + u = UserString("spam") + "eggs" + self.assertEqual(u, "spameggs") + self.assertIs(type(u), UserString) + + u = "spam" + UserString("eggs") + self.assertEqual(u, "spameggs") + self.assertIs(type(u), UserString) + + u = UserString("spam") + UserStringSubclass("eggs") + self.assertEqual(u, "spameggs") + self.assertIs(type(u), UserString) + + u = UserStringSubclass("spam") + UserString("eggs") + self.assertEqual(u, "spameggs") + self.assertIs(type(u), UserStringSubclass) + + u = UserStringSubclass("spam") + UserStringSubclass2("eggs") + self.assertEqual(u, "spameggs") + self.assertIs(type(u), UserStringSubclass) + + u2 = UserString("eggs").__radd__(UserString("spam")) + self.assertEqual(u2, "spameggs") + self.assertIs(type(u), UserStringSubclass) + + u2 = UserStringSubclass("eggs").__radd__(UserStringSubclass2("spam")) + self.assertEqual(u2, "spameggs") + self.assertIs(type(u), UserStringSubclass) + + def test_mixed_iadd(self): + u = UserString("spam") + u += "eggs" + self.assertEqual(u, "spameggs") + self.assertIs(type(u), UserString) + + u = "spam" + u += UserString("eggs") + self.assertEqual(u, "spameggs") + self.assertIs(type(u), UserString) + + u = UserString("spam") + u += UserStringSubclass("eggs") + self.assertEqual(u, "spameggs") + self.assertIs(type(u), UserString) + + u = UserStringSubclass("spam") + u += UserString("eggs") + self.assertEqual(u, "spameggs") + self.assertIs(type(u), UserStringSubclass) + + u = UserStringSubclass("spam") + u += UserStringSubclass2("eggs") + self.assertEqual(u, "spameggs") + self.assertIs(type(u), UserStringSubclass) + + def test_mixed_cmp(self): + a = self.fixtype('ab') + self._assert_cmp(a, 'ab', 0) + self._assert_cmp(a, 'a', 1) + self._assert_cmp(a, 'ac', -1) + def test_rmod(self): class ustr2(UserString): pass @@ -66,6 +147,20 @@ def test_encode_explicit_none_args(self): # Check that errors defaults to 'strict' self.checkraises(UnicodeError, '\ud800', 'encode', None, None) + def test_implementation(self): + s = UserString('ababahalamaha') + with support.swap_attr(UserString, '__iter__', None): + c = s.count('a') + c2 = s.count(UserString('a')) + self.assertEqual(c, 7) + self.assertEqual(c2, 7) + with (support.swap_attr(UserString, '__iter__', None), + support.swap_attr(UserString, '__getitem__', None)): + i = s.index('h') + i2 = s.index(UserString('h')) + self.assertEqual(i, 5) + self.assertEqual(i2, 5) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 0e1a723ce3a151..055be2994bf435 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -13,7 +13,7 @@ from unittest import mock from test import support -from test.support import import_helper +from test.support import force_not_colorized_test_class, import_helper, warnings_helper from test.support.script_helper import assert_python_ok py_uuid = import_helper.import_fresh_module('uuid', blocked=['_uuid']) @@ -590,6 +590,7 @@ def test_uuid1_safe(self): # dependent on the underlying platform support. At least it cannot be # unknown (unless I suppose the platform is buggy). self.assertNotEqual(u.is_safe, self.uuid.SafeUUID.unknown) + self.assertEqual(u.version, 1) @contextlib.contextmanager def mock_generate_time_safe(self, safe_value): @@ -612,24 +613,28 @@ def test_uuid1_unknown(self): with self.mock_generate_time_safe(None): u = self.uuid.uuid1() self.assertEqual(u.is_safe, self.uuid.SafeUUID.unknown) + self.assertEqual(u.version, 1) @unittest.skipUnless(os.name == 'posix', 'POSIX-only test') def test_uuid1_is_safe(self): with self.mock_generate_time_safe(0): u = self.uuid.uuid1() self.assertEqual(u.is_safe, self.uuid.SafeUUID.safe) + self.assertEqual(u.version, 1) @unittest.skipUnless(os.name == 'posix', 'POSIX-only test') def test_uuid1_is_unsafe(self): with self.mock_generate_time_safe(-1): u = self.uuid.uuid1() self.assertEqual(u.is_safe, self.uuid.SafeUUID.unsafe) + self.assertEqual(u.version, 1) @unittest.skipUnless(os.name == 'posix', 'POSIX-only test') def test_uuid1_bogus_return_value(self): with self.mock_generate_time_safe(3): u = self.uuid.uuid1() self.assertEqual(u.is_safe, self.uuid.SafeUUID.unknown) + self.assertEqual(u.version, 1) def test_uuid1_time(self): with mock.patch.object(self.uuid, '_generate_time_safe', None), \ @@ -1112,6 +1117,7 @@ def test_uuid8_uniqueness(self): versions = {u.version for u in uuids} self.assertSetEqual(versions, {8}) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() def testIssue8621(self): # On at least some versions of OSX self.uuid.uuid4 generates @@ -1176,6 +1182,47 @@ def test_cli_name_required_for_uuid3(self, mock_err): self.assertEqual(cm.exception.code, 2) self.assertIn("error: Incorrect number of arguments", mock_err.getvalue()) + @mock.patch.object(sys, "argv", + ["", "-u", "uuid3", "-n", "@dns", "-N", "python.org"]) + def test_cli_uuid3_outputted_with_valid_namespace_and_name(self): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + self.uuid.main() + + output = stdout.getvalue().strip() + uuid_output = self.uuid.UUID(output) + + # Output should be in the form of uuid3 + self.assertEqual(output, str(uuid_output)) + self.assertEqual(uuid_output.version, 3) + + @mock.patch.object(sys, "argv", + ["", "-u", "uuid3", "-n", + "0d6a16cc-34a7-47d8-b660-214d0ae184d2", + "-N", "some.user"]) + def test_cli_uuid3_outputted_with_custom_namespace_and_name(self): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + self.uuid.main() + + output = stdout.getvalue().strip() + uuid_output = self.uuid.UUID(output) + + # Output should be in the form of uuid3 + self.assertEqual(output, str(uuid_output)) + self.assertEqual(uuid_output.version, 3) + + @mock.patch.object(sys, "argv", + ["", "-u", "uuid3", "-n", "any UUID", "-N", "python.org"]) + @mock.patch('sys.stderr', new_callable=io.StringIO) + def test_cli_uuid3_with_invalid_namespace(self, mock_err): + with self.assertRaises(SystemExit) as cm: + self.uuid.main() + # Check that exception code is the same as argparse.ArgumentParser.error + self.assertEqual(cm.exception.code, 2) + self.assertIn("error: badly formed hexadecimal UUID string", + mock_err.getvalue()) + @mock.patch.object(sys, "argv", [""]) def test_cli_uuid4_outputted_with_no_args(self): stdout = io.StringIO() @@ -1204,8 +1251,8 @@ def test_cli_uuid4_outputted_with_count(self): self.assertEqual(uuid_output.version, 4) @mock.patch.object(sys, "argv", - ["", "-u", "uuid3", "-n", "@dns", "-N", "python.org"]) - def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self): + ["", "-u", "uuid5", "-n", "@dns", "-N", "python.org"]) + def test_cli_uuid5_outputted_with_valid_namespace_and_name(self): stdout = io.StringIO() with contextlib.redirect_stdout(stdout): self.uuid.main() @@ -1215,11 +1262,13 @@ def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self): # Output should be in the form of uuid5 self.assertEqual(output, str(uuid_output)) - self.assertEqual(uuid_output.version, 3) + self.assertEqual(uuid_output.version, 5) @mock.patch.object(sys, "argv", - ["", "-u", "uuid5", "-n", "@dns", "-N", "python.org"]) - def test_cli_uuid5_ouputted_with_valid_namespace_and_name(self): + ["", "-u", "uuid5", "-n", + "0d6a16cc-34a7-47d8-b660-214d0ae184d2", + "-N", "some.user"]) + def test_cli_uuid5_ouputted_with_custom_namespace_and_name(self): stdout = io.StringIO() with contextlib.redirect_stdout(stdout): self.uuid.main() @@ -1231,6 +1280,17 @@ def test_cli_uuid5_ouputted_with_valid_namespace_and_name(self): self.assertEqual(output, str(uuid_output)) self.assertEqual(uuid_output.version, 5) + @mock.patch.object(sys, "argv", + ["", "-u", "uuid5", "-n", "any UUID", "-N", "python.org"]) + @mock.patch('sys.stderr', new_callable=io.StringIO) + def test_cli_uuid5_with_invalid_namespace(self, mock_err): + with self.assertRaises(SystemExit) as cm: + self.uuid.main() + # Check that exception code is the same as argparse.ArgumentParser.error + self.assertEqual(cm.exception.code, 2) + self.assertIn("error: badly formed hexadecimal UUID string", + mock_err.getvalue()) + @mock.patch.object(sys, "argv", ["", "-u", "uuid6"]) def test_cli_uuid6(self): self.do_test_standalone_uuid(6) @@ -1244,10 +1304,12 @@ def test_cli_uuid8(self): self.do_test_standalone_uuid(8) +@force_not_colorized_test_class class TestUUIDWithoutExtModule(CommandLineTestCases, BaseTestUUID, unittest.TestCase): uuid = py_uuid +@force_not_colorized_test_class @unittest.skipUnless(c_uuid, 'requires the C _uuid module') class TestUUIDWithExtModule(CommandLineTestCases, BaseTestUUID, unittest.TestCase): uuid = c_uuid diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 3c18c9c2900ad7..0c6cd1b196ac81 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -11,13 +11,12 @@ import os.path import pathlib import re +import shlex import shutil -import struct import subprocess import sys import sysconfig import tempfile -import shlex from test.support import (captured_stdout, captured_stderr, skip_if_broken_multiprocessing_synchronize, verbose, requires_subprocess, is_android, is_apple_mobile, @@ -138,14 +137,9 @@ def _check_output_of_default_create(self): self.isdir(self.bindir) self.isdir(self.include) self.isdir(*self.lib) - # Issue 21197 p = self.get_env_file('lib64') - conditions = ((struct.calcsize('P') == 8) and (os.name == 'posix') and - (sys.platform != 'darwin')) - if conditions: - self.assertTrue(os.path.islink(p)) - else: - self.assertFalse(os.path.exists(p)) + if os.path.exists(p): + self.assertFalse(os.path.islink(p)) data = self.get_text_file_contents('pyvenv.cfg') executable = sys._base_executable path = os.path.dirname(executable) @@ -307,9 +301,9 @@ def test_sysconfig(self): self.assertEqual(out.strip(), expected, err) for attr, expected in ( ('executable', self.envpy()), - # Usually compare to sys.executable, but if we're running in our own - # venv then we really need to compare to our base executable - ('_base_executable', sys._base_executable), + # Usually compare to sys.prefix, but if we're running in our own + # venv then we really need to compare to our base prefix + ('base_prefix', sys.base_prefix), ): with self.subTest(attr): cmd[2] = f'import sys; print(sys.{attr})' @@ -379,6 +373,16 @@ def create_contents(self, paths, filename): with open(fn, 'wb') as f: f.write(b'Still here?') + @unittest.skipUnless(hasattr(os, 'listxattr'), 'test requires os.listxattr') + def test_install_scripts_selinux(self): + """ + gh-145417: Test that install_scripts does not copy SELinux context + when copying scripts. + """ + with patch('os.listxattr') as listxattr_mock: + venv.create(self.env_dir) + listxattr_mock.assert_not_called() + def test_overwrite_existing(self): """ Test creating environment in an existing directory. @@ -496,6 +500,7 @@ def test_executable_symlinks(self): # gh-124651: test quoted strings @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') + @unittest.skipIf(sys.platform == 'cygwin', 'fail to locate cygpython DLL') def test_special_chars_bash(self): """ Test that the template strings are quoted properly (bash) @@ -522,6 +527,8 @@ def test_special_chars_bash(self): # gh-124651: test quoted strings @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') + @unittest.skipIf(sys.platform.startswith('netbsd'), + "NetBSD csh fails with quoted special chars; see gh-139308") def test_special_chars_csh(self): """ Test that the template strings are quoted properly (csh) @@ -586,6 +593,51 @@ def test_unicode_in_batch_file(self): ) self.assertEqual(out.strip(), '0') + @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') + def test_activate_bat_respects_disable_prompt(self): + rmtree(self.env_dir) + env_dir = os.path.join(os.path.realpath(self.env_dir), 'venv') + builder = venv.EnvBuilder(clear=True) + builder.create(env_dir) + activate = os.path.join(env_dir, self.bindir, 'activate.bat') + test_batch = os.path.join(self.env_dir, 'test_disable_prompt.bat') + with open(test_batch, "w") as f: + f.write('@echo off\n' + 'set "PROMPT=base$G"\n' + 'set "VIRTUAL_ENV_DISABLE_PROMPT=1"\n' + f'call "{activate}"\n' + 'echo ACTIVE_PROMPT:%PROMPT%\n' + 'echo VIRTUAL_ENV:%VIRTUAL_ENV%\n' + 'set "PROMPT=changed$G"\n' + 'call deactivate\n' + 'echo FINAL_PROMPT:%PROMPT%\n') + out, err = check_output([test_batch]) + lines = out.splitlines() + self.assertEqual(lines[0], b'ACTIVE_PROMPT:base$G') + self.assertEndsWith(lines[1], os.fsencode(env_dir)) + self.assertEqual(lines[2], b'FINAL_PROMPT:changed$G') + + @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') + def test_activate_bat_prefixes_prompt_by_default(self): + rmtree(self.env_dir) + env_dir = os.path.join(os.path.realpath(self.env_dir), 'venv') + builder = venv.EnvBuilder(clear=True) + builder.create(env_dir) + activate = os.path.join(env_dir, self.bindir, 'activate.bat') + test_batch = os.path.join(self.env_dir, 'test_enable_prompt.bat') + with open(test_batch, "w") as f: + f.write('@echo off\n' + 'set "PROMPT=base) $G"\n' + 'set "VIRTUAL_ENV_DISABLE_PROMPT="\n' + f'call "{activate}"\n' + 'echo ACTIVE_PROMPT:%PROMPT%\n' + 'call deactivate\n' + 'echo FINAL_PROMPT:%PROMPT%\n') + out, err = check_output([test_batch]) + lines = out.splitlines() + self.assertEqual(lines[0], b'ACTIVE_PROMPT:(venv) base) $G') + self.assertEqual(lines[1], b'FINAL_PROMPT:base) $G') + @unittest.skipUnless(os.name == 'nt' and can_symlink(), 'symlinks on Windows') def test_failed_symlink(self): @@ -650,6 +702,26 @@ def test_deactivate_with_strict_bash_opts(self): self.assertEqual(out, "".encode()) self.assertEqual(err, "".encode()) + # gh-149701: Test exit code is zero even when hashing is disabled + @unittest.skipIf(os.name == 'nt', 'not relevant on Windows') + def test_deactivate_with_strict_bash_opts_and_hashing_disabled(self): + bash = shutil.which("bash") + if bash is None: + self.skipTest("bash required for this test") + rmtree(self.env_dir) + builder = venv.EnvBuilder(clear=True) + builder.create(self.env_dir) + activate = os.path.join(self.env_dir, self.bindir, "activate") + test_script = os.path.join(self.env_dir, "test_hash_disabled.sh") + with open(test_script, "w") as f: + f.write("set -euo pipefail\n" + "set +h\n" # disable hashing + f"source {activate}\n" + "deactivate") + out, err = check_output([bash, test_script]) + self.assertEqual(out, "".encode()) + self.assertEqual(err, "".encode()) + @unittest.skipUnless(sys.platform == 'darwin', 'only relevant on macOS') def test_macos_env(self): @@ -688,6 +760,12 @@ def test_zippath_from_non_installed_posix(self): os.mkdir(bindir) python_exe = os.path.basename(sys.executable) shutil.copy2(sys.executable, os.path.join(bindir, python_exe)) + if sys.platform == 'cygwin': + # Copy libpython DLL + exe_path = os.path.dirname(sys.executable) + libpython_dll = sysconfig.get_config_var('DLLLIBRARY') + shutil.copy2(os.path.join(exe_path, libpython_dll), + os.path.join(bindir, libpython_dll)) libdir = os.path.join(non_installed_dir, platlibdir, self.lib[1]) os.makedirs(libdir) landmark = os.path.join(libdir, "os.py") @@ -890,10 +968,10 @@ def test_venvwlauncher(self): exename = exename.replace("python", "pythonw") envpyw = os.path.join(self.env_dir, self.bindir, exename) try: - subprocess.check_call([envpyw, "-c", "import sys; " - "assert sys._base_executable.endswith('%s')" % exename]) + subprocess.check_call([envpyw, "-c", "import fnmatch, sys; " + "assert fnmatch.fnmatch(sys._base_executable, '**/pythonw*.exe')"]) except subprocess.CalledProcessError: - self.fail("venvwlauncher.exe did not run %s" % exename) + self.fail("venvwlauncher.exe did not run pythonw.exe") @requireVenvCreate diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index f89e94449b3031..bf1bcf8e6ed5d9 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -1,5 +1,6 @@ from contextlib import contextmanager import linecache +import logging import os import importlib import inspect @@ -241,6 +242,96 @@ def test_once(self): 42) self.assertEqual(len(w), 0) + def test_filter_module(self): + MS_WINDOWS = (sys.platform == 'win32') + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module=r'package\.module\z') + self.module.warn_explicit('msg', UserWarning, 'filename', 42, + module='package.module') + self.assertEqual(len(w), 1) + self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42) + self.assertEqual(len(w), 2) + self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42) + self.assertEqual(len(w), 3) + self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__.py', 42) + self.assertEqual(len(w), 4) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__', 42) + if MS_WINDOWS: + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42) + self.assertEqual(len(w), 5) + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__INIT__.PY', 42) + self.assertEqual(len(w), 6) + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PYW', 42) + self.assertEqual(len(w), 7) + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__INIT__.PYW', 42) + self.assertEqual(len(w), 8) + + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module='package') + self.module.warn_explicit('msg', UserWarning, 'filename', 42, + module='package.module') + self.assertEqual(len(w), 1) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, 'filename', 42, + module='other.package.module') + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, '/path/to/otherpackage/module.py', 42) + + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module=r'/path/to/package/module\z') + self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42) + self.assertEqual(len(w), 1) + self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42) + self.assertEqual(len(w), 2) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, '/PATH/TO/PACKAGE/MODULE', 42) + if MS_WINDOWS: + self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42) + self.assertEqual(len(w), 3) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module/__init__.py', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.pyw', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'\path\to\package\module', 42) + + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module=r'/path/to/package/__init__\z') + self.module.warn_explicit('msg', UserWarning, '/path/to/package/__init__.py', 42) + self.assertEqual(len(w), 1) + self.module.warn_explicit('msg', UserWarning, '/path/to/package/__init__', 42) + self.assertEqual(len(w), 2) + + if MS_WINDOWS: + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module=r'C:\\path\\to\\package\\module\z') + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module', 42) + self.assertEqual(len(w), 1) + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.py', 42) + self.assertEqual(len(w), 2) + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42) + self.assertEqual(len(w), 3) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.pyw', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'C:\PATH\TO\PACKAGE\MODULE', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'C:/path/to/package/module', 42) + with self.assertRaises(UserWarning): + self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__init__.py', 42) + + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter('error') + self.module.filterwarnings('always', module=r'<unknown>\z') + self.module.warn_explicit('msg', UserWarning, '', 42) + self.assertEqual(len(w), 1) + def test_module_globals(self): with self.module.catch_warnings(record=True) as w: self.module.simplefilter("always", UserWarning) @@ -320,7 +411,7 @@ def test_message_matching(self): def test_mutate_filter_list(self): class X: - def match(self, a): + def match(self, a, start=0): L[:] = [] L = [("default",X(),UserWarning,X(),0) for i in range(2)] @@ -419,6 +510,47 @@ def test_catchwarnings_with_simplefilter_error(self): stderr = stderr.getvalue() self.assertIn(error_msg, stderr) + def test_catchwarnings_with_showwarning(self): + # gh-146358: catch_warnings must override warnings.showwarning() + # if it's not the default implementation. + + warns = [] + def custom_showwarning(message, category, filename, lineno, + file=None, line=None): + warns.append(message) + + with self.module.catch_warnings(): + self.module.resetwarnings() + + with support.swap_attr(self.module, 'showwarning', + custom_showwarning): + with self.module.catch_warnings(record=True) as recorded: + self.module.warn("recorded") + self.assertEqual(len(recorded), 1) + self.assertEqual(str(recorded[0].message), 'recorded') + self.assertIs(self.module.showwarning, custom_showwarning) + + self.module.warn("custom") + + self.assertEqual(len(warns), 1) + self.assertEqual(str(warns[0]), "custom") + + def test_catchwarnings_logging(self): + # gh-146358: catch_warnings(record=True) must replace the + # showwarning() function set by logging.captureWarnings(True). + + with self.module.catch_warnings(): + self.module.resetwarnings() + logging.captureWarnings(True) + + with self.module.catch_warnings(record=True) as recorded: + self.module.warn("recorded") + self.assertEqual(len(recorded), 1) + self.assertEqual(str(recorded[0].message), 'recorded') + + logging.captureWarnings(False) + + class CFilterTests(FilterTests, unittest.TestCase): module = c_warnings @@ -450,15 +582,19 @@ def test_warn_nonstandard_types(self): # ``Warning() != Warning()``. self.assertEqual(str(w[-1].message), str(UserWarning(ob))) - def test_filename(self): + def test_filename_module(self): with warnings_state(self.module): with self.module.catch_warnings(record=True) as w: warning_tests.inner("spam1") self.assertEqual(os.path.basename(w[-1].filename), "stacklevel.py") + self.assertEqual(w[-1].module, + "test.test_warnings.data.stacklevel") warning_tests.outer("spam2") self.assertEqual(os.path.basename(w[-1].filename), "stacklevel.py") + self.assertEqual(w[-1].module, + "test.test_warnings.data.stacklevel") def test_stacklevel(self): # Test stacklevel argument @@ -468,23 +604,32 @@ def test_stacklevel(self): warning_tests.inner("spam3", stacklevel=1) self.assertEqual(os.path.basename(w[-1].filename), "stacklevel.py") + self.assertEqual(w[-1].module, + "test.test_warnings.data.stacklevel") warning_tests.outer("spam4", stacklevel=1) self.assertEqual(os.path.basename(w[-1].filename), "stacklevel.py") + self.assertEqual(w[-1].module, + "test.test_warnings.data.stacklevel") warning_tests.inner("spam5", stacklevel=2) self.assertEqual(os.path.basename(w[-1].filename), "__init__.py") + self.assertEqual(w[-1].module, __name__) warning_tests.outer("spam6", stacklevel=2) self.assertEqual(os.path.basename(w[-1].filename), "stacklevel.py") + self.assertEqual(w[-1].module, + "test.test_warnings.data.stacklevel") warning_tests.outer("spam6.5", stacklevel=3) self.assertEqual(os.path.basename(w[-1].filename), "__init__.py") + self.assertEqual(w[-1].module, __name__) warning_tests.inner("spam7", stacklevel=9999) self.assertEqual(os.path.basename(w[-1].filename), "<sys>") + self.assertEqual(w[-1].module, "sys") def test_stacklevel_import(self): # Issue #24305: With stacklevel=2, module-level warnings should work. @@ -495,6 +640,7 @@ def test_stacklevel_import(self): import test.test_warnings.data.import_warning # noqa: F401 self.assertEqual(len(w), 1) self.assertEqual(w[0].filename, __file__) + self.assertEqual(w[0].module, __name__) def test_skip_file_prefixes(self): with warnings_state(self.module): @@ -506,20 +652,27 @@ def test_skip_file_prefixes(self): "inner_api", stacklevel=2, warnings_module=warning_tests.warnings) self.assertEqual(w[-1].filename, __file__) + self.assertEqual(w[-1].module, __name__) warning_tests.package("package api", stacklevel=2) self.assertEqual(w[-1].filename, __file__) + self.assertEqual(w[-1].module, __name__) self.assertEqual(w[-2].filename, w[-1].filename) + self.assertEqual(w[-2].module, w[-1].module) # Low stacklevels are overridden to 2 behavior. warning_tests.package("package api 1", stacklevel=1) self.assertEqual(w[-1].filename, __file__) + self.assertEqual(w[-1].module, __name__) warning_tests.package("package api 0", stacklevel=0) self.assertEqual(w[-1].filename, __file__) + self.assertEqual(w[-1].module, __name__) warning_tests.package("package api -99", stacklevel=-99) self.assertEqual(w[-1].filename, __file__) + self.assertEqual(w[-1].module, __name__) # The stacklevel still goes up out of the package. warning_tests.package("prefix02", stacklevel=3) self.assertIn("unittest", w[-1].filename) + self.assertStartsWith(w[-1].module, "unittest") def test_skip_file_prefixes_file_path(self): # see: gh-126209 @@ -530,6 +683,8 @@ def test_skip_file_prefixes_file_path(self): self.assertEqual(len(w), 1) self.assertNotEqual(w[-1].filename, skipped) + self.assertEqual(w[-1].filename, __file__) + self.assertEqual(w[-1].module, __name__) def test_skip_file_prefixes_type_errors(self): with warnings_state(self.module): @@ -541,7 +696,7 @@ def test_skip_file_prefixes_type_errors(self): with self.assertRaises(TypeError): warn("msg", skip_file_prefixes="a sequence of strs") - def test_exec_filename(self): + def test_exec_filename_module(self): filename = "<warnings-test>" codeobj = compile(("import warnings\n" "warnings.warn('hello', UserWarning)"), @@ -550,6 +705,12 @@ def test_exec_filename(self): self.module.simplefilter("always", category=UserWarning) exec(codeobj) self.assertEqual(w[0].filename, filename) + self.assertEqual(w[0].module, __name__) + with self.module.catch_warnings(record=True) as w: + self.module.simplefilter("always", category=UserWarning) + exec(codeobj, {}) + self.assertEqual(w[0].filename, filename) + self.assertEqual(w[0].module, '<string>') def test_warn_explicit_non_ascii_filename(self): with self.module.catch_warnings(record=True) as w: @@ -596,25 +757,19 @@ def test_warning_classes(self): class MyWarningClass(Warning): pass - class NonWarningSubclass: - pass - # passing a non-subclass of Warning should raise a TypeError - with self.assertRaises(TypeError) as cm: + expected = "category must be a Warning subclass, not 'str'" + with self.assertRaisesRegex(TypeError, expected): self.module.warn('bad warning category', '') - self.assertIn('category must be a Warning subclass, not ', - str(cm.exception)) - with self.assertRaises(TypeError) as cm: - self.module.warn('bad warning category', NonWarningSubclass) - self.assertIn('category must be a Warning subclass, not ', - str(cm.exception)) + expected = "category must be a Warning subclass, not class 'int'" + with self.assertRaisesRegex(TypeError, expected): + self.module.warn('bad warning category', int) # check that warning instances also raise a TypeError - with self.assertRaises(TypeError) as cm: + expected = "category must be a Warning subclass, not '.*MyWarningClass'" + with self.assertRaisesRegex(TypeError, expected): self.module.warn('bad warning category', MyWarningClass()) - self.assertIn('category must be a Warning subclass, not ', - str(cm.exception)) with self.module.catch_warnings(): self.module.resetwarnings() @@ -643,7 +798,7 @@ def check_module_globals(self, module_globals): def check_module_globals_error(self, module_globals, errmsg, errtype=ValueError): if self.module is py_warnings: - self.check_module_globals(module_globals) + self.check_module_globals_deprecated(module_globals, errmsg) return with self.module.catch_warnings(record=True) as w: self.module.filterwarnings('always') @@ -654,9 +809,6 @@ def check_module_globals_error(self, module_globals, errmsg, errtype=ValueError) self.assertEqual(len(w), 0) def check_module_globals_deprecated(self, module_globals, msg): - if self.module is py_warnings: - self.check_module_globals(module_globals) - return with self.module.catch_warnings(record=True) as w: self.module.filterwarnings('always') self.module.warn_explicit( @@ -761,6 +913,10 @@ def test_improper_input(self): self.module._setoption('ignore::===') with self.assertRaisesRegex(self.module._OptionError, 'Wärning'): self.module._setoption('ignore::Wärning') + with self.assertRaisesRegex(self.module._OptionError, 'message'): + self.module._setoption('ignore:/?/:Warning') + with self.assertRaisesRegex(self.module._OptionError, 'module'): + self.module._setoption('ignore::Warning:/?/') self.module._setoption('error::Warning::0') self.assertRaises(UserWarning, self.module.warn, 'convert to error') @@ -775,6 +931,31 @@ def test_import_from_module(self): with self.assertRaises(TestWarning): self.module.warn('test warning', TestWarning) + def test_message(self): + # Match prefix, case-insensitive. + with self.module.catch_warnings(): + self.module._setoption('error:TEST WARN:UserWarning') + with self.assertRaises(UserWarning): + self.module.warn('Test Warning') + with self.module.catch_warnings(): + self.module._setoption(r'error:/TE.*WARN/:UserWarning') + with self.assertRaises(UserWarning): + self.module.warn('Test Warning') + + def test_module(self): + with self.module.catch_warnings(): + self.module._setoption(f'error::UserWarning:{__name__}') + with self.assertRaises(UserWarning): + self.module.warn('test warning') + # Only full match. + self.module._setoption(f'ignore::UserWarning:{__name__[:-2]}') + with self.assertRaises(UserWarning): + self.module.warn('test warning') + with self.module.catch_warnings(): + self.module._setoption(f'error::UserWarning:/{re.escape(__name__[:-2])}./') + with self.assertRaises(UserWarning): + self.module.warn('test warning') + class CWCmdLineTests(WCmdLineTests, unittest.TestCase): module = c_warnings @@ -1869,6 +2050,25 @@ class D(C, x=3): self.assertEqual(D.inited, 3) + def test_existing_init_subclass_in_sibling_base(self): + @deprecated("A will go away soon") + class A: + pass + class B: + def __init_subclass__(cls, x): + super().__init_subclass__() + cls.inited = x + + with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"): + class C(A, B, x=42): + pass + self.assertEqual(C.inited, 42) + + with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"): + class D(B, A, x=42): + pass + self.assertEqual(D.inited, 42) + def test_init_subclass_has_correct_cls(self): init_subclass_saw = None diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index 226b1aa84bd73c..d3723c04820d9d 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -1,9 +1,11 @@ import unittest from test import audiotests from test import support +from test.support.os_helper import FakePath, unlink import io import os import struct +import tempfile import sys import wave @@ -20,6 +22,7 @@ class WavePCM8Test(WaveTest, unittest.TestCase): sampwidth = 1 framerate = 11025 nframes = 48 + format = wave.WAVE_FORMAT_PCM comptype = 'NONE' compname = 'not compressed' frames = bytes.fromhex("""\ @@ -37,6 +40,7 @@ class WavePCM16Test(WaveTest, unittest.TestCase): sampwidth = 2 framerate = 11025 nframes = 48 + format = wave.WAVE_FORMAT_PCM comptype = 'NONE' compname = 'not compressed' frames = bytes.fromhex("""\ @@ -58,6 +62,7 @@ class WavePCM24Test(WaveTest, unittest.TestCase): sampwidth = 3 framerate = 11025 nframes = 48 + format = wave.WAVE_FORMAT_PCM comptype = 'NONE' compname = 'not compressed' frames = bytes.fromhex("""\ @@ -85,6 +90,8 @@ class WavePCM24ExtTest(WaveTest, unittest.TestCase): sampwidth = 3 framerate = 11025 nframes = 48 + format = wave.WAVE_FORMAT_EXTENSIBLE + readonly = True # Writing EXTENSIBLE wave format is not supported. comptype = 'NONE' compname = 'not compressed' frames = bytes.fromhex("""\ @@ -112,6 +119,7 @@ class WavePCM32Test(WaveTest, unittest.TestCase): sampwidth = 4 framerate = 11025 nframes = 48 + format = wave.WAVE_FORMAT_PCM comptype = 'NONE' compname = 'not compressed' frames = bytes.fromhex("""\ @@ -132,14 +140,142 @@ class WavePCM32Test(WaveTest, unittest.TestCase): frames = wave._byteswap(frames, 4) +class WaveIeeeFloatingPointTest(WaveTest, unittest.TestCase): + sndfilename = 'pluck-float32.wav' + sndfilenframes = 3307 + nchannels = 2 + sampwidth = 4 + framerate = 11025 + nframes = 48 + format = wave.WAVE_FORMAT_IEEE_FLOAT + comptype = 'NONE' + compname = 'not compressed' + frames = bytes.fromhex("""\ + 3C8B5960BA231400 3F16B41F3BFA5480 3EC44F0E3D1DC580 BF7E46533D843040 \ + BED084FC3D564C30 3F1153303CFCBE40 BF002FB73C583EC0 3CDAFEE0BC425180 \ + BF0F5154BD3826E0 BF169F56BDCAFD40 3EA660C0BE21A4EC 3E52E53CBE49332C \ + BE102E0CBE5B7214 BEE76852BE6C3BDC 3DE05A98BE7A4980 BE06B6B4BE7EB6EC \ + 3F2EB1B0BE6C7CC8 BD195500BE3E0F4C 3E1BBDF8BE03DFEC BE9F4E92BD8D8D58 \ + BF50E1D4BD111750 BDA079B0BCFBFB20 3D866358BD0C7640 BE833C0EBD17E240 \ + 3E0BFF04BD3978F0 3EFB9AE2BD14A780 BF0710B9BCD342E0 3F4DADB5BBA0CD80 \ + BEC3B11A3D024EB0 3F063AD33D97A8C0 BEF912803DEC74E0 3F2241733E1515D4 \ + BE0904D83E3AA604 BF7BF2003E3325BC 3F8000003E2229FC BF8000003E14A738 \ + 3F1338363DEB3B28 3F256E7C3DDBCA00 BE026A683DF5FD88 BEC70C923DFBE128 \ + BE5A5B183DCEA2D8 3F4689513DA5A7C8 3D8C8FE83DA1FFF0 3EAEE61C3DB0A0E0 \ + 3F2290DF3DE44E18 BF6867373E09D82C BF1216283DEEB360 3F08262F3DA5B488 \ + """) + if sys.byteorder != 'big': + frames = wave._byteswap(frames, 4) + class MiscTestCase(unittest.TestCase): def test__all__(self): - not_exported = {'WAVE_FORMAT_PCM', 'WAVE_FORMAT_EXTENSIBLE', 'KSDATAFORMAT_SUBTYPE_PCM'} + not_exported = {'KSDATAFORMAT_SUBTYPE_PCM'} support.check__all__(self, wave, not_exported=not_exported) class WaveLowLevelTest(unittest.TestCase): + def test_setparams_6_tuple_defaults_to_pcm(self): + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + filename = fp.name + self.addCleanup(unlink, filename) + + with wave.open(filename, 'wb') as w: + w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) + w.setparams((1, 2, 22050, 0, 'NONE', 'not compressed')) + self.assertEqual(w.getformat(), wave.WAVE_FORMAT_PCM) + + def test_setparams_7_tuple_uses_format(self): + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + filename = fp.name + self.addCleanup(unlink, filename) + + with wave.open(filename, 'wb') as w: + w.setparams((1, 4, 22050, 0, 'NONE', 'not compressed', + wave.WAVE_FORMAT_IEEE_FLOAT)) + self.assertEqual(w.getformat(), wave.WAVE_FORMAT_IEEE_FLOAT) + + def test_setparams_7_tuple_ieee_64bit_sampwidth(self): + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + filename = fp.name + self.addCleanup(unlink, filename) + + with wave.open(filename, 'wb') as w: + w.setparams((1, 8, 22050, 0, 'NONE', 'not compressed', + wave.WAVE_FORMAT_IEEE_FLOAT)) + self.assertEqual(w.getformat(), wave.WAVE_FORMAT_IEEE_FLOAT) + self.assertEqual(w.getsampwidth(), 8) + + def test_getparams_backward_compatible_shape(self): + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + filename = fp.name + self.addCleanup(unlink, filename) + + with wave.open(filename, 'wb') as w: + w.setparams((1, 4, 22050, 0, 'NONE', 'not compressed', + wave.WAVE_FORMAT_IEEE_FLOAT)) + params = w.getparams() + self.assertEqual(params, (1, 4, 22050, 0, 'NONE', 'not compressed')) + + def test_getformat_setformat(self): + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + filename = fp.name + self.addCleanup(unlink, filename) + + with wave.open(filename, 'wb') as w: + w.setnchannels(1) + w.setsampwidth(4) + w.setframerate(22050) + self.assertEqual(w.getformat(), wave.WAVE_FORMAT_PCM) + w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) + self.assertEqual(w.getformat(), wave.WAVE_FORMAT_IEEE_FLOAT) + + def test_setformat_ieee_requires_32_or_64_bit_sampwidth(self): + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + filename = fp.name + self.addCleanup(unlink, filename) + + with wave.open(filename, 'wb') as w: + w.setnchannels(1) + w.setsampwidth(2) + w.setframerate(22050) + with self.assertRaisesRegex(wave.Error, + 'unsupported sample width for IEEE float format'): + w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) + + def test_setsampwidth_ieee_requires_32_or_64_bit(self): + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + filename = fp.name + self.addCleanup(unlink, filename) + + with wave.open(filename, 'wb') as w: + w.setnchannels(1) + w.setframerate(22050) + w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) + with self.assertRaisesRegex(wave.Error, + 'unsupported sample width for IEEE float format'): + w.setsampwidth(2) + w.setsampwidth(4) + + def test_setsampwidth_ieee_accepts_64_bit(self): + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + filename = fp.name + self.addCleanup(unlink, filename) + + with wave.open(filename, 'wb') as w: + w.setnchannels(1) + w.setframerate(22050) + w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) + w.setsampwidth(8) + self.assertEqual(w.getsampwidth(), 8) + + def test_read_getformat(self): + b = b'RIFF' + struct.pack('<L', 36) + b'WAVE' + b += b'fmt ' + struct.pack('<LHHLLHH', 16, 1, 1, 11025, 11025, 1, 8) + b += b'data' + struct.pack('<L', 0) + with wave.open(io.BytesIO(b), 'rb') as r: + self.assertEqual(r.getformat(), wave.WAVE_FORMAT_PCM) + def test_read_no_chunks(self): b = b'SPAM' with self.assertRaises(EOFError): @@ -205,6 +341,139 @@ def test_open_in_write_raises(self): support.gc_collect() self.assertIsNone(cm.unraisable) + def test_ieee_float_has_fact_chunk(self): + nframes = 100 + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + filename = fp.name + self.addCleanup(unlink, filename) + + with wave.open(filename, 'wb') as w: + w.setnchannels(1) + w.setsampwidth(4) + w.setframerate(22050) + w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) + w.writeframes(b'\x00\x00\x00\x00' * nframes) + + with open(filename, 'rb') as f: + f.read(12) + fact_found = False + fact_samples = None + while True: + chunk_id = f.read(4) + if len(chunk_id) < 4: + break + chunk_size = struct.unpack('<L', f.read(4))[0] + if chunk_id == b'fact': + fact_found = True + fact_samples = struct.unpack('<L', f.read(4))[0] + break + f.seek(chunk_size + (chunk_size & 1), 1) + + self.assertTrue(fact_found) + self.assertEqual(fact_samples, nframes) + + def test_pcm_has_no_fact_chunk(self): + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + filename = fp.name + self.addCleanup(unlink, filename) + + with wave.open(filename, 'wb') as w: + w.setnchannels(1) + w.setsampwidth(2) + w.setframerate(22050) + w.writeframes(b'\x00\x00' * 100) + + with open(filename, 'rb') as f: + f.read(12) + while True: + chunk_id = f.read(4) + if len(chunk_id) < 4: + break + chunk_size = struct.unpack('<L', f.read(4))[0] + self.assertNotEqual(chunk_id, b'fact') + f.seek(chunk_size + (chunk_size & 1), 1) + + @support.subTests('arg', ( + # rounds to 0, should raise: + 0.5, + 0.4, + # Negative values should still raise: + -1, + -0.5, + -0.4, + # 0 should raise: + 0, + )) + def test_setframerate_validates_rounded_values(self, arg): + """Test that setframerate that round to 0 or negative are rejected""" + with wave.open(io.BytesIO(), 'wb') as f: + f.setnchannels(1) + f.setsampwidth(2) + with self.assertRaises(wave.Error): + f.setframerate(arg) + with self.assertRaises(wave.Error): + f.close() + + @support.subTests(('arg', 'expected'), ( + (1.4, 1), + (1.5, 2), + (1.6, 2), + (44100.4, 44100), + (44100.5, 44100), + (44100.6, 44101), + )) + def test_setframerate_rounds(self, arg, expected): + """Test that setframerate is rounded""" + with wave.open(io.BytesIO(), 'wb') as f: + f.setnchannels(1) + f.setsampwidth(2) + f.setframerate(arg) + self.assertEqual(f.getframerate(), expected) + + def test_write_odd_data_chunk_pads_and_updates_riff_size(self): + # gh-117716: odd-sized data chunks must be padded with one zero byte. + with io.BytesIO() as output: + with wave.open(output, mode='wb') as w: + w.setnchannels(1) + w.setsampwidth(1) + w.setframerate(48000) + w.writeframes(b'\x80') + + value = output.getvalue() + + self.assertEqual(value[-1], 0) + self.assertEqual( + int.from_bytes(value[4:8], byteorder='little'), + 38, + ) + + with wave.open(io.BytesIO(value), mode='rb') as r: + self.assertEqual(r.getnchannels(), 1) + self.assertEqual(r.getsampwidth(), 1) + self.assertEqual(r.getframerate(), 48000) + self.assertEqual(r.getnframes(), 1) + self.assertEqual(r.readframes(-1), b'\x80') + + +class WaveOpen(unittest.TestCase): + def test_open_pathlike(self): + """It is possible to use `wave.read` and `wave.write` with a path-like object""" + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: + cases = ( + FakePath(fp.name), + FakePath(os.fsencode(fp.name)), + os.fsencode(fp.name), + ) + for fake_path in cases: + with self.subTest(fake_path): + with wave.open(fake_path, 'wb') as f: + f.setnchannels(1) + f.setsampwidth(2) + f.setframerate(44100) + + with wave.open(fake_path, 'rb') as f: + pass + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 4c7c900eb56ae1..0c4717521f16f6 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -167,6 +167,11 @@ def test_basic_callback(self): self.check_basic_callback(create_function) self.check_basic_callback(create_bound_method) + def test_non_callable_callback(self): + c = C() + self.assertRaises(TypeError, weakref.ref, c, 42) + self.assertRaises(TypeError, weakref.proxy, c, 42) + @support.cpython_only def test_cfunction(self): _testcapi = import_helper.import_module("_testcapi") @@ -1044,6 +1049,32 @@ def callback(obj): stderr = res.err.decode("ascii", "backslashreplace") self.assertNotRegex(stderr, "_Py_Dealloc: Deallocator of type 'TestObj'") + def test_clearing_weakrefs_in_gc(self): + # This test checks that when finalizers are called: + # 1. weakrefs with callbacks have been cleared + # 2. weakrefs without callbacks have not been cleared + errors = [] + def test(): + class Class: + def __init__(self): + self._self = self + self.wr1 = weakref.ref(Class, lambda x: None) + self.wr2 = weakref.ref(Class) + + def __del__(self): + # we can't use assert* here, because gc will swallow + # exceptions + if self.wr1() is not None: + errors.append("weakref with callback as cleared") + if self.wr2() is not Class: + errors.append("weakref without callback was cleared") + + Class() + + test() + gc.collect() + self.assertEqual(errors, []) + class SubclassableWeakrefTestCase(TestBase): @@ -1789,6 +1820,11 @@ def test_weak_valued_union_operators(self): def test_weak_keyed_dict_update(self): self.check_update(weakref.WeakKeyDictionary, {C(): 1, C(): 2, C(): 3}) + d = weakref.WeakKeyDictionary() + msg = ("Keyword arguments are not supported: " + "cannot create weak reference to 'str' object") + with self.assertRaisesRegex(TypeError, msg): + d.update(k='v') def test_weak_keyed_delitem(self): d = weakref.WeakKeyDictionary() diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py index 6b577ae100e419..82f14ca968f266 100644 --- a/Lib/test/test_webbrowser.py +++ b/Lib/test/test_webbrowser.py @@ -5,8 +5,10 @@ import subprocess import sys import unittest +import warnings import webbrowser from test import support +from test.support import force_not_colorized_test_class from test.support import import_helper from test.support import is_apple_mobile from test.support import os_helper @@ -56,6 +58,14 @@ def _test(self, meth, *, args=[URL], kw={}, options, arguments): popen_args.pop(popen_args.index(option)) self.assertEqual(popen_args, arguments) + def test_reject_dash_prefixes(self): + browser = self.browser_class(name=CMD_NAME) + with self.assertRaisesRegex( + ValueError, + r"^Invalid URL \(leading dash disallowed\): '--key=val http.*'$" + ): + browser.open(f"--key=val {URL}") + class GenericBrowserCommandTest(CommandTestMixin, unittest.TestCase): @@ -110,6 +120,15 @@ def test_open_bad_new_parameter(self): arguments=[URL], kw=dict(new=999)) + def test_reject_action_dash_prefixes(self): + browser = self.browser_class(name=CMD_NAME) + with self.assertRaises(ValueError): + browser.open('%action--incognito') + # new=1: action is "--new-window", so "%action" itself expands to + # a dash-prefixed flag even with no dash in the original URL. + with self.assertRaises(ValueError): + browser.open('%action', new=1) + class EdgeCommandTest(CommandTestMixin, unittest.TestCase): @@ -319,8 +338,88 @@ def close(self): @unittest.skipUnless(sys.platform == "darwin", "macOS specific test") @requires_subprocess() -class MacOSXOSAScriptTest(unittest.TestCase): +class MacOSTest(unittest.TestCase): + + def setUp(self): + env = self.enterContext(os_helper.EnvironmentVarGuard()) + env.unset("BROWSER") + + def test_default(self): + browser = webbrowser.get() + self.assertIsInstance(browser, webbrowser.MacOS) + self.assertEqual(browser.name, 'default') + + def test_default_http_open(self): + # http/https URLs use /usr/bin/open directly — no bundle ID needed. + browser = webbrowser.MacOS('default') + with mock.patch('subprocess.run') as mock_run: + mock_run.return_value = mock.Mock(returncode=0) + result = browser.open(URL) + mock_run.assert_called_once_with( + ['/usr/bin/open', URL], + stderr=subprocess.DEVNULL, + ) + self.assertTrue(result) + + def test_default_non_http_uses_bundle_id(self): + # Non-http(s) URLs (e.g. file://) must be routed through the browser + # via -b <bundle-id> to prevent OS file handler dispatch. + file_url = 'file:///tmp/test.html' + browser = webbrowser.MacOS('default') + with mock.patch('webbrowser._macos_default_browser_bundle_id', + return_value='com.google.Chrome'), \ + mock.patch('subprocess.run') as mock_run: + mock_run.return_value = mock.Mock(returncode=0) + result = browser.open(file_url) + mock_run.assert_called_once_with( + ['/usr/bin/open', '-b', 'com.google.Chrome', file_url], + stderr=subprocess.DEVNULL, + ) + self.assertTrue(result) + + def test_named_known_browser_uses_bundle_id(self): + # Named browsers with a known bundle ID use /usr/bin/open -b. + browser = webbrowser.MacOS('safari') + with mock.patch('subprocess.run') as mock_run: + mock_run.return_value = mock.Mock(returncode=0) + result = browser.open(URL) + mock_run.assert_called_once_with( + ['/usr/bin/open', '-b', 'com.apple.Safari', URL], + stderr=subprocess.DEVNULL, + ) + self.assertTrue(result) + + def test_named_unknown_browser_falls_back_to_dash_a(self): + # Named browsers not in the bundle ID map fall back to -a. + browser = webbrowser.MacOS('lynx') + with mock.patch('subprocess.run') as mock_run: + mock_run.return_value = mock.Mock(returncode=0) + browser.open(URL) + mock_run.assert_called_once_with( + ['/usr/bin/open', '-a', 'lynx', URL], + stderr=subprocess.DEVNULL, + ) + + def test_open_failure(self): + browser = webbrowser.MacOS('default') + with mock.patch('subprocess.run') as mock_run: + mock_run.return_value = mock.Mock(returncode=1) + result = browser.open(URL) + self.assertFalse(result) + + +@unittest.skipUnless(sys.platform == "darwin", "macOS specific test") +@requires_subprocess() +class MacOSXOSAScriptDeprecationTest(unittest.TestCase): + + def test_deprecation_warning(self): + with self.assertWarns(DeprecationWarning): + webbrowser.MacOSXOSAScript('default') + +@unittest.skipUnless(sys.platform == "darwin", "macOS specific test") +@requires_subprocess() +class MacOSXOSAScriptTest(unittest.TestCase): def setUp(self): # Ensure that 'BROWSER' is not set to 'open' or something else. # See: https://github.com/python/cpython/issues/131254. @@ -328,22 +427,19 @@ def setUp(self): env.unset("BROWSER") support.patch(self, os, "popen", self.mock_popen) + self.enterContext(warnings.catch_warnings()) + warnings.simplefilter("ignore", DeprecationWarning) self.browser = webbrowser.MacOSXOSAScript("default") def mock_popen(self, cmd, mode): self.popen_pipe = MockPopenPipe(cmd, mode) return self.popen_pipe - def test_default(self): - browser = webbrowser.get() - assert isinstance(browser, webbrowser.MacOSXOSAScript) - self.assertEqual(browser.name, "default") - def test_default_open(self): url = "https://python.org" self.browser.open(url) self.assertTrue(self.popen_pipe._closed) - self.assertEqual(self.popen_pipe.cmd, "osascript") + self.assertEqual(self.popen_pipe.cmd, "/usr/bin/osascript") script = self.popen_pipe.pipe.getvalue() self.assertEqual(script.strip(), f'open location "{url}"') @@ -364,12 +460,21 @@ def test_default_browser_lookup(self): self.assertIn(f'open location "{url}"', script) def test_explicit_browser(self): - browser = webbrowser.MacOSXOSAScript("safari") + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + browser = webbrowser.MacOSXOSAScript("safari") browser.open("https://python.org") script = self.popen_pipe.pipe.getvalue() self.assertIn('tell application "safari"', script) self.assertIn('open location "https://python.org"', script) + def test_reject_dash_prefixes(self): + with self.assertRaisesRegex( + ValueError, + r"^Invalid URL \(leading dash disallowed\): '--key=val http.*'$" + ): + self.browser.open(f"--key=val {URL}") + class BrowserRegistrationTest(unittest.TestCase): @@ -503,6 +608,7 @@ def test_environment_preferred(self): self.assertEqual(webbrowser.get().name, sys.executable) +@force_not_colorized_test_class class CliTest(unittest.TestCase): def test_parse_args(self): for command, url, new_win in [ diff --git a/Lib/test/test_winapi.py b/Lib/test/test_winapi.py index e64208330ad2f9..a1c0b80d47e4d4 100644 --- a/Lib/test/test_winapi.py +++ b/Lib/test/test_winapi.py @@ -1,5 +1,6 @@ # Test the Windows-only _winapi module +import errno import os import pathlib import re @@ -156,3 +157,38 @@ def test_namedpipe(self): pipe2.write(b'testdata') pipe2.flush() self.assertEqual((b'testdata', 8), _winapi.PeekNamedPipe(pipe, 8)[:2]) + + def test_event_source_registration(self): + source_name = "PythonTestEventSource" + + handle = _winapi.RegisterEventSource(None, source_name) + self.addCleanup(_winapi.DeregisterEventSource, handle) + self.assertNotEqual(handle, _winapi.INVALID_HANDLE_VALUE) + + with self.assertRaises(OSError) as cm: + _winapi.RegisterEventSource(None, "") + self.assertEqual(cm.exception.errno, errno.EINVAL) + + with self.assertRaises(OSError) as cm: + _winapi.DeregisterEventSource(_winapi.INVALID_HANDLE_VALUE) + self.assertEqual(cm.exception.errno, errno.EBADF) + + def test_report_event(self): + source_name = "PythonTestEventSource" + + handle = _winapi.RegisterEventSource(None, source_name) + self.assertNotEqual(handle, _winapi.INVALID_HANDLE_VALUE) + self.addCleanup(_winapi.DeregisterEventSource, handle) + + _winapi.ReportEvent(handle, _winapi.EVENTLOG_SUCCESS, 1, 1002, + "Test message 1") + + with self.assertRaises(TypeError): + _winapi.ReportEvent(handle, _winapi.EVENTLOG_SUCCESS, 1, 1002, 42) + + with self.assertRaises(TypeError): + _winapi.ReportEvent(handle, _winapi.EVENTLOG_SUCCESS, 1, 1002, None) + + with self.assertRaises(ValueError): + _winapi.ReportEvent(handle, _winapi.EVENTLOG_SUCCESS, 1, 1002, + "Test message \0 with embedded null character") diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 924a962781a75b..fc0533c6e1531f 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -3,6 +3,7 @@ import gc import os, sys, errno +import itertools import threading import unittest from platform import machine, win32_edition @@ -209,6 +210,33 @@ def _test_named_args(self, key, sub_key): access=KEY_ALL_ACCESS) as okey: self.assertTrue(okey.handle != 0) + def test_hkey_comparison(self): + """Test HKEY comparison by handle value rather than object identity.""" + key1 = OpenKey(HKEY_CURRENT_USER, None) + key2 = OpenKey(HKEY_CURRENT_USER, None) + key3 = OpenKey(HKEY_LOCAL_MACHINE, None) + + self.addCleanup(CloseKey, key1) + self.addCleanup(CloseKey, key2) + self.addCleanup(CloseKey, key3) + + self.assertEqual(key1.handle, key2.handle) + self.assertTrue(key1 == key2) + self.assertFalse(key1 != key2) + + self.assertTrue(key1 != key3) + self.assertFalse(key1 == key3) + + # Closed keys should be equal (all have handle=0) + CloseKey(key1) + CloseKey(key2) + CloseKey(key3) + + self.assertEqual(key1.handle, 0) + self.assertEqual(key2.handle, 0) + self.assertEqual(key3.handle, 0) + self.assertEqual(key2, key3) + class LocalWinregTests(BaseWinregTests): @@ -291,6 +319,37 @@ def run(self): DeleteKey(HKEY_CURRENT_USER, test_key_name+'\\changing_value') DeleteKey(HKEY_CURRENT_USER, test_key_name) + def test_queryvalueex_race_condition(self): + # gh-142282: QueryValueEx could read garbage buffer under race + # condition when another thread changes the value size + done = False + ready = threading.Event() + values = [b'ham', b'spam'] + + class WriterThread(threading.Thread): + def run(self): + with CreateKey(HKEY_CURRENT_USER, test_key_name) as key: + values_iter = itertools.cycle(values) + while not done: + val = next(values_iter) + SetValueEx(key, 'test_value', 0, REG_BINARY, val) + ready.set() + + thread = WriterThread() + thread.start() + try: + ready.wait() + with CreateKey(HKEY_CURRENT_USER, test_key_name) as key: + for _ in range(1000): + result, typ = QueryValueEx(key, 'test_value') + # The result must be one of the written values, + # not garbage data from uninitialized buffer + self.assertIn(result, values) + finally: + done = True + thread.join() + DeleteKey(HKEY_CURRENT_USER, test_key_name) + def test_long_key(self): # Issue2810, in 2.6 and 3.1 when the key name was exactly 256 # characters, EnumKey raised "WindowsError: More data is @@ -517,6 +576,21 @@ def test_exception_numbers(self): with self.assertRaises(FileNotFoundError) as ctx: QueryValue(HKEY_CLASSES_ROOT, 'some_value_that_does_not_exist') + def test_delete_tree(self): + with CreateKey(HKEY_CURRENT_USER, test_key_name) as main_key: + with CreateKey(main_key, "subkey1") as subkey1: + SetValueEx(subkey1, "value1", 0, REG_SZ, "test_value1") + with CreateKey(subkey1, "subsubkey1") as subsubkey1: + SetValueEx(subsubkey1, "value2", 0, REG_DWORD, 42) + + with CreateKey(main_key, "subkey2") as subkey2: + SetValueEx(subkey2, "value3", 0, REG_SZ, "test_value3") + + DeleteTree(HKEY_CURRENT_USER, test_key_name) + + with self.assertRaises(OSError): + OpenKey(HKEY_CURRENT_USER, test_key_name) + if __name__ == "__main__": if not REMOTE_NAME: diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py index e04a4d2c2218a3..32ef0ccf4e638d 100644 --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -1,6 +1,6 @@ from unittest import mock from test import support -from test.support import socket_helper +from test.support import force_not_colorized, socket_helper, control_characters_c0 from test.test_httpservers import NoLogRequestHandler from unittest import TestCase from wsgiref.util import setup_testing_defaults @@ -109,7 +109,7 @@ def check_hello(self, out, has_length=True): sys.version.split()[0]) self.assertEqual(out, ("HTTP/1.0 200 OK\r\n" - "Server: WSGIServer/0.2 " + pyver +"\r\n" + "Server: WSGIServer " + pyver + "\r\n" "Content-Type: text/plain\r\n" "Date: Mon, 05 Jun 2006 18:49:54 GMT\r\n" + (has_length and "Content-Length: 13\r\n" or "") + @@ -192,6 +192,7 @@ def bad_app(e,s): err.splitlines()[-2], "AssertionError" ) + @force_not_colorized def test_bytes_validation(self): def app(e, s): s("200 OK", [ @@ -206,7 +207,7 @@ def app(e, s): pyver = py + b"/" + ver self.assertEqual( b"HTTP/1.0 200 OK\r\n" - b"Server: WSGIServer/0.2 "+ pyver + b"\r\n" + b"Server: WSGIServer " + pyver + b"\r\n" b"Content-Type: text/plain; charset=utf-8\r\n" b"Date: Wed, 24 Dec 2008 13:29:32 GMT\r\n" b"\r\n" @@ -503,6 +504,22 @@ def testExtras(self): '\r\n' ) + def testRaisesControlCharacters(self): + for c0 in control_characters_c0(): + with self.subTest(c0): + headers = Headers() + self.assertRaises(ValueError, headers.__setitem__, f"key{c0}", "val") + self.assertRaises(ValueError, headers.add_header, f"key{c0}", "val", param="param") + # HTAB (\x09) is allowed in values, not names. + if c0 == "\t": + headers["key"] = f"val{c0}" + headers.add_header("key", f"val{c0}") + headers.setdefault(f"key", f"val{c0}") + else: + self.assertRaises(ValueError, headers.__setitem__, "key", f"val{c0}") + self.assertRaises(ValueError, headers.add_header, "key", f"val{c0}", param="param") + self.assertRaises(ValueError, headers.add_header, "key", "val", param=f"param{c0}") + class ErrorHandler(BaseCGIHandler): """Simple handler subclass for testing BaseHandler""" @@ -839,6 +856,37 @@ def write(self, b): self.assertIsNotNone(h.status) self.assertIsNotNone(h.environ) + def testRaisesControlCharacters(self): + for c0 in control_characters_c0(): + with self.subTest(c0): + base = BaseHandler() + with self.assertRaises(ValueError): + base.start_response(c0, [('x', 'y')]) + + base = BaseHandler() + with self.assertRaises(ValueError): + base.start_response('200 OK', [(c0, 'y')]) + + # HTAB (\x09) is allowed in header values, but not in names. + base = BaseHandler() + if c0 != "\t": + with self.assertRaises(ValueError): + base.start_response('200 OK', [('x', c0)]) + else: + base.start_response('200 OK', [('x', c0)]) + + +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + from wsgiref import simple_server + + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(simple_server, "__version__") + self.assertEqual(cm.filename, __file__) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_xml.py b/Lib/test/test_xml.py new file mode 100644 index 00000000000000..3a8b92048166f2 --- /dev/null +++ b/Lib/test/test_xml.py @@ -0,0 +1,43 @@ +import xml +import unittest + + +class TestUtils(unittest.TestCase): + + def test_is_valid_name(self): + is_valid_name = xml.is_valid_name + self.assertFalse(is_valid_name('')) + self.assertTrue(is_valid_name('name')) + self.assertTrue(is_valid_name('NAME')) + self.assertTrue(is_valid_name('name0:-._·')) + self.assertTrue(is_valid_name('_')) + self.assertTrue(is_valid_name(':')) + self.assertTrue(is_valid_name('Ñàḿĕ')) + self.assertTrue(is_valid_name('\U000EFFFF')) + self.assertFalse(is_valid_name('0')) + self.assertFalse(is_valid_name('-')) + self.assertFalse(is_valid_name('.')) + self.assertFalse(is_valid_name('·')) + self.assertFalse(is_valid_name('na me')) + for c in '<>/!?=\x00\x01\x7f\ud800\udfff\ufffe\uffff\U000F0000': + self.assertFalse(is_valid_name('name' + c)) + + def test_is_valid_text(self): + is_valid_text = xml.is_valid_text + self.assertTrue(is_valid_text('')) + self.assertTrue(is_valid_text('!0Aa_~ \r\n\t\x85\xa0')) + self.assertTrue(is_valid_text('\ud7ff\ue000\ufffd\U00010000\U0010ffff')) + self.assertFalse(is_valid_text('\x00')) + self.assertFalse(is_valid_text('\x01')) + self.assertFalse(is_valid_text('\x1f')) + self.assertTrue(is_valid_text('\x7f')) + self.assertTrue(is_valid_text('\x80')) + self.assertTrue(is_valid_text('\x9f')) + self.assertFalse(is_valid_text('\ud800')) + self.assertFalse(is_valid_text('\udfff')) + self.assertFalse(is_valid_text('\ufffe')) + self.assertFalse(is_valid_text('\uffff')) + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index bf6d5074fdebd8..acec4ec2ca257c 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -370,8 +370,7 @@ def test_simpleops(self): self.serialize_check(element, '<tag key="value"><subtag /></tag>') # 4 element.remove(subelement) self.serialize_check(element, '<tag key="value" />') # 5 - with self.assertRaisesRegex(ValueError, - r'Element\.remove\(.+\): element not found'): + with self.assertRaises(ValueError): element.remove(subelement) self.serialize_check(element, '<tag key="value" />') # 6 element[0:0] = [subelement, subelement, subelement] @@ -382,6 +381,19 @@ def test_simpleops(self): self.serialize_check(element, '<tag key="value"><subtag /><subtag /></tag>') + def test_positional_only_parameter(self): + # Test Element positional-only parameters (gh-144846). + + # 'tag' is positional-only + with self.assertRaises(TypeError): + ET.Element(tag='fail') + + # 'tag' and 'attrib' as kwarg/attribute names + e = ET.Element('e', attrib={'attrib': 'foo'}, tag='bar') + self.assertEqual(e.tag, 'e') + self.assertEqual(e.get('attrib'), 'foo') + self.assertEqual(e.get('tag'), 'bar') + def test_cdata(self): # Test CDATA handling (etc). @@ -485,6 +497,28 @@ def test_attrib(self): self.assertEqual(ET.tostring(elem), b'<test a="&#13;" b="&#13;&#10;" c="&#09;&#10;&#13; " d="&#10;&#10;&#13;&#13;&#09;&#09; " />') + def test_subelement_positional_only_parameter(self): + # Test SubElement positional-only parameters (gh-144270). + parent = ET.Element('parent') + + # 'parent' and 'tag' are positional-only + with self.assertRaises(TypeError): + ET.SubElement(parent=parent, tag='fail') + with self.assertRaises(TypeError): + ET.SubElement(parent, tag='fail') + + # 'attrib' can be passed as keyword + sub1 = ET.SubElement(parent, 'sub1', attrib={'key': 'value'}) + self.assertEqual(sub1.get('key'), 'value') + + # 'tag' and 'parent' as kwargs become XML attributes, not func params + sub2 = ET.SubElement(parent, 'sub2', attrib={'attrib': 'foo'}, + tag='bar', parent='baz') + self.assertEqual(sub2.tag, 'sub2') + self.assertEqual(sub2.get('attrib'), 'foo') + self.assertEqual(sub2.get('tag'), 'bar') + self.assertEqual(sub2.get('parent'), 'baz') + def test_makeelement(self): # Test makeelement handling. @@ -574,208 +608,6 @@ def test_parseliteral(self): self.assertEqual(len(ids), 1) self.assertEqual(ids["body"].tag, 'body') - def test_iterparse(self): - # Test iterparse interface. - - iterparse = ET.iterparse - - context = iterparse(SIMPLE_XMLFILE) - self.assertIsNone(context.root) - action, elem = next(context) - self.assertIsNone(context.root) - self.assertEqual((action, elem.tag), ('end', 'element')) - self.assertEqual([(action, elem.tag) for action, elem in context], [ - ('end', 'element'), - ('end', 'empty-element'), - ('end', 'root'), - ]) - self.assertEqual(context.root.tag, 'root') - - context = iterparse(SIMPLE_NS_XMLFILE) - self.assertEqual([(action, elem.tag) for action, elem in context], [ - ('end', '{namespace}element'), - ('end', '{namespace}element'), - ('end', '{namespace}empty-element'), - ('end', '{namespace}root'), - ]) - - with open(SIMPLE_XMLFILE, 'rb') as source: - context = iterparse(source) - action, elem = next(context) - self.assertEqual((action, elem.tag), ('end', 'element')) - self.assertEqual([(action, elem.tag) for action, elem in context], [ - ('end', 'element'), - ('end', 'empty-element'), - ('end', 'root'), - ]) - self.assertEqual(context.root.tag, 'root') - - events = () - context = iterparse(SIMPLE_XMLFILE, events) - self.assertEqual([(action, elem.tag) for action, elem in context], []) - - events = () - context = iterparse(SIMPLE_XMLFILE, events=events) - self.assertEqual([(action, elem.tag) for action, elem in context], []) - - events = ("start", "end") - context = iterparse(SIMPLE_XMLFILE, events) - self.assertEqual([(action, elem.tag) for action, elem in context], [ - ('start', 'root'), - ('start', 'element'), - ('end', 'element'), - ('start', 'element'), - ('end', 'element'), - ('start', 'empty-element'), - ('end', 'empty-element'), - ('end', 'root'), - ]) - - events = ("start", "end", "start-ns", "end-ns") - context = iterparse(SIMPLE_NS_XMLFILE, events) - self.assertEqual([(action, elem.tag) if action in ("start", "end") - else (action, elem) - for action, elem in context], [ - ('start-ns', ('', 'namespace')), - ('start', '{namespace}root'), - ('start', '{namespace}element'), - ('end', '{namespace}element'), - ('start', '{namespace}element'), - ('end', '{namespace}element'), - ('start', '{namespace}empty-element'), - ('end', '{namespace}empty-element'), - ('end', '{namespace}root'), - ('end-ns', None), - ]) - - events = ('start-ns', 'end-ns') - context = iterparse(io.StringIO(r"<root xmlns=''/>"), events) - res = [action for action, elem in context] - self.assertEqual(res, ['start-ns', 'end-ns']) - - events = ("start", "end", "bogus") - with open(SIMPLE_XMLFILE, "rb") as f: - with self.assertRaises(ValueError) as cm: - iterparse(f, events) - self.assertFalse(f.closed) - self.assertEqual(str(cm.exception), "unknown event 'bogus'") - - with warnings_helper.check_no_resource_warning(self): - with self.assertRaises(ValueError) as cm: - iterparse(SIMPLE_XMLFILE, events) - self.assertEqual(str(cm.exception), "unknown event 'bogus'") - del cm - - source = io.BytesIO( - b"<?xml version='1.0' encoding='iso-8859-1'?>\n" - b"<body xmlns='http://&#233;ffbot.org/ns'\n" - b" xmlns:cl\xe9='http://effbot.org/ns'>text</body>\n") - events = ("start-ns",) - context = iterparse(source, events) - self.assertEqual([(action, elem) for action, elem in context], [ - ('start-ns', ('', 'http://\xe9ffbot.org/ns')), - ('start-ns', ('cl\xe9', 'http://effbot.org/ns')), - ]) - - source = io.StringIO("<document />junk") - it = iterparse(source) - action, elem = next(it) - self.assertEqual((action, elem.tag), ('end', 'document')) - with self.assertRaises(ET.ParseError) as cm: - next(it) - self.assertEqual(str(cm.exception), - 'junk after document element: line 1, column 12') - - self.addCleanup(os_helper.unlink, TESTFN) - with open(TESTFN, "wb") as f: - f.write(b"<document />junk") - it = iterparse(TESTFN) - action, elem = next(it) - self.assertEqual((action, elem.tag), ('end', 'document')) - with warnings_helper.check_no_resource_warning(self): - with self.assertRaises(ET.ParseError) as cm: - next(it) - self.assertEqual(str(cm.exception), - 'junk after document element: line 1, column 12') - del cm, it - - # Not exhausting the iterator still closes the resource (bpo-43292) - with warnings_helper.check_no_resource_warning(self): - it = iterparse(SIMPLE_XMLFILE) - del it - - with warnings_helper.check_no_resource_warning(self): - it = iterparse(SIMPLE_XMLFILE) - it.close() - del it - - with warnings_helper.check_no_resource_warning(self): - it = iterparse(SIMPLE_XMLFILE) - action, elem = next(it) - self.assertEqual((action, elem.tag), ('end', 'element')) - del it, elem - - with warnings_helper.check_no_resource_warning(self): - it = iterparse(SIMPLE_XMLFILE) - action, elem = next(it) - it.close() - self.assertEqual((action, elem.tag), ('end', 'element')) - del it, elem - - with self.assertRaises(FileNotFoundError): - iterparse("nonexistent") - - def test_iterparse_close(self): - iterparse = ET.iterparse - - it = iterparse(SIMPLE_XMLFILE) - it.close() - with self.assertRaises(StopIteration): - next(it) - it.close() # idempotent - - with open(SIMPLE_XMLFILE, 'rb') as source: - it = iterparse(source) - it.close() - self.assertFalse(source.closed) - with self.assertRaises(StopIteration): - next(it) - it.close() # idempotent - - it = iterparse(SIMPLE_XMLFILE) - action, elem = next(it) - self.assertEqual((action, elem.tag), ('end', 'element')) - it.close() - with self.assertRaises(StopIteration): - next(it) - it.close() # idempotent - - with open(SIMPLE_XMLFILE, 'rb') as source: - it = iterparse(source) - action, elem = next(it) - self.assertEqual((action, elem.tag), ('end', 'element')) - it.close() - self.assertFalse(source.closed) - with self.assertRaises(StopIteration): - next(it) - it.close() # idempotent - - it = iterparse(SIMPLE_XMLFILE) - list(it) - it.close() - with self.assertRaises(StopIteration): - next(it) - it.close() # idempotent - - with open(SIMPLE_XMLFILE, 'rb') as source: - it = iterparse(source) - list(it) - it.close() - self.assertFalse(source.closed) - with self.assertRaises(StopIteration): - next(it) - it.close() # idempotent - def test_writefile(self): elem = ET.Element("tag") elem.text = "text" @@ -1176,13 +1008,15 @@ def check(encoding, body=''): check("iso-8859-15", '\u20ac') check("cp437", '\u221a') check("mac-roman", '\u02da') + check('shift-jis-2004', '\u203e\u3406\uff66') + check('euc-jis-2004', '\u3406\uff66') - def xml(encoding): - return "<?xml version='1.0' encoding='%s'?><xml />" % encoding - def bxml(encoding): - return xml(encoding).encode(encoding) + def xml(encoding, body=''): + return "<?xml version='1.0' encoding='%s'?><xml>%s</xml>" % (encoding, body) + def bxml(encoding, body=''): + return xml(encoding, body).encode(encoding) supported_encodings = [ - 'ascii', 'utf-8', 'utf-8-sig', 'utf-16', 'utf-16be', 'utf-16le', + 'utf-8', 'utf-16', 'utf-16be', 'utf-16le', 'iso8859-1', 'iso8859-2', 'iso8859-3', 'iso8859-4', 'iso8859-5', 'iso8859-6', 'iso8859-7', 'iso8859-8', 'iso8859-9', 'iso8859-10', 'iso8859-13', 'iso8859-14', 'iso8859-15', 'iso8859-16', @@ -1193,36 +1027,54 @@ def bxml(encoding): 'cp1256', 'cp1257', 'cp1258', 'mac-cyrillic', 'mac-greek', 'mac-iceland', 'mac-latin2', 'mac-roman', 'mac-turkish', - 'iso2022-jp', 'iso2022-jp-1', 'iso2022-jp-2', 'iso2022-jp-2004', - 'iso2022-jp-3', 'iso2022-jp-ext', - 'koi8-r', 'koi8-t', 'koi8-u', 'kz1048', - 'hz', 'ptcp154', + 'koi8-r', 'koi8-t', 'koi8-u', 'kz1048', 'ptcp154', + 'big5', 'big5hkscs', + 'cp932', 'cp949', 'cp950', + 'euc-jp', 'euc-jis-2004', 'euc-jisx0213', + 'gb2312', 'gbk', 'johab', + 'shift-jis', 'shift-jis-2004', 'shift-jisx0213', + 'utf-8-sig', 'utf8', ] for encoding in supported_encodings: - self.assertEqual(ET.tostring(ET.XML(bxml(encoding))), b'<xml />') + with self.subTest(encoding=encoding): + self.assertEqual(ET.tostring(ET.XML(bxml(encoding))), b'<xml />') + c = 'éπя\u05d0\u060c€'.encode(encoding, 'ignore').decode(encoding)[0] + self.assertEqual(ET.tostring(ET.XML(bxml(encoding, c))), + ('<xml>&#%d;</xml>' % ord(c)).encode()) unsupported_ascii_compatible_encodings = [ - 'big5', 'big5hkscs', - 'cp932', 'cp949', 'cp950', - 'euc-jp', 'euc-jis-2004', 'euc-jisx0213', 'euc-kr', - 'gb2312', 'gbk', 'gb18030', - 'iso2022-kr', 'johab', - 'shift-jis', 'shift-jis-2004', 'shift-jisx0213', + 'euc-kr', 'gb18030', + 'iso2022-jp', 'iso2022-jp-1', 'iso2022-jp-2', 'iso2022-jp-2004', + 'iso2022-jp-3', 'iso2022-jp-ext', + 'iso2022-kr', 'hz', 'utf-7', ] for encoding in unsupported_ascii_compatible_encodings: - self.assertRaises(ValueError, ET.XML, bxml(encoding)) + with self.subTest(encoding=encoding): + self.assertRaises(ValueError, ET.XML, bxml(encoding)) unsupported_ascii_incompatible_encodings = [ 'cp037', 'cp424', 'cp500', 'cp864', 'cp875', 'cp1026', 'cp1140', 'utf_32', 'utf_32_be', 'utf_32_le', ] for encoding in unsupported_ascii_incompatible_encodings: - self.assertRaises(ET.ParseError, ET.XML, bxml(encoding)) + with self.subTest(encoding=encoding): + self.assertRaises(ET.ParseError, ET.XML, bxml(encoding)) self.assertRaises(ValueError, ET.XML, xml('undefined').encode('ascii')) self.assertRaises(LookupError, ET.XML, xml('xxx').encode('ascii')) + @support.subTests('sample,exception', [ + (b'<x> \xa1</x>', UnicodeDecodeError), # crashed + (b'<x> \xa1</x', UnicodeDecodeError), # crashed + (b'<x> \xa1', None), # ET.ParseError + ]) + def test_multibyte_encoding_errors(self, sample, exception): + exception = exception or ET.ParseError + data = b'<?xml version="1.0" encoding="EUC-JP"?>\n' + sample + with self.assertRaises(exception): + ET.XML(data) + def test_methods(self): # Test serialization methods. @@ -1446,7 +1298,15 @@ def check(p, expected, namespaces=None): {'': 'http://www.w3.org/2001/XMLSchema', 'ns': 'http://www.w3.org/2001/XMLSchema'}) - def test_processinginstruction(self): + def test_comment_serialization(self): + comm = ET.Comment('<spam> & ham') + # comments are not escaped + self.assertEqual(ET.tostring(comm), b'<!--<spam> & ham-->') + self.assertEqual(ET.tostring(comm, method='html'), b'<!--<spam> & ham-->') + # no comments in text serialization + self.assertEqual(ET.tostring(comm, method='text'), b'') + + def test_processinginstruction_serialization(self): # Test ProcessingInstruction directly self.assertEqual(ET.tostring(ET.ProcessingInstruction('test', 'instruction')), @@ -1455,12 +1315,32 @@ def test_processinginstruction(self): b'<?test instruction?>') # Issue #2746 - + # processing instructions are not escaped self.assertEqual(ET.tostring(ET.PI('test', '<testing&>')), b'<?test <testing&>?>') self.assertEqual(ET.tostring(ET.PI('test', '<testing&>\xe3'), 'latin-1'), b"<?xml version='1.0' encoding='latin-1'?>\n" b"<?test <testing&>\xe3?>") + pi = ET.PI('test', 'ham & eggs < spam') + self.assertEqual(ET.tostring(pi), b'<?test ham & eggs < spam?>') + self.assertEqual(ET.tostring(pi, method='html'), b'<?test ham & eggs < spam?>') + # no processing instructions in text serialization + self.assertEqual(ET.tostring(pi, method='text'), b'') + + def test_empty_attribute_serialization(self): + # empty attrs only work in html + elem = ET.Element('tag', attrib={'attr': None}) + self.assertRaises(TypeError, ET.tostring, elem) + self.assertEqual(ET.tostring(elem, method='html'), b'<tag attr></tag>') + + @support.subTests('tag', ("script", "style", "xmp", "iframe", "noembed", "noframes")) + def test_html_cdata_elems_serialization(self, tag): + # content of raw text elements is not escaped in html + tag = tag.title() + elem = ET.Element(tag) + elem.text = '<spam>&ham' + self.assertEqual(ET.tostring(elem, method='html'), + ('<%s><spam>&ham</%s>' % (tag, tag)).encode()) def test_html_empty_elems_serialization(self): # issue 15970 @@ -1476,6 +1356,14 @@ def test_html_empty_elems_serialization(self): method='html') self.assertEqual(serialized, expected) + def test_html_plaintext_serialization(self): + # content of plaintext is not escaped in html + # no end tag for plaintext + elem = ET.Element('PlainText') + elem.text = '<spam>&ham' + self.assertEqual(ET.tostring(elem, method='html'), + b'<PlainText><spam>&ham') + def test_dump_attribute_order(self): # See BPO 34160 e = ET.Element('cirriculum', status='public', company='example') @@ -1499,6 +1387,281 @@ def test_attlist_default(self): {'{http://www.w3.org/XML/1998/namespace}lang': 'eng'}) +class IterparseTest(unittest.TestCase): + # Test iterparse interface. + + def test_basic(self): + iterparse = ET.iterparse + + it = iterparse(SIMPLE_XMLFILE) + self.assertIsNone(it.root) + action, elem = next(it) + self.assertIsNone(it.root) + self.assertEqual((action, elem.tag), ('end', 'element')) + self.assertEqual([(action, elem.tag) for action, elem in it], [ + ('end', 'element'), + ('end', 'empty-element'), + ('end', 'root'), + ]) + self.assertEqual(it.root.tag, 'root') + it.close() + + it = iterparse(SIMPLE_NS_XMLFILE) + self.assertEqual([(action, elem.tag) for action, elem in it], [ + ('end', '{namespace}element'), + ('end', '{namespace}element'), + ('end', '{namespace}empty-element'), + ('end', '{namespace}root'), + ]) + it.close() + + def test_external_file(self): + with open(SIMPLE_XMLFILE, 'rb') as source: + it = ET.iterparse(source) + action, elem = next(it) + self.assertEqual((action, elem.tag), ('end', 'element')) + self.assertEqual([(action, elem.tag) for action, elem in it], [ + ('end', 'element'), + ('end', 'empty-element'), + ('end', 'root'), + ]) + self.assertEqual(it.root.tag, 'root') + + def test_events(self): + iterparse = ET.iterparse + + events = () + it = iterparse(SIMPLE_XMLFILE, events) + self.assertEqual([(action, elem.tag) for action, elem in it], []) + it.close() + + events = () + it = iterparse(SIMPLE_XMLFILE, events=events) + self.assertEqual([(action, elem.tag) for action, elem in it], []) + it.close() + + events = ("start", "end") + it = iterparse(SIMPLE_XMLFILE, events) + self.assertEqual([(action, elem.tag) for action, elem in it], [ + ('start', 'root'), + ('start', 'element'), + ('end', 'element'), + ('start', 'element'), + ('end', 'element'), + ('start', 'empty-element'), + ('end', 'empty-element'), + ('end', 'root'), + ]) + it.close() + + def test_namespace_events(self): + iterparse = ET.iterparse + + events = ("start", "end", "start-ns", "end-ns") + it = iterparse(SIMPLE_NS_XMLFILE, events) + self.assertEqual([(action, elem.tag) if action in ("start", "end") + else (action, elem) + for action, elem in it], [ + ('start-ns', ('', 'namespace')), + ('start', '{namespace}root'), + ('start', '{namespace}element'), + ('end', '{namespace}element'), + ('start', '{namespace}element'), + ('end', '{namespace}element'), + ('start', '{namespace}empty-element'), + ('end', '{namespace}empty-element'), + ('end', '{namespace}root'), + ('end-ns', None), + ]) + it.close() + + events = ('start-ns', 'end-ns') + it = iterparse(io.BytesIO(br"<root xmlns=''/>"), events) + res = [action for action, elem in it] + self.assertEqual(res, ['start-ns', 'end-ns']) + it.close() + + def test_unknown_events(self): + iterparse = ET.iterparse + + events = ("start", "end", "bogus") + with open(SIMPLE_XMLFILE, "rb") as f: + with self.assertRaises(ValueError) as cm: + iterparse(f, events) + self.assertFalse(f.closed) + self.assertEqual(str(cm.exception), "unknown event 'bogus'") + + with warnings_helper.check_no_resource_warning(self): + with self.assertRaises(ValueError) as cm: + iterparse(SIMPLE_XMLFILE, events) + self.assertEqual(str(cm.exception), "unknown event 'bogus'") + del cm + gc_collect() + + def test_non_utf8(self): + source = io.BytesIO( + b"<?xml version='1.0' encoding='iso-8859-1'?>\n" + b"<body xmlns='http://&#233;ffbot.org/ns'\n" + b" xmlns:cl\xe9='http://effbot.org/ns'>text</body>\n") + events = ("start-ns",) + it = ET.iterparse(source, events) + self.assertEqual([(action, elem) for action, elem in it], [ + ('start-ns', ('', 'http://\xe9ffbot.org/ns')), + ('start-ns', ('cl\xe9', 'http://effbot.org/ns')), + ]) + + def test_parsing_error(self): + source = io.BytesIO(b"<document />junk") + it = ET.iterparse(source) + action, elem = next(it) + self.assertEqual((action, elem.tag), ('end', 'document')) + with self.assertRaises(ET.ParseError) as cm: + next(it) + self.assertEqual(str(cm.exception), + 'junk after document element: line 1, column 12') + + def test_nonexistent_file(self): + with self.assertRaises(FileNotFoundError): + ET.iterparse("nonexistent") + + def test_resource_warnings_not_exhausted(self): + # Not exhausting the iterator still closes the underlying file (bpo-43292) + # Not closing before del should emit ResourceWarning + it = ET.iterparse(SIMPLE_XMLFILE) + with warnings_helper.check_no_resource_warning(self): + it.close() + del it + gc_collect() + + it = ET.iterparse(SIMPLE_XMLFILE) + with self.assertWarns(ResourceWarning) as wm: + del it + gc_collect() + # Not 'unclosed file'. + self.assertIn('unclosed iterparse iterator', str(wm.warning)) + self.assertIn(repr(SIMPLE_XMLFILE), str(wm.warning)) + self.assertEqual(wm.filename, __file__) + + it = ET.iterparse(SIMPLE_XMLFILE) + with warnings_helper.check_no_resource_warning(self): + action, elem = next(it) + it.close() + self.assertEqual((action, elem.tag), ('end', 'element')) + del it, elem + gc_collect() + + it = ET.iterparse(SIMPLE_XMLFILE) + with self.assertWarns(ResourceWarning) as wm: + action, elem = next(it) + self.assertEqual((action, elem.tag), ('end', 'element')) + del it, elem + gc_collect() + self.assertIn('unclosed iterparse iterator', str(wm.warning)) + self.assertIn(repr(SIMPLE_XMLFILE), str(wm.warning)) + self.assertEqual(wm.filename, __file__) + + def test_resource_warnings_failed_iteration(self): + self.addCleanup(os_helper.unlink, TESTFN) + with open(TESTFN, "wb") as f: + f.write(b"<document />junk") + + it = ET.iterparse(TESTFN) + action, elem = next(it) + self.assertEqual((action, elem.tag), ('end', 'document')) + with warnings_helper.check_no_resource_warning(self): + with self.assertRaises(ET.ParseError) as cm: + next(it) + self.assertEqual(str(cm.exception), + 'junk after document element: line 1, column 12') + it.close() + del cm, it + gc_collect() + + it = ET.iterparse(TESTFN) + action, elem = next(it) + self.assertEqual((action, elem.tag), ('end', 'document')) + with self.assertWarns(ResourceWarning) as wm: + with self.assertRaises(ET.ParseError) as cm: + next(it) + self.assertEqual(str(cm.exception), + 'junk after document element: line 1, column 12') + del cm, it + gc_collect() + self.assertIn('unclosed iterparse iterator', str(wm.warning)) + self.assertIn(repr(TESTFN), str(wm.warning)) + self.assertEqual(wm.filename, __file__) + + def test_resource_warnings_exhausted(self): + it = ET.iterparse(SIMPLE_XMLFILE) + with warnings_helper.check_no_resource_warning(self): + list(it) + it.close() + del it + gc_collect() + + it = ET.iterparse(SIMPLE_XMLFILE) + with self.assertWarns(ResourceWarning) as wm: + list(it) + del it + gc_collect() + self.assertIn('unclosed iterparse iterator', str(wm.warning)) + self.assertIn(repr(SIMPLE_XMLFILE), str(wm.warning)) + self.assertEqual(wm.filename, __file__) + + def test_close_not_exhausted(self): + iterparse = ET.iterparse + + it = iterparse(SIMPLE_XMLFILE) + it.close() + with self.assertRaises(StopIteration): + next(it) + it.close() # idempotent + + with open(SIMPLE_XMLFILE, 'rb') as source: + it = iterparse(source) + it.close() + self.assertFalse(source.closed) + with self.assertRaises(StopIteration): + next(it) + it.close() # idempotent + + it = iterparse(SIMPLE_XMLFILE) + action, elem = next(it) + self.assertEqual((action, elem.tag), ('end', 'element')) + it.close() + with self.assertRaises(StopIteration): + next(it) + it.close() # idempotent + + with open(SIMPLE_XMLFILE, 'rb') as source: + it = iterparse(source) + action, elem = next(it) + self.assertEqual((action, elem.tag), ('end', 'element')) + it.close() + self.assertFalse(source.closed) + with self.assertRaises(StopIteration): + next(it) + it.close() # idempotent + + def test_close_exhausted(self): + iterparse = ET.iterparse + it = iterparse(SIMPLE_XMLFILE) + list(it) + it.close() + with self.assertRaises(StopIteration): + next(it) + it.close() # idempotent + + with open(SIMPLE_XMLFILE, 'rb') as source: + it = iterparse(source) + list(it) + it.close() + self.assertFalse(source.closed) + with self.assertRaises(StopIteration): + next(it) + it.close() # idempotent + + class XMLPullParserTest(unittest.TestCase): def _feed(self, parser, data, chunk_size=None, flush=False): @@ -1749,6 +1912,8 @@ def __next__(self): def test_unknown_event(self): with self.assertRaises(ValueError): ET.XMLPullParser(events=('start', 'end', 'bogus')) + with self.assertRaisesRegex(ValueError, "unknown event 'bogus'"): + ET.XMLPullParser(events=(x.decode() for x in (b'start', b'end', b'bogus'))) @unittest.skipIf(pyexpat.version_info < (2, 6, 0), f'Expat {pyexpat.version_info} does not ' @@ -2683,6 +2848,17 @@ def test_pickle_issue18997(self): self.assertEqual(e2.tag, 'group') self.assertEqual(e2[0].tag, 'dogs') + def test_remove_errors(self): + e = ET.Element('tag') + with self.assertRaisesRegex(ValueError, + r"<Element 'subtag'.*> not in <Element 'tag'.*>"): + e.remove(ET.Element('subtag')) + with self.assertRaisesRegex(TypeError, + r".*\bElement, not type"): + e.remove(ET.Element) + with self.assertRaisesRegex(TypeError, + r".*\bElement, not int"): + e.remove(1) class BadElementTest(ElementTestCase, unittest.TestCase): @@ -2935,32 +3111,72 @@ def __del__(self): elem = b.close() self.assertEqual(elem[0].tail, 'ABCDEFGHIJKL') - def test_subscr(self): - # Issue #27863 + def test_subscr_with_clear(self): + # See https://github.com/python/cpython/issues/143200. + self.do_test_subscr_with_mutating_slice(use_clear_method=True) + + def test_subscr_with_delete(self): + # See https://github.com/python/cpython/issues/72050. + self.do_test_subscr_with_mutating_slice(use_clear_method=False) + + def do_test_subscr_with_mutating_slice(self, *, use_clear_method): class X: + def __init__(self, i=0): + self.i = i def __index__(self): - del e[:] - return 1 + if use_clear_method: + e.clear() + else: + del e[:] + return self.i - e = ET.Element('elem') - e.append(ET.Element('child')) - e[:X()] # shouldn't crash + for s in self.get_mutating_slices(X, 10): + with self.subTest(s): + e = ET.Element('elem') + e.extend([ET.Element(f'c{i}') for i in range(10)]) + e[s] # shouldn't crash - e.append(ET.Element('child')) - e[0:10:X()] # shouldn't crash + def test_ass_subscr_with_mutating_slice(self): + # See https://github.com/python/cpython/issues/72050 + # and https://github.com/python/cpython/issues/143200. - def test_ass_subscr(self): - # Issue #27863 class X: + def __init__(self, i=0): + self.i = i def __index__(self): e[:] = [] - return 1 + return self.i + + for s in self.get_mutating_slices(X, 10): + with self.subTest(s): + e = ET.Element('elem') + e.extend([ET.Element(f'c{i}') for i in range(10)]) + e[s] = [] # shouldn't crash + + def get_mutating_slices(self, index_class, n_children): + self.assertGreaterEqual(n_children, 10) + return [ + slice(index_class(), None, None), + slice(index_class(2), None, None), + slice(None, index_class(), None), + slice(None, index_class(2), None), + slice(0, 2, index_class(1)), + slice(0, 2, index_class(2)), + slice(0, n_children, index_class(1)), + slice(0, n_children, index_class(2)), + slice(0, 2 * n_children, index_class(1)), + slice(0, 2 * n_children, index_class(2)), + ] - e = ET.Element('elem') - for _ in range(10): - e.insert(0, ET.Element('child')) + def test_ass_subscr_with_mutating_iterable_value(self): + class V: + def __iter__(self): + e.clear() + return iter([ET.Element('a'), ET.Element('b')]) - e[0:10:X()] = [] # shouldn't crash + e = ET.Element('elem') + e.extend([ET.Element(f'c{i}') for i in range(10)]) + e[:] = V() def test_treebuilder_start(self): # Issue #27863 @@ -3030,6 +3246,19 @@ def __deepcopy__(self, memo): self.assertEqual([c.tag for c in children[3:]], [a.tag, b.tag, a.tag, b.tag]) + @support.skip_if_unlimited_stack_size + @support.skip_emscripten_stack_overflow() + @support.skip_wasi_stack_overflow() + def test_deeply_nested_deepcopy(self): + # This should raise a RecursionError and not crash. + # See https://github.com/python/cpython/issues/148801. + root = cur = ET.Element('s') + for _ in range(500_000): + cur = ET.SubElement(cur, 'u') + with support.infinite_recursion(): + with self.assertRaises(RecursionError): + copy.deepcopy(root) + class MutationDeleteElementPath(str): def __new__(cls, elem, *args): @@ -3098,6 +3327,16 @@ def test_findtext_with_mutating(self): e.extend([ET.Element('bar')]) e.findtext(cls(e, 'x')) + def test_findtext_with_mutating_non_none_text(self): + for cls in [MutationDeleteElementPath, MutationClearElementPath]: + with self.subTest(cls): + e = ET.Element('foo') + child = ET.Element('bar') + child.text = str(object()) + e.append(child) + del child + repr(e.findtext(cls(e, 'x'))) + def test_findtext_with_error(self): e = ET.Element('foo') e.extend([ET.Element('bar')]) @@ -3471,6 +3710,32 @@ def test_basic(self): doc = ET.XML("<root>a&amp;<sub>b&amp;</sub>c&amp;</root>") self.assertEqual(''.join(doc.itertext()), 'a&b&c&') + def test_comment(self): + e = ET.Element('root') + e.text = 'before' + comment = ET.Comment('comment') + self.assertEqual(comment.text, 'comment') + comment.tail = 'after' + e.append(comment) + self.assertEqual(''.join(e.itertext()), 'beforeafter') + self.assertEqual(list(e.iter()), [e, comment]) + self.assertEqual(list(e.iter('root')), [e]) + self.assertEqual(''.join(comment.itertext()), '') + self.assertEqual(list(comment.iter()), [comment]) + + def test_processinginstruction(self): + e = ET.Element('root') + e.text = 'before' + pi = ET.PI('test', 'instruction') + self.assertEqual(pi.text, 'test instruction') + pi.tail = 'after' + e.append(pi) + self.assertEqual(''.join(e.itertext()), 'beforeafter') + self.assertEqual(list(e.iter()), [e, pi]) + self.assertEqual(list(e.iter('root')), [e]) + self.assertEqual(''.join(pi.itertext()), '') + self.assertEqual(list(pi.iter()), [pi]) + def test_corners(self): # single root, no subelements a = ET.Element('a') @@ -4347,6 +4612,9 @@ def test_issue14818(self): ET.Element('a', dict(href="#"), id="foo"), ET.Element('a', href="#", id="foo"), ET.Element('a', dict(href="#", id="foo"), href="#", id="foo"), + ET.Element('a', frozendict(href="#", id="foo")), + ET.Element('a', frozendict(href="#"), id="foo"), + ET.Element('a', attrib=frozendict(href="#", id="foo")), ] for e in elements: self.assertEqual(e.tag, 'a') @@ -4354,10 +4622,14 @@ def test_issue14818(self): e2 = ET.SubElement(elements[0], 'foobar', attrib={'key1': 'value1'}) self.assertEqual(e2.attrib['key1'], 'value1') + e3 = ET.SubElement(elements[0], 'foobar', + attrib=frozendict({'key1': 'value1'})) + self.assertEqual(e3.attrib['key1'], 'value1') - with self.assertRaisesRegex(TypeError, 'must be dict, not str'): + errmsg = 'must be dict or frozendict, not str' + with self.assertRaisesRegex(TypeError, errmsg): ET.Element('a', "I'm not a dict") - with self.assertRaisesRegex(TypeError, 'must be dict, not str'): + with self.assertRaisesRegex(TypeError, errmsg): ET.Element('a', attrib="I'm not a dict") # -------------------------------------------------------------------- @@ -4630,6 +4902,19 @@ def get_option(config, option_name, default=None): # -------------------------------------------------------------------- + +class TestModule(unittest.TestCase): + def test_deprecated_version(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'VERSION' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(ET, "VERSION") + self.assertEqual(cm.filename, __file__) + + +# -------------------------------------------------------------------- + def setUpModule(module=None): # When invoked without a module, runs the Python ET tests by loading pyET. # Otherwise, uses the given module as the ET. diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 2803c6d45c27bf..ee0e24f6e86ae3 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -208,6 +208,17 @@ def test_dump_encoding(self): self.assertEqual(xmlrpclib.loads(strg)[0][0], value) self.assertEqual(xmlrpclib.loads(strg)[1], methodname) + def test_dump_escape_methodname(self): + payload = 'foo</methodName><injected attr="evil"/><methodName>bar' + s = xmlrpclib.dumps((), methodname=payload) + self.assertIn( + '<methodName>foo&lt;/methodName&gt;&lt;injected attr="evil"/&gt;' + '&lt;methodName&gt;bar</methodName>', s + ) + self.assertNotIn('<injected attr="evil"/>', s) + load, m = xmlrpclib.loads(s) + self.assertEqual(m, payload) + def test_dump_bytes(self): sample = b"my dog has fleas" self.assertEqual(sample, xmlrpclib.Binary(sample)) diff --git a/Lib/test/test_xpickle.py b/Lib/test/test_xpickle.py new file mode 100644 index 00000000000000..d87c671d4f5394 --- /dev/null +++ b/Lib/test/test_xpickle.py @@ -0,0 +1,281 @@ +# This test covers backwards compatibility with previous versions of Python +# by bouncing pickled objects through Python versions by running xpickle_worker.py. +import io +import os +import pickle +import struct +import subprocess +import sys +import unittest + + +from test import support +from test import pickletester + +try: + import _pickle + has_c_implementation = True +except ModuleNotFoundError: + has_c_implementation = False + +support.requires('xpickle') + +is_windows = sys.platform.startswith('win') + +# Map python version to a tuple containing the name of a corresponding valid +# Python binary to execute and its arguments. +py_executable_map = {} + +protocols_map = { + 3: (3, 0), + 4: (3, 4), + 5: (3, 8), +} + +def highest_proto_for_py_version(py_version): + """Finds the highest supported pickle protocol for a given Python version. + Args: + py_version: a 2-tuple of the major, minor version. Eg. Python 3.7 would + be (3, 7) + Returns: + int for the highest supported pickle protocol + """ + proto = 2 + for p, v in protocols_map.items(): + if py_version < v: + break + proto = p + return proto + +def have_python_version(py_version): + """Check whether a Python binary exists for the given py_version and has + support. This respects your PATH. + For Windows, it will first try to use the py launcher specified in PEP 397. + Otherwise (and for all other platforms), it will attempt to check for + python<py_version[0]>.<py_version[1]>. + + Eg. given a *py_version* of (3, 7), the function will attempt to try + 'py -3.7' (for Windows) first, then 'python3.7', and return + ['py', '-3.7'] (on Windows) or ['python3.7'] on other platforms. + + Args: + py_version: a 2-tuple of the major, minor version. Eg. python 3.7 would + be (3, 7) + Returns: + List/Tuple containing the Python binary name and its required arguments, + or None if no valid binary names found. + """ + python_str = ".".join(map(str, py_version)) + targets = [('py', f'-{python_str}'), (f'python{python_str}',)] + if py_version not in py_executable_map: + for target in targets[0 if is_windows else 1:]: + try: + worker = subprocess.Popen([*target, '-c', 'pass'], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + shell=is_windows) + worker.communicate() + if worker.returncode == 0: + py_executable_map[py_version] = target + break + except FileNotFoundError: + pass + + return py_executable_map.get(py_version, None) + + +def read_exact(f, n): + buf = b'' + while len(buf) < n: + chunk = f.read(n - len(buf)) + if not chunk: + raise EOFError + buf += chunk + return buf + + +class AbstractCompatTests(pickletester.AbstractPickleTests): + py_version = None + worker = None + + @classmethod + def setUpClass(cls): + assert cls.py_version is not None, 'Needs a python version tuple' + if not have_python_version(cls.py_version): + py_version_str = ".".join(map(str, cls.py_version)) + raise unittest.SkipTest(f'Python {py_version_str} not available') + cls.addClassCleanup(cls.finish_worker) + # Override the default pickle protocol to match what xpickle worker + # will be running. + highest_protocol = highest_proto_for_py_version(cls.py_version) + cls.enterClassContext(support.swap_attr(pickletester, 'protocols', + range(highest_protocol + 1))) + cls.enterClassContext(support.swap_attr(pickle, 'HIGHEST_PROTOCOL', + highest_protocol)) + + @classmethod + def start_worker(cls, python): + target = os.path.join(os.path.dirname(__file__), 'xpickle_worker.py') + worker = subprocess.Popen([*python, target], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + # For windows bpo-17023. + shell=is_windows) + cls.worker = worker + return worker + + @classmethod + def finish_worker(cls): + worker = cls.worker + if worker is None: + return + cls.worker = None + worker.stdin.close() + worker.stdout.close() + worker.stderr.close() + worker.terminate() + worker.wait() + + @classmethod + def send_to_worker(cls, python, data): + """Bounce a pickled object through another version of Python. + This will send data to a child process where it will + be unpickled, then repickled and sent back to the parent process. + Args: + python: list containing the python binary to start and its arguments + data: bytes object to send to the child process + Returns: + The pickled data received from the child process. + """ + worker = cls.worker + if worker is None: + worker = cls.start_worker(python) + + try: + worker.stdin.write(struct.pack('!i', len(data)) + data) + worker.stdin.flush() + + size, = struct.unpack('!i', read_exact(worker.stdout, 4)) + if size > 0: + return read_exact(worker.stdout, size) + # if the worker fails, it will write the exception to stdout + if size < 0: + stdout = read_exact(worker.stdout, -size) + try: + exception = pickle.loads(stdout) + except (pickle.UnpicklingError, EOFError): + pass + else: + if isinstance(exception, Exception): + # To allow for tests which test for errors. + raise exception + _, stderr = worker.communicate() + raise RuntimeError(stderr) + except: + cls.finish_worker() + raise + + def dumps(self, arg, proto=0, **kwargs): + # Skip tests that require buffer_callback arguments since + # there isn't a reliable way to marshal/pickle the callback and ensure + # it works in a different Python version. + if 'buffer_callback' in kwargs: + self.skipTest('Test does not support "buffer_callback" argument.') + f = io.BytesIO() + p = self.pickler(f, proto, **kwargs) + p.dump(arg) + data = struct.pack('!i', proto) + f.getvalue() + python = py_executable_map[self.py_version] + return self.send_to_worker(python, data) + + def loads(self, buf, **kwds): + f = io.BytesIO(buf) + u = self.unpickler(f, **kwds) + return u.load() + + # A scaled-down version of test_bytes from pickletester, to reduce + # the number of calls to self.dumps() and hence reduce the number of + # child python processes forked. This allows the test to complete + # much faster (the one from pickletester takes 3-4 minutes when running + # under text_xpickle). + def test_bytes(self): + if self.py_version < (3, 0): + self.skipTest('not supported in Python < 3.0') + for proto in pickletester.protocols: + for s in b'', b'xyz', b'xyz'*100: + p = self.dumps(s, proto) + self.assert_is_copy(s, self.loads(p)) + s = bytes(range(256)) + p = self.dumps(s, proto) + self.assert_is_copy(s, self.loads(p)) + s = bytes([i for i in range(256) for _ in range(2)]) + p = self.dumps(s, proto) + self.assert_is_copy(s, self.loads(p)) + + # These tests are disabled because they require some special setup + # on the worker that's hard to keep in sync. + test_global_ext1 = None + test_global_ext2 = None + test_global_ext4 = None + + # These tests fail because they require classes from pickletester + # which cannot be properly imported by the xpickle worker. + test_recursive_nested_names = None + test_recursive_nested_names2 = None + + # Attribute lookup problems are expected, disable the test + test_dynamic_class = None + test_evil_class_mutating_dict = None + + # Expected exception is raised during unpickling in a subprocess. + test_pickle_setstate_None = None + + # Other Python version may not have NumPy. + test_buffers_numpy = None + + # Skip tests that require buffer_callback arguments since + # there isn't a reliable way to marshal/pickle the callback and ensure + # it works in a different Python version. + test_in_band_buffers = None + test_buffers_error = None + test_oob_buffers = None + test_oob_buffers_writable_to_readonly = None + +class PyPicklePythonCompat(AbstractCompatTests): + pickler = pickle._Pickler + unpickler = pickle._Unpickler + +if has_c_implementation: + class CPicklePythonCompat(AbstractCompatTests): + pickler = _pickle.Pickler + unpickler = _pickle.Unpickler + + +def make_test(py_version, base): + class_dict = {'py_version': py_version} + name = base.__name__.replace('Python', 'Python%d%d' % py_version) + return type(name, (base, unittest.TestCase), class_dict) + +def load_tests(loader, tests, pattern): + def add_tests(py_version): + test_class = make_test(py_version, PyPicklePythonCompat) + tests.addTest(loader.loadTestsFromTestCase(test_class)) + if has_c_implementation: + test_class = make_test(py_version, CPicklePythonCompat) + tests.addTest(loader.loadTestsFromTestCase(test_class)) + + value = support.get_resource_value('xpickle') + if value is None: + major = sys.version_info.major + assert major == 3 + add_tests((2, 7)) + for minor in range(2, sys.version_info.minor): + add_tests((major, minor)) + else: + add_tests(tuple(map(int, value.split('.')))) + return tests + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_xxlimited.py b/Lib/test/test_xxlimited.py index b52e78bc4fb7e0..c6e9dc375d9a67 100644 --- a/Lib/test/test_xxlimited.py +++ b/Lib/test/test_xxlimited.py @@ -1,19 +1,39 @@ import unittest from test.support import import_helper -import types xxlimited = import_helper.import_module('xxlimited') -xxlimited_35 = import_helper.import_module('xxlimited_35') - -class CommonTests: - module: types.ModuleType - - def test_xxo_new(self): - xxo = self.module.Xxo() - - def test_xxo_attributes(self): - xxo = self.module.Xxo() +# if import of xxlimited succeeded, the other ones should be importable. +import xxlimited_3_13 +import xxlimited_35 + +MODULES = { + (3, 15): xxlimited, + (3, 13): xxlimited_3_13, + (3, 5): xxlimited_35, +} + +def test_with_xxlimited_modules(since=None, until=None): + def _decorator(func): + def _wrapper(self, *args, **kwargs): + for version, module in MODULES.items(): + if since and version < since: + continue + if until and version >= until: + continue + with self.subTest(version=version): + func(self, module, *args, **kwargs) + return _wrapper + return _decorator + +class XXLimitedTests(unittest.TestCase): + @test_with_xxlimited_modules() + def test_xxo_new(self, module): + xxo = module.Xxo() + + @test_with_xxlimited_modules() + def test_xxo_attributes(self, module): + xxo = module.Xxo() with self.assertRaises(AttributeError): xxo.foo with self.assertRaises(AttributeError): @@ -26,40 +46,61 @@ def test_xxo_attributes(self): with self.assertRaises(AttributeError): xxo.foo - def test_foo(self): + @test_with_xxlimited_modules() + def test_foo(self, module): # the foo function adds 2 numbers - self.assertEqual(self.module.foo(1, 2), 3) + self.assertEqual(module.foo(1, 2), 3) - def test_str(self): - self.assertIsSubclass(self.module.Str, str) - self.assertIsNot(self.module.Str, str) + @test_with_xxlimited_modules() + def test_str(self, module): + self.assertIsSubclass(module.Str, str) + self.assertIsNot(module.Str, str) - custom_string = self.module.Str("abcd") + custom_string = module.Str("abcd") self.assertEqual(custom_string, "abcd") self.assertEqual(custom_string.upper(), "ABCD") - def test_new(self): - xxo = self.module.new() + @test_with_xxlimited_modules() + def test_new(self, module): + xxo = module.new() self.assertEqual(xxo.demo("abc"), "abc") - -class TestXXLimited(CommonTests, unittest.TestCase): - module = xxlimited - - def test_xxo_demo(self): - xxo = self.module.Xxo() - other = self.module.Xxo() + @test_with_xxlimited_modules() + def test_xxo_demo(self, module): + xxo = module.Xxo() self.assertEqual(xxo.demo("abc"), "abc") + self.assertEqual(xxo.demo(0), None) + self.assertEqual(xxo.__module__, module.__name__) + with self.assertRaises(TypeError): + module.Xxo('arg') + with self.assertRaises(TypeError): + module.Xxo(kwarg='arg') + + @test_with_xxlimited_modules(since=(3, 13)) + def test_xxo_demo_extra(self, module): + xxo = module.Xxo() + other = module.Xxo() self.assertEqual(xxo.demo(xxo), xxo) self.assertEqual(xxo.demo(other), other) - self.assertEqual(xxo.demo(0), None) - def test_error(self): - with self.assertRaises(self.module.Error): - raise self.module.Error - - def test_buffer(self): - xxo = self.module.Xxo() + @test_with_xxlimited_modules(since=(3, 15)) + def test_xxo_subclass(self, module): + class Sub(module.Xxo): + pass + sub = Sub() + sub.a = 123 + self.assertEqual(sub.a, 123) + with self.assertRaisesRegex(AttributeError, "cannot set 'reserved'"): + sub.reserved = 123 + + @test_with_xxlimited_modules(since=(3, 13)) + def test_error(self, module): + with self.assertRaises(module.Error): + raise module.Error + + @test_with_xxlimited_modules(since=(3, 13)) + def test_buffer(self, module): + xxo = module.Xxo() self.assertEqual(xxo.x_exports, 0) b1 = memoryview(xxo) self.assertEqual(xxo.x_exports, 1) @@ -69,21 +110,13 @@ def test_buffer(self): self.assertEqual(b1[0], 1) self.assertEqual(b2[0], 1) - -class TestXXLimited35(CommonTests, unittest.TestCase): - module = xxlimited_35 - - def test_xxo_demo(self): - xxo = self.module.Xxo() - other = self.module.Xxo() - self.assertEqual(xxo.demo("abc"), "abc") - self.assertEqual(xxo.demo(0), None) - + @test_with_xxlimited_modules(until=(3, 5)) def test_roj(self): # the roj function always fails with self.assertRaises(SystemError): self.module.roj(0) + @test_with_xxlimited_modules(until=(3, 5)) def test_null(self): null1 = self.module.Null() null2 = self.module.Null() diff --git a/Lib/test/test_zipfile/_path/test_path.py b/Lib/test/test_zipfile/_path/test_path.py index 958a586b0dc8e8..e7931b6f394075 100644 --- a/Lib/test/test_zipfile/_path/test_path.py +++ b/Lib/test/test_zipfile/_path/test_path.py @@ -274,7 +274,8 @@ def test_pathlike_construction(self, alpharep): """ zipfile_ondisk = self.zipfile_ondisk(alpharep) pathlike = FakePath(str(zipfile_ondisk)) - zipfile.Path(pathlike) + root = zipfile.Path(pathlike) + root.root.close() @pass_alpharep def test_traverse_pathlike(self, alpharep): @@ -373,6 +374,7 @@ def test_root_on_disk(self, alpharep): root = zipfile.Path(self.zipfile_ondisk(alpharep)) assert root.name == 'alpharep.zip' == root.filename.name assert root.stem == 'alpharep' == root.filename.stem + root.root.close() @pass_alpharep def test_suffix(self, alpharep): @@ -574,11 +576,13 @@ def test_inheritance(self, alpharep): ) def test_pickle(self, alpharep, path_type, subpath): zipfile_ondisk = path_type(str(self.zipfile_ondisk(alpharep))) - - saved_1 = pickle.dumps(zipfile.Path(zipfile_ondisk, at=subpath)) + root = zipfile.Path(zipfile_ondisk, at=subpath) + saved_1 = pickle.dumps(root) + root.root.close() restored_1 = pickle.loads(saved_1) first, *rest = restored_1.iterdir() assert first.read_text(encoding='utf-8').startswith('content of ') + restored_1.root.close() @pass_alpharep def test_extract_orig_with_implied_dirs(self, alpharep): @@ -590,6 +594,7 @@ def test_extract_orig_with_implied_dirs(self, alpharep): # wrap the zipfile for its side effect zipfile.Path(zf) zf.extractall(source_path.parent) + zf.close() @pass_alpharep def test_getinfo_missing(self, alpharep): diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index c033059a515db6..ffed328b171fda 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -312,26 +312,26 @@ def test_low_compression(self): self.assertEqual(openobj.read(1), b'2') def test_writestr_compression(self): - zipfp = zipfile.ZipFile(TESTFN2, "w") - zipfp.writestr("b.txt", "hello world", compress_type=self.compression) - info = zipfp.getinfo('b.txt') - self.assertEqual(info.compress_type, self.compression) + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + zipfp.writestr("b.txt", "hello world", compress_type=self.compression) + info = zipfp.getinfo('b.txt') + self.assertEqual(info.compress_type, self.compression) def test_writestr_compresslevel(self): - zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1) - zipfp.writestr("a.txt", "hello world", compress_type=self.compression) - zipfp.writestr("b.txt", "hello world", compress_type=self.compression, - compresslevel=2) + with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp: + zipfp.writestr("a.txt", "hello world", compress_type=self.compression) + zipfp.writestr("b.txt", "hello world", compress_type=self.compression, + compresslevel=2) - # Compression level follows the constructor. - a_info = zipfp.getinfo('a.txt') - self.assertEqual(a_info.compress_type, self.compression) - self.assertEqual(a_info.compress_level, 1) + # Compression level follows the constructor. + a_info = zipfp.getinfo('a.txt') + self.assertEqual(a_info.compress_type, self.compression) + self.assertEqual(a_info.compress_level, 1) - # Compression level is overridden. - b_info = zipfp.getinfo('b.txt') - self.assertEqual(b_info.compress_type, self.compression) - self.assertEqual(b_info._compresslevel, 2) + # Compression level is overridden. + b_info = zipfp.getinfo('b.txt') + self.assertEqual(b_info.compress_type, self.compression) + self.assertEqual(b_info._compresslevel, 2) def test_read_return_size(self): # Issue #9837: ZipExtFile.read() shouldn't return more bytes @@ -898,6 +898,8 @@ def make_zip64_file( self, file_size_64_set=False, file_size_extra=False, compress_size_64_set=False, compress_size_extra=False, header_offset_64_set=False, header_offset_extra=False, + extensible_data=b'', + end_of_central_dir_size=None, offset_to_end_of_central_dir=None, ): """Generate bytes sequence for a zip with (incomplete) zip64 data. @@ -951,6 +953,12 @@ def make_zip64_file( central_dir_size = struct.pack('<Q', 58 + 8 * len(central_zip64_fields)) offset_to_central_dir = struct.pack('<Q', 50 + 8 * len(local_zip64_fields)) + if end_of_central_dir_size is None: + end_of_central_dir_size = 44 + len(extensible_data) + if offset_to_end_of_central_dir is None: + offset_to_end_of_central_dir = (108 + + 8 * len(local_zip64_fields) + + 8 * len(central_zip64_fields)) local_extra_length = struct.pack("<H", 4 + 8 * len(local_zip64_fields)) central_extra_length = struct.pack("<H", 4 + 8 * len(central_zip64_fields)) @@ -979,14 +987,17 @@ def make_zip64_file( + filename + central_extra # Zip64 end of central directory - + b"PK\x06\x06,\x00\x00\x00\x00\x00\x00\x00-\x00-" - + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00" + + b"PK\x06\x06" + + struct.pack('<Q', end_of_central_dir_size) + + b"-\x00-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00" + b"\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" + central_dir_size + offset_to_central_dir + + extensible_data # Zip64 end of central directory locator - + b"PK\x06\x07\x00\x00\x00\x00l\x00\x00\x00\x00\x00\x00\x00\x01" - + b"\x00\x00\x00" + + b"PK\x06\x07\x00\x00\x00\x00" + + struct.pack('<Q', offset_to_end_of_central_dir) + + b"\x01\x00\x00\x00" # end of central directory + b"PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00:\x00\x00\x002\x00" + b"\x00\x00\x00\x00" @@ -1017,6 +1028,7 @@ def test_bad_zip64_extra(self): with self.assertRaises(zipfile.BadZipFile) as e: zipfile.ZipFile(io.BytesIO(missing_file_size_extra)) self.assertIn('file size', str(e.exception).lower()) + self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_file_size_extra))) # zip64 file size present, zip64 compress size present, one field in # extra, expecting two, equals missing compress size. @@ -1028,6 +1040,7 @@ def test_bad_zip64_extra(self): with self.assertRaises(zipfile.BadZipFile) as e: zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) + self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_compress_size_extra))) # zip64 compress size present, no fields in extra, expecting one, # equals missing compress size. @@ -1037,6 +1050,7 @@ def test_bad_zip64_extra(self): with self.assertRaises(zipfile.BadZipFile) as e: zipfile.ZipFile(io.BytesIO(missing_compress_size_extra)) self.assertIn('compress size', str(e.exception).lower()) + self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_compress_size_extra))) # zip64 file size present, zip64 compress size present, zip64 header # offset present, two fields in extra, expecting three, equals missing @@ -1051,6 +1065,7 @@ def test_bad_zip64_extra(self): with self.assertRaises(zipfile.BadZipFile) as e: zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) + self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_header_offset_extra))) # zip64 compress size present, zip64 header offset present, one field # in extra, expecting two, equals missing header offset @@ -1063,6 +1078,7 @@ def test_bad_zip64_extra(self): with self.assertRaises(zipfile.BadZipFile) as e: zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) + self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_header_offset_extra))) # zip64 file size present, zip64 header offset present, one field in # extra, expecting two, equals missing header offset @@ -1075,6 +1091,7 @@ def test_bad_zip64_extra(self): with self.assertRaises(zipfile.BadZipFile) as e: zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) + self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_header_offset_extra))) # zip64 header offset present, no fields in extra, expecting one, # equals missing header offset @@ -1086,6 +1103,63 @@ def test_bad_zip64_extra(self): with self.assertRaises(zipfile.BadZipFile) as e: zipfile.ZipFile(io.BytesIO(missing_header_offset_extra)) self.assertIn('header offset', str(e.exception).lower()) + self.assertTrue(zipfile.is_zipfile(io.BytesIO(missing_header_offset_extra))) + + def test_bad_zip64_end_of_central_dir(self): + zipdata = self.make_zip64_file(end_of_central_dir_size=0) + with self.assertRaisesRegex(zipfile.BadZipFile, 'Corrupt.*record'): + zipfile.ZipFile(io.BytesIO(zipdata)) + self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata))) + + zipdata = self.make_zip64_file(end_of_central_dir_size=100) + with self.assertRaisesRegex(zipfile.BadZipFile, 'Corrupt.*record'): + zipfile.ZipFile(io.BytesIO(zipdata)) + self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata))) + + zipdata = self.make_zip64_file(offset_to_end_of_central_dir=0) + with self.assertRaisesRegex(zipfile.BadZipFile, 'Corrupt.*record'): + zipfile.ZipFile(io.BytesIO(zipdata)) + self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata))) + + zipdata = self.make_zip64_file(offset_to_end_of_central_dir=1000) + with self.assertRaisesRegex(zipfile.BadZipFile, 'Corrupt.*locator'): + zipfile.ZipFile(io.BytesIO(zipdata)) + self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata))) + + def test_zip64_end_of_central_dir_record_not_found(self): + zipdata = self.make_zip64_file() + zipdata = zipdata.replace(b"PK\x06\x06", b'\x00'*4) + with self.assertRaisesRegex(zipfile.BadZipFile, 'record not found'): + zipfile.ZipFile(io.BytesIO(zipdata)) + self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata))) + + zipdata = self.make_zip64_file( + extensible_data=b'\xca\xfe\x04\x00\x00\x00data') + zipdata = zipdata.replace(b"PK\x06\x06", b'\x00'*4) + with self.assertRaisesRegex(zipfile.BadZipFile, 'record not found'): + zipfile.ZipFile(io.BytesIO(zipdata)) + self.assertFalse(zipfile.is_zipfile(io.BytesIO(zipdata))) + + def test_zip64_extensible_data(self): + # These values are what is set in the make_zip64_file method. + expected_file_size = 8 + expected_compress_size = 8 + expected_header_offset = 0 + expected_content = b"test1234" + + zipdata = self.make_zip64_file( + extensible_data=b'\xca\xfe\x04\x00\x00\x00data') + with zipfile.ZipFile(io.BytesIO(zipdata)) as zf: + zinfo = zf.infolist()[0] + self.assertEqual(zinfo.file_size, expected_file_size) + self.assertEqual(zinfo.compress_size, expected_compress_size) + self.assertEqual(zinfo.header_offset, expected_header_offset) + self.assertEqual(zf.read(zinfo), expected_content) + self.assertTrue(zipfile.is_zipfile(io.BytesIO(zipdata))) + + with self.assertRaisesRegex(zipfile.BadZipFile, 'record not found'): + zipfile.ZipFile(io.BytesIO(b'prepended' + zipdata)) + self.assertFalse(zipfile.is_zipfile(io.BytesIO(b'prepended' + zipdata))) def test_generated_valid_zip64_extra(self): # These values are what is set in the make_zip64_file method. @@ -1812,11 +1886,8 @@ def test_write_with_source_date_epoch(self): with zipfile.ZipFile(TESTFN, "r") as zf: zip_info = zf.getinfo("test_source_date_epoch.txt") - get_time = time.localtime(int(os.environ['SOURCE_DATE_EPOCH']))[:6] - # Compare each element of the date_time tuple - # Allow for a 1-second difference - for z_time, g_time in zip(zip_info.date_time, get_time): - self.assertAlmostEqual(z_time, g_time, delta=1) + expected_utc = (2025, 1, 1, 7, 19, 58) + self.assertEqual(zip_info.date_time, expected_utc) def test_write_without_source_date_epoch(self): with os_helper.EnvironmentVarGuard() as env: @@ -1827,9 +1898,13 @@ def test_write_without_source_date_epoch(self): with zipfile.ZipFile(TESTFN, "r") as zf: zip_info = zf.getinfo("test_no_source_date_epoch.txt") - current_time = time.localtime()[:6] - for z_time, c_time in zip(zip_info.date_time, current_time): - self.assertAlmostEqual(z_time, c_time, delta=1) + self.assertTimestampAlmostEqual(time.localtime(), zip_info.date_time, tolerance=2) + + def assertTimestampAlmostEqual(self, time1, time2, tolerance): + import datetime + dt1 = datetime.datetime(*time1[:6]) + dt2 = datetime.datetime(*time2[:6]) + self.assertLessEqual((dt1 - dt2).total_seconds(), tolerance) def test_close(self): """Check that the zipfile is closed after the 'with' block.""" @@ -2256,6 +2331,7 @@ def test_empty_zipfile(self): zipf = zipfile.ZipFile(TESTFN, mode="r") except zipfile.BadZipFile: self.fail("Unable to create empty ZIP file in 'w' mode") + zipf.close() zipf = zipfile.ZipFile(TESTFN, mode="a") zipf.close() @@ -2263,6 +2339,7 @@ def test_empty_zipfile(self): zipf = zipfile.ZipFile(TESTFN, mode="r") except: self.fail("Unable to create empty ZIP file in 'a' mode") + zipf.close() def test_open_empty_file(self): # Issue 1710703: Check that opening a file with less than 22 bytes @@ -2455,6 +2532,10 @@ def test_decompress_without_3rd_party_library(self): @requires_zlib() def test_full_overlap_different_names(self): + # The ZIP file contains two central directory entries with + # different names which refer to the same local header. + # The name of the local header matches the name of the first + # central directory entry. data = ( b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00b\xed' @@ -2484,6 +2565,10 @@ def test_full_overlap_different_names(self): @requires_zlib() def test_full_overlap_different_names2(self): + # The ZIP file contains two central directory entries with + # different names which refer to the same local header. + # The name of the local header matches the name of the second + # central directory entry. data = ( b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00a\xed' @@ -2515,6 +2600,8 @@ def test_full_overlap_different_names2(self): @requires_zlib() def test_full_overlap_same_name(self): + # The ZIP file contains two central directory entries with + # the same name which refer to the same local header. data = ( b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00a\xed' @@ -2547,6 +2634,8 @@ def test_full_overlap_same_name(self): @requires_zlib() def test_quoted_overlap(self): + # The ZIP file contains two files. The second local header + # is contained in the range of the first file. data = ( b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05Y\xfc' b'8\x044\x00\x00\x00(\x04\x00\x00\x01\x00\x00\x00a\x00' @@ -2578,6 +2667,7 @@ def test_quoted_overlap(self): @requires_zlib() def test_overlap_with_central_dir(self): + # The local header offset is equal to the central directory offset. data = ( b'PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00G_|Z' b'\xe2\x1e8\xbb\x0b\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00' @@ -2592,11 +2682,15 @@ def test_overlap_with_central_dir(self): self.assertEqual(zi.header_offset, 0) self.assertEqual(zi.compress_size, 11) self.assertEqual(zi.file_size, 1033) + # Found central directory signature PK\x01\x02 instead of + # local header signature PK\x03\x04. with self.assertRaisesRegex(zipfile.BadZipFile, 'Bad magic number'): zipf.read('a') @requires_zlib() def test_overlap_with_archive_comment(self): + # The local header is written after the central directory, + # in the archive comment. data = ( b'PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00G_|Z' b'\xe2\x1e8\xbb\x0b\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00' @@ -3546,29 +3640,23 @@ def test_read_with_unsuitable_metadata_encoding(self): def test_read_after_append(self): newname = '\u56db' # Han 'four' - expected_names = [name.encode('shift_jis').decode('cp437') - for name in self.file_names[:2]] + self.file_names[2:] - expected_names.append(newname) - expected_content = (*self.file_content, b"newcontent") + newname2 = 'fünf' # representable in cp437, but still stored as UTF-8 + expected_names = [*self.file_names, newname, newname2] + mojibake_expected_names = [name.encode('shift_jis').decode('cp437') + if i < 2 else name + for i, name in enumerate(expected_names)] + expected_content = (*self.file_content, b"newcontent", b"newcontent2") with zipfile.ZipFile(TESTFN, "a") as zipfp: zipfp.writestr(newname, "newcontent") - self.assertEqual(sorted(zipfp.namelist()), sorted(expected_names)) + zipfp.writestr(newname2, "newcontent2") + self.assertEqual(sorted(zipfp.namelist()), sorted(mojibake_expected_names)) with zipfile.ZipFile(TESTFN, "r") as zipfp: - self._test_read(zipfp, expected_names, expected_content) + self._test_read(zipfp, mojibake_expected_names, expected_content) with zipfile.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp: - self.assertEqual(sorted(zipfp.namelist()), sorted(expected_names)) - for i, (name, content) in enumerate(zip(expected_names, expected_content)): - info = zipfp.getinfo(name) - self.assertEqual(info.filename, name) - self.assertEqual(info.file_size, len(content)) - if i < 2: - with self.assertRaises(zipfile.BadZipFile): - zipfp.read(name) - else: - self.assertEqual(zipfp.read(name), content) + self._test_read(zipfp, expected_names, expected_content) def test_write_with_metadata_encoding(self): ZF = zipfile.ZipFile @@ -3577,6 +3665,20 @@ def test_write_with_metadata_encoding(self): "^metadata_encoding is only"): ZF("nonesuch.zip", mode, metadata_encoding="shift_jis") + def test_add_comment(self): + with zipfile.ZipFile(TESTFN, "r") as zipfp: + mojibake_expected_names = zipfp.namelist() + + with zipfile.ZipFile(TESTFN, "a") as zipfp: + zipfp.comment = b'comment' + self.assertEqual(zipfp.namelist(), mojibake_expected_names) + + with zipfile.ZipFile(TESTFN, "r") as zipfp: + self._test_read(zipfp, mojibake_expected_names, self.file_content) + + with zipfile.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp: + self._test_read(zipfp, self.file_names, self.file_content) + def test_cli_with_metadata_encoding(self): errmsg = "Non-conforming encodings not supported with -c." args = ["--metadata-encoding=shift_jis", "-c", "nonesuch", "nonesuch"] diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py index b5b4acf5f850be..76cd85709a63af 100644 --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -9,13 +9,12 @@ import time import unittest import unittest.mock -import warnings from test import support from test.support import import_helper from test.support import os_helper -from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED +from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED, ZIP_ZSTANDARD import zipimport import linecache @@ -61,7 +60,6 @@ def module_path_to_dotted_name(path): TEMP_ZIP = os.path.abspath("junk95142.zip") TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "zipimport_data") -pyc_file = importlib.util.cache_from_source(TESTMOD + '.py') pyc_ext = '.pyc' @@ -194,19 +192,38 @@ def testAFakeZlib(self): # occur in that case (builtin modules are always found first), # so we'll simply skip it then. Bug #765456. # - if "zlib" in sys.builtin_module_names: - self.skipTest('zlib is a builtin module') - if "zlib" in sys.modules: - del sys.modules["zlib"] - files = {"zlib.py": test_src} + if self.compression == ZIP_DEFLATED: + mod_name = "zlib" + if zipimport._zlib_decompress: # validate attr name + # reset the cached import to avoid test order dependencies + zipimport._zlib_decompress = None # reset cache + elif self.compression == ZIP_ZSTANDARD: + mod_name = "_zstd" + if zipimport._zstd_decompressor_class: # validate attr name + # reset the cached import to avoid test order dependencies + zipimport._zstd_decompressor_class = None + else: + mod_name = "zlib" # the ZIP_STORED case below + + if mod_name in sys.builtin_module_names: + self.skipTest(f"{mod_name} is a builtin module") + if mod_name in sys.modules: + del sys.modules[mod_name] + files = {f"{mod_name}.py": test_src} try: - self.doTest(".py", files, "zlib") + self.doTest(".py", files, mod_name) except ImportError: - if self.compression != ZIP_DEFLATED: - self.fail("expected test to not raise ImportError") - else: if self.compression != ZIP_STORED: - self.fail("expected test to raise ImportError") + # Expected - fake compression module can't decompress + pass + else: + self.fail("expected test to not raise ImportError for uncompressed") + else: + if self.compression == ZIP_STORED: + # Expected - no compression needed, so fake module works + pass + else: + self.fail("expected test to raise ImportError for compressed zip with fake compression module") def testPy(self): files = {TESTMOD + ".py": test_src} @@ -556,13 +573,6 @@ def testZipImporterMethods(self): self.assertEqual(zi.archive, TEMP_ZIP) self.assertTrue(zi.is_package(TESTPACK)) - # PEP 302 - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - - mod = zi.load_module(TESTPACK) - self.assertEqual(zi.get_filename(TESTPACK), mod.__file__) - # PEP 451 spec = zi.find_spec('spam') self.assertIsNotNone(spec) @@ -577,6 +587,8 @@ def testZipImporterMethods(self): spec.loader.exec_module(mod) self.assertEqual(zi.get_filename(TESTPACK), mod.__file__) + sys.path.insert(0, TEMP_ZIP) + existing_pack_path = importlib.import_module(TESTPACK).__path__[0] expected_path_path = os.path.join(TEMP_ZIP, TESTPACK) self.assertEqual(existing_pack_path, expected_path_path) @@ -675,11 +687,6 @@ def testZipImporterMethodsInSubDirectory(self): self.assertEqual(zi.archive, TEMP_ZIP) self.assertEqual(zi.prefix, packdir) self.assertTrue(zi.is_package(TESTPACK2)) - # PEP 302 - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - mod = zi.load_module(TESTPACK2) - self.assertEqual(zi.get_filename(TESTPACK2), mod.__file__) # PEP 451 spec = zi.find_spec(TESTPACK2) mod = importlib.util.module_from_spec(spec) @@ -702,9 +709,12 @@ def testZipImporterMethodsInSubDirectory(self): self.assertEqual( spec.loader.get_filename(TESTMOD), load_mod.__file__) + sys.path.insert(0, TEMP_ZIP + os.sep + TESTPACK) + mod_path = TESTPACK2 + os.sep + TESTMOD mod_name = module_path_to_dotted_name(mod_path) mod = importlib.import_module(mod_name) + self.assertTrue(mod_name in sys.modules) self.assertIsNone(zi.get_source(TESTPACK2)) self.assertIsNone(zi.get_source(mod_path)) @@ -1008,10 +1018,15 @@ def assertDataEntry(name): @support.requires_zlib() -class CompressedZipImportTestCase(UncompressedZipImportTestCase): +class DeflateCompressedZipImportTestCase(UncompressedZipImportTestCase): compression = ZIP_DEFLATED +@support.requires_zstd() +class ZStdCompressedZipImportTestCase(UncompressedZipImportTestCase): + compression = ZIP_ZSTANDARD + + class BadFileZipImportTestCase(unittest.TestCase): def assertZipFailure(self, filename): self.assertRaises(zipimport.ZipImportError, @@ -1069,9 +1084,6 @@ def _testBogusZipFile(self): z = zipimport.zipimporter(TESTMOD) try: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self.assertRaises(TypeError, z.load_module, None) self.assertRaises(TypeError, z.find_module, None) self.assertRaises(TypeError, z.find_spec, None) self.assertRaises(TypeError, z.exec_module, None) @@ -1082,10 +1094,6 @@ def _testBogusZipFile(self): error = zipimport.ZipImportError self.assertIsNone(z.find_spec('abc')) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self.assertRaises(error, z.load_module, 'abc') self.assertRaises(error, z.get_code, 'abc') self.assertRaises(OSError, z.get_data, 'abc') self.assertRaises(error, z.get_source, 'abc') diff --git a/Lib/test/test_zipimport_support.py b/Lib/test/test_zipimport_support.py index ae8a8c99762313..2b28f46149b4ff 100644 --- a/Lib/test/test_zipimport_support.py +++ b/Lib/test/test_zipimport_support.py @@ -13,9 +13,12 @@ import inspect import linecache import unittest +import warnings +from test import support from test.support import os_helper from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, make_script, make_zip_script) +from test.support import import_helper verbose = test.support.verbose @@ -236,6 +239,26 @@ def f(): # bdb/pdb applies normcase to its filename before displaying self.assertIn(os.path.normcase(run_name.encode('utf-8')), data) + def test_import_filter_syntax_warnings_by_module(self): + filename = support.findfile('test_import/data/syntax_warnings.py') + with (os_helper.temp_dir() as tmpdir, + import_helper.DirsOnSysPath()): + zip_name, _ = make_zip_script(tmpdir, "test_zip", + filename, 'test_pkg/test_mod.py') + sys.path.insert(0, zip_name) + import_helper.unload('test_pkg.test_mod') + with warnings.catch_warnings(record=True) as wlog: + warnings.simplefilter('error') + warnings.filterwarnings('always', module=r'test_pkg\.test_mod\z') + warnings.filterwarnings('error', module='test_mod') + import test_pkg.test_mod + self.assertEqual(sorted(wm.lineno for wm in wlog), + sorted([4, 7, 10, 13, 14, 21]*2)) + filename = test_pkg.test_mod.__file__ + for wm in wlog: + self.assertEqual(wm.filename, filename) + self.assertIs(wm.category, SyntaxWarning) + def tearDownModule(): test.support.reap_children() diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index c57ab51eca16b4..ed9d85408159b2 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -1222,5 +1222,15 @@ def __index__(self): return 100 +class TestModule(unittest.TestCase): + def test_deprecated__version__(self): + with self.assertWarnsRegex( + DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + ) as cm: + getattr(zlib, "__version__") + self.assertEqual(cm.filename, __file__) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_zoneinfo/data/update_test_data.py b/Lib/test/test_zoneinfo/data/update_test_data.py index f531ab316a1f21..cf1b1eff4feca7 100644 --- a/Lib/test/test_zoneinfo/data/update_test_data.py +++ b/Lib/test/test_zoneinfo/data/update_test_data.py @@ -120,3 +120,6 @@ def update_test_data(fname: str = "zoneinfo_data.json") -> None: if __name__ == "__main__": update_test_data() + + print("Remember to update the HAS_TZDATA_PKG version requirement in " + "test_zoneinfo.py!") diff --git a/Lib/test/test_zoneinfo/data/zoneinfo_data.json b/Lib/test/test_zoneinfo/data/zoneinfo_data.json index ec4414a0cdedbe..6c9bf09e12fb58 100644 --- a/Lib/test/test_zoneinfo/data/zoneinfo_data.json +++ b/Lib/test/test_zoneinfo/data/zoneinfo_data.json @@ -1,190 +1,218 @@ { "data": { "Africa/Abidjan": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~f{VGF<>F7KxBg5R*{Ksocg8-YYVul=v7vZzaHN", - "uC=da5UI2rH18c!OnjV{y4u(+A!!VBKmY&$ORw>7UO^(500B;v0RR91bXh%WvBYQl0ssI2", - "00dcD" + "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~f{VGF<>F7KxBg5R*{Ksocg8-", + "YYVul=v7vZzaHNuC=da5UI2rH18c!OnjV{y4u(+A!!VBKmY&$ORw>7UO^(500B;v0RR91b", + "Xh%WvBYQl0ssI200dcD" ], "Africa/Casablanca": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0b&Kz+C_;7KxBg5R*{N&yjMUR~;C-fDaSOU;q-~", - "FqW+4{YBjbcw}`a!dW>b)R2-0a+uwf`P3{_Y@HuCz}S$J$ZJ>R_V<~|Fk>sgX4=%0vUrh-", - "lt@YP^Wrus;j?`Th#xRPzf<<~Hp4DH^gZX>d{+WOp~HNu8!{uWu}&XphAd{j1;rB4|9?R!", - "pqruAFUMt8#*WcrVS{;kLlY(cJRV$w?d2car%R<ALOSO?^`4;ZZtI)%f^^G^>s>q9BgTU4", - "Ht-tQKZ7Z`9QqOb?R#b%z?rk>!CkH7jy3wja4NG2q)H}fNRKg8v{);Em;K3Cncf4C6&Oaj", - "V+DbX%o4+)CV3+e!Lm6dutu(0BQpH1T?W(~cQtKV*^_Pdx!LirjpTs?Bmt@vktjLq4;)O!", - "rrly=c*rwTwMJFd0I57`hgkc?=nyI4RZf9W$6DCWugmf&)wk^tWH17owj=#PGH7Xv-?9$j", - "njwDlkOE+BFNR9YXEmBpO;rqEw=e2IR-8^(W;8ma?M3JVd($2T>IW+0tk|Gm8>ftukRQ9J", - "8k3brzqMnVyjsLI-CKneFa)Lxvp_a<CkQEd#(pMA^rr}rBNElGA=*!M)puBdoErR9{kWL@", - "w=svMc6eZ^-(vQZrV<u^PY#nOIUDJ8%A&;BUVlY9=;@i2j2J1_`P>q40f}0J3VVoWL5rox", - "`Kptivcp}o5xA^@>qNI%?zo=Yj4AMV?kbAA)j(1%)+Pp)bSn+7Yk`M{oE}L-Z!G6<Dgq&*", - "(C-mFJfbEGDH5M^vBr65rcnsx*~|Em_GeU#B)(+T!|MG-nxj0@IPbp-nHejH3~>OMr5G+h", - "p)$3Lg{ono{4cN>Vr&>L4kXH;_VnBL5U!LgzqE%P7QQ*<E!guRW2SE@ayq@)G2nXqA2tGo", - "QIgc6>tue}O`3(TZ0`aKn&~8trOQ-rBXCp)f@P6RMO4l0+;b|5-pk9_ryNh}Zc*v%mvz_#", - "yd<xXt%~gT90dn4e{Ac<baL-)Y{L7&5G($I$>6fjB0g9{MmMnu8bG%#C~ugXK^S^k@?ab#", - "O|aE>dDTt4s4n69(~@t~!wniV%g<uWQat_i6>7khFx~I*4>Y|V$4j5%KPF*-FyKIi@!Ho&", - "x8QQsksYt8)D+W)Ni!=G`ogSu^vLL-l#7A7=iIAKL2SuZk9F}NfNk86VI)9WZE?%2wC-ya", - "F~z#Qsq)LH0|_D8^5fU8X%GeQ4TB>R-dlziA&tZe&1ada208!$nk`7bOFO2S00G<w{Sp8G", - "{cR_IvBYQl0ssI200dcD" + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0b&Kz+C_;7KxBg5R*{N&yjMUR~;C-fDaSOU;q-", + "~FqW+4{YBjbcw}`a!dW>b)R2-", + "0a+uwf`P3{_Y@HuCz}S$J$ZJ>R_V<~|Fk>sgX4=%0vUrh-lt@YP^Wrus;j?`Th#xRPzf<<", + "~Hp4DH^gZX>d{+WOp~HNu8!{uWu}&XphAd{j1;rB4|9?R!pqruAFUMt8#*WcrVS{;kLlY(", + "cJRV$w?d2car%R<ALOSO?^`4;ZZtI)%f^^G^>s>q9BgTU4Ht-tQKZ7Z`9QqOb?R#b%z?rk", + ">!CkH7jy3wja4NG2q)H}fNRKg8v{);Em;K3Cncf4C6&OajV+DbX%o4+)CV3+e!Lm6dutu(", + "0BQpH1T?W(~cQtKV*^_Pdx!LirjpTs?Bmt@vktjLq4;)O!rrly=c*rwTwMJFd0I57`hgkc", + "?=nyI4RZf9W$6DCWugmf&)wk^tWH17owj=#PGH7Xv-", + "?9$jnjwDlkOE+BFNR9YXEmBpO;rqEw=e2IR-", + "8^(W;8ma?M3JVd($2T>IW+0tk|Gm8>ftukRQ9J8k3brzqMnVyjsLI-CKneFa)Lxvp_a<Ck", + "QEd#(pMA^rr}rBNElGA=*!M)puBdoErR9{kWL@w=svMc6eZ^-", + "(vQZrV<u^PY#nOIUDJ8%A&;BUVlY9=;@i2j2J1_`P>q40f}0J3VVoWL5rox`Kptivcp}o5", + "xA^@>qNI%?zo=Yj4AMV?kbAA)j(1%)+Pp)bSn+7Yk`M{oE}L-Z!G6<Dgq&*(C-", + "mFJfbEGDH5M^vBr65rcnsx*~|Em_GeU#B)(+T!|MG-nxj0@IPbp-nHejH3~>OMr5G+hp)$", + "3Lg{ono{4cN>Vr&>L4kXH;_VnBL5U!LgzqE%P7QQ*<E!guRW2SE@ayq@)G2nXqA2tGoQIg", + "c6>tue}O`3(TZ0`aKn&~8trOQ-rBXCp)f@P6RMO4l0+;b|5-", + "pk9_ryNh}Zc*v%mvz_#yd<xXt%~gT90dn4e{Ac<baL-", + ")Y{L7&5G($I$>6fjB0g9{MmMnu8bG%#C~ugXK^S^k@?ab#O|aE>dDTt4s4n69(~@t~!wni", + "V%g<uWQat_i6>7khFx~I*4>Y|V$4j5%KPF*-", + "FyKIi@!Ho&x8QQsksYt8)D+W)Ni!=G`ogSu^vLL-", + "l#7A7=iIAKL2SuZk9F}NfNk86VI)9WZE?%2wC-yaF~z#Qsq)LH0|_D8^5fU8X%GeQ4TB>R", + "-dlziA&tZe&1ada208!$nk`7bOFO2S00G<w{Sp8G{cR_IvBYQl0ssI200dcD" ], "America/Los_Angeles": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0qH3OkDsf7KxBg5R*;z{h&-RlhRYu$%jt%!jv+I", + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0q%JPh9{i7KxBg5R*;#4G>H)lhRYu$%jt%!jv+I", "JxhE=%W1?wYb!37Rb?(rgwFIAQI{L#8r*zy!$TMtER_1(vn(Zix^{AVB1(jwr$iL6h0Z!2", "8Gb~UW@0~e512{Z%8}Qzdnjl~wJ1{c2>`Z@1A~t&lyL{p{eM{5)QGf7Mo5FW9==mlyXJt2", - "UwpntR7H0eSq!(aYq#aqUz&RM*tvuMI)AsM?K3-dV3-TT{t)!Iy#JTo=tXkzAM9~j2YbiO", - "ls3(H8Dc>Y|D1aqL51vjLbpYG;GvGTQB4bXuJ%mA;(B4eUpu$$@zv2vVcq-Y)VKbzp^tei", - "uzy}R{Luv<C;_cPe*n$Z<jeC9ogWF9=1mvvUYXS>DjpuVb`79O+CBmg{Wx!bvx$eu4zRE&", - "PehMb=&G<9$>iZ|bFE)0=4I?KLFGBC0I(0_svgw0%FiMsT%koo*!nEYc6GY@QnU}&4Isg;", - "l=|khi(!VaiSE2=Ny`&&tpi~~;{$u<GHlsr3Ze!iYsU205RFKsLnrXwOL?Mq08xffgS{6h", - "E|figx+&N%wbO}re@|}$l;g_6J-Wl%j|qev8A<T?NJ)`;2neGi_DHE4ET*W!c*ggPAgU+L", - "E9=bH7;maCUikw^R)UM;TdVvNkQ;FGgN=yQER`SZ1nOgPXr0LCebLety&}kVdmVmB=8eSg", - "td!1%p=a2wooIL!Da}OPXvKBfRo?YxqS>N}%f|7mBhAy;<Er2&_LfND#qXN~Mkgf!@4VFA", - "Hr%$c)wrKA2cJYWK2>s3YT^sy!$eG~?`9mNJC9@4Bac_p^BZh)Yd_rWW5qh-?tKY(>5VHO", - "L*iT8P@wCavLj^yYbnDR+4ukhS+xPrpl)iqB?u)bj9a2aW==g6G3lCJd>(+Blf<d4CF%7u", - "tlBUDki}J-!_Dy}5S(MrxSXy~$Z+hgH3P^<<w7D72L7I-R%H3(xm&q_DXxkp$owLTS6Wzk", - "hc3nn;laROa3)6hl&gH#)2Lif8fZe$@CdeJ-Zn&*>r)~^40F4f>cRZ^UF;RibfZ>0m73hR", - "C{$vTfC(STN`g7(B<=Z2556{}0`?p&|Akkst!4Xy4OT;A@c$XTUI3FRRjy*KA7uC56FD)z", - "^X{WV*sr(w!c$W357o!&eLO2wTDNOyw@gf(&R<<LF_3URI4=Ei`-%dM3T66j#9!aG7&b_@", - "g1-9vo?DzXZ5vGaf~w__p_@_X?OdvQ_r5bvy2hpESTf+{p?jL+!~!{g8-<-5$@d8EZV&-5", - "@a|;^1gB*R-~{EHFA-td_G2bt;~Y}>t;=-Tu1TV{>%8ZVATC9tjD8|(&`$9YHvZ9bVe#>w", - "|8c;Tg|xE&)`*}LwM*E}q}q8^Qja%p`_U)*5DdLI9O@!e=3jFjOCrCq28b_bb;s>%D#iJB", - "CWJi{JH!Js;6nfayos$kq^OEX00HO-lokL0!mqm{vBYQl0ssI200dcD" + "UwpntR7H0eSq!(aYq#aqUz&RM*tvuMI)AsM?K3-dV3-", + "TT{t)!Iy#JTo=tXkzAM9~j2YbiOls3(H8Dc>Y|D1aqL51vjLbpYG;GvGTQB4bXuJ%mA;(B", + "4eUpu$$@zv2vVcq-", + "Y)VKbzp^teiuzy}R{Luv<C;_cPe*n$Z<jeC9ogWF9=1mvvUYXS>DjpuVb`79O+CBmg{Wx!", + "bvx$eu4zRE&PehMb=&G<9$>iZ|bFE)0=4I?KLFGBC0I(0_svgw0%FiMsT%koo*!nEYc6GY", + "@QnU}&4Isg;l=|khi(!VaiSE2=Ny`&&tpi~~;{$u<GHlsr3Ze!iYsU205RFKsLnrXwOL?M", + "q08xffgS{6hE|figx+&N%wbO}re@|}$l;g_6J-", + "Wl%j|qev8A<T?NJ)`;2neGi@^_$pxs;>M<D!I{tC-j+T1~*cwYttg9!+Q#-", + "3v6Saj3h8^~MB$h|*(;kAscyJKINq{Cf5mX^-&hWdY!FT=0og-vvZbyAQtkOs7Q$E{N_AV", + "!!c1+D7Ub{;^)2(>iozmmC;M)^ZwTGGtj|3E=aVrD5Bqa^;RV?wWs=hsohk2ql1XcqaOW|", + "E4urOCR0a_;Id)cJ-GU5w3iJ10Aox!FH|8SHoiD_{JMEEOV0O?MDsDaI;Y>v=$rgN_q~t`", + ";bL0p0cr+3oR<~L*q!A1N=YxYRoqaQo%ua#07hqE;U_X@B)L3Jd;2oN%<UMR&=A0TqSiEB", + "wgSrE^0emfRx6XBw90Y7S-{?+qA5<Lkx|CXym1<j0It$``e-", + "X+>}66Gz+0zO^<`*Jt_si5v;Qg-", + "3q})ae)#%Dl>20mc9;6%A4(VObs;0JpQAGD7ADLJ?<x`z`b)-", + "OhP%tJ&2xIc5Yafu_9A(Mj>Rqiv#V5ZljDomsklGi_^8gtLkmRF>EstlBz)cI_>MG*?Z%S", + "T;72Q8^Ku7t$MI_A1{5SEE>mII;1T8kfJf_FgrXNwx!AelhM`I@+>J+gg!uCyW&Vjnd0r~", + "xZpKPsHdS4@$h%w6Eq6>tdfnmQLWvk`|e2>DM-E4Qm9Nc-", + "(V_Gh@)x^Nj3H9p`fLm1IA_eaBVtRg`l1J@5pcFi$-", + "31XWMxJ!;}xqC^I};&9=Az*&VH*lfN0M|Gx-9!ypV67!-KSwt=VI-*jej4FC~WiMTU+-", + "L<2+d_ew|{n~!VJuGlSJ0d_=`@?_jC3h)r_?|J_;aJ|`U^>F6N?3pcY069b8yy#(^>pP8L", + "*@Vg?IIMosq$u-00HX=q!s`G%_Rg?vBYQl0ssI200dcD" ], "America/Santiago": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0fRZ<6QtM7KxBg84(fsEAUJ$J{f-TXlPEUec5Ee", - "n+hsD4lC(QYax=JdSpoyje8%VM`GW}<Unz6IOY4=y66tfqG2X4E8xIJQ(~?r{`L~T!sI~o", - "VBl7Ao!R1A76Y8P6Y<TfwVHf@sl@S-D4OuAy5mq0MKJZ>{bJ8@y$A8O&*$pw{(f~Os#}2w", - "eX6^Rgi$IT%n^V^85L>$_c7{cB^#ogV=rHBJGiz-RQNFGK?gdPi|q)j`&8)}KJ{qo6dixa", - "9@yYyVg+%lo0nO+Tw0-w2hJ%mafy<Co(;L+24CYl&?rN0mrh90nxG?%1&Ed@za`Yd>WL)|", - ")<o0dZL-*?RFtH7dAv%G*O%l?qvq!0F5C?K#_ZoT{P$77IMoj3&8w3f&n36zquu~s`s0T)", - ";>?W6Bi%FWuGPA1Dru$XR4SZANsAthU2EoKH<MU4wYvUTlZGcLIDR+hSik>F6oEtKq`rwP", - "(VNegnI_NI%;ma$)wj{k!@KFB30Yo)IOr<QX7IQ@TBq9d;e3QAtYU?$PS-WoaiqwFrg4PR", - "A->l>)$)D|+(5h&+%2vuwGuy^@S8FT^s21V5};>VA9Iu;?8bHz#r<;JtfZDI1(FT@edh0#", - "MYW$A1qkMGIwTZqqdYNE3gl#zp&NbL9Mp=voqN|;?gqR&4$)1`znddtEyuKS*^nMMD=0^>", - "7^z6-C4P67UWOXuMBubP>j6i~03aR@jD^-Y`JSYu#Yp0P8dLLJ0QOPE8=BoiuRX59YW7xg", - "WiexjHX%&0?`ZQCdxCdL^qd1v@kOjQKaWo2Y1++~LcA%FTq?5o<?(jL(_Uo}I}k_Fwflcr", - "aovwSR_(ILA6li<iBLPQ0#rEet;W-*54kj#sZEGK*tAF{)HNkn#&Hc5`#eaRF;N#$<xQU?", - "E%zm?2+b5Ho>%}fX1-RIvlB)1#iTNomGnUL=nM!>Ix|AGtON7!F1O?53kqlC2o-`ZGw*+s", - "NM$^9znsIJMwlgscE`|O3|;BRgsQMYm~`uv+nvuv`nigRa}X=BX=A5Sw$)WEklF7&c>_~$", - "zJ(m--bqXgiN^w-U=BJH9C0Qro(x90zo@rK;&TJ$nI@&k$ORgOb2<MjjIhYfr;pFUGdMd!", - "0d&bOvyq3AZPCez8E(XSg2hBu2A&^k?w|1u8v3JE>s%gWbc}ok_27)Eoku~Fq|B-Ps+4J_", - "HPJMLJ2^_)cOU$p&3kNAlrV!)%~6r$BJ>OOi~=-<6byle{?zd4J{NG}o8tw|+#ZNLcpNwk", - "TuPE~sbJB8_RZb2DopStO+Wwux~F#S59zm%00I98;S&G=b(j+6vBYQl0ssI200dcD" + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0e+L++6@F7KxBg84(fsEAUJ$9Yj;p>Mq3>*@-", + "8>razI(EFyOtYzTyCY`3kVX~k`hSJt^$bT@V;iGRbT`JI3V)lBnui#lA$wIsfE8sL=RFVi", + "Wi5>;Y=+h5(#B8hW33CV<Q;A#rKAP&WQAy#{Hgd}k5cfcABuYPC#AID{sCG^k7cV;Yjn{S", + "|8$i(c-", + "ZWn+h)T(*sa4gX}q{a%>CYO92<z+UyE(W(N=OBKTA!5FCM+4etR}Gx{Khjr_hfVM-", + "3bY_3-f(4kMdWkgjQjaFM1%?zB^dnmPD^*OEjG@|L4T9oM==J-", + "1~cwbTkO+{<rZ1j%y$A#;`~jj_5>MtE<w1^%V}&!W~^`U#G#Kclvxg2eSK@Sb_pe)d8JOq", + "iIv8N?HvCOAa(Pe)%y{uRZ=iSffMAb29m~K3V!5$(+5c4`<&%}YBXxa{Nmro^{EpwXUg(M", + "d=g45)b1lm^o9O|j=$7W0%Fg!1vvZpZsS3gq(aYGRPC6r&`5zc<D8jH3uj3*Twj>mXQtd@", + "E+3y)$%o~XozOC|QgFl*-mp9+(`s0w4O4a$v!xt`G&ZI1uE4c`JE-t5WW%1*91J-", + "tr^_>U@@l^R_i@%gJ)q{kYX|w-", + "m{DbN=Z+Zey_(<=ZlU4e**~fkShDtX&9_W<ytT83B=B(s`~GrFf60my*fRSlCwvb~u-", + "3SYP<jcj2q)<&>>qj>RuJZ-ko&-nY}Eq_?SFX8N<9wT-TH*xbF0XT;L+siI1gPQrnKc|85", + "UZaq6@7JAVeu%)Aa6I>6D;F*N)|f6jhs01q_UU%gc_Ek#>SbTrf6i;OsAQ>8&urTeX|Ier", + "uSV{o||MjbXV?)AHzh1+v~odHV<_+-", + "M0v=QR6~y%&K00{%grZ=+5)JNlFg!9Q}2rv{$OC*D{_yiUd$SL!S)f@K#pe<#CBb&}BH0p", + "%w(SzGJjGX`q+ybM!e4GEP>b&S_4lnbBR!*5>ZIxYP?QK2DIny}eL_od7Ll|WdneS(sbh~", + "!}|r_VlzIOB|lsf+rf*<iF2;5mq&ws+~(vXucW*o<B(-B;$$S+_(`@_xgEw=t_P_S=K-", + "2*4@GPK8pEDecQUL_<_<IfI`6dpUo@#J|q(HK=)fN2`##&}B&C<HKnv-Zo68>B~wqYWzu*", + "k-", + "19B1stj0gO@Kd^97k>cVnNvEEaMwZ6=ObszLNnyDGMW0?9>PhVq$r$BCx^ZdJ#fO^fC1JG", + "oW>FS5oC!pr7=y}O@ecL6T+R$E20#GvlwVfswl?qnCEe|@!hI*Ooyk8~vfN}h@~NGp9$00", + "H<1(-Qyy=a@C3vBYQl0ssI200dcD" ], "Asia/Tokyo": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~luMgIxeB7KxBg5R*;y?l4Rl4neXH3cv!OtfK@h", - "KZzauI)S!FSDREPhhBS6Fb$&Vv#7%;?Te|>pF^0HBr&z_Tk<%vMW_QqjevRZOp8XVFgP<8", - "TkT#`9H&0Ua;gT1#rZLV0HqbAKK;_z@nO;6t0L<i8TZ+%T<;ci2bYSG1u!mUSO5S3XcbN8", - "dIxbZ00Ex?wE_SDJu@vkvBYQl0ssI200dcD" + "{Wp48S^xk9=GL@E0stWa761SMbT8$j-", + "~luMgIxeB7KxBg5R*;y?l4Rl4neXH3cv!OtfK@hKZzauI)S!FSDREPhhBS6Fb$&Vv#7%;?", + "Te|>pF^0HBr&z_Tk<%vMW_QqjevRZOp8XVFgP<8TkT#`9H&0Ua;gT1#rZLV0HqbAKK;_z@", + "nO;6t0L<i8TZ+%T<;ci2bYSG1u!mUSO5S3XcbN8dIxbZ00Ex?wE_SDJu@vkvBYQl0ssI20", + "0dcD" ], "Australia/Sydney": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0T)o7+nA=7KxBg5R*_t6jS5T`_Ull(nK1_YY;k%", - ";_YdTuU3*!K)eKg@^kzjAtbo@Jd|KGai=Q%%sX5FI?*?LG!|m9cKH5~IEwI=PAr_Yc}w35", - ">}hOdk<>TdUa07R(LPI6@!GU$ty4=mwqHG-XVe*n(Yvgdlr+FqIU18!osi)48t~eWX8)&L", - "G)Ud^0zz@*AF+2r7E}N<P$kOfo*88g)_bOO?7N1Jr|HJyg+HXc7f4}?%Dur3w|~JU?<x4K", - "%RRC~q_D87;UyN{nLRu!fEqKeRR*U$vs>f9Y72K~o-T%}D&z%}#7g<qim`EbfhF7ntyAiP", - "%LFNc&!$@Kv)Olyf&Y9%(#SkM+%yI}S%b+@ZM2dH7DpmndGMIda<(`#E9q|?H(HzClx+l;", - "M?IEz1eF}r?}ay!V9?9rKD^-ayjE@wUMD$2kC!iwH`n=eVrJPmJyNKaW`LdJ68&u;2nF1K", - "kZjKCY_A<>2br?oH6ZiYH^%>J3D)TPKV(JY*bwjuw5=DsPB@~CrR<E_U_fJTF9ufU%!cXK", - "_4uM#!%%Q1e1G~{E}~vGVE0{Kxecm^NjtJM`c8EFHFTiUIVl@YUD8F+s!u8jz~6hte@oa|", - "qayb*^Lwd(etNmBro;aXQjkY8g(*`_JQ0%{V3QP2l!GGQ7D+v&k_PK0F(?f{GziU5>OZeN", - "x>A*H&CHrWt0`EP`m!F%waepl#|w#&`XgVc?~2M3uw$fGX~tf_Il!q#Aa<*8xlzQ2+7r6Z", - "^;Laa9F(WB_O&Dy2r>~@kSi16W{=6+i5GV=Uq~KX*~&HUN4oz7*O(gXIr}sDVcD`Ikgw#|", - "50ssal8s)Qy;?YGCf;*UKKKN!T4!Kqy_G;7<gSrPK{)5#a>PfQapugqvVBKy12v3TVH^L2", - "0?#5*VP~MOYfe$h`*L!7@tiW|_^X1N%<}`7YahiUYtMu5XwmOf3?dr+@zXHwW`z}ZDqZlT", - "<2Cs(<1%M!i6o&VK89BY0J7HPIo;O62s=|IbV^@y$N&#<x=a876<(U>=>i^F00FcHoDl#3", - "Mdv&xvBYQl0ssI200dcD" + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0TQa5M2N&7KxBg5R*_t3)o}P`_Ull(nK1~U=~g{", + "!2#v7ta-VPM|>+w;XP~5#XzS!mmq>y(@${PQsHdkBYiJixTuCQ2N?55>HhxKTEufgeVl}_", + "Sq_FUIXFF$kI6!-", + "eKVzudw1Zyg8;a%MynCz&_3Cl@%Zs*_JRo#I#Ss92V)!~{AwVung!|wK?4JFyFv#=nfhNI", + "6Czr|3VjINV7V6#eFr=Gl1UDdL9MQRWZ`RiZET+f<B?psO04A!^T@|OjD(g9%6r^uKWL~*", + "l5l((#kszU!MR&R^KmhB+uQG;V@@lt4iky54!7C3s3sgfut6UfX5na|U#Q8$>l#=kxZ_-", + "G@=#VCQEvCZVdj9;e?rY8ynpo&34D<xf3gdy0mso_&ACxT<X@=iK)(!)Cf=UJXS@mJ>Pa3", + "KI_Sc_0#?r^44F}Rzb_ChM7PPrq3@FkqqjWqgvOzPq+vkb;DT&2YMC|tC3lu6Ihz2prrFP", + "vOU@6I3$XU@68kFFWx8fx?jt@frXW`;AZso*plI73>*wENVoc+2cm`jgoe6(B`w`{Bdx<i", + "@MbN&WNDIQhD$&B-", + "uG<$!*}1d++*d~BtG=}tp$n^wQ7tVUh5HXx%pYRBr8eErtbe5x3d1_@!5Dfbr?|~LLM#@T", + "Jp{TC>VOWD1rqV<x%ZP)Pc1Wb+mhO{S8(-", + "3ndm1jWw?3!z_VRO<>TCN+Y4kRvHLYXlr)vU4G7S$p~Ia_n|G>=3N5dDB8cS6IGd-ckfc7", + "8#^71HtN_BO2#F%&%Ll+IhR1DlEUG@+#_Yl8AKa1US?{&+yOwA=WA=!z-", + "B9t2>Tn9Wu2*8Ydrbage(I4dkD_vcP(|c-", + "LhGRkSn{W*nk?RJQN!j1RSZ}L6e<&UGMisk>nLH5JiLNgOSC7c&Mn1+Pit*Gz;s=M?_q!v", + "DZ@dJctlzl2>W(rvs{(CERM)RT0xuqDAl{)$*ztEhe=?|;3u&!6O1c88W}SFHDBBHh%rrh", + "U6=f6kjYt!N19xk*s_9IpbeS;(cPoTPX!vr00FE9ju8L=r}(KdvBYQl0ssI200dcD" ], "Europe/Dublin": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0>b$_+0=h7KxBg5R*;&J77#T_U2R5sleVWFDmK~", - "Kzj5oh@`<njquRZ&tJIS(cXp1>QKHvW^6V{jU-w>qg1tSt0c^vh;?qAqA0%t?;#S~6U8Qi", - "v&f1s9IH#g$m1k1a#3+lylw4mwT4QnEUUQdwg+xnEcBlgu31bAVabn41OMZVLGz6NDwG%X", - "uQar!b>GI{qSahE`AG}$kRWbuI~JCt;38)Xwbb~Qggs55t+MAHIxgDxzTJ;2xXx99+qCy4", - "45kC#v_l8fx|G&jlVvaciR<-wwf22l%4(t@S6tnX39#_K(4S0fu$FUs$isu<UOJYm|4)2i", - "aEpsajn@}B#rnY=Cg_TXsm-A)*adXV&$klNTn3n{XXlaquu}6m{k%oRmY0Yyhlj*<W{D5m", - "22}OiqnwHT!tnK`wPqx?wiF%v{ipTrOkcJ5P@7OC4(-l`*&SB$Wd4Vf8gn?>d<i@%mP*e*", - "ttDj`9M1;9$YV@dhT)DVcwdq(Ly~KDm_&KL?{_mFwwYtJqRZBk)i1FVQy!40w_KyAg?hIA", - "=_{(3#S0eWsF8f%_4Zza$4@$lSmov+Huyn$vP^zJ|8-<C3#q#0kEs9cNg^xUR(m?wEWt-D", - "GctAh2nIo~fz%$m$I41=b_WuJ6M9g#A9_Epwqw{d0B|vzmg#_y<=_>9IKzCXB<o`d)**5V", - "6g!<<Jw1n5TrN-$)aYz4cLsTmpsUf-6L7ix+kk>78NkARYq@9Dc0TGkhz);NtM_SSzEffN", - "l{2^*CKGdp52h!52A)6q9fUSltXF{T*Ehc9Q7u8!W7pE(Fv$D$cKUAt6wY=DA1mGgxC*VX", - "q_If3G#FY6-Voj`fIKk`0}Cc72_SD{v>468LV{pyBI33^p0E?}RwDA6Pkq--C~0jF&Z@Pv", - "!dx_1SN_)jwz@P$(oK%P!Tk9?fRjK88yxhxlcFtTjjZ$DYssSsa#ufYrR+}}nKS+r384o~", - "!Uw$nwTbF~qgRsgr0N#d@KIinx%<pnyQ!|>hQB(SJyjJtDtIy(%mDm}ZBGN}dV6K~om|=U", - "VGkbciQ=^$_14|gT21!YQ)@y*Rd0i_lS6gtPBE9+ah%WIJPwzUTjIr+J1XckkmA!6WE16%", - "CVAl{Dn&-)=G$Bjh?bh0$Xt1UDcgXJjXzzojuw0>paV~?Sa`VN3FysqF<S*L0RYSAY3jt(", - "8wCD04RfyEcP(RNT%x7k(7m-9H3{zuQ`RZy-Rz%*&dldDVFF+TwSAPO1wRX^5W5@xJ9{vW", - "w?rc^NH({%Ie<rxKqSVy!Le-_`U&@W_(D+>xTzfKVAu*ucq#+m=|KSSMvp_#@-lwd+q*ue", - "FQ^5<D+|jLr?k{O39i8AX2Qb^zi9A<7XD1y!-W2|0Hk8JVkN;gl><|<0R-u4qYMbRqzSn&", - "Q7jSuvc%b+EZc%>nI(+&0Tl1Y>a6v4`uNFD-7$QrhHgS7Wnv~rDgfH;rQw3+m`LJxoM4v#", - "gK@?|B{RHJ*VxZgk#!p<_&-sjxOda0YaiJ1UnG41VPv(Et%ElzKRMcO$AfgU+Xnwg5p2_+", - "NrnZ1WfEj^fmHd^sx@%JWKkh#zaK0ox%rdP)zUmGZZnqmZ_9L=%6R8ibJH0bOT$AGhDo6{", - "fJ?;_U;D|^>5by2ul@i4Zf()InfFN}00EQ=q#FPL>RM>svBYQl0ssI200dcD" + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0>b$_gw%g7KxBg5R*;&J77#T_U2R5sleVWFDmK~", + "Kzj5oh@`<njquRZ&tJIS(cXp1>QKHvW^6V{jU-", + "w>qg1tSt0c^vh;?qAqA0%t?;#S~6U8Qiv&f1s9IH#g$m1k1a#3+lylw4mwT4QnEUUQdwg+", + "xnEcBlgu31bAVabn41OMZVLGz6NDwG%XuQar!b>GI{qSahE`AG}$kRWbuI~JCt;38)Xwbb", + "~Qggs55t+MAHIxgDxzTJ;2xXx99+qCy445kC#v_l8fx|G&jlVvaciR<-", + "wwf22l%4(t@S6tnX39#_K(4S0fu$FUs$isu<UOJYm|4)2iaEpsajn@}B#rnY=Cg_TXsm-", + "A)*adXV&$klNTn3n{XXlaquu}6m{k%oRmY0Yyhlj*<W{D5m22}OiqnwHT!tnK`wPqx?wiF", + "%v{ipTrOkcJ5P@7OC4(-", + "l`*&SB$Wd4Vf8gn?>d<i@%mP*e*ttDj`9M1;9$YV@dhT)DVcwdq(Ly~KDm_&KL?{_mFwwY", + "tJqRZBk)i1FVQy!40w_KyAg?hIA=_{(3#S0eWsF8f%_4Zza$4@$lSmov+Huyn$vP^zJ|8-", + "<C3#q#0kEs9cNg^xUR(m?wEWt-DGctAh2nIo~fz%$m$I41=b_WuJ6M9g#A9_Epwqw{d0B|", + "vzmg#_y<=_>9IKzCXB<o`d)**5V6g!<<Jw1n5TrN-$)aYz4cLsTmpsUf-", + "6L7ix+kk>78NkARYq@9Dc0TGkhz);NtM_SSzEffNl{2^*CKGdp52h!52A)6q9fUSltXF{T", + "*Ehc9Q7u8!W7pE(Fv$D$cKUAt6wY=DA1mGgxC*VXq_If3G#FY6-", + "Voj`fIKk`0}Cc72_SD{v>468LV{pyBI33^p0E?}RwDA6Pkq--C~0jF&Z@Pv!dx_1SN_)jw", + "z@P$(oK%P!Tk9?fRjK88yxhxlcFtTjjZ$DYssSsa#ufYrR+}}nKS+r384o~!Uw$nwTbF~q", + "gRsgr0N#d@KIinx%<pnyQ!|>hQB(SJyjJtDtIy(%mDm}ZBGN}dV6K~om|=UVGkbciQ=^$_", + "14|gT21!YQ)@y*Rd0i_lS6gtPBE9+ah%WIJPwzUTjIr+J1XckkmA!6WE16%CVAl{Dj@)nt", + "QW>5WCgG;uYjCg(j>qCs(*}h9<Z8uh9!SSPmJ9Us;ag{DE^LB{a>IWV8w>LXsNq+C>vLWc", + ">tZt7asixk*XDd^+o!O=O52B7aPqt&HfgmRCd4-+ky?P-", + "XP?5Np6dxH29G;c3@torNWCs!ufT_DK<~j-", + "UgqAdf5G4ZdP`bECDkw8*?qvrtnMfoJT^Wq|8_~4;n!EAt=*dv)-", + "0yF8uKXAiZ~3d|7Z>ZO|t~YD2@;`6Qg{xlf8Q&;*xB-Ys_R2Cp{y((HBw7kN@@CtfA<<$B", + "fGc2BhK{XBpaQiJGWmO!j9XN7n0YhjD8vjw0{?{0zZm5js?lNS*8*{rU&d|`Rl_i(p2n<E", + "2E*;E@{_pkawrja2!Gd<d7B3gss7z!RY?4jLVEtA|Zd9xtHrRH@!`4#&7dlz(EmqJ50R7o", + "WFfxIQ=*$MQ;FXMyzYE7tuZ8vWD!}Q(QFk)FlkhmB%|FGo4d;`{0trwc>Hf}VB&&P+m6k^", + "y|9Wu}iApF2OdyoJC2VV0^9?>qd00EN<q#FPLQfh6$vBYQl0ssI200dcD" ], "Europe/Lisbon": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0=rf*IfWA7KxBg5R*;*X|PN+G3LqthM?xgkNUN_", - ")gCt1Sc%YT6^TTomk4yVHXeyvQj8}l<;q&s7K}#Vnc8lII1?)AHh$*>OKUU4S;*h>v*ep0", - "xTi1cK2{aY*|2D*-~K<;-{_W+r@NvZ7-|NZv($ek_C%VfP0xjWeZP#CPXD`IKkakjh(kUd", - "&H)m;^Q(jGjIyiyrcUMtOP)u3A>sw6ux;Bmp3x$4QvQKMx5TrCx_!$srWQuXNs&`9=^IY1", - "yc&C31!sQh7P=Mk*#6x8Z@5^%ehR8UW<EvzdWer9z;R6PrdUaWab3G>$OWw0KMw}P1ycI^", - "4eh12oBUOV?S>n*d!+EM@>x#9PZD12iD=zaC;7`8dTfkU_6d}OZvSFSbGgXeKw}XyX@D=(", - ")D0!^DBGr8pXWBT$S-yhLP>Z3ys^VW<kSQr?{jhl<+{Fki;mTI=&Stgy$rttN?ulQM$lDr", - "G7))C7Dx=J6V-e^(Qk|r;f~TvIw1KqRIC{8f^jPy#blstV{-&2a}ZJe!Zr2c_R4NT)L@bs", - "+gRRm6Wn)VWVNHeK*TEV=f#2KZqu%y?mTx#EfRiK0)TG7$$~=LGxx@0D|lS2up|oCON{YQ", - "oN5-H$!_n-Kx2*=RO!epEX>3}RQ6{NGGVJG6vf*MH93vvNW6yLjie1;{4tVhg-KnSf|G`!", - "Z;j$7gJ1ows~RD=@n7I6aFd8rOR_7Y?E-$clI%1o5gA@O!KPa^(8^iFFeFykI-+z>E$mvp", - "E_h`vbHPjqkLs`Dn-0FV`R@z|h!S(Lb;M&|Exr<u8#s-T(>!biY`%bfp$6`hK;GDhdP|^Q", - "*Ty*}1d41K>H2B{jrjE9aFK>yAQJBX9CD%-384S;0fw`PlprHGS`^b$oS-`I4VH7ji8ou-", - "g|060jfb1XcxiInT0oO<S+<vh^)XY;lr@|IeXj}%k;}|kSlDGaYidk^zB|gEYaet~F%QYd", - "f7pbnQKLZ0o7=kso86doS;J@aQ>oeR7#%e5Ug5#KW)nV<Rc;|LjUDdhk8*dYJQwYN?hzH%", - "0<XB$!(rpf2nxaL22M`L4pKx>SRvLHNe$SQHM@2)`S9L7>RL@<XAlxVQfb2=%lcu!h+Um0", - "Q+Z=itevTFy}-Jl<g5crK55BF`VsoPH~qP3QrG%YtrD#s{=gA7p)QI<i=EwY(cel8`B=#u", - "Yq<K;4T(QBF_GvrYueSk*}gfrCSg22+YH-1N<WYkp|DA-P-&va<Xu<}^yafJKlzezB-lS{", - "a++P_^gYmgrc9FO-K3s~`jAcqVV!k?NV2IFV^86`cr>Qx%fmm7?3u7P5TywFQ}C@S(pq}|", - "eLPT{C^{<0Q?uU&kSVd%!~8q3;Z0s3OqzF`$HRkePL5Ywgiwn{R(<RY8ut&RJ;$?J*w*n)", - ">zi+jmOBFrVpW;)@UsU#%$8BcV#h@}m$#!Fglo&bwb78aYqOG_W7h{eb(+39&-mk4EIXq_", - "_`30=8sfA3=!3TO_TyS5X22~?6nKngZ|bq=grdq=9X)3xAkA42L!~rmS)n3w-~;lgz%Fhn", - "(?rXdp2ho~9?wmVs2JwVt~?@FVD%`tN69{(i3oQa;O0<Hp#T5?$WIy3h`IlL00Hv}jT-;}", - "Z2tpNvBYQl0ssI200dcD" + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0?wE>|FpV7KxBg5R*;+c_2(RlxFoAjepiN+t(&A", + "khpF;7sO(r>4o7M4zZVa$XY~v@-", + "@YI68ej+V4yBoGpi5ZYu;Zw>N;+Vkw)1TIVA4;Vn*JV=%f`o+fC9=H@gz8U6N-", + "<zlQBNWwuXloIY|=>M+jj4KAFBq5|dk<~RQE?jJV`tG;f@gAn7I{P&H39;;isJ!fMa`+wG", + "KkWXbi?P7&ivzm}BJ9guJ&D1=Ci_?iEC=T+}Ht)wPz7a<P1u#Hd12~xVtoKNVZZ9bK!~Gf", + ";7HSY!7IC}gsZ4-", + "I$jT1i`pR(J9#tXon~s<`_^d}&`qr^@i)SF`Bb~U?H>zBV$vs`Fg0TYba5j@iBgmJ}w{Wg", + "C;WaVQ5{ZjAIYY-lC8|yfnqplNF0_6r6Jo}h*-", + "^e6n7IHY9#^nyJM($bLr;XKyoo4e=k4E_rXAXU|8;BGkQ^kNAtK@OYtPw+AVo@wAk+F{Xd", + "YfNlG<<-FnlW4;LdIej=F0WFr~-c^-", + "M)7hP!$diHGAim;v4<LV)m{cX4!QE}&4=e0*`q#JM6ECR9=km919W`%;z(u&c4)$k#`^5Q", + "%<auc|-%@#^72gOa!BcLl_-", + ";@{3;XMzp%%Z(_TQo{SP(u0)+V(>c$Ui^z<`A!+F8RhS5DX<Qkb`v1Ppx<w)pQ`Oe9w2z$", + "W;R3APa$u$7+V=T0vX6Bq+}~Oi`T<CxP?En5_AWCA+WOr6Ba&T8?i(2iuLXc?c0Eb0#8fx", + "0;z)Qdw{&iu1p_}NcXrEZj<UU?o^>aZomO|PHR8B%eNWlI^Y;%XH23*BQ9Pv<U}>!0dNOS", + "JW<Ev&&4jcJ|ItzHY*}vkh#@p?GI~Z@92UqgCTvtS*M+R6>;D6iM<TKxRt(MO)fK0B`f-", + "Tgh%d%!X%ul%RzqJ&<cgV@U9}77Km>}OHVD9S;=_E<kAo67wo+uzcCI2-", + ">GP=j^CP6mQ~WcEd%i!wnK!|`afKPHZ{nSFK{L!7$MM299!v3kGuq;oa;+;@@n1r@0<Jpc", + "%OKHC*qZY)V7YMh$GJ7uZJ*pknb#4Lndxc5t*p~=j{8%jTcs@J`sWW0;gyRp=j6{m!9jg8", + "yBY-uv0ppo?(LlG%MXywQB)0kk7!saH~{t8ok=ap8ckC*S;vkAK<RzcLxv+K_UDp%n=;eQ", + "$32d2IQTOvm&n1x4gxS+SH)m=#|D9u~O6QLYUFTM@z@V2^Z*27`WQlTcSk<fG)JHRfF0un", + "`wvtDD^{B6fp60>Kc%yl18x$nR#Kzi@<EMdmds2!rvB-", + "%G=Qs8_PTjcEFU+5%TqHsrny=`4g%T<w?<?*L7vocihTo#Uf$}3PtSt59Gp?gp!2>?+E(y", + "7o2rS3FClOOT`Amt}UyGfYZ7QWYqqCA(t;kD}FGa+vi>Zi)j}a2lrvx_};$<>-", + "h&pt_ePq>W5KHZPr_P0CVzjF(&G-", + "bL>Xi>@Jv$b)=hLcP>(ngUOdeI5{4_W6pSrkv%rnFAW~Xq0%BpP=^5An)E2@X9`)$o<{(F", + "VhGYMIW)@T&B7JZ06+RhK~>g?V;J{)+)M6xch^XnnI^Q0TC;zLSj(LMJ&sOV*Qj&~gGAP}", + "&ujCT8Fj?*_GQ!H)<ip+T(TLY1KvvUUC&|zS0?)a81xs}-pb-", + ">00D>!#~T0u=fLlPvBYQl0ssI200dcD" ], "Europe/London": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0`|pJ6!-O7KxBg5R*;$9DqzW!kQs3DZt(=0_!m1", - "4wvE`6N%Vj#u6PS_3S?~(2)&xn8}2}3Wr#kG8n2!x8>$E$lF&~Y#_H6bu6(BiwblJ>;-Fs", - "gA$Y$*?=X)n1pFkKn}F~`>=4)+LLQk?L*P!bhAm0;`N~z3QbUIyVrm%kOZ(n1JJsm0pyb8", - "!GV{d*C!9KXv;4v<seWRpo=ZZxGf)-5Qsn$3dw`uhF)+6#mgUoNF-Y2jN73pVhdTs*p0`Z", - "AbnT1puEtudB{Nul>D4Q>-k#+x(!V5L@w5M>v2V5<gcLskF+p`aGTSn{sY8^@MUc;2o{&V", - "R!$180N}BtfYKS)i9w=!<~&l?1Cv^PWs!&a9{s(35^yqGU$72DKX|IkRtDblB>a`B>t(|B", - "|Fqr4^-{S*%Ep~ojUtx_CRbSQ(uFwu2=KH)Q@EBs@ZqRXn4mU;B!68;;IQs3Ub=n&UU%*m", - "k&zwD36&JSwsN(%k&x?H+tN^6)23c`I0=5^N_R0~1>tsFZ`^`3z~rXSXT&qcwa#n!%+Z#P", - "PG}(D^_CCILXnF|GKwabBh*xFS?4rwGo2vtJUwzrbv_$5PO+`?$l{H-jGB@X%S!OAhw;D4", - "XFycN3!XqQ&EorJOD3>~^U%Luw!jF<;6_q-f-S|6<EHry?%{@fuyH`_+D%uTA@g0$5e!Yi", - "P1vQuevyS;jE(-R>{cQDfZ2(4Xf1MMLr1=SA=MwVf2%Pp%VP;jn)|5Tf!-DbUGn%I-r<KG", - "4jJ(Y#L-fJUpUb$yNfvhX*iqWZoG7T*WUfE6iQD9_^EWqExH`rc&jJ<o^E8-mM10WrZ_Vv", - "xx9nj<vMlEt*KfP*pyth!c_AKnrKtQTACX08#{pioAFnDq!53+h*hO^f*yrWjg0u2pUcgv", - "UlpEZ9G_dlhlW1J^h@gTt7{KPL2mRal;1juJ3Q8-!GXO#IPzT4ciJ-nB+nkphssM}Q7IAT", - "pM}AT%y(J!78F?>kYaH7?$$O!t)wwClAisr3eUoeB^~T=U*_P~Y2*KdnO87>B!19sV=xZ5", - "yApq26RxgqA|*tmsvtL#OhcF(C<0EGWHP)BF<g*iSWicU6k1<Ps?BQ$IWg-#s2uF-qXgJ_", - "!H_mZIMx*L%&a*_6;_trMCULk0ZYM<hfJlYBddHwRyYUDu3!C_lJZWTQ?c-R&@9054pj0k", - "kQ{Xi{A$&)&b#^G*}8w^qE5i<@aDxaJQs2E$W)AIqUXO{gQ;U8|FA%BD~sORzq44)AntUu", - "QHBO{{Pi<EpK!$x4(~7w)la!dN=M@L_j};6|5G&QfuO~2?Q7996z)78fqW<D#8tKNV(*qc", - "mfA>l?h)_*7!{LoJiv%RsOs!q->n+DcV%9~B@Rb<ISu%16c5H-7zQIq+SuS+s<lQOWK5+C", - "d*>C_1G_1g6`Yd~8|%-=2l~oGN!~TVv2Bnk>7wW8L@^?vX$f3AiT)(4nrCuTm9%(XC6Nai", - "E(;}7&=YZagjAN$O-cN;1u{dTkElmB0GT$|Wa)QMmKrx<|LCJ9qlUoFsUbD^H^6_8(w<0{", - "ftj&O1~p_%lh5z;zNV&sP<T$*OgK)_0B#JDtXOkhC;Bo7h)#RUy;vBiVLN-T$*7t*t9@ey", - "3Woa&24QZ_z38BQ@A(A<(9n@%R?}B`7%w2wowt~UU;bAlqCzr(H$M5t==jGIqMqCsE=Jwa", - "$3P+3^&|~i28@=d_u6Cgthe(Lq(wxKpdSDL|7X6Un<nrt00Gwuz#ISo`BbmvvBYQl0ssI2", - "00dcD" + "{Wp48S^xk9=GL@E0stWa761SMbT8$j;0{j(Ib8rM7KxBg5R*;%EAUJ=!kQs3DZt(=0_!m1", + "4wvE`6N%Vj#u6PS_3S?~(2)&xn8}2}3Wr#kG8n2!x8>$E$lF&~Y#_H6bu6(BiwblJ>;-", + "FsgA$Y$*?=X)n1pFkKn}F~`>=4)+LLQk?L*P!bhAm0;`N~z3QbUIyVrm%kOZ(n1JJsm0py", + "b8!GV{d*C!9KXv;4v<seWRpo=ZZxGf)-5Qsn$3dw`uhF)+6#mgUoNF-", + "Y2jN73pVhdTs*p0`ZAbnT1puEtudB{Nul>D4Q>-", + "k#+x(!V5L@w5M>v2V5<gcLskF+p`aGTSn{sY8^@MUc;2o{&VR!$180N}BtfYKS)i9w=!<~", + "&l?1Cv^PWs!&a9{s(35^yqGU$72DKX|IkRtDblB>a`B>t(|B|Fqr4^-", + "{S*%Ep~ojUtx_CRbSQ(uFwu2=KH)Q@EBs@ZqRXn4mU;B!68;;IQs3Ub=n&UU%*mk&zwD36", + "&JSwsN(%k&x?H+tN^6)23c`I0=5^N_R0~1>tsFZ`^`3z~rXSXT&qcwa#n#k^5sFT7HKtyC", + "9A0oluNf^P4yk-d_|mUd;T0;~y@2Cu@Xd;<hk`=GfF)t3<(W;^;#vS*2CIM0}lIP!ER~kM", + "z)8{sshyiGGP5>IRT#++Y*Z6*0%M4`z`3a2vnHc-", + "B(#xp*94c8t_y=lV~#Q=Whn&p<+Qzs`pL<^`L`BrF9ohSDa^SnosMt{zpFXpUjqumClb$D", + "^}s!&5JM`)L?H!Gzj6q@G0mVG?P#xu;u<zZ1><E)+3E=(_l2Q};6M&`<X7b>1DMoWN$14T", + "b>^F1?&ymdJ4TS`!aAO1#Myz0fU6<#?jJoc&X-", + "z&$D#Zj!Dg3w*<XVS=jFmPAR!(5h?6II3@of1!~K^$-", + "QX2G2lXMyPJhkXLlU>*{JNc(J;D0DrI1K1-", + "iv3~i7vFT?5yqP3rSI@jsz*k!0bE*<fCU<^XircA9v4*(~nJ^>~Pkn+g>u6Z;Se-", + ")c$&;tI=8t;#10nr-)VFCx@^bf}Ak2a$g+MN&Pv7{W+wBYjvrBy)wwx-", + "|SlQynn<W$^Ik*J>=O3ULjr$jM*QQAezq5|DqO6<QT_YD!R$)!*RZ}#4L0{;;YiF!ta_vA", + "o_bRQz(yXVh(Pb?c@M0_Nw`OAh0wRSk&G;NwrYbla>3Zf+jql$4rQ2m|lPqGyA<>cV6bIA", + "o~*{`bgCh=A>WU}#b$t&5MPj$~|esT>8<kEOj;^~h1CXH8G(W#9e08Kd-", + "*F5|4A*&rbM;en?xL6l&Z{Ykm8CiF^r3y(3DvVoFpAb+ntAMvjr$pycbsU+9;c4roZ<eo^", + "PLSCW&pf@n2Q`ntKP;qNmw&SoHqbu1VHpCiJ*}hcmd((^642U>v-", + "C$_n!^iqI5;Q?X=YtG#wS0iS@Z+K+>Pw@;#vN~2NVaNcNiR~iPI$HEp0CZblbg!9944qJf", + "e^U%kyd74NW@4bR+8mK^RfSa}|2+8EsMDCnO<b%-", + "jeynUWspF(vCb3E!h#H}EYTD5FrIeRGbKg14?|O~2$j2xm;!0<!;WzH+-", + "i5d5tFbg7<Iby5Fk8J(*7&9_w84$GKMDrVHz;kCbn5G~;w8;xL!HKZvT=em^K46pU{o{_|", + "j^aXaZYS0#rN7ZS$)7F+7L39lWib5ukEMe<bXdIuFkbGR!az9hn%Ce>SZ@D)kcJx`Cx~hD", + "N7PV9xa~1y#{E$Hy#KX4zE=~k$7%Vm?00000LN&_Vg>>9w00Gqs&>R2&ZK8hovBYQl0ssI", + "200dcD" ], "Pacific/Kiritimati": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~jCaVO;<!7KxBg5R*{K!`A|q%C5j6({{dSEy5>+", - "NF2>iK{8KMUf+)<-)VxXbLxD(alL}N$AT-ogNbJSMMYeX+Z{jS)b8TK^PB=FxyBxzfmFto", - "eo0R`a(%NO?#aEH9|?Cv00000NIsFh6BW2800DjO0RR918Pu^`vBYQl0ssI200dcD" + "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~itMU0nbw7KxBg5R*{Ky3@w34e-", + "emlEe%!1&HoVz@2)g91$Qvf%e=}%J+k5^yTOu{Q*OrV;`agAU{YYC1>Bq#Lo)Ct}OFqAaP", + "SYvN>0#bjna^XrQl~da<70IbZ?+00000VC(mK^UzXX00DX60RR91ZYU6cvBYQl0ssI200d", + "cD" ], "UTC": [ - "{Wp48S^xk9=GL@E0stWa761SMbT8$j-~e#|9bEt_7KxBg5R*|3h1|xhHLji!C57qW6L*|H", - "pEErm00000ygu;I+>V)?00B92fhY-(AGY&-0RR9100dcD" + "{Wp48S^xk9=GL@E0stWa761SMbT8$j-", + "~e#|9bEt_7KxBg5R*|3h1|xhHLji!C57qW6L*|HpEErm00000ygu;I+>V)?00B92fhY-", + "(AGY&-0RR9100dcD" ] }, "metadata": { - "version": "2020a" + "version": "2025b" } } \ No newline at end of file diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 44e87e71c8ee7b..7502b120825fbc 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -18,17 +18,18 @@ from functools import cached_property from test.support import MISSING_C_DOCSTRINGS -from test.support.os_helper import EnvironmentVarGuard +from test.support.os_helper import EnvironmentVarGuard, FakePath from test.test_zoneinfo import _support as test_support from test.test_zoneinfo._support import TZPATH_TEST_LOCK, ZoneInfoTestBase from test.support.import_helper import import_module, CleanImport +from test.support.script_helper import assert_python_ok lzma = import_module('lzma') py_zoneinfo, c_zoneinfo = test_support.get_modules() try: importlib.metadata.metadata("tzdata") - HAS_TZDATA_PKG = True + HAS_TZDATA_PKG = (importlib.metadata.version("tzdata") == "2025.3") except importlib.metadata.PackageNotFoundError: HAS_TZDATA_PKG = False @@ -251,6 +252,8 @@ def test_bad_zones(self): bad_zones = [ b"", # Empty file b"AAAA3" + b" " * 15, # Bad magic + # Truncated V2 file (should not loop indefinitely) + b"TZif2" + (b"\x00" * 39) + b"TZif2" + (b"\x00" * 39) + b"\n" + b"Part", ] for bad_zone in bad_zones: @@ -738,6 +741,38 @@ def test_empty_zone(self): with self.assertRaises(ValueError): self.klass.from_file(zf) + def test_invalid_transition_index(self): + STD = ZoneOffset("STD", ZERO) + DST = ZoneOffset("DST", ONE_H, ONE_H) + + zf = self.construct_zone([ + ZoneTransition(datetime(2026, 3, 1, 2), STD, DST), + ZoneTransition(datetime(2026, 11, 1, 2), DST, STD), + ], after="", version=1) + + data = bytearray(zf.read()) + timecnt = struct.unpack_from(">l", data, 32)[0] + idx_offset = 44 + timecnt * 4 + data[idx_offset + 1] = 2 # typecnt is 2, so index 2 is OOB + f = io.BytesIO(bytes(data)) + + with self.assertRaises(ValueError): + self.klass.from_file(f) + + def test_transition_lookahead_out_of_bounds(self): + STD = ZoneOffset("STD", ZERO) + DST = ZoneOffset("DST", ONE_H, ONE_H) + EXT = ZoneOffset("EXT", ONE_H) + + zf = self.construct_zone([ + ZoneTransition(datetime(2026, 3, 1), STD, DST), + ZoneTransition(datetime(2026, 6, 1), DST, EXT), + ZoneTransition(datetime(2026, 9, 1), EXT, DST), + ], after="") + + zi = self.klass.from_file(zf) + self.assertIsNotNone(zi) + def test_zone_very_large_timestamp(self): """Test when a transition is in the far past or future. @@ -1550,10 +1585,80 @@ def __eq__(self, other): except CustomError: pass + def test_weak_cache_descriptor_use_after_free(self): + class BombDescriptor: + def __get__(self, obj, owner): + return {} + + class EvilZoneInfo(self.klass): + pass + + # Must be set after the class creation. + EvilZoneInfo._weak_cache = BombDescriptor() + + key = "America/Los_Angeles" + zone1 = EvilZoneInfo(key) + self.assertEqual(str(zone1), key) + + EvilZoneInfo.clear_cache() + zone2 = EvilZoneInfo(key) + self.assertEqual(str(zone2), key) + self.assertIsNot(zone2, zone1) + class CZoneInfoCacheTest(ZoneInfoCacheTest): module = c_zoneinfo + def test_inconsistent_weak_cache_get(self): + class Cache: + def get(self, key, default=None): + return 1337 + + class ZI(self.klass): + pass + # Class attribute must be set after class creation + # to override zoneinfo.ZoneInfo.__init_subclass__. + ZI._weak_cache = Cache() + + with self.assertRaises(RuntimeError) as te: + ZI("America/Los_Angeles") + self.assertEqual( + str(te.exception), + "Unexpected instance of int in ZI weak cache for key 'America/Los_Angeles'" + ) + + def test_deleted_weak_cache(self): + class ZI(self.klass): + pass + delattr(ZI, '_weak_cache') + + # These should not segfault + with self.assertRaises(AttributeError): + ZI("UTC") + + with self.assertRaises(AttributeError): + ZI.clear_cache() + + def test_inconsistent_weak_cache_setdefault(self): + class Cache: + def get(self, key, default=None): + return default + def setdefault(self, key, value): + return 1337 + + class ZI(self.klass): + pass + # Class attribute must be set after class creation + # to override zoneinfo.ZoneInfo.__init_subclass__. + ZI._weak_cache = Cache() + + with self.assertRaises(RuntimeError) as te: + ZI("America/Los_Angeles") + self.assertEqual( + str(te.exception), + "Unexpected instance of int in ZI weak cache for key 'America/Los_Angeles'" + ) + class ZoneInfoPickleTest(TzPathUserMixin, ZoneInfoTestBase): module = py_zoneinfo @@ -1783,6 +1888,7 @@ def test_reset_tzpath_relative_paths(self): ("/usr/share/zoneinfo", "../relative/path",), ("path/to/somewhere", "../relative/path",), ("/usr/share/zoneinfo", "path/to/somewhere", "../relative/path",), + (FakePath("path/to/somewhere"),) ] for input_paths in bad_values: with self.subTest(input_paths=input_paths): @@ -1794,6 +1900,9 @@ def test_tzpath_type_error(self): "/etc/zoneinfo:/usr/share/zoneinfo", b"/etc/zoneinfo:/usr/share/zoneinfo", 0, + (b"/bytes/path", "/valid/path"), + (FakePath(b"/bytes/path"),), + (0,), ] for bad_value in bad_values: @@ -1804,6 +1913,7 @@ def test_tzpath_type_error(self): def test_tzpath_attribute(self): tzpath_0 = [f"{DRIVE}/one", f"{DRIVE}/two"] tzpath_1 = [f"{DRIVE}/three"] + tzpath_pathlike = (FakePath(f"{DRIVE}/usr/share/zoneinfo"),) with self.tzpath_context(tzpath_0): query_0 = self.module.TZPATH @@ -1811,8 +1921,12 @@ def test_tzpath_attribute(self): with self.tzpath_context(tzpath_1): query_1 = self.module.TZPATH + with self.tzpath_context(tzpath_pathlike): + query_pathlike = self.module.TZPATH + self.assertSequenceEqual(tzpath_0, query_0) self.assertSequenceEqual(tzpath_1, query_1) + self.assertSequenceEqual(tuple([os.fspath(p) for p in tzpath_pathlike]), query_pathlike) class CTzPathTest(TzPathTest): @@ -1941,11 +2055,46 @@ def test_exclude_posixrules(self): actual = self.module.available_timezones() self.assertEqual(actual, expected) + def test_exclude_localtime(self): + expected = { + "America/New_York", + "Europe/London", + } + + tree = list(expected) + ["localtime"] + + with tempfile.TemporaryDirectory() as td: + for key in tree: + self.touch_zone(key, td) + + with self.tzpath_context([td]): + actual = self.module.available_timezones() + self.assertEqual(actual, expected) class CTestModule(TestModule): module = c_zoneinfo +class MiscTests(unittest.TestCase): + def test_pydatetime(self): + # Test that zoneinfo works if the C implementation of datetime + # is not available and the Python implementation of datetime is used. + # The Python implementation of zoneinfo should be used in thet case. + # + # Run the test in a subprocess, as importing _zoneinfo with + # _datettime disabled causes crash in the previously imported + # _zoneinfo. + assert_python_ok('-c', '''if 1: + import sys + sys.modules['_datetime'] = None + import datetime + import zoneinfo + tzinfo = zoneinfo.ZoneInfo('Europe/London') + datetime.datetime(2025, 10, 26, 2, 0, tzinfo=tzinfo) + ''', + PYTHONTZPATH=str(ZONEINFO_DATA.tzpath)) + + class ExtensionBuiltTest(unittest.TestCase): """Smoke test to ensure that the C and Python extensions are both tested. @@ -2162,8 +2311,8 @@ def _America_Los_Angeles(): ] def _America_Santiago(): - LMT = ZoneOffset("LMT", timedelta(seconds=-16966), ZERO) - SMT = ZoneOffset("SMT", timedelta(seconds=-16966), ZERO) + LMT = ZoneOffset("LMT", timedelta(seconds=-16965), ZERO) + SMT = ZoneOffset("SMT", timedelta(seconds=-16965), ZERO) N05 = ZoneOffset("-05", timedelta(seconds=-18000), ZERO) N04 = ZoneOffset("-04", timedelta(seconds=-14400), ZERO) N03 = ZoneOffset("-03", timedelta(seconds=-10800), ONE_H) @@ -2198,8 +2347,8 @@ def _Australia_Sydney(): return [ ZoneTransition(datetime(1895, 2, 1), LMT, AEST), - ZoneTransition(datetime(1917, 1, 1, 0, 1), AEST, AEDT), - ZoneTransition(datetime(1917, 3, 25, 2), AEDT, AEST), + ZoneTransition(datetime(1917, 1, 1, 2), AEST, AEDT), + ZoneTransition(datetime(1917, 3, 25, 3), AEDT, AEST), ZoneTransition(datetime(2012, 4, 1, 3), AEDT, AEST), ZoneTransition(datetime(2012, 10, 7, 2), AEST, AEDT), ZoneTransition(datetime(2040, 4, 1, 3), AEDT, AEST), @@ -2207,7 +2356,7 @@ def _Australia_Sydney(): ] def _Europe_Dublin(): - LMT = ZoneOffset("LMT", timedelta(seconds=-1500), ZERO) + LMT = ZoneOffset("LMT", timedelta(seconds=-1521), ZERO) DMT = ZoneOffset("DMT", timedelta(seconds=-1521), ZERO) IST_0 = ZoneOffset("IST", timedelta(seconds=2079), ONE_H) GMT_0 = ZoneOffset("GMT", ZERO, ZERO) diff --git a/Lib/test/xpickle_worker.py b/Lib/test/xpickle_worker.py new file mode 100644 index 00000000000000..1b49515123c6ab --- /dev/null +++ b/Lib/test/xpickle_worker.py @@ -0,0 +1,62 @@ +# This script is called by test_xpickle as a subprocess to load and dump +# pickles in a different Python version. +import os +import pickle +import struct +import sys + + +# This allows the xpickle worker to import picklecommon.py, which it needs +# since some of the pickle objects hold references to picklecommon.py. +test_mod_path = os.path.abspath(os.path.join(os.path.dirname(__file__), + 'picklecommon.py')) +if sys.version_info >= (3, 5): + import importlib.util + spec = importlib.util.spec_from_file_location('test.picklecommon', test_mod_path) + sys.modules['test'] = type(sys)('test') + test_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(test_module) + sys.modules['test.picklecommon'] = test_module +else: + test_module = type(sys)('test.picklecommon') + sys.modules['test.picklecommon'] = test_module + sys.modules['test'] = type(sys)('test') + with open(test_mod_path, 'rb') as f: + sources = f.read() + exec(sources, vars(test_module)) + +def read_exact(f, n): + buf = b'' + while len(buf) < n: + chunk = f.read(n - len(buf)) + if not chunk: + raise EOFError + buf += chunk + return buf + +in_stream = getattr(sys.stdin, 'buffer', sys.stdin) +out_stream = getattr(sys.stdout, 'buffer', sys.stdout) + +try: + while True: + size, = struct.unpack('!i', read_exact(in_stream, 4)) + if not size: + break + data = read_exact(in_stream, size) + protocol, = struct.unpack('!i', data[:4]) + obj = pickle.loads(data[4:]) + data = pickle.dumps(obj, protocol) + out_stream.write(struct.pack('!i', len(data)) + data) + out_stream.flush() +except Exception as exc: + # dump the exception to stdout and write to stderr, then exit + try: + data = pickle.dumps(exc) + out_stream.write(struct.pack('!i', -len(data)) + data) + out_stream.flush() + except Exception: + out_stream.write(struct.pack('!i', 0)) + out_stream.flush() + sys.stderr.write(repr(exc)) + sys.stderr.flush() + sys.exit(1) diff --git a/Lib/textwrap.py b/Lib/textwrap.py index 5ae439f5cd3b78..41366fbf443a4f 100644 --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -211,7 +211,7 @@ def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): # If we're allowed to break long words, then do so: put as much # of the next chunk onto the current line as will fit. - if self.break_long_words: + if self.break_long_words and space_left > 0: end = space_left chunk = reversed_chunks[-1] if self.break_on_hyphens and len(chunk) > space_left: diff --git a/Lib/threading.py b/Lib/threading.py index b6c451d1fbaabd..abac31e25886fa 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -29,6 +29,7 @@ 'Barrier', 'BrokenBarrierError', 'Timer', 'ThreadError', 'setprofile', 'settrace', 'local', 'stack_size', 'excepthook', 'ExceptHookArgs', 'gettrace', 'getprofile', + 'serialize_iterator', 'synchronized_iterator', 'concurrent_tee', 'setprofile_all_threads','settrace_all_threads'] # Rename some stuff so "from threading import *" is safe @@ -653,7 +654,8 @@ def wait(self, timeout=None): (or fractions thereof). This method returns the internal flag on exit, so it will always return - True except if a timeout is given and the operation times out. + ``True`` except if a timeout is given and the operation times out, when + it will return ``False``. """ with self._cond: @@ -841,6 +843,148 @@ class BrokenBarrierError(RuntimeError): pass +## Synchronization tools for iterators ##################### + +class serialize_iterator: + """Wrap a non-concurrent iterator with a lock to enforce sequential access. + + Applies a non-reentrant lock around calls to __next__. If the + wrapped iterator also defines send(), throw(), or close(), those + calls are serialized as well. + + Allows iterator and generator instances to be shared by multiple consumer + threads. + + For example, itertools.count does not make thread-safe instances, + but that is easily fixed with: + + atomic_counter = serialize_iterator(itertools.count()) + + """ + + __slots__ = ('_iterator', '_lock') + + def __init__(self, iterable): + self._iterator = iter(iterable) + self._lock = Lock() + + def __iter__(self): + return self + + def __next__(self): + with self._lock: + return next(self._iterator) + + def send(self, value, /): + """Send a value to a generator. + + Raises AttributeError if not a generator. + """ + with self._lock: + return self._iterator.send(value) + + def throw(self, *args): + """Call throw() on a generator. + + Raises AttributeError if not a generator. + """ + with self._lock: + return self._iterator.throw(*args) + + def close(self): + """Call close() on a generator. + + Raises AttributeError if not a generator. + """ + with self._lock: + return self._iterator.close() + + +def synchronized_iterator(func): + """Wrap an iterator-returning callable to make its iterators thread-safe. + + Existing itertools and more-itertools can be wrapped so that their + iterator instances are serialized. + + For example, itertools.count does not make thread-safe instances, + but that is easily fixed with: + + atomic_counter = synchronized_iterator(itertools.count) + + Can also be used as a decorator for generator function definitions + so that the generator instances are serialized:: + + import time + + @synchronized_iterator + def enumerate_and_timestamp(iterable): + for count, value in enumerate(iterable): + yield count, time.time_ns(), value + + """ + + from functools import wraps + + @wraps(func) + def inner(*args, **kwargs): + iterator = func(*args, **kwargs) + return serialize_iterator(iterator) + + return inner + + +def concurrent_tee(iterable, n=2): + """Variant of itertools.tee() but with guaranteed threading semantics. + + Takes a non-threadsafe iterator as an input and creates concurrent + tee objects for other threads to have reliable independent copies of + the data stream. + + The new iterators are only thread-safe if consumed within a single thread. + To share just one of the new iterators across multiple threads, wrap it + with threading.serialize_iterator(). + """ + + if n < 0: + raise ValueError("n must be a non-negative integer") + if n == 0: + return () + iterator = _concurrent_tee(iterable) + result = [iterator] + for _ in range(n - 1): + result.append(_concurrent_tee(iterator)) + return tuple(result) + + +class _concurrent_tee: + __slots__ = ('iterator', 'link', 'lock') + + def __init__(self, iterable): + if isinstance(iterable, _concurrent_tee): + self.iterator = iterable.iterator + self.link = iterable.link + self.lock = iterable.lock + else: + self.iterator = iter(iterable) + self.link = [None, None] + self.lock = Lock() + + def __iter__(self): + return self + + def __next__(self): + link = self.link + if link[1] is None: + with self.lock: + if link[1] is None: + link[0] = next(self.iterator) + link[1] = [None, None] + value, self.link = link + return value + +############################################################ + + # Helper to generate new thread names _counter = _count(1).__next__ def _newname(name_template): @@ -1414,7 +1558,7 @@ def __init__(self, dummy_thread): # the related _DummyThread will be kept forever! _thread_local_info._track_dummy_thread_ref = self - def __del__(self): + def __del__(self, _active_limbo_lock=_active_limbo_lock, _active=_active): with _active_limbo_lock: if _active.get(self._tident) is self._dummy_thread: _active.pop(self._tident, None) @@ -1557,8 +1701,9 @@ def _shutdown(): # normally - that won't happen until the interpreter is nearly dead. So # mark it done here. if _main_thread._os_thread_handle.is_done() and _is_main_interpreter(): - # _shutdown() was already called - return + # _shutdown() was already called, but threads might have started + # in the meantime. + return _thread_shutdown() global _SHUTTING_DOWN _SHUTTING_DOWN = True diff --git a/Lib/timeit.py b/Lib/timeit.py index e767f0187826df..01bdfd901e30d5 100644 --- a/Lib/timeit.py +++ b/Lib/timeit.py @@ -6,35 +6,6 @@ Library usage: see the Timer class. -Command line usage: - python timeit.py [-n N] [-r N] [-s S] [-p] [-h] [--] [statement] - -Options: - -n/--number N: how many times to execute 'statement' (default: see below) - -r/--repeat N: how many times to repeat the timer (default 5) - -s/--setup S: statement to be executed once initially (default 'pass'). - Execution time of this setup statement is NOT timed. - -p/--process: use time.process_time() (default is time.perf_counter()) - -v/--verbose: print raw timing results; repeat for more digits precision - -u/--unit: set the output time unit (nsec, usec, msec, or sec) - -h/--help: print this usage message and exit - --: separate options from statement, use when statement starts with - - statement: statement to be timed (default 'pass') - -A multi-line statement may be given by specifying each line as a -separate argument; indented lines are possible by enclosing an -argument in quotes and using leading spaces. Multiple -s options are -treated similarly. - -If -n is not given, a suitable number of loops is calculated by trying -increasing numbers from the sequence 1, 2, 5, 10, 20, 50, ... until the -total time is at least 0.2 seconds. - -Note: there is a certain baseline overhead associated with executing a -pass statement. It differs between versions. The code here doesn't try -to hide it, but you should be aware of it. The baseline overhead can be -measured by invoking the program without arguments. - Classes: Timer @@ -57,6 +28,7 @@ default_number = 1000000 default_repeat = 5 default_timer = time.perf_counter +default_target_time = 0.2 _globals = globals @@ -133,7 +105,7 @@ def __init__(self, stmt="pass", setup="pass", timer=default_timer, exec(code, global_ns, local_ns) self.inner = local_ns["inner"] - def print_exc(self, file=None): + def print_exc(self, file=None, **kwargs): """Helper to print a traceback from the timed code. Typical use: @@ -149,6 +121,11 @@ def print_exc(self, file=None): The optional file argument directs where the traceback is sent; it defaults to sys.stderr. + + The optional colorize keyword argument controls whether the + traceback is colorized; it defaults to False for programmatic + usage. When used from the command line, this is automatically + set based on terminal capabilities. """ import linecache, traceback if self.src is not None: @@ -158,7 +135,8 @@ def print_exc(self, file=None): dummy_src_name) # else the source is already stored somewhere else - traceback.print_exc(file=file) + kwargs['colorize'] = kwargs.get('colorize', False) + traceback.print_exc(file=file, **kwargs) def timeit(self, number=default_number): """Time 'number' executions of the main statement. @@ -206,12 +184,13 @@ def repeat(self, repeat=default_repeat, number=default_number): r.append(t) return r - def autorange(self, callback=None): - """Return the number of loops and time taken so that total time >= 0.2. + def autorange(self, callback=None, target_time=default_target_time): + """Return the number of loops and time taken so that + total time >= target_time (default is 0.2 seconds). Calls the timeit method with increasing numbers from the sequence - 1, 2, 5, 10, 20, 50, ... until the time taken is at least 0.2 - second. Returns (number, time_taken). + 1, 2, 5, 10, 20, 50, ... until the target time is reached. + Returns (number, time_taken). If *callback* is given and is not None, it will be called after each trial with two arguments: ``callback(number, time_taken)``. @@ -223,7 +202,7 @@ def autorange(self, callback=None): time_taken = self.timeit(number) if callback: callback(number, time_taken) - if time_taken >= 0.2: + if time_taken >= target_time: return (number, time_taken) i *= 10 @@ -257,53 +236,114 @@ def main(args=None, *, _wrap_timer=None): is not None, it must be a callable that accepts a timer function and returns another timer function (used for unit testing). """ + import argparse if args is None: args = sys.argv[1:] - import getopt + import _colorize + colorize = _colorize.can_colorize() + theme = _colorize.get_theme(force_color=colorize).timeit + reset = theme.reset + + epilog = """\ +A multi-line statement may be given by specifying each line as a +separate argument; indented lines are possible by enclosing an +argument in quotes and using leading spaces. Multiple `-s` options are +treated similarly. + +If `-n` is not given, a suitable number of loops is calculated by trying +increasing numbers from the sequence 1, 2, 5, 10, 20, 50, ... until the +total time is at least `--target-time` seconds. + +Note: there is a certain baseline overhead associated with executing a +pass statement. It differs between versions. The code here doesn't try +to hide it, but you should be aware of it. The baseline overhead can be +measured by invoking the program without arguments.""" + + parser = argparse.ArgumentParser( + prog="python -m timeit", + description="""\ +Tool for measuring execution time of small code snippets. + +This module avoids a number of common traps for measuring execution +times. See also Tim Peters' introduction to the Algorithms chapter in +the Python Cookbook, published by O'Reilly. + +Library usage: see the `Timer` class.""", + epilog=epilog, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + "-n", + "--number", + type=int, + default=0, + help="how many times to execute 'statement' (default: see below)", + ) + parser.add_argument( + "-r", + "--repeat", + type=int, + default=default_repeat, + help="how many times to repeat the timer (default %(default)s)", + ) + parser.add_argument( + "-s", + "--setup", + action="append", + default=[], + help="statement to be executed once initially. " + "Execution time of this setup statement is NOT timed. " + "(default 'pass')", + ) + parser.add_argument( + "-p", + "--process", + action="store_true", + help="use `time.process_time()` (default is `time.perf_counter()`)", + ) + parser.add_argument( + "-t", + "--target-time", + type=float, + default=default_target_time, + help="if `--number` is 0 the code will run until it takes " + "at least this many seconds (default %(default)s)", + ) + parser.add_argument( + "-v", + "--verbose", + action="count", + default=0, + help="print raw timing results; repeat for more digits precision", + ) + parser.add_argument( + "-u", + "--unit", + default=None, + choices=["nsec", "usec", "msec", "sec"], + help="set the output time unit", + ) + parser.add_argument( + "statement", + nargs="*", + default=["pass"], + help="statement to be timed (default 'pass')", + ) try: - opts, args = getopt.getopt(args, "n:u:s:r:pvh", - ["number=", "setup=", "repeat=", - "process", "verbose", "unit=", "help"]) - except getopt.error as err: - print(err) - print("use -h/--help for command line help") - return 2 - - timer = default_timer - stmt = "\n".join(args) or "pass" - number = 0 # auto-determine - setup = [] - repeat = default_repeat - verbose = 0 - time_unit = None + ns = parser.parse_args(args) + except SystemExit as e: + return e.code + + timer = time.process_time if ns.process else default_timer + stmt = "\n".join(ns.statement) or "pass" + number = ns.number + target_time = ns.target_time + setup = "\n".join(ns.setup) or "pass" + repeat = max(ns.repeat, 1) + verbose = ns.verbose + time_unit = ns.unit units = {"nsec": 1e-9, "usec": 1e-6, "msec": 1e-3, "sec": 1.0} - precision = 3 - for o, a in opts: - if o in ("-n", "--number"): - number = int(a) - if o in ("-s", "--setup"): - setup.append(a) - if o in ("-u", "--unit"): - if a in units: - time_unit = a - else: - print("Unrecognized unit. Please select nsec, usec, msec, or sec.", - file=sys.stderr) - return 2 - if o in ("-r", "--repeat"): - repeat = int(a) - if repeat <= 0: - repeat = 1 - if o in ("-p", "--process"): - timer = time.process_time - if o in ("-v", "--verbose"): - if verbose: - precision += 1 - verbose += 1 - if o in ("-h", "--help"): - print(__doc__, end="") - return 0 - setup = "\n".join(setup) or "pass" + precision = 3 + max(verbose - 1, 0) # Include the current directory, so that local imports work (sys.path # contains the directory of this script, rather than the current @@ -315,18 +355,21 @@ def main(args=None, *, _wrap_timer=None): t = Timer(stmt, setup, timer) if number == 0: - # determine number so that 0.2 <= total time < 2.0 + # determine number so that total time >= target_time callback = None if verbose: def callback(number, time_taken): - msg = "{num} loop{s} -> {secs:.{prec}g} secs" - plural = (number != 1) - print(msg.format(num=number, s='s' if plural else '', - secs=time_taken, prec=precision)) + s = "" if number == 1 else "s" + print( + f"{number} loop{s} " + f"{theme.punctuation}-> " + f"{theme.timing}{time_taken:.{precision}g} sec{reset}" + ) + try: - number, _ = t.autorange(callback) + number, _ = t.autorange(callback, target_time) except: - t.print_exc() + t.print_exc(colorize=colorize) return 1 if verbose: @@ -335,7 +378,7 @@ def callback(number, time_taken): try: raw_timings = t.repeat(repeat, number) except: - t.print_exc() + t.print_exc(colorize=colorize) return 1 def format_time(dt): @@ -353,24 +396,34 @@ def format_time(dt): return "%.*g %s" % (precision, dt / scale, unit) if verbose: - print("raw times: %s" % ", ".join(map(format_time, raw_timings))) + raw = f"{theme.punctuation}, ".join( + f"{theme.timing}{t}" for t in map(format_time, raw_timings) + ) + print(f"raw times: {raw}{reset}") print() timings = [dt / number for dt in raw_timings] - best = min(timings) - print("%d loop%s, best of %d: %s per loop" - % (number, 's' if number != 1 else '', - repeat, format_time(best))) - best = min(timings) worst = max(timings) + s = "" if number == 1 else "s" + print( + f"{number} loop{s}, best of {repeat}: " + f"{theme.best}{format_time(best)}{reset} " + f"{theme.per_loop}per loop{reset}" + ) + if worst >= best * 4: import warnings - warnings.warn_explicit("The test results are likely unreliable. " - "The worst time (%s) was more than four times " - "slower than the best time (%s)." - % (format_time(worst), format_time(best)), - UserWarning, '', 0) + + print(file=sys.stderr) + warnings.warn_explicit( + f"{theme.warning}The test results are likely unreliable. " + f"The {theme.warning_worst}worst time ({format_time(worst)})" + f"{theme.warning} was more than four times slower than the " + f"{theme.warning_best}best time ({format_time(best)})" + f"{theme.warning}.{reset}", + UserWarning, "", 0, + ) return None diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index a693b04870b995..ba8365f56c37a7 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -7,25 +7,25 @@ LabelFrame and PanedWindow. Properties of the widgets are specified with keyword arguments. -Keyword arguments have the same name as the corresponding resource +Keyword arguments have the same name as the corresponding options under Tk. Widgets are positioned with one of the geometry managers Place, Pack or Grid. These managers can be called with methods place, pack, grid available in every Widget. -Actions are bound to events by resources (e.g. keyword argument -command) or with the method bind. +Actions are bound to events by options (e.g. the command +keyword argument) or with the bind() method. Example (Hello, World): import tkinter from tkinter.constants import * tk = tkinter.Tk() frame = tkinter.Frame(tk, relief=RIDGE, borderwidth=2) -frame.pack(fill=BOTH,expand=1) +frame.pack(fill=BOTH, expand=1) label = tkinter.Label(frame, text="Hello, World") label.pack(fill=X, expand=1) -button = tkinter.Button(frame,text="Exit",command=tk.destroy) +button = tkinter.Button(frame, text="Exit", command=tk.destroy) button.pack(side=BOTTOM) tk.mainloop() """ @@ -255,6 +255,10 @@ class Event: type - type of the event as a number widget - widget in which the event occurred delta - delta of wheel movement (MouseWheel) + detail - certain fixed strings (see Tcl/Tk documentation) + (Enter, Leave, FocusIn, FocusOut, ConfigureRequest) + user_data - data string which was passed to event_generate() or empty + string (VirtualEvent) """ def __repr__(self): @@ -847,7 +851,7 @@ def tk_focusNext(self): The focus order first goes to the next child, then to the children of the child recursively and then to the next sibling which is higher in the stacking order. A - widget is omitted if it has the takefocus resource set + widget is omitted if it has the takefocus option set to 0.""" name = self.tk.call('tk_focusNext', self._w) if not name: return None @@ -1538,7 +1542,7 @@ def bind(self, sequence=None, func=None, add=None): <Alt-A> for pressing A and the Alt key (KeyPress can be omitted). An event pattern can also be a virtual event of the form <<AString>> where AString can be arbitrary. This - event can be generated by event_generate. + event can be generated by event_generate(). If events are concatenated they must appear shortly after each other. @@ -1723,7 +1727,7 @@ def _root(self): w = self while w.master is not None: w = w.master return w - _subst_format = ('%#', '%b', '%f', '%h', '%k', + _subst_format = ('%#', '%b', '%d', '%f', '%h', '%k', '%s', '%t', '%w', '%x', '%y', '%A', '%E', '%K', '%N', '%W', '%T', '%X', '%Y', '%D') _subst_format_str = " ".join(_subst_format) @@ -1744,11 +1748,14 @@ def getint_event(s): if any(isinstance(s, tuple) for s in args): args = [s[0] if isinstance(s, tuple) and len(s) == 1 else s for s in args] - nsign, b, f, h, k, s, t, w, x, y, A, E, K, N, W, T, X, Y, D = args - # Missing: (a, c, d, m, o, v, B, R) + nsign, b, d, f, h, k, s, t, w, x, y, A, E, K, N, W, T, X, Y, D = args + # Missing: (a, c, m, o, v, B, R) e = Event() # serial field: valid for all events # number of button: ButtonPress and ButtonRelease events only + # detail: for Enter, Leave, FocusIn, FocusOut and ConfigureRequest + # events certain fixed strings (see Tcl/Tk documentation) + # user_data: data string from a virtual event or an empty string # height field: Configure, ConfigureRequest, Create, # ResizeRequest, and Expose events only # keycode field: KeyPress and KeyRelease events only @@ -1762,6 +1769,12 @@ def getint_event(s): # KeyRelease, and Motion events e.serial = getint(nsign) e.num = getint_event(b) + if T == EventType.VirtualEvent: + e.user_data = d + e.detail = '??' + else: + e.user_data = '??' + e.detail = d try: e.focus = getboolean(f) except TclError: pass e.height = getint_event(h) @@ -1827,27 +1840,34 @@ def _configure(self, cmd, cnf, kw): # These used to be defined in Widget: def configure(self, cnf=None, **kw): - """Configure resources of a widget. + """Query or modify the configuration options of the widget. + + If no arguments are specified, return a dictionary describing + all of the available options for the widget. + + If an option name is specified, then return a tuple describing + the one named option. - The values for resources are specified as keyword - arguments. To get an overview about - the allowed keyword arguments call the method keys. + If one or more keyword arguments are specified or a dictionary + is specified, then modify the widget option(s) to have the given + value(s). """ return self._configure('configure', cnf, kw) config = configure def cget(self, key): - """Return the resource value for a KEY given as string.""" + """Return the current value of the configuration option.""" return self.tk.call(self._w, 'cget', '-' + key) __getitem__ = cget + __iter__ = None # prevent using __getitem__ for iteration def __setitem__(self, key, value): self.configure({key: value}) def keys(self): - """Return a list of all resource names of this widget.""" + """Return a list of all option names of this widget.""" splitlist = self.tk.splitlist return [splitlist(x)[0][1:] for x in splitlist(self.tk.call(self._w, 'configure'))] @@ -1860,15 +1880,15 @@ def __repr__(self): return '<%s.%s object %s>' % ( self.__class__.__module__, self.__class__.__qualname__, self._w) - # Pack methods that apply to the master + # Pack methods that apply to the container widget _noarg_ = ['_noarg_'] def pack_propagate(self, flag=_noarg_): """Set or get the status for propagation of geometry information. - A boolean argument specifies whether the geometry information - of the slaves will determine the size of this widget. If no argument - is given the current setting will be returned. + A boolean argument specifies whether the size of this container will + be determined by the geometry information of its content. + If no argument is given the current setting will be returned. """ if flag is Misc._noarg_: return self._getboolean(self.tk.call( @@ -1878,29 +1898,51 @@ def pack_propagate(self, flag=_noarg_): propagate = pack_propagate + def pack_content(self): + """Returns a list of all of the content widgets in the packing order + for this container.""" + try: + res = self.tk.call('pack', 'content', self._w) + except TclError: + if self.info_patchlevel() >= (8, 6): + raise + res = self.tk.call('pack', 'slaves', self._w) + return [self._nametowidget(x) for x in self.tk.splitlist(res)] + + content = pack_content + def pack_slaves(self): - """Return a list of all slaves of this widget - in its packing order.""" + """Synonym for pack_content().""" return [self._nametowidget(x) for x in self.tk.splitlist( self.tk.call('pack', 'slaves', self._w))] slaves = pack_slaves - # Place method that applies to the master + # Place method that applies to the container widget + def place_content(self): + """Returns a list of all the content widgets for which this widget is + the container.""" + try: + res = self.tk.call('place', 'content', self._w) + except TclError: + if self.info_patchlevel() >= (8, 6): + raise + res = self.tk.call('place', 'slaves', self._w) + return [self._nametowidget(x) for x in self.tk.splitlist(res)] + def place_slaves(self): - """Return a list of all slaves of this widget - in its packing order.""" + """Synonym for place_content().""" return [self._nametowidget(x) for x in self.tk.splitlist( self.tk.call( 'place', 'slaves', self._w))] - # Grid methods that apply to the master + # Grid methods that apply to the container widget def grid_anchor(self, anchor=None): # new in Tk 8.5 """The anchor value controls how to place the grid within the - master when no row/column has any weight. + container widget when no row/column has any weight. The default anchor is nw.""" self.tk.call('grid', 'anchor', self._w, anchor) @@ -1917,7 +1959,7 @@ def grid_bbox(self, column=None, row=None, col2=None, row2=None): starts at that cell. The returned integers specify the offset of the upper left - corner in the master widget and the width and height. + corner in the container widget and the width and height. """ args = ('grid', 'bbox', self._w) if column is not None and row is not None: @@ -1966,7 +2008,7 @@ def _grid_configure(self, command, index, cnf, kw): def grid_columnconfigure(self, index, cnf={}, **kw): """Configure column INDEX of a grid. - Valid resources are minsize (minimum size of the column), + Valid options are minsize (minimum size of the column), weight (how much does additional space propagate to this column) and pad (how much space to let additionally).""" return self._grid_configure('columnconfigure', index, cnf, kw) @@ -1975,7 +2017,7 @@ def grid_columnconfigure(self, index, cnf={}, **kw): def grid_location(self, x, y): """Return a tuple of column and row which identify the cell - at which the pixel at position X and Y inside the master + at which the pixel at position X and Y inside the container widget is located.""" return self._getints( self.tk.call( @@ -1984,9 +2026,9 @@ def grid_location(self, x, y): def grid_propagate(self, flag=_noarg_): """Set or get the status for propagation of geometry information. - A boolean argument specifies whether the geometry information - of the slaves will determine the size of this widget. If no argument - is given, the current setting will be returned. + A boolean argument specifies whether the size of this container will + be determined by the geometry information of its content. + If no argument is given the current setting will be returned. """ if flag is Misc._noarg_: return self._getboolean(self.tk.call( @@ -1997,7 +2039,7 @@ def grid_propagate(self, flag=_noarg_): def grid_rowconfigure(self, index, cnf={}, **kw): """Configure row INDEX of a grid. - Valid resources are minsize (minimum size of the row), + Valid options are minsize (minimum size of the row), weight (how much does additional space propagate to this row) and pad (how much space to let additionally).""" return self._grid_configure('rowconfigure', index, cnf, kw) @@ -2011,9 +2053,29 @@ def grid_size(self): size = grid_size + def grid_content(self, row=None, column=None): + """Returns a list of the content widgets. + + If no arguments are supplied, a list of all of the content in this + container widget is returned, most recently managed first. + If ROW or COLUMN is specified, only the content in the row or + column is returned. + """ + args = () + if row is not None: + args = args + ('-row', row) + if column is not None: + args = args + ('-column', column) + try: + res = self.tk.call('grid', 'content', self._w, *args) + except TclError: + if self.info_patchlevel() >= (8, 6): + raise + res = self.tk.call('grid', 'slaves', self._w, *args) + return [self._nametowidget(x) for x in self.tk.splitlist(res)] + def grid_slaves(self, row=None, column=None): - """Return a list of all slaves of this widget - in its packing order.""" + """Synonym for grid_content().""" args = () if row is not None: args = args + ('-row', row) @@ -2599,8 +2661,8 @@ def pack_configure(self, cnf={}, **kw): before=widget - pack it before you will pack widget expand=bool - expand widget if parent size grows fill=NONE or X or Y or BOTH - fill widget if widget grows - in=master - use master to contain this widget - in_=master - see 'in' option description + in=container - use the container widget to contain this widget + in_=container - see 'in' option description ipadx=amount - add internal padding in x direction ipady=amount - add internal padding in y direction padx=amount - add padding in x direction @@ -2629,6 +2691,7 @@ def pack_info(self): info = pack_info propagate = pack_propagate = Misc.pack_propagate + content = pack_content = Misc.pack_content slaves = pack_slaves = Misc.pack_slaves @@ -2639,25 +2702,31 @@ class Place: def place_configure(self, cnf={}, **kw): """Place a widget in the parent widget. Use as options: - in=master - master relative to which the widget is placed - in_=master - see 'in' option description - x=amount - locate anchor of this widget at position x of master - y=amount - locate anchor of this widget at position y of master + in=container - the container widget relative to which this widget is + placed + in_=container - see 'in' option description + x=amount - locate anchor of this widget at position x of the + container widget + y=amount - locate anchor of this widget at position y of the + container widget relx=amount - locate anchor of this widget between 0.0 and 1.0 - relative to width of master (1.0 is right edge) + relative to width of the container widget (1.0 is + right edge) rely=amount - locate anchor of this widget between 0.0 and 1.0 - relative to height of master (1.0 is bottom edge) - anchor=NSEW (or subset) - position anchor according to given direction + relative to height of the container widget (1.0 is + bottom edge) + anchor=NSEW (or subset) - position anchor according to given + direction width=amount - width of this widget in pixel height=amount - height of this widget in pixel relwidth=amount - width of this widget between 0.0 and 1.0 - relative to width of master (1.0 is the same width - as the master) + relative to width of the container widget (1.0 is + the same width as the container widget) relheight=amount - height of this widget between 0.0 and 1.0 - relative to height of master (1.0 is the same - height as the master) + relative to height of the container widget (1.0 + is the same height as the container widget) bordermode="inside" or "outside" - whether to take border width of - master widget into account + the container widget into account """ self.tk.call( ('place', 'configure', self._w) @@ -2680,6 +2749,7 @@ def place_info(self): return d info = place_info + content = place_content = Misc.place_content slaves = place_slaves = Misc.place_slaves @@ -2693,8 +2763,8 @@ def grid_configure(self, cnf={}, **kw): """Position a widget in the parent widget in a grid. Use as options: column=number - use cell identified with given column (starting with 0) columnspan=number - this widget will span several columns - in=master - use master to contain this widget - in_=master - see 'in' option description + in=container - use the container widget to contain this widget + in_=container - see 'in' option description ipadx=amount - add internal padding in x direction ipady=amount - add internal padding in y direction padx=amount - add padding in x direction @@ -2735,6 +2805,7 @@ def grid_info(self): propagate = grid_propagate = Misc.grid_propagate rowconfigure = grid_rowconfigure = Misc.grid_rowconfigure size = grid_size = Misc.grid_size + content = grid_content = Misc.grid_content slaves = grid_slaves = Misc.grid_slaves @@ -2817,7 +2888,7 @@ class Toplevel(BaseWidget, Wm): def __init__(self, master=None, cnf={}, **kw): """Construct a toplevel widget with the parent MASTER. - Valid resource names: background, bd, bg, borderwidth, class, + Valid option names: background, bd, bg, borderwidth, class, colormap, container, cursor, height, highlightbackground, highlightcolor, highlightthickness, menu, relief, screen, takefocus, use, visual, width.""" @@ -2894,7 +2965,7 @@ class Canvas(Widget, XView, YView): def __init__(self, master=None, cnf={}, **kw): """Construct a canvas widget with the parent MASTER. - Valid resource names: background, bd, bg, borderwidth, closeenough, + Valid option names: background, bd, bg, borderwidth, closeenough, confine, cursor, height, highlightbackground, highlightcolor, highlightthickness, insertbackground, insertborderwidth, insertofftime, insertontime, insertwidth, offset, relief, @@ -3103,16 +3174,14 @@ def insert(self, *args): self.tk.call((self._w, 'insert') + args) def itemcget(self, tagOrId, option): - """Return the resource value for an OPTION for item TAGORID.""" + """Return the value of OPTION for item TAGORID.""" return self.tk.call( (self._w, 'itemcget') + (tagOrId, '-'+option)) def itemconfigure(self, tagOrId, cnf=None, **kw): - """Configure resources of an item TAGORID. + """Query or modify the configuration options of an item TAGORID. - The values for resources are specified as keyword - arguments. To get an overview about - the allowed keyword arguments call the method without arguments. + Similar to configure() except that it applies to the specified item. """ return self._configure(('itemconfigure', tagOrId), cnf, kw) @@ -3204,7 +3273,7 @@ class Checkbutton(Widget): def __init__(self, master=None, cnf={}, **kw): """Construct a checkbutton widget with the parent MASTER. - Valid resource names: activebackground, activeforeground, anchor, + Valid option names: activebackground, activeforeground, anchor, background, bd, bg, bitmap, borderwidth, command, cursor, disabledforeground, fg, font, foreground, height, highlightbackground, highlightcolor, highlightthickness, image, @@ -3235,7 +3304,7 @@ def flash(self): self.tk.call(self._w, 'flash') def invoke(self): - """Toggle the button and invoke a command if given as resource.""" + """Toggle the button and invoke a command if given as option.""" return self.tk.call(self._w, 'invoke') def select(self): @@ -3253,7 +3322,7 @@ class Entry(Widget, XView): def __init__(self, master=None, cnf={}, **kw): """Construct an entry widget with the parent MASTER. - Valid resource names: background, bd, bg, borderwidth, cursor, + Valid option names: background, bd, bg, borderwidth, cursor, exportselection, fg, font, foreground, highlightbackground, highlightcolor, highlightthickness, insertbackground, insertborderwidth, insertofftime, insertontime, insertwidth, @@ -3339,7 +3408,7 @@ class Frame(Widget): def __init__(self, master=None, cnf={}, **kw): """Construct a frame widget with the parent MASTER. - Valid resource names: background, bd, bg, borderwidth, class, + Valid option names: background, bd, bg, borderwidth, class, colormap, container, cursor, height, highlightbackground, highlightcolor, highlightthickness, relief, takefocus, visual, width.""" cnf = _cnfmerge((cnf, kw)) @@ -3383,7 +3452,7 @@ class Listbox(Widget, XView, YView): def __init__(self, master=None, cnf={}, **kw): """Construct a listbox widget with the parent MASTER. - Valid resource names: background, bd, bg, borderwidth, cursor, + Valid option names: background, bd, bg, borderwidth, cursor, exportselection, fg, font, foreground, height, highlightbackground, highlightcolor, highlightthickness, relief, selectbackground, selectborderwidth, selectforeground, selectmode, setgrid, takefocus, @@ -3476,18 +3545,15 @@ def size(self): return self.tk.getint(self.tk.call(self._w, 'size')) def itemcget(self, index, option): - """Return the resource value for an ITEM and an OPTION.""" + """Return the value of OPTION for item at INDEX.""" return self.tk.call( (self._w, 'itemcget') + (index, '-'+option)) def itemconfigure(self, index, cnf=None, **kw): - """Configure resources of an ITEM. + """Query or modify the configuration options of an item at INDEX. - The values for resources are specified as keyword arguments. - To get an overview about the allowed keyword arguments - call the method without arguments. - Valid resource names: background, bg, foreground, fg, - selectbackground, selectforeground.""" + Similar to configure() except that it applies to the specified item. + """ return self._configure(('itemconfigure', index), cnf, kw) itemconfig = itemconfigure @@ -3499,7 +3565,7 @@ class Menu(Widget): def __init__(self, master=None, cnf={}, **kw): """Construct menu widget with the parent MASTER. - Valid resource names: activebackground, activeborderwidth, + Valid option names: activebackground, activeborderwidth, activeforeground, background, bd, bg, borderwidth, cursor, disabledforeground, fg, font, foreground, postcommand, relief, selectcolor, takefocus, tearoff, tearoffcommand, title, type.""" @@ -3580,11 +3646,15 @@ def delete(self, index1, index2=None): self.tk.call(self._w, 'delete', index1, index2) def entrycget(self, index, option): - """Return the resource value of a menu item for OPTION at INDEX.""" + """Return the value of OPTION for a menu item at INDEX.""" return self.tk.call(self._w, 'entrycget', index, '-' + option) def entryconfigure(self, index, cnf=None, **kw): - """Configure a menu item at INDEX.""" + """Query or modify the configuration options of a menu item at INDEX. + + Similar to configure() except that it applies to the specified + menu item. + """ return self._configure(('entryconfigure', index), cnf, kw) entryconfig = entryconfigure @@ -3642,7 +3712,7 @@ class Radiobutton(Widget): def __init__(self, master=None, cnf={}, **kw): """Construct a radiobutton widget with the parent MASTER. - Valid resource names: activebackground, activeforeground, anchor, + Valid option names: activebackground, activeforeground, anchor, background, bd, bg, bitmap, borderwidth, command, cursor, disabledforeground, fg, font, foreground, height, highlightbackground, highlightcolor, highlightthickness, image, @@ -3661,7 +3731,7 @@ def flash(self): self.tk.call(self._w, 'flash') def invoke(self): - """Toggle the button and invoke a command if given as resource.""" + """Toggle the button and invoke a command if given as option.""" return self.tk.call(self._w, 'invoke') def select(self): @@ -3675,7 +3745,7 @@ class Scale(Widget): def __init__(self, master=None, cnf={}, **kw): """Construct a scale widget with the parent MASTER. - Valid resource names: activebackground, background, bigincrement, bd, + Valid option names: activebackground, background, bigincrement, bd, bg, borderwidth, command, cursor, digits, fg, font, foreground, from, highlightbackground, highlightcolor, highlightthickness, label, length, orient, relief, repeatdelay, repeatinterval, resolution, @@ -3714,7 +3784,7 @@ class Scrollbar(Widget): def __init__(self, master=None, cnf={}, **kw): """Construct a scrollbar widget with the parent MASTER. - Valid resource names: activebackground, activerelief, + Valid option names: activebackground, activerelief, background, bd, bg, borderwidth, command, cursor, elementborderwidth, highlightbackground, highlightcolor, highlightthickness, jump, orient, @@ -3958,7 +4028,11 @@ def image_cget(self, index, option): return self.tk.call(self._w, "image", "cget", index, option) def image_configure(self, index, cnf=None, **kw): - """Configure an embedded image at INDEX.""" + """Query or modify the configuration options of an embedded image at INDEX. + + Similar to configure() except that it applies to the specified + embedded image. + """ return self._configure(('image', 'configure', index), cnf, kw) def image_create(self, index, cnf={}, **kw): @@ -4039,8 +4113,9 @@ def scan_dragto(self, x, y): self.tk.call(self._w, 'scan', 'dragto', x, y) def search(self, pattern, index, stopindex=None, - forwards=None, backwards=None, exact=None, - regexp=None, nocase=None, count=None, elide=None): + forwards=None, backwards=None, exact=None, + regexp=None, nocase=None, count=None, + elide=None, *, nolinestop=None, strictlimits=None): """Search PATTERN beginning from INDEX until STOPINDEX. Return the index of the first character of a match or an empty string.""" @@ -4052,12 +4127,39 @@ def search(self, pattern, index, stopindex=None, if nocase: args.append('-nocase') if elide: args.append('-elide') if count: args.append('-count'); args.append(count) + if nolinestop: args.append('-nolinestop') + if strictlimits: args.append('-strictlimits') if pattern and pattern[0] == '-': args.append('--') args.append(pattern) args.append(index) - if stopindex: args.append(stopindex) + if stopindex is not None: args.append(stopindex) return str(self.tk.call(tuple(args))) + def search_all(self, pattern, index, stopindex=None, *, + forwards=None, backwards=None, exact=None, + regexp=None, nocase=None, count=None, + elide=None, nolinestop=None, overlap=None, + strictlimits=None): + """Search all occurrences of PATTERN from INDEX to STOPINDEX. + Return a tuple of indices where matches begin.""" + args = [self._w, 'search', '-all'] + if forwards: args.append('-forwards') + if backwards: args.append('-backwards') + if exact: args.append('-exact') + if regexp: args.append('-regexp') + if nocase: args.append('-nocase') + if elide: args.append('-elide') + if count: args.append('-count'); args.append(count) + if nolinestop: args.append('-nolinestop') + if overlap: args.append('-overlap') + if strictlimits: args.append('-strictlimits') + if pattern and pattern[0] == '-': args.append('--') + args.append(pattern) + args.append(index) + if stopindex is not None: args.append(stopindex) + result = self.tk.call(tuple(args)) + return self.tk.splitlist(result) + def see(self, index): """Scroll such that the character at INDEX is visible.""" self.tk.call(self._w, 'see', index) @@ -4096,7 +4198,10 @@ def tag_cget(self, tagName, option): return self.tk.call(self._w, 'tag', 'cget', tagName, option) def tag_configure(self, tagName, cnf=None, **kw): - """Configure a tag TAGNAME.""" + """Query or modify the configuration options of a tag TAGNAME. + + Similar to configure() except that it applies to the specified tag. + """ return self._configure(('tag', 'configure', tagName), cnf, kw) tag_config = tag_configure @@ -4154,7 +4259,11 @@ def window_cget(self, index, option): return self.tk.call(self._w, 'window', 'cget', index, option) def window_configure(self, index, cnf=None, **kw): - """Configure an embedded window at INDEX.""" + """Query or modify the configuration options of an embedded window at INDEX. + + Similar to configure() except that it applies to the specified + embedded window. + """ return self._configure(('window', 'configure', index), cnf, kw) window_config = window_configure @@ -4194,7 +4303,7 @@ class OptionMenu(Menubutton): def __init__(self, master, variable, value, *values, **kwargs): """Construct an optionmenu widget with the parent MASTER, with - the resource textvariable set to VARIABLE, the initially selected + the option textvariable set to VARIABLE, the initially selected value VALUE, the other menu values VALUES and an additional keyword argument command.""" kw = {"borderwidth": 2, "textvariable": variable, @@ -4264,6 +4373,8 @@ def __setitem__(self, key, value): def __getitem__(self, key): return self.tk.call(self.name, 'configure', '-'+key) + __iter__ = None # prevent using __getitem__ for iteration + def configure(self, **kw): """Configure the image.""" res = () @@ -4296,7 +4407,7 @@ class PhotoImage(Image): def __init__(self, name=None, cnf={}, master=None, **kw): """Create an image with NAME. - Valid resource names: data, format, file, gamma, height, palette, + Valid option names: data, format, file, gamma, height, palette, width.""" Image.__init__(self, 'photo', name, cnf, master, **kw) @@ -4559,7 +4670,7 @@ class BitmapImage(Image): def __init__(self, name=None, cnf={}, master=None, **kw): """Create a bitmap with NAME. - Valid resource names: background, data, file, foreground, maskdata, maskfile.""" + Valid option names: background, data, file, foreground, maskdata, maskfile.""" Image.__init__(self, 'bitmap', name, cnf, master, **kw) @@ -4877,26 +4988,17 @@ def sash_place(self, index, x, y): return self.sash("place", index, x, y) def panecget(self, child, option): - """Query a management option for window. - - Option may be any value allowed by the paneconfigure subcommand - """ + """Return the value of option for a child window.""" return self.tk.call( (self._w, 'panecget') + (child, '-'+option)) def paneconfigure(self, tagOrId, cnf=None, **kw): - """Query or modify the management options for window. - - If no option is specified, returns a list describing all - of the available options for pathName. If option is - specified with no value, then the command returns a list - describing the one named option (this list will be identical - to the corresponding sublist of the value returned if no - option is specified). If one or more option-value pairs are - specified, then the command modifies the given widget - option(s) to have the given value(s); in this case the - command returns an empty string. The following options - are supported: + """Query or modify the configuration options for a child window. + + Similar to configure() except that it applies to the specified + window. + + The following options are supported: after window Insert the window after the window specified. window diff --git a/Lib/tkinter/font.py b/Lib/tkinter/font.py index 3e24e28ef58cde..896e910d69f6f3 100644 --- a/Lib/tkinter/font.py +++ b/Lib/tkinter/font.py @@ -6,7 +6,6 @@ import itertools import tkinter -__version__ = "0.9" __all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"] @@ -115,6 +114,8 @@ def __getitem__(self, key): def __setitem__(self, key, value): self.configure(**{key: value}) + __iter__ = None # prevent using __getitem__ for iteration + def __del__(self): try: if self.delete_font: @@ -198,6 +199,15 @@ def names(root=None): return root.tk.splitlist(root.tk.call("font", "names")) +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "0.9" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + # -------------------------------------------------------------------- # test stuff diff --git a/Lib/tkinter/simpledialog.py b/Lib/tkinter/simpledialog.py index 6e5b025a9f9d7d..4f9eb44f034677 100644 --- a/Lib/tkinter/simpledialog.py +++ b/Lib/tkinter/simpledialog.py @@ -23,9 +23,12 @@ askstring -- get a string from the user """ -from tkinter import * +from tkinter import Button, Entry, Frame, Label, Message, Tk, Toplevel from tkinter import _get_temp_root, _destroy_temp_root from tkinter import messagebox +from tkinter.constants import ACTIVE, BOTH, END, LEFT, RIDGE, W, E + +__all__ = ["SimpleDialog", "Dialog", "askinteger", "askfloat", "askstring"] class SimpleDialog: diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py index c0cf1e787fa9ad..5c5ef11ae05ba6 100644 --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -12,8 +12,6 @@ of the widgets appearance lies at Themes. """ -__version__ = "0.3.1" - __author__ = "Guilherme Polo <ggpolo@gmail.com>" __all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label", @@ -1589,7 +1587,7 @@ class OptionMenu(Menubutton): def __init__(self, master, variable, default=None, *values, **kwargs): """Construct a themed OptionMenu widget with master as the parent, - the resource textvariable set to variable, the initially selected + the option textvariable set to variable, the initially selected value specified by the default parameter, the menu values given by *values and additional keywords. @@ -1648,3 +1646,12 @@ def destroy(self): except AttributeError: pass super().destroy() + + +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "0.3.1" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/tokenize.py b/Lib/tokenize.py index 7e71755068e1df..3545d92c4f5d7f 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -35,8 +35,9 @@ from token import * from token import EXACT_TOKEN_TYPES import _tokenize +lazy import _colorize -cookie_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII) +cookie_re = re.compile(br'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII) blank_re = re.compile(br'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII) import token @@ -385,24 +386,25 @@ def read_or_stop(): except StopIteration: return b'' - def find_cookie(line): + def check(line, encoding): + # Check if the line matches the encoding. + if 0 in line: + raise SyntaxError("source code cannot contain null bytes") try: - # Decode as UTF-8. Either the line is an encoding declaration, - # in which case it should be pure ASCII, or it must be UTF-8 - # per default encoding. - line_string = line.decode('utf-8') + line.decode(encoding) except UnicodeDecodeError: msg = "invalid or missing encoding declaration" if filename is not None: msg = '{} for {!r}'.format(msg, filename) raise SyntaxError(msg) - match = cookie_re.match(line_string) + def find_cookie(line): + match = cookie_re.match(line) if not match: return None - encoding = _get_normal_name(match.group(1)) + encoding = _get_normal_name(match.group(1).decode()) try: - codec = lookup(encoding) + lookup(encoding) except LookupError: # This behaviour mimics the Python interpreter if filename is None: @@ -433,18 +435,23 @@ def find_cookie(line): encoding = find_cookie(first) if encoding: + check(first, encoding) return encoding, [first] if not blank_re.match(first): + check(first, default) return default, [first] second = read_or_stop() if not second: + check(first, default) return default, [first] encoding = find_cookie(second) if encoding: + check(first + second, encoding) return encoding, [first, second] + check(first + second, default) return default, [first, second] @@ -499,6 +506,56 @@ def generate_tokens(readline): """ return _generate_tokens_from_c_tokenizer(readline, extra_tokens=True) + +def _get_token_colors(syntax, tokenize): + """Map token type numbers to theme colors.""" + return frozendict({ + COMMENT: syntax.comment, + DEDENT: tokenize.whitespace, + ENCODING: tokenize.whitespace, + ENDMARKER: tokenize.whitespace, + ERRORTOKEN: tokenize.error, + FSTRING_START: syntax.string, + FSTRING_MIDDLE: syntax.string, + FSTRING_END: syntax.string, + INDENT: tokenize.whitespace, + NAME: syntax.reset, + NEWLINE: tokenize.whitespace, + NL: tokenize.whitespace, + NUMBER: syntax.number, + OP: syntax.op, + SOFT_KEYWORD: syntax.soft_keyword, + STRING: syntax.string, + TSTRING_START: syntax.string, + TSTRING_MIDDLE: syntax.string, + TSTRING_END: syntax.string, + }) + + +def _format_tokens(tokens, *, color=False, exact=False): + theme = _colorize.get_theme(force_no_color=not color) + s = theme.syntax + t = theme.tokenize + token_colors = _get_token_colors(s, t) + for token in tokens: + token_range = ( + f"{t.position}{token.start[0]}" + f"{t.delimiter},{t.position}{token.start[1]}" + f"{t.delimiter}-" + f"{t.position}{token.end[0]}" + f"{t.delimiter},{t.position}{token.end[1]}" + f"{t.delimiter}:" + ) + token_color = token_colors.get(token.type, s.reset) + token_name = tok_name[token.exact_type if exact else token.type] + visible_range = f"{token.start[0]},{token.start[1]}-{token.end[0]},{token.end[1]}:" + yield ( + f"{token_range}{' ' * (20 - len(visible_range))}" + f"{token_color}{token_name:<15}" + f"{s.reset}{token.string!r:<15}" + ) + + def _main(args=None): import argparse @@ -518,10 +575,10 @@ def error(message, filename=None, location=None): sys.exit(1) # Parse the arguments and options - parser = argparse.ArgumentParser(color=True) + parser = argparse.ArgumentParser() parser.add_argument(dest='filename', nargs='?', metavar='filename.py', - help='the file to tokenize; defaults to stdin') + help='the file to tokenize; defaults to `stdin`') parser.add_argument('-e', '--exact', dest='exact', action='store_true', help='display token names using the exact type') args = parser.parse_args(args) @@ -539,13 +596,8 @@ def error(message, filename=None, location=None): # Output the tokenization - for token in tokens: - token_type = token.type - if args.exact: - token_type = token.exact_type - token_range = "%d,%d-%d,%d:" % (token.start + token.end) - print("%-20s%-15s%-15r" % - (token_range, tok_name[token_type], token.string)) + for line in _format_tokens(tokens, color=True, exact=args.exact): + print(line) except IndentationError as err: line, column = err.args[1][1:3] error(err.args[0], filename, (line, column)) diff --git a/Lib/tomllib/_parser.py b/Lib/tomllib/_parser.py index 3ee47aa9e0afba..b89934808008ef 100644 --- a/Lib/tomllib/_parser.py +++ b/Lib/tomllib/_parser.py @@ -4,7 +4,11 @@ from __future__ import annotations -from types import MappingProxyType +# Defer loading regular expressions until we actually need them in +# parse_value(). +__lazy_modules__ = ["tomllib._re"] + +import sys from ._re import ( RE_DATETIME, @@ -15,42 +19,53 @@ match_to_number, ) +if sys.version_info < (3, 15): + from types import MappingProxyType as frozendict + TYPE_CHECKING = False if TYPE_CHECKING: from collections.abc import Iterable - from typing import IO, Any + from typing import IO, Any, Final from ._types import Key, ParseFloat, Pos -ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) +# Pathologically excessive number of parts in a key runs into quadratic +# behavior (e.g. in Flags.is_). +# Even if keys aren't currently parsed using recursion, they name a +# recursive structure, so it makes sense to limit it using getrecursionlimit() +# and RecursionError. +MAX_KEY_PARTS: Final = sys.getrecursionlimit() + +ASCII_CTRL: Final = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) # Neither of these sets include quotation mark or backslash. They are # currently handled as separate cases in the parser functions. -ILLEGAL_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t") -ILLEGAL_MULTILINE_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t\n") +ILLEGAL_BASIC_STR_CHARS: Final = ASCII_CTRL - frozenset("\t") +ILLEGAL_MULTILINE_BASIC_STR_CHARS: Final = ASCII_CTRL - frozenset("\t\n") -ILLEGAL_LITERAL_STR_CHARS = ILLEGAL_BASIC_STR_CHARS -ILLEGAL_MULTILINE_LITERAL_STR_CHARS = ILLEGAL_MULTILINE_BASIC_STR_CHARS +ILLEGAL_LITERAL_STR_CHARS: Final = ILLEGAL_BASIC_STR_CHARS +ILLEGAL_MULTILINE_LITERAL_STR_CHARS: Final = ILLEGAL_MULTILINE_BASIC_STR_CHARS -ILLEGAL_COMMENT_CHARS = ILLEGAL_BASIC_STR_CHARS +ILLEGAL_COMMENT_CHARS: Final = ILLEGAL_BASIC_STR_CHARS -TOML_WS = frozenset(" \t") -TOML_WS_AND_NEWLINE = TOML_WS | frozenset("\n") -BARE_KEY_CHARS = frozenset( +TOML_WS: Final = frozenset(" \t") +TOML_WS_AND_NEWLINE: Final = TOML_WS | frozenset("\n") +BARE_KEY_CHARS: Final = frozenset( "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "-_" ) -KEY_INITIAL_CHARS = BARE_KEY_CHARS | frozenset("\"'") -HEXDIGIT_CHARS = frozenset("abcdef" "ABCDEF" "0123456789") +KEY_INITIAL_CHARS: Final = BARE_KEY_CHARS | frozenset("\"'") +HEXDIGIT_CHARS: Final = frozenset("abcdef" "ABCDEF" "0123456789") -BASIC_STR_ESCAPE_REPLACEMENTS = MappingProxyType( +BASIC_STR_ESCAPE_REPLACEMENTS: Final = frozendict( { "\\b": "\u0008", # backspace "\\t": "\u0009", # tab - "\\n": "\u000A", # linefeed - "\\f": "\u000C", # form feed - "\\r": "\u000D", # carriage return + "\\n": "\u000a", # linefeed + "\\f": "\u000c", # form feed + "\\r": "\u000d", # carriage return + "\\e": "\u001b", # escape '\\"': "\u0022", # quote - "\\\\": "\u005C", # backslash + "\\\\": "\u005c", # backslash } ) @@ -133,7 +148,7 @@ def load(fp: IO[bytes], /, *, parse_float: ParseFloat = float) -> dict[str, Any] return loads(s, parse_float=parse_float) -def loads(s: str, /, *, parse_float: ParseFloat = float) -> dict[str, Any]: # noqa: C901 +def loads(s: str, /, *, parse_float: ParseFloat = float) -> dict[str, Any]: """Parse TOML from a string.""" # The spec allows converting "\r\n" to "\n", even in string @@ -208,10 +223,10 @@ class Flags: """Flags that map to parsed keys/namespaces.""" # Marks an immutable namespace (inline array or inline table). - FROZEN = 0 + FROZEN: Final = 0 # Marks a nest that has been explicitly created and can no longer # be opened using the "[table]" syntax. - EXPLICIT_NEST = 1 + EXPLICIT_NEST: Final = 1 def __init__(self) -> None: self._flags: dict[str, dict[Any, Any]] = {} @@ -257,8 +272,8 @@ def is_(self, key: Key, flag: int) -> bool: cont = inner_cont["nested"] key_stem = key[-1] if key_stem in cont: - cont = cont[key_stem] - return flag in cont["flags"] or flag in cont["recursive_flags"] + inner_cont = cont[key_stem] + return flag in inner_cont["flags"] or flag in inner_cont["recursive_flags"] return False @@ -462,6 +477,10 @@ def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]: pos = skip_chars(src, pos, TOML_WS) pos, key_part = parse_key_part(src, pos) key += (key_part,) + if len(key) > MAX_KEY_PARTS: + raise RecursionError( + f"TOML key has more than the allowed {MAX_KEY_PARTS} parts" + ) pos = skip_chars(src, pos, TOML_WS) @@ -515,7 +534,7 @@ def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos nested_dict = NestedDict() flags = Flags() - pos = skip_chars(src, pos, TOML_WS) + pos = skip_comments_and_array_ws(src, pos) if src.startswith("}", pos): return pos + 1, nested_dict.dict while True: @@ -530,16 +549,18 @@ def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos if key_stem in nest: raise TOMLDecodeError(f"Duplicate inline table key {key_stem!r}", src, pos) nest[key_stem] = value - pos = skip_chars(src, pos, TOML_WS) + pos = skip_comments_and_array_ws(src, pos) c = src[pos : pos + 1] if c == "}": return pos + 1, nested_dict.dict if c != ",": raise TOMLDecodeError("Unclosed inline table", src, pos) + pos += 1 + pos = skip_comments_and_array_ws(src, pos) + if src.startswith("}", pos): + return pos + 1, nested_dict.dict if isinstance(value, (dict, list)): flags.set(key, Flags.FROZEN, recursive=True) - pos += 1 - pos = skip_chars(src, pos, TOML_WS) def parse_basic_str_escape( @@ -561,6 +582,8 @@ def parse_basic_str_escape( pos += 1 pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) return pos, "" + if escape_id == "\\x": + return parse_hex_char(src, pos, 2) if escape_id == "\\u": return parse_hex_char(src, pos, 4) if escape_id == "\\U": @@ -660,7 +683,7 @@ def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]: pos += 1 -def parse_value( # noqa: C901 +def parse_value( src: str, pos: Pos, parse_float: ParseFloat ) -> tuple[Pos, Any]: try: diff --git a/Lib/tomllib/_re.py b/Lib/tomllib/_re.py index eb8beb19747288..fc374ed63d3e37 100644 --- a/Lib/tomllib/_re.py +++ b/Lib/tomllib/_re.py @@ -10,16 +10,20 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Any + from typing import Any, Final from ._types import ParseFloat -# E.g. -# - 00:32:00.999999 -# - 00:32:00 -_TIME_RE_STR = r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?" +_TIME_RE_STR: Final = r""" +([01][0-9]|2[0-3]) # hours +:([0-5][0-9]) # minutes +(?: + :([0-5][0-9]) # optional seconds + (?:\.([0-9]{1,6})[0-9]*)? # optional fractions of a second +)? +""" -RE_NUMBER = re.compile( +RE_NUMBER: Final = re.compile( r""" 0 (?: @@ -38,8 +42,8 @@ """, flags=re.VERBOSE, ) -RE_LOCALTIME = re.compile(_TIME_RE_STR) -RE_DATETIME = re.compile( +RE_LOCALTIME: Final = re.compile(_TIME_RE_STR, flags=re.VERBOSE) +RE_DATETIME: Final = re.compile( rf""" ([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27 (?: @@ -74,7 +78,8 @@ def match_to_datetime(match: re.Match[str]) -> datetime | date: year, month, day = int(year_str), int(month_str), int(day_str) if hour_str is None: return date(year, month, day) - hour, minute, sec = int(hour_str), int(minute_str), int(sec_str) + hour, minute = int(hour_str), int(minute_str) + sec = int(sec_str) if sec_str else 0 micros = int(micros_str.ljust(6, "0")) if micros_str else 0 if offset_sign_str: tz: tzinfo | None = cached_tz( @@ -103,8 +108,9 @@ def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone: def match_to_localtime(match: re.Match[str]) -> time: hour_str, minute_str, sec_str, micros_str = match.groups() + sec = int(sec_str) if sec_str else 0 micros = int(micros_str.ljust(6, "0")) if micros_str else 0 - return time(int(hour_str), int(minute_str), int(sec_str), micros) + return time(int(hour_str), int(minute_str), sec, micros) def match_to_number(match: re.Match[str], parse_float: ParseFloat) -> Any: diff --git a/Lib/tomllib/mypy.ini b/Lib/tomllib/mypy.ini index 1761dce45562a6..f7eeffd575c1c7 100644 --- a/Lib/tomllib/mypy.ini +++ b/Lib/tomllib/mypy.ini @@ -12,6 +12,4 @@ pretty = True # Enable most stricter settings enable_error_code = ignore-without-code strict = True -strict_bytes = True -local_partial_types = True warn_unreachable = True diff --git a/Lib/trace.py b/Lib/trace.py index cf8817f4383fc1..43ec201c4696d1 100644 --- a/Lib/trace.py +++ b/Lib/trace.py @@ -604,7 +604,7 @@ def results(self): def main(): import argparse - parser = argparse.ArgumentParser(color=True) + parser = argparse.ArgumentParser() parser.add_argument('--version', action='version', version='trace 2.0') grp = parser.add_argument_group('Main options', @@ -612,27 +612,27 @@ def main(): grp.add_argument('-c', '--count', action='store_true', help='Count the number of times each line is executed and write ' - 'the counts to <module>.cover for each module executed, in ' - 'the module\'s directory. See also --coverdir, --file, ' - '--no-report below.') + 'the counts to `<module>.cover` for each module executed, in ' + 'the module\'s directory. See also `--coverdir`, `--file`, ' + '`--no-report` below.') grp.add_argument('-t', '--trace', action='store_true', - help='Print each line to sys.stdout before it is executed') + help='Print each line to `sys.stdout` before it is executed') grp.add_argument('-l', '--listfuncs', action='store_true', help='Keep track of which functions are executed at least once ' - 'and write the results to sys.stdout after the program exits. ' - 'Cannot be specified alongside --trace or --count.') + 'and write the results to `sys.stdout` after the program exits. ' + 'Cannot be specified alongside `--trace` or `--count`.') grp.add_argument('-T', '--trackcalls', action='store_true', help='Keep track of caller/called pairs and write the results to ' - 'sys.stdout after the program exits.') + '`sys.stdout` after the program exits.') grp = parser.add_argument_group('Modifiers') _grp = grp.add_mutually_exclusive_group() _grp.add_argument('-r', '--report', action='store_true', help='Generate a report from a counts file; does not execute any ' - 'code. --file must specify the results file to read, which ' - 'must have been created in a previous run with --count ' - '--file=FILE') + 'code. `--file` must specify the results file to read, which ' + 'must have been created in a previous run with `--count` ' + '`--file=FILE`') _grp.add_argument('-R', '--no-report', action='store_true', help='Do not generate the coverage report files. ' 'Useful if you want to accumulate over several runs.') @@ -641,14 +641,14 @@ def main(): help='File to accumulate counts over several runs') grp.add_argument('-C', '--coverdir', help='Directory where the report files go. The coverage report ' - 'for <package>.<module> will be written to file ' - '<dir>/<package>/<module>.cover') + 'for `<package>.<module>` will be written to file ' + '`<dir>/<package>/<module>.cover`') grp.add_argument('-m', '--missing', action='store_true', help='Annotate executable lines that were not executed with ' '">>>>>> "') grp.add_argument('-s', '--summary', action='store_true', - help='Write a brief summary for each file to sys.stdout. ' - 'Can only be used with --count or --report') + help='Write a brief summary for each file to `sys.stdout`. ' + 'Can only be used with `--count` or `--report`') grp.add_argument('-g', '--timing', action='store_true', help='Prefix each line with the time since the program started. ' 'Only used while tracing') @@ -661,7 +661,7 @@ def main(): 'module names.') grp.add_argument('--ignore-dir', action='append', default=[], help='Ignore files in the given directory ' - '(multiple directories can be joined by os.pathsep).') + '(multiple directories can be joined by `os.pathsep`).') parser.add_argument('--module', action='store_true', default=False, help='Trace a module. ') @@ -721,7 +721,6 @@ def parse_ignore_dir(s): '__package__': mod_spec.parent, '__loader__': mod_spec.loader, '__spec__': mod_spec, - '__cached__': None, } else: sys.argv = [opts.progname, *opts.arguments] @@ -734,7 +733,6 @@ def parse_ignore_dir(s): '__file__': opts.progname, '__name__': '__main__', '__package__': None, - '__cached__': None, } t.runctx(code, globs, globs) except OSError as err: diff --git a/Lib/traceback.py b/Lib/traceback.py index f0dbb6352f7760..614a12f69b32e4 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1,18 +1,29 @@ """Extract, format and print information about Python stack traces.""" import collections.abc +import functools import itertools import linecache +import os +import re import sys import textwrap +import types import warnings import codeop import keyword import tokenize import io -import _colorize +import importlib.util +import pathlib from contextlib import suppress +lazy import _colorize + +try: + from _missing_stdlib_info import _MISSING_STDLIB_MODULE_MESSAGES +except ImportError: + _MISSING_STDLIB_MODULE_MESSAGES = {} __all__ = ['extract_stack', 'extract_tb', 'format_exception', 'format_exception_only', 'format_list', 'format_stack', @@ -21,6 +32,36 @@ 'FrameSummary', 'StackSummary', 'TracebackException', 'walk_stack', 'walk_tb', 'print_list'] + +class _ShutdownTheme: + """Empty stand-in if `_colorize` cannot be imported during late shutdown.""" + def __getattr__(self, _): return self + def __getitem__(self, _): return "" + def __format__(self, _): return "" + def __str__(self): return "" + def __add__(self, other): return other + __radd__ = __add__ + + +_shutdown_theme = _ShutdownTheme() + + +def _safe_get_theme(*, force_color=False, force_no_color=False): + try: + return _colorize.get_theme( + force_color=force_color, force_no_color=force_no_color + ) + except ImportError: + return _shutdown_theme + + +def _safe_can_colorize(*, file=None): + try: + return _colorize.can_colorize(file=file) + except ImportError: + return False + + # # Formatting and printing lists of traceback lines. # @@ -74,10 +115,10 @@ def extract_tb(tb, limit=None): This is useful for alternate formatting of stack traces. If 'limit' is omitted or None, all entries are extracted. A pre-processed stack trace entry is a FrameSummary object - containing attributes filename, lineno, name, and line - representing the information that is usually printed for a stack - trace. The line is a string with leading and trailing - whitespace stripped; if the source is not available it is None. + representing the information that is usually printed for a + stack trace. The line attribute is a string with + leading and trailing whitespace stripped; if the source is not + available the corresponding attribute is None. """ return StackSummary._extract_from_extended_frame_gen( _walk_tb_with_full_positions(tb), limit=limit) @@ -137,9 +178,10 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ BUILTIN_EXCEPTION_LIMIT = object() -def _print_exception_bltin(exc, /): - file = sys.stderr if sys.stderr is not None else sys.__stderr__ - colorize = _colorize.can_colorize(file=file) +def _print_exception_bltin(exc, file=None, /): + if file is None: + file = sys.stderr if sys.stderr is not None else sys.__stderr__ + colorize = _safe_can_colorize(file=file) return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize) @@ -187,9 +229,9 @@ def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize= valuestr = _safe_string(value, 'exception') end_char = "\n" if insert_final_newline else "" if colorize: - theme = _colorize.get_theme(force_color=True).traceback + theme = _safe_get_theme(force_color=True).traceback else: - theme = _colorize.get_theme(force_no_color=True).traceback + theme = _safe_get_theme(force_no_color=True).traceback if value is None or not valuestr: line = f"{theme.type}{etype}{theme.reset}{end_char}" else: @@ -205,9 +247,9 @@ def _safe_string(value, what, func=str): # -- -def print_exc(limit=None, file=None, chain=True): +def print_exc(limit=None, file=None, chain=True, **kwargs): """Shorthand for 'print_exception(sys.exception(), limit=limit, file=file, chain=chain)'.""" - print_exception(sys.exception(), limit=limit, file=file, chain=chain) + print_exception(sys.exception(), limit=limit, file=file, chain=chain, **kwargs) def format_exc(limit=None, chain=True): """Like print_exc() but return a string.""" @@ -253,9 +295,8 @@ def extract_stack(f=None, limit=None): The return value has the same format as for extract_tb(). The optional 'f' and 'limit' arguments have the same meaning as for - print_stack(). Each item in the list is a quadruple (filename, - line number, function name, text), and the entries are in order - from oldest to newest stack frame. + print_stack(). Each item in the list is a FrameSummary object, + and the entries are in order from oldest to newest stack frame. """ if f is None: f = sys._getframe().f_back @@ -283,7 +324,7 @@ class FrameSummary: active when the frame was captured. - :attr:`name` The name of the function or method that was executing when the frame was captured. - - :attr:`line` The text from the linecache module for the + - :attr:`line` The text from the linecache module for the line of code that was running when the frame was captured. - :attr:`locals` Either None if locals were not supplied, or a dict mapping the name to the repr() of the variable. @@ -540,12 +581,12 @@ def format_frame_summary(self, frame_summary, **kwargs): colorize = kwargs.get("colorize", False) row = [] filename = frame_summary.filename - if frame_summary.filename.startswith("<stdin>-"): + if frame_summary.filename.startswith("<stdin-") and frame_summary.filename.endswith('>'): filename = "<stdin>" if colorize: - theme = _colorize.get_theme(force_color=True).traceback + theme = _safe_get_theme(force_color=True).traceback else: - theme = _colorize.get_theme(force_no_color=True).traceback + theme = _safe_get_theme(force_no_color=True).traceback row.append( ' File {}"{}"{}, line {}{}{}, in {}{}{}\n'.format( theme.filename, @@ -674,12 +715,12 @@ def output_line(lineno): colorized_line_parts = [] colorized_carets_parts = [] - for color, group in itertools.groupby(itertools.zip_longest(line, carets, fillvalue=""), key=lambda x: x[1]): + for color, group in itertools.groupby(_zip_display_width(line, carets), key=lambda x: x[1]): caret_group = list(group) - if color == "^": + if "^" in color: colorized_line_parts.append(theme.error_highlight + "".join(char for char, _ in caret_group) + theme.reset) colorized_carets_parts.append(theme.error_highlight + "".join(caret for _, caret in caret_group) + theme.reset) - elif color == "~": + elif "~" in color: colorized_line_parts.append(theme.error_range + "".join(char for char, _ in caret_group) + theme.reset) colorized_carets_parts.append(theme.error_range + "".join(caret for _, caret in caret_group) + theme.reset) else: @@ -961,27 +1002,69 @@ def setup_positions(expr, force_valid=True): return None -_WIDE_CHAR_SPECIFIERS = "WF" + +def _zip_display_width(line, carets): + carets = iter(carets) + if line.isascii() and '\x1a' not in line: + for char in line: + yield char, next(carets, "") + return + + import unicodedata + for char in unicodedata.iter_graphemes(line): + char = str(char) + char_width = _display_width(char) + yield char, "".join(itertools.islice(carets, char_width)) + + +@functools.cache +def _str_width(c: str) -> int: + # copied from _pyrepl.utils to fix gh-130273 + + if ord(c) < 128: + return 1 + import unicodedata + # gh-139246 for zero-width joiner and combining characters + if unicodedata.combining(c): + return 0 + category = unicodedata.category(c) + if category == "Cf" and c != "\u00ad": + return 0 + w = unicodedata.east_asian_width(c) + if w in ("N", "Na", "H", "A"): + return 1 + return 2 + + +_ANSI_ESCAPE_SEQUENCE = re.compile(r"\x1b\[[ -@]*[A-~]") + + +def _wlen(s: str) -> int: + # copied from _pyrepl.utils to fix gh-130273 + + if len(s) == 1 and s != "\x1a": + return _str_width(s) + length = sum(_str_width(i) for i in s) + # remove lengths of any escape sequences + sequence = _ANSI_ESCAPE_SEQUENCE.findall(s) + ctrl_z_cnt = s.count("\x1a") + return length - sum(len(i) for i in sequence) + ctrl_z_cnt + def _display_width(line, offset=None): - """Calculate the extra amount of width space the given source + """Calculate the amount of width space the given source code segment might take if it were to be displayed on a fixed width output device. Supports wide unicode characters and emojis.""" if offset is None: - offset = len(line) + return _wlen(line) - # Fast track for ASCII-only strings - if line.isascii(): - return offset + return _wlen(line[:offset]) - import unicodedata - - return sum( - 2 if unicodedata.east_asian_width(char) in _WIDE_CHAR_SPECIFIERS else 1 - for char in line[:offset] - ) +def _format_note(note, indent, theme): + for l in note.split("\n"): + yield f"{indent}{theme.note}{l}{theme.reset}\n" class _ExceptionPrintContext: @@ -1050,7 +1133,7 @@ class TracebackException: def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, lookup_lines=True, capture_locals=False, compact=False, max_group_width=15, max_group_depth=10, save_exc_type=True, _seen=None): - # NB: we need to accept exc_traceback, exc_value, exc_traceback to + # NB: we need to accept exc_type, exc_value, exc_traceback to # permit backwards compat with the existing API, otherwise we # need stub thunk objects just to glue it together. # Handle loops in __cause__ or __context__. @@ -1105,25 +1188,62 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, wrong_name = getattr(exc_value, "name_from", None) suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name) if suggestion: - self._str += f". Did you mean: '{suggestion}'?" - elif exc_type and issubclass(exc_type, ModuleNotFoundError) and \ - sys.flags.no_site and \ - getattr(exc_value, "name", None) not in sys.stdlib_module_names: - self._str += (". Site initialization is disabled, did you forget to " - + "add the site-packages directory to sys.path?") - elif exc_type and issubclass(exc_type, (NameError, AttributeError)) and \ + if suggestion.isascii(): + self._str += f". Did you mean: '{suggestion}'?" + else: + self._str += f". Did you mean: '{suggestion}' ({suggestion!a})?" + elif exc_type and issubclass(exc_type, ModuleNotFoundError): + module_name = getattr(exc_value, "name", None) + if module_name in sys.stdlib_module_names: + message = _MISSING_STDLIB_MODULE_MESSAGES.get( + module_name, + f"Standard library module {module_name!r} was not found" + ) + self._str = message + elif sys.flags.no_site: + self._str += (". Site initialization is disabled, did you forget to " + + "add the site-packages directory to sys.path " + + "or to enable your virtual environment?") + elif abi_tag := _find_incompatible_extension_module(module_name): + self._str += ( + ". Although a module with this name was found for a " + f"different Python version ({abi_tag})." + ) + else: + suggestion = _compute_suggestion_error(exc_value, exc_traceback, module_name) + if suggestion: + self._str += f". Did you mean: '{suggestion}'?" + elif exc_type and issubclass(exc_type, AttributeError) and \ + getattr(exc_value, "name", None) is not None: + wrong_name = getattr(exc_value, "name", None) + # Check cross-language/wrong-type hints first (more specific), + # then fall back to Levenshtein distance suggestions. + hint = None + if hasattr(exc_value, 'obj'): + hint = _get_cross_language_hint(exc_value.obj, wrong_name) + if hint: + self._str += f". {hint}" + else: + suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name) + if suggestion: + if suggestion.isascii(): + self._str += f". Did you mean '.{suggestion}' instead of '.{wrong_name}'?" + else: + self._str += f". Did you mean '.{suggestion}' ({suggestion!a}) instead of '.{wrong_name}' ({wrong_name!a})?" + elif exc_type and issubclass(exc_type, NameError) and \ getattr(exc_value, "name", None) is not None: wrong_name = getattr(exc_value, "name", None) suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name) if suggestion: - self._str += f". Did you mean: '{suggestion}'?" - if issubclass(exc_type, NameError): - wrong_name = getattr(exc_value, "name", None) - if wrong_name is not None and wrong_name in sys.stdlib_module_names: - if suggestion: - self._str += f" Or did you forget to import '{wrong_name}'?" - else: - self._str += f". Did you forget to import '{wrong_name}'?" + if suggestion.isascii(): + self._str += f". Did you mean: '{suggestion}'?" + else: + self._str += f". Did you mean: '{suggestion}' ({suggestion!a})?" + if wrong_name is not None and wrong_name in sys.stdlib_module_names: + if suggestion: + self._str += f" Or did you forget to import '{wrong_name}'?" + else: + self._str += f". Did you forget to import '{wrong_name}'?" if lookup_lines: self._load_lines() self.__suppress_context__ = \ @@ -1252,6 +1372,10 @@ def format_exception_only(self, *, show_group=False, _depth=0, **kwargs): well, recursively, with indentation relative to their nesting depth. """ colorize = kwargs.get("colorize", False) + if colorize: + theme = _safe_get_theme(force_color=True).traceback + else: + theme = _safe_get_theme(force_no_color=True).traceback indent = 3 * _depth * ' ' if not self._have_exc_type: @@ -1280,9 +1404,10 @@ def format_exception_only(self, *, show_group=False, _depth=0, **kwargs): ): for note in self.__notes__: note = _safe_string(note, 'note') - yield from [indent + l + '\n' for l in note.split('\n')] + yield from _format_note(note, indent, theme) elif self.__notes__ is not None: - yield indent + "{}\n".format(_safe_string(self.__notes__, '__notes__', func=repr)) + note = _safe_string(self.__notes__, '__notes__', func=repr) + yield from _format_note(note, indent, theme) if self.exceptions and show_group: for ex in self.exceptions: @@ -1321,13 +1446,21 @@ def _find_keyword_typos(self): lines = source.splitlines() error_code = lines[line -1 if line > 0 else 0:end_line] - error_code[0] = error_code[0][offset:] error_code = textwrap.dedent('\n'.join(error_code)) # Do not continue if the source is too large if len(error_code) > 1024: return + # If the original code doesn't raise SyntaxError, we can't validate + # that a keyword replacement actually fixes anything + try: + codeop.compile_command(error_code, symbol="exec", flags=codeop.PyCF_ONLY_AST) + except SyntaxError: + pass # Good - the original code has a syntax error we might fix + else: + return # Original code compiles or is incomplete - can't validate fixes + error_lines = error_code.splitlines() tokens = tokenize.generate_tokens(io.StringIO(error_code).readline) tokens_left_to_process = 10 @@ -1337,7 +1470,8 @@ def _find_keyword_typos(self): if token.type != tokenize.NAME: continue # Only consider NAME tokens on the same line as the error - if from_filename and token.start[0]+line != end_line+1: + the_end = end_line if line == 0 else end_line + 1 + if from_filename and token.start[0]+line != the_end: continue wrong_name = token.string if wrong_name in keyword.kwlist: @@ -1389,9 +1523,9 @@ def _format_syntax_error(self, stype, **kwargs): # Show exactly where the problem was found. colorize = kwargs.get("colorize", False) if colorize: - theme = _colorize.get_theme(force_color=True).traceback + theme = _safe_get_theme(force_color=True).traceback else: - theme = _colorize.get_theme(force_no_color=True).traceback + theme = _safe_get_theme(force_no_color=True).traceback filename_suffix = '' if self.lineno is not None: yield ' File {}"{}"{}, line {}{}{}\n'.format( @@ -1442,10 +1576,11 @@ def _format_syntax_error(self, stype, **kwargs): # Convert 1-based column offset to 0-based index into stripped text colno = offset - 1 - spaces end_colno = end_offset - 1 - spaces - caretspace = ' ' if colno >= 0: - # non-space whitespace (likes tabs) must be kept for alignment - caretspace = ((c if c.isspace() else ' ') for c in ltext[:colno]) + # Calculate display width to account for wide characters + dp_colno = _display_width(ltext, colno) + highlighted = ltext[colno:end_colno] + caret_count = _display_width(highlighted) if highlighted else (end_colno - colno) start_color = end_color = "" if colorize: # colorize from colno to end_colno @@ -1458,9 +1593,9 @@ def _format_syntax_error(self, stype, **kwargs): end_color = theme.reset yield ' {}\n'.format(ltext) yield ' {}{}{}{}\n'.format( - "".join(caretspace), + ' ' * dp_colno, start_color, - ('^' * (end_colno - colno)), + '^' * caret_count, end_color, ) else: @@ -1591,6 +1726,66 @@ def print(self, *, file=None, chain=True, **kwargs): _MOVE_COST = 2 _CASE_COST = 1 +# Cross-language method suggestions for builtin types. +# Consulted as a fallback when Levenshtein-based suggestions find no match. +# +# Inclusion criteria: +# +# 1. Must have evidence of real cross-language confusion (Stack Overflow +# traffic, bug reports in production repos, developer survey data). +# 2. Must not be catchable by Levenshtein distance (too different from +# the correct Python method name). +# +# Each entry maps a wrong method name to a list of (type, suggestion, is_raw) +# tuples. The lookup checks isinstance() so subclasses are also matched. +# If is_raw is False, the suggestion is wrapped in "Did you mean '.X'?". +# If is_raw is True, the suggestion is rendered as-is. +# +# See https://github.com/python/cpython/issues/146406. +_CROSS_LANGUAGE_HINTS = frozendict({ + # list -- JavaScript/Ruby equivalents + "push": ((list, "append", False),), + "concat": ((list, "extend", False),), + # list -- Java/C# equivalents + "addAll": ((list, "extend", False),), + "contains": ((list, "Use 'x in list'.", True),), + # list -- wrong-type suggestion (user expected a set) + "add": ((list, "Did you mean to use a 'set' object?", True), + (frozenset, "Did you mean to use a 'set' object?", True)), + # str -- JavaScript equivalents + "toUpperCase": ((str, "upper", False),), + "toLowerCase": ((str, "lower", False),), + "trimStart": ((str, "lstrip", False),), + "trimEnd": ((str, "rstrip", False),), + # dict -- Java/JavaScript equivalents + "keySet": ((dict, "keys", False),), + "entrySet": ((dict, "items", False),), + "entries": ((dict, "items", False),), + "putAll": ((dict, "update", False),), + "put": ((dict, "Use d[k] = v.", True),), + # tuple -- mutable method on immutable type (user expected a list) + "append": ((tuple, "Did you mean to use a 'list' object?", True),), + "extend": ((tuple, "Did you mean to use a 'list' object?", True),), + "insert": ((tuple, "Did you mean to use a 'list' object?", True),), + "remove": ((tuple, "Did you mean to use a 'list' object?", True), + (frozenset, "Did you mean to use a 'set' object?", True)), + # frozenset -- mutable method on immutable type (user expected a set) + "discard": ((frozenset, "Did you mean to use a 'set' object?", True),), + # frozendict -- mutable method on immutable type (user expected a dict) + "update": ((frozenset, "Did you mean to use a 'set' object?", True), + (frozendict, "Did you mean to use a 'dict' object?", True)), + # clear() -- shared across immutable container types (user expected the mutable counterpart) + "clear": ((tuple, "Did you mean to use a 'list' object?", True), + (frozenset, "Did you mean to use a 'set' object?", True), + (frozendict, "Did you mean to use a 'dict' object?", True)), + # float -- bitwise operators belong to int + "__or__": ((float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True),), + "__and__": ((float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True),), + "__xor__": ((float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True),), + "__lshift__": ((float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True),), + "__rshift__": ((float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True),), +}) + def _substitution_cost(ch_a, ch_b): if ch_a == ch_b: @@ -1600,17 +1795,104 @@ def _substitution_cost(ch_a, ch_b): return _MOVE_COST +def _is_lazy_import(obj, attr_name): + """Check if attr_name in obj's __dict__ is a lazy import. + + Returns True if obj is a module and the attribute is a LazyImportType, + False otherwise. This avoids triggering module loading when computing + suggestions for AttributeError. + """ + if not isinstance(obj, types.ModuleType): + return False + obj_dict = getattr(obj, '__dict__', None) + if obj_dict is None: + return False + attr_value = obj_dict.get(attr_name) + return isinstance(attr_value, types.LazyImportType) + + +def _check_for_nested_attribute(obj, wrong_name, attrs): + """Check if any attribute of obj has the wrong_name as a nested attribute. + + Returns the first nested attribute suggestion found, or None. + Limited to checking 20 attributes. + Only considers non-descriptor outer attributes to avoid executing + arbitrary code. Checks nested attributes statically so descriptors such + as properties can still be suggested without invoking them. + Skips lazy imports to avoid triggering module loading. + """ + from inspect import getattr_static + + # Check for nested attributes (only one level deep) + attrs_to_check = [x for x in attrs if not x.startswith('_')][:20] # Limit number of attributes to check + for attr_name in attrs_to_check: + with suppress(Exception): + # Check if attr_name is a descriptor - if so, skip it + attr_from_class = getattr_static(type(obj), attr_name, _sentinel) + if attr_from_class is not _sentinel and hasattr(attr_from_class, '__get__'): + continue # Skip descriptors to avoid executing arbitrary code + + # Skip lazy imports to avoid triggering module loading + if _is_lazy_import(obj, attr_name): + continue + + # Safe to get the attribute since it's not a descriptor + attr_obj = getattr(obj, attr_name) + + if _is_lazy_import(attr_obj, wrong_name): + continue + + if getattr_static(attr_obj, wrong_name, _sentinel) is not _sentinel: + return f"{attr_name}.{wrong_name}" + + return None + + +def _get_cross_language_hint(obj, wrong_name): + """Check if wrong_name is a common method name from another language, + a mutable method on an immutable type, or a method tried on None. + + Uses isinstance() so subclasses of builtin types also get hints. + Returns a formatted hint string, or None. + """ + entries = _CROSS_LANGUAGE_HINTS.get(wrong_name) + if entries is None: + return None + for check_type, hint, is_raw in entries: + if isinstance(obj, check_type): + if is_raw: + return hint + return f"Did you mean '.{hint}'?" + return None + + +def _get_safe___dir__(obj): + # Use obj.__dir__() to avoid a TypeError when calling dir(obj). + # See gh-131001 and gh-139933. + # Also filters out lazy imports to avoid triggering module loading. + try: + d = obj.__dir__() + except TypeError: # when obj is a class + d = type(obj).__dir__(obj) + return sorted( + x for x in d if isinstance(x, str) and not _is_lazy_import(obj, x) + ) + + def _compute_suggestion_error(exc_value, tb, wrong_name): if wrong_name is None or not isinstance(wrong_name, str): return None + not_normalized = False + if not wrong_name.isascii(): + from unicodedata import normalize + normalized_name = normalize('NFKC', wrong_name) + if normalized_name != wrong_name: + not_normalized = True + wrong_name = normalized_name if isinstance(exc_value, AttributeError): obj = exc_value.obj try: - try: - d = dir(obj) - except TypeError: # Attributes are unsortable, e.g. int and str - d = list(obj.__class__.__dict__.keys()) + list(obj.__dict__.keys()) - d = sorted([x for x in d if isinstance(x, str)]) + d = _get_safe___dir__(obj) hide_underscored = (wrong_name[:1] != '_') if hide_underscored and tb is not None: while tb.tb_next is not None: @@ -1622,14 +1904,22 @@ def _compute_suggestion_error(exc_value, tb, wrong_name): d = [x for x in d if x[:1] != '_'] except Exception: return None + elif isinstance(exc_value, ModuleNotFoundError): + try: + if parent_name := wrong_name.rpartition('.')[0]: + parent = importlib.util.find_spec(parent_name) + else: + parent = None + d = [] + for finder in sys.meta_path: + if discover := getattr(finder, 'discover', None): + d += [spec.name for spec in discover(parent)] + except Exception: + return None elif isinstance(exc_value, ImportError): try: mod = __import__(exc_value.name) - try: - d = dir(mod) - except TypeError: # Attributes are unsortable, e.g. int and str - d = list(mod.__dict__.keys()) - d = sorted([x for x in d if isinstance(x, str)]) + d = _get_safe___dir__(mod) if wrong_name[:1] != '_': d = [x for x in d if x[:1] != '_'] except Exception: @@ -1648,6 +1938,8 @@ def _compute_suggestion_error(exc_value, tb, wrong_name): + list(frame.f_builtins) ) d = [x for x in d if isinstance(x, str)] + if not_normalized and wrong_name in d: + return wrong_name # Check first if we are in a method and the instance # has the wrong name as attribute @@ -1660,12 +1952,16 @@ def _compute_suggestion_error(exc_value, tb, wrong_name): if has_wrong_name: return f"self.{wrong_name}" + if not_normalized and wrong_name in d: + return wrong_name try: import _suggestions except ImportError: pass else: - return _suggestions._generate_suggestions(d, wrong_name) + suggestion = _suggestions._generate_suggestions(d, wrong_name) + if suggestion: + return suggestion # Compute closest match @@ -1690,6 +1986,14 @@ def _compute_suggestion_error(exc_value, tb, wrong_name): if not suggestion or current_distance < best_distance: suggestion = possible_name best_distance = current_distance + + # If no direct attribute match found, check for nested attributes + if not suggestion and isinstance(exc_value, AttributeError): + with suppress(Exception): + nested_suggestion = _check_for_nested_attribute(exc_value.obj, wrong_name, d) + if nested_suggestion: + return nested_suggestion + return suggestion @@ -1752,3 +2056,32 @@ def _levenshtein_distance(a, b, max_cost): # Everything in this row is too big, so bail early. return max_cost + 1 return result + + +def _find_incompatible_extension_module(module_name): + import importlib.machinery + import importlib.resources.readers + + if not module_name or not importlib.machinery.EXTENSION_SUFFIXES: + return + + # We assume the last extension is untagged (eg. .so, .pyd)! + # tests.test_traceback.MiscTest.test_find_incompatible_extension_modules + # tests that assumption. + untagged_suffix = importlib.machinery.EXTENSION_SUFFIXES[-1] + # On Windows the debug tag is part of the module file stem, instead of the + # extension (eg. foo_d.pyd), so let's remove it and just look for .pyd. + if os.name == 'nt': + untagged_suffix = untagged_suffix.removeprefix('_d') + + parent, _, child = module_name.rpartition('.') + if parent: + traversable = importlib.resources.files(parent) + else: + traversable = importlib.resources.readers.MultiplexedPath( + *map(pathlib.Path, filter(os.path.isdir, sys.path)) + ) + + for entry in traversable.iterdir(): + if entry.name.startswith(child + '.') and entry.name.endswith(untagged_suffix): + return entry.name diff --git a/Lib/turtle.py b/Lib/turtle.py index e88981d298ad52..b52d681b3af1c3 100644 --- a/Lib/turtle.py +++ b/Lib/turtle.py @@ -565,7 +565,7 @@ def _iscolorstring(self, color): """Check if the string color is a legal Tkinter color string. """ try: - rgb = self.cv.winfo_rgb(color) + self.cv.winfo_rgb(color) ok = True except TK.TclError: ok = False @@ -1214,16 +1214,32 @@ def turtles(self): def bgcolor(self, *args): """Set or return backgroundcolor of the TurtleScreen. - Arguments (if given): a color string or three numbers - in the range 0..colormode or a 3-tuple of such numbers. + Four input formats are allowed: + - bgcolor() + Return the current background color as color specification + string or as a tuple (see example). May be used as input + to another color/pencolor/fillcolor/bgcolor call. + - bgcolor(colorstring) + Set the background color to colorstring, which is a Tk color + specification string, such as "red", "yellow", or "#33cc8c". + - bgcolor((r, g, b)) + Set the background color to the RGB color represented by + the tuple of r, g, and b. Each of r, g, and b must be in + the range 0..colormode, where colormode is either 1.0 or 255 + (see colormode()). + - bgcolor(r, g, b) + Set the background color to the RGB color represented by + r, g, and b. Each of r, g, and b must be in the range + 0..colormode. Example (for a TurtleScreen instance named screen): >>> screen.bgcolor("orange") >>> screen.bgcolor() 'orange' - >>> screen.bgcolor(0.5,0,0.5) + >>> colormode(255) + >>> screen.bgcolor('#800080') >>> screen.bgcolor() - '#800080' + (128.0, 0.0, 128.0) """ if args: color = self._colorstr(args) @@ -1678,7 +1694,7 @@ def forward(self, distance): Example (for a Turtle instance named turtle): >>> turtle.position() - (0.00, 0.00) + (0.00,0.00) >>> turtle.forward(25) >>> turtle.position() (25.00,0.00) @@ -1701,10 +1717,10 @@ def back(self, distance): Example (for a Turtle instance named turtle): >>> turtle.position() - (0.00, 0.00) + (0.00,0.00) >>> turtle.backward(30) >>> turtle.position() - (-30.00, 0.00) + (-30.00,0.00) """ self._go(-distance) @@ -1811,7 +1827,7 @@ def goto(self, x, y=None): Example (for a Turtle instance named turtle): >>> tp = turtle.pos() >>> tp - (0.00, 0.00) + (0.00,0.00) >>> turtle.setpos(60,30) >>> turtle.pos() (60.00,30.00) @@ -1891,7 +1907,7 @@ def distance(self, x, y=None): Example (for a Turtle instance named turtle): >>> turtle.pos() - (0.00, 0.00) + (0.00,0.00) >>> turtle.distance(30,40) 50.0 >>> pen = Turtle() @@ -2230,19 +2246,17 @@ def color(self, *args): Arguments: Several input formats are allowed. - They use 0, 1, 2, or 3 arguments as follows: - - color() - Return the current pencolor and the current fillcolor - as a pair of color specification strings as are returned - by pencolor and fillcolor. - color(colorstring), color((r,g,b)), color(r,g,b) - inputs as in pencolor, set both, fillcolor and pencolor, + They use 0 to 3 arguments as follows: + - color() + Return the current pencolor and the current fillcolor as + a pair of color specification strings or tuples as returned + by pencolor() and fillcolor(). + - color(colorstring), color((r,g,b)), color(r,g,b) + Inputs as in pencolor(), set both, fillcolor and pencolor, to the given value. - color(colorstring1, colorstring2), - color((r1,g1,b1), (r2,g2,b2)) - equivalent to pencolor(colorstring1) and fillcolor(colorstring2) - and analogously, if the other input format is used. + - color(colorstring1, colorstring2), color((r1,g1,b1), (r2,g2,b2)) + Equivalent to pencolor(colorstring1) and fillcolor(colorstring2) + and analogously if the other input format is used. If turtleshape is a polygon, outline and interior of that polygon is drawn with the newly set colors. @@ -2253,9 +2267,9 @@ def color(self, *args): >>> turtle.color() ('red', 'green') >>> colormode(255) - >>> color((40, 80, 120), (160, 200, 240)) + >>> color(('#285078', '#a0c8f0')) >>> color() - ('#285078', '#a0c8f0') + ((40.0, 80.0, 120.0), (160.0, 200.0, 240.0)) """ if args: l = len(args) @@ -2277,28 +2291,32 @@ def pencolor(self, *args): Arguments: Four input formats are allowed: - pencolor() - Return the current pencolor as color specification string, - possibly in hex-number format (see example). - May be used as input to another color/pencolor/fillcolor call. + Return the current pencolor as color specification string or + as a tuple (see example). May be used as input to another + color/pencolor/fillcolor/bgcolor call. - pencolor(colorstring) - s is a Tk color specification string, such as "red" or "yellow" + Set pencolor to colorstring, which is a Tk color + specification string, such as "red", "yellow", or "#33cc8c". - pencolor((r, g, b)) - *a tuple* of r, g, and b, which represent, an RGB color, - and each of r, g, and b are in the range 0..colormode, - where colormode is either 1.0 or 255 + Set pencolor to the RGB color represented by the tuple of + r, g, and b. Each of r, g, and b must be in the range + 0..colormode, where colormode is either 1.0 or 255 (see + colormode()). - pencolor(r, g, b) - r, g, and b represent an RGB color, and each of r, g, and b - are in the range 0..colormode + Set pencolor to the RGB color represented by r, g, and b. + Each of r, g, and b must be in the range 0..colormode. If turtleshape is a polygon, the outline of that polygon is drawn with the newly set pencolor. Example (for a Turtle instance named turtle): >>> turtle.pencolor('brown') - >>> tup = (0.2, 0.8, 0.55) - >>> turtle.pencolor(tup) >>> turtle.pencolor() - '#33cc8c' + 'brown' + >>> colormode(255) + >>> turtle.pencolor('#32c18f') + >>> turtle.pencolor() + (50.0, 193.0, 143.0) """ if args: color = self._colorstr(args) @@ -2315,26 +2333,31 @@ def fillcolor(self, *args): Four input formats are allowed: - fillcolor() Return the current fillcolor as color specification string, - possibly in hex-number format (see example). - May be used as input to another color/pencolor/fillcolor call. + possibly in tuple format (see example). May be used as + input to another color/pencolor/fillcolor/bgcolor call. - fillcolor(colorstring) - s is a Tk color specification string, such as "red" or "yellow" + Set fillcolor to colorstring, which is a Tk color + specification string, such as "red", "yellow", or "#33cc8c". - fillcolor((r, g, b)) - *a tuple* of r, g, and b, which represent, an RGB color, - and each of r, g, and b are in the range 0..colormode, - where colormode is either 1.0 or 255 + Set fillcolor to the RGB color represented by the tuple of + r, g, and b. Each of r, g, and b must be in the range + 0..colormode, where colormode is either 1.0 or 255 (see + colormode()). - fillcolor(r, g, b) - r, g, and b represent an RGB color, and each of r, g, and b - are in the range 0..colormode + Set fillcolor to the RGB color represented by r, g, and b. + Each of r, g, and b must be in the range 0..colormode. If turtleshape is a polygon, the interior of that polygon is drawn with the newly set fillcolor. Example (for a Turtle instance named turtle): >>> turtle.fillcolor('violet') - >>> col = turtle.pencolor() - >>> turtle.fillcolor(col) - >>> turtle.fillcolor(0, .5, 0) + >>> turtle.fillcolor() + 'violet' + >>> colormode(255) + >>> turtle.fillcolor('#ffffff') + >>> turtle.fillcolor() + (255.0, 255.0, 255.0) """ if args: color = self._colorstr(args) @@ -3724,7 +3747,7 @@ def _undo(self, action, data): if action == "rot": angle, degPAU = data self._rotate(-angle*degPAU/self._degreesPerAU) - dummy = self.undobuffer.pop() + self.undobuffer.pop() elif action == "stamp": stitem = data[0] self.clearstamp(stitem) diff --git a/Lib/turtledemo/__main__.py b/Lib/turtledemo/__main__.py index b49c0beab3ccf7..7c2d753f4c3111 100644 --- a/Lib/turtledemo/__main__.py +++ b/Lib/turtledemo/__main__.py @@ -136,7 +136,7 @@ def __init__(self, filename=None): # so that our menu bar appears. subprocess.run( [ - 'osascript', + '/usr/bin/osascript', '-e', 'tell application "System Events"', '-e', 'set frontmost of the first process whose ' 'unix id is {} to true'.format(os.getpid()), diff --git a/Lib/types.py b/Lib/types.py index f96c75b46daba7..6c069591ab26ef 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -76,6 +76,9 @@ def _m(self): pass # CapsuleType cannot be accessed from pure Python, # so there is no fallback definition. + # LazyImportType in pure Python cannot be guaranteed + # without overriding the filter, so there is no fallback. + del sys, _f, _g, _C, _c, _ag, _cell_factory # Not for export @@ -192,18 +195,19 @@ class Baz(list[str]): ... class DynamicClassAttribute: """Route attribute access on a class to __getattr__. - This is a descriptor, used to define attributes that act differently when - accessed through an instance and through a class. Instance access remains - normal, but access to an attribute through a class will be routed to the - class's __getattr__ method; this is done by raising AttributeError. + This is a descriptor, used to define attributes that act differently + when accessed through an instance and through a class. Instance access + remains normal, but access to an attribute through a class will be + routed to the class's __getattr__ method; this is done by raising + AttributeError. - This allows one to have properties active on an instance, and have virtual - attributes on the class with the same name. (Enum used this between Python - versions 3.4 - 3.9 .) + This allows one to have properties active on an instance, and have + virtual attributes on the class with the same name. (Enum used this + between Python versions 3.4 - 3.9 .) - Subclass from this to use a different method of accessing virtual attributes - and still be treated properly by the inspect module. (Enum uses this since - Python 3.10 .) + Subclass from this to use a different method of accessing virtual + attributes and still be treated properly by the inspect module. (Enum + uses this since Python 3.10 .) """ def __init__(self, fget=None, fset=None, fdel=None, doc=None): @@ -276,10 +280,20 @@ def gi_running(self): @property def gi_yieldfrom(self): return self.__wrapped.gi_yieldfrom + @property + def gi_suspended(self): + return self.__wrapped.gi_suspended + @property + def gi_state(self): + return self.__wrapped.gi_state + @property + def cr_state(self): + return self.__wrapped.gi_state.replace('GEN_', 'CORO_') cr_code = gi_code cr_frame = gi_frame cr_running = gi_running cr_await = gi_yieldfrom + cr_suspended = gi_suspended def __next__(self): return next(self.__wrapped) def __iter__(self): diff --git a/Lib/typing.py b/Lib/typing.py index f1455c273d31ca..1579f492003f74 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -5,7 +5,7 @@ * Generic, Protocol, and internal machinery to support generic aliases. All subscripted types like X[int], Union[int, str] are generic aliases. * Various "special forms" that have unique meanings in type annotations: - NoReturn, Never, ClassVar, Self, Concatenate, Unpack, and others. + Any, Never, ClassVar, Self, Concatenate, Unpack, and others. * Classes whose instances can be type arguments to generic classes and functions: TypeVar, ParamSpec, TypeVarTuple. * Public helper functions: get_type_hints, overload, cast, final, and others. @@ -28,6 +28,7 @@ import sys import types from types import GenericAlias +lazy import annotationlib from _typing import ( _idfunc, @@ -126,6 +127,7 @@ 'cast', 'clear_overloads', 'dataclass_transform', + 'disjoint_base', 'evaluate_forward_ref', 'final', 'get_args', @@ -139,8 +141,8 @@ 'Never', 'NewType', 'no_type_check', - 'no_type_check_decorator', 'NoDefault', + 'NoExtraItems', 'NoReturn', 'NotRequired', 'overload', @@ -155,32 +157,24 @@ 'Text', 'TYPE_CHECKING', 'TypeAlias', + 'TypeForm', 'TypeGuard', 'TypeIs', 'TypeAliasType', 'Unpack', ] -class _LazyAnnotationLib: - def __getattr__(self, attr): - global _lazy_annotationlib - import annotationlib - _lazy_annotationlib = annotationlib - return getattr(annotationlib, attr) - -_lazy_annotationlib = _LazyAnnotationLib() - -def _type_convert(arg, module=None, *, allow_special_forms=False): +def _type_convert(arg, module=None, *, allow_special_forms=False, owner=None): """For converting None to type(None), and strings to ForwardRef.""" if arg is None: return type(None) if isinstance(arg, str): - return _make_forward_ref(arg, module=module, is_class=allow_special_forms) + return _make_forward_ref(arg, module=module, is_class=allow_special_forms, owner=owner) return arg -def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=False): +def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=False, owner=None): """Check that the argument is a type, and return it (internal helper). As a special case, accept None and return type(None) instead. Also wrap strings @@ -198,7 +192,7 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms= if is_argument: invalid_generic_forms += (Final,) - arg = _type_convert(arg, module=module, allow_special_forms=allow_special_forms) + arg = _type_convert(arg, module=module, allow_special_forms=allow_special_forms, owner=owner) if (isinstance(arg, _GenericAlias) and arg.__origin__ in invalid_generic_forms): raise TypeError(f"{arg} is not valid as type argument") @@ -253,19 +247,30 @@ def _type_repr(obj): if isinstance(obj, tuple): # Special case for `repr` of types with `ParamSpec`: return '[' + ', '.join(_type_repr(t) for t in obj) + ']' - return _lazy_annotationlib.type_repr(obj) + return annotationlib.type_repr(obj) -def _collect_type_parameters(args, *, enforce_default_ordering: bool = True): +def _collect_type_parameters( + args, + *, + enforce_default_ordering: bool = True, + validate_all: bool = False, +): """Collect all type parameters in args in order of first appearance (lexicographic order). + Having an explicit `Generic` or `Protocol` base class determines + the exact parameter order. + For example:: >>> P = ParamSpec('P') >>> T = TypeVar('T') >>> _collect_type_parameters((T, Callable[P, T])) (~T, ~P) + >>> _collect_type_parameters((list[T], Generic[P, T])) + (~P, ~T) + """ # required type parameter cannot appear after parameter with default default_encountered = False @@ -297,6 +302,17 @@ def _collect_type_parameters(args, *, enforce_default_ordering: bool = True): ' follows type parameter with a default') parameters.append(t) + elif ( + not validate_all + and isinstance(t, _GenericAlias) + and t.__origin__ in (Generic, Protocol) + ): + # If we see explicit `Generic[...]` or `Protocol[...]` base classes, + # we need to just copy them as-is. + # Unless `validate_all` is passed, in this case it means that + # we are doing a validation of `Generic` subclasses, + # then we collect all unique parameters to be able to inspect them. + parameters = t.__parameters__ else: if _is_unpacked_typevartuple(t): type_var_tuple_encountered = True @@ -418,38 +434,31 @@ def _rebuild_generic_alias(alias: GenericAlias, args: tuple[object, ...]) -> Gen return t -def _deprecation_warning_for_no_type_params_passed(funcname: str) -> None: - import warnings - - depr_message = ( - f"Failing to pass a value to the 'type_params' parameter " - f"of {funcname!r} is deprecated, as it leads to incorrect behaviour " - f"when calling {funcname} on a stringified annotation " - f"that references a PEP 695 type parameter. " - f"It will be disallowed in Python 3.15." - ) - warnings.warn(depr_message, category=DeprecationWarning, stacklevel=3) - - -class _Sentinel: - __slots__ = () - def __repr__(self): - return '<sentinel>' - - def _eval_type(t, globalns, localns, type_params, *, recursive_guard=frozenset(), - format=None, owner=None, parent_fwdref=None): + format=None, owner=None, parent_fwdref=None, prefer_fwd_module=False): """Evaluate all forward references in the given type t. For use of globalns and localns see the docstring for get_type_hints(). recursive_guard is used to prevent infinite recursion with a recursive ForwardRef. """ - if isinstance(t, _lazy_annotationlib.ForwardRef): + if isinstance(t, annotationlib.ForwardRef): # If the forward_ref has __forward_module__ set, evaluate() infers the globals # from the module, and it will probably pick better than the globals we have here. - if t.__forward_module__ is not None: + # We do this only for calls from get_type_hints() (which opts in through the + # prefer_fwd_module flag), so that the default behavior remains more straightforward. + if prefer_fwd_module and t.__forward_module__ is not None: globalns = None + # If there are type params on the owner, we need to add them back, because + # annotationlib won't. + if owner_type_params := getattr(owner, "__type_params__", None): + globalns = getattr( + sys.modules.get(t.__forward_module__, None), "__dict__", None + ) + if globalns is not None: + globalns = dict(globalns) + for type_param in owner_type_params: + globalns[type_param.__name__] = type_param return evaluate_forward_ref(t, globals=globalns, locals=localns, type_params=type_params, owner=owner, _recursive_guard=recursive_guard, format=format) @@ -465,7 +474,7 @@ def _eval_type(t, globalns, localns, type_params, *, recursive_guard=frozenset() ev_args = tuple( _eval_type( a, globalns, localns, type_params, recursive_guard=recursive_guard, - format=format, owner=owner, + format=format, owner=owner, prefer_fwd_module=prefer_fwd_module, ) for a in args ) @@ -560,6 +569,13 @@ def __getitem__(self, parameters): return self._getitem(self, *parameters) +class _TypeFormForm(_SpecialForm, _root=True): + # TypeForm(X) is equivalent to X but indicates to the type checker + # that the object is a TypeForm. + def __call__(self, obj, /): + return obj + + class _AnyMeta(type): def __instancecheck__(self, obj): if self is Any: @@ -575,12 +591,12 @@ def __repr__(self): class Any(metaclass=_AnyMeta): """Special type indicating an unconstrained type. - - Any is compatible with every type. - - Any assumed to have all methods. - - All values assumed to be instances of Any. + - Any is assignable to every type. + - Any assumed to have all methods and attributes. + - All values are assignable to Any. Note that all the above statements are true from the point of view of - static type checkers. At runtime, Any should not be used with instance + static type checkers. At runtime, Any cannot be used with instance checks. """ @@ -699,7 +715,7 @@ class Starship: ClassVar accepts only types and cannot be further subscribed. - Note that ClassVar is not a class itself, and should not + Note that ClassVar is not a class itself, and cannot be used with isinstance() or issubclass(). """ item = _type_check(parameters, f'{self} accepts only single type.', allow_special_forms=True) @@ -729,7 +745,7 @@ class FastConnector(Connection): @_SpecialForm def Optional(self, parameters): - """Optional[X] is equivalent to Union[X, None].""" + """Optional[X] is equivalent to X | None.""" arg = _type_check(parameters, f"{self} requires a single type.") return Union[arg, type(None)] @@ -772,7 +788,7 @@ def open_helper(file: str, mode: MODE) -> str: def TypeAlias(self, parameters): """Special form for marking type aliases. - Use TypeAlias to indicate that an assignment should + TypeAlias can be used to indicate that an assignment should be recognized as a proper type alias definition by type checkers. @@ -867,6 +883,31 @@ def func1(val: list[object]): return _GenericAlias(self, (item,)) +@_TypeFormForm +def TypeForm(self, parameters): + """A special form representing the value that results from the evaluation + of a type expression. + + This value encodes the information supplied in the type expression, and it + represents the type described by that type expression. + + When used in a type expression, TypeForm describes a set of type form + objects. It accepts a single type argument, which must be a valid type + expression. ``TypeForm[T]`` describes the set of all type form objects that + represent the type T or types that are assignable to T. + + Usage:: + + def cast[T](typ: TypeForm[T], value: Any) -> T: ... + + reveal_type(cast(int, "x")) # int + + See PEP 747 for more information. + """ + item = _type_check(parameters, f'{self} accepts only single type.') + return _GenericAlias(self, (item,)) + + @_SpecialForm def TypeIs(self, parameters): """Special typing construct for marking user-defined type predicate functions. @@ -942,7 +983,7 @@ def _make_forward_ref(code, *, parent_fwdref=None, **kwargs): kwargs['module'] = parent_fwdref.__forward_module__ if parent_fwdref.__owner__ is not None: kwargs['owner'] = parent_fwdref.__owner__ - forward_ref = _lazy_annotationlib.ForwardRef(code, **kwargs) + forward_ref = annotationlib.ForwardRef(code, **kwargs) # For compatibility, eagerly compile the forwardref's code. forward_ref.__forward_code__ return forward_ref @@ -977,18 +1018,18 @@ def evaluate_forward_ref( VALUE. """ - if format == _lazy_annotationlib.Format.STRING: + if format == annotationlib.Format.STRING: return forward_ref.__forward_arg__ if forward_ref.__forward_arg__ in _recursive_guard: return forward_ref if format is None: - format = _lazy_annotationlib.Format.VALUE + format = annotationlib.Format.VALUE value = forward_ref.evaluate(globals=globals, locals=locals, type_params=type_params, owner=owner, format=format) - if (isinstance(value, _lazy_annotationlib.ForwardRef) - and format == _lazy_annotationlib.Format.FORWARDREF): + if (isinstance(value, annotationlib.ForwardRef) + and format == annotationlib.Format.FORWARDREF): return value if isinstance(value, str): @@ -1011,8 +1052,10 @@ def evaluate_forward_ref( def _is_unpacked_typevartuple(x: Any) -> bool: + # Need to check 'is True' here + # See: https://github.com/python/cpython/issues/137706 return ((not isinstance(x, type)) and - getattr(x, '__typing_is_unpacked_typevartuple__', False)) + getattr(x, '__typing_is_unpacked_typevartuple__', False) is True) def _is_typevar_like(x: Any) -> bool: @@ -1082,7 +1125,7 @@ def _paramspec_prepare_subst(self, alias, args): params = alias.__parameters__ i = params.index(self) if i == len(args) and self.has_default(): - args = [*args, self.__default__] + args = (*args, self.__default__) if i >= len(args): raise TypeError(f"Too few arguments for {alias}") # Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612. @@ -1127,14 +1170,26 @@ def _generic_class_getitem(cls, args): f"Parameters to {cls.__name__}[...] must all be unique") else: # Subscripting a regular Generic subclass. - for param in cls.__parameters__: + try: + parameters = cls.__parameters__ + except AttributeError as e: + init_subclass = getattr(cls, '__init_subclass__', None) + if init_subclass not in {None, Generic.__init_subclass__}: + e.add_note( + f"Note: this exception may have been caused by " + f"{init_subclass.__qualname__!r} (or the " + f"'__init_subclass__' method on a superclass) not " + f"calling 'super().__init_subclass__()'" + ) + raise + for param in parameters: prepare = getattr(param, '__typing_prepare_subst__', None) if prepare is not None: args = prepare(cls, args) _check_generic_specialization(cls, args) new_args = [] - for param, new_arg in zip(cls.__parameters__, args): + for param, new_arg in zip(parameters, args): if isinstance(param, TypeVarTuple): new_args.extend(new_arg) else: @@ -1156,20 +1211,22 @@ def _generic_init_subclass(cls, *args, **kwargs): if error: raise TypeError("Cannot inherit from plain Generic") if '__orig_bases__' in cls.__dict__: - tvars = _collect_type_parameters(cls.__orig_bases__) + tvars = _collect_type_parameters(cls.__orig_bases__, validate_all=True) # Look for Generic[T1, ..., Tn]. # If found, tvars must be a subset of it. # If not found, tvars is it. # Also check for and reject plain Generic, # and reject multiple Generic[...]. gvars = None + basename = None for base in cls.__orig_bases__: if (isinstance(base, _GenericAlias) and - base.__origin__ is Generic): + base.__origin__ in (Generic, Protocol)): if gvars is not None: raise TypeError( "Cannot inherit from Generic[...] multiple times.") gvars = base.__parameters__ + basename = base.__origin__.__name__ if gvars is not None: tvarset = set(tvars) gvarset = set(gvars) @@ -1177,7 +1234,7 @@ def _generic_init_subclass(cls, *args, **kwargs): s_vars = ', '.join(str(t) for t in tvars if t not in gvarset) s_args = ', '.join(str(g) for g in gvars) raise TypeError(f"Some type variables ({s_vars}) are" - f" not listed in Generic[{s_args}]") + f" not listed in {basename}[{s_args}]") tvars = gvars cls.__parameters__ = tuple(tvars) @@ -1286,31 +1343,35 @@ def __dir__(self): class _GenericAlias(_BaseGenericAlias, _root=True): - # The type of parameterized generics. - # - # That is, for example, `type(List[int])` is `_GenericAlias`. - # - # Objects which are instances of this class include: - # * Parameterized container types, e.g. `Tuple[int]`, `List[int]`. - # * Note that native container types, e.g. `tuple`, `list`, use - # `types.GenericAlias` instead. - # * Parameterized classes: - # class C[T]: pass - # # C[int] is a _GenericAlias - # * `Callable` aliases, generic `Callable` aliases, and - # parameterized `Callable` aliases: - # T = TypeVar('T') - # # _CallableGenericAlias inherits from _GenericAlias. - # A = Callable[[], None] # _CallableGenericAlias - # B = Callable[[T], None] # _CallableGenericAlias - # C = B[int] # _CallableGenericAlias - # * Parameterized `Final`, `ClassVar`, `TypeGuard`, and `TypeIs`: - # # All _GenericAlias - # Final[int] - # ClassVar[float] - # TypeGuard[bool] - # TypeIs[range] - + """The type of parameterized generics. + + That is, for example, `type(List[int])` is `_GenericAlias`. + + Objects which are instances of this class include: + * Parameterized container types, e.g. `Tuple[int]`, `List[int]`. + * Note that native container types, e.g. `tuple`, `list`, use + `types.GenericAlias` instead. + * Parameterized classes: + class C[T]: pass + # C[int] is a _GenericAlias + * `Callable` aliases, generic `Callable` aliases, and + parameterized `Callable` aliases: + T = TypeVar('T') + # _CallableGenericAlias inherits from _GenericAlias. + A = Callable[[], None] # _CallableGenericAlias + B = Callable[[T], None] # _CallableGenericAlias + C = B[int] # _CallableGenericAlias + * Parameterized `Final`, `ClassVar`, `TypeForm`, `TypeGuard`, and `TypeIs`: + # All _GenericAlias + Final[int] + ClassVar[float] + TypeForm[bytearray] + TypeGuard[bool] + TypeIs[range] + + Note that instances of this class are not classes (e.g by `inspect.isclass`), + even though they behave like them. + """ def __init__(self, origin, args, *, inst=True, name=None): super().__init__(origin, inst=inst, name=name) if not isinstance(args, tuple): @@ -1342,20 +1403,21 @@ def __ror__(self, left): @_tp_cache def __getitem__(self, args): - # Parameterizes an already-parameterized object. - # - # For example, we arrive here doing something like: - # T1 = TypeVar('T1') - # T2 = TypeVar('T2') - # T3 = TypeVar('T3') - # class A(Generic[T1]): pass - # B = A[T2] # B is a _GenericAlias - # C = B[T3] # Invokes _GenericAlias.__getitem__ - # - # We also arrive here when parameterizing a generic `Callable` alias: - # T = TypeVar('T') - # C = Callable[[T], None] - # C[int] # Invokes _GenericAlias.__getitem__ + """Parameterizes an already-parameterized object. + + For example, we arrive here doing something like: + T1 = TypeVar('T1') + T2 = TypeVar('T2') + T3 = TypeVar('T3') + class A(Generic[T1]): pass + B = A[T2] # B is a _GenericAlias + C = B[T3] # Invokes _GenericAlias.__getitem__ + + We also arrive here when parameterizing a generic `Callable` alias: + T = TypeVar('T') + C = Callable[[T], None] + C[int] # Invokes _GenericAlias.__getitem__ + """ if self.__origin__ in (Generic, Protocol): # Can't subscript Generic[...] or Protocol[...]. @@ -1372,20 +1434,20 @@ def __getitem__(self, args): return r def _determine_new_args(self, args): - # Determines new __args__ for __getitem__. - # - # For example, suppose we had: - # T1 = TypeVar('T1') - # T2 = TypeVar('T2') - # class A(Generic[T1, T2]): pass - # T3 = TypeVar('T3') - # B = A[int, T3] - # C = B[str] - # `B.__args__` is `(int, T3)`, so `C.__args__` should be `(int, str)`. - # Unfortunately, this is harder than it looks, because if `T3` is - # anything more exotic than a plain `TypeVar`, we need to consider - # edge cases. - + """Determines new __args__ for __getitem__. + + For example, suppose we had: + T1 = TypeVar('T1') + T2 = TypeVar('T2') + class A(Generic[T1, T2]): pass + T3 = TypeVar('T3') + B = A[int, T3] + C = B[str] + `B.__args__` is `(int, T3)`, so `C.__args__` should be `(int, str)`. + Unfortunately, this is harder than it looks, because if `T3` is + anything more exotic than a plain `TypeVar`, we need to consider + edge cases. + """ params = self.__parameters__ # In the example above, this would be {T3: str} for param in params: @@ -1518,9 +1580,9 @@ def __init__(self, origin, nparams, *, inst=True, name=None, defaults=()): self._nparams = nparams self._defaults = defaults if origin.__module__ == 'builtins': - self.__doc__ = f'A generic version of {origin.__qualname__}.' + self.__doc__ = f'Deprecated alias to {origin.__qualname__}.' else: - self.__doc__ = f'A generic version of {origin.__module__}.{origin.__qualname__}.' + self.__doc__ = f'Deprecated alias to {origin.__module__}.{origin.__qualname__}.' @_tp_cache def __getitem__(self, params): @@ -1738,7 +1800,7 @@ class Movie(TypedDict): def foo(**kwargs: Unpack[Movie]): ... Note that there is only some runtime checking of this operator. Not - everything the runtime allows may be accepted by static type checkers. + everything the runtime allows is accepted by static type checkers. For more information, see PEPs 646 and 692. """ @@ -1782,6 +1844,7 @@ class _TypingEllipsis: _TYPING_INTERNALS = frozenset({ '__parameters__', '__orig_bases__', '__orig_class__', '_is_protocol', '_is_runtime_protocol', '__protocol_attrs__', + '__typing_is_deprecated_inherited_runtime_protocol__', '__non_callable_proto_members__', '__type_params__', }) @@ -1811,8 +1874,8 @@ def _get_protocol_attrs(cls): annotations = base.__annotations__ except Exception: # Only go through annotationlib to handle deferred annotations if we need to - annotations = _lazy_annotationlib.get_annotations( - base, format=_lazy_annotationlib.Format.FORWARDREF + annotations = annotationlib.get_annotations( + base, format=annotationlib.Format.FORWARDREF ) for attr in (*base.__dict__, *annotations): if not attr.startswith('_abc_') and attr not in EXCLUDED_ATTRIBUTES: @@ -1971,6 +2034,16 @@ def __subclasscheck__(cls, other): "Instance and class checks can only be used with " "@runtime_checkable protocols" ) + if getattr(cls, '__typing_is_deprecated_inherited_runtime_protocol__', False): + # See GH-132604. + import warnings + depr_message = ( + f"{cls!r} isn't explicitly decorated with @runtime_checkable but " + "it is used in issubclass() or isinstance(). Instance and class " + "checks can only be used with @runtime_checkable protocols. " + "This will raise a TypeError in Python 3.20." + ) + warnings.warn(depr_message, category=DeprecationWarning, stacklevel=2) if ( # this attribute is set by @runtime_checkable: cls.__non_callable_proto_members__ @@ -2000,6 +2073,18 @@ def __instancecheck__(cls, instance): raise TypeError("Instance and class checks can only be used with" " @runtime_checkable protocols") + if getattr(cls, '__typing_is_deprecated_inherited_runtime_protocol__', False): + # See GH-132604. + import warnings + + depr_message = ( + f"{cls!r} isn't explicitly decorated with @runtime_checkable but " + "it is used in issubclass() or isinstance(). Instance and class " + "checks can only be used with @runtime_checkable protocols. " + "This will raise a TypeError in Python 3.20." + ) + warnings.warn(depr_message, category=DeprecationWarning, stacklevel=2) + if _abc_instancecheck(cls, instance): return True @@ -2038,8 +2123,8 @@ def _proto_hook(cls, other): try: annos = base.__annotations__ except Exception: - annos = _lazy_annotationlib.get_annotations( - base, format=_lazy_annotationlib.Format.FORWARDREF + annos = annotationlib.get_annotations( + base, format=annotationlib.Format.FORWARDREF ) if attr in annos: break @@ -2092,6 +2177,11 @@ def __init_subclass__(cls, *args, **kwargs): if not cls.__dict__.get('_is_protocol', False): cls._is_protocol = any(b is Protocol for b in cls.__bases__) + # Mark inherited runtime checkability (deprecated). See GH-132604. + if cls._is_protocol and getattr(cls, '_is_runtime_protocol', False): + # This flag is set to False by @runtime_checkable. + cls.__typing_is_deprecated_inherited_runtime_protocol__ = True + # Set (or override) the protocol subclass hook. if '__subclasshook__' not in cls.__dict__: cls.__subclasshook__ = _proto_hook @@ -2221,7 +2311,7 @@ def runtime_checkable(cls): Such protocol can be used with isinstance() and issubclass(). Raise TypeError if applied to a non-protocol class. This allows a simple-minded structural check very similar to - one trick ponies in collections.abc such as Iterable. + one-trick ponies in collections.abc such as Iterable. For example:: @@ -2238,6 +2328,9 @@ def close(self): ... raise TypeError('@runtime_checkable can be only applied to protocol classes,' ' got %r' % cls) cls._is_runtime_protocol = True + # See GH-132604. + if hasattr(cls, '__typing_is_deprecated_inherited_runtime_protocol__'): + cls.__typing_is_deprecated_inherited_runtime_protocol__ = False # PEP 544 prohibits using issubclass() # with protocols that have non-method members. # See gh-113320 for why we compute this attribute here, @@ -2288,8 +2381,8 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False, *, format=None): """Return type hints for an object. - This is often the same as obj.__annotations__, but it handles - forward references encoded as string literals and recursively replaces all + This is often the same as annotationlib.get_annotations(obj) or obj.__annotations__, + but it handles forward references encoded as string literals and recursively replaces all 'Annotated[T, ...]' with 'T' (unless 'include_extras=True'). The argument may be a module, class, method, or function. The annotations @@ -2318,14 +2411,14 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False, """ if getattr(obj, '__no_type_check__', None): return {} - Format = _lazy_annotationlib.Format + Format = annotationlib.Format if format is None: format = Format.VALUE # Classes require a special treatment. if isinstance(obj, type): hints = {} for base in reversed(obj.__mro__): - ann = _lazy_annotationlib.get_annotations(base, format=format) + ann = annotationlib.get_annotations(base, format=format) if format == Format.STRING: hints.update(ann) continue @@ -2342,11 +2435,14 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False, # *base_globals* first rather than *base_locals*. # This only affects ForwardRefs. base_globals, base_locals = base_locals, base_globals + type_params = base.__type_params__ + base_globals, base_locals = _add_type_params_to_scope( + type_params, base_globals, base_locals, True) for name, value in ann.items(): if isinstance(value, str): value = _make_forward_ref(value, is_argument=False, is_class=True) - value = _eval_type(value, base_globals, base_locals, base.__type_params__, - format=format, owner=obj) + value = _eval_type(value, base_globals, base_locals, (), + format=format, owner=obj, prefer_fwd_module=True) if value is None: value = type(None) hints[name] = value @@ -2355,7 +2451,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False, else: return {k: _strip_annotations(t) for k, t in hints.items()} - hints = _lazy_annotationlib.get_annotations(obj, format=format) + hints = annotationlib.get_annotations(obj, format=format) if ( not hints and not isinstance(obj, types.ModuleType) @@ -2373,14 +2469,19 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False, else: nsobj = obj # Find globalns for the unwrapped object. + seen = {id(nsobj)} while hasattr(nsobj, '__wrapped__'): nsobj = nsobj.__wrapped__ + if id(nsobj) in seen: + raise ValueError(f'wrapper loop when unwrapping {obj!r}') + seen.add(id(nsobj)) globalns = getattr(nsobj, '__globals__', {}) if localns is None: localns = globalns elif localns is None: localns = globalns type_params = getattr(obj, "__type_params__", ()) + globalns, localns = _add_type_params_to_scope(type_params, globalns, localns, False) for name, value in hints.items(): if isinstance(value, str): # class-level forward refs were handled above, this must be either @@ -2390,13 +2491,27 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False, is_argument=not isinstance(obj, types.ModuleType), is_class=False, ) - value = _eval_type(value, globalns, localns, type_params, format=format, owner=obj) + value = _eval_type(value, globalns, localns, (), format=format, owner=obj, prefer_fwd_module=True) if value is None: value = type(None) hints[name] = value return hints if include_extras else {k: _strip_annotations(t) for k, t in hints.items()} +# Add type parameters to the globals and locals scope. This is needed for +# compatibility. +def _add_type_params_to_scope(type_params, globalns, localns, is_class): + if not type_params: + return globalns, localns + globalns = dict(globalns) + localns = dict(localns) + for param in type_params: + if not is_class or param.__name__ not in globalns: + globalns[param.__name__] = param + localns.pop(param.__name__, None) + return globalns, localns + + def _strip_annotations(t): """Strip the annotations from a given type.""" if isinstance(t, _AnnotatedAlias): @@ -2479,7 +2594,7 @@ def get_args(tp): def is_typeddict(tp): - """Check if an annotation is a TypedDict class. + """Check if an object is a TypedDict class. For example:: @@ -2560,23 +2675,6 @@ def no_type_check(arg): return arg -def no_type_check_decorator(decorator): - """Decorator to give another decorator the @no_type_check effect. - - This wraps the decorator with something that wraps the decorated - function in @no_type_check. - """ - import warnings - warnings._deprecated("typing.no_type_check_decorator", remove=(3, 15)) - @functools.wraps(decorator) - def wrapped_decorator(*args, **kwds): - func = decorator(*args, **kwds) - func = no_type_check(func) - return func - - return wrapped_decorator - - def _overload_dummy(*args, **kwds): """Helper for @overload to raise when called.""" raise NotImplementedError( @@ -2593,10 +2691,10 @@ def _overload_dummy(*args, **kwds): def overload(func): """Decorator for overloaded functions/methods. - In a stub file, place two or more stub definitions for the same - function in a row, each decorated with @overload. - - For example:: + In a non-stub file, place two or more stub definitions for the same + function in a row, each decorated with @overload, followed + by an implementation. The implementation should *not* + be decorated with @overload:: @overload def utf8(value: None) -> None: ... @@ -2604,10 +2702,11 @@ def utf8(value: None) -> None: ... def utf8(value: bytes) -> bytes: ... @overload def utf8(value: str) -> bytes: ... + def utf8(value): + ... # implementation goes here - In a non-stub file (i.e. a regular .py file), do the same but - follow it with an implementation. The implementation should *not* - be decorated with @overload:: + In a stub file or in an abstract method (for example, in a Protocol definition), + the implementation may be omitted:: @overload def utf8(value: None) -> None: ... @@ -2615,8 +2714,6 @@ def utf8(value: None) -> None: ... def utf8(value: bytes) -> bytes: ... @overload def utf8(value: str) -> bytes: ... - def utf8(value): - ... # implementation goes here The overloads for a function can be retrieved at runtime using the get_overloads() function. @@ -2652,7 +2749,7 @@ def final(f): """Decorator to indicate final methods and final classes. Use this decorator to indicate to type checkers that the decorated - method cannot be overridden, and decorated class cannot be subclassed. + method cannot be overridden, and the decorated class cannot be subclassed. For example:: @@ -2684,6 +2781,29 @@ class Other(Leaf): # Error reported by type checker return f +def disjoint_base(cls): + """This decorator marks a class as a disjoint base. + + Child classes of a disjoint base cannot inherit from other disjoint bases that are + not parent or child classes of the disjoint base. + + For example: + + @disjoint_base + class Disjoint1: pass + + @disjoint_base + class Disjoint2: pass + + class Disjoint3(Disjoint1, Disjoint2): pass # Type checker error + + Type checkers can use knowledge of disjoint bases to detect unreachable code + and determine when two types can overlap. + """ + cls.__disjoint_base__ = True + return cls + + # Some unconstrained type variables. These were initially used by the container types. # They were never meant for export and are now unused, but we keep them around to # avoid breaking compatibility with users who import them. @@ -2694,7 +2814,7 @@ class Other(Leaf): # Error reported by type checker V_co = TypeVar('V_co', covariant=True) # Any type covariant containers. VT_co = TypeVar('VT_co', covariant=True) # Value type covariant containers. T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. -# Internal type variable used for Type[]. +# Internal type bound to class object types. CT_co = TypeVar('CT_co', covariant=True, bound=type) @@ -2782,7 +2902,7 @@ class TeamUser(User): ... And a function that takes a class argument that's a subclass of User and returns an instance of the corresponding class:: - def new_user[U](user_class: Type[U]) -> U: + def new_user[U](user_class: type[U]) -> U: user = user_class() # (Here we could write the user object to a database) return user @@ -2795,7 +2915,7 @@ def new_user[U](user_class: Type[U]) -> U: @runtime_checkable class SupportsInt(Protocol): - """An ABC with one abstract method __int__.""" + """A protocol with one abstract method __int__.""" __slots__ = () @@ -2806,7 +2926,7 @@ def __int__(self) -> int: @runtime_checkable class SupportsFloat(Protocol): - """An ABC with one abstract method __float__.""" + """A protocol with one abstract method __float__.""" __slots__ = () @@ -2817,7 +2937,7 @@ def __float__(self) -> float: @runtime_checkable class SupportsComplex(Protocol): - """An ABC with one abstract method __complex__.""" + """A protocol with one abstract method __complex__.""" __slots__ = () @@ -2828,7 +2948,7 @@ def __complex__(self) -> complex: @runtime_checkable class SupportsBytes(Protocol): - """An ABC with one abstract method __bytes__.""" + """A protocol with one abstract method __bytes__.""" __slots__ = () @@ -2839,7 +2959,7 @@ def __bytes__(self) -> bytes: @runtime_checkable class SupportsIndex(Protocol): - """An ABC with one abstract method __index__.""" + """A protocol with one abstract method __index__.""" __slots__ = () @@ -2850,7 +2970,7 @@ def __index__(self) -> int: @runtime_checkable class SupportsAbs[T](Protocol): - """An ABC with one abstract method __abs__ that is covariant in its return type.""" + """A protocol with one abstract method __abs__ that is covariant in its return type.""" __slots__ = () @@ -2861,7 +2981,7 @@ def __abs__(self) -> T: @runtime_checkable class SupportsRound[T](Protocol): - """An ABC with one abstract method __round__ that is covariant in its return type.""" + """A protocol with one abstract method __round__ that is covariant in its return type.""" __slots__ = () @@ -2882,10 +3002,10 @@ def _make_eager_annotate(types): for key, val in types.items()} def annotate(format): match format: - case _lazy_annotationlib.Format.VALUE | _lazy_annotationlib.Format.FORWARDREF: + case annotationlib.Format.VALUE | annotationlib.Format.FORWARDREF: return checked_types - case _lazy_annotationlib.Format.STRING: - return _lazy_annotationlib.annotations_to_string(types) + case annotationlib.Format.STRING: + return annotationlib.annotations_to_string(types) case _: raise NotImplementedError(format) return annotate @@ -2915,9 +3035,9 @@ def __new__(cls, typename, bases, ns): types = ns["__annotations__"] field_names = list(types) annotate = _make_eager_annotate(types) - elif (original_annotate := _lazy_annotationlib.get_annotate_from_class_namespace(ns)) is not None: - types = _lazy_annotationlib.call_annotate_function( - original_annotate, _lazy_annotationlib.Format.FORWARDREF) + elif (original_annotate := annotationlib.get_annotate_from_class_namespace(ns)) is not None: + types = annotationlib.call_annotate_function( + original_annotate, annotationlib.Format.FORWARDREF) field_names = list(types) # For backward compatibility, type-check all the types at creation time @@ -2925,9 +3045,9 @@ def __new__(cls, typename, bases, ns): _type_check(typ, "field annotation must be a type") def annotate(format): - annos = _lazy_annotationlib.call_annotate_function( + annos = annotationlib.call_annotate_function( original_annotate, format) - if format != _lazy_annotationlib.Format.STRING: + if format != annotationlib.Format.STRING: return {key: _type_check(val, f"field {key} annotation must be a type") for key, val in annos.items()} return annos @@ -2978,7 +3098,7 @@ def annotate(format): def NamedTuple(typename, fields, /): - """Typed version of namedtuple. + """Typed version of collections.namedtuple. Usage:: @@ -2990,8 +3110,8 @@ class Employee(NamedTuple): Employee = collections.namedtuple('Employee', ['name', 'id']) - The resulting class has an extra __annotations__ attribute, giving a - dict that maps field names to types. (The field names are also in + The types for each field name can be retrieved by calling + annotationlib.get_annotations(Employee). (The field names are also in the _fields attribute, which is part of the namedtuple API.) An alternative equivalent functional syntax is also accepted:: @@ -2999,8 +3119,7 @@ class Employee(NamedTuple): """ types = {n: _type_check(t, f"field {n} annotation must be a type") for n, t in fields} - field_names = [n for n, _ in fields] - nt = _make_nmtuple(typename, field_names, _make_eager_annotate(types), module=_caller()) + nt = _make_nmtuple(typename, types, _make_eager_annotate(types), module=_caller()) nt.__orig_bases__ = (NamedTuple,) return nt @@ -3013,6 +3132,9 @@ def _namedtuple_mro_entries(bases): NamedTuple.__mro_entries__ = _namedtuple_mro_entries +NoExtraItems = sentinel("NoExtraItems") + + def _get_typeddict_qualifiers(annotation_type): while True: annotation_origin = get_origin(annotation_type) @@ -3036,18 +3158,21 @@ def _get_typeddict_qualifiers(annotation_type): class _TypedDictMeta(type): - def __new__(cls, name, bases, ns, total=True): + def __new__(cls, name, bases, ns, total=True, closed=None, + extra_items=NoExtraItems): """Create a new typed dict class object. This method is called when TypedDict is subclassed, or when TypedDict is instantiated. This way - TypedDict supports all three syntax forms described in its docstring. + TypedDict classes can be created through both class-based and functional syntax. Subclasses and instances of TypedDict return actual dictionaries. """ for base in bases: if type(base) is not _TypedDictMeta and base is not Generic: raise TypeError('cannot inherit from both a TypedDict type ' 'and a non-TypedDict base class') + if closed is not None and extra_items is not NoExtraItems: + raise TypeError(f"Cannot combine closed={closed!r} and extra_items") if any(issubclass(b, Generic) for b in bases): generic_base = (Generic,) @@ -3064,16 +3189,16 @@ def __new__(cls, name, bases, ns, total=True): if ns_annotations is not None: own_annotate = None own_annotations = ns_annotations - elif (own_annotate := _lazy_annotationlib.get_annotate_from_class_namespace(ns)) is not None: - own_annotations = _lazy_annotationlib.call_annotate_function( - own_annotate, _lazy_annotationlib.Format.FORWARDREF, owner=tp_dict + elif (own_annotate := annotationlib.get_annotate_from_class_namespace(ns)) is not None: + own_annotations = annotationlib.call_annotate_function( + own_annotate, annotationlib.Format.FORWARDREF, owner=tp_dict ) else: own_annotate = None own_annotations = {} msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" own_checked_annotations = { - n: _type_check(tp, msg, module=tp_dict.__module__) + n: _type_check(tp, msg, owner=tp_dict, module=tp_dict.__module__) for n, tp in own_annotations.items() } required_keys = set() @@ -3133,20 +3258,20 @@ def __annotate__(format): base_annotate = base.__annotate__ if base_annotate is None: continue - base_annos = _lazy_annotationlib.call_annotate_function( + base_annos = annotationlib.call_annotate_function( base_annotate, format, owner=base) annos.update(base_annos) if own_annotate is not None: - own = _lazy_annotationlib.call_annotate_function( + own = annotationlib.call_annotate_function( own_annotate, format, owner=tp_dict) - if format != _lazy_annotationlib.Format.STRING: + if format != annotationlib.Format.STRING: own = { n: _type_check(tp, msg, module=tp_dict.__module__) for n, tp in own.items() } - elif format == _lazy_annotationlib.Format.STRING: - own = _lazy_annotationlib.annotations_to_string(own_annotations) - elif format in (_lazy_annotationlib.Format.FORWARDREF, _lazy_annotationlib.Format.VALUE): + elif format == annotationlib.Format.STRING: + own = annotationlib.annotations_to_string(own_annotations) + elif format in (annotationlib.Format.FORWARDREF, annotationlib.Format.VALUE): own = own_checked_annotations else: raise NotImplementedError(format) @@ -3159,6 +3284,8 @@ def __annotate__(format): tp_dict.__readonly_keys__ = frozenset(readonly_keys) tp_dict.__mutable_keys__ = frozenset(mutable_keys) tp_dict.__total__ = total + tp_dict.__closed__ = closed + tp_dict.__extra_items__ = extra_items return tp_dict __call__ = dict # static method @@ -3170,7 +3297,8 @@ def __subclasscheck__(cls, other): __instancecheck__ = __subclasscheck__ -def TypedDict(typename, fields, /, *, total=True): +def TypedDict(typename, fields, /, *, total=True, closed=None, + extra_items=NoExtraItems): """A simple typed namespace. At runtime it is equivalent to a plain dict. TypedDict creates a dictionary type such that a type checker will expect all @@ -3190,14 +3318,22 @@ def TypedDict(typename, fields, /, *, total=True): >>> Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') True - The type info can be accessed via the Point2D.__annotations__ dict, and - the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. + The type info can be accessed by calling annotationlib.get_annotations(Point2D), and + via the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. TypedDict supports an additional equivalent form:: Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) By default, all keys must be present in a TypedDict. It is possible - to override this by specifying totality:: + to override this by using the NotRequired and Required special forms:: + + class Point2D(TypedDict): + x: int # the "x" key must always be present (Required is the default) + y: NotRequired[int] # the "y" key can be omitted + + This means that a Point2D TypedDict can have the "y" key omitted, but the "x" key must be present. + Items are required by default, so the Required special form is not necessary in this example. + In addition, the total argument to the TypedDict function can be used to make all items not required:: class Point2D(TypedDict, total=False): x: int @@ -3206,16 +3342,8 @@ class Point2D(TypedDict, total=False): This means that a Point2D TypedDict can have any of the keys omitted. A type checker is only expected to support a literal False or True as the value of the total argument. True is the default, and makes all items defined in the - class body be required. - - The Required and NotRequired special forms can also be used to mark - individual keys as being required or not required:: - - class Point2D(TypedDict): - x: int # the "x" key must always be present (Required is the default) - y: NotRequired[int] # the "y" key can be omitted - - See PEP 655 for more details on Required and NotRequired. + class body be required. The Required special form can be used to mark individual + keys as required in a total=False TypedDict. The ReadOnly special form can be used to mark individual keys as immutable for type checkers:: @@ -3224,6 +3352,32 @@ class DatabaseUser(TypedDict): id: ReadOnly[int] # the "id" key must not be modified username: str # the "username" key can be changed + The closed argument controls whether the TypedDict allows additional + non-required items during inheritance and assignability checks. + If closed=True, the TypedDict does not allow additional items:: + + Point2D = TypedDict('Point2D', {'x': int, 'y': int}, closed=True) + class Point3D(Point2D): + z: int # Type checker error + + Passing closed=False explicitly requests TypedDict's default open behavior. + If closed is not provided, the behavior is inherited from the superclass. + A type checker is only expected to support a literal False or True as the + value of the closed argument. + + The extra_items argument can instead be used to specify the assignable type + of unknown non-required keys:: + + Point2D = TypedDict('Point2D', {'x': int, 'y': int}, extra_items=int) + class Point3D(Point2D): + z: int # OK + label: str # Type checker error + + The extra_items argument is also inherited through subclassing. It is unset + by default, and it may not be used with the closed argument at the same + time. + + See PEPs 589, 655, 705, and 728 for more information. """ ns = {'__annotations__': dict(fields)} module = _caller() @@ -3231,7 +3385,8 @@ class DatabaseUser(TypedDict): # Setting correct module is necessary to make typed dict classes pickleable. ns['__module__'] = module - td = _TypedDictMeta(typename, (), ns, total=total) + td = _TypedDictMeta(typename, (), ns, total=total, closed=closed, + extra_items=extra_items) td.__orig_bases__ = (TypedDict,) return td @@ -3252,7 +3407,7 @@ class Movie(TypedDict, total=False): year: int m = Movie( - title='The Matrix', # typechecker error if key is omitted + title='The Matrix', # type checker error if key is omitted year=1999, ) @@ -3274,7 +3429,7 @@ class Movie(TypedDict): year: NotRequired[int] m = Movie( - title='The Matrix', # typechecker error if key is omitted + title='The Matrix', # type checker error if key is omitted year=1999, ) """ @@ -3294,7 +3449,7 @@ class Movie(TypedDict): def mutate_movie(m: Movie) -> None: m["year"] = 1992 # allowed - m["title"] = "The Matrix" # typechecker error + m["title"] = "The Matrix" # type checker error There is no runtime checking for this property. """ @@ -3381,8 +3536,8 @@ class IO(Generic[AnyStr]): classes (text vs. binary, read vs. write vs. read/write, append-only, unbuffered). The TextIO and BinaryIO subclasses below capture the distinctions between text vs. binary, which is - pervasive in the interface; however we currently do not offer a - way to track the other distinctions in the type system. + pervasive in the interface. For more precise types, define a custom + Protocol. """ __slots__ = () @@ -3419,7 +3574,7 @@ def isatty(self) -> bool: pass @abstractmethod - def read(self, n: int = -1) -> AnyStr: + def read(self, n: int = -1, /) -> AnyStr: pass @abstractmethod @@ -3427,15 +3582,15 @@ def readable(self) -> bool: pass @abstractmethod - def readline(self, limit: int = -1) -> AnyStr: + def readline(self, limit: int = -1, /) -> AnyStr: pass @abstractmethod - def readlines(self, hint: int = -1) -> list[AnyStr]: + def readlines(self, hint: int = -1, /) -> list[AnyStr]: pass @abstractmethod - def seek(self, offset: int, whence: int = 0) -> int: + def seek(self, offset: int, whence: int = 0, /) -> int: pass @abstractmethod @@ -3447,7 +3602,7 @@ def tell(self) -> int: pass @abstractmethod - def truncate(self, size: int | None = None) -> int: + def truncate(self, size: int | None = None, /) -> int: pass @abstractmethod @@ -3455,11 +3610,11 @@ def writable(self) -> bool: pass @abstractmethod - def write(self, s: AnyStr) -> int: + def write(self, s: AnyStr, /) -> int: pass @abstractmethod - def writelines(self, lines: list[AnyStr]) -> None: + def writelines(self, lines: list[AnyStr], /) -> None: pass @abstractmethod @@ -3467,17 +3622,17 @@ def __enter__(self) -> IO[AnyStr]: pass @abstractmethod - def __exit__(self, type, value, traceback) -> None: + def __exit__(self, type, value, traceback, /) -> None: pass class BinaryIO(IO[bytes]): - """Typed version of the return of open() in binary mode.""" + """Typed approximation of the return of open() in binary mode.""" __slots__ = () @abstractmethod - def write(self, s: bytes | bytearray) -> int: + def write(self, s: bytes | bytearray, /) -> int: pass @abstractmethod @@ -3486,7 +3641,7 @@ def __enter__(self) -> BinaryIO: class TextIO(IO[str]): - """Typed version of the return of open() in text mode.""" + """Typed approximation of the return of open() in text mode.""" __slots__ = () @@ -3553,7 +3708,7 @@ def dataclass_transform( field_specifiers: tuple[type[Any] | Callable[..., Any], ...] = (), **kwargs: Any, ) -> _IdentityCallable: - """Decorator to mark an object as providing dataclass-like behaviour. + """Decorator to mark an object as providing dataclass-like behavior. The decorator can be applied to a function, class, or metaclass. @@ -3713,7 +3868,7 @@ def __getattr__(attr): are only created on-demand here. """ if attr == "ForwardRef": - obj = _lazy_annotationlib.ForwardRef + obj = annotationlib.ForwardRef elif attr in {"Pattern", "Match"}: import re obj = _alias(getattr(re, attr), 1) @@ -3730,6 +3885,48 @@ def __getattr__(attr): ) warnings.warn(depr_message, category=DeprecationWarning, stacklevel=2) obj = _collect_type_parameters + elif attr == "ByteString": + import warnings + + warnings._deprecated( + "typing.ByteString", + message=( + "{name!r} and 'collections.abc.ByteString' are deprecated " + "and slated for removal in Python {remove}" + ), + remove=(3, 17) + ) + + class _DeprecatedGenericAlias(_SpecialGenericAlias, _root=True): + def __init__( + self, origin, nparams, *, removal_version, inst=True, name=None + ): + super().__init__(origin, nparams, inst=inst, name=name) + self._removal_version = removal_version + + def __instancecheck__(self, inst): + import warnings + warnings._deprecated( + f"{self.__module__}.{self._name}", remove=self._removal_version + ) + return super().__instancecheck__(inst) + + def __subclasscheck__(self, cls): + import warnings + warnings._deprecated( + f"{self.__module__}.{self._name}", remove=self._removal_version + ) + return super().__subclasscheck__(cls) + + with warnings.catch_warnings( + action="ignore", category=DeprecationWarning + ): + # Not generic + ByteString = globals()["ByteString"] = _DeprecatedGenericAlias( + collections.abc.ByteString, 0, removal_version=(3, 17) + ) + + return ByteString else: raise AttributeError(f"module {__name__!r} has no attribute {attr!r}") globals()[attr] = obj diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index eba50839cd33ae..a392238c85abfa 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -301,7 +301,7 @@ def __enter__(self): v.__warningregistry__ = {} self.warnings_manager = warnings.catch_warnings(record=True) self.warnings = self.warnings_manager.__enter__() - warnings.simplefilter("always", self.expected) + warnings.simplefilter("always") return self def __exit__(self, exc_type, exc_value, tb): @@ -314,19 +314,44 @@ def __exit__(self, exc_type, exc_value, tb): except AttributeError: exc_name = str(self.expected) first_matching = None + matched = False + non_matching_warnings = [] for m in self.warnings: w = m.message if not isinstance(w, self.expected): + non_matching_warnings.append(m) continue if first_matching is None: first_matching = w if (self.expected_regex is not None and not self.expected_regex.search(str(w))): + non_matching_warnings.append(m) continue + if matched: + continue + matched = True # store warning for later retrieval self.warning = w self.filename = m.filename self.lineno = m.lineno + for m in non_matching_warnings: + module = m.module + module_globals = None + registry = None + if module is not None: + try: + module_globals = vars(sys.modules[module]) + except (KeyError, TypeError): + # module == "<string>" or sys.modules[module] is None + pass + else: + registry = module_globals.setdefault("__warningregistry__", {}) + warnings.warn_explicit(m.message, m.category, m.filename, m.lineno, + module=module, + registry=registry, + module_globals=module_globals, + source=m.source) + if matched: return # Now we simply try to choose a helpful failure message if first_matching is not None: @@ -338,7 +363,6 @@ def __exit__(self, exc_type, exc_value, tb): else: self._raiseFailure("{} not triggered".format(exc_name)) - class _AssertNotWarnsContext(_AssertWarnsContext): def __exit__(self, exc_type, exc_value, tb): diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py index 6fd949581f3146..6eeebf9657a3c7 100644 --- a/Lib/unittest/main.py +++ b/Lib/unittest/main.py @@ -172,7 +172,7 @@ def _getParentArgParser(self): help='Show local variables in tracebacks') parser.add_argument('--durations', dest='durations', type=int, default=None, metavar="N", - help='Show the N slowest test cases (N=0 for all)') + help='Show the `N` slowest test cases (`N=0` for all)') if self.failfast is None: parser.add_argument('-f', '--failfast', dest='failfast', action='store_true', @@ -181,12 +181,12 @@ def _getParentArgParser(self): if self.catchbreak is None: parser.add_argument('-c', '--catch', dest='catchbreak', action='store_true', - help='Catch Ctrl-C and display results so far') + help='Catch `Ctrl-C` and display results so far') self.catchbreak = False if self.buffer is None: parser.add_argument('-b', '--buffer', dest='buffer', action='store_true', - help='Buffer stdout and stderr during tests') + help='Buffer `stdout` and `stderr` during tests') self.buffer = False if self.testNamePatterns is None: parser.add_argument('-k', dest='testNamePatterns', @@ -197,7 +197,7 @@ def _getParentArgParser(self): return parser def _getMainArgParser(self, parent): - parser = argparse.ArgumentParser(parents=[parent], color=True) + parser = argparse.ArgumentParser(parents=[parent]) parser.prog = self.progName parser.print_help = self._print_help @@ -208,16 +208,16 @@ def _getMainArgParser(self, parent): return parser def _getDiscoveryArgParser(self, parent): - parser = argparse.ArgumentParser(parents=[parent], color=True) + parser = argparse.ArgumentParser(parents=[parent]) parser.prog = '%s discover' % self.progName parser.epilog = ('For test discovery all test modules must be ' 'importable from the top level directory of the ' 'project.') parser.add_argument('-s', '--start-directory', dest='start', - help="Directory to start discovery ('.' default)") + help="Directory to start discovery (`.` default)") parser.add_argument('-p', '--pattern', dest='pattern', - help="Pattern to match tests ('test*.py' default)") + help="Pattern to match tests (`test*.py` default)") parser.add_argument('-t', '--top-level-directory', dest='top', help='Top level directory of project (defaults to ' 'start directory)') @@ -269,12 +269,12 @@ def runTests(self): testRunner = self.testRunner self.result = testRunner.run(self.test) if self.exit: - if self.result.testsRun == 0 and len(self.result.skipped) == 0: + if not self.result.wasSuccessful(): + sys.exit(1) + elif self.result.testsRun == 0 and len(self.result.skipped) == 0: sys.exit(_NO_TESTS_EXITCODE) - elif self.result.wasSuccessful(): - sys.exit(0) else: - sys.exit(1) + sys.exit(0) main = TestProgram diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index e1dbfdacf56337..14a490c1657585 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -24,7 +24,6 @@ ) -import asyncio import contextlib import io import inspect @@ -34,6 +33,7 @@ import pkgutil from inspect import iscoroutinefunction import threading +from annotationlib import Format from dataclasses import fields, is_dataclass from types import CodeType, ModuleType, MethodType from unittest.util import safe_repr @@ -119,7 +119,7 @@ def _get_signature_object(func, as_instance, eat_self): else: sig_func = func try: - return func, inspect.signature(sig_func) + return func, inspect.signature(sig_func, annotation_format=Format.FORWARDREF) except ValueError: # Certain callable types are not supported by inspect.signature() return None @@ -280,7 +280,6 @@ def reset_mock(): def _setup_async_mock(mock): - mock._is_coroutine = asyncio.coroutines._is_coroutine mock.await_count = 0 mock.await_args = None mock.await_args_list = _CallList() @@ -700,7 +699,7 @@ def __getattr__(self, name): if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')) or name in _ATTRIB_DENY_LIST: raise AttributeError( f"{name!r} is not a valid assertion. Use a spec " - f"for the mock if {name!r} is meant to be an attribute.") + f"for the mock if {name!r} is meant to be an attribute") with NonCallableMock._lock: result = self._mock_children.get(name) @@ -1180,14 +1179,20 @@ def _mock_call(self, /, *args, **kwargs): def _increment_mock_call(self, /, *args, **kwargs): self.called = True - self.call_count += 1 # handle call_args # needs to be set here so assertions on call arguments pass before # execution in the case of awaited calls - _call = _Call((args, kwargs), two=True) - self.call_args = _call - self.call_args_list.append(_call) + with NonCallableMock._lock: + # Lock is used here so that call_args_list and call_count are + # set atomically otherwise it is possible that by the time call_count + # is set another thread may have appended to call_args_list. + # The rest of this function relies on list.append being atomic and + # skips locking. + _call = _Call((args, kwargs), two=True) + self.call_args = _call + self.call_args_list.append(_call) + self.call_count = len(self.call_args_list) # initial stuff for method_calls: do_method_calls = self._mock_parent is not None @@ -2280,13 +2285,6 @@ class AsyncMockMixin(Base): def __init__(self, /, *args, **kwargs): super().__init__(*args, **kwargs) - # iscoroutinefunction() checks _is_coroutine property to say if an - # object is a coroutine. Without this check it looks to see if it is a - # function/method, which in this case it is not (since it is an - # AsyncMock). - # It is set through __dict__ because when spec_set is True, this - # attribute is likely undefined. - self.__dict__['_is_coroutine'] = asyncio.coroutines._is_coroutine self.__dict__['_mock_await_count'] = 0 self.__dict__['_mock_await_args'] = None self.__dict__['_mock_await_args_list'] = _CallList() @@ -3114,6 +3112,10 @@ def _mock_call(self, *args, **kwargs): return ret_value + def _increment_mock_call(self, /, *args, **kwargs): + with self._mock_calls_events_lock: + super()._increment_mock_call(*args, **kwargs) + def wait_until_called(self, *, timeout=_timeout_unset): """Wait until the mock object is called. diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py index 5f22d91aebd05f..893fcba968c3ef 100644 --- a/Lib/unittest/runner.py +++ b/Lib/unittest/runner.py @@ -4,11 +4,10 @@ import time import warnings -from _colorize import get_theme - from . import result from .case import _SubTest from .signals import registerResult +lazy from _colorize import get_theme __unittest = True diff --git a/Lib/unittest/util.py b/Lib/unittest/util.py index 050eaed0b3f58f..0681163c979587 100644 --- a/Lib/unittest/util.py +++ b/Lib/unittest/util.py @@ -1,7 +1,6 @@ """Various utility functions.""" from collections import namedtuple, Counter -from os.path import commonprefix __unittest = True @@ -21,13 +20,23 @@ def _shorten(s, prefixlen, suffixlen): s = '%s[%d chars]%s' % (s[:prefixlen], skip, s[len(s) - suffixlen:]) return s +def _common_prefix(m): + if not m: + return "" + s1 = min(m) + s2 = max(m) + for i, c in enumerate(s1): + if c != s2[i]: + return s1[:i] + return s1 + def _common_shorten_repr(*args): args = tuple(map(safe_repr, args)) maxlen = max(map(len, args)) if maxlen <= _MAX_LENGTH: return args - prefix = commonprefix(args) + prefix = _common_prefix(args) prefixlen = len(prefix) common_len = _MAX_LENGTH - \ @@ -54,6 +63,14 @@ def safe_repr(obj, short=False): def strclass(cls): return "%s.%s" % (cls.__module__, cls.__qualname__) +def _dedupe_sorted(lst): + """Remove consecutive duplicate elements from a sorted list.""" + result = [] + for item in lst: + if not result or result[-1] != item: + result.append(item) + return result + def sorted_list_difference(expected, actual): """Finds elements in only one or the other of two, sorted input lists. @@ -89,8 +106,8 @@ def sorted_list_difference(expected, actual): while actual[j] == a: j += 1 except IndexError: - missing.extend(expected[i:]) - unexpected.extend(actual[j:]) + missing.extend(_dedupe_sorted(expected[i:])) + unexpected.extend(_dedupe_sorted(actual[j:])) break return missing, unexpected diff --git a/Lib/urllib/error.py b/Lib/urllib/error.py index a9cd1ecadd63f2..beb32d0df48610 100644 --- a/Lib/urllib/error.py +++ b/Lib/urllib/error.py @@ -18,7 +18,7 @@ class URLError(OSError): # URLError is a sub-type of OSError, but it doesn't share any of - # the implementation. need to override __init__ and __str__. + # the implementation. It overrides __init__ and __str__. # It sets self.args for compatibility with other OSError # subclasses, but args doesn't have the typical format with errno in # slot 0 and strerror in slot 1. This may be better than nothing. diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py index 67d9bbea0d3150..82b95adbdc283e 100644 --- a/Lib/urllib/parse.py +++ b/Lib/urllib/parse.py @@ -1,6 +1,6 @@ """Parse (absolute and relative) URLs. -urlparse module is based upon the following RFC specifications. +urllib.parse module is based upon the following RFC specifications. RFC 3986 (STD66): "Uniform Resource Identifiers" by T. Berners-Lee, R. Fielding and L. Masinter, January 2005. @@ -20,7 +20,7 @@ McCahill, December 1994 RFC 3986 is considered the current standard and any future changes to -urlparse module should conform with it. The urlparse module is +urllib.parse module should conform with it. The urllib.parse module is currently not entirely compliant with this RFC due to defacto scenarios for parsing, and for backward compatibility purposes, some parsing quirks from older RFCs are retained. The testcases in @@ -97,11 +97,9 @@ def clear_cache(): _byte_quoter_factory.cache_clear() # Helpers for bytes handling -# For 3.2, we deliberately require applications that -# handle improperly quoted URLs to do their own -# decoding and encoding. If valid use cases are -# presented, we may relax this by using latin-1 -# decoding internally for 3.3 +# We deliberately require applications that +# handle improperly quoted URLs to do their +# own decoding and encoding. _implicit_encoding = 'ascii' _implicit_errors = 'strict' @@ -114,7 +112,10 @@ def _encode_result(obj, encoding=_implicit_encoding, def _decode_args(args, encoding=_implicit_encoding, errors=_implicit_errors): - return tuple(x.decode(encoding, errors) if x else '' for x in args) + return tuple(x.decode(encoding, errors) if x + else '' if x is not None + else None + for x in args) def _coerce_args(*args): # Invokes decode if necessary to create str args @@ -122,13 +123,20 @@ def _coerce_args(*args): # an appropriate result coercion function # - noop for str inputs # - encoding function otherwise - str_input = isinstance(args[0], str) - for arg in args[1:]: - # We special-case the empty string to support the - # "scheme=''" default argument to some functions - if arg and isinstance(arg, str) != str_input: - raise TypeError("Cannot mix str and non-str arguments") - if str_input: + str_input = None + for arg in args: + if arg: + if str_input is None: + str_input = isinstance(arg, str) + else: + if isinstance(arg, str) != str_input: + raise TypeError("Cannot mix str and non-str arguments") + if str_input is None: + for arg in args: + if arg is not None: + str_input = isinstance(arg, str) + break + if str_input is not False: return args + (_noop,) return _decode_args(args) + (_encode_result,) @@ -138,7 +146,14 @@ class _ResultMixinStr(object): __slots__ = () def encode(self, encoding='ascii', errors='strict'): - return self._encoded_counterpart(*(x.encode(encoding, errors) for x in self)) + result = self._encoded_counterpart(*(x.encode(encoding, errors) + if x is not None else None + for x in self)) + try: + result._keep_empty = self._keep_empty + except AttributeError: + pass + return result class _ResultMixinBytes(object): @@ -146,7 +161,14 @@ class _ResultMixinBytes(object): __slots__ = () def decode(self, encoding='ascii', errors='strict'): - return self._decoded_counterpart(*(x.decode(encoding, errors) for x in self)) + result = self._decoded_counterpart(*(x.decode(encoding, errors) + if x is not None else None + for x in self)) + try: + result._keep_empty = self._keep_empty + except AttributeError: + pass + return result class _NetlocResultMixinBase(object): @@ -193,6 +215,8 @@ class _NetlocResultMixinStr(_NetlocResultMixinBase, _ResultMixinStr): @property def _userinfo(self): netloc = self.netloc + if netloc is None: + return None, None userinfo, have_info, hostinfo = netloc.rpartition('@') if have_info: username, have_password, password = userinfo.partition(':') @@ -205,6 +229,8 @@ def _userinfo(self): @property def _hostinfo(self): netloc = self.netloc + if netloc is None: + return None, None _, _, hostinfo = netloc.rpartition('@') _, have_open_br, bracketed = hostinfo.partition('[') if have_open_br: @@ -223,6 +249,8 @@ class _NetlocResultMixinBytes(_NetlocResultMixinBase, _ResultMixinBytes): @property def _userinfo(self): netloc = self.netloc + if netloc is None: + return None, None userinfo, have_info, hostinfo = netloc.rpartition(b'@') if have_info: username, have_password, password = userinfo.partition(b':') @@ -235,6 +263,8 @@ def _userinfo(self): @property def _hostinfo(self): netloc = self.netloc + if netloc is None: + return None, None _, _, hostinfo = netloc.rpartition(b'@') _, have_open_br, bracketed = hostinfo.partition(b'[') if have_open_br: @@ -247,11 +277,70 @@ def _hostinfo(self): return hostname, port -_DefragResultBase = namedtuple('_DefragResultBase', 'url fragment') -_SplitResultBase = namedtuple( - '_SplitResultBase', 'scheme netloc path query fragment') -_ParseResultBase = namedtuple( - '_ParseResultBase', 'scheme netloc path params query fragment') +_UNSPECIFIED = sentinel("_UNSPECIFIED", repr="<not specified>") +_MISSING_AS_NONE_DEFAULT = False + +class _ResultBase: + __slots__ = () + + def __replace__(self, /, **kwargs): + result = super().__replace__(**kwargs) + try: + result._keep_empty = self._keep_empty + except AttributeError: + pass + return result + + def _replace(self, /, **kwargs): + result = super()._replace(**kwargs) + try: + result._keep_empty = self._keep_empty + except AttributeError: + pass + return result + + def __copy__(self): + return self + + def __deepcopy__(self, memo): + return self + + def __getstate__(self): + state = super().__getstate__() + try: + if state[1]['_keep_empty'] == _MISSING_AS_NONE_DEFAULT: + del state[1]['_keep_empty'] + if state == (None, {}): + state = None + except LookupError: + pass + return state + + +class _DefragResultBase(_ResultBase, namedtuple('_DefragResultBase', 'url fragment')): + __slots__ = ('_keep_empty',) + + def geturl(self): + if self.fragment or (self.fragment is not None and + getattr(self, '_keep_empty', _MISSING_AS_NONE_DEFAULT)): + return self.url + self._HASH + self.fragment + else: + return self.url + +class _SplitResultBase(_ResultBase, namedtuple( + '_SplitResultBase', 'scheme netloc path query fragment')): + __slots__ = ('_keep_empty',) + + def geturl(self): + return urlunsplit(self) + +class _ParseResultBase(_ResultBase, namedtuple( + '_ParseResultBase', 'scheme netloc path params query fragment')): + __slots__ = ('_keep_empty',) + + def geturl(self): + return urlunparse(self) + _DefragResultBase.__doc__ = """ DefragResult(url, fragment) @@ -322,40 +411,24 @@ def _hostinfo(self): # Structured result objects for string data class DefragResult(_DefragResultBase, _ResultMixinStr): __slots__ = () - def geturl(self): - if self.fragment: - return self.url + '#' + self.fragment - else: - return self.url + _HASH = '#' class SplitResult(_SplitResultBase, _NetlocResultMixinStr): __slots__ = () - def geturl(self): - return urlunsplit(self) class ParseResult(_ParseResultBase, _NetlocResultMixinStr): __slots__ = () - def geturl(self): - return urlunparse(self) # Structured result objects for bytes data class DefragResultBytes(_DefragResultBase, _ResultMixinBytes): __slots__ = () - def geturl(self): - if self.fragment: - return self.url + b'#' + self.fragment - else: - return self.url + _HASH = b'#' class SplitResultBytes(_SplitResultBase, _NetlocResultMixinBytes): __slots__ = () - def geturl(self): - return urlunsplit(self) class ParseResultBytes(_ParseResultBase, _NetlocResultMixinBytes): __slots__ = () - def geturl(self): - return urlunparse(self) # Set up the encode/decode result pairs def _fix_result_transcoding(): @@ -371,7 +444,7 @@ def _fix_result_transcoding(): _fix_result_transcoding() del _fix_result_transcoding -def urlparse(url, scheme='', allow_fragments=True): +def urlparse(url, scheme=None, allow_fragments=True, *, missing_as_none=_MISSING_AS_NONE_DEFAULT): """Parse a URL into 6 components: <scheme>://<netloc>/<path>;<params>?<query>#<fragment> @@ -390,25 +463,37 @@ def urlparse(url, scheme='', allow_fragments=True): path or query. Note that % escapes are not expanded. + + urlsplit() should generally be used instead of urlparse(). """ url, scheme, _coerce_result = _coerce_args(url, scheme) + if url is None: + url = '' scheme, netloc, url, params, query, fragment = _urlparse(url, scheme, allow_fragments) - result = ParseResult(scheme or '', netloc or '', url, params or '', query or '', fragment or '') - return _coerce_result(result) + if not missing_as_none: + if scheme is None: scheme = '' + if netloc is None: netloc = '' + if params is None: params = '' + if query is None: query = '' + if fragment is None: fragment = '' + result = ParseResult(scheme, netloc, url, params, query, fragment) + result = _coerce_result(result) + result._keep_empty = missing_as_none + return result def _urlparse(url, scheme=None, allow_fragments=True): scheme, netloc, url, query, fragment = _urlsplit(url, scheme, allow_fragments) if (scheme or '') in uses_params and ';' in url: - url, params = _splitparams(url, allow_none=True) + url, params = _splitparams(url, missing_as_none=True) else: params = None return (scheme, netloc, url, params, query, fragment) -def _splitparams(url, allow_none=False): +def _splitparams(url, missing_as_none=False): if '/' in url: i = url.find(';', url.rfind('/')) if i < 0: - return url, None if allow_none else '' + return url, None if missing_as_none else '' else: i = url.find(';') return url[:i], url[i+1:] @@ -470,7 +555,7 @@ def _check_bracketed_host(hostname): # typed=True avoids BytesWarnings being emitted during cache key # comparison since this API supports both bytes and str input. @functools.lru_cache(typed=True) -def urlsplit(url, scheme='', allow_fragments=True): +def urlsplit(url, scheme=None, allow_fragments=True, *, missing_as_none=_MISSING_AS_NONE_DEFAULT): """Parse a URL into 5 components: <scheme>://<netloc>/<path>?<query>#<fragment> @@ -492,9 +577,18 @@ def urlsplit(url, scheme='', allow_fragments=True): """ url, scheme, _coerce_result = _coerce_args(url, scheme) + if url is None: + url = '' scheme, netloc, url, query, fragment = _urlsplit(url, scheme, allow_fragments) - v = SplitResult(scheme or '', netloc or '', url, query or '', fragment or '') - return _coerce_result(v) + if not missing_as_none: + if scheme is None: scheme = '' + if netloc is None: netloc = '' + if query is None: query = '' + if fragment is None: fragment = '' + result = SplitResult(scheme, netloc, url, query, fragment) + result = _coerce_result(result) + result._keep_empty = missing_as_none + return result def _urlsplit(url, scheme=None, allow_fragments=True): # Only lstrip url as some applications rely on preserving trailing space. @@ -530,38 +624,61 @@ def _urlsplit(url, scheme=None, allow_fragments=True): _checknetloc(netloc) return (scheme, netloc, url, query, fragment) -def urlunparse(components): +def urlunparse(components, *, keep_empty=_UNSPECIFIED): """Put a parsed URL back together again. This may result in a slightly different, but equivalent URL, if the URL that was parsed originally had redundant delimiters, e.g. a ? with an empty query - (the draft states that these are equivalent).""" + (the draft states that these are equivalent) and keep_empty is false + or components is the result of the urlparse() call with + missing_as_none=False.""" scheme, netloc, url, params, query, fragment, _coerce_result = ( _coerce_args(*components)) - if not netloc: - if scheme and scheme in uses_netloc and (not url or url[:1] == '/'): - netloc = '' - else: - netloc = None - if params: + if keep_empty is _UNSPECIFIED: + keep_empty = getattr(components, '_keep_empty', _MISSING_AS_NONE_DEFAULT) + elif keep_empty and not getattr(components, '_keep_empty', True): + raise ValueError('Cannot distinguish between empty and not defined ' + 'URI components in the result of parsing URL with ' + 'missing_as_none=False') + if not keep_empty: + if not netloc: + if scheme and scheme in uses_netloc and (not url or url[:1] == '/'): + netloc = '' + else: + netloc = None + if not scheme: scheme = None + if not params: params = None + if not query: query = None + if not fragment: fragment = None + if params is not None: url = "%s;%s" % (url, params) - return _coerce_result(_urlunsplit(scheme or None, netloc, url, - query or None, fragment or None)) + return _coerce_result(_urlunsplit(scheme, netloc, url, query, fragment)) -def urlunsplit(components): +def urlunsplit(components, *, keep_empty=_UNSPECIFIED): """Combine the elements of a tuple as returned by urlsplit() into a complete URL as a string. The data argument can be any five-item iterable. This may result in a slightly different, but equivalent URL, if the URL that was parsed originally had unnecessary delimiters (for example, a ? with an - empty query; the RFC states that these are equivalent).""" + empty query; the RFC states that these are equivalent) and keep_empty + is false or components is the result of the urlsplit() call with + missing_as_none=False.""" scheme, netloc, url, query, fragment, _coerce_result = ( _coerce_args(*components)) - if not netloc: - if scheme and scheme in uses_netloc and (not url or url[:1] == '/'): - netloc = '' - else: - netloc = None - return _coerce_result(_urlunsplit(scheme or None, netloc, url, - query or None, fragment or None)) + if keep_empty is _UNSPECIFIED: + keep_empty = getattr(components, '_keep_empty', _MISSING_AS_NONE_DEFAULT) + elif keep_empty and not getattr(components, '_keep_empty', True): + raise ValueError('Cannot distinguish between empty and not defined ' + 'URI components in the result of parsing URL with ' + 'missing_as_none=False') + if not keep_empty: + if not netloc: + if scheme and scheme in uses_netloc and (not url or url[:1] == '/'): + netloc = '' + else: + netloc = None + if not scheme: scheme = None + if not query: query = None + if not fragment: fragment = None + return _coerce_result(_urlunsplit(scheme, netloc, url, query, fragment)) def _urlunsplit(scheme, netloc, url, query, fragment): if netloc is not None: @@ -649,21 +766,24 @@ def urljoin(base, url, allow_fragments=True): resolved_path) or '/', query, fragment)) -def urldefrag(url): +def urldefrag(url, *, missing_as_none=_MISSING_AS_NONE_DEFAULT): """Removes any existing fragment from URL. Returns a tuple of the defragmented URL and the fragment. If the URL contained no fragments, the second element is the - empty string. + empty string or None if missing_as_none is True. """ url, _coerce_result = _coerce_args(url) if '#' in url: s, n, p, q, frag = _urlsplit(url) defrag = _urlunsplit(s, n, p, q, None) else: - frag = '' + frag = None defrag = url - return _coerce_result(DefragResult(defrag, frag or '')) + if not missing_as_none and frag is None: frag = '' + result = _coerce_result(DefragResult(defrag, frag)) + result._keep_empty = missing_as_none + return result _hexdig = '0123456789ABCDEFabcdef' _hextobyte = None @@ -1064,7 +1184,7 @@ def urlencode(query, doseq=False, safe='', encoding=None, errors=None, else: try: # Is this a sufficient test for sequence-ness? - x = len(v) + len(v) except TypeError: # not a sequence v = quote_via(str(v), safe, encoding, errors) @@ -1115,7 +1235,7 @@ def unwrap(url): def splittype(url): warnings.warn("urllib.parse.splittype() is deprecated as of 3.8, " - "use urllib.parse.urlparse() instead", + "use urllib.parse.urlsplit() instead", DeprecationWarning, stacklevel=2) return _splittype(url) @@ -1136,7 +1256,7 @@ def _splittype(url): def splithost(url): warnings.warn("urllib.parse.splithost() is deprecated as of 3.8, " - "use urllib.parse.urlparse() instead", + "use urllib.parse.urlsplit() instead", DeprecationWarning, stacklevel=2) return _splithost(url) @@ -1159,7 +1279,7 @@ def _splithost(url): def splituser(host): warnings.warn("urllib.parse.splituser() is deprecated as of 3.8, " - "use urllib.parse.urlparse() instead", + "use urllib.parse.urlsplit() instead", DeprecationWarning, stacklevel=2) return _splituser(host) @@ -1172,7 +1292,7 @@ def _splituser(host): def splitpasswd(user): warnings.warn("urllib.parse.splitpasswd() is deprecated as of 3.8, " - "use urllib.parse.urlparse() instead", + "use urllib.parse.urlsplit() instead", DeprecationWarning, stacklevel=2) return _splitpasswd(user) @@ -1185,7 +1305,7 @@ def _splitpasswd(user): def splitport(host): warnings.warn("urllib.parse.splitport() is deprecated as of 3.8, " - "use urllib.parse.urlparse() instead", + "use urllib.parse.urlsplit() instead", DeprecationWarning, stacklevel=2) return _splitport(host) @@ -1208,7 +1328,7 @@ def _splitport(host): def splitnport(host, defport=-1): warnings.warn("urllib.parse.splitnport() is deprecated as of 3.8, " - "use urllib.parse.urlparse() instead", + "use urllib.parse.urlsplit() instead", DeprecationWarning, stacklevel=2) return _splitnport(host, defport) @@ -1232,7 +1352,7 @@ def _splitnport(host, defport=-1): def splitquery(url): warnings.warn("urllib.parse.splitquery() is deprecated as of 3.8, " - "use urllib.parse.urlparse() instead", + "use urllib.parse.urlsplit() instead", DeprecationWarning, stacklevel=2) return _splitquery(url) @@ -1247,7 +1367,7 @@ def _splitquery(url): def splittag(url): warnings.warn("urllib.parse.splittag() is deprecated as of 3.8, " - "use urllib.parse.urlparse() instead", + "use urllib.parse.urlsplit() instead", DeprecationWarning, stacklevel=2) return _splittag(url) @@ -1262,7 +1382,7 @@ def _splittag(url): def splitattr(url): warnings.warn("urllib.parse.splitattr() is deprecated as of 3.8, " - "use urllib.parse.urlparse() instead", + "use urllib.parse.urlsplit() instead", DeprecationWarning, stacklevel=2) return _splitattr(url) diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index c1c373d08815c1..f5f17f223a4585 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -415,6 +415,8 @@ def add_handler(self, handler): continue i = meth.find("_") + if i < 1: + continue protocol = meth[:i] condition = meth[i+1:] @@ -1535,6 +1537,7 @@ def ftp_open(self, req): dirs, file = dirs[:-1], dirs[-1] if dirs and not dirs[0]: dirs = dirs[1:] + fw = None try: fw = self.connect_ftp(user, passwd, host, port, dirs, req.timeout) type = file and 'I' or 'D' @@ -1552,8 +1555,12 @@ def ftp_open(self, req): headers += "Content-length: %d\n" % retrlen headers = email.message_from_string(headers) return addinfourl(fp, headers, req.full_url) - except ftplib.all_errors as exp: - raise URLError(f"ftp error: {exp}") from exp + except Exception as exp: + if fw is not None and not fw.keepalive: + fw.close() + if isinstance(exp, ftplib.all_errors): + raise URLError(f"ftp error: {exp}") from exp + raise def connect_ftp(self, user, passwd, host, port, dirs, timeout): return ftpwrapper(user, passwd, host, port, dirs, timeout, @@ -1577,14 +1584,15 @@ def setMaxConns(self, m): def connect_ftp(self, user, passwd, host, port, dirs, timeout): key = user, host, port, '/'.join(dirs), timeout - if key in self.cache: - self.timeout[key] = time.time() + self.delay - else: - self.cache[key] = ftpwrapper(user, passwd, host, port, - dirs, timeout) - self.timeout[key] = time.time() + self.delay + conn = self.cache.get(key) + if conn is None or not conn.keepalive: + if conn is not None: + conn.close() + conn = self.cache[key] = ftpwrapper(user, passwd, host, port, + dirs, timeout) + self.timeout[key] = time.time() + self.delay self.check_cache() - return self.cache[key] + return conn def check_cache(self): # first check for old ones @@ -1628,6 +1636,11 @@ def data_open(self, req): scheme, data = url.split(":",1) mediatype, data = data.split(",",1) + # Disallow control characters within mediatype. + if re.search(r"[\x00-\x1F\x7F]", mediatype): + raise ValueError( + "Control characters not allowed in data: mediatype") + # even base64 encoded data URLs might be quoted so unquote in any case: data = unquote_to_bytes(data) if mediatype.endswith(";base64"): @@ -1660,7 +1673,10 @@ def url2pathname(url, *, require_scheme=False, resolve_host=False): if scheme != 'file': raise URLError("URL is missing a 'file:' scheme") if os.name == 'nt': - if not _is_local_authority(authority, resolve_host): + if authority[1:2] == ':': + # e.g. file://c:/file.txt + url = authority + url + elif not _is_local_authority(authority, resolve_host): # e.g. file://server/share/file.txt url = '//' + authority + url elif url[:3] == '///': diff --git a/Lib/urllib/robotparser.py b/Lib/urllib/robotparser.py index 409f2b2e48de6e..e70eae80036784 100644 --- a/Lib/urllib/robotparser.py +++ b/Lib/urllib/robotparser.py @@ -7,10 +7,11 @@ 2) PSF license for Python 2.2 The robots.txt Exclusion Protocol is implemented as specified in - http://www.robotstxt.org/norobots-rfc.txt + RFC 9309 """ import collections +import re import urllib.error import urllib.parse import urllib.request @@ -28,6 +29,7 @@ class RobotFileParser: def __init__(self, url=''): self.entries = [] + self.groups = {} self.sitemaps = [] self.default_entry = None self.disallow_all = False @@ -55,7 +57,7 @@ def modified(self): def set_url(self, url): """Sets the URL referring to a robots.txt file.""" self.url = url - self.host, self.path = urllib.parse.urlparse(url)[1:3] + self.host, self.path = urllib.parse.urlsplit(url)[1:3] def read(self): """Reads the robots.txt URL and feeds it to the parser.""" @@ -69,16 +71,16 @@ def read(self): err.close() else: raw = f.read() - self.parse(raw.decode("utf-8").splitlines()) + self.parse(raw.decode("utf-8", "surrogateescape").splitlines()) def _add_entry(self, entry): - if "*" in entry.useragents: - # the default entry is considered last - if self.default_entry is None: - # the first default entry wins - self.default_entry = entry - else: - self.entries.append(entry) + self.entries.append(entry) + for agent in entry.useragents: + agent = agent.lower() + if agent not in self.groups: + self.groups[agent] = entry + else: + self.groups[agent] = merge_entries(self.groups[agent], entry) def parse(self, lines): """Parse the input lines from a robots.txt file. @@ -86,6 +88,7 @@ def parse(self, lines): We allow that a user-agent: line is not preceded by one or more blank lines. """ + entries = [] # states: # 0: start state # 1: saw user-agent line @@ -95,14 +98,6 @@ def parse(self, lines): self.modified() for line in lines: - if not line: - if state == 1: - entry = Entry() - state = 0 - elif state == 2: - self._add_entry(entry) - entry = Entry() - state = 0 # remove optional comment and strip line i = line.find('#') if i >= 0: @@ -113,21 +108,28 @@ def parse(self, lines): line = line.split(':', 1) if len(line) == 2: line[0] = line[0].strip().lower() - line[1] = urllib.parse.unquote(line[1].strip()) + line[1] = line[1].strip() if line[0] == "user-agent": if state == 2: self._add_entry(entry) entry = Entry() - entry.useragents.append(line[1]) + product_token = line[1] + entry.useragents.append(product_token) state = 1 elif line[0] == "disallow": if state != 0: - entry.rulelines.append(RuleLine(line[1], False)) state = 2 + try: + entry.rulelines.append(RuleLine(line[1], False)) + except ValueError: + pass elif line[0] == "allow": if state != 0: - entry.rulelines.append(RuleLine(line[1], True)) state = 2 + try: + entry.rulelines.append(RuleLine(line[1], True)) + except ValueError: + pass elif line[0] == "crawl-delay": if state != 0: # before trying to convert to int we need to make @@ -150,9 +152,18 @@ def parse(self, lines): # so it doesn't matter where you place it in your file." # Therefore we do not change the state of the parser. self.sitemaps.append(line[1]) - if state == 2: + if state != 0: self._add_entry(entry) + def _find_entry(self, useragent): + entry = self.groups.get(useragent.lower()) + if entry is not None: + return entry + for entry in self.groups.values(): + if entry.applies_to(useragent): + return entry + return self.groups.get('*') + def can_fetch(self, useragent, url): """using the parsed robots.txt decide if useragent can fetch url""" if self.disallow_all: @@ -165,42 +176,36 @@ def can_fetch(self, useragent, url): # calls can_fetch() before calling read(). if not self.last_checked: return False - # search for given user agent matches - # the first match counts - parsed_url = urllib.parse.urlparse(urllib.parse.unquote(url)) - url = urllib.parse.urlunparse(('','',parsed_url.path, - parsed_url.params,parsed_url.query, parsed_url.fragment)) - url = urllib.parse.quote(url) + # TODO: The private API is used in order to preserve an empty query. + # This is temporary until the public API starts supporting this feature. + parsed_url = urllib.parse._urlsplit(url, '') + url = urllib.parse._urlunsplit(None, None, *parsed_url[2:]) + url = normalize_uri(url) if not url: url = "/" - for entry in self.entries: - if entry.applies_to(useragent): - return entry.allowance(url) - # try the default entry last - if self.default_entry: - return self.default_entry.allowance(url) - # agent not found ==> access granted - return True + if url == '/robots.txt': + # The /robots.txt URI is implicitly allowed. + return True + entry = self._find_entry(useragent) + if entry is None: + return True + return entry.allowance(url) def crawl_delay(self, useragent): if not self.mtime(): return None - for entry in self.entries: - if entry.applies_to(useragent): - return entry.delay - if self.default_entry: - return self.default_entry.delay - return None + entry = self._find_entry(useragent) + if entry is None: + return None + return entry.delay def request_rate(self, useragent): if not self.mtime(): return None - for entry in self.entries: - if entry.applies_to(useragent): - return entry.req_rate - if self.default_entry: - return self.default_entry.req_rate - return None + entry = self._find_entry(useragent) + if entry is None: + return None + return entry.req_rate def site_maps(self): if not self.sitemaps: @@ -211,8 +216,7 @@ def __str__(self): entries = self.entries if self.default_entry is not None: entries = entries + [self.default_entry] - return '\n\n'.join(map(str, entries)) - + return '\n\n'.join(filter(None, map(str, entries))) class RuleLine: """A rule line is a single "Allow:" (allowance==True) or "Disallow:" @@ -221,15 +225,42 @@ def __init__(self, path, allowance): if path == '' and not allowance: # an empty value means allow all allowance = True - path = urllib.parse.urlunparse(urllib.parse.urlparse(path)) - self.path = urllib.parse.quote(path) + path = re.sub(r'[*]{2,}', '*', path) + path = re.sub(r'[$][$*]+', '$', path) + path = normalize_pattern(path) + self.fullmatch = path.endswith('$') + path = path.rstrip('$') + if '$' in path: + raise ValueError('$ not at the end of path') + self.matcher = None + if '*' in path: + pattern = re.compile(translate_pattern(path), re.DOTALL) + if self.fullmatch: + self.matcher = pattern.fullmatch + else: + self.matcher = pattern.match + self.path = path self.allowance = allowance def applies_to(self, filename): - return self.path == "*" or filename.startswith(self.path) + # If the filename matches the rule, return the matching length plus 1. + # If it does not match, return 0. + if self.matcher is not None: + m = self.matcher(filename) + if m: + return m.end() + 1 + else: + if self.fullmatch: + if filename == self.path: + return len(self.path) + 1 + else: + if filename.startswith(self.path): + return len(self.path) + 1 + return 0 def __str__(self): - return ("Allow" if self.allowance else "Disallow") + ": " + self.path + return (("Allow" if self.allowance else "Disallow") + ": " + self.path + + ('$' if self.fullmatch else '')) class Entry: @@ -241,6 +272,8 @@ def __init__(self): self.req_rate = None def __str__(self): + if not self.useragents: + return '' ret = [] for agent in self.useragents: ret.append(f"User-agent: {agent}") @@ -249,27 +282,74 @@ def __str__(self): if self.req_rate is not None: rate = self.req_rate ret.append(f"Request-rate: {rate.requests}/{rate.seconds}") - ret.extend(map(str, self.rulelines)) + if self.rulelines: + ret.extend(map(str, self.rulelines)) + else: + ret.append("Allow:") return '\n'.join(ret) def applies_to(self, useragent): """check if this entry applies to the specified agent""" + if useragent is None: + return '*' in self.useragents # split the name token and make it lower case useragent = useragent.split("/")[0].lower() for agent in self.useragents: - if agent == '*': - # we have the catch-all agent - return True - agent = agent.lower() - if agent in useragent: - return True + if agent != '*': + agent = agent.lower() + if agent in useragent: + return True return False def allowance(self, filename): """Preconditions: - our agent applies to this entry - - filename is URL decoded""" + - filename is URL encoded + """ + best_match = -1 + allowance = True for line in self.rulelines: - if line.applies_to(filename): - return line.allowance - return True + m = line.applies_to(filename) + if m: + if m > best_match: + best_match = m + allowance = line.allowance + elif m == best_match and not allowance: + allowance = line.allowance + return allowance + + +def normalize(path): + unquoted = urllib.parse.unquote(path, errors='surrogateescape') + return urllib.parse.quote(unquoted, errors='surrogateescape') + +def normalize_uri(path): + path, sep, query = path.partition('?') + path = normalize(path) + if sep: + query = re.sub(r'[^=&]+', lambda m: normalize(m[0]), query) + path += '?' + query + return path + +def normalize_pattern(path): + path, sep, query = path.partition('?') + path = re.sub(r'[^*$]+', lambda m: normalize(m[0]), path) + if sep: + query = re.sub(r'[^=&*$]+', lambda m: normalize(m[0]), query) + path += '?' + query + return path + +def translate_pattern(path): + parts = list(map(re.escape, path.split('*'))) + for i in range(1, len(parts)-1): + parts[i] = f'(?>.*?{parts[i]})' + parts[-1] = f'.*{parts[-1]}' + return ''.join(parts) + +def merge_entries(e1, e2): + entry = Entry() + entry.useragents = list(filter(set(e2.useragents).__contains__, e1.useragents)) + entry.rulelines = e1.rulelines + e2.rulelines + entry.delay = e1.delay if e2.delay is None else e2.delay + entry.req_rate = e1.req_rate if e2.req_rate is None else e2.req_rate + return entry diff --git a/Lib/uuid.py b/Lib/uuid.py index 313f2fc46cb346..4bdcb67775a2ea 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -737,6 +737,7 @@ def uuid1(node=None, clock_seq=None): is_safe = SafeUUID(safely_generated) except ValueError: is_safe = SafeUUID.unknown + # The version field is assumed to be handled by _generate_time_safe(). return UUID(bytes=uuid_time, is_safe=is_safe) global _last_timestamp @@ -954,22 +955,21 @@ def main(): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description="Generate a UUID using the selected UUID function.", - color=True, ) parser.add_argument("-u", "--uuid", choices=uuid_funcs.keys(), default="uuid4", help="function to generate the UUID") parser.add_argument("-n", "--namespace", - choices=["any UUID", *namespaces.keys()], - help="uuid3/uuid5 only: " + metavar=f"{{any UUID,{','.join(namespaces)}}}", + help="`uuid3`/`uuid5` only: " "a UUID, or a well-known predefined UUID addressed " "by namespace name") parser.add_argument("-N", "--name", - help="uuid3/uuid5 only: " + help="`uuid3`/`uuid5` only: " "name used as part of generating the UUID") parser.add_argument("-C", "--count", metavar="NUM", type=int, default=1, - help="generate NUM fresh UUIDs") + help="generate `NUM` fresh UUIDs") args = parser.parse_args() uuid_func = uuid_funcs[args.uuid] @@ -983,7 +983,13 @@ def main(): f"{args.uuid} requires a namespace and a name. " "Run 'python -m uuid -h' for more information." ) - namespace = namespaces[namespace] if namespace in namespaces else UUID(namespace) + if namespace in namespaces: + namespace = namespaces[namespace] + else: + try: + namespace = UUID(namespace) + except ValueError as exc: + parser.error(f"{exc}: {args.namespace!r}") for _ in range(args.count): print(uuid_func(namespace, name)) else: diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index dc9c5991df7e1c..bd2762d55ef696 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -174,6 +174,7 @@ def create_if_needed(d): context.python_exe = exename binpath = self._venv_path(env_dir, 'scripts') libpath = self._venv_path(env_dir, 'purelib') + platlibpath = self._venv_path(env_dir, 'platlib') # PEP 405 says venvs should create a local include directory. # See https://peps.python.org/pep-0405/#include-files @@ -191,12 +192,8 @@ def create_if_needed(d): create_if_needed(incpath) context.lib_path = libpath create_if_needed(libpath) - # Issue 21197: create lib64 as a symlink to lib on 64-bit non-OS X POSIX - if ((sys.maxsize > 2**32) and (os.name == 'posix') and - (sys.platform != 'darwin')): - link_path = os.path.join(env_dir, 'lib64') - if not os.path.exists(link_path): # Issue #21643 - os.symlink('lib', link_path) + context.platlib_path = platlibpath + create_if_needed(platlibpath) context.bin_path = binpath context.bin_name = os.path.relpath(binpath, env_dir) context.env_exe = os.path.join(binpath, exename) @@ -309,7 +306,6 @@ def setup_python(self, context): binpath = context.bin_path path = context.env_exe copier = self.symlink_or_copy - dirname = context.python_dir copier(context.executable, path) if not os.path.islink(path): os.chmod(path, 0o755) @@ -323,6 +319,14 @@ def setup_python(self, context): if not os.path.islink(path): os.chmod(path, 0o755) + if not self.symlinks and sys.platform == 'cygwin': + # Copy libpython DLL + libpython_dll = sysconfig.get_config_var('DLLLIBRARY') + if not os.path.exists(os.path.join(binpath, libpython_dll)): + exe_path = os.path.dirname(sys.executable) + shutil.copy(os.path.join(exe_path, libpython_dll), + os.path.join(binpath, libpython_dll)) + else: def setup_python(self, context): """ @@ -362,6 +366,9 @@ def setup_python(self, context): exe_t = f'3.{sys.version_info[1]}t' python_exe = os.path.join(dirname, f'python{exe_t}{exe_d}.exe') pythonw_exe = os.path.join(dirname, f'pythonw{exe_t}{exe_d}.exe') + if not os.path.isfile(python_exe): + python_exe = os.path.join(dirname, f'python{exe_d}.exe') + pythonw_exe = os.path.join(dirname, f'pythonw{exe_d}.exe') link_sources = { 'python.exe': python_exe, f'python{exe_d}.exe': python_exe, @@ -585,7 +592,7 @@ def skip_file(f): 'may be binary: %s', srcfile, e) continue if new_data == data: - shutil.copy2(srcfile, dstfile) + shutil.copy(srcfile, dstfile) else: with open(dstfile, 'wb') as f: f.write(new_data) @@ -622,7 +629,6 @@ def main(args=None): 'activate it, e.g. by ' 'sourcing an activate script ' 'in its bin directory.', - color=True, ) parser.add_argument('dirs', metavar='ENV_DIR', nargs='+', help='A directory to create the environment in.') diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate index 70673a265d41f8..241a8650bda33a 100644 --- a/Lib/venv/scripts/common/activate +++ b/Lib/venv/scripts/common/activate @@ -17,7 +17,7 @@ deactivate () { # Call hash to forget past locations. Without forgetting # past locations the $PATH changes we made may not be respected. # See "man bash" for more details. hash is usually a builtin of your shell - hash -r 2> /dev/null + hash -r 2> /dev/null || true if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then PS1="${_OLD_VIRTUAL_PS1:-}" @@ -73,4 +73,4 @@ fi # Call hash to forget past commands. Without forgetting # past commands the $PATH changes we made may not be respected -hash -r 2> /dev/null +hash -r 2> /dev/null || true diff --git a/Lib/venv/scripts/nt/activate.bat b/Lib/venv/scripts/nt/activate.bat index 35533e4b551155..4a3e791abb86bd 100644 --- a/Lib/venv/scripts/nt/activate.bat +++ b/Lib/venv/scripts/nt/activate.bat @@ -1,34 +1,32 @@ -@echo off - -rem This file is UTF-8 encoded, so we need to update the current code page while executing it -for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do ( - set _OLD_CODEPAGE=%%a +@rem This file is UTF-8 encoded, so we need to update the current code page while executing it +@for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do @( + @set _OLD_CODEPAGE=%%a ) -if defined _OLD_CODEPAGE ( - "%SystemRoot%\System32\chcp.com" 65001 > nul +@if defined _OLD_CODEPAGE ( + @"%SystemRoot%\System32\chcp.com" 65001 > nul ) -set "VIRTUAL_ENV=__VENV_DIR__" +@set "VIRTUAL_ENV=__VENV_DIR__" -if not defined PROMPT set PROMPT=$P$G +@if not defined PROMPT @set PROMPT=$P$G -if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT% -if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% +@if defined _OLD_VIRTUAL_PROMPT @set PROMPT=%_OLD_VIRTUAL_PROMPT% +@if defined _OLD_VIRTUAL_PYTHONHOME @set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% -set "_OLD_VIRTUAL_PROMPT=%PROMPT%" -set "PROMPT=(__VENV_PROMPT__) %PROMPT%" +@if not defined VIRTUAL_ENV_DISABLE_PROMPT @set "_OLD_VIRTUAL_PROMPT=%PROMPT%" +@if not defined VIRTUAL_ENV_DISABLE_PROMPT @set "PROMPT=(__VENV_PROMPT__) %PROMPT%" -if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% -set PYTHONHOME= +@if defined PYTHONHOME @set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% +@set PYTHONHOME= -if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH% -if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH% +@if defined _OLD_VIRTUAL_PATH @set PATH=%_OLD_VIRTUAL_PATH% +@if not defined _OLD_VIRTUAL_PATH @set _OLD_VIRTUAL_PATH=%PATH% -set "PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%" -set "VIRTUAL_ENV_PROMPT=__VENV_PROMPT__" +@set "PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%" +@set "VIRTUAL_ENV_PROMPT=__VENV_PROMPT__" :END -if defined _OLD_CODEPAGE ( - "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul - set _OLD_CODEPAGE= +@if defined _OLD_CODEPAGE ( + @"%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul + @set _OLD_CODEPAGE= ) diff --git a/Lib/venv/scripts/nt/deactivate.bat b/Lib/venv/scripts/nt/deactivate.bat index 62a39a7584f4d7..4a04fb7c0bed44 100644 --- a/Lib/venv/scripts/nt/deactivate.bat +++ b/Lib/venv/scripts/nt/deactivate.bat @@ -1,22 +1,20 @@ -@echo off - -if defined _OLD_VIRTUAL_PROMPT ( - set "PROMPT=%_OLD_VIRTUAL_PROMPT%" +@if defined _OLD_VIRTUAL_PROMPT ( + @set "PROMPT=%_OLD_VIRTUAL_PROMPT%" ) -set _OLD_VIRTUAL_PROMPT= +@set _OLD_VIRTUAL_PROMPT= -if defined _OLD_VIRTUAL_PYTHONHOME ( - set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" - set _OLD_VIRTUAL_PYTHONHOME= +@if defined _OLD_VIRTUAL_PYTHONHOME ( + @set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" + @set _OLD_VIRTUAL_PYTHONHOME= ) -if defined _OLD_VIRTUAL_PATH ( - set "PATH=%_OLD_VIRTUAL_PATH%" +@if defined _OLD_VIRTUAL_PATH ( + @set "PATH=%_OLD_VIRTUAL_PATH%" ) -set _OLD_VIRTUAL_PATH= +@set _OLD_VIRTUAL_PATH= -set VIRTUAL_ENV= -set VIRTUAL_ENV_PROMPT= +@set VIRTUAL_ENV= +@set VIRTUAL_ENV_PROMPT= :END diff --git a/Lib/wave.py b/Lib/wave.py index 5af745e2217ec3..c4e1a493a7ec7f 100644 --- a/Lib/wave.py +++ b/Lib/wave.py @@ -15,6 +15,8 @@ getsampwidth() -- returns sample width in bytes getframerate() -- returns sampling frequency getnframes() -- returns number of audio frames + getformat() -- returns frame encoding (WAVE_FORMAT_PCM, WAVE_FORMAT_IEEE_FLOAT + or WAVE_FORMAT_EXTENSIBLE) getcomptype() -- returns compression type ('NONE' for linear samples) getcompname() -- returns human-readable version of compression type ('not compressed' linear samples) @@ -42,6 +44,9 @@ setsampwidth(n) -- set the sample width setframerate(n) -- set the frame rate setnframes(n) -- set the number of frames + setformat(format) + -- set the frame format. Only WAVE_FORMAT_PCM and + WAVE_FORMAT_IEEE_FLOAT are supported. setcomptype(type, name) -- set the compression type and the human-readable compression type @@ -69,16 +74,26 @@ from collections import namedtuple import builtins +import os import struct import sys -__all__ = ["open", "Error", "Wave_read", "Wave_write"] +__all__ = [ + "open", + "Error", + "Wave_read", + "Wave_write", + "WAVE_FORMAT_PCM", + "WAVE_FORMAT_IEEE_FLOAT", + "WAVE_FORMAT_EXTENSIBLE", +] class Error(Exception): pass WAVE_FORMAT_PCM = 0x0001 +WAVE_FORMAT_IEEE_FLOAT = 0x0003 WAVE_FORMAT_EXTENSIBLE = 0xFFFE # Derived from uuid.UUID("00000001-0000-0010-8000-00aa00389b71").bytes_le KSDATAFORMAT_SUBTYPE_PCM = b'\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x008\x9bq' @@ -96,7 +111,7 @@ def _byteswap(data, width): for j in range(width): swapped_data[i + width - 1 - j] = data[i + j] - return bytes(swapped_data) + return swapped_data.take_bytes() class _Chunk: @@ -225,6 +240,10 @@ class Wave_read: available through the getsampwidth() method _framerate -- the sampling frequency available through the getframerate() method + _format -- frame format + One of WAVE_FORMAT_PCM, WAVE_FORMAT_IEEE_FLOAT + or WAVE_FORMAT_EXTENSIBLE available through + getformat() method _comptype -- the AIFF-C compression type ('NONE' if AIFF) available through the getcomptype() method _compname -- the human-readable AIFF-C compression type @@ -274,7 +293,7 @@ def initfp(self, file): def __init__(self, f): self._i_opened_the_file = None - if isinstance(f, str): + if isinstance(f, (bytes, str, os.PathLike)): f = builtins.open(f, 'rb') self._i_opened_the_file = f # else, assume it is an open file object already @@ -326,6 +345,9 @@ def getsampwidth(self): def getframerate(self): return self._framerate + def getformat(self): + return self._format + def getcomptype(self): return self._comptype @@ -366,16 +388,16 @@ def readframes(self, nframes): def _read_fmt_chunk(self, chunk): try: - wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14)) + self._format, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14)) except struct.error: raise EOFError from None - if wFormatTag != WAVE_FORMAT_PCM and wFormatTag != WAVE_FORMAT_EXTENSIBLE: - raise Error('unknown format: %r' % (wFormatTag,)) + if self._format not in (WAVE_FORMAT_PCM, WAVE_FORMAT_IEEE_FLOAT, WAVE_FORMAT_EXTENSIBLE): + raise Error('unknown format: %r' % (self._format,)) try: sampwidth = struct.unpack_from('<H', chunk.read(2))[0] except struct.error: raise EOFError from None - if wFormatTag == WAVE_FORMAT_EXTENSIBLE: + if self._format == WAVE_FORMAT_EXTENSIBLE: try: cbSize, wValidBitsPerSample, dwChannelMask = struct.unpack_from('<HHL', chunk.read(8)) # Read the entire UUID from the chunk @@ -418,6 +440,8 @@ class Wave_write: set through the setsampwidth() or setparams() method _framerate -- the sampling frequency set through the setframerate() or setparams() method + _format -- frame format + set through setformat() method _nframes -- the number of audio frames written to the header set through the setnframes() or setparams() method @@ -431,7 +455,7 @@ class Wave_write: def __init__(self, f): self._i_opened_the_file = None - if isinstance(f, str): + if isinstance(f, (bytes, str, os.PathLike)): f = builtins.open(f, 'wb') self._i_opened_the_file = f try: @@ -445,12 +469,14 @@ def initfp(self, file): self._file = file self._convert = None self._nchannels = 0 + self._format = WAVE_FORMAT_PCM self._sampwidth = 0 self._framerate = 0 self._nframes = 0 self._nframeswritten = 0 self._datawritten = 0 self._datalength = 0 + self._fact_sample_count_pos = None self._headerwritten = False def __del__(self): @@ -480,7 +506,10 @@ def getnchannels(self): def setsampwidth(self, sampwidth): if self._datawritten: raise Error('cannot change parameters after starting to write') - if sampwidth < 1 or sampwidth > 4: + if self._format == WAVE_FORMAT_IEEE_FLOAT: + if sampwidth not in (4, 8): + raise Error('unsupported sample width for IEEE float format') + elif sampwidth < 1 or sampwidth > 4: raise Error('bad sample width') self._sampwidth = sampwidth @@ -492,9 +521,10 @@ def getsampwidth(self): def setframerate(self, framerate): if self._datawritten: raise Error('cannot change parameters after starting to write') + framerate = int(round(framerate)) if framerate <= 0: raise Error('bad frame rate') - self._framerate = int(round(framerate)) + self._framerate = framerate def getframerate(self): if not self._framerate: @@ -517,6 +547,18 @@ def setcomptype(self, comptype, compname): self._comptype = comptype self._compname = compname + def setformat(self, format): + if self._datawritten: + raise Error('cannot change parameters after starting to write') + if format not in (WAVE_FORMAT_IEEE_FLOAT, WAVE_FORMAT_PCM): + raise Error('unsupported wave format') + if format == WAVE_FORMAT_IEEE_FLOAT and self._sampwidth and self._sampwidth not in (4, 8): + raise Error('unsupported sample width for IEEE float format') + self._format = format + + def getformat(self): + return self._format + def getcomptype(self): return self._comptype @@ -524,10 +566,15 @@ def getcompname(self): return self._compname def setparams(self, params): - nchannels, sampwidth, framerate, nframes, comptype, compname = params if self._datawritten: raise Error('cannot change parameters after starting to write') + if len(params) == 6: + nchannels, sampwidth, framerate, nframes, comptype, compname = params + format = WAVE_FORMAT_PCM + else: + nchannels, sampwidth, framerate, nframes, comptype, compname, format = params self.setnchannels(nchannels) + self.setformat(format) self.setsampwidth(sampwidth) self.setframerate(framerate) self.setnframes(nframes) @@ -564,6 +611,8 @@ def close(self): try: if self._file: self._ensure_header_written(0) + if self._datawritten & 1: + self._file.write(b'\x00') if self._datalength != self._datawritten: self._patchheader() self._file.flush() @@ -588,6 +637,9 @@ def _ensure_header_written(self, datasize): raise Error('sampling rate not specified') self._write_header(datasize) + def _needs_fact_chunk(self): + return self._format == WAVE_FORMAT_IEEE_FLOAT + def _write_header(self, initlength): assert not self._headerwritten self._file.write(b'RIFF') @@ -598,12 +650,23 @@ def _write_header(self, initlength): self._form_length_pos = self._file.tell() except (AttributeError, OSError): self._form_length_pos = None - self._file.write(struct.pack('<L4s4sLHHLLHH4s', - 36 + self._datalength, b'WAVE', b'fmt ', 16, - WAVE_FORMAT_PCM, self._nchannels, self._framerate, + has_fact = self._needs_fact_chunk() + header_overhead = 36 + (12 if has_fact else 0) + self._file.write(struct.pack('<L4s4sLHHLLHH', + header_overhead + self._datalength + (self._datalength & 1), b'WAVE', b'fmt ', 16, + self._format, self._nchannels, self._framerate, self._nchannels * self._framerate * self._sampwidth, self._nchannels * self._sampwidth, - self._sampwidth * 8, b'data')) + self._sampwidth * 8)) + if has_fact: + self._file.write(b'fact') + self._file.write(struct.pack('<L', 4)) + try: + self._fact_sample_count_pos = self._file.tell() + except (AttributeError, OSError): + self._fact_sample_count_pos = None + self._file.write(struct.pack('<L', self._nframes)) + self._file.write(b'data') if self._form_length_pos is not None: self._data_length_pos = self._file.tell() self._file.write(struct.pack('<L', self._datalength)) @@ -614,8 +677,13 @@ def _patchheader(self): if self._datawritten == self._datalength: return curpos = self._file.tell() + header_overhead = 36 + (12 if self._needs_fact_chunk() else 0) self._file.seek(self._form_length_pos, 0) - self._file.write(struct.pack('<L', 36 + self._datawritten)) + self._file.write(struct.pack('<L', header_overhead + self._datawritten + (self._datawritten & 1))) + if self._fact_sample_count_pos is not None: + self._file.seek(self._fact_sample_count_pos, 0) + nframes = self._datawritten // (self._nchannels * self._sampwidth) + self._file.write(struct.pack('<L', nframes)) self._file.seek(self._data_length_pos, 0) self._file.write(struct.pack('<L', self._datawritten)) self._file.seek(curpos, 0) diff --git a/Lib/weakref.py b/Lib/weakref.py index 94e4278143c987..af7244553c908c 100644 --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -408,14 +408,16 @@ def setdefault(self, key, default=None): return self.data.setdefault(ref(key, self._remove),default) def update(self, dict=None, /, **kwargs): + if kwargs: + msg = ("Keyword arguments are not supported: " + "cannot create weak reference to 'str' object") + raise TypeError(msg) d = self.data if dict is not None: if not hasattr(dict, "items"): dict = type({})(dict) for key, value in dict.items(): d[ref(key, self._remove)] = value - if len(kwargs): - self.update(kwargs) def __ior__(self, other): self.update(other) diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index f2e2394089d5a1..ec8b544a6b0523 100644 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -1,7 +1,8 @@ """Interfaces for launching and remotely controlling web browsers.""" -# Maintained by Georg Brandl. +import builtins # because we override open import os +lazy import plistlib import shlex import shutil import sys @@ -163,6 +164,12 @@ def open_new(self, url): def open_new_tab(self, url): return self.open(url, 2) + @staticmethod + def _check_url(url): + """Ensures that the URL is safe to pass to subprocesses as a parameter""" + if url and url.lstrip().startswith("-"): + raise ValueError(f"Invalid URL (leading dash disallowed): {url!r}") + class GenericBrowser(BaseBrowser): """Class for all browsers started with a command @@ -180,6 +187,7 @@ def __init__(self, name): def open(self, url, new=0, autoraise=True): sys.audit("webbrowser.open", url) + self._check_url(url) cmdline = [self.name] + [arg.replace("%s", url) for arg in self.args] try: @@ -200,6 +208,7 @@ def open(self, url, new=0, autoraise=True): cmdline = [self.name] + [arg.replace("%s", url) for arg in self.args] sys.audit("webbrowser.open", url) + self._check_url(url) try: if sys.platform[:3] == 'win': p = subprocess.Popen(cmdline) @@ -279,7 +288,9 @@ def open(self, url, new=0, autoraise=True): raise Error("Bad 'new' parameter to open(); " f"expected 0, 1, or 2, got {new}") - args = [arg.replace("%s", url).replace("%action", action) + self._check_url(url.replace("%action", action)) + + args = [arg.replace("%action", action).replace("%s", url) for arg in self.remote_args] args = [arg for arg in args if arg] success = self._invoke(args, True, autoraise, url) @@ -357,6 +368,7 @@ class Konqueror(BaseBrowser): def open(self, url, new=0, autoraise=True): sys.audit("webbrowser.open", url) + self._check_url(url) # XXX Currently I know no way to prevent KFM from opening a new win. if new == 2: action = "newTab" @@ -481,10 +493,15 @@ def register_standard_browsers(): _tryorder = [] if sys.platform == 'darwin': - register("MacOSX", None, MacOSXOSAScript('default')) - register("chrome", None, MacOSXOSAScript('google chrome')) - register("firefox", None, MacOSXOSAScript('firefox')) - register("safari", None, MacOSXOSAScript('safari')) + register("MacOS", None, MacOS('default')) + register("MacOSX", None, MacOS('default')) # backward compat alias + register("chrome", None, MacOS('google chrome')) + register("chromium", None, MacOS('chromium')) + register("firefox", None, MacOS('firefox')) + register("safari", None, MacOS('safari')) + register("opera", None, MacOS('opera')) + register("microsoft-edge", None, MacOS('microsoft edge')) + register("brave", None, MacOS('brave browser')) # macOS can use below Unix support (but we prefer using the macOS # specific stuff) @@ -588,6 +605,7 @@ def register_standard_browsers(): class WindowsDefault(BaseBrowser): def open(self, url, new=0, autoraise=True): sys.audit("webbrowser.open", url) + self._check_url(url) try: os.startfile(url) except OSError: @@ -602,12 +620,85 @@ def open(self, url, new=0, autoraise=True): # if sys.platform == 'darwin': + def _macos_default_browser_bundle_id(): + """Return the bundle ID of the default web browser. + + Reads the LaunchServices preferences file that macOS maintains + when the user sets a default browser. Returns 'com.apple.Safari' + if the file is absent or no https handler is recorded, because on + a fresh macOS installation Safari is the default browser and the + LaunchServices plist is not written until the user explicitly + changes their default browser. + """ + plist = os.path.expanduser( + '~/Library/Preferences/com.apple.LaunchServices/' + 'com.apple.launchservices.secure.plist' + ) + try: + with builtins.open(plist, 'rb') as f: + data = plistlib.load(f) + for handler in data.get('LSHandlers', []): + if handler.get('LSHandlerURLScheme') == 'https': + return (handler.get('LSHandlerRoleAll') + or handler.get('LSHandlerRoleViewer')) + except (OSError, KeyError, ValueError): + pass + return 'com.apple.Safari' + + class MacOS(BaseBrowser): + """Launcher class for macOS browsers, using /usr/bin/open. + + For http/https URLs with the default browser, /usr/bin/open is called + directly; macOS routes these to the registered browser. + + For all other URL schemes (e.g. file://) and for named browsers, + /usr/bin/open -b <bundle-id> is used so that the URL is always passed + to a browser application rather than dispatched by the OS file handler. + This prevents file injection attacks where a file:// URL pointing to an + executable bundle could otherwise be launched by the OS. + + Named browsers with known bundle IDs use -b; unknown names fall back + to -a. + """ + + _BUNDLE_IDS = { + 'google chrome': 'com.google.Chrome', + 'firefox': 'org.mozilla.firefox', + 'safari': 'com.apple.Safari', + 'chromium': 'org.chromium.Chromium', + 'opera': 'com.operasoftware.Opera', + 'microsoft edge': 'com.microsoft.edgemac', + 'brave browser': 'com.brave.Browser', + } + + def open(self, url, new=0, autoraise=True): + sys.audit("webbrowser.open", url) + self._check_url(url) + if self.name == 'default': + proto, sep, _ = url.partition(':') + if sep and proto.lower() in {'http', 'https'}: + cmd = ['/usr/bin/open', url] + else: + bundle_id = _macos_default_browser_bundle_id() + cmd = ['/usr/bin/open', '-b', bundle_id, url] + else: + bundle_id = self._BUNDLE_IDS.get(self.name.lower()) + if bundle_id: + cmd = ['/usr/bin/open', '-b', bundle_id, url] + else: + cmd = ['/usr/bin/open', '-a', self.name, url] + proc = subprocess.run(cmd, stderr=subprocess.DEVNULL) + return proc.returncode == 0 + class MacOSXOSAScript(BaseBrowser): def __init__(self, name='default'): + import warnings + warnings._deprecated("webbrowser.MacOSXOSAScript", remove=(3, 17)) super().__init__(name) def open(self, url, new=0, autoraise=True): sys.audit("webbrowser.open", url) + self._check_url(url) url = url.replace('"', '%22') if self.name == 'default': proto, _sep, _rest = url.partition(":") @@ -644,7 +735,7 @@ def open(self, url, new=0, autoraise=True): end ''' - osapipe = os.popen("osascript", "w") + osapipe = os.popen("/usr/bin/osascript", "w") if osapipe is None: return False @@ -664,6 +755,7 @@ def open(self, url, new=0, autoraise=True): class IOSBrowser(BaseBrowser): def open(self, url, new=0, autoraise=True): sys.audit("webbrowser.open", url) + self._check_url(url) # If ctypes isn't available, we can't open a browser if objc is None: return False @@ -720,7 +812,7 @@ def open(self, url, new=0, autoraise=True): def parse_args(arg_list: list[str] | None): import argparse parser = argparse.ArgumentParser( - description="Open URL in a web browser.", color=True, + description="Open URL in a web browser.", ) parser.add_argument("url", help="URL to open") diff --git a/Lib/wsgiref/handlers.py b/Lib/wsgiref/handlers.py index 9353fb678625b3..b82862deea7d74 100644 --- a/Lib/wsgiref/handlers.py +++ b/Lib/wsgiref/handlers.py @@ -1,7 +1,7 @@ """Base classes for server/gateway implementations""" from .util import FileWrapper, guess_scheme, is_hop_by_hop -from .headers import Headers +from .headers import Headers, _name_disallowed_re import sys, os, time @@ -250,6 +250,8 @@ def start_response(self, status, headers,exc_info=None): return self.write def _validate_status(self, status): + if _name_disallowed_re.search(status): + raise ValueError("Control characters are not allowed in status") if len(status) < 4: raise AssertionError("Status must be at least 4 characters") if not status[:3].isdigit(): diff --git a/Lib/wsgiref/headers.py b/Lib/wsgiref/headers.py index c78879f80c7df2..eb6ea6a412dcc9 100644 --- a/Lib/wsgiref/headers.py +++ b/Lib/wsgiref/headers.py @@ -9,6 +9,11 @@ # existence of which force quoting of the parameter value. import re tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]') +# Disallowed characters for headers and values. +# HTAB (\x09) is allowed in header values, but +# not in header names. (RFC 9110 Section 5.5) +_name_disallowed_re = re.compile(r'[\x00-\x1F\x7F]') +_value_disallowed_re = re.compile(r'[\x00-\x08\x0A-\x1F\x7F]') def _formatparam(param, value=None, quote=1): """Convenience function to format and return a key=value pair. @@ -35,12 +40,15 @@ def __init__(self, headers=None): self._headers = headers if __debug__: for k, v in headers: - self._convert_string_type(k) - self._convert_string_type(v) + self._convert_string_type(k, name=True) + self._convert_string_type(v, name=False) - def _convert_string_type(self, value): + def _convert_string_type(self, value, *, name): """Convert/check value type.""" if type(value) is str: + regex = (_name_disallowed_re if name else _value_disallowed_re) + if regex.search(value): + raise ValueError("Control characters not allowed in headers") return value raise AssertionError("Header names/values must be" " of type str (got {0})".format(repr(value))) @@ -53,14 +61,14 @@ def __setitem__(self, name, val): """Set the value of a header.""" del self[name] self._headers.append( - (self._convert_string_type(name), self._convert_string_type(val))) + (self._convert_string_type(name, name=True), self._convert_string_type(val, name=False))) def __delitem__(self,name): """Delete all occurrences of a header, if present. Does *not* raise an exception if the header is missing. """ - name = self._convert_string_type(name.lower()) + name = self._convert_string_type(name.lower(), name=True) self._headers[:] = [kv for kv in self._headers if kv[0].lower() != name] def __getitem__(self,name): @@ -87,13 +95,13 @@ def get_all(self, name): fields deleted and re-inserted are always appended to the header list. If no fields exist with the given name, returns an empty list. """ - name = self._convert_string_type(name.lower()) + name = self._convert_string_type(name.lower(), name=True) return [kv[1] for kv in self._headers if kv[0].lower()==name] def get(self,name,default=None): """Get the first header value for 'name', or return 'default'""" - name = self._convert_string_type(name.lower()) + name = self._convert_string_type(name.lower(), name=True) for k,v in self._headers: if k.lower()==name: return v @@ -148,8 +156,8 @@ def setdefault(self,name,value): and value 'value'.""" result = self.get(name) if result is None: - self._headers.append((self._convert_string_type(name), - self._convert_string_type(value))) + self._headers.append((self._convert_string_type(name, name=True), + self._convert_string_type(value, name=False))) return value else: return result @@ -172,13 +180,13 @@ def add_header(self, _name, _value, **_params): """ parts = [] if _value is not None: - _value = self._convert_string_type(_value) + _value = self._convert_string_type(_value, name=False) parts.append(_value) for k, v in _params.items(): - k = self._convert_string_type(k) + k = self._convert_string_type(k, name=True) if v is None: parts.append(k.replace('_', '-')) else: - v = self._convert_string_type(v) + v = self._convert_string_type(v, name=False) parts.append(_formatparam(k.replace('_', '-'), v)) - self._headers.append((self._convert_string_type(_name), "; ".join(parts))) + self._headers.append((self._convert_string_type(_name, name=True), "; ".join(parts))) diff --git a/Lib/wsgiref/simple_server.py b/Lib/wsgiref/simple_server.py index a0f2397fcf0006..31efd8c9baea0d 100644 --- a/Lib/wsgiref/simple_server.py +++ b/Lib/wsgiref/simple_server.py @@ -16,11 +16,10 @@ from wsgiref.handlers import SimpleHandler from platform import python_implementation -__version__ = "0.2" __all__ = ['WSGIServer', 'WSGIRequestHandler', 'demo_app', 'make_server'] -server_version = "WSGIServer/" + __version__ +server_version = "WSGIServer" sys_version = python_implementation() + "/" + sys.version.split()[0] software_version = server_version + ' ' + sys_version @@ -70,7 +69,7 @@ def set_app(self,application): class WSGIRequestHandler(BaseHTTPRequestHandler): - server_version = "WSGIServer/" + __version__ + server_version = "WSGIServer" def get_environ(self): env = self.server.base_environ.copy() @@ -152,6 +151,15 @@ def make_server( return server +def __getattr__(name): + if name == "__version__": + from warnings import _deprecated + + _deprecated("__version__", remove=(3, 20)) + return "0.2" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + if __name__ == '__main__': with make_server('', 8000, demo_app) as httpd: sa = httpd.socket.getsockname() diff --git a/Lib/xml/__init__.py b/Lib/xml/__init__.py index bf6d8ddfd04c93..ecfce1c6ae52cf 100644 --- a/Lib/xml/__init__.py +++ b/Lib/xml/__init__.py @@ -16,5 +16,6 @@ """ +from .utils import * -__all__ = ["dom", "parsers", "sax", "etree"] +__all__ = ["dom", "parsers", "sax", "etree", "is_valid_name", "is_valid_text"] diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index db51f350ea0153..16b33b90184dc5 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -292,13 +292,6 @@ def _append_child(self, node): childNodes.append(node) node.parentNode = self -def _in_document(node): - # return True iff node is part of a document tree - while node is not None: - if node.nodeType == Node.DOCUMENT_NODE: - return True - node = node.parentNode - return False def _write_data(writer, text, attr): "Writes datachars to writer." @@ -371,6 +364,7 @@ class Attr(Node): def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None, prefix=None): self.ownerElement = None + self.ownerDocument = None self._name = qName self.namespaceURI = namespaceURI self._prefix = prefix @@ -696,6 +690,7 @@ class Element(Node): def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None, localName=None): + self.ownerDocument = None self.parentNode = None self.tagName = self.nodeName = tagName self.prefix = prefix @@ -1555,7 +1550,7 @@ def _clear_id_cache(node): if node.nodeType == Node.DOCUMENT_NODE: node._id_cache.clear() node._id_search_stack = None - elif _in_document(node): + elif node.ownerDocument: node.ownerDocument._id_cache.clear() node.ownerDocument._id_search_stack= None diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index dafe5b1b8a0c3f..53727d7940b3f2 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -8,8 +8,8 @@ 2. Element represents a single node in this tree. Interactions with the whole document (reading and writing to/from files) are - usually done on the ElementTree level. Interactions with a single XML element - and its sub-elements are done on the Element level. + usually done on the ElementTree level. Interactions with a single XML + element and its sub-elements are done on the Element level. Element is a flexible container object designed to store hierarchical data structures in memory. It can be described as a cross between a list and a @@ -83,15 +83,12 @@ "SubElement", "tostring", "tostringlist", "TreeBuilder", - "VERSION", "XML", "XMLID", "XMLParser", "XMLPullParser", "register_namespace", "canonicalize", "C14NWriterTarget", ] -VERSION = "1.3.0" - import sys import re import warnings @@ -167,9 +164,9 @@ class Element: """ - def __init__(self, tag, attrib={}, **extra): - if not isinstance(attrib, dict): - raise TypeError("attrib must be dict, not %s" % ( + def __init__(self, tag, /, attrib={}, **extra): + if not isinstance(attrib, (dict, frozendict)): + raise TypeError("attrib must be dict or frozendict, not %s" % ( attrib.__class__.__name__,)) self.tag = tag self.attrib = {**attrib, **extra} @@ -266,18 +263,22 @@ def remove(self, subelement): ValueError is raised if a matching element could not be found. """ - # assert iselement(element) try: self._children.remove(subelement) except ValueError: + # to align the error type with the C implementation + if isinstance(subelement, type) or not iselement(subelement): + raise TypeError('expected an Element, not %s' % + type(subelement).__name__) from None # to align the error message with the C implementation - raise ValueError("Element.remove(x): element not found") from None + raise ValueError(f"{subelement!r} not in {self!r}") from None def find(self, path, namespaces=None): """Find first matching element by tag name or path. *path* is a string having either an element tag or an XPath, - *namespaces* is an optional mapping from namespace prefix to full name. + *namespaces* is an optional mapping from namespace prefix to full + name. Return the first matching element, or None if no element was found. @@ -289,7 +290,8 @@ def findtext(self, path, default=None, namespaces=None): *path* is a string having either an element tag or an XPath, *default* is the value to return if the element was not found, - *namespaces* is an optional mapping from namespace prefix to full name. + *namespaces* is an optional mapping from namespace prefix to full + name. Return text content of first matching element, or default value if none was found. Note that if an element is found having no text @@ -302,7 +304,8 @@ def findall(self, path, namespaces=None): """Find all matching subelements by tag name or path. *path* is a string having either an element tag or an XPath, - *namespaces* is an optional mapping from namespace prefix to full name. + *namespaces* is an optional mapping from namespace prefix to full + name. Returns list containing all matching elements in document order. @@ -313,7 +316,8 @@ def iterfind(self, path, namespaces=None): """Find all matching subelements by tag name or path. *path* is a string having either an element tag or an XPath, - *namespaces* is an optional mapping from namespace prefix to full name. + *namespaces* is an optional mapping from namespace prefix to full + name. Return an iterable yielding all matching elements in document order. @@ -416,7 +420,7 @@ def itertext(self): yield t -def SubElement(parent, tag, attrib={}, **extra): +def SubElement(parent, tag, /, attrib={}, **extra): """Subelement factory which creates an element instance, and appends it to an existing parent. @@ -553,8 +557,8 @@ def _setroot(self, element): def parse(self, source, parser=None): """Load external XML document into element tree. - *source* is a file name or file object, *parser* is an optional parser - instance that defaults to XMLParser. + *source* is a file name or file object, *parser* is an optional + parser instance that defaults to XMLParser. ParseError is raised if the parser fails to parse the document. @@ -587,7 +591,8 @@ def parse(self, source, parser=None): def iter(self, tag=None): """Create and return tree iterator for the root element. - The iterator loops over all elements in this tree, in document order. + The iterator loops over all elements in this tree, in document + order. *tag* is a string with the tag name to iterate over (default is to return all elements). @@ -602,7 +607,8 @@ def find(self, path, namespaces=None): Same as getroot().find(path), which is Element.find() *path* is a string having either an element tag or an XPath, - *namespaces* is an optional mapping from namespace prefix to full name. + *namespaces* is an optional mapping from namespace prefix to full + name. Return the first matching element, or None if no element was found. @@ -624,7 +630,8 @@ def findtext(self, path, default=None, namespaces=None): Same as getroot().findtext(path), which is Element.findtext() *path* is a string having either an element tag or an XPath, - *namespaces* is an optional mapping from namespace prefix to full name. + *namespaces* is an optional mapping from namespace prefix to full + name. Return the first matching element, or None if no element was found. @@ -646,7 +653,8 @@ def findall(self, path, namespaces=None): Same as getroot().findall(path), which is Element.findall(). *path* is a string having either an element tag or an XPath, - *namespaces* is an optional mapping from namespace prefix to full name. + *namespaces* is an optional mapping from namespace prefix to full + name. Return list containing all matching elements in document order. @@ -693,24 +701,26 @@ def write(self, file_or_filename, """Write element tree to a file as XML. Arguments: - *file_or_filename* -- file name or a file object opened for writing + *file_or_filename* -- file name or a file object opened for + writing *encoding* -- the output encoding (default: US-ASCII) - *xml_declaration* -- bool indicating if an XML declaration should be - added to the output. If None, an XML declaration - is added if encoding IS NOT either of: - US-ASCII, UTF-8, or Unicode + *xml_declaration* -- bool indicating if an XML declaration should + be added to the output. If None, an XML + declaration is added if encoding IS NOT + either of: US-ASCII, UTF-8, or Unicode - *default_namespace* -- sets the default XML namespace (for "xmlns") + *default_namespace* -- sets the default XML namespace (for + "xmlns") *method* -- either "xml" (default), "html, "text", or "c14n" *short_empty_elements* -- controls the formatting of elements - that contain no content. If True (default) - they are emitted as a single self-closed - tag, otherwise they are emitted as a pair - of start/end tags + that contain no content. If True + (default) they are emitted as a single + self-closed tag, otherwise they are + emitted as a pair of start/end tags """ if self._root is None: @@ -907,17 +917,20 @@ def _serialize_xml(write, elem, qnames, namespaces, if elem.tail: write(_escape_cdata(elem.tail)) +_CDATA_CONTENT_ELEMENTS = {"script", "style", "xmp", "iframe", "noembed", + "noframes", "plaintext"} + HTML_EMPTY = {"area", "base", "basefont", "br", "col", "embed", "frame", "hr", "img", "input", "isindex", "link", "meta", "param", "source", - "track", "wbr"} + "track", "wbr", "plaintext"} def _serialize_html(write, elem, qnames, namespaces, **kwargs): tag = elem.tag text = elem.text if tag is Comment: - write("<!--%s-->" % _escape_cdata(text)) + write("<!--%s-->" % text) elif tag is ProcessingInstruction: - write("<?%s?>" % _escape_cdata(text)) + write("<?%s?>" % text) else: tag = qnames[tag] if tag is None: @@ -941,16 +954,19 @@ def _serialize_html(write, elem, qnames, namespaces, **kwargs): for k, v in items: if isinstance(k, QName): k = k.text - if isinstance(v, QName): - v = qnames[v.text] + k = qnames[k] + if v is None: + write(" %s" % k) # empty attr else: - v = _escape_attrib_html(v) - # FIXME: handle boolean attributes - write(" %s=\"%s\"" % (qnames[k], v)) + if isinstance(v, QName): + v = qnames[v.text] + else: + v = _escape_attrib_html(v) + write(" %s=\"%s\"" % (k, v)) write(">") ltag = tag.lower() if text: - if ltag == "script" or ltag == "style": + if ltag in _CDATA_CONTENT_ELEMENTS: write(text) else: write(_escape_cdata(text)) @@ -1083,9 +1099,9 @@ def tostring(element, encoding=None, method=None, *, is returned. Otherwise a bytestring is returned. *element* is an Element instance, *encoding* is an optional output - encoding defaulting to US-ASCII, *method* is an optional output which can - be one of "xml" (default), "html", "text" or "c14n", *default_namespace* - sets the default XML namespace (for "xmlns"). + encoding defaulting to US-ASCII, *method* is an optional output which + can be one of "xml" (default), "html", "text" or "c14n", + *default_namespace* sets the default XML namespace (for "xmlns"). Returns an (optionally) encoded string containing the XML data. @@ -1225,7 +1241,8 @@ def iterparse(source, events=None, parser=None): "end" events are reported. *source* is a filename or file object containing XML data, *events* is - a list of events to report back, *parser* is an optional parser instance. + a list of events to report back, *parser* is an optional parser + instance. Returns an iterator providing (event, elem) pairs. @@ -1261,16 +1278,20 @@ def iterator(source): gen = iterator(source) class IterParseIterator(collections.abc.Iterator): __next__ = gen.__next__ + def close(self): + nonlocal close_source if close_source: source.close() + close_source = False gen.close() - def __del__(self): - # TODO: Emit a ResourceWarning if it was not explicitly closed. - # (When the close() method will be supported in all maintained Python versions.) + def __del__(self, _warn=warnings.warn): if close_source: - source.close() + try: + _warn(f"unclosed iterparse iterator {source.name!r}", ResourceWarning, stacklevel=2) + finally: + source.close() it = IterParseIterator() it.root = None @@ -1757,10 +1778,11 @@ def flush(self): def canonicalize(xml_data=None, *, out=None, from_file=None, **options): """Convert XML to its C14N 2.0 serialised form. - If *out* is provided, it must be a file or file-like object that receives - the serialised canonical XML output (text, not bytes) through its ``.write()`` - method. To write to a file, open it in text mode with encoding "utf-8". - If *out* is not provided, this function returns the output as text string. + If *out* is provided, it must be a file or file-like object that + receives the serialised canonical XML output (text, not bytes) through + its ``.write()`` method. To write to a file, open it in text mode with + encoding "utf-8". If *out* is not provided, this function returns the + output as text string. Either *xml_data* (an XML string) or *from_file* (a file path or file-like object) must be provided as input. @@ -1794,19 +1816,22 @@ class C14NWriterTarget: Serialises parse events to XML C14N 2.0. The *write* function is used for writing out the resulting data stream - as text (not bytes). To write to a file, open it in text mode with encoding - "utf-8" and pass its ``.write`` method. + as text (not bytes). To write to a file, open it in text mode with + encoding "utf-8" and pass its ``.write`` method. Configuration options: - *with_comments*: set to true to include comments - - *strip_text*: set to true to strip whitespace before and after text content - - *rewrite_prefixes*: set to true to replace namespace prefixes by "n{number}" + - *strip_text*: set to true to strip whitespace before and after text + content + - *rewrite_prefixes*: set to true to replace namespace prefixes by + "n{number}" - *qname_aware_tags*: a set of qname aware tag names in which prefixes should be replaced in text content - - *qname_aware_attrs*: a set of qname aware attribute names in which prefixes - should be replaced in text content - - *exclude_attrs*: a set of attribute names that should not be serialised + - *qname_aware_attrs*: a set of qname aware attribute names in which + prefixes should be replaced in text content + - *exclude_attrs*: a set of attribute names that should not be + serialised - *exclude_tags*: a set of tag names that should not be serialised """ def __init__(self, write, *, @@ -2100,3 +2125,14 @@ def _escape_attrib_c14n(text): pass else: _set_factories(Comment, ProcessingInstruction) + + +# -------------------------------------------------------------------- + +def __getattr__(name): + if name == "VERSION": + from warnings import _deprecated + + _deprecated("VERSION", remove=(3, 20)) + return "1.3.0" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/xml/sax/expatreader.py b/Lib/xml/sax/expatreader.py index ba3c1e98517429..37b1add2848487 100644 --- a/Lib/xml/sax/expatreader.py +++ b/Lib/xml/sax/expatreader.py @@ -3,8 +3,6 @@ pyexpat.__version__ == '2.22'. """ -version = "0.20" - from xml.sax._exceptions import * from xml.sax.handler import feature_validation, feature_namespaces from xml.sax.handler import feature_namespace_prefixes @@ -446,6 +444,16 @@ def create_parser(*args, **kwargs): # --- +def __getattr__(name): + if name == "version": + from warnings import _deprecated + + _deprecated("version", remove=(3, 20)) + return "0.20" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +# --- + if __name__ == "__main__": import xml.sax.saxutils p = create_parser() diff --git a/Lib/xml/sax/handler.py b/Lib/xml/sax/handler.py index 3183c3fe96d74f..9c2e3af838a40f 100644 --- a/Lib/xml/sax/handler.py +++ b/Lib/xml/sax/handler.py @@ -9,8 +9,6 @@ $Id$ """ -version = '2.0beta' - #============================================================================ # # HANDLER INTERFACES @@ -385,3 +383,12 @@ def startCDATA(self): def endCDATA(self): """Reports the end of a CDATA marked section.""" + + +def __getattr__(name): + if name == "version": + from warnings import _deprecated + + _deprecated("version", remove=(3, 20)) + return "2.0beta" # Do not change + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/Lib/xml/utils.py b/Lib/xml/utils.py new file mode 100644 index 00000000000000..532aa224dae677 --- /dev/null +++ b/Lib/xml/utils.py @@ -0,0 +1,37 @@ +lazy import re as _re + + +def is_valid_name(name): + """Test whether a string is a valid element or attribute name.""" + # https://www.w3.org/TR/xml/#NT-Name + return _re.fullmatch( + # NameStartChar + '[' + ':A-Z_a-z' + '\xC0-\xD6\xD8-\xF6\xF8-\u02FF\u0370-\u037D\u037F-\u1FFF' + '\u200C\u200D' + '\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF' + '\uF900-\uFDCF\uFDF0-\uFFFD\U00010000-\U000EFFFF' + ']' + # NameChar + '[' + r'\-.0-9:A-Z_a-z' + '\xB7' + '\xC0-\xD6\xD8-\xF6\xF8-\u037D\u037F-\u1FFF' + '\u200C\u200D\u203F\u2040' + '\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF' + '\uF900-\uFDCF\uFDF0-\uFFFD\U00010000-\U000EFFFF' + ']*+', + name) is not None + +# https://www.w3.org/TR/xml/#charsets +_ILLEGAL_XML_CHAR = ( + '[' + '\x00-\x08\x0B\x0C\x0E-\x1F' # C0 controls except TAB, CR and LF + '\uD800-\uDFFF' # the surrogate blocks + '\uFFFE\uFFFF' # special Unicode characters + ']') + +def is_valid_text(data): + """Test whether a string is a sequence of legal XML 1.0 characters.""" + return _re.search(_ILLEGAL_XML_CHAR, data) is None diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py index f441376d09c4aa..84e4e4d11a7319 100644 --- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -965,7 +965,7 @@ def dumps(params, methodname=None, methodresponse=None, encoding=None, data = ( xmlheader, "<methodCall>\n" - "<methodName>", methodname, "</methodName>\n", + "<methodName>", escape(methodname), "</methodName>\n", data, "</methodCall>\n" ) diff --git a/Lib/zipapp.py b/Lib/zipapp.py index 7a4ef96ea0f077..a1cef18ada9d05 100644 --- a/Lib/zipapp.py +++ b/Lib/zipapp.py @@ -187,16 +187,16 @@ def main(args=None): """ import argparse - parser = argparse.ArgumentParser(color=True) + parser = argparse.ArgumentParser() parser.add_argument('--output', '-o', default=None, help="The name of the output archive. " - "Required if SOURCE is an archive.") + "Required if `SOURCE` is an archive.") parser.add_argument('--python', '-p', default=None, help="The name of the Python interpreter to use " "(default: no shebang line).") parser.add_argument('--main', '-m', default=None, help="The main function of the application " - "(default: use an existing __main__.py).") + "(default: use an existing `__main__.py`).") parser.add_argument('--compress', '-c', action='store_true', help="Compress files with the deflate method. " "Files are stored uncompressed by default.") diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index 2969f735e8abb9..005b6f4eb84044 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -4,7 +4,6 @@ XXX references to utf-8 need further investigation. """ import binascii -import importlib.util import io import os import shutil @@ -265,7 +264,7 @@ def is_zipfile(filename): else: with open(filename, "rb") as fp: result = _check_zipfile(fp) - except OSError: + except (OSError, BadZipFile): pass return result @@ -275,9 +274,6 @@ def _handle_prepended_data(endrec, debug=0): # "concat" is zero, unless zip was concatenated to another file concat = endrec[_ECD_LOCATION] - size_cd - offset_cd - if endrec[_ECD_SIGNATURE] == stringEndArchive64: - # If Zip64 extension structures are present, account for them - concat -= (sizeEndCentDir64 + sizeEndCentDir64Locator) if debug > 2: inferred = concat + offset_cd @@ -289,16 +285,15 @@ def _EndRecData64(fpin, offset, endrec): """ Read the ZIP64 end-of-archive records and use that to update endrec """ - try: - fpin.seek(offset - sizeEndCentDir64Locator, 2) - except OSError: - # If the seek fails, the file is not large enough to contain a ZIP64 + offset -= sizeEndCentDir64Locator + if offset < 0: + # The file is not large enough to contain a ZIP64 # end-of-archive record, so just return the end record we were given. return endrec - + fpin.seek(offset) data = fpin.read(sizeEndCentDir64Locator) if len(data) != sizeEndCentDir64Locator: - return endrec + raise OSError("Unknown I/O error") sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data) if sig != stringEndArchive64Locator: return endrec @@ -306,16 +301,33 @@ def _EndRecData64(fpin, offset, endrec): if diskno != 0 or disks > 1: raise BadZipFile("zipfiles that span multiple disks are not supported") - # Assume no 'zip64 extensible data' - fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2) + offset -= sizeEndCentDir64 + if reloff > offset: + raise BadZipFile("Corrupt zip64 end of central directory locator") + # First, check the assumption that there is no prepended data. + fpin.seek(reloff) + extrasz = offset - reloff data = fpin.read(sizeEndCentDir64) if len(data) != sizeEndCentDir64: - return endrec + raise OSError("Unknown I/O error") + if not data.startswith(stringEndArchive64) and reloff != offset: + # Since we already have seen the Zip64 EOCD Locator, it's + # possible we got here because there is prepended data. + # Assume no 'zip64 extensible data' + fpin.seek(offset) + extrasz = 0 + data = fpin.read(sizeEndCentDir64) + if len(data) != sizeEndCentDir64: + raise OSError("Unknown I/O error") + if not data.startswith(stringEndArchive64): + raise BadZipFile("Zip64 end of central directory record not found") + sig, sz, create_version, read_version, disk_num, disk_dir, \ dircount, dircount2, dirsize, diroffset = \ struct.unpack(structEndArchive64, data) - if sig != stringEndArchive64: - return endrec + if (diroffset + dirsize != reloff or + sz + 12 != sizeEndCentDir64 + extrasz): + raise BadZipFile("Corrupt zip64 end of central directory record") # Update the original endrec using data from the ZIP64 record endrec[_ECD_SIGNATURE] = sig @@ -325,6 +337,7 @@ def _EndRecData64(fpin, offset, endrec): endrec[_ECD_ENTRIES_TOTAL] = dircount2 endrec[_ECD_SIZE] = dirsize endrec[_ECD_OFFSET] = diroffset + endrec[_ECD_LOCATION] = offset - extrasz return endrec @@ -358,7 +371,7 @@ def _EndRecData(fpin): endrec.append(filesize - sizeEndCentDir) # Try to read the "Zip64 end of central directory" structure - return _EndRecData64(fpin, -sizeEndCentDir, endrec) + return _EndRecData64(fpin, filesize - sizeEndCentDir, endrec) # Either this is not a ZIP file, or it is a ZIP file with an archive # comment. Search the end of the file for the "end of central directory" @@ -382,8 +395,7 @@ def _EndRecData(fpin): endrec.append(maxCommentStart + start) # Try to read the "Zip64 end of central directory" structure - return _EndRecData64(fpin, maxCommentStart + start - filesize, - endrec) + return _EndRecData64(fpin, maxCommentStart + start, endrec) # Unable to find a valid end of central directory structure return None @@ -553,8 +565,12 @@ def FileHeader(self, zip64=None): return header + filename + extra def _encodeFilenameFlags(self): + if self.flag_bits & _MASK_UTF_FILENAME: + encoding = 'ascii' + else: + encoding = 'cp437' try: - return self.filename.encode('ascii'), self.flag_bits + return self.filename.encode(encoding), self.flag_bits & ~_MASK_UTF_FILENAME except UnicodeEncodeError: return self.filename.encode('utf-8'), self.flag_bits | _MASK_UTF_FILENAME @@ -607,11 +623,12 @@ def _decodeExtra(self, filename_crc): def from_file(cls, filename, arcname=None, *, strict_timestamps=True): """Construct an appropriate ZipInfo for a file on the filesystem. - filename should be the path to a file or directory on the filesystem. + filename should be the path to a file or directory on the + filesystem. - arcname is the name which it will have within the archive (by default, - this will be the same as filename, but without a drive letter and with - leading path separators removed). + arcname is the name which it will have within the archive (by + default, this will be the same as filename, but without a drive + letter and with leading path separators removed). """ if isinstance(filename, os.PathLike): filename = os.fspath(filename) @@ -650,9 +667,12 @@ def _for_archive(self, archive): Return self. """ # gh-91279: Set the SOURCE_DATE_EPOCH to a specific timestamp - epoch = os.environ.get('SOURCE_DATE_EPOCH') - get_time = int(epoch) if epoch else time.time() - self.date_time = time.localtime(get_time)[:6] + source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH') + + if source_date_epoch: + self.date_time = time.gmtime(int(source_date_epoch))[:6] + else: + self.date_time = time.localtime(time.time())[:6] self.compress_type = archive.compression self.compress_level = archive.compresslevel @@ -937,7 +957,7 @@ class ZipExtFile(io.BufferedIOBase): """ # Max size supported by decompressor. - MAX_N = 1 << 31 - 1 + MAX_N = (1 << 31) - 1 # Read from compressed files in 4k blocks. MIN_READ_SIZE = 4096 @@ -1379,29 +1399,30 @@ class ZipFile: mode: The mode can be either read 'r', write 'w', exclusive create 'x', or append 'a'. compression: ZIP_STORED (no compression), ZIP_DEFLATED (requires zlib), - ZIP_BZIP2 (requires bz2), ZIP_LZMA (requires lzma), or - ZIP_ZSTANDARD (requires compression.zstd). - allowZip64: if True ZipFile will create files with ZIP64 extensions when - needed, otherwise it will raise an exception when this would - be necessary. - compresslevel: None (default for the given compression type) or an integer - specifying the level to pass to the compressor. - When using ZIP_STORED or ZIP_LZMA this keyword has no effect. - When using ZIP_DEFLATED integers 0 through 9 are accepted. - When using ZIP_BZIP2 integers 1 through 9 are accepted. - When using ZIP_ZSTANDARD integers -7 though 22 are common, - see the CompressionParameter enum in compression.zstd for - details. + ZIP_BZIP2 (requires bz2), ZIP_LZMA (requires lzma), or + ZIP_ZSTANDARD (requires compression.zstd). + allowZip64: if True ZipFile will create files with ZIP64 extensions + when needed, otherwise it will raise an exception when this + would be necessary. + compresslevel: None (default for the given compression type) or + an integer specifying the level to pass to the compressor. + When using ZIP_STORED or ZIP_LZMA this keyword has no effect. + When using ZIP_DEFLATED integers 0 through 9 are accepted. + When using ZIP_BZIP2 integers 1 through 9 are accepted. + When using ZIP_ZSTANDARD integers -7 though 22 are common, + see the CompressionParameter enum in compression.zstd for + details. """ fp = None # Set here since __del__ checks it _windows_illegal_name_trans_table = None + _ignore_invalid_names = False def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True, compresslevel=None, *, strict_timestamps=True, metadata_encoding=None): - """Open the ZIP file with mode read 'r', write 'w', exclusive create 'x', - or append 'a'.""" + """Open the ZIP file with mode read 'r', write 'w', exclusive create + 'x', or append 'a'.""" if mode not in ('r', 'w', 'x', 'a'): raise ValueError("ZipFile requires mode 'r', 'w', 'x', or 'a'") @@ -1679,10 +1700,10 @@ def open(self, name, mode="r", pwd=None, *, force_zip64=False): pwd is the password to decrypt files (only used for reading). - When writing, if the file size is not known in advance but may exceed - 2 GiB, pass force_zip64 to use the ZIP64 format, which can handle large - files. If the size is known in advance, it is best to pass a ZipInfo - instance for name, with zinfo.file_size set. + When writing, if the file size is not known in advance but may + exceed 2 GiB, pass force_zip64 to use the ZIP64 format, which can + handle large files. If the size is known in advance, it is best to + pass a ZipInfo instance for name, with zinfo.file_size set. """ if mode not in {"r", "w"}: raise ValueError('open() requires mode "r" or "w"') @@ -1794,7 +1815,7 @@ def _open_to_write(self, zinfo, force_zip64=False): zinfo.compress_size = 0 zinfo.CRC = 0 - zinfo.flag_bits = 0x00 + zinfo.flag_bits = _MASK_UTF_FILENAME if zinfo.compress_type == ZIP_LZMA: # Compressed data includes an end-of-stream (EOS) marker zinfo.flag_bits |= _MASK_COMPRESS_OPTION_1 @@ -1877,21 +1898,31 @@ def _extract_member(self, member, targetpath, pwd): # build the destination pathname, replacing # forward slashes to platform specific separators. - arcname = member.filename.replace('/', os.path.sep) - - if os.path.altsep: + arcname = member.filename + if os.path.sep != '/': + arcname = arcname.replace('/', os.path.sep) + if os.path.altsep and os.path.altsep != '/': arcname = arcname.replace(os.path.altsep, os.path.sep) # interpret absolute pathname as relative, remove drive letter or # UNC path, redundant separators, "." and ".." components. - arcname = os.path.splitdrive(arcname)[1] + drive, root, arcname = os.path.splitroot(arcname) + if self._ignore_invalid_names and (drive or root): + return None + if self._ignore_invalid_names and os.path.pardir in arcname.split(os.path.sep): + return None invalid_path_parts = ('', os.path.curdir, os.path.pardir) arcname = os.path.sep.join(x for x in arcname.split(os.path.sep) if x not in invalid_path_parts) if os.path.sep == '\\': # filter illegal characters on Windows - arcname = self._sanitize_windows_name(arcname, os.path.sep) + arcname2 = self._sanitize_windows_name(arcname, os.path.sep) + if self._ignore_invalid_names and arcname2 != arcname: + return None + arcname = arcname2 if not arcname and not member.is_dir(): + if self._ignore_invalid_names: + return None raise ValueError("Empty filename.") targetpath = os.path.join(targetpath, arcname) @@ -2142,7 +2173,7 @@ def _write_end_record(self): " would require ZIP64 extensions") zip64endrec = struct.pack( structEndArchive64, stringEndArchive64, - 44, 45, 45, 0, 0, centDirCount, centDirCount, + sizeEndCentDir64 - 12, 45, 45, 0, 0, centDirCount, centDirCount, centDirSize, centDirOffset) self.fp.write(zip64endrec) @@ -2210,10 +2241,10 @@ def writepy(self, pathname, basename="", filterfunc=None): basename = name if self.debug: print("Adding package in", pathname, "as", basename) - fname, arcname = self._get_codename(initname[0:-3], basename) + arcname, bytecode = self._get_code(initname[0:-3], basename) if self.debug: print("Adding", arcname) - self.write(fname, arcname) + self.writestr(arcname, bytecode) dirlist = sorted(os.listdir(pathname)) dirlist.remove("__init__.py") # Add all *.py files and package subdirectories @@ -2230,11 +2261,10 @@ def writepy(self, pathname, basename="", filterfunc=None): if self.debug: print('file %r skipped by filterfunc' % path) continue - fname, arcname = self._get_codename(path[0:-3], - basename) + arcname, bytecode = self._get_code(path[0:-3], basename) if self.debug: print("Adding", arcname) - self.write(fname, arcname) + self.writestr(arcname, bytecode) else: # This is NOT a package directory, add its files at top level if self.debug: @@ -2247,108 +2277,65 @@ def writepy(self, pathname, basename="", filterfunc=None): if self.debug: print('file %r skipped by filterfunc' % path) continue - fname, arcname = self._get_codename(path[0:-3], - basename) + arcname, bytecode = self._get_code(path[0:-3], basename) if self.debug: print("Adding", arcname) - self.write(fname, arcname) + self.writestr(arcname, bytecode) else: if pathname[-3:] != ".py": raise RuntimeError( 'Files added with writepy() must end with ".py"') - fname, arcname = self._get_codename(pathname[0:-3], basename) + arcname, bytecode = self._get_code(pathname[0:-3], basename) if self.debug: print("Adding file", arcname) - self.write(fname, arcname) + self.writestr(arcname, bytecode) - def _get_codename(self, pathname, basename): - """Return (filename, archivename) for the path. + def _get_code(self, pathname, basename): + """Return (arcname, bytecode) for the path. - Given a module name path, return the correct file path and - archive name, compiling if necessary. For example, given - /python/lib/string, return (/python/lib/string.pyc, string). + Given a module name path, return the bytecode and archive + name. For example, given /python/lib/string, return + ('string', b'<bytecode of string>'). """ - def _compile(file, optimize=-1): - import py_compile - if self.debug: - print("Compiling", file) - try: - py_compile.compile(file, doraise=True, optimize=optimize) - except py_compile.PyCompileError as err: - print(err.msg) - return False - return True + import importlib._bootstrap_external + import importlib.machinery file_py = pathname + ".py" file_pyc = pathname + ".pyc" - pycache_opt0 = importlib.util.cache_from_source(file_py, optimization='') - pycache_opt1 = importlib.util.cache_from_source(file_py, optimization=1) - pycache_opt2 = importlib.util.cache_from_source(file_py, optimization=2) - if self._optimize == -1: - # legacy mode: use whatever file is present - if (os.path.isfile(file_pyc) and - os.stat(file_pyc).st_mtime >= os.stat(file_py).st_mtime): - # Use .pyc file. - arcname = fname = file_pyc - elif (os.path.isfile(pycache_opt0) and - os.stat(pycache_opt0).st_mtime >= os.stat(file_py).st_mtime): - # Use the __pycache__/*.pyc file, but write it to the legacy pyc - # file name in the archive. - fname = pycache_opt0 - arcname = file_pyc - elif (os.path.isfile(pycache_opt1) and - os.stat(pycache_opt1).st_mtime >= os.stat(file_py).st_mtime): - # Use the __pycache__/*.pyc file, but write it to the legacy pyc - # file name in the archive. - fname = pycache_opt1 - arcname = file_pyc - elif (os.path.isfile(pycache_opt2) and - os.stat(pycache_opt2).st_mtime >= os.stat(file_py).st_mtime): - # Use the __pycache__/*.pyc file, but write it to the legacy pyc - # file name in the archive. - fname = pycache_opt2 - arcname = file_pyc - else: - # Compile py into PEP 3147 pyc file. - if _compile(file_py): - if sys.flags.optimize == 0: - fname = pycache_opt0 - elif sys.flags.optimize == 1: - fname = pycache_opt1 - else: - fname = pycache_opt2 - arcname = file_pyc - else: - fname = arcname = file_py + archivename = os.path.split(file_pyc)[1] + + loader = importlib.machinery.SourceFileLoader('<py_compile>', file_py) + source_bytes = loader.get_data(file_py) + try: + if self.debug: + print("Compiling", file_py) + code = loader.source_to_code(source_bytes, archivename) + except Exception as err: + # Historically, this function prints messages here rather than raising + # (see test_zipfile.test_write_filtered_python_package) + from py_compile import PyCompileError + print(PyCompileError(type(err), err, file_py).msg) + + archivename = os.path.split(file_py)[1] + bytecode = source_bytes else: - # new mode: use given optimization level - if self._optimize == 0: - fname = pycache_opt0 - arcname = file_pyc - else: - arcname = file_pyc - if self._optimize == 1: - fname = pycache_opt1 - elif self._optimize == 2: - fname = pycache_opt2 - else: - msg = "invalid value for 'optimize': {!r}".format(self._optimize) - raise ValueError(msg) - if not (os.path.isfile(fname) and - os.stat(fname).st_mtime >= os.stat(file_py).st_mtime): - if not _compile(file_py, optimize=self._optimize): - fname = arcname = file_py - archivename = os.path.split(arcname)[1] + # Historically this function has used timestamp comparisons, so we + # keep using it until someone makes that specific improvement. + source_stats = loader.path_stats(file_py) + bytecode = importlib._bootstrap_external._code_to_timestamp_pyc( + code, source_stats['mtime'], source_stats['size']) + if basename: archivename = "%s/%s" % (basename, archivename) - return (fname, archivename) + + return archivename, bytecode def main(args=None): import argparse description = 'A simple command-line interface for zipfile module.' - parser = argparse.ArgumentParser(description=description, color=True) + parser = argparse.ArgumentParser(description=description) group = parser.add_mutually_exclusive_group(required=True) group.add_argument('-l', '--list', metavar='<zipfile>', help='Show listing of a zipfile') @@ -2361,7 +2348,7 @@ def main(args=None): group.add_argument('-t', '--test', metavar='<zipfile>', help='Test if a zipfile is valid') parser.add_argument('--metadata-encoding', metavar='<encoding>', - help='Specify encoding of member names for -l, -e and -t') + help='Specify encoding of member names for `-l`, `-e` and `-t`') args = parser.parse_args(args) encoding = args.metadata_encoding diff --git a/Lib/zipimport.py b/Lib/zipimport.py index 444c9dd11d8672..19279d1c2bea36 100644 --- a/Lib/zipimport.py +++ b/Lib/zipimport.py @@ -10,15 +10,12 @@ to Zip archives. """ -#from importlib import _bootstrap_external -#from importlib import _bootstrap # for _verbose_message import _frozen_importlib_external as _bootstrap_external from _frozen_importlib_external import _unpack_uint16, _unpack_uint32, _unpack_uint64 import _frozen_importlib as _bootstrap # for _verbose_message import _imp # for check_hash_based_pycs import _io # for open import marshal # for loads -import sys # for modules import time # for mktime __all__ = ['ZipImportError', 'zipimporter'] @@ -34,8 +31,6 @@ class ZipImportError(ImportError): # _read_directory() cache _zip_directory_cache = {} -_module_type = type(sys) - END_CENTRAL_DIR_SIZE = 22 END_CENTRAL_DIR_SIZE_64 = 56 END_CENTRAL_DIR_LOCATOR_SIZE_64 = 20 @@ -210,52 +205,6 @@ def is_package(self, fullname): return mi - # Load and return the module named by 'fullname'. - def load_module(self, fullname): - """load_module(fullname) -> module. - - Load the module specified by 'fullname'. 'fullname' must be the - fully qualified (dotted) module name. It returns the imported - module, or raises ZipImportError if it could not be imported. - - Deprecated since Python 3.10. Use exec_module() instead. - """ - import warnings - warnings._deprecated("zipimport.zipimporter.load_module", - f"{warnings._DEPRECATED_MSG}; " - "use zipimport.zipimporter.exec_module() instead", - remove=(3, 15)) - code, ispackage, modpath = _get_module_code(self, fullname) - mod = sys.modules.get(fullname) - if mod is None or not isinstance(mod, _module_type): - mod = _module_type(fullname) - sys.modules[fullname] = mod - mod.__loader__ = self - - try: - if ispackage: - # add __path__ to the module *before* the code gets - # executed - path = _get_module_path(self, fullname) - fullpath = _bootstrap_external._path_join(self.archive, path) - mod.__path__ = [fullpath] - - if not hasattr(mod, '__builtins__'): - mod.__builtins__ = __builtins__ - _bootstrap_external._fix_up_module(mod.__dict__, fullname, modpath) - exec(code, mod.__dict__) - except: - del sys.modules[fullname] - raise - - try: - mod = sys.modules[fullname] - except KeyError: - raise ImportError(f'Loaded module {fullname!r} not found in sys.modules') - _bootstrap._verbose_message('import {} # loaded from Zip {}', fullname, modpath) - return mod - - def get_resource_reader(self, fullname): """Return the ResourceReader for a module in a zip file.""" from importlib.readers import ZipReader @@ -603,11 +552,16 @@ def _read_directory(archive): ) _importing_zlib = False +_zlib_decompress = None # Return the zlib.decompress function object, or NULL if zlib couldn't # be imported. The function is cached when found, so subsequent calls # don't import zlib again. -def _get_decompress_func(): +def _get_zlib_decompress_func(): + global _zlib_decompress + if _zlib_decompress: + return _zlib_decompress + global _importing_zlib if _importing_zlib: # Someone has a zlib.py[co] in their Zip file @@ -617,7 +571,7 @@ def _get_decompress_func(): _importing_zlib = True try: - from zlib import decompress + from zlib import decompress as _zlib_decompress except Exception: _bootstrap._verbose_message('zipimport: zlib UNAVAILABLE') raise ZipImportError("can't decompress data; zlib not available") @@ -625,7 +579,54 @@ def _get_decompress_func(): _importing_zlib = False _bootstrap._verbose_message('zipimport: zlib available') - return decompress + return _zlib_decompress + + +_importing_zstd = False +_zstd_decompressor_class = None + +# Return the _zstd.ZstdDecompressor function object, or NULL if _zstd couldn't +# be imported. The result is cached when found. +def _get_zstd_decompressor_class(): + global _zstd_decompressor_class + if _zstd_decompressor_class: + return _zstd_decompressor_class + + global _importing_zstd + if _importing_zstd: + # Someone has a _zstd.py[co] in their Zip file + # let's avoid a stack overflow. + _bootstrap._verbose_message("zipimport: zstd UNAVAILABLE") + raise ZipImportError("can't decompress data; zstd not available") + + _importing_zstd = True + try: + from _zstd import ZstdDecompressor as _zstd_decompressor_class + except Exception: + _bootstrap._verbose_message("zipimport: zstd UNAVAILABLE") + raise ZipImportError("can't decompress data; zstd not available") + finally: + _importing_zstd = False + + _bootstrap._verbose_message("zipimport: zstd available") + return _zstd_decompressor_class + + +def _zstd_decompress(data): + # A simple version of compression.zstd.decompress() as we cannot import + # that here as the stdlib itself could be being zipimported. + results = [] + while True: + decomp = _get_zstd_decompressor_class()() + results.append(decomp.decompress(data)) + if not decomp.eof: + raise ZipImportError("zipimport: zstd compressed data ended before " + "the end-of-stream marker") + data = decomp.unused_data + if not data: + break + return b"".join(results) + # Given a path to a Zip file and a toc_entry, return the (uncompressed) data. def _get_data(archive, toc_entry): @@ -659,16 +660,23 @@ def _get_data(archive, toc_entry): if len(raw_data) != data_size: raise OSError("zipimport: can't read data") - if compress == 0: - # data is not compressed - return raw_data - - # Decompress with zlib - try: - decompress = _get_decompress_func() - except Exception: - raise ZipImportError("can't decompress data; zlib not available") - return decompress(raw_data, -15) + match compress: + case 0: # stored + return raw_data + case 8: # deflate aka zlib + try: + decompress = _get_zlib_decompress_func() + except Exception: + raise ZipImportError("can't decompress data; zlib not available") + return decompress(raw_data, -15) + case 93: # zstd + try: + return _zstd_decompress(raw_data) + except Exception: + raise ZipImportError("could not decompress zstd data") + # bz2 and lzma could be added, but are largely obsolete. + case _: + raise ZipImportError(f"zipimport: unsupported compression {compress}") # Lenient date/time comparison function. The precision of the mtime @@ -734,9 +742,9 @@ def _normalize_line_endings(source): # Given a string buffer containing Python source code, compile it # and return a code object. -def _compile_source(pathname, source): +def _compile_source(pathname, source, module): source = _normalize_line_endings(source) - return compile(source, pathname, 'exec', dont_inherit=True) + return compile(source, pathname, 'exec', dont_inherit=True, module=module) # Convert the date/time values found in the Zip archive to a value # that's compatible with the time stamp stored in .pyc files. @@ -807,7 +815,7 @@ def _get_module_code(self, fullname): except ImportError as exc: import_error = exc else: - code = _compile_source(modpath, data) + code = _compile_source(modpath, data, fullname) if code is None: # bad magic number or non-matching mtime # in byte code, try next diff --git a/Lib/zoneinfo/__init__.py b/Lib/zoneinfo/__init__.py index f5510ee0497513..df2ae909f53cf2 100644 --- a/Lib/zoneinfo/__init__.py +++ b/Lib/zoneinfo/__init__.py @@ -12,7 +12,10 @@ try: from _zoneinfo import ZoneInfo -except ImportError: # pragma: nocover +except (ImportError, AttributeError): # pragma: nocover + # AttributeError: module 'datetime' has no attribute 'datetime_CAPI'. + # This happens when the '_datetime' module is not available and the + # pure Python implementation is used instead. from ._zoneinfo import ZoneInfo reset_tzpath = _tzpath.reset_tzpath diff --git a/Lib/zoneinfo/_common.py b/Lib/zoneinfo/_common.py index 03cc42149f9b74..98668c15d8bf94 100644 --- a/Lib/zoneinfo/_common.py +++ b/Lib/zoneinfo/_common.py @@ -67,6 +67,10 @@ def load_data(fobj): f">{timecnt}{time_type}", fobj.read(timecnt * time_size) ) trans_idx = struct.unpack(f">{timecnt}B", fobj.read(timecnt)) + + if max(trans_idx) >= typecnt: + raise ValueError("Invalid transition index found while reading TZif: " + f"{max(trans_idx)}") else: trans_list_utc = () trans_idx = () @@ -118,11 +122,10 @@ def get_abbr(idx): c = fobj.read(1) # Should be \n assert c == b"\n", c - tz_bytes = b"" - while (c := fobj.read(1)) != b"\n": - tz_bytes += c - - tz_str = tz_bytes + line = fobj.readline() + if not line.endswith(b"\n"): + raise ValueError("Invalid TZif file: unexpected end of file") + tz_str = line.rstrip(b"\n") else: tz_str = None diff --git a/Lib/zoneinfo/_tzpath.py b/Lib/zoneinfo/_tzpath.py index 5db17bea045d8c..3661c837daa0a4 100644 --- a/Lib/zoneinfo/_tzpath.py +++ b/Lib/zoneinfo/_tzpath.py @@ -13,6 +13,13 @@ def _reset_tzpath(to=None, stacklevel=4): + f"not {type(tzpaths)}: {tzpaths!r}" ) + tzpaths = [os.fspath(p) for p in tzpaths] + if not all(isinstance(p, str) for p in tzpaths): + raise TypeError( + "All elements of a tzpath sequence must be strings or " + "os.PathLike objects which convert to strings." + ) + if not all(map(os.path.isabs, tzpaths)): raise ValueError(_get_invalid_paths_message(tzpaths)) base_tzpath = tzpaths @@ -124,7 +131,8 @@ def available_timezones(): # Start with loading from the tzdata package if it exists: this has a # pre-assembled list of zones that only requires opening one file. try: - with resources.files("tzdata").joinpath("zones").open("r") as f: + zones_file = resources.files("tzdata").joinpath("zones") + with zones_file.open("r", encoding="utf-8") as f: for zone in f: zone = zone.strip() if zone: @@ -169,6 +177,10 @@ def valid_key(fpath): # posixrules is a special symlink-only time zone where it exists, it # should not be included in the output valid_zones.remove("posixrules") + if "localtime" in valid_zones: + # localtime is a special symlink-only time zone where it exists, it + # should not be included in the output + valid_zones.remove("localtime") return valid_zones diff --git a/Lib/zoneinfo/_zoneinfo.py b/Lib/zoneinfo/_zoneinfo.py index 3ffdb4c837192b..7063eb6a9025ac 100644 --- a/Lib/zoneinfo/_zoneinfo.py +++ b/Lib/zoneinfo/_zoneinfo.py @@ -47,7 +47,11 @@ def __new__(cls, key): cls._strong_cache[key] = cls._strong_cache.pop(key, instance) if len(cls._strong_cache) > cls._strong_cache_size: - cls._strong_cache.popitem(last=False) + try: + cls._strong_cache.popitem(last=False) + except KeyError: + # another thread may have already emptied the cache + pass return instance @@ -334,7 +338,7 @@ def _utcoff_to_dstoff(trans_idx, utcoffsets, isdsts): if not isdsts[comp_idx]: dstoff = utcoff - utcoffsets[comp_idx] - if not dstoff and idx < (typecnt - 1): + if not dstoff and idx < (typecnt - 1) and i + 1 < len(trans_idx): comp_idx = trans_idx[i + 1] # If the following transition is also DST and we couldn't diff --git a/Mac/BuildScript/README.rst b/Mac/BuildScript/README.rst index e44e74f3a49234..54341a81c26743 100644 --- a/Mac/BuildScript/README.rst +++ b/Mac/BuildScript/README.rst @@ -73,7 +73,7 @@ download them. - builds the following third-party libraries - * OpenSSL 3.0.x + * OpenSSL 3.5.x * Tcl/Tk 8.6.x * NCurses * SQLite diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py index b31cb766a468f4..394e42439f7710 100755 --- a/Mac/BuildScript/build-installer.py +++ b/Mac/BuildScript/build-installer.py @@ -246,9 +246,9 @@ def library_recipes(): result.extend([ dict( - name="OpenSSL 3.0.16", - url="https://github.com/openssl/openssl/releases/download/openssl-3.0.16/openssl-3.0.16.tar.gz", - checksum='57e03c50feab5d31b152af2b764f10379aecd8ee92f16c985983ce4a99f7ef86', + name="OpenSSL 3.5.7", + url="https://github.com/openssl/openssl/releases/download/openssl-3.5.7/openssl-3.5.7.tar.gz", + checksum="a8c0d28a529ca480f9f36cf5792e2cd21984552a3c8e4aa11a24aa31aeac98e8", buildrecipe=build_universal_openssl, configure=None, install=None, @@ -264,10 +264,10 @@ def library_recipes(): tk_patches = ['backport_gh71383_fix.patch', 'tk868_on_10_8_10_9.patch', 'backport_gh110950_fix.patch'] else: - tcl_tk_ver='8.6.16' - tcl_checksum='91cb8fa61771c63c262efb553059b7c7ad6757afa5857af6265e4b0bdc2a14a5' + tcl_tk_ver='9.0.3' + tcl_checksum='2537ba0c86112c8c953f7c09d33f134dd45c0fb3a71f2d7f7691fd301d2c33a6' - tk_checksum='be9f94d3575d4b3099d84bc3c10de8994df2d7aa405208173c709cc404a7e5fe' + tk_checksum='bf344efadb618babb7933f69275620f72454d1c8220130da93e3f7feb0efbf9b' tk_patches = [] @@ -359,9 +359,9 @@ def library_recipes(): ), ), dict( - name="SQLite 3.49.1", - url="https://sqlite.org/2025/sqlite-autoconf-3490100.tar.gz", - checksum="106642d8ccb36c5f7323b64e4152e9b719f7c0215acf5bfeac3d5e7f97b59254", + name="SQLite 3.53.2", + url="https://www.sqlite.org/2026/sqlite-autoconf-3530200.tar.gz", + checksum="588ad51949419a56ebe81fe56193d510c559eb94c9a57748387860b5d3069316", extra_cflags=('-Os ' '-DSQLITE_ENABLE_FTS5 ' '-DSQLITE_ENABLE_FTS4 ' @@ -378,9 +378,9 @@ def library_recipes(): install=f"make && ranlib libsqlite3.a && make install DESTDIR={shellQuote(os.path.join(WORKDIR, 'libraries'))}", ), dict( - name="libmpdec 4.0.0", - url="https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-4.0.0.tar.gz", - checksum="942445c3245b22730fd41a67a7c5c231d11cb1b9936b9c0f76334fb7d0b4468c", + name="libmpdec 4.0.1", + url="https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-4.0.1.tar.gz", + checksum="96d33abb4bb0070c7be0fed4246cd38416188325f820468214471938545b1ac8", configure_pre=[ "--disable-cxx", "MACHINE=universal", @@ -1158,7 +1158,6 @@ def buildPython(): print(" NOTE: --with-mimalloc=no pending resolution of weak linking issues") runCommand("%s -C --enable-framework --enable-universalsdk=/ " "--with-mimalloc=no " - "--with-system-libmpdec " "--with-universal-archs=%s " "%s " "%s " @@ -1747,7 +1746,7 @@ def main(): fn = os.path.join(folder, "ReadMe.rtf") patchFile("resources/ReadMe.rtf", fn) fn = os.path.join(folder, "Update Shell Profile.command") - patchScript("scripts/postflight.patch-profile", fn) + patchScript("resources/update_shell_profile.command", fn) fn = os.path.join(folder, "Install Certificates.command") patchScript("resources/install_certificates.command", fn) os.chmod(folder, STAT_0o755) diff --git a/Mac/BuildScript/resources/ReadMe.rtf b/Mac/BuildScript/resources/ReadMe.rtf index ee5ba4707dfea4..d81bcd5c745baa 100644 --- a/Mac/BuildScript/resources/ReadMe.rtf +++ b/Mac/BuildScript/resources/ReadMe.rtf @@ -19,7 +19,7 @@ \f1\b \cf0 \ul \ulc0 Certificate verification and OpenSSL\ \f0\b0 \ulnone \ -This package includes its own private copy of OpenSSL 3.0. The trust certificates in system and user keychains managed by the +This package includes its own private copy of OpenSSL 3.5. The trust certificates in system and user keychains managed by the \f2\i Keychain Access \f0\i0 application and the \f2\i security diff --git a/Mac/BuildScript/resources/install_certificates.command b/Mac/BuildScript/resources/install_certificates.command index 19b4adac07bb1d..700eb462b68c18 100755 --- a/Mac/BuildScript/resources/install_certificates.command +++ b/Mac/BuildScript/resources/install_certificates.command @@ -25,7 +25,8 @@ def main(): print(" -- pip install --upgrade certifi") subprocess.check_call([sys.executable, - "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"]) + "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi", + "--disable-pip-version-check"]) import certifi diff --git a/Mac/BuildScript/resources/update_shell_profile.command b/Mac/BuildScript/resources/update_shell_profile.command new file mode 100755 index 00000000000000..3cf4d74de9f09a --- /dev/null +++ b/Mac/BuildScript/resources/update_shell_profile.command @@ -0,0 +1,116 @@ +#!/bin/sh + +echo "This script will update your shell profile when the 'bin' directory" +echo "of python is not early enough of the PATH of your shell." +echo "These changes will be effective only in shell windows that you open" +echo "after running this script." + +PYVER=@PYVER@ +PYTHON_ROOT="/Library/Frameworks/Python.framework/Versions/@PYVER@" + +if [ `id -ur` = 0 ]; then + # Run from the installer, do some trickery to fetch the information + # we need. + theShell="`finger $USER | grep Shell: | head -1 | awk '{ print $NF }'`" + +else + theShell="${SHELL}" +fi + +# Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH. +BSH="`basename "${theShell}"`" +case "${BSH}" in +bash|ksh|sh|*csh|zsh|fish) + if [ `id -ur` = 0 ]; then + P=`su - ${USER} -c 'echo A-X-4-X@@$PATH@@X-4-X-A' | grep 'A-X-4-X@@.*@@X-4-X-A' | sed -e 's/^A-X-4-X@@//g' -e 's/@@X-4-X-A$//g'` + else + P="`(exec -l ${theShell} -c 'echo $PATH')`" + fi + ;; +*) + echo "Sorry, I don't know how to patch $BSH shells" + exit 0 + ;; +esac + +# Now ensure that our bin directory is on $P and before /usr/bin at that +for elem in `echo $P | tr ':' ' '` +do + if [ "${elem}" = "${PYTHON_ROOT}/bin" ]; then + echo "All right, you're a python lover already" + exit 0 + elif [ "${elem}" = "/usr/bin" ]; then + break + fi +done + +echo "${PYTHON_ROOT}/bin is not on your PATH or at least not early enough" +case "${BSH}" in +*csh) + if [ -f "${HOME}/.tcshrc" ]; then + RC="${HOME}/.tcshrc" + else + RC="${HOME}/.cshrc" + fi + # Create backup copy before patching + if [ -f "${RC}" ]; then + cp -fp "${RC}" "${RC}.pysave" + fi + echo "" >> "${RC}" + echo "# Setting PATH for Python ${PYVER}" >> "${RC}" + echo "# The original version is saved in .cshrc.pysave" >> "${RC}" + echo "set path=(${PYTHON_ROOT}/bin "'$path'")" >> "${RC}" + if [ `id -ur` = 0 ]; then + chown -h "${USER}" "${RC}" + fi + exit 0 + ;; +bash) + if [ -e "${HOME}/.bash_profile" ]; then + PR="${HOME}/.bash_profile" + elif [ -e "${HOME}/.bash_login" ]; then + PR="${HOME}/.bash_login" + elif [ -e "${HOME}/.profile" ]; then + PR="${HOME}/.profile" + else + PR="${HOME}/.bash_profile" + fi + ;; +fish) + CONFIG_DIR="${HOME}/.config/fish/conf.d/" + RC="${CONFIG_DIR}/python-${PYVER}.fish" + mkdir -p "$CONFIG_DIR" + if [ -f "${RC}" ]; then + cp -fp "${RC}" "${RC}.pysave" + fi + echo "# Setting PATH for Python ${PYVER}" > "${RC}" + if [ -f "${RC}.pysave" ]; then + echo "# The original version is saved in ${RC}.pysave" >> "${RC}" + fi + echo "fish_add_path -g \"${PYTHON_ROOT}/bin\"" >> "${RC}" + if [ `id -ur` = 0 ]; then + chown -h "${USER}" "${RC}" + fi + exit 0 + ;; +zsh) + PR="${HOME}/.zprofile" + ;; +*sh) + PR="${HOME}/.profile" + ;; +esac + +# Create backup copy before patching +if [ -f "${PR}" ]; then + cp -fp "${PR}" "${PR}.pysave" +fi +echo "" >> "${PR}" +echo "# Setting PATH for Python ${PYVER}" >> "${PR}" +echo "# The original version is saved in `basename ${PR}`.pysave" >> "${PR}" +echo 'PATH="'"${PYTHON_ROOT}/bin"':${PATH}"' >> "${PR}" +echo 'export PATH' >> "${PR}" +if [ `id -ur` = 0 ]; then + chown -h "${USER}" "${PR}" +fi +exit 0 diff --git a/Mac/BuildScript/scripts/postflight.patch-profile b/Mac/BuildScript/scripts/postflight.patch-profile index 9caf62211ddd16..ce8720f895d1b5 100755 --- a/Mac/BuildScript/scripts/postflight.patch-profile +++ b/Mac/BuildScript/scripts/postflight.patch-profile @@ -1,116 +1,104 @@ #!/bin/sh -echo "This script will update your shell profile when the 'bin' directory" -echo "of python is not early enough of the PATH of your shell." -echo "These changes will be effective only in shell windows that you open" -echo "after running this script." - PYVER=@PYVER@ PYTHON_ROOT="/Library/Frameworks/Python.framework/Versions/@PYVER@" -if [ `id -ur` = 0 ]; then - # Run from the installer, do some trickery to fetch the information - # we need. - theShell="`finger $USER | grep Shell: | head -1 | awk '{ print $NF }'`" -else - theShell="${SHELL}" -fi +# Run from the installer, do some trickery to fetch the information +# we need. +theShell="`finger $USER | grep Shell: | head -1 | awk '{ print $NF }'`" # Make sure the directory ${PYTHON_ROOT}/bin is on the users PATH. BSH="`basename "${theShell}"`" case "${BSH}" in bash|ksh|sh|*csh|zsh|fish) - if [ `id -ur` = 0 ]; then - P=`su - ${USER} -c 'echo A-X-4-X@@$PATH@@X-4-X-A' | grep 'A-X-4-X@@.*@@X-4-X-A' | sed -e 's/^A-X-4-X@@//g' -e 's/@@X-4-X-A$//g'` - else - P="`(exec -l ${theShell} -c 'echo $PATH')`" - fi - ;; + true + ;; *) - echo "Sorry, I don't know how to patch $BSH shells" - exit 0 - ;; + exit 0 + ;; esac -# Now ensure that our bin directory is on $P and before /usr/bin at that -for elem in `echo $P | tr ':' ' '` -do - if [ "${elem}" = "${PYTHON_ROOT}/bin" ]; then - echo "All right, you're a python lover already" - exit 0 - elif [ "${elem}" = "/usr/bin" ]; then - break - fi -done - -echo "${PYTHON_ROOT}/bin is not on your PATH or at least not early enough" case "${BSH}" in *csh) - if [ -f "${HOME}/.tcshrc" ]; then - RC="${HOME}/.tcshrc" - else - RC="${HOME}/.cshrc" - fi - # Create backup copy before patching - if [ -f "${RC}" ]; then - cp -fp "${RC}" "${RC}.pysave" - fi - echo "" >> "${RC}" - echo "# Setting PATH for Python ${PYVER}" >> "${RC}" - echo "# The original version is saved in .cshrc.pysave" >> "${RC}" - echo "set path=(${PYTHON_ROOT}/bin "'$path'")" >> "${RC}" - if [ `id -ur` = 0 ]; then - chown "${USER}" "${RC}" - fi - exit 0 - ;; + if [ -f "${HOME}/.tcshrc" ]; then + RC="${HOME}/.tcshrc" + else + RC="${HOME}/.cshrc" + fi + + # Drop privileges while writing files. + su -m ${USER} <<EOFC + # Create backup copy before patching + if [ -f "${RC}" ]; then + cp -fp "${RC}" "${RC}.pysave" + fi + echo "" >> "${RC}" + echo "# Setting PATH for Python ${PYVER}" >> "${RC}" + echo "# The original version is saved in .cshrc.pysave" >> "${RC}" + echo "set path=(${PYTHON_ROOT}/bin "'\$path'")" >> "${RC}" +EOFC + + if [ `id -ur` = 0 ]; then + chown -h "${USER}" "${RC}" + fi + exit 0 + ;; bash) - if [ -e "${HOME}/.bash_profile" ]; then - PR="${HOME}/.bash_profile" - elif [ -e "${HOME}/.bash_login" ]; then - PR="${HOME}/.bash_login" - elif [ -e "${HOME}/.profile" ]; then - PR="${HOME}/.profile" - else - PR="${HOME}/.bash_profile" - fi - ;; + if [ -e "${HOME}/.bash_profile" ]; then + PR="${HOME}/.bash_profile" + elif [ -e "${HOME}/.bash_login" ]; then + PR="${HOME}/.bash_login" + elif [ -e "${HOME}/.profile" ]; then + PR="${HOME}/.profile" + else + PR="${HOME}/.bash_profile" + fi + ;; fish) - CONFIG_DIR="${HOME}/.config/fish/conf.d/" - RC="${CONFIG_DIR}/python-${PYVER}.fish" - mkdir -p "$CONFIG_DIR" - if [ -f "${RC}" ]; then - cp -fp "${RC}" "${RC}.pysave" - fi - echo "# Setting PATH for Python ${PYVER}" > "${RC}" - if [ -f "${RC}.pysave" ]; then - echo "# The original version is saved in ${RC}.pysave" >> "${RC}" - fi - echo "fish_add_path -g \"${PYTHON_ROOT}/bin\"" >> "${RC}" - if [ `id -ur` = 0 ]; then - chown "${USER}" "${RC}" - fi - exit 0 - ;; + CONFIG_DIR="${HOME}/.config/fish/conf.d/" + RC="${CONFIG_DIR}/python-${PYVER}.fish" + + # Drop privileges while writing files. + su -m ${USER} <<EOFF + mkdir -p "$CONFIG_DIR" + if [ -f "${RC}" ]; then + cp -fp "${RC}" "${RC}.pysave" + fi + echo "# Setting PATH for Python ${PYVER}" > "${RC}" + if [ -f "${RC}.pysave" ]; then + echo "# The original version is saved in ${RC}.pysave" >> "${RC}" + fi + echo "fish_add_path -g \"${PYTHON_ROOT}/bin\"" >> "${RC}" +EOFF + + if [ `id -ur` = 0 ]; then + chown -h "${USER}" "${RC}" + fi + exit 0 + ;; zsh) - PR="${HOME}/.zprofile" - ;; + PR="${HOME}/.zprofile" + ;; *sh) - PR="${HOME}/.profile" - ;; + PR="${HOME}/.profile" + ;; esac +# Drop privileges while writing files. +su -m ${USER} <<EOFS # Create backup copy before patching if [ -f "${PR}" ]; then - cp -fp "${PR}" "${PR}.pysave" + cp -fp "${PR}" "${PR}.pysave" fi echo "" >> "${PR}" echo "# Setting PATH for Python ${PYVER}" >> "${PR}" echo "# The original version is saved in `basename ${PR}`.pysave" >> "${PR}" -echo 'PATH="'"${PYTHON_ROOT}/bin"':${PATH}"' >> "${PR}" +echo 'PATH="'"${PYTHON_ROOT}/bin"':\${PATH}"' >> "${PR}" echo 'export PATH' >> "${PR}" +EOFS + if [ `id -ur` = 0 ]; then - chown "${USER}" "${PR}" + chown -h "${USER}" "${PR}" fi exit 0 diff --git a/Mac/IDLE/IDLE.app/Contents/Info.plist b/Mac/IDLE/IDLE.app/Contents/Info.plist index 8549e405e2a65a..696625e64cdf32 100644 --- a/Mac/IDLE/IDLE.app/Contents/Info.plist +++ b/Mac/IDLE/IDLE.app/Contents/Info.plist @@ -1,9 +1,23 @@ <?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> - <key>CFBundleDevelopmentRegion</key> - <string>English</string> + <key>CFBundleName</key> + <string>IDLE</string> + <key>CFBundleIdentifier</key> + <string>org.python.IDLE</string> + <key>CFBundleVersion</key> + <string>%version%</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleExecutable</key> + <string>IDLE</string> + <key>CFBundleIconFile</key> + <string>IDLE.icns</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> <key>CFBundleDocumentTypes</key> <array> <dict> @@ -34,26 +48,16 @@ <string>Editor</string> </dict> </array> - <key>CFBundleExecutable</key> - <string>IDLE</string> - <key>CFBundleGetInfoString</key> - <string>%version%, © 2001-2024 Python Software Foundation</string> - <key>CFBundleIconFile</key> - <string>IDLE.icns</string> - <key>CFBundleIdentifier</key> - <string>org.python.IDLE</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>IDLE</string> - <key>CFBundlePackageType</key> - <string>APPL</string> <key>CFBundleShortVersionString</key> <string>%version%</string> - <key>CFBundleSignature</key> - <string>????</string> - <key>CFBundleVersion</key> - <string>%version%</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>NSHumanReadableCopyright</key> + <string>Copyright © 2001 Python Software Foundation. All rights reserved.</string> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> <key>NSHighResolutionCapable</key> <true/> <key>CFBundleAllowMixedLocalizations</key> diff --git a/Mac/PythonLauncher/Info.plist.in b/Mac/PythonLauncher/Info.plist.in index ce8f27cd7d4de7..dd63187ab836b3 100644 --- a/Mac/PythonLauncher/Info.plist.in +++ b/Mac/PythonLauncher/Info.plist.in @@ -2,8 +2,22 @@ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> - <key>CFBundleDevelopmentRegion</key> - <string>en</string> + <key>CFBundleName</key> + <string>Python Launcher</string> + <key>CFBundleIdentifier</key> + <string>org.python.PythonLauncher</string> + <key>CFBundleVersion</key> + <string>%VERSION%</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>PytL</string> + <key>CFBundleExecutable</key> + <string>Python Launcher</string> + <key>CFBundleIconFile</key> + <string>PythonLauncher.icns</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> <key>CFBundleDocumentTypes</key> <array> <dict> @@ -37,28 +51,16 @@ <string>MyDocument</string> </dict> </array> - <key>CFBundleExecutable</key> - <string>Python Launcher</string> - <key>NSHumanReadableCopyright</key> - <string>Copyright © 2001 Python Software Foundation</string> - <key>CFBundleGetInfoString</key> - <string>%VERSION%, © 2001 Python Software Foundation</string> - <key>CFBundleIconFile</key> - <string>PythonLauncher.icns</string> - <key>CFBundleIdentifier</key> - <string>org.python.PythonLauncher</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>Python Launcher</string> - <key>CFBundlePackageType</key> - <string>APPL</string> <key>CFBundleShortVersionString</key> <string>%VERSION%</string> - <key>CFBundleSignature</key> - <string>PytL</string> - <key>CFBundleVersion</key> - <string>%VERSION%</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>NSHumanReadableCopyright</key> + <string>Copyright © 2001 Python Software Foundation. All rights reserved.</string> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> <key>NSMainNibFile</key> <string>MainMenu</string> <key>NSPrincipalClass</key> diff --git a/Mac/Resources/app-store-compliance.patch b/Mac/Resources/app-store-compliance.patch index f4b7decc01cf1f..e5d1e7da866e4d 100644 --- a/Mac/Resources/app-store-compliance.patch +++ b/Mac/Resources/app-store-compliance.patch @@ -1,21 +1,21 @@ diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py -index d6c83a75c1c..19ed4e01091 100644 +index 49a292df934..e1669a0c9b2 100644 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py -@@ -237,11 +237,6 @@ def test_roundtrips(self): - '','',''), +@@ -282,11 +282,6 @@ def test_qs(self, orig, expect): + None,None,None), ('git+ssh', 'git@github.com','/user/project.git', - '', '')), + None, None)), - ('itms-services://?action=download-manifest&url=https://example.com/app', -- ('itms-services', '', '', '', -- 'action=download-manifest&url=https://example.com/app', ''), +- ('itms-services', '', '', None, +- 'action=download-manifest&url=https://example.com/app', None), - ('itms-services', '', '', -- 'action=download-manifest&url=https://example.com/app', '')), +- 'action=download-manifest&url=https://example.com/app', None)), ('+scheme:path/to/file', - ('', '', '+scheme:path/to/file', '', '', ''), - ('', '', '+scheme:path/to/file', '', '')), + (None, None, '+scheme:path/to/file', None, None, None), + (None, None, '+scheme:path/to/file', None, None)), diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py -index 8f724f907d4..148caf742c9 100644 +index e917f8b61bb..8575172573f 100644 --- a/Lib/urllib/parse.py +++ b/Lib/urllib/parse.py @@ -59,7 +59,7 @@ diff --git a/Mac/Resources/app/Info.plist.in b/Mac/Resources/app/Info.plist.in index a1fc1511c40e96..07dc351398d7b2 100644 --- a/Mac/Resources/app/Info.plist.in +++ b/Mac/Resources/app/Info.plist.in @@ -1,9 +1,23 @@ <?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> - <key>CFBundleDevelopmentRegion</key> - <string>English</string> + <key>CFBundleName</key> + <string>Python</string> + <key>CFBundleIdentifier</key> + <string>%bundleid%</string> + <key>CFBundleVersion</key> + <string>%version%</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>PytX</string> + <key>CFBundleExecutable</key> + <string>Python</string> + <key>CFBundleIconFile</key> + <string>PythonInterpreter.icns</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> <key>CFBundleDocumentTypes</key> <array> <dict> @@ -17,48 +31,33 @@ <string>Viewer</string> </dict> </array> - <key>CFBundleExecutable</key> - <string>Python</string> - <key>CFBundleGetInfoString</key> - <string>%version%, (c) 2001-2024 Python Software Foundation.</string> + <key>CFBundleShortVersionString</key> + <string>%version%</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>NSHumanReadableCopyright</key> + <string>Copyright © 2001 Python Software Foundation. All rights reserved.</string> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>NSHighResolutionCapable</key> + <true/> + <key>NSAppleScriptEnabled</key> + <true/> + <key>CFBundleAllowMixedLocalizations</key> + <true/> <key>CFBundleHelpBookFolder</key> <array> <string>Documentation</string> - <string>PythonDocumentation</string> </array> <key>CFBundleHelpBookName</key> - <string>MacPython Help</string> + <string>Python Help</string> <key>CFBundleHelpTOCFile</key> <string>index.html</string> - <key>CFBundleIconFile</key> - <string>PythonInterpreter.icns</string> - <key>CFBundleIdentifier</key> - <string>%bundleid%</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleLongVersionString</key> - <string>%version%, (c) 2001-2024 Python Software Foundation.</string> - <key>CFBundleName</key> - <string>Python</string> - <key>CFBundlePackageType</key> - <string>APPL</string> - <key>CFBundleShortVersionString</key> - <string>%version%</string> - <key>CFBundleSignature</key> - <string>PytX</string> - <key>CFBundleVersion</key> - <string>%version%</string> <key>CSResourcesFileMapped</key> <true/> <key>LSRequiresCarbon</key> <true/> - <key>NSAppleScriptEnabled</key> - <true/> - <key>NSHumanReadableCopyright</key> - <string>(c) 2001-2024 Python Software Foundation.</string> - <key>NSHighResolutionCapable</key> - <true/> - <key>CFBundleAllowMixedLocalizations</key> - <true/> </dict> </plist> diff --git a/Mac/Resources/framework/Info.plist.in b/Mac/Resources/framework/Info.plist.in index 4c42971ed90ee4..41400c91a5f1b0 100644 --- a/Mac/Resources/framework/Info.plist.in +++ b/Mac/Resources/framework/Info.plist.in @@ -1,29 +1,31 @@ <?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> -<plist version="0.9"> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> <dict> - <key>CFBundleDevelopmentRegion</key> - <string>English</string> - <key>CFBundleExecutable</key> + <key>CFBundleName</key> <string>Python</string> - <key>CFBundleGetInfoString</key> - <string>Python Runtime and Library</string> <key>CFBundleIdentifier</key> <string>@PYTHONFRAMEWORKIDENTIFIER@</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>Python</string> + <key>CFBundleVersion</key> + <string>%VERSION%</string> <key>CFBundlePackageType</key> <string>FMWK</string> - <key>CFBundleShortVersionString</key> - <string>%VERSION%, (c) 2001-2024 Python Software Foundation.</string> - <key>CFBundleLongVersionString</key> - <string>%VERSION%, (c) 2001-2024 Python Software Foundation.</string> <key>CFBundleSignature</key> <string>????</string> - <key>CFBundleVersion</key> + <key>CFBundleExecutable</key> + <string>Python</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleShortVersionString</key> <string>%VERSION%</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> + <key>NSHumanReadableCopyright</key> + <string>Copyright © 2001 Python Software Foundation. All rights reserved.</string> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> <key>CFBundleAllowMixedLocalizations</key> <true/> </dict> diff --git a/Makefile.pre.in b/Makefile.pre.in index 0c070131cda200..2b34b009fd745a 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -123,9 +123,15 @@ PY_STDMODULE_CFLAGS= $(PY_CFLAGS) $(PY_CFLAGS_NODIST) $(PY_CPPFLAGS) $(CFLAGSFOR PY_BUILTIN_MODULE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE_BUILTIN PY_CORE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE # Linker flags used for building the interpreter object files +# In particular, EXE_LDFLAGS is an extra flag to provide fine grain distinction between +# LDFLAGS used to build executables and shared targets. PY_CORE_LDFLAGS=$(PY_LDFLAGS) $(PY_LDFLAGS_NODIST) +CONFIGURE_EXE_LDFLAGS=@EXE_LDFLAGS@ +PY_CORE_EXE_LDFLAGS:= $(if $(CONFIGURE_EXE_LDFLAGS), $(CONFIGURE_EXE_LDFLAGS) $(PY_LDFLAGS_NODIST), $(PY_CORE_LDFLAGS)) # Strict or non-strict aliasing flags used to compile dtoa.c, see above CFLAGS_ALIASING=@CFLAGS_ALIASING@ +# Compilation flags only for ceval.c. +CFLAGS_CEVAL=@CFLAGS_CEVAL@ # Machine-dependent subdirectories @@ -166,7 +172,7 @@ WHEEL_PKG_DIR= @WHEEL_PKG_DIR@ # Detailed destination directories BINLIBDEST= @BINLIBDEST@ -LIBDEST= $(SCRIPTDIR)/python$(VERSION)$(ABI_THREAD) +LIBDEST= @LIBDEST@ INCLUDEPY= $(INCLUDEDIR)/python$(LDVERSION) CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(LDVERSION) @@ -209,6 +215,8 @@ MACOSX_DEPLOYMENT_TARGET=@CONFIGURE_MACOSX_DEPLOYMENT_TARGET@ # the build, and is only listed here so it will be included in sysconfigdata. IPHONEOS_DEPLOYMENT_TARGET=@IPHONEOS_DEPLOYMENT_TARGET@ +BUILD_DETAILS=@BUILD_DETAILS@ + # Option to install to strip binaries STRIPFLAG=-s @@ -225,7 +233,6 @@ RUNSHARED= @RUNSHARED@ ENSUREPIP= @ENSUREPIP@ # Internal static libraries -LIBMPDEC_A= Modules/_decimal/libmpdec/libmpdec.a LIBEXPAT_A= Modules/expat/libexpat.a # HACL* build configuration @@ -284,6 +291,7 @@ LDLIBRARYDIR= @LDLIBRARYDIR@ INSTSONAME= @INSTSONAME@ LIBRARY_DEPS= @LIBRARY_DEPS@ LINK_PYTHON_DEPS=@LINK_PYTHON_DEPS@ +JIT_OBJS= @JIT_SHIM_O@ PY_ENABLE_SHARED= @PY_ENABLE_SHARED@ STATIC_LIBPYTHON= @STATIC_LIBPYTHON@ @@ -463,6 +471,8 @@ PYTHON_OBJS= \ Python/instruction_sequence.o \ Python/intrinsics.o \ Python/jit.o \ + Python/jit_publish.o \ + $(JIT_OBJS) \ Python/legacy_tracing.o \ Python/lock.o \ Python/marshal.o \ @@ -483,11 +493,14 @@ PYTHON_OBJS= \ Python/pylifecycle.o \ Python/pymath.o \ Python/pystate.o \ + Python/pystats.o \ Python/pythonrun.o \ Python/pytime.o \ Python/qsbr.o \ Python/bootstrap_hash.o \ Python/specialize.o \ + Python/slots.o \ + Python/slots_generated.o \ Python/stackrefs.o \ Python/structmember.o \ Python/symtable.o \ @@ -501,11 +514,11 @@ PYTHON_OBJS= \ Python/pystrtod.o \ Python/pystrhex.o \ Python/dtoa.o \ - Python/formatter_unicode.o \ Python/fileutils.o \ Python/suggestions.o \ Python/perf_trampoline.o \ Python/perf_jit_trampoline.o \ + Python/jit_unwind.o \ Python/remote_debugging.o \ Python/$(DYNLOADFILE) \ $(LIBOBJS) \ @@ -539,6 +552,7 @@ OBJECT_OBJS= \ Objects/funcobject.o \ Objects/interpolationobject.o \ Objects/iterobject.o \ + Objects/lazyimportobject.o \ Objects/listobject.o \ Objects/longobject.o \ Objects/dictobject.o \ @@ -551,6 +565,7 @@ OBJECT_OBJS= \ Objects/obmalloc.o \ Objects/picklebufobject.o \ Objects/rangeobject.o \ + Objects/sentinelobject.o \ Objects/setobject.o \ Objects/sliceobject.o \ Objects/structseq.o \ @@ -558,8 +573,11 @@ OBJECT_OBJS= \ Objects/tupleobject.o \ Objects/typeobject.o \ Objects/typevarobject.o \ - Objects/unicodeobject.o \ + Objects/unicode_format.o \ + Objects/unicode_formatter.o \ + Objects/unicode_writer.o \ Objects/unicodectype.o \ + Objects/unicodeobject.o \ Objects/unionobject.o \ Objects/weakrefobject.o \ @PERF_TRAMPOLINE_OBJ@ @@ -589,45 +607,6 @@ LINK_PYTHON_OBJS=@LINK_PYTHON_OBJS@ DTRACE_DEPS = \ Python/ceval.o Python/gc.o Python/import.o Python/sysmodule.o -########################################################################## -# decimal's libmpdec - -LIBMPDEC_OBJS= \ - Modules/_decimal/libmpdec/basearith.o \ - Modules/_decimal/libmpdec/constants.o \ - Modules/_decimal/libmpdec/context.o \ - Modules/_decimal/libmpdec/convolute.o \ - Modules/_decimal/libmpdec/crt.o \ - Modules/_decimal/libmpdec/difradix2.o \ - Modules/_decimal/libmpdec/fnt.o \ - Modules/_decimal/libmpdec/fourstep.o \ - Modules/_decimal/libmpdec/io.o \ - Modules/_decimal/libmpdec/mpalloc.o \ - Modules/_decimal/libmpdec/mpdecimal.o \ - Modules/_decimal/libmpdec/numbertheory.o \ - Modules/_decimal/libmpdec/sixstep.o \ - Modules/_decimal/libmpdec/transpose.o - # _decimal does not use signaling API - # Modules/_decimal/libmpdec/mpsignal.o - -LIBMPDEC_HEADERS= \ - $(srcdir)/Modules/_decimal/libmpdec/basearith.h \ - $(srcdir)/Modules/_decimal/libmpdec/bits.h \ - $(srcdir)/Modules/_decimal/libmpdec/constants.h \ - $(srcdir)/Modules/_decimal/libmpdec/convolute.h \ - $(srcdir)/Modules/_decimal/libmpdec/crt.h \ - $(srcdir)/Modules/_decimal/libmpdec/difradix2.h \ - $(srcdir)/Modules/_decimal/libmpdec/fnt.h \ - $(srcdir)/Modules/_decimal/libmpdec/fourstep.h \ - $(srcdir)/Modules/_decimal/libmpdec/io.h \ - $(srcdir)/Modules/_decimal/libmpdec/mpalloc.h \ - $(srcdir)/Modules/_decimal/libmpdec/mpdecimal.h \ - $(srcdir)/Modules/_decimal/libmpdec/numbertheory.h \ - $(srcdir)/Modules/_decimal/libmpdec/sixstep.h \ - $(srcdir)/Modules/_decimal/libmpdec/transpose.h \ - $(srcdir)/Modules/_decimal/libmpdec/typearith.h \ - $(srcdir)/Modules/_decimal/libmpdec/umodarith.h - ########################################################################## # pyexpat's expat library @@ -797,11 +776,11 @@ list-targets: .PHONY: build_all build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) platform sharedmods \ - gdbhooks Programs/_testembed scripts checksharedmods rundsymutil build-details.json + gdbhooks Programs/_testembed scripts checksharedmods rundsymutil $(BUILD_DETAILS) .PHONY: build_wasm build_wasm: check-clean-src $(BUILDPYTHON) platform sharedmods \ - python-config checksharedmods + python-config checksharedmods $(BUILD_DETAILS) .PHONY: build_emscripten build_emscripten: build_wasm web_example web_example_pyrepl_jspi @@ -818,7 +797,7 @@ check-clean-src: echo "Building Python out of the source tree (in $(abs_builddir)) requires a clean source tree ($(abs_srcdir))" ; \ echo "Build artifacts such as .o files, executables, and Python/frozen_modules/*.h must not exist within $(srcdir)." ; \ echo "Try to run:" ; \ - echo " (cd \"$(srcdir)\" && make clean || git clean -fdx -e Doc/venv)" ; \ + echo " (cd \"$(srcdir)\" && make distclean || git clean -fdx -e Doc/venv)" ; \ exit 1; \ fi @@ -834,7 +813,7 @@ check-app-store-compliance: # Profile generation build must start from a clean tree. profile-clean-stamp: - $(MAKE) clean + $(MAKE) clean-profile touch $@ # Compile with profile generation enabled. @@ -945,7 +924,6 @@ coverage-lcov: @lcov $(COVERAGE_LCOV_OPTIONS) --remove $(COVERAGE_INFO) \ '*/Modules/_hacl/*' \ '*/Modules/_ctypes/libffi*/*' \ - '*/Modules/_decimal/libmpdec/*' \ '*/Modules/expat/*' \ '*/Modules/xx*.c' \ '*/Python/pyfpe.c' \ @@ -982,7 +960,7 @@ clinic-tests: check-clean-src $(srcdir)/Lib/test/clinic.test.c # Build the interpreter $(BUILDPYTHON): Programs/python.o $(LINK_PYTHON_DEPS) - $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) + $(LINKCC) $(PY_CORE_EXE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) platform: $(PYTHON_FOR_BUILD_DEPS) pybuilddir.txt $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform @@ -996,15 +974,15 @@ platform: $(PYTHON_FOR_BUILD_DEPS) pybuilddir.txt # or removed in case of failure. pybuilddir.txt: $(PYTHON_FOR_BUILD_DEPS) @echo "none" > ./pybuilddir.txt - $(RUNSHARED) $(PYTHON_FOR_BUILD) -S -m sysconfig --generate-posix-vars ;\ + $(RUNSHARED) $(PYTHON_FOR_BUILD) -S -X pathconfig_warnings=0 -m sysconfig --generate-posix-vars ;\ if test $$? -ne 0 ; then \ echo "generate-posix-vars failed" ; \ rm -f ./pybuilddir.txt ; \ exit 1 ; \ fi -build-details.json: pybuilddir.txt - $(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/generate-build-details.py `cat pybuilddir.txt`/build-details.json +$(BUILD_DETAILS): pybuilddir.txt + $(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/generate-build-details.py `cat pybuilddir.txt`/$(BUILD_DETAILS) # Build static library $(LIBRARY): $(LIBRARY_OBJS) @@ -1057,7 +1035,7 @@ $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION) $(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \ -all_load $(LIBRARY) \ - -install_name $(DESTDIR)$(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/$(PYTHONFRAMEWORK) \ + -install_name $(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/$(PYTHONFRAMEWORK) \ -compatibility_version $(VERSION) \ -current_version $(VERSION) \ -framework CoreFoundation $(LIBS); @@ -1095,7 +1073,7 @@ $(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) # wasm32-emscripten browser web example -EMSCRIPTEN_DIR=$(srcdir)/Tools/wasm/emscripten +EMSCRIPTEN_DIR=$(srcdir)/Platforms/emscripten WEBEX_DIR=$(EMSCRIPTEN_DIR)/web_example/ ZIP_STDLIB=python$(VERSION)$(ABI_THREAD).zip @@ -1109,6 +1087,10 @@ web_example/index.html: $(WEBEX_DIR)/index.html @mkdir -p web_example @cp $< $@ +web_example/python.worker.mjs: $(WEBEX_DIR)/python.worker.mjs + @mkdir -p web_example + @cp $< $@ + web_example/server.py: $(WEBEX_DIR)/server.py @mkdir -p web_example @cp $< $@ @@ -1126,7 +1108,7 @@ web_example/python.mjs web_example/python.wasm: $(BUILDPYTHON) cp python.wasm web_example/python.wasm .PHONY: web_example -web_example: web_example/python.mjs web_example/index.html web_example/server.py web_example/$(ZIP_STDLIB) +web_example: web_example/python.mjs web_example/python.worker.mjs web_example/index.html web_example/server.py web_example/$(ZIP_STDLIB) WEBEX2=web_example_pyrepl_jspi WEBEX2_DIR=$(EMSCRIPTEN_DIR)/$(WEBEX2)/ @@ -1187,14 +1169,12 @@ PYTHON_HEADERS= \ $(srcdir)/Include/intrcheck.h \ $(srcdir)/Include/iterobject.h \ $(srcdir)/Include/listobject.h \ - $(srcdir)/Include/pylock.h \ $(srcdir)/Include/longobject.h \ $(srcdir)/Include/marshal.h \ $(srcdir)/Include/memoryobject.h \ $(srcdir)/Include/methodobject.h \ $(srcdir)/Include/modsupport.h \ $(srcdir)/Include/moduleobject.h \ - $(srcdir)/Include/monitoring.h \ $(srcdir)/Include/object.h \ $(srcdir)/Include/objimpl.h \ $(srcdir)/Include/opcode.h \ @@ -1202,6 +1182,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/osdefs.h \ $(srcdir)/Include/osmodule.h \ $(srcdir)/Include/patchlevel.h \ + $(srcdir)/Include/pyabi.h \ $(srcdir)/Include/pyatomic.h \ $(srcdir)/Include/pybuffer.h \ $(srcdir)/Include/pycapsule.h \ @@ -1227,12 +1208,13 @@ PYTHON_HEADERS= \ $(srcdir)/Include/refcount.h \ $(srcdir)/Include/setobject.h \ $(srcdir)/Include/sliceobject.h \ + $(srcdir)/Include/slots.h \ + $(srcdir)/Include/slots_generated.h \ $(srcdir)/Include/structmember.h \ $(srcdir)/Include/structseq.h \ $(srcdir)/Include/sysmodule.h \ $(srcdir)/Include/traceback.h \ $(srcdir)/Include/tupleobject.h \ - $(srcdir)/Include/typeslots.h \ $(srcdir)/Include/unicodeobject.h \ $(srcdir)/Include/warnings.h \ $(srcdir)/Include/weakrefobject.h \ @@ -1267,6 +1249,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/cpython/pylock.h \ $(srcdir)/Include/cpython/longintrepr.h \ $(srcdir)/Include/cpython/longobject.h \ + $(srcdir)/Include/cpython/marshal.h \ $(srcdir)/Include/cpython/memoryobject.h \ $(srcdir)/Include/cpython/methodobject.h \ $(srcdir)/Include/cpython/modsupport.h \ @@ -1291,7 +1274,10 @@ PYTHON_HEADERS= \ $(srcdir)/Include/cpython/pystats.h \ $(srcdir)/Include/cpython/pythonrun.h \ $(srcdir)/Include/cpython/pythread.h \ + $(srcdir)/Include/cpython/sentinelobject.h \ $(srcdir)/Include/cpython/setobject.h \ + $(srcdir)/Include/cpython/sliceobject.h \ + $(srcdir)/Include/cpython/structseq.h \ $(srcdir)/Include/cpython/traceback.h \ $(srcdir)/Include/cpython/tracemalloc.h \ $(srcdir)/Include/cpython/tupleobject.h \ @@ -1364,12 +1350,14 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_interpolation.h \ $(srcdir)/Include/internal/pycore_intrinsics.h \ $(srcdir)/Include/internal/pycore_jit.h \ + $(srcdir)/Include/internal/pycore_lazyimportobject.h \ $(srcdir)/Include/internal/pycore_list.h \ $(srcdir)/Include/internal/pycore_llist.h \ $(srcdir)/Include/internal/pycore_lock.h \ $(srcdir)/Include/internal/pycore_long.h \ $(srcdir)/Include/internal/pycore_memoryobject.h \ $(srcdir)/Include/internal/pycore_mimalloc.h \ + $(srcdir)/Include/internal/pycore_mmap.h \ $(srcdir)/Include/internal/pycore_modsupport.h \ $(srcdir)/Include/internal/pycore_moduleobject.h \ $(srcdir)/Include/internal/pycore_namespace.h \ @@ -1409,6 +1397,8 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_setobject.h \ $(srcdir)/Include/internal/pycore_signal.h \ $(srcdir)/Include/internal/pycore_sliceobject.h \ + $(srcdir)/Include/internal/pycore_slots.h \ + $(srcdir)/Include/internal/pycore_slots_generated.h \ $(srcdir)/Include/internal/pycore_stats.h \ $(srcdir)/Include/internal/pycore_strhex.h \ $(srcdir)/Include/internal/pycore_stackref.h \ @@ -1427,10 +1417,12 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_typeobject.h \ $(srcdir)/Include/internal/pycore_typevarobject.h \ $(srcdir)/Include/internal/pycore_ucnhash.h \ + $(srcdir)/Include/internal/pycore_unicodectype.h \ $(srcdir)/Include/internal/pycore_unicodeobject.h \ $(srcdir)/Include/internal/pycore_unicodeobject_generated.h \ $(srcdir)/Include/internal/pycore_unionobject.h \ $(srcdir)/Include/internal/pycore_uniqueid.h \ + $(srcdir)/Include/internal/pycore_uop.h \ $(srcdir)/Include/internal/pycore_uop_ids.h \ $(srcdir)/Include/internal/pycore_uop_metadata.h \ $(srcdir)/Include/internal/pycore_warnings.h \ @@ -1440,60 +1432,6 @@ PYTHON_HEADERS= \ \ $(srcdir)/Python/stdlib_module_names.h -########################################################################## -# Build static libmpdec.a -LIBMPDEC_CFLAGS=@LIBMPDEC_CFLAGS@ $(PY_STDMODULE_CFLAGS) $(CCSHARED) - -# "%.o: %c" is not portable -Modules/_decimal/libmpdec/basearith.o: $(srcdir)/Modules/_decimal/libmpdec/basearith.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/basearith.c - -Modules/_decimal/libmpdec/constants.o: $(srcdir)/Modules/_decimal/libmpdec/constants.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/constants.c - -Modules/_decimal/libmpdec/context.o: $(srcdir)/Modules/_decimal/libmpdec/context.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/context.c - -Modules/_decimal/libmpdec/convolute.o: $(srcdir)/Modules/_decimal/libmpdec/convolute.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/convolute.c - -Modules/_decimal/libmpdec/crt.o: $(srcdir)/Modules/_decimal/libmpdec/crt.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/crt.c - -Modules/_decimal/libmpdec/difradix2.o: $(srcdir)/Modules/_decimal/libmpdec/difradix2.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/difradix2.c - -Modules/_decimal/libmpdec/fnt.o: $(srcdir)/Modules/_decimal/libmpdec/fnt.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/fnt.c - -Modules/_decimal/libmpdec/fourstep.o: $(srcdir)/Modules/_decimal/libmpdec/fourstep.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/fourstep.c - -Modules/_decimal/libmpdec/io.o: $(srcdir)/Modules/_decimal/libmpdec/io.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/io.c - -Modules/_decimal/libmpdec/mpalloc.o: $(srcdir)/Modules/_decimal/libmpdec/mpalloc.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/mpalloc.c - -Modules/_decimal/libmpdec/mpdecimal.o: $(srcdir)/Modules/_decimal/libmpdec/mpdecimal.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/mpdecimal.c - -Modules/_decimal/libmpdec/mpsignal.o: $(srcdir)/Modules/_decimal/libmpdec/mpsignal.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/mpsignal.c - -Modules/_decimal/libmpdec/numbertheory.o: $(srcdir)/Modules/_decimal/libmpdec/numbertheory.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/numbertheory.c - -Modules/_decimal/libmpdec/sixstep.o: $(srcdir)/Modules/_decimal/libmpdec/sixstep.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/sixstep.c - -Modules/_decimal/libmpdec/transpose.o: $(srcdir)/Modules/_decimal/libmpdec/transpose.c $(LIBMPDEC_HEADERS) $(PYTHON_HEADERS) - $(CC) -c $(LIBMPDEC_CFLAGS) -o $@ $(srcdir)/Modules/_decimal/libmpdec/transpose.c - -$(LIBMPDEC_A): $(LIBMPDEC_OBJS) - -rm -f $@ - $(AR) $(ARFLAGS) $@ $(LIBMPDEC_OBJS) - ########################################################################## # Build static libexpat.a LIBEXPAT_CFLAGS=@LIBEXPAT_CFLAGS@ $(PY_STDMODULE_CFLAGS) $(CCSHARED) @@ -1594,6 +1532,11 @@ sharedmods: $(SHAREDMODS) pybuilddir.txt # dependency on BUILDPYTHON ensures that the target is run last .PHONY: checksharedmods checksharedmods: sharedmods $(PYTHON_FOR_BUILD_DEPS) $(BUILDPYTHON) + @if [ -n "@MISSING_STDLIB_CONFIG@" ]; then \ + $(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/check_extension_modules.py --generate-missing-stdlib-info --with-missing-stdlib-config="@MISSING_STDLIB_CONFIG@"; \ + else \ + $(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/check_extension_modules.py --generate-missing-stdlib-info; \ + fi @$(RUNSHARED) $(PYTHON_FOR_BUILD) $(srcdir)/Tools/build/check_extension_modules.py .PHONY: rundsymutil @@ -1658,7 +1601,7 @@ regen-re: $(BUILDPYTHON) $(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/build/generate_re_casefix.py $(srcdir)/Lib/re/_casefix.py Programs/_testembed: Programs/_testembed.o $(LINK_PYTHON_DEPS) - $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) + $(LINKCC) $(PY_CORE_EXE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) ############################################################################ # "Bootstrap Python" used to run Programs/_freeze_module.py @@ -1707,6 +1650,10 @@ FROZEN_FILES_IN = \ Lib/zipimport.py \ Lib/abc.py \ Lib/codecs.py \ + Lib/encodings/__init__.py \ + Lib/encodings/aliases.py \ + Lib/encodings/utf_8.py \ + Lib/encodings/_win_cp_codecs.py \ Lib/io.py \ Lib/_collections_abc.py \ Lib/_sitebuiltins.py \ @@ -1716,6 +1663,7 @@ FROZEN_FILES_IN = \ Lib/os.py \ Lib/site.py \ Lib/stat.py \ + Lib/linecache.py \ Lib/importlib/util.py \ Lib/importlib/machinery.py \ Lib/runpy.py \ @@ -1732,6 +1680,10 @@ FROZEN_FILES_OUT = \ Python/frozen_modules/zipimport.h \ Python/frozen_modules/abc.h \ Python/frozen_modules/codecs.h \ + Python/frozen_modules/encodings.h \ + Python/frozen_modules/encodings.aliases.h \ + Python/frozen_modules/encodings.utf_8.h \ + Python/frozen_modules/encodings._win_cp_codecs.h \ Python/frozen_modules/io.h \ Python/frozen_modules/_collections_abc.h \ Python/frozen_modules/_sitebuiltins.h \ @@ -1741,6 +1693,7 @@ FROZEN_FILES_OUT = \ Python/frozen_modules/os.h \ Python/frozen_modules/site.h \ Python/frozen_modules/stat.h \ + Python/frozen_modules/linecache.h \ Python/frozen_modules/importlib.util.h \ Python/frozen_modules/importlib.machinery.h \ Python/frozen_modules/runpy.h \ @@ -1780,6 +1733,18 @@ Python/frozen_modules/abc.h: Lib/abc.py $(FREEZE_MODULE_DEPS) Python/frozen_modules/codecs.h: Lib/codecs.py $(FREEZE_MODULE_DEPS) $(FREEZE_MODULE) codecs $(srcdir)/Lib/codecs.py Python/frozen_modules/codecs.h +Python/frozen_modules/encodings.h: Lib/encodings/__init__.py $(FREEZE_MODULE_DEPS) + $(FREEZE_MODULE) encodings $(srcdir)/Lib/encodings/__init__.py Python/frozen_modules/encodings.h + +Python/frozen_modules/encodings.aliases.h: Lib/encodings/aliases.py $(FREEZE_MODULE_DEPS) + $(FREEZE_MODULE) encodings.aliases $(srcdir)/Lib/encodings/aliases.py Python/frozen_modules/encodings.aliases.h + +Python/frozen_modules/encodings.utf_8.h: Lib/encodings/utf_8.py $(FREEZE_MODULE_DEPS) + $(FREEZE_MODULE) encodings.utf_8 $(srcdir)/Lib/encodings/utf_8.py Python/frozen_modules/encodings.utf_8.h + +Python/frozen_modules/encodings._win_cp_codecs.h: Lib/encodings/_win_cp_codecs.py $(FREEZE_MODULE_DEPS) + $(FREEZE_MODULE) encodings._win_cp_codecs $(srcdir)/Lib/encodings/_win_cp_codecs.py Python/frozen_modules/encodings._win_cp_codecs.h + Python/frozen_modules/io.h: Lib/io.py $(FREEZE_MODULE_DEPS) $(FREEZE_MODULE) io $(srcdir)/Lib/io.py Python/frozen_modules/io.h @@ -1807,6 +1772,9 @@ Python/frozen_modules/site.h: Lib/site.py $(FREEZE_MODULE_DEPS) Python/frozen_modules/stat.h: Lib/stat.py $(FREEZE_MODULE_DEPS) $(FREEZE_MODULE) stat $(srcdir)/Lib/stat.py Python/frozen_modules/stat.h +Python/frozen_modules/linecache.h: Lib/linecache.py $(FREEZE_MODULE_DEPS) + $(FREEZE_MODULE) linecache $(srcdir)/Lib/linecache.py Python/frozen_modules/linecache.h + Python/frozen_modules/importlib.util.h: Lib/importlib/util.py $(FREEZE_MODULE_DEPS) $(FREEZE_MODULE) importlib.util $(srcdir)/Lib/importlib/util.py Python/frozen_modules/importlib.util.h @@ -1867,7 +1835,7 @@ regen-abidump: all .PHONY: check-abidump check-abidump: all - abidiff $(srcdir)/Doc/data/python$(LDVERSION).abi "libpython$(LDVERSION).so" --drop-private-types --no-architecture --no-added-syms + abidiff $(srcdir)/Doc/data/python$(LDVERSION).abi "libpython$(LDVERSION).so" --drop-private-types --no-architecture --no-added-syms --suppressions $(srcdir)/Misc/libabigail.abignore .PHONY: regen-limited-abi regen-limited-abi: all @@ -1886,7 +1854,7 @@ regen-unicodedata: # "clinic" is regenerated implicitly via "regen-global-objects". .PHONY: regen-all -regen-all: regen-cases regen-typeslots \ +regen-all: regen-cases regen-slots \ regen-token regen-ast regen-keyword regen-sre regen-frozen \ regen-pegen-metaparser regen-pegen regen-test-frozenmain \ regen-test-levenshtein regen-global-objects @@ -2085,7 +2053,6 @@ UNICODE_DEPS = \ $(srcdir)/Objects/stringlib/fastsearch.h \ $(srcdir)/Objects/stringlib/find.h \ $(srcdir)/Objects/stringlib/find_max_char.h \ - $(srcdir)/Objects/stringlib/localeutil.h \ $(srcdir)/Objects/stringlib/partition.h \ $(srcdir)/Objects/stringlib/replace.h \ $(srcdir)/Objects/stringlib/repr.h \ @@ -2100,6 +2067,7 @@ Objects/bytes_methods.o: $(srcdir)/Objects/bytes_methods.c $(BYTESTR_DEPS) Objects/bytesobject.o: $(srcdir)/Objects/bytesobject.c $(BYTESTR_DEPS) Objects/bytearrayobject.o: $(srcdir)/Objects/bytearrayobject.c $(BYTESTR_DEPS) +Objects/unicode_format.o: $(srcdir)/Objects/unicode_format.c $(UNICODE_DEPS) Objects/unicodeobject.o: $(srcdir)/Objects/unicodeobject.c $(UNICODE_DEPS) Objects/dictobject.o: $(srcdir)/Objects/stringlib/eq.h @@ -2133,8 +2101,8 @@ Objects/mimalloc/page.o: $(srcdir)/Objects/mimalloc/page-queue.c .PHONY: regen-cases regen-cases: \ regen-opcode-ids regen-opcode-targets regen-uop-ids regen-opcode-metadata-py \ - regen-generated-cases regen-executor-cases regen-optimizer-cases \ - regen-opcode-metadata regen-uop-metadata + regen-generated-cases regen-executor-cases regen-optimizer-cases regen-record-functions \ + regen-opcode-metadata regen-uop-metadata regen-test-cases regen-test-opcode-targets .PHONY: regen-opcode-ids regen-opcode-ids: @@ -2166,6 +2134,26 @@ regen-generated-cases: -o $(srcdir)/Python/generated_cases.c.h.new $(srcdir)/Python/bytecodes.c $(UPDATE_FILE) $(srcdir)/Python/generated_cases.c.h $(srcdir)/Python/generated_cases.c.h.new +.PHONY: regen-record-functions +regen-record-functions: + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/cases_generator/record_function_generator.py \ + -o $(srcdir)/Python/record_functions.c.h.new $(srcdir)/Python/bytecodes.c + $(UPDATE_FILE) $(srcdir)/Python/record_functions.c.h $(srcdir)/Python/record_functions.c.h.new + +.PHONY: regen-test-cases +regen-test-cases: + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/cases_generator/tier1_generator.py \ + -o $(srcdir)/Modules/_testinternalcapi/test_cases.c.h.new $(srcdir)/Python/bytecodes.c \ + $(srcdir)/Modules/_testinternalcapi/testbytecodes.c + $(UPDATE_FILE) $(srcdir)/Modules/_testinternalcapi/test_cases.c.h $(srcdir)/Modules/_testinternalcapi/test_cases.c.h.new + +.PHONY: regen-test-opcode-targets +regen-test-opcode-targets: + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/cases_generator/target_generator.py \ + -o $(srcdir)/Modules/_testinternalcapi/test_targets.h.new $(srcdir)/Python/bytecodes.c \ + $(srcdir)/Modules/_testinternalcapi/testbytecodes.c + $(UPDATE_FILE) $(srcdir)/Modules/_testinternalcapi/test_targets.h $(srcdir)/Modules/_testinternalcapi/test_targets.h.new + .PHONY: regen-executor-cases regen-executor-cases: $(PYTHON_FOR_REGEN) $(srcdir)/Tools/cases_generator/tier2_generator.py \ @@ -2204,7 +2192,8 @@ Python/ceval.o: \ $(srcdir)/Python/condvar.h \ $(srcdir)/Python/generated_cases.c.h \ $(srcdir)/Python/executor_cases.c.h \ - $(srcdir)/Python/opcode_targets.h + $(srcdir)/Python/opcode_targets.h \ + $(srcdir)/Python/record_functions.c.h Python/flowgraph.o: \ $(srcdir)/Include/internal/pycore_opcode_metadata.h @@ -2226,7 +2215,7 @@ Python/frozen.o: $(FROZEN_FILES_OUT) # an include guard, so we can't use a pipeline to transform its output. Include/pydtrace_probes.h: $(srcdir)/Include/pydtrace.d $(MKDIR_P) Include - CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -h -s $< + CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -h -s $(srcdir)/Include/pydtrace.d : sed in-place edit with POSIX-only tools sed 's/PYTHON_/PyDTrace_/' $@ > $@.tmp mv $@.tmp $@ @@ -2236,18 +2225,19 @@ Python/gc.o: $(srcdir)/Include/pydtrace.h Python/import.o: $(srcdir)/Include/pydtrace.h Python/pydtrace.o: $(srcdir)/Include/pydtrace.d $(DTRACE_DEPS) - CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -G -s $< $(DTRACE_DEPS) - -Objects/typeobject.o: Objects/typeslots.inc + CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -G -s $(srcdir)/Include/pydtrace.d $(DTRACE_DEPS) .PHONY: regen-typeslots regen-typeslots: - # Regenerate Objects/typeslots.inc from Include/typeslotsh - # using Objects/typeslots.py - $(PYTHON_FOR_REGEN) $(srcdir)/Objects/typeslots.py \ - < $(srcdir)/Include/typeslots.h \ - $(srcdir)/Objects/typeslots.inc.new - $(UPDATE_FILE) $(srcdir)/Objects/typeslots.inc $(srcdir)/Objects/typeslots.inc.new + echo 'NOTE: "regen-typeslots" was renamed to "regen-slots"' + $(MAKE) regen-slots + +.PHONY: regen-slots +regen-slots: Python/slots.toml + # Regenerate {Python,Include}/slots_generated.{c,h} + # from Python/slots.toml using Tools/build/generate_slots.py + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/generate_slots.py \ + --generate-all $(LIBRARY_OBJS) $(MODOBJS) Programs/python.o: $(PYTHON_HEADERS) @@ -2315,10 +2305,10 @@ testios: fi # Clone the testbed project into the XCFOLDER - $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" + $(PYTHON_FOR_BUILD) $(srcdir)/Platforms/Apple/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" # Run the testbed project - $(PYTHON_FOR_BUILD) "$(XCFOLDER)" run --verbose -- test -uall --single-process --rerun -W + $(PYTHON_FOR_BUILD) "$(XCFOLDER)" run --verbose -- test -uall --single-process --rerun -W --pythoninfo # Like test, but using --slow-ci which enables all test resources and use # longer timeout. Run an optional pybuildbot.identify script to include @@ -2358,8 +2348,10 @@ multissltest: all # prevent race conditions with PGO builds. PGO builds use recursive make, # which can lead to two parallel `./python setup.py build` processes that # step on each others toes. +# Only the main install gets a build-details.json. .PHONY: install install: @FRAMEWORKINSTALLFIRST@ @INSTALLTARGETS@ @FRAMEWORKINSTALLLAST@ + $(INSTALL_DATA) `cat pybuilddir.txt`/$(BUILD_DETAILS) $(DESTDIR)$(LIBDEST); \ if test "x$(ENSUREPIP)" != "xno" ; then \ case $(ENSUREPIP) in \ upgrade) ensurepip="--upgrade" ;; \ @@ -2561,6 +2553,14 @@ LIBSUBDIRS= asyncio \ multiprocessing multiprocessing/dummy \ pathlib \ profile \ + profiling profiling/sampling profiling/tracing \ + profiling/sampling/_assets \ + profiling/sampling/_heatmap_assets \ + profiling/sampling/_flamegraph_assets \ + profiling/sampling/_shared_assets \ + profiling/sampling/live_collector \ + profiling/sampling/_vendor/d3/7.8.5 \ + profiling/sampling/_vendor/d3-flame-graph/4.1.3 \ pydoc_data \ re \ site-packages \ @@ -2585,6 +2585,7 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_ast \ test/test_ast/data \ test/archivetestdata \ + test/audit_test_data \ test/audiodata \ test/certdata \ test/certdata/capath \ @@ -2643,6 +2644,7 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_importlib/namespace_pkgs \ test/test_importlib/namespace_pkgs/both_portions \ test/test_importlib/namespace_pkgs/both_portions/foo \ + test/test_importlib/namespace_pkgs/foo \ test/test_importlib/namespace_pkgs/module_and_namespace_package \ test/test_importlib/namespace_pkgs/module_and_namespace_package/a_test \ test/test_importlib/namespace_pkgs/not_a_namespace_pkg \ @@ -2665,14 +2667,22 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_importlib/source \ test/test_inspect \ test/test_interpreters \ + test/test_io \ test/test_json \ + test/test_lazy_import \ + test/test_lazy_import/data \ + test/test_lazy_import/data/pkg \ + test/test_lazy_import/data/badsyntax \ test/test_module \ test/test_multiprocessing_fork \ test/test_multiprocessing_forkserver \ test/test_multiprocessing_spawn \ + test/test_os \ test/test_pathlib \ test/test_pathlib/support \ test/test_peg_generator \ + test/test_profiling \ + test/test_profiling/test_sampling_profiler \ test/test_pydoc \ test/test_pyrepl \ test/test_string \ @@ -2695,6 +2705,7 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_tomllib/data/valid \ test/test_tomllib/data/valid/array \ test/test_tomllib/data/valid/dates-and-times \ + test/test_tomllib/data/valid/inline-table \ test/test_tomllib/data/valid/multiline-basic-str \ test/test_tools \ test/test_tools/i18n_data \ @@ -2798,7 +2809,7 @@ libinstall: all $(srcdir)/Modules/xxmodule.c done $(INSTALL_DATA) `cat pybuilddir.txt`/_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).py $(DESTDIR)$(LIBDEST); \ $(INSTALL_DATA) `cat pybuilddir.txt`/_sysconfig_vars_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).json $(DESTDIR)$(LIBDEST); \ - $(INSTALL_DATA) `cat pybuilddir.txt`/build-details.json $(DESTDIR)$(LIBDEST); \ + $(INSTALL_DATA) `cat pybuilddir.txt`/_missing_stdlib_info.py $(DESTDIR)$(LIBDEST); \ $(INSTALL_DATA) $(srcdir)/LICENSE $(DESTDIR)$(LIBDEST)/LICENSE.txt @ # If app store compliance has been configured, apply the patch to the @ # installed library code. The patch has been previously validated against @@ -3030,6 +3041,9 @@ frameworkinstallunversionedstructure: $(LDLIBRARY) $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR) sed 's/%VERSION%/'"`$(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Info.plist $(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY) + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(LIBDIR) + $(LN) -fs "../$(LDLIBRARY)" "$(DESTDIR)$(prefix)/lib/libpython$(LDVERSION).dylib" + $(LN) -fs "../$(LDLIBRARY)" "$(DESTDIR)$(prefix)/lib/libpython$(VERSION).dylib" $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(BINDIR) for file in $(srcdir)/$(RESSRCDIR)/bin/* ; do \ $(INSTALL) -m $(EXEMODE) $$file $(DESTDIR)$(BINDIR); \ @@ -3099,25 +3113,72 @@ config.status: $(srcdir)/configure .PRECIOUS: config.status $(BUILDPYTHON) Makefile Makefile.pre -Python/asm_trampoline.o: $(srcdir)/Python/asm_trampoline.S +Python/asm_trampoline_x86_64.o: $(srcdir)/Python/asm_trampoline_x86_64.S $(CC) -c $(PY_CORE_CFLAGS) -o $@ $< +Python/asm_trampoline_aarch64.o: $(srcdir)/Python/asm_trampoline_aarch64.S + $(CC) -c $(PY_CORE_CFLAGS) -o $@ $< + +Python/asm_trampoline_riscv64.o: $(srcdir)/Python/asm_trampoline_riscv64.S + $(CC) -c $(PY_CORE_CFLAGS) -o $@ $< + +# On macOS universal2 builds, $(PY_CORE_CFLAGS) contains "-arch arm64 -arch x86_64", +# which would produce fat .o files containing both architectures for each .S input. +# lipo -create then refuses to combine them because they share architectures. +# Build each per-arch object with a single -arch flag before merging with lipo. +Python/asm_trampoline_universal2.o: $(srcdir)/Python/asm_trampoline_aarch64.S $(srcdir)/Python/asm_trampoline_x86_64.S + $(CC) -c $(filter-out -arch arm64 x86_64,$(PY_CORE_CFLAGS)) -arch arm64 \ + -o Python/asm_trampoline_arm64-apple-darwin.o $(srcdir)/Python/asm_trampoline_aarch64.S + $(CC) -c $(filter-out -arch arm64 x86_64,$(PY_CORE_CFLAGS)) -arch x86_64 \ + -o Python/asm_trampoline_x86_64-apple-darwin.o $(srcdir)/Python/asm_trampoline_x86_64.S + lipo -create -output $@ \ + Python/asm_trampoline_arm64-apple-darwin.o \ + Python/asm_trampoline_x86_64-apple-darwin.o + rm -f Python/asm_trampoline_arm64-apple-darwin.o \ + Python/asm_trampoline_x86_64-apple-darwin.o + +Python/emscripten_trampoline_inner.wasm: $(srcdir)/Python/emscripten_trampoline_inner.c + # emcc has a path that ends with emsdk/upstream/emscripten/emcc, we're looking for emsdk/upstream/bin/clang. + $$(em-config LLVM_ROOT)/clang -o $@ $< -mgc -O2 -Wl,--no-entry -Wl,--import-table -Wl,--import-memory -target wasm32-unknown-unknown -nostdlib + +Python/emscripten_trampoline_wasm.c: Python/emscripten_trampoline_inner.wasm + $(PYTHON_FOR_REGEN) $(srcdir)/Platforms/emscripten/prepare_external_wasm.py $< $@ getWasmTrampolineModule + +JIT_SHIM_BUILD_OBJS= @JIT_SHIM_BUILD_O@ +JIT_UNWIND_INFO_H= $(if $(JIT_OBJS),jit_unwind_info.h $(patsubst jit_stencils-%.h,jit_unwind_info-%.h,@JIT_STENCILS_H@)) +JIT_BUILD_TARGETS= jit_stencils.h @JIT_STENCILS_H@ $(JIT_UNWIND_INFO_H) $(JIT_SHIM_BUILD_OBJS) +JIT_TARGETS= $(JIT_BUILD_TARGETS) $(filter-out $(JIT_SHIM_BUILD_OBJS),$(JIT_OBJS)) +JIT_GENERATED_STAMP= .jit-stamp JIT_DEPS = \ $(srcdir)/Tools/jit/*.c \ + $(srcdir)/Tools/jit/*.h \ $(srcdir)/Tools/jit/*.py \ $(srcdir)/Python/executor_cases.c.h \ pyconfig.h -jit_stencils.h: $(JIT_DEPS) +$(JIT_GENERATED_STAMP): $(JIT_DEPS) @REGEN_JIT_COMMAND@ + @touch $@ + +$(JIT_BUILD_TARGETS): $(JIT_GENERATED_STAMP) + @if test ! -f "$@"; then \ + rm -f $(JIT_GENERATED_STAMP); \ + $(MAKE) $(JIT_GENERATED_STAMP); \ + test -f "$@"; \ + fi + +jit_shim-universal2-apple-darwin.o: jit_shim-aarch64-apple-darwin.o jit_shim-x86_64-apple-darwin.o + lipo -create -output $@ jit_shim-aarch64-apple-darwin.o jit_shim-x86_64-apple-darwin.o Python/jit.o: $(srcdir)/Python/jit.c @JIT_STENCILS_H@ $(CC) -c $(PY_CORE_CFLAGS) -o $@ $< +Python/jit_unwind.o: $(srcdir)/Python/jit_unwind.c $(JIT_UNWIND_INFO_H) + $(CC) -c $(PY_CORE_CFLAGS) -o $@ $< + .PHONY: regen-jit -regen-jit: - @REGEN_JIT_COMMAND@ +regen-jit: $(JIT_TARGETS) # Some make's put the object file in the current directory .c.o: @@ -3130,6 +3191,9 @@ regen-jit: Python/dtoa.o: Python/dtoa.c $(CC) -c $(PY_CORE_CFLAGS) $(CFLAGS_ALIASING) -o $@ $< +Python/ceval.o: Python/ceval.c + $(CC) -c $(PY_CORE_CFLAGS) $(CFLAGS_CEVAL) -o $@ $< + # Run reindent on the library .PHONY: reindent reindent: @@ -3194,7 +3258,9 @@ docclean: # data. The PGO data is only valid if source code remains unchanged. .PHONY: clean-retain-profile clean-retain-profile: pycremoval - find . -name '*.[oa]' -exec rm -f {} ';' + # Keep the generated JIT shim objects with the rest of the JIT generated + # files: they are regenerated as a group and tracked by .jit-stamp. + find . -name '*.[oa]' ! -name 'jit_shim*.o' -exec rm -f {} ';' find . -name '*.s[ol]' -exec rm -f {} ';' find . -name '*.so.[0-9]*.[0-9]*' -exec rm -f {} ';' find . -name '*.lto' -exec rm -f {} ';' @@ -3210,14 +3276,13 @@ clean-retain-profile: pycremoval -rm -rf Python/deepfreeze -rm -f Python/frozen_modules/*.h -rm -f Python/frozen_modules/MANIFEST - -rm -f jit_stencils.h -find build -type f -a ! -name '*.gc??' -exec rm -f {} ';' -rm -f Include/pydtrace_probes.h -rm -f profile-gen-stamp - -rm -rf iOS/testbed/Python.xcframework/ios-*/bin - -rm -rf iOS/testbed/Python.xcframework/ios-*/lib - -rm -rf iOS/testbed/Python.xcframework/ios-*/include - -rm -rf iOS/testbed/Python.xcframework/ios-*/Python.framework + -rm -rf Platforms/Apple/iOS/testbed/Python.xcframework/ios-*/bin + -rm -rf Platforms/Apple/iOS/testbed/Python.xcframework/ios-*/lib + -rm -rf Platforms/Apple/iOS/testbed/Python.xcframework/ios-*/include + -rm -rf Platforms/Apple/iOS/testbed/Python.xcframework/ios-*/Python.framework .PHONY: profile-removal profile-removal: @@ -3229,13 +3294,21 @@ profile-removal: rm -f profile-run-stamp rm -f profile-bolt-stamp -.PHONY: clean -clean: clean-retain-profile clean-bolt +.PHONY: clean-profile +clean-profile: clean-retain-profile clean-bolt @if test @DEF_MAKE_ALL_RULE@ = profile-opt -o @DEF_MAKE_ALL_RULE@ = bolt-opt; then \ rm -f profile-gen-stamp profile-clean-stamp; \ $(MAKE) profile-removal; \ fi +# gh-141808: The JIT stencils are deliberately kept in clean-profile +.PHONY: clean-jit-stencils +clean-jit-stencils: + -rm -f $(JIT_TARGETS) $(JIT_GENERATED_STAMP) jit_stencils*.h jit_unwind_info*.h jit_shim*.o + +.PHONY: clean +clean: clean-profile clean-jit-stencils + .PHONY: clobber clobber: clean -rm -f $(BUILDPYTHON) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY) \ @@ -3243,7 +3316,7 @@ clobber: clean config.cache config.log pyconfig.h Modules/config.c -rm -rf build platform -rm -rf $(PYTHONFRAMEWORKDIR) - -rm -rf iOS/Frameworks + -rm -rf Platforms/Apple/iOS/Frameworks -rm -rf iOSTestbed.* -rm -f python-config.py python-config -rm -rf cross-build @@ -3283,6 +3356,11 @@ check-c-globals: --format summary \ --traceback +# Check for undocumented C APIs. +.PHONY: check-c-api-docs +check-c-api-docs: + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/check-c-api-docs/main.py + # Find files with funny names .PHONY: funny funny: @@ -3351,10 +3429,10 @@ MODULE_UNICODEDATA_DEPS=$(srcdir)/Modules/unicodedata_db.h $(srcdir)/Modules/uni MODULE__CTYPES_DEPS=$(srcdir)/Modules/_ctypes/ctypes.h MODULE__CTYPES_TEST_DEPS=$(srcdir)/Modules/_ctypes/_ctypes_test_generated.c.h MODULE__CTYPES_MALLOC_CLOSURE=@MODULE__CTYPES_MALLOC_CLOSURE@ -MODULE__DECIMAL_DEPS=$(srcdir)/Modules/_decimal/docstrings.h @LIBMPDEC_INTERNAL@ MODULE__ELEMENTTREE_DEPS=$(srcdir)/Modules/pyexpat.c @LIBEXPAT_INTERNAL@ MODULE__HASHLIB_DEPS=$(srcdir)/Modules/hashlib.h MODULE__IO_DEPS=$(srcdir)/Modules/_io/_iomodule.h +MODULE__REMOTE_DEBUGGING_DEPS=$(srcdir)/Modules/_remote_debugging/_remote_debugging.h $(srcdir)/Modules/_remote_debugging/gc_stats.h # HACL*-based cryptographic primitives MODULE__MD5_DEPS=$(srcdir)/Modules/hashlib.h $(LIBHACL_MD5_HEADERS) $(LIBHACL_MD5_LIB_@LIBHACL_LDEPS_LIBTYPE@) @@ -3374,7 +3452,7 @@ MODULE__SOCKET_DEPS=$(srcdir)/Modules/socketmodule.h $(srcdir)/Modules/addrinfo. MODULE__SSL_DEPS=$(srcdir)/Modules/_ssl.h $(srcdir)/Modules/_ssl/cert.c $(srcdir)/Modules/_ssl/debughelpers.c $(srcdir)/Modules/_ssl/misc.c $(srcdir)/Modules/_ssl_data_111.h $(srcdir)/Modules/_ssl_data_300.h $(srcdir)/Modules/socketmodule.h MODULE__TESTCAPI_DEPS=$(srcdir)/Modules/_testcapi/parts.h $(srcdir)/Modules/_testcapi/util.h MODULE__TESTLIMITEDCAPI_DEPS=$(srcdir)/Modules/_testlimitedcapi/testcapi_long.h $(srcdir)/Modules/_testlimitedcapi/parts.h $(srcdir)/Modules/_testlimitedcapi/util.h -MODULE__TESTINTERNALCAPI_DEPS=$(srcdir)/Modules/_testinternalcapi/parts.h +MODULE__TESTINTERNALCAPI_DEPS=$(srcdir)/Modules/_testinternalcapi/parts.h $(srcdir)/Python/ceval.h $(srcdir)/Modules/_testinternalcapi/test_targets.h $(srcdir)/Modules/_testinternalcapi/test_cases.c.h MODULE__SQLITE3_DEPS=$(srcdir)/Modules/_sqlite/connection.h $(srcdir)/Modules/_sqlite/cursor.h $(srcdir)/Modules/_sqlite/microprotocols.h $(srcdir)/Modules/_sqlite/module.h $(srcdir)/Modules/_sqlite/prepare_protocol.h $(srcdir)/Modules/_sqlite/row.h $(srcdir)/Modules/_sqlite/util.h MODULE__ZSTD_DEPS=$(srcdir)/Modules/_zstd/_zstdmodule.h $(srcdir)/Modules/_zstd/buffer.h $(srcdir)/Modules/_zstd/zstddict.h diff --git a/Misc/ACKS b/Misc/ACKS index 745f472474cd9d..ee68d91f13c431 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -122,6 +122,7 @@ Don Bashford Pior Bastida Nick Bastin Ned Batchelder +Facundo Batista Jeff Bauer Michael R Bax Anthony Baxter @@ -143,6 +144,7 @@ Bas van Beek Ian Beer Stefan Behnel Reimer Behrends +Tomi Belan Maxime Bélanger Ben Bell Thomas Bellman @@ -210,6 +212,7 @@ Médéric Boquien Matias Bordese Jonas Borgström Jurjen Bos +Jeffrey Bosboom Peter Bosch Dan Boswell Eric Bouck @@ -327,6 +330,7 @@ David Chaum Nicolas Chauvat Jerry Chen Michael Chermside +Dmitry Chestnykh Ingrid Cheung Adam Chhina Terry Chia @@ -417,11 +421,13 @@ Lisandro Dalcin Darren Dale Andrew Dalke Lars Damerow +Hauke Dämpfling Evan Dandrea Eric Daniel Scott David Daniels Derzsi Dániel Lawrence D'Anna +Steven D'Aprano Ben Darnell Kushal Das Jonathan Dasteel @@ -483,6 +489,7 @@ Weilin Du John DuBois Paul Dubois Jacques Ducasse +Jadon Duff Andrei Dorian Duma Graham Dumpleton Quinn Dunkan @@ -497,6 +504,7 @@ Eugene Dvurechenski Karmen Dykstra Josip Dzolonga Maxim Dzumanenko +Phillip J. Eby Hans Eckardt Rodolpho Eckhardt Ulrich Eckhardt @@ -621,6 +629,7 @@ Soumendra Ganguly (गङ्गोपाध्याय) Fred Gansevles Paul Ganssle Tian Gao +Katie Gardner Lars Marius Garshol Jake Garver Dan Gass @@ -734,6 +743,7 @@ Derek Harland Jason Harper David Harrigan Brian Harring +Peter Harris Jonathan Hartley Travis B. Hartwell Henrik Harutyunyan @@ -754,6 +764,7 @@ Tim Heaney Henrik Heimbuerger Christian Heimes Thomas Heller +Doug Hellmann Malte Helmert Lance Finn Helsten Gordon P. Hemsley @@ -840,6 +851,7 @@ Lawrence Hudson Michael Hudson Roberto Hueso Gomez Jim Hugunin +Taneli Hukkinen Greg Humphreys Chris Hunt Eric Huss @@ -905,6 +917,8 @@ Jim Jewett Pedro Diaz Jimenez Orjan Johansen Fredrik Johansson +Benjamin Johnson +Benjamin K. Johnson Gregory K. Johnson Kent Johnson Michael Johnson @@ -923,6 +937,7 @@ Kristján Valur Jónsson Jens B. Jorgensen John Jorgensen Sijin Joseph +Paresh Joshi Andreas Jung Tattoo Mabonzo K. Sarah K. @@ -1044,6 +1059,7 @@ Jon Kuhn Andrei Kulakov Ilya Kulakov Upendra Kumar +Senthil Kumaran Toshio Kuratomi Ilia Kurenkov Vladimir Kushnir @@ -1071,6 +1087,7 @@ Wolfgang Langner Detlef Lannert Rémi Lapeyre Soren Larsen +Seth Michael Larson Amos Latteier Keenan Lau Piers Lauder @@ -1144,6 +1161,7 @@ Per Lindqvist Eric Lindvall Gregor Lingl Everett Lipman +Jake Lishman Mirko Liss Alexander Liu Hui Liu @@ -1151,6 +1169,7 @@ Yuan Liu Nick Lockwood Stephanie Lockwood Martin von Löwis +Edward Loper Hugo Lopes Tavares Guillermo López-Anglada Anne Lord @@ -1167,6 +1186,7 @@ Kang-Hao (Kenny) Lu Raymond Lu Lukas Lueg Loren Luke +Steen Lumholt Fredrik Lundh Mike Lundy Zhongyue Luo @@ -1206,6 +1226,7 @@ Owen Martin Sidney San Martín Westley Martínez Sébastien Martini +Iván Márton Roger Masse Nick Mathewson Simon Mathieu @@ -1333,6 +1354,7 @@ Trent Nelson Andrew Nester Osvaldo Santana Neto Chad Netzer +Nick Neumann Max Neunhöffer Anthon van der Neut George Neville-Neil @@ -1345,6 +1367,7 @@ Gustavo Niemeyer Oscar Nierstrasz Lysandros Nikolaou Hrvoje Nikšić +Jan-Eric Nitschke Gregory Nofi Jesse Noller Bill Noon @@ -1536,6 +1559,7 @@ Ashwin Ramaswami Jeff Ramnani Grant Ramsay Bayard Randel +Eashwar Ranganathan Varpu Rantala Brodie Rao Rémi Rampin @@ -1651,11 +1675,13 @@ Amit Saha Suman Saha Koki Saito Hajime Saitou +Vinay Sajip George Sakkis Victor Salgado Rich Salz Kevin Samborn Adrian Sampson +Guillaume Sanchez Nevada Sanchez James Sanders Ilya Sandler @@ -1677,6 +1703,7 @@ David Scherer Wolfgang Scherer Felix Scherz Hynek Schlawack +Jakob Schluse Bob Schmertz Gregor Schmid Ralf Schmitt @@ -1706,6 +1733,7 @@ Jendrik Seipp Michael Selik Yury Selivanov Fred Sells +James Seo Jiwon Seo Iñigo Serna Joakim Sernbrant @@ -1735,6 +1763,7 @@ Charlie Shepherd Bruce Sherwood Gregory Shevchenko Hai Shi +Daniel Shields Alexander Shigin Pete Shinners Michael Shiplett @@ -1865,6 +1894,7 @@ John Szakmeister Piotr Szczepaniak Amir Szekely David Szotten +Paweł Szramowski Maciej Szulik Joel Taddei Arfrever Frehtes Taifersar Arahesis @@ -1892,6 +1922,7 @@ Guo Ci Teo Mikhail Terekhov Victor Terrón Pablo Galindo +Rue Ching Teh Richard M. Tew Srinivas Reddy Thatiparthy Tobias Thelen @@ -1902,6 +1933,7 @@ Nicolas M. Thiéry James Thomas Reuben Thomas Robin Thomas +Douglas Thor Brian Thorne Christopher Thorne Stephen Thorne @@ -1915,11 +1947,13 @@ Tim Tisdall Jason Tishler Christian Tismer Jim Tittsler +Abhishek Tiwari Frank J. Tobin James Tocknell Bennett Todd R Lindsay Todd Eugene Toder +Heikki Toivonen Erik Tollerud Stephen Tonkin Matias Torchinsky @@ -2111,12 +2145,14 @@ Xiang Zhang Robert Xiao Florent Xicluna Yanbo, Xie +Kaisheng Xu Xinhang Xu Arnon Yaari Alakshendra Yadav Hirokazu Yamamoto Masayuki Yamamoto Zhikang Yan +Jeffrey Yasskin Jingchen Ye Ka-Ping Yee Chi Hsuan Yen diff --git a/Misc/Brewfile b/Misc/Brewfile new file mode 100644 index 00000000000000..c799f099957f75 --- /dev/null +++ b/Misc/Brewfile @@ -0,0 +1,14 @@ +brew "gdbm" +brew "mpdecimal" +brew "openssl@3.5" +brew "pkg-config" +brew "tcl-tk@9" +brew "xz" +brew "zstd" + +brew "bzip2" if OS.linux? +brew "libedit" if OS.linux? +brew "libffi" if OS.linux? +brew "ncurses" if OS.linux? +brew "unzip" if OS.linux? +brew "zlib-ng-compat" if OS.linux? diff --git a/Misc/NEWS.d/3.10.0a1.rst b/Misc/NEWS.d/3.10.0a1.rst index f09842f1e77dea..349a7855e41c00 100644 --- a/Misc/NEWS.d/3.10.0a1.rst +++ b/Misc/NEWS.d/3.10.0a1.rst @@ -95,7 +95,7 @@ convention. Patch by Donghee Na. .. bpo: 1635741 .. date: 2020-09-26-14-43-30 .. nonce: aJS9B3 -.. section: Core and Builtins +.. section: Library Port the :mod:`!_bisect` module to the multi-phase initialization API (:pep:`489`). @@ -126,7 +126,7 @@ Taskaya. .. bpo: 1635741 .. date: 2020-09-12-18-34-34 .. nonce: lh335O -.. section: Core and Builtins +.. section: Library Port the :mod:`!_lsprof` extension module to multi-phase initialization (:pep:`489`). @@ -136,7 +136,7 @@ Port the :mod:`!_lsprof` extension module to multi-phase initialization .. bpo: 1635741 .. date: 2020-09-08-21-58-47 .. nonce: vdjSLH -.. section: Core and Builtins +.. section: Library Port the :mod:`cmath` extension module to multi-phase initialization (:pep:`489`). @@ -146,7 +146,7 @@ Port the :mod:`cmath` extension module to multi-phase initialization .. bpo: 1635741 .. date: 2020-09-08-20-39-43 .. nonce: jiXmyT -.. section: Core and Builtins +.. section: Library Port the :mod:`!_scproxy` extension module to multi-phase initialization (:pep:`489`). @@ -156,7 +156,7 @@ Port the :mod:`!_scproxy` extension module to multi-phase initialization .. bpo: 1635741 .. date: 2020-09-07-11-35-02 .. nonce: rvIexb -.. section: Core and Builtins +.. section: Library Port the :mod:`termios` extension module to multi-phase initialization (:pep:`489`). @@ -166,7 +166,7 @@ Port the :mod:`termios` extension module to multi-phase initialization .. bpo: 1635741 .. date: 2020-09-07-09-45-47 .. nonce: QuDIut -.. section: Core and Builtins +.. section: Library Convert the :mod:`!_sha256` extension module types to heap types. @@ -185,7 +185,7 @@ classes with a huge amount of arguments. Patch by Pablo Galindo. .. bpo: 1635741 .. date: 2020-09-01-17-22-35 .. nonce: CnRME3 -.. section: Core and Builtins +.. section: Library Port the :mod:`!_overlapped` extension module to multi-phase initialization (:pep:`489`). @@ -195,7 +195,7 @@ Port the :mod:`!_overlapped` extension module to multi-phase initialization .. bpo: 1635741 .. date: 2020-09-01-17-08-07 .. nonce: X9CZgo -.. section: Core and Builtins +.. section: Library Port the :mod:`!_curses_panel` extension module to multi-phase initialization (:pep:`489`). @@ -205,7 +205,7 @@ Port the :mod:`!_curses_panel` extension module to multi-phase initialization .. bpo: 1635741 .. date: 2020-09-01-17-06-02 .. nonce: 5jZymK -.. section: Core and Builtins +.. section: Library Port the :mod:`!_opcode` extension module to multi-phase initialization (:pep:`489`). @@ -225,7 +225,7 @@ format string in f-string and :meth:`str.format`. .. bpo: 41675 .. date: 2020-08-31-14-53-17 .. nonce: VSoqWU -.. section: Core and Builtins +.. section: Library The implementation of :func:`signal.siginterrupt` now uses :c:func:`!sigaction` (if it is available in the system) instead of the @@ -257,7 +257,7 @@ Fix a crash that occurred when destroying subclasses of .. bpo: 1635741 .. date: 2020-08-28-20-54-04 .. nonce: 7ijlcI -.. section: Core and Builtins +.. section: Library Port the :mod:`zlib` extension module to multi-phase initialization (:pep:`489`). @@ -280,7 +280,7 @@ initialized ``_ast`` module. .. bpo: 40077 .. date: 2020-08-25-22-43-33 .. nonce: vcxSUa -.. section: Core and Builtins +.. section: Library Convert :mod:`!_operator` to use :c:func:`PyType_FromSpec`. @@ -298,7 +298,7 @@ Port :mod:`!_sha3` to multi-phase init. Convert static types to heap types. .. bpo: 1635741 .. date: 2020-08-13-07-18-05 .. nonce: FC13e7 -.. section: Core and Builtins +.. section: Library Port the :mod:`!_blake2` extension module to the multi-phase initialization API (:pep:`489`). @@ -337,7 +337,7 @@ The output of ``python --help`` contains now only ASCII characters. .. bpo: 1635741 .. date: 2020-08-10-16-11-32 .. nonce: O0d3ym -.. section: Core and Builtins +.. section: Library Port the :mod:`!_sha1`, :mod:`!_sha512`, and :mod:`!_md5` extension modules to multi-phase initialization API (:pep:`489`). @@ -454,7 +454,7 @@ Port :mod:`multiprocessing` to multi-phase initialization .. bpo: 1635741 .. date: 2020-07-06-20-43-19 .. nonce: LYhsni -.. section: Core and Builtins +.. section: Library Port :mod:`winapi` to multiphase initialization @@ -486,7 +486,7 @@ will tagged as so. .. bpo: 1635741 .. date: 2020-07-03-23-10-02 .. nonce: F5coWe -.. section: Core and Builtins +.. section: Library Port :mod:`faulthandler` to multiphase initialization. @@ -495,7 +495,7 @@ Port :mod:`faulthandler` to multiphase initialization. .. bpo: 1635741 .. date: 2020-07-01-20-17-38 .. nonce: -AtPYu -.. section: Core and Builtins +.. section: Library Port :mod:`sha256` to multiphase initialization @@ -634,7 +634,7 @@ Remove the remaining files from the old parser and the :mod:`symbol` module. .. bpo: 40077 .. date: 2020-06-18-19-04-30 .. nonce: _yI-ax -.. section: Core and Builtins +.. section: Library Convert :mod:`!_bz2` to use :c:func:`PyType_FromSpec`. @@ -712,7 +712,7 @@ Fix refleak in _Py_fopen_obj() when PySys_Audit() fails .. bpo: 40950 .. date: 2020-06-12-00-12-28 .. nonce: tzMy7m -.. section: Core and Builtins +.. section: Library Add a state to the :mod:`!nis` module (:pep:`3121`) and apply the multiphase initialization. Patch by Donghee Na. @@ -999,7 +999,7 @@ Improve performance of generators by not raising internal StopIteration. .. bpo: 1635741 .. date: 2020-04-10-23-54-57 .. nonce: ZURqoN -.. section: Core and Builtins +.. section: Library Port :mod:`mmap` to multiphase initialization. @@ -1008,7 +1008,7 @@ Port :mod:`mmap` to multiphase initialization. .. bpo: 1635741 .. date: 2020-04-05-02-35-08 .. nonce: Kfe9fT -.. section: Core and Builtins +.. section: Library Port :mod:`!_lzma` to multiphase initialization. @@ -1040,7 +1040,7 @@ representation of an integer. Patch by Niklas Fiekas. .. bpo: 36982 .. date: 2019-05-25-05-27-39 .. nonce: 0UHgfB -.. section: Core and Builtins +.. section: Library Use ncurses extended color functions when available to support terminals with 256 colors, and add the new function @@ -3275,8 +3275,8 @@ Types created with :c:func:`PyType_FromSpec` now make any signature in their .. nonce: u6Xfr2 .. section: C API -Fix bug in PyOS_mystrnicmp and PyOS_mystricmp that incremented pointers -beyond the end of a string. +Fix bug in :c:func:`PyOS_mystrnicmp` and :c:func:`PyOS_mystricmp` that +incremented pointers beyond the end of a string. .. diff --git a/Misc/NEWS.d/3.10.0a2.rst b/Misc/NEWS.d/3.10.0a2.rst index 3e82de9ef266d6..013e13bfa88456 100644 --- a/Misc/NEWS.d/3.10.0a2.rst +++ b/Misc/NEWS.d/3.10.0a2.rst @@ -212,7 +212,7 @@ Micro optimization for range.index if step is 1. Patch by Donghee Na. .. bpo: 41435 .. date: 2020-08-07-13-42-48 .. nonce: qPWjJA -.. section: Core and Builtins +.. section: Library Add ``sys._current_exceptions()`` function to retrieve a dictionary mapping each thread's identifier to the topmost exception currently active in that @@ -593,7 +593,7 @@ of treating them as a match. .. nonce: ONk9Na .. section: Library -Fix ``--outfile`` for :mod:`cProfile` / :mod:`profile` not writing the +Fix ``--outfile`` for :mod:`!cProfile` / :mod:`!profile` not writing the output file in the original directory when the program being profiled changes the working directory. PR by Anthony Sottile. diff --git a/Misc/NEWS.d/3.10.0a3.rst b/Misc/NEWS.d/3.10.0a3.rst index 6cf3db3eb43c8b..ec02216849766f 100644 --- a/Misc/NEWS.d/3.10.0a3.rst +++ b/Misc/NEWS.d/3.10.0a3.rst @@ -89,7 +89,7 @@ non-buffer object. .. bpo: 1635741 .. date: 2020-11-18-23-46-31 .. nonce: GVOQ-m -.. section: Core and Builtins +.. section: Library Port the ``_warnings`` extension module to the multi-phase initialization API (:pep:`489`). Patch by Victor Stinner. @@ -204,7 +204,7 @@ Pablo Galindo. .. bpo: 40077 .. date: 2020-11-03-21-58-27 .. nonce: a9qM1j -.. section: Core and Builtins +.. section: Library Convert :mod:`queue` to use heap types. @@ -223,7 +223,7 @@ objects. See PEP 626 for details. .. bpo: 40077 .. date: 2020-11-02-14-39-48 .. nonce: grY9TG -.. section: Core and Builtins +.. section: Library Convert :mod:`mmap` to use heap types. diff --git a/Misc/NEWS.d/3.10.0a4.rst b/Misc/NEWS.d/3.10.0a4.rst index 19f0db9a6be5e9..cd419dfaaee2e8 100644 --- a/Misc/NEWS.d/3.10.0a4.rst +++ b/Misc/NEWS.d/3.10.0a4.rst @@ -102,7 +102,7 @@ blocks .. bpo: 42639 .. date: 2020-12-09-01-55-10 .. nonce: 5pI5HG -.. section: Core and Builtins +.. section: Library Make the :mod:`atexit` module state per-interpreter. It is now safe have more than one :mod:`atexit` module instance. Patch by Donghee Na and Victor @@ -124,7 +124,7 @@ the filename. .. bpo: 42195 .. date: 2020-11-20-00-57-47 .. nonce: HeqcpS -.. section: Core and Builtins +.. section: Library The ``__args__`` of the parameterized generics for :data:`typing.Callable` and :class:`collections.abc.Callable` are now consistent. The ``__args__`` @@ -143,7 +143,7 @@ Ken Jin. .. bpo: 40137 .. date: 2020-11-19-23-12-57 .. nonce: bihl9O -.. section: Core and Builtins +.. section: Library Convert functools module to use :c:func:`PyType_FromModuleAndSpec`. @@ -152,7 +152,7 @@ Convert functools module to use :c:func:`PyType_FromModuleAndSpec`. .. bpo: 40077 .. date: 2020-11-03-13-46-10 .. nonce: NfAIdj -.. section: Core and Builtins +.. section: Library Convert :mod:`array` to use heap types, and establish module state for these. @@ -162,7 +162,7 @@ these. .. bpo: 42008 .. date: 2020-10-12-14-51-59 .. nonce: ijWw2I -.. section: Core and Builtins +.. section: Library Fix _random.Random() seeding. @@ -171,7 +171,7 @@ Fix _random.Random() seeding. .. bpo: 1635741 .. date: 2020-09-12-19-21-52 .. nonce: F2kDrU -.. section: Core and Builtins +.. section: Library Port the :mod:`pyexpat` extension module to multi-phase initialization (:pep:`489`). diff --git a/Misc/NEWS.d/3.10.0a5.rst b/Misc/NEWS.d/3.10.0a5.rst index a85ea1ff1c2817..fd91a380b6d7a3 100644 --- a/Misc/NEWS.d/3.10.0a5.rst +++ b/Misc/NEWS.d/3.10.0a5.rst @@ -136,7 +136,7 @@ frame.f_lineno is correct even if frame.f_trace is set to True .. bpo: 37324 .. date: 2020-12-12-20-09-12 .. nonce: jB-9_U -.. section: Core and Builtins +.. section: Library Remove deprecated aliases to :ref:`collections-abstract-base-classes` from the :mod:`collections` module. @@ -426,7 +426,7 @@ specified using a relative path and the current directory changed. .. nonce: Jq6Az- .. section: Library -Fix CLI of :mod:`cProfile` and :mod:`profile` to catch +Fix CLI of :mod:`!cProfile` and :mod:`!profile` to catch :exc:`BrokenPipeError`. .. diff --git a/Misc/NEWS.d/3.10.0a6.rst b/Misc/NEWS.d/3.10.0a6.rst index 31b7df2c61158e..ec5b868708ba0b 100644 --- a/Misc/NEWS.d/3.10.0a6.rst +++ b/Misc/NEWS.d/3.10.0a6.rst @@ -88,7 +88,7 @@ Patch by Pablo Galindo. .. bpo: 42819 .. date: 2021-01-04-23-54-34 .. nonce: 4KO6wU -.. section: Core and Builtins +.. section: Library :mod:`readline`: Explicitly disable bracketed paste in the interactive interpreter, even if it's set in the inputrc, is enabled by default (eg GNU @@ -144,7 +144,7 @@ Implement :pep:`634` (structural pattern matching). Patch by Brandt Bucher. .. bpo: 40692 .. date: 2020-05-19-22-10-05 .. nonce: ajEhrR -.. section: Core and Builtins +.. section: Library In the :class:`concurrent.futures.ProcessPoolExecutor`, validate that :func:`multiprocess.synchronize` is available on a given platform and rely diff --git a/Misc/NEWS.d/3.10.0a7.rst b/Misc/NEWS.d/3.10.0a7.rst index d866e805fd3a7e..33e5e073202efc 100644 --- a/Misc/NEWS.d/3.10.0a7.rst +++ b/Misc/NEWS.d/3.10.0a7.rst @@ -81,7 +81,7 @@ instruction dispatch a bit. .. bpo: 40645 .. date: 2021-03-29-11-55-06 .. nonce: PhaT-B -.. section: Core and Builtins +.. section: Library Fix reference leak in the :mod:`!_hashopenssl` extension. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/3.10.0b1.rst b/Misc/NEWS.d/3.10.0b1.rst index 406a5d7853edc0..adae971d4002d1 100644 --- a/Misc/NEWS.d/3.10.0b1.rst +++ b/Misc/NEWS.d/3.10.0b1.rst @@ -202,7 +202,7 @@ line number tables more robust in some circumstances. .. bpo: 43908 .. date: 2021-04-26-21-20-41 .. nonce: 2L51nO -.. section: Core and Builtins +.. section: Library Make :mod:`re` types immutable. Patch by Erlend E. Aasland. @@ -211,7 +211,7 @@ Make :mod:`re` types immutable. Patch by Erlend E. Aasland. .. bpo: 43908 .. date: 2021-04-26-20-59-17 .. nonce: -COW4- -.. section: Core and Builtins +.. section: Library Make the :class:`array.array` type immutable. Patch by Erlend E. Aasland. @@ -402,8 +402,8 @@ the heap. Should speed up dispatch in the interpreter. .. nonce: eUn4p5 .. section: Core and Builtins -Static methods (:func:`@staticmethod <staticmethod>`) and class methods -(:func:`@classmethod <classmethod>`) now inherit the method attributes +Static methods (:deco:`staticmethod`) and class methods +(:deco:`classmethod`) now inherit the method attributes (``__module__``, ``__name__``, ``__qualname__``, ``__doc__``, ``__annotations__``) and have a new ``__wrapped__`` attribute. Patch by Victor Stinner. @@ -442,7 +442,7 @@ coroutine. .. bpo: 43105 .. date: 2021-03-31-20-35-11 .. nonce: PBVmHm -.. section: Core and Builtins +.. section: Library Importlib now resolves relative paths when creating module spec objects from file locations. @@ -454,7 +454,7 @@ file locations. .. nonce: VSF3vg .. section: Core and Builtins -Static methods (:func:`@staticmethod <staticmethod>`) are now callable as +Static methods (:deco:`staticmethod`) are now callable as regular functions. Patch by Victor Stinner. .. diff --git a/Misc/NEWS.d/3.11.0a1.rst b/Misc/NEWS.d/3.11.0a1.rst index 2c8e349d3c8bfb..d9f52c3b6f547e 100644 --- a/Misc/NEWS.d/3.11.0a1.rst +++ b/Misc/NEWS.d/3.11.0a1.rst @@ -127,7 +127,7 @@ default value of ``1`` for the ``length`` argument. .. bpo: 44219 .. date: 2021-09-09-10-32-33 .. nonce: WiYyjz -.. section: Core and Builtins +.. section: Library Release the GIL while performing ``isatty`` system calls on arbitrary file descriptors. In particular, this affects :func:`os.isatty`, @@ -149,7 +149,7 @@ Added fallback to extension modules with '.sl' suffix on HP-UX .. bpo: 45121 .. date: 2021-09-07-17-10-16 .. nonce: iG-Hsf -.. section: Core and Builtins +.. section: Library Fix issue where ``Protocol.__init__`` raises ``RecursionError`` when it's called directly or via ``super()``. Patch provided by Yurii Karabas. @@ -269,7 +269,7 @@ it's running out of the source tree). .. bpo: 45012 .. date: 2021-08-31-11-09-52 .. nonce: ueeOcx -.. section: Core and Builtins +.. section: Library In :mod:`posix`, release GIL during ``stat()``, ``lstat()``, and ``fstatat()`` syscalls made by :func:`os.DirEntry.stat`. Patch by Stanisław @@ -309,7 +309,7 @@ objects. Patch by Pablo Galindo. .. bpo: 44962 .. date: 2021-08-23-19-55-08 .. nonce: J00ftt -.. section: Core and Builtins +.. section: Library Fix a race in WeakKeyDictionary, WeakValueDictionary and WeakSet when two threads attempt to commit the last pending removal. This fixes @@ -379,7 +379,7 @@ new instructions: .. bpo: 44929 .. date: 2021-08-16-23-16-17 .. nonce: qpMEky -.. section: Core and Builtins +.. section: Library Fix some edge cases of ``enum.Flag`` string representation in the REPL. Patch by Pablo Galindo. @@ -728,7 +728,7 @@ Collapse union of equal types. E.g. the result of ``int | int`` is now .. bpo: 44611 .. date: 2021-07-16-01-01-11 .. nonce: LcfHN- -.. section: Core and Builtins +.. section: Library On Windows, :func:`os.urandom`: uses BCryptGenRandom API instead of CryptGenRandom API which is deprecated from Microsoft Windows API. Patch by @@ -840,7 +840,7 @@ from dynload_shlib.c. .. bpo: 44490 .. date: 2021-07-06-22-22-15 .. nonce: BJxPbZ -.. section: Core and Builtins +.. section: Library :mod:`typing` now searches for type parameters in ``types.Union`` objects. ``get_type_hints`` will also properly resolve annotations with nested @@ -875,7 +875,7 @@ Remove uses of :c:func:`PyObject_GC_Del` in error path when initializing .. bpo: 41486 .. date: 2021-07-04-17-41-47 .. nonce: DiM24a -.. section: Core and Builtins +.. section: Library Fix a memory consumption and copying performance regression in earlier 3.10 beta releases if someone used an output buffer larger than 4GiB with @@ -1116,7 +1116,7 @@ Improve tokenizer error with improved locations. Patch by Pablo Galindo. .. bpo: 44304 .. date: 2021-06-05-02-34-57 .. nonce: _MAoPc -.. section: Core and Builtins +.. section: Library Fix a crash in the :mod:`sqlite3` module that happened when the garbage collector clears :class:`sqlite.Statement` objects. Patch by Pablo Galindo @@ -2953,7 +2953,7 @@ support for Metadata 2.2. .. nonce: xTUyyX .. section: Library -Remove the :func:`@asyncio.coroutine <asyncio.coroutine>` :term:`decorator` +Remove the :deco:`asyncio.coroutine` :term:`decorator` enabling legacy generator-based coroutines to be compatible with async/await code; remove :class:`asyncio.coroutines.CoroWrapper` used for wrapping legacy coroutine objects in the debug mode. The decorator has been diff --git a/Misc/NEWS.d/3.11.0a2.rst b/Misc/NEWS.d/3.11.0a2.rst index 48cf2c1e428d87..12e03b46db0b3f 100644 --- a/Misc/NEWS.d/3.11.0a2.rst +++ b/Misc/NEWS.d/3.11.0a2.rst @@ -1188,7 +1188,7 @@ context objects can now be disabled. .. nonce: Z0Zk_m .. section: C API -Exclude :c:func:`PyWeakref_GET_OBJECT` from the limited C API. It never +Exclude :c:func:`!PyWeakref_GET_OBJECT` from the limited C API. It never worked since the :c:type:`!PyWeakReference` structure is opaque in the limited C API. diff --git a/Misc/NEWS.d/3.11.0a3.rst b/Misc/NEWS.d/3.11.0a3.rst index 6a0ae20d1fb5ed..c051074f50a148 100644 --- a/Misc/NEWS.d/3.11.0a3.rst +++ b/Misc/NEWS.d/3.11.0a3.rst @@ -133,7 +133,7 @@ additional allocation when the frame object outlives the frame activation. .. bpo: 45614 .. date: 2021-11-23-12-06-41 .. nonce: fIekgI -.. section: Core and Builtins +.. section: Library Fix :mod:`traceback` display for exceptions with invalid module name. diff --git a/Misc/NEWS.d/3.11.0a4.rst b/Misc/NEWS.d/3.11.0a4.rst index 47cbf33c3bb29b..8fc565bb76b9fc 100644 --- a/Misc/NEWS.d/3.11.0a4.rst +++ b/Misc/NEWS.d/3.11.0a4.rst @@ -119,7 +119,7 @@ perform tracing and optimizer checks. .. bpo: 46208 .. date: 2022-01-04-01-53-35 .. nonce: i00Vz5 -.. section: Core and Builtins +.. section: Library Fix the regression of os.path.normpath("A/../../B") not returning expected "../B" but "B". @@ -178,7 +178,7 @@ sequence of other opcodes. .. bpo: 46085 .. date: 2021-12-30-00-23-41 .. nonce: bDuJqu -.. section: Core and Builtins +.. section: Library Fix iterator cache mechanism of :class:`OrderedDict`. @@ -344,7 +344,7 @@ themselves before raising exceptions. Patch by Pablo Galindo. .. bpo: 46000 .. date: 2021-12-07-11-42-44 .. nonce: v_ru3k -.. section: Core and Builtins +.. section: Library Improve compatibility of the :mod:`curses` module with NetBSD curses. diff --git a/Misc/NEWS.d/3.11.0a5.rst b/Misc/NEWS.d/3.11.0a5.rst index 5418d5d59dd583..de03458062b95d 100644 --- a/Misc/NEWS.d/3.11.0a5.rst +++ b/Misc/NEWS.d/3.11.0a5.rst @@ -204,7 +204,7 @@ platform when dividing an int by a value smaller than ``2**30``. .. bpo: 46383 .. date: 2022-01-14-20-55-34 .. nonce: v8MTl4 -.. section: Core and Builtins +.. section: Library Fix invalid signature of ``_zoneinfo``'s ``module_free`` function to resolve a crash on wasm32-emscripten platform. @@ -255,7 +255,7 @@ that are ended by line continuation characters. Patch by Pablo Galindo .. bpo: 30512 .. date: 2021-12-12-00-49-19 .. nonce: nU9E9V -.. section: Core and Builtins +.. section: Library Add CAN Socket support for NetBSD. @@ -264,7 +264,7 @@ Add CAN Socket support for NetBSD. .. bpo: 46045 .. date: 2021-12-11-11-36-48 .. nonce: sfThay -.. section: Core and Builtins +.. section: Build Do not use POSIX semaphores on NetBSD diff --git a/Misc/NEWS.d/3.11.0a6.rst b/Misc/NEWS.d/3.11.0a6.rst index e88142e641f040..7fdfa4ebdf2e37 100644 --- a/Misc/NEWS.d/3.11.0a6.rst +++ b/Misc/NEWS.d/3.11.0a6.rst @@ -255,7 +255,7 @@ Fix specialization stats gathering for :opcode:`!PRECALL` instructions. .. bpo: 46794 .. date: 2022-02-22-12-07-53 .. nonce: 6WvJ9o -.. section: Core and Builtins +.. section: Library Bump up the libexpat version into 2.4.6 @@ -379,7 +379,7 @@ involving lots of brackets. Patch by Pablo Galindo. .. bpo: 46323 .. date: 2022-02-10-02-29-12 .. nonce: HK_cs0 -.. section: Core and Builtins +.. section: Library :mod:`ctypes` now allocates memory on the stack instead of on the heap to pass arguments while calling a Python callback function. Patch by Donghee @@ -429,7 +429,7 @@ now 254. .. bpo: 40479 .. date: 2022-02-06-23-08-30 .. nonce: zED3Zu -.. section: Core and Builtins +.. section: Library Add a missing call to ``va_end()`` in ``Modules/_hashopenssl.c``. @@ -438,7 +438,7 @@ Add a missing call to ``va_end()`` in ``Modules/_hashopenssl.c``. .. bpo: 46323 .. date: 2022-02-05-14-46-21 .. nonce: FC1OJg -.. section: Core and Builtins +.. section: Library Use :c:func:`PyObject_Vectorcall` while calling ctypes callback function. Patch by Donghee Na. diff --git a/Misc/NEWS.d/3.11.0a7.rst b/Misc/NEWS.d/3.11.0a7.rst index eff2ea2dac13f8..fddbb63218d1b9 100644 --- a/Misc/NEWS.d/3.11.0a7.rst +++ b/Misc/NEWS.d/3.11.0a7.rst @@ -181,7 +181,7 @@ to simplify clearing and deallocing frames and generators. .. bpo: 46968 .. date: 2022-03-17-14-22-23 .. nonce: 4gz4NA -.. section: Core and Builtins +.. section: Library Check for the existence of the "sys/auxv.h" header in :mod:`faulthandler` to avoid compilation problems in systems where this header doesn't exist. Patch @@ -243,7 +243,7 @@ reducing the number of invocations of ``memcpy``. .. bpo: 46829 .. date: 2022-03-12-21-07-21 .. nonce: cpGoPV -.. section: Core and Builtins +.. section: Library Deprecate passing a message into :meth:`asyncio.Future.cancel` and :meth:`asyncio.Task.cancel` diff --git a/Misc/NEWS.d/3.11.0b1.rst b/Misc/NEWS.d/3.11.0b1.rst index c3a1942b881ad4..b7ea1f018838a2 100644 --- a/Misc/NEWS.d/3.11.0b1.rst +++ b/Misc/NEWS.d/3.11.0b1.rst @@ -388,7 +388,7 @@ Add internal documentation explaining design of new (for 3.11) frame stack. .. bpo: 47197 .. date: 2022-04-03-17-21-04 .. nonce: Ji_c30 -.. section: Core and Builtins +.. section: Library ctypes used to mishandle ``void`` return types, so that for instance a function declared like ``ctypes.CFUNCTYPE(None, ctypes.c_int)`` would be @@ -500,7 +500,7 @@ at runtime where types are known at C compile time. .. bpo: 46045 .. date: 2021-12-11-11-36-48 .. nonce: sfThay -.. section: Core and Builtins +.. section: Build Do not use POSIX semaphores on NetBSD @@ -664,7 +664,7 @@ for :func:`os.fcopyfile` available in macOs. .. nonce: l1p7CJ .. section: Library -For :func:`@dataclass <dataclasses.dataclass>`, add *weakref_slot*. +For :deco:`~dataclasses.dataclass`, add *weakref_slot*. The new parameter defaults to ``False``. If true, and if ``slots=True``, add a slot named ``"__weakref__"``, which will allow instances to be weakref'd. Contributed by Eric V. Smith diff --git a/Misc/NEWS.d/3.12.0a1.rst b/Misc/NEWS.d/3.12.0a1.rst index f2668e99a6299b..c977022d87fc95 100644 --- a/Misc/NEWS.d/3.12.0a1.rst +++ b/Misc/NEWS.d/3.12.0a1.rst @@ -160,7 +160,7 @@ to calculate those doing pointer arithmetic. .. date: 2022-10-06-15-45-57 .. gh-issue: 96078 .. nonce: fS-6mU -.. section: Core and Builtins +.. section: Library :func:`os.sched_yield` now release the GIL while calling sched_yield(2). Patch by Donghee Na. @@ -170,7 +170,7 @@ Patch by Donghee Na. .. date: 2022-10-06-14-14-28 .. gh-issue: 97955 .. nonce: Nq5VXD -.. section: Core and Builtins +.. section: Library Migrate :mod:`zoneinfo` to Argument Clinic. @@ -361,7 +361,7 @@ branching conditions. .. date: 2022-09-19-03-35-01 .. gh-issue: 96821 .. nonce: izK6JA -.. section: Core and Builtins +.. section: Library Fix undefined behaviour in ``audioop.c``. @@ -481,7 +481,7 @@ Fix case of undefined behavior in ceval.c .. date: 2022-09-08-20-58-10 .. gh-issue: 64373 .. nonce: AfCi36 -.. section: Core and Builtins +.. section: Library Convert :mod:`!_functools` to argument clinic. @@ -490,7 +490,7 @@ Convert :mod:`!_functools` to argument clinic. .. date: 2022-09-07-13-38-37 .. gh-issue: 96641 .. nonce: wky0Fc -.. section: Core and Builtins +.. section: Library Do not expose ``KeyWrapper`` in :mod:`!_functools`. @@ -990,7 +990,7 @@ bytecode compiler. .. date: 2022-07-20-09-04-55 .. gh-issue: 95023 .. nonce: bs-xd7 -.. section: Core and Builtins +.. section: Library Implement :func:`os.setns` and :func:`os.unshare` for Linux. Patch by Noam Cohen. @@ -1021,7 +1021,7 @@ Previously it could cause SystemError or other undesired behavior. .. date: 2022-07-19-04-34-56 .. gh-issue: 94996 .. nonce: dV564A -.. section: Core and Builtins +.. section: Library :func:`ast.parse` will no longer parse function definitions with positional-only params when passed ``feature_version`` less than ``(3, 8)``. @@ -1041,7 +1041,7 @@ Allow jumping within, out of, and across exception handlers in the debugger. .. date: 2022-07-18-05-10-29 .. gh-issue: 94949 .. nonce: OsZ7_s -.. section: Core and Builtins +.. section: Library :func:`ast.parse` will no longer parse parenthesized context managers when passed ``feature_version`` less than ``(3, 9)``. Patch by Shantanu Jain. @@ -1051,7 +1051,7 @@ passed ``feature_version`` less than ``(3, 9)``. Patch by Shantanu Jain. .. date: 2022-07-18-04-48-34 .. gh-issue: 94947 .. nonce: df9gUw -.. section: Core and Builtins +.. section: Library :func:`ast.parse` will no longer parse assignment expressions when passed ``feature_version`` less than ``(3, 8)``. Patch by Shantanu Jain. @@ -1394,7 +1394,7 @@ calls. Previously, the end column could precede the column offset. .. date: 2022-06-09-19-19-02 .. gh-issue: 93461 .. nonce: 5DqP1e -.. section: Core and Builtins +.. section: Library :func:`importlib.invalidate_caches` now drops entries from :data:`sys.path_importer_cache` with a relative path as name. This solves a @@ -1729,7 +1729,7 @@ tracing functions implemented in C. .. date: 2022-05-11-09-16-54 .. gh-issue: 91102 .. nonce: lenv9h -.. section: Core and Builtins +.. section: Library :meth:`!_warnings.warn_explicit` is ported to Argument Clinic. @@ -1759,7 +1759,7 @@ no longer does anything. .. date: 2022-05-03-20-12-18 .. gh-issue: 92261 .. nonce: aigLnb -.. section: Core and Builtins +.. section: Library Fix hang when trying to iterate over a ``typing.Union``. @@ -4330,7 +4330,7 @@ and ``sendfile`` inside ``IocpProactor``. .. nonce: GsBL9- .. section: Library -Fixed :meth:`collections.UserDict.get` to not call :meth:`__missing__` when +Fixed :meth:`collections.UserDict.get` to not call :meth:`~object.__missing__` when a value is not found. This matches the behavior of :class:`dict`. Patch by Bar Harel. diff --git a/Misc/NEWS.d/3.12.0a2.rst b/Misc/NEWS.d/3.12.0a2.rst index bc028f30636bf7..a4792987ab7e2c 100644 --- a/Misc/NEWS.d/3.12.0a2.rst +++ b/Misc/NEWS.d/3.12.0a2.rst @@ -35,11 +35,11 @@ Update bundled libexpat to 2.5.0 .. nonce: ik4iOv .. section: Core and Builtins -The docs clearly say that ``PyImport_Inittab``, +The docs clearly say that :c:data:`PyImport_Inittab`, :c:func:`PyImport_AppendInittab`, and :c:func:`PyImport_ExtendInittab` should not be used after :c:func:`Py_Initialize` has been called. We now enforce this for the two functions. Additionally, the runtime now uses an -internal copy of ``PyImport_Inittab``, to guard against modification. +internal copy of :c:data:`PyImport_Inittab`, to guard against modification. .. @@ -333,7 +333,7 @@ aware of this shim frame and the changes to the semantics of .. date: 2022-10-19-01-01-08 .. gh-issue: 98415 .. nonce: ZS2eWh -.. section: Core and Builtins +.. section: Build Fix detection of MAC addresses for :mod:`uuid` on certain OSs. Patch by Chaim Sanders @@ -405,7 +405,7 @@ Expose :const:`~socket.ETH_P_ALL` and some of the :ref:`ETHERTYPE_* constants .. date: 2022-06-10-16-37-44 .. gh-issue: 93696 .. nonce: 65BI2R -.. section: Core and Builtins +.. section: Library Allow :mod:`pdb` to locate source for frozen modules in the standard library. @@ -957,7 +957,7 @@ Fix ``make regen-test-levenshtein`` for out-of-tree builds. .. nonce: eVXGEx .. section: Build -Don't use vendored ``libmpdec`` headers if :option:`--with-system-libmpdec` +Don't use vendored ``libmpdec`` headers if :option:`!--with-system-libmpdec` is passed to :program:`configure`. Don't use vendored ``libexpat`` headers if :option:`--with-system-expat` is passed to :program:`configure`. diff --git a/Misc/NEWS.d/3.12.0a3.rst b/Misc/NEWS.d/3.12.0a3.rst index 04a2bf9fb916b7..d2c717afcb6e8d 100644 --- a/Misc/NEWS.d/3.12.0a3.rst +++ b/Misc/NEWS.d/3.12.0a3.rst @@ -100,7 +100,7 @@ Fix bug where an :exc:`ExceptionGroup` subclass can wrap a .. date: 2022-11-16-21-35-30 .. gh-issue: 99547 .. nonce: p_c_bp -.. section: Core and Builtins +.. section: Library Add a function to os.path to check if a path is a junction: isjunction. Add similar functionality to pathlib.Path as is_junction. @@ -110,7 +110,7 @@ similar functionality to pathlib.Path as is_junction. .. date: 2022-11-12-01-39-57 .. gh-issue: 99370 .. nonce: _cu32j -.. section: Core and Builtins +.. section: Library Fix zip path for venv created from a non-installed python on POSIX platforms. diff --git a/Misc/NEWS.d/3.12.0a4.rst b/Misc/NEWS.d/3.12.0a4.rst index 57fb2052764b6f..1fdebf54da9cdc 100644 --- a/Misc/NEWS.d/3.12.0a4.rst +++ b/Misc/NEWS.d/3.12.0a4.rst @@ -125,7 +125,7 @@ Improve the accuracy of ``sum()`` with compensated summation. .. date: 2022-12-20-16-14-19 .. gh-issue: 100374 .. nonce: YRrVHT -.. section: Core and Builtins +.. section: Library Fix incorrect result and delay in :func:`socket.getfqdn`. Patch by Dominic Socular. @@ -315,7 +315,7 @@ Improve performance of ``list.pop`` for small lists. .. date: 2022-06-17-08-00-34 .. gh-issue: 89051 .. nonce: yP4Na0 -.. section: Core and Builtins +.. section: Library Add :const:`ssl.OP_LEGACY_SERVER_CONNECT` diff --git a/Misc/NEWS.d/3.12.0a5.rst b/Misc/NEWS.d/3.12.0a5.rst index b73bbfbfdc4819..74801bf1add5d8 100644 --- a/Misc/NEWS.d/3.12.0a5.rst +++ b/Misc/NEWS.d/3.12.0a5.rst @@ -181,7 +181,7 @@ regen-all``. .. bpo: 32780 .. date: 2018-02-05-21-54-46 .. nonce: Dtiz8z -.. section: Core and Builtins +.. section: Library Inter-field padding is now inserted into the PEP3118 format strings obtained from :class:`ctypes.Structure` objects, reflecting their true representation diff --git a/Misc/NEWS.d/3.12.0a7.rst b/Misc/NEWS.d/3.12.0a7.rst index f48b9ce0550440..e0c079c5def971 100644 --- a/Misc/NEWS.d/3.12.0a7.rst +++ b/Misc/NEWS.d/3.12.0a7.rst @@ -102,7 +102,7 @@ Shrink the number of inline :opcode:`CACHE` entries used by .. date: 2023-03-08-08-37-36 .. gh-issue: 102491 .. nonce: SFvvsC -.. section: Core and Builtins +.. section: Library Improve import time of ``platform`` by removing IronPython version parsing. The IronPython version parsing was not functional (see @@ -187,7 +187,7 @@ Improve build support for the Xbox. Patch by Max Bachmann. .. date: 2023-02-21-23-42-39 .. gh-issue: 102027 .. nonce: fQARG0 -.. section: Core and Builtins +.. section: Build Fix SSE2 and SSE3 detection in ``_blake2`` internal module. Patch by Max Bachmann. @@ -207,7 +207,7 @@ Deprecate ``co_lnotab`` in code objects, schedule it for removal in Python .. bpo: 1635741 .. date: 2020-07-04-09-04-41 .. nonce: ZsP31Y -.. section: Core and Builtins +.. section: Library Adapt :mod:`!_pickle` to :pep:`687`. Patch by Mohamed Koubaa and Erlend Aasland. diff --git a/Misc/NEWS.d/3.12.0b1.rst b/Misc/NEWS.d/3.12.0b1.rst index 3a3870ac9fe621..e897bfebd24f2c 100644 --- a/Misc/NEWS.d/3.12.0b1.rst +++ b/Misc/NEWS.d/3.12.0b1.rst @@ -44,7 +44,7 @@ response to :cve:`2023-24329`. Patch by Illia Volochii. .. date: 2023-05-20-23-08-48 .. gh-issue: 102856 .. nonce: Knv9WT -.. section: Core and Builtins +.. section: Library Implement PEP 701 changes in the :mod:`tokenize` module. Patch by Marta Gómez Macías and Pablo Galindo Salgado @@ -312,7 +312,7 @@ Patch by Eric Traut, Larry Hastings, and Jelle Zijlstra. .. date: 2023-04-24-21-47-38 .. gh-issue: 103801 .. nonce: WaBanq -.. section: Core and Builtins +.. section: Library Adds three minor linting fixes to the wasm module caught that were caught by ruff. @@ -322,7 +322,7 @@ ruff. .. date: 2023-04-24-14-38-16 .. gh-issue: 103793 .. nonce: kqoH6Q -.. section: Core and Builtins +.. section: Library Optimized asyncio Task creation by deferring expensive string formatting (task name generation) from Task creation to the first time ``get_name`` is @@ -573,7 +573,7 @@ raising an :exc:`IndexError`. .. bpo: 31821 .. date: 2019-12-01-12-58-31 .. nonce: 1FNmwk -.. section: Core and Builtins +.. section: Library Fix :func:`!pause_reading` to work when called from :func:`!connection_made` in :mod:`asyncio`. @@ -1257,7 +1257,7 @@ defined by any build system or documented for manual use. .. nonce: n_AfcS .. section: Library -Update :mod:`cProfile` to use PEP 669 API +Update :mod:`!cProfile` to use PEP 669 API .. diff --git a/Misc/NEWS.d/3.13.0a1.rst b/Misc/NEWS.d/3.13.0a1.rst index 0741eab4eca6a4..0b7b69bbcb2478 100644 --- a/Misc/NEWS.d/3.13.0a1.rst +++ b/Misc/NEWS.d/3.13.0a1.rst @@ -3426,7 +3426,7 @@ This bug was introduced in Python 3.12.0 beta 1. .. nonce: hSlB17 .. section: Library -Deprecate :func:`typing.no_type_check_decorator`. No major type checker ever +Deprecate :func:`!typing.no_type_check_decorator`. No major type checker ever added support for this decorator. Patch by Alex Waygood. .. @@ -3476,7 +3476,7 @@ Fix rare concurrency bug in lock acquisition by the logging package. .. nonce: ya5jBT .. section: Library -Added PY_THROW event hook for :mod:`cProfile` for generators +Added PY_THROW event hook for :mod:`!cProfile` for generators .. @@ -6458,8 +6458,8 @@ Victor Stinner. .. nonce: GRxZtI .. section: C API -Deprecate the :c:func:`PyWeakref_GetObject` and -:c:func:`PyWeakref_GET_OBJECT` functions: use the new +Deprecate the :c:func:`!PyWeakref_GetObject` and +:c:func:`!PyWeakref_GET_OBJECT` functions: use the new :c:func:`PyWeakref_GetRef` function instead. Patch by Victor Stinner. .. @@ -6470,7 +6470,7 @@ Deprecate the :c:func:`PyWeakref_GetObject` and .. section: C API Add :c:func:`PyWeakref_GetRef` function: similar to -:c:func:`PyWeakref_GetObject` but returns a :term:`strong reference`, or +:c:func:`!PyWeakref_GetObject` but returns a :term:`strong reference`, or ``NULL`` if the referent is no longer live. Patch by Victor Stinner. .. @@ -6592,7 +6592,7 @@ functions, deprecated in Python 3.9. Patch by Victor Stinner. Deprecate old Python initialization functions: -* :c:func:`PySys_ResetWarnOptions` +* :c:func:`!PySys_ResetWarnOptions` * :c:func:`!Py_GetExecPrefix` * :c:func:`!Py_GetPath` * :c:func:`!Py_GetPrefix` diff --git a/Misc/NEWS.d/3.13.0a3.rst b/Misc/NEWS.d/3.13.0a3.rst index 0f8dee261c6589..3d3b414c9d46af 100644 --- a/Misc/NEWS.d/3.13.0a3.rst +++ b/Misc/NEWS.d/3.13.0a3.rst @@ -2166,7 +2166,7 @@ current user has no permission to the WMI. .. nonce: WOpiNt .. section: Windows -Deprecate :func:`sys._enablelegacywindowsfsencoding`. Use +Deprecate :func:`!sys._enablelegacywindowsfsencoding`. Use :envvar:`PYTHONLEGACYWINDOWSFSENCODING` instead. Patch by Inada Naoki. .. diff --git a/Misc/NEWS.d/3.13.0a5.rst b/Misc/NEWS.d/3.13.0a5.rst index d56b1542b01823..6f8c82e5af3d75 100644 --- a/Misc/NEWS.d/3.13.0a5.rst +++ b/Misc/NEWS.d/3.13.0a5.rst @@ -136,7 +136,7 @@ threads to be interrupted. .. date: 2024-02-08-16-01-18 .. gh-issue: 115154 .. nonce: ji96FV -.. section: Core and Builtins +.. section: Library Fix a bug that was causing the :func:`tokenize.untokenize` function to handle unicode named literals incorrectly. Patch by Pablo Galindo @@ -156,7 +156,7 @@ Add ability to force alignment of :mod:`ctypes.Structure` by way of the new .. date: 2023-07-16-15-02-47 .. gh-issue: 104090 .. nonce: oMjNa9 -.. section: Core and Builtins +.. section: Library The multiprocessing resource tracker now exits with non-zero status code if a resource leak was detected. It still exits with status code 0 otherwise. @@ -742,8 +742,8 @@ Add ``windows_31j`` to aliases for ``cp932`` codec .. nonce: fv35wU .. section: Library -:func:`functools.partial`s of :func:`repr` has been improved to include the -:term:`module` name. Patched by Furkan Onder and Anilyka Barry. +Always include the :term:`module` name in the :func:`repr` of +:func:`functools.partial` objects. Patch by Furkan Onder and Anilyka Barry. .. diff --git a/Misc/NEWS.d/3.13.0a6.rst b/Misc/NEWS.d/3.13.0a6.rst index 2740b4f0d967ba..b97973fad020af 100644 --- a/Misc/NEWS.d/3.13.0a6.rst +++ b/Misc/NEWS.d/3.13.0a6.rst @@ -224,7 +224,7 @@ When the collecting space becomes empty, the two spaces are swapped. .. date: 2023-10-14-00-05-17 .. gh-issue: 109870 .. nonce: oKpJ3P -.. section: Core and Builtins +.. section: Library Dataclasses now calls :func:`exec` once per dataclass, instead of once per method being added. This can speed up dataclass creation by up to 20%. @@ -234,7 +234,7 @@ method being added. This can speed up dataclass creation by up to 20%. .. date: 2022-10-05-09-33-48 .. gh-issue: 97901 .. nonce: BOLluU -.. section: Core and Builtins +.. section: Library Mime type ``text/rtf`` is now supported by :mod:`mimetypes`. @@ -264,7 +264,8 @@ Improve performance of :func:`os.path.join` and :func:`os.path.expanduser`. .. nonce: hqk9Hn .. section: Library -Raise :exc:`TypeError` for non-paths in :func:`posixpath.relpath`. +Raise :exc:`TypeError` for non-paths in :func:`posixpath.relpath +<os.path.relpath>`. .. @@ -273,7 +274,8 @@ Raise :exc:`TypeError` for non-paths in :func:`posixpath.relpath`. .. nonce: l6rWlj .. section: Library -Preserve mailbox ownership when rewriting in :func:`mailbox.mbox.flush`. +Preserve mailbox ownership when rewriting in :func:`mailbox.mbox.flush +<mailbox.Mailbox.flush>`. Patch by Tony Mountifield. .. diff --git a/Misc/NEWS.d/3.13.0b1.rst b/Misc/NEWS.d/3.13.0b1.rst index 97731276679ba6..1b7a28d84dcb7a 100644 --- a/Misc/NEWS.d/3.13.0b1.rst +++ b/Misc/NEWS.d/3.13.0b1.rst @@ -60,7 +60,7 @@ for speed. .. date: 2024-05-03-18-01-26 .. gh-issue: 95382 .. nonce: 73FSEv -.. section: Core and Builtins +.. section: Library Improve performance of :func:`json.dumps` and :func:`json.dump` when using the argument *indent*. Depending on the data the encoding using @@ -1429,7 +1429,7 @@ script) without specifying a value for ``UseTIER2``. .. nonce: LT27pF .. section: Build -The :file:`configure` option :option:`--with-system-libmpdec` now defaults +The :file:`configure` option :option:`!--with-system-libmpdec` now defaults to ``yes``. The bundled copy of ``libmpdecimal`` will be removed in Python 3.15. @@ -1470,7 +1470,7 @@ Increase WASI stack size from 512 KiB to 8 MiB and the initial memory from .. section: Build :program:`configure` now uses :program:`pkg-config` to detect :mod:`decimal` -dependencies if the :option:`--with-system-libmpdec` option is given. +dependencies if the :option:`!--with-system-libmpdec` option is given. .. diff --git a/Misc/NEWS.d/3.14.0a1.rst b/Misc/NEWS.d/3.14.0a1.rst index 67451a7e0087cb..79936955757280 100644 --- a/Misc/NEWS.d/3.14.0a1.rst +++ b/Misc/NEWS.d/3.14.0a1.rst @@ -1999,7 +1999,7 @@ with an escape character. .. nonce: vi2bP- .. section: Library -:func:`@warnings.deprecated <warnings.deprecated>` now copies the coroutine +:deco:`warnings.deprecated` now copies the coroutine status of functions and methods so that :func:`inspect.iscoroutinefunction` returns the correct result. @@ -2038,7 +2038,7 @@ Remove workarounds for non-IEEE 754 systems in :mod:`cmath`. .. nonce: WlygzR .. section: Library -Due to the lack of interest for :meth:`symtable.Class.get_methods`, the +Due to the lack of interest for :meth:`!symtable.Class.get_methods`, the method is marked as deprecated and will be removed in Python 3.16. Patch by Bénédikt Tran. @@ -2623,7 +2623,7 @@ Fix :func:`unittest.mock.patch` to not read attributes of the target when .. nonce: s4HXR0 .. section: Library -Fixed the use-after-free issue in :mod:`cProfile` by disallowing +Fixed the use-after-free issue in :mod:`!cProfile` by disallowing ``disable()`` and ``clear()`` in external timers. .. @@ -2746,7 +2746,7 @@ situations. .. nonce: rRrprk .. section: Library -Fix :meth:`symtable.Class.get_methods` and document its behaviour. Patch by +Fix :meth:`!symtable.Class.get_methods` and document its behaviour. Patch by Bénédikt Tran. .. @@ -4016,7 +4016,7 @@ Make ``this_instr`` and ``prev_instr`` const in cases generator. .. date: 2024-10-05-23-53-06 .. gh-issue: 125008 .. nonce: ETANpd -.. section: Core and Builtins +.. section: Library Fix :func:`tokenize.untokenize` producing invalid syntax for double braces preceded by certain escape characters. @@ -4275,7 +4275,7 @@ devdanzin .. date: 2024-09-02-20-39-10 .. gh-issue: 123614 .. nonce: 26TMHp -.. section: Core and Builtins +.. section: Library Add :func:`turtle.save` to easily save Turtle drawings as PostScript files. Patch by Marie Roald and Yngve Mardal Moe. @@ -4761,7 +4761,7 @@ enabled (yet). .. date: 2024-07-18-21-19-04 .. gh-issue: 121999 .. nonce: 8IBbTK -.. section: Core and Builtins +.. section: Library The default extraction filter for the :mod:`tarfile` module is now set to :func:`'data' <tarfile.data_filter>`. @@ -4913,11 +4913,11 @@ Allow tuples of length 20 in the freelist to be reused. .. nonce: lYKYYP .. section: Core and Builtins -:exc:`ValueError` messages for :meth:`!list.index`, :meth:`!range.index`, +:exc:`ValueError` messages for :meth:`list.index`, :meth:`range.index`, :meth:`!deque.index`, :meth:`!deque.remove` and :meth:`!ShareableList.index` no longer contain the repr of the searched value (which can be arbitrary -large) and are consistent with error messages for other :meth:`!index` and -:meth:`!remove` methods. +large) and are consistent with error messages for other :meth:`~sequence.index` and +:meth:`~sequence.remove` methods. .. @@ -4955,7 +4955,7 @@ Galindo .. date: 2024-06-28-23-17-22 .. gh-issue: 121381 .. nonce: i2xL7P -.. section: Core and Builtins +.. section: Library Remove ``subprocess._USE_VFORK`` escape hatch code and documentation. It was added just in case, and doesn't have any known cases that require it. @@ -5115,7 +5115,7 @@ and identities of :class:`str` objects. .. date: 2024-06-14-07-52-00 .. gh-issue: 120485 .. nonce: yy4K4b -.. section: Core and Builtins +.. section: Library Add an override of ``allow_reuse_port`` on classes subclassing ``socketserver.TCPServer`` where ``allow_reuse_address`` is also overridden. @@ -5147,7 +5147,7 @@ after exception handlers are moved to the end of the code. .. date: 2024-06-12-18-23-15 .. gh-issue: 120380 .. nonce: edtqjq -.. section: Core and Builtins +.. section: Library Fix Python implementation of :class:`pickle.Pickler` for :class:`bytes` and :class:`bytearray` objects when using protocol version 5. Patch by Bénédikt @@ -5604,7 +5604,7 @@ Using :data:`NotImplemented` in a boolean context now raises .. nonce: wNMKVd .. section: Core and Builtins -Fix race condition in free-threaded build where :meth:`!list.extend` could +Fix race condition in free-threaded build where :meth:`list.extend` could expose uninitialised memory to concurrent readers. .. @@ -5625,7 +5625,7 @@ in the future. .. date: 2024-04-27-18-36-46 .. gh-issue: 115801 .. nonce: SVeHSy -.. section: Core and Builtins +.. section: Library Raise ``TypeError`` when passing a string to :func:`difflib.unified_diff` and :func:`difflib.context_diff`. @@ -6092,7 +6092,7 @@ Patch by Victor Stinner. .. nonce: qOr9GF .. section: C API -Soft deprecate the :c:macro:`!Py_MEMCPY` macro: use directly ``memcpy()`` +Soft deprecate the :c:macro:`Py_MEMCPY` macro: use directly ``memcpy()`` instead. Patch by Victor Stinner. .. diff --git a/Misc/NEWS.d/3.14.0a2.rst b/Misc/NEWS.d/3.14.0a2.rst index 7405a1344a9fa6..2fd86dbe72a29d 100644 --- a/Misc/NEWS.d/3.14.0a2.rst +++ b/Misc/NEWS.d/3.14.0a2.rst @@ -1253,7 +1253,7 @@ including SerenityOS. .. date: 2024-11-09-16-10-22 .. gh-issue: 126066 .. nonce: 9zs4m4 -.. section: Core and Builtins +.. section: Library Fix :mod:`importlib` to not write an incomplete .pyc files when a ulimit or some other operating system mechanism is preventing the write to go through @@ -1285,7 +1285,7 @@ its ``__iter__``. .. date: 2024-11-02-18-01-31 .. gh-issue: 126209 .. nonce: 2ZIhrS -.. section: Core and Builtins +.. section: Library Fix an issue with ``skip_file_prefixes`` parameter which resulted in an inconsistent behaviour between the C and Python implementations of @@ -1400,7 +1400,7 @@ The :class:`memoryview` type now supports subscription, making it a .. nonce: KlCdgD .. section: Core and Builtins -Adds :opcode:`LOAD_SMALL_INT` and :opcode:`LOAD_CONST_IMMORTAL` +Adds :opcode:`LOAD_SMALL_INT` and :opcode:`!LOAD_CONST_IMMORTAL` instructions. ``LOAD_SMALL_INT`` pushes a small integer equal to the ``oparg`` to the stack. ``LOAD_CONST_IMMORTAL`` does the same as ``LOAD_CONST`` but is more efficient for immortal objects. Removes @@ -1567,7 +1567,7 @@ Wannes Boeykens. .. date: 2024-05-12-03-10-36 .. gh-issue: 118950 .. nonce: 5Wc4vp -.. section: Core and Builtins +.. section: Library Fix bug where SSLProtocol.connection_lost wasn't getting called when OSError was thrown on writing to socket. @@ -1577,7 +1577,7 @@ was thrown on writing to socket. .. date: 2023-12-30-00-21-45 .. gh-issue: 113570 .. nonce: _XQgsW -.. section: Core and Builtins +.. section: Library Fixed a bug in ``reprlib.repr`` where it incorrectly called the repr method on shadowed Python built-in types. diff --git a/Misc/NEWS.d/3.14.0a3.rst b/Misc/NEWS.d/3.14.0a3.rst index 8393be8909ff8f..b4264335a2d8c3 100644 --- a/Misc/NEWS.d/3.14.0a3.rst +++ b/Misc/NEWS.d/3.14.0a3.rst @@ -820,7 +820,7 @@ Fix possible undefined behavior division by zero in :class:`complex`'s .. date: 2024-11-23-04-54-42 .. gh-issue: 127133 .. nonce: WMoJjF -.. section: Core and Builtins +.. section: Library Calling :meth:`argparse.ArgumentParser.add_argument_group` on an argument group, and calling :meth:`argparse.ArgumentParser.add_argument_group` or diff --git a/Misc/NEWS.d/3.14.0a4.rst b/Misc/NEWS.d/3.14.0a4.rst index 176ba72da65e4b..94350556093433 100644 --- a/Misc/NEWS.d/3.14.0a4.rst +++ b/Misc/NEWS.d/3.14.0a4.rst @@ -548,7 +548,7 @@ atomic operation. Patch by Donghee Na. .. date: 2024-12-23-11-14-07 .. gh-issue: 128192 .. nonce: 02mEhD -.. section: Core and Builtins +.. section: Library Upgrade HTTP digest authentication algorithm for :mod:`urllib.request` by supporting SHA-256 digest authentication as specified in :rfc:`7616`. @@ -613,7 +613,7 @@ object when importing a non-existent symbol from a non-module object. .. date: 2024-12-17-18-20-37 .. gh-issue: 128035 .. nonce: JwqHdB -.. section: Core and Builtins +.. section: Library Indicate through :data:`ssl.HAS_PHA` whether the :mod:`ssl` module supports TLSv1.3 post-handshake client authentication (PHA). Patch by Will diff --git a/Misc/NEWS.d/3.14.0a5.rst b/Misc/NEWS.d/3.14.0a5.rst index a3548d0a7b0357..6242fce823c569 100644 --- a/Misc/NEWS.d/3.14.0a5.rst +++ b/Misc/NEWS.d/3.14.0a5.rst @@ -944,7 +944,7 @@ It is always ``'freebsd'``, instead of ``'freebsd13'`` or ``'freebsd14'``. .. date: 2025-01-28-06-23-59 .. gh-issue: 129345 .. nonce: uOjkML -.. section: Core and Builtins +.. section: Library Fix null pointer dereference in :func:`syslog.openlog` when an audit hook raises an exception. @@ -1197,7 +1197,7 @@ generator. Patch by Mikhail Efimov. .. date: 2024-11-03-06-05-16 .. gh-issue: 126349 .. nonce: 7YwWsI -.. section: Core and Builtins +.. section: Library Add :func:`turtle.fill`, :func:`turtle.poly` and :func:`turtle.no_animation` context managers. Patch by Marie Roald and Yngve Mardal Moe. @@ -1218,7 +1218,7 @@ Willmer. .. date: 2023-12-04-15-53-25 .. gh-issue: 112713 .. nonce: Zrhv77 -.. section: Core and Builtins +.. section: Library Added support for the ``Partitioned`` cookie flag in :mod:`http.cookies`. diff --git a/Misc/NEWS.d/3.14.0a6.rst b/Misc/NEWS.d/3.14.0a6.rst index d8840b6f283e76..5fb5306eeaedf5 100644 --- a/Misc/NEWS.d/3.14.0a6.rst +++ b/Misc/NEWS.d/3.14.0a6.rst @@ -621,7 +621,7 @@ Andrew Svetlov. .. nonce: jQ0CvW .. section: Library -Update the deprecation warning of :meth:`importlib.abc.Loader.load_module`. +Update the deprecation warning of ``importlib.abc.Loader.load_module``. .. @@ -758,7 +758,7 @@ Patch by Semyon Moroz. .. nonce: wDLTay .. section: Library -Delay deprecated :meth:`zipimport.zipimporter.load_module` removal time to +Delay deprecated :meth:`!zipimport.zipimporter.load_module` removal time to 3.15. Use :meth:`zipimport.zipimporter.exec_module` instead. .. @@ -1187,7 +1187,7 @@ Improve the experimental JIT's handling of returns to unknown callers. .. date: 2025-02-11-20-38-37 .. gh-issue: 129983 .. nonce: _1Fujo -.. section: Core and Builtins +.. section: Library Fix data race in compile_template in :file:`sre.c`. @@ -1335,7 +1335,7 @@ interpreter state. .. date: 2022-12-21-14-28-01 .. gh-issue: 100388 .. nonce: vne8ky -.. section: Core and Builtins +.. section: Library Fix the ``platform._sys_version()`` method when ``__DATE__`` is undefined at buildtime by changing default buildtime datetime string to the UNIX epoch. diff --git a/Misc/NEWS.d/3.14.0a7.rst b/Misc/NEWS.d/3.14.0a7.rst index 35b96d33da4175..752d8be8dc8f44 100644 --- a/Misc/NEWS.d/3.14.0a7.rst +++ b/Misc/NEWS.d/3.14.0a7.rst @@ -192,7 +192,7 @@ The :class:`ctypes.py_object` type now supports subscription, making it a .. nonce: cX4yTn .. section: Library -Add the :attr:`zipfile.ZipFile.data_offset` attribute, which stores the +Add the :attr:`!zipfile.ZipFile.data_offset` attribute, which stores the offset to the beginning of ZIP data in a file when available. When the :class:`zipfile.ZipFile` is opened in either mode ``'w'`` or ``'x'`` and the underlying file does not support ``tell()``, the value will be ``None`` @@ -671,7 +671,7 @@ Allow the JIT to remove an extra ``_TO_BOOL_BOOL`` instruction after .. nonce: dNh64H .. section: Core and Builtins -Fix crash when calling :meth:`!list.append` as an unbound method. +Fix crash when calling :meth:`list.append` as an unbound method. .. @@ -880,7 +880,7 @@ Fix an issue with thread identifiers being sign-extended on some platforms. .. date: 2025-02-15-14-36-32 .. gh-issue: 99108 .. nonce: u6CfmK -.. section: Core and Builtins +.. section: Library Add support for built-in implementation of HMAC (:rfc:`2104`) based on HACL*. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/3.14.0b1.rst b/Misc/NEWS.d/3.14.0b1.rst index 5d03d429f9ee14..cb86c95a672ed7 100644 --- a/Misc/NEWS.d/3.14.0b1.rst +++ b/Misc/NEWS.d/3.14.0b1.rst @@ -747,7 +747,7 @@ caches. .. nonce: APBFCw .. section: Library -Fixed the :exc:`SystemError` in :mod:`cProfile` when locating the actual C +Fixed the :exc:`SystemError` in :mod:`!cProfile` when locating the actual C function of a method raises an exception. .. @@ -1325,7 +1325,7 @@ Add new utilities of observing JIT compilation: .. date: 2025-04-30-13-09-20 .. gh-issue: 133194 .. nonce: 25_G5c -.. section: Core and Builtins +.. section: Library :func:`ast.parse` will no longer parse new :pep:`758` syntax with older *feature_version* passed. @@ -1498,7 +1498,7 @@ helpful fix suggestion for the typo. Contributed by Pablo Galindo Salgado. .. date: 2025-04-19-18-07-34 .. gh-issue: 132737 .. nonce: 9mW1il -.. section: Core and Builtins +.. section: Library Support profiling code that requires ``__main__``, such as :mod:`pickle`. @@ -1881,8 +1881,8 @@ Improve error message when :exc:`TypeError` occurs during .. nonce: BS3uVt .. section: Core and Builtins -String arguments passed to "-c" are now automatically dedented as if by -:func:`textwrap.dedent`. This allows "python -c" invocations to be indented +String arguments passed to "-c" are now automatically dedented. +This allows "python -c" invocations to be indented in shell scripts without causing indentation errors. (Patch by Jon Crall and Steven Sun) @@ -1891,7 +1891,7 @@ Steven Sun) .. date: 2022-12-29-19-10-36 .. gh-issue: 89562 .. nonce: g8m8RC -.. section: Core and Builtins +.. section: Library Remove ``hostflags`` member from ``PySSLContext`` struct. diff --git a/Misc/NEWS.d/3.15.0a1.rst b/Misc/NEWS.d/3.15.0a1.rst new file mode 100644 index 00000000000000..c7e3a872fe7285 --- /dev/null +++ b/Misc/NEWS.d/3.15.0a1.rst @@ -0,0 +1,6438 @@ +.. date: 2025-10-14-00-17-48 +.. gh-issue: 115119 +.. nonce: 470I1N +.. release date: 2025-10-14 +.. section: macOS + +Update macOS installer to use libmpdecimal 4.0.1. + +.. + +.. date: 2025-10-14-00-08-16 +.. gh-issue: 124111 +.. nonce: 7-j-DQ +.. section: macOS + +Update macOS installer to use Tcl/Tk 9.0.2. + +.. + +.. date: 2025-10-13-23-46-12 +.. gh-issue: 132339 +.. nonce: kAp603 +.. section: macOS + +Update macOS installer version of OpenSSL to 3.5.4. + +.. + +.. date: 2025-08-06-06-29-12 +.. gh-issue: 137450 +.. nonce: JZypb7 +.. section: macOS + +macOS installer shell path management improvements: separate the installer +``Shell profile updater`` postinstall script from the ``Update Shell +Profile.command`` to enable more robust error handling. + +.. + +.. date: 2025-07-27-02-17-40 +.. gh-issue: 137134 +.. nonce: pjgITs +.. section: macOS + +Update macOS installer to ship with SQLite version 3.50.4. + +.. + +.. date: 2025-10-08-22-54-38 +.. gh-issue: 139810 +.. nonce: LAaemi +.. section: Windows + +Installing with ``py install 3[.x]-dev`` will now select final versions as +well as prereleases. + +.. + +.. date: 2025-10-04-12-18-45 +.. gh-issue: 139573 +.. nonce: EO9kVB +.. section: Windows + +Updated bundled version of OpenSSL to 3.0.18. + +.. + +.. date: 2025-09-15-15-34-29 +.. gh-issue: 138896 +.. nonce: lkiF_7 +.. section: Windows + +Fix error installing C runtime on non-updated Windows machines + +.. + +.. date: 2025-09-03-01-07-44 +.. gh-issue: 138314 +.. nonce: IeWQ2i +.. section: Windows + +Add :func:`winreg.DeleteTree`. + +.. + +.. date: 2025-07-27-14-25-11 +.. gh-issue: 137136 +.. nonce: xNthFT +.. section: Windows + +Suppress build warnings when build on Windows with +``--experimental-jit-interpreter``. + +.. + +.. date: 2025-07-27-02-16-53 +.. gh-issue: 137134 +.. nonce: W0WpDF +.. section: Windows + +Update Windows installer to ship with SQLite 3.50.4. + +.. + +.. date: 2025-06-03-18-26-54 +.. gh-issue: 135099 +.. nonce: Q9usKm +.. section: Windows + +Fix a crash that could occur on Windows when a background thread waits on a +:c:type:`PyMutex` while the main thread is shutting down the interpreter. + +.. + +.. date: 2025-05-20-21-43-20 +.. gh-issue: 130727 +.. nonce: -69t4D +.. section: Windows + +Fix a race in internal calls into WMI that can result in an "invalid handle" +exception under high load. Patch by Chris Eibl. + +.. + +.. date: 2025-05-19-03-02-04 +.. gh-issue: 76023 +.. nonce: vHOf6M +.. section: Windows + +Make :func:`os.path.realpath` ignore Windows error 1005 when in non-strict +mode. + +.. + +.. date: 2025-05-13-13-25-27 +.. gh-issue: 133779 +.. nonce: -YcTBz +.. section: Windows + +Reverts the change to generate different :file:`pyconfig.h` files based on +compiler settings, as it was frequently causing extension builds to break. +In particular, the ``Py_GIL_DISABLED`` preprocessor variable must now always +be defined explicitly when compiling for the experimental free-threaded +runtime. The :func:`sysconfig.get_config_var` function can be used to +determine whether the current runtime was compiled with that flag or not. + +.. + +.. date: 2025-05-08-19-07-26 +.. gh-issue: 133626 +.. nonce: yFTKYK +.. section: Windows + +Ensures packages are not accidentally bundled into the traditional +installer. + +.. + +.. date: 2025-05-07-13-04-22 +.. gh-issue: 133580 +.. nonce: jBMujJ +.. section: Windows + +Fix :func:`sys.getwindowsversion` failing without setting an exception when +called on some WinAPI partitions. + +.. + +.. date: 2025-05-07-11-45-30 +.. gh-issue: 133572 +.. nonce: Xc2zxH +.. section: Windows + +Avoid LsaNtStatus to WinError conversion on unsupported WinAPI partitions. + +.. + +.. date: 2025-05-07-11-25-29 +.. gh-issue: 133568 +.. nonce: oYV0d8 +.. section: Windows + +Fix compile error when using a WinAPI partition that doesn't support the RPC +runtime library. + +.. + +.. date: 2025-05-07-09-02-19 +.. gh-issue: 133562 +.. nonce: lqqNW1 +.. section: Windows + +Disable handling of security descriptors by :func:`os.mkdir` with mode +``0o700`` on WinAPI partitions that do not support it. This only affects +custom builds for specialized targets. + +.. + +.. date: 2025-05-07-08-19-15 +.. gh-issue: 133537 +.. nonce: yzf963 +.. section: Windows + +Avoid using console I/O in WinAPI partitions that don’t support it + +.. + +.. date: 2025-03-31-15-37-57 +.. gh-issue: 131942 +.. nonce: jip_aL +.. section: Windows + +Use the Python-specific :c:macro:`Py_DEBUG` macro rather than +:c:macro:`!_DEBUG` in Windows-related C code. Patch by Xuehai Pan. + +.. + +.. date: 2025-09-25-10-31-02 +.. gh-issue: 139330 +.. nonce: 5WWkY0 +.. section: Tools/Demos + +SBOM generation tool didn't cross-check the version and checksum values +against the ``Modules/expat/refresh.sh`` script, leading to the values +becoming out-of-date during routine updates. + +.. + +.. date: 2025-08-28-06-22-26 +.. gh-issue: 132006 +.. nonce: eZQmc6 +.. section: Tools/Demos + +XCframeworks now include privacy manifests to satisfy Apple App Store +submission requirements. + +.. + +.. date: 2025-08-27-11-14-53 +.. gh-issue: 138171 +.. nonce: Suz8ob +.. section: Tools/Demos + +A script for building an iOS XCframework was added. As part of this change, +the top level ``iOS`` folder has been moved to be a subdirectory of the +``Apple`` folder. + +.. + +.. date: 2025-08-21-14-04-50 +.. gh-issue: 137873 +.. nonce: qxffLt +.. section: Tools/Demos + +The iOS test runner has been simplified, resolving some issues that have +been observed using the runner in GitHub Actions and Azure Pipelines test +environments. + +.. + +.. date: 2025-08-06-11-54-55 +.. gh-issue: 137484 +.. nonce: 8iFAQs +.. section: Tools/Demos + +Have ``Tools/wasm/wasi`` put the build Python into a directory named after +the build triple instead of "build". + +.. + +.. date: 2025-08-01-13-27-43 +.. gh-issue: 137025 +.. nonce: ubuhQC +.. section: Tools/Demos + +The ``wasm_build.py`` script has been removed. ``Tools/wasm/emscripten`` +and ``Tools/wasm/wasi`` should be used instead, as described in the `Dev +Guide <https://devguide.python.org/contrib/workflows/compile/>`__. + +.. + +.. date: 2025-07-30-11-15-47 +.. gh-issue: 137248 +.. nonce: 8IxwY3 +.. section: Tools/Demos + +Add a ``--logdir`` option to ``Tools/wasm/wasi`` for specifying where to +write log files. + +.. + +.. date: 2025-07-30-10-28-35 +.. gh-issue: 137243 +.. nonce: NkdUqH +.. section: Tools/Demos + +Have Tools/wasm/wasi detect a WASI SDK install in /opt when it was directly +extracted from a release tarball. + +.. + +.. date: 2025-07-05-15-10-42 +.. gh-issue: 136251 +.. nonce: GRM6o8 +.. section: Tools/Demos + +Fixes and usability improvements for ``Tools/wasm/emscripten/web_example`` + +.. + +.. date: 2025-06-26-15-58-13 +.. gh-issue: 135968 +.. nonce: C4v_-W +.. section: Tools/Demos + +Stubs for ``strip`` are now provided as part of an iOS install. + +.. + +.. date: 2025-06-11-12-14-06 +.. gh-issue: 135379 +.. nonce: 25ttXq +.. section: Tools/Demos + +The cases generator no longer accepts type annotations on stack items. +Conversions to non-default types are now done explicitly in bytecodes.c and +optimizer_bytecodes.c. This will simplify code generation for top-of-stack +caching and other future features. + +.. + +.. date: 2025-05-19-14-57-46 +.. gh-issue: 134215 +.. nonce: sbdDK6 +.. section: Tools/Demos + +:term:`REPL` import autocomplete only suggests private modules when +explicitly specified. + +.. + +.. date: 2025-09-22-15-40-09 +.. gh-issue: 139208 +.. nonce: Tc13dl +.. section: Tests + +Fix regrtest ``--fast-ci --verbose``: don't ignore the ``--verbose`` option +anymore. Patch by Victor Stinner. + +.. + +.. date: 2025-09-21-16-00-30 +.. gh-issue: 138313 +.. nonce: lBx2en +.. section: Tests + +Restore skipped test and add janky workaround to prevent select buildbots +from failing with a ResourceWarning. + +.. + +.. date: 2025-06-26-15-15-35 +.. gh-issue: 135966 +.. nonce: EBpF8Y +.. section: Tests + +The iOS testbed now handles the ``app_packages`` folder as a site directory. + +.. + +.. date: 2025-06-19-15-29-38 +.. gh-issue: 135494 +.. nonce: FVl9a0 +.. section: Tests + +Fix regrtest to support excluding tests from ``--pgo`` tests. Patch by +Victor Stinner. + +.. + +.. date: 2025-06-17-08-48-08 +.. gh-issue: 132815 +.. nonce: CY1Esu +.. section: Tests + +Fix test__opcode: add ``JUMP_BACKWARD`` to specialization stats. + +.. + +.. date: 2025-06-14-13-20-17 +.. gh-issue: 135489 +.. nonce: Uh0yVO +.. section: Tests + +Show verbose output for failing tests during PGO profiling step with +--enable-optimizations. + +.. + +.. date: 2025-06-11-16-52-49 +.. gh-issue: 135401 +.. nonce: ccMXmL +.. section: Tests + +Add a new GitHub CI job to test the :mod:`ssl` module with `AWS-LC +<https://github.com/aws/aws-lc>`_ as the backing cryptography and TLS +library. + +.. + +.. date: 2025-06-04-13-07-44 +.. gh-issue: 135120 +.. nonce: NapnZT +.. section: Tests + +Add :func:`!test.support.subTests`. + +.. + +.. date: 2025-05-23-09-19-52 +.. gh-issue: 134567 +.. nonce: hwEIMb +.. section: Tests + +Expose log formatter to users in TestCase.assertLogs. +:func:`unittest.TestCase.assertLogs` will now optionally accept a formatter +that will be used to format the strings in output if provided. + +.. + +.. date: 2025-05-09-14-54-48 +.. gh-issue: 133744 +.. nonce: LCquu0 +.. section: Tests + +Fix multiprocessing interrupt test. Add an event to synchronize the parent +process with the child process: wait until the child process starts +sleeping. Patch by Victor Stinner. + +.. + +.. date: 2025-05-09-04-11-06 +.. gh-issue: 133682 +.. nonce: -_lwo3 +.. section: Tests + +Fixed test case ``test.test_annotationlib.TestStringFormat.test_displays`` +which ensures proper handling of complex data structures (lists, sets, +dictionaries, and tuples) in string annotations. + +.. + +.. date: 2025-05-08-15-06-01 +.. gh-issue: 133639 +.. nonce: 50-kbV +.. section: Tests + +Fix ``TestPyReplAutoindent.test_auto_indent_default()`` doesn't run +``input_code``. + +.. + +.. date: 2025-10-07-19-31-34 +.. gh-issue: 139700 +.. nonce: vNHU1O +.. section: Security + +Check consistency of the zip64 end of central directory record. Support +records with "zip64 extensible data" if there are no bytes prepended to the +ZIP file. + +.. + +.. date: 2025-09-29-00-01-28 +.. gh-issue: 139400 +.. nonce: X2T-jO +.. section: Security + +:mod:`xml.parsers.expat`: Make sure that parent Expat parsers are only +garbage-collected once they are no longer referenced by subparsers created +by :meth:`~xml.parsers.expat.xmlparser.ExternalEntityParserCreate`. Patch by +Sebastian Pipping. + +.. + +.. date: 2025-09-24-13-39-56 +.. gh-issue: 139283 +.. nonce: jODz_q +.. section: Security + +:mod:`sqlite3`: correctly handle maximum number of rows to fetch in +:meth:`Cursor.fetchmany <sqlite3.Cursor.fetchmany>` and reject negative +values for :attr:`Cursor.arraysize <sqlite3.Cursor.arraysize>`. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-06-27-21-23-19 +.. gh-issue: 136053 +.. nonce: QZxcee +.. section: Security + +:mod:`marshal`: fix a possible crash when deserializing :class:`slice` +objects. + +.. + +.. date: 2025-06-25-14-13-39 +.. gh-issue: 135661 +.. nonce: idjQ0B +.. section: Security + +Fix parsing start and end tags in :class:`html.parser.HTMLParser` according +to the HTML5 standard. + +* Whitespaces no longer accepted between ``</`` and the tag name. + E.g. ``</ script>`` does not end the script section. + +* Vertical tabulation (``\v``) and non-ASCII whitespaces no longer recognized + as whitespaces. The only whitespaces are ``\t\n\r\f`` and space. + +* Null character (U+0000) no longer ends the tag name. + +* Attributes and slashes after the tag name in end tags are now ignored, + instead of terminating after the first ``>`` in quoted attribute value. + E.g. ``</script/foo=">"/>``. + +* Multiple slashes and whitespaces between the last attribute and closing ``>`` + are now ignored in both start and end tags. E.g. ``<a foo=bar/ //>``. + +* Multiple ``=`` between attribute name and value are no longer collapsed. + E.g. ``<a foo==bar>`` produces attribute "foo" with value "=bar". + +.. + +.. date: 2025-06-18-13-34-55 +.. gh-issue: 135661 +.. nonce: NZlpWf +.. section: Security + +Fix CDATA section parsing in :class:`html.parser.HTMLParser` according to +the HTML5 standard: ``] ]>`` and ``]] >`` no longer end the CDATA section. +Add private method ``_set_support_cdata()`` which can be used to specify how +to parse ``<[CDATA[`` --- as a CDATA section in foreign content (SVG or +MathML) or as a bogus comment in the HTML namespace. + +.. + +.. date: 2025-06-18-13-28-08 +.. gh-issue: 102555 +.. nonce: nADrzJ +.. section: Security + +Fix comment parsing in :class:`html.parser.HTMLParser` according to the +HTML5 standard. ``--!>`` now ends the comment. ``-- >`` no longer ends the +comment. Support abnormally ended empty comments ``<-->`` and ``<--->``. + +.. + +.. date: 2025-06-13-15-55-22 +.. gh-issue: 135462 +.. nonce: KBeJpc +.. section: Security + +Fix quadratic complexity in processing specially crafted input in +:class:`html.parser.HTMLParser`. End-of-file errors are now handled +according to the HTML5 specs -- comments and declarations are automatically +closed, tags are ignored. + +.. + +.. date: 2025-06-09-20-38-25 +.. gh-issue: 118350 +.. nonce: KgWCcP +.. section: Security + +Fix support of escapable raw text mode (elements "textarea" and "title") in +:class:`html.parser.HTMLParser`. + +.. + +.. date: 2025-06-02-11-32-23 +.. gh-issue: 135034 +.. nonce: RLGjbp +.. section: Security + +Fixes multiple issues that allowed ``tarfile`` extraction filters +(``filter="data"`` and ``filter="tar"``) to be bypassed using crafted +symlinks and hard links. + +Addresses :cve:`2024-12718`, :cve:`2025-4138`, :cve:`2025-4330`, and +:cve:`2025-4517`. + +.. + +.. date: 2025-05-09-20-22-54 +.. gh-issue: 133767 +.. nonce: kN2i3Q +.. section: Security + +Fix use-after-free in the "unicode-escape" decoder with a non-"strict" error +handler. + +.. + +.. date: 2025-05-07-22-49-27 +.. gh-issue: 133623 +.. nonce: fgWkBm +.. section: Security + +Indicate through :data:`ssl.HAS_PSK_TLS13` whether the :mod:`ssl` module +supports "External PSKs" in TLSv1.3, as described in RFC 9258. Patch by Will +Childs-Klein. + +.. + +.. date: 2025-01-14-11-19-07 +.. gh-issue: 128840 +.. nonce: M1doZW +.. section: Security + +Short-circuit the processing of long IPv6 addresses early in +:mod:`ipaddress` to prevent excessive memory consumption and a minor +denial-of-service. + +.. + +.. date: 2025-10-11-20-03-13 +.. gh-issue: 139482 +.. nonce: du2Stg +.. section: Library + +Optimize :data:`os.environ.clear() <os.environ>` by calling +:manpage:`clearenv(3)` when this function is available. Patch by Victor +Stinner. + +.. + +.. date: 2025-10-11-17-41-26 +.. gh-issue: 139958 +.. nonce: AnCakj +.. section: Library + +The ``application/toml`` mime type is now supported by :mod:`mimetypes`. +Patch by Gil Forcada. + +.. + +.. date: 2025-10-11-14-37-42 +.. gh-issue: 139823 +.. nonce: uGF4oh +.. section: Library + +:mod:`ensurepip` now fails with a nicer error message when the :mod:`zlib` +module is not available. + +.. + +.. date: 2025-10-11-10-02-56 +.. gh-issue: 139905 +.. nonce: UyJIR_ +.. section: Library + +Add suggestion to error message for :class:`typing.Generic` subclasses when +``cls.__parameters__`` is missing due to a parent class failing to call +:meth:`super().__init_subclass__() <object.__init_subclass__>` in its +``__init_subclass__``. + +.. + +.. date: 2025-10-10-11-22-50 +.. gh-issue: 139894 +.. nonce: ECAXqj +.. section: Library + +Fix incorrect sharing of current task with the child process while forking +in :mod:`asyncio`. Patch by Kumar Aditya. + +.. + +.. date: 2025-10-09-21-37-20 +.. gh-issue: 139845 +.. nonce: dzx5UP +.. section: Library + +Fix to not print KeyboardInterrupt twice in default asyncio REPL. + +.. + +.. date: 2025-10-09-13-48-28 +.. gh-issue: 139783 +.. nonce: __NUgo +.. section: Library + +Fix :func:`inspect.getsourcelines` for the case when a decorator is followed +by a comment or an empty line. + +.. + +.. date: 2025-10-09-03-06-19 +.. gh-issue: 139809 +.. nonce: lzHJNu +.. section: Library + +Prevent premature colorization of subparser ``prog`` in +:meth:`argparse.ArgumentParser.add_subparsers` to respect color environment +variable changes after parser creation. + +.. + +.. date: 2025-10-08-00-06-30 +.. gh-issue: 139736 +.. nonce: baPeBd +.. section: Library + +Fix excessive indentation in the default :mod:`argparse` +:class:`!HelpFormatter`. Patch by Alexander Edland. + +.. + +.. date: 2025-10-02-17-40-10 +.. gh-issue: 70765 +.. nonce: zVlLZn +.. section: Library + +:mod:`http.server`: fix default handling of HTTP/0.9 requests in +:class:`~http.server.BaseHTTPRequestHandler`. Previously, +:meth:`!BaseHTTPRequestHandler.parse_request` incorrectly waited for headers +in the request although those are not supported in HTTP/0.9. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-10-02-15-45-08 +.. gh-issue: 139322 +.. nonce: rouPGj +.. section: Library + +Fix :func:`os.getlogin` error handling: fix the error number. Patch by +Victor Stinner. + +.. + +.. date: 2025-10-01-20-30-03 +.. gh-issue: 135953 +.. nonce: NAofJl +.. section: Library + +Add a Gecko format output to the tachyon profiler via ``--gecko``. + +.. + +.. date: 2025-09-29-14-15-20 +.. gh-issue: 139184 +.. nonce: dNl9O4 +.. section: Library + +:func:`os.forkpty` does now make the returned file descriptor +non-inheritable. + +.. + +.. date: 2025-09-28-16-34-11 +.. gh-issue: 139391 +.. nonce: nRFnmx +.. section: Library + +Fix an issue when, on non-Windows platforms, it was not possible to +gracefully exit a ``python -m asyncio`` process suspended by Ctrl+Z and +later resumed by :manpage:`fg` other than with :manpage:`kill`. + +.. + +.. date: 2025-09-27-08-26-31 +.. gh-issue: 139374 +.. nonce: hfh-dl +.. section: Library + +:mod:`timeit`: Add color to error tracebacks. + +.. + +.. date: 2025-09-26-18-04-28 +.. gh-issue: 90949 +.. nonce: YHjSzX +.. section: Library + +Add +:meth:`~xml.parsers.expat.xmlparser.SetBillionLaughsAttackProtectionActivationThreshold` +and +:meth:`~xml.parsers.expat.xmlparser.SetBillionLaughsAttackProtectionMaximumAmplification` +to :ref:`xmlparser <xmlparser-objects>` objects to tune protections against +`billion laughs <https://en.wikipedia.org/wiki/Billion_laughs_attack>`_ +attacks. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-25-07-33-43 +.. gh-issue: 139312 +.. nonce: ygE8AC +.. section: Library + +Upgrade bundled libexpat to 2.7.3 + +.. + +.. date: 2025-09-24-14-17-34 +.. gh-issue: 139289 +.. nonce: Vmk25k +.. section: Library + +Do a real lazy-import on :mod:`rlcompleter` in :mod:`pdb` and restore the +existing completer after importing :mod:`rlcompleter`. + +.. + +.. date: 2025-09-22-14-40-11 +.. gh-issue: 90949 +.. nonce: UM35nb +.. section: Library + +Add :meth:`~xml.parsers.expat.xmlparser.SetAllocTrackerActivationThreshold` +and :meth:`~xml.parsers.expat.xmlparser.SetAllocTrackerMaximumAmplification` +to :ref:`xmlparser <xmlparser-objects>` objects to tune protections against +disproportional amounts of dynamic memory usage from within an Expat parser. +Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-22-11-30-45 +.. gh-issue: 67795 +.. nonce: fROoZt +.. section: Library + +Functions that take timestamp or timeout arguments now accept any real +numbers (such as :class:`~decimal.Decimal` and +:class:`~fractions.Fraction`), not only integers or floats, although this +does not improve precision. + +.. + +.. date: 2025-09-22-11-19-05 +.. gh-issue: 95953 +.. nonce: 7oLoag +.. section: Library + +A CSS class, ``diff_changed``, was added to the changed lines in the +``make_table`` output of :class:`difflib.HtmlDiff`. Patch by Katie Gardner. + +.. + +.. date: 2025-09-21-15-58-57 +.. gh-issue: 139210 +.. nonce: HGbMvz +.. section: Library + +Fix use-after-free when reporting unknown event in +:func:`xml.etree.ElementTree.iterparse`. Patch by Ken Jin. + +.. + +.. date: 2025-09-20-17-50-31 +.. gh-issue: 138860 +.. nonce: Y9JXap +.. section: Library + +Lazy import :mod:`rlcompleter` in :mod:`pdb` to avoid deadlock in +subprocess. + +.. + +.. date: 2025-09-19-09-36-42 +.. gh-issue: 112729 +.. nonce: mmty0_ +.. section: Library + +Fix crash when calling :func:`concurrent.interpreters.create` when the +process is out of memory. + +.. + +.. date: 2025-09-19-07-41-52 +.. gh-issue: 126016 +.. nonce: Uz9W6h +.. section: Library + +Fix an assertion failure when sending :exc:`KeyboardInterrupt` to a Python +process running a subinterpreter in a separate thread. + +.. + +.. date: 2025-09-18-14-21-57 +.. gh-issue: 118803 +.. nonce: 2JPbto +.. section: Library + +:class:`collections.abc.ByteString` has been removed from +``collections.abc.__all__``, and :class:`typing.ByteString` has been removed +from ``typing.__all__``. The former has been deprecated since Python 3.12, +and the latter has been deprecated since Python 3.9. Both classes are +scheduled for removal in Python 3.17. + +Additionally, the following statements now cause ``DeprecationWarning``\ s +to be emitted at runtime: ``from collections.abc import ByteString``, ``from +typing import ByteString``, ``import collections.abc; +collections.abc.ByteString`` and ``import typing; typing.ByteString``. Both +classes already caused ``DeprecationWarning``\ s to be emitted if they were +subclassed or used as the second argument to ``isinstance()`` or +``issubclass()``, but they did not previously lead to +``DeprecationWarning``\ s if they were merely imported or accessed from +their respective modules. + +.. + +.. date: 2025-09-18-05-32-18 +.. gh-issue: 135729 +.. nonce: 8AmMza +.. section: Library + +Fix unraisable exception during finalization when using +:mod:`concurrent.interpreters` in the REPL. + +.. + +.. date: 2025-09-17-21-54-53 +.. gh-issue: 139076 +.. nonce: 2eX9lG +.. section: Library + +Fix a bug in the :mod:`pydoc` module that was hiding functions in a Python +module if they were implemented in an extension module and the module did +not have ``__all__``. + +.. + +.. date: 2025-09-17-21-52-30 +.. gh-issue: 139090 +.. nonce: W7vbhF +.. section: Library + +Add :data:`os.RWF_DONTCACHE` constant for Linux 6.14+. + +.. + +.. date: 2025-09-17-19-08-34 +.. gh-issue: 139065 +.. nonce: Hu8fM5 +.. section: Library + +Fix trailing space before a wrapped long word if the line length is exactly +*width* in :mod:`textwrap`. + +.. + +.. date: 2025-09-17-12-07-21 +.. gh-issue: 139001 +.. nonce: O6tseN +.. section: Library + +Fix race condition in :class:`pathlib.Path` on the internal ``_raw_paths`` +field. + +.. + +.. date: 2025-09-17-08-32-43 +.. gh-issue: 138813 +.. nonce: LHkHjX +.. section: Library + +:class:`!multiprocessing.BaseProcess` defaults ``kwargs`` to ``None`` +instead of a shared dictionary. + +.. + +.. date: 2025-09-16-19-05-29 +.. gh-issue: 138998 +.. nonce: URl0Y_ +.. section: Library + +Update bundled libexpat to 2.7.2 + +.. + +.. date: 2025-09-16-16-46-58 +.. gh-issue: 138993 +.. nonce: -8s8_T +.. section: Library + +Dedent :data:`credits` text. + +.. + +.. date: 2025-09-16-15-56-29 +.. gh-issue: 118803 +.. nonce: aOPtmL +.. section: Library + +Add back :class:`collections.abc.ByteString` and :class:`typing.ByteString`. +Both had been removed in prior alpha, beta and release candidates for Python +3.14, but their removal has now been postponed to Python 3.17. + +.. + +.. date: 2025-09-15-19-29-12 +.. gh-issue: 130567 +.. nonce: shDEnT +.. section: Library + +Fix possible crash in :func:`locale.strxfrm` due to a platform bug on macOS. + +.. + +.. date: 2025-09-15-13-09-19 +.. gh-issue: 137226 +.. nonce: HH3_ik +.. section: Library + +Fix :func:`typing.get_type_hints` calls on generic :class:`typing.TypedDict` +classes defined with string annotations. + +.. + +.. date: 2025-09-15-08-57-39 +.. gh-issue: 138899 +.. nonce: Uh6fvY +.. section: Library + +Executing ``quit`` command in :mod:`pdb` will raise :exc:`bdb.BdbQuit` when +:mod:`pdb` is started from an asyncio console using :func:`breakpoint` or +:func:`pdb.set_trace`. + +.. + +.. date: 2025-09-12-01-01-05 +.. gh-issue: 138804 +.. nonce: 46ZukT +.. section: Library + +Raise :exc:`TypeError` instead of :exc:`AttributeError` when an argument of +incorrect type is passed to :func:`shlex.quote`. This restores the behavior +of the function prior to 3.14. + +.. + +.. date: 2025-09-11-11-09-28 +.. gh-issue: 138779 +.. nonce: TNZnLr +.. section: Library + +Support device numbers larger than ``2**63-1`` for the +:attr:`~os.stat_result.st_rdev` field of the :class:`os.stat_result` +structure. + +.. + +.. date: 2025-09-10-13-32-25 +.. gh-issue: 138682 +.. nonce: iExqx1 +.. section: Library + +Added symmetric difference support to :class:`collections.Counter` objects. + +.. + +.. date: 2025-09-10-10-11-59 +.. gh-issue: 138712 +.. nonce: avrPG5 +.. section: Library + +Add :const:`os.NODEV`. + +.. + +.. date: 2025-09-10-10-02-59 +.. gh-issue: 128636 +.. nonce: ldRKGZ +.. section: Library + +Fix crash in PyREPL when os.environ is overwritten with an invalid value for +mac + +.. + +.. date: 2025-09-09-17-57-49 +.. gh-issue: 138720 +.. nonce: hAtsm- +.. section: Library + +Fix an issue where :class:`io.BufferedWriter` and :class:`io.BufferedRandom` +had different definitions of "closed" for :meth:`~io.IOBase.close` and +:meth:`~io.IOBase.flush` which resulted in an exception when close called +flush but flush thought the file was already closed. + +.. + +.. date: 2025-09-09-10-48-26 +.. gh-issue: 138706 +.. nonce: xB--LX +.. section: Library + +Update :mod:`unicodedata` database to Unicode 17.0.0. + +.. + +.. date: 2025-09-08-17-32-02 +.. gh-issue: 76007 +.. nonce: peEgcr +.. section: Library + +Deprecate ``__version__`` from a number of standard library modules. Patch +by Hugo van Kemenade. + +.. + +.. date: 2025-09-06-20-09-32 +.. gh-issue: 138535 +.. nonce: mlntEe +.. section: Library + +Speed up :func:`os.stat` for files with reasonable timestamps. Contributed +by Jeffrey Bosboom. + +.. + +.. date: 2025-09-06-14-56-40 +.. gh-issue: 116946 +.. nonce: GGIeyO +.. section: Library + +:mod:`curses.panel`: the type of :func:`curses.panel.new_panel` is now +immutable. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-06-14-54-01 +.. gh-issue: 116946 +.. nonce: hzQEWI +.. section: Library + +:mod:`zlib`: the types of :func:`zlib.compressobj` and +:func:`zlib.decompressobj` are now immutable. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-06-14-53-19 +.. gh-issue: 116946 +.. nonce: c-npxd +.. section: Library + +:mod:`os`: the :class:`os.DirEntry` type and the type of :func:`os.scandir` +are now immutable. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-06-14-47-23 +.. gh-issue: 116946 +.. nonce: hj_u1t +.. section: Library + +:mod:`tkinter`: the types :class:`!_tkinter.Tcl_Obj` (wrapper for Tcl +objects), :class:`!_tkinter.tktimertoken` (obtained by calling +``createtimerhandler()`` on a :attr:`Tk <tkinter.Tk.tk>` application) and +:class:`!_tkinter.tkapp` (the runtime type of Tk applications) are now +immutable. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-06-11-26-21 +.. gh-issue: 138514 +.. nonce: 66ltOb +.. section: Library + +Raise :exc:`ValueError` when a multi-character string is passed to the +*echo_char* parameter of :func:`getpass.getpass`. Patch by Benjamin Johnson. + +.. + +.. date: 2025-09-05-21-10-24 +.. gh-issue: 137706 +.. nonce: 0EztiJ +.. section: Library + +Fix the partial evaluation of annotations that use ``typing.Annotated[T, +x]`` where ``T`` is a forward reference. + +.. + +.. date: 2025-09-05-15-35-59 +.. gh-issue: 88375 +.. nonce: dC491a +.. section: Library + +Fix normalization of the ``robots.txt`` rules and URLs in the +:mod:`urllib.robotparser` module. No longer ignore trailing ``?``. +Distinguish raw special characters ``?``, ``=`` and ``&`` from the +percent-encoded ones. + +.. + +.. date: 2025-09-05-07-50-18 +.. gh-issue: 138515 +.. nonce: E3M-pu +.. section: Library + +:mod:`email` is added to Emscripten build. + +.. + +.. date: 2025-09-05-05-53-43 +.. gh-issue: 99948 +.. nonce: KMSlG6 +.. section: Library + +:func:`ctypes.util.find_library` now works in Emscripten build. + +.. + +.. date: 2025-09-04-15-18-11 +.. gh-issue: 111788 +.. nonce: tuTEM5 +.. section: Library + +Fix parsing errors in the :mod:`urllib.robotparser` module. Don't fail +trying to parse weird paths. Don't fail trying to decode non-UTF-8 +``robots.txt`` files. + +.. + +.. date: 2025-09-03-15-20-10 +.. gh-issue: 138432 +.. nonce: RMc7UX +.. section: Library + +:meth:`zoneinfo.reset_tzpath` will now convert any :class:`os.PathLike` +objects it receives into strings before adding them to ``TZPATH``. It will +raise ``TypeError`` if anything other than a string is found after this +conversion. If given an :class:`os.PathLike` object that represents a +relative path, it will now raise ``ValueError`` instead of ``TypeError``, +and present a more informative error message. + +.. + +.. date: 2025-09-03-09-03-11 +.. gh-issue: 132657 +.. nonce: cbAIDh +.. section: Library + +Improve the scaling of :func:`copy.copy` and :func:`copy.deepcopy` in the +free-threading build. + +.. + +.. date: 2025-09-02-10-27-21 +.. gh-issue: 116946 +.. nonce: VxXNGD +.. section: Library + +The types of :func:`select.poll` and :func:`select.epoll` objects are now +immutable. Patch by Bénédikt Tran. + +.. + +.. date: 2025-09-02-10-23-09 +.. gh-issue: 116946 +.. nonce: U6RpwK +.. section: Library + +The :class:`!_random.Random` C type is now immutable. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-08-31-22-10-22 +.. gh-issue: 57911 +.. nonce: N_Ixtv +.. section: Library + +When extracting tar files on Windows, slashes in symlink targets will be +replaced by backslashes to prevent corrupted links. + +.. + +.. date: 2025-08-31-12-34-02 +.. gh-issue: 138205 +.. nonce: iHXb1z +.. section: Library + +Removed the :meth:`~mmap.mmap.resize` method on platforms that don't support +the underlying syscall, instead of raising a :exc:`SystemError`. + +.. + +.. date: 2025-08-31-09-06-49 +.. gh-issue: 138008 +.. nonce: heOvsU +.. section: Library + +Fix segmentation faults in the :mod:`ctypes` module due to invalid +:attr:`~ctypes._CFuncPtr.argtypes`. Patch by Dung Nguyen. + +.. + +.. date: 2025-08-30-17-58-04 +.. gh-issue: 138252 +.. nonce: CDiEby +.. section: Library + +:mod:`ssl`: :class:`~ssl.SSLContext` objects can now set client and server +TLS signature algorithms. If Python has been built with OpenSSL 3.5 or +later, :class:`~ssl.SSLSocket` objects can return the signature algorithms +selected on a connection. + +.. + +.. date: 2025-08-30-10-58-15 +.. gh-issue: 138253 +.. nonce: 9Ehj-N +.. section: Library + +Add the *block* parameter in the :meth:`!put` and :meth:`!get` methods of +the :mod:`concurrent.interpreters` queues for compatibility with the +:class:`queue.Queue` interface. + +.. + +.. date: 2025-08-30-10-04-28 +.. gh-issue: 60462 +.. nonce: yh_vDc +.. section: Library + +Fix :func:`locale.strxfrm` on Solaris (and possibly other platforms). + +.. + +.. date: 2025-08-29-12-56-55 +.. gh-issue: 138239 +.. nonce: uthZFI +.. section: Library + +The REPL now highlights :keyword:`type` as a soft keyword in :ref:`type +statements <type>`. + +.. + +.. date: 2025-08-29-12-05-33 +.. gh-issue: 78502 +.. nonce: VpIMxg +.. section: Library + +:class:`mmap.mmap` now has a *trackfd* parameter on Windows; if it is +``False``, the file handle corresponding to *fileno* will not be duplicated. + +.. + +.. date: 2025-08-28-13-20-09 +.. gh-issue: 138204 +.. nonce: 8oLOud +.. section: Library + +Forbid expansion of shared anonymous :mod:`memory maps <mmap>` on Linux, +which caused a bus error. + +.. + +.. date: 2025-08-27-17-05-36 +.. gh-issue: 138010 +.. nonce: ZZJmPL +.. section: Library + +Fix an issue where defining a class with a +:deco:`warnings.deprecated`-decorated base class may not invoke the correct +:meth:`~object.__init_subclass__` method in cases involving multiple +inheritance. Patch by Brian Schubert. + +.. + +.. date: 2025-08-25-22-38-03 +.. gh-issue: 134716 +.. nonce: kyYKeX +.. section: Library + +Add support of regular expressions in the :option:`-W` option and the +:envvar:`PYTHONWARNINGS` environment variable. + +.. + +.. date: 2025-08-25-18-06-04 +.. gh-issue: 138133 +.. nonce: Zh9rGo +.. section: Library + +Prevent infinite traceback loop when sending CTRL^C to Python through +``strace``. + +.. + +.. date: 2025-08-25-16-22-32 +.. gh-issue: 138122 +.. nonce: eMNDZ1 +.. section: Library + +Implement :pep:`799` -- A dedicated profiling package for organizing Python +profiling tools. Patch by Pablo Galindo. + +.. + +.. date: 2025-08-24-02-04-32 +.. gh-issue: 138092 +.. nonce: V4-wTO +.. section: Library + +Fixed a bug in :meth:`mmap.mmap.flush` where calling with only an offset +parameter would fail. + +.. + +.. date: 2025-08-22-12-48-14 +.. gh-issue: 138044 +.. nonce: lEQULC +.. section: Library + +Remove compatibility shim for deprecated parameter *package* in +:func:`importlib.resources.files`. Patch by Semyon Moroz. + +.. + +.. date: 2025-08-22-09-53-45 +.. gh-issue: 86819 +.. nonce: ECxvwx +.. section: Library + +:mod:`socket`: Add missing constants for ISO-TP sockets. + +.. + +.. date: 2025-08-19-00-12-57 +.. gh-issue: 137884 +.. nonce: 4faCA_ +.. section: Library + +Add :func:`threading.get_native_id` support for Illumos/Solaris. Patch by +Yüce Tekol. + +.. + +.. date: 2025-08-18-16-02-51 +.. gh-issue: 134869 +.. nonce: GnAjnU +.. section: Library + +Fix an issue where pressing Ctrl+C during tab completion in the REPL would +leave the autocompletion menu in a corrupted state. + +.. + +.. date: 2025-08-18-07-10-55 +.. gh-issue: 137840 +.. nonce: 9b7AnG +.. section: Library + +:class:`typing.TypedDict` now supports the ``closed`` and ``extra_items`` +keyword arguments (as described in :pep:`728`) to control whether additional +non-required keys are allowed and to specify their value type. + +.. + +.. date: 2025-08-17-10-22-31 +.. gh-issue: 132947 +.. nonce: XR4MJ8 +.. section: Library + +Applied changes to ``importlib.metadata`` from `importlib_metadata 8.7 +<https://importlib-metadata.readthedocs.io/en/latest/history.html#v8-7-0>`_, +including ``dist`` now disallowed for ``EntryPoints.select``; deferred +imports for faster import times; added support for metadata with newlines +(python/cpython#119650); and ``metadata()`` function now returns ``None`` +when a metadata directory is present but no metadata is present. + +.. + +.. date: 2025-08-16-18-11-41 +.. gh-issue: 90548 +.. nonce: q3aJUK +.. section: Library + +Fix ``musl`` detection for :func:`platform.libc_ver` on Alpine Linux if +compiled with --strip-all. + +.. + +.. date: 2025-08-16-16-04-15 +.. gh-issue: 137317 +.. nonce: Dl13B5 +.. section: Library + +:func:`inspect.signature` now correctly handles classes that use a +descriptor on a wrapped :meth:`!__init__` or :meth:`!__new__` method. +Contributed by Yongyu Yan. + +.. + +.. date: 2025-08-16-09-02-11 +.. gh-issue: 137754 +.. nonce: mCev1Y +.. section: Library + +Fix import of the :mod:`zoneinfo` module if the C implementation of the +:mod:`datetime` module is not available. + +.. + +.. date: 2025-08-14-10-27-07 +.. gh-issue: 125854 +.. nonce: vDzFcZ +.. section: Library + +Improve error messages for invalid category in :func:`warnings.warn`. + +.. + +.. date: 2025-08-14-00-00-12 +.. gh-issue: 137729 +.. nonce: i9NSKP +.. section: Library + +:func:`locale.setlocale` now supports language codes with ``@``-modifiers. +``@``-modifier are no longer silently removed in :func:`locale.getlocale`, +but included in the language code. + +.. + +.. date: 2025-08-13-10-50-22 +.. gh-issue: 73487 +.. nonce: DUHbBq +.. section: Library + +Speedup processing arguments (up to 1.5x) in the :mod:`decimal` module +methods, that now using :c:macro:`METH_FASTCALL` calling convention. Patch +by Sergey B Kirpichev. + +.. + +.. date: 2025-08-11-14-18-32 +.. gh-issue: 137634 +.. nonce: M7iBG6 +.. section: Library + +Calendar pages generated by the :class:`calendar.HTMLCalendar` class now +support dark mode and have been migrated to the HTML5 standard for improved +accessibility. + +.. + +.. date: 2025-08-11-05-05-08 +.. gh-issue: 137630 +.. nonce: 9lmqyc +.. section: Library + +The :mod:`!_interpreters` module now uses Argument Clinic to parse +arguments. Patch by Adam Turner. + +.. + +.. date: 2025-08-09-08-53-32 +.. gh-issue: 137583 +.. nonce: s6OZud +.. section: Library + +Fix a deadlock introduced in 3.13.6 when a call to :meth:`ssl.SSLSocket.recv +<socket.socket.recv>` was blocked in one thread, and then another method on +the object (such as :meth:`ssl.SSLSocket.send <socket.socket.send>`) was +subsequently called in another thread. + +.. + +.. date: 2025-08-08-21-20-14 +.. gh-issue: 92936 +.. nonce: rOgG1S +.. section: Library + +Update regex used by ``http.cookies.SimpleCookie`` to handle values +containing double quotes. + +.. + +.. date: 2025-08-08-15-00-38 +.. gh-issue: 137426 +.. nonce: lW-Rk2 +.. section: Library + +Remove the code deprecation of ``importlib.abc.ResourceLoader``. It is +documented as deprecated, but left for backwards compatibility with other +classes in ``importlib.abc``. + +.. + +.. date: 2025-08-07-17-18-57 +.. gh-issue: 137490 +.. nonce: s89ieZ +.. section: Library + +Handle :data:`~errno.ECANCELED` in the same way as :data:`~errno.EINTR` in +:func:`signal.sigwaitinfo` on NetBSD. + +.. + +.. date: 2025-08-07-15-07-44 +.. gh-issue: 137512 +.. nonce: j2or5h +.. section: Library + +Add new constants in the :mod:`resource` module: +:data:`~resource.RLIMIT_NTHR`, :data:`~resource.RLIMIT_UMTXP`, +:data:`~resource.RLIMIT_PIPEBUF`, :data:`~resource.RLIMIT_THREADS`, +:data:`~resource.RLIM_SAVED_CUR`, and :data:`~resource.RLIM_SAVED_MAX`. + +.. + +.. date: 2025-08-07-12-32-23 +.. gh-issue: 137044 +.. nonce: abNoIy +.. section: Library + +:data:`resource.RLIM_INFINITY` is now always a positive integer. On all +supported platforms, it is larger than any limited resource value, which +simplifies comparison of the resource values. Previously, it could be +negative, such as -1 or -3, depending on platform. + +.. + +.. date: 2025-08-06-23-16-42 +.. gh-issue: 137477 +.. nonce: bk6BDV +.. section: Library + +Fix :func:`!inspect.getblock`, :func:`inspect.getsourcelines` and +:func:`inspect.getsource` for generator expressions. + +.. + +.. date: 2025-08-06-16-54-22 +.. gh-issue: 137481 +.. nonce: eSTkK0 +.. section: Library + +Calendar uses the lengths of the locale's weekdays to decide if the width +requires abbreviation. + +.. + +.. date: 2025-08-06-16-13-47 +.. gh-issue: 137466 +.. nonce: Whv0-A +.. section: Library + +Remove undocumented :func:`!glob.glob0` and :func:`!glob.glob1` functions, +which have been deprecated since Python 3.13. Use :func:`glob.glob` and pass +a directory to its *root_dir* argument instead. + +.. + +.. date: 2025-08-03-13-16-39 +.. gh-issue: 137044 +.. nonce: 0hPVL_ +.. section: Library + +Return large limit values as positive integers instead of negative integers +in :func:`resource.getrlimit`. Accept large values and reject negative +values (except :data:`~resource.RLIM_INFINITY`) for limits in +:func:`resource.setrlimit`. + +.. + +.. date: 2025-08-03-00-36-57 +.. gh-issue: 115766 +.. nonce: nJCFkW +.. section: Library + +Fix :attr:`!ipaddress.IPv4Interface.is_unspecified`. + +.. + +.. date: 2025-08-01-23-52-49 +.. gh-issue: 75989 +.. nonce: 5aYXNJ +.. section: Library + +:func:`tarfile.TarFile.extractall` and :func:`tarfile.TarFile.extract` now +overwrite symlinks when extracting hardlinks. (Contributed by Alexander +Enrique Urieles Nieto in :gh:`75989`.) + +.. + +.. date: 2025-08-01-23-11-25 +.. gh-issue: 137017 +.. nonce: 0yGcNc +.. section: Library + +Fix :obj:`threading.Thread.is_alive` to remain ``True`` until the underlying +OS thread is fully cleaned up. This avoids false negatives in edge cases +involving thread monitoring or premature :obj:`threading.Thread.is_alive` +calls. + +.. + +.. date: 2025-08-01-15-07-59 +.. gh-issue: 137273 +.. nonce: 4V8Xmv +.. section: Library + +Fix debug assertion failure in :func:`locale.setlocale` on Windows. + +.. + +.. date: 2025-07-31-16-43-16 +.. gh-issue: 137191 +.. nonce: FIogE8 +.. section: Library + +Fix how type parameters are collected, when :class:`typing.Protocol` are +specified with explicit parameters. Now, :class:`typing.Generic` and +:class:`typing.Protocol` always dictate the parameter number and parameter +ordering of types. Previous behavior was a bug. + +.. + +.. date: 2025-07-31-10-31-56 +.. gh-issue: 137282 +.. nonce: GOCwIC +.. section: Library + +Fix tab completion and :func:`dir` on :mod:`concurrent.futures`. + +.. + +.. date: 2025-07-30-18-07-33 +.. gh-issue: 137257 +.. nonce: XBtzf2 +.. section: Library + +Bump the version of pip bundled in ensurepip to version 25.2 + +.. + +.. date: 2025-07-30-17-42-36 +.. gh-issue: 137239 +.. nonce: qSpj32 +.. section: Library + +:mod:`heapq`: Update :data:`!heapq.__all__` with ``*_max`` functions. + +.. + +.. date: 2025-07-30-11-12-22 +.. gh-issue: 124503 +.. nonce: d4hc7b +.. section: Library + +:func:`ast.literal_eval` is 10-20% faster for small inputs. + +.. + +.. date: 2025-07-29-21-18-31 +.. gh-issue: 137226 +.. nonce: B_4lpu +.. section: Library + +Fix behavior of :meth:`annotationlib.ForwardRef.evaluate` when the +*type_params* parameter is passed and the name of a type param is also +present in an enclosing scope. + +.. + +.. date: 2025-07-29-05-12-50 +.. gh-issue: 137197 +.. nonce: bMK3sO +.. section: Library + +:class:`~ssl.SSLContext` objects can now set TLS 1.3 cipher suites via +:meth:`~ssl.SSLContext.set_ciphersuites`. + +.. + +.. date: 2025-07-28-23-11-29 +.. gh-issue: 81325 +.. nonce: jMJFBe +.. section: Library + +:class:`tarfile.TarFile` now accepts a :term:`path-like <path-like object>` +when working on a tar archive. (Contributed by Alexander Enrique Urieles +Nieto in :gh:`81325`.) + +.. + +.. date: 2025-07-28-20-48-32 +.. gh-issue: 137185 +.. nonce: fgI7-B +.. section: Library + +Fix a potential async-signal-safety issue in :mod:`faulthandler` when +printing C stack traces. + +.. + +.. date: 2025-07-27-17-03-17 +.. gh-issue: 133951 +.. nonce: 7kwt78 +.. section: Library + +Remove lib64-lib symlink creation when creating new virtual environments in +:mod:`venv` module + +.. + +.. date: 2025-07-25-09-21-56 +.. gh-issue: 130522 +.. nonce: Crwq68 +.. section: Library + +Fix unraisable :exc:`TypeError` raised during :term:`interpreter shutdown` +in the :mod:`threading` module. + +.. + +.. date: 2025-07-24-00-38-07 +.. gh-issue: 137059 +.. nonce: fr64oW +.. section: Library + +Fix handling of file URLs with a Windows drive letter in the URL authority +by :func:`urllib.request.url2pathname`. This fixes a regression in earlier +pre-releases of Python 3.14. + +.. + +.. date: 2025-07-23-11-59-48 +.. gh-issue: 136980 +.. nonce: BIJzkB +.. section: Library + +Remove unused C tracing code in bdb for event type ``c_call``, ``c_return`` +and ``c_exception`` + +.. + +.. date: 2025-07-23-00-35-29 +.. gh-issue: 130577 +.. nonce: c7EITy +.. section: Library + +:mod:`tarfile` now validates archives to ensure member offsets are +non-negative. (Contributed by Alexander Enrique Urieles Nieto in +:gh:`130577`.) + +.. + +.. date: 2025-07-21-22-35-50 +.. gh-issue: 136170 +.. nonce: QUlc78 +.. section: Library + +Removed the unreleased ``zipfile.ZipFile.data_offset`` property added in +3.14.0a7 as it wasn't fully clear which behavior it should have in some +situations so the result was not always what a user might expect. + +.. + +.. date: 2025-07-21-20-00-42 +.. gh-issue: 121237 +.. nonce: DyxNqo +.. section: Library + +Support ``%:z`` directive for :meth:`datetime.datetime.strptime`, +:meth:`datetime.time.strptime` and :func:`time.strptime`. Patch by Lucas +Esposito and Semyon Moroz. + +.. + +.. date: 2025-07-21-16-13-20 +.. gh-issue: 136929 +.. nonce: obKZ2S +.. section: Library + +Ensure that hash functions guaranteed to be always *available* exist as +attributes of :mod:`hashlib` even if they will not work at runtime due to +missing backend implementations. For instance, ``hashlib.md5`` will no +longer raise :exc:`AttributeError` if OpenSSL is not available and Python +has been built without MD5 support. Patch by Bénédikt Tran. + +.. + +.. date: 2025-07-21-16-10-24 +.. gh-issue: 124621 +.. nonce: wyoWc1 +.. section: Library + +pyrepl now works in Emscripten. + +.. + +.. date: 2025-07-21-15-40-00 +.. gh-issue: 136914 +.. nonce: -GNG-d +.. section: Library + +Fix retrieval of :attr:`doctest.DocTest.lineno` for objects decorated with +:func:`functools.cache` or :class:`functools.cached_property`. + +.. + +.. date: 2025-07-21-11-56-47 +.. gh-issue: 136912 +.. nonce: zWosAL +.. section: Library + +:func:`hmac.digest` now properly handles large keys and messages by falling +back to the pure Python implementation when necessary. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-07-21-01-16-32 +.. gh-issue: 83424 +.. nonce: Y3tEV4 +.. section: Library + +Allows creating a :class:`ctypes.CDLL` without name when passing a handle as +an argument. + +.. + +.. date: 2025-07-20-16-56-55 +.. gh-issue: 135228 +.. nonce: n_XIao +.. section: Library + +When :mod:`dataclasses` replaces a class with a slotted dataclass, the +original class can now be garbage collected again. Earlier changes in Python +3.14 caused this class to always remain in existence together with the +replacement class synthesized by :mod:`dataclasses`. + +.. + +.. date: 2025-07-20-16-02-00 +.. gh-issue: 136874 +.. nonce: cLC3o1 +.. section: Library + +Discard URL query and fragment in :func:`urllib.request.url2pathname`. + +.. + +.. date: 2025-07-20-10-21-49 +.. gh-issue: 136787 +.. nonce: _0Rbp_ +.. section: Library + +:mod:`hashlib`: improve exception messages when a hash algorithm is not +recognized, blocked by the current security policy or incompatible with the +desired operation (for instance, using HMAC with SHAKE). Patch by Bénédikt +Tran. + +.. + +.. date: 2025-07-19-16-20-54 +.. gh-issue: 130645 +.. nonce: O-dYcN +.. section: Library + +Enable color help by default in :mod:`argparse`. + +.. + +.. date: 2025-07-19-15-40-47 +.. gh-issue: 131724 +.. nonce: LS59nA +.. section: Library + +In :mod:`http.client`, a new *max_response_headers* keyword-only parameter +has been added to :class:`~http.client.HTTPConnection` and +:class:`~http.client.HTTPSConnection` constructors. This parameter sets the +maximum number of allowed response headers, helping to prevent +denial-of-service attacks. + +.. + +.. date: 2025-07-19-11-53-19 +.. gh-issue: 135427 +.. nonce: iJM_X2 +.. section: Library + +With :option:`-Werror <-W>`, the DeprecationWarning emitted by +:py:func:`os.fork` and :py:func:`os.forkpty` in mutli-threaded processes is +now raised as an exception. Previously it was silently ignored. Patch by +Rani Pinchuk. + +.. + +.. date: 2025-07-17-16-12-23 +.. gh-issue: 136234 +.. nonce: VmTxtj +.. section: Library + +Fix :meth:`asyncio.WriteTransport.writelines` to be robust to connection +failure, by using the same behavior as +:meth:`~asyncio.WriteTransport.write`. + +.. + +.. date: 2025-07-16-09-45-58 +.. gh-issue: 53144 +.. nonce: mrKwMW +.. section: Library + +:mod:`!encodings.aliases`: Add ``latin_N`` aliases + +.. + +.. date: 2025-07-15-16-37-34 +.. gh-issue: 136669 +.. nonce: Yexwah +.. section: Library + +:mod:`!_asyncio` is now statically linked for improved performance. + +.. + +.. date: 2025-07-13-13-31-22 +.. gh-issue: 136134 +.. nonce: mh6VjS +.. section: Library + +:meth:`!SMTP.auth_cram_md5` now raises an :exc:`~smtplib.SMTPException` +instead of a :exc:`ValueError` if Python has been built without MD5 support. +In particular, :class:`~smtplib.SMTP` clients will not attempt to use this +method even if the remote server is assumed to support it. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-07-13-11-20-05 +.. gh-issue: 136134 +.. nonce: xhh0Kq +.. section: Library + +:meth:`IMAP4.login_cram_md5 <imaplib.IMAP4.login_cram_md5>` now raises an +:exc:`IMAP4.error <imaplib.IMAP4.error>` if CRAM-MD5 authentication is not +supported. Patch by Bénédikt Tran. + +.. + +.. date: 2025-07-12-18-05-37 +.. gh-issue: 136591 +.. nonce: ujXmSN +.. section: Library + +:mod:`!_hashlib`: avoid using deprecated functions +:manpage:`ERR_func_error_string` and :manpage:`EVP_MD_CTX_md` when using +OpenSSL 3.0 and later. Patch by Bénédikt Tran. + +.. + +.. date: 2025-07-12-14-15-47 +.. gh-issue: 136571 +.. nonce: muHmBv +.. section: Library + +:meth:`datetime.date.fromisocalendar` can now raise OverflowError for out of +range arguments. + +.. + +.. date: 2025-07-11-23-04-39 +.. gh-issue: 136549 +.. nonce: oAi8u4 +.. section: Library + +Fix signature of :func:`threading.excepthook`. + +.. + +.. date: 2025-07-11-10-23-44 +.. gh-issue: 136492 +.. nonce: BVi5h0 +.. section: Library + +Expose :pep:`667`'s :data:`~types.FrameLocalsProxyType` in the :mod:`types` +module. + +.. + +.. date: 2025-07-11-08-15-17 +.. gh-issue: 83336 +.. nonce: ptpmq7 +.. section: Library + +``utf8_sig`` is now aliased to :mod:`encodings.utf_8_sig` + +.. + +.. date: 2025-07-11-03-39-15 +.. gh-issue: 136523 +.. nonce: s7caKL +.. section: Library + +Fix :class:`wave.Wave_write` emitting an unraisable when open raises. + +.. + +.. date: 2025-07-10-21-02-43 +.. gh-issue: 136507 +.. nonce: pnEuGS +.. section: Library + +Fix mimetypes CLI to handle multiple file parameters. + +.. + +.. date: 2025-07-10-10-18-19 +.. gh-issue: 52876 +.. nonce: 9Vjrd8 +.. section: Library + +Add missing ``keepends`` (default ``True``) parameter to +:meth:`!codecs.StreamReaderWriter.readline` and +:meth:`!codecs.StreamReaderWriter.readlines`. + +.. + +.. date: 2025-07-10-00-47-37 +.. gh-issue: 136470 +.. nonce: KlUEUG +.. section: Library + +Correct :class:`concurrent.futures.InterpreterPoolExecutor`'s default thread +name. + +.. + +.. date: 2025-07-09-20-29-30 +.. gh-issue: 136476 +.. nonce: HyLLzh +.. section: Library + +Fix a bug that was causing the ``get_async_stack_trace`` function to miss +some frames in the stack trace. + +.. + +.. date: 2025-07-08-20-58-01 +.. gh-issue: 136434 +.. nonce: uuJsjS +.. section: Library + +Fix docs generation of ``UnboundItem`` in :mod:`concurrent.interpreters` +when running with :option:`-OO`. + +.. + +.. date: 2025-07-07-22-12-32 +.. gh-issue: 136380 +.. nonce: 1b_nXl +.. section: Library + +Raises :exc:`AttributeError` when accessing +:class:`concurrent.futures.InterpreterPoolExecutor` and subinterpreters are +not available. + +.. + +.. date: 2025-07-07-16-46-55 +.. gh-issue: 72327 +.. nonce: wLvRuj +.. section: Library + +Suggest using the system command prompt when ``pip install`` is typed into +the REPL. Patch by Tom Viner, Richard Si, and Brian Schubert. + +.. + +.. date: 2025-07-06-18-38-10 +.. gh-issue: 135953 +.. nonce: Z29DCz +.. section: Library + +Implement a new high-frequency runtime profiler that leverages the existing +remote debugging functionality to collect detailed execution statistics from +running Python processes. This tool is exposed in the ``profile.sample`` +module and enables non-intrusive observation of production applications by +attaching to already-running processes without requiring any code +modifications, restarts, or special startup flags. The observer can perform +extremely high-frequency sampling of stack traces and interpreter state, +providing detailed runtime execution analysis of live applications. + +.. + +.. date: 2025-07-06-10-18-48 +.. gh-issue: 136021 +.. nonce: f-FJYT +.. section: Library + +Make ``type_params`` parameter required in :func:`!typing._eval_type` after +a deprecation period for not providing this parameter. Also remove the +:exc:`DeprecationWarning` for the old behavior. + +.. + +.. date: 2025-07-05-09-45-04 +.. gh-issue: 136286 +.. nonce: N67Amr +.. section: Library + +Fix pickling failures for protocols 0 and 1 for many objects related to +subinterpreters. + +.. + +.. date: 2025-07-05-06-59-46 +.. gh-issue: 136047 +.. nonce: qWvycf +.. section: Library + +Fix issues with :mod:`typing` when the C implementation of :mod:`abc` is not +available. + +.. + +.. date: 2025-07-05-06-56-16 +.. gh-issue: 136316 +.. nonce: 3zj_Do +.. section: Library + +Improve support for evaluating nested forward references in +:func:`typing.evaluate_forward_ref`. + +.. + +.. date: 2025-07-04-23-45-00 +.. gh-issue: 136306 +.. nonce: O1YLIU +.. section: Library + +:mod:`ssl` can now get and set groups used for key agreement. + +.. + +.. date: 2025-07-04-12-53-02 +.. gh-issue: 136156 +.. nonce: OYlXoz +.. section: Library + +:func:`tempfile.TemporaryFile` no longer uses :data:`os.O_EXCL` with +:data:`os.O_TMPFILE`, so it's possible to use ``linkat()`` on the file +descriptor. Patch by Victor Stinner. + +.. + +.. date: 2025-07-02-18-41-45 +.. gh-issue: 133982 +.. nonce: 7qqAn6 +.. section: Library + +Update Python implementation of :class:`io.BytesIO` to be thread safe. + +.. + +.. date: 2025-07-02-10-48-21 +.. gh-issue: 136193 +.. nonce: xfvras +.. section: Library + +Improve :exc:`TypeError` error message, when richcomparing two +:class:`types.SimpleNamespace` objects. + +.. + +.. date: 2025-07-01-14-44-03 +.. gh-issue: 136097 +.. nonce: bI1n14 +.. section: Library + +Fix potential infinite recursion and KeyError in ``sysconfig +--generate-posix-vars``. + +.. + +.. date: 2025-06-30-11-12-24 +.. gh-issue: 85702 +.. nonce: 0Lrbwu +.. section: Library + +If ``zoneinfo._common.load_tzdata`` is given a package without a resource a +:exc:`zoneinfo.ZoneInfoNotFoundError` is raised rather than a +:exc:`PermissionError`. Patch by Victor Stinner. + +.. + +.. date: 2025-06-29-15-22-13 +.. gh-issue: 90733 +.. nonce: NiquaA +.. section: Library + +Improve error messages when reporting invalid parameters in +:func:`hashlib.scrypt`. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-28-11-32-57 +.. gh-issue: 134759 +.. nonce: AjjKcG +.. section: Library + +Fix :exc:`UnboundLocalError` in :func:`email.message.Message.get_payload` +when the payload to decode is a :class:`bytes` object. Patch by Kliment +Lamonov. + +.. + +.. date: 2025-06-27-13-34-28 +.. gh-issue: 136028 +.. nonce: RY727g +.. section: Library + +Fix parsing month names containing "İ" (U+0130, LATIN CAPITAL LETTER I WITH +DOT ABOVE) in :func:`time.strptime`. This affects locales az_AZ, ber_DZ, +ber_MA and crh_UA. + +.. + +.. date: 2025-06-27-09-26-04 +.. gh-issue: 87135 +.. nonce: 33z0UW +.. section: Library + +Acquiring a :class:`threading.Lock` or :class:`threading.RLock` at +interpreter shutdown will raise :exc:`PythonFinalizationError` if Python can +determine that it would otherwise deadlock. + +.. + +.. date: 2025-06-26-17-28-49 +.. gh-issue: 135995 +.. nonce: pPrDCt +.. section: Library + +In the palmos encoding, make byte ``0x9b`` decode to ``›`` (U+203A - SINGLE +RIGHT-POINTING ANGLE QUOTATION MARK). + +.. + +.. date: 2025-06-26-17-19-36 +.. gh-issue: 105456 +.. nonce: eR9oHB +.. section: Library + +Removed :mod:`!sre_compile`, :mod:`!sre_constants` and :mod:`!sre_parse` +modules. + +.. + +.. date: 2025-06-26-11-52-40 +.. gh-issue: 53203 +.. nonce: TMigBr +.. section: Library + +Fix :func:`time.strptime` for ``%c`` and ``%x`` formats on locales byn_ER, +wal_ET and lzh_TW, and for ``%X`` format on locales ar_SA, bg_BG and lzh_TW. + +.. + +.. date: 2025-06-24-14-43-24 +.. gh-issue: 135878 +.. nonce: Db4roX +.. section: Library + +Fixes a crash of :class:`types.SimpleNamespace` on :term:`free threading` +builds, when several threads were calling its :meth:`~object.__repr__` +method at the same time. + +.. + +.. date: 2025-06-24-13-30-47 +.. gh-issue: 135853 +.. nonce: 7ejTvK +.. section: Library + +Add :func:`math.fmax` and :func:`math.fmin` to get the larger and smaller of +two floating-point values. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-24-10-52-35 +.. gh-issue: 135836 +.. nonce: s37351 +.. section: Library + +Fix :exc:`IndexError` in :meth:`asyncio.loop.create_connection` that could +occur when non-\ :exc:`OSError` exception is raised during connection and +socket's ``close()`` raises :exc:`!OSError`. + +.. + +.. date: 2025-06-24-10-23-37 +.. gh-issue: 135853 +.. nonce: 6xDNOG +.. section: Library + +:mod:`math`: expose C99 :func:`~math.signbit` function to determine whether +the sign bit of a floating-point value is set. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-23-13-02-08 +.. gh-issue: 134531 +.. nonce: yUmj07 +.. section: Library + +:mod:`hmac`: use the :manpage:`EVP_MAC(3ssl)` interface for HMAC when Python +is built with OpenSSL 3.0 and later instead of the deprecated +:manpage:`HMAC_CTX(3ssl) <hmac(3)>` interface. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-23-11-04-25 +.. gh-issue: 135836 +.. nonce: -C-c4v +.. section: Library + +Fix :exc:`IndexError` in :meth:`asyncio.loop.create_connection` that could +occur when the Happy Eyeballs algorithm resulted in an empty exceptions list +during connection attempts. + +.. + +.. date: 2025-06-23-10-19-11 +.. gh-issue: 135855 +.. nonce: -J0AGF +.. section: Library + +Raise :exc:`TypeError` instead of :exc:`SystemError` when +:func:`!_interpreters.set___main___attrs` is passed a non-dict object. Patch +by Brian Schubert. + +.. + +.. date: 2025-06-22-22-03-06 +.. gh-issue: 135823 +.. nonce: iDBg97 +.. section: Library + +:mod:`netrc`: improve the error message when the security check for the +ownership of the default configuration file ``~/.netrc`` fails. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-06-22-16-23-44 +.. gh-issue: 135815 +.. nonce: 0DandH +.. section: Library + +:mod:`netrc`: skip security checks if :func:`os.getuid` is missing. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-06-22-02-16-17 +.. gh-issue: 135640 +.. nonce: FXyFL6 +.. section: Library + +Address bug where it was possible to call +:func:`xml.etree.ElementTree.ElementTree.write` on an ElementTree object +with an invalid root element. This behavior blanked the file passed to +``write`` if it already existed. + +.. + +.. date: 2025-06-20-17-06-59 +.. gh-issue: 90117 +.. nonce: GYWVrn +.. section: Library + +Speed up :mod:`pprint` for :class:`list` and :class:`tuple`. + +.. + +.. date: 2025-06-20-16-28-47 +.. gh-issue: 135759 +.. nonce: jne0Zi +.. section: Library + +:mod:`hashlib`: reject negative digest lengths in OpenSSL-based SHAKE +objects by raising a :exc:`ValueError`. Previously, negative lengths were +implicitly rejected by raising a :exc:`MemoryError` or a :exc:`SystemError`. +Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-18-19-25-32 +.. gh-issue: 123471 +.. nonce: lx1Xbt +.. section: Library + +Make concurrent iterations over :class:`itertools.chain` safe under +:term:`free threading`. + +.. + +.. date: 2025-06-18-13-58-13 +.. gh-issue: 135645 +.. nonce: 109nff +.. section: Library + +Added ``supports_isolated_interpreters`` field to +:data:`sys.implementation`. + +.. + +.. date: 2025-06-18-11-43-17 +.. gh-issue: 135646 +.. nonce: r7ekEn +.. section: Library + +Raise consistent :exc:`NameError` exceptions in +:func:`annotationlib.ForwardRef.evaluate` + +.. + +.. date: 2025-06-17-23-13-56 +.. gh-issue: 135557 +.. nonce: Bfcy4v +.. section: Library + +Fix races on :mod:`heapq` updates and :class:`list` reads on the :term:`free +threaded <free threading>` build. + +.. + +.. date: 2025-06-17-22-44-19 +.. gh-issue: 119180 +.. nonce: Ogv8Nj +.. section: Library + +Only fetch globals and locals if necessary in +:func:`annotationlib.get_annotations` + +.. + +.. date: 2025-06-16-15-03-03 +.. gh-issue: 135561 +.. nonce: mJCN8D +.. section: Library + +Fix a crash on DEBUG builds when an HACL* HMAC routine fails. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-06-16-15-00-13 +.. gh-issue: 135386 +.. nonce: lNrxLc +.. section: Library + +Fix opening a :mod:`dbm.sqlite3` database for reading from read-only file or +directory. + +.. + +.. date: 2025-06-16-12-37-02 +.. gh-issue: 135444 +.. nonce: An2eeA +.. section: Library + +Fix :meth:`asyncio.DatagramTransport.sendto` to account for datagram header +size when data cannot be sent. + +.. + +.. date: 2025-06-15-03-03-22 +.. gh-issue: 65697 +.. nonce: COdwZd +.. section: Library + +:class:`configparser`'s error message when attempting to write an invalid +key is now more helpful. + +.. + +.. date: 2025-06-14-14-19-13 +.. gh-issue: 135497 +.. nonce: 1pzwdA +.. section: Library + +Fix :func:`os.getlogin` failing for longer usernames on BSD-based platforms. + +.. + +.. date: 2025-06-14-12-06-55 +.. gh-issue: 135487 +.. nonce: KdVFff +.. section: Library + +Fix :meth:`!reprlib.Repr.repr_int` when given integers with more than +:func:`sys.get_int_max_str_digits` digits. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-12-18-15-31 +.. gh-issue: 135429 +.. nonce: mch75_ +.. section: Library + +Fix the argument mismatch in ``_lsprof`` for ``PY_THROW`` event. + +.. + +.. date: 2025-06-12-10-45-02 +.. gh-issue: 135368 +.. nonce: OjWVHL +.. section: Library + +Fix :class:`unittest.mock.Mock` generation on :func:`dataclasses.dataclass` +objects. Now all special attributes are set as it was before :gh:`124429`. + +.. + +.. date: 2025-06-11-15-08-02 +.. gh-issue: 135336 +.. nonce: 6Gq6MI +.. section: Library + +:mod:`json` now encodes strings up to 2.2x faster if they consist solely of +characters that don’t require escaping. + +.. + +.. date: 2025-06-10-21-42-04 +.. gh-issue: 135335 +.. nonce: WnUqb_ +.. section: Library + +:mod:`multiprocessing`: Flush ``stdout`` and ``stderr`` after preloading +modules in the ``forkserver``. + +.. + +.. date: 2025-06-10-21-00-48 +.. gh-issue: 126631 +.. nonce: eITVJd +.. section: Library + +Fix :mod:`multiprocessing` ``forkserver`` bug which prevented ``__main__`` +from being preloaded. + +.. + +.. date: 2025-06-10-16-11-00 +.. gh-issue: 133967 +.. nonce: P0c24q +.. section: Library + +Do not normalize :mod:`locale` name 'C.UTF-8' to 'en_US.UTF-8'. + +.. + +.. date: 2025-06-10-10-22-18 +.. gh-issue: 130870 +.. nonce: JipqbO +.. section: Library + +Preserve :class:`types.GenericAlias` subclasses in +:func:`typing.get_type_hints` + +.. + +.. date: 2025-06-10-00-42-30 +.. gh-issue: 135321 +.. nonce: UHh9jT +.. section: Library + +Raise a correct exception for values greater than 0x7fffffff for the +``BINSTRING`` opcode in the C implementation of :mod:`pickle`. + +.. + +.. date: 2025-06-09-10-16-55 +.. gh-issue: 121914 +.. nonce: G6Avkq +.. section: Library + +Changed the names of the symbol tables for lambda expressions and generator +expressions to "<lambda>" and "<genexpr>" respectively to avoid conflicts +with user-defined names. + +.. + +.. date: 2025-06-08-14-50-34 +.. gh-issue: 135276 +.. nonce: ZLUhV1 +.. section: Library + +Synchronized zipfile.Path with zipp 3.23, including improved performance of +:meth:`zipfile.Path.open` for non-reading modes, rely on +:func:`functools.cached_property` to cache values on the instance. Rely on +``save_method_args`` to save the initialization method arguments. Fixed +``.name``, ``.stem`` and other basename-based properties on Windows when +working with a zipfile on disk. + +.. + +.. date: 2025-06-08-11-11-07 +.. gh-issue: 135234 +.. nonce: wJCdh0 +.. section: Library + +:mod:`hashlib`: improve exception messages when an OpenSSL function failed. +When memory allocation fails on OpenSSL's side, a :exc:`MemoryError` is +raised instead of a :exc:`ValueError`. Patch by Bénédikt Tran. + +.. + +.. date: 2025-06-08-10-22-22 +.. gh-issue: 135244 +.. nonce: Y2SOTJ +.. section: Library + +:mod:`uuid`: when the MAC address cannot be determined, the 48-bit node ID +is now generated with a cryptographically-secure pseudo-random number +generator (CSPRNG) as per :rfc:`RFC 9562, §6.10.3 <9562#section-6.10-3>`. +This affects :func:`~uuid.uuid1` and :func:`~uuid.uuid6`. + +.. + +.. date: 2025-06-08-01-10-34 +.. gh-issue: 135241 +.. nonce: 5j18IW +.. section: Library + +The :code:`INT` opcode of the C accelerator :mod:`!_pickle` module was +updated to look only for "00" and "01" to push booleans onto the stack, +aligning with the Python :mod:`pickle` module. + +.. + +.. date: 2025-06-06-17-34-18 +.. gh-issue: 133934 +.. nonce: yT1r68 +.. section: Library + +Improve :mod:`sqlite3` CLI's ``.help`` message. + +.. + +.. date: 2025-06-03-12-59-17 +.. gh-issue: 135069 +.. nonce: xop30V +.. section: Library + +Fix the "Invalid error handling" exception in +:class:`!encodings.idna.IncrementalDecoder` to correctly replace the +'errors' parameter. + +.. + +.. date: 2025-06-02-14-36-28 +.. gh-issue: 130662 +.. nonce: Gpr2GB +.. section: Library + ++Accept leading zeros in precision and width fields for ++:class:`~decimal.Decimal` formatting, for example ``format(Decimal(1.25), +'.016f')``. + +.. + +.. date: 2025-06-02-14-28-30 +.. gh-issue: 130662 +.. nonce: EIgIR8 +.. section: Library + +Accept leading zeros in precision and width fields for +:class:`~fractions.Fraction` formatting, for example ``format(Fraction(1, +3), '.016f')``. + +.. + +.. date: 2025-06-01-14-18-48 +.. gh-issue: 135004 +.. nonce: cq3-fp +.. section: Library + +Rewrite and cleanup the internal :mod:`!_blake2` module. Some exception +messages were changed but their types were left untouched. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-06-01-11-14-00 +.. gh-issue: 134953 +.. nonce: ashdfs +.. section: Library + +Expand ``_colorize`` theme with ``keyword_constant`` and implement in +:term:`repl`. + +.. + +.. date: 2025-05-31-15-49-46 +.. gh-issue: 134978 +.. nonce: mXXuvW +.. section: Library + +:mod:`hashlib`: Supporting the ``string`` keyword parameter in hash function +constructors such as :func:`~hashlib.new` or the direct hash-named +constructors such as :func:`~hashlib.md5` and :func:`~hashlib.sha256` is now +deprecated and slated for removal in Python 3.19. Prefer passing the initial +data as a positional argument for maximum backwards compatibility. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-05-31-12-08-12 +.. gh-issue: 134970 +.. nonce: lgSaxq +.. section: Library + +Fix the "unknown action" exception in +:meth:`argparse.ArgumentParser.add_argument_group` to correctly replace the +action class. + +.. + +.. date: 2025-05-30-18-13-48 +.. gh-issue: 134718 +.. nonce: 5FEspx +.. section: Library + +By default, omit optional ``Load()`` values in :func:`ast.dump`. + +.. + +.. date: 2025-05-30-13-07-29 +.. gh-issue: 134718 +.. nonce: 9Qvhxn +.. section: Library + +:func:`ast.dump` now only omits ``None`` and ``[]`` values if they are +default values. + +.. + +.. date: 2025-05-30-09-46-21 +.. gh-issue: 134939 +.. nonce: Pu3nnm +.. section: Library + +Add the :mod:`concurrent.interpreters` module. See :pep:`734`. + +.. + +.. date: 2025-05-29-17-39-13 +.. gh-issue: 108885 +.. nonce: MegCRA +.. section: Library + +Run each example as a subtest in unit tests synthesized by +:func:`doctest.DocFileSuite` and :func:`doctest.DocTestSuite`. Add the +:meth:`doctest.DocTestRunner.report_skip` method. + +.. + +.. date: 2025-05-29-06-53-40 +.. gh-issue: 134885 +.. nonce: -_L22o +.. section: Library + +Fix possible crash in the :mod:`compression.zstd` module related to setting +parameter types. Patch by Jelle Zijlstra. + +.. + +.. date: 2025-05-28-20-49-29 +.. gh-issue: 134857 +.. nonce: dVYXVO +.. section: Library + +Improve error report for :mod:`doctest`\ s run with :mod:`unittest`. Remove +:mod:`!doctest` module frames from tracebacks and redundant newline +character from a failure message. + +.. + +.. date: 2025-05-28-15-53-27 +.. gh-issue: 128840 +.. nonce: Nur2pB +.. section: Library + +Fix parsing long IPv6 addresses with embedded IPv4 address. + +.. + +.. date: 2025-05-27-11-24-38 +.. gh-issue: 133579 +.. nonce: WGPUC1 +.. section: Library + +:mod:`curses`: Consistently report failures of curses C API calls in +module-level methods by raising a :exc:`curses.error`. This affects +:func:`~curses.assume_default_colors`, :func:`~curses.baudrate`, +:func:`~curses.cbreak`, :func:`~curses.echo`, :func:`~curses.longname`, +:func:`~curses.initscr`, :func:`~curses.nl`, :func:`~curses.raw`, +:func:`~curses.termattrs`, :func:`~curses.termname` and +:func:`~curses.unctrl`. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-27-11-18-13 +.. gh-issue: 133579 +.. nonce: ohtgdC +.. section: Library + +:meth:`curses.window.refresh` and :meth:`curses.window.noutrefresh` now +raise a :exc:`TypeError` instead of :exc:`curses.error` when called with an +incorrect number of arguments for :ref:`pads <windows-and-pads>`. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-05-27-11-13-51 +.. gh-issue: 133579 +.. nonce: KY9M6S +.. section: Library + +:ref:`curses.window <curses-window-objects>`: Consistently report failures +of curses C API calls in Window methods by raising a :exc:`curses.error`. +This affects :meth:`~curses.window.addch`, :meth:`~curses.window.addnstr`, +:meth:`~curses.window.addstr`, :meth:`~curses.window.border`, +:meth:`~curses.window.box`, :meth:`~curses.window.chgat`, +:meth:`~curses.window.getbkgd`, :meth:`~curses.window.inch`, +:meth:`~curses.window.insstr` and :meth:`~curses.window.insnstr`. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-05-26-22-18-32 +.. gh-issue: 134771 +.. nonce: RKXpLT +.. section: Library + +The ``time_clockid_converter()`` function now selects correct type for +``clockid_t`` on Cygwin which fixes a build error. + +.. + +.. date: 2025-05-26-17-06-40 +.. gh-issue: 134637 +.. nonce: 9-3zRL +.. section: Library + +Fix performance regression in calling a :mod:`ctypes` function pointer in +:term:`free threading`. + +.. + +.. date: 2025-05-26-14-04-39 +.. gh-issue: 134696 +.. nonce: P04xUa +.. section: Library + +Built-in HACL* and OpenSSL implementations of hash function constructors now +correctly accept the same *documented* named arguments. For instance, +:func:`~hashlib.md5` could be previously invoked as ``md5(data=data)`` or +``md5(string=string)`` depending on the underlying implementation but these +calls were not compatible. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-26-12-31-08 +.. gh-issue: 132710 +.. nonce: ApU3TZ +.. section: Library + +If possible, ensure that :func:`uuid.getnode` returns the same result even +across different processes. Previously, the result was constant only within +the same process. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-26-11-01-54 +.. gh-issue: 134531 +.. nonce: my1Fzt +.. section: Library + +:mod:`!_hashlib`: Rename internal C functions for :class:`!_hashlib.HASH` +and :class:`!_hashlib.HASHXOF` objects. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-26-10-52-27 +.. gh-issue: 134698 +.. nonce: aJ1mZ1 +.. section: Library + +Fix a crash when calling methods of :class:`ssl.SSLContext` or +:class:`ssl.SSLSocket` across multiple threads. + +.. + +.. date: 2025-05-25-23-23-05 +.. gh-issue: 134151 +.. nonce: 13Wwsb +.. section: Library + +:mod:`email`: Fix :exc:`TypeError` in :func:`email.utils.decode_params` when +sorting :rfc:`2231` continuations that contain an unnumbered section. + +.. + +.. date: 2025-05-25-13-46-37 +.. gh-issue: 134635 +.. nonce: ZlPrlX +.. section: Library + +:mod:`zlib`: Allow to combine Adler-32 and CRC-32 checksums via +:func:`~zlib.adler32_combine` and :func:`~zlib.crc32_combine`. Patch by +Callum Attryde and Bénédikt Tran. + +.. + +.. date: 2025-05-25-11-02-05 +.. gh-issue: 134657 +.. nonce: 3YFhR9 +.. section: Library + +:mod:`asyncio`: Remove some private names from ``asyncio.__all__``. + +.. + +.. date: 2025-05-24-13-10-35 +.. gh-issue: 134210 +.. nonce: 0IuMY2 +.. section: Library + +:func:`curses.window.getch` now correctly handles signals. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-05-24-03-10-36 +.. gh-issue: 80334 +.. nonce: z21cMa +.. section: Library + +:func:`multiprocessing.freeze_support` now checks for work on any "spawn" +start method platform rather than only on Windows. + +.. + +.. date: 2025-05-23-23-43-39 +.. gh-issue: 134582 +.. nonce: 9POq3l +.. section: Library + +Fix tokenize.untokenize() round-trip errors related to t-strings braces +escaping + +.. + +.. date: 2025-05-23-20-01-52 +.. gh-issue: 134580 +.. nonce: xnaJ70 +.. section: Library + +Improved the styling of HTML diff pages generated by the +:class:`difflib.HtmlDiff` class, and migrated the output to the HTML5 +standard. + +.. + +.. date: 2025-05-23-10-15-36 +.. gh-issue: 134565 +.. nonce: zmb66C +.. section: Library + +:func:`unittest.doModuleCleanups` no longer swallows all but first exception +raised in the cleanup code, but raises a :exc:`ExceptionGroup` if multiple +errors occurred. + +.. + +.. date: 2025-05-22-18-14-13 +.. gh-issue: 134546 +.. nonce: fjLVzK +.. section: Library + +Ensure :mod:`pdb` remote debugging script is readable by remote Python +process. + +.. + +.. date: 2025-05-22-14-12-53 +.. gh-issue: 134451 +.. nonce: M1rD-j +.. section: Library + +Converted ``asyncio.tools.CycleFoundException`` from dataclass to a regular +exception type. + +.. + +.. date: 2025-05-22-13-10-32 +.. gh-issue: 114177 +.. nonce: 3TYUJ3 +.. section: Library + +Fix :mod:`asyncio` to not close subprocess pipes which would otherwise error +out when the event loop is already closed. + +.. + +.. date: 2025-05-20-21-45-58 +.. gh-issue: 90871 +.. nonce: Gkvtp6 +.. section: Library + +Fixed an off by one error concerning the backlog parameter in +:meth:`~asyncio.loop.create_unix_server`. Contributed by Christian Harries. + +.. + +.. date: 2025-05-20-19-16-30 +.. gh-issue: 134323 +.. nonce: ZQZGvw +.. section: Library + +Fix the :meth:`threading.RLock.locked` method. + +.. + +.. date: 2025-05-20-15-13-43 +.. gh-issue: 86802 +.. nonce: trF7TM +.. section: Library + +Fixed asyncio memory leak in cancelled shield tasks. For shielded tasks +where the shield was cancelled, log potential exceptions through the +exception handler. Contributed by Christian Harries. + +.. + +.. date: 2025-05-20-11-51-17 +.. gh-issue: 71189 +.. nonce: 0LpTB1 +.. section: Library + +Add support of the all-but-last mode in :func:`os.path.realpath`. + +.. + +.. date: 2025-05-20-11-35-08 +.. gh-issue: 72902 +.. nonce: jzEI-E +.. section: Library + +Improve speed (x1.1-1.8) of the :class:`~fractions.Fraction` constructor for +typical inputs (:class:`float`'s, :class:`~decimal.Decimal`'s or strings). + +.. + +.. date: 2025-05-19-20-59-06 +.. gh-issue: 134209 +.. nonce: anhTcF +.. section: Library + +:mod:`curses`: The :meth:`curses.window.instr` and +:meth:`curses.window.getstr` methods now allocate their internal buffer on +the heap instead of the stack; in addition, the max buffer size is increased +from 1023 to 2047. + +.. + +.. date: 2025-05-19-18-12-42 +.. gh-issue: 88994 +.. nonce: 7avvVu +.. section: Library + +Change :func:`datetime.datetime.now` to half-even rounding for consistency +with :func:`datetime.datetime.fromtimestamp`. Patch by John Keith Hohm. + +.. + +.. date: 2025-05-19-17-27-21 +.. gh-issue: 80184 +.. nonce: LOkbaw +.. section: Library + +The default queue size is now ``socket.SOMAXCONN`` for +:class:`socketserver.TCPServer`. + +.. + +.. date: 2025-05-19-15-30-00 +.. gh-issue: 132983 +.. nonce: asdsfs +.. section: Library + +Add :mod:`!compression.zstd` version information to ``test.pythoninfo``. + +.. + +.. date: 2025-05-19-15-05-24 +.. gh-issue: 134235 +.. nonce: pz9PwV +.. section: Library + +Updated tab completion on REPL to include builtin modules. Contributed by +Tom Wang, Hunter Young + +.. + +.. date: 2025-05-19-10-32-11 +.. gh-issue: 134152 +.. nonce: INJC2j +.. section: Library + +Fixed :exc:`UnboundLocalError` that could occur during :mod:`email` header +parsing if an expected trailing delimiter is missing in some contexts. + +.. + +.. date: 2025-05-18-23-46-21 +.. gh-issue: 134152 +.. nonce: 30HwbX +.. section: Library + +:mod:`email`: Fix parsing of email message ID with invalid domain. + +.. + +.. date: 2025-05-18-13-23-29 +.. gh-issue: 134168 +.. nonce: hgx3Xg +.. section: Library + +:mod:`http.server`: Fix IPv6 address binding and :option:`--directory +<http.server --directory>` handling when using HTTPS. + +.. + +.. date: 2025-05-18-12-48-39 +.. gh-issue: 62184 +.. nonce: y11l10 +.. section: Library + +Remove import of C implementation of :class:`io.FileIO` from Python +implementation which has its own implementation + +.. + +.. date: 2025-05-18-12-23-07 +.. gh-issue: 134087 +.. nonce: HilZWl +.. section: Library + +Remove support for arbitrary positional or keyword arguments in the C +implementation of :class:`threading.RLock` objects. This was deprecated +since Python 3.14. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-18-07-25-15 +.. gh-issue: 134173 +.. nonce: 53oOoF +.. section: Library + +Speed up :mod:`asyncio` performance of transferring state from thread pool +:class:`concurrent.futures.Future` by up to 4.4x. Patch by J. Nick Koston. + +.. + +.. date: 2025-05-17-20-23-57 +.. gh-issue: 133982 +.. nonce: smS7au +.. section: Library + +Emit :exc:`RuntimeWarning` in the Python implementation of :mod:`io` when +the :term:`file-like object <file object>` is not closed explicitly in the +presence of multiple I/O layers. + +.. + +.. date: 2025-05-17-18-08-35 +.. gh-issue: 133890 +.. nonce: onn9_X +.. section: Library + +The :mod:`tarfile` module now handles :exc:`UnicodeEncodeError` in the same +way as :exc:`OSError` when cannot extract a member. + +.. + +.. date: 2025-05-17-13-46-20 +.. gh-issue: 134097 +.. nonce: fgkjE1 +.. section: Library + +Fix interaction of the new :term:`REPL` and :option:`-X showrefcount <-X>` +command line option. + +.. + +.. date: 2025-05-17-12-40-12 +.. gh-issue: 133889 +.. nonce: Eh-zO4 +.. section: Library + +The generated directory listing page in +:class:`http.server.SimpleHTTPRequestHandler` now only shows the decoded +path component of the requested URL, and not the query and fragment. + +.. + +.. date: 2025-05-16-20-10-25 +.. gh-issue: 134098 +.. nonce: YyTkKr +.. section: Library + +Fix handling paths that end with a percent-encoded slash (``%2f`` or +``%2F``) in :class:`http.server.SimpleHTTPRequestHandler`. + +.. + +.. date: 2025-05-16-12-40-37 +.. gh-issue: 132124 +.. nonce: T_5Odx +.. section: Library + +On POSIX-compliant systems, :func:`!multiprocessing.util.get_temp_dir` now +ignores :envvar:`TMPDIR` (and similar environment variables) if the path +length of ``AF_UNIX`` socket files exceeds the platform-specific maximum +length when using the :ref:`forkserver +<multiprocessing-start-method-forkserver>` start method. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-05-15-14-27-01 +.. gh-issue: 134062 +.. nonce: fRbJet +.. section: Library + +:mod:`ipaddress`: fix collisions in :meth:`~object.__hash__` for +:class:`~ipaddress.IPv4Network` and :class:`~ipaddress.IPv6Network` objects. + +.. + +.. date: 2025-05-15-00-27-09 +.. gh-issue: 134004 +.. nonce: e8k4-R +.. section: Library + +:mod:`shelve` as well as underlying :mod:`!dbm.dumb` and :mod:`!dbm.sqlite` +now have :meth:`!reorganize` methods to recover unused free space previously +occupied by deleted entries. + +.. + +.. date: 2025-05-13-18-54-56 +.. gh-issue: 133970 +.. nonce: 6G-Oi6 +.. section: Library + +Make :class:`!string.templatelib.Template` and +:class:`!string.templatelib.Interpolation` generic. + +.. + +.. date: 2025-05-13-18-21-59 +.. gh-issue: 71253 +.. nonce: -3Sf_K +.. section: Library + +Raise :exc:`ValueError` in :func:`open` if *opener* returns a negative +file-descriptor in the Python implementation of :mod:`io` to match the C +implementation. + +.. + +.. date: 2025-05-12-20-38-57 +.. gh-issue: 133960 +.. nonce: Aee79f +.. section: Library + +Simplify and improve :func:`typing.evaluate_forward_ref`. It now no longer +raises errors on certain invalid types. In several situations, it is now +able to evaluate forward references that were previously unsupported. + +.. + +.. date: 2025-05-12-06-52-10 +.. gh-issue: 133925 +.. nonce: elInBY +.. section: Library + +Make the private class ``typing._UnionGenericAlias`` hashable. + +.. + +.. date: 2025-05-11-12-56-52 +.. gh-issue: 133604 +.. nonce: kFxhc8 +.. section: Library + +Remove :func:`!platform.java_ver` which was deprecated since Python 3.13. + +.. + +.. date: 2025-05-11-11-39-05 +.. gh-issue: 133875 +.. nonce: pUar3l +.. section: Library + +Removed deprecated :meth:`!pathlib.PurePath.is_reserved`. Use +:func:`os.path.isreserved` to detect reserved paths on Windows. + +.. + +.. date: 2025-05-11-10-28-11 +.. gh-issue: 133873 +.. nonce: H03nov +.. section: Library + +Remove the deprecated ``getmark()``, ``setmark()`` and ``getmarkers()`` +methods of the :class:`~wave.Wave_read` and :class:`~wave.Wave_write` +classes, which were deprecated since Python 3.13. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-11-10-01-48 +.. gh-issue: 133866 +.. nonce: g3dHP_ +.. section: Library + +Remove the undocumented function :func:`!ctypes.SetPointerType`, which has +been deprecated since Python 3.13. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-11-08-48-55 +.. gh-issue: 133823 +.. nonce: F8udQy +.. section: Library + +Remove support for ``TD = TypedDict("TD")`` and ``TD = TypedDict("TD", +None)`` calls for constructing :class:`typing.TypedDict` objects with zero +field. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-10-17-42-03 +.. gh-issue: 125996 +.. nonce: vaQp0- +.. section: Library + +Fix thread safety of :class:`collections.OrderedDict`. Patch by Kumar +Aditya. + +.. + +.. date: 2025-05-10-12-07-54 +.. gh-issue: 133817 +.. nonce: 4GMtKV +.. section: Library + +Remove support for creating :class:`~typing.NamedTuple` classes via the +undocumented keyword argument syntax. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-10-12-06-55 +.. gh-issue: 133653 +.. nonce: Gb2aG4 +.. section: Library + +Fix :class:`argparse.ArgumentParser` with the *formatter_class* argument. +Fix TypeError when *formatter_class* is a custom subclass of +:class:`!HelpFormatter`. Fix TypeError when *formatter_class* is not a +subclass of :class:`!HelpFormatter` and non-standard *prefix_char* is used. +Fix support of colorizing when *formatter_class* is not a subclass of +:class:`!HelpFormatter`. + +.. + +.. date: 2025-05-10-11-04-47 +.. gh-issue: 133810 +.. nonce: 03WhnK +.. section: Library + +Remove :class:`!http.server.CGIHTTPRequestHandler` and ``--cgi`` flag from +the :program:`python -m http.server` command-line interface. They were +deprecated in Python 3.13. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-09-20-59-24 +.. gh-issue: 132641 +.. nonce: 3qTw44 +.. section: Library + +Fixed a race in :func:`functools.lru_cache` under free-threading. + +.. + +.. date: 2025-05-09-19-05-24 +.. gh-issue: 133783 +.. nonce: 1voCnR +.. section: Library + +Fix bug with applying :func:`copy.replace` to :mod:`ast` objects. Attributes +that default to ``None`` were incorrectly treated as required for manually +created AST nodes. + +.. + +.. date: 2025-05-09-18-29-25 +.. gh-issue: 133684 +.. nonce: Y1DFSt +.. section: Library + +Fix bug where :func:`annotationlib.get_annotations` would return the wrong +result for certain classes that are part of a class hierarchy where ``from +__future__ import annotations`` is used. + +.. + +.. date: 2025-05-09-15-50-00 +.. gh-issue: 77057 +.. nonce: fV8SU- +.. section: Library + +Fix handling of invalid markup declarations in +:class:`html.parser.HTMLParser`. + +.. + +.. date: 2025-05-09-09-10-34 +.. gh-issue: 130328 +.. nonce: s9h4By +.. section: Library + +Speedup pasting in ``PyREPL`` on Windows in a legacy console. Patch by Chris +Eibl. + +.. + +.. date: 2025-05-09-08-49-03 +.. gh-issue: 133701 +.. nonce: KI8tGz +.. section: Library + +Fix bug where :class:`typing.TypedDict` classes defined under ``from +__future__ import annotations`` and inheriting from another ``TypedDict`` +had an incorrect ``__annotations__`` attribute. + +.. + +.. date: 2025-05-08-20-45-35 +.. gh-issue: 133656 +.. nonce: cxZODA +.. section: Library + +Remove deprecated :meth:`!zipimport.zipimporter.load_module`. Use +:meth:`zipimport.zipimporter.exec_module` instead. + +.. + +.. date: 2025-05-08-20-03-20 +.. gh-issue: 133722 +.. nonce: 1-B82a +.. section: Library + +Added a *color* option to :func:`difflib.unified_diff` that colors output +similar to :program:`git diff`. + +.. + +.. date: 2025-05-08-13-43-19 +.. gh-issue: 133489 +.. nonce: 9eGS1Z +.. section: Library + +:func:`random.getrandbits` can now generate more that 2\ :sup:`31` bits. +:func:`random.randbytes` can now generate more that 256 MiB. + +.. + +.. date: 2025-05-07-22-15-15 +.. gh-issue: 133595 +.. nonce: c3U88r +.. section: Library + +Clean up :class:`sqlite3.Connection` APIs. All parameters of +:func:`sqlite3.connect` except *database* are now keyword-only. The first +three parameters of methods :meth:`~sqlite3.Connection.create_function` and +:meth:`~sqlite3.Connection.create_aggregate` are now positional-only. The +first parameter of methods :meth:`~sqlite3.Connection.set_authorizer`, +:meth:`~sqlite3.Connection.set_progress_handler` and +:meth:`~sqlite3.Connection.set_trace_callback` is now positional-only. + +.. + +.. date: 2025-05-07-19-16-41 +.. gh-issue: 133581 +.. nonce: kERUCJ +.. section: Library + +Improve unparsing of t-strings in :func:`ast.unparse` and ``from __future__ +import annotations``. Empty t-strings now round-trip correctly and +formatting in interpolations is preserved. Patch by Jelle Zijlstra. + +.. + +.. date: 2025-05-07-14-36-30 +.. gh-issue: 133577 +.. nonce: BggPk9 +.. section: Library + +Add parameter ``formatter`` to :func:`logging.basicConfig`. + +.. + +.. date: 2025-05-07-13-31-06 +.. gh-issue: 92897 +.. nonce: ubeqGE +.. section: Library + +Removed the ``check_home`` parameter from :func:`sysconfig.is_python_build`, +deprecated since Python 3.12. + +.. + +.. date: 2025-05-06-22-54-37 +.. gh-issue: 133551 +.. nonce: rfy1tJ +.. section: Library + +Support t-strings (:pep:`750`) in :mod:`annotationlib`. Patch by Jelle +Zijlstra. + +.. + +.. date: 2025-05-06-14-44-55 +.. gh-issue: 133517 +.. nonce: Ca6NgW +.. section: Library + +Remove :func:`os.listdrives`, :func:`os.listvolumes` and +:func:`os.listmounts` in non Windows desktop builds since the underlying +functionality is missing. + +.. + +.. date: 2025-05-05-22-11-24 +.. gh-issue: 133439 +.. nonce: LpmyFz +.. section: Library + +Fix dot commands with trailing spaces are mistaken for multi-line SQL +statements in the sqlite3 command-line interface. + +.. + +.. date: 2025-05-05-18-50-00 +.. gh-issue: 133447 +.. nonce: ajshdb +.. section: Library + +Add basic color to :mod:`sqlite3` CLI interface. + +.. + +.. date: 2025-05-05-10-41-41 +.. gh-issue: 133253 +.. nonce: J5-xDD +.. section: Library + +Fix thread-safety issues in :mod:`linecache`. + +.. + +.. date: 2025-05-05-03-14-08 +.. gh-issue: 133390 +.. nonce: AuTggn +.. section: Library + +Support keyword completion in the :mod:`sqlite3` command-line interface and +add :data:`sqlite3.SQLITE_KEYWORDS` constant. + +.. + +.. date: 2025-05-04-17-04-55 +.. gh-issue: 132493 +.. nonce: huirKi +.. section: Library + +Avoid accessing ``__annotations__`` unnecessarily in +:func:`inspect.signature`. + +.. + +.. date: 2025-05-01-16-03-11 +.. gh-issue: 133017 +.. nonce: k7RLQp +.. section: Library + +Improve the error message of :func:`multiprocessing.sharedctypes.Array`, +:func:`multiprocessing.sharedctypes.RawArray`, +:func:`multiprocessing.sharedctypes.Value` and +:func:`multiprocessing.sharedctypes.RawValue` when an invalid typecode is +passed. Patch by Tomas Roun + +.. + +.. date: 2025-05-01-10-56-44 +.. gh-issue: 132813 +.. nonce: rKurvp +.. section: Library + +Improve error messages for incorrect types and values of +:class:`csv.Dialect` attributes. + +.. + +.. date: 2025-04-30-19-32-18 +.. gh-issue: 132969 +.. nonce: EagQ3G +.. section: Library + +Prevent the :class:`~concurrent.futures.ProcessPoolExecutor` executor +thread, which remains running when :meth:`shutdown(wait=False) +<concurrent.futures.Executor.shutdown>`, from attempting to adjust the +pool's worker processes after the object state has already been reset during +shutdown. A combination of conditions, including a worker process having +terminated abormally, resulted in an exception and a potential hang when the +still-running executor thread attempted to replace dead workers within the +pool. + +.. + +.. date: 2025-04-29-11-48-46 +.. gh-issue: 132876 +.. nonce: lyTQGZ +.. section: Library + +``ldexp()`` on Windows doesn't round subnormal results before Windows 11, +but should. Python's :func:`math.ldexp` wrapper now does round them, so +results may change slightly, in rare cases of very small results, on Windows +versions before 11. + +.. + +.. date: 2025-04-26-15-50-12 +.. gh-issue: 133009 +.. nonce: etBuz5 +.. section: Library + +:mod:`xml.etree.ElementTree`: Fix a crash in :meth:`Element.__deepcopy__ +<object.__deepcopy__>` when the element is concurrently mutated. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-04-25-16-06-53 +.. gh-issue: 132908 +.. nonce: wV5rja +.. section: Library + +Add :func:`math.isnormal` and :func:`math.issubnormal` functions. Patch by +Sergey B Kirpichev. + +.. + +.. date: 2025-04-25-11-53-37 +.. gh-issue: 95380 +.. nonce: 7dvPe- +.. section: Library + +:func:`fcntl.fcntl` and :func:`fcntl.ioctl`: Remove the 1024 bytes limit on +the size of not mutated bytes-like argument. + +.. + +.. date: 2025-04-25-11-48-00 +.. gh-issue: 122781 +.. nonce: ajsdns +.. section: Library + +Fix ``%z`` directive in :func:`datetime.datetime.strptime` to allow for no +provided offset as was documented. + +.. + +.. date: 2025-04-22-21-00-23 +.. gh-issue: 123471 +.. nonce: asOLA2 +.. section: Library + +Make concurrent iterations over :class:`itertools.combinations` and +:class:`itertools.product` safe under free-threading. + +.. + +.. date: 2025-04-21-01-05-14 +.. gh-issue: 127081 +.. nonce: Egrpq7 +.. section: Library + +Fix libc thread safety issues with :mod:`dbm` by performing stateful +operations in critical sections. + +.. + +.. date: 2025-04-21-01-03-15 +.. gh-issue: 127081 +.. nonce: WXRliX +.. section: Library + +Fix libc thread safety issues with :mod:`os` by replacing ``getlogin`` with +``getlogin_r`` re-entrant version. + +.. + +.. date: 2025-04-21-00-58-04 +.. gh-issue: 127081 +.. nonce: 3DCl92 +.. section: Library + +Fix libc thread safety issues with :mod:`pwd` by locking access to +``getpwall``. + +.. + +.. date: 2025-04-16-21-02-57 +.. gh-issue: 132551 +.. nonce: Psa7pL +.. section: Library + +Make :class:`io.BytesIO` safe in :term:`free-threaded <free threading>` +build. + +.. + +.. date: 2025-04-08-07-25-10 +.. gh-issue: 107583 +.. nonce: JGfbhq +.. section: Library + +Fix :class:`!Flag` inversion when flag set has missing values +(:class:`!IntFlag` still flips all bits); fix negative assigned values +during flag creation (both :class:`!Flag` and :class:`!IntFlag` ignore +missing values). + +.. + +.. date: 2025-04-07-10-20-16 +.. gh-issue: 87790 +.. nonce: X2SjJe +.. section: Library + +Support underscore and comma as thousands separators in the fractional part +for :class:`~fractions.Fraction`'s formatting. Patch by Sergey B Kirpichev. + +.. + +.. date: 2025-04-07-09-53-54 +.. gh-issue: 87790 +.. nonce: 6nj3zQ +.. section: Library + +Support underscore and comma as thousands separators in the fractional part +for :class:`~decimal.Decimal`'s formatting. Patch by Sergey B Kirpichev. + +.. + +.. date: 2025-04-07-06-41-54 +.. gh-issue: 131884 +.. nonce: ym9BJN +.. section: Library + +Fix formatting issues in :func:`json.dump` when both *indent* and *skipkeys* +are used. + +.. + +.. date: 2025-03-27-08-13-32 +.. gh-issue: 131788 +.. nonce: 0RWiFc +.. section: Library + +Make ``ResourceTracker.send`` from :mod:`multiprocessing` re-entrant safe + +.. + +.. date: 2025-03-19-12-41-42 +.. gh-issue: 91349 +.. nonce: 8eTOCP +.. section: Library + +Adjust default ``compressionlevel=`` to 6 (down from 9) in :mod:`gzip` and +:mod:`tarfile`. It is the default level used by most compression tools and a +better tradeoff between speed and performance. + +.. + +.. date: 2025-03-17-21-21-06 +.. gh-issue: 131146 +.. nonce: A5Obgv +.. section: Library + +Fix :class:`calendar.TextCalendar`, :class:`calendar.HTMLCalendar`, and the +:mod:`calendar` CLI to display month names in the nominative case by adding +:data:`calendar.standalone_month_name` and +:data:`calendar.standalone_month_abbr`, which provide month names and +abbreviations in the grammatical form used when a month name stands by +itself, if the locale supports it. + +.. + +.. date: 2025-03-13-20-48-58 +.. gh-issue: 123471 +.. nonce: cM4w4f +.. section: Library + +Make concurrent iterations over :class:`itertools.cycle` safe under +free-threading. + +.. + +.. date: 2025-03-11-05-24-14 +.. gh-issue: 130664 +.. nonce: g0yNMm +.. section: Library + +Handle corner-case for :class:`~fractions.Fraction`'s formatting: treat +zero-padding (preceding the width field by a zero (``'0'``) character) as an +equivalent to a fill character of ``'0'`` with an alignment type of ``'='``, +just as in case of :class:`float`'s. + +.. + +.. date: 2025-03-09-03-13-41 +.. gh-issue: 130999 +.. nonce: tBRBVB +.. section: Library + +Avoid exiting the new REPL and offer suggestions even if there are +non-string candidates when errors occur. + +.. + +.. date: 2025-03-08-17-07-00 +.. gh-issue: 88473 +.. nonce: qg23g8 +.. section: Library + +Implement a fast path for :class:`datetime.date` objects in +:func:`datetime.date.today` which results in a 5x performance gain while +proper subclasses retain their previous performance. + +.. + +.. date: 2024-11-25-10-22-08 +.. gh-issue: 126883 +.. nonce: MAEF7g +.. section: Library + +Add check that timezone fields are in range for +:meth:`datetime.datetime.fromisoformat` and +:meth:`datetime.time.fromisoformat`. Patch by Semyon Moroz. + +.. + +.. date: 2024-10-28-06-54-22 +.. gh-issue: 125028 +.. nonce: GEY8Ws +.. section: Library + +:data:`functools.Placeholder` cannot be passed to :func:`functools.partial` +as a keyword argument. + +.. + +.. date: 2024-10-22-16-21-55 +.. gh-issue: 125843 +.. nonce: 2ttzYo +.. section: Library + +If possible, indicate which :mod:`curses` C function or macro is responsible +for raising a :exc:`curses.error` exception. Patch by Bénédikt Tran. + +.. + +.. date: 2024-10-17-01-12-22 +.. gh-issue: 119109 +.. nonce: u4hcvb +.. section: Library + +:func:`functools.partial` calls are now faster when keyword arguments are +used. + +.. + +.. date: 2024-09-13-09-48-25 +.. gh-issue: 124033 +.. nonce: WNudS0 +.. section: Library + +``SimplePath`` is now presented in ``importlib.metadata.__all__``. + +.. + +.. date: 2024-09-13-09-46-47 +.. gh-issue: 91216 +.. nonce: LuOsF4 +.. section: Library + +``importlib.metadata`` now raises a ``KeyError`` instead of returning +``None`` when a key is missing from the metadata. + +.. + +.. date: 2024-09-13-09-43-15 +.. gh-issue: 120492 +.. nonce: Mm6CJ6 +.. section: Library + +``importlib.metadata`` now prioritizes valid dists to invalid dists when +retrieving by name. + +.. + +.. date: 2024-07-16-00-01-04 +.. gh-issue: 99631 +.. nonce: GWD4fD +.. section: Library + +The :mod:`shelve` module now accepts custom serialization and +deserialization functions. + +.. + +.. date: 2024-07-06-14-32-30 +.. gh-issue: 119186 +.. nonce: E5B1HQ +.. section: Library + +Slightly speed up :func:`os.walk` by calling :func:`os.path.join` less +often. + +.. + +.. date: 2024-06-06-17-49-07 +.. gh-issue: 120170 +.. nonce: DUxhmT +.. section: Library + +Fix an issue in the :mod:`!_pickle` extension module in which importing +:mod:`multiprocessing` could change how pickle identifies which module an +object belongs to, potentially breaking the unpickling of those objects. + +.. + +.. date: 2024-05-13-09-50-31 +.. gh-issue: 118981 +.. nonce: zgOQPv +.. section: Library + +Fix potential hang in ``multiprocessing.popen_spawn_posix`` that can happen +when the child proc dies early by closing the child fds right away. + +.. + +.. date: 2023-07-05-14-34-10 +.. gh-issue: 105497 +.. nonce: HU5u89 +.. section: Library + +Fix flag mask inversion when unnamed flags exist. + +.. + +.. date: 2023-03-13-22-51-40 +.. gh-issue: 99813 +.. nonce: 40TV02 +.. section: Library + +:mod:`ssl` now uses ``SSL_sendfile`` internally when it is possible (see +:data:`~ssl.OP_ENABLE_KTLS`). The function sends a file more efficiently +because it performs TLS encryption in the kernel to avoid additional context +switches. Patch by Illia Volochii. + +.. + +.. date: 2023-02-13-21-56-38 +.. gh-issue: 62824 +.. nonce: CBZzX3 +.. section: Library + +Fix aliases for ``iso8859_8`` encoding. Patch by Dave Goncalves. + +.. + +.. date: 2023-02-13-21-41-34 +.. gh-issue: 86155 +.. nonce: ppIGSC +.. section: Library + +:meth:`html.parser.HTMLParser.close` no longer loses data when the +``<script>`` tag is not closed. Patch by Waylan Limberg. + +.. + +.. date: 2023-02-13-20-34-52 +.. gh-issue: 78319 +.. nonce: V1zzed +.. section: Library + +UTF8 support for the IMAP APPEND command has been made RFC compliant. + +.. + +.. date: 2022-10-08-14-56-07 +.. gh-issue: 93334 +.. nonce: 0KUm8d +.. section: Library + +Reraise :exc:`KeyError` as :exc:`ModuleNotFoundError` when +:meth:`importlib.machinery.PathFinder.find_spec` is called on a submodule +without importing the parent (and without a ``path`` argument). + +.. + +.. date: 2022-07-24-20-56-32 +.. gh-issue: 69426 +.. nonce: unccw7 +.. section: Library + +Fix :class:`html.parser.HTMLParser` to not unescape character entities in +attribute values if they are followed by an ASCII alphanumeric or an equals +sign. + +.. + +.. bpo: 38735 +.. date: 2022-01-07-16-56-57 +.. nonce: NFfJX6 +.. section: Library + +Fix failure when importing a module from the root directory on unix-like +platforms with sys.pycache_prefix set. + +.. + +.. bpo: 45959 +.. date: 2021-12-18-12-46-20 +.. nonce: vPlr3P +.. section: Library + +:mod:`pprint` can now pretty-print dict views. + +.. + +.. date: 2021-09-21-17-17-29 +.. gh-issue: 84683 +.. nonce: wDSRsG +.. section: Library + +:mod:`zoneinfo`: Check in ``<prefix>/share/zoneinfo`` for data files on +Windows + +.. + +.. bpo: 43429 +.. date: 2021-03-07-16-31-36 +.. nonce: Koa0mf +.. section: Library + +The :meth:`~mmap.mmap.size` method of the :class:`mmap.mmap` class now +returns the size of an anonymous mapping on both Unix and Windows. +Previously, the size would be returned on Windows and an :exc:`OSError` +would be raised on Unix. :exc:`ValueError` is now raised instead of +:exc:`OSError` when ``trackfd=False``. + +.. + +.. bpo: 41839 +.. date: 2020-09-23-11-54-17 +.. nonce: kU5Ywl +.. section: Library + +Allow negative priority values from :func:`os.sched_get_priority_min` and +:func:`os.sched_get_priority_max` functions. + +.. + +.. bpo: 28494 +.. date: 2017-12-30-18-21-00 +.. nonce: Dt_Wks +.. section: Library + +Improve Zip file validation false positive rate in +:func:`zipfile.is_zipfile`. + +.. + +.. date: 2025-10-09-12-53-47 +.. gh-issue: 96491 +.. nonce: 4YKxvy +.. section: IDLE + +Deduplicate version number in IDLE shell title bar after saving to a file. + +.. + +.. date: 2025-10-08-08-35-50 +.. gh-issue: 139742 +.. nonce: B3fZLg +.. section: IDLE + +Colorize t-string prefixes for template strings in IDLE, as done for +f-string prefixes. + +.. + +.. date: 2025-07-01-23-00-58 +.. gh-issue: 136155 +.. nonce: 4siQQO +.. section: Documentation + +We are now checking for fatal errors in EPUB builds in CI. + +.. + +.. date: 2025-06-10-17-02-06 +.. gh-issue: 135171 +.. nonce: quHvts +.. section: Documentation + +Document that the :term:`iterator` for the leftmost :keyword:`!for` clause +in the generator expression is created immediately. + +.. + +.. bpo: 45210 +.. date: 2021-09-15-13-07-25 +.. nonce: RtGk7i +.. section: Documentation + +Document that error indicator may be set in tp_dealloc, and how to avoid +clobbering it. + +.. + +.. date: 2025-10-13-17-56-23 +.. gh-issue: 140000 +.. nonce: tLhn3e +.. section: Core and Builtins + +Fix potential memory leak when a reference cycle exists between an instance +of :class:`typing.TypeAliasType`, :class:`typing.TypeVar`, +:class:`typing.ParamSpec`, or :class:`typing.TypeVarTuple` and its +``__name__`` attribute. Patch by Mikhail Efimov. + +.. + +.. date: 2025-10-12-18-54-06 +.. gh-issue: 140009 +.. nonce: -MbFh_ +.. section: Core and Builtins + +Improve performance of list extension by dictionary items. + +.. + +.. date: 2025-10-12-11-00-06 +.. gh-issue: 139988 +.. nonce: 4wi51t +.. section: Core and Builtins + +Fix a memory leak when failing to create a :class:`~typing.Union` type. +Patch by Bénédikt Tran. + +.. + +.. date: 2025-10-08-13-52-00 +.. gh-issue: 139748 +.. nonce: jq0yFJ +.. section: Core and Builtins + +Fix reference leaks in error branches of functions accepting path strings or +bytes such as :func:`compile` and :func:`os.system`. Patch by Bénédikt Tran. + +.. + +.. date: 2025-10-06-13-15-26 +.. gh-issue: 139516 +.. nonce: d9Pkur +.. section: Core and Builtins + +Fix lambda colon erroneously start format spec in f-string in tokenizer. + +.. + +.. date: 2025-10-01-18-21-19 +.. gh-issue: 63161 +.. nonce: ef1S6N +.. section: Core and Builtins + +Support non-UTF-8 shebang and comments in Python source files if non-UTF-8 +encoding is specified. Detect decoding error in comments for default (UTF-8) +encoding. Show the line and position of decoding error for default encoding +in a traceback. Show the line containing the coding cookie when it conflicts +with the BOM in a traceback. + +.. + +.. date: 2025-09-30-14-57-19 +.. gh-issue: 139116 +.. nonce: nlVf40 +.. section: Core and Builtins + +Prevent a deadlock when multiple threads start, stop and use +:mod:`tracemalloc` simultaneously. + +.. + +.. date: 2025-09-24-17-32-52 +.. gh-issue: 139275 +.. nonce: novrqf +.. section: Core and Builtins + +Fix compilation problems in ``_remote_debugging_module.c`` when the system +doesn't have ``process_vm_readv``. Patch by Pablo Galindo + +.. + +.. date: 2025-09-24-17-08-42 +.. gh-issue: 133059 +.. nonce: EXvxb7 +.. section: Core and Builtins + +Increased the number of cached small positive integers from 256 to 1024. + +.. + +.. date: 2025-09-22-15-21-49 +.. gh-issue: 74857 +.. nonce: 5XRQaA +.. section: Core and Builtins + +:pep:`538`: Coerce the POSIX locale to a UTF-8 based locale. Patch by Victor +Stinner. + +.. + +.. date: 2025-09-21-14-33-17 +.. gh-issue: 116738 +.. nonce: vNaI4h +.. section: Library + +Make :mod:`mmap` thread-safe on the :term:`free threaded <free threading>` +build. + +.. + +.. date: 2025-09-17-17-17-21 +.. gh-issue: 138558 +.. nonce: 0VbzCH +.. section: Core and Builtins + +Fix handling of unusual t-string annotations in annotationlib. Patch by Dave +Peck. + +.. + +.. date: 2025-09-15-14-04-56 +.. gh-issue: 134466 +.. nonce: yR4fYW +.. section: Core and Builtins + +Don't run PyREPL in a degraded environment where setting termios attributes +is not allowed. + +.. + +.. date: 2025-09-11-15-56-18 +.. gh-issue: 138794 +.. nonce: nrOn1K +.. section: Core and Builtins + +When a new tracing function is registered with +:c:func:`PyRefTracer_SetTracer`, replacing the current a call to the trace +function will be made with the object set to **NULL** and **event** set to +:c:data:`PyRefTracer_TRACKER_REMOVED`. This will happen just before the new +function is registered. Patch by Pablo Galindo + +.. + +.. date: 2025-09-10-14-53-59 +.. gh-issue: 71810 +.. nonce: ppf0J- +.. section: Core and Builtins + +Raise :exc:`OverflowError` for ``(-1).to_bytes()`` for signed conversions +when bytes count is zero. Patch by Sergey B Kirpichev. + +.. + +.. date: 2025-09-09-23-59-13 +.. gh-issue: 138716 +.. nonce: UawDY0 +.. section: Core and Builtins + +Improve :exc:`SyntaxError` message for :keyword:`assert` in cases like +``assert a := b``. + +.. + +.. date: 2025-09-06-13-53-33 +.. gh-issue: 105487 +.. nonce: a43YaY +.. section: Core and Builtins + +Remove non-existent :meth:`~object.__copy__`, :meth:`~object.__deepcopy__`, +and :attr:`~type.__bases__` from the :meth:`~object.__dir__` entries of +:class:`types.GenericAlias`. + +.. + +.. date: 2025-09-05-01-19-04 +.. gh-issue: 138192 +.. nonce: erluq5 +.. section: Core and Builtins + +Fix :mod:`contextvars` initialization so that all subinterpreters are +assigned the :attr:`~contextvars.Token.MISSING` value. + +.. + +.. date: 2025-09-03-17-00-30 +.. gh-issue: 138479 +.. nonce: qUxgWs +.. section: Core and Builtins + +Fix a crash when a generic object's ``__typing_subst__`` returns an object +that isn't a :class:`tuple`. + +.. + +.. date: 2025-09-03-15-35-34 +.. gh-issue: 138431 +.. nonce: EUsrtA +.. section: Core and Builtins + +Fix a bug in the JIT optimizer when round-tripping strings and tuples. + +.. + +.. date: 2025-09-03-10-16-09 +.. gh-issue: 138378 +.. nonce: r6BQxV +.. section: Core and Builtins + +Move the globals-to-const JIT optimizer pass into to the main JIT optimizer +pass + +.. + +.. date: 2025-09-02-22-17-55 +.. gh-issue: 138401 +.. nonce: uTRvue +.. section: Library + +Add missing validation of argument ``count`` in :func:`os.sendfile` to be +non-negative. + +.. + +.. date: 2025-09-02-09-10-06 +.. gh-issue: 138372 +.. nonce: h1Xk4- +.. section: Core and Builtins + +Fix :exc:`SyntaxWarning` emitted for erroneous subscript expressions +involving :ref:`template string literals <t-strings>`. Patch by Brian +Schubert. + +.. + +.. date: 2025-09-01-21-52-54 +.. gh-issue: 138302 +.. nonce: -ez47B +.. section: Core and Builtins + +``BINARY_OP`` now specializes to ``BINARY_OP_ADD_INT``, +``BINARY_OP_SUBTRACT_INT`` or ``BINARY_OP_MULTIPLY_INT`` if operands are +compact ints. + +.. + +.. date: 2025-09-01-16-09-02 +.. gh-issue: 138318 +.. nonce: t-WEN5 +.. section: Core and Builtins + +The default REPL now avoids highlighting built-in names (for instance +:class:`set` or :func:`format`) when they are used as attribute names (for +instance in ``value.set`` or ``text.format``). + +.. + +.. date: 2025-09-01-13-54-43 +.. gh-issue: 138349 +.. nonce: 0fGmAi +.. section: Core and Builtins + +Fix crash in certain cases where a module contains both a module-level +annotation and a comprehension. + +.. + +.. date: 2025-08-30-17-15-05 +.. gh-issue: 69605 +.. nonce: KjBk99 +.. section: Core and Builtins + +Fix some standard library submodules missing from the :term:`REPL` +auto-completion of imports. + +.. + +.. date: 2025-08-30-00-55-35 +.. gh-issue: 61206 +.. nonce: HeFLvl +.. section: Core and Builtins + +:mod:`zipimport` now supports zstandard compressed zip file entries. + +.. + +.. date: 2025-08-28-09-29-46 +.. gh-issue: 116738 +.. nonce: yLZJpV +.. section: Library + +Make :mod:`!cProfile` thread-safe on the :term:`free threaded <free +threading>` build. + +.. + +.. date: 2025-08-27-17-51-38 +.. gh-issue: 137838 +.. nonce: lK6T0j +.. section: Core and Builtins + +Fix JIT trace buffer overrun by increasing possible exit stubs. Patch by +Donghee Na. + +.. + +.. date: 2025-08-27-13-11-47 +.. gh-issue: 71679 +.. nonce: V0yFeT +.. section: Core and Builtins + +Use the same quoting algorithm for the repr of bytearrays as for bytes +objects and strings -- use double quotes for quoting if the bytearray +contains single quotes and does not contain double quotes. + +.. + +.. date: 2025-08-22-11-39-40 +.. gh-issue: 137384 +.. nonce: j4b_in +.. section: Core and Builtins + +Fix a crash when using the :mod:`warnings` module in a finalizer at +shutdown. Patch by Kumar Aditya. + +.. + +.. date: 2025-08-21-06-31-42 +.. gh-issue: 138004 +.. nonce: FH2Hre +.. section: Library + +On Solaris/Illumos platforms, thread names are now encoded as ASCII to avoid +errors on systems (e.g. OpenIndiana) that don't support non-ASCII names. + +.. + +.. date: 2025-08-21-01-46-39 +.. gh-issue: 137976 +.. nonce: p4sb4x +.. section: Library + +Removed ``localtime`` from the list of reported system timezones. + +.. + +.. date: 2025-08-20-14-17-47 +.. gh-issue: 137992 +.. nonce: fcL3SK +.. section: Core and Builtins + +Ensure that :c:func:`PyRefTracer_SetTracer` sync with all existing threads +when called to avoid races in the free threaded build. Patch by Pablo +Galindo + +.. + +.. date: 2025-08-19-18-52-22 +.. gh-issue: 137967 +.. nonce: uw67Ys +.. section: Core and Builtins + +Show error suggestions on nested attribute access. Patch by Pablo Galindo + +.. + +.. date: 2025-08-19-16-07-07 +.. gh-issue: 137959 +.. nonce: EWj0RZ +.. section: Core and Builtins + +Replace the shim code added to every piece of jitted code with a single +trampoline function. + +.. + +.. date: 2025-08-17-13-36-53 +.. gh-issue: 137883 +.. nonce: 55VDCN +.. section: Core and Builtins + +Fix runaway recursion when calling a function with keyword arguments. + +.. + +.. date: 2025-08-15-15-45-26 +.. gh-issue: 137079 +.. nonce: YEow69 +.. section: Core and Builtins + +Fix keyword typo recognition when parsing files. Patch by Pablo Galindo. + +.. + +.. date: 2025-08-14-14-18-29 +.. gh-issue: 137728 +.. nonce: HdYS9R +.. section: Core and Builtins + +Fix the JIT's handling of many local variables. This previously caused a +segfault. + +.. + +.. date: 2025-08-13-16-58-58 +.. gh-issue: 137716 +.. nonce: ZcZSyi +.. section: Core and Builtins + +Fix double period in :exc:`AttributeError` message for invalid mock +assertions + +.. + +.. date: 2025-08-13-13-39-02 +.. gh-issue: 137433 +.. nonce: g6Atfz +.. section: Core and Builtins + +Fix a potential deadlock in the :term:`free threading` build when daemon +threads enable or disable profiling or tracing while the main thread is +shutting down the interpreter. + +.. + +.. date: 2025-08-10-21-34-12 +.. gh-issue: 137576 +.. nonce: 0ZicS- +.. section: Core and Builtins + +Fix for incorrect source code being shown in tracebacks from the Basic REPL +when :envvar:`PYTHONSTARTUP` is given. Patch by Adam Hartz. + +.. + +.. date: 2025-08-09-11-38-37 +.. gh-issue: 37817 +.. nonce: Y5Fhde +.. section: Core and Builtins + +Allow assignment to :attr:`~type.__bases__` of direct subclasses of builtin +classes. + +.. + +.. date: 2025-08-09-04-07-05 +.. gh-issue: 132732 +.. nonce: 8BiIVJ +.. section: Core and Builtins + +Optimize ``_COMPARE_OP``, ``_CONTAINS_OP``, ``_UNARY_NEGATIVE``, +``_UNARY_NOT``, and ``_UNARY_INVERT`` in JIT builds with constant-loading +uops (``_POP_TWO_LOAD_CONST_INLINE_BORROW`` and +``_POP_TOP_LOAD_CONST_INLINE_BORROW``), and then remove both to reduce +instruction count. + +.. + +.. date: 2025-08-07-09-52-19 +.. gh-issue: 137400 +.. nonce: AK1dy- +.. section: Core and Builtins + +Fix a crash in the :term:`free threading` build when disabling profiling or +tracing across all threads with :c:func:`PyEval_SetProfileAllThreads` or +:c:func:`PyEval_SetTraceAllThreads` or their Python equivalents +:func:`threading.settrace_all_threads` and +:func:`threading.setprofile_all_threads`. + +.. + +.. date: 2025-08-06-16-55-44 +.. gh-issue: 133143 +.. nonce: l7CI9v +.. section: Core and Builtins + +Add :data:`sys.abi_info` object to make ABI information more easily +accessible. + +.. + +.. date: 2025-08-06-15-39-54 +.. gh-issue: 137400 +.. nonce: xIw0zs +.. section: Core and Builtins + +Fix a crash in the :term:`free threading` build when disabling profiling or +tracing across all threads with :c:func:`PyEval_SetProfileAllThreads` or +:c:func:`PyEval_SetTraceAllThreads` or their Python equivalents +:func:`threading.settrace_all_threads` and +:func:`threading.setprofile_all_threads`. + +.. + +.. date: 2025-08-05-20-24-12 +.. gh-issue: 120037 +.. nonce: MB7MmI +.. section: Core and Builtins + +Disable user site packages directory when a ``._pth`` file is used, even if +it contains ``import site``. + +.. + +.. date: 2025-08-05-17-22-24 +.. gh-issue: 58124 +.. nonce: q1__53 +.. section: Core and Builtins + +Fix name of the Python encoding in Unicode errors of the code page codec: +use "cp65000" and "cp65001" instead of "CP_UTF7" and "CP_UTF8" which are not +valid Python code names. Patch by Victor Stinner. + +.. + +.. date: 2025-08-05-10-22-15 +.. gh-issue: 136966 +.. nonce: J5lrE0 +.. section: Core and Builtins + +The :attr:`object.__dict__` and :attr:`!__weakref__` descriptors now use a +single descriptor instance per interpreter, shared across all types that +need them. This speeds up class creation, and helps avoid reference cycles. + +.. + +.. date: 2025-08-02-23-04-57 +.. gh-issue: 137314 +.. nonce: wjEdzD +.. section: Core and Builtins + +Fixed a regression where raw f-strings incorrectly interpreted escape +sequences in format specifications. Raw f-strings now properly preserve +literal backslashes in format specs, matching the behavior from Python 3.11. +For example, ``rf"{obj:\xFF}"`` now correctly produces ``'\\xFF'`` instead +of ``'ÿ'``. Patch by Pablo Galindo. + +.. + +.. date: 2025-08-02-10-27-53 +.. gh-issue: 137308 +.. nonce: at05p_ +.. section: Core and Builtins + +A standalone docstring in a node body is optimized as a :keyword:`pass` +statement to ensure that the node's body is never empty. There was a +:exc:`ValueError` in :func:`compile` otherwise. + +.. + +.. date: 2025-08-01-18-54-31 +.. gh-issue: 137288 +.. nonce: FhE7ku +.. section: Core and Builtins + +Fix bug where some bytecode instructions of a boolean expression are not +associated with the correct exception handler. + +.. + +.. date: 2025-07-31-23-02-02 +.. gh-issue: 137291 +.. nonce: kIxVZd +.. section: Core and Builtins + +The perf profiler can now be used if a previous frame evaluation API has +been provided. + +.. + +.. date: 2025-07-28-19-11-34 +.. gh-issue: 134291 +.. nonce: IiB9Id +.. section: Core and Builtins + +Remove some newer macOS API usage from the JIT compiler in order to restore +compatibility with older OSX 10.15 deployment targets. + +.. + +.. date: 2025-07-28-17-01-05 +.. gh-issue: 88886 +.. nonce: g4XFPb +.. section: Core and Builtins + +The codecs lookup function now again performs only minimal normalization of +the encoding name before passing it to the search functions: all ASCII +letters are converted to lower case, spaces are replaced with hyphens. This +restores the pre-Python 3.9 behavior. + +.. + +.. date: 2025-07-25-22-31-52 +.. gh-issue: 131338 +.. nonce: zJDCMp +.. section: Core and Builtins + +Disable computed stack limit checks on non-glibc linux platforms to fix +crashes on deep recursion. + +.. + +.. date: 2025-07-24-17-30-58 +.. gh-issue: 136870 +.. nonce: ncx82J +.. section: Core and Builtins + +Fix data races while de-instrumenting bytecode of code objects running +concurrently in threads. + +.. + +.. date: 2025-07-24-02-13-59 +.. gh-issue: 132732 +.. nonce: p77xkb +.. section: Core and Builtins + +Optimize constant comparison for ``_COMPARE_OP_INT``, ``_COMPARE_OP_FLOAT`` +and ``_COMPARE_OP_STR`` in JIT builds + +.. + +.. date: 2025-07-19-17-08-09 +.. gh-issue: 127598 +.. nonce: Mx8S-y +.. section: Core and Builtins + +Improve :exc:`ModuleNotFoundError` by adding flavour text to the exception +when the :option:`-S` option is passed. Patch by Andrea Mattei. + +.. + +.. date: 2025-07-19-12-37-05 +.. gh-issue: 136801 +.. nonce: XU_tF2 +.. section: Core and Builtins + +Fix PyREPL syntax highlighting on match cases after multi-line case. +Contributed by Olga Matoula. + +.. + +.. date: 2025-07-19-10-35-31 +.. gh-issue: 74185 +.. nonce: 7hPCA5 +.. section: Core and Builtins + +The :meth:`~object.__repr__` of :class:`ImportError` and +:class:`ModuleNotFoundError` now shows "name" and "path" as ``name=<name>`` +and ``path=<path>`` if they were given as keyword arguments at construction +time. Patch by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir + +.. + +.. date: 2025-07-18-08-43-35 +.. gh-issue: 116738 +.. nonce: i0HWtP +.. section: Library + +Make functions in :mod:`syslog` thread-safe on the :term:`free threaded +<free threading>` build. + +.. + +.. date: 2025-07-15-10-03-57 +.. gh-issue: 116738 +.. nonce: oFttKl +.. section: Library + +Make functions in :mod:`pwd` thread-safe on the :term:`free threaded <free +threading>` build. + +.. + +.. date: 2025-07-14-17-01-23 +.. gh-issue: 136616 +.. nonce: FQjXE_ +.. section: Core and Builtins + +Improve :exc:`SyntaxError` error messages for invalid :keyword:`assert` +usages. + +.. + +.. date: 2025-07-13-21-21-17 +.. gh-issue: 136599 +.. nonce: sLhm2O +.. section: Core and Builtins + +Improve performance of :class:`int` hash calculations. + +.. + +.. date: 2025-07-12-09-59-14 +.. gh-issue: 136421 +.. nonce: ZD1rNj +.. section: Library + +Fix crash when initializing :mod:`datetime` concurrently. + +.. + +.. date: 2025-07-11-13-45-48 +.. gh-issue: 136541 +.. nonce: uZ_-Ju +.. section: Core and Builtins + +Fix some issues with the perf trampolines on x86-64 and aarch64. The +trampolines were not being generated correctly for some cases, which could +lead to the perf integration not working correctly. Patch by Pablo Galindo. + +.. + +.. date: 2025-07-11-12-29-09 +.. gh-issue: 107545 +.. nonce: ipfl7U +.. section: Library + +Improve the error messages that may be raised by +:meth:`~socket.socket.setsockopt`. + +.. + +.. date: 2025-07-10-23-23-50 +.. gh-issue: 136517 +.. nonce: _NHJyv +.. section: Core and Builtins + +Fixed a typo that prevented printing of uncollectable objects when the +:const:`gc.DEBUG_UNCOLLECTABLE` mode was set. + +.. + +.. date: 2025-07-10-15-53-16 +.. gh-issue: 136525 +.. nonce: xAko0e +.. section: Core and Builtins + +Fix issue where per-thread bytecode was not instrumented for newly created +threads. + +.. + +.. date: 2025-07-09-21-27-14 +.. gh-issue: 132657 +.. nonce: kSA8R3 +.. section: Core and Builtins + +Improve performance of :class:`frozenset` by removing locks in the +free-threading build. + +.. + +.. date: 2025-07-09-11-15-42 +.. gh-issue: 136459 +.. nonce: m4Udh8 +.. section: Core and Builtins + +Add support for perf trampoline on macOS, to allow profilers wit JIT map +support to read Python calls. While profiling, ``PYTHONPERFSUPPORT=1`` can +be appended to enable the trampoline. + +.. + +.. date: 2025-07-08-23-53-51 +.. gh-issue: 132661 +.. nonce: B84iYt +.. section: Core and Builtins + +``Interpolation.expression`` now has a default, the empty string. + +.. + +.. date: 2025-07-08-23-22-08 +.. gh-issue: 132661 +.. nonce: 34ftJl +.. section: Core and Builtins + +Reflect recent :pep:`750` change. + +Disallow concatenation of ``string.templatelib.Template`` and :class:`str`. +Also, disallow implicit concatenation of t-string literals with string or +f-string literals. + +.. + +.. date: 2025-07-07-17-26-06 +.. gh-issue: 91636 +.. nonce: GyHU72 +.. section: Core and Builtins + +While performing garbage collection, clear weakrefs to unreachable objects +that are created during running of finalizers. If those weakrefs were are +not cleared, they could reveal unreachable objects. + +.. + +.. date: 2025-07-07-12-24-00 +.. gh-issue: 136355 +.. nonce: MTcA8j +.. section: Core and Builtins + +Deprecate :option:`-b` and :option:`!-bb` command line options and schedule +them to become no-op in Python 3.17. + +.. + +.. date: 2025-07-06-14-53-19 +.. gh-issue: 109700 +.. nonce: KVNQQi +.. section: Core and Builtins + +Fix memory error handling in :c:func:`PyDict_SetDefault`. + +.. + +.. date: 2025-07-03-06-04-42 +.. gh-issue: 135552 +.. nonce: CbBQof +.. section: Core and Builtins + +Fix a bug caused by the garbage collector clearing weakrefs too early. The +weakrefs in the ``tp_subclasses`` dictionary are needed in order to +correctly invalidate type caches (for example, by calling +``PyType_Modified()``). Clearing weakrefs before calling finalizers causes +the caches to not be correctly invalidated. That can cause crashes since +the caches can refer to invalid objects. Defer the clearing of weakrefs +without callbacks until after finalizers are executed. + +.. + +.. date: 2025-07-02-15-18-41 +.. gh-issue: 136203 +.. nonce: Y934sC +.. section: Core and Builtins + +Improve :exc:`TypeError` error message, when richcomparing two +:class:`types.MappingProxyType` objects. + +.. + +.. date: 2025-06-26-18-44-34 +.. gh-issue: 136003 +.. nonce: sln51d +.. section: Core and Builtins + +Fix :class:`threading.Thread` objects becoming incorrectly daemon when +created from an :mod:`atexit` callback or a pending call +(:c:func:`Py_AddPendingCall`). + +.. + +.. date: 2025-06-26-15-25-51 +.. gh-issue: 78465 +.. nonce: MbDN8X +.. section: Core and Builtins + +Fix error message for ``cls.__new__(cls, ...)`` where ``cls`` is not +instantiable builtin or extension type (with ``tp_new`` set to ``NULL``). + +.. + +.. date: 2025-06-24-16-46-34 +.. gh-issue: 135904 +.. nonce: 78xfon +.. section: Core and Builtins + +Perform more aggressive control-flow optimizations on the machine code +templates emitted by the experimental JIT compiler. + +.. + +.. date: 2025-06-24-06-41-47 +.. gh-issue: 129958 +.. nonce: EaJuS0 +.. section: Core and Builtins + +Differentiate between t-strings and f-strings in syntax error for newlines +in format specifiers of single-quoted interpolated strings. + +.. + +.. date: 2025-06-23-18-08-32 +.. gh-issue: 135871 +.. nonce: 50C528 +.. section: Core and Builtins + +Non-blocking mutex lock attempts now return immediately when the lock is +busy instead of briefly spinning in the :term:`free threading` build. + +.. + +.. date: 2025-06-20-14-50-44 +.. gh-issue: 134584 +.. nonce: 3CJdAI +.. section: Core and Builtins + +Specialize :opcode:`POP_TOP` in the JIT compiler by specializing for +reference lifetime and type. This will also enable easier top of stack +caching in the JIT compiler. + +.. + +.. date: 2025-06-18-16-45-36 +.. gh-issue: 135106 +.. nonce: cpl6Aq +.. section: Core and Builtins + +Restrict the trashcan mechanism to GC'ed objects and untrack them while in +the trashcan to prevent the GC and trashcan mechanisms conflicting. + +.. + +.. date: 2025-06-18-12-19-13 +.. gh-issue: 135379 +.. nonce: TCvGpj +.. section: Core and Builtins + +Changes specialization of ``BINARY_OP`` for ints to only specialize for +"compact" ints. This streamlines the fast path at the cost of fewer +specializations when very large integers are used. + +.. + +.. date: 2025-06-17-22-34-58 +.. gh-issue: 135607 +.. nonce: ucsLVu +.. section: Core and Builtins + +Fix potential :mod:`weakref` races in an object's destructor on the +:term:`free threaded <free threading>` build. + +.. + +.. date: 2025-06-17-12-50-48 +.. gh-issue: 135608 +.. nonce: PnHckD +.. section: Core and Builtins + +Fix a crash in the JIT involving attributes of modules. + +.. + +.. date: 2025-06-17-08-37-45 +.. gh-issue: 82088 +.. nonce: TgPvLg +.. section: Core and Builtins + +Improve performance of ``PyLongObject`` conversion functions +``PyLong_AsLongAndOverflow()``, ``PyLong_AsSsize_t()``, +``PyLong_AsUnsignedLong()``, ``PyLong_AsSize_t()``, +``PyLong_AsUnsignedLongMask()``, ``PyLong_AsUnsignedLongLongMask()``, +``PyLong_AsLongLongAndOverflow()`` for integers larger than 2**30 up to 30%. + +.. + +.. date: 2025-06-16-03-56-15 +.. gh-issue: 135551 +.. nonce: hRTQO- +.. section: Core and Builtins + +Sorting randomly ordered lists will often run a bit faster, thanks to a new +scheme for picking minimum run lengths from Stefan Pochmann, which arranges +for the merge tree to be as evenly balanced as is possible. + +.. + +.. date: 2025-06-16-02-31-42 +.. gh-issue: 135543 +.. nonce: 6b0HOF +.. section: Core and Builtins + +Emit ``sys.remote_exec`` audit event when :func:`sys.remote_exec` is called +and migrate ``remote_debugger_script`` to +``cpython.remote_debugger_script``. + +.. + +.. date: 2025-06-14-01-01-14 +.. gh-issue: 135496 +.. nonce: ER0Me3 +.. section: Core and Builtins + +Fix typo in the f-string conversion type error ("exclamanation" -> +"exclamation"). + +.. + +.. date: 2025-06-13-16-05-24 +.. gh-issue: 135474 +.. nonce: 67nOl3 +.. section: Core and Builtins + +Specialize integer operations only on compact integers. This is a CPython +internal change. + +.. + +.. date: 2025-06-12-18-12-42 +.. gh-issue: 135371 +.. nonce: R_YUtR +.. section: Core and Builtins + +Fixed :mod:`asyncio` debugging tools to properly display internal coroutine +call stacks alongside external task dependencies. The ``python -m asyncio +ps`` and ``python -m asyncio pstree`` commands now show complete execution +context. Patch by Pablo Galindo. + +.. + +.. date: 2025-06-12-11-19-52 +.. gh-issue: 135422 +.. nonce: F6yQi6 +.. section: Core and Builtins + +Fix regression in :exc:`SyntaxError` messages after :gh:`134036`. + +.. + +.. date: 2025-06-12-00-03-34 +.. gh-issue: 116738 +.. nonce: iBBAdo +.. section: Library + +Make functions in :mod:`grp` thread-safe on the :term:`free threaded <free +threading>` build. + +.. + +.. date: 2025-06-11-15-08-10 +.. gh-issue: 127319 +.. nonce: OVGFSZ +.. section: Library + +Set the ``allow_reuse_port`` class variable to ``False`` on the XMLRPC, +logging, and HTTP servers. This matches the behavior in prior Python +releases, which is to not allow port reuse. + +.. + +.. date: 2025-06-09-23-57-37 +.. gh-issue: 130077 +.. nonce: MHknDB +.. section: Core and Builtins + +Properly raise custom syntax errors when incorrect syntax containing names +that are prefixes of soft keywords is encountered. Patch by Pablo Galindo. + +.. + +.. date: 2025-06-08-14-24-29 +.. gh-issue: 131798 +.. nonce: qfw91T +.. section: Core and Builtins + +Optimize _CALL_LEN in the JIT when the length is known. Patch by Tomas Roun + +.. + +.. date: 2025-06-06-19-17-22 +.. gh-issue: 131798 +.. nonce: XoV8Eb +.. section: Core and Builtins + +Optimize ``_UNARY_NEGATIVE`` in JIT-compiled code. + +.. + +.. date: 2025-06-06-02-24-42 +.. gh-issue: 135148 +.. nonce: r-t2sC +.. section: Core and Builtins + +Fixed a bug where f-string debug expressions (using =) would incorrectly +strip out parts of strings containing escaped quotes and # characters. Patch +by Pablo Galindo. + +.. + +.. date: 2025-06-06-01-09-44 +.. gh-issue: 131798 +.. nonce: 1SuxO9 +.. section: Core and Builtins + +Optimize ``_UNARY_INVERT`` in JIT-compiled code. + +.. + +.. date: 2025-06-05-21-58-30 +.. gh-issue: 131798 +.. nonce: nt5Ab7 +.. section: Core and Builtins + +Optimize away ``_CALL_TYPE_1`` in the JIT when the return type is known. +Patch by Tomas Roun + +.. + +.. date: 2025-06-03-21-06-22 +.. gh-issue: 133136 +.. nonce: Usnvri +.. section: Core and Builtins + +Limit excess memory usage in the :term:`free threading` build when a large +dictionary or list is resized and accessed by multiple threads. + +.. + +.. date: 2025-06-02-20-13-37 +.. gh-issue: 131798 +.. nonce: JQRFvR +.. section: Core and Builtins + +Optimize ``_CHECK_METHOD_VERSION`` into ``_CHECK_FUNCTION_VERSION_INLINE`` +in JIT-compiled code. + +.. + +.. date: 2025-06-02-13-57-40 +.. gh-issue: 116738 +.. nonce: ycJsL8 +.. section: Library + +Make methods in :mod:`heapq` thread-safe on the :term:`free threaded <free +threading>` build. + +.. + +.. date: 2025-05-31-19-24-54 +.. gh-issue: 134280 +.. nonce: NDVbzY +.. section: Core and Builtins + +Disable constant folding for ``~`` with a boolean argument. This moves the +deprecation warning from compile time to runtime. + +.. + +.. date: 2025-05-31-10-26-46 +.. gh-issue: 134876 +.. nonce: 8mBGJI +.. section: Core and Builtins + +Add support to :pep:`768` remote debugging for Linux kernels which don't +have CONFIG_CROSS_MEMORY_ATTACH configured. + +.. + +.. date: 2025-05-30-18-09-54 +.. gh-issue: 134889 +.. nonce: Ic9UM- +.. section: Core and Builtins + +Fix handling of a few opcodes that leave operands on the stack when +optimizing ``LOAD_FAST``. + +.. + +.. date: 2025-05-30-15-56-19 +.. gh-issue: 134908 +.. nonce: 3a7PxM +.. section: Library + +Fix crash when iterating over lines in a text file on the :term:`free +threaded <free threading>` build. + +.. + +.. date: 2025-05-28-23-58-50 +.. gh-issue: 117852 +.. nonce: BO9g7z +.. section: Core and Builtins + +Fix argument checking of :meth:`~agen.athrow`. + +.. + +.. date: 2025-05-27-20-29-00 +.. gh-issue: 132617 +.. nonce: EmUfQQ +.. section: Core and Builtins + +Fix :meth:`dict.update` modification check that could incorrectly raise a +"dict mutated during update" error when a different dictionary was modified +that happens to share the same underlying keys object. + +.. + +.. date: 2025-05-27-20-21-34 +.. gh-issue: 131798 +.. nonce: b32zkl +.. section: Core and Builtins + +Allow the JIT to remove unnecessary ``_ITER_CHECK_TUPLE`` ops. + +.. + +.. date: 2025-05-27-18-59-54 +.. gh-issue: 134679 +.. nonce: FWPBu6 +.. section: Core and Builtins + +Fix crash in the :term:`free threading` build's QSBR code that could occur +when changing an object's ``__dict__`` attribute. + +.. + +.. date: 2025-05-26-15-55-50 +.. gh-issue: 133912 +.. nonce: -xAguL +.. section: Core and Builtins + +Fix the C API function ``PyObject_GenericSetDict`` to handle extension +classes with inline values. + +.. + +.. date: 2025-05-25-19-32-15 +.. gh-issue: 131798 +.. nonce: f5h8aI +.. section: Core and Builtins + +Make the JIT optimizer understand that slicing a string/list/tuple returns +the same type. + +.. + +.. date: 2025-05-23-14-54-07 +.. gh-issue: 134584 +.. nonce: y-WDjf +.. section: Core and Builtins + +Add a reference count elimination pass to the JIT compiler. Patch by Ken +Jin. + +.. + +.. date: 2025-05-22-17-49-39 +.. gh-issue: 131798 +.. nonce: U6ZmFm +.. section: Core and Builtins + +Optimize ``_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW``. + +.. + +.. date: 2025-05-22-14-48-19 +.. gh-issue: 134381 +.. nonce: 2BXhth +.. section: Library + +Fix :exc:`RuntimeError` when using a not-started :class:`threading.Thread` +after calling :func:`os.fork` + +.. + +.. date: 2025-05-21-18-02-56 +.. gh-issue: 127960 +.. nonce: W3J_2X +.. section: Core and Builtins + +PyREPL interactive shell no longer starts with ``__package__`` and +``__file__`` global names set to ``_pyrepl`` package internals. Contributed +by Yuichiro Tachibana. + +.. + +.. date: 2025-05-21-15-14-32 +.. gh-issue: 130397 +.. nonce: aG6EON +.. section: Core and Builtins + +Remove special-casing for C stack depth limits for WASI. Due to +WebAssembly's built-in stack protection this does not pose a security +concern. + +.. + +.. date: 2025-05-21-13-57-26 +.. gh-issue: 131798 +.. nonce: QwS5Bb +.. section: Core and Builtins + +JIT: replace ``_LOAD_SMALL_INT`` with ``_LOAD_CONST_INLINE_BORROW`` + +.. + +.. date: 2025-05-20-23-32-11 +.. gh-issue: 131798 +.. nonce: G9ZQZw +.. section: Core and Builtins + +Improve the JIT's ability to optimize away cached class attribute and method +loads. + +.. + +.. date: 2025-05-20-14-41-50 +.. gh-issue: 128066 +.. nonce: qzzGfv +.. section: Core and Builtins + +Fixes an edge case where PyREPL improperly threw an error when Python is +invoked on a read only filesystem while trying to write history file +entries. + +.. + +.. date: 2025-05-20-13-58-18 +.. gh-issue: 131798 +.. nonce: hG8xBw +.. section: Core and Builtins + +Improve the JIT's ability to narrow unknown classes to constant values. + +.. + +.. date: 2025-05-19-20-52-53 +.. gh-issue: 134268 +.. nonce: HPKX1e +.. section: Core and Builtins + +Add ``_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW`` and use it to further +optimize ``CALL_ISINSTANCE``. + +.. + +.. date: 2025-05-19-15-15-58 +.. gh-issue: 131798 +.. nonce: PCP71j +.. section: Core and Builtins + +Split ``CALL_LIST_APPEND`` into several uops. Patch by Diego Russo. + +.. + +.. date: 2025-05-18-14-33-23 +.. gh-issue: 69605 +.. nonce: ZMO49F +.. section: Core and Builtins + +When auto-completing an import in the :term:`REPL`, finding no candidates +now issues no suggestion, rather than suggestions from the current +namespace. + +.. + +.. date: 2025-05-18-10-50-46 +.. gh-issue: 134170 +.. nonce: J0Hvmi +.. section: Core and Builtins + +Add colorization to :func:`sys.unraisablehook` by default. + +.. + +.. date: 2025-05-17-20-56-05 +.. gh-issue: 91153 +.. nonce: afgtG2 +.. section: Core and Builtins + +Fix a crash when a :class:`bytearray` is concurrently mutated during item +assignment. + +.. + +.. date: 2025-05-17-20-44-51 +.. gh-issue: 134158 +.. nonce: ewLNLp +.. section: Core and Builtins + +Fix coloring of double braces in f-strings and t-strings in the +:term:`REPL`. + +.. + +.. date: 2025-05-16-20-59-12 +.. gh-issue: 134119 +.. nonce: w8expI +.. section: Core and Builtins + +Fix crash when calling :func:`next` on an exhausted template string +iterator. Patch by Jelle Zijlstra. + +.. + +.. date: 2025-05-16-17-25-52 +.. gh-issue: 134100 +.. nonce: 5-FbLK +.. section: Core and Builtins + +Fix a use-after-free bug that occurs when an imported module isn't in +:data:`sys.modules` after its initial import. Patch by Nico-Posada. + +.. + +.. date: 2025-05-16-09-06-38 +.. gh-issue: 134036 +.. nonce: st2e-B +.. section: Core and Builtins + +Improve :exc:`SyntaxError` message when using invalid :keyword:`raise` +statements. + +.. + +.. date: 2025-05-15-11-38-16 +.. gh-issue: 133999 +.. nonce: uBZ8uS +.. section: Core and Builtins + +Fix :exc:`SyntaxError` regression in :keyword:`except` parsing after +:gh:`123440`. + +.. + +.. date: 2025-05-11-13-40-42 +.. gh-issue: 133886 +.. nonce: ryBAyo +.. section: Core and Builtins + +Fix :func:`sys.remote_exec` for non-ASCII paths in non-UTF-8 locales and +non-UTF-8 paths in UTF-8 locales. + +.. + +.. date: 2025-05-11-09-40-19 +.. gh-issue: 133400 +.. nonce: zkWla8 +.. section: Core and Builtins + +Fixed Ctrl+D (^D) behavior in _pyrepl module to match old pre-3.13 REPL +behavior. + +.. + +.. date: 2025-05-10-17-12-27 +.. gh-issue: 133703 +.. nonce: bVM-re +.. section: Core and Builtins + +Fix hashtable in dict can be bigger than intended in some situations. + +.. + +.. date: 2025-05-09-18-11-21 +.. gh-issue: 133778 +.. nonce: pWEV3t +.. section: Core and Builtins + +Fix bug where assigning to the :attr:`~type.__annotations__` attributes of +classes defined under ``from __future__ import annotations`` had no effect. + +.. + +.. date: 2025-05-08-22-19-10 +.. gh-issue: 133711 +.. nonce: e91wUy +.. section: Core and Builtins + +Implement :pep:`686`: Enable :ref:`Python UTF-8 Mode <utf8-mode>` by +default. Patch by Adam Turner. + +.. + +.. date: 2025-05-08-13-48-02 +.. gh-issue: 132762 +.. nonce: tKbygC +.. section: Core and Builtins + +:meth:`~dict.fromkeys` no longer loops forever when adding a small set of +keys to a large base dict. Patch by Angela Liss. + +.. + +.. date: 2025-05-07-23-26-53 +.. gh-issue: 133541 +.. nonce: bHIC55 +.. section: Core and Builtins + +Inconsistent indentation in user input crashed the new REPL when syntax +highlighting was active. This is now fixed. + +.. + +.. date: 2025-05-06-15-01-41 +.. gh-issue: 133516 +.. nonce: RqWVf2 +.. section: Core and Builtins + +Raise :exc:`ValueError` when constants ``True``, ``False`` or ``None`` are +used as an identifier after NFKC normalization. + +.. + +.. date: 2025-05-03-22-31-53 +.. gh-issue: 131798 +.. nonce: fQ0ato +.. section: Core and Builtins + +Allow the JIT to remove int guards after ``_GET_LEN`` by setting the return +type to int. + +.. + +.. date: 2025-05-03-13-36-01 +.. gh-issue: 131798 +.. nonce: U4_QEJ +.. section: Core and Builtins + +Split ``CALL_ISINSTANCE`` into several uops, allowing the JIT to remove some +of them. + +.. + +.. date: 2025-04-30-14-13-01 +.. gh-issue: 132554 +.. nonce: GqQaUp +.. section: Core and Builtins + +Change iteration to use "virtual iterators" for sequences. Instead of +creating an iterator, a tagged integer representing the next index is pushed +to the stack above the iterable. For non-sequence iterators, ``NULL`` is +pushed. + +.. + +.. date: 2025-04-28-18-59-11 +.. gh-issue: 130821 +.. nonce: B11LU1 +.. section: Core and Builtins + +Enhance wrong type error messages and make them more consistent. Patch by +Semyon Moroz. + +.. + +.. date: 2025-04-26-17-50-01 +.. gh-issue: 131798 +.. nonce: XiOgw5 +.. section: Core and Builtins + +Narrow the return type and constant-evaluate ``CALL_ISINSTANCE`` for a +subset of known values in the JIT. Patch by Tomas Roun + +.. + +.. date: 2025-04-19-17-16-46 +.. gh-issue: 132542 +.. nonce: 7T_TY_ +.. section: Core and Builtins + +Update :attr:`Thread.native_id <threading.Thread.native_id>` after +:manpage:`fork(2)` to ensure accuracy. Patch by Noam Cohen. + +.. + +.. date: 2025-04-19-16-22-47 +.. gh-issue: 132732 +.. nonce: jgqhlF +.. section: Library + +Automatically constant evaluate bytecode operations marked as pure in the +JIT optimizer. + +.. + +.. date: 2025-04-16-12-01-13 +.. gh-issue: 127971 +.. nonce: pMDOQ0 +.. section: Core and Builtins + +Fix off-by-one read beyond the end of a string in string search. + +.. + +.. date: 2025-04-10-01-52-42 +.. gh-issue: 132042 +.. nonce: fePwlj +.. section: Core and Builtins + +Improve class creation times by up to 12% by pre-computing type slots just +once. Patch by Sergey Miryanov. + +.. + +.. date: 2025-04-04-16-41-00 +.. gh-issue: 133379 +.. nonce: asdjhjdf +.. section: Core and Builtins + +Correct usage of *arguments* in error messages. + +.. + +.. date: 2025-03-14-13-08-20 +.. gh-issue: 127266 +.. nonce: _tyfBp +.. section: Core and Builtins + +In the free-threaded build, avoid data races caused by updating type slots +or type flags after the type was initially created. For those (typically +rare) cases, use the stop-the-world mechanism. Remove the use of atomics +when reading or writing type flags. The use of atomics is not sufficient to +avoid races (since flags are sometimes read without a lock and without +atomics) and are no longer required. + +.. + +.. date: 2025-02-22-01-23-23 +.. gh-issue: 130425 +.. nonce: x5SNQ8 +.. section: Core and Builtins + +Add ``"Did you mean: 'attr'?"`` suggestion when using ``del obj.attr`` if +``attr`` does not exist. + +.. + +.. date: 2025-01-08-12-52-47 +.. gh-issue: 128640 +.. nonce: 9nbh9z +.. section: Core and Builtins + +Fix a crash when using threads inside of a subinterpreter. + +.. + +.. date: 2024-06-04-20-26-21 +.. gh-issue: 116738 +.. nonce: q_hPYq +.. section: Library + +Make the module :mod:`json` safe to use under the free-threading build. + +.. + +.. date: 2024-05-24-07-02-47 +.. gh-issue: 119494 +.. nonce: x3KUMC +.. section: Core and Builtins + +Exception text when trying to delete attributes of types was clarified. + +.. + +.. date: 2025-10-10-20-59-07 +.. gh-issue: 139924 +.. nonce: ALByCb +.. section: C API + +Function watchers can now receive a PyFunction_PYFUNC_EVENT_MODIFY_QUALNAME +event when a watched functions qualname is changed. + +.. + +.. date: 2025-10-07-12-51-32 +.. gh-issue: 111489 +.. nonce: LCKKlg +.. section: C API + +Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array. +Patch by Victor Stinner. + +.. + +.. date: 2025-09-14-14-44-24 +.. gh-issue: 136355 +.. nonce: LCaYyC +.. section: C API + +Deprecate :c:member:`PyConfig.bytes_warning` field and schedule its removal +in 3.17. + +.. + +.. date: 2025-09-14-13-09-47 +.. gh-issue: 138886 +.. nonce: dlcTXL +.. section: C API + +Remove deprecated :c:func:`!PySys_ResetWarnOptions` C-API function. + +.. + +.. date: 2025-09-12-13-05-20 +.. gh-issue: 129813 +.. nonce: dJZpME +.. section: C API + +Implement :pep:`782`, the :c:type:`PyBytesWriter` API. Add functions: + +* :c:func:`PyBytesWriter_Create` +* :c:func:`PyBytesWriter_Discard` +* :c:func:`PyBytesWriter_FinishWithPointer` +* :c:func:`PyBytesWriter_FinishWithSize` +* :c:func:`PyBytesWriter_Finish` +* :c:func:`PyBytesWriter_Format` +* :c:func:`PyBytesWriter_GetData` +* :c:func:`PyBytesWriter_GetSize` +* :c:func:`PyBytesWriter_GrowAndUpdatePointer` +* :c:func:`PyBytesWriter_Grow` +* :c:func:`PyBytesWriter_Resize` +* :c:func:`PyBytesWriter_WriteBytes` + +Patch by Victor Stinner. + +.. + +.. date: 2025-08-19-15-31-36 +.. gh-issue: 137956 +.. nonce: P4TK1d +.. section: C API + +Display and raise an exception if an extension compiled for +non-free-threaded Python is loaded in a free-threaded interpreter. + +.. + +.. date: 2025-08-13-13-41-04 +.. gh-issue: 137573 +.. nonce: r6uwRf +.. section: C API + +Mark ``_PyOptimizer_Optimize`` as :c:macro:`Py_NO_INLINE` to prevent stack +overflow crashes on macOS. + +.. + +.. date: 2025-07-31-04-30-42 +.. gh-issue: 128813 +.. nonce: opL-Pv +.. section: C API + +Functions :c:func:`_Py_c_sum`, :c:func:`_Py_c_diff`, :c:func:`_Py_c_neg`, +:c:func:`_Py_c_prod`, :c:func:`_Py_c_quot`, :c:func:`_Py_c_pow` and +previously undocumented :c:func:`_Py_c_abs` are :term:`soft deprecated`. +Deprecate also :c:member:`~PyComplexObject.cval` field of the +:c:type:`PyComplexObject` type. Patch by Sergey B Kirpichev. + +.. + +.. date: 2025-07-29-18-00-22 +.. gh-issue: 137210 +.. nonce: DD4VEm +.. section: C API + +Add API for checking an extension module's ABI compatibility: +:c:data:`Py_mod_abi`, :c:func:`PyABIInfo_Check`, :c:macro:`PyABIInfo_VAR` +and :c:data:`Py_mod_abi`. + +.. + +.. date: 2025-07-23-22-30-23 +.. gh-issue: 136759 +.. nonce: ffB4wO +.. section: C API + +Rename ``lock.h`` to ``pylock.h`` to avoid potential include conflicts. + +.. + +.. date: 2025-07-22-15-18-08 +.. gh-issue: 112068 +.. nonce: 4WvT-8 +.. section: C API + +Revert support of nullable arguments in :c:func:`PyArg_Parse`. + +.. + +.. date: 2025-07-08-22-07-54 +.. gh-issue: 136006 +.. nonce: XRU5w4 +.. section: C API + +On Solaris, the :c:macro:`!Py_NAN` macro now expands to a :c:type:`!double` +instead of a function address. Patch by Bénédikt Tran. + +.. + +.. date: 2025-07-01-16-22-39 +.. gh-issue: 135075 +.. nonce: angu3J +.. section: C API + +Make :c:func:`PyObject_SetAttr` and :c:func:`PyObject_SetAttrString` fail if +called with ``NULL`` value and an exception set. Patch by Victor Stinner. + +.. + +.. date: 2025-06-25-01-03-10 +.. gh-issue: 135906 +.. nonce: UBrCWq +.. section: C API + +Fix compilation errors when compiling the internal headers with a C++ +compiler. + +.. + +.. date: 2025-06-24-11-10-01 +.. gh-issue: 133296 +.. nonce: lIEuVJ +.. section: C API + +New variants for the critical section API that accept one or two +:c:type:`PyMutex` pointers rather than :c:type:`PyObject` instances are now +public in the non-limited C API. + +.. + +.. date: 2025-06-19-12-47-18 +.. gh-issue: 133157 +.. nonce: 1WA85f +.. section: C API + +Remove the private, undocumented macro +:c:macro:`!_Py_NO_SANITIZE_UNDEFINED`. + +.. + +.. date: 2025-06-05-11-06-07 +.. gh-issue: 134989 +.. nonce: 74p4ud +.. section: C API + +Fix ``Py_RETURN_NONE``, ``Py_RETURN_TRUE`` and ``Py_RETURN_FALSE`` macros in +the limited C API 3.11 and older: don't treat ``Py_None``, ``Py_True`` and +``Py_False`` as immortal. Patch by Victor Stinner. + +.. + +.. date: 2025-06-02-13-19-22 +.. gh-issue: 134989 +.. nonce: sDDyBN +.. section: C API + +Implement :c:func:`PyObject_DelAttr` and :c:func:`PyObject_DelAttrString` as +macros in the limited C API 3.12 and older. Patch by Victor Stinner. + +.. + +.. date: 2025-05-30-11-33-17 +.. gh-issue: 134745 +.. nonce: GN-zk2 +.. section: C API + +Change :c:func:`!PyThread_allocate_lock` implementation to ``PyMutex``. On +Windows, :c:func:`!PyThread_acquire_lock_timed` now supports the *intr_flag* +parameter: it can be interrupted. Patch by Victor Stinner. + +.. + +.. date: 2025-05-29-16-56-23 +.. gh-issue: 134891 +.. nonce: 7eKO8U +.. section: C API + +Add :c:type:`PyUnstable_Unicode_GET_CACHED_HASH` to get the cached hash of a +string. + +.. + +.. date: 2025-05-20-17-13-51 +.. gh-issue: 134009 +.. nonce: CpCmry +.. section: C API + +Expose :c:func:`PyMutex_IsLocked` as part of the public C API. + +.. + +.. date: 2025-05-17-14-41-21 +.. gh-issue: 134144 +.. nonce: xVpZik +.. section: C API + +Fix crash when calling :c:func:`Py_EndInterpreter` with a :term:`thread +state` that isn't the initial thread for the interpreter. + +.. + +.. date: 2025-05-13-16-06-46 +.. gh-issue: 133968 +.. nonce: 6alWst +.. section: C API + +Add :c:func:`PyUnicodeWriter_WriteASCII` function to write an ASCII string +into a :c:type:`PyUnicodeWriter`. The function is faster than +:c:func:`PyUnicodeWriter_WriteUTF8`, but has an undefined behavior if the +input string contains non-ASCII characters. Patch by Victor Stinner. + +.. + +.. date: 2025-05-08-13-14-45 +.. gh-issue: 133644 +.. nonce: J8_KZ2 +.. section: C API + +Remove deprecated Python initialization getter functions ``Py_Get*``. Patch +by Bénédikt Tran. + +.. + +.. date: 2025-05-08-12-40-59 +.. gh-issue: 133644 +.. nonce: FNexLJ +.. section: C API + +Remove deprecated function :c:func:`!PyWeakref_GetObject` and macro +:c:macro:`!PyWeakref_GET_OBJECT`. Use :c:func:`PyWeakref_GetRef` instead. +Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-08-12-25-47 +.. gh-issue: 133644 +.. nonce: Yb86Rm +.. section: C API + +Remove deprecated alias :c:func:`!PyImport_ImportModuleNoBlock` of +:c:func:`PyImport_ImportModule`. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-07-21-18-00 +.. gh-issue: 133610 +.. nonce: asdfjs +.. section: C API + +Remove deprecated functions :c:func:`!PyUnicode_AsDecodedObject`, +:c:func:`!PyUnicode_AsDecodedUnicode`, :c:func:`!PyUnicode_AsEncodedObject`, +and :c:func:`!PyUnicode_AsEncodedUnicode`. + +.. + +.. date: 2025-04-17-12-37-27 +.. gh-issue: 132629 +.. nonce: 01ArwX +.. section: C API + +For unsigned integer formats in :c:func:`PyArg_ParseTuple`, accepting Python +integers with value that is larger than the maximal value for the C type or +less than the minimal value for the corresponding signed integer type of the +same size is now deprecated. + +.. + +.. date: 2025-04-14-07-41-28 +.. gh-issue: 131185 +.. nonce: ZCjMHD +.. section: C API + +:c:func:`PyGILState_Ensure` no longer crashes when called after interpreter +finalization. + +.. + +.. date: 2023-10-18-14-36-35 +.. gh-issue: 108512 +.. nonce: fMZLfr +.. section: C API + +Add functions :c:func:`PySys_GetAttr`, :c:func:`PySys_GetAttrString`, +:c:func:`PySys_GetOptionalAttr` and :c:func:`PySys_GetOptionalAttrString`. + +.. + +.. date: 2025-09-24-13-59-26 +.. gh-issue: 138489 +.. nonce: 1AcuZM +.. section: Build + +When cross-compiling for WASI by ``build_wasm`` or ``build_emscripten``, the +``build-details.json`` step is now included in the build process, just like +with native builds. + +This fixes the ``libinstall`` task which requires the ``build-details.json`` +file during the process. + +.. + +.. date: 2025-09-04-12-16-31 +.. gh-issue: 138497 +.. nonce: Y_5YXh +.. section: Build + +The LLVM version used by the JIT at build time can now be modified using the +``LLVM_VERSION`` environment variable. Use this at your own risk, as there +is only one officially supported LLVM version. For more information, please +check ``Tools/jit/README.md``. + +.. + +.. date: 2025-08-27-11-32-02 +.. gh-issue: 95952 +.. nonce: KSymc7 +.. section: Build + +When cross-compiling for WASI, require that the HOSTRUNNER environment +variable be explicitly set. + +This was needed as macOS lacks the appropriate CLI tools to set a reasonable +default. + +.. + +.. date: 2025-08-27-09-52-45 +.. gh-issue: 138061 +.. nonce: fMVS9w +.. section: Build + +Ensure reproducible builds by making JIT stencil header generation +deterministic. + +.. + +.. date: 2025-08-26-21-18-32 +.. gh-issue: 128042 +.. nonce: 5voC8H +.. section: Build + +``./configure`` now warns when ``--enable-optimizations`` and ``CFLAGS=-O0`` +are both set, suggesting removing ``-O0`` from ``CFLAGS`` for optimal +performance. Patch by Taegyun Kim. + +.. + +.. date: 2025-08-13-12-10-12 +.. gh-issue: 132339 +.. nonce: 3Czz5y +.. section: Build + +Add support for OpenSSL 3.5. + +.. + +.. date: 2025-07-18-17-15-00 +.. gh-issue: 135621 +.. nonce: 9cyCNb +.. section: Build + +PyREPL no longer depends on the :mod:`curses` standard library. Contributed +by Łukasz Langa. + +.. + +.. date: 2025-06-25-13-27-14 +.. gh-issue: 135927 +.. nonce: iCNPQc +.. section: Build + +Fix building with MSVC when passing option ``/std:clatest``. + +.. + +.. date: 2025-06-16-07-20-28 +.. gh-issue: 119132 +.. nonce: fcI8s7 +.. section: Build + +Remove "experimental" tag from the CPython free-threading build. + +.. + +.. date: 2025-06-14-10-32-11 +.. gh-issue: 135497 +.. nonce: ajlV4F +.. section: Build + +Fix the detection of ``MAXLOGNAME`` in the ``configure.ac`` script. + +.. + +.. date: 2025-05-30-11-02-30 +.. gh-issue: 134923 +.. nonce: gBkRg4 +.. section: Build + +Windows builds with profile-guided optimization enabled now use +``/GENPROFILE`` and ``/USEPROFILE`` instead of deprecated ``/LTCG:`` +options. + +.. + +.. date: 2025-05-24-16-59-20 +.. gh-issue: 134632 +.. nonce: i0W2hc +.. section: Build + +Fixed ``build-details.json`` generation to use ``INCLUDEPY``, in order to +reference the ``pythonX.Y`` subdirectory of the include directory, as +required in :pep:`739`, instead of the top-level include directory. + +.. + +.. date: 2025-05-21-22-13-30 +.. gh-issue: 134486 +.. nonce: yvdL6f +.. section: Build + +The :mod:`ctypes` module now performs a more portable test for the +definition of :manpage:`alloca(3)`, fixing a compilation failure on NetBSD. + +.. + +.. date: 2025-05-21-19-46-28 +.. gh-issue: 134455 +.. nonce: vdwlrq +.. section: Build + +Fixed ``build-details.json`` generation to use the correct ``c_api.headers`` +as defined in :pep:`739`, instead of ``c_api.include``. + +.. + +.. date: 2025-05-19-18-09-20 +.. gh-issue: 134273 +.. nonce: ZAliyy +.. section: Build + +Add support for configuring compiler flags for the JIT with ``CFLAGS_JIT`` + +.. + +.. date: 2025-05-16-07-46-06 +.. gh-issue: 115119 +.. nonce: ALBgS_ +.. section: Build + +Removed implicit fallback to the bundled copy of the ``libmpdec`` library. +Now this should be explicitly enabled via :option:`!--with-system-libmpdec` +set to ``no`` or :option:`!--without-system-libmpdec`. Patch by Sergey B +Kirpichev. + +.. + +.. date: 2025-05-14-09-43-48 +.. gh-issue: 131769 +.. nonce: H0oy5x +.. section: Build + +Fix detecting when the build Python in a cross-build is a pydebug build. + +.. + +.. date: 2025-04-16-09-38-48 +.. gh-issue: 117088 +.. nonce: EFt_5c +.. section: Build + +AIX linker don't support -h option, so avoid it through platform check + +.. + +.. date: 2025-01-03-13-02-06 +.. gh-issue: 123681 +.. nonce: gQ67nK +.. section: Build + +Check the ``strftime()`` behavior at runtime instead of at the compile time +to support cross-compiling. Remove the internal macro +``_Py_NORMALIZE_CENTURY``. + +.. + +.. date: 2024-12-04-10-00-35 +.. gh-issue: 127545 +.. nonce: t0THjE +.. section: Build + +Fix crash when building on Linux/m68k. diff --git a/Misc/NEWS.d/3.15.0a2.rst b/Misc/NEWS.d/3.15.0a2.rst new file mode 100644 index 00000000000000..852bce71ebd2b5 --- /dev/null +++ b/Misc/NEWS.d/3.15.0a2.rst @@ -0,0 +1,1746 @@ +.. date: 2025-11-04-19-20-05 +.. gh-issue: 140849 +.. nonce: YjB2ZZ +.. release date: 2025-11-18 +.. section: Windows + +Update bundled liblzma to version 5.8.1. + +.. + +.. date: 2025-11-12-12-54-28 +.. gh-issue: 141442 +.. nonce: 50dS3P +.. section: Tools/Demos + +The iOS testbed now correctly handles test arguments that contain spaces. + +.. + +.. date: 2025-10-29-15-20-19 +.. gh-issue: 140702 +.. nonce: ZXtW8h +.. section: Tools/Demos + +The iOS testbed app will now expose the ``GITHUB_ACTIONS`` environment +variable to iOS apps being tested. + +.. + +.. date: 2025-09-21-10-30-08 +.. gh-issue: 139198 +.. nonce: Fm7NfU +.. section: Tools/Demos + +Remove ``Tools/scripts/checkpip.py`` script. + +.. + +.. date: 2025-09-20-20-31-54 +.. gh-issue: 139188 +.. nonce: zfcxkW +.. section: Tools/Demos + +Remove ``Tools/tz/zdump.py`` script. + +.. + +.. date: 2025-10-23-16-39-49 +.. gh-issue: 140482 +.. nonce: ZMtyeD +.. section: Tests + +Preserve and restore the state of ``stty echo`` as part of the test +environment. + +.. + +.. date: 2025-10-15-00-52-12 +.. gh-issue: 140082 +.. nonce: fpET50 +.. section: Tests + +Update ``python -m test`` to set ``FORCE_COLOR=1`` when being run with color +enabled so that :mod:`unittest` which is run by it with redirected output +will output in color. + +.. + +.. date: 2025-07-09-21-45-51 +.. gh-issue: 136442 +.. nonce: jlbklP +.. section: Tests + +Use exitcode ``1`` instead of ``5`` if :func:`unittest.TestCase.setUpClass` +raises an exception + +.. + +.. date: 2025-08-15-23-08-44 +.. gh-issue: 137836 +.. nonce: b55rhh +.. section: Security + +Add support of the "plaintext" element, RAWTEXT elements "xmp", "iframe", +"noembed" and "noframes", and optionally RAWTEXT element "noscript" in +:class:`html.parser.HTMLParser`. + +.. + +.. date: 2025-06-28-13-23-53 +.. gh-issue: 136063 +.. nonce: aGk0Jv +.. section: Security + +:mod:`email.message`: ensure linear complexity for legacy HTTP parameters +parsing. Patch by Bénédikt Tran. + +.. + +.. date: 2025-05-30-22-33-27 +.. gh-issue: 136065 +.. nonce: bu337o +.. section: Security + +Fix quadratic complexity in :func:`os.path.expandvars`. + +.. + +.. date: 2025-11-14-16-24-20 +.. gh-issue: 141497 +.. nonce: L_CxDJ +.. section: Library + +:mod:`ipaddress`: ensure that the methods :meth:`IPv4Network.hosts() +<ipaddress.IPv4Network.hosts>` and :meth:`IPv6Network.hosts() +<ipaddress.IPv6Network.hosts>` always return an iterator. + +.. + +.. date: 2025-11-13-14-51-30 +.. gh-issue: 140938 +.. nonce: kXsHHv +.. section: Library + +The :func:`statistics.stdev` and :func:`statistics.pstdev` functions now +raise a :exc:`ValueError` when the input contains an infinity or a NaN. + +.. + +.. date: 2025-11-12-15-42-47 +.. gh-issue: 124111 +.. nonce: hTw4OE +.. section: Library + +Updated Tcl threading configuration in :mod:`_tkinter` to assume that +threads are always available in Tcl 9 and later. + +.. + +.. date: 2025-11-12-01-49-03 +.. gh-issue: 137109 +.. nonce: D6sq2B +.. section: Library + +The :mod:`os.fork` and related forking APIs will no longer warn in the +common case where Linux or macOS platform APIs return the number of threads +in a process and find the answer to be 1 even when a +:func:`os.register_at_fork` ``after_in_parent=`` callback (re)starts a +thread. + +.. + +.. date: 2025-11-10-01-47-18 +.. gh-issue: 141314 +.. nonce: baaa28 +.. section: Library + +Fix assertion failure in :meth:`io.TextIOWrapper.tell` when reading files +with standalone carriage return (``\r``) line endings. + +.. + +.. date: 2025-11-09-18-55-13 +.. gh-issue: 141311 +.. nonce: qZ3swc +.. section: Library + +Fix assertion failure in :func:`!io.BytesIO.readinto` and undefined behavior +arising when read position is above capcity in :class:`io.BytesIO`. + +.. + +.. date: 2025-11-08-13-03-10 +.. gh-issue: 87710 +.. nonce: XJeZlP +.. section: Library + +:mod:`mimetypes`: Update mime type for ``.ai`` files to ``application/pdf``. + +.. + +.. date: 2025-11-07-12-25-46 +.. gh-issue: 85524 +.. nonce: 9SWFIC +.. section: Library + +Update ``io.FileIO.readall``, an implementation of +:meth:`io.RawIOBase.readall`, to follow :class:`io.IOBase` guidelines and +raise :exc:`io.UnsupportedOperation` when a file is in "w" mode rather than +:exc:`OSError` + +.. + +.. date: 2025-11-06-15-11-50 +.. gh-issue: 141141 +.. nonce: tgIfgH +.. section: Library + +Fix a thread safety issue with :func:`base64.b85decode`. Contributed by +Benel Tayar. + +.. + +.. date: 2025-11-04-20-08-41 +.. gh-issue: 141018 +.. nonce: d_oyOI +.. section: Library + +:mod:`mimetypes`: Update ``.exe``, ``.dll``, ``.rtf`` and (when +``strict=False``) ``.jpg`` to their correct IANA mime type. + +.. + +.. date: 2025-11-04-15-40-35 +.. gh-issue: 137969 +.. nonce: 9VZQVt +.. section: Library + +Fix :meth:`annotationlib.ForwardRef.evaluate` returning +:class:`~annotationlib.ForwardRef` objects which don't update with new +globals. + +.. + +.. date: 2025-11-04-12-16-13 +.. gh-issue: 75593 +.. nonce: EFVhKR +.. section: Library + +Add support of :term:`path-like objects <path-like object>` and +:term:`bytes-like objects <bytes-like object>` in :func:`wave.open`. + +.. + +.. date: 2025-11-03-16-23-54 +.. gh-issue: 140797 +.. nonce: DuFEeR +.. section: Library + +The undocumented :class:`!re.Scanner` class now forbids regular expressions +containing capturing groups in its lexicon patterns. Patterns using +capturing groups could previously lead to crashes with segmentation fault. +Use non-capturing groups (?:...) instead. + +.. + +.. date: 2025-11-03-05-38-31 +.. gh-issue: 125115 +.. nonce: jGS8MN +.. section: Library + +Refactor the :mod:`pdb` parsing issue so positional arguments can pass +through intuitively. + +.. + +.. date: 2025-11-02-19-23-32 +.. gh-issue: 140815 +.. nonce: McEG-T +.. section: Library + +:mod:`faulthandler` now detects if a frame or a code object is invalid or +freed. Patch by Victor Stinner. + +.. + +.. date: 2025-11-02-11-46-00 +.. gh-issue: 100218 +.. nonce: 9Ezfdq +.. section: Library + +Correctly set :attr:`~OSError.errno` when :func:`socket.if_nametoindex` or +:func:`socket.if_indextoname` raise an :exc:`OSError`. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-11-02-09-37-22 +.. gh-issue: 140734 +.. nonce: f8gST9 +.. section: Library + +:mod:`multiprocessing`: fix off-by-one error when checking the length of a +temporary socket file path. Patch by Bénédikt Tran. + +.. + +.. date: 2025-11-01-14-44-09 +.. gh-issue: 140873 +.. nonce: kfuc9B +.. section: Library + +Add support of non-:term:`descriptor` callables in +:func:`functools.singledispatchmethod`. + +.. + +.. date: 2025-11-01-00-36-14 +.. gh-issue: 140874 +.. nonce: eAWt3K +.. section: Library + +Bump the version of pip bundled in ensurepip to version 25.3 + +.. + +.. date: 2025-11-01-00-34-53 +.. gh-issue: 140826 +.. nonce: JEDd7U +.. section: Library + +Now :class:`!winreg.HKEYType` objects are compared by their underlying +Windows registry handle value instead of their object identity. + +.. + +.. date: 2025-10-31-16-25-13 +.. gh-issue: 140808 +.. nonce: XBiQ4j +.. section: Library + +The internal class ``mailbox._ProxyFile`` is no longer a parameterized +generic. + +.. + +.. date: 2025-10-31-15-06-26 +.. gh-issue: 140691 +.. nonce: JzHGtg +.. section: Library + +In :mod:`urllib.request`, when opening a FTP URL fails because a data +connection cannot be made, the control connection's socket is now closed to +avoid a :exc:`ResourceWarning`. + +.. + +.. date: 2025-10-31-13-57-55 +.. gh-issue: 103847 +.. nonce: VM7TnW +.. section: Library + +Fix hang when cancelling process created by +:func:`asyncio.create_subprocess_exec` or +:func:`asyncio.create_subprocess_shell`. Patch by Kumar Aditya. + +.. + +.. date: 2025-10-30-15-33-07 +.. gh-issue: 137821 +.. nonce: 8_Iavt +.. section: Library + +Convert ``_json`` module to use Argument Clinic. Patched by Yoonho Hann. + +.. + +.. date: 2025-10-30-12-36-19 +.. gh-issue: 140790 +.. nonce: _3T6-N +.. section: Library + +Initialize all Pdb's instance variables in ``__init__``, remove some +hasattr/getattr + +.. + +.. date: 2025-10-29-16-53-00 +.. gh-issue: 140766 +.. nonce: CNagKF +.. section: Library + +Add :func:`enum.show_flag_values` and ``enum.bin`` to ``enum.__all__``. + +.. + +.. date: 2025-10-29-16-12-41 +.. gh-issue: 120057 +.. nonce: qGj5Dl +.. section: Library + +Add :func:`os.reload_environ` to ``os.__all__``. + +.. + +.. date: 2025-10-29-09-40-10 +.. gh-issue: 140741 +.. nonce: L13UCV +.. section: Library + +Fix ``profiling.sampling.sample()`` incorrectly handling a +:exc:`FileNotFoundError` or :exc:`PermissionError`. + +.. + +.. date: 2025-10-28-17-43-51 +.. gh-issue: 140228 +.. nonce: 8kfHhO +.. section: Library + +Avoid making unnecessary filesystem calls for frozen modules in +:mod:`linecache` when the global module cache is not present. + +.. + +.. date: 2025-10-28-02-46-56 +.. gh-issue: 139946 +.. nonce: aN3_uY +.. section: Library + +Error and warning keywords in ``argparse.ArgumentParser`` messages are now +colorized when color output is enabled, fixing a visual inconsistency in +which they remained plain text while other output was colorized. + +.. + +.. date: 2025-10-27-18-29-42 +.. gh-issue: 140590 +.. nonce: LT9HHn +.. section: Library + +Fix arguments checking for the :meth:`!functools.partial.__setstate__` that +may lead to internal state corruption and crash. Patch by Sergey Miryanov. + +.. + +.. date: 2025-10-27-16-01-41 +.. gh-issue: 125434 +.. nonce: qy0uRA +.. section: Library + +Display thread name in :mod:`faulthandler` on Windows. Patch by Victor +Stinner. + +.. + +.. date: 2025-10-27-13-49-31 +.. gh-issue: 140634 +.. nonce: ULng9G +.. section: Library + +Fix a reference counting bug in :meth:`!os.sched_param.__reduce__`. + +.. + +.. date: 2025-10-27-00-40-49 +.. gh-issue: 140650 +.. nonce: DYJPJ9 +.. section: Library + +Fix an issue where closing :class:`io.BufferedWriter` could crash if the +closed attribute raised an exception on access or could not be converted to +a boolean. + +.. + +.. date: 2025-10-26-16-24-12 +.. gh-issue: 140633 +.. nonce: ioayC1 +.. section: Library + +Ignore :exc:`AttributeError` when setting a module's ``__file__`` attribute +when loading an extension module packaged as Apple Framework. + +.. + +.. date: 2025-10-25-22-55-07 +.. gh-issue: 140601 +.. nonce: In3MlS +.. section: Library + +:func:`xml.etree.ElementTree.iterparse` now emits a :exc:`ResourceWarning` +when the iterator is not explicitly closed and was opened with a filename. +This helps developers identify and fix resource leaks. Patch by Osama +Abdelkader. + +.. + +.. date: 2025-10-25-21-26-16 +.. gh-issue: 140593 +.. nonce: OxlLc9 +.. section: Library + +:mod:`xml.parsers.expat`: Fix a memory leak that could affect users with +:meth:`~xml.parsers.expat.xmlparser.ElementDeclHandler` set to a custom +element declaration handler. Patch by Sebastian Pipping. + +.. + +.. date: 2025-10-25-21-04-00 +.. gh-issue: 140607 +.. nonce: oOZGxS +.. section: Library + +Inside :meth:`io.RawIOBase.read`, validate that the count of bytes returned +by :meth:`io.RawIOBase.readinto` is valid (inside the provided buffer). + +.. + +.. date: 2025-10-23-19-39-16 +.. gh-issue: 138162 +.. nonce: Znw5DN +.. section: Library + +Fix :class:`logging.LoggerAdapter` with ``merge_extra=True`` and without the +*extra* argument. + +.. + +.. date: 2025-10-23-13-42-15 +.. gh-issue: 140481 +.. nonce: XKxWpq +.. section: Library + +Improve error message when trying to iterate a Tk widget, image or font. + +.. + +.. date: 2025-10-23-12-12-22 +.. gh-issue: 138774 +.. nonce: mnh2gU +.. section: Library + +:func:`ast.unparse` now generates full source code when handling +:class:`ast.Interpolation` nodes that do not have a specified source. + +.. + +.. date: 2025-10-22-20-52-13 +.. gh-issue: 140474 +.. nonce: xIWlip +.. section: Library + +Fix memory leak in :class:`array.array` when creating arrays from an empty +:class:`str` and the ``u`` type code. + +.. + +.. date: 2025-10-22-12-56-57 +.. gh-issue: 140448 +.. nonce: GsEkXD +.. section: Library + +Change the default of ``suggest_on_error`` to ``True`` in +``argparse.ArgumentParser``. + +.. + +.. date: 2025-10-21-15-54-13 +.. gh-issue: 137530 +.. nonce: ZyIVUH +.. section: Library + +:mod:`dataclasses` Fix annotations for generated ``__init__`` methods by +replacing the annotations that were in-line in the generated source code +with ``__annotate__`` functions attached to the methods. + +.. + +.. date: 2025-10-20-12-33-49 +.. gh-issue: 140348 +.. nonce: SAKnQZ +.. section: Library + +Fix regression in Python 3.14.0 where using the ``|`` operator on a +:class:`typing.Union` object combined with an object that is not a type +would raise an error. + +.. + +.. date: 2025-10-18-15-20-25 +.. gh-issue: 76007 +.. nonce: SNUzRq +.. section: Library + +:mod:`decimal`: Deprecate ``__version__`` and replace with +:data:`decimal.SPEC_VERSION`. + +.. + +.. date: 2025-10-18-14-30-21 +.. gh-issue: 76007 +.. nonce: peEgcr +.. section: Library + +Deprecate ``__version__`` from :mod:`imaplib`. Patch by Hugo van Kemenade. + +.. + +.. date: 2025-10-17-23-58-11 +.. gh-issue: 140272 +.. nonce: lhY8uS +.. section: Library + +Fix memory leak in the :meth:`!clear` method of the :mod:`dbm.gnu` database. + +.. + +.. date: 2025-10-17-20-42-38 +.. gh-issue: 129117 +.. nonce: X9jr4p +.. section: Library + +:mod:`unicodedata`: Add :func:`~unicodedata.isxidstart` and +:func:`~unicodedata.isxidcontinue` functions to check whether a character +can start or continue a `Unicode Standard Annex #31 +<https://www.unicode.org/reports/tr31/>`_ identifier. + +.. + +.. date: 2025-10-17-12-33-01 +.. gh-issue: 140251 +.. nonce: esM-OX +.. section: Library + +Colorize the default import statement ``import asyncio`` in asyncio REPL. + +.. + +.. date: 2025-10-16-22-49-16 +.. gh-issue: 140212 +.. nonce: llBNd0 +.. section: Library + +Calendar's HTML formatting now accepts year and month as options. +Previously, running ``python -m calendar -t html 2025 10`` would result in +an error message. It now generates an HTML document displaying the calendar +for the specified month. Contributed by Pål Grønås Drange. + +.. + +.. date: 2025-10-16-17-17-20 +.. gh-issue: 135801 +.. nonce: faH3fa +.. section: Library + +Improve filtering by module in :func:`warnings.warn_explicit` if no *module* +argument is passed. It now tests the module regular expression in the +warnings filter not only against the filename with ``.py`` stripped, but +also against module names constructed starting from different parent +directories of the filename (with ``/__init__.py``, ``.py`` and, on Windows, +``.pyw`` stripped). + +.. + +.. date: 2025-10-16-16-10-11 +.. gh-issue: 139707 +.. nonce: zR6Qtn +.. section: Library + +Improve :exc:`ModuleNotFoundError` error message when a :term:`standard +library` module is missing. + +.. + +.. date: 2025-10-15-21-42-13 +.. gh-issue: 140041 +.. nonce: _Fka2j +.. section: Library + +Fix import of :mod:`ctypes` on Android and Cygwin when ABI flags are +present. + +.. + +.. date: 2025-10-15-20-47-04 +.. gh-issue: 140120 +.. nonce: 3gffZq +.. section: Library + +Fixed a memory leak in :mod:`hmac` when it was using the hacl-star backend. +Discovered by ``@ashm-dev`` using AddressSanitizer. + +.. + +.. date: 2025-10-15-17-23-51 +.. gh-issue: 140141 +.. nonce: j2mUDB +.. section: Library + +The :py:class:`importlib.metadata.PackageNotFoundError` traceback raised +when ``importlib.metadata.Distribution.from_name`` cannot discover a +distribution no longer includes a transient :exc:`StopIteration` exception +trace. + +Contributed by Bartosz Sławecki in :gh:`140142`. + +.. + +.. date: 2025-10-15-15-10-34 +.. gh-issue: 140166 +.. nonce: NtxRez +.. section: Library + +:mod:`mimetypes`: Per the `IANA assignment +<https://www.iana.org/assignments/media-types/application/texinfo>`_, update +the MIME type for the ``.texi`` and ``.texinfo`` file formats to +``application/texinfo``, instead of ``application/x-texinfo``. + +.. + +.. date: 2025-10-15-02-26-50 +.. gh-issue: 140135 +.. nonce: 54JYfM +.. section: Library + +Speed up :meth:`io.RawIOBase.readall` by using PyBytesWriter API (about 4x +faster) + +.. + +.. date: 2025-10-14-20-27-06 +.. gh-issue: 76007 +.. nonce: 2NcUbo +.. section: Library + +:mod:`zlib`: Deprecate ``__version__`` and schedule for removal in Python +3.20. + +.. + +.. date: 2025-10-13-11-25-41 +.. gh-issue: 136702 +.. nonce: uvLGK1 +.. section: Library + +:mod:`encodings`: Deprecate passing a non-ascii *encoding* name to +:func:`encodings.normalize_encoding` and schedule removal of support for +Python 3.17. + +.. + +.. date: 2025-10-11-09-07-06 +.. gh-issue: 139940 +.. nonce: g54efZ +.. section: Library + +Print clearer error message when using ``pdb`` to attach to a non-existing +process. + +.. + +.. date: 2025-10-02-22-29-00 +.. gh-issue: 139462 +.. nonce: VZXUHe +.. section: Library + +When a child process in a :class:`concurrent.futures.ProcessPoolExecutor` +terminates abruptly, the resulting traceback will now tell you the PID and +exit code of the terminated process. Contributed by Jonathan Berg. + +.. + +.. date: 2025-09-30-12-52-54 +.. gh-issue: 63161 +.. nonce: mECM1A +.. section: Library + +Fix :func:`tokenize.detect_encoding`. Support non-UTF-8 shebang and comments +if non-UTF-8 encoding is specified. Detect decoding error for non-UTF-8 +encoding. Detect null bytes in source code. + +.. + +.. date: 2025-09-25-20-16-10 +.. gh-issue: 101828 +.. nonce: yTxJlJ +.. section: Library + +Fix ``'shift_jisx0213'``, ``'shift_jis_2004'``, ``'euc_jisx0213'`` and +``'euc_jis_2004'`` codecs truncating null chars as they were treated as part +of multi-character sequences. + +.. + +.. date: 2025-09-23-09-46-46 +.. gh-issue: 139246 +.. nonce: pzfM-w +.. section: Library + +fix: paste zero-width in default repl width is wrong. + +.. + +.. date: 2025-09-18-21-25-41 +.. gh-issue: 83714 +.. nonce: TQjDWZ +.. section: Library + +Implement :func:`os.statx` on Linux kernel versions 4.11 and later with +glibc versions 2.28 and later. Contributed by Jeffrey Bosboom and Victor +Stinner. + +.. + +.. date: 2025-09-15-21-03-11 +.. gh-issue: 138891 +.. nonce: oZFdtR +.. section: Library + +Fix ``SyntaxError`` when ``inspect.get_annotations(f, eval_str=True)`` is +called on a function annotated with a :pep:`646` ``star_expression`` + +.. + +.. date: 2025-09-13-12-19-17 +.. gh-issue: 138859 +.. nonce: PxjIoN +.. section: Library + +Fix generic type parameterization raising a :exc:`TypeError` when omitting a +:class:`ParamSpec` that has a default which is not a list of types. + +.. + +.. date: 2025-09-12-09-34-37 +.. gh-issue: 138764 +.. nonce: mokHoY +.. section: Library + +Prevent :func:`annotationlib.call_annotate_function` from calling +``__annotate__`` functions that don't support ``VALUE_WITH_FAKE_GLOBALS`` in +a fake globals namespace with empty globals. + +Make ``FORWARDREF`` and ``STRING`` annotations fall back to using ``VALUE`` +annotations in the case that neither their own format, nor +``VALUE_WITH_FAKE_GLOBALS`` are supported. + +.. + +.. date: 2025-09-11-15-03-37 +.. gh-issue: 138775 +.. nonce: w7rnSx +.. section: Library + +Use of ``python -m`` with :mod:`base64` has been fixed to detect input from +a terminal so that it properly notices EOF. + +.. + +.. date: 2025-09-03-20-18-39 +.. gh-issue: 98896 +.. nonce: tjez89 +.. section: Library + +Fix a failure in multiprocessing resource_tracker when SharedMemory names +contain colons. Patch by Rani Pinchuk. + +.. + +.. date: 2025-09-03-18-26-07 +.. gh-issue: 138425 +.. nonce: cVE9Ho +.. section: Library + +Fix partial evaluation of :class:`annotationlib.ForwardRef` objects which +rely on names defined as globals. + +.. + +.. date: 2025-08-26-08-17-56 +.. gh-issue: 138151 +.. nonce: I6CdAk +.. section: Library + +In :mod:`annotationlib`, improve evaluation of forward references to +nonlocal variables that are not yet defined when the annotations are +initially evaluated. + +.. + +.. date: 2025-08-15-20-35-30 +.. gh-issue: 69528 +.. nonce: qc-Eh_ +.. section: Library + +The :attr:`~io.FileIO.mode` attribute of files opened in the ``'wb+'`` mode +is now ``'wb+'`` instead of ``'rb+'``. + +.. + +.. date: 2025-08-11-04-52-18 +.. gh-issue: 137627 +.. nonce: Ku5Yi2 +.. section: Library + +Speed up :meth:`csv.Sniffer.sniff` delimiter detection by up to 1.6x. + +.. + +.. date: 2025-07-14-09-33-17 +.. gh-issue: 55531 +.. nonce: Gt2e12 +.. section: Library + +:mod:`encodings`: Improve :func:`~encodings.normalize_encoding` performance +by implementing the function in C using the private +``_Py_normalize_encoding`` which has been modified to make lowercase +conversion optional. + +.. + +.. date: 2025-07-01-04-57-57 +.. gh-issue: 136057 +.. nonce: 4-t596 +.. section: Library + +Fixed the bug in :mod:`pdb` and :mod:`bdb` where ``next`` and ``step`` can't +go over the line if a loop exists in the line. + +.. + +.. date: 2025-06-29-22-01-00 +.. gh-issue: 133390 +.. nonce: I1DW_3 +.. section: Library + +Support table, index, trigger, view, column, function, and schema completion +for :mod:`sqlite3`'s :ref:`command-line interface <sqlite3-cli>`. + +.. + +.. date: 2025-06-10-18-02-29 +.. gh-issue: 135307 +.. nonce: fXGrcK +.. section: Library + +:mod:`email`: Fix exception in ``set_content()`` when encoding text and +max_line_length is set to ``0`` or ``None`` (unlimited). + +.. + +.. date: 2025-05-10-15-10-54 +.. gh-issue: 133789 +.. nonce: I-ZlUX +.. section: Library + +Fix unpickling of :mod:`pathlib` objects that were pickled in Python 3.13. + +.. + +.. date: 2025-05-07-22-09-28 +.. gh-issue: 133601 +.. nonce: 9kUL3P +.. section: Library + +Remove deprecated :func:`!typing.no_type_check_decorator`. + +.. + +.. date: 2025-04-18-18-08-05 +.. gh-issue: 132686 +.. nonce: 6kV_Gs +.. section: Library + +Add parameters *inherit_class_doc* and *fallback_to_class_doc* for +:func:`inspect.getdoc`. + +.. + +.. date: 2025-03-12-18-57-10 +.. gh-issue: 131116 +.. nonce: uTpwXZ +.. section: Library + +:func:`inspect.getdoc` now correctly returns an inherited docstring on +:class:`~functools.cached_property` objects if none is given in a subclass. + +.. + +.. date: 2025-03-04-17-19-26 +.. gh-issue: 130693 +.. nonce: Kv01r8 +.. section: Library + +Add support for ``-nolinestop``, and ``-strictlimits`` options to +:meth:`!tkinter.Text.search`. Also add the :meth:`!tkinter.Text.search_all` +method for ``-all`` and ``-overlap`` options. + +.. + +.. date: 2024-08-08-12-39-36 +.. gh-issue: 122255 +.. nonce: J_gU8Y +.. section: Library + +In the :mod:`linecache` module and in the Python implementation of the +:mod:`warnings` module, a ``DeprecationWarning`` is issued when +``mod.__loader__`` differs from ``mod.__spec__.loader`` (like in the C +implementation of the :mod:`!warnings` module). + +.. + +.. date: 2024-06-26-16-16-43 +.. gh-issue: 121011 +.. nonce: qW54eh +.. section: Library + +:func:`math.log` now supports arbitrary large integer-like arguments in the +same way as arbitrary large integer arguments. + +.. + +.. date: 2024-05-28-17-14-30 +.. gh-issue: 119668 +.. nonce: RrIGpn +.. section: Library + +Publicly expose and document :class:`importlib.machinery.NamespacePath`. + +.. + +.. date: 2023-03-21-10-59-40 +.. gh-issue: 102431 +.. nonce: eUDnf4 +.. section: Library + +Clarify constraints for "logical" arguments in methods of +:class:`decimal.Context`. + +.. + +.. date: 2019-06-02-13-56-16 +.. gh-issue: 81313 +.. nonce: axawSH +.. section: Library + +Add the :mod:`math.integer` module (:pep:`791`). + +.. + +.. date: 2025-11-15-01-21-00 +.. gh-issue: 141579 +.. nonce: aB7cD9 +.. section: Core and Builtins + +Fix :func:`sys.activate_stack_trampoline` to properly support the +``perf_jit`` backend. Patch by Pablo Galindo. + +.. + +.. date: 2025-11-14-16-25-15 +.. gh-issue: 114203 +.. nonce: n3tlQO +.. section: Core and Builtins + +Skip locking if object is already locked by two-mutex critical section. + +.. + +.. date: 2025-11-14-00-19-45 +.. gh-issue: 141528 +.. nonce: VWdax1 +.. section: Core and Builtins + +Suggest using :meth:`concurrent.interpreters.Interpreter.close` instead of +the private ``_interpreters.destroy`` function when warning about remaining +subinterpreters. Patch by Sergey Miryanov. + +.. + +.. date: 2025-11-11-13-40-45 +.. gh-issue: 141367 +.. nonce: I5KY7F +.. section: Core and Builtins + +Specialize ``CALL_LIST_APPEND`` instruction only for lists, not for list +subclasses, to avoid unnecessary deopt. Patch by Mikhail Efimov. + +.. + +.. date: 2025-11-10-23-07-06 +.. gh-issue: 141312 +.. nonce: H-58GB +.. section: Core and Builtins + +Fix the assertion failure in the ``__setstate__`` method of the range +iterator when a non-integer argument is passed. Patch by Sergey Miryanov. + +.. + +.. date: 2025-11-05-19-50-37 +.. gh-issue: 140643 +.. nonce: QCEOqG +.. section: Core and Builtins + +Add support for ``<GC>`` and ``<native>`` frames to +:mod:`!profiling.sampling` output to denote active garbage collection and +calls to native code. + +.. + +.. date: 2025-11-04-12-18-06 +.. gh-issue: 140942 +.. nonce: GYns6n +.. section: Library + +Add ``.cjs`` to :mod:`mimetypes` to give CommonJS modules a MIME type of +``application/node``. + +.. + +.. date: 2025-11-04-04-57-24 +.. gh-issue: 140479 +.. nonce: lwQ2v2 +.. section: Core and Builtins + +Update JIT compilation to use LLVM 21 at build time. + +.. + +.. date: 2025-11-03-17-21-38 +.. gh-issue: 140939 +.. nonce: FVboAw +.. section: Core and Builtins + +Fix memory leak when :class:`bytearray` or :class:`bytes` is formated with +the ``%*b`` format with a large width that results in a :exc:`MemoryError`. + +.. + +.. date: 2025-11-02-15-28-33 +.. gh-issue: 140260 +.. nonce: JNzlGz +.. section: Library + +Fix :mod:`struct` data race in endian table initialization with +subinterpreters. Patch by Shamil Abdulaev. + +.. + +.. date: 2025-11-02-12-47-38 +.. gh-issue: 140530 +.. nonce: S934bp +.. section: Core and Builtins + +Fix a reference leak when ``raise exc from cause`` fails. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-10-31-14-03-42 +.. gh-issue: 90344 +.. nonce: gvZigO +.. section: Library + +Replace :class:`io.IncrementalNewlineDecoder` with non incremental newline +decoders in codebase where :meth:`!io.IncrementalNewlineDecoder.decode` was +being called once. + +.. + +.. date: 2025-10-29-20-59-10 +.. gh-issue: 140373 +.. nonce: -uoaPP +.. section: Core and Builtins + +Correctly emit ``PY_UNWIND`` event when generator object is closed. Patch by +Mikhail Efimov. + +.. + +.. date: 2025-10-29-11-31-59 +.. gh-issue: 140729 +.. nonce: t9JsNt +.. section: Core and Builtins + +Fix pickling error in the sampling profiler when using +``concurrent.futures.ProcessPoolExecutor`` script can not be properly +pickled and executed in worker processes. + +.. + +.. date: 2025-10-25-21-31-43 +.. gh-issue: 131527 +.. nonce: V-JVNP +.. section: Core and Builtins + +Dynamic borrow checking for stackrefs is added to ``Py_STACKREF_DEBUG`` +mode. Patch by Mikhail Efimov. + +.. + +.. date: 2025-10-25-17-36-46 +.. gh-issue: 140576 +.. nonce: kj0SCY +.. section: Core and Builtins + +Fixed crash in :func:`tokenize.generate_tokens` in case of specific +incorrect input. Patch by Mikhail Efimov. + +.. + +.. date: 2025-10-25-07-25-52 +.. gh-issue: 140544 +.. nonce: lwjtQe +.. section: Core and Builtins + +Speed up accessing interpreter state by caching it in a thread local +variable. Patch by Kumar Aditya. + +.. + +.. date: 2025-10-24-20-42-33 +.. gh-issue: 140551 +.. nonce: -9swrl +.. section: Core and Builtins + +Fixed crash in :class:`dict` if :meth:`dict.clear` is called at the lookup +stage. Patch by Mikhail Efimov and Inada Naoki. + +.. + +.. date: 2025-10-24-20-16-42 +.. gh-issue: 140517 +.. nonce: cqun-K +.. section: Core and Builtins + +Fixed a reference leak when iterating over the result of :func:`map` with +``strict=True`` when the input iterables have different lengths. Patch by +Mikhail Efimov. + +.. + +.. date: 2025-10-24-14-29-12 +.. gh-issue: 133467 +.. nonce: A5d6TM +.. section: Core and Builtins + +Fix race when updating :attr:`!type.__bases__` that could allow a read of +:attr:`!type.__base__` to observe an inconsistent value on the free threaded +build. + +.. + +.. date: 2025-10-23-16-05-50 +.. gh-issue: 140471 +.. nonce: Ax_aXn +.. section: Core and Builtins + +Fix potential buffer overflow in :class:`ast.AST` node initialization when +encountering malformed :attr:`~ast.AST._fields` containing non-:class:`str`. + +.. + +.. date: 2025-10-22-23-26-37 +.. gh-issue: 140443 +.. nonce: wT5i1A +.. section: Library + +The logarithm functions (such as :func:`math.log10` and :func:`math.log`) +may now produce slightly different results for extremely large integers that +cannot be converted to floats without overflow. These results are generally +more accurate, with reduced worst-case error and a tighter overall error +distribution. + +.. + +.. date: 2025-10-22-17-22-22 +.. gh-issue: 140431 +.. nonce: m8D_A- +.. section: Core and Builtins + +Fix a crash in Python's :term:`garbage collector <garbage collection>` due +to partially initialized :term:`coroutine` objects when coroutine origin +tracking depth is enabled (:func:`sys.set_coroutine_origin_tracking_depth`). + +.. + +.. date: 2025-10-22-12-48-05 +.. gh-issue: 140476 +.. nonce: F3-d1P +.. section: Core and Builtins + +Optimize :c:func:`PySet_Add` for :class:`frozenset` in :term:`free threaded +<free threading>` build. + +.. + +.. date: 2025-10-22-11-30-16 +.. gh-issue: 135904 +.. nonce: 3WE5oW +.. section: Core and Builtins + +Add special labels to the assembly created during stencil creation to +support relocations that the native object file format does not support. +Specifically, 19 bit branches for AArch64 in Mach-O object files. + +.. + +.. date: 2025-10-21-09-20-03 +.. gh-issue: 140398 +.. nonce: SoABwJ +.. section: Library + +Fix memory leaks in :mod:`readline` functions +:func:`~readline.read_init_file`, :func:`~readline.read_history_file`, +:func:`~readline.write_history_file`, and +:func:`~readline.append_history_file` when :c:func:`PySys_Audit` fails. + +.. + +.. date: 2025-10-21-06-51-50 +.. gh-issue: 140406 +.. nonce: 0gJs8M +.. section: Core and Builtins + +Fix memory leak when an object's :meth:`~object.__hash__` method returns an +object that isn't an :class:`int`. + +.. + +.. date: 2025-10-20-11-24-36 +.. gh-issue: 140358 +.. nonce: UQuKdV +.. section: Core and Builtins + +Restore elapsed time and unreachable object count in GC debug output. These +were inadvertently removed during a refactor of ``gc.c``. The debug log now +again reports elapsed collection time and the number of unreachable objects. +Contributed by Pål Grønås Drange. + +.. + +.. date: 2025-10-19-10-32-28 +.. gh-issue: 136895 +.. nonce: HfsEh0 +.. section: Core and Builtins + +Update JIT compilation to use LLVM 20 at build time. + +.. + +.. date: 2025-10-18-21-50-44 +.. gh-issue: 139109 +.. nonce: 9QQOzN +.. section: Core and Builtins + +A new tracing frontend for the JIT compiler has been implemented. Patch by +Ken Jin. Design for CPython by Ken Jin, Mark Shannon and Brandt Bucher. + +.. + +.. date: 2025-10-18-21-29-45 +.. gh-issue: 140306 +.. nonce: xS5CcS +.. section: Core and Builtins + +Fix memory leaks in cross-interpreter channel operations and shared +namespace handling. + +.. + +.. date: 2025-10-18-19-52-20 +.. gh-issue: 116738 +.. nonce: NLJW0L +.. section: Core and Builtins + +Make _suggestions module thread-safe on the :term:`free threaded <free +threading>` build. + +.. + +.. date: 2025-10-18-18-08-36 +.. gh-issue: 140301 +.. nonce: m-2HxC +.. section: Core and Builtins + +Fix memory leak of ``PyConfig`` in subinterpreters. + +.. + +.. date: 2025-10-17-20-23-19 +.. gh-issue: 140257 +.. nonce: 8Txmem +.. section: Core and Builtins + +Fix data race between interpreter_clear() and take_gil() on eval_breaker +during finalization with daemon threads. + +.. + +.. date: 2025-10-17-18-03-12 +.. gh-issue: 139951 +.. nonce: IdwM2O +.. section: Core and Builtins + +Fixes a regression in GC performance for a growing heap composed mostly of +small tuples. + +* Counts number of actually tracked objects, instead of trackable objects. + This ensures that untracking tuples has the desired effect of reducing GC overhead. +* Does not track most untrackable tuples during creation. + This prevents large numbers of small tuples causing excessive GCs. + +.. + +.. date: 2025-10-17-14-38-10 +.. gh-issue: 140253 +.. nonce: gCqFaL +.. section: Core and Builtins + +Wrong placement of a double-star pattern inside a mapping pattern now throws +a specialized syntax error. Contributed by Bartosz Sławecki in :gh:`140253`. + +.. + +.. date: 2025-10-16-21-47-00 +.. gh-issue: 140104 +.. nonce: A8SQIm +.. section: Core and Builtins + +Fix a bug with exception handling in the JIT. Patch by Ken Jin. Bug reported +by Daniel Diniz. + +.. + +.. date: 2025-10-15-17-12-32 +.. gh-issue: 140149 +.. nonce: cy1m3d +.. section: Core and Builtins + +Speed up parsing bytes literals concatenation by using PyBytesWriter API and +a single memory allocation (about 3x faster). + +.. + +.. date: 2025-10-15-00-21-40 +.. gh-issue: 140061 +.. nonce: J0XeDV +.. section: Core and Builtins + +Fixing the checking of whether an object is uniquely referenced to ensure +free-threaded compatibility. Patch by Sergey Miryanov. + +.. + +.. date: 2025-10-14-20-18-31 +.. gh-issue: 140080 +.. nonce: 8ROjxW +.. section: Core and Builtins + +Fix hang during finalization when attempting to call :mod:`atexit` handlers +under no memory. + +.. + +.. date: 2025-10-14-18-24-16 +.. gh-issue: 139871 +.. nonce: SWtuUz +.. section: Core and Builtins + +Update :class:`bytearray` to use a :class:`bytes` under the hood as its +buffer and add :meth:`bytearray.take_bytes` to take it out. + +.. + +.. date: 2025-10-14-17-07-37 +.. gh-issue: 140067 +.. nonce: ID2gOm +.. section: Core and Builtins + +Fix memory leak in sub-interpreter creation. + +.. + +.. date: 2025-10-13-13-54-19 +.. gh-issue: 139914 +.. nonce: M-y_3E +.. section: Core and Builtins + +Restore support for HP PA-RISC, which has an upwards-growing stack. + +.. + +.. date: 2025-10-12-01-12-12 +.. gh-issue: 139817 +.. nonce: PAn-8Z +.. section: Core and Builtins + +Attribute ``__qualname__`` is added to :class:`typing.TypeAliasType`. Patch +by Mikhail Efimov. + +.. + +.. date: 2025-10-06-14-19-47 +.. gh-issue: 135801 +.. nonce: OhxEZS +.. section: Core and Builtins + +Many functions related to compiling or parsing Python code, such as +:func:`compile`, :func:`ast.parse`, :func:`symtable.symtable`, and +:func:`importlib.abc.InspectLoader.source_to_code` now allow to specify the +module name. It is needed to unambiguous :ref:`filter <warning-filter>` +syntax warnings by module name. + +.. + +.. date: 2025-10-06-10-03-37 +.. gh-issue: 139640 +.. nonce: gY5oTb2 +.. section: Core and Builtins + +:func:`ast.parse` no longer emits syntax warnings for +``return``/``break``/``continue`` in ``finally`` (see :pep:`765`) -- they +are only emitted during compilation. + +.. + +.. date: 2025-10-06-10-03-37 +.. gh-issue: 139640 +.. nonce: gY5oTb +.. section: Core and Builtins + +Fix swallowing some syntax warnings in different modules if they +accidentally have the same message and are emitted from the same line. Fix +duplicated warnings in the ``finally`` block. + +.. + +.. date: 2025-10-03-17-51-43 +.. gh-issue: 139475 +.. nonce: _684ED +.. section: Core and Builtins + +Changes in stackref debugging mode when ``Py_STACKREF_DEBUG`` is set. We use +the same pattern of refcounting for stackrefs as in production build. + +.. + +.. date: 2025-09-23-21-01-12 +.. gh-issue: 139269 +.. nonce: 1rIaxy +.. section: Core and Builtins + +Fix undefined behavior when using unaligned store in JIT's ``patch_*`` +functions. + +.. + +.. date: 2025-09-15-13-06-11 +.. gh-issue: 138944 +.. nonce: PeCgLb +.. section: Core and Builtins + +Fix :exc:`SyntaxError` message when invalid syntax appears on the same line +as a valid ``import ... as ...`` or ``from ... import ... as ...`` +statement. Patch by Brian Schubert. + +.. + +.. date: 2025-09-13-01-23-25 +.. gh-issue: 138857 +.. nonce: YQ5gdc +.. section: Core and Builtins + +Improve :exc:`SyntaxError` message for ``case`` keyword placed outside +:keyword:`match` body. + +.. + +.. date: 2025-07-29-17-51-14 +.. gh-issue: 131253 +.. nonce: GpRjWy +.. section: Core and Builtins + +Support the ``--enable-pystats`` build option for the free-threaded build. + +.. + +.. date: 2025-07-08-00-41-46 +.. gh-issue: 136327 +.. nonce: 7AiTb_ +.. section: Core and Builtins + +Errors when calling functions with invalid values after ``*`` and ``**`` now +do not include the function name. Patch by Ilia Solin. + +.. + +.. date: 2025-06-24-13-12-58 +.. gh-issue: 134786 +.. nonce: MF0VVk +.. section: Core and Builtins + +If :c:macro:`Py_TPFLAGS_MANAGED_DICT` and +:c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` are used, then +:c:macro:`Py_TPFLAGS_HAVE_GC` must be used as well. + +.. + +.. date: 2025-11-10-11-26-26 +.. gh-issue: 141341 +.. nonce: OsO6-y +.. section: C API + +On Windows, rename the ``COMPILER`` macro to ``_Py_COMPILER`` to avoid name +conflicts. Patch by Victor Stinner. + +.. + +.. date: 2025-11-08-10-51-50 +.. gh-issue: 116146 +.. nonce: pCmx6L +.. section: C API + +Add a new :c:func:`PyImport_CreateModuleFromInitfunc` C-API for creating a +module from a *spec* and *initfunc*. Patch by Itamar Oren. + +.. + +.. date: 2025-11-06-06-28-14 +.. gh-issue: 141042 +.. nonce: brOioJ +.. section: C API + +Make qNaN in :c:func:`PyFloat_Pack2` and :c:func:`PyFloat_Pack4`, if while +conversion to a narrower precision floating-point format --- the remaining +after truncation payload will be zero. Patch by Sergey B Kirpichev. + +.. + +.. date: 2025-11-05-05-45-49 +.. gh-issue: 141004 +.. nonce: N9Ooh9 +.. section: C API + +:c:macro:`!Py_MATH_El` and :c:macro:`!Py_MATH_PIl` are deprecated. + +.. + +.. date: 2025-11-05-04-38-16 +.. gh-issue: 141004 +.. nonce: rJL43P +.. section: C API + +The :c:macro:`!Py_INFINITY` macro is :term:`soft deprecated`. + +.. + +.. date: 2025-10-26-16-45-28 +.. gh-issue: 140556 +.. nonce: s__Dae +.. section: C API + +:pep:`793`: Add a new entry point for C extension modules, +``PyModExport_<modulename>``. + +.. + +.. date: 2025-10-26-16-45-06 +.. gh-issue: 140487 +.. nonce: fGOqss +.. section: C API + +Fix :c:macro:`Py_RETURN_NOTIMPLEMENTED` in limited C API 3.11 and older: +don't treat ``Py_NotImplemented`` as immortal. Patch by Victor Stinner. + +.. + +.. date: 2025-10-15-15-59-59 +.. gh-issue: 140153 +.. nonce: BO7sH4 +.. section: C API + +Fix :c:func:`Py_REFCNT` definition on limited C API 3.11-3.13. Patch by +Victor Stinner. + +.. + +.. date: 2025-10-06-22-17-47 +.. gh-issue: 139653 +.. nonce: 6-1MOd +.. section: C API + +Add :c:func:`PyUnstable_ThreadState_SetStackProtection` and +:c:func:`PyUnstable_ThreadState_ResetStackProtection` functions to set the +stack protection base address and stack protection size of a Python thread +state. Patch by Victor Stinner. + +.. + +.. date: 2025-10-31-13-20-16 +.. gh-issue: 140454 +.. nonce: gF6dCe +.. section: Build + +When building the JIT, match the jit_stencils filename expectations in +Makefile with the generator script. This avoid needless JIT recompilation +during ``make install``. + +.. + +.. date: 2025-10-29-12-30-38 +.. gh-issue: 140768 +.. nonce: ITYrzw +.. section: Build + +Warn when the WASI SDK version doesn't match what's supported. + +.. + +.. date: 2025-10-25-08-07-06 +.. gh-issue: 140513 +.. nonce: 6OhLTs +.. section: Build + +Generate a clear compilation error when ``_Py_TAIL_CALL_INTERP`` is enabled +but either ``preserve_none`` or ``musttail`` is not supported. + +.. + +.. date: 2025-10-22-12-44-07 +.. gh-issue: 140475 +.. nonce: OhzQbR +.. section: Build + +Support WASI SDK 25. + +.. + +.. date: 2025-10-17-11-33-45 +.. gh-issue: 140239 +.. nonce: _k-GgW +.. section: Build + +Check ``statx`` availability only on Linux (including Android). + +.. + +.. date: 2025-10-16-11-30-53 +.. gh-issue: 140189 +.. nonce: YCrUyt +.. section: Build + +iOS builds were added to CI. + +.. + +.. date: 2025-08-10-22-28-06 +.. gh-issue: 137618 +.. nonce: FdNvIE +.. section: Build + +``PYTHON_FOR_REGEN`` now requires Python 3.10 to Python 3.15. Patch by Adam +Turner. diff --git a/Misc/NEWS.d/3.15.0a3.rst b/Misc/NEWS.d/3.15.0a3.rst new file mode 100644 index 00000000000000..8f4200cec1d61a --- /dev/null +++ b/Misc/NEWS.d/3.15.0a3.rst @@ -0,0 +1,1596 @@ +.. date: 2025-11-18-13-55-47 +.. gh-issue: 141692 +.. nonce: tud9if +.. release date: 2025-12-16 +.. section: Tools/Demos + +Each slice of an iOS XCframework now contains a ``lib`` folder that contains +a symlink to the libpython dylib. This allows binary modules to be compiled +for iOS using dynamic libreary linking, rather than Framework linking. + +.. + +.. date: 2025-10-27-15-53-47 +.. gh-issue: 140381 +.. nonce: N5o3pa +.. section: Tests + +Fix flaky test_profiling tests on i686 and s390x architectures by increasing +slow_fibonacci call frequency from every 5th iteration to every 2nd +iteration. + +.. + +.. date: 2025-10-16-15-08-58 +.. gh-issue: 140210 +.. nonce: P9vUP8 +.. section: Tests + +Make ``test_sysconfig.test_parse_makefile_renamed_vars`` less fragile by +clearing the environment variables before parsing the Makefile. + +.. + +.. date: 2025-12-01-09-36-45 +.. gh-issue: 142145 +.. nonce: tcAUhg +.. section: Security + +Remove quadratic behavior in ``xml.minidom`` node ID cache clearing. + +.. + +.. date: 2025-11-13-22-31-56 +.. gh-issue: 42400 +.. nonce: pqB5Kq +.. section: Security + +Fix buffer overflow in ``_Py_wrealpath()`` for paths exceeding +``MAXPATHLEN`` bytes by using dynamic memory allocation instead of +fixed-size buffer. Patch by Shamil Abdulaev. + +.. + +.. date: 2024-05-23-11-47-48 +.. gh-issue: 119451 +.. nonce: qkJe9- +.. section: Security + +Fix a potential memory denial of service in the :mod:`http.client` module. +When connecting to a malicious server, it could cause an arbitrary amount of +memory to be allocated. This could have led to symptoms including a +:exc:`MemoryError`, swapping, out of memory (OOM) killed processes or +containers, or even system crashes. + +.. + +.. date: 2024-05-21-22-11-31 +.. gh-issue: 119342 +.. nonce: BTFj4Z +.. section: Security + +Fix a potential memory denial of service in the :mod:`plistlib` module. When +reading a Plist file received from untrusted source, it could cause an +arbitrary amount of memory to be allocated. This could have led to symptoms +including a :exc:`MemoryError`, swapping, out of memory (OOM) killed +processes or containers, or even system crashes. + +.. + +.. date: 2025-12-16-11-55-55 +.. gh-issue: 142754 +.. nonce: xuCrt3 +.. section: Library + +Add the *ownerDocument* attribute to :mod:`xml.dom.minidom` elements and +attributes created by directly instantiating the ``Element`` or ``Attr`` +class. Note that this way of creating nodes is not supported; creator +functions like :py:meth:`xml.dom.Document.documentElement` should be used +instead. + +.. + +.. date: 2025-12-14-18-30-48 +.. gh-issue: 142594 +.. nonce: belDmD +.. section: Library + +Fix crash in ``TextIOWrapper.close()`` when the underlying buffer's +``closed`` property calls :meth:`~io.TextIOBase.detach`. + +.. + +.. date: 2025-12-13-21-19-28 +.. gh-issue: 76007 +.. nonce: 6fs_gT +.. section: Library + +Deprecate ``__version__`` from :mod:`ctypes`. Patch by Hugo van Kemenade. + +.. + +.. date: 2025-12-13-19-17-01 +.. gh-issue: 76007 +.. nonce: -OSQU3 +.. section: Library + +Deprecate ``__version__`` from :mod:`wsgiref.simple_server`. Patch by Hugo +van Kemenade. + +.. + +.. date: 2025-12-13-06-17-44 +.. gh-issue: 142651 +.. nonce: ZRtBu4 +.. section: Library + +:mod:`unittest.mock`: fix a thread safety issue where :attr:`Mock.call_count +<unittest.mock.Mock.call_count>` may return inaccurate values when the mock +is called concurrently from multiple threads. + +.. + +.. date: 2025-12-13-00-09-09 +.. gh-issue: 76007 +.. nonce: Xg1xCO +.. section: Library + +Deprecate ``__version__`` from :mod:`http.server`. Patch by Hugo van +Kemenade. + +.. + +.. date: 2025-12-12-15-14-03 +.. gh-issue: 138122 +.. nonce: m3EF9E +.. section: Library + +Add ``--subprocesses`` flag to :mod:`profiling.sampling` CLI to +automatically profile subprocesses spawned by the target. When enabled, the +profiler monitors for new Python subprocesses and profiles each one +separately, writing results to individual output files. This is useful for +profiling applications that use :mod:`multiprocessing`, +:class:`~concurrent.futures.ProcessPoolExecutor`, or other subprocess-based +parallelism. Patch by Pablo Galindo. + +.. + +.. date: 2025-12-12-02-56-26 +.. gh-issue: 142595 +.. nonce: wHvTqq +.. section: Library + +Added type check during initialization of the :mod:`decimal` module to +prevent a crash in case of broken stdlib. Patch by Sergey B Kirpichev. + +.. + +.. date: 2025-12-11-09-03-07 +.. gh-issue: 142556 +.. nonce: RuiBte +.. section: Library + +Fix crash when a task gets re-registered during finalization in +:mod:`asyncio`. Patch by Kumar Aditya. + +.. + +.. date: 2025-12-11-04-18-49 +.. gh-issue: 138122 +.. nonce: m3EF9E +.. section: Library + +Add ``--mode=exception`` to the sampling profiler to capture samples only +from threads with an active exception, useful for analyzing exception +handling overhead. Patch by Pablo Galindo. + +.. + +.. date: 2025-12-10-21-19-10 +.. gh-issue: 142539 +.. nonce: _8Vzr0 +.. section: Library + +:mod:`traceback`: Fix location of carets in :exc:`SyntaxError`\s when the +source contains wide characters. + +.. + +.. date: 2025-12-10-11-20-05 +.. gh-issue: 123241 +.. nonce: oYg2n7 +.. section: Library + +Avoid reference count operations in garbage collection of :mod:`ctypes` +objects. + +.. + +.. date: 2025-12-10-11-02-53 +.. gh-issue: 142451 +.. nonce: eCLvhG +.. section: Library + +:mod:`hmac`: correctly copy :class:`~hmac.HMAC` attributes for objects +copied through :meth:`HMAC.copy() <hmac.HMAC.copy>`. Patch by Bénédikt Tran. + +.. + +.. date: 2025-12-09-22-11-59 +.. gh-issue: 138122 +.. nonce: CsoBEo +.. section: Library + +The ``profiling.sampling`` flamegraph profiler now supports inverted +flamegraph view that aggregates all leaf nodes. In a standard flamegraph, if +a hot function is called from multiple locations, it appears multiple times +as separate leaf nodes. In the inverted flamegraph, all occurrences of the +same leaf function are merged into a single aggregated node at the root, +showing the total hotness of that function in one place. The children of +each aggregated node represent its callers, making it easier to identify +which functions consume the most CPU time and where they are called from. + +.. + +.. date: 2025-12-09-14-40-45 +.. gh-issue: 112527 +.. nonce: Tvf5Zk +.. section: Library + +The help text for required options in :mod:`argparse` no longer extended +with " (default: None)". + +.. + +.. date: 2025-12-08-18-12-44 +.. gh-issue: 142438 +.. nonce: UF_0nd +.. section: Library + +Fixed a possible leaked GIL in _PySSL_keylog_callback. + +.. + +.. date: 2025-12-07-23-21-13 +.. gh-issue: 138122 +.. nonce: m3EF9E +.. section: Library + +Add bytecode-level instruction profiling to the sampling profiler via the +new ``--opcodes`` flag. When enabled, the profiler captures which bytecode +opcode is executing at each sample, including Python 3.11+ adaptive +specializations, and visualizes this data in the heatmap, flamegraph, gecko, +and live output formats. Patch by Pablo Galindo + +.. + +.. date: 2025-12-07-22-13-28 +.. gh-issue: 142389 +.. nonce: J9v904 +.. section: Library + +Add backtick markup support in :mod:`argparse` description and epilog text +to highlight inline code when color output is enabled. + +.. + +.. date: 2025-12-07-17-30-05 +.. gh-issue: 142346 +.. nonce: okcAAp +.. section: Library + +Fix usage formatting for mutually exclusive groups in :mod:`argparse` when +they are preceded by positional arguments or followed or intermixed with +other optional arguments. + +.. + +.. date: 2025-12-07-13-37-18 +.. gh-issue: 142374 +.. nonce: m3EF9E +.. section: Library + +Fix cumulative percentage calculation for recursive functions in the new +sampling profiler. When profiling recursive functions, cumulative statistics +(cumul%, cumtime) could exceed 100% because each recursive frame in a stack +was counted separately. For example, a function recursing 500 times in every +sample would show 50000% cumulative presence. The fix deduplicates locations +within each sample so cumulative stats correctly represent "percentage of +samples where this function was on the stack". Patch by Pablo Galindo. + +.. + +.. date: 2025-12-07-02-36-24 +.. gh-issue: 142315 +.. nonce: 02o5E_ +.. section: Library + +Pdb can now run scripts from anonymous pipes used in process substitution. +Patch by Bartosz Sławecki. + +.. + +.. date: 2025-12-06-16-45-34 +.. gh-issue: 64532 +.. nonce: 4OXZpF +.. section: Library + +Subparser help now includes required optional arguments from the parent +parser in the usage, making it clearer what arguments are needed to run a +subcommand. Patch by Savannah Ostrowski. + +.. + +.. date: 2025-12-06-13-19-43 +.. gh-issue: 142207 +.. nonce: x_X9oH +.. section: Library + +Fix: profiling.sampling may cause assertion ``!(has_gil && gil_requested)`` + +.. + +.. date: 2025-12-06-13-02-13 +.. gh-issue: 142332 +.. nonce: PNvXCV +.. section: Library + +Fix usage formatting for positional arguments in mutually exclusive groups +in :mod:`argparse`. in :mod:`argparse`. + +.. + +.. date: 2025-12-05-18-26-50 +.. gh-issue: 142282 +.. nonce: g6RQUN +.. section: Library + +Fix :func:`winreg.QueryValueEx` to not accidentally read garbage buffer +under race condition. + +.. + +.. date: 2025-12-05-18-25-29 +.. gh-issue: 142318 +.. nonce: EzcQ3N +.. section: Library + +Fix typing ``'q'`` at the help of the interactive tachyon profiler exiting +the profiler. + +.. + +.. date: 2025-12-05-16-39-17 +.. gh-issue: 75949 +.. nonce: pHxW98 +.. section: Library + +Fix :mod:`argparse` to preserve ``|`` separators in mutually exclusive +groups when the usage line wraps due to length. + +.. + +.. date: 2025-12-04-23-26-12 +.. gh-issue: 142267 +.. nonce: yOM6fP +.. section: Library + +Improve :mod:`argparse` performance by caching the formatter used for +argument validation. + +.. + +.. date: 2025-12-04-23-24-24 +.. gh-issue: 139862 +.. nonce: NBfsD4 +.. section: Library + +Remove ``color`` parameter from :class:`!argparse.HelpFormatter` +constructor. Color is controlled by :class:`~argparse.ArgumentParser`. + +.. + +.. date: 2025-12-04-09-22-31 +.. gh-issue: 68552 +.. nonce: I_v-xB +.. section: Library + +``MisplacedEnvelopeHeaderDefect`` and ``Missing header name`` defects are +now correctly passed to the ``handle_defect`` method of ``policy`` in +:class:`~email.parser.FeedParser`. + +.. + +.. date: 2025-12-03-09-36-29 +.. gh-issue: 142206 +.. nonce: ilwegH +.. section: Library + +The resource tracker in the :mod:`multiprocessing` module can now understand +messages from older versions of itself. This avoids issues with upgrading +Python while it is running. (Note that such 'in-place' upgrades are not +tested.) + +.. + +.. date: 2025-12-03-06-12-39 +.. gh-issue: 142214 +.. nonce: appYNZ +.. section: Library + +Fix two regressions in :mod:`dataclasses` in Python 3.14.1 related to +annotations. + +* An exception is no longer raised if ``slots=True`` is used and the + ``__init__`` method does not have an ``__annotate__`` attribute + (likely because ``init=False`` was used). + +* An exception is no longer raised if annotations are requested on the + ``__init__`` method and one of the fields is not present in the class + annotations. This can occur in certain dynamic scenarios. + +Patch by Jelle Zijlstra. + +.. + +.. date: 2025-12-02-14-52-51 +.. gh-issue: 142203 +.. nonce: ofWOvV +.. section: Library + +Remove the *debug_override* parameter from +:func:`importlib.util.cache_from_source` which has been deprecated since +Python 3.5. + +.. + +.. date: 2025-12-01-14-43-58 +.. gh-issue: 138122 +.. nonce: nRm3ic +.. section: Library + +The ``_remote_debugging`` module now implements frame caching in the +``RemoteUnwinder`` class to reduce memory reads when profiling remote +processes. When ``cache_frames=True``, unchanged portions of the call stack +are reused from previous samples, significantly improving profiling +performance for deep call stacks. + +.. + +.. date: 2025-12-01-10-03-08 +.. gh-issue: 116738 +.. nonce: 972YsG +.. section: Library + +Fix :mod:`cmath` data race when initializing trigonometric tables with +subinterpreters. + +.. + +.. date: 2025-11-30-04-28-30 +.. gh-issue: 141982 +.. nonce: pxZct9 +.. section: Library + +Allow :mod:`pdb` to set breakpoints on async functions with function names. + +.. + +.. date: 2025-11-29-04-20-44 +.. gh-issue: 74389 +.. nonce: pW3URj +.. section: Library + +When the stdin being used by a :class:`subprocess.Popen` instance is closed, +this is now ignored in :meth:`subprocess.Popen.communicate` instead of +leaving the class in an inconsistent state. + +.. + +.. date: 2025-11-29-03-02-45 +.. gh-issue: 87512 +.. nonce: bn4xbm +.. section: Library + +Fix :func:`subprocess.Popen.communicate` timeout handling on Windows when +writing large input. Previously, the timeout was ignored during stdin +writing, causing the method to block indefinitely if the child process did +not consume input quickly. The stdin write is now performed in a background +thread, allowing the timeout to be properly enforced. + +.. + +.. date: 2025-11-28-08-25-19 +.. gh-issue: 141939 +.. nonce: BXPnFj +.. section: Library + +Add color to all interpolated values in :mod:`argparse` help, like +``%(default)s`` or ``%(choices)s``. Patch by Alex Prengère. + +.. + +.. date: 2025-11-27-20-16-38 +.. gh-issue: 141473 +.. nonce: Wq4xVN +.. section: Library + +When :meth:`subprocess.Popen.communicate` was called with *input* and a +*timeout* and is called for a second time after a +:exc:`~subprocess.TimeoutExpired` exception before the process has died, it +should no longer hang. + +.. + +.. date: 2025-11-27-11-39-50 +.. gh-issue: 141999 +.. nonce: _FKGlu +.. section: Library + +Correctly allow :exc:`KeyboardInterrupt` to stop the process when using +:mod:`!profiling.sampling`. + +.. + +.. date: 2025-11-27-10-49-13 +.. gh-issue: 142006 +.. nonce: nzJDG5 +.. section: Library + +Fix a bug in the :mod:`email.policy.default` folding algorithm which +incorrectly resulted in a doubled newline when a line ending at exactly +max_line_length was followed by an unfoldable token. + +.. + +.. date: 2025-11-26-14-20-10 +.. gh-issue: 141968 +.. nonce: W139Pv +.. section: Library + +Remove data copy from :mod:`re` compilation of regexes with large charsets +by using :meth:`bytearray.take_bytes`. + +.. + +.. date: 2025-11-25-23-35-07 +.. gh-issue: 141968 +.. nonce: b3Gscp +.. section: Library + +Remove data copy from :mod:`encodings.idna` :meth:`~codecs.Codec.encode` and +:meth:`~codecs.IncrementalEncoder.encode` by using +:meth:`bytearray.take_bytes`. + +.. + +.. date: 2025-11-25-23-29-08 +.. gh-issue: 141968 +.. nonce: 0JnjXf +.. section: Library + +Remove data copy from :mod:`codecs` ``punycode`` encoding by using +:meth:`bytearray.take_bytes`. + +.. + +.. date: 2025-11-25-23-22-46 +.. gh-issue: 141968 +.. nonce: R1sHnJ +.. section: Library + +Remove data copy from :func:`wave.Wave_read.readframes` and +:func:`wave.Wave_write.writeframes` by using :meth:`bytearray.take_bytes`. + +.. + +.. date: 2025-11-25-22-54-07 +.. gh-issue: 141968 +.. nonce: vg3AMJ +.. section: Library + +Remove a data copy from :func:`base64.b32decode` and +:func:`base64.b32encode` by using :meth:`bytearray.take_bytes`. + +.. + +.. date: 2025-11-25-16-00-29 +.. gh-issue: 59000 +.. nonce: YtOyJy +.. section: Library + +Fix :mod:`pdb` breakpoint resolution for class methods when the module +defining the class is not imported. + +.. + +.. date: 2025-11-25-13-13-34 +.. gh-issue: 116738 +.. nonce: MnZRdV +.. section: Library + +Fix thread safety issue with :mod:`re` scanner objects in free-threaded +builds. + +.. + +.. date: 2025-11-24-14-05-52 +.. gh-issue: 138122 +.. nonce: 2bbGA8 +.. section: Library + +The ``profiling.sampling`` flamegraph profiler now displays thread status +statistics showing the percentage of time threads spend holding the GIL, +running without the GIL, waiting for the GIL, and performing garbage +collection. These statistics help identify GIL contention and thread +behavior patterns. When filtering by thread, the display shows per-thread +metrics. + +.. + +.. date: 2025-11-24-06-44-45 +.. gh-issue: 141781 +.. nonce: MsK27r +.. section: Library + +Fixed an issue where pdb.line_prefix assignment was ignored if assigned +after the module was imported. + +.. + +.. date: 2025-11-22-16-33-48 +.. gh-issue: 141863 +.. nonce: 4PLhnv +.. section: Library + +Update :ref:`asyncio-streams` to use :meth:`bytearray.take_bytes` for a over +10% performance improvement on pyperformance asyncio_tcp benchmark. + +.. + +.. date: 2025-11-21-21-14-10 +.. gh-issue: 141817 +.. nonce: _v5LdB +.. section: Library + +Add :data:`!socket.IPV6_HDRINCL` constant. + +.. + +.. date: 2025-11-18-15-48-13 +.. gh-issue: 105836 +.. nonce: sbUw24 +.. section: Library + +Fix :meth:`asyncio.run_coroutine_threadsafe` leaving underlying cancelled +asyncio task running. + +.. + +.. date: 2025-11-18-14-39-31 +.. gh-issue: 141570 +.. nonce: q3n984 +.. section: Library + +Support :term:`file-like object` raising :exc:`OSError` from +:meth:`~io.IOBase.fileno` in color detection (``_colorize.can_colorize()``). +This can occur when ``sys.stdout`` is redirected. + +.. + +.. date: 2025-11-17-21-41-58 +.. gh-issue: 141679 +.. nonce: fs7zLJ +.. section: Library + +Add colour to defaults in :mod:`argparse` help. Patch by Hugo van Kemenade. + +.. + +.. date: 2025-11-17-16-53-49 +.. gh-issue: 141686 +.. nonce: V-xaoI +.. section: Library + +Break reference cycles created by each call to :func:`json.dump` or +:meth:`json.JSONEncoder.iterencode`. + +.. + +.. date: 2025-11-17-08-16-30 +.. gh-issue: 141659 +.. nonce: QNi9Aj +.. section: Library + +Fix bad file descriptor errors from ``_posixsubprocess`` on AIX. + +.. + +.. date: 2025-11-17-00-53-51 +.. gh-issue: 141645 +.. nonce: TC3TL3 +.. section: Library + +Add a new ``--live`` mode to the tachyon profiler in +:mod:`!profiling.sampling` module. This mode consist of a live TUI that +displays real-time profiling statistics as the target application runs, +similar to ``top``. Patch by Pablo Galindo + +.. + +.. date: 2025-11-16-06-08-46 +.. gh-issue: 141615 +.. nonce: --6EK3 +.. section: Library + +Check ``stdin`` instead of ``stdout`` for ``use_rawinput`` in :mod:`pdb`. + +.. + +.. date: 2025-11-16-04-40-06 +.. gh-issue: 69113 +.. nonce: Xy7Fmn +.. section: Library + +Fix :mod:`doctest` to correctly report line numbers for doctests in +``__test__`` dictionary when formatted as triple-quoted strings by finding +unique lines in the string and matching them in the source file. + +.. + +.. date: 2025-11-15-14-58-12 +.. gh-issue: 141600 +.. nonce: XY2BXg +.. section: Library + +Fix musl version detection on Void Linux. + +.. + +.. date: 2025-11-15-11-10-16 +.. gh-issue: 48752 +.. nonce: aB3xYz +.. section: Library + +Add :func:`readline.get_pre_input_hook` function to retrieve the current +pre-input hook. This allows applications to save and restore the hook +without overwriting user settings. Patch by Sanyam Khurana. + +.. + +.. date: 2025-11-14-18-00-41 +.. gh-issue: 141565 +.. nonce: Ap2bhJ +.. section: Library + +Add async-aware profiling to the Tachyon sampling profiler. The profiler now +reconstructs and displays async task hierarchies in flamegraphs, making the +output more actionable for users. Patch by Savannah Ostrowski and Pablo +Galindo Salgado. + +.. + +.. date: 2025-11-13-13-11-02 +.. gh-issue: 60107 +.. nonce: LZq3QF +.. section: Library + +Remove a copy from :meth:`io.RawIOBase.read`. If the underlying I/O class +keeps a reference to the mutable memory, raise a :exc:`BufferError`. + +.. + +.. date: 2025-11-10-00-14-20 +.. gh-issue: 116738 +.. nonce: IxliC_ +.. section: Library + +Make csv module thread-safe on the :term:`free threaded <free threading>` +build. + +.. + +.. date: 2025-11-03-17-13-00 +.. gh-issue: 140911 +.. nonce: 7KFvSQ +.. section: Library + +:mod:`collections`: Ensure that the methods ``UserString.rindex()`` and +``UserString.index()`` accept :class:`collections.UserString` instances as +the sub argument. + +.. + +.. date: 2025-11-02-10-44-23 +.. gh-issue: 140875 +.. nonce: wt6B37 +.. section: Library + +Fix handling of unclosed character references (named and numerical) followed +by the end of file in :class:`html.parser.HTMLParser` with +``convert_charrefs=False``. + +.. + +.. date: 2025-10-27-17-00-11 +.. gh-issue: 140677 +.. nonce: hM9pTq +.. section: Library + +Add heatmap visualization mode to the Tachyon sampling profiler. The new +``--heatmap`` output format provides a line-by-line view showing execution +intensity with color-coded samples, inline statistics, and interactive call +graph navigation between callers and callees. + +.. + +.. date: 2025-10-23-06-38-35 +.. gh-issue: 139946 +.. nonce: HZa5hu +.. section: Library + +Distinguish stdout and stderr when colorizing output in argparse module. + +.. + +.. date: 2025-10-12-12-43-56 +.. gh-issue: 76007 +.. nonce: PyGM14 +.. section: Library + +:mod:`pydoc`: Fix :exc:`DeprecationWarning` being raised when generating doc +for :term:`stdlib` modules. + +.. + +.. date: 2025-09-09-13-00-42 +.. gh-issue: 138697 +.. nonce: QVwJw_ +.. section: Library + +Fix inferring *dest* from a single-dash long option in :mod:`argparse`. If a +short option and a single-dash long option are passed to +:meth:`!add_argument`, *dest* is now inferred from the single-dash long +option. + +.. + +.. date: 2025-09-09-10-13-24 +.. gh-issue: 138525 +.. nonce: hDTaAM +.. section: Library + +Add support for single-dash long options and alternate prefix characters in +:class:`argparse.BooleanOptionalAction`. + +.. + +.. date: 2025-07-29-11-37-22 +.. gh-issue: 79986 +.. nonce: fnJbE_ +.. section: Library + +Add parsing for ``References`` and ``In-Reply-To`` headers to the +:mod:`email` library that parses the header content as lists of message id +tokens. This prevents them from being folded incorrectly. + +.. + +.. date: 2025-07-10-18-40-11 +.. gh-issue: 135559 +.. nonce: BMDtYn +.. section: Library + +Flag: a ``dir()`` on a ``Flag`` enumeration now shows non-canonical members. +(i.e. aliases). + +.. + +.. date: 2025-05-30-18-37-44 +.. gh-issue: 134453 +.. nonce: kxkA-o +.. section: Library + +Fixed :func:`subprocess.Popen.communicate` ``input=`` handling of +:class:`memoryview` instances that were non-byte shaped on POSIX platforms. +Those are now properly cast to a byte shaped view instead of truncating the +input. Windows platforms did not have this bug. + +.. + +.. date: 2024-12-14-19-51-39 +.. gh-issue: 127930 +.. nonce: WsGnh9 +.. section: Library + +Add ``__all__`` to :mod:`tkinter.simpledialog`. + +.. + +.. date: 2024-05-20-12-35-52 +.. gh-issue: 115952 +.. nonce: J6n_Kf +.. section: Library + +Fix a potential memory denial of service in the :mod:`pickle` module. When +reading a pickled data received from untrusted source, it could cause an +arbitrary amount of memory to be allocated, even if the code that is allowed +to execute is restricted by overriding the +:meth:`~pickle.Unpickler.find_class` method. This could have led to symptoms +including a :exc:`MemoryError`, swapping, out of memory (OOM) killed +processes or containers, or even system crashes. + +.. + +.. bpo: 40350 +.. date: 2021-10-23-22-12-13 +.. nonce: t0dQMY +.. section: Library + +Fix support for namespace packages in :mod:`modulefinder`. + +.. + +.. date: 2025-11-26-23-30-09 +.. gh-issue: 141994 +.. nonce: arBEG6 +.. section: Documentation + +:mod:`xml.sax.handler`: Make Documentation of +:data:`xml.sax.handler.feature_external_ges` warn of opening up to `external +entity attacks <https://en.wikipedia.org/wiki/XML_external_entity_attack>`_. +Patch by Sebastian Pipping. + +.. + +.. date: 2025-12-16-01-17-21 +.. gh-issue: 134584 +.. nonce: tsxYYw +.. section: Core and Builtins + +Eliminate redundant refcounting from ``_STORE_ATTR_INSTANCE_VALUE``. + +.. + +.. date: 2025-12-15-15-07-40 +.. gh-issue: 142718 +.. nonce: zjiGjS +.. section: Core and Builtins + +JIT: Fix segfault caused by not flushing the stack to memory at side exits. + +.. + +.. date: 2025-12-15-15-01-21 +.. gh-issue: 142737 +.. nonce: xYXzeB +.. section: Core and Builtins + +Tracebacks will be displayed in fallback mode even if :func:`io.open` is +lost. Previously, this would crash the interpreter. Patch by Bartosz +Sławecki. + +.. + +.. date: 2025-12-15-03-20-24 +.. gh-issue: 116738 +.. nonce: NNHiTK +.. section: Core and Builtins + +Make the attributes in :mod:`bz2` thread-safe on the :term:`free threaded +<free threading>` build. + +.. + +.. date: 2025-12-14-21-46-07 +.. gh-issue: 134584 +.. nonce: vyec2h +.. section: Core and Builtins + +Eliminate redundant refcounting from ``_CALL_LIST_APPEND``. + +.. + +.. date: 2025-12-13-17-20-38 +.. gh-issue: 142554 +.. nonce: wNtEFF +.. section: Core and Builtins + +Fix a crash in :func:`divmod` when :func:`!_pylong.int_divmod` does not +return a tuple of length two exactly. Patch by Bénédikt Tran. + +.. + +.. date: 2025-12-10-23-03-10 +.. gh-issue: 142531 +.. nonce: NUEa1T +.. section: Core and Builtins + +Fix a free-threaded GC performance regression. If there are many untracked +tuples, the GC will run too often, resulting in poor performance. The fix +is to include untracked tuples in the "long lived" object count. The number +of frozen objects is also now included since the free-threaded GC must scan +those too. + +.. + +.. date: 2025-12-08-17-34-57 +.. gh-issue: 142402 +.. nonce: iV0ON3 +.. section: Core and Builtins + +Fix reference counting when adjacent literal parts are merged while +constructing :class:`string.templatelib.Template`, preventing the displaced +string object from leaking. + +.. + +.. date: 2025-12-08-14-14-40 +.. gh-issue: 116738 +.. nonce: x7aaBF +.. section: Core and Builtins + +Make the attributes in :mod:`zlib` thread-safe on the :term:`free threaded +<free threading>` build. + +.. + +.. date: 2025-12-08-13-04-37 +.. gh-issue: 142343 +.. nonce: BTAyML +.. section: Core and Builtins + +Fix SIGILL crash on m68k due to incorrect assembly constraint. + +.. + +.. date: 2025-12-06-00-38-37 +.. gh-issue: 142236 +.. nonce: m3EF9E +.. section: Core and Builtins + +Improve the "Perhaps you forgot a comma?" syntax error for multi-line string +concatenations to point to the last string instead of the first, making it +easier to locate where the comma is missing. Patch by Pablo Galindo. + +.. + +.. date: 2025-12-06-00-16-43 +.. gh-issue: 142236 +.. nonce: m3EF9E +.. section: Core and Builtins + +Fix incorrect keyword suggestions for syntax errors in :mod:`traceback`. The +keyword typo suggestion mechanism would incorrectly suggest replacements +when the extracted source code was incomplete rather than containing an +actual typo. Patch by Pablo Galindo. + +.. + +.. date: 2025-12-05-17-24-34 +.. gh-issue: 142305 +.. nonce: ybXvtr +.. section: Core and Builtins + +Decrease the size of the generated stencils and the runtime JIT code. Patch +by Diego Russo. + +.. + +.. date: 2025-12-05-15-59-03 +.. gh-issue: 135379 +.. nonce: lDXbKO +.. section: Core and Builtins + +Implement a limited form of register allocation known as "top of stack +caching" in the JIT. It works by keeping 0-3 of the top items in the stack +in registers. The code generator generates multiple versions of those uops +that do not escape and are relatively small. During JIT compilation, the +copy that produces the least memory traffic is selected, spilling or +reloading values when needed. + +.. + +.. date: 2025-12-05-14-33-54 +.. gh-issue: 142276 +.. nonce: H4j8hP +.. section: Core and Builtins + +Fix missing type watcher when promoting attribute loads to constants in the +JIT. Patch by Ken Jin. Reproducer by Yuancheng Jiang. + +.. + +.. date: 2025-12-03-11-03-35 +.. gh-issue: 142218 +.. nonce: 44Fq_J +.. section: Core and Builtins + +Fix crash when inserting into a split table dictionary with a non +:class:`str` key that matches an existing key. + +.. + +.. date: 2025-12-02-21-11-46 +.. gh-issue: 141976 +.. nonce: yu7pDV +.. section: Core and Builtins + +Check against abstract stack overflow in the JIT optimizer. + +.. + +.. date: 2025-12-02-15-39-16 +.. gh-issue: 97850 +.. nonce: H6QKwl +.. section: Core and Builtins + +Remove all ``*.load_module()`` usage and definitions from the import system +and importlib. The method has been deprecated in favor of +``importlib.abc.Loader.exec_module()`` since Python 3.4. + +.. + +.. date: 2025-12-01-20-41-26 +.. gh-issue: 142048 +.. nonce: c2YosX +.. section: Core and Builtins + +Fix quadratically increasing garbage collection delays in free-threaded +build. + +.. + +.. date: 2025-12-01-15-22-54 +.. gh-issue: 65961 +.. nonce: hCJvRB +.. section: Core and Builtins + +Stop setting ``__cached__`` on modules. + +.. + +.. date: 2025-11-29-18-14-28 +.. gh-issue: 141770 +.. nonce: JURnvg +.. section: Core and Builtins + +Annotate anonymous mmap usage only when supported by the Linux kernel and if +``-X dev`` is used or Python is built in debug mode. Patch by Donghee Na. + +.. + +.. date: 2025-11-29-08-51-56 +.. gh-issue: 142029 +.. nonce: rUpcmt +.. section: Core and Builtins + +Raise :exc:`ModuleNotFoundError` instead of crashing when a nonexistent +module is used as a name in ``_imp.create_builtin()``. + +.. + +.. date: 2025-11-28-16-45-07 +.. gh-issue: 142029 +.. nonce: JuXiKu +.. section: Core and Builtins + +Raise :exc:`ValueError` instead of crashing when empty string is used as a +name in ``_imp.create_builtin()``. + +.. + +.. date: 2025-11-26-20-01-07 +.. gh-issue: 141976 +.. nonce: K8NDmR +.. section: Core and Builtins + +Protect against specialization failures in the tracing JIT compiler for +performance reasons. + +.. + +.. date: 2025-11-25-02-23-31 +.. gh-issue: 141861 +.. nonce: QcMdcM +.. section: Core and Builtins + +Fix invalid memory read in the ``ENTER_EXECUTOR`` instruction. + +.. + +.. date: 2025-11-24-21-09-30 +.. gh-issue: 141930 +.. nonce: hIIzSd +.. section: Core and Builtins + +When importing a module, use Python's regular file object to ensure that +writes to ``.pyc`` files are complete or an appropriate error is raised. + +.. + +.. date: 2025-11-24-16-07-57 +.. gh-issue: 138122 +.. nonce: m3EF9E +.. section: Core and Builtins + +Add incomplete sample detection to prevent corrupted profiling data. Each +thread state now contains an embedded base frame (sentinel at the bottom of +the frame stack) with owner type ``FRAME_OWNED_BY_INTERPRETER``. The +profiler validates that stack unwinding terminates at this sentinel frame. +Samples that fail to reach the base frame (due to race conditions, memory +corruption, or other errors) are now rejected rather than being included as +spurious data. + +.. + +.. date: 2025-11-22-10-43-26 +.. gh-issue: 120158 +.. nonce: 41_rXd +.. section: Core and Builtins + +Fix inconsistent state when enabling or disabling monitoring events too many +times. + +.. + +.. date: 2025-11-20-22-09-22 +.. gh-issue: 140638 +.. nonce: f6btj0 +.. section: Core and Builtins + +Expose a ``"candidates"`` stat in :func:`gc.get_stats` and +:data:`gc.callbacks`. + +.. + +.. date: 2025-11-20-13-18-57 +.. gh-issue: 141780 +.. nonce: xDrVNr +.. section: Core and Builtins + +Fix :c:macro:`Py_mod_gil` with API added in :pep:`793`: +:c:func:`!PyModule_FromSlotsAndSpec` and ``PyModExport`` hooks + +.. + +.. date: 2025-11-19-16-40-24 +.. gh-issue: 141732 +.. nonce: PTetqp +.. section: Core and Builtins + +Ensure the :meth:`~object.__repr__` for :exc:`ExceptionGroup` and +:exc:`BaseExceptionGroup` does not change when the exception sequence that +was original passed in to its constructor is subsequently mutated. + +.. + +.. date: 2025-11-18-07-45-37 +.. gh-issue: 140638 +.. nonce: i06qxD +.. section: Core and Builtins + +Expose a ``"duration"`` stat in :func:`gc.get_stats` and +:data:`gc.callbacks`. + +.. + +.. date: 2025-11-17-14-40-45 +.. gh-issue: 139653 +.. nonce: LzOy1M +.. section: Core and Builtins + +Only raise a ``RecursionError`` or trigger a fatal error if the stack +pointer is both below the limit pointer *and* above the stack base. If +outside of these bounds assume that it is OK. This prevents false positives +when user-space threads swap stacks. + +.. + +.. date: 2025-11-16-21-14-48 +.. gh-issue: 41779 +.. nonce: rXIj5h +.. section: Core and Builtins + +Allowed defining the *__dict__* and *__weakref__* :ref:`__slots__ <slots>` +for any class. + +.. + +.. date: 2025-11-15-23-58-23 +.. gh-issue: 139103 +.. nonce: 9cVYJ0 +.. section: Core and Builtins + +Improve multithreaded scaling of dataclasses on the free-threaded build. + +.. + +.. date: 2025-11-15-14-04-35 +.. gh-issue: 141589 +.. nonce: VfdMDD +.. section: Core and Builtins + +Change ``backoff counter`` to use prime numbers instead of powers of 2. Use +only 3 bits for ``counter`` and 13 bits for ``value``. This allows to +support values up to 8191. Patch by Mikhail Efimov. + +.. + +.. date: 2025-07-22-16-20-06 +.. gh-issue: 137007 +.. nonce: 1oPvvK +.. section: Core and Builtins + +Fix a bug during JIT compilation failure which caused garbage collection +debug assertions to fail. + +.. + +.. date: 2025-07-11-19-57-27 +.. gh-issue: 132657 +.. nonce: vwDuO2 +.. section: Core and Builtins + +For the free-threaded build, avoid locking the :class:`set` object for the +``__contains__`` method. + +.. + +.. date: 2025-06-28-17-54-27 +.. gh-issue: 134584 +.. nonce: EXgPub +.. section: Core and Builtins + +Eliminate redundant refcounting from ``_CALL_STR_1``. + +.. + +.. date: 2025-06-28-04-32-38 +.. gh-issue: 134584 +.. nonce: eZogqn +.. section: Core and Builtins + +Eliminate redundant refcounting from ``_CALL_BUILTIN_O``. + +.. + +.. date: 2025-06-23-22-52-20 +.. gh-issue: 134584 +.. nonce: qbiQfG +.. section: Core and Builtins + +Eliminate redundant refcounting from ``_CALL_TUPLE_1``. Patch by Noam Cohen + +.. + +.. date: 2025-12-11-13-01-49 +.. gh-issue: 142589 +.. nonce: nNAqgw +.. section: C API + +Fix :c:func:`PyUnstable_Object_IsUniqueReferencedTemporary()` handling of +tagged ints on the interpreter stack. + +.. + +.. date: 2025-12-11-09-06-36 +.. gh-issue: 142571 +.. nonce: Csdxnn +.. section: C API + +:c:func:`!PyUnstable_CopyPerfMapFile` now checks that opening the file +succeeded before flushing. + +.. + +.. date: 2025-12-03-16-35-24 +.. gh-issue: 142225 +.. nonce: vmCJoo +.. section: C API + +Fixed the :c:macro:`PyABIInfo_VAR` macro. + +.. + +.. date: 2025-12-03-14-41-07 +.. gh-issue: 141049 +.. nonce: VuAUe2 +.. section: C API + +:c:func:`!_PyObject_CallMethodId`, :c:func:`!_PyObject_GetAttrId` and +:c:func:`!_PyUnicode_FromId` are deprecated since 3.15 and will be removed +in 3.20. Instead, use :c:func:`PyUnicode_InternFromString()` and cache the +result in the module state, then call :c:func:`PyObject_CallMethod` or +:c:func:`PyObject_GetAttr`. Patch by Victor Stinner. + +.. + +.. date: 2025-12-01-18-17-16 +.. gh-issue: 142163 +.. nonce: 2HiX5A +.. section: C API + +Fix the ``HAVE_THREAD_LOCAL`` macro being defined without the +``Py_BUILD_CORE`` macro set after including :file:`Python.h`. + +.. + +.. date: 2025-11-21-10-34-00 +.. gh-issue: 137422 +.. nonce: tzZKLi +.. section: C API + +Fix :term:`free threading` race condition in +:c:func:`PyImport_AddModuleRef`. It was previously possible for two calls to +the function return two different objects, only one of which was stored in +:data:`sys.modules`. + +.. + +.. date: 2025-11-18-18-36-15 +.. gh-issue: 141726 +.. nonce: ILrhyK +.. section: C API + +Add :c:func:`PyDict_SetDefaultRef` to the Stable ABI. + +.. + +.. date: 2025-11-18-04-16-09 +.. gh-issue: 140042 +.. nonce: S1C7id +.. section: C API + +Removed the sqlite3_shutdown call that could cause closing connections for +sqlite when used with multiple sub interpreters. + +.. + +.. date: 2025-11-05-21-48-31 +.. gh-issue: 141070 +.. nonce: mkrhjQ +.. section: C API + +Add :c:func:`!PyUnstable_Object_Dump` to dump an object to ``stderr``. It +should only be used for debugging. Patch by Victor Stinner. + +.. + +.. date: 2025-09-22-16-32-00 +.. gh-issue: 139165 +.. nonce: 6Czn7S +.. section: C API + +Expose the functions :c:func:`Py_SIZE`, :c:func:`Py_IS_TYPE` and +:c:func:`Py_SET_SIZE` in the Stable ABI. + +.. + +.. date: 2025-12-09-14-23-51 +.. gh-issue: 131372 +.. nonce: 2TAEyz +.. section: Build + +Add ``LDVERSION`` and ``EXE`` to the ``base_interpreter`` value of +``build-details.json``. + +.. + +.. date: 2025-12-09-13-33-46 +.. gh-issue: 142454 +.. nonce: cqUxzQ +.. section: Build + +When calculating the digest of the JIT stencils input, sort the hashed files +by filenames before adding their content to the hasher. This ensures +deterministic hash input and hence deterministic hash, independent on +filesystem order. + +.. + +.. date: 2025-12-04-20-57-15 +.. gh-issue: 131372 +.. nonce: o397g7 +.. section: Build + +``build-details.py`` will only be installed as part of the main install +(``make install``). ``make altinstall`` will no longer include it. + +.. + +.. date: 2025-12-03-10-44-42 +.. gh-issue: 142234 +.. nonce: i1kaFb +.. section: Build + +Allow ``--enable-wasm-dynamic-linking`` for WASI. While CPython doesn't +directly support it so external/downstream users do not have to patch in +support for the flag. + +.. + +.. date: 2025-11-28-21-43-07 +.. gh-issue: 142050 +.. nonce: PFi4tv +.. section: Build + +Fixed a bug where JIT stencils produced on Windows contained debug data. +Patch by Chris Eibl. + +.. + +.. date: 2025-11-28-19-49-01 +.. gh-issue: 141808 +.. nonce: cV5K12 +.. section: Build + +Do not generate the jit stencils twice in case of PGO builds on Windows. + +.. + +.. date: 2025-11-25-13-17-47 +.. gh-issue: 141926 +.. nonce: KmuM2h +.. section: Build + +``RUNSHARED`` is no longer cleared when cross-compiling. Previously, +``RUNSHARED`` was cleared when cross-compiling, which breaks PGO when using +``--enabled-shared`` on systems where the cross-compiled CPython is +otherwise executable (e.g., via transparent emulation). + +.. + +.. date: 2025-11-20-23-15-39 +.. gh-issue: 141808 +.. nonce: NEewZC +.. section: Build + +When running ``make clean-retain-profile``, keep the generated JIT stencils. +That way, the stencils are not generated twice when Profile-guided +optimization (PGO) is used. It also allows distributors to supply their own +pre-built JIT stencils. + +.. + +.. date: 2025-11-20-17-01-05 +.. gh-issue: 141784 +.. nonce: LkYI2n +.. section: Build + +Fix ``_remote_debugging_module.c`` compilation on 32-bit Linux. Include +Python.h before system headers to make sure that +``_remote_debugging_module.c`` uses the same types (ABI) than Python. Patch +by Victor Stinner. + +.. + +.. date: 2025-11-19-09-21-17 +.. gh-issue: 141172 +.. nonce: cYWc4x +.. section: Build + +Update to WASI SDK 29. + +.. + +.. date: 2025-10-30-10-36-15 +.. gh-issue: 139707 +.. nonce: QJ1FfJ +.. section: Build + +Add configure option :option:`--with-missing-stdlib-config=FILE` allows +which distributors to pass a `JSON <https://www.json.org/json-en.html>`_ +configuration file containing custom error messages for missing +:term:`standard library` modules. + +.. + +.. date: 2025-04-29-18-25-34 +.. gh-issue: 108819 +.. nonce: qMUTRB +.. section: Build + +Honor :option:`--with-platlibdir` in the pure-Python standard library +installation path, if ``PLATLIBDIR`` doesn't match the value used in +``LIBDIR``. diff --git a/Misc/NEWS.d/3.15.0a4.rst b/Misc/NEWS.d/3.15.0a4.rst new file mode 100644 index 00000000000000..408f76cab411e3 --- /dev/null +++ b/Misc/NEWS.d/3.15.0a4.rst @@ -0,0 +1,351 @@ +.. date: 2025-12-17-02-02-57 +.. gh-issue: 142836 +.. nonce: mR-fvK +.. release date: 2026-01-13 +.. section: Tests + +Accommodated Solaris in ``test_pdb.test_script_target_anonymous_pipe``. + +.. + +.. date: 2025-12-22-22-36-21 +.. gh-issue: 122431 +.. nonce: 9E3085 +.. section: Library + +Corrected the error message in :func:`readline.append_history_file` to state +that ``nelements`` must be non-negative instead of positive. + +.. + +.. date: 2025-12-21-17-44-28 +.. gh-issue: 143046 +.. nonce: GBa5Ip +.. section: Library + +The :mod:`asyncio` REPL no longer prints copyright and version messages in +the quiet mode (:option:`-q`). Patch by Bartosz Sławecki. + +.. + +.. date: 2025-12-20-16-35-42 +.. gh-issue: 80744 +.. nonce: X4pZ2N +.. section: Library + +Fix issue where ``pdb`` would read a ``.pdbrc`` twice if launched from the +home directory + +.. + +.. date: 2025-12-20-02-33-05 +.. gh-issue: 138122 +.. nonce: m3EF9E +.. section: Library + +Add blocking mode to Tachyon for accurate stack traces in applications with +many generators or fast-changing call stacks. Patch by Pablo Galindo. + +.. + +.. date: 2025-12-20-01-49-02 +.. gh-issue: 143010 +.. nonce: _-SWX0 +.. section: Library + +Fixed a bug in :mod:`mailbox` where the precise timing of an external event +could result in the library opening an existing file instead of a file it +expected to create. + +.. + +.. date: 2025-12-17-14-41-09 +.. gh-issue: 112127 +.. nonce: 13OHQk +.. section: Library + +Fix possible use-after-free in :func:`atexit.unregister` when the callback +is unregistered during comparison. + +.. + +.. date: 2025-12-17-03-03-12 +.. gh-issue: 138122 +.. nonce: m3EF9E +.. section: Library + +Fix incomplete stack traces in the Tachyon profiler's frame cache when +profiling code with deeply nested generators. The frame cache now validates +that stack traces reach the base frame before caching, preventing broken +flamegraphs. Patch by Pablo Galindo. + +.. + +.. date: 2025-12-16-15-32-41 +.. gh-issue: 142834 +.. nonce: g7mHw_ +.. section: Library + +Change the :mod:`pdb` ``commands`` command to use the last available +breakpoint instead of failing when the most recently created breakpoint was +deleted. + +.. + +.. date: 2025-12-16-14-49-19 +.. gh-issue: 142783 +.. nonce: VPV1ig +.. section: Library + +Fix zoneinfo use-after-free with descriptor _weak_cache. a descriptor as +_weak_cache could cause crashes during object creation. The fix ensures +proper reference counting for descriptor-provided objects. + +.. + +.. date: 2025-12-16-14-21-20 +.. gh-issue: 76007 +.. nonce: O4AmYl +.. section: Library + +Deprecate ``VERSION`` from :mod:`xml.etree.ElementTree` and ``version`` from +:mod:`!xml.sax.expatreader` and :mod:`xml.sax.handler`. Patch by Hugo van +Kemenade. + +.. + +.. date: 2025-12-16-04-39-27 +.. gh-issue: 142784 +.. nonce: HBGJag +.. section: Library + +The :mod:`asyncio` REPL now properly closes the loop upon the end of +interactive session. Previously, it could cause surprising warnings. +Contributed by Bartosz Sławecki. + +.. + +.. date: 2025-12-15-02-00-31 +.. gh-issue: 138122 +.. nonce: m3EF9E +.. section: Library + +Add binary output format to :mod:`profiling.sampling` for compact storage of +profiling data. The new ``--binary`` option captures samples to a file that +can be converted to other formats using the ``replay`` command. Patch by +Pablo Galindo + +.. + +.. date: 2025-12-13-23-26-42 +.. gh-issue: 142495 +.. nonce: I88Uv_ +.. section: Library + +:class:`collections.defaultdict` now prioritizes :meth:`~object.__setitem__` +when inserting default values from ``default_factory``. This prevents race +conditions where a default value would overwrite a value set before +``default_factory`` returns. + +.. + +.. date: 2025-12-13-10-34-59 +.. gh-issue: 142654 +.. nonce: fmm974 +.. section: Library + +Show the clearer error message when using ``profiling.sampling`` on an +unknown PID. + +.. + +.. date: 2025-12-11-22-59-33 +.. gh-issue: 142560 +.. nonce: GkJrkk +.. section: Library + +Fix use-after-free in :class:`bytearray` search-like methods +(:meth:`~bytearray.find`, :meth:`~bytearray.count`, +:meth:`~bytearray.index`, :meth:`~bytearray.rindex`, and +:meth:`~bytearray.rfind`) by marking the storage as exported which causes +reallocation attempts to raise :exc:`BufferError`. For +:func:`~operator.contains`, :meth:`~bytearray.split`, and +:meth:`~bytearray.rsplit` the :ref:`buffer protocol <bufferobjects>` is used +for this. + +.. + +.. date: 2025-12-10-02-31-43 +.. gh-issue: 142419 +.. nonce: C8_LES +.. section: Library + +:meth:`mmap.mmap.set_name` method added to annotate an anonymous memory map +if Linux kernel supports ``PR_SET_VMA_ANON_NAME`` (Linux 5.17 or newer). +Patch by Donghee Na. + +.. + +.. date: 2025-10-12-12-05-52 +.. gh-issue: 139971 +.. nonce: UdoStU +.. section: Library + +:mod:`pydoc`: Ensure that the link to the online documentation of a +:term:`stdlib` module is correct. + +.. + +.. date: 2025-07-20-15-39-54 +.. gh-issue: 124098 +.. nonce: znFPIp +.. section: Library + +Fix issue where methods in handlers that lacked the protocol name but +matched a valid base handler method (e.g., ``_open()`` or ``error()``) were +incorrectly added to :class:`urllib.request.OpenerDirector`'s handlers. +Contributed by Andrea Mattei. + +.. + +.. date: 2025-07-05-08-30-07 +.. gh-issue: 136282 +.. nonce: K3JKyD +.. section: Library + +Add support for :const:`~configparser.UNNAMED_SECTION` when creating a +section via the mapping protocol access + +.. + +.. date: 2025-12-22-12-03-09 +.. gh-issue: 143057 +.. nonce: Majsre +.. section: Core and Builtins + +Avoid locking in :c:func:`PyTraceMalloc_Track` and +:c:func:`PyTraceMalloc_Untrack` when :mod:`tracemalloc` is not enabled. + +.. + +.. date: 2025-12-21-00-25-26 +.. gh-issue: 139109 +.. nonce: gwSsOL +.. section: Core and Builtins + +Add missing terminator in certain cases when tracing in the new JIT +compiler. + +.. + +.. date: 2025-12-19-00-59-29 +.. gh-issue: 142961 +.. nonce: q8WRSq +.. section: Core and Builtins + +Fix a segfault in the JIT when constant folding ``len(tuple)``. + +.. + +.. date: 2025-12-18-01-00-14 +.. gh-issue: 142776 +.. nonce: ACaoeP +.. section: Core and Builtins + +Fix a file descriptor leak in import.c + +.. + +.. date: 2025-12-17-20-31-09 +.. gh-issue: 139757 +.. nonce: 6DWxeQ +.. section: Core and Builtins + +Fix building JIT stencils on free-threaded builds. + +.. + +.. date: 2025-12-17-10-49-03 +.. gh-issue: 129068 +.. nonce: GlYnrO +.. section: Core and Builtins + +Make concurrent iteration over the same range iterator thread-safe in the +free threading build. + +.. + +.. date: 2025-12-16-23-26-41 +.. gh-issue: 142543 +.. nonce: wJKjBs +.. section: Core and Builtins + +Fix a stack overflow on Clang JIT build configurations with full LTO. + +.. + +.. date: 2025-12-16-20-38-17 +.. gh-issue: 142448 +.. nonce: mAFqwL +.. section: Core and Builtins + +Fix a bug when using monitoring with the JIT. + +.. + +.. date: 2025-12-16-11-56-20 +.. gh-issue: 142766 +.. nonce: Uy2HTm +.. section: Core and Builtins + +Clear the frame of a generator when :meth:`generator.close` is called. + +.. + +.. date: 2025-12-16-05-52-37 +.. gh-issue: 134584 +.. nonce: VsfOQR +.. section: Core and Builtins + +Eliminate redundant refcounting from ``_LOAD_ATTR_INSTANCE_VALUE``. + +.. + +.. date: 2025-12-16-05-24-24 +.. gh-issue: 134584 +.. nonce: tJ1usH +.. section: Core and Builtins + +Eliminate redundant refcounting from ``_STORE_ATTR_WITH_HINT``. + +.. + +.. date: 2025-12-13-01-11-03 +.. gh-issue: 142476 +.. nonce: 44Sp4N +.. section: Core and Builtins + +Fix a memory leak in the experimental Tier 2 optimizer when creating +executors. Patched by Shamil Abdulaev. + +.. + +.. date: 2025-11-06-05-21-25 +.. gh-issue: 100964 +.. nonce: TxPf1b +.. section: Core and Builtins + +Fix reference cycle in exhausted generator frames. Patch by Savannah +Ostrowski. + +.. + +.. date: 2025-10-11-17-01-21 +.. gh-issue: 139922 +.. nonce: RUkXyd +.. section: Core and Builtins + +Allow building CPython with the tail calling interpreter on Visual Studio +2026 MSVC. This provides a performance gain over the prior interpreter for +MSVC. Patch by Ken Jin, Brandt Bucher, and Chris Eibl. With help from the +MSVC team including Hulon Jenkins. diff --git a/Misc/NEWS.d/3.15.0a5.rst b/Misc/NEWS.d/3.15.0a5.rst new file mode 100644 index 00000000000000..85b733b6d19608 --- /dev/null +++ b/Misc/NEWS.d/3.15.0a5.rst @@ -0,0 +1,942 @@ +.. date: 2025-12-25-00-38-20 +.. gh-issue: 143082 +.. nonce: CYUeux +.. release date: 2026-01-14 +.. section: Windows + +Fix :mod:`pdb` arrow key history not working when ``stdin`` is +``sys.stdin``. + +.. + +.. date: 2025-09-14-13-35-44 +.. gh-issue: 128067 +.. nonce: BGdP_A +.. section: Windows + +Fix a bug in PyREPL on Windows where output without a trailing newline was +overwritten by the next prompt. + +.. + +.. date: 2026-01-02-11-44-56 +.. gh-issue: 142095 +.. nonce: 4ssgnM +.. section: Tools/Demos + +Make gdb 'py-bt' command use frame from thread local state when available. +Patch by Sam Gross and Victor Stinner. + +.. + +.. date: 2026-01-09-13-52-10 +.. gh-issue: 143460 +.. nonce: _nW2jt +.. section: Tests + +Skip tests relying on infinite recusion if stack size is unlimited. + +.. + +.. date: 2026-01-08-11-50-06 +.. gh-issue: 143553 +.. nonce: KyyNTt +.. section: Tests + +Add support for parametrized resources, such as ``-u xpickle=2.7``. + +.. + +.. bpo: 31391 +.. date: 2020-09-29-23-14-01 +.. nonce: IZr2P8 +.. section: Tests + +Forward-port test_xpickle from Python 2 to Python 3 and add the resource +back to test's command line. + +.. + +.. date: 2026-01-12-07-17-38 +.. gh-issue: 143706 +.. nonce: sysArgv +.. section: Library + +Fix :mod:`multiprocessing` forkserver so that :data:`sys.argv` is correctly +set before ``__main__`` is preloaded. Previously, :data:`sys.argv` was empty +during main module import in forkserver child processes. This fixes a +regression introduced in 3.13.8 and 3.14.1. Root caused by Aaron Wieczorek, +test provided by Thomas Watson, thanks! + +.. + +.. date: 2026-01-10-16-42-47 +.. gh-issue: 143638 +.. nonce: du5G7d +.. section: Library + +Forbid reentrant calls of the :class:`pickle.Pickler` and +:class:`pickle.Unpickler` methods for the C implementation. Previously, this +could cause crash or data corruption, now concurrent calls of methods of the +same object raise :exc:`RuntimeError`. + +.. + +.. date: 2026-01-10-15-40-57 +.. gh-issue: 143658 +.. nonce: Ox6pE5 +.. section: Library + +:mod:`importlib.metadata`: Use :meth:`str.translate` to improve performance +of :meth:`!importlib.metadata.Prepared.normalize`. Patch by Hugo van +Kemenade and Henry Schreiner. + +.. + +.. date: 2026-01-10-10-04-08 +.. gh-issue: 78724 +.. nonce: xkXfxX +.. section: Library + +Raise :exc:`RuntimeError`'s when user attempts to call methods on +half-initialized :class:`~struct.Struct` objects, For example, created by +``Struct.__new__(Struct)``. Patch by Sergey B Kirpichev. + +.. + +.. date: 2026-01-09-17-50-26 +.. gh-issue: 143196 +.. nonce: WxKxzU +.. section: Library + +Fix crash when the internal encoder object returned by undocumented function +:func:`!json.encoder.c_make_encoder` was called with non-zero second +(*_current_indent_level*) argument. + +.. + +.. date: 2026-01-09-13-07-22 +.. gh-issue: 143191 +.. nonce: PPR_vW +.. section: Library + +:func:`_thread.stack_size` now raises :exc:`ValueError` if the stack size is +too small. Patch by Victor Stinner. + +.. + +.. date: 2026-01-08-14-53-46 +.. gh-issue: 143547 +.. nonce: wHBVlr +.. section: Library + +Fix :func:`sys.unraisablehook` when the hook raises an exception and changes +:func:`sys.unraisablehook`: hold a strong reference to the old hook. Patch +by Victor Stinner. + +.. + +.. date: 2026-01-08-13-41-58 +.. gh-issue: 139686 +.. nonce: S_nzkl +.. section: Library + +Revert 0a97941245f1dda6d838f9aaf0512104e5253929 and +57db12514ac686f0a752ec8fe1c08b6daa0c6219 which made importlib.reload a no-op +for lazy modules; caused Buildbot failures. + +.. + +.. date: 2026-01-07-15-49-06 +.. gh-issue: 143517 +.. nonce: FP5KgL +.. section: Library + +:func:`annotationlib.get_annotations` no longer raises a :exc:`SyntaxError` +when evaluating a stringified starred annotation that starts with one or +more whitespace characters followed by a ``*``. Patch by Bartosz Sławecki. + +.. + +.. date: 2026-01-06-12-00-00 +.. gh-issue: 143474 +.. nonce: cQM4VA +.. section: Library + +Add :data:`os.RWF_ATOMIC` constant for Linux 6.11+. + +.. + +.. date: 2026-01-05-12-20-42 +.. gh-issue: 143445 +.. nonce: rgxnbL +.. section: Library + +Speed up :func:`copy.deepcopy` by 1.04x. + +.. + +.. date: 2026-01-03-19-41-36 +.. gh-issue: 143378 +.. nonce: 29AvE7 +.. section: Library + +Fix use-after-free crashes when a :class:`~io.BytesIO` object is +concurrently mutated during :meth:`~io.RawIOBase.write` or +:meth:`~io.IOBase.writelines`. + +.. + +.. date: 2026-01-02-17-26-33 +.. gh-issue: 143368 +.. nonce: m3EF9E +.. section: Library + +Fix endless retry loop in :mod:`profiling.sampling` blocking mode when +threads cannot be seized due to ``EPERM``. Such threads are now skipped +instead of causing repeated error messages. Patch by Pablo Galindo. + +.. + +.. date: 2026-01-02-12-55-52 +.. gh-issue: 143346 +.. nonce: iTekce +.. section: Library + +Fix incorrect wrapping of the Base64 data in :class:`!plistlib._PlistWriter` +when the indent contains a mix of tabs and spaces. + +.. + +.. date: 2026-01-02-09-32-43 +.. gh-issue: 140025 +.. nonce: zOX58_ +.. section: Library + +:mod:`queue`: Fix :meth:`!SimpleQueue.__sizeof__` computation. + +.. + +.. date: 2026-01-01-11-21-57 +.. gh-issue: 143310 +.. nonce: 8rxtH3 +.. section: Library + +:mod:`tkinter`: fix a crash when a Python :class:`list` is mutated during +the conversion to a Tcl object (e.g., when setting a Tcl variable). Patch by +Bénédikt Tran. + +.. + +.. date: 2025-12-31-20-43-02 +.. gh-issue: 143309 +.. nonce: cdFxdH +.. section: Library + +Fix a crash in :func:`os.execve` on non-Windows platforms when given a +custom environment mapping which is then mutated during parsing. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-12-31-17-38-33 +.. gh-issue: 143308 +.. nonce: lY8UCR +.. section: Library + +:mod:`pickle`: fix use-after-free crashes when a +:class:`~pickle.PickleBuffer` is concurrently mutated by a custom buffer +callback during pickling. Patch by Bénédikt Tran and Aaron Wieczorek. + +.. + +.. date: 2025-12-29-21-12-12 +.. gh-issue: 142939 +.. nonce: OyQQr5 +.. section: Library + +Performance optimisations for :func:`difflib.get_close_matches` + +.. + +.. date: 2025-12-29-00-42-26 +.. gh-issue: 124951 +.. nonce: OsC5K4 +.. section: Library + +The base64 implementation behind the :mod:`binascii`, :mod:`base64`, and +related codec has been optimized for modern pipelined CPU architectures and +now performs 2-3x faster across all platforms. + +.. + +.. date: 2025-12-28-20-28-05 +.. gh-issue: 143237 +.. nonce: q1ymuA +.. section: Library + +Fix support of named pipes in the rotating :mod:`logging` handlers. + +.. + +.. date: 2025-12-28-14-41-02 +.. gh-issue: 143249 +.. nonce: K4vEp4 +.. section: Library + +Fix possible buffer leaks in Windows overlapped I/O on error handling. + +.. + +.. date: 2025-12-28-13-49-06 +.. gh-issue: 143241 +.. nonce: 5H4b8d +.. section: Library + +:mod:`zoneinfo`: fix infinite loop in :meth:`ZoneInfo.from_file +<zoneinfo.ZoneInfo.from_file>` when parsing a malformed TZif file. Patch by +Fatih Celik. + +.. + +.. date: 2025-12-28-13-12-40 +.. gh-issue: 142830 +.. nonce: uEyd6r +.. section: Library + +:mod:`sqlite3`: fix use-after-free crashes when the connection's callbacks +are mutated during a callback execution. Patch by Bénédikt Tran. + +.. + +.. date: 2025-12-27-15-41-27 +.. gh-issue: 143200 +.. nonce: 2hEUAl +.. section: Library + +:mod:`xml.etree.ElementTree`: fix use-after-free crashes in +:meth:`~object.__getitem__` and :meth:`~object.__setitem__` methods of +:class:`~xml.etree.ElementTree.Element` when the element is concurrently +mutated. Patch by Bénédikt Tran. + +.. + +.. date: 2025-12-27-13-47-59 +.. gh-issue: 143214 +.. nonce: gf6nZK +.. section: Library + +Add the *wrapcol* parameter in :func:`binascii.b2a_base64` and +:func:`base64.b64encode`. + +.. + +.. date: 2025-12-27-00-14-56 +.. gh-issue: 142195 +.. nonce: UgBEo5 +.. section: Library + +Updated timeout evaluation logic in :mod:`subprocess` to be compatible with +deterministic environments like Shadow where time moves exactly as +requested. + +.. + +.. date: 2025-12-26-14-51-50 +.. gh-issue: 140739 +.. nonce: BAbZTo +.. section: Library + +Fix several crashes due to reading invalid memory in the new Tachyon +sampling profiler. Patch by Pablo Galindo. + +.. + +.. date: 2025-12-25-08-58-55 +.. gh-issue: 142164 +.. nonce: XrFztf +.. section: Library + +Fix the ctypes bitfield overflow error message to report the correct offset +and size calculation. + +.. + +.. date: 2025-12-24-14-18-52 +.. gh-issue: 143145 +.. nonce: eXLw8D +.. section: Library + +Fixed a possible reference leak in ctypes when constructing results with +multiple output parameters on error. + +.. + +.. date: 2025-12-23-17-07-22 +.. gh-issue: 143103 +.. nonce: LRjXEW +.. section: Library + +Add padding support to :func:`base64.z85encode` via the ``pad`` parameter. + +.. + +.. date: 2025-12-23-11-43-05 +.. gh-issue: 130796 +.. nonce: TkzUGx +.. section: Library + +Undeprecate the :func:`locale.getdefaultlocale` function. Patch by Victor +Stinner. + +.. + +.. date: 2025-12-22-18-25-54 +.. gh-issue: 74902 +.. nonce: HqrWUV +.. section: Library + +Add the :func:`~unicodedata.iter_graphemes` function in the +:mod:`unicodedata` module to iterate over grapheme clusters according to +rules defined in `Unicode Standard Annex #29, "Unicode Text Segmentation" +<https://www.unicode.org/reports/tr29/>`_. Add +:func:`~unicodedata.grapheme_cluster_break`, +:func:`~unicodedata.indic_conjunct_break` and +:func:`~unicodedata.extended_pictographic` functions to get the properties +of the character which are related to the above algorithm. + +.. + +.. date: 2025-12-22-00-00-00 +.. gh-issue: 143004 +.. nonce: uaf-counter +.. section: Library + +Fix a potential use-after-free in :meth:`collections.Counter.update` when +user code mutates the Counter during an update. + +.. + +.. date: 2025-12-21-17-24-29 +.. gh-issue: 140648 +.. nonce: i8dca6 +.. section: Library + +The :mod:`asyncio` REPL now respects the :option:`-I` flag (isolated mode). +Previously, it would load and execute :envvar:`PYTHONSTARTUP` even if the +flag was set. Contributed by Bartosz Sławecki. + +.. + +.. date: 2025-12-20-10-21-23 +.. gh-issue: 142991 +.. nonce: jYHD9E +.. section: Library + +Fixed socket operations such as recvfrom() and sendto() for FreeBSD +divert(4) socket. + +.. + +.. date: 2025-12-19-12-38-01 +.. gh-issue: 116738 +.. nonce: iMt3Ol +.. section: Library + +Make the attributes in :mod:`lzma` thread-safe on the :term:`free threaded +<free threading>` build. + +.. + +.. date: 2025-12-18-22-58-46 +.. gh-issue: 142950 +.. nonce: EJ8w-T +.. section: Library + +Fix regression in :mod:`argparse` where format specifiers in help strings +raised :exc:`ValueError`. + +.. + +.. date: 2025-12-17-20-18-17 +.. gh-issue: 142881 +.. nonce: 5IizIQ +.. section: Library + +Fix concurrent and reentrant call of :func:`atexit.unregister`. + +.. + +.. date: 2025-12-12-08-51-29 +.. gh-issue: 142615 +.. nonce: GoJ6el +.. section: Library + +Fix possible crashes when initializing :class:`asyncio.Task` or +:class:`asyncio.Future` multiple times. These classes can now be initialized +only once and any subsequent initialization attempt will raise a +RuntimeError. Patch by Kumar Aditya. + +.. + +.. date: 2025-12-10-10-00-06 +.. gh-issue: 142517 +.. nonce: fG4hbe +.. section: Library + +The non-``compat32`` :mod:`email` policies now correctly handle refolding +encoded words that contain bytes that can not be decoded in their specified +character set. Previously this resulted in an encoding exception during +folding. + +.. + +.. date: 2025-12-06-19-49-20 +.. gh-issue: 138122 +.. nonce: m3EF9E +.. section: Library + +The Tachyon profiler's live TUI now integrates with the experimental +:mod:`!_colorize` theming system. Users can customize colors via +:func:`!_colorize.set_theme` (experimental API, subject to change). A +:class:`!LiveProfilerLight` theme is provided for light terminal +backgrounds. Patch by Pablo Galindo. + +.. + +.. date: 2025-12-05-17-22-25 +.. gh-issue: 142306 +.. nonce: Gj3_1m +.. section: Library + +Improve errors for :meth:`Element.remove +<xml.etree.ElementTree.Element.remove>`. + +.. + +.. date: 2025-10-04-20-48-02 +.. gh-issue: 63016 +.. nonce: EC9QN_ +.. section: Library + +Add a ``flags`` parameter to :meth:`mmap.mmap.flush` to control +synchronization behavior. + +.. + +.. date: 2025-09-23-16-41-21 +.. gh-issue: 139262 +.. nonce: RO0E98 +.. section: Library + +Some keystrokes can be swallowed in the new ``PyREPL`` on Windows, +especially when used together with the ALT key. Fix by Chris Eibl. + +.. + +.. date: 2025-09-14-22-26-50 +.. gh-issue: 138897 +.. nonce: vnUb_L +.. section: Library + +Improved :data:`license`/:data:`copyright`/:data:`credits` display in the +:term:`REPL`: now uses a pager. + +.. + +.. date: 2025-08-17-00-28-50 +.. gh-issue: 135852 +.. nonce: lQqOjQ +.. section: Library + +Add :func:`!_winapi.RegisterEventSource`, +:func:`!_winapi.DeregisterEventSource` and :func:`!_winapi.ReportEvent`. +Using these functions in :class:`~logging.handlers.NTEventLogHandler` to +replace :mod:`!pywin32`. + +.. + +.. date: 2025-06-22-18-57-19 +.. gh-issue: 109263 +.. nonce: f92V95 +.. section: Library + +Starting a process from spawn context in :mod:`multiprocessing` no longer +sets the start method globally. + +.. + +.. date: 2025-04-19-17-34-11 +.. gh-issue: 132715 +.. nonce: XXl47F +.. section: Library + +Skip writing objects during marshalling once a failure has occurred. + +.. + +.. date: 2025-10-30-19-28-42 +.. gh-issue: 140806 +.. nonce: RBT9YH +.. section: Documentation + +Add documentation for :func:`enum.bin`. + +.. + +.. date: 2026-01-12-22-49-36 +.. gh-issue: 134584 +.. nonce: guDlsj +.. section: Core and Builtins + +Eliminate redundant refcounting from ``_CONTAINS_OP``, ``_CONTAINS_OP_SET`` +and ``_CONTAINS_OP_DICT``. + +.. + +.. date: 2026-01-10-17-13-04 +.. gh-issue: 143604 +.. nonce: BygbVT +.. section: Core and Builtins + +Fix a reference counting issue in the JIT tracer where the current executor +could be prematurely freed during tracing. + +.. + +.. date: 2026-01-06-12-30-03 +.. gh-issue: 143469 +.. nonce: vHVhEY +.. section: Core and Builtins + +Enable :opcode:`!LOAD_ATTR_MODULE` specialization even if +:func:`!__getattr__` is defined in module. + +.. + +.. date: 2026-01-04-23-53-42 +.. gh-issue: 134584 +.. nonce: CNrxI_ +.. section: Core and Builtins + +Eliminate redundant refcounting from ``TO_BOOL_STR``. + +.. + +.. date: 2026-01-04-16-56-17 +.. gh-issue: 143377 +.. nonce: YJqMCa +.. section: Core and Builtins + +Fix a crash in :func:`!_interpreters.capture_exception` when the exception +is incorrectly formatted. Patch by Bénédikt Tran. + +.. + +.. date: 2026-01-04-11-08-20 +.. gh-issue: 139757 +.. nonce: AR6LG0 +.. section: Core and Builtins + +Add ``BINARY_OP_SUBSCR_USTR_INT`` to specialize reading an ASCII character +from any string. Patch by Chris Eibl. + +.. + +.. date: 2026-01-03-15-44-51 +.. gh-issue: 141504 +.. nonce: sbnJlM +.. section: Core and Builtins + +Factor out tracing and optimization heuristics into a single object. Patch +by Donghee Na. + +.. + +.. date: 2026-01-03-14-47-49 +.. gh-issue: 142982 +.. nonce: 0lAtc7 +.. section: Core and Builtins + +Specialize :opcode:`CALL_FUNCTION_EX` for Python and non-Python callables. + +.. + +.. date: 2026-01-03-14-02-11 +.. gh-issue: 136924 +.. nonce: UMgdPn +.. section: Core and Builtins + +The interactive help mode in the :term:`REPL` no longer incorrectly syntax +highlights text input as Python code. Contributed by Olga Matoula. + +.. + +.. date: 2026-01-02-22-35-12 +.. gh-issue: 139757 +.. nonce: v5LRew +.. section: Core and Builtins + +Fix unintended bytecode specialization for non-ascii string. Patch by +Donghee Na, Ken Jin and Chris Eibl. + +.. + +.. date: 2026-01-02-17-11-16 +.. gh-issue: 143361 +.. nonce: YDnvdC +.. section: Core and Builtins + +Add ``PY_VECTORCALL_ARGUMENTS_OFFSET`` to +``_Py_CallBuiltinClass_StackRefSteal`` to avoid redundant allocations + +.. + +.. date: 2026-01-01-23-41-50 +.. gh-issue: 131798 +.. nonce: QUqDdK +.. section: Core and Builtins + +The JIT optimizer now understands more generator instructions. + +.. + +.. date: 2026-01-01-17-01-24 +.. gh-issue: 134584 +.. nonce: nis8LC +.. section: Core and Builtins + +Eliminate redundant refcounting from ``_LOAD_ATTR_SLOT``. + +.. + +.. date: 2025-12-30-06-48-48 +.. gh-issue: 143189 +.. nonce: in_sv2 +.. section: Core and Builtins + +Fix crash when inserting a non-:class:`str` key into a split table +dictionary when the key matches an existing key in the split table but has +no corresponding value in the dict. + +.. + +.. date: 2025-12-27-23-57-43 +.. gh-issue: 143228 +.. nonce: m3EF9E +.. section: Core and Builtins + +Fix use-after-free in perf trampoline when toggling profiling while threads +are running or during interpreter finalization with daemon threads active. +The fix uses reference counting to ensure trampolines are not freed while +any code object could still reference them. Pach by Pablo Galindo + +.. + +.. date: 2025-12-27-13-18-12 +.. gh-issue: 142664 +.. nonce: peeEDV +.. section: Core and Builtins + +Fix a use-after-free crash in :meth:`memoryview.__hash__ <object.__hash__>` +when the ``__hash__`` method of the referenced object mutates that object or +the view. Patch by Bénédikt Tran. + +.. + +.. date: 2025-12-27-12-25-06 +.. gh-issue: 142557 +.. nonce: KWOc8b +.. section: Core and Builtins + +Fix a use-after-free crash in :ref:`bytearray.__mod__ <bytes-formatting>` +when the :class:`!bytearray` is mutated while formatting the ``%``-style +arguments. Patch by Bénédikt Tran. + +.. + +.. date: 2025-12-27-10-14-26 +.. gh-issue: 143195 +.. nonce: MNldfr +.. section: Core and Builtins + +Fix use-after-free crashes in :meth:`bytearray.hex` and +:meth:`memoryview.hex` when the separator's :meth:`~object.__len__` mutates +the original object. Patch by Bénédikt Tran. + +.. + +.. date: 2025-12-26-11-00-44 +.. gh-issue: 143183 +.. nonce: rhxzZr +.. section: Core and Builtins + +Fix a bug in the JIT when dealing with unsupported control-flow or +operations. + +.. + +.. date: 2025-12-24-13-44-24 +.. gh-issue: 142975 +.. nonce: 8C4vIP +.. section: Core and Builtins + +Fix crash after unfreezing all objects tracked by the garbage collector on +the :term:`free threaded <free threading>` build. + +.. + +.. date: 2025-12-24-11-39-59 +.. gh-issue: 143135 +.. nonce: 3d5ovx +.. section: Core and Builtins + +Set :data:`sys.flags.inspect` to ``1`` when :envvar:`PYTHONINSPECT` is +``0``. Previously, it was set to ``0`` in this case. + +.. + +.. date: 2025-12-23-23-36-41 +.. gh-issue: 143123 +.. nonce: -51gt_ +.. section: Core and Builtins + +Protect the JIT against recursive tracing. + +.. + +.. date: 2025-12-23-23-06-11 +.. gh-issue: 143092 +.. nonce: 6MISbb +.. section: Core and Builtins + +Fix a crash in the JIT when dealing with ``list.append(x)`` style code. + +.. + +.. date: 2025-12-23-00-13-02 +.. gh-issue: 143003 +.. nonce: 92g5qW +.. section: Core and Builtins + +Fix an overflow of the shared empty buffer in :meth:`bytearray.extend` when +``__length_hint__()`` returns 0 for non-empty iterator. + +.. + +.. date: 2025-12-22-22-37-53 +.. gh-issue: 143006 +.. nonce: ZBQwbN +.. section: Core and Builtins + +Fix a possible assertion error when comparing negative non-integer ``float`` +and ``int`` with the same number of bits in the integer part. + +.. + +.. date: 2025-12-22-16-22-02 +.. gh-issue: 116738 +.. nonce: caQuq_ +.. section: Core and Builtins + +Fix thread safety of :func:`contextvars.Context.run`. + +.. + +.. date: 2025-12-17-19-45-10 +.. gh-issue: 142829 +.. nonce: ICtLXy +.. section: Core and Builtins + +Fix a use-after-free crash in :class:`contextvars.Context` comparison when a +custom ``__eq__`` method modifies the context via +:meth:`~contextvars.ContextVar.set`. + +.. + +.. date: 2025-12-17-10-12-09 +.. gh-issue: 142863 +.. nonce: ZW2ZF8 +.. section: Core and Builtins + +Generate optimized bytecode when calling :class:`list` or :class:`set` with +generator expression. + +.. + +.. date: 2025-11-19-20-42-21 +.. gh-issue: 41779 +.. nonce: Psz9Vo +.. section: Core and Builtins + +Allowed defining any :ref:`__slots__ <slots>` for a class derived from +:class:`tuple` (including classes created by +:func:`collections.namedtuple`). + +.. + +.. date: 2025-09-30-21-59-56 +.. gh-issue: 69605 +.. nonce: qcmGF3 +.. section: Core and Builtins + +Fix edge-cases around already imported modules in the :term:`REPL` +auto-completion of imports. + +.. + +.. date: 2025-09-06-08-29-08 +.. gh-issue: 138568 +.. nonce: iZlalC +.. section: Core and Builtins + +Adjusted the built-in :func:`help` function so that empty inputs are ignored +in interactive mode. + +.. + +.. date: 2025-08-10-12-46-36 +.. gh-issue: 131798 +.. nonce: 5ys0H_ +.. section: Core and Builtins + +Remove bounds check when indexing into tuples with a constant index. + +.. + +.. date: 2025-06-23-20-54-15 +.. gh-issue: 134584 +.. nonce: ZNcziF +.. section: Core and Builtins + +Eliminate redundant refcounting from ``_CALL_TYPE_1``. Patch by Tomas Roun + +.. + +.. date: 2025-04-04-20-38-29 +.. gh-issue: 132108 +.. nonce: UwZIQy +.. section: Core and Builtins + +Speed up :meth:`int.from_bytes` when passed object supports :ref:`buffer +protocol <bufferobjects>`, like :class:`bytearray` by ~1.2x. + +.. + +.. date: 2024-12-29-21-33-08 +.. gh-issue: 128334 +.. nonce: 3c5Nou +.. section: Core and Builtins + +Make the :class:`slice` class subscriptable at runtime to be consistent with +typing implementation. + +.. + +.. date: 2025-11-17-17-46-16 +.. gh-issue: 141671 +.. nonce: cVXNW5 +.. section: C API + +:c:macro:`PyMODINIT_FUNC` (and the new :c:macro:`PyMODEXPORT_FUNC`) now adds +a linkage declaration (``__declspec(dllexport)``) on Windows. diff --git a/Misc/NEWS.d/3.15.0a6.rst b/Misc/NEWS.d/3.15.0a6.rst new file mode 100644 index 00000000000000..c3e20e0f662fb2 --- /dev/null +++ b/Misc/NEWS.d/3.15.0a6.rst @@ -0,0 +1,1101 @@ +.. date: 2026-02-10-08-00-17 +.. gh-issue: 144648 +.. nonce: KEuUXp +.. release date: 2026-02-11 +.. section: macOS + +Allowed _remote_debugging to build on more OS versions by using +proc_listpids() rather than proc_listallpids(). + +.. + +.. date: 2026-02-09-23-01-49 +.. gh-issue: 124111 +.. nonce: WmQG7S +.. section: macOS + +Update macOS installer to use Tcl/Tk 9.0.3. + +.. + +.. date: 2026-02-09-22-43-47 +.. gh-issue: 144551 +.. nonce: VOfgfD +.. section: macOS + +Update macOS installer to use OpenSSL 3.5.5. + +.. + +.. date: 2026-01-05-21-36-58 +.. gh-issue: 80620 +.. nonce: p1bD58 +.. section: Windows + +Support negative timestamps in :func:`time.gmtime`, :func:`time.localtime`, +and various :mod:`datetime` functions. + +.. + +.. date: 2026-02-03-07-57-24 +.. gh-issue: 144415 +.. nonce: U3L15r +.. section: Tests + +The Android testbed now distinguishes between stdout/stderr messages which +were triggered by a newline, and those triggered by a manual call to +``flush``. This fixes logging of progress indicators and similar content. + +.. + +.. date: 2026-01-08-16-56-59 +.. gh-issue: 65784 +.. nonce: aKNo1U +.. section: Tests + +Add support for parametrized resource ``wantobjects`` in regrtests, which +allows to run Tkinter tests with the specified value of +:data:`!tkinter.wantobjects`, for example ``-u wantobjects=0``. + +.. + +.. date: 2026-01-21-12-34-05 +.. gh-issue: 144125 +.. nonce: TAz5uo +.. section: Security + +:mod:`~email.generator.BytesGenerator` will now refuse to serialize (write) +headers that are unsafely folded or delimited; see +:attr:`~email.policy.Policy.verify_generated_headers`. (Contributed by Bas +Bloemsaat and Petr Viktorin in :gh:`121650`). + +.. + +.. date: 2026-01-16-14-40-31 +.. gh-issue: 143935 +.. nonce: U2YtKl +.. section: Security + +Fixed a bug in the folding of comments when flattening an email message +using a modern email policy. Comments consisting of a very long sequence of +non-foldable characters could trigger a forced line wrap that omitted the +required leading space on the continuation line, causing the remainder of +the comment to be interpreted as a new header field. This enabled header +injection with carefully crafted inputs. + +.. + +.. date: 2026-01-16-11-51-19 +.. gh-issue: 143925 +.. nonce: mrtcHW +.. section: Security + +Reject control characters in ``data:`` URL media types. + +.. + +.. date: 2026-01-16-11-43-47 +.. gh-issue: 143923 +.. nonce: DuytMe +.. section: Security + +Reject control characters in POP3 commands. + +.. + +.. date: 2026-01-16-11-41-06 +.. gh-issue: 143921 +.. nonce: AeCOor +.. section: Security + +Reject control characters in IMAP commands. + +.. + +.. date: 2026-01-16-11-13-15 +.. gh-issue: 143919 +.. nonce: kchwZV +.. section: Security + +Reject control characters in :class:`http.cookies.Morsel` fields and values. + +.. + +.. date: 2026-01-16-11-07-36 +.. gh-issue: 143916 +.. nonce: dpWeOD +.. section: Security + +Reject C0 control characters within wsgiref.headers.Headers fields, values, +and parameters. + +.. + +.. date: 2026-02-06-23-58-54 +.. gh-issue: 144538 +.. nonce: 5_OvGv +.. section: Library + +Bump the version of pip bundled in ensurepip to version 26.0.1 + +.. + +.. date: 2026-02-05-17-15-31 +.. gh-issue: 144493 +.. nonce: XuxwVu +.. section: Library + +Improve an exception error message in ``_overlapped.BindLocal()`` that is +raised when :meth:`asyncio.loop.sock_connect` is called on a +:class:`asyncio.ProactorEventLoop` with a socket that has an invalid address +family. + +.. + +.. date: 2026-02-03-14-16-49 +.. gh-issue: 144386 +.. nonce: 9Wa59r +.. section: Library + +Add support for arbitrary descriptors :meth:`!__enter__`, :meth:`!__exit__`, +:meth:`!__aenter__`, and :meth:`!__aexit__` in :class:`contextlib.ExitStack` +and :class:`contextlib.AsyncExitStack`, for consistency with the +:keyword:`with` and :keyword:`async with` statements. + +.. + +.. date: 2026-02-03-08-50-58 +.. gh-issue: 123471 +.. nonce: yF1Gym +.. section: Library + +Make concurrent iteration over +:class:`itertools.combinations_with_replacement` and +:class:`itertools.permutations` safe under free-threading. + +.. + +.. date: 2026-02-02-12-09-38 +.. gh-issue: 74453 +.. nonce: 19h4Z5 +.. section: Library + +Deprecate :func:`os.path.commonprefix` in favor of +:func:`os.path.commonpath` for path segment prefixes. + +The :func:`os.path.commonprefix` function is being deprecated due to having +a misleading name and module. The function is not safe to use for path +prefixes despite being included in a module about path manipulation, meaning +it is easy to accidentally introduce path traversal vulnerabilities into +Python programs by using this function. + +.. + +.. date: 2026-02-01-15-25-00 +.. gh-issue: 144380 +.. nonce: U7py_s +.. section: Library + +Improve performance of :class:`io.BufferedReader` line iteration by ~49%. + +.. + +.. date: 2026-01-31-17-15-49 +.. gh-issue: 144363 +.. nonce: X9f0sU +.. section: Library + +Update bundled `libexpat <https://libexpat.github.io/>`_ to 2.7.4 + +.. + +.. date: 2026-01-30-13-23-06 +.. gh-issue: 140824 +.. nonce: J1OCrC +.. section: Library + +When :mod:`faulthandler` dumps the list of third-party extension modules, +ignore sub-modules of stdlib packages. Patch by Victor Stinner. + +.. + +.. date: 2026-01-27-14-23-10 +.. gh-issue: 144206 +.. nonce: l0un4U +.. section: Library + +Improve error messages for buffer overflow in :func:`fcntl.fcntl` and +:func:`fcntl.ioctl`. + +.. + +.. date: 2026-01-27-10-02-04 +.. gh-issue: 144264 +.. nonce: Wmzbol +.. section: Library + +Speed up Base64 decoding of data containing ignored characters (both in +non-strict mode and with an explicit *ignorechars* argument). It is now up +to 2 times faster for multiline Base64 data. + +.. + +.. date: 2026-01-27-09-58-52 +.. gh-issue: 144249 +.. nonce: mCIy95 +.. section: Library + +Add filename context to :exc:`OSError` exceptions raised by +:func:`ssl.SSLContext.load_cert_chain`, allowing users to have more context. + +.. + +.. date: 2026-01-27-00-03-41 +.. gh-issue: 132888 +.. nonce: yhTfUN +.. section: Library + +Fix incorrect use of :func:`ctypes.GetLastError` and add missing error +checks for Windows API calls in :mod:`!_pyrepl.windows_console`. + +.. + +.. date: 2026-01-26-12-30-57 +.. gh-issue: 142956 +.. nonce: X9CS8J +.. section: Library + +Updated :mod:`tomllib` to parse TOML 1.1.0. + +.. + +.. date: 2026-01-25-03-23-20 +.. gh-issue: 144217 +.. nonce: E1wVXH +.. section: Library + +:mod:`mimetypes`: Add support for DICOM files (for medical imaging) with the +official MIME type ``application/dicom``. Patch by Benedikt Johannes. + +.. + +.. date: 2026-01-24-23-11-17 +.. gh-issue: 144212 +.. nonce: IXqVL8 +.. section: Library + +Mime type ``image/jxl`` is now supported by :mod:`mimetypes`. + +.. + +.. date: 2026-01-24-13-49-05 +.. gh-issue: 143594 +.. nonce: nilGlg +.. section: Library + +Add :meth:`symtable.Function.get_cells` and :meth:`symtable.Symbol.is_cell` +methods. + +.. + +.. date: 2026-01-23-06-43-21 +.. gh-issue: 144169 +.. nonce: LFy9yi +.. section: Library + +Fix three crashes when non-string keyword arguments are supplied to objects +in the :mod:`ast` module. + +.. + +.. date: 2026-01-22-10-18-17 +.. gh-issue: 144128 +.. nonce: akwY06 +.. section: Library + +Fix a crash in :meth:`array.array.fromlist` when an element's +:meth:`~object.__index__` method mutates the input list during conversion. + +.. + +.. date: 2026-01-21-19-39-07 +.. gh-issue: 144100 +.. nonce: hLMZ8Y +.. section: Library + +Fixed a crash in ctypes when using a deprecated ``POINTER(str)`` type in +``argtypes``. Instead of aborting, ctypes now raises a proper Python +exception when the pointer target type is unresolved. + +.. + +.. date: 2026-01-20-20-54-46 +.. gh-issue: 143658 +.. nonce: v8i1jE +.. section: Library + +:mod:`importlib.metadata`: Use :meth:`str.lower` and :meth:`str.replace` to +further improve performance of +:meth:`!importlib.metadata.Prepared.normalize`. Patch by Hugo van Kemenade +and Henry Schreiner. + +.. + +.. date: 2026-01-20-16-35-55 +.. gh-issue: 144050 +.. nonce: 0kKFbF +.. section: Library + +Fix :func:`stat.filemode` in the pure-Python implementation to avoid +misclassifying invalid mode values as block devices. + +.. + +.. date: 2026-01-19-16-45-16 +.. gh-issue: 83069 +.. nonce: 0TaeH9 +.. section: Library + +:meth:`subprocess.Popen.wait`: when ``timeout`` is not ``None``, an +efficient event-driven mechanism now waits for process termination, if +available. Linux >= 5.3 uses :func:`os.pidfd_open` + :func:`select.poll`. +macOS and other BSD variants use :func:`select.kqueue` + ``KQ_FILTER_PROC`` ++ ``KQ_NOTE_EXIT``. Windows keeps using ``WaitForSingleObject`` (unchanged). +If none of these mechanisms are available, the function falls back to the +traditional busy loop (non-blocking call and short sleeps). Patch by +Giampaolo Rodola. + +.. + +.. date: 2026-01-19-12-48-59 +.. gh-issue: 144030 +.. nonce: 7OK_gB +.. section: Library + +The Python implementation of :func:`functools.lru_cache` differed from the +default C implementation in that it did not check that its argument is +callable. This discrepancy is now fixed and both raise a :exc:`TypeError`. + +.. + +.. date: 2026-01-19-10-26-59 +.. gh-issue: 144001 +.. nonce: dGj8Nk +.. section: Library + +Added the *ignorechars* parameter in :func:`binascii.a2b_base64` and +:func:`base64.b64decode`. + +.. + +.. date: 2026-01-19-00-57-40 +.. gh-issue: 144023 +.. nonce: 29XUcp +.. section: Library + +Fixed validation of file descriptor 0 in posix functions when used with +follow_symlinks parameter. + +.. + +.. date: 2026-01-18-14-35-37 +.. gh-issue: 143999 +.. nonce: MneN4O +.. section: Library + +Fix an issue where :func:`inspect.getgeneratorstate` and +:func:`inspect.getcoroutinestate` could fail for generators wrapped by +:func:`types.coroutine` in the suspended state. + +.. + +.. date: 2026-01-17-07-48-27 +.. gh-issue: 143952 +.. nonce: lqJ55y +.. section: Library + +Fixed :mod:`asyncio` debugging tools to work with new remote debugging API. +Patch by Bartosz Sławecki. + +.. + +.. date: 2026-01-16-14-02-39 +.. gh-issue: 143904 +.. nonce: rErHHA +.. section: Library + +:func:`struct.pack_into` now raises OverflowError instead of IndexError for +too large *offset* argument. + +.. + +.. date: 2026-01-16-10-53-17 +.. gh-issue: 143897 +.. nonce: hWJBHN +.. section: Library + +Remove the :meth:`!isxidstart` and :meth:`!isxidcontinue` methods of +:data:`unicodedata.ucd_3_2_0`. They are now only exposed as +:func:`unicodedata.isxidstart` and :func:`unicodedata.isxidcontinue`. + +.. + +.. date: 2026-01-16-06-22-10 +.. gh-issue: 143831 +.. nonce: VLBTLp +.. section: Library + +:class:`annotationlib.ForwardRef` objects are now hashable when created from +annotation scopes with closures. Previously, hashing such objects would +throw an exception. Patch by Bartosz Sławecki. + +.. + +.. date: 2026-01-15-16-04-39 +.. gh-issue: 143874 +.. nonce: 1qQgvo +.. section: Library + +Fixed a bug in :mod:`pdb` where expression results were not sent back to +remote client. + +.. + +.. date: 2026-01-14-20-35-40 +.. gh-issue: 143754 +.. nonce: m2NQXA +.. section: Library + +Add new :mod:`tkinter` widget methods :meth:`!pack_content`, +:meth:`!place_content` and :meth:`!grid_content` which are alternative +spelling of old :meth:`!*_slaves` methods. + +.. + +.. date: 2026-01-13-16-19-50 +.. gh-issue: 143756 +.. nonce: LQOra1 +.. section: Library + +Fix potential thread safety issues in :mod:`ssl` module. + +.. + +.. date: 2026-01-13-15-56-03 +.. gh-issue: 132604 +.. nonce: lvjNTr +.. section: Library + +Previously, :class:`~typing.Protocol` classes that were not decorated with +:deco:`~typing.runtime_checkable`, but that inherited from another +``Protocol`` class that did have this decorator, could be used in +:func:`isinstance` and :func:`issubclass` checks. This behavior is now +deprecated and such checks will throw a :exc:`TypeError` in Python 3.20. +Patch by Bartosz Sławecki. + +.. + +.. date: 2026-01-13-10-38-43 +.. gh-issue: 143543 +.. nonce: DeQRCO +.. section: Library + +Fix a crash in itertools.groupby that could occur when a user-defined +:meth:`~object.__eq__` method re-enters the iterator during key comparison. + +.. + +.. date: 2026-01-11-14-14-19 +.. gh-issue: 143689 +.. nonce: fzHJ2W +.. section: Library + +Fix :meth:`io.BufferedReader.read1` state cleanup on buffer allocation +failure. + +.. + +.. date: 2026-01-09-12-37-19 +.. gh-issue: 143602 +.. nonce: V8vQpj +.. section: Library + +Fix a inconsistency issue in :meth:`~io.RawIOBase.write` that leads to +unexpected buffer overwrite by deduplicating the buffer exports. + +.. + +.. date: 2026-01-07-19-01-59 +.. gh-issue: 142434 +.. nonce: SHRS5p +.. section: Library + +Use ``ppoll()`` if available in :func:`select.poll` to have a timeout +resolution of 1 nanosecond, instead of a resolution of 1 ms. Patch by Victor +Stinner. + +.. + +.. date: 2026-01-07-11-57-59 +.. gh-issue: 140557 +.. nonce: 3P6-nW +.. section: Library + +:class:`array.array` buffers now have the same alignment when empty as when +allocated. Unaligned buffers can still be created by slicing. + +.. + +.. date: 2026-01-05-05-31-05 +.. gh-issue: 143423 +.. nonce: X7YdnR +.. section: Library + +Fix free-threaded build detection in the sampling profiler when +Py_GIL_DISABLED is set to 0. + +.. + +.. date: 2025-12-28-15-55-53 +.. gh-issue: 101178 +.. nonce: 26jYPs +.. section: Library + +Add Ascii85, Base85, and Z85 support to :mod:`binascii` and improve the +performance of the base-85 converters in :mod:`base64`. + +.. + +.. date: 2025-12-19-11-30-31 +.. gh-issue: 142966 +.. nonce: PzGiv2 +.. section: Library + +Fix :func:`!ctypes.POINTER.set_type` not updating the format string to match +the type. + +.. + +.. date: 2025-12-15-02-02-45 +.. gh-issue: 142555 +.. nonce: EC9QN_ +.. section: Library + +:mod:`array`: fix a crash in ``a[i] = v`` when converting *i* to an index +via :meth:`i.__index__ <object.__index__>` or :meth:`i.__float__ +<object.__float__>` mutates the array. + +.. + +.. date: 2025-12-08-18-40-17 +.. gh-issue: 142438 +.. nonce: tH-Y16 +.. section: Library + +Fix _decimal builds configured with EXTRA_FUNCTIONALITY by correcting the +Context.apply wrapper to pass the right argument. + +.. + +.. date: 2025-11-22-20-30-00 +.. gh-issue: 141860 +.. nonce: frksvr +.. section: Library + +Add an ``on_error`` keyword-only parameter to +:func:`multiprocessing.set_forkserver_preload` to control how import +failures during module preloading are handled. Accepts ``'ignore'`` +(default, silent), ``'warn'`` (emit :exc:`ImportWarning`), or ``'fail'`` +(raise exception). Contributed by Nick Neumann and Gregory P. Smith. + +.. + +.. date: 2025-11-06-12-03-29 +.. gh-issue: 125346 +.. nonce: 7Gfpgw +.. section: Library + +Accepting ``+`` and ``/`` characters with an alternative alphabet in +:func:`base64.b64decode` and :func:`base64.urlsafe_b64decode` is now +deprecated. In future Python versions they will be errors in the strict mode +and discarded in the non-strict mode. + +.. + +.. date: 2025-10-27-00-13-04 +.. gh-issue: 140715 +.. nonce: WkozE0 +.. section: Library + +Add ``'%F'`` support to :meth:`~datetime.datetime.strptime`. + +.. + +.. date: 2024-11-27-13-11-16 +.. gh-issue: 67041 +.. nonce: ym2WKK +.. section: Library + +Add the *missing_as_none* parameter to :func:`~urllib.parse.urlparse`, +:func:`~urllib.parse.urlsplit` and :func:`~urllib.parse.urldefrag` +functions. Add the *keep_empty* parameter to +:func:`~urllib.parse.urlunparse` and :func:`~urllib.parse.urlunsplit` +functions. This allows to distinguish between empty and not defined URI +components and preserve empty components. + +.. + +.. date: 2020-07-14-23-54-18 +.. gh-issue: 77188 +.. nonce: TyI3_Q +.. section: Library + +The :mod:`pickle` module now properly handles name-mangled private methods. + +.. + +.. date: 2026-01-13-01-21-20 +.. gh-issue: 143774 +.. nonce: rqGwX1 +.. section: IDLE + +Better explain the operation of Format / Format Paragraph. + +.. + +.. date: 2026-02-10-12-08-58 +.. gh-issue: 134584 +.. nonce: P9LDy5 +.. section: Core and Builtins + +Optimize and eliminate ref-counting in ``_BINARY_OP_SUBSCR_LIST_SLICE`` + +.. + +.. date: 2026-02-08-18-13-38 +.. gh-issue: 144563 +.. nonce: hb3kpp +.. section: Core and Builtins + +Fix interaction of the Tachyon profiler and :mod:`ctypes` and other modules +that load the Python shared library (if present) in an independent map as +this was causing the mechanism that loads the binary information to be +confused. Patch by Pablo Galindo + +.. + +.. date: 2026-02-08-12-47-27 +.. gh-issue: 144601 +.. nonce: E4Yi9J +.. section: Core and Builtins + +Fix crash when importing a module whose ``PyInit`` function raises an +exception from a subinterpreter. + +.. + +.. date: 2026-02-06-17-59-47 +.. gh-issue: 144549 +.. nonce: 5BhPlY +.. section: Core and Builtins + +Fix building the tail calling interpreter on Visual Studio 2026 with +free-threading. + +.. + +.. date: 2026-02-05-13-30-00 +.. gh-issue: 144513 +.. nonce: IjSTd7 +.. section: Core and Builtins + +Fix potential deadlock when using critical sections during stop-the-world +pauses in the free-threaded build. + +.. + +.. date: 2026-02-04-12-19-48 +.. gh-issue: 131798 +.. nonce: My5jLy +.. section: Core and Builtins + +Optimise ``_GUARD_TOS_SLICE`` in the JIT. + +.. + +.. date: 2026-02-04-11-19-45 +.. gh-issue: 144330 +.. nonce: kOowSb +.. section: Core and Builtins + +Move ``classmethod`` and ``staticmethod`` initialization from ``__init__()`` +to ``__new__()``. Patch by Victor Stinner. + +.. + +.. date: 2026-02-03-17-08-13 +.. gh-issue: 144446 +.. nonce: db5619 +.. section: Core and Builtins + +Fix data races in the free-threaded build when reading frame object +attributes while another thread is executing the frame. + +.. + +.. date: 2026-02-02-17-50-14 +.. gh-issue: 120321 +.. nonce: Xfr7tL +.. section: Core and Builtins + +Add ``gi_state``, ``cr_state``, and ``ag_state`` attributes to generators, +coroutines, and async generators that return the current state as a string +(e.g., ``GEN_RUNNING``). The :mod:`inspect` module functions +:func:`~inspect.getgeneratorstate`, :func:`~inspect.getcoroutinestate`, and +:func:`~inspect.getasyncgenstate` now return these attributes directly. + +.. + +.. date: 2026-02-02-17-07-34 +.. gh-issue: 141563 +.. nonce: GheXjr +.. section: Core and Builtins + +Fix thread safety of :c:macro:`! PyDateTime_IMPORT`. + +.. + +.. date: 2026-01-30-15-54-50 +.. gh-issue: 144280 +.. nonce: kgiP5R +.. section: Core and Builtins + +Fix a bug in JIT where the predicate symbol had no truthiness + +.. + +.. date: 2026-01-30-10-38-07 +.. gh-issue: 140550 +.. nonce: Us9vPD +.. section: Core and Builtins + +In :c:member:`PyModuleDef.m_slots`, allow slots that repeat information +present in :c:type:`PyModuleDef`. + +.. + +.. date: 2026-01-29-16-57-11 +.. gh-issue: 139103 +.. nonce: icXIEQ +.. section: Core and Builtins + +Improve scaling of :func:`~collections.namedtuple` instantiation in the +free-threaded build. + +.. + +.. date: 2026-01-29-02-18-08 +.. gh-issue: 144307 +.. nonce: CLbm_o +.. section: Core and Builtins + +Prevent a reference leak in module teardown at interpreter finalization. + +.. + +.. date: 2026-01-29-01-42-14 +.. gh-issue: 144319 +.. nonce: _7EtdB +.. section: Core and Builtins + +Add huge pages support for the pymalloc allocator. Patch by Pablo Galindo + +.. + +.. date: 2026-01-27-17-49-43 +.. gh-issue: 120321 +.. nonce: Vo7c9T +.. section: Core and Builtins + +Made ``gi_yieldfrom`` thread-safe in the free-threading build by using a +lightweight lock on the frame state. + +.. + +.. date: 2026-01-23-20-20-42 +.. gh-issue: 144194 +.. nonce: IbXfxd +.. section: Core and Builtins + +Fix error handling in perf jitdump initialization on memory allocation +failure. + +.. + +.. date: 2026-01-22-17-04-30 +.. gh-issue: 143962 +.. nonce: dQR1a9 +.. section: Core and Builtins + +Name suggestion for not normalized name suggests now the normalized name or +the closest name to the normalized name. If the suggested name is not ASCII, +include also its ASCII representation. + +.. + +.. date: 2026-01-22-16-20-16 +.. gh-issue: 144157 +.. nonce: dxyp7k +.. section: Core and Builtins + +:meth:`bytes.translate` now allows the compiler to unroll its loop more +usefully for a 2x speedup in the common no-deletions specified case. + +.. + +.. date: 2026-01-21-02-30-06 +.. gh-issue: 144068 +.. nonce: 9TTu7v +.. section: Core and Builtins + +Fix JIT tracer memory leak, ensure the JIT tracer state is freed when daemon +threads are cleaned up during interpreter shutdown. + +.. + +.. date: 2026-01-19-02-33-45 +.. gh-issue: 144012 +.. nonce: wVEEWs +.. section: Core and Builtins + +Check if the result is ``NULL`` in ``BINARY_OP_EXTENT`` opcode. + +.. + +.. date: 2026-01-19-01-56-44 +.. gh-issue: 144007 +.. nonce: 1xjdBf +.. section: Core and Builtins + +Eliminate redundant refcounting in the JIT for ``BINARY_OP``. + +.. + +.. date: 2026-01-19-01-26-12 +.. gh-issue: 144005 +.. nonce: Z3O33m +.. section: Core and Builtins + +Eliminate redundant refcounting from ``BINARY_OP_EXTEND``. + +.. + +.. date: 2026-01-16-23-19-38 +.. gh-issue: 143939 +.. nonce: w9TWch +.. section: Core and Builtins + +Fix erroneous "cannot reuse already awaited coroutine" error that could +occur when a generator was run during the process of clearing a coroutine's +frame. + +.. + +.. date: 2026-01-13-22-26-49 +.. gh-issue: 141805 +.. nonce: QzIKPS +.. section: Core and Builtins + +Fix crash in :class:`set` when objects with the same hash are concurrently +added to the set after removing an element with the same hash while the set +still contains elements with the same hash. + +.. + +.. date: 2026-01-11-20-11-36 +.. gh-issue: 143670 +.. nonce: klnGoD +.. section: Core and Builtins + +Fixes a crash in ``ga_repr_items_list`` function. + +.. + +.. date: 2026-01-10-10-58-36 +.. gh-issue: 143650 +.. nonce: k8mR4x +.. section: Core and Builtins + +Fix race condition in :mod:`importlib` where a thread could receive a stale +module reference when another thread's import fails. + +.. + +.. date: 2026-01-08-14-55-31 +.. gh-issue: 143569 +.. nonce: -Ltu3c +.. section: Core and Builtins + +Generator expressions in 3.15 now conform to the documented behavior when +the iterable does not support iteration. This matches the behavior in 3.14 +and earlier + +.. + +.. date: 2025-12-29-19-31-46 +.. gh-issue: 143192 +.. nonce: JxGAyl +.. section: Core and Builtins + +Improve performance of bitwise operations on multi-digit ints. + +.. + +.. date: 2025-12-24-13-19-16 +.. gh-issue: 132657 +.. nonce: _P4DDb +.. section: Core and Builtins + +If we are specializing to ``LOAD_GLOBAL_MODULE`` or ``LOAD_ATTR_MODULE``, +try to enable deferred reference counting for the value, if the object is +owned by a different thread. This applies to the free-threaded build only +and should improve scaling of multi-threaded programs. Note that when +deferred reference counting is enabled, the object will be deallocated by +the GC, rather than by :c:func:`Py_DECREF`. + +.. + +.. date: 2025-12-21-18-12-30 +.. gh-issue: 143055 +.. nonce: PzwccL +.. section: Core and Builtins + +Implement :pep:`798` (Unpacking in Comprehensions). Patch by Adam Hartz. + +.. + +.. date: 2025-11-29-10-06-06 +.. gh-issue: 142037 +.. nonce: OpIGzK +.. section: Core and Builtins + +Improve error messages for printf-style formatting. For errors in the format +string, always include the position of the start of the format unit. For +errors related to the formatted arguments, always include the number or the +name of the argument. Raise more specific errors and include more +information (type and number of arguments, most probable causes of error). + +.. + +.. date: 2025-10-24-17-30-51 +.. gh-issue: 140557 +.. nonce: X2GETk +.. section: Core and Builtins + +:class:`bytearray` buffers now have the same alignment when empty as when +allocated. Unaligned buffers can still be created by slicing. + +.. + +.. date: 2025-10-16-22-36-05 +.. gh-issue: 140232 +.. nonce: u3srgv +.. section: Core and Builtins + +Frozenset objects with immutable elements are no longer tracked by the +garbage collector. + +.. + +.. date: 2024-02-10-05-42-26 +.. gh-issue: 115231 +.. nonce: 6T7dzi +.. section: Core and Builtins + +Setup ``__module__`` attribute for built-in static methods. Patch by Sergey +B Kirpichev. + +.. + +.. date: 2026-01-16-15-04-26 +.. gh-issue: 143869 +.. nonce: vf94km +.. section: C API + +Added :c:func:`PyLong_GetNativeLayout`, :c:struct:`PyLongLayout`, +:c:struct:`PyLongExport`, :c:func:`PyLong_Export`, +:c:func:`PyLong_FreeExport`, :c:struct:`PyLongWriter`, +:c:func:`PyLongWriter_Create`, :c:func:`PyLongWriter_Finish` and +:c:func:`PyLongWriter_Discard` to the limited API. + +.. + +.. date: 2025-12-16-18-39-30 +.. gh-issue: 141070 +.. nonce: 4EKDZ1 +.. section: C API + +Renamed :c:func:`!PyUnstable_Object_Dump` to :c:func:`PyObject_Dump`. + +.. + +.. date: 2026-02-10-06-31-29 +.. gh-issue: 140421 +.. nonce: vxosUx +.. section: Build + +Disable the perf trampoline on older macOS versions where it cannot be +built. + +.. + +.. date: 2026-01-28-19-04-12 +.. gh-issue: 144309 +.. nonce: 3sMFOh +.. section: Build + +Build Python with POSIX 2024, instead of POSIX 2008. Patch by Victor +Stinner. + +.. + +.. date: 2026-01-27-23-39-26 +.. gh-issue: 144278 +.. nonce: tejFwL +.. section: Build + +Enables defining the ``_PY_IMPL_NAME`` and ``_PY_IMPL_CACHE_TAG`` +preprocessor definitions to override :data:`sys.implementation` at build +time. Definitions need to include quotes when setting to a string literal. +Setting the cache tag to ``NULL`` has the effect of completely disabling +automatic creation and use of ``.pyc`` files. + +.. + +.. date: 2026-01-17-15-31-19 +.. gh-issue: 143960 +.. nonce: Zi0EqR +.. section: Build + +Add support for OpenSSL 3.6, drop EOL 3.2. Patch by Hugo van Kemenade. + +.. + +.. date: 2026-01-16-14-27-53 +.. gh-issue: 143941 +.. nonce: TiaE-3 +.. section: Build + +Move WASI-related files to :file:`Platforms/WASI`. Along the way, leave a +deprecated :file:`Tools/wasm/wasi/__main__.py` behind for +backwards-compatibility. + +.. + +.. date: 2026-01-15-03-36-16 +.. gh-issue: 143842 +.. nonce: EZLutl +.. section: Build + +Prevent static builds from clashing with curses by making the optimizer +COLORS table static. diff --git a/Misc/NEWS.d/3.15.0a7.rst b/Misc/NEWS.d/3.15.0a7.rst new file mode 100644 index 00000000000000..7d9681cbcbef00 --- /dev/null +++ b/Misc/NEWS.d/3.15.0a7.rst @@ -0,0 +1,1212 @@ +.. date: 2026-03-10-09-46-44 +.. gh-issue: 145731 +.. nonce: 5uEGgb +.. release date: 2026-03-10 +.. section: Windows + +Fix negative timestamp during DST on Windows. Patch by Hugo van Kemenade. + +.. + +.. date: 2026-02-27-10-57-20 +.. gh-issue: 145307 +.. nonce: ueoT7j +.. section: Windows + +Defers loading of the ``psapi.dll`` module until it is used by +:func:`ctypes.util.dllist`. + +.. + +.. date: 2026-02-13-11-07-51 +.. gh-issue: 144551 +.. nonce: ENtMYD +.. section: Windows + +Updated bundled version of OpenSSL to 3.5.5. + +.. + +.. date: 2026-03-04-17-39-15 +.. gh-issue: 144741 +.. nonce: 0RHhBF +.. section: Tests + +Fix ``test_frame_pointer_unwind`` when Python is built with +:option:`--enable-shared`. Classify also libpython frames as ``"python"``. +Patch by Victor Stinner. + +.. + +.. date: 2026-02-12-12-12-00 +.. gh-issue: 144739 +.. nonce: -fx1tN +.. section: Tests + +When Python was compiled with system expat older then 2.7.2 but tests run +with newer expat, still skip +:class:`!test.test_pyexpat.MemoryProtectionTest`. + +.. + +.. date: 2026-03-04-18-59-17 +.. gh-issue: 145506 +.. nonce: 6hwvEh +.. section: Security + +Fixes :cve:`2026-2297` by ensuring that ``SourcelessFileLoader`` uses +:func:`io.open_code` when opening ``.pyc`` files. + +.. + +.. date: 2026-01-31-21-56-54 +.. gh-issue: 144370 +.. nonce: fp9m8t +.. section: Security + +Disallow usage of control characters in status in :mod:`wsgiref.handlers` to +prevent HTTP header injections. Patch by Benedikt Johannes. + +.. + +.. date: 2026-03-07-15-00-00 +.. gh-issue: 145623 +.. nonce: 2Y7LzT +.. section: Library + +Fix crash in :mod:`struct` when calling :func:`repr` or ``__sizeof__()`` on +an uninitialized :class:`struct.Struct` object created via +``Struct.__new__()`` without calling ``__init__()``. + +.. + +.. date: 2026-03-05-19-01-28 +.. gh-issue: 145551 +.. nonce: gItPRl +.. section: Library + +Fix InvalidStateError when cancelling process created by +:func:`asyncio.create_subprocess_exec` or +:func:`asyncio.create_subprocess_shell`. Patch by Daan De Meyer. + +.. + +.. date: 2026-03-05-16-06-09 +.. gh-issue: 141510 +.. nonce: dFPAQS +.. section: Library + +:mod:`marshal` now supports :class:`frozendict` objects. The marshal format +version was increased to 6. Patch by Victor Stinner. + +.. + +.. date: 2026-03-03-11-49-44 +.. gh-issue: 145417 +.. nonce: m_HxIL +.. section: Library + +:mod:`venv`: Prevent incorrect preservation of SELinux context when copying +the ``Activate.ps1`` script. The script inherited the SELinux security +context of the system template directory, rather than the destination +project directory. + +.. + +.. date: 2026-03-02-20-08-09 +.. gh-issue: 145335 +.. nonce: lVTBvd +.. section: Library + +``os.listdir(-1)`` and ``os.scandir(-1)`` now fail with +``OSError(errno.EBADF)`` rather than listing the current directory. +``os.listxattr(-1)`` now fails with ``OSError(errno.EBADF)`` rather than +listing extended attributes of the current directory. Patch by Victor +Stinner. + +.. + +.. date: 2026-03-02-19-41-39 +.. gh-issue: 145376 +.. nonce: OOzSOh +.. section: Library + +Fix double free and null pointer dereference in unusual error scenarios in +:mod:`hashlib` and :mod:`hmac` modules. + +.. + +.. date: 2026-02-28-00-55-00 +.. gh-issue: 145301 +.. nonce: Lk2bRl +.. section: Library + +:mod:`hmac`: fix a crash when the initialization of the underlying C +extension module fails. + +.. + +.. date: 2026-02-27-19-00-26 +.. gh-issue: 145301 +.. nonce: 2Wih4b +.. section: Library + +:mod:`hashlib`: fix a crash when the initialization of the underlying C +extension module fails. + +.. + +.. date: 2026-02-27-18-04-51 +.. gh-issue: 76007 +.. nonce: 17idfK +.. section: Library + +The ``version`` attribute of the :mod:`tarfile` module is deprecated and +slated for removal in Python 3.20. + +.. + +.. date: 2026-02-23-20-52-55 +.. gh-issue: 145158 +.. nonce: vWJtxI +.. section: Library + +Avoid undefined behaviour from signed integer overflow when parsing format +strings in the :mod:`struct` module. + +.. + +.. date: 2026-02-21-17-34-53 +.. gh-issue: 123853 +.. nonce: 6RUwWh +.. section: Library + +Removed Windows 95 compatibility for :func:`locale.getdefaultlocale`. + +.. + +.. date: 2026-02-20-13-03-10 +.. gh-issue: 66802 +.. nonce: OYcAi_ +.. section: Library + +Add :func:`unicodedata.block` function to return the `Unicode block +<https://www.unicode.org/versions/Unicode17.0.0/core-spec/chapter-3/#G64189>`_ +of a character. + +.. + +.. date: 2026-02-19-20-54-25 +.. gh-issue: 145033 +.. nonce: X9EBPQ +.. section: Library + +Add :data:`typing.TypeForm`, implementing :pep:`747`. Patch by Jelle +Zijlstra. + +.. + +.. date: 2026-02-19-18-02-54 +.. gh-issue: 141510 +.. nonce: qzvYsO +.. section: Library + +:func:`dataclasses.field`: if *metadata* is ``None``, use an empty +:class:`frozendict`, instead of a :func:`~types.MappingProxyType` of an +empty :class:`dict`. Patch by Victor Stinner. + +.. + +.. date: 2026-02-19-17-50-47 +.. gh-issue: 145006 +.. nonce: 9gqA0Q +.. section: Library + +Add :exc:`ModuleNotFoundError` hints when a module for a different ABI +exists. + +.. + +.. date: 2026-02-19-16-26-08 +.. gh-issue: 141510 +.. nonce: 4Qxy8_ +.. section: Library + +``ParameterizedMIMEHeader.params`` of :mod:`email.headerregistry` is now a +:class:`frozendict` instead of a :class:`types.MappingProxyType`. Patch by +Victor Stinner. + +.. + +.. date: 2026-02-19-15-42-06 +.. gh-issue: 134872 +.. nonce: sjYX1- +.. section: Library + +Add valid import name suggestions on :exc:`ModuleNotFoundError`. + +.. + +.. date: 2026-02-19-10-57-40 +.. gh-issue: 88091 +.. nonce: N7qGV- +.. section: Library + +Fix :func:`unicodedata.decomposition` for Hangul characters. + +.. + +.. date: 2026-02-19-00-00-00 +.. gh-issue: 144986 +.. nonce: atexit-leak +.. section: Library + +Fix a memory leak in :func:`atexit.register`. Patch by Shamil Abdulaev. + +.. + +.. date: 2026-02-18-13-45-00 +.. gh-issue: 144777 +.. nonce: R97q0a +.. section: Library + +Fix data races in :class:`io.IncrementalNewlineDecoder` in the +:term:`free-threaded build`. + +.. + +.. date: 2026-02-18-00-00-00 +.. gh-issue: 144809 +.. nonce: nYpEUx +.. section: Library + +Make :class:`collections.deque` copy atomic in the :term:`free-threaded +build`. + +.. + +.. date: 2026-02-17-11-28-37 +.. gh-issue: 141510 +.. nonce: OpAz0M +.. section: Library + +The :mod:`copy` module now supports the :class:`frozendict` type. Patch by +Pieter Eendebak based on work by Victor Stinner. + +.. + +.. date: 2026-02-17-11-15-17 +.. gh-issue: 141510 +.. nonce: ZmqEUb +.. section: Library + +The :mod:`json` module now supports the :class:`frozendict` type. Patch by +Victor Stinner. + +.. + +.. date: 2026-02-15-12-02-20 +.. gh-issue: 144835 +.. nonce: w_oS_J +.. section: Library + +Added missing explanations for some parameters in :func:`glob.glob` and +:func:`glob.iglob`. + +.. + +.. date: 2026-02-15-00-00-00 +.. gh-issue: 144833 +.. nonce: TUelo1 +.. section: Library + +Fixed a use-after-free in :mod:`ssl` when ``SSL_new()`` returns NULL in +``newPySSLSocket()``. The error was reported via a dangling pointer after +the object had already been freed. + +.. + +.. date: 2026-02-14-14-56-44 +.. gh-issue: 140715 +.. nonce: AbSheM +.. section: Library + +Add ``'%D'`` support to :meth:`~datetime.datetime.strptime`. + +.. + +.. date: 2026-02-13-14-20-10 +.. gh-issue: 144782 +.. nonce: 0Y8TKj +.. section: Library + +Fix :class:`argparse.ArgumentParser` to be :mod:`pickleable <pickle>`. + +.. + +.. date: 2026-02-13-11-14-18 +.. gh-issue: 144763 +.. nonce: cDwnEE +.. section: Library + +Fix a race condition in :mod:`tracemalloc`: it no longer detaches the +attached thread state to acquire its internal lock. Patch by Victor Stinner. + +.. + +.. date: 2026-02-13-00-00-00 +.. gh-issue: 142224 +.. nonce: BidiMissing +.. section: Library + +:func:`unicodedata.bidirectional` now return the correct default bidi class +for unassigned code points. + +.. + +.. date: 2026-02-12-17-56-17 +.. gh-issue: 117865 +.. nonce: jE1ema +.. section: Library + +Reduce the import time of :mod:`inspect` module by ~20%. + +.. + +.. date: 2026-02-10-22-05-51 +.. gh-issue: 144156 +.. nonce: UbrC7F +.. section: Library + +Fix the folding of headers by the :mod:`email` library when :rfc:`2047` +encoded words are used. Now whitespace is correctly preserved and also +correctly added between adjacent encoded words. The latter property was +broken by the fix for gh-92081, which mostly fixed previous failures to +preserve whitespace. + +.. + +.. date: 2026-02-10-16-56-05 +.. gh-issue: 66305 +.. nonce: PZ6GN8 +.. section: Library + +Fixed a hang on Windows in the :mod:`tempfile` module when trying to create +a temporary file or subdirectory in a non-writable directory. + +.. + +.. date: 2026-02-09-02-16-36 +.. gh-issue: 144615 +.. nonce: s04x4n +.. section: Library + +Methods directly decorated with :deco:`functools.singledispatchmethod` now +dispatch on the second argument when called after being accessed as class +attributes. Patch by Bartosz Sławecki. + +.. + +.. date: 2026-02-08-17-09-10 +.. gh-issue: 144321 +.. nonce: w58PhQ +.. section: Library + +The functional syntax for creating :class:`typing.NamedTuple` classes now +supports passing any :term:`iterable` of fields and types. Previously, only +sequences were supported. + +.. + +.. date: 2026-02-07-16-37-42 +.. gh-issue: 144475 +.. nonce: 8tFEXw +.. section: Library + +Calling :func:`repr` on :func:`functools.partial` is now safer when the +partial object's internal attributes are replaced while the string +representation is being generated. + +.. + +.. date: 2026-02-07-16-31-42 +.. gh-issue: 144285 +.. nonce: iyH9iL +.. section: Library + +Attribute suggestions in :exc:`AttributeError` tracebacks are now formatted +differently to make them easier to understand, for example: ``Did you mean +'.datetime.now' instead of '.now'``. Contributed by Bartosz Sławecki. + +.. + +.. date: 2026-02-03-19-57-41 +.. gh-issue: 144316 +.. nonce: wop870 +.. section: Library + +Fix crash in ``_remote_debugging`` that caused ``test_external_inspection`` +to intermittently fail. Patch by Taegyun Kim. + +.. + +.. date: 2026-01-17-08-44-25 +.. gh-issue: 143637 +.. nonce: qyPqDo +.. section: Library + +Fixed a crash in socket.sendmsg() that could occur if ancillary data is +mutated re-entrantly during argument parsing. + +.. + +.. date: 2026-01-12-19-39-57 +.. gh-issue: 140652 +.. nonce: HvM9Bl +.. section: Library + +Fix a crash in :func:`!_interpchannels.list_all` after closing a channel. + +.. + +.. date: 2026-01-11-18-35-52 +.. gh-issue: 143698 +.. nonce: gXDzsJ +.. section: Library + +Allow *scheduler* and *setpgroup* arguments to be explicitly :const:`None` +when calling :func:`os.posix_spawn` or :func:`os.posix_spawnp`. Patch by +Bénédikt Tran. + +.. + +.. date: 2026-01-11-16-59-22 +.. gh-issue: 143698 +.. nonce: b-Cpeb +.. section: Library + +Raise :exc:`TypeError` instead of :exc:`SystemError` when the *scheduler* in +:func:`os.posix_spawn` or :func:`os.posix_spawnp` is not a tuple. Patch by +Bénédikt Tran. + +.. + +.. date: 2026-01-11-13-03-32 +.. gh-issue: 142516 +.. nonce: u7An-s +.. section: Library + +:mod:`ssl`: fix reference leaks in :class:`ssl.SSLContext` objects. Patch by +Bénédikt Tran. + +.. + +.. date: 2026-01-10-22-58-30 +.. gh-issue: 85809 +.. nonce: 0eW4wt +.. section: Library + +Added :term:`path-like object` support for :func:`shutil.make_archive`. + +.. + +.. date: 2026-01-01-05-26-00 +.. gh-issue: 143304 +.. nonce: Kv7x9Q +.. section: Library + +Fix :class:`ctypes.CDLL` to honor the ``handle`` parameter on POSIX systems. + +.. + +.. date: 2025-12-18-00-14-16 +.. gh-issue: 142781 +.. nonce: gcOeYF +.. section: Library + +:mod:`zoneinfo`: fix a crash when instantiating :class:`~zoneinfo.ZoneInfo` +objects for which the internal class-level cache is inconsistent. + +.. + +.. date: 2025-12-16-13-34-48 +.. gh-issue: 142787 +.. nonce: wNitJX +.. section: Library + +Fix assertion failure in :mod:`sqlite3` blob subscript when slicing with +indices that result in an empty slice. + +.. + +.. date: 2025-12-06-16-14-18 +.. gh-issue: 142352 +.. nonce: pW5HLX88 +.. section: Library + +Fix :meth:`asyncio.StreamWriter.start_tls` to transfer buffered data from +:class:`~asyncio.StreamReader` to the SSL layer, preventing data loss when +upgrading a connection to TLS mid-stream (e.g., when implementing PROXY +protocol support). + +.. + +.. date: 2025-10-10-14-08-58 +.. gh-issue: 139899 +.. nonce: 09leRY +.. section: Library + +Introduced :meth:`importlib.abc.MetaPathFinder.discover` and +:meth:`importlib.abc.PathEntryFinder.discover` to allow module and submodule +name discovery without assuming the use of traditional filesystem based +imports. + +.. + +.. date: 2025-08-04-23-20-43 +.. gh-issue: 137335 +.. nonce: IIjDJN +.. section: Library + +Get rid of any possibility of a name conflict for named pipes in +:mod:`multiprocessing` and :mod:`asyncio` on Windows, no matter how small. + +.. + +.. date: 2025-06-24-19-07-18 +.. gh-issue: 135883 +.. nonce: 38cePA +.. section: Library + +Fix :mod:`sqlite3`'s :ref:`interactive shell <sqlite3-cli>` keeping part of +previous commands when scrolling history. + +.. + +.. date: 2024-09-30-15-31-59 +.. gh-issue: 124748 +.. nonce: KYzYFp +.. section: Library + +Improve :exc:`TypeError` error message when +:meth:`!weakref.WeakKeyDictionary.update` is used with keyword-only +parameters. + +.. + +.. date: 2023-02-05-20-02-30 +.. gh-issue: 80667 +.. nonce: 7LmzeA +.. section: Library + +Add support for Tangut Ideographs names in :mod:`unicodedata`. + +.. + +.. bpo: 42353 +.. date: 2022-02-05-00-15-03 +.. nonce: 0ebVGG +.. section: Library + +The :mod:`re` module gains a new :func:`re.prefixmatch` function as an +explicit spelling of what has to date always been known as :func:`re.match`. +:class:`re.Pattern` similary gains a :meth:`re.Pattern.prefixmatch` method. + +Why? Explicit is better than implicit. Other widely used languages all use +the term "match" to mean what Python uses the term "search" for. The +unadorened "match" name in Python has been a frequent case of confusion and +coding bugs due to the inconsistency with the rest if the software industry. + +We do not plan to deprecate and remove the older ``match`` name. + +.. + +.. bpo: 40243 +.. date: 2020-04-10-14-29-53 +.. nonce: 85HRib +.. section: Library + +Fix :meth:`!unicodedata.ucd_3_2_0.numeric` for non-decimal values. + +.. + +.. bpo: 40212 +.. date: 2020-04-07-05-09-34 +.. nonce: oPYeBs +.. section: Library + +Re-enable :func:`os.posix_fallocate` and :func:`os.posix_fadvise` on AIX. + +.. + +.. bpo: 3405 +.. date: 2018-05-11-12-26-16 +.. nonce: CacMw9 +.. section: Library + +Add support for user data of Tk virtual events and detail for ``Enter``, +``Leave``, ``FocusIn``, ``FocusOut``, and ``ConfigureRequest`` events to +:mod:`tkinter`. + +.. + +.. bpo: 32234 +.. date: 2017-12-15-09-32-57 +.. nonce: XaOkhR +.. section: Library + +:class:`mailbox.Mailbox` instances can now be used as a context manager. The +Mailbox is locked on context entry and unlocked and closed at context exit. + +.. + +.. date: 2026-03-03-08-18-00 +.. gh-issue: 145450 +.. nonce: VI7GXj +.. section: Documentation + +Document missing public :class:`wave.Wave_write` getter methods. + +.. + +.. date: 2026-01-06-16-04-08 +.. gh-issue: 110937 +.. nonce: SyO5lk +.. section: Documentation + +Document rest of full public :class:`importlib.metadata.Distribution` API. +Also add the (already documented) :class:`~importlib.metadata.PackagePath` +to ``__all__``. + +.. + +.. date: 2025-08-02-18-59-01 +.. gh-issue: 136246 +.. nonce: RIK7nE +.. section: Documentation + +A new "Improve this page" link is available in the left-hand sidebar of the +docs, offering links to create GitHub issues, discussion forum posts, or +pull requests. + +.. + +.. date: 2026-03-09-18-52-03 +.. gh-issue: 145701 +.. nonce: 79KQyO +.. section: Core and Builtins + +Fix :exc:`SystemError` when ``__classdict__`` or +``__conditional_annotations__`` is in a class-scope inlined comprehension. +Found by OSS Fuzz in :oss-fuzz:`491105000`. + +.. + +.. date: 2026-03-06-21-05-05 +.. gh-issue: 145615 +.. nonce: NKXXZgDW +.. section: Core and Builtins + +Fixed a memory leak in the :term:`free-threaded build` where mimalloc pages +could become permanently unreclaimable until the owning thread exited. + +.. + +.. date: 2026-03-06-01-36-20 +.. gh-issue: 116738 +.. nonce: OWVWRx +.. section: Core and Builtins + +Make :meth:`!mmap.mmap.set_name` thread-safe on the :term:`free threaded +<free threading>` build. + +.. + +.. date: 2026-03-05-19-10-56 +.. gh-issue: 145566 +.. nonce: H4RupyYN +.. section: Core and Builtins + +In the free threading build, skip the stop-the-world pause when reassigning +``__class__`` on a newly created object. + +.. + +.. date: 2026-03-05-16-16-17 +.. gh-issue: 143055 +.. nonce: qDUFlY +.. section: Core and Builtins + +Fix crash in AST unparser when unparsing dict comprehension unpacking. Found +by OSS Fuzz in :oss-fuzz:`489790200`. + +.. + +.. date: 2026-03-01-13-37-31 +.. gh-issue: 145335 +.. nonce: e36kPJ +.. section: Core and Builtins + +Fix a crash in :func:`os.pathconf` when called with ``-1`` as the path +argument. + +.. + +.. date: 2026-02-28-16-46-17 +.. gh-issue: 145376 +.. nonce: lG5u1a +.. section: Core and Builtins + +Fix reference leaks in various unusual error scenarios. + +.. + +.. date: 2026-02-26-21-36-00 +.. gh-issue: 145234 +.. nonce: w0mQ9n +.. section: Core and Builtins + +Fixed a ``SystemError`` in the parser when an encoding cookie (for example, +UTF-7) decodes to carriage returns (``\r``). Newlines are now normalized +after decoding in the string tokenizer. + +Patch by Pablo Galindo. + +.. + +.. date: 2026-02-26-21-07-38 +.. gh-issue: 145275 +.. nonce: qE-3O1 +.. section: Core and Builtins + +Added the :option:`-X pathconfig_warnings<-X>` and +:envvar:`PYTHON_PATHCONFIG_WARNINGS` options, allowing to disable warnings +from :ref:`sys-path-init`. + +.. + +.. date: 2026-02-26-20-51-54 +.. gh-issue: 145273 +.. nonce: B5QcUp +.. section: Core and Builtins + +A warning is now shown during :ref:`sys-path-init` if it can't find a valid +standard library. + +.. + +.. date: 2026-02-26-18-00-00 +.. gh-issue: 145241 +.. nonce: hL2k9Q +.. section: Core and Builtins + +Specialized the parser error for when ``with`` items are followed by a +trailing comma (for example, ``with item,:``), raising a clearer +:exc:`SyntaxError` message. Patch by Pablo Galindo and Bartosz Sławecki. + +.. + +.. date: 2026-02-26-12-00-00 +.. gh-issue: 130555 +.. nonce: TMSOIu +.. section: Core and Builtins + +Fix use-after-free in :meth:`dict.clear` when the dictionary values are +embedded in an object and a destructor causes re-entrant mutation of the +dictionary. + +.. + +.. date: 2026-02-25-15-02-08 +.. gh-issue: 145197 +.. nonce: G6hAUk +.. section: Core and Builtins + +Fix JIT trace crash when recording function from cleared generator frame. + +.. + +.. date: 2026-02-24-18-30-56 +.. gh-issue: 145187 +.. nonce: YjPu1Z +.. section: Core and Builtins + +Fix compiler assertion fail when a type parameter bound contains an invalid +expression in a conditional block. + +.. + +.. date: 2026-02-23-23-18-28 +.. gh-issue: 145142 +.. nonce: T-XbVe +.. section: Core and Builtins + +Fix a crash in the free-threaded build when the dictionary argument to +:meth:`str.maketrans` is concurrently modified. + +.. + +.. date: 2026-02-22-22-05-09 +.. gh-issue: 145118 +.. nonce: TaKMJE +.. section: Core and Builtins + +:meth:`str.maketrans` now accepts :class:`frozendict`. + +.. + +.. date: 2026-02-22-20-15-00 +.. gh-issue: 144015 +.. nonce: pystrhex_simd +.. section: Core and Builtins + +Speed up :meth:`bytes.hex`, :meth:`bytearray.hex`, :func:`binascii.hexlify`, +and :mod:`hashlib` ``.hexdigest()`` operations with SIMD on x86-64, ARM64, +and ARM32 with NEON when built with gcc (version 12 or higher) or clang +(version 3 or higher) compilers. Around 1.1-3x faster for common 16-64 byte +inputs such as hashlib hex digests, and up to 8x faster for larger data. + +.. + +.. date: 2026-02-22-19-05-03 +.. gh-issue: 145118 +.. nonce: bU6Sic +.. section: Core and Builtins + +:func:`type` now accepts :class:`frozendict` as an argument. + +.. + +.. date: 2026-02-22-07-51-10 +.. gh-issue: 145064 +.. nonce: iIMGKA +.. section: Core and Builtins + +Fix JIT optimizer assertion failure during ``CALL_ALLOC_AND_ENTER_INIT`` +side exit. + +.. + +.. date: 2026-02-21-12-16-46 +.. gh-issue: 145055 +.. nonce: VyT-zI +.. section: Core and Builtins + +:func:`exec` and :func:`eval` now accept :class:`frozendict` for *globals*. +Patch by Victor Stinner. + +.. + +.. date: 2026-02-21-09-47-45 +.. gh-issue: 145058 +.. nonce: e-RBw- +.. section: Core and Builtins + +Fix a crash when :func:`!__lazy_import__` is passed a non-string argument, +by raising an :exc:`TypeError` instead. + +.. + +.. date: 2026-02-19-12-49-15 +.. gh-issue: 144995 +.. nonce: Ob2oYJ +.. section: Core and Builtins + +Optimize :class:`memoryview` comparison: a :class:`memoryview` is equal to +itself, there is no need to compare values. Patch by Victor Stinner. + +.. + +.. date: 2026-02-18-21-44-39 +.. gh-issue: 141510 +.. nonce: 7LST2O +.. section: Core and Builtins + +Update specializer to support frozendict. Patch by Donghee Na. + +.. + +.. date: 2026-02-17-22-27-11 +.. gh-issue: 141510 +.. nonce: -4yYsf +.. section: Core and Builtins + +Optimize :meth:`!frozendict.fromkeys` to avoid unnecessary thread-safety +operations in frozendict cases. Patch by Donghee Na. + +.. + +.. date: 2026-02-17-21-04-03 +.. gh-issue: 100239 +.. nonce: LyVabQ +.. section: Core and Builtins + +Speedup ``BINARY_OP_EXTEND`` for exact floats and medium-size integers by up +to 15%. Patch by Chris Eibl. + +.. + +.. date: 2026-02-17-18-27-28 +.. gh-issue: 144914 +.. nonce: DcXO4m +.. section: Core and Builtins + +Use ``mimalloc`` for raw memory allocations such as via +:c:func:`PyMem_RawMalloc` for better performance on :term:`free-threaded +builds <free-threaded build>`. Patch by Kumar Aditya. + +.. + +.. date: 2026-02-16-12-28-43 +.. gh-issue: 144872 +.. nonce: k9_Q30 +.. section: Core and Builtins + +Fix heap buffer overflow in the parser found by OSS-Fuzz. + +.. + +.. date: 2026-02-13-18-30-59 +.. gh-issue: 144766 +.. nonce: JGu3x3 +.. section: Core and Builtins + +Fix a crash in fork child process when perf support is enabled. + +.. + +.. date: 2026-02-13-12-00-00 +.. gh-issue: 144759 +.. nonce: d3qYpe +.. section: Core and Builtins + +Fix undefined behavior in the lexer when ``start`` and ``multi_line_start`` +pointers are ``NULL`` in ``_PyLexer_remember_fstring_buffers()`` and +``_PyLexer_restore_fstring_buffers()``. The ``NULL`` pointer arithmetic +(``NULL - valid_pointer``) is now guarded with explicit ``NULL`` checks. + +.. + +.. date: 2026-02-12-19-01-13 +.. gh-issue: 141510 +.. nonce: KlKjZg +.. section: Core and Builtins + +Add built-in :class:`frozendict` type. Patch by Victor Stinner. + +.. + +.. date: 2026-02-12-12-39-50 +.. gh-issue: 144681 +.. nonce: Ns2OT2 +.. section: Core and Builtins + +Fix a JIT assertion failure when a conditional branch jumps to the same +target as the fallthrough path. + +.. + +.. date: 2026-02-11-13-30-11 +.. gh-issue: 143300 +.. nonce: yjB63- +.. section: Core and Builtins + +Add :c:func:`PyUnstable_SetImmortal` C-API function to mark objects as +:term:`immortal`. + +.. + +.. date: 2026-02-11-11-28-25 +.. gh-issue: 144702 +.. nonce: XjFumv +.. section: Core and Builtins + +Clarify the error message raised when a class pattern is used to match on a +non-class object. + +.. + +.. date: 2026-02-08-13-14-00 +.. gh-issue: 144569 +.. nonce: pjlJVe +.. section: Core and Builtins + +Optimize ``BINARY_SLICE`` for list, tuple, and unicode by avoiding temporary +``slice`` object creation. + +.. + +.. date: 2026-02-06-21-45-52 +.. gh-issue: 144438 +.. nonce: GI_uB1LR +.. section: Core and Builtins + +Align the QSBR thread state array to a 64-byte cache line boundary to avoid +false sharing in the :term:`free-threaded build`. + +.. + +.. date: 2025-12-06-15-46-32 +.. gh-issue: 142349 +.. nonce: IdTuYL +.. section: Core and Builtins + +Implement :pep:`810`. Patch by Pablo Galindo and Dino Viehland. + +.. + +.. date: 2025-11-09-15-44-58 +.. gh-issue: 141226 +.. nonce: KTb_3F +.. section: Core and Builtins + +Deprecate :pep:`456` support for providing an external definition of the +string hashing scheme. Removal is scheduled for Python 3.19. Patch by +Bénédikt Tran. + +.. + +.. date: 2025-09-15-13-28-48 +.. gh-issue: 138912 +.. nonce: 61EYbn +.. section: Core and Builtins + +Improve :opcode:`MATCH_CLASS` performance by up to 52% in certain cases. +Patch by Marc Mueller. + +.. + +.. date: 2025-02-19-21-06-30 +.. gh-issue: 130327 +.. nonce: z3TaR8 +.. section: Core and Builtins + +Fix erroneous clearing of an object's :attr:`~object.__dict__` if +overwritten at runtime. + +.. + +.. date: 2023-07-26-00-03-00 +.. gh-issue: 80667 +.. nonce: N7Dh8B +.. section: Core and Builtins + +Literals using the ``\N{name}`` escape syntax can now construct CJK +ideographs and Hangul syllables using case-insensitive names. + +.. + +.. date: 2026-03-03-14-59-57 +.. gh-issue: 142417 +.. nonce: HiNP5j +.. section: C API + +Restore private provisional ``_Py_InitializeMain()`` function removed in +Python 3.14. Patch by Victor Stinner. + +.. + +.. date: 2026-02-24-14-46-05 +.. gh-issue: 144748 +.. nonce: uhnFtE +.. section: C API + +:c:func:`PyErr_CheckSignals` now raises the exception scheduled by +:c:func:`PyThreadState_SetAsyncExc`, if any. + +.. + +.. date: 2026-02-18-15-12-34 +.. gh-issue: 144981 +.. nonce: 4ZdM63 +.. section: C API + +Made :c:func:`PyUnstable_Code_SetExtra`, :c:func:`PyUnstable_Code_GetExtra`, +and :c:func:`PyUnstable_Eval_RequestCodeExtraIndex` thread-safe on the +:term:`free threaded <free threading>` build. + +.. + +.. date: 2026-02-12-19-03-31 +.. gh-issue: 141510 +.. nonce: U_1tjz +.. section: C API + +Add the following functions for the new :class:`frozendict` type: + +* :c:func:`PyAnyDict_Check` +* :c:func:`PyAnyDict_CheckExact` +* :c:func:`PyFrozenDict_Check` +* :c:func:`PyFrozenDict_CheckExact` +* :c:func:`PyFrozenDict_New` + +Patch by Victor Stinner. + +.. + +.. date: 2026-02-10-14-49-49 +.. gh-issue: 121617 +.. nonce: 57vMqa +.. section: C API + +``Python.h`` now also includes ``<string.h>`` in the limited C API version +3.11 and newer to fix the :c:macro:`Py_CLEAR` macro which uses ``memcpy()``. +Patch by Victor Stinner. + +.. + +.. date: 2026-01-27-18-15-15 +.. gh-issue: 144175 +.. nonce: qHK5Jf +.. section: C API + +Add :c:func:`PyArg_ParseArray` and :c:func:`PyArg_ParseArrayAndKeywords` +functions to parse arguments of functions using the :c:macro:`METH_FASTCALL` +calling convention. Patch by Victor Stinner. + +.. + +.. date: 2026-02-27-18-10-02 +.. gh-issue: 144533 +.. nonce: 21fk9L +.. section: Build + +Use wasmtime's ``--argv0`` to auto-discover sysconfig in WASI builds + +.. + +.. date: 2026-02-22-13-35-20 +.. gh-issue: 145110 +.. nonce: KgWofW +.. section: Build + +Fix targets "Clean" and "CLeanAll" in case of PGO builds on Windows. Patch +by Chris Eibl. + +.. + +.. date: 2026-02-10-18-26-04 +.. gh-issue: 144679 +.. nonce: FIH73W +.. section: Build + +When building with Visual Studio 2026 (Version 18), use PlatformToolSet v145 +by default. Patch by Chris Eibl. + +.. + +.. date: 2026-02-10-16-59-56 +.. gh-issue: 144675 +.. nonce: Wrf3Es +.. section: Build + +Update to WASI SDK 30. + +.. + +.. date: 2025-07-21-00-33-38 +.. gh-issue: 136677 +.. nonce: Y1_3ec +.. section: Build + +Introduce executable specific linker flags to ``./configure``. diff --git a/Misc/NEWS.d/3.15.0a8.rst b/Misc/NEWS.d/3.15.0a8.rst new file mode 100644 index 00000000000000..28e9ca85166620 --- /dev/null +++ b/Misc/NEWS.d/3.15.0a8.rst @@ -0,0 +1,1583 @@ +.. date: 2026-03-14-17-31-39 +.. gh-issue: 145986 +.. nonce: ifSSr8 +.. release date: 2026-04-07 +.. section: Security + +:mod:`xml.parsers.expat`: Fixed a crash caused by unbounded C recursion when +converting deeply nested XML content models with +:meth:`~xml.parsers.expat.xmlparser.ElementDeclHandler`. This addresses +:cve:`2026-4224`. + +.. + +.. date: 2026-03-06-17-03-38 +.. gh-issue: 145599 +.. nonce: kchwZV +.. section: Security + +Reject control characters in :class:`http.cookies.Morsel` +:meth:`~http.cookies.Morsel.update` and +:meth:`~http.cookies.BaseCookie.js_output`. This addresses :cve:`2026-3644`. + +.. + +.. date: 2026-01-16-12-04-49 +.. gh-issue: 143930 +.. nonce: zYC5x3 +.. section: Security + +Reject leading dashes in URLs passed to :func:`webbrowser.open`. + +.. + +.. date: 2026-04-06-11-15-46 +.. gh-issue: 148157 +.. nonce: JFnZDn +.. section: Core and Builtins + +Fix an unlikely crash when parsing an invalid type comments for function +parameters. Found by OSS Fuzz in :oss-fuzz:`492782951`. + +.. + +.. date: 2026-04-06-00-00-00 +.. gh-issue: 100239 +.. nonce: binopxt +.. section: Core and Builtins + +Propagate result type and uniqueness information through +``_BINARY_OP_EXTEND`` in the tier 2 optimizer, enabling elimination of +downstream type guards and selection of inplace float operations. + +.. + +.. date: 2026-04-05-15-20-00 +.. gh-issue: 148144 +.. nonce: f7qA0x +.. section: Core and Builtins + +Initialize ``_PyInterpreterFrame.visited`` when copying interpreter frames +so incremental GC does not read an uninitialized byte from generator and +frame-object copies. + +.. + +.. date: 2026-04-05-00-00-00 +.. gh-issue: 148072 +.. nonce: xid9Pe +.. section: Core and Builtins + +Cache ``pickle.dumps`` and ``pickle.loads`` per interpreter in the XIData +framework, avoiding repeated module lookups on every cross-interpreter data +transfer. This speeds up +:class:`~concurrent.futures.InterpreterPoolExecutor` for mutable types +(``list``, ``dict``) by 1.7x--3.3x. + +.. + +.. date: 2026-04-04-22-20-00 +.. gh-issue: 148110 +.. nonce: cL5x2Q +.. section: Core and Builtins + +Fix :func:`sys.set_lazy_imports_filter` so relative lazy imports pass the +resolved imported module name to the filter callback. Patch by Pablo +Galindo. + +.. + +.. date: 2026-04-04-20-59-12 +.. gh-issue: 148083 +.. nonce: 9ZHNBN +.. section: Core and Builtins + +Constant-fold ``_CONTAINS_OP_SET`` for :class:`frozenset`. Patch by Donghee +Na. + +.. + +.. date: 2026-04-01-12-52-31 +.. gh-issue: 144319 +.. nonce: iZk4hs +.. section: Core and Builtins + +Fix a bug that could cause applications with specific allocation patterns to +leak memory via Huge Pages if compiled with Huge Page support. Patch by +Pablo Galindo + +.. + +.. date: 2026-04-01-12-35-55 +.. gh-issue: 147985 +.. nonce: YVirHJ +.. section: Core and Builtins + +Make :c:func:`PySet_Contains` attempt a lock-free lookup, similar to +:meth:`!set.__contains__`. This avoids acquiring the set object mutex in +the normal case. + +.. + +.. date: 2026-03-31-18-07-53 +.. gh-issue: 147856 +.. nonce: 62Dwee +.. section: Core and Builtins + +Allow the *count* argument of :meth:`bytes.replace` to be a keyword. + +.. + +.. date: 2026-03-31-01-06-35 +.. gh-issue: 146615 +.. nonce: fix-method-get +.. section: Core and Builtins + +Fix a crash in :meth:`~object.__get__` for :c:expr:`METH_METHOD` descriptors +when an invalid (non-type) object is passed as the second argument. Patch by +Steven Sun. + +.. + +.. date: 2026-03-30-20-00-00 +.. gh-issue: 146306 +.. nonce: C45609 +.. section: Core and Builtins + +Optimize compact integer arithmetic in the JIT by mutating +uniquely-referenced operands in place, avoiding allocation of a new int +object. Speeds up the pyperformance ``spectral_norm`` benchmark by ~10%. + +.. + +.. date: 2026-03-29-11-39-05 +.. gh-issue: 146587 +.. nonce: YJicXt +.. section: Core and Builtins + +Fix type slot assignment incase of multiple slots for same name in type +object implementation. Patch by Kumar Aditya. + +.. + +.. date: 2026-03-27-17-14-18 +.. gh-issue: 126910 +.. nonce: hooVFQ +.. section: Core and Builtins + +Set frame pointers in ``aarch64-unknown-linux-gnu`` JIT code, allowing most +native profilers and debuggers to unwind through them. Patch by Diego Russo + +.. + +.. date: 2026-03-26-11-18-45 +.. gh-issue: 146388 +.. nonce: O0u1c3 +.. section: Core and Builtins + +Adds a null check to handle when the JIT optimizer runs out of space when +dealing with contradictions in ``make_bottom``. + +.. + +.. date: 2026-03-22-19-30-00 +.. gh-issue: 146308 +.. nonce: AxnRVA +.. section: Core and Builtins + +Fixed multiple error handling issues in the :mod:`!_remote_debugging` module +including a double-free in code object caching, memory leaks on allocation +failure, missing exception checks in binary format varint decoding, +reference leaks on error paths in frame chain processing, and inconsistent +thread status error reporting across platforms. Patch by Pablo Galindo. + +.. + +.. date: 2026-03-22-12-00-00 +.. gh-issue: 146306 +.. nonce: 870ef4 +.. section: Core and Builtins + +Optimize float arithmetic in the JIT by mutating uniquely-referenced +operands in place, avoiding allocation of a new float object. Speeds up the +pyperformance ``nbody`` benchmark by ~19%. + +.. + +.. date: 2026-03-21-15-05-14 +.. gh-issue: 146128 +.. nonce: DG1Hfa +.. section: Core and Builtins + +Fix a bug which could cause constant values to be partially corrupted in +AArch64 JIT code. This issue is theoretical, and hasn't actually been +observed in unmodified Python interpreters. + +.. + +.. date: 2026-03-21-11-55-16 +.. gh-issue: 146250 +.. nonce: ahl3O2 +.. section: Core and Builtins + +Fixed a memory leak in :exc:`SyntaxError` when re-initializing it. + +.. + +.. date: 2026-03-21-08-48-25 +.. gh-issue: 146245 +.. nonce: cqM3_4 +.. section: Core and Builtins + +Fixed reference leaks in :mod:`socket` when audit hooks raise exceptions in +:func:`socket.getaddrinfo` and :meth:`!socket.sendto`. + +.. + +.. date: 2026-03-21-08-11-58 +.. gh-issue: 146151 +.. nonce: 4-lhim +.. section: Core and Builtins + +:class:`memoryview` now supports the :c:expr:`float complex` and +:c:expr:`double complex` C types: formatting characters ``'F'`` and ``'D'`` +respectively. Patch by Sergey B Kirpichev. + +.. + +.. date: 2026-03-20-13-55-14 +.. gh-issue: 146196 +.. nonce: Zg70Kb +.. section: Core and Builtins + +Fix potential Undefined Behavior in :c:func:`PyUnicodeWriter_WriteASCII` by +adding a zero-length check. Patch by Shamil Abdulaev. + +.. + +.. date: 2026-03-20-13-07-33 +.. gh-issue: 146227 +.. nonce: MqBPEo +.. section: Core and Builtins + +Fix wrong type in ``_Py_atomic_load_uint16`` in the C11 atomics backend +(``pyatomic_std.h``), which used a 32-bit atomic load instead of 16-bit. +Found by Mohammed Zuhaib. + +.. + +.. date: 2026-03-20-12-52-55 +.. gh-issue: 146205 +.. nonce: M4yKdf +.. section: Core and Builtins + +Fixed a bug where :meth:`select.epoll.close`, :meth:`select.kqueue.close`, +and :meth:`select.devpoll.close` silently ignored errors. + +.. + +.. date: 2026-03-20-12-26-24 +.. gh-issue: 146199 +.. nonce: vV8V9s +.. section: Core and Builtins + +Comparison of code objects now handles errors correctly. + +.. + +.. date: 2026-03-20-11-34-17 +.. gh-issue: 145667 +.. nonce: _Agp9o +.. section: Core and Builtins + +Remove the ``GET_ITER_YIELD_FROM`` instruction, modifying ``SEND`` to pair +with ``GET_ITER`` when compiling ``yield from`` expressions. + +.. + +.. date: 2026-03-20-00-39-25 +.. gh-issue: 146192 +.. nonce: 8aQ6sC +.. section: Core and Builtins + +Add Base32 support to :mod:`binascii` and improve the performance of the +Base32 converters in :mod:`base64`. Patch by James Seo. + +.. + +.. date: 2026-03-19-16-16-40 +.. gh-issue: 135871 +.. nonce: jSExZ3 +.. section: Core and Builtins + +Improve multithreaded scaling of PyMutex in low-contention scenarios by +reloading the lock's internal state, without slowing down high-contention +scenarios. + +.. + +.. date: 2026-03-19-01-19-34 +.. gh-issue: 146096 +.. nonce: R9tkJX +.. section: Core and Builtins + +Fixed segmentation fault when called repr for BaseExceptionGroup with empty +or 1-size tuple args. + +.. + +.. date: 2026-03-18-18-52-00 +.. gh-issue: 146056 +.. nonce: r1tVSo +.. section: Core and Builtins + +Fix :func:`repr` for lists and tuples containing ``NULL``\ s. + +.. + +.. date: 2026-03-17-14-20-56 +.. gh-issue: 145059 +.. nonce: aB3xKm +.. section: Core and Builtins + +Fixed :py:attr:`sys.lazy_modules` to include lazy modules without submodules. Patch +by Bartosz Sławecki. + +.. + +.. date: 2026-03-17-00-00-00 +.. gh-issue: 146041 +.. nonce: 7799bb +.. section: Core and Builtins + +Fix free-threading scaling bottleneck in :func:`sys.intern` and +:c:func:`PyObject_SetAttr` by avoiding the interpreter-wide lock when the +string is already interned and immortalized. + +.. + +.. date: 2026-03-15-21-45-35 +.. gh-issue: 145990 +.. nonce: tmXwRB +.. section: Core and Builtins + +``python --help-env`` sections are now sorted by environment variable name. + +.. + +.. date: 2026-03-15-20-47-34 +.. gh-issue: 145990 +.. nonce: 14BUzw +.. section: Core and Builtins + +``python --help-xoptions`` is now sorted by ``-X`` option name. + +.. + +.. date: 2026-03-13-12-24-17 +.. gh-issue: 145876 +.. nonce: LWFO2K +.. section: Core and Builtins + +:exc:`AttributeError`\ s and :exc:`KeyError`\ s raised in :meth:`!keys` or +:meth:`!__getitem__` during dictionary unpacking (``{**mymapping}`` or +``func(**mymapping)``) are no longer masked by :exc:`TypeError`. + +.. + +.. date: 2026-03-13-09-48-57 +.. gh-issue: 127958 +.. nonce: U-znTv +.. section: Core and Builtins + +Support tracing from function entrypoints in the JIT. Patch by Ken Jin. + +.. + +.. date: 2026-03-11-21-27-28 +.. gh-issue: 145376 +.. nonce: LfDvyw +.. section: Core and Builtins + +Fix GC tracking in ``structseq.__replace__()``. + +.. + +.. date: 2026-03-11-19-09-47 +.. gh-issue: 145792 +.. nonce: X5KUhc +.. section: Core and Builtins + +Fix out-of-bounds access when invoking faulthandler on a CPython build +compiled without support for VLAs. + +.. + +.. date: 2026-03-11-00-13-59 +.. gh-issue: 142183 +.. nonce: 2iVhJH +.. section: Core and Builtins + +Avoid a pathological case where repeated calls at a specific stack depth +could be significantly slower. + +.. + +.. date: 2026-03-10-22-38-40 +.. gh-issue: 145779 +.. nonce: 5375381d80 +.. section: Core and Builtins + +Improve scaling of :func:`classmethod` and :func:`staticmethod` calls in the +free-threaded build by avoiding the descriptor ``__get__`` call. + +.. + +.. date: 2026-03-10-19-00-39 +.. gh-issue: 145783 +.. nonce: dS5TM9 +.. section: Core and Builtins + +Fix an unlikely crash in the parser when certain errors were erroneously not +propagated. Found by OSS Fuzz in :oss-fuzz:`491369109`. + +.. + +.. date: 2026-03-10-12-52-06 +.. gh-issue: 145685 +.. nonce: 80B7gK +.. section: Core and Builtins + +Improve scaling of type attribute lookups in the :term:`free-threaded build` +by avoiding contention on the internal type lock. + +.. + +.. date: 2026-03-09-00-00-00 +.. gh-issue: 145713 +.. nonce: KR6azvzI +.. section: Core and Builtins + +Make :meth:`bytearray.resize` thread-safe in the free-threaded build by +using a critical section and calling the lock-held variant of the resize +function. + +.. + +.. date: 2026-02-28-18-42-36 +.. gh-issue: 145036 +.. nonce: 70Kbfz +.. section: Core and Builtins + +In free-threaded build, fix race condition when calling :meth:`!__sizeof__` +on a :class:`list` + +.. + +.. date: 2026-02-14-15-51-16 +.. gh-issue: 134584 +.. nonce: 6WFSuB +.. section: Core and Builtins + +Eliminate redundant refcounting for ``MATCH_CLASS`` in the JIT. + +.. + +.. date: 2026-02-14-13-07-08 +.. gh-issue: 69605 +.. nonce: 4aL4hn +.. section: Core and Builtins + +Add :mod:`math.integer` to :term:`REPL` auto-completion of imports. + +.. + +.. date: 2026-02-08-01-19-50 +.. gh-issue: 131798 +.. nonce: PaWDNH +.. section: Core and Builtins + +Optimize ``_ITER_CHECK_RANGE`` and ``_ITER_CHECK_LIST`` in the JIT + +.. + +.. date: 2026-01-31-15-15-43 +.. gh-issue: 143414 +.. nonce: Jgl4xu +.. section: Core and Builtins + +Add tracking to the JIT optimizer to determine whether a reference is +uniquely owned or shared + +.. + +.. date: 2026-01-10-12-59-58 +.. gh-issue: 143636 +.. nonce: dzr26e +.. section: Core and Builtins + +Fix a crash when calling :class:`SimpleNamespace.__replace__() +<types.SimpleNamespace>` on non-namespace instances. Patch by Bénédikt Tran. + +.. + +.. date: 2026-01-07-23-07-17 +.. gh-issue: 126910 +.. nonce: d8zdm- +.. section: Core and Builtins + +Set frame pointers in ``x86_64-unknown-linux-gnu`` JIT code, allowing most +native profilers and debuggers to unwind through them. + +.. + +.. date: 2025-11-02-16-23-17 +.. gh-issue: 140594 +.. nonce: YIWUpl +.. section: Core and Builtins + +Fix an out of bounds read when a single NUL character is read from the +standard input. Patch by Shamil Abdulaev. + +.. + +.. date: 2025-11-01-01-49-52 +.. gh-issue: 140870 +.. nonce: iknc12 +.. section: Core and Builtins + +Add support for module attributes in the :term:`REPL` auto-completion of +imports. + +.. + +.. date: 2026-04-07-01-04-00 +.. gh-issue: 144503 +.. nonce: argvfs +.. section: Library + +Fix a regression introduced in 3.14.3 and 3.13.12 where the +:mod:`multiprocessing` ``forkserver`` start method would fail with +:exc:`BrokenPipeError` when the parent process had a very large +:data:`sys.argv`. The argv is now passed to the forkserver as separate +command-line arguments rather than being embedded in the ``-c`` command +string, avoiding the operating system's per-argument length limit. + +.. + +.. date: 2026-04-06-11-20-24 +.. gh-issue: 148153 +.. nonce: ZtsuTl +.. section: Library + +:func:`base64.b32encode` now always raises :exc:`ValueError` instead of +:exc:`AssertionError` for the value of *map01* with invalid length. + +.. + +.. date: 2026-04-01-18-17-55 +.. gh-issue: 73613 +.. nonce: PLEebm +.. section: Library + +Add the *padded* parameter in functions related to Base32 and Base64 codecs +in the :mod:`binascii` and :mod:`base64` modules. In the encoding functions +it controls whether the pad character can be added in the output, in the +decoding functions it controls whether padding is required in input. Padding +of input no longer required in :func:`base64.urlsafe_b64decode` by default. + +.. + +.. date: 2026-04-01-11-05-36 +.. gh-issue: 146613 +.. nonce: GzjUFK +.. section: Library + +:mod:`itertools`: Fix a crash in :func:`itertools.groupby` when the grouper +iterator is concurrently mutated. + +.. + +.. date: 2026-03-31-19-54-32 +.. gh-issue: 147944 +.. nonce: 3dn8GZ +.. section: Library + +Accepted range for the *bytes_per_sep* argument of :meth:`bytes.hex`, +:meth:`bytearray.hex`, :meth:`memoryview.hex`, and :func:`binascii.b2a_hex` +is now increased, so passing ``sys.maxsize`` and ``-sys.maxsize`` is now +valid. + +.. + +.. date: 2026-03-28-13-19-20 +.. gh-issue: 146080 +.. nonce: srN12a +.. section: Library + +:mod:`ssl`: fix a crash when an SNI callback tries to use an SSL object that +has already been garbage-collected. Patch by Bénédikt Tran. + +.. + +.. date: 2026-03-28-12-20-19 +.. gh-issue: 146556 +.. nonce: Y8Eson +.. section: Library + +Fix :func:`annotationlib.get_annotations` hanging indefinitely when called +with ``eval_str=True`` on a callable that has a circular ``__wrapped__`` +chain (e.g. ``f.__wrapped__ = f``). Cycle detection using an id-based +visited set now stops the traversal and falls back to the globals found so +far, mirroring the approach of :func:`inspect.unwrap`. + +.. + +.. date: 2026-03-28-12-05-34 +.. gh-issue: 146090 +.. nonce: wf9_ef +.. section: Library + +:mod:`sqlite3`: fix a crash when :meth:`sqlite3.Connection.create_collation` +fails with `SQLITE_BUSY <https://sqlite.org/rescode.html#busy>`__. Patch by +Bénédikt Tran. + +.. + +.. date: 2026-03-28-12-01-48 +.. gh-issue: 146090 +.. nonce: wh1qJR +.. section: Library + +:mod:`sqlite3`: properly raise :exc:`MemoryError` instead of +:exc:`SystemError` when a context callback fails to be allocated. Patch by +Bénédikt Tran. + +.. + +.. date: 2026-03-27-12-00-00 +.. gh-issue: 146507 +.. nonce: 1D95A7 +.. section: Library + +Make :meth:`asyncio.SelectorEventLoop` stream transport's +:meth:`~asyncio.WriteTransport.get_write_buffer_size` O(1) by maintaining a +running byte counter instead of iterating the buffer on every call. + +.. + +.. date: 2026-03-26-14-51-55 +.. gh-issue: 145056 +.. nonce: QS-6l1 +.. section: Library + +Fix merging of :class:`collections.OrderedDict` and :class:`frozendict`. + +.. + +.. date: 2026-03-26-14-44-07 +.. gh-issue: 145056 +.. nonce: L9KPC3 +.. section: Library + +Add support for merging :class:`collections.UserDict` and +:class:`frozendict`. + +.. + +.. date: 2026-03-26-11-04-42 +.. gh-issue: 145633 +.. nonce: RWjlaX +.. section: Library + +Fix ``struct.pack('f', float)``: use :c:func:`PyFloat_Pack4` to raise +:exc:`OverflowError`. Patch by Sergey B Kirpichev and Victor Stinner. + +.. + +.. date: 2026-03-26-02-06-52 +.. gh-issue: 146440 +.. nonce: HXjhQO +.. section: Library + +:mod:`json`: Add the *array_hook* parameter to :func:`~json.load` and +:func:`~json.loads` functions: allow a callback for JSON literal array types +to customize Python lists in the resulting decoded object. Passing combined +:class:`frozendict` to *object_pairs_hook* param and :class:`tuple` to +``array_hook`` will yield a deeply nested immutable Python structure +representing the JSON data. + +.. + +.. date: 2026-03-25-21-08-51 +.. gh-issue: 146431 +.. nonce: zERPwe +.. section: Library + +Add the *wrapcol* parameter to :mod:`base64` functions +:func:`~base64.b16encode`, :func:`~base64.b32encode`, +:func:`~base64.b32hexencode`, :func:`~base64.b85encode` and +:func:`~base64.z85encode`, and :mod:`binascii` functions +:func:`~binascii.b2a_base32` and :func:`~binascii.b2a_base85`. Add the +*ignorechars* parameter to :mod:`base64` functions +:func:`~base64.b16decode`, :func:`~base64.b32decode`, +:func:`~base64.b32hexdecode`, :func:`~base64.b85decode` and +:func:`~base64.z85decode`, and :mod:`binascii` functions +:func:`~binascii.a2b_hex`, :func:`~binascii.unhexlify`, +:func:`~binascii.a2b_base32` and :func:`~binascii.a2b_base85`. + +.. + +.. date: 2026-03-24-03-49-50 +.. gh-issue: 146310 +.. nonce: WhlDir +.. section: Library + +The :mod:`ensurepip` module no longer looks for ``pip-*.whl`` wheel packages +in the current directory. + +.. + +.. date: 2026-03-21-16-03-16 +.. gh-issue: 141510 +.. nonce: tKptA7 +.. section: Library + +Support :class:`frozendict` in :mod:`plistlib`, for serialization only. +Patch by Hugo van Kemenade. + +.. + +.. date: 2026-03-21-10-02-20 +.. gh-issue: 146238 +.. nonce: 2WpMOj +.. section: Library + +Support half-floats (type code ``'e'`` of the :mod:`struct` module) in the +:mod:`array` module. Patch by Sergey B Kirpichev. + +.. + +.. date: 2026-03-21-08-23-26 +.. gh-issue: 140947 +.. nonce: owZ4r_ +.. section: Library + +Fix incorrect contextvars handling in server tasks created by +:mod:`asyncio`. Patch by Kumar Aditya. + +.. + +.. date: 2026-03-21-06-21-38 +.. gh-issue: 146151 +.. nonce: yNpgml +.. section: Library + +Support the :c:expr:`float complex` and :c:expr:`double complex` C types in +the :mod:`array` module: formatting characters ``'F'`` and ``'D'`` +respectively. Patch by Sergey B Kirpichev. + +.. + +.. date: 2026-03-20-16-17-31 +.. gh-issue: 143387 +.. nonce: 9Waopa +.. section: Library + +In importlib.metadata, when a distribution file is corrupt and there is no +metadata file, calls to ``Distribution.metadata()`` (including implicit +calls from other properties like ``.name`` and ``.requires``) will now raise +a ``MetadataNotFound`` Exception. This allows callers to distinguish between +missing metadata and a degenerate (empty) metadata. Previously, if the file +was missing, an empty ``PackageMetadata`` would be returned and would be +indistinguishable from the presence of an empty file. + +.. + +.. date: 2026-03-20-14-53-00 +.. gh-issue: 146228 +.. nonce: OJVEDL +.. section: Library + +Cached FastPath objects in importlib.metadata are now cleared on fork, +avoiding broken references to zip files during fork. + +.. + +.. date: 2026-03-20-00-28-00 +.. gh-issue: 146171 +.. nonce: P5Jk2R7v +.. section: Library + +Nested :exc:`AttributeError` suggestions now include property-backed +attributes on nested objects without executing the property getter. + +.. + +.. date: 2026-03-18-23-54-36 +.. gh-issue: 145410 +.. nonce: NvLWj5 +.. section: Library + +On Windows, :func:`sysconfig.get_platform` now gets the platform from the +``_sysconfig`` module instead of parsing :data:`sys.version` string. Patch +by Victor Stinner. + +.. + +.. date: 2026-03-18-16-58-17 +.. gh-issue: 146091 +.. nonce: lBbo1L +.. section: Library + +Fix a bug in :func:`termios.tcsetwinsize` where passing a sequence that +raises an exception in ``__getitem__`` would cause a :exc:`SystemError` +instead of propagating the original exception. + +.. + +.. date: 2026-03-17-20-52-24 +.. gh-issue: 146083 +.. nonce: NxZa_c +.. section: Library + +Update bundled `libexpat <https://libexpat.github.io/>`_ to version 2.7.5. + +.. + +.. date: 2026-03-17-20-41-27 +.. gh-issue: 146076 +.. nonce: yoBNnB +.. section: Library + +:mod:`zoneinfo`: fix crashes when deleting ``_weak_cache`` from a +:class:`zoneinfo.ZoneInfo` subclass. + +.. + +.. date: 2026-03-17-19-51-05 +.. gh-issue: 123471 +.. nonce: oY4UR5 +.. section: Library + +Make concurrent iteration over :class:`itertools.zip_longest` safe under +free-threading. + +.. + +.. date: 2026-03-17-19-30-45 +.. gh-issue: 146075 +.. nonce: 85sCSh +.. section: Library + +Errors when calling :func:`functools.partial` with a malformed keyword will +no longer crash the interpreter. + +.. + +.. date: 2026-03-17-11-46-20 +.. gh-issue: 146054 +.. nonce: udYcqn +.. section: Library + +Limit the size of :func:`encodings.search_function` cache. Found by OSS Fuzz +in :oss-fuzz:`493449985`. + +.. + +.. date: 2026-03-16-00-00-00 +.. gh-issue: 146004 +.. nonce: xOptProp +.. section: Library + +All :option:`-X` options from the Python command line are now propagated to +child processes spawned by :mod:`multiprocessing`, not just a hard-coded +subset. This makes the behavior consistent between default "spawn" and +"forkserver" start methods and the old "fork" start method. The options +that were previously not propagated are: ``context_aware_warnings``, +``cpu_count``, ``disable-remote-debug``, ``int_max_str_digits``, +``lazy_imports``, ``no_debug_ranges``, ``pathconfig_warnings``, ``perf``, +``perf_jit``, ``presite``, ``pycache_prefix``, ``thread_inherit_context``, +and ``warn_default_encoding``. + +.. + +.. date: 2026-03-15-16-38-48 +.. gh-issue: 145980 +.. nonce: mRze5H +.. section: Library + +Added the *alphabet* parameter in :func:`~binascii.b2a_base64`, +:func:`~binascii.a2b_base64`, :func:`~binascii.b2a_base85` and +:func:`~binascii.a2b_base85` and a number of ``*_ALPHABET`` constants in the +:mod:`binascii` module. Removed :func:`!b2a_z85` and :func:`!a2b_z85`. + +.. + +.. date: 2026-03-15-10-17-51 +.. gh-issue: 145968 +.. nonce: gZexry +.. section: Library + +Fix translation in :func:`base64.b64decode` when altchars overlaps with the +standard ones. + +.. + +.. date: 2026-03-15-00-00-00 +.. gh-issue: 145966 +.. nonce: tCI0uD4I +.. section: Library + +Non-:exc:`AttributeError` exceptions raised during dialect attribute lookup +in :mod:`csv` are no longer silently suppressed. + +.. + +.. date: 2026-03-12-21-01-48 +.. gh-issue: 145883 +.. nonce: lUvXcc +.. section: Library + +:mod:`zoneinfo`: Fix heap buffer overflow reads from malformed TZif data. +Found by OSS Fuzz, issues :oss-fuzz:`492245058` and :oss-fuzz:`492230068`. + +.. + +.. date: 2026-03-12-12-17-39 +.. gh-issue: 145850 +.. nonce: uW3stt +.. section: Library + +Changed some implementation details in :class:`struct.Struct`: calling it +with non-ASCII string format will now raise a :exc:`ValueError` instead of +:exc:`UnicodeEncodeError`, calling it with non-ASCII bytes format will now +raise a :exc:`ValueError` instead of :exc:`struct.error`, getting the +:attr:`!format` attribute of uninitialized object will now raise an +:exc:`AttributeError` instead of :exc:`RuntimeError`. + +.. + +.. date: 2026-03-11-10-25-32 +.. gh-issue: 123720 +.. nonce: TauFRx +.. section: Library + +asyncio: Fix :func:`asyncio.Server.serve_forever` shutdown regression. Since +3.12, cancelling ``serve_forever()`` could hang waiting for a handler +blocked on a read from a client that never closed (effectively requiring two +interrupts to stop); the shutdown sequence now ensures client streams are +closed so ``serve_forever()`` exits promptly and handlers observe EOF. + +.. + +.. date: 2026-03-10-19-50-59 +.. gh-issue: 138122 +.. nonce: CsoBEo +.. section: Library + +The ``profiling.sampling`` module now supports differential flamegraph +visualization via ``--diff-flamegraph`` to compare two profiling runs. +Functions are colored red (regressions), blue (improvements), gray +(neutral), or purple (new). Elided stacks show code paths that disappeared +between runs. + +.. + +.. date: 2026-03-10-14-57-15 +.. gh-issue: 145754 +.. nonce: YBL5Ko +.. section: Library + +Request signature during mock autospec with ``FORWARDREF`` annotation +format. This prevents runtime errors when an annotation uses a name that is +not defined at runtime. + +.. + +.. date: 2026-03-10-14-13-12 +.. gh-issue: 145750 +.. nonce: iQsTeX +.. section: Library + +Avoid undefined behaviour from signed integer overflow when parsing format +strings in the :mod:`struct` module. Found by OSS Fuzz in +:oss-fuzz:`488466741`. + +.. + +.. date: 2026-03-10-01-54-34 +.. gh-issue: 145719 +.. nonce: okJRoK +.. section: Library + +Add ``application/efi`` MIME type to :mod:`mimetypes`. + +.. + +.. date: 2026-03-10-01-48-12 +.. gh-issue: 145717 +.. nonce: dPc0Rt +.. section: Library + +Add a few Microsoft-specific MIME types. + +.. + +.. date: 2026-03-09-19-59-05 +.. gh-issue: 145703 +.. nonce: 4EEP7J +.. section: Library + +:mod:`asyncio`: Make sure that :meth:`loop.call_at <asyncio.loop.call_at>` +and :meth:`loop.call_later <asyncio.loop.call_later>` trigger scheduled +events on time when the clock resolution becomes too small. + +.. + +.. date: 2026-03-09-18-33-16 +.. gh-issue: 145697 +.. nonce: d6hFmm +.. section: Library + +Add ``application/sql`` and ``application/vnd.sqlite3`` into ``mimetypes``. + +.. + +.. date: 2026-03-09-00-00-00 +.. gh-issue: 145492 +.. nonce: 457Afc +.. section: Library + +Fix infinite recursion in :class:`collections.defaultdict` ``__repr__`` when +a ``defaultdict`` contains itself. Based on analysis by KowalskiThomas in +:gh:`145492`. + +.. + +.. date: 2026-03-08-00-00-00 +.. gh-issue: 145650 +.. nonce: LgRepr +.. section: Library + +Add :meth:`~object.__repr__` support to :class:`logging.Formatter` and +:class:`logging.Filter`, showing the format string and filter name +respectively. + +.. + +.. date: 2026-03-07-14-34-39 +.. gh-issue: 145587 +.. nonce: flFQ5- +.. section: Library + +Resolved a performance regression in ``multiprocessing.connection.wait`` on +Windows that caused infinite busy loops when called with no objects. The +function now properly yields control to the OS to conserve CPU resources. +Patch By Shrey Naithani + +.. + +.. date: 2026-03-07-02-44-52 +.. gh-issue: 145616 +.. nonce: x8Mf23 +.. section: Library + +Detect Android sysconfig ABI correctly on 32-bit ARM Android on 64-bit ARM +kernel + +.. + +.. date: 2026-03-05-14-13-10 +.. gh-issue: 145546 +.. nonce: 3tnlxx +.. section: Library + +Fix ``unittest.util.sorted_list_difference()`` to deduplicate remaining +elements when one input list is exhausted before the other. + +.. + +.. date: 2026-03-03-23-21-40 +.. gh-issue: 145446 +.. nonce: 0c-TJX +.. section: Library + +Now :mod:`functools` is safer in free-threaded build when using keywords in +:func:`functools.partial` + +.. + +.. date: 2026-02-26-20-13-16 +.. gh-issue: 145264 +.. nonce: 4pggX_ +.. section: Library + +Base64 decoder (see :func:`binascii.a2b_base64`, :func:`base64.b64decode`, +etc) no longer ignores excess data after the first padded quad in non-strict +(default) mode. Instead, in conformance with :rfc:`4648`, section 3.3, it +now ignores the pad character, "=", if it is present before the end of the +encoded data. + +.. + +.. date: 2026-02-23-21-28-12 +.. gh-issue: 145035 +.. nonce: J5UjS6 +.. section: Library + +Allows omitting the internal library ``_pyrepl`` with limited loss of +functionality. This allows complete removal of the modern REPL, which is an +unsupported configuration, but still desirable for some distributions. + +.. + +.. date: 2026-02-19-16-34-18 +.. gh-issue: 144270 +.. nonce: wJRtSr +.. section: Library + +Made the *tag* parameter of :class:`xml.etree.ElementTree.Element` and the +*parent* and *tag* parameters of :func:`xml.etree.ElementTree.SubElement` +positional-only, matching the behavior of the C accelerator. + +.. + +.. date: 2026-02-19-12-00-00 +.. gh-issue: 144984 +.. nonce: b93995c982 +.. section: Library + +Fix crash in :meth:`xml.parsers.expat.xmlparser.ExternalEntityParserCreate` +when an allocation fails. The error paths could dereference NULL +``handlers`` and double-decrement the parent parser's reference count. + +.. + +.. date: 2026-02-18-21-45-00 +.. gh-issue: 144975 +.. nonce: Ab3XyZ +.. section: Library + +:meth:`wave.Wave_write.setframerate` now validates the frame rate after +rounding to an integer, preventing values like ``0.5`` from being accepted +and causing confusing errors later. Patch by Michiel Beijen. + +.. + +.. date: 2026-02-17-03-43-07 +.. gh-issue: 140715 +.. nonce: twmcM_ +.. section: Library + +Add ``%n`` and ``%t`` support to :meth:`~datetime.datetime.strptime`. + +.. + +.. date: 2026-02-11-21-01-30 +.. gh-issue: 144259 +.. nonce: OAhOR8 +.. section: Library + +Fix inconsistent display of long multiline pasted content in the REPL. + +.. + +.. date: 2026-02-08-22-04-06 +.. gh-issue: 140814 +.. nonce: frzSpn +.. section: Library + +:func:`multiprocessing.freeze_support` no longer sets the default start +method as a side effect, which previously caused a subsequent +:func:`multiprocessing.set_start_method` call to raise :exc:`RuntimeError`. + +.. + +.. date: 2026-02-04-20-30-59 +.. gh-issue: 123471 +.. nonce: 1dnPvs +.. section: Library + +Make concurrent iteration over :class:`itertools.accumulate` safe under +free-threading. + +.. + +.. date: 2026-01-10-16-23-21 +.. gh-issue: 143715 +.. nonce: HZrfSA +.. section: Library + +Calling the ``Struct.__new__()`` without required argument now is +deprecated. Calling :meth:`~object.__init__` method on initialized +:class:`~struct.Struct` objects is deprecated. + +.. + +.. date: 2025-12-18-00-00-00 +.. gh-issue: 142763 +.. nonce: AJpZPVG5 +.. section: Library + +Fix a race condition between :class:`zoneinfo.ZoneInfo` creation and +:func:`zoneinfo.ZoneInfo.clear_cache` that could raise :exc:`KeyError`. + +.. + +.. date: 2025-11-18-06-35-53 +.. gh-issue: 141707 +.. nonce: DBmQIy +.. section: Library + +Don't change :class:`tarfile.TarInfo` type from ``AREGTYPE`` to ``DIRTYPE`` +when parsing GNU long name or link headers. + +.. + +.. date: 2025-11-15-23-14-30 +.. gh-issue: 138577 +.. nonce: KbShrt +.. section: Library + +:func:`getpass.getpass` with non-empty ``echo_char`` now handles keyboard +shortcuts including Ctrl+A/E (cursor movement), Ctrl+K/U (kill line), Ctrl+W +(erase word), and Ctrl+V (literal next) by reading the terminal's control +character settings and processing them appropriately in non-canonical mode. +Patch by Sanyam Khurana. + +.. + +.. date: 2025-10-13-16-43-36 +.. gh-issue: 140049 +.. nonce: VvmAzN +.. section: Library + +:func:`traceback.format_exception_only` now colorizes exception notes. + +.. + +.. date: 2025-10-11-11-50-59 +.. gh-issue: 139933 +.. nonce: 05MHlx +.. section: Library + +Improve :exc:`AttributeError` suggestions for classes with a custom +:meth:`~object.__dir__` method returning a list of unsortable values. Patch +by Bénédikt Tran. + +.. + +.. date: 2025-10-05-15-38-02 +.. gh-issue: 139633 +.. nonce: l3P839 +.. section: Library + +The :mod:`netrc` security check is now run once per parse rather than once +per entry. + +.. + +.. date: 2025-09-19-13-54-54 +.. gh-issue: 130472 +.. nonce: LODfdk +.. section: Library + +Add fancycompleter and enable it by default when using pyrepl. This gives +colored tab completion. + +.. + +.. date: 2025-02-07-00-48-07 +.. gh-issue: 112632 +.. nonce: 95MM0C +.. section: Library + +Add an *expand* keyword argument for :func:`pprint.pprint`, +:func:`pprint.pformat`, :func:`pprint.pp` by passing on all *kwargs* and +:class:`pprint.PrettyPrinter`. Contributed by Stefan Todoran and Semyon +Moroz. + +.. + +.. date: 2024-09-25-12-47-50 +.. gh-issue: 66419 +.. nonce: DVSukU +.. section: Library + +Optional argument with :ref:`nargs` equals to ``argparse.REMAINDER`` now +consumes all remaining arguments including ``'--'``. + +.. + +.. date: 2023-03-10-13-10-06 +.. gh-issue: 60729 +.. nonce: KCCHTe +.. section: Library + +Add support for floating point audio wave files in :mod:`wave`. + +.. + +.. bpo: 36461 +.. date: 2019-04-25-21-11-37 +.. nonce: TO5YyP +.. section: Library + +Make the target time of :meth:`timeit.Timer.autorange` configurable and add +``--target-time`` option to the command-line interface of :mod:`timeit`. + +.. + +.. date: 2026-03-25-00-00-00 +.. gh-issue: 126676 +.. nonce: 052336 +.. section: Documentation + +Expand :mod:`argparse` documentation for ``type=bool`` with a demonstration +of the surprising behavior and pointers to common alternatives. + +.. + +.. date: 2026-03-09-00-00-00 +.. gh-issue: 145649 +.. nonce: 8BcbAB +.. section: Documentation + +Fix text wrapping and formatting of ``-X`` option descriptions in the +:manpage:`python(1)` man page by using proper roff markup. + +.. + +.. date: 2026-04-03-21-37-18 +.. gh-issue: 144418 +.. nonce: PusC0S +.. section: Tests + +The Android testbed's emulator RAM has been increased from 2 GB to 4 GB. + +.. + +.. date: 2026-03-24-00-15-58 +.. gh-issue: 146202 +.. nonce: LgH6Bj +.. section: Tests + +Fix a race condition in regrtest: make sure that the temporary directory is +created in the worker process. Previously, temp_cwd() could fail on Windows +if the "build" directory was not created. Patch by Victor Stinner. + +.. + +.. date: 2026-03-28-02-48-51 +.. gh-issue: 146541 +.. nonce: k-zlM6 +.. section: Build + +The Android testbed can now be built for 32-bit ARM and x86 targets. + +.. + +.. date: 2026-03-27-06-55-10 +.. gh-issue: 146498 +.. nonce: uOiCab +.. section: Build + +The iOS XCframework build script now ensures libpython isn't included in +installed app content, and is more robust in identifying standard library +binary content that requires processing. + +.. + +.. date: 2026-03-26-14-35-29 +.. gh-issue: 146450 +.. nonce: 9Kmp5Q +.. section: Build + +The Android build script was modified to improve parity with other platform +build scripts. + +.. + +.. date: 2026-03-26-12-48-42 +.. gh-issue: 146446 +.. nonce: 0GyMu4 +.. section: Build + +The clean target for the Apple/iOS XCframework build script is now more +selective when targeting a single architecture. + +.. + +.. date: 2026-03-26-12-27-42 +.. gh-issue: 146444 +.. nonce: JKJuEa +.. section: Build + +The Apple/iOS build script has been moved to the Platforms directory. + +.. + +.. date: 2026-03-23-20-06-35 +.. gh-issue: 146210 +.. nonce: C01Rmq +.. section: Build + +Fix building the jit stencils on Windows when the interpreter is built with +a different clang version. Patch by Chris Eibl. + +.. + +.. date: 2026-03-12-12-30-24 +.. gh-issue: 145844 +.. nonce: VOPeCU +.. section: Build + +Update to WASI SDK 32. + +.. + +.. date: 2026-03-11-11-58-42 +.. gh-issue: 145801 +.. nonce: iCXa3v +.. section: Build + +When Python build is optimized with GCC using PGO, use +``-fprofile-update=atomic`` option to use atomic operations when updating +profile information. This option reduces the risk of gcov Data Files (.gcda) +corruption which can cause random GCC crashes. Patch by Victor Stinner. + +.. + +.. date: 2026-03-10-16-58-55 +.. gh-issue: 138850 +.. nonce: CkqTw6 +.. section: Build + +Add :option:`--disable-epoll` to ``configure`` + +.. + +.. date: 2026-03-08-06-18-26 +.. gh-issue: 145633 +.. nonce: Ogu-RF +.. section: Build + +Remove support for ancient ARM platforms (ARMv4L and ARMv5L OABI boards), +using mixed-endian representation for doubles. Patch by Sergey B Kirpichev. + +.. + +.. date: 2026-01-08-22-27-07 +.. gh-issue: 85277 +.. nonce: TotySi +.. section: Build + +Fix building without ``stropts.h`` or empty ``stropts.h`` + +.. + +.. date: 2025-10-19-23-44-46 +.. gh-issue: 140131 +.. nonce: AABF2k +.. section: Windows + +Fix REPL cursor position on Windows when module completion suggestion line +hits console width. + +.. + +.. date: 2025-10-17-01-07-03 +.. gh-issue: 137586 +.. nonce: kVzxvp +.. section: macOS + +Invoke :program:`osascript` with absolute path in :mod:`webbrowser` and +:mod:`!turtledemo`. + +.. + +.. date: 2026-03-22-00-00-00 +.. gh-issue: 135953 +.. nonce: IptOwg +.. section: Tools/Demos + +Properly identify the main thread in the Gecko profiler collector by using a +status flag from the interpreter state instead of relying on +:func:`threading.main_thread` in the collector process. + +.. + +.. date: 2026-03-15-20-59-29 +.. gh-issue: 145976 +.. nonce: rEdUI- +.. section: Tools/Demos + +Remove :file:`Misc/indent.pro`, a configuration file for GNU +:manpage:`indent(1)`. + +.. + +.. date: 2026-03-15-11-32-35 +.. gh-issue: 145976 +.. nonce: mqhzmB +.. section: Tools/Demos + +Remove :file:`Misc/vgrindefs` and :file:`Misc/Porting`. + +.. + +.. date: 2026-03-31-13-33-41 +.. gh-issue: 146636 +.. nonce: 5do3wt +.. section: C API + +The :c:data:`Py_mod_abi` slot is now mandatory for modules created from a +slots array (using :c:func:`PyModule_FromSlotsAndSpec` or the +:c:func:`PyModExport_* <PyModExport_modulename>` export hook). + +.. + +.. date: 2026-03-19-16-50-27 +.. gh-issue: 146175 +.. nonce: pISQGX +.. section: C API + +The following macros are :term:`soft deprecated`: :c:macro:`Py_ALIGNED`, +:c:macro:`PY_FORMAT_SIZE_T`, :c:macro:`Py_LL`, :c:macro:`Py_ULL`, +:c:macro:`PY_LONG_LONG`, :c:macro:`PY_LLONG_MIN`, :c:macro:`PY_LLONG_MAX`, +:c:macro:`PY_ULLONG_MAX`, :c:macro:`PY_INT32_T`, :c:macro:`PY_UINT32_T`, +:c:macro:`PY_INT64_T`, :c:macro:`PY_UINT64_T`, :c:macro:`PY_SIZE_MAX`, +:c:macro:`Py_UNICODE_SIZE`, :c:macro:`Py_VA_COPY`. + +The macro :c:macro:`Py_UNICODE_WIDE`, which was scheduled for removal, is +:term:`soft deprecated` instead. + +.. + +.. date: 2026-03-18-23-44-29 +.. gh-issue: 146143 +.. nonce: pwIrJq +.. section: C API + +:c:func:`PyUnicodeWriter_WriteUCS4` now accepts a pointer to a constant +buffer of ``Py_UCS4``. + +.. + +.. date: 2026-03-18-20-18-59 +.. gh-issue: 146056 +.. nonce: nnZIgp +.. section: C API + +:c:func:`PyUnicodeWriter_WriteRepr` now supports ``NULL`` argument. + +.. + +.. date: 2026-02-19-18-39-11 +.. gh-issue: 145010 +.. nonce: mKzjci +.. section: C API + +Use GCC dialect alternatives for inline assembly in ``object.h`` so that the +Python headers compile correctly with ``-masm=intel``. diff --git a/Misc/NEWS.d/3.15.0b1.rst b/Misc/NEWS.d/3.15.0b1.rst new file mode 100644 index 00000000000000..e0fcab0eef870f --- /dev/null +++ b/Misc/NEWS.d/3.15.0b1.rst @@ -0,0 +1,2256 @@ +.. date: 2026-05-02-15-38-03 +.. gh-issue: 149254 +.. nonce: 0HOL0j +.. release date: 2026-05-07 +.. section: Security + +Update Android and iOS installer to use OpenSSL 3.5.6. + +.. + +.. date: 2026-04-26-17-49-58 +.. gh-issue: 149017 +.. nonce: EiVFPo +.. section: Security + +Update bundled `libexpat <https://libexpat.github.io/>`_ to version 2.8.0. + +.. + +.. date: 2026-04-24-23-15-42 +.. gh-issue: 148252 +.. nonce: 8BLmzd +.. section: Security + +Fixed string table and sample record bounds checks in +:mod:`!_remote_debugging` when decoding certain ``.pyb`` inputs on 32-bit +builds. Patch by Maurycy Pawłowski-Wieroński. + +.. + +.. date: 2026-04-21-13-46-30 +.. gh-issue: 90309 +.. nonce: srvj9q +.. section: Security + +Base64-encode values when embedding cookies to JavaScript using the +:meth:`http.cookies.BaseCookie.js_output` method to avoid injection and +escaping. + +.. + +.. date: 2026-04-20-15-31-37 +.. gh-issue: 148808 +.. nonce: _Z8JL0 +.. section: Security + +Added buffer boundary check when using ``nbytes`` parameter with +:meth:`!asyncio.AbstractEventLoop.sock_recvfrom_into`. Only relevant for +Windows and the :class:`asyncio.ProactorEventLoop`. + +.. + +.. date: 2026-04-10-16-28-21 +.. gh-issue: 148395 +.. nonce: kfzm0G +.. section: Security + +Fix a dangling input pointer in :class:`lzma.LZMADecompressor`, +:class:`bz2.BZ2Decompressor`, and internal :class:`!zlib._ZlibDecompressor` +when memory allocation fails with :exc:`MemoryError`, which could let a +subsequent :meth:`!decompress` call read or write through a stale pointer to +the already-released caller buffer. + +.. + +.. date: 2026-04-08-14-25-47 +.. gh-issue: 148252 +.. nonce: IEp9Rt +.. section: Security + +Fixed stack depth calculation in :mod:`!_remote_debugging` when decoding +certain ``.pyb`` inputs on 32-bit builds. Issue originally identified and +diagnosed by Tristan Madani (@TristanInSec on GitHub). + +.. + +.. date: 2026-04-06-13-55-00 +.. gh-issue: 148178 +.. nonce: Rs7kLm +.. section: Security + +Hardened :mod:`!_remote_debugging` by validating remote debug offset tables +before using them to size memory reads or interpret remote layouts. + +.. + +.. date: 2026-03-31-09-15-51 +.. gh-issue: 148169 +.. nonce: EZJzz2 +.. section: Security + +A bypass in :mod:`webbrowser` allowed URLs prefixed with ``%action`` to pass +the dash-prefix safety check. + +.. + +.. date: 2026-03-29-12-51-33 +.. gh-issue: 146581 +.. nonce: 4vZfB0 +.. section: Security + +Fix vulnerability in :func:`shutil.unpack_archive` for ZIP files on Windows +which allowed to write files outside of the destination tree if the patch in +the archive contains a Windows drive prefix. Now such invalid paths will be +skipped. Files containing ".." in the name (like "foo..bar") are no longer +skipped. + +.. + +.. date: 2026-03-26-01-42-15 +.. gh-issue: 137586 +.. nonce: j3SkOm +.. section: Security + +Fix a PATH-injection vulnerability in :mod:`webbrowser` on macOS where +``osascript`` was invoked without an absolute path. The new :class:`!MacOS` +class uses ``/usr/bin/open`` directly, eliminating the dependency on +``osascript`` entirely. + +.. + +.. date: 2026-03-25-00-51-03 +.. gh-issue: 146333 +.. nonce: LqdL__bn +.. section: Security + +Fix quadratic backtracking in :class:`configparser.RawConfigParser` option +parsing regexes (``OPTCRE`` and ``OPTCRE_NV``). A crafted configuration line +with many whitespace characters could cause excessive CPU usage. + +.. + +.. date: 2026-03-20-09-29-42 +.. gh-issue: 146211 +.. nonce: PQVbs7 +.. section: Security + +Reject CR/LF characters in tunnel request headers for the +HTTPConnection.set_tunnel() method. + +.. + +.. date: 2026-05-06-15-57-28 +.. gh-issue: 148940 +.. nonce: dRIXiY +.. section: Core and Builtins + +Revert the process size based deferral of garbage collection (GH-133464). +The performance issue this change resolves is also fixed by GH-142562. This +approach has the problem that process size as seen by the OS (e.g. the +resident size or RSS) does not immediately decrease after cyclic garbage is +freed since mimalloc defers returning memory of the OS. This change applies +to the free-threaded GC only. + +.. + +.. date: 2026-05-03-10-24-50 +.. gh-issue: 149243 +.. nonce: Zh1q9_ +.. section: Core and Builtins + +Check for recursion limits in ``CALL_ALLOC_AND_ENTER_INIT`` opcode. + +.. + +.. date: 2026-05-02-18-02-41 +.. gh-issue: 126910 +.. nonce: nqDVrp +.. section: Core and Builtins + +Add support for unwinding JIT frames using GNU backtrace. Patch by Diego +Russo and Pablo Galindo + +.. + +.. date: 2026-04-30-01-35-09 +.. gh-issue: 149171 +.. nonce: meXWpl +.. section: Core and Builtins + +Allow assignment to the ``__module__`` attribute of +:class:`typing.TypeAliasType` instances. + +.. + +.. date: 2026-04-29-14-06-00 +.. gh-issue: 149122 +.. nonce: P8k2Lm +.. section: Core and Builtins + +Fix a crash in optimized calls to :func:`all`, :func:`any`, :func:`tuple`, +:func:`list`, and :func:`set` with an async generator expression argument +(for example, ``tuple(await x for x in y)``). These calls now correctly +raise ``TypeError`` instead of crashing. + +.. + +.. date: 2026-04-28-21-19-21 +.. gh-issue: 149049 +.. nonce: 98u2Ib +.. section: Core and Builtins + +Fix stack underflow for ``BINARY_OP`` in tier 2. + +.. + +.. date: 2026-04-28-05-59-17 +.. gh-issue: 83065 +.. nonce: f0UPNE +.. section: Core and Builtins + +Fix a deadlock that could occur when one thread is importing a submodule +(for example ``import pkg.sub.mod``) while another thread is importing one +of its parent packages (for example ``import pkg.sub``) and that parent's +``__init__.py`` itself imports the submodule. The import system now acquires +module locks in hierarchical (parent-before-child) order so the two threads +serialise instead of raising ``_DeadlockError``. + +.. + +.. date: 2026-04-22-14-55-18 +.. gh-issue: 113956 +.. nonce: 0VEXd6 +.. section: Core and Builtins + +Fix a data race in :func:`sys.intern` in the free-threaded build when +interning a string owned by another thread. An interned copy owned by the +current thread is used instead when it is not safe to immortalize the +original. + +.. + +.. date: 2026-04-21-19-29-29 +.. gh-issue: 148850 +.. nonce: MSH0J_ +.. section: Core and Builtins + +Fix the memory sanitizer false positive in :func:`os.getrandom`. + +.. + +.. date: 2026-04-21-14-36-44 +.. gh-issue: 148820 +.. nonce: XhOGhA +.. section: Core and Builtins + +Fix a race in :c:type:`!_PyRawMutex` on the free-threaded build where a +``Py_PARK_INTR`` return from ``_PySemaphore_Wait`` could let the waiter +destroy its semaphore before the unlocking thread's ``_PySemaphore_Wakeup`` +completed, causing a fatal ``ReleaseSemaphore`` error. + +.. + +.. date: 2026-04-21-06-43-32 +.. gh-issue: 148829 +.. nonce: GtIrYO +.. section: Core and Builtins + +Add :class:`sentinel`, implementing :pep:`661`. PEP by Tal Einat; patch by +Jelle Zijlstra. + +.. + +.. date: 2026-04-20-15-25-55 +.. gh-issue: 146270 +.. nonce: qZYfyc +.. section: Core and Builtins + +Fix a sequential consistency bug in ``structmember.c``. + +.. + +.. date: 2026-04-19-22-35-39 +.. gh-issue: 148766 +.. nonce: coLWln +.. section: Core and Builtins + +The interpreter help (such as ``python --help``) is now in color. Patch by +Hugo van Kemenade. + +.. + +.. date: 2026-04-18-16-41-04 +.. gh-issue: 148571 +.. nonce: Q6WB3A +.. section: Core and Builtins + +Fix a crash in the JIT optimizer when specialized opcode families inherited +incompatible recorded operand layouts. + +.. + +.. date: 2026-04-17-20-37-02 +.. gh-issue: 148653 +.. nonce: nbbHMh +.. section: Core and Builtins + +Forbid :mod:`marshalling <marshal>` recursive code objects, :class:`slice` +and :class:`frozendict` objects which cannot be correctly unmarshalled. + +.. + +.. date: 2026-04-17-11-30-00 +.. gh-issue: 142516 +.. nonce: GcGen315 +.. section: Core and Builtins + +Forward-port the generational cycle garbage collector to the default 3.15 +build, replacing the incremental collector while leaving the free-threaded +collector unchanged. + +.. + +.. date: 2026-04-15-12-00-00 +.. gh-issue: 146462 +.. nonce: 1YfK6v +.. section: Core and Builtins + +Added ``PyTypeObject.tp_basicsize``, ``PyTypeObject.tp_dictoffset``, and +``PyHeapTypeObject.ht_cached_keys`` offsets to :c:type:`!_Py_DebugOffsets` +to support version-independent read-only dict introspection tools. + +.. + +.. date: 2026-04-13-23-21-45 +.. gh-issue: 145239 +.. nonce: pL8qRt +.. section: Core and Builtins + +Unary plus is now accepted in :keyword:`match` literal patterns, mirroring +the existing support for unary minus. Patch by Bartosz Sławecki. + +.. + +.. date: 2026-04-13-16-52-33 +.. gh-issue: 148515 +.. nonce: 09xulC +.. section: Core and Builtins + +Fix a bug in the JIT optimizer reading operands for uops with multiple +caches. + +.. + +.. date: 2026-04-12-17-27-28 +.. gh-issue: 148390 +.. nonce: MAhw7F +.. section: Core and Builtins + +Fix an undefined behavior in :class:`memoryview` when using the native +boolean format (``?``) in :meth:`~memoryview.cast`. Previously, on some +common platforms, calling ``memoryview(b).cast("?").tolist()`` incorrectly +returned ``[False]`` instead of ``[True]`` for any even byte *b*. Patch by +Bénédikt Tran. + +.. + +.. date: 2026-04-12-10-40-57 +.. gh-issue: 148418 +.. nonce: ggA1LZ +.. section: Core and Builtins + +Fix a possible reference leak in a corrupted ``TYPE_CODE`` marshal stream. + +.. + +.. date: 2026-04-11-17-28-52 +.. gh-issue: 148393 +.. nonce: lX6gwN +.. section: Core and Builtins + +Fix data races between :c:func:`PyDict_Watch` / :c:func:`PyDict_Unwatch` and +concurrent dict mutation in the :term:`free-threaded build`. + +.. + +.. date: 2026-04-11-15-12-53 +.. gh-issue: 148398 +.. nonce: g62jCA +.. section: Core and Builtins + +Fix a bug in the JIT optimizer where class attribute loads were not +invalidated after type mutation. + +.. + +.. date: 2026-04-10-23-13-19 +.. gh-issue: 146527 +.. nonce: P3Xv4Q +.. section: Core and Builtins + +Add a ``GCMonitor`` class with a ``get_gc_stats`` method to the +:mod:`!_remote_debugging` module to allow reading GC statistics from an +external Python process without requiring the full ``RemoteUnwinder`` +functionality. Patch by Sergey Miryanov and Pablo Galindo. + +.. + +.. date: 2026-04-10-14-20-54 +.. gh-issue: 148284 +.. nonce: HKs-S_ +.. section: Core and Builtins + +Fix high stack consumption in Python's interpreter loop on Clang 22 by +setting function limits for inlining when building with computed gotos. + +.. + +.. date: 2026-04-09-14-18-33 +.. gh-issue: 148037 +.. nonce: aP3CSX +.. section: Core and Builtins + +Remove critical section from :c:func:`!PyCode_Addr2Line` in free-threading. + +.. + +.. date: 2026-04-08-06-59-23 +.. gh-issue: 115802 +.. nonce: jqfZty +.. section: Core and Builtins + +Improve JIT code generation on Linux AArch64 by reducing the indirect call +to external symbols. Patch by Diego Russo. + +.. + +.. date: 2026-04-08-02-49-07 +.. gh-issue: 148189 +.. nonce: 0KpXID +.. section: Core and Builtins + +Repaired undercount of bytes in type-specific free lists reported by +sys._debugmallocstats(). For types that participate in cyclic garbage +collection, it was missing two pointers used by GC. + +.. + +.. date: 2026-04-07-20-37-23 +.. gh-issue: 148222 +.. nonce: uF4D4E +.. section: Core and Builtins + +Fix vectorcall support in :class:`types.GenericAlias` when the underlying +type does not support the vectorcall protocol. Fix possible leaks in +:class:`types.GenericAlias` and :class:`types.UnionType` in case of memory +error. + +.. + +.. date: 2026-04-07-20-21-44 +.. gh-issue: 148208 +.. nonce: JAxpDU +.. section: Core and Builtins + +Fix recursion depth leak in :c:func:`PyObject_Print` + +.. + +.. date: 2026-04-06-18-25-53 +.. gh-issue: 95004 +.. nonce: CQeT_H +.. section: Core and Builtins + +The specializing interpreter now specializes for :class:`enum.Enum` +improving performance and scaling in free-threading. Patch by Kumar Aditya. + +.. + +.. date: 2026-04-05-16-10-00 +.. gh-issue: 149202 +.. nonce: W8sQeR +.. section: Core and Builtins + +Enable frame pointers by default for GCC-compatible CPython builds, +including ``-mno-omit-leaf-frame-pointer``, ``-marm`` on 32-bit ARM, and/or +``-mbackchain`` on s390x platforms when the compiler supports them, so +profilers and debuggers can unwind native interpreter frames more reliably. +Users can pass :option:`--without-frame-pointers` to ``./configure`` to opt +out. + +.. + +.. date: 2026-04-02-17-52-33 +.. gh-issue: 148014 +.. nonce: 2Y6ND_ +.. section: Core and Builtins + +Accept a function name in :option:`-X presite <-X>` command line option and +:envvar:`PYTHON_PRESITE` environment variable. Patch by Victor Stinner. + +.. + +.. date: 2026-04-02-13-25-09 +.. gh-issue: 147998 +.. nonce: wnzkRT +.. section: Core and Builtins + +Fixed a memory leak in interpreter helper calls so cleanup works when an +operation falls across interpreter boundaries. Patch by Maurycy +Pawłowski-Wieroński. + +.. + +.. date: 2026-03-26-08-49-35 +.. gh-issue: 146455 +.. nonce: f54083a9 +.. section: Core and Builtins + +Fix O(N²) compile-time regression in constant folding after it was moved +from AST to CFG optimizer. + +.. + +.. date: 2026-03-25-12-00-00 +.. gh-issue: 146306 +.. nonce: B9f62e +.. section: Core and Builtins + +Specialize float true division in the tier 2 optimizer with inplace mutation +for uniquely-referenced operands. + +.. + +.. date: 2026-03-23-11-34-37 +.. gh-issue: 142186 +.. nonce: v8Yp3W +.. section: Core and Builtins + +Global :mod:`sys.monitoring` events can now be turned on and disabled on a +per code object basis. Returning ``DISABLE`` from a callback disables the +event for the entire code object (for the current tool). + +.. + +.. date: 2026-03-17-20-30-17 +.. gh-issue: 126910 +.. nonce: NaUwmD +.. section: Core and Builtins + +Add support for unwinding JIT frames using GDB. Patch by Diego Russo and +Pablo Galindo. + +.. + +.. date: 2026-03-16-17-29-22 +.. gh-issue: 146031 +.. nonce: 6nyB7C +.. section: Core and Builtins + +The unstable API _PyInterpreterState_SetEvalFrameFunc has a companion +function _PyInterpreterState_SetEvalFrameAllowSpecialization to specify if +specialization should be allowed. When this option is set to 1 the +specializer will turn Python -> Python calls into specialized opcodes which +the replacement interpreter loop can choose to respect and perform inlined +dispatch. + +.. + +.. date: 2026-02-26-21-22-34 +.. gh-issue: 145278 +.. nonce: DHkYqt +.. section: Core and Builtins + +The :mod:`encodings` is now partially frozen, including the ``aliases`` and +``utf_8`` submodules. + +The :mod:`linecache` is now frozen. + +.. + +.. date: 2026-02-18-16-53-26 +.. gh-issue: 134584 +.. nonce: a-O4sd +.. section: Core and Builtins + +Optimize and eliminate redundant ref-counting for ``MAKE_FUNCTION`` in the +JIT. + +.. + +.. date: 2026-01-15-13-37-21 +.. gh-issue: 143886 +.. nonce: 2gk5QC +.. section: Core and Builtins + +Reorder function annotations so positional-only arguments are returned +before other arguments. This fixes how :func:`functools.singledispatch` +registers functions with positional-only arguments. + +.. + +.. date: 2025-12-08-00-25-35 +.. gh-issue: 98894 +.. nonce: hKWyfqNx +.. section: Core and Builtins + +Restore ``function__entry`` and ``function__return`` DTrace/SystemTap probes +that were broken since Python 3.11. + +.. + +.. date: 2025-08-16-12-56-08 +.. gh-issue: 116021 +.. nonce: hMN9yw +.. section: Core and Builtins + +Support for creating instances of abstract AST nodes from the :mod:`ast` +module is deprecated and scheduled for removal in Python 3.20. Patch by +Brian Schubert. + +.. + +.. date: 2025-08-15-21-33-16 +.. gh-issue: 137814 +.. nonce: 6yRTeu +.. section: Core and Builtins + +Fix the ``__qualname__`` attribute of ``__annotate__`` functions on +functions. + +.. + +.. date: 2025-08-09-19-00-36 +.. gh-issue: 137600 +.. nonce: p_p6OU +.. section: Core and Builtins + +:mod:`ast`: The constructors of AST nodes now raise a :exc:`TypeError` when +a required argument is omitted or when a keyword argument that does not map +to a field on the AST node is passed. These cases had previously raised a +:exc:`DeprecationWarning` since Python 3.13. Patch by Brian Schubert. + +.. + +.. date: 2025-08-01-20-31-30 +.. gh-issue: 137293 +.. nonce: 4x3JbV +.. section: Core and Builtins + +Fix :exc:`SystemError` when searching ELF Files in :func:`sys.remote_exec`. + +.. + +.. date: 2025-06-10-17-30-55 +.. gh-issue: 135357 +.. nonce: sUXU1W +.. section: Core and Builtins + +Add support for :data:`!socket.SO_PASSRIGHTS` on Linux. + +.. + +.. date: 2025-05-26-10-03-18 +.. gh-issue: 134690 +.. nonce: mUMT16 +.. section: Core and Builtins + +Removed deprecated in :pep:`626` since Python 3.12 +:attr:`!codeobject.co_lnotab` from :class:`types.CodeType`. + +.. + +.. date: 2025-01-17-19-48-28 +.. gh-issue: 100239 +.. nonce: 7pbTEA +.. section: Core and Builtins + +Specialize ``BINARY_OP`` for concatenation of lists and tuples, and +propagate the result type through ``_BINARY_OP_EXTEND`` in the tier 2 +optimizer so that follow-up type guards can be eliminated. + +.. + +.. date: 2026-05-06-14-26-37 +.. gh-issue: 148823 +.. nonce: ySmOE4 +.. section: Library + +Defer the import of ``_colorize`` in ``argparse`` until needed for coloring +output. + +.. + +.. date: 2026-05-06-05-56-59 +.. gh-issue: 141560 +.. nonce: wlSQaW +.. section: Library + +Add an *annotation_format* parameter to :func:`inspect.getfullargspec`. + +.. + +.. date: 2026-05-05-13-12-58 +.. gh-issue: 139489 +.. nonce: a8qqIM +.. section: Library + +Add the :func:`xml.is_valid_text` function, which allows to check whether a +string can be used in the XML document. + +.. + +.. date: 2026-05-05-00-30-04 +.. gh-issue: 142389 +.. nonce: 4daLzc +.. section: Library + +Add backticks to stdlib argparse help to display in colour. Patch by Hugo +van Kemenade. + +.. + +.. date: 2026-05-04-19-28-48 +.. gh-issue: 149377 +.. nonce: WNlc8Y +.. section: Library + +Update bundled pip to 26.1.1 + +.. + +.. date: 2026-05-04-18-01-35 +.. gh-issue: 142389 +.. nonce: 4Faqpq +.. section: Library + +Add backtick markup support in :mod:`argparse` option help text to highlight +inline code when color output is enabled. Patch by Hugo van Kemenade. + +.. + +.. date: 2026-05-04-16-26-33 +.. gh-issue: 148675 +.. nonce: xZwXa6 +.. section: Library + +Remove ``F`` and ``D`` formats from :mod:`array` and :class:`memoryview`. +Patch by Victor Stinner. + +.. + +.. date: 2026-05-04-04-06-36 +.. gh-issue: 149342 +.. nonce: d3CK-y +.. section: Library + +Fix :mod:`!_remote_debugging` binary writing so that sampling a thread whose +Python frame stack is empty (for example while it is in a C call or +mid-syscall) no longer raises ``RuntimeError("Invalid stack encoding +type")``, and so that ``BinaryWriter.total_samples`` after :meth:`!finalize` +or context-manager exit includes samples flushed from the RLE buffer. Patch +by Maurycy Pawłowski-Wieroński. + +.. + +.. date: 2026-05-04-00-51-32 +.. gh-issue: 149010 +.. nonce: BCp_8k +.. section: Library + +The ``inspect`` module CLI now reports as much information as it has +available for non-source modules when ``--details`` is specified, and +provides an error message rather than a traceback when ``--details`` is +omitted. It also reports improved information when the given target location +is not the target's defining location and when the given target is a data +value rather than a class or function definition. + +.. + +.. date: 2026-05-03-23-47-59 +.. gh-issue: 146609 +.. nonce: V9jqYf +.. section: Library + +Use :mod:`argparse` for colour help :mod:`timeit` CLI. Patch by Hugo van +Kemenade. + +.. + +.. date: 2026-05-03-23-29-34 +.. gh-issue: 142389 +.. nonce: SVYiSv +.. section: Library + +Add backticks for colour to regrtest and pdb's help description. Patch by +Hugo van Kemenade. + +.. + +.. date: 2026-05-03-17-32-24 +.. gh-issue: 144384 +.. nonce: q-8jSr +.. section: Library + +Lazily import :mod:`!_colorize`. Patch by Hugo van Kemenade. + +.. + +.. date: 2026-05-03-12-00-00 +.. gh-issue: 149321 +.. nonce: fUaxrz +.. section: Library + +Fix import cycles exposed by running standard library modules with ``-X +lazy_imports=none``. + +.. + +.. date: 2026-05-03-01-49-57 +.. gh-issue: 145378 +.. nonce: rtyAWM +.. section: Library + +Generate consistent colors for :mod:`pdb` commands in :mod:`pdb` REPL. + +.. + +.. date: 2026-05-02-19-09-04 +.. gh-issue: 149296 +.. nonce: DuKF0j +.. section: Library + +Add a ``dump`` subcommand to :mod:`profiling.sampling` that prints a single +traceback-style snapshot of a running process's Python stack, including +per-thread status, source line highlighting, optional bytecode opcode names, +and async-aware task reconstruction. Patch by Pablo Galindo. + +.. + +.. date: 2026-05-02-18-23-50 +.. gh-issue: 143231 +.. nonce: oBbQb5 +.. section: Library + +A *module* attribute has been added to :class:`!warnings.WarningMessage`. + +.. + +.. date: 2026-05-02-15-58-08 +.. gh-issue: 148675 +.. nonce: b3ZNlj +.. section: Library + +:mod:`ctypes`: Change the :py:attr:`~ctypes._SimpleCData._type_` of +:class:`~ctypes.c_float_complex`, :class:`~ctypes.c_double_complex` and +:class:`~ctypes.c_longdouble_complex` from ``F``, ``D`` and ``G`` to ``Zf``, +``Zd`` and ``Zg`` for compatibility with numpy. Patch by Victor Stinner. + +.. + +.. date: 2026-05-02-12-30-35 +.. gh-issue: 148675 +.. nonce: cu2YFT +.. section: Library + +The :data:`array.typecodes` type changed from :class:`str` to :class:`tuple` +to support type codes longer than 1 character (``Zf`` and ``Zd``). Patch by +Victor Stinner. + +.. + +.. date: 2026-05-02-01-09-29 +.. gh-issue: 149221 +.. nonce: __KOks +.. section: Library + +Catch rare math domain error for :func:`random.binomialvariate`. + +.. + +.. date: 2026-05-01-16-45-31 +.. gh-issue: 149231 +.. nonce: x2nBEE +.. section: Library + +In :mod:`tomllib`, the number of parts in TOML keys is now limited. + +.. + +.. date: 2026-05-01-11-39-37 +.. gh-issue: 143231 +.. nonce: 0cOHET +.. section: Library + +:func:`unittest.TestCase.assertWarns` and +:func:`unittest.TestCase.assertWarnsRegex` no longer swallow warnings that +do not match the specified category or regex. Nested context managers are +now supported. + +.. + +.. date: 2026-05-01-10-20-27 +.. gh-issue: 149214 +.. nonce: btP546 +.. section: Library + +Fix :mod:`!_remote_debugging` misreading non-ASCII Unicode strings (Latin-1, +BMP and non-BMP) from a remote process. Filenames and function names that +contain non-ASCII characters are now reported correctly in stack traces, the +sampling profiler, and :mod:`asyncio` task introspection. + +.. + +.. date: 2026-04-30-18-56-23 +.. gh-issue: 149189 +.. nonce: mszW10 +.. section: Library + +:mod:`pprint` now uses modern defaults: ``indent=4`` and ``width=88``, and +the default ``compact=False`` output is now formatted similar to +pretty-printed :func:`json.dumps`, with opening parentheses and brackets +followed by a newline and the contents indented by one level. The *expand* +parameter, added in 3.15.0a8, has been removed; ``compact=False`` (the +default) now produces the former ``expand=True`` layout. Patch by Hugo van +Kemenade. + +.. + +.. date: 2026-04-30-14-21-26 +.. gh-issue: 149173 +.. nonce: KJqZm0 +.. section: Library + +Fix inverted :envvar:`PYTHON_BASIC_REPL` environment check in +``pdb._pyrepl_available``. + +.. + +.. date: 2026-04-29-16-11-27 +.. gh-issue: 149117 +.. nonce: yEeTYd +.. section: Library + +Fix :func:`runpy.run_module` and :func:`runpy.run_path` to set the +:attr:`~ImportError.name` attribute on the :exc:`ImportError` they raise. + +.. + +.. date: 2026-04-29-14-33-42 +.. gh-issue: 149148 +.. nonce: EaiYvk +.. section: Library + +:mod:`ensurepip`: Upgrade bundled pip to 26.1. This version fixes the +:cve:`2026-3219` vulnerability. Patch by Victor Stinner. + +.. + +.. date: 2026-04-29-13-08-46 +.. gh-issue: 149009 +.. nonce: rek3Tw +.. section: Library + +Validate that :mod:`profiling.sampling` binary profiles do not contain more +unique (thread, interpreter) pairs than declared in the header. Patch by +Maurycy Pawłowski-Wieroński. + +.. + +.. date: 2026-04-28-17-47-55 +.. gh-issue: 148292 +.. nonce: oIq3ml +.. section: Library + +:mod:`ssl`: Update :class:`ssl.SSLSocket` and :class:`ssl.SSLObject` for +OpenSSL 4. The classes now remember if they get a :exc:`ssl.SSLEOFError`. In +this case, following :meth:`~ssl.SSLSocket.read`, :meth:`!sendfile`, +:meth:`~ssl.SSLSocket.write`, and :meth:`~ssl.SSLSocket.do_handshake` calls +raise :exc:`ssl.SSLEOFError` without calling the underlying OpenSSL +function. Thanks to that, :class:`ssl.SSLSocket` behaves the same on all +OpenSSL versions on EOF. Patch by Victor Stinner. + +.. + +.. date: 2026-04-28-16-30-48 +.. gh-issue: 149085 +.. nonce: 5aNgBD +.. section: Library + +Add a *max_threads* keyword argument to :func:`faulthandler.dump_traceback`, +:func:`faulthandler.dump_traceback_later`, :func:`faulthandler.enable`, and +:func:`faulthandler.register`. + +.. + +.. date: 2026-04-28-16-25-40 +.. gh-issue: 148641 +.. nonce: aFgym0 +.. section: Library + +:func:`pkgutil.resolve_name` gets a new optional, keyword-only argument +called ``strict``. The default is ``False`` for backward compatibility. + +.. + +.. date: 2026-04-27-22-34-09 +.. gh-issue: 148093 +.. nonce: 9pWceM +.. section: Library + +Fix an out-of-bounds read of one byte in :func:`binascii.a2b_uu`. Raise +:exc:`binascii.Error`, instead of reading past the buffer end. + +.. + +.. date: 2026-04-27-20-15-54 +.. gh-issue: 149083 +.. nonce: BdrpU8 +.. section: Library + +:data:`dataclasses.MISSING` and :data:`dataclasses.KW_ONLY` are now +instances of :class:`sentinel`. + +.. + +.. date: 2026-04-27-17-12-11 +.. gh-issue: 148914 +.. nonce: i5C3kW +.. section: Library + +Fix memoization of in-band :class:`~pickle.PickleBuffer` in the Python +implementation of :mod:`pickle`. Previously, identical +:class:`!PickleBuffer`\ s did not preserve identity, and empty writable +:class:`!PickleBuffer` memoized an empty bytearray object in place of +``b''``, so the following references to ``b''`` were unpickled as an empty +bytearray object. + +.. + +.. date: 2026-04-26-23-01-50 +.. gh-issue: 149026 +.. nonce: Akk4Bc +.. section: Library + +Add colour to :mod:`pickletools` CLI output. Patch by Hugo van Kemenade. + +.. + +.. date: 2026-04-25-18-09-16 +.. gh-issue: 148991 +.. nonce: AZ64Et +.. section: Library + +Add colour to :mod:`tokenize` CLI output. Patch by Hugo van Kemenade. + +.. + +.. date: 2026-04-25-14-11-24 +.. gh-issue: 138907 +.. nonce: u21Wnh +.. section: Library + +Support :rfc:`9309` in :mod:`urllib.robotparser`. + +.. + +.. date: 2026-04-25-12-50-46 +.. gh-issue: 148981 +.. nonce: YMM4Y9 +.. section: Library + +Add *color* parameter to :func:`ast.dump`. + +.. + +.. date: 2026-04-25-12-04-27 +.. gh-issue: 148849 +.. nonce: Vk6yEW +.. section: Library + +Deprecate :meth:`http.cookies.Morsel.js_output` and +:meth:`http.cookies.BaseCookie.js_output`, which will be removed in Python +3.19. Use :meth:`http.cookies.Morsel.output` or +:meth:`http.cookies.BaseCookie.output` instead. + +.. + +.. date: 2026-04-25-11-56-05 +.. gh-issue: 146311 +.. nonce: iHWO0v +.. section: Library + +Add a *canonical* keyword-only parameter to the base16, base32, base64, +base85, ascii85, and Z85 decoders in :mod:`base64` and :mod:`binascii`. When +true, encodings with non-zero padding bits (base16/32/64) or non-canonical +encodings (base85/ascii85) are rejected. Single-character final groups in +:func:`binascii.a2b_ascii85` and :func:`binascii.a2b_base85` are now always +rejected as encoding violations, regardless of *canonical*; previously they +were silently ignored and produced no output bytes. + +.. + +.. date: 2026-04-23-21-47-49 +.. gh-issue: 148947 +.. nonce: W4V2lG +.. section: Library + +Fix crash in :deco:`dataclasses.dataclass` with ``slots=True`` that occurred +when a function found within the class had an empty ``__class__`` cell. + +.. + +.. date: 2026-04-23-07-38-04 +.. gh-issue: 148680 +.. nonce: ___ePl +.. section: Library + +``ForwardRef`` objects that contain internal names to represent known +objects now show the ``type_repr`` of the known object rather than the +internal ``__annotationlib_name_x__`` name when evaluated as strings. + +.. + +.. date: 2026-04-22-20-49-49 +.. gh-issue: 124397 +.. nonce: plMglV +.. section: Library + +The threading module added tooling to support concurrent iterator access: +:class:`threading.serialize_iterator`, +:func:`threading.synchronized_iterator`, and +:func:`threading.concurrent_tee`. + +.. + +.. date: 2026-04-20-18-29-21 +.. gh-issue: 148801 +.. nonce: ROeNqs +.. section: Library + +:mod:`xml.etree.ElementTree`: Fix a crash in :meth:`Element.__deepcopy__ +<object.__deepcopy__>` on deeply nested trees. + +.. + +.. date: 2026-04-18-21-39-15 +.. gh-issue: 148735 +.. nonce: siw6DG +.. section: Library + +:mod:`xml.etree.ElementTree`: Fix a use-after-free in +:meth:`Element.findtext <xml.etree.ElementTree.Element.findtext>` when the +element tree is mutated concurrently during the search. + +.. + +.. date: 2026-04-18-17-37-13 +.. gh-issue: 148740 +.. nonce: sYnFi0 +.. section: Library + +Fix usage for :mod:`uuid` command-line interface to support a custom +namespace be provided for uuid3 and uuid5. + +.. + +.. date: 2026-04-17-16-31-58 +.. gh-issue: 148688 +.. nonce: vVugFn +.. section: Library + +:mod:`bz2`, :mod:`compression.zstd`, :mod:`lzma`, :mod:`zlib`: Fix a double +free on memory allocation failure. Patch by Victor Stinner. + +.. + +.. date: 2026-04-17-13-56-44 +.. gh-issue: 148675 +.. nonce: f1kG70 +.. section: Library + +:mod:`array`, :mod:`struct`: Add support for ``Zd`` and ``Zf`` formats for +double complex and float complex. Patch by Victor Stinner. + +.. + +.. date: 2026-04-16-13-30-00 +.. gh-issue: 148651 +.. nonce: ZsTdLk +.. section: Library + +Fix reference leak in :class:`compression.zstd.ZstdDecompressor` when an +invalid option key is passed. + +.. + +.. date: 2026-04-15-21-46-52 +.. gh-issue: 148641 +.. nonce: -aoFyC +.. section: Library + +:pep:`829` (package startup configuration files) implements a new format +``<name>.start`` parallel to ``<name>.pth`` files, to replace ``import`` +lines in the latter. + +.. + +.. date: 2026-04-15-20-32-55 +.. gh-issue: 148639 +.. nonce: -dwsjB +.. section: Library + +Implement :pep:`800`, adding the :deco:`typing.disjoint_base` decorator. +Patch by Jelle Zijlstra. + +.. + +.. date: 2026-04-15-16-08-12 +.. gh-issue: 148615 +.. nonce: Uvx50R +.. section: Library + +Fix :mod:`pdb` to accept standard -- end of options separator. Reported by +haampie. Patched by Shrey Naithani. + +.. + +.. date: 2026-04-15-11-00-39 +.. gh-issue: 146553 +.. nonce: VGOsoP +.. section: Library + +Fix infinite loop in :func:`typing.get_type_hints` when ``__wrapped__`` +forms a cycle. Patch by Shamil Abdulaev. + +.. + +.. date: 2026-04-15-09-36-03 +.. gh-issue: 148599 +.. nonce: 90i1Ku +.. section: Library + +Update the :mod:`socket` module's WSA error messages to match official +documentation. + +.. + +.. date: 2026-04-14-09-04-35 +.. gh-issue: 148508 +.. nonce: -GiXml +.. section: Library + +An intermittent timing error when running SSL tests on iOS has been +resolved. + +.. + +.. date: 2026-04-13-21-38-50 +.. gh-issue: 144881 +.. nonce: 3kPqXw +.. section: Library + +:mod:`asyncio` debugging tools (``python -m asyncio ps`` and ``pstree``) now +retry automatically on transient errors that can occur when attaching to a +process under active thread delegation. The number of retries can be +controlled with the ``--retries`` flag. Patch by Bartosz Sławecki. + +.. + +.. date: 2026-04-13-15-59-44 +.. gh-issue: 148518 +.. nonce: RQdvsu +.. section: Library + +If an email containing an address header that ended in an open double quote +was parsed with a non-``compat32`` policy, accessing the ``username`` +attribute of the mailbox accessed through that header object would result in +an ``IndexError``. It now correctly returns an empty string as the result. + +.. + +.. date: 2026-04-13-06-22-27 +.. gh-issue: 148464 +.. nonce: Bj_NZy +.. section: Library + +Add missing ``__ctype_le/be__`` attributes for +:class:`~ctypes.c_float_complex` and :class:`~ctypes.c_double_complex`. +Patch by Sergey B Kirpichev. + +.. + +.. date: 2026-04-12-16-40-11 +.. gh-issue: 148370 +.. nonce: 0Li2EK +.. section: Library + +:mod:`configparser`: prevent quadratic behavior when a +:exc:`~configparser.ParsingError` is raised after a parser fails to parse +multiple lines. Patch by Bénédikt Tran. + +.. + +.. date: 2026-04-12-12-31-45 +.. gh-issue: 121190 +.. nonce: O6-E5_ +.. section: Library + +``importlib.resources.files()`` now emits a more meaningful error message +when module spec is None (as found in some ``__main__`` modules). + +.. + +.. date: 2026-04-11-17-28-06 +.. gh-issue: 127012 +.. nonce: h3rLYS +.. section: Library + +``importlib.abc.Traversable.read_text`` now allows/solicits an ``errors`` +parameter. + +.. + +.. date: 2026-04-11-12-32-38 +.. gh-issue: 137855 +.. nonce: tsVny_ +.. section: Library + +Improve import time of :mod:`dataclasses` module by lazy importing :mod:`re` +and :mod:`copy` modules. + +.. + +.. date: 2026-04-10-20-23-22 +.. gh-issue: 148352 +.. nonce: lrec3W +.. section: Library + +Add more color to :mod:`calendar`'s CLI output. Patch by Hugo van Kemenade. + +.. + +.. date: 2026-04-09-12-42-42 +.. gh-issue: 148254 +.. nonce: Xt7vKs +.. section: Library + +Use singular "sec" instead of "secs" in :mod:`timeit` verbose output for +consistency with other time units. + +.. + +.. date: 2026-04-08-21-39-01 +.. gh-issue: 130472 +.. nonce: 4Bk6qH +.. section: Library + +Integrate fancycompleter with import completions. + +.. + +.. date: 2026-04-08-14-19-17 +.. gh-issue: 148241 +.. nonce: fO_QT4 +.. section: Library + +:mod:`json`: Fix serialization: no longer call ``str(obj)`` on :class:`str` +subclasses. Patch by Victor Stinner. + +.. + +.. date: 2026-04-08-11-44-12 +.. gh-issue: 148225 +.. nonce: H34yJp +.. section: Library + +The :mod:`profiling.sampling` ``replay`` command now rejects non-binary +profile files with a clear error explaining that replay only accepts files +created with ``--binary``. + +.. + +.. date: 2026-04-07-14-13-40 +.. gh-issue: 148192 +.. nonce: 34AUYQ +.. section: Library + +``email.generator.Generator._make_boundary`` could fail to detect a +duplicate boundary string if linesep was not \n. It now correctly detects +boundary strings when linesep is \r\n as well. + +.. + +.. date: 2026-04-07-12-37-53 +.. gh-issue: 148207 +.. nonce: YhGem4 +.. section: Library + +:class:`typing.TypeVarTuple` now accepts ``bound``, ``covariant``, +``contravariant``, and ``infer_variance`` parameters, matching the interface +of :class:`typing.TypeVar` and :class:`typing.ParamSpec`. + +.. + +.. date: 2026-04-04-20-22-02 +.. gh-issue: 148100 +.. nonce: lSmGQi +.. section: Library + +:term:`Soft deprecate <soft deprecated>` :func:`re.match` and +:meth:`re.Pattern.match` in favour of :func:`re.prefixmatch` and +:meth:`re.Pattern.prefixmatch`. Patch by Hugo van Kemenade. + +.. + +.. date: 2026-04-02-05-06-34 +.. gh-issue: 147991 +.. nonce: 2ANtR5 +.. section: Library + +Improve :mod:`tomllib` import time (up to 10x faster). Patch by Victor +Stinner. + +.. + +.. date: 2026-04-01-07-10-49 +.. gh-issue: 147957 +.. nonce: QXf5Xx +.. section: Library + +Guarantees that :meth:`collections.UserDict.popitem` will pop in the same +order as the wrapped dictionary rather than an arbitrary order. + +.. + +.. date: 2026-03-31-17-33-10 +.. gh-issue: 146256 +.. nonce: Nm_Ke_ +.. section: Library + +The ``profiling.sampling`` module now supports JSONL output format via +``--jsonl``. Each run emits a newline-delimited JSON file that is +sequentially parseable by external tools, scripts, and programmatic +consumers. Patch by Maurycy Pawłowski-Wieroński. + +.. + +.. date: 2026-03-29-21-31-14 +.. gh-issue: 146609 +.. nonce: BnshCt +.. section: Library + +Add colour to :mod:`timeit` CLI output. Patch by Hugo van Kemenade. + +.. + +.. date: 2026-03-28-11-31-32 +.. gh-issue: 146563 +.. nonce: cXtSym +.. section: Library + +:mod:`xml.parsers.expat`: add an exception note when a custom Expat handler +return value cannot be properly interpreted. Patch by Bénédikt Tran. + +.. + +.. date: 2026-03-26-01-42-20 +.. gh-issue: 137586 +.. nonce: KmHRwR +.. section: Library + +Add :class:`!MacOS` to :mod:`webbrowser` for macOS, which opens URLs via +``/usr/bin/open`` instead of piping AppleScript to ``osascript``. Deprecate +:class:`!MacOSXOSAScript` in favour of :class:`!MacOS`. + +.. + +.. date: 2026-03-25-07-17-41 +.. gh-issue: 146406 +.. nonce: ydsmqe +.. section: Library + +Cross-language method suggestions are now shown for :exc:`AttributeError` on +builtin types and their subclasses. For example, ``[].push()`` suggests +``append``, ``(1,2).append(3)`` suggests using a ``list``, ``None.keys()`` +suggests expecting a ``dict``, and ``1.0.__or__`` suggests using an ``int``. + +.. + +.. date: 2026-03-22-23-42-22 +.. gh-issue: 146313 +.. nonce: RtDeAd +.. section: Library + +Fix a deadlock in :mod:`multiprocessing`'s resource tracker where the parent +process could hang indefinitely in :func:`os.waitpid` during interpreter +shutdown if a child created via :func:`os.fork` still held the resource +tracker's pipe open. + +.. + +.. date: 2026-03-22-16-52-04 +.. gh-issue: 146292 +.. nonce: rJvvs0 +.. section: Library + +Add colour to :mod:`~http.server.BaseHTTPRequestHandler` logs, as used by +the :mod:`http.server` CLI. Patch by Hugo van Kemenade. + +.. + +.. date: 2026-03-13-14-23-33 +.. gh-issue: 145917 +.. nonce: TooGKx +.. section: Library + +Add MIME types for TTC and Haptics formats to :mod:`mimetypes`. (Contributed +by Charlie Lin in :gh:`145918`.) + +.. + +.. date: 2026-03-12-00-00-00 +.. gh-issue: 145846 +.. nonce: UbHxjv +.. section: Library + +Fix memory leak in ``_lsprof`` when ``clear()`` is called during active +profiling with nested calls. ``clearEntries()`` now walks the entire +``currentProfilerContext`` linked list instead of only freeing the top +context. + +.. + +.. date: 2026-03-11-15-09-52 +.. gh-issue: 145831 +.. nonce: _sW94w +.. section: Library + +Fix :func:`!email.quoprimime.decode` leaving a stray ``\r`` when +``eol='\r\n'`` by stripping the full *eol* string instead of one character. + +.. + +.. date: 2026-03-01-01-58-10 +.. gh-issue: 145378 +.. nonce: oy6rb9 +.. section: Library + +Use ``PyREPL`` as the default input console for :mod:`pdb` + +.. + +.. date: 2026-02-26-12-00-00 +.. gh-issue: 145244 +.. nonce: Kj31cp +.. section: Library + +Fixed a use-after-free in :mod:`json` encoder when a ``default`` callback +mutates the dictionary being serialized. + +.. + +.. date: 2026-02-25-22-20-00 +.. gh-issue: 117716 +.. nonce: w6kYp9 +.. section: Library + +Fix :mod:`wave` writing of odd-sized ``data`` chunks by appending the +required RIFF pad byte and correcting the RIFF chunk size field accordingly. + +.. + +.. date: 2026-02-25-10-00-00 +.. gh-issue: 145200 +.. nonce: m_4PAtcI +.. section: Library + +:mod:`hashlib`: fix a memory leak when allocating or initializing an OpenSSL +HMAC context fails. + +.. + +.. date: 2026-02-22-19-36-00 +.. gh-issue: 145056 +.. nonce: TH8nX4 +.. section: Library + +Add support for :class:`frozendict` in :meth:`dataclasses.asdict` and +:meth:`dataclasses.astuple`. + +.. + +.. date: 2026-02-22-00-00-00 +.. gh-issue: 145105 +.. nonce: csv-reader-reentrant +.. section: Library + +Fix crash in :mod:`csv` reader when iterating with a re-entrant iterator +that calls :func:`next` on the same reader from within ``__next__``. + +.. + +.. date: 2026-02-19-04-40-57 +.. gh-issue: 130750 +.. nonce: 0hW52O +.. section: Library + +Restore quoting of choices in :mod:`argparse` error messages for improved +clarity and consistency with documentation. + +.. + +.. date: 2026-02-12-18-05-16 +.. gh-issue: 137855 +.. nonce: 2_PTbg +.. section: Library + +Reduce the import time of :mod:`dataclasses` module by ~20%. + +.. + +.. date: 2026-02-07-12-54-20 +.. gh-issue: 70647 +.. nonce: Bja_Lk +.. section: Library + +:meth:`~datetime.datetime.strptime` now raises :exc:`ValueError` when the +format string contains ``%d`` without a year directive. Using ``%e`` without +a year now emits a :exc:`DeprecationWarning`. + +.. + +.. date: 2026-01-19-21-23-18 +.. gh-issue: 105936 +.. nonce: dGrzjM +.. section: Library + +Attempting to mutate non-field attributes of :mod:`dataclasses` with both +*frozen* and *slots* being ``True`` now raises +:class:`~dataclasses.FrozenInstanceError` instead of :class:`TypeError`. +Their non-dataclass subclasses can now freely mutate non-field attributes, +and the original non-slotted class can be garbage collected. + +.. + +.. date: 2025-12-17-04-10-35 +.. gh-issue: 142831 +.. nonce: ee3t4L +.. section: Library + +Fix a crash in the :mod:`json` module where a use-after-free could occur if +the object being encoded is modified during serialization. + +.. + +.. date: 2025-12-17-02-55-03 +.. gh-issue: 108411 +.. nonce: up7MAc +.. section: Library + +``typing.IO`` and ``typing.BinaryIO`` method arguments are now +positional-only. + +.. + +.. date: 2025-12-10-15-15-09 +.. gh-issue: 130273 +.. nonce: iCfiY5 +.. section: Library + +Fix traceback color output with Unicode characters. + +.. + +.. date: 2025-12-06-11-24-25 +.. gh-issue: 142307 +.. nonce: w8evI9 +.. section: Library + +:mod:`imaplib`: deprecate support for :attr:`IMAP4.file +<imaplib.IMAP4.file>`. This attribute was never meant to be part of the +public interface and altering its value may result in unclosed files or +other synchronization issues with the underlying socket. Patch by Bénédikt +Tran. + +.. + +.. date: 2025-12-06-08-48-26 +.. gh-issue: 141449 +.. nonce: hQvNW_ +.. section: Library + +Improve tests and documentation for non-function callables as +:term:`annotate functions <annotate function>`. + +.. + +.. date: 2025-10-18-12-13-39 +.. gh-issue: 140287 +.. nonce: 49iU-4 +.. section: Library + +The :mod:`asyncio` REPL now handles exceptions when executing +:envvar:`PYTHONSTARTUP` scripts. Patch by Bartosz Sławecki. + +.. + +.. date: 2025-10-08-15-36-00 +.. gh-issue: 139489 +.. nonce: W46tvn +.. section: Library + +Add the :func:`xml.is_valid_name` function, which allows to check whether a +string can be used as an element or attribute name in XML. + +.. + +.. date: 2025-08-24-15-09-30 +.. gh-issue: 75707 +.. nonce: GOWZrC +.. section: Library + +Add optional ``mtime`` argument to :func:`tarfile.open`, for setting the +``mtime`` header field in ``.tar.gz`` archives. + +.. + +.. date: 2025-07-02-17-01-17 +.. gh-issue: 125862 +.. nonce: WgFYj3 +.. section: Library + +The :func:`contextlib.contextmanager` and +:func:`contextlib.asynccontextmanager` decorators now work correctly with +generators, coroutine functions, and async generators when the wrapped +callables are used as decorators. + +.. + +.. date: 2025-06-22-16-29-10 +.. gh-issue: 135528 +.. nonce: Rt_QhR +.. section: Library + +:mod:`http.cookiejar`: add "tv", "or", "nom", "sch", and "web" to the +default list of supported country code second-level domains. + +.. + +.. date: 2025-06-02-22-23-38 +.. gh-issue: 135056 +.. nonce: yz3dSs +.. section: Library + +Add a ``-H`` or ``--header`` CLI option to :program:`python -m http.server`. +Contributed by Anton I. Sipos. + +.. + +.. date: 2025-05-23-10-28-51 +.. gh-issue: 134551 +.. nonce: 0rnq0X +.. section: Library + +Add t-strings support to pprint functions + +.. + +.. date: 2025-05-16-01-43-58 +.. gh-issue: 133956 +.. nonce: 5kWDYd +.. section: Library + +Fix bug where :func:`@dataclass <dataclasses.dataclass>` wouldn't detect +``ClassVar`` fields if ``ClassVar`` was re-exported from a module other than +:mod:`typing`. + +.. + +.. date: 2025-04-17-15-26-35 +.. gh-issue: 132631 +.. nonce: IDFZfb +.. section: Library + +Fix "I/O operation on closed file" when parsing JSON Lines file with +:mod:`JSON CLI <json.tool>`. + +.. + +.. date: 2024-11-24-07-18-40 +.. gh-issue: 108951 +.. nonce: jyKygP +.. section: Library + +:mod:`asyncio`: Add :meth:`TaskGroup.cancel <asyncio.TaskGroup.cancel>` +which cancels unfinished tasks and exits the group without raising +:exc:`asyncio.CancelledError`. + +.. + +.. date: 2024-09-09-12-48-37 +.. gh-issue: 123853 +.. nonce: e-zFxb +.. section: Library + +Update the table of Windows language code identifiers (LCIDs) used by +:func:`locale.getdefaultlocale` on Windows to protocol version 16.0 +(2024-04-23). + +.. + +.. date: 2024-07-31-17-23-06 +.. gh-issue: 122476 +.. nonce: TtUa-c +.. section: Library + +The :mod:`email` module no longer incorrectly uses :rfc:`2047` encoding for +a mailbox with non-ASCII characters in its local-part. Under a policy with +:attr:`~email.policy.EmailPolicy.utf8` set ``False``, attempting to +serialize such a message will now raise an +:exc:`~email.errors.HeaderWriteError`. There is no valid 7-bit encoding for +an internationalized local-part. Use :data:`email.policy.SMTPUTF8` (or +another policy with ``utf8=True``) to correctly pass through the local-part +as Unicode characters. + +.. + +.. date: 2024-07-31-17-22-10 +.. gh-issue: 83938 +.. nonce: TtUa-c +.. section: Library + +The :mod:`email` module no longer incorrectly uses :rfc:`2047` encoding for +a mailbox with non-ASCII characters in its domain. Under a policy with +:attr:`~email.policy.EmailPolicy.utf8` set ``False``, attempting to +serialize such a message will now raise an +:exc:`~email.errors.HeaderWriteError`. Either apply an appropriate IDNA +encoding to convert the domain to ASCII before serialization, or use +:data:`email.policy.SMTPUTF8` (or another policy with ``utf8=True``) to +correctly pass through the internationalized domain name as Unicode +characters. + +.. + +.. date: 2024-07-30-19-19-33 +.. gh-issue: 81074 +.. nonce: YAeWNf +.. section: Library + +The :mod:`email` module no longer treats email addresses with non-ASCII +characters as defects when parsing a Unicode string or in the ``addr_spec`` +parameter to :class:`email.headerregistry.Address`. :rfc:`5322` permits such +addresses, and they were already supported when parsing bytes and in the +Address ``username`` parameter. + +The (undocumented) :exc:`!email.errors.NonASCIILocalPartDefect` is no longer +used and should be considered deprecated. + +.. + +.. date: 2024-02-10-21-25-22 +.. gh-issue: 70039 +.. nonce: 6wvcAP +.. section: Library + +Fixed bug where :meth:`smtplib.SMTP.starttls` could fail if +:meth:`smtplib.SMTP.connect` is called explicitly rather than implicitly. + +.. + +.. date: 2023-12-25-19-14-07 +.. gh-issue: 113471 +.. nonce: ZQMpbI +.. section: Library + +Allow :mod:`http.server` to set a default content-type when serving files +with an unknown or missing extension. + +.. + +.. date: 2023-09-08-13-10-32 +.. gh-issue: 83281 +.. nonce: 2Plpcj +.. section: Library + +:mod:`email`: improve handling trailing garbage in address lists to avoid +throwing AttributeError in certain edge cases + +.. + +.. date: 2022-09-17-20-20-01 +.. gh-issue: 96894 +.. nonce: t7my0A +.. section: Library + +Do not turn echo off for subsequent commands in batch activators +(``activate.bat`` and ``deactivate.bat``) of :mod:`venv`. + +.. + +.. date: 2026-04-17-02-28-55 +.. gh-issue: 148663 +.. nonce: MHIbRB +.. section: Documentation + +Document that :class:`calendar.IllegalMonthError` is a subclass of both +:exc:`ValueError` and :exc:`IndexError` since Python 3.12. + +.. + +.. date: 2026-04-02-07-20-00 +.. gh-issue: 146646 +.. nonce: GlobDoc1 +.. section: Documentation + +Document that :func:`glob.glob`, :func:`glob.iglob`, +:meth:`pathlib.Path.glob`, and :meth:`pathlib.Path.rglob` silently suppress +:exc:`OSError` exceptions raised from scanning the filesystem. + +.. + +.. date: 2026-05-05-18-49-44 +.. gh-issue: 149425 +.. nonce: QnQL8j +.. section: Tests + +Increase time delta in +``test.test_zipfile.test_core.OtherTests.test_write_without_source_date_epoch`` + +.. + +.. date: 2026-04-21-12-33-14 +.. gh-issue: 148600 +.. nonce: vnTb3t +.. section: Tests + +Add OpenSSL 4.0.0 support to test configurations. + +.. + +.. date: 2026-05-04-23-07-45 +.. gh-issue: 149353 +.. nonce: XfM8aQ +.. section: Build + +Avoid unnecessary JIT-related rebuilds during ``make install`` after +``--enable-optimizations`` builds. + +.. + +.. date: 2026-05-04-06-03-50 +.. gh-issue: 149351 +.. nonce: hN4sF0 +.. section: Build + +Avoid possible broken macOS framework install names when DESTDIR is +specified during builds. + +.. + +.. date: 2026-05-01-20-01-32 +.. gh-issue: 149252 +.. nonce: 4W_0-w +.. section: Build + +Update to WASI SDK 33. + +.. + +.. date: 2026-05-01-12-01-54 +.. gh-issue: 148690 +.. nonce: oTtYk- +.. section: Build + +Windows free-threaded builds now output to a different default path with +default filenames, for example, ``PCbuild/amd64t/python.exe`` rather than +``PCbuild/amd64/python3.15t.exe``. The ``PC/layout`` script has been updated +to ensure compatibility of generated layouts. + +.. + +.. date: 2026-04-30-08-43-47 +.. gh-issue: 146475 +.. nonce: 1cL4hX +.. section: Build + +Block Apple Clang from being used to build the JIT as it ships without +required LLVM tools. + +.. + +.. date: 2026-04-17-21-45-32 +.. gh-issue: 148644 +.. nonce: vwkknh +.. section: Build + +Errors during the PGO training job on Windows are no longer ignored, and a +non-zero return code will cause the build to fail. + +.. + +.. date: 2026-04-14-15-20-29 +.. gh-issue: 148535 +.. nonce: JjKiaa +.. section: Build + +No longer use the ``gcc -fprofile-update=atomic`` flag on i686. The flag has +been added to fix a random GCC internal error on PGO build (:gh:`145801`) +caused by corruption of profile data (.gcda files). The problem is that it +makes the PGO build way slower (up to 47x slower) on i686. Since the GCC +internal error was not seen on i686 so far, don't use +``-fprofile-update=atomic`` on i686 anymore. Patch by Victor Stinner. + +.. + +.. date: 2026-04-13-02-36-13 +.. gh-issue: 148483 +.. nonce: gLe1h8 +.. section: Build + +Use ``Py_GCC_ATTRIBUTE(unused)`` for stop_tracing label. + +.. + +.. date: 2026-04-12-22-54-16 +.. gh-issue: 148474 +.. nonce: ouIO8R +.. section: Build + +Fixed compilation of :file:`Python/pystrhex.c` with older clang versions. + +.. + +.. date: 2026-04-09-11-42-32 +.. gh-issue: 146445 +.. nonce: Z1vccC +.. section: Build + +The Android build tools have been moved to the Platforms folder. + +.. + +.. date: 2026-03-21-18-51-31 +.. gh-issue: 146264 +.. nonce: Q9Ej4m +.. section: Build + +Fix static module builds on non-WASI targets by linking HACL dependencies as +static libraries when ``MODULE_BUILDTYPE=static``, preventing duplicate +``_Py_LibHacl_*`` symbol errors at link time. + +.. + +.. date: 2025-09-03-14-55-59 +.. gh-issue: 138451 +.. nonce: -Qzh2S +.. section: Build + +Allow for custom LLVM path using ``LLVM_TOOLS_INSTALL_DIR`` during JIT +build. + +.. + +.. date: 2025-05-02-17-06-10 +.. gh-issue: 133312 +.. nonce: YkO6BI +.. section: Build + +Add a new ``./configure`` option +:option:`--enable-static-libpython-for-interpreter` which, when used with +:option:`--enable-shared`, continues to build the shared library but does +not use it for the interpreter. Instead, libpython is statically linked into +the interpreter, as if :option:`--enable-shared` had not been used. This +allows you to do a single build and get a Python interpreter binary that +does not use a shared library but also get a shared library for use by other +programs. + +.. + +.. date: 2026-05-03-13-55-51 +.. gh-issue: 149254 +.. nonce: ENtMYD +.. section: Windows + +Updated bundled version of OpenSSL to 3.5.6. + +.. + +.. date: 2026-05-01-12-03-39 +.. gh-issue: 148690 +.. nonce: TMV8dU +.. section: Windows + +Non-freethreaded builds on Windows now support extensions linked to +``python3t.dll``, and will include a copy of that library in normal installs +that references the non-freethreaded runtime. + +.. + +.. date: 2026-03-27-22-06-10 +.. gh-issue: 146458 +.. nonce: fYj0UQ +.. section: Windows + +Fix incorrect REPL height and width tracking on console window resize on +Windows. + +.. + +.. date: 2026-05-06-18-23-36 +.. gh-issue: 142295 +.. nonce: O9RmZH +.. section: macOS + +For Python macOS framework builds, update Info.plist files to be more +compliant with current Apple guidelines. Original patch contributed by +Martinus Verburg. + +.. + +.. date: 2026-05-01-20-12-33 +.. gh-issue: 149254 +.. nonce: kXdWpS +.. section: macOS + +Update macOS installer to use OpenSSL 3.5.6. + +.. + +.. date: 2026-03-07-20-47-40 +.. gh-issue: 94523 +.. nonce: dq7m2k +.. section: IDLE + +Detect file if modified at local disk and prompt to ask refresh. Patch by +Shixian Li. + +.. + +.. date: 2025-10-05-19-33-39 +.. gh-issue: 139551 +.. nonce: TX9BRc +.. section: IDLE + +Support rendering :exc:`BaseExceptionGroup` in IDLE. + +.. + +.. date: 2021-10-03-21-55-34 +.. gh-issue: 89520 +.. nonce: etEExa +.. section: IDLE + +Make IDLE extension configuration look at user config files, allowing +user-installed extensions to have settings and key bindings defined in +~/.idlerc. + +.. + +.. date: 2026-05-01-14-49-09 +.. gh-issue: 149225 +.. nonce: IdAYPZ +.. section: C API + +:c:type:`PyCriticalSection` and related functions are added to the Stable +ABI. + +.. + +.. date: 2026-05-01-00-00-00 +.. gh-issue: 149216 +.. nonce: TpWatch +.. section: C API + +:c:type:`PyType_WatchCallback` callbacks registered via +:c:func:`PyType_AddWatcher` are now also invoked when a watched heap type is +deallocated. Previously, type watchers were only notified of modifications, +which could cause stale references when a type was freed and its address was +reused. + +.. + +.. date: 2026-04-28-17-43-12 +.. gh-issue: 149101 +.. nonce: HTuHTb +.. section: C API + +Implement :pep:`788`. + +.. + +.. date: 2026-04-27-10-56-22 +.. gh-issue: 149044 +.. nonce: TbOcUS +.. section: C API + +Implement :pep:`820`: Unified slot system for the C API. + +.. + +.. date: 2026-04-09-14-45-44 +.. gh-issue: 148267 +.. nonce: p84kG_ +.. section: C API + +Using :c:macro:`Py_LIMITED_API` on a non-Windows free-threaded build no +longer needs an extra :c:macro:`Py_GIL_DISABLED`. + +.. + +.. date: 2026-04-05-18-18-59 +.. gh-issue: 145559 +.. nonce: qKJH9S +.. section: C API + +Rename ``_Py_DumpTraceback`` and ``_Py_DumpTracebackThreads`` to +:c:func:`PyUnstable_DumpTraceback` and +:c:func:`PyUnstable_DumpTracebackThreads`. + +.. + +.. date: 2026-04-03-11-06-20 +.. gh-issue: 146636 +.. nonce: zR6Jsn +.. section: C API + +Implement :pep:`803` -- ``abi3t``: Stable ABI for Free-Threaded Builds. + +.. + +.. date: 2026-03-22-00-00-00 +.. gh-issue: 146302 +.. nonce: PyIsInit +.. section: C API + +:c:func:`Py_IsInitialized` no longer returns true until initialization has +fully completed, including import of the :mod:`site` module. The underlying +runtime flags now use atomic operations. + +.. + +.. date: 2026-03-19-15-28-14 +.. gh-issue: 146063 +.. nonce: Sc-1RU +.. section: C API + +Add :c:func:`PyObject_CallFinalizerFromDealloc` function to the limited C +API. Patch by Victor Stinner. + +.. + +.. date: 2026-03-13-16-37-54 +.. gh-issue: 145921 +.. nonce: ssA7HZ +.. section: C API + +Add functions that are guaranteed to be safe for use in +:c:member:`~PyTypeObject.tp_traverse` handlers: +:c:func:`PyObject_GetTypeData_DuringGC`, +:c:func:`PyObject_GetItemData_DuringGC`, +:c:func:`PyType_GetModuleState_DuringGC`, +:c:func:`PyModule_GetState_DuringGC`, :c:func:`PyModule_GetToken_DuringGC`, +:c:func:`PyType_GetBaseByToken_DuringGC`, +:c:func:`PyType_GetModule_DuringGC`, +:c:func:`PyType_GetModuleByToken_DuringGC`. diff --git a/Misc/NEWS.d/3.7.0a1.rst b/Misc/NEWS.d/3.7.0a1.rst index fd6ba07b53a617..69a0c09bda4145 100644 --- a/Misc/NEWS.d/3.7.0a1.rst +++ b/Misc/NEWS.d/3.7.0a1.rst @@ -1042,8 +1042,8 @@ method of other descriptors. Remove the ``PyEval_GetCallStats()`` function and deprecate the untested and undocumented ``sys.callstats()`` function. Remove the ``CALL_PROFILE`` -special build: use the :func:`sys.setprofile` function, :mod:`cProfile` or -:mod:`profile` to profile function calls. +special build: use the :func:`sys.setprofile` function, :mod:`!cProfile` or +:mod:`!profile` to profile function calls. .. diff --git a/Misc/NEWS.d/3.7.0a4.rst b/Misc/NEWS.d/3.7.0a4.rst index 2ceb9e78e0421b..691ac0f4a8cec8 100644 --- a/Misc/NEWS.d/3.7.0a4.rst +++ b/Misc/NEWS.d/3.7.0a4.rst @@ -403,7 +403,7 @@ SOCK_CLOEXEC. .. nonce: zmO8G2 .. section: Library -Add :class:`importlib.abc.ResourceReader` as an ABC for loaders to provide a +Add :class:`!importlib.abc.ResourceReader` as an ABC for loaders to provide a unified API for reading resources contained within packages. Also add :mod:`importlib.resources` as the port of ``importlib_resources``. diff --git a/Misc/NEWS.d/3.7.0b1.rst b/Misc/NEWS.d/3.7.0b1.rst index c9786e55c20739..d785f6d8c4c800 100644 --- a/Misc/NEWS.d/3.7.0b1.rst +++ b/Misc/NEWS.d/3.7.0b1.rst @@ -598,7 +598,7 @@ Add socket.getblocking() method. .. nonce: zmO8G2 .. section: Library -Add :mod:`importlib.resources` and :class:`importlib.abc.ResourceReader` as +Add :mod:`importlib.resources` and :class:`!importlib.abc.ResourceReader` as the unified API for reading resources contained within packages. Loaders wishing to support resource reading must implement the :meth:`get_resource_reader` method. File-based and zipimport-based diff --git a/Misc/NEWS.d/3.7.0b4.rst b/Misc/NEWS.d/3.7.0b4.rst index 93627f54900ddd..789d96dffd666f 100644 --- a/Misc/NEWS.d/3.7.0b4.rst +++ b/Misc/NEWS.d/3.7.0b4.rst @@ -152,7 +152,7 @@ Ensure line-endings are respected when using lib2to3. .. section: Library Have :func:`importlib.resources.contents` and -:meth:`importlib.abc.ResourceReader.contents` return an :term:`iterable` +:meth:`!importlib.abc.ResourceReader.contents` return an :term:`iterable` instead of an :term:`iterator`. .. diff --git a/Misc/NEWS.d/3.8.0a1.rst b/Misc/NEWS.d/3.8.0a1.rst index 93995bc8feaad7..3a6966deb87825 100644 --- a/Misc/NEWS.d/3.8.0a1.rst +++ b/Misc/NEWS.d/3.8.0a1.rst @@ -5130,7 +5130,7 @@ Ensure line-endings are respected when using lib2to3. .. section: Library Have :func:`importlib.resources.contents` and -:meth:`importlib.abc.ResourceReader.contents` return an :term:`iterable` +:meth:`!importlib.abc.ResourceReader.contents` return an :term:`iterable` instead of an :term:`iterator`. .. diff --git a/Misc/NEWS.d/next/Build/2024-12-04-10-00-35.gh-issue-127545.t0THjE.rst b/Misc/NEWS.d/next/Build/2024-12-04-10-00-35.gh-issue-127545.t0THjE.rst deleted file mode 100644 index 3667e2778b7b93..00000000000000 --- a/Misc/NEWS.d/next/Build/2024-12-04-10-00-35.gh-issue-127545.t0THjE.rst +++ /dev/null @@ -1 +0,0 @@ -Fix crash when building on Linux/m68k. diff --git a/Misc/NEWS.d/next/Build/2025-04-16-09-38-48.gh-issue-117088.EFt_5c.rst b/Misc/NEWS.d/next/Build/2025-04-16-09-38-48.gh-issue-117088.EFt_5c.rst deleted file mode 100644 index 0845b0551394f8..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-04-16-09-38-48.gh-issue-117088.EFt_5c.rst +++ /dev/null @@ -1 +0,0 @@ -AIX linker don't support -h option, so avoid it through platform check diff --git a/Misc/NEWS.d/next/Build/2025-05-13-12-38-26.gh-issue-115119.7n0nx3.rst b/Misc/NEWS.d/next/Build/2025-05-13-12-38-26.gh-issue-115119.7n0nx3.rst new file mode 100644 index 00000000000000..0c6804ebf9c890 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-05-13-12-38-26.gh-issue-115119.7n0nx3.rst @@ -0,0 +1,2 @@ +Removed bundled copy of the libmpdec, use system library if it's available. +Patch by Sergey B Kirpichev. diff --git a/Misc/NEWS.d/next/Build/2025-05-14-09-43-48.gh-issue-131769.H0oy5x.rst b/Misc/NEWS.d/next/Build/2025-05-14-09-43-48.gh-issue-131769.H0oy5x.rst deleted file mode 100644 index 834b0d9f7e145d..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-05-14-09-43-48.gh-issue-131769.H0oy5x.rst +++ /dev/null @@ -1 +0,0 @@ -Fix detecting when the build Python in a cross-build is a pydebug build. diff --git a/Misc/NEWS.d/next/Build/2025-05-16-07-46-06.gh-issue-115119.ALBgS_.rst b/Misc/NEWS.d/next/Build/2025-05-16-07-46-06.gh-issue-115119.ALBgS_.rst deleted file mode 100644 index 8c2d15a3228fcf..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-05-16-07-46-06.gh-issue-115119.ALBgS_.rst +++ /dev/null @@ -1,4 +0,0 @@ -Removed implicit fallback to the bundled copy of the ``libmpdec`` library. -Now this should be explicitly enabled via :option:`--with-system-libmpdec` -set to ``no`` or :option:`!--without-system-libmpdec`. Patch by Sergey -B Kirpichev. diff --git a/Misc/NEWS.d/next/Build/2025-05-19-18-09-20.gh-issue-134273.ZAliyy.rst b/Misc/NEWS.d/next/Build/2025-05-19-18-09-20.gh-issue-134273.ZAliyy.rst deleted file mode 100644 index 3eb13cefbe6dfc..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-05-19-18-09-20.gh-issue-134273.ZAliyy.rst +++ /dev/null @@ -1 +0,0 @@ -Add support for configuring compiler flags for the JIT with ``CFLAGS_JIT`` diff --git a/Misc/NEWS.d/next/Build/2025-05-21-19-46-28.gh-issue-134455.vdwlrq.rst b/Misc/NEWS.d/next/Build/2025-05-21-19-46-28.gh-issue-134455.vdwlrq.rst deleted file mode 100644 index 08833b3344f20b..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-05-21-19-46-28.gh-issue-134455.vdwlrq.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed ``build-details.json`` generation to use the correct ``c_api.headers`` -as defined in :pep:`739`, instead of ``c_api.include``. diff --git a/Misc/NEWS.d/next/Build/2025-05-21-22-13-30.gh-issue-134486.yvdL6f.rst b/Misc/NEWS.d/next/Build/2025-05-21-22-13-30.gh-issue-134486.yvdL6f.rst deleted file mode 100644 index 2754e61f0182b5..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-05-21-22-13-30.gh-issue-134486.yvdL6f.rst +++ /dev/null @@ -1,3 +0,0 @@ -The :mod:`ctypes` module now performs a more portable test for the -definition of :manpage:`alloca(3)`, fixing a compilation failure on -NetBSD. diff --git a/Misc/NEWS.d/next/Build/2025-05-24-16-59-20.gh-issue-134632.i0W2hc.rst b/Misc/NEWS.d/next/Build/2025-05-24-16-59-20.gh-issue-134632.i0W2hc.rst deleted file mode 100644 index f41c8744b8aaa0..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-05-24-16-59-20.gh-issue-134632.i0W2hc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed ``build-details.json`` generation to use ``INCLUDEPY``, in order to -reference the ``pythonX.Y`` subdirectory of the include directory, as -required in :pep:`739`, instead of the top-level include directory. diff --git a/Misc/NEWS.d/next/Build/2025-05-30-11-02-30.gh-issue-134923.gBkRg4.rst b/Misc/NEWS.d/next/Build/2025-05-30-11-02-30.gh-issue-134923.gBkRg4.rst deleted file mode 100644 index a742a6add8ae93..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-05-30-11-02-30.gh-issue-134923.gBkRg4.rst +++ /dev/null @@ -1,3 +0,0 @@ -Windows builds with profile-guided optimization enabled now use -``/GENPROFILE`` and ``/USEPROFILE`` instead of deprecated ``/LTCG:`` -options. diff --git a/Misc/NEWS.d/next/Build/2025-06-14-10-32-11.gh-issue-135497.ajlV4F.rst b/Misc/NEWS.d/next/Build/2025-06-14-10-32-11.gh-issue-135497.ajlV4F.rst deleted file mode 100644 index c84663b14668d6..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-06-14-10-32-11.gh-issue-135497.ajlV4F.rst +++ /dev/null @@ -1 +0,0 @@ -Fix the detection of ``MAXLOGNAME`` in the ``configure.ac`` script. diff --git a/Misc/NEWS.d/next/Build/2025-06-16-07-20-28.gh-issue-119132.fcI8s7.rst b/Misc/NEWS.d/next/Build/2025-06-16-07-20-28.gh-issue-119132.fcI8s7.rst deleted file mode 100644 index 3eb0805b9ce1be..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-06-16-07-20-28.gh-issue-119132.fcI8s7.rst +++ /dev/null @@ -1 +0,0 @@ -Remove "experimental" tag from the CPython free-threading build. diff --git a/Misc/NEWS.d/next/Build/2025-06-25-13-27-14.gh-issue-135927.iCNPQc.rst b/Misc/NEWS.d/next/Build/2025-06-25-13-27-14.gh-issue-135927.iCNPQc.rst deleted file mode 100644 index 21a2c87d3448c1..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-06-25-13-27-14.gh-issue-135927.iCNPQc.rst +++ /dev/null @@ -1 +0,0 @@ -Fix building with MSVC when passing option ``/std:clatest``. diff --git a/Misc/NEWS.d/next/Build/2025-07-18-17-15-00.gh-issue-135621.9cyCNb.rst b/Misc/NEWS.d/next/Build/2025-07-18-17-15-00.gh-issue-135621.9cyCNb.rst deleted file mode 100644 index fe7f962ccbb096..00000000000000 --- a/Misc/NEWS.d/next/Build/2025-07-18-17-15-00.gh-issue-135621.9cyCNb.rst +++ /dev/null @@ -1,2 +0,0 @@ -PyREPL no longer depends on the :mod:`curses` standard library. Contributed -by Łukasz Langa. diff --git a/Misc/NEWS.d/next/Build/2026-04-21-17-27-52.gh-issue-148832.Kx9aQ1.rst b/Misc/NEWS.d/next/Build/2026-04-21-17-27-52.gh-issue-148832.Kx9aQ1.rst new file mode 100644 index 00000000000000..867cfcc5ab62ca --- /dev/null +++ b/Misc/NEWS.d/next/Build/2026-04-21-17-27-52.gh-issue-148832.Kx9aQ1.rst @@ -0,0 +1,4 @@ +Fix :option:`--enable-bolt` build by switching the default BOLT flag from +``-icf=1`` to ``-icf=0``. ``-icf=1`` folds address-taken functions and +breaks type-slot dispatch, crashing on :mod:`list` and :mod:`tuple` +concatenation. Patched by Shamil Abdulaev. diff --git a/Misc/NEWS.d/next/Build/2026-05-18-16-00-41.gh-issue-148260.UwFiIX.rst b/Misc/NEWS.d/next/Build/2026-05-18-16-00-41.gh-issue-148260.UwFiIX.rst new file mode 100644 index 00000000000000..8248c24cbd511a --- /dev/null +++ b/Misc/NEWS.d/next/Build/2026-05-18-16-00-41.gh-issue-148260.UwFiIX.rst @@ -0,0 +1,3 @@ +On Linux when Python is linked to the musl C library, use a thread stack +size of at least 1 MiB instead of musl default which is 128 kiB. Patch by +Victor Stinner. diff --git a/Misc/NEWS.d/next/Build/2026-05-19-12-45-23.gh-issue-131372.oJykeB.rst b/Misc/NEWS.d/next/Build/2026-05-19-12-45-23.gh-issue-131372.oJykeB.rst new file mode 100644 index 00000000000000..912f4131af27da --- /dev/null +++ b/Misc/NEWS.d/next/Build/2026-05-19-12-45-23.gh-issue-131372.oJykeB.rst @@ -0,0 +1,3 @@ +Add a :option:`--with-build-details-suffix` configure flag to allow Linux +distributions that co-install multiple versions of Python in the same tree +to avoid ``build-details.json`` clashes. diff --git a/Misc/NEWS.d/next/Build/2026-05-21-15-14-59.gh-issue-148294.VtFaW4.rst b/Misc/NEWS.d/next/Build/2026-05-21-15-14-59.gh-issue-148294.VtFaW4.rst new file mode 100644 index 00000000000000..861261dd97269f --- /dev/null +++ b/Misc/NEWS.d/next/Build/2026-05-21-15-14-59.gh-issue-148294.VtFaW4.rst @@ -0,0 +1,2 @@ +Corrected the use of ``AC_PATH_TOOL`` in ``configure.ac`` to allow a C++ +compiler to be found on :envvar:`!PATH`. diff --git a/Misc/NEWS.d/next/Build/2026-05-27-14-28-36.gh-issue-115119.wotFWh.rst b/Misc/NEWS.d/next/Build/2026-05-27-14-28-36.gh-issue-115119.wotFWh.rst new file mode 100644 index 00000000000000..04fdf3617f0cde --- /dev/null +++ b/Misc/NEWS.d/next/Build/2026-05-27-14-28-36.gh-issue-115119.wotFWh.rst @@ -0,0 +1 @@ +Fix the detection of libmpdec header when no .pc files are available. diff --git a/Misc/NEWS.d/next/Build/2026-06-09-11-54-13.gh-issue-151163.vFAtjv.rst b/Misc/NEWS.d/next/Build/2026-06-09-11-54-13.gh-issue-151163.vFAtjv.rst new file mode 100644 index 00000000000000..e4f3a044c81c6f --- /dev/null +++ b/Misc/NEWS.d/next/Build/2026-06-09-11-54-13.gh-issue-151163.vFAtjv.rst @@ -0,0 +1 @@ +Updated Android build to include SQLite version 3.53.2. diff --git a/Misc/NEWS.d/next/C_API/2023-10-18-14-36-35.gh-issue-108512.fMZLfr.rst b/Misc/NEWS.d/next/C_API/2023-10-18-14-36-35.gh-issue-108512.fMZLfr.rst deleted file mode 100644 index 279e588f3adcb7..00000000000000 --- a/Misc/NEWS.d/next/C_API/2023-10-18-14-36-35.gh-issue-108512.fMZLfr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add functions :c:func:`PySys_GetAttr`, :c:func:`PySys_GetAttrString`, -:c:func:`PySys_GetOptionalAttr` and :c:func:`PySys_GetOptionalAttrString`. diff --git a/Misc/NEWS.d/next/C_API/2025-04-14-07-41-28.gh-issue-131185.ZCjMHD.rst b/Misc/NEWS.d/next/C_API/2025-04-14-07-41-28.gh-issue-131185.ZCjMHD.rst deleted file mode 100644 index aa0e8bca93b46f..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-04-14-07-41-28.gh-issue-131185.ZCjMHD.rst +++ /dev/null @@ -1,2 +0,0 @@ -:c:func:`PyGILState_Ensure` no longer crashes when called after interpreter -finalization. diff --git a/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst b/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst deleted file mode 100644 index 38b7a0a493e39f..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-04-17-12-37-27.gh-issue-132629.01ArwX.rst +++ /dev/null @@ -1,4 +0,0 @@ -For unsigned integer formats in :c:func:`PyArg_ParseTuple`, accepting Python -integers with value that is larger than the maximal value for the C type or -less than the minimal value for the corresponding signed integer type -of the same size is now deprecated. diff --git a/Misc/NEWS.d/next/C_API/2025-05-07-21-18-00.gh-issue-133610.asdfjs.rst b/Misc/NEWS.d/next/C_API/2025-05-07-21-18-00.gh-issue-133610.asdfjs.rst deleted file mode 100644 index bdc53331f6aa62..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-05-07-21-18-00.gh-issue-133610.asdfjs.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove deprecated functions :c:func:`!PyUnicode_AsDecodedObject`, -:c:func:`!PyUnicode_AsDecodedUnicode`, :c:func:`!PyUnicode_AsEncodedObject`, -and :c:func:`!PyUnicode_AsEncodedUnicode`. diff --git a/Misc/NEWS.d/next/C_API/2025-05-08-12-25-47.gh-issue-133644.Yb86Rm.rst b/Misc/NEWS.d/next/C_API/2025-05-08-12-25-47.gh-issue-133644.Yb86Rm.rst deleted file mode 100644 index 9569456eb760c2..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-05-08-12-25-47.gh-issue-133644.Yb86Rm.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove deprecated alias :c:func:`!PyImport_ImportModuleNoBlock` of -:c:func:`PyImport_ImportModule`. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/C_API/2025-05-08-13-14-45.gh-issue-133644.J8_KZ2.rst b/Misc/NEWS.d/next/C_API/2025-05-08-13-14-45.gh-issue-133644.J8_KZ2.rst deleted file mode 100644 index a9275e81112058..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-05-08-13-14-45.gh-issue-133644.J8_KZ2.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove deprecated Python initialization getter functions ``Py_Get*``. -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/C_API/2025-05-13-16-06-46.gh-issue-133968.6alWst.rst b/Misc/NEWS.d/next/C_API/2025-05-13-16-06-46.gh-issue-133968.6alWst.rst deleted file mode 100644 index 47d5a3bda39942..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-05-13-16-06-46.gh-issue-133968.6alWst.rst +++ /dev/null @@ -1,4 +0,0 @@ -Add :c:func:`PyUnicodeWriter_WriteASCII` function to write an ASCII string -into a :c:type:`PyUnicodeWriter`. The function is faster than -:c:func:`PyUnicodeWriter_WriteUTF8`, but has an undefined behavior if the -input string contains non-ASCII characters. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C_API/2025-05-17-14-41-21.gh-issue-134144.xVpZik.rst b/Misc/NEWS.d/next/C_API/2025-05-17-14-41-21.gh-issue-134144.xVpZik.rst deleted file mode 100644 index 11c7bd59a4dd1d..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-05-17-14-41-21.gh-issue-134144.xVpZik.rst +++ /dev/null @@ -1 +0,0 @@ -Fix crash when calling :c:func:`Py_EndInterpreter` with a :term:`thread state` that isn't the initial thread for the interpreter. diff --git a/Misc/NEWS.d/next/C_API/2025-05-20-17-13-51.gh-issue-134009.CpCmry.rst b/Misc/NEWS.d/next/C_API/2025-05-20-17-13-51.gh-issue-134009.CpCmry.rst deleted file mode 100644 index f060f09de19bb8..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-05-20-17-13-51.gh-issue-134009.CpCmry.rst +++ /dev/null @@ -1 +0,0 @@ -Expose :c:func:`PyMutex_IsLocked` as part of the public C API. diff --git a/Misc/NEWS.d/next/C_API/2025-05-29-16-56-23.gh-issue-134891.7eKO8U.rst b/Misc/NEWS.d/next/C_API/2025-05-29-16-56-23.gh-issue-134891.7eKO8U.rst deleted file mode 100644 index db30d5e9a94584..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-05-29-16-56-23.gh-issue-134891.7eKO8U.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :c:type:`PyUnstable_Unicode_GET_CACHED_HASH` to get the cached hash of a -string. diff --git a/Misc/NEWS.d/next/C_API/2025-05-30-11-33-17.gh-issue-134745.GN-zk2.rst b/Misc/NEWS.d/next/C_API/2025-05-30-11-33-17.gh-issue-134745.GN-zk2.rst deleted file mode 100644 index a85d2e90576a49..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-05-30-11-33-17.gh-issue-134745.GN-zk2.rst +++ /dev/null @@ -1,3 +0,0 @@ -Change :c:func:`!PyThread_allocate_lock` implementation to ``PyMutex``. -On Windows, :c:func:`!PyThread_acquire_lock_timed` now supports the *intr_flag* -parameter: it can be interrupted. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C_API/2025-06-02-13-19-22.gh-issue-134989.sDDyBN.rst b/Misc/NEWS.d/next/C_API/2025-06-02-13-19-22.gh-issue-134989.sDDyBN.rst deleted file mode 100644 index e49f765106582e..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-06-02-13-19-22.gh-issue-134989.sDDyBN.rst +++ /dev/null @@ -1,2 +0,0 @@ -Implement :c:func:`PyObject_DelAttr` and :c:func:`PyObject_DelAttrString` as -macros in the limited C API 3.12 and older. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C_API/2025-06-05-11-06-07.gh-issue-134989.74p4ud.rst b/Misc/NEWS.d/next/C_API/2025-06-05-11-06-07.gh-issue-134989.74p4ud.rst deleted file mode 100644 index 844e9a666640c6..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-06-05-11-06-07.gh-issue-134989.74p4ud.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix ``Py_RETURN_NONE``, ``Py_RETURN_TRUE`` and ``Py_RETURN_FALSE`` macros in -the limited C API 3.11 and older: don't treat ``Py_None``, ``Py_True`` and -``Py_False`` as immortal. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C_API/2025-06-19-12-47-18.gh-issue-133157.1WA85f.rst b/Misc/NEWS.d/next/C_API/2025-06-19-12-47-18.gh-issue-133157.1WA85f.rst deleted file mode 100644 index 1b37d884e57273..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-06-19-12-47-18.gh-issue-133157.1WA85f.rst +++ /dev/null @@ -1 +0,0 @@ -Remove the private, undocumented macro :c:macro:`!_Py_NO_SANITIZE_UNDEFINED`. diff --git a/Misc/NEWS.d/next/C_API/2025-06-24-11-10-01.gh-issue-133296.lIEuVJ.rst b/Misc/NEWS.d/next/C_API/2025-06-24-11-10-01.gh-issue-133296.lIEuVJ.rst deleted file mode 100644 index 41401911a6bc0b..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-06-24-11-10-01.gh-issue-133296.lIEuVJ.rst +++ /dev/null @@ -1,3 +0,0 @@ -New variants for the critical section API that accept one or two -:c:type:`PyMutex` pointers rather than :c:type:`PyObject` instances are now -public in the non-limited C API. diff --git a/Misc/NEWS.d/next/C_API/2025-06-25-01-03-10.gh-issue-135906.UBrCWq.rst b/Misc/NEWS.d/next/C_API/2025-06-25-01-03-10.gh-issue-135906.UBrCWq.rst deleted file mode 100644 index 7852759a702804..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-06-25-01-03-10.gh-issue-135906.UBrCWq.rst +++ /dev/null @@ -1 +0,0 @@ -Fix compilation errors when compiling the internal headers with a C++ compiler. diff --git a/Misc/NEWS.d/next/C_API/2025-07-01-16-22-39.gh-issue-135075.angu3J.rst b/Misc/NEWS.d/next/C_API/2025-07-01-16-22-39.gh-issue-135075.angu3J.rst deleted file mode 100644 index 88e0fa65f45dd0..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-07-01-16-22-39.gh-issue-135075.angu3J.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make :c:func:`PyObject_SetAttr` and :c:func:`PyObject_SetAttrString` fail if -called with ``NULL`` value and an exception set. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C_API/2025-07-22-15-18-08.gh-issue-112068.4WvT-8.rst b/Misc/NEWS.d/next/C_API/2025-07-22-15-18-08.gh-issue-112068.4WvT-8.rst deleted file mode 100644 index 018c5c7880c001..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-07-22-15-18-08.gh-issue-112068.4WvT-8.rst +++ /dev/null @@ -1 +0,0 @@ -Revert support of nullable arguments in :c:func:`PyArg_Parse`. diff --git a/Misc/NEWS.d/next/C_API/2025-07-23-22-30-23.gh-issue-136759.ffB4wO.rst b/Misc/NEWS.d/next/C_API/2025-07-23-22-30-23.gh-issue-136759.ffB4wO.rst deleted file mode 100644 index 79819b4735ff17..00000000000000 --- a/Misc/NEWS.d/next/C_API/2025-07-23-22-30-23.gh-issue-136759.ffB4wO.rst +++ /dev/null @@ -1 +0,0 @@ -Rename ``lock.h`` to ``pylock.h`` to avoid potential include conflicts. diff --git a/Misc/NEWS.d/next/C_API/2026-02-25-13-37-10.gh-issue-145235.-1ySNR.rst b/Misc/NEWS.d/next/C_API/2026-02-25-13-37-10.gh-issue-145235.-1ySNR.rst new file mode 100644 index 00000000000000..98a8c268735726 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-02-25-13-37-10.gh-issue-145235.-1ySNR.rst @@ -0,0 +1,3 @@ +Made :c:func:`PyDict_AddWatcher`, :c:func:`PyDict_ClearWatcher`, +:c:func:`PyDict_Watch`, and :c:func:`PyDict_Unwatch` thread-safe on the +:term:`free threaded <free threading>` build. diff --git a/Misc/NEWS.d/next/C_API/2026-05-12-16-47-21.gh-issue-149725.HZLBTZ.rst b/Misc/NEWS.d/next/C_API/2026-05-12-16-47-21.gh-issue-149725.HZLBTZ.rst new file mode 100644 index 00000000000000..97721430edbd69 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-05-12-16-47-21.gh-issue-149725.HZLBTZ.rst @@ -0,0 +1,2 @@ +Add :c:func:`PySentinel_CheckExact` for exact :class:`sentinel` type tests +to accompany the existing :c:func:`PySentinel_Check`. diff --git a/Misc/NEWS.d/next/C_API/2026-05-31-15-39-58.gh-issue-150671.bTkDVC.rst b/Misc/NEWS.d/next/C_API/2026-05-31-15-39-58.gh-issue-150671.bTkDVC.rst new file mode 100644 index 00000000000000..a67a0f85db1a50 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-05-31-15-39-58.gh-issue-150671.bTkDVC.rst @@ -0,0 +1,4 @@ +Deprecate these C-API functions: :c:func:`PyGen_New`, +:c:func:`PyGen_NewWithQualName`, :c:func:`PyCoro_New`, and +:c:func:`PyAsyncGen_New`. +Schedule them for removal in 3.18 diff --git a/Misc/NEWS.d/next/C_API/2026-06-04-14-26-17.gh-issue-150907.CA91_B.rst b/Misc/NEWS.d/next/C_API/2026-06-04-14-26-17.gh-issue-150907.CA91_B.rst new file mode 100644 index 00000000000000..f58b248f3a0b98 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-06-04-14-26-17.gh-issue-150907.CA91_B.rst @@ -0,0 +1,2 @@ +Fix ``dynamic_annotations.h`` header file when built with C++ and Valgrind: +add ``extern "C++" scope`` for the C++ template. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/C_API/2026-06-09-14-50-35.gh-issue-80384.UEGyvB.rst b/Misc/NEWS.d/next/C_API/2026-06-09-14-50-35.gh-issue-80384.UEGyvB.rst new file mode 100644 index 00000000000000..c4f5e8f3f9bbc2 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-06-09-14-50-35.gh-issue-80384.UEGyvB.rst @@ -0,0 +1,3 @@ +:c:func:`PyWeakref_NewRef` and :c:func:`PyWeakref_NewProxy` now raise +:exc:`TypeError` if the *callback* argument is not callable, ``None``, or +``NULL``. diff --git a/Misc/NEWS.d/next/C_API/2026-06-10-16-43-37.gh-issue-123619.dV82r6.rst b/Misc/NEWS.d/next/C_API/2026-06-10-16-43-37.gh-issue-123619.dV82r6.rst new file mode 100644 index 00000000000000..4d4c94563330c0 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-06-10-16-43-37.gh-issue-123619.dV82r6.rst @@ -0,0 +1,3 @@ +:c:func:`PyUnstable_Object_EnableDeferredRefcount` now returns ``0`` if the +object is not tracked by the garbage collector: if :func:`gc.is_tracked` is +false. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2019-12-27-19-20-33.bpo-38131.J5fv54.rst b/Misc/NEWS.d/next/Core_and_Builtins/2019-12-27-19-20-33.bpo-38131.J5fv54.rst new file mode 100644 index 00000000000000..8b606c4335e033 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2019-12-27-19-20-33.bpo-38131.J5fv54.rst @@ -0,0 +1,2 @@ +Produce more meaningful messages when compiling AST objects with wrong field +values. Patch by Batuhan Taskaya. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-03-14-13-08-20.gh-issue-127266._tyfBp.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-03-14-13-08-20.gh-issue-127266._tyfBp.rst deleted file mode 100644 index b26977628de136..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-03-14-13-08-20.gh-issue-127266._tyfBp.rst +++ /dev/null @@ -1,6 +0,0 @@ -In the free-threaded build, avoid data races caused by updating type slots -or type flags after the type was initially created. For those (typically -rare) cases, use the stop-the-world mechanism. Remove the use of atomics -when reading or writing type flags. The use of atomics is not sufficient to -avoid races (since flags are sometimes read without a lock and without -atomics) and are no longer required. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-04-16-41-00.gh-issue-133379.asdjhjdf.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-04-16-41-00.gh-issue-133379.asdjhjdf.rst deleted file mode 100644 index cf2e1e4eaff779..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-04-16-41-00.gh-issue-133379.asdjhjdf.rst +++ /dev/null @@ -1 +0,0 @@ -Correct usage of *arguments* in error messages. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-16-12-01-13.gh-issue-127971.pMDOQ0.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-16-12-01-13.gh-issue-127971.pMDOQ0.rst deleted file mode 100644 index ced7a9c9fd3e63..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-16-12-01-13.gh-issue-127971.pMDOQ0.rst +++ /dev/null @@ -1 +0,0 @@ -Fix off-by-one read beyond the end of a string in string search. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-19-16-22-47.gh-issue-132732.jgqhlF.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-19-16-22-47.gh-issue-132732.jgqhlF.rst deleted file mode 100644 index aadaf2169fd01a..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-19-16-22-47.gh-issue-132732.jgqhlF.rst +++ /dev/null @@ -1 +0,0 @@ -Automatically constant evaluate bytecode operations marked as pure in the JIT optimizer. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-19-17-16-46.gh-issue-132542.7T_TY_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-19-17-16-46.gh-issue-132542.7T_TY_.rst deleted file mode 100644 index c69ce5efdedcca..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-19-17-16-46.gh-issue-132542.7T_TY_.rst +++ /dev/null @@ -1,2 +0,0 @@ -Update :attr:`Thread.native_id <threading.Thread.native_id>` after -:manpage:`fork(2)` to ensure accuracy. Patch by Noam Cohen. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-17-50-01.gh-issue-131798.XiOgw5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-17-50-01.gh-issue-131798.XiOgw5.rst deleted file mode 100644 index 45ab1bea6b1dd7..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-26-17-50-01.gh-issue-131798.XiOgw5.rst +++ /dev/null @@ -1,2 +0,0 @@ -Narrow the return type and constant-evaluate ``CALL_ISINSTANCE`` for a -subset of known values in the JIT. Patch by Tomas Roun diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-30-14-13-01.gh-issue-132554.GqQaUp.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-04-30-14-13-01.gh-issue-132554.GqQaUp.rst deleted file mode 100644 index bfe2d633309191..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-04-30-14-13-01.gh-issue-132554.GqQaUp.rst +++ /dev/null @@ -1,4 +0,0 @@ -Change iteration to use "virtual iterators" for sequences. Instead of -creating an iterator, a tagged integer representing the next index is pushed -to the stack above the iterable. For non-sequence iterators, ``NULL`` is -pushed. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-03-13-36-01.gh-issue-131798.U4_QEJ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-03-13-36-01.gh-issue-131798.U4_QEJ.rst deleted file mode 100644 index ca8eb999ae5ef0..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-03-13-36-01.gh-issue-131798.U4_QEJ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Split ``CALL_ISINSTANCE`` into several uops, allowing the JIT to remove some -of them. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-03-22-31-53.gh-issue-131798.fQ0ato.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-03-22-31-53.gh-issue-131798.fQ0ato.rst deleted file mode 100644 index f322d43b30a9f3..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-03-22-31-53.gh-issue-131798.fQ0ato.rst +++ /dev/null @@ -1,2 +0,0 @@ -Allow the JIT to remove int guards after ``_GET_LEN`` by setting the return -type to int. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-06-15-01-41.gh-issue-133516.RqWVf2.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-06-15-01-41.gh-issue-133516.RqWVf2.rst deleted file mode 100644 index b93ba11f93252b..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-06-15-01-41.gh-issue-133516.RqWVf2.rst +++ /dev/null @@ -1,2 +0,0 @@ -Raise :exc:`ValueError` when constants ``True``, ``False`` or ``None`` are -used as an identifier after NFKC normalization. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-07-23-26-53.gh-issue-133541.bHIC55.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-07-23-26-53.gh-issue-133541.bHIC55.rst deleted file mode 100644 index 4f4cd847fa51fb..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-07-23-26-53.gh-issue-133541.bHIC55.rst +++ /dev/null @@ -1,2 +0,0 @@ -Inconsistent indentation in user input crashed the new REPL when syntax -highlighting was active. This is now fixed. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-08-13-48-02.gh-issue-132762.tKbygC.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-08-13-48-02.gh-issue-132762.tKbygC.rst deleted file mode 100644 index 80b830ebd786f3..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-08-13-48-02.gh-issue-132762.tKbygC.rst +++ /dev/null @@ -1 +0,0 @@ -:meth:`~dict.fromkeys` no longer loops forever when adding a small set of keys to a large base dict. Patch by Angela Liss. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-08-22-19-10.gh-issue-133711.e91wUy.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-08-22-19-10.gh-issue-133711.e91wUy.rst deleted file mode 100644 index c8d3d62763dc12..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-08-22-19-10.gh-issue-133711.e91wUy.rst +++ /dev/null @@ -1,2 +0,0 @@ -Implement :pep:`686`: Enable :ref:`Python UTF-8 Mode <utf8-mode>` by -default. Patch by Adam Turner. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-09-18-11-21.gh-issue-133778.pWEV3t.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-09-18-11-21.gh-issue-133778.pWEV3t.rst deleted file mode 100644 index 6eb6881213c434..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-09-18-11-21.gh-issue-133778.pWEV3t.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix bug where assigning to the :attr:`~type.__annotations__` attributes of -classes defined under ``from __future__ import annotations`` had no effect. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-10-17-12-27.gh-issue-133703.bVM-re.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-10-17-12-27.gh-issue-133703.bVM-re.rst deleted file mode 100644 index 05bf6103314a74..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-10-17-12-27.gh-issue-133703.bVM-re.rst +++ /dev/null @@ -1 +0,0 @@ -Fix hashtable in dict can be bigger than intended in some situations. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-11-13-40-42.gh-issue-133886.ryBAyo.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-11-13-40-42.gh-issue-133886.ryBAyo.rst deleted file mode 100644 index fd1020f05d6b0d..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-11-13-40-42.gh-issue-133886.ryBAyo.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :func:`sys.remote_exec` for non-ASCII paths in non-UTF-8 locales and -non-UTF-8 paths in UTF-8 locales. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-15-11-38-16.gh-issue-133999.uBZ8uS.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-15-11-38-16.gh-issue-133999.uBZ8uS.rst deleted file mode 100644 index 7d9c49688c3ce2..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-15-11-38-16.gh-issue-133999.uBZ8uS.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :exc:`SyntaxError` regression in :keyword:`except` parsing after -:gh:`123440`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-09-06-38.gh-issue-134036.st2e-B.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-09-06-38.gh-issue-134036.st2e-B.rst deleted file mode 100644 index 176aab1c93ab83..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-09-06-38.gh-issue-134036.st2e-B.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve :exc:`SyntaxError` message when using invalid :keyword:`raise` -statements. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-17-25-52.gh-issue-134100.5-FbLK.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-17-25-52.gh-issue-134100.5-FbLK.rst deleted file mode 100644 index d672347f9ad246..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-17-25-52.gh-issue-134100.5-FbLK.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a use-after-free bug that occurs when an imported module isn't -in :data:`sys.modules` after its initial import. Patch by Nico-Posada. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-20-59-12.gh-issue-134119.w8expI.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-20-59-12.gh-issue-134119.w8expI.rst deleted file mode 100644 index 754e816628563e..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-16-20-59-12.gh-issue-134119.w8expI.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash when calling :func:`next` on an exhausted template string iterator. -Patch by Jelle Zijlstra. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-44-51.gh-issue-134158.ewLNLp.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-44-51.gh-issue-134158.ewLNLp.rst deleted file mode 100644 index 7b8bab739c37c8..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-44-51.gh-issue-134158.ewLNLp.rst +++ /dev/null @@ -1 +0,0 @@ -Fix coloring of double braces in f-strings and t-strings in the :term:`REPL`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-56-05.gh-issue-91153.afgtG2.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-56-05.gh-issue-91153.afgtG2.rst deleted file mode 100644 index dc2f1e22ba5b05..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-17-20-56-05.gh-issue-91153.afgtG2.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a crash when a :class:`bytearray` is concurrently mutated during item assignment. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-18-14-33-23.gh-issue-69605.ZMO49F.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-18-14-33-23.gh-issue-69605.ZMO49F.rst deleted file mode 100644 index 7b7275fee69b9b..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-18-14-33-23.gh-issue-69605.ZMO49F.rst +++ /dev/null @@ -1,2 +0,0 @@ -When auto-completing an import in the :term:`REPL`, finding no candidates -now issues no suggestion, rather than suggestions from the current namespace. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-15-15-58.gh-issue-131798.PCP71j.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-15-15-58.gh-issue-131798.PCP71j.rst deleted file mode 100644 index c816a0afad4166..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-15-15-58.gh-issue-131798.PCP71j.rst +++ /dev/null @@ -1 +0,0 @@ -Split ``CALL_LIST_APPEND`` into several uops. Patch by Diego Russo. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst deleted file mode 100644 index 98d770cf054926..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-19-20-52-53.gh-issue-134268.HPKX1e.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add ``_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW`` and use it to further -optimize ``CALL_ISINSTANCE``. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-13-58-18.gh-issue-131798.hG8xBw.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-13-58-18.gh-issue-131798.hG8xBw.rst deleted file mode 100644 index c490ecf15605b2..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-13-58-18.gh-issue-131798.hG8xBw.rst +++ /dev/null @@ -1 +0,0 @@ -Improve the JIT's ability to narrow unknown classes to constant values. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-14-41-50.gh-issue-128066.qzzGfv.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-14-41-50.gh-issue-128066.qzzGfv.rst deleted file mode 100644 index f78190276851b4..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-14-41-50.gh-issue-128066.qzzGfv.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixes an edge case where PyREPL improperly threw an error when Python is -invoked on a read only filesystem while trying to write history file -entries. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-23-32-11.gh-issue-131798.G9ZQZw.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-23-32-11.gh-issue-131798.G9ZQZw.rst deleted file mode 100644 index 8eb8782037abfa..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-20-23-32-11.gh-issue-131798.G9ZQZw.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve the JIT's ability to optimize away cached class attribute and method -loads. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-13-57-26.gh-issue-131798.QwS5Bb.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-13-57-26.gh-issue-131798.QwS5Bb.rst deleted file mode 100644 index f873bbfb4dcb68..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-13-57-26.gh-issue-131798.QwS5Bb.rst +++ /dev/null @@ -1 +0,0 @@ -JIT: replace ``_LOAD_SMALL_INT`` with ``_LOAD_CONST_INLINE_BORROW`` diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-15-14-32.gh-issue-130397.aG6EON.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-15-14-32.gh-issue-130397.aG6EON.rst deleted file mode 100644 index 34a2f4d1278e74..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-15-14-32.gh-issue-130397.aG6EON.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove special-casing for C stack depth limits for WASI. Due to -WebAssembly's built-in stack protection this does not pose a security -concern. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-18-02-56.gh-issue-127960.W3J_2X.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-18-02-56.gh-issue-127960.W3J_2X.rst deleted file mode 100644 index 730d8a5af51f54..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-21-18-02-56.gh-issue-127960.W3J_2X.rst +++ /dev/null @@ -1,3 +0,0 @@ -PyREPL interactive shell no longer starts with ``__package__`` and -``__file__`` global names set to ``_pyrepl`` package internals. Contributed -by Yuichiro Tachibana. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-14-48-19.gh-issue-134381.2BXhth.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-14-48-19.gh-issue-134381.2BXhth.rst deleted file mode 100644 index aa8900296ae2fc..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-14-48-19.gh-issue-134381.2BXhth.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :exc:`RuntimeError` when using a not-started :class:`threading.Thread` after calling :func:`os.fork` diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-17-49-39.gh-issue-131798.U6ZmFm.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-17-49-39.gh-issue-131798.U6ZmFm.rst deleted file mode 100644 index fdb6a2fe0b383b..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-22-17-49-39.gh-issue-131798.U6ZmFm.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize ``_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW``. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-23-14-54-07.gh-issue-134584.y-WDjf.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-23-14-54-07.gh-issue-134584.y-WDjf.rst deleted file mode 100644 index 5f9e1553ae7ca5..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-23-14-54-07.gh-issue-134584.y-WDjf.rst +++ /dev/null @@ -1 +0,0 @@ -Add a reference count elimination pass to the JIT compiler. Patch by Ken Jin. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-25-19-32-15.gh-issue-131798.f5h8aI.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-25-19-32-15.gh-issue-131798.f5h8aI.rst deleted file mode 100644 index 6ecbfb8d9cf7df..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-25-19-32-15.gh-issue-131798.f5h8aI.rst +++ /dev/null @@ -1 +0,0 @@ -Make the JIT optimizer understand that slicing a string/list/tuple returns the same type. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-26-15-55-50.gh-issue-133912.-xAguL.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-26-15-55-50.gh-issue-133912.-xAguL.rst deleted file mode 100644 index 2118f3d0c350ec..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-26-15-55-50.gh-issue-133912.-xAguL.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix the C API function ``PyObject_GenericSetDict`` to handle extension -classes with inline values. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-18-59-54.gh-issue-134679.FWPBu6.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-18-59-54.gh-issue-134679.FWPBu6.rst deleted file mode 100644 index 22f1282fea13e9..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-18-59-54.gh-issue-134679.FWPBu6.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix crash in the :term:`free threading` build's QSBR code that could occur -when changing an object's ``__dict__`` attribute. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-20-21-34.gh-issue-131798.b32zkl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-20-21-34.gh-issue-131798.b32zkl.rst deleted file mode 100644 index ed4b31bd7bedce..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-20-21-34.gh-issue-131798.b32zkl.rst +++ /dev/null @@ -1 +0,0 @@ -Allow the JIT to remove unnecessary ``_ITER_CHECK_TUPLE`` ops. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-20-29-00.gh-issue-132617.EmUfQQ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-20-29-00.gh-issue-132617.EmUfQQ.rst deleted file mode 100644 index 53aef541e64c23..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-27-20-29-00.gh-issue-132617.EmUfQQ.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix :meth:`dict.update` modification check that could incorrectly raise a -"dict mutated during update" error when a different dictionary was modified -that happens to share the same underlying keys object. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-28-23-58-50.gh-issue-117852.BO9g7z.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-28-23-58-50.gh-issue-117852.BO9g7z.rst deleted file mode 100644 index fc71cd21a367b2..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-28-23-58-50.gh-issue-117852.BO9g7z.rst +++ /dev/null @@ -1 +0,0 @@ -Fix argument checking of :meth:`~agen.athrow`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-30-15-56-19.gh-issue-134908.3a7PxM.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-30-15-56-19.gh-issue-134908.3a7PxM.rst deleted file mode 100644 index 3178f0aaf885f8..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-30-15-56-19.gh-issue-134908.3a7PxM.rst +++ /dev/null @@ -1 +0,0 @@ -Fix crash when iterating over lines in a text file on the :term:`free threaded <free threading>` build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-30-18-09-54.gh-issue-134889.Ic9UM-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-30-18-09-54.gh-issue-134889.Ic9UM-.rst deleted file mode 100644 index 3b86134bf16800..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-30-18-09-54.gh-issue-134889.Ic9UM-.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix handling of a few opcodes that leave operands on the stack when -optimizing ``LOAD_FAST``. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-31-10-26-46.gh-issue-134876.8mBGJI.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-31-10-26-46.gh-issue-134876.8mBGJI.rst deleted file mode 100644 index 1da76561469a41..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-31-10-26-46.gh-issue-134876.8mBGJI.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add support to :pep:`768` remote debugging for Linux kernels which don't -have CONFIG_CROSS_MEMORY_ATTACH configured. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-31-19-24-54.gh-issue-134280.NDVbzY.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-31-19-24-54.gh-issue-134280.NDVbzY.rst deleted file mode 100644 index f822721690975a..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-31-19-24-54.gh-issue-134280.NDVbzY.rst +++ /dev/null @@ -1,2 +0,0 @@ -Disable constant folding for ``~`` with a boolean argument. -This moves the deprecation warning from compile time to runtime. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-13-57-40.gh-issue-116738.ycJsL8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-13-57-40.gh-issue-116738.ycJsL8.rst deleted file mode 100644 index 506eefdb21aa9a..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-13-57-40.gh-issue-116738.ycJsL8.rst +++ /dev/null @@ -1 +0,0 @@ -Make methods in :mod:`heapq` thread-safe on the :term:`free threaded <free threading>` build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst deleted file mode 100644 index 0e68c793e5e133..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-02-20-13-37.gh-issue-131798.JQRFvR.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize ``_CHECK_METHOD_VERSION`` into ``_CHECK_FUNCTION_VERSION_INLINE`` in JIT-compiled code. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-03-21-06-22.gh-issue-133136.Usnvri.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-03-21-06-22.gh-issue-133136.Usnvri.rst deleted file mode 100644 index a9501c13c95b3a..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-03-21-06-22.gh-issue-133136.Usnvri.rst +++ /dev/null @@ -1,2 +0,0 @@ -Limit excess memory usage in the :term:`free threading` build when a -large dictionary or list is resized and accessed by multiple threads. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-05-21-58-30.gh-issue-131798.nt5Ab7.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-05-21-58-30.gh-issue-131798.nt5Ab7.rst deleted file mode 100644 index e4b5f610353f94..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-05-21-58-30.gh-issue-131798.nt5Ab7.rst +++ /dev/null @@ -1,2 +0,0 @@ -Optimize away ``_CALL_TYPE_1`` in the JIT when the return type is known. -Patch by Tomas Roun diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-01-09-44.gh-issue-131798.1SuxO9.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-01-09-44.gh-issue-131798.1SuxO9.rst deleted file mode 100644 index a377526230659f..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-01-09-44.gh-issue-131798.1SuxO9.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize ``_UNARY_INVERT`` in JIT-compiled code. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-02-24-42.gh-issue-135148.r-t2sC.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-02-24-42.gh-issue-135148.r-t2sC.rst deleted file mode 100644 index 9b1f62433b45ed..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-02-24-42.gh-issue-135148.r-t2sC.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed a bug where f-string debug expressions (using =) would incorrectly -strip out parts of strings containing escaped quotes and # characters. Patch -by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-19-17-22.gh-issue-131798.XoV8Eb.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-19-17-22.gh-issue-131798.XoV8Eb.rst deleted file mode 100644 index 6a9d9c683f9a8c..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-06-19-17-22.gh-issue-131798.XoV8Eb.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize ``_UNARY_NEGATIVE`` in JIT-compiled code. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-08-14-24-29.gh-issue-131798.qfw91T.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-08-14-24-29.gh-issue-131798.qfw91T.rst deleted file mode 100644 index 7965169d46e820..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-08-14-24-29.gh-issue-131798.qfw91T.rst +++ /dev/null @@ -1 +0,0 @@ -Optimize _CALL_LEN in the JIT when the length is known. Patch by Tomas Roun diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-09-23-57-37.gh-issue-130077.MHknDB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-09-23-57-37.gh-issue-130077.MHknDB.rst deleted file mode 100644 index a7d02426b6fc13..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-09-23-57-37.gh-issue-130077.MHknDB.rst +++ /dev/null @@ -1,2 +0,0 @@ -Properly raise custom syntax errors when incorrect syntax containing names -that are prefixes of soft keywords is encountered. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-11-15-08-10.gh-issue-127319.OVGFSZ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-11-15-08-10.gh-issue-127319.OVGFSZ.rst deleted file mode 100644 index d90153c96841df..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-11-15-08-10.gh-issue-127319.OVGFSZ.rst +++ /dev/null @@ -1,3 +0,0 @@ -Set the ``allow_reuse_port`` class variable to ``False`` on the XMLRPC, -logging, and HTTP servers. This matches the behavior in prior Python -releases, which is to not allow port reuse. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-00-03-34.gh-issue-116738.iBBAdo.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-00-03-34.gh-issue-116738.iBBAdo.rst deleted file mode 100644 index 2a1ed2944d8ddf..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-00-03-34.gh-issue-116738.iBBAdo.rst +++ /dev/null @@ -1 +0,0 @@ -Make functions in :mod:`grp` thread-safe on the :term:`free threaded <free threading>` build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-11-19-52.gh-issue-135422.F6yQi6.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-11-19-52.gh-issue-135422.F6yQi6.rst deleted file mode 100644 index bb0f178f91ab0f..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-11-19-52.gh-issue-135422.F6yQi6.rst +++ /dev/null @@ -1 +0,0 @@ -Fix regression in :exc:`SyntaxError` messages after :gh:`134036`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-18-12-42.gh-issue-135371.R_YUtR.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-18-12-42.gh-issue-135371.R_YUtR.rst deleted file mode 100644 index 9f2e825e57bb2a..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-12-18-12-42.gh-issue-135371.R_YUtR.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fixed :mod:`asyncio` debugging tools to properly display internal coroutine -call stacks alongside external task dependencies. The ``python -m asyncio -ps`` and ``python -m asyncio pstree`` commands now show complete execution -context. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-13-16-05-24.gh-issue-135474.67nOl3.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-13-16-05-24.gh-issue-135474.67nOl3.rst deleted file mode 100644 index 716d9b78748fa2..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-13-16-05-24.gh-issue-135474.67nOl3.rst +++ /dev/null @@ -1 +0,0 @@ -Specialize integer operations only on compact integers. This is a CPython internal change. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-14-01-01-14.gh-issue-135496.ER0Me3.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-14-01-01-14.gh-issue-135496.ER0Me3.rst deleted file mode 100644 index 03b1f4590c5846..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-14-01-01-14.gh-issue-135496.ER0Me3.rst +++ /dev/null @@ -1 +0,0 @@ -Fix typo in the f-string conversion type error ("exclamanation" -> "exclamation"). diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-16-02-31-42.gh-issue-135543.6b0HOF.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-16-02-31-42.gh-issue-135543.6b0HOF.rst deleted file mode 100644 index 6efe2a47bac5d4..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-16-02-31-42.gh-issue-135543.6b0HOF.rst +++ /dev/null @@ -1,2 +0,0 @@ -Emit ``sys.remote_exec`` audit event when :func:`sys.remote_exec` is called -and migrate ``remote_debugger_script`` to ``cpython.remote_debugger_script``. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-16-03-56-15.gh-issue-135551.hRTQO-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-16-03-56-15.gh-issue-135551.hRTQO-.rst deleted file mode 100644 index 22dda2a3e972a8..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-16-03-56-15.gh-issue-135551.hRTQO-.rst +++ /dev/null @@ -1 +0,0 @@ -Sorting randomly ordered lists will often run a bit faster, thanks to a new scheme for picking minimum run lengths from Stefan Pochmann, which arranges for the merge tree to be as evenly balanced as is possible. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst deleted file mode 100644 index 74bd182c1990f8..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst +++ /dev/null @@ -1,5 +0,0 @@ -Improve performance of ``PyLongObject`` conversion functions -``PyLong_AsLongAndOverflow()``, ``PyLong_AsSsize_t()``, ``PyLong_AsUnsignedLong()``, ``PyLong_AsSize_t()``, -``PyLong_AsUnsignedLongMask()``, ``PyLong_AsUnsignedLongLongMask()``, ``PyLong_AsLongLongAndOverflow()`` -for integers larger than 2**30 up to 30%. - diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-12-50-48.gh-issue-135608.PnHckD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-12-50-48.gh-issue-135608.PnHckD.rst deleted file mode 100644 index a65a0c85fa64fa..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-12-50-48.gh-issue-135608.PnHckD.rst +++ /dev/null @@ -1 +0,0 @@ -Fix a crash in the JIT involving attributes of modules. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst deleted file mode 100644 index 859259a9ace758..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix potential :mod:`weakref` races in an object's destructor on the :term:`free threaded <free -threading>` build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-12-19-13.gh-issue-135379.TCvGpj.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-12-19-13.gh-issue-135379.TCvGpj.rst deleted file mode 100644 index 089d00c77da25d..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-12-19-13.gh-issue-135379.TCvGpj.rst +++ /dev/null @@ -1,3 +0,0 @@ -Changes specialization of ``BINARY_OP`` for ints to only specialize for -"compact" ints. This streamlines the fast path at the cost of fewer -specializations when very large integers are used. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-16-45-36.gh-issue-135106.cpl6Aq.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-16-45-36.gh-issue-135106.cpl6Aq.rst deleted file mode 100644 index b6e953a7719be1..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-18-16-45-36.gh-issue-135106.cpl6Aq.rst +++ /dev/null @@ -1,2 +0,0 @@ -Restrict the trashcan mechanism to GC'ed objects and untrack them while in -the trashcan to prevent the GC and trashcan mechanisms conflicting. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-20-14-50-44.gh-issue-134584.3CJdAI.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-20-14-50-44.gh-issue-134584.3CJdAI.rst deleted file mode 100644 index 715ac7dc9253d5..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-20-14-50-44.gh-issue-134584.3CJdAI.rst +++ /dev/null @@ -1 +0,0 @@ -Specialize :opcode:`POP_TOP` in the JIT compiler by specializing for reference lifetime and type. This will also enable easier top of stack caching in the JIT compiler. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-23-18-08-32.gh-issue-135871.50C528.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-23-18-08-32.gh-issue-135871.50C528.rst deleted file mode 100644 index ce29ddecefe17f..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-23-18-08-32.gh-issue-135871.50C528.rst +++ /dev/null @@ -1,2 +0,0 @@ -Non-blocking mutex lock attempts now return immediately when the lock is busy -instead of briefly spinning in the :term:`free threading` build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-06-41-47.gh-issue-129958.EaJuS0.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-06-41-47.gh-issue-129958.EaJuS0.rst deleted file mode 100644 index 70b3e99425df14..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-06-41-47.gh-issue-129958.EaJuS0.rst +++ /dev/null @@ -1,2 +0,0 @@ -Differentiate between t-strings and f-strings in syntax error for newlines -in format specifiers of single-quoted interpolated strings. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-16-46-34.gh-issue-135904.78xfon.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-16-46-34.gh-issue-135904.78xfon.rst deleted file mode 100644 index ecbd8fda9a5e9d..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-16-46-34.gh-issue-135904.78xfon.rst +++ /dev/null @@ -1,2 +0,0 @@ -Perform more aggressive control-flow optimizations on the machine code -templates emitted by the experimental JIT compiler. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-26-15-25-51.gh-issue-78465.MbDN8X.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-26-15-25-51.gh-issue-78465.MbDN8X.rst deleted file mode 100644 index 99734d63c5d87e..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-26-15-25-51.gh-issue-78465.MbDN8X.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix error message for ``cls.__new__(cls, ...)`` where ``cls`` is not -instantiable builtin or extension type (with ``tp_new`` set to ``NULL``). diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-02-15-18-41.gh-issue-136203.Y934sC.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-02-15-18-41.gh-issue-136203.Y934sC.rst deleted file mode 100644 index 5a622bab8b893d..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-02-15-18-41.gh-issue-136203.Y934sC.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve :exc:`TypeError` error message, when richcomparing two -:class:`types.MappingProxyType` objects. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-06-14-53-19.gh-issue-109700.KVNQQi.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-06-14-53-19.gh-issue-109700.KVNQQi.rst deleted file mode 100644 index a37f4a51050947..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-06-14-53-19.gh-issue-109700.KVNQQi.rst +++ /dev/null @@ -1 +0,0 @@ -Fix memory error handling in :c:func:`PyDict_SetDefault`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-07-17-26-06.gh-issue-91636.GyHU72.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-07-17-26-06.gh-issue-91636.GyHU72.rst deleted file mode 100644 index 09c192f9c5657e..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-07-17-26-06.gh-issue-91636.GyHU72.rst +++ /dev/null @@ -1,3 +0,0 @@ -While performing garbage collection, clear weakrefs to unreachable objects -that are created during running of finalizers. If those weakrefs were are -not cleared, they could reveal unreachable objects. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-22-08.gh-issue-132661.34ftJl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-22-08.gh-issue-132661.34ftJl.rst deleted file mode 100644 index 5d59e024389c18..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-22-08.gh-issue-132661.34ftJl.rst +++ /dev/null @@ -1,5 +0,0 @@ -Reflect recent :pep:`750` change. - -Disallow concatenation of ``string.templatelib.Template`` and :class:`str`. -Also, disallow implicit concatenation of t-string literals with string or -f-string literals. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-53-51.gh-issue-132661.B84iYt.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-53-51.gh-issue-132661.B84iYt.rst deleted file mode 100644 index 9930413b53c150..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-53-51.gh-issue-132661.B84iYt.rst +++ /dev/null @@ -1 +0,0 @@ -``Interpolation.expression`` now has a default, the empty string. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-09-11-15-42.gh-issue-136459.m4Udh8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-09-11-15-42.gh-issue-136459.m4Udh8.rst deleted file mode 100644 index 470f3311526e83..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-09-11-15-42.gh-issue-136459.m4Udh8.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add support for perf trampoline on macOS, to allow profilers wit JIT map -support to read Python calls. While profiling, ``PYTHONPERFSUPPORT=1`` can -be appended to enable the trampoline. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-10-15-53-16.gh-issue-136525.xAko0e.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-10-15-53-16.gh-issue-136525.xAko0e.rst deleted file mode 100644 index f28eb2ca3b71e8..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-10-15-53-16.gh-issue-136525.xAko0e.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix issue where per-thread bytecode was not instrumented for newly created -threads. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-10-23-23-50.gh-issue-136517._NHJyv.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-10-23-23-50.gh-issue-136517._NHJyv.rst deleted file mode 100644 index bf26c4eb0e4c74..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-10-23-23-50.gh-issue-136517._NHJyv.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed a typo that prevented printing of uncollectable objects when the -:const:`gc.DEBUG_UNCOLLECTABLE` mode was set. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-11-13-45-48.gh-issue-136541.uZ_-Ju.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-11-13-45-48.gh-issue-136541.uZ_-Ju.rst deleted file mode 100644 index af9b94ad0613d7..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-11-13-45-48.gh-issue-136541.uZ_-Ju.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix some issues with the perf trampolines on x86-64 and aarch64. The -trampolines were not being generated correctly for some cases, which could -lead to the perf integration not working correctly. Patch by Pablo Galindo. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst deleted file mode 100644 index dcc73267a78546..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst +++ /dev/null @@ -1 +0,0 @@ -Fix crash when initializing :mod:`datetime` concurrently. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-15-10-03-57.gh-issue-116738.oFttKl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-15-10-03-57.gh-issue-116738.oFttKl.rst deleted file mode 100644 index 07d8b45fb7fbf1..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-15-10-03-57.gh-issue-116738.oFttKl.rst +++ /dev/null @@ -1 +0,0 @@ -Make functions in :mod:`pwd` thread-safe on the :term:`free threaded <free threading>` build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-18-08-43-35.gh-issue-116738.i0HWtP.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-18-08-43-35.gh-issue-116738.i0HWtP.rst deleted file mode 100644 index 77dca4074b742f..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-18-08-43-35.gh-issue-116738.i0HWtP.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make functions in :mod:`syslog` thread-safe on the :term:`free threaded -<free threading>` build. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-12-37-05.gh-issue-136801.XU_tF2.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-12-37-05.gh-issue-136801.XU_tF2.rst deleted file mode 100644 index 767d7b97726971..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-12-37-05.gh-issue-136801.XU_tF2.rst +++ /dev/null @@ -1 +0,0 @@ -Fix PyREPL syntax highlighting on match cases after multi-line case. Contributed by Olga Matoula. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-17-08-09.gh-issue-127598.Mx8S-y.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-17-08-09.gh-issue-127598.Mx8S-y.rst deleted file mode 100644 index aff047bbef0f07..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-17-08-09.gh-issue-127598.Mx8S-y.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve :exc:`ModuleNotFoundError` by adding flavour text to the exception when the -:option:`-S` option is passed. Patch by Andrea Mattei. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-24-17-30-58.gh-issue-136870.ncx82J.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-24-17-30-58.gh-issue-136870.ncx82J.rst deleted file mode 100644 index 7552dc69169b67..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-24-17-30-58.gh-issue-136870.ncx82J.rst +++ /dev/null @@ -1 +0,0 @@ -Fix data races while de-instrumenting bytecode of code objects running concurrently in threads. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-03-31-16-15-15.gh-issue-75723.BZ4Rsn.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-31-16-15-15.gh-issue-75723.BZ4Rsn.rst new file mode 100644 index 00000000000000..596ca89958c9ed --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-31-16-15-15.gh-issue-75723.BZ4Rsn.rst @@ -0,0 +1 @@ +Avoid re-executing ``.pth`` files when :func:`site.addsitedir` is called for a known directory. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-15-15-48-04.gh-issue-148450.2MEVqH.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-15-15-48-04.gh-issue-148450.2MEVqH.rst new file mode 100644 index 00000000000000..2a7d0d9bb3a7f7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-15-15-48-04.gh-issue-148450.2MEVqH.rst @@ -0,0 +1 @@ +Fix ``abc.register()`` so it invalidates type version tags for registered classes. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-05-12-00-00.gh-issue-149321.remove-lazy-imports-none.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-05-12-00-00.gh-issue-149321.remove-lazy-imports-none.rst new file mode 100644 index 00000000000000..44e96ce7be0bfb --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-05-12-00-00.gh-issue-149321.remove-lazy-imports-none.rst @@ -0,0 +1 @@ +Do not support ``none`` as a lazy imports mode. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-07-03-18-59.gh-issue-149459.5fhAqP.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-07-03-18-59.gh-issue-149459.5fhAqP.rst new file mode 100644 index 00000000000000..4cd0a148df3c70 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-07-03-18-59.gh-issue-149459.5fhAqP.rst @@ -0,0 +1 @@ +Fix a crash in the JIT optimizer when a specialized ``LOAD_SPECIAL`` guard deoptimized after inserting the synthetic ``NULL`` stack entry. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-09-15-22-32.gh-issue-144957.u1F2aQ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-09-15-22-32.gh-issue-144957.u1F2aQ.rst new file mode 100644 index 00000000000000..3063f1a3c0e6d3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-09-15-22-32.gh-issue-144957.u1F2aQ.rst @@ -0,0 +1,2 @@ +Fix lazy ``from`` imports of module attributes provided by module-level +``__getattr__``. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-10-07-42-36.gh-issue-149642.6ZksML.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-10-07-42-36.gh-issue-149642.6ZksML.rst new file mode 100644 index 00000000000000..815a084db69d8d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-10-07-42-36.gh-issue-149642.6ZksML.rst @@ -0,0 +1,2 @@ +Allow imports inside ``exec()`` calls within functions under +``PYTHON_LAZY_IMPORTS=all``. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-10-16-43-50.gh-issue-148829.gscS14.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-10-16-43-50.gh-issue-148829.gscS14.rst new file mode 100644 index 00000000000000..3f9b1ccb518787 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-10-16-43-50.gh-issue-148829.gscS14.rst @@ -0,0 +1,2 @@ +:class:`sentinel` objects now support a ``repr=`` argument and their +:attr:`~sentinel.__module__` attribute is writable. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-14-48-56.gh-issue-149676.6aTrw1.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-14-48-56.gh-issue-149676.6aTrw1.rst new file mode 100644 index 00000000000000..96f407cf5ad25a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-14-48-56.gh-issue-149676.6aTrw1.rst @@ -0,0 +1 @@ +Fix ``frozendict | frozendict`` hash. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-18-20-41.gh-issue-148871.AeCbq7.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-18-20-41.gh-issue-148871.AeCbq7.rst new file mode 100644 index 00000000000000..506e369f553e50 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-11-18-20-41.gh-issue-148871.AeCbq7.rst @@ -0,0 +1,3 @@ +The empty tuple ``()`` is now loaded via :opcode:`LOAD_COMMON_CONSTANT` +instead of :opcode:`LOAD_CONST`, removing it from per-code-object +:attr:`~codeobject.co_consts` tuples. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-12-16-47-23.gh-issue-139808.iIs7_E.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-12-16-47-23.gh-issue-139808.iIs7_E.rst new file mode 100644 index 00000000000000..3e9d930bf1de89 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-12-16-47-23.gh-issue-139808.iIs7_E.rst @@ -0,0 +1,2 @@ +Add branch protections for AArch64 (BTI/PAC) in assembly code used by +:option:`-X perf_jit <-X>` (Linux perf profiler integration). diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-06-54-41.gh-issue-149738.4BLFoH.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-06-54-41.gh-issue-149738.4BLFoH.rst new file mode 100644 index 00000000000000..e62b681d716650 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-06-54-41.gh-issue-149738.4BLFoH.rst @@ -0,0 +1,2 @@ +:mod:`sqlite3`: Disallow removing ``row_factory`` and ``text_factory`` attributes +of a connection to prevent a crash on a query. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-21-26-26.gh-issue-149805.IG6cza.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-21-26-26.gh-issue-149805.IG6cza.rst new file mode 100644 index 00000000000000..02d050840ee1f9 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-21-26-26.gh-issue-149805.IG6cza.rst @@ -0,0 +1,2 @@ +Fix a :exc:`SystemError` when compiling a compiling ``__classdict__`` class +annotation. Found by OSS-Fuzz in :oss-fuzz:`512907042`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-14-19-41-03.gh-issue-149807.IwGaCo.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-14-19-41-03.gh-issue-149807.IwGaCo.rst new file mode 100644 index 00000000000000..a94c737e73619d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-14-19-41-03.gh-issue-149807.IwGaCo.rst @@ -0,0 +1,2 @@ +Fix ``hash(frozendict)``: compute the hash of each ``(key, value)`` pair +correctly. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-15-11-31-57.gh-issue-149816.ugN2rx.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-15-11-31-57.gh-issue-149816.ugN2rx.rst new file mode 100644 index 00000000000000..016c17dd66b19e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-15-11-31-57.gh-issue-149816.ugN2rx.rst @@ -0,0 +1 @@ +Fix a race condition in :class:`memoryview` with free-threading. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-16-11-03-54.gh-issue-149816.X_gqMT.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-16-11-03-54.gh-issue-149816.X_gqMT.rst new file mode 100644 index 00000000000000..d35f0857a1aefe --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-16-11-03-54.gh-issue-149816.X_gqMT.rst @@ -0,0 +1 @@ +Fix a race condition in ``_PyBytes_FromList`` in free-threading mode. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-17-12-00-00.gh-issue-138325.h2XSfe.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-17-12-00-00.gh-issue-138325.h2XSfe.rst new file mode 100644 index 00000000000000..0eb528c68d8d6f --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-17-12-00-00.gh-issue-138325.h2XSfe.rst @@ -0,0 +1,3 @@ +Speed up converting a list to a tuple in the ``tuple(genexpr)`` fast path +and in starred tuple displays (e.g. ``(*a, *b)``) by stealing the list's +items into the tuple instead of copying them. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-13-47-17.gh-issue-149590.IPBeQx.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-13-47-17.gh-issue-149590.IPBeQx.rst new file mode 100644 index 00000000000000..8d3b29d69cc857 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-13-47-17.gh-issue-149590.IPBeQx.rst @@ -0,0 +1 @@ +Fix crash when faulthandler is imported more than once. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-16-54-54.gh-issue-150042.LSr5W8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-16-54-54.gh-issue-150042.LSr5W8.rst new file mode 100644 index 00000000000000..18a4fbd9dadd60 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-16-54-54.gh-issue-150042.LSr5W8.rst @@ -0,0 +1 @@ +Fix refleak in queue.SimpleQueue.put if memory allocation fails. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-17-16-51.gh-issue-150027.sJgLvd.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-17-16-51.gh-issue-150027.sJgLvd.rst new file mode 100644 index 00000000000000..66446105da3fba --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-17-16-51.gh-issue-150027.sJgLvd.rst @@ -0,0 +1,2 @@ +Improve performance of :class:`frozenset` objects by avoiding copies during +construction. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-18-36-28.gh-issue-148587.-RD3z5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-18-36-28.gh-issue-148587.-RD3z5.rst new file mode 100644 index 00000000000000..d623dbdd75f0c8 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-18-18-36-28.gh-issue-148587.-RD3z5.rst @@ -0,0 +1 @@ +:py:attr:`sys.lazy_modules` is now a set instead of a dict as initially spelled out in PEP 810. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-20-13-06-17.gh-issue-150146.i5m_SL.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-20-13-06-17.gh-issue-150146.i5m_SL.rst new file mode 100644 index 00000000000000..f373f0bee7023e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-20-13-06-17.gh-issue-150146.i5m_SL.rst @@ -0,0 +1,5 @@ +Fix a crash on a complex type variable substitution. + +``from typing import TypeVar; memoryview[TypeVar("")][*typing.Mapping[..., +...]]`` used to fail due to missing ``NULL`` check on ``_unpack_args`` C +function call. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-21-21-04-43.gh-issue-150208.VOkxRG.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-21-21-04-43.gh-issue-150208.VOkxRG.rst new file mode 100644 index 00000000000000..e9bc1d9e976523 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-21-21-04-43.gh-issue-150208.VOkxRG.rst @@ -0,0 +1,2 @@ +Avoid double-quoting string values from ``pyconfig.h`` in ``sysconfigdata`` +variables. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-22-17-09-28.gh-issue-150107.GD72-D.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-22-17-09-28.gh-issue-150107.GD72-D.rst new file mode 100644 index 00000000000000..a13f249e48cc02 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-22-17-09-28.gh-issue-150107.GD72-D.rst @@ -0,0 +1,3 @@ +:mod:`asyncio`: ``sendfile()`` and ``sock_sendfile()`` event loop methods +now call ``file.seek(offset)`` if *file* has a ``seek()`` method, +even if *offset* is ``0`` (default value). diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-22-21-52-38.gh-issue-150207.l2BUtI.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-22-21-52-38.gh-issue-150207.l2BUtI.rst new file mode 100644 index 00000000000000..12fbffcd170684 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-22-21-52-38.gh-issue-150207.l2BUtI.rst @@ -0,0 +1 @@ +Fix a crash when a memory allocation fails during tokenizer initialization. A proper :exc:`MemoryError` is now raised instead. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-23-22-08-01.gh-issue-149449.2lhQFF.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-23-22-08-01.gh-issue-149449.2lhQFF.rst new file mode 100644 index 00000000000000..7d11442468d207 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-23-22-08-01.gh-issue-149449.2lhQFF.rst @@ -0,0 +1,3 @@ +Fix a use-after-free crash when the :mod:`unicodedata` module was removed +from :data:`sys.modules` and garbage-collected between calls that decode +``\N{...}`` escapes or use the ``namereplace`` codec error handler. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-14-45-00.gh-issue-149156.NP73rB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-14-45-00.gh-issue-149156.NP73rB.rst new file mode 100644 index 00000000000000..2cb091e2b162f6 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-14-45-00.gh-issue-149156.NP73rB.rst @@ -0,0 +1,3 @@ +Fix an intermittent crash after :func:`os.fork` when perf trampoline +profiling is enabled and the child returns through trampoline frames +inherited from the parent process. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-22-46-49.gh-issue-148613.PLpmyd.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-22-46-49.gh-issue-148613.PLpmyd.rst new file mode 100644 index 00000000000000..71a701bf3eb355 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-24-22-46-49.gh-issue-148613.PLpmyd.rst @@ -0,0 +1,2 @@ +Fix a data race in the free-threaded build between :func:`gc.set_threshold` +and garbage collection scheduling during object allocation. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-25-16-00-22.gh-issue-150374.Emu6d8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-25-16-00-22.gh-issue-150374.Emu6d8.rst new file mode 100644 index 00000000000000..7189ca186d2b7e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-25-16-00-22.gh-issue-150374.Emu6d8.rst @@ -0,0 +1 @@ +Fix double release of the import lock on lazy import reification errors. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst new file mode 100644 index 00000000000000..5b19a4fff5ddc7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-26-00-06-30.gh-issue-150411.u-d-_5.rst @@ -0,0 +1,2 @@ +Fix a data race in the free-threaded build when :func:`gc.get_count` reads +the young generation allocation count while another thread updates it. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-29-17-51-11.gh-issue-144774.jPcixe.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-29-17-51-11.gh-issue-144774.jPcixe.rst new file mode 100644 index 00000000000000..3dd117e7d3c350 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-29-17-51-11.gh-issue-144774.jPcixe.rst @@ -0,0 +1 @@ +Fix data race in :class:`BaseException` when an exception is copied while being mutated. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-30-20-19-35.gh-issue-150633.XkNul0.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-30-20-19-35.gh-issue-150633.XkNul0.rst new file mode 100644 index 00000000000000..c397ad61f086c1 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-30-20-19-35.gh-issue-150633.XkNul0.rst @@ -0,0 +1,3 @@ +Fix the frozen importer accepting module names with embedded null bytes, which +caused it to bypass the :data:`sys.modules` cache and create duplicate module +objects. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-00-00.gh-issue-150700.W8CzVR.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-00-00.gh-issue-150700.W8CzVR.rst new file mode 100644 index 00000000000000..e7734034ff5c81 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-00-00.gh-issue-150700.W8CzVR.rst @@ -0,0 +1,3 @@ +Fix a :exc:`SystemError` when compiling a class-scope comprehension containing +a ``lambda`` that references ``__class__``, ``__classdict__``, or +``__conditional_annotations__``. Patch by Bartosz Sławecki. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-21-01.gh-issue-150723.Hb3JDG.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-21-01.gh-issue-150723.Hb3JDG.rst new file mode 100644 index 00000000000000..1920c8cdfce4f4 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-21-01.gh-issue-150723.Hb3JDG.rst @@ -0,0 +1,4 @@ +Fix malformed perf jitdump thread ids on macOS. The ``thread_id`` field of the +``JR_CODE_LOAD`` record was written as a 64-bit value instead of the 32-bit +value required by the jitdump format, which shifted every following field and +prevented profilers from resolving Python frames. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-24-12.gh-issue-150723.WlcL_-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-24-12.gh-issue-150723.WlcL_-.rst new file mode 100644 index 00000000000000..78c896b669c239 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-01-19-24-12.gh-issue-150723.WlcL_-.rst @@ -0,0 +1,4 @@ +Fix perf jitdump timestamps on macOS. Events were stamped using +``CLOCK_MONOTONIC``, but macOS profilers timestamp their samples with +``mach_absolute_time()``. The mismatch prevented the JIT code mappings from +lining up with the samples, so no Python frame could be resolved. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-03-16-27-00.gh-issue-150858.j2dSkD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-03-16-27-00.gh-issue-150858.j2dSkD.rst new file mode 100644 index 00000000000000..dcb932bf320629 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-03-16-27-00.gh-issue-150858.j2dSkD.rst @@ -0,0 +1 @@ +Fix a data race while changing ``__qualname__`` of a type concurrently on free-threaded builds. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst new file mode 100644 index 00000000000000..6fb70a1ce2685c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-05-22-52-41.gh-issue-150988.fDKfMJ.rst @@ -0,0 +1,2 @@ +Fix a reference leak in :exc:`OSError` when attributes are set before +``super().__init__()``. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-06-08-20-00.gh-issue-150942.Jk9pQr.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-06-08-20-00.gh-issue-150942.Jk9pQr.rst new file mode 100644 index 00000000000000..9777b893227140 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-06-08-20-00.gh-issue-150942.Jk9pQr.rst @@ -0,0 +1,3 @@ +Speed up frame local variable item collection by appending result pairs to the +output list without an extra reference-count round-trip (using the internal +reference-stealing list append helper). Patch by Omkar Kabde. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-05-31-22.gh-issue-151065._o_31F.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-05-31-22.gh-issue-151065._o_31F.rst new file mode 100644 index 00000000000000..e46c96ef784cc9 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-05-31-22.gh-issue-151065._o_31F.rst @@ -0,0 +1 @@ +Fix memory leak when using the :ref:`mimalloc memory allocator <mimalloc>`. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst new file mode 100644 index 00000000000000..67e2ce4044431f --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-10-28-30.gh-issue-151126.DKa6Sl.rst @@ -0,0 +1,5 @@ +Fix a crash, when there's no memory left on a device, +which happened in: code compilation, :mod:`!_interpchannels` module, +:func:`!_winapi.CreateProcess` function. + +Now these places raise proper :exc:`MemoryError` errors. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-12-24-35.gh-issue-151112.4RKCkD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-12-24-35.gh-issue-151112.4RKCkD.rst new file mode 100644 index 00000000000000..93ee5c8cf1914b --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-09-12-24-35.gh-issue-151112.4RKCkD.rst @@ -0,0 +1 @@ +Fix a crash in the compiler that could occur when running out of memory. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-19-58.gh-issue-151238.C9Wu4x.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-19-58.gh-issue-151238.C9Wu4x.rst new file mode 100644 index 00000000000000..fe7519fb487895 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-19-58.gh-issue-151238.C9Wu4x.rst @@ -0,0 +1,2 @@ +Fix a crash when compiling a concatenated f-string or t-string if an error +occurs when processing one of it's parts. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-42-46.gh-issue-151253.7MMQ8P.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-42-46.gh-issue-151253.7MMQ8P.rst new file mode 100644 index 00000000000000..56d2f3b2633bb0 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-10-15-42-46.gh-issue-151253.7MMQ8P.rst @@ -0,0 +1,3 @@ +If ``import encodings`` (first import) fails at Python startup, dump the +Python path configuration to help users debugging their configuration. Patch +by Victor Stinner. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-11-16-03-23.gh-issue-151297.NGPkUM.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-11-16-03-23.gh-issue-151297.NGPkUM.rst new file mode 100644 index 00000000000000..288d726e0f1004 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-11-16-03-23.gh-issue-151297.NGPkUM.rst @@ -0,0 +1 @@ +Fix an invalid pointer dereference that could occur when calling :c:func:`PyObject_Realloc` with a NULL pointer in :term:`free-threaded builds <free-threaded build>` or with :envvar:`PYTHONMALLOC` set to ``mimalloc``. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-14-05-05-15.gh-issue-151461.5q0s88.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-14-05-05-15.gh-issue-151461.5q0s88.rst new file mode 100644 index 00000000000000..d76a9bc95278bc --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-14-05-05-15.gh-issue-151461.5q0s88.rst @@ -0,0 +1,3 @@ +Fix direct execution of files with invalid source encodings to report the +underlying codec lookup or decoding error instead of the generic +``SyntaxError: encoding problem`` message. Patch by Bartosz Sławecki. diff --git a/Misc/NEWS.d/next/Documentation/2021-09-15-13-07-25.bpo-45210.RtGk7i.rst b/Misc/NEWS.d/next/Documentation/2021-09-15-13-07-25.bpo-45210.RtGk7i.rst deleted file mode 100644 index ce3eba154ba6aa..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2021-09-15-13-07-25.bpo-45210.RtGk7i.rst +++ /dev/null @@ -1,2 +0,0 @@ -Document that error indicator may be set in tp_dealloc, and how to avoid -clobbering it. diff --git a/Misc/NEWS.d/next/Documentation/2023-09-16-23-42-27.gh-issue-109503.mZ-kdU.rst b/Misc/NEWS.d/next/Documentation/2023-09-16-23-42-27.gh-issue-109503.mZ-kdU.rst new file mode 100644 index 00000000000000..c3c6c57569c2ea --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-09-16-23-42-27.gh-issue-109503.mZ-kdU.rst @@ -0,0 +1,3 @@ +Fix documentation for :func:`shutil.move` on usage of +:func:`os.rename` since nonatomic move might be used even if the files are +on the same filesystem. Patch by Fang Li diff --git a/Misc/NEWS.d/next/Documentation/2025-06-10-17-02-06.gh-issue-135171.quHvts.rst b/Misc/NEWS.d/next/Documentation/2025-06-10-17-02-06.gh-issue-135171.quHvts.rst deleted file mode 100644 index 129ff74189bc64..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2025-06-10-17-02-06.gh-issue-135171.quHvts.rst +++ /dev/null @@ -1,2 +0,0 @@ -Document that the :term:`iterator` for the leftmost :keyword:`!for` clause -in the generator expression is created immediately. diff --git a/Misc/NEWS.d/next/Documentation/2025-07-01-21-04-47.gh-issue-136155.ufmH4Q.rst b/Misc/NEWS.d/next/Documentation/2025-07-01-21-04-47.gh-issue-136155.ufmH4Q.rst deleted file mode 100644 index 0341b5f7f0d5e6..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2025-07-01-21-04-47.gh-issue-136155.ufmH4Q.rst +++ /dev/null @@ -1 +0,0 @@ -EPUB builds are fixed by excluding non-XHTML-compatible tags. diff --git a/Misc/NEWS.d/next/Documentation/2025-07-01-23-00-58.gh-issue-136155.4siQQO.rst b/Misc/NEWS.d/next/Documentation/2025-07-01-23-00-58.gh-issue-136155.4siQQO.rst deleted file mode 100644 index 70f54936c80f55..00000000000000 --- a/Misc/NEWS.d/next/Documentation/2025-07-01-23-00-58.gh-issue-136155.4siQQO.rst +++ /dev/null @@ -1 +0,0 @@ -We are now checking for fatal errors in EPUB builds in CI. diff --git a/Misc/NEWS.d/next/Documentation/2026-05-23-17-27-41.gh-issue-150319.ol9tWK.rst b/Misc/NEWS.d/next/Documentation/2026-05-23-17-27-41.gh-issue-150319.ol9tWK.rst new file mode 100644 index 00000000000000..d56ccbce2fa325 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2026-05-23-17-27-41.gh-issue-150319.ol9tWK.rst @@ -0,0 +1,2 @@ +Generic builtin and standard library types now document the meaning of their +type parameters. diff --git a/Misc/NEWS.d/next/IDLE/2019-12-12-03-18-02.bpo-6699.1CqJFG.rst b/Misc/NEWS.d/next/IDLE/2019-12-12-03-18-02.bpo-6699.1CqJFG.rst new file mode 100644 index 00000000000000..e7fb9bf1b3bdf6 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-12-12-03-18-02.bpo-6699.1CqJFG.rst @@ -0,0 +1 @@ +Warn the user if a file will be overwritten when saving. diff --git a/Misc/NEWS.d/next/Library/2017-12-30-18-21-00.bpo-28494.Dt_Wks.rst b/Misc/NEWS.d/next/Library/2017-12-30-18-21-00.bpo-28494.Dt_Wks.rst deleted file mode 100644 index 0c518983770d5d..00000000000000 --- a/Misc/NEWS.d/next/Library/2017-12-30-18-21-00.bpo-28494.Dt_Wks.rst +++ /dev/null @@ -1 +0,0 @@ -Improve Zip file validation false positive rate in :func:`zipfile.is_zipfile`. diff --git a/Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst b/Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst new file mode 100644 index 00000000000000..239f856bb04c0a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-02-26-13-17-57.bpo-40469.yJHeQg.rst @@ -0,0 +1,5 @@ +:class:`~logging.handlers.TimedRotatingFileHandler` now uses the creation time +instead of the last modification time of an existing log file as the basis +for the first rotation after handler creation, if supported by the OS and file system. +This allows it to be used in short-running programs that start and end before +the rotation interval expires. diff --git a/Misc/NEWS.d/next/Library/2021-10-18-13-46-55.bpo-45509.Upwb60.rst b/Misc/NEWS.d/next/Library/2021-10-18-13-46-55.bpo-45509.Upwb60.rst new file mode 100644 index 00000000000000..80c38c03f8fe78 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-10-18-13-46-55.bpo-45509.Upwb60.rst @@ -0,0 +1 @@ +Gzip headers are now checked for corrupted NAME, COMMENT and HCRC fields. diff --git a/Misc/NEWS.d/next/Library/2021-12-18-12-46-20.bpo-45959.vPlr3P.rst b/Misc/NEWS.d/next/Library/2021-12-18-12-46-20.bpo-45959.vPlr3P.rst deleted file mode 100644 index bcafa64d2638dc..00000000000000 --- a/Misc/NEWS.d/next/Library/2021-12-18-12-46-20.bpo-45959.vPlr3P.rst +++ /dev/null @@ -1 +0,0 @@ -:mod:`pprint` can now pretty-print dict views. diff --git a/Misc/NEWS.d/next/Library/2022-04-04-17-58-05.bpo-47216.gPyPte.rst b/Misc/NEWS.d/next/Library/2022-04-04-17-58-05.bpo-47216.gPyPte.rst new file mode 100644 index 00000000000000..066e17c1c6e06a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-04-04-17-58-05.bpo-47216.gPyPte.rst @@ -0,0 +1,2 @@ +Added *mtime* option to :func:`gzip.open`, which will be passed +to the constructor of :class:`~gzip.GzipFile`. diff --git a/Misc/NEWS.d/next/Library/2022-07-24-20-56-32.gh-issue-69426.unccw7.rst b/Misc/NEWS.d/next/Library/2022-07-24-20-56-32.gh-issue-69426.unccw7.rst deleted file mode 100644 index d8c081390d0669..00000000000000 --- a/Misc/NEWS.d/next/Library/2022-07-24-20-56-32.gh-issue-69426.unccw7.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix :class:`html.parser.HTMLParser` to not unescape character entities in -attribute values if they are followed by an ASCII alphanumeric or an equals -sign. diff --git a/Misc/NEWS.d/next/Library/2023-01-23-21-23-50.gh-issue-101267._f-cFH.rst b/Misc/NEWS.d/next/Library/2023-01-23-21-23-50.gh-issue-101267._f-cFH.rst new file mode 100644 index 00000000000000..901a3fb60ab5b9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-23-21-23-50.gh-issue-101267._f-cFH.rst @@ -0,0 +1,7 @@ +When a worker process terminates unexpectedly, +:class:`concurrent.futures.ProcessPoolExecutor` now sets a separate +:exc:`~concurrent.futures.process.BrokenProcessPool` exception on each pending +future instead of sharing a single instance among them all. Sharing one +exception produced malformed tracebacks: each +:meth:`Future.result() <concurrent.futures.Future.result>` call re-raised the +same object, appending another copy of the traceback to it. diff --git a/Misc/NEWS.d/next/Library/2023-02-13-21-41-34.gh-issue-86155.ppIGSC.rst b/Misc/NEWS.d/next/Library/2023-02-13-21-41-34.gh-issue-86155.ppIGSC.rst deleted file mode 100644 index bb85481b229697..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-02-13-21-41-34.gh-issue-86155.ppIGSC.rst +++ /dev/null @@ -1,2 +0,0 @@ -:meth:`html.parser.HTMLParser.close` no longer loses data when the -``<script>`` tag is not closed. Patch by Waylan Limberg. diff --git a/Misc/NEWS.d/next/Library/2023-02-13-21-56-38.gh-issue-62824.CBZzX3.rst b/Misc/NEWS.d/next/Library/2023-02-13-21-56-38.gh-issue-62824.CBZzX3.rst deleted file mode 100644 index 1fe4e47c9ec213..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-02-13-21-56-38.gh-issue-62824.CBZzX3.rst +++ /dev/null @@ -1 +0,0 @@ -Fix aliases for ``iso8859_8`` encoding. Patch by Dave Goncalves. diff --git a/Misc/NEWS.d/next/Library/2023-02-26-14-07-18.gh-issue-91099._QPbEL.rst b/Misc/NEWS.d/next/Library/2023-02-26-14-07-18.gh-issue-91099._QPbEL.rst new file mode 100644 index 00000000000000..d886e8ac6032a4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-26-14-07-18.gh-issue-91099._QPbEL.rst @@ -0,0 +1,2 @@ +:meth:`imaplib.IMAP4.login` now raises exceptions with :class:`str` instead of +:class:`bytes`. Patch by Florian Best. diff --git a/Misc/NEWS.d/next/Library/2023-03-13-22-51-40.gh-issue-99813.40TV02.rst b/Misc/NEWS.d/next/Library/2023-03-13-22-51-40.gh-issue-99813.40TV02.rst deleted file mode 100644 index c511c63021442b..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-03-13-22-51-40.gh-issue-99813.40TV02.rst +++ /dev/null @@ -1,4 +0,0 @@ -:mod:`ssl` now uses ``SSL_sendfile`` internally when it is possible (see -:data:`~ssl.OP_ENABLE_KTLS`). The function sends a file more efficiently -because it performs TLS encryption in the kernel to avoid additional context -switches. Patch by Illia Volochii. diff --git a/Misc/NEWS.d/next/Library/2023-07-05-14-34-10.gh-issue-105497.HU5u89.rst b/Misc/NEWS.d/next/Library/2023-07-05-14-34-10.gh-issue-105497.HU5u89.rst deleted file mode 100644 index f4f2db08f73f50..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-07-05-14-34-10.gh-issue-105497.HU5u89.rst +++ /dev/null @@ -1 +0,0 @@ -Fix flag mask inversion when unnamed flags exist. diff --git a/Misc/NEWS.d/next/Library/2024-03-17-22-16-53.gh-issue-115988.EshSDM.rst b/Misc/NEWS.d/next/Library/2024-03-17-22-16-53.gh-issue-115988.EshSDM.rst new file mode 100644 index 00000000000000..4efde6a48c26cc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-03-17-22-16-53.gh-issue-115988.EshSDM.rst @@ -0,0 +1 @@ +:mod:`lzma` adds constants to support the newer BCJ filters for ARM64 and RISC-V. diff --git a/Misc/NEWS.d/next/Library/2024-06-06-17-49-07.gh-issue-120170.DUxhmT.rst b/Misc/NEWS.d/next/Library/2024-06-06-17-49-07.gh-issue-120170.DUxhmT.rst deleted file mode 100644 index ce7d874aa20375..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-06-06-17-49-07.gh-issue-120170.DUxhmT.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix an issue in the :mod:`!_pickle` extension module in which importing -:mod:`multiprocessing` could change how pickle identifies which module an -object belongs to, potentially breaking the unpickling of those objects. diff --git a/Misc/NEWS.d/next/Library/2024-07-02-20-57-43.gh-issue-121109.Tp6R2s.rst b/Misc/NEWS.d/next/Library/2024-07-02-20-57-43.gh-issue-121109.Tp6R2s.rst new file mode 100644 index 00000000000000..eca6014e4a0aed --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-07-02-20-57-43.gh-issue-121109.Tp6R2s.rst @@ -0,0 +1,2 @@ +Fix :mod:`tarfile` performance issue when reading archives in streaming mode +(e.g. ``r|*``). diff --git a/Misc/NEWS.d/next/Library/2024-07-16-00-01-04.gh-issue-99631.GWD4fD.rst b/Misc/NEWS.d/next/Library/2024-07-16-00-01-04.gh-issue-99631.GWD4fD.rst deleted file mode 100644 index 735249b4dae224..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-07-16-00-01-04.gh-issue-99631.GWD4fD.rst +++ /dev/null @@ -1,2 +0,0 @@ -The :mod:`shelve` module now accepts custom serialization -and deserialization functions. diff --git a/Misc/NEWS.d/next/Library/2024-10-17-01-12-22.gh-issue-119109.u4hcvb.rst b/Misc/NEWS.d/next/Library/2024-10-17-01-12-22.gh-issue-119109.u4hcvb.rst deleted file mode 100644 index 782b2eb8d74c8e..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-10-17-01-12-22.gh-issue-119109.u4hcvb.rst +++ /dev/null @@ -1 +0,0 @@ -:func:`functools.partial` calls are now faster when keyword arguments are used. diff --git a/Misc/NEWS.d/next/Library/2024-10-22-16-21-55.gh-issue-125843.2ttzYo.rst b/Misc/NEWS.d/next/Library/2024-10-22-16-21-55.gh-issue-125843.2ttzYo.rst deleted file mode 100644 index ec8f3a750065ff..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-10-22-16-21-55.gh-issue-125843.2ttzYo.rst +++ /dev/null @@ -1,2 +0,0 @@ -If possible, indicate which :mod:`curses` C function or macro is responsible -for raising a :exc:`curses.error` exception. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2024-10-28-06-54-22.gh-issue-125028.GEY8Ws.rst b/Misc/NEWS.d/next/Library/2024-10-28-06-54-22.gh-issue-125028.GEY8Ws.rst deleted file mode 100644 index 09ebee4d41b68e..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-10-28-06-54-22.gh-issue-125028.GEY8Ws.rst +++ /dev/null @@ -1 +0,0 @@ -:data:`functools.Placeholder` cannot be passed to :func:`functools.partial` as a keyword argument. diff --git a/Misc/NEWS.d/next/Library/2024-11-02-02-02-31.gh-issue-107398.uUtA6Q.rst b/Misc/NEWS.d/next/Library/2024-11-02-02-02-31.gh-issue-107398.uUtA6Q.rst new file mode 100644 index 00000000000000..d5af322d68d309 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-02-02-02-31.gh-issue-107398.uUtA6Q.rst @@ -0,0 +1 @@ +Fix :mod:`tarfile` stream mode exception when process the file with the gzip extra field. diff --git a/Misc/NEWS.d/next/Library/2024-11-25-10-22-08.gh-issue-126883.MAEF7g.rst b/Misc/NEWS.d/next/Library/2024-11-25-10-22-08.gh-issue-126883.MAEF7g.rst deleted file mode 100644 index 5e3fa39acf1979..00000000000000 --- a/Misc/NEWS.d/next/Library/2024-11-25-10-22-08.gh-issue-126883.MAEF7g.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add check that timezone fields are in range for -:meth:`datetime.datetime.fromisoformat` and -:meth:`datetime.time.fromisoformat`. Patch by Semyon Moroz. diff --git a/Misc/NEWS.d/next/Library/2025-03-01-13-36-02.gh-issue-128110.9wx_G0.rst b/Misc/NEWS.d/next/Library/2025-03-01-13-36-02.gh-issue-128110.9wx_G0.rst new file mode 100644 index 00000000000000..b08b1886cff9cf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-01-13-36-02.gh-issue-128110.9wx_G0.rst @@ -0,0 +1,5 @@ +Fix bug in the parsing of :mod:`email` address headers that could result in +extraneous spaces in the decoded text when using a modern email policy. +Space between pairs of adjacent :rfc:`2047` encoded-words is now ignored, per +section 6.2 (and consistent with existing parsing of unstructured +headers like *Subject*). diff --git a/Misc/NEWS.d/next/Library/2025-03-09-03-13-41.gh-issue-130999.tBRBVB.rst b/Misc/NEWS.d/next/Library/2025-03-09-03-13-41.gh-issue-130999.tBRBVB.rst deleted file mode 100644 index 157522f9aab1b6..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-03-09-03-13-41.gh-issue-130999.tBRBVB.rst +++ /dev/null @@ -1,2 +0,0 @@ -Avoid exiting the new REPL and offer suggestions even if there are non-string -candidates when errors occur. diff --git a/Misc/NEWS.d/next/Library/2025-03-11-05-24-14.gh-issue-130664.g0yNMm.rst b/Misc/NEWS.d/next/Library/2025-03-11-05-24-14.gh-issue-130664.g0yNMm.rst deleted file mode 100644 index dbe783a2a99e0a..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-03-11-05-24-14.gh-issue-130664.g0yNMm.rst +++ /dev/null @@ -1,4 +0,0 @@ -Handle corner-case for :class:`~fractions.Fraction`'s formatting: treat -zero-padding (preceding the width field by a zero (``'0'``) character) as an -equivalent to a fill character of ``'0'`` with an alignment type of ``'='``, -just as in case of :class:`float`'s. diff --git a/Misc/NEWS.d/next/Library/2025-03-13-20-48-58.gh-issue-123471.cM4w4f.rst b/Misc/NEWS.d/next/Library/2025-03-13-20-48-58.gh-issue-123471.cM4w4f.rst deleted file mode 100644 index cfc783900de70f..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-03-13-20-48-58.gh-issue-123471.cM4w4f.rst +++ /dev/null @@ -1 +0,0 @@ -Make concurrent iterations over :class:`itertools.cycle` safe under free-threading. diff --git a/Misc/NEWS.d/next/Library/2025-03-19-12-41-42.gh-issue-91349.8eTOCP.rst b/Misc/NEWS.d/next/Library/2025-03-19-12-41-42.gh-issue-91349.8eTOCP.rst deleted file mode 100644 index 0e866fa4ef6169..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-03-19-12-41-42.gh-issue-91349.8eTOCP.rst +++ /dev/null @@ -1,3 +0,0 @@ -Adjust default ``compressionlevel=`` to 6 (down from 9) in :mod:`gzip` and :mod:`tarfile`. -It is the default level used by most compression tools and a better -tradeoff between speed and performance. diff --git a/Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst b/Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst deleted file mode 100644 index d9e2eae02dce1f..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst +++ /dev/null @@ -1 +0,0 @@ -Fix formatting issues in :func:`json.dump` when both *indent* and *skipkeys* are used. diff --git a/Misc/NEWS.d/next/Library/2025-04-07-09-53-54.gh-issue-87790.6nj3zQ.rst b/Misc/NEWS.d/next/Library/2025-04-07-09-53-54.gh-issue-87790.6nj3zQ.rst deleted file mode 100644 index cf80c71271bbd1..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-07-09-53-54.gh-issue-87790.6nj3zQ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Support underscore and comma as thousands separators in the fractional part -for :class:`~decimal.Decimal`'s formatting. Patch by Sergey B Kirpichev. diff --git a/Misc/NEWS.d/next/Library/2025-04-07-10-20-16.gh-issue-87790.X2SjJe.rst b/Misc/NEWS.d/next/Library/2025-04-07-10-20-16.gh-issue-87790.X2SjJe.rst deleted file mode 100644 index be2a30d69cab45..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-07-10-20-16.gh-issue-87790.X2SjJe.rst +++ /dev/null @@ -1,2 +0,0 @@ -Support underscore and comma as thousands separators in the fractional part -for :class:`~fractions.Fraction`'s formatting. Patch by Sergey B Kirpichev. diff --git a/Misc/NEWS.d/next/Library/2025-04-08-07-25-10.gh-issue-107583.JGfbhq.rst b/Misc/NEWS.d/next/Library/2025-04-08-07-25-10.gh-issue-107583.JGfbhq.rst deleted file mode 100644 index 4235612627341b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-08-07-25-10.gh-issue-107583.JGfbhq.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix :class:`!Flag` inversion when flag set has missing values -(:class:`!IntFlag` still flips all bits); fix negative assigned values -during flag creation (both :class:`!Flag` and :class:`!IntFlag` ignore -missing values). diff --git a/Misc/NEWS.d/next/Library/2025-04-16-21-02-57.gh-issue-132551.Psa7pL.rst b/Misc/NEWS.d/next/Library/2025-04-16-21-02-57.gh-issue-132551.Psa7pL.rst deleted file mode 100644 index c8743d49ca01d2..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-16-21-02-57.gh-issue-132551.Psa7pL.rst +++ /dev/null @@ -1 +0,0 @@ -Make :class:`io.BytesIO` safe in :term:`free-threaded <free threading>` build. diff --git a/Misc/NEWS.d/next/Library/2025-04-21-00-58-04.gh-issue-127081.3DCl92.rst b/Misc/NEWS.d/next/Library/2025-04-21-00-58-04.gh-issue-127081.3DCl92.rst deleted file mode 100644 index a99669a1bc021a..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-21-00-58-04.gh-issue-127081.3DCl92.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix libc thread safety issues with :mod:`pwd` by locking access to -``getpwall``. diff --git a/Misc/NEWS.d/next/Library/2025-04-21-01-03-15.gh-issue-127081.WXRliX.rst b/Misc/NEWS.d/next/Library/2025-04-21-01-03-15.gh-issue-127081.WXRliX.rst deleted file mode 100644 index 63fed60ced03c5..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-21-01-03-15.gh-issue-127081.WXRliX.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix libc thread safety issues with :mod:`os` by replacing ``getlogin`` with -``getlogin_r`` re-entrant version. diff --git a/Misc/NEWS.d/next/Library/2025-04-21-01-05-14.gh-issue-127081.Egrpq7.rst b/Misc/NEWS.d/next/Library/2025-04-21-01-05-14.gh-issue-127081.Egrpq7.rst deleted file mode 100644 index 30643673bf9a3f..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-21-01-05-14.gh-issue-127081.Egrpq7.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix libc thread safety issues with :mod:`dbm` by performing stateful -operations in critical sections. diff --git a/Misc/NEWS.d/next/Library/2025-04-22-21-00-23.gh-issue-123471.asOLA2.rst b/Misc/NEWS.d/next/Library/2025-04-22-21-00-23.gh-issue-123471.asOLA2.rst deleted file mode 100644 index a4b4b6d2c23d49..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-22-21-00-23.gh-issue-123471.asOLA2.rst +++ /dev/null @@ -1 +0,0 @@ -Make concurrent iterations over :class:`itertools.combinations` and :class:`itertools.product` safe under free-threading. diff --git a/Misc/NEWS.d/next/Library/2025-04-25-11-48-00.gh-issue-122781.ajsdns.rst b/Misc/NEWS.d/next/Library/2025-04-25-11-48-00.gh-issue-122781.ajsdns.rst deleted file mode 100644 index 5a9a0cdf7986dc..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-25-11-48-00.gh-issue-122781.ajsdns.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix ``%z`` directive in :func:`datetime.datetime.strptime` to allow for no provided -offset as was documented. diff --git a/Misc/NEWS.d/next/Library/2025-04-25-11-53-37.gh-issue-95380.7dvPe-.rst b/Misc/NEWS.d/next/Library/2025-04-25-11-53-37.gh-issue-95380.7dvPe-.rst deleted file mode 100644 index 8dcc6190cfdcc7..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-25-11-53-37.gh-issue-95380.7dvPe-.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`fcntl.fcntl` and :func:`fcntl.ioctl`: Remove the 1024 bytes limit -on the size of not mutated bytes-like argument. diff --git a/Misc/NEWS.d/next/Library/2025-04-25-16-06-53.gh-issue-132908.wV5rja.rst b/Misc/NEWS.d/next/Library/2025-04-25-16-06-53.gh-issue-132908.wV5rja.rst deleted file mode 100644 index e33b061bb9ba1f..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-25-16-06-53.gh-issue-132908.wV5rja.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :func:`math.isnormal` and :func:`math.issubnormal` functions. Patch by -Sergey B Kirpichev. diff --git a/Misc/NEWS.d/next/Library/2025-04-26-15-50-12.gh-issue-133009.etBuz5.rst b/Misc/NEWS.d/next/Library/2025-04-26-15-50-12.gh-issue-133009.etBuz5.rst deleted file mode 100644 index 1f7155c6a40d00..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-26-15-50-12.gh-issue-133009.etBuz5.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`xml.etree.ElementTree`: Fix a crash in :meth:`Element.__deepcopy__ -<object.__deepcopy__>` when the element is concurrently mutated. -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-04-29-11-48-46.gh-issue-132876.lyTQGZ.rst b/Misc/NEWS.d/next/Library/2025-04-29-11-48-46.gh-issue-132876.lyTQGZ.rst deleted file mode 100644 index cb3ca3321e3d26..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-29-11-48-46.gh-issue-132876.lyTQGZ.rst +++ /dev/null @@ -1,4 +0,0 @@ -``ldexp()`` on Windows doesn't round subnormal results before Windows 11, -but should. Python's :func:`math.ldexp` wrapper now does round them, so -results may change slightly, in rare cases of very small results, on -Windows versions before 11. diff --git a/Misc/NEWS.d/next/Library/2025-04-30-19-32-18.gh-issue-132969.EagQ3G.rst b/Misc/NEWS.d/next/Library/2025-04-30-19-32-18.gh-issue-132969.EagQ3G.rst deleted file mode 100644 index 7364c425941233..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-30-19-32-18.gh-issue-132969.EagQ3G.rst +++ /dev/null @@ -1,7 +0,0 @@ -Prevent the :class:`~concurrent.futures.ProcessPoolExecutor` executor thread, -which remains running when :meth:`shutdown(wait=False) -<concurrent.futures.Executor.shutdown>`, from -attempting to adjust the pool's worker processes after the object state has already been reset during shutdown. -A combination of conditions, including a worker process having terminated abormally, -resulted in an exception and a potential hang when the still-running executor thread -attempted to replace dead workers within the pool. diff --git a/Misc/NEWS.d/next/Library/2025-05-01-10-56-44.gh-issue-132813.rKurvp.rst b/Misc/NEWS.d/next/Library/2025-05-01-10-56-44.gh-issue-132813.rKurvp.rst deleted file mode 100644 index 55608528a4564b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-01-10-56-44.gh-issue-132813.rKurvp.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve error messages for incorrect types and values of :class:`csv.Dialect` -attributes. diff --git a/Misc/NEWS.d/next/Library/2025-05-01-16-03-11.gh-issue-133017.k7RLQp.rst b/Misc/NEWS.d/next/Library/2025-05-01-16-03-11.gh-issue-133017.k7RLQp.rst deleted file mode 100644 index 1b5bf74fb47e33..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-01-16-03-11.gh-issue-133017.k7RLQp.rst +++ /dev/null @@ -1,4 +0,0 @@ -Improve the error message of :func:`multiprocessing.sharedctypes.Array`, -:func:`multiprocessing.sharedctypes.RawArray`, :func:`multiprocessing.sharedctypes.Value` and -:func:`multiprocessing.sharedctypes.RawValue` when an invalid typecode is passed. Patch -by Tomas Roun diff --git a/Misc/NEWS.d/next/Library/2025-05-01-16-44-16.gh-issue-72902.19qwJW.rst b/Misc/NEWS.d/next/Library/2025-05-01-16-44-16.gh-issue-72902.19qwJW.rst new file mode 100644 index 00000000000000..2b7d8e44840894 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-01-16-44-16.gh-issue-72902.19qwJW.rst @@ -0,0 +1,3 @@ +Optimize (~x1.4 speedup) :meth:`fractions.Fraction.from_decimal` and +:meth:`fractions.Fraction.from_float` for :class:`~decimal.Decimal` and +:class:`float` inputs, respectively. Patch by Sergey B Kirpichev. diff --git a/Misc/NEWS.d/next/Library/2025-05-04-17-04-55.gh-issue-132493.huirKi.rst b/Misc/NEWS.d/next/Library/2025-05-04-17-04-55.gh-issue-132493.huirKi.rst deleted file mode 100644 index ad06ee6b7a2757..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-04-17-04-55.gh-issue-132493.huirKi.rst +++ /dev/null @@ -1,2 +0,0 @@ -Avoid accessing ``__annotations__`` unnecessarily in -:func:`inspect.signature`. diff --git a/Misc/NEWS.d/next/Library/2025-05-05-03-14-08.gh-issue-133390.AuTggn.rst b/Misc/NEWS.d/next/Library/2025-05-05-03-14-08.gh-issue-133390.AuTggn.rst deleted file mode 100644 index 943e4addebcc9e..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-05-03-14-08.gh-issue-133390.AuTggn.rst +++ /dev/null @@ -1,2 +0,0 @@ -Support keyword completion in the :mod:`sqlite3` command-line interface and add -:data:`sqlite3.SQLITE_KEYWORDS` constant. diff --git a/Misc/NEWS.d/next/Library/2025-05-05-10-41-41.gh-issue-133253.J5-xDD.rst b/Misc/NEWS.d/next/Library/2025-05-05-10-41-41.gh-issue-133253.J5-xDD.rst deleted file mode 100644 index 7009ca258bcabd..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-05-10-41-41.gh-issue-133253.J5-xDD.rst +++ /dev/null @@ -1 +0,0 @@ -Fix thread-safety issues in :mod:`linecache`. diff --git a/Misc/NEWS.d/next/Library/2025-05-05-18-50-00.gh-issue-133447.ajshdb.rst b/Misc/NEWS.d/next/Library/2025-05-05-18-50-00.gh-issue-133447.ajshdb.rst deleted file mode 100644 index f453690cab2cb3..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-05-18-50-00.gh-issue-133447.ajshdb.rst +++ /dev/null @@ -1 +0,0 @@ -Add basic color to :mod:`sqlite3` CLI interface. diff --git a/Misc/NEWS.d/next/Library/2025-05-05-22-11-24.gh-issue-133439.LpmyFz.rst b/Misc/NEWS.d/next/Library/2025-05-05-22-11-24.gh-issue-133439.LpmyFz.rst deleted file mode 100644 index e0a3ce98bf7158..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-05-22-11-24.gh-issue-133439.LpmyFz.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix dot commands with trailing spaces are mistaken for multi-line SQL -statements in the sqlite3 command-line interface. diff --git a/Misc/NEWS.d/next/Library/2025-05-06-14-44-55.gh-issue-133517.Ca6NgW.rst b/Misc/NEWS.d/next/Library/2025-05-06-14-44-55.gh-issue-133517.Ca6NgW.rst deleted file mode 100644 index 7c53fc484a84ac..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-06-14-44-55.gh-issue-133517.Ca6NgW.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove :func:`os.listdrives`, :func:`os.listvolumes` and :func:`os.listmounts` -in non Windows desktop builds since the underlying functionality is missing. diff --git a/Misc/NEWS.d/next/Library/2025-05-06-22-54-37.gh-issue-133551.rfy1tJ.rst b/Misc/NEWS.d/next/Library/2025-05-06-22-54-37.gh-issue-133551.rfy1tJ.rst deleted file mode 100644 index 7fedc0818dc469..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-06-22-54-37.gh-issue-133551.rfy1tJ.rst +++ /dev/null @@ -1,2 +0,0 @@ -Support t-strings (:pep:`750`) in :mod:`annotationlib`. Patch by Jelle -Zijlstra. diff --git a/Misc/NEWS.d/next/Library/2025-05-07-13-31-06.gh-issue-92897.ubeqGE.rst b/Misc/NEWS.d/next/Library/2025-05-07-13-31-06.gh-issue-92897.ubeqGE.rst deleted file mode 100644 index 647166bfcf84c8..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-07-13-31-06.gh-issue-92897.ubeqGE.rst +++ /dev/null @@ -1,2 +0,0 @@ -Removed the ``check_home`` parameter from :func:`sysconfig.is_python_build`, -deprecated since Python 3.12. diff --git a/Misc/NEWS.d/next/Library/2025-05-07-14-36-30.gh-issue-133577.BggPk9.rst b/Misc/NEWS.d/next/Library/2025-05-07-14-36-30.gh-issue-133577.BggPk9.rst deleted file mode 100644 index 9d056983439e4b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-07-14-36-30.gh-issue-133577.BggPk9.rst +++ /dev/null @@ -1 +0,0 @@ -Add parameter ``formatter`` to :func:`logging.basicConfig`. diff --git a/Misc/NEWS.d/next/Library/2025-05-07-19-16-41.gh-issue-133581.kERUCJ.rst b/Misc/NEWS.d/next/Library/2025-05-07-19-16-41.gh-issue-133581.kERUCJ.rst deleted file mode 100644 index 3749904cd9b8f1..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-07-19-16-41.gh-issue-133581.kERUCJ.rst +++ /dev/null @@ -1,4 +0,0 @@ -Improve unparsing of t-strings in :func:`ast.unparse` and ``from __future__ -import annotations``. Empty t-strings now round-trip correctly and -formatting in interpolations is preserved. -Patch by Jelle Zijlstra. diff --git a/Misc/NEWS.d/next/Library/2025-05-07-22-15-15.gh-issue-133595.c3U88r.rst b/Misc/NEWS.d/next/Library/2025-05-07-22-15-15.gh-issue-133595.c3U88r.rst deleted file mode 100644 index a61c4bf1913ca6..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-07-22-15-15.gh-issue-133595.c3U88r.rst +++ /dev/null @@ -1,7 +0,0 @@ -Clean up :class:`sqlite3.Connection` APIs. All parameters of -:func:`sqlite3.connect` except *database* are now keyword-only. The first -three parameters of methods :meth:`~sqlite3.Connection.create_function` and -:meth:`~sqlite3.Connection.create_aggregate` are now positional-only. The -first parameter of methods :meth:`~sqlite3.Connection.set_authorizer`, -:meth:`~sqlite3.Connection.set_progress_handler` and -:meth:`~sqlite3.Connection.set_trace_callback` is now positional-only. diff --git a/Misc/NEWS.d/next/Library/2025-05-08-13-43-19.gh-issue-133489.9eGS1Z.rst b/Misc/NEWS.d/next/Library/2025-05-08-13-43-19.gh-issue-133489.9eGS1Z.rst deleted file mode 100644 index 0c07beb76938f0..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-08-13-43-19.gh-issue-133489.9eGS1Z.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`random.getrandbits` can now generate more that 2\ :sup:`31` bits. -:func:`random.randbytes` can now generate more that 256 MiB. diff --git a/Misc/NEWS.d/next/Library/2025-05-09-08-49-03.gh-issue-133701.KI8tGz.rst b/Misc/NEWS.d/next/Library/2025-05-09-08-49-03.gh-issue-133701.KI8tGz.rst deleted file mode 100644 index 163d9b331d1f8a..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-09-08-49-03.gh-issue-133701.KI8tGz.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix bug where :class:`typing.TypedDict` classes defined under ``from -__future__ import annotations`` and inheriting from another ``TypedDict`` -had an incorrect ``__annotations__`` attribute. diff --git a/Misc/NEWS.d/next/Library/2025-05-09-09-10-34.gh-issue-130328.s9h4By.rst b/Misc/NEWS.d/next/Library/2025-05-09-09-10-34.gh-issue-130328.s9h4By.rst deleted file mode 100644 index 00b556c6a336ee..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-09-09-10-34.gh-issue-130328.s9h4By.rst +++ /dev/null @@ -1,2 +0,0 @@ -Speedup pasting in ``PyREPL`` on Windows in a legacy console. Patch by Chris -Eibl. diff --git a/Misc/NEWS.d/next/Library/2025-05-09-15-50-00.gh-issue-77057.fV8SU-.rst b/Misc/NEWS.d/next/Library/2025-05-09-15-50-00.gh-issue-77057.fV8SU-.rst deleted file mode 100644 index 42107de75c7d29..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-09-15-50-00.gh-issue-77057.fV8SU-.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix handling of invalid markup declarations in -:class:`html.parser.HTMLParser`. diff --git a/Misc/NEWS.d/next/Library/2025-05-09-18-29-25.gh-issue-133684.Y1DFSt.rst b/Misc/NEWS.d/next/Library/2025-05-09-18-29-25.gh-issue-133684.Y1DFSt.rst deleted file mode 100644 index 0cb1bc237a12c9..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-09-18-29-25.gh-issue-133684.Y1DFSt.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix bug where :func:`annotationlib.get_annotations` would return the wrong -result for certain classes that are part of a class hierarchy where ``from -__future__ import annotations`` is used. diff --git a/Misc/NEWS.d/next/Library/2025-05-09-19-05-24.gh-issue-133783.1voCnR.rst b/Misc/NEWS.d/next/Library/2025-05-09-19-05-24.gh-issue-133783.1voCnR.rst deleted file mode 100644 index 62e742df17954e..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-09-19-05-24.gh-issue-133783.1voCnR.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix bug with applying :func:`copy.replace` to :mod:`ast` objects. Attributes -that default to ``None`` were incorrectly treated as required for manually -created AST nodes. diff --git a/Misc/NEWS.d/next/Library/2025-05-09-20-59-24.gh-issue-132641.3qTw44.rst b/Misc/NEWS.d/next/Library/2025-05-09-20-59-24.gh-issue-132641.3qTw44.rst deleted file mode 100644 index 419ff19d9c009a..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-09-20-59-24.gh-issue-132641.3qTw44.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a race in :func:`functools.lru_cache` under free-threading. diff --git a/Misc/NEWS.d/next/Library/2025-05-10-11-04-47.gh-issue-133810.03WhnK.rst b/Misc/NEWS.d/next/Library/2025-05-10-11-04-47.gh-issue-133810.03WhnK.rst deleted file mode 100644 index 4073974e364a1c..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-10-11-04-47.gh-issue-133810.03WhnK.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove :class:`!http.server.CGIHTTPRequestHandler` and ``--cgi`` flag from the -:program:`python -m http.server` command-line interface. They were -deprecated in Python 3.13. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-10-12-06-55.gh-issue-133653.Gb2aG4.rst b/Misc/NEWS.d/next/Library/2025-05-10-12-06-55.gh-issue-133653.Gb2aG4.rst deleted file mode 100644 index 56fb1dc31dc22f..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-10-12-06-55.gh-issue-133653.Gb2aG4.rst +++ /dev/null @@ -1,7 +0,0 @@ -Fix :class:`argparse.ArgumentParser` with the *formatter_class* argument. -Fix TypeError when *formatter_class* is a custom subclass of -:class:`!HelpFormatter`. -Fix TypeError when *formatter_class* is not a subclass of -:class:`!HelpFormatter` and non-standard *prefix_char* is used. -Fix support of colorizing when *formatter_class* is not a subclass of -:class:`!HelpFormatter`. diff --git a/Misc/NEWS.d/next/Library/2025-05-10-12-07-54.gh-issue-133817.4GMtKV.rst b/Misc/NEWS.d/next/Library/2025-05-10-12-07-54.gh-issue-133817.4GMtKV.rst deleted file mode 100644 index 326e767de5f532..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-10-12-07-54.gh-issue-133817.4GMtKV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove support for creating :class:`~typing.NamedTuple` classes via the -undocumented keyword argument syntax. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-11-08-48-55.gh-issue-133823.F8udQy.rst b/Misc/NEWS.d/next/Library/2025-05-11-08-48-55.gh-issue-133823.F8udQy.rst deleted file mode 100644 index 67b44ac3ef319d..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-11-08-48-55.gh-issue-133823.F8udQy.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove support for ``TD = TypedDict("TD")`` and ``TD = TypedDict("TD", None)`` -calls for constructing :class:`typing.TypedDict` objects with zero field. -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-11-10-01-48.gh-issue-133866.g3dHP_.rst b/Misc/NEWS.d/next/Library/2025-05-11-10-01-48.gh-issue-133866.g3dHP_.rst deleted file mode 100644 index 00f13c9a305eb5..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-11-10-01-48.gh-issue-133866.g3dHP_.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove the undocumented function :func:`!ctypes.SetPointerType`, -which has been deprecated since Python 3.13. -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-11-10-28-11.gh-issue-133873.H03nov.rst b/Misc/NEWS.d/next/Library/2025-05-11-10-28-11.gh-issue-133873.H03nov.rst deleted file mode 100644 index 79f630b2418e70..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-11-10-28-11.gh-issue-133873.H03nov.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove the deprecated ``getmark()``, ``setmark()`` and ``getmarkers()`` -methods of the :class:`~wave.Wave_read` and :class:`~wave.Wave_write` -classes, which were deprecated since Python 3.13. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-11-11-39-05.gh-issue-133875.pUar3l.rst b/Misc/NEWS.d/next/Library/2025-05-11-11-39-05.gh-issue-133875.pUar3l.rst deleted file mode 100644 index b4a2b0336370bc..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-11-11-39-05.gh-issue-133875.pUar3l.rst +++ /dev/null @@ -1,2 +0,0 @@ -Removed deprecated :meth:`!pathlib.PurePath.is_reserved`. Use -:func:`os.path.isreserved` to detect reserved paths on Windows. diff --git a/Misc/NEWS.d/next/Library/2025-05-11-12-56-52.gh-issue-133604.kFxhc8.rst b/Misc/NEWS.d/next/Library/2025-05-11-12-56-52.gh-issue-133604.kFxhc8.rst deleted file mode 100644 index 526ac38f09bab3..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-11-12-56-52.gh-issue-133604.kFxhc8.rst +++ /dev/null @@ -1 +0,0 @@ -Remove :func:`!platform.java_ver` which was deprecated since Python 3.13. diff --git a/Misc/NEWS.d/next/Library/2025-05-12-06-52-10.gh-issue-133925.elInBY.rst b/Misc/NEWS.d/next/Library/2025-05-12-06-52-10.gh-issue-133925.elInBY.rst deleted file mode 100644 index 328e28abc3b42b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-12-06-52-10.gh-issue-133925.elInBY.rst +++ /dev/null @@ -1 +0,0 @@ -Make the private class ``typing._UnionGenericAlias`` hashable. diff --git a/Misc/NEWS.d/next/Library/2025-05-12-20-38-57.gh-issue-133960.Aee79f.rst b/Misc/NEWS.d/next/Library/2025-05-12-20-38-57.gh-issue-133960.Aee79f.rst deleted file mode 100644 index 66e8483b25bc37..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-12-20-38-57.gh-issue-133960.Aee79f.rst +++ /dev/null @@ -1,3 +0,0 @@ -Simplify and improve :func:`typing.evaluate_forward_ref`. It now no longer -raises errors on certain invalid types. In several situations, it is now -able to evaluate forward references that were previously unsupported. diff --git a/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst b/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst deleted file mode 100644 index 714d707f488709..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-13-18-21-59.gh-issue-71253.-3Sf_K.rst +++ /dev/null @@ -1,3 +0,0 @@ -Raise :exc:`ValueError` in :func:`open` if *opener* returns a negative -file-descriptor in the Python implementation of :mod:`io` to match the -C implementation. diff --git a/Misc/NEWS.d/next/Library/2025-05-13-18-54-56.gh-issue-133970.6G-Oi6.rst b/Misc/NEWS.d/next/Library/2025-05-13-18-54-56.gh-issue-133970.6G-Oi6.rst deleted file mode 100644 index ddf456d3939056..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-13-18-54-56.gh-issue-133970.6G-Oi6.rst +++ /dev/null @@ -1,2 +0,0 @@ -Make :class:`!string.templatelib.Template` and -:class:`!string.templatelib.Interpolation` generic. diff --git a/Misc/NEWS.d/next/Library/2025-05-15-00-27-09.gh-issue-134004.e8k4-R.rst b/Misc/NEWS.d/next/Library/2025-05-15-00-27-09.gh-issue-134004.e8k4-R.rst deleted file mode 100644 index a9a56d9239b305..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-15-00-27-09.gh-issue-134004.e8k4-R.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`shelve` as well as underlying :mod:`!dbm.dumb` and :mod:`!dbm.sqlite` now have :meth:`!reorganize` methods to -recover unused free space previously occupied by deleted entries. diff --git a/Misc/NEWS.d/next/Library/2025-05-15-14-27-01.gh-issue-134062.fRbJet.rst b/Misc/NEWS.d/next/Library/2025-05-15-14-27-01.gh-issue-134062.fRbJet.rst deleted file mode 100644 index f62a3ec480193d..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-15-14-27-01.gh-issue-134062.fRbJet.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`ipaddress`: fix collisions in :meth:`~object.__hash__` for -:class:`~ipaddress.IPv4Network` and :class:`~ipaddress.IPv6Network` -objects. diff --git a/Misc/NEWS.d/next/Library/2025-05-16-12-40-37.gh-issue-132124.T_5Odx.rst b/Misc/NEWS.d/next/Library/2025-05-16-12-40-37.gh-issue-132124.T_5Odx.rst deleted file mode 100644 index acf3577ece4e9c..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-16-12-40-37.gh-issue-132124.T_5Odx.rst +++ /dev/null @@ -1,6 +0,0 @@ -On POSIX-compliant systems, :func:`!multiprocessing.util.get_temp_dir` now -ignores :envvar:`TMPDIR` (and similar environment variables) if the path -length of ``AF_UNIX`` socket files exceeds the platform-specific maximum -length when using the :ref:`forkserver -<multiprocessing-start-method-forkserver>` start method. Patch by Bénédikt -Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-16-20-10-25.gh-issue-134098.YyTkKr.rst b/Misc/NEWS.d/next/Library/2025-05-16-20-10-25.gh-issue-134098.YyTkKr.rst deleted file mode 100644 index 32eff5371c4f1e..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-16-20-10-25.gh-issue-134098.YyTkKr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix handling paths that end with a percent-encoded slash (``%2f`` or -``%2F``) in :class:`http.server.SimpleHTTPRequestHandler`. diff --git a/Misc/NEWS.d/next/Library/2025-05-17-12-40-12.gh-issue-133889.Eh-zO4.rst b/Misc/NEWS.d/next/Library/2025-05-17-12-40-12.gh-issue-133889.Eh-zO4.rst deleted file mode 100644 index 58b213e29f98d8..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-17-12-40-12.gh-issue-133889.Eh-zO4.rst +++ /dev/null @@ -1,3 +0,0 @@ -The generated directory listing page in -:class:`http.server.SimpleHTTPRequestHandler` now only shows the decoded -path component of the requested URL, and not the query and fragment. diff --git a/Misc/NEWS.d/next/Library/2025-05-17-13-46-20.gh-issue-134097.fgkjE1.rst b/Misc/NEWS.d/next/Library/2025-05-17-13-46-20.gh-issue-134097.fgkjE1.rst deleted file mode 100644 index 0b388d9db380f5..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-17-13-46-20.gh-issue-134097.fgkjE1.rst +++ /dev/null @@ -1 +0,0 @@ -Fix interaction of the new :term:`REPL` and :option:`-X showrefcount <-X>` command line option. diff --git a/Misc/NEWS.d/next/Library/2025-05-17-18-08-35.gh-issue-133890.onn9_X.rst b/Misc/NEWS.d/next/Library/2025-05-17-18-08-35.gh-issue-133890.onn9_X.rst deleted file mode 100644 index 44565a5424e65b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-17-18-08-35.gh-issue-133890.onn9_X.rst +++ /dev/null @@ -1,2 +0,0 @@ -The :mod:`tarfile` module now handles :exc:`UnicodeEncodeError` in the same -way as :exc:`OSError` when cannot extract a member. diff --git a/Misc/NEWS.d/next/Library/2025-05-17-20-23-57.gh-issue-133982.smS7au.rst b/Misc/NEWS.d/next/Library/2025-05-17-20-23-57.gh-issue-133982.smS7au.rst deleted file mode 100644 index a6753145981181..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-17-20-23-57.gh-issue-133982.smS7au.rst +++ /dev/null @@ -1,3 +0,0 @@ -Emit :exc:`RuntimeWarning` in the Python implementation of :mod:`io` when -the :term:`file-like object <file object>` is not closed explicitly in the -presence of multiple I/O layers. diff --git a/Misc/NEWS.d/next/Library/2025-05-18-07-25-15.gh-issue-134173.53oOoF.rst b/Misc/NEWS.d/next/Library/2025-05-18-07-25-15.gh-issue-134173.53oOoF.rst deleted file mode 100644 index 57fba5e21a3e9a..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-18-07-25-15.gh-issue-134173.53oOoF.rst +++ /dev/null @@ -1,3 +0,0 @@ -Speed up :mod:`asyncio` performance of transferring state from thread -pool :class:`concurrent.futures.Future` by up to 4.4x. Patch by J. Nick -Koston. diff --git a/Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst b/Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst deleted file mode 100644 index c4a05965f73ece..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-18-12-23-07.gh-issue-134087.HilZWl.rst +++ /dev/null @@ -1,3 +0,0 @@ -Remove support for arbitrary positional or keyword arguments in the C -implementation of :class:`threading.RLock` objects. This was deprecated -since Python 3.14. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-18-12-48-39.gh-issue-62184.y11l10.rst b/Misc/NEWS.d/next/Library/2025-05-18-12-48-39.gh-issue-62184.y11l10.rst deleted file mode 100644 index 7bc994e57fb58f..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-18-12-48-39.gh-issue-62184.y11l10.rst +++ /dev/null @@ -1,2 +0,0 @@ -Remove import of C implementation of :class:`io.FileIO` from Python -implementation which has its own implementation diff --git a/Misc/NEWS.d/next/Library/2025-05-18-13-23-29.gh-issue-134168.hgx3Xg.rst b/Misc/NEWS.d/next/Library/2025-05-18-13-23-29.gh-issue-134168.hgx3Xg.rst deleted file mode 100644 index 5a0e20005db11e..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-18-13-23-29.gh-issue-134168.hgx3Xg.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`http.server`: Fix IPv6 address binding and -:option:`--directory <http.server --directory>` handling when using HTTPS. diff --git a/Misc/NEWS.d/next/Library/2025-05-18-23-46-21.gh-issue-134152.30HwbX.rst b/Misc/NEWS.d/next/Library/2025-05-18-23-46-21.gh-issue-134152.30HwbX.rst deleted file mode 100644 index 911a4a59ea6079..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-18-23-46-21.gh-issue-134152.30HwbX.rst +++ /dev/null @@ -1 +0,0 @@ -:mod:`email`: Fix parsing of email message ID with invalid domain. diff --git a/Misc/NEWS.d/next/Library/2025-05-19-10-32-11.gh-issue-134152.INJC2j.rst b/Misc/NEWS.d/next/Library/2025-05-19-10-32-11.gh-issue-134152.INJC2j.rst deleted file mode 100644 index 6da3d4147dd960..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-19-10-32-11.gh-issue-134152.INJC2j.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed :exc:`UnboundLocalError` that could occur during :mod:`email` header -parsing if an expected trailing delimiter is missing in some contexts. diff --git a/Misc/NEWS.d/next/Library/2025-05-19-15-05-24.gh-issue-134235.pz9PwV.rst b/Misc/NEWS.d/next/Library/2025-05-19-15-05-24.gh-issue-134235.pz9PwV.rst deleted file mode 100644 index a65df886919145..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-19-15-05-24.gh-issue-134235.pz9PwV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Updated tab completion on REPL to include builtin modules. Contributed by -Tom Wang, Hunter Young diff --git a/Misc/NEWS.d/next/Library/2025-05-19-15-30-00.gh-issue-132983.asdsfs.rst b/Misc/NEWS.d/next/Library/2025-05-19-15-30-00.gh-issue-132983.asdsfs.rst deleted file mode 100644 index 3893eeafa9c594..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-19-15-30-00.gh-issue-132983.asdsfs.rst +++ /dev/null @@ -1 +0,0 @@ -Add :mod:`!compression.zstd` version information to ``test.pythoninfo``. diff --git a/Misc/NEWS.d/next/Library/2025-05-19-17-27-21.gh-issue-80184.LOkbaw.rst b/Misc/NEWS.d/next/Library/2025-05-19-17-27-21.gh-issue-80184.LOkbaw.rst deleted file mode 100644 index 089268dc4c3e7b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-19-17-27-21.gh-issue-80184.LOkbaw.rst +++ /dev/null @@ -1 +0,0 @@ -The default queue size is now ``socket.SOMAXCONN`` for :class:`socketserver.TCPServer`. diff --git a/Misc/NEWS.d/next/Library/2025-05-19-18-12-42.gh-issue-88994.7avvVu.rst b/Misc/NEWS.d/next/Library/2025-05-19-18-12-42.gh-issue-88994.7avvVu.rst deleted file mode 100644 index 554a0c3bcb26bf..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-19-18-12-42.gh-issue-88994.7avvVu.rst +++ /dev/null @@ -1,3 +0,0 @@ -Change :func:`datetime.datetime.now` to half-even rounding for -consistency with :func:`datetime.datetime.fromtimestamp`. Patch by -John Keith Hohm. diff --git a/Misc/NEWS.d/next/Library/2025-05-19-20-29-35.gh-issue-133998.KmElUw.rst b/Misc/NEWS.d/next/Library/2025-05-19-20-29-35.gh-issue-133998.KmElUw.rst new file mode 100644 index 00000000000000..77d92628beefac --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-19-20-29-35.gh-issue-133998.KmElUw.rst @@ -0,0 +1,5 @@ +Fix :exc:`struct.error` exception when creating a file with +:class:`gzip.GzipFile` or compressing data with :func:`gzip.compress` +if the system time is outside the range 00:00:00 UTC, January 1, 1970 +through 06:28:15 UTC, February 7, 2106, or explicitly passed *mtime* +argument is outside the range ``0`` to ``2**32-1``. diff --git a/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst b/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst deleted file mode 100644 index f985872f3c975e..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-19-20-59-06.gh-issue-134209.anhTcF.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`curses`: The :meth:`curses.window.instr` and :meth:`curses.window.getstr` -methods now allocate their internal buffer on the heap instead of the stack; -in addition, the max buffer size is increased from 1023 to 2047. diff --git a/Misc/NEWS.d/next/Library/2025-05-19-21-08-25.gh-issue-134261.ravGYm.rst b/Misc/NEWS.d/next/Library/2025-05-19-21-08-25.gh-issue-134261.ravGYm.rst new file mode 100644 index 00000000000000..bf552fee814acb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-19-21-08-25.gh-issue-134261.ravGYm.rst @@ -0,0 +1 @@ +zip: On reproducible builds, ZipFile uses UTC instead of the local time when writing file datetimes to avoid underflows. diff --git a/Misc/NEWS.d/next/Library/2025-05-20-11-35-08.gh-issue-72902.jzEI-E.rst b/Misc/NEWS.d/next/Library/2025-05-20-11-35-08.gh-issue-72902.jzEI-E.rst deleted file mode 100644 index 932e751a32a043..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-20-11-35-08.gh-issue-72902.jzEI-E.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve speed (x1.1-1.8) of the :class:`~fractions.Fraction` constructor for -typical inputs (:class:`float`'s, :class:`~decimal.Decimal`'s or strings). diff --git a/Misc/NEWS.d/next/Library/2025-05-20-15-13-43.gh-issue-86802.trF7TM.rst b/Misc/NEWS.d/next/Library/2025-05-20-15-13-43.gh-issue-86802.trF7TM.rst deleted file mode 100644 index d3117b16f04436..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-20-15-13-43.gh-issue-86802.trF7TM.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed asyncio memory leak in cancelled shield tasks. For shielded tasks -where the shield was cancelled, log potential exceptions through the -exception handler. Contributed by Christian Harries. diff --git a/Misc/NEWS.d/next/Library/2025-05-20-19-16-30.gh-issue-134323.ZQZGvw.rst b/Misc/NEWS.d/next/Library/2025-05-20-19-16-30.gh-issue-134323.ZQZGvw.rst deleted file mode 100644 index 7982b52f77a172..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-20-19-16-30.gh-issue-134323.ZQZGvw.rst +++ /dev/null @@ -1 +0,0 @@ -Fix the :meth:`threading.RLock.locked` method. diff --git a/Misc/NEWS.d/next/Library/2025-05-20-21-45-58.gh-issue-90871.Gkvtp6.rst b/Misc/NEWS.d/next/Library/2025-05-20-21-45-58.gh-issue-90871.Gkvtp6.rst deleted file mode 100644 index 49397c9705ecfe..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-20-21-45-58.gh-issue-90871.Gkvtp6.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed an off by one error concerning the backlog parameter in -:meth:`~asyncio.loop.create_unix_server`. Contributed by Christian Harries. diff --git a/Misc/NEWS.d/next/Library/2025-05-22-13-10-32.gh-issue-114177.3TYUJ3.rst b/Misc/NEWS.d/next/Library/2025-05-22-13-10-32.gh-issue-114177.3TYUJ3.rst deleted file mode 100644 index c98fde5fb04f5c..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-22-13-10-32.gh-issue-114177.3TYUJ3.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :mod:`asyncio` to not close subprocess pipes which would otherwise error out when the event loop is already closed. diff --git a/Misc/NEWS.d/next/Library/2025-05-22-14-12-53.gh-issue-134451.M1rD-j.rst b/Misc/NEWS.d/next/Library/2025-05-22-14-12-53.gh-issue-134451.M1rD-j.rst deleted file mode 100644 index 3c8339f8842254..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-22-14-12-53.gh-issue-134451.M1rD-j.rst +++ /dev/null @@ -1 +0,0 @@ -Converted ``asyncio.tools.CycleFoundException`` from dataclass to a regular exception type. diff --git a/Misc/NEWS.d/next/Library/2025-05-22-18-14-13.gh-issue-134546.fjLVzK.rst b/Misc/NEWS.d/next/Library/2025-05-22-18-14-13.gh-issue-134546.fjLVzK.rst deleted file mode 100644 index eea897f59181c9..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-22-18-14-13.gh-issue-134546.fjLVzK.rst +++ /dev/null @@ -1 +0,0 @@ -Ensure :mod:`pdb` remote debugging script is readable by remote Python process. diff --git a/Misc/NEWS.d/next/Library/2025-05-23-10-15-36.gh-issue-134565.zmb66C.rst b/Misc/NEWS.d/next/Library/2025-05-23-10-15-36.gh-issue-134565.zmb66C.rst deleted file mode 100644 index 17d2b23b62d3c3..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-23-10-15-36.gh-issue-134565.zmb66C.rst +++ /dev/null @@ -1,3 +0,0 @@ -:func:`unittest.doModuleCleanups` no longer swallows all but first exception -raised in the cleanup code, but raises a :exc:`ExceptionGroup` if multiple -errors occurred. diff --git a/Misc/NEWS.d/next/Library/2025-05-23-20-01-52.gh-issue-134580.xnaJ70.rst b/Misc/NEWS.d/next/Library/2025-05-23-20-01-52.gh-issue-134580.xnaJ70.rst deleted file mode 100644 index 979d310d3cee58..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-23-20-01-52.gh-issue-134580.xnaJ70.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improved the styling of HTML diff pages generated by the -:class:`difflib.HtmlDiff` class, and migrated the output to the HTML5 -standard. diff --git a/Misc/NEWS.d/next/Library/2025-05-23-23-43-39.gh-issue-134582.9POq3l.rst b/Misc/NEWS.d/next/Library/2025-05-23-23-43-39.gh-issue-134582.9POq3l.rst deleted file mode 100644 index 23e1d5891b685f..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-23-23-43-39.gh-issue-134582.9POq3l.rst +++ /dev/null @@ -1 +0,0 @@ -Fix tokenize.untokenize() round-trip errors related to t-strings braces escaping diff --git a/Misc/NEWS.d/next/Library/2025-05-24-03-10-36.gh-issue-80334.z21cMa.rst b/Misc/NEWS.d/next/Library/2025-05-24-03-10-36.gh-issue-80334.z21cMa.rst deleted file mode 100644 index 228429516db416..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-24-03-10-36.gh-issue-80334.z21cMa.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`multiprocessing.freeze_support` now checks for work on any "spawn" -start method platform rather than only on Windows. diff --git a/Misc/NEWS.d/next/Library/2025-05-24-13-10-35.gh-issue-134210.0IuMY2.rst b/Misc/NEWS.d/next/Library/2025-05-24-13-10-35.gh-issue-134210.0IuMY2.rst deleted file mode 100644 index b440e8308db6a2..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-24-13-10-35.gh-issue-134210.0IuMY2.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`curses.window.getch` now correctly handles signals. Patch by Bénédikt -Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-25-11-02-05.gh-issue-134657.3YFhR9.rst b/Misc/NEWS.d/next/Library/2025-05-25-11-02-05.gh-issue-134657.3YFhR9.rst deleted file mode 100644 index 1bf8ee504ef180..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-25-11-02-05.gh-issue-134657.3YFhR9.rst +++ /dev/null @@ -1 +0,0 @@ -:mod:`asyncio`: Remove some private names from ``asyncio.__all__``. diff --git a/Misc/NEWS.d/next/Library/2025-05-25-13-46-37.gh-issue-134635.ZlPrlX.rst b/Misc/NEWS.d/next/Library/2025-05-25-13-46-37.gh-issue-134635.ZlPrlX.rst deleted file mode 100644 index 4cabbf2f896917..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-25-13-46-37.gh-issue-134635.ZlPrlX.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`zlib`: Allow to combine Adler-32 and CRC-32 checksums via -:func:`~zlib.adler32_combine` and :func:`~zlib.crc32_combine`. Patch by -Callum Attryde and Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-25-23-23-05.gh-issue-134151.13Wwsb.rst b/Misc/NEWS.d/next/Library/2025-05-25-23-23-05.gh-issue-134151.13Wwsb.rst deleted file mode 100644 index ecdde240b4aadc..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-25-23-23-05.gh-issue-134151.13Wwsb.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`email`: Fix :exc:`TypeError` in :func:`email.utils.decode_params` -when sorting :rfc:`2231` continuations that contain an unnumbered section. diff --git a/Misc/NEWS.d/next/Library/2025-05-26-11-01-54.gh-issue-134531.my1Fzt.rst b/Misc/NEWS.d/next/Library/2025-05-26-11-01-54.gh-issue-134531.my1Fzt.rst deleted file mode 100644 index ee5690df5c4193..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-26-11-01-54.gh-issue-134531.my1Fzt.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`!_hashlib`: Rename internal C functions for :class:`!_hashlib.HASH` -and :class:`!_hashlib.HASHXOF` objects. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-26-12-31-08.gh-issue-132710.ApU3TZ.rst b/Misc/NEWS.d/next/Library/2025-05-26-12-31-08.gh-issue-132710.ApU3TZ.rst deleted file mode 100644 index b7011517aa9db8..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-26-12-31-08.gh-issue-132710.ApU3TZ.rst +++ /dev/null @@ -1,3 +0,0 @@ -If possible, ensure that :func:`uuid.getnode` returns the same result even -across different processes. Previously, the result was constant only within -the same process. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-26-14-04-39.gh-issue-134696.P04xUa.rst b/Misc/NEWS.d/next/Library/2025-05-26-14-04-39.gh-issue-134696.P04xUa.rst deleted file mode 100644 index 282eb088b89503..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-26-14-04-39.gh-issue-134696.P04xUa.rst +++ /dev/null @@ -1,5 +0,0 @@ -Built-in HACL* and OpenSSL implementations of hash function constructors -now correctly accept the same *documented* named arguments. For instance, -:func:`~hashlib.md5` could be previously invoked as ``md5(data=data)`` -or ``md5(string=string)`` depending on the underlying implementation -but these calls were not compatible. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-26-17-06-40.gh-issue-134637.9-3zRL.rst b/Misc/NEWS.d/next/Library/2025-05-26-17-06-40.gh-issue-134637.9-3zRL.rst deleted file mode 100644 index 2a4d8725210834..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-26-17-06-40.gh-issue-134637.9-3zRL.rst +++ /dev/null @@ -1 +0,0 @@ -Fix performance regression in calling a :mod:`ctypes` function pointer in :term:`free threading`. diff --git a/Misc/NEWS.d/next/Library/2025-05-26-22-18-32.gh-issue-134771.RKXpLT.rst b/Misc/NEWS.d/next/Library/2025-05-26-22-18-32.gh-issue-134771.RKXpLT.rst deleted file mode 100644 index 4b70c6ef398793..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-26-22-18-32.gh-issue-134771.RKXpLT.rst +++ /dev/null @@ -1,2 +0,0 @@ -The ``time_clockid_converter()`` function now selects correct type for -``clockid_t`` on Cygwin which fixes a build error. diff --git a/Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst b/Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst deleted file mode 100644 index 129d5d9842576b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-27-11-13-51.gh-issue-133579.KY9M6S.rst +++ /dev/null @@ -1,8 +0,0 @@ -:ref:`curses.window <curses-window-objects>`: Consistently report failures -of curses C API calls in Window methods by raising a :exc:`curses.error`. -This affects :meth:`~curses.window.addch`, :meth:`~curses.window.addnstr`, -:meth:`~curses.window.addstr`, :meth:`~curses.window.border`, -:meth:`~curses.window.box`, :meth:`~curses.window.chgat`, -:meth:`~curses.window.getbkgd`, :meth:`~curses.window.inch`, -:meth:`~curses.window.insstr` and :meth:`~curses.window.insnstr`. -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst b/Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst deleted file mode 100644 index e0ef959f125d62..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-27-11-18-13.gh-issue-133579.ohtgdC.rst +++ /dev/null @@ -1,3 +0,0 @@ -:meth:`curses.window.refresh` and :meth:`curses.window.noutrefresh` now raise -a :exc:`TypeError` instead of :exc:`curses.error` when called with an incorrect -number of arguments for :ref:`pads <windows-and-pads>`. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-27-11-24-38.gh-issue-133579.WGPUC1.rst b/Misc/NEWS.d/next/Library/2025-05-27-11-24-38.gh-issue-133579.WGPUC1.rst deleted file mode 100644 index 552b7ca1a7187d..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-27-11-24-38.gh-issue-133579.WGPUC1.rst +++ /dev/null @@ -1,7 +0,0 @@ -:mod:`curses`: Consistently report failures of curses C API calls in -module-level methods by raising a :exc:`curses.error`. This affects -:func:`~curses.assume_default_colors`, :func:`~curses.baudrate`, -:func:`~curses.cbreak`, :func:`~curses.echo`, :func:`~curses.longname`, -:func:`~curses.initscr`, :func:`~curses.nl`, :func:`~curses.raw`, -:func:`~curses.termattrs`, :func:`~curses.termname` and :func:`~curses.unctrl`. -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-05-28-15-53-27.gh-issue-128840.Nur2pB.rst b/Misc/NEWS.d/next/Library/2025-05-28-15-53-27.gh-issue-128840.Nur2pB.rst deleted file mode 100644 index faff433aa4b86e..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-28-15-53-27.gh-issue-128840.Nur2pB.rst +++ /dev/null @@ -1 +0,0 @@ -Fix parsing long IPv6 addresses with embedded IPv4 address. diff --git a/Misc/NEWS.d/next/Library/2025-05-28-20-49-29.gh-issue-134857.dVYXVO.rst b/Misc/NEWS.d/next/Library/2025-05-28-20-49-29.gh-issue-134857.dVYXVO.rst deleted file mode 100644 index 92e38c0bb5ac87..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-28-20-49-29.gh-issue-134857.dVYXVO.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improve error report for :mod:`doctest`\ s run with :mod:`unittest`. Remove -:mod:`!doctest` module frames from tracebacks and redundant newline -character from a failure message. diff --git a/Misc/NEWS.d/next/Library/2025-05-29-06-53-40.gh-issue-134885.-_L22o.rst b/Misc/NEWS.d/next/Library/2025-05-29-06-53-40.gh-issue-134885.-_L22o.rst deleted file mode 100644 index 4b05d42c109d06..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-29-06-53-40.gh-issue-134885.-_L22o.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix possible crash in the :mod:`compression.zstd` module related to setting -parameter types. Patch by Jelle Zijlstra. diff --git a/Misc/NEWS.d/next/Library/2025-05-29-17-39-13.gh-issue-108885.MegCRA.rst b/Misc/NEWS.d/next/Library/2025-05-29-17-39-13.gh-issue-108885.MegCRA.rst deleted file mode 100644 index e37cf121f5f529..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-29-17-39-13.gh-issue-108885.MegCRA.rst +++ /dev/null @@ -1,3 +0,0 @@ -Run each example as a subtest in unit tests synthesized by -:func:`doctest.DocFileSuite` and :func:`doctest.DocTestSuite`. -Add the :meth:`doctest.DocTestRunner.report_skip` method. diff --git a/Misc/NEWS.d/next/Library/2025-05-30-09-46-21.gh-issue-134939.Pu3nnm.rst b/Misc/NEWS.d/next/Library/2025-05-30-09-46-21.gh-issue-134939.Pu3nnm.rst deleted file mode 100644 index 2bda69bff52156..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-30-09-46-21.gh-issue-134939.Pu3nnm.rst +++ /dev/null @@ -1 +0,0 @@ -Add the :mod:`concurrent.interpreters` module. See :pep:`734`. diff --git a/Misc/NEWS.d/next/Library/2025-05-30-13-07-29.gh-issue-134718.9Qvhxn.rst b/Misc/NEWS.d/next/Library/2025-05-30-13-07-29.gh-issue-134718.9Qvhxn.rst deleted file mode 100644 index 922ab168fdd095..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-30-13-07-29.gh-issue-134718.9Qvhxn.rst +++ /dev/null @@ -1,2 +0,0 @@ -:func:`ast.dump` now only omits ``None`` and ``[]`` values if they are -default values. diff --git a/Misc/NEWS.d/next/Library/2025-05-30-18-13-48.gh-issue-134718.5FEspx.rst b/Misc/NEWS.d/next/Library/2025-05-30-18-13-48.gh-issue-134718.5FEspx.rst deleted file mode 100644 index 06c1d5583be0bb..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-30-18-13-48.gh-issue-134718.5FEspx.rst +++ /dev/null @@ -1 +0,0 @@ -By default, omit optional ``Load()`` values in :func:`ast.dump`. diff --git a/Misc/NEWS.d/next/Library/2025-05-31-12-08-12.gh-issue-134970.lgSaxq.rst b/Misc/NEWS.d/next/Library/2025-05-31-12-08-12.gh-issue-134970.lgSaxq.rst deleted file mode 100644 index 20f53569ef4566..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-31-12-08-12.gh-issue-134970.lgSaxq.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix the "unknown action" exception in -:meth:`argparse.ArgumentParser.add_argument_group` to correctly replace the -action class. diff --git a/Misc/NEWS.d/next/Library/2025-05-31-15-49-46.gh-issue-134978.mXXuvW.rst b/Misc/NEWS.d/next/Library/2025-05-31-15-49-46.gh-issue-134978.mXXuvW.rst deleted file mode 100644 index e75ce1622d6396..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-05-31-15-49-46.gh-issue-134978.mXXuvW.rst +++ /dev/null @@ -1,7 +0,0 @@ -:mod:`hashlib`: Supporting the ``string`` keyword parameter in hash function -constructors such as :func:`~hashlib.new` or the direct hash-named constructors -such as :func:`~hashlib.md5` and :func:`~hashlib.sha256` is now deprecated and -slated for removal in Python 3.19. -Prefer passing the initial data as a positional argument for maximum backwards -compatibility. -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-01-14-18-48.gh-issue-135004.cq3-fp.rst b/Misc/NEWS.d/next/Library/2025-06-01-14-18-48.gh-issue-135004.cq3-fp.rst deleted file mode 100644 index 4c59b0f8e19e6b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-01-14-18-48.gh-issue-135004.cq3-fp.rst +++ /dev/null @@ -1,3 +0,0 @@ -Rewrite and cleanup the internal :mod:`!_blake2` module. Some exception -messages were changed but their types were left untouched. Patch by Bénédikt -Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-01-15-13-07.gh-issue-66234.Jw7OdC.rst b/Misc/NEWS.d/next/Library/2025-06-01-15-13-07.gh-issue-66234.Jw7OdC.rst deleted file mode 100644 index 1defb9a72e04e7..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-01-15-13-07.gh-issue-66234.Jw7OdC.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add the ``'m'`` flag for :func:`dbm.gnu.open` which allows to disable the -use of :manpage:`mmap(2)`. This may harm performance, but improve crash -tolerance. diff --git a/Misc/NEWS.d/next/Library/2025-06-02-14-28-30.gh-issue-130662.EIgIR8.rst b/Misc/NEWS.d/next/Library/2025-06-02-14-28-30.gh-issue-130662.EIgIR8.rst deleted file mode 100644 index e07200f9a3fbbd..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-02-14-28-30.gh-issue-130662.EIgIR8.rst +++ /dev/null @@ -1,3 +0,0 @@ -Accept leading zeros in precision and width fields for -:class:`~fractions.Fraction` formatting, for example ``format(Fraction(1, -3), '.016f')``. diff --git a/Misc/NEWS.d/next/Library/2025-06-02-14-36-28.gh-issue-130662.Gpr2GB.rst b/Misc/NEWS.d/next/Library/2025-06-02-14-36-28.gh-issue-130662.Gpr2GB.rst deleted file mode 100644 index d97d937376a9d0..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-02-14-36-28.gh-issue-130662.Gpr2GB.rst +++ /dev/null @@ -1,3 +0,0 @@ -+Accept leading zeros in precision and width fields for -+:class:`~decimal.Decimal` formatting, for example ``format(Decimal(1.25), -'.016f')``. diff --git a/Misc/NEWS.d/next/Library/2025-06-03-12-59-17.gh-issue-135069.xop30V.rst b/Misc/NEWS.d/next/Library/2025-06-03-12-59-17.gh-issue-135069.xop30V.rst deleted file mode 100644 index 1affb5e2aadab7..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-03-12-59-17.gh-issue-135069.xop30V.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix the "Invalid error handling" exception in -:class:`!encodings.idna.IncrementalDecoder` to correctly replace the -'errors' parameter. diff --git a/Misc/NEWS.d/next/Library/2025-06-06-17-34-18.gh-issue-133934.yT1r68.rst b/Misc/NEWS.d/next/Library/2025-06-06-17-34-18.gh-issue-133934.yT1r68.rst deleted file mode 100644 index 4de7b4cceca977..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-06-17-34-18.gh-issue-133934.yT1r68.rst +++ /dev/null @@ -1 +0,0 @@ -Improve :mod:`sqlite3` CLI's ``.help`` message. diff --git a/Misc/NEWS.d/next/Library/2025-06-08-10-22-22.gh-issue-135244.Y2SOTJ.rst b/Misc/NEWS.d/next/Library/2025-06-08-10-22-22.gh-issue-135244.Y2SOTJ.rst deleted file mode 100644 index 1f70358e64e2a0..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-08-10-22-22.gh-issue-135244.Y2SOTJ.rst +++ /dev/null @@ -1,4 +0,0 @@ -:mod:`uuid`: when the MAC address cannot be determined, the 48-bit node -ID is now generated with a cryptographically-secure pseudo-random number -generator (CSPRNG) as per :rfc:`RFC 9562, §6.10.3 <9562#section-6.10-3>`. -This affects :func:`~uuid.uuid1` and :func:`~uuid.uuid6`. diff --git a/Misc/NEWS.d/next/Library/2025-06-08-11-11-07.gh-issue-135234.wJCdh0.rst b/Misc/NEWS.d/next/Library/2025-06-08-11-11-07.gh-issue-135234.wJCdh0.rst deleted file mode 100644 index e1c11e46735f0b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-08-11-11-07.gh-issue-135234.wJCdh0.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`hashlib`: improve exception messages when an OpenSSL function failed. -When memory allocation fails on OpenSSL's side, a :exc:`MemoryError` is -raised instead of a :exc:`ValueError`. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-08-14-50-34.gh-issue-135276.ZLUhV1.rst b/Misc/NEWS.d/next/Library/2025-06-08-14-50-34.gh-issue-135276.ZLUhV1.rst deleted file mode 100644 index a8fbd48d08aa37..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-08-14-50-34.gh-issue-135276.ZLUhV1.rst +++ /dev/null @@ -1,6 +0,0 @@ -Synchronized zipfile.Path with zipp 3.23, including improved performance of -:meth:`zipfile.Path.open` for non-reading modes, rely on -:func:`functools.cached_property` to cache values on the instance. Rely on -``save_method_args`` to save the initialization method arguments. Fixed -``.name``, ``.stem`` and other basename-based properties on Windows when -working with a zipfile on disk. diff --git a/Misc/NEWS.d/next/Library/2025-06-09-10-16-55.gh-issue-121914.G6Avkq.rst b/Misc/NEWS.d/next/Library/2025-06-09-10-16-55.gh-issue-121914.G6Avkq.rst deleted file mode 100644 index a1314a9cb7f943..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-09-10-16-55.gh-issue-121914.G6Avkq.rst +++ /dev/null @@ -1,3 +0,0 @@ -Changed the names of the symbol tables for lambda expressions and generator -expressions to "<lambda>" and "<genexpr>" respectively to avoid conflicts -with user-defined names. diff --git a/Misc/NEWS.d/next/Library/2025-06-10-00-42-30.gh-issue-135321.UHh9jT.rst b/Misc/NEWS.d/next/Library/2025-06-10-00-42-30.gh-issue-135321.UHh9jT.rst deleted file mode 100644 index 9e63d8e28b7696..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-10-00-42-30.gh-issue-135321.UHh9jT.rst +++ /dev/null @@ -1 +0,0 @@ -Raise a correct exception for values greater than 0x7fffffff for the ``BINSTRING`` opcode in the C implementation of :mod:`pickle`. diff --git a/Misc/NEWS.d/next/Library/2025-06-10-10-22-18.gh-issue-130870.JipqbO.rst b/Misc/NEWS.d/next/Library/2025-06-10-10-22-18.gh-issue-130870.JipqbO.rst deleted file mode 100644 index 64173285e08417..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-10-10-22-18.gh-issue-130870.JipqbO.rst +++ /dev/null @@ -1,2 +0,0 @@ -Preserve :class:`types.GenericAlias` subclasses in -:func:`typing.get_type_hints` diff --git a/Misc/NEWS.d/next/Library/2025-06-10-16-11-00.gh-issue-133967.P0c24q.rst b/Misc/NEWS.d/next/Library/2025-06-10-16-11-00.gh-issue-133967.P0c24q.rst deleted file mode 100644 index 1976981727e235..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-10-16-11-00.gh-issue-133967.P0c24q.rst +++ /dev/null @@ -1 +0,0 @@ -Do not normalize :mod:`locale` name 'C.UTF-8' to 'en_US.UTF-8'. diff --git a/Misc/NEWS.d/next/Library/2025-06-10-21-42-04.gh-issue-135335.WnUqb_.rst b/Misc/NEWS.d/next/Library/2025-06-10-21-42-04.gh-issue-135335.WnUqb_.rst deleted file mode 100644 index 466ba0d232cd1f..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-10-21-42-04.gh-issue-135335.WnUqb_.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`multiprocessing`: Flush ``stdout`` and ``stderr`` after preloading -modules in the ``forkserver``. diff --git a/Misc/NEWS.d/next/Library/2025-06-12-10-45-02.gh-issue-135368.OjWVHL.rst b/Misc/NEWS.d/next/Library/2025-06-12-10-45-02.gh-issue-135368.OjWVHL.rst deleted file mode 100644 index b9973d88a859c6..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-12-10-45-02.gh-issue-135368.OjWVHL.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :class:`unittest.mock.Mock` generation on :func:`dataclasses.dataclass` -objects. Now all special attributes are set as it was before :gh:`124429`. diff --git a/Misc/NEWS.d/next/Library/2025-06-12-18-15-31.gh-issue-135429.mch75_.rst b/Misc/NEWS.d/next/Library/2025-06-12-18-15-31.gh-issue-135429.mch75_.rst deleted file mode 100644 index b5213520a957f3..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-12-18-15-31.gh-issue-135429.mch75_.rst +++ /dev/null @@ -1 +0,0 @@ -Fix the argument mismatch in ``_lsprof`` for ``PY_THROW`` event. diff --git a/Misc/NEWS.d/next/Library/2025-06-14-12-06-55.gh-issue-135487.KdVFff.rst b/Misc/NEWS.d/next/Library/2025-06-14-12-06-55.gh-issue-135487.KdVFff.rst deleted file mode 100644 index 3ef51fa31dfac9..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-14-12-06-55.gh-issue-135487.KdVFff.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :meth:`!reprlib.Repr.repr_int` when given integers with more than -:func:`sys.get_int_max_str_digits` digits. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-14-14-19-13.gh-issue-135497.1pzwdA.rst b/Misc/NEWS.d/next/Library/2025-06-14-14-19-13.gh-issue-135497.1pzwdA.rst deleted file mode 100644 index d3e81de9dbfd34..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-14-14-19-13.gh-issue-135497.1pzwdA.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :func:`os.getlogin` failing for longer usernames on BSD-based platforms. diff --git a/Misc/NEWS.d/next/Library/2025-06-15-03-03-22.gh-issue-65697.COdwZd.rst b/Misc/NEWS.d/next/Library/2025-06-15-03-03-22.gh-issue-65697.COdwZd.rst deleted file mode 100644 index d374220d02f5ce..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-15-03-03-22.gh-issue-65697.COdwZd.rst +++ /dev/null @@ -1 +0,0 @@ -:class:`configparser`'s error message when attempting to write an invalid key is now more helpful. diff --git a/Misc/NEWS.d/next/Library/2025-06-16-15-03-03.gh-issue-135561.mJCN8D.rst b/Misc/NEWS.d/next/Library/2025-06-16-15-03-03.gh-issue-135561.mJCN8D.rst deleted file mode 100644 index ee743f161138e6..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-16-15-03-03.gh-issue-135561.mJCN8D.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a crash on DEBUG builds when an HACL* HMAC routine fails. Patch by -Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-17-22-44-19.gh-issue-119180.Ogv8Nj.rst b/Misc/NEWS.d/next/Library/2025-06-17-22-44-19.gh-issue-119180.Ogv8Nj.rst deleted file mode 100644 index c5e5d5b4f8d85e..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-17-22-44-19.gh-issue-119180.Ogv8Nj.rst +++ /dev/null @@ -1,2 +0,0 @@ -Only fetch globals and locals if necessary in -:func:`annotationlib.get_annotations` diff --git a/Misc/NEWS.d/next/Library/2025-06-17-23-13-56.gh-issue-135557.Bfcy4v.rst b/Misc/NEWS.d/next/Library/2025-06-17-23-13-56.gh-issue-135557.Bfcy4v.rst deleted file mode 100644 index eabf5ea4aaa2ac..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-17-23-13-56.gh-issue-135557.Bfcy4v.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix races on :mod:`heapq` updates and :class:`list` reads on the :term:`free threaded <free threading>` -build. diff --git a/Misc/NEWS.d/next/Library/2025-06-18-11-43-17.gh-issue-135646.r7ekEn.rst b/Misc/NEWS.d/next/Library/2025-06-18-11-43-17.gh-issue-135646.r7ekEn.rst deleted file mode 100644 index 5fbd751467dd58..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-18-11-43-17.gh-issue-135646.r7ekEn.rst +++ /dev/null @@ -1 +0,0 @@ -Raise consistent :exc:`NameError` exceptions in :func:`annotationlib.ForwardRef.evaluate` diff --git a/Misc/NEWS.d/next/Library/2025-06-18-13-58-13.gh-issue-135645.109nff.rst b/Misc/NEWS.d/next/Library/2025-06-18-13-58-13.gh-issue-135645.109nff.rst deleted file mode 100644 index a7764a0105b8b3..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-18-13-58-13.gh-issue-135645.109nff.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added ``supports_isolated_interpreters`` field to -:data:`sys.implementation`. diff --git a/Misc/NEWS.d/next/Library/2025-06-18-19-25-32.gh-issue-123471.lx1Xbt.rst b/Misc/NEWS.d/next/Library/2025-06-18-19-25-32.gh-issue-123471.lx1Xbt.rst deleted file mode 100644 index 6f395024a9e179..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-18-19-25-32.gh-issue-123471.lx1Xbt.rst +++ /dev/null @@ -1 +0,0 @@ -Make concurrent iterations over :class:`itertools.chain` safe under :term:`free threading`. diff --git a/Misc/NEWS.d/next/Library/2025-06-20-16-28-47.gh-issue-135759.jne0Zi.rst b/Misc/NEWS.d/next/Library/2025-06-20-16-28-47.gh-issue-135759.jne0Zi.rst deleted file mode 100644 index 268d7eccdabc7f..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-20-16-28-47.gh-issue-135759.jne0Zi.rst +++ /dev/null @@ -1,4 +0,0 @@ -:mod:`hashlib`: reject negative digest lengths in OpenSSL-based SHAKE objects -by raising a :exc:`ValueError`. Previously, negative lengths were implicitly -rejected by raising a :exc:`MemoryError` or a :exc:`SystemError`. -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-20-17-06-59.gh-issue-90117.GYWVrn.rst b/Misc/NEWS.d/next/Library/2025-06-20-17-06-59.gh-issue-90117.GYWVrn.rst deleted file mode 100644 index 2bb15cb6d9c61c..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-20-17-06-59.gh-issue-90117.GYWVrn.rst +++ /dev/null @@ -1 +0,0 @@ -Speed up :mod:`pprint` for :class:`list` and :class:`tuple`. diff --git a/Misc/NEWS.d/next/Library/2025-06-22-02-16-17.gh-issue-135640.FXyFL6.rst b/Misc/NEWS.d/next/Library/2025-06-22-02-16-17.gh-issue-135640.FXyFL6.rst deleted file mode 100644 index ad217b57b4b510..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-22-02-16-17.gh-issue-135640.FXyFL6.rst +++ /dev/null @@ -1,4 +0,0 @@ -Address bug where it was possible to call -:func:`xml.etree.ElementTree.ElementTree.write` on an ElementTree object with -an invalid root element. This behavior blanked the file passed to ``write`` -if it already existed. diff --git a/Misc/NEWS.d/next/Library/2025-06-22-16-23-44.gh-issue-135815.0DandH.rst b/Misc/NEWS.d/next/Library/2025-06-22-16-23-44.gh-issue-135815.0DandH.rst deleted file mode 100644 index 0f4a68bf745a8e..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-22-16-23-44.gh-issue-135815.0DandH.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`netrc`: skip security checks if :func:`os.getuid` is missing. -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-22-22-03-06.gh-issue-135823.iDBg97.rst b/Misc/NEWS.d/next/Library/2025-06-22-22-03-06.gh-issue-135823.iDBg97.rst deleted file mode 100644 index 5b9d89caae7a55..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-22-22-03-06.gh-issue-135823.iDBg97.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`netrc`: improve the error message when the security check for the -ownership of the default configuration file ``~/.netrc`` fails. Patch by -Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst b/Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst deleted file mode 100644 index fcf495bdceb168..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst +++ /dev/null @@ -1,3 +0,0 @@ -Raise :exc:`TypeError` instead of :exc:`SystemError` when -:func:`!_interpreters.set___main___attrs` is passed a non-dict object. -Patch by Brian Schubert. diff --git a/Misc/NEWS.d/next/Library/2025-06-23-11-04-25.gh-issue-135836.-C-c4v.rst b/Misc/NEWS.d/next/Library/2025-06-23-11-04-25.gh-issue-135836.-C-c4v.rst deleted file mode 100644 index f93c9faee5863a..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-23-11-04-25.gh-issue-135836.-C-c4v.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :exc:`IndexError` in :meth:`asyncio.loop.create_connection` that could occur when the Happy Eyeballs algorithm resulted in an empty exceptions list during connection attempts. diff --git a/Misc/NEWS.d/next/Library/2025-06-24-10-23-37.gh-issue-135853.6xDNOG.rst b/Misc/NEWS.d/next/Library/2025-06-24-10-23-37.gh-issue-135853.6xDNOG.rst deleted file mode 100644 index 3fea3bc3e7cfe8..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-24-10-23-37.gh-issue-135853.6xDNOG.rst +++ /dev/null @@ -1,2 +0,0 @@ -:mod:`math`: expose C99 :func:`~math.signbit` function to determine whether -the sign bit of a floating-point value is set. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-24-10-52-35.gh-issue-135836.s37351.rst b/Misc/NEWS.d/next/Library/2025-06-24-10-52-35.gh-issue-135836.s37351.rst deleted file mode 100644 index 1d1e7a2298c085..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-24-10-52-35.gh-issue-135836.s37351.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix :exc:`IndexError` in :meth:`asyncio.loop.create_connection` that could -occur when non-\ :exc:`OSError` exception is raised during connection and -socket's ``close()`` raises :exc:`!OSError`. diff --git a/Misc/NEWS.d/next/Library/2025-06-24-13-30-47.gh-issue-135853.7ejTvK.rst b/Misc/NEWS.d/next/Library/2025-06-24-13-30-47.gh-issue-135853.7ejTvK.rst deleted file mode 100644 index 240ea72c69fa6c..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-24-13-30-47.gh-issue-135853.7ejTvK.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add :func:`math.fmax` and :func:`math.fmin` to get the larger and smaller of -two floating-point values. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-24-14-43-24.gh-issue-135878.Db4roX.rst b/Misc/NEWS.d/next/Library/2025-06-24-14-43-24.gh-issue-135878.Db4roX.rst deleted file mode 100644 index 969cf2dfa40878..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-24-14-43-24.gh-issue-135878.Db4roX.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixes a crash of :class:`types.SimpleNamespace` on :term:`free threading` builds, -when several threads were calling its :meth:`~object.__repr__` method at the -same time. diff --git a/Misc/NEWS.d/next/Library/2025-06-26-11-52-40.gh-issue-53203.TMigBr.rst b/Misc/NEWS.d/next/Library/2025-06-26-11-52-40.gh-issue-53203.TMigBr.rst deleted file mode 100644 index ba2fae49fdc933..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-26-11-52-40.gh-issue-53203.TMigBr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :func:`time.strptime` for ``%c`` and ``%x`` formats on locales byn_ER, -wal_ET and lzh_TW, and for ``%X`` format on locales ar_SA, bg_BG and lzh_TW. diff --git a/Misc/NEWS.d/next/Library/2025-06-26-17-19-36.gh-issue-105456.eR9oHB.rst b/Misc/NEWS.d/next/Library/2025-06-26-17-19-36.gh-issue-105456.eR9oHB.rst deleted file mode 100644 index 772403a240aa00..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-26-17-19-36.gh-issue-105456.eR9oHB.rst +++ /dev/null @@ -1,2 +0,0 @@ -Removed :mod:`!sre_compile`, :mod:`!sre_constants` and :mod:`!sre_parse` -modules. diff --git a/Misc/NEWS.d/next/Library/2025-06-26-17-28-49.gh-issue-135995.pPrDCt.rst b/Misc/NEWS.d/next/Library/2025-06-26-17-28-49.gh-issue-135995.pPrDCt.rst deleted file mode 100644 index 998b3cd85b1d5d..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-26-17-28-49.gh-issue-135995.pPrDCt.rst +++ /dev/null @@ -1 +0,0 @@ -In the palmos encoding, make byte ``0x9b`` decode to ``›`` (U+203A - SINGLE RIGHT-POINTING ANGLE QUOTATION MARK). diff --git a/Misc/NEWS.d/next/Library/2025-06-27-09-26-04.gh-issue-87135.33z0UW.rst b/Misc/NEWS.d/next/Library/2025-06-27-09-26-04.gh-issue-87135.33z0UW.rst deleted file mode 100644 index 4b6bc74cad87a4..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-27-09-26-04.gh-issue-87135.33z0UW.rst +++ /dev/null @@ -1,3 +0,0 @@ -Acquiring a :class:`threading.Lock` or :class:`threading.RLock` at interpreter -shutdown will raise :exc:`PythonFinalizationError` if Python can determine -that it would otherwise deadlock. diff --git a/Misc/NEWS.d/next/Library/2025-06-27-13-34-28.gh-issue-136028.RY727g.rst b/Misc/NEWS.d/next/Library/2025-06-27-13-34-28.gh-issue-136028.RY727g.rst deleted file mode 100644 index 9859df7cf6a69c..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-27-13-34-28.gh-issue-136028.RY727g.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix parsing month names containing "İ" (U+0130, LATIN CAPITAL LETTER I WITH -DOT ABOVE) in :func:`time.strptime`. This affects locales az_AZ, ber_DZ, -ber_MA and crh_UA. diff --git a/Misc/NEWS.d/next/Library/2025-06-28-11-32-57.gh-issue-134759.AjjKcG.rst b/Misc/NEWS.d/next/Library/2025-06-28-11-32-57.gh-issue-134759.AjjKcG.rst deleted file mode 100644 index 79b85320926954..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-28-11-32-57.gh-issue-134759.AjjKcG.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix :exc:`UnboundLocalError` in :func:`email.message.Message.get_payload` when -the payload to decode is a :class:`bytes` object. Patch by Kliment Lamonov. diff --git a/Misc/NEWS.d/next/Library/2025-06-29-15-22-13.gh-issue-90733.NiquaA.rst b/Misc/NEWS.d/next/Library/2025-06-29-15-22-13.gh-issue-90733.NiquaA.rst deleted file mode 100644 index cba930c5860522..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-29-15-22-13.gh-issue-90733.NiquaA.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve error messages when reporting invalid parameters in -:func:`hashlib.scrypt`. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-06-30-11-12-24.gh-issue-85702.0Lrbwu.rst b/Misc/NEWS.d/next/Library/2025-06-30-11-12-24.gh-issue-85702.0Lrbwu.rst deleted file mode 100644 index fc13eb1d9e0c99..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-06-30-11-12-24.gh-issue-85702.0Lrbwu.rst +++ /dev/null @@ -1,3 +0,0 @@ -If ``zoneinfo._common.load_tzdata`` is given a package without a resource a -:exc:`zoneinfo.ZoneInfoNotFoundError` is raised rather than a :exc:`PermissionError`. -Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2025-07-02-10-48-21.gh-issue-136193.xfvras.rst b/Misc/NEWS.d/next/Library/2025-07-02-10-48-21.gh-issue-136193.xfvras.rst deleted file mode 100644 index 801115202d0c95..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-02-10-48-21.gh-issue-136193.xfvras.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve :exc:`TypeError` error message, when richcomparing two -:class:`types.SimpleNamespace` objects. diff --git a/Misc/NEWS.d/next/Library/2025-07-02-18-41-45.gh-issue-133982.7qqAn6.rst b/Misc/NEWS.d/next/Library/2025-07-02-18-41-45.gh-issue-133982.7qqAn6.rst deleted file mode 100644 index a2d0810cebeaa6..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-02-18-41-45.gh-issue-133982.7qqAn6.rst +++ /dev/null @@ -1 +0,0 @@ -Update Python implementation of :class:`io.BytesIO` to be thread safe. diff --git a/Misc/NEWS.d/next/Library/2025-07-04-12-53-02.gh-issue-136156.OYlXoz.rst b/Misc/NEWS.d/next/Library/2025-07-04-12-53-02.gh-issue-136156.OYlXoz.rst deleted file mode 100644 index 95606790e991a4..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-04-12-53-02.gh-issue-136156.OYlXoz.rst +++ /dev/null @@ -1,3 +0,0 @@ -:func:`tempfile.TemporaryFile` no longer uses :data:`os.O_EXCL` with -:data:`os.O_TMPFILE`, so it's possible to use ``linkat()`` on the file -descriptor. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2025-07-05-06-56-16.gh-issue-136316.3zj_Do.rst b/Misc/NEWS.d/next/Library/2025-07-05-06-56-16.gh-issue-136316.3zj_Do.rst deleted file mode 100644 index dd5cecdf3a1209..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-05-06-56-16.gh-issue-136316.3zj_Do.rst +++ /dev/null @@ -1,2 +0,0 @@ -Improve support for evaluating nested forward references in -:func:`typing.evaluate_forward_ref`. diff --git a/Misc/NEWS.d/next/Library/2025-07-05-06-59-46.gh-issue-136047.qWvycf.rst b/Misc/NEWS.d/next/Library/2025-07-05-06-59-46.gh-issue-136047.qWvycf.rst deleted file mode 100644 index 1a381860914bc3..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-05-06-59-46.gh-issue-136047.qWvycf.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix issues with :mod:`typing` when the C implementation of :mod:`abc` is not -available. diff --git a/Misc/NEWS.d/next/Library/2025-07-05-09-45-04.gh-issue-136286.N67Amr.rst b/Misc/NEWS.d/next/Library/2025-07-05-09-45-04.gh-issue-136286.N67Amr.rst deleted file mode 100644 index ddc2310392fe92..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-05-09-45-04.gh-issue-136286.N67Amr.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix pickling failures for protocols 0 and 1 for many objects related to -subinterpreters. diff --git a/Misc/NEWS.d/next/Library/2025-07-06-10-18-48.gh-issue-136021.f-FJYT.rst b/Misc/NEWS.d/next/Library/2025-07-06-10-18-48.gh-issue-136021.f-FJYT.rst deleted file mode 100644 index 39a848c11eb8a1..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-06-10-18-48.gh-issue-136021.f-FJYT.rst +++ /dev/null @@ -1,3 +0,0 @@ -Make ``type_params`` parameter required in :func:`!typing._eval_type` after -a deprecation period for not providing this parameter. Also remove the -:exc:`DeprecationWarning` for the old behavior. diff --git a/Misc/NEWS.d/next/Library/2025-07-06-18-38-10.gh-issue-135953.Z29DCz.rst b/Misc/NEWS.d/next/Library/2025-07-06-18-38-10.gh-issue-135953.Z29DCz.rst deleted file mode 100644 index 7e32388ed791cb..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-06-18-38-10.gh-issue-135953.Z29DCz.rst +++ /dev/null @@ -1,9 +0,0 @@ -Implement a new high-frequency runtime profiler that leverages the existing -remote debugging functionality to collect detailed execution statistics -from running Python processes. This tool is exposed in the -``profile.sample`` module and enables non-intrusive observation of -production applications by attaching to already-running processes without -requiring any code modifications, restarts, or special startup flags. The -observer can perform extremely high-frequency sampling of stack traces and -interpreter state, providing detailed runtime execution analysis of live -applications. diff --git a/Misc/NEWS.d/next/Library/2025-07-07-16-46-55.gh-issue-72327.wLvRuj.rst b/Misc/NEWS.d/next/Library/2025-07-07-16-46-55.gh-issue-72327.wLvRuj.rst deleted file mode 100644 index f305abb655a6f6..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-07-16-46-55.gh-issue-72327.wLvRuj.rst +++ /dev/null @@ -1,2 +0,0 @@ -Suggest using the system command prompt when ``pip install`` is typed into -the REPL. Patch by Tom Viner, Richard Si, and Brian Schubert. diff --git a/Misc/NEWS.d/next/Library/2025-07-07-22-12-32.gh-issue-136380.1b_nXl.rst b/Misc/NEWS.d/next/Library/2025-07-07-22-12-32.gh-issue-136380.1b_nXl.rst deleted file mode 100644 index 4ac04b9c0a6c5c..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-07-22-12-32.gh-issue-136380.1b_nXl.rst +++ /dev/null @@ -1,3 +0,0 @@ -Raises :exc:`AttributeError` when accessing -:class:`concurrent.futures.InterpreterPoolExecutor` and subinterpreters are -not available. diff --git a/Misc/NEWS.d/next/Library/2025-07-08-20-58-01.gh-issue-136434.uuJsjS.rst b/Misc/NEWS.d/next/Library/2025-07-08-20-58-01.gh-issue-136434.uuJsjS.rst deleted file mode 100644 index 951f57100b650c..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-08-20-58-01.gh-issue-136434.uuJsjS.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix docs generation of ``UnboundItem`` in :mod:`concurrent.interpreters` -when running with :option:`-OO`. diff --git a/Misc/NEWS.d/next/Library/2025-07-09-20-29-30.gh-issue-136476.HyLLzh.rst b/Misc/NEWS.d/next/Library/2025-07-09-20-29-30.gh-issue-136476.HyLLzh.rst deleted file mode 100644 index 7634bd3be9346d..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-09-20-29-30.gh-issue-136476.HyLLzh.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a bug that was causing the ``get_async_stack_trace`` function to miss -some frames in the stack trace. diff --git a/Misc/NEWS.d/next/Library/2025-07-10-00-47-37.gh-issue-136470.KlUEUG.rst b/Misc/NEWS.d/next/Library/2025-07-10-00-47-37.gh-issue-136470.KlUEUG.rst deleted file mode 100644 index 5a0429cae07168..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-10-00-47-37.gh-issue-136470.KlUEUG.rst +++ /dev/null @@ -1,2 +0,0 @@ -Correct :class:`concurrent.futures.InterpreterPoolExecutor`'s default thread -name. diff --git a/Misc/NEWS.d/next/Library/2025-07-10-10-18-19.gh-issue-52876.9Vjrd8.rst b/Misc/NEWS.d/next/Library/2025-07-10-10-18-19.gh-issue-52876.9Vjrd8.rst deleted file mode 100644 index a835306868d3dc..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-10-10-18-19.gh-issue-52876.9Vjrd8.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add missing ``keepends`` (default ``True``) parameter to -:meth:`!codecs.StreamReaderWriter.readline` and -:meth:`!codecs.StreamReaderWriter.readlines`. diff --git a/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst b/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst deleted file mode 100644 index 71ec66a37ef4c3..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :class:`wave.Wave_write` emitting an unraisable when open raises. diff --git a/Misc/NEWS.d/next/Library/2025-07-11-10-23-44.gh-issue-136492.BVi5h0.rst b/Misc/NEWS.d/next/Library/2025-07-11-10-23-44.gh-issue-136492.BVi5h0.rst deleted file mode 100644 index 7ab5b068a7f691..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-11-10-23-44.gh-issue-136492.BVi5h0.rst +++ /dev/null @@ -1 +0,0 @@ -Expose :pep:`667`'s :data:`~types.FrameLocalsProxyType` in the :mod:`types` module. diff --git a/Misc/NEWS.d/next/Library/2025-07-11-23-04-39.gh-issue-136549.oAi8u4.rst b/Misc/NEWS.d/next/Library/2025-07-11-23-04-39.gh-issue-136549.oAi8u4.rst deleted file mode 100644 index f3050ad5d5aa10..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-11-23-04-39.gh-issue-136549.oAi8u4.rst +++ /dev/null @@ -1 +0,0 @@ -Fix signature of :func:`threading.excepthook`. diff --git a/Misc/NEWS.d/next/Library/2025-07-12-18-05-37.gh-issue-136591.ujXmSN.rst b/Misc/NEWS.d/next/Library/2025-07-12-18-05-37.gh-issue-136591.ujXmSN.rst deleted file mode 100644 index ccd5bf11f0467a..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-12-18-05-37.gh-issue-136591.ujXmSN.rst +++ /dev/null @@ -1,3 +0,0 @@ -:mod:`!_hashlib`: avoid using deprecated functions -:manpage:`ERR_func_error_string` and :manpage:`EVP_MD_CTX_md` when using -OpenSSL 3.0 and later. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-07-15-16-37-34.gh-issue-136669.Yexwah.rst b/Misc/NEWS.d/next/Library/2025-07-15-16-37-34.gh-issue-136669.Yexwah.rst deleted file mode 100644 index 0d93397ff35d0b..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-15-16-37-34.gh-issue-136669.Yexwah.rst +++ /dev/null @@ -1 +0,0 @@ -:mod:`!_asyncio` is now statically linked for improved performance. diff --git a/Misc/NEWS.d/next/Library/2025-07-19-15-40-47.gh-issue-131724.LS59nA.rst b/Misc/NEWS.d/next/Library/2025-07-19-15-40-47.gh-issue-131724.LS59nA.rst deleted file mode 100644 index 71a991aa2c5ae6..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-19-15-40-47.gh-issue-131724.LS59nA.rst +++ /dev/null @@ -1,4 +0,0 @@ -In :mod:`http.client`, a new *max_response_headers* keyword-only parameter has been -added to :class:`~http.client.HTTPConnection` and :class:`~http.client.HTTPSConnection` -constructors. This parameter sets the maximum number of allowed response headers, -helping to prevent denial-of-service attacks. diff --git a/Misc/NEWS.d/next/Library/2025-07-19-16-20-54.gh-issue-130645.O-dYcN.rst b/Misc/NEWS.d/next/Library/2025-07-19-16-20-54.gh-issue-130645.O-dYcN.rst deleted file mode 100644 index 96e076dfe5bd12..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-19-16-20-54.gh-issue-130645.O-dYcN.rst +++ /dev/null @@ -1 +0,0 @@ -Enable color help by default in :mod:`argparse`. diff --git a/Misc/NEWS.d/next/Library/2025-07-20-10-21-49.gh-issue-136787._0Rbp_.rst b/Misc/NEWS.d/next/Library/2025-07-20-10-21-49.gh-issue-136787._0Rbp_.rst deleted file mode 100644 index c6a8088e5da81f..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-20-10-21-49.gh-issue-136787._0Rbp_.rst +++ /dev/null @@ -1,4 +0,0 @@ -:mod:`hashlib`: improve exception messages when a hash algorithm is not -recognized, blocked by the current security policy or incompatible with -the desired operation (for instance, using HMAC with SHAKE). -Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2025-07-20-16-02-00.gh-issue-136874.cLC3o1.rst b/Misc/NEWS.d/next/Library/2025-07-20-16-02-00.gh-issue-136874.cLC3o1.rst deleted file mode 100644 index 9a71eb8ef1ac8d..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-20-16-02-00.gh-issue-136874.cLC3o1.rst +++ /dev/null @@ -1 +0,0 @@ -Discard URL query and fragment in :func:`urllib.request.url2pathname`. diff --git a/Misc/NEWS.d/next/Library/2025-07-21-15-40-00.gh-issue-136914.-GNG-d.rst b/Misc/NEWS.d/next/Library/2025-07-21-15-40-00.gh-issue-136914.-GNG-d.rst deleted file mode 100644 index 78ec8025fbc0fd..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-21-15-40-00.gh-issue-136914.-GNG-d.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix retrieval of :attr:`doctest.DocTest.lineno` for objects decorated with -:func:`functools.cache` or :class:`functools.cached_property`. diff --git a/Misc/NEWS.d/next/Library/2025-07-21-16-10-24.gh-issue-124621.wyoWc1.rst b/Misc/NEWS.d/next/Library/2025-07-21-16-10-24.gh-issue-124621.wyoWc1.rst deleted file mode 100644 index 34049183649271..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-21-16-10-24.gh-issue-124621.wyoWc1.rst +++ /dev/null @@ -1 +0,0 @@ -pyrepl now works in Emscripten. diff --git a/Misc/NEWS.d/next/Library/2025-07-21-22-35-50.gh-issue-136170.QUlc78.rst b/Misc/NEWS.d/next/Library/2025-07-21-22-35-50.gh-issue-136170.QUlc78.rst deleted file mode 100644 index fd30fe156a1b32..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-21-22-35-50.gh-issue-136170.QUlc78.rst +++ /dev/null @@ -1,3 +0,0 @@ -Removed the unreleased ``zipfile.ZipFile.data_offset`` property added in 3.14.0a7 -as it wasn't fully clear which behavior it should have in some situations so -the result was not always what a user might expect. diff --git a/Misc/NEWS.d/next/Library/2025-07-23-11-59-48.gh-issue-136980.BIJzkB.rst b/Misc/NEWS.d/next/Library/2025-07-23-11-59-48.gh-issue-136980.BIJzkB.rst deleted file mode 100644 index a7111dd2b865ba..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-07-23-11-59-48.gh-issue-136980.BIJzkB.rst +++ /dev/null @@ -1 +0,0 @@ -Remove unused C tracing code in bdb for event type ``c_call``, ``c_return`` and ``c_exception`` diff --git a/Misc/NEWS.d/next/Library/2025-08-30-07-44-30.gh-issue-86533.pathlib.rst b/Misc/NEWS.d/next/Library/2025-08-30-07-44-30.gh-issue-86533.pathlib.rst new file mode 100644 index 00000000000000..9c32671173e0ad --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-30-07-44-30.gh-issue-86533.pathlib.rst @@ -0,0 +1,4 @@ +The :func:`os.makedirs` function and :meth:`pathlib.Path.mkdir` method now have +a *parent_mode* parameter to specify the mode for intermediate directories when +creating parent directories. This allows one to match the behavior from Python +3.6 and earlier for :func:`os.makedirs`. diff --git a/Misc/NEWS.d/next/Library/2025-10-09-12-13-29.gh-issue-139819.YxUDyH.rst b/Misc/NEWS.d/next/Library/2025-10-09-12-13-29.gh-issue-139819.YxUDyH.rst new file mode 100644 index 00000000000000..ab3a8c6ed16ce4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-09-12-13-29.gh-issue-139819.YxUDyH.rst @@ -0,0 +1,3 @@ +:mod:`rlcompleter`: Avoid suggesting attributes that are not accessible on +instances (e.g., Enum members showing ``__name__``). Patch by Peter +(ttw225). diff --git a/Misc/NEWS.d/next/Library/2025-10-12-08-46-26.gh-issue-139398.hESOMl.rst b/Misc/NEWS.d/next/Library/2025-10-12-08-46-26.gh-issue-139398.hESOMl.rst new file mode 100644 index 00000000000000..201b682d177c01 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-12-08-46-26.gh-issue-139398.hESOMl.rst @@ -0,0 +1,2 @@ +Add supported ``_sunder_`` names to the :func:`dir` method of the :mod:`enum` module +to support them in :term:`REPL` autocompletion. diff --git a/Misc/NEWS.d/next/Library/2025-10-20-09-12-18.gh-issue-140344.WjbYg-.rst b/Misc/NEWS.d/next/Library/2025-10-20-09-12-18.gh-issue-140344.WjbYg-.rst new file mode 100644 index 00000000000000..ca3046941c6c93 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-20-09-12-18.gh-issue-140344.WjbYg-.rst @@ -0,0 +1,11 @@ +The classes ``ast.slice``, ``ast.ExtSlice``, ``ast.Index``, ``ast.Suite``, ``ast.AugLoad``, +``ast.AugStore``, and ``ast.Param``, deprecated since Python 3.9, now issue +deprecation warnings on use. They are now scheduled for removal in Python 3.21. + +The ``dims`` property of ``ast.Tuple`` objects, deprecated since Python 3.9, +now issues a deprecation warning when accessed. The property is scheduled for +removal in Python 3.21. Use the (non-deprecated) ``elts`` property of +``ast.Tuple`` objects instead. + +The deprecated global names are also no longer imported by +``from ast import *``. diff --git a/Misc/NEWS.d/next/Library/2025-11-02-22-24-13.gh-issue-140924.NQVbR_.rst b/Misc/NEWS.d/next/Library/2025-11-02-22-24-13.gh-issue-140924.NQVbR_.rst new file mode 100644 index 00000000000000..91c30c95469f08 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-11-02-22-24-13.gh-issue-140924.NQVbR_.rst @@ -0,0 +1,3 @@ +Add :func:`locale.localize`, :func:`locale.delocalize` +and platform-specific locale constants +from the :mod:`!_locale` module to ``locale.__all__``. diff --git a/Misc/NEWS.d/next/Library/2025-12-21-17-56-37.gh-issue-143008.aakErJ.rst b/Misc/NEWS.d/next/Library/2025-12-21-17-56-37.gh-issue-143008.aakErJ.rst new file mode 100644 index 00000000000000..907cdb770ab65f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-21-17-56-37.gh-issue-143008.aakErJ.rst @@ -0,0 +1,2 @@ +Fix crash in :class:`io.TextIOWrapper` when reentrant +:meth:`io.TextIOBase.detach` is called reentrantly from the underlying buffer. diff --git a/Misc/NEWS.d/next/Library/2026-01-18-06-42-47.gh-issue-143988.MtLtCP.rst b/Misc/NEWS.d/next/Library/2026-01-18-06-42-47.gh-issue-143988.MtLtCP.rst new file mode 100644 index 00000000000000..fcc0cb54934b90 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-18-06-42-47.gh-issue-143988.MtLtCP.rst @@ -0,0 +1,2 @@ +Fixed crashes in :meth:`socket.socket.sendmsg` and :meth:`socket.socket.recvmsg_into` +that could occur if buffer sequences are concurrently mutated. diff --git a/Misc/NEWS.d/next/Library/2026-03-26-09-30-00.gh-issue-146452.Y2N6qZ8J.rst b/Misc/NEWS.d/next/Library/2026-03-26-09-30-00.gh-issue-146452.Y2N6qZ8J.rst new file mode 100644 index 00000000000000..99f3cce33497a1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-26-09-30-00.gh-issue-146452.Y2N6qZ8J.rst @@ -0,0 +1,2 @@ +Fix segfault in :mod:`pickle` when pickling a dictionary concurrently +mutated by another thread in the free-threaded build. diff --git a/Misc/NEWS.d/next/Library/2026-04-21-06-30-59.gh-issue-119670.pMWZfY.rst b/Misc/NEWS.d/next/Library/2026-04-21-06-30-59.gh-issue-119670.pMWZfY.rst new file mode 100644 index 00000000000000..fc1941be4dc792 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-21-06-30-59.gh-issue-119670.pMWZfY.rst @@ -0,0 +1,2 @@ +Add keyword-only parameter *force* to :func:`shlex.quote` to force quoting +a string, even if it is already safe for a shell without being quoted. diff --git a/Misc/NEWS.d/next/Library/2026-04-23-12-50-15.gh-issue-148441.zvpCkR.rst b/Misc/NEWS.d/next/Library/2026-04-23-12-50-15.gh-issue-148441.zvpCkR.rst new file mode 100644 index 00000000000000..762815270e4d40 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-23-12-50-15.gh-issue-148441.zvpCkR.rst @@ -0,0 +1,4 @@ +:mod:`xml.parsers.expat`: prevent a crash in +:meth:`~xml.parsers.expat.xmlparser.CharacterDataHandler` +when the character data size exceeds the parser's +:attr:`buffer size <xml.parsers.expat.xmlparser.buffer_size>`. diff --git a/Misc/NEWS.d/next/Library/2026-04-24-19-54-00.gh-issue-148954.v1.rst b/Misc/NEWS.d/next/Library/2026-04-24-19-54-00.gh-issue-148954.v1.rst new file mode 100644 index 00000000000000..6245af7e362e92 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-24-19-54-00.gh-issue-148954.v1.rst @@ -0,0 +1 @@ +Fix XML injection vulnerability in :func:`xmlrpc.client.dumps` where the ``methodname`` was not being escaped before interpolation into the XML body. diff --git a/Misc/NEWS.d/next/Library/2026-04-27-11-12-00.gh-issue-149046.74shDd.rst b/Misc/NEWS.d/next/Library/2026-04-27-11-12-00.gh-issue-149046.74shDd.rst new file mode 100644 index 00000000000000..b05c4222e30fcd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-27-11-12-00.gh-issue-149046.74shDd.rst @@ -0,0 +1,2 @@ +:mod:`io`: Fix :class:`io.StringIO` serialization: no longer call ``str(obj)`` on :class:`str` +subclasses. Patch by Thomas Kowalski. diff --git a/Misc/NEWS.d/next/Library/2026-04-29-08-10-17.gh-issue-149056.jnaD4W.rst b/Misc/NEWS.d/next/Library/2026-04-29-08-10-17.gh-issue-149056.jnaD4W.rst new file mode 100644 index 00000000000000..0026d02c876257 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-29-08-10-17.gh-issue-149056.jnaD4W.rst @@ -0,0 +1,2 @@ +Fix :func:`json.load` not forwarding the *array_hook* argument to +:func:`json.loads`. Patch by Thomas Kowalski. diff --git a/Misc/NEWS.d/next/Library/2026-05-06-20-10-44.gh-issue-149464.3mPhcr.rst b/Misc/NEWS.d/next/Library/2026-05-06-20-10-44.gh-issue-149464.3mPhcr.rst new file mode 100644 index 00000000000000..9f987d2a662d8f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-06-20-10-44.gh-issue-149464.3mPhcr.rst @@ -0,0 +1,3 @@ +Add :func:`os.pidfd_getfd` for duplicating a file descriptor from another +process via a pidfd. Available on Linux 5.6+. Patch by Maurycy +Pawłowski-Wieroński. diff --git a/Misc/NEWS.d/next/Library/2026-05-07-14-18-47.gh-issue-149489.bX9iHe.rst b/Misc/NEWS.d/next/Library/2026-05-07-14-18-47.gh-issue-149489.bX9iHe.rst new file mode 100644 index 00000000000000..1550c893fd7c45 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-07-14-18-47.gh-issue-149489.bX9iHe.rst @@ -0,0 +1,5 @@ +Fix :mod:`~xml.etree.ElementTree` serialization to HTML. The content of +comments, processing instructions and elements "xmp", "iframe", "noembed", +"noframes", and "plaintext" is no longer escaped. The "plaintext" element no +longer have the closing tag. Add support of empty attributes (with value +``None``). diff --git a/Misc/NEWS.d/next/Library/2026-05-07-17-35-47.gh-issue-149499.G2ER-R.rst b/Misc/NEWS.d/next/Library/2026-05-07-17-35-47.gh-issue-149499.G2ER-R.rst new file mode 100644 index 00000000000000..796d39f5ba05bf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-07-17-35-47.gh-issue-149499.G2ER-R.rst @@ -0,0 +1,3 @@ +Removed the :func:`!sysconfig.expand_makefile_vars` function which has been +deprecated since Python 3.14. Use the ``vars`` argument of +:func:`sysconfig.get_paths` instead. diff --git a/Misc/NEWS.d/next/Library/2026-05-07-21-58-17.gh-issue-149388.DDBPeA.rst b/Misc/NEWS.d/next/Library/2026-05-07-21-58-17.gh-issue-149388.DDBPeA.rst new file mode 100644 index 00000000000000..4a1c6f3f5b4e57 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-07-21-58-17.gh-issue-149388.DDBPeA.rst @@ -0,0 +1 @@ +Make :class:`!asyncio.windows_utils.PipeHandle` closing idempotent. diff --git a/Misc/NEWS.d/next/Library/2026-05-08-06-12-26.gh-issue-149528.AxdDxa.rst b/Misc/NEWS.d/next/Library/2026-05-08-06-12-26.gh-issue-149528.AxdDxa.rst new file mode 100644 index 00000000000000..b2c6e427a1c592 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-08-06-12-26.gh-issue-149528.AxdDxa.rst @@ -0,0 +1 @@ +Remove ``annotationlib.ForwardRef._evaludate`` deprecated method. diff --git a/Misc/NEWS.d/next/Library/2026-05-08-06-56-57.gh-issue-149530.cl2AJ8.rst b/Misc/NEWS.d/next/Library/2026-05-08-06-56-57.gh-issue-149530.cl2AJ8.rst new file mode 100644 index 00000000000000..2b74e8cdadd03c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-08-06-56-57.gh-issue-149530.cl2AJ8.rst @@ -0,0 +1,2 @@ +Removed :meth:`!symtable.Class.get_methods` which has been deprecated since +3.14. diff --git a/Misc/NEWS.d/next/Library/2026-05-08-07-51-54.gh-issue-80480.nnA4p1.rst b/Misc/NEWS.d/next/Library/2026-05-08-07-51-54.gh-issue-80480.nnA4p1.rst new file mode 100644 index 00000000000000..2f7f15b169b26b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-08-07-51-54.gh-issue-80480.nnA4p1.rst @@ -0,0 +1,2 @@ +Remove deprecated ``'u'`` type code (:c:type:`wchar_t`) for the :mod:`array` +module. Use ``'w'`` format code instead (:c:type:`Py_UCS4`, always 4 bytes). diff --git a/Misc/NEWS.d/next/Library/2026-05-08-08-24-01.gh-issue-149537.hVFVnt.rst b/Misc/NEWS.d/next/Library/2026-05-08-08-24-01.gh-issue-149537.hVFVnt.rst new file mode 100644 index 00000000000000..2fb0359a2ea893 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-08-08-24-01.gh-issue-149537.hVFVnt.rst @@ -0,0 +1,2 @@ +Remove kw parameters from python version of :func:`functools.reduce` +function. diff --git a/Misc/NEWS.d/next/Library/2026-05-08-09-11-48.gh-issue-149534.Tw7eeY.rst b/Misc/NEWS.d/next/Library/2026-05-08-09-11-48.gh-issue-149534.Tw7eeY.rst new file mode 100644 index 00000000000000..0938935a75d8c1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-08-09-11-48.gh-issue-149534.Tw7eeY.rst @@ -0,0 +1 @@ +Fix merging of :class:`collections.defaultdict` and :class:`frozendict`. diff --git a/Misc/NEWS.d/next/Library/2026-05-08-14-24-16.gh-issue-149436.dEV02X.rst b/Misc/NEWS.d/next/Library/2026-05-08-14-24-16.gh-issue-149436.dEV02X.rst new file mode 100644 index 00000000000000..401cf044e1514f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-08-14-24-16.gh-issue-149436.dEV02X.rst @@ -0,0 +1 @@ +Improve performance of :meth:`inspect.getattr_static`. diff --git a/Misc/NEWS.d/next/Library/2026-05-08-15-08-35.gh-issue-112821.t9T1YD.rst b/Misc/NEWS.d/next/Library/2026-05-08-15-08-35.gh-issue-112821.t9T1YD.rst new file mode 100644 index 00000000000000..cfbcde81493e22 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-08-15-08-35.gh-issue-112821.t9T1YD.rst @@ -0,0 +1,4 @@ +In the REPL, autocompletion might run arbitrary code in the getter of a +descriptor. If that getter raised an exception, autocompletion would fail to +present any options for the entire object. Autocompletion now works as +expected for these objects. diff --git a/Misc/NEWS.d/next/Library/2026-05-08-16-44-20.gh-issue-149567.iiZKEj.rst b/Misc/NEWS.d/next/Library/2026-05-08-16-44-20.gh-issue-149567.iiZKEj.rst new file mode 100644 index 00000000000000..e30563df79b3bc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-08-16-44-20.gh-issue-149567.iiZKEj.rst @@ -0,0 +1,2 @@ +Remove the :exc:`!shutil.ExecError` exception which has been deprecated +since Python 3.14. diff --git a/Misc/NEWS.d/next/Library/2026-05-09-11-22-29.gh-issue-149595.1nuYXw.rst b/Misc/NEWS.d/next/Library/2026-05-09-11-22-29.gh-issue-149595.1nuYXw.rst new file mode 100644 index 00000000000000..f3971554de3748 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-09-11-22-29.gh-issue-149595.1nuYXw.rst @@ -0,0 +1,2 @@ +Remove the :func:`!sys._enablelegacywindowsfsencoding` function which has +been deprecated since Python 3.13. diff --git a/Misc/NEWS.d/next/Library/2026-05-09-15-17-59.gh-issue-149598.aLrXRw.rst b/Misc/NEWS.d/next/Library/2026-05-09-15-17-59.gh-issue-149598.aLrXRw.rst new file mode 100644 index 00000000000000..8c06ba5e7d5e4e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-09-15-17-59.gh-issue-149598.aLrXRw.rst @@ -0,0 +1 @@ +Remove support of deprecated *strm* argument for :mod:`logging` handlers. diff --git a/Misc/NEWS.d/next/Library/2026-05-09-15-42-54.gh-issue-149600.ZbL-iv.rst b/Misc/NEWS.d/next/Library/2026-05-09-15-42-54.gh-issue-149600.ZbL-iv.rst new file mode 100644 index 00000000000000..732787dc92c109 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-09-15-42-54.gh-issue-149600.ZbL-iv.rst @@ -0,0 +1 @@ +Remove deprecated :func:`!asyncio.iscoroutinefunction` function. diff --git a/Misc/NEWS.d/next/Library/2026-05-09-21-02-08.gh-issue-149614.U4snj3.rst b/Misc/NEWS.d/next/Library/2026-05-09-21-02-08.gh-issue-149614.U4snj3.rst new file mode 100644 index 00000000000000..5169c6c203fc1b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-09-21-02-08.gh-issue-149614.U4snj3.rst @@ -0,0 +1 @@ +Fix a regression that broke the ability to deepcopy :class:`argparse.ArgumentParser` instances. diff --git a/Misc/NEWS.d/next/Library/2026-05-10-07-21-51.gh-issue-139489.rS7LTA.rst b/Misc/NEWS.d/next/Library/2026-05-10-07-21-51.gh-issue-139489.rS7LTA.rst new file mode 100644 index 00000000000000..40fe7e9fd6a008 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-10-07-21-51.gh-issue-139489.rS7LTA.rst @@ -0,0 +1 @@ +Add :func:`xml.is_valid_text` to ``xml.__all__``. diff --git a/Misc/NEWS.d/next/Library/2026-05-10-14-10-00.gh-issue-149634.iT5cwC.rst b/Misc/NEWS.d/next/Library/2026-05-10-14-10-00.gh-issue-149634.iT5cwC.rst new file mode 100644 index 00000000000000..73e8d73c36891a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-10-14-10-00.gh-issue-149634.iT5cwC.rst @@ -0,0 +1 @@ +Remove deprecated and unused :attr:`!tarfile.TarInfo.tarfile` attribute. diff --git a/Misc/NEWS.d/next/Library/2026-05-10-16-06-27.gh-issue-149609.yLKPXo.rst b/Misc/NEWS.d/next/Library/2026-05-10-16-06-27.gh-issue-149609.yLKPXo.rst new file mode 100644 index 00000000000000..3acba7d25021b2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-10-16-06-27.gh-issue-149609.yLKPXo.rst @@ -0,0 +1,3 @@ +Raise :exc:`DeprecationWarning` on using :class:`abc.abstractclassmethod`, +:class:`abc.abstractstaticmethod`, and :class:`abc.abstractproperty`, +schedule its removal for Python 3.21. diff --git a/Misc/NEWS.d/next/Library/2026-05-10-19-26-50.gh-issue-149584.x7Qm9A.rst b/Misc/NEWS.d/next/Library/2026-05-10-19-26-50.gh-issue-149584.x7Qm9A.rst new file mode 100644 index 00000000000000..6734250fdd6af3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-10-19-26-50.gh-issue-149584.x7Qm9A.rst @@ -0,0 +1,4 @@ +Fix excessive overhead in the Tachyon profiler when inspecting a remote +process by avoiding repeated remote page-cache scans, batching predicted +remote reads, and reusing cached profiler result objects. Patch by Pablo +Galindo and Maurycy Pawłowski-Wieroński. diff --git a/Misc/NEWS.d/next/Library/2026-05-10-23-51-23.gh-issue-149504.pDSCbn.rst b/Misc/NEWS.d/next/Library/2026-05-10-23-51-23.gh-issue-149504.pDSCbn.rst new file mode 100644 index 00000000000000..88bf268123bbec --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-10-23-51-23.gh-issue-149504.pDSCbn.rst @@ -0,0 +1,5 @@ +Fix :func:`site.addsitedir` to allow re-entrant calls from within startup +files. Previously, a ``.pth`` file containing an ``import`` line that +called :func:`site.addsitedir` (or a ``.start`` entry point doing the same) +could crash with ``RuntimeError: dictionary changed size during iteration`` +during site initialization, breaking tools such as ``uv run --with``. diff --git a/Misc/NEWS.d/next/Library/2026-05-12-06-24-54.gh-issue-149701.8v9RTm.rst b/Misc/NEWS.d/next/Library/2026-05-12-06-24-54.gh-issue-149701.8v9RTm.rst new file mode 100644 index 00000000000000..676d788cbce62a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-12-06-24-54.gh-issue-149701.8v9RTm.rst @@ -0,0 +1 @@ +Fix bad return code from Lib/venv/bin/activate if hashing is disabled diff --git a/Misc/NEWS.d/next/Library/2026-05-12-13-03-45.gh-issue-149718.SaM1NJ.rst b/Misc/NEWS.d/next/Library/2026-05-12-13-03-45.gh-issue-149718.SaM1NJ.rst new file mode 100644 index 00000000000000..25344e5a90f022 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-12-13-03-45.gh-issue-149718.SaM1NJ.rst @@ -0,0 +1,4 @@ +Coalesce consecutive identical stack frames in Tachyon, so aggregating +collectors (pstats, collapsed, flamegraph, gecko) receive one collect. +Improves sample rate 3x, error rate and missed rate drop by 70%. Patch by +Maurycy Pawłowski-Wieroński. diff --git a/Misc/NEWS.d/next/Library/2026-05-12-14-44-39.gh-issue-149720.ccsoW-.rst b/Misc/NEWS.d/next/Library/2026-05-12-14-44-39.gh-issue-149720.ccsoW-.rst new file mode 100644 index 00000000000000..f16d8770d740f4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-12-14-44-39.gh-issue-149720.ccsoW-.rst @@ -0,0 +1 @@ +Remove support for undotted *ext* in :meth:`mimetypes.MimeTypes.add_type`. diff --git a/Misc/NEWS.d/next/Library/2026-05-13-12-16-54.gh-issue-149473.nOQZqn.rst b/Misc/NEWS.d/next/Library/2026-05-13-12-16-54.gh-issue-149473.nOQZqn.rst new file mode 100644 index 00000000000000..db624aba31a9de --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-13-12-16-54.gh-issue-149473.nOQZqn.rst @@ -0,0 +1,2 @@ +Calling ``os.environ.clear()`` now emits ``os._clearenv`` auditing event. +Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Library/2026-05-13-23-18-39.gh-issue-149801.S_FfGr.rst b/Misc/NEWS.d/next/Library/2026-05-13-23-18-39.gh-issue-149801.S_FfGr.rst new file mode 100644 index 00000000000000..f9e8538527d204 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-13-23-18-39.gh-issue-149801.S_FfGr.rst @@ -0,0 +1,2 @@ +Add IANA registered names and aliases with leading zeros before number (like +IBM00858, CP00858, IBM01140, CP01140) for corresponding codecs. diff --git a/Misc/NEWS.d/next/Library/2026-05-14-15-55-28.gh-issue-149816.ZaXQ0q.rst b/Misc/NEWS.d/next/Library/2026-05-14-15-55-28.gh-issue-149816.ZaXQ0q.rst new file mode 100644 index 00000000000000..3ea70071ec3c75 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-14-15-55-28.gh-issue-149816.ZaXQ0q.rst @@ -0,0 +1,2 @@ +Fix a race condition in ``_random.Random.__init__`` method in free-threading +mode. diff --git a/Misc/NEWS.d/next/Library/2026-05-14-17-01-19.gh-issue-62259.ytlFD5.rst b/Misc/NEWS.d/next/Library/2026-05-14-17-01-19.gh-issue-62259.ytlFD5.rst new file mode 100644 index 00000000000000..d0af77366378b8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-14-17-01-19.gh-issue-62259.ytlFD5.rst @@ -0,0 +1,9 @@ +Add support for multiple multi-byte encodings in the :mod:`XML parser +<xml.parsers.expat>`: "cp932", "cp949", "cp950", "Big5","EUC-JP", "GB2312", +"GBK", "johab", and "Shift_JIS". Add partial support (only BMP characters) +for multi-byte encodings "Big5-HKSCS", "EUC_JIS-2004", "EUC_JISX0213", +"Shift_JIS-2004", "Shift_JISX0213", "utf-8-sig" and non-standard aliases +like "UTF8" (without hyphen). The parser now raises :exc:`ValueError` for +known unsupported multi-byte encodings such us "ISO-2022-JP" or +"raw-unicode-escape" instead of failing later, when encounter non-ASCII +data. diff --git a/Misc/NEWS.d/next/Library/2026-05-15-16-28-00.gh-issue-149819.fixpth.rst b/Misc/NEWS.d/next/Library/2026-05-15-16-28-00.gh-issue-149819.fixpth.rst new file mode 100644 index 00000000000000..66e6da0ecf0d87 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-15-16-28-00.gh-issue-149819.fixpth.rst @@ -0,0 +1,4 @@ +Fix regression in :func:`site.addsitedir` where ``.pth`` files were no +longer processed in Python subprocesses. This happened because +:func:`site.main` seeded ``known_paths`` with entries inherited from +the parent process, causing ``addsitedir`` to skip ``.pth`` processing. diff --git a/Misc/NEWS.d/next/Library/2026-05-15-18-44-20.gh-issue-142349.fHK3v1.rst b/Misc/NEWS.d/next/Library/2026-05-15-18-44-20.gh-issue-142349.fHK3v1.rst new file mode 100644 index 00000000000000..fa667c4110941e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-15-18-44-20.gh-issue-142349.fHK3v1.rst @@ -0,0 +1 @@ +Add :keyword:`lazy` to the list of support topic by :func:`help`. diff --git a/Misc/NEWS.d/next/Library/2026-05-15-19-52-41.gh-issue-149891.BJUIGB.rst b/Misc/NEWS.d/next/Library/2026-05-15-19-52-41.gh-issue-149891.BJUIGB.rst new file mode 100644 index 00000000000000..f8bc28659533af --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-15-19-52-41.gh-issue-149891.BJUIGB.rst @@ -0,0 +1 @@ +Add support for more encoding aliases `officially registered in IANA <https://www.iana.org/assignments/character-sets/character-sets.xhtml>`__. diff --git a/Misc/NEWS.d/next/Library/2026-05-16-21-08-33.gh-issue-149921.I1yNML.rst b/Misc/NEWS.d/next/Library/2026-05-16-21-08-33.gh-issue-149921.I1yNML.rst new file mode 100644 index 00000000000000..113bd1a802f799 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-16-21-08-33.gh-issue-149921.I1yNML.rst @@ -0,0 +1,2 @@ +Fix reference leaks in error paths of the :mod:`!_interpchannels` and +:mod:`!_interpqueues` extension modules. diff --git a/Misc/NEWS.d/next/Library/2026-05-16-22-00-00.gh-issue-146406.10e0da.rst b/Misc/NEWS.d/next/Library/2026-05-16-22-00-00.gh-issue-146406.10e0da.rst new file mode 100644 index 00000000000000..8f65fecd85dbf4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-16-22-00-00.gh-issue-146406.10e0da.rst @@ -0,0 +1,3 @@ +Add cross-language hints for ``.clear()`` on :class:`tuple`, +:class:`frozenset`, and :class:`frozendict`, suggesting the mutable +counterpart. Follow-up to :gh:`146406`. diff --git a/Misc/NEWS.d/next/Library/2026-05-17-02-25-56.gh-issue-149571.LNyuWJ.rst b/Misc/NEWS.d/next/Library/2026-05-17-02-25-56.gh-issue-149571.LNyuWJ.rst new file mode 100644 index 00000000000000..2b71d9cf2200be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-17-02-25-56.gh-issue-149571.LNyuWJ.rst @@ -0,0 +1,2 @@ +Fix the C implementation of :meth:`xml.etree.ElementTree.Element.itertext`: +it no longer emits text for comments and processing instructions. diff --git a/Misc/NEWS.d/next/Library/2026-05-17-12-37-59.gh-issue-53144.c5tr1p.rst b/Misc/NEWS.d/next/Library/2026-05-17-12-37-59.gh-issue-53144.c5tr1p.rst new file mode 100644 index 00000000000000..283a5ba44d1f19 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-17-12-37-59.gh-issue-53144.c5tr1p.rst @@ -0,0 +1,2 @@ +The :mod:`email` package now supports all aliases of Python codecs and uses +MIME/IANA names for all IANA registered charsets. diff --git a/Misc/NEWS.d/next/Library/2026-05-17-22-37-02.gh-issue-88726.BAoL6j.rst b/Misc/NEWS.d/next/Library/2026-05-17-22-37-02.gh-issue-88726.BAoL6j.rst new file mode 100644 index 00000000000000..ba9058d79c9873 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-17-22-37-02.gh-issue-88726.BAoL6j.rst @@ -0,0 +1,2 @@ +The :mod:`email` package now uses standard MIME charset names "gb2312" and +"big5" instead of non-standard names "eucgb2312_cn" and "big5_tw". diff --git a/Misc/NEWS.d/next/Library/2026-05-18-07-44-46.gh-issue-149995.vvtFHn.rst b/Misc/NEWS.d/next/Library/2026-05-18-07-44-46.gh-issue-149995.vvtFHn.rst new file mode 100644 index 00000000000000..a8e412b578da37 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-18-07-44-46.gh-issue-149995.vvtFHn.rst @@ -0,0 +1 @@ +Update various docstrings in :mod:`typing`. diff --git a/Misc/NEWS.d/next/Library/2026-05-18-12-42-31.gh-issue-149816.F98iME.rst b/Misc/NEWS.d/next/Library/2026-05-18-12-42-31.gh-issue-149816.F98iME.rst new file mode 100644 index 00000000000000..21e3ae0df57621 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-18-12-42-31.gh-issue-149816.F98iME.rst @@ -0,0 +1,2 @@ +Fix a potential use after free condition in :func:`pickle.dumps` in free-threaded +mode when serializing lists. diff --git a/Misc/NEWS.d/next/Library/2026-05-18-13-43-06.gh-issue-79413.OpTXbV.rst b/Misc/NEWS.d/next/Library/2026-05-18-13-43-06.gh-issue-79413.OpTXbV.rst new file mode 100644 index 00000000000000..8d62fda8b2b67f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-18-13-43-06.gh-issue-79413.OpTXbV.rst @@ -0,0 +1,3 @@ +Update :func:`dataclasses.make_dataclass` to add a *qualname* parameter. The +*qualname* parameter will be used to set the :attr:`!__qualname__` of the +created ``dataclass``. diff --git a/Misc/NEWS.d/next/Library/2026-05-18-17-17-20.gh-issue-149189.a8IooK.rst b/Misc/NEWS.d/next/Library/2026-05-18-17-17-20.gh-issue-149189.a8IooK.rst new file mode 100644 index 00000000000000..bad027f2c71c6f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-18-17-17-20.gh-issue-149189.a8IooK.rst @@ -0,0 +1 @@ +Revert the changes to :mod:`pprint` defaults. Patch by Hugo van Kemenade. diff --git a/Misc/NEWS.d/next/Library/2026-05-18-22-45-54.gh-issue-149816.T68vc_.rst b/Misc/NEWS.d/next/Library/2026-05-18-22-45-54.gh-issue-149816.T68vc_.rst new file mode 100644 index 00000000000000..9996cc7ec0e866 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-18-22-45-54.gh-issue-149816.T68vc_.rst @@ -0,0 +1 @@ +Fix race condition in :attr:`ssl.SSLContext.sni_callback` diff --git a/Misc/NEWS.d/next/Library/2026-05-19-12-00-00.gh-issue-149980.Kx7p2A.rst b/Misc/NEWS.d/next/Library/2026-05-19-12-00-00.gh-issue-149980.Kx7p2A.rst new file mode 100644 index 00000000000000..b056f6a3cda25d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-19-12-00-00.gh-issue-149980.Kx7p2A.rst @@ -0,0 +1,3 @@ +Fix :mod:`tarfile` so that GNU long-name directory entries have all +trailing slashes stripped from their names, matching the behavior for +short-name entries. Previously, only a single trailing slash was removed. diff --git a/Misc/NEWS.d/next/Library/2026-05-19-19-00-49.gh-issue-84353.ZU5zaQ.rst b/Misc/NEWS.d/next/Library/2026-05-19-19-00-49.gh-issue-84353.ZU5zaQ.rst new file mode 100644 index 00000000000000..84fb12e2abd81a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-19-19-00-49.gh-issue-84353.ZU5zaQ.rst @@ -0,0 +1,5 @@ +Preserve non-UTF-8 encoded filenames when appending to a +:class:`zipfile.ZipFile`. Previously, non-ASCII names stored in a legacy +encoding (without the UTF-8 flag bit set) could be corrupted when the +central directory was rewritten: they were decoded as cp437 and then +re-stored as UTF-8. diff --git a/Misc/NEWS.d/next/Library/2026-05-21-11-25-58.gh-issue-150175.8H4Caz.rst b/Misc/NEWS.d/next/Library/2026-05-21-11-25-58.gh-issue-150175.8H4Caz.rst new file mode 100644 index 00000000000000..80fc80d4d50a63 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-21-11-25-58.gh-issue-150175.8H4Caz.rst @@ -0,0 +1,3 @@ +Fix race condition in :class:`unittest.mock.ThreadingMock` where +concurrent calls could lose increments to ``call_count`` and other +attributes due to a missing lock in ``_increment_mock_call``. diff --git a/Misc/NEWS.d/next/Library/2026-05-21-20-47-45.gh-issue-150157.ZvmO-bQZ.rst b/Misc/NEWS.d/next/Library/2026-05-21-20-47-45.gh-issue-150157.ZvmO-bQZ.rst new file mode 100644 index 00000000000000..3a12e26cf736f7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-21-20-47-45.gh-issue-150157.ZvmO-bQZ.rst @@ -0,0 +1,3 @@ +Fix a crash in free-threaded builds that occurs when pickling by name +objects without a ``__module__`` attribute while :data:`sys.modules` +is concurrently being modified. diff --git a/Misc/NEWS.d/next/Library/2026-05-22-15-30-00.gh-issue-132372.YP4a6x.rst b/Misc/NEWS.d/next/Library/2026-05-22-15-30-00.gh-issue-132372.YP4a6x.rst new file mode 100644 index 00000000000000..bcd96e88eac1bf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-22-15-30-00.gh-issue-132372.YP4a6x.rst @@ -0,0 +1,2 @@ +Speed up :func:`logging.config.fileConfig` and +:func:`logging.config.dictConfig` when handling many existing loggers. diff --git a/Misc/NEWS.d/next/Library/2026-05-25-07-22-05.gh-issue-150372.9hLqhe.rst b/Misc/NEWS.d/next/Library/2026-05-25-07-22-05.gh-issue-150372.9hLqhe.rst new file mode 100644 index 00000000000000..7b83bd8fe73f11 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-25-07-22-05.gh-issue-150372.9hLqhe.rst @@ -0,0 +1,2 @@ +:mod:`readline`: Fix a potential crash during tab completion caused by an +out-of-memory error during module initialization. diff --git a/Misc/NEWS.d/next/Library/2026-05-25-17-00-00.gh-issue-150406.jF3g63.rst b/Misc/NEWS.d/next/Library/2026-05-25-17-00-00.gh-issue-150406.jF3g63.rst new file mode 100644 index 00000000000000..230e961abd3f58 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-25-17-00-00.gh-issue-150406.jF3g63.rst @@ -0,0 +1,3 @@ +Fix a possible crash occurring during :mod:`socket` module initialization +when the system is out of memory on platforms without a reentrant +``gethostbyname``. diff --git a/Misc/NEWS.d/next/Library/2026-05-27-11-18-36.gh-issue-150228.pNPiO-.rst b/Misc/NEWS.d/next/Library/2026-05-27-11-18-36.gh-issue-150228.pNPiO-.rst new file mode 100644 index 00000000000000..8c03989e90b240 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-27-11-18-36.gh-issue-150228.pNPiO-.rst @@ -0,0 +1,11 @@ +The new :class:`site.StartupState` class lets callers batch-process +:pep:`829` startup configuration files across multiple site directories +before any startup code runs, with public +:meth:`~site.StartupState.addsitedir`, +:meth:`~site.StartupState.addusersitepackages`, +:meth:`~site.StartupState.addsitepackages`, and +:meth:`~site.StartupState.process` methods. The signature of +:func:`site.addsitedir` is unchanged from Python 3.14. The +:data:`!defer_processing_start_files` argument and the +``process_startup_files()`` function added earlier in the 3.15 cycle have +been removed; use :class:`!site.StartupState` instead. diff --git a/Misc/NEWS.d/next/Library/2026-05-27-15-51-29.gh-issue-150534.3Nan6S.rst b/Misc/NEWS.d/next/Library/2026-05-27-15-51-29.gh-issue-150534.3Nan6S.rst new file mode 100644 index 00000000000000..2ead9990513015 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-27-15-51-29.gh-issue-150534.3Nan6S.rst @@ -0,0 +1,6 @@ +Added trigonometric functions that work in units of half turns, rather than +radians. The new functions :func:`math.acospi`, :func:`math.asinpi`, +:func:`math.atanpi`, and :func:`math.atan2pi` return half-turn angles. The +new functions :func:`math.cospi`, :func:`math.sinpi`, and :func:`math.tanpi` +take half-turn angle arguments. These functions are recommended by IEEE +754-2019 and standardized in C23. diff --git a/Misc/NEWS.d/next/Library/2026-05-27-23-47-31.gh-issue-148932.Y1xmvA.rst b/Misc/NEWS.d/next/Library/2026-05-27-23-47-31.gh-issue-148932.Y1xmvA.rst new file mode 100644 index 00000000000000..a0b7a9740cd518 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-27-23-47-31.gh-issue-148932.Y1xmvA.rst @@ -0,0 +1 @@ +Fix ``profiling.sampling`` on Windows virtual environments to resolve the actual Python PID from a virtual environment shim. diff --git a/Misc/NEWS.d/next/Library/2026-05-28-23-38-46.gh-issue-150478.qEr0E-.rst b/Misc/NEWS.d/next/Library/2026-05-28-23-38-46.gh-issue-150478.qEr0E-.rst new file mode 100644 index 00000000000000..0ceae5e313977a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-28-23-38-46.gh-issue-150478.qEr0E-.rst @@ -0,0 +1,3 @@ +Add ``show_jit`` option to ``dis.dis`` to show JIT entry points in the +bytecode. This is useful for visualizing where JIT traces are entered from +the interpreter. diff --git a/Misc/NEWS.d/next/Library/2026-05-31-10-00-00.gh-issue-150942.Tk9aRef.rst b/Misc/NEWS.d/next/Library/2026-05-31-10-00-00.gh-issue-150942.Tk9aRef.rst new file mode 100644 index 00000000000000..ba0de8fd434d30 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-31-10-00-00.gh-issue-150942.Tk9aRef.rst @@ -0,0 +1,3 @@ +Speed up :func:`json.loads` decoding of arrays and objects by storing +parsed values into the result list/dict without an extra reference-count +round-trip (using the internal reference-stealing append/insert helpers). diff --git a/Misc/NEWS.d/next/Library/2026-05-31-12-00-00.gh-issue-150942.Re7Ref.rst b/Misc/NEWS.d/next/Library/2026-05-31-12-00-00.gh-issue-150942.Re7Ref.rst new file mode 100644 index 00000000000000..63967108b1e0b3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-31-12-00-00.gh-issue-150942.Re7Ref.rst @@ -0,0 +1,3 @@ +Speed up :func:`re.findall`, :func:`re.sub` and :func:`re.subn` by appending +result items to the output list without an extra reference-count round-trip +(using the internal reference-stealing list append helper). diff --git a/Misc/NEWS.d/next/Library/2026-05-31-17-47-30.gh-issue-150685.EBB2mU.rst b/Misc/NEWS.d/next/Library/2026-05-31-17-47-30.gh-issue-150685.EBB2mU.rst new file mode 100644 index 00000000000000..eb7f31112d004c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-31-17-47-30.gh-issue-150685.EBB2mU.rst @@ -0,0 +1 @@ +Update bundled pip to 26.1.2 diff --git a/Misc/NEWS.d/next/Library/2026-06-01-08-12-34.gh-issue-150717.LVRJXH.rst b/Misc/NEWS.d/next/Library/2026-06-01-08-12-34.gh-issue-150717.LVRJXH.rst new file mode 100644 index 00000000000000..da7171fcc72ce4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-01-08-12-34.gh-issue-150717.LVRJXH.rst @@ -0,0 +1,2 @@ +Avoid an unnecessary per-call memory allocation when matching :mod:`re` +patterns that have no capturing groups. Patch by Bernát Gábor. diff --git a/Misc/NEWS.d/next/Library/2026-06-02-14-21-46.gh-issue-150750.SVS2o0.rst b/Misc/NEWS.d/next/Library/2026-06-02-14-21-46.gh-issue-150750.SVS2o0.rst new file mode 100644 index 00000000000000..bda500383e7cda --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-02-14-21-46.gh-issue-150750.SVS2o0.rst @@ -0,0 +1 @@ +Fix a race condition in :meth:`collections.deque.index` with free-threading. diff --git a/Misc/NEWS.d/next/Library/2026-06-02-15-44-57.gh-issue-150817.lpFCh0.rst b/Misc/NEWS.d/next/Library/2026-06-02-15-44-57.gh-issue-150817.lpFCh0.rst new file mode 100644 index 00000000000000..42532bb0222cf4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-02-15-44-57.gh-issue-150817.lpFCh0.rst @@ -0,0 +1,2 @@ +Speed up the ``|``, ``&`` and ``^`` operations on :class:`enum.Flag` members. +Patch by Bernát Gábor. diff --git a/Misc/NEWS.d/next/Library/2026-06-02-15-44-58.gh-issue-150818.Orcefu.rst b/Misc/NEWS.d/next/Library/2026-06-02-15-44-58.gh-issue-150818.Orcefu.rst new file mode 100644 index 00000000000000..3bb16001cc0bb4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-02-15-44-58.gh-issue-150818.Orcefu.rst @@ -0,0 +1,3 @@ +Speed up :func:`logging.getLogger` with a lock-free fast path that returns an +already-registered logger without acquiring the logging lock. Patch by Bernát +Gábor. diff --git a/Misc/NEWS.d/next/Library/2026-06-03-13-51-29.gh-issue-150662.ELT8Vg.rst b/Misc/NEWS.d/next/Library/2026-06-03-13-51-29.gh-issue-150662.ELT8Vg.rst new file mode 100644 index 00000000000000..42ed6ad7cd3c65 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-03-13-51-29.gh-issue-150662.ELT8Vg.rst @@ -0,0 +1,4 @@ +Fix the ``--gecko`` collector in :mod:`profiling.sampling` that kept every +sample in memory. It now writes sample and marker data to temporary files +and reads them back, ultimately building the output file at the end. Patch +by Pablo Galindo and Maurycy Pawłowski-Wieroński. diff --git a/Misc/NEWS.d/next/Library/2026-06-03-21-59-11.gh-issue-150886.r2c25g.rst b/Misc/NEWS.d/next/Library/2026-06-03-21-59-11.gh-issue-150886.r2c25g.rst new file mode 100644 index 00000000000000..b220b63cfdb4ab --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-03-21-59-11.gh-issue-150886.r2c25g.rst @@ -0,0 +1,4 @@ +Remove the private, undocumented function +``importlib._bootstrap._object_name()``. It had no caller after +``load_module()`` and its deprecation warnings were removed from +:mod:`importlib`. diff --git a/Misc/NEWS.d/next/Library/2026-06-04-06-50-06.gh-issue-150898.1LkLA3.rst b/Misc/NEWS.d/next/Library/2026-06-04-06-50-06.gh-issue-150898.1LkLA3.rst new file mode 100644 index 00000000000000..85328c43368e19 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-04-06-50-06.gh-issue-150898.1LkLA3.rst @@ -0,0 +1 @@ +Unconditionally assume :attr:`ssl.SSLContext.keylog_filename` exists. diff --git a/Misc/NEWS.d/next/Library/2026-06-04-10-44-36.gh-issue-150889.UYNLR_.rst b/Misc/NEWS.d/next/Library/2026-06-04-10-44-36.gh-issue-150889.UYNLR_.rst new file mode 100644 index 00000000000000..a5cc1da758637c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-04-10-44-36.gh-issue-150889.UYNLR_.rst @@ -0,0 +1 @@ +Speed up :func:`unicodedata.normalize` for the NFC and NFKC forms of non-ASCII text up to a factor 2. diff --git a/Misc/NEWS.d/next/Library/2026-06-04-18-22-56.gh-issue-143008.z5tw-J.rst b/Misc/NEWS.d/next/Library/2026-06-04-18-22-56.gh-issue-143008.z5tw-J.rst new file mode 100644 index 00000000000000..e99bc39c45f9b8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-04-18-22-56.gh-issue-143008.z5tw-J.rst @@ -0,0 +1 @@ +Fix race conditions when re-initializing a :class:`io.TextIOWrapper` object. diff --git a/Misc/NEWS.d/next/Library/2026-06-04-21-49-18.gh-issue-150913.EmptyBl.rst b/Misc/NEWS.d/next/Library/2026-06-04-21-49-18.gh-issue-150913.EmptyBl.rst new file mode 100644 index 00000000000000..f95a6ee6ee15bf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-04-21-49-18.gh-issue-150913.EmptyBl.rst @@ -0,0 +1,3 @@ +Fix :class:`sqlite3.Blob` slice assignment to raise +:exc:`TypeError` and :exc:`IndexError` for type and size mismatches +respectively, even when the target slice is empty. diff --git a/Misc/NEWS.d/next/Library/2026-06-04-23-10-31.gh-issue-62825.BtG_yQ.rst b/Misc/NEWS.d/next/Library/2026-06-04-23-10-31.gh-issue-62825.BtG_yQ.rst new file mode 100644 index 00000000000000..95a4fb1c61d4c3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-04-23-10-31.gh-issue-62825.BtG_yQ.rst @@ -0,0 +1,2 @@ +Encodings "KS_C_5601-1987", "KS X 1001", etc are now aliases of "CP949" +instead of "EUC-KR". diff --git a/Misc/NEWS.d/next/Library/2026-06-06-15-20-54.gh-issue-151021.J4qk2A.rst b/Misc/NEWS.d/next/Library/2026-06-06-15-20-54.gh-issue-151021.J4qk2A.rst new file mode 100644 index 00000000000000..0617fa068c844d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-06-15-20-54.gh-issue-151021.J4qk2A.rst @@ -0,0 +1,3 @@ +Fix :meth:`mmap.mmap.find` and :meth:`~mmap.mmap.rfind` to return ``-1`` +when searching for an empty subsequence with a start position past the end +of the mapping. diff --git a/Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst b/Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst new file mode 100644 index 00000000000000..1e99567f555057 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-07-17-29-33.gh-issue-151039.AZ0qBn.rst @@ -0,0 +1 @@ +Fix a crash when static :mod:`datetime` types outlive the ``_datetime`` module. diff --git a/Misc/NEWS.d/next/Library/2026-06-08-15-04-35.gh-issue-150285.-Tj94n.rst b/Misc/NEWS.d/next/Library/2026-06-08-15-04-35.gh-issue-150285.-Tj94n.rst new file mode 100644 index 00000000000000..60b1f3e0a71f5b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-08-15-04-35.gh-issue-150285.-Tj94n.rst @@ -0,0 +1 @@ +:mod:`pydoc` now wraps long single-line summary in text output. diff --git a/Misc/NEWS.d/next/Library/2026-06-09-14-49-17.gh-issue-80384.ttRAja.rst b/Misc/NEWS.d/next/Library/2026-06-09-14-49-17.gh-issue-80384.ttRAja.rst new file mode 100644 index 00000000000000..53d4d513ba757d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-09-14-49-17.gh-issue-80384.ttRAja.rst @@ -0,0 +1,2 @@ +:func:`weakref.ref` and :func:`weakref.proxy` now raise :exc:`TypeError` if +the *callback* argument is not callable or ``None``. diff --git a/Misc/NEWS.d/next/Library/2026-06-09-19-43-24.gh-issue-151179.hl_6Z0.rst b/Misc/NEWS.d/next/Library/2026-06-09-19-43-24.gh-issue-151179.hl_6Z0.rst new file mode 100644 index 00000000000000..904edf3a8c7090 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-09-19-43-24.gh-issue-151179.hl_6Z0.rst @@ -0,0 +1,3 @@ +Fix a pidfd leak in ``_PidfdChildWatcher`` on Linux: the watcher no +longer leaks the process file descriptor when ``waitpid()`` fails with an +error other than :exc:`ChildProcessError`. diff --git a/Misc/NEWS.d/next/Library/2026-06-10-00-00-02.gh-issue-109940.Cx1099.rst b/Misc/NEWS.d/next/Library/2026-06-10-00-00-02.gh-issue-109940.Cx1099.rst new file mode 100644 index 00000000000000..130dc780b61286 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-10-00-00-02.gh-issue-109940.Cx1099.rst @@ -0,0 +1,2 @@ +Fix Windows :mod:`venv` activation in ``cmd.exe`` to respect +``VIRTUAL_ENV_DISABLE_PROMPT``. diff --git a/Misc/NEWS.d/next/Library/2026-06-11-00-00-00.gh-issue-151295.NQYUzW.rst b/Misc/NEWS.d/next/Library/2026-06-11-00-00-00.gh-issue-151295.NQYUzW.rst new file mode 100644 index 00000000000000..e9012f023ff7f7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-11-00-00-00.gh-issue-151295.NQYUzW.rst @@ -0,0 +1,4 @@ +Fixed a crash (use-after-free) in :meth:`bytes.join` and +:meth:`bytearray.join` that could occur if an item's +:meth:`~object.__buffer__` concurrently mutates the sequence being joined. +The mutation is now reported as a :exc:`RuntimeError` instead. diff --git a/Misc/NEWS.d/next/Library/2026-06-11-16-25-38.gh-issue-151126.bh_Usy.rst b/Misc/NEWS.d/next/Library/2026-06-11-16-25-38.gh-issue-151126.bh_Usy.rst new file mode 100644 index 00000000000000..25149057aa7d09 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-11-16-25-38.gh-issue-151126.bh_Usy.rst @@ -0,0 +1,2 @@ +Fix a crash when :exc:`MemoryError` in :func:`!os._path_splitroot` +was not set properly. diff --git a/Misc/NEWS.d/next/Library/2026-06-11-19-46-16.gh-issue-150285.wuhAsL.rst b/Misc/NEWS.d/next/Library/2026-06-11-19-46-16.gh-issue-150285.wuhAsL.rst new file mode 100644 index 00000000000000..3f19150488f313 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-11-19-46-16.gh-issue-150285.wuhAsL.rst @@ -0,0 +1,2 @@ +:mod:`pydoc` now uses all available space (80 columns) for formatting reprs +of module and class data, but ensure that they do not overflow. diff --git a/Misc/NEWS.d/next/Library/2026-06-11-21-43-24.gh-issue-151337.JSVV18.rst b/Misc/NEWS.d/next/Library/2026-06-11-21-43-24.gh-issue-151337.JSVV18.rst new file mode 100644 index 00000000000000..0344eee9471d29 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-11-21-43-24.gh-issue-151337.JSVV18.rst @@ -0,0 +1 @@ +Avoid possible memory leak in ``tkinter.c`` on Windows. diff --git a/Misc/NEWS.d/next/Library/2026-06-12-00-04-34.gh-issue-151126.aHaBYq.rst b/Misc/NEWS.d/next/Library/2026-06-12-00-04-34.gh-issue-151126.aHaBYq.rst new file mode 100644 index 00000000000000..20ef69d5de5ac5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-12-00-04-34.gh-issue-151126.aHaBYq.rst @@ -0,0 +1,2 @@ +Fix crash on unset :exc:`MemoryError` on allocation failure in +:func:`ctypes.get_errno`. diff --git a/Misc/NEWS.d/next/Library/2026-06-12-22-46-31.gh-issue-151403.DalZWh.rst b/Misc/NEWS.d/next/Library/2026-06-12-22-46-31.gh-issue-151403.DalZWh.rst new file mode 100644 index 00000000000000..ca779ed684e761 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-12-22-46-31.gh-issue-151403.DalZWh.rst @@ -0,0 +1,3 @@ +Fixed a crash in :class:`subprocess.Popen` (and ``_posixsubprocess.fork_exec``) +when an ``argv`` item's :meth:`~os.PathLike.__fspath__` concurrently mutates the +``args`` sequence being converted. diff --git a/Misc/NEWS.d/next/Library/2026-06-13-04-11-00.gh-issue-151426.f2V67e.rst b/Misc/NEWS.d/next/Library/2026-06-13-04-11-00.gh-issue-151426.f2V67e.rst new file mode 100644 index 00000000000000..428302e5f847f3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-13-04-11-00.gh-issue-151426.f2V67e.rst @@ -0,0 +1,4 @@ +Fix impossible stack traces (callers and callees cross called, orphans and +incorrect lines) in the Tachyon profiler when caching frames, by snapshotting +the stack chunks before walking the frame chain on a cache miss. Patch by +Maurycy Pawłowski-Wieroński. diff --git a/Misc/NEWS.d/next/Security/2025-01-14-11-19-07.gh-issue-128840.M1doZW.rst b/Misc/NEWS.d/next/Security/2025-01-14-11-19-07.gh-issue-128840.M1doZW.rst deleted file mode 100644 index b57ec3e70dcc5f..00000000000000 --- a/Misc/NEWS.d/next/Security/2025-01-14-11-19-07.gh-issue-128840.M1doZW.rst +++ /dev/null @@ -1,2 +0,0 @@ -Short-circuit the processing of long IPv6 addresses early in :mod:`ipaddress` to prevent excessive -memory consumption and a minor denial-of-service. diff --git a/Misc/NEWS.d/next/Security/2025-05-07-22-49-27.gh-issue-133623.fgWkBm.rst b/Misc/NEWS.d/next/Security/2025-05-07-22-49-27.gh-issue-133623.fgWkBm.rst deleted file mode 100644 index 09279bbfb4fd58..00000000000000 --- a/Misc/NEWS.d/next/Security/2025-05-07-22-49-27.gh-issue-133623.fgWkBm.rst +++ /dev/null @@ -1 +0,0 @@ -Indicate through :data:`ssl.HAS_PSK_TLS13` whether the :mod:`ssl` module supports "External PSKs" in TLSv1.3, as described in RFC 9258. Patch by Will Childs-Klein. diff --git a/Misc/NEWS.d/next/Security/2025-05-09-20-22-54.gh-issue-133767.kN2i3Q.rst b/Misc/NEWS.d/next/Security/2025-05-09-20-22-54.gh-issue-133767.kN2i3Q.rst deleted file mode 100644 index 39d2f1e1a892cf..00000000000000 --- a/Misc/NEWS.d/next/Security/2025-05-09-20-22-54.gh-issue-133767.kN2i3Q.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix use-after-free in the "unicode-escape" decoder with a non-"strict" error -handler. diff --git a/Misc/NEWS.d/next/Security/2025-06-02-11-32-23.gh-issue-135034.RLGjbp.rst b/Misc/NEWS.d/next/Security/2025-06-02-11-32-23.gh-issue-135034.RLGjbp.rst deleted file mode 100644 index 08a0087e203671..00000000000000 --- a/Misc/NEWS.d/next/Security/2025-06-02-11-32-23.gh-issue-135034.RLGjbp.rst +++ /dev/null @@ -1,6 +0,0 @@ -Fixes multiple issues that allowed ``tarfile`` extraction filters -(``filter="data"`` and ``filter="tar"``) to be bypassed using crafted -symlinks and hard links. - -Addresses :cve:`2024-12718`, :cve:`2025-4138`, :cve:`2025-4330`, and :cve:`2025-4517`. - diff --git a/Misc/NEWS.d/next/Security/2025-06-09-20-38-25.gh-issue-118350.KgWCcP.rst b/Misc/NEWS.d/next/Security/2025-06-09-20-38-25.gh-issue-118350.KgWCcP.rst deleted file mode 100644 index 6ad3caf33b2201..00000000000000 --- a/Misc/NEWS.d/next/Security/2025-06-09-20-38-25.gh-issue-118350.KgWCcP.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix support of escapable raw text mode (elements "textarea" and "title") -in :class:`html.parser.HTMLParser`. diff --git a/Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst b/Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst deleted file mode 100644 index cf9aa8dbdf2efe..00000000000000 --- a/Misc/NEWS.d/next/Security/2025-06-13-15-55-22.gh-issue-135462.KBeJpc.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fix quadratic complexity in processing specially crafted input in -:class:`html.parser.HTMLParser`. End-of-file errors are now handled according -to the HTML5 specs -- comments and declarations are automatically closed, -tags are ignored. diff --git a/Misc/NEWS.d/next/Security/2025-06-18-13-28-08.gh-issue-102555.nADrzJ.rst b/Misc/NEWS.d/next/Security/2025-06-18-13-28-08.gh-issue-102555.nADrzJ.rst deleted file mode 100644 index 71d15ee0852ebd..00000000000000 --- a/Misc/NEWS.d/next/Security/2025-06-18-13-28-08.gh-issue-102555.nADrzJ.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix comment parsing in :class:`html.parser.HTMLParser` according to the -HTML5 standard. ``--!>`` now ends the comment. ``-- >`` no longer ends the -comment. Support abnormally ended empty comments ``<-->`` and ``<--->``. diff --git a/Misc/NEWS.d/next/Security/2025-06-25-14-13-39.gh-issue-135661.idjQ0B.rst b/Misc/NEWS.d/next/Security/2025-06-25-14-13-39.gh-issue-135661.idjQ0B.rst deleted file mode 100644 index 27e886abdb58e5..00000000000000 --- a/Misc/NEWS.d/next/Security/2025-06-25-14-13-39.gh-issue-135661.idjQ0B.rst +++ /dev/null @@ -1,20 +0,0 @@ -Fix parsing start and end tags in :class:`html.parser.HTMLParser` -according to the HTML5 standard. - -* Whitespaces no longer accepted between ``</`` and the tag name. - E.g. ``</ script>`` does not end the script section. - -* Vertical tabulation (``\v``) and non-ASCII whitespaces no longer recognized - as whitespaces. The only whitespaces are ``\t\n\r\f`` and space. - -* Null character (U+0000) no longer ends the tag name. - -* Attributes and slashes after the tag name in end tags are now ignored, - instead of terminating after the first ``>`` in quoted attribute value. - E.g. ``</script/foo=">"/>``. - -* Multiple slashes and whitespaces between the last attribute and closing ``>`` - are now ignored in both start and end tags. E.g. ``<a foo=bar/ //>``. - -* Multiple ``=`` between attribute name and value are no longer collapsed. - E.g. ``<a foo==bar>`` produces attribute "foo" with value "=bar". diff --git a/Misc/NEWS.d/next/Security/2025-06-27-21-23-19.gh-issue-136053.QZxcee.rst b/Misc/NEWS.d/next/Security/2025-06-27-21-23-19.gh-issue-136053.QZxcee.rst deleted file mode 100644 index 93caed3aa3b9dd..00000000000000 --- a/Misc/NEWS.d/next/Security/2025-06-27-21-23-19.gh-issue-136053.QZxcee.rst +++ /dev/null @@ -1 +0,0 @@ -:mod:`marshal`: fix a possible crash when deserializing :class:`slice` objects. diff --git a/Misc/NEWS.d/next/Security/2026-04-26-19-30-45.gh-issue-149018.a9SqWb.rst b/Misc/NEWS.d/next/Security/2026-04-26-19-30-45.gh-issue-149018.a9SqWb.rst new file mode 100644 index 00000000000000..d1b5b368684e6a --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-04-26-19-30-45.gh-issue-149018.a9SqWb.rst @@ -0,0 +1,3 @@ +Improved protection against XML hash-flooding attacks in +:mod:`xml.parsers.expat` and :mod:`xml.etree.ElementTree` when Python is +compiled with libExpat 2.8.0 or later. diff --git a/Misc/NEWS.d/next/Security/2026-04-27-16-36-11.gh-issue-149079.vKl-LM.rst b/Misc/NEWS.d/next/Security/2026-04-27-16-36-11.gh-issue-149079.vKl-LM.rst new file mode 100644 index 00000000000000..4ed22b58f7405f --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-04-27-16-36-11.gh-issue-149079.vKl-LM.rst @@ -0,0 +1,5 @@ +Fix a potential denial of service in :func:`unicodedata.normalize`. The +canonical ordering step of Unicode normalization used a quadratic-time insertion +sort for reordering combining characters, which could be exploited with +crafted input containing many combining characters in non-canonical order. +Replaced with a linear-time counting sort for long runs. diff --git a/Misc/NEWS.d/next/Security/2026-05-03-21-00-00.gh-issue-149486.tarflt.rst b/Misc/NEWS.d/next/Security/2026-05-03-21-00-00.gh-issue-149486.tarflt.rst new file mode 100644 index 00000000000000..7c69edb683cf80 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-05-03-21-00-00.gh-issue-149486.tarflt.rst @@ -0,0 +1,5 @@ +:func:`tarfile.data_filter` now validates link targets using the same +normalised value that is written to disk, strips trailing separators from +the member name when resolving a symlink's directory, and rejects link +members that would replace the destination directory itself. This closes +several path-traversal bypasses of the ``data`` extraction filter. diff --git a/Misc/NEWS.d/next/Security/2026-05-08-02-18-54.gh-issue-149474.ujQ-mu.rst b/Misc/NEWS.d/next/Security/2026-05-08-02-18-54.gh-issue-149474.ujQ-mu.rst new file mode 100644 index 00000000000000..48e718b95ebe3a --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-05-08-02-18-54.gh-issue-149474.ujQ-mu.rst @@ -0,0 +1,3 @@ +Fix the binary writer in :mod:`profiling.sampling` not firing the audit +(:pep:`578`) when creating the output file. The writer and the reader now +accept any path-like object. Patch by Maurycy Pawłowski-Wieroński. diff --git a/Misc/NEWS.d/next/Security/2026-05-10-18-05-32.gh-issue-87451.XkKB6M.rst b/Misc/NEWS.d/next/Security/2026-05-10-18-05-32.gh-issue-87451.XkKB6M.rst new file mode 100644 index 00000000000000..21a79c3e0e7db7 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-05-10-18-05-32.gh-issue-87451.XkKB6M.rst @@ -0,0 +1,6 @@ +The :mod:`ftplib` module's undocumented ``ftpcp`` function no longer trusts +the IPv4 address value returned from the source server in response to the +``PASV`` command by default, completing the fix for CVE-2021-4189. As with +:class:`ftplib.FTP`, the former behavior can be re-enabled by setting the +``trust_server_pasv_ipv4_address`` attribute on the source :class:`ftplib.FTP` +instance to ``True``. Thanks to Qi Deng at Aurascape AI for the report. diff --git a/Misc/NEWS.d/next/Security/2026-05-11-21-15-07.gh-issue-149698.OudOcW.rst b/Misc/NEWS.d/next/Security/2026-05-11-21-15-07.gh-issue-149698.OudOcW.rst new file mode 100644 index 00000000000000..3c8671b9a5adc4 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-05-11-21-15-07.gh-issue-149698.OudOcW.rst @@ -0,0 +1,2 @@ +Update bundled `libexpat <https://libexpat.github.io/>`_ to version 2.8.1 +for the fix for :cve:`2026-45186`. diff --git a/Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst b/Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst new file mode 100644 index 00000000000000..20cab736552486 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-05-18-17-46-00.gh-issue-149835.EebFlk.rst @@ -0,0 +1,3 @@ +:func:`shutil.move` now resolves symlinks via :func:`os.path.realpath` +when checking whether the destination is inside the source directory, +preventing a symlink-based bypass of that guard. diff --git a/Misc/NEWS.d/next/Security/2026-05-30-09-36-20.gh-issue-150599.nlHqU-.rst b/Misc/NEWS.d/next/Security/2026-05-30-09-36-20.gh-issue-150599.nlHqU-.rst new file mode 100644 index 00000000000000..a37d86cf423f82 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-05-30-09-36-20.gh-issue-150599.nlHqU-.rst @@ -0,0 +1,3 @@ +Fix a possible stack buffer overflow in :mod:`bz2` when a +:class:`bz2.BZ2Decompressor` is reused after a decompression error. +The decompressor now becomes unusable after libbz2 reports an error. diff --git a/Misc/NEWS.d/next/Security/2026-06-09-10-23-57.gh-issue-151159.91GpWQ.rst b/Misc/NEWS.d/next/Security/2026-06-09-10-23-57.gh-issue-151159.91GpWQ.rst new file mode 100644 index 00000000000000..735164c1a65ec3 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-06-09-10-23-57.gh-issue-151159.91GpWQ.rst @@ -0,0 +1 @@ +Update Android and iOS installers to use OpenSSL 3.5.7. diff --git a/Misc/NEWS.d/next/Security/2026-06-09-23-38-08.gh-issue-151159.ds-9f8.rst b/Misc/NEWS.d/next/Security/2026-06-09-23-38-08.gh-issue-151159.ds-9f8.rst new file mode 100644 index 00000000000000..d9251a93b40b2c --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-06-09-23-38-08.gh-issue-151159.ds-9f8.rst @@ -0,0 +1 @@ +Update macOS installer to use OpenSSL 3.5.7. diff --git a/Misc/NEWS.d/next/Tests/2025-05-08-15-06-01.gh-issue-133639.50-kbV.rst b/Misc/NEWS.d/next/Tests/2025-05-08-15-06-01.gh-issue-133639.50-kbV.rst deleted file mode 100644 index 68826cd95fa2b0..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-05-08-15-06-01.gh-issue-133639.50-kbV.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix ``TestPyReplAutoindent.test_auto_indent_default()`` doesn't run -``input_code``. diff --git a/Misc/NEWS.d/next/Tests/2025-05-09-04-11-06.gh-issue-133682.-_lwo3.rst b/Misc/NEWS.d/next/Tests/2025-05-09-04-11-06.gh-issue-133682.-_lwo3.rst deleted file mode 100644 index ebd17f73ca57f1..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-05-09-04-11-06.gh-issue-133682.-_lwo3.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed test case ``test.test_annotationlib.TestStringFormat.test_displays`` which ensures proper handling of complex data structures (lists, sets, dictionaries, and tuples) in string annotations. diff --git a/Misc/NEWS.d/next/Tests/2025-05-09-14-54-48.gh-issue-133744.LCquu0.rst b/Misc/NEWS.d/next/Tests/2025-05-09-14-54-48.gh-issue-133744.LCquu0.rst deleted file mode 100644 index f19186db1adb79..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-05-09-14-54-48.gh-issue-133744.LCquu0.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fix multiprocessing interrupt test. Add an event to synchronize the parent -process with the child process: wait until the child process starts -sleeping. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2025-05-23-09-19-52.gh-issue-134567.hwEIMb.rst b/Misc/NEWS.d/next/Tests/2025-05-23-09-19-52.gh-issue-134567.hwEIMb.rst deleted file mode 100644 index 42e4a01c0ccb4b..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-05-23-09-19-52.gh-issue-134567.hwEIMb.rst +++ /dev/null @@ -1,2 +0,0 @@ -Expose log formatter to users in TestCase.assertLogs. -:func:`unittest.TestCase.assertLogs` will now optionally accept a formatter that will be used to format the strings in output if provided. diff --git a/Misc/NEWS.d/next/Tests/2025-06-04-13-07-44.gh-issue-135120.NapnZT.rst b/Misc/NEWS.d/next/Tests/2025-06-04-13-07-44.gh-issue-135120.NapnZT.rst deleted file mode 100644 index 772173774b1ac1..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-04-13-07-44.gh-issue-135120.NapnZT.rst +++ /dev/null @@ -1 +0,0 @@ -Add :func:`!test.support.subTests`. diff --git a/Misc/NEWS.d/next/Tests/2025-06-11-16-52-49.gh-issue-135401.ccMXmL.rst b/Misc/NEWS.d/next/Tests/2025-06-11-16-52-49.gh-issue-135401.ccMXmL.rst deleted file mode 100644 index 6885fba30dbab0..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-11-16-52-49.gh-issue-135401.ccMXmL.rst +++ /dev/null @@ -1 +0,0 @@ -Add a new GitHub CI job to test the :mod:`ssl` module with `AWS-LC <https://github.com/aws/aws-lc>`_ as the backing cryptography and TLS library. diff --git a/Misc/NEWS.d/next/Tests/2025-06-14-13-20-17.gh-issue-135489.Uh0yVO.rst b/Misc/NEWS.d/next/Tests/2025-06-14-13-20-17.gh-issue-135489.Uh0yVO.rst deleted file mode 100644 index 2c9ecc5182935b..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-14-13-20-17.gh-issue-135489.Uh0yVO.rst +++ /dev/null @@ -1 +0,0 @@ -Show verbose output for failing tests during PGO profiling step with --enable-optimizations. diff --git a/Misc/NEWS.d/next/Tests/2025-06-17-08-48-08.gh-issue-132815.CY1Esu.rst b/Misc/NEWS.d/next/Tests/2025-06-17-08-48-08.gh-issue-132815.CY1Esu.rst deleted file mode 100644 index 5b7485ce2d6a9f..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-17-08-48-08.gh-issue-132815.CY1Esu.rst +++ /dev/null @@ -1 +0,0 @@ -Fix test__opcode: add ``JUMP_BACKWARD`` to specialization stats. diff --git a/Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst b/Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst deleted file mode 100644 index 832d1fe033e229..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-19-15-29-38.gh-issue-135494.FVl9a0.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix regrtest to support excluding tests from ``--pgo`` tests. Patch by -Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2025-06-26-15-15-35.gh-issue-135966.EBpF8Y.rst b/Misc/NEWS.d/next/Tests/2025-06-26-15-15-35.gh-issue-135966.EBpF8Y.rst deleted file mode 100644 index 8dc007431f3919..00000000000000 --- a/Misc/NEWS.d/next/Tests/2025-06-26-15-15-35.gh-issue-135966.EBpF8Y.rst +++ /dev/null @@ -1 +0,0 @@ -The iOS testbed now handles the ``app_packages`` folder as a site directory. diff --git a/Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst b/Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst new file mode 100644 index 00000000000000..e86a9130ff9bfb --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-05-13-14-53-23.gh-issue-149776.orqgsn.rst @@ -0,0 +1,2 @@ +Fix test_socket on Linux kernel 7.1 and newer: skip UDP Lite tests if it's +not supported. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2026-05-22-16-41-45.gh-issue-150114.UdMikH.rst b/Misc/NEWS.d/next/Tests/2026-05-22-16-41-45.gh-issue-150114.UdMikH.rst new file mode 100644 index 00000000000000..a140bf921972ed --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-05-22-16-41-45.gh-issue-150114.UdMikH.rst @@ -0,0 +1,2 @@ +On Linux, regrtest now logs the total memory usage of all Python processes. +Read the private memory in ``/proc/pid/smaps``. Patch by Victor Stinner. diff --git a/Misc/NEWS.d/next/Tests/2026-05-25-15-39-53.gh-issue-150387.yzZ7jq.rst b/Misc/NEWS.d/next/Tests/2026-05-25-15-39-53.gh-issue-150387.yzZ7jq.rst new file mode 100644 index 00000000000000..663a357a179204 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-05-25-15-39-53.gh-issue-150387.yzZ7jq.rst @@ -0,0 +1,5 @@ +Fix hang in +``test.test_profiling.test_sampling_profiler.test_live_collector_ui.TestLiveModeErrors.test_run_failed_script_live`` +on slow buildbots. The test now always queues a final ``q`` keystroke so the +live TUI loop exits even when the profiler collects enough samples to enter +the post-finished input loop. diff --git a/Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst b/Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst new file mode 100644 index 00000000000000..0333e66446ce16 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-06-09-11-52-52.gh-issue-151130.1vslPH.rst @@ -0,0 +1 @@ +Add more tests for ``PyWeakref_*`` C API. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-05-19-14-57-46.gh-issue-134215.sbdDK6.rst b/Misc/NEWS.d/next/Tools-Demos/2025-05-19-14-57-46.gh-issue-134215.sbdDK6.rst deleted file mode 100644 index 546ed2a56b6695..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-05-19-14-57-46.gh-issue-134215.sbdDK6.rst +++ /dev/null @@ -1 +0,0 @@ -:term:`REPL` import autocomplete only suggests private modules when explicitly specified. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-06-11-12-14-06.gh-issue-135379.25ttXq.rst b/Misc/NEWS.d/next/Tools-Demos/2025-06-11-12-14-06.gh-issue-135379.25ttXq.rst deleted file mode 100644 index ebe3ab0e7d1993..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-06-11-12-14-06.gh-issue-135379.25ttXq.rst +++ /dev/null @@ -1,4 +0,0 @@ -The cases generator no longer accepts type annotations on stack items. -Conversions to non-default types are now done explicitly in bytecodes.c and -optimizer_bytecodes.c. This will simplify code generation for top-of-stack -caching and other future features. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-06-26-15-58-13.gh-issue-135968.C4v_-W.rst b/Misc/NEWS.d/next/Tools-Demos/2025-06-26-15-58-13.gh-issue-135968.C4v_-W.rst deleted file mode 100644 index 1c0b3825c71c1d..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-06-26-15-58-13.gh-issue-135968.C4v_-W.rst +++ /dev/null @@ -1 +0,0 @@ -Stubs for ``strip`` are now provided as part of an iOS install. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-07-05-15-10-42.gh-issue-136251.GRM6o8.rst b/Misc/NEWS.d/next/Tools-Demos/2025-07-05-15-10-42.gh-issue-136251.GRM6o8.rst deleted file mode 100644 index 6a35afe15e3875..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2025-07-05-15-10-42.gh-issue-136251.GRM6o8.rst +++ /dev/null @@ -1 +0,0 @@ -Fixes and usability improvements for ``Tools/wasm/emscripten/web_example`` diff --git a/Misc/NEWS.d/next/Tools-Demos/2026-05-22-18-51-09.gh-issue-150258.dh8GVK.rst b/Misc/NEWS.d/next/Tools-Demos/2026-05-22-18-51-09.gh-issue-150258.dh8GVK.rst new file mode 100644 index 00000000000000..02cad6c4f53d92 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2026-05-22-18-51-09.gh-issue-150258.dh8GVK.rst @@ -0,0 +1 @@ +Update the tooltip on the Tachyon flame graph to show both absolute and relative percentages. diff --git a/Misc/NEWS.d/next/Windows/2025-03-31-15-37-57.gh-issue-131942.jip_aL.rst b/Misc/NEWS.d/next/Windows/2025-03-31-15-37-57.gh-issue-131942.jip_aL.rst deleted file mode 100644 index 837f7265bba246..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-03-31-15-37-57.gh-issue-131942.jip_aL.rst +++ /dev/null @@ -1 +0,0 @@ -Use the Python-specific :c:macro:`Py_DEBUG` macro rather than :c:macro:`!_DEBUG` in Windows-related C code. Patch by Xuehai Pan. diff --git a/Misc/NEWS.d/next/Windows/2025-05-07-08-19-15.gh-issue-133537.yzf963.rst b/Misc/NEWS.d/next/Windows/2025-05-07-08-19-15.gh-issue-133537.yzf963.rst deleted file mode 100644 index 94e45f9d8ee741..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-07-08-19-15.gh-issue-133537.yzf963.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid using console I/O in WinAPI partitions that don’t support it diff --git a/Misc/NEWS.d/next/Windows/2025-05-07-09-02-19.gh-issue-133562.lqqNW1.rst b/Misc/NEWS.d/next/Windows/2025-05-07-09-02-19.gh-issue-133562.lqqNW1.rst deleted file mode 100644 index 884425b839a264..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-07-09-02-19.gh-issue-133562.lqqNW1.rst +++ /dev/null @@ -1 +0,0 @@ -Disable handling of security descriptors by :func:`os.mkdir` with mode ``0o700`` on WinAPI partitions that do not support it. This only affects custom builds for specialized targets. diff --git a/Misc/NEWS.d/next/Windows/2025-05-07-11-25-29.gh-issue-133568.oYV0d8.rst b/Misc/NEWS.d/next/Windows/2025-05-07-11-25-29.gh-issue-133568.oYV0d8.rst deleted file mode 100644 index 1e2a5b582cbe7f..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-07-11-25-29.gh-issue-133568.oYV0d8.rst +++ /dev/null @@ -1 +0,0 @@ -Fix compile error when using a WinAPI partition that doesn't support the RPC runtime library. diff --git a/Misc/NEWS.d/next/Windows/2025-05-07-11-45-30.gh-issue-133572.Xc2zxH.rst b/Misc/NEWS.d/next/Windows/2025-05-07-11-45-30.gh-issue-133572.Xc2zxH.rst deleted file mode 100644 index 993eceab0b79c2..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-07-11-45-30.gh-issue-133572.Xc2zxH.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid LsaNtStatus to WinError conversion on unsupported WinAPI partitions. diff --git a/Misc/NEWS.d/next/Windows/2025-05-07-13-04-22.gh-issue-133580.jBMujJ.rst b/Misc/NEWS.d/next/Windows/2025-05-07-13-04-22.gh-issue-133580.jBMujJ.rst deleted file mode 100644 index 414a6f87e651ae..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-07-13-04-22.gh-issue-133580.jBMujJ.rst +++ /dev/null @@ -1 +0,0 @@ -Fix :func:`sys.getwindowsversion` failing without setting an exception when called on some WinAPI partitions. diff --git a/Misc/NEWS.d/next/Windows/2025-05-08-19-07-26.gh-issue-133626.yFTKYK.rst b/Misc/NEWS.d/next/Windows/2025-05-08-19-07-26.gh-issue-133626.yFTKYK.rst deleted file mode 100644 index 6c80d96bb832a1..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-08-19-07-26.gh-issue-133626.yFTKYK.rst +++ /dev/null @@ -1,2 +0,0 @@ -Ensures packages are not accidentally bundled into the traditional -installer. diff --git a/Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst b/Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst deleted file mode 100644 index 550600d5eebf11..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-13-13-25-27.gh-issue-133779.-YcTBz.rst +++ /dev/null @@ -1,6 +0,0 @@ -Reverts the change to generate different :file:`pyconfig.h` files based on -compiler settings, as it was frequently causing extension builds to break. -In particular, the ``Py_GIL_DISABLED`` preprocessor variable must now always -be defined explicitly when compiling for the experimental free-threaded -runtime. The :func:`sysconfig.get_config_var` function can be used to -determine whether the current runtime was compiled with that flag or not. diff --git a/Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst b/Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst deleted file mode 100644 index 958f4f4a4408f7..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-19-03-02-04.gh-issue-76023.vHOf6M.rst +++ /dev/null @@ -1 +0,0 @@ -Make :func:`os.path.realpath` ignore Windows error 1005 when in non-strict mode. diff --git a/Misc/NEWS.d/next/Windows/2025-05-20-21-43-20.gh-issue-130727.-69t4D.rst b/Misc/NEWS.d/next/Windows/2025-05-20-21-43-20.gh-issue-130727.-69t4D.rst deleted file mode 100644 index dc10b3e62c8d4a..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-05-20-21-43-20.gh-issue-130727.-69t4D.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a race in internal calls into WMI that can result in an "invalid handle" -exception under high load. Patch by Chris Eibl. diff --git a/Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst b/Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst deleted file mode 100644 index 36e70b1c0d8cb6..00000000000000 --- a/Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fix a crash that could occur on Windows when a background thread waits on a -:c:type:`PyMutex` while the main thread is shutting down the interpreter. diff --git a/Misc/NEWS.d/next/Windows/2026-04-29-14-44-51.gh-issue-138489.234aj6.rst b/Misc/NEWS.d/next/Windows/2026-04-29-14-44-51.gh-issue-138489.234aj6.rst new file mode 100644 index 00000000000000..4afb8f737b692e --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2026-04-29-14-44-51.gh-issue-138489.234aj6.rst @@ -0,0 +1,4 @@ +Windows distributions now include a :file:`build-details.json` file (see +:pep:`739`). The legacy installer does not install it, but all other +distributions from python.org and all preset configurations in the +``PC\layout`` script will include one. diff --git a/Misc/NEWS.d/next/Windows/2026-05-06-21-36-53.gh-issue-124111.m4OBX8.rst b/Misc/NEWS.d/next/Windows/2026-05-06-21-36-53.gh-issue-124111.m4OBX8.rst new file mode 100644 index 00000000000000..9a57536f1dc96b --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2026-05-06-21-36-53.gh-issue-124111.m4OBX8.rst @@ -0,0 +1 @@ +Updated Windows builds to use Tcl/Tk 9.0.3. diff --git a/Misc/NEWS.d/next/Windows/2026-05-14-22-09-46.gh-issue-149786.UI-HZM.rst b/Misc/NEWS.d/next/Windows/2026-05-14-22-09-46.gh-issue-149786.UI-HZM.rst new file mode 100644 index 00000000000000..64ca91a01f41af --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2026-05-14-22-09-46.gh-issue-149786.UI-HZM.rst @@ -0,0 +1 @@ +Fixes virtual environment launchers on Windows free-threaded builds. diff --git a/Misc/NEWS.d/next/Windows/2026-06-09-11-40-48.gh-issue-151159.JKVfme.rst b/Misc/NEWS.d/next/Windows/2026-06-09-11-40-48.gh-issue-151159.JKVfme.rst new file mode 100644 index 00000000000000..ad1be115db5ce8 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2026-06-09-11-40-48.gh-issue-151159.JKVfme.rst @@ -0,0 +1 @@ +Updated bundled version of OpenSSL to 3.5.7. diff --git a/Misc/NEWS.d/next/Windows/2026-06-09-11-55-41.gh-issue-151163.oizZYV.rst b/Misc/NEWS.d/next/Windows/2026-06-09-11-55-41.gh-issue-151163.oizZYV.rst new file mode 100644 index 00000000000000..580a87400862c5 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2026-06-09-11-55-41.gh-issue-151163.oizZYV.rst @@ -0,0 +1 @@ +Updated Windows builds to include SQLite version 3.53.2. diff --git a/Misc/NEWS.d/next/macOS/2026-05-31-10-40-00.gh-issue-150644.zLWyjj.rst b/Misc/NEWS.d/next/macOS/2026-05-31-10-40-00.gh-issue-150644.zLWyjj.rst new file mode 100644 index 00000000000000..7452a7c765c0a8 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2026-05-31-10-40-00.gh-issue-150644.zLWyjj.rst @@ -0,0 +1,3 @@ +When system logging is enabled (with ``config.use_system_logger``, messages +are now tagged as public. This allows the macOS 26 system logger to view +messages without special configuration. diff --git a/Misc/NEWS.d/next/macOS/2026-06-09-11-52-35.gh-issue-151163.RlPXHq.rst b/Misc/NEWS.d/next/macOS/2026-06-09-11-52-35.gh-issue-151163.RlPXHq.rst new file mode 100644 index 00000000000000..7e9bf6f4587974 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2026-06-09-11-52-35.gh-issue-151163.RlPXHq.rst @@ -0,0 +1 @@ +Updated macOS installer to include SQLite version 3.53.2. diff --git a/Misc/Porting b/Misc/Porting deleted file mode 100644 index f16c460052151c..00000000000000 --- a/Misc/Porting +++ /dev/null @@ -1 +0,0 @@ -This document is moved to https://devguide.python.org/porting/ diff --git a/Misc/README b/Misc/README index cbad9b72dc713c..1993c58ad8c960 100644 --- a/Misc/README +++ b/Misc/README @@ -9,9 +9,7 @@ Files found here ACKS Acknowledgements HISTORY News from previous releases -- oldest last -indent.pro GNU indent profile approximating my C style -NEWS News for this release (for some meaning of "this") -Porting Mini-FAQ on porting to new platforms +NEWS.d/ News files for this release (for some meaning of "this") python-config.in Python script template for python-config python.man UNIX man page for the python interpreter python.pc.in Package configuration info template for pkg-config @@ -22,4 +20,3 @@ SpecialBuilds.txt Describes extra symbols you can set for debug builds svnmap.txt Map of old SVN revs and branches to hg changeset ids, help history-digging valgrind-python.supp Valgrind suppression file, see README.valgrind -vgrindefs Python configuration for vgrind (a generic pretty printer) diff --git a/Misc/externals.spdx.json b/Misc/externals.spdx.json index 69f3beec82ed34..523d20259adaaa 100644 --- a/Misc/externals.spdx.json +++ b/Misc/externals.spdx.json @@ -70,105 +70,105 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "6bb739ecddbd2cfb6d255eb5898437a9b5739277dee931338d3275bac5d96ba2" + "checksumValue": "ca94e7c6c223d9caf77bb51aac5949186379608ea2a0cad3aa8bdf31856912e9" } ], - "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/openssl-3.0.16.tar.gz", + "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/openssl-3.5.7.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:openssl:openssl:3.0.16:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:openssl:openssl:3.5.7:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], "licenseConcluded": "NOASSERTION", "name": "openssl", "primaryPackagePurpose": "SOURCE", - "versionInfo": "3.0.16" + "versionInfo": "3.5.7" }, { "SPDXID": "SPDXRef-PACKAGE-sqlite", "checksums": [ { "algorithm": "SHA256", - "checksumValue": "e335aeb44fa36cde60ecbb6a9f8be6f5d449d645ce9b0199ee53a7e6728d19d2" + "checksumValue": "53f8711811090cc4d9ffc624c360f81e7b409763b145ab2e948998f1a0d6a612" } ], - "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/sqlite-3.49.1.0.tar.gz", + "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/sqlite-3.53.2.0.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:sqlite:sqlite:3.49.1.0:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:sqlite:sqlite:3.53.2.0:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], "licenseConcluded": "NOASSERTION", "name": "sqlite", "primaryPackagePurpose": "SOURCE", - "versionInfo": "3.49.1.0" + "versionInfo": "3.53.2.0" }, { - "SPDXID": "SPDXRef-PACKAGE-tcl-core", + "SPDXID": "SPDXRef-PACKAGE-tcl", "checksums": [ { "algorithm": "SHA256", - "checksumValue": "4c23f0dd3efcbe6f3a22c503a68d147617bb30c4f5290f1eb3eaacf0b460440b" + "checksumValue": "7a1d1f3a2b8f4484a9c2a027a157963c18f85a81785e85fcb5d1e3df6b6a4fd4" } ], - "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/tcl-core-8.6.15.0.tar.gz", + "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/tcl-9.0.3.0.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:tcl_tk:tcl_tk:8.6.15.0:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:tcl_tk:tcl_tk:9.0.3.0:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], "licenseConcluded": "NOASSERTION", - "name": "tcl-core", + "name": "tcl", "primaryPackagePurpose": "SOURCE", - "versionInfo": "8.6.15.0" + "versionInfo": "9.0.3.0" }, { "SPDXID": "SPDXRef-PACKAGE-tk", "checksums": [ { "algorithm": "SHA256", - "checksumValue": "0ae56d39bca92865f338529557a1e56d110594184b6dc5a91339c5675751e264" + "checksumValue": "54fb59df12c489c6264f5b7d3d7444b150d1e3d6561fd59cdb11483440cec000" } ], - "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/tk-8.6.15.0.tar.gz", + "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/tk-9.0.3.1.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:tcl_tk:tcl_tk:8.6.15.0:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:tcl_tk:tcl_tk:9.0.3.1:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], "licenseConcluded": "NOASSERTION", "name": "tk", "primaryPackagePurpose": "SOURCE", - "versionInfo": "8.6.15.0" + "versionInfo": "9.0.3.1" }, { "SPDXID": "SPDXRef-PACKAGE-xz", "checksums": [ { "algorithm": "SHA256", - "checksumValue": "a15c168e39e87d750c3dc766edc7f19bdda57dacf01e509678467eace91ad282" + "checksumValue": "1bfaba0ccacc6681d3ba85335cc7f49c24cf6f9d16f848cbd153b896d8a7d631" } ], - "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/xz-5.2.5.tar.gz", + "downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/xz-5.8.1.1.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:tukaani:xz:5.2.5:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:tukaani:xz:5.8.1.1:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], "licenseConcluded": "NOASSERTION", "name": "xz", "primaryPackagePurpose": "SOURCE", - "versionInfo": "5.2.5" + "versionInfo": "5.8.1.1" }, { "SPDXID": "SPDXRef-PACKAGE-zlib-ng", diff --git a/Misc/indent.pro b/Misc/indent.pro deleted file mode 100644 index 02cceb62021453..00000000000000 --- a/Misc/indent.pro +++ /dev/null @@ -1,24 +0,0 @@ ---blank-lines-after-declarations ---blank-lines-after-procedures ---braces-after-func-def-line ---braces-on-if-line ---braces-on-struct-decl-line ---break-after-boolean-operator ---comment-indentation25 ---comment-line-length79 ---continue-at-parentheses ---dont-cuddle-do-while ---dont-cuddle-else ---indent-level4 ---line-length79 ---no-space-after-casts ---no-space-after-function-call-names ---no-space-after-parentheses ---no-tabs ---procnames-start-lines ---space-after-for ---space-after-if ---space-after-while ---swallow-optional-blank-lines --T PyCFunction --T PyObject diff --git a/Misc/libabigail.abignore b/Misc/libabigail.abignore new file mode 100644 index 00000000000000..369813021faa3d --- /dev/null +++ b/Misc/libabigail.abignore @@ -0,0 +1,25 @@ +# libabigail suppression file for CPython ABI checks +# +# Suppress types defined directly in internal headers (pycore_*.h) +# Regex matches filenames NOT starting with "pycore_", so pycore_* types are suppressed. +[suppress_type] + source_location_not_regexp = ^([^p]|p[^y]|py[^c]|pyc[^o]|pyco[^r]|pycor[^e]|pycore[^_]) + accessed_through = pointer + +# Suppress public typedefs that alias internal structs. +# These are public names but their underlying struct layout is internal. +[suppress_type] + name = PyInterpreterState + accessed_through = pointer + +[suppress_type] + name = _PyRuntimeState + accessed_through = pointer + +[suppress_type] + name = PyThreadState + accessed_through = pointer + +[suppress_variable] + name = _PyRuntime + type_name = _PyRuntimeState diff --git a/Misc/python.man b/Misc/python.man index 612e2bbf56800e..5e4f3c3debe935 100644 --- a/Misc/python.man +++ b/Misc/python.man @@ -1,4 +1,4 @@ -.TH PYTHON "1" +.TH PYTHON 1 .\" To view this file while editing, run it through groff: .\" groff -Tascii -man python.man | less @@ -162,7 +162,7 @@ compilation options). Ignore environment variables like PYTHONPATH and PYTHONHOME that modify the behavior of the interpreter. .TP -.B \-h ", " \-? ", "\-\-help +.BR \-h ", " \-? ", " \-\-help Prints the usage for the interpreter executable and exits. .TP .B "\-\-help\-env" @@ -171,7 +171,6 @@ Prints help about Python-specific environment variables and exits. .B "\-\-help\-xoptions" Prints help about implementation-specific \fB\-X\fP options and exits. .TP -.TP .B "\-\-help\-all" Prints complete usage information and exits. .TP @@ -243,7 +242,7 @@ twice, print a message for each file that is checked for when searching for a module. Also provides information on module cleanup at exit. .TP -.B \-V ", " \-\-version +.BR \-V ", " \-\-version Prints the Python version number of the executable and exits. When given twice, print more information about the build. @@ -255,23 +254,38 @@ to The simplest settings apply a particular action unconditionally to all warnings emitted by a process (even those that are otherwise ignored by default): - - -Wdefault # Warn once per call location - -Werror # Convert to exceptions - -Walways # Warn every time - -Wall # Same as -Walways - -Wmodule # Warn once per calling module - -Wonce # Warn once per Python process - -Wignore # Never warn - +.RS +.TP +.B \-Wdefault +Warn once per call location +.TP +.B \-Werror +Convert to exceptions +.TP +.B \-Walways +Warn every time +.TP +.B \-Wall +Same as \-Walways +.TP +.B \-Wmodule +Warn once per calling module +.TP +.B \-Wonce +Warn once per Python process +.TP +.B \-Wignore +Never warn +.RE +.IP The action names can be abbreviated as desired and the interpreter will resolve them to the appropriate action name. For example, .B \-Wi is the same as -.B \-Wignore . +.BR \-Wignore . The full form of argument is: -.IB action:message:category:module:lineno +.IB action : message : category : module : lineno Empty fields match all values; trailing empty fields may be omitted. For example @@ -320,82 +334,105 @@ a regular expression on the warning message. .TP .BI "\-X " option Set implementation-specific option. The following options are available: - - \fB\-X cpu_count=\fIN\fR: override the return value of \fIos.cpu_count()\fR; - \fB\-X cpu_count=default\fR cancels overriding; also \fBPYTHON_CPU_COUNT\fI - - \fB\-X dev\fR: enable CPython's "development mode", introducing additional - runtime checks which are too expensive to be enabled by default. It - will not be more verbose than the default if the code is correct: new - warnings are only emitted when an issue is detected. Effect of the - developer mode: - * Add default warning filter, as \fB\-W default\fR - * Install debug hooks on memory allocators: see the - PyMem_SetupDebugHooks() C function - * Enable the faulthandler module to dump the Python traceback on a - crash - * Enable asyncio debug mode - * Set the dev_mode attribute of sys.flags to True - * io.IOBase destructor logs close() exceptions - - \fB\-X importtime\fR: show how long each import takes. It shows module name, - cumulative time (including nested imports) and self time (excluding - nested imports). Note that its output may be broken in multi-threaded - application. Typical usage is - \fBpython3 \-X importtime \-c 'import asyncio'\fR - - \fB\-X importtime=2\fR enables additional output that indicates when an - imported module has already been loaded. In such cases, the string - \fBcached\fR will be printed in both time columns. - - \fB\-X faulthandler\fR: enable faulthandler - - \fB\-X frozen_modules=\fR[\fBon\fR|\fBoff\fR]: whether or not frozen modules - should be used. - The default is "on" (or "off" if you are running a local build). - - \fB\-X gil=\fR[\fB0\fR|\fB1\fR]: enable (1) or disable (0) the GIL; also - \fBPYTHON_GIL\fR - Only available in builds configured with \fB\-\-disable\-gil\fR. - - \fB\-X int_max_str_digits=\fInumber\fR: limit the size of int<->str conversions. - This helps avoid denial of service attacks when parsing untrusted data. - The default is sys.int_info.default_max_str_digits. 0 disables. - - \fB\-X no_debug_ranges\fR: disable the inclusion of the tables mapping extra - location information (end line, start column offset and end column - offset) to every instruction in code objects. This is useful when - smaller code objects and pyc files are desired as well as suppressing - the extra visual location indicators when the interpreter displays - tracebacks. - - \fB\-X perf\fR: support the Linux "perf" profiler; also \fBPYTHONPERFSUPPORT=1\fR - - \fB\-X perf_jit\fR: support the Linux "perf" profiler with DWARF support; - also \fBPYTHON_PERF_JIT_SUPPORT=1\fR - - \fB\-X presite=\fIMOD\fR: import this module before site; also \fBPYTHON_PRESITE\fR - This only works on debug builds. - - \fB\-X pycache_prefix=\fIPATH\fR: enable writing .pyc files to a parallel - tree rooted at the given directory instead of to the code tree. - - \fB\-X showrefcount\fR: output the total reference count and number of used - memory blocks when the program finishes or after each statement in the - interactive interpreter. This only works on debug builds - - \fB\-X tracemalloc\fR: start tracing Python memory allocations using the - tracemalloc module. By default, only the most recent frame is stored in a - traceback of a trace. Use \-X tracemalloc=NFRAME to start tracing with a - traceback limit of NFRAME frames - - \fB\-X utf8\fR: enable UTF-8 mode for operating system interfaces, - overriding the default locale-aware mode. \fB\-X utf8=0\fR explicitly - disables UTF-8 mode (even when it would otherwise activate - automatically). See \fBPYTHONUTF8\fR for more details - - \fB\-X warn_default_encoding\fR: enable opt-in EncodingWarning for 'encoding=None' - +.RS +.TP +\fB\-X cpu_count=\fIN\fR +Override the return value of \fIos.cpu_count()\fR. +\fB\-X cpu_count=default\fR cancels overriding. +See also \fBPYTHON_CPU_COUNT\fR. +.TP +\fB\-X dev\fR +Enable CPython's "development mode", introducing additional +runtime checks which are too expensive to be enabled by default. It +will not be more verbose than the default if the code is correct: new +warnings are only emitted when an issue is detected. Effect of the +developer mode: +.RS +.IP \(bu 2 +Add default warning filter, as \fB\-W default\fR. +.IP \(bu 2 +Install debug hooks on memory allocators: see the +PyMem_SetupDebugHooks() C function. +.IP \(bu 2 +Enable the faulthandler module to dump the Python traceback on a crash. +.IP \(bu 2 +Enable asyncio debug mode. +.IP \(bu 2 +Set the dev_mode attribute of sys.flags to True. +.IP \(bu 2 +io.IOBase destructor logs close() exceptions. +.RE +.TP +\fB\-X importtime\fR +Show how long each import takes. It shows module name, +cumulative time (including nested imports) and self time (excluding +nested imports). Note that its output may be broken in multi-threaded +application. Typical usage is +\fBpython3 \-X importtime \-c 'import asyncio'\fR. +.IP +\fB\-X importtime=2\fR enables additional output that indicates when an +imported module has already been loaded. In such cases, the string +\fBcached\fR will be printed in both time columns. +.TP +\fB\-X faulthandler\fR +Enable faulthandler. +.TP +\fB\-X frozen_modules=\fR[\fBon\fR|\fBoff\fR] +Whether or not frozen modules should be used. +The default is "on" (or "off" if you are running a local build). +.TP +\fB\-X gil=\fR[\fB0\fR|\fB1\fR] +Enable (1) or disable (0) the GIL. See also \fBPYTHON_GIL\fR. +Only available in builds configured with \fB\-\-disable\-gil\fR. +.TP +\fB\-X int_max_str_digits=\fInumber\fR +Limit the size of int<->str conversions. +This helps avoid denial of service attacks when parsing untrusted data. +The default is sys.int_info.default_max_str_digits. 0 disables. +.TP +\fB\-X no_debug_ranges\fR +Disable the inclusion of the tables mapping extra +location information (end line, start column offset and end column +offset) to every instruction in code objects. This is useful when +smaller code objects and pyc files are desired as well as suppressing +the extra visual location indicators when the interpreter displays +tracebacks. +.TP +\fB\-X perf\fR +Support the Linux "perf" profiler. See also \fBPYTHONPERFSUPPORT=1\fR. +.TP +\fB\-X perf_jit\fR +Support the Linux "perf" profiler with DWARF support. +See also \fBPYTHON_PERF_JIT_SUPPORT=1\fR. +.TP +\fB\-X presite=\fIMOD\fR +Import this module before site. See also \fBPYTHON_PRESITE\fR. +This only works on debug builds. +.TP +\fB\-X pycache_prefix=\fIPATH\fR +Enable writing .pyc files to a parallel +tree rooted at the given directory instead of to the code tree. +.TP +\fB\-X showrefcount\fR +Output the total reference count and number of used +memory blocks when the program finishes or after each statement in the +interactive interpreter. This only works on debug builds. +.TP +\fB\-X tracemalloc\fR +Start tracing Python memory allocations using the +tracemalloc module. By default, only the most recent frame is stored in a +traceback of a trace. Use \fB\-X tracemalloc=\fINFRAME\fR to start tracing with a +traceback limit of NFRAME frames. +.TP +\fB\-X utf8\fR +Enable UTF-8 mode for operating system interfaces, +overriding the default locale-aware mode. \fB\-X utf8=0\fR explicitly +disables UTF-8 mode (even when it would otherwise activate +automatically). See \fBPYTHONUTF8\fR for more details. +.TP +\fB\-X warn_default_encoding\fR +Enable opt-in EncodingWarning for 'encoding=None'. +.RE .TP .B \-x Skip the first line of the source. This is intended for a DOS @@ -434,7 +471,7 @@ is an empty string; if is used, .I sys.argv[0] contains the string -.I '\-c'. +.RI ' \-c '. Note that options interpreted by the Python interpreter itself are not placed in .IR sys.argv . @@ -508,12 +545,12 @@ the \fB\-d\fP option. If set to an integer, it is equivalent to specifying \fB\-d\fP multiple times. .IP PYTHONEXECUTABLE If this environment variable is set, -.IB sys.argv[0] +.I sys.argv[0] will be set to its value instead of the value got through the C runtime. Only works on Mac OS X. .IP PYTHONFAULTHANDLER If this environment variable is set to a non-empty string, -.IR faulthandler.enable() +.I faulthandler.enable() is called at startup: install a handler for SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL signals to dump the Python traceback. .IP @@ -561,8 +598,8 @@ purpose is to allow repeatable hashing, such as for selftests for the interpreter itself, or to allow a cluster of python processes to share hash values. -The integer must be a decimal number in the range [0,4294967295]. Specifying -the value 0 will disable hash randomization. +The integer must be a decimal number in the range [0,4\|294\|967\|295]. +Specifying the value 0 will disable hash randomization. .IP PYTHONHOME Change the location of the standard Python libraries. By default, the libraries are searched in ${prefix}/lib/python<version> and @@ -584,16 +621,16 @@ This is equivalent to the \fB\-X int_max_str_digits=\fINUMBER\fR option. .IP PYTHONIOENCODING If this is set before running the interpreter, it overrides the encoding used for stdin/stdout/stderr, in the syntax -.IB encodingname ":" errorhandler +.IB encodingname : errorhandler The -.IB errorhandler +.I errorhandler part is optional and has the same meaning as in str.encode. For stderr, the -.IB errorhandler +.I errorhandler part is ignored; the handler will always be \'backslashreplace\'. .IP PYTHONMALLOC Set the Python memory allocators and/or install debug hooks. The available memory allocators are -.IR malloc +.I malloc and .IR pymalloc . The available debug hooks are @@ -603,7 +640,7 @@ and .IR pymalloc_debug . .IP When Python is compiled in debug mode, the default is -.IR pymalloc_debug +.I pymalloc_debug and the debug hooks are automatically used. Otherwise, the default is .IR pymalloc . .IP PYTHONMALLOCSTATS @@ -684,14 +721,14 @@ Python memory allocations using the tracemalloc module. .IP The value of the variable is the maximum number of frames stored in a traceback of a trace. For example, -.IB PYTHONTRACEMALLOC=1 +.I PYTHONTRACEMALLOC=1 stores only the most recent frame. .IP PYTHONUNBUFFERED If this is set to a non-empty string it is equivalent to specifying the \fB\-u\fP option. .IP PYTHONUSERBASE Defines the user base directory, which is used to compute the path of the user -.IR site\-packages +.I site\-packages directory and installation paths for .IR "python \-m pip install \-\-user" . .IP PYTHONUTF8 @@ -727,17 +764,17 @@ This is equivalent to the \fB\-X presite=\fImodule\fR option. .SH AUTHOR The Python Software Foundation: https://www.python.org/psf/ .SH INTERNET RESOURCES -Main website: https://www.python.org/ +Main website: https://www.python.org/ .br -Documentation: https://docs.python.org/ +Documentation: https://docs.python.org/ .br -Developer resources: https://devguide.python.org/ +Developer resources: https://devguide.python.org/ .br -Downloads: https://www.python.org/downloads/ +Downloads: https://www.python.org/downloads/ .br -Module repository: https://pypi.org/ +Module repository: https://pypi.org/ .br -Newsgroups: comp.lang.python, comp.lang.python.announce +Newsgroups: comp.lang.python, comp.lang.python.announce .SH LICENSING Python is distributed under an Open Source license. See the file "LICENSE" in the Python source distribution for information on terms & diff --git a/Misc/sbom.spdx.json b/Misc/sbom.spdx.json index 738b33908857a8..5791592dc77a84 100644 --- a/Misc/sbom.spdx.json +++ b/Misc/sbom.spdx.json @@ -6,11 +6,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "39e6f567a10e36b2e77727e98e60bbcb3eb3af0b" + "checksumValue": "f1b1126ed7da8f2068302e7a692b0600e6f94b07" }, { "algorithm": "SHA256", - "checksumValue": "122f2c27000472a201d337b9b31f7eb2b52d091b02857061a8880371612d9534" + "checksumValue": "31b15de82aa19a845156169a17a5488bf597e561b2c318d159ed583139b25e87" } ], "fileName": "Modules/expat/COPYING" @@ -48,11 +48,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "6984055af7b4e01429d8ebc910fe2be900d8ee9c" + "checksumValue": "58101ef0951568acadd3117033bef084fea24cc1" }, { "algorithm": "SHA256", - "checksumValue": "7c16a5cf0eea844ae579db083b8d75f23a71859cac77e3c4cb7a8fa3b7621685" + "checksumValue": "52d756026bf09befdb211c453e2009a646d6c6b519e6885e971b2550396619fb" } ], "fileName": "Modules/expat/expat.h" @@ -62,11 +62,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "9e615c6e5c3ba00670f674a6b071bb855b0b563d" + "checksumValue": "d8f9211d52ff0384e229e4d4d56adae5db2d7f91" }, { "algorithm": "SHA256", - "checksumValue": "3d90a4b65c40a3f848c36100f4d73b933a015c7b7cd85c28e4331a6b845c1ad0" + "checksumValue": "b77f8192baf90aaa41f7023bc68fd1f22ab2552f98758271a1e090544537def5" } ], "fileName": "Modules/expat/expat_external.h" @@ -90,11 +90,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "60b0ee8b4a93ef0276193ed1051c15ecab73c02e" + "checksumValue": "2555e70b29c1efc0af40879daafd12f8b36aca2c" }, { "algorithm": "SHA256", - "checksumValue": "6af6e8fbf5c83c1431464a2811b10ea2d1ff64c0eabfd9f18b1d4e53bf400c35" + "checksumValue": "4feb1df53898a48ae0ae04b5d0352c90395c8e693e5c2675f8ced41903d6fa94" } ], "fileName": "Modules/expat/internal.h" @@ -174,11 +174,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "3db0435d69e5eb904c9c88400a5ab073a81049bc" + "checksumValue": "1dad2ab196cdbe37572674c465bd9187fdbe4495" }, { "algorithm": "SHA256", - "checksumValue": "633b272fa893dfbef539edbba35f1b11ecf09a13b89189105b0dfa6c7ecfc3bf" + "checksumValue": "740137e670d2f3b7269364ffb6f60064e6560091850c5d6f2c3bb1b8ca6e3dd1" } ], "fileName": "Modules/expat/xmlparse.c" @@ -188,11 +188,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "ef767128d2dda99436712dcf3465dde5dbaab876" + "checksumValue": "c8769fcb93f00272a6e6ca560be633649c817ff7" }, { "algorithm": "SHA256", - "checksumValue": "71fb52aa302cf6f56e41943009965804f49ff2210d9bd15b258f70aaf70db772" + "checksumValue": "5b81f0eb0e144b611dbd1bc9e6037075a16bff94f823d57a81eb2a3e4999e91a" } ], "fileName": "Modules/expat/xmlrole.c" @@ -202,11 +202,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "c961fb1a80f7b0601a63e69fba793fe5f6dff157" + "checksumValue": "ac2964cca107f62dd133bfd4736a9a17defbc401" }, { "algorithm": "SHA256", - "checksumValue": "228470eb9181a9a7575b63137edcb61b817ee4e0923faffdbeba29e07c939713" + "checksumValue": "92e41f373b67f6e0dcd7735faef3c3f1e2c17fe59e007e6b74beef6a2e70fa88" } ], "fileName": "Modules/expat/xmlrole.h" @@ -216,11 +216,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "8394790c0199c8f88108542ad78f23095d28a3fe" + "checksumValue": "63e4766a09e63760c6518670509198f8d638f4ad" }, { "algorithm": "SHA256", - "checksumValue": "5b16c671ccc42496374762768e4bf48f614aecfd2025a07925b8d94244aec645" + "checksumValue": "0ad3f915f2748dc91bf4e4b4a50cf40bf2c95769d0eca7e3b293a230d82bb779" } ], "fileName": "Modules/expat/xmltok.c" @@ -230,11 +230,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "7d2943a0128094455004b1a98007b98734221bae" + "checksumValue": "d126831eaa5158cff187a8c93f4bc1c8118f3b17" }, { "algorithm": "SHA256", - "checksumValue": "6b8919dc951606dc6f2b0175f8955a9ced901ce8bd08db47f291b6c04227ae7f" + "checksumValue": "91bf003a725a675761ea8d92cebc299a76fd28c3a950572f41bc7ce5327ee7b5" } ], "fileName": "Modules/expat/xmltok.h" @@ -272,11 +272,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "2d82d0a1201f78d478b30d108ff8fc27ee3e2672" + "checksumValue": "41b8c8fc275882c76d4210b7d40a18e506b07147" }, { "algorithm": "SHA256", - "checksumValue": "6ce6d03193279078d55280150fe91e7370370b504a6c123a79182f28341f3e90" + "checksumValue": "b2188c7e5fa5b33e355cf6cf342dfb8f6e23859f2a6b1ddf79841d7f84f7b196" } ], "fileName": "Modules/expat/xmltok_ns.c" @@ -300,11 +300,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "de7179fe6970e2b5d281dfed977ed91be635b8d2" + "checksumValue": "a57d53c35aa916ce0cd1567c8f10dcea58270321" }, { "algorithm": "SHA256", - "checksumValue": "c0ba888d87775c7d7f7d8a08dac7b3988fed81e11bb52396d90f762a8e90a7eb" + "checksumValue": "d68209032703137326f9aadd9abac8fe4a5c92d391e2f43b0414fa63087adc6b" } ], "fileName": "Modules/_hacl/Hacl_HMAC.h" @@ -328,11 +328,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "7c66ac004a1dcf3fee0ab9aa62d61972f029de3a" + "checksumValue": "4d767388a34e2a2000e0cbd31f06cf36af1ae10d" }, { "algorithm": "SHA256", - "checksumValue": "9a7239a01a4ee8defbe3ebd9f0d12c873a1dd8e0659070380b2eab3ab0177333" + "checksumValue": "fac493e96af252abcf5705f0ab4eec59c1f3bc8244e105d75a57c43552dd1569" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2b.h" @@ -356,11 +356,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "7f273d26942233e5dcdfb4c1a16ff2486b15f899" + "checksumValue": "280fd03ee23e13ecf90711d2be8230035d957343" }, { "algorithm": "SHA256", - "checksumValue": "dbc0dacc68ed52dbf1b7d6fba2c87870317998bc046e65f6deaaa150625432f8" + "checksumValue": "817dcd05d06d804587fce7d8f2f3f42a6fcf6818d2419a3551ef0df70cb7125a" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h" @@ -398,11 +398,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "2120c8c467aeebcc7c8b9678c15e79648433b91a" + "checksumValue": "79f47ab5458d88bce0f210a566aa117ce2125049" }, { "algorithm": "SHA256", - "checksumValue": "45735f7fe2dbbad7656d07854e9ec8176ad26c79f90dcc0fec0b9a59a6311ba7" + "checksumValue": "5cc96313f8c066f055c2819e473c79aeff086ba91a1449d54aa569127cd8601e" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2s.h" @@ -426,11 +426,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "9028e24c9876d9d16b2435ec29240c6b57bfe2a0" + "checksumValue": "c98890b6193baa3dbd472cfb67f22aea3dd0d3e1" }, { "algorithm": "SHA256", - "checksumValue": "062e3b856acac4f929c1e04b8264a754cad21ca6580215f7094a3f0a04edb912" + "checksumValue": "fe3f17bf237166f872ba88c8204333c4f64bdc4bb29c265448581c7bfea4b151" } ], "fileName": "Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h" @@ -468,11 +468,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "e67a9bc18358c57afaeff3a174893ddfdb52dfc6" + "checksumValue": "2ed70f612a998ef6c04d62f9487a1b2534e6d76a" }, { "algorithm": "SHA256", - "checksumValue": "16e982081f6c2fd03ea751fcc64f5a835c94652841836e231fe562b9e287f4bc" + "checksumValue": "a34534ef36bc8428ac1169ca5f4f1c17a0817507ebae388e3dd825d1a331f28d" } ], "fileName": "Modules/_hacl/Hacl_Hash_MD5.h" @@ -496,11 +496,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "f1ca21f1ee8b15ad9ccfbda72165b9d86912166c" + "checksumValue": "a3f9bdc9e73f80eb4a586dc180246cfb8267ffc0" }, { "algorithm": "SHA256", - "checksumValue": "4b2ad9ea93fdd9c2fdc521fc4e14e02550666c2717a23b85819db2e07ea555f3" + "checksumValue": "2d5cae94382f5473cf6d2654760375ff1ee9cebdb8d4506b63ba33f488de1559" } ], "fileName": "Modules/_hacl/Hacl_Hash_SHA1.h" @@ -524,11 +524,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "f38cebeeca40a83aeb2cf5dfce578ffefe176d84" + "checksumValue": "68b35d0c573f7301ba4d6919b1680d55675a0d98" }, { "algorithm": "SHA256", - "checksumValue": "ee03bf9368d1a3a3c70cfd4e9391b2485466404db4a60bfc5319630cc314b590" + "checksumValue": "442d8997d2bcda20ddf628665e2e69945400e1ab3019bc14fc7c8e20db20c320" } ], "fileName": "Modules/_hacl/Hacl_Hash_SHA2.h" @@ -552,11 +552,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "01717207aef77174e328186d48c27517f6644c15" + "checksumValue": "db1008095b64b2f8ee2a4ce72fa7cfc4a622a108" }, { "algorithm": "SHA256", - "checksumValue": "620dded172e94cb3f25f9904b44977d91f2cc9573e41b38f19e929d083ae0308" + "checksumValue": "961a686186b76a1cd6a64bcfd06afdc8657b1f901bac991166f498ed16f9349a" } ], "fileName": "Modules/_hacl/Hacl_Hash_SHA3.h" @@ -566,11 +566,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "417e68ac8498cb2f93a06a19003ea1cc3f0f6753" + "checksumValue": "eb224e26bc0503a48c88c837e28bbc7a9878ba5c" }, { "algorithm": "SHA256", - "checksumValue": "843db4bba78a476d4d53dabe4eed5c9235f490ccc9fdaf19e22af488af858920" + "checksumValue": "29fcf948a3715e09cbbd434cebb6105f276236a6e4947d237b7bf06634bf2432" } ], "fileName": "Modules/_hacl/Hacl_Streaming_HMAC.c" @@ -580,11 +580,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "49523144583a15d96ba1646af02dc292e633bf8f" + "checksumValue": "2d6c600024275c780e8313e0a5ab506e025bb4d6" }, { "algorithm": "SHA256", - "checksumValue": "78345519bf6789264f6792b809ee97a9ecf7cb5829c674c61e2d99bfdfdc36fc" + "checksumValue": "6450aef92f507fb0a8a3086b1d2792e7fca07121580c24fdaedd1b32e9ad0a76" } ], "fileName": "Modules/_hacl/Hacl_Streaming_HMAC.h" @@ -594,11 +594,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "372448599774a98e5c5d083e91f301ed1c4b822e" + "checksumValue": "ed283b95ebb772b05bdf802b70dbb301ece84e91" }, { "algorithm": "SHA256", - "checksumValue": "95d8e70ca4bc6aa98f6d2435ceb6410ead299b1f700fae1f5c603ec3f57ea551" + "checksumValue": "efc7bd11460744768425aedf4e004d3ad3397a5489752b80044ae785f30b78d4" } ], "fileName": "Modules/_hacl/Hacl_Streaming_Types.h" @@ -608,11 +608,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "0a0b7f3714167ad45ddf5a6a48d76f525a119c9c" + "checksumValue": "67a029736da5efe96f1b2d41b92f00934b955253" }, { "algorithm": "SHA256", - "checksumValue": "135d4afb4812468885c963c9c87a55ba5fae9181df4431af5fbad08588dda229" + "checksumValue": "0d80c2e3f5ff3b31ef0d435fc3196660df89ccf95bdcbb6545dc2f9df07bec9f" } ], "fileName": "Modules/_hacl/Lib_Memzero0.c" @@ -622,11 +622,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "911c97a0f24067635b164fbca49da76055f192cd" + "checksumValue": "ae06c415ae7e0e1e85558863f29d1b9013e46e9b" }, { "algorithm": "SHA256", - "checksumValue": "972b5111ebada8e11dd60df3119da1af505fd3e0b6c782ead6cab7f1daf134f1" + "checksumValue": "69dc2e78411e9b271100eb0d2a2d8dc39dd2348afc26f8b4cfba14c025102c7f" } ], "fileName": "Modules/_hacl/include/krml/FStar_UInt128_Verified.h" @@ -636,11 +636,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "41ee1e34ede7ef5b24b87d4ca816fd6d9fac8010" + "checksumValue": "edefe48ced707327fd6cdf3f18b3ca42dda9a9f7" }, { "algorithm": "SHA256", - "checksumValue": "d48ed03e504cb87793a310a9552fb3ba2ebd6fe90127b7d642c8740fba1b9748" + "checksumValue": "f01e3f0892935f3f659019875f580445b9ea23482afbe11ffbe7cdbd22dbd4d2" } ], "fileName": "Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h" @@ -678,11 +678,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "e01d7d493fbaceeedc4b1c6451d8240bcb9c903a" + "checksumValue": "7a943fbb8f55729a960f9b426e9ff2794453aca9" }, { "algorithm": "SHA256", - "checksumValue": "c2f0a43884771f24d7cb744b79818b160020d2739b2881b2054cfc97fb2e7b4a" + "checksumValue": "08bb43ef5626a8450f985da0af345b6658ac8bcc4585f77271decd0e41fc02e0" } ], "fileName": "Modules/_hacl/include/krml/internal/target.h" @@ -692,11 +692,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "3f66313d16891f43b21c1a736081c2c6d46bf370" + "checksumValue": "b4e6c5fc5e17864cc2bf452db2688be733d6df72" }, { "algorithm": "SHA256", - "checksumValue": "78e9bff9124968108e1699e1c6388e3d4ec9bd72dd8adff49734a69ab380ee5c" + "checksumValue": "404691cf9b2269ecc754ceca27bb8f8f029f1c8deaa4d967990dcf5ce08cd016" } ], "fileName": "Modules/_hacl/include/krml/internal/types.h" @@ -706,11 +706,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "e18efc9239a5df0f222b5f7b0a65f72509d7e304" + "checksumValue": "8c3e09e00459951e060ad469c1fa31eeb9b6b9cb" }, { "algorithm": "SHA256", - "checksumValue": "47dd5a7d21b5302255f9fff28884f65d3056fc3f54471ed62ec85fa1904f8aa5" + "checksumValue": "6d1a350ec272c83761f1789611d8f60947a4d69fed3d23d8eee38709a0ad2c0a" } ], "fileName": "Modules/_hacl/include/krml/lowstar_endianness.h" @@ -720,11 +720,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "aaa656e25a92ba83655e1398a97efa6981f60fc4" + "checksumValue": "c9517c43046de129f02da8b74e454bade4872c00" }, { "algorithm": "SHA256", - "checksumValue": "a59abc6e9b3019cb18976a15e634f5146bd965fc9babf4ccbf2b531164a34f85" + "checksumValue": "96c2a973bc01b1295f7af67c7de3839facfeee36c8848d7d88c62695de98ab21" } ], "fileName": "Modules/_hacl/internal/Hacl_HMAC.h" @@ -734,11 +734,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "0741cb8497309d648428be1e7b5944b1fc167187" + "checksumValue": "1c5eeb5d1866acb6bc8b4e5a01a107e3a87f5694" }, { "algorithm": "SHA256", - "checksumValue": "f9b923a566d62de047c753637143d439ca1c25221c08352ddc1738ff4a6ac721" + "checksumValue": "f262738390f56e8e9692acadecd1434c688075047d788283e1fb45d920ea6956" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_Blake2b.h" @@ -748,11 +748,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "878ae284c93824b80b1763e8b3e6be3c410777a8" + "checksumValue": "b600c1b5eafc34b29a778186737d8a51e2342d85" }, { "algorithm": "SHA256", - "checksumValue": "49df6223f6403daf503a1af1a3d2f943d30b5889fe7ed20299c3df24c1e3853d" + "checksumValue": "f22e088ad9c2eb739aefc3685ef3ab1ccaab3c5ef2e5d06cc846ad9f8e3d2669" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h" @@ -762,11 +762,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "25552d8cbf8aa345907635b38f284eec9075301e" + "checksumValue": "eb618ecc6ca2830066448f5f6d4df84a5c09f0f4" }, { "algorithm": "SHA256", - "checksumValue": "a3424cf4c5518654908086bbbf5d465715ec3b23625ef0cadc29492d1f90366c" + "checksumValue": "a4728e43deb0a9d8213b8ddcbda68a63bc73a12fb99aad54b7d28b314776a0d4" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_Blake2s.h" @@ -776,11 +776,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "54a712fc3ed5a817288351cbac5b7d9afa7e379f" + "checksumValue": "5441b6a97cc053c332b29477238d70fa011c74ac" }, { "algorithm": "SHA256", - "checksumValue": "c6abae648b8a1e9d5631c0a959620cad1f7e92ce522e07c3416199fe51debef6" + "checksumValue": "dad568d256a2ccbbbcdd419fe0543ea7137d8065a713a5f009aa52521c0f7f6a" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h" @@ -790,11 +790,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "c15c5f83bbb9f62611c49f0f8f723eaab1a27488" + "checksumValue": "7081b58f28568f600d4624dd5bd6f735191e068b" }, { "algorithm": "SHA256", - "checksumValue": "95cd5d91c4a9217901d0b3395dcd8881e62e2055d723b532ec5176386a636d22" + "checksumValue": "305af1422213ed97e8e5d3d8a9dfee4f21cb2fcd2acf65ee7303ce00b2b4bd2a" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_MD5.h" @@ -804,11 +804,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "7b8717e3a24e7e16a34b251d0d02da6f68439695" + "checksumValue": "b6fb6219cb40d039e789666e34b43461e3b5c82a" }, { "algorithm": "SHA256", - "checksumValue": "9473d8bc9506fe0053d7d98c225d4873011329863f1c4a8e93e43fc71bd1f314" + "checksumValue": "63c58363ff95e8146d5628dab2da25bc2fd0e8b590fd4823b512c33f843355bb" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA1.h" @@ -818,11 +818,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "e319c949f5a2dd765be2c8c7ff77bfe52ee6c7da" + "checksumValue": "bcc71e702df1070cb0081cb983aec7683e036719" }, { "algorithm": "SHA256", - "checksumValue": "75261448e51c3eb1ba441e973b193e23570b167f67743942ee2ee57417491c9f" + "checksumValue": "709c6272f77e2368f6cc0bf36527e3b16dd5d5f3dc26a2afdef8238200af8770" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA2.h" @@ -832,11 +832,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "dbd92415c31606804102b79d5ba3d1752fe03887" + "checksumValue": "e3b5e6add5357760554b032b818992ce9bc211e0" }, { "algorithm": "SHA256", - "checksumValue": "5d74a76a0ac3659a1ae1276c3ca55521f09e83d2f0039f5c519a76f8f3c76a8e" + "checksumValue": "cf49d536a5663379fb4517018b4b3f8a232f8d4c7ddc7edfd60163d13f98a530" } ], "fileName": "Modules/_hacl/internal/Hacl_Hash_SHA3.h" @@ -846,11 +846,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "ad788265f8e1b078c4d1cb6e90b8c031590e6baf" + "checksumValue": "09a0ea364fb073f5f622a763f1351fbf4bac4627" }, { "algorithm": "SHA256", - "checksumValue": "d8354a9b75e2470085fa7e538493130e81fa23a804a6a69d34da8fdcc941c038" + "checksumValue": "916d54d7217517f0360edea920dc21585fbe6d1c2458eac86826182e12c82ec2" } ], "fileName": "Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h" @@ -860,11 +860,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "2048f3cd61dbda2df862a2982ebaf24b6815ed51" + "checksumValue": "b082645b43f8841db5e9e57c0b9a3825fa7e52f5" }, { "algorithm": "SHA256", - "checksumValue": "b0f5a79c98525b0cb1659238e095641328b7da16a94cb57a0793e635d1da3653" + "checksumValue": "059cf0d31427abf84c626797a97c140630a1a6ead578005162f46fad46663389" } ], "fileName": "Modules/_hacl/internal/Hacl_Streaming_HMAC.h" @@ -874,11 +874,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "4e6b098e89fd447bd03f47b55208208456b20966" + "checksumValue": "1f85ed69e395829b3ac5af9e2d049af7708cb9cb" }, { "algorithm": "SHA256", - "checksumValue": "d54d947968ca125978d61fea844711b990f0a18ab0fbca87e41029004d9d04b6" + "checksumValue": "76997c7069a347ac78b65d26354a0324d54add4ca7a02e7be36d6d5e1c9702e0" } ], "fileName": "Modules/_hacl/internal/Hacl_Streaming_Types.h" @@ -916,11 +916,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "63e47cc290c4ec887dca708000876ac37ee75ba0" + "checksumValue": "8d6a23f4e932cace0c67896bdda46cb24bf5e0ea" }, { "algorithm": "SHA256", - "checksumValue": "d09a6196d65c2645974100eb922002bd387d3ae13f2653780f82ed97a79af635" + "checksumValue": "25decbcadf1fef51f473daa4975a3754480fc903b03068bf95f149fb5349456e" } ], "fileName": "Modules/_hacl/libintvector.h" @@ -944,11 +944,11 @@ "checksums": [ { "algorithm": "SHA1", - "checksumValue": "0fbc026a9771d9675e7094790b5b945334d3cb53" + "checksumValue": "0d83ed429ede0a2c6617cd2f24490be16d8393f4" }, { "algorithm": "SHA256", - "checksumValue": "1e77c01eec8f167ed10b754f153c0c743c8e5196ae9c81dffc08f129ab56dbfd" + "checksumValue": "876f4486d08d3eac3581a8681d7fb3cec2fe5b823d1bef27cca15e755510365c" } ], "fileName": "Lib/ctypes/macholib/__init__.py" @@ -994,734 +994,6 @@ } ], "fileName": "Lib/ctypes/macholib/framework.py" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-README.txt", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "bda6e0bd6121f7069b420bdc0bc7c49414d948d1" - }, - { - "algorithm": "SHA256", - "checksumValue": "89926cd0fe6cfb33a2b5b7416c101e9b5d42b0d639d348e0871acf6ffc8258a3" - } - ], - "fileName": "Modules/_decimal/libmpdec/README.txt" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "33757ce2ec0c93c1b5e03c45a495563a00e498ae" - }, - { - "algorithm": "SHA256", - "checksumValue": "ad498362c31a5b99ab19fce320ac540cf14c5c4ec09478f0ad3858da1428113d" - } - ], - "fileName": "Modules/_decimal/libmpdec/basearith.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "bf03919412c068e6969e7ac48850f91bfcd3b2b1" - }, - { - "algorithm": "SHA256", - "checksumValue": "2eaac88a71b9bcf3144396c12dcfeced573e0e550a0050d75b9ed3903248596d" - } - ], - "fileName": "Modules/_decimal/libmpdec/basearith.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-bench.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "c925b7f26754ae182aaa461d51802e8b6a2bb5e9" - }, - { - "algorithm": "SHA256", - "checksumValue": "007e38542ec8d9d8805fe243b5390d79211b9360e2797a20079e833e68ad9e45" - } - ], - "fileName": "Modules/_decimal/libmpdec/bench.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-bench-full.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "cb22686269685a53a17afdea9ed984714e399d9d" - }, - { - "algorithm": "SHA256", - "checksumValue": "1b9e892d4b268deea835ec8906f20a1e5d25e037b2e698edcd34315613f3608c" - } - ], - "fileName": "Modules/_decimal/libmpdec/bench_full.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-bits.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "fc91c2450cdf1e785d1347411662294c3945eb27" - }, - { - "algorithm": "SHA256", - "checksumValue": "ce7741e58ea761a24250c0bfa10058cec8c4fd220dca70a41de3927a2e4f5376" - } - ], - "fileName": "Modules/_decimal/libmpdec/bits.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "7187c18916b0a546ec19b4fc4bec43d0d9fb5fc2" - }, - { - "algorithm": "SHA256", - "checksumValue": "cd430b8657cf8a616916e02f9bd5ca044d5fc19e69333f5d427e1fdb90b0864b" - } - ], - "fileName": "Modules/_decimal/libmpdec/constants.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "af9cbd016fb0ef0b30ced49c0aa4ce2ca3c20125" - }, - { - "algorithm": "SHA256", - "checksumValue": "19dc46df04abb7ee08e9a403f87c8aac8d4a077efcce314c597f8b73e22884f2" - } - ], - "fileName": "Modules/_decimal/libmpdec/constants.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-context.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "666162870230bebd3f2383020d908806fd03909e" - }, - { - "algorithm": "SHA256", - "checksumValue": "9a265d366f31894aad78bca7fcdc1457bc4a3aa3887ca231b7d78e41f79541c0" - } - ], - "fileName": "Modules/_decimal/libmpdec/context.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "0545547a8b37b922fbe2574fbad8fc3bf16f1d33" - }, - { - "algorithm": "SHA256", - "checksumValue": "66fe27b9bb37039cad5be32b105ed509e5aefa15c1957a9058af8ee23cddc97a" - } - ], - "fileName": "Modules/_decimal/libmpdec/convolute.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "05ff0936c5bb08f40d460f5843004a1cc0751d9b" - }, - { - "algorithm": "SHA256", - "checksumValue": "c00d17450c2b8e1d7f1eb8a084f7e6a68f257a453f8701600e860bf357c531d7" - } - ], - "fileName": "Modules/_decimal/libmpdec/convolute.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "fe8176849bc99a306332ba25caa4e91bfa3c6f7d" - }, - { - "algorithm": "SHA256", - "checksumValue": "1f4e65c44864c3e911a6e91f33adec76765293e90553459e3ebce35a58898dba" - } - ], - "fileName": "Modules/_decimal/libmpdec/crt.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "1930b9e0910014b3479aec4e940f02118d9e4a08" - }, - { - "algorithm": "SHA256", - "checksumValue": "7d31f1d0dd73b62964dab0f7a1724473bf87f1f95d8febf0b40c15430ae9a47c" - } - ], - "fileName": "Modules/_decimal/libmpdec/crt.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "415c51e7d7f517b6366bec2a809610d0d38ada14" - }, - { - "algorithm": "SHA256", - "checksumValue": "0a9fef8a374f55277e9f6000b7277bb037b9763c32b156c29950422b057498bd" - } - ], - "fileName": "Modules/_decimal/libmpdec/difradix2.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "d8a998c3bee4c3d9059ba7bf9ae6a8b64649c2ba" - }, - { - "algorithm": "SHA256", - "checksumValue": "5c6766496224de657400995b58b64db3e7084004bf00daebdd7e08d0c5995243" - } - ], - "fileName": "Modules/_decimal/libmpdec/difradix2.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-README.txt", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "158f6ad18edf348efa4fdd7cf61114c77c1d22e9" - }, - { - "algorithm": "SHA256", - "checksumValue": "7b0da2758097a2688f06b3c7ca46b2ebc8329addbd28bb4f5fe95626cc81f8a9" - } - ], - "fileName": "Modules/_decimal/libmpdec/examples/README.txt" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-compare.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "ef80ba26847287fb351ab0df0a78b5f08ba0b5b7" - }, - { - "algorithm": "SHA256", - "checksumValue": "452666ee4eb10a8cf0a926cb3bcf5e95b5c361fa129dbdfe27b654e6d640417e" - } - ], - "fileName": "Modules/_decimal/libmpdec/examples/compare.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-div.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "6ca3a369b3d1e140fdc93c4fdbedb724f7daf969" - }, - { - "algorithm": "SHA256", - "checksumValue": "6d369f5a24d0bb1e7cb6a4f8b0e97a273260e7668c8a540a8fcc92e039f7af2e" - } - ], - "fileName": "Modules/_decimal/libmpdec/examples/div.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-divmod.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "3872a28b4f77e07e1760256067ea338a8dd183f8" - }, - { - "algorithm": "SHA256", - "checksumValue": "5db54bae75ac3d7fa12f1bb0f7ce1bf797df86a81030e8c3ce44d3b1f9b958b7" - } - ], - "fileName": "Modules/_decimal/libmpdec/examples/divmod.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-multiply.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "25dbc94fd4ee5dec21061d2d40dd5d0f88849cb1" - }, - { - "algorithm": "SHA256", - "checksumValue": "22ed39b18fa740a27aacfd29a7bb40066be24500ba49b9b1f24e2af1e039fcd9" - } - ], - "fileName": "Modules/_decimal/libmpdec/examples/multiply.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-pow.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "13d3b7657dc2dc5000fea428f57963d520792ef7" - }, - { - "algorithm": "SHA256", - "checksumValue": "cd8c037649b3d4d6897c9acd2f92f3f9d5390433061d5e48623a5d526a3f4f9c" - } - ], - "fileName": "Modules/_decimal/libmpdec/examples/pow.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-powmod.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "1f7e6c3d3e38df52bbcec0f5a180a8f328679618" - }, - { - "algorithm": "SHA256", - "checksumValue": "e29614b43abf1856b656a84d6b67c22cc5dc7af8cbae8ddc7acf17022220ee12" - } - ], - "fileName": "Modules/_decimal/libmpdec/examples/powmod.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-shift.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "0bd9ce89c7987d1109eb7b0c8f1f9a1298e1422e" - }, - { - "algorithm": "SHA256", - "checksumValue": "203f2dbf11d115580cb3c7c524ac6ccca2a7b31d89545db1b6263381b5de2b6a" - } - ], - "fileName": "Modules/_decimal/libmpdec/examples/shift.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-sqrt.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "b401ba0814e17c9164c0df26e01cc0a355382f46" - }, - { - "algorithm": "SHA256", - "checksumValue": "f3dc2ce321833bbd4b3d1d9ea6fa2e0bcc1bfe1e39abb8d55be53e46c33949db" - } - ], - "fileName": "Modules/_decimal/libmpdec/examples/sqrt.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "060615ddef089a5a8f879a57e4968d920972a0e2" - }, - { - "algorithm": "SHA256", - "checksumValue": "a9f923524d53a9445769f27405375ec3d95fa804bb11db5ee249ae047f11cfce" - } - ], - "fileName": "Modules/_decimal/libmpdec/fnt.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "b205043ebeaf065b16505a299342a992654f19b0" - }, - { - "algorithm": "SHA256", - "checksumValue": "3b03e69adf78fde68c8f87d33595d557237581d33fc067e1039eed9e9f2cc44c" - } - ], - "fileName": "Modules/_decimal/libmpdec/fnt.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "702c27599b43280c94906235d7e1a74193ba701b" - }, - { - "algorithm": "SHA256", - "checksumValue": "cf2e69b946ec14b087e523c0ff606553070d13c23e851fb0ba1df51a728017e6" - } - ], - "fileName": "Modules/_decimal/libmpdec/fourstep.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "ee5291c265ef1f5ae373bc243a4d96975eb3e7b5" - }, - { - "algorithm": "SHA256", - "checksumValue": "dbaced03b52d0f880c377b86c943bcb36f24d557c99a5e9732df3ad5debb5917" - } - ], - "fileName": "Modules/_decimal/libmpdec/fourstep.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-io.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "5d6fdd98730584f74f7b731da6e488fe234504b3" - }, - { - "algorithm": "SHA256", - "checksumValue": "d74f365463166891f62e1326d22b2d39d865776b7ea5e0df2aea5eede4d85b0f" - } - ], - "fileName": "Modules/_decimal/libmpdec/io.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-io.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "28c653cd40b1ce46575e41f5dbfda5f6dd0db4d1" - }, - { - "algorithm": "SHA256", - "checksumValue": "259eab89fe27914e0e39e61199094a357ac60d86b2aab613c909040ff64a4a0c" - } - ], - "fileName": "Modules/_decimal/libmpdec/io.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-REFERENCES.txt", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "218d1d7bedb335cd2c31eae89a15873c3139e13f" - }, - { - "algorithm": "SHA256", - "checksumValue": "a57e8bed93ded481ef264166aec2c49d1a7f3252f29a873ee41fff053cfd9c20" - } - ], - "fileName": "Modules/_decimal/libmpdec/literature/REFERENCES.txt" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-bignum.txt", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "f67eab2431336cf6eeafb30cdafd7e54c251def3" - }, - { - "algorithm": "SHA256", - "checksumValue": "dc34aa122c208ce79e3fc6baee8628094ffaf6a662862dd5647836241f6ebd79" - } - ], - "fileName": "Modules/_decimal/libmpdec/literature/bignum.txt" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-fnt.py", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "a58cfbcd8ea57d66ddfd11fb5a170138c8bbfb3a" - }, - { - "algorithm": "SHA256", - "checksumValue": "122de20eebf87274af2d02072251a94500e7df2d5ef29e81aeabeda991c079e3" - } - ], - "fileName": "Modules/_decimal/libmpdec/literature/fnt.py" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-matrix-transform.txt", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "9a947f6b660150cbd457c4458da2956a36c5824d" - }, - { - "algorithm": "SHA256", - "checksumValue": "592659e7192e3a939b797f5bc7455455834a285f5d8b643ccd780b5114914f73" - } - ], - "fileName": "Modules/_decimal/libmpdec/literature/matrix-transform.txt" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-64.txt", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "69fe9afb8353b5a2b57917469c51c64ac518169d" - }, - { - "algorithm": "SHA256", - "checksumValue": "229a80ca940c594a32e3345412370cbc097043fe59c66a6153cbcf01e7837266" - } - ], - "fileName": "Modules/_decimal/libmpdec/literature/mulmod-64.txt" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-ppro.txt", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "720d468a1f51098036c7a0c869810fff22ed9b79" - }, - { - "algorithm": "SHA256", - "checksumValue": "f3549fc73f697a087267c7b042e30a409e191cbba69a2c0902685e507fbae9f7" - } - ], - "fileName": "Modules/_decimal/libmpdec/literature/mulmod-ppro.txt" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-six-step.txt", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "6815ec3a39baebebe7b3f51d45d10c180a659f17" - }, - { - "algorithm": "SHA256", - "checksumValue": "bf15f73910a173c98fca9db56122b6cc71983668fa8b934c46ca21a57398ec54" - } - ], - "fileName": "Modules/_decimal/libmpdec/literature/six-step.txt" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-umodarith.lisp", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "c91ac4438e661ce78f86e981257546e5adff39ae" - }, - { - "algorithm": "SHA256", - "checksumValue": "783a1b4b9b7143677b0c3d30ffaf28aa0cb01956409031fa38ed8011970bdee0" - } - ], - "fileName": "Modules/_decimal/libmpdec/literature/umodarith.lisp" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "7e8dfb4b7a801b48c501969b001153203b14679e" - }, - { - "algorithm": "SHA256", - "checksumValue": "5ba2f4c80302e71fb216aa247c858e0bf6c8cfabffe7980ac17d4d023c0fef2b" - } - ], - "fileName": "Modules/_decimal/libmpdec/mpalloc.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "bccb6a6ae76fd7f6c8a9102a78958bcad7862950" - }, - { - "algorithm": "SHA256", - "checksumValue": "f7412521de38afb837fcabc2b1d48b971b86bfaa55f3f40d58ff9e46e92debd3" - } - ], - "fileName": "Modules/_decimal/libmpdec/mpalloc.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "f4539afb1ace58c52d18ffd0cc7704f53ca55182" - }, - { - "algorithm": "SHA256", - "checksumValue": "4f89b8095e408a18deff79cfb605299e615bae747898eb105d8936064f7fb626" - } - ], - "fileName": "Modules/_decimal/libmpdec/mpdecimal.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "4b80e25ac49b7e1ea0d1e84967ee32a3d111fc4c" - }, - { - "algorithm": "SHA256", - "checksumValue": "ea0b9c6b296c13aed6ecaa50b463e39a9c1bdc059b84f50507fd8247b2e660f9" - } - ], - "fileName": "Modules/_decimal/libmpdec/mpdecimal.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-mpsignal.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "5c7305a6db0fddf64c6d97e29d3b0c402e3d5d6e" - }, - { - "algorithm": "SHA256", - "checksumValue": "653171cf2549719478417db7e9800fa0f9d99c02dec6da6876329ccf2c07b93f" - } - ], - "fileName": "Modules/_decimal/libmpdec/mpsignal.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "d736b874c43777ca021dde5289ea718893f39219" - }, - { - "algorithm": "SHA256", - "checksumValue": "bdbf2e246f341a3ba3f6f9d8759e7cb222eb9b15f9ed1e7c9f6a59cbb9f8bc91" - } - ], - "fileName": "Modules/_decimal/libmpdec/numbertheory.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "d341508d8c6dd4c4cbd8b99afc8029945f9bbe0d" - }, - { - "algorithm": "SHA256", - "checksumValue": "2f7d5b40af508fa6ac86f5d62101fa3bf683c63b24aa87c9548e3fdd13abc57b" - } - ], - "fileName": "Modules/_decimal/libmpdec/numbertheory.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "cbd05d68bb3940d0d7d0818b14cc03b090a4dd74" - }, - { - "algorithm": "SHA256", - "checksumValue": "7602aaf98ec9525bc4b3cab9631615e1be2efd9af894002ef4e3f5ec63924fcf" - } - ], - "fileName": "Modules/_decimal/libmpdec/sixstep.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "4c059463ec4b4522562dab24760fc64c172c9eee" - }, - { - "algorithm": "SHA256", - "checksumValue": "a191366348b3d3dd49b9090ec5c77dbd77bb3a523c01ff32adafa137e5097ce7" - } - ], - "fileName": "Modules/_decimal/libmpdec/sixstep.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.c", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "cc5593ac9fdb60480cc23fc9d6f27d85670bd35f" - }, - { - "algorithm": "SHA256", - "checksumValue": "2d12fcae512143a9376c8a0d4c1ba3008e420e024497a7e7ec64c6bec23fcddc" - } - ], - "fileName": "Modules/_decimal/libmpdec/transpose.c" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "2f616425756b6cbdf7d189744870b98b613455bd" - }, - { - "algorithm": "SHA256", - "checksumValue": "fafeb2b901b2b41bf0df00be7d99b84df1a78e3cc1e582e09cbfc3b6d44d4abe" - } - ], - "fileName": "Modules/_decimal/libmpdec/transpose.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-typearith.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "b1e9341e173cc8e219ad4aa45fad36d92cce10d3" - }, - { - "algorithm": "SHA256", - "checksumValue": "25e0a0703b51744277834e6b2398d7b7d2c17f92bf30f8b6f949e0486ae2b346" - } - ], - "fileName": "Modules/_decimal/libmpdec/typearith.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-umodarith.h", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "46f6483fce136cd3cc2f7516ee119a487d86333e" - }, - { - "algorithm": "SHA256", - "checksumValue": "bfe1ddb2ca92906456b80745adcbe02c83cadac3ef69caa21bc09b7292cc152b" - } - ], - "fileName": "Modules/_decimal/libmpdec/umodarith.h" - }, - { - "SPDXID": "SPDXRef-FILE-Modules-decimal-libmpdec-vcdiv64.asm", - "checksums": [ - { - "algorithm": "SHA1", - "checksumValue": "d0cc1052fcba08b773d935b0ae2dc6b80d0f2f68" - }, - { - "algorithm": "SHA256", - "checksumValue": "aacc3e47ea8f41e8840c6c67f64ec96d54696a16889903098fa1aab56949a00f" - } - ], - "fileName": "Modules/_decimal/libmpdec/vcdiv64.asm" } ], "packages": [ @@ -1730,14 +1002,14 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "17aa6cfc5c4c219c09287abfc10bc13f0c06f30bb654b28bfe6f567ca646eb79" + "checksumValue": "a52eb72108be160e190b5cafa5bba8663f1313f2013e26060d1c18e26e31067b" } ], - "downloadLocation": "https://github.com/libexpat/libexpat/releases/download/R_2_6_3/expat-2.6.3.tar.gz", + "downloadLocation": "https://github.com/libexpat/libexpat/releases/download/R_2_8_1/expat-2.8.1.tar.gz", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:libexpat_project:libexpat:2.6.3:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:libexpat_project:libexpat:2.8.1:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], @@ -1745,21 +1017,21 @@ "name": "expat", "originator": "Organization: Expat development team", "primaryPackagePurpose": "SOURCE", - "versionInfo": "2.6.3" + "versionInfo": "2.8.1" }, { "SPDXID": "SPDXRef-PACKAGE-hacl-star", "checksums": [ { "algorithm": "SHA256", - "checksumValue": "39f6fd4f2fe98aecc995a2fd980fae0e0835cef6e563ebbf25f69d3d3102bafd" + "checksumValue": "d6db56a5d061dcc0890eabdbb5f58a9fa6c606d9f2fbbe9d626925b870ffadfb" } ], - "downloadLocation": "https://github.com/hacl-star/hacl-star/archive/4ef25b547b377dcef855db4289c6a00580e7221c.zip", + "downloadLocation": "https://github.com/hacl-star/hacl-star/archive/504c2987452f87fe44bce9b9f12e19d6e051761f.zip", "externalRefs": [ { "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:hacl-star:hacl-star:4ef25b547b377dcef855db4289c6a00580e7221c:*:*:*:*:*:*:*", + "referenceLocator": "cpe:2.3:a:hacl-star:hacl-star:504c2987452f87fe44bce9b9f12e19d6e051761f:*:*:*:*:*:*:*", "referenceType": "cpe23Type" } ], @@ -1767,7 +1039,7 @@ "name": "hacl-star", "originator": "Organization: HACL* Developers", "primaryPackagePurpose": "SOURCE", - "versionInfo": "4ef25b547b377dcef855db4289c6a00580e7221c" + "versionInfo": "504c2987452f87fe44bce9b9f12e19d6e051761f" }, { "SPDXID": "SPDXRef-PACKAGE-macholib", @@ -1790,28 +1062,6 @@ "originator": "Person: Ronald Oussoren (ronaldoussoren@mac.com)", "primaryPackagePurpose": "SOURCE", "versionInfo": "1.0" - }, - { - "SPDXID": "SPDXRef-PACKAGE-mpdecimal", - "checksums": [ - { - "algorithm": "SHA256", - "checksumValue": "9f9cd4c041f99b5c49ffb7b59d9f12d95b683d88585608aa56a6307667b2b21f" - } - ], - "downloadLocation": "https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-2.5.1.tar.gz", - "externalRefs": [ - { - "referenceCategory": "SECURITY", - "referenceLocator": "cpe:2.3:a:bytereef:mpdecimal:2.5.1:*:*:*:*:*:*:*", - "referenceType": "cpe23Type" - } - ], - "licenseConcluded": "NOASSERTION", - "name": "mpdecimal", - "originator": "Organization: bytereef.org", - "primaryPackagePurpose": "SOURCE", - "versionInfo": "2.5.1" } ], "relationships": [ @@ -2169,266 +1419,6 @@ "relatedSpdxElement": "SPDXRef-FILE-Lib-ctypes-macholib-framework.py", "relationshipType": "CONTAINS", "spdxElementId": "SPDXRef-PACKAGE-macholib" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-README.txt", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-basearith.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-bench.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-bench-full.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-bits.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-constants.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-context.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-convolute.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-crt.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-difradix2.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-README.txt", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-compare.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-div.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-divmod.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-multiply.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-pow.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-powmod.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-shift.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-examples-sqrt.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fnt.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-fourstep.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-io.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-io.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-REFERENCES.txt", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-bignum.txt", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-fnt.py", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-matrix-transform.txt", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-64.txt", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-mulmod-ppro.txt", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-six-step.txt", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-literature-umodarith.lisp", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpalloc.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpdecimal.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-mpsignal.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-numbertheory.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-sixstep.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.c", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-transpose.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-typearith.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-umodarith.h", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" - }, - { - "relatedSpdxElement": "SPDXRef-FILE-Modules-decimal-libmpdec-vcdiv64.asm", - "relationshipType": "CONTAINS", - "spdxElementId": "SPDXRef-PACKAGE-mpdecimal" } ], "spdxVersion": "SPDX-2.3" diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 1f323cc03973e5..d59a7c788fa9e0 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1,4 +1,4 @@ -# This file lists the contents of the Limited API and Stable ABI. +# This file lists the contents of Limited API and Stable ABI. # Please append new items at the end. # The syntax of this file is not fixed. @@ -41,18 +41,29 @@ # - 'full-abi': All of the struct is part of the ABI, including the size # (users may define arrays of these structs). # Typically used for initialization, rather than at runtime. +# Any members whose names start with an underscore are not part of the +# limited API; they're for CPython's use only. # - 'opaque': No members are part of the ABI, nor is the size. The Limited # API only handles these via pointers. The C definition should be # incomplete (opaque). -# - 'members': Only specific members are part of the stable ABI. -# The struct's size may change, so it can't be used in arrays. +# - 'abi3t-opaque': 'full-abi' in abi3; 'opaque' in abi3t. +# For docs, the generated annotation refers to details that need to +# be added to the ReST file manually. +# - 'members': +# - 'opaque' in abi3t. +# - In abi3, only specific members are part of the stable ABI. +# The struct's size may change, so it can't be used in arrays. # Do not add new structs of this kind without an extremely good reason. +# For docs, the generated annotation refers to details that need to +# be added to the ReST file manually. # - members: For `struct` with struct_abi_kind = 'members', a list of the # exposed members. # - doc: for `feature_macro`, the blurb added in documentation # - windows: for `feature_macro`, this macro is defined on Windows. # (This info is used to generate the DLL manifest and needs to be available # on all platforms.) +# - abi3t_opaque: In abi3t, this struct is opaque (as if `struct_abi_kind` +# was 'opaque' and `members` was missing). # Removing items from this file is generally not allowed, and additions should # be considered with that in mind. See the devguide for exact rules: @@ -105,10 +116,10 @@ struct_abi_kind = 'full-abi' [struct.PyModuleDef_Base] added = '3.2' - struct_abi_kind = 'full-abi' + struct_abi_kind = 'abi3t-opaque' [struct.PyModuleDef] added = '3.2' - struct_abi_kind = 'full-abi' + struct_abi_kind = 'abi3t-opaque' [struct.PyStructSequence_Field] added = '3.2' struct_abi_kind = 'full-abi' @@ -285,10 +296,12 @@ [const.Py_sq_inplace_repeat] added = '3.2' [const.Py_mp_length] + # Note: value changed in 3.15 (old value is still accepted) added = '3.2' [const.Py_mp_subscript] added = '3.2' [const.Py_mp_ass_subscript] + # Note: value changed in 3.15 (old value is still accepted) added = '3.2' [typedef.Py_uintptr_t] @@ -1335,6 +1348,7 @@ abi_only = true [function.PySys_ResetWarnOptions] added = '3.2' + abi_only = true [function.PySys_SetArgv] added = '3.2' [function.PySys_SetArgvEx] @@ -1599,6 +1613,7 @@ added = '3.2' [function.PyWeakref_GetObject] added = '3.2' + abi_only = true [function.PyWeakref_NewProxy] added = '3.2' [function.PyWeakref_NewRef] @@ -1798,7 +1813,6 @@ [const.METH_COEXIST] added = '3.2' # METH_STACKLESS is undocumented -# METH_FASTCALL is not part of limited API. # The following are defined in private headers, but historically # they were exported as part of the stable ABI. @@ -1900,6 +1914,15 @@ added = '3.5' [data.PyModuleDef_Type] added = '3.5' +[const.Py_mod_create] + # Note: value changed in 3.15 (old value is still accepted) + added = '3.5' +[const.Py_mod_exec] + # Note: value changed in 3.15 (old value is still accepted) + added = '3.5' +[struct.PyModuleDef_Slot] + added = '3.5' + struct_abi_kind = 'full-abi' # New slots in 3.5: # d51374ed78a3e3145911a16cdf3b9b84b3ba7d15 - Matrix multiplication (PEP 465) @@ -2125,8 +2148,6 @@ # New method flags in 3.7 (PEP 590): -[const.METH_FASTCALL] - added = '3.7' [const.METH_METHOD] added = '3.7' @@ -2276,6 +2297,10 @@ [data.PyStructSequence_UnnamedField] added = '3.11' +# Added in 3.7 but in the Stable ABI from 3.10 +[const.METH_FASTCALL] + added = '3.10' + # Add stable Py_buffer API in Python 3.11 (https://bugs.python.org/issue45459) [struct.Py_buffer] added = '3.11' @@ -2304,6 +2329,12 @@ added = '3.11' [function.PyMemoryView_FromBuffer] added = '3.11' +[const.Py_bf_getbuffer] + # Note: value changed in 3.15 (old value is still accepted) + added = '3.11' +[const.Py_bf_releasebuffer] + # Note: value changed in 3.15 (old value is still accepted) + added = '3.11' # Constants for Py_buffer API added to this list in Python 3.11.1 (https://github.com/python/cpython/issues/98680) # (they were available with 3.11.0) @@ -2438,6 +2469,10 @@ added = '3.12' [const.Py_TPFLAGS_ITEMS_AT_END] added = '3.12' +[const.Py_mod_multiple_interpreters] + # Note: value changed in 3.15 (old value is still accepted) + added = '3.12' + [function.PyImport_AddModuleRef] added = '3.13' [function.PyWeakref_GetRef] @@ -2516,10 +2551,15 @@ added = '3.13' [function.PyEval_GetFrameLocals] added = '3.13' +[const.Py_mod_gil] + # Note: value changed in 3.15 (old value is still accepted) + added = '3.13' [function.Py_TYPE] + # Before 3.14, this was a macro that accessed the PyObject member added = '3.14' [function.Py_REFCNT] + # Before 3.14, this was a macro that accessed the PyObject member added = '3.14' [function.PyIter_NextItem] added = '3.14' @@ -2575,6 +2615,7 @@ added = '3.14' [function.Py_PACK_VERSION] added = '3.14' + [function.PySys_GetAttr] added = '3.15' [function.PySys_GetAttrString] @@ -2583,3 +2624,211 @@ added = '3.15' [function.PySys_GetOptionalAttrString] added = '3.15' +[function.PyABIInfo_Check] + added = '3.15' +[macro.PyABIInfo_VAR] + added = '3.15' +[struct.PyABIInfo] + added = '3.15' + struct_abi_kind = 'full-abi' +[const.Py_mod_abi] + added = '3.15' +[const.PyABIInfo_DEFAULT_ABI_VERSION] + added = '3.15' +[const.PyABIInfo_DEFAULT_FLAGS] + added = '3.15' +[const.PyABIInfo_STABLE] + added = '3.15' +[const.PyABIInfo_FREETHREADED] + added = '3.15' +[const.PyABIInfo_GIL] + added = '3.15' +[const.PyABIInfo_FREETHREADING_AGNOSTIC] + added = '3.15' +[function.PyModule_FromSlotsAndSpec] + added = '3.15' +[function.PyModule_Exec] + added = '3.15' +[function.PyModule_GetToken] + added = '3.15' +[function.PyType_GetModuleByToken] + added = '3.15' +[function.PyModule_GetStateSize] + added = '3.15' +[macro.PyMODEXPORT_FUNC] + added = '3.15' +[const.Py_mod_name] + added = '3.15' +[const.Py_mod_doc] + added = '3.15' +[const.Py_mod_state_size] + added = '3.15' +[const.Py_mod_methods] + added = '3.15' +[const.Py_mod_state_traverse] + added = '3.15' +[const.Py_mod_state_clear] + added = '3.15' +[const.Py_mod_state_free] + added = '3.15' +[const.Py_mod_token] + added = '3.15' +[function.PyDict_SetDefaultRef] + added = '3.15' +[function.Py_SIZE] + # Before 3.15, this was a macro that accessed the PyObject member + added = '3.15' +[function.Py_IS_TYPE] + # Before 3.15, this was a macro that accessed the PyObject member + added = '3.15' +[function.Py_SET_SIZE] + # Before 3.15, this was a macro that accessed the PyObject member + added = '3.15' +[function.PyObject_GetTypeData_DuringGC] + added = '3.15' +[function.PyType_GetModuleState_DuringGC] + added = '3.15' +[function.PyModule_GetState_DuringGC] + added = '3.15' +[function.PyModule_GetToken_DuringGC] + added = '3.15' +[function.PyType_GetBaseByToken_DuringGC] + added = '3.15' +[function.PyType_GetModule_DuringGC] + added = '3.15' +[function.PyType_GetModuleByToken_DuringGC] + added = '3.15' +[function.PyCriticalSection_Begin] + added = '3.15' +[function.PyCriticalSection_End] + added = '3.15' +[struct.PyCriticalSection] + added = '3.15' + struct_abi_kind = 'full-abi' +[macro.Py_BEGIN_CRITICAL_SECTION] + added = '3.15' +[macro.Py_END_CRITICAL_SECTION] + added = '3.15' +[function.PyCriticalSection2_Begin] + added = '3.15' +[function.PyCriticalSection2_End] + added = '3.15' +[struct.PyCriticalSection2] + added = '3.15' + struct_abi_kind = 'full-abi' +[macro.Py_BEGIN_CRITICAL_SECTION2] + added = '3.15' +[macro.Py_END_CRITICAL_SECTION2] + added = '3.15' +[struct.PySlot] + added = '3.15' + struct_abi_kind = 'full-abi' +[function.PyType_FromSlots] + added = '3.15' +[const.PySlot_OPTIONAL] + added = '3.15' +[const.PySlot_STATIC] + added = '3.15' +[const.PySlot_INTPTR] + added = '3.15' +[macro.PySlot_DATA] + added = '3.15' +[macro.PySlot_FUNC] + added = '3.15' +[macro.PySlot_SIZE] + added = '3.15' +[macro.PySlot_INT64] + added = '3.15' +[macro.PySlot_UINT64] + added = '3.15' +[macro.PySlot_STATIC_DATA] + added = '3.15' +[macro.PySlot_END] + added = '3.15' +[macro.PySlot_PTR] + added = '3.15' +[macro.PySlot_PTR_STATIC] + added = '3.15' +[const.Py_slot_end] + added = '3.15' +[const.Py_slot_invalid] + added = '3.15' +[const.Py_slot_subslots] + added = '3.15' +[const.Py_tp_slots] + added = '3.15' +[const.Py_mod_slots] + added = '3.15' +[const.Py_tp_name] + added = '3.15' +[const.Py_tp_basicsize] + added = '3.15' +[const.Py_tp_extra_basicsize] + added = '3.15' +[const.Py_tp_itemsize] + added = '3.15' +[const.Py_tp_flags] + added = '3.15' +[const.Py_tp_metaclass] + added = '3.15' +[const.Py_tp_module] + added = '3.15' + +# PEP 757 import/export API. + +[function.PyLong_GetNativeLayout] + added = '3.15' +[function.PyLong_Export] + added = '3.15' +[function.PyLong_FreeExport] + added = '3.15' +[function.PyLongWriter_Create] + added = '3.15' +[function.PyLongWriter_Finish] + added = '3.15' +[function.PyLongWriter_Discard] + added = '3.15' +[struct.PyLongWriter] + added = '3.15' + struct_abi_kind = 'opaque' +[struct.PyLongLayout] + added = '3.15' + struct_abi_kind = 'full-abi' +[struct.PyLongExport] + added = '3.15' + # Note: The `_reserved` member of this struct is for interal use only. + # (The definition of 'full-abi' was clarified when this entry was added.) + struct_abi_kind = 'full-abi' + +# PEP 788 finalization protection + +[struct.PyInterpreterGuard] + added = '3.15' + struct_abi_kind = 'opaque' +[function.PyInterpreterGuard_FromCurrent] + added = '3.15' +[function.PyInterpreterGuard_FromView] + added = '3.15' +[function.PyInterpreterGuard_Close] + added = '3.15' +[struct.PyInterpreterView] + added = '3.15' + struct_abi_kind = 'opaque' +[function.PyInterpreterView_FromCurrent] + added = '3.15' +[function.PyInterpreterView_FromMain] + added = '3.15' +[function.PyInterpreterView_Close] + added = '3.15' +[function.PyThreadState_Ensure] + added = '3.15' +[function.PyThreadState_EnsureFromView] + added = '3.15' +[function.PyThreadState_Release] + added = '3.15' +[struct.PyThreadStateToken] + added = '3.15' + struct_abi_kind = 'opaque' + +[function.PyObject_CallFinalizerFromDealloc] + added = '3.15' diff --git a/Misc/vgrindefs b/Misc/vgrindefs deleted file mode 100644 index 3e6d8a4629a455..00000000000000 --- a/Misc/vgrindefs +++ /dev/null @@ -1,10 +0,0 @@ -# vgrind is a pretty-printer that takes source code and outputs -# eye-pleasing postscript. The entry below should be added to your -# local vgrindefs file. Contributed by Neale Pickett <neale@lanl.gov>. - -python|Python|py:\ - :pb=^\d?(def|class)\d\p(\d|\\|\(|\:):\ - :cb=#:ce=$:sb=":se=\e":lb=':le=\e':\ - :kw=assert and break class continue def del elif else except\ - finally for from global if import in is lambda not or\ - pass print raise return try while yield: diff --git a/Modules/Setup b/Modules/Setup index a066982df1ae86..e97a78e628693d 100644 --- a/Modules/Setup +++ b/Modules/Setup @@ -156,6 +156,7 @@ PYTHONPATH=$(COREPYTHONPATH) #binascii binascii.c #cmath cmathmodule.c #math mathmodule.c +#_math_integer mathintegermodule.c #mmap mmapmodule.c #select selectmodule.c #_sysconfig _sysconfig.c @@ -272,6 +273,7 @@ PYTHONPATH=$(COREPYTHONPATH) #xx xxmodule.c #xxlimited xxlimited.c #xxlimited_35 xxlimited_35.c +#xxlimited_3_13 xxlimited_3_13.c #xxsubtype xxsubtype.c # Testing @@ -284,7 +286,7 @@ PYTHONPATH=$(COREPYTHONPATH) #*shared* #_ctypes_test _ctypes/_ctypes_test.c -#_remote_debugging _remote_debugging_module.c +#_remote_debugging _remote_debugging/module.c _remote_debugging/object_reading.c _remote_debugging/code_objects.c _remote_debugging/frames.c _remote_debugging/threads.c _remote_debugging/asyncio.c _remote_debugging/interpreters.c #_testcapi _testcapimodule.c #_testimportmultiple _testimportmultiple.c #_testmultiphase _testmultiphase.c diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 7f4c4a806737ac..8efea27824f0e8 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -37,10 +37,11 @@ @MODULE__HEAPQ_TRUE@_heapq _heapqmodule.c @MODULE__JSON_TRUE@_json _json.c @MODULE__LSPROF_TRUE@_lsprof _lsprof.c rotatingtree.c +@MODULE__MATH_INTEGER_TRUE@_math_integer mathintegermodule.c @MODULE__PICKLE_TRUE@_pickle _pickle.c @MODULE__QUEUE_TRUE@_queue _queuemodule.c @MODULE__RANDOM_TRUE@_random _randommodule.c -@MODULE__REMOTE_DEBUGGING_TRUE@_remote_debugging _remote_debugging_module.c +@MODULE__REMOTE_DEBUGGING_TRUE@_remote_debugging _remote_debugging/module.c _remote_debugging/gc_stats.c _remote_debugging/object_reading.c _remote_debugging/code_objects.c _remote_debugging/frames.c _remote_debugging/frame_cache.c _remote_debugging/threads.c _remote_debugging/asyncio.c _remote_debugging/binary_io_writer.c _remote_debugging/binary_io_reader.c _remote_debugging/subprocess.c _remote_debugging/interpreters.c @MODULE__STRUCT_TRUE@_struct _struct.c # build supports subinterpreters @@ -55,9 +56,7 @@ @MODULE_CMATH_TRUE@cmath cmathmodule.c @MODULE__STATISTICS_TRUE@_statistics _statisticsmodule.c -# _decimal uses libmpdec -# either static libmpdec.a from Modules/_decimal/libmpdec or libmpdec.so -# with ./configure --with-system-libmpdec +# _decimal uses the system's libmpdec.so @MODULE__DECIMAL_TRUE@_decimal _decimal/_decimal.c # compression libs and binascii (optional CRC32 from zlib) @@ -173,9 +172,9 @@ @MODULE_XXSUBTYPE_TRUE@xxsubtype xxsubtype.c @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c -@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c _testinternalcapi/complex.c -@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c -@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c +@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c _testinternalcapi/complex.c _testinternalcapi/interpreter.c _testinternalcapi/tuple.c +@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/modsupport.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c _testcapi/module.c _testcapi/weakref.c +@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c _testlimitedcapi/weakref.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c @@ -189,6 +188,7 @@ # Limited API template modules; must be built as shared modules. @MODULE_XXLIMITED_TRUE@xxlimited xxlimited.c @MODULE_XXLIMITED_35_TRUE@xxlimited_35 xxlimited_35.c +@MODULE_XXLIMITED_3_13_TRUE@xxlimited_3_13 xxlimited_3_13.c # for performance diff --git a/Modules/_abc.c b/Modules/_abc.c index d6a953b336025d..5826efbfecb690 100644 --- a/Modules/_abc.c +++ b/Modules/_abc.c @@ -484,6 +484,7 @@ compute_abstract_methods(PyObject *self) #define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING) /*[clinic input] +@permit_long_summary _abc._abc_init self: object @@ -494,7 +495,7 @@ Internal ABC helper for class set-up. Should be never used outside abc module. static PyObject * _abc__abc_init(PyObject *module, PyObject *self) -/*[clinic end generated code: output=594757375714cda1 input=8d7fe470ff77f029]*/ +/*[clinic end generated code: output=594757375714cda1 input=0b3513f947736d39]*/ { _abcmodule_state *state = get_abc_state(module); PyObject *data; @@ -543,6 +544,7 @@ _abc__abc_init(PyObject *module, PyObject *self) } /*[clinic input] +@permit_long_summary _abc._abc_register self: object @@ -554,7 +556,7 @@ Internal ABC helper for subclasss registration. Should be never used outside abc static PyObject * _abc__abc_register_impl(PyObject *module, PyObject *self, PyObject *subclass) -/*[clinic end generated code: output=7851e7668c963524 input=ca589f8c3080e67f]*/ +/*[clinic end generated code: output=7851e7668c963524 input=135ab13a581b4414]*/ { if (!PyType_Check(subclass)) { PyErr_SetString(PyExc_TypeError, "Can only register classes"); @@ -606,6 +608,7 @@ _abc__abc_register_impl(PyObject *module, PyObject *self, PyObject *subclass) /*[clinic input] +@permit_long_summary _abc._abc_instancecheck self: object @@ -618,7 +621,7 @@ Internal ABC helper for instance checks. Should be never used outside abc module static PyObject * _abc__abc_instancecheck_impl(PyObject *module, PyObject *self, PyObject *instance) -/*[clinic end generated code: output=b8b5148f63b6b56f input=a4f4525679261084]*/ +/*[clinic end generated code: output=b8b5148f63b6b56f input=0bbc8da0ea346719]*/ { PyObject *subtype, *result = NULL, *subclass = NULL; _abc_data *impl = _get_impl(module, self); @@ -692,6 +695,7 @@ static int subclasscheck_check_registry(_abc_data *impl, PyObject *subclass, PyObject **result); /*[clinic input] +@permit_long_summary _abc._abc_subclasscheck self: object @@ -704,7 +708,7 @@ Internal ABC helper for subclasss checks. Should be never used outside abc modul static PyObject * _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self, PyObject *subclass) -/*[clinic end generated code: output=b56c9e4a530e3894 input=1d947243409d10b8]*/ +/*[clinic end generated code: output=b56c9e4a530e3894 input=5bf1ef712f5d3610]*/ { if (!PyType_Check(subclass)) { PyErr_SetString(PyExc_TypeError, "issubclass() arg 1 must be a class"); @@ -911,14 +915,14 @@ _abc.get_cache_token Returns the current ABC cache token. -The token is an opaque object (supporting equality testing) identifying the -current version of the ABC cache for virtual subclasses. The token changes -with every call to register() on any ABC. +The token is an opaque object (supporting equality testing) identifying +the current version of the ABC cache for virtual subclasses. The token +changes with every call to register() on any ABC. [clinic start generated code]*/ static PyObject * _abc_get_cache_token_impl(PyObject *module) -/*[clinic end generated code: output=c7d87841e033dacc input=70413d1c423ad9f9]*/ +/*[clinic end generated code: output=c7d87841e033dacc input=d87acc04492f6bf3]*/ { _abcmodule_state *state = get_abc_state(module); return PyLong_FromUnsignedLongLong(get_invalidation_counter(state)); @@ -972,6 +976,7 @@ _abcmodule_free(void *module) } static PyModuleDef_Slot _abcmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _abcmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 99408e60721c60..6620ee26449b16 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -13,6 +13,7 @@ #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_runtime_init.h" // _Py_ID() +#include "pycore_tuple.h" // _PyTuple_FromPair #include <stddef.h> // offsetof() @@ -119,7 +120,7 @@ typedef struct _Py_AsyncioModuleDebugOffsets { } asyncio_thread_state; } Py_AsyncioModuleDebugOffsets; -GENERATE_DEBUG_SECTION(AsyncioDebug, Py_AsyncioModuleDebugOffsets _AsyncioDebug) +GENERATE_DEBUG_SECTION(AsyncioDebug, Py_AsyncioModuleDebugOffsets _Py_AsyncioDebug) = {.asyncio_task_object = { .size = sizeof(TaskObj), .task_name = offsetof(TaskObj, task_name), @@ -498,21 +499,13 @@ future_schedule_callbacks(asyncio_state *state, FutureObj *fut) static int future_init(FutureObj *fut, PyObject *loop) { + if (fut->fut_loop != NULL) { + PyErr_Format(PyExc_RuntimeError, "%T object is already initialized", fut); + return -1; + } + PyObject *res; int is_true; - - Py_CLEAR(fut->fut_loop); - Py_CLEAR(fut->fut_callback0); - Py_CLEAR(fut->fut_context0); - Py_CLEAR(fut->fut_callbacks); - Py_CLEAR(fut->fut_result); - Py_CLEAR(fut->fut_exception); - Py_CLEAR(fut->fut_exception_tb); - Py_CLEAR(fut->fut_source_tb); - Py_CLEAR(fut->fut_cancel_msg); - Py_CLEAR(fut->fut_cancelled_exc); - Py_CLEAR(fut->fut_awaited_by); - fut->fut_state = STATE_PENDING; fut->fut_log_tb = 0; fut->fut_blocking = 0; @@ -837,14 +830,10 @@ future_add_done_callback(asyncio_state *state, FutureObj *fut, PyObject *arg, fut->fut_context0 = Py_NewRef(ctx); } else { - PyObject *tup = PyTuple_New(2); + PyObject *tup = _PyTuple_FromPair(arg, (PyObject *)ctx); if (tup == NULL) { return NULL; } - Py_INCREF(arg); - PyTuple_SET_ITEM(tup, 0, arg); - Py_INCREF(ctx); - PyTuple_SET_ITEM(tup, 1, (PyObject *)ctx); if (fut->fut_callbacks != NULL) { int err = PyList_Append(fut->fut_callbacks, tup); @@ -955,8 +944,7 @@ FutureObj_traverse(PyObject *op, visitproc visit, void *arg) Py_VISIT(fut->fut_cancel_msg); Py_VISIT(fut->fut_cancelled_exc); Py_VISIT(fut->fut_awaited_by); - PyObject_VisitManagedDict((PyObject *)fut, visit, arg); - return 0; + return PyObject_VisitManagedDict((PyObject *)fut, visit, arg); } /*[clinic input] @@ -967,12 +955,13 @@ Return the result this future represents. If the future has been cancelled, raises CancelledError. If the future's result isn't yet available, raises InvalidStateError. If -the future is done and has an exception set, this exception is raised. +the future is done and has an exception set, this exception is +raised. [clinic start generated code]*/ static PyObject * _asyncio_Future_result_impl(FutureObj *self) -/*[clinic end generated code: output=f35f940936a4b1e5 input=61d89f48e4c8b670]*/ +/*[clinic end generated code: output=f35f940936a4b1e5 input=ee20e126776cbb04]*/ { asyncio_state *state = get_asyncio_state_by_def((PyObject *)self); PyObject *result; @@ -1107,15 +1096,15 @@ _asyncio.Future.add_done_callback Add a callback to be run when the future becomes done. -The callback is called with a single argument - the future object. If -the future is already done when this is called, the callback is +The callback is called with a single argument - the future object. +If the future is already done when this is called, the callback is scheduled with call_soon. [clinic start generated code]*/ static PyObject * _asyncio_Future_add_done_callback_impl(FutureObj *self, PyTypeObject *cls, PyObject *fn, PyObject *context) -/*[clinic end generated code: output=922e9a4cbd601167 input=37d97f941beb7b3e]*/ +/*[clinic end generated code: output=922e9a4cbd601167 input=f4f6adb074cd3e0f]*/ { asyncio_state *state = get_asyncio_state_by_cls(cls); if (context == NULL) { @@ -1264,15 +1253,15 @@ _asyncio.Future.cancel Cancel the future and schedule callbacks. -If the future is already done or cancelled, return False. Otherwise, -change the future's state to cancelled, schedule the callbacks and -return True. +If the future is already done or cancelled, return False. +Otherwise, change the future's state to cancelled, schedule the +callbacks and return True. [clinic start generated code]*/ static PyObject * _asyncio_Future_cancel_impl(FutureObj *self, PyTypeObject *cls, PyObject *msg) -/*[clinic end generated code: output=074956f35904b034 input=44ab4003da839970]*/ +/*[clinic end generated code: output=074956f35904b034 input=0c9157547a964c4c]*/ { asyncio_state *state = get_asyncio_state_by_cls(cls); ENSURE_FUTURE_ALIVE(state, self) @@ -1304,13 +1293,13 @@ _asyncio.Future.done Return True if the future is done. -Done means either that a result / exception are available, or that the -future was cancelled. +Done means either that a result / exception are available, or that +the future was cancelled. [clinic start generated code]*/ static PyObject * _asyncio_Future_done_impl(FutureObj *self) -/*[clinic end generated code: output=244c5ac351145096 input=7204d3cc63bef7f3]*/ +/*[clinic end generated code: output=244c5ac351145096 input=acf2c2347f3c01d8]*/ { if (!future_is_alive(self) || self->fut_state == STATE_PENDING) { Py_RETURN_FALSE; @@ -1511,14 +1500,12 @@ _asyncio_Future__callbacks_get_impl(FutureObj *self) Py_ssize_t i = 0; if (self->fut_callback0 != NULL) { - PyObject *tup0 = PyTuple_New(2); + assert(self->fut_context0 != NULL); + PyObject *tup0 = _PyTuple_FromPair(self->fut_callback0, self->fut_context0); if (tup0 == NULL) { Py_DECREF(callbacks); return NULL; } - PyTuple_SET_ITEM(tup0, 0, Py_NewRef(self->fut_callback0)); - assert(self->fut_context0 != NULL); - PyTuple_SET_ITEM(tup0, 1, Py_NewRef(self->fut_context0)); PyList_SET_ITEM(callbacks, i, tup0); i++; } @@ -1755,7 +1742,8 @@ static PyMethodDef FutureType_methods[] = { _ASYNCIO_FUTURE_DONE_METHODDEF _ASYNCIO_FUTURE_GET_LOOP_METHODDEF _ASYNCIO_FUTURE__MAKE_CANCELLED_ERROR_METHODDEF - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("Futures are generic over the type of their results")}, {NULL, NULL} /* Sentinel */ }; @@ -2076,8 +2064,8 @@ class _asyncio.Task "TaskObj *" "&Task_Type" static int task_call_step_soon(asyncio_state *state, TaskObj *, PyObject *); static PyObject *task_wakeup(PyObject *op, PyObject *arg); -static PyObject * task_step(asyncio_state *, TaskObj *, PyObject *); -static int task_eager_start(asyncio_state *state, TaskObj *task); +static PyObject *task_step(asyncio_state *, TaskObj *, PyObject *); +static int task_eager_start(_PyThreadStateImpl *ts, asyncio_state *state, TaskObj *task); /* ----- Task._step wrapper */ @@ -2195,15 +2183,14 @@ static PyMethodDef TaskWakeupDef = { /* ----- Task introspection helpers */ static void -register_task(TaskObj *task) +register_task(_PyThreadStateImpl *ts, TaskObj *task) { if (task->task_node.next != NULL) { // already registered assert(task->task_node.prev != NULL); return; } - _PyThreadStateImpl *tstate = (_PyThreadStateImpl *) _PyThreadState_GET(); - struct llist_node *head = &tstate->asyncio_tasks_head; + struct llist_node *head = &ts->asyncio_tasks_head; llist_insert_tail(head, &task->task_node); } @@ -2241,10 +2228,8 @@ unregister_task(TaskObj *task) } static int -enter_task(PyObject *loop, PyObject *task) +enter_task(_PyThreadStateImpl *ts, PyObject *loop, PyObject *task) { - _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); - if (ts->asyncio_running_loop != loop) { PyErr_Format(PyExc_RuntimeError, "loop %R is not the running loop", loop); return -1; @@ -2255,7 +2240,7 @@ enter_task(PyObject *loop, PyObject *task) PyExc_RuntimeError, "Cannot enter into task %R while another " \ "task %R is being executed.", - task, ts->asyncio_running_task, NULL); + task, ts->asyncio_running_task); return -1; } @@ -2264,10 +2249,8 @@ enter_task(PyObject *loop, PyObject *task) } static int -leave_task(PyObject *loop, PyObject *task) +leave_task(_PyThreadStateImpl *ts, PyObject *loop, PyObject *task) { - _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); - if (ts->asyncio_running_loop != loop) { PyErr_Format(PyExc_RuntimeError, "loop %R is not the running loop", loop); return -1; @@ -2278,7 +2261,7 @@ leave_task(PyObject *loop, PyObject *task) PyExc_RuntimeError, "Invalid attempt to leave task %R while " \ "task %R is entered.", - task, ts->asyncio_running_task ? ts->asyncio_running_task : Py_None, NULL); + task, ts->asyncio_running_task ? ts->asyncio_running_task : Py_None); return -1; } Py_CLEAR(ts->asyncio_running_task); @@ -2286,10 +2269,8 @@ leave_task(PyObject *loop, PyObject *task) } static PyObject * -swap_current_task(PyObject *loop, PyObject *task) +swap_current_task(_PyThreadStateImpl *ts, PyObject *loop, PyObject *task) { - _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); - if (ts->asyncio_running_loop != loop) { PyErr_Format(PyExc_RuntimeError, "loop %R is not the running loop", loop); return NULL; @@ -2343,7 +2324,7 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, self->task_log_destroy_pending = 0; PyErr_Format(PyExc_TypeError, "a coroutine was expected, got %R", - coro, NULL); + coro); return -1; } @@ -2384,7 +2365,7 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, if (self->task_name == NULL) { return -1; } - + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); if (eager_start) { PyObject *res = PyObject_CallMethodNoArgs(loop, &_Py_ID(is_running)); if (res == NULL) { @@ -2393,7 +2374,7 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, int is_loop_running = Py_IsTrue(res); Py_DECREF(res); if (is_loop_running) { - if (task_eager_start(state, self)) { + if (task_eager_start(ts, state, self)) { return -1; } return 0; @@ -2408,7 +2389,7 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, // works correctly in non-owning threads. _PyObject_SetMaybeWeakref((PyObject *)self); #endif - register_task(self); + register_task(ts, self); return 0; } @@ -2445,8 +2426,7 @@ TaskObj_traverse(PyObject *op, visitproc visit, void *arg) Py_VISIT(fut->fut_cancel_msg); Py_VISIT(fut->fut_cancelled_exc); Py_VISIT(fut->fut_awaited_by); - PyObject_VisitManagedDict((PyObject *)fut, visit, arg); - return 0; + return PyObject_VisitManagedDict((PyObject *)fut, visit, arg); } /*[clinic input] @@ -2948,7 +2928,8 @@ static PyMethodDef TaskType_methods[] = { _ASYNCIO_TASK_SET_NAME_METHODDEF _ASYNCIO_TASK_GET_CORO_METHODDEF _ASYNCIO_TASK_GET_CONTEXT_METHODDEF - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("Tasks are generic over the return type of their wrapped coroutines")}, {NULL, NULL} /* Sentinel */ }; @@ -2990,16 +2971,12 @@ static PyType_Spec Task_spec = { static void TaskObj_dealloc(PyObject *self) { - _PyObject_ResurrectStart(self); - // Unregister the task here so that even if any subclass of Task - // which doesn't end up calling TaskObj_finalize not crashes. - unregister_task((TaskObj *)self); - - PyObject_CallFinalizer(self); - - if (_PyObject_ResurrectEnd(self)) { - return; + if (PyObject_CallFinalizerFromDealloc(self) < 0) { + return; // resurrected } + // unregister the task after finalization so that + // if the task gets resurrected, it remains registered + unregister_task((TaskObj *)self); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); @@ -3019,11 +2996,7 @@ task_call_step_soon(asyncio_state *state, TaskObj *task, PyObject *arg) return -1; } - // Beware: An evil call_soon could alter task_context. - // See: https://github.com/python/cpython/issues/126080. - PyObject *task_context = Py_NewRef(task->task_context); - int ret = call_soon(state, task->task_loop, cb, NULL, task_context); - Py_DECREF(task_context); + int ret = call_soon(state, task->task_loop, cb, NULL, task->task_context); Py_DECREF(cb); return ret; } @@ -3456,7 +3429,9 @@ task_step(asyncio_state *state, TaskObj *task, PyObject *exc) { PyObject *res; - if (enter_task(task->task_loop, (PyObject*)task) < 0) { + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); + + if (enter_task(ts, task->task_loop, (PyObject*)task) < 0) { return NULL; } @@ -3464,12 +3439,12 @@ task_step(asyncio_state *state, TaskObj *task, PyObject *exc) if (res == NULL) { PyObject *exc = PyErr_GetRaisedException(); - leave_task(task->task_loop, (PyObject*)task); + leave_task(ts, task->task_loop, (PyObject*)task); _PyErr_ChainExceptions1(exc); return NULL; } else { - if (leave_task(task->task_loop, (PyObject*)task) < 0) { + if (leave_task(ts, task->task_loop, (PyObject*)task) < 0) { Py_DECREF(res); return NULL; } @@ -3480,10 +3455,10 @@ task_step(asyncio_state *state, TaskObj *task, PyObject *exc) } static int -task_eager_start(asyncio_state *state, TaskObj *task) +task_eager_start(_PyThreadStateImpl *ts, asyncio_state *state, TaskObj *task) { assert(task != NULL); - PyObject *prevtask = swap_current_task(task->task_loop, (PyObject *)task); + PyObject *prevtask = swap_current_task(ts, task->task_loop, (PyObject *)task); if (prevtask == NULL) { return -1; } @@ -3491,9 +3466,9 @@ task_eager_start(asyncio_state *state, TaskObj *task) // if the task completes eagerly (without suspending) then it will unregister itself // in future_schedule_callbacks when done, otherwise // it will continue as a regular (non-eager) asyncio task - register_task(task); + register_task(ts, task); - if (PyContext_Enter(task->task_context) == -1) { + if (_PyContext_Enter(&ts->base, task->task_context) == -1) { Py_DECREF(prevtask); return -1; } @@ -3512,7 +3487,7 @@ task_eager_start(asyncio_state *state, TaskObj *task) Py_DECREF(stepres); } - PyObject *curtask = swap_current_task(task->task_loop, prevtask); + PyObject *curtask = swap_current_task(ts, task->task_loop, prevtask); Py_DECREF(prevtask); if (curtask == NULL) { retval = -1; @@ -3521,7 +3496,7 @@ task_eager_start(asyncio_state *state, TaskObj *task) Py_DECREF(curtask); } - if (PyContext_Exit(task->task_context) == -1) { + if (_PyContext_Exit(&ts->base, task->task_context) == -1) { retval = -1; } @@ -3716,7 +3691,8 @@ _asyncio__register_task_impl(PyObject *module, PyObject *task) if (Task_Check(state, task)) { // task is an asyncio.Task instance or subclass, use efficient // linked-list implementation. - register_task((TaskObj *)task); + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); + register_task(ts, (TaskObj *)task); Py_RETURN_NONE; } // As task does not inherit from asyncio.Task, fallback to less efficient @@ -3749,7 +3725,8 @@ _asyncio__register_eager_task_impl(PyObject *module, PyObject *task) if (Task_Check(state, task)) { // task is an asyncio.Task instance or subclass, use efficient // linked-list implementation. - register_task((TaskObj *)task); + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); + register_task(ts, (TaskObj *)task); Py_RETURN_NONE; } @@ -3836,7 +3813,8 @@ static PyObject * _asyncio__enter_task_impl(PyObject *module, PyObject *loop, PyObject *task) /*[clinic end generated code: output=a22611c858035b73 input=de1b06dca70d8737]*/ { - if (enter_task(loop, task) < 0) { + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); + if (enter_task(ts, loop, task) < 0) { return NULL; } Py_RETURN_NONE; @@ -3860,7 +3838,8 @@ static PyObject * _asyncio__leave_task_impl(PyObject *module, PyObject *loop, PyObject *task) /*[clinic end generated code: output=0ebf6db4b858fb41 input=51296a46313d1ad8]*/ { - if (leave_task(loop, task) < 0) { + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); + if (leave_task(ts, loop, task) < 0) { return NULL; } Py_RETURN_NONE; @@ -3868,6 +3847,7 @@ _asyncio__leave_task_impl(PyObject *module, PyObject *loop, PyObject *task) /*[clinic input] +@permit_long_summary _asyncio._swap_current_task loop: object @@ -3882,9 +3862,10 @@ This is intended for use during eager coroutine execution. static PyObject * _asyncio__swap_current_task_impl(PyObject *module, PyObject *loop, PyObject *task) -/*[clinic end generated code: output=9f88de958df74c7e input=c9c72208d3d38b6c]*/ +/*[clinic end generated code: output=9f88de958df74c7e input=ec14ed25855e3068]*/ { - return swap_current_task(loop, task); + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); + return swap_current_task(ts, loop, task); } @@ -4079,30 +4060,44 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop) return NULL; } - PyInterpreterState *interp = PyInterpreterState_Get(); - // Stop the world and traverse the per-thread linked list - // of asyncio tasks for every thread, as well as the - // interpreter's linked list, and add them to `tasks`. - // The interpreter linked list is used for any lingering tasks - // whose thread state has been deallocated while the task was - // still alive. This can happen if a task is referenced by - // a different thread, in which case the task is moved to - // the interpreter's linked list from the thread's linked - // list before deallocation. See PyThreadState_Clear. - // - // The stop-the-world pause is required so that no thread - // modifies its linked list while being iterated here - // in parallel. This design allows for lock-free - // register_task/unregister_task for loops running in parallel - // in different threads (the general case). - _PyEval_StopTheWorld(interp); - int ret = add_tasks_interp(interp, (PyListObject *)tasks); - _PyEval_StartTheWorld(interp); - if (ret < 0) { - // call any escaping calls after starting the world to avoid any deadlocks. - Py_DECREF(tasks); - Py_DECREF(loop); - return NULL; + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET(); + if (ts->asyncio_running_loop == loop) { + // Fast path for the current running loop of current thread + // no locking or stop the world pause is required + struct llist_node *head = &ts->asyncio_tasks_head; + if (add_tasks_llist(head, (PyListObject *)tasks) < 0) { + Py_DECREF(tasks); + Py_DECREF(loop); + return NULL; + } + } + else { + // Slow path for loop running in different thread + PyInterpreterState *interp = ts->base.interp; + // Stop the world and traverse the per-thread linked list + // of asyncio tasks for every thread, as well as the + // interpreter's linked list, and add them to `tasks`. + // The interpreter linked list is used for any lingering tasks + // whose thread state has been deallocated while the task was + // still alive. This can happen if a task is referenced by + // a different thread, in which case the task is moved to + // the interpreter's linked list from the thread's linked + // list before deallocation. See PyThreadState_Clear. + // + // The stop-the-world pause is required so that no thread + // modifies its linked list while being iterated here + // in parallel. This design allows for lock-free + // register_task/unregister_task for loops running in parallel + // in different threads (the general case). + _PyEval_StopTheWorld(interp); + int ret = add_tasks_interp(interp, (PyListObject *)tasks); + _PyEval_StartTheWorld(interp); + if (ret < 0) { + // call any escaping calls after starting the world to avoid any deadlocks. + Py_DECREF(tasks); + Py_DECREF(loop); + return NULL; + } } // All the tasks are now in the list, now filter the tasks which are done @@ -4324,7 +4319,7 @@ module_init(asyncio_state *state) goto fail; } - state->debug_offsets = &_AsyncioDebug; + state->debug_offsets = &_Py_AsyncioDebug; Py_DECREF(module); return 0; @@ -4396,6 +4391,7 @@ module_exec(PyObject *mod) } static struct PyModuleDef_Slot module_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_bisectmodule.c b/Modules/_bisectmodule.c index 9b146265445d9a..a953f8bfa11aea 100644 --- a/Modules/_bisectmodule.c +++ b/Modules/_bisectmodule.c @@ -57,10 +57,6 @@ internal_bisect_right(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t Py_ssize_t mid; int res; - if (lo < 0) { - PyErr_SetString(PyExc_ValueError, "lo must be non-negative"); - return -1; - } if (hi == -1) { hi = PySequence_Size(list); if (hi < 0) @@ -153,7 +149,7 @@ _bisect.bisect_right -> Py_ssize_t a: object x: object - lo: Py_ssize_t = 0 + lo: Py_ssize_t(allow_negative=False) = 0 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None * key: object = None @@ -161,8 +157,8 @@ _bisect.bisect_right -> Py_ssize_t Return the index where to insert item x in list a, assuming a is sorted. The return value i is such that all e in a[:i] have e <= x, and all e in -a[i:] have e > x. So if x already appears in the list, a.insert(i, x) will -insert just after the rightmost x already there. +a[i:] have e > x. So if x already appears in the list, a.insert(i, x) +will insert just after the rightmost x already there. Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. @@ -173,7 +169,7 @@ A custom key function can be supplied to customize the sort order. static Py_ssize_t _bisect_bisect_right_impl(PyObject *module, PyObject *a, PyObject *x, Py_ssize_t lo, Py_ssize_t hi, PyObject *key) -/*[clinic end generated code: output=3a4bc09cc7c8a73d input=43071869772dd53a]*/ +/*[clinic end generated code: output=3a4bc09cc7c8a73d input=27717afe1a61bfaa]*/ { return internal_bisect_right(a, x, lo, hi, key); } @@ -183,7 +179,7 @@ _bisect.insort_right a: object x: object - lo: Py_ssize_t = 0 + lo: Py_ssize_t(allow_negative=False) = 0 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None * key: object = None @@ -201,7 +197,7 @@ A custom key function can be supplied to customize the sort order. static PyObject * _bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x, Py_ssize_t lo, Py_ssize_t hi, PyObject *key) -/*[clinic end generated code: output=ac3bf26d07aedda2 input=f60777d2b6ddb239]*/ +/*[clinic end generated code: output=ac3bf26d07aedda2 input=f2caa8abec0763e8]*/ { PyObject *result, *key_x; Py_ssize_t index; @@ -241,10 +237,6 @@ internal_bisect_left(PyObject *list, PyObject *item, Py_ssize_t lo, Py_ssize_t h Py_ssize_t mid; int res; - if (lo < 0) { - PyErr_SetString(PyExc_ValueError, "lo must be non-negative"); - return -1; - } if (hi == -1) { hi = PySequence_Size(list); if (hi < 0) @@ -338,7 +330,7 @@ _bisect.bisect_left -> Py_ssize_t a: object x: object - lo: Py_ssize_t = 0 + lo: Py_ssize_t(allow_negative=False) = 0 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None * key: object = None @@ -346,8 +338,8 @@ _bisect.bisect_left -> Py_ssize_t Return the index where to insert item x in list a, assuming a is sorted. The return value i is such that all e in a[:i] have e < x, and all e in -a[i:] have e >= x. So if x already appears in the list, a.insert(i, x) will -insert just before the leftmost x already there. +a[i:] have e >= x. So if x already appears in the list, a.insert(i, x) +will insert just before the leftmost x already there. Optional args lo (default 0) and hi (default len(a)) bound the slice of a to be searched. @@ -358,7 +350,7 @@ A custom key function can be supplied to customize the sort order. static Py_ssize_t _bisect_bisect_left_impl(PyObject *module, PyObject *a, PyObject *x, Py_ssize_t lo, Py_ssize_t hi, PyObject *key) -/*[clinic end generated code: output=70749d6e5cae9284 input=f29c4fe7f9b797c7]*/ +/*[clinic end generated code: output=70749d6e5cae9284 input=259fedbe35e882e1]*/ { return internal_bisect_left(a, x, lo, hi, key); } @@ -369,7 +361,7 @@ _bisect.insort_left a: object x: object - lo: Py_ssize_t = 0 + lo: Py_ssize_t(allow_negative=False) = 0 hi: Py_ssize_t(c_default='-1', accept={int, NoneType}) = None * key: object = None @@ -387,7 +379,7 @@ A custom key function can be supplied to customize the sort order. static PyObject * _bisect_insort_left_impl(PyObject *module, PyObject *a, PyObject *x, Py_ssize_t lo, Py_ssize_t hi, PyObject *key) -/*[clinic end generated code: output=b1d33e5e7ffff11e input=0a700a82edbd472c]*/ +/*[clinic end generated code: output=b1d33e5e7ffff11e input=ff85a79826e22f31]*/ { PyObject *result, *key_x; Py_ssize_t index; @@ -460,6 +452,7 @@ bisect_modexec(PyObject *m) } static PyModuleDef_Slot bisect_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, bisect_modexec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c index 9e85e0de42cd8d..9db3ac39da5209 100644 --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -12,6 +12,7 @@ // Blocks output buffer wrappers #include "pycore_blocks_output_buffer.h" +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_CHAR_RELAXED #if OUTPUT_BUFFER_MAX_BLOCK_SIZE > UINT32_MAX #error "The maximum block size accepted by libbzip2 is UINT32_MAX." @@ -97,25 +98,17 @@ OutputBuffer_OnError(_BlocksOutputBuffer *buffer) #endif /* ! BZ_CONFIG_ERROR */ -#define ACQUIRE_LOCK(obj) do { \ - if (!PyThread_acquire_lock((obj)->lock, 0)) { \ - Py_BEGIN_ALLOW_THREADS \ - PyThread_acquire_lock((obj)->lock, 1); \ - Py_END_ALLOW_THREADS \ - } } while (0) -#define RELEASE_LOCK(obj) PyThread_release_lock((obj)->lock) - - typedef struct { PyObject_HEAD bz_stream bzs; int flushed; - PyThread_type_lock lock; + PyMutex mutex; } BZ2Compressor; typedef struct { PyObject_HEAD bz_stream bzs; + int bzerror; char eof; /* Py_T_BOOL expects a char */ PyObject *unused_data; char needs_input; @@ -126,7 +119,7 @@ typedef struct { separately. Conversion and looping is encapsulated in decompress_buf() */ size_t bzs_avail_in_real; - PyThread_type_lock lock; + PyMutex mutex; } BZ2Decompressor; #define _BZ2Compressor_CAST(op) ((BZ2Compressor *)(op)) @@ -190,7 +183,7 @@ static PyObject * compress(BZ2Compressor *c, char *data, size_t len, int action) { PyObject *result; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; if (OutputBuffer_InitAndGrow(&buffer, -1, &c->bzs.next_out, &c->bzs.avail_out) < 0) { goto error; @@ -271,12 +264,12 @@ _bz2_BZ2Compressor_compress_impl(BZ2Compressor *self, Py_buffer *data) { PyObject *result = NULL; - ACQUIRE_LOCK(self); + PyMutex_Lock(&self->mutex); if (self->flushed) PyErr_SetString(PyExc_ValueError, "Compressor has been flushed"); else result = compress(self, data->buf, data->len, BZ_RUN); - RELEASE_LOCK(self); + PyMutex_Unlock(&self->mutex); return result; } @@ -296,14 +289,14 @@ _bz2_BZ2Compressor_flush_impl(BZ2Compressor *self) { PyObject *result = NULL; - ACQUIRE_LOCK(self); + PyMutex_Lock(&self->mutex); if (self->flushed) PyErr_SetString(PyExc_ValueError, "Repeated call to flush()"); else { self->flushed = 1; result = compress(self, NULL, 0, BZ_FINISH); } - RELEASE_LOCK(self); + PyMutex_Unlock(&self->mutex); return result; } @@ -357,13 +350,7 @@ _bz2_BZ2Compressor_impl(PyTypeObject *type, int compresslevel) return NULL; } - self->lock = PyThread_allocate_lock(); - if (self->lock == NULL) { - Py_DECREF(self); - PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); - return NULL; - } - + self->mutex = (PyMutex){0}; self->bzs.opaque = NULL; self->bzs.bzalloc = BZ2_Malloc; self->bzs.bzfree = BZ2_Free; @@ -382,22 +369,13 @@ static void BZ2Compressor_dealloc(PyObject *op) { BZ2Compressor *self = _BZ2Compressor_CAST(op); + assert(!PyMutex_IsLocked(&self->mutex)); BZ2_bzCompressEnd(&self->bzs); - if (self->lock != NULL) { - PyThread_free_lock(self->lock); - } PyTypeObject *tp = Py_TYPE(self); tp->tp_free((PyObject *)self); Py_DECREF(tp); } -static int -BZ2Compressor_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static PyMethodDef BZ2Compressor_methods[] = { _BZ2_BZ2COMPRESSOR_COMPRESS_METHODDEF _BZ2_BZ2COMPRESSOR_FLUSH_METHODDEF @@ -409,7 +387,6 @@ static PyType_Slot bz2_compressor_type_slots[] = { {Py_tp_methods, BZ2Compressor_methods}, {Py_tp_new, _bz2_BZ2Compressor}, {Py_tp_doc, (char *)_bz2_BZ2Compressor__doc__}, - {Py_tp_traverse, BZ2Compressor_traverse}, {0, 0} }; @@ -437,7 +414,7 @@ decompress_buf(BZ2Decompressor *d, Py_ssize_t max_length) compare against max_length and PyBytes_GET_SIZE we declare it as signed */ PyObject *result; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; bz_stream *bzs = &d->bzs; if (OutputBuffer_InitAndGrow(&buffer, max_length, &bzs->next_out, &bzs->avail_out) < 0) { @@ -459,10 +436,13 @@ decompress_buf(BZ2Decompressor *d, Py_ssize_t max_length) d->bzs_avail_in_real += bzs->avail_in; - if (catch_bz2_error(bzret)) + if (catch_bz2_error(bzret)) { + d->bzerror = bzret; + FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 0); goto error; + } if (bzret == BZ_STREAM_END) { - d->eof = 1; + FT_ATOMIC_STORE_CHAR_RELAXED(d->eof, 1); break; } else if (d->bzs_avail_in_real == 0) { break; @@ -546,20 +526,22 @@ decompress(BZ2Decompressor *d, char *data, size_t len, Py_ssize_t max_length) } if (d->eof) { - d->needs_input = 0; + FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 0); if (d->bzs_avail_in_real > 0) { - Py_XSETREF(d->unused_data, - PyBytes_FromStringAndSize(bzs->next_in, d->bzs_avail_in_real)); - if (d->unused_data == NULL) + PyObject *unused_data = PyBytes_FromStringAndSize( + bzs->next_in, d->bzs_avail_in_real); + if (unused_data == NULL) { goto error; + } + Py_XSETREF(d->unused_data, unused_data); } } else if (d->bzs_avail_in_real == 0) { bzs->next_in = NULL; - d->needs_input = 1; + FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 1); } else { - d->needs_input = 0; + FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 0); /* If we did not use the input buffer, we now have to copy the tail from the caller's buffer into the @@ -593,6 +575,7 @@ decompress(BZ2Decompressor *d, char *data, size_t len, Py_ssize_t max_length) return result; error: + bzs->next_in = NULL; Py_XDECREF(result); return NULL; } @@ -605,33 +588,41 @@ _bz2.BZ2Decompressor.decompress Decompress *data*, returning uncompressed data as bytes. -If *max_length* is nonnegative, returns at most *max_length* bytes of -decompressed data. If this limit is reached and further output can be -produced, *self.needs_input* will be set to ``False``. In this case, the next -call to *decompress()* may provide *data* as b'' to obtain more of the output. +If *max_length* is nonnegative, returns at most *max_length* bytes +of decompressed data. If this limit is reached and further output +can be produced, *self.needs_input* will be set to ``False``. In +this case, the next call to *decompress()* may provide *data* as b'' +to obtain more of the output. -If all of the input data was decompressed and returned (either because this -was less than *max_length* bytes, or because *max_length* was negative), -*self.needs_input* will be set to True. +If all of the input data was decompressed and returned (either +because this was less than *max_length* bytes, or because +*max_length* was negative), *self.needs_input* will be set to True. -Attempting to decompress data after the end of stream is reached raises an -EOFError. Any data found after the end of the stream is ignored and saved in -the unused_data attribute. +Attempting to decompress data after the end of stream is reached +raises an EOFError. Any data found after the end of the stream is +ignored and saved in the unused_data attribute. [clinic start generated code]*/ static PyObject * _bz2_BZ2Decompressor_decompress_impl(BZ2Decompressor *self, Py_buffer *data, Py_ssize_t max_length) -/*[clinic end generated code: output=23e41045deb240a3 input=52e1ffc66a8ea624]*/ +/*[clinic end generated code: output=23e41045deb240a3 input=7f68faa9ff7a1b51]*/ { PyObject *result = NULL; - ACQUIRE_LOCK(self); - if (self->eof) + PyMutex_Lock(&self->mutex); + if (self->eof) { PyErr_SetString(PyExc_EOFError, "End of stream already reached"); - else + } + else if (self->bzerror) { + // Re-entering BZ2_bzDecompress() after an error can write out of bounds. + PyErr_SetString(PyExc_ValueError, + "Decompressor is unusable after a previous error"); + } + else { result = decompress(self, data->buf, data->len, max_length); - RELEASE_LOCK(self); + } + PyMutex_Unlock(&self->mutex); return result; } @@ -657,20 +648,13 @@ _bz2_BZ2Decompressor_impl(PyTypeObject *type) return NULL; } - self->lock = PyThread_allocate_lock(); - if (self->lock == NULL) { - Py_DECREF(self); - PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); - return NULL; - } - + self->mutex = (PyMutex){0}; + self->bzerror = 0; self->needs_input = 1; self->bzs_avail_in_real = 0; self->input_buffer = NULL; self->input_buffer_size = 0; - self->unused_data = PyBytes_FromStringAndSize(NULL, 0); - if (self->unused_data == NULL) - goto error; + self->unused_data = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); bzerror = BZ2_bzDecompressInit(&self->bzs, 0, 0); if (catch_bz2_error(bzerror)) @@ -687,28 +671,19 @@ static void BZ2Decompressor_dealloc(PyObject *op) { BZ2Decompressor *self = _BZ2Decompressor_CAST(op); + assert(!PyMutex_IsLocked(&self->mutex)); if(self->input_buffer != NULL) { PyMem_Free(self->input_buffer); } BZ2_bzDecompressEnd(&self->bzs); Py_CLEAR(self->unused_data); - if (self->lock != NULL) { - PyThread_free_lock(self->lock); - } PyTypeObject *tp = Py_TYPE(self); tp->tp_free((PyObject *)self); Py_DECREF(tp); } -static int -BZ2Decompressor_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static PyMethodDef BZ2Decompressor_methods[] = { _BZ2_BZ2DECOMPRESSOR_DECOMPRESS_METHODDEF {NULL} @@ -723,11 +698,29 @@ PyDoc_STRVAR(BZ2Decompressor_unused_data__doc__, PyDoc_STRVAR(BZ2Decompressor_needs_input_doc, "True if more input is needed before more decompressed data can be produced."); +static PyObject * +BZ2Decompressor_unused_data_get(PyObject *op, void *Py_UNUSED(ignored)) +{ + BZ2Decompressor *self = _BZ2Decompressor_CAST(op); + if (!FT_ATOMIC_LOAD_CHAR_RELAXED(self->eof)) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } + PyMutex_Lock(&self->mutex); + assert(self->unused_data != NULL); + PyObject *result = Py_NewRef(self->unused_data); + PyMutex_Unlock(&self->mutex); + return result; +} + +static PyGetSetDef BZ2Decompressor_getset[] = { + {"unused_data", BZ2Decompressor_unused_data_get, NULL, + BZ2Decompressor_unused_data__doc__}, + {NULL}, +}; + static PyMemberDef BZ2Decompressor_members[] = { {"eof", Py_T_BOOL, offsetof(BZ2Decompressor, eof), Py_READONLY, BZ2Decompressor_eof__doc__}, - {"unused_data", Py_T_OBJECT_EX, offsetof(BZ2Decompressor, unused_data), - Py_READONLY, BZ2Decompressor_unused_data__doc__}, {"needs_input", Py_T_BOOL, offsetof(BZ2Decompressor, needs_input), Py_READONLY, BZ2Decompressor_needs_input_doc}, {NULL} @@ -738,8 +731,8 @@ static PyType_Slot bz2_decompressor_type_slots[] = { {Py_tp_methods, BZ2Decompressor_methods}, {Py_tp_doc, (char *)_bz2_BZ2Decompressor__doc__}, {Py_tp_members, BZ2Decompressor_members}, + {Py_tp_getset, BZ2Decompressor_getset}, {Py_tp_new, _bz2_BZ2Decompressor}, - {Py_tp_traverse, BZ2Decompressor_traverse}, {0, 0} }; @@ -806,6 +799,7 @@ _bz2_free(void *module) } static struct PyModuleDef_Slot _bz2_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _bz2_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_codecsmodule.c b/Modules/_codecsmodule.c index 7cf3f152eeecc6..272182f7bf49ac 100644 --- a/Modules/_codecsmodule.c +++ b/Modules/_codecsmodule.c @@ -55,14 +55,15 @@ _codecs.register Register a codec search function. -Search functions are expected to take one argument, the encoding name in -all lower case letters, and either return None, or a tuple of functions -(encoder, decoder, stream_reader, stream_writer) (or a CodecInfo object). +Search functions are expected to take one argument, the encoding +name in all lower case letters, and either return None, or a tuple +of functions (encoder, decoder, stream_reader, stream_writer) (or +a CodecInfo object). [clinic start generated code]*/ static PyObject * _codecs_register(PyObject *module, PyObject *search_function) -/*[clinic end generated code: output=d1bf21e99db7d6d3 input=369578467955cae4]*/ +/*[clinic end generated code: output=d1bf21e99db7d6d3 input=2321d8c8c0420dfc]*/ { if (PyCodec_Register(search_function)) return NULL; @@ -92,6 +93,7 @@ _codecs_unregister(PyObject *module, PyObject *search_function) } /*[clinic input] +@permit_long_summary _codecs.lookup encoding: str / @@ -101,7 +103,7 @@ Looks up a codec tuple in the Python codec registry and returns a CodecInfo obje static PyObject * _codecs_lookup_impl(PyObject *module, const char *encoding) -/*[clinic end generated code: output=9f0afa572080c36d input=3c572c0db3febe9c]*/ +/*[clinic end generated code: output=9f0afa572080c36d input=02227d5429491ab3]*/ { return _PyCodec_Lookup(encoding); } @@ -115,16 +117,16 @@ _codecs.encode Encodes obj using the codec registered for encoding. The default encoding is 'utf-8'. errors may be given to set a -different error handling scheme. Default is 'strict' meaning that encoding -errors raise a ValueError. Other possible values are 'ignore', 'replace' -and 'backslashreplace' as well as any other name registered with -codecs.register_error that can handle ValueErrors. +different error handling scheme. Default is 'strict' meaning that +encoding errors raise a ValueError. Other possible values are 'ignore', +'replace' and 'backslashreplace' as well as any other name registered +with codecs.register_error that can handle ValueErrors. [clinic start generated code]*/ static PyObject * _codecs_encode_impl(PyObject *module, PyObject *obj, const char *encoding, const char *errors) -/*[clinic end generated code: output=385148eb9a067c86 input=cd5b685040ff61f0]*/ +/*[clinic end generated code: output=385148eb9a067c86 input=e5271d443e391d7f]*/ { if (encoding == NULL) encoding = PyUnicode_GetDefaultEncoding(); @@ -142,16 +144,16 @@ _codecs.decode Decodes obj using the codec registered for encoding. Default encoding is 'utf-8'. errors may be given to set a -different error handling scheme. Default is 'strict' meaning that encoding -errors raise a ValueError. Other possible values are 'ignore', 'replace' -and 'backslashreplace' as well as any other name registered with -codecs.register_error that can handle ValueErrors. +different error handling scheme. Default is 'strict' meaning that +encoding errors raise a ValueError. Other possible values are 'ignore', +'replace' and 'backslashreplace' as well as any other name registered +with codecs.register_error that can handle ValueErrors. [clinic start generated code]*/ static PyObject * _codecs_decode_impl(PyObject *module, PyObject *obj, const char *encoding, const char *errors) -/*[clinic end generated code: output=679882417dc3a0bd input=7702c0cc2fa1add6]*/ +/*[clinic end generated code: output=679882417dc3a0bd input=3e6254628f9ca538]*/ { if (encoding == NULL) encoding = PyUnicode_GetDefaultEncoding(); @@ -201,55 +203,50 @@ _codecs_escape_encode_impl(PyObject *module, PyObject *data, const char *errors) /*[clinic end generated code: output=4af1d477834bab34 input=8f4b144799a94245]*/ { - Py_ssize_t size; - Py_ssize_t newsize; - PyObject *v; - - size = PyBytes_GET_SIZE(data); + Py_ssize_t size = PyBytes_GET_SIZE(data); if (size > PY_SSIZE_T_MAX / 4) { PyErr_SetString(PyExc_OverflowError, "string is too large to encode"); return NULL; } - newsize = 4*size; - v = PyBytes_FromStringAndSize(NULL, newsize); + Py_ssize_t newsize = 4*size; - if (v == NULL) { + PyBytesWriter *writer = PyBytesWriter_Create(newsize); + if (writer == NULL) { return NULL; } - else { - Py_ssize_t i; - char c; - char *p = PyBytes_AS_STRING(v); - - for (i = 0; i < size; i++) { - /* There's at least enough room for a hex escape */ - assert(newsize - (p - PyBytes_AS_STRING(v)) >= 4); - c = PyBytes_AS_STRING(data)[i]; - if (c == '\'' || c == '\\') - *p++ = '\\', *p++ = c; - else if (c == '\t') - *p++ = '\\', *p++ = 't'; - else if (c == '\n') - *p++ = '\\', *p++ = 'n'; - else if (c == '\r') - *p++ = '\\', *p++ = 'r'; - else if (c < ' ' || c >= 0x7f) { - *p++ = '\\'; - *p++ = 'x'; - *p++ = Py_hexdigits[(c & 0xf0) >> 4]; - *p++ = Py_hexdigits[c & 0xf]; - } - else - *p++ = c; + char *p = PyBytesWriter_GetData(writer); + + for (Py_ssize_t i = 0; i < size; i++) { + /* There's at least enough room for a hex escape */ + assert(newsize - (p - (char*)PyBytesWriter_GetData(writer)) >= 4); + + char c = PyBytes_AS_STRING(data)[i]; + if (c == '\'' || c == '\\') { + *p++ = '\\'; *p++ = c; } - *p = '\0'; - if (_PyBytes_Resize(&v, (p - PyBytes_AS_STRING(v)))) { - return NULL; + else if (c == '\t') { + *p++ = '\\'; *p++ = 't'; + } + else if (c == '\n') { + *p++ = '\\'; *p++ = 'n'; + } + else if (c == '\r') { + *p++ = '\\'; *p++ = 'r'; + } + else if (c < ' ' || c >= 0x7f) { + *p++ = '\\'; + *p++ = 'x'; + *p++ = Py_hexdigits[(c & 0xf0) >> 4]; + *p++ = Py_hexdigits[c & 0xf]; + } + else { + *p++ = c; } } - return codec_tuple(v, size); + PyObject *decoded = PyBytesWriter_FinishWithPointer(writer, p); + return codec_tuple(decoded, size); } /* --- Decoder ------------------------------------------------------------ */ @@ -675,7 +672,7 @@ _codecs_utf_7_encode_impl(PyObject *module, PyObject *str, const char *errors) /*[clinic end generated code: output=0feda21ffc921bc8 input=2546dbbb3fa53114]*/ { - return codec_tuple(_PyUnicode_EncodeUTF7(str, 0, 0, errors), + return codec_tuple(_PyUnicode_EncodeUTF7(str, errors), PyUnicode_GET_LENGTH(str)); } @@ -966,14 +963,15 @@ _codecs.register_error Register the specified error handler under the name errors. handler must be a callable object, that will be called with an exception -instance containing information about the location of the encoding/decoding -error and must return a (replacement, new position) tuple. +instance containing information about the location of the +encoding/decoding error and must return a (replacement, new position) +tuple. [clinic start generated code]*/ static PyObject * _codecs_register_error_impl(PyObject *module, const char *errors, PyObject *handler) -/*[clinic end generated code: output=fa2f7d1879b3067d input=5e6709203c2e33fe]*/ +/*[clinic end generated code: output=fa2f7d1879b3067d input=5bea01dfe835d9d8]*/ { if (PyCodec_RegisterError(errors, handler)) return NULL; @@ -1011,17 +1009,58 @@ _codecs.lookup_error lookup_error(errors) -> handler -Return the error handler for the specified error handling name or raise a -LookupError, if no handler exists under this name. +Return the error handler for the specified error handling name or raise +a LookupError, if no handler exists under this name. [clinic start generated code]*/ static PyObject * _codecs_lookup_error_impl(PyObject *module, const char *name) -/*[clinic end generated code: output=087f05dc0c9a98cc input=4775dd65e6235aba]*/ +/*[clinic end generated code: output=087f05dc0c9a98cc input=86cfb6a7a9c67113]*/ { return PyCodec_LookupError(name); } +extern int _Py_normalize_encoding(const char *, char *, size_t, int); + +/*[clinic input] +_codecs._normalize_encoding + encoding: unicode + +Normalize an encoding name *encoding*. + +Used for encodings.normalize_encoding. Does not convert to lower case. +[clinic start generated code]*/ + +static PyObject * +_codecs__normalize_encoding_impl(PyObject *module, PyObject *encoding) +/*[clinic end generated code: output=d27465d81e361f8e input=3ff3f4d64995b988]*/ +{ + Py_ssize_t len; + const char *cstr = PyUnicode_AsUTF8AndSize(encoding, &len); + if (cstr == NULL) { + return NULL; + } + + if (len > PY_SSIZE_T_MAX) { + PyErr_SetString(PyExc_OverflowError, "encoding is too large"); + return NULL; + } + + char *normalized = PyMem_Malloc(len + 1); + if (normalized == NULL) { + return PyErr_NoMemory(); + } + + if (!_Py_normalize_encoding(cstr, normalized, len + 1, 0)) { + PyMem_Free(normalized); + return NULL; + } + + PyObject *result = PyUnicode_FromString(normalized); + PyMem_Free(normalized); + return result; +} + /* --- Module API --------------------------------------------------------- */ static PyMethodDef _codecs_functions[] = { @@ -1071,10 +1110,12 @@ static PyMethodDef _codecs_functions[] = { _CODECS_REGISTER_ERROR_METHODDEF _CODECS__UNREGISTER_ERROR_METHODDEF _CODECS_LOOKUP_ERROR_METHODDEF + _CODECS__NORMALIZE_ENCODING_METHODDEF {NULL, NULL} /* sentinel */ }; static PyModuleDef_Slot _codecs_slots[] = { + _Py_ABI_SLOT, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 3ba48d5d9d3c64..5ca6362406a78b 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -342,8 +342,10 @@ deque_append_lock_held(dequeobject *deque, PyObject *item, Py_ssize_t maxlen) { if (deque->rightindex == BLOCKLEN - 1) { block *b = newblock(deque); - if (b == NULL) + if (b == NULL) { + Py_DECREF(item); return -1; + } b->leftlink = deque->rightblock; CHECK_END(deque->rightblock->rightlink); deque->rightblock->rightlink = b; @@ -389,8 +391,10 @@ deque_appendleft_lock_held(dequeobject *deque, PyObject *item, { if (deque->leftindex == 0) { block *b = newblock(deque); - if (b == NULL) + if (b == NULL) { + Py_DECREF(item); return -1; + } b->rightlink = deque->leftblock; CHECK_END(deque->leftblock->leftlink); deque->leftblock->leftlink = b; @@ -564,7 +568,6 @@ deque_extendleft_impl(dequeobject *deque, PyObject *iterable) iternext = *Py_TYPE(it)->tp_iternext; while ((item = iternext(it)) != NULL) { if (deque_appendleft_lock_held(deque, item, maxlen) == -1) { - Py_DECREF(item); Py_DECREF(it); return NULL; } @@ -605,29 +608,42 @@ deque_copy_impl(dequeobject *deque) collections_state *state = find_module_state_by_def(Py_TYPE(deque)); if (Py_IS_TYPE(deque, state->deque_type)) { dequeobject *new_deque; - PyObject *rv; + Py_ssize_t n = Py_SIZE(deque); new_deque = (dequeobject *)deque_new(state->deque_type, NULL, NULL); if (new_deque == NULL) return NULL; new_deque->maxlen = old_deque->maxlen; - /* Fast path for the deque_repeat() common case where len(deque) == 1 - * - * It's safe to not acquire the per-object lock for new_deque; it's - * invisible to other threads. + + /* Copy elements directly by walking the block structure. + * This is safe because the caller holds the deque lock and + * the new deque is not yet visible to other threads. */ - if (Py_SIZE(deque) == 1) { - PyObject *item = old_deque->leftblock->data[old_deque->leftindex]; - rv = deque_append_impl(new_deque, item); - } else { - rv = deque_extend_impl(new_deque, (PyObject *)deque); - } - if (rv != NULL) { - Py_DECREF(rv); - return (PyObject *)new_deque; + if (n > 0) { + block *b = old_deque->leftblock; + Py_ssize_t index = old_deque->leftindex; + + /* Space saving heuristic. Start filling from the left */ + assert(new_deque->leftblock == new_deque->rightblock); + assert(new_deque->leftindex == new_deque->rightindex + 1); + new_deque->leftindex = 1; + new_deque->rightindex = 0; + + for (Py_ssize_t i = 0; i < n; i++) { + PyObject *item = b->data[index]; + if (deque_append_lock_held(new_deque, Py_NewRef(item), + new_deque->maxlen) < 0) { + Py_DECREF(new_deque); + return NULL; + } + index++; + if (index == BLOCKLEN) { + b = b->rightlink; + index = 0; + } + } } - Py_DECREF(new_deque); - return NULL; + return (PyObject *)new_deque; } if (old_deque->maxlen < 0) result = PyObject_CallOneArg((PyObject *)(Py_TYPE(deque)), @@ -1061,6 +1077,7 @@ _deque_rotate(dequeobject *deque, Py_ssize_t n) } /*[clinic input] +@permit_long_summary @critical_section _collections.deque.rotate as deque_rotate @@ -1073,7 +1090,7 @@ Rotate the deque n steps to the right. If n is negative, rotates left. static PyObject * deque_rotate_impl(dequeobject *deque, Py_ssize_t n) -/*[clinic end generated code: output=96c2402a371eb15d input=5bf834296246e002]*/ +/*[clinic end generated code: output=96c2402a371eb15d input=3543c3b2297de8f1]*/ { if (!_deque_rotate(deque, n)) Py_RETURN_NONE; @@ -1234,7 +1251,7 @@ _collections.deque.index as deque_index deque: dequeobject value as v: object start: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='0') = NULL - stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='Py_SIZE(deque)') = NULL + stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='PY_SSIZE_T_MAX') = NULL / Return first index of value. @@ -1245,7 +1262,7 @@ Raises ValueError if the value is not present. static PyObject * deque_index_impl(dequeobject *deque, PyObject *v, Py_ssize_t start, Py_ssize_t stop) -/*[clinic end generated code: output=df45132753175ef9 input=90f48833a91e1743]*/ +/*[clinic end generated code: output=df45132753175ef9 input=1c3b19632cf3484f]*/ { Py_ssize_t i, n; PyObject *item; @@ -1253,22 +1270,23 @@ deque_index_impl(dequeobject *deque, PyObject *v, Py_ssize_t start, Py_ssize_t index = deque->leftindex; size_t start_state = deque->state; int cmp; + Py_ssize_t size = Py_SIZE(deque); if (start < 0) { - start += Py_SIZE(deque); + start += size; if (start < 0) start = 0; } if (stop < 0) { - stop += Py_SIZE(deque); + stop += size; if (stop < 0) stop = 0; } - if (stop > Py_SIZE(deque)) - stop = Py_SIZE(deque); + if (stop > size) + stop = size; if (start > stop) start = stop; - assert(0 <= start && start <= stop && stop <= Py_SIZE(deque)); + assert(0 <= start && start <= stop && stop <= size); for (i=0 ; i < start - BLOCKLEN ; i += BLOCKLEN) { b = b->rightlink; @@ -1838,7 +1856,7 @@ static PyMethodDef deque_methods[] = { DEQUE_ROTATE_METHODDEF DEQUE___SIZEOF___METHODDEF {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, PyDoc_STR("deques are generic over the type of their contents")}, {NULL, NULL} /* sentinel */ }; @@ -2231,11 +2249,11 @@ defdict_missing(PyObject *op, PyObject *key) value = _PyObject_CallNoArgs(factory); if (value == NULL) return value; - if (PyObject_SetItem(op, key, value) < 0) { - Py_DECREF(value); - return NULL; - } - return value; + PyObject *result = NULL; + (void)PyDict_SetDefaultRef(op, key, value, &result); + // 'result' is NULL, or a strong reference to 'value' or 'op[key]' + Py_DECREF(value); + return result; } static inline PyObject* @@ -2314,6 +2332,12 @@ defdict_reduce(PyObject *op, PyObject *Py_UNUSED(dummy)) return result; } + +PyDoc_STRVAR(defdict_class_getitem_doc, +"defaultdicts are generic over two types, signifying (respectively) the types \ +of the dictionary's keys and values"); + + static PyMethodDef defdict_methods[] = { {"__missing__", defdict_missing, METH_O, defdict_missing_doc}, @@ -2324,7 +2348,7 @@ static PyMethodDef defdict_methods[] = { {"__reduce__", defdict_reduce, METH_NOARGS, reduce_doc}, {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, - PyDoc_STR("See PEP 585")}, + defdict_class_getitem_doc}, {NULL} }; @@ -2369,9 +2393,10 @@ defdict_repr(PyObject *op) } defrepr = PyUnicode_FromString("..."); } - else + else { defrepr = PyObject_Repr(dd->default_factory); - Py_ReprLeave(dd->default_factory); + Py_ReprLeave(dd->default_factory); + } } if (defrepr == NULL) { Py_DECREF(baserepr); @@ -2403,7 +2428,7 @@ defdict_or(PyObject* left, PyObject* right) self = right; other = left; } - if (!PyDict_Check(other)) { + if (!PyAnyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } // Like copy(), this calls the object's class. @@ -2577,7 +2602,12 @@ _collections__count_elements_impl(PyObject *module, PyObject *mapping, if (_PyDict_SetItem_KnownHash(mapping, key, one, hash) < 0) goto done; } else { + /* oldval is a borrowed reference. Keep it alive across + PyNumber_Add(), which can execute arbitrary user code and + mutate (or even clear) the underlying dict. */ + Py_INCREF(oldval); newval = PyNumber_Add(oldval, one); + Py_DECREF(oldval); if (newval == NULL) goto done; if (_PyDict_SetItem_KnownHash(mapping, key, newval, hash) < 0) @@ -2851,6 +2881,7 @@ collections_exec(PyObject *module) { #undef ADD_TYPE static struct PyModuleDef_Slot collections_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, collections_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_csv.c b/Modules/_csv.c index 2e04136e0ac657..a7fcc78e058f05 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -315,8 +315,12 @@ _set_char(const char *name, Py_UCS4 *target, PyObject *src, Py_UCS4 dflt) static int _set_str(const char *name, PyObject **target, PyObject *src, const char *dflt) { - if (src == NULL) + if (src == NULL) { *target = PyUnicode_DecodeASCII(dflt, strlen(dflt), NULL); + if (*target == NULL) { + return -1; + } + } else { if (!PyUnicode_Check(src)) { PyErr_Format(PyExc_TypeError, @@ -497,13 +501,13 @@ dialect_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_XINCREF(skipinitialspace); Py_XINCREF(strict); if (dialect != NULL) { -#define DIALECT_GETATTR(v, n) \ - do { \ - if (v == NULL) { \ - v = PyObject_GetAttrString(dialect, n); \ - if (v == NULL) \ - PyErr_Clear(); \ - } \ +#define DIALECT_GETATTR(v, n) \ + do { \ + if (v == NULL) { \ + if (PyObject_GetOptionalAttrString(dialect, n, &v) < 0) { \ + goto err; \ + } \ + } \ } while (0) DIALECT_GETATTR(delimiter, "delimiter"); DIALECT_GETATTR(doublequote, "doublequote"); @@ -918,7 +922,7 @@ parse_reset(ReaderObj *self) } static PyObject * -Reader_iternext(PyObject *op) +Reader_iternext_lock_held(PyObject *op) { ReaderObj *self = _ReaderObj_CAST(op); @@ -961,6 +965,12 @@ Reader_iternext(PyObject *op) Py_DECREF(lineobj); return NULL; } + if (self->fields == NULL) { + PyErr_SetString(module_state->error_obj, + "iterator has already advanced the reader"); + Py_DECREF(lineobj); + return NULL; + } ++self->line_num; kind = PyUnicode_KIND(lineobj); data = PyUnicode_DATA(lineobj); @@ -985,6 +995,16 @@ Reader_iternext(PyObject *op) return fields; } +static PyObject * +Reader_iternext(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = Reader_iternext_lock_held(op); + Py_END_CRITICAL_SECTION(); + return result; +} + static void Reader_dealloc(PyObject *op) { @@ -1303,14 +1323,8 @@ join_append_lineterminator(WriterObj *self) return 1; } -PyDoc_STRVAR(csv_writerow_doc, -"writerow(iterable)\n" -"\n" -"Construct and write a CSV record from an iterable of fields. Non-string\n" -"elements will be converted to string."); - static PyObject * -csv_writerow(PyObject *op, PyObject *seq) +csv_writerow_lock_held(PyObject *op, PyObject *seq) { WriterObj *self = _WriterObj_CAST(op); DialectObj *dialect = self->dialect; @@ -1413,11 +1427,29 @@ csv_writerow(PyObject *op, PyObject *seq) return result; } +PyDoc_STRVAR(csv_writerow_doc, +"writerow($self, row, /)\n" +"--\n\n" +"Construct and write a CSV record from an iterable of fields.\n" +"\n" +"Non-string elements will be converted to string."); + +static PyObject * +csv_writerow(PyObject *op, PyObject *seq) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = csv_writerow_lock_held(op, seq); + Py_END_CRITICAL_SECTION(); + return result; +} + PyDoc_STRVAR(csv_writerows_doc, -"writerows(iterable of iterables)\n" +"writerows($self, rows, /)\n" +"--\n\n" +"Construct and write a series of iterables to a csv file.\n" "\n" -"Construct and write a series of iterables to a csv file. Non-string\n" -"elements will be converted to string."); +"Non-string elements will be converted to string."); static PyObject * csv_writerows(PyObject *self, PyObject *seqseq) @@ -1574,13 +1606,11 @@ csv_writer(PyObject *module, PyObject *args, PyObject *keyword_args) _csv.list_dialects Return a list of all known dialect names. - - names = csv.list_dialects() [clinic start generated code]*/ static PyObject * _csv_list_dialects_impl(PyObject *module) -/*[clinic end generated code: output=a5b92b215b006a6d input=8953943eb17d98ab]*/ +/*[clinic end generated code: output=a5b92b215b006a6d input=ec58040aafd6a20a]*/ { return PyDict_Keys(get_csv_state(module)->dialects); } @@ -1617,13 +1647,11 @@ _csv.unregister_dialect name: object Delete the name/dialect mapping associated with a string name. - - csv.unregister_dialect(name) [clinic start generated code]*/ static PyObject * _csv_unregister_dialect_impl(PyObject *module, PyObject *name) -/*[clinic end generated code: output=0813ebca6c058df4 input=6b5c1557bf60c7e7]*/ +/*[clinic end generated code: output=0813ebca6c058df4 input=e1cf81bfe3ba0f62]*/ { _csvstate *module_state = get_csv_state(module); int rc = PyDict_Pop(module_state->dialects, name, NULL); @@ -1643,13 +1671,11 @@ _csv.get_dialect name: object Return the dialect instance associated with name. - - dialect = csv.get_dialect(name) [clinic start generated code]*/ static PyObject * _csv_get_dialect_impl(PyObject *module, PyObject *name) -/*[clinic end generated code: output=aa988cd573bebebb input=edf9ddab32e448fb]*/ +/*[clinic end generated code: output=aa988cd573bebebb input=74865c659dcb441f]*/ { return get_dialect_from_registry(name, get_csv_state(module)); } @@ -1661,15 +1687,13 @@ _csv.field_size_limit Sets an upper limit on parsed fields. - csv.field_size_limit([limit]) - Returns old limit. If limit is not given, no new limit is set and the old limit is returned [clinic start generated code]*/ static PyObject * _csv_field_size_limit_impl(PyObject *module, PyObject *new_limit) -/*[clinic end generated code: output=f2799ecd908e250b input=cec70e9226406435]*/ +/*[clinic end generated code: output=f2799ecd908e250b input=77db7485ee3ae90a]*/ { _csvstate *module_state = get_csv_state(module); Py_ssize_t old_limit = FT_ATOMIC_LOAD_SSIZE_RELAXED(module_state->field_limit); @@ -1705,14 +1729,13 @@ PyType_Spec error_spec = { PyDoc_STRVAR(csv_module_doc, "CSV parsing and writing.\n"); PyDoc_STRVAR(csv_reader_doc, -" csv_reader = reader(iterable [, dialect='excel']\n" -" [optional keyword args])\n" -" for row in csv_reader:\n" -" process(row)\n" +"reader($module, iterable, /, dialect='excel', **fmtparams)\n" +"--\n\n" +"Return a reader object that will process lines from the given iterable.\n" "\n" "The \"iterable\" argument can be any object that returns a line\n" "of input for each iteration, such as a file object or a list. The\n" -"optional \"dialect\" parameter is discussed below. The function\n" +"optional \"dialect\" argument defines a CSV dialect. The function\n" "also accepts optional keyword arguments which override settings\n" "provided by the dialect.\n" "\n" @@ -1720,22 +1743,24 @@ PyDoc_STRVAR(csv_reader_doc, "of the CSV file (which can span multiple input lines).\n"); PyDoc_STRVAR(csv_writer_doc, -" csv_writer = csv.writer(fileobj [, dialect='excel']\n" -" [optional keyword args])\n" -" for row in sequence:\n" -" csv_writer.writerow(row)\n" +"writer($module, fileobj, /, dialect='excel', **fmtparams)\n" +"--\n\n" +"Return a writer object that will write user data on the given file object.\n" "\n" -" [or]\n" -"\n" -" csv_writer = csv.writer(fileobj [, dialect='excel']\n" -" [optional keyword args])\n" -" csv_writer.writerows(rows)\n" -"\n" -"The \"fileobj\" argument can be any object that supports the file API.\n"); +"The \"fileobj\" argument can be any object that supports the file API.\n" +"The optional \"dialect\" argument defines a CSV dialect. The function\n" +"also accepts optional keyword arguments which override settings\n" +"provided by the dialect.\n"); PyDoc_STRVAR(csv_register_dialect_doc, -"Create a mapping from a string name to a dialect class.\n" -" dialect = csv.register_dialect(name[, dialect[, **fmtparams]])"); +"register_dialect($module, name, /, dialect='excel', **fmtparams)\n" +"--\n\n" +"Create a mapping from a string name to a CVS dialect.\n" +"\n" +"The optional \"dialect\" argument specifies the base dialect instance\n" +"or the name of the registered dialect. The function also accepts\n" +"optional keyword arguments which override settings provided by the\n" +"dialect.\n"); static struct PyMethodDef csv_methods[] = { { "reader", _PyCFunction_CAST(csv_reader), @@ -1814,6 +1839,7 @@ csv_exec(PyObject *module) { } static PyModuleDef_Slot csv_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, csv_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 4bd3e380b3bc4b..e891249668c20f 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -111,6 +111,7 @@ bytes(cdata) #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() #include "pycore_pyatomic_ft_wrappers.h" #include "pycore_object.h" +#include "pycore_tuple.h" // _PyTuple_FromPair #ifdef MS_WIN32 # include "pycore_modsupport.h" // _PyArg_NoKeywords() #endif @@ -266,41 +267,40 @@ _PyDict_GetItemProxy(PyObject *dict, PyObject *key, PyObject **presult) later on. */ static char * -_ctypes_alloc_format_string_for_type(char code, int big_endian) +_ctypes_alloc_format_string_for_type(const char *code, int big_endian) { - char *result; - char pep_code = '\0'; + const char *pep_code = NULL; - switch (code) { + switch (code[0]) { #if SIZEOF_INT == 2 - case 'i': pep_code = 'h'; break; - case 'I': pep_code = 'H'; break; + case 'i': pep_code = "h"; break; + case 'I': pep_code = "H"; break; #elif SIZEOF_INT == 4 - case 'i': pep_code = 'i'; break; - case 'I': pep_code = 'I'; break; + case 'i': pep_code = "i"; break; + case 'I': pep_code = "I"; break; #elif SIZEOF_INT == 8 - case 'i': pep_code = 'q'; break; - case 'I': pep_code = 'Q'; break; + case 'i': pep_code = "q"; break; + case 'I': pep_code = "Q"; break; #else # error SIZEOF_INT has an unexpected value #endif /* SIZEOF_INT */ #if SIZEOF_LONG == 4 - case 'l': pep_code = 'l'; break; - case 'L': pep_code = 'L'; break; + case 'l': pep_code = "l"; break; + case 'L': pep_code = "L"; break; #elif SIZEOF_LONG == 8 - case 'l': pep_code = 'q'; break; - case 'L': pep_code = 'Q'; break; + case 'l': pep_code = "q"; break; + case 'L': pep_code = "Q"; break; #else # error SIZEOF_LONG has an unexpected value #endif /* SIZEOF_LONG */ #if SIZEOF__BOOL == 1 - case '?': pep_code = '?'; break; + case '?': pep_code = "?"; break; #elif SIZEOF__BOOL == 2 - case '?': pep_code = 'H'; break; + case '?': pep_code = "H"; break; #elif SIZEOF__BOOL == 4 - case '?': pep_code = 'L'; break; + case '?': pep_code = "L"; break; #elif SIZEOF__BOOL == 8 - case '?': pep_code = 'Q'; break; + case '?': pep_code = "Q"; break; #else # error SIZEOF__BOOL has an unexpected value #endif /* SIZEOF__BOOL */ @@ -310,15 +310,14 @@ _ctypes_alloc_format_string_for_type(char code, int big_endian) break; } - result = PyMem_Malloc(3); + char *result = PyMem_Malloc(1 + strlen(pep_code) + 1); if (result == NULL) { PyErr_NoMemory(); return NULL; } result[0] = big_endian ? '>' : '<'; - result[1] = pep_code; - result[2] = '\0'; + strcpy(result + 1, pep_code); return result; } @@ -467,11 +466,7 @@ class _ctypes.CType_Type "PyObject *" "clinic_state()->CType_Type" static int CType_Type_traverse(PyObject *self, visitproc visit, void *arg) { - StgInfo *info = _PyStgInfo_FromType_NoState(self); - if (!info) { - PyErr_FormatUnraisable("Exception ignored while " - "calling ctypes traverse function %R", self); - } + StgInfo *info = _PyStgInfo_FromType_DuringGC(self); if (info) { Py_VISIT(info->proto); Py_VISIT(info->argtypes); @@ -515,11 +510,7 @@ ctype_free_stginfo_members(StgInfo *info) static int CType_Type_clear(PyObject *self) { - StgInfo *info = _PyStgInfo_FromType_NoState(self); - if (!info) { - PyErr_FormatUnraisable("Exception ignored while " - "clearing ctypes %R", self); - } + StgInfo *info = _PyStgInfo_FromType_DuringGC(self); if (info) { ctype_clear_stginfo(info); } @@ -529,11 +520,7 @@ CType_Type_clear(PyObject *self) static void CType_Type_dealloc(PyObject *self) { - StgInfo *info = _PyStgInfo_FromType_NoState(self); - if (!info) { - PyErr_FormatUnraisable("Exception ignored while " - "deallocating ctypes %R", self); - } + StgInfo *info = _PyStgInfo_FromType_DuringGC(self); if (info) { ctype_free_stginfo_members(info); } @@ -721,7 +708,7 @@ StructUnionType_paramfunc(ctypes_state *st, CDataObject *self) } assert(stginfo); /* Cannot be NULL for structure/union instances */ - parg->tag = 'V'; + parg->tag = "V"; parg->pffi_type = &stginfo->ffi_type_pointer; parg->value.p = ptr; parg->size = self->b_size; @@ -859,7 +846,7 @@ _ctypes.CDataType.from_buffer as CDataType_from_buffer type: self cls: defining_class obj: object - offset: Py_ssize_t = 0 + offset: Py_ssize_t(allow_negative=False) = 0 / C.from_buffer(object, offset=0) -> C instance @@ -870,7 +857,7 @@ Create a C instance from a writeable buffer. static PyObject * CDataType_from_buffer_impl(PyObject *type, PyTypeObject *cls, PyObject *obj, Py_ssize_t offset) -/*[clinic end generated code: output=57604e99635abd31 input=0f36cedd105ca28d]*/ +/*[clinic end generated code: output=57604e99635abd31 input=8f43e6bc44373180]*/ { PyObject *mv; PyObject *result; @@ -906,13 +893,6 @@ CDataType_from_buffer_impl(PyObject *type, PyTypeObject *cls, PyObject *obj, return NULL; } - if (offset < 0) { - PyErr_SetString(PyExc_ValueError, - "offset cannot be negative"); - Py_DECREF(mv); - return NULL; - } - if (info->size > buffer->len - offset) { PyErr_Format(PyExc_ValueError, "Buffer size too small " @@ -955,7 +935,7 @@ _ctypes.CDataType.from_buffer_copy as CDataType_from_buffer_copy type: self cls: defining_class buffer: Py_buffer - offset: Py_ssize_t = 0 + offset: Py_ssize_t(allow_negative=False) = 0 / C.from_buffer_copy(object, offset=0) -> C instance @@ -966,7 +946,7 @@ Create a C instance from a readable buffer. static PyObject * CDataType_from_buffer_copy_impl(PyObject *type, PyTypeObject *cls, Py_buffer *buffer, Py_ssize_t offset) -/*[clinic end generated code: output=c8fc62b03e5cc6fa input=2a81e11b765a6253]*/ +/*[clinic end generated code: output=c8fc62b03e5cc6fa input=41f97f512295ceec]*/ { PyObject *result; @@ -980,12 +960,6 @@ CDataType_from_buffer_copy_impl(PyObject *type, PyTypeObject *cls, return NULL; } - if (offset < 0) { - PyErr_SetString(PyExc_ValueError, - "offset cannot be negative"); - return NULL; - } - if (info->size > buffer->len - offset) { PyErr_Format(PyExc_ValueError, "Buffer size too small (%zd instead of at least %zd bytes)", @@ -1271,11 +1245,30 @@ PyCPointerType_SetProto(ctypes_state *st, PyObject *self, StgInfo *stginfo, PyOb return -1; } Py_XSETREF(stginfo->proto, Py_NewRef(proto)); + + // Set the format string for the pointer type based on element type. + // If info->format is NULL, this is a pointer to an incomplete type. + // We create a generic format string 'pointer to bytes' in this case. + char *new_format = NULL; STGINFO_LOCK(info); if (info->pointer_type == NULL) { Py_XSETREF(info->pointer_type, Py_NewRef(self)); } + const char *current_format = info->format ? info->format : "B"; + if (info->shape != NULL) { + // pointer to an array: the shape needs to be prefixed + new_format = _ctypes_alloc_format_string_with_shape( + info->ndim, info->shape, "&", current_format); + } else { + new_format = _ctypes_alloc_format_string("&", current_format); + } + PyMem_Free(stginfo->format); + stginfo->format = new_format; STGINFO_UNLOCK(); + + if (new_format == NULL) { + return -1; + } return 0; } @@ -1289,7 +1282,7 @@ PyCPointerType_paramfunc(ctypes_state *st, CDataObject *self) if (parg == NULL) return NULL; - parg->tag = 'P'; + parg->tag = "P"; parg->pffi_type = &ffi_type_pointer; parg->obj = Py_NewRef(self); parg->value.p = *(void **)self->b_ptr; @@ -1327,35 +1320,11 @@ PyCPointerType_init(PyObject *self, PyObject *args, PyObject *kwds) return -1; } if (proto) { - const char *current_format; if (PyCPointerType_SetProto(st, self, stginfo, proto) < 0) { Py_DECREF(proto); return -1; } - StgInfo *iteminfo; - if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { - Py_DECREF(proto); - return -1; - } - /* PyCPointerType_SetProto has verified proto has a stginfo. */ - assert(iteminfo); - /* If iteminfo->format is NULL, then this is a pointer to an - incomplete type. We create a generic format string - 'pointer to bytes' in this case. XXX Better would be to - fix the format string later... - */ - current_format = iteminfo->format ? iteminfo->format : "B"; - if (iteminfo->shape != NULL) { - /* pointer to an array: the shape needs to be prefixed */ - stginfo->format = _ctypes_alloc_format_string_with_shape( - iteminfo->ndim, iteminfo->shape, "&", current_format); - } else { - stginfo->format = _ctypes_alloc_format_string("&", current_format); - } Py_DECREF(proto); - if (stginfo->format == NULL) { - return -1; - } } return 0; @@ -1432,7 +1401,13 @@ PyCPointerType_from_param_impl(PyObject *type, PyTypeObject *cls, /* If we expect POINTER(<type>), but receive a <type> instance, accept it by calling byref(<type>). */ - assert(typeinfo->proto); + if (typeinfo->proto == NULL) { + PyErr_SetString( + PyExc_TypeError, + "cannot convert argument: POINTER _type_ type is not set" + ); + return NULL; + } switch (PyObject_IsInstance(value, typeinfo->proto)) { case 1: Py_INCREF(value); /* _byref steals a refcount */ @@ -1728,7 +1703,7 @@ PyCArrayType_paramfunc(ctypes_state *st, CDataObject *self) PyCArgObject *p = PyCArgObject_new(st); if (p == NULL) return NULL; - p->tag = 'P'; + p->tag = "P"; p->pffi_type = &ffi_type_pointer; p->value.p = (char *)self->b_ptr; p->obj = Py_NewRef(self); @@ -1934,7 +1909,7 @@ c_wchar_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) if (parg == NULL) return NULL; parg->pffi_type = &ffi_type_pointer; - parg->tag = 'Z'; + parg->tag = "Z"; parg->obj = fd->setfunc(&parg->value, value, 0); if (parg->obj == NULL) { Py_DECREF(parg); @@ -2023,7 +1998,7 @@ c_char_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) if (parg == NULL) return NULL; parg->pffi_type = &ffi_type_pointer; - parg->tag = 'z'; + parg->tag = "z"; parg->obj = fd->setfunc(&parg->value, value, 0); if (parg->obj == NULL) { Py_DECREF(parg); @@ -2117,7 +2092,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) if (parg == NULL) return NULL; parg->pffi_type = &ffi_type_pointer; - parg->tag = 'P'; + parg->tag = "P"; parg->obj = fd->setfunc(&parg->value, value, sizeof(void*)); if (parg->obj == NULL) { Py_DECREF(parg); @@ -2135,7 +2110,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) if (parg == NULL) return NULL; parg->pffi_type = &ffi_type_pointer; - parg->tag = 'z'; + parg->tag = "z"; parg->obj = fd->setfunc(&parg->value, value, 0); if (parg->obj == NULL) { Py_DECREF(parg); @@ -2152,7 +2127,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) if (parg == NULL) return NULL; parg->pffi_type = &ffi_type_pointer; - parg->tag = 'Z'; + parg->tag = "Z"; parg->obj = fd->setfunc(&parg->value, value, 0); if (parg->obj == NULL) { Py_DECREF(parg); @@ -2177,7 +2152,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) if (PyCArg_CheckExact(st, value)) { /* byref(c_xxx()) */ PyCArgObject *a = (PyCArgObject *)value; - if (a->tag == 'P') { + if (strcmp(a->tag, "P") == 0) { return Py_NewRef(value); } } @@ -2190,7 +2165,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) if (parg == NULL) return NULL; parg->pffi_type = &ffi_type_pointer; - parg->tag = 'P'; + parg->tag = "P"; Py_INCREF(value); // Function pointers don't change their contents, no need to lock parg->value.p = *(void **)func->b_ptr; @@ -2216,7 +2191,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) if (parg == NULL) return NULL; parg->pffi_type = &ffi_type_pointer; - parg->tag = 'Z'; + parg->tag = "Z"; parg->obj = Py_NewRef(value); /* Remember: b_ptr points to where the pointer is stored! */ Py_BEGIN_CRITICAL_SECTION(value); @@ -2245,6 +2220,36 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) return NULL; } +static int +set_stginfo_ffi_type_pointer(StgInfo *stginfo, struct fielddesc *fmt) +{ +#if defined(_Py_FFI_SUPPORT_C_COMPLEX) + if (!fmt->pffi_type->elements) { + stginfo->ffi_type_pointer = *fmt->pffi_type; + } + else { + /* From primitive types - only complex types have the elements + struct field as non-NULL (two element array). */ + assert(fmt->pffi_type->type == FFI_TYPE_COMPLEX); + const size_t els_size = 2 * sizeof(ffi_type *); + stginfo->ffi_type_pointer.size = fmt->pffi_type->size; + stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment; + stginfo->ffi_type_pointer.type = fmt->pffi_type->type; + stginfo->ffi_type_pointer.elements = PyMem_Malloc(els_size); + if (!stginfo->ffi_type_pointer.elements) { + PyErr_NoMemory(); + return -1; + } + memcpy(stginfo->ffi_type_pointer.elements, + fmt->pffi_type->elements, els_size); + } +#else + assert(!fmt->pffi_type->elements); + stginfo->ffi_type_pointer = *fmt->pffi_type; +#endif + return 0; +} + static PyMethodDef c_void_p_methods[] = {C_VOID_P_FROM_PARAM_METHODDEF {0}}; static PyMethodDef c_char_p_methods[] = {C_CHAR_P_FROM_PARAM_METHODDEF {0}}; static PyMethodDef c_wchar_p_methods[] = {C_WCHAR_P_FROM_PARAM_METHODDEF {0}}; @@ -2289,8 +2294,10 @@ static PyObject *CreateSwappedType(ctypes_state *st, PyTypeObject *type, Py_DECREF(result); return NULL; } - - stginfo->ffi_type_pointer = *fmt->pffi_type; + if (set_stginfo_ffi_type_pointer(stginfo, fmt)) { + Py_DECREF(result); + return NULL; + } stginfo->align = fmt->pffi_type->alignment; stginfo->length = 0; stginfo->size = fmt->pffi_type->size; @@ -2325,7 +2332,8 @@ PyCSimpleType_paramfunc(ctypes_state *st, CDataObject *self) if (parg == NULL) return NULL; - parg->tag = fmt[0]; + assert(strcmp(fd->code, fmt) == 0); + parg->tag = fd->code; parg->pffi_type = fd->pffi_type; parg->obj = Py_NewRef(self); memcpy(&parg->value, self->b_ptr, self->b_size); @@ -2338,7 +2346,6 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) { PyObject *proto; const char *proto_str; - Py_ssize_t proto_len; PyMethodDef *ml; struct fielddesc *fmt; @@ -2356,7 +2363,7 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) return -1; } if (PyUnicode_Check(proto)) { - proto_str = PyUnicode_AsUTF8AndSize(proto, &proto_len); + proto_str = PyUnicode_AsUTF8(proto); if (!proto_str) goto error; } else { @@ -2364,19 +2371,23 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) "class must define a '_type_' string attribute"); goto error; } - if (proto_len != 1) { - PyErr_SetString(PyExc_ValueError, - "class must define a '_type_' attribute " - "which must be a string of length 1"); - goto error; - } fmt = _ctypes_get_fielddesc(proto_str); if (!fmt) { - PyErr_Format(PyExc_AttributeError, - "class must define a '_type_' attribute which must be\n" - "a single character string containing one of the\n" - "supported types: '%s'.", - _ctypes_get_simple_type_chars()); + const char *complex_formats = _ctypes_get_complex_type_formats(); + if (complex_formats) { + PyErr_Format(PyExc_AttributeError, + "class must define a '_type_' attribute which must be\n" + "one of these characters: '%s',\n" + "or one of these strings: %s.", + _ctypes_get_simple_type_chars(), + complex_formats); + } + else { + PyErr_Format(PyExc_AttributeError, + "class must define a '_type_' attribute which must be\n" + "one of these characters: '%s'.\n", + _ctypes_get_simple_type_chars()); + } goto error; } @@ -2385,18 +2396,8 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) if (!stginfo) { goto error; } - - if (!fmt->pffi_type->elements) { - stginfo->ffi_type_pointer = *fmt->pffi_type; - } - else { - const size_t els_size = sizeof(fmt->pffi_type->elements); - stginfo->ffi_type_pointer.size = fmt->pffi_type->size; - stginfo->ffi_type_pointer.alignment = fmt->pffi_type->alignment; - stginfo->ffi_type_pointer.type = fmt->pffi_type->type; - stginfo->ffi_type_pointer.elements = PyMem_Malloc(els_size); - memcpy(stginfo->ffi_type_pointer.elements, - fmt->pffi_type->elements, els_size); + if (set_stginfo_ffi_type_pointer(stginfo, fmt)) { + goto error; } stginfo->align = fmt->pffi_type->alignment; stginfo->length = 0; @@ -2404,9 +2405,9 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) stginfo->setfunc = fmt->setfunc; stginfo->getfunc = fmt->getfunc; #ifdef WORDS_BIGENDIAN - stginfo->format = _ctypes_alloc_format_string_for_type(proto_str[0], 1); + stginfo->format = _ctypes_alloc_format_string_for_type(proto_str, 1); #else - stginfo->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0); + stginfo->format = _ctypes_alloc_format_string_for_type(proto_str, 0); #endif if (stginfo->format == NULL) { Py_DECREF(proto); @@ -2433,9 +2434,15 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) ml = c_char_p_methods; stginfo->flags |= TYPEFLAG_ISPOINTER; break; - case 'Z': /* c_wchar_p */ - ml = c_wchar_p_methods; - stginfo->flags |= TYPEFLAG_ISPOINTER; + case 'Z': + if (proto_str[1] == '\0') { + /* "Z": c_wchar_p */ + ml = c_wchar_p_methods; + stginfo->flags |= TYPEFLAG_ISPOINTER; + } + else { + ml = NULL; + } break; case 'P': /* c_void_p */ ml = c_void_p_methods; @@ -2572,7 +2579,8 @@ PyCSimpleType_from_param_impl(PyObject *type, PyTypeObject *cls, if (parg == NULL) return NULL; - parg->tag = fmt[0]; + assert(strcmp(fd->code, fmt) == 0); + parg->tag = fd->code; parg->pffi_type = fd->pffi_type; parg->obj = fd->setfunc(&parg->value, value, info->size); if (parg->obj) @@ -2826,7 +2834,7 @@ PyCFuncPtrType_paramfunc(ctypes_state *st, CDataObject *self) if (parg == NULL) return NULL; - parg->tag = 'P'; + parg->tag = "P"; parg->pffi_type = &ffi_type_pointer; parg->obj = Py_NewRef(self); parg->value.p = *(void **)self->b_ptr; @@ -3523,7 +3531,7 @@ _PyCData_set(ctypes_state *st, only it's object list. So we create a tuple, containing b_objects list PLUS the array itself, and return that! */ - return PyTuple_Pack(2, keep, value); + return _PyTuple_FromPair(keep, value); } PyErr_Format(PyExc_TypeError, "incompatible types, %s instance instead of %s instance", @@ -3647,6 +3655,9 @@ atomic_xgetref(PyObject *obj, PyObject **field) #endif } +static int +_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags, PyObject *argtypes); + /*[clinic input] @@ -3760,16 +3771,22 @@ static int _ctypes_CFuncPtr_argtypes_set_impl(PyCFuncPtrObject *self, PyObject *value) /*[clinic end generated code: output=596a36e2ae89d7d1 input=c4627573e980aa8b]*/ { - PyObject *converters; - if (value == NULL || value == Py_None) { atomic_xsetref(&self->argtypes, NULL); atomic_xsetref(&self->converters, NULL); } else { - ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self))); - converters = converters_from_argtypes(st, value); + PyTypeObject *type = Py_TYPE(self); + ctypes_state *st = get_module_state_by_def(Py_TYPE(type)); + + PyObject *converters = converters_from_argtypes(st, value); if (!converters) return -1; + + /* Verify paramflags again due to constraints with argtypes */ + if (!_validate_paramflags(st, type, self->paramflags, value)) { + Py_DECREF(converters); + return -1; + } atomic_xsetref(&self->converters, converters); Py_INCREF(value); atomic_xsetref(&self->argtypes, value); @@ -3899,10 +3916,9 @@ _check_outarg_type(ctypes_state *st, PyObject *arg, Py_ssize_t index) /* Returns 1 on success, 0 on error */ static int -_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags) +_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags, PyObject *argtypes) { Py_ssize_t i, len; - PyObject *argtypes; StgInfo *info; if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { @@ -3913,10 +3929,13 @@ _validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags) "abstract class"); return 0; } - argtypes = info->argtypes; + if (argtypes == NULL) { + argtypes = info->argtypes; + } - if (paramflags == NULL || info->argtypes == NULL) + if (paramflags == NULL || argtypes == NULL) { return 1; + } if (!PyTuple_Check(paramflags)) { PyErr_SetString(PyExc_TypeError, @@ -3925,7 +3944,7 @@ _validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags) } len = PyTuple_GET_SIZE(paramflags); - if (len != PyTuple_GET_SIZE(info->argtypes)) { + if (len != PyTuple_GET_SIZE(argtypes)) { PyErr_SetString(PyExc_ValueError, "paramflags must have the same length as argtypes"); return 0; @@ -4101,7 +4120,7 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) #endif #undef USE_DLERROR ctypes_state *st = get_module_state_by_def(Py_TYPE(type)); - if (!_validate_paramflags(st, type, paramflags)) { + if (!_validate_paramflags(st, type, paramflags, NULL)) { Py_DECREF(ftuple); return NULL; } @@ -4145,7 +4164,7 @@ PyCFuncPtr_FromVtblIndex(PyTypeObject *type, PyObject *args, PyObject *kwds) paramflags = NULL; ctypes_state *st = get_module_state_by_def(Py_TYPE(type)); - if (!_validate_paramflags(st, type, paramflags)) { + if (!_validate_paramflags(st, type, paramflags, NULL)) { return NULL; } self = (PyCFuncPtrObject *)generic_pycdata_new(st, type, args, kwds); @@ -4286,7 +4305,7 @@ _byref(ctypes_state *st, PyObject *obj) return NULL; } - parg->tag = 'P'; + parg->tag = "P"; parg->pffi_type = &ffi_type_pointer; parg->obj = obj; parg->value.p = ((CDataObject *)obj)->b_ptr; @@ -4559,6 +4578,7 @@ _build_result(PyObject *result, PyObject *callargs, v = PyTuple_GET_ITEM(callargs, i); v = PyObject_CallMethodNoArgs(v, &_Py_ID(__ctypes_from_outparam__)); if (v == NULL || numretvals == 1) { + Py_XDECREF(tup); Py_DECREF(callargs); return v; } @@ -5285,7 +5305,7 @@ Array_length(PyObject *myself) static PyMethodDef Array_methods[] = { {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, PyDoc_STR("Arrays are generic over the type of their elements")}, { NULL, NULL } }; @@ -5332,8 +5352,7 @@ PyCArrayType_from_ctype(ctypes_state *st, PyObject *itemtype, Py_ssize_t length) len = PyLong_FromSsize_t(length); if (len == NULL) return NULL; - key = PyTuple_Pack(2, itemtype, len); - Py_DECREF(len); + key = _PyTuple_FromPairSteal(Py_NewRef(itemtype), len); if (!key) return NULL; @@ -6336,7 +6355,6 @@ _ctypes_add_objects(PyObject *mod) MOD_ADD("FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO)); MOD_ADD("FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR)); MOD_ADD("FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI)); - MOD_ADD("__version__", PyUnicode_FromString("1.1.0")); MOD_ADD("_memmove_addr", PyLong_FromVoidPtr(memmove)); MOD_ADD("_memset_addr", PyLong_FromVoidPtr(memset)); @@ -6498,6 +6516,7 @@ module_free(void *module) } static PyModuleDef_Slot module_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _ctypes_mod_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index 66338805007853..991ff0d675c2f1 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -446,7 +446,7 @@ EXPORT(char *)my_strtok(char *token, const char *delim) return strtok(token, delim); } -EXPORT(char *)my_strchr(const char *s, int c) +EXPORT(const char *) my_strchr(const char *s, int c) { return strchr(s, c); } @@ -989,13 +989,10 @@ EXPORT(RECT) ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, { case 0: return ar; - break; case 1: return dr; - break; case 2: return gr; - break; } return ar; diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 65a0f14e2b076c..ccc57e347b07ac 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -168,8 +168,9 @@ _ctypes_get_errobj(ctypes_state *st, int **pspace) } else { void *space = PyMem_Calloc(2, sizeof(int)); - if (space == NULL) - return NULL; + if (space == NULL) { + return PyErr_NoMemory(); + } errobj = PyCapsule_New(space, CTYPES_CAPSULE_NAME_PYMEM, pymem_destructor); if (errobj == NULL) { PyMem_Free(space); @@ -468,7 +469,7 @@ PyCArgObject_new(ctypes_state *st) if (p == NULL) return NULL; p->pffi_type = NULL; - p->tag = '\0'; + p->tag = ""; p->obj = NULL; memset(&p->value, 0, sizeof(p->value)); PyObject_GC_Track(p); @@ -512,45 +513,50 @@ static PyObject * PyCArg_repr(PyObject *op) { PyCArgObject *self = _PyCArgObject_CAST(op); - switch(self->tag) { + + if (strlen(self->tag) != 1) { + goto generic; + } + + switch(self->tag[0]) { case 'b': case 'B': - return PyUnicode_FromFormat("<cparam '%c' (%d)>", + return PyUnicode_FromFormat("<cparam '%s' (%d)>", self->tag, self->value.b); case 'h': case 'H': - return PyUnicode_FromFormat("<cparam '%c' (%d)>", + return PyUnicode_FromFormat("<cparam '%s' (%d)>", self->tag, self->value.h); case 'i': case 'I': - return PyUnicode_FromFormat("<cparam '%c' (%d)>", + return PyUnicode_FromFormat("<cparam '%s' (%d)>", self->tag, self->value.i); case 'l': case 'L': - return PyUnicode_FromFormat("<cparam '%c' (%ld)>", + return PyUnicode_FromFormat("<cparam '%s' (%ld)>", self->tag, self->value.l); case 'q': case 'Q': - return PyUnicode_FromFormat("<cparam '%c' (%lld)>", + return PyUnicode_FromFormat("<cparam '%s' (%lld)>", self->tag, self->value.q); case 'd': case 'f': { - PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d); + PyObject *f = PyFloat_FromDouble((strcmp(self->tag, "f") == 0) ? self->value.f : self->value.d); if (f == NULL) { return NULL; } - PyObject *result = PyUnicode_FromFormat("<cparam '%c' (%R)>", self->tag, f); + PyObject *result = PyUnicode_FromFormat("<cparam '%s' (%R)>", self->tag, f); Py_DECREF(f); return result; } case 'c': if (is_literal_char((unsigned char)self->value.c)) { - return PyUnicode_FromFormat("<cparam '%c' ('%c')>", + return PyUnicode_FromFormat("<cparam '%s' ('%c')>", self->tag, self->value.c); } else { - return PyUnicode_FromFormat("<cparam '%c' ('\\x%02x')>", + return PyUnicode_FromFormat("<cparam '%s' ('\\x%02x')>", self->tag, (unsigned char)self->value.c); } @@ -561,20 +567,16 @@ PyCArg_repr(PyObject *op) case 'z': case 'Z': case 'P': - return PyUnicode_FromFormat("<cparam '%c' (%p)>", + return PyUnicode_FromFormat("<cparam '%s' (%p)>", self->tag, self->value.p); - break; default: - if (is_literal_char((unsigned char)self->tag)) { - return PyUnicode_FromFormat("<cparam '%c' at %p>", - (unsigned char)self->tag, (void *)self); - } - else { - return PyUnicode_FromFormat("<cparam 0x%02x at %p>", - (unsigned char)self->tag, (void *)self); - } + break; } + +generic: + return PyUnicode_FromFormat("<cparam '%s' at %p>", + self->tag, (void *)self); } static PyMemberDef PyCArgType_members[] = { @@ -641,9 +643,9 @@ union result { double d; float f; void *p; - double D[2]; - float F[2]; - long double G[2]; + double Zd[2]; + float Zf[2]; + long double Zg[2]; }; struct argument { @@ -1787,6 +1789,7 @@ align_func(PyObject *self, PyObject *obj) /*[clinic input] +@permit_long_summary @critical_section obj _ctypes.byref obj: object(subclass_of="clinic_state()->PyCData_Type") @@ -1798,7 +1801,7 @@ Return a pointer lookalike to a C instance, only usable as function argument. static PyObject * _ctypes_byref_impl(PyObject *module, PyObject *obj, Py_ssize_t offset) -/*[clinic end generated code: output=60dec5ed520c71de input=6ec02d95d15fbd56]*/ +/*[clinic end generated code: output=60dec5ed520c71de input=870076149a2de427]*/ { ctypes_state *st = get_module_state(module); @@ -1806,7 +1809,7 @@ _ctypes_byref_impl(PyObject *module, PyObject *obj, Py_ssize_t offset) if (parg == NULL) return NULL; - parg->tag = 'P'; + parg->tag = "P"; parg->pffi_type = &ffi_type_pointer; parg->obj = Py_NewRef(obj); parg->value.p = (char *)((CDataObject *)obj)->b_ptr + offset; @@ -1989,8 +1992,32 @@ buffer_info(PyObject *self, PyObject *arg) } +static PyObject * +_ctypes_getattr(PyObject *Py_UNUSED(self), PyObject *args) +{ + PyObject *name; + if (!PyArg_UnpackTuple(args, "__getattr__", 1, 1, &name)) { + return NULL; + } + + if (PyUnicode_Check(name) && PyUnicode_EqualToUTF8(name, "__version__")) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "'__version__' is deprecated and slated for " + "removal in Python 3.20", + 1) < 0) { + return NULL; + } + return PyUnicode_FromString("1.1.0"); // Do not change + } + + PyErr_Format(PyExc_AttributeError, + "module '_ctypes' has no attribute %R", name); + return NULL; +} + PyMethodDef _ctypes_module_methods[] = { + {"__getattr__", _ctypes_getattr, METH_VARARGS}, {"get_errno", get_errno, METH_NOARGS}, {"set_errno", set_errno, METH_VARARGS}, {"_unpickle", unpickle, METH_VARARGS }, diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 547e2471a1cbc0..00b075bccff664 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -160,8 +160,8 @@ PyCField_new_impl(PyTypeObject *type, PyObject *name, PyObject *proto, if ((bitfield_size + bit_offset) > byte_size * 8) { PyErr_Format( PyExc_ValueError, - "bit field %R overflows its type (%zd + %zd >= %zd)", - name, bit_offset, byte_size*8); + "bit field %R overflows its type (%zd + %zd > %zd)", + name, bit_offset, bitfield_size, byte_size * 8); goto error; } } @@ -767,9 +767,9 @@ d_get(void *ptr, Py_ssize_t size) corresponding real type; the first element is equal to the real part, and the second element to the imaginary part, of the complex number." */ -/* D: double complex */ +/* Zd: double complex */ static PyObject * -D_set(void *ptr, PyObject *value, Py_ssize_t size) +Zd_set(void *ptr, PyObject *value, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(double))); Py_complex c = PyComplex_AsCComplex(value); @@ -783,7 +783,7 @@ D_set(void *ptr, PyObject *value, Py_ssize_t size) } static PyObject * -D_get(void *ptr, Py_ssize_t size) +Zd_get(void *ptr, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(double))); double x[2]; @@ -792,9 +792,47 @@ D_get(void *ptr, Py_ssize_t size) return PyComplex_FromDoubles(x[0], x[1]); } -/* F: float complex */ static PyObject * -F_set(void *ptr, PyObject *value, Py_ssize_t size) +Zd_set_sw(void *ptr, PyObject *value, Py_ssize_t size) +{ + assert(NUM_BITS(size) || (size == 2*sizeof(double))); + Py_complex c = PyComplex_AsCComplex(value); + + if (c.real == -1 && PyErr_Occurred()) { + return NULL; + } +#ifdef WORDS_BIGENDIAN + if (PyFloat_Pack8(c.real, ptr, 1) + || PyFloat_Pack8(c.imag, ptr + sizeof(double), 1)) + { + return NULL; + } +#else + if (PyFloat_Pack8(c.real, ptr, 0) + || PyFloat_Pack8(c.imag, ptr + sizeof(double), 0)) + { + return NULL; + } +#endif + _RET(value); +} + +static PyObject * +Zd_get_sw(void *ptr, Py_ssize_t size) +{ + assert(NUM_BITS(size) || (size == 2*sizeof(double))); +#ifdef WORDS_BIGENDIAN + return PyComplex_FromDoubles(PyFloat_Unpack8(ptr, 1), + PyFloat_Unpack8(ptr + sizeof(double), 1)); +#else + return PyComplex_FromDoubles(PyFloat_Unpack8(ptr, 0), + PyFloat_Unpack8(ptr + sizeof(double), 0)); +#endif +} + +/* Zf: float complex */ +static PyObject * +Zf_set(void *ptr, PyObject *value, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(float))); Py_complex c = PyComplex_AsCComplex(value); @@ -808,7 +846,7 @@ F_set(void *ptr, PyObject *value, Py_ssize_t size) } static PyObject * -F_get(void *ptr, Py_ssize_t size) +Zf_get(void *ptr, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(float))); float x[2]; @@ -817,9 +855,47 @@ F_get(void *ptr, Py_ssize_t size) return PyComplex_FromDoubles(x[0], x[1]); } -/* G: long double complex */ static PyObject * -G_set(void *ptr, PyObject *value, Py_ssize_t size) +Zf_set_sw(void *ptr, PyObject *value, Py_ssize_t size) +{ + assert(NUM_BITS(size) || (size == 2*sizeof(float))); + Py_complex c = PyComplex_AsCComplex(value); + + if (c.real == -1 && PyErr_Occurred()) { + return NULL; + } +#ifdef WORDS_BIGENDIAN + if (PyFloat_Pack4(c.real, ptr, 1) + || PyFloat_Pack4(c.imag, ptr + sizeof(float), 1)) + { + return NULL; + } +#else + if (PyFloat_Pack4(c.real, ptr, 0) + || PyFloat_Pack4(c.imag, ptr + sizeof(float), 0)) + { + return NULL; + } +#endif + _RET(value); +} + +static PyObject * +Zf_get_sw(void *ptr, Py_ssize_t size) +{ + assert(NUM_BITS(size) || (size == 2*sizeof(float))); +#ifdef WORDS_BIGENDIAN + return PyComplex_FromDoubles(PyFloat_Unpack4(ptr, 1), + PyFloat_Unpack4(ptr + sizeof(float), 1)); +#else + return PyComplex_FromDoubles(PyFloat_Unpack4(ptr, 0), + PyFloat_Unpack4(ptr + sizeof(float), 0)); +#endif +} + +/* Zg: long double complex */ +static PyObject * +Zg_set(void *ptr, PyObject *value, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(long double))); Py_complex c = PyComplex_AsCComplex(value); @@ -833,7 +909,7 @@ G_set(void *ptr, PyObject *value, Py_ssize_t size) } static PyObject * -G_get(void *ptr, Py_ssize_t size) +Zg_get(void *ptr, Py_ssize_t size) { assert(NUM_BITS(size) || (size == 2*sizeof(long double))); long double x[2]; @@ -841,7 +917,8 @@ G_get(void *ptr, Py_ssize_t size) memcpy(&x, ptr, sizeof(x)); return PyComplex_FromDoubles((double)x[0], (double)x[1]); } -#endif +#endif // _Py_FFI_SUPPORT_C_COMPLEX + /* d: double */ static PyObject * @@ -1375,7 +1452,7 @@ struct formattable { for nbytes in 8, 16, 32, 64: for sgn in 'i', 'u': print(f' struct fielddesc fmt_{sgn}{nbytes};') -for code in 'sbBcdFDGgfhHiIlLqQPzuUZXvO': +for code in 'sbBcdgfhHiIlLqQPzuUZXvO': print(f' struct fielddesc fmt_{code};') [python start generated code]*/ struct fielddesc fmt_i8; @@ -1391,9 +1468,6 @@ for code in 'sbBcdFDGgfhHiIlLqQPzuUZXvO': struct fielddesc fmt_B; struct fielddesc fmt_c; struct fielddesc fmt_d; - struct fielddesc fmt_F; - struct fielddesc fmt_D; - struct fielddesc fmt_G; struct fielddesc fmt_g; struct fielddesc fmt_f; struct fielddesc fmt_h; @@ -1412,7 +1486,10 @@ for code in 'sbBcdFDGgfhHiIlLqQPzuUZXvO': struct fielddesc fmt_X; struct fielddesc fmt_v; struct fielddesc fmt_O; -/*[python end generated code: output=f5a07c066fedaca6 input=ffa5d46c29dfb07a]*/ +/*[python end generated code: output=266ae6d30b6286a1 input=a1b7a263c7cf681f]*/ + struct fielddesc fmt_Zf; + struct fielddesc fmt_Zd; + struct fielddesc fmt_Zg; // bool has code '?': struct fielddesc fmt_bool; @@ -1422,7 +1499,7 @@ for code in 'sbBcdFDGgfhHiIlLqQPzuUZXvO': // Result of _ctypes_get_simple_type_chars. Initialized just after // the rest of formattable, so we stash it here. - char simple_type_chars[26]; + char simple_type_chars[23]; }; static struct formattable formattable; @@ -1541,7 +1618,7 @@ for base_code, base_c_type in [ (base_code.upper(), 'unsigned ' + base_c_type, 'u' + base_c_type), ]: print(f' formattable.fmt_{code} = *FIXINT_FIELDDESC_FOR({c_type});') - print(f" formattable.fmt_{code}.code = '{code}';") + print(f' formattable.fmt_{code}.code = "{code}";') if base_code == 'q': # ffi doesn't have `long long`; keep use the fixint type pass @@ -1549,34 +1626,34 @@ for base_code, base_c_type in [ print(f' formattable.fmt_{code}.pffi_type = &ffi_type_{ffi_type};') [python start generated code]*/ formattable.fmt_b = *FIXINT_FIELDDESC_FOR(signed char); - formattable.fmt_b.code = 'b'; + formattable.fmt_b.code = "b"; formattable.fmt_b.pffi_type = &ffi_type_schar; formattable.fmt_B = *FIXINT_FIELDDESC_FOR(unsigned char); - formattable.fmt_B.code = 'B'; + formattable.fmt_B.code = "B"; formattable.fmt_B.pffi_type = &ffi_type_uchar; formattable.fmt_h = *FIXINT_FIELDDESC_FOR(signed short); - formattable.fmt_h.code = 'h'; + formattable.fmt_h.code = "h"; formattable.fmt_h.pffi_type = &ffi_type_sshort; formattable.fmt_H = *FIXINT_FIELDDESC_FOR(unsigned short); - formattable.fmt_H.code = 'H'; + formattable.fmt_H.code = "H"; formattable.fmt_H.pffi_type = &ffi_type_ushort; formattable.fmt_i = *FIXINT_FIELDDESC_FOR(signed int); - formattable.fmt_i.code = 'i'; + formattable.fmt_i.code = "i"; formattable.fmt_i.pffi_type = &ffi_type_sint; formattable.fmt_I = *FIXINT_FIELDDESC_FOR(unsigned int); - formattable.fmt_I.code = 'I'; + formattable.fmt_I.code = "I"; formattable.fmt_I.pffi_type = &ffi_type_uint; formattable.fmt_l = *FIXINT_FIELDDESC_FOR(signed long); - formattable.fmt_l.code = 'l'; + formattable.fmt_l.code = "l"; formattable.fmt_l.pffi_type = &ffi_type_slong; formattable.fmt_L = *FIXINT_FIELDDESC_FOR(unsigned long); - formattable.fmt_L.code = 'L'; + formattable.fmt_L.code = "L"; formattable.fmt_L.pffi_type = &ffi_type_ulong; formattable.fmt_q = *FIXINT_FIELDDESC_FOR(signed long long); - formattable.fmt_q.code = 'q'; + formattable.fmt_q.code = "q"; formattable.fmt_Q = *FIXINT_FIELDDESC_FOR(unsigned long long); - formattable.fmt_Q.code = 'Q'; -/*[python end generated code: output=873c87a2e6b5075a input=ee814ca263aac18e]*/ + formattable.fmt_Q.code = "Q"; +/*[python end generated code: output=b91080b4b821a6da input=7356e281df4debd3]*/ /* Other types have bespoke setters and getters named `@_set` and `@_get`, @@ -1586,7 +1663,7 @@ for base_code, base_c_type in [ #define _TABLE_ENTRY(SYMBOL, FFI_TYPE, ...) \ formattable.fmt_ ## SYMBOL = \ - (struct fielddesc){(#SYMBOL)[0], (FFI_TYPE), __VA_ARGS__}; \ + (struct fielddesc){(#SYMBOL), (FFI_TYPE), __VA_ARGS__}; \ /////////////////////////////////////////////////////////////////////////// #define TABLE_ENTRY(SYMBOL, FFI_TYPE) \ @@ -1601,9 +1678,11 @@ for base_code, base_c_type in [ TABLE_ENTRY_SW(d, &ffi_type_double); #if defined(_Py_FFI_SUPPORT_C_COMPLEX) if (Py_FFI_COMPLEX_AVAILABLE) { - TABLE_ENTRY(D, &ffi_type_complex_double); - TABLE_ENTRY(F, &ffi_type_complex_float); - TABLE_ENTRY(G, &ffi_type_complex_longdouble); + TABLE_ENTRY(Zd, &ffi_type_complex_double); + TABLE_ENTRY_SW(Zd, &ffi_type_complex_double); + TABLE_ENTRY(Zf, &ffi_type_complex_float); + TABLE_ENTRY_SW(Zf, &ffi_type_complex_float); + TABLE_ENTRY(Zg, &ffi_type_complex_longdouble); } #endif TABLE_ENTRY(g, &ffi_type_longdouble); @@ -1633,12 +1712,12 @@ for base_code, base_c_type in [ // ctypes.c_bool is unsigned for FFI, even where C bool is signed. formattable.fmt_bool = *_ctypes_fixint_fielddesc(sizeof(bool), false); - formattable.fmt_bool.code = '?'; + formattable.fmt_bool.code = "?"; formattable.fmt_bool.setfunc = bool_set; formattable.fmt_bool.getfunc = bool_get; /*[python input] -all_chars = "cbBhHiIlLdDFGfuzZqQPXOv?g" +all_chars = "cbBhHiIlLdfuzZqQPXOv?g" print(f' assert(sizeof(formattable.simple_type_chars) == {len(all_chars)+1});') print(f' int i = 0;') for char in all_chars: @@ -1647,7 +1726,7 @@ for char in all_chars: + f"formattable.simple_type_chars[i++] = '{char}';") print(f" formattable.simple_type_chars[i] = 0;") [python start generated code]*/ - assert(sizeof(formattable.simple_type_chars) == 26); + assert(sizeof(formattable.simple_type_chars) == 23); int i = 0; if (formattable.fmt_c.code) formattable.simple_type_chars[i++] = 'c'; if (formattable.fmt_b.code) formattable.simple_type_chars[i++] = 'b'; @@ -1659,9 +1738,6 @@ print(f" formattable.simple_type_chars[i] = 0;") if (formattable.fmt_l.code) formattable.simple_type_chars[i++] = 'l'; if (formattable.fmt_L.code) formattable.simple_type_chars[i++] = 'L'; if (formattable.fmt_d.code) formattable.simple_type_chars[i++] = 'd'; - if (formattable.fmt_D.code) formattable.simple_type_chars[i++] = 'D'; - if (formattable.fmt_F.code) formattable.simple_type_chars[i++] = 'F'; - if (formattable.fmt_G.code) formattable.simple_type_chars[i++] = 'G'; if (formattable.fmt_f.code) formattable.simple_type_chars[i++] = 'f'; if (formattable.fmt_u.code) formattable.simple_type_chars[i++] = 'u'; if (formattable.fmt_z.code) formattable.simple_type_chars[i++] = 'z'; @@ -1675,24 +1751,34 @@ print(f" formattable.simple_type_chars[i] = 0;") if (formattable.fmt_bool.code) formattable.simple_type_chars[i++] = '?'; if (formattable.fmt_g.code) formattable.simple_type_chars[i++] = 'g'; formattable.simple_type_chars[i] = 0; -/*[python end generated code: output=2aa52670d1570f18 input=cff3e7cb95adac61]*/ +/*[python end generated code: output=b78c8b7eed73d45a input=30ddc50637dd8ee4]*/ } #undef FIXINT_FIELDDESC_FOR _Py_COMP_DIAG_POP -char * +const char* _ctypes_get_simple_type_chars(void) { return formattable.simple_type_chars; } +const char* +_ctypes_get_complex_type_formats(void) { + if (Py_FFI_COMPLEX_AVAILABLE) { + return "'Zf', 'Zd', 'Zg'"; + } + else { + return NULL; + } +} + struct fielddesc * _ctypes_get_fielddesc(const char *fmt) { struct fielddesc *result = NULL; switch(fmt[0]) { /*[python input] -for code in 'sbBcdDFGgfhHiIlLqQPzuUZXvO': +for code in 'sbBcdgfhHiIlLqQPzuUXvO': print(f" case '{code}': result = &formattable.fmt_{code}; break;") [python start generated code]*/ case 's': result = &formattable.fmt_s; break; @@ -1700,9 +1786,6 @@ for code in 'sbBcdDFGgfhHiIlLqQPzuUZXvO': case 'B': result = &formattable.fmt_B; break; case 'c': result = &formattable.fmt_c; break; case 'd': result = &formattable.fmt_d; break; - case 'D': result = &formattable.fmt_D; break; - case 'F': result = &formattable.fmt_F; break; - case 'G': result = &formattable.fmt_G; break; case 'g': result = &formattable.fmt_g; break; case 'f': result = &formattable.fmt_f; break; case 'h': result = &formattable.fmt_h; break; @@ -1717,16 +1800,34 @@ for code in 'sbBcdDFGgfhHiIlLqQPzuUZXvO': case 'z': result = &formattable.fmt_z; break; case 'u': result = &formattable.fmt_u; break; case 'U': result = &formattable.fmt_U; break; - case 'Z': result = &formattable.fmt_Z; break; case 'X': result = &formattable.fmt_X; break; case 'v': result = &formattable.fmt_v; break; case 'O': result = &formattable.fmt_O; break; -/*[python end generated code: output=6e5c91940732fde9 input=902223feffc2fe38]*/ +/*[python end generated code: output=8e95bd0d49efb1c8 input=82d4ee1538b9b282]*/ + case 'Z': { + switch(fmt[1]) { + case '\0': result = &formattable.fmt_Z; break; + case 'd': result = &formattable.fmt_Zd; break; + case 'f': result = &formattable.fmt_Zf; break; + case 'g': result = &formattable.fmt_Zg; break; + } + break; + } case '?': result = &formattable.fmt_bool; break; } if (!result || !result->code) { return NULL; } + if (fmt[1] != '\0') { + if (fmt[0] == 'Z') { + if (fmt[2] != '\0') { + return NULL; + } + } + else { + return NULL; + } + } assert(result->pffi_type); assert(result->setfunc); assert(result->getfunc); diff --git a/Modules/_ctypes/clinic/_ctypes.c.h b/Modules/_ctypes/clinic/_ctypes.c.h index cf2e3fa21072ae..529872f0f17ebe 100644 --- a/Modules/_ctypes/clinic/_ctypes.c.h +++ b/Modules/_ctypes/clinic/_ctypes.c.h @@ -176,6 +176,11 @@ CDataType_from_buffer(PyObject *type, PyTypeObject *cls, PyObject *const *args, goto exit; } offset = ival; + if (offset < 0) { + PyErr_SetString(PyExc_ValueError, + "offset cannot be negative"); + goto exit; + } } skip_optional_posonly: return_value = CDataType_from_buffer_impl(type, cls, obj, offset); @@ -242,6 +247,11 @@ CDataType_from_buffer_copy(PyObject *type, PyTypeObject *cls, PyObject *const *a goto exit; } offset = ival; + if (offset < 0) { + PyErr_SetString(PyExc_ValueError, + "offset cannot be negative"); + goto exit; + } } skip_optional_posonly: return_value = CDataType_from_buffer_copy_impl(type, cls, &buffer, offset); @@ -1042,4 +1052,4 @@ Simple_from_outparm(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py } return Simple_from_outparm_impl(self, cls); } -/*[clinic end generated code: output=536c9bcf4e05913e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=22105663d71237ca input=a9049054013a1b77]*/ diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 9f995bf24080a5..248559aa364a19 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -280,7 +280,7 @@ extern CThunkObject *_ctypes_alloc_callback(ctypes_state *st, int flags); /* a table entry describing a predefined ctypes type */ struct fielddesc { - char code; + const char *code; ffi_type *pffi_type; /* always statically allocated */ SETFUNC setfunc; GETFUNC getfunc; @@ -289,7 +289,8 @@ struct fielddesc { }; // Get all single-character type codes (for use in error messages) -extern char *_ctypes_get_simple_type_chars(void); +extern const char* _ctypes_get_simple_type_chars(void); +extern const char* _ctypes_get_complex_type_formats(void); typedef struct CFieldObject { PyObject_HEAD @@ -493,7 +494,7 @@ PyObject *_ctypes_callproc(ctypes_state *st, struct tagPyCArgObject { PyObject_HEAD ffi_type *pffi_type; - char tag; + const char *tag; union { char c; char b; @@ -510,7 +511,7 @@ struct tagPyCArgObject { long double G[2]; } value; PyObject *obj; - Py_ssize_t size; /* for the 'V' tag */ + Py_ssize_t size; /* for the "V" tag */ }; #define _PyCArgObject_CAST(op) ((PyCArgObject *)(op)) @@ -608,25 +609,20 @@ PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result) return _stginfo_from_type(state, Py_TYPE(obj), result); } -/* A variant of PyStgInfo_FromType that doesn't need the state, +/* A variant of PyStgInfo_FromType that doesn't need the state + * and doesn't modify any refcounts, * so it can be called from finalization functions when the module * state is torn down. */ static inline StgInfo * -_PyStgInfo_FromType_NoState(PyObject *type) +_PyStgInfo_FromType_DuringGC(PyObject *type) { PyTypeObject *PyCType_Type; - if (PyType_GetBaseByToken(Py_TYPE(type), &pyctype_type_spec, &PyCType_Type) < 0) { - return NULL; - } + PyType_GetBaseByToken_DuringGC(Py_TYPE(type), &pyctype_type_spec, &PyCType_Type); if (PyCType_Type == NULL) { - PyErr_Format(PyExc_TypeError, "expected a ctypes type, got '%N'", type); return NULL; } - - StgInfo *info = PyObject_GetTypeData(type, PyCType_Type); - Py_DECREF(PyCType_Type); - return info; + return PyObject_GetTypeData_DuringGC(type, PyCType_Type); } // Initialize StgInfo on a newly created type diff --git a/Modules/_ctypes/malloc_closure.c b/Modules/_ctypes/malloc_closure.c index 80ba96614bff79..62c7aa5d6affbf 100644 --- a/Modules/_ctypes/malloc_closure.c +++ b/Modules/_ctypes/malloc_closure.c @@ -14,6 +14,7 @@ # endif #endif #include "ctypes.h" +#include "pycore_mmap.h" // _PyAnnotateMemoryMap() /* BLOCKSIZE can be adjusted. Larger blocksize will take a larger memory overhead, but allocate less blocks from the system. It may be that some @@ -30,11 +31,6 @@ #ifdef Py_GIL_DISABLED static PyMutex malloc_closure_lock; -# define MALLOC_CLOSURE_LOCK() PyMutex_Lock(&malloc_closure_lock) -# define MALLOC_CLOSURE_UNLOCK() PyMutex_Unlock(&malloc_closure_lock) -#else -# define MALLOC_CLOSURE_LOCK() ((void)0) -# define MALLOC_CLOSURE_UNLOCK() ((void)0) #endif typedef union _tagITEM { @@ -79,14 +75,16 @@ static void more_core(void) if (item == NULL) return; #else + size_t mem_size = count * sizeof(ITEM); item = (ITEM *)mmap(NULL, - count * sizeof(ITEM), + mem_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (item == (void *)MAP_FAILED) return; + _PyAnnotateMemoryMap(item, mem_size, "cpython:ctypes"); #endif #ifdef MALLOC_CLOSURE_DEBUG @@ -120,11 +118,11 @@ void Py_ffi_closure_free(void *p) } #endif #endif - MALLOC_CLOSURE_LOCK(); + FT_MUTEX_LOCK(&malloc_closure_lock); ITEM *item = (ITEM *)p; item->next = free_list; free_list = item; - MALLOC_CLOSURE_UNLOCK(); + FT_MUTEX_UNLOCK(&malloc_closure_lock); } /* return one item from the free list, allocating more if needed */ @@ -143,13 +141,13 @@ void *Py_ffi_closure_alloc(size_t size, void** codeloc) } #endif #endif - MALLOC_CLOSURE_LOCK(); + FT_MUTEX_LOCK(&malloc_closure_lock); ITEM *item; if (!free_list) { more_core(); } if (!free_list) { - MALLOC_CLOSURE_UNLOCK(); + FT_MUTEX_UNLOCK(&malloc_closure_lock); return NULL; } item = free_list; @@ -160,6 +158,6 @@ void *Py_ffi_closure_alloc(size_t size, void** codeloc) #else *codeloc = (void *)item; #endif - MALLOC_CLOSURE_UNLOCK(); + FT_MUTEX_UNLOCK(&malloc_closure_lock); return (void *)item; } diff --git a/Modules/_curses_panel.c b/Modules/_curses_panel.c index d7acfc6a06a974..52411e413533ce 100644 --- a/Modules/_curses_panel.c +++ b/Modules/_curses_panel.c @@ -364,12 +364,13 @@ _curses_panel.panel.hide Hide the panel. -This does not delete the object, it just makes the window on screen invisible. +This does not delete the object, it just makes the window on screen +invisible. [clinic start generated code]*/ static PyObject * _curses_panel_panel_hide_impl(PyCursesPanelObject *self) -/*[clinic end generated code: output=a7bbbd523e1eab49 input=f6ab884e99386118]*/ +/*[clinic end generated code: output=a7bbbd523e1eab49 input=9456aca9b264dde1]*/ { int rtn = hide_panel(self->pan); return curses_panel_panel_check_err(self, rtn, "hide_panel", "hide"); @@ -409,8 +410,11 @@ static PyObject * PyCursesPanel_New(_curses_panel_state *state, PANEL *pan, PyCursesWindowObject *wo) { - PyCursesPanelObject *po = PyObject_New(PyCursesPanelObject, - state->PyCursesPanel_Type); + assert(state != NULL); + PyTypeObject *type = state->PyCursesPanel_Type; + assert(type != NULL); + assert(type->tp_alloc != NULL); + PyCursesPanelObject *po = (PyCursesPanelObject *)type->tp_alloc(type, 0); if (po == NULL) { return NULL; } @@ -425,20 +429,31 @@ PyCursesPanel_New(_curses_panel_state *state, PANEL *pan, return (PyObject *)po; } +static int +PyCursesPanel_Clear(PyObject *op) +{ + PyCursesPanelObject *self = _PyCursesPanelObject_CAST(op); + PyObject *extra = (PyObject *)panel_userptr(self->pan); + if (extra != NULL) { + Py_DECREF(extra); + if (set_panel_userptr(self->pan, NULL) == ERR) { + curses_panel_panel_set_error(self, "set_panel_userptr", NULL); + return -1; + } + } + // self->wo should not be cleared because an associated WINDOW may exist + return 0; +} + static void PyCursesPanel_Dealloc(PyObject *self) { - PyObject *tp, *obj; - PyCursesPanelObject *po = _PyCursesPanelObject_CAST(self); + PyTypeObject *tp = Py_TYPE(self); + PyObject_GC_UnTrack(self); - tp = (PyObject *) Py_TYPE(po); - obj = (PyObject *) panel_userptr(po->pan); - if (obj) { - Py_DECREF(obj); - if (set_panel_userptr(po->pan, NULL) == ERR) { - curses_panel_panel_set_error(po, "set_panel_userptr", "__del__"); - PyErr_FormatUnraisable("Exception ignored in PyCursesPanel_Dealloc()"); - } + PyCursesPanelObject *po = _PyCursesPanelObject_CAST(self); + if (PyCursesPanel_Clear(self) < 0) { + PyErr_FormatUnraisable("Exception ignored in PyCursesPanel_Dealloc()"); } if (del_panel(po->pan) == ERR && !PyErr_Occurred()) { curses_panel_panel_set_error(po, "del_panel", "__del__"); @@ -451,10 +466,20 @@ PyCursesPanel_Dealloc(PyObject *self) PyErr_FormatUnraisable("Exception ignored in PyCursesPanel_Dealloc()"); } } - PyObject_Free(po); + tp->tp_free(po); Py_DECREF(tp); } +static int +PyCursesPanel_Traverse(PyObject *op, visitproc visit, void *arg) +{ + PyCursesPanelObject *self = _PyCursesPanelObject_CAST(op); + Py_VISIT(Py_TYPE(op)); + Py_VISIT(panel_userptr(self->pan)); + Py_VISIT(self->wo); + return 0; +} + /* panel_above(NULL) returns the bottom panel in the stack. To get this behaviour we use curses.panel.bottom_panel(). */ /*[clinic input] @@ -646,7 +671,9 @@ static PyMethodDef PyCursesPanel_Methods[] = { /* -------------------------------------------------------*/ static PyType_Slot PyCursesPanel_Type_slots[] = { + {Py_tp_clear, PyCursesPanel_Clear}, {Py_tp_dealloc, PyCursesPanel_Dealloc}, + {Py_tp_traverse, PyCursesPanel_Traverse}, {Py_tp_methods, PyCursesPanel_Methods}, {0, 0}, }; @@ -654,7 +681,12 @@ static PyType_Slot PyCursesPanel_Type_slots[] = { static PyType_Spec PyCursesPanel_Type_spec = { .name = "_curses_panel.panel", .basicsize = sizeof(PyCursesPanelObject), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_HAVE_GC + ), .slots = PyCursesPanel_Type_slots }; @@ -740,12 +772,13 @@ _curses_panel.update_panels Updates the virtual screen after changes in the panel stack. -This does not call curses.doupdate(), so you'll have to do this yourself. +This does not call curses.doupdate(), so you'll have to do this +yourself. [clinic start generated code]*/ static PyObject * _curses_panel_update_panels_impl(PyObject *module) -/*[clinic end generated code: output=2f3b4c2e03d90ded input=5299624c9a708621]*/ +/*[clinic end generated code: output=2f3b4c2e03d90ded input=0d0db79f05ec3ef4]*/ { PyCursesInitialised; update_panels(); @@ -813,6 +846,7 @@ _curses_panel_exec(PyObject *mod) } static PyModuleDef_Slot _curses_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _curses_panel_exec}, // XXX gh-103092: fix isolation. {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index d7788ef7a585b0..4438e384aab9b2 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1112,11 +1112,13 @@ _curses_window_addstr_impl(PyCursesWindowObject *self, int group_left_1, attr_old = getattrs(self->win); if (curses_wattrset(self, attr, "addstr") < 0) { curses_release_wstr(strtype, wstr); + Py_XDECREF(bytesobj); return NULL; } } #ifdef HAVE_NCURSESW if (strtype == 2) { + assert(bytesobj == NULL); if (use_xy) { rtn = mvwaddwstr(self->win,y,x,wstr); funcname = "mvwaddwstr"; @@ -1130,6 +1132,9 @@ _curses_window_addstr_impl(PyCursesWindowObject *self, int group_left_1, else #endif { +#ifdef HAVE_NCURSESW + assert(wstr == NULL); +#endif const char *str = PyBytes_AS_STRING(bytesobj); if (use_xy) { rtn = mvwaddstr(self->win,y,x,str); @@ -1210,6 +1215,7 @@ _curses_window_addnstr_impl(PyCursesWindowObject *self, int group_left_1, attr_old = getattrs(self->win); if (curses_wattrset(self, attr, "addnstr") < 0) { curses_release_wstr(strtype, wstr); + Py_XDECREF(bytesobj); return NULL; } } @@ -1298,12 +1304,12 @@ _curses.window.attron attr: long / -Add attribute attr from the "background" set. +Add attribute attr to the "background" set. [clinic start generated code]*/ static PyObject * _curses_window_attron_impl(PyCursesWindowObject *self, long attr) -/*[clinic end generated code: output=7afea43b237fa870 input=5a88fba7b1524f32]*/ +/*[clinic end generated code: output=7afea43b237fa870 input=b57f824e1bf58326]*/ { int rtn = wattron(self->win, (attr_t)attr); return curses_window_check_err(self, rtn, "wattron", "attron"); @@ -1375,10 +1381,10 @@ _curses.window.border Draw a border around the edges of the window. -Each parameter specifies the character to use for a specific part of the -border. The characters can be specified as integers or as one-character -strings. A 0 value for any parameter will cause the default character to be -used for that parameter. +Each parameter specifies the character to use for a specific part of +the border. The characters can be specified as integers or as +one-character strings. A 0 value for any parameter will cause the +default character to be used for that parameter. [clinic start generated code]*/ static PyObject * @@ -1386,7 +1392,7 @@ _curses_window_border_impl(PyCursesWindowObject *self, PyObject *ls, PyObject *rs, PyObject *ts, PyObject *bs, PyObject *tl, PyObject *tr, PyObject *bl, PyObject *br) -/*[clinic end generated code: output=670ef38d3d7c2aa3 input=e015f735d67a240b]*/ +/*[clinic end generated code: output=670ef38d3d7c2aa3 input=42568c1458221d24]*/ { chtype ch[8]; int i, rtn; @@ -1429,14 +1435,15 @@ _curses.window.box Draw a border around the edges of the window. -Similar to border(), but both ls and rs are verch and both ts and bs are -horch. The default corner characters are always used by this function. +Similar to border(), but both ls and rs are verch and both ts and bs +are horch. The default corner characters are always used by this +function. [clinic start generated code]*/ static PyObject * _curses_window_box_impl(PyCursesWindowObject *self, int group_right_1, PyObject *verch, PyObject *horch) -/*[clinic end generated code: output=f3fcb038bb287192 input=f00435f9c8c98f60]*/ +/*[clinic end generated code: output=f3fcb038bb287192 input=e11acb7dbf6790b6]*/ { chtype ch1 = 0, ch2 = 0; if (group_right_1) { @@ -1605,15 +1612,15 @@ _curses.window.derwin Create a sub-window (window-relative coordinates). -derwin() is the same as calling subwin(), except that begin_y and begin_x -are relative to the origin of the window, rather than relative to the entire -screen. +derwin() is the same as calling subwin(), except that begin_y and +begin_x are relative to the origin of the window, rather than +relative to the entire screen. [clinic start generated code]*/ static PyObject * _curses_window_derwin_impl(PyCursesWindowObject *self, int group_left_1, int nlines, int ncols, int begin_y, int begin_x) -/*[clinic end generated code: output=7924b112d9f70d6e input=966d9481f7f5022e]*/ +/*[clinic end generated code: output=7924b112d9f70d6e input=6efb50722be444ba]*/ { WINDOW *win; @@ -1669,6 +1676,7 @@ _curses_window_echochar_impl(PyCursesWindowObject *self, PyObject *ch, #ifdef NCURSES_MOUSE_VERSION /*[clinic input] +@permit_long_summary _curses.window.enclose y: int @@ -1682,7 +1690,7 @@ Return True if the screen-relative coordinates are enclosed by the window. static PyObject * _curses_window_enclose_impl(PyCursesWindowObject *self, int y, int x) -/*[clinic end generated code: output=8679beef50502648 input=4fd3355d723f7bc9]*/ +/*[clinic end generated code: output=8679beef50502648 input=9ba7c894cffe5507]*/ { return PyBool_FromLong(wenclose(self->win, y, x)); } @@ -1734,15 +1742,16 @@ _curses.window.getch Get a character code from terminal keyboard. -The integer returned does not have to be in ASCII range: function keys, -keypad keys and so on return numbers higher than 256. In no-delay mode, -1 -is returned if there is no input, else getch() waits until a key is pressed. +The integer returned does not have to be in ASCII range: function +keys, keypad keys and so on return numbers higher than 256. In +no-delay mode, -1 is returned if there is no input, else getch() +waits until a key is pressed. [clinic start generated code]*/ static PyObject * _curses_window_getch_impl(PyCursesWindowObject *self, int group_right_1, int y, int x) -/*[clinic end generated code: output=e1639e87d545e676 input=73f350336b1ee8c8]*/ +/*[clinic end generated code: output=e1639e87d545e676 input=0dc5ff40e079787a]*/ { int rtn; @@ -1781,15 +1790,16 @@ _curses.window.getkey Get a character (string) from terminal keyboard. -Returning a string instead of an integer, as getch() does. Function keys, -keypad keys and other special keys return a multibyte string containing the -key name. In no-delay mode, an exception is raised if there is no input. +Returning a string instead of an integer, as getch() does. Function +keys, keypad keys and other special keys return a multibyte string +containing the key name. In no-delay mode, an exception is raised +if there is no input. [clinic start generated code]*/ static PyObject * _curses_window_getkey_impl(PyCursesWindowObject *self, int group_right_1, int y, int x) -/*[clinic end generated code: output=8490a182db46b10f input=be2dee34f5cf57f8]*/ +/*[clinic end generated code: output=8490a182db46b10f input=bd24a7da1ed9c73b]*/ { int rtn; @@ -1927,7 +1937,6 @@ PyCursesWindow_getstr(PyObject *op, PyObject *args) int rtn, use_xy = 0, y = 0, x = 0; unsigned int max_buf_size = 2048; unsigned int n = max_buf_size - 1; - PyObject *res; if (!curses_clinic_parse_optional_xy_n(args, &y, &x, &n, &use_xy, "_curses.window.instr")) @@ -1936,11 +1945,11 @@ PyCursesWindow_getstr(PyObject *op, PyObject *args) } n = Py_MIN(n, max_buf_size - 1); - res = PyBytes_FromStringAndSize(NULL, n + 1); - if (res == NULL) { + PyBytesWriter *writer = PyBytesWriter_Create(n + 1); + if (writer == NULL) { return NULL; } - char *buf = PyBytes_AS_STRING(res); + char *buf = PyBytesWriter_GetData(writer); if (use_xy) { Py_BEGIN_ALLOW_THREADS @@ -1960,11 +1969,10 @@ PyCursesWindow_getstr(PyObject *op, PyObject *args) } if (rtn == ERR) { - Py_DECREF(res); + PyBytesWriter_Discard(writer); return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } - _PyBytes_Resize(&res, strlen(buf)); // 'res' is set to NULL on failure - return res; + return PyBytesWriter_FinishWithSize(writer, strlen(buf)); } /*[clinic input] @@ -2032,15 +2040,15 @@ _curses.window.insch Insert a character before the current or specified position. -All characters to the right of the cursor are shifted one position right, with -the rightmost characters on the line being lost. +All characters to the right of the cursor are shifted one position +right, with the rightmost characters on the line being lost. [clinic start generated code]*/ static PyObject * _curses_window_insch_impl(PyCursesWindowObject *self, int group_left_1, int y, int x, PyObject *ch, int group_right_1, long attr) -/*[clinic end generated code: output=ade8cfe3a3bf3e34 input=336342756ee19812]*/ +/*[clinic end generated code: output=ade8cfe3a3bf3e34 input=d662a0f96f33e15a]*/ { int rtn; chtype ch_ = 0; @@ -2074,13 +2082,14 @@ _curses.window.inch Return the character at the given position in the window. -The bottom 8 bits are the character proper, and upper bits are the attributes. +The bottom 8 bits are the character proper, and upper bits are the +attributes. [clinic start generated code]*/ static PyObject * _curses_window_inch_impl(PyCursesWindowObject *self, int group_right_1, int y, int x) -/*[clinic end generated code: output=97ca8581baaafd06 input=4b4fb43d85b177c3]*/ +/*[clinic end generated code: output=97ca8581baaafd06 input=7a03956d94dc9a69]*/ { chtype rtn; const char *funcname; @@ -2123,7 +2132,6 @@ PyCursesWindow_instr(PyObject *op, PyObject *args) int rtn, use_xy = 0, y = 0, x = 0; unsigned int max_buf_size = 2048; unsigned int n = max_buf_size - 1; - PyObject *res; if (!curses_clinic_parse_optional_xy_n(args, &y, &x, &n, &use_xy, "_curses.window.instr")) @@ -2132,11 +2140,11 @@ PyCursesWindow_instr(PyObject *op, PyObject *args) } n = Py_MIN(n, max_buf_size - 1); - res = PyBytes_FromStringAndSize(NULL, n + 1); - if (res == NULL) { + PyBytesWriter *writer = PyBytesWriter_Create(n + 1); + if (writer == NULL) { return NULL; } - char *buf = PyBytes_AS_STRING(res); + char *buf = PyBytesWriter_GetData(writer); if (use_xy) { rtn = mvwinnstr(self->win, y, x, buf, n); @@ -2146,11 +2154,10 @@ PyCursesWindow_instr(PyObject *op, PyObject *args) } if (rtn == ERR) { - Py_DECREF(res); + PyBytesWriter_Discard(writer); return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } - _PyBytes_Resize(&res, strlen(buf)); // 'res' is set to NULL on failure - return res; + return PyBytesWriter_FinishWithSize(writer, strlen(buf)); } /*[clinic input] @@ -2174,18 +2181,18 @@ _curses.window.insstr Insert the string before the current or specified position. -Insert a character string (as many characters as will fit on the line) -before the character under the cursor. All characters to the right of -the cursor are shifted right, with the rightmost characters on the line -being lost. The cursor position does not change (after moving to y, x, -if specified). +Insert a character string (as many characters as will fit on the +line) before the character under the cursor. All characters to the +right of the cursor are shifted right, with the rightmost characters +on the line being lost. The cursor position does not change (after +moving to y, x, if specified). [clinic start generated code]*/ static PyObject * _curses_window_insstr_impl(PyCursesWindowObject *self, int group_left_1, int y, int x, PyObject *str, int group_right_1, long attr) -/*[clinic end generated code: output=c259a5265ad0b777 input=6827cddc6340a7f3]*/ +/*[clinic end generated code: output=c259a5265ad0b777 input=dbfbdd3892155ea6]*/ { int rtn; int strtype; @@ -2209,6 +2216,7 @@ _curses_window_insstr_impl(PyCursesWindowObject *self, int group_left_1, attr_old = getattrs(self->win); if (curses_wattrset(self, attr, "insstr") < 0) { curses_release_wstr(strtype, wstr); + Py_XDECREF(bytesobj); return NULL; } } @@ -2273,19 +2281,19 @@ _curses.window.insnstr Insert at most n characters of the string. -Insert a character string (as many characters as will fit on the line) -before the character under the cursor, up to n characters. If n is zero -or negative, the entire string is inserted. All characters to the right -of the cursor are shifted right, with the rightmost characters on the line -being lost. The cursor position does not change (after moving to y, x, if -specified). +Insert a character string (as many characters as will fit on the +line) before the character under the cursor, up to n characters. If +n is zero or negative, the entire string is inserted. All +characters to the right of the cursor are shifted right, with the +rightmost characters on the line being lost. The cursor position +does not change (after moving to y, x, if specified). [clinic start generated code]*/ static PyObject * _curses_window_insnstr_impl(PyCursesWindowObject *self, int group_left_1, int y, int x, PyObject *str, int n, int group_right_1, long attr) -/*[clinic end generated code: output=971a32ea6328ec8b input=70fa0cd543901a4c]*/ +/*[clinic end generated code: output=971a32ea6328ec8b input=fd0a9b65b84b385f]*/ { int rtn; int strtype; @@ -2350,6 +2358,7 @@ _curses_window_insnstr_impl(PyCursesWindowObject *self, int group_left_1, } /*[clinic input] +@permit_long_summary _curses.window.is_linetouched line: int @@ -2358,12 +2367,13 @@ _curses.window.is_linetouched Return True if the specified line was modified, otherwise return False. -Raise a curses.error exception if line is not valid for the given window. +Raise a curses.error exception if line is not valid for the given +window. [clinic start generated code]*/ static PyObject * _curses_window_is_linetouched_impl(PyCursesWindowObject *self, int line) -/*[clinic end generated code: output=ad4a4edfee2db08c input=a7be0c189f243914]*/ +/*[clinic end generated code: output=ad4a4edfee2db08c input=18924dfac25ab7f1]*/ { int erg; erg = is_linetouched(self->win, line); @@ -2390,9 +2400,9 @@ _curses.window.noutrefresh Mark for refresh but wait. -This function updates the data structure representing the desired state of the -window, but does not force an update of the physical screen. To accomplish -that, call doupdate(). +This function updates the data structure representing the desired +state of the window, but does not force an update of the physical +screen. To accomplish that, call doupdate(). [clinic start generated code]*/ static PyObject * @@ -2400,21 +2410,21 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self, int group_right_1, int pminrow, int pmincol, int sminrow, int smincol, int smaxrow, int smaxcol) -/*[clinic end generated code: output=809a1f3c6a03e23e input=3e56898388cd739e]*/ +/*[clinic end generated code: output=809a1f3c6a03e23e input=8b4c74bf55008803]*/ #else /*[clinic input] _curses.window.noutrefresh Mark for refresh but wait. -This function updates the data structure representing the desired state of the -window, but does not force an update of the physical screen. To accomplish -that, call doupdate(). +This function updates the data structure representing the desired +state of the window, but does not force an update of the physical +screen. To accomplish that, call doupdate(). [clinic start generated code]*/ static PyObject * _curses_window_noutrefresh_impl(PyCursesWindowObject *self) -/*[clinic end generated code: output=6ef6dec666643fee input=876902e3fa431dbd]*/ +/*[clinic end generated code: output=6ef6dec666643fee input=a7c6306f8af9d0dd]*/ #endif { int rtn; @@ -2463,14 +2473,15 @@ _curses.window.overlay Overlay the window on top of destwin. -The windows need not be the same size, only the overlapping region is copied. -This copy is non-destructive, which means that the current background -character does not overwrite the old contents of destwin. +The windows need not be the same size, only the overlapping region +is copied. This copy is non-destructive, which means that the +current background character does not overwrite the old contents of +destwin. -To get fine-grained control over the copied region, the second form of -overlay() can be used. sminrow and smincol are the upper-left coordinates -of the source window, and the other variables mark a rectangle in the -destination window. +To get fine-grained control over the copied region, the second form +of overlay() can be used. sminrow and smincol are the upper-left +coordinates of the source window, and the other variables mark +a rectangle in the destination window. [clinic start generated code]*/ static PyObject * @@ -2478,7 +2489,7 @@ _curses_window_overlay_impl(PyCursesWindowObject *self, PyCursesWindowObject *destwin, int group_right_1, int sminrow, int smincol, int dminrow, int dmincol, int dmaxrow, int dmaxcol) -/*[clinic end generated code: output=82bb2c4cb443ca58 input=6e4b32a7c627a356]*/ +/*[clinic end generated code: output=82bb2c4cb443ca58 input=da0cec7f7bda1b3f]*/ { int rtn; @@ -2510,14 +2521,15 @@ _curses.window.overwrite Overwrite the window on top of destwin. -The windows need not be the same size, in which case only the overlapping -region is copied. This copy is destructive, which means that the current -background character overwrites the old contents of destwin. +The windows need not be the same size, in which case only the +overlapping region is copied. This copy is destructive, which means +that the current background character overwrites the old contents of +destwin. -To get fine-grained control over the copied region, the second form of -overwrite() can be used. sminrow and smincol are the upper-left coordinates -of the source window, the other variables mark a rectangle in the destination -window. +To get fine-grained control over the copied region, the second form +of overwrite() can be used. sminrow and smincol are the upper-left +coordinates of the source window, the other variables mark +a rectangle in the destination window. [clinic start generated code]*/ static PyObject * @@ -2526,7 +2538,7 @@ _curses_window_overwrite_impl(PyCursesWindowObject *self, int group_right_1, int sminrow, int smincol, int dminrow, int dmincol, int dmaxrow, int dmaxcol) -/*[clinic end generated code: output=12ae007d1681be28 input=d83dd8b24ff2bcc9]*/ +/*[clinic end generated code: output=12ae007d1681be28 input=4244ab8a97087898]*/ { int rtn; @@ -2542,6 +2554,7 @@ _curses_window_overwrite_impl(PyCursesWindowObject *self, } /*[clinic input] +@permit_long_summary _curses.window.putwin file: object @@ -2554,7 +2567,7 @@ This information can be later retrieved using the getwin() function. static PyObject * _curses_window_putwin_impl(PyCursesWindowObject *self, PyObject *file) -/*[clinic end generated code: output=fdae68ac59b0281b input=0608648e09c8ea0a]*/ +/*[clinic end generated code: output=fdae68ac59b0281b input=959fc85a9e4a31c2]*/ { /* We have to simulate this by writing to a temporary FILE*, then reading back, then writing to the argument file. */ @@ -2625,23 +2638,24 @@ _curses.window.refresh Update the display immediately. Synchronize actual screen with previous drawing/deleting methods. -The 6 optional arguments can only be specified when the window is a pad -created with newpad(). The additional parameters are needed to indicate -what part of the pad and screen are involved. pminrow and pmincol specify -the upper left-hand corner of the rectangle to be displayed in the pad. -sminrow, smincol, smaxrow, and smaxcol specify the edges of the rectangle to -be displayed on the screen. The lower right-hand corner of the rectangle to -be displayed in the pad is calculated from the screen coordinates, since the -rectangles must be the same size. Both rectangles must be entirely contained -within their respective structures. Negative values of pminrow, pmincol, -sminrow, or smincol are treated as if they were zero. +The 6 optional arguments can only be specified when the window is +a pad created with newpad(). The additional parameters are needed +to indicate what part of the pad and screen are involved. pminrow +and pmincol specify the upper left-hand corner of the rectangle to +be displayed in the pad. sminrow, smincol, smaxrow, and smaxcol +specify the edges of the rectangle to be displayed on the screen. +The lower right-hand corner of the rectangle to be displayed in the +pad is calculated from the screen coordinates, since the rectangles +must be the same size. Both rectangles must be entirely contained +within their respective structures. Negative values of pminrow, +pmincol, sminrow, or smincol are treated as if they were zero. [clinic start generated code]*/ static PyObject * _curses_window_refresh_impl(PyCursesWindowObject *self, int group_right_1, int pminrow, int pmincol, int sminrow, int smincol, int smaxrow, int smaxcol) -/*[clinic end generated code: output=42199543115e6e63 input=95e01cb5ffc635d0]*/ +/*[clinic end generated code: output=42199543115e6e63 input=ff2e900c6b2696b1]*/ { int rtn; @@ -2710,14 +2724,14 @@ _curses.window.subwin Create a sub-window (screen-relative coordinates). -By default, the sub-window will extend from the specified position to the -lower right corner of the window. +By default, the sub-window will extend from the specified position +to the lower right corner of the window. [clinic start generated code]*/ static PyObject * _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1, int nlines, int ncols, int begin_y, int begin_x) -/*[clinic end generated code: output=93e898afc348f59a input=2129fa47fd57721c]*/ +/*[clinic end generated code: output=93e898afc348f59a input=07b5058cb8820595]*/ { WINDOW *win; const char *funcname; @@ -2755,13 +2769,14 @@ _curses.window.scroll Scroll the screen or scrolling region. -Scroll upward if the argument is positive and downward if it is negative. +Scroll upward if the argument is positive and downward if it is +negative. [clinic start generated code]*/ static PyObject * _curses_window_scroll_impl(PyCursesWindowObject *self, int group_right_1, int lines) -/*[clinic end generated code: output=4541a8a11852d360 input=c969ca0cfabbdbec]*/ +/*[clinic end generated code: output=4541a8a11852d360 input=d8d81a5b52b9b40f]*/ { int rtn; const char *funcname; @@ -2788,14 +2803,15 @@ _curses.window.touchline Pretend count lines have been changed, starting with line start. -If changed is supplied, it specifies whether the affected lines are marked -as having been changed (changed=True) or unchanged (changed=False). +If changed is supplied, it specifies whether the affected lines are +marked as having been changed (changed=True) or unchanged +(changed=False). [clinic start generated code]*/ static PyObject * _curses_window_touchline_impl(PyCursesWindowObject *self, int start, int count, int group_right_1, int changed) -/*[clinic end generated code: output=65d05b3f7438c61d input=a98aa4f79b6be845]*/ +/*[clinic end generated code: output=65d05b3f7438c61d input=e0dc62f90d9dea55]*/ { int rtn; const char *funcname; @@ -3142,6 +3158,7 @@ _curses_beep_impl(PyObject *module) NoArgNoReturnFunctionBody(beep) /*[clinic input] +@permit_long_summary _curses.can_change_color Return True if the programmer can change the colors displayed by the terminal. @@ -3149,7 +3166,7 @@ Return True if the programmer can change the colors displayed by the terminal. static PyObject * _curses_can_change_color_impl(PyObject *module) -/*[clinic end generated code: output=359df8c3c77d8bf1 input=d7718884de0092f2]*/ +/*[clinic end generated code: output=359df8c3c77d8bf1 input=8315c364ba1e5b4c]*/ NoArgTrueFalseFunctionBody(can_change_color) /*[clinic input] @@ -3161,16 +3178,17 @@ _curses.cbreak Enter cbreak mode. -In cbreak mode (sometimes called "rare" mode) normal tty line buffering is -turned off and characters are available to be read one by one. However, -unlike raw mode, special characters (interrupt, quit, suspend, and flow -control) retain their effects on the tty driver and calling program. -Calling first raw() then cbreak() leaves the terminal in cbreak mode. +In cbreak mode (sometimes called "rare" mode) normal tty line buffering +is turned off and characters are available to be read one by one. +However, unlike raw mode, special characters (interrupt, quit, suspend, +and flow control) retain their effects on the tty driver and calling +program. Calling first raw() then cbreak() leaves the terminal in +cbreak mode. [clinic start generated code]*/ static PyObject * _curses_cbreak_impl(PyObject *module, int flag) -/*[clinic end generated code: output=9f9dee9664769751 input=c7d0bddda93016c1]*/ +/*[clinic end generated code: output=9f9dee9664769751 input=42d81687f11ddbf3]*/ NoArgOrFlagNoReturnFunctionBody(cbreak, flag) /*[clinic input] @@ -3182,13 +3200,14 @@ _curses.color_content Return the red, green, and blue (RGB) components of the specified color. -A 3-tuple is returned, containing the R, G, B values for the given color, -which will be between 0 (no component) and 1000 (maximum amount of component). +A 3-tuple is returned, containing the R, G, B values for the given +color, which will be between 0 (no component) and 1000 (maximum amount +of component). [clinic start generated code]*/ static PyObject * _curses_color_content_impl(PyObject *module, int color_number) -/*[clinic end generated code: output=17b466df7054e0de input=03b5ed0472662aea]*/ +/*[clinic end generated code: output=17b466df7054e0de input=c95fb50093fa0be0]*/ { _CURSES_COLOR_VAL_TYPE r,g,b; @@ -3214,12 +3233,13 @@ _curses.color_pair Return the attribute value for displaying text in the specified color. This attribute value can be combined with A_STANDOUT, A_REVERSE, and the -other A_* attributes. pair_number() is the counterpart to this function. +other A_* attributes. pair_number() is the counterpart to this +function. [clinic start generated code]*/ static PyObject * _curses_color_pair_impl(PyObject *module, int pair_number) -/*[clinic end generated code: output=60718abb10ce9feb input=6034e9146f343802]*/ +/*[clinic end generated code: output=60718abb10ce9feb input=cf74bb81d3cc3370]*/ { PyCursesStatefulInitialised(module); PyCursesStatefulInitialisedColor(module); @@ -3237,14 +3257,14 @@ _curses.curs_set Set the cursor state. If the terminal supports the visibility requested, the previous cursor -state is returned; otherwise, an exception is raised. On many terminals, -the "visible" mode is an underline cursor and the "very visible" mode is -a block cursor. +state is returned; otherwise, an exception is raised. On many +terminals, the "visible" mode is an underline cursor and the "very +visible" mode is a block cursor. [clinic start generated code]*/ static PyObject * _curses_curs_set_impl(PyObject *module, int visibility) -/*[clinic end generated code: output=ee8e62483b1d6cd4 input=81a7924a65d29504]*/ +/*[clinic end generated code: output=ee8e62483b1d6cd4 input=e010767a328f322b]*/ { int erg; @@ -3279,14 +3299,15 @@ _curses.def_shell_mode Save the current terminal mode as the "shell" mode. -The "shell" mode is the mode when the running program is not using curses. +The "shell" mode is the mode when the running program is not using +curses. Subsequent calls to reset_shell_mode() will restore this mode. [clinic start generated code]*/ static PyObject * _curses_def_shell_mode_impl(PyObject *module) -/*[clinic end generated code: output=d6e42f5c768f860f input=5ead21f6f0baa894]*/ +/*[clinic end generated code: output=d6e42f5c768f860f input=3809f85615c0b693]*/ NoArgNoReturnFunctionBody(def_shell_mode) /*[clinic input] @@ -3328,12 +3349,13 @@ _curses.echo Enter echo mode. -In echo mode, each character input is echoed to the screen as it is entered. +In echo mode, each character input is echoed to the screen as it is +entered. [clinic start generated code]*/ static PyObject * _curses_echo_impl(PyObject *module, int flag) -/*[clinic end generated code: output=03acb2ddfa6c8729 input=86cd4d5bb1d569c0]*/ +/*[clinic end generated code: output=03acb2ddfa6c8729 input=b4e9064326da9da4]*/ NoArgOrFlagNoReturnFunctionBody(echo, flag) /*[clinic input] @@ -3371,12 +3393,13 @@ _curses.flash Flash the screen. -That is, change it to reverse-video and then change it back in a short interval. +That is, change it to reverse-video and then change it back in a short +interval. [clinic start generated code]*/ static PyObject * _curses_flash_impl(PyObject *module) -/*[clinic end generated code: output=488b8a0ebd9ea9b8 input=02fdfb06c8fc3171]*/ +/*[clinic end generated code: output=488b8a0ebd9ea9b8 input=90878e305432add9]*/ NoArgNoReturnFunctionBody(flash) /*[clinic input] @@ -3384,13 +3407,13 @@ _curses.flushinp Flush all input buffers. -This throws away any typeahead that has been typed by the user and has not -yet been processed by the program. +This throws away any typeahead that has been typed by the user and has +not yet been processed by the program. [clinic start generated code]*/ static PyObject * _curses_flushinp_impl(PyObject *module) -/*[clinic end generated code: output=7e7a1fc1473960f5 input=59d042e705cef5ec]*/ +/*[clinic end generated code: output=7e7a1fc1473960f5 input=3a63c7213be8043c]*/ NoArgNoReturnVoidFunctionBody(flushinp) #ifdef getsyx @@ -3576,6 +3599,7 @@ _curses_has_colors_impl(PyObject *module) NoArgTrueFalseFunctionBody(has_colors) /*[clinic input] +@permit_long_summary _curses.has_ic Return True if the terminal has insert- and delete-character capabilities. @@ -3583,7 +3607,7 @@ Return True if the terminal has insert- and delete-character capabilities. static PyObject * _curses_has_ic_impl(PyObject *module) -/*[clinic end generated code: output=6be24da9cb1268fe input=9bc2d3a797cc7324]*/ +/*[clinic end generated code: output=6be24da9cb1268fe input=e37fa080d879f7a9]*/ NoArgTrueFalseFunctionBody(has_ic) /*[clinic input] @@ -3599,6 +3623,7 @@ NoArgTrueFalseFunctionBody(has_il) #ifdef HAVE_CURSES_HAS_KEY /*[clinic input] +@permit_long_summary _curses.has_key key: int @@ -3610,7 +3635,7 @@ Return True if the current terminal type recognizes a key with that value. static PyObject * _curses_has_key_impl(PyObject *module, int key) -/*[clinic end generated code: output=19ad48319414d0b1 input=78bd44acf1a4997c]*/ +/*[clinic end generated code: output=19ad48319414d0b1 input=046ac6c72bbc9587]*/ { PyCursesStatefulInitialised(module); @@ -3665,13 +3690,14 @@ _curses.init_pair Change the definition of a color-pair. -If the color-pair was previously initialized, the screen is refreshed and -all occurrences of that color-pair are changed to the new definition. +If the color-pair was previously initialized, the screen is refreshed +and all occurrences of that color-pair are changed to the new +definition. [clinic start generated code]*/ static PyObject * _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg) -/*[clinic end generated code: output=a0bba03d2bbc3ee6 input=54b421b44c12c389]*/ +/*[clinic end generated code: output=a0bba03d2bbc3ee6 input=5486c3a105130dae]*/ { PyCursesStatefulInitialised(module); PyCursesStatefulInitialisedColor(module); @@ -3891,14 +3917,14 @@ _curses.get_escdelay Gets the curses ESCDELAY setting. -Gets the number of milliseconds to wait after reading an escape character, -to distinguish between an individual escape character entered on the -keyboard from escape sequences sent by cursor and function keys. +Gets the number of milliseconds to wait after reading an escape +character, to distinguish between an individual escape character entered +on the keyboard from escape sequences sent by cursor and function keys. [clinic start generated code]*/ static PyObject * _curses_get_escdelay_impl(PyObject *module) -/*[clinic end generated code: output=222fa1a822555d60 input=be2d5b3dd974d0a4]*/ +/*[clinic end generated code: output=222fa1a822555d60 input=b39eeae4b8f169ab]*/ { return PyLong_FromLong(ESCDELAY); } @@ -3910,14 +3936,14 @@ _curses.set_escdelay Sets the curses ESCDELAY setting. -Sets the number of milliseconds to wait after reading an escape character, -to distinguish between an individual escape character entered on the -keyboard from escape sequences sent by cursor and function keys. +Sets the number of milliseconds to wait after reading an escape +character, to distinguish between an individual escape character entered +on the keyboard from escape sequences sent by cursor and function keys. [clinic start generated code]*/ static PyObject * _curses_set_escdelay_impl(PyObject *module, int ms) -/*[clinic end generated code: output=43818efbf7980ac4 input=7796fe19f111e250]*/ +/*[clinic end generated code: output=43818efbf7980ac4 input=cc2529bcdda3b06c]*/ { if (ms <= 0) { PyErr_SetString(PyExc_ValueError, "ms must be > 0"); @@ -3932,13 +3958,13 @@ _curses.get_tabsize Gets the curses TABSIZE setting. -Gets the number of columns used by the curses library when converting a tab -character to spaces as it adds the tab to a window. +Gets the number of columns used by the curses library when converting +a tab character to spaces as it adds the tab to a window. [clinic start generated code]*/ static PyObject * _curses_get_tabsize_impl(PyObject *module) -/*[clinic end generated code: output=7e9e51fb6126fbdf input=74af86bf6c9f5d7e]*/ +/*[clinic end generated code: output=7e9e51fb6126fbdf input=58bdaacb337c103b]*/ { return PyLong_FromLong(TABSIZE); } @@ -3950,13 +3976,13 @@ _curses.set_tabsize Sets the curses TABSIZE setting. -Sets the number of columns used by the curses library when converting a tab -character to spaces as it adds the tab to a window. +Sets the number of columns used by the curses library when converting +a tab character to spaces as it adds the tab to a window. [clinic start generated code]*/ static PyObject * _curses_set_tabsize_impl(PyObject *module, int size) -/*[clinic end generated code: output=c1de5a76c0daab1e input=78cba6a3021ad061]*/ +/*[clinic end generated code: output=c1de5a76c0daab1e input=34c1be9a78cd28a2]*/ { if (size <= 0) { PyErr_SetString(PyExc_ValueError, "size must be > 0"); @@ -3997,6 +4023,7 @@ NoArgTrueFalseFunctionBody(isendwin) #ifdef HAVE_CURSES_IS_TERM_RESIZED /*[clinic input] +@permit_long_summary _curses.is_term_resized nlines: int @@ -4010,7 +4037,7 @@ Return True if resize_term() would modify the window structure, False otherwise. static PyObject * _curses_is_term_resized_impl(PyObject *module, int nlines, int ncols) -/*[clinic end generated code: output=aafe04afe50f1288 input=ca9c0bd0fb8ab444]*/ +/*[clinic end generated code: output=aafe04afe50f1288 input=5792a3f40cecb010]*/ { PyCursesStatefulInitialised(module); @@ -4067,13 +4094,13 @@ _curses.longname Return the terminfo long name field describing the current terminal. -The maximum length of a verbose description is 128 characters. It is defined -only after the call to initscr(). +The maximum length of a verbose description is 128 characters. It is +defined only after the call to initscr(). [clinic start generated code]*/ static PyObject * _curses_longname_impl(PyObject *module) -/*[clinic end generated code: output=fdf30433727ef568 input=84c3f20201b1098e]*/ +/*[clinic end generated code: output=fdf30433727ef568 input=a924fabba0de78a6]*/ NoArgReturnStringFunctionBody(longname) /*[clinic input] @@ -4108,13 +4135,13 @@ _curses.mouseinterval Set and retrieve the maximum time between press and release in a click. Set the maximum time that can elapse between press and release events in -order for them to be recognized as a click, and return the previous interval -value. +order for them to be recognized as a click, and return the previous +interval value. [clinic start generated code]*/ static PyObject * _curses_mouseinterval_impl(PyObject *module, int interval) -/*[clinic end generated code: output=c4f5ff04354634c5 input=75aaa3f0db10ac4e]*/ +/*[clinic end generated code: output=c4f5ff04354634c5 input=b90249254389c080]*/ { PyCursesStatefulInitialised(module); int value = mouseinterval(interval); @@ -4126,6 +4153,7 @@ _curses_mouseinterval_impl(PyObject *module, int interval) } /*[clinic input] +@permit_long_summary _curses.mousemask newmask: unsigned_long(bitwise=True) @@ -4134,14 +4162,15 @@ _curses.mousemask Set the mouse events to be reported, and return a tuple (availmask, oldmask). Return a tuple (availmask, oldmask). availmask indicates which of the -specified mouse events can be reported; on complete failure it returns 0. -oldmask is the previous value of the given window's mouse event mask. -If this function is never called, no mouse events are ever reported. +specified mouse events can be reported; on complete failure it returns +0. oldmask is the previous value of the given window's mouse event +mask. If this function is never called, no mouse events are ever +reported. [clinic start generated code]*/ static PyObject * _curses_mousemask_impl(PyObject *module, unsigned long newmask) -/*[clinic end generated code: output=9406cf1b8a36e485 input=bdf76b7568a3c541]*/ +/*[clinic end generated code: output=9406cf1b8a36e485 input=78990ec6c52aa888]*/ { mmask_t oldmask, availmask; @@ -4223,14 +4252,14 @@ _curses.newwin Return a new window. -By default, the window will extend from the specified position to the lower -right corner of the screen. +By default, the window will extend from the specified position to the +lower right corner of the screen. [clinic start generated code]*/ static PyObject * _curses_newwin_impl(PyObject *module, int nlines, int ncols, int group_right_1, int begin_y, int begin_x) -/*[clinic end generated code: output=c1e0a8dc8ac2826c input=29312c15a72a003d]*/ +/*[clinic end generated code: output=c1e0a8dc8ac2826c input=a1517cbfea4ab24b]*/ { WINDOW *win; @@ -4255,13 +4284,14 @@ _curses.nl Enter newline mode. -This mode translates the return key into newline on input, and translates -newline into return and line-feed on output. Newline mode is initially on. +This mode translates the return key into newline on input, and +translates newline into return and line-feed on output. Newline mode +is initially on. [clinic start generated code]*/ static PyObject * _curses_nl_impl(PyObject *module, int flag) -/*[clinic end generated code: output=b39cc0ffc9015003 input=18e3e9c6e8cfcf6f]*/ +/*[clinic end generated code: output=b39cc0ffc9015003 input=3fb21dcf55521ee4]*/ NoArgOrFlagNoReturnFunctionBody(nl, flag) /*[clinic input] @@ -4295,13 +4325,13 @@ _curses.nonl Leave newline mode. -Disable translation of return into newline on input, and disable low-level -translation of newline into newline/return on output. +Disable translation of return into newline on input, and disable +low-level translation of newline into newline/return on output. [clinic start generated code]*/ static PyObject * _curses_nonl_impl(PyObject *module) -/*[clinic end generated code: output=99e917e9715770c6 input=9d37dd122d3022fc]*/ +/*[clinic end generated code: output=99e917e9715770c6 input=75cce08e4b6b3ef1]*/ NoArgNoReturnFunctionBody(nonl) /*[clinic input] @@ -4332,6 +4362,7 @@ _curses_noraw_impl(PyObject *module) NoArgNoReturnFunctionBody(noraw) /*[clinic input] +@permit_long_summary _curses.pair_content pair_number: pair @@ -4343,7 +4374,7 @@ Return a tuple (fg, bg) containing the colors for the requested color pair. static PyObject * _curses_pair_content_impl(PyObject *module, int pair_number) -/*[clinic end generated code: output=4a726dd0e6885f3f input=03970f840fc7b739]*/ +/*[clinic end generated code: output=4a726dd0e6885f3f input=faede9e26f1f2ca4]*/ { _CURSES_COLOR_NUM_TYPE f, b; @@ -4367,6 +4398,7 @@ _curses_pair_content_impl(PyObject *module, int pair_number) } /*[clinic input] +@permit_long_summary _curses.pair_number attr: int @@ -4379,7 +4411,7 @@ color_pair() is the counterpart to this function. static PyObject * _curses_pair_number_impl(PyObject *module, int attr) -/*[clinic end generated code: output=85bce7d65c0aa3f4 input=d478548e33f5e61a]*/ +/*[clinic end generated code: output=85bce7d65c0aa3f4 input=b11152a78c2f9abf]*/ { PyCursesStatefulInitialised(module); PyCursesStatefulInitialisedColor(module); @@ -4388,6 +4420,7 @@ _curses_pair_number_impl(PyObject *module, int attr) } /*[clinic input] +@permit_long_summary _curses.putp string: str(accept={robuffer}) @@ -4400,7 +4433,7 @@ Note that the output of putp() always goes to standard output. static PyObject * _curses_putp_impl(PyObject *module, const char *string) -/*[clinic end generated code: output=e98081d1b8eb5816 input=1601faa828b44cb3]*/ +/*[clinic end generated code: output=e98081d1b8eb5816 input=2f3b9e0f22829ee7]*/ { return curses_check_err(module, putp(string), "putp", NULL); } @@ -4513,16 +4546,17 @@ _curses.raw Enter raw mode. In raw mode, normal line buffering and processing of interrupt, quit, -suspend, and flow control keys are turned off; characters are presented to -curses input functions one by one. +suspend, and flow control keys are turned off; characters are presented +to curses input functions one by one. [clinic start generated code]*/ static PyObject * _curses_raw_impl(PyObject *module, int flag) -/*[clinic end generated code: output=a750e4b342be015b input=4b447701389fb4df]*/ +/*[clinic end generated code: output=a750e4b342be015b input=18a7de7eef16987a]*/ NoArgOrFlagNoReturnFunctionBody(raw, flag) /*[clinic input] +@permit_long_summary _curses.reset_prog_mode Restore the terminal to "program" mode, as previously saved by def_prog_mode(). @@ -4530,10 +4564,11 @@ Restore the terminal to "program" mode, as previously saved by def_prog_mode(). static PyObject * _curses_reset_prog_mode_impl(PyObject *module) -/*[clinic end generated code: output=15eb765abf0b6575 input=3d82bea2b3243471]*/ +/*[clinic end generated code: output=15eb765abf0b6575 input=a8b44b5261c8cf3a]*/ NoArgNoReturnFunctionBody(reset_prog_mode) /*[clinic input] +@permit_long_summary _curses.reset_shell_mode Restore the terminal to "shell" mode, as previously saved by def_shell_mode(). @@ -4541,7 +4576,7 @@ Restore the terminal to "shell" mode, as previously saved by def_shell_mode(). static PyObject * _curses_reset_shell_mode_impl(PyObject *module) -/*[clinic end generated code: output=0238de2962090d33 input=1c738fa64bd1a24f]*/ +/*[clinic end generated code: output=0238de2962090d33 input=f5224034a2c95931]*/ NoArgNoReturnFunctionBody(reset_shell_mode) /*[clinic input] @@ -4567,13 +4602,13 @@ _curses.resizeterm Resize the standard and current windows to the specified dimensions. -Adjusts other bookkeeping data used by the curses library that record the -window dimensions (in particular the SIGWINCH handler). +Adjusts other bookkeeping data used by the curses library that record +the window dimensions (in particular the SIGWINCH handler). [clinic start generated code]*/ static PyObject * _curses_resizeterm_impl(PyObject *module, short nlines, short ncols) -/*[clinic end generated code: output=4de3abab50c67f02 input=414e92a63e3e9899]*/ +/*[clinic end generated code: output=4de3abab50c67f02 input=7f0f077df2da1cf5]*/ { PyObject *result; int code; @@ -4606,15 +4641,16 @@ _curses.resize_term Backend function used by resizeterm(), performing most of the work. When resizing the windows, resize_term() blank-fills the areas that are -extended. The calling application should fill in these areas with appropriate -data. The resize_term() function attempts to resize all windows. However, -due to the calling convention of pads, it is not possible to resize these -without additional interaction with the application. +extended. The calling application should fill in these areas with +appropriate data. The resize_term() function attempts to resize all +windows. However, due to the calling convention of pads, it is not +possible to resize these without additional interaction with the +application. [clinic start generated code]*/ static PyObject * _curses_resize_term_impl(PyObject *module, short nlines, short ncols) -/*[clinic end generated code: output=46c6d749fa291dbd input=276afa43d8ea7091]*/ +/*[clinic end generated code: output=46c6d749fa291dbd input=ff4baaf2320c8ac9]*/ { PyObject *result; int code; @@ -4672,21 +4708,22 @@ _curses_setsyx_impl(PyObject *module, int y, int x) #endif /*[clinic input] +@permit_long_summary _curses.start_color Initializes eight basic colors and global variables COLORS and COLOR_PAIRS. -Must be called if the programmer wants to use colors, and before any other -color manipulation routine is called. It is good practice to call this -routine right after initscr(). +Must be called if the programmer wants to use colors, and before any +other color manipulation routine is called. It is good practice to call +this routine right after initscr(). -It also restores the colors on the terminal to the values they had when the -terminal was just turned on. +It also restores the colors on the terminal to the values they had when +the terminal was just turned on. [clinic start generated code]*/ static PyObject * _curses_start_color_impl(PyObject *module) -/*[clinic end generated code: output=8b772b41d8090ede input=0ca0ecb2b77e1a12]*/ +/*[clinic end generated code: output=8b772b41d8090ede input=7daacc6b6baba643]*/ { PyCursesStatefulInitialised(module); @@ -4733,6 +4770,7 @@ _curses_termattrs_impl(PyObject *module) NoArgReturnIntFunctionBody(termattrs) /*[clinic input] +@permit_long_summary _curses.termname Return the value of the environment variable TERM, truncated to 14 characters. @@ -4740,7 +4778,7 @@ Return the value of the environment variable TERM, truncated to 14 characters. static PyObject * _curses_termname_impl(PyObject *module) -/*[clinic end generated code: output=96375577ebbd67fd input=33c08d000944f33f]*/ +/*[clinic end generated code: output=96375577ebbd67fd input=c34f724d8ce8fc4e]*/ NoArgReturnStringFunctionBody(termname) /*[clinic input] @@ -4774,13 +4812,13 @@ _curses.tigetnum Return the value of the numeric capability. -The value -2 is returned if capname is not a numeric capability, or -1 if -it is canceled or absent from the terminal description. +The value -2 is returned if capname is not a numeric capability, or -1 +if it is canceled or absent from the terminal description. [clinic start generated code]*/ static PyObject * _curses_tigetnum_impl(PyObject *module, const char *capname) -/*[clinic end generated code: output=46f8b0a1b5dff42f input=5cdf2f410b109720]*/ +/*[clinic end generated code: output=46f8b0a1b5dff42f input=87a64beec16ae077]*/ { PyCursesStatefulSetupTermCalled(module); @@ -4796,13 +4834,13 @@ _curses.tigetstr Return the value of the string capability. -None is returned if capname is not a string capability, or is canceled or -absent from the terminal description. +None is returned if capname is not a string capability, or is canceled +or absent from the terminal description. [clinic start generated code]*/ static PyObject * _curses_tigetstr_impl(PyObject *module, const char *capname) -/*[clinic end generated code: output=f22b576ad60248f3 input=36644df25c73c0a7]*/ +/*[clinic end generated code: output=f22b576ad60248f3 input=00bf0feda2207724]*/ { PyCursesStatefulSetupTermCalled(module); @@ -5007,19 +5045,19 @@ _curses.use_env Use environment variables LINES and COLUMNS. -If used, this function should be called before initscr() or newterm() are -called. +If used, this function should be called before initscr() or newterm() +are called. -When flag is False, the values of lines and columns specified in the terminfo -database will be used, even if environment variables LINES and COLUMNS (used -by default) are set, or if curses is running in a window (in which case -default behavior would be to use the window size if LINES and COLUMNS are -not set). +When flag is False, the values of lines and columns specified in the +terminfo database will be used, even if environment variables LINES and +COLUMNS (used by default) are set, or if curses is running in a window +(in which case default behavior would be to use the window size if LINES +and COLUMNS are not set). [clinic start generated code]*/ static PyObject * _curses_use_env_impl(PyObject *module, int flag) -/*[clinic end generated code: output=b2c445e435c0b164 input=06ac30948f2d78e4]*/ +/*[clinic end generated code: output=b2c445e435c0b164 input=8e8feed746cf7fc1]*/ { use_env(flag); Py_RETURN_NONE; @@ -5047,6 +5085,7 @@ _curses_use_default_colors_impl(PyObject *module) } /*[clinic input] +@permit_long_summary _curses.assume_default_colors fg: int bg: int @@ -5062,7 +5101,7 @@ Use this to support transparency in your application. static PyObject * _curses_assume_default_colors_impl(PyObject *module, int fg, int bg) -/*[clinic end generated code: output=54985397a7d2b3a5 input=7fe301712ef3e9fb]*/ +/*[clinic end generated code: output=54985397a7d2b3a5 input=8945333c09893cf2]*/ { int code; @@ -5131,6 +5170,7 @@ make_ncurses_version(PyTypeObject *type) #endif /* NCURSES_VERSION */ /*[clinic input] +@permit_long_summary _curses.has_extended_color_support Return True if the module supports extended colors; otherwise, return False. @@ -5141,7 +5181,7 @@ that support more than 16 colors (e.g. xterm-256color). static PyObject * _curses_has_extended_color_support_impl(PyObject *module) -/*[clinic end generated code: output=68f1be2b57d92e22 input=4b905f046e35ee9f]*/ +/*[clinic end generated code: output=68f1be2b57d92e22 input=40d673471c5056f0]*/ { return PyBool_FromLong(_NCURSES_EXTENDED_COLOR_FUNCS); } @@ -5600,6 +5640,7 @@ cursesmodule_exec(PyObject *module) /* Initialization function for the module */ static PyModuleDef_Slot cursesmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, cursesmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 01039dfeec0719..82b1f898a3c674 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -13,8 +13,10 @@ #include "pycore_long.h" // _PyLong_GetOne() #include "pycore_object.h" // _PyObject_Init() #include "pycore_time.h" // _PyTime_ObjectToTime_t() +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_unicodeobject.h" // _PyUnicode_Copy() #include "pycore_initconfig.h" // _PyStatus_OK() +#include "pycore_pyatomic_ft_wrappers.h" #include "datetime.h" @@ -124,8 +126,8 @@ get_module_state(PyObject *module) #define INTERP_KEY ((PyObject *)&_Py_ID(cached_datetime_module)) -static PyObject * -get_current_module(PyInterpreterState *interp) +static int +get_current_module(PyInterpreterState *interp, PyObject **p_mod) { PyObject *mod = NULL; @@ -137,20 +139,24 @@ get_current_module(PyInterpreterState *interp) if (PyDict_GetItemRef(dict, INTERP_KEY, &ref) < 0) { goto error; } - if (ref != NULL) { - if (ref != Py_None) { - (void)PyWeakref_GetRef(ref, &mod); - if (mod == Py_None) { - Py_CLEAR(mod); - } + if (ref != NULL && ref != Py_None) { + if (PyWeakref_GetRef(ref, &mod) < 0) { Py_DECREF(ref); + goto error; } + if (mod == Py_None) { + Py_CLEAR(mod); + } + Py_DECREF(ref); } - return mod; + assert(!PyErr_Occurred()); + *p_mod = mod; + return mod != NULL; error: assert(PyErr_Occurred()); - return NULL; + *p_mod = NULL; + return -1; } static PyModuleDef datetimemodule; @@ -159,22 +165,26 @@ static datetime_state * _get_current_state(PyObject **p_mod) { PyInterpreterState *interp = PyInterpreterState_Get(); - PyObject *mod = get_current_module(interp); + PyObject *mod; + if (get_current_module(interp, &mod) < 0) { + goto error; + } if (mod == NULL) { - assert(!PyErr_Occurred()); - if (PyErr_Occurred()) { - return NULL; - } /* The static types can outlive the module, * so we must re-import the module. */ mod = PyImport_ImportModule("_datetime"); if (mod == NULL) { - return NULL; + goto error; } } datetime_state *st = get_module_state(mod); *p_mod = mod; return st; + +error: + assert(PyErr_Occurred()); + *p_mod = NULL; + return NULL; } #define GET_CURRENT_STATE(MOD_VAR) \ @@ -214,7 +224,7 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected) if (PyDict_GetItemRef(dict, INTERP_KEY, &ref) < 0) { goto error; } - if (ref != NULL) { + if (ref != NULL && ref != Py_None) { PyObject *current = NULL; int rc = PyWeakref_GetRef(ref, &current); /* We only need "current" for pointer comparison. */ @@ -331,8 +341,10 @@ class datetime.datetime "PyDateTime_DateTime *" "get_datetime_state()->datetime_ class datetime.date "PyDateTime_Date *" "get_datetime_state()->date_type" class datetime.time "PyDateTime_Time *" "get_datetime_state()->time_type" class datetime.IsoCalendarDate "PyDateTime_IsoCalendarDate *" "get_datetime_state()->isocalendar_date_type" +class datetime.timedelta "PyDateTime_Delta *" "&PyDateTime_DeltaType" +class datetime.timezone "PyDateTime_TimeZone *" "&PyDateTime_TimeZoneType" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c8f3d834a860d50a]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c54b9adf60082f0d]*/ #include "clinic/_datetimemodule.c.h" @@ -1171,19 +1183,18 @@ new_datetime_ex(int, int, int, int, int, int, int, PyObject *, PyTypeObject *); /* Create date instance with no range checking, or call subclass constructor */ static PyObject * -new_date_subclass_ex(int year, int month, int day, PyObject *cls) +new_date_subclass_ex(int year, int month, int day, PyTypeObject *cls) { PyObject *result; // We have "fast path" constructors for two subclasses: date and datetime - if ((PyTypeObject *)cls == DATE_TYPE(NO_STATE)) { - result = new_date_ex(year, month, day, (PyTypeObject *)cls); + if (cls == DATE_TYPE(NO_STATE)) { + result = new_date_ex(year, month, day, cls); } - else if ((PyTypeObject *)cls == DATETIME_TYPE(NO_STATE)) { - result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None, - (PyTypeObject *)cls); + else if (cls == DATETIME_TYPE(NO_STATE)) { + result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None, cls); } else { - result = PyObject_CallFunction(cls, "iii", year, month, day); + result = PyObject_CallFunction((PyObject *)cls, "iii", year, month, day); } return result; @@ -1235,7 +1246,7 @@ new_datetime_ex(int year, int month, int day, int hour, int minute, new_datetime_ex2(y, m, d, hh, mm, ss, us, tzinfo, fold, DATETIME_TYPE(NO_STATE)) static PyObject * -call_subclass_fold(PyObject *cls, int fold, const char *format, ...) +call_subclass_fold(PyTypeObject *cls, int fold, const char *format, ...) { PyObject *kwargs = NULL, *res = NULL; va_list va; @@ -1261,7 +1272,7 @@ call_subclass_fold(PyObject *cls, int fold, const char *format, ...) goto Done; } } - res = PyObject_Call(cls, args, kwargs); + res = PyObject_Call((PyObject *)cls, args, kwargs); Done: Py_DECREF(args); Py_XDECREF(kwargs); @@ -1271,10 +1282,10 @@ call_subclass_fold(PyObject *cls, int fold, const char *format, ...) static PyObject * new_datetime_subclass_fold_ex(int year, int month, int day, int hour, int minute, int second, int usecond, PyObject *tzinfo, - int fold, PyObject *cls) + int fold, PyTypeObject *cls) { PyObject* dt; - if ((PyTypeObject*)cls == DATETIME_TYPE(NO_STATE)) { + if (cls == DATETIME_TYPE(NO_STATE)) { // Use the fast path constructor dt = new_datetime(year, month, day, hour, minute, second, usecond, tzinfo, fold); @@ -1291,7 +1302,7 @@ new_datetime_subclass_fold_ex(int year, int month, int day, int hour, int minute static PyObject * new_datetime_subclass_ex(int year, int month, int day, int hour, int minute, int second, int usecond, PyObject *tzinfo, - PyObject *cls) { + PyTypeObject *cls) { return new_datetime_subclass_fold_ex(year, month, day, hour, minute, second, usecond, tzinfo, 0, cls); @@ -1340,10 +1351,10 @@ new_time_ex(int hour, int minute, int second, int usecond, static PyObject * new_time_subclass_fold_ex(int hour, int minute, int second, int usecond, - PyObject *tzinfo, int fold, PyObject *cls) + PyObject *tzinfo, int fold, PyTypeObject *cls) { PyObject *t; - if ((PyTypeObject*)cls == TIME_TYPE(NO_STATE)) { + if (cls == TIME_TYPE(NO_STATE)) { // Use the fast path constructor t = new_time(hour, minute, second, usecond, tzinfo, fold); } @@ -1506,7 +1517,7 @@ get_tzinfo_member(PyObject *self) * this returns NULL. Else result is returned. */ static PyObject * -call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) +call_tzinfo_method(PyObject *tzinfo, PyObject *name, PyObject *tzinfoarg) { PyObject *offset; @@ -1516,7 +1527,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) if (tzinfo == Py_None) Py_RETURN_NONE; - offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg); + offset = PyObject_CallMethodOneArg(tzinfo, name, tzinfoarg); if (offset == Py_None || offset == NULL) return offset; if (PyDelta_Check(offset)) { @@ -1533,7 +1544,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) } else { PyErr_Format(PyExc_TypeError, - "tzinfo.%s() must return None or " + "tzinfo.%U() must return None or " "timedelta, not '%.200s'", name, Py_TYPE(offset)->tp_name); Py_DECREF(offset); @@ -1554,7 +1565,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) static PyObject * call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg) { - return call_tzinfo_method(tzinfo, "utcoffset", tzinfoarg); + return call_tzinfo_method(tzinfo, &_Py_ID(utcoffset), tzinfoarg); } /* Call tzinfo.dst(tzinfoarg), and extract an integer from the @@ -1568,7 +1579,7 @@ call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg) static PyObject * call_dst(PyObject *tzinfo, PyObject *tzinfoarg) { - return call_tzinfo_method(tzinfo, "dst", tzinfoarg); + return call_tzinfo_method(tzinfo, &_Py_ID(dst), tzinfoarg); } /* Call tzinfo.tzname(tzinfoarg), and return the result. tzinfo must be @@ -1759,6 +1770,24 @@ format_utcoffset(char *buf, size_t buflen, const char *sep, return 0; } +/* Check whether year with century should be normalized for strftime. */ +inline static int +normalize_century(void) +{ + static int cache = -1; + if (cache < 0) { + char year[5]; + struct tm date = { + .tm_year = -1801, + .tm_mon = 0, + .tm_mday = 1 + }; + cache = (strftime(year, sizeof(year), "%Y", &date) && + strcmp(year, "0099") != 0); + } + return cache; +} + static PyObject * make_somezreplacement(PyObject *object, char *sep, PyObject *tzinfoarg) { @@ -1930,10 +1959,9 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, } replacement = freplacement; } -#ifdef _Py_NORMALIZE_CENTURY - else if (ch == 'Y' || ch == 'G' - || ch == 'F' || ch == 'C' - ) { + else if (normalize_century() + && (ch == 'Y' || ch == 'G' || ch == 'F' || ch == 'C')) + { /* 0-pad year with century as necessary */ PyObject *item = PySequence_GetItem(timetuple, 0); if (item == NULL) { @@ -1984,7 +2012,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, } continue; } -#endif else { /* percent followed by something else */ continue; @@ -2109,8 +2136,11 @@ delta_to_microseconds(PyDateTime_Delta *self) PyObject *x3 = NULL; PyObject *result = NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } x1 = PyLong_FromLong(GET_TD_DAYS(self)); if (x1 == NULL) @@ -2188,8 +2218,11 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) PyObject *num = NULL; PyObject *result = NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } tuple = checked_divmod(pyus, CONST_US_PER_SECOND(st)); if (tuple == NULL) { @@ -2523,14 +2556,16 @@ static Py_hash_t delta_hash(PyObject *op) { PyDateTime_Delta *self = PyDelta_CAST(op); - if (self->hashcode == -1) { + Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->hashcode); + if (hash == -1) { PyObject *temp = delta_getstate(self); if (temp != NULL) { - self->hashcode = PyObject_Hash(temp); + hash = PyObject_Hash(temp); + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); Py_DECREF(temp); } } - return self->hashcode; + return hash; } static PyObject * @@ -2672,7 +2707,7 @@ delta_divmod(PyObject *left, PyObject *right) Py_DECREF(divmod); return NULL; } - result = PyTuple_Pack(2, PyTuple_GET_ITEM(divmod, 0), delta); + result = _PyTuple_FromPair(PyTuple_GET_ITEM(divmod, 0), delta); Py_DECREF(delta); Py_DECREF(divmod); return result; @@ -2768,38 +2803,42 @@ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor, return NULL; } +/*[clinic input] +@classmethod +datetime.timedelta.__new__ as delta_new + + days: object(c_default="NULL") = 0 + seconds: object(c_default="NULL") = 0 + microseconds: object(c_default="NULL") = 0 + milliseconds: object(c_default="NULL") = 0 + minutes: object(c_default="NULL") = 0 + hours: object(c_default="NULL") = 0 + weeks: object(c_default="NULL") = 0 + +Difference between two datetime values. + +All arguments are optional and default to 0. +Arguments may be integers or floats, and may be positive or negative. +[clinic start generated code]*/ + static PyObject * -delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) +delta_new_impl(PyTypeObject *type, PyObject *days, PyObject *seconds, + PyObject *microseconds, PyObject *milliseconds, + PyObject *minutes, PyObject *hours, PyObject *weeks) +/*[clinic end generated code: output=61d7e02a92a97700 input=e8cd54819295d34b]*/ { PyObject *self = NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); - - /* Argument objects. */ - PyObject *day = NULL; - PyObject *second = NULL; - PyObject *us = NULL; - PyObject *ms = NULL; - PyObject *minute = NULL; - PyObject *hour = NULL; - PyObject *week = NULL; + if (st == NULL) { + return NULL; + } PyObject *x = NULL; /* running sum of microseconds */ PyObject *y = NULL; /* temp sum of microseconds */ double leftover_us = 0.0; - static char *keywords[] = { - "days", "seconds", "microseconds", "milliseconds", - "minutes", "hours", "weeks", NULL - }; - - if (PyArg_ParseTupleAndKeywords(args, kw, "|OOOOOOO:__new__", - keywords, - &day, &second, &us, - &ms, &minute, &hour, &week) == 0) - goto Done; - x = PyLong_FromLong(0); if (x == NULL) goto Done; @@ -2810,32 +2849,32 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (x == NULL) \ goto Done - if (us) { - y = accum("microseconds", x, us, _PyLong_GetOne(), &leftover_us); + if (microseconds) { + y = accum("microseconds", x, microseconds, _PyLong_GetOne(), &leftover_us); CLEANUP; } - if (ms) { - y = accum("milliseconds", x, ms, CONST_US_PER_MS(st), &leftover_us); + if (milliseconds) { + y = accum("milliseconds", x, milliseconds, CONST_US_PER_MS(st), &leftover_us); CLEANUP; } - if (second) { - y = accum("seconds", x, second, CONST_US_PER_SECOND(st), &leftover_us); + if (seconds) { + y = accum("seconds", x, seconds, CONST_US_PER_SECOND(st), &leftover_us); CLEANUP; } - if (minute) { - y = accum("minutes", x, minute, CONST_US_PER_MINUTE(st), &leftover_us); + if (minutes) { + y = accum("minutes", x, minutes, CONST_US_PER_MINUTE(st), &leftover_us); CLEANUP; } - if (hour) { - y = accum("hours", x, hour, CONST_US_PER_HOUR(st), &leftover_us); + if (hours) { + y = accum("hours", x, hours, CONST_US_PER_HOUR(st), &leftover_us); CLEANUP; } - if (day) { - y = accum("days", x, day, CONST_US_PER_DAY(st), &leftover_us); + if (days) { + y = accum("days", x, days, CONST_US_PER_DAY(st), &leftover_us); CLEANUP; } - if (week) { - y = accum("weeks", x, week, CONST_US_PER_WEEK(st), &leftover_us); + if (weeks) { + y = accum("weeks", x, weeks, CONST_US_PER_WEEK(st), &leftover_us); CLEANUP; } if (leftover_us) { @@ -2992,8 +3031,12 @@ delta_total_seconds(PyObject *op, PyObject *Py_UNUSED(dummy)) if (total_microseconds == NULL) return NULL; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + Py_DECREF(total_microseconds); + return NULL; + } total_seconds = PyNumber_TrueDivide(total_microseconds, CONST_US_PER_SECOND(st)); @@ -3034,13 +3077,6 @@ static PyMethodDef delta_methods[] = { {NULL, NULL}, }; -static const char delta_doc[] = -PyDoc_STR("Difference between two datetime values.\n\n" - "timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, " - "minutes=0, hours=0, weeks=0)\n\n" - "All arguments are optional and default to 0.\n" - "Arguments may be integers or floats, and may be positive or negative."); - static PyNumberMethods delta_as_number = { delta_add, /* nb_add */ delta_subtract, /* nb_subtract */ @@ -3098,7 +3134,7 @@ static PyTypeObject PyDateTime_DeltaType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - delta_doc, /* tp_doc */ + delta_new__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ delta_richcompare, /* tp_richcompare */ @@ -3174,8 +3210,6 @@ static PyGetSetDef date_getset[] = { /* Constructors. */ -static char *date_kws[] = {"year", "month", "day", NULL}; - static PyObject * date_from_pickle(PyTypeObject *type, PyObject *state) { @@ -3193,11 +3227,6 @@ date_from_pickle(PyTypeObject *type, PyObject *state) static PyObject * date_new(PyTypeObject *type, PyObject *args, PyObject *kw) { - PyObject *self = NULL; - int year; - int month; - int day; - /* Check for invocation from pickle with __getstate__ state */ if (PyTuple_GET_SIZE(args) == 1) { PyObject *state = PyTuple_GET_ITEM(args, 0); @@ -3223,22 +3252,36 @@ date_new(PyTypeObject *type, PyObject *args, PyObject *kw) } return NULL; } - self = date_from_pickle(type, state); + PyObject *self = date_from_pickle(type, state); Py_DECREF(state); return self; } } } - if (PyArg_ParseTupleAndKeywords(args, kw, "iii", date_kws, - &year, &month, &day)) { - self = new_date_ex(year, month, day, type); - } - return self; + return datetime_date(type, args, kw); +} + +/*[clinic input] +@classmethod +datetime.date.__new__ + + year: int + month: int + day: int + +Concrete date type. +[clinic start generated code]*/ + +static PyObject * +datetime_date_impl(PyTypeObject *type, int year, int month, int day) +/*[clinic end generated code: output=6654caa3dea7d518 input=fd1bac0658690455]*/ +{ + return new_date_ex(year, month, day, type); } static PyObject * -date_fromtimestamp(PyObject *cls, PyObject *obj) +date_fromtimestamp(PyTypeObject *cls, PyObject *obj) { struct tm tm; time_t t; @@ -3260,22 +3303,44 @@ date_fromtimestamp(PyObject *cls, PyObject *obj) * only way to be sure of that is to *call* time.time(). That's not * generally the same as calling C's time. */ +/*[clinic input] +@classmethod +datetime.date.today + +Current date or datetime. + +Equivalent to fromtimestamp(time.time()). +[clinic start generated code]*/ + static PyObject * -date_today(PyObject *cls, PyObject *Py_UNUSED(dummy)) +datetime_date_today_impl(PyTypeObject *type) +/*[clinic end generated code: output=d5474697df6b251c input=21688afa289c0a06]*/ { - PyObject *time; - PyObject *result; - time = time_time(); - if (time == NULL) + /* Use C implementation to boost performance for date type */ + if (type == &PyDateTime_DateType) { + struct tm tm; + time_t t; + time(&t); + + if (_PyTime_localtime(t, &tm) != 0) { + return NULL; + } + + return new_date_ex(tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + type); + } + + PyObject *time = time_time(); + if (time == NULL) { return NULL; + } - /* Note well: today() is a class method, so this may not call - * date.fromtimestamp. For example, it may call - * datetime.fromtimestamp. That's why we need all the accuracy - * time.time() delivers; if someone were gonzo about optimization, - * date.today() could get away with plain C time(). + /* Note well: since today() is a class method, it may not call + * date.fromtimestamp, e.g., it may call datetime.fromtimestamp. */ - result = PyObject_CallMethodOneArg(cls, &_Py_ID(fromtimestamp), time); + PyObject *result = PyObject_CallMethodOneArg((PyObject*)type, &_Py_ID(fromtimestamp), time); Py_DECREF(time); return result; } @@ -3289,15 +3354,15 @@ datetime.date.fromtimestamp Create a date from a POSIX timestamp. -The timestamp is a number, e.g. created via time.time(), that is interpreted -as local time. +The timestamp is a number, e.g. created via time.time(), that is +interpreted as local time. [clinic start generated code]*/ static PyObject * datetime_date_fromtimestamp_impl(PyTypeObject *type, PyObject *timestamp) -/*[clinic end generated code: output=59def4e32c028fb6 input=eabb3fe7f40491fe]*/ +/*[clinic end generated code: output=59def4e32c028fb6 input=15720eef43b169a1]*/ { - return date_fromtimestamp((PyObject *) type, timestamp); + return date_fromtimestamp(type, timestamp); } /* bpo-36025: This is a wrapper for API compatibility with the public C API, @@ -3311,52 +3376,58 @@ datetime_date_fromtimestamp_capi(PyObject *cls, PyObject *args) PyObject *result = NULL; if (PyArg_UnpackTuple(args, "fromtimestamp", 1, 1, &timestamp)) { - result = date_fromtimestamp(cls, timestamp); + result = date_fromtimestamp((PyTypeObject *)cls, timestamp); } return result; } -/* Return new date from proleptic Gregorian ordinal. Raises ValueError if - * the ordinal is out of range. - */ -static PyObject * -date_fromordinal(PyObject *cls, PyObject *args) -{ - PyObject *result = NULL; - int ordinal; +/*[clinic input] +@classmethod +datetime.date.fromordinal - if (PyArg_ParseTuple(args, "i:fromordinal", &ordinal)) { - int year; - int month; - int day; + ordinal: int + / - if (ordinal < 1) - PyErr_SetString(PyExc_ValueError, "ordinal must be " - ">= 1"); - else { - ord_to_ymd(ordinal, &year, &month, &day); - result = new_date_subclass_ex(year, month, day, cls); - } - } - return result; -} +Construct a date from a proleptic Gregorian ordinal. + +January 1 of year 1 is day 1. Only the year, month and day are +non-zero in the result. +[clinic start generated code]*/ -/* Return the new date from a string as generated by date.isoformat() */ static PyObject * -date_fromisoformat(PyObject *cls, PyObject *dtstr) +datetime_date_fromordinal_impl(PyTypeObject *type, int ordinal) +/*[clinic end generated code: output=ea5cc69d86614a6b input=a3a4eedf582f145e]*/ { - assert(dtstr != NULL); + int year; + int month; + int day; - if (!PyUnicode_Check(dtstr)) { - PyErr_SetString(PyExc_TypeError, - "fromisoformat: argument must be str"); + if (ordinal < 1) { + PyErr_SetString(PyExc_ValueError, "ordinal must be >= 1"); return NULL; } + ord_to_ymd(ordinal, &year, &month, &day); + return new_date_subclass_ex(year, month, day, type); +} + +/*[clinic input] +@classmethod +datetime.date.fromisoformat + + string: unicode + / + +Construct a date from a string in ISO 8601 format. +[clinic start generated code]*/ +static PyObject * +datetime_date_fromisoformat_impl(PyTypeObject *type, PyObject *string) +/*[clinic end generated code: output=8b9f9324904fca02 input=73c64216c10bcc8e]*/ +{ Py_ssize_t len; - const char *dt_ptr = PyUnicode_AsUTF8AndSize(dtstr, &len); + const char *dt_ptr = PyUnicode_AsUTF8AndSize(string, &len); if (dt_ptr == NULL) { goto invalid_string_error; } @@ -3375,33 +3446,32 @@ date_fromisoformat(PyObject *cls, PyObject *dtstr) goto invalid_string_error; } - return new_date_subclass_ex(year, month, day, cls); + return new_date_subclass_ex(year, month, day, type); invalid_string_error: - PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", dtstr); + PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", string); return NULL; } -static PyObject * -date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw) -{ - static char *keywords[] = { - "year", "week", "day", NULL - }; +/*[clinic input] +@classmethod +datetime.date.fromisocalendar - int year, week, day; - if (PyArg_ParseTupleAndKeywords(args, kw, "iii:fromisocalendar", - keywords, - &year, &week, &day) == 0) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { - PyErr_Format(PyExc_ValueError, - "ISO calendar component out of range"); + year: int + week: int + day: int - } - return NULL; - } +Construct a date from the ISO year, week number and weekday. + +This is the inverse of the date.isocalendar() function. +[clinic start generated code]*/ +static PyObject * +datetime_date_fromisocalendar_impl(PyTypeObject *type, int year, int week, + int day) +/*[clinic end generated code: output=7b26e15115d24df6 input=fbb05b53d6fb51d8]*/ +{ int month; int rv = iso_to_ymd(year, week, day, &year, &month, &day); @@ -3422,26 +3492,38 @@ date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw) return NULL; } - return new_date_subclass_ex(year, month, day, cls); + return new_date_subclass_ex(year, month, day, type); } -/* Return new date from _strptime.strptime_datetime_date(). */ +/*[clinic input] +@permit_long_summary +@classmethod +datetime.date.strptime + + string: unicode + format: unicode + / + +Parse string according to the given date format (like time.strptime()). + +For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes +[clinic start generated code]*/ + static PyObject * -date_strptime(PyObject *cls, PyObject *args) +datetime_date_strptime_impl(PyTypeObject *type, PyObject *string, + PyObject *format) +/*[clinic end generated code: output=454d473bee2d5161 input=2db8f0b2b5242deb]*/ { - PyObject *string, *format, *result; - - if (!PyArg_ParseTuple(args, "UU:strptime", &string, &format)) { - return NULL; - } + PyObject *result; PyObject *module = PyImport_Import(&_Py_ID(_strptime)); if (module == NULL) { return NULL; } result = PyObject_CallMethodObjArgs(module, - &_Py_ID(_strptime_datetime_date), cls, - string, format, NULL); + &_Py_ID(_strptime_datetime_date), + (PyObject *)type, string, format, NULL); Py_DECREF(module); return result; } @@ -3465,8 +3547,7 @@ add_date_timedelta(PyDateTime_Date *date, PyDateTime_Delta *delta, int negate) int day = GET_DAY(date) + (negate ? -deltadays : deltadays); if (normalize_date(&year, &month, &day) >= 0) - result = new_date_subclass_ex(year, month, day, - (PyObject* )Py_TYPE(date)); + result = new_date_subclass_ex(year, month, day, Py_TYPE(date)); return result; } @@ -3558,20 +3639,29 @@ date_ctime(PyObject *self, PyObject *Py_UNUSED(dummy)) return format_ctime(self, 0, 0, 0); } +/*[clinic input] +datetime.date.strftime + + self: self(type="PyObject *") + format: unicode + +Format using strftime(). + +Example: "%d/%m/%Y, %H:%M:%S". + +For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes +[clinic start generated code]*/ + static PyObject * -date_strftime(PyObject *self, PyObject *args, PyObject *kw) +datetime_date_strftime_impl(PyObject *self, PyObject *format) +/*[clinic end generated code: output=6529b70095e16778 input=b6fd4a2ded27b557]*/ { /* This method can be inherited, and needs to call the * timetuple() method appropriate to self's class. */ PyObject *result; PyObject *tuple; - PyObject *format; - static char *keywords[] = {"format", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kw, "U:strftime", keywords, - &format)) - return NULL; tuple = PyObject_CallMethodNoArgs(self, &_Py_ID(timetuple)); if (tuple == NULL) @@ -3581,14 +3671,20 @@ date_strftime(PyObject *self, PyObject *args, PyObject *kw) return result; } -static PyObject * -date_format(PyObject *self, PyObject *args) -{ - PyObject *format; +/*[clinic input] +datetime.date.__format__ - if (!PyArg_ParseTuple(args, "U:__format__", &format)) - return NULL; + self: self(type="PyObject *") + format: unicode + / + +Formats self with strftime. +[clinic start generated code]*/ +static PyObject * +datetime_date___format___impl(PyObject *self, PyObject *format) +/*[clinic end generated code: output=efa0223d000a93b7 input=e417a7c84e1abaf9]*/ +{ /* if the format is zero length, return str(self) */ if (PyUnicode_GetLength(format) == 0) return PyObject_Str(self); @@ -3748,9 +3844,26 @@ iso_calendar_date_new_impl(PyTypeObject *type, int year, int week, return NULL; } - PyTuple_SET_ITEM(self, 0, PyLong_FromLong(year)); - PyTuple_SET_ITEM(self, 1, PyLong_FromLong(week)); - PyTuple_SET_ITEM(self, 2, PyLong_FromLong(weekday)); + PyObject *year_object = PyLong_FromLong(year); + if (year_object == NULL) { + Py_DECREF(self); + return NULL; + } + PyTuple_SET_ITEM(self, 0, year_object); + + PyObject *week_object = PyLong_FromLong(week); + if (week_object == NULL) { + Py_DECREF(self); + return NULL; + } + PyTuple_SET_ITEM(self, 1, week_object); + + PyObject *weekday_object = PyLong_FromLong(weekday); + if (weekday_object == NULL) { + Py_DECREF(self); + return NULL; + } + PyTuple_SET_ITEM(self, 2, weekday_object); return (PyObject *)self; } @@ -3775,8 +3888,11 @@ date_isocalendar(PyObject *self, PyObject *Py_UNUSED(dummy)) week = 0; } - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } PyObject *v = iso_calendar_date_new_impl(ISOCALENDAR_DATE_TYPE(st), year, week + 1, day + 1); @@ -3834,7 +3950,7 @@ datetime_date_replace_impl(PyDateTime_Date *self, int year, int month, int day) /*[clinic end generated code: output=2a9430d1e6318aeb input=0d1f02685b3e90f6]*/ { - return new_date_subclass_ex(year, month, day, (PyObject *)Py_TYPE(self)); + return new_date_subclass_ex(year, month, day, Py_TYPE(self)); } static Py_hash_t @@ -3850,12 +3966,14 @@ static Py_hash_t date_hash(PyObject *op) { PyDateTime_Date *self = PyDate_CAST(op); - if (self->hashcode == -1) { - self->hashcode = generic_hash( + Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->hashcode); + if (hash == -1) { + hash = generic_hash( (unsigned char *)self->data, _PyDateTime_DATE_DATASIZE); + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); } - return self->hashcode; + return hash; } static PyObject * @@ -3895,38 +4013,19 @@ static PyMethodDef date_methods[] = { /* Class methods: */ DATETIME_DATE_FROMTIMESTAMP_METHODDEF - - {"fromordinal", date_fromordinal, METH_VARARGS | METH_CLASS, - PyDoc_STR("int -> date corresponding to a proleptic Gregorian " - "ordinal.")}, - - {"fromisoformat", date_fromisoformat, METH_O | METH_CLASS, - PyDoc_STR("str -> Construct a date from a string in ISO 8601 format.")}, - - {"fromisocalendar", _PyCFunction_CAST(date_fromisocalendar), - METH_VARARGS | METH_KEYWORDS | METH_CLASS, - PyDoc_STR("int, int, int -> Construct a date from the ISO year, week " - "number and weekday.\n\n" - "This is the inverse of the date.isocalendar() function")}, - - {"strptime", date_strptime, METH_VARARGS | METH_CLASS, - PyDoc_STR("string, format -> new date parsed from a string " - "(like time.strptime()).")}, - - {"today", date_today, METH_NOARGS | METH_CLASS, - PyDoc_STR("Current date or datetime: same as " - "self.__class__.fromtimestamp(time.time()).")}, + DATETIME_DATE_FROMORDINAL_METHODDEF + DATETIME_DATE_FROMISOFORMAT_METHODDEF + DATETIME_DATE_FROMISOCALENDAR_METHODDEF + DATETIME_DATE_STRPTIME_METHODDEF + DATETIME_DATE_TODAY_METHODDEF /* Instance methods: */ {"ctime", date_ctime, METH_NOARGS, PyDoc_STR("Return ctime() style string.")}, - {"strftime", _PyCFunction_CAST(date_strftime), METH_VARARGS | METH_KEYWORDS, - PyDoc_STR("format -> strftime() style string.")}, - - {"__format__", date_format, METH_VARARGS, - PyDoc_STR("Formats self with strftime.")}, + DATETIME_DATE_STRFTIME_METHODDEF + DATETIME_DATE___FORMAT___METHODDEF {"timetuple", date_timetuple, METH_NOARGS, PyDoc_STR("Return time tuple, compatible with time.localtime().")}, @@ -3961,9 +4060,6 @@ static PyMethodDef date_methods[] = { {NULL, NULL} }; -static const char date_doc[] = -PyDoc_STR("date(year, month, day) --> date object"); - static PyNumberMethods date_as_number = { date_add, /* nb_add */ date_subtract, /* nb_subtract */ @@ -3998,7 +4094,7 @@ static PyTypeObject PyDateTime_DateType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - date_doc, /* tp_doc */ + datetime_date__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ date_richcompare, /* tp_richcompare */ @@ -4201,7 +4297,8 @@ static PyMethodDef tzinfo_methods[] = { }; static const char tzinfo_doc[] = -PyDoc_STR("Abstract base class for time zone info objects."); +PyDoc_STR("Abstract base class for time zone info objects.\n\n" + "Subclasses must override the tzname(), utcoffset() and dst() methods."); static PyTypeObject PyDateTime_TZInfoType = { PyVarObject_HEAD_INIT(NULL, 0) @@ -4245,18 +4342,21 @@ static PyTypeObject PyDateTime_TZInfoType = { 0, /* tp_free */ }; -static char *timezone_kws[] = {"offset", "name", NULL}; +/*[clinic input] +@classmethod +datetime.timezone.__new__ as timezone_new + + offset: object(subclass_of="DELTA_TYPE(NO_STATE)") + name: unicode = NULL + +Fixed offset from UTC implementation of tzinfo. +[clinic start generated code]*/ static PyObject * -timezone_new(PyTypeObject *type, PyObject *args, PyObject *kw) +timezone_new_impl(PyTypeObject *type, PyObject *offset, PyObject *name) +/*[clinic end generated code: output=41a2dda500424187 input=d51255afe60382cd]*/ { - PyObject *offset; - PyObject *name = NULL; - if (PyArg_ParseTupleAndKeywords(args, kw, "O!|U:timezone", timezone_kws, - DELTA_TYPE(NO_STATE), &offset, &name)) - return new_timezone(offset, name); - - return NULL; + return new_timezone(offset, name); } static void @@ -4421,7 +4521,7 @@ timezone_getinitargs(PyObject *op, PyObject *Py_UNUSED(dummy)) PyDateTime_TimeZone *self = PyTimeZone_CAST(op); if (self->name == NULL) return PyTuple_Pack(1, self->offset); - return PyTuple_Pack(2, self->offset, self->name); + return _PyTuple_FromPair(self->offset, self->name); } static PyMethodDef timezone_methods[] = { @@ -4444,9 +4544,6 @@ static PyMethodDef timezone_methods[] = { {NULL, NULL} }; -static const char timezone_doc[] = -PyDoc_STR("Fixed offset from UTC implementation of tzinfo."); - static PyTypeObject PyDateTime_TimeZoneType = { PyVarObject_HEAD_INIT(NULL, 0) "datetime.timezone", /* tp_name */ @@ -4468,7 +4565,7 @@ static PyTypeObject PyDateTime_TimeZoneType = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - timezone_doc, /* tp_doc */ + timezone_new__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ timezone_richcompare, /* tp_richcompare */ @@ -4570,9 +4667,6 @@ static PyGetSetDef time_getset[] = { * Constructors. */ -static char *time_kws[] = {"hour", "minute", "second", "microsecond", - "tzinfo", "fold", NULL}; - static PyObject * time_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) { @@ -4608,17 +4702,10 @@ time_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) static PyObject * time_new(PyTypeObject *type, PyObject *args, PyObject *kw) { - PyObject *self = NULL; - int hour = 0; - int minute = 0; - int second = 0; - int usecond = 0; - PyObject *tzinfo = Py_None; - int fold = 0; - /* Check for invocation from pickle with __getstate__ state */ if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) { PyObject *state = PyTuple_GET_ITEM(args, 0); + PyObject *tzinfo = Py_None; if (PyTuple_GET_SIZE(args) == 2) { tzinfo = PyTuple_GET_ITEM(args, 1); } @@ -4644,40 +4731,71 @@ time_new(PyTypeObject *type, PyObject *args, PyObject *kw) } return NULL; } - self = time_from_pickle(type, state, tzinfo); + PyObject *self = time_from_pickle(type, state, tzinfo); Py_DECREF(state); return self; } } - tzinfo = Py_None; } - if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO$i", time_kws, - &hour, &minute, &second, &usecond, - &tzinfo, &fold)) { - self = new_time_ex2(hour, minute, second, usecond, tzinfo, fold, - type); - } - return self; + return datetime_time(type, args, kw); } -/* Return new time from _strptime.strptime_datetime_time(). */ +/*[clinic input] +@classmethod +datetime.time.__new__ + + hour: int = 0 + minute: int = 0 + second: int = 0 + microsecond: int = 0 + tzinfo: object = None + * + fold: int = 0 + +Time with time zone. + +All arguments are optional. tzinfo may be None, or an instance of +a tzinfo subclass. The remaining arguments may be ints. +[clinic start generated code]*/ + static PyObject * -time_strptime(PyObject *cls, PyObject *args) +datetime_time_impl(PyTypeObject *type, int hour, int minute, int second, + int microsecond, PyObject *tzinfo, int fold) +/*[clinic end generated code: output=f06bb4315225e7f6 input=0148df5e8138fe7b]*/ { - PyObject *string, *format, *result; + return new_time_ex2(hour, minute, second, microsecond, tzinfo, fold, type); +} - if (!PyArg_ParseTuple(args, "UU:strptime", &string, &format)) { - return NULL; - } +/*[clinic input] +@permit_long_summary +@classmethod +datetime.time.strptime + + string: unicode + format: unicode + / + +Parse string according to the given time format (like time.strptime()). + +For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes +[clinic start generated code]*/ + +static PyObject * +datetime_time_strptime_impl(PyTypeObject *type, PyObject *string, + PyObject *format) +/*[clinic end generated code: output=ae05a9bc0241d3bf input=f01d0b9eb5383da5]*/ +{ + PyObject *result; PyObject *module = PyImport_Import(&_Py_ID(_strptime)); if (module == NULL) { return NULL; } result = PyObject_CallMethodObjArgs(module, - &_Py_ID(_strptime_datetime_time), cls, - string, format, NULL); + &_Py_ID(_strptime_datetime_time), + (PyObject *)type, string, format, NULL); Py_DECREF(module); return result; } @@ -4756,17 +4874,30 @@ time_str(PyObject *op) return PyObject_CallMethodNoArgs(op, &_Py_ID(isoformat)); } +/*[clinic input] +datetime.time.isoformat + + timespec: str(c_default="NULL") = 'auto' + +Return the time formatted according to ISO. + +The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the +fractional part is omitted if self.microsecond == 0. + +The optional argument timespec specifies the number of additional +terms of the time to include. Valid options are 'auto', 'hours', +'minutes', 'seconds', 'milliseconds' and 'microseconds'. +[clinic start generated code]*/ + static PyObject * -time_isoformat(PyObject *op, PyObject *args, PyObject *kw) +datetime_time_isoformat_impl(PyDateTime_Time *self, const char *timespec) +/*[clinic end generated code: output=2bcc7cab65c35545 input=0efae103081060f4]*/ { char buf[100]; - const char *timespec = NULL; - static char *keywords[] = {"timespec", NULL}; - PyDateTime_Time *self = PyTime_CAST(op); PyObject *result; int us = TIME_GET_MICROSECOND(self); - static const char *specs[][2] = { + static const char * const specs[][2] = { {"hours", "%02d"}, {"minutes", "%02d:%02d"}, {"seconds", "%02d:%02d:%02d"}, @@ -4775,9 +4906,6 @@ time_isoformat(PyObject *op, PyObject *args, PyObject *kw) }; size_t given_spec; - if (!PyArg_ParseTupleAndKeywords(args, kw, "|s:isoformat", keywords, &timespec)) - return NULL; - if (timespec == NULL || strcmp(timespec, "auto") == 0) { if (us == 0) { /* seconds */ @@ -4823,18 +4951,26 @@ time_isoformat(PyObject *op, PyObject *args, PyObject *kw) return result; } +/*[clinic input] +datetime.time.strftime + + format: unicode + +Format using strftime(). + +The date part of the timestamp passed to underlying strftime should +not be used. + +For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes +[clinic start generated code]*/ + static PyObject * -time_strftime(PyObject *op, PyObject *args, PyObject *kw) +datetime_time_strftime_impl(PyDateTime_Time *self, PyObject *format) +/*[clinic end generated code: output=10f65af20e2a78c7 input=184e1c0d7d356c5d]*/ { PyObject *result; PyObject *tuple; - PyObject *format; - static char *keywords[] = {"format", NULL}; - PyDateTime_Time *self = PyTime_CAST(op); - - if (! PyArg_ParseTupleAndKeywords(args, kw, "U:strftime", keywords, - &format)) - return NULL; /* Python's strftime does insane things with the year part of the * timetuple. The year is forced to (the otherwise nonsensical) @@ -4855,6 +4991,27 @@ time_strftime(PyObject *op, PyObject *args, PyObject *kw) return result; } +/*[clinic input] +datetime.time.__format__ + + self: self(type="PyObject *") + format: unicode + / + +Formats self with strftime. +[clinic start generated code]*/ + +static PyObject * +datetime_time___format___impl(PyObject *self, PyObject *format) +/*[clinic end generated code: output=4646451f7a5d2156 input=6a858ae787d20230]*/ +{ + /* if the format is zero length, return str(self) */ + if (PyUnicode_GetLength(format) == 0) + return PyObject_Str(self); + + return PyObject_CallMethodOneArg(self, &_Py_ID(strftime), format); +} + /* * Miscellaneous methods. */ @@ -4934,7 +5091,8 @@ static Py_hash_t time_hash(PyObject *op) { PyDateTime_Time *self = PyTime_CAST(op); - if (self->hashcode == -1) { + Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->hashcode); + if (hash == -1) { PyObject *offset, *self0; if (TIME_GET_FOLD(self)) { self0 = new_time_ex2(TIME_GET_HOUR(self), @@ -4956,10 +5114,11 @@ time_hash(PyObject *op) return -1; /* Reduce this to a hash of another object. */ - if (offset == Py_None) - self->hashcode = generic_hash( + if (offset == Py_None) { + hash = generic_hash( (unsigned char *)self->data, _PyDateTime_TIME_DATASIZE); - else { + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); + } else { PyObject *temp1, *temp2; int seconds, microseconds; assert(HASTZINFO(self)); @@ -4978,12 +5137,13 @@ time_hash(PyObject *op) Py_DECREF(offset); return -1; } - self->hashcode = PyObject_Hash(temp2); + hash = PyObject_Hash(temp2); + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); Py_DECREF(temp2); } Py_DECREF(offset); } - return self->hashcode; + return hash; } /*[clinic input] @@ -5007,20 +5167,25 @@ datetime_time_replace_impl(PyDateTime_Time *self, int hour, int minute, /*[clinic end generated code: output=0b89a44c299e4f80 input=abf23656e8df4e97]*/ { return new_time_subclass_fold_ex(hour, minute, second, microsecond, tzinfo, - fold, (PyObject *)Py_TYPE(self)); + fold, Py_TYPE(self)); } -static PyObject * -time_fromisoformat(PyObject *cls, PyObject *tstr) { - assert(tstr != NULL); +/*[clinic input] +@classmethod +datetime.time.fromisoformat - if (!PyUnicode_Check(tstr)) { - PyErr_SetString(PyExc_TypeError, "fromisoformat: argument must be str"); - return NULL; - } + string: unicode + / +Construct a time from a string in ISO 8601 format. +[clinic start generated code]*/ + +static PyObject * +datetime_time_fromisoformat_impl(PyTypeObject *type, PyObject *string) +/*[clinic end generated code: output=97c57e896e7f2535 input=bdb4b8abea9cd688]*/ +{ Py_ssize_t len; - const char *p = PyUnicode_AsUTF8AndSize(tstr, &len); + const char *p = PyUnicode_AsUTF8AndSize(string, &len); if (p == NULL) { goto invalid_string_error; @@ -5063,10 +5228,10 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { } PyObject *t; - if ( (PyTypeObject *)cls == TIME_TYPE(NO_STATE)) { + if (type == TIME_TYPE(NO_STATE)) { t = new_time(hour, minute, second, microsecond, tzinfo, 0); } else { - t = PyObject_CallFunction(cls, "iiiiO", + t = PyObject_CallFunction((PyObject *)type, "iiiiO", hour, minute, second, microsecond, tzinfo); } @@ -5078,7 +5243,7 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { return NULL; invalid_string_error: - PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", tstr); + PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", string); return NULL; error: @@ -5108,27 +5273,34 @@ time_getstate(PyDateTime_Time *self, int proto) if (! HASTZINFO(self) || self->tzinfo == Py_None) result = PyTuple_Pack(1, basestate); else - result = PyTuple_Pack(2, basestate, self->tzinfo); + result = _PyTuple_FromPair(basestate, self->tzinfo); Py_DECREF(basestate); } return result; } +/*[clinic input] +datetime.time.__reduce_ex__ + + proto: int + / +[clinic start generated code]*/ + static PyObject * -time_reduce_ex(PyObject *op, PyObject *args) +datetime_time___reduce_ex___impl(PyDateTime_Time *self, int proto) +/*[clinic end generated code: output=ccfab65f5c320c1b input=4cd06bb3ac3657bb]*/ { - int proto; - if (!PyArg_ParseTuple(args, "i:__reduce_ex__", &proto)) - return NULL; - - PyDateTime_Time *self = PyTime_CAST(op); return Py_BuildValue("(ON)", Py_TYPE(self), time_getstate(self, proto)); } +/*[clinic input] +datetime.time.__reduce__ +[clinic start generated code]*/ + static PyObject * -time_reduce(PyObject *op, PyObject *Py_UNUSED(dummy)) +datetime_time___reduce___impl(PyDateTime_Time *self) +/*[clinic end generated code: output=9a2fcc87e64ce300 input=0fb8dd14d275857f]*/ { - PyDateTime_Time *self = PyTime_CAST(op); return Py_BuildValue("(ON)", Py_TYPE(self), time_getstate(self, 2)); } @@ -5136,26 +5308,14 @@ static PyMethodDef time_methods[] = { /* Class method: */ - {"strptime", time_strptime, - METH_VARARGS | METH_CLASS, - PyDoc_STR("string, format -> new time parsed from a string " - "(like time.strptime()).")}, + DATETIME_TIME_FROMISOFORMAT_METHODDEF + DATETIME_TIME_STRPTIME_METHODDEF /* Instance methods: */ - {"isoformat", _PyCFunction_CAST(time_isoformat), METH_VARARGS | METH_KEYWORDS, - PyDoc_STR("Return string in ISO 8601 format, [HH[:MM[:SS[.mmm[uuu]]]]]" - "[+HH:MM].\n\n" - "The optional argument timespec specifies the number " - "of additional terms\nof the time to include. Valid " - "options are 'auto', 'hours', 'minutes',\n'seconds', " - "'milliseconds' and 'microseconds'.\n")}, - - {"strftime", _PyCFunction_CAST(time_strftime), METH_VARARGS | METH_KEYWORDS, - PyDoc_STR("format -> strftime() style string.")}, - - {"__format__", date_format, METH_VARARGS, - PyDoc_STR("Formats self with strftime.")}, + DATETIME_TIME_ISOFORMAT_METHODDEF + DATETIME_TIME_STRFTIME_METHODDEF + DATETIME_TIME___FORMAT___METHODDEF {"utcoffset", time_utcoffset, METH_NOARGS, PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, @@ -5171,24 +5331,12 @@ static PyMethodDef time_methods[] = { {"__replace__", _PyCFunction_CAST(datetime_time_replace), METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("__replace__($self, /, **changes)\n--\n\nThe same as replace().")}, - {"fromisoformat", time_fromisoformat, METH_O | METH_CLASS, - PyDoc_STR("string -> time from a string in ISO 8601 format")}, - - {"__reduce_ex__", time_reduce_ex, METH_VARARGS, - PyDoc_STR("__reduce_ex__(proto) -> (cls, state)")}, - - {"__reduce__", time_reduce, METH_NOARGS, - PyDoc_STR("__reduce__() -> (cls, state)")}, + DATETIME_TIME___REDUCE_EX___METHODDEF + DATETIME_TIME___REDUCE___METHODDEF {NULL, NULL} }; -static const char time_doc[] = -PyDoc_STR("time([hour[, minute[, second[, microsecond[, tzinfo]]]]]) --> a time object\n\ -\n\ -All arguments are optional. tzinfo may be None, or an instance of\n\ -a tzinfo subclass. The remaining arguments may be ints.\n"); - static PyTypeObject PyDateTime_TimeType = { PyVarObject_HEAD_INIT(NULL, 0) "datetime.time", /* tp_name */ @@ -5209,8 +5357,8 @@ static PyTypeObject PyDateTime_TimeType = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - time_doc, /* tp_doc */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + datetime_time__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ time_richcompare, /* tp_richcompare */ @@ -5296,11 +5444,6 @@ static PyGetSetDef datetime_getset[] = { * Constructors. */ -static char *datetime_kws[] = { - "year", "month", "day", "hour", "minute", "second", - "microsecond", "tzinfo", "fold", NULL -}; - static PyObject * datetime_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) { @@ -5336,20 +5479,10 @@ datetime_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) static PyObject * datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) { - PyObject *self = NULL; - int year; - int month; - int day; - int hour = 0; - int minute = 0; - int second = 0; - int usecond = 0; - int fold = 0; - PyObject *tzinfo = Py_None; - /* Check for invocation from pickle with __getstate__ state */ if (PyTuple_GET_SIZE(args) >= 1 && PyTuple_GET_SIZE(args) <= 2) { PyObject *state = PyTuple_GET_ITEM(args, 0); + PyObject *tzinfo = Py_None; if (PyTuple_GET_SIZE(args) == 2) { tzinfo = PyTuple_GET_ITEM(args, 1); } @@ -5375,22 +5508,46 @@ datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) } return NULL; } - self = datetime_from_pickle(type, state, tzinfo); + PyObject *self = datetime_from_pickle(type, state, tzinfo); Py_DECREF(state); return self; } } - tzinfo = Py_None; } - if (PyArg_ParseTupleAndKeywords(args, kw, "iii|iiiiO$i", datetime_kws, - &year, &month, &day, &hour, &minute, - &second, &usecond, &tzinfo, &fold)) { - self = new_datetime_ex2(year, month, day, - hour, minute, second, usecond, - tzinfo, fold, type); - } - return self; + return datetime_datetime(type, args, kw); +} + +/*[clinic input] +@classmethod +datetime.datetime.__new__ + + year: int + month: int + day: int + hour: int = 0 + minute: int = 0 + second: int = 0 + microsecond: int = 0 + tzinfo: object = None + * + fold: int = 0 + +A combination of a date and a time. + +The year, month and day arguments are required. tzinfo may be None, or +an instance of a tzinfo subclass. The remaining arguments may be ints. +[clinic start generated code]*/ + +static PyObject * +datetime_datetime_impl(PyTypeObject *type, int year, int month, int day, + int hour, int minute, int second, int microsecond, + PyObject *tzinfo, int fold) +/*[clinic end generated code: output=47983ddb47d36037 input=c7fd85dcf6fe9691]*/ +{ + return new_datetime_ex2(year, month, day, + hour, minute, second, microsecond, + tzinfo, fold, type); } /* TM_FUNC is the shared type of _PyTime_localtime() and @@ -5447,7 +5604,7 @@ local(long long u) * Pass localtime or gmtime for f, to control the interpretation of timet. */ static PyObject * -datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us, +datetime_from_timet_and_us(PyTypeObject *cls, TM_FUNC f, time_t timet, int us, PyObject *tzinfo) { struct tm tm; @@ -5470,22 +5627,7 @@ datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us, second = Py_MIN(59, tm.tm_sec); /* local timezone requires to compute fold */ - if (tzinfo == Py_None && f == _PyTime_localtime - /* On Windows, passing a negative value to local results - * in an OSError because localtime_s on Windows does - * not support negative timestamps. Unfortunately this - * means that fold detection for time values between - * 0 and max_fold_seconds will result in an identical - * error since we subtract max_fold_seconds to detect a - * fold. However, since we know there haven't been any - * folds in the interval [0, max_fold_seconds) in any - * timezone, we can hackily just forego fold detection - * for this time range. - */ -#ifdef MS_WINDOWS - && (timet - max_fold_seconds > 0) -#endif - ) { + if (tzinfo == Py_None && f == _PyTime_localtime) { long long probe_seconds, result_seconds, transition; result_seconds = utc_to_seconds(year, month, day, @@ -5519,7 +5661,7 @@ datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us, * to get that much precision (e.g., C time() isn't good enough). */ static PyObject * -datetime_from_timestamp(PyObject *cls, TM_FUNC f, PyObject *timestamp, +datetime_from_timestamp(PyTypeObject *cls, TM_FUNC f, PyObject *timestamp, PyObject *tzinfo) { time_t timet; @@ -5537,7 +5679,7 @@ datetime_from_timestamp(PyObject *cls, TM_FUNC f, PyObject *timestamp, * gmtime for f as appropriate. */ static PyObject * -datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo) +datetime_best_possible(PyTypeObject *cls, TM_FUNC f, PyObject *tzinfo) { PyTime_t ts; if (PyTime_Time(&ts) < 0) { @@ -5580,7 +5722,7 @@ datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz) if (check_tzinfo_subclass(tz) < 0) return NULL; - self = datetime_best_possible((PyObject *)type, + self = datetime_best_possible(type, tz == Py_None ? _PyTime_localtime : _PyTime_gmtime, tz); @@ -5596,8 +5738,16 @@ datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz) /* Return best possible UTC time -- this isn't constrained by the * precision of a timestamp. */ +/*[clinic input] +@classmethod +datetime.datetime.utcnow + +Return a new datetime representing UTC day and time. +[clinic start generated code]*/ + static PyObject * -datetime_utcnow(PyObject *cls, PyObject *dummy) +datetime_datetime_utcnow_impl(PyTypeObject *type) +/*[clinic end generated code: output=cfcfe71c6c916ba9 input=576eff2b222b80a1]*/ { if (PyErr_WarnEx(PyExc_DeprecationWarning, "datetime.datetime.utcnow() is deprecated and scheduled for removal in a " @@ -5606,25 +5756,32 @@ datetime_utcnow(PyObject *cls, PyObject *dummy) { return NULL; } - return datetime_best_possible(cls, _PyTime_gmtime, Py_None); + return datetime_best_possible(type, _PyTime_gmtime, Py_None); } -/* Return new local datetime from timestamp (Python timestamp -- a double). */ +/*[clinic input] +@classmethod +datetime.datetime.fromtimestamp + + timestamp: object + tz as tzinfo: object = None + +Create a datetime from a POSIX timestamp. + +The timestamp is a number, e.g. created via time.time(), that is +interpreted as local time. +[clinic start generated code]*/ + static PyObject * -datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw) +datetime_datetime_fromtimestamp_impl(PyTypeObject *type, PyObject *timestamp, + PyObject *tzinfo) +/*[clinic end generated code: output=9c47ea2b2ebdaded input=7a2bc81a049ea287]*/ { PyObject *self; - PyObject *timestamp; - PyObject *tzinfo = Py_None; - static char *keywords[] = {"timestamp", "tz", NULL}; - - if (! PyArg_ParseTupleAndKeywords(args, kw, "O|O:fromtimestamp", - keywords, &timestamp, &tzinfo)) - return NULL; if (check_tzinfo_subclass(tzinfo) < 0) return NULL; - self = datetime_from_timestamp(cls, + self = datetime_from_timestamp(type, tzinfo == Py_None ? _PyTime_localtime : _PyTime_gmtime, timestamp, @@ -5638,9 +5795,35 @@ datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw) return self; } -/* Return new UTC datetime from timestamp (Python timestamp -- a double). */ +/* This is a wrapper for API compatibility with the public C API. */ static PyObject * -datetime_utcfromtimestamp(PyObject *cls, PyObject *args) +datetime_datetime_fromtimestamp_capi(PyObject *cls, PyObject *args, PyObject *kw) +{ + PyObject *timestamp; + PyObject *tzinfo = Py_None; + static char *keywords[] = {"timestamp", "tz", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|O:fromtimestamp", + keywords, &timestamp, &tzinfo)) + return NULL; + return datetime_datetime_fromtimestamp_impl((PyTypeObject *)cls, + timestamp, tzinfo); +} + +/*[clinic input] +@classmethod +datetime.datetime.utcfromtimestamp + + timestamp: object + / + +Create a naive UTC datetime from a POSIX timestamp. +[clinic start generated code]*/ + +static PyObject * +datetime_datetime_utcfromtimestamp_impl(PyTypeObject *type, + PyObject *timestamp) +/*[clinic end generated code: output=66d0b1741d788fd2 input=13fabd4296b1c206]*/ { if (PyErr_WarnEx(PyExc_DeprecationWarning, "datetime.datetime.utcfromtimestamp() is deprecated and scheduled for removal " @@ -5649,23 +5832,31 @@ datetime_utcfromtimestamp(PyObject *cls, PyObject *args) { return NULL; } - PyObject *timestamp; - PyObject *result = NULL; - if (PyArg_ParseTuple(args, "O:utcfromtimestamp", &timestamp)) - result = datetime_from_timestamp(cls, _PyTime_gmtime, timestamp, - Py_None); - return result; + return datetime_from_timestamp(type, _PyTime_gmtime, timestamp, Py_None); } -/* Return new datetime from _strptime.strptime_datetime_datetime(). */ +/*[clinic input] +@permit_long_summary +@classmethod +datetime.datetime.strptime + + string: unicode + format: unicode + / + +Parse string according to the given date and time format (like time.strptime()). + +For a list of supported format codes, see the documentation: + https://docs.python.org/3/library/datetime.html#format-codes +[clinic start generated code]*/ + static PyObject * -datetime_strptime(PyObject *cls, PyObject *args) +datetime_datetime_strptime_impl(PyTypeObject *type, PyObject *string, + PyObject *format) +/*[clinic end generated code: output=af2c2d024f3203f5 input=ef7807589f1d50e7]*/ { - PyObject *string, *format, *result; - - if (!PyArg_ParseTuple(args, "UU:strptime", &string, &format)) - return NULL; + PyObject *result; PyObject *module = PyImport_Import(&_Py_ID(_strptime)); if (module == NULL) { @@ -5673,42 +5864,43 @@ datetime_strptime(PyObject *cls, PyObject *args) } result = PyObject_CallMethodObjArgs(module, &_Py_ID(_strptime_datetime_datetime), - cls, string, format, NULL); + (PyObject *)type, string, format, NULL); Py_DECREF(module); return result; } -/* Return new datetime from date/datetime and time arguments. */ +/*[clinic input] +@classmethod +datetime.datetime.combine + + date: object(subclass_of="DATE_TYPE(NO_STATE)") + time: object(subclass_of="TIME_TYPE(NO_STATE)") + tzinfo: object = NULL + +Construct a datetime from a given date and a given time. +[clinic start generated code]*/ + static PyObject * -datetime_combine(PyObject *cls, PyObject *args, PyObject *kw) +datetime_datetime_combine_impl(PyTypeObject *type, PyObject *date, + PyObject *time, PyObject *tzinfo) +/*[clinic end generated code: output=a10f3cbb90f4d0aa input=4fcf0743288d0bab]*/ { - static char *keywords[] = {"date", "time", "tzinfo", NULL}; - PyObject *date; - PyObject *time; - PyObject *tzinfo = NULL; - PyObject *result = NULL; - - if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!|O:combine", keywords, - DATE_TYPE(NO_STATE), &date, - TIME_TYPE(NO_STATE), &time, &tzinfo)) { - if (tzinfo == NULL) { - if (HASTZINFO(time)) - tzinfo = ((PyDateTime_Time *)time)->tzinfo; - else - tzinfo = Py_None; - } - result = new_datetime_subclass_fold_ex(GET_YEAR(date), - GET_MONTH(date), - GET_DAY(date), - TIME_GET_HOUR(time), - TIME_GET_MINUTE(time), - TIME_GET_SECOND(time), - TIME_GET_MICROSECOND(time), - tzinfo, - TIME_GET_FOLD(time), - cls); + if (tzinfo == NULL) { + if (HASTZINFO(time)) + tzinfo = ((PyDateTime_Time *)time)->tzinfo; + else + tzinfo = Py_None; } - return result; + return new_datetime_subclass_fold_ex(GET_YEAR(date), + GET_MONTH(date), + GET_DAY(date), + TIME_GET_HOUR(time), + TIME_GET_MINUTE(time), + TIME_GET_SECOND(time), + TIME_GET_MICROSECOND(time), + tzinfo, + TIME_GET_FOLD(time), + type); } static PyObject * @@ -5866,23 +6058,26 @@ _find_isoformat_datetime_separator(const char *dtstr, Py_ssize_t len) { } } -static PyObject * -datetime_fromisoformat(PyObject *cls, PyObject *dtstr) -{ - assert(dtstr != NULL); +/*[clinic input] +@classmethod +datetime.datetime.fromisoformat - if (!PyUnicode_Check(dtstr)) { - PyErr_SetString(PyExc_TypeError, - "fromisoformat: argument must be str"); - return NULL; - } + string: unicode + / + +Construct a date from a string in ISO 8601 format. +[clinic start generated code]*/ +static PyObject * +datetime_datetime_fromisoformat_impl(PyTypeObject *type, PyObject *string) +/*[clinic end generated code: output=1800a952fcab79d9 input=d517b158209ded42]*/ +{ // We only need to sanitize this string if the separator is a surrogate // character. In the situation where the separator location is ambiguous, // we don't have to sanitize it anything because that can only happen when // the separator is either '-' or a number. This should mostly be a noop // but it makes the reference counting easier if we still sanitize. - PyObject *dtstr_clean = _sanitize_isoformat_str(dtstr); + PyObject *dtstr_clean = _sanitize_isoformat_str(string); if (dtstr_clean == NULL) { goto invalid_string_error; } @@ -5970,7 +6165,7 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) } } PyObject *dt = new_datetime_subclass_ex(year, month, day, hour, minute, - second, microsecond, tzinfo, cls); + second, microsecond, tzinfo, type); Py_DECREF(tzinfo); Py_DECREF(dtstr_clean); @@ -5983,7 +6178,7 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) return NULL; invalid_string_error: - PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", dtstr); + PyErr_Format(PyExc_ValueError, "Invalid isoformat string: %R", string); error: Py_XDECREF(dtstr_clean); @@ -6060,7 +6255,7 @@ add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta, return new_datetime_subclass_ex(year, month, day, hour, minute, second, microsecond, HASTZINFO(date) ? date->tzinfo : Py_None, - (PyObject *)Py_TYPE(date)); + Py_TYPE(date)); } static PyObject * @@ -6222,18 +6417,38 @@ datetime_str(PyObject *op) return res; } +/*[clinic input] +datetime.datetime.isoformat + + sep: int(accept={str}) = 'T' + timespec: str(c_default="NULL") = 'auto' + +Return the time formatted according to ISO. + +The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'. +By default, the fractional part is omitted if self.microsecond == 0. + +If self.tzinfo is not None, the UTC offset is also attached, giving +a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'. + +Optional argument sep specifies the separator between date and +time, default 'T'. + +The optional argument timespec specifies the number of additional +terms of the time to include. Valid options are 'auto', 'hours', +'minutes', 'seconds', 'milliseconds' and 'microseconds'. +[clinic start generated code]*/ + static PyObject * -datetime_isoformat(PyObject *op, PyObject *args, PyObject *kw) +datetime_datetime_isoformat_impl(PyDateTime_DateTime *self, int sep, + const char *timespec) +/*[clinic end generated code: output=9b6ce1383189b0bf input=db935a57fa697c5e]*/ { - int sep = 'T'; - char *timespec = NULL; - static char *keywords[] = {"sep", "timespec", NULL}; char buffer[100]; - PyDateTime_DateTime *self = PyDateTime_CAST(op); PyObject *result = NULL; int us = DATE_GET_MICROSECOND(self); - static const char *specs[][2] = { + static const char * const specs[][2] = { {"hours", "%04d-%02d-%02d%c%02d"}, {"minutes", "%04d-%02d-%02d%c%02d:%02d"}, {"seconds", "%04d-%02d-%02d%c%02d:%02d:%02d"}, @@ -6242,9 +6457,6 @@ datetime_isoformat(PyObject *op, PyObject *args, PyObject *kw) }; size_t given_spec; - if (!PyArg_ParseTupleAndKeywords(args, kw, "|Cs:isoformat", keywords, &sep, &timespec)) - return NULL; - if (timespec == NULL || strcmp(timespec, "auto") == 0) { if (us == 0) { /* seconds */ @@ -6282,7 +6494,7 @@ datetime_isoformat(PyObject *op, PyObject *args, PyObject *kw) return result; /* We need to append the UTC offset. */ - if (format_utcoffset(buffer, sizeof(buffer), ":", self->tzinfo, op) < 0) { + if (format_utcoffset(buffer, sizeof(buffer), ":", self->tzinfo, (PyObject *)self) < 0) { Py_DECREF(result); return NULL; } @@ -6412,8 +6624,7 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) PyDateTime_Delta *delta; assert(offset1 != offset2); /* else last "if" handled it */ - delta = (PyDateTime_Delta *)datetime_subtract((PyObject *)self, - other); + delta = (PyDateTime_Delta *)datetime_subtract(self, other); if (delta == NULL) goto done; diff = GET_TD_DAYS(delta); @@ -6451,7 +6662,8 @@ static Py_hash_t datetime_hash(PyObject *op) { PyDateTime_DateTime *self = PyDateTime_CAST(op); - if (self->hashcode == -1) { + Py_hash_t hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->hashcode); + if (hash == -1) { PyObject *offset, *self0; if (DATE_GET_FOLD(self)) { self0 = new_datetime_ex2(GET_YEAR(self), @@ -6476,10 +6688,11 @@ datetime_hash(PyObject *op) return -1; /* Reduce this to a hash of another object. */ - if (offset == Py_None) - self->hashcode = generic_hash( + if (offset == Py_None) { + hash = generic_hash( (unsigned char *)self->data, _PyDateTime_DATETIME_DATASIZE); - else { + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); + } else { PyObject *temp1, *temp2; int days, seconds; @@ -6503,12 +6716,13 @@ datetime_hash(PyObject *op) Py_DECREF(offset); return -1; } - self->hashcode = PyObject_Hash(temp2); + hash = PyObject_Hash(temp2); + FT_ATOMIC_STORE_SSIZE_RELAXED(self->hashcode, hash); Py_DECREF(temp2); } Py_DECREF(offset); } - return self->hashcode; + return hash; } /*[clinic input] @@ -6537,7 +6751,7 @@ datetime_datetime_replace_impl(PyDateTime_DateTime *self, int year, { return new_datetime_subclass_fold_ex(year, month, day, hour, minute, second, microsecond, tzinfo, fold, - (PyObject *)Py_TYPE(self)); + Py_TYPE(self)); } static PyObject * @@ -6610,8 +6824,11 @@ local_timezone(PyDateTime_DateTime *utc_time) PyObject *one_second; PyObject *seconds; - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } delta = datetime_subtract((PyObject *)utc_time, CONST_EPOCH(st)); RELEASE_CURRENT_STATE(st, current_mod); @@ -6673,20 +6890,23 @@ local_timezone_from_local(PyDateTime_DateTime *local_dt) return local_timezone_from_timestamp(timestamp); } +/*[clinic input] +datetime.datetime.astimezone + + tz as tzinfo: object = None + +Convert to local time in new timezone tz. +[clinic start generated code]*/ + static PyObject * -datetime_astimezone(PyObject *op, PyObject *args, PyObject *kw) +datetime_datetime_astimezone_impl(PyDateTime_DateTime *self, + PyObject *tzinfo) +/*[clinic end generated code: output=ae2263d04e944537 input=9c675c8595009935]*/ { - PyDateTime_DateTime *self = PyDateTime_CAST(op); PyDateTime_DateTime *result; PyObject *offset; PyObject *temp; PyObject *self_tzinfo; - PyObject *tzinfo = Py_None; - static char *keywords[] = {"tz", NULL}; - - if (! PyArg_ParseTupleAndKeywords(args, kw, "|O:astimezone", keywords, - &tzinfo)) - return NULL; if (check_tzinfo_subclass(tzinfo) == -1) return NULL; @@ -6716,9 +6936,9 @@ datetime_astimezone(PyObject *op, PyObject *args, PyObject *kw) goto naive; } else if (!PyDelta_Check(offset)) { + PyErr_Format(PyExc_TypeError, "utcoffset() returned %T," + " expected timedelta or None", offset); Py_DECREF(offset); - PyErr_Format(PyExc_TypeError, "utcoffset() returned %.200s," - " expected timedelta or None", Py_TYPE(offset)->tp_name); return NULL; } /* result = self - offset */ @@ -6854,8 +7074,11 @@ datetime_timestamp(PyObject *op, PyObject *Py_UNUSED(dummy)) PyObject *result; if (HASTZINFO(self) && self->tzinfo != Py_None) { - PyObject *current_mod = NULL; + PyObject *current_mod; datetime_state *st = GET_CURRENT_STATE(current_mod); + if (st == NULL) { + return NULL; + } PyObject *delta; delta = datetime_subtract(op, CONST_EPOCH(st)); @@ -6977,28 +7200,35 @@ datetime_getstate(PyDateTime_DateTime *self, int proto) if (! HASTZINFO(self) || self->tzinfo == Py_None) result = PyTuple_Pack(1, basestate); else - result = PyTuple_Pack(2, basestate, self->tzinfo); + result = _PyTuple_FromPair(basestate, self->tzinfo); Py_DECREF(basestate); } return result; } +/*[clinic input] +datetime.datetime.__reduce_ex__ + + proto: int + / +[clinic start generated code]*/ + static PyObject * -datetime_reduce_ex(PyObject *op, PyObject *args) +datetime_datetime___reduce_ex___impl(PyDateTime_DateTime *self, int proto) +/*[clinic end generated code: output=53d712ce3e927735 input=bab748e49ffb30c3]*/ { - int proto; - if (!PyArg_ParseTuple(args, "i:__reduce_ex__", &proto)) - return NULL; - - PyDateTime_DateTime *self = PyDateTime_CAST(op); return Py_BuildValue("(ON)", Py_TYPE(self), datetime_getstate(self, proto)); } +/*[clinic input] +datetime.datetime.__reduce__ +[clinic start generated code]*/ + static PyObject * -datetime_reduce(PyObject *op, PyObject *Py_UNUSED(arg)) +datetime_datetime___reduce___impl(PyDateTime_DateTime *self) +/*[clinic end generated code: output=6794df9ea75666cf input=cadbbeb3bf3bf94c]*/ { - PyDateTime_DateTime *self = PyDateTime_CAST(op); return Py_BuildValue("(ON)", Py_TYPE(self), datetime_getstate(self, 2)); } @@ -7008,31 +7238,12 @@ static PyMethodDef datetime_methods[] = { /* Class methods: */ DATETIME_DATETIME_NOW_METHODDEF - - {"utcnow", datetime_utcnow, - METH_NOARGS | METH_CLASS, - PyDoc_STR("Return a new datetime representing UTC day and time.")}, - - {"fromtimestamp", _PyCFunction_CAST(datetime_fromtimestamp), - METH_VARARGS | METH_KEYWORDS | METH_CLASS, - PyDoc_STR("timestamp[, tz] -> tz's local time from POSIX timestamp.")}, - - {"utcfromtimestamp", datetime_utcfromtimestamp, - METH_VARARGS | METH_CLASS, - PyDoc_STR("Construct a naive UTC datetime from a POSIX timestamp.")}, - - {"strptime", datetime_strptime, - METH_VARARGS | METH_CLASS, - PyDoc_STR("string, format -> new datetime parsed from a string " - "(like time.strptime()).")}, - - {"combine", _PyCFunction_CAST(datetime_combine), - METH_VARARGS | METH_KEYWORDS | METH_CLASS, - PyDoc_STR("date, time -> datetime with same date and time fields")}, - - {"fromisoformat", datetime_fromisoformat, - METH_O | METH_CLASS, - PyDoc_STR("string -> datetime from a string in most ISO 8601 formats")}, + DATETIME_DATETIME_UTCNOW_METHODDEF + DATETIME_DATETIME_FROMTIMESTAMP_METHODDEF + DATETIME_DATETIME_UTCFROMTIMESTAMP_METHODDEF + DATETIME_DATETIME_STRPTIME_METHODDEF + DATETIME_DATETIME_COMBINE_METHODDEF + DATETIME_DATETIME_FROMISOFORMAT_METHODDEF /* Instance methods: */ @@ -7057,15 +7268,7 @@ static PyMethodDef datetime_methods[] = { {"utctimetuple", datetime_utctimetuple, METH_NOARGS, PyDoc_STR("Return UTC time tuple, compatible with time.localtime().")}, - {"isoformat", _PyCFunction_CAST(datetime_isoformat), METH_VARARGS | METH_KEYWORDS, - PyDoc_STR("[sep] -> string in ISO 8601 format, " - "YYYY-MM-DDT[HH[:MM[:SS[.mmm[uuu]]]]][+HH:MM].\n" - "sep is used to separate the year from the time, and " - "defaults to 'T'.\n" - "The optional argument timespec specifies the number " - "of additional terms\nof the time to include. Valid " - "options are 'auto', 'hours', 'minutes',\n'seconds', " - "'milliseconds' and 'microseconds'.\n")}, + DATETIME_DATETIME_ISOFORMAT_METHODDEF {"utcoffset", datetime_utcoffset, METH_NOARGS, PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, @@ -7081,24 +7284,13 @@ static PyMethodDef datetime_methods[] = { {"__replace__", _PyCFunction_CAST(datetime_datetime_replace), METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("__replace__($self, /, **changes)\n--\n\nThe same as replace().")}, - {"astimezone", _PyCFunction_CAST(datetime_astimezone), METH_VARARGS | METH_KEYWORDS, - PyDoc_STR("tz -> convert to local time in new timezone tz\n")}, - - {"__reduce_ex__", datetime_reduce_ex, METH_VARARGS, - PyDoc_STR("__reduce_ex__(proto) -> (cls, state)")}, - - {"__reduce__", datetime_reduce, METH_NOARGS, - PyDoc_STR("__reduce__() -> (cls, state)")}, + DATETIME_DATETIME_ASTIMEZONE_METHODDEF + DATETIME_DATETIME___REDUCE_EX___METHODDEF + DATETIME_DATETIME___REDUCE___METHODDEF {NULL, NULL} }; -static const char datetime_doc[] = -PyDoc_STR("datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])\n\ -\n\ -The year, month and day arguments are required. tzinfo may be None, or an\n\ -instance of a tzinfo subclass. The remaining arguments may be ints.\n"); - static PyNumberMethods datetime_as_number = { datetime_add, /* nb_add */ datetime_subtract, /* nb_subtract */ @@ -7132,8 +7324,8 @@ static PyTypeObject PyDateTime_DateTimeType = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - datetime_doc, /* tp_doc */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + datetime_datetime__doc__, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ datetime_richcompare, /* tp_richcompare */ @@ -7190,7 +7382,7 @@ static PyDateTime_CAPI capi = { .Time_FromTime = new_time_ex, .Delta_FromDelta = new_delta_ex, .TimeZone_FromTimeZone = new_timezone, - .DateTime_FromTimestamp = datetime_fromtimestamp, + .DateTime_FromTimestamp = datetime_datetime_fromtimestamp_capi, .Date_FromTimestamp = datetime_date_fromtimestamp_capi, .DateTime_FromDateAndTimeAndFold = new_datetime_ex2, .Time_FromTimeAndFold = new_time_ex2, @@ -7419,9 +7611,8 @@ _datetime_exec(PyObject *module) datetime_state *st = get_module_state(module); PyInterpreterState *interp = PyInterpreterState_Get(); - PyObject *old_module = get_current_module(interp); - if (PyErr_Occurred()) { - assert(old_module == NULL); + PyObject *old_module; + if (get_current_module(interp, &old_module) < 0) { goto error; } /* We actually set the "current" module right before a successful return. */ @@ -7495,6 +7686,7 @@ _datetime_exec(PyObject *module) } static PyModuleDef_Slot module_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _datetime_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 0cd0f043de453d..a9f4f27d9eb742 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -8,6 +8,7 @@ #endif #include "Python.h" +#include "pycore_object.h" // _PyObject_VisitType() #include <sys/types.h> #include <sys/stat.h> @@ -96,12 +97,6 @@ newdbmobject(_dbm_state *state, const char *file, int flags, int mode) } /* Methods */ -static int -dbm_traverse(PyObject *dp, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(dp)); - return 0; -} static void dbm_dealloc(PyObject *self) @@ -436,13 +431,14 @@ _dbm.dbm.setdefault Return the value for key if present, otherwise default. -If key is not in the database, it is inserted with default as the value. +If key is not in the database, it is inserted with default as the +value. [clinic start generated code]*/ static PyObject * _dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key, Py_ssize_t key_length, PyObject *default_value) -/*[clinic end generated code: output=9c2f6ea6d0fb576c input=c01510ef7571e13b]*/ +/*[clinic end generated code: output=9c2f6ea6d0fb576c input=81224965c110f830]*/ { datum dbm_key, val; Py_ssize_t tmp_size; @@ -456,10 +452,7 @@ _dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key, return PyBytes_FromStringAndSize(val.dptr, val.dsize); } if (default_value == NULL) { - default_value = PyBytes_FromStringAndSize(NULL, 0); - if (default_value == NULL) { - return NULL; - } + default_value = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); val.dptr = NULL; val.dsize = 0; } @@ -523,8 +516,12 @@ dbm__enter__(PyObject *self, PyObject *Py_UNUSED(dummy)) static PyObject * dbm__exit__(PyObject *self, PyObject *Py_UNUSED(args)) { + PyObject *result; dbmobject *dp = dbmobject_CAST(self); - return _dbm_dbm_close_impl(dp); + Py_BEGIN_CRITICAL_SECTION(self); + result = _dbm_dbm_close_impl(dp); + Py_END_CRITICAL_SECTION(); + return result; } static PyMethodDef dbm_methods[] = { @@ -540,7 +537,7 @@ static PyMethodDef dbm_methods[] = { static PyType_Slot dbmtype_spec_slots[] = { {Py_tp_dealloc, dbm_dealloc}, - {Py_tp_traverse, dbm_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_methods, dbm_methods}, {Py_sq_contains, dbm_contains}, {Py_mp_length, dbm_length}, @@ -678,6 +675,7 @@ _dbm_module_free(void *module) } static PyModuleDef_Slot _dbmmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _dbm_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_decimal/README.txt b/Modules/_decimal/README.txt index 7eae0f88f28428..0fa161c8c84555 100644 --- a/Modules/_decimal/README.txt +++ b/Modules/_decimal/README.txt @@ -1,5 +1,3 @@ - - About ===== @@ -7,40 +5,3 @@ _decimal.c is a wrapper for the libmpdec library. libmpdec is a fast C library for correctly-rounded arbitrary precision decimal floating point arithmetic. It is a complete implementation of Mike Cowlishaw/IBM's General Decimal Arithmetic Specification. - - -Build process for the module -============================ - -As usual, the build process for _decimal.so is driven by setup.py in the top -level directory. setup.py autodetects the following build configurations: - - 1) x64 - 64-bit Python, x86_64 processor (AMD, Intel) - - 2) uint128 - 64-bit Python, compiler provides __uint128_t (gcc) - - 3) ansi64 - 64-bit Python, ANSI C - - 4) ppro - 32-bit Python, x86 CPU, PentiumPro or later - - 5) ansi32 - 32-bit Python, ANSI C - - 6) ansi-legacy - 32-bit Python, compiler without uint64_t - - 7) universal - Mac OS only (multi-arch) - - -It is possible to override autodetection by exporting: - - PYTHON_DECIMAL_WITH_MACHINE=value, where value is one of the above options. - - -NOTE -==== - -decimal.so is not built from a static libmpdec.a since doing so led to -failures on AIX (user report) and Windows (mixing static and dynamic CRTs -causes locale problems and more). - - - diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 602b23cfca8945..4db1b60be77844 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -30,9 +30,10 @@ #endif #include <Python.h> +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_typeobject.h" -#include "complexobject.h" #include <mpdecimal.h> @@ -50,14 +51,24 @@ #include <ctype.h> // isascii() #include <stdlib.h> -#include "docstrings.h" - #ifdef EXTRA_FUNCTIONALITY #define _PY_DEC_ROUND_GUARD MPD_ROUND_GUARD #else #define _PY_DEC_ROUND_GUARD (MPD_ROUND_GUARD-1) #endif +#include "clinic/_decimal.c.h" + +#define MPD_SPEC_VERSION "1.70" // Highest version of the spec this complies with + // See https://speleotrove.com/decimal/decarith.html + +/*[clinic input] +module _decimal +class _decimal.Decimal "PyObject *" "&dec_spec" +class _decimal.Context "PyObject *" "&context_spec" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a6a6c0bdf4e576ef]*/ + struct PyDecContextObject; struct DecCondMap; @@ -177,7 +188,7 @@ find_state_ternary(PyObject *left, PyObject *right, PyObject *modulus) * sizeof(size_t) == sizeof(mpd_uint_t) == sizeof(mpd_ssize_t) */ -#ifdef TEST_COVERAGE +#ifdef Py_DEBUG #undef Py_LOCAL_INLINE #define Py_LOCAL_INLINE Py_LOCAL #endif @@ -542,7 +553,7 @@ dict_as_flags(decimal_state *state, PyObject *val) uint32_t flags = 0; int x; - if (!PyDict_Check(val)) { + if (!PyAnyDict_Check(val)) { PyErr_SetString(PyExc_TypeError, "argument must be a signal dict"); return DEC_INVALID_SIGNALS; @@ -739,13 +750,6 @@ signaldict_setitem(PyObject *self, PyObject *key, PyObject *value) return 0; } -static int -signaldict_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static void signaldict_dealloc(PyObject *self) { @@ -799,7 +803,7 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op) if (PyDecSignalDict_Check(state, w)) { res = (SdFlags(v)==SdFlags(w)) ^ (op==Py_NE) ? Py_True : Py_False; } - else if (PyDict_Check(w)) { + else if (PyAnyDict_Check(w)) { uint32_t flags = dict_as_flags(state, w); if (flags & DEC_ERRORS) { if (flags & DEC_INVALID_SIGNALS) { @@ -838,7 +842,7 @@ static PyMethodDef signaldict_methods[] = { static PyType_Slot signaldict_slots[] = { {Py_tp_dealloc, signaldict_dealloc}, - {Py_tp_traverse, signaldict_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_repr, signaldict_repr}, {Py_tp_hash, PyObject_HashNotImplemented}, {Py_tp_getattro, PyObject_GenericGetAttr}, @@ -914,14 +918,34 @@ context_getallcr(PyObject *self, void *Py_UNUSED(closure)) } #endif +/*[clinic input] +_decimal.Context.Etiny + +Return a value equal to Emin - prec + 1. + +This is the minimum exponent value for subnormal results. When +underflow occurs, the exponent is set to Etiny. +[clinic start generated code]*/ + static PyObject * -context_getetiny(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Context_Etiny_impl(PyObject *self) +/*[clinic end generated code: output=c9a4a1a3e3575289 input=1274040f303f2244]*/ { return PyLong_FromSsize_t(mpd_etiny(CTX(self))); } +/*[clinic input] +_decimal.Context.Etop + +Return a value equal to Emax - prec + 1. + +This is the maximum exponent if the _clamp field of the context is +set to 1 (IEEE clamp mode). Etop() must not be negative. +[clinic start generated code]*/ + static PyObject * -context_getetop(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Context_Etop_impl(PyObject *self) +/*[clinic end generated code: output=f0a3f6e1b829074e input=35b9defc69d5e5d1]*/ { return PyLong_FromSsize_t(mpd_etop(CTX(self))); } @@ -987,16 +1011,19 @@ context_setemax(PyObject *self, PyObject *value, void *Py_UNUSED(closure)) } #ifdef CONFIG_32 +/*[clinic input] +_decimal.Context._unsafe_setprec + + x: Py_ssize_t + / + +[clinic start generated code]*/ + static PyObject * -context_unsafe_setprec(PyObject *self, PyObject *value) +_decimal_Context__unsafe_setprec_impl(PyObject *self, Py_ssize_t x) +/*[clinic end generated code: output=dd838edf08e12dd9 input=23a1b19ceb1569be]*/ { mpd_context_t *ctx = CTX(self); - mpd_ssize_t x; - - x = PyLong_AsSsize_t(value); - if (x == -1 && PyErr_Occurred()) { - return NULL; - } if (x < 1 || x > 1070000000L) { return value_error_ptr( @@ -1007,16 +1034,19 @@ context_unsafe_setprec(PyObject *self, PyObject *value) Py_RETURN_NONE; } +/*[clinic input] +_decimal.Context._unsafe_setemin + + x: Py_ssize_t + / + +[clinic start generated code]*/ + static PyObject * -context_unsafe_setemin(PyObject *self, PyObject *value) +_decimal_Context__unsafe_setemin_impl(PyObject *self, Py_ssize_t x) +/*[clinic end generated code: output=0c49cafee8a65846 input=652f1ecacca7e0ce]*/ { mpd_context_t *ctx = CTX(self); - mpd_ssize_t x; - - x = PyLong_AsSsize_t(value); - if (x == -1 && PyErr_Occurred()) { - return NULL; - } if (x < -1070000000L || x > 0) { return value_error_ptr( @@ -1027,16 +1057,19 @@ context_unsafe_setemin(PyObject *self, PyObject *value) Py_RETURN_NONE; } +/*[clinic input] +_decimal.Context._unsafe_setemax + + x: Py_ssize_t + / + +[clinic start generated code]*/ + static PyObject * -context_unsafe_setemax(PyObject *self, PyObject *value) +_decimal_Context__unsafe_setemax_impl(PyObject *self, Py_ssize_t x) +/*[clinic end generated code: output=776563e0377a00e8 input=b2a32a9a2750e7a8]*/ { mpd_context_t *ctx = CTX(self); - mpd_ssize_t x; - - x = PyLong_AsSsize_t(value); - if (x == -1 && PyErr_Occurred()) { - return NULL; - } if (x < 0 || x > 1070000000L) { return value_error_ptr( @@ -1361,15 +1394,29 @@ context_setattrs(PyObject *self, PyObject *prec, PyObject *rounding, return 0; } +/*[clinic input] +_decimal.Context.clear_traps + +Set all traps to False. +[clinic start generated code]*/ + static PyObject * -context_clear_traps(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Context_clear_traps_impl(PyObject *self) +/*[clinic end generated code: output=b47cfa6e32407d40 input=3872e80637148035]*/ { CTX(self)->traps = 0; Py_RETURN_NONE; } +/*[clinic input] +_decimal.Context.clear_flags + +Reset all flags to False. +[clinic start generated code]*/ + static PyObject * -context_clear_flags(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Context_clear_flags_impl(PyObject *self) +/*[clinic end generated code: output=c86719a70177d0b6 input=a06055e2f3e7edb1]*/ { CTX(self)->status = 0; Py_RETURN_NONE; @@ -1467,32 +1514,37 @@ context_dealloc(PyObject *self) Py_DECREF(tp); } -static int -context_init(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = { - "prec", "rounding", "Emin", "Emax", "capitals", "clamp", - "flags", "traps", NULL - }; - PyObject *prec = Py_None; - PyObject *rounding = Py_None; - PyObject *emin = Py_None; - PyObject *emax = Py_None; - PyObject *capitals = Py_None; - PyObject *clamp = Py_None; - PyObject *status = Py_None; - PyObject *traps = Py_None; - - assert(PyTuple_Check(args)); - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, - "|OOOOOOOO", kwlist, - &prec, &rounding, &emin, &emax, &capitals, &clamp, &status, &traps - )) { - return -1; - } +/*[clinic input] +_decimal.Context.__init__ as context_init + + prec: object = None + rounding: object = None + Emin as emin: object = None + Emax as emax: object = None + capitals: object = None + clamp: object = None + flags as status: object = None + traps: object = None +Create context. + +The context affects almost all operations and controls rounding, +Over/Underflow, raising of exceptions and much more. A new context +can be constructed as follows: + + >>> c = Context(prec=28, Emin=-425000000, Emax=425000000, + ... rounding=ROUND_HALF_EVEN, capitals=1, clamp=1, + ... traps=[InvalidOperation, DivisionByZero, Overflow], + ... flags=[]) + >>> +[clinic start generated code]*/ + +static int +context_init_impl(PyObject *self, PyObject *prec, PyObject *rounding, + PyObject *emin, PyObject *emax, PyObject *capitals, + PyObject *clamp, PyObject *status, PyObject *traps) +/*[clinic end generated code: output=8bfdc59fbe862f44 input=45c704b93cd02959]*/ +{ return context_setattrs( self, prec, rounding, emin, emax, capitals, @@ -1558,17 +1610,26 @@ init_extended_context(PyObject *v) } /* Factory function for creating IEEE interchange format contexts */ + +/*[clinic input] +_decimal.IEEEContext + + bits: Py_ssize_t + / + +Return a context, initialized as one of the IEEE interchange formats. + +The argument must be a multiple of 32 and less than +IEEE_CONTEXT_MAX_BITS. +[clinic start generated code]*/ + static PyObject * -ieee_context(PyObject *module, PyObject *v) +_decimal_IEEEContext_impl(PyObject *module, Py_ssize_t bits) +/*[clinic end generated code: output=19a35f320fe19789 input=5cff864d899eb2d7]*/ { PyObject *context; - mpd_ssize_t bits; mpd_context_t ctx; - bits = PyLong_AsSsize_t(v); - if (bits == -1 && PyErr_Occurred()) { - return NULL; - } if (bits <= 0 || bits > INT_MAX) { goto error; } @@ -1594,31 +1655,67 @@ ieee_context(PyObject *module, PyObject *v) } static PyObject * -context_copy(PyObject *self, PyObject *Py_UNUSED(dummy)) +context_copy(decimal_state *state, PyObject *v) { - PyObject *copy; + PyObject *copy = + PyObject_CallObject((PyObject *)state->PyDecContext_Type, NULL); - decimal_state *state = get_module_state_from_ctx(self); - copy = PyObject_CallObject((PyObject *)state->PyDecContext_Type, NULL); if (copy == NULL) { return NULL; } - *CTX(copy) = *CTX(self); + *CTX(copy) = *CTX(v); CTX(copy)->newtrap = 0; - CtxCaps(copy) = CtxCaps(self); + CtxCaps(copy) = CtxCaps(v); return copy; } +/*[clinic input] +_decimal.Context.copy + + cls: defining_class + +Return a duplicate of the context with all flags cleared. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_copy_impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=31c9c8eeb0c0cf77 input=aef1c0bddabdf8f0]*/ +{ + decimal_state *state = PyType_GetModuleState(cls); + + return context_copy(state, self); +} + +/*[clinic input] +_decimal.Context.__copy__ = _decimal.Context.copy + +[clinic start generated code]*/ + +static PyObject * +_decimal_Context___copy___impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=93552486e5fb0ab4 input=4a55dd22f6d31bcc]*/ +{ + decimal_state *state = PyType_GetModuleState(cls); + + return context_copy(state, self); +} + +/*[clinic input] +_decimal.Context.__reduce__ = _decimal.Context.copy + +[clinic start generated code]*/ + static PyObject * -context_reduce(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Context___reduce___impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=4e77de55efdbb56a input=787683f13d047ce8]*/ { PyObject *flags; PyObject *traps; PyObject *ret; mpd_context_t *ctx; - decimal_state *state = get_module_state_from_ctx(self); + decimal_state *state = PyType_GetModuleState(cls); ctx = CTX(self); @@ -1721,7 +1818,7 @@ current_context_from_dict(decimal_state *modstate) } /* Set up a new thread local context. */ - tl_context = context_copy(modstate->default_context_template, NULL); + tl_context = context_copy(modstate, modstate->default_context_template); if (tl_context == NULL) { return NULL; } @@ -1767,7 +1864,7 @@ current_context(decimal_state *modstate) /* Return a new reference to the current context */ static PyObject * -PyDec_GetCurrentContext(PyObject *self, PyObject *Py_UNUSED(dummy)) +PyDec_GetCurrentContext(PyObject *self) { PyObject *context; decimal_state *state = get_module_state(self); @@ -1797,7 +1894,7 @@ PyDec_SetCurrentContext(PyObject *self, PyObject *v) if (v == state->default_context_template || v == state->basic_context_template || v == state->extended_context_template) { - v = context_copy(v, NULL); + v = context_copy(state, v); if (v == NULL) { return NULL; } @@ -1820,7 +1917,7 @@ PyDec_SetCurrentContext(PyObject *self, PyObject *v) static PyObject * init_current_context(decimal_state *state) { - PyObject *tl_context = context_copy(state->default_context_template, NULL); + PyObject *tl_context = context_copy(state, state->default_context_template); if (tl_context == NULL) { return NULL; } @@ -1863,7 +1960,7 @@ current_context(decimal_state *state) /* Return a new reference to the current context */ static PyObject * -PyDec_GetCurrentContext(PyObject *self, PyObject *Py_UNUSED(dummy)) +PyDec_GetCurrentContext(PyObject *self) { decimal_state *state = get_module_state(self); return current_context(state); @@ -1881,7 +1978,7 @@ PyDec_SetCurrentContext(PyObject *self, PyObject *v) if (v == state->default_context_template || v == state->basic_context_template || v == state->extended_context_template) { - v = context_copy(v, NULL); + v = context_copy(state, v); if (v == NULL) { return NULL; } @@ -1902,36 +1999,73 @@ PyDec_SetCurrentContext(PyObject *self, PyObject *v) } #endif +/*[clinic input] +_decimal.getcontext + +Get the current default context. +[clinic start generated code]*/ + +static PyObject * +_decimal_getcontext_impl(PyObject *module) +/*[clinic end generated code: output=5982062c4d39e3dd input=7ac316fe42a1b6f5]*/ +{ + return PyDec_GetCurrentContext(module); +} + +/*[clinic input] +_decimal.setcontext + + context: object + / + +Set a new default context. +[clinic start generated code]*/ + +static PyObject * +_decimal_setcontext(PyObject *module, PyObject *context) +/*[clinic end generated code: output=8065f870be2852ce input=b57d7ee786b022a6]*/ +{ + return PyDec_SetCurrentContext(module, context); +} + /* Context manager object for the 'with' statement. The manager * owns one reference to the global (outer) context and one * to the local (inner) context. */ + +/*[clinic input] +@text_signature "($module, /, ctx=None, **kwargs)" +_decimal.localcontext + + ctx as local: object = None + * + prec: object = None + rounding: object = None + Emin: object = None + Emax: object = None + capitals: object = None + clamp: object = None + flags: object = None + traps: object = None + +Return a context manager for a copy of the supplied context. + +That will set the default context to a copy of ctx on entry to the +with-statement and restore the previous default context when exiting +the with-statement. If no context is specified, a copy of the current +default context is used. +[clinic start generated code]*/ + static PyObject * -ctxmanager_new(PyObject *m, PyObject *args, PyObject *kwds) +_decimal_localcontext_impl(PyObject *module, PyObject *local, PyObject *prec, + PyObject *rounding, PyObject *Emin, + PyObject *Emax, PyObject *capitals, + PyObject *clamp, PyObject *flags, PyObject *traps) +/*[clinic end generated code: output=9bf4e47742a809b0 input=490307b9689c3856]*/ { - static char *kwlist[] = { - "ctx", "prec", "rounding", - "Emin", "Emax", "capitals", - "clamp", "flags", "traps", - NULL - }; - PyObject *local = Py_None; PyObject *global; - PyObject *prec = Py_None; - PyObject *rounding = Py_None; - PyObject *Emin = Py_None; - PyObject *Emax = Py_None; - PyObject *capitals = Py_None; - PyObject *clamp = Py_None; - PyObject *flags = Py_None; - PyObject *traps = Py_None; - - decimal_state *state = get_module_state(m); + decimal_state *state = get_module_state(module); CURRENT_CONTEXT(state, global); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOOOOO", kwlist, &local, - &prec, &rounding, &Emin, &Emax, &capitals, &clamp, &flags, &traps)) { - return NULL; - } if (local == Py_None) { local = global; } @@ -1941,7 +2075,7 @@ ctxmanager_new(PyObject *m, PyObject *args, PyObject *kwds) return NULL; } - PyObject *local_copy = context_copy(local, NULL); + PyObject *local_copy = context_copy(state, local); if (local_copy == NULL) { return NULL; } @@ -2089,13 +2223,6 @@ PyDecType_New(decimal_state *state, PyTypeObject *type) } #define dec_alloc(st) PyDecType_New(st, (st)->PyDec_Type) -static int -dec_traverse(PyObject *dec, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(dec)); - return 0; -} - static void dec_dealloc(PyObject *dec) { @@ -2869,18 +2996,44 @@ PyDecType_FromSequenceExact(PyTypeObject *type, PyObject *v, #define PyDec_FromSequenceExact(st, sequence, context) \ PyDecType_FromSequenceExact((st)->PyDec_Type, sequence, context) -/* class method */ +/*[clinic input] +@permit_long_docstring_body +@classmethod +_decimal.Decimal.from_float + + cls: defining_class + f as pyfloat: object + / + +Class method that converts a float to a decimal number, exactly. + +Since 0.1 is not exactly representable in binary floating point, +Decimal.from_float(0.1) is not the same as Decimal('0.1'). + + >>> Decimal.from_float(0.1) + Decimal('0.1000000000000000055511151231257827021181583404541015625') + >>> Decimal.from_float(float('nan')) + Decimal('NaN') + >>> Decimal.from_float(float('inf')) + Decimal('Infinity') + >>> Decimal.from_float(float('-inf')) + Decimal('-Infinity') +[clinic start generated code]*/ + static PyObject * -dec_from_float(PyObject *type, PyObject *pyfloat) +_decimal_Decimal_from_float_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *pyfloat) +/*[clinic end generated code: output=fcb7d55d2f9dc790 input=29abf05dd8fe79e4]*/ { PyObject *context; PyObject *result; - decimal_state *state = get_module_state_by_def((PyTypeObject *)type); + decimal_state *state = PyType_GetModuleState(cls); CURRENT_CONTEXT(state, context); result = PyDecType_FromFloatExact(state->PyDec_Type, pyfloat, context); - if (type != (PyObject *)state->PyDec_Type && result != NULL) { - Py_SETREF(result, PyObject_CallFunctionObjArgs(type, result, NULL)); + if (type != state->PyDec_Type && result != NULL) { + Py_SETREF(result, + PyObject_CallFunctionObjArgs((PyObject *)type, result, NULL)); } return result; @@ -2890,9 +3043,10 @@ dec_from_float(PyObject *type, PyObject *pyfloat) an exact conversion. If the result does not meet the restrictions for an mpd_t, fail with InvalidOperation. */ static PyObject * -PyDecType_FromNumberExact(PyTypeObject *type, PyObject *v, PyObject *context) +PyDecType_FromNumberExact(PyTypeObject *type, PyTypeObject *cls, + PyObject *v, PyObject *context) { - decimal_state *state = get_module_state_by_def(type); + decimal_state *state = PyType_GetModuleState(cls); assert(v != NULL); if (PyDec_Check(state, v)) { return PyDecType_FromDecimalExact(type, v, context); @@ -2914,29 +3068,69 @@ PyDecType_FromNumberExact(PyTypeObject *type, PyObject *v, PyObject *context) } } -/* class method */ +/*[clinic input] +@permit_long_summary +@permit_long_docstring_body +@classmethod +_decimal.Decimal.from_number + + cls: defining_class + number: object + / + +Class method that converts a real number to a decimal number, exactly. + + >>> Decimal.from_number(314) # int + Decimal('314') + >>> Decimal.from_number(0.1) # float + Decimal('0.1000000000000000055511151231257827021181583404541015625') + >>> Decimal.from_number(Decimal('3.14')) # another decimal instance + Decimal('3.14') +[clinic start generated code]*/ + static PyObject * -dec_from_number(PyObject *type, PyObject *number) +_decimal_Decimal_from_number_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *number) +/*[clinic end generated code: output=4d3ec722b7acfd8b input=34ff3696955d3def]*/ { PyObject *context; PyObject *result; - decimal_state *state = get_module_state_by_def((PyTypeObject *)type); + decimal_state *state = PyType_GetModuleState(cls); CURRENT_CONTEXT(state, context); - result = PyDecType_FromNumberExact(state->PyDec_Type, number, context); - if (type != (PyObject *)state->PyDec_Type && result != NULL) { - Py_SETREF(result, PyObject_CallFunctionObjArgs(type, result, NULL)); + result = PyDecType_FromNumberExact(state->PyDec_Type, cls, number, context); + if (type != state->PyDec_Type && result != NULL) { + Py_SETREF(result, + PyObject_CallFunctionObjArgs((PyObject *)type, result, NULL)); } return result; } /* create_decimal_from_float */ + +/*[clinic input] +_decimal.Context.create_decimal_from_float + + self as context: self + cls: defining_class + f: object + / + +Create a new Decimal instance from float f. + +Unlike the Decimal.from_float() class method, this function observes +the context limits. +[clinic start generated code]*/ + static PyObject * -ctx_from_float(PyObject *context, PyObject *v) +_decimal_Context_create_decimal_from_float_impl(PyObject *context, + PyTypeObject *cls, + PyObject *f) +/*[clinic end generated code: output=a5548f5140fa0870 input=8c66eeb22b01ddd4]*/ { - decimal_state *state = get_module_state_from_ctx(context); - return PyDec_FromFloat(state, v, context); + decimal_state *state = PyType_GetModuleState(cls); + return PyDec_FromFloat(state, f, context); } /* Apply the context to the input operand. Return a new PyDecObject. */ @@ -3053,33 +3247,49 @@ PyDec_FromObject(PyObject *v, PyObject *context) } } +/*[clinic input] +@classmethod +_decimal.Decimal.__new__ as dec_new + + value: object(c_default="NULL") = "0" + context: object = None + +Construct a new Decimal object. + +value can be an integer, string, tuple, or another Decimal object. If +no value is given, return Decimal('0'). The context does not affect +the conversion and is only passed to determine if the InvalidOperation +trap is active. +[clinic start generated code]*/ + static PyObject * -dec_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +dec_new_impl(PyTypeObject *type, PyObject *value, PyObject *context) +/*[clinic end generated code: output=35f48a40c65625ba input=5f8a0892d3fcef80]*/ { - static char *kwlist[] = {"value", "context", NULL}; - PyObject *v = NULL; - PyObject *context = Py_None; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, - &v, &context)) { - return NULL; - } decimal_state *state = get_module_state_by_def(type); CONTEXT_CHECK_VA(state, context); - return PyDecType_FromObjectExact(type, v, context); + return PyDecType_FromObjectExact(type, value, context); } -static PyObject * -ctx_create_decimal(PyObject *context, PyObject *args) -{ - PyObject *v = NULL; +/*[clinic input] +_decimal.Context.create_decimal - if (!PyArg_ParseTuple(args, "|O", &v)) { - return NULL; - } + self as context: self + num: object(c_default="NULL") = "0" + / + +Create a new Decimal instance from num, using self as the context. - return PyDec_FromObject(v, context); +Unlike the Decimal constructor, this function observes the context +limits. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_create_decimal_impl(PyObject *context, PyObject *num) +/*[clinic end generated code: output=85e08ae02f3b34da input=d2c4946cf7804fbe]*/ +{ + return PyDec_FromObject(num, context); } @@ -3520,15 +3730,28 @@ pydec_format(PyObject *dec, PyObject *context, PyObject *fmt, decimal_state *sta } /* Formatted representation of a PyDecObject. */ + +/*[clinic input] +_decimal.Decimal.__format__ + + self as dec: self + cls: defining_class + format_spec as fmtarg: unicode + override: object = NULL + / + +Formats the Decimal according to format_spec. +[clinic start generated code]*/ + static PyObject * -dec_format(PyObject *dec, PyObject *args) +_decimal_Decimal___format___impl(PyObject *dec, PyTypeObject *cls, + PyObject *fmtarg, PyObject *override) +/*[clinic end generated code: output=6d95f91bbb28b3ed input=2dbfaa0cbe243e9e]*/ { PyObject *result = NULL; - PyObject *override = NULL; PyObject *dot = NULL; PyObject *sep = NULL; PyObject *grouping = NULL; - PyObject *fmtarg; PyObject *context; mpd_spec_t spec; char *fmt; @@ -3536,42 +3759,30 @@ dec_format(PyObject *dec, PyObject *args) uint32_t status = 0; int replace_fillchar = 0; Py_ssize_t size; - - - decimal_state *state = get_module_state_by_def(Py_TYPE(dec)); + decimal_state *state = PyType_GetModuleState(cls); CURRENT_CONTEXT(state, context); - if (!PyArg_ParseTuple(args, "O|O", &fmtarg, &override)) { + fmt = (char *)PyUnicode_AsUTF8AndSize(fmtarg, &size); + if (fmt == NULL) { return NULL; } - if (PyUnicode_Check(fmtarg)) { - fmt = (char *)PyUnicode_AsUTF8AndSize(fmtarg, &size); - if (fmt == NULL) { + if (size > 0 && fmt[size-1] == 'N') { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Format specifier 'N' is deprecated and " + "slated for removal in Python 3.18", 1) < 0) { return NULL; } + } - if (size > 0 && fmt[size-1] == 'N') { - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "Format specifier 'N' is deprecated", 1) < 0) { - return NULL; - } - } - - if (size > 0 && fmt[0] == '\0') { - /* NUL fill character: must be replaced with a valid UTF-8 char - before calling mpd_parse_fmt_str(). */ - replace_fillchar = 1; - fmt = dec_strdup(fmt, size); - if (fmt == NULL) { - return NULL; - } - fmt[0] = '_'; + if (size > 0 && fmt[0] == '\0') { + /* NUL fill character: must be replaced with a valid UTF-8 char + before calling mpd_parse_fmt_str(). */ + replace_fillchar = 1; + fmt = dec_strdup(fmt, size); + if (fmt == NULL) { + return NULL; } - } - else { - PyErr_SetString(PyExc_TypeError, - "format arg must be str"); - return NULL; + fmt[0] = '_'; } if (!mpd_parse_fmt_str(&spec, fmt, CtxCaps(context))) { @@ -3750,14 +3961,25 @@ dec_as_long(PyObject *dec, PyObject *context, int round) return PyLongWriter_Finish(writer); } -/* Convert a Decimal to its exact integer ratio representation. */ +/*[clinic input] +@permit_long_summary +_decimal.Decimal.as_integer_ratio + + cls: defining_class + +Return a pair of integers whose ratio is exactly equal to the original. + +The ratio is in lowest terms and with a positive denominator. +Raise OverflowError on infinities and a ValueError on NaNs. +[clinic start generated code]*/ + static PyObject * -dec_as_integer_ratio(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal_as_integer_ratio_impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=eb49c512701f844b input=136f1dc585ca8d80]*/ { PyObject *numerator = NULL; PyObject *denominator = NULL; PyObject *exponent = NULL; - PyObject *result = NULL; PyObject *tmp; mpd_ssize_t exp; PyObject *context; @@ -3775,7 +3997,7 @@ dec_as_integer_ratio(PyObject *self, PyObject *Py_UNUSED(dummy)) return NULL; } - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); + decimal_state *state = PyType_GetModuleState(cls); CURRENT_CONTEXT(state, context); tmp = dec_alloc(state); @@ -3817,6 +4039,7 @@ dec_as_integer_ratio(PyObject *self, PyObject *Py_UNUSED(dummy)) if (exp >= 0) { Py_SETREF(numerator, state->_py_long_multiply(numerator, exponent)); + Py_CLEAR(exponent); if (numerator == NULL) { goto error; } @@ -3843,32 +4066,40 @@ dec_as_integer_ratio(PyObject *self, PyObject *Py_UNUSED(dummy)) goto error; } } - - result = PyTuple_Pack(2, numerator, denominator); - + return _PyTuple_FromPairSteal(numerator, denominator); error: Py_XDECREF(exponent); Py_XDECREF(denominator); Py_XDECREF(numerator); - return result; + return NULL; } +/*[clinic input] +_decimal.Decimal.to_integral_value + + cls: defining_class + rounding: object = None + context: object = None + +Round to the nearest integer without signaling Inexact or Rounded. + +The rounding mode is determined by the rounding parameter if given, +else by the given context. If neither parameter is given, then the +rounding mode of the current default context is used. +[clinic start generated code]*/ + static PyObject * -PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds) +_decimal_Decimal_to_integral_value_impl(PyObject *self, PyTypeObject *cls, + PyObject *rounding, + PyObject *context) +/*[clinic end generated code: output=23047d848ef84db1 input=85aa9499a21ea8d7]*/ { - static char *kwlist[] = {"rounding", "context", NULL}; PyObject *result; - PyObject *rounding = Py_None; - PyObject *context = Py_None; uint32_t status = 0; mpd_context_t workctx; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, - &rounding, &context)) { - return NULL; - } - decimal_state *state = get_module_state_by_def(Py_TYPE(dec)); + decimal_state *state = PyType_GetModuleState(cls); CONTEXT_CHECK_VA(state, context); workctx = *CTX(context); @@ -3887,7 +4118,7 @@ PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds) return NULL; } - mpd_qround_to_int(MPD(result), MPD(dec), &workctx, &status); + mpd_qround_to_int(MPD(result), MPD(self), &workctx, &status); if (dec_addstatus(context, status)) { Py_DECREF(result); return NULL; @@ -3896,21 +4127,46 @@ PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds) return result; } +/*[clinic input] +_decimal.Decimal.to_integral = _decimal.Decimal.to_integral_value + +Identical to the to_integral_value() method. + +The to_integral() name has been kept for compatibility with older +versions. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_to_integral_impl(PyObject *self, PyTypeObject *cls, + PyObject *rounding, PyObject *context) +/*[clinic end generated code: output=5dac8f54c2a3ed26 input=709b54618ecd0d8b]*/ +{ + return _decimal_Decimal_to_integral_value_impl(self, cls, rounding, + context); +} + +/*[clinic input] +_decimal.Decimal.to_integral_exact = _decimal.Decimal.to_integral_value + +Round to the nearest integer. + +This method signals Inexact or Rounded as appropriate if rounding +occurs. The rounding mode is determined by the rounding parameter +if given, else by the given context. If neither parameter is given, +then the rounding mode of the current default context is used. +[clinic start generated code]*/ + static PyObject * -PyDec_ToIntegralExact(PyObject *dec, PyObject *args, PyObject *kwds) +_decimal_Decimal_to_integral_exact_impl(PyObject *self, PyTypeObject *cls, + PyObject *rounding, + PyObject *context) +/*[clinic end generated code: output=543a39a02eea9917 input=d4d8abe543393de1]*/ { - static char *kwlist[] = {"rounding", "context", NULL}; PyObject *result; - PyObject *rounding = Py_None; - PyObject *context = Py_None; uint32_t status = 0; mpd_context_t workctx; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, - &rounding, &context)) { - return NULL; - } - decimal_state *state = get_module_state_by_def(Py_TYPE(dec)); + decimal_state *state = PyType_GetModuleState(cls); CONTEXT_CHECK_VA(state, context); workctx = *CTX(context); @@ -3929,7 +4185,7 @@ PyDec_ToIntegralExact(PyObject *dec, PyObject *args, PyObject *kwds) return NULL; } - mpd_qround_to_intx(MPD(result), MPD(dec), &workctx, &status); + mpd_qround_to_intx(MPD(result), MPD(self), &workctx, &status); if (dec_addstatus(context, status)) { Py_DECREF(result); return NULL; @@ -3970,32 +4226,38 @@ PyDec_AsFloat(PyObject *dec) return f; } +/*[clinic input] +_decimal.Decimal.__round__ + + cls: defining_class + ndigits: object = NULL + / + +Return the Integral closest to self, rounding half toward even. +[clinic start generated code]*/ + static PyObject * -PyDec_Round(PyObject *dec, PyObject *args) +_decimal_Decimal___round___impl(PyObject *self, PyTypeObject *cls, + PyObject *ndigits) +/*[clinic end generated code: output=790c2c6bd57890e6 input=d69e7178a58a66b1]*/ { PyObject *result; - PyObject *x = NULL; uint32_t status = 0; PyObject *context; - - decimal_state *state = get_module_state_by_def(Py_TYPE(dec)); + decimal_state *state = PyType_GetModuleState(cls); CURRENT_CONTEXT(state, context); - if (!PyArg_ParseTuple(args, "|O", &x)) { - return NULL; - } - - if (x) { + if (ndigits) { mpd_uint_t dq[1] = {1}; mpd_t q = {MPD_STATIC|MPD_CONST_DATA,0,1,1,1,dq}; mpd_ssize_t y; - if (!PyLong_Check(x)) { + if (!PyLong_Check(ndigits)) { PyErr_SetString(PyExc_TypeError, "optional arg must be an integer"); return NULL; } - y = PyLong_AsSsize_t(x); + y = PyLong_AsSsize_t(ndigits); if (y == -1 && PyErr_Occurred()) { return NULL; } @@ -4005,7 +4267,7 @@ PyDec_Round(PyObject *dec, PyObject *args) } q.exp = (y == MPD_SSIZE_MIN) ? MPD_SSIZE_MAX : -y; - mpd_qquantize(MPD(result), MPD(dec), &q, CTX(context), &status); + mpd_qquantize(MPD(result), MPD(self), &q, CTX(context), &status); if (dec_addstatus(context, status)) { Py_DECREF(result); return NULL; @@ -4014,13 +4276,21 @@ PyDec_Round(PyObject *dec, PyObject *args) return result; } else { - return dec_as_long(dec, context, MPD_ROUND_HALF_EVEN); + return dec_as_long(self, context, MPD_ROUND_HALF_EVEN); } } -/* Return the DecimalTuple representation of a PyDecObject. */ +/*[clinic input] +_decimal.Decimal.as_tuple + + cls: defining_class + +Return a tuple representation of the number. +[clinic start generated code]*/ + static PyObject * -PyDec_AsTuple(PyObject *dec, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal_as_tuple_impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=d68b967becee8ab9 input=bfa86d640224d9f5]*/ { PyObject *result = NULL; PyObject *sign = NULL; @@ -4032,13 +4302,13 @@ PyDec_AsTuple(PyObject *dec, PyObject *Py_UNUSED(dummy)) Py_ssize_t intlen, i; - x = mpd_qncopy(MPD(dec)); + x = mpd_qncopy(MPD(self)); if (x == NULL) { PyErr_NoMemory(); goto out; } - sign = PyLong_FromUnsignedLong(mpd_sign(MPD(dec))); + sign = PyLong_FromUnsignedLong(mpd_sign(MPD(self))); if (sign == NULL) { goto out; } @@ -4059,7 +4329,7 @@ PyDec_AsTuple(PyObject *dec, PyObject *Py_UNUSED(dummy)) expt = PyUnicode_FromString(mpd_isqnan(x)?"n":"N"); } else { - expt = PyLong_FromSsize_t(MPD(dec)->exp); + expt = PyLong_FromSsize_t(MPD(self)->exp); } if (expt == NULL) { goto out; @@ -4100,7 +4370,7 @@ PyDec_AsTuple(PyObject *dec, PyObject *Py_UNUSED(dummy)) } } - decimal_state *state = get_module_state_by_def(Py_TYPE(dec)); + decimal_state *state = PyType_GetModuleState(cls); result = PyObject_CallFunctionObjArgs((PyObject *)state->DecimalTuple, sign, coeff, expt, NULL); @@ -4152,7 +4422,7 @@ nm_##MPDFUNC(PyObject *self, PyObject *other) \ PyObject *context; \ uint32_t status = 0; \ \ - decimal_state *state = find_state_left_or_right(self, other); \ + decimal_state *state = find_state_left_or_right(self, other); \ CURRENT_CONTEXT(state, context) ; \ CONVERT_BINOP(&a, &b, self, other, context); \ \ @@ -4173,48 +4443,35 @@ nm_##MPDFUNC(PyObject *self, PyObject *other) \ return result; \ } -/* Boolean function without a context arg. */ +/* Boolean function without a context arg. + Argument Clinic provides PyObject *self +*/ #define Dec_BoolFunc(MPDFUNC) \ -static PyObject * \ -dec_##MPDFUNC(PyObject *self, PyObject *Py_UNUSED(dummy)) \ { \ return MPDFUNC(MPD(self)) ? incr_true() : incr_false(); \ } -/* Boolean function with an optional context arg. */ +/* Boolean function with an optional context arg. + Argument Clinic provides PyObject *self, PyTypeObject *cls, + PyObject *context +*/ #define Dec_BoolFuncVA(MPDFUNC) \ -static PyObject * \ -dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ { \ - static char *kwlist[] = {"context", NULL}; \ - PyObject *context = Py_None; \ - \ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \ - &context)) { \ - return NULL; \ - } \ - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); \ + decimal_state *state = PyType_GetModuleState(cls); \ CONTEXT_CHECK_VA(state, context); \ \ return MPDFUNC(MPD(self), CTX(context)) ? incr_true() : incr_false(); \ } -/* Unary function with an optional context arg. */ +/* Unary function with an optional context arg. + Argument Clinic provides PyObject *self, PyTypeObject *cls, + PyObject *context +*/ #define Dec_UnaryFuncVA(MPDFUNC) \ -static PyObject * \ -dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ { \ - static char *kwlist[] = {"context", NULL}; \ PyObject *result; \ - PyObject *context = Py_None; \ uint32_t status = 0; \ - \ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, \ - &context)) { \ - return NULL; \ - } \ - decimal_state *state = \ - get_module_state_by_def(Py_TYPE(self)); \ + decimal_state *state = PyType_GetModuleState(cls); \ CONTEXT_CHECK_VA(state, context); \ \ if ((result = dec_alloc(state)) == NULL) { \ @@ -4230,24 +4487,16 @@ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ return result; \ } -/* Binary function with an optional context arg. */ +/* Binary function with an optional context arg. + Argument Clinic provides PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context +*/ #define Dec_BinaryFuncVA(MPDFUNC) \ -static PyObject * \ -dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ { \ - static char *kwlist[] = {"other", "context", NULL}; \ - PyObject *other; \ PyObject *a, *b; \ PyObject *result; \ - PyObject *context = Py_None; \ uint32_t status = 0; \ - \ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, \ - &other, &context)) { \ - return NULL; \ - } \ - decimal_state *state = \ - get_module_state_by_def(Py_TYPE(self)); \ + decimal_state *state = PyType_GetModuleState(cls); \ CONTEXT_CHECK_VA(state, context); \ CONVERT_BINOP_RAISE(&a, &b, self, other, context); \ \ @@ -4270,23 +4519,16 @@ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ /* Binary function with an optional context arg. Actual MPDFUNC does NOT take a context. The context is used to record InvalidOperation - if the second operand cannot be converted exactly. */ + if the second operand cannot be converted exactly. + + Argument Clinic provides PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context +*/ #define Dec_BinaryFuncVA_NO_CTX(MPDFUNC) \ -static PyObject * \ -dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ { \ - static char *kwlist[] = {"other", "context", NULL}; \ - PyObject *context = Py_None; \ - PyObject *other; \ PyObject *a, *b; \ PyObject *result; \ - \ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, \ - &other, &context)) { \ - return NULL; \ - } \ - decimal_state *state = \ - get_module_state_by_def(Py_TYPE(self)); \ + decimal_state *state = PyType_GetModuleState(cls); \ CONTEXT_CHECK_VA(state, context); \ CONVERT_BINOP_RAISE(&a, &b, self, other, context); \ \ @@ -4303,23 +4545,17 @@ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ return result; \ } -/* Ternary function with an optional context arg. */ +/* Ternary function with an optional context arg. + Argument Clinic provides PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *third, + PyObject *context +*/ #define Dec_TernaryFuncVA(MPDFUNC) \ -static PyObject * \ -dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds) \ { \ - static char *kwlist[] = {"other", "third", "context", NULL}; \ - PyObject *other, *third; \ PyObject *a, *b, *c; \ PyObject *result; \ - PyObject *context = Py_None; \ uint32_t status = 0; \ - \ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, \ - &other, &third, &context)) { \ - return NULL; \ - } \ - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); \ + decimal_state *state = PyType_GetModuleState(cls); \ CONTEXT_CHECK_VA(state, context); \ CONVERT_TERNOP_RAISE(&a, &b, &c, self, other, third, context); \ \ @@ -4380,7 +4616,6 @@ nm_mpd_qdivmod(PyObject *v, PyObject *w) PyObject *q, *r; PyObject *context; uint32_t status = 0; - PyObject *ret; decimal_state *state = find_state_left_or_right(v, w); CURRENT_CONTEXT(state, context); @@ -4409,10 +4644,7 @@ nm_mpd_qdivmod(PyObject *v, PyObject *w) return NULL; } - ret = PyTuple_Pack(2, q, r); - Py_DECREF(r); - Py_DECREF(q); - return ret; + return _PyTuple_FromPairSteal(q, r); } static PyObject * @@ -4468,48 +4700,408 @@ nm_mpd_qpow(PyObject *base, PyObject *exp, PyObject *mod) /******************************************************************************/ /* Unary arithmetic functions, optional context arg */ -Dec_UnaryFuncVA(mpd_qexp) -Dec_UnaryFuncVA(mpd_qln) -Dec_UnaryFuncVA(mpd_qlog10) -Dec_UnaryFuncVA(mpd_qnext_minus) -Dec_UnaryFuncVA(mpd_qnext_plus) -Dec_UnaryFuncVA(mpd_qreduce) -Dec_UnaryFuncVA(mpd_qsqrt) -/* Binary arithmetic functions, optional context arg */ -Dec_BinaryFuncVA(mpd_qcompare) -Dec_BinaryFuncVA(mpd_qcompare_signal) -Dec_BinaryFuncVA(mpd_qmax) -Dec_BinaryFuncVA(mpd_qmax_mag) -Dec_BinaryFuncVA(mpd_qmin) -Dec_BinaryFuncVA(mpd_qmin_mag) -Dec_BinaryFuncVA(mpd_qnext_toward) -Dec_BinaryFuncVA(mpd_qrem_near) +/*[clinic input] +_decimal.Decimal.exp -/* Ternary arithmetic functions, optional context arg */ -Dec_TernaryFuncVA(mpd_qfma) + cls: defining_class + context: object = None -/* Boolean functions, no context arg */ -Dec_BoolFunc(mpd_iscanonical) -Dec_BoolFunc(mpd_isfinite) -Dec_BoolFunc(mpd_isinfinite) -Dec_BoolFunc(mpd_isnan) -Dec_BoolFunc(mpd_isqnan) -Dec_BoolFunc(mpd_issnan) -Dec_BoolFunc(mpd_issigned) -Dec_BoolFunc(mpd_iszero) +Return the value of the (natural) exponential function e**x. -/* Boolean functions, optional context arg */ -Dec_BoolFuncVA(mpd_isnormal) -Dec_BoolFuncVA(mpd_issubnormal) +The function always uses the ROUND_HALF_EVEN mode and the result is +correctly rounded. +[clinic start generated code]*/ -/* Unary functions, no context arg */ static PyObject * -dec_mpd_adjexp(PyObject *self, PyObject *Py_UNUSED(dummy)) -{ - mpd_ssize_t retval; +_decimal_Decimal_exp_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=40317012aedbaeac input=84919aad3dabda08]*/ +Dec_UnaryFuncVA(mpd_qexp) - if (mpd_isspecial(MPD(self))) { +/*[clinic input] +_decimal.Decimal.ln = _decimal.Decimal.exp + +Return the natural (base e) logarithm of the operand. + +The function always uses the ROUND_HALF_EVEN mode and the result is +correctly rounded. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_ln_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=e8f9e81cac38e5dc input=d353c51ec00d1cff]*/ +Dec_UnaryFuncVA(mpd_qln) + +/*[clinic input] +_decimal.Decimal.log10 = _decimal.Decimal.exp + +Return the base ten logarithm of the operand. + +The function always uses the ROUND_HALF_EVEN mode and the result is +correctly rounded. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_log10_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=00b3255648135c95 input=48a6be60154c0b46]*/ +Dec_UnaryFuncVA(mpd_qlog10) + +/*[clinic input] +_decimal.Decimal.next_minus = _decimal.Decimal.exp + +Returns the largest representable number smaller than itself. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_next_minus_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=a187a55e6976b572 input=666b348f71e6c090]*/ +Dec_UnaryFuncVA(mpd_qnext_minus) + +/*[clinic input] +_decimal.Decimal.next_plus = _decimal.Decimal.exp + +Returns the smallest representable number larger than itself. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_next_plus_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=13737d41714e320e input=04e105060ad1fa15]*/ +Dec_UnaryFuncVA(mpd_qnext_plus) + +/*[clinic input] +_decimal.Decimal.normalize = _decimal.Decimal.exp + +Normalize the number by stripping trailing 0s + +This also change anything equal to 0 to 0e0. Used for producing +canonical values for members of an equivalence class. For example, +Decimal('32.100') and Decimal('0.321000e+2') both normalize to +the equivalent value Decimal('32.1'). +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_normalize_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=32c4c0d13fe33fb9 input=d5ee63acd904d4de]*/ +Dec_UnaryFuncVA(mpd_qreduce) + +/*[clinic input] +_decimal.Decimal.sqrt = _decimal.Decimal.exp + +Return the square root of the argument to full precision. + +The result is correctly rounded using the ROUND_HALF_EVEN rounding +mode. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_sqrt_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=deb1280077b5e586 input=c565a7216e9605e7]*/ +Dec_UnaryFuncVA(mpd_qsqrt) + +/* Binary arithmetic functions, optional context arg */ + +/*[clinic input] +_decimal.Decimal.compare + + cls: defining_class + other: object + context: object = None + +Compare self to other. + +Return a decimal value: + + a or b is a NaN ==> Decimal('NaN') + a < b ==> Decimal('-1') + a == b ==> Decimal('0') + a > b ==> Decimal('1') +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_compare_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=a4a1d383ec192cfa input=d18a02bb8083e92a]*/ +Dec_BinaryFuncVA(mpd_qcompare) + +/*[clinic input] +_decimal.Decimal.compare_signal = _decimal.Decimal.compare + +Identical to compare, except that all NaNs signal. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_compare_signal_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=22f757371fd4167b input=a52a39d1c6fc369d]*/ +Dec_BinaryFuncVA(mpd_qcompare_signal) + +/*[clinic input] +_decimal.Decimal.max = _decimal.Decimal.compare + +Maximum of self and other. + +If one operand is a quiet NaN and the other is numeric, the numeric +operand is returned. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_max_impl(PyObject *self, PyTypeObject *cls, PyObject *other, + PyObject *context) +/*[clinic end generated code: output=d3d12db9815869e5 input=2ae2582f551296d8]*/ +Dec_BinaryFuncVA(mpd_qmax) + +/*[clinic input] +@permit_long_summary +_decimal.Decimal.max_mag = _decimal.Decimal.compare + +As the max() method, but compares the absolute values of the operands. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_max_mag_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=f71f2c27d9bc7cac input=5f81b9da49b45e5d]*/ +Dec_BinaryFuncVA(mpd_qmax_mag) + +/*[clinic input] +_decimal.Decimal.min = _decimal.Decimal.compare + +Minimum of self and other. + +If one operand is a quiet NaN and the other is numeric, the numeric +operand is returned. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_min_impl(PyObject *self, PyTypeObject *cls, PyObject *other, + PyObject *context) +/*[clinic end generated code: output=c5620344ae5f3dd1 input=2a70f2c087c418c9]*/ +Dec_BinaryFuncVA(mpd_qmin) + +/*[clinic input] +@permit_long_summary +_decimal.Decimal.min_mag = _decimal.Decimal.compare + +As the min() method, but compares the absolute values of the operands. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_min_mag_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=018562ad1c22aae3 input=94c29817c7f16db7]*/ +Dec_BinaryFuncVA(mpd_qmin_mag) + +/*[clinic input] +_decimal.Decimal.next_toward = _decimal.Decimal.compare + +Returns the number closest to self, in the direction towards other. + +If the two operands are unequal, return the number closest to the +first operand in the direction of the second operand. If both +operands are numerically equal, return a copy of the first operand +with the sign set to be the same as the sign of the second operand. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_next_toward_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=71d879bca8bc1019 input=adc5d453fc140341]*/ +Dec_BinaryFuncVA(mpd_qnext_toward) + +/*[clinic input] +_decimal.Decimal.remainder_near = _decimal.Decimal.compare + +Return the remainder from dividing self by other. + +This differs from self % other in that the sign of the remainder is +chosen so as to minimize its absolute value. More precisely, the +return value is self - n * other where n is the integer nearest to +the exact value of self / other, and if two integers are equally +near then the even one is chosen. + +If the result is zero then its sign will be the sign of self. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_remainder_near_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=d3fbb4985f2077fa input=dcb66d4afa0c77c3]*/ +Dec_BinaryFuncVA(mpd_qrem_near) + +/* Ternary arithmetic functions, optional context arg */ + +/*[clinic input] +_decimal.Decimal.fma + + cls: defining_class + other: object + third: object + context: object = None + +Fused multiply-add. + +Return self*other+third with no rounding of the intermediate product +self*other. + + >>> Decimal(2).fma(3, 5) + Decimal('11') +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_fma_impl(PyObject *self, PyTypeObject *cls, PyObject *other, + PyObject *third, PyObject *context) +/*[clinic end generated code: output=db49a777e85b71e4 input=2104c001f6077c35]*/ +Dec_TernaryFuncVA(mpd_qfma) + +/* Boolean functions, no context arg */ + +/*[clinic input] +_decimal.Decimal.is_canonical + +Return True if the argument is canonical and False otherwise. + +Currently, a Decimal instance is always canonical, so this operation +always returns True. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_is_canonical_impl(PyObject *self) +/*[clinic end generated code: output=b29668684f45443e input=b3b3e6878ccf40b8]*/ +Dec_BoolFunc(mpd_iscanonical) + +/*[clinic input] +_decimal.Decimal.is_finite + +Return True if the argument is a finite number, and False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_is_finite_impl(PyObject *self) +/*[clinic end generated code: output=537306fbfc9131f8 input=e9b8b5866704bae6]*/ +Dec_BoolFunc(mpd_isfinite) + +/*[clinic input] +_decimal.Decimal.is_infinite + +Return True if the argument is infinite, and False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_is_infinite_impl(PyObject *self) +/*[clinic end generated code: output=31b775ff28f05ce2 input=8f3937a790ee4ec2]*/ +Dec_BoolFunc(mpd_isinfinite) + +/*[clinic input] +@permit_long_summary +_decimal.Decimal.is_nan + +Return True if the argument is a (quiet or signaling) NaN, else False. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_is_nan_impl(PyObject *self) +/*[clinic end generated code: output=b704e8b49a164388 input=b7d8f0d59fe2332a]*/ +Dec_BoolFunc(mpd_isnan) + +/*[clinic input] +_decimal.Decimal.is_qnan + +Return True if the argument is a quiet NaN, and False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_is_qnan_impl(PyObject *self) +/*[clinic end generated code: output=85b5241f43798376 input=00485f3c3cfae0af]*/ +Dec_BoolFunc(mpd_isqnan) + +/*[clinic input] +_decimal.Decimal.is_snan + +Return True if the argument is a signaling NaN and False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_is_snan_impl(PyObject *self) +/*[clinic end generated code: output=50de9ec6507e4a4f input=f3b0f8592c921879]*/ +Dec_BoolFunc(mpd_issnan) + +/*[clinic input] +_decimal.Decimal.is_signed + +Return True if the argument has a negative sign and False otherwise. + +Note that both zeros and NaNs can carry signs. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_is_signed_impl(PyObject *self) +/*[clinic end generated code: output=8ec7bc85d8e755e4 input=97c3437ab5dffecc]*/ +Dec_BoolFunc(mpd_issigned) + +/*[clinic input] +_decimal.Decimal.is_zero + +Return True if the argument is a zero and False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_is_zero_impl(PyObject *self) +/*[clinic end generated code: output=2d87ea1b15879112 input=ae616674cd050a51]*/ +Dec_BoolFunc(mpd_iszero) + +/* Boolean functions, optional context arg */ + +/*[clinic input] +_decimal.Decimal.is_normal = _decimal.Decimal.exp + +Return True if the argument is a normal number and False otherwise. + +Normal number is a finite nonzero number, which is not subnormal. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_is_normal_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=92a3878e293758d4 input=9afe43b9db9f4818]*/ +Dec_BoolFuncVA(mpd_isnormal) + +/*[clinic input] +_decimal.Decimal.is_subnormal = _decimal.Decimal.exp + +Return True if the argument is subnormal, and False otherwise. + +A number is subnormal if it is non-zero, finite, and has an adjusted +exponent less than Emin. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_is_subnormal_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=1404c04d980ebc07 input=11839c122c185b8b]*/ +Dec_BoolFuncVA(mpd_issubnormal) + +/* Unary functions, no context arg */ + +/*[clinic input] +_decimal.Decimal.adjusted + +Return the adjusted exponent (exp + digits - 1) of the number. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_adjusted_impl(PyObject *self) +/*[clinic end generated code: output=21ea2c9f23994c52 input=8ba2029d8d906b18]*/ +{ + mpd_ssize_t retval; + + if (mpd_isspecial(MPD(self))) { retval = 0; } else { @@ -4519,14 +5111,31 @@ dec_mpd_adjexp(PyObject *self, PyObject *Py_UNUSED(dummy)) return PyLong_FromSsize_t(retval); } +/*[clinic input] +_decimal.Decimal.canonical + +Return the canonical encoding of the argument. + +Currently, the encoding of a Decimal instance is always canonical, +so this operation returns its argument unchanged. +[clinic start generated code]*/ + static PyObject * -dec_canonical(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal_canonical_impl(PyObject *self) +/*[clinic end generated code: output=3cbeb47d91e6da2d input=8a4719d14c52d521]*/ { return Py_NewRef(self); } +/*[clinic input] +_decimal.Decimal.conjugate + +Return self. +[clinic start generated code]*/ + static PyObject * -dec_conjugate(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal_conjugate_impl(PyObject *self) +/*[clinic end generated code: output=9a37bf633f25a291 input=c7179975ef74fd84]*/ { return Py_NewRef(self); } @@ -4545,20 +5154,44 @@ _dec_mpd_radix(decimal_state *state) return result; } +/*[clinic input] +_decimal.Decimal.radix + + cls: defining_class + +Return Decimal(10). + +This is the radix (base) in which the Decimal class does all its +arithmetic. Included for compatibility with the specification. +[clinic start generated code]*/ + static PyObject * -dec_mpd_radix(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal_radix_impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=40a3bc7ec3d99228 input=d1cdbdbbbdefdec2]*/ { - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); + decimal_state *state = PyType_GetModuleState(cls); return _dec_mpd_radix(state); } +/*[clinic input] +_decimal.Decimal.copy_abs + + cls: defining_class + +Return the absolute value of the argument. + +This operation is unaffected by context and is quiet: no flags are +changed and no rounding is performed. +[clinic start generated code]*/ + static PyObject * -dec_mpd_qcopy_abs(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal_copy_abs_impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=081cb7fb4230676e input=676d7c62b1795512]*/ { PyObject *result; uint32_t status = 0; + decimal_state *state = PyType_GetModuleState(cls); - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); if ((result = dec_alloc(state)) == NULL) { return NULL; } @@ -4573,13 +5206,23 @@ dec_mpd_qcopy_abs(PyObject *self, PyObject *Py_UNUSED(dummy)) return result; } +/*[clinic input] +_decimal.Decimal.copy_negate = _decimal.Decimal.copy_abs + +Return the negation of the argument. + +This operation is unaffected by context and is quiet: no flags are +changed and no rounding is performed. +[clinic start generated code]*/ + static PyObject * -dec_mpd_qcopy_negate(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal_copy_negate_impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=04fed82c17d4e28b input=23f41ee8899f3891]*/ { PyObject *result; uint32_t status = 0; + decimal_state *state = PyType_GetModuleState(cls); - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); if ((result = dec_alloc(state)) == NULL) { return NULL; } @@ -4595,41 +5238,99 @@ dec_mpd_qcopy_negate(PyObject *self, PyObject *Py_UNUSED(dummy)) } /* Unary functions, optional context arg */ + +/*[clinic input] +_decimal.Decimal.logical_invert = _decimal.Decimal.exp + +Invert all its digits. + +The self must be logical number. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_logical_invert_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=c626ed4b104a97b7 input=7158d5b525417955]*/ Dec_UnaryFuncVA(mpd_qinvert) + +/*[clinic input] +_decimal.Decimal.logb = _decimal.Decimal.exp + +Return the adjusted exponent of the operand as a Decimal instance. + +If the operand is a zero, then Decimal('-Infinity') is returned and +the DivisionByZero condition is raised. If the operand is an +infinity then Decimal('Infinity') is returned. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_logb_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=36b0bda09e934245 input=eeafa6bbf8d8a013]*/ Dec_UnaryFuncVA(mpd_qlogb) +/*[clinic input] +_decimal.Decimal.number_class = _decimal.Decimal.exp + +Return a string describing the class of the operand. + +The returned value is one of the following ten strings: + + * '-Infinity', indicating that the operand is negative infinity. + * '-Normal', indicating that the operand is a negative normal + number. + * '-Subnormal', indicating that the operand is negative and + subnormal. + * '-Zero', indicating that the operand is a negative zero. + * '+Zero', indicating that the operand is a positive zero. + * '+Subnormal', indicating that the operand is positive and + subnormal. + * '+Normal', indicating that the operand is a positive normal + number. + * '+Infinity', indicating that the operand is positive infinity. + * 'NaN', indicating that the operand is a quiet NaN (Not a + Number). + * 'sNaN', indicating that the operand is a signaling NaN. +[clinic start generated code]*/ + static PyObject * -dec_mpd_class(PyObject *self, PyObject *args, PyObject *kwds) +_decimal_Decimal_number_class_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=1ac82412e0849c52 input=0b59852b43c521aa]*/ { - static char *kwlist[] = {"context", NULL}; - PyObject *context = Py_None; const char *cp; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, - &context)) { - return NULL; - } - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); + decimal_state *state = PyType_GetModuleState(cls); CONTEXT_CHECK_VA(state, context); cp = mpd_class(MPD(self), CTX(context)); return PyUnicode_FromString(cp); } +/*[clinic input] +_decimal.Decimal.to_eng_string = _decimal.Decimal.exp + +Convert to an engineering-type string. + +Engineering notation has an exponent which is a multiple of 3, so +there are up to 3 digits left of the decimal place. For example, +Decimal('123E+1') is converted to Decimal('1.23E+3'). + +The value of context.capitals determines whether the exponent sign +is lower or upper case. Otherwise, the context does not affect the +operation. +[clinic start generated code]*/ + static PyObject * -dec_mpd_to_eng(PyObject *self, PyObject *args, PyObject *kwds) +_decimal_Decimal_to_eng_string_impl(PyObject *self, PyTypeObject *cls, + PyObject *context) +/*[clinic end generated code: output=901f128d437ae5c0 input=111db4de6561f211]*/ { - static char *kwlist[] = {"context", NULL}; PyObject *result; - PyObject *context = Py_None; mpd_ssize_t size; char *s; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, - &context)) { - return NULL; - } - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); + decimal_state *state = PyType_GetModuleState(cls); CONTEXT_CHECK_VA(state, context); size = mpd_to_eng_size(&s, MPD(self), CtxCaps(context)); @@ -4645,24 +5346,85 @@ dec_mpd_to_eng(PyObject *self, PyObject *args, PyObject *kwds) } /* Binary functions, optional context arg for conversion errors */ + +/*[clinic input] +_decimal.Decimal.compare_total = _decimal.Decimal.compare + +Compare two operands using their abstract representation. + +Similar to the compare() method, but the result gives a total +ordering on Decimal instances. Two Decimal instances with the same +numeric value but different representations compare unequal in this +ordering: + + >>> Decimal('12.0').compare_total(Decimal('12')) + Decimal('-1') + +Quiet and signaling NaNs are also included in the total ordering. +The result of this function is Decimal('0') if both operands have +the same representation, Decimal('-1') if the first operand is lower +in the total order than the second, and Decimal('1') if the first +operand is higher in the total order than the second operand. See +the specification for details of the total order. + +This operation is unaffected by context and is quiet: no flags are +changed and no rounding is performed. As an exception, the C +version may raise InvalidOperation if the second operand cannot be +converted exactly. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_compare_total_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=83649010bad7815f input=d795bf204b9ff2a8]*/ Dec_BinaryFuncVA_NO_CTX(mpd_compare_total) + +/*[clinic input] +_decimal.Decimal.compare_total_mag = _decimal.Decimal.compare + +As compare_total(), but ignores the sign of each operand. + +x.compare_total_mag(y) is equivalent to +x.copy_abs().compare_total(y.copy_abs()). + +This operation is unaffected by context and is quiet: no flags are +changed and no rounding is performed. As an exception, the C version +may raise InvalidOperation if the second operand cannot be converted +exactly. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_compare_total_mag_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=b99c924cafb5f0e3 input=eba17c4c24eb2833]*/ Dec_BinaryFuncVA_NO_CTX(mpd_compare_total_mag) +/*[clinic input] +_decimal.Decimal.copy_sign = _decimal.Decimal.compare + +Return a copy of *self* with the sign of *other*. + +For example: + + >>> Decimal('2.3').copy_sign(Decimal('-1.5')) + Decimal('-2.3') + +This operation is unaffected by context and is quiet: no flags are +changed and no rounding is performed. As an exception, the C version +may raise InvalidOperation if the second operand cannot be converted +exactly. +[clinic start generated code]*/ + static PyObject * -dec_mpd_qcopy_sign(PyObject *self, PyObject *args, PyObject *kwds) +_decimal_Decimal_copy_sign_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=e4c8f884f4d75801 input=51ed9e4691e2249e]*/ { - static char *kwlist[] = {"other", "context", NULL}; - PyObject *other; PyObject *a, *b; PyObject *result; - PyObject *context = Py_None; uint32_t status = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, - &other, &context)) { - return NULL; - } - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); + decimal_state *state = PyType_GetModuleState(cls); CONTEXT_CHECK_VA(state, context); CONVERT_BINOP_RAISE(&a, &b, self, other, context); @@ -4684,20 +5446,26 @@ dec_mpd_qcopy_sign(PyObject *self, PyObject *args, PyObject *kwds) return result; } +/*[clinic input] +_decimal.Decimal.same_quantum = _decimal.Decimal.compare + +Test whether self and other have the same exponent or both are NaN. + +This operation is unaffected by context and is quiet: no flags are +changed and no rounding is performed. As an exception, the C version +may raise InvalidOperation if the second operand cannot be converted +exactly. +[clinic start generated code]*/ + static PyObject * -dec_mpd_same_quantum(PyObject *self, PyObject *args, PyObject *kwds) +_decimal_Decimal_same_quantum_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=7c757edb0c263721 input=8339415fa359e7df]*/ { - static char *kwlist[] = {"other", "context", NULL}; - PyObject *other; PyObject *a, *b; PyObject *result; - PyObject *context = Py_None; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", kwlist, - &other, &context)) { - return NULL; - } - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); + decimal_state *state = PyType_GetModuleState(cls); CONTEXT_CHECK_VA(state, context); CONVERT_BINOP_RAISE(&a, &b, self, other, context); @@ -4709,30 +5477,147 @@ dec_mpd_same_quantum(PyObject *self, PyObject *args, PyObject *kwds) } /* Binary functions, optional context arg */ + +/*[clinic input] +_decimal.Decimal.logical_and = _decimal.Decimal.compare + +Applies an 'and' operation between self and other's digits. + +Both self and other must be logical numbers. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_logical_and_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=9a4cbb74c180b0bb input=f22460f1285782d2]*/ Dec_BinaryFuncVA(mpd_qand) + +/*[clinic input] +_decimal.Decimal.logical_or = _decimal.Decimal.compare + +Applies an 'or' operation between self and other's digits. + +Both self and other must be logical numbers. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_logical_or_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=063c4de18dc41ecb input=b5afa1e1fdebdfce]*/ Dec_BinaryFuncVA(mpd_qor) + +/*[clinic input] +_decimal.Decimal.logical_xor = _decimal.Decimal.compare + +Applies an 'xor' operation between self and other's digits. + +Both self and other must be logical numbers. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_logical_xor_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=829b09cb49926ad7 input=84d722ada08a2da7]*/ Dec_BinaryFuncVA(mpd_qxor) +/*[clinic input] +_decimal.Decimal.rotate = _decimal.Decimal.compare + +Returns a rotated copy of self's digits, value-of-other times. + +The second operand must be an integer in the range -precision +through precision. The absolute value of the second operand gives +the number of places to rotate. If the second operand is positive +then rotation is to the left; otherwise rotation is to the right. +The coefficient of the first operand is padded on the left with +zeros to length precision if necessary. The sign and exponent of +the first operand are unchanged. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_rotate_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=09f2737082882b83 input=4bc840d51842934c]*/ Dec_BinaryFuncVA(mpd_qrotate) + +/*[clinic input] +_decimal.Decimal.scaleb = _decimal.Decimal.compare + +Return the first operand with the exponent adjusted the second. + +Equivalently, return the first operand multiplied by 10**other. The +second operand must be an integer. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_scaleb_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=ae8730536c9f2d30 input=7f29f83278d05f83]*/ Dec_BinaryFuncVA(mpd_qscaleb) + +/*[clinic input] +_decimal.Decimal.shift = _decimal.Decimal.compare + +Returns a shifted copy of self's digits, value-of-other times. + +The second operand must be an integer in the range -precision +through precision. The absolute value of the second operand gives +the number of places to shift. If the second operand is positive, +then the shift is to the left; otherwise the shift is to the right. +Digits shifted into the coefficient are zeros. The sign and +exponent of the first operand are unchanged. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal_shift_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context) +/*[clinic end generated code: output=82e061a0d9ecc4f5 input=c05f3fd69fc1f9f9]*/ Dec_BinaryFuncVA(mpd_qshift) +/*[clinic input] +_decimal.Decimal.quantize + + cls: defining_class + exp as w: object + rounding: object = None + context: object = None + +Quantize *self* so its exponent is the same as that of *exp*. + +Return a value equal to *self* after rounding, with the exponent +of *exp*. + + >>> Decimal('1.41421356').quantize(Decimal('1.000')) + Decimal('1.414') + +Unlike other operations, if the length of the coefficient after the +quantize operation would be greater than precision, then an +InvalidOperation is signaled. This guarantees that, unless there +is an error condition, the quantized exponent is always equal to +that of the right-hand operand. + +Also unlike other operations, quantize never signals Underflow, even +if the result is subnormal and inexact. + +If the exponent of the second operand is larger than that of the +first, then rounding may be necessary. In this case, the rounding +mode is determined by the rounding argument if given, else by the +given context argument; if neither argument is given, the rounding +mode of the current thread's context is used. +[clinic start generated code]*/ + static PyObject * -dec_mpd_qquantize(PyObject *v, PyObject *args, PyObject *kwds) +_decimal_Decimal_quantize_impl(PyObject *self, PyTypeObject *cls, + PyObject *w, PyObject *rounding, + PyObject *context) +/*[clinic end generated code: output=fc51edf458559913 input=7838b0a5f684adb8]*/ { - static char *kwlist[] = {"exp", "rounding", "context", NULL}; - PyObject *rounding = Py_None; - PyObject *context = Py_None; - PyObject *w, *a, *b; + PyObject *a, *b; PyObject *result; uint32_t status = 0; mpd_context_t workctx; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", kwlist, - &w, &rounding, &context)) { - return NULL; - } - decimal_state *state = get_module_state_by_def(Py_TYPE(v)); + decimal_state *state = PyType_GetModuleState(cls); CONTEXT_CHECK_VA(state, context); workctx = *CTX(context); @@ -4746,7 +5631,7 @@ dec_mpd_qquantize(PyObject *v, PyObject *args, PyObject *kwds) } } - CONVERT_BINOP_RAISE(&a, &b, v, w, context); + CONVERT_BINOP_RAISE(&a, &b, self, w, context); result = dec_alloc(state); if (result == NULL) { @@ -4826,20 +5711,34 @@ dec_richcompare(PyObject *v, PyObject *w, int op) return PyBool_FromLong(r); } -/* __ceil__ */ +/*[clinic input] +_decimal.Decimal.__ceil__ + + cls: defining_class + +Return the ceiling as an Integral. +[clinic start generated code]*/ + static PyObject * -dec_ceil(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal___ceil___impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=d986ebf9aadbf9fe input=a8e0b87897706816]*/ { PyObject *context; - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); + decimal_state *state = PyType_GetModuleState(cls); CURRENT_CONTEXT(state, context); return dec_as_long(self, context, MPD_ROUND_CEILING); } -/* __complex__ */ +/*[clinic input] +_decimal.Decimal.__complex__ + +Convert this value to exact type complex. +[clinic start generated code]*/ + static PyObject * -dec_complex(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal___complex___impl(PyObject *self) +/*[clinic end generated code: output=c9b5b4a9fdebc912 input=6b11c6f20af7061a]*/ { PyObject *f; double x; @@ -4858,20 +5757,46 @@ dec_complex(PyObject *self, PyObject *Py_UNUSED(dummy)) return PyComplex_FromDoubles(x, 0); } -/* __copy__ (METH_NOARGS) and __deepcopy__ (METH_O) */ +/*[clinic input] +_decimal.Decimal.__copy__ + +[clinic start generated code]*/ + static PyObject * -dec_copy(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal___copy___impl(PyObject *self) +/*[clinic end generated code: output=8eb3656c0250762b input=3dfd30a3e1493c01]*/ { return Py_NewRef(self); } -/* __floor__ */ +/*[clinic input] +_decimal.Decimal.__deepcopy__ + + memo: object + / + +[clinic start generated code]*/ + static PyObject * -dec_floor(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal___deepcopy__(PyObject *self, PyObject *memo) +/*[clinic end generated code: output=988fb34e0136b376 input=f95598c6f43233aa]*/ +{ + return Py_NewRef(self); +} + +/*[clinic input] +_decimal.Decimal.__floor__ = _decimal.Decimal.__ceil__ + +Return the floor as an Integral. +[clinic start generated code]*/ + +static PyObject * +_decimal_Decimal___floor___impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=e239a2f7f6514c12 input=dcc37aeceb0efb8d]*/ { PyObject *context; - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); + decimal_state *state = PyType_GetModuleState(cls); CURRENT_CONTEXT(state, context); return dec_as_long(self, context, MPD_ROUND_FLOOR); } @@ -4880,7 +5805,7 @@ dec_floor(PyObject *self, PyObject *Py_UNUSED(dummy)) static Py_hash_t _dec_hash(PyDecObject *v) { -#if defined(CONFIG_64) && _PyHASH_BITS == 61 +#if defined(CONFIG_64) && PyHASH_BITS == 61 /* 2**61 - 1 */ mpd_uint_t p_data[1] = {2305843009213693951ULL}; mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 19, 1, 1, p_data}; @@ -4888,7 +5813,7 @@ _dec_hash(PyDecObject *v) mpd_uint_t inv10_p_data[1] = {2075258708292324556ULL}; mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 19, 1, 1, inv10_p_data}; -#elif defined(CONFIG_32) && _PyHASH_BITS == 31 +#elif defined(CONFIG_32) && PyHASH_BITS == 31 /* 2**31 - 1 */ mpd_uint_t p_data[2] = {147483647UL, 2}; mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 10, 2, 2, p_data}; @@ -4897,7 +5822,7 @@ _dec_hash(PyDecObject *v) mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 10, 2, 2, inv10_p_data}; #else - #error "No valid combination of CONFIG_64, CONFIG_32 and _PyHASH_BITS" + #error "No valid combination of CONFIG_64, CONFIG_32 and PyHASH_BITS" #endif const Py_hash_t py_hash_inf = 314159; mpd_uint_t ten_data[1] = {10}; @@ -5003,9 +5928,15 @@ dec_hash(PyObject *op) return self->hash; } -/* __reduce__ */ +/*[clinic input] +_decimal.Decimal.__reduce__ + +Return state information for pickling. +[clinic start generated code]*/ + static PyObject * -dec_reduce(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal___reduce___impl(PyObject *self) +/*[clinic end generated code: output=84fa6648a496a8d2 input=0345ea951d9b986f]*/ { PyObject *result, *str; @@ -5020,9 +5951,17 @@ dec_reduce(PyObject *self, PyObject *Py_UNUSED(dummy)) return result; } -/* __sizeof__ */ +/*[clinic input] +_decimal.Decimal.__sizeof__ + + self as v: self + +Returns size in memory, in bytes +[clinic start generated code]*/ + static PyObject * -dec_sizeof(PyObject *v, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal___sizeof___impl(PyObject *v) +/*[clinic end generated code: output=f16de05097c62b79 input=a557db538cfddbb7]*/ { size_t res = _PyObject_SIZE(Py_TYPE(v)); if (mpd_isdynamic_data(MPD(v))) { @@ -5031,13 +5970,19 @@ dec_sizeof(PyObject *v, PyObject *Py_UNUSED(dummy)) return PyLong_FromSize_t(res); } -/* __trunc__ */ +/*[clinic input] +_decimal.Decimal.__trunc__ = _decimal.Decimal.__ceil__ + +Return the Integral closest to x between 0 and x. +[clinic start generated code]*/ + static PyObject * -dec_trunc(PyObject *self, PyObject *Py_UNUSED(dummy)) +_decimal_Decimal___trunc___impl(PyObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=7b3decc4b636ce32 input=9b3a3a85f63b0515]*/ { PyObject *context; - decimal_state *state = get_module_state_by_def(Py_TYPE(self)); + decimal_state *state = PyType_GetModuleState(cls); CURRENT_CONTEXT(state, context); return dec_as_long(self, context, MPD_ROUND_DOWN); } @@ -5075,92 +6020,92 @@ static PyGetSetDef dec_getsets [] = static PyMethodDef dec_methods [] = { /* Unary arithmetic functions, optional context arg */ - { "exp", _PyCFunction_CAST(dec_mpd_qexp), METH_VARARGS|METH_KEYWORDS, doc_exp }, - { "ln", _PyCFunction_CAST(dec_mpd_qln), METH_VARARGS|METH_KEYWORDS, doc_ln }, - { "log10", _PyCFunction_CAST(dec_mpd_qlog10), METH_VARARGS|METH_KEYWORDS, doc_log10 }, - { "next_minus", _PyCFunction_CAST(dec_mpd_qnext_minus), METH_VARARGS|METH_KEYWORDS, doc_next_minus }, - { "next_plus", _PyCFunction_CAST(dec_mpd_qnext_plus), METH_VARARGS|METH_KEYWORDS, doc_next_plus }, - { "normalize", _PyCFunction_CAST(dec_mpd_qreduce), METH_VARARGS|METH_KEYWORDS, doc_normalize }, - { "to_integral", _PyCFunction_CAST(PyDec_ToIntegralValue), METH_VARARGS|METH_KEYWORDS, doc_to_integral }, - { "to_integral_exact", _PyCFunction_CAST(PyDec_ToIntegralExact), METH_VARARGS|METH_KEYWORDS, doc_to_integral_exact }, - { "to_integral_value", _PyCFunction_CAST(PyDec_ToIntegralValue), METH_VARARGS|METH_KEYWORDS, doc_to_integral_value }, - { "sqrt", _PyCFunction_CAST(dec_mpd_qsqrt), METH_VARARGS|METH_KEYWORDS, doc_sqrt }, + _DECIMAL_DECIMAL_EXP_METHODDEF + _DECIMAL_DECIMAL_LN_METHODDEF + _DECIMAL_DECIMAL_LOG10_METHODDEF + _DECIMAL_DECIMAL_NEXT_MINUS_METHODDEF + _DECIMAL_DECIMAL_NEXT_PLUS_METHODDEF + _DECIMAL_DECIMAL_NORMALIZE_METHODDEF + _DECIMAL_DECIMAL_TO_INTEGRAL_METHODDEF + _DECIMAL_DECIMAL_TO_INTEGRAL_EXACT_METHODDEF + _DECIMAL_DECIMAL_TO_INTEGRAL_VALUE_METHODDEF + _DECIMAL_DECIMAL_SQRT_METHODDEF /* Binary arithmetic functions, optional context arg */ - { "compare", _PyCFunction_CAST(dec_mpd_qcompare), METH_VARARGS|METH_KEYWORDS, doc_compare }, - { "compare_signal", _PyCFunction_CAST(dec_mpd_qcompare_signal), METH_VARARGS|METH_KEYWORDS, doc_compare_signal }, - { "max", _PyCFunction_CAST(dec_mpd_qmax), METH_VARARGS|METH_KEYWORDS, doc_max }, - { "max_mag", _PyCFunction_CAST(dec_mpd_qmax_mag), METH_VARARGS|METH_KEYWORDS, doc_max_mag }, - { "min", _PyCFunction_CAST(dec_mpd_qmin), METH_VARARGS|METH_KEYWORDS, doc_min }, - { "min_mag", _PyCFunction_CAST(dec_mpd_qmin_mag), METH_VARARGS|METH_KEYWORDS, doc_min_mag }, - { "next_toward", _PyCFunction_CAST(dec_mpd_qnext_toward), METH_VARARGS|METH_KEYWORDS, doc_next_toward }, - { "quantize", _PyCFunction_CAST(dec_mpd_qquantize), METH_VARARGS|METH_KEYWORDS, doc_quantize }, - { "remainder_near", _PyCFunction_CAST(dec_mpd_qrem_near), METH_VARARGS|METH_KEYWORDS, doc_remainder_near }, + _DECIMAL_DECIMAL_COMPARE_METHODDEF + _DECIMAL_DECIMAL_COMPARE_SIGNAL_METHODDEF + _DECIMAL_DECIMAL_MAX_METHODDEF + _DECIMAL_DECIMAL_MAX_MAG_METHODDEF + _DECIMAL_DECIMAL_MIN_METHODDEF + _DECIMAL_DECIMAL_MIN_MAG_METHODDEF + _DECIMAL_DECIMAL_NEXT_TOWARD_METHODDEF + _DECIMAL_DECIMAL_QUANTIZE_METHODDEF + _DECIMAL_DECIMAL_REMAINDER_NEAR_METHODDEF /* Ternary arithmetic functions, optional context arg */ - { "fma", _PyCFunction_CAST(dec_mpd_qfma), METH_VARARGS|METH_KEYWORDS, doc_fma }, + _DECIMAL_DECIMAL_FMA_METHODDEF /* Boolean functions, no context arg */ - { "is_canonical", dec_mpd_iscanonical, METH_NOARGS, doc_is_canonical }, - { "is_finite", dec_mpd_isfinite, METH_NOARGS, doc_is_finite }, - { "is_infinite", dec_mpd_isinfinite, METH_NOARGS, doc_is_infinite }, - { "is_nan", dec_mpd_isnan, METH_NOARGS, doc_is_nan }, - { "is_qnan", dec_mpd_isqnan, METH_NOARGS, doc_is_qnan }, - { "is_snan", dec_mpd_issnan, METH_NOARGS, doc_is_snan }, - { "is_signed", dec_mpd_issigned, METH_NOARGS, doc_is_signed }, - { "is_zero", dec_mpd_iszero, METH_NOARGS, doc_is_zero }, + _DECIMAL_DECIMAL_IS_CANONICAL_METHODDEF + _DECIMAL_DECIMAL_IS_FINITE_METHODDEF + _DECIMAL_DECIMAL_IS_INFINITE_METHODDEF + _DECIMAL_DECIMAL_IS_NAN_METHODDEF + _DECIMAL_DECIMAL_IS_QNAN_METHODDEF + _DECIMAL_DECIMAL_IS_SNAN_METHODDEF + _DECIMAL_DECIMAL_IS_SIGNED_METHODDEF + _DECIMAL_DECIMAL_IS_ZERO_METHODDEF /* Boolean functions, optional context arg */ - { "is_normal", _PyCFunction_CAST(dec_mpd_isnormal), METH_VARARGS|METH_KEYWORDS, doc_is_normal }, - { "is_subnormal", _PyCFunction_CAST(dec_mpd_issubnormal), METH_VARARGS|METH_KEYWORDS, doc_is_subnormal }, + _DECIMAL_DECIMAL_IS_NORMAL_METHODDEF + _DECIMAL_DECIMAL_IS_SUBNORMAL_METHODDEF /* Unary functions, no context arg */ - { "adjusted", dec_mpd_adjexp, METH_NOARGS, doc_adjusted }, - { "canonical", dec_canonical, METH_NOARGS, doc_canonical }, - { "conjugate", dec_conjugate, METH_NOARGS, doc_conjugate }, - { "radix", dec_mpd_radix, METH_NOARGS, doc_radix }, + _DECIMAL_DECIMAL_ADJUSTED_METHODDEF + _DECIMAL_DECIMAL_CANONICAL_METHODDEF + _DECIMAL_DECIMAL_CONJUGATE_METHODDEF + _DECIMAL_DECIMAL_RADIX_METHODDEF /* Unary functions, optional context arg for conversion errors */ - { "copy_abs", dec_mpd_qcopy_abs, METH_NOARGS, doc_copy_abs }, - { "copy_negate", dec_mpd_qcopy_negate, METH_NOARGS, doc_copy_negate }, + _DECIMAL_DECIMAL_COPY_ABS_METHODDEF + _DECIMAL_DECIMAL_COPY_NEGATE_METHODDEF /* Unary functions, optional context arg */ - { "logb", _PyCFunction_CAST(dec_mpd_qlogb), METH_VARARGS|METH_KEYWORDS, doc_logb }, - { "logical_invert", _PyCFunction_CAST(dec_mpd_qinvert), METH_VARARGS|METH_KEYWORDS, doc_logical_invert }, - { "number_class", _PyCFunction_CAST(dec_mpd_class), METH_VARARGS|METH_KEYWORDS, doc_number_class }, - { "to_eng_string", _PyCFunction_CAST(dec_mpd_to_eng), METH_VARARGS|METH_KEYWORDS, doc_to_eng_string }, + _DECIMAL_DECIMAL_LOGB_METHODDEF + _DECIMAL_DECIMAL_LOGICAL_INVERT_METHODDEF + _DECIMAL_DECIMAL_NUMBER_CLASS_METHODDEF + _DECIMAL_DECIMAL_TO_ENG_STRING_METHODDEF /* Binary functions, optional context arg for conversion errors */ - { "compare_total", _PyCFunction_CAST(dec_mpd_compare_total), METH_VARARGS|METH_KEYWORDS, doc_compare_total }, - { "compare_total_mag", _PyCFunction_CAST(dec_mpd_compare_total_mag), METH_VARARGS|METH_KEYWORDS, doc_compare_total_mag }, - { "copy_sign", _PyCFunction_CAST(dec_mpd_qcopy_sign), METH_VARARGS|METH_KEYWORDS, doc_copy_sign }, - { "same_quantum", _PyCFunction_CAST(dec_mpd_same_quantum), METH_VARARGS|METH_KEYWORDS, doc_same_quantum }, + _DECIMAL_DECIMAL_COMPARE_TOTAL_METHODDEF + _DECIMAL_DECIMAL_COMPARE_TOTAL_MAG_METHODDEF + _DECIMAL_DECIMAL_COPY_SIGN_METHODDEF + _DECIMAL_DECIMAL_SAME_QUANTUM_METHODDEF /* Binary functions, optional context arg */ - { "logical_and", _PyCFunction_CAST(dec_mpd_qand), METH_VARARGS|METH_KEYWORDS, doc_logical_and }, - { "logical_or", _PyCFunction_CAST(dec_mpd_qor), METH_VARARGS|METH_KEYWORDS, doc_logical_or }, - { "logical_xor", _PyCFunction_CAST(dec_mpd_qxor), METH_VARARGS|METH_KEYWORDS, doc_logical_xor }, - { "rotate", _PyCFunction_CAST(dec_mpd_qrotate), METH_VARARGS|METH_KEYWORDS, doc_rotate }, - { "scaleb", _PyCFunction_CAST(dec_mpd_qscaleb), METH_VARARGS|METH_KEYWORDS, doc_scaleb }, - { "shift", _PyCFunction_CAST(dec_mpd_qshift), METH_VARARGS|METH_KEYWORDS, doc_shift }, + _DECIMAL_DECIMAL_LOGICAL_AND_METHODDEF + _DECIMAL_DECIMAL_LOGICAL_OR_METHODDEF + _DECIMAL_DECIMAL_LOGICAL_XOR_METHODDEF + _DECIMAL_DECIMAL_ROTATE_METHODDEF + _DECIMAL_DECIMAL_SCALEB_METHODDEF + _DECIMAL_DECIMAL_SHIFT_METHODDEF /* Miscellaneous */ - { "from_float", dec_from_float, METH_O|METH_CLASS, doc_from_float }, - { "from_number", dec_from_number, METH_O|METH_CLASS, doc_from_number }, - { "as_tuple", PyDec_AsTuple, METH_NOARGS, doc_as_tuple }, - { "as_integer_ratio", dec_as_integer_ratio, METH_NOARGS, doc_as_integer_ratio }, + _DECIMAL_DECIMAL_FROM_FLOAT_METHODDEF + _DECIMAL_DECIMAL_FROM_NUMBER_METHODDEF + _DECIMAL_DECIMAL_AS_TUPLE_METHODDEF + _DECIMAL_DECIMAL_AS_INTEGER_RATIO_METHODDEF /* Special methods */ - { "__copy__", dec_copy, METH_NOARGS, NULL }, - { "__deepcopy__", dec_copy, METH_O, NULL }, - { "__format__", dec_format, METH_VARARGS, NULL }, - { "__reduce__", dec_reduce, METH_NOARGS, NULL }, - { "__round__", PyDec_Round, METH_VARARGS, NULL }, - { "__ceil__", dec_ceil, METH_NOARGS, NULL }, - { "__floor__", dec_floor, METH_NOARGS, NULL }, - { "__trunc__", dec_trunc, METH_NOARGS, NULL }, - { "__complex__", dec_complex, METH_NOARGS, NULL }, - { "__sizeof__", dec_sizeof, METH_NOARGS, NULL }, + _DECIMAL_DECIMAL___COPY___METHODDEF + _DECIMAL_DECIMAL___DEEPCOPY___METHODDEF + _DECIMAL_DECIMAL___FORMAT___METHODDEF + _DECIMAL_DECIMAL___REDUCE___METHODDEF + _DECIMAL_DECIMAL___ROUND___METHODDEF + _DECIMAL_DECIMAL___CEIL___METHODDEF + _DECIMAL_DECIMAL___FLOOR___METHODDEF + _DECIMAL_DECIMAL___TRUNC___METHODDEF + _DECIMAL_DECIMAL___COMPLEX___METHODDEF + _DECIMAL_DECIMAL___SIZEOF___METHODDEF { NULL, NULL, 1 } }; @@ -5169,11 +6114,11 @@ static PyType_Slot dec_slots[] = { {Py_tp_token, Py_TP_USE_SPEC}, {Py_tp_dealloc, dec_dealloc}, {Py_tp_getattro, PyObject_GenericGetAttr}, - {Py_tp_traverse, dec_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_repr, dec_repr}, {Py_tp_hash, dec_hash}, {Py_tp_str, dec_str}, - {Py_tp_doc, (void *)doc_decimal}, + {Py_tp_doc, (void *)dec_new__doc__}, {Py_tp_richcompare, dec_richcompare}, {Py_tp_methods, dec_methods}, {Py_tp_getset, dec_getsets}, @@ -5216,47 +6161,47 @@ static PyType_Spec dec_spec = { /* Macros for converting mpdecimal functions to Context methods */ /************************************************************************/ -/* Boolean context method. */ +/* Boolean context method. + Argument Clinic provides PyObject *context, PyObject *x +*/ #define DecCtx_BoolFunc(MPDFUNC) \ -static PyObject * \ -ctx_##MPDFUNC(PyObject *context, PyObject *v) \ { \ PyObject *ret; \ PyObject *a; \ \ - CONVERT_OP_RAISE(&a, v, context); \ + CONVERT_OP_RAISE(&a, x, context); \ \ ret = MPDFUNC(MPD(a), CTX(context)) ? incr_true() : incr_false(); \ Py_DECREF(a); \ return ret; \ } -/* Boolean context method. MPDFUNC does NOT use a context. */ +/* Boolean context method. MPDFUNC does NOT use a context. + Argument Clinic provides PyObject *context, PyObject *x +*/ #define DecCtx_BoolFunc_NO_CTX(MPDFUNC) \ -static PyObject * \ -ctx_##MPDFUNC(PyObject *context, PyObject *v) \ { \ PyObject *ret; \ PyObject *a; \ \ - CONVERT_OP_RAISE(&a, v, context); \ + CONVERT_OP_RAISE(&a, x, context); \ \ ret = MPDFUNC(MPD(a)) ? incr_true() : incr_false(); \ Py_DECREF(a); \ return ret; \ } -/* Unary context method. */ +/* Unary context method. + Argument Clinic provides PyObject *context, + PyTypeObject *cls, PyObject *x +*/ #define DecCtx_UnaryFunc(MPDFUNC) \ -static PyObject * \ -ctx_##MPDFUNC(PyObject *context, PyObject *v) \ { \ PyObject *result, *a; \ uint32_t status = 0; \ \ - CONVERT_OP_RAISE(&a, v, context); \ - decimal_state *state = \ - get_module_state_from_ctx(context); \ + CONVERT_OP_RAISE(&a, x, context); \ + decimal_state *state = PyType_GetModuleState(cls); \ if ((result = dec_alloc(state)) == NULL) { \ Py_DECREF(a); \ return NULL; \ @@ -5272,23 +6217,18 @@ ctx_##MPDFUNC(PyObject *context, PyObject *v) \ return result; \ } -/* Binary context method. */ +/* Binary context method. + Argument Clinic provides PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y +*/ #define DecCtx_BinaryFunc(MPDFUNC) \ -static PyObject * \ -ctx_##MPDFUNC(PyObject *context, PyObject *args) \ { \ - PyObject *v, *w; \ PyObject *a, *b; \ PyObject *result; \ uint32_t status = 0; \ \ - if (!PyArg_ParseTuple(args, "OO", &v, &w)) { \ - return NULL; \ - } \ - \ - CONVERT_BINOP_RAISE(&a, &b, v, w, context); \ - decimal_state *state = \ - get_module_state_from_ctx(context); \ + CONVERT_BINOP_RAISE(&a, &b, x, y, context); \ + decimal_state *state = PyType_GetModuleState(cls); \ if ((result = dec_alloc(state)) == NULL) { \ Py_DECREF(a); \ Py_DECREF(b); \ @@ -5309,22 +6249,17 @@ ctx_##MPDFUNC(PyObject *context, PyObject *args) \ /* * Binary context method. The context is only used for conversion. * The actual MPDFUNC does NOT take a context arg. + * Argument Clinic provides PyObject *context, PyTypeObject *cls, + * PyObject *x, PyObject *y */ #define DecCtx_BinaryFunc_NO_CTX(MPDFUNC) \ -static PyObject * \ -ctx_##MPDFUNC(PyObject *context, PyObject *args) \ { \ - PyObject *v, *w; \ PyObject *a, *b; \ PyObject *result; \ \ - if (!PyArg_ParseTuple(args, "OO", &v, &w)) { \ - return NULL; \ - } \ - \ - CONVERT_BINOP_RAISE(&a, &b, v, w, context); \ + CONVERT_BINOP_RAISE(&a, &b, x, y, context); \ decimal_state *state = \ - get_module_state_from_ctx(context); \ + PyType_GetModuleState(cls); \ if ((result = dec_alloc(state)) == NULL) { \ Py_DECREF(a); \ Py_DECREF(b); \ @@ -5338,22 +6273,18 @@ ctx_##MPDFUNC(PyObject *context, PyObject *args) \ return result; \ } -/* Ternary context method. */ +/* Ternary context method. + Argument Clinic provides PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y, PyObject *z +*/ #define DecCtx_TernaryFunc(MPDFUNC) \ -static PyObject * \ -ctx_##MPDFUNC(PyObject *context, PyObject *args) \ { \ - PyObject *v, *w, *x; \ PyObject *a, *b, *c; \ PyObject *result; \ uint32_t status = 0; \ \ - if (!PyArg_ParseTuple(args, "OOO", &v, &w, &x)) { \ - return NULL; \ - } \ - \ - CONVERT_TERNOP_RAISE(&a, &b, &c, v, w, x, context); \ - decimal_state *state = get_module_state_from_ctx(context); \ + CONVERT_TERNOP_RAISE(&a, &b, &c, x, y, z, context); \ + decimal_state *state = PyType_GetModuleState(cls); \ if ((result = dec_alloc(state)) == NULL) { \ Py_DECREF(a); \ Py_DECREF(b); \ @@ -5374,51 +6305,382 @@ ctx_##MPDFUNC(PyObject *context, PyObject *args) \ } -/* Unary arithmetic functions */ -DecCtx_UnaryFunc(mpd_qabs) -DecCtx_UnaryFunc(mpd_qexp) -DecCtx_UnaryFunc(mpd_qln) -DecCtx_UnaryFunc(mpd_qlog10) -DecCtx_UnaryFunc(mpd_qminus) -DecCtx_UnaryFunc(mpd_qnext_minus) -DecCtx_UnaryFunc(mpd_qnext_plus) -DecCtx_UnaryFunc(mpd_qplus) -DecCtx_UnaryFunc(mpd_qreduce) +/* Unary arithmetic functions */ + +/*[clinic input] +_decimal.Context.abs + + self as context: self + cls: defining_class + x: object + / + +Return the absolute value of x. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_abs_impl(PyObject *context, PyTypeObject *cls, PyObject *x) +/*[clinic end generated code: output=fe080467d32e229c input=00a33f9c68463bb0]*/ +DecCtx_UnaryFunc(mpd_qabs) + +/*[clinic input] +_decimal.Context.exp = _decimal.Context.abs + +Return e ** x. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_exp_impl(PyObject *context, PyTypeObject *cls, PyObject *x) +/*[clinic end generated code: output=c7477a67010ccc5f input=5b443c4ab153dd2e]*/ +DecCtx_UnaryFunc(mpd_qexp) + +/*[clinic input] +_decimal.Context.ln = _decimal.Context.abs + +Return the natural (base e) logarithm of x. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_ln_impl(PyObject *context, PyTypeObject *cls, PyObject *x) +/*[clinic end generated code: output=63e691b0680bffc7 input=cf43cd98a0fe7425]*/ +DecCtx_UnaryFunc(mpd_qln) + +/*[clinic input] +_decimal.Context.log10 = _decimal.Context.abs + +Return the base 10 logarithm of x. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_log10_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=e0d9fc928570304d input=309e57faf42c257d]*/ +DecCtx_UnaryFunc(mpd_qlog10) + +/*[clinic input] +_decimal.Context.minus = _decimal.Context.abs + +Minus corresponds to unary prefix minus in Python. + +This operation applies the context to the result. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_minus_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=f06c409b6aef1aad input=63be4c419d1d554b]*/ +DecCtx_UnaryFunc(mpd_qminus) + +/*[clinic input] +_decimal.Context.next_minus = _decimal.Context.abs + +Return the largest representable number smaller than x. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_next_minus_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=8dd168f08bec9547 input=969f4d24dfcd5e85]*/ +DecCtx_UnaryFunc(mpd_qnext_minus) + +/*[clinic input] +_decimal.Context.next_plus = _decimal.Context.abs + +Return the smallest representable number larger than x. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_next_plus_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=2a50586ad2f7c108 input=af1a85ee59b56a3c]*/ +DecCtx_UnaryFunc(mpd_qnext_plus) + +/*[clinic input] +_decimal.Context.normalize = _decimal.Context.abs + +Reduce x to its simplest form. Alias for reduce(x). +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_normalize_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=9a9510f442ba2852 input=a65bc39c81a654a9]*/ +DecCtx_UnaryFunc(mpd_qreduce) + +/*[clinic input] +_decimal.Context.plus = _decimal.Context.abs + +Plus corresponds to the unary prefix plus operator in Python. + +This operation applies the context to the result. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_plus_impl(PyObject *context, PyTypeObject *cls, PyObject *x) +/*[clinic end generated code: output=c37d29f58a47f93a input=5d8a75702d20e2f9]*/ +DecCtx_UnaryFunc(mpd_qplus) + +/*[clinic input] +_decimal.Context.to_integral_value = _decimal.Context.abs + +Round to an integer. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_to_integral_value_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=e3d9ad000bc06036 input=3103e147cb9de9ed]*/ DecCtx_UnaryFunc(mpd_qround_to_int) + +/*[clinic input] +_decimal.Context.to_integral_exact = _decimal.Context.abs + +Round to an integer. Signal if the result is rounded or inexact. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_to_integral_exact_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=680b796dfae8e2ef input=677dc4b915907b68]*/ DecCtx_UnaryFunc(mpd_qround_to_intx) + +/*[clinic input] +_decimal.Context.to_integral = _decimal.Context.abs + +Identical to to_integral_value(x). +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_to_integral_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=09f4823b90b2cf17 input=89d4a4b15495b8c9]*/ +DecCtx_UnaryFunc(mpd_qround_to_int) + +/*[clinic input] +_decimal.Context.sqrt = _decimal.Context.abs + +Square root of a non-negative number to context precision. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_sqrt_impl(PyObject *context, PyTypeObject *cls, PyObject *x) +/*[clinic end generated code: output=2b9c16c6f5ceead0 input=90bd954b0b8076fb]*/ DecCtx_UnaryFunc(mpd_qsqrt) /* Binary arithmetic functions */ + +/*[clinic input] +_decimal.Context.add + + self as context: self + cls: defining_class + x: object + y: object + / + +Return the sum of x and y. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_add_impl(PyObject *context, PyTypeObject *cls, PyObject *x, + PyObject *y) +/*[clinic end generated code: output=ab4f0fb841e6a867 input=f2c74f6a845f62e9]*/ DecCtx_BinaryFunc(mpd_qadd) + +/*[clinic input] +_decimal.Context.compare = _decimal.Context.add + +Compare x and y numerically. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_compare_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=56efd1faf653f1d7 input=f701cb179c966ec1]*/ DecCtx_BinaryFunc(mpd_qcompare) + +/*[clinic input] +_decimal.Context.compare_signal = _decimal.Context.add + +Compare x and y numerically. All NaNs signal. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_compare_signal_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=7c1a9a9f6ae4e5cd input=32a1bcef7bbc5179]*/ DecCtx_BinaryFunc(mpd_qcompare_signal) + +/*[clinic input] +_decimal.Context.divide = _decimal.Context.add + +Return x divided by y. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_divide_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=1a7924b20e24a528 input=00cd9bc2ba2a1786]*/ DecCtx_BinaryFunc(mpd_qdiv) + +/*[clinic input] +_decimal.Context.divide_int = _decimal.Context.add + +Return x divided by y, truncated to an integer. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_divide_int_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=7a1d8948625105f0 input=e80ada2f50d9719d]*/ DecCtx_BinaryFunc(mpd_qdivint) + +/*[clinic input] +_decimal.Context.max = _decimal.Context.add + +Compare the values numerically and return the maximum. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_max_impl(PyObject *context, PyTypeObject *cls, PyObject *x, + PyObject *y) +/*[clinic end generated code: output=cd54af10a51c11fc input=22008ab898c86a8b]*/ DecCtx_BinaryFunc(mpd_qmax) + +/*[clinic input] +_decimal.Context.max_mag = _decimal.Context.add + +Compare the values numerically with their sign ignored. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_max_mag_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=1c812e73bcb7827f input=f7ce42ef82a7c52e]*/ DecCtx_BinaryFunc(mpd_qmax_mag) + +/*[clinic input] +_decimal.Context.min = _decimal.Context.add + +Compare the values numerically and return the minimum. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_min_impl(PyObject *context, PyTypeObject *cls, PyObject *x, + PyObject *y) +/*[clinic end generated code: output=aa494e95b88107b3 input=2aeec1167638c5ef]*/ DecCtx_BinaryFunc(mpd_qmin) + +/*[clinic input] +_decimal.Context.min_mag = _decimal.Context.add + +Compare the values numerically with their sign ignored. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_min_mag_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=ee0b69c1d9a14185 input=19d158c29e4fc140]*/ DecCtx_BinaryFunc(mpd_qmin_mag) + +/*[clinic input] +_decimal.Context.multiply = _decimal.Context.add + +Return the product of x and y. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_multiply_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=45f33b805afa01a8 input=2fdd01acdbeef8ba]*/ DecCtx_BinaryFunc(mpd_qmul) + +/*[clinic input] +_decimal.Context.next_toward = _decimal.Context.add + +Return the number closest to x, in the direction towards y. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_next_toward_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=436afff6f43edec2 input=aac775298e02b68c]*/ DecCtx_BinaryFunc(mpd_qnext_toward) + +/*[clinic input] +_decimal.Context.quantize = _decimal.Context.add + +Return a value equal to x (rounded), having the exponent of y. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_quantize_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=fcf8cd32b7d628c9 input=43d67a696ab6d895]*/ DecCtx_BinaryFunc(mpd_qquantize) + +/*[clinic input] +_decimal.Context.remainder = _decimal.Context.add + +Return the remainder from integer division. + +The sign of the result, if non-zero, is the same as that of the +original dividend. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_remainder_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=e0f96c834abbfbd2 input=36d0eb2b392c1215]*/ DecCtx_BinaryFunc(mpd_qrem) + +/*[clinic input] +_decimal.Context.remainder_near = _decimal.Context.add + +Return x - y * n. + +Here n is the integer nearest the exact value of x / y (if the +result is 0 then its sign will be the sign of x). +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_remainder_near_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=7f18c535a12cf8ac input=60342558000d4be6]*/ DecCtx_BinaryFunc(mpd_qrem_near) + +/*[clinic input] +_decimal.Context.subtract = _decimal.Context.add + +Return the difference between x and y. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_subtract_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=3d764a8a87e79401 input=6767683ec68f7a1a]*/ DecCtx_BinaryFunc(mpd_qsub) +/*[clinic input] +_decimal.Context.divmod + + self as context: self + x: object + y: object + / + +Return quotient and remainder of the division x / y. +[clinic start generated code]*/ + static PyObject * -ctx_mpd_qdivmod(PyObject *context, PyObject *args) +_decimal_Context_divmod_impl(PyObject *context, PyObject *x, PyObject *y) +/*[clinic end generated code: output=5dbf5410e3f302af input=4d8eee07823c752a]*/ { - PyObject *v, *w; PyObject *a, *b; PyObject *q, *r; uint32_t status = 0; - PyObject *ret; - - if (!PyArg_ParseTuple(args, "OO", &v, &w)) { - return NULL; - } - CONVERT_BINOP_RAISE(&a, &b, v, w, context); + CONVERT_BINOP_RAISE(&a, &b, x, y, context); decimal_state *state = get_module_state_from_ctx(context); q = dec_alloc(state); if (q == NULL) { @@ -5443,27 +6705,47 @@ ctx_mpd_qdivmod(PyObject *context, PyObject *args) return NULL; } - ret = PyTuple_Pack(2, q, r); - Py_DECREF(r); - Py_DECREF(q); - return ret; + return _PyTuple_FromPairSteal(q, r); } /* Binary or ternary arithmetic functions */ + +/*[clinic input] +_decimal.Context.power + + self as context: self + cls: defining_class + a as base: object + b as exp: object + modulo as mod: object = None + +Compute a**b. + +If 'a' is negative, then 'b' must be integral. The result will be +inexact unless 'a' is integral and the result is finite and can be +expressed exactly in 'precision' digits. In the Python version the +result is always correctly rounded, in the C version the result is +almost always correctly rounded. + +If modulo is given, compute (a**b) % modulo. The following +restrictions hold: + + * all three arguments must be integral + * 'b' must be nonnegative + * at least one of 'a' or 'b' must be nonzero + * modulo must be nonzero and less than 10**prec in absolute + value +[clinic start generated code]*/ + static PyObject * -ctx_mpd_qpow(PyObject *context, PyObject *args, PyObject *kwds) +_decimal_Context_power_impl(PyObject *context, PyTypeObject *cls, + PyObject *base, PyObject *exp, PyObject *mod) +/*[clinic end generated code: output=d06d40c37cdd69dc input=178a254468ec189b]*/ { - static char *kwlist[] = {"a", "b", "modulo", NULL}; - PyObject *base, *exp, *mod = Py_None; PyObject *a, *b, *c = NULL; PyObject *result; uint32_t status = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist, - &base, &exp, &mod)) { - return NULL; - } - CONVERT_BINOP_RAISE(&a, &b, base, exp, context); if (mod != Py_None) { @@ -5474,7 +6756,7 @@ ctx_mpd_qpow(PyObject *context, PyObject *args, PyObject *kwds) } } - decimal_state *state = get_module_state_from_ctx(context); + decimal_state *state = PyType_GetModuleState(cls); result = dec_alloc(state); if (result == NULL) { Py_DECREF(a); @@ -5503,74 +6785,255 @@ ctx_mpd_qpow(PyObject *context, PyObject *args, PyObject *kwds) } /* Ternary arithmetic functions */ + +/*[clinic input] +_decimal.Context.fma + + self as context: self + cls: defining_class + x: object + y: object + z: object + / + +Return x multiplied by y, plus z. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_fma_impl(PyObject *context, PyTypeObject *cls, PyObject *x, + PyObject *y, PyObject *z) +/*[clinic end generated code: output=08ec3cefc59d71a9 input=da3963b1a1da83b9]*/ DecCtx_TernaryFunc(mpd_qfma) /* No argument */ + +/*[clinic input] +_decimal.Context.radix + + self as context: self + cls: defining_class + +Return 10. +[clinic start generated code]*/ + static PyObject * -ctx_mpd_radix(PyObject *context, PyObject *dummy) +_decimal_Context_radix_impl(PyObject *context, PyTypeObject *cls) +/*[clinic end generated code: output=674b88b7cd0c264d input=e1e4f8c0abf86825]*/ { - decimal_state *state = get_module_state_from_ctx(context); + decimal_state *state = PyType_GetModuleState(cls); return _dec_mpd_radix(state); } /* Boolean functions: single decimal argument */ + +/*[clinic input] +_decimal.Context.is_normal + + self as context: self + cls: defining_class + x: object + / + +Return True if x is a normal number, False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_is_normal_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=089c5609db60bf57 input=7c90b825a517ef7e]*/ DecCtx_BoolFunc(mpd_isnormal) + +/*[clinic input] +_decimal.Context.is_subnormal = _decimal.Context.is_normal + +Return True if x is subnormal, False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_is_subnormal_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=f58c45a288aadeda input=73f1bd9367b913a4]*/ DecCtx_BoolFunc(mpd_issubnormal) + +/*[clinic input] +_decimal.Context.is_finite = _decimal.Context.is_normal + +Return True if x is finite, False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_is_finite_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=dfb00f1b5589b9f0 input=abff92a8a6bb85e6]*/ DecCtx_BoolFunc_NO_CTX(mpd_isfinite) + +/*[clinic input] +_decimal.Context.is_infinite = _decimal.Context.is_normal + +Return True if x is infinite, False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_is_infinite_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=1c28517500811d01 input=591242ae9a1e60e6]*/ DecCtx_BoolFunc_NO_CTX(mpd_isinfinite) + +/*[clinic input] +_decimal.Context.is_nan = _decimal.Context.is_normal + +Return True if x is a qNaN or sNaN, False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_is_nan_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=9dc15463ee19864a input=520218376d5eec5e]*/ DecCtx_BoolFunc_NO_CTX(mpd_isnan) + +/*[clinic input] +_decimal.Context.is_qnan = _decimal.Context.is_normal + +Return True if x is a quiet NaN, False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_is_qnan_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=4caa672e03703b6d input=97d06a14ab3360d1]*/ DecCtx_BoolFunc_NO_CTX(mpd_isqnan) -DecCtx_BoolFunc_NO_CTX(mpd_issigned) + +/*[clinic input] +_decimal.Context.is_snan = _decimal.Context.is_normal + +Return True if x is a signaling NaN, False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_is_snan_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=a8caa929d9f82ecd input=0059fe4e9c3b25a8]*/ DecCtx_BoolFunc_NO_CTX(mpd_issnan) + +/*[clinic input] +_decimal.Context.is_signed = _decimal.Context.is_normal + +Return True if x is negative, False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_is_signed_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=42c450c99d4fe7db input=b950cd697721ab8b]*/ +DecCtx_BoolFunc_NO_CTX(mpd_issigned) + +/*[clinic input] +_decimal.Context.is_zero = _decimal.Context.is_normal + +Return True if x is a zero, False otherwise. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_is_zero_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=e6c55359b7241d9e input=bf08197d142a8027]*/ DecCtx_BoolFunc_NO_CTX(mpd_iszero) +/*[clinic input] +_decimal.Context.is_canonical = _decimal.Context.is_normal + +Return True if x is canonical, False otherwise. +[clinic start generated code]*/ + static PyObject * -ctx_iscanonical(PyObject *context, PyObject *v) +_decimal_Context_is_canonical_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=18ee249d9aec957c input=1bf2129808e55eb9]*/ { - decimal_state *state = get_module_state_from_ctx(context); - if (!PyDec_Check(state, v)) { + decimal_state *state = PyType_GetModuleState(cls); + if (!PyDec_Check(state, x)) { PyErr_SetString(PyExc_TypeError, "argument must be a Decimal"); return NULL; } - return mpd_iscanonical(MPD(v)) ? incr_true() : incr_false(); + return mpd_iscanonical(MPD(x)) ? incr_true() : incr_false(); } /* Functions with a single decimal argument */ +/*[clinic input] +_decimal.Context._apply = _decimal.Context.is_normal + +Apply self to Decimal x. +[clinic start generated code]*/ + static PyObject * -PyDecContext_Apply(PyObject *context, PyObject *v) +_decimal_Context__apply_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=c6b542f4e8114b97 input=12b34468ca4a4c30]*/ { PyObject *result, *a; - CONVERT_OP_RAISE(&a, v, context); + CONVERT_OP_RAISE(&a, x, context); result = dec_apply(a, context); Py_DECREF(a); return result; } +#ifdef EXTRA_FUNCTIONALITY +/*[clinic input] +_decimal.Context.apply = _decimal.Context._apply + +Apply self to Decimal x. +[clinic start generated code]*/ + static PyObject * -ctx_canonical(PyObject *context, PyObject *v) +_decimal_Context_apply_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=f8a7142d47ad4ff3 input=388e66ca82733516]*/ { - decimal_state *state = get_module_state_from_ctx(context); - if (!PyDec_Check(state, v)) { + return _decimal_Context__apply(context, x); +} +#endif + +/*[clinic input] +_decimal.Context.canonical = _decimal.Context.is_normal + +Return a new instance of x. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_canonical_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=f213e433e2032e5e input=025ecb106ac15bff]*/ +{ + decimal_state *state = PyType_GetModuleState(cls); + if (!PyDec_Check(state, x)) { PyErr_SetString(PyExc_TypeError, "argument must be a Decimal"); return NULL; } - return Py_NewRef(v); + return Py_NewRef(x); } +/*[clinic input] +_decimal.Context.copy_abs = _decimal.Context.is_normal + +Return a copy of x with the sign set to 0. +[clinic start generated code]*/ + static PyObject * -ctx_mpd_qcopy_abs(PyObject *context, PyObject *v) +_decimal_Context_copy_abs_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=a141ad4b9afe2deb input=4aa2f612625f0f73]*/ { PyObject *result, *a; uint32_t status = 0; - CONVERT_OP_RAISE(&a, v, context); - decimal_state *state = get_module_state_from_ctx(context); + CONVERT_OP_RAISE(&a, x, context); + decimal_state *state = PyType_GetModuleState(cls); result = dec_alloc(state); if (result == NULL) { Py_DECREF(a); @@ -5587,23 +7050,39 @@ ctx_mpd_qcopy_abs(PyObject *context, PyObject *v) return result; } +/*[clinic input] +_decimal.Context.copy_decimal = _decimal.Context.is_normal + +Return a copy of Decimal x. +[clinic start generated code]*/ + static PyObject * -ctx_copy_decimal(PyObject *context, PyObject *v) +_decimal_Context_copy_decimal_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=639a82e1193d31f6 input=4db4f942f45fb7c9]*/ { PyObject *result; - CONVERT_OP_RAISE(&result, v, context); + CONVERT_OP_RAISE(&result, x, context); return result; } +/*[clinic input] +_decimal.Context.copy_negate = _decimal.Context.is_normal + +Return a copy of x with the sign inverted. +[clinic start generated code]*/ + static PyObject * -ctx_mpd_qcopy_negate(PyObject *context, PyObject *v) +_decimal_Context_copy_negate_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=e49d013489dc252b input=2e6e213e2ed0efda]*/ { PyObject *result, *a; uint32_t status = 0; - CONVERT_OP_RAISE(&a, v, context); - decimal_state *state = get_module_state_from_ctx(context); + CONVERT_OP_RAISE(&a, x, context); + decimal_state *state = PyType_GetModuleState(cls); result = dec_alloc(state); if (result == NULL) { Py_DECREF(a); @@ -5620,16 +7099,57 @@ ctx_mpd_qcopy_negate(PyObject *context, PyObject *v) return result; } +/*[clinic input] +_decimal.Context.logb = _decimal.Context.abs + +Return the exponent of the magnitude of the operand's MSD. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_logb_impl(PyObject *context, PyTypeObject *cls, PyObject *x) +/*[clinic end generated code: output=9b9697e1eb68093f input=28d1cd1a8a906b9a]*/ DecCtx_UnaryFunc(mpd_qlogb) + +/*[clinic input] +_decimal.Context.logical_invert = _decimal.Context.abs + +Invert all the digits in the operand. + +The operand must be a logical number. + + >>> ExtendedContext.logical_invert(Decimal('0')) + Decimal('111111111') + >>> ExtendedContext.logical_invert(Decimal('1')) + Decimal('111111110') + >>> ExtendedContext.logical_invert(Decimal('111111111')) + Decimal('0') + >>> ExtendedContext.logical_invert(Decimal('101010101')) + Decimal('10101010') + >>> ExtendedContext.logical_invert(1101) + Decimal('111110010') +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_logical_invert_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=97760277a958e2b0 input=8e568f4c745ab596]*/ DecCtx_UnaryFunc(mpd_qinvert) +/*[clinic input] +_decimal.Context.number_class = _decimal.Context.is_normal + +Return an indication of the class of x. +[clinic start generated code]*/ + static PyObject * -ctx_mpd_class(PyObject *context, PyObject *v) +_decimal_Context_number_class_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=c1592a23e25ba5ee input=1ead8462f1800e4e]*/ { PyObject *a; const char *cp; - CONVERT_OP_RAISE(&a, v, context); + CONVERT_OP_RAISE(&a, x, context); cp = mpd_class(MPD(a), CTX(context)); Py_DECREF(a); @@ -5637,15 +7157,23 @@ ctx_mpd_class(PyObject *context, PyObject *v) return PyUnicode_FromString(cp); } +/*[clinic input] +_decimal.Context.to_sci_string = _decimal.Context.is_normal + +Convert a number to a string using scientific notation. +[clinic start generated code]*/ + static PyObject * -ctx_mpd_to_sci(PyObject *context, PyObject *v) +_decimal_Context_to_sci_string_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=092dcdef999d72da input=ed442677c66d342d]*/ { PyObject *result; PyObject *a; mpd_ssize_t size; char *s; - CONVERT_OP_RAISE(&a, v, context); + CONVERT_OP_RAISE(&a, x, context); size = mpd_to_sci_size(&s, MPD(a), CtxCaps(context)); Py_DECREF(a); @@ -5660,15 +7188,23 @@ ctx_mpd_to_sci(PyObject *context, PyObject *v) return result; } +/*[clinic input] +_decimal.Context.to_eng_string = _decimal.Context.is_normal + +Convert a number to a string, using engineering notation. +[clinic start generated code]*/ + static PyObject * -ctx_mpd_to_eng(PyObject *context, PyObject *v) +_decimal_Context_to_eng_string_impl(PyObject *context, PyTypeObject *cls, + PyObject *x) +/*[clinic end generated code: output=7fc53216c208f487 input=a574385e2e3e3bc0]*/ { PyObject *result; PyObject *a; mpd_ssize_t size; char *s; - CONVERT_OP_RAISE(&a, v, context); + CONVERT_OP_RAISE(&a, x, context); size = mpd_to_eng_size(&s, MPD(a), CtxCaps(context)); Py_DECREF(a); @@ -5684,23 +7220,54 @@ ctx_mpd_to_eng(PyObject *context, PyObject *v) } /* Functions with two decimal arguments */ + +/*[clinic input] +_decimal.Context.compare_total + + self as context: self + cls: defining_class + x: object + y: object + / + +Compare x and y using their abstract representation. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_compare_total_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=f79177b27fe930e3 input=2bfc677a841e297a]*/ DecCtx_BinaryFunc_NO_CTX(mpd_compare_total) + +/*[clinic input] +_decimal.Context.compare_total_mag = _decimal.Context.compare_total + +Compare x and y using their abstract representation, ignoring sign. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_compare_total_mag_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=2528c669ccd6d6ff input=2b982e69f932dcb2]*/ DecCtx_BinaryFunc_NO_CTX(mpd_compare_total_mag) +/*[clinic input] +_decimal.Context.copy_sign = _decimal.Context.compare_total + +Copy the sign from y to x. +[clinic start generated code]*/ + static PyObject * -ctx_mpd_qcopy_sign(PyObject *context, PyObject *args) +_decimal_Context_copy_sign_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=77d23b6f4e42120c input=c0682aeaffc7cfdf]*/ { - PyObject *v, *w; PyObject *a, *b; PyObject *result; uint32_t status = 0; - if (!PyArg_ParseTuple(args, "OO", &v, &w)) { - return NULL; - } - - CONVERT_BINOP_RAISE(&a, &b, v, w, context); - decimal_state *state = get_module_state_from_ctx(context); + CONVERT_BINOP_RAISE(&a, &b, x, y, context); + decimal_state *state = PyType_GetModuleState(cls); result = dec_alloc(state); if (result == NULL) { Py_DECREF(a); @@ -5719,26 +7286,158 @@ ctx_mpd_qcopy_sign(PyObject *context, PyObject *args) return result; } +/*[clinic input] +@permit_long_docstring_body +_decimal.Context.logical_and = _decimal.Context.add + +Applies the logical operation 'and' between each operand's digits. + +The operands must be both logical numbers. + + >>> ExtendedContext.logical_and(Decimal('0'), Decimal('0')) + Decimal('0') + >>> ExtendedContext.logical_and(Decimal('0'), Decimal('1')) + Decimal('0') + >>> ExtendedContext.logical_and(Decimal('1'), Decimal('0')) + Decimal('0') + >>> ExtendedContext.logical_and(Decimal('1'), Decimal('1')) + Decimal('1') + >>> ExtendedContext.logical_and(Decimal('1100'), Decimal('1010')) + Decimal('1000') + >>> ExtendedContext.logical_and(Decimal('1111'), Decimal('10')) + Decimal('10') + >>> ExtendedContext.logical_and(110, 1101) + Decimal('100') + >>> ExtendedContext.logical_and(Decimal(110), 1101) + Decimal('100') + >>> ExtendedContext.logical_and(110, Decimal(1101)) + Decimal('100') +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_logical_and_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=009dfa08ecaa2ac8 input=9f8a93a31b9d7088]*/ DecCtx_BinaryFunc(mpd_qand) + +/*[clinic input] +_decimal.Context.logical_or = _decimal.Context.add + +Applies the logical operation 'or' between each operand's digits. + +The operands must be both logical numbers. + + >>> ExtendedContext.logical_or(Decimal('0'), Decimal('0')) + Decimal('0') + >>> ExtendedContext.logical_or(Decimal('0'), Decimal('1')) + Decimal('1') + >>> ExtendedContext.logical_or(Decimal('1'), Decimal('0')) + Decimal('1') + >>> ExtendedContext.logical_or(Decimal('1'), Decimal('1')) + Decimal('1') + >>> ExtendedContext.logical_or(Decimal('1100'), Decimal('1010')) + Decimal('1110') + >>> ExtendedContext.logical_or(Decimal('1110'), Decimal('10')) + Decimal('1110') + >>> ExtendedContext.logical_or(110, 1101) + Decimal('1111') + >>> ExtendedContext.logical_or(Decimal(110), 1101) + Decimal('1111') + >>> ExtendedContext.logical_or(110, Decimal(1101)) + Decimal('1111') +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_logical_or_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=eb38617e8d31bf12 input=47b45d296fb90846]*/ DecCtx_BinaryFunc(mpd_qor) + +/*[clinic input] +@permit_long_docstring_body +_decimal.Context.logical_xor = _decimal.Context.add + +Applies the logical operation 'xor' between each operand's digits. + +The operands must be both logical numbers. + + >>> ExtendedContext.logical_xor(Decimal('0'), Decimal('0')) + Decimal('0') + >>> ExtendedContext.logical_xor(Decimal('0'), Decimal('1')) + Decimal('1') + >>> ExtendedContext.logical_xor(Decimal('1'), Decimal('0')) + Decimal('1') + >>> ExtendedContext.logical_xor(Decimal('1'), Decimal('1')) + Decimal('0') + >>> ExtendedContext.logical_xor(Decimal('1100'), Decimal('1010')) + Decimal('110') + >>> ExtendedContext.logical_xor(Decimal('1111'), Decimal('10')) + Decimal('1101') + >>> ExtendedContext.logical_xor(110, 1101) + Decimal('1011') + >>> ExtendedContext.logical_xor(Decimal(110), 1101) + Decimal('1011') + >>> ExtendedContext.logical_xor(110, Decimal(1101)) + Decimal('1011') +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_logical_xor_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=23cd81fdcd865d5a input=119412854ae58440]*/ DecCtx_BinaryFunc(mpd_qxor) +/*[clinic input] +_decimal.Context.rotate = _decimal.Context.add + +Return a copy of x, rotated by y places. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_rotate_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=3d5b3cfcb4659432 input=7ad91845c909eb0a]*/ DecCtx_BinaryFunc(mpd_qrotate) + +/*[clinic input] +_decimal.Context.scaleb = _decimal.Context.add + +Return the first operand after adding the second value to its exp. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_scaleb_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=795ac61bcbe61c67 input=c5d2ee7a57f65f8c]*/ DecCtx_BinaryFunc(mpd_qscaleb) + +/*[clinic input] +_decimal.Context.shift = _decimal.Context.add + +Return a copy of x, shifted by y places. +[clinic start generated code]*/ + +static PyObject * +_decimal_Context_shift_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=43d69615f0271c81 input=1ab44ff0854420ce]*/ DecCtx_BinaryFunc(mpd_qshift) +/*[clinic input] +_decimal.Context.same_quantum = _decimal.Context.add + +Return True if the two operands have the same exponent. +[clinic start generated code]*/ + static PyObject * -ctx_mpd_same_quantum(PyObject *context, PyObject *args) +_decimal_Context_same_quantum_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y) +/*[clinic end generated code: output=91a4d8325f98d9e9 input=194cd156e398eaf9]*/ { - PyObject *v, *w; PyObject *a, *b; PyObject *result; - if (!PyArg_ParseTuple(args, "OO", &v, &w)) { - return NULL; - } - - CONVERT_BINOP_RAISE(&a, &b, v, w, context); + CONVERT_BINOP_RAISE(&a, &b, x, y, context); result = mpd_same_quantum(MPD(a), MPD(b)) ? incr_true() : incr_false(); Py_DECREF(a); @@ -5751,105 +7450,101 @@ ctx_mpd_same_quantum(PyObject *context, PyObject *args) static PyMethodDef context_methods [] = { /* Unary arithmetic functions */ - { "abs", ctx_mpd_qabs, METH_O, doc_ctx_abs }, - { "exp", ctx_mpd_qexp, METH_O, doc_ctx_exp }, - { "ln", ctx_mpd_qln, METH_O, doc_ctx_ln }, - { "log10", ctx_mpd_qlog10, METH_O, doc_ctx_log10 }, - { "minus", ctx_mpd_qminus, METH_O, doc_ctx_minus }, - { "next_minus", ctx_mpd_qnext_minus, METH_O, doc_ctx_next_minus }, - { "next_plus", ctx_mpd_qnext_plus, METH_O, doc_ctx_next_plus }, - { "normalize", ctx_mpd_qreduce, METH_O, doc_ctx_normalize }, - { "plus", ctx_mpd_qplus, METH_O, doc_ctx_plus }, - { "to_integral", ctx_mpd_qround_to_int, METH_O, doc_ctx_to_integral }, - { "to_integral_exact", ctx_mpd_qround_to_intx, METH_O, doc_ctx_to_integral_exact }, - { "to_integral_value", ctx_mpd_qround_to_int, METH_O, doc_ctx_to_integral_value }, - { "sqrt", ctx_mpd_qsqrt, METH_O, doc_ctx_sqrt }, + _DECIMAL_CONTEXT_ABS_METHODDEF + _DECIMAL_CONTEXT_EXP_METHODDEF + _DECIMAL_CONTEXT_LN_METHODDEF + _DECIMAL_CONTEXT_LOG10_METHODDEF + _DECIMAL_CONTEXT_MINUS_METHODDEF + _DECIMAL_CONTEXT_NEXT_MINUS_METHODDEF + _DECIMAL_CONTEXT_NEXT_PLUS_METHODDEF + _DECIMAL_CONTEXT_NORMALIZE_METHODDEF + _DECIMAL_CONTEXT_PLUS_METHODDEF + _DECIMAL_CONTEXT_TO_INTEGRAL_METHODDEF + _DECIMAL_CONTEXT_TO_INTEGRAL_EXACT_METHODDEF + _DECIMAL_CONTEXT_TO_INTEGRAL_VALUE_METHODDEF + _DECIMAL_CONTEXT_SQRT_METHODDEF /* Binary arithmetic functions */ - { "add", ctx_mpd_qadd, METH_VARARGS, doc_ctx_add }, - { "compare", ctx_mpd_qcompare, METH_VARARGS, doc_ctx_compare }, - { "compare_signal", ctx_mpd_qcompare_signal, METH_VARARGS, doc_ctx_compare_signal }, - { "divide", ctx_mpd_qdiv, METH_VARARGS, doc_ctx_divide }, - { "divide_int", ctx_mpd_qdivint, METH_VARARGS, doc_ctx_divide_int }, - { "divmod", ctx_mpd_qdivmod, METH_VARARGS, doc_ctx_divmod }, - { "max", ctx_mpd_qmax, METH_VARARGS, doc_ctx_max }, - { "max_mag", ctx_mpd_qmax_mag, METH_VARARGS, doc_ctx_max_mag }, - { "min", ctx_mpd_qmin, METH_VARARGS, doc_ctx_min }, - { "min_mag", ctx_mpd_qmin_mag, METH_VARARGS, doc_ctx_min_mag }, - { "multiply", ctx_mpd_qmul, METH_VARARGS, doc_ctx_multiply }, - { "next_toward", ctx_mpd_qnext_toward, METH_VARARGS, doc_ctx_next_toward }, - { "quantize", ctx_mpd_qquantize, METH_VARARGS, doc_ctx_quantize }, - { "remainder", ctx_mpd_qrem, METH_VARARGS, doc_ctx_remainder }, - { "remainder_near", ctx_mpd_qrem_near, METH_VARARGS, doc_ctx_remainder_near }, - { "subtract", ctx_mpd_qsub, METH_VARARGS, doc_ctx_subtract }, + _DECIMAL_CONTEXT_ADD_METHODDEF + _DECIMAL_CONTEXT_COMPARE_METHODDEF + _DECIMAL_CONTEXT_COMPARE_SIGNAL_METHODDEF + _DECIMAL_CONTEXT_DIVIDE_METHODDEF + _DECIMAL_CONTEXT_DIVIDE_INT_METHODDEF + _DECIMAL_CONTEXT_DIVMOD_METHODDEF + _DECIMAL_CONTEXT_MAX_METHODDEF + _DECIMAL_CONTEXT_MAX_MAG_METHODDEF + _DECIMAL_CONTEXT_MIN_METHODDEF + _DECIMAL_CONTEXT_MIN_MAG_METHODDEF + _DECIMAL_CONTEXT_MULTIPLY_METHODDEF + _DECIMAL_CONTEXT_NEXT_TOWARD_METHODDEF + _DECIMAL_CONTEXT_QUANTIZE_METHODDEF + _DECIMAL_CONTEXT_REMAINDER_METHODDEF + _DECIMAL_CONTEXT_REMAINDER_NEAR_METHODDEF + _DECIMAL_CONTEXT_SUBTRACT_METHODDEF /* Binary or ternary arithmetic functions */ - { "power", _PyCFunction_CAST(ctx_mpd_qpow), METH_VARARGS|METH_KEYWORDS, doc_ctx_power }, + _DECIMAL_CONTEXT_POWER_METHODDEF /* Ternary arithmetic functions */ - { "fma", ctx_mpd_qfma, METH_VARARGS, doc_ctx_fma }, + _DECIMAL_CONTEXT_FMA_METHODDEF /* No argument */ - { "Etiny", context_getetiny, METH_NOARGS, doc_ctx_Etiny }, - { "Etop", context_getetop, METH_NOARGS, doc_ctx_Etop }, - { "radix", ctx_mpd_radix, METH_NOARGS, doc_ctx_radix }, + _DECIMAL_CONTEXT_ETINY_METHODDEF + _DECIMAL_CONTEXT_ETOP_METHODDEF + _DECIMAL_CONTEXT_RADIX_METHODDEF /* Boolean functions */ - { "is_canonical", ctx_iscanonical, METH_O, doc_ctx_is_canonical }, - { "is_finite", ctx_mpd_isfinite, METH_O, doc_ctx_is_finite }, - { "is_infinite", ctx_mpd_isinfinite, METH_O, doc_ctx_is_infinite }, - { "is_nan", ctx_mpd_isnan, METH_O, doc_ctx_is_nan }, - { "is_normal", ctx_mpd_isnormal, METH_O, doc_ctx_is_normal }, - { "is_qnan", ctx_mpd_isqnan, METH_O, doc_ctx_is_qnan }, - { "is_signed", ctx_mpd_issigned, METH_O, doc_ctx_is_signed }, - { "is_snan", ctx_mpd_issnan, METH_O, doc_ctx_is_snan }, - { "is_subnormal", ctx_mpd_issubnormal, METH_O, doc_ctx_is_subnormal }, - { "is_zero", ctx_mpd_iszero, METH_O, doc_ctx_is_zero }, + _DECIMAL_CONTEXT_IS_CANONICAL_METHODDEF + _DECIMAL_CONTEXT_IS_FINITE_METHODDEF + _DECIMAL_CONTEXT_IS_INFINITE_METHODDEF + _DECIMAL_CONTEXT_IS_NAN_METHODDEF + _DECIMAL_CONTEXT_IS_NORMAL_METHODDEF + _DECIMAL_CONTEXT_IS_QNAN_METHODDEF + _DECIMAL_CONTEXT_IS_SIGNED_METHODDEF + _DECIMAL_CONTEXT_IS_SNAN_METHODDEF + _DECIMAL_CONTEXT_IS_SUBNORMAL_METHODDEF + _DECIMAL_CONTEXT_IS_ZERO_METHODDEF /* Functions with a single decimal argument */ - { "_apply", PyDecContext_Apply, METH_O, NULL }, /* alias for apply */ -#ifdef EXTRA_FUNCTIONALITY - { "apply", PyDecContext_Apply, METH_O, doc_ctx_apply }, -#endif - { "canonical", ctx_canonical, METH_O, doc_ctx_canonical }, - { "copy_abs", ctx_mpd_qcopy_abs, METH_O, doc_ctx_copy_abs }, - { "copy_decimal", ctx_copy_decimal, METH_O, doc_ctx_copy_decimal }, - { "copy_negate", ctx_mpd_qcopy_negate, METH_O, doc_ctx_copy_negate }, - { "logb", ctx_mpd_qlogb, METH_O, doc_ctx_logb }, - { "logical_invert", ctx_mpd_qinvert, METH_O, doc_ctx_logical_invert }, - { "number_class", ctx_mpd_class, METH_O, doc_ctx_number_class }, - { "to_sci_string", ctx_mpd_to_sci, METH_O, doc_ctx_to_sci_string }, - { "to_eng_string", ctx_mpd_to_eng, METH_O, doc_ctx_to_eng_string }, + _DECIMAL_CONTEXT__APPLY_METHODDEF + _DECIMAL_CONTEXT_APPLY_METHODDEF + _DECIMAL_CONTEXT_CANONICAL_METHODDEF + _DECIMAL_CONTEXT_COPY_ABS_METHODDEF + _DECIMAL_CONTEXT_COPY_DECIMAL_METHODDEF + _DECIMAL_CONTEXT_COPY_NEGATE_METHODDEF + _DECIMAL_CONTEXT_LOGB_METHODDEF + _DECIMAL_CONTEXT_LOGICAL_INVERT_METHODDEF + _DECIMAL_CONTEXT_NUMBER_CLASS_METHODDEF + _DECIMAL_CONTEXT_TO_SCI_STRING_METHODDEF + _DECIMAL_CONTEXT_TO_ENG_STRING_METHODDEF /* Functions with two decimal arguments */ - { "compare_total", ctx_mpd_compare_total, METH_VARARGS, doc_ctx_compare_total }, - { "compare_total_mag", ctx_mpd_compare_total_mag, METH_VARARGS, doc_ctx_compare_total_mag }, - { "copy_sign", ctx_mpd_qcopy_sign, METH_VARARGS, doc_ctx_copy_sign }, - { "logical_and", ctx_mpd_qand, METH_VARARGS, doc_ctx_logical_and }, - { "logical_or", ctx_mpd_qor, METH_VARARGS, doc_ctx_logical_or }, - { "logical_xor", ctx_mpd_qxor, METH_VARARGS, doc_ctx_logical_xor }, - { "rotate", ctx_mpd_qrotate, METH_VARARGS, doc_ctx_rotate }, - { "same_quantum", ctx_mpd_same_quantum, METH_VARARGS, doc_ctx_same_quantum }, - { "scaleb", ctx_mpd_qscaleb, METH_VARARGS, doc_ctx_scaleb }, - { "shift", ctx_mpd_qshift, METH_VARARGS, doc_ctx_shift }, + _DECIMAL_CONTEXT_COMPARE_TOTAL_METHODDEF + _DECIMAL_CONTEXT_COMPARE_TOTAL_MAG_METHODDEF + _DECIMAL_CONTEXT_COPY_SIGN_METHODDEF + _DECIMAL_CONTEXT_LOGICAL_AND_METHODDEF + _DECIMAL_CONTEXT_LOGICAL_OR_METHODDEF + _DECIMAL_CONTEXT_LOGICAL_XOR_METHODDEF + _DECIMAL_CONTEXT_ROTATE_METHODDEF + _DECIMAL_CONTEXT_SAME_QUANTUM_METHODDEF + _DECIMAL_CONTEXT_SCALEB_METHODDEF + _DECIMAL_CONTEXT_SHIFT_METHODDEF /* Set context values */ - { "clear_flags", context_clear_flags, METH_NOARGS, doc_ctx_clear_flags }, - { "clear_traps", context_clear_traps, METH_NOARGS, doc_ctx_clear_traps }, + _DECIMAL_CONTEXT_CLEAR_FLAGS_METHODDEF + _DECIMAL_CONTEXT_CLEAR_TRAPS_METHODDEF -#ifdef CONFIG_32 /* Unsafe set functions with relaxed range checks */ - { "_unsafe_setprec", context_unsafe_setprec, METH_O, NULL }, - { "_unsafe_setemin", context_unsafe_setemin, METH_O, NULL }, - { "_unsafe_setemax", context_unsafe_setemax, METH_O, NULL }, -#endif + _DECIMAL_CONTEXT__UNSAFE_SETPREC_METHODDEF + _DECIMAL_CONTEXT__UNSAFE_SETEMIN_METHODDEF + _DECIMAL_CONTEXT__UNSAFE_SETEMAX_METHODDEF /* Miscellaneous */ - { "__copy__", context_copy, METH_NOARGS, NULL }, - { "__reduce__", context_reduce, METH_NOARGS, NULL }, - { "copy", context_copy, METH_NOARGS, doc_ctx_copy }, - { "create_decimal", ctx_create_decimal, METH_VARARGS, doc_ctx_create_decimal }, - { "create_decimal_from_float", ctx_from_float, METH_O, doc_ctx_create_decimal_from_float }, + _DECIMAL_CONTEXT___COPY___METHODDEF + _DECIMAL_CONTEXT___REDUCE___METHODDEF + _DECIMAL_CONTEXT_COPY_METHODDEF + _DECIMAL_CONTEXT_CREATE_DECIMAL_METHODDEF + _DECIMAL_CONTEXT_CREATE_DECIMAL_FROM_FLOAT_METHODDEF { NULL, NULL, 1 } }; @@ -5862,7 +7557,7 @@ static PyType_Slot context_slots[] = { {Py_tp_repr, context_repr}, {Py_tp_getattro, context_getattr}, {Py_tp_setattro, context_setattr}, - {Py_tp_doc, (void *)doc_context}, + {Py_tp_doc, (void *)context_init__doc__}, {Py_tp_methods, context_methods}, {Py_tp_getset, context_getsets}, {Py_tp_init, context_init}, @@ -5879,12 +7574,35 @@ static PyType_Spec context_spec = { }; +static PyObject * +decimal_getattr(PyObject *self, PyObject *args) +{ + PyObject *name; + if (!PyArg_UnpackTuple(args, "__getattr__", 1, 1, &name)) { + return NULL; + } + + if (PyUnicode_Check(name) && PyUnicode_EqualToUTF8(name, "__version__")) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + 1) < 0) { + return NULL; + } + return PyUnicode_FromString(MPD_SPEC_VERSION); + } + + PyErr_Format(PyExc_AttributeError, "module 'decimal' has no attribute %R", name); + return NULL; +} + + static PyMethodDef _decimal_methods [] = { - { "getcontext", PyDec_GetCurrentContext, METH_NOARGS, doc_getcontext}, - { "setcontext", PyDec_SetCurrentContext, METH_O, doc_setcontext}, - { "localcontext", _PyCFunction_CAST(ctxmanager_new), METH_VARARGS|METH_KEYWORDS, doc_localcontext}, - { "IEEEContext", ieee_context, METH_O, doc_ieee_context}, + _DECIMAL_GETCONTEXT_METHODDEF + _DECIMAL_SETCONTEXT_METHODDEF + _DECIMAL_LOCALCONTEXT_METHODDEF + _DECIMAL_IEEECONTEXT_METHODDEF + {"__getattr__", decimal_getattr, METH_VARARGS, "Module __getattr__"}, { NULL, NULL, 1, NULL } }; @@ -6040,10 +7758,15 @@ _decimal_exec(PyObject *m) /* DecimalTuple */ ASSIGN_PTR(collections, PyImport_ImportModule("collections")); - ASSIGN_PTR(state->DecimalTuple, (PyTypeObject *)PyObject_CallMethod(collections, - "namedtuple", "(ss)", "DecimalTuple", - "sign digits exponent")); - + ASSIGN_PTR(obj, PyObject_CallMethod(collections, "namedtuple", "(ss)", + "DecimalTuple", + "sign digits exponent")); + if (!PyType_Check(obj)) { + PyErr_SetString(PyExc_TypeError, + "type is expected from namedtuple call"); + goto error; + } + ASSIGN_PTR(state->DecimalTuple, (PyTypeObject *)obj); ASSIGN_PTR(obj, PyUnicode_FromString("decimal")); CHECK_INT(PyDict_SetItemString(state->DecimalTuple->tp_dict, "__module__", obj)); Py_CLEAR(obj); @@ -6091,15 +7814,15 @@ _decimal_exec(PyObject *m) switch (cm->flag) { case MPD_Float_operation: - base = PyTuple_Pack(2, state->DecimalException, PyExc_TypeError); + base = _PyTuple_FromPair(state->DecimalException, PyExc_TypeError); break; case MPD_Division_by_zero: - base = PyTuple_Pack(2, state->DecimalException, - PyExc_ZeroDivisionError); + base = _PyTuple_FromPair(state->DecimalException, + PyExc_ZeroDivisionError); break; case MPD_Overflow: - base = PyTuple_Pack(2, state->signal_map[INEXACT].ex, - state->signal_map[ROUNDED].ex); + base = _PyTuple_FromPair(state->signal_map[INEXACT].ex, + state->signal_map[ROUNDED].ex); break; case MPD_Underflow: base = PyTuple_Pack(3, state->signal_map[INEXACT].ex, @@ -6138,7 +7861,7 @@ _decimal_exec(PyObject *m) for (cm = state->cond_map+1; cm->name != NULL; cm++) { PyObject *base; if (cm->flag == MPD_Division_undefined) { - base = PyTuple_Pack(2, state->signal_map[0].ex, PyExc_ZeroDivisionError); + base = _PyTuple_FromPair(state->signal_map[0].ex, PyExc_ZeroDivisionError); } else { base = PyTuple_Pack(1, state->signal_map[0].ex); @@ -6204,7 +7927,7 @@ _decimal_exec(PyObject *m) } /* Add specification version number */ - CHECK_INT(PyModule_AddStringConstant(m, "__version__", "1.70")); + CHECK_INT(PyModule_AddStringConstant(m, "SPEC_VERSION", MPD_SPEC_VERSION)); CHECK_INT(PyModule_AddStringConstant(m, "__libmpdec_version__", mpd_version())); return 0; @@ -6310,6 +8033,7 @@ decimal_free(void *module) } static struct PyModuleDef_Slot _decimal_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _decimal_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -6319,7 +8043,7 @@ static struct PyModuleDef_Slot _decimal_slots[] = { static struct PyModuleDef _decimal_module = { PyModuleDef_HEAD_INIT, .m_name = "decimal", - .m_doc = doc__decimal, + .m_doc = "C decimal arithmetic module", .m_size = sizeof(decimal_state), .m_methods = _decimal_methods, .m_slots = _decimal_slots, diff --git a/Modules/_decimal/clinic/_decimal.c.h b/Modules/_decimal/clinic/_decimal.c.h new file mode 100644 index 00000000000000..c803006ad44382 --- /dev/null +++ b/Modules/_decimal/clinic/_decimal.c.h @@ -0,0 +1,6987 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +PyDoc_STRVAR(_decimal_Context_Etiny__doc__, +"Etiny($self, /)\n" +"--\n" +"\n" +"Return a value equal to Emin - prec + 1.\n" +"\n" +"This is the minimum exponent value for subnormal results. When\n" +"underflow occurs, the exponent is set to Etiny."); + +#define _DECIMAL_CONTEXT_ETINY_METHODDEF \ + {"Etiny", (PyCFunction)_decimal_Context_Etiny, METH_NOARGS, _decimal_Context_Etiny__doc__}, + +static PyObject * +_decimal_Context_Etiny_impl(PyObject *self); + +static PyObject * +_decimal_Context_Etiny(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Context_Etiny_impl(self); +} + +PyDoc_STRVAR(_decimal_Context_Etop__doc__, +"Etop($self, /)\n" +"--\n" +"\n" +"Return a value equal to Emax - prec + 1.\n" +"\n" +"This is the maximum exponent if the _clamp field of the context is\n" +"set to 1 (IEEE clamp mode). Etop() must not be negative."); + +#define _DECIMAL_CONTEXT_ETOP_METHODDEF \ + {"Etop", (PyCFunction)_decimal_Context_Etop, METH_NOARGS, _decimal_Context_Etop__doc__}, + +static PyObject * +_decimal_Context_Etop_impl(PyObject *self); + +static PyObject * +_decimal_Context_Etop(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Context_Etop_impl(self); +} + +#if defined(CONFIG_32) + +PyDoc_STRVAR(_decimal_Context__unsafe_setprec__doc__, +"_unsafe_setprec($self, x, /)\n" +"--\n" +"\n"); + +#define _DECIMAL_CONTEXT__UNSAFE_SETPREC_METHODDEF \ + {"_unsafe_setprec", (PyCFunction)_decimal_Context__unsafe_setprec, METH_O, _decimal_Context__unsafe_setprec__doc__}, + +static PyObject * +_decimal_Context__unsafe_setprec_impl(PyObject *self, Py_ssize_t x); + +static PyObject * +_decimal_Context__unsafe_setprec(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_ssize_t x; + + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(arg); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + x = ival; + } + return_value = _decimal_Context__unsafe_setprec_impl(self, x); + +exit: + return return_value; +} + +#endif /* defined(CONFIG_32) */ + +#if defined(CONFIG_32) + +PyDoc_STRVAR(_decimal_Context__unsafe_setemin__doc__, +"_unsafe_setemin($self, x, /)\n" +"--\n" +"\n"); + +#define _DECIMAL_CONTEXT__UNSAFE_SETEMIN_METHODDEF \ + {"_unsafe_setemin", (PyCFunction)_decimal_Context__unsafe_setemin, METH_O, _decimal_Context__unsafe_setemin__doc__}, + +static PyObject * +_decimal_Context__unsafe_setemin_impl(PyObject *self, Py_ssize_t x); + +static PyObject * +_decimal_Context__unsafe_setemin(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_ssize_t x; + + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(arg); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + x = ival; + } + return_value = _decimal_Context__unsafe_setemin_impl(self, x); + +exit: + return return_value; +} + +#endif /* defined(CONFIG_32) */ + +#if defined(CONFIG_32) + +PyDoc_STRVAR(_decimal_Context__unsafe_setemax__doc__, +"_unsafe_setemax($self, x, /)\n" +"--\n" +"\n"); + +#define _DECIMAL_CONTEXT__UNSAFE_SETEMAX_METHODDEF \ + {"_unsafe_setemax", (PyCFunction)_decimal_Context__unsafe_setemax, METH_O, _decimal_Context__unsafe_setemax__doc__}, + +static PyObject * +_decimal_Context__unsafe_setemax_impl(PyObject *self, Py_ssize_t x); + +static PyObject * +_decimal_Context__unsafe_setemax(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_ssize_t x; + + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(arg); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + x = ival; + } + return_value = _decimal_Context__unsafe_setemax_impl(self, x); + +exit: + return return_value; +} + +#endif /* defined(CONFIG_32) */ + +PyDoc_STRVAR(_decimal_Context_clear_traps__doc__, +"clear_traps($self, /)\n" +"--\n" +"\n" +"Set all traps to False."); + +#define _DECIMAL_CONTEXT_CLEAR_TRAPS_METHODDEF \ + {"clear_traps", (PyCFunction)_decimal_Context_clear_traps, METH_NOARGS, _decimal_Context_clear_traps__doc__}, + +static PyObject * +_decimal_Context_clear_traps_impl(PyObject *self); + +static PyObject * +_decimal_Context_clear_traps(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Context_clear_traps_impl(self); +} + +PyDoc_STRVAR(_decimal_Context_clear_flags__doc__, +"clear_flags($self, /)\n" +"--\n" +"\n" +"Reset all flags to False."); + +#define _DECIMAL_CONTEXT_CLEAR_FLAGS_METHODDEF \ + {"clear_flags", (PyCFunction)_decimal_Context_clear_flags, METH_NOARGS, _decimal_Context_clear_flags__doc__}, + +static PyObject * +_decimal_Context_clear_flags_impl(PyObject *self); + +static PyObject * +_decimal_Context_clear_flags(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Context_clear_flags_impl(self); +} + +PyDoc_STRVAR(context_init__doc__, +"Context(prec=None, rounding=None, Emin=None, Emax=None, capitals=None,\n" +" clamp=None, flags=None, traps=None)\n" +"--\n" +"\n" +"Create context.\n" +"\n" +"The context affects almost all operations and controls rounding,\n" +"Over/Underflow, raising of exceptions and much more. A new context\n" +"can be constructed as follows:\n" +"\n" +" >>> c = Context(prec=28, Emin=-425000000, Emax=425000000,\n" +" ... rounding=ROUND_HALF_EVEN, capitals=1, clamp=1,\n" +" ... traps=[InvalidOperation, DivisionByZero, Overflow],\n" +" ... flags=[])\n" +" >>>"); + +static int +context_init_impl(PyObject *self, PyObject *prec, PyObject *rounding, + PyObject *emin, PyObject *emax, PyObject *capitals, + PyObject *clamp, PyObject *status, PyObject *traps); + +static int +context_init(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 8 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(prec), &_Py_ID(rounding), &_Py_ID(Emin), &_Py_ID(Emax), &_Py_ID(capitals), &_Py_ID(clamp), &_Py_ID(flags), &_Py_ID(traps), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"prec", "rounding", "Emin", "Emax", "capitals", "clamp", "flags", "traps", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "Context", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[8]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; + PyObject *prec = Py_None; + PyObject *rounding = Py_None; + PyObject *emin = Py_None; + PyObject *emax = Py_None; + PyObject *capitals = Py_None; + PyObject *clamp = Py_None; + PyObject *status = Py_None; + PyObject *traps = Py_None; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 8, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (fastargs[0]) { + prec = fastargs[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[1]) { + rounding = fastargs[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[2]) { + emin = fastargs[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[3]) { + emax = fastargs[3]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[4]) { + capitals = fastargs[4]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[5]) { + clamp = fastargs[5]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[6]) { + status = fastargs[6]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + traps = fastargs[7]; +skip_optional_pos: + return_value = context_init_impl(self, prec, rounding, emin, emax, capitals, clamp, status, traps); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_IEEEContext__doc__, +"IEEEContext($module, bits, /)\n" +"--\n" +"\n" +"Return a context, initialized as one of the IEEE interchange formats.\n" +"\n" +"The argument must be a multiple of 32 and less than\n" +"IEEE_CONTEXT_MAX_BITS."); + +#define _DECIMAL_IEEECONTEXT_METHODDEF \ + {"IEEEContext", (PyCFunction)_decimal_IEEEContext, METH_O, _decimal_IEEEContext__doc__}, + +static PyObject * +_decimal_IEEEContext_impl(PyObject *module, Py_ssize_t bits); + +static PyObject * +_decimal_IEEEContext(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_ssize_t bits; + + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(arg); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + bits = ival; + } + return_value = _decimal_IEEEContext_impl(module, bits); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_copy__doc__, +"copy($self, /)\n" +"--\n" +"\n" +"Return a duplicate of the context with all flags cleared."); + +#define _DECIMAL_CONTEXT_COPY_METHODDEF \ + {"copy", _PyCFunction_CAST(_decimal_Context_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_copy__doc__}, + +static PyObject * +_decimal_Context_copy_impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Context_copy(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "copy() takes no arguments"); + return NULL; + } + return _decimal_Context_copy_impl(self, cls); +} + +PyDoc_STRVAR(_decimal_Context___copy____doc__, +"__copy__($self, /)\n" +"--\n" +"\n"); + +#define _DECIMAL_CONTEXT___COPY___METHODDEF \ + {"__copy__", _PyCFunction_CAST(_decimal_Context___copy__), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context___copy____doc__}, + +static PyObject * +_decimal_Context___copy___impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Context___copy__(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "__copy__() takes no arguments"); + return NULL; + } + return _decimal_Context___copy___impl(self, cls); +} + +PyDoc_STRVAR(_decimal_Context___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n"); + +#define _DECIMAL_CONTEXT___REDUCE___METHODDEF \ + {"__reduce__", _PyCFunction_CAST(_decimal_Context___reduce__), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context___reduce____doc__}, + +static PyObject * +_decimal_Context___reduce___impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Context___reduce__(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "__reduce__() takes no arguments"); + return NULL; + } + return _decimal_Context___reduce___impl(self, cls); +} + +PyDoc_STRVAR(_decimal_getcontext__doc__, +"getcontext($module, /)\n" +"--\n" +"\n" +"Get the current default context."); + +#define _DECIMAL_GETCONTEXT_METHODDEF \ + {"getcontext", (PyCFunction)_decimal_getcontext, METH_NOARGS, _decimal_getcontext__doc__}, + +static PyObject * +_decimal_getcontext_impl(PyObject *module); + +static PyObject * +_decimal_getcontext(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_getcontext_impl(module); +} + +PyDoc_STRVAR(_decimal_setcontext__doc__, +"setcontext($module, context, /)\n" +"--\n" +"\n" +"Set a new default context."); + +#define _DECIMAL_SETCONTEXT_METHODDEF \ + {"setcontext", (PyCFunction)_decimal_setcontext, METH_O, _decimal_setcontext__doc__}, + +PyDoc_STRVAR(_decimal_localcontext__doc__, +"localcontext($module, /, ctx=None, **kwargs)\n" +"--\n" +"\n" +"Return a context manager for a copy of the supplied context.\n" +"\n" +"That will set the default context to a copy of ctx on entry to the\n" +"with-statement and restore the previous default context when exiting\n" +"the with-statement. If no context is specified, a copy of the current\n" +"default context is used."); + +#define _DECIMAL_LOCALCONTEXT_METHODDEF \ + {"localcontext", _PyCFunction_CAST(_decimal_localcontext), METH_FASTCALL|METH_KEYWORDS, _decimal_localcontext__doc__}, + +static PyObject * +_decimal_localcontext_impl(PyObject *module, PyObject *local, PyObject *prec, + PyObject *rounding, PyObject *Emin, + PyObject *Emax, PyObject *capitals, + PyObject *clamp, PyObject *flags, PyObject *traps); + +static PyObject * +_decimal_localcontext(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 9 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(ctx), &_Py_ID(prec), &_Py_ID(rounding), &_Py_ID(Emin), &_Py_ID(Emax), &_Py_ID(capitals), &_Py_ID(clamp), &_Py_ID(flags), &_Py_ID(traps), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"ctx", "prec", "rounding", "Emin", "Emax", "capitals", "clamp", "flags", "traps", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "localcontext", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[9]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *local = Py_None; + PyObject *prec = Py_None; + PyObject *rounding = Py_None; + PyObject *Emin = Py_None; + PyObject *Emax = Py_None; + PyObject *capitals = Py_None; + PyObject *clamp = Py_None; + PyObject *flags = Py_None; + PyObject *traps = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + local = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + prec = args[1]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[2]) { + rounding = args[2]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[3]) { + Emin = args[3]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[4]) { + Emax = args[4]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[5]) { + capitals = args[5]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[6]) { + clamp = args[6]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[7]) { + flags = args[7]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + traps = args[8]; +skip_optional_kwonly: + return_value = _decimal_localcontext_impl(module, local, prec, rounding, Emin, Emax, capitals, clamp, flags, traps); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_from_float__doc__, +"from_float($type, f, /)\n" +"--\n" +"\n" +"Class method that converts a float to a decimal number, exactly.\n" +"\n" +"Since 0.1 is not exactly representable in binary floating point,\n" +"Decimal.from_float(0.1) is not the same as Decimal(\'0.1\').\n" +"\n" +" >>> Decimal.from_float(0.1)\n" +" Decimal(\'0.1000000000000000055511151231257827021181583404541015625\')\n" +" >>> Decimal.from_float(float(\'nan\'))\n" +" Decimal(\'NaN\')\n" +" >>> Decimal.from_float(float(\'inf\'))\n" +" Decimal(\'Infinity\')\n" +" >>> Decimal.from_float(float(\'-inf\'))\n" +" Decimal(\'-Infinity\')"); + +#define _DECIMAL_DECIMAL_FROM_FLOAT_METHODDEF \ + {"from_float", _PyCFunction_CAST(_decimal_Decimal_from_float), METH_METHOD|METH_FASTCALL|METH_KEYWORDS|METH_CLASS, _decimal_Decimal_from_float__doc__}, + +static PyObject * +_decimal_Decimal_from_float_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *pyfloat); + +static PyObject * +_decimal_Decimal_from_float(PyObject *type, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "from_float", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *pyfloat; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + pyfloat = args[0]; + return_value = _decimal_Decimal_from_float_impl((PyTypeObject *)type, cls, pyfloat); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_from_number__doc__, +"from_number($type, number, /)\n" +"--\n" +"\n" +"Class method that converts a real number to a decimal number, exactly.\n" +"\n" +" >>> Decimal.from_number(314) # int\n" +" Decimal(\'314\')\n" +" >>> Decimal.from_number(0.1) # float\n" +" Decimal(\'0.1000000000000000055511151231257827021181583404541015625\')\n" +" >>> Decimal.from_number(Decimal(\'3.14\')) # another decimal instance\n" +" Decimal(\'3.14\')"); + +#define _DECIMAL_DECIMAL_FROM_NUMBER_METHODDEF \ + {"from_number", _PyCFunction_CAST(_decimal_Decimal_from_number), METH_METHOD|METH_FASTCALL|METH_KEYWORDS|METH_CLASS, _decimal_Decimal_from_number__doc__}, + +static PyObject * +_decimal_Decimal_from_number_impl(PyTypeObject *type, PyTypeObject *cls, + PyObject *number); + +static PyObject * +_decimal_Decimal_from_number(PyObject *type, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "from_number", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *number; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + number = args[0]; + return_value = _decimal_Decimal_from_number_impl((PyTypeObject *)type, cls, number); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_create_decimal_from_float__doc__, +"create_decimal_from_float($self, f, /)\n" +"--\n" +"\n" +"Create a new Decimal instance from float f.\n" +"\n" +"Unlike the Decimal.from_float() class method, this function observes\n" +"the context limits."); + +#define _DECIMAL_CONTEXT_CREATE_DECIMAL_FROM_FLOAT_METHODDEF \ + {"create_decimal_from_float", _PyCFunction_CAST(_decimal_Context_create_decimal_from_float), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_create_decimal_from_float__doc__}, + +static PyObject * +_decimal_Context_create_decimal_from_float_impl(PyObject *context, + PyTypeObject *cls, + PyObject *f); + +static PyObject * +_decimal_Context_create_decimal_from_float(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "create_decimal_from_float", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *f; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + f = args[0]; + return_value = _decimal_Context_create_decimal_from_float_impl(context, cls, f); + +exit: + return return_value; +} + +PyDoc_STRVAR(dec_new__doc__, +"Decimal(value=\'0\', context=None)\n" +"--\n" +"\n" +"Construct a new Decimal object.\n" +"\n" +"value can be an integer, string, tuple, or another Decimal object. If\n" +"no value is given, return Decimal(\'0\'). The context does not affect\n" +"the conversion and is only passed to determine if the InvalidOperation\n" +"trap is active."); + +static PyObject * +dec_new_impl(PyTypeObject *type, PyObject *value, PyObject *context); + +static PyObject * +dec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(value), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"value", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "Decimal", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; + PyObject *value = NULL; + PyObject *context = Py_None; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (fastargs[0]) { + value = fastargs[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + context = fastargs[1]; +skip_optional_pos: + return_value = dec_new_impl(type, value, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_create_decimal__doc__, +"create_decimal($self, num=\'0\', /)\n" +"--\n" +"\n" +"Create a new Decimal instance from num, using self as the context.\n" +"\n" +"Unlike the Decimal constructor, this function observes the context\n" +"limits."); + +#define _DECIMAL_CONTEXT_CREATE_DECIMAL_METHODDEF \ + {"create_decimal", _PyCFunction_CAST(_decimal_Context_create_decimal), METH_FASTCALL, _decimal_Context_create_decimal__doc__}, + +static PyObject * +_decimal_Context_create_decimal_impl(PyObject *context, PyObject *num); + +static PyObject * +_decimal_Context_create_decimal(PyObject *context, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *num = NULL; + + if (!_PyArg_CheckPositional("create_decimal", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + num = args[0]; +skip_optional: + return_value = _decimal_Context_create_decimal_impl(context, num); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal___format____doc__, +"__format__($self, format_spec, override=<unrepresentable>, /)\n" +"--\n" +"\n" +"Formats the Decimal according to format_spec."); + +#define _DECIMAL_DECIMAL___FORMAT___METHODDEF \ + {"__format__", _PyCFunction_CAST(_decimal_Decimal___format__), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal___format____doc__}, + +static PyObject * +_decimal_Decimal___format___impl(PyObject *dec, PyTypeObject *cls, + PyObject *fmtarg, PyObject *override); + +static PyObject * +_decimal_Decimal___format__(PyObject *dec, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "__format__", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *fmtarg; + PyObject *override = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("__format__", "argument 1", "str", args[0]); + goto exit; + } + fmtarg = args[0]; + if (nargs < 2) { + goto skip_optional_posonly; + } + override = args[1]; +skip_optional_posonly: + return_value = _decimal_Decimal___format___impl(dec, cls, fmtarg, override); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_as_integer_ratio__doc__, +"as_integer_ratio($self, /)\n" +"--\n" +"\n" +"Return a pair of integers whose ratio is exactly equal to the original.\n" +"\n" +"The ratio is in lowest terms and with a positive denominator.\n" +"Raise OverflowError on infinities and a ValueError on NaNs."); + +#define _DECIMAL_DECIMAL_AS_INTEGER_RATIO_METHODDEF \ + {"as_integer_ratio", _PyCFunction_CAST(_decimal_Decimal_as_integer_ratio), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_as_integer_ratio__doc__}, + +static PyObject * +_decimal_Decimal_as_integer_ratio_impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Decimal_as_integer_ratio(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "as_integer_ratio() takes no arguments"); + return NULL; + } + return _decimal_Decimal_as_integer_ratio_impl(self, cls); +} + +PyDoc_STRVAR(_decimal_Decimal_to_integral_value__doc__, +"to_integral_value($self, /, rounding=None, context=None)\n" +"--\n" +"\n" +"Round to the nearest integer without signaling Inexact or Rounded.\n" +"\n" +"The rounding mode is determined by the rounding parameter if given,\n" +"else by the given context. If neither parameter is given, then the\n" +"rounding mode of the current default context is used."); + +#define _DECIMAL_DECIMAL_TO_INTEGRAL_VALUE_METHODDEF \ + {"to_integral_value", _PyCFunction_CAST(_decimal_Decimal_to_integral_value), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_to_integral_value__doc__}, + +static PyObject * +_decimal_Decimal_to_integral_value_impl(PyObject *self, PyTypeObject *cls, + PyObject *rounding, + PyObject *context); + +static PyObject * +_decimal_Decimal_to_integral_value(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(rounding), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"rounding", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "to_integral_value", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *rounding = Py_None; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + rounding = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_to_integral_value_impl(self, cls, rounding, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_to_integral__doc__, +"to_integral($self, /, rounding=None, context=None)\n" +"--\n" +"\n" +"Identical to the to_integral_value() method.\n" +"\n" +"The to_integral() name has been kept for compatibility with older\n" +"versions."); + +#define _DECIMAL_DECIMAL_TO_INTEGRAL_METHODDEF \ + {"to_integral", _PyCFunction_CAST(_decimal_Decimal_to_integral), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_to_integral__doc__}, + +static PyObject * +_decimal_Decimal_to_integral_impl(PyObject *self, PyTypeObject *cls, + PyObject *rounding, PyObject *context); + +static PyObject * +_decimal_Decimal_to_integral(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(rounding), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"rounding", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "to_integral", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *rounding = Py_None; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + rounding = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_to_integral_impl(self, cls, rounding, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_to_integral_exact__doc__, +"to_integral_exact($self, /, rounding=None, context=None)\n" +"--\n" +"\n" +"Round to the nearest integer.\n" +"\n" +"This method signals Inexact or Rounded as appropriate if rounding\n" +"occurs. The rounding mode is determined by the rounding parameter\n" +"if given, else by the given context. If neither parameter is given,\n" +"then the rounding mode of the current default context is used."); + +#define _DECIMAL_DECIMAL_TO_INTEGRAL_EXACT_METHODDEF \ + {"to_integral_exact", _PyCFunction_CAST(_decimal_Decimal_to_integral_exact), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_to_integral_exact__doc__}, + +static PyObject * +_decimal_Decimal_to_integral_exact_impl(PyObject *self, PyTypeObject *cls, + PyObject *rounding, + PyObject *context); + +static PyObject * +_decimal_Decimal_to_integral_exact(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(rounding), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"rounding", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "to_integral_exact", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *rounding = Py_None; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + rounding = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_to_integral_exact_impl(self, cls, rounding, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal___round____doc__, +"__round__($self, ndigits=<unrepresentable>, /)\n" +"--\n" +"\n" +"Return the Integral closest to self, rounding half toward even."); + +#define _DECIMAL_DECIMAL___ROUND___METHODDEF \ + {"__round__", _PyCFunction_CAST(_decimal_Decimal___round__), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal___round____doc__}, + +static PyObject * +_decimal_Decimal___round___impl(PyObject *self, PyTypeObject *cls, + PyObject *ndigits); + +static PyObject * +_decimal_Decimal___round__(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "__round__", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *ndigits = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (nargs < 1) { + goto skip_optional_posonly; + } + ndigits = args[0]; +skip_optional_posonly: + return_value = _decimal_Decimal___round___impl(self, cls, ndigits); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_as_tuple__doc__, +"as_tuple($self, /)\n" +"--\n" +"\n" +"Return a tuple representation of the number."); + +#define _DECIMAL_DECIMAL_AS_TUPLE_METHODDEF \ + {"as_tuple", _PyCFunction_CAST(_decimal_Decimal_as_tuple), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_as_tuple__doc__}, + +static PyObject * +_decimal_Decimal_as_tuple_impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Decimal_as_tuple(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "as_tuple() takes no arguments"); + return NULL; + } + return _decimal_Decimal_as_tuple_impl(self, cls); +} + +PyDoc_STRVAR(_decimal_Decimal_exp__doc__, +"exp($self, /, context=None)\n" +"--\n" +"\n" +"Return the value of the (natural) exponential function e**x.\n" +"\n" +"The function always uses the ROUND_HALF_EVEN mode and the result is\n" +"correctly rounded."); + +#define _DECIMAL_DECIMAL_EXP_METHODDEF \ + {"exp", _PyCFunction_CAST(_decimal_Decimal_exp), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_exp__doc__}, + +static PyObject * +_decimal_Decimal_exp_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_exp(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "exp", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_exp_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_ln__doc__, +"ln($self, /, context=None)\n" +"--\n" +"\n" +"Return the natural (base e) logarithm of the operand.\n" +"\n" +"The function always uses the ROUND_HALF_EVEN mode and the result is\n" +"correctly rounded."); + +#define _DECIMAL_DECIMAL_LN_METHODDEF \ + {"ln", _PyCFunction_CAST(_decimal_Decimal_ln), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_ln__doc__}, + +static PyObject * +_decimal_Decimal_ln_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_ln(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "ln", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_ln_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_log10__doc__, +"log10($self, /, context=None)\n" +"--\n" +"\n" +"Return the base ten logarithm of the operand.\n" +"\n" +"The function always uses the ROUND_HALF_EVEN mode and the result is\n" +"correctly rounded."); + +#define _DECIMAL_DECIMAL_LOG10_METHODDEF \ + {"log10", _PyCFunction_CAST(_decimal_Decimal_log10), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_log10__doc__}, + +static PyObject * +_decimal_Decimal_log10_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_log10(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "log10", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_log10_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_next_minus__doc__, +"next_minus($self, /, context=None)\n" +"--\n" +"\n" +"Returns the largest representable number smaller than itself."); + +#define _DECIMAL_DECIMAL_NEXT_MINUS_METHODDEF \ + {"next_minus", _PyCFunction_CAST(_decimal_Decimal_next_minus), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_next_minus__doc__}, + +static PyObject * +_decimal_Decimal_next_minus_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_next_minus(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "next_minus", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_next_minus_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_next_plus__doc__, +"next_plus($self, /, context=None)\n" +"--\n" +"\n" +"Returns the smallest representable number larger than itself."); + +#define _DECIMAL_DECIMAL_NEXT_PLUS_METHODDEF \ + {"next_plus", _PyCFunction_CAST(_decimal_Decimal_next_plus), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_next_plus__doc__}, + +static PyObject * +_decimal_Decimal_next_plus_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_next_plus(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "next_plus", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_next_plus_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_normalize__doc__, +"normalize($self, /, context=None)\n" +"--\n" +"\n" +"Normalize the number by stripping trailing 0s\n" +"\n" +"This also change anything equal to 0 to 0e0. Used for producing\n" +"canonical values for members of an equivalence class. For example,\n" +"Decimal(\'32.100\') and Decimal(\'0.321000e+2\') both normalize to\n" +"the equivalent value Decimal(\'32.1\')."); + +#define _DECIMAL_DECIMAL_NORMALIZE_METHODDEF \ + {"normalize", _PyCFunction_CAST(_decimal_Decimal_normalize), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_normalize__doc__}, + +static PyObject * +_decimal_Decimal_normalize_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_normalize(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "normalize", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_normalize_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_sqrt__doc__, +"sqrt($self, /, context=None)\n" +"--\n" +"\n" +"Return the square root of the argument to full precision.\n" +"\n" +"The result is correctly rounded using the ROUND_HALF_EVEN rounding\n" +"mode."); + +#define _DECIMAL_DECIMAL_SQRT_METHODDEF \ + {"sqrt", _PyCFunction_CAST(_decimal_Decimal_sqrt), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_sqrt__doc__}, + +static PyObject * +_decimal_Decimal_sqrt_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_sqrt(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sqrt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_sqrt_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_compare__doc__, +"compare($self, /, other, context=None)\n" +"--\n" +"\n" +"Compare self to other.\n" +"\n" +"Return a decimal value:\n" +"\n" +" a or b is a NaN ==> Decimal(\'NaN\')\n" +" a < b ==> Decimal(\'-1\')\n" +" a == b ==> Decimal(\'0\')\n" +" a > b ==> Decimal(\'1\')"); + +#define _DECIMAL_DECIMAL_COMPARE_METHODDEF \ + {"compare", _PyCFunction_CAST(_decimal_Decimal_compare), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_compare__doc__}, + +static PyObject * +_decimal_Decimal_compare_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_compare(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "compare", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_compare_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_compare_signal__doc__, +"compare_signal($self, /, other, context=None)\n" +"--\n" +"\n" +"Identical to compare, except that all NaNs signal."); + +#define _DECIMAL_DECIMAL_COMPARE_SIGNAL_METHODDEF \ + {"compare_signal", _PyCFunction_CAST(_decimal_Decimal_compare_signal), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_compare_signal__doc__}, + +static PyObject * +_decimal_Decimal_compare_signal_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_compare_signal(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "compare_signal", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_compare_signal_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_max__doc__, +"max($self, /, other, context=None)\n" +"--\n" +"\n" +"Maximum of self and other.\n" +"\n" +"If one operand is a quiet NaN and the other is numeric, the numeric\n" +"operand is returned."); + +#define _DECIMAL_DECIMAL_MAX_METHODDEF \ + {"max", _PyCFunction_CAST(_decimal_Decimal_max), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_max__doc__}, + +static PyObject * +_decimal_Decimal_max_impl(PyObject *self, PyTypeObject *cls, PyObject *other, + PyObject *context); + +static PyObject * +_decimal_Decimal_max(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "max", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_max_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_max_mag__doc__, +"max_mag($self, /, other, context=None)\n" +"--\n" +"\n" +"As the max() method, but compares the absolute values of the operands."); + +#define _DECIMAL_DECIMAL_MAX_MAG_METHODDEF \ + {"max_mag", _PyCFunction_CAST(_decimal_Decimal_max_mag), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_max_mag__doc__}, + +static PyObject * +_decimal_Decimal_max_mag_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_max_mag(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "max_mag", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_max_mag_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_min__doc__, +"min($self, /, other, context=None)\n" +"--\n" +"\n" +"Minimum of self and other.\n" +"\n" +"If one operand is a quiet NaN and the other is numeric, the numeric\n" +"operand is returned."); + +#define _DECIMAL_DECIMAL_MIN_METHODDEF \ + {"min", _PyCFunction_CAST(_decimal_Decimal_min), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_min__doc__}, + +static PyObject * +_decimal_Decimal_min_impl(PyObject *self, PyTypeObject *cls, PyObject *other, + PyObject *context); + +static PyObject * +_decimal_Decimal_min(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "min", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_min_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_min_mag__doc__, +"min_mag($self, /, other, context=None)\n" +"--\n" +"\n" +"As the min() method, but compares the absolute values of the operands."); + +#define _DECIMAL_DECIMAL_MIN_MAG_METHODDEF \ + {"min_mag", _PyCFunction_CAST(_decimal_Decimal_min_mag), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_min_mag__doc__}, + +static PyObject * +_decimal_Decimal_min_mag_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_min_mag(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "min_mag", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_min_mag_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_next_toward__doc__, +"next_toward($self, /, other, context=None)\n" +"--\n" +"\n" +"Returns the number closest to self, in the direction towards other.\n" +"\n" +"If the two operands are unequal, return the number closest to the\n" +"first operand in the direction of the second operand. If both\n" +"operands are numerically equal, return a copy of the first operand\n" +"with the sign set to be the same as the sign of the second operand."); + +#define _DECIMAL_DECIMAL_NEXT_TOWARD_METHODDEF \ + {"next_toward", _PyCFunction_CAST(_decimal_Decimal_next_toward), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_next_toward__doc__}, + +static PyObject * +_decimal_Decimal_next_toward_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_next_toward(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "next_toward", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_next_toward_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_remainder_near__doc__, +"remainder_near($self, /, other, context=None)\n" +"--\n" +"\n" +"Return the remainder from dividing self by other.\n" +"\n" +"This differs from self % other in that the sign of the remainder is\n" +"chosen so as to minimize its absolute value. More precisely, the\n" +"return value is self - n * other where n is the integer nearest to\n" +"the exact value of self / other, and if two integers are equally\n" +"near then the even one is chosen.\n" +"\n" +"If the result is zero then its sign will be the sign of self."); + +#define _DECIMAL_DECIMAL_REMAINDER_NEAR_METHODDEF \ + {"remainder_near", _PyCFunction_CAST(_decimal_Decimal_remainder_near), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_remainder_near__doc__}, + +static PyObject * +_decimal_Decimal_remainder_near_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_remainder_near(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "remainder_near", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_remainder_near_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_fma__doc__, +"fma($self, /, other, third, context=None)\n" +"--\n" +"\n" +"Fused multiply-add.\n" +"\n" +"Return self*other+third with no rounding of the intermediate product\n" +"self*other.\n" +"\n" +" >>> Decimal(2).fma(3, 5)\n" +" Decimal(\'11\')"); + +#define _DECIMAL_DECIMAL_FMA_METHODDEF \ + {"fma", _PyCFunction_CAST(_decimal_Decimal_fma), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_fma__doc__}, + +static PyObject * +_decimal_Decimal_fma_impl(PyObject *self, PyTypeObject *cls, PyObject *other, + PyObject *third, PyObject *context); + +static PyObject * +_decimal_Decimal_fma(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(third), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "third", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "fma", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *other; + PyObject *third; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + third = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[2]; +skip_optional_pos: + return_value = _decimal_Decimal_fma_impl(self, cls, other, third, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_is_canonical__doc__, +"is_canonical($self, /)\n" +"--\n" +"\n" +"Return True if the argument is canonical and False otherwise.\n" +"\n" +"Currently, a Decimal instance is always canonical, so this operation\n" +"always returns True."); + +#define _DECIMAL_DECIMAL_IS_CANONICAL_METHODDEF \ + {"is_canonical", (PyCFunction)_decimal_Decimal_is_canonical, METH_NOARGS, _decimal_Decimal_is_canonical__doc__}, + +static PyObject * +_decimal_Decimal_is_canonical_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_is_canonical(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_is_canonical_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_is_finite__doc__, +"is_finite($self, /)\n" +"--\n" +"\n" +"Return True if the argument is a finite number, and False otherwise."); + +#define _DECIMAL_DECIMAL_IS_FINITE_METHODDEF \ + {"is_finite", (PyCFunction)_decimal_Decimal_is_finite, METH_NOARGS, _decimal_Decimal_is_finite__doc__}, + +static PyObject * +_decimal_Decimal_is_finite_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_is_finite(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_is_finite_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_is_infinite__doc__, +"is_infinite($self, /)\n" +"--\n" +"\n" +"Return True if the argument is infinite, and False otherwise."); + +#define _DECIMAL_DECIMAL_IS_INFINITE_METHODDEF \ + {"is_infinite", (PyCFunction)_decimal_Decimal_is_infinite, METH_NOARGS, _decimal_Decimal_is_infinite__doc__}, + +static PyObject * +_decimal_Decimal_is_infinite_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_is_infinite(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_is_infinite_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_is_nan__doc__, +"is_nan($self, /)\n" +"--\n" +"\n" +"Return True if the argument is a (quiet or signaling) NaN, else False."); + +#define _DECIMAL_DECIMAL_IS_NAN_METHODDEF \ + {"is_nan", (PyCFunction)_decimal_Decimal_is_nan, METH_NOARGS, _decimal_Decimal_is_nan__doc__}, + +static PyObject * +_decimal_Decimal_is_nan_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_is_nan(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_is_nan_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_is_qnan__doc__, +"is_qnan($self, /)\n" +"--\n" +"\n" +"Return True if the argument is a quiet NaN, and False otherwise."); + +#define _DECIMAL_DECIMAL_IS_QNAN_METHODDEF \ + {"is_qnan", (PyCFunction)_decimal_Decimal_is_qnan, METH_NOARGS, _decimal_Decimal_is_qnan__doc__}, + +static PyObject * +_decimal_Decimal_is_qnan_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_is_qnan(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_is_qnan_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_is_snan__doc__, +"is_snan($self, /)\n" +"--\n" +"\n" +"Return True if the argument is a signaling NaN and False otherwise."); + +#define _DECIMAL_DECIMAL_IS_SNAN_METHODDEF \ + {"is_snan", (PyCFunction)_decimal_Decimal_is_snan, METH_NOARGS, _decimal_Decimal_is_snan__doc__}, + +static PyObject * +_decimal_Decimal_is_snan_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_is_snan(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_is_snan_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_is_signed__doc__, +"is_signed($self, /)\n" +"--\n" +"\n" +"Return True if the argument has a negative sign and False otherwise.\n" +"\n" +"Note that both zeros and NaNs can carry signs."); + +#define _DECIMAL_DECIMAL_IS_SIGNED_METHODDEF \ + {"is_signed", (PyCFunction)_decimal_Decimal_is_signed, METH_NOARGS, _decimal_Decimal_is_signed__doc__}, + +static PyObject * +_decimal_Decimal_is_signed_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_is_signed(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_is_signed_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_is_zero__doc__, +"is_zero($self, /)\n" +"--\n" +"\n" +"Return True if the argument is a zero and False otherwise."); + +#define _DECIMAL_DECIMAL_IS_ZERO_METHODDEF \ + {"is_zero", (PyCFunction)_decimal_Decimal_is_zero, METH_NOARGS, _decimal_Decimal_is_zero__doc__}, + +static PyObject * +_decimal_Decimal_is_zero_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_is_zero(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_is_zero_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_is_normal__doc__, +"is_normal($self, /, context=None)\n" +"--\n" +"\n" +"Return True if the argument is a normal number and False otherwise.\n" +"\n" +"Normal number is a finite nonzero number, which is not subnormal."); + +#define _DECIMAL_DECIMAL_IS_NORMAL_METHODDEF \ + {"is_normal", _PyCFunction_CAST(_decimal_Decimal_is_normal), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_is_normal__doc__}, + +static PyObject * +_decimal_Decimal_is_normal_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_is_normal(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_normal", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_is_normal_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_is_subnormal__doc__, +"is_subnormal($self, /, context=None)\n" +"--\n" +"\n" +"Return True if the argument is subnormal, and False otherwise.\n" +"\n" +"A number is subnormal if it is non-zero, finite, and has an adjusted\n" +"exponent less than Emin."); + +#define _DECIMAL_DECIMAL_IS_SUBNORMAL_METHODDEF \ + {"is_subnormal", _PyCFunction_CAST(_decimal_Decimal_is_subnormal), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_is_subnormal__doc__}, + +static PyObject * +_decimal_Decimal_is_subnormal_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_is_subnormal(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_subnormal", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_is_subnormal_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_adjusted__doc__, +"adjusted($self, /)\n" +"--\n" +"\n" +"Return the adjusted exponent (exp + digits - 1) of the number."); + +#define _DECIMAL_DECIMAL_ADJUSTED_METHODDEF \ + {"adjusted", (PyCFunction)_decimal_Decimal_adjusted, METH_NOARGS, _decimal_Decimal_adjusted__doc__}, + +static PyObject * +_decimal_Decimal_adjusted_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_adjusted(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_adjusted_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_canonical__doc__, +"canonical($self, /)\n" +"--\n" +"\n" +"Return the canonical encoding of the argument.\n" +"\n" +"Currently, the encoding of a Decimal instance is always canonical,\n" +"so this operation returns its argument unchanged."); + +#define _DECIMAL_DECIMAL_CANONICAL_METHODDEF \ + {"canonical", (PyCFunction)_decimal_Decimal_canonical, METH_NOARGS, _decimal_Decimal_canonical__doc__}, + +static PyObject * +_decimal_Decimal_canonical_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_canonical(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_canonical_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_conjugate__doc__, +"conjugate($self, /)\n" +"--\n" +"\n" +"Return self."); + +#define _DECIMAL_DECIMAL_CONJUGATE_METHODDEF \ + {"conjugate", (PyCFunction)_decimal_Decimal_conjugate, METH_NOARGS, _decimal_Decimal_conjugate__doc__}, + +static PyObject * +_decimal_Decimal_conjugate_impl(PyObject *self); + +static PyObject * +_decimal_Decimal_conjugate(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal_conjugate_impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal_radix__doc__, +"radix($self, /)\n" +"--\n" +"\n" +"Return Decimal(10).\n" +"\n" +"This is the radix (base) in which the Decimal class does all its\n" +"arithmetic. Included for compatibility with the specification."); + +#define _DECIMAL_DECIMAL_RADIX_METHODDEF \ + {"radix", _PyCFunction_CAST(_decimal_Decimal_radix), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_radix__doc__}, + +static PyObject * +_decimal_Decimal_radix_impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Decimal_radix(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "radix() takes no arguments"); + return NULL; + } + return _decimal_Decimal_radix_impl(self, cls); +} + +PyDoc_STRVAR(_decimal_Decimal_copy_abs__doc__, +"copy_abs($self, /)\n" +"--\n" +"\n" +"Return the absolute value of the argument.\n" +"\n" +"This operation is unaffected by context and is quiet: no flags are\n" +"changed and no rounding is performed."); + +#define _DECIMAL_DECIMAL_COPY_ABS_METHODDEF \ + {"copy_abs", _PyCFunction_CAST(_decimal_Decimal_copy_abs), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_copy_abs__doc__}, + +static PyObject * +_decimal_Decimal_copy_abs_impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Decimal_copy_abs(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "copy_abs() takes no arguments"); + return NULL; + } + return _decimal_Decimal_copy_abs_impl(self, cls); +} + +PyDoc_STRVAR(_decimal_Decimal_copy_negate__doc__, +"copy_negate($self, /)\n" +"--\n" +"\n" +"Return the negation of the argument.\n" +"\n" +"This operation is unaffected by context and is quiet: no flags are\n" +"changed and no rounding is performed."); + +#define _DECIMAL_DECIMAL_COPY_NEGATE_METHODDEF \ + {"copy_negate", _PyCFunction_CAST(_decimal_Decimal_copy_negate), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_copy_negate__doc__}, + +static PyObject * +_decimal_Decimal_copy_negate_impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Decimal_copy_negate(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "copy_negate() takes no arguments"); + return NULL; + } + return _decimal_Decimal_copy_negate_impl(self, cls); +} + +PyDoc_STRVAR(_decimal_Decimal_logical_invert__doc__, +"logical_invert($self, /, context=None)\n" +"--\n" +"\n" +"Invert all its digits.\n" +"\n" +"The self must be logical number."); + +#define _DECIMAL_DECIMAL_LOGICAL_INVERT_METHODDEF \ + {"logical_invert", _PyCFunction_CAST(_decimal_Decimal_logical_invert), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_logical_invert__doc__}, + +static PyObject * +_decimal_Decimal_logical_invert_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_logical_invert(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "logical_invert", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_logical_invert_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_logb__doc__, +"logb($self, /, context=None)\n" +"--\n" +"\n" +"Return the adjusted exponent of the operand as a Decimal instance.\n" +"\n" +"If the operand is a zero, then Decimal(\'-Infinity\') is returned and\n" +"the DivisionByZero condition is raised. If the operand is an\n" +"infinity then Decimal(\'Infinity\') is returned."); + +#define _DECIMAL_DECIMAL_LOGB_METHODDEF \ + {"logb", _PyCFunction_CAST(_decimal_Decimal_logb), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_logb__doc__}, + +static PyObject * +_decimal_Decimal_logb_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_logb(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "logb", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_logb_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_number_class__doc__, +"number_class($self, /, context=None)\n" +"--\n" +"\n" +"Return a string describing the class of the operand.\n" +"\n" +"The returned value is one of the following ten strings:\n" +"\n" +" * \'-Infinity\', indicating that the operand is negative infinity.\n" +" * \'-Normal\', indicating that the operand is a negative normal\n" +" number.\n" +" * \'-Subnormal\', indicating that the operand is negative and\n" +" subnormal.\n" +" * \'-Zero\', indicating that the operand is a negative zero.\n" +" * \'+Zero\', indicating that the operand is a positive zero.\n" +" * \'+Subnormal\', indicating that the operand is positive and\n" +" subnormal.\n" +" * \'+Normal\', indicating that the operand is a positive normal\n" +" number.\n" +" * \'+Infinity\', indicating that the operand is positive infinity.\n" +" * \'NaN\', indicating that the operand is a quiet NaN (Not a\n" +" Number).\n" +" * \'sNaN\', indicating that the operand is a signaling NaN."); + +#define _DECIMAL_DECIMAL_NUMBER_CLASS_METHODDEF \ + {"number_class", _PyCFunction_CAST(_decimal_Decimal_number_class), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_number_class__doc__}, + +static PyObject * +_decimal_Decimal_number_class_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_number_class(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "number_class", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_number_class_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_to_eng_string__doc__, +"to_eng_string($self, /, context=None)\n" +"--\n" +"\n" +"Convert to an engineering-type string.\n" +"\n" +"Engineering notation has an exponent which is a multiple of 3, so\n" +"there are up to 3 digits left of the decimal place. For example,\n" +"Decimal(\'123E+1\') is converted to Decimal(\'1.23E+3\').\n" +"\n" +"The value of context.capitals determines whether the exponent sign\n" +"is lower or upper case. Otherwise, the context does not affect the\n" +"operation."); + +#define _DECIMAL_DECIMAL_TO_ENG_STRING_METHODDEF \ + {"to_eng_string", _PyCFunction_CAST(_decimal_Decimal_to_eng_string), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_to_eng_string__doc__}, + +static PyObject * +_decimal_Decimal_to_eng_string_impl(PyObject *self, PyTypeObject *cls, + PyObject *context); + +static PyObject * +_decimal_Decimal_to_eng_string(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "to_eng_string", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + context = args[0]; +skip_optional_pos: + return_value = _decimal_Decimal_to_eng_string_impl(self, cls, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_compare_total__doc__, +"compare_total($self, /, other, context=None)\n" +"--\n" +"\n" +"Compare two operands using their abstract representation.\n" +"\n" +"Similar to the compare() method, but the result gives a total\n" +"ordering on Decimal instances. Two Decimal instances with the same\n" +"numeric value but different representations compare unequal in this\n" +"ordering:\n" +"\n" +" >>> Decimal(\'12.0\').compare_total(Decimal(\'12\'))\n" +" Decimal(\'-1\')\n" +"\n" +"Quiet and signaling NaNs are also included in the total ordering.\n" +"The result of this function is Decimal(\'0\') if both operands have\n" +"the same representation, Decimal(\'-1\') if the first operand is lower\n" +"in the total order than the second, and Decimal(\'1\') if the first\n" +"operand is higher in the total order than the second operand. See\n" +"the specification for details of the total order.\n" +"\n" +"This operation is unaffected by context and is quiet: no flags are\n" +"changed and no rounding is performed. As an exception, the C\n" +"version may raise InvalidOperation if the second operand cannot be\n" +"converted exactly."); + +#define _DECIMAL_DECIMAL_COMPARE_TOTAL_METHODDEF \ + {"compare_total", _PyCFunction_CAST(_decimal_Decimal_compare_total), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_compare_total__doc__}, + +static PyObject * +_decimal_Decimal_compare_total_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_compare_total(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "compare_total", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_compare_total_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_compare_total_mag__doc__, +"compare_total_mag($self, /, other, context=None)\n" +"--\n" +"\n" +"As compare_total(), but ignores the sign of each operand.\n" +"\n" +"x.compare_total_mag(y) is equivalent to\n" +"x.copy_abs().compare_total(y.copy_abs()).\n" +"\n" +"This operation is unaffected by context and is quiet: no flags are\n" +"changed and no rounding is performed. As an exception, the C version\n" +"may raise InvalidOperation if the second operand cannot be converted\n" +"exactly."); + +#define _DECIMAL_DECIMAL_COMPARE_TOTAL_MAG_METHODDEF \ + {"compare_total_mag", _PyCFunction_CAST(_decimal_Decimal_compare_total_mag), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_compare_total_mag__doc__}, + +static PyObject * +_decimal_Decimal_compare_total_mag_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_compare_total_mag(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "compare_total_mag", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_compare_total_mag_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_copy_sign__doc__, +"copy_sign($self, /, other, context=None)\n" +"--\n" +"\n" +"Return a copy of *self* with the sign of *other*.\n" +"\n" +"For example:\n" +"\n" +" >>> Decimal(\'2.3\').copy_sign(Decimal(\'-1.5\'))\n" +" Decimal(\'-2.3\')\n" +"\n" +"This operation is unaffected by context and is quiet: no flags are\n" +"changed and no rounding is performed. As an exception, the C version\n" +"may raise InvalidOperation if the second operand cannot be converted\n" +"exactly."); + +#define _DECIMAL_DECIMAL_COPY_SIGN_METHODDEF \ + {"copy_sign", _PyCFunction_CAST(_decimal_Decimal_copy_sign), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_copy_sign__doc__}, + +static PyObject * +_decimal_Decimal_copy_sign_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_copy_sign(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "copy_sign", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_copy_sign_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_same_quantum__doc__, +"same_quantum($self, /, other, context=None)\n" +"--\n" +"\n" +"Test whether self and other have the same exponent or both are NaN.\n" +"\n" +"This operation is unaffected by context and is quiet: no flags are\n" +"changed and no rounding is performed. As an exception, the C version\n" +"may raise InvalidOperation if the second operand cannot be converted\n" +"exactly."); + +#define _DECIMAL_DECIMAL_SAME_QUANTUM_METHODDEF \ + {"same_quantum", _PyCFunction_CAST(_decimal_Decimal_same_quantum), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_same_quantum__doc__}, + +static PyObject * +_decimal_Decimal_same_quantum_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_same_quantum(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "same_quantum", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_same_quantum_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_logical_and__doc__, +"logical_and($self, /, other, context=None)\n" +"--\n" +"\n" +"Applies an \'and\' operation between self and other\'s digits.\n" +"\n" +"Both self and other must be logical numbers."); + +#define _DECIMAL_DECIMAL_LOGICAL_AND_METHODDEF \ + {"logical_and", _PyCFunction_CAST(_decimal_Decimal_logical_and), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_logical_and__doc__}, + +static PyObject * +_decimal_Decimal_logical_and_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_logical_and(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "logical_and", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_logical_and_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_logical_or__doc__, +"logical_or($self, /, other, context=None)\n" +"--\n" +"\n" +"Applies an \'or\' operation between self and other\'s digits.\n" +"\n" +"Both self and other must be logical numbers."); + +#define _DECIMAL_DECIMAL_LOGICAL_OR_METHODDEF \ + {"logical_or", _PyCFunction_CAST(_decimal_Decimal_logical_or), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_logical_or__doc__}, + +static PyObject * +_decimal_Decimal_logical_or_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_logical_or(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "logical_or", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_logical_or_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_logical_xor__doc__, +"logical_xor($self, /, other, context=None)\n" +"--\n" +"\n" +"Applies an \'xor\' operation between self and other\'s digits.\n" +"\n" +"Both self and other must be logical numbers."); + +#define _DECIMAL_DECIMAL_LOGICAL_XOR_METHODDEF \ + {"logical_xor", _PyCFunction_CAST(_decimal_Decimal_logical_xor), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_logical_xor__doc__}, + +static PyObject * +_decimal_Decimal_logical_xor_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_logical_xor(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "logical_xor", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_logical_xor_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_rotate__doc__, +"rotate($self, /, other, context=None)\n" +"--\n" +"\n" +"Returns a rotated copy of self\'s digits, value-of-other times.\n" +"\n" +"The second operand must be an integer in the range -precision\n" +"through precision. The absolute value of the second operand gives\n" +"the number of places to rotate. If the second operand is positive\n" +"then rotation is to the left; otherwise rotation is to the right.\n" +"The coefficient of the first operand is padded on the left with\n" +"zeros to length precision if necessary. The sign and exponent of\n" +"the first operand are unchanged."); + +#define _DECIMAL_DECIMAL_ROTATE_METHODDEF \ + {"rotate", _PyCFunction_CAST(_decimal_Decimal_rotate), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_rotate__doc__}, + +static PyObject * +_decimal_Decimal_rotate_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_rotate(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "rotate", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_rotate_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_scaleb__doc__, +"scaleb($self, /, other, context=None)\n" +"--\n" +"\n" +"Return the first operand with the exponent adjusted the second.\n" +"\n" +"Equivalently, return the first operand multiplied by 10**other. The\n" +"second operand must be an integer."); + +#define _DECIMAL_DECIMAL_SCALEB_METHODDEF \ + {"scaleb", _PyCFunction_CAST(_decimal_Decimal_scaleb), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_scaleb__doc__}, + +static PyObject * +_decimal_Decimal_scaleb_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_scaleb(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "scaleb", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_scaleb_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_shift__doc__, +"shift($self, /, other, context=None)\n" +"--\n" +"\n" +"Returns a shifted copy of self\'s digits, value-of-other times.\n" +"\n" +"The second operand must be an integer in the range -precision\n" +"through precision. The absolute value of the second operand gives\n" +"the number of places to shift. If the second operand is positive,\n" +"then the shift is to the left; otherwise the shift is to the right.\n" +"Digits shifted into the coefficient are zeros. The sign and\n" +"exponent of the first operand are unchanged."); + +#define _DECIMAL_DECIMAL_SHIFT_METHODDEF \ + {"shift", _PyCFunction_CAST(_decimal_Decimal_shift), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_shift__doc__}, + +static PyObject * +_decimal_Decimal_shift_impl(PyObject *self, PyTypeObject *cls, + PyObject *other, PyObject *context); + +static PyObject * +_decimal_Decimal_shift(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(other), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"other", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "shift", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *other; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + other = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + context = args[1]; +skip_optional_pos: + return_value = _decimal_Decimal_shift_impl(self, cls, other, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal_quantize__doc__, +"quantize($self, /, exp, rounding=None, context=None)\n" +"--\n" +"\n" +"Quantize *self* so its exponent is the same as that of *exp*.\n" +"\n" +"Return a value equal to *self* after rounding, with the exponent\n" +"of *exp*.\n" +"\n" +" >>> Decimal(\'1.41421356\').quantize(Decimal(\'1.000\'))\n" +" Decimal(\'1.414\')\n" +"\n" +"Unlike other operations, if the length of the coefficient after the\n" +"quantize operation would be greater than precision, then an\n" +"InvalidOperation is signaled. This guarantees that, unless there\n" +"is an error condition, the quantized exponent is always equal to\n" +"that of the right-hand operand.\n" +"\n" +"Also unlike other operations, quantize never signals Underflow, even\n" +"if the result is subnormal and inexact.\n" +"\n" +"If the exponent of the second operand is larger than that of the\n" +"first, then rounding may be necessary. In this case, the rounding\n" +"mode is determined by the rounding argument if given, else by the\n" +"given context argument; if neither argument is given, the rounding\n" +"mode of the current thread\'s context is used."); + +#define _DECIMAL_DECIMAL_QUANTIZE_METHODDEF \ + {"quantize", _PyCFunction_CAST(_decimal_Decimal_quantize), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal_quantize__doc__}, + +static PyObject * +_decimal_Decimal_quantize_impl(PyObject *self, PyTypeObject *cls, + PyObject *w, PyObject *rounding, + PyObject *context); + +static PyObject * +_decimal_Decimal_quantize(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(exp), &_Py_ID(rounding), &_Py_ID(context), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"exp", "rounding", "context", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "quantize", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *w; + PyObject *rounding = Py_None; + PyObject *context = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + w = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1]) { + rounding = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + context = args[2]; +skip_optional_pos: + return_value = _decimal_Decimal_quantize_impl(self, cls, w, rounding, context); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Decimal___ceil____doc__, +"__ceil__($self, /)\n" +"--\n" +"\n" +"Return the ceiling as an Integral."); + +#define _DECIMAL_DECIMAL___CEIL___METHODDEF \ + {"__ceil__", _PyCFunction_CAST(_decimal_Decimal___ceil__), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal___ceil____doc__}, + +static PyObject * +_decimal_Decimal___ceil___impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Decimal___ceil__(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "__ceil__() takes no arguments"); + return NULL; + } + return _decimal_Decimal___ceil___impl(self, cls); +} + +PyDoc_STRVAR(_decimal_Decimal___complex____doc__, +"__complex__($self, /)\n" +"--\n" +"\n" +"Convert this value to exact type complex."); + +#define _DECIMAL_DECIMAL___COMPLEX___METHODDEF \ + {"__complex__", (PyCFunction)_decimal_Decimal___complex__, METH_NOARGS, _decimal_Decimal___complex____doc__}, + +static PyObject * +_decimal_Decimal___complex___impl(PyObject *self); + +static PyObject * +_decimal_Decimal___complex__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal___complex___impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal___copy____doc__, +"__copy__($self, /)\n" +"--\n" +"\n"); + +#define _DECIMAL_DECIMAL___COPY___METHODDEF \ + {"__copy__", (PyCFunction)_decimal_Decimal___copy__, METH_NOARGS, _decimal_Decimal___copy____doc__}, + +static PyObject * +_decimal_Decimal___copy___impl(PyObject *self); + +static PyObject * +_decimal_Decimal___copy__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal___copy___impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal___deepcopy____doc__, +"__deepcopy__($self, memo, /)\n" +"--\n" +"\n"); + +#define _DECIMAL_DECIMAL___DEEPCOPY___METHODDEF \ + {"__deepcopy__", (PyCFunction)_decimal_Decimal___deepcopy__, METH_O, _decimal_Decimal___deepcopy____doc__}, + +PyDoc_STRVAR(_decimal_Decimal___floor____doc__, +"__floor__($self, /)\n" +"--\n" +"\n" +"Return the floor as an Integral."); + +#define _DECIMAL_DECIMAL___FLOOR___METHODDEF \ + {"__floor__", _PyCFunction_CAST(_decimal_Decimal___floor__), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal___floor____doc__}, + +static PyObject * +_decimal_Decimal___floor___impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Decimal___floor__(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "__floor__() takes no arguments"); + return NULL; + } + return _decimal_Decimal___floor___impl(self, cls); +} + +PyDoc_STRVAR(_decimal_Decimal___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n" +"Return state information for pickling."); + +#define _DECIMAL_DECIMAL___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)_decimal_Decimal___reduce__, METH_NOARGS, _decimal_Decimal___reduce____doc__}, + +static PyObject * +_decimal_Decimal___reduce___impl(PyObject *self); + +static PyObject * +_decimal_Decimal___reduce__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal___reduce___impl(self); +} + +PyDoc_STRVAR(_decimal_Decimal___sizeof____doc__, +"__sizeof__($self, /)\n" +"--\n" +"\n" +"Returns size in memory, in bytes"); + +#define _DECIMAL_DECIMAL___SIZEOF___METHODDEF \ + {"__sizeof__", (PyCFunction)_decimal_Decimal___sizeof__, METH_NOARGS, _decimal_Decimal___sizeof____doc__}, + +static PyObject * +_decimal_Decimal___sizeof___impl(PyObject *v); + +static PyObject * +_decimal_Decimal___sizeof__(PyObject *v, PyObject *Py_UNUSED(ignored)) +{ + return _decimal_Decimal___sizeof___impl(v); +} + +PyDoc_STRVAR(_decimal_Decimal___trunc____doc__, +"__trunc__($self, /)\n" +"--\n" +"\n" +"Return the Integral closest to x between 0 and x."); + +#define _DECIMAL_DECIMAL___TRUNC___METHODDEF \ + {"__trunc__", _PyCFunction_CAST(_decimal_Decimal___trunc__), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Decimal___trunc____doc__}, + +static PyObject * +_decimal_Decimal___trunc___impl(PyObject *self, PyTypeObject *cls); + +static PyObject * +_decimal_Decimal___trunc__(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "__trunc__() takes no arguments"); + return NULL; + } + return _decimal_Decimal___trunc___impl(self, cls); +} + +PyDoc_STRVAR(_decimal_Context_abs__doc__, +"abs($self, x, /)\n" +"--\n" +"\n" +"Return the absolute value of x."); + +#define _DECIMAL_CONTEXT_ABS_METHODDEF \ + {"abs", _PyCFunction_CAST(_decimal_Context_abs), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_abs__doc__}, + +static PyObject * +_decimal_Context_abs_impl(PyObject *context, PyTypeObject *cls, PyObject *x); + +static PyObject * +_decimal_Context_abs(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "abs", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_abs_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_exp__doc__, +"exp($self, x, /)\n" +"--\n" +"\n" +"Return e ** x."); + +#define _DECIMAL_CONTEXT_EXP_METHODDEF \ + {"exp", _PyCFunction_CAST(_decimal_Context_exp), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_exp__doc__}, + +static PyObject * +_decimal_Context_exp_impl(PyObject *context, PyTypeObject *cls, PyObject *x); + +static PyObject * +_decimal_Context_exp(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "exp", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_exp_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_ln__doc__, +"ln($self, x, /)\n" +"--\n" +"\n" +"Return the natural (base e) logarithm of x."); + +#define _DECIMAL_CONTEXT_LN_METHODDEF \ + {"ln", _PyCFunction_CAST(_decimal_Context_ln), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_ln__doc__}, + +static PyObject * +_decimal_Context_ln_impl(PyObject *context, PyTypeObject *cls, PyObject *x); + +static PyObject * +_decimal_Context_ln(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "ln", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_ln_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_log10__doc__, +"log10($self, x, /)\n" +"--\n" +"\n" +"Return the base 10 logarithm of x."); + +#define _DECIMAL_CONTEXT_LOG10_METHODDEF \ + {"log10", _PyCFunction_CAST(_decimal_Context_log10), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_log10__doc__}, + +static PyObject * +_decimal_Context_log10_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_log10(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "log10", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_log10_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_minus__doc__, +"minus($self, x, /)\n" +"--\n" +"\n" +"Minus corresponds to unary prefix minus in Python.\n" +"\n" +"This operation applies the context to the result."); + +#define _DECIMAL_CONTEXT_MINUS_METHODDEF \ + {"minus", _PyCFunction_CAST(_decimal_Context_minus), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_minus__doc__}, + +static PyObject * +_decimal_Context_minus_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_minus(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "minus", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_minus_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_next_minus__doc__, +"next_minus($self, x, /)\n" +"--\n" +"\n" +"Return the largest representable number smaller than x."); + +#define _DECIMAL_CONTEXT_NEXT_MINUS_METHODDEF \ + {"next_minus", _PyCFunction_CAST(_decimal_Context_next_minus), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_next_minus__doc__}, + +static PyObject * +_decimal_Context_next_minus_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_next_minus(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "next_minus", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_next_minus_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_next_plus__doc__, +"next_plus($self, x, /)\n" +"--\n" +"\n" +"Return the smallest representable number larger than x."); + +#define _DECIMAL_CONTEXT_NEXT_PLUS_METHODDEF \ + {"next_plus", _PyCFunction_CAST(_decimal_Context_next_plus), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_next_plus__doc__}, + +static PyObject * +_decimal_Context_next_plus_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_next_plus(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "next_plus", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_next_plus_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_normalize__doc__, +"normalize($self, x, /)\n" +"--\n" +"\n" +"Reduce x to its simplest form. Alias for reduce(x)."); + +#define _DECIMAL_CONTEXT_NORMALIZE_METHODDEF \ + {"normalize", _PyCFunction_CAST(_decimal_Context_normalize), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_normalize__doc__}, + +static PyObject * +_decimal_Context_normalize_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_normalize(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "normalize", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_normalize_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_plus__doc__, +"plus($self, x, /)\n" +"--\n" +"\n" +"Plus corresponds to the unary prefix plus operator in Python.\n" +"\n" +"This operation applies the context to the result."); + +#define _DECIMAL_CONTEXT_PLUS_METHODDEF \ + {"plus", _PyCFunction_CAST(_decimal_Context_plus), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_plus__doc__}, + +static PyObject * +_decimal_Context_plus_impl(PyObject *context, PyTypeObject *cls, PyObject *x); + +static PyObject * +_decimal_Context_plus(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "plus", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_plus_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_to_integral_value__doc__, +"to_integral_value($self, x, /)\n" +"--\n" +"\n" +"Round to an integer."); + +#define _DECIMAL_CONTEXT_TO_INTEGRAL_VALUE_METHODDEF \ + {"to_integral_value", _PyCFunction_CAST(_decimal_Context_to_integral_value), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_to_integral_value__doc__}, + +static PyObject * +_decimal_Context_to_integral_value_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_to_integral_value(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "to_integral_value", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_to_integral_value_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_to_integral_exact__doc__, +"to_integral_exact($self, x, /)\n" +"--\n" +"\n" +"Round to an integer. Signal if the result is rounded or inexact."); + +#define _DECIMAL_CONTEXT_TO_INTEGRAL_EXACT_METHODDEF \ + {"to_integral_exact", _PyCFunction_CAST(_decimal_Context_to_integral_exact), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_to_integral_exact__doc__}, + +static PyObject * +_decimal_Context_to_integral_exact_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_to_integral_exact(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "to_integral_exact", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_to_integral_exact_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_to_integral__doc__, +"to_integral($self, x, /)\n" +"--\n" +"\n" +"Identical to to_integral_value(x)."); + +#define _DECIMAL_CONTEXT_TO_INTEGRAL_METHODDEF \ + {"to_integral", _PyCFunction_CAST(_decimal_Context_to_integral), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_to_integral__doc__}, + +static PyObject * +_decimal_Context_to_integral_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_to_integral(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "to_integral", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_to_integral_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_sqrt__doc__, +"sqrt($self, x, /)\n" +"--\n" +"\n" +"Square root of a non-negative number to context precision."); + +#define _DECIMAL_CONTEXT_SQRT_METHODDEF \ + {"sqrt", _PyCFunction_CAST(_decimal_Context_sqrt), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_sqrt__doc__}, + +static PyObject * +_decimal_Context_sqrt_impl(PyObject *context, PyTypeObject *cls, PyObject *x); + +static PyObject * +_decimal_Context_sqrt(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sqrt", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_sqrt_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_add__doc__, +"add($self, x, y, /)\n" +"--\n" +"\n" +"Return the sum of x and y."); + +#define _DECIMAL_CONTEXT_ADD_METHODDEF \ + {"add", _PyCFunction_CAST(_decimal_Context_add), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_add__doc__}, + +static PyObject * +_decimal_Context_add_impl(PyObject *context, PyTypeObject *cls, PyObject *x, + PyObject *y); + +static PyObject * +_decimal_Context_add(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "add", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_add_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_compare__doc__, +"compare($self, x, y, /)\n" +"--\n" +"\n" +"Compare x and y numerically."); + +#define _DECIMAL_CONTEXT_COMPARE_METHODDEF \ + {"compare", _PyCFunction_CAST(_decimal_Context_compare), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_compare__doc__}, + +static PyObject * +_decimal_Context_compare_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_compare(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "compare", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_compare_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_compare_signal__doc__, +"compare_signal($self, x, y, /)\n" +"--\n" +"\n" +"Compare x and y numerically. All NaNs signal."); + +#define _DECIMAL_CONTEXT_COMPARE_SIGNAL_METHODDEF \ + {"compare_signal", _PyCFunction_CAST(_decimal_Context_compare_signal), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_compare_signal__doc__}, + +static PyObject * +_decimal_Context_compare_signal_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_compare_signal(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "compare_signal", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_compare_signal_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_divide__doc__, +"divide($self, x, y, /)\n" +"--\n" +"\n" +"Return x divided by y."); + +#define _DECIMAL_CONTEXT_DIVIDE_METHODDEF \ + {"divide", _PyCFunction_CAST(_decimal_Context_divide), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_divide__doc__}, + +static PyObject * +_decimal_Context_divide_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_divide(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "divide", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_divide_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_divide_int__doc__, +"divide_int($self, x, y, /)\n" +"--\n" +"\n" +"Return x divided by y, truncated to an integer."); + +#define _DECIMAL_CONTEXT_DIVIDE_INT_METHODDEF \ + {"divide_int", _PyCFunction_CAST(_decimal_Context_divide_int), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_divide_int__doc__}, + +static PyObject * +_decimal_Context_divide_int_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_divide_int(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "divide_int", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_divide_int_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_max__doc__, +"max($self, x, y, /)\n" +"--\n" +"\n" +"Compare the values numerically and return the maximum."); + +#define _DECIMAL_CONTEXT_MAX_METHODDEF \ + {"max", _PyCFunction_CAST(_decimal_Context_max), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_max__doc__}, + +static PyObject * +_decimal_Context_max_impl(PyObject *context, PyTypeObject *cls, PyObject *x, + PyObject *y); + +static PyObject * +_decimal_Context_max(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "max", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_max_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_max_mag__doc__, +"max_mag($self, x, y, /)\n" +"--\n" +"\n" +"Compare the values numerically with their sign ignored."); + +#define _DECIMAL_CONTEXT_MAX_MAG_METHODDEF \ + {"max_mag", _PyCFunction_CAST(_decimal_Context_max_mag), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_max_mag__doc__}, + +static PyObject * +_decimal_Context_max_mag_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_max_mag(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "max_mag", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_max_mag_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_min__doc__, +"min($self, x, y, /)\n" +"--\n" +"\n" +"Compare the values numerically and return the minimum."); + +#define _DECIMAL_CONTEXT_MIN_METHODDEF \ + {"min", _PyCFunction_CAST(_decimal_Context_min), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_min__doc__}, + +static PyObject * +_decimal_Context_min_impl(PyObject *context, PyTypeObject *cls, PyObject *x, + PyObject *y); + +static PyObject * +_decimal_Context_min(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "min", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_min_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_min_mag__doc__, +"min_mag($self, x, y, /)\n" +"--\n" +"\n" +"Compare the values numerically with their sign ignored."); + +#define _DECIMAL_CONTEXT_MIN_MAG_METHODDEF \ + {"min_mag", _PyCFunction_CAST(_decimal_Context_min_mag), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_min_mag__doc__}, + +static PyObject * +_decimal_Context_min_mag_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_min_mag(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "min_mag", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_min_mag_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_multiply__doc__, +"multiply($self, x, y, /)\n" +"--\n" +"\n" +"Return the product of x and y."); + +#define _DECIMAL_CONTEXT_MULTIPLY_METHODDEF \ + {"multiply", _PyCFunction_CAST(_decimal_Context_multiply), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_multiply__doc__}, + +static PyObject * +_decimal_Context_multiply_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_multiply(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "multiply", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_multiply_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_next_toward__doc__, +"next_toward($self, x, y, /)\n" +"--\n" +"\n" +"Return the number closest to x, in the direction towards y."); + +#define _DECIMAL_CONTEXT_NEXT_TOWARD_METHODDEF \ + {"next_toward", _PyCFunction_CAST(_decimal_Context_next_toward), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_next_toward__doc__}, + +static PyObject * +_decimal_Context_next_toward_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_next_toward(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "next_toward", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_next_toward_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_quantize__doc__, +"quantize($self, x, y, /)\n" +"--\n" +"\n" +"Return a value equal to x (rounded), having the exponent of y."); + +#define _DECIMAL_CONTEXT_QUANTIZE_METHODDEF \ + {"quantize", _PyCFunction_CAST(_decimal_Context_quantize), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_quantize__doc__}, + +static PyObject * +_decimal_Context_quantize_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_quantize(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "quantize", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_quantize_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_remainder__doc__, +"remainder($self, x, y, /)\n" +"--\n" +"\n" +"Return the remainder from integer division.\n" +"\n" +"The sign of the result, if non-zero, is the same as that of the\n" +"original dividend."); + +#define _DECIMAL_CONTEXT_REMAINDER_METHODDEF \ + {"remainder", _PyCFunction_CAST(_decimal_Context_remainder), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_remainder__doc__}, + +static PyObject * +_decimal_Context_remainder_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_remainder(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "remainder", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_remainder_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_remainder_near__doc__, +"remainder_near($self, x, y, /)\n" +"--\n" +"\n" +"Return x - y * n.\n" +"\n" +"Here n is the integer nearest the exact value of x / y (if the\n" +"result is 0 then its sign will be the sign of x)."); + +#define _DECIMAL_CONTEXT_REMAINDER_NEAR_METHODDEF \ + {"remainder_near", _PyCFunction_CAST(_decimal_Context_remainder_near), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_remainder_near__doc__}, + +static PyObject * +_decimal_Context_remainder_near_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_remainder_near(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "remainder_near", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_remainder_near_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_subtract__doc__, +"subtract($self, x, y, /)\n" +"--\n" +"\n" +"Return the difference between x and y."); + +#define _DECIMAL_CONTEXT_SUBTRACT_METHODDEF \ + {"subtract", _PyCFunction_CAST(_decimal_Context_subtract), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_subtract__doc__}, + +static PyObject * +_decimal_Context_subtract_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_subtract(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "subtract", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_subtract_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_divmod__doc__, +"divmod($self, x, y, /)\n" +"--\n" +"\n" +"Return quotient and remainder of the division x / y."); + +#define _DECIMAL_CONTEXT_DIVMOD_METHODDEF \ + {"divmod", _PyCFunction_CAST(_decimal_Context_divmod), METH_FASTCALL, _decimal_Context_divmod__doc__}, + +static PyObject * +_decimal_Context_divmod_impl(PyObject *context, PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_divmod(PyObject *context, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *x; + PyObject *y; + + if (!_PyArg_CheckPositional("divmod", nargs, 2, 2)) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_divmod_impl(context, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_power__doc__, +"power($self, /, a, b, modulo=None)\n" +"--\n" +"\n" +"Compute a**b.\n" +"\n" +"If \'a\' is negative, then \'b\' must be integral. The result will be\n" +"inexact unless \'a\' is integral and the result is finite and can be\n" +"expressed exactly in \'precision\' digits. In the Python version the\n" +"result is always correctly rounded, in the C version the result is\n" +"almost always correctly rounded.\n" +"\n" +"If modulo is given, compute (a**b) % modulo. The following\n" +"restrictions hold:\n" +"\n" +" * all three arguments must be integral\n" +" * \'b\' must be nonnegative\n" +" * at least one of \'a\' or \'b\' must be nonzero\n" +" * modulo must be nonzero and less than 10**prec in absolute\n" +" value"); + +#define _DECIMAL_CONTEXT_POWER_METHODDEF \ + {"power", _PyCFunction_CAST(_decimal_Context_power), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_power__doc__}, + +static PyObject * +_decimal_Context_power_impl(PyObject *context, PyTypeObject *cls, + PyObject *base, PyObject *exp, PyObject *mod); + +static PyObject * +_decimal_Context_power(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { _Py_LATIN1_CHR('a'), _Py_LATIN1_CHR('b'), &_Py_ID(modulo), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", "modulo", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "power", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *base; + PyObject *exp; + PyObject *mod = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + base = args[0]; + exp = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + mod = args[2]; +skip_optional_pos: + return_value = _decimal_Context_power_impl(context, cls, base, exp, mod); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_fma__doc__, +"fma($self, x, y, z, /)\n" +"--\n" +"\n" +"Return x multiplied by y, plus z."); + +#define _DECIMAL_CONTEXT_FMA_METHODDEF \ + {"fma", _PyCFunction_CAST(_decimal_Context_fma), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_fma__doc__}, + +static PyObject * +_decimal_Context_fma_impl(PyObject *context, PyTypeObject *cls, PyObject *x, + PyObject *y, PyObject *z); + +static PyObject * +_decimal_Context_fma(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "fma", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + PyObject *x; + PyObject *y; + PyObject *z; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + z = args[2]; + return_value = _decimal_Context_fma_impl(context, cls, x, y, z); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_radix__doc__, +"radix($self, /)\n" +"--\n" +"\n" +"Return 10."); + +#define _DECIMAL_CONTEXT_RADIX_METHODDEF \ + {"radix", _PyCFunction_CAST(_decimal_Context_radix), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_radix__doc__}, + +static PyObject * +_decimal_Context_radix_impl(PyObject *context, PyTypeObject *cls); + +static PyObject * +_decimal_Context_radix(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "radix() takes no arguments"); + return NULL; + } + return _decimal_Context_radix_impl(context, cls); +} + +PyDoc_STRVAR(_decimal_Context_is_normal__doc__, +"is_normal($self, x, /)\n" +"--\n" +"\n" +"Return True if x is a normal number, False otherwise."); + +#define _DECIMAL_CONTEXT_IS_NORMAL_METHODDEF \ + {"is_normal", _PyCFunction_CAST(_decimal_Context_is_normal), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_is_normal__doc__}, + +static PyObject * +_decimal_Context_is_normal_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_is_normal(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_normal", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_is_normal_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_is_subnormal__doc__, +"is_subnormal($self, x, /)\n" +"--\n" +"\n" +"Return True if x is subnormal, False otherwise."); + +#define _DECIMAL_CONTEXT_IS_SUBNORMAL_METHODDEF \ + {"is_subnormal", _PyCFunction_CAST(_decimal_Context_is_subnormal), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_is_subnormal__doc__}, + +static PyObject * +_decimal_Context_is_subnormal_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_is_subnormal(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_subnormal", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_is_subnormal_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_is_finite__doc__, +"is_finite($self, x, /)\n" +"--\n" +"\n" +"Return True if x is finite, False otherwise."); + +#define _DECIMAL_CONTEXT_IS_FINITE_METHODDEF \ + {"is_finite", _PyCFunction_CAST(_decimal_Context_is_finite), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_is_finite__doc__}, + +static PyObject * +_decimal_Context_is_finite_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_is_finite(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_finite", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_is_finite_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_is_infinite__doc__, +"is_infinite($self, x, /)\n" +"--\n" +"\n" +"Return True if x is infinite, False otherwise."); + +#define _DECIMAL_CONTEXT_IS_INFINITE_METHODDEF \ + {"is_infinite", _PyCFunction_CAST(_decimal_Context_is_infinite), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_is_infinite__doc__}, + +static PyObject * +_decimal_Context_is_infinite_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_is_infinite(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_infinite", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_is_infinite_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_is_nan__doc__, +"is_nan($self, x, /)\n" +"--\n" +"\n" +"Return True if x is a qNaN or sNaN, False otherwise."); + +#define _DECIMAL_CONTEXT_IS_NAN_METHODDEF \ + {"is_nan", _PyCFunction_CAST(_decimal_Context_is_nan), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_is_nan__doc__}, + +static PyObject * +_decimal_Context_is_nan_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_is_nan(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_nan", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_is_nan_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_is_qnan__doc__, +"is_qnan($self, x, /)\n" +"--\n" +"\n" +"Return True if x is a quiet NaN, False otherwise."); + +#define _DECIMAL_CONTEXT_IS_QNAN_METHODDEF \ + {"is_qnan", _PyCFunction_CAST(_decimal_Context_is_qnan), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_is_qnan__doc__}, + +static PyObject * +_decimal_Context_is_qnan_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_is_qnan(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_qnan", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_is_qnan_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_is_snan__doc__, +"is_snan($self, x, /)\n" +"--\n" +"\n" +"Return True if x is a signaling NaN, False otherwise."); + +#define _DECIMAL_CONTEXT_IS_SNAN_METHODDEF \ + {"is_snan", _PyCFunction_CAST(_decimal_Context_is_snan), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_is_snan__doc__}, + +static PyObject * +_decimal_Context_is_snan_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_is_snan(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_snan", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_is_snan_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_is_signed__doc__, +"is_signed($self, x, /)\n" +"--\n" +"\n" +"Return True if x is negative, False otherwise."); + +#define _DECIMAL_CONTEXT_IS_SIGNED_METHODDEF \ + {"is_signed", _PyCFunction_CAST(_decimal_Context_is_signed), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_is_signed__doc__}, + +static PyObject * +_decimal_Context_is_signed_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_is_signed(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_signed", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_is_signed_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_is_zero__doc__, +"is_zero($self, x, /)\n" +"--\n" +"\n" +"Return True if x is a zero, False otherwise."); + +#define _DECIMAL_CONTEXT_IS_ZERO_METHODDEF \ + {"is_zero", _PyCFunction_CAST(_decimal_Context_is_zero), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_is_zero__doc__}, + +static PyObject * +_decimal_Context_is_zero_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_is_zero(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_zero", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_is_zero_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_is_canonical__doc__, +"is_canonical($self, x, /)\n" +"--\n" +"\n" +"Return True if x is canonical, False otherwise."); + +#define _DECIMAL_CONTEXT_IS_CANONICAL_METHODDEF \ + {"is_canonical", _PyCFunction_CAST(_decimal_Context_is_canonical), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_is_canonical__doc__}, + +static PyObject * +_decimal_Context_is_canonical_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_is_canonical(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_canonical", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_is_canonical_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context__apply__doc__, +"_apply($self, x, /)\n" +"--\n" +"\n" +"Apply self to Decimal x."); + +#define _DECIMAL_CONTEXT__APPLY_METHODDEF \ + {"_apply", _PyCFunction_CAST(_decimal_Context__apply), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context__apply__doc__}, + +static PyObject * +_decimal_Context__apply_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context__apply(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_apply", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context__apply_impl(context, cls, x); + +exit: + return return_value; +} + +#if defined(EXTRA_FUNCTIONALITY) + +PyDoc_STRVAR(_decimal_Context_apply__doc__, +"apply($self, x, /)\n" +"--\n" +"\n" +"Apply self to Decimal x."); + +#define _DECIMAL_CONTEXT_APPLY_METHODDEF \ + {"apply", _PyCFunction_CAST(_decimal_Context_apply), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_apply__doc__}, + +static PyObject * +_decimal_Context_apply_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_apply(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "apply", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_apply_impl(context, cls, x); + +exit: + return return_value; +} + +#endif /* defined(EXTRA_FUNCTIONALITY) */ + +PyDoc_STRVAR(_decimal_Context_canonical__doc__, +"canonical($self, x, /)\n" +"--\n" +"\n" +"Return a new instance of x."); + +#define _DECIMAL_CONTEXT_CANONICAL_METHODDEF \ + {"canonical", _PyCFunction_CAST(_decimal_Context_canonical), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_canonical__doc__}, + +static PyObject * +_decimal_Context_canonical_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_canonical(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "canonical", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_canonical_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_copy_abs__doc__, +"copy_abs($self, x, /)\n" +"--\n" +"\n" +"Return a copy of x with the sign set to 0."); + +#define _DECIMAL_CONTEXT_COPY_ABS_METHODDEF \ + {"copy_abs", _PyCFunction_CAST(_decimal_Context_copy_abs), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_copy_abs__doc__}, + +static PyObject * +_decimal_Context_copy_abs_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_copy_abs(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "copy_abs", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_copy_abs_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_copy_decimal__doc__, +"copy_decimal($self, x, /)\n" +"--\n" +"\n" +"Return a copy of Decimal x."); + +#define _DECIMAL_CONTEXT_COPY_DECIMAL_METHODDEF \ + {"copy_decimal", _PyCFunction_CAST(_decimal_Context_copy_decimal), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_copy_decimal__doc__}, + +static PyObject * +_decimal_Context_copy_decimal_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_copy_decimal(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "copy_decimal", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_copy_decimal_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_copy_negate__doc__, +"copy_negate($self, x, /)\n" +"--\n" +"\n" +"Return a copy of x with the sign inverted."); + +#define _DECIMAL_CONTEXT_COPY_NEGATE_METHODDEF \ + {"copy_negate", _PyCFunction_CAST(_decimal_Context_copy_negate), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_copy_negate__doc__}, + +static PyObject * +_decimal_Context_copy_negate_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_copy_negate(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "copy_negate", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_copy_negate_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_logb__doc__, +"logb($self, x, /)\n" +"--\n" +"\n" +"Return the exponent of the magnitude of the operand\'s MSD."); + +#define _DECIMAL_CONTEXT_LOGB_METHODDEF \ + {"logb", _PyCFunction_CAST(_decimal_Context_logb), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_logb__doc__}, + +static PyObject * +_decimal_Context_logb_impl(PyObject *context, PyTypeObject *cls, PyObject *x); + +static PyObject * +_decimal_Context_logb(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "logb", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_logb_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_logical_invert__doc__, +"logical_invert($self, x, /)\n" +"--\n" +"\n" +"Invert all the digits in the operand.\n" +"\n" +"The operand must be a logical number.\n" +"\n" +" >>> ExtendedContext.logical_invert(Decimal(\'0\'))\n" +" Decimal(\'111111111\')\n" +" >>> ExtendedContext.logical_invert(Decimal(\'1\'))\n" +" Decimal(\'111111110\')\n" +" >>> ExtendedContext.logical_invert(Decimal(\'111111111\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_invert(Decimal(\'101010101\'))\n" +" Decimal(\'10101010\')\n" +" >>> ExtendedContext.logical_invert(1101)\n" +" Decimal(\'111110010\')"); + +#define _DECIMAL_CONTEXT_LOGICAL_INVERT_METHODDEF \ + {"logical_invert", _PyCFunction_CAST(_decimal_Context_logical_invert), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_logical_invert__doc__}, + +static PyObject * +_decimal_Context_logical_invert_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_logical_invert(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "logical_invert", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_logical_invert_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_number_class__doc__, +"number_class($self, x, /)\n" +"--\n" +"\n" +"Return an indication of the class of x."); + +#define _DECIMAL_CONTEXT_NUMBER_CLASS_METHODDEF \ + {"number_class", _PyCFunction_CAST(_decimal_Context_number_class), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_number_class__doc__}, + +static PyObject * +_decimal_Context_number_class_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_number_class(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "number_class", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_number_class_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_to_sci_string__doc__, +"to_sci_string($self, x, /)\n" +"--\n" +"\n" +"Convert a number to a string using scientific notation."); + +#define _DECIMAL_CONTEXT_TO_SCI_STRING_METHODDEF \ + {"to_sci_string", _PyCFunction_CAST(_decimal_Context_to_sci_string), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_to_sci_string__doc__}, + +static PyObject * +_decimal_Context_to_sci_string_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_to_sci_string(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "to_sci_string", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_to_sci_string_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_to_eng_string__doc__, +"to_eng_string($self, x, /)\n" +"--\n" +"\n" +"Convert a number to a string, using engineering notation."); + +#define _DECIMAL_CONTEXT_TO_ENG_STRING_METHODDEF \ + {"to_eng_string", _PyCFunction_CAST(_decimal_Context_to_eng_string), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_to_eng_string__doc__}, + +static PyObject * +_decimal_Context_to_eng_string_impl(PyObject *context, PyTypeObject *cls, + PyObject *x); + +static PyObject * +_decimal_Context_to_eng_string(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "to_eng_string", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *x; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + return_value = _decimal_Context_to_eng_string_impl(context, cls, x); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_compare_total__doc__, +"compare_total($self, x, y, /)\n" +"--\n" +"\n" +"Compare x and y using their abstract representation."); + +#define _DECIMAL_CONTEXT_COMPARE_TOTAL_METHODDEF \ + {"compare_total", _PyCFunction_CAST(_decimal_Context_compare_total), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_compare_total__doc__}, + +static PyObject * +_decimal_Context_compare_total_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_compare_total(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "compare_total", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_compare_total_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_compare_total_mag__doc__, +"compare_total_mag($self, x, y, /)\n" +"--\n" +"\n" +"Compare x and y using their abstract representation, ignoring sign."); + +#define _DECIMAL_CONTEXT_COMPARE_TOTAL_MAG_METHODDEF \ + {"compare_total_mag", _PyCFunction_CAST(_decimal_Context_compare_total_mag), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_compare_total_mag__doc__}, + +static PyObject * +_decimal_Context_compare_total_mag_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_compare_total_mag(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "compare_total_mag", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_compare_total_mag_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_copy_sign__doc__, +"copy_sign($self, x, y, /)\n" +"--\n" +"\n" +"Copy the sign from y to x."); + +#define _DECIMAL_CONTEXT_COPY_SIGN_METHODDEF \ + {"copy_sign", _PyCFunction_CAST(_decimal_Context_copy_sign), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_copy_sign__doc__}, + +static PyObject * +_decimal_Context_copy_sign_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_copy_sign(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "copy_sign", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_copy_sign_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_logical_and__doc__, +"logical_and($self, x, y, /)\n" +"--\n" +"\n" +"Applies the logical operation \'and\' between each operand\'s digits.\n" +"\n" +"The operands must be both logical numbers.\n" +"\n" +" >>> ExtendedContext.logical_and(Decimal(\'0\'), Decimal(\'0\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_and(Decimal(\'0\'), Decimal(\'1\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_and(Decimal(\'1\'), Decimal(\'0\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_and(Decimal(\'1\'), Decimal(\'1\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_and(Decimal(\'1100\'), Decimal(\'1010\'))\n" +" Decimal(\'1000\')\n" +" >>> ExtendedContext.logical_and(Decimal(\'1111\'), Decimal(\'10\'))\n" +" Decimal(\'10\')\n" +" >>> ExtendedContext.logical_and(110, 1101)\n" +" Decimal(\'100\')\n" +" >>> ExtendedContext.logical_and(Decimal(110), 1101)\n" +" Decimal(\'100\')\n" +" >>> ExtendedContext.logical_and(110, Decimal(1101))\n" +" Decimal(\'100\')"); + +#define _DECIMAL_CONTEXT_LOGICAL_AND_METHODDEF \ + {"logical_and", _PyCFunction_CAST(_decimal_Context_logical_and), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_logical_and__doc__}, + +static PyObject * +_decimal_Context_logical_and_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_logical_and(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "logical_and", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_logical_and_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_logical_or__doc__, +"logical_or($self, x, y, /)\n" +"--\n" +"\n" +"Applies the logical operation \'or\' between each operand\'s digits.\n" +"\n" +"The operands must be both logical numbers.\n" +"\n" +" >>> ExtendedContext.logical_or(Decimal(\'0\'), Decimal(\'0\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_or(Decimal(\'0\'), Decimal(\'1\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_or(Decimal(\'1\'), Decimal(\'0\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_or(Decimal(\'1\'), Decimal(\'1\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_or(Decimal(\'1100\'), Decimal(\'1010\'))\n" +" Decimal(\'1110\')\n" +" >>> ExtendedContext.logical_or(Decimal(\'1110\'), Decimal(\'10\'))\n" +" Decimal(\'1110\')\n" +" >>> ExtendedContext.logical_or(110, 1101)\n" +" Decimal(\'1111\')\n" +" >>> ExtendedContext.logical_or(Decimal(110), 1101)\n" +" Decimal(\'1111\')\n" +" >>> ExtendedContext.logical_or(110, Decimal(1101))\n" +" Decimal(\'1111\')"); + +#define _DECIMAL_CONTEXT_LOGICAL_OR_METHODDEF \ + {"logical_or", _PyCFunction_CAST(_decimal_Context_logical_or), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_logical_or__doc__}, + +static PyObject * +_decimal_Context_logical_or_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_logical_or(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "logical_or", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_logical_or_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_logical_xor__doc__, +"logical_xor($self, x, y, /)\n" +"--\n" +"\n" +"Applies the logical operation \'xor\' between each operand\'s digits.\n" +"\n" +"The operands must be both logical numbers.\n" +"\n" +" >>> ExtendedContext.logical_xor(Decimal(\'0\'), Decimal(\'0\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_xor(Decimal(\'0\'), Decimal(\'1\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_xor(Decimal(\'1\'), Decimal(\'0\'))\n" +" Decimal(\'1\')\n" +" >>> ExtendedContext.logical_xor(Decimal(\'1\'), Decimal(\'1\'))\n" +" Decimal(\'0\')\n" +" >>> ExtendedContext.logical_xor(Decimal(\'1100\'), Decimal(\'1010\'))\n" +" Decimal(\'110\')\n" +" >>> ExtendedContext.logical_xor(Decimal(\'1111\'), Decimal(\'10\'))\n" +" Decimal(\'1101\')\n" +" >>> ExtendedContext.logical_xor(110, 1101)\n" +" Decimal(\'1011\')\n" +" >>> ExtendedContext.logical_xor(Decimal(110), 1101)\n" +" Decimal(\'1011\')\n" +" >>> ExtendedContext.logical_xor(110, Decimal(1101))\n" +" Decimal(\'1011\')"); + +#define _DECIMAL_CONTEXT_LOGICAL_XOR_METHODDEF \ + {"logical_xor", _PyCFunction_CAST(_decimal_Context_logical_xor), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_logical_xor__doc__}, + +static PyObject * +_decimal_Context_logical_xor_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_logical_xor(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "logical_xor", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_logical_xor_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_rotate__doc__, +"rotate($self, x, y, /)\n" +"--\n" +"\n" +"Return a copy of x, rotated by y places."); + +#define _DECIMAL_CONTEXT_ROTATE_METHODDEF \ + {"rotate", _PyCFunction_CAST(_decimal_Context_rotate), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_rotate__doc__}, + +static PyObject * +_decimal_Context_rotate_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_rotate(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "rotate", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_rotate_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_scaleb__doc__, +"scaleb($self, x, y, /)\n" +"--\n" +"\n" +"Return the first operand after adding the second value to its exp."); + +#define _DECIMAL_CONTEXT_SCALEB_METHODDEF \ + {"scaleb", _PyCFunction_CAST(_decimal_Context_scaleb), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_scaleb__doc__}, + +static PyObject * +_decimal_Context_scaleb_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_scaleb(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "scaleb", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_scaleb_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_shift__doc__, +"shift($self, x, y, /)\n" +"--\n" +"\n" +"Return a copy of x, shifted by y places."); + +#define _DECIMAL_CONTEXT_SHIFT_METHODDEF \ + {"shift", _PyCFunction_CAST(_decimal_Context_shift), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_shift__doc__}, + +static PyObject * +_decimal_Context_shift_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_shift(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "shift", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_shift_impl(context, cls, x, y); + +exit: + return return_value; +} + +PyDoc_STRVAR(_decimal_Context_same_quantum__doc__, +"same_quantum($self, x, y, /)\n" +"--\n" +"\n" +"Return True if the two operands have the same exponent."); + +#define _DECIMAL_CONTEXT_SAME_QUANTUM_METHODDEF \ + {"same_quantum", _PyCFunction_CAST(_decimal_Context_same_quantum), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_same_quantum__doc__}, + +static PyObject * +_decimal_Context_same_quantum_impl(PyObject *context, PyTypeObject *cls, + PyObject *x, PyObject *y); + +static PyObject * +_decimal_Context_same_quantum(PyObject *context, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", "", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "same_quantum", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *x; + PyObject *y; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + x = args[0]; + y = args[1]; + return_value = _decimal_Context_same_quantum_impl(context, cls, x, y); + +exit: + return return_value; +} + +#ifndef _DECIMAL_CONTEXT__UNSAFE_SETPREC_METHODDEF + #define _DECIMAL_CONTEXT__UNSAFE_SETPREC_METHODDEF +#endif /* !defined(_DECIMAL_CONTEXT__UNSAFE_SETPREC_METHODDEF) */ + +#ifndef _DECIMAL_CONTEXT__UNSAFE_SETEMIN_METHODDEF + #define _DECIMAL_CONTEXT__UNSAFE_SETEMIN_METHODDEF +#endif /* !defined(_DECIMAL_CONTEXT__UNSAFE_SETEMIN_METHODDEF) */ + +#ifndef _DECIMAL_CONTEXT__UNSAFE_SETEMAX_METHODDEF + #define _DECIMAL_CONTEXT__UNSAFE_SETEMAX_METHODDEF +#endif /* !defined(_DECIMAL_CONTEXT__UNSAFE_SETEMAX_METHODDEF) */ + +#ifndef _DECIMAL_CONTEXT_APPLY_METHODDEF + #define _DECIMAL_CONTEXT_APPLY_METHODDEF +#endif /* !defined(_DECIMAL_CONTEXT_APPLY_METHODDEF) */ +/*[clinic end generated code: output=0eb835634388294e input=a9049054013a1b77]*/ diff --git a/Modules/_decimal/docstrings.h b/Modules/_decimal/docstrings.h deleted file mode 100644 index 77017a92252cb8..00000000000000 --- a/Modules/_decimal/docstrings.h +++ /dev/null @@ -1,894 +0,0 @@ -/* - * Copyright (c) 2001 Python Software Foundation. All Rights Reserved. - * Modified and extended by Stefan Krah. - */ - - -#ifndef DOCSTRINGS_H -#define DOCSTRINGS_H - - -#include "pymacro.h" - - -/******************************************************************************/ -/* Module */ -/******************************************************************************/ - - -PyDoc_STRVAR(doc__decimal, -"C decimal arithmetic module"); - -PyDoc_STRVAR(doc_getcontext, -"getcontext($module, /)\n--\n\n\ -Get the current default context.\n\ -\n"); - -PyDoc_STRVAR(doc_setcontext, -"setcontext($module, context, /)\n--\n\n\ -Set a new default context.\n\ -\n"); - -PyDoc_STRVAR(doc_localcontext, -"localcontext($module, /, ctx=None, **kwargs)\n--\n\n\ -Return a context manager that will set the default context to a copy of ctx\n\ -on entry to the with-statement and restore the previous default context when\n\ -exiting the with-statement. If no context is specified, a copy of the current\n\ -default context is used.\n\ -\n"); - -PyDoc_STRVAR(doc_ieee_context, -"IEEEContext($module, bits, /)\n--\n\n\ -Return a context object initialized to the proper values for one of the\n\ -IEEE interchange formats. The argument must be a multiple of 32 and less\n\ -than IEEE_CONTEXT_MAX_BITS.\n\ -\n"); - - -/******************************************************************************/ -/* Decimal Object and Methods */ -/******************************************************************************/ - -PyDoc_STRVAR(doc_decimal, -"Decimal(value=\"0\", context=None)\n--\n\n\ -Construct a new Decimal object. 'value' can be an integer, string, tuple,\n\ -or another Decimal object. If no value is given, return Decimal('0'). The\n\ -context does not affect the conversion and is only passed to determine if\n\ -the InvalidOperation trap is active.\n\ -\n"); - -PyDoc_STRVAR(doc_adjusted, -"adjusted($self, /)\n--\n\n\ -Return the adjusted exponent of the number. Defined as exp + digits - 1.\n\ -\n"); - -PyDoc_STRVAR(doc_as_tuple, -"as_tuple($self, /)\n--\n\n\ -Return a tuple representation of the number.\n\ -\n"); - -PyDoc_STRVAR(doc_as_integer_ratio, -"as_integer_ratio($self, /)\n--\n\n\ -Decimal.as_integer_ratio() -> (int, int)\n\ -\n\ -Return a pair of integers, whose ratio is exactly equal to the original\n\ -Decimal and with a positive denominator. The ratio is in lowest terms.\n\ -Raise OverflowError on infinities and a ValueError on NaNs.\n\ -\n"); - -PyDoc_STRVAR(doc_canonical, -"canonical($self, /)\n--\n\n\ -Return the canonical encoding of the argument. Currently, the encoding\n\ -of a Decimal instance is always canonical, so this operation returns its\n\ -argument unchanged.\n\ -\n"); - -PyDoc_STRVAR(doc_compare, -"compare($self, /, other, context=None)\n--\n\n\ -Compare self to other. Return a decimal value:\n\ -\n\ - a or b is a NaN ==> Decimal('NaN')\n\ - a < b ==> Decimal('-1')\n\ - a == b ==> Decimal('0')\n\ - a > b ==> Decimal('1')\n\ -\n"); - -PyDoc_STRVAR(doc_compare_signal, -"compare_signal($self, /, other, context=None)\n--\n\n\ -Identical to compare, except that all NaNs signal.\n\ -\n"); - -PyDoc_STRVAR(doc_compare_total, -"compare_total($self, /, other, context=None)\n--\n\n\ -Compare two operands using their abstract representation rather than\n\ -their numerical value. Similar to the compare() method, but the result\n\ -gives a total ordering on Decimal instances. Two Decimal instances with\n\ -the same numeric value but different representations compare unequal\n\ -in this ordering:\n\ -\n\ - >>> Decimal('12.0').compare_total(Decimal('12'))\n\ - Decimal('-1')\n\ -\n\ -Quiet and signaling NaNs are also included in the total ordering. The result\n\ -of this function is Decimal('0') if both operands have the same representation,\n\ -Decimal('-1') if the first operand is lower in the total order than the second,\n\ -and Decimal('1') if the first operand is higher in the total order than the\n\ -second operand. See the specification for details of the total order.\n\ -\n\ -This operation is unaffected by context and is quiet: no flags are changed\n\ -and no rounding is performed. As an exception, the C version may raise\n\ -InvalidOperation if the second operand cannot be converted exactly.\n\ -\n"); - -PyDoc_STRVAR(doc_compare_total_mag, -"compare_total_mag($self, /, other, context=None)\n--\n\n\ -Compare two operands using their abstract representation rather than their\n\ -value as in compare_total(), but ignoring the sign of each operand.\n\ -\n\ -x.compare_total_mag(y) is equivalent to x.copy_abs().compare_total(y.copy_abs()).\n\ -\n\ -This operation is unaffected by context and is quiet: no flags are changed\n\ -and no rounding is performed. As an exception, the C version may raise\n\ -InvalidOperation if the second operand cannot be converted exactly.\n\ -\n"); - -PyDoc_STRVAR(doc_conjugate, -"conjugate($self, /)\n--\n\n\ -Return self.\n\ -\n"); - -PyDoc_STRVAR(doc_copy_abs, -"copy_abs($self, /)\n--\n\n\ -Return the absolute value of the argument. This operation is unaffected by\n\ -context and is quiet: no flags are changed and no rounding is performed.\n\ -\n"); - -PyDoc_STRVAR(doc_copy_negate, -"copy_negate($self, /)\n--\n\n\ -Return the negation of the argument. This operation is unaffected by context\n\ -and is quiet: no flags are changed and no rounding is performed.\n\ -\n"); - -PyDoc_STRVAR(doc_copy_sign, -"copy_sign($self, /, other, context=None)\n--\n\n\ -Return a copy of the first operand with the sign set to be the same as the\n\ -sign of the second operand. For example:\n\ -\n\ - >>> Decimal('2.3').copy_sign(Decimal('-1.5'))\n\ - Decimal('-2.3')\n\ -\n\ -This operation is unaffected by context and is quiet: no flags are changed\n\ -and no rounding is performed. As an exception, the C version may raise\n\ -InvalidOperation if the second operand cannot be converted exactly.\n\ -\n"); - -PyDoc_STRVAR(doc_exp, -"exp($self, /, context=None)\n--\n\n\ -Return the value of the (natural) exponential function e**x at the given\n\ -number. The function always uses the ROUND_HALF_EVEN mode and the result\n\ -is correctly rounded.\n\ -\n"); - -PyDoc_STRVAR(doc_from_float, -"from_float($type, f, /)\n--\n\n\ -Class method that converts a float to a decimal number, exactly.\n\ -Since 0.1 is not exactly representable in binary floating point,\n\ -Decimal.from_float(0.1) is not the same as Decimal('0.1').\n\ -\n\ - >>> Decimal.from_float(0.1)\n\ - Decimal('0.1000000000000000055511151231257827021181583404541015625')\n\ - >>> Decimal.from_float(float('nan'))\n\ - Decimal('NaN')\n\ - >>> Decimal.from_float(float('inf'))\n\ - Decimal('Infinity')\n\ - >>> Decimal.from_float(float('-inf'))\n\ - Decimal('-Infinity')\n\ -\n\ -\n"); - -PyDoc_STRVAR(doc_from_number, -"from_number($type, number, /)\n--\n\n\ -Class method that converts a real number to a decimal number, exactly.\n\ -\n\ - >>> Decimal.from_number(314) # int\n\ - Decimal('314')\n\ - >>> Decimal.from_number(0.1) # float\n\ - Decimal('0.1000000000000000055511151231257827021181583404541015625')\n\ - >>> Decimal.from_number(Decimal('3.14')) # another decimal instance\n\ - Decimal('3.14')\n\ -\n\ -\n"); - -PyDoc_STRVAR(doc_fma, -"fma($self, /, other, third, context=None)\n--\n\n\ -Fused multiply-add. Return self*other+third with no rounding of the\n\ -intermediate product self*other.\n\ -\n\ - >>> Decimal(2).fma(3, 5)\n\ - Decimal('11')\n\ -\n\ -\n"); - -PyDoc_STRVAR(doc_is_canonical, -"is_canonical($self, /)\n--\n\n\ -Return True if the argument is canonical and False otherwise. Currently,\n\ -a Decimal instance is always canonical, so this operation always returns\n\ -True.\n\ -\n"); - -PyDoc_STRVAR(doc_is_finite, -"is_finite($self, /)\n--\n\n\ -Return True if the argument is a finite number, and False if the argument\n\ -is infinite or a NaN.\n\ -\n"); - -PyDoc_STRVAR(doc_is_infinite, -"is_infinite($self, /)\n--\n\n\ -Return True if the argument is either positive or negative infinity and\n\ -False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_is_nan, -"is_nan($self, /)\n--\n\n\ -Return True if the argument is a (quiet or signaling) NaN and False\n\ -otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_is_normal, -"is_normal($self, /, context=None)\n--\n\n\ -Return True if the argument is a normal finite non-zero number with an\n\ -adjusted exponent greater than or equal to Emin. Return False if the\n\ -argument is zero, subnormal, infinite or a NaN.\n\ -\n"); - -PyDoc_STRVAR(doc_is_qnan, -"is_qnan($self, /)\n--\n\n\ -Return True if the argument is a quiet NaN, and False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_is_signed, -"is_signed($self, /)\n--\n\n\ -Return True if the argument has a negative sign and False otherwise.\n\ -Note that both zeros and NaNs can carry signs.\n\ -\n"); - -PyDoc_STRVAR(doc_is_snan, -"is_snan($self, /)\n--\n\n\ -Return True if the argument is a signaling NaN and False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_is_subnormal, -"is_subnormal($self, /, context=None)\n--\n\n\ -Return True if the argument is subnormal, and False otherwise. A number is\n\ -subnormal if it is non-zero, finite, and has an adjusted exponent less\n\ -than Emin.\n\ -\n"); - -PyDoc_STRVAR(doc_is_zero, -"is_zero($self, /)\n--\n\n\ -Return True if the argument is a (positive or negative) zero and False\n\ -otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ln, -"ln($self, /, context=None)\n--\n\n\ -Return the natural (base e) logarithm of the operand. The function always\n\ -uses the ROUND_HALF_EVEN mode and the result is correctly rounded.\n\ -\n"); - -PyDoc_STRVAR(doc_log10, -"log10($self, /, context=None)\n--\n\n\ -Return the base ten logarithm of the operand. The function always uses the\n\ -ROUND_HALF_EVEN mode and the result is correctly rounded.\n\ -\n"); - -PyDoc_STRVAR(doc_logb, -"logb($self, /, context=None)\n--\n\n\ -For a non-zero number, return the adjusted exponent of the operand as a\n\ -Decimal instance. If the operand is a zero, then Decimal('-Infinity') is\n\ -returned and the DivisionByZero condition is raised. If the operand is\n\ -an infinity then Decimal('Infinity') is returned.\n\ -\n"); - -PyDoc_STRVAR(doc_logical_and, -"logical_and($self, /, other, context=None)\n--\n\n\ -Return the digit-wise 'and' of the two (logical) operands.\n\ -\n"); - -PyDoc_STRVAR(doc_logical_invert, -"logical_invert($self, /, context=None)\n--\n\n\ -Return the digit-wise inversion of the (logical) operand.\n\ -\n"); - -PyDoc_STRVAR(doc_logical_or, -"logical_or($self, /, other, context=None)\n--\n\n\ -Return the digit-wise 'or' of the two (logical) operands.\n\ -\n"); - -PyDoc_STRVAR(doc_logical_xor, -"logical_xor($self, /, other, context=None)\n--\n\n\ -Return the digit-wise 'exclusive or' of the two (logical) operands.\n\ -\n"); - -PyDoc_STRVAR(doc_max, -"max($self, /, other, context=None)\n--\n\n\ -Maximum of self and other. If one operand is a quiet NaN and the other is\n\ -numeric, the numeric operand is returned.\n\ -\n"); - -PyDoc_STRVAR(doc_max_mag, -"max_mag($self, /, other, context=None)\n--\n\n\ -Similar to the max() method, but the comparison is done using the absolute\n\ -values of the operands.\n\ -\n"); - -PyDoc_STRVAR(doc_min, -"min($self, /, other, context=None)\n--\n\n\ -Minimum of self and other. If one operand is a quiet NaN and the other is\n\ -numeric, the numeric operand is returned.\n\ -\n"); - -PyDoc_STRVAR(doc_min_mag, -"min_mag($self, /, other, context=None)\n--\n\n\ -Similar to the min() method, but the comparison is done using the absolute\n\ -values of the operands.\n\ -\n"); - -PyDoc_STRVAR(doc_next_minus, -"next_minus($self, /, context=None)\n--\n\n\ -Return the largest number representable in the given context (or in the\n\ -current default context if no context is given) that is smaller than the\n\ -given operand.\n\ -\n"); - -PyDoc_STRVAR(doc_next_plus, -"next_plus($self, /, context=None)\n--\n\n\ -Return the smallest number representable in the given context (or in the\n\ -current default context if no context is given) that is larger than the\n\ -given operand.\n\ -\n"); - -PyDoc_STRVAR(doc_next_toward, -"next_toward($self, /, other, context=None)\n--\n\n\ -If the two operands are unequal, return the number closest to the first\n\ -operand in the direction of the second operand. If both operands are\n\ -numerically equal, return a copy of the first operand with the sign set\n\ -to be the same as the sign of the second operand.\n\ -\n"); - -PyDoc_STRVAR(doc_normalize, -"normalize($self, /, context=None)\n--\n\n\ -Normalize the number by stripping the rightmost trailing zeros and\n\ -converting any result equal to Decimal('0') to Decimal('0e0'). Used\n\ -for producing canonical values for members of an equivalence class.\n\ -For example, Decimal('32.100') and Decimal('0.321000e+2') both normalize\n\ -to the equivalent value Decimal('32.1').\n\ -\n"); - -PyDoc_STRVAR(doc_number_class, -"number_class($self, /, context=None)\n--\n\n\ -Return a string describing the class of the operand. The returned value\n\ -is one of the following ten strings:\n\ -\n\ - * '-Infinity', indicating that the operand is negative infinity.\n\ - * '-Normal', indicating that the operand is a negative normal number.\n\ - * '-Subnormal', indicating that the operand is negative and subnormal.\n\ - * '-Zero', indicating that the operand is a negative zero.\n\ - * '+Zero', indicating that the operand is a positive zero.\n\ - * '+Subnormal', indicating that the operand is positive and subnormal.\n\ - * '+Normal', indicating that the operand is a positive normal number.\n\ - * '+Infinity', indicating that the operand is positive infinity.\n\ - * 'NaN', indicating that the operand is a quiet NaN (Not a Number).\n\ - * 'sNaN', indicating that the operand is a signaling NaN.\n\ -\n\ -\n"); - -PyDoc_STRVAR(doc_quantize, -"quantize($self, /, exp, rounding=None, context=None)\n--\n\n\ -Return a value equal to the first operand after rounding and having the\n\ -exponent of the second operand.\n\ -\n\ - >>> Decimal('1.41421356').quantize(Decimal('1.000'))\n\ - Decimal('1.414')\n\ -\n\ -Unlike other operations, if the length of the coefficient after the quantize\n\ -operation would be greater than precision, then an InvalidOperation is signaled.\n\ -This guarantees that, unless there is an error condition, the quantized exponent\n\ -is always equal to that of the right-hand operand.\n\ -\n\ -Also unlike other operations, quantize never signals Underflow, even if the\n\ -result is subnormal and inexact.\n\ -\n\ -If the exponent of the second operand is larger than that of the first, then\n\ -rounding may be necessary. In this case, the rounding mode is determined by the\n\ -rounding argument if given, else by the given context argument; if neither\n\ -argument is given, the rounding mode of the current thread's context is used.\n\ -\n"); - -PyDoc_STRVAR(doc_radix, -"radix($self, /)\n--\n\n\ -Return Decimal(10), the radix (base) in which the Decimal class does\n\ -all its arithmetic. Included for compatibility with the specification.\n\ -\n"); - -PyDoc_STRVAR(doc_remainder_near, -"remainder_near($self, /, other, context=None)\n--\n\n\ -Return the remainder from dividing self by other. This differs from\n\ -self % other in that the sign of the remainder is chosen so as to minimize\n\ -its absolute value. More precisely, the return value is self - n * other\n\ -where n is the integer nearest to the exact value of self / other, and\n\ -if two integers are equally near then the even one is chosen.\n\ -\n\ -If the result is zero then its sign will be the sign of self.\n\ -\n"); - -PyDoc_STRVAR(doc_rotate, -"rotate($self, /, other, context=None)\n--\n\n\ -Return the result of rotating the digits of the first operand by an amount\n\ -specified by the second operand. The second operand must be an integer in\n\ -the range -precision through precision. The absolute value of the second\n\ -operand gives the number of places to rotate. If the second operand is\n\ -positive then rotation is to the left; otherwise rotation is to the right.\n\ -The coefficient of the first operand is padded on the left with zeros to\n\ -length precision if necessary. The sign and exponent of the first operand are\n\ -unchanged.\n\ -\n"); - -PyDoc_STRVAR(doc_same_quantum, -"same_quantum($self, /, other, context=None)\n--\n\n\ -Test whether self and other have the same exponent or whether both are NaN.\n\ -\n\ -This operation is unaffected by context and is quiet: no flags are changed\n\ -and no rounding is performed. As an exception, the C version may raise\n\ -InvalidOperation if the second operand cannot be converted exactly.\n\ -\n"); - -PyDoc_STRVAR(doc_scaleb, -"scaleb($self, /, other, context=None)\n--\n\n\ -Return the first operand with the exponent adjusted the second. Equivalently,\n\ -return the first operand multiplied by 10**other. The second operand must be\n\ -an integer.\n\ -\n"); - -PyDoc_STRVAR(doc_shift, -"shift($self, /, other, context=None)\n--\n\n\ -Return the result of shifting the digits of the first operand by an amount\n\ -specified by the second operand. The second operand must be an integer in\n\ -the range -precision through precision. The absolute value of the second\n\ -operand gives the number of places to shift. If the second operand is\n\ -positive, then the shift is to the left; otherwise the shift is to the\n\ -right. Digits shifted into the coefficient are zeros. The sign and exponent\n\ -of the first operand are unchanged.\n\ -\n"); - -PyDoc_STRVAR(doc_sqrt, -"sqrt($self, /, context=None)\n--\n\n\ -Return the square root of the argument to full precision. The result is\n\ -correctly rounded using the ROUND_HALF_EVEN rounding mode.\n\ -\n"); - -PyDoc_STRVAR(doc_to_eng_string, -"to_eng_string($self, /, context=None)\n--\n\n\ -Convert to an engineering-type string. Engineering notation has an exponent\n\ -which is a multiple of 3, so there are up to 3 digits left of the decimal\n\ -place. For example, Decimal('123E+1') is converted to Decimal('1.23E+3').\n\ -\n\ -The value of context.capitals determines whether the exponent sign is lower\n\ -or upper case. Otherwise, the context does not affect the operation.\n\ -\n"); - -PyDoc_STRVAR(doc_to_integral, -"to_integral($self, /, rounding=None, context=None)\n--\n\n\ -Identical to the to_integral_value() method. The to_integral() name has been\n\ -kept for compatibility with older versions.\n\ -\n"); - -PyDoc_STRVAR(doc_to_integral_exact, -"to_integral_exact($self, /, rounding=None, context=None)\n--\n\n\ -Round to the nearest integer, signaling Inexact or Rounded as appropriate if\n\ -rounding occurs. The rounding mode is determined by the rounding parameter\n\ -if given, else by the given context. If neither parameter is given, then the\n\ -rounding mode of the current default context is used.\n\ -\n"); - -PyDoc_STRVAR(doc_to_integral_value, -"to_integral_value($self, /, rounding=None, context=None)\n--\n\n\ -Round to the nearest integer without signaling Inexact or Rounded. The\n\ -rounding mode is determined by the rounding parameter if given, else by\n\ -the given context. If neither parameter is given, then the rounding mode\n\ -of the current default context is used.\n\ -\n"); - - -/******************************************************************************/ -/* Context Object and Methods */ -/******************************************************************************/ - -PyDoc_STRVAR(doc_context, -"Context(prec=None, rounding=None, Emin=None, Emax=None, capitals=None, clamp=None, flags=None, traps=None)\n--\n\n\ -The context affects almost all operations and controls rounding,\n\ -Over/Underflow, raising of exceptions and much more. A new context\n\ -can be constructed as follows:\n\ -\n\ - >>> c = Context(prec=28, Emin=-425000000, Emax=425000000,\n\ - ... rounding=ROUND_HALF_EVEN, capitals=1, clamp=1,\n\ - ... traps=[InvalidOperation, DivisionByZero, Overflow],\n\ - ... flags=[])\n\ - >>>\n\ -\n\ -\n"); - -#ifdef EXTRA_FUNCTIONALITY -PyDoc_STRVAR(doc_ctx_apply, -"apply($self, x, /)\n--\n\n\ -Apply self to Decimal x.\n\ -\n"); -#endif - -PyDoc_STRVAR(doc_ctx_clear_flags, -"clear_flags($self, /)\n--\n\n\ -Reset all flags to False.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_clear_traps, -"clear_traps($self, /)\n--\n\n\ -Set all traps to False.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_copy, -"copy($self, /)\n--\n\n\ -Return a duplicate of the context with all flags cleared.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_copy_decimal, -"copy_decimal($self, x, /)\n--\n\n\ -Return a copy of Decimal x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_create_decimal, -"create_decimal($self, num=\"0\", /)\n--\n\n\ -Create a new Decimal instance from num, using self as the context. Unlike the\n\ -Decimal constructor, this function observes the context limits.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_create_decimal_from_float, -"create_decimal_from_float($self, f, /)\n--\n\n\ -Create a new Decimal instance from float f. Unlike the Decimal.from_float()\n\ -class method, this function observes the context limits.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_Etiny, -"Etiny($self, /)\n--\n\n\ -Return a value equal to Emin - prec + 1, which is the minimum exponent value\n\ -for subnormal results. When underflow occurs, the exponent is set to Etiny.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_Etop, -"Etop($self, /)\n--\n\n\ -Return a value equal to Emax - prec + 1. This is the maximum exponent\n\ -if the _clamp field of the context is set to 1 (IEEE clamp mode). Etop()\n\ -must not be negative.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_abs, -"abs($self, x, /)\n--\n\n\ -Return the absolute value of x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_add, -"add($self, x, y, /)\n--\n\n\ -Return the sum of x and y.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_canonical, -"canonical($self, x, /)\n--\n\n\ -Return a new instance of x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_compare, -"compare($self, x, y, /)\n--\n\n\ -Compare x and y numerically.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_compare_signal, -"compare_signal($self, x, y, /)\n--\n\n\ -Compare x and y numerically. All NaNs signal.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_compare_total, -"compare_total($self, x, y, /)\n--\n\n\ -Compare x and y using their abstract representation.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_compare_total_mag, -"compare_total_mag($self, x, y, /)\n--\n\n\ -Compare x and y using their abstract representation, ignoring sign.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_copy_abs, -"copy_abs($self, x, /)\n--\n\n\ -Return a copy of x with the sign set to 0.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_copy_negate, -"copy_negate($self, x, /)\n--\n\n\ -Return a copy of x with the sign inverted.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_copy_sign, -"copy_sign($self, x, y, /)\n--\n\n\ -Copy the sign from y to x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_divide, -"divide($self, x, y, /)\n--\n\n\ -Return x divided by y.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_divide_int, -"divide_int($self, x, y, /)\n--\n\n\ -Return x divided by y, truncated to an integer.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_divmod, -"divmod($self, x, y, /)\n--\n\n\ -Return quotient and remainder of the division x / y.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_exp, -"exp($self, x, /)\n--\n\n\ -Return e ** x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_fma, -"fma($self, x, y, z, /)\n--\n\n\ -Return x multiplied by y, plus z.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_is_canonical, -"is_canonical($self, x, /)\n--\n\n\ -Return True if x is canonical, False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_is_finite, -"is_finite($self, x, /)\n--\n\n\ -Return True if x is finite, False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_is_infinite, -"is_infinite($self, x, /)\n--\n\n\ -Return True if x is infinite, False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_is_nan, -"is_nan($self, x, /)\n--\n\n\ -Return True if x is a qNaN or sNaN, False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_is_normal, -"is_normal($self, x, /)\n--\n\n\ -Return True if x is a normal number, False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_is_qnan, -"is_qnan($self, x, /)\n--\n\n\ -Return True if x is a quiet NaN, False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_is_signed, -"is_signed($self, x, /)\n--\n\n\ -Return True if x is negative, False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_is_snan, -"is_snan($self, x, /)\n--\n\n\ -Return True if x is a signaling NaN, False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_is_subnormal, -"is_subnormal($self, x, /)\n--\n\n\ -Return True if x is subnormal, False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_is_zero, -"is_zero($self, x, /)\n--\n\n\ -Return True if x is a zero, False otherwise.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_ln, -"ln($self, x, /)\n--\n\n\ -Return the natural (base e) logarithm of x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_log10, -"log10($self, x, /)\n--\n\n\ -Return the base 10 logarithm of x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_logb, -"logb($self, x, /)\n--\n\n\ -Return the exponent of the magnitude of the operand's MSD.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_logical_and, -"logical_and($self, x, y, /)\n--\n\n\ -Digit-wise and of x and y.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_logical_invert, -"logical_invert($self, x, /)\n--\n\n\ -Invert all digits of x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_logical_or, -"logical_or($self, x, y, /)\n--\n\n\ -Digit-wise or of x and y.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_logical_xor, -"logical_xor($self, x, y, /)\n--\n\n\ -Digit-wise xor of x and y.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_max, -"max($self, x, y, /)\n--\n\n\ -Compare the values numerically and return the maximum.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_max_mag, -"max_mag($self, x, y, /)\n--\n\n\ -Compare the values numerically with their sign ignored.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_min, -"min($self, x, y, /)\n--\n\n\ -Compare the values numerically and return the minimum.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_min_mag, -"min_mag($self, x, y, /)\n--\n\n\ -Compare the values numerically with their sign ignored.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_minus, -"minus($self, x, /)\n--\n\n\ -Minus corresponds to the unary prefix minus operator in Python, but applies\n\ -the context to the result.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_multiply, -"multiply($self, x, y, /)\n--\n\n\ -Return the product of x and y.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_next_minus, -"next_minus($self, x, /)\n--\n\n\ -Return the largest representable number smaller than x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_next_plus, -"next_plus($self, x, /)\n--\n\n\ -Return the smallest representable number larger than x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_next_toward, -"next_toward($self, x, y, /)\n--\n\n\ -Return the number closest to x, in the direction towards y.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_normalize, -"normalize($self, x, /)\n--\n\n\ -Reduce x to its simplest form. Alias for reduce(x).\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_number_class, -"number_class($self, x, /)\n--\n\n\ -Return an indication of the class of x.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_plus, -"plus($self, x, /)\n--\n\n\ -Plus corresponds to the unary prefix plus operator in Python, but applies\n\ -the context to the result.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_power, -"power($self, /, a, b, modulo=None)\n--\n\n\ -Compute a**b. If 'a' is negative, then 'b' must be integral. The result\n\ -will be inexact unless 'a' is integral and the result is finite and can\n\ -be expressed exactly in 'precision' digits. In the Python version the\n\ -result is always correctly rounded, in the C version the result is almost\n\ -always correctly rounded.\n\ -\n\ -If modulo is given, compute (a**b) % modulo. The following restrictions\n\ -hold:\n\ -\n\ - * all three arguments must be integral\n\ - * 'b' must be nonnegative\n\ - * at least one of 'a' or 'b' must be nonzero\n\ - * modulo must be nonzero and less than 10**prec in absolute value\n\ -\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_quantize, -"quantize($self, x, y, /)\n--\n\n\ -Return a value equal to x (rounded), having the exponent of y.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_radix, -"radix($self, /)\n--\n\n\ -Return 10.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_remainder, -"remainder($self, x, y, /)\n--\n\n\ -Return the remainder from integer division. The sign of the result,\n\ -if non-zero, is the same as that of the original dividend.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_remainder_near, -"remainder_near($self, x, y, /)\n--\n\n\ -Return x - y * n, where n is the integer nearest the exact value of x / y\n\ -(if the result is 0 then its sign will be the sign of x).\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_rotate, -"rotate($self, x, y, /)\n--\n\n\ -Return a copy of x, rotated by y places.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_same_quantum, -"same_quantum($self, x, y, /)\n--\n\n\ -Return True if the two operands have the same exponent.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_scaleb, -"scaleb($self, x, y, /)\n--\n\n\ -Return the first operand after adding the second value to its exp.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_shift, -"shift($self, x, y, /)\n--\n\n\ -Return a copy of x, shifted by y places.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_sqrt, -"sqrt($self, x, /)\n--\n\n\ -Square root of a non-negative number to context precision.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_subtract, -"subtract($self, x, y, /)\n--\n\n\ -Return the difference between x and y.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_to_eng_string, -"to_eng_string($self, x, /)\n--\n\n\ -Convert a number to a string, using engineering notation.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_to_integral, -"to_integral($self, x, /)\n--\n\n\ -Identical to to_integral_value(x).\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_to_integral_exact, -"to_integral_exact($self, x, /)\n--\n\n\ -Round to an integer. Signal if the result is rounded or inexact.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_to_integral_value, -"to_integral_value($self, x, /)\n--\n\n\ -Round to an integer.\n\ -\n"); - -PyDoc_STRVAR(doc_ctx_to_sci_string, -"to_sci_string($self, x, /)\n--\n\n\ -Convert a number to a string using scientific notation.\n\ -\n"); - - -#endif /* DOCSTRINGS_H */ - - - diff --git a/Modules/_decimal/libmpdec/README.txt b/Modules/_decimal/libmpdec/README.txt deleted file mode 100644 index c1d481dee76452..00000000000000 --- a/Modules/_decimal/libmpdec/README.txt +++ /dev/null @@ -1,88 +0,0 @@ - - -libmpdec -======== - -libmpdec is a fast C/C++ library for correctly-rounded arbitrary precision -decimal floating point arithmetic. It is a complete implementation of -Mike Cowlishaw/IBM's General Decimal Arithmetic Specification. - - -Files required for the Python _decimal module -============================================= - - Core files for small and medium precision arithmetic - ---------------------------------------------------- - - basearith.{c,h} -> Core arithmetic in base 10**9 or 10**19. - bits.h -> Portable detection of least/most significant one-bit. - constants.{c,h} -> Constants that are used in multiple files. - context.c -> Context functions. - io.{c,h} -> Conversions between mpd_t and ASCII strings, - mpd_t formatting (allows UTF-8 fill character). - mpalloc.{c,h} -> Allocation handlers with overflow detection - and functions for switching between static - and dynamic mpd_t. - mpdecimal.{c,h} -> All (quiet) functions of the specification. - typearith.h -> Fast primitives for double word multiplication, - division etc. - - Visual Studio only: - ~~~~~~~~~~~~~~~~~~~ - vcdiv64.asm -> Double word division used in typearith.h. VS 2008 does - not allow inline asm for x64. Also, it does not provide - an intrinsic for double word division. - - Files for bignum arithmetic: - ---------------------------- - - The following files implement the Fast Number Theoretic Transform - used for multiplying coefficients with more than 1024 words (see - mpdecimal.c: _mpd_fntmul()). - - umodarith.h -> Fast low level routines for unsigned modular arithmetic. - numbertheory.{c,h} -> Routines for setting up the Number Theoretic Transform. - difradix2.{c,h} -> Decimation in frequency transform, used as the - "base case" by the following three files: - - fnt.{c,h} -> Transform arrays up to 4096 words. - sixstep.{c,h} -> Transform larger arrays of length 2**n. - fourstep.{c,h} -> Transform larger arrays of length 3 * 2**n. - - convolute.{c,h} -> Fast convolution using one of the three transform - functions. - transpose.{c,h} -> Transpositions needed for the sixstep algorithm. - crt.{c,h} -> Chinese Remainder Theorem: use information from three - transforms modulo three different primes to get the - final result. - - -Pointers to literature, proofs and more -======================================= - - literature/ - ----------- - - REFERENCES.txt -> List of relevant papers. - bignum.txt -> Explanation of the Fast Number Theoretic Transform (FNT). - fnt.py -> Verify constants used in the FNT; Python demo for the - O(N**2) discrete transform. - - matrix-transform.txt -> Proof for the Matrix Fourier Transform used in - fourstep.c. - six-step.txt -> Show that the algorithm used in sixstep.c is - a variant of the Matrix Fourier Transform. - mulmod-64.txt -> Proof for the mulmod64 algorithm from - umodarith.h. - mulmod-ppro.txt -> Proof for the x87 FPU modular multiplication - from umodarith.h. - umodarith.lisp -> ACL2 proofs for many functions from umodarith.h. - - -Library Author -============== - - Stefan Krah <skrah@bytereef.org> - - - diff --git a/Modules/_decimal/libmpdec/basearith.c b/Modules/_decimal/libmpdec/basearith.c deleted file mode 100644 index 85c608fadf5156..00000000000000 --- a/Modules/_decimal/libmpdec/basearith.c +++ /dev/null @@ -1,655 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> -#include <stdio.h> - -#include "basearith.h" -#include "constants.h" -#include "typearith.h" - - -/*********************************************************************/ -/* Calculations in base MPD_RADIX */ -/*********************************************************************/ - - -/* - * Knuth, TAOCP, Volume 2, 4.3.1: - * w := sum of u (len m) and v (len n) - * n > 0 and m >= n - * The calling function has to handle a possible final carry. - */ -mpd_uint_t -_mpd_baseadd(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, - mpd_size_t m, mpd_size_t n) -{ - mpd_uint_t s; - mpd_uint_t carry = 0; - mpd_size_t i; - - assert(n > 0 && m >= n); - - /* add n members of u and v */ - for (i = 0; i < n; i++) { - s = u[i] + (v[i] + carry); - carry = (s < u[i]) | (s >= MPD_RADIX); - w[i] = carry ? s-MPD_RADIX : s; - } - /* if there is a carry, propagate it */ - for (; carry && i < m; i++) { - s = u[i] + carry; - carry = (s == MPD_RADIX); - w[i] = carry ? 0 : s; - } - /* copy the rest of u */ - for (; i < m; i++) { - w[i] = u[i]; - } - - return carry; -} - -/* - * Add the contents of u to w. Carries are propagated further. The caller - * has to make sure that w is big enough. - */ -void -_mpd_baseaddto(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n) -{ - mpd_uint_t s; - mpd_uint_t carry = 0; - mpd_size_t i; - - if (n == 0) return; - - /* add n members of u to w */ - for (i = 0; i < n; i++) { - s = w[i] + (u[i] + carry); - carry = (s < w[i]) | (s >= MPD_RADIX); - w[i] = carry ? s-MPD_RADIX : s; - } - /* if there is a carry, propagate it */ - for (; carry; i++) { - s = w[i] + carry; - carry = (s == MPD_RADIX); - w[i] = carry ? 0 : s; - } -} - -/* - * Add v to w (len m). The calling function has to handle a possible - * final carry. Assumption: m > 0. - */ -mpd_uint_t -_mpd_shortadd(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v) -{ - mpd_uint_t s; - mpd_uint_t carry; - mpd_size_t i; - - assert(m > 0); - - /* add v to w */ - s = w[0] + v; - carry = (s < v) | (s >= MPD_RADIX); - w[0] = carry ? s-MPD_RADIX : s; - - /* if there is a carry, propagate it */ - for (i = 1; carry && i < m; i++) { - s = w[i] + carry; - carry = (s == MPD_RADIX); - w[i] = carry ? 0 : s; - } - - return carry; -} - -/* Increment u. The calling function has to handle a possible carry. */ -mpd_uint_t -_mpd_baseincr(mpd_uint_t *u, mpd_size_t n) -{ - mpd_uint_t s; - mpd_uint_t carry = 1; - mpd_size_t i; - - assert(n > 0); - - /* if there is a carry, propagate it */ - for (i = 0; carry && i < n; i++) { - s = u[i] + carry; - carry = (s == MPD_RADIX); - u[i] = carry ? 0 : s; - } - - return carry; -} - -/* - * Knuth, TAOCP, Volume 2, 4.3.1: - * w := difference of u (len m) and v (len n). - * number in u >= number in v; - */ -void -_mpd_basesub(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, - mpd_size_t m, mpd_size_t n) -{ - mpd_uint_t d; - mpd_uint_t borrow = 0; - mpd_size_t i; - - assert(m > 0 && n > 0); - - /* subtract n members of v from u */ - for (i = 0; i < n; i++) { - d = u[i] - (v[i] + borrow); - borrow = (u[i] < d); - w[i] = borrow ? d + MPD_RADIX : d; - } - /* if there is a borrow, propagate it */ - for (; borrow && i < m; i++) { - d = u[i] - borrow; - borrow = (u[i] == 0); - w[i] = borrow ? MPD_RADIX-1 : d; - } - /* copy the rest of u */ - for (; i < m; i++) { - w[i] = u[i]; - } -} - -/* - * Subtract the contents of u from w. w is larger than u. Borrows are - * propagated further, but eventually w can absorb the final borrow. - */ -void -_mpd_basesubfrom(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n) -{ - mpd_uint_t d; - mpd_uint_t borrow = 0; - mpd_size_t i; - - if (n == 0) return; - - /* subtract n members of u from w */ - for (i = 0; i < n; i++) { - d = w[i] - (u[i] + borrow); - borrow = (w[i] < d); - w[i] = borrow ? d + MPD_RADIX : d; - } - /* if there is a borrow, propagate it */ - for (; borrow; i++) { - d = w[i] - borrow; - borrow = (w[i] == 0); - w[i] = borrow ? MPD_RADIX-1 : d; - } -} - -/* w := product of u (len n) and v (single word) */ -void -_mpd_shortmul(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v) -{ - mpd_uint_t hi, lo; - mpd_uint_t carry = 0; - mpd_size_t i; - - assert(n > 0); - - for (i=0; i < n; i++) { - - _mpd_mul_words(&hi, &lo, u[i], v); - lo = carry + lo; - if (lo < carry) hi++; - - _mpd_div_words_r(&carry, &w[i], hi, lo); - } - w[i] = carry; -} - -/* - * Knuth, TAOCP, Volume 2, 4.3.1: - * w := product of u (len m) and v (len n) - * w must be initialized to zero - */ -void -_mpd_basemul(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, - mpd_size_t m, mpd_size_t n) -{ - mpd_uint_t hi, lo; - mpd_uint_t carry; - mpd_size_t i, j; - - assert(m > 0 && n > 0); - - for (j=0; j < n; j++) { - carry = 0; - for (i=0; i < m; i++) { - - _mpd_mul_words(&hi, &lo, u[i], v[j]); - lo = w[i+j] + lo; - if (lo < w[i+j]) hi++; - lo = carry + lo; - if (lo < carry) hi++; - - _mpd_div_words_r(&carry, &w[i+j], hi, lo); - } - w[j+m] = carry; - } -} - -/* - * Knuth, TAOCP Volume 2, 4.3.1, exercise 16: - * w := quotient of u (len n) divided by a single word v - */ -mpd_uint_t -_mpd_shortdiv(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v) -{ - mpd_uint_t hi, lo; - mpd_uint_t rem = 0; - mpd_size_t i; - - assert(n > 0); - - for (i=n-1; i != MPD_SIZE_MAX; i--) { - - _mpd_mul_words(&hi, &lo, rem, MPD_RADIX); - lo = u[i] + lo; - if (lo < u[i]) hi++; - - _mpd_div_words(&w[i], &rem, hi, lo, v); - } - - return rem; -} - -/* - * Knuth, TAOCP Volume 2, 4.3.1: - * q, r := quotient and remainder of uconst (len nplusm) - * divided by vconst (len n) - * nplusm >= n - * - * If r is not NULL, r will contain the remainder. If r is NULL, the - * return value indicates if there is a remainder: 1 for true, 0 for - * false. A return value of -1 indicates an error. - */ -int -_mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r, - const mpd_uint_t *uconst, const mpd_uint_t *vconst, - mpd_size_t nplusm, mpd_size_t n) -{ - mpd_uint_t ustatic[MPD_MINALLOC_MAX]; - mpd_uint_t vstatic[MPD_MINALLOC_MAX]; - mpd_uint_t *u = ustatic; - mpd_uint_t *v = vstatic; - mpd_uint_t d, qhat, rhat, w2[2]; - mpd_uint_t hi, lo, x; - mpd_uint_t carry; - mpd_size_t i, j, m; - int retval = 0; - - assert(n > 1 && nplusm >= n); - m = sub_size_t(nplusm, n); - - /* D1: normalize */ - d = MPD_RADIX / (vconst[n-1] + 1); - - if (nplusm >= MPD_MINALLOC_MAX) { - if ((u = mpd_alloc(nplusm+1, sizeof *u)) == NULL) { - return -1; - } - } - if (n >= MPD_MINALLOC_MAX) { - if ((v = mpd_alloc(n+1, sizeof *v)) == NULL) { - mpd_free(u); - return -1; - } - } - - _mpd_shortmul(u, uconst, nplusm, d); - _mpd_shortmul(v, vconst, n, d); - - /* D2: loop */ - for (j=m; j != MPD_SIZE_MAX; j--) { - assert(2 <= j+n && j+n <= nplusm); /* annotation for scan-build */ - - /* D3: calculate qhat and rhat */ - rhat = _mpd_shortdiv(w2, u+j+n-1, 2, v[n-1]); - qhat = w2[1] * MPD_RADIX + w2[0]; - - while (1) { - if (qhat < MPD_RADIX) { - _mpd_singlemul(w2, qhat, v[n-2]); - if (w2[1] <= rhat) { - if (w2[1] != rhat || w2[0] <= u[j+n-2]) { - break; - } - } - } - qhat -= 1; - rhat += v[n-1]; - if (rhat < v[n-1] || rhat >= MPD_RADIX) { - break; - } - } - /* D4: multiply and subtract */ - carry = 0; - for (i=0; i <= n; i++) { - - _mpd_mul_words(&hi, &lo, qhat, v[i]); - - lo = carry + lo; - if (lo < carry) hi++; - - _mpd_div_words_r(&hi, &lo, hi, lo); - - x = u[i+j] - lo; - carry = (u[i+j] < x); - u[i+j] = carry ? x+MPD_RADIX : x; - carry += hi; - } - q[j] = qhat; - /* D5: test remainder */ - if (carry) { - q[j] -= 1; - /* D6: add back */ - (void)_mpd_baseadd(u+j, u+j, v, n+1, n); - } - } - - /* D8: unnormalize */ - if (r != NULL) { - _mpd_shortdiv(r, u, n, d); - /* we are not interested in the return value here */ - retval = 0; - } - else { - retval = !_mpd_isallzero(u, n); - } - - -if (u != ustatic) mpd_free(u); -if (v != vstatic) mpd_free(v); -return retval; -} - -/* - * Left shift of src by 'shift' digits; src may equal dest. - * - * dest := area of n mpd_uint_t with space for srcdigits+shift digits. - * src := coefficient with length m. - * - * The case splits in the function are non-obvious. The following - * equations might help: - * - * Let msdigits denote the number of digits in the most significant - * word of src. Then 1 <= msdigits <= rdigits. - * - * 1) shift = q * rdigits + r - * 2) srcdigits = qsrc * rdigits + msdigits - * 3) destdigits = shift + srcdigits - * = q * rdigits + r + qsrc * rdigits + msdigits - * = q * rdigits + (qsrc * rdigits + (r + msdigits)) - * - * The result has q zero words, followed by the coefficient that - * is left-shifted by r. The case r == 0 is trivial. For r > 0, it - * is important to keep in mind that we always read m source words, - * but write m+1 destination words if r + msdigits > rdigits, m words - * otherwise. - */ -void -_mpd_baseshiftl(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t n, mpd_size_t m, - mpd_size_t shift) -{ -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) - /* spurious uninitialized warnings */ - mpd_uint_t l=l, lprev=lprev, h=h; -#else - mpd_uint_t l, lprev, h; -#endif - mpd_uint_t q, r; - mpd_uint_t ph; - - assert(m > 0 && n >= m); - - _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS); - - if (r != 0) { - - ph = mpd_pow10[r]; - - --m; --n; - _mpd_divmod_pow10(&h, &lprev, src[m--], MPD_RDIGITS-r); - if (h != 0) { /* r + msdigits > rdigits <==> h != 0 */ - dest[n--] = h; - } - /* write m-1 shifted words */ - for (; m != MPD_SIZE_MAX; m--,n--) { - _mpd_divmod_pow10(&h, &l, src[m], MPD_RDIGITS-r); - dest[n] = ph * lprev + h; - lprev = l; - } - /* write least significant word */ - dest[q] = ph * lprev; - } - else { - while (--m != MPD_SIZE_MAX) { - dest[m+q] = src[m]; - } - } - - mpd_uint_zero(dest, q); -} - -/* - * Right shift of src by 'shift' digits; src may equal dest. - * Assumption: srcdigits-shift > 0. - * - * dest := area with space for srcdigits-shift digits. - * src := coefficient with length 'slen'. - * - * The case splits in the function rely on the following equations: - * - * Let msdigits denote the number of digits in the most significant - * word of src. Then 1 <= msdigits <= rdigits. - * - * 1) shift = q * rdigits + r - * 2) srcdigits = qsrc * rdigits + msdigits - * 3) destdigits = srcdigits - shift - * = qsrc * rdigits + msdigits - (q * rdigits + r) - * = (qsrc - q) * rdigits + msdigits - r - * - * Since destdigits > 0 and 1 <= msdigits <= rdigits: - * - * 4) qsrc >= q - * 5) qsrc == q ==> msdigits > r - * - * The result has slen-q words if msdigits > r, slen-q-1 words otherwise. - */ -mpd_uint_t -_mpd_baseshiftr(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t slen, - mpd_size_t shift) -{ -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) - /* spurious uninitialized warnings */ - mpd_uint_t l=l, h=h, hprev=hprev; /* low, high, previous high */ -#else - mpd_uint_t l, h, hprev; /* low, high, previous high */ -#endif - mpd_uint_t rnd, rest; /* rounding digit, rest */ - mpd_uint_t q, r; - mpd_size_t i, j; - mpd_uint_t ph; - - assert(slen > 0); - - _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS); - - rnd = rest = 0; - if (r != 0) { - - ph = mpd_pow10[MPD_RDIGITS-r]; - - _mpd_divmod_pow10(&hprev, &rest, src[q], r); - _mpd_divmod_pow10(&rnd, &rest, rest, r-1); - - if (rest == 0 && q > 0) { - rest = !_mpd_isallzero(src, q); - } - /* write slen-q-1 words */ - for (j=0,i=q+1; i<slen; i++,j++) { - _mpd_divmod_pow10(&h, &l, src[i], r); - dest[j] = ph * l + hprev; - hprev = h; - } - /* write most significant word */ - if (hprev != 0) { /* always the case if slen==q-1 */ - dest[j] = hprev; - } - } - else { - if (q > 0) { - _mpd_divmod_pow10(&rnd, &rest, src[q-1], MPD_RDIGITS-1); - /* is there any non-zero digit below rnd? */ - if (rest == 0) rest = !_mpd_isallzero(src, q-1); - } - for (j = 0; j < slen-q; j++) { - dest[j] = src[q+j]; - } - } - - /* 0-4 ==> rnd+rest < 0.5 */ - /* 5 ==> rnd+rest == 0.5 */ - /* 6-9 ==> rnd+rest > 0.5 */ - return (rnd == 0 || rnd == 5) ? rnd + !!rest : rnd; -} - - -/*********************************************************************/ -/* Calculations in base b */ -/*********************************************************************/ - -/* - * Add v to w (len m). The calling function has to handle a possible - * final carry. Assumption: m > 0. - */ -mpd_uint_t -_mpd_shortadd_b(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v, mpd_uint_t b) -{ - mpd_uint_t s; - mpd_uint_t carry; - mpd_size_t i; - - assert(m > 0); - - /* add v to w */ - s = w[0] + v; - carry = (s < v) | (s >= b); - w[0] = carry ? s-b : s; - - /* if there is a carry, propagate it */ - for (i = 1; carry && i < m; i++) { - s = w[i] + carry; - carry = (s == b); - w[i] = carry ? 0 : s; - } - - return carry; -} - -/* w := product of u (len n) and v (single word). Return carry. */ -mpd_uint_t -_mpd_shortmul_c(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v) -{ - mpd_uint_t hi, lo; - mpd_uint_t carry = 0; - mpd_size_t i; - - assert(n > 0); - - for (i=0; i < n; i++) { - - _mpd_mul_words(&hi, &lo, u[i], v); - lo = carry + lo; - if (lo < carry) hi++; - - _mpd_div_words_r(&carry, &w[i], hi, lo); - } - - return carry; -} - -/* w := product of u (len n) and v (single word) */ -mpd_uint_t -_mpd_shortmul_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, - mpd_uint_t v, mpd_uint_t b) -{ - mpd_uint_t hi, lo; - mpd_uint_t carry = 0; - mpd_size_t i; - - assert(n > 0); - - for (i=0; i < n; i++) { - - _mpd_mul_words(&hi, &lo, u[i], v); - lo = carry + lo; - if (lo < carry) hi++; - - _mpd_div_words(&carry, &w[i], hi, lo, b); - } - - return carry; -} - -/* - * Knuth, TAOCP Volume 2, 4.3.1, exercise 16: - * w := quotient of u (len n) divided by a single word v - */ -mpd_uint_t -_mpd_shortdiv_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, - mpd_uint_t v, mpd_uint_t b) -{ - mpd_uint_t hi, lo; - mpd_uint_t rem = 0; - mpd_size_t i; - - assert(n > 0); - - for (i=n-1; i != MPD_SIZE_MAX; i--) { - - _mpd_mul_words(&hi, &lo, rem, b); - lo = u[i] + lo; - if (lo < u[i]) hi++; - - _mpd_div_words(&w[i], &rem, hi, lo, v); - } - - return rem; -} diff --git a/Modules/_decimal/libmpdec/basearith.h b/Modules/_decimal/libmpdec/basearith.h deleted file mode 100644 index d35925aaddb48e..00000000000000 --- a/Modules/_decimal/libmpdec/basearith.h +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_BASEARITH_H_ -#define LIBMPDEC_BASEARITH_H_ - - -#include "mpdecimal.h" -#include "typearith.h" - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -mpd_uint_t _mpd_baseadd(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, - mpd_size_t m, mpd_size_t n); -void _mpd_baseaddto(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n); -mpd_uint_t _mpd_shortadd(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v); -mpd_uint_t _mpd_shortadd_b(mpd_uint_t *w, mpd_size_t m, mpd_uint_t v, - mpd_uint_t b); -mpd_uint_t _mpd_baseincr(mpd_uint_t *u, mpd_size_t n); -void _mpd_basesub(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, - mpd_size_t m, mpd_size_t n); -void _mpd_basesubfrom(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n); -void _mpd_basemul(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, - mpd_size_t m, mpd_size_t n); -void _mpd_shortmul(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, - mpd_uint_t v); -mpd_uint_t _mpd_shortmul_c(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, - mpd_uint_t v); -mpd_uint_t _mpd_shortmul_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, - mpd_uint_t v, mpd_uint_t b); -mpd_uint_t _mpd_shortdiv(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, - mpd_uint_t v); -mpd_uint_t _mpd_shortdiv_b(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, - mpd_uint_t v, mpd_uint_t b); -int _mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r, const mpd_uint_t *uconst, - const mpd_uint_t *vconst, mpd_size_t nplusm, mpd_size_t n); -void _mpd_baseshiftl(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t n, - mpd_size_t m, mpd_size_t shift); -mpd_uint_t _mpd_baseshiftr(mpd_uint_t *dest, mpd_uint_t *src, mpd_size_t slen, - mpd_size_t shift); - - - -#ifdef CONFIG_64 -extern const mpd_uint_t mprime_rdx; - -/* - * Algorithm from: Division by Invariant Integers using Multiplication, - * T. Granlund and P. L. Montgomery, Proceedings of the SIGPLAN '94 - * Conference on Programming Language Design and Implementation. - * - * http://gmplib.org/~tege/divcnst-pldi94.pdf - * - * Variables from the paper and their translations (See section 8): - * - * N := 64 - * d := MPD_RADIX - * l := 64 - * m' := floor((2**(64+64) - 1)/MPD_RADIX) - 2**64 - * - * Since N-l == 0: - * - * dnorm := d - * n2 := hi - * n10 := lo - * - * ACL2 proof: mpd-div-words-r-correct - */ -static inline void -_mpd_div_words_r(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo) -{ - mpd_uint_t n_adj, h, l, t; - mpd_uint_t n1_neg; - - /* n1_neg = if lo >= 2**63 then MPD_UINT_MAX else 0 */ - n1_neg = (lo & (1ULL<<63)) ? MPD_UINT_MAX : 0; - /* n_adj = if lo >= 2**63 then lo+MPD_RADIX else lo */ - n_adj = lo + (n1_neg & MPD_RADIX); - - /* (h, l) = if lo >= 2**63 then m'*(hi+1) else m'*hi */ - _mpd_mul_words(&h, &l, mprime_rdx, hi-n1_neg); - l = l + n_adj; - if (l < n_adj) h++; - t = h + hi; - /* At this point t == qest, with q == qest or q == qest+1: - * 1) 0 <= 2**64*hi + lo - qest*MPD_RADIX < 2*MPD_RADIX - */ - - /* t = 2**64-1 - qest = 2**64 - (qest+1) */ - t = MPD_UINT_MAX - t; - - /* (h, l) = 2**64*MPD_RADIX - (qest+1)*MPD_RADIX */ - _mpd_mul_words(&h, &l, t, MPD_RADIX); - l = l + lo; - if (l < lo) h++; - h += hi; - h -= MPD_RADIX; - /* (h, l) = 2**64*hi + lo - (qest+1)*MPD_RADIX (mod 2**128) - * Case q == qest+1: - * a) h == 0, l == r - * b) q := h - t == qest+1 - * c) r := l - * Case q == qest: - * a) h == MPD_UINT_MAX, l == 2**64-(MPD_RADIX-r) - * b) q := h - t == qest - * c) r := l + MPD_RADIX = r - */ - - *q = (h - t); - *r = l + (MPD_RADIX & h); -} -#else -static inline void -_mpd_div_words_r(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo) -{ - _mpd_div_words(q, r, hi, lo, MPD_RADIX); -} -#endif - - -/* Multiply two single base MPD_RADIX words, store result in array w[2]. */ -static inline void -_mpd_singlemul(mpd_uint_t w[2], mpd_uint_t u, mpd_uint_t v) -{ - mpd_uint_t hi, lo; - - _mpd_mul_words(&hi, &lo, u, v); - _mpd_div_words_r(&w[1], &w[0], hi, lo); -} - -/* Multiply u (len 2) and v (len m, 1 <= m <= 2). */ -static inline void -_mpd_mul_2_le2(mpd_uint_t w[4], mpd_uint_t u[2], mpd_uint_t v[2], mpd_ssize_t m) -{ - mpd_uint_t hi, lo; - - _mpd_mul_words(&hi, &lo, u[0], v[0]); - _mpd_div_words_r(&w[1], &w[0], hi, lo); - - _mpd_mul_words(&hi, &lo, u[1], v[0]); - lo = w[1] + lo; - if (lo < w[1]) hi++; - _mpd_div_words_r(&w[2], &w[1], hi, lo); - if (m == 1) return; - - _mpd_mul_words(&hi, &lo, u[0], v[1]); - lo = w[1] + lo; - if (lo < w[1]) hi++; - _mpd_div_words_r(&w[3], &w[1], hi, lo); - - _mpd_mul_words(&hi, &lo, u[1], v[1]); - lo = w[2] + lo; - if (lo < w[2]) hi++; - lo = w[3] + lo; - if (lo < w[3]) hi++; - _mpd_div_words_r(&w[3], &w[2], hi, lo); -} - - -/* - * Test if all words from data[len-1] to data[0] are zero. If len is 0, nothing - * is tested and the coefficient is regarded as "all zero". - */ -static inline int -_mpd_isallzero(const mpd_uint_t *data, mpd_ssize_t len) -{ - while (--len >= 0) { - if (data[len] != 0) return 0; - } - return 1; -} - -/* - * Test if all full words from data[len-1] to data[0] are MPD_RADIX-1 - * (all nines). Return true if len == 0. - */ -static inline int -_mpd_isallnine(const mpd_uint_t *data, mpd_ssize_t len) -{ - while (--len >= 0) { - if (data[len] != MPD_RADIX-1) return 0; - } - return 1; -} - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_BASEARITH_H_ */ diff --git a/Modules/_decimal/libmpdec/bench.c b/Modules/_decimal/libmpdec/bench.c deleted file mode 100644 index 09138f4ce9c03a..00000000000000 --- a/Modules/_decimal/libmpdec/bench.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> - - -static void -err_exit(const char *msg) -{ - fprintf(stderr, "%s\n", msg); - exit(1); -} - -static mpd_t * -new_mpd(void) -{ - mpd_t *x = mpd_qnew(); - if (x == NULL) { - err_exit("out of memory"); - } - - return x; -} - -/* Nonsense version of escape-time algorithm for calculating a mandelbrot - * set. Just for benchmarking. */ -static void -color_point(mpd_t *x0, mpd_t *y0, long maxiter, mpd_context_t *ctx) -{ - mpd_t *x, *y, *sq_x, *sq_y; - mpd_t *two; - - x = new_mpd(); - y = new_mpd(); - mpd_set_u32(x, 0, ctx); - mpd_set_u32(y, 0, ctx); - - sq_x = new_mpd(); - sq_y = new_mpd(); - mpd_set_u32(sq_x, 0, ctx); - mpd_set_u32(sq_y, 0, ctx); - - two = new_mpd(); - mpd_set_u32(two, 2, ctx); - - for (long i = 0; i < maxiter; i++) { - mpd_mul(y, x, y, ctx); - mpd_mul(y, y, two, ctx); - mpd_add(y, y, y0, ctx); - - mpd_sub(x, sq_x, sq_y, ctx); - mpd_add(x, x, x0, ctx); - - mpd_mul(sq_x, x, x, ctx); - mpd_mul(sq_y, y, y, ctx); - } - - mpd_copy(x0, x, ctx); - - mpd_del(two); - mpd_del(sq_y); - mpd_del(sq_x); - mpd_del(y); - mpd_del(x); -} - - -int -main(int argc, char **argv) -{ - mpd_context_t ctx; - mpd_t *x0, *y0; - uint32_t prec = 19; - long iter = 10000000; - clock_t start_clock, end_clock; - - if (argc != 3) { - err_exit("usage: bench prec iter\n"); - } - prec = strtoul(argv[1], NULL, 10); - iter = strtol(argv[2], NULL, 10); - - mpd_init(&ctx, prec); - /* no more MPD_MINALLOC changes after here */ - - x0 = new_mpd(); - y0 = new_mpd(); - mpd_set_string(x0, "0.222", &ctx); - mpd_set_string(y0, "0.333", &ctx); - if (ctx.status & MPD_Errors) { - mpd_del(y0); - mpd_del(x0); - err_exit("unexpected error during conversion"); - } - - start_clock = clock(); - color_point(x0, y0, iter, &ctx); - end_clock = clock(); - - mpd_print(x0); - fprintf(stderr, "time: %f\n\n", (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); - - mpd_del(y0); - mpd_del(x0); - - return 0; -} diff --git a/Modules/_decimal/libmpdec/bench_full.c b/Modules/_decimal/libmpdec/bench_full.c deleted file mode 100644 index 6ab73917e1c32c..00000000000000 --- a/Modules/_decimal/libmpdec/bench_full.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> - - -static void -err_exit(const char *msg) -{ - fprintf(stderr, "%s\n", msg); - exit(1); -} - -static mpd_t * -new_mpd(void) -{ - mpd_t *x = mpd_qnew(); - if (x == NULL) { - err_exit("out of memory"); - } - - return x; -} - -/* - * Example from: http://en.wikipedia.org/wiki/Mandelbrot_set - * - * Escape time algorithm for drawing the set: - * - * Point x0, y0 is deemed to be in the Mandelbrot set if the return - * value is maxiter. Lower return values indicate how quickly points - * escaped and can be used for coloring. - */ -static int -color_point(const mpd_t *x0, const mpd_t *y0, const long maxiter, mpd_context_t *ctx) -{ - mpd_t *x, *y, *sq_x, *sq_y; - mpd_t *two, *four, *c; - long i; - - x = new_mpd(); - y = new_mpd(); - mpd_set_u32(x, 0, ctx); - mpd_set_u32(y, 0, ctx); - - sq_x = new_mpd(); - sq_y = new_mpd(); - mpd_set_u32(sq_x, 0, ctx); - mpd_set_u32(sq_y, 0, ctx); - - two = new_mpd(); - four = new_mpd(); - mpd_set_u32(two, 2, ctx); - mpd_set_u32(four, 4, ctx); - - c = new_mpd(); - mpd_set_u32(c, 0, ctx); - - for (i = 0; i < maxiter && mpd_cmp(c, four, ctx) <= 0; i++) { - mpd_mul(y, x, y, ctx); - mpd_mul(y, y, two, ctx); - mpd_add(y, y, y0, ctx); - - mpd_sub(x, sq_x, sq_y, ctx); - mpd_add(x, x, x0, ctx); - - mpd_mul(sq_x, x, x, ctx); - mpd_mul(sq_y, y, y, ctx); - mpd_add(c, sq_x, sq_y, ctx); - } - - mpd_del(c); - mpd_del(four); - mpd_del(two); - mpd_del(sq_y); - mpd_del(sq_x); - mpd_del(y); - mpd_del(x); - - return i; -} - -int -main(int argc, char **argv) -{ - mpd_context_t ctx; - mpd_t *x0, *y0; - mpd_t *sqrt_2, *xstep, *ystep; - mpd_ssize_t prec = 19; - - long iter = 1000; - int points[40][80]; - int i, j; - clock_t start_clock, end_clock; - - - if (argc != 3) { - fprintf(stderr, "usage: ./bench prec iter\n"); - exit(1); - } - prec = strtoll(argv[1], NULL, 10); - iter = strtol(argv[2], NULL, 10); - - mpd_init(&ctx, prec); - /* no more MPD_MINALLOC changes after here */ - - sqrt_2 = new_mpd(); - xstep = new_mpd(); - ystep = new_mpd(); - x0 = new_mpd(); - y0 = new_mpd(); - - mpd_set_u32(sqrt_2, 2, &ctx); - mpd_sqrt(sqrt_2, sqrt_2, &ctx); - mpd_div_u32(xstep, sqrt_2, 40, &ctx); - mpd_div_u32(ystep, sqrt_2, 20, &ctx); - - start_clock = clock(); - mpd_copy(y0, sqrt_2, &ctx); - for (i = 0; i < 40; i++) { - mpd_copy(x0, sqrt_2, &ctx); - mpd_set_negative(x0); - for (j = 0; j < 80; j++) { - points[i][j] = color_point(x0, y0, iter, &ctx); - mpd_add(x0, x0, xstep, &ctx); - } - mpd_sub(y0, y0, ystep, &ctx); - } - end_clock = clock(); - -#ifdef BENCH_VERBOSE - for (i = 0; i < 40; i++) { - for (j = 0; j < 80; j++) { - if (points[i][j] == iter) { - putchar('*'); - } - else if (points[i][j] >= 10) { - putchar('+'); - } - else if (points[i][j] >= 5) { - putchar('.'); - } - else { - putchar(' '); - } - } - putchar('\n'); - } - putchar('\n'); -#else - (void)points; /* suppress gcc warning */ -#endif - - printf("time: %f\n\n", (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); - - mpd_del(y0); - mpd_del(x0); - mpd_del(ystep); - mpd_del(xstep); - mpd_del(sqrt_2); - - return 0; -} diff --git a/Modules/_decimal/libmpdec/bits.h b/Modules/_decimal/libmpdec/bits.h deleted file mode 100644 index aa9c3e77980c03..00000000000000 --- a/Modules/_decimal/libmpdec/bits.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_BITS_H_ -#define LIBMPDEC_BITS_H_ - - -#include "mpdecimal.h" - - -/* Check if n is a power of 2. */ -static inline int -ispower2(mpd_size_t n) -{ - return n != 0 && (n & (n-1)) == 0; -} - -#if defined(ANSI) -/* - * Return the most significant bit position of n from 0 to 31 (63). - * Assumptions: n != 0. - */ -static inline int -mpd_bsr(mpd_size_t n) -{ - int pos = 0; - mpd_size_t tmp; - -#ifdef CONFIG_64 - tmp = n >> 32; - if (tmp != 0) { n = tmp; pos += 32; } -#endif - tmp = n >> 16; - if (tmp != 0) { n = tmp; pos += 16; } - tmp = n >> 8; - if (tmp != 0) { n = tmp; pos += 8; } - tmp = n >> 4; - if (tmp != 0) { n = tmp; pos += 4; } - tmp = n >> 2; - if (tmp != 0) { n = tmp; pos += 2; } - tmp = n >> 1; - if (tmp != 0) { n = tmp; pos += 1; } - - return pos + (int)n - 1; -} - -/* - * Return the least significant bit position of n from 0 to 31 (63). - * Assumptions: n != 0. - */ -static inline int -mpd_bsf(mpd_size_t n) -{ - int pos; - -#ifdef CONFIG_64 - pos = 63; - if (n & 0x00000000FFFFFFFFULL) { pos -= 32; } else { n >>= 32; } - if (n & 0x000000000000FFFFULL) { pos -= 16; } else { n >>= 16; } - if (n & 0x00000000000000FFULL) { pos -= 8; } else { n >>= 8; } - if (n & 0x000000000000000FULL) { pos -= 4; } else { n >>= 4; } - if (n & 0x0000000000000003ULL) { pos -= 2; } else { n >>= 2; } - if (n & 0x0000000000000001ULL) { pos -= 1; } -#else - pos = 31; - if (n & 0x000000000000FFFFUL) { pos -= 16; } else { n >>= 16; } - if (n & 0x00000000000000FFUL) { pos -= 8; } else { n >>= 8; } - if (n & 0x000000000000000FUL) { pos -= 4; } else { n >>= 4; } - if (n & 0x0000000000000003UL) { pos -= 2; } else { n >>= 2; } - if (n & 0x0000000000000001UL) { pos -= 1; } -#endif - return pos; -} -/* END ANSI */ - -#elif defined(ASM) -/* - * Bit scan reverse. Assumptions: a != 0. - */ -static inline int -mpd_bsr(mpd_size_t a) -{ - mpd_size_t retval; - - __asm__ ( -#ifdef CONFIG_64 - "bsrq %1, %0\n\t" -#else - "bsr %1, %0\n\t" -#endif - :"=r" (retval) - :"r" (a) - :"cc" - ); - - return (int)retval; -} - -/* - * Bit scan forward. Assumptions: a != 0. - */ -static inline int -mpd_bsf(mpd_size_t a) -{ - mpd_size_t retval; - - __asm__ ( -#ifdef CONFIG_64 - "bsfq %1, %0\n\t" -#else - "bsf %1, %0\n\t" -#endif - :"=r" (retval) - :"r" (a) - :"cc" - ); - - return (int)retval; -} -/* END ASM */ - -#elif defined(MASM) -#include <intrin.h> -/* - * Bit scan reverse. Assumptions: a != 0. - */ -static inline int __cdecl -mpd_bsr(mpd_size_t a) -{ - unsigned long retval; - -#ifdef CONFIG_64 - _BitScanReverse64(&retval, a); -#else - _BitScanReverse(&retval, a); -#endif - - return (int)retval; -} - -/* - * Bit scan forward. Assumptions: a != 0. - */ -static inline int __cdecl -mpd_bsf(mpd_size_t a) -{ - unsigned long retval; - -#ifdef CONFIG_64 - _BitScanForward64(&retval, a); -#else - _BitScanForward(&retval, a); -#endif - - return (int)retval; -} -/* END MASM (_MSC_VER) */ -#else - #error "missing preprocessor definitions" -#endif /* BSR/BSF */ - - -#endif /* LIBMPDEC_BITS_H_ */ diff --git a/Modules/_decimal/libmpdec/constants.c b/Modules/_decimal/libmpdec/constants.c deleted file mode 100644 index ed074fa81c6d2e..00000000000000 --- a/Modules/_decimal/libmpdec/constants.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" -#include "basearith.h" -#include "constants.h" - - -#if defined(CONFIG_64) - - /* number-theory.c */ - const mpd_uint_t mpd_moduli[3] = { - 18446744069414584321ULL, 18446744056529682433ULL, 18446742974197923841ULL - }; - const mpd_uint_t mpd_roots[3] = {7ULL, 10ULL, 19ULL}; - - /* crt.c */ - const mpd_uint_t INV_P1_MOD_P2 = 18446744055098026669ULL; - const mpd_uint_t INV_P1P2_MOD_P3 = 287064143708160ULL; - const mpd_uint_t LH_P1P2 = 18446744052234715137ULL; /* (P1*P2) % 2^64 */ - const mpd_uint_t UH_P1P2 = 18446744052234715141ULL; /* (P1*P2) / 2^64 */ - - /* transpose.c */ - const mpd_size_t mpd_bits[64] = { - 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, - 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, - 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, - 2147483648ULL, 4294967296ULL, 8589934592ULL, 17179869184ULL, 34359738368ULL, - 68719476736ULL, 137438953472ULL, 274877906944ULL, 549755813888ULL, - 1099511627776ULL, 2199023255552ULL, 4398046511104, 8796093022208ULL, - 17592186044416ULL, 35184372088832ULL, 70368744177664ULL, 140737488355328ULL, - 281474976710656ULL, 562949953421312ULL, 1125899906842624ULL, - 2251799813685248ULL, 4503599627370496ULL, 9007199254740992ULL, - 18014398509481984ULL, 36028797018963968ULL, 72057594037927936ULL, - 144115188075855872ULL, 288230376151711744ULL, 576460752303423488ULL, - 1152921504606846976ULL, 2305843009213693952ULL, 4611686018427387904ULL, - 9223372036854775808ULL - }; - - /* mpdecimal.c */ - const mpd_uint_t mpd_pow10[MPD_RDIGITS+1] = { - 1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000, - 10000000000ULL,100000000000ULL,1000000000000ULL,10000000000000ULL, - 100000000000000ULL,1000000000000000ULL,10000000000000000ULL, - 100000000000000000ULL,1000000000000000000ULL,10000000000000000000ULL - }; - - /* magic number for constant division by MPD_RADIX */ - const mpd_uint_t mprime_rdx = 15581492618384294730ULL; - -#elif defined(CONFIG_32) - - /* number-theory.c */ - const mpd_uint_t mpd_moduli[3] = {2113929217UL, 2013265921UL, 1811939329UL}; - const mpd_uint_t mpd_roots[3] = {5UL, 31UL, 13UL}; - - /* PentiumPro modular multiplication: These constants have to be loaded as - * 80 bit long doubles, which are not supported by certain compilers. */ - const uint32_t mpd_invmoduli[3][3] = { - {4293885170U, 2181570688U, 16352U}, /* ((long double) 1 / 2113929217UL) */ - {1698898177U, 2290649223U, 16352U}, /* ((long double) 1 / 2013265921UL) */ - {2716021846U, 2545165803U, 16352U} /* ((long double) 1 / 1811939329UL) */ - }; - - const float MPD_TWO63 = 9223372036854775808.0; /* 2^63 */ - - /* crt.c */ - const mpd_uint_t INV_P1_MOD_P2 = 2013265901UL; - const mpd_uint_t INV_P1P2_MOD_P3 = 54UL; - const mpd_uint_t LH_P1P2 = 4127195137UL; /* (P1*P2) % 2^32 */ - const mpd_uint_t UH_P1P2 = 990904320UL; /* (P1*P2) / 2^32 */ - - /* transpose.c */ - const mpd_size_t mpd_bits[32] = { - 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, - 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, - 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, - 2147483648UL - }; - - /* mpdecimal.c */ - const mpd_uint_t mpd_pow10[MPD_RDIGITS+1] = { - 1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000 - }; - -#else - #error "CONFIG_64 or CONFIG_32 must be defined." -#endif - -const char * const mpd_round_string[MPD_ROUND_GUARD] = { - "ROUND_UP", /* round away from 0 */ - "ROUND_DOWN", /* round toward 0 (truncate) */ - "ROUND_CEILING", /* round toward +infinity */ - "ROUND_FLOOR", /* round toward -infinity */ - "ROUND_HALF_UP", /* 0.5 is rounded up */ - "ROUND_HALF_DOWN", /* 0.5 is rounded down */ - "ROUND_HALF_EVEN", /* 0.5 is rounded to even */ - "ROUND_05UP", /* round zero or five away from 0 */ - "ROUND_TRUNC", /* truncate, but set infinity */ -}; - -const char * const mpd_clamp_string[MPD_CLAMP_GUARD] = { - "CLAMP_DEFAULT", - "CLAMP_IEEE_754" -}; diff --git a/Modules/_decimal/libmpdec/constants.h b/Modules/_decimal/libmpdec/constants.h deleted file mode 100644 index 7c1db839c20ba2..00000000000000 --- a/Modules/_decimal/libmpdec/constants.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_CONSTANTS_H_ -#define LIBMPDEC_CONSTANTS_H_ - - -#include "mpdecimal.h" - -#include <stdint.h> - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -/* choice of optimized functions */ -#if defined(CONFIG_64) -/* x64 */ - #define MULMOD(a, b) x64_mulmod(a, b, umod) - #define MULMOD2C(a0, a1, w) x64_mulmod2c(a0, a1, w, umod) - #define MULMOD2(a0, b0, a1, b1) x64_mulmod2(a0, b0, a1, b1, umod) - #define POWMOD(base, exp) x64_powmod(base, exp, umod) - #define SETMODULUS(modnum) std_setmodulus(modnum, &umod) - #define SIZE3_NTT(x0, x1, x2, w3table) std_size3_ntt(x0, x1, x2, w3table, umod) -#elif defined(PPRO) -/* PentiumPro (or later) gcc inline asm */ - #define MULMOD(a, b) ppro_mulmod(a, b, &dmod, dinvmod) - #define MULMOD2C(a0, a1, w) ppro_mulmod2c(a0, a1, w, &dmod, dinvmod) - #define MULMOD2(a0, b0, a1, b1) ppro_mulmod2(a0, b0, a1, b1, &dmod, dinvmod) - #define POWMOD(base, exp) ppro_powmod(base, exp, &dmod, dinvmod) - #define SETMODULUS(modnum) ppro_setmodulus(modnum, &umod, &dmod, dinvmod) - #define SIZE3_NTT(x0, x1, x2, w3table) ppro_size3_ntt(x0, x1, x2, w3table, umod, &dmod, dinvmod) -#else - /* ANSI C99 */ - #define MULMOD(a, b) std_mulmod(a, b, umod) - #define MULMOD2C(a0, a1, w) std_mulmod2c(a0, a1, w, umod) - #define MULMOD2(a0, b0, a1, b1) std_mulmod2(a0, b0, a1, b1, umod) - #define POWMOD(base, exp) std_powmod(base, exp, umod) - #define SETMODULUS(modnum) std_setmodulus(modnum, &umod) - #define SIZE3_NTT(x0, x1, x2, w3table) std_size3_ntt(x0, x1, x2, w3table, umod) -#endif - -/* PentiumPro (or later) gcc inline asm */ -extern const float MPD_TWO63; -extern const uint32_t mpd_invmoduli[3][3]; - -enum {P1, P2, P3}; - -extern const mpd_uint_t mpd_moduli[]; -extern const mpd_uint_t mpd_roots[]; -extern const mpd_size_t mpd_bits[]; -extern const mpd_uint_t mpd_pow10[]; - -extern const mpd_uint_t INV_P1_MOD_P2; -extern const mpd_uint_t INV_P1P2_MOD_P3; -extern const mpd_uint_t LH_P1P2; -extern const mpd_uint_t UH_P1P2; - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_CONSTANTS_H_ */ diff --git a/Modules/_decimal/libmpdec/context.c b/Modules/_decimal/libmpdec/context.c deleted file mode 100644 index 172794b67d8009..00000000000000 --- a/Modules/_decimal/libmpdec/context.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <signal.h> -#include <stdio.h> -#include <string.h> - - -void -mpd_dflt_traphandler(mpd_context_t *ctx) -{ - (void)ctx; - raise(SIGFPE); -} - -void (* mpd_traphandler)(mpd_context_t *) = mpd_dflt_traphandler; - - -/* Set guaranteed minimum number of coefficient words. The function may - be used once at program start. Setting MPD_MINALLOC to out-of-bounds - values is a catastrophic error, so in that case the function exits rather - than relying on the user to check a return value. */ -void -mpd_setminalloc(mpd_ssize_t n) -{ - static int minalloc_is_set = 0; - - if (minalloc_is_set) { - mpd_err_warn("mpd_setminalloc: ignoring request to set " - "MPD_MINALLOC a second time\n"); - return; - } - if (n < MPD_MINALLOC_MIN || n > MPD_MINALLOC_MAX) { - mpd_err_fatal("illegal value for MPD_MINALLOC"); /* GCOV_NOT_REACHED */ - } - MPD_MINALLOC = n; - minalloc_is_set = 1; -} - -void -mpd_init(mpd_context_t *ctx, mpd_ssize_t prec) -{ - mpd_ssize_t ideal_minalloc; - - mpd_defaultcontext(ctx); - - if (!mpd_qsetprec(ctx, prec)) { - mpd_addstatus_raise(ctx, MPD_Invalid_context); - return; - } - - ideal_minalloc = 2 * ((prec+MPD_RDIGITS-1) / MPD_RDIGITS); - if (ideal_minalloc < MPD_MINALLOC_MIN) ideal_minalloc = MPD_MINALLOC_MIN; - if (ideal_minalloc > MPD_MINALLOC_MAX) ideal_minalloc = MPD_MINALLOC_MAX; - - mpd_setminalloc(ideal_minalloc); -} - -void -mpd_maxcontext(mpd_context_t *ctx) -{ - ctx->prec=MPD_MAX_PREC; - ctx->emax=MPD_MAX_EMAX; - ctx->emin=MPD_MIN_EMIN; - ctx->round=MPD_ROUND_HALF_EVEN; - ctx->traps=MPD_Traps; - ctx->status=0; - ctx->newtrap=0; - ctx->clamp=0; - ctx->allcr=1; -} - -void -mpd_defaultcontext(mpd_context_t *ctx) -{ - ctx->prec=2*MPD_RDIGITS; - ctx->emax=MPD_MAX_EMAX; - ctx->emin=MPD_MIN_EMIN; - ctx->round=MPD_ROUND_HALF_UP; - ctx->traps=MPD_Traps; - ctx->status=0; - ctx->newtrap=0; - ctx->clamp=0; - ctx->allcr=1; -} - -void -mpd_basiccontext(mpd_context_t *ctx) -{ - ctx->prec=9; - ctx->emax=MPD_MAX_EMAX; - ctx->emin=MPD_MIN_EMIN; - ctx->round=MPD_ROUND_HALF_UP; - ctx->traps=MPD_Traps|MPD_Clamped; - ctx->status=0; - ctx->newtrap=0; - ctx->clamp=0; - ctx->allcr=1; -} - -int -mpd_ieee_context(mpd_context_t *ctx, int bits) -{ - if (bits <= 0 || bits > MPD_IEEE_CONTEXT_MAX_BITS || bits % 32) { - return -1; - } - - ctx->prec = 9 * (bits/32) - 2; - ctx->emax = 3 * ((mpd_ssize_t)1<<(bits/16+3)); - ctx->emin = 1 - ctx->emax; - ctx->round=MPD_ROUND_HALF_EVEN; - ctx->traps=0; - ctx->status=0; - ctx->newtrap=0; - ctx->clamp=1; - ctx->allcr=1; - - return 0; -} - -mpd_ssize_t -mpd_getprec(const mpd_context_t *ctx) -{ - return ctx->prec; -} - -mpd_ssize_t -mpd_getemax(const mpd_context_t *ctx) -{ - return ctx->emax; -} - -mpd_ssize_t -mpd_getemin(const mpd_context_t *ctx) -{ - return ctx->emin; -} - -int -mpd_getround(const mpd_context_t *ctx) -{ - return ctx->round; -} - -uint32_t -mpd_gettraps(const mpd_context_t *ctx) -{ - return ctx->traps; -} - -uint32_t -mpd_getstatus(const mpd_context_t *ctx) -{ - return ctx->status; -} - -int -mpd_getclamp(const mpd_context_t *ctx) -{ - return ctx->clamp; -} - -int -mpd_getcr(const mpd_context_t *ctx) -{ - return ctx->allcr; -} - - -int -mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec) -{ - if (prec <= 0 || prec > MPD_MAX_PREC) { - return 0; - } - ctx->prec = prec; - return 1; -} - -int -mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax) -{ - if (emax < 0 || emax > MPD_MAX_EMAX) { - return 0; - } - ctx->emax = emax; - return 1; -} - -int -mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin) -{ - if (emin > 0 || emin < MPD_MIN_EMIN) { - return 0; - } - ctx->emin = emin; - return 1; -} - -int -mpd_qsetround(mpd_context_t *ctx, int round) -{ - if (!(0 <= round && round < MPD_ROUND_GUARD)) { - return 0; - } - ctx->round = round; - return 1; -} - -int -mpd_qsettraps(mpd_context_t *ctx, uint32_t flags) -{ - if (flags > MPD_Max_status) { - return 0; - } - ctx->traps = flags; - return 1; -} - -int -mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags) -{ - if (flags > MPD_Max_status) { - return 0; - } - ctx->status = flags; - return 1; -} - -int -mpd_qsetclamp(mpd_context_t *ctx, int c) -{ - if (c != 0 && c != 1) { - return 0; - } - ctx->clamp = c; - return 1; -} - -int -mpd_qsetcr(mpd_context_t *ctx, int c) -{ - if (c != 0 && c != 1) { - return 0; - } - ctx->allcr = c; - return 1; -} - - -void -mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags) -{ - ctx->status |= flags; - if (flags&ctx->traps) { - ctx->newtrap = (flags&ctx->traps); - mpd_traphandler(ctx); - } -} diff --git a/Modules/_decimal/libmpdec/convolute.c b/Modules/_decimal/libmpdec/convolute.c deleted file mode 100644 index 4bc8e8b5fd32f4..00000000000000 --- a/Modules/_decimal/libmpdec/convolute.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" -#include "bits.h" -#include "constants.h" -#include "convolute.h" -#include "fnt.h" -#include "fourstep.h" -#include "numbertheory.h" -#include "sixstep.h" -#include "umodarith.h" - - -/* Bignum: Fast convolution using the Number Theoretic Transform. Used for - the multiplication of very large coefficients. */ - - -/* Convolute the data in c1 and c2. Result is in c1. */ -int -fnt_convolute(mpd_uint_t *c1, mpd_uint_t *c2, mpd_size_t n, int modnum) -{ - int (*fnt)(mpd_uint_t *, mpd_size_t, int); - int (*inv_fnt)(mpd_uint_t *, mpd_size_t, int); -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - mpd_uint_t n_inv, umod; - mpd_size_t i; - - - SETMODULUS(modnum); - n_inv = POWMOD(n, (umod-2)); - - if (ispower2(n)) { - if (n > SIX_STEP_THRESHOLD) { - fnt = six_step_fnt; - inv_fnt = inv_six_step_fnt; - } - else { - fnt = std_fnt; - inv_fnt = std_inv_fnt; - } - } - else { - fnt = four_step_fnt; - inv_fnt = inv_four_step_fnt; - } - - if (!fnt(c1, n, modnum)) { - return 0; - } - if (!fnt(c2, n, modnum)) { - return 0; - } - for (i = 0; i < n-1; i += 2) { - mpd_uint_t x0 = c1[i]; - mpd_uint_t y0 = c2[i]; - mpd_uint_t x1 = c1[i+1]; - mpd_uint_t y1 = c2[i+1]; - MULMOD2(&x0, y0, &x1, y1); - c1[i] = x0; - c1[i+1] = x1; - } - - if (!inv_fnt(c1, n, modnum)) { - return 0; - } - for (i = 0; i < n-3; i += 4) { - mpd_uint_t x0 = c1[i]; - mpd_uint_t x1 = c1[i+1]; - mpd_uint_t x2 = c1[i+2]; - mpd_uint_t x3 = c1[i+3]; - MULMOD2C(&x0, &x1, n_inv); - MULMOD2C(&x2, &x3, n_inv); - c1[i] = x0; - c1[i+1] = x1; - c1[i+2] = x2; - c1[i+3] = x3; - } - - return 1; -} - -/* Autoconvolute the data in c1. Result is in c1. */ -int -fnt_autoconvolute(mpd_uint_t *c1, mpd_size_t n, int modnum) -{ - int (*fnt)(mpd_uint_t *, mpd_size_t, int); - int (*inv_fnt)(mpd_uint_t *, mpd_size_t, int); -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - mpd_uint_t n_inv, umod; - mpd_size_t i; - - - SETMODULUS(modnum); - n_inv = POWMOD(n, (umod-2)); - - if (ispower2(n)) { - if (n > SIX_STEP_THRESHOLD) { - fnt = six_step_fnt; - inv_fnt = inv_six_step_fnt; - } - else { - fnt = std_fnt; - inv_fnt = std_inv_fnt; - } - } - else { - fnt = four_step_fnt; - inv_fnt = inv_four_step_fnt; - } - - if (!fnt(c1, n, modnum)) { - return 0; - } - for (i = 0; i < n-1; i += 2) { - mpd_uint_t x0 = c1[i]; - mpd_uint_t x1 = c1[i+1]; - MULMOD2(&x0, x0, &x1, x1); - c1[i] = x0; - c1[i+1] = x1; - } - - if (!inv_fnt(c1, n, modnum)) { - return 0; - } - for (i = 0; i < n-3; i += 4) { - mpd_uint_t x0 = c1[i]; - mpd_uint_t x1 = c1[i+1]; - mpd_uint_t x2 = c1[i+2]; - mpd_uint_t x3 = c1[i+3]; - MULMOD2C(&x0, &x1, n_inv); - MULMOD2C(&x2, &x3, n_inv); - c1[i] = x0; - c1[i+1] = x1; - c1[i+2] = x2; - c1[i+3] = x3; - } - - return 1; -} diff --git a/Modules/_decimal/libmpdec/convolute.h b/Modules/_decimal/libmpdec/convolute.h deleted file mode 100644 index 62edb3e45739cb..00000000000000 --- a/Modules/_decimal/libmpdec/convolute.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_CONVOLUTE_H_ -#define LIBMPDEC_CONVOLUTE_H_ - - -#include "mpdecimal.h" - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -#define SIX_STEP_THRESHOLD 4096 - -int fnt_convolute(mpd_uint_t *c1, mpd_uint_t *c2, mpd_size_t n, int modnum); -int fnt_autoconvolute(mpd_uint_t *c1, mpd_size_t n, int modnum); - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_CONVOLUTE_H_ */ diff --git a/Modules/_decimal/libmpdec/crt.c b/Modules/_decimal/libmpdec/crt.c deleted file mode 100644 index babcce41bf67cd..00000000000000 --- a/Modules/_decimal/libmpdec/crt.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> - -#include "constants.h" -#include "crt.h" -#include "numbertheory.h" -#include "typearith.h" -#include "umodarith.h" - - -/* Bignum: Chinese Remainder Theorem, extends the maximum transform length. */ - - -/* Multiply P1P2 by v, store result in w. */ -static inline void -_crt_mulP1P2_3(mpd_uint_t w[3], mpd_uint_t v) -{ - mpd_uint_t hi1, hi2, lo; - - _mpd_mul_words(&hi1, &lo, LH_P1P2, v); - w[0] = lo; - - _mpd_mul_words(&hi2, &lo, UH_P1P2, v); - lo = hi1 + lo; - if (lo < hi1) hi2++; - - w[1] = lo; - w[2] = hi2; -} - -/* Add 3 words from v to w. The result is known to fit in w. */ -static inline void -_crt_add3(mpd_uint_t w[3], mpd_uint_t v[3]) -{ - mpd_uint_t carry; - - w[0] = w[0] + v[0]; - carry = (w[0] < v[0]); - - w[1] = w[1] + v[1]; - if (w[1] < v[1]) w[2]++; - - w[1] = w[1] + carry; - if (w[1] < carry) w[2]++; - - w[2] += v[2]; -} - -/* Divide 3 words in u by v, store result in w, return remainder. */ -static inline mpd_uint_t -_crt_div3(mpd_uint_t *w, const mpd_uint_t *u, mpd_uint_t v) -{ - mpd_uint_t r1 = u[2]; - mpd_uint_t r2; - - if (r1 < v) { - w[2] = 0; - } - else { - _mpd_div_word(&w[2], &r1, u[2], v); /* GCOV_NOT_REACHED */ - } - - _mpd_div_words(&w[1], &r2, r1, u[1], v); - _mpd_div_words(&w[0], &r1, r2, u[0], v); - - return r1; -} - - -/* - * Chinese Remainder Theorem: - * Algorithm from Joerg Arndt, "Matters Computational", - * Chapter 37.4.1 [http://www.jjj.de/fxt/] - * - * See also Knuth, TAOCP, Volume 2, 4.3.2, exercise 7. - */ - -/* - * CRT with carry: x1, x2, x3 contain numbers modulo p1, p2, p3. For each - * triple of members of the arrays, find the unique z modulo p1*p2*p3, with - * zmax = p1*p2*p3 - 1. - * - * In each iteration of the loop, split z into result[i] = z % MPD_RADIX - * and carry = z / MPD_RADIX. Let N be the size of carry[] and cmax the - * maximum carry. - * - * Limits for the 32-bit build: - * - * N = 2**96 - * cmax = 7711435591312380274 - * - * Limits for the 64 bit build: - * - * N = 2**192 - * cmax = 627710135393475385904124401220046371710 - * - * The following statements hold for both versions: - * - * 1) cmax + zmax < N, so the addition does not overflow. - * - * 2) (cmax + zmax) / MPD_RADIX == cmax. - * - * 3) If c <= cmax, then c_next = (c + zmax) / MPD_RADIX <= cmax. - */ -void -crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t rsize) -{ - mpd_uint_t p1 = mpd_moduli[P1]; - mpd_uint_t umod; -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - mpd_uint_t a1, a2, a3; - mpd_uint_t s; - mpd_uint_t z[3], t[3]; - mpd_uint_t carry[3] = {0,0,0}; - mpd_uint_t hi, lo; - mpd_size_t i; - - for (i = 0; i < rsize; i++) { - - a1 = x1[i]; - a2 = x2[i]; - a3 = x3[i]; - - SETMODULUS(P2); - s = ext_submod(a2, a1, umod); - s = MULMOD(s, INV_P1_MOD_P2); - - _mpd_mul_words(&hi, &lo, s, p1); - lo = lo + a1; - if (lo < a1) hi++; - - SETMODULUS(P3); - s = dw_submod(a3, hi, lo, umod); - s = MULMOD(s, INV_P1P2_MOD_P3); - - z[0] = lo; - z[1] = hi; - z[2] = 0; - - _crt_mulP1P2_3(t, s); - _crt_add3(z, t); - _crt_add3(carry, z); - - x1[i] = _crt_div3(carry, carry, MPD_RADIX); - } - - assert(carry[0] == 0 && carry[1] == 0 && carry[2] == 0); -} diff --git a/Modules/_decimal/libmpdec/crt.h b/Modules/_decimal/libmpdec/crt.h deleted file mode 100644 index ed66753c2510ba..00000000000000 --- a/Modules/_decimal/libmpdec/crt.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_CRT_H_ -#define LIBMPDEC_CRT_H_ - - -#include "mpdecimal.h" - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -void crt3(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_size_t rsize); - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_CRT_H_ */ diff --git a/Modules/_decimal/libmpdec/difradix2.c b/Modules/_decimal/libmpdec/difradix2.c deleted file mode 100644 index 049ecff65b6eef..00000000000000 --- a/Modules/_decimal/libmpdec/difradix2.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> - -#include "bits.h" -#include "constants.h" -#include "difradix2.h" -#include "numbertheory.h" -#include "umodarith.h" - - -/* Bignum: The actual transform routine (decimation in frequency). */ - - -/* - * Generate index pairs (x, bitreverse(x)) and carry out the permutation. - * n must be a power of two. - * Algorithm due to Brent/Lehmann, see Joerg Arndt, "Matters Computational", - * Chapter 1.14.4. [http://www.jjj.de/fxt/] - */ -static inline void -bitreverse_permute(mpd_uint_t a[], mpd_size_t n) -{ - mpd_size_t x = 0; - mpd_size_t r = 0; - mpd_uint_t t; - - do { /* Invariant: r = bitreverse(x) */ - if (r > x) { - t = a[x]; - a[x] = a[r]; - a[r] = t; - } - /* Flip trailing consecutive 1 bits and the first zero bit - * that absorbs a possible carry. */ - x += 1; - /* Mirror the operation on r: Flip n_trailing_zeros(x)+1 - high bits of r. */ - r ^= (n - (n >> (mpd_bsf(x)+1))); - /* The loop invariant is preserved. */ - } while (x < n); -} - - -/* Fast Number Theoretic Transform, decimation in frequency. */ -void -fnt_dif2(mpd_uint_t a[], mpd_size_t n, struct fnt_params *tparams) -{ - mpd_uint_t *wtable = tparams->wtable; - mpd_uint_t umod; -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - mpd_uint_t u0, u1, v0, v1; - mpd_uint_t w, w0, w1, wstep; - mpd_size_t m, mhalf; - mpd_size_t j, r; - - - assert(ispower2(n)); - assert(n >= 4); - - SETMODULUS(tparams->modnum); - - /* m == n */ - mhalf = n / 2; - for (j = 0; j < mhalf; j += 2) { - - w0 = wtable[j]; - w1 = wtable[j+1]; - - u0 = a[j]; - v0 = a[j+mhalf]; - - u1 = a[j+1]; - v1 = a[j+1+mhalf]; - - a[j] = addmod(u0, v0, umod); - v0 = submod(u0, v0, umod); - - a[j+1] = addmod(u1, v1, umod); - v1 = submod(u1, v1, umod); - - MULMOD2(&v0, w0, &v1, w1); - - a[j+mhalf] = v0; - a[j+1+mhalf] = v1; - - } - - wstep = 2; - for (m = n/2; m >= 2; m>>=1, wstep<<=1) { - - mhalf = m / 2; - - /* j == 0 */ - for (r = 0; r < n; r += 2*m) { - - u0 = a[r]; - v0 = a[r+mhalf]; - - u1 = a[m+r]; - v1 = a[m+r+mhalf]; - - a[r] = addmod(u0, v0, umod); - v0 = submod(u0, v0, umod); - - a[m+r] = addmod(u1, v1, umod); - v1 = submod(u1, v1, umod); - - a[r+mhalf] = v0; - a[m+r+mhalf] = v1; - } - - for (j = 1; j < mhalf; j++) { - - w = wtable[j*wstep]; - - for (r = 0; r < n; r += 2*m) { - - u0 = a[r+j]; - v0 = a[r+j+mhalf]; - - u1 = a[m+r+j]; - v1 = a[m+r+j+mhalf]; - - a[r+j] = addmod(u0, v0, umod); - v0 = submod(u0, v0, umod); - - a[m+r+j] = addmod(u1, v1, umod); - v1 = submod(u1, v1, umod); - - MULMOD2C(&v0, &v1, w); - - a[r+j+mhalf] = v0; - a[m+r+j+mhalf] = v1; - } - - } - - } - - bitreverse_permute(a, n); -} diff --git a/Modules/_decimal/libmpdec/difradix2.h b/Modules/_decimal/libmpdec/difradix2.h deleted file mode 100644 index cdcbcf9a71043c..00000000000000 --- a/Modules/_decimal/libmpdec/difradix2.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_DIFRADIX2_H_ -#define LIBMPDEC_DIFRADIX2_H_ - - -#include "mpdecimal.h" -#include "numbertheory.h" - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -void fnt_dif2(mpd_uint_t a[], mpd_size_t n, struct fnt_params *tparams); - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_DIFRADIX2_H_ */ diff --git a/Modules/_decimal/libmpdec/examples/README.txt b/Modules/_decimal/libmpdec/examples/README.txt deleted file mode 100644 index 69615b45f9821a..00000000000000 --- a/Modules/_decimal/libmpdec/examples/README.txt +++ /dev/null @@ -1,8 +0,0 @@ - - -This directory contains a number of examples. In order to compile, run -(for example): - -gcc -Wall -W -O2 -o powmod powmod.c -lmpdec -lm - - diff --git a/Modules/_decimal/libmpdec/examples/compare.c b/Modules/_decimal/libmpdec/examples/compare.c deleted file mode 100644 index 9051773e116dec..00000000000000 --- a/Modules/_decimal/libmpdec/examples/compare.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <mpdecimal.h> - - -int -main(int argc, char **argv) -{ - mpd_context_t ctx; - mpd_t *a, *b; - mpd_t *result; - char *rstring; - char status_str[MPD_MAX_FLAG_STRING]; - clock_t start_clock, end_clock; - - if (argc != 3) { - fprintf(stderr, "compare: usage: ./compare x y\n"); - exit(1); - } - - mpd_init(&ctx, 38); - ctx.traps = 0; - - result = mpd_new(&ctx); - a = mpd_new(&ctx); - b = mpd_new(&ctx); - mpd_set_string(a, argv[1], &ctx); - mpd_set_string(b, argv[2], &ctx); - - start_clock = clock(); - mpd_compare(result, a, b, &ctx); - end_clock = clock(); - fprintf(stderr, "time: %f\n\n", - (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); - - rstring = mpd_to_sci(result, 1); - mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status); - printf("%s %s\n", rstring, status_str); - - mpd_del(a); - mpd_del(b); - mpd_del(result); - mpd_free(rstring); - - return 0; -} - - diff --git a/Modules/_decimal/libmpdec/examples/div.c b/Modules/_decimal/libmpdec/examples/div.c deleted file mode 100644 index b76037d2a64c67..00000000000000 --- a/Modules/_decimal/libmpdec/examples/div.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <mpdecimal.h> - - -int -main(int argc, char **argv) -{ - mpd_context_t ctx; - mpd_t *a, *b; - mpd_t *result; - char *rstring; - char status_str[MPD_MAX_FLAG_STRING]; - clock_t start_clock, end_clock; - - if (argc != 3) { - fprintf(stderr, "div: usage: ./div x y\n"); - exit(1); - } - - mpd_init(&ctx, 38); - ctx.traps = 0; - - result = mpd_new(&ctx); - a = mpd_new(&ctx); - b = mpd_new(&ctx); - mpd_set_string(a, argv[1], &ctx); - mpd_set_string(b, argv[2], &ctx); - - start_clock = clock(); - mpd_div(result, a, b, &ctx); - end_clock = clock(); - fprintf(stderr, "time: %f\n\n", - (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); - - rstring = mpd_to_sci(result, 1); - mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status); - printf("%s %s\n", rstring, status_str); - - mpd_del(a); - mpd_del(b); - mpd_del(result); - mpd_free(rstring); - - return 0; -} - - diff --git a/Modules/_decimal/libmpdec/examples/divmod.c b/Modules/_decimal/libmpdec/examples/divmod.c deleted file mode 100644 index 1f2b48306d6d06..00000000000000 --- a/Modules/_decimal/libmpdec/examples/divmod.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <mpdecimal.h> - - -int -main(int argc, char **argv) -{ - mpd_context_t ctx; - mpd_t *a, *b; - mpd_t *q, *r; - char *qs, *rs; - char status_str[MPD_MAX_FLAG_STRING]; - clock_t start_clock, end_clock; - - if (argc != 3) { - fprintf(stderr, "divmod: usage: ./divmod x y\n"); - exit(1); - } - - mpd_init(&ctx, 38); - ctx.traps = 0; - - q = mpd_new(&ctx); - r = mpd_new(&ctx); - a = mpd_new(&ctx); - b = mpd_new(&ctx); - mpd_set_string(a, argv[1], &ctx); - mpd_set_string(b, argv[2], &ctx); - - start_clock = clock(); - mpd_divmod(q, r, a, b, &ctx); - end_clock = clock(); - fprintf(stderr, "time: %f\n\n", - (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); - - qs = mpd_to_sci(q, 1); - rs = mpd_to_sci(r, 1); - - mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status); - printf("%s %s %s\n", qs, rs, status_str); - - mpd_del(q); - mpd_del(r); - mpd_del(a); - mpd_del(b); - mpd_free(qs); - mpd_free(rs); - - return 0; -} - - diff --git a/Modules/_decimal/libmpdec/examples/multiply.c b/Modules/_decimal/libmpdec/examples/multiply.c deleted file mode 100644 index 7f2687d15f8273..00000000000000 --- a/Modules/_decimal/libmpdec/examples/multiply.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <mpdecimal.h> - - -int -main(int argc, char **argv) -{ - mpd_context_t ctx; - mpd_t *a, *b; - mpd_t *result; - char *rstring; - char status_str[MPD_MAX_FLAG_STRING]; - clock_t start_clock, end_clock; - - if (argc != 3) { - fprintf(stderr, "multiply: usage: ./multiply x y\n"); - exit(1); - } - - mpd_init(&ctx, 38); - ctx.traps = 0; - - result = mpd_new(&ctx); - a = mpd_new(&ctx); - b = mpd_new(&ctx); - mpd_set_string(a, argv[1], &ctx); - mpd_set_string(b, argv[2], &ctx); - - start_clock = clock(); - mpd_mul(result, a, b, &ctx); - end_clock = clock(); - fprintf(stderr, "time: %f\n\n", - (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); - - rstring = mpd_to_sci(result, 1); - mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status); - printf("%s %s\n", rstring, status_str); - - mpd_del(a); - mpd_del(b); - mpd_del(result); - mpd_free(rstring); - - return 0; -} - - diff --git a/Modules/_decimal/libmpdec/examples/pow.c b/Modules/_decimal/libmpdec/examples/pow.c deleted file mode 100644 index 628c1434273573..00000000000000 --- a/Modules/_decimal/libmpdec/examples/pow.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <mpdecimal.h> - - -int -main(int argc, char **argv) -{ - mpd_context_t ctx; - mpd_t *a, *b; - mpd_t *result; - char *rstring; - char status_str[MPD_MAX_FLAG_STRING]; - clock_t start_clock, end_clock; - - if (argc != 3) { - fprintf(stderr, "pow: usage: ./pow x y\n"); - exit(1); - } - - mpd_init(&ctx, 38); - ctx.traps = 0; - - result = mpd_new(&ctx); - a = mpd_new(&ctx); - b = mpd_new(&ctx); - mpd_set_string(a, argv[1], &ctx); - mpd_set_string(b, argv[2], &ctx); - - start_clock = clock(); - mpd_pow(result, a, b, &ctx); - end_clock = clock(); - fprintf(stderr, "time: %f\n\n", - (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); - - rstring = mpd_to_sci(result, 1); - mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status); - printf("%s %s\n", rstring, status_str); - - mpd_del(a); - mpd_del(b); - mpd_del(result); - mpd_free(rstring); - - return 0; -} - - diff --git a/Modules/_decimal/libmpdec/examples/powmod.c b/Modules/_decimal/libmpdec/examples/powmod.c deleted file mode 100644 index b422fdbbb955d7..00000000000000 --- a/Modules/_decimal/libmpdec/examples/powmod.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <mpdecimal.h> - - -int -main(int argc, char **argv) -{ - mpd_context_t ctx; - mpd_t *a, *b, *c; - mpd_t *result; - char *rstring; - char status_str[MPD_MAX_FLAG_STRING]; - clock_t start_clock, end_clock; - - if (argc != 4) { - fprintf(stderr, "powmod: usage: ./powmod x y z\n"); - exit(1); - } - - mpd_init(&ctx, 38); - ctx.traps = 0; - - result = mpd_new(&ctx); - a = mpd_new(&ctx); - b = mpd_new(&ctx); - c = mpd_new(&ctx); - mpd_set_string(a, argv[1], &ctx); - mpd_set_string(b, argv[2], &ctx); - mpd_set_string(c, argv[3], &ctx); - - start_clock = clock(); - mpd_powmod(result, a, b, c, &ctx); - end_clock = clock(); - fprintf(stderr, "time: %f\n\n", - (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); - - rstring = mpd_to_sci(result, 1); - mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status); - printf("%s %s\n", rstring, status_str); - - mpd_del(a); - mpd_del(b); - mpd_del(c); - mpd_del(result); - mpd_free(rstring); - - return 0; -} - - diff --git a/Modules/_decimal/libmpdec/examples/shift.c b/Modules/_decimal/libmpdec/examples/shift.c deleted file mode 100644 index 6d54e108ca87f0..00000000000000 --- a/Modules/_decimal/libmpdec/examples/shift.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <mpdecimal.h> - - -int -main(int argc, char **argv) -{ - mpd_context_t ctx; - mpd_t *a, *b; - mpd_t *result; - char *rstring; - char status_str[MPD_MAX_FLAG_STRING]; - clock_t start_clock, end_clock; - - if (argc != 3) { - fprintf(stderr, "shift: usage: ./shift x y\n"); - exit(1); - } - - mpd_init(&ctx, 38); - ctx.traps = 0; - - result = mpd_new(&ctx); - a = mpd_new(&ctx); - b = mpd_new(&ctx); - mpd_set_string(a, argv[1], &ctx); - mpd_set_string(b, argv[2], &ctx); - - start_clock = clock(); - mpd_shift(result, a, b, &ctx); - end_clock = clock(); - fprintf(stderr, "time: %f\n\n", - (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); - - rstring = mpd_to_sci(result, 1); - mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status); - printf("%s %s\n", rstring, status_str); - - mpd_del(a); - mpd_del(b); - mpd_del(result); - mpd_free(rstring); - - return 0; -} - - diff --git a/Modules/_decimal/libmpdec/examples/sqrt.c b/Modules/_decimal/libmpdec/examples/sqrt.c deleted file mode 100644 index d8272789b18c23..00000000000000 --- a/Modules/_decimal/libmpdec/examples/sqrt.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <mpdecimal.h> - - -int -main(int argc, char **argv) -{ - mpd_context_t ctx; - mpd_t *a; - mpd_t *result; - char *rstring; - char status_str[MPD_MAX_FLAG_STRING]; - clock_t start_clock, end_clock; - - if (argc != 2) { - fprintf(stderr, "sqrt: usage: ./sqrt x\n"); - exit(1); - } - - mpd_init(&ctx, 38); - ctx.traps = 0; - - result = mpd_new(&ctx); - a = mpd_new(&ctx); - mpd_set_string(a, argv[1], &ctx); - - start_clock = clock(); - mpd_sqrt(result, a, &ctx); - end_clock = clock(); - fprintf(stderr, "time: %f\n\n", - (double)(end_clock-start_clock)/(double)CLOCKS_PER_SEC); - - rstring = mpd_to_sci(result, 1); - mpd_snprint_flags(status_str, MPD_MAX_FLAG_STRING, ctx.status); - printf("%s %s\n", rstring, status_str); - - mpd_del(a); - mpd_del(result); - mpd_free(rstring); - - return 0; -} - - diff --git a/Modules/_decimal/libmpdec/fnt.c b/Modules/_decimal/libmpdec/fnt.c deleted file mode 100644 index 0dbe98fc71c9ea..00000000000000 --- a/Modules/_decimal/libmpdec/fnt.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> -#include <stdio.h> - -#include "bits.h" -#include "difradix2.h" -#include "fnt.h" -#include "numbertheory.h" - - -/* Bignum: Fast transform for medium-sized coefficients. */ - - -/* forward transform, sign = -1 */ -int -std_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) -{ - struct fnt_params *tparams; - - assert(ispower2(n)); - assert(n >= 4); - assert(n <= 3*MPD_MAXTRANSFORM_2N); - - if ((tparams = _mpd_init_fnt_params(n, -1, modnum)) == NULL) { - return 0; - } - fnt_dif2(a, n, tparams); - - mpd_free(tparams); - return 1; -} - -/* reverse transform, sign = 1 */ -int -std_inv_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) -{ - struct fnt_params *tparams; - - assert(ispower2(n)); - assert(n >= 4); - assert(n <= 3*MPD_MAXTRANSFORM_2N); - - if ((tparams = _mpd_init_fnt_params(n, 1, modnum)) == NULL) { - return 0; - } - fnt_dif2(a, n, tparams); - - mpd_free(tparams); - return 1; -} diff --git a/Modules/_decimal/libmpdec/fnt.h b/Modules/_decimal/libmpdec/fnt.h deleted file mode 100644 index 5222c476a3a4f5..00000000000000 --- a/Modules/_decimal/libmpdec/fnt.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_FNT_H_ -#define LIBMPDEC_FNT_H_ - - -#include "mpdecimal.h" - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -int std_fnt(mpd_uint_t a[], mpd_size_t n, int modnum); -int std_inv_fnt(mpd_uint_t a[], mpd_size_t n, int modnum); - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_FNT_H_ */ diff --git a/Modules/_decimal/libmpdec/fourstep.c b/Modules/_decimal/libmpdec/fourstep.c deleted file mode 100644 index fb173ed5a52e46..00000000000000 --- a/Modules/_decimal/libmpdec/fourstep.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> - -#include "constants.h" -#include "fourstep.h" -#include "numbertheory.h" -#include "sixstep.h" -#include "umodarith.h" - - -/* Bignum: Cache efficient Matrix Fourier Transform for arrays of the - form 3 * 2**n (See literature/matrix-transform.txt). */ - - -#ifndef PPRO -static inline void -std_size3_ntt(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, - mpd_uint_t w3table[3], mpd_uint_t umod) -{ - mpd_uint_t r1, r2; - mpd_uint_t w; - mpd_uint_t s, tmp; - - - /* k = 0 -> w = 1 */ - s = *x1; - s = addmod(s, *x2, umod); - s = addmod(s, *x3, umod); - - r1 = s; - - /* k = 1 */ - s = *x1; - - w = w3table[1]; - tmp = MULMOD(*x2, w); - s = addmod(s, tmp, umod); - - w = w3table[2]; - tmp = MULMOD(*x3, w); - s = addmod(s, tmp, umod); - - r2 = s; - - /* k = 2 */ - s = *x1; - - w = w3table[2]; - tmp = MULMOD(*x2, w); - s = addmod(s, tmp, umod); - - w = w3table[1]; - tmp = MULMOD(*x3, w); - s = addmod(s, tmp, umod); - - *x3 = s; - *x2 = r2; - *x1 = r1; -} -#else /* PPRO */ -static inline void -ppro_size3_ntt(mpd_uint_t *x1, mpd_uint_t *x2, mpd_uint_t *x3, mpd_uint_t w3table[3], - mpd_uint_t umod, double *dmod, uint32_t dinvmod[3]) -{ - mpd_uint_t r1, r2; - mpd_uint_t w; - mpd_uint_t s, tmp; - - - /* k = 0 -> w = 1 */ - s = *x1; - s = addmod(s, *x2, umod); - s = addmod(s, *x3, umod); - - r1 = s; - - /* k = 1 */ - s = *x1; - - w = w3table[1]; - tmp = ppro_mulmod(*x2, w, dmod, dinvmod); - s = addmod(s, tmp, umod); - - w = w3table[2]; - tmp = ppro_mulmod(*x3, w, dmod, dinvmod); - s = addmod(s, tmp, umod); - - r2 = s; - - /* k = 2 */ - s = *x1; - - w = w3table[2]; - tmp = ppro_mulmod(*x2, w, dmod, dinvmod); - s = addmod(s, tmp, umod); - - w = w3table[1]; - tmp = ppro_mulmod(*x3, w, dmod, dinvmod); - s = addmod(s, tmp, umod); - - *x3 = s; - *x2 = r2; - *x1 = r1; -} -#endif - - -/* forward transform, sign = -1; transform length = 3 * 2**n */ -int -four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) -{ - mpd_size_t R = 3; /* number of rows */ - mpd_size_t C = n / 3; /* number of columns */ - mpd_uint_t w3table[3]; - mpd_uint_t kernel, w0, w1, wstep; - mpd_uint_t *s, *p0, *p1, *p2; - mpd_uint_t umod; -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - mpd_size_t i, k; - - - assert(n >= 48); - assert(n <= 3*MPD_MAXTRANSFORM_2N); - - - /* Length R transform on the columns. */ - SETMODULUS(modnum); - _mpd_init_w3table(w3table, -1, modnum); - for (p0=a, p1=p0+C, p2=p0+2*C; p0<a+C; p0++,p1++,p2++) { - - SIZE3_NTT(p0, p1, p2, w3table); - } - - /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */ - kernel = _mpd_getkernel(n, -1, modnum); - for (i = 1; i < R; i++) { - w0 = 1; /* r**(i*0): initial value for k=0 */ - w1 = POWMOD(kernel, i); /* r**(i*1): initial value for k=1 */ - wstep = MULMOD(w1, w1); /* r**(2*i) */ - for (k = 0; k < C-1; k += 2) { - mpd_uint_t x0 = a[i*C+k]; - mpd_uint_t x1 = a[i*C+k+1]; - MULMOD2(&x0, w0, &x1, w1); - MULMOD2C(&w0, &w1, wstep); /* r**(i*(k+2)) = r**(i*k) * r**(2*i) */ - a[i*C+k] = x0; - a[i*C+k+1] = x1; - } - } - - /* Length C transform on the rows. */ - for (s = a; s < a+n; s += C) { - if (!six_step_fnt(s, C, modnum)) { - return 0; - } - } - -#if 0 - /* An unordered transform is sufficient for convolution. */ - /* Transpose the matrix. */ - #include "transpose.h" - transpose_3xpow2(a, R, C); -#endif - - return 1; -} - -/* backward transform, sign = 1; transform length = 3 * 2**n */ -int -inv_four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) -{ - mpd_size_t R = 3; /* number of rows */ - mpd_size_t C = n / 3; /* number of columns */ - mpd_uint_t w3table[3]; - mpd_uint_t kernel, w0, w1, wstep; - mpd_uint_t *s, *p0, *p1, *p2; - mpd_uint_t umod; -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - mpd_size_t i, k; - - - assert(n >= 48); - assert(n <= 3*MPD_MAXTRANSFORM_2N); - - -#if 0 - /* An unordered transform is sufficient for convolution. */ - /* Transpose the matrix, producing an R*C matrix. */ - #include "transpose.h" - transpose_3xpow2(a, C, R); -#endif - - /* Length C transform on the rows. */ - for (s = a; s < a+n; s += C) { - if (!inv_six_step_fnt(s, C, modnum)) { - return 0; - } - } - - /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */ - SETMODULUS(modnum); - kernel = _mpd_getkernel(n, 1, modnum); - for (i = 1; i < R; i++) { - w0 = 1; - w1 = POWMOD(kernel, i); - wstep = MULMOD(w1, w1); - for (k = 0; k < C; k += 2) { - mpd_uint_t x0 = a[i*C+k]; - mpd_uint_t x1 = a[i*C+k+1]; - MULMOD2(&x0, w0, &x1, w1); - MULMOD2C(&w0, &w1, wstep); - a[i*C+k] = x0; - a[i*C+k+1] = x1; - } - } - - /* Length R transform on the columns. */ - _mpd_init_w3table(w3table, 1, modnum); - for (p0=a, p1=p0+C, p2=p0+2*C; p0<a+C; p0++,p1++,p2++) { - - SIZE3_NTT(p0, p1, p2, w3table); - } - - return 1; -} diff --git a/Modules/_decimal/libmpdec/fourstep.h b/Modules/_decimal/libmpdec/fourstep.h deleted file mode 100644 index 5ffb6fcc8ecd0d..00000000000000 --- a/Modules/_decimal/libmpdec/fourstep.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_FOURSTEP_H_ -#define LIBMPDEC_FOURSTEP_H_ - - -#include "mpdecimal.h" - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -int four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum); -int inv_four_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum); - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_FOURSTEP_H_ */ diff --git a/Modules/_decimal/libmpdec/io.c b/Modules/_decimal/libmpdec/io.c deleted file mode 100644 index bdcca001659bc0..00000000000000 --- a/Modules/_decimal/libmpdec/io.c +++ /dev/null @@ -1,1606 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <locale.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "io.h" -#include "typearith.h" - - -/* This file contains functions for decimal <-> string conversions, including - PEP-3101 formatting for numeric types. */ - - -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" - #pragma GCC diagnostic ignored "-Wmisleading-indentation" - #pragma GCC diagnostic ignored "-Warray-bounds" -#endif - - -/* - * Work around the behavior of tolower() and strcasecmp() in certain - * locales. For example, in tr_TR.utf8: - * - * tolower((unsigned char)'I') == 'I' - * - * u is the exact uppercase version of l; n is strlen(l) or strlen(l)+1 - */ -static inline int -_mpd_strneq(const char *s, const char *l, const char *u, size_t n) -{ - while (--n != SIZE_MAX) { - if (*s != *l && *s != *u) { - return 0; - } - s++; u++; l++; - } - - return 1; -} - -static mpd_ssize_t -strtoexp(const char *s) -{ - char *end; - mpd_ssize_t retval; - - errno = 0; - retval = mpd_strtossize(s, &end, 10); - if (errno == 0 && !(*s != '\0' && *end == '\0')) - errno = EINVAL; - - return retval; -} - -/* - * Scan 'len' words. The most significant word contains 'r' digits, - * the remaining words are full words. Skip dpoint. The string 's' must - * consist of digits and an optional single decimal point at 'dpoint'. - */ -static void -string_to_coeff(mpd_uint_t *data, const char *s, const char *dpoint, int r, - size_t len) -{ - int j; - - if (r > 0) { - data[--len] = 0; - for (j = 0; j < r; j++, s++) { - if (s == dpoint) s++; - data[len] = 10 * data[len] + (*s - '0'); - } - } - - while (--len != SIZE_MAX) { - data[len] = 0; - for (j = 0; j < MPD_RDIGITS; j++, s++) { - if (s == dpoint) s++; - data[len] = 10 * data[len] + (*s - '0'); - } - } -} - -/* - * Partially verify a numeric string of the form: - * - * [cdigits][.][cdigits][eE][+-][edigits] - * - * If successful, return a pointer to the location of the first - * relevant coefficient digit. This digit is either non-zero or - * part of one of the following patterns: - * - * ["0\x00", "0.\x00", "0.E", "0.e", "0E", "0e"] - * - * The locations of a single optional dot or indicator are stored - * in 'dpoint' and 'exp'. - * - * The end of the string is stored in 'end'. If an indicator [eE] - * occurs without trailing [edigits], the condition is caught - * later by strtoexp(). - */ -static const char * -scan_dpoint_exp(const char *s, const char **dpoint, const char **exp, - const char **end) -{ - const char *coeff = NULL; - - *dpoint = NULL; - *exp = NULL; - for (; *s != '\0'; s++) { - switch (*s) { - case '.': - if (*dpoint != NULL || *exp != NULL) - return NULL; - *dpoint = s; - break; - case 'E': case 'e': - if (*exp != NULL) - return NULL; - *exp = s; - if (*(s+1) == '+' || *(s+1) == '-') - s++; - break; - default: - if (!isdigit((unsigned char)*s)) - return NULL; - if (coeff == NULL && *exp == NULL) { - if (*s == '0') { - if (!isdigit((unsigned char)*(s+1))) - if (!(*(s+1) == '.' && - isdigit((unsigned char)*(s+2)))) - coeff = s; - } - else { - coeff = s; - } - } - break; - - } - } - - *end = s; - return coeff; -} - -/* scan the payload of a NaN */ -static const char * -scan_payload(const char *s, const char **end) -{ - const char *coeff; - - while (*s == '0') - s++; - coeff = s; - - while (isdigit((unsigned char)*s)) - s++; - *end = s; - - return (*s == '\0') ? coeff : NULL; -} - -/* convert a character string to a decimal */ -void -mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_ssize_t q, r, len; - const char *coeff, *end; - const char *dpoint = NULL, *exp = NULL; - size_t digits; - uint8_t sign = MPD_POS; - - mpd_set_flags(dec, 0); - dec->len = 0; - dec->exp = 0; - - /* sign */ - if (*s == '+') { - s++; - } - else if (*s == '-') { - mpd_set_negative(dec); - sign = MPD_NEG; - s++; - } - - if (_mpd_strneq(s, "nan", "NAN", 3)) { /* NaN */ - s += 3; - mpd_setspecial(dec, sign, MPD_NAN); - if (*s == '\0') - return; - /* validate payload: digits only */ - if ((coeff = scan_payload(s, &end)) == NULL) - goto conversion_error; - /* payload consists entirely of zeros */ - if (*coeff == '\0') - return; - digits = end - coeff; - /* prec >= 1, clamp is 0 or 1 */ - if (digits > (size_t)(ctx->prec-ctx->clamp)) - goto conversion_error; - } /* sNaN */ - else if (_mpd_strneq(s, "snan", "SNAN", 4)) { - s += 4; - mpd_setspecial(dec, sign, MPD_SNAN); - if (*s == '\0') - return; - /* validate payload: digits only */ - if ((coeff = scan_payload(s, &end)) == NULL) - goto conversion_error; - /* payload consists entirely of zeros */ - if (*coeff == '\0') - return; - digits = end - coeff; - if (digits > (size_t)(ctx->prec-ctx->clamp)) - goto conversion_error; - } - else if (_mpd_strneq(s, "inf", "INF", 3)) { - s += 3; - if (*s == '\0' || _mpd_strneq(s, "inity", "INITY", 6)) { - /* numeric-value: infinity */ - mpd_setspecial(dec, sign, MPD_INF); - return; - } - goto conversion_error; - } - else { - /* scan for start of coefficient, decimal point, indicator, end */ - if ((coeff = scan_dpoint_exp(s, &dpoint, &exp, &end)) == NULL) - goto conversion_error; - - /* numeric-value: [exponent-part] */ - if (exp) { - /* exponent-part */ - end = exp; exp++; - dec->exp = strtoexp(exp); - if (errno) { - if (!(errno == ERANGE && - (dec->exp == MPD_SSIZE_MAX || - dec->exp == MPD_SSIZE_MIN))) - goto conversion_error; - } - } - - digits = end - coeff; - if (dpoint) { - size_t fracdigits = end-dpoint-1; - if (dpoint > coeff) digits--; - - if (fracdigits > MPD_MAX_PREC) { - goto conversion_error; - } - if (dec->exp < MPD_SSIZE_MIN+(mpd_ssize_t)fracdigits) { - dec->exp = MPD_SSIZE_MIN; - } - else { - dec->exp -= (mpd_ssize_t)fracdigits; - } - } - if (digits > MPD_MAX_PREC) { - goto conversion_error; - } - if (dec->exp > MPD_EXP_INF) { - dec->exp = MPD_EXP_INF; - } - if (dec->exp == MPD_SSIZE_MIN) { - dec->exp = MPD_SSIZE_MIN+1; - } - } - - _mpd_idiv_word(&q, &r, (mpd_ssize_t)digits, MPD_RDIGITS); - - len = (r == 0) ? q : q+1; - if (len == 0) { - goto conversion_error; /* GCOV_NOT_REACHED */ - } - if (!mpd_qresize(dec, len, status)) { - mpd_seterror(dec, MPD_Malloc_error, status); - return; - } - dec->len = len; - - string_to_coeff(dec->data, coeff, dpoint, (int)r, len); - - mpd_setdigits(dec); - mpd_qfinalize(dec, ctx, status); - return; - -conversion_error: - /* standard wants a positive NaN */ - mpd_seterror(dec, MPD_Conversion_syntax, status); -} - -/* convert a character string to a decimal, use a maxcontext for conversion */ -void -mpd_qset_string_exact(mpd_t *dec, const char *s, uint32_t *status) -{ - mpd_context_t maxcontext; - - mpd_maxcontext(&maxcontext); - mpd_qset_string(dec, s, &maxcontext, status); - - if (*status & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) { - /* we want exact results */ - mpd_seterror(dec, MPD_Invalid_operation, status); - } - *status &= MPD_Errors; -} - -/* Print word x with n decimal digits to string s. dot is either NULL - or the location of a decimal point. */ -#define EXTRACT_DIGIT(s, x, d, dot) \ - if (s == dot) *s++ = '.'; *s++ = '0' + (char)(x / d); x %= d -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && __GNUC__ >= 12 - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wstringop-overflow" -#endif -static inline char * -word_to_string(char *s, mpd_uint_t x, int n, char *dot) -{ - switch(n) { -#ifdef CONFIG_64 - case 20: EXTRACT_DIGIT(s, x, 10000000000000000000ULL, dot); /* GCOV_NOT_REACHED */ - case 19: EXTRACT_DIGIT(s, x, 1000000000000000000ULL, dot); - case 18: EXTRACT_DIGIT(s, x, 100000000000000000ULL, dot); - case 17: EXTRACT_DIGIT(s, x, 10000000000000000ULL, dot); - case 16: EXTRACT_DIGIT(s, x, 1000000000000000ULL, dot); - case 15: EXTRACT_DIGIT(s, x, 100000000000000ULL, dot); - case 14: EXTRACT_DIGIT(s, x, 10000000000000ULL, dot); - case 13: EXTRACT_DIGIT(s, x, 1000000000000ULL, dot); - case 12: EXTRACT_DIGIT(s, x, 100000000000ULL, dot); - case 11: EXTRACT_DIGIT(s, x, 10000000000ULL, dot); -#endif - case 10: EXTRACT_DIGIT(s, x, 1000000000UL, dot); - case 9: EXTRACT_DIGIT(s, x, 100000000UL, dot); - case 8: EXTRACT_DIGIT(s, x, 10000000UL, dot); - case 7: EXTRACT_DIGIT(s, x, 1000000UL, dot); - case 6: EXTRACT_DIGIT(s, x, 100000UL, dot); - case 5: EXTRACT_DIGIT(s, x, 10000UL, dot); - case 4: EXTRACT_DIGIT(s, x, 1000UL, dot); - case 3: EXTRACT_DIGIT(s, x, 100UL, dot); - case 2: EXTRACT_DIGIT(s, x, 10UL, dot); - default: if (s == dot) *s++ = '.'; *s++ = '0' + (char)x; - } - - *s = '\0'; - return s; -} -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && __GNUC__ >= 12 - #pragma GCC diagnostic pop -#endif - -/* Print exponent x to string s. Undefined for MPD_SSIZE_MIN. */ -static inline char * -exp_to_string(char *s, mpd_ssize_t x) -{ - char sign = '+'; - - if (x < 0) { - sign = '-'; - x = -x; - } - *s++ = sign; - - return word_to_string(s, x, mpd_word_digits(x), NULL); -} - -/* Print the coefficient of dec to string s. len(dec) > 0. */ -static inline char * -coeff_to_string(char *s, const mpd_t *dec) -{ - mpd_uint_t x; - mpd_ssize_t i; - - /* most significant word */ - x = mpd_msword(dec); - s = word_to_string(s, x, mpd_word_digits(x), NULL); - - /* remaining full words */ - for (i=dec->len-2; i >= 0; --i) { - x = dec->data[i]; - s = word_to_string(s, x, MPD_RDIGITS, NULL); - } - - return s; -} - -/* Print the coefficient of dec to string s. len(dec) > 0. dot is either - NULL or a pointer to the location of a decimal point. */ -static inline char * -coeff_to_string_dot(char *s, char *dot, const mpd_t *dec) -{ - mpd_uint_t x; - mpd_ssize_t i; - - /* most significant word */ - x = mpd_msword(dec); - s = word_to_string(s, x, mpd_word_digits(x), dot); - - /* remaining full words */ - for (i=dec->len-2; i >= 0; --i) { - x = dec->data[i]; - s = word_to_string(s, x, MPD_RDIGITS, dot); - } - - return s; -} - -/* Format type */ -#define MPD_FMT_LOWER 0x00000000 -#define MPD_FMT_UPPER 0x00000001 -#define MPD_FMT_TOSCI 0x00000002 -#define MPD_FMT_TOENG 0x00000004 -#define MPD_FMT_EXP 0x00000008 -#define MPD_FMT_FIXED 0x00000010 -#define MPD_FMT_PERCENT 0x00000020 -#define MPD_FMT_SIGN_SPACE 0x00000040 -#define MPD_FMT_SIGN_PLUS 0x00000080 - -/* Default place of the decimal point for MPD_FMT_TOSCI, MPD_FMT_EXP */ -#define MPD_DEFAULT_DOTPLACE 1 - -/* - * Set *result to the string representation of a decimal. Return the length - * of *result, not including the terminating '\0' character. - * - * Formatting is done according to 'flags'. A return value of -1 with *result - * set to NULL indicates MPD_Malloc_error. - * - * 'dplace' is the default place of the decimal point. It is always set to - * MPD_DEFAULT_DOTPLACE except for zeros in combination with MPD_FMT_EXP. - */ -static mpd_ssize_t -_mpd_to_string(char **result, const mpd_t *dec, int flags, mpd_ssize_t dplace) -{ - char *decstring = NULL, *cp = NULL; - mpd_ssize_t ldigits; - mpd_ssize_t mem = 0, k; - - if (mpd_isspecial(dec)) { - - mem = sizeof "-Infinity%"; - if (mpd_isnan(dec) && dec->len > 0) { - /* diagnostic code */ - mem += dec->digits; - } - cp = decstring = mpd_alloc(mem, sizeof *decstring); - if (cp == NULL) { - *result = NULL; - return -1; - } - - if (mpd_isnegative(dec)) { - *cp++ = '-'; - } - else if (flags&MPD_FMT_SIGN_SPACE) { - *cp++ = ' '; - } - else if (flags&MPD_FMT_SIGN_PLUS) { - *cp++ = '+'; - } - - if (mpd_isnan(dec)) { - if (mpd_isqnan(dec)) { - strcpy(cp, "NaN"); - cp += 3; - } - else { - strcpy(cp, "sNaN"); - cp += 4; - } - if (dec->len > 0) { /* diagnostic code */ - cp = coeff_to_string(cp, dec); - } - } - else if (mpd_isinfinite(dec)) { - strcpy(cp, "Infinity"); - cp += 8; - } - else { /* debug */ - abort(); /* GCOV_NOT_REACHED */ - } - } - else { - assert(dec->len > 0); - - /* - * For easier manipulation of the decimal point's location - * and the exponent that is finally printed, the number is - * rescaled to a virtual representation with exp = 0. Here - * ldigits denotes the number of decimal digits to the left - * of the decimal point and remains constant once initialized. - * - * dplace is the location of the decimal point relative to - * the start of the coefficient. Note that 3) always holds - * when dplace is shifted. - * - * 1) ldigits := dec->digits - dec->exp - * 2) dplace := ldigits (initially) - * 3) exp := ldigits - dplace (initially exp = 0) - * - * 0.00000_.____._____000000. - * ^ ^ ^ ^ - * | | | | - * | | | `- dplace >= digits - * | | `- dplace in the middle of the coefficient - * | ` dplace = 1 (after the first coefficient digit) - * `- dplace <= 0 - */ - - ldigits = dec->digits + dec->exp; - - if (flags&MPD_FMT_EXP) { - ; - } - else if (flags&MPD_FMT_FIXED || (dec->exp <= 0 && ldigits > -6)) { - /* MPD_FMT_FIXED: always use fixed point notation. - * MPD_FMT_TOSCI, MPD_FMT_TOENG: for a certain range, - * override exponent notation. */ - dplace = ldigits; - } - else if (flags&MPD_FMT_TOENG) { - if (mpd_iszero(dec)) { - /* If the exponent is divisible by three, - * dplace = 1. Otherwise, move dplace one - * or two places to the left. */ - dplace = -1 + mod_mpd_ssize_t(dec->exp+2, 3); - } - else { /* ldigits-1 is the adjusted exponent, which - * should be divisible by three. If not, move - * dplace one or two places to the right. */ - dplace += mod_mpd_ssize_t(ldigits-1, 3); - } - } - - /* - * Basic space requirements: - * - * [-][.][coeffdigits][E][-][expdigits+1][%]['\0'] - * - * If the decimal point lies outside of the coefficient digits, - * space is adjusted accordingly. - */ - if (dplace <= 0) { - mem = -dplace + dec->digits + 2; - } - else if (dplace >= dec->digits) { - mem = dplace; - } - else { - mem = dec->digits; - } - mem += (MPD_EXPDIGITS+1+6); - - cp = decstring = mpd_alloc(mem, sizeof *decstring); - if (cp == NULL) { - *result = NULL; - return -1; - } - - - if (mpd_isnegative(dec)) { - *cp++ = '-'; - } - else if (flags&MPD_FMT_SIGN_SPACE) { - *cp++ = ' '; - } - else if (flags&MPD_FMT_SIGN_PLUS) { - *cp++ = '+'; - } - - if (dplace <= 0) { - /* space: -dplace+dec->digits+2 */ - *cp++ = '0'; - *cp++ = '.'; - for (k = 0; k < -dplace; k++) { - *cp++ = '0'; - } - cp = coeff_to_string(cp, dec); - } - else if (dplace >= dec->digits) { - /* space: dplace */ - cp = coeff_to_string(cp, dec); - for (k = 0; k < dplace-dec->digits; k++) { - *cp++ = '0'; - } - } - else { - /* space: dec->digits+1 */ - cp = coeff_to_string_dot(cp, cp+dplace, dec); - } - - /* - * Conditions for printing an exponent: - * - * MPD_FMT_TOSCI, MPD_FMT_TOENG: only if ldigits != dplace - * MPD_FMT_FIXED: never (ldigits == dplace) - * MPD_FMT_EXP: always - */ - if (ldigits != dplace || flags&MPD_FMT_EXP) { - /* space: expdigits+2 */ - *cp++ = (flags&MPD_FMT_UPPER) ? 'E' : 'e'; - cp = exp_to_string(cp, ldigits-dplace); - } - } - - if (flags&MPD_FMT_PERCENT) { - *cp++ = '%'; - } - - assert(cp < decstring+mem); - assert(cp-decstring < MPD_SSIZE_MAX); - - *cp = '\0'; - *result = decstring; - return (mpd_ssize_t)(cp-decstring); -} - -char * -mpd_to_sci(const mpd_t *dec, int fmt) -{ - char *res; - int flags = MPD_FMT_TOSCI; - - flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; - (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE); - return res; -} - -char * -mpd_to_eng(const mpd_t *dec, int fmt) -{ - char *res; - int flags = MPD_FMT_TOENG; - - flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; - (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE); - return res; -} - -mpd_ssize_t -mpd_to_sci_size(char **res, const mpd_t *dec, int fmt) -{ - int flags = MPD_FMT_TOSCI; - - flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; - return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE); -} - -mpd_ssize_t -mpd_to_eng_size(char **res, const mpd_t *dec, int fmt) -{ - int flags = MPD_FMT_TOENG; - - flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER; - return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE); -} - -/* Copy a single UTF-8 char to dest. See: The Unicode Standard, version 5.2, - chapter 3.9: Well-formed UTF-8 byte sequences. */ -static int -_mpd_copy_utf8(char dest[5], const char *s) -{ - const unsigned char *cp = (const unsigned char *)s; - unsigned char lb, ub; - int count, i; - - - if (*cp == 0) { - /* empty string */ - dest[0] = '\0'; - return 0; - } - else if (*cp <= 0x7f) { - /* ascii */ - dest[0] = *cp; - dest[1] = '\0'; - return 1; - } - else if (0xc2 <= *cp && *cp <= 0xdf) { - lb = 0x80; ub = 0xbf; - count = 2; - } - else if (*cp == 0xe0) { - lb = 0xa0; ub = 0xbf; - count = 3; - } - else if (*cp <= 0xec) { - lb = 0x80; ub = 0xbf; - count = 3; - } - else if (*cp == 0xed) { - lb = 0x80; ub = 0x9f; - count = 3; - } - else if (*cp <= 0xef) { - lb = 0x80; ub = 0xbf; - count = 3; - } - else if (*cp == 0xf0) { - lb = 0x90; ub = 0xbf; - count = 4; - } - else if (*cp <= 0xf3) { - lb = 0x80; ub = 0xbf; - count = 4; - } - else if (*cp == 0xf4) { - lb = 0x80; ub = 0x8f; - count = 4; - } - else { - /* invalid */ - goto error; - } - - dest[0] = *cp++; - if (*cp < lb || ub < *cp) { - goto error; - } - dest[1] = *cp++; - for (i = 2; i < count; i++) { - if (*cp < 0x80 || 0xbf < *cp) { - goto error; - } - dest[i] = *cp++; - } - dest[i] = '\0'; - - return count; - -error: - dest[0] = '\0'; - return -1; -} - -int -mpd_validate_lconv(mpd_spec_t *spec) -{ - size_t n; -#if CHAR_MAX == SCHAR_MAX - const char *cp = spec->grouping; - while (*cp != '\0') { - if (*cp++ < 0) { - return -1; - } - } -#endif - n = strlen(spec->dot); - if (n == 0 || n > 4) { - return -1; - } - if (strlen(spec->sep) > 4) { - return -1; - } - - return 0; -} - -int -mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps) -{ - char *cp = (char *)fmt; - int have_align = 0, n; - - /* defaults */ - spec->min_width = 0; - spec->prec = -1; - spec->type = caps ? 'G' : 'g'; - spec->align = '>'; - spec->sign = '-'; - spec->dot = ""; - spec->sep = ""; - spec->grouping = ""; - - - /* presume that the first character is a UTF-8 fill character */ - if ((n = _mpd_copy_utf8(spec->fill, cp)) < 0) { - return 0; - } - - /* alignment directive, prefixed by a fill character */ - if (*cp && (*(cp+n) == '<' || *(cp+n) == '>' || - *(cp+n) == '=' || *(cp+n) == '^')) { - cp += n; - spec->align = *cp++; - have_align = 1; - } /* alignment directive */ - else { - /* default fill character */ - spec->fill[0] = ' '; - spec->fill[1] = '\0'; - if (*cp == '<' || *cp == '>' || - *cp == '=' || *cp == '^') { - spec->align = *cp++; - have_align = 1; - } - } - - /* sign formatting */ - if (*cp == '+' || *cp == '-' || *cp == ' ') { - spec->sign = *cp++; - } - - /* zero padding */ - if (*cp == '0') { - /* zero padding implies alignment, which should not be - * specified twice. */ - if (have_align) { - return 0; - } - spec->align = 'z'; - spec->fill[0] = *cp++; - spec->fill[1] = '\0'; - } - - /* minimum width */ - if (isdigit((unsigned char)*cp)) { - if (*cp == '0') { - return 0; - } - errno = 0; - spec->min_width = mpd_strtossize(cp, &cp, 10); - if (errno == ERANGE || errno == EINVAL) { - return 0; - } - } - - /* thousands separator */ - if (*cp == ',') { - spec->dot = "."; - spec->sep = ","; - spec->grouping = "\003\003"; - cp++; - } - - /* fraction digits or significant digits */ - if (*cp == '.') { - cp++; - if (!isdigit((unsigned char)*cp)) { - return 0; - } - errno = 0; - spec->prec = mpd_strtossize(cp, &cp, 10); - if (errno == ERANGE || errno == EINVAL) { - return 0; - } - } - - /* type */ - if (*cp == 'E' || *cp == 'e' || *cp == 'F' || *cp == 'f' || - *cp == 'G' || *cp == 'g' || *cp == '%') { - spec->type = *cp++; - } - else if (*cp == 'N' || *cp == 'n') { - /* locale specific conversion */ - struct lconv *lc; - /* separator has already been specified */ - if (*spec->sep) { - return 0; - } - spec->type = *cp++; - spec->type = (spec->type == 'N') ? 'G' : 'g'; - lc = localeconv(); - spec->dot = lc->decimal_point; - spec->sep = lc->thousands_sep; - spec->grouping = lc->grouping; - if (mpd_validate_lconv(spec) < 0) { - return 0; /* GCOV_NOT_REACHED */ - } - } - - /* check correctness */ - if (*cp != '\0') { - return 0; - } - - return 1; -} - -/* - * The following functions assume that spec->min_width <= MPD_MAX_PREC, which - * is made sure in mpd_qformat_spec. Then, even with a spec that inserts a - * four-byte separator after each digit, nbytes in the following struct - * cannot overflow. - */ - -/* Multibyte string */ -typedef struct { - mpd_ssize_t nbytes; /* length in bytes */ - mpd_ssize_t nchars; /* length in chars */ - mpd_ssize_t cur; /* current write index */ - char *data; -} mpd_mbstr_t; - -static inline void -_mpd_bcopy(char *dest, const char *src, mpd_ssize_t n) -{ - while (--n >= 0) { - dest[n] = src[n]; - } -} - -static inline void -_mbstr_copy_char(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n) -{ - dest->nbytes += n; - dest->nchars += (n > 0 ? 1 : 0); - dest->cur -= n; - - if (dest->data != NULL) { - _mpd_bcopy(dest->data+dest->cur, src, n); - } -} - -static inline void -_mbstr_copy_ascii(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n) -{ - dest->nbytes += n; - dest->nchars += n; - dest->cur -= n; - - if (dest->data != NULL) { - _mpd_bcopy(dest->data+dest->cur, src, n); - } -} - -static inline void -_mbstr_copy_pad(mpd_mbstr_t *dest, mpd_ssize_t n) -{ - dest->nbytes += n; - dest->nchars += n; - dest->cur -= n; - - if (dest->data != NULL) { - char *cp = dest->data + dest->cur; - while (--n >= 0) { - cp[n] = '0'; - } - } -} - -/* - * Copy a numeric string to dest->data, adding separators in the integer - * part according to spec->grouping. If leading zero padding is enabled - * and the result is smaller than spec->min_width, continue adding zeros - * and separators until the minimum width is reached. - * - * The final length of dest->data is stored in dest->nbytes. The number - * of UTF-8 characters is stored in dest->nchars. - * - * First run (dest->data == NULL): determine the length of the result - * string and store it in dest->nbytes. - * - * Second run (write to dest->data): data is written in chunks and in - * reverse order, starting with the rest of the numeric string. - */ -static void -_mpd_add_sep_dot(mpd_mbstr_t *dest, - const char *sign, /* location of optional sign */ - const char *src, mpd_ssize_t n_src, /* integer part and length */ - const char *dot, /* location of optional decimal point */ - const char *rest, mpd_ssize_t n_rest, /* remaining part and length */ - const mpd_spec_t *spec) -{ - mpd_ssize_t n_sep, n_sign, consume; - const char *g; - int pad = 0; - - n_sign = sign ? 1 : 0; - n_sep = (mpd_ssize_t)strlen(spec->sep); - /* Initial write index: set to location of '\0' in the output string. - * Irrelevant for the first run. */ - dest->cur = dest->nbytes; - dest->nbytes = dest->nchars = 0; - - _mbstr_copy_ascii(dest, rest, n_rest); - - if (dot) { - _mbstr_copy_char(dest, dot, (mpd_ssize_t)strlen(dot)); - } - - g = spec->grouping; - consume = *g; - while (1) { - /* If the group length is 0 or CHAR_MAX or greater than the - * number of source bytes, consume all remaining bytes. */ - if (*g == 0 || *g == CHAR_MAX || consume > n_src) { - consume = n_src; - } - n_src -= consume; - if (pad) { - _mbstr_copy_pad(dest, consume); - } - else { - _mbstr_copy_ascii(dest, src+n_src, consume); - } - - if (n_src == 0) { - /* Either the real source of intpart digits or the virtual - * source of padding zeros is exhausted. */ - if (spec->align == 'z' && - dest->nchars + n_sign < spec->min_width) { - /* Zero padding is set and length < min_width: - * Generate n_src additional characters. */ - n_src = spec->min_width - (dest->nchars + n_sign); - /* Next iteration: - * case *g == 0 || *g == CHAR_MAX: - * consume all padding characters - * case consume < g*: - * fill remainder of current group - * case consume == g* - * copying is a no-op */ - consume = *g - consume; - /* Switch on virtual source of zeros. */ - pad = 1; - continue; - } - break; - } - - if (n_sep > 0) { - /* If padding is switched on, separators are counted - * as padding characters. This rule does not apply if - * the separator would be the first character of the - * result string. */ - if (pad && n_src > 1) n_src -= 1; - _mbstr_copy_char(dest, spec->sep, n_sep); - } - - /* If non-NUL, use the next value for grouping. */ - if (*g && *(g+1)) g++; - consume = *g; - } - - if (sign) { - _mbstr_copy_ascii(dest, sign, 1); - } - - if (dest->data) { - dest->data[dest->nbytes] = '\0'; - } -} - -/* - * Convert a numeric-string to its locale-specific appearance. - * The string must have one of these forms: - * - * 1) [sign] digits [exponent-part] - * 2) [sign] digits '.' [digits] [exponent-part] - * - * Not allowed, since _mpd_to_string() never returns this form: - * - * 3) [sign] '.' digits [exponent-part] - * - * Input: result->data := original numeric string (ASCII) - * result->bytes := strlen(result->data) - * result->nchars := strlen(result->data) - * - * Output: result->data := modified or original string - * result->bytes := strlen(result->data) - * result->nchars := number of characters (possibly UTF-8) - */ -static int -_mpd_apply_lconv(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status) -{ - const char *sign = NULL, *intpart = NULL, *dot = NULL; - const char *rest, *dp; - char *decstring; - mpd_ssize_t n_int, n_rest; - - /* original numeric string */ - dp = result->data; - - /* sign */ - if (*dp == '+' || *dp == '-' || *dp == ' ') { - sign = dp++; - } - /* integer part */ - assert(isdigit((unsigned char)*dp)); - intpart = dp++; - while (isdigit((unsigned char)*dp)) { - dp++; - } - n_int = (mpd_ssize_t)(dp-intpart); - /* decimal point */ - if (*dp == '.') { - dp++; dot = spec->dot; - } - /* rest */ - rest = dp; - n_rest = result->nbytes - (mpd_ssize_t)(dp-result->data); - - if (dot == NULL && (*spec->sep == '\0' || *spec->grouping == '\0')) { - /* _mpd_add_sep_dot() would not change anything */ - return 1; - } - - /* Determine the size of the new decimal string after inserting the - * decimal point, optional separators and optional padding. */ - decstring = result->data; - result->data = NULL; - _mpd_add_sep_dot(result, sign, intpart, n_int, dot, - rest, n_rest, spec); - - result->data = mpd_alloc(result->nbytes+1, 1); - if (result->data == NULL) { - *status |= MPD_Malloc_error; - mpd_free(decstring); - return 0; - } - - /* Perform actual writes. */ - _mpd_add_sep_dot(result, sign, intpart, n_int, dot, - rest, n_rest, spec); - - mpd_free(decstring); - return 1; -} - -/* Add padding to the formatted string if necessary. */ -static int -_mpd_add_pad(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status) -{ - if (result->nchars < spec->min_width) { - mpd_ssize_t add_chars, add_bytes; - size_t lpad = 0, rpad = 0; - size_t n_fill, len, i, j; - char align = spec->align; - uint8_t err = 0; - char *cp; - - n_fill = strlen(spec->fill); - add_chars = (spec->min_width - result->nchars); - /* max value: MPD_MAX_PREC * 4 */ - add_bytes = add_chars * (mpd_ssize_t)n_fill; - - cp = result->data = mpd_realloc(result->data, - result->nbytes+add_bytes+1, - sizeof *result->data, &err); - if (err) { - *status |= MPD_Malloc_error; - mpd_free(result->data); - return 0; - } - - if (align == 'z') { - align = '='; - } - - if (align == '<') { - rpad = add_chars; - } - else if (align == '>' || align == '=') { - lpad = add_chars; - } - else { /* align == '^' */ - lpad = add_chars/2; - rpad = add_chars-lpad; - } - - len = result->nbytes; - if (align == '=' && (*cp == '-' || *cp == '+' || *cp == ' ')) { - /* leave sign in the leading position */ - cp++; len--; - } - - memmove(cp+n_fill*lpad, cp, len); - for (i = 0; i < lpad; i++) { - for (j = 0; j < n_fill; j++) { - cp[i*n_fill+j] = spec->fill[j]; - } - } - cp += (n_fill*lpad + len); - for (i = 0; i < rpad; i++) { - for (j = 0; j < n_fill; j++) { - cp[i*n_fill+j] = spec->fill[j]; - } - } - - result->nbytes += add_bytes; - result->nchars += add_chars; - result->data[result->nbytes] = '\0'; - } - - return 1; -} - -/* Round a number to prec digits. The adjusted exponent stays the same - or increases by one if rounding up crosses a power of ten boundary. - If result->digits would exceed MPD_MAX_PREC+1, MPD_Invalid_operation - is set and the result is NaN. */ -static inline void -_mpd_round(mpd_t *result, const mpd_t *a, mpd_ssize_t prec, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_ssize_t exp = a->exp + a->digits - prec; - - if (prec <= 0) { - mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_NOT_REACHED */ - return; /* GCOV_NOT_REACHED */ - } - if (mpd_isspecial(a) || mpd_iszero(a)) { - mpd_qcopy(result, a, status); /* GCOV_NOT_REACHED */ - return; /* GCOV_NOT_REACHED */ - } - - mpd_qrescale_fmt(result, a, exp, ctx, status); - if (result->digits > prec) { - mpd_qrescale_fmt(result, result, exp+1, ctx, status); - } -} - -/* - * Return the string representation of an mpd_t, formatted according to 'spec'. - * The format specification is assumed to be valid. Memory errors are indicated - * as usual. This function is quiet. - */ -char * -mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_uint_t dt[MPD_MINALLOC_MAX]; - mpd_t tmp = {MPD_STATIC|MPD_STATIC_DATA,0,0,0,MPD_MINALLOC_MAX,dt}; - mpd_ssize_t dplace = MPD_DEFAULT_DOTPLACE; - mpd_mbstr_t result; - mpd_spec_t stackspec; - char type = spec->type; - int flags = 0; - - - if (spec->min_width > MPD_MAX_PREC) { - *status |= MPD_Invalid_operation; - return NULL; - } - - if (isupper((unsigned char)type)) { - type = (char)tolower((unsigned char)type); - flags |= MPD_FMT_UPPER; - } - if (spec->sign == ' ') { - flags |= MPD_FMT_SIGN_SPACE; - } - else if (spec->sign == '+') { - flags |= MPD_FMT_SIGN_PLUS; - } - - if (mpd_isspecial(dec)) { - if (spec->align == 'z') { - stackspec = *spec; - stackspec.fill[0] = ' '; - stackspec.fill[1] = '\0'; - stackspec.align = '>'; - spec = &stackspec; - } - assert(strlen(spec->fill) == 1); /* annotation for scan-build */ - if (type == '%') { - flags |= MPD_FMT_PERCENT; - } - } - else { - uint32_t workstatus = 0; - mpd_ssize_t prec; - - switch (type) { - case 'g': flags |= MPD_FMT_TOSCI; break; - case 'e': flags |= MPD_FMT_EXP; break; - case '%': flags |= MPD_FMT_PERCENT; - if (!mpd_qcopy(&tmp, dec, status)) { - return NULL; - } - tmp.exp += 2; - dec = &tmp; - type = 'f'; /* fall through */ - case 'f': flags |= MPD_FMT_FIXED; break; - default: abort(); /* debug: GCOV_NOT_REACHED */ - } - - if (spec->prec >= 0) { - if (spec->prec > MPD_MAX_PREC) { - *status |= MPD_Invalid_operation; - goto error; - } - - switch (type) { - case 'g': - prec = (spec->prec == 0) ? 1 : spec->prec; - if (dec->digits > prec) { - _mpd_round(&tmp, dec, prec, ctx, - &workstatus); - dec = &tmp; - } - break; - case 'e': - if (mpd_iszero(dec)) { - dplace = 1-spec->prec; - } - else { - _mpd_round(&tmp, dec, spec->prec+1, ctx, - &workstatus); - dec = &tmp; - } - break; - case 'f': - mpd_qrescale(&tmp, dec, -spec->prec, ctx, - &workstatus); - dec = &tmp; - break; - } - } - - if (type == 'f') { - if (mpd_iszero(dec) && dec->exp > 0) { - mpd_qrescale(&tmp, dec, 0, ctx, &workstatus); - dec = &tmp; - } - } - - if (workstatus&MPD_Errors) { - *status |= (workstatus&MPD_Errors); - goto error; - } - } - - /* - * At this point, for all scaled or non-scaled decimals: - * 1) 1 <= digits <= MAX_PREC+1 - * 2) adjexp(scaled) = adjexp(orig) [+1] - * 3) case 'g': MIN_ETINY <= exp <= MAX_EMAX+1 - * case 'e': MIN_ETINY-MAX_PREC <= exp <= MAX_EMAX+1 - * case 'f': MIN_ETINY <= exp <= MAX_EMAX+1 - * 4) max memory alloc in _mpd_to_string: - * case 'g': MAX_PREC+36 - * case 'e': MAX_PREC+36 - * case 'f': 2*MPD_MAX_PREC+30 - */ - result.nbytes = _mpd_to_string(&result.data, dec, flags, dplace); - result.nchars = result.nbytes; - if (result.nbytes < 0) { - *status |= MPD_Malloc_error; - goto error; - } - - if (*spec->dot != '\0' && !mpd_isspecial(dec)) { - if (result.nchars > MPD_MAX_PREC+36) { - /* Since a group length of one is not explicitly - * disallowed, ensure that it is always possible to - * insert a four byte separator after each digit. */ - *status |= MPD_Invalid_operation; - mpd_free(result.data); - goto error; - } - if (!_mpd_apply_lconv(&result, spec, status)) { - goto error; - } - } - - if (spec->min_width) { - if (!_mpd_add_pad(&result, spec, status)) { - goto error; - } - } - - mpd_del(&tmp); - return result.data; - -error: - mpd_del(&tmp); - return NULL; -} - -char * -mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_spec_t spec; - - if (!mpd_parse_fmt_str(&spec, fmt, 1)) { - *status |= MPD_Invalid_operation; - return NULL; - } - - return mpd_qformat_spec(dec, &spec, ctx, status); -} - -/* - * The specification has a *condition* called Invalid_operation and an - * IEEE *signal* called Invalid_operation. The former corresponds to - * MPD_Invalid_operation, the latter to MPD_IEEE_Invalid_operation. - * MPD_IEEE_Invalid_operation comprises the following conditions: - * - * [MPD_Conversion_syntax, MPD_Division_impossible, MPD_Division_undefined, - * MPD_Fpu_error, MPD_Invalid_context, MPD_Invalid_operation, - * MPD_Malloc_error] - * - * In the following functions, 'flag' denotes the condition, 'signal' - * denotes the IEEE signal. - */ - -static const char *mpd_flag_string[MPD_NUM_FLAGS] = { - "Clamped", - "Conversion_syntax", - "Division_by_zero", - "Division_impossible", - "Division_undefined", - "Fpu_error", - "Inexact", - "Invalid_context", - "Invalid_operation", - "Malloc_error", - "Not_implemented", - "Overflow", - "Rounded", - "Subnormal", - "Underflow", -}; - -static const char *mpd_signal_string[MPD_NUM_FLAGS] = { - "Clamped", - "IEEE_Invalid_operation", - "Division_by_zero", - "IEEE_Invalid_operation", - "IEEE_Invalid_operation", - "IEEE_Invalid_operation", - "Inexact", - "IEEE_Invalid_operation", - "IEEE_Invalid_operation", - "IEEE_Invalid_operation", - "Not_implemented", - "Overflow", - "Rounded", - "Subnormal", - "Underflow", -}; - -/* print conditions to buffer, separated by spaces */ -int -mpd_snprint_flags(char *dest, int nmemb, uint32_t flags) -{ - char *cp; - int n, j; - - assert(nmemb >= MPD_MAX_FLAG_STRING); - - *dest = '\0'; cp = dest; - for (j = 0; j < MPD_NUM_FLAGS; j++) { - if (flags & (1U<<j)) { - n = snprintf(cp, nmemb, "%s ", mpd_flag_string[j]); - if (n < 0 || n >= nmemb) return -1; - cp += n; nmemb -= n; - } - } - - if (cp != dest) { - *(--cp) = '\0'; - } - - return (int)(cp-dest); -} - -/* print conditions to buffer, in list form */ -int -mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[]) -{ - char *cp; - int n, j; - - assert(nmemb >= MPD_MAX_FLAG_LIST); - if (flag_string == NULL) { - flag_string = mpd_flag_string; - } - - *dest = '['; - *(dest+1) = '\0'; - cp = dest+1; - --nmemb; - - for (j = 0; j < MPD_NUM_FLAGS; j++) { - if (flags & (1U<<j)) { - n = snprintf(cp, nmemb, "%s, ", flag_string[j]); - if (n < 0 || n >= nmemb) return -1; - cp += n; nmemb -= n; - } - } - - /* erase the last ", " */ - if (cp != dest+1) { - cp -= 2; - } - - *cp++ = ']'; - *cp = '\0'; - - return (int)(cp-dest); /* strlen, without NUL terminator */ -} - -/* print signals to buffer, in list form */ -int -mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[]) -{ - char *cp; - int n, j; - int ieee_invalid_done = 0; - - assert(nmemb >= MPD_MAX_SIGNAL_LIST); - if (signal_string == NULL) { - signal_string = mpd_signal_string; - } - - *dest = '['; - *(dest+1) = '\0'; - cp = dest+1; - --nmemb; - - for (j = 0; j < MPD_NUM_FLAGS; j++) { - uint32_t f = flags & (1U<<j); - if (f) { - if (f&MPD_IEEE_Invalid_operation) { - if (ieee_invalid_done) { - continue; - } - ieee_invalid_done = 1; - } - n = snprintf(cp, nmemb, "%s, ", signal_string[j]); - if (n < 0 || n >= nmemb) return -1; - cp += n; nmemb -= n; - } - } - - /* erase the last ", " */ - if (cp != dest+1) { - cp -= 2; - } - - *cp++ = ']'; - *cp = '\0'; - - return (int)(cp-dest); /* strlen, without NUL terminator */ -} - -/* The following two functions are mainly intended for debugging. */ -void -mpd_fprint(FILE *file, const mpd_t *dec) -{ - char *decstring; - - decstring = mpd_to_sci(dec, 1); - if (decstring != NULL) { - fprintf(file, "%s\n", decstring); - mpd_free(decstring); - } - else { - fputs("mpd_fprint: output error\n", file); /* GCOV_NOT_REACHED */ - } -} - -void -mpd_print(const mpd_t *dec) -{ - char *decstring; - - decstring = mpd_to_sci(dec, 1); - if (decstring != NULL) { - printf("%s\n", decstring); - mpd_free(decstring); - } - else { - fputs("mpd_fprint: output error\n", stderr); /* GCOV_NOT_REACHED */ - } -} diff --git a/Modules/_decimal/libmpdec/io.h b/Modules/_decimal/libmpdec/io.h deleted file mode 100644 index 79d7c05ce369c6..00000000000000 --- a/Modules/_decimal/libmpdec/io.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_IO_H_ -#define LIBMPDEC_IO_H_ - - -#include "mpdecimal.h" - -#include <stdint.h> - - -#if SIZE_MAX == MPD_SIZE_MAX - #define mpd_strtossize _mpd_strtossize -#else -#include <errno.h> - -static inline mpd_ssize_t -mpd_strtossize(const char *s, char **end, int base) -{ - int64_t retval; - - errno = 0; - retval = _mpd_strtossize(s, end, base); - if (errno == 0 && (retval > MPD_SSIZE_MAX || retval < MPD_SSIZE_MIN)) { - errno = ERANGE; - } - if (errno == ERANGE) { - return (retval < 0) ? MPD_SSIZE_MIN : MPD_SSIZE_MAX; - } - - return (mpd_ssize_t)retval; -} -#endif - - -#endif /* LIBMPDEC_IO_H_ */ diff --git a/Modules/_decimal/libmpdec/literature/REFERENCES.txt b/Modules/_decimal/libmpdec/literature/REFERENCES.txt deleted file mode 100644 index 9ed5782656a31b..00000000000000 --- a/Modules/_decimal/libmpdec/literature/REFERENCES.txt +++ /dev/null @@ -1,51 +0,0 @@ - - -This document contains links to the literature used in the process of -creating the library. The list is probably not complete. - - -Mike Cowlishaw: General Decimal Arithmetic Specification -http://speleotrove.com/decimal/decarith.html - - -Jean-Michel Muller: On the definition of ulp (x) -lara.inist.fr/bitstream/2332/518/1/LIP-RR2005-09.pdf - - -T. E. Hull, A. Abrham: Properly rounded variable precision square root -http://portal.acm.org/citation.cfm?id=214413 - - -T. E. Hull, A. Abrham: Variable precision exponential function -http://portal.acm.org/citation.cfm?id=6498 - - -Roman E. Maeder: Storage allocation for the Karatsuba integer multiplication -algorithm. http://www.springerlink.com/content/w15058mj6v59t565/ - - -J. M. Pollard: The fast Fourier transform in a finite field -http://www.ams.org/journals/mcom/1971-25-114/S0025-5718-1971-0301966-0/home.html - - -David H. Bailey: FFTs in External or Hierarchical Memory -http://crd.lbl.gov/~dhbailey/dhbpapers/ - - -W. Morven Gentleman: Matrix Multiplication and Fast Fourier Transforms -http://www.alcatel-lucent.com/bstj/vol47-1968/articles/bstj47-6-1099.pdf - - -Mikko Tommila: Apfloat documentation -http://www.apfloat.org/apfloat/2.41/apfloat.pdf - - -Joerg Arndt: "Matters Computational" -http://www.jjj.de/fxt/ - - -Karl Hasselstrom: Fast Division of Large Integers -www.treskal.com/kalle/exjobb/original-report.pdf - - - diff --git a/Modules/_decimal/libmpdec/literature/bignum.txt b/Modules/_decimal/libmpdec/literature/bignum.txt deleted file mode 100644 index f34ff67c612ab4..00000000000000 --- a/Modules/_decimal/libmpdec/literature/bignum.txt +++ /dev/null @@ -1,83 +0,0 @@ - - -Bignum support (Fast Number Theoretic Transform or FNT): -======================================================== - -Bignum arithmetic in libmpdec uses the scheme for fast convolution -of integer sequences from: - -J. M. Pollard: The fast Fourier transform in a finite field -http://www.ams.org/journals/mcom/1971-25-114/S0025-5718-1971-0301966-0/home.html - - -The transform in a finite field can be used for convolution in the same -way as the Fourier Transform. The main advantages of the Number Theoretic -Transform are that it is both exact and very memory efficient. - - -Convolution in pseudo-code: -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - fnt_convolute(a, b): - x = fnt(a) # forward transform of a - y = fnt(b) # forward transform of b - z = pairwise multiply x[i] and y[i] - result = inv_fnt(z) # backward transform of z. - - -Extending the maximum transform length (Chinese Remainder Theorem): -------------------------------------------------------------------- - -The maximum transform length is quite limited when using a single -prime field. However, it is possible to use multiple primes and -recover the result using the Chinese Remainder Theorem. - - -Multiplication in pseudo-code: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - _mpd_fntmul(u, v): - c1 = fnt_convolute(u, v, P1) # convolute modulo prime1 - c2 = fnt_convolute(u, v, P2) # convolute modulo prime2 - c3 = fnt_convolute(u, v, P3) # convolute modulo prime3 - result = crt3(c1, c2, c3) # Chinese Remainder Theorem - - -Optimized transform functions: ------------------------------- - -There are three different fnt() functions: - - std_fnt: "standard" decimation in frequency transform for array lengths - of 2**n. Performs well up to 1024 words. - - sixstep: Cache-friendly algorithm for array lengths of 2**n. Outperforms - std_fnt for large arrays. - - fourstep: Algorithm for array lengths of 3 * 2**n. Also cache friendly - in large parts. - - -List of bignum-only files: --------------------------- - -Functions from these files are only used in _mpd_fntmul(). - - umodarith.h -> fast low level routines for unsigned modular arithmetic - numbertheory.c -> routines for setting up the FNT - difradix2.c -> decimation in frequency transform, used as the - "base case" by the following three files: - - fnt.c -> standard transform for smaller arrays - sixstep.c -> transform large arrays of length 2**n - fourstep.c -> transform arrays of length 3 * 2**n - - convolute.c -> do the actual fast convolution, using one of - the three transform functions. - transpose.c -> transpositions needed for the sixstep algorithm. - crt.c -> Chinese Remainder Theorem: use information from three - transforms modulo three different primes to get the - final result. - - - diff --git a/Modules/_decimal/libmpdec/literature/fnt.py b/Modules/_decimal/libmpdec/literature/fnt.py deleted file mode 100644 index c1285a565db96b..00000000000000 --- a/Modules/_decimal/libmpdec/literature/fnt.py +++ /dev/null @@ -1,208 +0,0 @@ -# -# Copyright (c) 2008-2020 Stefan Krah. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# - - -###################################################################### -# This file lists and checks some of the constants and limits used # -# in libmpdec's Number Theoretic Transform. At the end of the file # -# there is an example function for the plain DFT transform. # -###################################################################### - - -# -# Number theoretic transforms are done in subfields of F(p). P[i] -# are the primes, D[i] = P[i] - 1 are highly composite and w[i] -# are the respective primitive roots of F(p). -# -# The strategy is to convolute two coefficients modulo all three -# primes, then use the Chinese Remainder Theorem on the three -# result arrays to recover the result in the usual base RADIX -# form. -# - -# ====================================================================== -# Primitive roots -# ====================================================================== - -# -# Verify primitive roots: -# -# For a prime field, r is a primitive root if and only if for all prime -# factors f of p-1, r**((p-1)/f) =/= 1 (mod p). -# -def prod(F, E): - """Check that the factorization of P-1 is correct. F is the list of - factors of P-1, E lists the number of occurrences of each factor.""" - x = 1 - for y, z in zip(F, E): - x *= y**z - return x - -def is_primitive_root(r, p, factors, exponents): - """Check if r is a primitive root of F(p).""" - if p != prod(factors, exponents) + 1: - return False - for f in factors: - q, control = divmod(p-1, f) - if control != 0: - return False - if pow(r, q, p) == 1: - return False - return True - - -# ================================================================= -# Constants and limits for the 64-bit version -# ================================================================= - -RADIX = 10**19 - -# Primes P1, P2 and P3: -P = [2**64-2**32+1, 2**64-2**34+1, 2**64-2**40+1] - -# P-1, highly composite. The transform length d is variable and -# must divide D = P-1. Since all D are divisible by 3 * 2**32, -# transform lengths can be 2**n or 3 * 2**n (where n <= 32). -D = [2**32 * 3 * (5 * 17 * 257 * 65537), - 2**34 * 3**2 * (7 * 11 * 31 * 151 * 331), - 2**40 * 3**2 * (5 * 7 * 13 * 17 * 241)] - -# Prime factors of P-1 and their exponents: -F = [(2,3,5,17,257,65537), (2,3,7,11,31,151,331), (2,3,5,7,13,17,241)] -E = [(32,1,1,1,1,1), (34,2,1,1,1,1,1), (40,2,1,1,1,1,1)] - -# Maximum transform length for 2**n. Above that only 3 * 2**31 -# or 3 * 2**32 are possible. -MPD_MAXTRANSFORM_2N = 2**32 - - -# Limits in the terminology of Pollard's paper: -m2 = (MPD_MAXTRANSFORM_2N * 3) // 2 # Maximum length of the smaller array. -M1 = M2 = RADIX-1 # Maximum value per single word. -L = m2 * M1 * M2 -P[0] * P[1] * P[2] > 2 * L - - -# Primitive roots of F(P1), F(P2) and F(P3): -w = [7, 10, 19] - -# The primitive roots are correct: -for i in range(3): - if not is_primitive_root(w[i], P[i], F[i], E[i]): - print("FAIL") - - -# ================================================================= -# Constants and limits for the 32-bit version -# ================================================================= - -RADIX = 10**9 - -# Primes P1, P2 and P3: -P = [2113929217, 2013265921, 1811939329] - -# P-1, highly composite. All D = P-1 are divisible by 3 * 2**25, -# allowing for transform lengths up to 3 * 2**25 words. -D = [2**25 * 3**2 * 7, - 2**27 * 3 * 5, - 2**26 * 3**3] - -# Prime factors of P-1 and their exponents: -F = [(2,3,7), (2,3,5), (2,3)] -E = [(25,2,1), (27,1,1), (26,3)] - -# Maximum transform length for 2**n. Above that only 3 * 2**24 or -# 3 * 2**25 are possible. -MPD_MAXTRANSFORM_2N = 2**25 - - -# Limits in the terminology of Pollard's paper: -m2 = (MPD_MAXTRANSFORM_2N * 3) // 2 # Maximum length of the smaller array. -M1 = M2 = RADIX-1 # Maximum value per single word. -L = m2 * M1 * M2 -P[0] * P[1] * P[2] > 2 * L - - -# Primitive roots of F(P1), F(P2) and F(P3): -w = [5, 31, 13] - -# The primitive roots are correct: -for i in range(3): - if not is_primitive_root(w[i], P[i], F[i], E[i]): - print("FAIL") - - -# ====================================================================== -# Example transform using a single prime -# ====================================================================== - -def ntt(lst, dir): - """Perform a transform on the elements of lst. len(lst) must - be 2**n or 3 * 2**n, where n <= 25. This is the slow DFT.""" - p = 2113929217 # prime - d = len(lst) # transform length - d_prime = pow(d, (p-2), p) # inverse of d - xi = (p-1)//d - w = 5 # primitive root of F(p) - r = pow(w, xi, p) # primitive root of the subfield - r_prime = pow(w, (p-1-xi), p) # inverse of r - if dir == 1: # forward transform - a = lst # input array - A = [0] * d # transformed values - for i in range(d): - s = 0 - for j in range(d): - s += a[j] * pow(r, i*j, p) - A[i] = s % p - return A - elif dir == -1: # backward transform - A = lst # input array - a = [0] * d # transformed values - for j in range(d): - s = 0 - for i in range(d): - s += A[i] * pow(r_prime, i*j, p) - a[j] = (d_prime * s) % p - return a - -def ntt_convolute(a, b): - """convolute arrays a and b.""" - assert(len(a) == len(b)) - x = ntt(a, 1) - y = ntt(b, 1) - for i in range(len(a)): - y[i] = y[i] * x[i] - r = ntt(y, -1) - return r - - -# Example: Two arrays representing 21 and 81 in little-endian: -a = [1, 2, 0, 0] -b = [1, 8, 0, 0] - -assert(ntt_convolute(a, b) == [1, 10, 16, 0]) -assert(21 * 81 == (1*10**0 + 10*10**1 + 16*10**2 + 0*10**3)) diff --git a/Modules/_decimal/libmpdec/literature/matrix-transform.txt b/Modules/_decimal/libmpdec/literature/matrix-transform.txt deleted file mode 100644 index 6e7ad7420909fd..00000000000000 --- a/Modules/_decimal/libmpdec/literature/matrix-transform.txt +++ /dev/null @@ -1,256 +0,0 @@ - - -(* Copyright (c) 2011-2020 Stefan Krah. All rights reserved. *) - - -The Matrix Fourier Transform: -============================= - -In libmpdec, the Matrix Fourier Transform [1] is called four-step transform -after a variant that appears in [2]. The algorithm requires that the input -array can be viewed as an R*C matrix. - -All operations are done modulo p. For readability, the proofs drop all -instances of (mod p). - - -Algorithm four-step (forward transform): ----------------------------------------- - - a := input array - d := len(a) = R * C - p := prime - w := primitive root of unity of the prime field - r := w**((p-1)/d) - A := output array - - 1) Apply a length R FNT to each column. - - 2) Multiply each matrix element (addressed by j*C+m) by r**(j*m). - - 3) Apply a length C FNT to each row. - - 4) Transpose the matrix. - - -Proof (forward transform): --------------------------- - - The algorithm can be derived starting from the regular definition of - the finite-field transform of length d: - - d-1 - ,---- - \ - A[k] = | a[l] * r**(k * l) - / - `---- - l = 0 - - - The sum can be rearranged into the sum of the sums of columns: - - C-1 R-1 - ,---- ,---- - \ \ - = | | a[i * C + j] * r**(k * (i * C + j)) - / / - `---- `---- - j = 0 i = 0 - - - Extracting a constant from the inner sum: - - C-1 R-1 - ,---- ,---- - \ \ - = | r**k*j * | a[i * C + j] * r**(k * i * C) - / / - `---- `---- - j = 0 i = 0 - - - Without any loss of generality, let k = n * R + m, - where n < C and m < R: - - C-1 R-1 - ,---- ,---- - \ \ - A[n*R+m] = | r**(R*n*j) * r**(m*j) * | a[i*C+j] * r**(R*C*n*i) * r**(C*m*i) - / / - `---- `---- - j = 0 i = 0 - - - Since r = w ** ((p-1) / (R*C)): - - a) r**(R*C*n*i) = w**((p-1)*n*i) = 1 - - b) r**(C*m*i) = w**((p-1) / R) ** (m*i) = r_R ** (m*i) - - c) r**(R*n*j) = w**((p-1) / C) ** (n*j) = r_C ** (n*j) - - r_R := root of the subfield of length R. - r_C := root of the subfield of length C. - - - C-1 R-1 - ,---- ,---- - \ \ - A[n*R+m] = | r_C**(n*j) * [ r**(m*j) * | a[i*C+j] * r_R**(m*i) ] - / ^ / - `---- | `---- 1) transform the columns - j = 0 | i = 0 - ^ | - | `-- 2) multiply - | - `-- 3) transform the rows - - - Note that the entire RHS is a function of n and m and that the results - for each pair (n, m) are stored in Fortran order. - - Let the term in square brackets be f(m, j). Step 1) and 2) precalculate - the term for all (m, j). After that, the original matrix is now a lookup - table with the mth element in the jth column at location m * C + j. - - Let the complete RHS be g(m, n). Step 3) does an in-place transform of - length n on all rows. After that, the original matrix is now a lookup - table with the mth element in the nth column at location m * C + n. - - But each (m, n) pair should be written to location n * R + m. Therefore, - step 4) transposes the result of step 3). - - - -Algorithm four-step (inverse transform): ----------------------------------------- - - A := input array - d := len(A) = R * C - p := prime - d' := d**(p-2) # inverse of d - w := primitive root of unity of the prime field - r := w**((p-1)/d) # root of the subfield - r' := w**((p-1) - (p-1)/d) # inverse of r - a := output array - - 0) View the matrix as a C*R matrix. - - 1) Transpose the matrix, producing an R*C matrix. - - 2) Apply a length C FNT to each row. - - 3) Multiply each matrix element (addressed by i*C+n) by r**(i*n). - - 4) Apply a length R FNT to each column. - - -Proof (inverse transform): --------------------------- - - The algorithm can be derived starting from the regular definition of - the finite-field inverse transform of length d: - - d-1 - ,---- - \ - a[k] = d' * | A[l] * r' ** (k * l) - / - `---- - l = 0 - - - The sum can be rearranged into the sum of the sums of columns. Note - that at this stage we still have a C*R matrix, so C denotes the number - of rows: - - R-1 C-1 - ,---- ,---- - \ \ - = d' * | | a[j * R + i] * r' ** (k * (j * R + i)) - / / - `---- `---- - i = 0 j = 0 - - - Extracting a constant from the inner sum: - - R-1 C-1 - ,---- ,---- - \ \ - = d' * | r' ** (k*i) * | a[j * R + i] * r' ** (k * j * R) - / / - `---- `---- - i = 0 j = 0 - - - Without any loss of generality, let k = m * C + n, - where m < R and n < C: - - R-1 C-1 - ,---- ,---- - \ \ - A[m*C+n] = d' * | r' ** (C*m*i) * r' ** (n*i) * | a[j*R+i] * r' ** (R*C*m*j) * r' ** (R*n*j) - / / - `---- `---- - i = 0 j = 0 - - - Since r' = w**((p-1) - (p-1)/d) and d = R*C: - - a) r' ** (R*C*m*j) = w**((p-1)*R*C*m*j - (p-1)*m*j) = 1 - - b) r' ** (C*m*i) = w**((p-1)*C - (p-1)/R) ** (m*i) = r_R' ** (m*i) - - c) r' ** (R*n*j) = r_C' ** (n*j) - - d) d' = d**(p-2) = (R*C) ** (p-2) = R**(p-2) * C**(p-2) = R' * C' - - r_R' := inverse of the root of the subfield of length R. - r_C' := inverse of the root of the subfield of length C. - R' := inverse of R - C' := inverse of C - - - R-1 C-1 - ,---- ,---- 2) transform the rows of a^T - \ \ - A[m*C+n] = R' * | r_R' ** (m*i) * [ r' ** (n*i) * C' * | a[j*R+i] * r_C' ** (n*j) ] - / ^ / ^ - `---- | `---- | - i = 0 | j = 0 | - ^ | `-- 1) Transpose input matrix - | `-- 3) multiply to address elements by - | i * C + j - `-- 3) transform the columns - - - - Note that the entire RHS is a function of m and n and that the results - for each pair (m, n) are stored in C order. - - Let the term in square brackets be f(n, i). Without step 1), the sum - would perform a length C transform on the columns of the input matrix. - This is a) inefficient and b) the results are needed in C order, so - step 1) exchanges rows and columns. - - Step 2) and 3) precalculate f(n, i) for all (n, i). After that, the - original matrix is now a lookup table with the ith element in the nth - column at location i * C + n. - - Let the complete RHS be g(m, n). Step 4) does an in-place transform of - length m on all columns. After that, the original matrix is now a lookup - table with the mth element in the nth column at location m * C + n, - which means that all A[k] = A[m * C + n] are in the correct order. - - --- - - [1] Joerg Arndt: "Matters Computational" - http://www.jjj.de/fxt/ - [2] David H. Bailey: FFTs in External or Hierarchical Memory - http://crd.lbl.gov/~dhbailey/dhbpapers/ - - - diff --git a/Modules/_decimal/libmpdec/literature/mulmod-64.txt b/Modules/_decimal/libmpdec/literature/mulmod-64.txt deleted file mode 100644 index fa967bf95e3030..00000000000000 --- a/Modules/_decimal/libmpdec/literature/mulmod-64.txt +++ /dev/null @@ -1,127 +0,0 @@ - - -(* Copyright (c) 2011-2020 Stefan Krah. All rights reserved. *) - - -========================================================================== - Calculate (a * b) % p using special primes -========================================================================== - -A description of the algorithm can be found in the apfloat manual by -Tommila [1]. - - -Definitions: ------------- - -In the whole document, "==" stands for "is congruent with". - -Result of a * b in terms of high/low words: - - (1) hi * 2**64 + lo = a * b - -Special primes: - - (2) p = 2**64 - z + 1, where z = 2**n - -Single step modular reduction: - - (3) R(hi, lo) = hi * z - hi + lo - - -Strategy: ---------- - - a) Set (hi, lo) to the result of a * b. - - b) Set (hi', lo') to the result of R(hi, lo). - - c) Repeat step b) until 0 <= hi' * 2**64 + lo' < 2*p. - - d) If the result is less than p, return lo'. Otherwise return lo' - p. - - -The reduction step b) preserves congruence: -------------------------------------------- - - hi * 2**64 + lo == hi * z - hi + lo (mod p) - - Proof: - ~~~~~~ - - hi * 2**64 + lo = (2**64 - z + 1) * hi + z * hi - hi + lo - - = p * hi + z * hi - hi + lo - - == z * hi - hi + lo (mod p) - - -Maximum numbers of step b): ---------------------------- - -# To avoid unnecessary formalism, define: - -def R(hi, lo, z): - return divmod(hi * z - hi + lo, 2**64) - -# For simplicity, assume hi=2**64-1, lo=2**64-1 after the -# initial multiplication a * b. This is of course impossible -# but certainly covers all cases. - -# Then, for p1: -hi=2**64-1; lo=2**64-1; z=2**32 -p1 = 2**64 - z + 1 - -hi, lo = R(hi, lo, z) # First reduction -hi, lo = R(hi, lo, z) # Second reduction -hi * 2**64 + lo < 2 * p1 # True - -# For p2: -hi=2**64-1; lo=2**64-1; z=2**34 -p2 = 2**64 - z + 1 - -hi, lo = R(hi, lo, z) # First reduction -hi, lo = R(hi, lo, z) # Second reduction -hi, lo = R(hi, lo, z) # Third reduction -hi * 2**64 + lo < 2 * p2 # True - -# For p3: -hi=2**64-1; lo=2**64-1; z=2**40 -p3 = 2**64 - z + 1 - -hi, lo = R(hi, lo, z) # First reduction -hi, lo = R(hi, lo, z) # Second reduction -hi, lo = R(hi, lo, z) # Third reduction -hi * 2**64 + lo < 2 * p3 # True - - -Step d) preserves congruence and yields a result < p: ------------------------------------------------------ - - Case hi = 0: - - Case lo < p: trivial. - - Case lo >= p: - - lo == lo - p (mod p) # result is congruent - - p <= lo < 2*p -> 0 <= lo - p < p # result is in the correct range - - Case hi = 1: - - p < 2**64 /\ 2**64 + lo < 2*p -> lo < p # lo is always less than p - - 2**64 + lo == 2**64 + (lo - p) (mod p) # result is congruent - - = lo - p # exactly the same value as the previous RHS - # in uint64_t arithmetic. - - p < 2**64 + lo < 2*p -> 0 < 2**64 + (lo - p) < p # correct range - - - -[1] http://www.apfloat.org/apfloat/2.40/apfloat.pdf - - - diff --git a/Modules/_decimal/libmpdec/literature/mulmod-ppro.txt b/Modules/_decimal/libmpdec/literature/mulmod-ppro.txt deleted file mode 100644 index ba804e4b4e7864..00000000000000 --- a/Modules/_decimal/libmpdec/literature/mulmod-ppro.txt +++ /dev/null @@ -1,269 +0,0 @@ - - -(* Copyright (c) 2011-2020 Stefan Krah. All rights reserved. *) - - -======================================================================== - Calculate (a * b) % p using the 80-bit x87 FPU -======================================================================== - -A description of the algorithm can be found in the apfloat manual by -Tommila [1]. - -The proof follows an argument made by Granlund/Montgomery in [2]. - - -Definitions and assumptions: ----------------------------- - -The 80-bit extended precision format uses 64 bits for the significand: - - (1) F = 64 - -The modulus is prime and less than 2**31: - - (2) 2 <= p < 2**31 - -The factors are less than p: - - (3) 0 <= a < p - (4) 0 <= b < p - -The product a * b is less than 2**62 and is thus exact in 64 bits: - - (5) n = a * b - -The product can be represented in terms of quotient and remainder: - - (6) n = q * p + r - -Using (3), (4) and the fact that p is prime, the remainder is always -greater than zero: - - (7) 0 <= q < p /\ 1 <= r < p - - -Strategy: ---------- - -Precalculate the 80-bit long double inverse of p, with a maximum -relative error of 2**(1-F): - - (8) pinv = (long double)1.0 / p - -Calculate an estimate for q = floor(n/p). The multiplication has another -maximum relative error of 2**(1-F): - - (9) qest = n * pinv - -If we can show that q < qest < q+1, then trunc(qest) = q. It is then -easy to recover the remainder r. The complete algorithm is: - - a) Set the control word to 64-bit precision and truncation mode. - - b) n = a * b # Calculate exact product. - - c) qest = n * pinv # Calculate estimate for the quotient. - - d) q = (qest+2**63)-2**63 # Truncate qest to the exact quotient. - - f) r = n - q * p # Calculate remainder. - - -Proof for q < qest < q+1: -------------------------- - -Using the cumulative error, the error bounds for qest are: - - n n * (1 + 2**(1-F))**2 - (9) --------------------- <= qest <= --------------------- - p * (1 + 2**(1-F))**2 p - - - Lemma 1: - -------- - n q * p + r - (10) q < --------------------- = --------------------- - p * (1 + 2**(1-F))**2 p * (1 + 2**(1-F))**2 - - - Proof: - ~~~~~~ - - (I) q * p * (1 + 2**(1-F))**2 < q * p + r - - (II) q * p * 2**(2-F) + q * p * 2**(2-2*F) < r - - Using (1) and (7), it is sufficient to show that: - - (III) q * p * 2**(-62) + q * p * 2**(-126) < 1 <= r - - (III) can easily be verified by substituting the largest possible - values p = 2**31-1 and q = 2**31-2. - - The critical cases occur when r = 1, n = m * p + 1. These cases - can be exhaustively verified with a test program. - - - Lemma 2: - -------- - - n * (1 + 2**(1-F))**2 (q * p + r) * (1 + 2**(1-F))**2 - (11) --------------------- = ------------------------------- < q + 1 - p p - - Proof: - ~~~~~~ - - (I) (q * p + r) + (q * p + r) * 2**(2-F) + (q * p + r) * 2**(2-2*F) < q * p + p - - (II) (q * p + r) * 2**(2-F) + (q * p + r) * 2**(2-2*F) < p - r - - Using (1) and (7), it is sufficient to show that: - - (III) (q * p + r) * 2**(-62) + (q * p + r) * 2**(-126) < 1 <= p - r - - (III) can easily be verified by substituting the largest possible - values p = 2**31-1, q = 2**31-2 and r = 2**31-2. - - The critical cases occur when r = (p - 1), n = m * p - 1. These cases - can be exhaustively verified with a test program. - - -[1] http://www.apfloat.org/apfloat/2.40/apfloat.pdf -[2] http://gmplib.org/~tege/divcnst-pldi94.pdf - [Section 7: "Use of floating point"] - - - -(* Coq proof for (10) and (11) *) - -Require Import ZArith. -Require Import QArith. -Require Import Qpower. -Require Import Qabs. -Require Import Psatz. - -Open Scope Q_scope. - - -Ltac qreduce T := - rewrite <- (Qred_correct (T)); simpl (Qred (T)). - -Theorem Qlt_move_right : - forall x y z:Q, x + z < y <-> x < y - z. -Proof. - intros. - split. - intros. - psatzl Q. - intros. - psatzl Q. -Qed. - -Theorem Qlt_mult_by_z : - forall x y z:Q, 0 < z -> (x < y <-> x * z < y * z). -Proof. - intros. - split. - intros. - apply Qmult_lt_compat_r. trivial. trivial. - intros. - rewrite <- (Qdiv_mult_l x z). rewrite <- (Qdiv_mult_l y z). - apply Qmult_lt_compat_r. - apply Qlt_shift_inv_l. - trivial. psatzl Q. trivial. psatzl Q. psatzl Q. -Qed. - -Theorem Qle_mult_quad : - forall (a b c d:Q), - 0 <= a -> a <= c -> - 0 <= b -> b <= d -> - a * b <= c * d. - intros. - psatz Q. -Qed. - - -Theorem q_lt_qest: - forall (p q r:Q), - (0 < p) -> (p <= (2#1)^31 - 1) -> - (0 <= q) -> (q <= p - 1) -> - (1 <= r) -> (r <= p - 1) -> - q < (q * p + r) / (p * (1 + (2#1)^(-63))^2). -Proof. - intros. - rewrite Qlt_mult_by_z with (z := (p * (1 + (2#1)^(-63))^2)). - - unfold Qdiv. - rewrite <- Qmult_assoc. - rewrite (Qmult_comm (/ (p * (1 + (2 # 1) ^ (-63)) ^ 2)) (p * (1 + (2 # 1) ^ (-63)) ^ 2)). - rewrite Qmult_inv_r. - rewrite Qmult_1_r. - - assert (q * (p * (1 + (2 # 1) ^ (-63)) ^ 2) == q * p + (q * p) * ((2 # 1) ^ (-62) + (2 # 1) ^ (-126))). - qreduce ((1 + (2 # 1) ^ (-63)) ^ 2). - qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)). - ring_simplify. - reflexivity. - rewrite H5. - - rewrite Qplus_comm. - rewrite Qlt_move_right. - ring_simplify (q * p + r - q * p). - qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)). - - apply Qlt_le_trans with (y := 1). - rewrite Qlt_mult_by_z with (z := 85070591730234615865843651857942052864 # 18446744073709551617). - ring_simplify. - - apply Qle_lt_trans with (y := ((2 # 1) ^ 31 - (2#1)) * ((2 # 1) ^ 31 - 1)). - apply Qle_mult_quad. - assumption. psatzl Q. psatzl Q. psatzl Q. psatzl Q. psatzl Q. assumption. psatzl Q. psatzl Q. -Qed. - -Theorem qest_lt_qplus1: - forall (p q r:Q), - (0 < p) -> (p <= (2#1)^31 - 1) -> - (0 <= q) -> (q <= p - 1) -> - (1 <= r) -> (r <= p - 1) -> - ((q * p + r) * (1 + (2#1)^(-63))^2) / p < q + 1. -Proof. - intros. - rewrite Qlt_mult_by_z with (z := p). - - unfold Qdiv. - rewrite <- Qmult_assoc. - rewrite (Qmult_comm (/ p) p). - rewrite Qmult_inv_r. - rewrite Qmult_1_r. - - assert ((q * p + r) * (1 + (2 # 1) ^ (-63)) ^ 2 == q * p + r + (q * p + r) * ((2 # 1) ^ (-62) + (2 # 1) ^ (-126))). - qreduce ((1 + (2 # 1) ^ (-63)) ^ 2). - qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)). - ring_simplify. reflexivity. - rewrite H5. - - rewrite <- Qplus_assoc. rewrite <- Qplus_comm. rewrite Qlt_move_right. - ring_simplify ((q + 1) * p - q * p). - - rewrite <- Qplus_comm. rewrite Qlt_move_right. - - apply Qlt_le_trans with (y := 1). - qreduce ((2 # 1) ^ (-62) + (2 # 1) ^ (-126)). - - rewrite Qlt_mult_by_z with (z := 85070591730234615865843651857942052864 # 18446744073709551617). - ring_simplify. - - ring_simplify in H0. - apply Qle_lt_trans with (y := (2147483646 # 1) * (2147483647 # 1) + (2147483646 # 1)). - - apply Qplus_le_compat. - apply Qle_mult_quad. - assumption. psatzl Q. auto with qarith. assumption. psatzl Q. - auto with qarith. auto with qarith. - psatzl Q. psatzl Q. assumption. -Qed. - - - diff --git a/Modules/_decimal/libmpdec/literature/six-step.txt b/Modules/_decimal/libmpdec/literature/six-step.txt deleted file mode 100644 index 852d5b0df8bf37..00000000000000 --- a/Modules/_decimal/libmpdec/literature/six-step.txt +++ /dev/null @@ -1,63 +0,0 @@ - - -(* Copyright (c) 2011-2020 Stefan Krah. All rights reserved. *) - - -The Six Step Transform: -======================= - -In libmpdec, the six-step transform is the Matrix Fourier Transform (See -matrix-transform.txt) in disguise. It is called six-step transform after -a variant that appears in [1]. The algorithm requires that the input -array can be viewed as an R*C matrix. - - -Algorithm six-step (forward transform): ---------------------------------------- - - 1a) Transpose the matrix. - - 1b) Apply a length R FNT to each row. - - 1c) Transpose the matrix. - - 2) Multiply each matrix element (addressed by j*C+m) by r**(j*m). - - 3) Apply a length C FNT to each row. - - 4) Transpose the matrix. - -Note that steps 1a) - 1c) are exactly equivalent to step 1) of the Matrix -Fourier Transform. For large R, it is faster to transpose twice and do -a transform on the rows than to perform a column transpose directly. - - - -Algorithm six-step (inverse transform): ---------------------------------------- - - 0) View the matrix as a C*R matrix. - - 1) Transpose the matrix, producing an R*C matrix. - - 2) Apply a length C FNT to each row. - - 3) Multiply each matrix element (addressed by i*C+n) by r**(i*n). - - 4a) Transpose the matrix. - - 4b) Apply a length R FNT to each row. - - 4c) Transpose the matrix. - -Again, steps 4a) - 4c) are equivalent to step 4) of the Matrix Fourier -Transform. - - - --- - - [1] David H. Bailey: FFTs in External or Hierarchical Memory - http://crd.lbl.gov/~dhbailey/dhbpapers/ - - diff --git a/Modules/_decimal/libmpdec/literature/umodarith.lisp b/Modules/_decimal/libmpdec/literature/umodarith.lisp deleted file mode 100644 index d71f074a26dcca..00000000000000 --- a/Modules/_decimal/libmpdec/literature/umodarith.lisp +++ /dev/null @@ -1,692 +0,0 @@ -; -; Copyright (c) 2008-2020 Stefan Krah. All rights reserved. -; -; Redistribution and use in source and binary forms, with or without -; modification, are permitted provided that the following conditions -; are met: -; -; 1. Redistributions of source code must retain the above copyright -; notice, this list of conditions and the following disclaimer. -; -; 2. Redistributions in binary form must reproduce the above copyright -; notice, this list of conditions and the following disclaimer in the -; documentation and/or other materials provided with the distribution. -; -; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND -; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -; SUCH DAMAGE. -; - - -(in-package "ACL2") - -(include-book "arithmetic/top-with-meta" :dir :system) -(include-book "arithmetic-2/floor-mod/floor-mod" :dir :system) - - -;; ===================================================================== -;; Proofs for several functions in umodarith.h -;; ===================================================================== - - - -;; ===================================================================== -;; Helper theorems -;; ===================================================================== - -(defthm elim-mod-m<x<2*m - (implies (and (<= m x) - (< x (* 2 m)) - (rationalp x) (rationalp m)) - (equal (mod x m) - (+ x (- m))))) - -(defthm modaux-1a - (implies (and (< x m) (< 0 x) (< 0 m) - (rationalp x) (rationalp m)) - (equal (mod (- x) m) - (+ (- x) m)))) - -(defthm modaux-1b - (implies (and (< (- x) m) (< x 0) (< 0 m) - (rationalp x) (rationalp m)) - (equal (mod x m) - (+ x m))) - :hints (("Goal" :use ((:instance modaux-1a - (x (- x))))))) - -(defthm modaux-1c - (implies (and (< x m) (< 0 x) (< 0 m) - (rationalp x) (rationalp m)) - (equal (mod x m) - x))) - -(defthm modaux-2a - (implies (and (< 0 b) (< b m) - (natp x) (natp b) (natp m) - (< (mod (+ b x) m) b)) - (equal (mod (+ (- m) b x) m) - (+ (- m) b (mod x m))))) - -(defthm modaux-2b - (implies (and (< 0 b) (< b m) - (natp x) (natp b) (natp m) - (< (mod (+ b x) m) b)) - (equal (mod (+ b x) m) - (+ (- m) b (mod x m)))) - :hints (("Goal" :use (modaux-2a)))) - -(defthm linear-mod-1 - (implies (and (< x m) (< b m) - (natp x) (natp b) - (rationalp m)) - (equal (< x (mod (+ (- b) x) m)) - (< x b))) - :hints (("Goal" :use ((:instance modaux-1a - (x (+ b (- x)))))))) - -(defthm linear-mod-2 - (implies (and (< 0 b) (< b m) - (natp x) (natp b) - (natp m)) - (equal (< (mod x m) - (mod (+ (- b) x) m)) - (< (mod x m) b)))) - -(defthm linear-mod-3 - (implies (and (< x m) (< b m) - (natp x) (natp b) - (rationalp m)) - (equal (<= b (mod (+ b x) m)) - (< (+ b x) m))) - :hints (("Goal" :use ((:instance elim-mod-m<x<2*m - (x (+ b x))))))) - -(defthm modaux-2c - (implies (and (< 0 b) (< b m) - (natp x) (natp b) (natp m) - (<= b (mod (+ b x) m))) - (equal (mod (+ b x) m) - (+ b (mod x m)))) - :hints (("Subgoal *1/8''" :use (linear-mod-3)))) - -(defthmd modaux-2d - (implies (and (< x m) (< 0 x) (< 0 m) - (< (- m) b) (< b 0) (rationalp m) - (<= x (mod (+ b x) m)) - (rationalp x) (rationalp b)) - (equal (+ (- m) (mod (+ b x) m)) - (+ b x))) - :hints (("Goal" :cases ((<= 0 (+ b x)))) - ("Subgoal 2'" :use ((:instance modaux-1b - (x (+ b x))))))) - -(defthm mod-m-b - (implies (and (< 0 x) (< 0 b) (< 0 m) - (< x b) (< b m) - (natp x) (natp b) (natp m)) - (equal (mod (+ (mod (- x) m) b) m) - (mod (- x) b)))) - - -;; ===================================================================== -;; addmod, submod -;; ===================================================================== - -(defun addmod (a b m base) - (let* ((s (mod (+ a b) base)) - (s (if (< s a) (mod (- s m) base) s)) - (s (if (>= s m) (mod (- s m) base) s))) - s)) - -(defthmd addmod-correct - (implies (and (< 0 m) (< m base) - (< a m) (<= b m) - (natp m) (natp base) - (natp a) (natp b)) - (equal (addmod a b m base) - (mod (+ a b) m))) - :hints (("Goal" :cases ((<= base (+ a b)))) - ("Subgoal 2.1'" :use ((:instance elim-mod-m<x<2*m - (x (+ a b))))))) - -(defun submod (a b m base) - (let* ((d (mod (- a b) base)) - (d (if (< a d) (mod (+ d m) base) d))) - d)) - -(defthmd submod-aux1 - (implies (and (< a (mod (+ a (- b)) base)) - (< 0 base) (< a base) (<= b base) - (natp base) (natp a) (natp b)) - (< a b)) - :rule-classes :forward-chaining) - -(defthmd submod-aux2 - (implies (and (<= (mod (+ a (- b)) base) a) - (< 0 base) (< a base) (< b base) - (natp base) (natp a) (natp b)) - (<= b a)) - :rule-classes :forward-chaining) - -(defthmd submod-correct - (implies (and (< 0 m) (< m base) - (< a m) (<= b m) - (natp m) (natp base) - (natp a) (natp b)) - (equal (submod a b m base) - (mod (- a b) m))) - :hints (("Goal" :cases ((<= base (+ a b)))) - ("Subgoal 2.2" :use ((:instance submod-aux1))) - ("Subgoal 2.2'''" :cases ((and (< 0 (+ a (- b) m)) - (< (+ a (- b) m) m)))) - ("Subgoal 2.1" :use ((:instance submod-aux2))) - ("Subgoal 1.2" :use ((:instance submod-aux1))) - ("Subgoal 1.1" :use ((:instance submod-aux2))))) - - -(defun submod-2 (a b m base) - (let* ((d (mod (- a b) base)) - (d (if (< a b) (mod (+ d m) base) d))) - d)) - -(defthm submod-2-correct - (implies (and (< 0 m) (< m base) - (< a m) (<= b m) - (natp m) (natp base) - (natp a) (natp b)) - (equal (submod-2 a b m base) - (mod (- a b) m))) - :hints (("Subgoal 2'" :cases ((and (< 0 (+ a (- b) m)) - (< (+ a (- b) m) m)))))) - - -;; ========================================================================= -;; ext-submod is correct -;; ========================================================================= - -; a < 2*m, b < 2*m -(defun ext-submod (a b m base) - (let* ((a (if (>= a m) (- a m) a)) - (b (if (>= b m) (- b m) b)) - (d (mod (- a b) base)) - (d (if (< a b) (mod (+ d m) base) d))) - d)) - -; a < 2*m, b < 2*m -(defun ext-submod-2 (a b m base) - (let* ((a (mod a m)) - (b (mod b m)) - (d (mod (- a b) base)) - (d (if (< a b) (mod (+ d m) base) d))) - d)) - -(defthmd ext-submod-ext-submod-2-equal - (implies (and (< 0 m) (< m base) - (< a (* 2 m)) (< b (* 2 m)) - (natp m) (natp base) - (natp a) (natp b)) - (equal (ext-submod a b m base) - (ext-submod-2 a b m base)))) - -(defthmd ext-submod-2-correct - (implies (and (< 0 m) (< m base) - (< a (* 2 m)) (< b (* 2 m)) - (natp m) (natp base) - (natp a) (natp b)) - (equal (ext-submod-2 a b m base) - (mod (- a b) m)))) - - -;; ========================================================================= -;; dw-reduce is correct -;; ========================================================================= - -(defun dw-reduce (hi lo m base) - (let* ((r1 (mod hi m)) - (r2 (mod (+ (* r1 base) lo) m))) - r2)) - -(defthmd dw-reduce-correct - (implies (and (< 0 m) (< m base) - (< hi base) (< lo base) - (natp m) (natp base) - (natp hi) (natp lo)) - (equal (dw-reduce hi lo m base) - (mod (+ (* hi base) lo) m)))) - -(defthmd <=-multiply-both-sides-by-z - (implies (and (rationalp x) (rationalp y) - (< 0 z) (rationalp z)) - (equal (<= x y) - (<= (* z x) (* z y))))) - -(defthmd dw-reduce-aux1 - (implies (and (< 0 m) (< m base) - (natp m) (natp base) - (< lo base) (natp lo) - (< x m) (natp x)) - (< (+ lo (* base x)) (* base m))) - :hints (("Goal" :cases ((<= (+ x 1) m))) - ("Subgoal 1''" :cases ((<= (* base (+ x 1)) (* base m)))) - ("subgoal 1.2" :use ((:instance <=-multiply-both-sides-by-z - (x (+ 1 x)) - (y m) - (z base)))))) - -(defthm dw-reduce-aux2 - (implies (and (< x (* base m)) - (< 0 m) (< m base) - (natp m) (natp base) (natp x)) - (< (floor x m) base))) - -;; This is the necessary condition for using _mpd_div_words(). -(defthmd dw-reduce-second-quotient-fits-in-single-word - (implies (and (< 0 m) (< m base) - (< hi base) (< lo base) - (natp m) (natp base) - (natp hi) (natp lo) - (equal r1 (mod hi m))) - (< (floor (+ (* r1 base) lo) m) - base)) - :hints (("Goal" :cases ((< r1 m))) - ("Subgoal 1''" :cases ((< (+ lo (* base (mod hi m))) (* base m)))) - ("Subgoal 1.2" :use ((:instance dw-reduce-aux1 - (x (mod hi m))))))) - - -;; ========================================================================= -;; dw-submod is correct -;; ========================================================================= - -(defun dw-submod (a hi lo m base) - (let* ((r (dw-reduce hi lo m base)) - (d (mod (- a r) base)) - (d (if (< a r) (mod (+ d m) base) d))) - d)) - -(defthmd dw-submod-aux1 - (implies (and (natp a) (< 0 m) (natp m) - (natp x) (equal r (mod x m))) - (equal (mod (- a x) m) - (mod (- a r) m)))) - -(defthmd dw-submod-correct - (implies (and (< 0 m) (< m base) - (natp a) (< a m) - (< hi base) (< lo base) - (natp m) (natp base) - (natp hi) (natp lo)) - (equal (dw-submod a hi lo m base) - (mod (- a (+ (* base hi) lo)) m))) - :hints (("Goal" :in-theory (disable dw-reduce) - :use ((:instance dw-submod-aux1 - (x (+ lo (* base hi))) - (r (dw-reduce hi lo m base))) - (:instance dw-reduce-correct))))) - - -;; ========================================================================= -;; ANSI C arithmetic for uint64_t -;; ========================================================================= - -(defun add (a b) - (mod (+ a b) - (expt 2 64))) - -(defun sub (a b) - (mod (- a b) - (expt 2 64))) - -(defun << (w n) - (mod (* w (expt 2 n)) - (expt 2 64))) - -(defun >> (w n) - (floor w (expt 2 n))) - -;; join upper and lower half of a double word, yielding a 128 bit number -(defun join (hi lo) - (+ (* (expt 2 64) hi) lo)) - - -;; ============================================================================= -;; Fast modular reduction -;; ============================================================================= - -;; These are the three primes used in the Number Theoretic Transform. -;; A fast modular reduction scheme exists for all of them. -(defmacro p1 () - (+ (expt 2 64) (- (expt 2 32)) 1)) - -(defmacro p2 () - (+ (expt 2 64) (- (expt 2 34)) 1)) - -(defmacro p3 () - (+ (expt 2 64) (- (expt 2 40)) 1)) - - -;; reduce the double word number hi*2**64 + lo (mod p1) -(defun simple-mod-reduce-p1 (hi lo) - (+ (* (expt 2 32) hi) (- hi) lo)) - -;; reduce the double word number hi*2**64 + lo (mod p2) -(defun simple-mod-reduce-p2 (hi lo) - (+ (* (expt 2 34) hi) (- hi) lo)) - -;; reduce the double word number hi*2**64 + lo (mod p3) -(defun simple-mod-reduce-p3 (hi lo) - (+ (* (expt 2 40) hi) (- hi) lo)) - - -; ---------------------------------------------------------- -; The modular reductions given above are correct -; ---------------------------------------------------------- - -(defthmd congruence-p1-aux - (equal (* (expt 2 64) hi) - (+ (* (p1) hi) - (* (expt 2 32) hi) - (- hi)))) - -(defthmd congruence-p2-aux - (equal (* (expt 2 64) hi) - (+ (* (p2) hi) - (* (expt 2 34) hi) - (- hi)))) - -(defthmd congruence-p3-aux - (equal (* (expt 2 64) hi) - (+ (* (p3) hi) - (* (expt 2 40) hi) - (- hi)))) - -(defthmd mod-augment - (implies (and (rationalp x) - (rationalp y) - (rationalp m)) - (equal (mod (+ x y) m) - (mod (+ x (mod y m)) m)))) - -(defthmd simple-mod-reduce-p1-congruent - (implies (and (integerp hi) - (integerp lo)) - (equal (mod (simple-mod-reduce-p1 hi lo) (p1)) - (mod (join hi lo) (p1)))) - :hints (("Goal''" :use ((:instance congruence-p1-aux) - (:instance mod-augment - (m (p1)) - (x (+ (- hi) lo (* (expt 2 32) hi))) - (y (* (p1) hi))))))) - -(defthmd simple-mod-reduce-p2-congruent - (implies (and (integerp hi) - (integerp lo)) - (equal (mod (simple-mod-reduce-p2 hi lo) (p2)) - (mod (join hi lo) (p2)))) - :hints (("Goal''" :use ((:instance congruence-p2-aux) - (:instance mod-augment - (m (p2)) - (x (+ (- hi) lo (* (expt 2 34) hi))) - (y (* (p2) hi))))))) - -(defthmd simple-mod-reduce-p3-congruent - (implies (and (integerp hi) - (integerp lo)) - (equal (mod (simple-mod-reduce-p3 hi lo) (p3)) - (mod (join hi lo) (p3)))) - :hints (("Goal''" :use ((:instance congruence-p3-aux) - (:instance mod-augment - (m (p3)) - (x (+ (- hi) lo (* (expt 2 40) hi))) - (y (* (p3) hi))))))) - - -; --------------------------------------------------------------------- -; We need a number less than 2*p, so that we can use the trick from -; elim-mod-m<x<2*m for the final reduction. -; For p1, two modular reductions are sufficient, for p2 and p3 three. -; --------------------------------------------------------------------- - -;; p1: the first reduction is less than 2**96 -(defthmd simple-mod-reduce-p1-<-2**96 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (natp hi) (natp lo)) - (< (simple-mod-reduce-p1 hi lo) - (expt 2 96)))) - -;; p1: the second reduction is less than 2*p1 -(defthmd simple-mod-reduce-p1-<-2*p1 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (< (join hi lo) (expt 2 96)) - (natp hi) (natp lo)) - (< (simple-mod-reduce-p1 hi lo) - (* 2 (p1))))) - - -;; p2: the first reduction is less than 2**98 -(defthmd simple-mod-reduce-p2-<-2**98 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (natp hi) (natp lo)) - (< (simple-mod-reduce-p2 hi lo) - (expt 2 98)))) - -;; p2: the second reduction is less than 2**69 -(defthmd simple-mod-reduce-p2-<-2*69 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (< (join hi lo) (expt 2 98)) - (natp hi) (natp lo)) - (< (simple-mod-reduce-p2 hi lo) - (expt 2 69)))) - -;; p3: the third reduction is less than 2*p2 -(defthmd simple-mod-reduce-p2-<-2*p2 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (< (join hi lo) (expt 2 69)) - (natp hi) (natp lo)) - (< (simple-mod-reduce-p2 hi lo) - (* 2 (p2))))) - - -;; p3: the first reduction is less than 2**104 -(defthmd simple-mod-reduce-p3-<-2**104 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (natp hi) (natp lo)) - (< (simple-mod-reduce-p3 hi lo) - (expt 2 104)))) - -;; p3: the second reduction is less than 2**81 -(defthmd simple-mod-reduce-p3-<-2**81 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (< (join hi lo) (expt 2 104)) - (natp hi) (natp lo)) - (< (simple-mod-reduce-p3 hi lo) - (expt 2 81)))) - -;; p3: the third reduction is less than 2*p3 -(defthmd simple-mod-reduce-p3-<-2*p3 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (< (join hi lo) (expt 2 81)) - (natp hi) (natp lo)) - (< (simple-mod-reduce-p3 hi lo) - (* 2 (p3))))) - - -; ------------------------------------------------------------------------- -; The simple modular reductions, adapted for compiler friendly C -; ------------------------------------------------------------------------- - -(defun mod-reduce-p1 (hi lo) - (let* ((y hi) - (x y) - (hi (>> hi 32)) - (x (sub lo x)) - (hi (if (> x lo) (+ hi -1) hi)) - (y (<< y 32)) - (lo (add y x)) - (hi (if (< lo y) (+ hi 1) hi))) - (+ (* hi (expt 2 64)) lo))) - -(defun mod-reduce-p2 (hi lo) - (let* ((y hi) - (x y) - (hi (>> hi 30)) - (x (sub lo x)) - (hi (if (> x lo) (+ hi -1) hi)) - (y (<< y 34)) - (lo (add y x)) - (hi (if (< lo y) (+ hi 1) hi))) - (+ (* hi (expt 2 64)) lo))) - -(defun mod-reduce-p3 (hi lo) - (let* ((y hi) - (x y) - (hi (>> hi 24)) - (x (sub lo x)) - (hi (if (> x lo) (+ hi -1) hi)) - (y (<< y 40)) - (lo (add y x)) - (hi (if (< lo y) (+ hi 1) hi))) - (+ (* hi (expt 2 64)) lo))) - - -; ------------------------------------------------------------------------- -; The compiler friendly versions are equal to the simple versions -; ------------------------------------------------------------------------- - -(defthm mod-reduce-aux1 - (implies (and (<= 0 a) (natp a) (natp m) - (< (- m) b) (<= b 0) - (integerp b) - (< (mod (+ b a) m) - (mod a m))) - (equal (mod (+ b a) m) - (+ b (mod a m)))) - :hints (("Subgoal 2" :use ((:instance modaux-1b - (x (+ a b))))))) - -(defthm mod-reduce-aux2 - (implies (and (<= 0 a) (natp a) (natp m) - (< b m) (natp b) - (< (mod (+ b a) m) - (mod a m))) - (equal (+ m (mod (+ b a) m)) - (+ b (mod a m))))) - - -(defthm mod-reduce-aux3 - (implies (and (< 0 a) (natp a) (natp m) - (< (- m) b) (< b 0) - (integerp b) - (<= (mod a m) - (mod (+ b a) m))) - (equal (+ (- m) (mod (+ b a) m)) - (+ b (mod a m)))) - :hints (("Subgoal 1.2'" :use ((:instance modaux-1b - (x b)))) - ("Subgoal 1''" :use ((:instance modaux-2d - (x I)))))) - - -(defthm mod-reduce-aux4 - (implies (and (< 0 a) (natp a) (natp m) - (< b m) (natp b) - (<= (mod a m) - (mod (+ b a) m))) - (equal (mod (+ b a) m) - (+ b (mod a m))))) - - -(defthm mod-reduce-p1==simple-mod-reduce-p1 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (natp hi) (natp lo)) - (equal (mod-reduce-p1 hi lo) - (simple-mod-reduce-p1 hi lo))) - :hints (("Goal" :in-theory (disable expt) - :cases ((< 0 hi))) - ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 32) hi))))) - ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 32) hi))))) - ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 32) hi))))) - ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 32) hi))))))) - - -(defthm mod-reduce-p2==simple-mod-reduce-p2 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (natp hi) (natp lo)) - (equal (mod-reduce-p2 hi lo) - (simple-mod-reduce-p2 hi lo))) - :hints (("Goal" :cases ((< 0 hi))) - ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 34) hi))))) - ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 34) hi))))) - ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 34) hi))))) - ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 34) hi))))))) - - -(defthm mod-reduce-p3==simple-mod-reduce-p3 - (implies (and (< hi (expt 2 64)) - (< lo (expt 2 64)) - (natp hi) (natp lo)) - (equal (mod-reduce-p3 hi lo) - (simple-mod-reduce-p3 hi lo))) - :hints (("Goal" :cases ((< 0 hi))) - ("Subgoal 1.2.2'" :use ((:instance mod-reduce-aux1 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 40) hi))))) - ("Subgoal 1.2.1'" :use ((:instance mod-reduce-aux3 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 40) hi))))) - ("Subgoal 1.1.2'" :use ((:instance mod-reduce-aux2 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 40) hi))))) - ("Subgoal 1.1.1'" :use ((:instance mod-reduce-aux4 - (m (expt 2 64)) - (b (+ (- HI) LO)) - (a (* (expt 2 40) hi))))))) - - - diff --git a/Modules/_decimal/libmpdec/mpalloc.c b/Modules/_decimal/libmpdec/mpalloc.c deleted file mode 100644 index 5871d5c0f53519..00000000000000 --- a/Modules/_decimal/libmpdec/mpalloc.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "mpalloc.h" -#include "typearith.h" - - -#if defined(_MSC_VER) - #pragma warning(disable : 4232) -#endif - - -/* Guaranteed minimum allocation for a coefficient. May be changed once - at program start using mpd_setminalloc(). */ -mpd_ssize_t MPD_MINALLOC = MPD_MINALLOC_MIN; - -/* Custom allocation and free functions */ -void *(* mpd_mallocfunc)(size_t size) = malloc; -void *(* mpd_reallocfunc)(void *ptr, size_t size) = realloc; -void *(* mpd_callocfunc)(size_t nmemb, size_t size) = calloc; -void (* mpd_free)(void *ptr) = free; - - -/* emulate calloc if it is not available */ -void * -mpd_callocfunc_em(size_t nmemb, size_t size) -{ - void *ptr; - size_t req; - mpd_size_t overflow; - - req = mul_size_t_overflow((mpd_size_t)nmemb, (mpd_size_t)size, - &overflow); - if (overflow) { - return NULL; - } - - ptr = mpd_mallocfunc(req); - if (ptr == NULL) { - return NULL; - } - /* used on uint32_t or uint64_t */ - memset(ptr, 0, req); - - return ptr; -} - - -/* malloc with overflow checking */ -void * -mpd_alloc(mpd_size_t nmemb, mpd_size_t size) -{ - mpd_size_t req, overflow; - - req = mul_size_t_overflow(nmemb, size, &overflow); - if (overflow) { - return NULL; - } - - return mpd_mallocfunc(req); -} - -/* calloc with overflow checking */ -void * -mpd_calloc(mpd_size_t nmemb, mpd_size_t size) -{ - mpd_size_t overflow; - - (void)mul_size_t_overflow(nmemb, size, &overflow); - if (overflow) { - return NULL; - } - - return mpd_callocfunc(nmemb, size); -} - -/* realloc with overflow checking */ -void * -mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err) -{ - void *new; - mpd_size_t req, overflow; - - req = mul_size_t_overflow(nmemb, size, &overflow); - if (overflow) { - *err = 1; - return ptr; - } - - new = mpd_reallocfunc(ptr, req); - if (new == NULL) { - *err = 1; - return ptr; - } - - return new; -} - -/* struct hack malloc with overflow checking */ -void * -mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size) -{ - mpd_size_t req, overflow; - - req = mul_size_t_overflow(nmemb, size, &overflow); - if (overflow) { - return NULL; - } - - req = add_size_t_overflow(req, struct_size, &overflow); - if (overflow) { - return NULL; - } - - return mpd_mallocfunc(req); -} - - -/* Allocate a new decimal with a coefficient of length 'nwords'. In case - of an error the return value is NULL. */ -mpd_t * -mpd_qnew_size(mpd_ssize_t nwords) -{ - mpd_t *result; - - nwords = (nwords < MPD_MINALLOC) ? MPD_MINALLOC : nwords; - - result = mpd_alloc(1, sizeof *result); - if (result == NULL) { - return NULL; - } - - result->data = mpd_alloc(nwords, sizeof *result->data); - if (result->data == NULL) { - mpd_free(result); - return NULL; - } - - result->flags = 0; - result->exp = 0; - result->digits = 0; - result->len = 0; - result->alloc = nwords; - - return result; -} - -/* Allocate a new decimal with a coefficient of length MPD_MINALLOC. - In case of an error the return value is NULL. */ -mpd_t * -mpd_qnew(void) -{ - return mpd_qnew_size(MPD_MINALLOC); -} - -/* Allocate new decimal. Caller can check for NULL or MPD_Malloc_error. - Raises on error. */ -mpd_t * -mpd_new(mpd_context_t *ctx) -{ - mpd_t *result; - - result = mpd_qnew(); - if (result == NULL) { - mpd_addstatus_raise(ctx, MPD_Malloc_error); - } - return result; -} - -/* - * Input: 'result' is a static mpd_t with a static coefficient. - * Assumption: 'nwords' >= result->alloc. - * - * Resize the static coefficient to a larger dynamic one and copy the - * existing data. If successful, the value of 'result' is unchanged. - * Otherwise, set 'result' to NaN and update 'status' with MPD_Malloc_error. - */ -int -mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) -{ - mpd_uint_t *p = result->data; - - assert(nwords >= result->alloc); - - result->data = mpd_alloc(nwords, sizeof *result->data); - if (result->data == NULL) { - result->data = p; - mpd_set_qnan(result); - mpd_set_positive(result); - result->exp = result->digits = result->len = 0; - *status |= MPD_Malloc_error; - return 0; - } - - memcpy(result->data, p, result->alloc * (sizeof *result->data)); - result->alloc = nwords; - mpd_set_dynamic_data(result); - return 1; -} - -/* - * Input: 'result' is a static mpd_t with a static coefficient. - * - * Convert the coefficient to a dynamic one that is initialized to zero. If - * malloc fails, set 'result' to NaN and update 'status' with MPD_Malloc_error. - */ -int -mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) -{ - mpd_uint_t *p = result->data; - - result->data = mpd_calloc(nwords, sizeof *result->data); - if (result->data == NULL) { - result->data = p; - mpd_set_qnan(result); - mpd_set_positive(result); - result->exp = result->digits = result->len = 0; - *status |= MPD_Malloc_error; - return 0; - } - - result->alloc = nwords; - mpd_set_dynamic_data(result); - - return 1; -} - -/* - * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient. - * Resize the coefficient to length 'nwords': - * Case nwords > result->alloc: - * If realloc is successful: - * 'result' has a larger coefficient but the same value. Return 1. - * Otherwise: - * Set 'result' to NaN, update status with MPD_Malloc_error and return 0. - * Case nwords < result->alloc: - * If realloc is successful: - * 'result' has a smaller coefficient. result->len is undefined. Return 1. - * Otherwise (unlikely): - * 'result' is unchanged. Reuse the now oversized coefficient. Return 1. - */ -int -mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) -{ - uint8_t err = 0; - - result->data = mpd_realloc(result->data, nwords, sizeof *result->data, &err); - if (!err) { - result->alloc = nwords; - } - else if (nwords > result->alloc) { - mpd_set_qnan(result); - mpd_set_positive(result); - result->exp = result->digits = result->len = 0; - *status |= MPD_Malloc_error; - return 0; - } - - return 1; -} - -/* - * Input: 'result' is a static mpd_t with a static coefficient. - * Assumption: 'nwords' >= result->alloc. - * - * Resize the static coefficient to a larger dynamic one and copy the - * existing data. - * - * On failure the value of 'result' is unchanged. - */ -int -mpd_switch_to_dyn_cxx(mpd_t *result, mpd_ssize_t nwords) -{ - assert(nwords >= result->alloc); - - mpd_uint_t *data = mpd_alloc(nwords, sizeof *result->data); - if (data == NULL) { - return 0; - } - - memcpy(data, result->data, result->alloc * (sizeof *result->data)); - result->data = data; - result->alloc = nwords; - mpd_set_dynamic_data(result); - return 1; -} - -/* - * Input: 'result' is a static or a dynamic mpd_t with a dynamic coefficient. - * Resize the coefficient to length 'nwords': - * Case nwords > result->alloc: - * If realloc is successful: - * 'result' has a larger coefficient but the same value. Return 1. - * Otherwise: - * 'result' has a the same coefficient. Return 0. - * Case nwords < result->alloc: - * If realloc is successful: - * 'result' has a smaller coefficient. result->len is undefined. Return 1. - * Otherwise (unlikely): - * 'result' is unchanged. Reuse the now oversized coefficient. Return 1. - */ -int -mpd_realloc_dyn_cxx(mpd_t *result, mpd_ssize_t nwords) -{ - uint8_t err = 0; - - mpd_uint_t *p = mpd_realloc(result->data, nwords, sizeof *result->data, &err); - if (!err) { - result->data = p; - result->alloc = nwords; - } - else if (nwords > result->alloc) { - return 0; - } - - return 1; -} diff --git a/Modules/_decimal/libmpdec/mpalloc.h b/Modules/_decimal/libmpdec/mpalloc.h deleted file mode 100644 index 22650044218241..00000000000000 --- a/Modules/_decimal/libmpdec/mpalloc.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_MPALLOC_H_ -#define LIBMPDEC_MPALLOC_H_ - - -#include "mpdecimal.h" - -#include <stdint.h> - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -int mpd_switch_to_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status); -int mpd_switch_to_dyn_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status); -int mpd_realloc_dyn(mpd_t *result, mpd_ssize_t nwords, uint32_t *status); - -int mpd_switch_to_dyn_cxx(mpd_t *result, mpd_ssize_t nwords); -int mpd_realloc_dyn_cxx(mpd_t *result, mpd_ssize_t nwords); - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_MPALLOC_H_ */ diff --git a/Modules/_decimal/libmpdec/mpdecimal.c b/Modules/_decimal/libmpdec/mpdecimal.c deleted file mode 100644 index 959934bda7a449..00000000000000 --- a/Modules/_decimal/libmpdec/mpdecimal.c +++ /dev/null @@ -1,9015 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> -#include <limits.h> -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "basearith.h" -#include "bits.h" -#include "constants.h" -#include "convolute.h" -#include "crt.h" -#include "mpalloc.h" -#include "typearith.h" - -#ifdef PPRO - #if defined(_MSC_VER) - #include <float.h> - #pragma float_control(precise, on) - #pragma fenv_access(on) - #elif !defined(__OpenBSD__) && !defined(__NetBSD__) - /* C99 */ - #include <fenv.h> - #pragma STDC FENV_ACCESS ON - #endif -#endif - - -/* Disable warning that is part of -Wextra since gcc 7.0. */ -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && __GNUC__ >= 7 - #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" -#endif - - -#if defined(_MSC_VER) - #define ALWAYS_INLINE __forceinline -#elif defined (__IBMC__) || defined(LEGACY_COMPILER) - #define ALWAYS_INLINE - #undef inline - #define inline -#else - #ifdef TEST_COVERAGE - #define ALWAYS_INLINE - #else - #define ALWAYS_INLINE inline __attribute__ ((always_inline)) - #endif -#endif - -/* ClangCL claims to support 128-bit int, but doesn't */ -#if defined(__SIZEOF_INT128__) && defined(__clang__) && defined(_MSC_VER) -#undef __SIZEOF_INT128__ -#endif - - - -#define MPD_NEWTONDIV_CUTOFF 1024L - -#define MPD_NEW_STATIC(name, flags, exp, digits, len) \ - mpd_uint_t name##_data[MPD_MINALLOC_MAX]; \ - mpd_t name = {flags|MPD_STATIC|MPD_STATIC_DATA, exp, digits, \ - len, MPD_MINALLOC_MAX, name##_data} - -#define MPD_NEW_CONST(name, flags, exp, digits, len, alloc, initval) \ - mpd_uint_t name##_data[alloc] = {initval}; \ - mpd_t name = {flags|MPD_STATIC|MPD_CONST_DATA, exp, digits, \ - len, alloc, name##_data} - -#define MPD_NEW_SHARED(name, a) \ - mpd_t name = {(a->flags&~MPD_DATAFLAGS)|MPD_STATIC|MPD_SHARED_DATA, \ - a->exp, a->digits, a->len, a->alloc, a->data} - - -static mpd_uint_t data_one[1] = {1}; -static mpd_uint_t data_zero[1] = {0}; -static const mpd_t one = {MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, data_one}; -static const mpd_t minus_one = {MPD_NEG|MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, - data_one}; -static const mpd_t zero = {MPD_STATIC|MPD_CONST_DATA, 0, 1, 1, 1, data_zero}; - -static inline void _mpd_check_exp(mpd_t *dec, const mpd_context_t *ctx, - uint32_t *status); -static void _settriple(mpd_t *result, uint8_t sign, mpd_uint_t a, - mpd_ssize_t exp); -static inline mpd_ssize_t _mpd_real_size(mpd_uint_t *data, mpd_ssize_t size); - -static int _mpd_cmp_abs(const mpd_t *a, const mpd_t *b); - -static void _mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status); -static inline void _mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status); -static void _mpd_base_ndivmod(mpd_t *q, mpd_t *r, const mpd_t *a, - const mpd_t *b, uint32_t *status); -static inline void _mpd_qpow_uint(mpd_t *result, const mpd_t *base, - mpd_uint_t exp, uint8_t resultsign, - const mpd_context_t *ctx, uint32_t *status); - -static mpd_uint_t mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n); - - -/******************************************************************************/ -/* Version */ -/******************************************************************************/ - -const char * -mpd_version(void) -{ - return MPD_VERSION; -} - - -/******************************************************************************/ -/* Performance critical inline functions */ -/******************************************************************************/ - -#ifdef CONFIG_64 -/* Digits in a word, primarily useful for the most significant word. */ -ALWAYS_INLINE int -mpd_word_digits(mpd_uint_t word) -{ - if (word < mpd_pow10[9]) { - if (word < mpd_pow10[4]) { - if (word < mpd_pow10[2]) { - return (word < mpd_pow10[1]) ? 1 : 2; - } - return (word < mpd_pow10[3]) ? 3 : 4; - } - if (word < mpd_pow10[6]) { - return (word < mpd_pow10[5]) ? 5 : 6; - } - if (word < mpd_pow10[8]) { - return (word < mpd_pow10[7]) ? 7 : 8; - } - return 9; - } - if (word < mpd_pow10[14]) { - if (word < mpd_pow10[11]) { - return (word < mpd_pow10[10]) ? 10 : 11; - } - if (word < mpd_pow10[13]) { - return (word < mpd_pow10[12]) ? 12 : 13; - } - return 14; - } - if (word < mpd_pow10[18]) { - if (word < mpd_pow10[16]) { - return (word < mpd_pow10[15]) ? 15 : 16; - } - return (word < mpd_pow10[17]) ? 17 : 18; - } - - return (word < mpd_pow10[19]) ? 19 : 20; -} -#else -ALWAYS_INLINE int -mpd_word_digits(mpd_uint_t word) -{ - if (word < mpd_pow10[4]) { - if (word < mpd_pow10[2]) { - return (word < mpd_pow10[1]) ? 1 : 2; - } - return (word < mpd_pow10[3]) ? 3 : 4; - } - if (word < mpd_pow10[6]) { - return (word < mpd_pow10[5]) ? 5 : 6; - } - if (word < mpd_pow10[8]) { - return (word < mpd_pow10[7]) ? 7 : 8; - } - - return (word < mpd_pow10[9]) ? 9 : 10; -} -#endif - - -/* Adjusted exponent */ -ALWAYS_INLINE mpd_ssize_t -mpd_adjexp(const mpd_t *dec) -{ - return (dec->exp + dec->digits) - 1; -} - -/* Etiny */ -ALWAYS_INLINE mpd_ssize_t -mpd_etiny(const mpd_context_t *ctx) -{ - return ctx->emin - (ctx->prec - 1); -} - -/* Etop: used for folding down in IEEE clamping */ -ALWAYS_INLINE mpd_ssize_t -mpd_etop(const mpd_context_t *ctx) -{ - return ctx->emax - (ctx->prec - 1); -} - -/* Most significant word */ -ALWAYS_INLINE mpd_uint_t -mpd_msword(const mpd_t *dec) -{ - assert(dec->len > 0); - return dec->data[dec->len-1]; -} - -/* Most significant digit of a word */ -inline mpd_uint_t -mpd_msd(mpd_uint_t word) -{ - int n; - - n = mpd_word_digits(word); - return word / mpd_pow10[n-1]; -} - -/* Least significant digit of a word */ -ALWAYS_INLINE mpd_uint_t -mpd_lsd(mpd_uint_t word) -{ - return word % 10; -} - -/* Coefficient size needed to store 'digits' */ -mpd_ssize_t -mpd_digits_to_size(mpd_ssize_t digits) -{ - mpd_ssize_t q, r; - - _mpd_idiv_word(&q, &r, digits, MPD_RDIGITS); - return (r == 0) ? q : q+1; -} - -/* Number of digits in the exponent. Not defined for MPD_SSIZE_MIN. */ -inline int -mpd_exp_digits(mpd_ssize_t exp) -{ - exp = (exp < 0) ? -exp : exp; - return mpd_word_digits(exp); -} - -/* Canonical */ -ALWAYS_INLINE int -mpd_iscanonical(const mpd_t *dec) -{ - (void)dec; - return 1; -} - -/* Finite */ -ALWAYS_INLINE int -mpd_isfinite(const mpd_t *dec) -{ - return !(dec->flags & MPD_SPECIAL); -} - -/* Infinite */ -ALWAYS_INLINE int -mpd_isinfinite(const mpd_t *dec) -{ - return dec->flags & MPD_INF; -} - -/* NaN */ -ALWAYS_INLINE int -mpd_isnan(const mpd_t *dec) -{ - return dec->flags & (MPD_NAN|MPD_SNAN); -} - -/* Negative */ -ALWAYS_INLINE int -mpd_isnegative(const mpd_t *dec) -{ - return dec->flags & MPD_NEG; -} - -/* Positive */ -ALWAYS_INLINE int -mpd_ispositive(const mpd_t *dec) -{ - return !(dec->flags & MPD_NEG); -} - -/* qNaN */ -ALWAYS_INLINE int -mpd_isqnan(const mpd_t *dec) -{ - return dec->flags & MPD_NAN; -} - -/* Signed */ -ALWAYS_INLINE int -mpd_issigned(const mpd_t *dec) -{ - return dec->flags & MPD_NEG; -} - -/* sNaN */ -ALWAYS_INLINE int -mpd_issnan(const mpd_t *dec) -{ - return dec->flags & MPD_SNAN; -} - -/* Special */ -ALWAYS_INLINE int -mpd_isspecial(const mpd_t *dec) -{ - return dec->flags & MPD_SPECIAL; -} - -/* Zero */ -ALWAYS_INLINE int -mpd_iszero(const mpd_t *dec) -{ - return !mpd_isspecial(dec) && mpd_msword(dec) == 0; -} - -/* Test for zero when specials have been ruled out already */ -ALWAYS_INLINE int -mpd_iszerocoeff(const mpd_t *dec) -{ - return mpd_msword(dec) == 0; -} - -/* Normal */ -inline int -mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx) -{ - if (mpd_isspecial(dec)) return 0; - if (mpd_iszerocoeff(dec)) return 0; - - return mpd_adjexp(dec) >= ctx->emin; -} - -/* Subnormal */ -inline int -mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx) -{ - if (mpd_isspecial(dec)) return 0; - if (mpd_iszerocoeff(dec)) return 0; - - return mpd_adjexp(dec) < ctx->emin; -} - -/* Odd word */ -ALWAYS_INLINE int -mpd_isoddword(mpd_uint_t word) -{ - return word & 1; -} - -/* Odd coefficient */ -ALWAYS_INLINE int -mpd_isoddcoeff(const mpd_t *dec) -{ - return mpd_isoddword(dec->data[0]); -} - -/* 0 if dec is positive, 1 if dec is negative */ -ALWAYS_INLINE uint8_t -mpd_sign(const mpd_t *dec) -{ - return dec->flags & MPD_NEG; -} - -/* 1 if dec is positive, -1 if dec is negative */ -ALWAYS_INLINE int -mpd_arith_sign(const mpd_t *dec) -{ - return 1 - 2 * mpd_isnegative(dec); -} - -/* Radix */ -ALWAYS_INLINE long -mpd_radix(void) -{ - return 10; -} - -/* Dynamic decimal */ -ALWAYS_INLINE int -mpd_isdynamic(const mpd_t *dec) -{ - return !(dec->flags & MPD_STATIC); -} - -/* Static decimal */ -ALWAYS_INLINE int -mpd_isstatic(const mpd_t *dec) -{ - return dec->flags & MPD_STATIC; -} - -/* Data of decimal is dynamic */ -ALWAYS_INLINE int -mpd_isdynamic_data(const mpd_t *dec) -{ - return !(dec->flags & MPD_DATAFLAGS); -} - -/* Data of decimal is static */ -ALWAYS_INLINE int -mpd_isstatic_data(const mpd_t *dec) -{ - return dec->flags & MPD_STATIC_DATA; -} - -/* Data of decimal is shared */ -ALWAYS_INLINE int -mpd_isshared_data(const mpd_t *dec) -{ - return dec->flags & MPD_SHARED_DATA; -} - -/* Data of decimal is const */ -ALWAYS_INLINE int -mpd_isconst_data(const mpd_t *dec) -{ - return dec->flags & MPD_CONST_DATA; -} - - -/******************************************************************************/ -/* Inline memory handling */ -/******************************************************************************/ - -/* Fill destination with zeros */ -ALWAYS_INLINE void -mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len) -{ - mpd_size_t i; - - for (i = 0; i < len; i++) { - dest[i] = 0; - } -} - -/* Free a decimal */ -ALWAYS_INLINE void -mpd_del(mpd_t *dec) -{ - if (mpd_isdynamic_data(dec)) { - mpd_free(dec->data); - } - if (mpd_isdynamic(dec)) { - mpd_free(dec); - } -} - -/* - * Resize the coefficient. Existing data up to 'nwords' is left untouched. - * Return 1 on success, 0 otherwise. - * - * Input invariant: MPD_MINALLOC <= result->alloc. - * - * Case nwords == result->alloc: - * 'result' is unchanged. Return 1. - * - * Case nwords > result->alloc: - * Case realloc success: - * The value of 'result' does not change. Return 1. - * Case realloc failure: - * 'result' is NaN, status is updated with MPD_Malloc_error. Return 0. - * - * Case nwords < result->alloc: - * Case is_static_data or realloc failure [1]: - * 'result' is unchanged. Return 1. - * Case realloc success: - * The value of result is undefined (expected). Return 1. - * - * - * [1] In that case the old (now oversized) area is still valid. - */ -ALWAYS_INLINE int -mpd_qresize(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) -{ - assert(!mpd_isconst_data(result)); /* illegal operation for a const */ - assert(!mpd_isshared_data(result)); /* illegal operation for a shared */ - assert(MPD_MINALLOC <= result->alloc); - - nwords = (nwords <= MPD_MINALLOC) ? MPD_MINALLOC : nwords; - if (nwords == result->alloc) { - return 1; - } - if (mpd_isstatic_data(result)) { - if (nwords > result->alloc) { - return mpd_switch_to_dyn(result, nwords, status); - } - return 1; - } - - return mpd_realloc_dyn(result, nwords, status); -} - -/* Same as mpd_qresize, but do not set the result no NaN on failure. */ -static ALWAYS_INLINE int -mpd_qresize_cxx(mpd_t *result, mpd_ssize_t nwords) -{ - assert(!mpd_isconst_data(result)); /* illegal operation for a const */ - assert(!mpd_isshared_data(result)); /* illegal operation for a shared */ - assert(MPD_MINALLOC <= result->alloc); - - nwords = (nwords <= MPD_MINALLOC) ? MPD_MINALLOC : nwords; - if (nwords == result->alloc) { - return 1; - } - if (mpd_isstatic_data(result)) { - if (nwords > result->alloc) { - return mpd_switch_to_dyn_cxx(result, nwords); - } - return 1; - } - - return mpd_realloc_dyn_cxx(result, nwords); -} - -/* Same as mpd_qresize, but the complete coefficient (including the old - * memory area!) is initialized to zero. */ -ALWAYS_INLINE int -mpd_qresize_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status) -{ - assert(!mpd_isconst_data(result)); /* illegal operation for a const */ - assert(!mpd_isshared_data(result)); /* illegal operation for a shared */ - assert(MPD_MINALLOC <= result->alloc); - - nwords = (nwords <= MPD_MINALLOC) ? MPD_MINALLOC : nwords; - if (nwords != result->alloc) { - if (mpd_isstatic_data(result)) { - if (nwords > result->alloc) { - return mpd_switch_to_dyn_zero(result, nwords, status); - } - } - else if (!mpd_realloc_dyn(result, nwords, status)) { - return 0; - } - } - - mpd_uint_zero(result->data, nwords); - return 1; -} - -/* - * Reduce memory size for the coefficient to MPD_MINALLOC. In theory, - * realloc may fail even when reducing the memory size. But in that case - * the old memory area is always big enough, so checking for MPD_Malloc_error - * is not imperative. - */ -ALWAYS_INLINE void -mpd_minalloc(mpd_t *result) -{ - assert(!mpd_isconst_data(result)); /* illegal operation for a const */ - assert(!mpd_isshared_data(result)); /* illegal operation for a shared */ - - if (!mpd_isstatic_data(result) && result->alloc > MPD_MINALLOC) { - uint8_t err = 0; - result->data = mpd_realloc(result->data, MPD_MINALLOC, - sizeof *result->data, &err); - if (!err) { - result->alloc = MPD_MINALLOC; - } - } -} - -int -mpd_resize(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx) -{ - uint32_t status = 0; - if (!mpd_qresize(result, nwords, &status)) { - mpd_addstatus_raise(ctx, status); - return 0; - } - return 1; -} - -int -mpd_resize_zero(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx) -{ - uint32_t status = 0; - if (!mpd_qresize_zero(result, nwords, &status)) { - mpd_addstatus_raise(ctx, status); - return 0; - } - return 1; -} - - -/******************************************************************************/ -/* Set attributes of a decimal */ -/******************************************************************************/ - -/* Set digits. Assumption: result->len is initialized and > 0. */ -inline void -mpd_setdigits(mpd_t *result) -{ - mpd_ssize_t wdigits = mpd_word_digits(mpd_msword(result)); - result->digits = wdigits + (result->len-1) * MPD_RDIGITS; -} - -/* Set sign */ -ALWAYS_INLINE void -mpd_set_sign(mpd_t *result, uint8_t sign) -{ - result->flags &= ~MPD_NEG; - result->flags |= sign; -} - -/* Copy sign from another decimal */ -ALWAYS_INLINE void -mpd_signcpy(mpd_t *result, const mpd_t *a) -{ - uint8_t sign = a->flags&MPD_NEG; - - result->flags &= ~MPD_NEG; - result->flags |= sign; -} - -/* Set infinity */ -ALWAYS_INLINE void -mpd_set_infinity(mpd_t *result) -{ - result->flags &= ~MPD_SPECIAL; - result->flags |= MPD_INF; -} - -/* Set qNaN */ -ALWAYS_INLINE void -mpd_set_qnan(mpd_t *result) -{ - result->flags &= ~MPD_SPECIAL; - result->flags |= MPD_NAN; -} - -/* Set sNaN */ -ALWAYS_INLINE void -mpd_set_snan(mpd_t *result) -{ - result->flags &= ~MPD_SPECIAL; - result->flags |= MPD_SNAN; -} - -/* Set to negative */ -ALWAYS_INLINE void -mpd_set_negative(mpd_t *result) -{ - result->flags |= MPD_NEG; -} - -/* Set to positive */ -ALWAYS_INLINE void -mpd_set_positive(mpd_t *result) -{ - result->flags &= ~MPD_NEG; -} - -/* Set to dynamic */ -ALWAYS_INLINE void -mpd_set_dynamic(mpd_t *result) -{ - result->flags &= ~MPD_STATIC; -} - -/* Set to static */ -ALWAYS_INLINE void -mpd_set_static(mpd_t *result) -{ - result->flags |= MPD_STATIC; -} - -/* Set data to dynamic */ -ALWAYS_INLINE void -mpd_set_dynamic_data(mpd_t *result) -{ - result->flags &= ~MPD_DATAFLAGS; -} - -/* Set data to static */ -ALWAYS_INLINE void -mpd_set_static_data(mpd_t *result) -{ - result->flags &= ~MPD_DATAFLAGS; - result->flags |= MPD_STATIC_DATA; -} - -/* Set data to shared */ -ALWAYS_INLINE void -mpd_set_shared_data(mpd_t *result) -{ - result->flags &= ~MPD_DATAFLAGS; - result->flags |= MPD_SHARED_DATA; -} - -/* Set data to const */ -ALWAYS_INLINE void -mpd_set_const_data(mpd_t *result) -{ - result->flags &= ~MPD_DATAFLAGS; - result->flags |= MPD_CONST_DATA; -} - -/* Clear flags, preserving memory attributes. */ -ALWAYS_INLINE void -mpd_clear_flags(mpd_t *result) -{ - result->flags &= (MPD_STATIC|MPD_DATAFLAGS); -} - -/* Set flags, preserving memory attributes. */ -ALWAYS_INLINE void -mpd_set_flags(mpd_t *result, uint8_t flags) -{ - result->flags &= (MPD_STATIC|MPD_DATAFLAGS); - result->flags |= flags; -} - -/* Copy flags, preserving memory attributes of result. */ -ALWAYS_INLINE void -mpd_copy_flags(mpd_t *result, const mpd_t *a) -{ - uint8_t aflags = a->flags; - result->flags &= (MPD_STATIC|MPD_DATAFLAGS); - result->flags |= (aflags & ~(MPD_STATIC|MPD_DATAFLAGS)); -} - -/* Initialize a workcontext from ctx. Set traps, flags and newtrap to 0. */ -static inline void -mpd_workcontext(mpd_context_t *workctx, const mpd_context_t *ctx) -{ - workctx->prec = ctx->prec; - workctx->emax = ctx->emax; - workctx->emin = ctx->emin; - workctx->round = ctx->round; - workctx->traps = 0; - workctx->status = 0; - workctx->newtrap = 0; - workctx->clamp = ctx->clamp; - workctx->allcr = ctx->allcr; -} - - -/******************************************************************************/ -/* Getting and setting parts of decimals */ -/******************************************************************************/ - -/* Flip the sign of a decimal */ -static inline void -_mpd_negate(mpd_t *dec) -{ - dec->flags ^= MPD_NEG; -} - -/* Set coefficient to zero */ -void -mpd_zerocoeff(mpd_t *result) -{ - mpd_minalloc(result); - result->digits = 1; - result->len = 1; - result->data[0] = 0; -} - -/* Set the coefficient to all nines. */ -void -mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status) -{ - mpd_ssize_t len, r; - - _mpd_idiv_word(&len, &r, ctx->prec, MPD_RDIGITS); - len = (r == 0) ? len : len+1; - - if (!mpd_qresize(result, len, status)) { - return; - } - - result->len = len; - result->digits = ctx->prec; - - --len; - if (r > 0) { - result->data[len--] = mpd_pow10[r]-1; - } - for (; len >= 0; --len) { - result->data[len] = MPD_RADIX-1; - } -} - -/* - * Cut off the most significant digits so that the rest fits in ctx->prec. - * Cannot fail. - */ -static void -_mpd_cap(mpd_t *result, const mpd_context_t *ctx) -{ - uint32_t dummy; - mpd_ssize_t len, r; - - if (result->len > 0 && result->digits > ctx->prec) { - _mpd_idiv_word(&len, &r, ctx->prec, MPD_RDIGITS); - len = (r == 0) ? len : len+1; - - if (r != 0) { - result->data[len-1] %= mpd_pow10[r]; - } - - len = _mpd_real_size(result->data, len); - /* resize to fewer words cannot fail */ - mpd_qresize(result, len, &dummy); - result->len = len; - mpd_setdigits(result); - } - if (mpd_iszero(result)) { - _settriple(result, mpd_sign(result), 0, result->exp); - } -} - -/* - * Cut off the most significant digits of a NaN payload so that the rest - * fits in ctx->prec - ctx->clamp. Cannot fail. - */ -static void -_mpd_fix_nan(mpd_t *result, const mpd_context_t *ctx) -{ - uint32_t dummy; - mpd_ssize_t prec; - mpd_ssize_t len, r; - - prec = ctx->prec - ctx->clamp; - if (result->len > 0 && result->digits > prec) { - if (prec == 0) { - mpd_minalloc(result); - result->len = result->digits = 0; - } - else { - _mpd_idiv_word(&len, &r, prec, MPD_RDIGITS); - len = (r == 0) ? len : len+1; - - if (r != 0) { - result->data[len-1] %= mpd_pow10[r]; - } - - len = _mpd_real_size(result->data, len); - /* resize to fewer words cannot fail */ - mpd_qresize(result, len, &dummy); - result->len = len; - mpd_setdigits(result); - if (mpd_iszerocoeff(result)) { - /* NaN0 is not a valid representation */ - result->len = result->digits = 0; - } - } - } -} - -/* - * Get n most significant digits from a decimal, where 0 < n <= MPD_UINT_DIGITS. - * Assumes MPD_UINT_DIGITS == MPD_RDIGITS+1, which is true for 32 and 64 bit - * machines. - * - * The result of the operation will be in lo. If the operation is impossible, - * hi will be nonzero. This is used to indicate an error. - */ -static inline void -_mpd_get_msdigits(mpd_uint_t *hi, mpd_uint_t *lo, const mpd_t *dec, - unsigned int n) -{ - mpd_uint_t r, tmp; - - assert(0 < n && n <= MPD_RDIGITS+1); - - _mpd_div_word(&tmp, &r, dec->digits, MPD_RDIGITS); - r = (r == 0) ? MPD_RDIGITS : r; /* digits in the most significant word */ - - *hi = 0; - *lo = dec->data[dec->len-1]; - if (n <= r) { - *lo /= mpd_pow10[r-n]; - } - else if (dec->len > 1) { - /* at this point 1 <= r < n <= MPD_RDIGITS+1 */ - _mpd_mul_words(hi, lo, *lo, mpd_pow10[n-r]); - tmp = dec->data[dec->len-2] / mpd_pow10[MPD_RDIGITS-(n-r)]; - *lo = *lo + tmp; - if (*lo < tmp) (*hi)++; - } -} - - -/******************************************************************************/ -/* Gathering information about a decimal */ -/******************************************************************************/ - -/* The real size of the coefficient without leading zero words. */ -static inline mpd_ssize_t -_mpd_real_size(mpd_uint_t *data, mpd_ssize_t size) -{ - while (size > 1 && data[size-1] == 0) { - size--; - } - - return size; -} - -/* Return number of trailing zeros. No errors are possible. */ -mpd_ssize_t -mpd_trail_zeros(const mpd_t *dec) -{ - mpd_uint_t word; - mpd_ssize_t i, tz = 0; - - for (i=0; i < dec->len; ++i) { - if (dec->data[i] != 0) { - word = dec->data[i]; - tz = i * MPD_RDIGITS; - while (word % 10 == 0) { - word /= 10; - tz++; - } - break; - } - } - - return tz; -} - -/* Integer: Undefined for specials */ -static int -_mpd_isint(const mpd_t *dec) -{ - mpd_ssize_t tz; - - if (mpd_iszerocoeff(dec)) { - return 1; - } - - tz = mpd_trail_zeros(dec); - return (dec->exp + tz >= 0); -} - -/* Integer */ -int -mpd_isinteger(const mpd_t *dec) -{ - if (mpd_isspecial(dec)) { - return 0; - } - return _mpd_isint(dec); -} - -/* Word is a power of 10 */ -static int -mpd_word_ispow10(mpd_uint_t word) -{ - int n; - - n = mpd_word_digits(word); - if (word == mpd_pow10[n-1]) { - return 1; - } - - return 0; -} - -/* Coefficient is a power of 10 */ -static int -mpd_coeff_ispow10(const mpd_t *dec) -{ - if (mpd_word_ispow10(mpd_msword(dec))) { - if (_mpd_isallzero(dec->data, dec->len-1)) { - return 1; - } - } - - return 0; -} - -/* All digits of a word are nines */ -static int -mpd_word_isallnine(mpd_uint_t word) -{ - int n; - - n = mpd_word_digits(word); - if (word == mpd_pow10[n]-1) { - return 1; - } - - return 0; -} - -/* All digits of the coefficient are nines */ -static int -mpd_coeff_isallnine(const mpd_t *dec) -{ - if (mpd_word_isallnine(mpd_msword(dec))) { - if (_mpd_isallnine(dec->data, dec->len-1)) { - return 1; - } - } - - return 0; -} - -/* Odd decimal: Undefined for non-integers! */ -int -mpd_isodd(const mpd_t *dec) -{ - mpd_uint_t q, r; - assert(mpd_isinteger(dec)); - if (mpd_iszerocoeff(dec)) return 0; - if (dec->exp < 0) { - _mpd_div_word(&q, &r, -dec->exp, MPD_RDIGITS); - q = dec->data[q] / mpd_pow10[r]; - return mpd_isoddword(q); - } - return dec->exp == 0 && mpd_isoddword(dec->data[0]); -} - -/* Even: Undefined for non-integers! */ -int -mpd_iseven(const mpd_t *dec) -{ - return !mpd_isodd(dec); -} - -/******************************************************************************/ -/* Getting and setting decimals */ -/******************************************************************************/ - -/* Internal function: Set a static decimal from a triple, no error checking. */ -static void -_ssettriple(mpd_t *result, uint8_t sign, mpd_uint_t a, mpd_ssize_t exp) -{ - mpd_set_flags(result, sign); - result->exp = exp; - _mpd_div_word(&result->data[1], &result->data[0], a, MPD_RADIX); - result->len = (result->data[1] == 0) ? 1 : 2; - mpd_setdigits(result); -} - -/* Internal function: Set a decimal from a triple, no error checking. */ -static void -_settriple(mpd_t *result, uint8_t sign, mpd_uint_t a, mpd_ssize_t exp) -{ - mpd_minalloc(result); - mpd_set_flags(result, sign); - result->exp = exp; - _mpd_div_word(&result->data[1], &result->data[0], a, MPD_RADIX); - result->len = (result->data[1] == 0) ? 1 : 2; - mpd_setdigits(result); -} - -/* Set a special number from a triple */ -void -mpd_setspecial(mpd_t *result, uint8_t sign, uint8_t type) -{ - mpd_minalloc(result); - result->flags &= ~(MPD_NEG|MPD_SPECIAL); - result->flags |= (sign|type); - result->exp = result->digits = result->len = 0; -} - -/* Set result of NaN with an error status */ -void -mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status) -{ - mpd_minalloc(result); - mpd_set_qnan(result); - mpd_set_positive(result); - result->exp = result->digits = result->len = 0; - *status |= flags; -} - -/* quietly set a static decimal from an mpd_ssize_t */ -void -mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_uint_t u; - uint8_t sign = MPD_POS; - - if (a < 0) { - if (a == MPD_SSIZE_MIN) { - u = (mpd_uint_t)MPD_SSIZE_MAX + - (-(MPD_SSIZE_MIN+MPD_SSIZE_MAX)); - } - else { - u = -a; - } - sign = MPD_NEG; - } - else { - u = a; - } - _ssettriple(result, sign, u, 0); - mpd_qfinalize(result, ctx, status); -} - -/* quietly set a static decimal from an mpd_uint_t */ -void -mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - _ssettriple(result, MPD_POS, a, 0); - mpd_qfinalize(result, ctx, status); -} - -/* quietly set a static decimal from an int32_t */ -void -mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_qsset_ssize(result, a, ctx, status); -} - -/* quietly set a static decimal from a uint32_t */ -void -mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_qsset_uint(result, a, ctx, status); -} - -#ifdef CONFIG_64 -/* quietly set a static decimal from an int64_t */ -void -mpd_qsset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_qsset_ssize(result, a, ctx, status); -} - -/* quietly set a static decimal from a uint64_t */ -void -mpd_qsset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_qsset_uint(result, a, ctx, status); -} -#endif - -/* quietly set a decimal from an mpd_ssize_t */ -void -mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_minalloc(result); - mpd_qsset_ssize(result, a, ctx, status); -} - -/* quietly set a decimal from an mpd_uint_t */ -void -mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - _settriple(result, MPD_POS, a, 0); - mpd_qfinalize(result, ctx, status); -} - -/* quietly set a decimal from an int32_t */ -void -mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_qset_ssize(result, a, ctx, status); -} - -/* quietly set a decimal from a uint32_t */ -void -mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_qset_uint(result, a, ctx, status); -} - -#if defined(CONFIG_32) && !defined(LEGACY_COMPILER) -/* set a decimal from a uint64_t */ -static void -_c32setu64(mpd_t *result, uint64_t u, uint8_t sign, uint32_t *status) -{ - mpd_uint_t w[3]; - uint64_t q; - int i, len; - - len = 0; - do { - q = u / MPD_RADIX; - w[len] = (mpd_uint_t)(u - q * MPD_RADIX); - u = q; len++; - } while (u != 0); - - if (!mpd_qresize(result, len, status)) { - return; - } - for (i = 0; i < len; i++) { - result->data[i] = w[i]; - } - - mpd_set_flags(result, sign); - result->exp = 0; - result->len = len; - mpd_setdigits(result); -} - -static void -_c32_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - _c32setu64(result, a, MPD_POS, status); - mpd_qfinalize(result, ctx, status); -} - -/* set a decimal from an int64_t */ -static void -_c32_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, - uint32_t *status) -{ - uint64_t u; - uint8_t sign = MPD_POS; - - if (a < 0) { - if (a == INT64_MIN) { - u = (uint64_t)INT64_MAX + (-(INT64_MIN+INT64_MAX)); - } - else { - u = -a; - } - sign = MPD_NEG; - } - else { - u = a; - } - _c32setu64(result, u, sign, status); - mpd_qfinalize(result, ctx, status); -} -#endif /* CONFIG_32 && !LEGACY_COMPILER */ - -#ifndef LEGACY_COMPILER -/* quietly set a decimal from an int64_t */ -void -mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, - uint32_t *status) -{ -#ifdef CONFIG_64 - mpd_qset_ssize(result, a, ctx, status); -#else - _c32_qset_i64(result, a, ctx, status); -#endif -} - -/* quietly set a decimal from an int64_t, use a maxcontext for conversion */ -void -mpd_qset_i64_exact(mpd_t *result, int64_t a, uint32_t *status) -{ - mpd_context_t maxcontext; - - mpd_maxcontext(&maxcontext); -#ifdef CONFIG_64 - mpd_qset_ssize(result, a, &maxcontext, status); -#else - _c32_qset_i64(result, a, &maxcontext, status); -#endif - - if (*status & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) { - /* we want exact results */ - mpd_seterror(result, MPD_Invalid_operation, status); - } - *status &= MPD_Errors; -} - -/* quietly set a decimal from a uint64_t */ -void -mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, - uint32_t *status) -{ -#ifdef CONFIG_64 - mpd_qset_uint(result, a, ctx, status); -#else - _c32_qset_u64(result, a, ctx, status); -#endif -} - -/* quietly set a decimal from a uint64_t, use a maxcontext for conversion */ -void -mpd_qset_u64_exact(mpd_t *result, uint64_t a, uint32_t *status) -{ - mpd_context_t maxcontext; - - mpd_maxcontext(&maxcontext); -#ifdef CONFIG_64 - mpd_qset_uint(result, a, &maxcontext, status); -#else - _c32_qset_u64(result, a, &maxcontext, status); -#endif - - if (*status & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) { - /* we want exact results */ - mpd_seterror(result, MPD_Invalid_operation, status); - } - *status &= MPD_Errors; -} -#endif /* !LEGACY_COMPILER */ - -/* - * Quietly get an mpd_uint_t from a decimal. Assumes - * MPD_UINT_DIGITS == MPD_RDIGITS+1, which is true for - * 32 and 64 bit machines. - * - * If the operation is impossible, MPD_Invalid_operation is set. - */ -static mpd_uint_t -_mpd_qget_uint(int use_sign, const mpd_t *a, uint32_t *status) -{ - mpd_t tmp; - mpd_uint_t tmp_data[2]; - mpd_uint_t lo, hi; - - if (mpd_isspecial(a)) { - *status |= MPD_Invalid_operation; - return MPD_UINT_MAX; - } - if (mpd_iszero(a)) { - return 0; - } - if (use_sign && mpd_isnegative(a)) { - *status |= MPD_Invalid_operation; - return MPD_UINT_MAX; - } - - if (a->digits+a->exp > MPD_RDIGITS+1) { - *status |= MPD_Invalid_operation; - return MPD_UINT_MAX; - } - - if (a->exp < 0) { - if (!_mpd_isint(a)) { - *status |= MPD_Invalid_operation; - return MPD_UINT_MAX; - } - /* At this point a->digits+a->exp <= MPD_RDIGITS+1, - * so the shift fits. */ - tmp.data = tmp_data; - tmp.flags = MPD_STATIC|MPD_STATIC_DATA; - tmp.alloc = 2; - mpd_qsshiftr(&tmp, a, -a->exp); - tmp.exp = 0; - a = &tmp; - } - - _mpd_get_msdigits(&hi, &lo, a, MPD_RDIGITS+1); - if (hi) { - *status |= MPD_Invalid_operation; - return MPD_UINT_MAX; - } - - if (a->exp > 0) { - _mpd_mul_words(&hi, &lo, lo, mpd_pow10[a->exp]); - if (hi) { - *status |= MPD_Invalid_operation; - return MPD_UINT_MAX; - } - } - - return lo; -} - -/* - * Sets Invalid_operation for: - * - specials - * - negative numbers (except negative zero) - * - non-integers - * - overflow - */ -mpd_uint_t -mpd_qget_uint(const mpd_t *a, uint32_t *status) -{ - return _mpd_qget_uint(1, a, status); -} - -/* Same as above, but gets the absolute value, i.e. the sign is ignored. */ -mpd_uint_t -mpd_qabs_uint(const mpd_t *a, uint32_t *status) -{ - return _mpd_qget_uint(0, a, status); -} - -/* quietly get an mpd_ssize_t from a decimal */ -mpd_ssize_t -mpd_qget_ssize(const mpd_t *a, uint32_t *status) -{ - uint32_t workstatus = 0; - mpd_uint_t u; - int isneg; - - u = mpd_qabs_uint(a, &workstatus); - if (workstatus&MPD_Invalid_operation) { - *status |= workstatus; - return MPD_SSIZE_MAX; - } - - isneg = mpd_isnegative(a); - if (u <= MPD_SSIZE_MAX) { - return isneg ? -((mpd_ssize_t)u) : (mpd_ssize_t)u; - } - else if (isneg && u+(MPD_SSIZE_MIN+MPD_SSIZE_MAX) == MPD_SSIZE_MAX) { - return MPD_SSIZE_MIN; - } - - *status |= MPD_Invalid_operation; - return MPD_SSIZE_MAX; -} - -#if defined(CONFIG_32) && !defined(LEGACY_COMPILER) -/* - * Quietly get a uint64_t from a decimal. If the operation is impossible, - * MPD_Invalid_operation is set. - */ -static uint64_t -_c32_qget_u64(int use_sign, const mpd_t *a, uint32_t *status) -{ - MPD_NEW_STATIC(tmp,0,0,20,3); - mpd_context_t maxcontext; - uint64_t ret; - - tmp_data[0] = 709551615; - tmp_data[1] = 446744073; - tmp_data[2] = 18; - - if (mpd_isspecial(a)) { - *status |= MPD_Invalid_operation; - return UINT64_MAX; - } - if (mpd_iszero(a)) { - return 0; - } - if (use_sign && mpd_isnegative(a)) { - *status |= MPD_Invalid_operation; - return UINT64_MAX; - } - if (!_mpd_isint(a)) { - *status |= MPD_Invalid_operation; - return UINT64_MAX; - } - - if (_mpd_cmp_abs(a, &tmp) > 0) { - *status |= MPD_Invalid_operation; - return UINT64_MAX; - } - - mpd_maxcontext(&maxcontext); - mpd_qrescale(&tmp, a, 0, &maxcontext, &maxcontext.status); - maxcontext.status &= ~MPD_Rounded; - if (maxcontext.status != 0) { - *status |= (maxcontext.status|MPD_Invalid_operation); /* GCOV_NOT_REACHED */ - return UINT64_MAX; /* GCOV_NOT_REACHED */ - } - - ret = 0; - switch (tmp.len) { - case 3: - ret += (uint64_t)tmp_data[2] * 1000000000000000000ULL; - case 2: - ret += (uint64_t)tmp_data[1] * 1000000000ULL; - case 1: - ret += tmp_data[0]; - break; - default: - abort(); /* GCOV_NOT_REACHED */ - } - - return ret; -} - -static int64_t -_c32_qget_i64(const mpd_t *a, uint32_t *status) -{ - uint64_t u; - int isneg; - - u = _c32_qget_u64(0, a, status); - if (*status&MPD_Invalid_operation) { - return INT64_MAX; - } - - isneg = mpd_isnegative(a); - if (u <= INT64_MAX) { - return isneg ? -((int64_t)u) : (int64_t)u; - } - else if (isneg && u+(INT64_MIN+INT64_MAX) == INT64_MAX) { - return INT64_MIN; - } - - *status |= MPD_Invalid_operation; - return INT64_MAX; -} -#endif /* CONFIG_32 && !LEGACY_COMPILER */ - -#ifdef CONFIG_64 -/* quietly get a uint64_t from a decimal */ -uint64_t -mpd_qget_u64(const mpd_t *a, uint32_t *status) -{ - return mpd_qget_uint(a, status); -} - -/* quietly get an int64_t from a decimal */ -int64_t -mpd_qget_i64(const mpd_t *a, uint32_t *status) -{ - return mpd_qget_ssize(a, status); -} - -/* quietly get a uint32_t from a decimal */ -uint32_t -mpd_qget_u32(const mpd_t *a, uint32_t *status) -{ - uint32_t workstatus = 0; - uint64_t x = mpd_qget_uint(a, &workstatus); - - if (workstatus&MPD_Invalid_operation) { - *status |= workstatus; - return UINT32_MAX; - } - if (x > UINT32_MAX) { - *status |= MPD_Invalid_operation; - return UINT32_MAX; - } - - return (uint32_t)x; -} - -/* quietly get an int32_t from a decimal */ -int32_t -mpd_qget_i32(const mpd_t *a, uint32_t *status) -{ - uint32_t workstatus = 0; - int64_t x = mpd_qget_ssize(a, &workstatus); - - if (workstatus&MPD_Invalid_operation) { - *status |= workstatus; - return INT32_MAX; - } - if (x < INT32_MIN || x > INT32_MAX) { - *status |= MPD_Invalid_operation; - return INT32_MAX; - } - - return (int32_t)x; -} -#else -#ifndef LEGACY_COMPILER -/* quietly get a uint64_t from a decimal */ -uint64_t -mpd_qget_u64(const mpd_t *a, uint32_t *status) -{ - uint32_t workstatus = 0; - uint64_t x = _c32_qget_u64(1, a, &workstatus); - *status |= workstatus; - return x; -} - -/* quietly get an int64_t from a decimal */ -int64_t -mpd_qget_i64(const mpd_t *a, uint32_t *status) -{ - uint32_t workstatus = 0; - int64_t x = _c32_qget_i64(a, &workstatus); - *status |= workstatus; - return x; -} -#endif - -/* quietly get a uint32_t from a decimal */ -uint32_t -mpd_qget_u32(const mpd_t *a, uint32_t *status) -{ - return mpd_qget_uint(a, status); -} - -/* quietly get an int32_t from a decimal */ -int32_t -mpd_qget_i32(const mpd_t *a, uint32_t *status) -{ - return mpd_qget_ssize(a, status); -} -#endif - - -/******************************************************************************/ -/* Filtering input of functions, finalizing output of functions */ -/******************************************************************************/ - -/* - * Check if the operand is NaN, copy to result and return 1 if this is - * the case. Copying can fail since NaNs are allowed to have a payload that - * does not fit in MPD_MINALLOC. - */ -int -mpd_qcheck_nan(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - if (mpd_isnan(a)) { - *status |= mpd_issnan(a) ? MPD_Invalid_operation : 0; - mpd_qcopy(result, a, status); - mpd_set_qnan(result); - _mpd_fix_nan(result, ctx); - return 1; - } - return 0; -} - -/* - * Check if either operand is NaN, copy to result and return 1 if this - * is the case. Copying can fail since NaNs are allowed to have a payload - * that does not fit in MPD_MINALLOC. - */ -int -mpd_qcheck_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - if ((a->flags|b->flags)&(MPD_NAN|MPD_SNAN)) { - const mpd_t *choice = b; - if (mpd_issnan(a)) { - choice = a; - *status |= MPD_Invalid_operation; - } - else if (mpd_issnan(b)) { - *status |= MPD_Invalid_operation; - } - else if (mpd_isqnan(a)) { - choice = a; - } - mpd_qcopy(result, choice, status); - mpd_set_qnan(result); - _mpd_fix_nan(result, ctx); - return 1; - } - return 0; -} - -/* - * Check if one of the operands is NaN, copy to result and return 1 if this - * is the case. Copying can fail since NaNs are allowed to have a payload - * that does not fit in MPD_MINALLOC. - */ -static int -mpd_qcheck_3nans(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, - const mpd_context_t *ctx, uint32_t *status) -{ - if ((a->flags|b->flags|c->flags)&(MPD_NAN|MPD_SNAN)) { - const mpd_t *choice = c; - if (mpd_issnan(a)) { - choice = a; - *status |= MPD_Invalid_operation; - } - else if (mpd_issnan(b)) { - choice = b; - *status |= MPD_Invalid_operation; - } - else if (mpd_issnan(c)) { - *status |= MPD_Invalid_operation; - } - else if (mpd_isqnan(a)) { - choice = a; - } - else if (mpd_isqnan(b)) { - choice = b; - } - mpd_qcopy(result, choice, status); - mpd_set_qnan(result); - _mpd_fix_nan(result, ctx); - return 1; - } - return 0; -} - -/* Check if rounding digit 'rnd' leads to an increment. */ -static inline int -_mpd_rnd_incr(const mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx) -{ - int ld; - - switch (ctx->round) { - case MPD_ROUND_DOWN: case MPD_ROUND_TRUNC: - return 0; - case MPD_ROUND_HALF_UP: - return (rnd >= 5); - case MPD_ROUND_HALF_EVEN: - return (rnd > 5) || ((rnd == 5) && mpd_isoddcoeff(dec)); - case MPD_ROUND_CEILING: - return !(rnd == 0 || mpd_isnegative(dec)); - case MPD_ROUND_FLOOR: - return !(rnd == 0 || mpd_ispositive(dec)); - case MPD_ROUND_HALF_DOWN: - return (rnd > 5); - case MPD_ROUND_UP: - return !(rnd == 0); - case MPD_ROUND_05UP: - ld = (int)mpd_lsd(dec->data[0]); - return (!(rnd == 0) && (ld == 0 || ld == 5)); - default: - /* Without a valid context, further results will be undefined. */ - return 0; /* GCOV_NOT_REACHED */ - } -} - -/* - * Apply rounding to a decimal that has been right-shifted into a full - * precision decimal. If an increment leads to an overflow of the precision, - * adjust the coefficient and the exponent and check the new exponent for - * overflow. - */ -static inline void -_mpd_apply_round(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx, - uint32_t *status) -{ - if (_mpd_rnd_incr(dec, rnd, ctx)) { - /* We have a number with exactly ctx->prec digits. The increment - * can only lead to an overflow if the decimal is all nines. In - * that case, the result is a power of ten with prec+1 digits. - * - * If the precision is a multiple of MPD_RDIGITS, this situation is - * detected by _mpd_baseincr returning a carry. - * If the precision is not a multiple of MPD_RDIGITS, we have to - * check if the result has one digit too many. - */ - mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len); - if (carry) { - dec->data[dec->len-1] = mpd_pow10[MPD_RDIGITS-1]; - dec->exp += 1; - _mpd_check_exp(dec, ctx, status); - return; - } - mpd_setdigits(dec); - if (dec->digits > ctx->prec) { - mpd_qshiftr_inplace(dec, 1); - dec->exp += 1; - dec->digits = ctx->prec; - _mpd_check_exp(dec, ctx, status); - } - } -} - -/* - * Apply rounding to a decimal. Allow overflow of the precision. - */ -static inline void -_mpd_apply_round_excess(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx, - uint32_t *status) -{ - if (_mpd_rnd_incr(dec, rnd, ctx)) { - mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len); - if (carry) { - if (!mpd_qresize(dec, dec->len+1, status)) { - return; - } - dec->data[dec->len] = 1; - dec->len += 1; - } - mpd_setdigits(dec); - } -} - -/* - * Apply rounding to a decimal that has been right-shifted into a decimal - * with full precision or less. Return failure if an increment would - * overflow the precision. - */ -static inline int -_mpd_apply_round_fit(mpd_t *dec, mpd_uint_t rnd, const mpd_context_t *ctx, - uint32_t *status) -{ - if (_mpd_rnd_incr(dec, rnd, ctx)) { - mpd_uint_t carry = _mpd_baseincr(dec->data, dec->len); - if (carry) { - if (!mpd_qresize(dec, dec->len+1, status)) { - return 0; - } - dec->data[dec->len] = 1; - dec->len += 1; - } - mpd_setdigits(dec); - if (dec->digits > ctx->prec) { - mpd_seterror(dec, MPD_Invalid_operation, status); - return 0; - } - } - return 1; -} - -/* Check a normal number for overflow, underflow, clamping. If the operand - is modified, it will be zero, special or (sub)normal with a coefficient - that fits into the current context precision. */ -static inline void -_mpd_check_exp(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status) -{ - mpd_ssize_t adjexp, etiny, shift; - int rnd; - - adjexp = mpd_adjexp(dec); - if (adjexp > ctx->emax) { - - if (mpd_iszerocoeff(dec)) { - dec->exp = ctx->emax; - if (ctx->clamp) { - dec->exp -= (ctx->prec-1); - } - mpd_zerocoeff(dec); - *status |= MPD_Clamped; - return; - } - - switch (ctx->round) { - case MPD_ROUND_HALF_UP: case MPD_ROUND_HALF_EVEN: - case MPD_ROUND_HALF_DOWN: case MPD_ROUND_UP: - case MPD_ROUND_TRUNC: - mpd_setspecial(dec, mpd_sign(dec), MPD_INF); - break; - case MPD_ROUND_DOWN: case MPD_ROUND_05UP: - mpd_qmaxcoeff(dec, ctx, status); - dec->exp = ctx->emax - ctx->prec + 1; - break; - case MPD_ROUND_CEILING: - if (mpd_isnegative(dec)) { - mpd_qmaxcoeff(dec, ctx, status); - dec->exp = ctx->emax - ctx->prec + 1; - } - else { - mpd_setspecial(dec, MPD_POS, MPD_INF); - } - break; - case MPD_ROUND_FLOOR: - if (mpd_ispositive(dec)) { - mpd_qmaxcoeff(dec, ctx, status); - dec->exp = ctx->emax - ctx->prec + 1; - } - else { - mpd_setspecial(dec, MPD_NEG, MPD_INF); - } - break; - default: /* debug */ - abort(); /* GCOV_NOT_REACHED */ - } - - *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded; - - } /* fold down */ - else if (ctx->clamp && dec->exp > mpd_etop(ctx)) { - /* At this point adjexp=exp+digits-1 <= emax and exp > etop=emax-prec+1: - * (1) shift = exp -emax+prec-1 > 0 - * (2) digits+shift = exp+digits-1 - emax + prec <= prec */ - shift = dec->exp - mpd_etop(ctx); - if (!mpd_qshiftl(dec, dec, shift, status)) { - return; - } - dec->exp -= shift; - *status |= MPD_Clamped; - if (!mpd_iszerocoeff(dec) && adjexp < ctx->emin) { - /* Underflow is impossible, since exp < etiny=emin-prec+1 - * and exp > etop=emax-prec+1 would imply emax < emin. */ - *status |= MPD_Subnormal; - } - } - else if (adjexp < ctx->emin) { - - etiny = mpd_etiny(ctx); - - if (mpd_iszerocoeff(dec)) { - if (dec->exp < etiny) { - dec->exp = etiny; - mpd_zerocoeff(dec); - *status |= MPD_Clamped; - } - return; - } - - *status |= MPD_Subnormal; - if (dec->exp < etiny) { - /* At this point adjexp=exp+digits-1 < emin and exp < etiny=emin-prec+1: - * (1) shift = emin-prec+1 - exp > 0 - * (2) digits-shift = exp+digits-1 - emin + prec < prec */ - shift = etiny - dec->exp; - rnd = (int)mpd_qshiftr_inplace(dec, shift); - dec->exp = etiny; - /* We always have a spare digit in case of an increment. */ - _mpd_apply_round_excess(dec, rnd, ctx, status); - *status |= MPD_Rounded; - if (rnd) { - *status |= (MPD_Inexact|MPD_Underflow); - if (mpd_iszerocoeff(dec)) { - mpd_zerocoeff(dec); - *status |= MPD_Clamped; - } - } - } - /* Case exp >= etiny=emin-prec+1: - * (1) adjexp=exp+digits-1 < emin - * (2) digits < emin-exp+1 <= prec */ - } -} - -/* Transcendental functions do not always set Underflow reliably, - * since they only use as much precision as is necessary for correct - * rounding. If a result like 1.0000000000e-101 is finalized, there - * is no rounding digit that would trigger Underflow. But we can - * assume Inexact, so a short check suffices. */ -static inline void -mpd_check_underflow(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status) -{ - if (mpd_adjexp(dec) < ctx->emin && !mpd_iszero(dec) && - dec->exp < mpd_etiny(ctx)) { - *status |= MPD_Underflow; - } -} - -/* Check if a normal number must be rounded after the exponent has been checked. */ -static inline void -_mpd_check_round(mpd_t *dec, const mpd_context_t *ctx, uint32_t *status) -{ - mpd_uint_t rnd; - mpd_ssize_t shift; - - /* must handle specials: _mpd_check_exp() can produce infinities or NaNs */ - if (mpd_isspecial(dec)) { - return; - } - - if (dec->digits > ctx->prec) { - shift = dec->digits - ctx->prec; - rnd = mpd_qshiftr_inplace(dec, shift); - dec->exp += shift; - _mpd_apply_round(dec, rnd, ctx, status); - *status |= MPD_Rounded; - if (rnd) { - *status |= MPD_Inexact; - } - } -} - -/* Finalize all operations. */ -void -mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status) -{ - if (mpd_isspecial(result)) { - if (mpd_isnan(result)) { - _mpd_fix_nan(result, ctx); - } - return; - } - - _mpd_check_exp(result, ctx, status); - _mpd_check_round(result, ctx, status); -} - - -/******************************************************************************/ -/* Copying */ -/******************************************************************************/ - -/* Internal function: Copy a decimal, share data with src: USE WITH CARE! */ -static inline void -_mpd_copy_shared(mpd_t *dest, const mpd_t *src) -{ - dest->flags = src->flags; - dest->exp = src->exp; - dest->digits = src->digits; - dest->len = src->len; - dest->alloc = src->alloc; - dest->data = src->data; - - mpd_set_shared_data(dest); -} - -/* - * Copy a decimal. In case of an error, status is set to MPD_Malloc_error. - */ -int -mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status) -{ - if (result == a) return 1; - - if (!mpd_qresize(result, a->len, status)) { - return 0; - } - - mpd_copy_flags(result, a); - result->exp = a->exp; - result->digits = a->digits; - result->len = a->len; - memcpy(result->data, a->data, a->len * (sizeof *result->data)); - - return 1; -} - -/* Same as mpd_qcopy, but do not set the result to NaN on failure. */ -int -mpd_qcopy_cxx(mpd_t *result, const mpd_t *a) -{ - if (result == a) return 1; - - if (!mpd_qresize_cxx(result, a->len)) { - return 0; - } - - mpd_copy_flags(result, a); - result->exp = a->exp; - result->digits = a->digits; - result->len = a->len; - memcpy(result->data, a->data, a->len * (sizeof *result->data)); - - return 1; -} - -/* - * Copy to a decimal with a static buffer. The caller has to make sure that - * the buffer is big enough. Cannot fail. - */ -static void -mpd_qcopy_static(mpd_t *result, const mpd_t *a) -{ - if (result == a) return; - - memcpy(result->data, a->data, a->len * (sizeof *result->data)); - - mpd_copy_flags(result, a); - result->exp = a->exp; - result->digits = a->digits; - result->len = a->len; -} - -/* - * Return a newly allocated copy of the operand. In case of an error, - * status is set to MPD_Malloc_error and the return value is NULL. - */ -mpd_t * -mpd_qncopy(const mpd_t *a) -{ - mpd_t *result; - - if ((result = mpd_qnew_size(a->len)) == NULL) { - return NULL; - } - memcpy(result->data, a->data, a->len * (sizeof *result->data)); - mpd_copy_flags(result, a); - result->exp = a->exp; - result->digits = a->digits; - result->len = a->len; - - return result; -} - -/* - * Copy a decimal and set the sign to positive. In case of an error, the - * status is set to MPD_Malloc_error. - */ -int -mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status) -{ - if (!mpd_qcopy(result, a, status)) { - return 0; - } - mpd_set_positive(result); - return 1; -} - -/* - * Copy a decimal and negate the sign. In case of an error, the - * status is set to MPD_Malloc_error. - */ -int -mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status) -{ - if (!mpd_qcopy(result, a, status)) { - return 0; - } - _mpd_negate(result); - return 1; -} - -/* - * Copy a decimal, setting the sign of the first operand to the sign of the - * second operand. In case of an error, the status is set to MPD_Malloc_error. - */ -int -mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status) -{ - uint8_t sign_b = mpd_sign(b); /* result may equal b! */ - - if (!mpd_qcopy(result, a, status)) { - return 0; - } - mpd_set_sign(result, sign_b); - return 1; -} - - -/******************************************************************************/ -/* Comparisons */ -/******************************************************************************/ - -/* - * For all functions that compare two operands and return an int the usual - * convention applies to the return value: - * - * -1 if op1 < op2 - * 0 if op1 == op2 - * 1 if op1 > op2 - * - * INT_MAX for error - */ - - -/* Convenience macro. If a and b are not equal, return from the calling - * function with the correct comparison value. */ -#define CMP_EQUAL_OR_RETURN(a, b) \ - if (a != b) { \ - if (a < b) { \ - return -1; \ - } \ - return 1; \ - } - -/* - * Compare the data of big and small. This function does the equivalent - * of first shifting small to the left and then comparing the data of - * big and small, except that no allocation for the left shift is needed. - */ -static int -_mpd_basecmp(mpd_uint_t *big, mpd_uint_t *small, mpd_size_t n, mpd_size_t m, - mpd_size_t shift) -{ -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) - /* spurious uninitialized warnings */ - mpd_uint_t l=l, lprev=lprev, h=h; -#else - mpd_uint_t l, lprev, h; -#endif - mpd_uint_t q, r; - mpd_uint_t ph, x; - - assert(m > 0 && n >= m && shift > 0); - - _mpd_div_word(&q, &r, (mpd_uint_t)shift, MPD_RDIGITS); - - if (r != 0) { - - ph = mpd_pow10[r]; - - --m; --n; - _mpd_divmod_pow10(&h, &lprev, small[m--], MPD_RDIGITS-r); - if (h != 0) { - CMP_EQUAL_OR_RETURN(big[n], h) - --n; - } - for (; m != MPD_SIZE_MAX; m--,n--) { - _mpd_divmod_pow10(&h, &l, small[m], MPD_RDIGITS-r); - x = ph * lprev + h; - CMP_EQUAL_OR_RETURN(big[n], x) - lprev = l; - } - x = ph * lprev; - CMP_EQUAL_OR_RETURN(big[q], x) - } - else { - while (--m != MPD_SIZE_MAX) { - CMP_EQUAL_OR_RETURN(big[m+q], small[m]) - } - } - - return !_mpd_isallzero(big, q); -} - -/* Compare two decimals with the same adjusted exponent. */ -static int -_mpd_cmp_same_adjexp(const mpd_t *a, const mpd_t *b) -{ - mpd_ssize_t shift, i; - - if (a->exp != b->exp) { - /* Cannot wrap: a->exp + a->digits = b->exp + b->digits, so - * a->exp - b->exp = b->digits - a->digits. */ - shift = a->exp - b->exp; - if (shift > 0) { - return -1 * _mpd_basecmp(b->data, a->data, b->len, a->len, shift); - } - else { - return _mpd_basecmp(a->data, b->data, a->len, b->len, -shift); - } - } - - /* - * At this point adjexp(a) == adjexp(b) and a->exp == b->exp, - * so a->digits == b->digits, therefore a->len == b->len. - */ - for (i = a->len-1; i >= 0; --i) { - CMP_EQUAL_OR_RETURN(a->data[i], b->data[i]) - } - - return 0; -} - -/* Compare two numerical values. */ -static int -_mpd_cmp(const mpd_t *a, const mpd_t *b) -{ - mpd_ssize_t adjexp_a, adjexp_b; - - /* equal pointers */ - if (a == b) { - return 0; - } - - /* infinities */ - if (mpd_isinfinite(a)) { - if (mpd_isinfinite(b)) { - return mpd_isnegative(b) - mpd_isnegative(a); - } - return mpd_arith_sign(a); - } - if (mpd_isinfinite(b)) { - return -mpd_arith_sign(b); - } - - /* zeros */ - if (mpd_iszerocoeff(a)) { - if (mpd_iszerocoeff(b)) { - return 0; - } - return -mpd_arith_sign(b); - } - if (mpd_iszerocoeff(b)) { - return mpd_arith_sign(a); - } - - /* different signs */ - if (mpd_sign(a) != mpd_sign(b)) { - return mpd_sign(b) - mpd_sign(a); - } - - /* different adjusted exponents */ - adjexp_a = mpd_adjexp(a); - adjexp_b = mpd_adjexp(b); - if (adjexp_a != adjexp_b) { - if (adjexp_a < adjexp_b) { - return -1 * mpd_arith_sign(a); - } - return mpd_arith_sign(a); - } - - /* same adjusted exponents */ - return _mpd_cmp_same_adjexp(a, b) * mpd_arith_sign(a); -} - -/* Compare the absolutes of two numerical values. */ -static int -_mpd_cmp_abs(const mpd_t *a, const mpd_t *b) -{ - mpd_ssize_t adjexp_a, adjexp_b; - - /* equal pointers */ - if (a == b) { - return 0; - } - - /* infinities */ - if (mpd_isinfinite(a)) { - if (mpd_isinfinite(b)) { - return 0; - } - return 1; - } - if (mpd_isinfinite(b)) { - return -1; - } - - /* zeros */ - if (mpd_iszerocoeff(a)) { - if (mpd_iszerocoeff(b)) { - return 0; - } - return -1; - } - if (mpd_iszerocoeff(b)) { - return 1; - } - - /* different adjusted exponents */ - adjexp_a = mpd_adjexp(a); - adjexp_b = mpd_adjexp(b); - if (adjexp_a != adjexp_b) { - if (adjexp_a < adjexp_b) { - return -1; - } - return 1; - } - - /* same adjusted exponents */ - return _mpd_cmp_same_adjexp(a, b); -} - -/* Compare two values and return an integer result. */ -int -mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status) -{ - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_isnan(a) || mpd_isnan(b)) { - *status |= MPD_Invalid_operation; - return INT_MAX; - } - } - - return _mpd_cmp(a, b); -} - -/* - * Compare a and b, convert the usual integer result to a decimal and - * store it in 'result'. For convenience, the integer result of the comparison - * is returned. Comparisons involving NaNs return NaN/INT_MAX. - */ -int -mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - int c; - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return INT_MAX; - } - } - - c = _mpd_cmp(a, b); - _settriple(result, (c < 0), (c != 0), 0); - return c; -} - -/* Same as mpd_compare(), but signal for all NaNs, i.e. also for quiet NaNs. */ -int -mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - int c; - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) { - *status |= MPD_Invalid_operation; - return INT_MAX; - } - } - - c = _mpd_cmp(a, b); - _settriple(result, (c < 0), (c != 0), 0); - return c; -} - -/* Compare the operands using a total order. */ -int -mpd_cmp_total(const mpd_t *a, const mpd_t *b) -{ - mpd_t aa, bb; - int nan_a, nan_b; - int c; - - if (mpd_sign(a) != mpd_sign(b)) { - return mpd_sign(b) - mpd_sign(a); - } - - - if (mpd_isnan(a)) { - c = 1; - if (mpd_isnan(b)) { - nan_a = (mpd_isqnan(a)) ? 1 : 0; - nan_b = (mpd_isqnan(b)) ? 1 : 0; - if (nan_b == nan_a) { - if (a->len > 0 && b->len > 0) { - _mpd_copy_shared(&aa, a); - _mpd_copy_shared(&bb, b); - aa.exp = bb.exp = 0; - /* compare payload */ - c = _mpd_cmp_abs(&aa, &bb); - } - else { - c = (a->len > 0) - (b->len > 0); - } - } - else { - c = nan_a - nan_b; - } - } - } - else if (mpd_isnan(b)) { - c = -1; - } - else { - c = _mpd_cmp_abs(a, b); - if (c == 0 && a->exp != b->exp) { - c = (a->exp < b->exp) ? -1 : 1; - } - } - - return c * mpd_arith_sign(a); -} - -/* - * Compare a and b according to a total order, convert the usual integer result - * to a decimal and store it in 'result'. For convenience, the integer result - * of the comparison is returned. - */ -int -mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b) -{ - int c; - - c = mpd_cmp_total(a, b); - _settriple(result, (c < 0), (c != 0), 0); - return c; -} - -/* Compare the magnitude of the operands using a total order. */ -int -mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b) -{ - mpd_t aa, bb; - - _mpd_copy_shared(&aa, a); - _mpd_copy_shared(&bb, b); - - mpd_set_positive(&aa); - mpd_set_positive(&bb); - - return mpd_cmp_total(&aa, &bb); -} - -/* - * Compare the magnitude of a and b according to a total order, convert the - * the usual integer result to a decimal and store it in 'result'. - * For convenience, the integer result of the comparison is returned. - */ -int -mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b) -{ - int c; - - c = mpd_cmp_total_mag(a, b); - _settriple(result, (c < 0), (c != 0), 0); - return c; -} - -/* Determine an ordering for operands that are numerically equal. */ -static inline int -_mpd_cmp_numequal(const mpd_t *a, const mpd_t *b) -{ - int sign_a, sign_b; - int c; - - sign_a = mpd_sign(a); - sign_b = mpd_sign(b); - if (sign_a != sign_b) { - c = sign_b - sign_a; - } - else { - c = (a->exp < b->exp) ? -1 : 1; - c *= mpd_arith_sign(a); - } - - return c; -} - - -/******************************************************************************/ -/* Shifting the coefficient */ -/******************************************************************************/ - -/* - * Shift the coefficient of the operand to the left, no check for specials. - * Both operands may be the same pointer. If the result length has to be - * increased, mpd_qresize() might fail with MPD_Malloc_error. - */ -int -mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status) -{ - mpd_ssize_t size; - - assert(!mpd_isspecial(a)); - assert(n >= 0); - - if (mpd_iszerocoeff(a) || n == 0) { - return mpd_qcopy(result, a, status); - } - - size = mpd_digits_to_size(a->digits+n); - if (!mpd_qresize(result, size, status)) { - return 0; /* result is NaN */ - } - - _mpd_baseshiftl(result->data, a->data, size, a->len, n); - - mpd_copy_flags(result, a); - result->exp = a->exp; - result->digits = a->digits+n; - result->len = size; - - return 1; -} - -/* Determine the rounding indicator if all digits of the coefficient are shifted - * out of the picture. */ -static mpd_uint_t -_mpd_get_rnd(const mpd_uint_t *data, mpd_ssize_t len, int use_msd) -{ - mpd_uint_t rnd = 0, rest = 0, word; - - word = data[len-1]; - /* special treatment for the most significant digit if shift == digits */ - if (use_msd) { - _mpd_divmod_pow10(&rnd, &rest, word, mpd_word_digits(word)-1); - if (len > 1 && rest == 0) { - rest = !_mpd_isallzero(data, len-1); - } - } - else { - rest = !_mpd_isallzero(data, len); - } - - return (rnd == 0 || rnd == 5) ? rnd + !!rest : rnd; -} - -/* - * Same as mpd_qshiftr(), but 'result' is an mpd_t with a static coefficient. - * It is the caller's responsibility to ensure that the coefficient is big - * enough. The function cannot fail. - */ -static mpd_uint_t -mpd_qsshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n) -{ - mpd_uint_t rnd; - mpd_ssize_t size; - - assert(!mpd_isspecial(a)); - assert(n >= 0); - - if (mpd_iszerocoeff(a) || n == 0) { - mpd_qcopy_static(result, a); - return 0; - } - - if (n >= a->digits) { - rnd = _mpd_get_rnd(a->data, a->len, (n==a->digits)); - mpd_zerocoeff(result); - } - else { - result->digits = a->digits-n; - size = mpd_digits_to_size(result->digits); - rnd = _mpd_baseshiftr(result->data, a->data, a->len, n); - result->len = size; - } - - mpd_copy_flags(result, a); - result->exp = a->exp; - - return rnd; -} - -/* - * Inplace shift of the coefficient to the right, no check for specials. - * Returns the rounding indicator for mpd_rnd_incr(). - * The function cannot fail. - */ -mpd_uint_t -mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n) -{ - uint32_t dummy; - mpd_uint_t rnd; - mpd_ssize_t size; - - assert(!mpd_isspecial(result)); - assert(n >= 0); - - if (mpd_iszerocoeff(result) || n == 0) { - return 0; - } - - if (n >= result->digits) { - rnd = _mpd_get_rnd(result->data, result->len, (n==result->digits)); - mpd_zerocoeff(result); - } - else { - rnd = _mpd_baseshiftr(result->data, result->data, result->len, n); - result->digits -= n; - size = mpd_digits_to_size(result->digits); - /* reducing the size cannot fail */ - mpd_qresize(result, size, &dummy); - result->len = size; - } - - return rnd; -} - -/* - * Shift the coefficient of the operand to the right, no check for specials. - * Both operands may be the same pointer. Returns the rounding indicator to - * be used by mpd_rnd_incr(). If the result length has to be increased, - * mpd_qcopy() or mpd_qresize() might fail with MPD_Malloc_error. In those - * cases, MPD_UINT_MAX is returned. - */ -mpd_uint_t -mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status) -{ - mpd_uint_t rnd; - mpd_ssize_t size; - - assert(!mpd_isspecial(a)); - assert(n >= 0); - - if (mpd_iszerocoeff(a) || n == 0) { - if (!mpd_qcopy(result, a, status)) { - return MPD_UINT_MAX; - } - return 0; - } - - if (n >= a->digits) { - rnd = _mpd_get_rnd(a->data, a->len, (n==a->digits)); - mpd_zerocoeff(result); - } - else { - result->digits = a->digits-n; - size = mpd_digits_to_size(result->digits); - if (result == a) { - rnd = _mpd_baseshiftr(result->data, a->data, a->len, n); - /* reducing the size cannot fail */ - mpd_qresize(result, size, status); - } - else { - if (!mpd_qresize(result, size, status)) { - return MPD_UINT_MAX; - } - rnd = _mpd_baseshiftr(result->data, a->data, a->len, n); - } - result->len = size; - } - - mpd_copy_flags(result, a); - result->exp = a->exp; - - return rnd; -} - - -/******************************************************************************/ -/* Miscellaneous operations */ -/******************************************************************************/ - -/* Logical And */ -void -mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - const mpd_t *big = a, *small = b; - mpd_uint_t x, y, z, xbit, ybit; - int k, mswdigits; - mpd_ssize_t i; - - if (mpd_isspecial(a) || mpd_isspecial(b) || - mpd_isnegative(a) || mpd_isnegative(b) || - a->exp != 0 || b->exp != 0) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (b->digits > a->digits) { - big = b; - small = a; - } - if (!mpd_qresize(result, big->len, status)) { - return; - } - - - /* full words */ - for (i = 0; i < small->len-1; i++) { - x = small->data[i]; - y = big->data[i]; - z = 0; - for (k = 0; k < MPD_RDIGITS; k++) { - xbit = x % 10; - x /= 10; - ybit = y % 10; - y /= 10; - if (xbit > 1 || ybit > 1) { - goto invalid_operation; - } - z += (xbit&ybit) ? mpd_pow10[k] : 0; - } - result->data[i] = z; - } - /* most significant word of small */ - x = small->data[i]; - y = big->data[i]; - z = 0; - mswdigits = mpd_word_digits(x); - for (k = 0; k < mswdigits; k++) { - xbit = x % 10; - x /= 10; - ybit = y % 10; - y /= 10; - if (xbit > 1 || ybit > 1) { - goto invalid_operation; - } - z += (xbit&ybit) ? mpd_pow10[k] : 0; - } - result->data[i++] = z; - - /* scan the rest of y for digits > 1 */ - for (; k < MPD_RDIGITS; k++) { - ybit = y % 10; - y /= 10; - if (ybit > 1) { - goto invalid_operation; - } - } - /* scan the rest of big for digits > 1 */ - for (; i < big->len; i++) { - y = big->data[i]; - for (k = 0; k < MPD_RDIGITS; k++) { - ybit = y % 10; - y /= 10; - if (ybit > 1) { - goto invalid_operation; - } - } - } - - mpd_clear_flags(result); - result->exp = 0; - result->len = _mpd_real_size(result->data, small->len); - mpd_qresize(result, result->len, status); - mpd_setdigits(result); - _mpd_cap(result, ctx); - return; - -invalid_operation: - mpd_seterror(result, MPD_Invalid_operation, status); -} - -/* Class of an operand. Returns a pointer to the constant name. */ -const char * -mpd_class(const mpd_t *a, const mpd_context_t *ctx) -{ - if (mpd_isnan(a)) { - if (mpd_isqnan(a)) - return "NaN"; - else - return "sNaN"; - } - else if (mpd_ispositive(a)) { - if (mpd_isinfinite(a)) - return "+Infinity"; - else if (mpd_iszero(a)) - return "+Zero"; - else if (mpd_isnormal(a, ctx)) - return "+Normal"; - else - return "+Subnormal"; - } - else { - if (mpd_isinfinite(a)) - return "-Infinity"; - else if (mpd_iszero(a)) - return "-Zero"; - else if (mpd_isnormal(a, ctx)) - return "-Normal"; - else - return "-Subnormal"; - } -} - -/* Logical Xor */ -void -mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_uint_t x, z, xbit; - mpd_ssize_t i, digits, len; - mpd_ssize_t q, r; - int k; - - if (mpd_isspecial(a) || mpd_isnegative(a) || a->exp != 0) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - digits = (a->digits < ctx->prec) ? ctx->prec : a->digits; - _mpd_idiv_word(&q, &r, digits, MPD_RDIGITS); - len = (r == 0) ? q : q+1; - if (!mpd_qresize(result, len, status)) { - return; - } - - for (i = 0; i < len; i++) { - x = (i < a->len) ? a->data[i] : 0; - z = 0; - for (k = 0; k < MPD_RDIGITS; k++) { - xbit = x % 10; - x /= 10; - if (xbit > 1) { - goto invalid_operation; - } - z += !xbit ? mpd_pow10[k] : 0; - } - result->data[i] = z; - } - - mpd_clear_flags(result); - result->exp = 0; - result->len = _mpd_real_size(result->data, len); - mpd_qresize(result, result->len, status); - mpd_setdigits(result); - _mpd_cap(result, ctx); - return; - -invalid_operation: - mpd_seterror(result, MPD_Invalid_operation, status); -} - -/* Exponent of the magnitude of the most significant digit of the operand. */ -void -mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - mpd_setspecial(result, MPD_POS, MPD_INF); - } - else if (mpd_iszerocoeff(a)) { - mpd_setspecial(result, MPD_NEG, MPD_INF); - *status |= MPD_Division_by_zero; - } - else { - mpd_qset_ssize(result, mpd_adjexp(a), ctx, status); - } -} - -/* Logical Or */ -void -mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - const mpd_t *big = a, *small = b; - mpd_uint_t x, y, z, xbit, ybit; - int k, mswdigits; - mpd_ssize_t i; - - if (mpd_isspecial(a) || mpd_isspecial(b) || - mpd_isnegative(a) || mpd_isnegative(b) || - a->exp != 0 || b->exp != 0) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (b->digits > a->digits) { - big = b; - small = a; - } - if (!mpd_qresize(result, big->len, status)) { - return; - } - - - /* full words */ - for (i = 0; i < small->len-1; i++) { - x = small->data[i]; - y = big->data[i]; - z = 0; - for (k = 0; k < MPD_RDIGITS; k++) { - xbit = x % 10; - x /= 10; - ybit = y % 10; - y /= 10; - if (xbit > 1 || ybit > 1) { - goto invalid_operation; - } - z += (xbit|ybit) ? mpd_pow10[k] : 0; - } - result->data[i] = z; - } - /* most significant word of small */ - x = small->data[i]; - y = big->data[i]; - z = 0; - mswdigits = mpd_word_digits(x); - for (k = 0; k < mswdigits; k++) { - xbit = x % 10; - x /= 10; - ybit = y % 10; - y /= 10; - if (xbit > 1 || ybit > 1) { - goto invalid_operation; - } - z += (xbit|ybit) ? mpd_pow10[k] : 0; - } - - /* scan for digits > 1 and copy the rest of y */ - for (; k < MPD_RDIGITS; k++) { - ybit = y % 10; - y /= 10; - if (ybit > 1) { - goto invalid_operation; - } - z += ybit*mpd_pow10[k]; - } - result->data[i++] = z; - /* scan for digits > 1 and copy the rest of big */ - for (; i < big->len; i++) { - y = big->data[i]; - for (k = 0; k < MPD_RDIGITS; k++) { - ybit = y % 10; - y /= 10; - if (ybit > 1) { - goto invalid_operation; - } - } - result->data[i] = big->data[i]; - } - - mpd_clear_flags(result); - result->exp = 0; - result->len = _mpd_real_size(result->data, big->len); - mpd_qresize(result, result->len, status); - mpd_setdigits(result); - _mpd_cap(result, ctx); - return; - -invalid_operation: - mpd_seterror(result, MPD_Invalid_operation, status); -} - -/* - * Rotate the coefficient of 'a' by 'b' digits. 'b' must be an integer with - * exponent 0. - */ -void -mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - uint32_t workstatus = 0; - MPD_NEW_STATIC(tmp,0,0,0,0); - MPD_NEW_STATIC(big,0,0,0,0); - MPD_NEW_STATIC(small,0,0,0,0); - mpd_ssize_t n, lshift, rshift; - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - } - if (b->exp != 0 || mpd_isinfinite(b)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - n = mpd_qget_ssize(b, &workstatus); - if (workstatus&MPD_Invalid_operation) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (n > ctx->prec || n < -ctx->prec) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (mpd_isinfinite(a)) { - mpd_qcopy(result, a, status); - return; - } - - if (n >= 0) { - lshift = n; - rshift = ctx->prec-n; - } - else { - lshift = ctx->prec+n; - rshift = -n; - } - - if (a->digits > ctx->prec) { - if (!mpd_qcopy(&tmp, a, status)) { - mpd_seterror(result, MPD_Malloc_error, status); - goto finish; - } - _mpd_cap(&tmp, ctx); - a = &tmp; - } - - if (!mpd_qshiftl(&big, a, lshift, status)) { - mpd_seterror(result, MPD_Malloc_error, status); - goto finish; - } - _mpd_cap(&big, ctx); - - if (mpd_qshiftr(&small, a, rshift, status) == MPD_UINT_MAX) { - mpd_seterror(result, MPD_Malloc_error, status); - goto finish; - } - _mpd_qadd(result, &big, &small, ctx, status); - - -finish: - mpd_del(&tmp); - mpd_del(&big); - mpd_del(&small); -} - -/* - * b must be an integer with exponent 0 and in the range +-2*(emax + prec). - * XXX: In my opinion +-(2*emax + prec) would be more sensible. - * The result is a with the value of b added to its exponent. - */ -void -mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - uint32_t workstatus = 0; - mpd_uint_t n, maxjump; -#ifndef LEGACY_COMPILER - int64_t exp; -#else - mpd_uint_t x; - int x_sign, n_sign; - mpd_ssize_t exp; -#endif - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - } - if (b->exp != 0 || mpd_isinfinite(b)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - n = mpd_qabs_uint(b, &workstatus); - /* the spec demands this */ - maxjump = 2 * (mpd_uint_t)(ctx->emax + ctx->prec); - - if (n > maxjump || workstatus&MPD_Invalid_operation) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (mpd_isinfinite(a)) { - mpd_qcopy(result, a, status); - return; - } - -#ifndef LEGACY_COMPILER - exp = a->exp + (int64_t)n * mpd_arith_sign(b); - exp = (exp > MPD_EXP_INF) ? MPD_EXP_INF : exp; - exp = (exp < MPD_EXP_CLAMP) ? MPD_EXP_CLAMP : exp; -#else - x = (a->exp < 0) ? -a->exp : a->exp; - x_sign = (a->exp < 0) ? 1 : 0; - n_sign = mpd_isnegative(b) ? 1 : 0; - - if (x_sign == n_sign) { - x = x + n; - if (x < n) x = MPD_UINT_MAX; - } - else { - x_sign = (x >= n) ? x_sign : n_sign; - x = (x >= n) ? x - n : n - x; - } - if (!x_sign && x > MPD_EXP_INF) x = MPD_EXP_INF; - if (x_sign && x > -MPD_EXP_CLAMP) x = -MPD_EXP_CLAMP; - exp = x_sign ? -((mpd_ssize_t)x) : (mpd_ssize_t)x; -#endif - - mpd_qcopy(result, a, status); - result->exp = (mpd_ssize_t)exp; - - mpd_qfinalize(result, ctx, status); -} - -/* - * Shift the coefficient by n digits, positive n is a left shift. In the case - * of a left shift, the result is decapitated to fit the context precision. If - * you don't want that, use mpd_shiftl(). - */ -void -mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx, - uint32_t *status) -{ - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - mpd_qcopy(result, a, status); - return; - } - - if (n >= 0 && n <= ctx->prec) { - mpd_qshiftl(result, a, n, status); - _mpd_cap(result, ctx); - } - else if (n < 0 && n >= -ctx->prec) { - if (!mpd_qcopy(result, a, status)) { - return; - } - _mpd_cap(result, ctx); - mpd_qshiftr_inplace(result, -n); - } - else { - mpd_seterror(result, MPD_Invalid_operation, status); - } -} - -/* - * Same as mpd_shiftn(), but the shift is specified by the decimal b, which - * must be an integer with a zero exponent. Infinities remain infinities. - */ -void -mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, - uint32_t *status) -{ - uint32_t workstatus = 0; - mpd_ssize_t n; - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - } - if (b->exp != 0 || mpd_isinfinite(b)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - n = mpd_qget_ssize(b, &workstatus); - if (workstatus&MPD_Invalid_operation) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (n > ctx->prec || n < -ctx->prec) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (mpd_isinfinite(a)) { - mpd_qcopy(result, a, status); - return; - } - - if (n >= 0) { - mpd_qshiftl(result, a, n, status); - _mpd_cap(result, ctx); - } - else { - if (!mpd_qcopy(result, a, status)) { - return; - } - _mpd_cap(result, ctx); - mpd_qshiftr_inplace(result, -n); - } -} - -/* Logical Xor */ -void -mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - const mpd_t *big = a, *small = b; - mpd_uint_t x, y, z, xbit, ybit; - int k, mswdigits; - mpd_ssize_t i; - - if (mpd_isspecial(a) || mpd_isspecial(b) || - mpd_isnegative(a) || mpd_isnegative(b) || - a->exp != 0 || b->exp != 0) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (b->digits > a->digits) { - big = b; - small = a; - } - if (!mpd_qresize(result, big->len, status)) { - return; - } - - - /* full words */ - for (i = 0; i < small->len-1; i++) { - x = small->data[i]; - y = big->data[i]; - z = 0; - for (k = 0; k < MPD_RDIGITS; k++) { - xbit = x % 10; - x /= 10; - ybit = y % 10; - y /= 10; - if (xbit > 1 || ybit > 1) { - goto invalid_operation; - } - z += (xbit^ybit) ? mpd_pow10[k] : 0; - } - result->data[i] = z; - } - /* most significant word of small */ - x = small->data[i]; - y = big->data[i]; - z = 0; - mswdigits = mpd_word_digits(x); - for (k = 0; k < mswdigits; k++) { - xbit = x % 10; - x /= 10; - ybit = y % 10; - y /= 10; - if (xbit > 1 || ybit > 1) { - goto invalid_operation; - } - z += (xbit^ybit) ? mpd_pow10[k] : 0; - } - - /* scan for digits > 1 and copy the rest of y */ - for (; k < MPD_RDIGITS; k++) { - ybit = y % 10; - y /= 10; - if (ybit > 1) { - goto invalid_operation; - } - z += ybit*mpd_pow10[k]; - } - result->data[i++] = z; - /* scan for digits > 1 and copy the rest of big */ - for (; i < big->len; i++) { - y = big->data[i]; - for (k = 0; k < MPD_RDIGITS; k++) { - ybit = y % 10; - y /= 10; - if (ybit > 1) { - goto invalid_operation; - } - } - result->data[i] = big->data[i]; - } - - mpd_clear_flags(result); - result->exp = 0; - result->len = _mpd_real_size(result->data, big->len); - mpd_qresize(result, result->len, status); - mpd_setdigits(result); - _mpd_cap(result, ctx); - return; - -invalid_operation: - mpd_seterror(result, MPD_Invalid_operation, status); -} - - -/******************************************************************************/ -/* Arithmetic operations */ -/******************************************************************************/ - -/* - * The absolute value of a. If a is negative, the result is the same - * as the result of the minus operation. Otherwise, the result is the - * result of the plus operation. - */ -void -mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - } - - if (mpd_isnegative(a)) { - mpd_qminus(result, a, ctx, status); - } - else { - mpd_qplus(result, a, ctx, status); - } -} - -static inline void -_mpd_ptrswap(const mpd_t **a, const mpd_t **b) -{ - const mpd_t *t = *a; - *a = *b; - *b = t; -} - -/* Add or subtract infinities. */ -static void -_mpd_qaddsub_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b, - uint32_t *status) -{ - if (mpd_isinfinite(a)) { - if (mpd_sign(a) != sign_b && mpd_isinfinite(b)) { - mpd_seterror(result, MPD_Invalid_operation, status); - } - else { - mpd_setspecial(result, mpd_sign(a), MPD_INF); - } - return; - } - assert(mpd_isinfinite(b)); - mpd_setspecial(result, sign_b, MPD_INF); -} - -/* Add or subtract non-special numbers. */ -static void -_mpd_qaddsub(mpd_t *result, const mpd_t *a, const mpd_t *b, uint8_t sign_b, - const mpd_context_t *ctx, uint32_t *status) -{ - const mpd_t *big, *small; - MPD_NEW_STATIC(big_aligned,0,0,0,0); - MPD_NEW_CONST(tiny,0,0,1,1,1,1); - mpd_uint_t carry; - mpd_ssize_t newsize, shift; - mpd_ssize_t exp, i; - int swap = 0; - - - /* compare exponents */ - big = a; small = b; - if (big->exp != small->exp) { - if (small->exp > big->exp) { - _mpd_ptrswap(&big, &small); - swap++; - } - /* align the coefficients */ - if (!mpd_iszerocoeff(big)) { - exp = big->exp - 1; - exp += (big->digits > ctx->prec) ? 0 : big->digits-ctx->prec-1; - if (mpd_adjexp(small) < exp) { - /* - * Avoid huge shifts by substituting a value for small that is - * guaranteed to produce the same results. - * - * adjexp(small) < exp if and only if: - * - * bdigits <= prec AND - * bdigits+shift >= prec+2+sdigits AND - * exp = bexp+bdigits-prec-2 - * - * 1234567000000000 -> bdigits + shift - * ----------XX1234 -> sdigits - * ----------X1 -> tiny-digits - * |- prec -| - * - * OR - * - * bdigits > prec AND - * shift > sdigits AND - * exp = bexp-1 - * - * 1234567892100000 -> bdigits + shift - * ----------XX1234 -> sdigits - * ----------X1 -> tiny-digits - * |- prec -| - * - * If tiny is zero, adding or subtracting is a no-op. - * Otherwise, adding tiny generates a non-zero digit either - * below the rounding digit or the least significant digit - * of big. When subtracting, tiny is in the same position as - * the carry that would be generated by subtracting sdigits. - */ - mpd_copy_flags(&tiny, small); - tiny.exp = exp; - tiny.digits = 1; - tiny.len = 1; - tiny.data[0] = mpd_iszerocoeff(small) ? 0 : 1; - small = &tiny; - } - /* This cannot wrap: the difference is positive and <= maxprec */ - shift = big->exp - small->exp; - if (!mpd_qshiftl(&big_aligned, big, shift, status)) { - mpd_seterror(result, MPD_Malloc_error, status); - goto finish; - } - big = &big_aligned; - } - } - result->exp = small->exp; - - - /* compare length of coefficients */ - if (big->len < small->len) { - _mpd_ptrswap(&big, &small); - swap++; - } - - newsize = big->len; - if (!mpd_qresize(result, newsize, status)) { - goto finish; - } - - if (mpd_sign(a) == sign_b) { - - carry = _mpd_baseadd(result->data, big->data, small->data, - big->len, small->len); - - if (carry) { - newsize = big->len + 1; - if (!mpd_qresize(result, newsize, status)) { - goto finish; - } - result->data[newsize-1] = carry; - } - - result->len = newsize; - mpd_set_flags(result, sign_b); - } - else { - if (big->len == small->len) { - for (i=big->len-1; i >= 0; --i) { - if (big->data[i] != small->data[i]) { - if (big->data[i] < small->data[i]) { - _mpd_ptrswap(&big, &small); - swap++; - } - break; - } - } - } - - _mpd_basesub(result->data, big->data, small->data, - big->len, small->len); - newsize = _mpd_real_size(result->data, big->len); - /* resize to smaller cannot fail */ - (void)mpd_qresize(result, newsize, status); - - result->len = newsize; - sign_b = (swap & 1) ? sign_b : mpd_sign(a); - mpd_set_flags(result, sign_b); - - if (mpd_iszerocoeff(result)) { - mpd_set_positive(result); - if (ctx->round == MPD_ROUND_FLOOR) { - mpd_set_negative(result); - } - } - } - - mpd_setdigits(result); - -finish: - mpd_del(&big_aligned); -} - -/* Add a and b. No specials, no finalizing. */ -static void -_mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - _mpd_qaddsub(result, a, b, mpd_sign(b), ctx, status); -} - -/* Subtract b from a. No specials, no finalizing. */ -static void -_mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - _mpd_qaddsub(result, a, b, !mpd_sign(b), ctx, status); -} - -/* Add a and b. */ -void -mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - _mpd_qaddsub_inf(result, a, b, mpd_sign(b), status); - return; - } - - _mpd_qaddsub(result, a, b, mpd_sign(b), ctx, status); - mpd_qfinalize(result, ctx, status); -} - -/* Add a and b. Set NaN/Invalid_operation if the result is inexact. */ -static void -_mpd_qadd_exact(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - uint32_t workstatus = 0; - - mpd_qadd(result, a, b, ctx, &workstatus); - *status |= workstatus; - if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) { - mpd_seterror(result, MPD_Invalid_operation, status); - } -} - -/* Subtract b from a. */ -void -mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - _mpd_qaddsub_inf(result, a, b, !mpd_sign(b), status); - return; - } - - _mpd_qaddsub(result, a, b, !mpd_sign(b), ctx, status); - mpd_qfinalize(result, ctx, status); -} - -/* Subtract b from a. Set NaN/Invalid_operation if the result is inexact. */ -static void -_mpd_qsub_exact(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - uint32_t workstatus = 0; - - mpd_qsub(result, a, b, ctx, &workstatus); - *status |= workstatus; - if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) { - mpd_seterror(result, MPD_Invalid_operation, status); - } -} - -/* Add decimal and mpd_ssize_t. */ -void -mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qsset_ssize(&bb, b, &maxcontext, status); - mpd_qadd(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Add decimal and mpd_uint_t. */ -void -mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qsset_uint(&bb, b, &maxcontext, status); - mpd_qadd(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Subtract mpd_ssize_t from decimal. */ -void -mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qsset_ssize(&bb, b, &maxcontext, status); - mpd_qsub(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Subtract mpd_uint_t from decimal. */ -void -mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qsset_uint(&bb, b, &maxcontext, status); - mpd_qsub(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Add decimal and int32_t. */ -void -mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qadd_ssize(result, a, b, ctx, status); -} - -/* Add decimal and uint32_t. */ -void -mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qadd_uint(result, a, b, ctx, status); -} - -#ifdef CONFIG_64 -/* Add decimal and int64_t. */ -void -mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qadd_ssize(result, a, b, ctx, status); -} - -/* Add decimal and uint64_t. */ -void -mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qadd_uint(result, a, b, ctx, status); -} -#elif !defined(LEGACY_COMPILER) -/* Add decimal and int64_t. */ -void -mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qset_i64(&bb, b, &maxcontext, status); - mpd_qadd(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Add decimal and uint64_t. */ -void -mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qset_u64(&bb, b, &maxcontext, status); - mpd_qadd(result, a, &bb, ctx, status); - mpd_del(&bb); -} -#endif - -/* Subtract int32_t from decimal. */ -void -mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qsub_ssize(result, a, b, ctx, status); -} - -/* Subtract uint32_t from decimal. */ -void -mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qsub_uint(result, a, b, ctx, status); -} - -#ifdef CONFIG_64 -/* Subtract int64_t from decimal. */ -void -mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qsub_ssize(result, a, b, ctx, status); -} - -/* Subtract uint64_t from decimal. */ -void -mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qsub_uint(result, a, b, ctx, status); -} -#elif !defined(LEGACY_COMPILER) -/* Subtract int64_t from decimal. */ -void -mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qset_i64(&bb, b, &maxcontext, status); - mpd_qsub(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Subtract uint64_t from decimal. */ -void -mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qset_u64(&bb, b, &maxcontext, status); - mpd_qsub(result, a, &bb, ctx, status); - mpd_del(&bb); -} -#endif - - -/* Divide infinities. */ -static void -_mpd_qdiv_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - if (mpd_isinfinite(a)) { - if (mpd_isinfinite(b)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF); - return; - } - assert(mpd_isinfinite(b)); - _settriple(result, mpd_sign(a)^mpd_sign(b), 0, mpd_etiny(ctx)); - *status |= MPD_Clamped; -} - -enum {NO_IDEAL_EXP, SET_IDEAL_EXP}; -/* Divide a by b. */ -static void -_mpd_qdiv(int action, mpd_t *q, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - MPD_NEW_STATIC(aligned,0,0,0,0); - mpd_uint_t ld; - mpd_ssize_t shift, exp, tz; - mpd_ssize_t newsize; - mpd_ssize_t ideal_exp; - mpd_uint_t rem; - uint8_t sign_a = mpd_sign(a); - uint8_t sign_b = mpd_sign(b); - - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(q, a, b, ctx, status)) { - return; - } - _mpd_qdiv_inf(q, a, b, ctx, status); - return; - } - if (mpd_iszerocoeff(b)) { - if (mpd_iszerocoeff(a)) { - mpd_seterror(q, MPD_Division_undefined, status); - } - else { - mpd_setspecial(q, sign_a^sign_b, MPD_INF); - *status |= MPD_Division_by_zero; - } - return; - } - if (mpd_iszerocoeff(a)) { - exp = a->exp - b->exp; - _settriple(q, sign_a^sign_b, 0, exp); - mpd_qfinalize(q, ctx, status); - return; - } - - shift = (b->digits - a->digits) + ctx->prec + 1; - ideal_exp = a->exp - b->exp; - exp = ideal_exp - shift; - if (shift > 0) { - if (!mpd_qshiftl(&aligned, a, shift, status)) { - mpd_seterror(q, MPD_Malloc_error, status); - goto finish; - } - a = &aligned; - } - else if (shift < 0) { - shift = -shift; - if (!mpd_qshiftl(&aligned, b, shift, status)) { - mpd_seterror(q, MPD_Malloc_error, status); - goto finish; - } - b = &aligned; - } - - - newsize = a->len - b->len + 1; - if ((q != b && q != a) || (q == b && newsize > b->len)) { - if (!mpd_qresize(q, newsize, status)) { - mpd_seterror(q, MPD_Malloc_error, status); - goto finish; - } - } - - - if (b->len == 1) { - rem = _mpd_shortdiv(q->data, a->data, a->len, b->data[0]); - } - else if (b->len <= MPD_NEWTONDIV_CUTOFF) { - int ret = _mpd_basedivmod(q->data, NULL, a->data, b->data, - a->len, b->len); - if (ret < 0) { - mpd_seterror(q, MPD_Malloc_error, status); - goto finish; - } - rem = ret; - } - else { - MPD_NEW_STATIC(r,0,0,0,0); - _mpd_base_ndivmod(q, &r, a, b, status); - if (mpd_isspecial(q) || mpd_isspecial(&r)) { - mpd_setspecial(q, MPD_POS, MPD_NAN); - mpd_del(&r); - goto finish; - } - rem = !mpd_iszerocoeff(&r); - mpd_del(&r); - newsize = q->len; - } - - newsize = _mpd_real_size(q->data, newsize); - /* resize to smaller cannot fail */ - mpd_qresize(q, newsize, status); - mpd_set_flags(q, sign_a^sign_b); - q->len = newsize; - mpd_setdigits(q); - - shift = ideal_exp - exp; - if (rem) { - ld = mpd_lsd(q->data[0]); - if (ld == 0 || ld == 5) { - q->data[0] += 1; - } - } - else if (action == SET_IDEAL_EXP && shift > 0) { - tz = mpd_trail_zeros(q); - shift = (tz > shift) ? shift : tz; - mpd_qshiftr_inplace(q, shift); - exp += shift; - } - - q->exp = exp; - - -finish: - mpd_del(&aligned); - mpd_qfinalize(q, ctx, status); -} - -/* Divide a by b. */ -void -mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - MPD_NEW_STATIC(aa,0,0,0,0); - MPD_NEW_STATIC(bb,0,0,0,0); - uint32_t xstatus = 0; - - if (q == a) { - if (!mpd_qcopy(&aa, a, status)) { - mpd_seterror(q, MPD_Malloc_error, status); - goto out; - } - a = &aa; - } - - if (q == b) { - if (!mpd_qcopy(&bb, b, status)) { - mpd_seterror(q, MPD_Malloc_error, status); - goto out; - } - b = &bb; - } - - _mpd_qdiv(SET_IDEAL_EXP, q, a, b, ctx, &xstatus); - - if (xstatus & (MPD_Malloc_error|MPD_Division_impossible)) { - /* Inexact quotients (the usual case) fill the entire context precision, - * which can lead to the above errors for very high precisions. Retry - * the operation with a lower precision in case the result is exact. - * - * We need an upper bound for the number of digits of a_coeff / b_coeff - * when the result is exact. If a_coeff' * 1 / b_coeff' is in lowest - * terms, then maxdigits(a_coeff') + maxdigits(1 / b_coeff') is a suitable - * bound. - * - * 1 / b_coeff' is exact iff b_coeff' exclusively has prime factors 2 or 5. - * The largest amount of digits is generated if b_coeff' is a power of 2 or - * a power of 5 and is less than or equal to log5(b_coeff') <= log2(b_coeff'). - * - * We arrive at a total upper bound: - * - * maxdigits(a_coeff') + maxdigits(1 / b_coeff') <= - * log10(a_coeff) + log2(b_coeff) = - * log10(a_coeff) + log10(b_coeff) / log10(2) <= - * a->digits + b->digits * 4; - */ - mpd_context_t workctx = *ctx; - uint32_t ystatus = 0; - - workctx.prec = a->digits + b->digits * 4; - if (workctx.prec >= ctx->prec) { - *status |= (xstatus&MPD_Errors); - goto out; /* No point in retrying, keep the original error. */ - } - - _mpd_qdiv(SET_IDEAL_EXP, q, a, b, &workctx, &ystatus); - if (ystatus != 0) { - ystatus = *status | ((ystatus|xstatus)&MPD_Errors); - mpd_seterror(q, ystatus, status); - } - } - else { - *status |= xstatus; - } - - -out: - mpd_del(&aa); - mpd_del(&bb); -} - -/* Internal function. */ -static void -_mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - MPD_NEW_STATIC(aligned,0,0,0,0); - mpd_ssize_t qsize, rsize; - mpd_ssize_t ideal_exp, expdiff, shift; - uint8_t sign_a = mpd_sign(a); - uint8_t sign_ab = mpd_sign(a)^mpd_sign(b); - - - ideal_exp = (a->exp > b->exp) ? b->exp : a->exp; - if (mpd_iszerocoeff(a)) { - if (!mpd_qcopy(r, a, status)) { - goto nanresult; /* GCOV_NOT_REACHED */ - } - r->exp = ideal_exp; - _settriple(q, sign_ab, 0, 0); - return; - } - - expdiff = mpd_adjexp(a) - mpd_adjexp(b); - if (expdiff < 0) { - if (a->exp > b->exp) { - /* positive and less than b->digits - a->digits */ - shift = a->exp - b->exp; - if (!mpd_qshiftl(r, a, shift, status)) { - goto nanresult; - } - r->exp = ideal_exp; - } - else { - if (!mpd_qcopy(r, a, status)) { - goto nanresult; - } - } - _settriple(q, sign_ab, 0, 0); - return; - } - if (expdiff > ctx->prec) { - *status |= MPD_Division_impossible; - goto nanresult; - } - - - /* - * At this point we have: - * (1) 0 <= a->exp + a->digits - b->exp - b->digits <= prec - * (2) a->exp - b->exp >= b->digits - a->digits - * (3) a->exp - b->exp <= prec + b->digits - a->digits - */ - if (a->exp != b->exp) { - shift = a->exp - b->exp; - if (shift > 0) { - /* by (3), after the shift a->digits <= prec + b->digits */ - if (!mpd_qshiftl(&aligned, a, shift, status)) { - goto nanresult; - } - a = &aligned; - } - else { - shift = -shift; - /* by (2), after the shift b->digits <= a->digits */ - if (!mpd_qshiftl(&aligned, b, shift, status)) { - goto nanresult; - } - b = &aligned; - } - } - - - qsize = a->len - b->len + 1; - if (!(q == a && qsize < a->len) && !(q == b && qsize < b->len)) { - if (!mpd_qresize(q, qsize, status)) { - goto nanresult; - } - } - - rsize = b->len; - if (!(r == a && rsize < a->len)) { - if (!mpd_qresize(r, rsize, status)) { - goto nanresult; - } - } - - if (b->len == 1) { - assert(b->data[0] != 0); /* annotation for scan-build */ - if (a->len == 1) { - _mpd_div_word(&q->data[0], &r->data[0], a->data[0], b->data[0]); - } - else { - r->data[0] = _mpd_shortdiv(q->data, a->data, a->len, b->data[0]); - } - } - else if (b->len <= MPD_NEWTONDIV_CUTOFF) { - int ret; - ret = _mpd_basedivmod(q->data, r->data, a->data, b->data, - a->len, b->len); - if (ret == -1) { - *status |= MPD_Malloc_error; - goto nanresult; - } - } - else { - _mpd_base_ndivmod(q, r, a, b, status); - if (mpd_isspecial(q) || mpd_isspecial(r)) { - goto nanresult; - } - qsize = q->len; - rsize = r->len; - } - - qsize = _mpd_real_size(q->data, qsize); - /* resize to smaller cannot fail */ - mpd_qresize(q, qsize, status); - q->len = qsize; - mpd_setdigits(q); - mpd_set_flags(q, sign_ab); - q->exp = 0; - if (q->digits > ctx->prec) { - *status |= MPD_Division_impossible; - goto nanresult; - } - - rsize = _mpd_real_size(r->data, rsize); - /* resize to smaller cannot fail */ - mpd_qresize(r, rsize, status); - r->len = rsize; - mpd_setdigits(r); - mpd_set_flags(r, sign_a); - r->exp = ideal_exp; - -out: - mpd_del(&aligned); - return; - -nanresult: - mpd_setspecial(q, MPD_POS, MPD_NAN); - mpd_setspecial(r, MPD_POS, MPD_NAN); - goto out; -} - -/* Integer division with remainder. */ -void -mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - uint8_t sign = mpd_sign(a)^mpd_sign(b); - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(q, a, b, ctx, status)) { - mpd_qcopy(r, q, status); - return; - } - if (mpd_isinfinite(a)) { - if (mpd_isinfinite(b)) { - mpd_setspecial(q, MPD_POS, MPD_NAN); - } - else { - mpd_setspecial(q, sign, MPD_INF); - } - mpd_setspecial(r, MPD_POS, MPD_NAN); - *status |= MPD_Invalid_operation; - return; - } - if (mpd_isinfinite(b)) { - if (!mpd_qcopy(r, a, status)) { - mpd_seterror(q, MPD_Malloc_error, status); - return; - } - mpd_qfinalize(r, ctx, status); - _settriple(q, sign, 0, 0); - return; - } - /* debug */ - abort(); /* GCOV_NOT_REACHED */ - } - if (mpd_iszerocoeff(b)) { - if (mpd_iszerocoeff(a)) { - mpd_setspecial(q, MPD_POS, MPD_NAN); - mpd_setspecial(r, MPD_POS, MPD_NAN); - *status |= MPD_Division_undefined; - } - else { - mpd_setspecial(q, sign, MPD_INF); - mpd_setspecial(r, MPD_POS, MPD_NAN); - *status |= (MPD_Division_by_zero|MPD_Invalid_operation); - } - return; - } - - _mpd_qdivmod(q, r, a, b, ctx, status); - mpd_qfinalize(q, ctx, status); - mpd_qfinalize(r, ctx, status); -} - -void -mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - MPD_NEW_STATIC(r,0,0,0,0); - uint8_t sign = mpd_sign(a)^mpd_sign(b); - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(q, a, b, ctx, status)) { - return; - } - if (mpd_isinfinite(a) && mpd_isinfinite(b)) { - mpd_seterror(q, MPD_Invalid_operation, status); - return; - } - if (mpd_isinfinite(a)) { - mpd_setspecial(q, sign, MPD_INF); - return; - } - if (mpd_isinfinite(b)) { - _settriple(q, sign, 0, 0); - return; - } - /* debug */ - abort(); /* GCOV_NOT_REACHED */ - } - if (mpd_iszerocoeff(b)) { - if (mpd_iszerocoeff(a)) { - mpd_seterror(q, MPD_Division_undefined, status); - } - else { - mpd_setspecial(q, sign, MPD_INF); - *status |= MPD_Division_by_zero; - } - return; - } - - - _mpd_qdivmod(q, &r, a, b, ctx, status); - mpd_del(&r); - mpd_qfinalize(q, ctx, status); -} - -/* Divide decimal by mpd_ssize_t. */ -void -mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qsset_ssize(&bb, b, &maxcontext, status); - mpd_qdiv(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Divide decimal by mpd_uint_t. */ -void -mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qsset_uint(&bb, b, &maxcontext, status); - mpd_qdiv(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Divide decimal by int32_t. */ -void -mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qdiv_ssize(result, a, b, ctx, status); -} - -/* Divide decimal by uint32_t. */ -void -mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qdiv_uint(result, a, b, ctx, status); -} - -#ifdef CONFIG_64 -/* Divide decimal by int64_t. */ -void -mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qdiv_ssize(result, a, b, ctx, status); -} - -/* Divide decimal by uint64_t. */ -void -mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qdiv_uint(result, a, b, ctx, status); -} -#elif !defined(LEGACY_COMPILER) -/* Divide decimal by int64_t. */ -void -mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qset_i64(&bb, b, &maxcontext, status); - mpd_qdiv(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Divide decimal by uint64_t. */ -void -mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qset_u64(&bb, b, &maxcontext, status); - mpd_qdiv(result, a, &bb, ctx, status); - mpd_del(&bb); -} -#endif - -/* Pad the result with trailing zeros if it has fewer digits than prec. */ -static void -_mpd_zeropad(mpd_t *result, const mpd_context_t *ctx, uint32_t *status) -{ - if (!mpd_isspecial(result) && !mpd_iszero(result) && - result->digits < ctx->prec) { - mpd_ssize_t shift = ctx->prec - result->digits; - mpd_qshiftl(result, result, shift, status); - result->exp -= shift; - } -} - -/* Check if the result is guaranteed to be one. */ -static int -_mpd_qexp_check_one(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - MPD_NEW_CONST(lim,0,-(ctx->prec+1),1,1,1,9); - MPD_NEW_SHARED(aa, a); - - mpd_set_positive(&aa); - - /* abs(a) <= 9 * 10**(-prec-1) */ - if (_mpd_cmp(&aa, &lim) <= 0) { - _settriple(result, 0, 1, 0); - *status |= MPD_Rounded|MPD_Inexact; - return 1; - } - - return 0; -} - -/* - * Get the number of iterations for the Horner scheme in _mpd_qexp(). - */ -static inline mpd_ssize_t -_mpd_get_exp_iterations(const mpd_t *r, mpd_ssize_t p) -{ - mpd_ssize_t log10pbyr; /* lower bound for log10(p / abs(r)) */ - mpd_ssize_t n; - - assert(p >= 10); - assert(!mpd_iszero(r)); - assert(-p < mpd_adjexp(r) && mpd_adjexp(r) <= -1); - -#ifdef CONFIG_64 - if (p > (mpd_ssize_t)(1ULL<<52)) { - return MPD_SSIZE_MAX; - } -#endif - - /* - * Lower bound for log10(p / abs(r)): adjexp(p) - (adjexp(r) + 1) - * At this point (for CONFIG_64, CONFIG_32 is not problematic): - * 1) 10 <= p <= 2**52 - * 2) -p < adjexp(r) <= -1 - * 3) 1 <= log10pbyr <= 2**52 + 14 - */ - log10pbyr = (mpd_word_digits(p)-1) - (mpd_adjexp(r)+1); - - /* - * The numerator in the paper is 1.435 * p - 1.182, calculated - * exactly. We compensate for rounding errors by using 1.43503. - * ACL2 proofs: - * 1) exp-iter-approx-lower-bound: The term below evaluated - * in 53-bit floating point arithmetic is greater than or - * equal to the exact term used in the paper. - * 2) exp-iter-approx-upper-bound: The term below is less than - * or equal to 3/2 * p <= 3/2 * 2**52. - */ - n = (mpd_ssize_t)ceil((1.43503*(double)p - 1.182) / (double)log10pbyr); - return n >= 3 ? n : 3; -} - -/* - * Internal function, specials have been dealt with. Apart from Overflow - * and Underflow, two cases must be considered for the error of the result: - * - * 1) abs(a) <= 9 * 10**(-prec-1) ==> result == 1 - * - * Absolute error: abs(1 - e**x) < 10**(-prec) - * ------------------------------------------- - * - * 2) abs(a) > 9 * 10**(-prec-1) - * - * Relative error: abs(result - e**x) < 0.5 * 10**(-prec) * e**x - * ------------------------------------------------------------- - * - * The algorithm is from Hull&Abrham, Variable Precision Exponential Function, - * ACM Transactions on Mathematical Software, Vol. 12, No. 2, June 1986. - * - * Main differences: - * - * - The number of iterations for the Horner scheme is calculated using - * 53-bit floating point arithmetic. - * - * - In the error analysis for ER (relative error accumulated in the - * evaluation of the truncated series) the reduced operand r may - * have any number of digits. - * ACL2 proof: exponent-relative-error - * - * - The analysis for early abortion has been adapted for the mpd_t - * ranges. - */ -static void -_mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t workctx; - MPD_NEW_STATIC(tmp,0,0,0,0); - MPD_NEW_STATIC(sum,0,0,0,0); - MPD_NEW_CONST(word,0,0,1,1,1,1); - mpd_ssize_t j, n, t; - - assert(!mpd_isspecial(a)); - - if (mpd_iszerocoeff(a)) { - _settriple(result, MPD_POS, 1, 0); - return; - } - - /* - * We are calculating e^x = e^(r*10^t) = (e^r)^(10^t), where abs(r) < 1 and t >= 0. - * - * If t > 0, we have: - * - * (1) 0.1 <= r < 1, so e^0.1 <= e^r. If t > MAX_T, overflow occurs: - * - * MAX-EMAX+1 < log10(e^(0.1*10*t)) <= log10(e^(r*10^t)) < adjexp(e^(r*10^t))+1 - * - * (2) -1 < r <= -0.1, so e^r <= e^-0.1. If t > MAX_T, underflow occurs: - * - * adjexp(e^(r*10^t)) <= log10(e^(r*10^t)) <= log10(e^(-0.1*10^t)) < MIN-ETINY - */ -#if defined(CONFIG_64) - #define MPD_EXP_MAX_T 19 -#elif defined(CONFIG_32) - #define MPD_EXP_MAX_T 10 -#endif - t = a->digits + a->exp; - t = (t > 0) ? t : 0; - if (t > MPD_EXP_MAX_T) { - if (mpd_ispositive(a)) { - mpd_setspecial(result, MPD_POS, MPD_INF); - *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded; - } - else { - _settriple(result, MPD_POS, 0, mpd_etiny(ctx)); - *status |= (MPD_Inexact|MPD_Rounded|MPD_Subnormal| - MPD_Underflow|MPD_Clamped); - } - return; - } - - /* abs(a) <= 9 * 10**(-prec-1) */ - if (_mpd_qexp_check_one(result, a, ctx, status)) { - return; - } - - mpd_maxcontext(&workctx); - workctx.prec = ctx->prec + t + 2; - workctx.prec = (workctx.prec < 10) ? 10 : workctx.prec; - workctx.round = MPD_ROUND_HALF_EVEN; - - if (!mpd_qcopy(result, a, status)) { - return; - } - result->exp -= t; - - /* - * At this point: - * 1) 9 * 10**(-prec-1) < abs(a) - * 2) 9 * 10**(-prec-t-1) < abs(r) - * 3) log10(9) - prec - t - 1 < log10(abs(r)) < adjexp(abs(r)) + 1 - * 4) - prec - t - 2 < adjexp(abs(r)) <= -1 - */ - n = _mpd_get_exp_iterations(result, workctx.prec); - if (n == MPD_SSIZE_MAX) { - mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_UNLIKELY */ - return; /* GCOV_UNLIKELY */ - } - - _settriple(&sum, MPD_POS, 1, 0); - - for (j = n-1; j >= 1; j--) { - word.data[0] = j; - mpd_setdigits(&word); - mpd_qdiv(&tmp, result, &word, &workctx, &workctx.status); - mpd_qfma(&sum, &sum, &tmp, &one, &workctx, &workctx.status); - } - -#ifdef CONFIG_64 - _mpd_qpow_uint(result, &sum, mpd_pow10[t], MPD_POS, &workctx, status); -#else - if (t <= MPD_MAX_POW10) { - _mpd_qpow_uint(result, &sum, mpd_pow10[t], MPD_POS, &workctx, status); - } - else { - t -= MPD_MAX_POW10; - _mpd_qpow_uint(&tmp, &sum, mpd_pow10[MPD_MAX_POW10], MPD_POS, - &workctx, status); - _mpd_qpow_uint(result, &tmp, mpd_pow10[t], MPD_POS, &workctx, status); - } -#endif - - mpd_del(&tmp); - mpd_del(&sum); - *status |= (workctx.status&MPD_Errors); - *status |= (MPD_Inexact|MPD_Rounded); -} - -/* exp(a) */ -void -mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t workctx; - - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - if (mpd_isnegative(a)) { - _settriple(result, MPD_POS, 0, 0); - } - else { - mpd_setspecial(result, MPD_POS, MPD_INF); - } - return; - } - if (mpd_iszerocoeff(a)) { - _settriple(result, MPD_POS, 1, 0); - return; - } - - workctx = *ctx; - workctx.round = MPD_ROUND_HALF_EVEN; - - if (ctx->allcr) { - MPD_NEW_STATIC(t1, 0,0,0,0); - MPD_NEW_STATIC(t2, 0,0,0,0); - MPD_NEW_STATIC(ulp, 0,0,0,0); - MPD_NEW_STATIC(aa, 0,0,0,0); - mpd_ssize_t prec; - mpd_ssize_t ulpexp; - uint32_t workstatus; - - if (result == a) { - if (!mpd_qcopy(&aa, a, status)) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - a = &aa; - } - - workctx.clamp = 0; - prec = ctx->prec + 3; - while (1) { - workctx.prec = prec; - workstatus = 0; - - _mpd_qexp(result, a, &workctx, &workstatus); - *status |= workstatus; - - ulpexp = result->exp + result->digits - workctx.prec; - if (workstatus & MPD_Underflow) { - /* The effective work precision is result->digits. */ - ulpexp = result->exp; - } - _ssettriple(&ulp, MPD_POS, 1, ulpexp); - - /* - * At this point [1]: - * 1) abs(result - e**x) < 0.5 * 10**(-prec) * e**x - * 2) result - ulp < e**x < result + ulp - * 3) result - ulp < result < result + ulp - * - * If round(result-ulp)==round(result+ulp), then - * round(result)==round(e**x). Therefore the result - * is correctly rounded. - * - * [1] If abs(a) <= 9 * 10**(-prec-1), use the absolute - * error for a similar argument. - */ - workctx.prec = ctx->prec; - mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status); - mpd_qsub(&t2, result, &ulp, &workctx, &workctx.status); - if (mpd_isspecial(result) || mpd_iszerocoeff(result) || - mpd_qcmp(&t1, &t2, status) == 0) { - workctx.clamp = ctx->clamp; - _mpd_zeropad(result, &workctx, status); - mpd_check_underflow(result, &workctx, status); - mpd_qfinalize(result, &workctx, status); - break; - } - prec += MPD_RDIGITS; - } - mpd_del(&t1); - mpd_del(&t2); - mpd_del(&ulp); - mpd_del(&aa); - } - else { - _mpd_qexp(result, a, &workctx, status); - _mpd_zeropad(result, &workctx, status); - mpd_check_underflow(result, &workctx, status); - mpd_qfinalize(result, &workctx, status); - } -} - -/* Fused multiply-add: (a * b) + c, with a single final rounding. */ -void -mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, - const mpd_context_t *ctx, uint32_t *status) -{ - uint32_t workstatus = 0; - mpd_t *cc = NULL; - - if (result == c) { - if ((cc = mpd_qncopy(c)) == NULL) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - c = cc; - } - - _mpd_qmul(result, a, b, ctx, &workstatus); - if (!(workstatus&MPD_Invalid_operation)) { - mpd_qadd(result, result, c, ctx, &workstatus); - } - - if (cc) mpd_del(cc); - *status |= workstatus; -} - -/* - * Schedule the optimal precision increase for the Newton iteration. - * v := input operand - * z_0 := initial approximation - * initprec := natural number such that abs(log(v) - z_0) < 10**-initprec - * maxprec := target precision - * - * For convenience the output klist contains the elements in reverse order: - * klist := [k_n-1, ..., k_0], where - * 1) k_0 <= initprec and - * 2) abs(log(v) - result) < 10**(-2*k_n-1 + 1) <= 10**-maxprec. - */ -static inline int -ln_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], mpd_ssize_t maxprec, - mpd_ssize_t initprec) -{ - mpd_ssize_t k; - int i; - - assert(maxprec >= 2 && initprec >= 2); - if (maxprec <= initprec) return -1; - - i = 0; k = maxprec; - do { - k = (k+2) / 2; - klist[i++] = k; - } while (k > initprec); - - return i-1; -} - -/* The constants have been verified with both decimal.py and mpfr. */ -#ifdef CONFIG_64 -#if MPD_RDIGITS != 19 - #error "mpdecimal.c: MPD_RDIGITS must be 19." -#endif -static const mpd_uint_t mpd_ln10_data[MPD_MINALLOC_MAX] = { - 6983716328982174407ULL, 9089704281976336583ULL, 1515961135648465461ULL, - 4416816335727555703ULL, 2900988039194170265ULL, 2307925037472986509ULL, - 107598438319191292ULL, 3466624107184669231ULL, 4450099781311469159ULL, - 9807828059751193854ULL, 7713456862091670584ULL, 1492198849978748873ULL, - 6528728696511086257ULL, 2385392051446341972ULL, 8692180205189339507ULL, - 6518769751037497088ULL, 2375253577097505395ULL, 9095610299291824318ULL, - 982748238504564801ULL, 5438635917781170543ULL, 7547331541421808427ULL, - 752371033310119785ULL, 3171643095059950878ULL, 9785265383207606726ULL, - 2932258279850258550ULL, 5497347726624257094ULL, 2976979522110718264ULL, - 9221477656763693866ULL, 1979650047149510504ULL, 6674183485704422507ULL, - 9702766860595249671ULL, 9278096762712757753ULL, 9314848524948644871ULL, - 6826928280848118428ULL, 754403708474699401ULL, 230105703089634572ULL, - 1929203337658714166ULL, 7589402567763113569ULL, 4208241314695689016ULL, - 2922455440575892572ULL, 9356734206705811364ULL, 2684916746550586856ULL, - 644507064800027750ULL, 9476834636167921018ULL, 5659121373450747856ULL, - 2835522011480466371ULL, 6470806855677432162ULL, 7141748003688084012ULL, - 9619404400222105101ULL, 5504893431493939147ULL, 6674744042432743651ULL, - 2287698219886746543ULL, 7773262884616336622ULL, 1985283935053089653ULL, - 4680843799894826233ULL, 8168948290720832555ULL, 8067566662873690987ULL, - 6248633409525465082ULL, 9829834196778404228ULL, 3524802359972050895ULL, - 3327900967572609677ULL, 110148862877297603ULL, 179914546843642076ULL, - 2302585092994045684ULL -}; -#else -#if MPD_RDIGITS != 9 - #error "mpdecimal.c: MPD_RDIGITS must be 9." -#endif -static const mpd_uint_t mpd_ln10_data[MPD_MINALLOC_MAX] = { - 401682692UL, 708474699UL, 720754403UL, 30896345UL, 602301057UL, 765871416UL, - 192920333UL, 763113569UL, 589402567UL, 956890167UL, 82413146UL, 589257242UL, - 245544057UL, 811364292UL, 734206705UL, 868569356UL, 167465505UL, 775026849UL, - 706480002UL, 18064450UL, 636167921UL, 569476834UL, 734507478UL, 156591213UL, - 148046637UL, 283552201UL, 677432162UL, 470806855UL, 880840126UL, 417480036UL, - 210510171UL, 940440022UL, 939147961UL, 893431493UL, 436515504UL, 440424327UL, - 654366747UL, 821988674UL, 622228769UL, 884616336UL, 537773262UL, 350530896UL, - 319852839UL, 989482623UL, 468084379UL, 720832555UL, 168948290UL, 736909878UL, - 675666628UL, 546508280UL, 863340952UL, 404228624UL, 834196778UL, 508959829UL, - 23599720UL, 967735248UL, 96757260UL, 603332790UL, 862877297UL, 760110148UL, - 468436420UL, 401799145UL, 299404568UL, 230258509UL -}; -#endif -/* _mpd_ln10 is used directly for precisions smaller than MINALLOC_MAX*RDIGITS. - Otherwise, it serves as the initial approximation for calculating ln(10). */ -static const mpd_t _mpd_ln10 = { - MPD_STATIC|MPD_CONST_DATA, -(MPD_MINALLOC_MAX*MPD_RDIGITS-1), - MPD_MINALLOC_MAX*MPD_RDIGITS, MPD_MINALLOC_MAX, MPD_MINALLOC_MAX, - (mpd_uint_t *)mpd_ln10_data -}; - -/* - * Set 'result' to log(10). - * Ulp error: abs(result - log(10)) < ulp(log(10)) - * Relative error: abs(result - log(10)) < 5 * 10**-prec * log(10) - * - * NOTE: The relative error is not derived from the ulp error, but - * calculated separately using the fact that 23/10 < log(10) < 24/10. - */ -void -mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status) -{ - mpd_context_t varcontext, maxcontext; - MPD_NEW_STATIC(tmp, 0,0,0,0); - MPD_NEW_CONST(static10, 0,0,2,1,1,10); - mpd_ssize_t klist[MPD_MAX_PREC_LOG2]; - mpd_uint_t rnd; - mpd_ssize_t shift; - int i; - - assert(prec >= 1); - - shift = MPD_MINALLOC_MAX*MPD_RDIGITS-prec; - shift = shift < 0 ? 0 : shift; - - rnd = mpd_qshiftr(result, &_mpd_ln10, shift, status); - if (rnd == MPD_UINT_MAX) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - result->exp = -(result->digits-1); - - mpd_maxcontext(&maxcontext); - if (prec < MPD_MINALLOC_MAX*MPD_RDIGITS) { - maxcontext.prec = prec; - _mpd_apply_round_excess(result, rnd, &maxcontext, status); - *status |= (MPD_Inexact|MPD_Rounded); - return; - } - - mpd_maxcontext(&varcontext); - varcontext.round = MPD_ROUND_TRUNC; - - i = ln_schedule_prec(klist, prec+2, -result->exp); - for (; i >= 0; i--) { - varcontext.prec = 2*klist[i]+3; - result->flags ^= MPD_NEG; - _mpd_qexp(&tmp, result, &varcontext, status); - result->flags ^= MPD_NEG; - mpd_qmul(&tmp, &static10, &tmp, &varcontext, status); - mpd_qsub(&tmp, &tmp, &one, &maxcontext, status); - mpd_qadd(result, result, &tmp, &maxcontext, status); - if (mpd_isspecial(result)) { - break; - } - } - - mpd_del(&tmp); - maxcontext.prec = prec; - mpd_qfinalize(result, &maxcontext, status); -} - -/* - * Initial approximations for the ln() iteration. The values have the - * following properties (established with both decimal.py and mpfr): - * - * Index 0 - 400, logarithms of x in [1.00, 5.00]: - * abs(lnapprox[i] * 10**-3 - log((i+100)/100)) < 10**-2 - * abs(lnapprox[i] * 10**-3 - log((i+1+100)/100)) < 10**-2 - * - * Index 401 - 899, logarithms of x in (0.500, 0.999]: - * abs(-lnapprox[i] * 10**-3 - log((i+100)/1000)) < 10**-2 - * abs(-lnapprox[i] * 10**-3 - log((i+1+100)/1000)) < 10**-2 - */ -static const uint16_t lnapprox[900] = { - /* index 0 - 400: log((i+100)/100) * 1000 */ - 0, 10, 20, 30, 39, 49, 58, 68, 77, 86, 95, 104, 113, 122, 131, 140, 148, 157, - 166, 174, 182, 191, 199, 207, 215, 223, 231, 239, 247, 255, 262, 270, 278, - 285, 293, 300, 308, 315, 322, 329, 336, 344, 351, 358, 365, 372, 378, 385, - 392, 399, 406, 412, 419, 425, 432, 438, 445, 451, 457, 464, 470, 476, 482, - 489, 495, 501, 507, 513, 519, 525, 531, 536, 542, 548, 554, 560, 565, 571, - 577, 582, 588, 593, 599, 604, 610, 615, 621, 626, 631, 637, 642, 647, 652, - 658, 663, 668, 673, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, - 732, 737, 742, 747, 751, 756, 761, 766, 770, 775, 779, 784, 788, 793, 798, - 802, 806, 811, 815, 820, 824, 829, 833, 837, 842, 846, 850, 854, 859, 863, - 867, 871, 876, 880, 884, 888, 892, 896, 900, 904, 908, 912, 916, 920, 924, - 928, 932, 936, 940, 944, 948, 952, 956, 959, 963, 967, 971, 975, 978, 982, - 986, 990, 993, 997, 1001, 1004, 1008, 1012, 1015, 1019, 1022, 1026, 1030, - 1033, 1037, 1040, 1044, 1047, 1051, 1054, 1058, 1061, 1065, 1068, 1072, 1075, - 1078, 1082, 1085, 1089, 1092, 1095, 1099, 1102, 1105, 1109, 1112, 1115, 1118, - 1122, 1125, 1128, 1131, 1135, 1138, 1141, 1144, 1147, 1151, 1154, 1157, 1160, - 1163, 1166, 1169, 1172, 1176, 1179, 1182, 1185, 1188, 1191, 1194, 1197, 1200, - 1203, 1206, 1209, 1212, 1215, 1218, 1221, 1224, 1227, 1230, 1233, 1235, 1238, - 1241, 1244, 1247, 1250, 1253, 1256, 1258, 1261, 1264, 1267, 1270, 1273, 1275, - 1278, 1281, 1284, 1286, 1289, 1292, 1295, 1297, 1300, 1303, 1306, 1308, 1311, - 1314, 1316, 1319, 1322, 1324, 1327, 1330, 1332, 1335, 1338, 1340, 1343, 1345, - 1348, 1351, 1353, 1356, 1358, 1361, 1364, 1366, 1369, 1371, 1374, 1376, 1379, - 1381, 1384, 1386, 1389, 1391, 1394, 1396, 1399, 1401, 1404, 1406, 1409, 1411, - 1413, 1416, 1418, 1421, 1423, 1426, 1428, 1430, 1433, 1435, 1437, 1440, 1442, - 1445, 1447, 1449, 1452, 1454, 1456, 1459, 1461, 1463, 1466, 1468, 1470, 1472, - 1475, 1477, 1479, 1482, 1484, 1486, 1488, 1491, 1493, 1495, 1497, 1500, 1502, - 1504, 1506, 1509, 1511, 1513, 1515, 1517, 1520, 1522, 1524, 1526, 1528, 1530, - 1533, 1535, 1537, 1539, 1541, 1543, 1545, 1548, 1550, 1552, 1554, 1556, 1558, - 1560, 1562, 1564, 1567, 1569, 1571, 1573, 1575, 1577, 1579, 1581, 1583, 1585, - 1587, 1589, 1591, 1593, 1595, 1597, 1599, 1601, 1603, 1605, 1607, 1609, - /* index 401 - 899: -log((i+100)/1000) * 1000 */ - 691, 689, 687, 685, 683, 681, 679, 677, 675, 673, 671, 669, 668, 666, 664, - 662, 660, 658, 656, 654, 652, 650, 648, 646, 644, 642, 641, 639, 637, 635, - 633, 631, 629, 627, 626, 624, 622, 620, 618, 616, 614, 612, 611, 609, 607, - 605, 603, 602, 600, 598, 596, 594, 592, 591, 589, 587, 585, 583, 582, 580, - 578, 576, 574, 573, 571, 569, 567, 566, 564, 562, 560, 559, 557, 555, 553, - 552, 550, 548, 546, 545, 543, 541, 540, 538, 536, 534, 533, 531, 529, 528, - 526, 524, 523, 521, 519, 518, 516, 514, 512, 511, 509, 508, 506, 504, 502, - 501, 499, 498, 496, 494, 493, 491, 489, 488, 486, 484, 483, 481, 480, 478, - 476, 475, 473, 472, 470, 468, 467, 465, 464, 462, 460, 459, 457, 456, 454, - 453, 451, 449, 448, 446, 445, 443, 442, 440, 438, 437, 435, 434, 432, 431, - 429, 428, 426, 425, 423, 422, 420, 419, 417, 416, 414, 412, 411, 410, 408, - 406, 405, 404, 402, 400, 399, 398, 396, 394, 393, 392, 390, 389, 387, 386, - 384, 383, 381, 380, 378, 377, 375, 374, 372, 371, 370, 368, 367, 365, 364, - 362, 361, 360, 358, 357, 355, 354, 352, 351, 350, 348, 347, 345, 344, 342, - 341, 340, 338, 337, 336, 334, 333, 331, 330, 328, 327, 326, 324, 323, 322, - 320, 319, 318, 316, 315, 313, 312, 311, 309, 308, 306, 305, 304, 302, 301, - 300, 298, 297, 296, 294, 293, 292, 290, 289, 288, 286, 285, 284, 282, 281, - 280, 278, 277, 276, 274, 273, 272, 270, 269, 268, 267, 265, 264, 263, 261, - 260, 259, 258, 256, 255, 254, 252, 251, 250, 248, 247, 246, 245, 243, 242, - 241, 240, 238, 237, 236, 234, 233, 232, 231, 229, 228, 227, 226, 224, 223, - 222, 221, 219, 218, 217, 216, 214, 213, 212, 211, 210, 208, 207, 206, 205, - 203, 202, 201, 200, 198, 197, 196, 195, 194, 192, 191, 190, 189, 188, 186, - 185, 184, 183, 182, 180, 179, 178, 177, 176, 174, 173, 172, 171, 170, 168, - 167, 166, 165, 164, 162, 161, 160, 159, 158, 157, 156, 154, 153, 152, 151, - 150, 148, 147, 146, 145, 144, 143, 142, 140, 139, 138, 137, 136, 135, 134, - 132, 131, 130, 129, 128, 127, 126, 124, 123, 122, 121, 120, 119, 118, 116, - 115, 114, 113, 112, 111, 110, 109, 108, 106, 105, 104, 103, 102, 101, 100, - 99, 98, 97, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 84, 83, 82, 81, 80, 79, - 78, 77, 76, 75, 74, 73, 72, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, - 58, 57, 56, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, - 38, 37, 36, 35, 34, 33, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, - 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 -}; - -/* - * Internal ln() function that does not check for specials, zero or one. - * Relative error: abs(result - log(a)) < 0.1 * 10**-prec * abs(log(a)) - */ -static void -_mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t varcontext, maxcontext; - mpd_t *z = result; - MPD_NEW_STATIC(v,0,0,0,0); - MPD_NEW_STATIC(vtmp,0,0,0,0); - MPD_NEW_STATIC(tmp,0,0,0,0); - mpd_ssize_t klist[MPD_MAX_PREC_LOG2]; - mpd_ssize_t maxprec, shift, t; - mpd_ssize_t a_digits, a_exp; - mpd_uint_t dummy, x; - int i; - - assert(!mpd_isspecial(a) && !mpd_iszerocoeff(a)); - - /* - * We are calculating ln(a) = ln(v * 10^t) = ln(v) + t*ln(10), - * where 0.5 < v <= 5. - */ - if (!mpd_qcopy(&v, a, status)) { - mpd_seterror(result, MPD_Malloc_error, status); - goto finish; - } - - /* Initial approximation: we have at least one non-zero digit */ - _mpd_get_msdigits(&dummy, &x, &v, 3); - if (x < 10) x *= 10; - if (x < 100) x *= 10; - x -= 100; - - /* a may equal z */ - a_digits = a->digits; - a_exp = a->exp; - - mpd_minalloc(z); - mpd_clear_flags(z); - z->data[0] = lnapprox[x]; - z->len = 1; - z->exp = -3; - mpd_setdigits(z); - - if (x <= 400) { - /* Reduce the input operand to 1.00 <= v <= 5.00. Let y = x + 100, - * so 100 <= y <= 500. Since y contains the most significant digits - * of v, y/100 <= v < (y+1)/100 and abs(z - log(v)) < 10**-2. */ - v.exp = -(a_digits - 1); - t = a_exp + a_digits - 1; - } - else { - /* Reduce the input operand to 0.500 < v <= 0.999. Let y = x + 100, - * so 500 < y <= 999. Since y contains the most significant digits - * of v, y/1000 <= v < (y+1)/1000 and abs(z - log(v)) < 10**-2. */ - v.exp = -a_digits; - t = a_exp + a_digits; - mpd_set_negative(z); - } - - mpd_maxcontext(&maxcontext); - mpd_maxcontext(&varcontext); - varcontext.round = MPD_ROUND_TRUNC; - - maxprec = ctx->prec + 2; - if (t == 0 && (x <= 15 || x >= 800)) { - /* 0.900 <= v <= 1.15: Estimate the magnitude of the logarithm. - * If ln(v) will underflow, skip the loop. Otherwise, adjust the - * precision upwards in order to obtain a sufficient number of - * significant digits. - * - * Case v > 1: - * abs((v-1)/10) < abs((v-1)/v) < abs(ln(v)) < abs(v-1) - * Case v < 1: - * abs(v-1) < abs(ln(v)) < abs((v-1)/v) < abs((v-1)*10) - */ - int cmp = _mpd_cmp(&v, &one); - - /* Upper bound (assume v > 1): abs(v-1), unrounded */ - _mpd_qsub(&tmp, &v, &one, &maxcontext, &maxcontext.status); - if (maxcontext.status & MPD_Errors) { - mpd_seterror(result, MPD_Malloc_error, status); - goto finish; - } - - if (cmp < 0) { - /* v < 1: abs((v-1)*10) */ - tmp.exp += 1; - } - if (mpd_adjexp(&tmp) < mpd_etiny(ctx)) { - /* The upper bound is less than etiny: Underflow to zero */ - _settriple(result, (cmp<0), 1, mpd_etiny(ctx)-1); - goto finish; - } - /* Lower bound: abs((v-1)/10) or abs(v-1) */ - tmp.exp -= 1; - if (mpd_adjexp(&tmp) < 0) { - /* Absolute error of the loop: abs(z - log(v)) < 10**-p. If - * p = ctx->prec+2-adjexp(lower), then the relative error of - * the result is (using 10**adjexp(x) <= abs(x)): - * - * abs(z - log(v)) / abs(log(v)) < 10**-p / abs(log(v)) - * <= 10**(-ctx->prec-2) - */ - maxprec = maxprec - mpd_adjexp(&tmp); - } - } - - i = ln_schedule_prec(klist, maxprec, 2); - for (; i >= 0; i--) { - varcontext.prec = 2*klist[i]+3; - z->flags ^= MPD_NEG; - _mpd_qexp(&tmp, z, &varcontext, status); - z->flags ^= MPD_NEG; - - if (v.digits > varcontext.prec) { - shift = v.digits - varcontext.prec; - mpd_qshiftr(&vtmp, &v, shift, status); - vtmp.exp += shift; - mpd_qmul(&tmp, &vtmp, &tmp, &varcontext, status); - } - else { - mpd_qmul(&tmp, &v, &tmp, &varcontext, status); - } - - mpd_qsub(&tmp, &tmp, &one, &maxcontext, status); - mpd_qadd(z, z, &tmp, &maxcontext, status); - if (mpd_isspecial(z)) { - break; - } - } - - /* - * Case t == 0: - * t * log(10) == 0, the result does not change and the analysis - * above applies. If v < 0.900 or v > 1.15, the relative error is - * less than 10**(-ctx.prec-1). - * Case t != 0: - * z := approx(log(v)) - * y := approx(log(10)) - * p := maxprec = ctx->prec + 2 - * Absolute errors: - * 1) abs(z - log(v)) < 10**-p - * 2) abs(y - log(10)) < 10**-p - * The multiplication is exact, so: - * 3) abs(t*y - t*log(10)) < t*10**-p - * The sum is exact, so: - * 4) abs((z + t*y) - (log(v) + t*log(10))) < (abs(t) + 1) * 10**-p - * Bounds for log(v) and log(10): - * 5) -7/10 < log(v) < 17/10 - * 6) 23/10 < log(10) < 24/10 - * Using 4), 5), 6) and t != 0, the relative error is: - * - * 7) relerr < ((abs(t) + 1)*10**-p) / abs(log(v) + t*log(10)) - * < 0.5 * 10**(-p + 1) = 0.5 * 10**(-ctx->prec-1) - */ - mpd_qln10(&v, maxprec+1, status); - mpd_qmul_ssize(&tmp, &v, t, &maxcontext, status); - mpd_qadd(result, &tmp, z, &maxcontext, status); - - -finish: - *status |= (MPD_Inexact|MPD_Rounded); - mpd_del(&v); - mpd_del(&vtmp); - mpd_del(&tmp); -} - -/* ln(a) */ -void -mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t workctx; - mpd_ssize_t adjexp, t; - - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - if (mpd_isnegative(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - mpd_setspecial(result, MPD_POS, MPD_INF); - return; - } - if (mpd_iszerocoeff(a)) { - mpd_setspecial(result, MPD_NEG, MPD_INF); - return; - } - if (mpd_isnegative(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (_mpd_cmp(a, &one) == 0) { - _settriple(result, MPD_POS, 0, 0); - return; - } - /* - * Check if the result will overflow (0 < x, x != 1): - * 1) log10(x) < 0 iff adjexp(x) < 0 - * 2) 0 < x /\ x <= y ==> adjexp(x) <= adjexp(y) - * 3) 0 < x /\ x != 1 ==> 2 * abs(log10(x)) < abs(log(x)) - * 4) adjexp(x) <= log10(x) < adjexp(x) + 1 - * - * Case adjexp(x) >= 0: - * 5) 2 * adjexp(x) < abs(log(x)) - * Case adjexp(x) > 0: - * 6) adjexp(2 * adjexp(x)) <= adjexp(abs(log(x))) - * Case adjexp(x) == 0: - * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) - * - * Case adjexp(x) < 0: - * 7) 2 * (-adjexp(x) - 1) < abs(log(x)) - * Case adjexp(x) < -1: - * 8) adjexp(2 * (-adjexp(x) - 1)) <= adjexp(abs(log(x))) - * Case adjexp(x) == -1: - * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) - */ - adjexp = mpd_adjexp(a); - t = (adjexp < 0) ? -adjexp-1 : adjexp; - t *= 2; - if (mpd_exp_digits(t)-1 > ctx->emax) { - *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded; - mpd_setspecial(result, (adjexp<0), MPD_INF); - return; - } - - workctx = *ctx; - workctx.round = MPD_ROUND_HALF_EVEN; - - if (ctx->allcr) { - MPD_NEW_STATIC(t1, 0,0,0,0); - MPD_NEW_STATIC(t2, 0,0,0,0); - MPD_NEW_STATIC(ulp, 0,0,0,0); - MPD_NEW_STATIC(aa, 0,0,0,0); - mpd_ssize_t prec; - - if (result == a) { - if (!mpd_qcopy(&aa, a, status)) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - a = &aa; - } - - workctx.clamp = 0; - prec = ctx->prec + 3; - while (1) { - workctx.prec = prec; - _mpd_qln(result, a, &workctx, status); - _ssettriple(&ulp, MPD_POS, 1, - result->exp + result->digits-workctx.prec); - - workctx.prec = ctx->prec; - mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status); - mpd_qsub(&t2, result, &ulp, &workctx, &workctx.status); - if (mpd_isspecial(result) || mpd_iszerocoeff(result) || - mpd_qcmp(&t1, &t2, status) == 0) { - workctx.clamp = ctx->clamp; - mpd_check_underflow(result, &workctx, status); - mpd_qfinalize(result, &workctx, status); - break; - } - prec += MPD_RDIGITS; - } - mpd_del(&t1); - mpd_del(&t2); - mpd_del(&ulp); - mpd_del(&aa); - } - else { - _mpd_qln(result, a, &workctx, status); - mpd_check_underflow(result, &workctx, status); - mpd_qfinalize(result, &workctx, status); - } -} - -/* - * Internal log10() function that does not check for specials, zero or one. - * Case SKIP_FINALIZE: - * Relative error: abs(result - log10(a)) < 0.1 * 10**-prec * abs(log10(a)) - * Case DO_FINALIZE: - * Ulp error: abs(result - log10(a)) < ulp(log10(a)) - */ -enum {SKIP_FINALIZE, DO_FINALIZE}; -static void -_mpd_qlog10(int action, mpd_t *result, const mpd_t *a, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t workctx; - MPD_NEW_STATIC(ln10,0,0,0,0); - - mpd_maxcontext(&workctx); - workctx.prec = ctx->prec + 3; - /* relative error: 0.1 * 10**(-p-3). The specific underflow shortcut - * in _mpd_qln() does not change the final result. */ - _mpd_qln(result, a, &workctx, status); - /* relative error: 5 * 10**(-p-3) */ - mpd_qln10(&ln10, workctx.prec, status); - - if (action == DO_FINALIZE) { - workctx = *ctx; - workctx.round = MPD_ROUND_HALF_EVEN; - } - /* SKIP_FINALIZE: relative error: 5 * 10**(-p-3) */ - _mpd_qdiv(NO_IDEAL_EXP, result, result, &ln10, &workctx, status); - - mpd_del(&ln10); -} - -/* log10(a) */ -void -mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t workctx; - mpd_ssize_t adjexp, t; - - workctx = *ctx; - workctx.round = MPD_ROUND_HALF_EVEN; - - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - if (mpd_isnegative(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - mpd_setspecial(result, MPD_POS, MPD_INF); - return; - } - if (mpd_iszerocoeff(a)) { - mpd_setspecial(result, MPD_NEG, MPD_INF); - return; - } - if (mpd_isnegative(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (mpd_coeff_ispow10(a)) { - uint8_t sign = 0; - adjexp = mpd_adjexp(a); - if (adjexp < 0) { - sign = 1; - adjexp = -adjexp; - } - _settriple(result, sign, adjexp, 0); - mpd_qfinalize(result, &workctx, status); - return; - } - /* - * Check if the result will overflow (0 < x, x != 1): - * 1) log10(x) < 0 iff adjexp(x) < 0 - * 2) 0 < x /\ x <= y ==> adjexp(x) <= adjexp(y) - * 3) adjexp(x) <= log10(x) < adjexp(x) + 1 - * - * Case adjexp(x) >= 0: - * 4) adjexp(x) <= abs(log10(x)) - * Case adjexp(x) > 0: - * 5) adjexp(adjexp(x)) <= adjexp(abs(log10(x))) - * Case adjexp(x) == 0: - * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) - * - * Case adjexp(x) < 0: - * 6) -adjexp(x) - 1 < abs(log10(x)) - * Case adjexp(x) < -1: - * 7) adjexp(-adjexp(x) - 1) <= adjexp(abs(log(x))) - * Case adjexp(x) == -1: - * mpd_exp_digits(t)-1 == 0 <= emax (the shortcut is not triggered) - */ - adjexp = mpd_adjexp(a); - t = (adjexp < 0) ? -adjexp-1 : adjexp; - if (mpd_exp_digits(t)-1 > ctx->emax) { - *status |= MPD_Overflow|MPD_Inexact|MPD_Rounded; - mpd_setspecial(result, (adjexp<0), MPD_INF); - return; - } - - if (ctx->allcr) { - MPD_NEW_STATIC(t1, 0,0,0,0); - MPD_NEW_STATIC(t2, 0,0,0,0); - MPD_NEW_STATIC(ulp, 0,0,0,0); - MPD_NEW_STATIC(aa, 0,0,0,0); - mpd_ssize_t prec; - - if (result == a) { - if (!mpd_qcopy(&aa, a, status)) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - a = &aa; - } - - workctx.clamp = 0; - prec = ctx->prec + 3; - while (1) { - workctx.prec = prec; - _mpd_qlog10(SKIP_FINALIZE, result, a, &workctx, status); - _ssettriple(&ulp, MPD_POS, 1, - result->exp + result->digits-workctx.prec); - - workctx.prec = ctx->prec; - mpd_qadd(&t1, result, &ulp, &workctx, &workctx.status); - mpd_qsub(&t2, result, &ulp, &workctx, &workctx.status); - if (mpd_isspecial(result) || mpd_iszerocoeff(result) || - mpd_qcmp(&t1, &t2, status) == 0) { - workctx.clamp = ctx->clamp; - mpd_check_underflow(result, &workctx, status); - mpd_qfinalize(result, &workctx, status); - break; - } - prec += MPD_RDIGITS; - } - mpd_del(&t1); - mpd_del(&t2); - mpd_del(&ulp); - mpd_del(&aa); - } - else { - _mpd_qlog10(DO_FINALIZE, result, a, &workctx, status); - mpd_check_underflow(result, &workctx, status); - } -} - -/* - * Maximum of the two operands. Attention: If one operand is a quiet NaN and the - * other is numeric, the numeric operand is returned. This may not be what one - * expects. - */ -void -mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - int c; - - if (mpd_isqnan(a) && !mpd_isnan(b)) { - mpd_qcopy(result, b, status); - } - else if (mpd_isqnan(b) && !mpd_isnan(a)) { - mpd_qcopy(result, a, status); - } - else if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - else { - c = _mpd_cmp(a, b); - if (c == 0) { - c = _mpd_cmp_numequal(a, b); - } - - if (c < 0) { - mpd_qcopy(result, b, status); - } - else { - mpd_qcopy(result, a, status); - } - } - - mpd_qfinalize(result, ctx, status); -} - -/* - * Maximum magnitude: Same as mpd_max(), but compares the operands with their - * sign ignored. - */ -void -mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - int c; - - if (mpd_isqnan(a) && !mpd_isnan(b)) { - mpd_qcopy(result, b, status); - } - else if (mpd_isqnan(b) && !mpd_isnan(a)) { - mpd_qcopy(result, a, status); - } - else if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - else { - c = _mpd_cmp_abs(a, b); - if (c == 0) { - c = _mpd_cmp_numequal(a, b); - } - - if (c < 0) { - mpd_qcopy(result, b, status); - } - else { - mpd_qcopy(result, a, status); - } - } - - mpd_qfinalize(result, ctx, status); -} - -/* - * Minimum of the two operands. Attention: If one operand is a quiet NaN and the - * other is numeric, the numeric operand is returned. This may not be what one - * expects. - */ -void -mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - int c; - - if (mpd_isqnan(a) && !mpd_isnan(b)) { - mpd_qcopy(result, b, status); - } - else if (mpd_isqnan(b) && !mpd_isnan(a)) { - mpd_qcopy(result, a, status); - } - else if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - else { - c = _mpd_cmp(a, b); - if (c == 0) { - c = _mpd_cmp_numequal(a, b); - } - - if (c < 0) { - mpd_qcopy(result, a, status); - } - else { - mpd_qcopy(result, b, status); - } - } - - mpd_qfinalize(result, ctx, status); -} - -/* - * Minimum magnitude: Same as mpd_min(), but compares the operands with their - * sign ignored. - */ -void -mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - int c; - - if (mpd_isqnan(a) && !mpd_isnan(b)) { - mpd_qcopy(result, b, status); - } - else if (mpd_isqnan(b) && !mpd_isnan(a)) { - mpd_qcopy(result, a, status); - } - else if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - else { - c = _mpd_cmp_abs(a, b); - if (c == 0) { - c = _mpd_cmp_numequal(a, b); - } - - if (c < 0) { - mpd_qcopy(result, a, status); - } - else { - mpd_qcopy(result, b, status); - } - } - - mpd_qfinalize(result, ctx, status); -} - -/* Minimum space needed for the result array in _karatsuba_rec(). */ -static inline mpd_size_t -_kmul_resultsize(mpd_size_t la, mpd_size_t lb) -{ - mpd_size_t n, m; - - n = add_size_t(la, lb); - n = add_size_t(n, 1); - - m = (la+1)/2 + 1; - m = mul_size_t(m, 3); - - return (m > n) ? m : n; -} - -/* Work space needed in _karatsuba_rec(). lim >= 4 */ -static inline mpd_size_t -_kmul_worksize(mpd_size_t n, mpd_size_t lim) -{ - mpd_size_t m; - - if (n <= lim) { - return 0; - } - - m = (n+1)/2 + 1; - - return add_size_t(mul_size_t(m, 2), _kmul_worksize(m, lim)); -} - - -#define MPD_KARATSUBA_BASECASE 16 /* must be >= 4 */ - -/* - * Add the product of a and b to c. - * c must be _kmul_resultsize(la, lb) in size. - * w is used as a work array and must be _kmul_worksize(a, lim) in size. - * Roman E. Maeder, Storage Allocation for the Karatsuba Integer Multiplication - * Algorithm. In "Design and implementation of symbolic computation systems", - * Springer, 1993, ISBN 354057235X, 9783540572350. - */ -static void -_karatsuba_rec(mpd_uint_t *c, const mpd_uint_t *a, const mpd_uint_t *b, - mpd_uint_t *w, mpd_size_t la, mpd_size_t lb) -{ - mpd_size_t m, lt; - - assert(la >= lb && lb > 0); - assert(la <= MPD_KARATSUBA_BASECASE || w != NULL); - - if (la <= MPD_KARATSUBA_BASECASE) { - _mpd_basemul(c, a, b, la, lb); - return; - } - - m = (la+1)/2; /* ceil(la/2) */ - - /* lb <= m < la */ - if (lb <= m) { - - /* lb can now be larger than la-m */ - if (lb > la-m) { - lt = lb + lb + 1; /* space needed for result array */ - mpd_uint_zero(w, lt); /* clear result array */ - _karatsuba_rec(w, b, a+m, w+lt, lb, la-m); /* b*ah */ - } - else { - lt = (la-m) + (la-m) + 1; /* space needed for result array */ - mpd_uint_zero(w, lt); /* clear result array */ - _karatsuba_rec(w, a+m, b, w+lt, la-m, lb); /* ah*b */ - } - _mpd_baseaddto(c+m, w, (la-m)+lb); /* add ah*b*B**m */ - - lt = m + m + 1; /* space needed for the result array */ - mpd_uint_zero(w, lt); /* clear result array */ - _karatsuba_rec(w, a, b, w+lt, m, lb); /* al*b */ - _mpd_baseaddto(c, w, m+lb); /* add al*b */ - - return; - } - - /* la >= lb > m */ - memcpy(w, a, m * sizeof *w); - w[m] = 0; - _mpd_baseaddto(w, a+m, la-m); - - memcpy(w+(m+1), b, m * sizeof *w); - w[m+1+m] = 0; - _mpd_baseaddto(w+(m+1), b+m, lb-m); - - _karatsuba_rec(c+m, w, w+(m+1), w+2*(m+1), m+1, m+1); - - lt = (la-m) + (la-m) + 1; - mpd_uint_zero(w, lt); - - _karatsuba_rec(w, a+m, b+m, w+lt, la-m, lb-m); - - _mpd_baseaddto(c+2*m, w, (la-m) + (lb-m)); - _mpd_basesubfrom(c+m, w, (la-m) + (lb-m)); - - lt = m + m + 1; - mpd_uint_zero(w, lt); - - _karatsuba_rec(w, a, b, w+lt, m, m); - _mpd_baseaddto(c, w, m+m); - _mpd_basesubfrom(c+m, w, m+m); - - return; -} - -/* - * Multiply u and v, using Karatsuba multiplication. Returns a pointer - * to the result or NULL in case of failure (malloc error). - * Conditions: ulen >= vlen, ulen >= 4 - */ -static mpd_uint_t * -_mpd_kmul(const mpd_uint_t *u, const mpd_uint_t *v, - mpd_size_t ulen, mpd_size_t vlen, - mpd_size_t *rsize) -{ - mpd_uint_t *result = NULL, *w = NULL; - mpd_size_t m; - - assert(ulen >= 4); - assert(ulen >= vlen); - - *rsize = _kmul_resultsize(ulen, vlen); - if ((result = mpd_calloc(*rsize, sizeof *result)) == NULL) { - return NULL; - } - - m = _kmul_worksize(ulen, MPD_KARATSUBA_BASECASE); - if (m && ((w = mpd_calloc(m, sizeof *w)) == NULL)) { - mpd_free(result); - return NULL; - } - - _karatsuba_rec(result, u, v, w, ulen, vlen); - - - if (w) mpd_free(w); - return result; -} - - -/* - * Determine the minimum length for the number theoretic transform. Valid - * transform lengths are 2**n or 3*2**n, where 2**n <= MPD_MAXTRANSFORM_2N. - * The function finds the shortest length m such that rsize <= m. - */ -static inline mpd_size_t -_mpd_get_transform_len(mpd_size_t rsize) -{ - mpd_size_t log2rsize; - mpd_size_t x, step; - - assert(rsize >= 4); - log2rsize = mpd_bsr(rsize); - - if (rsize <= 1024) { - /* 2**n is faster in this range. */ - x = ((mpd_size_t)1)<<log2rsize; - return (rsize == x) ? x : x<<1; - } - else if (rsize <= MPD_MAXTRANSFORM_2N) { - x = ((mpd_size_t)1)<<log2rsize; - if (rsize == x) return x; - step = x>>1; - x += step; - return (rsize <= x) ? x : x + step; - } - else if (rsize <= MPD_MAXTRANSFORM_2N+MPD_MAXTRANSFORM_2N/2) { - return MPD_MAXTRANSFORM_2N+MPD_MAXTRANSFORM_2N/2; - } - else if (rsize <= 3*MPD_MAXTRANSFORM_2N) { - return 3*MPD_MAXTRANSFORM_2N; - } - else { - return MPD_SIZE_MAX; - } -} - -#ifdef PPRO -#ifndef _MSC_VER -static inline unsigned short -_mpd_get_control87(void) -{ - unsigned short cw; - - __asm__ __volatile__ ("fnstcw %0" : "=m" (cw)); - return cw; -} - -static inline void -_mpd_set_control87(unsigned short cw) -{ - __asm__ __volatile__ ("fldcw %0" : : "m" (cw)); -} -#endif - -static unsigned int -mpd_set_fenv(void) -{ - unsigned int cw; -#ifdef _MSC_VER - unsigned int flags = - _EM_INVALID|_EM_DENORMAL|_EM_ZERODIVIDE|_EM_OVERFLOW| - _EM_UNDERFLOW|_EM_INEXACT|_RC_CHOP|_PC_64; - unsigned int mask = _MCW_EM|_MCW_RC|_MCW_PC; - unsigned int dummy; - - __control87_2(0, 0, &cw, NULL); - __control87_2(flags, mask, &dummy, NULL); -#else - cw = _mpd_get_control87(); - _mpd_set_control87(cw|0xF3F); -#endif - return cw; -} - -static void -mpd_restore_fenv(unsigned int cw) -{ -#ifdef _MSC_VER - unsigned int mask = _MCW_EM|_MCW_RC|_MCW_PC; - unsigned int dummy; - - __control87_2(cw, mask, &dummy, NULL); -#else - _mpd_set_control87((unsigned short)cw); -#endif -} -#endif /* PPRO */ - -/* - * Multiply u and v, using the fast number theoretic transform. Returns - * a pointer to the result or NULL in case of failure (malloc error). - */ -static mpd_uint_t * -_mpd_fntmul(const mpd_uint_t *u, const mpd_uint_t *v, - mpd_size_t ulen, mpd_size_t vlen, - mpd_size_t *rsize) -{ - mpd_uint_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *vtmp = NULL; - mpd_size_t n; - -#ifdef PPRO - unsigned int cw; - cw = mpd_set_fenv(); -#endif - - *rsize = add_size_t(ulen, vlen); - if ((n = _mpd_get_transform_len(*rsize)) == MPD_SIZE_MAX) { - goto malloc_error; - } - - if ((c1 = mpd_calloc(n, sizeof *c1)) == NULL) { - goto malloc_error; - } - if ((c2 = mpd_calloc(n, sizeof *c2)) == NULL) { - goto malloc_error; - } - if ((c3 = mpd_calloc(n, sizeof *c3)) == NULL) { - goto malloc_error; - } - - memcpy(c1, u, ulen * (sizeof *c1)); - memcpy(c2, u, ulen * (sizeof *c2)); - memcpy(c3, u, ulen * (sizeof *c3)); - - if (u == v) { - if (!fnt_autoconvolute(c1, n, P1) || - !fnt_autoconvolute(c2, n, P2) || - !fnt_autoconvolute(c3, n, P3)) { - goto malloc_error; - } - } - else { - if ((vtmp = mpd_calloc(n, sizeof *vtmp)) == NULL) { - goto malloc_error; - } - - memcpy(vtmp, v, vlen * (sizeof *vtmp)); - if (!fnt_convolute(c1, vtmp, n, P1)) { - mpd_free(vtmp); - goto malloc_error; - } - - memcpy(vtmp, v, vlen * (sizeof *vtmp)); - mpd_uint_zero(vtmp+vlen, n-vlen); - if (!fnt_convolute(c2, vtmp, n, P2)) { - mpd_free(vtmp); - goto malloc_error; - } - - memcpy(vtmp, v, vlen * (sizeof *vtmp)); - mpd_uint_zero(vtmp+vlen, n-vlen); - if (!fnt_convolute(c3, vtmp, n, P3)) { - mpd_free(vtmp); - goto malloc_error; - } - - mpd_free(vtmp); - } - - crt3(c1, c2, c3, *rsize); - -out: -#ifdef PPRO - mpd_restore_fenv(cw); -#endif - if (c2) mpd_free(c2); - if (c3) mpd_free(c3); - return c1; - -malloc_error: - if (c1) mpd_free(c1); - c1 = NULL; - goto out; -} - - -/* - * Karatsuba multiplication with FNT/basemul as the base case. - */ -static int -_karatsuba_rec_fnt(mpd_uint_t *c, const mpd_uint_t *a, const mpd_uint_t *b, - mpd_uint_t *w, mpd_size_t la, mpd_size_t lb) -{ - mpd_size_t m, lt; - - assert(la >= lb && lb > 0); - assert(la <= 3*(MPD_MAXTRANSFORM_2N/2) || w != NULL); - - if (la <= 3*(MPD_MAXTRANSFORM_2N/2)) { - - if (lb <= 192) { - _mpd_basemul(c, b, a, lb, la); - } - else { - mpd_uint_t *result; - mpd_size_t dummy; - - if ((result = _mpd_fntmul(a, b, la, lb, &dummy)) == NULL) { - return 0; - } - memcpy(c, result, (la+lb) * (sizeof *result)); - mpd_free(result); - } - return 1; - } - - m = (la+1)/2; /* ceil(la/2) */ - - /* lb <= m < la */ - if (lb <= m) { - - /* lb can now be larger than la-m */ - if (lb > la-m) { - lt = lb + lb + 1; /* space needed for result array */ - mpd_uint_zero(w, lt); /* clear result array */ - if (!_karatsuba_rec_fnt(w, b, a+m, w+lt, lb, la-m)) { /* b*ah */ - return 0; /* GCOV_UNLIKELY */ - } - } - else { - lt = (la-m) + (la-m) + 1; /* space needed for result array */ - mpd_uint_zero(w, lt); /* clear result array */ - if (!_karatsuba_rec_fnt(w, a+m, b, w+lt, la-m, lb)) { /* ah*b */ - return 0; /* GCOV_UNLIKELY */ - } - } - _mpd_baseaddto(c+m, w, (la-m)+lb); /* add ah*b*B**m */ - - lt = m + m + 1; /* space needed for the result array */ - mpd_uint_zero(w, lt); /* clear result array */ - if (!_karatsuba_rec_fnt(w, a, b, w+lt, m, lb)) { /* al*b */ - return 0; /* GCOV_UNLIKELY */ - } - _mpd_baseaddto(c, w, m+lb); /* add al*b */ - - return 1; - } - - /* la >= lb > m */ - memcpy(w, a, m * sizeof *w); - w[m] = 0; - _mpd_baseaddto(w, a+m, la-m); - - memcpy(w+(m+1), b, m * sizeof *w); - w[m+1+m] = 0; - _mpd_baseaddto(w+(m+1), b+m, lb-m); - - if (!_karatsuba_rec_fnt(c+m, w, w+(m+1), w+2*(m+1), m+1, m+1)) { - return 0; /* GCOV_UNLIKELY */ - } - - lt = (la-m) + (la-m) + 1; - mpd_uint_zero(w, lt); - - if (!_karatsuba_rec_fnt(w, a+m, b+m, w+lt, la-m, lb-m)) { - return 0; /* GCOV_UNLIKELY */ - } - - _mpd_baseaddto(c+2*m, w, (la-m) + (lb-m)); - _mpd_basesubfrom(c+m, w, (la-m) + (lb-m)); - - lt = m + m + 1; - mpd_uint_zero(w, lt); - - if (!_karatsuba_rec_fnt(w, a, b, w+lt, m, m)) { - return 0; /* GCOV_UNLIKELY */ - } - _mpd_baseaddto(c, w, m+m); - _mpd_basesubfrom(c+m, w, m+m); - - return 1; -} - -/* - * Multiply u and v, using Karatsuba multiplication with the FNT as the - * base case. Returns a pointer to the result or NULL in case of failure - * (malloc error). Conditions: ulen >= vlen, ulen >= 4. - */ -static mpd_uint_t * -_mpd_kmul_fnt(const mpd_uint_t *u, const mpd_uint_t *v, - mpd_size_t ulen, mpd_size_t vlen, - mpd_size_t *rsize) -{ - mpd_uint_t *result = NULL, *w = NULL; - mpd_size_t m; - - assert(ulen >= 4); - assert(ulen >= vlen); - - *rsize = _kmul_resultsize(ulen, vlen); - if ((result = mpd_calloc(*rsize, sizeof *result)) == NULL) { - return NULL; - } - - m = _kmul_worksize(ulen, 3*(MPD_MAXTRANSFORM_2N/2)); - if (m && ((w = mpd_calloc(m, sizeof *w)) == NULL)) { - mpd_free(result); /* GCOV_UNLIKELY */ - return NULL; /* GCOV_UNLIKELY */ - } - - if (!_karatsuba_rec_fnt(result, u, v, w, ulen, vlen)) { - mpd_free(result); - result = NULL; - } - - - if (w) mpd_free(w); - return result; -} - - -/* Deal with the special cases of multiplying infinities. */ -static void -_mpd_qmul_inf(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status) -{ - if (mpd_isinfinite(a)) { - if (mpd_iszero(b)) { - mpd_seterror(result, MPD_Invalid_operation, status); - } - else { - mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF); - } - return; - } - assert(mpd_isinfinite(b)); - if (mpd_iszero(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - } - else { - mpd_setspecial(result, mpd_sign(a)^mpd_sign(b), MPD_INF); - } -} - -/* - * Internal function: Multiply a and b. _mpd_qmul deals with specials but - * does NOT finalize the result. This is for use in mpd_fma(). - */ -static inline void -_mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - const mpd_t *big = a, *small = b; - mpd_uint_t *rdata = NULL; - mpd_uint_t rbuf[MPD_MINALLOC_MAX]; - mpd_size_t rsize, i; - - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - _mpd_qmul_inf(result, a, b, status); - return; - } - - if (small->len > big->len) { - _mpd_ptrswap(&big, &small); - } - - rsize = big->len + small->len; - - if (big->len == 1) { - _mpd_singlemul(result->data, big->data[0], small->data[0]); - goto finish; - } - if (rsize <= (mpd_size_t)MPD_MINALLOC_MAX) { - if (big->len == 2) { - _mpd_mul_2_le2(rbuf, big->data, small->data, small->len); - } - else { - mpd_uint_zero(rbuf, rsize); - if (small->len == 1) { - _mpd_shortmul(rbuf, big->data, big->len, small->data[0]); - } - else { - _mpd_basemul(rbuf, small->data, big->data, small->len, big->len); - } - } - if (!mpd_qresize(result, rsize, status)) { - return; - } - for(i = 0; i < rsize; i++) { - result->data[i] = rbuf[i]; - } - goto finish; - } - - - if (small->len <= 256) { - rdata = mpd_calloc(rsize, sizeof *rdata); - if (rdata != NULL) { - if (small->len == 1) { - _mpd_shortmul(rdata, big->data, big->len, small->data[0]); - } - else { - _mpd_basemul(rdata, small->data, big->data, small->len, big->len); - } - } - } - else if (rsize <= 1024) { - rdata = _mpd_kmul(big->data, small->data, big->len, small->len, &rsize); - } - else if (rsize <= 3*MPD_MAXTRANSFORM_2N) { - rdata = _mpd_fntmul(big->data, small->data, big->len, small->len, &rsize); - } - else { - rdata = _mpd_kmul_fnt(big->data, small->data, big->len, small->len, &rsize); - } - - if (rdata == NULL) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - - if (mpd_isdynamic_data(result)) { - mpd_free(result->data); - } - result->data = rdata; - result->alloc = rsize; - mpd_set_dynamic_data(result); - - -finish: - mpd_set_flags(result, mpd_sign(a)^mpd_sign(b)); - result->exp = big->exp + small->exp; - result->len = _mpd_real_size(result->data, rsize); - /* resize to smaller cannot fail */ - mpd_qresize(result, result->len, status); - mpd_setdigits(result); -} - -/* Multiply a and b. */ -void -mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - _mpd_qmul(result, a, b, ctx, status); - mpd_qfinalize(result, ctx, status); -} - -/* Multiply a and b. Set NaN/Invalid_operation if the result is inexact. */ -static void -_mpd_qmul_exact(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - uint32_t workstatus = 0; - - mpd_qmul(result, a, b, ctx, &workstatus); - *status |= workstatus; - if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) { - mpd_seterror(result, MPD_Invalid_operation, status); - } -} - -/* Multiply decimal and mpd_ssize_t. */ -void -mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qsset_ssize(&bb, b, &maxcontext, status); - mpd_qmul(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Multiply decimal and mpd_uint_t. */ -void -mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qsset_uint(&bb, b, &maxcontext, status); - mpd_qmul(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -void -mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qmul_ssize(result, a, b, ctx, status); -} - -void -mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qmul_uint(result, a, b, ctx, status); -} - -#ifdef CONFIG_64 -void -mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qmul_ssize(result, a, b, ctx, status); -} - -void -mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_qmul_uint(result, a, b, ctx, status); -} -#elif !defined(LEGACY_COMPILER) -/* Multiply decimal and int64_t. */ -void -mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qset_i64(&bb, b, &maxcontext, status); - mpd_qmul(result, a, &bb, ctx, status); - mpd_del(&bb); -} - -/* Multiply decimal and uint64_t. */ -void -mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(bb,0,0,0,0); - - mpd_maxcontext(&maxcontext); - mpd_qset_u64(&bb, b, &maxcontext, status); - mpd_qmul(result, a, &bb, ctx, status); - mpd_del(&bb); -} -#endif - -/* Like the minus operator. */ -void -mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - } - - if (mpd_iszero(a) && ctx->round != MPD_ROUND_FLOOR) { - mpd_qcopy_abs(result, a, status); - } - else { - mpd_qcopy_negate(result, a, status); - } - - mpd_qfinalize(result, ctx, status); -} - -/* Like the plus operator. */ -void -mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - } - - if (mpd_iszero(a) && ctx->round != MPD_ROUND_FLOOR) { - mpd_qcopy_abs(result, a, status); - } - else { - mpd_qcopy(result, a, status); - } - - mpd_qfinalize(result, ctx, status); -} - -/* The largest representable number that is smaller than the operand. */ -void -mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t workctx; - MPD_NEW_CONST(tiny,MPD_POS,mpd_etiny(ctx)-1,1,1,1,1); - - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - - assert(mpd_isinfinite(a)); - if (mpd_isnegative(a)) { - mpd_qcopy(result, a, status); - return; - } - else { - mpd_clear_flags(result); - mpd_qmaxcoeff(result, ctx, status); - if (mpd_isnan(result)) { - return; - } - result->exp = mpd_etop(ctx); - return; - } - } - - mpd_workcontext(&workctx, ctx); - workctx.round = MPD_ROUND_FLOOR; - - if (!mpd_qcopy(result, a, status)) { - return; - } - - mpd_qfinalize(result, &workctx, &workctx.status); - if (workctx.status&(MPD_Inexact|MPD_Errors)) { - *status |= (workctx.status&MPD_Errors); - return; - } - - workctx.status = 0; - mpd_qsub(result, a, &tiny, &workctx, &workctx.status); - *status |= (workctx.status&MPD_Errors); -} - -/* The smallest representable number that is larger than the operand. */ -void -mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t workctx; - MPD_NEW_CONST(tiny,MPD_POS,mpd_etiny(ctx)-1,1,1,1,1); - - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - - assert(mpd_isinfinite(a)); - if (mpd_ispositive(a)) { - mpd_qcopy(result, a, status); - } - else { - mpd_clear_flags(result); - mpd_qmaxcoeff(result, ctx, status); - if (mpd_isnan(result)) { - return; - } - mpd_set_flags(result, MPD_NEG); - result->exp = mpd_etop(ctx); - } - return; - } - - mpd_workcontext(&workctx, ctx); - workctx.round = MPD_ROUND_CEILING; - - if (!mpd_qcopy(result, a, status)) { - return; - } - - mpd_qfinalize(result, &workctx, &workctx.status); - if (workctx.status & (MPD_Inexact|MPD_Errors)) { - *status |= (workctx.status&MPD_Errors); - return; - } - - workctx.status = 0; - mpd_qadd(result, a, &tiny, &workctx, &workctx.status); - *status |= (workctx.status&MPD_Errors); -} - -/* - * The number closest to the first operand that is in the direction towards - * the second operand. - */ -void -mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - int c; - - if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - - c = _mpd_cmp(a, b); - if (c == 0) { - mpd_qcopy_sign(result, a, b, status); - return; - } - - if (c < 0) { - mpd_qnext_plus(result, a, ctx, status); - } - else { - mpd_qnext_minus(result, a, ctx, status); - } - - if (mpd_isinfinite(result)) { - *status |= (MPD_Overflow|MPD_Rounded|MPD_Inexact); - } - else if (mpd_adjexp(result) < ctx->emin) { - *status |= (MPD_Underflow|MPD_Subnormal|MPD_Rounded|MPD_Inexact); - if (mpd_iszero(result)) { - *status |= MPD_Clamped; - } - } -} - -/* - * Internal function: Integer power with mpd_uint_t exponent. The function - * can fail with MPD_Malloc_error. - * - * The error is equal to the error incurred in k-1 multiplications. Assuming - * the upper bound for the relative error in each operation: - * - * abs(err) = 5 * 10**-prec - * result = x**k * (1 + err)**(k-1) - */ -static inline void -_mpd_qpow_uint(mpd_t *result, const mpd_t *base, mpd_uint_t exp, - uint8_t resultsign, const mpd_context_t *ctx, uint32_t *status) -{ - uint32_t workstatus = 0; - mpd_uint_t n; - - if (exp == 0) { - _settriple(result, resultsign, 1, 0); /* GCOV_NOT_REACHED */ - return; /* GCOV_NOT_REACHED */ - } - - if (!mpd_qcopy(result, base, status)) { - return; - } - - n = mpd_bits[mpd_bsr(exp)]; - while (n >>= 1) { - mpd_qmul(result, result, result, ctx, &workstatus); - if (exp & n) { - mpd_qmul(result, result, base, ctx, &workstatus); - } - if (mpd_isspecial(result) || - (mpd_iszerocoeff(result) && (workstatus & MPD_Clamped))) { - break; - } - } - - *status |= workstatus; - mpd_set_sign(result, resultsign); -} - -/* - * Internal function: Integer power with mpd_t exponent, tbase and texp - * are modified!! Function can fail with MPD_Malloc_error. - * - * The error is equal to the error incurred in k multiplications. Assuming - * the upper bound for the relative error in each operation: - * - * abs(err) = 5 * 10**-prec - * result = x**k * (1 + err)**k - */ -static inline void -_mpd_qpow_mpd(mpd_t *result, mpd_t *tbase, mpd_t *texp, uint8_t resultsign, - const mpd_context_t *ctx, uint32_t *status) -{ - uint32_t workstatus = 0; - mpd_context_t maxctx; - MPD_NEW_CONST(two,0,0,1,1,1,2); - - - mpd_maxcontext(&maxctx); - - /* resize to smaller cannot fail */ - mpd_qcopy(result, &one, status); - - while (!mpd_iszero(texp)) { - if (mpd_isodd(texp)) { - mpd_qmul(result, result, tbase, ctx, &workstatus); - *status |= workstatus; - if (mpd_isspecial(result) || - (mpd_iszerocoeff(result) && (workstatus & MPD_Clamped))) { - break; - } - } - mpd_qmul(tbase, tbase, tbase, ctx, &workstatus); - mpd_qdivint(texp, texp, &two, &maxctx, &workstatus); - if (mpd_isnan(tbase) || mpd_isnan(texp)) { - mpd_seterror(result, workstatus&MPD_Errors, status); - return; - } - } - mpd_set_sign(result, resultsign); -} - -/* - * The power function for integer exponents. Relative error _before_ the - * final rounding to prec: - * abs(result - base**exp) < 0.1 * 10**-prec * abs(base**exp) - */ -static void -_mpd_qpow_int(mpd_t *result, const mpd_t *base, const mpd_t *exp, - uint8_t resultsign, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t workctx; - MPD_NEW_STATIC(tbase,0,0,0,0); - MPD_NEW_STATIC(texp,0,0,0,0); - mpd_uint_t n; - - - mpd_workcontext(&workctx, ctx); - workctx.prec += (exp->digits + exp->exp + 2); - workctx.round = MPD_ROUND_HALF_EVEN; - workctx.clamp = 0; - if (mpd_isnegative(exp)) { - uint32_t workstatus = 0; - workctx.prec += 1; - mpd_qdiv(&tbase, &one, base, &workctx, &workstatus); - *status |= workstatus; - if (workstatus&MPD_Errors) { - mpd_setspecial(result, MPD_POS, MPD_NAN); - goto finish; - } - } - else { - if (!mpd_qcopy(&tbase, base, status)) { - mpd_setspecial(result, MPD_POS, MPD_NAN); - goto finish; - } - } - - n = mpd_qabs_uint(exp, &workctx.status); - if (workctx.status&MPD_Invalid_operation) { - if (!mpd_qcopy(&texp, exp, status)) { - mpd_setspecial(result, MPD_POS, MPD_NAN); /* GCOV_UNLIKELY */ - goto finish; /* GCOV_UNLIKELY */ - } - _mpd_qpow_mpd(result, &tbase, &texp, resultsign, &workctx, status); - } - else { - _mpd_qpow_uint(result, &tbase, n, resultsign, &workctx, status); - } - - if (mpd_isinfinite(result)) { - /* for ROUND_DOWN, ROUND_FLOOR, etc. */ - _settriple(result, resultsign, 1, MPD_EXP_INF); - } - -finish: - mpd_del(&tbase); - mpd_del(&texp); - mpd_qfinalize(result, ctx, status); -} - -/* - * If the exponent is infinite and base equals one, the result is one - * with a coefficient of length prec. Otherwise, result is undefined. - * Return the value of the comparison against one. - */ -static int -_qcheck_pow_one_inf(mpd_t *result, const mpd_t *base, uint8_t resultsign, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_ssize_t shift; - int cmp; - - if ((cmp = _mpd_cmp(base, &one)) == 0) { - shift = ctx->prec-1; - mpd_qshiftl(result, &one, shift, status); - result->exp = -shift; - mpd_set_flags(result, resultsign); - *status |= (MPD_Inexact|MPD_Rounded); - } - - return cmp; -} - -/* - * If abs(base) equals one, calculate the correct power of one result. - * Otherwise, result is undefined. Return the value of the comparison - * against 1. - * - * This is an internal function that does not check for specials. - */ -static int -_qcheck_pow_one(mpd_t *result, const mpd_t *base, const mpd_t *exp, - uint8_t resultsign, - const mpd_context_t *ctx, uint32_t *status) -{ - uint32_t workstatus = 0; - mpd_ssize_t shift; - int cmp; - - if ((cmp = _mpd_cmp_abs(base, &one)) == 0) { - if (_mpd_isint(exp)) { - if (mpd_isnegative(exp)) { - _settriple(result, resultsign, 1, 0); - return 0; - } - /* 1.000**3 = 1.000000000 */ - mpd_qmul_ssize(result, exp, -base->exp, ctx, &workstatus); - if (workstatus&MPD_Errors) { - *status |= (workstatus&MPD_Errors); - return 0; - } - /* digits-1 after exponentiation */ - shift = mpd_qget_ssize(result, &workstatus); - /* shift is MPD_SSIZE_MAX if result is too large */ - if (shift > ctx->prec-1) { - shift = ctx->prec-1; - *status |= MPD_Rounded; - } - } - else if (mpd_ispositive(base)) { - shift = ctx->prec-1; - *status |= (MPD_Inexact|MPD_Rounded); - } - else { - return -2; /* GCOV_NOT_REACHED */ - } - if (!mpd_qshiftl(result, &one, shift, status)) { - return 0; - } - result->exp = -shift; - mpd_set_flags(result, resultsign); - } - - return cmp; -} - -/* - * Detect certain over/underflow of x**y. - * ACL2 proof: pow-bounds.lisp. - * - * Symbols: - * - * e: EXP_INF or EXP_CLAMP - * x: base - * y: exponent - * - * omega(e) = log10(abs(e)) - * zeta(x) = log10(abs(log10(x))) - * theta(y) = log10(abs(y)) - * - * Upper and lower bounds: - * - * ub_omega(e) = ceil(log10(abs(e))) - * lb_theta(y) = floor(log10(abs(y))) - * - * | floor(log10(floor(abs(log10(x))))) if x < 1/10 or x >= 10 - * lb_zeta(x) = | floor(log10(abs(x-1)/10)) if 1/10 <= x < 1 - * | floor(log10(abs((x-1)/100))) if 1 < x < 10 - * - * ub_omega(e) and lb_theta(y) are obviously upper and lower bounds - * for omega(e) and theta(y). - * - * lb_zeta is a lower bound for zeta(x): - * - * x < 1/10 or x >= 10: - * - * abs(log10(x)) >= 1, so the outer log10 is well defined. Since log10 - * is strictly increasing, the end result is a lower bound. - * - * 1/10 <= x < 1: - * - * We use: log10(x) <= (x-1)/log(10) - * abs(log10(x)) >= abs(x-1)/log(10) - * abs(log10(x)) >= abs(x-1)/10 - * - * 1 < x < 10: - * - * We use: (x-1)/(x*log(10)) < log10(x) - * abs((x-1)/100) < abs(log10(x)) - * - * XXX: abs((x-1)/10) would work, need ACL2 proof. - * - * - * Let (0 < x < 1 and y < 0) or (x > 1 and y > 0). (H1) - * Let ub_omega(exp_inf) < lb_zeta(x) + lb_theta(y) (H2) - * - * Then: - * log10(abs(exp_inf)) < log10(abs(log10(x))) + log10(abs(y)). (1) - * exp_inf < log10(x) * y (2) - * 10**exp_inf < x**y (3) - * - * Let (0 < x < 1 and y > 0) or (x > 1 and y < 0). (H3) - * Let ub_omega(exp_clamp) < lb_zeta(x) + lb_theta(y) (H4) - * - * Then: - * log10(abs(exp_clamp)) < log10(abs(log10(x))) + log10(abs(y)). (4) - * log10(x) * y < exp_clamp (5) - * x**y < 10**exp_clamp (6) - * - */ -static mpd_ssize_t -_lower_bound_zeta(const mpd_t *x, uint32_t *status) -{ - mpd_context_t maxctx; - MPD_NEW_STATIC(scratch,0,0,0,0); - mpd_ssize_t t, u; - - t = mpd_adjexp(x); - if (t > 0) { - /* x >= 10 -> floor(log10(floor(abs(log10(x))))) */ - return mpd_exp_digits(t) - 1; - } - else if (t < -1) { - /* x < 1/10 -> floor(log10(floor(abs(log10(x))))) */ - return mpd_exp_digits(t+1) - 1; - } - else { - mpd_maxcontext(&maxctx); - mpd_qsub(&scratch, x, &one, &maxctx, status); - if (mpd_isspecial(&scratch)) { - mpd_del(&scratch); - return MPD_SSIZE_MAX; - } - u = mpd_adjexp(&scratch); - mpd_del(&scratch); - - /* t == -1, 1/10 <= x < 1 -> floor(log10(abs(x-1)/10)) - * t == 0, 1 < x < 10 -> floor(log10(abs(x-1)/100)) */ - return (t == 0) ? u-2 : u-1; - } -} - -/* - * Detect cases of certain overflow/underflow in the power function. - * Assumptions: x != 1, y != 0. The proof above is for positive x. - * If x is negative and y is an odd integer, x**y == -(abs(x)**y), - * so the analysis does not change. - */ -static int -_qcheck_pow_bounds(mpd_t *result, const mpd_t *x, const mpd_t *y, - uint8_t resultsign, - const mpd_context_t *ctx, uint32_t *status) -{ - MPD_NEW_SHARED(abs_x, x); - mpd_ssize_t ub_omega, lb_zeta, lb_theta; - uint8_t sign; - - mpd_set_positive(&abs_x); - - lb_theta = mpd_adjexp(y); - lb_zeta = _lower_bound_zeta(&abs_x, status); - if (lb_zeta == MPD_SSIZE_MAX) { - mpd_seterror(result, MPD_Malloc_error, status); - return 1; - } - - sign = (mpd_adjexp(&abs_x) < 0) ^ mpd_sign(y); - if (sign == 0) { - /* (0 < |x| < 1 and y < 0) or (|x| > 1 and y > 0) */ - ub_omega = mpd_exp_digits(ctx->emax); - if (ub_omega < lb_zeta + lb_theta) { - _settriple(result, resultsign, 1, MPD_EXP_INF); - mpd_qfinalize(result, ctx, status); - return 1; - } - } - else { - /* (0 < |x| < 1 and y > 0) or (|x| > 1 and y < 0). */ - ub_omega = mpd_exp_digits(mpd_etiny(ctx)); - if (ub_omega < lb_zeta + lb_theta) { - _settriple(result, resultsign, 1, mpd_etiny(ctx)-1); - mpd_qfinalize(result, ctx, status); - return 1; - } - } - - return 0; -} - -/* - * TODO: Implement algorithm for computing exact powers from decimal.py. - * In order to prevent infinite loops, this has to be called before - * using Ziv's strategy for correct rounding. - */ -/* -static int -_mpd_qpow_exact(mpd_t *result, const mpd_t *base, const mpd_t *exp, - const mpd_context_t *ctx, uint32_t *status) -{ - return 0; -} -*/ - -/* - * The power function for real exponents. - * Relative error: abs(result - e**y) < e**y * 1/5 * 10**(-prec - 1) - */ -static void -_mpd_qpow_real(mpd_t *result, const mpd_t *base, const mpd_t *exp, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t workctx; - MPD_NEW_STATIC(texp,0,0,0,0); - - if (!mpd_qcopy(&texp, exp, status)) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - - mpd_maxcontext(&workctx); - workctx.prec = (base->digits > ctx->prec) ? base->digits : ctx->prec; - workctx.prec += (4 + MPD_EXPDIGITS); - workctx.round = MPD_ROUND_HALF_EVEN; - workctx.allcr = ctx->allcr; - - /* - * extra := MPD_EXPDIGITS = MPD_EXP_MAX_T - * wp := prec + 4 + extra - * abs(err) < 5 * 10**-wp - * y := log(base) * exp - * Calculate: - * 1) e**(y * (1 + err)**2) * (1 + err) - * = e**y * e**(y * (2*err + err**2)) * (1 + err) - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * Relative error of the underlined term: - * 2) abs(e**(y * (2*err + err**2)) - 1) - * Case abs(y) >= 10**extra: - * 3) adjexp(y)+1 > log10(abs(y)) >= extra - * This triggers the Overflow/Underflow shortcut in _mpd_qexp(), - * so no further analysis is necessary. - * Case abs(y) < 10**extra: - * 4) abs(y * (2*err + err**2)) < 1/5 * 10**(-prec - 2) - * Use (see _mpd_qexp): - * 5) abs(x) <= 9/10 * 10**-p ==> abs(e**x - 1) < 10**-p - * With 2), 4) and 5): - * 6) abs(e**(y * (2*err + err**2)) - 1) < 10**(-prec - 2) - * The complete relative error of 1) is: - * 7) abs(result - e**y) < e**y * 1/5 * 10**(-prec - 1) - */ - mpd_qln(result, base, &workctx, &workctx.status); - mpd_qmul(result, result, &texp, &workctx, &workctx.status); - mpd_qexp(result, result, &workctx, status); - - mpd_del(&texp); - *status |= (workctx.status&MPD_Errors); - *status |= (MPD_Inexact|MPD_Rounded); -} - -/* The power function: base**exp */ -void -mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp, - const mpd_context_t *ctx, uint32_t *status) -{ - uint8_t resultsign = 0; - int intexp = 0; - int cmp; - - if (mpd_isspecial(base) || mpd_isspecial(exp)) { - if (mpd_qcheck_nans(result, base, exp, ctx, status)) { - return; - } - } - if (mpd_isinteger(exp)) { - intexp = 1; - resultsign = mpd_isnegative(base) && mpd_isodd(exp); - } - - if (mpd_iszero(base)) { - if (mpd_iszero(exp)) { - mpd_seterror(result, MPD_Invalid_operation, status); - } - else if (mpd_isnegative(exp)) { - mpd_setspecial(result, resultsign, MPD_INF); - } - else { - _settriple(result, resultsign, 0, 0); - } - return; - } - if (mpd_isnegative(base)) { - if (!intexp || mpd_isinfinite(exp)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - } - if (mpd_isinfinite(exp)) { - /* power of one */ - cmp = _qcheck_pow_one_inf(result, base, resultsign, ctx, status); - if (cmp == 0) { - return; - } - else { - cmp *= mpd_arith_sign(exp); - if (cmp < 0) { - _settriple(result, resultsign, 0, 0); - } - else { - mpd_setspecial(result, resultsign, MPD_INF); - } - } - return; - } - if (mpd_isinfinite(base)) { - if (mpd_iszero(exp)) { - _settriple(result, resultsign, 1, 0); - } - else if (mpd_isnegative(exp)) { - _settriple(result, resultsign, 0, 0); - } - else { - mpd_setspecial(result, resultsign, MPD_INF); - } - return; - } - if (mpd_iszero(exp)) { - _settriple(result, resultsign, 1, 0); - return; - } - if (_qcheck_pow_one(result, base, exp, resultsign, ctx, status) == 0) { - return; - } - if (_qcheck_pow_bounds(result, base, exp, resultsign, ctx, status)) { - return; - } - - if (intexp) { - _mpd_qpow_int(result, base, exp, resultsign, ctx, status); - } - else { - _mpd_qpow_real(result, base, exp, ctx, status); - if (!mpd_isspecial(result) && _mpd_cmp(result, &one) == 0) { - mpd_ssize_t shift = ctx->prec-1; - mpd_qshiftl(result, &one, shift, status); - result->exp = -shift; - } - if (mpd_isinfinite(result)) { - /* for ROUND_DOWN, ROUND_FLOOR, etc. */ - _settriple(result, MPD_POS, 1, MPD_EXP_INF); - } - mpd_qfinalize(result, ctx, status); - } -} - -/* - * Internal function: Integer powmod with mpd_uint_t exponent, base is modified! - * Function can fail with MPD_Malloc_error. - */ -static inline void -_mpd_qpowmod_uint(mpd_t *result, mpd_t *base, mpd_uint_t exp, - const mpd_t *mod, uint32_t *status) -{ - mpd_context_t maxcontext; - - mpd_maxcontext(&maxcontext); - - /* resize to smaller cannot fail */ - mpd_qcopy(result, &one, status); - - while (exp > 0) { - if (exp & 1) { - _mpd_qmul_exact(result, result, base, &maxcontext, status); - mpd_qrem(result, result, mod, &maxcontext, status); - } - _mpd_qmul_exact(base, base, base, &maxcontext, status); - mpd_qrem(base, base, mod, &maxcontext, status); - exp >>= 1; - } -} - -/* The powmod function: (base**exp) % mod */ -void -mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, - const mpd_t *mod, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(tbase,0,0,0,0); - MPD_NEW_STATIC(texp,0,0,0,0); - MPD_NEW_STATIC(tmod,0,0,0,0); - MPD_NEW_STATIC(tmp,0,0,0,0); - MPD_NEW_CONST(two,0,0,1,1,1,2); - mpd_ssize_t tbase_exp, texp_exp; - mpd_ssize_t i; - mpd_t t; - mpd_uint_t r; - uint8_t sign; - - - if (mpd_isspecial(base) || mpd_isspecial(exp) || mpd_isspecial(mod)) { - if (mpd_qcheck_3nans(result, base, exp, mod, ctx, status)) { - return; - } - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - - if (!_mpd_isint(base) || !_mpd_isint(exp) || !_mpd_isint(mod)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (mpd_iszerocoeff(mod)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (mod->digits+mod->exp > ctx->prec) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - sign = (mpd_isnegative(base)) && (mpd_isodd(exp)); - if (mpd_iszerocoeff(exp)) { - if (mpd_iszerocoeff(base)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - r = (_mpd_cmp_abs(mod, &one)==0) ? 0 : 1; - _settriple(result, sign, r, 0); - return; - } - if (mpd_isnegative(exp)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (mpd_iszerocoeff(base)) { - _settriple(result, sign, 0, 0); - return; - } - - mpd_maxcontext(&maxcontext); - - mpd_qrescale(&tmod, mod, 0, &maxcontext, &maxcontext.status); - if (maxcontext.status&MPD_Errors) { - mpd_seterror(result, maxcontext.status&MPD_Errors, status); - goto out; - } - maxcontext.status = 0; - mpd_set_positive(&tmod); - - mpd_qround_to_int(&tbase, base, &maxcontext, status); - mpd_set_positive(&tbase); - tbase_exp = tbase.exp; - tbase.exp = 0; - - mpd_qround_to_int(&texp, exp, &maxcontext, status); - texp_exp = texp.exp; - texp.exp = 0; - - /* base = (base.int % modulo * pow(10, base.exp, modulo)) % modulo */ - mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status); - mpd_qshiftl(result, &one, tbase_exp, status); - mpd_qrem(result, result, &tmod, &maxcontext, status); - _mpd_qmul_exact(&tbase, &tbase, result, &maxcontext, status); - mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status); - if (mpd_isspecial(&tbase) || - mpd_isspecial(&texp) || - mpd_isspecial(&tmod)) { - goto mpd_errors; - } - - for (i = 0; i < texp_exp; i++) { - _mpd_qpowmod_uint(&tmp, &tbase, 10, &tmod, status); - t = tmp; - tmp = tbase; - tbase = t; - } - if (mpd_isspecial(&tbase)) { - goto mpd_errors; /* GCOV_UNLIKELY */ - } - - /* resize to smaller cannot fail */ - mpd_qcopy(result, &one, status); - while (mpd_isfinite(&texp) && !mpd_iszero(&texp)) { - if (mpd_isodd(&texp)) { - _mpd_qmul_exact(result, result, &tbase, &maxcontext, status); - mpd_qrem(result, result, &tmod, &maxcontext, status); - } - _mpd_qmul_exact(&tbase, &tbase, &tbase, &maxcontext, status); - mpd_qrem(&tbase, &tbase, &tmod, &maxcontext, status); - mpd_qdivint(&texp, &texp, &two, &maxcontext, status); - } - if (mpd_isspecial(&texp) || mpd_isspecial(&tbase) || - mpd_isspecial(&tmod) || mpd_isspecial(result)) { - /* MPD_Malloc_error */ - goto mpd_errors; - } - else { - mpd_set_sign(result, sign); - } - -out: - mpd_del(&tbase); - mpd_del(&texp); - mpd_del(&tmod); - mpd_del(&tmp); - return; - -mpd_errors: - mpd_setspecial(result, MPD_POS, MPD_NAN); - goto out; -} - -void -mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - uint32_t workstatus = 0; - mpd_ssize_t b_exp = b->exp; - mpd_ssize_t expdiff, shift; - mpd_uint_t rnd; - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(result, a, b, ctx, status)) { - return; - } - if (mpd_isinfinite(a) && mpd_isinfinite(b)) { - mpd_qcopy(result, a, status); - return; - } - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - if (b->exp > ctx->emax || b->exp < mpd_etiny(ctx)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - if (mpd_iszero(a)) { - _settriple(result, mpd_sign(a), 0, b->exp); - mpd_qfinalize(result, ctx, status); - return; - } - - - expdiff = a->exp - b->exp; - if (a->digits + expdiff > ctx->prec) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - if (expdiff >= 0) { - shift = expdiff; - if (!mpd_qshiftl(result, a, shift, status)) { - return; - } - result->exp = b_exp; - } - else { - /* At this point expdiff < 0 and a->digits+expdiff <= prec, - * so the shift before an increment will fit in prec. */ - shift = -expdiff; - rnd = mpd_qshiftr(result, a, shift, status); - if (rnd == MPD_UINT_MAX) { - return; - } - result->exp = b_exp; - if (!_mpd_apply_round_fit(result, rnd, ctx, status)) { - return; - } - workstatus |= MPD_Rounded; - if (rnd) { - workstatus |= MPD_Inexact; - } - } - - if (mpd_adjexp(result) > ctx->emax || - mpd_adjexp(result) < mpd_etiny(ctx)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - *status |= workstatus; - mpd_qfinalize(result, ctx, status); -} - -void -mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_ssize_t shift, maxexp, maxshift; - uint8_t sign_a = mpd_sign(a); - - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - mpd_qcopy(result, a, status); - return; - } - - if (!mpd_qcopy(result, a, status)) { - return; - } - mpd_qfinalize(result, ctx, status); - if (mpd_isspecial(result)) { - return; - } - if (mpd_iszero(result)) { - _settriple(result, sign_a, 0, 0); - return; - } - - shift = mpd_trail_zeros(result); - maxexp = (ctx->clamp) ? mpd_etop(ctx) : ctx->emax; - /* After the finalizing above result->exp <= maxexp. */ - maxshift = maxexp - result->exp; - shift = (shift > maxshift) ? maxshift : shift; - - mpd_qshiftr_inplace(result, shift); - result->exp += shift; -} - -void -mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, - uint32_t *status) -{ - MPD_NEW_STATIC(q,0,0,0,0); - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(r, a, b, ctx, status)) { - return; - } - if (mpd_isinfinite(a)) { - mpd_seterror(r, MPD_Invalid_operation, status); - return; - } - if (mpd_isinfinite(b)) { - mpd_qcopy(r, a, status); - mpd_qfinalize(r, ctx, status); - return; - } - /* debug */ - abort(); /* GCOV_NOT_REACHED */ - } - if (mpd_iszerocoeff(b)) { - if (mpd_iszerocoeff(a)) { - mpd_seterror(r, MPD_Division_undefined, status); - } - else { - mpd_seterror(r, MPD_Invalid_operation, status); - } - return; - } - - _mpd_qdivmod(&q, r, a, b, ctx, status); - mpd_del(&q); - mpd_qfinalize(r, ctx, status); -} - -void -mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_context_t workctx; - MPD_NEW_STATIC(btmp,0,0,0,0); - MPD_NEW_STATIC(q,0,0,0,0); - mpd_ssize_t expdiff, qdigits; - int cmp, isodd, allnine; - - assert(r != NULL); /* annotation for scan-build */ - - if (mpd_isspecial(a) || mpd_isspecial(b)) { - if (mpd_qcheck_nans(r, a, b, ctx, status)) { - return; - } - if (mpd_isinfinite(a)) { - mpd_seterror(r, MPD_Invalid_operation, status); - return; - } - if (mpd_isinfinite(b)) { - mpd_qcopy(r, a, status); - mpd_qfinalize(r, ctx, status); - return; - } - /* debug */ - abort(); /* GCOV_NOT_REACHED */ - } - if (mpd_iszerocoeff(b)) { - if (mpd_iszerocoeff(a)) { - mpd_seterror(r, MPD_Division_undefined, status); - } - else { - mpd_seterror(r, MPD_Invalid_operation, status); - } - return; - } - - if (r == b) { - if (!mpd_qcopy(&btmp, b, status)) { - mpd_seterror(r, MPD_Malloc_error, status); - return; - } - b = &btmp; - } - - _mpd_qdivmod(&q, r, a, b, ctx, status); - if (mpd_isnan(&q) || mpd_isnan(r)) { - goto finish; - } - if (mpd_iszerocoeff(r)) { - goto finish; - } - - expdiff = mpd_adjexp(b) - mpd_adjexp(r); - if (-1 <= expdiff && expdiff <= 1) { - - allnine = mpd_coeff_isallnine(&q); - qdigits = q.digits; - isodd = mpd_isodd(&q); - - mpd_maxcontext(&workctx); - if (mpd_sign(a) == mpd_sign(b)) { - /* sign(r) == sign(b) */ - _mpd_qsub(&q, r, b, &workctx, &workctx.status); - } - else { - /* sign(r) != sign(b) */ - _mpd_qadd(&q, r, b, &workctx, &workctx.status); - } - - if (workctx.status&MPD_Errors) { - mpd_seterror(r, workctx.status&MPD_Errors, status); - goto finish; - } - - cmp = _mpd_cmp_abs(&q, r); - if (cmp < 0 || (cmp == 0 && isodd)) { - /* abs(r) > abs(b)/2 or abs(r) == abs(b)/2 and isodd(quotient) */ - if (allnine && qdigits == ctx->prec) { - /* abs(quotient) + 1 == 10**prec */ - mpd_seterror(r, MPD_Division_impossible, status); - goto finish; - } - mpd_qcopy(r, &q, status); - } - } - - -finish: - mpd_del(&btmp); - mpd_del(&q); - mpd_qfinalize(r, ctx, status); -} - -static void -_mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_ssize_t expdiff, shift; - mpd_uint_t rnd; - - if (mpd_isspecial(a)) { - mpd_qcopy(result, a, status); - return; - } - - if (mpd_iszero(a)) { - _settriple(result, mpd_sign(a), 0, exp); - return; - } - - expdiff = a->exp - exp; - if (expdiff >= 0) { - shift = expdiff; - if (a->digits + shift > MPD_MAX_PREC+1) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - if (!mpd_qshiftl(result, a, shift, status)) { - return; - } - result->exp = exp; - } - else { - shift = -expdiff; - rnd = mpd_qshiftr(result, a, shift, status); - if (rnd == MPD_UINT_MAX) { - return; - } - result->exp = exp; - _mpd_apply_round_excess(result, rnd, ctx, status); - *status |= MPD_Rounded; - if (rnd) { - *status |= MPD_Inexact; - } - } - - if (mpd_issubnormal(result, ctx)) { - *status |= MPD_Subnormal; - } -} - -/* - * Rescale a number so that it has exponent 'exp'. Does not regard context - * precision, emax, emin, but uses the rounding mode. Special numbers are - * quietly copied. Restrictions: - * - * MPD_MIN_ETINY <= exp <= MPD_MAX_EMAX+1 - * result->digits <= MPD_MAX_PREC+1 - */ -void -mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, - const mpd_context_t *ctx, uint32_t *status) -{ - if (exp > MPD_MAX_EMAX+1 || exp < MPD_MIN_ETINY) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - _mpd_qrescale(result, a, exp, ctx, status); -} - -/* - * Same as mpd_qrescale, but with relaxed restrictions. The result of this - * function should only be used for formatting a number and never as input - * for other operations. - * - * MPD_MIN_ETINY-MPD_MAX_PREC <= exp <= MPD_MAX_EMAX+1 - * result->digits <= MPD_MAX_PREC+1 - */ -void -mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, - const mpd_context_t *ctx, uint32_t *status) -{ - if (exp > MPD_MAX_EMAX+1 || exp < MPD_MIN_ETINY-MPD_MAX_PREC) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - _mpd_qrescale(result, a, exp, ctx, status); -} - -/* Round to an integer according to 'action' and ctx->round. */ -enum {TO_INT_EXACT, TO_INT_SILENT, TO_INT_TRUNC}; -static void -_mpd_qround_to_integral(int action, mpd_t *result, const mpd_t *a, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_uint_t rnd; - - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - mpd_qcopy(result, a, status); - return; - } - if (a->exp >= 0) { - mpd_qcopy(result, a, status); - return; - } - if (mpd_iszerocoeff(a)) { - _settriple(result, mpd_sign(a), 0, 0); - return; - } - - rnd = mpd_qshiftr(result, a, -a->exp, status); - if (rnd == MPD_UINT_MAX) { - return; - } - result->exp = 0; - - if (action == TO_INT_EXACT || action == TO_INT_SILENT) { - _mpd_apply_round_excess(result, rnd, ctx, status); - if (action == TO_INT_EXACT) { - *status |= MPD_Rounded; - if (rnd) { - *status |= MPD_Inexact; - } - } - } -} - -void -mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - (void)_mpd_qround_to_integral(TO_INT_EXACT, result, a, ctx, status); -} - -void -mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a, ctx, status); -} - -void -mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - if (mpd_isspecial(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - (void)_mpd_qround_to_integral(TO_INT_TRUNC, result, a, ctx, status); -} - -void -mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t workctx = *ctx; - - if (mpd_isspecial(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - workctx.round = MPD_ROUND_FLOOR; - (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a, - &workctx, status); -} - -void -mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t workctx = *ctx; - - if (mpd_isspecial(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - workctx.round = MPD_ROUND_CEILING; - (void)_mpd_qround_to_integral(TO_INT_SILENT, result, a, - &workctx, status); -} - -int -mpd_same_quantum(const mpd_t *a, const mpd_t *b) -{ - if (mpd_isspecial(a) || mpd_isspecial(b)) { - return ((mpd_isnan(a) && mpd_isnan(b)) || - (mpd_isinfinite(a) && mpd_isinfinite(b))); - } - - return a->exp == b->exp; -} - -/* Schedule the increase in precision for the Newton iteration. */ -static inline int -recpr_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], - mpd_ssize_t maxprec, mpd_ssize_t initprec) -{ - mpd_ssize_t k; - int i; - - assert(maxprec > 0 && initprec > 0); - if (maxprec <= initprec) return -1; - - i = 0; k = maxprec; - do { - k = (k+1) / 2; - klist[i++] = k; - } while (k > initprec); - - return i-1; -} - -/* - * Initial approximation for the reciprocal: - * k_0 := MPD_RDIGITS-2 - * z_0 := 10**(-k_0) * floor(10**(2*k_0 + 2) / floor(v * 10**(k_0 + 2))) - * Absolute error: - * |1/v - z_0| < 10**(-k_0) - * ACL2 proof: maxerror-inverse-approx - */ -static void -_mpd_qreciprocal_approx(mpd_t *z, const mpd_t *v, uint32_t *status) -{ - mpd_uint_t p10data[2] = {0, mpd_pow10[MPD_RDIGITS-2]}; - mpd_uint_t dummy, word; - int n; - - assert(v->exp == -v->digits); - - _mpd_get_msdigits(&dummy, &word, v, MPD_RDIGITS); - n = mpd_word_digits(word); - word *= mpd_pow10[MPD_RDIGITS-n]; - - mpd_qresize(z, 2, status); - (void)_mpd_shortdiv(z->data, p10data, 2, word); - - mpd_clear_flags(z); - z->exp = -(MPD_RDIGITS-2); - z->len = (z->data[1] == 0) ? 1 : 2; - mpd_setdigits(z); -} - -/* - * Reciprocal, calculated with Newton's Method. Assumption: result != a. - * NOTE: The comments in the function show that certain operations are - * exact. The proof for the maximum error is too long to fit in here. - * ACL2 proof: maxerror-inverse-complete - */ -static void -_mpd_qreciprocal(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t varcontext, maxcontext; - mpd_t *z = result; /* current approximation */ - mpd_t *v; /* a, normalized to a number between 0.1 and 1 */ - MPD_NEW_SHARED(vtmp, a); /* v shares data with a */ - MPD_NEW_STATIC(s,0,0,0,0); /* temporary variable */ - MPD_NEW_STATIC(t,0,0,0,0); /* temporary variable */ - MPD_NEW_CONST(two,0,0,1,1,1,2); /* const 2 */ - mpd_ssize_t klist[MPD_MAX_PREC_LOG2]; - mpd_ssize_t adj, maxprec, initprec; - uint8_t sign = mpd_sign(a); - int i; - - assert(result != a); - - v = &vtmp; - mpd_clear_flags(v); - adj = v->digits + v->exp; - v->exp = -v->digits; - - /* Initial approximation */ - _mpd_qreciprocal_approx(z, v, status); - - mpd_maxcontext(&varcontext); - mpd_maxcontext(&maxcontext); - varcontext.round = maxcontext.round = MPD_ROUND_TRUNC; - varcontext.emax = maxcontext.emax = MPD_MAX_EMAX + 100; - varcontext.emin = maxcontext.emin = MPD_MIN_EMIN - 100; - maxcontext.prec = MPD_MAX_PREC + 100; - - maxprec = ctx->prec; - maxprec += 2; - initprec = MPD_RDIGITS-3; - - i = recpr_schedule_prec(klist, maxprec, initprec); - for (; i >= 0; i--) { - /* Loop invariant: z->digits <= klist[i]+7 */ - /* Let s := z**2, exact result */ - _mpd_qmul_exact(&s, z, z, &maxcontext, status); - varcontext.prec = 2*klist[i] + 5; - if (v->digits > varcontext.prec) { - /* Let t := v, truncated to n >= 2*k+5 fraction digits */ - mpd_qshiftr(&t, v, v->digits-varcontext.prec, status); - t.exp = -varcontext.prec; - /* Let t := trunc(v)*s, truncated to n >= 2*k+1 fraction digits */ - mpd_qmul(&t, &t, &s, &varcontext, status); - } - else { /* v->digits <= 2*k+5 */ - /* Let t := v*s, truncated to n >= 2*k+1 fraction digits */ - mpd_qmul(&t, v, &s, &varcontext, status); - } - /* Let s := 2*z, exact result */ - _mpd_qmul_exact(&s, z, &two, &maxcontext, status); - /* s.digits < t.digits <= 2*k+5, |adjexp(s)-adjexp(t)| <= 1, - * so the subtraction generates at most 2*k+6 <= klist[i+1]+7 - * digits. The loop invariant is preserved. */ - _mpd_qsub_exact(z, &s, &t, &maxcontext, status); - } - - if (!mpd_isspecial(z)) { - z->exp -= adj; - mpd_set_flags(z, sign); - } - - mpd_del(&s); - mpd_del(&t); - mpd_qfinalize(z, ctx, status); -} - -/* - * Internal function for large numbers: - * - * q, r = divmod(coeff(a), coeff(b)) - * - * Strategy: Multiply the dividend by the reciprocal of the divisor. The - * inexact result is fixed by a small loop, using at most one iteration. - * - * ACL2 proofs: - * ------------ - * 1) q is a natural number. (ndivmod-quotient-natp) - * 2) r is a natural number. (ndivmod-remainder-natp) - * 3) a = q * b + r (ndivmod-q*b+r==a) - * 4) r < b (ndivmod-remainder-<-b) - */ -static void -_mpd_base_ndivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, - uint32_t *status) -{ - mpd_context_t workctx; - mpd_t *qq = q, *rr = r; - mpd_t aa, bb; - int k; - - _mpd_copy_shared(&aa, a); - _mpd_copy_shared(&bb, b); - - mpd_set_positive(&aa); - mpd_set_positive(&bb); - aa.exp = 0; - bb.exp = 0; - - if (q == a || q == b) { - if ((qq = mpd_qnew()) == NULL) { - *status |= MPD_Malloc_error; - goto nanresult; - } - } - if (r == a || r == b) { - if ((rr = mpd_qnew()) == NULL) { - *status |= MPD_Malloc_error; - goto nanresult; - } - } - - mpd_maxcontext(&workctx); - - /* Let prec := adigits - bdigits + 4 */ - workctx.prec = a->digits - b->digits + 1 + 3; - if (a->digits > MPD_MAX_PREC || workctx.prec > MPD_MAX_PREC) { - *status |= MPD_Division_impossible; - goto nanresult; - } - - /* Let x := _mpd_qreciprocal(b, prec) - * Then x is bounded by: - * 1) 1/b - 10**(-prec - bdigits) < x < 1/b + 10**(-prec - bdigits) - * 2) 1/b - 10**(-adigits - 4) < x < 1/b + 10**(-adigits - 4) - */ - _mpd_qreciprocal(rr, &bb, &workctx, &workctx.status); - - /* Get an estimate for the quotient. Let q := a * x - * Then q is bounded by: - * 3) a/b - 10**-4 < q < a/b + 10**-4 - */ - _mpd_qmul(qq, &aa, rr, &workctx, &workctx.status); - /* Truncate q to an integer: - * 4) a/b - 2 < trunc(q) < a/b + 1 - */ - mpd_qtrunc(qq, qq, &workctx, &workctx.status); - - workctx.prec = aa.digits + 3; - workctx.emax = MPD_MAX_EMAX + 3; - workctx.emin = MPD_MIN_EMIN - 3; - /* Multiply the estimate for q by b: - * 5) a - 2 * b < trunc(q) * b < a + b - */ - _mpd_qmul(rr, &bb, qq, &workctx, &workctx.status); - /* Get the estimate for r such that a = q * b + r. */ - _mpd_qsub_exact(rr, &aa, rr, &workctx, &workctx.status); - - /* Fix the result. At this point -b < r < 2*b, so the correction loop - takes at most one iteration. */ - for (k = 0;; k++) { - if (mpd_isspecial(qq) || mpd_isspecial(rr)) { - *status |= (workctx.status&MPD_Errors); - goto nanresult; - } - if (k > 2) { /* Allow two iterations despite the proof. */ - mpd_err_warn("libmpdec: internal error in " /* GCOV_NOT_REACHED */ - "_mpd_base_ndivmod: please report"); /* GCOV_NOT_REACHED */ - *status |= MPD_Invalid_operation; /* GCOV_NOT_REACHED */ - goto nanresult; /* GCOV_NOT_REACHED */ - } - /* r < 0 */ - else if (_mpd_cmp(&zero, rr) == 1) { - _mpd_qadd_exact(rr, rr, &bb, &workctx, &workctx.status); - _mpd_qadd_exact(qq, qq, &minus_one, &workctx, &workctx.status); - } - /* 0 <= r < b */ - else if (_mpd_cmp(rr, &bb) == -1) { - break; - } - /* r >= b */ - else { - _mpd_qsub_exact(rr, rr, &bb, &workctx, &workctx.status); - _mpd_qadd_exact(qq, qq, &one, &workctx, &workctx.status); - } - } - - if (qq != q) { - if (!mpd_qcopy(q, qq, status)) { - goto nanresult; /* GCOV_UNLIKELY */ - } - mpd_del(qq); - } - if (rr != r) { - if (!mpd_qcopy(r, rr, status)) { - goto nanresult; /* GCOV_UNLIKELY */ - } - mpd_del(rr); - } - - *status |= (workctx.status&MPD_Errors); - return; - - -nanresult: - if (qq && qq != q) mpd_del(qq); - if (rr && rr != r) mpd_del(rr); - mpd_setspecial(q, MPD_POS, MPD_NAN); - mpd_setspecial(r, MPD_POS, MPD_NAN); -} - -/* LIBMPDEC_ONLY */ -/* - * Schedule the optimal precision increase for the Newton iteration. - * v := input operand - * z_0 := initial approximation - * initprec := natural number such that abs(sqrt(v) - z_0) < 10**-initprec - * maxprec := target precision - * - * For convenience the output klist contains the elements in reverse order: - * klist := [k_n-1, ..., k_0], where - * 1) k_0 <= initprec and - * 2) abs(sqrt(v) - result) < 10**(-2*k_n-1 + 2) <= 10**-maxprec. - */ -static inline int -invroot_schedule_prec(mpd_ssize_t klist[MPD_MAX_PREC_LOG2], - mpd_ssize_t maxprec, mpd_ssize_t initprec) -{ - mpd_ssize_t k; - int i; - - assert(maxprec >= 3 && initprec >= 3); - if (maxprec <= initprec) return -1; - - i = 0; k = maxprec; - do { - k = (k+3) / 2; - klist[i++] = k; - } while (k > initprec); - - return i-1; -} - -/* - * Initial approximation for the inverse square root function. - * Input: - * v := rational number, with 1 <= v < 100 - * vhat := floor(v * 10**6) - * Output: - * z := approximation to 1/sqrt(v), such that abs(z - 1/sqrt(v)) < 10**-3. - */ -static inline void -_invroot_init_approx(mpd_t *z, mpd_uint_t vhat) -{ - mpd_uint_t lo = 1000; - mpd_uint_t hi = 10000; - mpd_uint_t a, sq; - - assert(lo*lo <= vhat && vhat < (hi+1)*(hi+1)); - - for(;;) { - a = (lo + hi) / 2; - sq = a * a; - if (vhat >= sq) { - if (vhat < sq + 2*a + 1) { - break; - } - lo = a + 1; - } - else { - hi = a - 1; - } - } - - /* - * After the binary search we have: - * 1) a**2 <= floor(v * 10**6) < (a + 1)**2 - * This implies: - * 2) a**2 <= v * 10**6 < (a + 1)**2 - * 3) a <= sqrt(v) * 10**3 < a + 1 - * Since 10**3 <= a: - * 4) 0 <= 10**prec/a - 1/sqrt(v) < 10**-prec - * We have: - * 5) 10**3/a - 10**-3 < floor(10**9/a) * 10**-6 <= 10**3/a - * Merging 4) and 5): - * 6) abs(floor(10**9/a) * 10**-6 - 1/sqrt(v)) < 10**-3 - */ - mpd_minalloc(z); - mpd_clear_flags(z); - z->data[0] = 1000000000UL / a; - z->len = 1; - z->exp = -6; - mpd_setdigits(z); -} - -/* - * Set 'result' to 1/sqrt(a). - * Relative error: abs(result - 1/sqrt(a)) < 10**-prec * 1/sqrt(a) - */ -static void -_mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - uint32_t workstatus = 0; - mpd_context_t varcontext, maxcontext; - mpd_t *z = result; /* current approximation */ - mpd_t *v; /* a, normalized to a number between 1 and 100 */ - MPD_NEW_SHARED(vtmp, a); /* by default v will share data with a */ - MPD_NEW_STATIC(s,0,0,0,0); /* temporary variable */ - MPD_NEW_STATIC(t,0,0,0,0); /* temporary variable */ - MPD_NEW_CONST(one_half,0,-1,1,1,1,5); - MPD_NEW_CONST(three,0,0,1,1,1,3); - mpd_ssize_t klist[MPD_MAX_PREC_LOG2]; - mpd_ssize_t ideal_exp, shift; - mpd_ssize_t adj, tz; - mpd_ssize_t maxprec, fracdigits; - mpd_uint_t vhat, dummy; - int i, n; - - - ideal_exp = -(a->exp - (a->exp & 1)) / 2; - - v = &vtmp; - if (result == a) { - if ((v = mpd_qncopy(a)) == NULL) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - } - - /* normalize a to 1 <= v < 100 */ - if ((v->digits+v->exp) & 1) { - fracdigits = v->digits - 1; - v->exp = -fracdigits; - n = (v->digits > 7) ? 7 : (int)v->digits; - /* Let vhat := floor(v * 10**(2*initprec)) */ - _mpd_get_msdigits(&dummy, &vhat, v, n); - if (n < 7) { - vhat *= mpd_pow10[7-n]; - } - } - else { - fracdigits = v->digits - 2; - v->exp = -fracdigits; - n = (v->digits > 8) ? 8 : (int)v->digits; - /* Let vhat := floor(v * 10**(2*initprec)) */ - _mpd_get_msdigits(&dummy, &vhat, v, n); - if (n < 8) { - vhat *= mpd_pow10[8-n]; - } - } - adj = (a->exp-v->exp) / 2; - - /* initial approximation */ - _invroot_init_approx(z, vhat); - - mpd_maxcontext(&maxcontext); - mpd_maxcontext(&varcontext); - varcontext.round = MPD_ROUND_TRUNC; - maxprec = ctx->prec + 1; - - /* initprec == 3 */ - i = invroot_schedule_prec(klist, maxprec, 3); - for (; i >= 0; i--) { - varcontext.prec = 2*klist[i]+2; - mpd_qmul(&s, z, z, &maxcontext, &workstatus); - if (v->digits > varcontext.prec) { - shift = v->digits - varcontext.prec; - mpd_qshiftr(&t, v, shift, &workstatus); - t.exp += shift; - mpd_qmul(&t, &t, &s, &varcontext, &workstatus); - } - else { - mpd_qmul(&t, v, &s, &varcontext, &workstatus); - } - mpd_qsub(&t, &three, &t, &maxcontext, &workstatus); - mpd_qmul(z, z, &t, &varcontext, &workstatus); - mpd_qmul(z, z, &one_half, &maxcontext, &workstatus); - } - - z->exp -= adj; - - tz = mpd_trail_zeros(result); - shift = ideal_exp - result->exp; - shift = (tz > shift) ? shift : tz; - if (shift > 0) { - mpd_qshiftr_inplace(result, shift); - result->exp += shift; - } - - - mpd_del(&s); - mpd_del(&t); - if (v != &vtmp) mpd_del(v); - *status |= (workstatus&MPD_Errors); - *status |= (MPD_Rounded|MPD_Inexact); -} - -void -mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t workctx; - - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - if (mpd_isnegative(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - /* positive infinity */ - _settriple(result, MPD_POS, 0, mpd_etiny(ctx)); - *status |= MPD_Clamped; - return; - } - if (mpd_iszero(a)) { - mpd_setspecial(result, mpd_sign(a), MPD_INF); - *status |= MPD_Division_by_zero; - return; - } - if (mpd_isnegative(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - workctx = *ctx; - workctx.prec += 2; - workctx.round = MPD_ROUND_HALF_EVEN; - _mpd_qinvroot(result, a, &workctx, status); - mpd_qfinalize(result, ctx, status); -} -/* END LIBMPDEC_ONLY */ - -/* Algorithm from decimal.py */ -static void -_mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - mpd_context_t maxcontext; - MPD_NEW_STATIC(c,0,0,0,0); - MPD_NEW_STATIC(q,0,0,0,0); - MPD_NEW_STATIC(r,0,0,0,0); - MPD_NEW_CONST(two,0,0,1,1,1,2); - mpd_ssize_t prec, ideal_exp; - mpd_ssize_t l, shift; - int exact = 0; - - - ideal_exp = (a->exp - (a->exp & 1)) / 2; - - if (mpd_isspecial(a)) { - if (mpd_qcheck_nan(result, a, ctx, status)) { - return; - } - if (mpd_isnegative(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - mpd_setspecial(result, MPD_POS, MPD_INF); - return; - } - if (mpd_iszero(a)) { - _settriple(result, mpd_sign(a), 0, ideal_exp); - mpd_qfinalize(result, ctx, status); - return; - } - if (mpd_isnegative(a)) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - mpd_maxcontext(&maxcontext); - prec = ctx->prec + 1; - - if (!mpd_qcopy(&c, a, status)) { - goto malloc_error; - } - c.exp = 0; - - if (a->exp & 1) { - if (!mpd_qshiftl(&c, &c, 1, status)) { - goto malloc_error; - } - l = (a->digits >> 1) + 1; - } - else { - l = (a->digits + 1) >> 1; - } - - shift = prec - l; - if (shift >= 0) { - if (!mpd_qshiftl(&c, &c, 2*shift, status)) { - goto malloc_error; - } - exact = 1; - } - else { - exact = !mpd_qshiftr_inplace(&c, -2*shift); - } - - ideal_exp -= shift; - - /* find result = floor(sqrt(c)) using Newton's method */ - if (!mpd_qshiftl(result, &one, prec, status)) { - goto malloc_error; - } - - while (1) { - _mpd_qdivmod(&q, &r, &c, result, &maxcontext, &maxcontext.status); - if (mpd_isspecial(result) || mpd_isspecial(&q)) { - mpd_seterror(result, maxcontext.status&MPD_Errors, status); - goto out; - } - if (_mpd_cmp(result, &q) <= 0) { - break; - } - _mpd_qadd_exact(result, result, &q, &maxcontext, &maxcontext.status); - if (mpd_isspecial(result)) { - mpd_seterror(result, maxcontext.status&MPD_Errors, status); - goto out; - } - _mpd_qdivmod(result, &r, result, &two, &maxcontext, &maxcontext.status); - } - - if (exact) { - _mpd_qmul_exact(&r, result, result, &maxcontext, &maxcontext.status); - if (mpd_isspecial(&r)) { - mpd_seterror(result, maxcontext.status&MPD_Errors, status); - goto out; - } - exact = (_mpd_cmp(&r, &c) == 0); - } - - if (exact) { - if (shift >= 0) { - mpd_qshiftr_inplace(result, shift); - } - else { - if (!mpd_qshiftl(result, result, -shift, status)) { - goto malloc_error; - } - } - ideal_exp += shift; - } - else { - int lsd = (int)mpd_lsd(result->data[0]); - if (lsd == 0 || lsd == 5) { - result->data[0] += 1; - } - } - - result->exp = ideal_exp; - - -out: - mpd_del(&c); - mpd_del(&q); - mpd_del(&r); - maxcontext = *ctx; - maxcontext.round = MPD_ROUND_HALF_EVEN; - mpd_qfinalize(result, &maxcontext, status); - return; - -malloc_error: - mpd_seterror(result, MPD_Malloc_error, status); - goto out; -} - -void -mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, - uint32_t *status) -{ - MPD_NEW_STATIC(aa,0,0,0,0); - uint32_t xstatus = 0; - - if (result == a) { - if (!mpd_qcopy(&aa, a, status)) { - mpd_seterror(result, MPD_Malloc_error, status); - goto out; - } - a = &aa; - } - - _mpd_qsqrt(result, a, ctx, &xstatus); - - if (xstatus & (MPD_Malloc_error|MPD_Division_impossible)) { - /* The above conditions can occur at very high context precisions - * if intermediate values get too large. Retry the operation with - * a lower context precision in case the result is exact. - * - * If the result is exact, an upper bound for the number of digits - * is the number of digits in the input. - * - * NOTE: sqrt(40e9) = 2.0e+5 /\ digits(40e9) = digits(2.0e+5) = 2 - */ - uint32_t ystatus = 0; - mpd_context_t workctx = *ctx; - - workctx.prec = a->digits; - if (workctx.prec >= ctx->prec) { - *status |= (xstatus|MPD_Errors); - goto out; /* No point in repeating this, keep the original error. */ - } - - _mpd_qsqrt(result, a, &workctx, &ystatus); - if (ystatus != 0) { - ystatus = *status | ((xstatus|ystatus)&MPD_Errors); - mpd_seterror(result, ystatus, status); - } - } - else { - *status |= xstatus; - } - -out: - mpd_del(&aa); -} - - -/******************************************************************************/ -/* Base conversions */ -/******************************************************************************/ - -/* Space needed to represent an integer mpd_t in base 'base'. */ -size_t -mpd_sizeinbase(const mpd_t *a, uint32_t base) -{ - double x; - size_t digits; - double upper_bound; - - assert(mpd_isinteger(a)); - assert(base >= 2); - - if (mpd_iszero(a)) { - return 1; - } - - digits = a->digits+a->exp; - -#ifdef CONFIG_64 - /* ceil(2711437152599294 / log10(2)) + 4 == 2**53 */ - if (digits > 2711437152599294ULL) { - return SIZE_MAX; - } - - upper_bound = (double)((1ULL<<53)-1); -#else - upper_bound = (double)(SIZE_MAX-1); -#endif - - x = (double)digits / log10(base); - return (x > upper_bound) ? SIZE_MAX : (size_t)x + 1; -} - -/* Space needed to import a base 'base' integer of length 'srclen'. */ -static mpd_ssize_t -_mpd_importsize(size_t srclen, uint32_t base) -{ - double x; - double upper_bound; - - assert(srclen > 0); - assert(base >= 2); - -#if SIZE_MAX == UINT64_MAX - if (srclen > (1ULL<<53)) { - return MPD_SSIZE_MAX; - } - - assert((1ULL<<53) <= MPD_MAXIMPORT); - upper_bound = (double)((1ULL<<53)-1); -#else - upper_bound = MPD_MAXIMPORT-1; -#endif - - x = (double)srclen * (log10(base)/MPD_RDIGITS); - return (x > upper_bound) ? MPD_SSIZE_MAX : (mpd_ssize_t)x + 1; -} - -static uint8_t -mpd_resize_u16(uint16_t **w, size_t nmemb) -{ - uint8_t err = 0; - *w = mpd_realloc(*w, nmemb, sizeof **w, &err); - return !err; -} - -static uint8_t -mpd_resize_u32(uint32_t **w, size_t nmemb) -{ - uint8_t err = 0; - *w = mpd_realloc(*w, nmemb, sizeof **w, &err); - return !err; -} - -static size_t -_baseconv_to_u16(uint16_t **w, size_t wlen, mpd_uint_t wbase, - mpd_uint_t *u, mpd_ssize_t ulen) -{ - size_t n = 0; - - assert(wlen > 0 && ulen > 0); - assert(wbase <= (1U<<16)); - - do { - if (n >= wlen) { - if (!mpd_resize_u16(w, n+1)) { - return SIZE_MAX; - } - wlen = n+1; - } - (*w)[n++] = (uint16_t)_mpd_shortdiv(u, u, ulen, wbase); - /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */ - ulen = _mpd_real_size(u, ulen); - - } while (u[ulen-1] != 0); - - return n; -} - -static size_t -_coeff_from_u16(mpd_t *w, mpd_ssize_t wlen, - const mpd_uint_t *u, size_t ulen, uint32_t ubase, - uint32_t *status) -{ - mpd_ssize_t n = 0; - mpd_uint_t carry; - - assert(wlen > 0 && ulen > 0); - assert(ubase <= (1U<<16)); - - w->data[n++] = u[--ulen]; - while (--ulen != SIZE_MAX) { - carry = _mpd_shortmul_c(w->data, w->data, n, ubase); - if (carry) { - if (n >= wlen) { - if (!mpd_qresize(w, n+1, status)) { - return SIZE_MAX; - } - wlen = n+1; - } - w->data[n++] = carry; - } - carry = _mpd_shortadd(w->data, n, u[ulen]); - if (carry) { - if (n >= wlen) { - if (!mpd_qresize(w, n+1, status)) { - return SIZE_MAX; - } - wlen = n+1; - } - w->data[n++] = carry; - } - } - - return n; -} - -/* target base wbase < source base ubase */ -static size_t -_baseconv_to_smaller(uint32_t **w, size_t wlen, uint32_t wbase, - mpd_uint_t *u, mpd_ssize_t ulen, mpd_uint_t ubase) -{ - size_t n = 0; - - assert(wlen > 0 && ulen > 0); - assert(wbase < ubase); - - do { - if (n >= wlen) { - if (!mpd_resize_u32(w, n+1)) { - return SIZE_MAX; - } - wlen = n+1; - } - (*w)[n++] = (uint32_t)_mpd_shortdiv_b(u, u, ulen, wbase, ubase); - /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */ - ulen = _mpd_real_size(u, ulen); - - } while (u[ulen-1] != 0); - - return n; -} - -#ifdef CONFIG_32 -/* target base 'wbase' == source base 'ubase' */ -static size_t -_copy_equal_base(uint32_t **w, size_t wlen, - const uint32_t *u, size_t ulen) -{ - if (wlen < ulen) { - if (!mpd_resize_u32(w, ulen)) { - return SIZE_MAX; - } - } - - memcpy(*w, u, ulen * (sizeof **w)); - return ulen; -} - -/* target base 'wbase' > source base 'ubase' */ -static size_t -_baseconv_to_larger(uint32_t **w, size_t wlen, mpd_uint_t wbase, - const mpd_uint_t *u, size_t ulen, mpd_uint_t ubase) -{ - size_t n = 0; - mpd_uint_t carry; - - assert(wlen > 0 && ulen > 0); - assert(ubase < wbase); - - (*w)[n++] = u[--ulen]; - while (--ulen != SIZE_MAX) { - carry = _mpd_shortmul_b(*w, *w, n, ubase, wbase); - if (carry) { - if (n >= wlen) { - if (!mpd_resize_u32(w, n+1)) { - return SIZE_MAX; - } - wlen = n+1; - } - (*w)[n++] = carry; - } - carry = _mpd_shortadd_b(*w, n, u[ulen], wbase); - if (carry) { - if (n >= wlen) { - if (!mpd_resize_u32(w, n+1)) { - return SIZE_MAX; - } - wlen = n+1; - } - (*w)[n++] = carry; - } - } - - return n; -} - -/* target base wbase < source base ubase */ -static size_t -_coeff_from_larger_base(mpd_t *w, size_t wlen, mpd_uint_t wbase, - mpd_uint_t *u, mpd_ssize_t ulen, mpd_uint_t ubase, - uint32_t *status) -{ - size_t n = 0; - - assert(wlen > 0 && ulen > 0); - assert(wbase < ubase); - - do { - if (n >= wlen) { - if (!mpd_qresize(w, n+1, status)) { - return SIZE_MAX; - } - wlen = n+1; - } - w->data[n++] = (uint32_t)_mpd_shortdiv_b(u, u, ulen, wbase, ubase); - /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */ - ulen = _mpd_real_size(u, ulen); - - } while (u[ulen-1] != 0); - - return n; -} -#endif - -/* target base 'wbase' > source base 'ubase' */ -static size_t -_coeff_from_smaller_base(mpd_t *w, mpd_ssize_t wlen, mpd_uint_t wbase, - const uint32_t *u, size_t ulen, mpd_uint_t ubase, - uint32_t *status) -{ - mpd_ssize_t n = 0; - mpd_uint_t carry; - - assert(wlen > 0 && ulen > 0); - assert(wbase > ubase); - - w->data[n++] = u[--ulen]; - while (--ulen != SIZE_MAX) { - carry = _mpd_shortmul_b(w->data, w->data, n, ubase, wbase); - if (carry) { - if (n >= wlen) { - if (!mpd_qresize(w, n+1, status)) { - return SIZE_MAX; - } - wlen = n+1; - } - w->data[n++] = carry; - } - carry = _mpd_shortadd_b(w->data, n, u[ulen], wbase); - if (carry) { - if (n >= wlen) { - if (!mpd_qresize(w, n+1, status)) { - return SIZE_MAX; - } - wlen = n+1; - } - w->data[n++] = carry; - } - } - - return n; -} - -/* - * Convert an integer mpd_t to a multiprecision integer with base <= 2**16. - * The least significant word of the result is (*rdata)[0]. - * - * If rdata is NULL, space is allocated by the function and rlen is irrelevant. - * In case of an error any allocated storage is freed and rdata is set back to - * NULL. - * - * If rdata is non-NULL, it MUST be allocated by one of libmpdec's allocation - * functions and rlen MUST be correct. If necessary, the function will resize - * rdata. In case of an error the caller must free rdata. - * - * Return value: In case of success, the exact length of rdata, SIZE_MAX - * otherwise. - */ -size_t -mpd_qexport_u16(uint16_t **rdata, size_t rlen, uint32_t rbase, - const mpd_t *src, uint32_t *status) -{ - MPD_NEW_STATIC(tsrc,0,0,0,0); - int alloc = 0; /* rdata == NULL */ - size_t n; - - assert(rbase <= (1U<<16)); - - if (mpd_isspecial(src) || !_mpd_isint(src)) { - *status |= MPD_Invalid_operation; - return SIZE_MAX; - } - - if (*rdata == NULL) { - rlen = mpd_sizeinbase(src, rbase); - if (rlen == SIZE_MAX) { - *status |= MPD_Invalid_operation; - return SIZE_MAX; - } - *rdata = mpd_alloc(rlen, sizeof **rdata); - if (*rdata == NULL) { - goto malloc_error; - } - alloc = 1; - } - - if (mpd_iszero(src)) { - **rdata = 0; - return 1; - } - - if (src->exp >= 0) { - if (!mpd_qshiftl(&tsrc, src, src->exp, status)) { - goto malloc_error; - } - } - else { - if (mpd_qshiftr(&tsrc, src, -src->exp, status) == MPD_UINT_MAX) { - goto malloc_error; - } - } - - n = _baseconv_to_u16(rdata, rlen, rbase, tsrc.data, tsrc.len); - if (n == SIZE_MAX) { - goto malloc_error; - } - - -out: - mpd_del(&tsrc); - return n; - -malloc_error: - if (alloc) { - mpd_free(*rdata); - *rdata = NULL; - } - n = SIZE_MAX; - *status |= MPD_Malloc_error; - goto out; -} - -/* - * Convert an integer mpd_t to a multiprecision integer with base<=UINT32_MAX. - * The least significant word of the result is (*rdata)[0]. - * - * If rdata is NULL, space is allocated by the function and rlen is irrelevant. - * In case of an error any allocated storage is freed and rdata is set back to - * NULL. - * - * If rdata is non-NULL, it MUST be allocated by one of libmpdec's allocation - * functions and rlen MUST be correct. If necessary, the function will resize - * rdata. In case of an error the caller must free rdata. - * - * Return value: In case of success, the exact length of rdata, SIZE_MAX - * otherwise. - */ -size_t -mpd_qexport_u32(uint32_t **rdata, size_t rlen, uint32_t rbase, - const mpd_t *src, uint32_t *status) -{ - MPD_NEW_STATIC(tsrc,0,0,0,0); - int alloc = 0; /* rdata == NULL */ - size_t n; - - if (mpd_isspecial(src) || !_mpd_isint(src)) { - *status |= MPD_Invalid_operation; - return SIZE_MAX; - } - - if (*rdata == NULL) { - rlen = mpd_sizeinbase(src, rbase); - if (rlen == SIZE_MAX) { - *status |= MPD_Invalid_operation; - return SIZE_MAX; - } - *rdata = mpd_alloc(rlen, sizeof **rdata); - if (*rdata == NULL) { - goto malloc_error; - } - alloc = 1; - } - - if (mpd_iszero(src)) { - **rdata = 0; - return 1; - } - - if (src->exp >= 0) { - if (!mpd_qshiftl(&tsrc, src, src->exp, status)) { - goto malloc_error; - } - } - else { - if (mpd_qshiftr(&tsrc, src, -src->exp, status) == MPD_UINT_MAX) { - goto malloc_error; - } - } - -#ifdef CONFIG_64 - n = _baseconv_to_smaller(rdata, rlen, rbase, - tsrc.data, tsrc.len, MPD_RADIX); -#else - if (rbase == MPD_RADIX) { - n = _copy_equal_base(rdata, rlen, tsrc.data, tsrc.len); - } - else if (rbase < MPD_RADIX) { - n = _baseconv_to_smaller(rdata, rlen, rbase, - tsrc.data, tsrc.len, MPD_RADIX); - } - else { - n = _baseconv_to_larger(rdata, rlen, rbase, - tsrc.data, tsrc.len, MPD_RADIX); - } -#endif - - if (n == SIZE_MAX) { - goto malloc_error; - } - - -out: - mpd_del(&tsrc); - return n; - -malloc_error: - if (alloc) { - mpd_free(*rdata); - *rdata = NULL; - } - n = SIZE_MAX; - *status |= MPD_Malloc_error; - goto out; -} - - -/* - * Converts a multiprecision integer with base <= UINT16_MAX+1 to an mpd_t. - * The least significant word of the source is srcdata[0]. - */ -void -mpd_qimport_u16(mpd_t *result, - const uint16_t *srcdata, size_t srclen, - uint8_t srcsign, uint32_t srcbase, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_uint_t *usrc; /* uint16_t src copied to an mpd_uint_t array */ - mpd_ssize_t rlen; /* length of the result */ - size_t n; - - assert(srclen > 0); - assert(srcbase <= (1U<<16)); - - rlen = _mpd_importsize(srclen, srcbase); - if (rlen == MPD_SSIZE_MAX) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - usrc = mpd_alloc((mpd_size_t)srclen, sizeof *usrc); - if (usrc == NULL) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - for (n = 0; n < srclen; n++) { - usrc[n] = srcdata[n]; - } - - if (!mpd_qresize(result, rlen, status)) { - goto finish; - } - - n = _coeff_from_u16(result, rlen, usrc, srclen, srcbase, status); - if (n == SIZE_MAX) { - goto finish; - } - - mpd_set_flags(result, srcsign); - result->exp = 0; - result->len = n; - mpd_setdigits(result); - - mpd_qresize(result, result->len, status); - mpd_qfinalize(result, ctx, status); - - -finish: - mpd_free(usrc); -} - -/* - * Converts a multiprecision integer with base <= UINT32_MAX to an mpd_t. - * The least significant word of the source is srcdata[0]. - */ -void -mpd_qimport_u32(mpd_t *result, - const uint32_t *srcdata, size_t srclen, - uint8_t srcsign, uint32_t srcbase, - const mpd_context_t *ctx, uint32_t *status) -{ - mpd_ssize_t rlen; /* length of the result */ - size_t n; - - assert(srclen > 0); - - rlen = _mpd_importsize(srclen, srcbase); - if (rlen == MPD_SSIZE_MAX) { - mpd_seterror(result, MPD_Invalid_operation, status); - return; - } - - if (!mpd_qresize(result, rlen, status)) { - return; - } - -#ifdef CONFIG_64 - n = _coeff_from_smaller_base(result, rlen, MPD_RADIX, - srcdata, srclen, srcbase, - status); -#else - if (srcbase == MPD_RADIX) { - if (!mpd_qresize(result, srclen, status)) { - return; - } - memcpy(result->data, srcdata, srclen * (sizeof *srcdata)); - n = srclen; - } - else if (srcbase < MPD_RADIX) { - n = _coeff_from_smaller_base(result, rlen, MPD_RADIX, - srcdata, srclen, srcbase, - status); - } - else { - mpd_uint_t *usrc = mpd_alloc((mpd_size_t)srclen, sizeof *usrc); - if (usrc == NULL) { - mpd_seterror(result, MPD_Malloc_error, status); - return; - } - for (n = 0; n < srclen; n++) { - usrc[n] = srcdata[n]; - } - - n = _coeff_from_larger_base(result, rlen, MPD_RADIX, - usrc, (mpd_ssize_t)srclen, srcbase, - status); - mpd_free(usrc); - } -#endif - - if (n == SIZE_MAX) { - return; - } - - mpd_set_flags(result, srcsign); - result->exp = 0; - result->len = n; - mpd_setdigits(result); - - mpd_qresize(result, result->len, status); - mpd_qfinalize(result, ctx, status); -} - - -/******************************************************************************/ -/* From triple */ -/******************************************************************************/ - -#if defined(CONFIG_64) && defined(__SIZEOF_INT128__) -static mpd_ssize_t -_set_coeff(uint64_t data[3], uint64_t hi, uint64_t lo) -{ - __uint128_t d = ((__uint128_t)hi << 64) + lo; - __uint128_t q, r; - - q = d / MPD_RADIX; - r = d % MPD_RADIX; - data[0] = (uint64_t)r; - d = q; - - q = d / MPD_RADIX; - r = d % MPD_RADIX; - data[1] = (uint64_t)r; - d = q; - - q = d / MPD_RADIX; - r = d % MPD_RADIX; - data[2] = (uint64_t)r; - - if (q != 0) { - abort(); /* GCOV_NOT_REACHED */ - } - - return data[2] != 0 ? 3 : (data[1] != 0 ? 2 : 1); -} -#else -static size_t -_uint_from_u16(mpd_uint_t *w, mpd_ssize_t wlen, const uint16_t *u, size_t ulen) -{ - const mpd_uint_t ubase = 1U<<16; - mpd_ssize_t n = 0; - mpd_uint_t carry; - - assert(wlen > 0 && ulen > 0); - - w[n++] = u[--ulen]; - while (--ulen != SIZE_MAX) { - carry = _mpd_shortmul_c(w, w, n, ubase); - if (carry) { - if (n >= wlen) { - abort(); /* GCOV_NOT_REACHED */ - } - w[n++] = carry; - } - carry = _mpd_shortadd(w, n, u[ulen]); - if (carry) { - if (n >= wlen) { - abort(); /* GCOV_NOT_REACHED */ - } - w[n++] = carry; - } - } - - return n; -} - -static mpd_ssize_t -_set_coeff(mpd_uint_t *data, mpd_ssize_t len, uint64_t hi, uint64_t lo) -{ - uint16_t u16[8] = {0}; - - u16[7] = (uint16_t)((hi & 0xFFFF000000000000ULL) >> 48); - u16[6] = (uint16_t)((hi & 0x0000FFFF00000000ULL) >> 32); - u16[5] = (uint16_t)((hi & 0x00000000FFFF0000ULL) >> 16); - u16[4] = (uint16_t) (hi & 0x000000000000FFFFULL); - - u16[3] = (uint16_t)((lo & 0xFFFF000000000000ULL) >> 48); - u16[2] = (uint16_t)((lo & 0x0000FFFF00000000ULL) >> 32); - u16[1] = (uint16_t)((lo & 0x00000000FFFF0000ULL) >> 16); - u16[0] = (uint16_t) (lo & 0x000000000000FFFFULL); - - return (mpd_ssize_t)_uint_from_u16(data, len, u16, 8); -} -#endif - -static int -_set_uint128_coeff_exp(mpd_t *result, uint64_t hi, uint64_t lo, mpd_ssize_t exp) -{ - mpd_uint_t data[5] = {0}; - uint32_t status = 0; - mpd_ssize_t len; - -#if defined(CONFIG_64) && defined(__SIZEOF_INT128__) - len = _set_coeff(data, hi, lo); -#else - len = _set_coeff(data, 5, hi, lo); -#endif - - if (!mpd_qresize(result, len, &status)) { - return -1; - } - - for (mpd_ssize_t i = 0; i < len; i++) { - result->data[i] = data[i]; - } - - result->exp = exp; - result->len = len; - mpd_setdigits(result); - - return 0; -} - -int -mpd_from_uint128_triple(mpd_t *result, const mpd_uint128_triple_t *triple, uint32_t *status) -{ - static const mpd_context_t maxcontext = { - .prec=MPD_MAX_PREC, - .emax=MPD_MAX_EMAX, - .emin=MPD_MIN_EMIN, - .round=MPD_ROUND_HALF_EVEN, - .traps=MPD_Traps, - .status=0, - .newtrap=0, - .clamp=0, - .allcr=1, - }; - const enum mpd_triple_class tag = triple->tag; - const uint8_t sign = triple->sign; - const uint64_t hi = triple->hi; - const uint64_t lo = triple->lo; - mpd_ssize_t exp; - -#ifdef CONFIG_32 - if (triple->exp < MPD_SSIZE_MIN || triple->exp > MPD_SSIZE_MAX) { - goto conversion_error; - } -#endif - exp = (mpd_ssize_t)triple->exp; - - switch (tag) { - case MPD_TRIPLE_QNAN: case MPD_TRIPLE_SNAN: { - if (sign > 1 || exp != 0) { - goto conversion_error; - } - - const uint8_t flags = tag == MPD_TRIPLE_QNAN ? MPD_NAN : MPD_SNAN; - mpd_setspecial(result, sign, flags); - - if (hi == 0 && lo == 0) { /* no payload */ - return 0; - } - - if (_set_uint128_coeff_exp(result, hi, lo, exp) < 0) { - goto malloc_error; - } - - return 0; - } - - case MPD_TRIPLE_INF: { - if (sign > 1 || hi != 0 || lo != 0 || exp != 0) { - goto conversion_error; - } - - mpd_setspecial(result, sign, MPD_INF); - - return 0; - } - - case MPD_TRIPLE_NORMAL: { - if (sign > 1) { - goto conversion_error; - } - - const uint8_t flags = sign ? MPD_NEG : MPD_POS; - mpd_set_flags(result, flags); - - if (exp > MPD_EXP_INF) { - exp = MPD_EXP_INF; - } - if (exp == MPD_SSIZE_MIN) { - exp = MPD_SSIZE_MIN+1; - } - - if (_set_uint128_coeff_exp(result, hi, lo, exp) < 0) { - goto malloc_error; - } - - uint32_t workstatus = 0; - mpd_qfinalize(result, &maxcontext, &workstatus); - if (workstatus & (MPD_Inexact|MPD_Rounded|MPD_Clamped)) { - goto conversion_error; - } - - return 0; - } - - default: - goto conversion_error; - } - -conversion_error: - mpd_seterror(result, MPD_Conversion_syntax, status); - return -1; - -malloc_error: - mpd_seterror(result, MPD_Malloc_error, status); - return -1; -} - - -/******************************************************************************/ -/* As triple */ -/******************************************************************************/ - -#if defined(CONFIG_64) && defined(__SIZEOF_INT128__) -static void -_get_coeff(uint64_t *hi, uint64_t *lo, const mpd_t *a) -{ - __uint128_t u128 = 0; - - switch (a->len) { - case 3: - u128 = a->data[2]; /* fall through */ - case 2: - u128 = u128 * MPD_RADIX + a->data[1]; /* fall through */ - case 1: - u128 = u128 * MPD_RADIX + a->data[0]; - break; - default: - abort(); /* GCOV_NOT_REACHED */ - } - - *hi = u128 >> 64; - *lo = (uint64_t)u128; -} -#else -static size_t -_uint_to_u16(uint16_t w[8], mpd_uint_t *u, mpd_ssize_t ulen) -{ - const mpd_uint_t wbase = 1U<<16; - size_t n = 0; - - assert(ulen > 0); - - do { - if (n >= 8) { - abort(); /* GCOV_NOT_REACHED */ - } - w[n++] = (uint16_t)_mpd_shortdiv(u, u, ulen, wbase); - /* ulen is at least 1. u[ulen-1] can only be zero if ulen == 1. */ - ulen = _mpd_real_size(u, ulen); - - } while (u[ulen-1] != 0); - - return n; -} - -static void -_get_coeff(uint64_t *hi, uint64_t *lo, const mpd_t *a) -{ - uint16_t u16[8] = {0}; - mpd_uint_t data[5] = {0}; - - switch (a->len) { - case 5: - data[4] = a->data[4]; /* fall through */ - case 4: - data[3] = a->data[3]; /* fall through */ - case 3: - data[2] = a->data[2]; /* fall through */ - case 2: - data[1] = a->data[1]; /* fall through */ - case 1: - data[0] = a->data[0]; - break; - default: - abort(); /* GCOV_NOT_REACHED */ - } - - _uint_to_u16(u16, data, a->len); - - *hi = (uint64_t)u16[7] << 48; - *hi |= (uint64_t)u16[6] << 32; - *hi |= (uint64_t)u16[5] << 16; - *hi |= (uint64_t)u16[4]; - - *lo = (uint64_t)u16[3] << 48; - *lo |= (uint64_t)u16[2] << 32; - *lo |= (uint64_t)u16[1] << 16; - *lo |= (uint64_t)u16[0]; -} -#endif - -static enum mpd_triple_class -_coeff_as_uint128(uint64_t *hi, uint64_t *lo, const mpd_t *a) -{ -#ifdef CONFIG_64 - static mpd_uint_t uint128_max_data[3] = { 3374607431768211455ULL, 4028236692093846346ULL, 3ULL }; - static const mpd_t uint128_max = { MPD_STATIC|MPD_CONST_DATA, 0, 39, 3, 3, uint128_max_data }; -#else - static mpd_uint_t uint128_max_data[5] = { 768211455U, 374607431U, 938463463U, 282366920U, 340U }; - static const mpd_t uint128_max = { MPD_STATIC|MPD_CONST_DATA, 0, 39, 5, 5, uint128_max_data }; -#endif - enum mpd_triple_class ret = MPD_TRIPLE_NORMAL; - uint32_t status = 0; - mpd_t coeff; - - *hi = *lo = 0ULL; - - if (mpd_isspecial(a)) { - if (mpd_isinfinite(a)) { - return MPD_TRIPLE_INF; - } - - ret = mpd_isqnan(a) ? MPD_TRIPLE_QNAN : MPD_TRIPLE_SNAN; - if (a->len == 0) { /* no payload */ - return ret; - } - } - else if (mpd_iszero(a)) { - return ret; - } - - _mpd_copy_shared(&coeff, a); - mpd_set_flags(&coeff, 0); - coeff.exp = 0; - - if (mpd_qcmp(&coeff, &uint128_max, &status) > 0) { - return MPD_TRIPLE_ERROR; - } - - _get_coeff(hi, lo, &coeff); - return ret; -} - -mpd_uint128_triple_t -mpd_as_uint128_triple(const mpd_t *a) -{ - mpd_uint128_triple_t triple = { MPD_TRIPLE_ERROR, 0, 0, 0, 0 }; - - triple.tag = _coeff_as_uint128(&triple.hi, &triple.lo, a); - if (triple.tag == MPD_TRIPLE_ERROR) { - return triple; - } - - triple.sign = !!mpd_isnegative(a); - if (triple.tag == MPD_TRIPLE_NORMAL) { - triple.exp = a->exp; - } - - return triple; -} diff --git a/Modules/_decimal/libmpdec/mpdecimal.h b/Modules/_decimal/libmpdec/mpdecimal.h deleted file mode 100644 index 24c280b00ebcd0..00000000000000 --- a/Modules/_decimal/libmpdec/mpdecimal.h +++ /dev/null @@ -1,847 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_MPDECIMAL_H_ -#define LIBMPDEC_MPDECIMAL_H_ - - -#ifndef _MSC_VER - #include "pyconfig.h" -#endif - -#ifdef __cplusplus - #include <cinttypes> - #include <climits> - #include <cstdint> - #include <cstdio> - #include <cstdlib> - #define MPD_UINT8_C(x) (static_cast<uint8_t>(x)) -extern "C" { -#else - #include <inttypes.h> - #include <limits.h> - #include <stdint.h> - #include <stdio.h> - #include <stdlib.h> - #define MPD_UINT8_C(x) ((uint8_t)x) -#endif - - -#if (defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)) && \ - defined(__GNUC__) && __GNUC__ >= 4 && !defined(__INTEL_COMPILER) - #define MPD_PRAGMA(x) _Pragma(x) - #define MPD_HIDE_SYMBOLS_START "GCC visibility push(hidden)" - #define MPD_HIDE_SYMBOLS_END "GCC visibility pop" -#else - #define MPD_PRAGMA(x) - #define MPD_HIDE_SYMBOLS_START - #define MPD_HIDE_SYMBOLS_END -#endif - -#if defined(_MSC_VER) - #define EXTINLINE extern inline -#else - #define EXTINLINE -#endif - - -/* This header file is internal for the purpose of building _decimal.so. - * All symbols should have local scope in the DSO. */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -/******************************************************************************/ -/* Version */ -/******************************************************************************/ - -#define MPD_MAJOR_VERSION 2 -#define MPD_MINOR_VERSION 5 -#define MPD_MICRO_VERSION 1 - -#define MPD_VERSION "2.5.1" - -#define MPD_VERSION_HEX ((MPD_MAJOR_VERSION << 24) | \ - (MPD_MINOR_VERSION << 16) | \ - (MPD_MICRO_VERSION << 8)) - -const char *mpd_version(void); - - -/******************************************************************************/ -/* Configuration */ -/******************************************************************************/ - -#if defined(UNIVERSAL) - #if defined(CONFIG_64) || defined(CONFIG_32) - #error "cannot use CONFIG_64 or CONFIG_32 with UNIVERSAL." - #endif - #if defined(__ppc__) - #define CONFIG_32 - #define ANSI - #elif defined(__ppc64__) - #define CONFIG_64 - #define ANSI - #elif defined(__i386__) - #define CONFIG_32 - #define ANSI - #elif defined(__x86_64__) - #define CONFIG_64 - #define ASM - #elif defined(__arm64__) - #define CONFIG_64 - #define ANSI - #else - #error "unknown architecture for universal build." - #endif -#endif - - -/* BEGIN CONFIG_64 */ -#if defined(CONFIG_64) -/* types for modular and base arithmetic */ -#define MPD_UINT_MAX UINT64_MAX -#define MPD_BITS_PER_UINT 64 -typedef uint64_t mpd_uint_t; /* unsigned mod type */ - -#define MPD_SIZE_MAX SIZE_MAX -typedef size_t mpd_size_t; /* unsigned size type */ - -/* type for exp, digits, len, prec */ -#define MPD_SSIZE_MAX INT64_MAX -#define MPD_SSIZE_MIN INT64_MIN -typedef int64_t mpd_ssize_t; -#define _mpd_strtossize strtoll - -/* decimal arithmetic */ -#define MPD_RADIX 10000000000000000000ULL /* 10**19 */ -#define MPD_RDIGITS 19 -#define MPD_MAX_POW10 19 -#define MPD_EXPDIGITS 19 /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */ - -#define MPD_MAXTRANSFORM_2N 4294967296ULL /* 2**32 */ -#define MPD_MAX_PREC 999999999999999999LL -#define MPD_MAX_PREC_LOG2 64 -#define MPD_ELIMIT 1000000000000000000LL -#define MPD_MAX_EMAX 999999999999999999LL /* ELIMIT-1 */ -#define MPD_MIN_EMIN (-999999999999999999LL) /* -EMAX */ -#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1)) -#define MPD_EXP_INF 2000000000000000001LL -#define MPD_EXP_CLAMP (-4000000000000000001LL) -#define MPD_MAXIMPORT 105263157894736842L /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */ -#define MPD_IEEE_CONTEXT_MAX_BITS 512 /* 16*(log2(MPD_MAX_EMAX / 3)-3) */ - -/* conversion specifiers */ -#define PRI_mpd_uint_t PRIu64 -#define PRI_mpd_ssize_t PRIi64 -/* END CONFIG_64 */ - - -/* BEGIN CONFIG_32 */ -#elif defined(CONFIG_32) -/* types for modular and base arithmetic */ -#define MPD_UINT_MAX UINT32_MAX -#define MPD_BITS_PER_UINT 32 -typedef uint32_t mpd_uint_t; /* unsigned mod type */ - -#ifndef LEGACY_COMPILER -#define MPD_UUINT_MAX UINT64_MAX -typedef uint64_t mpd_uuint_t; /* double width unsigned mod type */ -#endif - -#define MPD_SIZE_MAX SIZE_MAX -typedef size_t mpd_size_t; /* unsigned size type */ - -/* type for dec->len, dec->exp, ctx->prec */ -#define MPD_SSIZE_MAX INT32_MAX -#define MPD_SSIZE_MIN INT32_MIN -typedef int32_t mpd_ssize_t; -#define _mpd_strtossize strtol - -/* decimal arithmetic */ -#define MPD_RADIX 1000000000UL /* 10**9 */ -#define MPD_RDIGITS 9 -#define MPD_MAX_POW10 9 -#define MPD_EXPDIGITS 10 /* MPD_EXPDIGITS <= MPD_RDIGITS+1 */ - -#define MPD_MAXTRANSFORM_2N 33554432UL /* 2**25 */ -#define MPD_MAX_PREC 425000000L -#define MPD_MAX_PREC_LOG2 32 -#define MPD_ELIMIT 425000001L -#define MPD_MAX_EMAX 425000000L /* ELIMIT-1 */ -#define MPD_MIN_EMIN (-425000000L) /* -EMAX */ -#define MPD_MIN_ETINY (MPD_MIN_EMIN-(MPD_MAX_PREC-1)) -#define MPD_EXP_INF 1000000001L /* allows for emax=999999999 in the tests */ -#define MPD_EXP_CLAMP (-2000000001L) /* allows for emin=-999999999 in the tests */ -#define MPD_MAXIMPORT 94444445L /* ceil((2*MPD_MAX_PREC)/MPD_RDIGITS) */ -#define MPD_IEEE_CONTEXT_MAX_BITS 256 /* 16*(log2(MPD_MAX_EMAX / 3)-3) */ - -/* conversion specifiers */ -#define PRI_mpd_uint_t PRIu32 -#define PRI_mpd_ssize_t PRIi32 -/* END CONFIG_32 */ - -#else - #error "define CONFIG_64 or CONFIG_32" -#endif -/* END CONFIG */ - - -#if MPD_SIZE_MAX != MPD_UINT_MAX - #error "unsupported platform: need mpd_size_t == mpd_uint_t" -#endif - - -/******************************************************************************/ -/* Context */ -/******************************************************************************/ - -enum { - MPD_ROUND_UP, /* round away from 0 */ - MPD_ROUND_DOWN, /* round toward 0 (truncate) */ - MPD_ROUND_CEILING, /* round toward +infinity */ - MPD_ROUND_FLOOR, /* round toward -infinity */ - MPD_ROUND_HALF_UP, /* 0.5 is rounded up */ - MPD_ROUND_HALF_DOWN, /* 0.5 is rounded down */ - MPD_ROUND_HALF_EVEN, /* 0.5 is rounded to even */ - MPD_ROUND_05UP, /* round zero or five away from 0 */ - MPD_ROUND_TRUNC, /* truncate, but set infinity */ - MPD_ROUND_GUARD -}; - -enum { MPD_CLAMP_DEFAULT, MPD_CLAMP_IEEE_754, MPD_CLAMP_GUARD }; - -extern const char * const mpd_round_string[MPD_ROUND_GUARD]; -extern const char * const mpd_clamp_string[MPD_CLAMP_GUARD]; - - -typedef struct mpd_context_t { - mpd_ssize_t prec; /* precision */ - mpd_ssize_t emax; /* max positive exp */ - mpd_ssize_t emin; /* min negative exp */ - uint32_t traps; /* status events that should be trapped */ - uint32_t status; /* status flags */ - uint32_t newtrap; /* set by mpd_addstatus_raise() */ - int round; /* rounding mode */ - int clamp; /* clamp mode */ - int allcr; /* all functions correctly rounded */ -} mpd_context_t; - - -/* Status flags */ -#define MPD_Clamped 0x00000001U -#define MPD_Conversion_syntax 0x00000002U -#define MPD_Division_by_zero 0x00000004U -#define MPD_Division_impossible 0x00000008U -#define MPD_Division_undefined 0x00000010U -#define MPD_Fpu_error 0x00000020U -#define MPD_Inexact 0x00000040U -#define MPD_Invalid_context 0x00000080U -#define MPD_Invalid_operation 0x00000100U -#define MPD_Malloc_error 0x00000200U -#define MPD_Not_implemented 0x00000400U -#define MPD_Overflow 0x00000800U -#define MPD_Rounded 0x00001000U -#define MPD_Subnormal 0x00002000U -#define MPD_Underflow 0x00004000U -#define MPD_Max_status (0x00008000U-1U) - -/* Conditions that result in an IEEE 754 exception */ -#define MPD_IEEE_Invalid_operation (MPD_Conversion_syntax | \ - MPD_Division_impossible | \ - MPD_Division_undefined | \ - MPD_Fpu_error | \ - MPD_Invalid_context | \ - MPD_Invalid_operation | \ - MPD_Malloc_error) \ - -/* Errors that require the result of an operation to be set to NaN */ -#define MPD_Errors (MPD_IEEE_Invalid_operation | \ - MPD_Division_by_zero) - -/* Default traps */ -#define MPD_Traps (MPD_IEEE_Invalid_operation | \ - MPD_Division_by_zero | \ - MPD_Overflow | \ - MPD_Underflow) - -/* Official name */ -#define MPD_Insufficient_storage MPD_Malloc_error - -/* IEEE 754 interchange format contexts */ -#define MPD_DECIMAL32 32 -#define MPD_DECIMAL64 64 -#define MPD_DECIMAL128 128 - - -#define MPD_MINALLOC_MIN 2 -#define MPD_MINALLOC_MAX 64 -extern mpd_ssize_t MPD_MINALLOC; -extern void (* mpd_traphandler)(mpd_context_t *); -void mpd_dflt_traphandler(mpd_context_t *); - -void mpd_setminalloc(mpd_ssize_t n); -void mpd_init(mpd_context_t *ctx, mpd_ssize_t prec); - -void mpd_maxcontext(mpd_context_t *ctx); -void mpd_defaultcontext(mpd_context_t *ctx); -void mpd_basiccontext(mpd_context_t *ctx); -int mpd_ieee_context(mpd_context_t *ctx, int bits); - -mpd_ssize_t mpd_getprec(const mpd_context_t *ctx); -mpd_ssize_t mpd_getemax(const mpd_context_t *ctx); -mpd_ssize_t mpd_getemin(const mpd_context_t *ctx); -int mpd_getround(const mpd_context_t *ctx); -uint32_t mpd_gettraps(const mpd_context_t *ctx); -uint32_t mpd_getstatus(const mpd_context_t *ctx); -int mpd_getclamp(const mpd_context_t *ctx); -int mpd_getcr(const mpd_context_t *ctx); - -int mpd_qsetprec(mpd_context_t *ctx, mpd_ssize_t prec); -int mpd_qsetemax(mpd_context_t *ctx, mpd_ssize_t emax); -int mpd_qsetemin(mpd_context_t *ctx, mpd_ssize_t emin); -int mpd_qsetround(mpd_context_t *ctx, int newround); -int mpd_qsettraps(mpd_context_t *ctx, uint32_t flags); -int mpd_qsetstatus(mpd_context_t *ctx, uint32_t flags); -int mpd_qsetclamp(mpd_context_t *ctx, int c); -int mpd_qsetcr(mpd_context_t *ctx, int c); -void mpd_addstatus_raise(mpd_context_t *ctx, uint32_t flags); - - -/******************************************************************************/ -/* Decimal Arithmetic */ -/******************************************************************************/ - -/* mpd_t flags */ -#define MPD_POS MPD_UINT8_C(0) -#define MPD_NEG MPD_UINT8_C(1) -#define MPD_INF MPD_UINT8_C(2) -#define MPD_NAN MPD_UINT8_C(4) -#define MPD_SNAN MPD_UINT8_C(8) -#define MPD_SPECIAL (MPD_INF|MPD_NAN|MPD_SNAN) -#define MPD_STATIC MPD_UINT8_C(16) -#define MPD_STATIC_DATA MPD_UINT8_C(32) -#define MPD_SHARED_DATA MPD_UINT8_C(64) -#define MPD_CONST_DATA MPD_UINT8_C(128) -#define MPD_DATAFLAGS (MPD_STATIC_DATA|MPD_SHARED_DATA|MPD_CONST_DATA) - -/* mpd_t */ -typedef struct mpd_t { - uint8_t flags; - mpd_ssize_t exp; - mpd_ssize_t digits; - mpd_ssize_t len; - mpd_ssize_t alloc; - mpd_uint_t *data; -} mpd_t; - - -/******************************************************************************/ -/* Triple */ -/******************************************************************************/ - -/* status cases for getting a triple */ -enum mpd_triple_class { - MPD_TRIPLE_NORMAL, - MPD_TRIPLE_INF, - MPD_TRIPLE_QNAN, - MPD_TRIPLE_SNAN, - MPD_TRIPLE_ERROR, -}; - -typedef struct { - enum mpd_triple_class tag; - uint8_t sign; - uint64_t hi; - uint64_t lo; - int64_t exp; -} mpd_uint128_triple_t; - -int mpd_from_uint128_triple(mpd_t *result, const mpd_uint128_triple_t *triple, uint32_t *status); -mpd_uint128_triple_t mpd_as_uint128_triple(const mpd_t *a); - - -/******************************************************************************/ -/* Quiet, thread-safe functions */ -/******************************************************************************/ - -/* format specification */ -typedef struct mpd_spec_t { - mpd_ssize_t min_width; /* minimum field width */ - mpd_ssize_t prec; /* fraction digits or significant digits */ - char type; /* conversion specifier */ - char align; /* alignment */ - char sign; /* sign printing/alignment */ - char fill[5]; /* fill character */ - const char *dot; /* decimal point */ - const char *sep; /* thousands separator */ - const char *grouping; /* grouping of digits */ -} mpd_spec_t; - -/* output to a string */ -char *mpd_to_sci(const mpd_t *dec, int fmt); -char *mpd_to_eng(const mpd_t *dec, int fmt); -mpd_ssize_t mpd_to_sci_size(char **res, const mpd_t *dec, int fmt); -mpd_ssize_t mpd_to_eng_size(char **res, const mpd_t *dec, int fmt); -int mpd_validate_lconv(mpd_spec_t *spec); -int mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps); -char *mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec, const mpd_context_t *ctx, uint32_t *status); -char *mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx, uint32_t *status); - -#define MPD_NUM_FLAGS 15 -#define MPD_MAX_FLAG_STRING 208 -#define MPD_MAX_FLAG_LIST (MPD_MAX_FLAG_STRING+18) -#define MPD_MAX_SIGNAL_LIST 121 -int mpd_snprint_flags(char *dest, int nmemb, uint32_t flags); -int mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[]); -int mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[]); - -/* output to a file */ -void mpd_fprint(FILE *file, const mpd_t *dec); -void mpd_print(const mpd_t *dec); - -/* assignment from a string */ -void mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx, uint32_t *status); -void mpd_qset_string_exact(mpd_t *dec, const char *s, uint32_t *status); - -/* set to NaN with error flags */ -void mpd_seterror(mpd_t *result, uint32_t flags, uint32_t *status); -/* set a special with sign and type */ -void mpd_setspecial(mpd_t *result, uint8_t sign, uint8_t type); -/* set coefficient to zero or all nines */ -void mpd_zerocoeff(mpd_t *result); -void mpd_qmaxcoeff(mpd_t *result, const mpd_context_t *ctx, uint32_t *status); - -/* quietly assign a C integer type to an mpd_t */ -void mpd_qset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status); -#ifndef LEGACY_COMPILER -void mpd_qset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qset_i64_exact(mpd_t *result, int64_t a, uint32_t *status); -void mpd_qset_u64_exact(mpd_t *result, uint64_t a, uint32_t *status); -#endif - -/* quietly assign a C integer type to an mpd_t with a static coefficient */ -void mpd_qsset_ssize(mpd_t *result, mpd_ssize_t a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsset_i32(mpd_t *result, int32_t a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsset_uint(mpd_t *result, mpd_uint_t a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsset_u32(mpd_t *result, uint32_t a, const mpd_context_t *ctx, uint32_t *status); - -/* quietly get a C integer type from an mpd_t */ -mpd_ssize_t mpd_qget_ssize(const mpd_t *dec, uint32_t *status); -mpd_uint_t mpd_qget_uint(const mpd_t *dec, uint32_t *status); -mpd_uint_t mpd_qabs_uint(const mpd_t *dec, uint32_t *status); - -int32_t mpd_qget_i32(const mpd_t *dec, uint32_t *status); -uint32_t mpd_qget_u32(const mpd_t *dec, uint32_t *status); -#ifndef LEGACY_COMPILER -int64_t mpd_qget_i64(const mpd_t *dec, uint32_t *status); -uint64_t mpd_qget_u64(const mpd_t *dec, uint32_t *status); -#endif - -/* quiet functions */ -int mpd_qcheck_nan(mpd_t *nanresult, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -int mpd_qcheck_nans(mpd_t *nanresult, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qfinalize(mpd_t *result, const mpd_context_t *ctx, uint32_t *status); - -const char *mpd_class(const mpd_t *a, const mpd_context_t *ctx); - -int mpd_qcopy(mpd_t *result, const mpd_t *a, uint32_t *status); -int mpd_qcopy_cxx(mpd_t *result, const mpd_t *a); -mpd_t *mpd_qncopy(const mpd_t *a); -int mpd_qcopy_abs(mpd_t *result, const mpd_t *a, uint32_t *status); -int mpd_qcopy_negate(mpd_t *result, const mpd_t *a, uint32_t *status); -int mpd_qcopy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, uint32_t *status); - -void mpd_qand(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qinvert(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qlogb(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qscaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qxor(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -int mpd_same_quantum(const mpd_t *a, const mpd_t *b); - -void mpd_qrotate(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -int mpd_qshiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status); -mpd_uint_t mpd_qshiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, uint32_t *status); -mpd_uint_t mpd_qshiftr_inplace(mpd_t *result, mpd_ssize_t n); -void mpd_qshift(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qshiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, const mpd_context_t *ctx, uint32_t *status); - -int mpd_qcmp(const mpd_t *a, const mpd_t *b, uint32_t *status); -int mpd_qcompare(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -int mpd_qcompare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -int mpd_cmp_total(const mpd_t *a, const mpd_t *b); -int mpd_cmp_total_mag(const mpd_t *a, const mpd_t *b); -int mpd_compare_total(mpd_t *result, const mpd_t *a, const mpd_t *b); -int mpd_compare_total_mag(mpd_t *result, const mpd_t *a, const mpd_t *b); - -void mpd_qround_to_intx(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qround_to_int(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qtrunc(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qfloor(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qceil(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); - -void mpd_qabs(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmax(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmax_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmin(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmin_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qminus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qplus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qnext_minus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qnext_plus(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qnext_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qquantize(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qrescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status); -void mpd_qrescale_fmt(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, const mpd_context_t *ctx, uint32_t *status); -void mpd_qreduce(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qadd(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qadd_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qadd_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qadd_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qadd_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsub(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsub_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsub_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmul(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmul_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmul_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qfma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, const mpd_context_t *ctx, uint32_t *status); -void mpd_qdiv(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qdiv_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qdiv_i32(mpd_t *result, const mpd_t *a, int32_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qdiv_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qdiv_u32(mpd_t *result, const mpd_t *a, uint32_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qdivint(mpd_t *q, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qrem(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qrem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qdivmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qpow(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_context_t *ctx, uint32_t *status); -void mpd_qpowmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, const mpd_context_t *ctx, uint32_t *status); -void mpd_qexp(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qln10(mpd_t *result, mpd_ssize_t prec, uint32_t *status); -void mpd_qln(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qlog10(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsqrt(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qinvroot(mpd_t *result, const mpd_t *a, const mpd_context_t *ctx, uint32_t *status); - -#ifndef LEGACY_COMPILER -void mpd_qadd_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qadd_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsub_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsub_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmul_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qmul_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qdiv_i64(mpd_t *result, const mpd_t *a, int64_t b, const mpd_context_t *ctx, uint32_t *status); -void mpd_qdiv_u64(mpd_t *result, const mpd_t *a, uint64_t b, const mpd_context_t *ctx, uint32_t *status); -#endif - - -size_t mpd_sizeinbase(const mpd_t *a, uint32_t base); -void mpd_qimport_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen, - uint8_t srcsign, uint32_t srcbase, - const mpd_context_t *ctx, uint32_t *status); -void mpd_qimport_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen, - uint8_t srcsign, uint32_t srcbase, - const mpd_context_t *ctx, uint32_t *status); -size_t mpd_qexport_u16(uint16_t **rdata, size_t rlen, uint32_t base, - const mpd_t *src, uint32_t *status); -size_t mpd_qexport_u32(uint32_t **rdata, size_t rlen, uint32_t base, - const mpd_t *src, uint32_t *status); - - -/******************************************************************************/ -/* Signalling functions */ -/******************************************************************************/ - -char *mpd_format(const mpd_t *dec, const char *fmt, mpd_context_t *ctx); -void mpd_import_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx); -void mpd_import_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen, uint8_t srcsign, uint32_t base, mpd_context_t *ctx); -size_t mpd_export_u16(uint16_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx); -size_t mpd_export_u32(uint32_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, mpd_context_t *ctx); -void mpd_finalize(mpd_t *result, mpd_context_t *ctx); -int mpd_check_nan(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -int mpd_check_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_set_string(mpd_t *result, const char *s, mpd_context_t *ctx); -void mpd_maxcoeff(mpd_t *result, mpd_context_t *ctx); -void mpd_sset_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx); -void mpd_sset_i32(mpd_t *result, int32_t a, mpd_context_t *ctx); -void mpd_sset_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx); -void mpd_sset_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx); -void mpd_set_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx); -void mpd_set_i32(mpd_t *result, int32_t a, mpd_context_t *ctx); -void mpd_set_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx); -void mpd_set_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx); -#ifndef LEGACY_COMPILER -void mpd_set_i64(mpd_t *result, int64_t a, mpd_context_t *ctx); -void mpd_set_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx); -#endif -mpd_ssize_t mpd_get_ssize(const mpd_t *a, mpd_context_t *ctx); -mpd_uint_t mpd_get_uint(const mpd_t *a, mpd_context_t *ctx); -mpd_uint_t mpd_abs_uint(const mpd_t *a, mpd_context_t *ctx); -int32_t mpd_get_i32(const mpd_t *a, mpd_context_t *ctx); -uint32_t mpd_get_u32(const mpd_t *a, mpd_context_t *ctx); -#ifndef LEGACY_COMPILER -int64_t mpd_get_i64(const mpd_t *a, mpd_context_t *ctx); -uint64_t mpd_get_u64(const mpd_t *a, mpd_context_t *ctx); -#endif -void mpd_and(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_copy(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_canonical(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_copy_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_copy_negate(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_copy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_invert(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_logb(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_or(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_rotate(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_scaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_shiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx); -mpd_uint_t mpd_shiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx); -void mpd_shiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx); -void mpd_shift(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_xor(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -int mpd_cmp(const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -int mpd_compare(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -int mpd_compare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_add(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_add_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx); -void mpd_add_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx); -void mpd_add_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx); -void mpd_add_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx); -void mpd_sub(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_sub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx); -void mpd_sub_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx); -void mpd_sub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx); -void mpd_sub_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx); -void mpd_div(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_div_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx); -void mpd_div_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx); -void mpd_div_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx); -void mpd_div_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx); -void mpd_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_divint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_exp(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_fma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, mpd_context_t *ctx); -void mpd_ln(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_log10(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_max(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_max_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_min(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_min_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_mul(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_mul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx); -void mpd_mul_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx); -void mpd_mul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx); -void mpd_mul_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx); -void mpd_next_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_next_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_next_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_pow(mpd_t *result, const mpd_t *base, const mpd_t *exp, mpd_context_t *ctx); -void mpd_powmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, mpd_context_t *ctx); -void mpd_quantize(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_rescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, mpd_context_t *ctx); -void mpd_reduce(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_rem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_rem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx); -void mpd_round_to_intx(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_round_to_int(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_trunc(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_floor(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_ceil(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_sqrt(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); -void mpd_invroot(mpd_t *result, const mpd_t *a, mpd_context_t *ctx); - -#ifndef LEGACY_COMPILER -void mpd_add_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx); -void mpd_add_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx); -void mpd_sub_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx); -void mpd_sub_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx); -void mpd_div_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx); -void mpd_div_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx); -void mpd_mul_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx); -void mpd_mul_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx); -#endif - - -/******************************************************************************/ -/* Configuration specific */ -/******************************************************************************/ - -#ifdef CONFIG_64 -void mpd_qsset_i64(mpd_t *result, int64_t a, const mpd_context_t *ctx, uint32_t *status); -void mpd_qsset_u64(mpd_t *result, uint64_t a, const mpd_context_t *ctx, uint32_t *status); -void mpd_sset_i64(mpd_t *result, int64_t a, mpd_context_t *ctx); -void mpd_sset_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx); -#endif - - -/******************************************************************************/ -/* Get attributes of a decimal */ -/******************************************************************************/ - -EXTINLINE mpd_ssize_t mpd_adjexp(const mpd_t *dec); -EXTINLINE mpd_ssize_t mpd_etiny(const mpd_context_t *ctx); -EXTINLINE mpd_ssize_t mpd_etop(const mpd_context_t *ctx); -EXTINLINE mpd_uint_t mpd_msword(const mpd_t *dec); -EXTINLINE int mpd_word_digits(mpd_uint_t word); -/* most significant digit of a word */ -EXTINLINE mpd_uint_t mpd_msd(mpd_uint_t word); -/* least significant digit of a word */ -EXTINLINE mpd_uint_t mpd_lsd(mpd_uint_t word); -/* coefficient size needed to store 'digits' */ -EXTINLINE mpd_ssize_t mpd_digits_to_size(mpd_ssize_t digits); -/* number of digits in the exponent, undefined for MPD_SSIZE_MIN */ -EXTINLINE int mpd_exp_digits(mpd_ssize_t exp); -EXTINLINE int mpd_iscanonical(const mpd_t *dec); -EXTINLINE int mpd_isfinite(const mpd_t *dec); -EXTINLINE int mpd_isinfinite(const mpd_t *dec); -EXTINLINE int mpd_isinteger(const mpd_t *dec); -EXTINLINE int mpd_isnan(const mpd_t *dec); -EXTINLINE int mpd_isnegative(const mpd_t *dec); -EXTINLINE int mpd_ispositive(const mpd_t *dec); -EXTINLINE int mpd_isqnan(const mpd_t *dec); -EXTINLINE int mpd_issigned(const mpd_t *dec); -EXTINLINE int mpd_issnan(const mpd_t *dec); -EXTINLINE int mpd_isspecial(const mpd_t *dec); -EXTINLINE int mpd_iszero(const mpd_t *dec); -/* undefined for special numbers */ -EXTINLINE int mpd_iszerocoeff(const mpd_t *dec); -EXTINLINE int mpd_isnormal(const mpd_t *dec, const mpd_context_t *ctx); -EXTINLINE int mpd_issubnormal(const mpd_t *dec, const mpd_context_t *ctx); -/* odd word */ -EXTINLINE int mpd_isoddword(mpd_uint_t word); -/* odd coefficient */ -EXTINLINE int mpd_isoddcoeff(const mpd_t *dec); -/* odd decimal, only defined for integers */ -int mpd_isodd(const mpd_t *dec); -/* even decimal, only defined for integers */ -int mpd_iseven(const mpd_t *dec); -/* 0 if dec is positive, 1 if dec is negative */ -EXTINLINE uint8_t mpd_sign(const mpd_t *dec); -/* 1 if dec is positive, -1 if dec is negative */ -EXTINLINE int mpd_arith_sign(const mpd_t *dec); -EXTINLINE long mpd_radix(void); -EXTINLINE int mpd_isdynamic(const mpd_t *dec); -EXTINLINE int mpd_isstatic(const mpd_t *dec); -EXTINLINE int mpd_isdynamic_data(const mpd_t *dec); -EXTINLINE int mpd_isstatic_data(const mpd_t *dec); -EXTINLINE int mpd_isshared_data(const mpd_t *dec); -EXTINLINE int mpd_isconst_data(const mpd_t *dec); -EXTINLINE mpd_ssize_t mpd_trail_zeros(const mpd_t *dec); - - -/******************************************************************************/ -/* Set attributes of a decimal */ -/******************************************************************************/ - -/* set number of decimal digits in the coefficient */ -EXTINLINE void mpd_setdigits(mpd_t *result); -EXTINLINE void mpd_set_sign(mpd_t *result, uint8_t sign); -/* copy sign from another decimal */ -EXTINLINE void mpd_signcpy(mpd_t *result, const mpd_t *a); -EXTINLINE void mpd_set_infinity(mpd_t *result); -EXTINLINE void mpd_set_qnan(mpd_t *result); -EXTINLINE void mpd_set_snan(mpd_t *result); -EXTINLINE void mpd_set_negative(mpd_t *result); -EXTINLINE void mpd_set_positive(mpd_t *result); -EXTINLINE void mpd_set_dynamic(mpd_t *result); -EXTINLINE void mpd_set_static(mpd_t *result); -EXTINLINE void mpd_set_dynamic_data(mpd_t *result); -EXTINLINE void mpd_set_static_data(mpd_t *result); -EXTINLINE void mpd_set_shared_data(mpd_t *result); -EXTINLINE void mpd_set_const_data(mpd_t *result); -EXTINLINE void mpd_clear_flags(mpd_t *result); -EXTINLINE void mpd_set_flags(mpd_t *result, uint8_t flags); -EXTINLINE void mpd_copy_flags(mpd_t *result, const mpd_t *a); - - -/******************************************************************************/ -/* Error Macros */ -/******************************************************************************/ - -#define mpd_err_fatal(...) \ - do {fprintf(stderr, "%s:%d: error: ", __FILE__, __LINE__); \ - fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); \ - abort(); \ - } while (0) -#define mpd_err_warn(...) \ - do {fprintf(stderr, "%s:%d: warning: ", __FILE__, __LINE__); \ - fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); \ - } while (0) - - -/******************************************************************************/ -/* Memory handling */ -/******************************************************************************/ - -extern void *(* mpd_mallocfunc)(size_t size); -extern void *(* mpd_callocfunc)(size_t nmemb, size_t size); -extern void *(* mpd_reallocfunc)(void *ptr, size_t size); -extern void (* mpd_free)(void *ptr); - -void *mpd_callocfunc_em(size_t nmemb, size_t size); - -void *mpd_alloc(mpd_size_t nmemb, mpd_size_t size); -void *mpd_calloc(mpd_size_t nmemb, mpd_size_t size); -void *mpd_realloc(void *ptr, mpd_size_t nmemb, mpd_size_t size, uint8_t *err); -void *mpd_sh_alloc(mpd_size_t struct_size, mpd_size_t nmemb, mpd_size_t size); - -mpd_t *mpd_qnew(void); -mpd_t *mpd_new(mpd_context_t *ctx); -mpd_t *mpd_qnew_size(mpd_ssize_t nwords); -EXTINLINE void mpd_del(mpd_t *dec); - -EXTINLINE void mpd_uint_zero(mpd_uint_t *dest, mpd_size_t len); -EXTINLINE int mpd_qresize(mpd_t *result, mpd_ssize_t nwords, uint32_t *status); -EXTINLINE int mpd_qresize_zero(mpd_t *result, mpd_ssize_t nwords, uint32_t *status); -EXTINLINE void mpd_minalloc(mpd_t *result); - -int mpd_resize(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx); -int mpd_resize_zero(mpd_t *result, mpd_ssize_t nwords, mpd_context_t *ctx); - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#ifdef __cplusplus -} /* END extern "C" */ -#endif - - -#endif /* LIBMPDEC_MPDECIMAL_H_ */ diff --git a/Modules/_decimal/libmpdec/mpsignal.c b/Modules/_decimal/libmpdec/mpsignal.c deleted file mode 100644 index fc2af48f4f3795..00000000000000 --- a/Modules/_decimal/libmpdec/mpsignal.c +++ /dev/null @@ -1,967 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <stddef.h> -#include <stdint.h> - - -/* Signaling wrappers for the quiet functions in mpdecimal.c. */ - - -char * -mpd_format(const mpd_t *dec, const char *fmt, mpd_context_t *ctx) -{ - char *ret; - uint32_t status = 0; - ret = mpd_qformat(dec, fmt, ctx, &status); - mpd_addstatus_raise(ctx, status); - return ret; -} - -void -mpd_import_u16(mpd_t *result, const uint16_t *srcdata, size_t srclen, - uint8_t srcsign, uint32_t base, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qimport_u16(result, srcdata, srclen, srcsign, base, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_import_u32(mpd_t *result, const uint32_t *srcdata, size_t srclen, - uint8_t srcsign, uint32_t base, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qimport_u32(result, srcdata, srclen, srcsign, base, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -size_t -mpd_export_u16(uint16_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, - mpd_context_t *ctx) -{ - size_t n; - uint32_t status = 0; - n = mpd_qexport_u16(rdata, rlen, base, src, &status); - mpd_addstatus_raise(ctx, status); - return n; -} - -size_t -mpd_export_u32(uint32_t **rdata, size_t rlen, uint32_t base, const mpd_t *src, - mpd_context_t *ctx) -{ - size_t n; - uint32_t status = 0; - n = mpd_qexport_u32(rdata, rlen, base, src, &status); - mpd_addstatus_raise(ctx, status); - return n; -} - -void -mpd_finalize(mpd_t *result, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qfinalize(result, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -int -mpd_check_nan(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - if (mpd_qcheck_nan(result, a, ctx, &status)) { - mpd_addstatus_raise(ctx, status); - return 1; - } - return 0; -} - -int -mpd_check_nans(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - if (mpd_qcheck_nans(result, a, b, ctx, &status)) { - mpd_addstatus_raise(ctx, status); - return 1; - } - return 0; -} - -void -mpd_set_string(mpd_t *result, const char *s, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qset_string(result, s, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_maxcoeff(mpd_t *result, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmaxcoeff(result, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -/* set static mpd from signed integer */ -void -mpd_sset_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsset_ssize(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_sset_i32(mpd_t *result, int32_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsset_i32(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifdef CONFIG_64 -void -mpd_sset_i64(mpd_t *result, int64_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsset_i64(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -/* set static mpd from unsigned integer */ -void -mpd_sset_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsset_uint(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_sset_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsset_u32(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifdef CONFIG_64 -void -mpd_sset_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsset_u64(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -/* set mpd from signed integer */ -void -mpd_set_ssize(mpd_t *result, mpd_ssize_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qset_ssize(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_set_i32(mpd_t *result, int32_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qset_i32(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifndef LEGACY_COMPILER -void -mpd_set_i64(mpd_t *result, int64_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qset_i64(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -/* set mpd from unsigned integer */ -void -mpd_set_uint(mpd_t *result, mpd_uint_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qset_uint(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_set_u32(mpd_t *result, uint32_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qset_u32(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifndef LEGACY_COMPILER -void -mpd_set_u64(mpd_t *result, uint64_t a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qset_u64(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -/* convert mpd to signed integer */ -mpd_ssize_t -mpd_get_ssize(const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_ssize_t ret; - - ret = mpd_qget_ssize(a, &status); - mpd_addstatus_raise(ctx, status); - return ret; -} - -int32_t -mpd_get_i32(const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - int32_t ret; - - ret = mpd_qget_i32(a, &status); - mpd_addstatus_raise(ctx, status); - return ret; -} - -#ifndef LEGACY_COMPILER -int64_t -mpd_get_i64(const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - int64_t ret; - - ret = mpd_qget_i64(a, &status); - mpd_addstatus_raise(ctx, status); - return ret; -} -#endif - -mpd_uint_t -mpd_get_uint(const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_uint_t ret; - - ret = mpd_qget_uint(a, &status); - mpd_addstatus_raise(ctx, status); - return ret; -} - -mpd_uint_t -mpd_abs_uint(const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_uint_t ret; - - ret = mpd_qabs_uint(a, &status); - mpd_addstatus_raise(ctx, status); - return ret; -} - -uint32_t -mpd_get_u32(const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - uint32_t ret; - - ret = mpd_qget_u32(a, &status); - mpd_addstatus_raise(ctx, status); - return ret; -} - -#ifndef LEGACY_COMPILER -uint64_t -mpd_get_u64(const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - uint64_t ret; - - ret = mpd_qget_u64(a, &status); - mpd_addstatus_raise(ctx, status); - return ret; -} -#endif - -void -mpd_and(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qand(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_copy(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - if (!mpd_qcopy(result, a, &status)) { - mpd_addstatus_raise(ctx, status); - } -} - -void -mpd_canonical(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - mpd_copy(result, a, ctx); -} - -void -mpd_copy_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - if (!mpd_qcopy_abs(result, a, &status)) { - mpd_addstatus_raise(ctx, status); - } -} - -void -mpd_copy_negate(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - if (!mpd_qcopy_negate(result, a, &status)) { - mpd_addstatus_raise(ctx, status); - } -} - -void -mpd_copy_sign(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - if (!mpd_qcopy_sign(result, a, b, &status)) { - mpd_addstatus_raise(ctx, status); - } -} - -void -mpd_invert(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qinvert(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_logb(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qlogb(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_or(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qor(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_rotate(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qrotate(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_scaleb(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qscaleb(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_shiftl(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qshiftl(result, a, n, &status); - mpd_addstatus_raise(ctx, status); -} - -mpd_uint_t -mpd_shiftr(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_uint_t rnd; - - rnd = mpd_qshiftr(result, a, n, &status); - mpd_addstatus_raise(ctx, status); - return rnd; -} - -void -mpd_shiftn(mpd_t *result, const mpd_t *a, mpd_ssize_t n, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qshiftn(result, a, n, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_shift(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qshift(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_xor(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qxor(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_abs(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qabs(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -int -mpd_cmp(const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - int c; - c = mpd_qcmp(a, b, &status); - mpd_addstatus_raise(ctx, status); - return c; -} - -int -mpd_compare(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - int c; - c = mpd_qcompare(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); - return c; -} - -int -mpd_compare_signal(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - int c; - c = mpd_qcompare_signal(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); - return c; -} - -void -mpd_add(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qadd(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_sub(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsub(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_add_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qadd_ssize(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_add_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qadd_i32(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifndef LEGACY_COMPILER -void -mpd_add_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qadd_i64(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -void -mpd_add_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qadd_uint(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_add_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qadd_u32(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifndef LEGACY_COMPILER -void -mpd_add_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qadd_u64(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -void -mpd_sub_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsub_ssize(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_sub_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsub_i32(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifndef LEGACY_COMPILER -void -mpd_sub_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsub_i64(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -void -mpd_sub_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsub_uint(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_sub_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsub_u32(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifndef LEGACY_COMPILER -void -mpd_sub_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsub_u64(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -void -mpd_div(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qdiv(q, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_div_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qdiv_ssize(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_div_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qdiv_i32(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifndef LEGACY_COMPILER -void -mpd_div_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qdiv_i64(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -void -mpd_div_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qdiv_uint(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_div_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qdiv_u32(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifndef LEGACY_COMPILER -void -mpd_div_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qdiv_u64(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -void -mpd_divmod(mpd_t *q, mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qdivmod(q, r, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_divint(mpd_t *q, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qdivint(q, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_exp(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qexp(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_fma(mpd_t *result, const mpd_t *a, const mpd_t *b, const mpd_t *c, - mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qfma(result, a, b, c, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_ln(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qln(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_log10(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qlog10(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_max(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmax(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_max_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmax_mag(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_min(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmin(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_min_mag(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmin_mag(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qminus(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_mul(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmul(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_mul_ssize(mpd_t *result, const mpd_t *a, mpd_ssize_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmul_ssize(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_mul_i32(mpd_t *result, const mpd_t *a, int32_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmul_i32(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifndef LEGACY_COMPILER -void -mpd_mul_i64(mpd_t *result, const mpd_t *a, int64_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmul_i64(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -void -mpd_mul_uint(mpd_t *result, const mpd_t *a, mpd_uint_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmul_uint(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_mul_u32(mpd_t *result, const mpd_t *a, uint32_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmul_u32(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -#ifndef LEGACY_COMPILER -void -mpd_mul_u64(mpd_t *result, const mpd_t *a, uint64_t b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qmul_u64(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} -#endif - -void -mpd_next_minus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qnext_minus(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_next_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qnext_plus(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_next_toward(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qnext_toward(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_plus(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qplus(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_pow(mpd_t *result, const mpd_t *base, const mpd_t *exp, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qpow(result, base, exp, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_powmod(mpd_t *result, const mpd_t *base, const mpd_t *exp, const mpd_t *mod, - mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qpowmod(result, base, exp, mod, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_quantize(mpd_t *result, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qquantize(result, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_rescale(mpd_t *result, const mpd_t *a, mpd_ssize_t exp, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qrescale(result, a, exp, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_reduce(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qreduce(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_rem(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qrem(r, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_rem_near(mpd_t *r, const mpd_t *a, const mpd_t *b, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qrem_near(r, a, b, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_round_to_intx(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qround_to_intx(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_round_to_int(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qround_to_int(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_trunc(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qtrunc(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_floor(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qfloor(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_ceil(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qceil(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_sqrt(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qsqrt(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} - -void -mpd_invroot(mpd_t *result, const mpd_t *a, mpd_context_t *ctx) -{ - uint32_t status = 0; - mpd_qinvroot(result, a, ctx, &status); - mpd_addstatus_raise(ctx, status); -} diff --git a/Modules/_decimal/libmpdec/numbertheory.c b/Modules/_decimal/libmpdec/numbertheory.c deleted file mode 100644 index 210e0deb371203..00000000000000 --- a/Modules/_decimal/libmpdec/numbertheory.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> -#include <stdlib.h> - -#include "bits.h" -#include "numbertheory.h" -#include "umodarith.h" - - -/* Bignum: Initialize the Number Theoretic Transform. */ - - -/* - * Return the nth root of unity in F(p). This corresponds to e**((2*pi*i)/n) - * in the Fourier transform. We have w**n == 1 (mod p). - * n := transform length. - * sign := -1 for forward transform, 1 for backward transform. - * modnum := one of {P1, P2, P3}. - */ -mpd_uint_t -_mpd_getkernel(mpd_uint_t n, int sign, int modnum) -{ - mpd_uint_t umod, p, r, xi; -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - - SETMODULUS(modnum); - r = mpd_roots[modnum]; /* primitive root of F(p) */ - p = umod; - xi = (p-1) / n; - - if (sign == -1) - return POWMOD(r, (p-1-xi)); - else - return POWMOD(r, xi); -} - -/* - * Initialize and return transform parameters. - * n := transform length. - * sign := -1 for forward transform, 1 for backward transform. - * modnum := one of {P1, P2, P3}. - */ -struct fnt_params * -_mpd_init_fnt_params(mpd_size_t n, int sign, int modnum) -{ - struct fnt_params *tparams; - mpd_uint_t umod; -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - mpd_uint_t kernel, w; - mpd_uint_t i; - mpd_size_t nhalf; - - assert(ispower2(n)); - assert(sign == -1 || sign == 1); - assert(P1 <= modnum && modnum <= P3); - - nhalf = n/2; - tparams = mpd_sh_alloc(sizeof *tparams, nhalf, sizeof (mpd_uint_t)); - if (tparams == NULL) { - return NULL; - } - - SETMODULUS(modnum); - kernel = _mpd_getkernel(n, sign, modnum); - - tparams->modnum = modnum; - tparams->modulus = umod; - tparams->kernel = kernel; - - /* wtable[] := w**0, w**1, ..., w**(nhalf-1) */ - w = 1; - for (i = 0; i < nhalf; i++) { - tparams->wtable[i] = w; - w = MULMOD(w, kernel); - } - - return tparams; -} - -/* Initialize wtable of size three. */ -void -_mpd_init_w3table(mpd_uint_t w3table[3], int sign, int modnum) -{ - mpd_uint_t umod; -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - mpd_uint_t kernel; - - SETMODULUS(modnum); - kernel = _mpd_getkernel(3, sign, modnum); - - w3table[0] = 1; - w3table[1] = kernel; - w3table[2] = POWMOD(kernel, 2); -} diff --git a/Modules/_decimal/libmpdec/numbertheory.h b/Modules/_decimal/libmpdec/numbertheory.h deleted file mode 100644 index 47b7753b831b89..00000000000000 --- a/Modules/_decimal/libmpdec/numbertheory.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_NUMBERTHEORY_H_ -#define LIBMPDEC_NUMBERTHEORY_H_ - - -#include "mpdecimal.h" -#include "constants.h" - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -/* transform parameters */ -struct fnt_params { - int modnum; - mpd_uint_t modulus; - mpd_uint_t kernel; - mpd_uint_t wtable[]; -}; - - -mpd_uint_t _mpd_getkernel(mpd_uint_t n, int sign, int modnum); -struct fnt_params *_mpd_init_fnt_params(mpd_size_t n, int sign, int modnum); -void _mpd_init_w3table(mpd_uint_t w3table[3], int sign, int modnum); - - -#ifdef PPRO -static inline void -ppro_setmodulus(int modnum, mpd_uint_t *umod, double *dmod, uint32_t dinvmod[3]) -{ - *dmod = *umod = mpd_moduli[modnum]; - dinvmod[0] = mpd_invmoduli[modnum][0]; - dinvmod[1] = mpd_invmoduli[modnum][1]; - dinvmod[2] = mpd_invmoduli[modnum][2]; -} -#else -static inline void -std_setmodulus(int modnum, mpd_uint_t *umod) -{ - *umod = mpd_moduli[modnum]; -} -#endif - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_NUMBERTHEORY_H_ */ diff --git a/Modules/_decimal/libmpdec/sixstep.c b/Modules/_decimal/libmpdec/sixstep.c deleted file mode 100644 index a4d1dbed7813c6..00000000000000 --- a/Modules/_decimal/libmpdec/sixstep.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> -#include <stdio.h> - -#include "bits.h" -#include "constants.h" -#include "difradix2.h" -#include "numbertheory.h" -#include "sixstep.h" -#include "transpose.h" -#include "umodarith.h" - - -/* Bignum: Cache efficient Matrix Fourier Transform for arrays of the - form 2**n (See literature/six-step.txt). */ - - -/* forward transform with sign = -1 */ -int -six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) -{ - struct fnt_params *tparams; - mpd_size_t log2n, C, R; - mpd_uint_t kernel; - mpd_uint_t umod; -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - mpd_uint_t *x, w0, w1, wstep; - mpd_size_t i, k; - - - assert(ispower2(n)); - assert(n >= 16); - assert(n <= MPD_MAXTRANSFORM_2N); - - log2n = mpd_bsr(n); - C = ((mpd_size_t)1) << (log2n / 2); /* number of columns */ - R = ((mpd_size_t)1) << (log2n - (log2n / 2)); /* number of rows */ - - - /* Transpose the matrix. */ - if (!transpose_pow2(a, R, C)) { - return 0; - } - - /* Length R transform on the rows. */ - if ((tparams = _mpd_init_fnt_params(R, -1, modnum)) == NULL) { - return 0; - } - for (x = a; x < a+n; x += R) { - fnt_dif2(x, R, tparams); - } - - /* Transpose the matrix. */ - if (!transpose_pow2(a, C, R)) { - mpd_free(tparams); - return 0; - } - - /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */ - SETMODULUS(modnum); - kernel = _mpd_getkernel(n, -1, modnum); - for (i = 1; i < R; i++) { - w0 = 1; /* r**(i*0): initial value for k=0 */ - w1 = POWMOD(kernel, i); /* r**(i*1): initial value for k=1 */ - wstep = MULMOD(w1, w1); /* r**(2*i) */ - for (k = 0; k < C; k += 2) { - mpd_uint_t x0 = a[i*C+k]; - mpd_uint_t x1 = a[i*C+k+1]; - MULMOD2(&x0, w0, &x1, w1); - MULMOD2C(&w0, &w1, wstep); /* r**(i*(k+2)) = r**(i*k) * r**(2*i) */ - a[i*C+k] = x0; - a[i*C+k+1] = x1; - } - } - - /* Length C transform on the rows. */ - if (C != R) { - mpd_free(tparams); - if ((tparams = _mpd_init_fnt_params(C, -1, modnum)) == NULL) { - return 0; - } - } - for (x = a; x < a+n; x += C) { - fnt_dif2(x, C, tparams); - } - mpd_free(tparams); - -#if 0 - /* An unordered transform is sufficient for convolution. */ - /* Transpose the matrix. */ - if (!transpose_pow2(a, R, C)) { - return 0; - } -#endif - - return 1; -} - - -/* reverse transform, sign = 1 */ -int -inv_six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum) -{ - struct fnt_params *tparams; - mpd_size_t log2n, C, R; - mpd_uint_t kernel; - mpd_uint_t umod; -#ifdef PPRO - double dmod; - uint32_t dinvmod[3]; -#endif - mpd_uint_t *x, w0, w1, wstep; - mpd_size_t i, k; - - - assert(ispower2(n)); - assert(n >= 16); - assert(n <= MPD_MAXTRANSFORM_2N); - - log2n = mpd_bsr(n); - C = ((mpd_size_t)1) << (log2n / 2); /* number of columns */ - R = ((mpd_size_t)1) << (log2n - (log2n / 2)); /* number of rows */ - - -#if 0 - /* An unordered transform is sufficient for convolution. */ - /* Transpose the matrix, producing an R*C matrix. */ - if (!transpose_pow2(a, C, R)) { - return 0; - } -#endif - - /* Length C transform on the rows. */ - if ((tparams = _mpd_init_fnt_params(C, 1, modnum)) == NULL) { - return 0; - } - for (x = a; x < a+n; x += C) { - fnt_dif2(x, C, tparams); - } - - /* Multiply each matrix element (addressed by i*C+k) by r**(i*k). */ - SETMODULUS(modnum); - kernel = _mpd_getkernel(n, 1, modnum); - for (i = 1; i < R; i++) { - w0 = 1; - w1 = POWMOD(kernel, i); - wstep = MULMOD(w1, w1); - for (k = 0; k < C; k += 2) { - mpd_uint_t x0 = a[i*C+k]; - mpd_uint_t x1 = a[i*C+k+1]; - MULMOD2(&x0, w0, &x1, w1); - MULMOD2C(&w0, &w1, wstep); - a[i*C+k] = x0; - a[i*C+k+1] = x1; - } - } - - /* Transpose the matrix. */ - if (!transpose_pow2(a, R, C)) { - mpd_free(tparams); - return 0; - } - - /* Length R transform on the rows. */ - if (R != C) { - mpd_free(tparams); - if ((tparams = _mpd_init_fnt_params(R, 1, modnum)) == NULL) { - return 0; - } - } - for (x = a; x < a+n; x += R) { - fnt_dif2(x, R, tparams); - } - mpd_free(tparams); - - /* Transpose the matrix. */ - if (!transpose_pow2(a, C, R)) { - return 0; - } - - return 1; -} diff --git a/Modules/_decimal/libmpdec/sixstep.h b/Modules/_decimal/libmpdec/sixstep.h deleted file mode 100644 index 89b4a33afc7920..00000000000000 --- a/Modules/_decimal/libmpdec/sixstep.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_SIXSTEP_H_ -#define LIBMPDEC_SIXSTEP_H_ - - -#include "mpdecimal.h" - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -int six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum); -int inv_six_step_fnt(mpd_uint_t *a, mpd_size_t n, int modnum); - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_SIXSTEP_H_ */ diff --git a/Modules/_decimal/libmpdec/transpose.c b/Modules/_decimal/libmpdec/transpose.c deleted file mode 100644 index 56321b5f39a733..00000000000000 --- a/Modules/_decimal/libmpdec/transpose.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#include "mpdecimal.h" - -#include <assert.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "bits.h" -#include "constants.h" -#include "transpose.h" -#include "typearith.h" - - -#define BUFSIZE 4096 -#define SIDE 128 - - -/* Bignum: The transpose functions are used for very large transforms - in sixstep.c and fourstep.c. */ - - -/* Definition of the matrix transpose */ -void -std_trans(mpd_uint_t dest[], mpd_uint_t src[], mpd_size_t rows, mpd_size_t cols) -{ - mpd_size_t idest, isrc; - mpd_size_t r, c; - - for (r = 0; r < rows; r++) { - isrc = r * cols; - idest = r; - for (c = 0; c < cols; c++) { - dest[idest] = src[isrc]; - isrc += 1; - idest += rows; - } - } -} - -/* - * Swap half-rows of 2^n * (2*2^n) matrix. - * FORWARD_CYCLE: even/odd permutation of the halfrows. - * BACKWARD_CYCLE: reverse the even/odd permutation. - */ -static int -swap_halfrows_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols, int dir) -{ - mpd_uint_t buf1[BUFSIZE]; - mpd_uint_t buf2[BUFSIZE]; - mpd_uint_t *readbuf, *writebuf, *hp; - mpd_size_t *done, dbits; - mpd_size_t b = BUFSIZE, stride; - mpd_size_t hn, hmax; /* halfrow number */ - mpd_size_t m, r=0; - mpd_size_t offset; - mpd_size_t next; - - - assert(cols == mul_size_t(2, rows)); - - if (dir == FORWARD_CYCLE) { - r = rows; - } - else if (dir == BACKWARD_CYCLE) { - r = 2; - } - else { - abort(); /* GCOV_NOT_REACHED */ - } - - m = cols - 1; - hmax = rows; /* cycles start at odd halfrows */ - dbits = 8 * sizeof *done; - if ((done = mpd_calloc(hmax/(sizeof *done) + 1, sizeof *done)) == NULL) { - return 0; - } - - for (hn = 1; hn <= hmax; hn += 2) { - - if (done[hn/dbits] & mpd_bits[hn%dbits]) { - continue; - } - - readbuf = buf1; writebuf = buf2; - - for (offset = 0; offset < cols/2; offset += b) { - - stride = (offset + b < cols/2) ? b : cols/2-offset; - - hp = matrix + hn*cols/2; - memcpy(readbuf, hp+offset, stride*(sizeof *readbuf)); - pointerswap(&readbuf, &writebuf); - - next = mulmod_size_t(hn, r, m); - hp = matrix + next*cols/2; - - while (next != hn) { - - memcpy(readbuf, hp+offset, stride*(sizeof *readbuf)); - memcpy(hp+offset, writebuf, stride*(sizeof *writebuf)); - pointerswap(&readbuf, &writebuf); - - done[next/dbits] |= mpd_bits[next%dbits]; - - next = mulmod_size_t(next, r, m); - hp = matrix + next*cols/2; - - } - - memcpy(hp+offset, writebuf, stride*(sizeof *writebuf)); - - done[hn/dbits] |= mpd_bits[hn%dbits]; - } - } - - mpd_free(done); - return 1; -} - -/* In-place transpose of a square matrix */ -static inline void -squaretrans(mpd_uint_t *buf, mpd_size_t cols) -{ - mpd_uint_t tmp; - mpd_size_t idest, isrc; - mpd_size_t r, c; - - for (r = 0; r < cols; r++) { - c = r+1; - isrc = r*cols + c; - idest = c*cols + r; - for (c = r+1; c < cols; c++) { - tmp = buf[isrc]; - buf[isrc] = buf[idest]; - buf[idest] = tmp; - isrc += 1; - idest += cols; - } - } -} - -/* - * Transpose 2^n * 2^n matrix. For cache efficiency, the matrix is split into - * square blocks with side length 'SIDE'. First, the blocks are transposed, - * then a square transposition is done on each individual block. - */ -static void -squaretrans_pow2(mpd_uint_t *matrix, mpd_size_t size) -{ - mpd_uint_t buf1[SIDE*SIDE]; - mpd_uint_t buf2[SIDE*SIDE]; - mpd_uint_t *to, *from; - mpd_size_t b = size; - mpd_size_t r, c; - mpd_size_t i; - - while (b > SIDE) b >>= 1; - - for (r = 0; r < size; r += b) { - - for (c = r; c < size; c += b) { - - from = matrix + r*size + c; - to = buf1; - for (i = 0; i < b; i++) { - memcpy(to, from, b*(sizeof *to)); - from += size; - to += b; - } - squaretrans(buf1, b); - - if (r == c) { - to = matrix + r*size + c; - from = buf1; - for (i = 0; i < b; i++) { - memcpy(to, from, b*(sizeof *to)); - from += b; - to += size; - } - continue; - } - else { - from = matrix + c*size + r; - to = buf2; - for (i = 0; i < b; i++) { - memcpy(to, from, b*(sizeof *to)); - from += size; - to += b; - } - squaretrans(buf2, b); - - to = matrix + c*size + r; - from = buf1; - for (i = 0; i < b; i++) { - memcpy(to, from, b*(sizeof *to)); - from += b; - to += size; - } - - to = matrix + r*size + c; - from = buf2; - for (i = 0; i < b; i++) { - memcpy(to, from, b*(sizeof *to)); - from += b; - to += size; - } - } - } - } - -} - -/* - * In-place transposition of a 2^n x 2^n or a 2^n x (2*2^n) - * or a (2*2^n) x 2^n matrix. - */ -int -transpose_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols) -{ - mpd_size_t size = mul_size_t(rows, cols); - - assert(ispower2(rows)); - assert(ispower2(cols)); - - if (cols == rows) { - squaretrans_pow2(matrix, rows); - } - else if (cols == mul_size_t(2, rows)) { - if (!swap_halfrows_pow2(matrix, rows, cols, FORWARD_CYCLE)) { - return 0; - } - squaretrans_pow2(matrix, rows); - squaretrans_pow2(matrix+(size/2), rows); - } - else if (rows == mul_size_t(2, cols)) { - squaretrans_pow2(matrix, cols); - squaretrans_pow2(matrix+(size/2), cols); - if (!swap_halfrows_pow2(matrix, cols, rows, BACKWARD_CYCLE)) { - return 0; - } - } - else { - abort(); /* GCOV_NOT_REACHED */ - } - - return 1; -} diff --git a/Modules/_decimal/libmpdec/transpose.h b/Modules/_decimal/libmpdec/transpose.h deleted file mode 100644 index e91c18d74356bc..00000000000000 --- a/Modules/_decimal/libmpdec/transpose.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_TRANSPOSE_H_ -#define LIBMPDEC_TRANSPOSE_H_ - - -#include "mpdecimal.h" - - -/* Internal header file: all symbols have local scope in the DSO */ -MPD_PRAGMA(MPD_HIDE_SYMBOLS_START) - - -enum {FORWARD_CYCLE, BACKWARD_CYCLE}; - - -void std_trans(mpd_uint_t dest[], mpd_uint_t src[], mpd_size_t rows, mpd_size_t cols); -int transpose_pow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols); -void transpose_3xpow2(mpd_uint_t *matrix, mpd_size_t rows, mpd_size_t cols); - - -static inline void pointerswap(mpd_uint_t **a, mpd_uint_t **b) -{ - mpd_uint_t *tmp; - - tmp = *b; - *b = *a; - *a = tmp; -} - - -MPD_PRAGMA(MPD_HIDE_SYMBOLS_END) /* restore previous scope rules */ - - -#endif /* LIBMPDEC_TRANSPOSE_H_ */ diff --git a/Modules/_decimal/libmpdec/typearith.h b/Modules/_decimal/libmpdec/typearith.h deleted file mode 100644 index dd3776453d098d..00000000000000 --- a/Modules/_decimal/libmpdec/typearith.h +++ /dev/null @@ -1,668 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_TYPEARITH_H_ -#define LIBMPDEC_TYPEARITH_H_ - - -#include "mpdecimal.h" - -#include <assert.h> - - -/*****************************************************************************/ -/* Low level native arithmetic on basic types */ -/*****************************************************************************/ - - -/** ------------------------------------------------------------ - ** Double width multiplication and division - ** ------------------------------------------------------------ - */ - -#if defined(CONFIG_64) -#if defined(ANSI) -#if defined(HAVE_UINT128_T) -static inline void -_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) -{ - __uint128_t hl; - - hl = (__uint128_t)a * b; - - *hi = hl >> 64; - *lo = (mpd_uint_t)hl; -} - -static inline void -_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, - mpd_uint_t d) -{ - __uint128_t hl; - - hl = ((__uint128_t)hi<<64) + lo; - *q = (mpd_uint_t)(hl / d); /* quotient is known to fit */ - *r = (mpd_uint_t)(hl - (__uint128_t)(*q) * d); -} -#else -static inline void -_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) -{ - uint32_t w[4], carry; - uint32_t ah, al, bh, bl; - uint64_t hl; - - ah = (uint32_t)(a>>32); al = (uint32_t)a; - bh = (uint32_t)(b>>32); bl = (uint32_t)b; - - hl = (uint64_t)al * bl; - w[0] = (uint32_t)hl; - carry = (uint32_t)(hl>>32); - - hl = (uint64_t)ah * bl + carry; - w[1] = (uint32_t)hl; - w[2] = (uint32_t)(hl>>32); - - hl = (uint64_t)al * bh + w[1]; - w[1] = (uint32_t)hl; - carry = (uint32_t)(hl>>32); - - hl = ((uint64_t)ah * bh + w[2]) + carry; - w[2] = (uint32_t)hl; - w[3] = (uint32_t)(hl>>32); - - *hi = ((uint64_t)w[3]<<32) + w[2]; - *lo = ((uint64_t)w[1]<<32) + w[0]; -} - -/* - * By Henry S. Warren: http://www.hackersdelight.org/HDcode/divlu.c.txt - * http://www.hackersdelight.org/permissions.htm: - * "You are free to use, copy, and distribute any of the code on this web - * site, whether modified by you or not. You need not give attribution." - * - * Slightly modified, comments are mine. - */ -static inline int -nlz(uint64_t x) -{ - int n; - - if (x == 0) return(64); - - n = 0; - if (x <= 0x00000000FFFFFFFF) {n = n +32; x = x <<32;} - if (x <= 0x0000FFFFFFFFFFFF) {n = n +16; x = x <<16;} - if (x <= 0x00FFFFFFFFFFFFFF) {n = n + 8; x = x << 8;} - if (x <= 0x0FFFFFFFFFFFFFFF) {n = n + 4; x = x << 4;} - if (x <= 0x3FFFFFFFFFFFFFFF) {n = n + 2; x = x << 2;} - if (x <= 0x7FFFFFFFFFFFFFFF) {n = n + 1;} - - return n; -} - -static inline void -_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t u1, mpd_uint_t u0, - mpd_uint_t v) -{ - const mpd_uint_t b = 4294967296; - mpd_uint_t un1, un0, - vn1, vn0, - q1, q0, - un32, un21, un10, - rhat, t; - int s; - - assert(u1 < v); - - s = nlz(v); - v = v << s; - vn1 = v >> 32; - vn0 = v & 0xFFFFFFFF; - - t = (s == 0) ? 0 : u0 >> (64 - s); - un32 = (u1 << s) | t; - un10 = u0 << s; - - un1 = un10 >> 32; - un0 = un10 & 0xFFFFFFFF; - - q1 = un32 / vn1; - rhat = un32 - q1*vn1; -again1: - if (q1 >= b || q1*vn0 > b*rhat + un1) { - q1 = q1 - 1; - rhat = rhat + vn1; - if (rhat < b) goto again1; - } - - /* - * Before again1 we had: - * (1) q1*vn1 + rhat = un32 - * (2) q1*vn1*b + rhat*b + un1 = un32*b + un1 - * - * The statements inside the if-clause do not change the value - * of the left-hand side of (2), and the loop is only exited - * if q1*vn0 <= rhat*b + un1, so: - * - * (3) q1*vn1*b + q1*vn0 <= un32*b + un1 - * (4) q1*v <= un32*b + un1 - * (5) 0 <= un32*b + un1 - q1*v - * - * By (5) we are certain that the possible add-back step from - * Knuth's algorithm D is never required. - * - * Since the final quotient is less than 2**64, the following - * must be true: - * - * (6) un32*b + un1 - q1*v <= UINT64_MAX - * - * This means that in the following line, the high words - * of un32*b and q1*v can be discarded without any effect - * on the result. - */ - un21 = un32*b + un1 - q1*v; - - q0 = un21 / vn1; - rhat = un21 - q0*vn1; -again2: - if (q0 >= b || q0*vn0 > b*rhat + un0) { - q0 = q0 - 1; - rhat = rhat + vn1; - if (rhat < b) goto again2; - } - - *q = q1*b + q0; - *r = (un21*b + un0 - q0*v) >> s; -} -#endif - -/* END ANSI */ -#elif defined(ASM) -static inline void -_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) -{ - mpd_uint_t h, l; - - __asm__ ( "mulq %3\n\t" - : "=d" (h), "=a" (l) - : "%a" (a), "rm" (b) - : "cc" - ); - - *hi = h; - *lo = l; -} - -static inline void -_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, - mpd_uint_t d) -{ - mpd_uint_t qq, rr; - - __asm__ ( "divq %4\n\t" - : "=a" (qq), "=d" (rr) - : "a" (lo), "d" (hi), "rm" (d) - : "cc" - ); - - *q = qq; - *r = rr; -} -/* END GCC ASM */ -#elif defined(MASM) -#include <intrin.h> -#pragma intrinsic(_umul128) - -static inline void -_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) -{ - *lo = _umul128(a, b, hi); -} - -void _mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, - mpd_uint_t d); - -/* END MASM (_MSC_VER) */ -#else - #error "need platform specific 128 bit multiplication and division" -#endif - -#define DIVMOD(q, r, v, d) *q = v / d; *r = v - *q * d -static inline void -_mpd_divmod_pow10(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t exp) -{ - assert(exp <= 19); - - if (exp <= 9) { - if (exp <= 4) { - switch (exp) { - case 0: *q = v; *r = 0; break; - case 1: DIVMOD(q, r, v, 10UL); break; - case 2: DIVMOD(q, r, v, 100UL); break; - case 3: DIVMOD(q, r, v, 1000UL); break; - case 4: DIVMOD(q, r, v, 10000UL); break; - } - } - else { - switch (exp) { - case 5: DIVMOD(q, r, v, 100000UL); break; - case 6: DIVMOD(q, r, v, 1000000UL); break; - case 7: DIVMOD(q, r, v, 10000000UL); break; - case 8: DIVMOD(q, r, v, 100000000UL); break; - case 9: DIVMOD(q, r, v, 1000000000UL); break; - } - } - } - else { - if (exp <= 14) { - switch (exp) { - case 10: DIVMOD(q, r, v, 10000000000ULL); break; - case 11: DIVMOD(q, r, v, 100000000000ULL); break; - case 12: DIVMOD(q, r, v, 1000000000000ULL); break; - case 13: DIVMOD(q, r, v, 10000000000000ULL); break; - case 14: DIVMOD(q, r, v, 100000000000000ULL); break; - } - } - else { - switch (exp) { - case 15: DIVMOD(q, r, v, 1000000000000000ULL); break; - case 16: DIVMOD(q, r, v, 10000000000000000ULL); break; - case 17: DIVMOD(q, r, v, 100000000000000000ULL); break; - case 18: DIVMOD(q, r, v, 1000000000000000000ULL); break; - case 19: DIVMOD(q, r, v, 10000000000000000000ULL); break; /* GCOV_NOT_REACHED */ - } - } - } -} - -/* END CONFIG_64 */ -#elif defined(CONFIG_32) -#if defined(ANSI) -#if !defined(LEGACY_COMPILER) -static inline void -_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) -{ - mpd_uuint_t hl; - - hl = (mpd_uuint_t)a * b; - - *hi = hl >> 32; - *lo = (mpd_uint_t)hl; -} - -static inline void -_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, - mpd_uint_t d) -{ - mpd_uuint_t hl; - - hl = ((mpd_uuint_t)hi<<32) + lo; - *q = (mpd_uint_t)(hl / d); /* quotient is known to fit */ - *r = (mpd_uint_t)(hl - (mpd_uuint_t)(*q) * d); -} -/* END ANSI + uint64_t */ -#else -static inline void -_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) -{ - uint16_t w[4], carry; - uint16_t ah, al, bh, bl; - uint32_t hl; - - ah = (uint16_t)(a>>16); al = (uint16_t)a; - bh = (uint16_t)(b>>16); bl = (uint16_t)b; - - hl = (uint32_t)al * bl; - w[0] = (uint16_t)hl; - carry = (uint16_t)(hl>>16); - - hl = (uint32_t)ah * bl + carry; - w[1] = (uint16_t)hl; - w[2] = (uint16_t)(hl>>16); - - hl = (uint32_t)al * bh + w[1]; - w[1] = (uint16_t)hl; - carry = (uint16_t)(hl>>16); - - hl = ((uint32_t)ah * bh + w[2]) + carry; - w[2] = (uint16_t)hl; - w[3] = (uint16_t)(hl>>16); - - *hi = ((uint32_t)w[3]<<16) + w[2]; - *lo = ((uint32_t)w[1]<<16) + w[0]; -} - -/* - * By Henry S. Warren: http://www.hackersdelight.org/HDcode/divlu.c.txt - * http://www.hackersdelight.org/permissions.htm: - * "You are free to use, copy, and distribute any of the code on this web - * site, whether modified by you or not. You need not give attribution." - * - * Slightly modified, comments are mine. - */ -static inline int -nlz(uint32_t x) -{ - int n; - - if (x == 0) return(32); - - n = 0; - if (x <= 0x0000FFFF) {n = n +16; x = x <<16;} - if (x <= 0x00FFFFFF) {n = n + 8; x = x << 8;} - if (x <= 0x0FFFFFFF) {n = n + 4; x = x << 4;} - if (x <= 0x3FFFFFFF) {n = n + 2; x = x << 2;} - if (x <= 0x7FFFFFFF) {n = n + 1;} - - return n; -} - -static inline void -_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t u1, mpd_uint_t u0, - mpd_uint_t v) -{ - const mpd_uint_t b = 65536; - mpd_uint_t un1, un0, - vn1, vn0, - q1, q0, - un32, un21, un10, - rhat, t; - int s; - - assert(u1 < v); - - s = nlz(v); - v = v << s; - vn1 = v >> 16; - vn0 = v & 0xFFFF; - - t = (s == 0) ? 0 : u0 >> (32 - s); - un32 = (u1 << s) | t; - un10 = u0 << s; - - un1 = un10 >> 16; - un0 = un10 & 0xFFFF; - - q1 = un32 / vn1; - rhat = un32 - q1*vn1; -again1: - if (q1 >= b || q1*vn0 > b*rhat + un1) { - q1 = q1 - 1; - rhat = rhat + vn1; - if (rhat < b) goto again1; - } - - /* - * Before again1 we had: - * (1) q1*vn1 + rhat = un32 - * (2) q1*vn1*b + rhat*b + un1 = un32*b + un1 - * - * The statements inside the if-clause do not change the value - * of the left-hand side of (2), and the loop is only exited - * if q1*vn0 <= rhat*b + un1, so: - * - * (3) q1*vn1*b + q1*vn0 <= un32*b + un1 - * (4) q1*v <= un32*b + un1 - * (5) 0 <= un32*b + un1 - q1*v - * - * By (5) we are certain that the possible add-back step from - * Knuth's algorithm D is never required. - * - * Since the final quotient is less than 2**32, the following - * must be true: - * - * (6) un32*b + un1 - q1*v <= UINT32_MAX - * - * This means that in the following line, the high words - * of un32*b and q1*v can be discarded without any effect - * on the result. - */ - un21 = un32*b + un1 - q1*v; - - q0 = un21 / vn1; - rhat = un21 - q0*vn1; -again2: - if (q0 >= b || q0*vn0 > b*rhat + un0) { - q0 = q0 - 1; - rhat = rhat + vn1; - if (rhat < b) goto again2; - } - - *q = q1*b + q0; - *r = (un21*b + un0 - q0*v) >> s; -} -#endif /* END ANSI + LEGACY_COMPILER */ - -/* END ANSI */ -#elif defined(ASM) -static inline void -_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) -{ - mpd_uint_t h, l; - - __asm__ ( "mull %3\n\t" - : "=d" (h), "=a" (l) - : "%a" (a), "rm" (b) - : "cc" - ); - - *hi = h; - *lo = l; -} - -static inline void -_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, - mpd_uint_t d) -{ - mpd_uint_t qq, rr; - - __asm__ ( "divl %4\n\t" - : "=a" (qq), "=d" (rr) - : "a" (lo), "d" (hi), "rm" (d) - : "cc" - ); - - *q = qq; - *r = rr; -} -/* END GCC ASM */ -#elif defined(MASM) -static inline void __cdecl -_mpd_mul_words(mpd_uint_t *hi, mpd_uint_t *lo, mpd_uint_t a, mpd_uint_t b) -{ - mpd_uint_t h, l; - - __asm { - mov eax, a - mul b - mov h, edx - mov l, eax - } - - *hi = h; - *lo = l; -} - -static inline void __cdecl -_mpd_div_words(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t hi, mpd_uint_t lo, - mpd_uint_t d) -{ - mpd_uint_t qq, rr; - - __asm { - mov eax, lo - mov edx, hi - div d - mov qq, eax - mov rr, edx - } - - *q = qq; - *r = rr; -} -/* END MASM (_MSC_VER) */ -#else - #error "need platform specific 64 bit multiplication and division" -#endif - -#define DIVMOD(q, r, v, d) *q = v / d; *r = v - *q * d -static inline void -_mpd_divmod_pow10(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t exp) -{ - assert(exp <= 9); - - if (exp <= 4) { - switch (exp) { - case 0: *q = v; *r = 0; break; - case 1: DIVMOD(q, r, v, 10UL); break; - case 2: DIVMOD(q, r, v, 100UL); break; - case 3: DIVMOD(q, r, v, 1000UL); break; - case 4: DIVMOD(q, r, v, 10000UL); break; - } - } - else { - switch (exp) { - case 5: DIVMOD(q, r, v, 100000UL); break; - case 6: DIVMOD(q, r, v, 1000000UL); break; - case 7: DIVMOD(q, r, v, 10000000UL); break; - case 8: DIVMOD(q, r, v, 100000000UL); break; - case 9: DIVMOD(q, r, v, 1000000000UL); break; /* GCOV_NOT_REACHED */ - } - } -} -/* END CONFIG_32 */ - -/* NO CONFIG */ -#else - #error "define CONFIG_64 or CONFIG_32" -#endif /* CONFIG */ - - -static inline void -_mpd_div_word(mpd_uint_t *q, mpd_uint_t *r, mpd_uint_t v, mpd_uint_t d) -{ - *q = v / d; - *r = v - *q * d; -} - -static inline void -_mpd_idiv_word(mpd_ssize_t *q, mpd_ssize_t *r, mpd_ssize_t v, mpd_ssize_t d) -{ - *q = v / d; - *r = v - *q * d; -} - - -/** ------------------------------------------------------------ - ** Arithmetic with overflow checking - ** ------------------------------------------------------------ - */ - -/* The following macros do call exit() in case of an overflow. - If the library is used correctly (i.e. with valid context - parameters), such overflows cannot occur. The macros are used - as sanity checks in a couple of strategic places and should - be viewed as a handwritten version of gcc's -ftrapv option. */ - -static inline mpd_size_t -add_size_t(mpd_size_t a, mpd_size_t b) -{ - if (a > MPD_SIZE_MAX - b) { - mpd_err_fatal("add_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */ - } - return a + b; -} - -static inline mpd_size_t -sub_size_t(mpd_size_t a, mpd_size_t b) -{ - if (b > a) { - mpd_err_fatal("sub_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */ - } - return a - b; -} - -#if MPD_SIZE_MAX != MPD_UINT_MAX - #error "adapt mul_size_t() and mulmod_size_t()" -#endif - -static inline mpd_size_t -mul_size_t(mpd_size_t a, mpd_size_t b) -{ - mpd_uint_t hi, lo; - - _mpd_mul_words(&hi, &lo, (mpd_uint_t)a, (mpd_uint_t)b); - if (hi) { - mpd_err_fatal("mul_size_t(): overflow: check the context"); /* GCOV_NOT_REACHED */ - } - return lo; -} - -static inline mpd_size_t -add_size_t_overflow(mpd_size_t a, mpd_size_t b, mpd_size_t *overflow) -{ - mpd_size_t ret; - - *overflow = 0; - ret = a + b; - if (ret < a) *overflow = 1; - return ret; -} - -static inline mpd_size_t -mul_size_t_overflow(mpd_size_t a, mpd_size_t b, mpd_size_t *overflow) -{ - mpd_uint_t hi, lo; - - _mpd_mul_words(&hi, &lo, (mpd_uint_t)a, (mpd_uint_t)b); - *overflow = (mpd_size_t)hi; - return lo; -} - -static inline mpd_ssize_t -mod_mpd_ssize_t(mpd_ssize_t a, mpd_ssize_t m) -{ - mpd_ssize_t r = a % m; - return (r < 0) ? r + m : r; -} - -static inline mpd_size_t -mulmod_size_t(mpd_size_t a, mpd_size_t b, mpd_size_t m) -{ - mpd_uint_t hi, lo; - mpd_uint_t q, r; - - _mpd_mul_words(&hi, &lo, (mpd_uint_t)a, (mpd_uint_t)b); - _mpd_div_words(&q, &r, hi, lo, (mpd_uint_t)m); - - return r; -} - - -#endif /* LIBMPDEC_TYPEARITH_H_ */ diff --git a/Modules/_decimal/libmpdec/umodarith.h b/Modules/_decimal/libmpdec/umodarith.h deleted file mode 100644 index d7dbbbe6a7331a..00000000000000 --- a/Modules/_decimal/libmpdec/umodarith.h +++ /dev/null @@ -1,648 +0,0 @@ -/* - * Copyright (c) 2008-2020 Stefan Krah. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#ifndef LIBMPDEC_UMODARITH_H_ -#define LIBMPDEC_UMODARITH_H_ - - -#include "mpdecimal.h" - -#include "constants.h" -#include "typearith.h" - - -/* Bignum: Low level routines for unsigned modular arithmetic. These are - used in the fast convolution functions for very large coefficients. */ - - -/**************************************************************************/ -/* ANSI modular arithmetic */ -/**************************************************************************/ - - -/* - * Restrictions: a < m and b < m - * ACL2 proof: umodarith.lisp: addmod-correct - */ -static inline mpd_uint_t -addmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) -{ - mpd_uint_t s; - - s = a + b; - s = (s < a) ? s - m : s; - s = (s >= m) ? s - m : s; - - return s; -} - -/* - * Restrictions: a < m and b < m - * ACL2 proof: umodarith.lisp: submod-2-correct - */ -static inline mpd_uint_t -submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) -{ - mpd_uint_t d; - - d = a - b; - d = (a < b) ? d + m : d; - - return d; -} - -/* - * Restrictions: a < 2m and b < 2m - * ACL2 proof: umodarith.lisp: section ext-submod - */ -static inline mpd_uint_t -ext_submod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) -{ - mpd_uint_t d; - - a = (a >= m) ? a - m : a; - b = (b >= m) ? b - m : b; - - d = a - b; - d = (a < b) ? d + m : d; - - return d; -} - -/* - * Reduce double word modulo m. - * Restrictions: m != 0 - * ACL2 proof: umodarith.lisp: section dw-reduce - */ -static inline mpd_uint_t -dw_reduce(mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m) -{ - mpd_uint_t r1, r2, w; - - _mpd_div_word(&w, &r1, hi, m); - _mpd_div_words(&w, &r2, r1, lo, m); - - return r2; -} - -/* - * Subtract double word from a. - * Restrictions: a < m - * ACL2 proof: umodarith.lisp: section dw-submod - */ -static inline mpd_uint_t -dw_submod(mpd_uint_t a, mpd_uint_t hi, mpd_uint_t lo, mpd_uint_t m) -{ - mpd_uint_t d, r; - - r = dw_reduce(hi, lo, m); - d = a - r; - d = (a < r) ? d + m : d; - - return d; -} - -#ifdef CONFIG_64 - -/**************************************************************************/ -/* 64-bit modular arithmetic */ -/**************************************************************************/ - -/* - * A proof of the algorithm is in literature/mulmod-64.txt. An ACL2 - * proof is in umodarith.lisp: section "Fast modular reduction". - * - * Algorithm: calculate (a * b) % p: - * - * a) hi, lo <- a * b # Calculate a * b. - * - * b) hi, lo <- R(hi, lo) # Reduce modulo p. - * - * c) Repeat step b) until 0 <= hi * 2**64 + lo < 2*p. - * - * d) If the result is less than p, return lo. Otherwise return lo - p. - */ - -static inline mpd_uint_t -x64_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) -{ - mpd_uint_t hi, lo, x, y; - - - _mpd_mul_words(&hi, &lo, a, b); - - if (m & (1ULL<<32)) { /* P1 */ - - /* first reduction */ - x = y = hi; - hi >>= 32; - - x = lo - x; - if (x > lo) hi--; - - y <<= 32; - lo = y + x; - if (lo < y) hi++; - - /* second reduction */ - x = y = hi; - hi >>= 32; - - x = lo - x; - if (x > lo) hi--; - - y <<= 32; - lo = y + x; - if (lo < y) hi++; - - return (hi || lo >= m ? lo - m : lo); - } - else if (m & (1ULL<<34)) { /* P2 */ - - /* first reduction */ - x = y = hi; - hi >>= 30; - - x = lo - x; - if (x > lo) hi--; - - y <<= 34; - lo = y + x; - if (lo < y) hi++; - - /* second reduction */ - x = y = hi; - hi >>= 30; - - x = lo - x; - if (x > lo) hi--; - - y <<= 34; - lo = y + x; - if (lo < y) hi++; - - /* third reduction */ - x = y = hi; - hi >>= 30; - - x = lo - x; - if (x > lo) hi--; - - y <<= 34; - lo = y + x; - if (lo < y) hi++; - - return (hi || lo >= m ? lo - m : lo); - } - else { /* P3 */ - - /* first reduction */ - x = y = hi; - hi >>= 24; - - x = lo - x; - if (x > lo) hi--; - - y <<= 40; - lo = y + x; - if (lo < y) hi++; - - /* second reduction */ - x = y = hi; - hi >>= 24; - - x = lo - x; - if (x > lo) hi--; - - y <<= 40; - lo = y + x; - if (lo < y) hi++; - - /* third reduction */ - x = y = hi; - hi >>= 24; - - x = lo - x; - if (x > lo) hi--; - - y <<= 40; - lo = y + x; - if (lo < y) hi++; - - return (hi || lo >= m ? lo - m : lo); - } -} - -static inline void -x64_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m) -{ - *a = x64_mulmod(*a, w, m); - *b = x64_mulmod(*b, w, m); -} - -static inline void -x64_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1, - mpd_uint_t m) -{ - *a0 = x64_mulmod(*a0, b0, m); - *a1 = x64_mulmod(*a1, b1, m); -} - -static inline mpd_uint_t -x64_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod) -{ - mpd_uint_t r = 1; - - while (exp > 0) { - if (exp & 1) - r = x64_mulmod(r, base, umod); - base = x64_mulmod(base, base, umod); - exp >>= 1; - } - - return r; -} - -/* END CONFIG_64 */ -#else /* CONFIG_32 */ - - -/**************************************************************************/ -/* 32-bit modular arithmetic */ -/**************************************************************************/ - -#if defined(ANSI) -#if !defined(LEGACY_COMPILER) -/* HAVE_UINT64_T */ -static inline mpd_uint_t -std_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) -{ - return ((mpd_uuint_t) a * b) % m; -} - -static inline void -std_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m) -{ - *a = ((mpd_uuint_t) *a * w) % m; - *b = ((mpd_uuint_t) *b * w) % m; -} - -static inline void -std_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1, - mpd_uint_t m) -{ - *a0 = ((mpd_uuint_t) *a0 * b0) % m; - *a1 = ((mpd_uuint_t) *a1 * b1) % m; -} -/* END HAVE_UINT64_T */ -#else -/* LEGACY_COMPILER */ -static inline mpd_uint_t -std_mulmod(mpd_uint_t a, mpd_uint_t b, mpd_uint_t m) -{ - mpd_uint_t hi, lo, q, r; - _mpd_mul_words(&hi, &lo, a, b); - _mpd_div_words(&q, &r, hi, lo, m); - return r; -} - -static inline void -std_mulmod2c(mpd_uint_t *a, mpd_uint_t *b, mpd_uint_t w, mpd_uint_t m) -{ - *a = std_mulmod(*a, w, m); - *b = std_mulmod(*b, w, m); -} - -static inline void -std_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1, - mpd_uint_t m) -{ - *a0 = std_mulmod(*a0, b0, m); - *a1 = std_mulmod(*a1, b1, m); -} -/* END LEGACY_COMPILER */ -#endif - -static inline mpd_uint_t -std_powmod(mpd_uint_t base, mpd_uint_t exp, mpd_uint_t umod) -{ - mpd_uint_t r = 1; - - while (exp > 0) { - if (exp & 1) - r = std_mulmod(r, base, umod); - base = std_mulmod(base, base, umod); - exp >>= 1; - } - - return r; -} -#endif /* ANSI CONFIG_32 */ - - -/**************************************************************************/ -/* Pentium Pro modular arithmetic */ -/**************************************************************************/ - -/* - * A proof of the algorithm is in literature/mulmod-ppro.txt. The FPU - * control word must be set to 64-bit precision and truncation mode - * prior to using these functions. - * - * Algorithm: calculate (a * b) % p: - * - * p := prime < 2**31 - * pinv := (long double)1.0 / p (precalculated) - * - * a) n = a * b # Calculate exact product. - * b) qest = n * pinv # Calculate estimate for q = n / p. - * c) q = (qest+2**63)-2**63 # Truncate qest to the exact quotient. - * d) r = n - q * p # Calculate remainder. - * - * Remarks: - * - * - p = dmod and pinv = dinvmod. - * - dinvmod points to an array of three uint32_t, which is interpreted - * as an 80 bit long double by fldt. - * - Intel compilers prior to version 11 do not seem to handle the - * __GNUC__ inline assembly correctly. - * - random tests are provided in tests/extended/ppro_mulmod.c - */ - -#if defined(PPRO) -#if defined(ASM) - -/* Return (a * b) % dmod */ -static inline mpd_uint_t -ppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod) -{ - mpd_uint_t retval; - - __asm__ ( - "fildl %2\n\t" - "fildl %1\n\t" - "fmulp %%st, %%st(1)\n\t" - "fldt (%4)\n\t" - "fmul %%st(1), %%st\n\t" - "flds %5\n\t" - "fadd %%st, %%st(1)\n\t" - "fsubrp %%st, %%st(1)\n\t" - "fldl (%3)\n\t" - "fmulp %%st, %%st(1)\n\t" - "fsubrp %%st, %%st(1)\n\t" - "fistpl %0\n\t" - : "=m" (retval) - : "m" (a), "m" (b), "r" (dmod), "r" (dinvmod), "m" (MPD_TWO63) - : "st", "memory" - ); - - return retval; -} - -/* - * Two modular multiplications in parallel: - * *a0 = (*a0 * w) % dmod - * *a1 = (*a1 * w) % dmod - */ -static inline void -ppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w, - double *dmod, uint32_t *dinvmod) -{ - __asm__ ( - "fildl %2\n\t" - "fildl (%1)\n\t" - "fmul %%st(1), %%st\n\t" - "fxch %%st(1)\n\t" - "fildl (%0)\n\t" - "fmulp %%st, %%st(1) \n\t" - "fldt (%4)\n\t" - "flds %5\n\t" - "fld %%st(2)\n\t" - "fmul %%st(2)\n\t" - "fadd %%st(1)\n\t" - "fsub %%st(1)\n\t" - "fmull (%3)\n\t" - "fsubrp %%st, %%st(3)\n\t" - "fxch %%st(2)\n\t" - "fistpl (%0)\n\t" - "fmul %%st(2)\n\t" - "fadd %%st(1)\n\t" - "fsubp %%st, %%st(1)\n\t" - "fmull (%3)\n\t" - "fsubrp %%st, %%st(1)\n\t" - "fistpl (%1)\n\t" - : : "r" (a0), "r" (a1), "m" (w), - "r" (dmod), "r" (dinvmod), - "m" (MPD_TWO63) - : "st", "memory" - ); -} - -/* - * Two modular multiplications in parallel: - * *a0 = (*a0 * b0) % dmod - * *a1 = (*a1 * b1) % dmod - */ -static inline void -ppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1, - double *dmod, uint32_t *dinvmod) -{ - __asm__ ( - "fildl %3\n\t" - "fildl (%2)\n\t" - "fmulp %%st, %%st(1)\n\t" - "fildl %1\n\t" - "fildl (%0)\n\t" - "fmulp %%st, %%st(1)\n\t" - "fldt (%5)\n\t" - "fld %%st(2)\n\t" - "fmul %%st(1), %%st\n\t" - "fxch %%st(1)\n\t" - "fmul %%st(2), %%st\n\t" - "flds %6\n\t" - "fldl (%4)\n\t" - "fxch %%st(3)\n\t" - "fadd %%st(1), %%st\n\t" - "fxch %%st(2)\n\t" - "fadd %%st(1), %%st\n\t" - "fxch %%st(2)\n\t" - "fsub %%st(1), %%st\n\t" - "fxch %%st(2)\n\t" - "fsubp %%st, %%st(1)\n\t" - "fxch %%st(1)\n\t" - "fmul %%st(2), %%st\n\t" - "fxch %%st(1)\n\t" - "fmulp %%st, %%st(2)\n\t" - "fsubrp %%st, %%st(3)\n\t" - "fsubrp %%st, %%st(1)\n\t" - "fxch %%st(1)\n\t" - "fistpl (%2)\n\t" - "fistpl (%0)\n\t" - : : "r" (a0), "m" (b0), "r" (a1), "m" (b1), - "r" (dmod), "r" (dinvmod), - "m" (MPD_TWO63) - : "st", "memory" - ); -} -/* END PPRO GCC ASM */ -#elif defined(MASM) - -/* Return (a * b) % dmod */ -static inline mpd_uint_t __cdecl -ppro_mulmod(mpd_uint_t a, mpd_uint_t b, double *dmod, uint32_t *dinvmod) -{ - mpd_uint_t retval; - - __asm { - mov eax, dinvmod - mov edx, dmod - fild b - fild a - fmulp st(1), st - fld TBYTE PTR [eax] - fmul st, st(1) - fld MPD_TWO63 - fadd st(1), st - fsubp st(1), st - fld QWORD PTR [edx] - fmulp st(1), st - fsubp st(1), st - fistp retval - } - - return retval; -} - -/* - * Two modular multiplications in parallel: - * *a0 = (*a0 * w) % dmod - * *a1 = (*a1 * w) % dmod - */ -static inline mpd_uint_t __cdecl -ppro_mulmod2c(mpd_uint_t *a0, mpd_uint_t *a1, mpd_uint_t w, - double *dmod, uint32_t *dinvmod) -{ - __asm { - mov ecx, dmod - mov edx, a1 - mov ebx, dinvmod - mov eax, a0 - fild w - fild DWORD PTR [edx] - fmul st, st(1) - fxch st(1) - fild DWORD PTR [eax] - fmulp st(1), st - fld TBYTE PTR [ebx] - fld MPD_TWO63 - fld st(2) - fmul st, st(2) - fadd st, st(1) - fsub st, st(1) - fmul QWORD PTR [ecx] - fsubp st(3), st - fxch st(2) - fistp DWORD PTR [eax] - fmul st, st(2) - fadd st, st(1) - fsubrp st(1), st - fmul QWORD PTR [ecx] - fsubp st(1), st - fistp DWORD PTR [edx] - } -} - -/* - * Two modular multiplications in parallel: - * *a0 = (*a0 * b0) % dmod - * *a1 = (*a1 * b1) % dmod - */ -static inline void __cdecl -ppro_mulmod2(mpd_uint_t *a0, mpd_uint_t b0, mpd_uint_t *a1, mpd_uint_t b1, - double *dmod, uint32_t *dinvmod) -{ - __asm { - mov ecx, dmod - mov edx, a1 - mov ebx, dinvmod - mov eax, a0 - fild b1 - fild DWORD PTR [edx] - fmulp st(1), st - fild b0 - fild DWORD PTR [eax] - fmulp st(1), st - fld TBYTE PTR [ebx] - fld st(2) - fmul st, st(1) - fxch st(1) - fmul st, st(2) - fld DWORD PTR MPD_TWO63 - fld QWORD PTR [ecx] - fxch st(3) - fadd st, st(1) - fxch st(2) - fadd st, st(1) - fxch st(2) - fsub st, st(1) - fxch st(2) - fsubrp st(1), st - fxch st(1) - fmul st, st(2) - fxch st(1) - fmulp st(2), st - fsubp st(3), st - fsubp st(1), st - fxch st(1) - fistp DWORD PTR [edx] - fistp DWORD PTR [eax] - } -} -#endif /* PPRO MASM (_MSC_VER) */ - - -/* Return (base ** exp) % dmod */ -static inline mpd_uint_t -ppro_powmod(mpd_uint_t base, mpd_uint_t exp, double *dmod, uint32_t *dinvmod) -{ - mpd_uint_t r = 1; - - while (exp > 0) { - if (exp & 1) - r = ppro_mulmod(r, base, dmod, dinvmod); - base = ppro_mulmod(base, base, dmod, dinvmod); - exp >>= 1; - } - - return r; -} -#endif /* PPRO */ -#endif /* CONFIG_32 */ - - -#endif /* LIBMPDEC_UMODARITH_H_ */ diff --git a/Modules/_decimal/libmpdec/vcdiv64.asm b/Modules/_decimal/libmpdec/vcdiv64.asm deleted file mode 100644 index 597e9ba9352c8f..00000000000000 --- a/Modules/_decimal/libmpdec/vcdiv64.asm +++ /dev/null @@ -1,46 +0,0 @@ -; -; Copyright (c) 2008-2020 Stefan Krah. All rights reserved. -; -; Redistribution and use in source and binary forms, with or without -; modification, are permitted provided that the following conditions -; are met: -; -; 1. Redistributions of source code must retain the above copyright -; notice, this list of conditions and the following disclaimer. -; -; 2. Redistributions in binary form must reproduce the above copyright -; notice, this list of conditions and the following disclaimer in the -; documentation and/or other materials provided with the distribution. -; -; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND -; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -; SUCH DAMAGE. -; - - -PUBLIC _mpd_div_words -_TEXT SEGMENT -q$ = 8 -r$ = 16 -hi$ = 24 -lo$ = 32 -d$ = 40 -_mpd_div_words PROC - mov r10, rdx - mov rdx, r8 - mov rax, r9 - div QWORD PTR d$[rsp] - mov QWORD PTR [r10], rdx - mov QWORD PTR [rcx], rax - ret 0 -_mpd_div_words ENDP -_TEXT ENDS -END diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index b9e12ab2026f65..f827274eeffba8 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -16,7 +16,10 @@ #endif #include "Python.h" +#include "pycore_ceval.h" // _Py_EnterRecursiveCall() +#include "pycore_dict.h" // _PyDict_CopyAsDict() #include "pycore_pyhash.h" // _Py_HashSecret +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() #include <stddef.h> // offsetof() @@ -382,13 +385,14 @@ get_attrib_from_keywords(PyObject *kwds) /* If attrib was found in kwds, copy its value and remove it from * kwds */ - if (!PyDict_Check(attrib)) { - PyErr_Format(PyExc_TypeError, "attrib must be dict, not %.100s", - Py_TYPE(attrib)->tp_name); + if (!PyAnyDict_Check(attrib)) { + PyErr_Format(PyExc_TypeError, + "attrib must be dict or frozendict, not %T", + attrib); Py_DECREF(attrib); return NULL; } - Py_SETREF(attrib, PyDict_Copy(attrib)); + Py_SETREF(attrib, _PyDict_CopyAsDict(attrib)); } else { attrib = PyDict_New(); @@ -416,12 +420,18 @@ element_init(PyObject *self, PyObject *args, PyObject *kwds) PyObject *attrib = NULL; ElementObject *self_elem; - if (!PyArg_ParseTuple(args, "O|O!:Element", &tag, &PyDict_Type, &attrib)) + if (!PyArg_ParseTuple(args, "O|O:Element", &tag, &attrib)) return -1; + if (attrib != NULL && !PyAnyDict_Check(attrib)) { + PyErr_Format(PyExc_TypeError, + "Element() argument 2 must be dict or frozendict, not %T", + attrib); + return -1; + } if (attrib) { /* attrib passed as positional arg */ - attrib = PyDict_Copy(attrib); + attrib = _PyDict_CopyAsDict(attrib); if (!attrib) return -1; if (kwds) { @@ -563,7 +573,7 @@ element_get_attrib(ElementObject* self) LOCAL(PyObject*) element_get_text(ElementObject* self) { - /* return borrowed reference to text attribute */ + /* return new reference to text attribute */ PyObject *res = self->text; @@ -578,13 +588,13 @@ element_get_text(ElementObject* self) } } - return res; + return Py_NewRef(res); } LOCAL(PyObject*) element_get_tail(ElementObject* self) { - /* return borrowed reference to text attribute */ + /* return new reference to tail attribute */ PyObject *res = self->tail; @@ -599,7 +609,7 @@ element_get_tail(ElementObject* self) } } - return res; + return Py_NewRef(res); } static PyObject* @@ -802,26 +812,31 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) /*[clinic end generated code: output=eefc3df50465b642 input=a2d40348c0aade10]*/ { Py_ssize_t i; - ElementObject* element; + ElementObject* element = NULL; PyObject* tag; PyObject* attrib; PyObject* text; PyObject* tail; PyObject* id; + if (_Py_EnterRecursiveCall(" in Element.__deepcopy__")) { + return NULL; + } + PyTypeObject *tp = Py_TYPE(self); elementtreestate *st = get_elementtree_state_by_type(tp); // The deepcopy() helper takes care of incrementing the refcount // of the object to copy so to avoid use-after-frees. tag = deepcopy(st, self->tag, memo); - if (!tag) - return NULL; + if (!tag) { + goto error; + } if (self->extra && self->extra->attrib) { attrib = deepcopy(st, self->extra->attrib, memo); if (!attrib) { Py_DECREF(tag); - return NULL; + goto error; } } else { attrib = NULL; @@ -832,8 +847,9 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) Py_DECREF(tag); Py_XDECREF(attrib); - if (!element) - return NULL; + if (!element) { + goto error; + } text = deepcopy(st, JOIN_OBJ(self->text), memo); if (!text) @@ -895,10 +911,12 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo) if (i < 0) goto error; + _Py_LeaveRecursiveCall(); return (PyObject*) element; error: - Py_DECREF(element); + _Py_LeaveRecursiveCall(); + Py_XDECREF(element); return NULL; } @@ -912,7 +930,7 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo) return Py_NewRef(object); } - if (Py_REFCNT(object) == 1) { + if (_PyObject_IsUniquelyReferenced(object)) { if (PyDict_CheckExact(object)) { PyObject *key, *value; Py_ssize_t pos = 0; @@ -1341,9 +1359,9 @@ _elementtree_Element_findtext_impl(ElementObject *self, PyTypeObject *cls, PyObject *text = element_get_text((ElementObject *)item); Py_DECREF(item); if (text == Py_None) { + Py_DECREF(text); return Py_GetConstant(Py_CONSTANT_EMPTY_STR); } - Py_XINCREF(text); return text; } Py_DECREF(item); @@ -1679,8 +1697,7 @@ _elementtree_Element_remove_impl(ElementObject *self, PyObject *subelement) } if (rc == 0) { - PyErr_SetString(PyExc_ValueError, - "Element.remove(x): element not found"); + PyErr_Format(PyExc_ValueError, "%R not in %R", subelement, self); return NULL; } @@ -1806,16 +1823,20 @@ element_subscr(PyObject *op, PyObject *item) return element_getitem(op, i); } else if (PySlice_Check(item)) { + // Note: 'slicelen' is computed once we are sure that 'self->extra' + // cannot be mutated by user-defined code. + // See https://github.com/python/cpython/issues/143200. Py_ssize_t start, stop, step, slicelen, i; size_t cur; PyObject* list; - if (!self->extra) - return PyList_New(0); - if (PySlice_Unpack(item, &start, &stop, &step) < 0) { return NULL; } + + if (self->extra == NULL) { + return PyList_New(0); + } slicelen = PySlice_AdjustIndices(self->extra->length, &start, &stop, step); @@ -1858,28 +1879,26 @@ element_ass_subscr(PyObject *op, PyObject *item, PyObject *value) return element_setitem(op, i, value); } else if (PySlice_Check(item)) { + // Note: 'slicelen' is computed once we are sure that 'self->extra' + // cannot be mutated by user-defined code. + // See https://github.com/python/cpython/issues/143200. Py_ssize_t start, stop, step, slicelen, newlen, i; size_t cur; PyObject* recycle = NULL; PyObject* seq; - if (!self->extra) { - if (create_extra(self, NULL) < 0) - return -1; - } - if (PySlice_Unpack(item, &start, &stop, &step) < 0) { return -1; } - slicelen = PySlice_AdjustIndices(self->extra->length, &start, &stop, - step); if (value == NULL) { /* Delete slice */ - size_t cur; - Py_ssize_t i; - + if (self->extra == NULL) { + return 0; + } + slicelen = PySlice_AdjustIndices(self->extra->length, &start, &stop, + step); if (slicelen <= 0) return 0; @@ -1948,8 +1967,16 @@ element_ass_subscr(PyObject *op, PyObject *item, PyObject *value) } newlen = PySequence_Fast_GET_SIZE(seq); - if (step != 1 && newlen != slicelen) - { + if (self->extra == NULL) { + if (create_extra(self, NULL) < 0) { + Py_DECREF(seq); + return -1; + } + } + slicelen = PySlice_AdjustIndices(self->extra->length, &start, &stop, + step); + + if (step != 1 && newlen != slicelen) { Py_DECREF(seq); PyErr_Format(PyExc_ValueError, "attempt to assign sequence of size %zd " @@ -2037,16 +2064,14 @@ static PyObject* element_text_getter(PyObject *op, void *closure) { ElementObject *self = _Element_CAST(op); - PyObject *res = element_get_text(self); - return Py_XNewRef(res); + return element_get_text(self); } static PyObject* element_tail_getter(PyObject *op, void *closure) { ElementObject *self = _Element_CAST(op); - PyObject *res = element_get_tail(self); - return Py_XNewRef(res); + return element_get_tail(self); } static PyObject* @@ -2102,10 +2127,10 @@ static int element_attrib_setter(PyObject *op, PyObject *value, void *closure) { _VALIDATE_ATTR_VALUE(value); - if (!PyDict_Check(value)) { + if (!PyAnyDict_Check(value)) { PyErr_Format(PyExc_TypeError, - "attrib must be dict, not %.200s", - Py_TYPE(value)->tp_name); + "attrib must be dict or frozendict, not %T", + value); return -1; } ElementObject *self = _Element_CAST(op); @@ -2272,6 +2297,10 @@ elementiter_next(PyObject *op) return NULL; } if (it->gettext) { + if (elem->tag != Py_None && !PyUnicode_Check(elem->tag)) { + Py_DECREF(elem); + continue; + } text = element_get_text(elem); goto gettext; } @@ -2289,16 +2318,14 @@ elementiter_next(PyObject *op) continue; gettext: + Py_DECREF(elem); if (!text) { - Py_DECREF(elem); return NULL; } if (text == Py_None) { - Py_DECREF(elem); + Py_DECREF(text); } else { - Py_INCREF(text); - Py_DECREF(elem); rc = PyObject_IsTrue(text); if (rc > 0) return text; @@ -2547,6 +2574,7 @@ treebuilder_dealloc(PyObject *self) /* helpers for handling of arbitrary element-like objects */ /*[clinic input] +@permit_long_summary _elementtree._set_factories comment_factory: object @@ -2561,7 +2589,7 @@ For internal use only. static PyObject * _elementtree__set_factories_impl(PyObject *module, PyObject *comment_factory, PyObject *pi_factory) -/*[clinic end generated code: output=813b408adee26535 input=99d17627aea7fb3b]*/ +/*[clinic end generated code: output=813b408adee26535 input=0f415cb6b821f768]*/ { elementtreestate *st = get_elementtree_state(module); PyObject *old; @@ -2577,7 +2605,7 @@ _elementtree__set_factories_impl(PyObject *module, PyObject *comment_factory, return NULL; } - old = PyTuple_Pack(2, + old = _PyTuple_FromPair( st->comment_factory ? st->comment_factory : Py_None, st->pi_factory ? st->pi_factory : Py_None); @@ -2695,7 +2723,7 @@ treebuilder_append_event(TreeBuilderObject *self, PyObject *action, { if (action != NULL) { PyObject *res; - PyObject *event = PyTuple_Pack(2, action, node); + PyObject *event = _PyTuple_FromPair(action, node); if (event == NULL) return -1; res = PyObject_CallOneArg(self->events_append, event); @@ -2794,8 +2822,9 @@ treebuilder_handle_data(TreeBuilderObject* self, PyObject* data) self->data = Py_NewRef(data); } else { /* more than one item; use a list to collect items */ - if (PyBytes_CheckExact(self->data) && Py_REFCNT(self->data) == 1 && - PyBytes_CheckExact(data) && PyBytes_GET_SIZE(data) == 1) { + if (PyBytes_CheckExact(self->data) + && _PyObject_IsUniquelyReferenced(self->data) + && PyBytes_CheckExact(data) && PyBytes_GET_SIZE(data) == 1) { /* XXX this code path unused in Python 3? */ /* expat often generates single character data sections; handle the most common case by resizing the existing string... */ @@ -2822,8 +2851,6 @@ treebuilder_handle_data(TreeBuilderObject* self, PyObject* data) LOCAL(PyObject*) treebuilder_handle_end(TreeBuilderObject* self, PyObject* tag) { - PyObject* item; - if (treebuilder_flush_data(self) < 0) { return NULL; } @@ -2836,17 +2863,22 @@ treebuilder_handle_end(TreeBuilderObject* self, PyObject* tag) return NULL; } - item = self->last; - self->last = Py_NewRef(self->this); - Py_XSETREF(self->last_for_tail, self->last); + PyObject *last = self->last; + PyObject *last_for_tail = self->last_for_tail; + PyObject *this = self->this; + self->last = Py_NewRef(this); + self->last_for_tail = Py_NewRef(this); self->index--; self->this = Py_NewRef(PyList_GET_ITEM(self->stack, self->index)); - Py_DECREF(item); + Py_DECREF(last); + Py_XDECREF(last_for_tail); - if (treebuilder_append_event(self, self->end_event_obj, self->last) < 0) + if (treebuilder_append_event(self, self->end_event_obj, self->last) < 0) { + Py_DECREF(this); return NULL; + } - return Py_NewRef(self->last); + return this; } LOCAL(PyObject*) @@ -2912,7 +2944,7 @@ treebuilder_handle_pi(TreeBuilderObject* self, PyObject* target, PyObject* text) Py_XSETREF(self->last_for_tail, Py_NewRef(pi)); } } else { - pi = PyTuple_Pack(2, target, text); + pi = _PyTuple_FromPair(target, text); if (!pi) { return NULL; } @@ -2936,7 +2968,7 @@ treebuilder_handle_start_ns(TreeBuilderObject* self, PyObject* prefix, PyObject* PyObject* parcel; if (self->events_append && self->start_ns_event_obj) { - parcel = PyTuple_Pack(2, prefix, uri); + parcel = _PyTuple_FromPair(prefix, uri); if (!parcel) { return NULL; } @@ -3708,8 +3740,12 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *target, PyErr_NoMemory(); return -1; } - /* expat < 2.1.0 has no XML_SetHashSalt() */ - if (EXPAT(st, SetHashSalt) != NULL) { + // Prefer 16-byte entropy, only expat >= 2.8.0. See gh-149018 + if (EXPAT(st, SetHashSalt16Bytes) != NULL) { + EXPAT(st, SetHashSalt16Bytes)(self->parser, + _Py_HashSecret.expat.hashsalt16); + } + else if (EXPAT(st, SetHashSalt) != NULL) { EXPAT(st, SetHashSalt)(self->parser, (unsigned long)_Py_HashSecret.expat.hashsalt); } @@ -4214,8 +4250,8 @@ _elementtree_XMLParser__setevents_impl(XMLParserObject *self, (XML_ProcessingInstructionHandler) expat_pi_handler ); } else { - Py_DECREF(events_seq); PyErr_Format(PyExc_ValueError, "unknown event '%s'", event_name); + Py_DECREF(events_seq); return NULL; } } @@ -4512,6 +4548,7 @@ module_exec(PyObject *m) } static struct PyModuleDef_Slot elementtree_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 1c888295cb07f1..b4595c55d519b9 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -108,20 +108,13 @@ placeholder_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return placeholder; } -static int -placeholder_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static PyType_Slot placeholder_type_slots[] = { {Py_tp_dealloc, placeholder_dealloc}, {Py_tp_repr, placeholder_repr}, {Py_tp_doc, (void *)placeholder_doc}, {Py_tp_methods, placeholder_methods}, {Py_tp_new, placeholder_new}, - {Py_tp_traverse, placeholder_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {0, 0} }; @@ -259,6 +252,11 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) } PyObject *item; PyObject *tot_args = PyTuple_New(tot_nargs); + if (tot_args == NULL) { + Py_DECREF(new_args); + Py_DECREF(pto); + return NULL; + } for (Py_ssize_t i = 0, j = 0; i < tot_nargs; i++) { if (i < npargs) { item = PyTuple_GET_ITEM(pto_args, i); @@ -298,7 +296,7 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (kw == NULL) { pto->kw = PyDict_New(); } - else if (Py_REFCNT(kw) == 1) { + else if (_PyObject_IsUniquelyReferenced(kw)) { pto->kw = Py_NewRef(kw); } else { @@ -459,7 +457,11 @@ partial_vectorcall(PyObject *self, PyObject *const *args, for (Py_ssize_t i = 0; i < nkwds; ++i) { key = PyTuple_GET_ITEM(kwnames, i); val = args[nargs + i]; - if (PyDict_Contains(pto->kw, key)) { + int contains = PyDict_Contains(pto->kw, key); + if (contains < 0) { + goto error; + } + else if (contains == 1) { if (pto_kw_merged == NULL) { pto_kw_merged = PyDict_Copy(pto->kw); if (pto_kw_merged == NULL) { @@ -494,12 +496,15 @@ partial_vectorcall(PyObject *self, PyObject *const *args, /* Copy pto_keywords with overlapping call keywords merged * Note, tail is already coppied. */ Py_ssize_t pos = 0, i = 0; - while (PyDict_Next(n_merges ? pto_kw_merged : pto->kw, &pos, &key, &val)) { + PyObject *keyword_dict = n_merges ? pto_kw_merged : pto->kw; + Py_BEGIN_CRITICAL_SECTION(keyword_dict); + while (PyDict_Next(keyword_dict, &pos, &key, &val)) { assert(i < pto_nkwds); PyTuple_SET_ITEM(tot_kwnames, i, Py_NewRef(key)); stack[tot_nargs + i] = val; i++; } + Py_END_CRITICAL_SECTION(); assert(i == pto_nkwds); Py_XDECREF(pto_kw_merged); @@ -695,65 +700,79 @@ partial_repr(PyObject *self) { partialobject *pto = partialobject_CAST(self); PyObject *result = NULL; - PyObject *arglist; - PyObject *mod; - PyObject *name; + PyObject *arglist = NULL; + PyObject *mod = NULL; + PyObject *name = NULL; Py_ssize_t i, n; PyObject *key, *value; int status; status = Py_ReprEnter(self); if (status != 0) { - if (status < 0) + if (status < 0) { return NULL; + } return PyUnicode_FromString("..."); } + /* Reference arguments in case they change */ + PyObject *fn = Py_NewRef(pto->fn); + PyObject *args = Py_NewRef(pto->args); + PyObject *kw = Py_NewRef(pto->kw); + assert(PyTuple_Check(args)); + assert(PyDict_Check(kw)); arglist = Py_GetConstant(Py_CONSTANT_EMPTY_STR); - if (arglist == NULL) + if (arglist == NULL) { goto done; + } /* Pack positional arguments */ - assert(PyTuple_Check(pto->args)); - n = PyTuple_GET_SIZE(pto->args); + n = PyTuple_GET_SIZE(args); for (i = 0; i < n; i++) { Py_SETREF(arglist, PyUnicode_FromFormat("%U, %R", arglist, - PyTuple_GET_ITEM(pto->args, i))); - if (arglist == NULL) + PyTuple_GET_ITEM(args, i))); + if (arglist == NULL) { goto done; + } } /* Pack keyword arguments */ - assert (PyDict_Check(pto->kw)); - for (i = 0; PyDict_Next(pto->kw, &i, &key, &value);) { + int error = 0; + Py_BEGIN_CRITICAL_SECTION(kw); + for (i = 0; PyDict_Next(kw, &i, &key, &value);) { /* Prevent key.__str__ from deleting the value. */ Py_INCREF(value); Py_SETREF(arglist, PyUnicode_FromFormat("%U, %S=%R", arglist, key, value)); Py_DECREF(value); - if (arglist == NULL) - goto done; + if (arglist == NULL) { + error = 1; + break; + } + } + Py_END_CRITICAL_SECTION(); + if (error) { + goto done; } mod = PyType_GetModuleName(Py_TYPE(pto)); if (mod == NULL) { - goto error; + goto done; } + name = PyType_GetQualName(Py_TYPE(pto)); if (name == NULL) { - Py_DECREF(mod); - goto error; + goto done; } - result = PyUnicode_FromFormat("%S.%S(%R%U)", mod, name, pto->fn, arglist); - Py_DECREF(mod); - Py_DECREF(name); - Py_DECREF(arglist); - done: + result = PyUnicode_FromFormat("%S.%S(%R%U)", mod, name, fn, arglist); +done: + Py_XDECREF(name); + Py_XDECREF(mod); + Py_XDECREF(arglist); + Py_DECREF(fn); + Py_DECREF(args); + Py_DECREF(kw); Py_ReprLeave(self); return result; - error: - Py_DECREF(arglist); - Py_ReprLeave(self); - return NULL; } /* Pickle strategy: @@ -785,7 +804,8 @@ partial_setstate(PyObject *self, PyObject *state) if (!PyArg_ParseTuple(state, "OOOO", &fn, &fnargs, &kw, &dict) || !PyCallable_Check(fn) || !PyTuple_Check(fnargs) || - (kw != Py_None && !PyDict_Check(kw))) + (kw != Py_None && !PyDict_Check(kw)) || + (dict != Py_None && !PyDict_Check(dict))) { PyErr_SetString(PyExc_TypeError, "invalid partial state"); return NULL; @@ -840,7 +860,8 @@ static PyMethodDef partial_methods[] = { {"__reduce__", partial_reduce, METH_NOARGS}, {"__setstate__", partial_setstate, METH_O}, {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, + PyDoc_STR("partial is generic over the wrapped function's return type")}, {NULL, NULL} /* sentinel */ }; @@ -1039,18 +1060,19 @@ _functools_cmp_to_key_impl(PyObject *module, PyObject *mycmp) /* reduce (used to be a builtin) ********************************************/ /*[clinic input] +@permit_long_summary _functools.reduce function as func: object iterable as seq: object / - initial as result: object = NULL + initial as result: object(c_default="NULL") = functools._initial_missing Apply a function of two arguments cumulatively to the items of an iterable, from left to right. -This effectively reduces the iterable to a single value. If initial is present, -it is placed before the items of the iterable in the calculation, and serves as -a default when the iterable is empty. +This effectively reduces the iterable to a single value. If initial is +present, it is placed before the items of the iterable in the +calculation, and serves as a default when the iterable is empty. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates ((((1 + 2) + 3) + 4) + 5). @@ -1059,7 +1081,7 @@ calculates ((((1 + 2) + 3) + 4) + 5). static PyObject * _functools_reduce_impl(PyObject *module, PyObject *func, PyObject *seq, PyObject *result) -/*[clinic end generated code: output=30d898fe1267c79d input=1511e9a8c38581ac]*/ +/*[clinic end generated code: output=30d898fe1267c79d input=ff4d5c73100e72e8]*/ { PyObject *args, *it; @@ -1081,7 +1103,7 @@ _functools_reduce_impl(PyObject *module, PyObject *func, PyObject *seq, for (;;) { PyObject *op2; - if (Py_REFCNT(args) > 1) { + if (!_PyObject_IsUniquelyReferenced(args)) { Py_DECREF(args); if ((args = PyTuple_New(2)) == NULL) goto Fail; @@ -1996,6 +2018,7 @@ _functools_free(void *module) } static struct PyModuleDef_Slot _functools_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _functools_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 6a4939512b22fc..20d482021656a5 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -8,11 +8,12 @@ #endif #include "Python.h" -#include "pycore_pyerrors.h" // _PyErr_SetLocaleString() +#include "pycore_object.h" // _PyObject_VisitType() +#include "pycore_pyerrors.h" // _PyErr_SetLocaleString() #include "gdbm.h" #include <fcntl.h> -#include <stdlib.h> // free() +#include <stdlib.h> // free() #include <sys/stat.h> #include <sys/types.h> @@ -122,13 +123,6 @@ newgdbmobject(_gdbm_state *state, const char *file, int flags, int mode) } /* Methods */ -static int -gdbm_traverse(PyObject *op, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(op)); - return 0; -} - static void gdbm_dealloc(PyObject *op) { @@ -377,6 +371,7 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w) } /*[clinic input] +@permit_long_summary @critical_section _gdbm.gdbm.setdefault @@ -390,7 +385,7 @@ Get value for key, or set it to default and return default if not present. static PyObject * _gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=f3246e880509f142 input=854374cd81ab51b6]*/ +/*[clinic end generated code: output=f3246e880509f142 input=f4008b358165bbb8]*/ { PyObject *res; @@ -525,14 +520,14 @@ _gdbm.gdbm.firstkey Return the starting key for the traversal. -It's possible to loop over every key in the database using this method -and the nextkey() method. The traversal is ordered by GDBM's internal -hash values, and won't be sorted by the key values. +It's possible to loop over every key in the database using this +method and the nextkey() method. The traversal is ordered by GDBM's +internal hash values, and won't be sorted by the key values. [clinic start generated code]*/ static PyObject * _gdbm_gdbm_firstkey_impl(gdbmobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=139275e9c8b60827 input=aad5a7c886c542f5]*/ +/*[clinic end generated code: output=139275e9c8b60827 input=ba40f0d81eae0f35]*/ { PyObject *v; datum key; @@ -561,8 +556,8 @@ _gdbm.gdbm.nextkey Returns the key that follows key in the traversal. -The following code prints every key in the database db, without having -to create a list in memory that contains them all: +The following code prints every key in the database db, without +having to create a list in memory that contains them all: k = db.firstkey() while k is not None: @@ -573,7 +568,7 @@ to create a list in memory that contains them all: static PyObject * _gdbm_gdbm_nextkey_impl(gdbmobject *self, PyTypeObject *cls, const char *key, Py_ssize_t key_length) -/*[clinic end generated code: output=c81a69300ef41766 input=181f1130d5bfeb1e]*/ +/*[clinic end generated code: output=c81a69300ef41766 input=78293a913b02387e]*/ { PyObject *v; datum dbm_key, nextkey; @@ -604,14 +599,14 @@ Reorganize the database. If you have carried out a lot of deletions and would like to shrink the space used by the GDBM file, this routine will reorganize the -database. GDBM will not shorten the length of a database file except -by using this reorganization; otherwise, deleted file space will be -kept and reused as new (key,value) pairs are added. +database. GDBM will not shorten the length of a database file +except by using this reorganization; otherwise, deleted file space +will be kept and reused as new (key,value) pairs are added. [clinic start generated code]*/ static PyObject * _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls) -/*[clinic end generated code: output=d77c69e8e3dd644a input=3e3ca0d2ea787861]*/ +/*[clinic end generated code: output=d77c69e8e3dd644a input=d7fcf03051c6f7cd]*/ { _gdbm_state *state = PyType_GetModuleState(cls); assert(state != NULL); @@ -678,8 +673,10 @@ _gdbm_gdbm_clear_impl(gdbmobject *self, PyTypeObject *cls) } if (gdbm_delete(self->di_dbm, key) < 0) { PyErr_SetString(state->gdbm_error, "cannot delete item from database"); + free(key.dptr); return NULL; } + free(key.dptr); } Py_RETURN_NONE; } @@ -693,7 +690,11 @@ gdbm__enter__(PyObject *self, PyObject *args) static PyObject * gdbm__exit__(PyObject *self, PyObject *args) { - return _gdbm_gdbm_close_impl((gdbmobject *)self); + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(self); + result = _gdbm_gdbm_close_impl((gdbmobject *)self); + Py_END_CRITICAL_SECTION(); + return result; } static PyMethodDef gdbm_methods[] = { @@ -713,7 +714,7 @@ static PyMethodDef gdbm_methods[] = { static PyType_Slot gdbmtype_spec_slots[] = { {Py_tp_dealloc, gdbm_dealloc}, - {Py_tp_traverse, gdbm_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_methods, gdbm_methods}, {Py_sq_contains, gdbm_contains}, {Py_mp_length, gdbm_length}, @@ -813,11 +814,6 @@ dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, case 'u': iflags |= GDBM_NOLOCK; break; -#endif -#ifdef GDBM_NOMMAP - case 'm': - iflags |= GDBM_NOMMAP; - break; #endif default: PyErr_Format(state->gdbm_error, @@ -851,9 +847,6 @@ static const char gdbmmodule_open_flags[] = "rwcn" #endif #ifdef GDBM_NOLOCK "u" -#endif -#ifdef GDBM_NOMMAP - "m" #endif ; @@ -919,6 +912,7 @@ _gdbm_module_free(void *module) } static PyModuleDef_Slot _gdbm_module_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _gdbm_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_hacl/Hacl_HMAC.h b/Modules/_hacl/Hacl_HMAC.h index 10ff15183f2834..7dca53c9254546 100644 --- a/Modules/_hacl/Hacl_HMAC.h +++ b/Modules/_hacl/Hacl_HMAC.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_HMAC_H -#define __Hacl_HMAC_H +#ifndef Hacl_HMAC_H +#define Hacl_HMAC_H #if defined(__cplusplus) extern "C" { @@ -220,5 +220,5 @@ Hacl_HMAC_compute_blake2b_32( } #endif -#define __Hacl_HMAC_H_DEFINED -#endif +#define Hacl_HMAC_H_DEFINED +#endif /* Hacl_HMAC_H */ diff --git a/Modules/_hacl/Hacl_Hash_Blake2b.h b/Modules/_hacl/Hacl_Hash_Blake2b.h index 3a73f358c98cc3..b07893ba339245 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2b.h +++ b/Modules/_hacl/Hacl_Hash_Blake2b.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_Blake2b_H -#define __Hacl_Hash_Blake2b_H +#ifndef Hacl_Hash_Blake2b_H +#define Hacl_Hash_Blake2b_H #if defined(__cplusplus) extern "C" { @@ -220,5 +220,5 @@ Hacl_Hash_Blake2b_hash_with_key_and_params( } #endif -#define __Hacl_Hash_Blake2b_H_DEFINED -#endif +#define Hacl_Hash_Blake2b_H_DEFINED +#endif /* Hacl_Hash_Blake2b_H */ diff --git a/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h b/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h index 0271ab8e024e51..8be8f32db62b15 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h +++ b/Modules/_hacl/Hacl_Hash_Blake2b_Simd256.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_Blake2b_Simd256_H -#define __Hacl_Hash_Blake2b_Simd256_H +#ifndef Hacl_Hash_Blake2b_Simd256_H +#define Hacl_Hash_Blake2b_Simd256_H #if defined(__cplusplus) extern "C" { @@ -206,5 +206,5 @@ Hacl_Hash_Blake2b_Simd256_hash_with_key_and_params( } #endif -#define __Hacl_Hash_Blake2b_Simd256_H_DEFINED -#endif +#define Hacl_Hash_Blake2b_Simd256_H_DEFINED +#endif /* Hacl_Hash_Blake2b_Simd256_H */ diff --git a/Modules/_hacl/Hacl_Hash_Blake2s.h b/Modules/_hacl/Hacl_Hash_Blake2s.h index fbf8cff5cd1073..2582a3d34e34fc 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2s.h +++ b/Modules/_hacl/Hacl_Hash_Blake2s.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_Blake2s_H -#define __Hacl_Hash_Blake2s_H +#ifndef Hacl_Hash_Blake2s_H +#define Hacl_Hash_Blake2s_H #if defined(__cplusplus) extern "C" { @@ -198,5 +198,5 @@ Hacl_Hash_Blake2s_hash_with_key_and_params( } #endif -#define __Hacl_Hash_Blake2s_H_DEFINED -#endif +#define Hacl_Hash_Blake2s_H_DEFINED +#endif /* Hacl_Hash_Blake2s_H */ diff --git a/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h b/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h index 56456e6fbb3df8..4b3b4e4fbb5630 100644 --- a/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h +++ b/Modules/_hacl/Hacl_Hash_Blake2s_Simd128.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_Blake2s_Simd128_H -#define __Hacl_Hash_Blake2s_Simd128_H +#ifndef Hacl_Hash_Blake2s_Simd128_H +#define Hacl_Hash_Blake2s_Simd128_H #if defined(__cplusplus) extern "C" { @@ -206,5 +206,5 @@ Hacl_Hash_Blake2s_Simd128_hash_with_key_and_params( } #endif -#define __Hacl_Hash_Blake2s_Simd128_H_DEFINED -#endif +#define Hacl_Hash_Blake2s_Simd128_H_DEFINED +#endif /* Hacl_Hash_Blake2s_Simd128_H */ diff --git a/Modules/_hacl/Hacl_Hash_MD5.h b/Modules/_hacl/Hacl_Hash_MD5.h index 521c2addc50289..b220bbf6890ffa 100644 --- a/Modules/_hacl/Hacl_Hash_MD5.h +++ b/Modules/_hacl/Hacl_Hash_MD5.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_MD5_H -#define __Hacl_Hash_MD5_H +#ifndef Hacl_Hash_MD5_H +#define Hacl_Hash_MD5_H #if defined(__cplusplus) extern "C" { @@ -62,5 +62,5 @@ void Hacl_Hash_MD5_hash(uint8_t *output, uint8_t *input, uint32_t input_len); } #endif -#define __Hacl_Hash_MD5_H_DEFINED -#endif +#define Hacl_Hash_MD5_H_DEFINED +#endif /* Hacl_Hash_MD5_H */ diff --git a/Modules/_hacl/Hacl_Hash_SHA1.h b/Modules/_hacl/Hacl_Hash_SHA1.h index 63ac83f9dce018..7fc8c8cfd205d9 100644 --- a/Modules/_hacl/Hacl_Hash_SHA1.h +++ b/Modules/_hacl/Hacl_Hash_SHA1.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_SHA1_H -#define __Hacl_Hash_SHA1_H +#ifndef Hacl_Hash_SHA1_H +#define Hacl_Hash_SHA1_H #if defined(__cplusplus) extern "C" { @@ -62,5 +62,5 @@ void Hacl_Hash_SHA1_hash(uint8_t *output, uint8_t *input, uint32_t input_len); } #endif -#define __Hacl_Hash_SHA1_H_DEFINED -#endif +#define Hacl_Hash_SHA1_H_DEFINED +#endif /* Hacl_Hash_SHA1_H */ diff --git a/Modules/_hacl/Hacl_Hash_SHA2.h b/Modules/_hacl/Hacl_Hash_SHA2.h index a93138fb7ee7a7..dd168b8aa1a424 100644 --- a/Modules/_hacl/Hacl_Hash_SHA2.h +++ b/Modules/_hacl/Hacl_Hash_SHA2.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_SHA2_H -#define __Hacl_Hash_SHA2_H +#ifndef Hacl_Hash_SHA2_H +#define Hacl_Hash_SHA2_H #if defined(__cplusplus) extern "C" { @@ -199,5 +199,5 @@ void Hacl_Hash_SHA2_hash_384(uint8_t *output, uint8_t *input, uint32_t input_len } #endif -#define __Hacl_Hash_SHA2_H_DEFINED -#endif +#define Hacl_Hash_SHA2_H_DEFINED +#endif /* Hacl_Hash_SHA2_H */ diff --git a/Modules/_hacl/Hacl_Hash_SHA3.h b/Modules/_hacl/Hacl_Hash_SHA3.h index 65ec99ee3a3ac9..df6ebe439e98b1 100644 --- a/Modules/_hacl/Hacl_Hash_SHA3.h +++ b/Modules/_hacl/Hacl_Hash_SHA3.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Hash_SHA3_H -#define __Hacl_Hash_SHA3_H +#ifndef Hacl_Hash_SHA3_H +#define Hacl_Hash_SHA3_H #if defined(__cplusplus) extern "C" { @@ -155,5 +155,5 @@ Hacl_Hash_SHA3_shake128_squeeze_nblocks( } #endif -#define __Hacl_Hash_SHA3_H_DEFINED -#endif +#define Hacl_Hash_SHA3_H_DEFINED +#endif /* Hacl_Hash_SHA3_H */ diff --git a/Modules/_hacl/Hacl_Streaming_HMAC.c b/Modules/_hacl/Hacl_Streaming_HMAC.c index 8dd7e2c0bf3e71..f89c00ee1ae416 100644 --- a/Modules/_hacl/Hacl_Streaming_HMAC.c +++ b/Modules/_hacl/Hacl_Streaming_HMAC.c @@ -2375,9 +2375,13 @@ Hacl_Streaming_HMAC_digest( Hacl_Agile_Hash_state_s *s112 = tmp_block_state1.snd; update_multi(s112, prev_len, buf_multi, 0U); uint64_t prev_len_last = total_len - (uint64_t)r; - Hacl_Agile_Hash_state_s *s11 = tmp_block_state1.snd; - update_last(s11, prev_len_last, buf_last, r); + Hacl_Agile_Hash_state_s *s113 = tmp_block_state1.snd; + update_last(s113, prev_len_last, buf_last, r); finish0(tmp_block_state1, output); + Hacl_Agile_Hash_state_s *s210 = tmp_block_state1.thd; + Hacl_Agile_Hash_state_s *s11 = tmp_block_state1.snd; + free_(s11); + free_(s210); return Hacl_Streaming_Types_Success; } KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", diff --git a/Modules/_hacl/Hacl_Streaming_HMAC.h b/Modules/_hacl/Hacl_Streaming_HMAC.h index a0806c02d0bb9f..6c302b1f92d6a3 100644 --- a/Modules/_hacl/Hacl_Streaming_HMAC.h +++ b/Modules/_hacl/Hacl_Streaming_HMAC.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Streaming_HMAC_H -#define __Hacl_Streaming_HMAC_H +#ifndef Hacl_Streaming_HMAC_H +#define Hacl_Streaming_HMAC_H #if defined(__cplusplus) extern "C" { @@ -130,5 +130,5 @@ Hacl_Streaming_HMAC_agile_state } #endif -#define __Hacl_Streaming_HMAC_H_DEFINED -#endif +#define Hacl_Streaming_HMAC_H_DEFINED +#endif /* Hacl_Streaming_HMAC_H */ diff --git a/Modules/_hacl/Hacl_Streaming_Types.h b/Modules/_hacl/Hacl_Streaming_Types.h index 5c497750793720..cce25eb7946750 100644 --- a/Modules/_hacl/Hacl_Streaming_Types.h +++ b/Modules/_hacl/Hacl_Streaming_Types.h @@ -23,8 +23,8 @@ */ -#ifndef __Hacl_Streaming_Types_H -#define __Hacl_Streaming_Types_H +#ifndef Hacl_Streaming_Types_H +#define Hacl_Streaming_Types_H #if defined(__cplusplus) extern "C" { @@ -68,5 +68,5 @@ typedef struct Hacl_Streaming_MD_state_64_s Hacl_Streaming_MD_state_64; } #endif -#define __Hacl_Streaming_Types_H_DEFINED -#endif +#define Hacl_Streaming_Types_H_DEFINED +#endif /* Hacl_Streaming_Types_H */ diff --git a/Modules/_hacl/Lib_Memzero0.c b/Modules/_hacl/Lib_Memzero0.c index f94e0e2254a912..7ab722e737c2ef 100644 --- a/Modules/_hacl/Lib_Memzero0.c +++ b/Modules/_hacl/Lib_Memzero0.c @@ -31,7 +31,7 @@ #include <string.h> #endif -#if defined(__FreeBSD__) || defined(__NetBSD__) +#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__NetBSD__) #include <strings.h> #endif @@ -57,7 +57,7 @@ void Lib_Memzero0_memzero0(void *dst, uint64_t len) { SecureZeroMemory(dst, len_); #elif defined(__APPLE__) && defined(__MACH__) && defined(APPLE_HAS_MEMSET_S) memset_s(dst, len_, 0, len_); - #elif (defined(__linux__) && !defined(LINUX_NO_EXPLICIT_BZERO)) || defined(__FreeBSD__) || defined(__OpenBSD__) + #elif (defined(__linux__) && !defined(LINUX_NO_EXPLICIT_BZERO)) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__OpenBSD__) explicit_bzero(dst, len_); #elif defined(__NetBSD__) explicit_memset(dst, 0, len_); diff --git a/Modules/_hacl/include/krml/FStar_UInt128_Verified.h b/Modules/_hacl/include/krml/FStar_UInt128_Verified.h index f85982f33735d3..7aa2a6d83f9c4b 100644 --- a/Modules/_hacl/include/krml/FStar_UInt128_Verified.h +++ b/Modules/_hacl/include/krml/FStar_UInt128_Verified.h @@ -4,8 +4,8 @@ */ -#ifndef __FStar_UInt128_Verified_H -#define __FStar_UInt128_Verified_H +#ifndef FStar_UInt128_Verified_H +#define FStar_UInt128_Verified_H #include "FStar_UInt_8_16_32_64.h" #include <inttypes.h> @@ -331,5 +331,5 @@ static inline FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x, uint64_t } -#define __FStar_UInt128_Verified_H_DEFINED -#endif +#define FStar_UInt128_Verified_H_DEFINED +#endif /* FStar_UInt128_Verified_H */ diff --git a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h index 00be80836574af..2720348bbb79e6 100644 --- a/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h +++ b/Modules/_hacl/include/krml/FStar_UInt_8_16_32_64.h @@ -4,8 +4,8 @@ */ -#ifndef __FStar_UInt_8_16_32_64_H -#define __FStar_UInt_8_16_32_64_H +#ifndef FStar_UInt_8_16_32_64_H +#define FStar_UInt_8_16_32_64_H #include <inttypes.h> #include <stdbool.h> @@ -30,6 +30,8 @@ extern uint64_t FStar_UInt64_zero; extern uint64_t FStar_UInt64_one; +extern bool FStar_UInt64_ne(uint64_t a, uint64_t b); + extern uint64_t FStar_UInt64_minus(uint64_t a); extern uint32_t FStar_UInt64_n_minus_one; @@ -80,6 +82,8 @@ extern uint32_t FStar_UInt32_zero; extern uint32_t FStar_UInt32_one; +extern bool FStar_UInt32_ne(uint32_t a, uint32_t b); + extern uint32_t FStar_UInt32_minus(uint32_t a); extern uint32_t FStar_UInt32_n_minus_one; @@ -130,6 +134,8 @@ extern uint16_t FStar_UInt16_zero; extern uint16_t FStar_UInt16_one; +extern bool FStar_UInt16_ne(uint16_t a, uint16_t b); + extern uint16_t FStar_UInt16_minus(uint16_t a); extern uint32_t FStar_UInt16_n_minus_one; @@ -180,6 +186,8 @@ extern uint8_t FStar_UInt8_zero; extern uint8_t FStar_UInt8_one; +extern bool FStar_UInt8_ne(uint8_t a, uint8_t b); + extern uint8_t FStar_UInt8_minus(uint8_t a); extern uint32_t FStar_UInt8_n_minus_one; @@ -217,5 +225,5 @@ extern uint8_t FStar_UInt8_of_string(Prims_string uu___); typedef uint8_t FStar_UInt8_byte; -#define __FStar_UInt_8_16_32_64_H_DEFINED -#endif +#define FStar_UInt_8_16_32_64_H_DEFINED +#endif /* FStar_UInt_8_16_32_64_H */ diff --git a/Modules/_hacl/include/krml/internal/target.h b/Modules/_hacl/include/krml/internal/target.h index c592214634a3bd..73555ab5ca19a4 100644 --- a/Modules/_hacl/include/krml/internal/target.h +++ b/Modules/_hacl/include/krml/internal/target.h @@ -1,8 +1,8 @@ /* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 and MIT Licenses. */ -#ifndef __KRML_TARGET_H -#define __KRML_TARGET_H +#ifndef KRML_HEADER_TARGET_H +#define KRML_HEADER_TARGET_H #include <assert.h> #include <inttypes.h> @@ -12,6 +12,9 @@ #include <stdio.h> #include <stdlib.h> +typedef float float32_t; +typedef double float64_t; + /* Since KaRaMeL emits the inline keyword unconditionally, we follow the * guidelines at https://gcc.gnu.org/onlinedocs/gcc/Inline.html and make this * __inline__ to ensure the code compiles with -std=c90 and earlier. */ @@ -425,4 +428,4 @@ inline static int32_t krml_time(void) { #else # define KRML_MAYBE_FOR16(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif -#endif +#endif /* KRML_HEADER_TARGET_H */ diff --git a/Modules/_hacl/include/krml/internal/types.h b/Modules/_hacl/include/krml/internal/types.h index 2280dfad48db1e..d96ed19fcca5ac 100644 --- a/Modules/_hacl/include/krml/internal/types.h +++ b/Modules/_hacl/include/krml/internal/types.h @@ -92,7 +92,7 @@ typedef FStar_UInt128_uint128 FStar_UInt128_t, uint128_t; /* Avoid a circular loop: if this header is included via FStar_UInt8_16_32_64, * then don't bring the uint128 definitions into scope. */ -#ifndef __FStar_UInt_8_16_32_64_H +#ifndef FStar_UInt_8_16_32_64_H #if !defined(KRML_VERIFIED_UINT128) && defined(IS_MSVC64) #include "fstar_uint128_msvc.h" diff --git a/Modules/_hacl/include/krml/lowstar_endianness.h b/Modules/_hacl/include/krml/lowstar_endianness.h index af6b882cf259cc..5f706fa51aad10 100644 --- a/Modules/_hacl/include/krml/lowstar_endianness.h +++ b/Modules/_hacl/include/krml/lowstar_endianness.h @@ -1,8 +1,8 @@ /* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 and MIT Licenses. */ -#ifndef __LOWSTAR_ENDIANNESS_H -#define __LOWSTAR_ENDIANNESS_H +#ifndef KRML_HEADER_LOWSTAR_ENDIANNESS_H +#define KRML_HEADER_LOWSTAR_ENDIANNESS_H #include <string.h> #include <inttypes.h> @@ -228,4 +228,4 @@ inline static void store64(uint8_t *b, uint64_t i) { #define load128_be0 load128_be #define store128_be0 store128_be -#endif +#endif /* KRML_HEADER_LOWSTAR_ENDIANNESS_H */ diff --git a/Modules/_hacl/internal/Hacl_HMAC.h b/Modules/_hacl/internal/Hacl_HMAC.h index ad29d50760c3aa..ef9f25f5d420d3 100644 --- a/Modules/_hacl/internal/Hacl_HMAC.h +++ b/Modules/_hacl/internal/Hacl_HMAC.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_HMAC_H -#define __internal_Hacl_HMAC_H +#ifndef internal_Hacl_HMAC_H +#define internal_Hacl_HMAC_H #if defined(__cplusplus) extern "C" { @@ -48,5 +48,5 @@ K___uint32_t_uint32_t; } #endif -#define __internal_Hacl_HMAC_H_DEFINED -#endif +#define internal_Hacl_HMAC_H_DEFINED +#endif /* internal_Hacl_HMAC_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_Blake2b.h b/Modules/_hacl/internal/Hacl_Hash_Blake2b.h index e74c320e073f4b..7ca8e10e34cbbc 100644 --- a/Modules/_hacl/internal/Hacl_Hash_Blake2b.h +++ b/Modules/_hacl/internal/Hacl_Hash_Blake2b.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_Blake2b_H -#define __internal_Hacl_Hash_Blake2b_H +#ifndef internal_Hacl_Hash_Blake2b_H +#define internal_Hacl_Hash_Blake2b_H #if defined(__cplusplus) extern "C" { @@ -91,5 +91,5 @@ Hacl_Hash_Blake2b_state_t; } #endif -#define __internal_Hacl_Hash_Blake2b_H_DEFINED -#endif +#define internal_Hacl_Hash_Blake2b_H_DEFINED +#endif /* internal_Hacl_Hash_Blake2b_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h b/Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h index 27633f22b24f0d..6507d287922eec 100644 --- a/Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h +++ b/Modules/_hacl/internal/Hacl_Hash_Blake2b_Simd256.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_Blake2b_Simd256_H -#define __internal_Hacl_Hash_Blake2b_Simd256_H +#ifndef internal_Hacl_Hash_Blake2b_Simd256_H +#define internal_Hacl_Hash_Blake2b_Simd256_H #if defined(__cplusplus) extern "C" { @@ -134,5 +134,5 @@ Hacl_Hash_Blake2b_Simd256_state_t; } #endif -#define __internal_Hacl_Hash_Blake2b_Simd256_H_DEFINED -#endif +#define internal_Hacl_Hash_Blake2b_Simd256_H_DEFINED +#endif /* internal_Hacl_Hash_Blake2b_Simd256_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_Blake2s.h b/Modules/_hacl/internal/Hacl_Hash_Blake2s.h index 0c5781df8cea81..5fa5bb34efc0e0 100644 --- a/Modules/_hacl/internal/Hacl_Hash_Blake2s.h +++ b/Modules/_hacl/internal/Hacl_Hash_Blake2s.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_Blake2s_H -#define __internal_Hacl_Hash_Blake2s_H +#ifndef internal_Hacl_Hash_Blake2s_H +#define internal_Hacl_Hash_Blake2s_H #if defined(__cplusplus) extern "C" { @@ -90,5 +90,5 @@ Hacl_Hash_Blake2s_state_t; } #endif -#define __internal_Hacl_Hash_Blake2s_H_DEFINED -#endif +#define internal_Hacl_Hash_Blake2s_H_DEFINED +#endif /* internal_Hacl_Hash_Blake2s_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h b/Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h index 0f5db552d6cfed..3220cddac30288 100644 --- a/Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h +++ b/Modules/_hacl/internal/Hacl_Hash_Blake2s_Simd128.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_Blake2s_Simd128_H -#define __internal_Hacl_Hash_Blake2s_Simd128_H +#ifndef internal_Hacl_Hash_Blake2s_Simd128_H +#define internal_Hacl_Hash_Blake2s_Simd128_H #if defined(__cplusplus) extern "C" { @@ -134,5 +134,5 @@ Hacl_Hash_Blake2s_Simd128_state_t; } #endif -#define __internal_Hacl_Hash_Blake2s_Simd128_H_DEFINED -#endif +#define internal_Hacl_Hash_Blake2s_Simd128_H_DEFINED +#endif /* internal_Hacl_Hash_Blake2s_Simd128_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_MD5.h b/Modules/_hacl/internal/Hacl_Hash_MD5.h index 7fe71a49c6df85..d8761de99f5e2a 100644 --- a/Modules/_hacl/internal/Hacl_Hash_MD5.h +++ b/Modules/_hacl/internal/Hacl_Hash_MD5.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_MD5_H -#define __internal_Hacl_Hash_MD5_H +#ifndef internal_Hacl_Hash_MD5_H +#define internal_Hacl_Hash_MD5_H #if defined(__cplusplus) extern "C" { @@ -53,5 +53,5 @@ void Hacl_Hash_MD5_hash_oneshot(uint8_t *output, uint8_t *input, uint32_t input_ } #endif -#define __internal_Hacl_Hash_MD5_H_DEFINED -#endif +#define internal_Hacl_Hash_MD5_H_DEFINED +#endif /* internal_Hacl_Hash_MD5_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_SHA1.h b/Modules/_hacl/internal/Hacl_Hash_SHA1.h index ed53be559fe465..0e9f1c911f6165 100644 --- a/Modules/_hacl/internal/Hacl_Hash_SHA1.h +++ b/Modules/_hacl/internal/Hacl_Hash_SHA1.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_SHA1_H -#define __internal_Hacl_Hash_SHA1_H +#ifndef internal_Hacl_Hash_SHA1_H +#define internal_Hacl_Hash_SHA1_H #if defined(__cplusplus) extern "C" { @@ -52,5 +52,5 @@ void Hacl_Hash_SHA1_hash_oneshot(uint8_t *output, uint8_t *input, uint32_t input } #endif -#define __internal_Hacl_Hash_SHA1_H_DEFINED -#endif +#define internal_Hacl_Hash_SHA1_H_DEFINED +#endif /* internal_Hacl_Hash_SHA1_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_SHA2.h b/Modules/_hacl/internal/Hacl_Hash_SHA2.h index 98498ee9376996..e6750207980aef 100644 --- a/Modules/_hacl/internal/Hacl_Hash_SHA2.h +++ b/Modules/_hacl/internal/Hacl_Hash_SHA2.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_SHA2_H -#define __internal_Hacl_Hash_SHA2_H +#ifndef internal_Hacl_Hash_SHA2_H +#define internal_Hacl_Hash_SHA2_H #if defined(__cplusplus) extern "C" { @@ -161,5 +161,5 @@ void Hacl_Hash_SHA2_sha384_finish(uint64_t *st, uint8_t *h); } #endif -#define __internal_Hacl_Hash_SHA2_H_DEFINED -#endif +#define internal_Hacl_Hash_SHA2_H_DEFINED +#endif /* internal_Hacl_Hash_SHA2_H */ diff --git a/Modules/_hacl/internal/Hacl_Hash_SHA3.h b/Modules/_hacl/internal/Hacl_Hash_SHA3.h index e653c73b1d03f7..c08a4e7858bbab 100644 --- a/Modules/_hacl/internal/Hacl_Hash_SHA3.h +++ b/Modules/_hacl/internal/Hacl_Hash_SHA3.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Hash_SHA3_H -#define __internal_Hacl_Hash_SHA3_H +#ifndef internal_Hacl_Hash_SHA3_H +#define internal_Hacl_Hash_SHA3_H #if defined(__cplusplus) extern "C" { @@ -81,5 +81,5 @@ Hacl_Hash_SHA3_state_t; } #endif -#define __internal_Hacl_Hash_SHA3_H_DEFINED -#endif +#define internal_Hacl_Hash_SHA3_H_DEFINED +#endif /* internal_Hacl_Hash_SHA3_H */ diff --git a/Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h b/Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h index fb3a045cd5c608..2726cc484780e8 100644 --- a/Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h +++ b/Modules/_hacl/internal/Hacl_Impl_Blake2_Constants.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Impl_Blake2_Constants_H -#define __internal_Hacl_Impl_Blake2_Constants_H +#ifndef internal_Hacl_Impl_Blake2_Constants_H +#define internal_Hacl_Impl_Blake2_Constants_H #if defined(__cplusplus) extern "C" { @@ -69,5 +69,5 @@ Hacl_Hash_Blake2b_ivTable_B[8U] = } #endif -#define __internal_Hacl_Impl_Blake2_Constants_H_DEFINED -#endif +#define internal_Hacl_Impl_Blake2_Constants_H_DEFINED +#endif /* internal_Hacl_Impl_Blake2_Constants_H */ diff --git a/Modules/_hacl/internal/Hacl_Streaming_HMAC.h b/Modules/_hacl/internal/Hacl_Streaming_HMAC.h index acc4f3996026ee..fb44f707463e04 100644 --- a/Modules/_hacl/internal/Hacl_Streaming_HMAC.h +++ b/Modules/_hacl/internal/Hacl_Streaming_HMAC.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Streaming_HMAC_H -#define __internal_Hacl_Streaming_HMAC_H +#ifndef internal_Hacl_Streaming_HMAC_H +#define internal_Hacl_Streaming_HMAC_H #if defined(__cplusplus) extern "C" { @@ -90,5 +90,5 @@ Hacl_Streaming_HMAC_agile_state; } #endif -#define __internal_Hacl_Streaming_HMAC_H_DEFINED -#endif +#define internal_Hacl_Streaming_HMAC_H_DEFINED +#endif /* internal_Hacl_Streaming_HMAC_H */ diff --git a/Modules/_hacl/internal/Hacl_Streaming_Types.h b/Modules/_hacl/internal/Hacl_Streaming_Types.h index fed3cbd425917c..d6c4fc0d6255f7 100644 --- a/Modules/_hacl/internal/Hacl_Streaming_Types.h +++ b/Modules/_hacl/internal/Hacl_Streaming_Types.h @@ -23,8 +23,8 @@ */ -#ifndef __internal_Hacl_Streaming_Types_H -#define __internal_Hacl_Streaming_Types_H +#ifndef internal_Hacl_Streaming_Types_H +#define internal_Hacl_Streaming_Types_H #if defined(__cplusplus) extern "C" { @@ -83,5 +83,5 @@ Hacl_Streaming_MD_state_64; } #endif -#define __internal_Hacl_Streaming_Types_H_DEFINED -#endif +#define internal_Hacl_Streaming_Types_H_DEFINED +#endif /* internal_Hacl_Streaming_Types_H */ diff --git a/Modules/_hacl/libintvector.h b/Modules/_hacl/libintvector.h index 6db5253eee4f24..75ec2e0589830b 100644 --- a/Modules/_hacl/libintvector.h +++ b/Modules/_hacl/libintvector.h @@ -802,6 +802,18 @@ vector128 Lib_IntVector_Intrinsics_vec128_xor(vector128 x0, vector128 x1) { #if defined(HACL_CAN_COMPILE_VEC128) #include <altivec.h> + +/* GCC's AltiVec extension hijacks 'bool' as '__vector __bool int'. + Restore C99/C11 scalar bool for HACL* code. */ +#if defined(__GNUC__) && !defined(__clang__) +#undef bool +#define bool _Bool +#undef true +#define true 1 +#undef false +#define false 0 +#endif + #include <string.h> // for memcpy #include <stdint.h> diff --git a/Modules/_hacl/refresh.sh b/Modules/_hacl/refresh.sh index a6776282423b95..c73bd4ee90f4c5 100755 --- a/Modules/_hacl/refresh.sh +++ b/Modules/_hacl/refresh.sh @@ -22,7 +22,7 @@ fi # Update this when updating to a new version after verifying that the changes # the update brings in are good. -expected_hacl_star_rev=4ef25b547b377dcef855db4289c6a00580e7221c +expected_hacl_star_rev=504c2987452f87fe44bce9b9f12e19d6e051761f hacl_dir="$(realpath "$1")" cd "$(dirname "$0")" diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index d79e4b360e95c5..f895c9037485c4 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -30,7 +30,6 @@ /* EVP is the preferred interface to hashing in OpenSSL */ #include <openssl/evp.h> -#include <openssl/hmac.h> #include <openssl/crypto.h> // FIPS_mode() /* We use the object interface to discover what hashes OpenSSL supports. */ #include <openssl/objects.h> @@ -40,6 +39,10 @@ #if OPENSSL_VERSION_NUMBER >= 0x30000000L # define Py_HAS_OPENSSL3_SUPPORT +# include <openssl/core_names.h> // OSSL_MAC_PARAM_DIGEST +# include <openssl/params.h> // OSSL_PARAM_*() +#else +# include <openssl/hmac.h> // HMAC() #endif #ifndef OPENSSL_THREADS @@ -65,6 +68,10 @@ #define PY_EVP_MD_free(md) EVP_MD_free(md) #define PY_EVP_MD_CTX_md(CTX) EVP_MD_CTX_get0_md(CTX) + +#define PY_HMAC_CTX_TYPE EVP_MAC_CTX +#define PY_HMAC_CTX_free EVP_MAC_CTX_free +#define PY_HMAC_update EVP_MAC_update #else #define PY_EVP_MD const EVP_MD #define PY_EVP_MD_fetch(algorithm, properties) EVP_get_digestbyname(algorithm) @@ -72,6 +79,10 @@ #define PY_EVP_MD_free(md) do {} while(0) #define PY_EVP_MD_CTX_md(CTX) EVP_MD_CTX_md(CTX) + +#define PY_HMAC_CTX_TYPE HMAC_CTX +#define PY_HMAC_CTX_free HMAC_CTX_free +#define PY_HMAC_update HMAC_Update #endif /* @@ -257,7 +268,7 @@ py_hashentry_table_new(void) { if (h->py_alias != NULL) { if (_Py_hashtable_set(ht, (const void*)entry->py_alias, (void*)entry) < 0) { - PyMem_Free(entry); + /* entry is already in ht, will be freed by _Py_hashtable_destroy() */ goto error; } entry->refcnt++; @@ -283,6 +294,9 @@ typedef struct { PyObject *constructs; PyObject *unsupported_digestmod_error; _Py_hashtable_t *hashtable; +#ifdef Py_HAS_OPENSSL3_SUPPORT + EVP_MAC *evp_hmac; +#endif } _hashlibstate; static inline _hashlibstate* @@ -304,7 +318,12 @@ typedef struct { typedef struct { HASHLIB_OBJECT_HEAD - HMAC_CTX *ctx; /* OpenSSL hmac context */ +#ifdef Py_HAS_OPENSSL3_SUPPORT + EVP_MAC_CTX *ctx; /* OpenSSL HMAC EVP-based context */ + int evp_md_nid; /* needed to find the message digest name */ +#else + HMAC_CTX *ctx; /* OpenSSL HMAC plain context */ +#endif } HMACobject; #define HMACobject_CAST(op) ((HMACobject *)(op)) @@ -587,9 +606,14 @@ get_asn1_utf8name_by_nid(int nid) { const char *name = OBJ_nid2ln(nid); if (name == NULL) { - // In OpenSSL 3.0 and later, OBJ_nid*() are thread-safe and may raise. - assert(ERR_peek_last_error() != 0); - if (ERR_GET_REASON(ERR_peek_last_error()) != OBJ_R_UNKNOWN_NID) { + /* In OpenSSL 3.0 and later, OBJ_nid*() are thread-safe and may raise. + * However, not all versions of OpenSSL set a last error, so we simply + * ignore the last error if none exists. + * + * See https://github.com/python/cpython/issues/142451. + */ + unsigned long errcode = ERR_peek_last_error(); + if (errcode && ERR_GET_REASON(errcode) != OBJ_R_UNKNOWN_NID) { goto error; } // fallback to short name and unconditionally propagate errors @@ -617,6 +641,20 @@ get_hashlib_utf8name_by_nid(int nid) return e ? e->py_name : get_asn1_utf8name_by_nid(nid); } +#ifdef Py_HAS_OPENSSL3_SUPPORT +/* + * Convert the NID to an OpenSSL "canonical" cached, SN_* or LN_* digest name. + * + * On error, set an exception and return NULL. + */ +static const char * +get_openssl_utf8name_by_nid(int nid) +{ + const py_hashentry_t *e = get_hashentry_by_nid(nid); + return e ? e->ossl_name : get_asn1_utf8name_by_nid(nid); +} +#endif + /* Same as get_hashlib_utf8name_by_nid() but using an EVP_MD object. */ static const char * get_hashlib_utf8name_by_evp_md(const EVP_MD *md) @@ -733,6 +771,47 @@ get_openssl_evp_md(_hashlibstate *state, PyObject *digestmod, Py_hash_type py_ht return get_openssl_evp_md_by_utf8name(state, name, py_ht); } +#ifdef Py_HAS_OPENSSL3_SUPPORT +/* + * Get the "canonical" name of an EVP_MD described by 'digestmod' and purpose. + * + * On error, set an exception and return NULL. + * + * This function should not be used to construct the exposed Python name, + * but rather to invoke OpenSSL EVP_* functions. + */ +static const char * +get_openssl_digest_name(_hashlibstate *state, + PyObject *digestmod, Py_hash_type py_ht, + EVP_MD **evp_md) +{ + PY_EVP_MD *md = get_openssl_evp_md(state, digestmod, py_ht); + if (md == NULL) { + if (evp_md != NULL) { + *evp_md = NULL; + } + return NULL; + } + int nid = EVP_MD_nid(md); + const char *name = get_openssl_utf8name_by_nid(nid); + if (name == NULL) { + if (evp_md != NULL) { + *evp_md = NULL; + } + PY_EVP_MD_free(md); + raise_unsupported_algorithm_error(state, digestmod); + return NULL; + } + if (evp_md != NULL) { + *evp_md = md; + } + else { + PY_EVP_MD_free(md); + } + return name; +} +#endif + // --- OpenSSL HASH wrappers -------------------------------------------------- /* Thin wrapper around EVP_MD_CTX_new() which sets an exception on failure. */ @@ -927,6 +1006,7 @@ _hashlib_HASH_get_blocksize(PyObject *op, void *Py_UNUSED(closure)) { HASHobject *self = HASHobject_CAST(op); long block_size = EVP_MD_CTX_block_size(self->ctx); + assert(block_size > 0); return PyLong_FromLong(block_size); } @@ -935,6 +1015,7 @@ _hashlib_HASH_get_digestsize(PyObject *op, void *Py_UNUSED(closure)) { HASHobject *self = HASHobject_CAST(op); long size = EVP_MD_CTX_size(self->ctx); + assert(size > 0); return PyLong_FromLong(size); } @@ -1017,35 +1098,29 @@ static PyType_Spec HASHobject_type_spec = { /*[clinic input] _hashlib.HASHXOF.digest - length: Py_ssize_t + length: Py_ssize_t(allow_negative=False) Return the digest value as a bytes object. [clinic start generated code]*/ static PyObject * _hashlib_HASHXOF_digest_impl(HASHobject *self, Py_ssize_t length) -/*[clinic end generated code: output=dcb09335dd2fe908 input=3eb034ce03c55b21]*/ +/*[clinic end generated code: output=dcb09335dd2fe908 input=224d047da2c12a42]*/ { EVP_MD_CTX *temp_ctx; - PyObject *retval; - - if (length < 0) { - PyErr_SetString(PyExc_ValueError, "negative digest length"); - return NULL; - } if (length == 0) { return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } - retval = PyBytes_FromStringAndSize(NULL, length); - if (retval == NULL) { + PyBytesWriter *writer = PyBytesWriter_Create(length); + if (writer == NULL) { return NULL; } temp_ctx = py_wrapper_EVP_MD_CTX_new(); if (temp_ctx == NULL) { - Py_DECREF(retval); + PyBytesWriter_Discard(writer); return NULL; } @@ -1053,7 +1128,7 @@ _hashlib_HASHXOF_digest_impl(HASHobject *self, Py_ssize_t length) goto error; } if (!EVP_DigestFinalXOF(temp_ctx, - (unsigned char*)PyBytes_AS_STRING(retval), + (unsigned char*)PyBytesWriter_GetData(writer), length)) { notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_DigestFinalXOF)); @@ -1061,10 +1136,10 @@ _hashlib_HASHXOF_digest_impl(HASHobject *self, Py_ssize_t length) } EVP_MD_CTX_free(temp_ctx); - return retval; + return PyBytesWriter_Finish(writer); error: - Py_DECREF(retval); + PyBytesWriter_Discard(writer); EVP_MD_CTX_free(temp_ctx); return NULL; } @@ -1072,24 +1147,19 @@ _hashlib_HASHXOF_digest_impl(HASHobject *self, Py_ssize_t length) /*[clinic input] _hashlib.HASHXOF.hexdigest - length: Py_ssize_t + length: Py_ssize_t(allow_negative=False) Return the digest value as a string of hexadecimal digits. [clinic start generated code]*/ static PyObject * _hashlib_HASHXOF_hexdigest_impl(HASHobject *self, Py_ssize_t length) -/*[clinic end generated code: output=519431cafa014f39 input=0e58f7238adb7ab8]*/ +/*[clinic end generated code: output=519431cafa014f39 input=4a41b8ab5d3bfee2]*/ { unsigned char *digest; EVP_MD_CTX *temp_ctx; PyObject *retval; - if (length < 0) { - PyErr_SetString(PyExc_ValueError, "negative digest length"); - return NULL; - } - if (length == 0) { return Py_GetConstant(Py_CONSTANT_EMPTY_STR); } @@ -1251,15 +1321,11 @@ _hashlib_HASH(_hashlibstate *state, const char *digestname, PyObject *data_obj, return (PyObject *)self; } +// In Python 3.19, we can remove the "STRING" argument and would also be able +// to remove the macro (or keep it as an alias for better naming) since calls +// to _hashlib_HASH_new_impl() would fit on 80 characters. #define CALL_HASHLIB_NEW(MODULE, NAME, DATA, STRING, USEDFORSECURITY) \ - do { \ - PyObject *data_obj; \ - if (_Py_hashlib_data_argument(&data_obj, DATA, STRING) < 0) { \ - return NULL; \ - } \ - _hashlibstate *state = get_hashlib_state(MODULE); \ - return _hashlib_HASH(state, NAME, data_obj, USEDFORSECURITY); \ - } while (0) + return _hashlib_HASH_new_impl(MODULE, NAME, DATA, USEDFORSECURITY, STRING) /* The module-level function: new() */ @@ -1285,7 +1351,12 @@ _hashlib_HASH_new_impl(PyObject *module, const char *name, PyObject *data, int usedforsecurity, PyObject *string) /*[clinic end generated code: output=b905aaf9840c1bbd input=c34af6c6e696d44e]*/ { - CALL_HASHLIB_NEW(module, name, data, string, usedforsecurity); + PyObject *data_obj; + if (_Py_hashlib_data_argument(&data_obj, data, string) < 0) { + return NULL; + } + _hashlibstate *state = get_hashlib_state(module); + return _hashlib_HASH(state, name, data_obj, usedforsecurity); } @@ -1500,6 +1571,7 @@ _hashlib_openssl_sha3_512_impl(PyObject *module, PyObject *data, #ifdef PY_OPENSSL_HAS_SHAKE /*[clinic input] +@permit_long_summary _hashlib.openssl_shake_128 data: object(c_default="NULL") = b'' @@ -1514,12 +1586,13 @@ Returns a shake-128 variable hash object; optionally initialized with a string static PyObject * _hashlib_openssl_shake_128_impl(PyObject *module, PyObject *data, int usedforsecurity, PyObject *string) -/*[clinic end generated code: output=4e6afed8d18980ad input=373c3f1c93d87b37]*/ +/*[clinic end generated code: output=4e6afed8d18980ad input=0d2803af1158b23c]*/ { CALL_HASHLIB_NEW(module, Py_hash_shake_128, data, string, usedforsecurity); } /*[clinic input] +@permit_long_summary _hashlib.openssl_shake_256 data: object(c_default="NULL") = b'' @@ -1534,7 +1607,7 @@ Returns a shake-256 variable hash object; optionally initialized with a string static PyObject * _hashlib_openssl_shake_256_impl(PyObject *module, PyObject *data, int usedforsecurity, PyObject *string) -/*[clinic end generated code: output=62481bce4a77d16c input=101c139ea2ddfcbf]*/ +/*[clinic end generated code: output=62481bce4a77d16c input=f27b98d9c749f55d]*/ { CALL_HASHLIB_NEW(module, Py_hash_shake_256, data, string, usedforsecurity); } @@ -1543,6 +1616,7 @@ _hashlib_openssl_shake_256_impl(PyObject *module, PyObject *data, #undef CALL_HASHLIB_NEW /*[clinic input] +@permit_long_summary _hashlib.pbkdf2_hmac as pbkdf2_hmac hash_name: str @@ -1558,11 +1632,10 @@ static PyObject * pbkdf2_hmac_impl(PyObject *module, const char *hash_name, Py_buffer *password, Py_buffer *salt, long iterations, PyObject *dklen_obj) -/*[clinic end generated code: output=144b76005416599b input=ed3ab0d2d28b5d5c]*/ +/*[clinic end generated code: output=144b76005416599b input=83417fbd9ec2b8a3]*/ { _hashlibstate *state = get_hashlib_state(module); PyObject *key_obj = NULL; - char *key; long dklen; int retval; @@ -1615,24 +1688,24 @@ pbkdf2_hmac_impl(PyObject *module, const char *hash_name, goto end; } - key_obj = PyBytes_FromStringAndSize(NULL, dklen); - if (key_obj == NULL) { + PyBytesWriter *writer = PyBytesWriter_Create(dklen); + if (writer == NULL) { goto end; } - key = PyBytes_AS_STRING(key_obj); Py_BEGIN_ALLOW_THREADS retval = PKCS5_PBKDF2_HMAC((const char *)password->buf, (int)password->len, (const unsigned char *)salt->buf, (int)salt->len, iterations, digest, dklen, - (unsigned char *)key); + (unsigned char *)PyBytesWriter_GetData(writer)); Py_END_ALLOW_THREADS if (!retval) { - Py_CLEAR(key_obj); + PyBytesWriter_Discard(writer); notify_ssl_error_occurred_in(Py_STRINGIFY(PKCS5_PBKDF2_HMAC)); goto end; } + key_obj = PyBytesWriter_Finish(writer); end: if (digest != NULL) { @@ -1682,7 +1755,6 @@ _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt, long maxmem, long dklen) /*[clinic end generated code: output=d424bc3e8c6b9654 input=bdeac9628d07f7a1]*/ { - PyObject *key = NULL; int retval; if (password->len > INT_MAX) { @@ -1723,8 +1795,8 @@ _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt, return NULL; } - key = PyBytes_FromStringAndSize(NULL, dklen); - if (key == NULL) { + PyBytesWriter *writer = PyBytesWriter_Create(dklen); + if (writer == NULL) { return NULL; } @@ -1733,24 +1805,42 @@ _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt, (const char *)password->buf, (size_t)password->len, (const unsigned char *)salt->buf, (size_t)salt->len, (uint64_t)n, (uint64_t)r, (uint64_t)p, (uint64_t)maxmem, - (unsigned char *)PyBytes_AS_STRING(key), (size_t)dklen + (unsigned char *)PyBytesWriter_GetData(writer), (size_t)dklen ); Py_END_ALLOW_THREADS if (!retval) { - Py_DECREF(key); + PyBytesWriter_Discard(writer); notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_PBE_scrypt)); return NULL; } - return key; + return PyBytesWriter_Finish(writer); } #undef HASHLIB_SCRYPT_MAX_DKLEN #undef HASHLIB_SCRYPT_MAX_MAXMEM -/* Fast HMAC for hmac.digest() +// --- OpenSSL HMAC interface ------------------------------------------------- + +/* + * Functions prefixed by hashlib_openssl_HMAC_* are wrappers around OpenSSL + * and implement "atomic" operations (e.g., "free"). These functions are used + * by those prefixed by _hashlib_HMAC_* that are methods for HMAC objects, or + * other (local) helper functions prefixed by hashlib_HMAC_*. */ +#ifdef Py_HAS_OPENSSL3_SUPPORT +/* EVP_MAC_CTX array of parameters specifying the "digest" */ +#define HASHLIB_HMAC_OSSL_PARAMS(DIGEST) \ + (const OSSL_PARAM []) { \ + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, \ + (char *)DIGEST, strlen(DIGEST)), \ + OSSL_PARAM_END \ + } +#endif + +// --- One-shot HMAC interface ------------------------------------------------ + /*[clinic input] _hashlib.hmac_digest as _hashlib_hmac_singleshot @@ -1768,9 +1858,14 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, { _hashlibstate *state = get_hashlib_state(module); unsigned char md[EVP_MAX_MD_SIZE] = {0}; +#ifdef Py_HAS_OPENSSL3_SUPPORT + size_t md_len = 0; + const char *digest_name = NULL; +#else unsigned int md_len = 0; - unsigned char *result; - PY_EVP_MD *evp; +#endif + unsigned char *result = NULL; + PY_EVP_MD *evp = NULL; int is_xof; if (key->len > INT_MAX) { @@ -1784,13 +1879,34 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, return NULL; } +#ifdef Py_HAS_OPENSSL3_SUPPORT + digest_name = get_openssl_digest_name(state, digest, Py_ht_mac, &evp); + if (digest_name == NULL) { + assert(evp == NULL); + return NULL; + } + assert(evp != NULL); + is_xof = PY_EVP_MD_xof(evp); + + Py_BEGIN_ALLOW_THREADS + result = EVP_Q_mac( + NULL, OSSL_MAC_NAME_HMAC, NULL, NULL, + HASHLIB_HMAC_OSSL_PARAMS(digest_name), + (const void *)key->buf, (size_t)key->len, + (const unsigned char *)msg->buf, (size_t)msg->len, + md, sizeof(md), &md_len + ); + Py_END_ALLOW_THREADS + PY_EVP_MD_free(evp); + assert(md_len < (size_t)PY_SSIZE_T_MAX); +#else evp = get_openssl_evp_md(state, digest, Py_ht_mac); if (evp == NULL) { return NULL; } + is_xof = PY_EVP_MD_xof(evp); Py_BEGIN_ALLOW_THREADS - is_xof = PY_EVP_MD_xof(evp); result = HMAC( evp, (const void *)key->buf, (int)key->len, @@ -1799,23 +1915,27 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, ); Py_END_ALLOW_THREADS PY_EVP_MD_free(evp); - +#endif if (result == NULL) { if (is_xof) { /* use a better default error message if an XOF is used */ raise_unsupported_algorithm_error(state, digest); } else { +#ifdef Py_HAS_OPENSSL3_SUPPORT + notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_Q_mac)); +#else notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC)); +#endif } return NULL; } return PyBytes_FromStringAndSize((const char*)md, md_len); } -/* OpenSSL-based HMAC implementation - */ +// --- HMAC Object ------------------------------------------------------------ +#ifndef Py_HAS_OPENSSL3_SUPPORT /* Thin wrapper around HMAC_CTX_new() which sets an exception on failure. */ static HMAC_CTX * py_openssl_wrapper_HMAC_CTX_new(void) @@ -1827,18 +1947,180 @@ py_openssl_wrapper_HMAC_CTX_new(void) } return ctx; } +#endif static int _hmac_update(HMACobject*, PyObject*); +#ifndef Py_HAS_OPENSSL3_SUPPORT static const EVP_MD * _hashlib_hmac_get_md(HMACobject *self) { + assert(self->ctx != NULL); const EVP_MD *md = HMAC_CTX_get_md(self->ctx); if (md == NULL) { notify_ssl_error_occurred("missing EVP_MD for HMAC context"); } return md; } +#endif + +static const char * +hashlib_HMAC_get_hashlib_digest_name(HMACobject *self) +{ +#ifdef Py_HAS_OPENSSL3_SUPPORT + return get_hashlib_utf8name_by_nid(self->evp_md_nid); +#else + const EVP_MD *md = _hashlib_hmac_get_md(self); + return md == NULL ? NULL : get_hashlib_utf8name_by_evp_md(md); +#endif +} + +static int +hashlib_openssl_HMAC_update_once(PY_HMAC_CTX_TYPE *ctx, const Py_buffer *v) +{ + if (!PY_HMAC_update(ctx, (const unsigned char *)v->buf, (size_t)v->len)) { + notify_smart_ssl_error_occurred_in(Py_STRINGIFY(PY_HMAC_update)); + return -1; + } + return 0; +} + +/* Thin wrapper around PY_HMAC_CTX_free that allows to pass a NULL 'ctx'. */ +static inline void +hashlib_openssl_HMAC_CTX_free(PY_HMAC_CTX_TYPE *ctx) +{ + /* The NULL check was not present in every OpenSSL versions. */ + if (ctx) { + PY_HMAC_CTX_free(ctx); + } +} + +static PY_HMAC_CTX_TYPE * +hashlib_openssl_HMAC_ctx_copy_with_lock(HMACobject *self) +{ + PY_HMAC_CTX_TYPE *ctx = NULL; +#ifdef Py_HAS_OPENSSL3_SUPPORT + HASHLIB_ACQUIRE_LOCK(self); + ctx = EVP_MAC_CTX_dup(self->ctx); + HASHLIB_RELEASE_LOCK(self); + if (ctx == NULL) { + notify_smart_ssl_error_occurred_in(Py_STRINGIFY(EVP_MAC_CTX_dup)); + goto error; + } +#else + int r; + ctx = py_openssl_wrapper_HMAC_CTX_new(); + if (ctx == NULL) { + return NULL; + } + HASHLIB_ACQUIRE_LOCK(self); + r = HMAC_CTX_copy(ctx, self->ctx); + HASHLIB_RELEASE_LOCK(self); + if (r == 0) { + notify_smart_ssl_error_occurred_in(Py_STRINGIFY(HMAC_CTX_copy)); + goto error; + } +#endif + return ctx; + +error: + hashlib_openssl_HMAC_CTX_free(ctx); + return NULL; +} + +static PY_HMAC_CTX_TYPE * +hashlib_HMAC_CTX_new_from_digestmod(_hashlibstate *state, + Py_buffer *key, PyObject *digestmod, + int *nid) +{ + PY_HMAC_CTX_TYPE *ctx = NULL; + PY_EVP_MD *md = NULL; + int is_xof, r; +#ifdef Py_HAS_OPENSSL3_SUPPORT + const char *digest = NULL; +#endif + +#ifdef Py_HAS_OPENSSL3_SUPPORT + /* + * OpenSSL 3.0 does not provide a way to extract the NID from an EVP_MAC + * object and does not expose the underlying digest name. The reason is + * that OpenSSL 3.0 treats HMAC objects as being the "same", differing + * only by their *context* parameters. While it is *required* to set + * the digest name when constructing EVP_MAC_CTX objects, that name + * is unfortunately not recoverable through EVP_MAC_CTX_get_params(). + * + * On the other hand, the (deprecated) interface based on HMAC_CTX is + * based on EVP_MD, which allows to treat HMAC objects as if they were + * hash functions when querying the digest name. + * + * Since HMAC objects are constructed from DIGESTMOD values and since + * we have a way to map DIGESTMOD to EVP_MD objects, and then to NIDs, + * HMAC objects based on EVP_MAC will store the NID of the EVP_MD we + * used to deduce the digest name to pass to EVP_MAC_CTX_set_params(). + */ + assert(nid != NULL); + digest = get_openssl_digest_name(state, digestmod, Py_ht_mac, &md); + assert((digest == NULL && md == NULL) || (digest != NULL && md != NULL)); + if (digest == NULL) { + *nid = NID_undef; + return NULL; + } + *nid = EVP_MD_nid(md); + is_xof = PY_EVP_MD_xof(md); + PY_EVP_MD_free(md); + + /* + * OpenSSL is responsible for managing the EVP_MAC object's ref. count + * by calling EVP_MAC_up_ref() and EVP_MAC_free() in EVP_MAC_CTX_new() + * and EVP_MAC_CTX_free() respectively. + */ + ctx = EVP_MAC_CTX_new(state->evp_hmac); + if (ctx == NULL) { + /* EVP_MAC_CTX_new() may also set an ERR_R_EVP_LIB error */ + notify_smart_ssl_error_occurred_in(Py_STRINGIFY(EVP_MAC_CTX_new)); + return NULL; + } + + r = EVP_MAC_init( + ctx, + (const unsigned char *)key->buf, + (size_t)key->len, + HASHLIB_HMAC_OSSL_PARAMS(digest) + ); +#else + assert(nid == NULL); + md = get_openssl_evp_md(state, digestmod, Py_ht_mac); + if (md == NULL) { + return NULL; + } + is_xof = PY_EVP_MD_xof(md); + + ctx = py_openssl_wrapper_HMAC_CTX_new(); + if (ctx == NULL) { + PY_EVP_MD_free(md); + return NULL; + } + + r = HMAC_Init_ex(ctx, key->buf, (int)key->len, md, NULL /* impl */); + PY_EVP_MD_free(md); +#endif + if (r == 0) { + hashlib_openssl_HMAC_CTX_free(ctx); + if (is_xof) { + /* use a better default error message if an XOF is used */ + raise_unsupported_algorithm_error(state, digestmod); + } + else { +#ifdef Py_HAS_OPENSSL3_SUPPORT + notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_MAC_init)); +#else + notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Init_ex)); +#endif + } + return NULL; + } + return ctx; +} /*[clinic input] _hashlib.hmac_new @@ -1856,10 +2138,11 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, /*[clinic end generated code: output=c20d9e4d9ed6d219 input=5f4071dcc7f34362]*/ { _hashlibstate *state = get_hashlib_state(module); - PY_EVP_MD *digest; - HMAC_CTX *ctx = NULL; + PY_HMAC_CTX_TYPE *ctx = NULL; HMACobject *self = NULL; - int is_xof, r; +#ifdef Py_HAS_OPENSSL3_SUPPORT + int nid; +#endif if (key->len > INT_MAX) { PyErr_SetString(PyExc_OverflowError, @@ -1873,29 +2156,15 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, return NULL; } - digest = get_openssl_evp_md(state, digestmod, Py_ht_mac); - if (digest == NULL) { - return NULL; - } +#ifdef Py_HAS_OPENSSL3_SUPPORT + ctx = hashlib_HMAC_CTX_new_from_digestmod(state, key, digestmod, &nid); +#else + ctx = hashlib_HMAC_CTX_new_from_digestmod(state, key, digestmod, NULL); +#endif - ctx = py_openssl_wrapper_HMAC_CTX_new(); if (ctx == NULL) { - PY_EVP_MD_free(digest); - goto error; - } - - is_xof = PY_EVP_MD_xof(digest); - r = HMAC_Init_ex(ctx, key->buf, (int)key->len, digest, NULL /* impl */); - PY_EVP_MD_free(digest); - if (r == 0) { - if (is_xof) { - /* use a better default error message if an XOF is used */ - raise_unsupported_algorithm_error(state, digestmod); - } - else { - notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Init_ex)); - } - goto error; + assert(PyErr_Occurred()); + return NULL; } self = PyObject_New(HMACobject, state->HMAC_type); @@ -1905,50 +2174,61 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, self->ctx = ctx; ctx = NULL; // 'ctx' is now owned by 'self' +#ifdef Py_HAS_OPENSSL3_SUPPORT + assert(nid != NID_undef); + self->evp_md_nid = nid; +#endif HASHLIB_INIT_MUTEX(self); + /* feed initial data */ if ((msg_obj != NULL) && (msg_obj != Py_None)) { - if (!_hmac_update(self, msg_obj)) { + if (_hmac_update(self, msg_obj) < 0) { goto error; } } return (PyObject *)self; error: - if (ctx) HMAC_CTX_free(ctx); + hashlib_openssl_HMAC_CTX_free(ctx); Py_XDECREF(self); return NULL; } /* helper functions */ -static int -locked_HMAC_CTX_copy(HMAC_CTX *new_ctx_p, HMACobject *self) -{ - int result; - HASHLIB_ACQUIRE_LOCK(self); - result = HMAC_CTX_copy(new_ctx_p, self->ctx); - HASHLIB_RELEASE_LOCK(self); - if (result == 0) { - notify_smart_ssl_error_occurred_in(Py_STRINGIFY(HMAC_CTX_copy)); - return -1; - } - return 0; -} +#define BAD_DIGEST_SIZE 0 -/* returning 0 means that an error occurred and an exception is set */ -static unsigned int +/* + * Return the digest size in bytes. + * + * On error, set an exception and return BAD_DIGEST_SIZE. + */ +static int _hashlib_hmac_digest_size(HMACobject *self) { + assert(EVP_MAX_MD_SIZE < INT_MAX); +#ifdef Py_HAS_OPENSSL3_SUPPORT + assert(self->ctx != NULL); + size_t digest_size = EVP_MAC_CTX_get_mac_size(self->ctx); + assert(digest_size <= (size_t)EVP_MAX_MD_SIZE); +#else const EVP_MD *md = _hashlib_hmac_get_md(self); if (md == NULL) { - return 0; + return BAD_DIGEST_SIZE; } - unsigned int digest_size = EVP_MD_size(md); - assert(digest_size <= EVP_MAX_MD_SIZE); + int digest_size = EVP_MD_size(md); + /* digest_size < 0 iff EVP_MD context is NULL (which is impossible here) */ + assert(digest_size <= (int)EVP_MAX_MD_SIZE); + if (digest_size < 0) { + raise_ssl_error(PyExc_SystemError, "invalid digest size"); + return BAD_DIGEST_SIZE; + } +#endif + /* digest_size == 0 means that the context is not entirely initialized */ if (digest_size == 0) { - notify_ssl_error_occurred("invalid digest size"); + raise_ssl_error(PyExc_SystemError, "missing digest size"); + return BAD_DIGEST_SIZE; } - return digest_size; + return (int)digest_size; } static int @@ -1956,21 +2236,13 @@ _hmac_update(HMACobject *self, PyObject *obj) { int r; Py_buffer view = {0}; - - GET_BUFFER_VIEW_OR_ERROR(obj, &view, return 0); + GET_BUFFER_VIEW_OR_ERROR(obj, &view, return -1); HASHLIB_EXTERNAL_INSTRUCTIONS_LOCKED( self, view.len, - r = HMAC_Update( - self->ctx, (const unsigned char *)view.buf, (size_t)view.len - ) + r = hashlib_openssl_HMAC_update_once(self->ctx, &view) ); PyBuffer_Release(&view); - - if (r == 0) { - notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Update)); - return 0; - } - return 1; + return r; } /*[clinic input] @@ -1984,24 +2256,20 @@ _hashlib_HMAC_copy_impl(HMACobject *self) /*[clinic end generated code: output=29aa28b452833127 input=e2fa6a05db61a4d6]*/ { HMACobject *retval; - - HMAC_CTX *ctx = py_openssl_wrapper_HMAC_CTX_new(); + PY_HMAC_CTX_TYPE *ctx = hashlib_openssl_HMAC_ctx_copy_with_lock(self); if (ctx == NULL) { return NULL; } - if (locked_HMAC_CTX_copy(ctx, self) < 0) { - HMAC_CTX_free(ctx); - return NULL; - } - retval = PyObject_New(HMACobject, Py_TYPE(self)); if (retval == NULL) { - HMAC_CTX_free(ctx); + PY_HMAC_CTX_free(ctx); return NULL; } retval->ctx = ctx; +#ifdef Py_HAS_OPENSSL3_SUPPORT + retval->evp_md_nid = self->evp_md_nid; +#endif HASHLIB_INIT_MUTEX(retval); - return (PyObject *)retval; } @@ -2011,7 +2279,7 @@ _hmac_dealloc(PyObject *op) HMACobject *self = HMACobject_CAST(op); PyTypeObject *tp = Py_TYPE(self); if (self->ctx != NULL) { - HMAC_CTX_free(self->ctx); + PY_HMAC_CTX_free(self->ctx); self->ctx = NULL; } PyObject_Free(self); @@ -2021,10 +2289,8 @@ _hmac_dealloc(PyObject *op) static PyObject * _hmac_repr(PyObject *op) { - const char *digest_name; HMACobject *self = HMACobject_CAST(op); - const EVP_MD *md = _hashlib_hmac_get_md(self); - digest_name = md == NULL ? NULL : get_hashlib_utf8name_by_evp_md(md); + const char *digest_name = hashlib_HMAC_get_hashlib_digest_name(self); if (digest_name == NULL) { assert(PyErr_Occurred()); return NULL; @@ -2043,30 +2309,48 @@ static PyObject * _hashlib_HMAC_update_impl(HMACobject *self, PyObject *msg) /*[clinic end generated code: output=f31f0ace8c625b00 input=1829173bb3cfd4e6]*/ { - if (!_hmac_update(self, msg)) { + if (_hmac_update(self, msg) < 0) { return NULL; } Py_RETURN_NONE; } -static int -_hmac_digest(HMACobject *self, unsigned char *buf, unsigned int len) +/* + * Extract the MAC value to 'buf' and return the digest size. + * + * The buffer 'buf' must have at least _hashlib_hmac_digest_size(self) + * bytes. Smaller buffers lead to undefined behaviors. + * + * On error, set an exception and return -1. + */ +static Py_ssize_t +_hmac_digest(HMACobject *self, unsigned char *buf) { - HMAC_CTX *temp_ctx = py_openssl_wrapper_HMAC_CTX_new(); - if (temp_ctx == NULL) { - return 0; + int digest_size = _hashlib_hmac_digest_size(self); + assert(digest_size <= EVP_MAX_MD_SIZE); + if (digest_size == BAD_DIGEST_SIZE) { + assert(PyErr_Occurred()); + return -1; } - if (locked_HMAC_CTX_copy(temp_ctx, self) < 0) { - HMAC_CTX_free(temp_ctx); - return 0; + PY_HMAC_CTX_TYPE *ctx = hashlib_openssl_HMAC_ctx_copy_with_lock(self); + if (ctx == NULL) { + return -1; } - int r = HMAC_Final(temp_ctx, buf, &len); - HMAC_CTX_free(temp_ctx); +#ifdef Py_HAS_OPENSSL3_SUPPORT + int r = EVP_MAC_final(ctx, buf, NULL, digest_size); +#else + int r = HMAC_Final(ctx, buf, NULL); +#endif + PY_HMAC_CTX_free(ctx); if (r == 0) { +#ifdef Py_HAS_OPENSSL3_SUPPORT + notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_MAC_final)); +#else notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Final)); - return 0; +#endif + return -1; } - return 1; + return digest_size; } /*[clinic input] @@ -2078,68 +2362,56 @@ static PyObject * _hashlib_HMAC_digest_impl(HMACobject *self) /*[clinic end generated code: output=1b1424355af7a41e input=bff07f74da318fb4]*/ { - unsigned char digest[EVP_MAX_MD_SIZE]; - unsigned int digest_size = _hashlib_hmac_digest_size(self); - if (digest_size == 0) { - return NULL; - } - int r = _hmac_digest(self, digest, digest_size); - if (r == 0) { - return NULL; - } - return PyBytes_FromStringAndSize((const char *)digest, digest_size); + unsigned char buf[EVP_MAX_MD_SIZE]; + Py_ssize_t n = _hmac_digest(self, buf); + return n < 0 ? NULL : PyBytes_FromStringAndSize((const char *)buf, n); } /*[clinic input] +@permit_long_summary _hashlib.HMAC.hexdigest Return hexadecimal digest of the bytes passed to the update() method so far. -This may be used to exchange the value safely in email or other non-binary -environments. +This may be used to exchange the value safely in email or other +non-binary environments. [clinic start generated code]*/ static PyObject * _hashlib_HMAC_hexdigest_impl(HMACobject *self) -/*[clinic end generated code: output=80d825be1eaae6a7 input=5abc42702874ddcf]*/ +/*[clinic end generated code: output=80d825be1eaae6a7 input=e37a84c36a43787c]*/ { - unsigned char digest[EVP_MAX_MD_SIZE]; - unsigned int digest_size = _hashlib_hmac_digest_size(self); - if (digest_size == 0) { - return NULL; - } - int r = _hmac_digest(self, digest, digest_size); - if (r == 0) { - return NULL; - } - return _Py_strhex((const char *)digest, digest_size); + unsigned char buf[EVP_MAX_MD_SIZE]; + Py_ssize_t n = _hmac_digest(self, buf); + return n < 0 ? NULL : _Py_strhex((const char *)buf, n); } static PyObject * _hashlib_hmac_get_digest_size(PyObject *op, void *Py_UNUSED(closure)) { HMACobject *self = HMACobject_CAST(op); - unsigned int digest_size = _hashlib_hmac_digest_size(self); - return digest_size == 0 ? NULL : PyLong_FromLong(digest_size); + int size = _hashlib_hmac_digest_size(self); + return size == BAD_DIGEST_SIZE ? NULL : PyLong_FromLong(size); } static PyObject * _hashlib_hmac_get_block_size(PyObject *op, void *Py_UNUSED(closure)) { HMACobject *self = HMACobject_CAST(op); +#ifdef Py_HAS_OPENSSL3_SUPPORT + assert(self->ctx != NULL); + return PyLong_FromSize_t(EVP_MAC_CTX_get_block_size(self->ctx)); +#else const EVP_MD *md = _hashlib_hmac_get_md(self); return md == NULL ? NULL : PyLong_FromLong(EVP_MD_block_size(md)); +#endif } static PyObject * _hashlib_hmac_get_name(PyObject *op, void *Py_UNUSED(closure)) { HMACobject *self = HMACobject_CAST(op); - const EVP_MD *md = _hashlib_hmac_get_md(self); - if (md == NULL) { - return NULL; - } - const char *digest_name = get_hashlib_utf8name_by_evp_md(md); + const char *digest_name = hashlib_HMAC_get_hashlib_digest_name(self); if (digest_name == NULL) { assert(PyErr_Occurred()); return NULL; @@ -2267,8 +2539,8 @@ _hashlib.get_fips_mode -> int Determine the OpenSSL FIPS mode of operation. For OpenSSL 3.0.0 and newer it returns the state of the default provider -in the default OSSL context. It's not quite the same as FIPS_mode() but good -enough for unittests. +in the default OSSL context. It's not quite the same as FIPS_mode() but +good enough for unittests. Effectively any non-zero return value indicates FIPS mode; values other than 1 may have additional significance. @@ -2276,7 +2548,7 @@ values other than 1 may have additional significance. static int _hashlib_get_fips_mode_impl(PyObject *module) -/*[clinic end generated code: output=87eece1bab4d3fa9 input=2db61538c41c6fef]*/ +/*[clinic end generated code: output=87eece1bab4d3fa9 input=a6cdb6901421d122]*/ { #ifdef Py_HAS_OPENSSL3_SUPPORT @@ -2466,6 +2738,12 @@ hashlib_clear(PyObject *m) _Py_hashtable_destroy(state->hashtable); state->hashtable = NULL; } +#ifdef Py_HAS_OPENSSL3_SUPPORT + if (state->evp_hmac != NULL) { + EVP_MAC_free(state->evp_hmac); + state->evp_hmac = NULL; + } +#endif return 0; } @@ -2540,6 +2818,15 @@ hashlib_init_hmactype(PyObject *module) if (PyModule_AddType(module, state->HMAC_type) < 0) { return -1; } +#ifdef Py_HAS_OPENSSL3_SUPPORT + state->evp_hmac = EVP_MAC_fetch(NULL, "HMAC", NULL); + if (state->evp_hmac == NULL) { + ERR_clear_error(); + PyErr_SetString(PyExc_ImportError, "cannot initialize EVP_MAC HMAC"); + return -1; + } +#endif + return 0; } @@ -2617,6 +2904,7 @@ hashlib_constants(PyObject *module) } static PyModuleDef_Slot hashlib_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, hashlib_init_hashtable}, {Py_mod_exec, hashlib_init_HASH_type}, {Py_mod_exec, hashlib_init_HASHXOF_type}, diff --git a/Modules/_heapqmodule.c b/Modules/_heapqmodule.c index 05d01acd77109b..014c838694975d 100644 --- a/Modules/_heapqmodule.c +++ b/Modules/_heapqmodule.c @@ -247,6 +247,7 @@ _heapq_heapreplace_impl(PyObject *module, PyObject *heap, PyObject *item) } /*[clinic input] +@permit_long_summary @critical_section heap _heapq.heappushpop @@ -262,7 +263,7 @@ a separate call to heappop(). static PyObject * _heapq_heappushpop_impl(PyObject *module, PyObject *heap, PyObject *item) -/*[clinic end generated code: output=67231dc98ed5774f input=db05c81b1dd92c44]*/ +/*[clinic end generated code: output=67231dc98ed5774f input=491178a1c7d417ba]*/ { PyObject *returnitem; int cmp; @@ -593,13 +594,13 @@ _heapq.heappushpop_max Maxheap variant of heappushpop. -The combined action runs more efficiently than heappush_max() followed by -a separate call to heappop_max(). +The combined action runs more efficiently than heappush_max() +followed by a separate call to heappop_max(). [clinic start generated code]*/ static PyObject * _heapq_heappushpop_max_impl(PyObject *module, PyObject *heap, PyObject *item) -/*[clinic end generated code: output=ff0019f0941aca0d input=24d0defa6fd6df4a]*/ +/*[clinic end generated code: output=ff0019f0941aca0d input=52030929667a4c08]*/ { PyObject *returnitem; int cmp; @@ -786,6 +787,7 @@ heapq_exec(PyObject *m) } static struct PyModuleDef_Slot heapq_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, heapq_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index 9c1f8615161275..3614890757d69d 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -511,12 +511,12 @@ _waiting_release(_waiting_t *waiting, int received) assert(!waiting->received); waiting->status = WAITING_RELEASING; - PyThread_release_lock(waiting->mutex); if (waiting->received != received) { assert(received == 1); waiting->received = received; } waiting->status = WAITING_RELEASED; + PyThread_release_lock(waiting->mutex); } static void @@ -580,7 +580,7 @@ _channelitem_clear_data(_channelitem *item, int removed) { if (item->data != NULL) { // It was allocated in channel_send(). - (void)_release_xid_data(item->data, XID_IGNORE_EXC & XID_FREE); + (void)_release_xid_data(item->data, XID_IGNORE_EXC | XID_FREE); item->data = NULL; } @@ -921,7 +921,8 @@ static _channelends * _channelends_new(void) { _channelends *ends = GLOBAL_MALLOC(_channelends); - if (ends== NULL) { + if (ends == NULL) { + PyErr_NoMemory(); return NULL; } ends->numsendopen = 0; @@ -1115,6 +1116,7 @@ _channel_new(PyThread_type_lock mutex, struct _channeldefaults defaults) assert(check_unbound(defaults.unboundop)); _channel_state *chan = GLOBAL_MALLOC(_channel_state); if (chan == NULL) { + PyErr_NoMemory(); return NULL; } chan->mutex = mutex; @@ -1313,6 +1315,7 @@ _channelref_new(int64_t cid, _channel_state *chan) { _channelref *ref = GLOBAL_MALLOC(_channelref); if (ref == NULL) { + PyErr_NoMemory(); return NULL; } ref->cid = cid; @@ -1644,14 +1647,16 @@ _channels_list_all(_channels *channels, int64_t *count) if (ids == NULL) { goto done; } - _channelref *ref = channels->head; - for (int64_t i=0; ref != NULL; ref = ref->next, i++) { - ids[i] = (struct channel_id_and_info){ - .id = ref->cid, - .defaults = ref->chan->defaults, - }; + int64_t i = 0; + for (_channelref *ref = channels->head; ref != NULL; ref = ref->next) { + if (ref->chan != NULL) { + ids[i++] = (struct channel_id_and_info){ + .id = ref->cid, + .defaults = ref->chan->defaults, + }; + } } - *count = channels->numopen; + *count = i; cids = ids; done: @@ -1696,6 +1701,7 @@ _channel_set_closing(_channelref *ref, PyThread_type_lock mutex) { } chan->closing = GLOBAL_MALLOC(struct _channel_closing); if (chan->closing == NULL) { + PyErr_NoMemory(); goto done; } chan->closing->ref = ref; @@ -2584,6 +2590,7 @@ static PyObject * _channelid_from_xid(_PyXIData_t *data) { struct _channelid_xid *xid = (struct _channelid_xid *)_PyXIData_DATA(data); + PyObject *cidobj = NULL; // It might not be imported yet, so we can't use _get_current_module(). PyObject *mod = PyImport_ImportModule(MODULE_NAME_STR); @@ -2593,11 +2600,10 @@ _channelid_from_xid(_PyXIData_t *data) assert(mod != Py_None); module_state *state = get_module_state(mod); if (state == NULL) { - return NULL; + goto done; } // Note that we do not preserve the "resolve" flag. - PyObject *cidobj = NULL; int err = newchannelid(state->ChannelIDType, xid->cid, xid->end, _global_channels(), 0, 0, (channelid **)&cidobj); @@ -3603,6 +3609,7 @@ module_exec(PyObject *mod) } static struct PyModuleDef_Slot module_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_interpqueuesmodule.c b/Modules/_interpqueuesmodule.c index e5afe746f90bdc..9979cd3457e101 100644 --- a/Modules/_interpqueuesmodule.c +++ b/Modules/_interpqueuesmodule.c @@ -22,6 +22,11 @@ #define MODINIT_FUNC_NAME RESOLVE_MODINIT_FUNC_NAME(MODULE_NAME) +/*[clinic input] +module _interpqueues +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=cb1313f77fab132b]*/ + #define GLOBAL_MALLOC(TYPE) \ PyMem_RawMalloc(sizeof(TYPE)) #define GLOBAL_FREE(VAR) \ @@ -431,7 +436,7 @@ _queueitem_clear_data(_queueitem *item) return; } // It was allocated in queue_put(). - (void)_release_xid_data(item->data, XID_IGNORE_EXC & XID_FREE); + (void)_release_xid_data(item->data, XID_IGNORE_EXC | XID_FREE); item->data = NULL; } @@ -1358,6 +1363,7 @@ _queueobj_from_xid(_PyXIData_t *data) if (mod == NULL) { mod = PyImport_ImportModule(MODULE_NAME_STR); if (mod == NULL) { + Py_DECREF(qidobj); return NULL; } } @@ -1466,31 +1472,48 @@ clear_interpreter(void *data) } -typedef struct idarg_int64_converter_data qidarg_converter_data; +/*[python input] + +class qidarg_converter(CConverter): + type = 'int64_t' + converter = 'qidarg_converter' + +[python start generated code]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=c64fbf36771164d6]*/ static int qidarg_converter(PyObject *arg, void *ptr) { - qidarg_converter_data *data = ptr; - if (data->label == NULL) { - data->label = "queue ID"; - } - return idarg_int64_converter(arg, ptr); + int64_t *qid_ptr = ptr; + struct idarg_int64_converter_data data = { + .label = "queue ID", + }; + int res = idarg_int64_converter(arg, &data); + *qid_ptr = data.id; + return res; } +#include "clinic/_interpqueuesmodule.c.h" + + +/*[clinic input] +_interpqueues.create + maxsize: Py_ssize_t + unboundop as unboundarg: int = -1 + fallback as fallbackarg: int = -1 + +Create a new cross-interpreter queue and return its unique generated ID. + +It is a new reference as though bind() had been called on the queue. +The caller is responsible for calling destroy() for the new queue +before the runtime is finalized. +[clinic start generated code]*/ static PyObject * -queuesmod_create(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"maxsize", "unboundop", "fallback", NULL}; - Py_ssize_t maxsize; - int unboundarg = -1; - int fallbackarg = -1; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "n|ii:create", kwlist, - &maxsize, &unboundarg, &fallbackarg)) - { - return NULL; - } +_interpqueues_create_impl(PyObject *module, Py_ssize_t maxsize, + int unboundarg, int fallbackarg) +/*[clinic end generated code: output=9a889b93773251eb input=4f79b710a87360e1]*/ +{ struct _queuedefaults defaults = {0}; if (resolve_unboundop(unboundarg, UNBOUND_REPLACE, &defaults.unboundop) < 0) @@ -1505,7 +1528,7 @@ queuesmod_create(PyObject *self, PyObject *args, PyObject *kwds) int64_t qid = queue_create(&_globals.queues, maxsize, defaults); if (qid < 0) { - (void)handle_queue_error((int)qid, self, qid); + (void)handle_queue_error((int)qid, module, qid); return NULL; } @@ -1513,7 +1536,7 @@ queuesmod_create(PyObject *self, PyObject *args, PyObject *kwds) if (qidobj == NULL) { PyObject *exc = PyErr_GetRaisedException(); int err = queue_destroy(&_globals.queues, qid); - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { // XXX issue a warning? PyErr_Clear(); } @@ -1524,41 +1547,38 @@ queuesmod_create(PyObject *self, PyObject *args, PyObject *kwds) return qidobj; } -PyDoc_STRVAR(queuesmod_create_doc, -"create(maxsize, unboundop, fallback) -> qid\n\ -\n\ -Create a new cross-interpreter queue and return its unique generated ID.\n\ -It is a new reference as though bind() had been called on the queue.\n\ -\n\ -The caller is responsible for calling destroy() for the new queue\n\ -before the runtime is finalized."); +/*[clinic input] +_interpqueues.destroy + qid: qidarg + +Clear and destroy the queue. + +Afterward attempts to use the queue will behave as though it never +existed. +[clinic start generated code]*/ static PyObject * -queuesmod_destroy(PyObject *self, PyObject *args, PyObject *kwds) +_interpqueues_destroy_impl(PyObject *module, int64_t qid) +/*[clinic end generated code: output=46b35623f080cbff input=75136ad807e28677]*/ { - static char *kwlist[] = {"qid", NULL}; - qidarg_converter_data qidarg = {0}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:destroy", kwlist, - qidarg_converter, &qidarg)) { - return NULL; - } - int64_t qid = qidarg.id; - int err = queue_destroy(&_globals.queues, qid); - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { return NULL; } Py_RETURN_NONE; } -PyDoc_STRVAR(queuesmod_destroy_doc, -"destroy(qid)\n\ -\n\ -Clear and destroy the queue. Afterward attempts to use the queue\n\ -will behave as though it never existed."); +/*[clinic input] +_interpqueues.list_all + +Return the list of ID triples for all queues. + +Each ID triple consists of (ID, default unbound op, default fallback). +[clinic start generated code]*/ static PyObject * -queuesmod_list_all(PyObject *self, PyObject *Py_UNUSED(ignored)) +_interpqueues_list_all_impl(PyObject *module) +/*[clinic end generated code: output=974280cb6442afdb input=19495f02cbb38b33]*/ { int64_t count = 0; struct queue_id_and_info *qids = _queues_list_all(&_globals.queues, &count); @@ -1589,31 +1609,25 @@ queuesmod_list_all(PyObject *self, PyObject *Py_UNUSED(ignored)) return ids; } -PyDoc_STRVAR(queuesmod_list_all_doc, -"list_all() -> [(qid, unboundop, fallback)]\n\ -\n\ -Return the list of IDs for all queues.\n\ -Each corresponding default unbound op and fallback is also included."); +/*[clinic input] +_interpqueues.put + qid: qidarg + obj: object + unboundop as unboundarg: int = -1 + fallback as fallbackarg: int = -1 + +Add the object's data to the queue. +[clinic start generated code]*/ static PyObject * -queuesmod_put(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"qid", "obj", "unboundop", "fallback", NULL}; - qidarg_converter_data qidarg = {0}; - PyObject *obj; - int unboundarg = -1; - int fallbackarg = -1; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O|ii$p:put", kwlist, - qidarg_converter, &qidarg, &obj, - &unboundarg, &fallbackarg)) - { - return NULL; - } - int64_t qid = qidarg.id; +_interpqueues_put_impl(PyObject *module, int64_t qid, PyObject *obj, + int unboundarg, int fallbackarg) +/*[clinic end generated code: output=2e0b31c6eaec29c9 input=4906550ab5c73be3]*/ +{ struct _queuedefaults defaults = {-1, -1}; if (unboundarg < 0 || fallbackarg < 0) { int err = queue_get_defaults(&_globals.queues, qid, &defaults); - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { return NULL; } } @@ -1629,34 +1643,31 @@ queuesmod_put(PyObject *self, PyObject *args, PyObject *kwds) /* Queue up the object. */ int err = queue_put(&_globals.queues, qid, obj, unboundop, fallback); // This is the only place that raises QueueFull. - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { return NULL; } Py_RETURN_NONE; } -PyDoc_STRVAR(queuesmod_put_doc, -"put(qid, obj)\n\ -\n\ -Add the object's data to the queue."); +/*[clinic input] +_interpqueues.get + qid: qidarg + +Return the (object, unbound op) from the front of the queue. + +If there is nothing to receive then raise QueueEmpty. +[clinic start generated code]*/ static PyObject * -queuesmod_get(PyObject *self, PyObject *args, PyObject *kwds) +_interpqueues_get_impl(PyObject *module, int64_t qid) +/*[clinic end generated code: output=b0988a0e29194f05 input=c5bccbc409ad0190]*/ { - static char *kwlist[] = {"qid", NULL}; - qidarg_converter_data qidarg = {0}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:get", kwlist, - qidarg_converter, &qidarg)) { - return NULL; - } - int64_t qid = qidarg.id; - PyObject *obj = NULL; int unboundop = 0; int err = queue_get(&_globals.queues, qid, &obj, &unboundop); // This is the only place that raises QueueEmpty. - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { return NULL; } @@ -1668,29 +1679,23 @@ queuesmod_get(PyObject *self, PyObject *args, PyObject *kwds) return res; } -PyDoc_STRVAR(queuesmod_get_doc, -"get(qid) -> (obj, unboundop)\n\ -\n\ -Return a new object from the data at the front of the queue.\n\ -The unbound op is also returned.\n\ -\n\ -If there is nothing to receive then raise QueueEmpty."); +/*[clinic input] +_interpqueues.bind + qid: qidarg + +Take a reference to the identified queue. + +The queue is not destroyed until there are no references left. +[clinic start generated code]*/ static PyObject * -queuesmod_bind(PyObject *self, PyObject *args, PyObject *kwds) +_interpqueues_bind_impl(PyObject *module, int64_t qid) +/*[clinic end generated code: output=02b515e203c3f926 input=b0efd1a6ce0e576e]*/ { - static char *kwlist[] = {"qid", NULL}; - qidarg_converter_data qidarg = {0}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:bind", kwlist, - qidarg_converter, &qidarg)) { - return NULL; - } - int64_t qid = qidarg.id; - // XXX Check module state if bound already. int err = _queues_incref(&_globals.queues, qid); - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { return NULL; } @@ -1699,82 +1704,65 @@ queuesmod_bind(PyObject *self, PyObject *args, PyObject *kwds) Py_RETURN_NONE; } -PyDoc_STRVAR(queuesmod_bind_doc, -"bind(qid)\n\ -\n\ -Take a reference to the identified queue.\n\ -The queue is not destroyed until there are no references left."); +/*[clinic input] +_interpqueues.release + qid: qidarg + +Release a reference to the queue. + +The queue is destroyed once there are no references left. +[clinic start generated code]*/ static PyObject * -queuesmod_release(PyObject *self, PyObject *args, PyObject *kwds) +_interpqueues_release_impl(PyObject *module, int64_t qid) +/*[clinic end generated code: output=a59545d7c61fc6ee input=664125cf0262ff6f]*/ { // Note that only the current interpreter is affected. - static char *kwlist[] = {"qid", NULL}; - qidarg_converter_data qidarg = {0}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O&:release", kwlist, - qidarg_converter, &qidarg)) { - return NULL; - } - int64_t qid = qidarg.id; // XXX Check module state if bound already. // XXX Update module state. int err = _queues_decref(&_globals.queues, qid); - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { return NULL; } Py_RETURN_NONE; } -PyDoc_STRVAR(queuesmod_release_doc, -"release(qid)\n\ -\n\ -Release a reference to the queue.\n\ -The queue is destroyed once there are no references left."); +/*[clinic input] +_interpqueues.get_maxsize + qid: qidarg + +Return the maximum number of items in the queue. +[clinic start generated code]*/ static PyObject * -queuesmod_get_maxsize(PyObject *self, PyObject *args, PyObject *kwds) +_interpqueues_get_maxsize_impl(PyObject *module, int64_t qid) +/*[clinic end generated code: output=074202b9c6dc37bf input=ef55def3496cc379]*/ { - static char *kwlist[] = {"qid", NULL}; - qidarg_converter_data qidarg = {0}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O&:get_maxsize", kwlist, - qidarg_converter, &qidarg)) { - return NULL; - } - int64_t qid = qidarg.id; - Py_ssize_t maxsize = -1; int err = queue_get_maxsize(&_globals.queues, qid, &maxsize); - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { return NULL; } return PyLong_FromLongLong(maxsize); } -PyDoc_STRVAR(queuesmod_get_maxsize_doc, -"get_maxsize(qid)\n\ -\n\ -Return the maximum number of items in the queue."); +/*[clinic input] +_interpqueues.get_queue_defaults + qid: qidarg + +Return the queue's default values, set when it was created. +[clinic start generated code]*/ static PyObject * -queuesmod_get_queue_defaults(PyObject *self, PyObject *args, PyObject *kwds) +_interpqueues_get_queue_defaults_impl(PyObject *module, int64_t qid) +/*[clinic end generated code: output=b1b8b8103834191a input=3102315a7bff77fc]*/ { - static char *kwlist[] = {"qid", NULL}; - qidarg_converter_data qidarg = {0}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O&:get_queue_defaults", kwlist, - qidarg_converter, &qidarg)) { - return NULL; - } - int64_t qid = qidarg.id; - struct _queuedefaults defaults = {0}; int err = queue_get_defaults(&_globals.queues, qid, &defaults); - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { return NULL; } @@ -1782,26 +1770,20 @@ queuesmod_get_queue_defaults(PyObject *self, PyObject *args, PyObject *kwds) return res; } -PyDoc_STRVAR(queuesmod_get_queue_defaults_doc, -"get_queue_defaults(qid)\n\ -\n\ -Return the queue's default values, set when it was created."); +/*[clinic input] +_interpqueues.is_full + qid: qidarg + +Return true if the queue has a maxsize and has reached it. +[clinic start generated code]*/ static PyObject * -queuesmod_is_full(PyObject *self, PyObject *args, PyObject *kwds) +_interpqueues_is_full_impl(PyObject *module, int64_t qid) +/*[clinic end generated code: output=47a6e18477cddfee input=25d86a327ed3a2e7]*/ { - static char *kwlist[] = {"qid", NULL}; - qidarg_converter_data qidarg = {0}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O&:is_full", kwlist, - qidarg_converter, &qidarg)) { - return NULL; - } - int64_t qid = qidarg.id; - int is_full = 0; int err = queue_is_full(&_globals.queues, qid, &is_full); - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { return NULL; } if (is_full) { @@ -1810,54 +1792,42 @@ queuesmod_is_full(PyObject *self, PyObject *args, PyObject *kwds) Py_RETURN_FALSE; } -PyDoc_STRVAR(queuesmod_is_full_doc, -"is_full(qid)\n\ -\n\ -Return true if the queue has a maxsize and has reached it."); +/*[clinic input] +_interpqueues.get_count + qid: qidarg + +Return the number of items in the queue. +[clinic start generated code]*/ static PyObject * -queuesmod_get_count(PyObject *self, PyObject *args, PyObject *kwds) +_interpqueues_get_count_impl(PyObject *module, int64_t qid) +/*[clinic end generated code: output=fb9e66e829cdd964 input=ce47690e7598884b]*/ { - static char *kwlist[] = {"qid", NULL}; - qidarg_converter_data qidarg = {0}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O&:get_count", kwlist, - qidarg_converter, &qidarg)) { - return NULL; - } - int64_t qid = qidarg.id; - Py_ssize_t count = -1; int err = queue_get_count(&_globals.queues, qid, &count); - if (handle_queue_error(err, self, qid)) { + if (handle_queue_error(err, module, qid)) { return NULL; } assert(count >= 0); return PyLong_FromSsize_t(count); } -PyDoc_STRVAR(queuesmod_get_count_doc, -"get_count(qid)\n\ -\n\ -Return the number of items in the queue."); +/*[clinic input] +_interpqueues._register_heap_types + queuetype: object(subclass_of='&PyType_Type', type='PyTypeObject *') + emptyerror: object + fullerror: object + +Return the number of items in the queue. +[clinic start generated code]*/ static PyObject * -queuesmod__register_heap_types(PyObject *self, PyObject *args, PyObject *kwds) -{ - static char *kwlist[] = {"queuetype", "emptyerror", "fullerror", NULL}; - PyObject *queuetype; - PyObject *emptyerror; - PyObject *fullerror; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OOO:_register_heap_types", kwlist, - &queuetype, &emptyerror, &fullerror)) { - return NULL; - } - if (!PyType_Check(queuetype)) { - PyErr_SetString(PyExc_TypeError, - "expected a type for 'queuetype'"); - return NULL; - } +_interpqueues__register_heap_types_impl(PyObject *module, + PyTypeObject *queuetype, + PyObject *emptyerror, + PyObject *fullerror) +/*[clinic end generated code: output=f33f6e8b5af905cd input=57d24ae405eda521]*/ +{ if (!PyExceptionClass_Check(emptyerror)) { PyErr_SetString(PyExc_TypeError, "expected an exception type for 'emptyerror'"); @@ -1869,9 +1839,9 @@ queuesmod__register_heap_types(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - module_state *state = get_module_state(self); + module_state *state = get_module_state(module); - if (set_external_queue_type(state, (PyTypeObject *)queuetype) < 0) { + if (set_external_queue_type(state, queuetype) < 0) { return NULL; } if (set_external_exc_types(state, emptyerror, fullerror) < 0) { @@ -1882,30 +1852,18 @@ queuesmod__register_heap_types(PyObject *self, PyObject *args, PyObject *kwds) } static PyMethodDef module_functions[] = { - {"create", _PyCFunction_CAST(queuesmod_create), - METH_VARARGS | METH_KEYWORDS, queuesmod_create_doc}, - {"destroy", _PyCFunction_CAST(queuesmod_destroy), - METH_VARARGS | METH_KEYWORDS, queuesmod_destroy_doc}, - {"list_all", queuesmod_list_all, - METH_NOARGS, queuesmod_list_all_doc}, - {"put", _PyCFunction_CAST(queuesmod_put), - METH_VARARGS | METH_KEYWORDS, queuesmod_put_doc}, - {"get", _PyCFunction_CAST(queuesmod_get), - METH_VARARGS | METH_KEYWORDS, queuesmod_get_doc}, - {"bind", _PyCFunction_CAST(queuesmod_bind), - METH_VARARGS | METH_KEYWORDS, queuesmod_bind_doc}, - {"release", _PyCFunction_CAST(queuesmod_release), - METH_VARARGS | METH_KEYWORDS, queuesmod_release_doc}, - {"get_maxsize", _PyCFunction_CAST(queuesmod_get_maxsize), - METH_VARARGS | METH_KEYWORDS, queuesmod_get_maxsize_doc}, - {"get_queue_defaults", _PyCFunction_CAST(queuesmod_get_queue_defaults), - METH_VARARGS | METH_KEYWORDS, queuesmod_get_queue_defaults_doc}, - {"is_full", _PyCFunction_CAST(queuesmod_is_full), - METH_VARARGS | METH_KEYWORDS, queuesmod_is_full_doc}, - {"get_count", _PyCFunction_CAST(queuesmod_get_count), - METH_VARARGS | METH_KEYWORDS, queuesmod_get_count_doc}, - {"_register_heap_types", _PyCFunction_CAST(queuesmod__register_heap_types), - METH_VARARGS | METH_KEYWORDS, NULL}, + _INTERPQUEUES_CREATE_METHODDEF + _INTERPQUEUES_DESTROY_METHODDEF + _INTERPQUEUES_LIST_ALL_METHODDEF + _INTERPQUEUES_PUT_METHODDEF + _INTERPQUEUES_GET_METHODDEF + _INTERPQUEUES_BIND_METHODDEF + _INTERPQUEUES_RELEASE_METHODDEF + _INTERPQUEUES_GET_MAXSIZE_METHODDEF + _INTERPQUEUES_GET_QUEUE_DEFAULTS_METHODDEF + _INTERPQUEUES_IS_FULL_METHODDEF + _INTERPQUEUES_GET_COUNT_METHODDEF + _INTERPQUEUES__REGISTER_HEAP_TYPES_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -1942,6 +1900,7 @@ module_exec(PyObject *mod) } static struct PyModuleDef_Slot module_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index faf3b25b68c4eb..e7a91ced48f176 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -15,17 +15,24 @@ #include "pycore_pybuffer.h" // _PyBuffer_ReleaseInInterpreterAndRawFree() #include "pycore_pylifecycle.h" // _PyInterpreterConfig_AsDict() #include "pycore_pystate.h" // _PyInterpreterState_IsRunningMain() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #include "marshal.h" // PyMarshal_ReadObjectFromString() #include "_interpreters_common.h" +#include "clinic/_interpretersmodule.c.h" #define MODULE_NAME _interpreters #define MODULE_NAME_STR Py_STRINGIFY(MODULE_NAME) #define MODINIT_FUNC_NAME RESOLVE_MODINIT_FUNC_NAME(MODULE_NAME) +/*[clinic input] +module _interpreters +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=bfd967980a0de892]*/ + static PyInterpreterState * _get_current_interp(void) { @@ -790,19 +797,16 @@ get_summary(PyInterpreterState *interp) Py_DECREF(idobj); return NULL; } - PyObject *res = PyTuple_Pack(2, idobj, whenceobj); - Py_DECREF(idobj); - Py_DECREF(whenceobj); - return res; + return _PyTuple_FromPairSteal(idobj, whenceobj); } +// Not converted to Argument Clinic because the function uses ``**kwargs``. static PyObject * interp_new_config(PyObject *self, PyObject *args, PyObject *kwds) { const char *name = NULL; - if (!PyArg_ParseTuple(args, "|s:" MODULE_NAME_STR ".new_config", - &name)) + if (!PyArg_ParseTuple(args, "|s:" MODULE_NAME_STR ".new_config", &name)) { return NULL; } @@ -830,7 +834,8 @@ interp_new_config(PyObject *self, PyObject *args, PyObject *kwds) } PyDoc_STRVAR(new_config_doc, -"new_config(name='isolated', /, **overrides) -> type.SimpleNamespace\n\ +"new_config($module, name='isolated', /, **overrides)\n\ +--\n\ \n\ Return a representation of a new PyInterpreterConfig.\n\ \n\ @@ -841,17 +846,28 @@ Any keyword arguments are set on the corresponding config fields,\n\ overriding the initial values."); +/*[clinic input] +_interpreters.create + config as configobj: object(py_default="'isolated'") = NULL + * + reqrefs: bool = False + +Create a new interpreter and return a unique generated ID. + +The caller is responsible for destroying the interpreter before exiting, +typically by using _interpreters.destroy(). This can be managed +automatically by passing "reqrefs=True" and then using _incref() and +_decref() appropriately. + +"config" must be a valid interpreter config or the name of a +predefined config ('isolated' or 'legacy'). The default +is 'isolated'. +[clinic start generated code]*/ + static PyObject * -interp_create(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_create_impl(PyObject *module, PyObject *configobj, int reqrefs) +/*[clinic end generated code: output=c1cc6835b1277c16 input=235ce396a23624d5]*/ { - static char *kwlist[] = {"config", "reqrefs", NULL}; - PyObject *configobj = NULL; - int reqrefs = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O$p:create", kwlist, - &configobj, &reqrefs)) { - return NULL; - } - PyInterpreterConfig config; if (config_from_object(configobj, &config) < 0) { return NULL; @@ -885,34 +901,22 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds) } -PyDoc_STRVAR(create_doc, -"create([config], *, reqrefs=False) -> ID\n\ -\n\ -Create a new interpreter and return a unique generated ID.\n\ -\n\ -The caller is responsible for destroying the interpreter before exiting,\n\ -typically by using _interpreters.destroy(). This can be managed \n\ -automatically by passing \"reqrefs=True\" and then using _incref() and\n\ -_decref() appropriately.\n\ -\n\ -\"config\" must be a valid interpreter config or the name of a\n\ -predefined config (\"isolated\" or \"legacy\"). The default\n\ -is \"isolated\"."); +/*[clinic input] +_interpreters.destroy + id: object + * + restrict as restricted: bool = False +Destroy the identified interpreter. + +Attempting to destroy the current interpreter raises InterpreterError. +So does an unrecognized ID. +[clinic start generated code]*/ static PyObject * -interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_destroy_impl(PyObject *module, PyObject *id, int restricted) +/*[clinic end generated code: output=0bc20da8700ab4dd input=561bdd6537639d40]*/ { - static char *kwlist[] = {"id", "restrict", NULL}; - PyObject *id; - int restricted = 0; - // XXX Use "L" for id? - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|$p:destroy", kwlist, &id, &restricted)) - { - return NULL; - } - // Look up the interpreter. int reqready = 0; PyInterpreterState *interp = \ @@ -946,27 +950,19 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) Py_RETURN_NONE; } -PyDoc_STRVAR(destroy_doc, -"destroy(id, *, restrict=False)\n\ -\n\ -Destroy the identified interpreter.\n\ -\n\ -Attempting to destroy the current interpreter raises InterpreterError.\n\ -So does an unrecognized ID."); +/*[clinic input] +_interpreters.list_all + * + require_ready as reqready: bool = False + +Return a list containing the ID of every existing interpreter. +[clinic start generated code]*/ static PyObject * -interp_list_all(PyObject *self, PyObject *args, PyObject *kwargs) +_interpreters_list_all_impl(PyObject *module, int reqready) +/*[clinic end generated code: output=3f21c1a7c78043c0 input=35bae91c381a2cf9]*/ { - static char *kwlist[] = {"require_ready", NULL}; - int reqready = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "|$p:" MODULE_NAME_STR ".list_all", - kwlist, &reqready)) - { - return NULL; - } - PyObject *ids = PyList_New(0); if (ids == NULL) { return NULL; @@ -995,14 +991,16 @@ interp_list_all(PyObject *self, PyObject *args, PyObject *kwargs) return ids; } -PyDoc_STRVAR(list_all_doc, -"list_all() -> [(ID, whence)]\n\ -\n\ -Return a list containing the ID of every existing interpreter."); +/*[clinic input] +_interpreters.get_current + +Return (ID, whence) of the current interpreter. +[clinic start generated code]*/ static PyObject * -interp_get_current(PyObject *self, PyObject *Py_UNUSED(ignored)) +_interpreters_get_current_impl(PyObject *module) +/*[clinic end generated code: output=03161c8fcc0136eb input=37fb2c067c14d543]*/ { PyInterpreterState *interp =_get_current_interp(); if (interp == NULL) { @@ -1012,39 +1010,38 @@ interp_get_current(PyObject *self, PyObject *Py_UNUSED(ignored)) return get_summary(interp); } -PyDoc_STRVAR(get_current_doc, -"get_current() -> (ID, whence)\n\ -\n\ -Return the ID of current interpreter."); +/*[clinic input] +_interpreters.get_main + +Return (ID, whence) of the main interpreter. +[clinic start generated code]*/ static PyObject * -interp_get_main(PyObject *self, PyObject *Py_UNUSED(ignored)) +_interpreters_get_main_impl(PyObject *module) +/*[clinic end generated code: output=9647288aff735557 input=b4ace23ca562146f]*/ { PyInterpreterState *interp = _PyInterpreterState_Main(); assert(_PyInterpreterState_IsReady(interp)); return get_summary(interp); } -PyDoc_STRVAR(get_main_doc, -"get_main() -> (ID, whence)\n\ -\n\ -Return the ID of main interpreter."); +/*[clinic input] +_interpreters.set___main___attrs + id: object + updates: object(subclass_of='&PyDict_Type') + * + restrict as restricted: bool = False + +Bind the given attributes in the interpreter's __main__ module. +[clinic start generated code]*/ static PyObject * -interp_set___main___attrs(PyObject *self, PyObject *args, PyObject *kwargs) +_interpreters_set___main___attrs_impl(PyObject *module, PyObject *id, + PyObject *updates, int restricted) +/*[clinic end generated code: output=f3803010cb452bf0 input=d16ab8d81371f86a]*/ { - static char *kwlist[] = {"id", "updates", "restrict", NULL}; - PyObject *id, *updates; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "OO!|$p:" MODULE_NAME_STR ".set___main___attrs", - kwlist, &id, &PyDict_Type, &updates, &restricted)) - { - return NULL; - } - // Look up the interpreter. int reqready = 1; PyInterpreterState *interp = \ @@ -1091,11 +1088,6 @@ interp_set___main___attrs(PyObject *self, PyObject *args, PyObject *kwargs) Py_RETURN_NONE; } -PyDoc_STRVAR(set___main___attrs_doc, -"set___main___attrs(id, ns, *, restrict=False)\n\ -\n\ -Bind the given attributes in the interpreter's __main__ module."); - static PyObject * _handle_script_error(struct run_result *runres) @@ -1109,23 +1101,36 @@ _handle_script_error(struct run_result *runres) return runres->excinfo; } +/*[clinic input] +_interpreters.exec + id: object + code: object + shared: object(subclass_of='&PyDict_Type', c_default='NULL') = {} + * + restrict as restricted: bool = False + +Execute the provided code in the identified interpreter. + +This is equivalent to running the builtin exec() under the target +interpreter, using the __dict__ of its __main__ module as both +globals and locals. + +"code" may be a string containing the text of a Python script. + +Functions (and code objects) are also supported, with some restrictions. +The code/function must not take any arguments or be a closure +(i.e. have cell vars). Methods and other callables are not supported. + +If a function is provided, its code object is used and all its state +is ignored, including its __globals__ dict. +[clinic start generated code]*/ + static PyObject * -interp_exec(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_exec_impl(PyObject *module, PyObject *id, PyObject *code, + PyObject *shared, int restricted) +/*[clinic end generated code: output=492057c4f10dc304 input=5a22c1ed0c5dbcf3]*/ { -#define FUNCNAME MODULE_NAME_STR ".exec" PyThreadState *tstate = _PyThreadState_GET(); - static char *kwlist[] = {"id", "code", "shared", "restrict", NULL}; - PyObject *id, *code; - PyObject *shared = NULL; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OO|O!$p:" FUNCNAME, kwlist, - &id, &code, &PyDict_Type, &shared, - &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "exec code for"); @@ -1150,43 +1155,29 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds) } assert(runres.result == NULL); Py_RETURN_NONE; -#undef FUNCNAME } -PyDoc_STRVAR(exec_doc, -"exec(id, code, shared=None, *, restrict=False)\n\ -\n\ -Execute the provided code in the identified interpreter.\n\ -This is equivalent to running the builtin exec() under the target\n\ -interpreter, using the __dict__ of its __main__ module as both\n\ -globals and locals.\n\ -\n\ -\"code\" may be a string containing the text of a Python script.\n\ -\n\ -Functions (and code objects) are also supported, with some restrictions.\n\ -The code/function must not take any arguments or be a closure\n\ -(i.e. have cell vars). Methods and other callables are not supported.\n\ -\n\ -If a function is provided, its code object is used and all its state\n\ -is ignored, including its __globals__ dict."); +/*[clinic input] +_interpreters.run_string + id: object + script: unicode + shared: object(subclass_of='&PyDict_Type', c_default='NULL') = {} + * + restrict as restricted: bool = False + +Execute the provided string in the identified interpreter. + +(See _interpreters.exec().) +[clinic start generated code]*/ static PyObject * -interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_run_string_impl(PyObject *module, PyObject *id, + PyObject *script, PyObject *shared, + int restricted) +/*[clinic end generated code: output=a30a64fb9ad396a2 input=51ce549b9a8dbe21]*/ { #define FUNCNAME MODULE_NAME_STR ".run_string" PyThreadState *tstate = _PyThreadState_GET(); - static char *kwlist[] = {"id", "script", "shared", "restrict", NULL}; - PyObject *id, *script; - PyObject *shared = NULL; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OU|O!$p:" FUNCNAME, kwlist, - &id, &script, &PyDict_Type, &shared, - &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "run a string in"); @@ -1217,30 +1208,30 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) #undef FUNCNAME } -PyDoc_STRVAR(run_string_doc, -"run_string(id, script, shared=None, *, restrict=False)\n\ -\n\ -Execute the provided string in the identified interpreter.\n\ -\n\ -(See " MODULE_NAME_STR ".exec()."); +/*[clinic input] +_interpreters.run_func + id: object + func: object + shared: object(subclass_of='&PyDict_Type', c_default='NULL') = {} + * + restrict as restricted: bool = False + +Execute the body of the provided function in the identified interpreter. + +Code objects are also supported. In both cases, closures and args +are not supported. Methods and other callables are not supported +either. + +(See _interpreters.exec().) +[clinic start generated code]*/ static PyObject * -interp_run_func(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_run_func_impl(PyObject *module, PyObject *id, PyObject *func, + PyObject *shared, int restricted) +/*[clinic end generated code: output=131f7202ca4a0c5e input=162b29823b33d5cc]*/ { #define FUNCNAME MODULE_NAME_STR ".run_func" PyThreadState *tstate = _PyThreadState_GET(); - static char *kwlist[] = {"id", "func", "shared", "restrict", NULL}; - PyObject *id, *func; - PyObject *shared = NULL; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OO|O!$p:" FUNCNAME, kwlist, - &id, &func, &PyDict_Type, &shared, - &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "run a function in"); @@ -1280,37 +1271,28 @@ interp_run_func(PyObject *self, PyObject *args, PyObject *kwds) #undef FUNCNAME } -PyDoc_STRVAR(run_func_doc, -"run_func(id, func, shared=None, *, restrict=False)\n\ -\n\ -Execute the body of the provided function in the identified interpreter.\n\ -Code objects are also supported. In both cases, closures and args\n\ -are not supported. Methods and other callables are not supported either.\n\ -\n\ -(See " MODULE_NAME_STR ".exec()."); +/*[clinic input] +_interpreters.call + id: object + callable: object + args: object(subclass_of='&PyTuple_Type', c_default='NULL') = () + kwargs: object(subclass_of='&PyDict_Type', c_default='NULL') = {} + * + preserve_exc: bool = False + restrict as restricted: bool = False + +Call the provided object in the identified interpreter. + +Pass the given args and kwargs, if possible. +[clinic start generated code]*/ static PyObject * -interp_call(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_call_impl(PyObject *module, PyObject *id, PyObject *callable, + PyObject *args, PyObject *kwargs, int preserve_exc, + int restricted) +/*[clinic end generated code: output=b7a4a27d72df3ebc input=b026d0b212a575e6]*/ { -#define FUNCNAME MODULE_NAME_STR ".call" PyThreadState *tstate = _PyThreadState_GET(); - static char *kwlist[] = {"id", "callable", "args", "kwargs", - "preserve_exc", "restrict", NULL}; - PyObject *id, *callable; - PyObject *args_obj = NULL; - PyObject *kwargs_obj = NULL; - int preserve_exc = 0; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "OO|O!O!$pp:" FUNCNAME, kwlist, - &id, &callable, - &PyTuple_Type, &args_obj, - &PyDict_Type, &kwargs_obj, - &preserve_exc, &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "make a call in"); @@ -1319,7 +1301,7 @@ interp_call(PyObject *self, PyObject *args, PyObject *kwds) } struct interp_call call = {0}; - if (_interp_call_pack(tstate, &call, callable, args_obj, kwargs_obj) < 0) { + if (_interp_call_pack(tstate, &call, callable, args, kwargs) < 0) { return NULL; } @@ -1341,26 +1323,21 @@ interp_call(PyObject *self, PyObject *args, PyObject *kwds) _interp_call_clear(&call); _run_result_clear(&runres); return res_and_exc; -#undef FUNCNAME } -PyDoc_STRVAR(call_doc, -"call(id, callable, args=None, kwargs=None, *, restrict=False)\n\ -\n\ -Call the provided object in the identified interpreter.\n\ -Pass the given args and kwargs, if possible."); +/*[clinic input] +@permit_long_summary +_interpreters.is_shareable + obj: object + +Return True if the object's data may be shared between interpreters and False otherwise. +[clinic start generated code]*/ static PyObject * -object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_is_shareable_impl(PyObject *module, PyObject *obj) +/*[clinic end generated code: output=227856926a22940b input=95f888d35a6d4bb3]*/ { - static char *kwlist[] = {"obj", NULL}; - PyObject *obj; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O:is_shareable", kwlist, &obj)) { - return NULL; - } - PyThreadState *tstate = _PyThreadState_GET(); if (_PyObject_CheckXIData(tstate, obj) == 0) { Py_RETURN_TRUE; @@ -1369,26 +1346,20 @@ object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds) Py_RETURN_FALSE; } -PyDoc_STRVAR(is_shareable_doc, -"is_shareable(obj) -> bool\n\ -\n\ -Return True if the object's data may be shared between interpreters and\n\ -False otherwise."); +/*[clinic input] +_interpreters.is_running + id: object + * + restrict as restricted: bool = False + +Return whether or not the identified interpreter is running. +[clinic start generated code]*/ static PyObject * -interp_is_running(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_is_running_impl(PyObject *module, PyObject *id, int restricted) +/*[clinic end generated code: output=32a6225d5ded9bdb input=3291578d04231125]*/ { - static char *kwlist[] = {"id", "restrict", NULL}; - PyObject *id; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|$p:is_running", kwlist, - &id, &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "check if running for"); @@ -1402,31 +1373,28 @@ interp_is_running(PyObject *self, PyObject *args, PyObject *kwds) Py_RETURN_FALSE; } -PyDoc_STRVAR(is_running_doc, -"is_running(id, *, restrict=False) -> bool\n\ -\n\ -Return whether or not the identified interpreter is running."); +/*[clinic input] +@permit_long_summary +_interpreters.get_config + id: object + * + restrict as restricted: bool = False + +Return a representation of the config used to initialize the interpreter. +[clinic start generated code]*/ static PyObject * -interp_get_config(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_get_config_impl(PyObject *module, PyObject *id, int restricted) +/*[clinic end generated code: output=56773353b9b7224a input=8272d9ea9e4fb42a]*/ { - static char *kwlist[] = {"id", "restrict", NULL}; - PyObject *idobj = NULL; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|$p:get_config", kwlist, - &idobj, &restricted)) - { - return NULL; - } - if (idobj == Py_None) { - idobj = NULL; + if (id == Py_None) { + id = NULL; } int reqready = 0; PyInterpreterState *interp = \ - resolve_interp(idobj, restricted, reqready, "get the config of"); + resolve_interp(id, restricted, reqready, "get the config of"); if (interp == NULL) { return NULL; } @@ -1445,23 +1413,18 @@ interp_get_config(PyObject *self, PyObject *args, PyObject *kwds) return configobj; } -PyDoc_STRVAR(get_config_doc, -"get_config(id, *, restrict=False) -> types.SimpleNamespace\n\ -\n\ -Return a representation of the config used to initialize the interpreter."); +/*[clinic input] +_interpreters.whence + id: object + +Return an identifier for where the interpreter was created. +[clinic start generated code]*/ static PyObject * -interp_whence(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_whence_impl(PyObject *module, PyObject *id) +/*[clinic end generated code: output=ef2c21ab106c2c20 input=eeede0a2fbfa2968]*/ { - static char *kwlist[] = {"id", NULL}; - PyObject *id; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O:whence", kwlist, &id)) - { - return NULL; - } - PyInterpreterState *interp = look_up_interp(id); if (interp == NULL) { return NULL; @@ -1471,26 +1434,21 @@ interp_whence(PyObject *self, PyObject *args, PyObject *kwds) return PyLong_FromLong(whence); } -PyDoc_STRVAR(whence_doc, -"whence(id) -> int\n\ -\n\ -Return an identifier for where the interpreter was created."); +/*[clinic input] +_interpreters.incref + id: object + * + implieslink: bool = False + restrict as restricted: bool = False + +[clinic start generated code]*/ static PyObject * -interp_incref(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_incref_impl(PyObject *module, PyObject *id, int implieslink, + int restricted) +/*[clinic end generated code: output=eccaa4e03fbe8ee2 input=a0a614748f2e348c]*/ { - static char *kwlist[] = {"id", "implieslink", "restrict", NULL}; - PyObject *id; - int implieslink = 0; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|$pp:incref", kwlist, - &id, &implieslink, &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "incref"); @@ -1508,18 +1466,18 @@ interp_incref(PyObject *self, PyObject *args, PyObject *kwds) } +/*[clinic input] +_interpreters.decref + id: object + * + restrict as restricted: bool = False + +[clinic start generated code]*/ + static PyObject * -interp_decref(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_decref_impl(PyObject *module, PyObject *id, int restricted) +/*[clinic end generated code: output=5c54db4b22086171 input=c4aa34f09c44e62a]*/ { - static char *kwlist[] = {"id", "restrict", NULL}; - PyObject *id; - int restricted = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|$p:decref", kwlist, &id, &restricted)) - { - return NULL; - } - int reqready = 1; PyInterpreterState *interp = \ resolve_interp(id, restricted, reqready, "decref"); @@ -1533,18 +1491,21 @@ interp_decref(PyObject *self, PyObject *args, PyObject *kwds) } +/*[clinic input] +_interpreters.capture_exception + exc as exc_arg: object = None + +Return a snapshot of an exception. + +If "exc" is None then the current exception, if any, is used (but not +cleared). The returned snapshot is the same as what +_interpreters.exec() returns. +[clinic start generated code]*/ + static PyObject * -capture_exception(PyObject *self, PyObject *args, PyObject *kwds) +_interpreters_capture_exception_impl(PyObject *module, PyObject *exc_arg) +/*[clinic end generated code: output=ef3f5393ef9c88a6 input=4e6289f8f2a47b5b]*/ { - static char *kwlist[] = {"exc", NULL}; - PyObject *exc_arg = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, - "|O:capture_exception", kwlist, - &exc_arg)) - { - return NULL; - } - PyObject *exc = exc_arg; if (exc == NULL || exc == Py_None) { exc = PyErr_GetRaisedException(); @@ -1592,58 +1553,33 @@ capture_exception(PyObject *self, PyObject *args, PyObject *kwds) return captured; } -PyDoc_STRVAR(capture_exception_doc, -"capture_exception(exc=None) -> types.SimpleNamespace\n\ -\n\ -Return a snapshot of an exception. If \"exc\" is None\n\ -then the current exception, if any, is used (but not cleared).\n\ -\n\ -The returned snapshot is the same as what _interpreters.exec() returns."); - static PyMethodDef module_functions[] = { {"new_config", _PyCFunction_CAST(interp_new_config), METH_VARARGS | METH_KEYWORDS, new_config_doc}, - {"create", _PyCFunction_CAST(interp_create), - METH_VARARGS | METH_KEYWORDS, create_doc}, - {"destroy", _PyCFunction_CAST(interp_destroy), - METH_VARARGS | METH_KEYWORDS, destroy_doc}, - {"list_all", _PyCFunction_CAST(interp_list_all), - METH_VARARGS | METH_KEYWORDS, list_all_doc}, - {"get_current", interp_get_current, - METH_NOARGS, get_current_doc}, - {"get_main", interp_get_main, - METH_NOARGS, get_main_doc}, - - {"is_running", _PyCFunction_CAST(interp_is_running), - METH_VARARGS | METH_KEYWORDS, is_running_doc}, - {"get_config", _PyCFunction_CAST(interp_get_config), - METH_VARARGS | METH_KEYWORDS, get_config_doc}, - {"whence", _PyCFunction_CAST(interp_whence), - METH_VARARGS | METH_KEYWORDS, whence_doc}, - {"exec", _PyCFunction_CAST(interp_exec), - METH_VARARGS | METH_KEYWORDS, exec_doc}, - {"call", _PyCFunction_CAST(interp_call), - METH_VARARGS | METH_KEYWORDS, call_doc}, - {"run_string", _PyCFunction_CAST(interp_run_string), - METH_VARARGS | METH_KEYWORDS, run_string_doc}, - {"run_func", _PyCFunction_CAST(interp_run_func), - METH_VARARGS | METH_KEYWORDS, run_func_doc}, - - {"set___main___attrs", _PyCFunction_CAST(interp_set___main___attrs), - METH_VARARGS | METH_KEYWORDS, set___main___attrs_doc}, - - {"incref", _PyCFunction_CAST(interp_incref), - METH_VARARGS | METH_KEYWORDS, NULL}, - {"decref", _PyCFunction_CAST(interp_decref), - METH_VARARGS | METH_KEYWORDS, NULL}, - - {"is_shareable", _PyCFunction_CAST(object_is_shareable), - METH_VARARGS | METH_KEYWORDS, is_shareable_doc}, - - {"capture_exception", _PyCFunction_CAST(capture_exception), - METH_VARARGS | METH_KEYWORDS, capture_exception_doc}, + _INTERPRETERS_CREATE_METHODDEF + _INTERPRETERS_DESTROY_METHODDEF + _INTERPRETERS_LIST_ALL_METHODDEF + _INTERPRETERS_GET_CURRENT_METHODDEF + _INTERPRETERS_GET_MAIN_METHODDEF + + _INTERPRETERS_IS_RUNNING_METHODDEF + _INTERPRETERS_GET_CONFIG_METHODDEF + _INTERPRETERS_WHENCE_METHODDEF + _INTERPRETERS_EXEC_METHODDEF + _INTERPRETERS_CALL_METHODDEF + _INTERPRETERS_RUN_STRING_METHODDEF + _INTERPRETERS_RUN_FUNC_METHODDEF + + _INTERPRETERS_SET___MAIN___ATTRS_METHODDEF + + _INTERPRETERS_INCREF_METHODDEF + _INTERPRETERS_DECREF_METHODDEF + + _INTERPRETERS_IS_SHAREABLE_METHODDEF + + _INTERPRETERS_CAPTURE_EXCEPTION_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -1698,6 +1634,7 @@ module_exec(PyObject *mod) } static struct PyModuleDef_Slot module_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 50fe5d50c91a35..03e6fbe08889d4 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -85,112 +85,113 @@ Open file and return a stream. Raise OSError upon failure. file is either a text or byte string giving the name (and the path if the file isn't in the current working directory) of the file to be opened or an integer file descriptor of the file to be -wrapped. (If a file descriptor is given, it is closed when the +wrapped. (If a file descriptor is given, it is closed when the returned I/O object is closed, unless closefd is set to False.) mode is an optional string that specifies the mode in which the file -is opened. It defaults to 'r' which means open for reading in text +is opened. It defaults to 'r' which means open for reading in text mode. Other common values are 'w' for writing (truncating the file if it already exists), 'x' for creating and writing to a new file, and 'a' for appending (which on some Unix systems, means that all writes append to the end of the file regardless of the current seek position). In text mode, if encoding is not specified the encoding used is platform -dependent: locale.getencoding() is called to get the current locale encoding. -(For reading and writing raw bytes use binary mode and leave encoding -unspecified.) The available modes are: +dependent: locale.getencoding() is called to get the current locale +encoding. (For reading and writing raw bytes use binary mode and leave +encoding unspecified.) The available modes are: -========= =============================================================== +========= ========================================================== Character Meaning ---------- --------------------------------------------------------------- +--------- ---------------------------------------------------------- 'r' open for reading (default) 'w' open for writing, truncating the file first 'x' create a new file and open it for writing -'a' open for writing, appending to the end of the file if it exists +'a' open for writing, appending to the end of the file if it + exists 'b' binary mode 't' text mode (default) '+' open a disk file for updating (reading and writing) -========= =============================================================== +========= ========================================================== -The default mode is 'rt' (open for reading text). For binary random +The default mode is 'rt' (open for reading text). For binary random access, the mode 'w+b' opens and truncates the file to 0 bytes, while -'r+b' opens the file without truncation. The 'x' mode implies 'w' and +'r+b' opens the file without truncation. The 'x' mode implies 'w' and raises an `FileExistsError` if the file already exists. Python distinguishes between files opened in binary and text modes, -even when the underlying operating system doesn't. Files opened in +even when the underlying operating system doesn't. Files opened in binary mode (appending 'b' to the mode argument) return contents as -bytes objects without any decoding. In text mode (the default, or when +bytes objects without any decoding. In text mode (the default, or when 't' is appended to the mode argument), the contents of the file are returned as strings, the bytes having been first decoded using a platform-dependent encoding or using the specified encoding if given. buffering is an optional integer used to set the buffering policy. -Pass 0 to switch buffering off (only allowed in binary mode), 1 to select -line buffering (only usable in text mode), and an integer > 1 to indicate -the size of a fixed-size chunk buffer. When no buffering argument is -given, the default buffering policy works as follows: +Pass 0 to switch buffering off (only allowed in binary mode), 1 to +select line buffering (only usable in text mode), and an integer > 1 to +indicate the size of a fixed-size chunk buffer. When no buffering +argument is given, the default buffering policy works as follows: * Binary files are buffered in fixed-size chunks; the size of the buffer - is max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE) - when the device block size is available. - On most systems, the buffer will typically be 128 kilobytes long. + is max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE) when the device + block size is available. + On most systems, the buffer will typically be 128 kilobytes long. * "Interactive" text files (files for which isatty() returns True) use line buffering. Other text files use the policy described above for binary files. encoding is the name of the encoding used to decode or encode the -file. This should only be used in text mode. The default encoding is +file. This should only be used in text mode. The default encoding is platform dependent, but any encoding supported by Python can be passed. See the codecs module for the list of supported encodings. errors is an optional string that specifies how encoding errors are to -be handled---this argument should not be used in binary mode. Pass +be handled---this argument should not be used in binary mode. Pass 'strict' to raise a ValueError exception if there is an encoding error (the default of None has the same effect), or pass 'ignore' to ignore -errors. (Note that ignoring encoding errors can lead to data loss.) +errors. (Note that ignoring encoding errors can lead to data loss.) See the documentation for codecs.register or run 'help(codecs.Codec)' for a list of the permitted encoding error strings. newline controls how universal newlines works (it only applies to text -mode). It can be None, '', '\n', '\r', and '\r\n'. It works as +mode). It can be None, '', '\n', '\r', and '\r\n'. It works as follows: -* On input, if newline is None, universal newlines mode is - enabled. Lines in the input can end in '\n', '\r', or '\r\n', and - these are translated into '\n' before being returned to the - caller. If it is '', universal newline mode is enabled, but line - endings are returned to the caller untranslated. If it has any of - the other legal values, input lines are only terminated by the given - string, and the line ending is returned to the caller untranslated. +* On input, if newline is None, universal newlines mode is enabled. + Lines in the input can end in '\n', '\r', or '\r\n', and these are + translated into '\n' before being returned to the caller. If it is + '', universal newline mode is enabled, but line endings are returned + to the caller untranslated. If it has any of the other legal values, + input lines are only terminated by the given string, and the line + ending is returned to the caller untranslated. * On output, if newline is None, any '\n' characters written are - translated to the system default line separator, os.linesep. If - newline is '' or '\n', no translation takes place. If newline is any + translated to the system default line separator, os.linesep. If + newline is '' or '\n', no translation takes place. If newline is any of the other legal values, any '\n' characters written are translated to the given string. If closefd is False, the underlying file descriptor will be kept open -when the file is closed. This does not work when a file name is given +when the file is closed. This does not work when a file name is given and must be True in that case. -A custom opener can be used by passing a callable as *opener*. The +A custom opener can be used by passing a callable as *opener*. The underlying file descriptor for the file object is then obtained by -calling *opener* with (*file*, *flags*). *opener* must return an open +calling *opener* with (*file*, *flags*). *opener* must return an open file descriptor (passing os.open as *opener* results in functionality similar to passing None). open() returns a file object whose type depends on the mode, and through which the standard file operations such as reading and writing -are performed. When open() is used to open a file in a text mode ('w', -'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open +are performed. When open() is used to open a file in a text mode ('w', +'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open a file in a binary mode, the returned class varies: in read binary mode, it returns a BufferedReader; in write binary and append binary modes, it returns a BufferedWriter, and in read/write mode, it returns a BufferedRandom. It is also possible to use a string or bytearray as a file for both -reading and writing. For strings StringIO can be used like a file +reading and writing. For strings StringIO can be used like a file opened in a text mode, and for bytes a BytesIO can be used like a file opened in a binary mode. [clinic start generated code]*/ @@ -199,7 +200,7 @@ static PyObject * _io_open_impl(PyObject *module, PyObject *file, const char *mode, int buffering, const char *encoding, const char *errors, const char *newline, int closefd, PyObject *opener) -/*[clinic end generated code: output=aefafc4ce2b46dc0 input=28027fdaabb8d744]*/ +/*[clinic end generated code: output=aefafc4ce2b46dc0 input=b3cefa70bef404b3]*/ { size_t i; @@ -504,14 +505,14 @@ _io.open_code Opens the provided file with the intent to import the contents. -This may perform extra validation beyond open(), but is otherwise interchangeable -with calling open(path, 'rb'). +This may perform extra validation beyond open(), but is otherwise +interchangeable with calling open(path, 'rb'). [clinic start generated code]*/ static PyObject * _io_open_code_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=2fe4ecbd6f3d6844 input=f5c18e23f4b2ed9f]*/ +/*[clinic end generated code: output=2fe4ecbd6f3d6844 input=2803c35aeb63c719]*/ { return PyFile_OpenCodeObject(path); } @@ -679,40 +680,40 @@ iomodule_exec(PyObject *m) } // Base classes - ADD_TYPE(m, state->PyIncrementalNewlineDecoder_Type, &nldecoder_spec, NULL); - ADD_TYPE(m, state->PyBytesIOBuffer_Type, &bytesiobuf_spec, NULL); - ADD_TYPE(m, state->PyIOBase_Type, &iobase_spec, NULL); + ADD_TYPE(m, state->PyIncrementalNewlineDecoder_Type, &_Py_nldecoder_spec, NULL); + ADD_TYPE(m, state->PyBytesIOBuffer_Type, &_Py_bytesiobuf_spec, NULL); + ADD_TYPE(m, state->PyIOBase_Type, &_Py_iobase_spec, NULL); // PyIOBase_Type subclasses - ADD_TYPE(m, state->PyTextIOBase_Type, &textiobase_spec, + ADD_TYPE(m, state->PyTextIOBase_Type, &_Py_textiobase_spec, state->PyIOBase_Type); - ADD_TYPE(m, state->PyBufferedIOBase_Type, &bufferediobase_spec, + ADD_TYPE(m, state->PyBufferedIOBase_Type, &_Py_bufferediobase_spec, state->PyIOBase_Type); - ADD_TYPE(m, state->PyRawIOBase_Type, &rawiobase_spec, + ADD_TYPE(m, state->PyRawIOBase_Type, &_Py_rawiobase_spec, state->PyIOBase_Type); // PyBufferedIOBase_Type(PyIOBase_Type) subclasses - ADD_TYPE(m, state->PyBytesIO_Type, &bytesio_spec, state->PyBufferedIOBase_Type); - ADD_TYPE(m, state->PyBufferedWriter_Type, &bufferedwriter_spec, + ADD_TYPE(m, state->PyBytesIO_Type, &_Py_bytesio_spec, state->PyBufferedIOBase_Type); + ADD_TYPE(m, state->PyBufferedWriter_Type, &_Py_bufferedwriter_spec, state->PyBufferedIOBase_Type); - ADD_TYPE(m, state->PyBufferedReader_Type, &bufferedreader_spec, + ADD_TYPE(m, state->PyBufferedReader_Type, &_Py_bufferedreader_spec, state->PyBufferedIOBase_Type); - ADD_TYPE(m, state->PyBufferedRWPair_Type, &bufferedrwpair_spec, + ADD_TYPE(m, state->PyBufferedRWPair_Type, &_Py_bufferedrwpair_spec, state->PyBufferedIOBase_Type); - ADD_TYPE(m, state->PyBufferedRandom_Type, &bufferedrandom_spec, + ADD_TYPE(m, state->PyBufferedRandom_Type, &_Py_bufferedrandom_spec, state->PyBufferedIOBase_Type); // PyRawIOBase_Type(PyIOBase_Type) subclasses - ADD_TYPE(m, state->PyFileIO_Type, &fileio_spec, state->PyRawIOBase_Type); + ADD_TYPE(m, state->PyFileIO_Type, &_Py_fileio_spec, state->PyRawIOBase_Type); #ifdef HAVE_WINDOWS_CONSOLE_IO - ADD_TYPE(m, state->PyWindowsConsoleIO_Type, &winconsoleio_spec, + ADD_TYPE(m, state->PyWindowsConsoleIO_Type, &_Py_winconsoleio_spec, state->PyRawIOBase_Type); #endif // PyTextIOBase_Type(PyIOBase_Type) subclasses - ADD_TYPE(m, state->PyStringIO_Type, &stringio_spec, state->PyTextIOBase_Type); - ADD_TYPE(m, state->PyTextIOWrapper_Type, &textiowrapper_spec, + ADD_TYPE(m, state->PyStringIO_Type, &_Py_stringio_spec, state->PyTextIOBase_Type); + ADD_TYPE(m, state->PyTextIOWrapper_Type, &_Py_textiowrapper_spec, state->PyTextIOBase_Type); #undef ADD_TYPE @@ -720,6 +721,7 @@ iomodule_exec(PyObject *m) } static struct PyModuleDef_Slot iomodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, iomodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_io/_iomodule.h b/Modules/_io/_iomodule.h index 18cf20edf26f7d..4ae487c8e2adf7 100644 --- a/Modules/_io/_iomodule.h +++ b/Modules/_io/_iomodule.h @@ -9,23 +9,23 @@ #include "structmember.h" /* Type specs */ -extern PyType_Spec bufferediobase_spec; -extern PyType_Spec bufferedrandom_spec; -extern PyType_Spec bufferedreader_spec; -extern PyType_Spec bufferedrwpair_spec; -extern PyType_Spec bufferedwriter_spec; -extern PyType_Spec bytesio_spec; -extern PyType_Spec bytesiobuf_spec; -extern PyType_Spec fileio_spec; -extern PyType_Spec iobase_spec; -extern PyType_Spec nldecoder_spec; -extern PyType_Spec rawiobase_spec; -extern PyType_Spec stringio_spec; -extern PyType_Spec textiobase_spec; -extern PyType_Spec textiowrapper_spec; +extern PyType_Spec _Py_bufferediobase_spec; +extern PyType_Spec _Py_bufferedrandom_spec; +extern PyType_Spec _Py_bufferedreader_spec; +extern PyType_Spec _Py_bufferedrwpair_spec; +extern PyType_Spec _Py_bufferedwriter_spec; +extern PyType_Spec _Py_bytesio_spec; +extern PyType_Spec _Py_bytesiobuf_spec; +extern PyType_Spec _Py_fileio_spec; +extern PyType_Spec _Py_iobase_spec; +extern PyType_Spec _Py_nldecoder_spec; +extern PyType_Spec _Py_rawiobase_spec; +extern PyType_Spec _Py_stringio_spec; +extern PyType_Spec _Py_textiobase_spec; +extern PyType_Spec _Py_textiowrapper_spec; #ifdef HAVE_WINDOWS_CONSOLE_IO -extern PyType_Spec winconsoleio_spec; +extern PyType_Spec _Py_winconsoleio_spec; #endif /* These functions are used as METH_NOARGS methods, are normally called diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 25c8bf8b3d508b..5537947f6a51c1 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -172,6 +172,7 @@ _io__BufferedIOBase_read_impl(PyObject *self, PyTypeObject *cls, } /*[clinic input] +@permit_long_summary _io._BufferedIOBase.read1 cls: defining_class @@ -187,7 +188,7 @@ A short result does not imply that EOF is imminent. static PyObject * _io__BufferedIOBase_read1_impl(PyObject *self, PyTypeObject *cls, int Py_UNUSED(size)) -/*[clinic end generated code: output=2e7fc62972487eaa input=af76380e020fd9e6]*/ +/*[clinic end generated code: output=2e7fc62972487eaa input=1e76df255063afd6]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return bufferediobase_unsupported(state, "read1"); @@ -361,16 +362,24 @@ _enter_buffered_busy(buffered *self) } #define IS_CLOSED(self) \ - (!self->buffer || \ + (!self->buffer ? 1 : \ (self->fast_closed_checks \ ? _PyFileIO_closed(self->raw) \ : buffered_closed(self))) #define CHECK_CLOSED(self, error_msg) \ - if (IS_CLOSED(self) && (Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t) == 0)) { \ - PyErr_SetString(PyExc_ValueError, error_msg); \ - return NULL; \ - } \ + do { \ + int _closed = IS_CLOSED(self); \ + if (_closed < 0) { \ + return NULL; \ + } \ + if (_closed && \ + (Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t) == 0)) \ + { \ + PyErr_SetString(PyExc_ValueError, error_msg); \ + return NULL; \ + } \ + } while (0); #define VALID_READ_BUFFER(self) \ (self->readable && self->read_end != -1) @@ -552,8 +561,8 @@ _io__Buffered_close_impl(buffered *self) if (!ENTER_BUFFERED(self)) { return NULL; } - - r = buffered_closed(self); + /* gh-138720: Use IS_CLOSED to match flush CHECK_CLOSED. */ + r = IS_CLOSED(self); if (r < 0) goto end; if (r > 0) { @@ -1025,9 +1034,6 @@ static PyObject * _io__Buffered_read1_impl(buffered *self, Py_ssize_t n) /*[clinic end generated code: output=bcc4fb4e54d103a3 input=3d0ad241aa52b36c]*/ { - Py_ssize_t have, r; - PyObject *res = NULL; - CHECK_INITIALIZED(self) if (n < 0) { n = self->buffer_size; @@ -1035,48 +1041,54 @@ _io__Buffered_read1_impl(buffered *self, Py_ssize_t n) CHECK_CLOSED(self, "read of closed file") - if (n == 0) - return PyBytes_FromStringAndSize(NULL, 0); + if (n == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } /* Return up to n bytes. If at least one byte is buffered, we only return buffered bytes. Otherwise, we do one raw read. */ - have = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t); + Py_ssize_t have = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t); if (have > 0) { n = Py_MIN(have, n); - res = _bufferedreader_read_fast(self, n); + PyObject *res = _bufferedreader_read_fast(self, n); assert(res != Py_None); return res; } - res = PyBytes_FromStringAndSize(NULL, n); - if (res == NULL) - return NULL; + if (!ENTER_BUFFERED(self)) { - Py_DECREF(res); return NULL; } + /* Flush the write buffer if necessary */ if (self->writable) { - PyObject *r = buffered_flush_and_rewind_unlocked(self); - if (r == NULL) { + PyObject *res = buffered_flush_and_rewind_unlocked(self); + if (res == NULL) { LEAVE_BUFFERED(self) - Py_DECREF(res); return NULL; } - Py_DECREF(r); + Py_DECREF(res); } _bufferedreader_reset_buf(self); - r = _bufferedreader_raw_read(self, PyBytes_AS_STRING(res), n); + + PyBytesWriter *writer = PyBytesWriter_Create(n); + if (writer == NULL) { + LEAVE_BUFFERED(self) + return NULL; + } + + Py_ssize_t r = _bufferedreader_raw_read(self, + PyBytesWriter_GetData(writer), n); LEAVE_BUFFERED(self) if (r == -1) { - Py_DECREF(res); + PyBytesWriter_Discard(writer); return NULL; } - if (r == -2) + if (r == -2) { r = 0; - if (n > r) - _PyBytes_Resize(&res, r); - return res; + } + + return PyBytesWriter_FinishWithSize(writer, r); } static PyObject * @@ -1493,11 +1505,13 @@ buffered_iternext(PyObject *op) _PyIO_State *state = find_io_state_by_def(Py_TYPE(self)); tp = Py_TYPE(self); - if (Py_IS_TYPE(tp, state->PyBufferedReader_Type) || - Py_IS_TYPE(tp, state->PyBufferedRandom_Type)) + if (tp == state->PyBufferedReader_Type || + tp == state->PyBufferedRandom_Type) { /* Skip method call overhead for speed */ + Py_BEGIN_CRITICAL_SECTION(self); line = _buffered_readline(self, -1); + Py_END_CRITICAL_SECTION(); } else { line = PyObject_CallMethodNoArgs((PyObject *)self, @@ -1786,18 +1800,18 @@ _bufferedreader_read_fast(buffered *self, Py_ssize_t n) static PyObject * _bufferedreader_read_generic(buffered *self, Py_ssize_t n) { - PyObject *res = NULL; Py_ssize_t current_size, remaining, written; - char *out; current_size = Py_SAFE_DOWNCAST(READAHEAD(self), Py_off_t, Py_ssize_t); if (n <= current_size) return _bufferedreader_read_fast(self, n); - res = PyBytes_FromStringAndSize(NULL, n); - if (res == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(n); + if (writer == NULL) { goto error; - out = PyBytes_AS_STRING(res); + } + char *out = PyBytesWriter_GetData(writer); + remaining = n; written = 0; if (current_size > 0) { @@ -1826,11 +1840,9 @@ _bufferedreader_read_generic(buffered *self, Py_ssize_t n) if (r == 0 || r == -2) { /* EOF occurred or read() would block. */ if (r == 0 || written > 0) { - if (_PyBytes_Resize(&res, written)) - goto error; - return res; + return PyBytesWriter_FinishWithSize(writer, written); } - Py_DECREF(res); + PyBytesWriter_Discard(writer); Py_RETURN_NONE; } remaining -= r; @@ -1850,11 +1862,9 @@ _bufferedreader_read_generic(buffered *self, Py_ssize_t n) if (r == 0 || r == -2) { /* EOF occurred or read() would block. */ if (r == 0 || written > 0) { - if (_PyBytes_Resize(&res, written)) - goto error; - return res; + return PyBytesWriter_FinishWithSize(writer, written); } - Py_DECREF(res); + PyBytesWriter_Discard(writer); Py_RETURN_NONE; } if (remaining > r) { @@ -1873,10 +1883,10 @@ _bufferedreader_read_generic(buffered *self, Py_ssize_t n) break; } - return res; + return PyBytesWriter_Finish(writer); error: - Py_XDECREF(res); + PyBytesWriter_Discard(writer); return NULL; } @@ -2080,6 +2090,7 @@ _io_BufferedWriter_write_impl(buffered *self, Py_buffer *buffer) PyObject *res = NULL; Py_ssize_t written, avail, remaining; Py_off_t offset; + int r; CHECK_INITIALIZED(self) @@ -2088,7 +2099,11 @@ _io_BufferedWriter_write_impl(buffered *self, Py_buffer *buffer) /* Issue #31976: Check for closed file after acquiring the lock. Another thread could be holding the lock while closing the file. */ - if (IS_CLOSED(self)) { + r = IS_CLOSED(self); + if (r < 0) { + goto error; + } + if (r > 0) { PyErr_SetString(PyExc_ValueError, "write to closed file"); goto error; } @@ -2525,7 +2540,7 @@ static PyType_Slot bufferediobase_slots[] = { }; /* Do not set Py_TPFLAGS_HAVE_GC so that tp_traverse and tp_clear are inherited */ -PyType_Spec bufferediobase_spec = { +PyType_Spec _Py_bufferediobase_spec = { .name = "_io._BufferedIOBase", .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE), @@ -2588,7 +2603,7 @@ static PyType_Slot bufferedreader_slots[] = { {0, NULL}, }; -PyType_Spec bufferedreader_spec = { +PyType_Spec _Py_bufferedreader_spec = { .name = "_io.BufferedReader", .basicsize = sizeof(buffered), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | @@ -2646,7 +2661,7 @@ static PyType_Slot bufferedwriter_slots[] = { {0, NULL}, }; -PyType_Spec bufferedwriter_spec = { +PyType_Spec _Py_bufferedwriter_spec = { .name = "_io.BufferedWriter", .basicsize = sizeof(buffered), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | @@ -2696,7 +2711,7 @@ static PyType_Slot bufferedrwpair_slots[] = { {0, NULL}, }; -PyType_Spec bufferedrwpair_spec = { +PyType_Spec _Py_bufferedrwpair_spec = { .name = "_io.BufferedRWPair", .basicsize = sizeof(rwpair), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | @@ -2764,7 +2779,7 @@ static PyType_Slot bufferedrandom_slots[] = { {0, NULL}, }; -PyType_Spec bufferedrandom_spec = { +PyType_Spec _Py_bufferedrandom_spec = { .name = "_io.BufferedRandom", .basicsize = sizeof(buffered), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 30d61f9d68e610..8cdcbd0d89c718 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -194,18 +194,18 @@ write_bytes_lock_held(bytesio *self, PyObject *b) { _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); - if (check_closed(self)) { - return -1; - } - if (check_exports(self)) { - return -1; - } - Py_buffer buf; + Py_ssize_t len; if (PyObject_GetBuffer(b, &buf, PyBUF_CONTIG_RO) < 0) { return -1; } - Py_ssize_t len = buf.len; + + if (check_closed(self) || check_exports(self)) { + len = -1; + goto done; + } + + len = buf.len; if (len == 0) { goto done; } @@ -436,6 +436,13 @@ read_bytes_lock_held(bytesio *self, Py_ssize_t size) return Py_NewRef(self->buf); } + /* gh-141311: Avoid undefined behavior when self->pos (limit PY_SSIZE_T_MAX) + is beyond the size of self->buf. Assert above validates size is always in + bounds. When self->pos is out of bounds calling code sets size to 0. */ + if (size == 0) { + return PyBytes_FromStringAndSize(NULL, 0); + } + output = PyBytes_AS_STRING(self->buf) + self->pos; self->pos += size; return PyBytes_FromStringAndSize(output, size); @@ -481,13 +488,13 @@ _io.BytesIO.read1 Read at most size bytes, returned as a bytes object. -If the size argument is negative or omitted, read until EOF is reached. -Return an empty bytes object at EOF. +If the size argument is negative or omitted, read until EOF is +reached. Return an empty bytes object at EOF. [clinic start generated code]*/ static PyObject * _io_BytesIO_read1_impl(bytesio *self, Py_ssize_t size) -/*[clinic end generated code: output=d0f843285aa95f1c input=a08fc9e507ab380c]*/ +/*[clinic end generated code: output=d0f843285aa95f1c input=796ff4e0efccc4d9]*/ { return _io_BytesIO_read_impl(self, size); } @@ -609,11 +616,14 @@ _io_BytesIO_readinto_impl(bytesio *self, Py_buffer *buffer) n = self->string_size - self->pos; if (len > n) { len = n; - if (len < 0) - len = 0; + if (len < 0) { + /* gh-141311: Avoid undefined behavior when self->pos (limit + PY_SSIZE_T_MAX) points beyond the size of self->buf. */ + return PyLong_FromSsize_t(0); + } } - assert(self->pos + len < PY_SSIZE_T_MAX); + assert(self->pos + len <= PY_SSIZE_T_MAX); assert(len >= 0); memcpy(buffer->buf, PyBytes_AS_STRING(self->buf) + self->pos, len); self->pos += len; @@ -782,13 +792,13 @@ _io.BytesIO.writelines Write lines to the file. Note that newlines are not added. lines can be any iterable object -producing bytes-like objects. This is equivalent to calling write() for -each element. +producing bytes-like objects. This is equivalent to calling write() +for each element. [clinic start generated code]*/ static PyObject * _io_BytesIO_writelines_impl(bytesio *self, PyObject *lines) -/*[clinic end generated code: output=03a43a75773bc397 input=5d6a616ae39dc9ca]*/ +/*[clinic end generated code: output=03a43a75773bc397 input=d265f76533b058e7]*/ { PyObject *it, *item; @@ -1156,7 +1166,7 @@ static PyType_Slot bytesio_slots[] = { {0, NULL}, }; -PyType_Spec bytesio_spec = { +PyType_Spec _Py_bytesio_spec = { .name = "_io.BytesIO", .basicsize = sizeof(bytesio), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | @@ -1246,7 +1256,7 @@ static PyType_Slot bytesiobuf_slots[] = { {0, NULL}, }; -PyType_Spec bytesiobuf_spec = { +PyType_Spec _Py_bytesiobuf_spec = { .name = "_io._BytesIOBuffer", .basicsize = sizeof(bytesiobuf), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | diff --git a/Modules/_io/clinic/_iomodule.c.h b/Modules/_io/clinic/_iomodule.c.h index 90b80af3018fb0..f03638064385e2 100644 --- a/Modules/_io/clinic/_iomodule.c.h +++ b/Modules/_io/clinic/_iomodule.c.h @@ -18,112 +18,113 @@ PyDoc_STRVAR(_io_open__doc__, "file is either a text or byte string giving the name (and the path\n" "if the file isn\'t in the current working directory) of the file to\n" "be opened or an integer file descriptor of the file to be\n" -"wrapped. (If a file descriptor is given, it is closed when the\n" +"wrapped. (If a file descriptor is given, it is closed when the\n" "returned I/O object is closed, unless closefd is set to False.)\n" "\n" "mode is an optional string that specifies the mode in which the file\n" -"is opened. It defaults to \'r\' which means open for reading in text\n" +"is opened. It defaults to \'r\' which means open for reading in text\n" "mode. Other common values are \'w\' for writing (truncating the file if\n" "it already exists), \'x\' for creating and writing to a new file, and\n" "\'a\' for appending (which on some Unix systems, means that all writes\n" "append to the end of the file regardless of the current seek position).\n" "In text mode, if encoding is not specified the encoding used is platform\n" -"dependent: locale.getencoding() is called to get the current locale encoding.\n" -"(For reading and writing raw bytes use binary mode and leave encoding\n" -"unspecified.) The available modes are:\n" +"dependent: locale.getencoding() is called to get the current locale\n" +"encoding. (For reading and writing raw bytes use binary mode and leave\n" +"encoding unspecified.) The available modes are:\n" "\n" -"========= ===============================================================\n" +"========= ==========================================================\n" "Character Meaning\n" -"--------- ---------------------------------------------------------------\n" +"--------- ----------------------------------------------------------\n" "\'r\' open for reading (default)\n" "\'w\' open for writing, truncating the file first\n" "\'x\' create a new file and open it for writing\n" -"\'a\' open for writing, appending to the end of the file if it exists\n" +"\'a\' open for writing, appending to the end of the file if it\n" +" exists\n" "\'b\' binary mode\n" "\'t\' text mode (default)\n" "\'+\' open a disk file for updating (reading and writing)\n" -"========= ===============================================================\n" +"========= ==========================================================\n" "\n" -"The default mode is \'rt\' (open for reading text). For binary random\n" +"The default mode is \'rt\' (open for reading text). For binary random\n" "access, the mode \'w+b\' opens and truncates the file to 0 bytes, while\n" -"\'r+b\' opens the file without truncation. The \'x\' mode implies \'w\' and\n" +"\'r+b\' opens the file without truncation. The \'x\' mode implies \'w\' and\n" "raises an `FileExistsError` if the file already exists.\n" "\n" "Python distinguishes between files opened in binary and text modes,\n" -"even when the underlying operating system doesn\'t. Files opened in\n" +"even when the underlying operating system doesn\'t. Files opened in\n" "binary mode (appending \'b\' to the mode argument) return contents as\n" -"bytes objects without any decoding. In text mode (the default, or when\n" +"bytes objects without any decoding. In text mode (the default, or when\n" "\'t\' is appended to the mode argument), the contents of the file are\n" "returned as strings, the bytes having been first decoded using a\n" "platform-dependent encoding or using the specified encoding if given.\n" "\n" "buffering is an optional integer used to set the buffering policy.\n" -"Pass 0 to switch buffering off (only allowed in binary mode), 1 to select\n" -"line buffering (only usable in text mode), and an integer > 1 to indicate\n" -"the size of a fixed-size chunk buffer. When no buffering argument is\n" -"given, the default buffering policy works as follows:\n" +"Pass 0 to switch buffering off (only allowed in binary mode), 1 to\n" +"select line buffering (only usable in text mode), and an integer > 1 to\n" +"indicate the size of a fixed-size chunk buffer. When no buffering\n" +"argument is given, the default buffering policy works as follows:\n" "\n" "* Binary files are buffered in fixed-size chunks; the size of the buffer\n" -" is max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE)\n" -" when the device block size is available.\n" -" On most systems, the buffer will typically be 128 kilobytes long.\n" +" is max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE) when the device\n" +" block size is available.\n" +" On most systems, the buffer will typically be 128 kilobytes long.\n" "\n" "* \"Interactive\" text files (files for which isatty() returns True)\n" " use line buffering. Other text files use the policy described above\n" " for binary files.\n" "\n" "encoding is the name of the encoding used to decode or encode the\n" -"file. This should only be used in text mode. The default encoding is\n" +"file. This should only be used in text mode. The default encoding is\n" "platform dependent, but any encoding supported by Python can be\n" "passed. See the codecs module for the list of supported encodings.\n" "\n" "errors is an optional string that specifies how encoding errors are to\n" -"be handled---this argument should not be used in binary mode. Pass\n" +"be handled---this argument should not be used in binary mode. Pass\n" "\'strict\' to raise a ValueError exception if there is an encoding error\n" "(the default of None has the same effect), or pass \'ignore\' to ignore\n" -"errors. (Note that ignoring encoding errors can lead to data loss.)\n" +"errors. (Note that ignoring encoding errors can lead to data loss.)\n" "See the documentation for codecs.register or run \'help(codecs.Codec)\'\n" "for a list of the permitted encoding error strings.\n" "\n" "newline controls how universal newlines works (it only applies to text\n" -"mode). It can be None, \'\', \'\\n\', \'\\r\', and \'\\r\\n\'. It works as\n" +"mode). It can be None, \'\', \'\\n\', \'\\r\', and \'\\r\\n\'. It works as\n" "follows:\n" "\n" -"* On input, if newline is None, universal newlines mode is\n" -" enabled. Lines in the input can end in \'\\n\', \'\\r\', or \'\\r\\n\', and\n" -" these are translated into \'\\n\' before being returned to the\n" -" caller. If it is \'\', universal newline mode is enabled, but line\n" -" endings are returned to the caller untranslated. If it has any of\n" -" the other legal values, input lines are only terminated by the given\n" -" string, and the line ending is returned to the caller untranslated.\n" +"* On input, if newline is None, universal newlines mode is enabled.\n" +" Lines in the input can end in \'\\n\', \'\\r\', or \'\\r\\n\', and these are\n" +" translated into \'\\n\' before being returned to the caller. If it is\n" +" \'\', universal newline mode is enabled, but line endings are returned\n" +" to the caller untranslated. If it has any of the other legal values,\n" +" input lines are only terminated by the given string, and the line\n" +" ending is returned to the caller untranslated.\n" "\n" "* On output, if newline is None, any \'\\n\' characters written are\n" -" translated to the system default line separator, os.linesep. If\n" -" newline is \'\' or \'\\n\', no translation takes place. If newline is any\n" +" translated to the system default line separator, os.linesep. If\n" +" newline is \'\' or \'\\n\', no translation takes place. If newline is any\n" " of the other legal values, any \'\\n\' characters written are translated\n" " to the given string.\n" "\n" "If closefd is False, the underlying file descriptor will be kept open\n" -"when the file is closed. This does not work when a file name is given\n" +"when the file is closed. This does not work when a file name is given\n" "and must be True in that case.\n" "\n" -"A custom opener can be used by passing a callable as *opener*. The\n" +"A custom opener can be used by passing a callable as *opener*. The\n" "underlying file descriptor for the file object is then obtained by\n" -"calling *opener* with (*file*, *flags*). *opener* must return an open\n" +"calling *opener* with (*file*, *flags*). *opener* must return an open\n" "file descriptor (passing os.open as *opener* results in functionality\n" "similar to passing None).\n" "\n" "open() returns a file object whose type depends on the mode, and\n" "through which the standard file operations such as reading and writing\n" -"are performed. When open() is used to open a file in a text mode (\'w\',\n" -"\'r\', \'wt\', \'rt\', etc.), it returns a TextIOWrapper. When used to open\n" +"are performed. When open() is used to open a file in a text mode (\'w\',\n" +"\'r\', \'wt\', \'rt\', etc.), it returns a TextIOWrapper. When used to open\n" "a file in a binary mode, the returned class varies: in read binary\n" "mode, it returns a BufferedReader; in write binary and append binary\n" "modes, it returns a BufferedWriter, and in read/write mode, it returns\n" "a BufferedRandom.\n" "\n" "It is also possible to use a string or bytearray as a file for both\n" -"reading and writing. For strings StringIO can be used like a file\n" +"reading and writing. For strings StringIO can be used like a file\n" "opened in a text mode, and for bytes a BytesIO can be used like a file\n" "opened in a binary mode."); @@ -352,8 +353,8 @@ PyDoc_STRVAR(_io_open_code__doc__, "\n" "Opens the provided file with the intent to import the contents.\n" "\n" -"This may perform extra validation beyond open(), but is otherwise interchangeable\n" -"with calling open(path, \'rb\')."); +"This may perform extra validation beyond open(), but is otherwise\n" +"interchangeable with calling open(path, \'rb\')."); #define _IO_OPEN_CODE_METHODDEF \ {"open_code", _PyCFunction_CAST(_io_open_code), METH_FASTCALL|METH_KEYWORDS, _io_open_code__doc__}, @@ -410,4 +411,4 @@ _io_open_code(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=7a8e032c0424bce2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5190d11f0803bfe8 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/bytesio.c.h b/Modules/_io/clinic/bytesio.c.h index 6595dc937bbcf0..fad11ea6c9f6cf 100644 --- a/Modules/_io/clinic/bytesio.c.h +++ b/Modules/_io/clinic/bytesio.c.h @@ -254,8 +254,8 @@ PyDoc_STRVAR(_io_BytesIO_read1__doc__, "\n" "Read at most size bytes, returned as a bytes object.\n" "\n" -"If the size argument is negative or omitted, read until EOF is reached.\n" -"Return an empty bytes object at EOF."); +"If the size argument is negative or omitted, read until EOF is\n" +"reached. Return an empty bytes object at EOF."); #define _IO_BYTESIO_READ1_METHODDEF \ {"read1", _PyCFunction_CAST(_io_BytesIO_read1), METH_FASTCALL, _io_BytesIO_read1__doc__}, @@ -529,8 +529,8 @@ PyDoc_STRVAR(_io_BytesIO_writelines__doc__, "Write lines to the file.\n" "\n" "Note that newlines are not added. lines can be any iterable object\n" -"producing bytes-like objects. This is equivalent to calling write() for\n" -"each element."); +"producing bytes-like objects. This is equivalent to calling write()\n" +"for each element."); #define _IO_BYTESIO_WRITELINES_METHODDEF \ {"writelines", (PyCFunction)_io_BytesIO_writelines, METH_O, _io_BytesIO_writelines__doc__}, @@ -637,4 +637,4 @@ _io_BytesIO___init__(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=daa81dfdae5ccc57 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=eac3911e207aaf45 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/fileio.c.h b/Modules/_io/clinic/fileio.c.h index 04870b1c890361..890b6bc3fac9d5 100644 --- a/Modules/_io/clinic/fileio.c.h +++ b/Modules/_io/clinic/fileio.c.h @@ -15,8 +15,8 @@ PyDoc_STRVAR(_io_FileIO_close__doc__, "\n" "Close the file.\n" "\n" -"A closed file cannot be used for further I/O operations. close() may be\n" -"called more than once without error."); +"A closed file cannot be used for further I/O operations. close()\n" +"may be called more than once without error."); #define _IO_FILEIO_CLOSE_METHODDEF \ {"close", _PyCFunction_CAST(_io_FileIO_close), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io_FileIO_close__doc__}, @@ -41,16 +41,19 @@ PyDoc_STRVAR(_io_FileIO___init____doc__, "Open a file.\n" "\n" "The mode can be \'r\' (default), \'w\', \'x\' or \'a\' for reading,\n" -"writing, exclusive creation or appending. The file will be created if it\n" -"doesn\'t exist when opened for writing or appending; it will be truncated\n" -"when opened for writing. A FileExistsError will be raised if it already\n" -"exists when opened for creating. Opening a file for creating implies\n" -"writing so this mode behaves in a similar way to \'w\'.Add a \'+\' to the mode\n" -"to allow simultaneous reading and writing. A custom opener can be used by\n" -"passing a callable as *opener*. The underlying file descriptor for the file\n" -"object is then obtained by calling opener with (*name*, *flags*).\n" -"*opener* must return an open file descriptor (passing os.open as *opener*\n" -"results in functionality similar to passing None)."); +"writing, exclusive creation or appending. The file will be created\n" +"if it doesn\'t exist when opened for writing or appending; it will be\n" +"truncated when opened for writing. A FileExistsError will be raised\n" +"if it already exists when opened for creating. Opening a file for\n" +"creating implies writing so this mode behaves in a similar way to\n" +"\'w\'. Add a \'+\' to the mode to allow simultaneous reading and\n" +"writing.\n" +"\n" +"A custom opener can be used by passing a callable as *opener*.\n" +"The underlying file descriptor for the file object is then obtained\n" +"by calling opener with (*name*, *flags*). *opener* must return\n" +"an open file descriptor (passing os.open as *opener* results in\n" +"functionality similar to passing None)."); static int _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, @@ -270,22 +273,28 @@ PyDoc_STRVAR(_io_FileIO_readall__doc__, "\n" "Read all data from the file, returned as bytes.\n" "\n" -"Reads until either there is an error or read() returns size 0 (indicates EOF).\n" -"If the file is already at EOF, returns an empty bytes object.\n" +"Reads until either there is an error or read() returns size 0\n" +"(indicates EOF). If the file is already at EOF, returns an empty\n" +"bytes object.\n" "\n" -"In non-blocking mode, returns as much data as could be read before EAGAIN. If no\n" -"data is available (EAGAIN is returned before bytes are read) returns None."); +"In non-blocking mode, returns as much data as could be read before\n" +"EAGAIN. If no data is available (EAGAIN is returned before bytes\n" +"are read) returns None."); #define _IO_FILEIO_READALL_METHODDEF \ - {"readall", (PyCFunction)_io_FileIO_readall, METH_NOARGS, _io_FileIO_readall__doc__}, + {"readall", _PyCFunction_CAST(_io_FileIO_readall), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io_FileIO_readall__doc__}, static PyObject * -_io_FileIO_readall_impl(fileio *self); +_io_FileIO_readall_impl(fileio *self, PyTypeObject *cls); static PyObject * -_io_FileIO_readall(PyObject *self, PyObject *Py_UNUSED(ignored)) +_io_FileIO_readall(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - return _io_FileIO_readall_impl((fileio *)self); + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { + PyErr_SetString(PyExc_TypeError, "readall() takes no arguments"); + return NULL; + } + return _io_FileIO_readall_impl((fileio *)self, cls); } PyDoc_STRVAR(_io_FileIO_read__doc__, @@ -294,14 +303,14 @@ PyDoc_STRVAR(_io_FileIO_read__doc__, "\n" "Read at most size bytes, returned as bytes.\n" "\n" -"If size is less than 0, read all bytes in the file making multiple read calls.\n" -"See ``FileIO.readall``.\n" +"If size is less than 0, read all bytes in the file making multiple\n" +"read calls. See ``FileIO.readall``.\n" "\n" -"Attempts to make only one system call, retrying only per PEP 475 (EINTR). This\n" -"means less data may be returned than requested.\n" +"Attempts to make only one system call, retrying only per PEP 475\n" +"(EINTR). This means less data may be returned than requested.\n" "\n" -"In non-blocking mode, returns None if no data is available. Return an empty\n" -"bytes object at EOF."); +"In non-blocking mode, returns None if no data is available. Return\n" +"an empty bytes object at EOF."); #define _IO_FILEIO_READ_METHODDEF \ {"read", _PyCFunction_CAST(_io_FileIO_read), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io_FileIO_read__doc__}, @@ -354,8 +363,8 @@ PyDoc_STRVAR(_io_FileIO_write__doc__, "Write buffer b to file, return number of bytes written.\n" "\n" "Only makes one system call, so not all of the data may be written.\n" -"The number of bytes actually written is returned. In non-blocking mode,\n" -"returns None if the write would block."); +"The number of bytes actually written is returned. In non-blocking\n" +"mode, returns None if the write would block."); #define _IO_FILEIO_WRITE_METHODDEF \ {"write", _PyCFunction_CAST(_io_FileIO_write), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io_FileIO_write__doc__}, @@ -408,11 +417,12 @@ PyDoc_STRVAR(_io_FileIO_seek__doc__, "\n" "Move to new file position and return the file position.\n" "\n" -"Argument offset is a byte count. Optional argument whence defaults to\n" -"SEEK_SET or 0 (offset from start of file, offset should be >= 0); other values\n" -"are SEEK_CUR or 1 (move relative to current position, positive or negative),\n" -"and SEEK_END or 2 (move relative to end of file, usually negative, although\n" -"many platforms allow seeking beyond the end of a file).\n" +"Argument offset is a byte count. Optional argument whence defaults\n" +"to SEEK_SET or 0 (offset from start of file, offset should be >= 0);\n" +"other values are SEEK_CUR or 1 (move relative to current position,\n" +"positive or negative), and SEEK_END or 2 (move relative to end of\n" +"file, usually negative, although many platforms allow seeking beyond\n" +"the end of a file).\n" "\n" "Note that not all file objects are seekable."); @@ -543,4 +553,4 @@ _io_FileIO_isatty(PyObject *self, PyObject *Py_UNUSED(ignored)) #ifndef _IO_FILEIO_TRUNCATE_METHODDEF #define _IO_FILEIO_TRUNCATE_METHODDEF #endif /* !defined(_IO_FILEIO_TRUNCATE_METHODDEF) */ -/*[clinic end generated code: output=1902fac9e39358aa input=a9049054013a1b77]*/ +/*[clinic end generated code: output=453d584e2e72f986 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/iobase.c.h b/Modules/_io/clinic/iobase.c.h index 402448545dfc51..e4438c26431aa8 100644 --- a/Modules/_io/clinic/iobase.c.h +++ b/Modules/_io/clinic/iobase.c.h @@ -19,11 +19,13 @@ PyDoc_STRVAR(_io__IOBase_seek__doc__, " whence\n" " The relative position to seek from.\n" "\n" -"The offset is interpreted relative to the position indicated by whence.\n" -"Values for whence are:\n" +"The offset is interpreted relative to the position indicated by\n" +"whence. Values for whence are:\n" "\n" -"* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive\n" -"* os.SEEK_CUR or 1 -- current stream position; offset may be negative\n" +"* os.SEEK_SET or 0 -- start of stream (the default); offset should\n" +" be zero or positive\n" +"* os.SEEK_CUR or 1 -- current stream position; offset may be\n" +" negative\n" "* os.SEEK_END or 2 -- end of stream; offset is usually negative\n" "\n" "Return the new absolute position."); @@ -103,8 +105,8 @@ PyDoc_STRVAR(_io__IOBase_truncate__doc__, "\n" "Truncate file to size bytes.\n" "\n" -"File pointer is left unchanged. Size defaults to the current IO position\n" -"as reported by tell(). Return the new size."); +"File pointer is left unchanged. Size defaults to the current IO\n" +"position as reported by tell(). Return the new size."); #define _IO__IOBASE_TRUNCATE_METHODDEF \ {"truncate", _PyCFunction_CAST(_io__IOBase_truncate), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io__IOBase_truncate__doc__}, @@ -443,4 +445,4 @@ _io__RawIOBase_readall(PyObject *self, PyObject *Py_UNUSED(ignored)) { return _io__RawIOBase_readall_impl(self); } -/*[clinic end generated code: output=9359e74d95534bef input=a9049054013a1b77]*/ +/*[clinic end generated code: output=28c06bb6db32c096 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/stringio.c.h b/Modules/_io/clinic/stringio.c.h index 83165e5f7ad08b..d6d4afb9b63c62 100644 --- a/Modules/_io/clinic/stringio.c.h +++ b/Modules/_io/clinic/stringio.c.h @@ -179,7 +179,8 @@ PyDoc_STRVAR(_io_StringIO_seek__doc__, "\n" "Change stream position.\n" "\n" -"Seek to character offset pos relative to position indicated by whence:\n" +"Seek to character offset pos relative to position indicated by\n" +"whence:\n" " 0 Start of stream (the default). pos should be >= 0;\n" " 1 Current position - pos must be 0;\n" " 2 End of stream - pos must be 0.\n" @@ -550,4 +551,4 @@ _io_StringIO_newlines_get(PyObject *self, void *Py_UNUSED(context)) return return_value; } -/*[clinic end generated code: output=bccc25ef8e6ce9ef input=a9049054013a1b77]*/ +/*[clinic end generated code: output=730c34b2a6c0500b input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h index 128a5ad1678f26..8d59bda5f74b38 100644 --- a/Modules/_io/clinic/textio.c.h +++ b/Modules/_io/clinic/textio.c.h @@ -16,7 +16,8 @@ PyDoc_STRVAR(_io__TextIOBase_detach__doc__, "\n" "Separate the underlying buffer from the TextIOBase and return it.\n" "\n" -"After the underlying buffer has been detached, the TextIO is in an unusable state."); +"After the underlying buffer has been detached, the TextIO is in\n" +"an unusable state."); #define _IO__TEXTIOBASE_DETACH_METHODDEF \ {"detach", _PyCFunction_CAST(_io__TextIOBase_detach), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io__TextIOBase_detach__doc__}, @@ -40,8 +41,8 @@ PyDoc_STRVAR(_io__TextIOBase_read__doc__, "\n" "Read at most size characters from stream.\n" "\n" -"Read from underlying buffer until we have size characters or we hit EOF.\n" -"If size is negative or omitted, read until EOF."); +"Read from underlying buffer until we have size characters or we hit\n" +"EOF. If size is negative or omitted, read until EOF."); #define _IO__TEXTIOBASE_READ_METHODDEF \ {"read", _PyCFunction_CAST(_io__TextIOBase_read), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _io__TextIOBase_read__doc__}, @@ -430,7 +431,9 @@ _io_IncrementalNewlineDecoder_decode(PyObject *self, PyObject *const *args, Py_s goto exit; } skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_IncrementalNewlineDecoder_decode_impl((nldecoder_object *)self, input, final); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -450,7 +453,13 @@ _io_IncrementalNewlineDecoder_getstate_impl(nldecoder_object *self); static PyObject * _io_IncrementalNewlineDecoder_getstate(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return _io_IncrementalNewlineDecoder_getstate_impl((nldecoder_object *)self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_IncrementalNewlineDecoder_getstate_impl((nldecoder_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_IncrementalNewlineDecoder_setstate__doc__, @@ -470,7 +479,9 @@ _io_IncrementalNewlineDecoder_setstate(PyObject *self, PyObject *state) { PyObject *return_value = NULL; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_IncrementalNewlineDecoder_setstate_impl((nldecoder_object *)self, state); + Py_END_CRITICAL_SECTION(); return return_value; } @@ -489,7 +500,13 @@ _io_IncrementalNewlineDecoder_reset_impl(nldecoder_object *self); static PyObject * _io_IncrementalNewlineDecoder_reset(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return _io_IncrementalNewlineDecoder_reset_impl((nldecoder_object *)self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _io_IncrementalNewlineDecoder_reset_impl((nldecoder_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_io_TextIOWrapper___init____doc__, @@ -649,7 +666,9 @@ _io_TextIOWrapper___init__(PyObject *self, PyObject *args, PyObject *kwargs) goto exit; } skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _io_TextIOWrapper___init___impl((textio *)self, buffer, encoding, errors, newline, line_buffering, write_through); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -948,8 +967,8 @@ PyDoc_STRVAR(_io_TextIOWrapper_tell__doc__, "\n" "Return the stream position as an opaque number.\n" "\n" -"The return value of tell() can be given as input to seek(), to restore a\n" -"previous stream position."); +"The return value of tell() can be given as input to seek(), to\n" +"restore a previous stream position."); #define _IO_TEXTIOWRAPPER_TELL_METHODDEF \ {"tell", (PyCFunction)_io_TextIOWrapper_tell, METH_NOARGS, _io_TextIOWrapper_tell__doc__}, @@ -1312,4 +1331,4 @@ _io_TextIOWrapper__CHUNK_SIZE_set(PyObject *self, PyObject *value, void *Py_UNUS return return_value; } -/*[clinic end generated code: output=30404271a1151056 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8c571c9dba87d2b1 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/winconsoleio.c.h b/Modules/_io/clinic/winconsoleio.c.h index 7af5923b6c1747..bd8073cd0af3f6 100644 --- a/Modules/_io/clinic/winconsoleio.c.h +++ b/Modules/_io/clinic/winconsoleio.c.h @@ -46,9 +46,9 @@ PyDoc_STRVAR(_io__WindowsConsoleIO___init____doc__, "\n" "Open a console buffer by file descriptor.\n" "\n" -"The mode can be \'rb\' (default), or \'wb\' for reading or writing bytes. All\n" -"other mode characters will be ignored. Mode \'b\' will be assumed if it is\n" -"omitted. The *opener* parameter is always ignored."); +"The mode can be \'rb\' (default), or \'wb\' for reading or writing\n" +"bytes. All other mode characters will be ignored. Mode \'b\' will be\n" +"assumed if it is omitted. The *opener* parameter is always ignored."); static int _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj, @@ -463,4 +463,4 @@ _io__WindowsConsoleIO_isatty(PyObject *self, PyObject *Py_UNUSED(ignored)) #ifndef _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF #define _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF #endif /* !defined(_IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF) */ -/*[clinic end generated code: output=ce50bcd905f1f213 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=dfe49dd71f4f4b1d input=a9049054013a1b77]*/ diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 26537fc6395e9f..3aeb30dfe24a35 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -70,6 +70,7 @@ typedef struct { unsigned int writable : 1; unsigned int appending : 1; signed int seekable : 2; /* -1 means unknown */ + unsigned int truncate : 1; unsigned int closefd : 1; char finalizing; /* Stat result which was grabbed at file open, useful for optimizing common @@ -152,13 +153,13 @@ _io.FileIO.close Close the file. -A closed file cannot be used for further I/O operations. close() may be -called more than once without error. +A closed file cannot be used for further I/O operations. close() +may be called more than once without error. [clinic start generated code]*/ static PyObject * _io_FileIO_close_impl(fileio *self, PyTypeObject *cls) -/*[clinic end generated code: output=c30cbe9d1f23ca58 input=70da49e63db7c64d]*/ +/*[clinic end generated code: output=c30cbe9d1f23ca58 input=b405751dc4163da3]*/ { PyObject *res; int rc; @@ -209,6 +210,7 @@ fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self->writable = 0; self->appending = 0; self->seekable = -1; + self->truncate = 0; self->stat_atopen = NULL; self->closefd = 1; self->weakreflist = NULL; @@ -229,22 +231,25 @@ _io.FileIO.__init__ Open a file. The mode can be 'r' (default), 'w', 'x' or 'a' for reading, -writing, exclusive creation or appending. The file will be created if it -doesn't exist when opened for writing or appending; it will be truncated -when opened for writing. A FileExistsError will be raised if it already -exists when opened for creating. Opening a file for creating implies -writing so this mode behaves in a similar way to 'w'.Add a '+' to the mode -to allow simultaneous reading and writing. A custom opener can be used by -passing a callable as *opener*. The underlying file descriptor for the file -object is then obtained by calling opener with (*name*, *flags*). -*opener* must return an open file descriptor (passing os.open as *opener* -results in functionality similar to passing None). +writing, exclusive creation or appending. The file will be created +if it doesn't exist when opened for writing or appending; it will be +truncated when opened for writing. A FileExistsError will be raised +if it already exists when opened for creating. Opening a file for +creating implies writing so this mode behaves in a similar way to +'w'. Add a '+' to the mode to allow simultaneous reading and +writing. + +A custom opener can be used by passing a callable as *opener*. +The underlying file descriptor for the file object is then obtained +by calling opener with (*name*, *flags*). *opener* must return +an open file descriptor (passing os.open as *opener* results in +functionality similar to passing None). [clinic start generated code]*/ static int _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, int closefd, PyObject *opener) -/*[clinic end generated code: output=23413f68e6484bbd input=588aac967e0ba74b]*/ +/*[clinic end generated code: output=23413f68e6484bbd input=bac4efcd8f930bf3]*/ { #ifdef MS_WINDOWS wchar_t *widename = NULL; @@ -341,6 +346,7 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, goto bad_mode; rwa = 1; self->writable = 1; + self->truncate = 1; flags |= O_CREAT | O_TRUNC; break; case 'a': @@ -724,21 +730,26 @@ new_buffersize(fileio *self, size_t currentsize) /*[clinic input] _io.FileIO.readall + cls: defining_class + / + Read all data from the file, returned as bytes. -Reads until either there is an error or read() returns size 0 (indicates EOF). -If the file is already at EOF, returns an empty bytes object. +Reads until either there is an error or read() returns size 0 +(indicates EOF). If the file is already at EOF, returns an empty +bytes object. -In non-blocking mode, returns as much data as could be read before EAGAIN. If no -data is available (EAGAIN is returned before bytes are read) returns None. +In non-blocking mode, returns as much data as could be read before +EAGAIN. If no data is available (EAGAIN is returned before bytes +are read) returns None. [clinic start generated code]*/ static PyObject * -_io_FileIO_readall_impl(fileio *self) -/*[clinic end generated code: output=faa0292b213b4022 input=1e19849857f5d0a1]*/ +_io_FileIO_readall_impl(fileio *self, PyTypeObject *cls) +/*[clinic end generated code: output=d546737ec895c462 input=65d05bd0169f2df5]*/ { Py_off_t pos, end; - PyObject *result; + PyBytesWriter *writer; Py_ssize_t bytes_read = 0; Py_ssize_t n; size_t bufsize; @@ -746,6 +757,10 @@ _io_FileIO_readall_impl(fileio *self) if (self->fd < 0) { return err_closed(); } + if (!self->readable) { + _PyIO_State *state = get_io_state_by_cls(cls); + return err_mode(state, "reading"); + } if (self->stat_atopen != NULL && self->stat_atopen->st_size < _PY_READ_MAX) { end = (Py_off_t)self->stat_atopen->st_size; @@ -793,10 +808,10 @@ _io_FileIO_readall_impl(fileio *self) } } - - result = PyBytes_FromStringAndSize(NULL, bufsize); - if (result == NULL) + writer = PyBytesWriter_Create(bufsize); + if (writer == NULL) { return NULL; + } while (1) { if (bytes_read >= (Py_ssize_t)bufsize) { @@ -805,18 +820,18 @@ _io_FileIO_readall_impl(fileio *self) PyErr_SetString(PyExc_OverflowError, "unbounded read returned more bytes " "than a Python bytes object can hold"); - Py_DECREF(result); + PyBytesWriter_Discard(writer); return NULL; } - if (PyBytes_GET_SIZE(result) < (Py_ssize_t)bufsize) { - if (_PyBytes_Resize(&result, bufsize) < 0) + if (PyBytesWriter_GetSize(writer) < (Py_ssize_t)bufsize) { + if (PyBytesWriter_Resize(writer, bufsize) < 0) return NULL; } } n = _Py_read(self->fd, - PyBytes_AS_STRING(result) + bytes_read, + (char*)PyBytesWriter_GetData(writer) + bytes_read, bufsize - bytes_read); if (n == 0) @@ -826,20 +841,16 @@ _io_FileIO_readall_impl(fileio *self) PyErr_Clear(); if (bytes_read > 0) break; - Py_DECREF(result); + PyBytesWriter_Discard(writer); Py_RETURN_NONE; } - Py_DECREF(result); + PyBytesWriter_Discard(writer); return NULL; } bytes_read += n; } - if (PyBytes_GET_SIZE(result) > bytes_read) { - if (_PyBytes_Resize(&result, bytes_read) < 0) - return NULL; - } - return result; + return PyBytesWriter_FinishWithSize(writer, bytes_read); } /*[clinic input] @@ -850,24 +861,20 @@ _io.FileIO.read Read at most size bytes, returned as bytes. -If size is less than 0, read all bytes in the file making multiple read calls. -See ``FileIO.readall``. +If size is less than 0, read all bytes in the file making multiple +read calls. See ``FileIO.readall``. -Attempts to make only one system call, retrying only per PEP 475 (EINTR). This -means less data may be returned than requested. +Attempts to make only one system call, retrying only per PEP 475 +(EINTR). This means less data may be returned than requested. -In non-blocking mode, returns None if no data is available. Return an empty -bytes object at EOF. +In non-blocking mode, returns None if no data is available. Return +an empty bytes object at EOF. [clinic start generated code]*/ static PyObject * _io_FileIO_read_impl(fileio *self, PyTypeObject *cls, Py_ssize_t size) -/*[clinic end generated code: output=bbd749c7c224143e input=cf21fddef7d38ab6]*/ +/*[clinic end generated code: output=bbd749c7c224143e input=c7baa3b440af9337]*/ { - char *ptr; - Py_ssize_t n; - PyObject *bytes; - if (self->fd < 0) return err_closed(); if (!self->readable) { @@ -876,22 +883,23 @@ _io_FileIO_read_impl(fileio *self, PyTypeObject *cls, Py_ssize_t size) } if (size < 0) - return _io_FileIO_readall_impl(self); + return _io_FileIO_readall_impl(self, cls); if (size > _PY_READ_MAX) { size = _PY_READ_MAX; } - bytes = PyBytes_FromStringAndSize(NULL, size); - if (bytes == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; - ptr = PyBytes_AS_STRING(bytes); + } + char *ptr = PyBytesWriter_GetData(writer); - n = _Py_read(self->fd, ptr, size); + Py_ssize_t n = _Py_read(self->fd, ptr, size); if (n == -1) { - /* copy errno because Py_DECREF() can indirectly modify it */ + // copy errno because PyBytesWriter_Discard() can indirectly modify it int err = errno; - Py_DECREF(bytes); + PyBytesWriter_Discard(writer); if (err == EAGAIN) { PyErr_Clear(); Py_RETURN_NONE; @@ -899,14 +907,7 @@ _io_FileIO_read_impl(fileio *self, PyTypeObject *cls, Py_ssize_t size) return NULL; } - if (n != size) { - if (_PyBytes_Resize(&bytes, n) < 0) { - Py_CLEAR(bytes); - return NULL; - } - } - - return (PyObject *) bytes; + return PyBytesWriter_FinishWithSize(writer, n); } /*[clinic input] @@ -918,13 +919,13 @@ _io.FileIO.write Write buffer b to file, return number of bytes written. Only makes one system call, so not all of the data may be written. -The number of bytes actually written is returned. In non-blocking mode, -returns None if the write would block. +The number of bytes actually written is returned. In non-blocking +mode, returns None if the write would block. [clinic start generated code]*/ static PyObject * _io_FileIO_write_impl(fileio *self, PyTypeObject *cls, Py_buffer *b) -/*[clinic end generated code: output=927e25be80f3b77b input=2776314f043088f5]*/ +/*[clinic end generated code: output=927e25be80f3b77b input=233f1f70f9e8b09e]*/ { Py_ssize_t n; int err; @@ -1025,18 +1026,19 @@ _io.FileIO.seek Move to new file position and return the file position. -Argument offset is a byte count. Optional argument whence defaults to -SEEK_SET or 0 (offset from start of file, offset should be >= 0); other values -are SEEK_CUR or 1 (move relative to current position, positive or negative), -and SEEK_END or 2 (move relative to end of file, usually negative, although -many platforms allow seeking beyond the end of a file). +Argument offset is a byte count. Optional argument whence defaults +to SEEK_SET or 0 (offset from start of file, offset should be >= 0); +other values are SEEK_CUR or 1 (move relative to current position, +positive or negative), and SEEK_END or 2 (move relative to end of +file, usually negative, although many platforms allow seeking beyond +the end of a file). Note that not all file objects are seekable. [clinic start generated code]*/ static PyObject * _io_FileIO_seek_impl(fileio *self, PyObject *pos, int whence) -/*[clinic end generated code: output=c976acdf054e6655 input=0439194b0774d454]*/ +/*[clinic end generated code: output=c976acdf054e6655 input=f165a1b4f5d494ad]*/ { if (self->fd < 0) return err_closed(); @@ -1064,6 +1066,7 @@ _io_FileIO_tell_impl(fileio *self) #ifdef HAVE_FTRUNCATE /*[clinic input] +@permit_long_summary _io.FileIO.truncate cls: defining_class size as posobj: object = None @@ -1077,7 +1080,7 @@ The current file position is changed to the value of size. static PyObject * _io_FileIO_truncate_impl(fileio *self, PyTypeObject *cls, PyObject *posobj) -/*[clinic end generated code: output=d936732a49e8d5a2 input=c367fb45d6bb2c18]*/ +/*[clinic end generated code: output=d936732a49e8d5a2 input=8f22152bcf900ed2]*/ { Py_off_t pos; int ret; @@ -1156,10 +1159,17 @@ mode_string(fileio *self) return "ab"; } else if (self->readable) { - if (self->writable) - return "rb+"; - else + if (self->writable) { + if (self->truncate) { + return "wb+"; + } + else { + return "rb+"; + } + } + else { return "rb"; + } } else return "wb"; @@ -1330,7 +1340,7 @@ static PyType_Slot fileio_slots[] = { {0, NULL}, }; -PyType_Spec fileio_spec = { +PyType_Spec _Py_fileio_spec = { .name = "_io.FileIO", .basicsize = sizeof(fileio), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 044f6b7803c571..1253f124108bdb 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -103,11 +103,13 @@ _io._IOBase.seek Change the stream position to the given byte offset. -The offset is interpreted relative to the position indicated by whence. -Values for whence are: +The offset is interpreted relative to the position indicated by +whence. Values for whence are: -* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive -* os.SEEK_CUR or 1 -- current stream position; offset may be negative +* os.SEEK_SET or 0 -- start of stream (the default); offset should + be zero or positive +* os.SEEK_CUR or 1 -- current stream position; offset may be + negative * os.SEEK_END or 2 -- end of stream; offset is usually negative Return the new absolute position. @@ -116,7 +118,7 @@ Return the new absolute position. static PyObject * _io__IOBase_seek_impl(PyObject *self, PyTypeObject *cls, int Py_UNUSED(offset), int Py_UNUSED(whence)) -/*[clinic end generated code: output=8bd74ea6538ded53 input=74211232b363363e]*/ +/*[clinic end generated code: output=8bd74ea6538ded53 input=22eaf07a7a0ee289]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return iobase_unsupported(state, "seek"); @@ -143,14 +145,14 @@ _io._IOBase.truncate Truncate file to size bytes. -File pointer is left unchanged. Size defaults to the current IO position -as reported by tell(). Return the new size. +File pointer is left unchanged. Size defaults to the current IO +position as reported by tell(). Return the new size. [clinic start generated code]*/ static PyObject * _io__IOBase_truncate_impl(PyObject *self, PyTypeObject *cls, PyObject *Py_UNUSED(size)) -/*[clinic end generated code: output=2013179bff1fe8ef input=660ac20936612c27]*/ +/*[clinic end generated code: output=2013179bff1fe8ef input=5b3b6ab3c7abd806]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return iobase_unsupported(state, "truncate"); @@ -884,7 +886,7 @@ static PyType_Slot iobase_slots[] = { {0, NULL}, }; -PyType_Spec iobase_spec = { +PyType_Spec _Py_iobase_spec = { .name = "_io._IOBase", .basicsize = sizeof(iobase), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | @@ -926,26 +928,33 @@ _io__RawIOBase_read_impl(PyObject *self, Py_ssize_t n) return PyObject_CallMethodNoArgs(self, &_Py_ID(readall)); } - /* TODO: allocate a bytes object directly instead and manually construct - a writable memoryview pointing to it. */ b = PyByteArray_FromStringAndSize(NULL, n); - if (b == NULL) + if (b == NULL) { return NULL; + } res = PyObject_CallMethodObjArgs(self, &_Py_ID(readinto), b, NULL); if (res == NULL || res == Py_None) { - Py_DECREF(b); - return res; + goto cleanup; } - n = PyNumber_AsSsize_t(res, PyExc_ValueError); - Py_DECREF(res); - if (n == -1 && PyErr_Occurred()) { - Py_DECREF(b); - return NULL; + Py_ssize_t bytes_filled = PyNumber_AsSsize_t(res, PyExc_ValueError); + Py_CLEAR(res); + if (bytes_filled == -1 && PyErr_Occurred()) { + goto cleanup; + } + if (bytes_filled < 0 || bytes_filled > n) { + PyErr_Format(PyExc_ValueError, + "readinto returned %zd outside buffer size %zd", + bytes_filled, n); + goto cleanup; + } + if (PyByteArray_Resize(b, bytes_filled) < 0) { + goto cleanup; } + res = PyObject_CallMethodNoArgs(b, &_Py_ID(take_bytes)); - res = PyBytes_FromStringAndSize(PyByteArray_AsString(b), n); +cleanup: Py_DECREF(b); return res; } @@ -961,12 +970,10 @@ static PyObject * _io__RawIOBase_readall_impl(PyObject *self) /*[clinic end generated code: output=1987b9ce929425a0 input=688874141213622a]*/ { - int r; - PyObject *chunks = PyList_New(0); - PyObject *result; - - if (chunks == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(0); + if (writer == NULL) { return NULL; + } while (1) { PyObject *data = _PyObject_CallMethod(self, &_Py_ID(read), @@ -977,21 +984,21 @@ _io__RawIOBase_readall_impl(PyObject *self) if (_PyIO_trap_eintr()) { continue; } - Py_DECREF(chunks); + PyBytesWriter_Discard(writer); return NULL; } if (data == Py_None) { - if (PyList_GET_SIZE(chunks) == 0) { - Py_DECREF(chunks); + if (PyBytesWriter_GetSize(writer) == 0) { + PyBytesWriter_Discard(writer); return data; } Py_DECREF(data); break; } if (!PyBytes_Check(data)) { - Py_DECREF(chunks); Py_DECREF(data); PyErr_SetString(PyExc_TypeError, "read() should return bytes"); + PyBytesWriter_Discard(writer); return NULL; } if (PyBytes_GET_SIZE(data) == 0) { @@ -999,16 +1006,16 @@ _io__RawIOBase_readall_impl(PyObject *self) Py_DECREF(data); break; } - r = PyList_Append(chunks, data); - Py_DECREF(data); - if (r < 0) { - Py_DECREF(chunks); + if (PyBytesWriter_WriteBytes(writer, + PyBytes_AS_STRING(data), + PyBytes_GET_SIZE(data)) < 0) { + Py_DECREF(data); + PyBytesWriter_Discard(writer); return NULL; } + Py_DECREF(data); } - result = PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), chunks); - Py_DECREF(chunks); - return result; + return PyBytesWriter_Finish(writer); } static PyObject * @@ -1040,7 +1047,7 @@ static PyType_Slot rawiobase_slots[] = { }; /* Do not set Py_TPFLAGS_HAVE_GC so that tp_traverse and tp_clear are inherited */ -PyType_Spec rawiobase_spec = { +PyType_Spec _Py_rawiobase_spec = { .name = "_io._RawIOBase", .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE), diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 20b7cfc00880cd..b8601383ad0a26 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -111,7 +111,7 @@ resize_buffer(stringio *self, size_t size) alloc = size + 1; } - if (alloc > PY_SIZE_MAX / sizeof(Py_UCS4)) + if (alloc > SIZE_MAX / sizeof(Py_UCS4)) goto overflow; new_buf = (Py_UCS4 *)PyMem_Realloc(self->buf, alloc * sizeof(Py_UCS4)); if (new_buf == NULL) { @@ -225,7 +225,9 @@ write_str(stringio *self, PyObject *obj) if (self->state == STATE_ACCUMULATING) { if (self->string_size == self->pos) { - if (PyUnicodeWriter_WriteStr(self->writer, decoded)) + // gh-149046: Avoid PyUnicodeWriter_WriteStr() which calls str(obj) + // on str subclasses + if (_PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)self->writer, decoded)) goto fail; goto success; } @@ -497,7 +499,8 @@ _io.StringIO.seek Change stream position. -Seek to character offset pos relative to position indicated by whence: +Seek to character offset pos relative to position indicated by +whence: 0 Start of stream (the default). pos should be >= 0; 1 Current position - pos must be 0; 2 End of stream - pos must be 0. @@ -506,7 +509,7 @@ Returns the new absolute position. static PyObject * _io_StringIO_seek_impl(stringio *self, Py_ssize_t pos, int whence) -/*[clinic end generated code: output=e9e0ac9a8ae71c25 input=c75ced09343a00d7]*/ +/*[clinic end generated code: output=e9e0ac9a8ae71c25 input=ffef24668fd71a5d]*/ { CHECK_INITIALIZED(self); CHECK_CLOSED(self); @@ -1094,7 +1097,7 @@ static PyType_Slot stringio_slots[] = { {0, NULL}, }; -PyType_Spec stringio_spec = { +PyType_Spec _Py_stringio_spec = { .name = "_io.StringIO", .basicsize = sizeof(stringio), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 5354cf63442599..24e08cec88f2a3 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -59,12 +59,13 @@ _io._TextIOBase.detach Separate the underlying buffer from the TextIOBase and return it. -After the underlying buffer has been detached, the TextIO is in an unusable state. +After the underlying buffer has been detached, the TextIO is in +an unusable state. [clinic start generated code]*/ static PyObject * _io__TextIOBase_detach_impl(PyObject *self, PyTypeObject *cls) -/*[clinic end generated code: output=50915f40c609eaa4 input=987ca3640d0a3776]*/ +/*[clinic end generated code: output=50915f40c609eaa4 input=8099c088abcb87d8]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return _unsupported(state, "detach"); @@ -78,14 +79,14 @@ _io._TextIOBase.read Read at most size characters from stream. -Read from underlying buffer until we have size characters or we hit EOF. -If size is negative or omitted, read until EOF. +Read from underlying buffer until we have size characters or we hit +EOF. If size is negative or omitted, read until EOF. [clinic start generated code]*/ static PyObject * _io__TextIOBase_read_impl(PyObject *self, PyTypeObject *cls, int Py_UNUSED(size)) -/*[clinic end generated code: output=51a5178a309ce647 input=f5e37720f9fc563f]*/ +/*[clinic end generated code: output=51a5178a309ce647 input=c9fd4cc1cf1b4614]*/ { _PyIO_State *state = get_io_state_by_cls(cls); return _unsupported(state, "read"); @@ -207,7 +208,7 @@ static PyType_Slot textiobase_slots[] = { }; /* Do not set Py_TPFLAGS_HAVE_GC so that tp_traverse and tp_clear are inherited */ -PyType_Spec textiobase_spec = { +PyType_Spec _Py_textiobase_spec = { .name = "_io._TextIOBase", .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE), @@ -518,6 +519,7 @@ _PyIncrementalNewlineDecoder_decode(PyObject *myself, } /*[clinic input] +@critical_section _io.IncrementalNewlineDecoder.decode input: object final: bool = False @@ -526,18 +528,19 @@ _io.IncrementalNewlineDecoder.decode static PyObject * _io_IncrementalNewlineDecoder_decode_impl(nldecoder_object *self, PyObject *input, int final) -/*[clinic end generated code: output=0d486755bb37a66e input=90e223c70322c5cd]*/ +/*[clinic end generated code: output=0d486755bb37a66e input=9475d16a73168504]*/ { return _PyIncrementalNewlineDecoder_decode((PyObject *) self, input, final); } /*[clinic input] +@critical_section _io.IncrementalNewlineDecoder.getstate [clinic start generated code]*/ static PyObject * _io_IncrementalNewlineDecoder_getstate_impl(nldecoder_object *self) -/*[clinic end generated code: output=f0d2c9c136f4e0d0 input=f8ff101825e32e7f]*/ +/*[clinic end generated code: output=f0d2c9c136f4e0d0 input=dc3e1f27aa850f12]*/ { PyObject *buffer; unsigned long long flag; @@ -575,6 +578,7 @@ _io_IncrementalNewlineDecoder_getstate_impl(nldecoder_object *self) } /*[clinic input] +@critical_section _io.IncrementalNewlineDecoder.setstate state: object / @@ -583,7 +587,7 @@ _io.IncrementalNewlineDecoder.setstate static PyObject * _io_IncrementalNewlineDecoder_setstate_impl(nldecoder_object *self, PyObject *state) -/*[clinic end generated code: output=09135cb6e78a1dc8 input=c53fb505a76dbbe2]*/ +/*[clinic end generated code: output=09135cb6e78a1dc8 input=275fd3982d2b08cb]*/ { PyObject *buffer; unsigned long long flag; @@ -613,12 +617,13 @@ _io_IncrementalNewlineDecoder_setstate_impl(nldecoder_object *self, } /*[clinic input] +@critical_section _io.IncrementalNewlineDecoder.reset [clinic start generated code]*/ static PyObject * _io_IncrementalNewlineDecoder_reset_impl(nldecoder_object *self) -/*[clinic end generated code: output=32fa40c7462aa8ff input=728678ddaea776df]*/ +/*[clinic end generated code: output=32fa40c7462aa8ff input=31bd8ae4e36cec83]*/ { CHECK_INITIALIZED_DECODER(self); @@ -667,6 +672,8 @@ struct textio int ok; /* initialized? */ int detached; Py_ssize_t chunk_size; + /* Use helpers buffer_*() functions to access buffer; many operations can set it to + NULL (see gh-143008, gh-142594). */ PyObject *buffer; PyObject *encoding; PyObject *encoder; @@ -724,6 +731,67 @@ struct textio #define textio_CAST(op) ((textio *)(op)) +/* Helpers to safely operate on self->buffer. + + self->buffer can be detached (set to NULL) by any user code that is called + leading to NULL pointer dereferences (see gh-143008, gh-142594). Protect + against that by using helpers to check self->buffer validity at callsites. */ +static PyObject * +buffer_access_safe(textio *self) +{ + /* Check self->buffer directly but match errors of CHECK_ATTACHED since this + is called during construction and finalization where self->ok == 0. */ + if (self->buffer == NULL) { + if (self->ok <= 0) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on uninitialized object"); + } + else { + PyErr_SetString(PyExc_ValueError, + "underlying buffer has been detached"); + } + return NULL; + } + + /* Returning a borrowed reference is safe since TextIOWrapper methods are + protected by critical sections. */ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); + return self->buffer; +} + +static PyObject * +buffer_getattr(textio *self, PyObject *attr_name) +{ + PyObject *buffer = buffer_access_safe(self); + if (buffer == NULL) { + return NULL; + } + + return PyObject_GetAttr(buffer, attr_name); +} + +static PyObject * +buffer_callmethod_noargs(textio *self, PyObject *name) +{ + PyObject *buffer = buffer_access_safe(self); + if (buffer == NULL) { + return NULL; + } + + return PyObject_CallMethodNoArgs(buffer, name); +} + +static PyObject * +buffer_callmethod_onearg(textio *self, PyObject *name, PyObject *arg) +{ + PyObject *buffer = buffer_access_safe(self); + if (buffer == NULL) { + return NULL; + } + + return PyObject_CallMethodOneArg(buffer, name, arg); +} + static void textiowrapper_set_decoded_chars(textio *self, PyObject *chars); @@ -893,7 +961,7 @@ _textiowrapper_set_decoder(textio *self, PyObject *codec_info, PyObject *res; int r; - res = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(readable)); + res = buffer_callmethod_noargs(self, &_Py_ID(readable)); if (res == NULL) return -1; @@ -949,7 +1017,7 @@ _textiowrapper_set_encoder(textio *self, PyObject *codec_info, PyObject *res; int r; - res = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(writable)); + res = buffer_callmethod_noargs(self, &_Py_ID(writable)); if (res == NULL) return -1; @@ -995,8 +1063,7 @@ _textiowrapper_fix_encoder_state(textio *self) self->encoding_start_of_stream = 1; - PyObject *cookieObj = PyObject_CallMethodNoArgs( - self->buffer, &_Py_ID(tell)); + PyObject *cookieObj = buffer_callmethod_noargs(self, &_Py_ID(tell)); if (cookieObj == NULL) { return -1; } @@ -1056,6 +1123,7 @@ io_check_errors(PyObject *errors) /*[clinic input] +@critical_section _io.TextIOWrapper.__init__ buffer: object encoding: str(accept={str, NoneType}) = None @@ -1099,7 +1167,7 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer, const char *encoding, PyObject *errors, const char *newline, int line_buffering, int write_through) -/*[clinic end generated code: output=72267c0c01032ed2 input=e6cfaaaf6059d4f5]*/ +/*[clinic end generated code: output=72267c0c01032ed2 input=0f077220214c40a4]*/ { PyObject *raw, *codec_info = NULL; PyObject *res; @@ -1563,11 +1631,14 @@ _io_TextIOWrapper_detach_impl(textio *self) /*[clinic end generated code: output=7ba3715cd032d5f2 input=c908a3b4ef203b0f]*/ { PyObject *buffer; - CHECK_ATTACHED(self); if (_PyFile_Flush((PyObject *)self) < 0) { return NULL; } - buffer = self->buffer; + /* _PyFile_Flush could detach before returning; raise an exception. */ + buffer = buffer_access_safe(self); + if (buffer == NULL) { + return NULL; + } self->buffer = NULL; self->detached = 1; return buffer; @@ -1636,7 +1707,7 @@ _textiowrapper_writeflush(textio *self) PyObject *ret; do { - ret = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(write), b); + ret = buffer_callmethod_onearg(self, &_Py_ID(write), b); } while (ret == NULL && _PyIO_trap_eintr()); Py_DECREF(b); // NOTE: We cleared buffer but we don't know how many bytes are actually written @@ -1782,7 +1853,8 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text) } if (needflush) { - if (_PyFile_Flush(self->buffer) < 0) { + PyObject *buffer = buffer_access_safe(self); + if (buffer == NULL || _PyFile_Flush(buffer) < 0) { return NULL; } } @@ -1912,9 +1984,10 @@ textiowrapper_read_chunk(textio *self, Py_ssize_t size_hint) if (chunk_size == NULL) goto fail; - input_chunk = PyObject_CallMethodOneArg(self->buffer, - (self->has_read1 ? &_Py_ID(read1): &_Py_ID(read)), - chunk_size); + input_chunk = buffer_callmethod_onearg(self, + (self->has_read1 ? &_Py_ID(read1) : + &_Py_ID(read)), + chunk_size); Py_DECREF(chunk_size); if (input_chunk == NULL) goto fail; @@ -1998,7 +2071,7 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n) if (n < 0) { /* Read everything */ - PyObject *bytes = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(read)); + PyObject *bytes = buffer_callmethod_noargs(self, &_Py_ID(read)); PyObject *decoded; if (bytes == NULL) goto fail; @@ -2595,7 +2668,11 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) Py_DECREF(res); } - res = _PyObject_CallMethod(self->buffer, &_Py_ID(seek), "ii", 0, 2); + PyObject *buf = buffer_access_safe(self); + if (buf == NULL) { + goto fail; + } + res = _PyObject_CallMethod(buf, &_Py_ID(seek), "ii", 0, 2); Py_CLEAR(cookieObj); if (res == NULL) goto fail; @@ -2643,7 +2720,7 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) posobj = PyLong_FromOff_t(cookie.start_pos); if (posobj == NULL) goto fail; - res = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(seek), posobj); + res = buffer_callmethod_onearg(self, &_Py_ID(seek), posobj); Py_DECREF(posobj); if (res == NULL) goto fail; @@ -2660,8 +2737,15 @@ _io_TextIOWrapper_seek_impl(textio *self, PyObject *cookieObj, int whence) if (cookie.chars_to_skip) { /* Just like _read_chunk, feed the decoder and save a snapshot. */ - PyObject *input_chunk = _PyObject_CallMethod(self->buffer, &_Py_ID(read), - "i", cookie.bytes_to_feed); + PyObject *bytes_to_feed = PyLong_FromLong(cookie.bytes_to_feed); + if (bytes_to_feed == NULL) { + goto fail; + } + PyObject *input_chunk = buffer_callmethod_onearg(self, + &_Py_ID(read), + bytes_to_feed); + Py_DECREF(bytes_to_feed); + PyObject *decoded; if (input_chunk == NULL) @@ -2722,13 +2806,13 @@ _io.TextIOWrapper.tell Return the stream position as an opaque number. -The return value of tell() can be given as input to seek(), to restore a -previous stream position. +The return value of tell() can be given as input to seek(), to +restore a previous stream position. [clinic start generated code]*/ static PyObject * _io_TextIOWrapper_tell_impl(textio *self) -/*[clinic end generated code: output=4f168c08bf34ad5f input=415d6b4e4f8e6e8c]*/ +/*[clinic end generated code: output=4f168c08bf34ad5f input=aeece020f747fd92]*/ { PyObject *res; PyObject *posobj = NULL; @@ -2760,7 +2844,7 @@ _io_TextIOWrapper_tell_impl(textio *self) goto fail; } - posobj = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(tell)); + posobj = buffer_callmethod_noargs(self, &_Py_ID(tell)); if (posobj == NULL) goto fail; @@ -2844,7 +2928,7 @@ _io_TextIOWrapper_tell_impl(textio *self) current pos */ skip_bytes = (Py_ssize_t) (self->b2cratio * chars_to_skip); skip_back = 1; - assert(skip_back <= PyBytes_GET_SIZE(next_input)); + assert(skip_bytes <= PyBytes_GET_SIZE(next_input)); input = PyBytes_AS_STRING(next_input); while (skip_bytes > 0) { /* Decode up to temptative start point */ @@ -2970,7 +3054,7 @@ _io_TextIOWrapper_truncate_impl(textio *self, PyObject *pos) return NULL; } - return PyObject_CallMethodOneArg(self->buffer, &_Py_ID(truncate), pos); + return buffer_callmethod_onearg(self, &_Py_ID(truncate), pos); } static PyObject * @@ -3052,8 +3136,7 @@ static PyObject * _io_TextIOWrapper_fileno_impl(textio *self) /*[clinic end generated code: output=21490a4c3da13e6c input=515e1196aceb97ab]*/ { - CHECK_ATTACHED(self); - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(fileno)); + return buffer_callmethod_noargs(self, &_Py_ID(fileno)); } /*[clinic input] @@ -3065,8 +3148,7 @@ static PyObject * _io_TextIOWrapper_seekable_impl(textio *self) /*[clinic end generated code: output=ab223dbbcffc0f00 input=71c4c092736c549b]*/ { - CHECK_ATTACHED(self); - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(seekable)); + return buffer_callmethod_noargs(self, &_Py_ID(seekable)); } /*[clinic input] @@ -3078,8 +3160,7 @@ static PyObject * _io_TextIOWrapper_readable_impl(textio *self) /*[clinic end generated code: output=72ff7ba289a8a91b input=80438d1f01b0a89b]*/ { - CHECK_ATTACHED(self); - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(readable)); + return buffer_callmethod_noargs(self, &_Py_ID(readable)); } /*[clinic input] @@ -3091,8 +3172,7 @@ static PyObject * _io_TextIOWrapper_writable_impl(textio *self) /*[clinic end generated code: output=a728c71790d03200 input=9d6c22befb0c340a]*/ { - CHECK_ATTACHED(self); - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(writable)); + return buffer_callmethod_noargs(self, &_Py_ID(writable)); } /*[clinic input] @@ -3104,8 +3184,7 @@ static PyObject * _io_TextIOWrapper_isatty_impl(textio *self) /*[clinic end generated code: output=12be1a35bace882e input=7f83ff04d4d1733d]*/ { - CHECK_ATTACHED(self); - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(isatty)); + return buffer_callmethod_noargs(self, &_Py_ID(isatty)); } /*[clinic input] @@ -3122,7 +3201,7 @@ _io_TextIOWrapper_flush_impl(textio *self) self->telling = self->seekable; if (_textiowrapper_writeflush(self) < 0) return NULL; - return PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(flush)); + return buffer_callmethod_noargs(self, &_Py_ID(flush)); } /*[clinic input] @@ -3149,11 +3228,15 @@ _io_TextIOWrapper_close_impl(textio *self) if (r > 0) { Py_RETURN_NONE; /* stream already closed */ } + if (self->detached) { + Py_RETURN_NONE; /* gh-142594 null pointer issue */ + } else { PyObject *exc = NULL; if (self->finalizing) { - res = PyObject_CallMethodOneArg(self->buffer, &_Py_ID(_dealloc_warn), - (PyObject *)self); + res = buffer_callmethod_onearg(self, + &_Py_ID(_dealloc_warn), + (PyObject *)self); if (res) { Py_DECREF(res); } @@ -3165,7 +3248,7 @@ _io_TextIOWrapper_close_impl(textio *self) exc = PyErr_GetRaisedException(); } - res = PyObject_CallMethodNoArgs(self->buffer, &_Py_ID(close)); + res = buffer_callmethod_noargs(self, &_Py_ID(close)); if (exc != NULL) { _PyErr_ChainExceptions1(exc); Py_CLEAR(res); @@ -3233,8 +3316,7 @@ static PyObject * _io_TextIOWrapper_name_get_impl(textio *self) /*[clinic end generated code: output=8c2f1d6d8756af40 input=26ecec9b39e30e07]*/ { - CHECK_ATTACHED(self); - return PyObject_GetAttr(self->buffer, &_Py_ID(name)); + return buffer_getattr(self, &_Py_ID(name)); } /*[clinic input] @@ -3247,8 +3329,17 @@ static PyObject * _io_TextIOWrapper_closed_get_impl(textio *self) /*[clinic end generated code: output=b49b68f443a85e3c input=7dfcf43f63c7003d]*/ { - CHECK_ATTACHED(self); - return PyObject_GetAttr(self->buffer, &_Py_ID(closed)); + /* If partially constructed or deconstructed, return that the underlying + buffer is closed. + + The code managing the transition is responsible for closing. The closed + attribute is often called in re-initalization, as part of repr in error + cases, and when the I/O stack is garbage collected. */ + if (self->ok <= 0) { + Py_RETURN_TRUE; + } + + return buffer_getattr(self, &_Py_ID(closed)); } /*[clinic input] @@ -3351,7 +3442,7 @@ static PyType_Slot nldecoder_slots[] = { {0, NULL}, }; -PyType_Spec nldecoder_spec = { +PyType_Spec _Py_nldecoder_spec = { .name = "_io.IncrementalNewlineDecoder", .basicsize = sizeof(nldecoder_object), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | @@ -3403,7 +3494,7 @@ static PyGetSetDef textiowrapper_getset[] = { {NULL} }; -PyType_Slot textiowrapper_slots[] = { +static PyType_Slot textiowrapper_slots[] = { {Py_tp_dealloc, textiowrapper_dealloc}, {Py_tp_repr, textiowrapper_repr}, {Py_tp_doc, (void *)_io_TextIOWrapper___init____doc__}, @@ -3417,7 +3508,7 @@ PyType_Slot textiowrapper_slots[] = { {0, NULL}, }; -PyType_Spec textiowrapper_spec = { +PyType_Spec _Py_textiowrapper_spec = { .name = "_io.TextIOWrapper", .basicsize = sizeof(textio), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 950b7fe241ccde..4cd71094e8f459 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -315,16 +315,16 @@ _io._WindowsConsoleIO.__init__ Open a console buffer by file descriptor. -The mode can be 'rb' (default), or 'wb' for reading or writing bytes. All -other mode characters will be ignored. Mode 'b' will be assumed if it is -omitted. The *opener* parameter is always ignored. +The mode can be 'rb' (default), or 'wb' for reading or writing +bytes. All other mode characters will be ignored. Mode 'b' will be +assumed if it is omitted. The *opener* parameter is always ignored. [clinic start generated code]*/ static int _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj, const char *mode, int closefd, PyObject *opener) -/*[clinic end generated code: output=3fd9cbcdd8d95429 input=7a3eed6bbe998fd9]*/ +/*[clinic end generated code: output=3fd9cbcdd8d95429 input=f31100e2cd724617]*/ { const char *s; wchar_t *name = NULL; @@ -673,12 +673,13 @@ read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) { maxlen += 1; Py_BLOCK_THREADS newbuf = (wchar_t*)PyMem_Realloc(buf, maxlen * sizeof(wchar_t)); - Py_UNBLOCK_THREADS if (!newbuf) { sig = -1; PyErr_NoMemory(); + Py_UNBLOCK_THREADS break; } + Py_UNBLOCK_THREADS buf = newbuf; /* Only advance by n and not BUFSIZ in this case */ off += n; @@ -1253,7 +1254,7 @@ static PyType_Slot winconsoleio_slots[] = { {0, NULL}, }; -PyType_Spec winconsoleio_spec = { +PyType_Spec _Py_winconsoleio_spec = { .name = "_io._WindowsConsoleIO", .basicsize = sizeof(winconsoleio), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | diff --git a/Modules/_json.c b/Modules/_json.c index 7580b589e2d937..b057b56b2f9f8d 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -10,19 +10,30 @@ #include "Python.h" #include "pycore_ceval.h" // _Py_EnterRecursiveCall() +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST() +#include "pycore_dict.h" // _PyDict_SetItem_Take2() +#include "pycore_list.h" // _PyList_AppendTakeRef() #include "pycore_global_strings.h" // _Py_ID() #include "pycore_pyerrors.h" // _PyErr_FormatNote #include "pycore_runtime.h" // _PyRuntime +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_unicodeobject.h" // _PyUnicode_CheckConsistency() #include <stdbool.h> // bool +#include "clinic/_json.c.h" + +/*[clinic input] +module _json +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=549fa53592c925b2]*/ typedef struct _PyScannerObject { PyObject_HEAD signed char strict; PyObject *object_hook; PyObject *object_pairs_hook; + PyObject *array_hook; PyObject *parse_float; PyObject *parse_int; PyObject *parse_constant; @@ -34,6 +45,7 @@ static PyMemberDef scanner_members[] = { {"strict", Py_T_BOOL, offsetof(PyScannerObject, strict), Py_READONLY, "strict"}, {"object_hook", _Py_T_OBJECT, offsetof(PyScannerObject, object_hook), Py_READONLY, "object_hook"}, {"object_pairs_hook", _Py_T_OBJECT, offsetof(PyScannerObject, object_pairs_hook), Py_READONLY}, + {"array_hook", _Py_T_OBJECT, offsetof(PyScannerObject, array_hook), Py_READONLY}, {"parse_float", _Py_T_OBJECT, offsetof(PyScannerObject, parse_float), Py_READONLY, "parse_float"}, {"parse_int", _Py_T_OBJECT, offsetof(PyScannerObject, parse_int), Py_READONLY, "parse_int"}, {"parse_constant", _Py_T_OBJECT, offsetof(PyScannerObject, parse_constant), Py_READONLY, "parse_constant"}, @@ -51,7 +63,7 @@ typedef struct _PyEncoderObject { char sort_keys; char skipkeys; int allow_nan; - PyCFunction fast_encode; + int (*fast_encode)(PyUnicodeWriter *, PyObject *); } PyEncoderObject; #define PyEncoderObject_CAST(op) ((PyEncoderObject *)(op)) @@ -102,8 +114,10 @@ static PyObject * _encoded_const(PyObject *obj); static void raise_errmsg(const char *msg, PyObject *s, Py_ssize_t end); -static PyObject * -encoder_encode_string(PyEncoderObject *s, PyObject *obj); +static int +_steal_accumulate(PyUnicodeWriter *writer, PyObject *stolen); +static int +encoder_write_string(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *obj); static PyObject * encoder_encode_float(PyEncoderObject *s, PyObject *obj); @@ -146,22 +160,11 @@ ascii_escape_unichar(Py_UCS4 c, unsigned char *output, Py_ssize_t chars) return chars; } -static PyObject * -ascii_escape_unicode(PyObject *pystr) +static Py_ssize_t +ascii_escape_size(const void *input, int kind, Py_ssize_t input_chars) { - /* Take a PyUnicode pystr and return a new ASCII-only escaped PyUnicode */ Py_ssize_t i; - Py_ssize_t input_chars; Py_ssize_t output_size; - Py_ssize_t chars; - PyObject *rval; - const void *input; - Py_UCS1 *output; - int kind; - - input_chars = PyUnicode_GET_LENGTH(pystr); - input = PyUnicode_DATA(pystr); - kind = PyUnicode_KIND(pystr); /* Compute the output size */ for (i = 0, output_size = 2; i < input_chars; i++) { @@ -181,11 +184,22 @@ ascii_escape_unicode(PyObject *pystr) } if (output_size > PY_SSIZE_T_MAX - d) { PyErr_SetString(PyExc_OverflowError, "string is too long to escape"); - return NULL; + return -1; } output_size += d; } + return output_size; +} + +static PyObject * +ascii_escape_unicode_and_size(const void *input, int kind, Py_ssize_t input_chars, Py_ssize_t output_size) +{ + Py_ssize_t i; + Py_ssize_t chars; + PyObject *rval; + Py_UCS1 *output; + rval = PyUnicode_New(output_size, 127); if (rval == NULL) { return NULL; @@ -210,23 +224,65 @@ ascii_escape_unicode(PyObject *pystr) } static PyObject * -escape_unicode(PyObject *pystr) +ascii_escape_unicode(PyObject *pystr) +{ + /* Take a PyUnicode pystr and return a new ASCII-only escaped PyUnicode */ + Py_ssize_t input_chars = PyUnicode_GET_LENGTH(pystr); + const void *input = PyUnicode_DATA(pystr); + int kind = PyUnicode_KIND(pystr); + + Py_ssize_t output_size = ascii_escape_size(input, kind, input_chars); + if (output_size < 0) { + return NULL; + } + + return ascii_escape_unicode_and_size(input, kind, input_chars, output_size); +} + +static int +write_escaped_ascii(PyUnicodeWriter *writer, PyObject *pystr) { - /* Take a PyUnicode pystr and return a new escaped PyUnicode */ - Py_ssize_t i; Py_ssize_t input_chars; - Py_ssize_t output_size; - Py_ssize_t chars; - PyObject *rval; const void *input; int kind; - Py_UCS4 maxchar; - maxchar = PyUnicode_MAX_CHAR_VALUE(pystr); input_chars = PyUnicode_GET_LENGTH(pystr); input = PyUnicode_DATA(pystr); kind = PyUnicode_KIND(pystr); + Py_ssize_t output_size = ascii_escape_size(input, kind, input_chars); + if (output_size < 0) { + return -1; + } + + if (output_size == input_chars + 2) { + /* No need to escape anything */ + if (PyUnicodeWriter_WriteChar(writer, '"') < 0) { + return -1; + } + // gh-148241: Avoid PyUnicodeWriter_WriteStr() which calls str(obj) + // on str subclasses + assert(PyUnicode_IS_ASCII(pystr)); + if (PyUnicodeWriter_WriteASCII(writer, input, input_chars) < 0) { + return -1; + } + return PyUnicodeWriter_WriteChar(writer, '"'); + } + + PyObject *rval = ascii_escape_unicode_and_size(input, kind, input_chars, output_size); + if (rval == NULL) { + return -1; + } + + return _steal_accumulate(writer, rval); +} + +static Py_ssize_t +escape_size(const void *input, int kind, Py_ssize_t input_chars) +{ + Py_ssize_t i; + Py_ssize_t output_size; + /* Compute the output size */ for (i = 0, output_size = 2; i < input_chars; i++) { Py_UCS4 c = PyUnicode_READ(kind, input, i); @@ -244,11 +300,21 @@ escape_unicode(PyObject *pystr) } if (output_size > PY_SSIZE_T_MAX - d) { PyErr_SetString(PyExc_OverflowError, "string is too long to escape"); - return NULL; + return -1; } output_size += d; } + return output_size; +} + +static PyObject * +escape_unicode_and_size(const void *input, int kind, Py_UCS4 maxchar, Py_ssize_t input_chars, Py_ssize_t output_size) +{ + Py_ssize_t i; + Py_ssize_t chars; + PyObject *rval; + rval = PyUnicode_New(output_size, maxchar); if (rval == NULL) return NULL; @@ -303,6 +369,57 @@ escape_unicode(PyObject *pystr) return rval; } +static PyObject * +escape_unicode(PyObject *pystr) +{ + /* Take a PyUnicode pystr and return a new escaped PyUnicode */ + Py_ssize_t input_chars = PyUnicode_GET_LENGTH(pystr); + const void *input = PyUnicode_DATA(pystr); + int kind = PyUnicode_KIND(pystr); + Py_UCS4 maxchar = PyUnicode_MAX_CHAR_VALUE(pystr); + + Py_ssize_t output_size = escape_size(input, kind, input_chars); + if (output_size < 0) { + return NULL; + } + + return escape_unicode_and_size(input, kind, maxchar, input_chars, output_size); +} + +static int +write_escaped_unicode(PyUnicodeWriter *writer, PyObject *pystr) +{ + Py_ssize_t input_chars = PyUnicode_GET_LENGTH(pystr); + const void *input = PyUnicode_DATA(pystr); + int kind = PyUnicode_KIND(pystr); + Py_UCS4 maxchar = PyUnicode_MAX_CHAR_VALUE(pystr); + + Py_ssize_t output_size = escape_size(input, kind, input_chars); + if (output_size < 0) { + return -1; + } + + if (output_size == input_chars + 2) { + /* No need to escape anything */ + if (PyUnicodeWriter_WriteChar(writer, '"') < 0) { + return -1; + } + // gh-148241: Avoid PyUnicodeWriter_WriteStr() which calls str(obj) + // on str subclasses + if (_PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, pystr) < 0) { + return -1; + } + return PyUnicodeWriter_WriteChar(writer, '"'); + } + + PyObject *rval = escape_unicode_and_size(input, kind, maxchar, input_chars, output_size); + if (rval == NULL) { + return -1; + } + + return _steal_accumulate(writer, rval); +} + static void raise_errmsg(const char *msg, PyObject *s, Py_ssize_t end) { @@ -316,11 +433,12 @@ raise_errmsg(const char *msg, PyObject *s, Py_ssize_t end) PyObject *exc; exc = PyObject_CallFunction(JSONDecodeError, "zOn", msg, s, end); - Py_DECREF(JSONDecodeError); if (exc) { PyErr_SetObject(JSONDecodeError, exc); Py_DECREF(exc); } + + Py_DECREF(JSONDecodeError); } static void @@ -336,7 +454,6 @@ raise_stop_iteration(Py_ssize_t idx) static PyObject * _build_rval_index_tuple(PyObject *rval, Py_ssize_t idx) { /* return (rval, idx) tuple, stealing reference to rval */ - PyObject *tpl; PyObject *pyidx; /* steal a reference to rval, returns (rval, idx) @@ -349,15 +466,7 @@ _build_rval_index_tuple(PyObject *rval, Py_ssize_t idx) { Py_DECREF(rval); return NULL; } - tpl = PyTuple_New(2); - if (tpl == NULL) { - Py_DECREF(pyidx); - Py_DECREF(rval); - return NULL; - } - PyTuple_SET_ITEM(tpl, 0, rval); - PyTuple_SET_ITEM(tpl, 1, pyidx); - return tpl; + return _PyTuple_FromPairSteal(rval, pyidx); } static PyObject * @@ -536,89 +645,62 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next return NULL; } -PyDoc_STRVAR(pydoc_scanstring, - "scanstring(string, end, strict=True) -> (string, end)\n" - "\n" - "Scan the string s for a JSON string. End is the index of the\n" - "character in s after the quote that started the JSON string.\n" - "Unescapes all valid JSON string escape sequences and raises ValueError\n" - "on attempt to decode an invalid string. If strict is False then literal\n" - "control characters are allowed in the string.\n" - "\n" - "Returns a tuple of the decoded string and the index of the character in s\n" - "after the end quote." -); +/*[clinic input] +_json.scanstring as py_scanstring + pystr: unicode + end: Py_ssize_t + strict: bool = True + / + +Scan the string s for a JSON string. + +End is the index of the character in s after the quote that started the +JSON string. Unescapes all valid JSON string escape sequences and raises +ValueError on attempt to decode an invalid string. If strict is False +then literal control characters are allowed in the string. + +Returns a tuple of the decoded string and the index of the character in +s after the end quote. +[clinic start generated code]*/ static PyObject * -py_scanstring(PyObject* Py_UNUSED(self), PyObject *args) +py_scanstring_impl(PyObject *module, PyObject *pystr, Py_ssize_t end, + int strict) +/*[clinic end generated code: output=961740cfae07cdb3 input=6d5abb5947ccc297]*/ { - PyObject *pystr; - PyObject *rval; - Py_ssize_t end; Py_ssize_t next_end = -1; - int strict = 1; - if (!PyArg_ParseTuple(args, "On|p:scanstring", &pystr, &end, &strict)) { - return NULL; - } - if (PyUnicode_Check(pystr)) { - rval = scanstring_unicode(pystr, end, strict, &next_end); - } - else { - PyErr_Format(PyExc_TypeError, - "first argument must be a string, not %.80s", - Py_TYPE(pystr)->tp_name); - return NULL; - } + PyObject *rval = scanstring_unicode(pystr, end, strict, &next_end); return _build_rval_index_tuple(rval, next_end); } -PyDoc_STRVAR(pydoc_encode_basestring_ascii, - "encode_basestring_ascii(string) -> string\n" - "\n" - "Return an ASCII-only JSON representation of a Python string" -); +/*[clinic input] +_json.encode_basestring_ascii as py_encode_basestring_ascii + pystr: unicode + / + +Return an ASCII-only JSON representation of a Python string +[clinic start generated code]*/ static PyObject * -py_encode_basestring_ascii(PyObject* Py_UNUSED(self), PyObject *pystr) +py_encode_basestring_ascii_impl(PyObject *module, PyObject *pystr) +/*[clinic end generated code: output=7b3841287cf211df input=4f3609498aff2de5]*/ { - PyObject *rval; - /* Return an ASCII-only JSON representation of a Python string */ - /* METH_O */ - if (PyUnicode_Check(pystr)) { - rval = ascii_escape_unicode(pystr); - } - else { - PyErr_Format(PyExc_TypeError, - "first argument must be a string, not %.80s", - Py_TYPE(pystr)->tp_name); - return NULL; - } - return rval; + return ascii_escape_unicode(pystr); } +/*[clinic input] +_json.encode_basestring as py_encode_basestring + pystr: unicode + / -PyDoc_STRVAR(pydoc_encode_basestring, - "encode_basestring(string) -> string\n" - "\n" - "Return a JSON representation of a Python string" -); +Return a JSON representation of a Python string +[clinic start generated code]*/ static PyObject * -py_encode_basestring(PyObject* Py_UNUSED(self), PyObject *pystr) +py_encode_basestring_impl(PyObject *module, PyObject *pystr) +/*[clinic end generated code: output=900950f95df3f1c9 input=d42ef714b2c07386]*/ { - PyObject *rval; - /* Return a JSON representation of a Python string */ - /* METH_O */ - if (PyUnicode_Check(pystr)) { - rval = escape_unicode(pystr); - } - else { - PyErr_Format(PyExc_TypeError, - "first argument must be a string, not %.80s", - Py_TYPE(pystr)->tp_name); - return NULL; - } - return rval; + return escape_unicode(pystr); } static void @@ -639,6 +721,7 @@ scanner_traverse(PyObject *op, visitproc visit, void *arg) Py_VISIT(Py_TYPE(self)); Py_VISIT(self->object_hook); Py_VISIT(self->object_pairs_hook); + Py_VISIT(self->array_hook); Py_VISIT(self->parse_float); Py_VISIT(self->parse_int); Py_VISIT(self->parse_constant); @@ -651,6 +734,7 @@ scanner_clear(PyObject *op) PyScannerObject *self = PyScannerObject_CAST(op); Py_CLEAR(self->object_hook); Py_CLEAR(self->object_pairs_hook); + Py_CLEAR(self->array_hook); Py_CLEAR(self->parse_float); Py_CLEAR(self->parse_int); Py_CLEAR(self->parse_constant); @@ -670,7 +754,6 @@ _parse_object_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ss const void *str; int kind; Py_ssize_t end_idx; - PyObject *val = NULL; PyObject *rval = NULL; PyObject *key = NULL; int has_pairs_hook = (s->object_pairs_hook != Py_None); @@ -720,16 +803,18 @@ _parse_object_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ss while (idx <= end_idx && IS_WHITESPACE(PyUnicode_READ(kind, str, idx))) idx++; /* read any JSON term */ - val = scan_once_unicode(s, memo, pystr, idx, &next_idx); + PyObject *val = scan_once_unicode(s, memo, pystr, idx, &next_idx); if (val == NULL) goto bail; + /* The steal below takes our references to both key and val + (releasing them on failure). Only key is reset for the bail + path; val is never live there, so it needs no cleanup. */ if (has_pairs_hook) { - PyObject *item = PyTuple_Pack(2, key, val); + PyObject *item = _PyTuple_FromPairSteal(key, val); + key = NULL; if (item == NULL) goto bail; - Py_CLEAR(key); - Py_CLEAR(val); if (PyList_Append(rval, item) == -1) { Py_DECREF(item); goto bail; @@ -737,10 +822,10 @@ _parse_object_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ss Py_DECREF(item); } else { - if (PyDict_SetItem(rval, key, val) < 0) + int err = _PyDict_SetItem_Take2((PyDictObject *)rval, key, val); + key = NULL; + if (err < 0) goto bail; - Py_CLEAR(key); - Py_CLEAR(val); } idx = next_idx; @@ -770,21 +855,20 @@ _parse_object_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ss *next_idx_ptr = idx + 1; if (has_pairs_hook) { - val = PyObject_CallOneArg(s->object_pairs_hook, rval); + PyObject *res = PyObject_CallOneArg(s->object_pairs_hook, rval); Py_DECREF(rval); - return val; + return res; } /* if object_hook is not None: rval = object_hook(rval) */ if (s->object_hook != Py_None) { - val = PyObject_CallOneArg(s->object_hook, rval); + PyObject *res = PyObject_CallOneArg(s->object_hook, rval); Py_DECREF(rval); - return val; + return res; } return rval; bail: Py_XDECREF(key); - Py_XDECREF(val); Py_XDECREF(rval); return NULL; } @@ -801,7 +885,6 @@ _parse_array_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ssi const void *str; int kind; Py_ssize_t end_idx; - PyObject *val = NULL; PyObject *rval; Py_ssize_t next_idx; Py_ssize_t comma_idx; @@ -822,14 +905,12 @@ _parse_array_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ssi while (1) { /* read any JSON term */ - val = scan_once_unicode(s, memo, pystr, idx, &next_idx); + PyObject *val = scan_once_unicode(s, memo, pystr, idx, &next_idx); if (val == NULL) goto bail; - if (PyList_Append(rval, val) == -1) + if (_PyList_AppendTakeRef((PyListObject *)rval, val) < 0) goto bail; - - Py_CLEAR(val); idx = next_idx; /* skip whitespace between term and , */ @@ -861,9 +942,14 @@ _parse_array_unicode(PyScannerObject *s, PyObject *memo, PyObject *pystr, Py_ssi goto bail; } *next_idx_ptr = idx + 1; + /* if array_hook is not None: return array_hook(rval) */ + if (!Py_IsNone(s->array_hook)) { + PyObject *res = PyObject_CallOneArg(s->array_hook, rval); + Py_DECREF(rval); + return res; + } return rval; bail: - Py_XDECREF(val); Py_DECREF(rval); return NULL; } @@ -1178,6 +1264,10 @@ scanner_new(PyTypeObject *type, PyObject *args, PyObject *kwds) s->object_pairs_hook = PyObject_GetAttrString(ctx, "object_pairs_hook"); if (s->object_pairs_hook == NULL) goto bail; + s->array_hook = PyObject_GetAttrString(ctx, "array_hook"); + if (s->array_hook == NULL) { + goto bail; + } s->parse_float = PyObject_GetAttrString(ctx, "parse_float"); if (s->parse_float == NULL) goto bail; @@ -1256,8 +1346,11 @@ encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (PyCFunction_Check(s->encoder)) { PyCFunction f = PyCFunction_GetFunction(s->encoder); - if (f == py_encode_basestring_ascii || f == py_encode_basestring) { - s->fast_encode = f; + if (f == py_encode_basestring_ascii) { + s->fast_encode = write_escaped_ascii; + } + else if (f == py_encode_basestring) { + s->fast_encode = write_escaped_unicode; } } @@ -1353,7 +1446,7 @@ write_newline_indent(PyUnicodeWriter *writer, static PyObject * encoder_call(PyObject *op, PyObject *args, PyObject *kwds) { - /* Python callable interface to encode_listencode_obj */ + /* Python callable interface to encoder_listencode_obj */ static char *kwlist[] = {"obj", "_current_indent_level", NULL}; PyObject *obj; Py_ssize_t indent_level; @@ -1376,6 +1469,7 @@ encoder_call(PyObject *op, PyObject *args, PyObject *kwds) return NULL; } } + indent_level = 0; if (encoder_listencode_obj(self, writer, obj, indent_level, indent_cache)) { PyUnicodeWriter_Discard(writer); Py_XDECREF(indent_cache); @@ -1438,24 +1532,27 @@ encoder_encode_float(PyEncoderObject *s, PyObject *obj) return PyFloat_Type.tp_repr(obj); } -static PyObject * -encoder_encode_string(PyEncoderObject *s, PyObject *obj) +static int +encoder_write_string(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *obj) { /* Return the JSON representation of a string */ PyObject *encoded; if (s->fast_encode) { - return s->fast_encode(NULL, obj); + return s->fast_encode(writer, obj); } encoded = PyObject_CallOneArg(s->encoder, obj); - if (encoded != NULL && !PyUnicode_Check(encoded)) { + if (encoded == NULL) { + return -1; + } + if (!PyUnicode_Check(encoded)) { PyErr_Format(PyExc_TypeError, "encoder() must return a string, not %.80s", Py_TYPE(encoded)->tp_name); Py_DECREF(encoded); - return NULL; + return -1; } - return encoded; + return _steal_accumulate(writer, encoded); } static int @@ -1486,10 +1583,7 @@ encoder_listencode_obj(PyEncoderObject *s, PyUnicodeWriter *writer, return PyUnicodeWriter_WriteASCII(writer, "false", 5); } else if (PyUnicode_Check(obj)) { - PyObject *encoded = encoder_encode_string(s, obj); - if (encoded == NULL) - return -1; - return _steal_accumulate(writer, encoded); + return encoder_write_string(s, writer, obj); } else if (PyLong_Check(obj)) { if (PyLong_CheckExact(obj)) { @@ -1514,7 +1608,7 @@ encoder_listencode_obj(PyEncoderObject *s, PyUnicodeWriter *writer, _Py_LeaveRecursiveCall(); return rv; } - else if (PyDict_Check(obj)) { + else if (PyAnyDict_Check(obj)) { if (_Py_EnterRecursiveCall(" while encoding a JSON object")) return -1; rv = encoder_listencode_dict(s, writer, obj, indent_level, indent_cache); @@ -1578,7 +1672,7 @@ encoder_encode_key_value(PyEncoderObject *s, PyUnicodeWriter *writer, bool *firs PyObject *item_separator) { PyObject *keystr = NULL; - PyObject *encoded; + int rv; if (PyUnicode_Check(key)) { keystr = Py_NewRef(key); @@ -1624,13 +1718,10 @@ encoder_encode_key_value(PyEncoderObject *s, PyUnicodeWriter *writer, bool *firs } } - encoded = encoder_encode_string(s, keystr); + rv = encoder_write_string(s, writer, keystr); Py_DECREF(keystr); - if (encoded == NULL) { - return -1; - } - if (_steal_accumulate(writer, encoded) < 0) { + if (rv < 0) { return -1; } if (PyUnicodeWriter_WriteStr(writer, s->key_separator) < 0) { @@ -1643,15 +1734,72 @@ encoder_encode_key_value(PyEncoderObject *s, PyUnicodeWriter *writer, bool *firs return 0; } +static inline int +_encoder_iterate_mapping_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, + bool *first, PyObject *dct, PyObject *items, + Py_ssize_t indent_level, PyObject *indent_cache, + PyObject *separator) +{ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(items); + PyObject *key, *value; + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(items); i++) { + PyObject *item = PyList_GET_ITEM(items, i); + // gh-142831: encoder_encode_key_value() can invoke user code + // that mutates the items list, invalidating this borrowed ref. + Py_INCREF(item); + if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) { + PyErr_SetString(PyExc_ValueError, "items must return 2-tuples"); + Py_DECREF(item); + return -1; + } + + key = PyTuple_GET_ITEM(item, 0); + value = PyTuple_GET_ITEM(item, 1); + if (encoder_encode_key_value(s, writer, first, dct, key, value, + indent_level, indent_cache, + separator) < 0) { + Py_DECREF(item); + return -1; + } + Py_DECREF(item); + } + + return 0; +} + +static inline int +_encoder_iterate_dict_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, + bool *first, PyObject *dct, Py_ssize_t indent_level, + PyObject *indent_cache, PyObject *separator) +{ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(dct); + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(dct, &pos, &key, &value)) { + // gh-145244: encoder_encode_key_value() can invoke user code + // that mutates the dict, invalidating these borrowed refs. + Py_INCREF(key); + Py_INCREF(value); + if (encoder_encode_key_value(s, writer, first, dct, key, value, + indent_level, indent_cache, + separator) < 0) { + Py_DECREF(key); + Py_DECREF(value); + return -1; + } + Py_DECREF(key); + Py_DECREF(value); + } + return 0; +} + static int encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, - PyObject *dct, + PyObject *dct, Py_ssize_t indent_level, PyObject *indent_cache) { /* Encode Python dict dct a JSON term */ PyObject *ident = NULL; - PyObject *items = NULL; - PyObject *key, *value; bool first = true; if (PyDict_GET_SIZE(dct) == 0) { @@ -1687,35 +1835,31 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, goto bail; } - if (s->sort_keys || !PyDict_CheckExact(dct)) { - items = PyMapping_Items(dct); - if (items == NULL || (s->sort_keys && PyList_Sort(items) < 0)) + if (s->sort_keys || !PyAnyDict_CheckExact(dct)) { + PyObject *items = PyMapping_Items(dct); + if (items == NULL || (s->sort_keys && PyList_Sort(items) < 0)) { + Py_XDECREF(items); goto bail; + } - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(items); i++) { - PyObject *item = PyList_GET_ITEM(items, i); - - if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) { - PyErr_SetString(PyExc_ValueError, "items must return 2-tuples"); - goto bail; - } - - key = PyTuple_GET_ITEM(item, 0); - value = PyTuple_GET_ITEM(item, 1); - if (encoder_encode_key_value(s, writer, &first, dct, key, value, - indent_level, indent_cache, - separator) < 0) - goto bail; + int result; + Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(items); + result = _encoder_iterate_mapping_lock_held(s, writer, &first, dct, + items, indent_level, indent_cache, separator); + Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); + Py_DECREF(items); + if (result < 0) { + goto bail; } - Py_CLEAR(items); } else { - Py_ssize_t pos = 0; - while (PyDict_Next(dct, &pos, &key, &value)) { - if (encoder_encode_key_value(s, writer, &first, dct, key, value, - indent_level, indent_cache, - separator) < 0) - goto bail; + int result; + Py_BEGIN_CRITICAL_SECTION(dct); + result = _encoder_iterate_dict_lock_held(s, writer, &first, dct, + indent_level, indent_cache, separator); + Py_END_CRITICAL_SECTION(); + if (result < 0) { + goto bail; } } @@ -1737,11 +1881,36 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, return 0; bail: - Py_XDECREF(items); Py_XDECREF(ident); return -1; } +static inline int +_encoder_iterate_fast_seq_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer, + PyObject *seq, PyObject *s_fast, + Py_ssize_t indent_level, PyObject *indent_cache, PyObject *separator) +{ + for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(s_fast); i++) { + PyObject *obj = PySequence_Fast_GET_ITEM(s_fast, i); + // gh-142831: encoder_listencode_obj() can invoke user code + // that mutates the sequence, invalidating this borrowed ref. + Py_INCREF(obj); + if (i) { + if (PyUnicodeWriter_WriteStr(writer, separator) < 0) { + Py_DECREF(obj); + return -1; + } + } + if (encoder_listencode_obj(s, writer, obj, indent_level, indent_cache)) { + _PyErr_FormatNote("when serializing %T item %zd", seq, i); + Py_DECREF(obj); + return -1; + } + Py_DECREF(obj); + } + return 0; +} + static int encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, PyObject *seq, @@ -1749,10 +1918,8 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, { PyObject *ident = NULL; PyObject *s_fast = NULL; - Py_ssize_t i; - ident = NULL; - s_fast = PySequence_Fast(seq, "_iterencode_list needs a sequence"); + s_fast = PySequence_Fast(seq, "encoder_listencode_list needs a sequence"); if (s_fast == NULL) return -1; if (PySequence_Fast_GET_SIZE(s_fast) == 0) { @@ -1790,16 +1957,13 @@ encoder_listencode_list(PyEncoderObject *s, PyUnicodeWriter *writer, goto bail; } } - for (i = 0; i < PySequence_Fast_GET_SIZE(s_fast); i++) { - PyObject *obj = PySequence_Fast_GET_ITEM(s_fast, i); - if (i) { - if (PyUnicodeWriter_WriteStr(writer, separator) < 0) - goto bail; - } - if (encoder_listencode_obj(s, writer, obj, indent_level, indent_cache)) { - _PyErr_FormatNote("when serializing %T item %zd", seq, i); - goto bail; - } + int result; + Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(seq); + result = _encoder_iterate_fast_seq_lock_held(s, writer, seq, s_fast, + indent_level, indent_cache, separator); + Py_END_CRITICAL_SECTION_SEQUENCE_FAST(); + if (result < 0) { + goto bail; } if (ident != NULL) { if (PyDict_DelItem(s->markers, ident)) @@ -1887,18 +2051,9 @@ static PyType_Spec PyEncoderType_spec = { }; static PyMethodDef speedups_methods[] = { - {"encode_basestring_ascii", - py_encode_basestring_ascii, - METH_O, - pydoc_encode_basestring_ascii}, - {"encode_basestring", - py_encode_basestring, - METH_O, - pydoc_encode_basestring}, - {"scanstring", - py_scanstring, - METH_VARARGS, - pydoc_scanstring}, + PY_ENCODE_BASESTRING_ASCII_METHODDEF + PY_ENCODE_BASESTRING_METHODDEF + PY_SCANSTRING_METHODDEF {NULL, NULL, 0, NULL} }; @@ -1922,6 +2077,7 @@ _json_exec(PyObject *module) } static PyModuleDef_Slot _json_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _json_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index 41e6d48b1dbd9b..8f7d662b00b21b 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -87,6 +87,41 @@ copy_grouping(const char* s) return result; } +#if defined(MS_WINDOWS) + +// 16 is the number of elements in the szCodePage field +// of the __crt_locale_strings structure. +#define MAX_CP_LEN 15 + +static int +check_locale_name(const char *locale, const char *end) +{ + size_t len = end ? (size_t)(end - locale) : strlen(locale); + const char *dot = memchr(locale, '.', len); + if (dot && locale + len - dot - 1 > MAX_CP_LEN) { + return -1; + } + return 0; +} + +static int +check_locale_name_all(const char *locale) +{ + const char *start = locale; + while (1) { + const char *end = strchr(start, ';'); + if (check_locale_name(start, end) < 0) { + return -1; + } + if (end == NULL) { + break; + } + start = end + 1; + } + return 0; +} +#endif + /*[clinic input] _locale.setlocale @@ -111,6 +146,18 @@ _locale_setlocale_impl(PyObject *module, int category, const char *locale) "invalid locale category"); return NULL; } + if (locale) { + if ((category == LC_ALL + ? check_locale_name_all(locale) + : check_locale_name(locale, NULL)) < 0) + { + /* Debug assertion failure on Windows. + * _Py_BEGIN_SUPPRESS_IPH/_Py_END_SUPPRESS_IPH do not help. */ + PyErr_SetString(get_locale_state(module)->Error, + "unsupported locale setting"); + return NULL; + } + } #endif if (locale) { @@ -408,35 +455,72 @@ _locale_strxfrm_impl(PyObject *module, PyObject *str) goto exit; } - /* assume no change in size, first */ - n1 = n1 + 1; - buf = PyMem_New(wchar_t, n1); + errno = 0; + n2 = wcsxfrm(NULL, s, 0); + if (errno && errno != ERANGE) { + PyErr_SetFromErrno(PyExc_OSError); + goto exit; + } + buf = PyMem_New(wchar_t, n2+1); if (!buf) { PyErr_NoMemory(); goto exit; } + errno = 0; - n2 = wcsxfrm(buf, s, n1); - if (errno && errno != ERANGE) { + n2 = wcsxfrm(buf, s, n2+1); + if (errno) { PyErr_SetFromErrno(PyExc_OSError); goto exit; } - if (n2 >= (size_t)n1) { - /* more space needed */ - wchar_t * new_buf = PyMem_Realloc(buf, (n2+1)*sizeof(wchar_t)); - if (!new_buf) { - PyErr_NoMemory(); - goto exit; + /* The result is just a sequence of integers, they are not necessary + Unicode code points, so PyUnicode_FromWideChar() cannot be used + here. For example, 0xD83D 0xDC0D should not be larger than 0xFF41. + */ +#if SIZEOF_WCHAR_T == 4 + { + /* Some codes can exceed the range of Unicode code points + (0 - 0x10FFFF), so they cannot be directly used in + PyUnicode_FromKindAndData(). They should be first encoded in + a way that preserves the lexicographical order. + + Codes in the range 0-0xFFFF represent themself. + Codes larger than 0xFFFF are encoded as a pair: + * 0x1xxxx -- the highest 16 bits + * 0x0xxxx -- the lowest 16 bits + */ + size_t n3 = 0; + for (size_t i = 0; i < n2; i++) { + if ((Py_UCS4)buf[i] > 0x10000u) { + n3++; + } } - buf = new_buf; - errno = 0; - n2 = wcsxfrm(buf, s, n2+1); - if (errno) { - PyErr_SetFromErrno(PyExc_OSError); + if (n3) { + n3 += n2; // no integer overflow + Py_UCS4 *buf2 = PyMem_New(Py_UCS4, n3); + if (buf2 == NULL) { + PyErr_NoMemory(); + goto exit; + } + size_t j = 0; + for (size_t i = 0; i < n2; i++) { + Py_UCS4 c = (Py_UCS4)buf[i]; + if (c > 0x10000u) { + buf2[j++] = (c >> 16) | 0x10000u; + buf2[j++] = c & 0xFFFFu; + } + else { + buf2[j++] = c; + } + } + assert(j == n3); + result = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf2, n3); + PyMem_Free(buf2); goto exit; } } - result = PyUnicode_FromWideChar(buf, n2); +#endif + result = PyUnicode_FromKindAndData(sizeof(wchar_t), buf, n2); exit: PyMem_Free(buf); PyMem_Free(s); @@ -471,19 +555,7 @@ _locale__getdefaultlocale_impl(PyObject *module) return Py_BuildValue("ss", locale, encoding); } - /* If we end up here, this windows version didn't know about - ISO639/ISO3166 names (it's probably Windows 95). Return the - Windows language identifier instead (a hexadecimal number) */ - - locale[0] = '0'; - locale[1] = 'x'; - if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTLANGUAGE, - locale+2, sizeof(locale)-2)) { - return Py_BuildValue("ss", locale, encoding); - } - /* cannot determine the language code (very unlikely) */ - Py_INCREF(Py_None); return Py_BuildValue("Os", Py_None, encoding); } #endif @@ -980,6 +1052,7 @@ _locale_exec(PyObject *module) } static struct PyModuleDef_Slot _locale_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _locale_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index d0074b2a0d1f4d..3d341a336ab22e 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -6,7 +6,7 @@ #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_ceval.h" // _PyEval_SetProfile() #include "pycore_pystate.h" // _PyThreadState_GET() -#include "pycore_time.h" // _PyTime_FromLong() +#include "pycore_time.h" // _PyTime_FromSecondsObject() #include "pycore_typeobject.h" // _PyType_GetModuleState() #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() @@ -111,7 +111,7 @@ static PyTime_t CallExternalTimer(ProfilerObject *pObj) if (pObj->externalTimerUnit > 0.0) { /* interpret the result as an integer that will be scaled in profiler_getstats() */ - err = _PyTime_FromLong(&result, o); + err = PyLong_AsInt64(o, &result); } else { /* interpret the result as a double measured in seconds. @@ -292,9 +292,10 @@ static void clearEntries(ProfilerObject *pObj) RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL); pObj->profilerEntries = EMPTY_ROTATING_TREE; /* release the memory hold by the ProfilerContexts */ - if (pObj->currentProfilerContext) { - PyMem_Free(pObj->currentProfilerContext); - pObj->currentProfilerContext = NULL; + while (pObj->currentProfilerContext) { + ProfilerContext *pContext = pObj->currentProfilerContext; + pObj->currentProfilerContext = pContext->previous; + PyMem_Free(pContext); } while (pObj->freelistProfilerContext) { ProfilerContext *c = pObj->freelistProfilerContext; @@ -534,6 +535,7 @@ static int statsForEntry(rotating_node_t *node, void *arg) } /*[clinic input] +@critical_section _lsprof.Profiler.getstats cls: defining_class @@ -565,7 +567,7 @@ profiler_subentry objects: static PyObject * _lsprof_Profiler_getstats_impl(ProfilerObject *self, PyTypeObject *cls) -/*[clinic end generated code: output=1806ef720019ee03 input=445e193ef4522902]*/ +/*[clinic end generated code: output=1806ef720019ee03 input=3dc69eb85ed73d91]*/ { statscollector_t collect; collect.state = _PyType_GetModuleState(cls); @@ -613,6 +615,7 @@ setBuiltins(ProfilerObject *pObj, int nvalue) } /*[clinic input] +@critical_section _lsprof.Profiler._pystart_callback code: object @@ -624,7 +627,7 @@ _lsprof.Profiler._pystart_callback static PyObject * _lsprof_Profiler__pystart_callback_impl(ProfilerObject *self, PyObject *code, PyObject *instruction_offset) -/*[clinic end generated code: output=5fec8b7ad5ed25e8 input=b166e6953c579cda]*/ +/*[clinic end generated code: output=5fec8b7ad5ed25e8 input=b61a0e79cf1f8499]*/ { ptrace_enter_call((PyObject*)self, (void *)code, code); @@ -632,6 +635,7 @@ _lsprof_Profiler__pystart_callback_impl(ProfilerObject *self, PyObject *code, } /*[clinic input] +@critical_section _lsprof.Profiler._pythrow_callback code: object @@ -645,7 +649,7 @@ static PyObject * _lsprof_Profiler__pythrow_callback_impl(ProfilerObject *self, PyObject *code, PyObject *instruction_offset, PyObject *exception) -/*[clinic end generated code: output=0a32988919dfb94c input=fd728fc2c074f5e6]*/ +/*[clinic end generated code: output=0a32988919dfb94c input=60c7f272206d3758]*/ { ptrace_enter_call((PyObject*)self, (void *)code, code); @@ -653,6 +657,7 @@ _lsprof_Profiler__pythrow_callback_impl(ProfilerObject *self, PyObject *code, } /*[clinic input] +@critical_section _lsprof.Profiler._pyreturn_callback code: object @@ -667,7 +672,7 @@ _lsprof_Profiler__pyreturn_callback_impl(ProfilerObject *self, PyObject *code, PyObject *instruction_offset, PyObject *retval) -/*[clinic end generated code: output=9e2f6fc1b882c51e input=667ffaeb2fa6fd1f]*/ +/*[clinic end generated code: output=9e2f6fc1b882c51e input=0ddcc1ec53faa928]*/ { ptrace_leave_call((PyObject*)self, (void *)code); @@ -698,11 +703,13 @@ PyObject* get_cfunc_from_callable(PyObject* callable, PyObject* self_arg, PyObje if (PyCFunction_Check(meth)) { return (PyObject*)((PyCFunctionObject *)meth); } + Py_DECREF(meth); } return NULL; } /*[clinic input] +@critical_section _lsprof.Profiler._ccall_callback code: object @@ -717,7 +724,7 @@ static PyObject * _lsprof_Profiler__ccall_callback_impl(ProfilerObject *self, PyObject *code, PyObject *instruction_offset, PyObject *callable, PyObject *self_arg) -/*[clinic end generated code: output=152db83cabd18cad input=0e66687cfb95c001]*/ +/*[clinic end generated code: output=152db83cabd18cad input=2fc1e0630ee5e32b]*/ { if (self->flags & POF_BUILTINS) { PyObject* cfunc = get_cfunc_from_callable(callable, self_arg, self->missing); @@ -733,6 +740,7 @@ _lsprof_Profiler__ccall_callback_impl(ProfilerObject *self, PyObject *code, } /*[clinic input] +@critical_section _lsprof.Profiler._creturn_callback code: object @@ -748,7 +756,7 @@ _lsprof_Profiler__creturn_callback_impl(ProfilerObject *self, PyObject *code, PyObject *instruction_offset, PyObject *callable, PyObject *self_arg) -/*[clinic end generated code: output=1e886dde8fed8fb0 input=b18afe023746923a]*/ +/*[clinic end generated code: output=1e886dde8fed8fb0 input=bdc246d6b5b8714a]*/ { if (self->flags & POF_BUILTINS) { PyObject* cfunc = get_cfunc_from_callable(callable, self_arg, self->missing); @@ -780,6 +788,7 @@ static const struct { /*[clinic input] +@critical_section _lsprof.Profiler.enable subcalls: bool = True @@ -796,7 +805,7 @@ Start collecting profiling information. static PyObject * _lsprof_Profiler_enable_impl(ProfilerObject *self, int subcalls, int builtins) -/*[clinic end generated code: output=1e747f9dc1edd571 input=9ab81405107ab7f1]*/ +/*[clinic end generated code: output=1e747f9dc1edd571 input=0b88115b1c796173]*/ { int all_events = 0; if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0) { @@ -869,6 +878,7 @@ flush_unmatched(ProfilerObject *pObj) /*[clinic input] +@critical_section _lsprof.Profiler.disable Stop collecting profiling information. @@ -876,7 +886,7 @@ Stop collecting profiling information. static PyObject * _lsprof_Profiler_disable_impl(ProfilerObject *self) -/*[clinic end generated code: output=838cffef7f651870 input=05700b3fc68d1f50]*/ +/*[clinic end generated code: output=838cffef7f651870 input=f7e4787cae20f7f6]*/ { if (self->flags & POF_EXT_TIMER) { PyErr_SetString(PyExc_RuntimeError, @@ -928,6 +938,7 @@ _lsprof_Profiler_disable_impl(ProfilerObject *self) } /*[clinic input] +@critical_section _lsprof.Profiler.clear Clear all profiling information collected so far. @@ -935,7 +946,7 @@ Clear all profiling information collected so far. static PyObject * _lsprof_Profiler_clear_impl(ProfilerObject *self) -/*[clinic end generated code: output=dd1c668fb84b1335 input=fbe1f88c28be4f98]*/ +/*[clinic end generated code: output=dd1c668fb84b1335 input=4aab219d5d7a9bec]*/ { if (self->flags & POF_EXT_TIMER) { PyErr_SetString(PyExc_RuntimeError, @@ -952,6 +963,8 @@ profiler_traverse(PyObject *op, visitproc visit, void *arg) ProfilerObject *self = ProfilerObject_CAST(op); Py_VISIT(Py_TYPE(op)); Py_VISIT(self->externalTimer); + Py_VISIT(self->missing); + return 0; } @@ -970,6 +983,7 @@ profiler_dealloc(PyObject *op) flush_unmatched(self); clearEntries(self); + Py_XDECREF(self->missing); Py_XDECREF(self->externalTimer); PyTypeObject *tp = Py_TYPE(self); tp->tp_free(self); @@ -1008,7 +1022,7 @@ profiler_init_impl(ProfilerObject *self, PyObject *timer, double timeunit, if (!monitoring) { return -1; } - self->missing = PyObject_GetAttrString(monitoring, "MISSING"); + Py_XSETREF(self->missing, PyObject_GetAttrString(monitoring, "MISSING")); if (!self->missing) { Py_DECREF(monitoring); return -1; @@ -1112,6 +1126,7 @@ _lsprof_exec(PyObject *module) } static PyModuleDef_Slot _lsprofslots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _lsprof_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index 462c2181fa6036..4335a8bb162414 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -20,11 +20,25 @@ #include "pycore_long.h" // _PyLong_UInt32_Converter() // Blocks output buffer wrappers #include "pycore_blocks_output_buffer.h" +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_*_RELAXED #if OUTPUT_BUFFER_MAX_BLOCK_SIZE > SIZE_MAX #error "The maximum block size accepted by liblzma is SIZE_MAX." #endif + +/* + * If the lzma.h we're building against is so old as not to define these, this + * provides their equivalent values so that the names remain defined in Python + * regardless of the header versions used at build time. + */ +#ifndef LZMA_FILTER_ARM64 +#define LZMA_FILTER_ARM64 LZMA_VLI_C(0x0A) +#endif +#ifndef LZMA_FILTER_RISCV +#define LZMA_FILTER_RISCV LZMA_VLI_C(0x0B) +#endif + /* On success, return value >= 0 On failure, return -1 */ static inline Py_ssize_t @@ -72,13 +86,6 @@ OutputBuffer_OnError(_BlocksOutputBuffer *buffer) } -#define ACQUIRE_LOCK(obj) do { \ - if (!PyThread_acquire_lock((obj)->lock, 0)) { \ - Py_BEGIN_ALLOW_THREADS \ - PyThread_acquire_lock((obj)->lock, 1); \ - Py_END_ALLOW_THREADS \ - } } while (0) -#define RELEASE_LOCK(obj) PyThread_release_lock((obj)->lock) typedef struct { PyTypeObject *lzma_compressor_type; @@ -111,7 +118,7 @@ typedef struct { lzma_allocator alloc; lzma_stream lzs; int flushed; - PyThread_type_lock lock; + PyMutex mutex; } Compressor; typedef struct { @@ -124,7 +131,7 @@ typedef struct { char needs_input; uint8_t *input_buffer; size_t input_buffer_size; - PyThread_type_lock lock; + PyMutex mutex; } Decompressor; #define Compressor_CAST(op) ((Compressor *)(op)) @@ -379,6 +386,8 @@ lzma_filter_converter(_lzma_state *state, PyObject *spec, void *ptr) case LZMA_FILTER_ARM: case LZMA_FILTER_ARMTHUMB: case LZMA_FILTER_SPARC: + case LZMA_FILTER_ARM64: + case LZMA_FILTER_RISCV: f->options = parse_filter_spec_bcj(state, spec); return f->options != NULL; default: @@ -497,7 +506,9 @@ build_filter_spec(const lzma_filter *f) case LZMA_FILTER_IA64: case LZMA_FILTER_ARM: case LZMA_FILTER_ARMTHUMB: - case LZMA_FILTER_SPARC: { + case LZMA_FILTER_SPARC: + case LZMA_FILTER_ARM64: + case LZMA_FILTER_RISCV: { lzma_options_bcj *options = f->options; if (options) { ADD_FIELD(options, start_offset); @@ -554,7 +565,7 @@ static PyObject * compress(Compressor *c, uint8_t *data, size_t len, lzma_action action) { PyObject *result; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; _lzma_state *state = PyType_GetModuleState(Py_TYPE(c)); assert(state != NULL); @@ -617,14 +628,14 @@ _lzma_LZMACompressor_compress_impl(Compressor *self, Py_buffer *data) { PyObject *result = NULL; - ACQUIRE_LOCK(self); + PyMutex_Lock(&self->mutex); if (self->flushed) { PyErr_SetString(PyExc_ValueError, "Compressor has been flushed"); } else { result = compress(self, data->buf, data->len, LZMA_RUN); } - RELEASE_LOCK(self); + PyMutex_Unlock(&self->mutex); return result; } @@ -644,14 +655,14 @@ _lzma_LZMACompressor_flush_impl(Compressor *self) { PyObject *result = NULL; - ACQUIRE_LOCK(self); + PyMutex_Lock(&self->mutex); if (self->flushed) { PyErr_SetString(PyExc_ValueError, "Repeated call to flush()"); } else { self->flushed = 1; result = compress(self, NULL, 0, LZMA_FINISH); } - RELEASE_LOCK(self); + PyMutex_Unlock(&self->mutex); return result; } @@ -820,12 +831,7 @@ Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) self->alloc.free = PyLzma_Free; self->lzs.allocator = &self->alloc; - self->lock = PyThread_allocate_lock(); - if (self->lock == NULL) { - Py_DECREF(self); - PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); - return NULL; - } + self->mutex = (PyMutex){0}; self->flushed = 0; switch (format) { @@ -867,10 +873,8 @@ static void Compressor_dealloc(PyObject *op) { Compressor *self = Compressor_CAST(op); + assert(!PyMutex_IsLocked(&self->mutex)); lzma_end(&self->lzs); - if (self->lock != NULL) { - PyThread_free_lock(self->lock); - } PyTypeObject *tp = Py_TYPE(self); tp->tp_free(self); Py_DECREF(tp); @@ -882,13 +886,6 @@ static PyMethodDef Compressor_methods[] = { {NULL} }; -static int -Compressor_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - PyDoc_STRVAR(Compressor_doc, "LZMACompressor(format=FORMAT_XZ, check=-1, preset=None, filters=None)\n" "\n" @@ -922,7 +919,6 @@ static PyType_Slot lzma_compressor_type_slots[] = { {Py_tp_methods, Compressor_methods}, {Py_tp_new, Compressor_new}, {Py_tp_doc, (char *)Compressor_doc}, - {Py_tp_traverse, Compressor_traverse}, {0, 0} }; @@ -948,7 +944,7 @@ decompress_buf(Decompressor *d, Py_ssize_t max_length) { PyObject *result; lzma_stream *lzs = &d->lzs; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; _lzma_state *state = PyType_GetModuleState(Py_TYPE(d)); assert(state != NULL); @@ -970,10 +966,10 @@ decompress_buf(Decompressor *d, Py_ssize_t max_length) goto error; } if (lzret == LZMA_GET_CHECK || lzret == LZMA_NO_CHECK) { - d->check = lzma_get_check(&d->lzs); + FT_ATOMIC_STORE_INT_RELAXED(d->check, lzma_get_check(&d->lzs)); } if (lzret == LZMA_STREAM_END) { - d->eof = 1; + FT_ATOMIC_STORE_CHAR_RELAXED(d->eof, 1); break; } else if (lzs->avail_out == 0) { /* Need to check lzs->avail_out before lzs->avail_in. @@ -1060,13 +1056,14 @@ decompress(Decompressor *d, uint8_t *data, size_t len, Py_ssize_t max_length) } if (d->eof) { - d->needs_input = 0; + FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 0); if (lzs->avail_in > 0) { - Py_XSETREF(d->unused_data, - PyBytes_FromStringAndSize((char *)lzs->next_in, lzs->avail_in)); - if (d->unused_data == NULL) { + PyObject *unused_data = PyBytes_FromStringAndSize( + (char *)lzs->next_in, lzs->avail_in); + if (unused_data == NULL) { goto error; } + Py_XSETREF(d->unused_data, unused_data); } } else if (lzs->avail_in == 0) { @@ -1076,17 +1073,17 @@ decompress(Decompressor *d, uint8_t *data, size_t len, Py_ssize_t max_length) /* (avail_in==0 && avail_out==0) Maybe lzs's internal state still have a few bytes can be output, try to output them next time. */ - d->needs_input = 0; + FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 0); /* If max_length < 0, lzs->avail_out always > 0 */ assert(max_length >= 0); } else { /* Input buffer exhausted, output buffer has space. */ - d->needs_input = 1; + FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 1); } } else { - d->needs_input = 0; + FT_ATOMIC_STORE_CHAR_RELAXED(d->needs_input, 0); /* If we did not use the input buffer, we now have to copy the tail from the caller's buffer into the @@ -1120,6 +1117,7 @@ decompress(Decompressor *d, uint8_t *data, size_t len, Py_ssize_t max_length) return result; error: + lzs->next_in = NULL; Py_XDECREF(result); return NULL; } @@ -1132,33 +1130,34 @@ _lzma.LZMADecompressor.decompress Decompress *data*, returning uncompressed data as bytes. -If *max_length* is nonnegative, returns at most *max_length* bytes of -decompressed data. If this limit is reached and further output can be -produced, *self.needs_input* will be set to ``False``. In this case, the next -call to *decompress()* may provide *data* as b'' to obtain more of the output. +If *max_length* is nonnegative, returns at most *max_length* bytes +of decompressed data. If this limit is reached and further output +can be produced, *self.needs_input* will be set to ``False``. In +this case, the next call to *decompress()* may provide *data* as b'' +to obtain more of the output. -If all of the input data was decompressed and returned (either because this -was less than *max_length* bytes, or because *max_length* was negative), -*self.needs_input* will be set to True. +If all of the input data was decompressed and returned (either +because this was less than *max_length* bytes, or because +*max_length* was negative), *self.needs_input* will be set to True. -Attempting to decompress data after the end of stream is reached raises an -EOFError. Any data found after the end of the stream is ignored and saved in -the unused_data attribute. +Attempting to decompress data after the end of stream is reached +raises an EOFError. Any data found after the end of the stream is +ignored and saved in the unused_data attribute. [clinic start generated code]*/ static PyObject * _lzma_LZMADecompressor_decompress_impl(Decompressor *self, Py_buffer *data, Py_ssize_t max_length) -/*[clinic end generated code: output=ef4e20ec7122241d input=60c1f135820e309d]*/ +/*[clinic end generated code: output=ef4e20ec7122241d input=0eb62669c4315dee]*/ { PyObject *result = NULL; - ACQUIRE_LOCK(self); + PyMutex_Lock(&self->mutex); if (self->eof) PyErr_SetString(PyExc_EOFError, "Already at end of stream"); else result = decompress(self, data->buf, data->len, max_length); - RELEASE_LOCK(self); + PyMutex_Unlock(&self->mutex); return result; } @@ -1251,21 +1250,13 @@ _lzma_LZMADecompressor_impl(PyTypeObject *type, int format, self->lzs.allocator = &self->alloc; self->lzs.next_in = NULL; - self->lock = PyThread_allocate_lock(); - if (self->lock == NULL) { - Py_DECREF(self); - PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); - return NULL; - } + self->mutex = (PyMutex){0}; self->check = LZMA_CHECK_UNKNOWN; self->needs_input = 1; self->input_buffer = NULL; self->input_buffer_size = 0; - Py_XSETREF(self->unused_data, PyBytes_FromStringAndSize(NULL, 0)); - if (self->unused_data == NULL) { - goto error; - } + Py_XSETREF(self->unused_data, Py_GetConstant(Py_CONSTANT_EMPTY_BYTES)); switch (format) { case FORMAT_AUTO: @@ -1314,26 +1305,18 @@ static void Decompressor_dealloc(PyObject *op) { Decompressor *self = Decompressor_CAST(op); + assert(!PyMutex_IsLocked(&self->mutex)); + if(self->input_buffer != NULL) PyMem_Free(self->input_buffer); lzma_end(&self->lzs); Py_CLEAR(self->unused_data); - if (self->lock != NULL) { - PyThread_free_lock(self->lock); - } PyTypeObject *tp = Py_TYPE(self); tp->tp_free(self); Py_DECREF(tp); } -static int -Decompressor_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static PyMethodDef Decompressor_methods[] = { _LZMA_LZMADECOMPRESSOR_DECOMPRESS_METHODDEF {NULL} @@ -1351,6 +1334,26 @@ PyDoc_STRVAR(Decompressor_needs_input_doc, PyDoc_STRVAR(Decompressor_unused_data_doc, "Data found after the end of the compressed stream."); +static PyObject * +Decompressor_unused_data_get(PyObject *op, void *Py_UNUSED(closure)) +{ + Decompressor *self = Decompressor_CAST(op); + if (!FT_ATOMIC_LOAD_CHAR_RELAXED(self->eof)) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } + PyMutex_Lock(&self->mutex); + assert(self->unused_data != NULL); + PyObject *result = Py_NewRef(self->unused_data); + PyMutex_Unlock(&self->mutex); + return result; +} + +static PyGetSetDef Decompressor_getset[] = { + {"unused_data", Decompressor_unused_data_get, NULL, + Decompressor_unused_data_doc}, + {NULL}, +}; + static PyMemberDef Decompressor_members[] = { {"check", Py_T_INT, offsetof(Decompressor, check), Py_READONLY, Decompressor_check_doc}, @@ -1358,8 +1361,6 @@ static PyMemberDef Decompressor_members[] = { Decompressor_eof_doc}, {"needs_input", Py_T_BOOL, offsetof(Decompressor, needs_input), Py_READONLY, Decompressor_needs_input_doc}, - {"unused_data", Py_T_OBJECT_EX, offsetof(Decompressor, unused_data), Py_READONLY, - Decompressor_unused_data_doc}, {NULL} }; @@ -1368,8 +1369,8 @@ static PyType_Slot lzma_decompressor_type_slots[] = { {Py_tp_methods, Decompressor_methods}, {Py_tp_new, _lzma_LZMADecompressor}, {Py_tp_doc, (char *)_lzma_LZMADecompressor__doc__}, - {Py_tp_traverse, Decompressor_traverse}, {Py_tp_members, Decompressor_members}, + {Py_tp_getset, Decompressor_getset}, {0, 0} }; @@ -1444,7 +1445,7 @@ _lzma__encode_filter_properties_impl(PyObject *module, lzma_filter filter) { lzma_ret lzret; uint32_t encoded_size; - PyObject *result = NULL; + PyBytesWriter *writer = NULL; _lzma_state *state = get_lzma_state(module); assert(state != NULL); @@ -1452,25 +1453,26 @@ _lzma__encode_filter_properties_impl(PyObject *module, lzma_filter filter) if (catch_lzma_error(state, lzret)) goto error; - result = PyBytes_FromStringAndSize(NULL, encoded_size); - if (result == NULL) + writer = PyBytesWriter_Create(encoded_size); + if (writer == NULL) { goto error; + } - lzret = lzma_properties_encode( - &filter, (uint8_t *)PyBytes_AS_STRING(result)); + lzret = lzma_properties_encode(&filter, PyBytesWriter_GetData(writer)); if (catch_lzma_error(state, lzret)) { goto error; } - return result; + return PyBytesWriter_Finish(writer); error: - Py_XDECREF(result); + PyBytesWriter_Discard(writer); return NULL; } /*[clinic input] +@permit_long_summary _lzma._decode_filter_properties filter_id: lzma_vli encoded_props: Py_buffer @@ -1484,7 +1486,7 @@ The result does not include the filter ID itself, only the options. static PyObject * _lzma__decode_filter_properties_impl(PyObject *module, lzma_vli filter_id, Py_buffer *encoded_props) -/*[clinic end generated code: output=714fd2ef565d5c60 input=246410800782160c]*/ +/*[clinic end generated code: output=714fd2ef565d5c60 input=b9750bf909109bbe]*/ { lzma_filter filter; lzma_ret lzret; @@ -1559,6 +1561,8 @@ lzma_exec(PyObject *module) ADD_INT_PREFIX_MACRO(module, FILTER_ARMTHUMB); ADD_INT_PREFIX_MACRO(module, FILTER_SPARC); ADD_INT_PREFIX_MACRO(module, FILTER_POWERPC); + ADD_INT_PREFIX_MACRO(module, FILTER_ARM64); + ADD_INT_PREFIX_MACRO(module, FILTER_RISCV); ADD_INT_PREFIX_MACRO(module, MF_HC3); ADD_INT_PREFIX_MACRO(module, MF_HC4); ADD_INT_PREFIX_MACRO(module, MF_BT2); @@ -1610,6 +1614,7 @@ static PyMethodDef lzma_methods[] = { }; static PyModuleDef_Slot lzma_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, lzma_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_multiprocessing/clinic/posixshmem.c.h b/Modules/_multiprocessing/clinic/posixshmem.c.h index a545ff4d80f067..a4d7273aea718a 100644 --- a/Modules/_multiprocessing/clinic/posixshmem.c.h +++ b/Modules/_multiprocessing/clinic/posixshmem.c.h @@ -50,9 +50,9 @@ PyDoc_STRVAR(_posixshmem_shm_unlink__doc__, "\n" "Remove a shared memory object (similar to unlink()).\n" "\n" -"Remove a shared memory object name, and, once all processes have unmapped\n" -"the object, de-allocates and destroys the contents of the associated memory\n" -"region."); +"Remove a shared memory object name, and, once all processes have\n" +"unmapped the object, de-allocates and destroys the contents of the\n" +"associated memory region."); #define _POSIXSHMEM_SHM_UNLINK_METHODDEF \ {"shm_unlink", (PyCFunction)_posixshmem_shm_unlink, METH_O, _posixshmem_shm_unlink__doc__}, @@ -86,4 +86,4 @@ _posixshmem_shm_unlink(PyObject *module, PyObject *arg) #ifndef _POSIXSHMEM_SHM_UNLINK_METHODDEF #define _POSIXSHMEM_SHM_UNLINK_METHODDEF #endif /* !defined(_POSIXSHMEM_SHM_UNLINK_METHODDEF) */ -/*[clinic end generated code: output=74588a5abba6e36c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e69afacce7b0595e input=a9049054013a1b77]*/ diff --git a/Modules/_multiprocessing/multiprocessing.c b/Modules/_multiprocessing/multiprocessing.c index cee8cf7b9a83c0..201cedbb59818f 100644 --- a/Modules/_multiprocessing/multiprocessing.c +++ b/Modules/_multiprocessing/multiprocessing.c @@ -109,23 +109,22 @@ static PyObject * _multiprocessing_recv_impl(PyObject *module, HANDLE handle, int size) /*[clinic end generated code: output=92322781ba9ff598 input=6a5b0834372cee5b]*/ { - int nread; - PyObject *buf; - - buf = PyBytes_FromStringAndSize(NULL, size); - if (!buf) + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (!writer) { return NULL; + } + char *buf = PyBytesWriter_GetData(writer); + Py_ssize_t nread; Py_BEGIN_ALLOW_THREADS - nread = recv((SOCKET) handle, PyBytes_AS_STRING(buf), size, 0); + nread = recv((SOCKET) handle, buf, size, 0); Py_END_ALLOW_THREADS if (nread < 0) { - Py_DECREF(buf); + PyBytesWriter_Discard(writer); return PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError()); } - _PyBytes_Resize(&buf, nread); - return buf; + return PyBytesWriter_FinishWithSize(writer, nread); } /*[clinic input] @@ -275,6 +274,7 @@ multiprocessing_exec(PyObject *module) } static PyModuleDef_Slot multiprocessing_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, multiprocessing_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_multiprocessing/posixshmem.c b/Modules/_multiprocessing/posixshmem.c index ab45e4136c7d46..22b4af212662b3 100644 --- a/Modules/_multiprocessing/posixshmem.c +++ b/Modules/_multiprocessing/posixshmem.c @@ -81,15 +81,15 @@ _posixshmem.shm_unlink Remove a shared memory object (similar to unlink()). -Remove a shared memory object name, and, once all processes have unmapped -the object, de-allocates and destroys the contents of the associated memory -region. +Remove a shared memory object name, and, once all processes have +unmapped the object, de-allocates and destroys the contents of the +associated memory region. [clinic start generated code]*/ static PyObject * _posixshmem_shm_unlink_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=42f8b23d134b9ff5 input=298369d013dcad63]*/ +/*[clinic end generated code: output=42f8b23d134b9ff5 input=cf7a30ec6503cf78]*/ { int rv; int async_err = 0; diff --git a/Modules/_multiprocessing/semaphore.c b/Modules/_multiprocessing/semaphore.c index a4a2a866ccbfce..85cc0ac70a6563 100644 --- a/Modules/_multiprocessing/semaphore.c +++ b/Modules/_multiprocessing/semaphore.c @@ -8,6 +8,7 @@ */ #include "multiprocessing.h" +#include "pycore_object.h" // _PyObject_VisitType() #ifdef HAVE_SYS_TIME_H # include <sys/time.h> // gettimeofday() @@ -720,13 +721,6 @@ _multiprocessing_SemLock___exit___impl(SemLockObject *self, return _multiprocessing_SemLock_release_impl(self); } -static int -semlock_traverse(PyObject *s, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(s)); - return 0; -} - /* * Semaphore methods */ @@ -773,7 +767,7 @@ static PyType_Slot _PyMp_SemLockType_slots[] = { {Py_tp_members, semlock_members}, {Py_tp_alloc, PyType_GenericAlloc}, {Py_tp_new, _multiprocessing_SemLock}, - {Py_tp_traverse, semlock_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_free, PyObject_GC_Del}, {Py_tp_doc, (void *)PyDoc_STR("Semaphore/Mutex type")}, {0, 0}, diff --git a/Modules/_opcode.c b/Modules/_opcode.c index ef271b6ef566b9..2a34559fd1f437 100644 --- a/Modules/_opcode.c +++ b/Modules/_opcode.c @@ -119,7 +119,7 @@ _opcode_has_const_impl(PyObject *module, int opcode) } /*[clinic input] - +@permit_long_summary _opcode.has_name -> bool opcode: int @@ -129,7 +129,7 @@ Return True if the opcode accesses an attribute by name, False otherwise. static int _opcode_has_name_impl(PyObject *module, int opcode) -/*[clinic end generated code: output=b49a83555c2fa517 input=448aa5e4bcc947ba]*/ +/*[clinic end generated code: output=b49a83555c2fa517 input=8faf669024d97fad]*/ { return IS_VALID_OPCODE(opcode) && OPCODE_HAS_NAME(opcode); } @@ -423,13 +423,11 @@ _opcode_exec(PyObject *m) { if (PyModule_AddIntMacro(m, ENABLE_SPECIALIZATION) < 0) { return -1; } - if (PyModule_AddIntMacro(m, ENABLE_SPECIALIZATION_FT) < 0) { - return -1; - } return 0; } static PyModuleDef_Slot module_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _opcode_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_operator.c b/Modules/_operator.c index 1cc05c39f5dbad..417403dc4c10c1 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -1194,7 +1194,7 @@ itemgetter_reduce(PyObject *op, PyObject *Py_UNUSED(dummy)) itemgetterobject *ig = itemgetterobject_CAST(op); if (ig->nitems == 1) return Py_BuildValue("O(O)", Py_TYPE(ig), ig->item); - return PyTuple_Pack(2, Py_TYPE(ig), ig->item); + return _PyTuple_FromPair((PyObject *)Py_TYPE(ig), ig->item); } PyDoc_STRVAR(reduce_doc, "Return state information for pickling"); @@ -1981,6 +1981,7 @@ operator_exec(PyObject *module) static struct PyModuleDef_Slot operator_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, operator_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_pickle.c b/Modules/_pickle.c index cf3ceb43fb3f3f..a5595be251a738 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -19,7 +19,9 @@ #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_runtime.h" // _Py_ID() #include "pycore_setobject.h" // _PySet_NextEntry() +#include "pycore_symtable.h" // _Py_Mangle() #include "pycore_sysmodule.h" // _PySys_GetSizeOf() +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() #include <stdlib.h> // strtol() @@ -155,6 +157,9 @@ enum { /* Prefetch size when unpickling (disabled on unpeekable streams) */ PREFETCH = 8192 * 16, + /* Data larger that this will be read in chunks, to prevent extreme + overallocation. */ + MIN_READ_BUF_SIZE = 1 << 20, FRAME_SIZE_MIN = 4, FRAME_SIZE_TARGET = 64 * 1024, @@ -413,13 +418,6 @@ typedef struct { #define Pdata_CAST(op) ((Pdata *)(op)) -static int -Pdata_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static void Pdata_dealloc(PyObject *op) { @@ -437,7 +435,7 @@ Pdata_dealloc(PyObject *op) static PyType_Slot pdata_slots[] = { {Py_tp_dealloc, Pdata_dealloc}, - {Py_tp_traverse, Pdata_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {0, NULL}, }; @@ -645,6 +643,7 @@ typedef struct PicklerObject { int fast_nesting; int fix_imports; /* Indicate whether Pickler should fix the name of globals for Python 2.x. */ + int running; /* True when a method of Pickler is executing. */ PyObject *fast_memo; PyObject *buffer_callback; /* Callback for out-of-band buffers, or NULL */ } PicklerObject; @@ -654,10 +653,11 @@ typedef struct UnpicklerObject { Pdata *stack; /* Pickle data stack, store unpickled objects. */ /* The unpickler memo is just an array of PyObject *s. Using a dict - is unnecessary, since the keys are contiguous ints. */ + is unnecessary, since the keys usually are contiguous ints. */ PyObject **memo; size_t memo_size; /* Capacity of the memo array */ size_t memo_len; /* Number of objects in the memo */ + PyObject *memo_dict; /* The backup memo dict for non-continuous keys. */ PyObject *persistent_load; /* persistent_load() method, can be NULL. */ PyObject *persistent_load_attr; /* instance attribute, can be NULL. */ @@ -688,6 +688,8 @@ typedef struct UnpicklerObject { int proto; /* Protocol of the pickle loaded. */ int fix_imports; /* Indicate whether Unpickler should fix the name of globals pickled by Python 2.x. */ + int running; /* True when a method of Unpickler is executing. */ + } UnpicklerObject; typedef struct { @@ -705,6 +707,32 @@ typedef struct { #define PicklerMemoProxyObject_CAST(op) ((PicklerMemoProxyObject *)(op)) #define UnpicklerMemoProxyObject_CAST(op) ((UnpicklerMemoProxyObject *)(op)) +#define BEGIN_USING_PICKLER(SELF, RET) do { \ + if ((SELF)->running) { \ + PyErr_SetString(PyExc_RuntimeError, \ + "Pickler object is already used"); \ + return (RET); \ + } \ + (SELF)->running = 1; \ + } while (0) + +#define END_USING_PICKLER(SELF) do { \ + (SELF)->running = 0; \ + } while (0) + +#define BEGIN_USING_UNPICKLER(SELF, RET) do { \ + if ((SELF)->running) { \ + PyErr_SetString(PyExc_RuntimeError, \ + "Unpickler object is already used"); \ + return (RET); \ + } \ + (SELF)->running = 1; \ + } while (0) + +#define END_USING_UNPICKLER(SELF) do { \ + (SELF)->running = 0; \ + } while (0) + /* Forward declarations */ static int save(PickleState *state, PicklerObject *, PyObject *, int); static int save_reduce(PickleState *, PicklerObject *, PyObject *, PyObject *); @@ -1134,6 +1162,7 @@ _Pickler_New(PickleState *st) self->fast = 0; self->fast_nesting = 0; self->fix_imports = 0; + self->running = 0; self->fast_memo = NULL; self->buffer_callback = NULL; @@ -1254,6 +1283,66 @@ _Unpickler_SkipConsumed(UnpicklerObject *self) static const Py_ssize_t READ_WHOLE_LINE = -1; +/* Don't call it directly: use _Unpickler_ReadInto() */ +static Py_ssize_t +_Unpickler_ReadIntoFromFile(PickleState *state, UnpicklerObject *self, char *buf, + Py_ssize_t n) +{ + assert(n != READ_WHOLE_LINE); + + if (!self->readinto) { + /* readinto() not supported on file-like object, fall back to read() + * and copy into destination buffer (bpo-39681) */ + PyObject* len = PyLong_FromSsize_t(n); + if (len == NULL) { + return -1; + } + PyObject* data = _Pickle_FastCall(self->read, len); + if (data == NULL) { + return -1; + } + if (!PyBytes_Check(data)) { + PyErr_Format(PyExc_ValueError, + "read() returned non-bytes object (%R)", + Py_TYPE(data)); + Py_DECREF(data); + return -1; + } + Py_ssize_t read_size = PyBytes_GET_SIZE(data); + if (read_size < n) { + Py_DECREF(data); + return bad_readline(state); + } + memcpy(buf, PyBytes_AS_STRING(data), n); + Py_DECREF(data); + return n; + } + + /* Call readinto() into user buffer */ + PyObject *buf_obj = PyMemoryView_FromMemory(buf, n, PyBUF_WRITE); + if (buf_obj == NULL) { + return -1; + } + PyObject *read_size_obj = _Pickle_FastCall(self->readinto, buf_obj); + if (read_size_obj == NULL) { + return -1; + } + Py_ssize_t read_size = PyLong_AsSsize_t(read_size_obj); + Py_DECREF(read_size_obj); + + if (read_size < 0) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, + "readinto() returned negative size"); + } + return -1; + } + if (read_size < n) { + return bad_readline(state); + } + return n; +} + /* If reading from a file, we need to only pull the bytes we need, since there may be multiple pickle objects arranged contiguously in the same input buffer. @@ -1269,7 +1358,7 @@ static const Py_ssize_t READ_WHOLE_LINE = -1; causing the Unpickler to go back to the file for more data. Use the returned size to tell you how much data you can process. */ static Py_ssize_t -_Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) +_Unpickler_ReadFromFile(PickleState *state, UnpicklerObject *self, Py_ssize_t n) { PyObject *data; Py_ssize_t read_size; @@ -1281,6 +1370,9 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) if (n == READ_WHOLE_LINE) { data = PyObject_CallNoArgs(self->readline); + if (data == NULL) { + return -1; + } } else { PyObject *len; @@ -1309,13 +1401,29 @@ _Unpickler_ReadFromFile(UnpicklerObject *self, Py_ssize_t n) return n; } } - len = PyLong_FromSsize_t(n); + Py_ssize_t cursize = Py_MIN(n, MIN_READ_BUF_SIZE); + len = PyLong_FromSsize_t(cursize); if (len == NULL) return -1; data = _Pickle_FastCall(self->read, len); + if (data == NULL) { + return -1; + } + while (cursize < n) { + Py_ssize_t prevsize = cursize; + // geometrically double the chunk size to avoid CPU DoS + cursize += Py_MIN(cursize, n - cursize); + if (_PyBytes_Resize(&data, cursize) < 0) { + return -1; + } + if (_Unpickler_ReadIntoFromFile(state, self, + PyBytes_AS_STRING(data) + prevsize, cursize - prevsize) < 0) + { + Py_DECREF(data); + return -1; + } + } } - if (data == NULL) - return -1; read_size = _Unpickler_SetStringInput(self, data); Py_DECREF(data); @@ -1342,7 +1450,7 @@ _Unpickler_ReadImpl(UnpicklerObject *self, PickleState *st, char **s, Py_ssize_t return bad_readline(st); /* Extend the buffer to satisfy desired size */ - num_read = _Unpickler_ReadFromFile(self, n); + num_read = _Unpickler_ReadFromFile(st, self, n); if (num_read < 0) return -1; if (num_read < n) @@ -1389,57 +1497,7 @@ _Unpickler_ReadInto(PickleState *state, UnpicklerObject *self, char *buf, return -1; } - if (!self->readinto) { - /* readinto() not supported on file-like object, fall back to read() - * and copy into destination buffer (bpo-39681) */ - PyObject* len = PyLong_FromSsize_t(n); - if (len == NULL) { - return -1; - } - PyObject* data = _Pickle_FastCall(self->read, len); - if (data == NULL) { - return -1; - } - if (!PyBytes_Check(data)) { - PyErr_Format(PyExc_ValueError, - "read() returned non-bytes object (%R)", - Py_TYPE(data)); - Py_DECREF(data); - return -1; - } - Py_ssize_t read_size = PyBytes_GET_SIZE(data); - if (read_size < n) { - Py_DECREF(data); - return bad_readline(state); - } - memcpy(buf, PyBytes_AS_STRING(data), n); - Py_DECREF(data); - return n; - } - - /* Call readinto() into user buffer */ - PyObject *buf_obj = PyMemoryView_FromMemory(buf, n, PyBUF_WRITE); - if (buf_obj == NULL) { - return -1; - } - PyObject *read_size_obj = _Pickle_FastCall(self->readinto, buf_obj); - if (read_size_obj == NULL) { - return -1; - } - Py_ssize_t read_size = PyLong_AsSsize_t(read_size_obj); - Py_DECREF(read_size_obj); - - if (read_size < 0) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_ValueError, - "readinto() returned negative size"); - } - return -1; - } - if (read_size < n) { - return bad_readline(state); - } - return n; + return _Unpickler_ReadIntoFromFile(state, self, buf, n); } /* Read `n` bytes from the unpickler's data source, storing the result in `*s`. @@ -1499,7 +1557,7 @@ _Unpickler_Readline(PickleState *state, UnpicklerObject *self, char **result) if (!self->read) return bad_readline(state); - num_read = _Unpickler_ReadFromFile(self, READ_WHOLE_LINE); + num_read = _Unpickler_ReadFromFile(state, self, READ_WHOLE_LINE); if (num_read < 0) return -1; if (num_read == 0 || self->input_buffer[num_read - 1] != '\n') @@ -1532,12 +1590,35 @@ _Unpickler_ResizeMemoList(UnpicklerObject *self, size_t new_size) /* Returns NULL if idx is out of bounds. */ static PyObject * -_Unpickler_MemoGet(UnpicklerObject *self, size_t idx) +_Unpickler_MemoGet(PickleState *st, UnpicklerObject *self, size_t idx) { - if (idx >= self->memo_size) - return NULL; - - return self->memo[idx]; + PyObject *value; + if (idx < self->memo_size) { + value = self->memo[idx]; + if (value != NULL) { + return value; + } + } + if (self->memo_dict != NULL) { + PyObject *key = PyLong_FromSize_t(idx); + if (key == NULL) { + return NULL; + } + if (idx < self->memo_size) { + (void)PyDict_Pop(self->memo_dict, key, &value); + // Migrate dict entry to array for faster future access + self->memo[idx] = value; + } + else { + value = PyDict_GetItemWithError(self->memo_dict, key); + } + Py_DECREF(key); + if (value != NULL || PyErr_Occurred()) { + return value; + } + } + PyErr_Format(st->UnpicklingError, "Memo value not found at index %zd", idx); + return NULL; } /* Returns -1 (with an exception set) on failure, 0 on success. @@ -1548,6 +1629,27 @@ _Unpickler_MemoPut(UnpicklerObject *self, size_t idx, PyObject *value) PyObject *old_item; if (idx >= self->memo_size) { + if (idx > self->memo_len * 2) { + /* The memo keys are too sparse. Use a dict instead of + * a continuous array for the memo. */ + if (self->memo_dict == NULL) { + self->memo_dict = PyDict_New(); + if (self->memo_dict == NULL) { + return -1; + } + } + PyObject *key = PyLong_FromSize_t(idx); + if (key == NULL) { + return -1; + } + + if (PyDict_SetItem(self->memo_dict, key, value) < 0) { + Py_DECREF(key); + return -1; + } + Py_DECREF(key); + return 0; + } if (_Unpickler_ResizeMemoList(self, idx * 2) < 0) return -1; assert(idx < self->memo_size); @@ -1617,6 +1719,7 @@ _Unpickler_New(PyObject *module) self->memo = memo; self->memo_size = MEMO_SIZE; self->memo_len = 0; + self->memo_dict = NULL; self->persistent_load = NULL; self->persistent_load_attr = NULL; memset(&self->buffer, 0, sizeof(Py_buffer)); @@ -1637,6 +1740,7 @@ _Unpickler_New(PyObject *module) self->marks_size = 0; self->proto = 0; self->fix_imports = 0; + self->running = 0; PyObject_GC_Track(self); return self; @@ -1826,6 +1930,37 @@ get_dotted_path(PyObject *name) return PyUnicode_Split(name, _Py_LATIN1_CHR('.'), -1); } +static PyObject * +join_dotted_path(PyObject *dotted_path) +{ + return PyUnicode_Join(_Py_LATIN1_CHR('.'), dotted_path); +} + +/* Returns -1 (with an exception set) on error, 0 if there were no changes, + * 1 if some names were mangled. */ +static int +mangle_dotted_path(PyObject *dotted_path) +{ + int rc = 0; + Py_ssize_t n = PyList_GET_SIZE(dotted_path); + for (Py_ssize_t i = n-1; i > 0; i--) { + PyObject *subpath = PyList_GET_ITEM(dotted_path, i); + if (_Py_IsPrivateName(subpath)) { + PyObject *parent = PyList_GET_ITEM(dotted_path, i-1); + PyObject *mangled = _Py_Mangle(parent, subpath); + if (mangled == NULL) { + return -1; + } + if (mangled != subpath) { + rc = 1; + } + PyList_SET_ITEM(dotted_path, i, mangled); + Py_DECREF(subpath); + } + } + return rc; +} + static int check_dotted_path(PickleState *st, PyObject *obj, PyObject *dotted_path) { @@ -1920,22 +2055,34 @@ whichmodule(PickleState *st, PyObject *global, PyObject *global_name, PyObject * return NULL; } if (PyDict_CheckExact(modules)) { + PyObject *found_name = NULL; + int error = 0; i = 0; + Py_BEGIN_CRITICAL_SECTION(modules); while (PyDict_Next(modules, &i, &module_name, &module)) { Py_INCREF(module_name); Py_INCREF(module); if (_checkmodule(module_name, module, global, dotted_path) == 0) { Py_DECREF(module); - Py_DECREF(modules); - return module_name; + found_name = module_name; + break; } Py_DECREF(module); Py_DECREF(module_name); if (PyErr_Occurred()) { - Py_DECREF(modules); - return NULL; + error = 1; + break; } } + Py_END_CRITICAL_SECTION(); + if (error) { + Py_DECREF(modules); + return NULL; + } + if (found_name != NULL) { + Py_DECREF(modules); + return found_name; + } } else { PyObject *iterator = PyObject_GetIter(modules); @@ -2565,53 +2712,61 @@ save_picklebuffer(PickleState *st, PicklerObject *self, PyObject *obj) "PickleBuffer can only be pickled with protocol >= 5"); return -1; } - const Py_buffer* view = PyPickleBuffer_GetBuffer(obj); - if (view == NULL) { + Py_buffer view; + if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) != 0) { return -1; } - if (view->suboffsets != NULL || !PyBuffer_IsContiguous(view, 'A')) { + if (view.suboffsets != NULL || !PyBuffer_IsContiguous(&view, 'A')) { PyErr_SetString(st->PicklingError, "PickleBuffer can not be pickled when " "pointing to a non-contiguous buffer"); - return -1; + goto error; } + + int rc = 0; int in_band = 1; if (self->buffer_callback != NULL) { PyObject *ret = PyObject_CallOneArg(self->buffer_callback, obj); if (ret == NULL) { - return -1; + goto error; } in_band = PyObject_IsTrue(ret); Py_DECREF(ret); if (in_band == -1) { - return -1; + goto error; } } if (in_band) { /* Write data in-band */ - if (view->readonly) { - return _save_bytes_data(st, self, obj, (const char *)view->buf, - view->len); + if (view.readonly) { + rc = _save_bytes_data(st, self, obj, (const char *)view.buf, + view.len); } else { - return _save_bytearray_data(st, self, obj, (const char *)view->buf, - view->len); + rc = _save_bytearray_data(st, self, obj, (const char *)view.buf, + view.len); } } else { /* Write data out-of-band */ const char next_buffer_op = NEXT_BUFFER; if (_Pickler_Write(self, &next_buffer_op, 1) < 0) { - return -1; + goto error; } - if (view->readonly) { + if (view.readonly) { const char readonly_buffer_op = READONLY_BUFFER; if (_Pickler_Write(self, &readonly_buffer_op, 1) < 0) { - return -1; + goto error; } } } - return 0; + + PyBuffer_Release(&view); + return rc; + +error: + PyBuffer_Release(&view); + return -1; } /* A copy of PyUnicode_AsRawUnicodeEscapeString() that also translates @@ -2619,31 +2774,26 @@ save_picklebuffer(PickleState *st, PicklerObject *self, PyObject *obj) static PyObject * raw_unicode_escape(PyObject *obj) { - char *p; - Py_ssize_t i, size; - const void *data; - int kind; - _PyBytesWriter writer; - - _PyBytesWriter_Init(&writer); - - size = PyUnicode_GET_LENGTH(obj); - data = PyUnicode_DATA(obj); - kind = PyUnicode_KIND(obj); + Py_ssize_t size = PyUnicode_GET_LENGTH(obj); + const void *data = PyUnicode_DATA(obj); + int kind = PyUnicode_KIND(obj); - p = _PyBytesWriter_Alloc(&writer, size); - if (p == NULL) - goto error; - writer.overallocate = 1; + Py_ssize_t alloc = size; + PyBytesWriter *writer = PyBytesWriter_Create(alloc); + if (writer == NULL) { + return NULL; + } + char *p = PyBytesWriter_GetData(writer); - for (i=0; i < size; i++) { + for (Py_ssize_t i=0; i < size; i++) { Py_UCS4 ch = PyUnicode_READ(kind, data, i); /* Map 32-bit characters to '\Uxxxxxxxx' */ if (ch >= 0x10000) { /* -1: subtract 1 preallocated byte */ - p = _PyBytesWriter_Prepare(&writer, p, 10-1); - if (p == NULL) + p = PyBytesWriter_GrowAndUpdatePointer(writer, 10-1, p); + if (p == NULL) { goto error; + } *p++ = '\\'; *p++ = 'U'; @@ -2662,9 +2812,10 @@ raw_unicode_escape(PyObject *obj) ch == 0x1a) { /* -1: subtract 1 preallocated byte */ - p = _PyBytesWriter_Prepare(&writer, p, 6-1); - if (p == NULL) + p = PyBytesWriter_GrowAndUpdatePointer(writer, 6-1, p); + if (p == NULL) { goto error; + } *p++ = '\\'; *p++ = 'u'; @@ -2678,10 +2829,10 @@ raw_unicode_escape(PyObject *obj) *p++ = (char) ch; } - return _PyBytesWriter_Finish(&writer, p); + return PyBytesWriter_FinishWithPointer(writer, p); error: - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter_Discard(writer); return NULL; } @@ -2928,11 +3079,6 @@ batch_list(PickleState *state, PicklerObject *self, PyObject *iter, PyObject *or assert(iter != NULL); - /* XXX: I think this function could be made faster by avoiding the - iterator interface and fetching objects directly from list using - PyList_GET_ITEM. - */ - if (self->proto == 0) { /* APPENDS isn't available; do one at a time. */ for (;; total++) { @@ -3045,7 +3191,7 @@ static int batch_list_exact(PickleState *state, PicklerObject *self, PyObject *obj) { PyObject *item = NULL; - Py_ssize_t this_batch, total; + Py_ssize_t this_batch, total, list_size; const char append_op = APPEND; const char appends_op = APPENDS; @@ -3055,29 +3201,36 @@ batch_list_exact(PickleState *state, PicklerObject *self, PyObject *obj) assert(self->proto > 0); assert(PyList_CheckExact(obj)); - if (PyList_GET_SIZE(obj) == 1) { - item = PyList_GET_ITEM(obj, 0); - Py_INCREF(item); - int err = save(state, self, item, 0); - Py_DECREF(item); - if (err < 0) { - _PyErr_FormatNote("when serializing %T item 0", obj); - return -1; - } - if (_Pickler_Write(self, &append_op, 1) < 0) - return -1; - return 0; - } + list_size = PyList_GET_SIZE(obj); /* Write in batches of BATCHSIZE. */ total = 0; do { + if (list_size - total == 1) { + item = PyList_GetItemRef(obj, total); + if (item == NULL) { + _PyErr_FormatNote("when serializing %T item %zd", obj, total); + return -1; + } + int err = save(state, self, item, 0); + Py_DECREF(item); + if (err < 0) { + _PyErr_FormatNote("when serializing %T item %zd", obj, total); + return -1; + } + if (_Pickler_Write(self, &append_op, 1) < 0) + return -1; + return 0; + } this_batch = 0; if (_Pickler_Write(self, &mark_op, 1) < 0) return -1; while (total < PyList_GET_SIZE(obj)) { - item = PyList_GET_ITEM(obj, total); - Py_INCREF(item); + item = PyList_GetItemRef(obj, total); + if (item == NULL) { + _PyErr_FormatNote("when serializing %T item %zd", obj, total); + return -1; + } int err = save(state, self, item, 0); Py_DECREF(item); if (err < 0) { @@ -3090,8 +3243,14 @@ batch_list_exact(PickleState *state, PicklerObject *self, PyObject *obj) } if (_Pickler_Write(self, &appends_op, 1) < 0) return -1; + if (PyList_GET_SIZE(obj) != list_size) { + PyErr_Format( + PyExc_RuntimeError, + "list changed size during iteration"); + return -1; + } - } while (total < PyList_GET_SIZE(obj)); + } while (total < list_size); return 0; } @@ -3318,7 +3477,7 @@ batch_dict(PickleState *state, PicklerObject *self, PyObject *iter, PyObject *or * Note that this currently doesn't work for protocol 0. */ static int -batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj) +batch_dict_exact_impl(PickleState *state, PicklerObject *self, PyObject *obj) { PyObject *key = NULL, *value = NULL; int i; @@ -3332,28 +3491,29 @@ batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj) assert(self->proto > 0); dict_size = PyDict_GET_SIZE(obj); - - /* Special-case len(d) == 1 to save space. */ - if (dict_size == 1) { - PyDict_Next(obj, &ppos, &key, &value); - Py_INCREF(key); - Py_INCREF(value); - if (save(state, self, key, 0) < 0) { - goto error; - } - if (save(state, self, value, 0) < 0) { - _PyErr_FormatNote("when serializing %T item %R", obj, key); - goto error; - } - Py_CLEAR(key); - Py_CLEAR(value); - if (_Pickler_Write(self, &setitem_op, 1) < 0) - return -1; - return 0; - } + assert(dict_size); /* Write in batches of BATCHSIZE. */ + Py_ssize_t total = 0; do { + if (dict_size - total == 1) { + PyDict_Next(obj, &ppos, &key, &value); + Py_INCREF(key); + Py_INCREF(value); + if (save(state, self, key, 0) < 0) { + goto error; + } + if (save(state, self, value, 0) < 0) { + _PyErr_FormatNote("when serializing %T item %R", obj, key); + goto error; + } + Py_CLEAR(key); + Py_CLEAR(value); + if (_Pickler_Write(self, &setitem_op, 1) < 0) + return -1; + return 0; + } + i = 0; if (_Pickler_Write(self, &mark_op, 1) < 0) return -1; @@ -3369,6 +3529,7 @@ batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj) } Py_CLEAR(key); Py_CLEAR(value); + total++; if (++i == BATCHSIZE) break; } @@ -3381,7 +3542,7 @@ batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj) return -1; } - } while (i == BATCHSIZE); + } while (total < dict_size); return 0; error: Py_XDECREF(key); @@ -3389,6 +3550,18 @@ batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj) return -1; } +/* gh-146452: Wrap the dict iteration in a critical section to prevent + concurrent mutation from invalidating PyDict_Next() iteration state. */ +static int +batch_dict_exact(PickleState *state, PicklerObject *self, PyObject *obj) +{ + int ret; + Py_BEGIN_CRITICAL_SECTION(obj); + ret = batch_dict_exact_impl(state, self, obj); + Py_END_CRITICAL_SECTION(); + return ret; +} + static int save_dict(PickleState *state, PicklerObject *self, PyObject *obj) { @@ -3499,6 +3672,7 @@ save_set(PickleState *state, PicklerObject *self, PyObject *obj) return 0; /* nothing to do */ /* Write in batches of BATCHSIZE. */ + Py_ssize_t total = 0; do { i = 0; if (_Pickler_Write(self, &mark_op, 1) < 0) @@ -3513,6 +3687,7 @@ save_set(PickleState *state, PicklerObject *self, PyObject *obj) _PyErr_FormatNote("when serializing %T element", obj); break; } + total++; if (++i == BATCHSIZE) break; } @@ -3528,22 +3703,19 @@ save_set(PickleState *state, PicklerObject *self, PyObject *obj) "set changed size during iteration"); return -1; } - } while (i == BATCHSIZE); + } while (total < set_size); return 0; } static int -save_frozenset(PickleState *state, PicklerObject *self, PyObject *obj) +save_frozenset_impl(PickleState *state, PicklerObject *self, PyObject *obj) { PyObject *iter; const char mark_op = MARK; const char frozenset_op = FROZENSET; - if (self->fast && !fast_save_enter(self, obj)) - return -1; - if (self->proto < 4) { PyObject *items; PyObject *reduce_value; @@ -3614,13 +3786,26 @@ save_frozenset(PickleState *state, PicklerObject *self, PyObject *obj) return 0; } +static int +save_frozenset(PickleState *state, PicklerObject *self, PyObject *obj) +{ + if (self->fast && !fast_save_enter(self, obj)) { + return -1; + } + int status = save_frozenset_impl(state, self, obj); + if (self->fast && !fast_save_leave(self, obj)) { + return -1; + } + return status; +} + static int fix_imports(PickleState *st, PyObject **module_name, PyObject **global_name) { PyObject *key; PyObject *item; - key = PyTuple_Pack(2, *module_name, *global_name); + key = _PyTuple_FromPair(*module_name, *global_name); if (key == NULL) return -1; item = PyDict_GetItemWithError(st->name_mapping_3to2, key); @@ -3703,6 +3888,15 @@ save_global(PickleState *st, PicklerObject *self, PyObject *obj, dotted_path = get_dotted_path(global_name); if (dotted_path == NULL) goto error; + switch (mangle_dotted_path(dotted_path)) { + case -1: + goto error; + case 1: + Py_SETREF(global_name, join_dotted_path(dotted_path)); + if (global_name == NULL) { + goto error; + } + } module_name = whichmodule(st, obj, global_name, dotted_path); if (module_name == NULL) goto error; @@ -3717,7 +3911,7 @@ save_global(PickleState *st, PicklerObject *self, PyObject *obj, char pdata[5]; Py_ssize_t n; - extension_key = PyTuple_Pack(2, module_name, global_name); + extension_key = _PyTuple_FromPair(module_name, global_name); if (extension_key == NULL) { goto error; } @@ -4689,17 +4883,23 @@ _pickle_Pickler_dump_impl(PicklerObject *self, PyTypeObject *cls, Py_TYPE(self)->tp_name); return NULL; } + BEGIN_USING_PICKLER(self, NULL); - if (_Pickler_ClearBuffer(self) < 0) - return NULL; - - if (dump(st, self, obj) < 0) - return NULL; - - if (_Pickler_FlushToFile(self) < 0) - return NULL; - + if (_Pickler_ClearBuffer(self) < 0) { + goto error; + } + if (dump(st, self, obj) < 0) { + goto error; + } + if (_Pickler_FlushToFile(self) < 0) { + goto error; + } + END_USING_PICKLER(self); Py_RETURN_NONE; + +error: + END_USING_PICKLER(self); + return NULL; } /*[clinic input] @@ -4840,47 +5040,54 @@ _pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, PyObject *buffer_callback) /*[clinic end generated code: output=0abedc50590d259b input=cddc50f66b770002]*/ { + BEGIN_USING_PICKLER(self, -1); /* In case of multiple __init__() calls, clear previous content. */ if (self->write != NULL) (void)Pickler_clear((PyObject *)self); - if (_Pickler_SetProtocol(self, protocol, fix_imports) < 0) - return -1; - - if (_Pickler_SetOutputStream(self, file) < 0) - return -1; - - if (_Pickler_SetBufferCallback(self, buffer_callback) < 0) - return -1; - + if (_Pickler_SetProtocol(self, protocol, fix_imports) < 0) { + goto error; + } + if (_Pickler_SetOutputStream(self, file) < 0) { + goto error; + } + if (_Pickler_SetBufferCallback(self, buffer_callback) < 0) { + goto error; + } /* memo and output_buffer may have already been created in _Pickler_New */ if (self->memo == NULL) { self->memo = PyMemoTable_New(); - if (self->memo == NULL) - return -1; + if (self->memo == NULL) { + goto error; + } } self->output_len = 0; if (self->output_buffer == NULL) { self->max_output_len = WRITE_BUF_SIZE; self->output_buffer = PyBytes_FromStringAndSize(NULL, self->max_output_len); - if (self->output_buffer == NULL) - return -1; + if (self->output_buffer == NULL) { + goto error; + } } self->fast = 0; self->fast_nesting = 0; self->fast_memo = NULL; - if (self->dispatch_table != NULL) { - return 0; - } - if (PyObject_GetOptionalAttr((PyObject *)self, &_Py_ID(dispatch_table), - &self->dispatch_table) < 0) { - return -1; + if (self->dispatch_table == NULL) { + if (PyObject_GetOptionalAttr((PyObject *)self, &_Py_ID(dispatch_table), + &self->dispatch_table) < 0) { + goto error; + } } + END_USING_PICKLER(self); return 0; + +error: + END_USING_PICKLER(self); + return -1; } @@ -4964,26 +5171,19 @@ static PyObject * _pickle_PicklerMemoProxy___reduce___impl(PicklerMemoProxyObject *self) /*[clinic end generated code: output=bebba1168863ab1d input=2f7c540e24b7aae4]*/ { - PyObject *reduce_value, *dict_args; + PyObject *dict_args; PyObject *contents = _pickle_PicklerMemoProxy_copy_impl(self); if (contents == NULL) return NULL; - reduce_value = PyTuple_New(2); - if (reduce_value == NULL) { - Py_DECREF(contents); - return NULL; - } dict_args = PyTuple_New(1); if (dict_args == NULL) { Py_DECREF(contents); - Py_DECREF(reduce_value); return NULL; } PyTuple_SET_ITEM(dict_args, 0, contents); - PyTuple_SET_ITEM(reduce_value, 0, Py_NewRef(&PyDict_Type)); - PyTuple_SET_ITEM(reduce_value, 1, dict_args); - return reduce_value; + + return _PyTuple_FromPairSteal(Py_NewRef(&PyDict_Type), dict_args); } static PyMethodDef picklerproxy_methods[] = { @@ -5255,7 +5455,7 @@ load_int(PickleState *state, UnpicklerObject *self) } } else { - if (len == 3 && (x == 0 || x == 1)) { + if (len == 3 && s[0] == '0' && (s[1] == '0' || s[1] == '1')) { if ((value = PyBool_FromLong(x)) == NULL) return -1; } @@ -5593,13 +5793,28 @@ load_counted_binbytes(PickleState *state, UnpicklerObject *self, int nbytes) return -1; } - bytes = PyBytes_FromStringAndSize(NULL, size); - if (bytes == NULL) - return -1; - if (_Unpickler_ReadInto(state, self, PyBytes_AS_STRING(bytes), size) < 0) { - Py_DECREF(bytes); + Py_ssize_t cursize = Py_MIN(size, MIN_READ_BUF_SIZE); + Py_ssize_t prevsize = 0; + bytes = PyBytes_FromStringAndSize(NULL, cursize); + if (bytes == NULL) { return -1; } + while (1) { + if (_Unpickler_ReadInto(state, self, + PyBytes_AS_STRING(bytes) + prevsize, cursize - prevsize) < 0) + { + Py_DECREF(bytes); + return -1; + } + if (cursize >= size) { + break; + } + prevsize = cursize; + cursize += Py_MIN(cursize, size - cursize); + if (_PyBytes_Resize(&bytes, cursize) < 0) { + return -1; + } + } PDATA_PUSH(self->stack, bytes, -1); return 0; @@ -5624,14 +5839,27 @@ load_counted_bytearray(PickleState *state, UnpicklerObject *self) return -1; } - bytearray = PyByteArray_FromStringAndSize(NULL, size); + Py_ssize_t cursize = Py_MIN(size, MIN_READ_BUF_SIZE); + Py_ssize_t prevsize = 0; + bytearray = PyByteArray_FromStringAndSize(NULL, cursize); if (bytearray == NULL) { return -1; } - char *str = PyByteArray_AS_STRING(bytearray); - if (_Unpickler_ReadInto(state, self, str, size) < 0) { - Py_DECREF(bytearray); - return -1; + while (1) { + if (_Unpickler_ReadInto(state, self, + PyByteArray_AS_STRING(bytearray) + prevsize, + cursize - prevsize) < 0) { + Py_DECREF(bytearray); + return -1; + } + if (cursize >= size) { + break; + } + prevsize = cursize; + cursize += Py_MIN(cursize, size - cursize); + if (PyByteArray_Resize(bytearray, cursize) < 0) { + return -1; + } } PDATA_PUSH(self->stack, bytearray, -1); @@ -6233,20 +6461,15 @@ load_get(PickleState *st, UnpicklerObject *self) if (key == NULL) return -1; idx = PyLong_AsSsize_t(key); + Py_DECREF(key); if (idx == -1 && PyErr_Occurred()) { - Py_DECREF(key); return -1; } - value = _Unpickler_MemoGet(self, idx); + value = _Unpickler_MemoGet(st, self, idx); if (value == NULL) { - if (!PyErr_Occurred()) { - PyErr_Format(st->UnpicklingError, "Memo value not found at index %ld", idx); - } - Py_DECREF(key); return -1; } - Py_DECREF(key); PDATA_APPEND(self->stack, value, -1); return 0; @@ -6264,13 +6487,8 @@ load_binget(PickleState *st, UnpicklerObject *self) idx = Py_CHARMASK(s[0]); - value = _Unpickler_MemoGet(self, idx); + value = _Unpickler_MemoGet(st, self, idx); if (value == NULL) { - PyObject *key = PyLong_FromSsize_t(idx); - if (key != NULL) { - PyErr_Format(st->UnpicklingError, "Memo value not found at index %ld", idx); - Py_DECREF(key); - } return -1; } @@ -6290,13 +6508,8 @@ load_long_binget(PickleState *st, UnpicklerObject *self) idx = calc_binsize(s, 4); - value = _Unpickler_MemoGet(self, idx); + value = _Unpickler_MemoGet(st, self, idx); if (value == NULL) { - PyObject *key = PyLong_FromSsize_t(idx); - if (key != NULL) { - PyErr_Format(st->UnpicklingError, "Memo value not found at index %ld", idx); - Py_DECREF(key); - } return -1; } @@ -6491,8 +6704,6 @@ do_append(PickleState *state, UnpicklerObject *self, Py_ssize_t x) len = Py_SIZE(self->stack); if (x > len || x <= self->stack->fence) return Pdata_stack_underflow(state, self->stack); - if (len == x) /* nothing to do */ - return 0; list = self->stack->data[x - 1]; @@ -6582,8 +6793,6 @@ do_setitems(PickleState *st, UnpicklerObject *self, Py_ssize_t x) len = Py_SIZE(self->stack); if (x > len || x <= self->stack->fence) return Pdata_stack_underflow(st, self->stack); - if (len == x) /* nothing to do */ - return 0; if ((len - x) % 2 != 0) { /* Corrupt or hostile pickle -- we never write one like this. */ PyErr_SetString(st->UnpicklingError, @@ -6635,8 +6844,6 @@ load_additems(PickleState *state, UnpicklerObject *self) len = Py_SIZE(self->stack); if (mark > len || mark <= self->stack->fence) return Pdata_stack_underflow(state, self->stack); - if (len == mark) /* nothing to do */ - return 0; set = self->stack->data[mark - 1]; @@ -7069,22 +7276,22 @@ static PyObject * _pickle_Unpickler_load_impl(UnpicklerObject *self, PyTypeObject *cls) /*[clinic end generated code: output=cc88168f608e3007 input=f5d2f87e61d5f07f]*/ { - UnpicklerObject *unpickler = (UnpicklerObject*)self; - PickleState *st = _Pickle_GetStateByClass(cls); /* Check whether the Unpickler was initialized correctly. This prevents segfaulting if a subclass overridden __init__ with a function that does not call Unpickler.__init__(). Here, we simply ensure that self->read is not NULL. */ - if (unpickler->read == NULL) { + if (self->read == NULL) { PyErr_Format(st->UnpicklingError, "Unpickler.__init__() was not called by %s.__init__()", - Py_TYPE(unpickler)->tp_name); + Py_TYPE(self)->tp_name); return NULL; } - - return load(st, unpickler); + BEGIN_USING_UNPICKLER(self, NULL); + PyObject *res = load(st, self); + END_USING_UNPICKLER(self); + return res; } /* The name of find_class() is misleading. In newer pickle protocols, this @@ -7134,7 +7341,7 @@ _pickle_Unpickler_find_class_impl(UnpicklerObject *self, PyTypeObject *cls, /* Check if the global (i.e., a function or a class) was renamed or moved to another module. */ - key = PyTuple_Pack(2, module_name, global_name); + key = _PyTuple_FromPair(module_name, global_name); if (key == NULL) return NULL; item = PyDict_GetItemWithError(st->name_mapping_2to3, key); @@ -7261,6 +7468,7 @@ Unpickler_clear(PyObject *op) self->buffer.buf = NULL; } + Py_CLEAR(self->memo_dict); _Unpickler_MemoCleanup(self); PyMem_Free(self->marks); self->marks = NULL; @@ -7297,6 +7505,7 @@ Unpickler_traverse(PyObject *op, visitproc visit, void *arg) Py_VISIT(self->persistent_load); Py_VISIT(self->persistent_load_attr); Py_VISIT(self->buffers); + Py_VISIT(self->memo_dict); PyObject **memo = self->memo; if (memo) { Py_ssize_t i = self->memo_size; @@ -7346,35 +7555,41 @@ _pickle_Unpickler___init___impl(UnpicklerObject *self, PyObject *file, const char *errors, PyObject *buffers) /*[clinic end generated code: output=09f0192649ea3f85 input=ca4c1faea9553121]*/ { + BEGIN_USING_UNPICKLER(self, -1); /* In case of multiple __init__() calls, clear previous content. */ if (self->read != NULL) (void)Unpickler_clear((PyObject *)self); - if (_Unpickler_SetInputStream(self, file) < 0) - return -1; - - if (_Unpickler_SetInputEncoding(self, encoding, errors) < 0) - return -1; - - if (_Unpickler_SetBuffers(self, buffers) < 0) - return -1; - + if (_Unpickler_SetInputStream(self, file) < 0) { + goto error; + } + if (_Unpickler_SetInputEncoding(self, encoding, errors) < 0) { + goto error; + } + if (_Unpickler_SetBuffers(self, buffers) < 0) { + goto error; + } self->fix_imports = fix_imports; PyTypeObject *tp = Py_TYPE(self); PickleState *state = _Pickle_FindStateByType(tp); self->stack = (Pdata *)Pdata_New(state); - if (self->stack == NULL) - return -1; - + if (self->stack == NULL) { + goto error; + } self->memo_size = 32; self->memo = _Unpickler_NewMemo(self->memo_size); - if (self->memo == NULL) - return -1; - + if (self->memo == NULL) { + goto error; + } self->proto = 0; + END_USING_UNPICKLER(self); return 0; + +error: + END_USING_UNPICKLER(self); + return -1; } @@ -7456,27 +7671,19 @@ static PyObject * _pickle_UnpicklerMemoProxy___reduce___impl(UnpicklerMemoProxyObject *self) /*[clinic end generated code: output=6da34ac048d94cca input=6920862413407199]*/ { - PyObject *reduce_value; PyObject *constructor_args; PyObject *contents = _pickle_UnpicklerMemoProxy_copy_impl(self); if (contents == NULL) return NULL; - reduce_value = PyTuple_New(2); - if (reduce_value == NULL) { - Py_DECREF(contents); - return NULL; - } constructor_args = PyTuple_New(1); if (constructor_args == NULL) { Py_DECREF(contents); - Py_DECREF(reduce_value); return NULL; } PyTuple_SET_ITEM(constructor_args, 0, contents); - PyTuple_SET_ITEM(reduce_value, 0, Py_NewRef(&PyDict_Type)); - PyTuple_SET_ITEM(reduce_value, 1, constructor_args); - return reduce_value; + + return _PyTuple_FromPairSteal(Py_NewRef(&PyDict_Type), constructor_args); } static PyMethodDef unpicklerproxy_methods[] = { @@ -8066,6 +8273,7 @@ _pickle_exec(PyObject *m) } static PyModuleDef_Slot pickle_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _pickle_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index b542f86b6fe8da..2aa3923f68e66a 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -63,7 +63,7 @@ # endif #endif -#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__DragonFly__) +#if defined(__CYGWIN__) || defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__DragonFly__) # define FD_DIR "/dev/fd" #else # define FD_DIR "/proc/self/fd" @@ -514,7 +514,13 @@ _close_open_fds_maybe_unsafe(int start_fd, int *fds_to_keep, proc_fd_dir = NULL; else #endif +#if defined(_AIX) + char fd_path[PATH_MAX]; + snprintf(fd_path, sizeof(fd_path), "/proc/%ld/fd", (long)getpid()); + proc_fd_dir = opendir(fd_path); +#else proc_fd_dir = opendir(FD_DIR); +#endif if (!proc_fd_dir) { /* No way to get a list of open fds. */ _close_range_except(start_fd, -1, fds_to_keep, fds_to_keep_len, @@ -630,7 +636,7 @@ reset_signal_handlers(const sigset_t *child_sigmask) * (v)fork to set things up and call exec(). * * All of the code in this function must only use async-signal-safe functions, - * listed at `man 7 signal` or + * listed at `man 7 signal-safety` or * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. * * This restriction is documented at @@ -983,15 +989,15 @@ _posixsubprocess.fork_exec as subprocess_fork_exec Spawn a fresh new child process. -Fork a child process, close parent file descriptors as appropriate in the -child and duplicate the few that are needed before calling exec() in the -child process. +Fork a child process, close parent file descriptors as appropriate in +the child and duplicate the few that are needed before calling exec() in +the child process. -If close_fds is True, close file descriptors 3 and higher, except those listed -in the sorted tuple pass_fds. +If close_fds is True, close file descriptors 3 and higher, except those +listed in the sorted tuple pass_fds. -The preexec_fn, if supplied, will be called immediately before closing file -descriptors and exec. +The preexec_fn, if supplied, will be called immediately before closing +file descriptors and exec. WARNING: preexec_fn is NOT SAFE if your application uses threads. It may trigger infrequent, difficult to debug deadlocks. @@ -1016,7 +1022,7 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, PyObject *extra_groups_packed, PyObject *uid_object, int child_umask, PyObject *preexec_fn) -/*[clinic end generated code: output=288464dc56e373c7 input=f311c3bcb5dd55c8]*/ +/*[clinic end generated code: output=288464dc56e373c7 input=5e56eac3e036e349]*/ { PyObject *converted_args = NULL, *fast_args = NULL; PyObject *preexec_fn_args_tuple = NULL; @@ -1084,8 +1090,14 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, goto cleanup; } borrowed_arg = PySequence_Fast_GET_ITEM(fast_args, arg_num); - if (PyUnicode_FSConverter(borrowed_arg, &converted_arg) == 0) + /* borrowed_arg is only borrowed; its __fspath__() may run Python + that drops fast_args' last reference to it. */ + Py_INCREF(borrowed_arg); + if (PyUnicode_FSConverter(borrowed_arg, &converted_arg) == 0) { + Py_DECREF(borrowed_arg); goto cleanup; + } + Py_DECREF(borrowed_arg); PyTuple_SET_ITEM(converted_args, arg_num, converted_arg); } @@ -1330,6 +1342,7 @@ static PyMethodDef module_methods[] = { }; static PyModuleDef_Slot _posixsubprocess_slots[] = { + _Py_ABI_SLOT, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} diff --git a/Modules/_queuemodule.c b/Modules/_queuemodule.c index 01235c77bd7db8..af54e42a6af584 100644 --- a/Modules/_queuemodule.c +++ b/Modules/_queuemodule.c @@ -154,8 +154,6 @@ RingBuf_Get(RingBuf *buf) } // Returns 0 on success or -1 if the buffer failed to grow. -// -// Steals a reference to item. static int RingBuf_Put(RingBuf *buf, PyObject *item) { @@ -168,7 +166,7 @@ RingBuf_Put(RingBuf *buf, PyObject *item) return -1; } } - buf->items[buf->put_idx] = item; + buf->items[buf->put_idx] = Py_NewRef(item); buf->put_idx = (buf->put_idx + 1) % buf->items_cap; buf->num_items++; return 0; @@ -275,16 +273,13 @@ maybe_handoff_item(void *arg, void *park_arg, int has_more_waiters) { HandoffData *data = (HandoffData*)arg; PyObject **item = (PyObject**)park_arg; - if (item == NULL) { - // No threads were waiting - data->handed_off = false; - } - else { + data->queue->has_threads_waiting = has_more_waiters; + + data->handed_off = item != NULL; + if (data->handed_off) { // There was at least one waiting thread, hand off the item - *item = data->item; - data->handed_off = true; + *item = Py_NewRef(data->item); } - data->queue->has_threads_waiting = has_more_waiters; } /*[clinic input] @@ -296,31 +291,33 @@ _queue.SimpleQueue.put Put the item on the queue. -The optional 'block' and 'timeout' arguments are ignored, as this method -never blocks. They are provided for compatibility with the Queue class. +The optional 'block' and 'timeout' arguments are ignored, as this +method never blocks. They are provided for compatibility with the +Queue class. [clinic start generated code]*/ static PyObject * _queue_SimpleQueue_put_impl(simplequeueobject *self, PyObject *item, int block, PyObject *timeout) -/*[clinic end generated code: output=4333136e88f90d8b input=a16dbb33363c0fa8]*/ +/*[clinic end generated code: output=4333136e88f90d8b input=9f9ff270a74670c3]*/ { - HandoffData data = { - .handed_off = 0, - .item = Py_NewRef(item), - .queue = self, - }; if (self->has_threads_waiting) { + HandoffData data = { + .handed_off = 0, + .item = item, + .queue = self, + }; // Try to hand the item off directly if there are threads waiting _PyParkingLot_Unpark(&self->has_threads_waiting, maybe_handoff_item, &data); - } - if (!data.handed_off) { - if (RingBuf_Put(&self->buf, item) < 0) { - return NULL; + if (data.handed_off) { + Py_RETURN_NONE; } } + if (RingBuf_Put(&self->buf, item) < 0) { + return NULL; + } Py_RETURN_NONE; } @@ -364,10 +361,11 @@ _queue.SimpleQueue.get Remove and return an item from the queue. -If optional args 'block' is true and 'timeout' is None (the default), -block if necessary until an item is available. If 'timeout' is -a non-negative number, it blocks at most 'timeout' seconds and raises -the Empty exception if no item was available within that time. +If optional args 'block' is true and 'timeout' is None (the +default), block if necessary until an item is available. If +'timeout' is a non-negative number, it blocks at most 'timeout' +seconds and raises the Empty exception if no item was available +within that time. Otherwise ('block' is false), return an item if one is immediately available, else raise the Empty exception ('timeout' is ignored in that case). @@ -377,7 +375,7 @@ in that case). static PyObject * _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls, int block, PyObject *timeout_obj) -/*[clinic end generated code: output=5c2cca914cd1e55b input=f7836c65e5839c51]*/ +/*[clinic end generated code: output=5c2cca914cd1e55b input=afa0889bbc6b4761]*/ { PyTime_t endtime = 0; @@ -500,6 +498,22 @@ _queue_SimpleQueue_qsize_impl(simplequeueobject *self) return RingBuf_Len(&self->buf); } +/*[clinic input] +@critical_section +_queue.SimpleQueue.__sizeof__ -> Py_ssize_t + +Returns size in memory, in bytes. +[clinic start generated code]*/ + +static Py_ssize_t +_queue_SimpleQueue___sizeof___impl(simplequeueobject *self) +/*[clinic end generated code: output=58ce4e3bbc078fd4 input=a3a7f05c9616598f]*/ +{ + Py_ssize_t res = sizeof(simplequeueobject); + res += self->buf.items_cap * sizeof(PyObject *); + return res; +} + static int queue_traverse(PyObject *m, visitproc visit, void *arg) { @@ -534,8 +548,9 @@ static PyMethodDef simplequeue_methods[] = { _QUEUE_SIMPLEQUEUE_PUT_METHODDEF _QUEUE_SIMPLEQUEUE_PUT_NOWAIT_METHODDEF _QUEUE_SIMPLEQUEUE_QSIZE_METHODDEF + _QUEUE_SIMPLEQUEUE___SIZEOF___METHODDEF {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, PyDoc_STR("SimpleQueues are generic over the type of their contents")}, {NULL, NULL} /* sentinel */ }; @@ -599,6 +614,7 @@ queuemodule_exec(PyObject *module) } static PyModuleDef_Slot queuemodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, queuemodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c index 2f4f388ce1161a..a06966be23be1e 100644 --- a/Modules/_randommodule.c +++ b/Modules/_randommodule.c @@ -123,9 +123,9 @@ typedef struct { /*[clinic input] module _random -class _random.Random "RandomObject *" "_randomstate_type(type)->Random_Type" +class _random.Random "RandomObject *" "(PyTypeObject *)_randomstate_type(Py_TYPE(self))->Random_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=70a2c99619474983]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=f04bcbfba61a322e]*/ /* Random methods */ @@ -522,7 +522,7 @@ _random_Random_getrandbits_impl(RandomObject *self, uint64_t k) PyErr_NoMemory(); return NULL; } - words = (k - 1u) / 32u + 1u; + words = (Py_ssize_t)((k - 1u) / 32u + 1u); wordarray = (uint32_t *)PyMem_Malloc(words * 4); if (wordarray == NULL) { PyErr_NoMemory(); @@ -549,27 +549,20 @@ _random_Random_getrandbits_impl(RandomObject *self, uint64_t k) return result; } -static int -random_init(PyObject *self, PyObject *args, PyObject *kwds) -{ - PyObject *arg = NULL; - _randomstate *state = _randomstate_type(Py_TYPE(self)); - - if ((Py_IS_TYPE(self, (PyTypeObject *)state->Random_Type) || - Py_TYPE(self)->tp_init == ((PyTypeObject*)state->Random_Type)->tp_init) && - !_PyArg_NoKeywords("Random", kwds)) { - return -1; - } - - if (PyTuple_GET_SIZE(args) > 1) { - PyErr_SetString(PyExc_TypeError, "Random() requires 0 or 1 argument"); - return -1; - } +/*[clinic input] +@critical_section +@text_signature "($self, [seed])" +_random.Random.__init__ as random_init - if (PyTuple_GET_SIZE(args) == 1) - arg = PyTuple_GET_ITEM(args, 0); + seed: object = NULL + / +[clinic start generated code]*/ - return random_seed(RandomObject_CAST(self), arg); +static int +random_init_impl(RandomObject *self, PyObject *seed) +/*[clinic end generated code: output=260734a3739c394f input=e516bf32e8a05e28]*/ +{ + return random_seed(self, seed); } @@ -595,11 +588,14 @@ static PyType_Slot Random_Type_slots[] = { }; static PyType_Spec Random_Type_spec = { - "_random.Random", - sizeof(RandomObject), - 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - Random_Type_slots + .name = "_random.Random", + .basicsize = sizeof(RandomObject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = Random_Type_slots }; PyDoc_STRVAR(module_doc, @@ -640,6 +636,7 @@ _random_exec(PyObject *module) } static PyModuleDef_Slot _random_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _random_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_remote_debugging/_remote_debugging.h b/Modules/_remote_debugging/_remote_debugging.h new file mode 100644 index 00000000000000..635e6e208902af --- /dev/null +++ b/Modules/_remote_debugging/_remote_debugging.h @@ -0,0 +1,809 @@ +/****************************************************************************** + * Python Remote Debugging Module - Shared Header + * + * This header provides common declarations, types, and utilities shared + * across the remote debugging module implementation files. + ******************************************************************************/ + +#ifndef Py_REMOTE_DEBUGGING_H +#define Py_REMOTE_DEBUGGING_H + +/* _GNU_SOURCE must be defined before any system headers */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE_BUILTIN +# ifndef Py_BUILD_CORE_MODULE +# define Py_BUILD_CORE_MODULE 1 +# endif +#endif + +#include "Python.h" +#include "internal/pycore_debug_offsets.h" // _Py_DebugOffsets +#include "internal/pycore_frame.h" // FRAME_SUSPENDED_YIELD_FROM +#include "internal/pycore_interpframe.h" // FRAME_OWNED_BY_INTERPRETER +#include "internal/pycore_llist.h" // struct llist_node +#include "internal/pycore_long.h" // _PyLong_GetZero +#include "internal/pycore_pyerrors.h" // _PyErr_FormatFromCause +#include "internal/pycore_pyhash.h" // _Py_HashPointerRaw +#include "internal/pycore_stackref.h" // Py_TAG_BITS +#include "../../Python/remote_debug.h" + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef HAVE_PROCESS_VM_READV +# define HAVE_PROCESS_VM_READV 0 +#endif + +#if defined(__APPLE__) +#include <TargetConditionals.h> +# if !defined(TARGET_OS_OSX) + /* Older macOS SDKs do not define TARGET_OS_OSX */ +# define TARGET_OS_OSX 1 +# endif +# if TARGET_OS_OSX +# include <libproc.h> +# include <sys/types.h> +# define MAX_NATIVE_THREADS 4096 +# endif +#endif + +// Platforms that support pausing/resuming threads for accurate stack sampling +#if defined(MS_WINDOWS) || defined(__linux__) || (defined(__APPLE__) && TARGET_OS_OSX) +# define Py_REMOTE_DEBUG_SUPPORTS_BLOCKING 1 +#endif + +#ifdef MS_WINDOWS +#include <windows.h> +#include <winternl.h> +#endif + +#if defined(__APPLE__) && TARGET_OS_OSX + +typedef struct { + mach_port_t task; + int suspended; +} _Py_RemoteDebug_ThreadsState; + +#elif defined(__linux__) + +typedef struct { + pid_t *tids; // Points to unwinder's reusable buffer + size_t count; // Number of threads currently seized +} _Py_RemoteDebug_ThreadsState; + +#elif defined(MS_WINDOWS) + +typedef NTSTATUS (NTAPI *NtSuspendProcessFunc)(HANDLE ProcessHandle); +typedef NTSTATUS (NTAPI *NtResumeProcessFunc)(HANDLE ProcessHandle); + +typedef struct { + HANDLE hProcess; + int suspended; +} _Py_RemoteDebug_ThreadsState; + +#else + +typedef struct { + int dummy; +} _Py_RemoteDebug_ThreadsState; + +#endif + +#ifdef MS_WINDOWS +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) +typedef enum _WIN32_THREADSTATE { + WIN32_THREADSTATE_INITIALIZED = 0, + WIN32_THREADSTATE_READY = 1, + WIN32_THREADSTATE_RUNNING = 2, + WIN32_THREADSTATE_STANDBY = 3, + WIN32_THREADSTATE_TERMINATED = 4, + WIN32_THREADSTATE_WAITING = 5, + WIN32_THREADSTATE_TRANSITION = 6, + WIN32_THREADSTATE_UNKNOWN = 7 +} WIN32_THREADSTATE; +#endif + +/* ============================================================================ + * MACROS AND CONSTANTS + * ============================================================================ */ + +#define GET_MEMBER(type, obj, offset) \ + (*(const type *)memcpy(&(type){0}, (const char *)(obj) + (offset), sizeof(type))) +#define CLEAR_PTR_TAG(ptr) (((uintptr_t)(ptr) & ~Py_TAG_BITS)) +#define GET_MEMBER_NO_TAG(type, obj, offset) (type)(CLEAR_PTR_TAG(GET_MEMBER(type, obj, offset))) + +/* Size macros for opaque buffers */ +#define SIZEOF_BYTES_OBJ sizeof(PyBytesObject) +#define SIZEOF_CODE_OBJ sizeof(PyCodeObject) +#define SIZEOF_GEN_OBJ sizeof(PyGenObject) +#define SIZEOF_INTERP_FRAME sizeof(_PyInterpreterFrame) +#define SIZEOF_LLIST_NODE sizeof(struct llist_node) +#define SIZEOF_PAGE_CACHE_ENTRY sizeof(page_cache_entry_t) +#define SIZEOF_PYOBJECT sizeof(PyObject) +#define SIZEOF_SET_OBJ sizeof(PySetObject) +#define SIZEOF_TASK_OBJ 4096 +#define SIZEOF_THREAD_STATE sizeof(PyThreadState) +#define SIZEOF_TYPE_OBJ sizeof(PyTypeObject) +#define SIZEOF_UNICODE_OBJ sizeof(PyUnicodeObject) +#define SIZEOF_LONG_OBJ sizeof(PyLongObject) +#define SIZEOF_GC_RUNTIME_STATE sizeof(struct _gc_runtime_state) +#define SIZEOF_INTERPRETER_STATE sizeof(PyInterpreterState) + +/* Maximum sizes for validation to prevent buffer overflows from corrupted data */ +#define MAX_STACK_CHUNK_SIZE (16 * 1024 * 1024) /* 16 MB max for stack chunks */ +#define MAX_LONG_DIGITS 64 /* Allows values up to ~2^1920 */ +#define MAX_SET_TABLE_SIZE (1 << 20) /* 1 million entries max for set iteration */ + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifdef Py_GIL_DISABLED +#define INTERP_STATE_MIN_SIZE MAX(MAX(MAX(MAX(offsetof(PyInterpreterState, _code_object_generation) + sizeof(uint64_t), \ + offsetof(PyInterpreterState, tlbc_indices.tlbc_generation) + sizeof(uint32_t)), \ + offsetof(PyInterpreterState, threads.head) + sizeof(void*)), \ + offsetof(PyInterpreterState, _gil.last_holder) + sizeof(PyThreadState*)), \ + offsetof(PyInterpreterState, gc.frame) + sizeof(_PyInterpreterFrame *)) +#else +#define INTERP_STATE_MIN_SIZE MAX(MAX(MAX(offsetof(PyInterpreterState, _code_object_generation) + sizeof(uint64_t), \ + offsetof(PyInterpreterState, threads.head) + sizeof(void*)), \ + offsetof(PyInterpreterState, _gil.last_holder) + sizeof(PyThreadState*)), \ + offsetof(PyInterpreterState, gc.frame) + sizeof(_PyInterpreterFrame *)) +#endif +#define INTERP_STATE_BUFFER_SIZE MAX(INTERP_STATE_MIN_SIZE, 256) + +#define MAX_TLBC_SIZE 2048 + +/* Thread status flags */ +#define THREAD_STATUS_HAS_GIL (1 << 0) +#define THREAD_STATUS_ON_CPU (1 << 1) +#define THREAD_STATUS_UNKNOWN (1 << 2) +#define THREAD_STATUS_GIL_REQUESTED (1 << 3) +#define THREAD_STATUS_HAS_EXCEPTION (1 << 4) +#define THREAD_STATUS_MAIN_THREAD (1 << 5) + +/* Exception cause macro */ +#define set_exception_cause(unwinder, exc_type, message) \ + do { \ + assert(PyErr_Occurred() && "function returned -1 without setting exception"); \ + if (unwinder->debug && !_Py_RemoteDebug_HasPermissionError()) { \ + _set_debug_exception_cause(exc_type, message); \ + } \ + } while (0) + +/* ============================================================================ + * TYPE DEFINITIONS + * ============================================================================ */ + +struct _Py_AsyncioModuleDebugOffsets { + struct _asyncio_task_object { + uint64_t size; + uint64_t task_name; + uint64_t task_awaited_by; + uint64_t task_is_task; + uint64_t task_awaited_by_is_set; + uint64_t task_coro; + uint64_t task_node; + } asyncio_task_object; + struct _asyncio_interpreter_state { + uint64_t size; + uint64_t asyncio_tasks_head; + } asyncio_interpreter_state; + struct _asyncio_thread_state { + uint64_t size; + uint64_t asyncio_running_loop; + uint64_t asyncio_running_task; + uint64_t asyncio_tasks_head; + } asyncio_thread_state; +}; + +typedef struct { + PyObject *func_name; + PyObject *file_name; + int first_lineno; + PyObject *linetable; // bytes + PyObject *last_frame_info; + ptrdiff_t last_addrq; + uintptr_t addr_code_adaptive; +} CachedCodeMetadata; + +/* Frame cache constants and types */ +#define FRAME_CACHE_MAX_THREADS 32 +#define FRAME_CACHE_MAX_FRAMES 1024 + +typedef struct { + uint64_t thread_id; // 0 = empty slot + uintptr_t thread_state_addr; + uintptr_t addrs[FRAME_CACHE_MAX_FRAMES]; + Py_ssize_t num_addrs; + PyObject *thread_id_obj; // owned reference, NULL if empty + PyObject *frame_list; // owned reference, NULL if empty +} FrameCacheEntry; + +#define INTERPRETER_THREAD_CACHE_SIZE 32 +#if (INTERPRETER_THREAD_CACHE_SIZE & (INTERPRETER_THREAD_CACHE_SIZE - 1)) != 0 +# error "INTERPRETER_THREAD_CACHE_SIZE must be a power of two" +#endif + +// The two per-interpreter L2 caches below are split into per-field tables so +// that a writer rebinding one slot cannot leave stale data in a field owned by +// the other when the slot is reused across interpreters. +typedef struct { + uintptr_t interpreter_addr; + uintptr_t thread_state_addr; +} InterpreterTstateCacheEntry; +typedef struct { + uintptr_t interpreter_addr; + uint64_t code_object_generation; +} InterpreterGenerationCacheEntry; + +// Carries already-read thread state and/or frame buffers across helpers so the +// downstream callee can skip a remote read. Address fields are caller-supplied +// inputs; buffer pointers (tstate, frame) are NULL unless a prior batched read +// successfully populated them. +typedef struct { + const char *tstate; + uintptr_t tstate_addr; + const char *frame; + uintptr_t frame_addr; +} RemoteReadPrefetch; + +/* Statistics for profiling performance analysis */ +typedef struct { + uint64_t total_samples; // Total number of get_stack_trace calls + uint64_t frame_cache_hits; // Full cache hits (entire stack unchanged) + uint64_t frame_cache_misses; // Cache misses requiring full walk + uint64_t frame_cache_partial_hits; // Partial hits (stopped at cached frame) + uint64_t frames_read_from_cache; // Total frames retrieved from cache + uint64_t frames_read_from_memory; // Total frames read from remote memory + uint64_t memory_reads; // Total remote memory read operations + uint64_t memory_bytes_read; // Total bytes read from remote memory + uint64_t code_object_cache_hits; // Code object cache hits + uint64_t code_object_cache_misses; // Code object cache misses + uint64_t stale_cache_invalidations; // Times stale entries were cleared + uint64_t batched_read_attempts; // Batched remote-read attempts + uint64_t batched_read_successes; // Attempts that read all requested segments + uint64_t batched_read_misses; // Attempts that fell back or partially read + uint64_t batched_read_segments_requested; // Segments requested by batched reads + uint64_t batched_read_segments_completed; // Segments completed by batched reads +} UnwinderStats; + +#if defined(__GNUC__) || defined(__clang__) +# define REMOTE_DEBUG_UNLIKELY(value) __builtin_expect(!!(value), 0) +#else +# define REMOTE_DEBUG_UNLIKELY(value) (value) +#endif + +/* Stats tracking macros - no-op when stats collection is disabled */ +#define STATS_INC(unwinder, field) \ + do { if (REMOTE_DEBUG_UNLIKELY((unwinder)->collect_stats)) (unwinder)->stats.field++; } while(0) + +#define STATS_ADD(unwinder, field, val) \ + do { if (REMOTE_DEBUG_UNLIKELY((unwinder)->collect_stats)) (unwinder)->stats.field += (val); } while(0) + +#if HAVE_PROCESS_VM_READV +# define STATS_BATCHED_READ(unwinder, requested, completed) \ + do { \ + if (REMOTE_DEBUG_UNLIKELY((unwinder)->collect_stats)) { \ + (unwinder)->stats.batched_read_attempts++; \ + (unwinder)->stats.batched_read_segments_requested += (uint64_t)(requested); \ + (unwinder)->stats.batched_read_segments_completed += (uint64_t)(completed); \ + if ((completed) == (requested)) { \ + (unwinder)->stats.batched_read_successes++; \ + } \ + else { \ + (unwinder)->stats.batched_read_misses++; \ + } \ + } \ + } while(0) +#else +# define STATS_BATCHED_READ(unwinder, requested, completed) ((void)0) +#endif + +typedef struct { + PyTypeObject *RemoteDebugging_Type; + PyTypeObject *TaskInfo_Type; + PyTypeObject *LocationInfo_Type; + PyTypeObject *FrameInfo_Type; + PyTypeObject *CoroInfo_Type; + PyTypeObject *ThreadInfo_Type; + PyTypeObject *InterpreterInfo_Type; + PyTypeObject *AwaitedInfo_Type; + PyTypeObject *GCStatsInfo_Type; + PyTypeObject *BinaryWriter_Type; + PyTypeObject *BinaryReader_Type; + PyTypeObject *GCMonitor_Type; +} RemoteDebuggingState; + +enum _ThreadState { + THREAD_STATE_RUNNING, + THREAD_STATE_IDLE, + THREAD_STATE_GIL_WAIT, + THREAD_STATE_UNKNOWN +}; + +enum _ProfilingMode { + PROFILING_MODE_WALL = 0, + PROFILING_MODE_CPU = 1, + PROFILING_MODE_GIL = 2, + PROFILING_MODE_ALL = 3, + PROFILING_MODE_EXCEPTION = 4 +}; + +typedef struct { + PyObject_HEAD + proc_handle_t handle; + uintptr_t runtime_start_address; + struct _Py_DebugOffsets debug_offsets; + int async_debug_offsets_available; + struct _Py_AsyncioModuleDebugOffsets async_debug_offsets; + uintptr_t interpreter_addr; + uintptr_t tstate_addr; + _Py_hashtable_t *code_object_cache; + int debug; + int only_active_thread; + int mode; + int skip_non_matching_threads; + int native; + int gc; + int opcodes; + int cache_frames; + int collect_stats; // whether to collect statistics + uint32_t stale_invalidation_counter; // counter for throttling frame_cache_invalidate_stale + // L1 single-entry shortcut over cached_tstates[]: most workloads sample one + // interpreter, so check these pairs before hashing into the table below. + uintptr_t cached_tstate_interpreter_addr; + uintptr_t cached_tstate_addr; + uintptr_t cached_generation_interpreter_addr; + uint64_t cached_code_object_generation; + RemoteDebuggingState *cached_state; + FrameCacheEntry *frame_cache; // preallocated array of FRAME_CACHE_MAX_THREADS entries + UnwinderStats stats; // statistics for performance analysis + InterpreterTstateCacheEntry cached_tstates[INTERPRETER_THREAD_CACHE_SIZE]; + InterpreterGenerationCacheEntry cached_generations[INTERPRETER_THREAD_CACHE_SIZE]; +#ifdef Py_GIL_DISABLED + uint32_t tlbc_generation; + _Py_hashtable_t *tlbc_cache; +#endif +#ifdef __APPLE__ + uint64_t thread_id_offset; + int thread_id_offset_initialized; +#endif +#ifdef MS_WINDOWS + PVOID win_process_buffer; + ULONG win_process_buffer_size; +#endif + // Thread stopping state (only on platforms that support it) +#ifdef Py_REMOTE_DEBUG_SUPPORTS_BLOCKING + _Py_RemoteDebug_ThreadsState threads_state; + int threads_stopped; // 1 if threads are currently stopped +#endif +#ifdef __linux__ + pid_t *thread_tids; // Reusable buffer for thread IDs + size_t thread_tids_capacity; // Current capacity of thread_tids buffer +#endif +} RemoteUnwinderObject; + +#define RemoteUnwinder_CAST(op) ((RemoteUnwinderObject *)(op)) + +typedef struct { + int lineno; + int end_lineno; + int column; + int end_column; +} LocationInfo; + +typedef struct { + uintptr_t remote_addr; + size_t size; + void *local_copy; +} StackChunkInfo; + +typedef struct { + StackChunkInfo *chunks; + size_t count; +} StackChunkList; + +typedef struct { + proc_handle_t handle; + uintptr_t runtime_start_address; + struct _Py_DebugOffsets debug_offsets; + int debug; +} RuntimeOffsets; + +/* + * Context for frame chain traversal operations. + */ +typedef struct { + /* Inputs */ + uintptr_t frame_addr; // Starting frame address + uintptr_t thread_state_addr; // Owning thread state address + uintptr_t base_frame_addr; // Sentinel at bottom (for validation) + uintptr_t gc_frame; // GC frame address (0 if not tracking) + uintptr_t last_profiled_frame; // Last cached frame (0 if no cache) + StackChunkList *chunks; // Pre-copied stack chunks + int skip_first_frame; // Skip frame_addr itself (continue from its caller) + RemoteReadPrefetch prefetch; // Optional already-read thread/frame buffers + + /* Outputs */ + PyObject *frame_info; // List to append FrameInfo objects + uintptr_t *frame_addrs; // Array of visited frame addresses + Py_ssize_t num_addrs; // Count of addresses collected + Py_ssize_t max_addrs; // Capacity of frame_addrs array + uintptr_t last_frame_visited; // Last frame address visited + int stopped_at_cached_frame; // Whether we stopped at cached frame +} FrameWalkContext; + +/* + * Context for code object parsing. + */ +typedef struct { + uintptr_t code_addr; // Code object address in remote process + uintptr_t instruction_pointer; // Current instruction pointer + int32_t tlbc_index; // Thread-local bytecode index (free-threading) +} CodeObjectContext; + +typedef struct { + PyObject_HEAD + RuntimeOffsets offsets; +} GCMonitorObject; + +#define GCMonitor_CAST(op) ((GCMonitorObject *)(op)) + +/* Function pointer types for iteration callbacks */ +typedef int (*thread_processor_func)( + RemoteUnwinderObject *unwinder, + uintptr_t thread_state_addr, + unsigned long tid, + void *context +); + +typedef int (*set_entry_processor_func)( + RemoteUnwinderObject *unwinder, + uintptr_t key_addr, + void *context +); + +typedef int (*interpreter_processor_func)( + RuntimeOffsets *offsets, + uintptr_t interpreter_state_addr, + int64_t iid, + void *context +); + + +/* ============================================================================ + * STRUCTSEQ DESCRIPTORS (extern declarations) + * ============================================================================ */ + +extern PyStructSequence_Desc TaskInfo_desc; +extern PyStructSequence_Desc LocationInfo_desc; +extern PyStructSequence_Desc FrameInfo_desc; +extern PyStructSequence_Desc CoroInfo_desc; +extern PyStructSequence_Desc ThreadInfo_desc; +extern PyStructSequence_Desc InterpreterInfo_desc; +extern PyStructSequence_Desc AwaitedInfo_desc; +extern PyStructSequence_Desc GCStatsInfo_desc; + +/* ============================================================================ + * UTILITY FUNCTION DECLARATIONS + * ============================================================================ */ + +/* State access functions */ +extern RemoteDebuggingState *RemoteDebugging_GetState(PyObject *module); +extern RemoteDebuggingState *RemoteDebugging_GetStateFromType(PyTypeObject *type); +extern RemoteDebuggingState *RemoteDebugging_GetStateFromObject(PyObject *obj); +extern int RemoteDebugging_InitState(RemoteDebuggingState *st); + +/* Cache management */ +extern void cached_code_metadata_destroy(void *ptr); + +/* Validation */ +extern int is_prerelease_version(uint64_t version); +extern int validate_debug_offsets(struct _Py_DebugOffsets *debug_offsets); +#define PY_REMOTE_DEBUG_INVALID_ASYNC_DEBUG_OFFSETS (-2) + +/* ============================================================================ + * MEMORY READING FUNCTION DECLARATIONS + * ============================================================================ */ + +extern int read_ptr(RemoteUnwinderObject *unwinder, uintptr_t address, uintptr_t *result); +extern int read_Py_ssize_t(RemoteUnwinderObject *unwinder, uintptr_t address, Py_ssize_t *result); +extern int read_char(RemoteUnwinderObject *unwinder, uintptr_t address, char *result); +extern int read_py_ptr(RemoteUnwinderObject *unwinder, uintptr_t address, uintptr_t *ptr_addr); + +/* Python object reading */ +extern PyObject *read_py_str(RemoteUnwinderObject *unwinder, uintptr_t address, Py_ssize_t max_len); +extern PyObject *read_py_bytes(RemoteUnwinderObject *unwinder, uintptr_t address, Py_ssize_t max_len); +extern long read_py_long(RemoteUnwinderObject *unwinder, uintptr_t address); + +/* ============================================================================ + * CODE OBJECT FUNCTION DECLARATIONS + * ============================================================================ */ + +extern int parse_code_object( + RemoteUnwinderObject *unwinder, + PyObject **result, + const CodeObjectContext *ctx +); + +extern PyObject *make_location_info( + RemoteUnwinderObject *unwinder, + int lineno, + int end_lineno, + int col_offset, + int end_col_offset +); + +extern PyObject *make_frame_info( + RemoteUnwinderObject *unwinder, + PyObject *file, + PyObject *location, // LocationInfo structseq or None for synthetic frames + PyObject *func, + PyObject *opcode +); + +/* Line table parsing */ +extern bool parse_linetable( + const uintptr_t addrq, + const char* linetable, + Py_ssize_t linetable_size, + int firstlineno, + LocationInfo* info +); + +/* TLBC cache (only for Py_GIL_DISABLED) */ +#ifdef Py_GIL_DISABLED +typedef struct { + void *tlbc_array; + Py_ssize_t tlbc_array_size; + uint32_t generation; +} TLBCCacheEntry; + +extern void tlbc_cache_entry_destroy(void *ptr); +extern TLBCCacheEntry *get_tlbc_cache_entry(RemoteUnwinderObject *self, uintptr_t code_addr, uint32_t current_generation); +extern int cache_tlbc_array(RemoteUnwinderObject *unwinder, uintptr_t code_addr, uintptr_t tlbc_array_addr, uint32_t generation); +#endif + +/* ============================================================================ + * FRAME FUNCTION DECLARATIONS + * ============================================================================ */ + +extern int is_frame_valid( + RemoteUnwinderObject *unwinder, + uintptr_t frame_addr, + uintptr_t code_object_addr +); + +extern int parse_frame_object( + RemoteUnwinderObject *unwinder, + PyObject** result, + uintptr_t address, + uintptr_t* address_of_code_object, + uintptr_t* previous_frame +); + +extern int parse_frame_from_chunks( + RemoteUnwinderObject *unwinder, + PyObject **result, + uintptr_t address, + uintptr_t *previous_frame, + uintptr_t *stackpointer, + StackChunkList *chunks +); + +/* Stack chunk management */ +extern void cleanup_stack_chunks(StackChunkList *chunks); +extern int copy_stack_chunks(RemoteUnwinderObject *unwinder, uintptr_t tstate_addr, StackChunkList *out_chunks); +extern void *find_frame_in_chunks(StackChunkList *chunks, uintptr_t remote_ptr); + +extern int process_frame_chain( + RemoteUnwinderObject *unwinder, + FrameWalkContext *ctx +); + +/* Frame cache functions */ +extern int frame_cache_init(RemoteUnwinderObject *unwinder); +extern void frame_cache_cleanup(RemoteUnwinderObject *unwinder); +extern FrameCacheEntry *frame_cache_find(RemoteUnwinderObject *unwinder, uint64_t thread_id); +extern FrameCacheEntry *frame_cache_find_by_tstate(RemoteUnwinderObject *unwinder, uintptr_t tstate_addr); +extern int clear_last_profiled_frames(RemoteUnwinderObject *unwinder); +extern void frame_cache_invalidate_stale(RemoteUnwinderObject *unwinder, PyObject *result); +extern int frame_cache_lookup_and_extend( + RemoteUnwinderObject *unwinder, + uint64_t thread_id, + uintptr_t last_profiled_frame, + PyObject *frame_info, + uintptr_t *frame_addrs, + Py_ssize_t *num_addrs, + Py_ssize_t max_addrs); +// Returns: 1 = stored, 0 = not stored (graceful), -1 = error +// Only stores complete stacks that reach base_frame_addr +extern int frame_cache_store( + RemoteUnwinderObject *unwinder, + uint64_t thread_id, + PyObject *frame_list, + const uintptr_t *addrs, + Py_ssize_t num_addrs, + uintptr_t thread_state_addr, + uintptr_t base_frame_addr, + uintptr_t last_frame_visited); + +extern int collect_frames_with_cache( + RemoteUnwinderObject *unwinder, + FrameWalkContext *ctx, + uint64_t thread_id); + +/* ============================================================================ + * THREAD FUNCTION DECLARATIONS + * ============================================================================ */ + +extern int iterate_threads( + RemoteUnwinderObject *unwinder, + thread_processor_func processor, + void *context +); + +extern int populate_initial_state_data( + int all_threads, + RemoteUnwinderObject *unwinder, + uintptr_t runtime_start_address, + uintptr_t *interpreter_state, + uintptr_t *tstate +); + +extern int find_running_frame( + RemoteUnwinderObject *unwinder, + uintptr_t address_of_thread, + uintptr_t *frame +); + +extern int get_thread_status(RemoteUnwinderObject *unwinder, uint64_t tid, uint64_t pthread_id); + +extern PyObject* unwind_stack_for_thread( + RemoteUnwinderObject *unwinder, + uintptr_t *current_tstate, + uintptr_t gil_holder_tstate, + uintptr_t gc_frame, + uintptr_t main_thread_tstate, + const RemoteReadPrefetch *prefetch +); + +/* Thread stopping functions (for blocking mode) */ +extern void _Py_RemoteDebug_InitThreadsState(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st); +extern int _Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st); +extern void _Py_RemoteDebug_ResumeAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st); + +/* ============================================================================ + * INTERPRETER FUNCTION DECLARATIONS + * ============================================================================ */ + +extern int +iterate_interpreters( + RuntimeOffsets *offsets, + interpreter_processor_func processor, + void *context +); + +/* ============================================================================ + * ASYNCIO FUNCTION DECLARATIONS + * ============================================================================ */ + +extern uintptr_t _Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t* handle); +extern int read_async_debug(RemoteUnwinderObject *unwinder); +extern int ensure_async_debug_offsets(RemoteUnwinderObject *unwinder); + +/* Task parsing */ +extern PyObject *parse_task_name(RemoteUnwinderObject *unwinder, uintptr_t task_address); + +extern int parse_task( + RemoteUnwinderObject *unwinder, + uintptr_t task_address, + PyObject *render_to +); + +extern int parse_coro_chain( + RemoteUnwinderObject *unwinder, + uintptr_t coro_address, + PyObject *render_to +); + +extern int parse_async_frame_chain( + RemoteUnwinderObject *unwinder, + PyObject *calls, + uintptr_t address_of_thread, + uintptr_t running_task_code_obj +); + +/* Set iteration */ +extern int iterate_set_entries( + RemoteUnwinderObject *unwinder, + uintptr_t set_addr, + set_entry_processor_func processor, + void *context +); + +/* Task awaited_by processing */ +extern int process_task_awaited_by( + RemoteUnwinderObject *unwinder, + uintptr_t task_address, + set_entry_processor_func processor, + void *context +); + +extern int process_single_task_node( + RemoteUnwinderObject *unwinder, + uintptr_t task_addr, + PyObject **task_info, + PyObject *result +); + +extern int process_task_and_waiters( + RemoteUnwinderObject *unwinder, + uintptr_t task_addr, + PyObject *result +); + +extern int find_running_task_in_thread( + RemoteUnwinderObject *unwinder, + uintptr_t thread_state_addr, + uintptr_t *running_task_addr +); + +extern int get_task_code_object( + RemoteUnwinderObject *unwinder, + uintptr_t task_addr, + uintptr_t *code_obj_addr +); + +extern int append_awaited_by( + RemoteUnwinderObject *unwinder, + unsigned long tid, + uintptr_t head_addr, + PyObject *result +); + +/* Thread processors for asyncio */ +extern int process_thread_for_awaited_by( + RemoteUnwinderObject *unwinder, + uintptr_t thread_state_addr, + unsigned long tid, + void *context +); + +extern int process_thread_for_async_stack_trace( + RemoteUnwinderObject *unwinder, + uintptr_t thread_state_addr, + unsigned long tid, + void *context +); + +/* ============================================================================ + * SUBPROCESS ENUMERATION FUNCTION DECLARATIONS + * ============================================================================ */ + +/* Get all child PIDs of a process. + * Returns a new Python list of PIDs, or NULL on error with exception set. + * If recursive is true, includes all descendants (children, grandchildren, etc.) + */ +extern PyObject *enumerate_child_pids(pid_t target_pid, int recursive); + +#ifdef __cplusplus +} +#endif + +#endif /* Py_REMOTE_DEBUGGING_H */ diff --git a/Modules/_remote_debugging/asyncio.c b/Modules/_remote_debugging/asyncio.c new file mode 100644 index 00000000000000..44a9a3cbce0061 --- /dev/null +++ b/Modules/_remote_debugging/asyncio.c @@ -0,0 +1,1046 @@ +/****************************************************************************** + * Remote Debugging Module - Asyncio Functions + * + * This file contains functions for parsing asyncio tasks, coroutines, + * and awaited_by relationships from remote process memory. + ******************************************************************************/ + +#include "_remote_debugging.h" +#include "debug_offsets_validation.h" + +/* ============================================================================ + * ASYNCIO DEBUG ADDRESS FUNCTIONS + * ============================================================================ */ + +uintptr_t +_Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t* handle) +{ + uintptr_t address; + +#ifdef MS_WINDOWS + // On Windows, search for asyncio debug in executable or DLL + address = search_windows_map_for_section(handle, "AsyncioD", L"_asyncio", + NULL); + if (address == 0) { + if (!_Py_RemoteDebug_HasPermissionError()) { + PyObject *exc = PyErr_GetRaisedException(); + PyErr_SetString(PyExc_RuntimeError, "Failed to find the AsyncioDebug section in the process."); + _PyErr_ChainExceptions1(exc); + } + } +#elif defined(__linux__) && HAVE_PROCESS_VM_READV + // On Linux, search for asyncio debug in executable or DLL + address = search_linux_map_for_section(handle, "AsyncioDebug", "python", + NULL); + if (address == 0) { + if (!_Py_RemoteDebug_HasPermissionError()) { + PyObject *exc = PyErr_GetRaisedException(); + PyErr_SetString(PyExc_RuntimeError, "Failed to find the AsyncioDebug section in the process."); + _PyErr_ChainExceptions1(exc); + } + } +#elif defined(__APPLE__) && TARGET_OS_OSX + // On macOS, try libpython first, then fall back to python + address = search_map_for_section(handle, "AsyncioDebug", "libpython", + NULL); + if (address == 0 && !_Py_RemoteDebug_HasPermissionError()) { + PyErr_Clear(); + address = search_map_for_section(handle, "AsyncioDebug", "python", + NULL); + } + if (address == 0) { + if (!_Py_RemoteDebug_HasPermissionError()) { + PyObject *exc = PyErr_GetRaisedException(); + PyErr_SetString(PyExc_RuntimeError, "Failed to find the AsyncioDebug section in the process."); + _PyErr_ChainExceptions1(exc); + } + } +#else + Py_UNREACHABLE(); +#endif + + return address; +} + +int +read_async_debug(RemoteUnwinderObject *unwinder) +{ + uintptr_t async_debug_addr = _Py_RemoteDebug_GetAsyncioDebugAddress(&unwinder->handle); + if (!async_debug_addr) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to get AsyncioDebug address"); + return -1; + } + + size_t size = sizeof(struct _Py_AsyncioModuleDebugOffsets); + int result = _Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, async_debug_addr, size, &unwinder->async_debug_offsets); + if (result < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read AsyncioDebug offsets"); + return result; + } + if (_PyRemoteDebug_ValidateAsyncDebugOffsets(&unwinder->async_debug_offsets) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Invalid AsyncioDebug offsets"); + return PY_REMOTE_DEBUG_INVALID_ASYNC_DEBUG_OFFSETS; + } + return 0; +} + +int +ensure_async_debug_offsets(RemoteUnwinderObject *unwinder) +{ + // If already available, nothing to do + if (unwinder->async_debug_offsets_available) { + return 0; + } + + // Try to load async debug offsets (the target process may have + // loaded asyncio since we last checked) + int result = read_async_debug(unwinder); + if (result == PY_REMOTE_DEBUG_INVALID_ASYNC_DEBUG_OFFSETS) { + return -1; + } + if (result < 0) { + if (!_Py_RemoteDebug_HasPermissionError()) { + PyErr_Clear(); + PyErr_SetString(PyExc_RuntimeError, "AsyncioDebug section not available"); + set_exception_cause(unwinder, PyExc_RuntimeError, + "AsyncioDebug section unavailable - asyncio module may not be loaded in target process"); + } + return -1; + } + + unwinder->async_debug_offsets_available = 1; + return 0; +} + +/* ============================================================================ + * SET ITERATION FUNCTIONS + * ============================================================================ */ + +int +iterate_set_entries( + RemoteUnwinderObject *unwinder, + uintptr_t set_addr, + set_entry_processor_func processor, + void *context +) { + char set_object[SIZEOF_SET_OBJ]; + if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, set_addr, + SIZEOF_SET_OBJ, set_object) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read set object"); + return -1; + } + + Py_ssize_t num_els = GET_MEMBER(Py_ssize_t, set_object, unwinder->debug_offsets.set_object.used); + Py_ssize_t mask = GET_MEMBER(Py_ssize_t, set_object, unwinder->debug_offsets.set_object.mask); + uintptr_t table_ptr = GET_MEMBER(uintptr_t, set_object, unwinder->debug_offsets.set_object.table); + + // Validate mask and num_els to prevent huge loop iterations from garbage data + if (mask < 0 || mask >= MAX_SET_TABLE_SIZE || num_els < 0 || num_els > mask + 1) { + PyErr_SetString(PyExc_RuntimeError, "Invalid set object (corrupted remote memory)"); + set_exception_cause(unwinder, PyExc_RuntimeError, + "Invalid set object (corrupted remote memory)"); + return -1; + } + Py_ssize_t set_len = mask + 1; + + Py_ssize_t i = 0; + Py_ssize_t els = 0; + while (i < set_len && els < num_els) { + uintptr_t key_addr; + if (read_py_ptr(unwinder, table_ptr, &key_addr) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read set entry key"); + return -1; + } + + if ((void*)key_addr != NULL) { + Py_ssize_t ref_cnt; + if (read_Py_ssize_t(unwinder, table_ptr, &ref_cnt) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read set entry ref count"); + return -1; + } + + if (ref_cnt) { + // Process this valid set entry + if (processor(unwinder, key_addr, context) < 0) { + return -1; + } + els++; + } + } + table_ptr += sizeof(void*) * 2; + i++; + } + + return 0; +} + +/* ============================================================================ + * TASK NAME PARSING + * ============================================================================ */ + +PyObject * +parse_task_name( + RemoteUnwinderObject *unwinder, + uintptr_t task_address +) { + // Read the entire TaskObj at once + char task_obj[SIZEOF_TASK_OBJ]; + int err = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + task_address, + (size_t)unwinder->async_debug_offsets.asyncio_task_object.size, + task_obj); + if (err < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task object"); + return NULL; + } + + uintptr_t task_name_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_name); + + // The task name can be a long or a string so we need to check the type + char task_name_obj[SIZEOF_PYOBJECT]; + err = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + task_name_addr, + SIZEOF_PYOBJECT, + task_name_obj); + if (err < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task name object"); + return NULL; + } + + // Now read the type object to get the flags + char type_obj[SIZEOF_TYPE_OBJ]; + err = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + GET_MEMBER(uintptr_t, task_name_obj, unwinder->debug_offsets.pyobject.ob_type), + SIZEOF_TYPE_OBJ, + type_obj); + if (err < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task name type object"); + return NULL; + } + + if ((GET_MEMBER(unsigned long, type_obj, unwinder->debug_offsets.type_object.tp_flags) & Py_TPFLAGS_LONG_SUBCLASS)) { + long res = read_py_long(unwinder, task_name_addr); + if (res == -1 && PyErr_Occurred()) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Task name PyLong parsing failed"); + return NULL; + } + return PyUnicode_FromFormat("Task-%ld", res); + } + + if(!(GET_MEMBER(unsigned long, type_obj, unwinder->debug_offsets.type_object.tp_flags) & Py_TPFLAGS_UNICODE_SUBCLASS)) { + PyErr_SetString(PyExc_RuntimeError, "Invalid task name object"); + set_exception_cause(unwinder, PyExc_RuntimeError, "Task name object is neither long nor unicode"); + return NULL; + } + + return read_py_str( + unwinder, + task_name_addr, + 255 + ); +} + +/* ============================================================================ + * COROUTINE CHAIN PARSING + * ============================================================================ */ + +static int +handle_yield_from_frame( + RemoteUnwinderObject *unwinder, + uintptr_t gi_iframe_addr, + uintptr_t gen_type_addr, + PyObject *render_to +) { + // Read the entire interpreter frame at once + char iframe[SIZEOF_INTERP_FRAME]; + int err = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + gi_iframe_addr, + SIZEOF_INTERP_FRAME, + iframe); + if (err < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read interpreter frame in yield_from handler"); + return -1; + } + + if (GET_MEMBER(char, iframe, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_GENERATOR) { + PyErr_SetString( + PyExc_RuntimeError, + "generator doesn't own its frame \\_o_/"); + set_exception_cause(unwinder, PyExc_RuntimeError, "Frame ownership mismatch in yield_from"); + return -1; + } + + uintptr_t stackpointer_addr = GET_MEMBER_NO_TAG(uintptr_t, iframe, unwinder->debug_offsets.interpreter_frame.stackpointer); + + if ((void*)stackpointer_addr != NULL) { + uintptr_t gi_await_addr; + err = read_py_ptr( + unwinder, + stackpointer_addr - sizeof(void*) * 2, + &gi_await_addr); + if (err) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read gi_await address"); + return -1; + } + + if ((void*)gi_await_addr != NULL) { + uintptr_t gi_await_addr_type_addr; + err = read_ptr( + unwinder, + gi_await_addr + (uintptr_t)unwinder->debug_offsets.pyobject.ob_type, + &gi_await_addr_type_addr); + if (err) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read gi_await type address"); + return -1; + } + + if (gen_type_addr == gi_await_addr_type_addr) { + /* This needs an explanation. We always start with parsing + native coroutine / generator frames. Ultimately they + are awaiting on something. That something can be + a native coroutine frame or... an iterator. + If it's the latter -- we can't continue building + our chain. So the condition to bail out of this is + to do that when the type of the current coroutine + doesn't match the type of whatever it points to + in its cr_await. + */ + err = parse_coro_chain(unwinder, gi_await_addr, render_to); + if (err) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse coroutine chain in yield_from"); + return -1; + } + } + } + } + + return 0; +} + +int +parse_coro_chain( + RemoteUnwinderObject *unwinder, + uintptr_t coro_address, + PyObject *render_to +) { + assert((void*)coro_address != NULL); + + // Read the entire generator object at once + char gen_object[SIZEOF_GEN_OBJ]; + int err = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + coro_address, + SIZEOF_GEN_OBJ, + gen_object); + if (err < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read generator object in coro chain"); + return -1; + } + + int8_t frame_state = GET_MEMBER(int8_t, gen_object, unwinder->debug_offsets.gen_object.gi_frame_state); + if (frame_state == FRAME_CLEARED) { + return 0; + } + + uintptr_t gen_type_addr = GET_MEMBER(uintptr_t, gen_object, unwinder->debug_offsets.pyobject.ob_type); + + PyObject* name = NULL; + + // Parse the previous frame using the gi_iframe from local copy + uintptr_t prev_frame; + uintptr_t gi_iframe_addr = coro_address + (uintptr_t)unwinder->debug_offsets.gen_object.gi_iframe; + uintptr_t address_of_code_object = 0; + if (parse_frame_object(unwinder, &name, gi_iframe_addr, &address_of_code_object, &prev_frame) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse frame object in coro chain"); + return -1; + } + + if (!name) { + return 0; + } + + if (PyList_Append(render_to, name)) { + Py_DECREF(name); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame to coro chain"); + return -1; + } + Py_DECREF(name); + + if (frame_state == FRAME_SUSPENDED_YIELD_FROM) { + return handle_yield_from_frame(unwinder, gi_iframe_addr, gen_type_addr, render_to); + } + + return 0; +} + +/* ============================================================================ + * TASK PARSING FUNCTIONS + * ============================================================================ */ + +static PyObject* +create_task_result( + RemoteUnwinderObject *unwinder, + uintptr_t task_address +) { + PyObject* result = NULL; + PyObject *call_stack = NULL; + PyObject *tn = NULL; + char task_obj[SIZEOF_TASK_OBJ]; + uintptr_t coro_addr; + + // Create call_stack first since it's the first tuple element + call_stack = PyList_New(0); + if (call_stack == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create call stack list"); + goto error; + } + + // Create task name/address for second tuple element + tn = PyLong_FromUnsignedLongLong(task_address); + if (tn == NULL) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create task name/address"); + goto error; + } + + // Parse coroutine chain + if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, task_address, + (size_t)unwinder->async_debug_offsets.asyncio_task_object.size, + task_obj) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task object for coro chain"); + goto error; + } + + coro_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_coro); + + if ((void*)coro_addr != NULL) { + if (parse_coro_chain(unwinder, coro_addr, call_stack) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse coroutine chain"); + goto error; + } + + if (PyList_Reverse(call_stack)) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to reverse call stack"); + goto error; + } + } + + // Create final CoroInfo result + RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); + result = PyStructSequence_New(state->CoroInfo_Type); + if (result == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create CoroInfo"); + goto error; + } + + // PyStructSequence_SetItem steals references, so we don't need to DECREF on success + PyStructSequence_SetItem(result, 0, call_stack); // This steals the reference + PyStructSequence_SetItem(result, 1, tn); // This steals the reference + + return result; + +error: + Py_XDECREF(result); + Py_XDECREF(call_stack); + Py_XDECREF(tn); + return NULL; +} + +int +parse_task( + RemoteUnwinderObject *unwinder, + uintptr_t task_address, + PyObject *render_to +) { + char is_task; + PyObject* result = NULL; + int err; + + err = read_char( + unwinder, + task_address + (uintptr_t)unwinder->async_debug_offsets.asyncio_task_object.task_is_task, + &is_task); + if (err) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read is_task flag"); + goto error; + } + + if (is_task) { + result = create_task_result(unwinder, task_address); + if (!result) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create task result"); + goto error; + } + } else { + // Create an empty CoroInfo for non-task objects + RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); + result = PyStructSequence_New(state->CoroInfo_Type); + if (result == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create empty CoroInfo"); + goto error; + } + PyObject *empty_list = PyList_New(0); + if (empty_list == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create empty list"); + goto error; + } + PyObject *task_name = PyLong_FromUnsignedLongLong(task_address); + if (task_name == NULL) { + Py_DECREF(empty_list); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create task name"); + goto error; + } + PyStructSequence_SetItem(result, 0, empty_list); // This steals the reference + PyStructSequence_SetItem(result, 1, task_name); // This steals the reference + } + if (PyList_Append(render_to, result)) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append task result to render list"); + goto error; + } + + Py_DECREF(result); + return 0; + +error: + Py_XDECREF(result); + return -1; +} + +/* ============================================================================ + * TASK AWAITED_BY PROCESSING + * ============================================================================ */ + +// Forward declaration for mutual recursion +static int process_waiter_task(RemoteUnwinderObject *unwinder, uintptr_t key_addr, void *context); + +// Processor function for parsing tasks in sets +static int +process_task_parser( + RemoteUnwinderObject *unwinder, + uintptr_t key_addr, + void *context +) { + PyObject *awaited_by = (PyObject *)context; + return parse_task(unwinder, key_addr, awaited_by); +} + +static int +parse_task_awaited_by( + RemoteUnwinderObject *unwinder, + uintptr_t task_address, + PyObject *awaited_by +) { + return process_task_awaited_by(unwinder, task_address, process_task_parser, awaited_by); +} + +int +process_task_awaited_by( + RemoteUnwinderObject *unwinder, + uintptr_t task_address, + set_entry_processor_func processor, + void *context +) { + // Read the entire TaskObj at once + char task_obj[SIZEOF_TASK_OBJ]; + if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, task_address, + (size_t)unwinder->async_debug_offsets.asyncio_task_object.size, + task_obj) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task object"); + return -1; + } + + uintptr_t task_ab_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_awaited_by); + if ((void*)task_ab_addr == NULL) { + return 0; // No tasks waiting for this one + } + + char awaited_by_is_a_set = GET_MEMBER(char, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_awaited_by_is_set); + + if (awaited_by_is_a_set) { + return iterate_set_entries(unwinder, task_ab_addr, processor, context); + } else { + // Single task waiting + return processor(unwinder, task_ab_addr, context); + } +} + +int +process_single_task_node( + RemoteUnwinderObject *unwinder, + uintptr_t task_addr, + PyObject **task_info, + PyObject *result +) { + PyObject *tn = NULL; + PyObject *current_awaited_by = NULL; + PyObject *task_id = NULL; + PyObject *result_item = NULL; + PyObject *coroutine_stack = NULL; + + tn = parse_task_name(unwinder, task_addr); + if (tn == NULL) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse task name in single task node"); + goto error; + } + + current_awaited_by = PyList_New(0); + if (current_awaited_by == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create awaited_by list in single task node"); + goto error; + } + + // Extract the coroutine stack for this task + coroutine_stack = PyList_New(0); + if (coroutine_stack == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create coroutine stack list in single task node"); + goto error; + } + + if (parse_task(unwinder, task_addr, coroutine_stack) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse task coroutine stack in single task node"); + goto error; + } + + task_id = PyLong_FromUnsignedLongLong(task_addr); + if (task_id == NULL) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create task ID in single task node"); + goto error; + } + + RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); + result_item = PyStructSequence_New(state->TaskInfo_Type); + if (result_item == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create TaskInfo in single task node"); + goto error; + } + + PyStructSequence_SetItem(result_item, 0, task_id); // steals ref + PyStructSequence_SetItem(result_item, 1, tn); // steals ref + PyStructSequence_SetItem(result_item, 2, coroutine_stack); // steals ref + PyStructSequence_SetItem(result_item, 3, current_awaited_by); // steals ref + + // References transferred to tuple + task_id = NULL; + tn = NULL; + coroutine_stack = NULL; + current_awaited_by = NULL; + + if (PyList_Append(result, result_item)) { + Py_DECREF(result_item); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append result item in single task node"); + return -1; + } + if (task_info != NULL) { + *task_info = result_item; + } + Py_DECREF(result_item); + + // Get back current_awaited_by reference for parse_task_awaited_by + current_awaited_by = PyStructSequence_GetItem(result_item, 3); + if (parse_task_awaited_by(unwinder, task_addr, current_awaited_by) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse awaited_by in single task node"); + // No cleanup needed here since all references were transferred to result_item + // and result_item was already added to result list and decreffed + return -1; + } + + return 0; + +error: + Py_XDECREF(tn); + Py_XDECREF(current_awaited_by); + Py_XDECREF(task_id); + Py_XDECREF(result_item); + Py_XDECREF(coroutine_stack); + return -1; +} + +int +process_task_and_waiters( + RemoteUnwinderObject *unwinder, + uintptr_t task_addr, + PyObject *result +) { + // First, add this task to the result + if (process_single_task_node(unwinder, task_addr, NULL, result) < 0) { + return -1; + } + + // Now find all tasks that are waiting for this task and process them + return process_task_awaited_by(unwinder, task_addr, process_waiter_task, result); +} + +// Processor function for task waiters +static int +process_waiter_task( + RemoteUnwinderObject *unwinder, + uintptr_t key_addr, + void *context +) { + PyObject *result = (PyObject *)context; + return process_task_and_waiters(unwinder, key_addr, result); +} + +/* ============================================================================ + * RUNNING TASK FUNCTIONS + * ============================================================================ */ + +int +find_running_task_in_thread( + RemoteUnwinderObject *unwinder, + uintptr_t thread_state_addr, + uintptr_t *running_task_addr +) { + *running_task_addr = (uintptr_t)NULL; + + uintptr_t address_of_running_loop; + int bytes_read = read_py_ptr( + unwinder, + thread_state_addr + (uintptr_t)unwinder->async_debug_offsets.asyncio_thread_state.asyncio_running_loop, + &address_of_running_loop); + if (bytes_read == -1) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read running loop address"); + return -1; + } + + // no asyncio loop is now running + if ((void*)address_of_running_loop == NULL) { + return 0; + } + + int err = read_ptr( + unwinder, + thread_state_addr + (uintptr_t)unwinder->async_debug_offsets.asyncio_thread_state.asyncio_running_task, + running_task_addr); + if (err) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read running task address"); + return -1; + } + + return 0; +} + +int +get_task_code_object(RemoteUnwinderObject *unwinder, uintptr_t task_addr, uintptr_t *code_obj_addr) { + uintptr_t running_coro_addr = 0; + + if(read_py_ptr( + unwinder, + task_addr + (uintptr_t)unwinder->async_debug_offsets.asyncio_task_object.task_coro, + &running_coro_addr) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Running task coro read failed"); + return -1; + } + + if (running_coro_addr == 0) { + PyErr_SetString(PyExc_RuntimeError, "Running task coro is NULL"); + set_exception_cause(unwinder, PyExc_RuntimeError, "Running task coro address is NULL"); + return -1; + } + + // note: genobject's gi_iframe is an embedded struct so the address to + // the offset leads directly to its first field: f_executable + if (read_py_ptr( + unwinder, + running_coro_addr + (uintptr_t)unwinder->debug_offsets.gen_object.gi_iframe, code_obj_addr) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read running task code object"); + return -1; + } + + if (*code_obj_addr == 0) { + PyErr_SetString(PyExc_RuntimeError, "Running task code object is NULL"); + set_exception_cause(unwinder, PyExc_RuntimeError, "Running task code object address is NULL"); + return -1; + } + + return 0; +} + +/* ============================================================================ + * ASYNC FRAME CHAIN PARSING + * ============================================================================ */ + +int +parse_async_frame_chain( + RemoteUnwinderObject *unwinder, + PyObject *calls, + uintptr_t address_of_thread, + uintptr_t running_task_code_obj +) { + uintptr_t address_of_current_frame; + if (find_running_frame(unwinder, address_of_thread, &address_of_current_frame) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Running frame search failed in async chain"); + return -1; + } + + while ((void*)address_of_current_frame != NULL) { + PyObject* frame_info = NULL; + uintptr_t address_of_code_object; + int res = parse_frame_object( + unwinder, + &frame_info, + address_of_current_frame, + &address_of_code_object, + &address_of_current_frame + ); + + if (res < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Async frame object parsing failed in chain"); + return -1; + } + + if (!frame_info) { + continue; + } + + if (PyList_Append(calls, frame_info) == -1) { + Py_DECREF(frame_info); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame info to async chain"); + return -1; + } + + Py_DECREF(frame_info); + + if (address_of_code_object == running_task_code_obj) { + break; + } + } + + return 0; +} + +/* ============================================================================ + * AWAITED BY PARSING FUNCTIONS + * ============================================================================ */ + +static int +append_awaited_by_for_thread( + RemoteUnwinderObject *unwinder, + uintptr_t head_addr, + PyObject *result +) { + char task_node[SIZEOF_LLIST_NODE]; + + if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, head_addr, + sizeof(task_node), task_node) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task node head"); + return -1; + } + + size_t iteration_count = 0; + const size_t MAX_ITERATIONS = 2 << 15; // A reasonable upper bound + + while (GET_MEMBER(uintptr_t, task_node, unwinder->debug_offsets.llist_node.next) != head_addr) { + if (++iteration_count > MAX_ITERATIONS) { + PyErr_SetString(PyExc_RuntimeError, "Task list appears corrupted"); + set_exception_cause(unwinder, PyExc_RuntimeError, "Task list iteration limit exceeded"); + return -1; + } + + uintptr_t next_node = GET_MEMBER(uintptr_t, task_node, unwinder->debug_offsets.llist_node.next); + if (next_node == 0) { + PyErr_SetString(PyExc_RuntimeError, + "Invalid linked list structure reading remote memory"); + set_exception_cause(unwinder, PyExc_RuntimeError, "NULL pointer in task linked list"); + return -1; + } + + uintptr_t task_addr = next_node + - (uintptr_t)unwinder->async_debug_offsets.asyncio_task_object.task_node; + + if (process_single_task_node(unwinder, task_addr, NULL, result) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to process task node in awaited_by"); + return -1; + } + + // Read next node + if (_Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + next_node, + sizeof(task_node), + task_node) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read next task node in awaited_by"); + return -1; + } + } + + return 0; +} + +int +append_awaited_by( + RemoteUnwinderObject *unwinder, + unsigned long tid, + uintptr_t head_addr, + PyObject *result) +{ + PyObject *tid_py = PyLong_FromUnsignedLong(tid); + if (tid_py == NULL) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create thread ID object"); + return -1; + } + + PyObject* awaited_by_for_thread = PyList_New(0); + if (awaited_by_for_thread == NULL) { + Py_DECREF(tid_py); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create awaited_by thread list"); + return -1; + } + + RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); + PyObject *result_item = PyStructSequence_New(state->AwaitedInfo_Type); + if (result_item == NULL) { + Py_DECREF(tid_py); + Py_DECREF(awaited_by_for_thread); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create AwaitedInfo"); + return -1; + } + + PyStructSequence_SetItem(result_item, 0, tid_py); // steals ref + PyStructSequence_SetItem(result_item, 1, awaited_by_for_thread); // steals ref + if (PyList_Append(result, result_item)) { + Py_DECREF(result_item); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append awaited_by result item"); + return -1; + } + Py_DECREF(result_item); + + if (append_awaited_by_for_thread(unwinder, head_addr, awaited_by_for_thread)) + { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append awaited_by for thread"); + return -1; + } + + return 0; +} + +/* ============================================================================ + * THREAD PROCESSOR FUNCTIONS FOR ASYNCIO + * ============================================================================ */ + +int +process_thread_for_awaited_by( + RemoteUnwinderObject *unwinder, + uintptr_t thread_state_addr, + unsigned long tid, + void *context +) { + PyObject *result = (PyObject *)context; + uintptr_t head_addr = thread_state_addr + (uintptr_t)unwinder->async_debug_offsets.asyncio_thread_state.asyncio_tasks_head; + return append_awaited_by(unwinder, tid, head_addr, result); +} + +static int +process_running_task_chain( + RemoteUnwinderObject *unwinder, + uintptr_t running_task_addr, + uintptr_t thread_state_addr, + PyObject *result +) { + uintptr_t running_task_code_obj = 0; + if(get_task_code_object(unwinder, running_task_addr, &running_task_code_obj) < 0) { + return -1; + } + + // First, add this task to the result + PyObject *task_info = NULL; + if (process_single_task_node(unwinder, running_task_addr, &task_info, result) < 0) { + return -1; + } + + // Get the chain from the current frame to this task + PyObject *coro_chain = PyStructSequence_GET_ITEM(task_info, 2); + assert(coro_chain != NULL); + if (PyList_GET_SIZE(coro_chain) != 1) { + PyErr_Format(PyExc_RuntimeError, + "Expected single-item coro chain, got %zd items", + PyList_GET_SIZE(coro_chain)); + set_exception_cause(unwinder, PyExc_RuntimeError, "Coro chain is not a single item"); + return -1; + } + PyObject *coro_info = PyList_GET_ITEM(coro_chain, 0); + assert(coro_info != NULL); + PyObject *frame_chain = PyStructSequence_GET_ITEM(coro_info, 0); + assert(frame_chain != NULL); + + // Clear the coro_chain + if (PyList_Clear(frame_chain) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to clear coroutine chain"); + return -1; + } + + // Add the chain from the current frame to this task + if (parse_async_frame_chain(unwinder, frame_chain, thread_state_addr, running_task_code_obj) < 0) { + return -1; + } + + // Now find all tasks that are waiting for this task and process them + if (process_task_awaited_by(unwinder, running_task_addr, process_waiter_task, result) < 0) { + return -1; + } + + return 0; +} + +int +process_thread_for_async_stack_trace( + RemoteUnwinderObject *unwinder, + uintptr_t thread_state_addr, + unsigned long tid, + void *context +) { + PyObject *result = (PyObject *)context; + + // Find running task in this thread + uintptr_t running_task_addr; + if (find_running_task_in_thread(unwinder, thread_state_addr, &running_task_addr) < 0) { + return 0; + } + + // If we found a running task, process it and its waiters + if ((void*)running_task_addr != NULL) { + PyObject *task_list = PyList_New(0); + if (task_list == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create task list for thread"); + return -1; + } + + if (process_running_task_chain(unwinder, running_task_addr, thread_state_addr, task_list) < 0) { + Py_DECREF(task_list); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to process running task chain"); + return -1; + } + + // Create AwaitedInfo structure for this thread + PyObject *tid_py = PyLong_FromUnsignedLong(tid); + if (tid_py == NULL) { + Py_DECREF(task_list); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create thread ID"); + return -1; + } + + RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); + PyObject *awaited_info = PyStructSequence_New(state->AwaitedInfo_Type); + if (awaited_info == NULL) { + Py_DECREF(tid_py); + Py_DECREF(task_list); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create AwaitedInfo"); + return -1; + } + + PyStructSequence_SetItem(awaited_info, 0, tid_py); // steals ref + PyStructSequence_SetItem(awaited_info, 1, task_list); // steals ref + + if (PyList_Append(result, awaited_info)) { + Py_DECREF(awaited_info); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append AwaitedInfo to result"); + return -1; + } + Py_DECREF(awaited_info); + } + + return 0; +} diff --git a/Modules/_remote_debugging/binary_io.h b/Modules/_remote_debugging/binary_io.h new file mode 100644 index 00000000000000..d4188335c0b6d0 --- /dev/null +++ b/Modules/_remote_debugging/binary_io.h @@ -0,0 +1,675 @@ +/****************************************************************************** + * Python Remote Debugging Module - Binary I/O Header + * + * This header provides declarations for high-performance binary file I/O + * for profiling data with optional zstd streaming compression. + ******************************************************************************/ + +#ifndef Py_BINARY_IO_H +#define Py_BINARY_IO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "Python.h" +#include "pycore_hashtable.h" +#include <stdint.h> +#include <stdio.h> + +/* ============================================================================ + * BINARY FORMAT CONSTANTS + * ============================================================================ */ + +#define BINARY_FORMAT_MAGIC 0x54414348 /* "TACH" (Tachyon) in native byte order */ +#define BINARY_FORMAT_MAGIC_SWAPPED 0x48434154 /* Byte-swapped magic for endianness detection */ +#define BINARY_FORMAT_VERSION 1 + +/* Sentinel values for optional frame fields */ +#define OPCODE_NONE 255 /* No opcode captured (u8 sentinel) */ +#define LOCATION_NOT_AVAILABLE (-1) /* lineno/column not available (zigzag sentinel) */ + +/* Conditional byte-swap macros for cross-endian file reading. + * Uses Python's optimized byte-swap functions from pycore_bitutils.h */ +#define SWAP16_IF(swap, x) ((swap) ? _Py_bswap16(x) : (x)) +#define SWAP32_IF(swap, x) ((swap) ? _Py_bswap32(x) : (x)) +#define SWAP64_IF(swap, x) ((swap) ? _Py_bswap64(x) : (x)) + +/* Header field offsets and sizes */ +#define HDR_OFF_MAGIC 0 +#define HDR_SIZE_MAGIC 4 +#define HDR_OFF_VERSION (HDR_OFF_MAGIC + HDR_SIZE_MAGIC) +#define HDR_SIZE_VERSION 4 +#define HDR_OFF_PY_VERSION (HDR_OFF_VERSION + HDR_SIZE_VERSION) +#define HDR_SIZE_PY_VERSION 4 /* 3 bytes: major, minor, micro + 1 reserved */ +#define HDR_OFF_PY_MAJOR HDR_OFF_PY_VERSION +#define HDR_OFF_PY_MINOR (HDR_OFF_PY_VERSION + 1) +#define HDR_OFF_PY_MICRO (HDR_OFF_PY_VERSION + 2) +#define HDR_OFF_START_TIME (HDR_OFF_PY_VERSION + HDR_SIZE_PY_VERSION) +#define HDR_SIZE_START_TIME 8 +#define HDR_OFF_INTERVAL (HDR_OFF_START_TIME + HDR_SIZE_START_TIME) +#define HDR_SIZE_INTERVAL 8 +#define HDR_OFF_SAMPLES (HDR_OFF_INTERVAL + HDR_SIZE_INTERVAL) +#define HDR_SIZE_SAMPLES 4 +#define HDR_OFF_THREADS (HDR_OFF_SAMPLES + HDR_SIZE_SAMPLES) +#define HDR_SIZE_THREADS 4 +#define HDR_OFF_STR_TABLE (HDR_OFF_THREADS + HDR_SIZE_THREADS) +#define HDR_SIZE_STR_TABLE 8 +#define HDR_OFF_FRAME_TABLE (HDR_OFF_STR_TABLE + HDR_SIZE_STR_TABLE) +#define HDR_SIZE_FRAME_TABLE 8 +#define HDR_OFF_COMPRESSION (HDR_OFF_FRAME_TABLE + HDR_SIZE_FRAME_TABLE) +#define HDR_SIZE_COMPRESSION 4 +#define FILE_HEADER_SIZE (HDR_OFF_COMPRESSION + HDR_SIZE_COMPRESSION) +#define FILE_HEADER_PLACEHOLDER_SIZE 64 + +static_assert(FILE_HEADER_SIZE <= FILE_HEADER_PLACEHOLDER_SIZE, + "FILE_HEADER_SIZE exceeds FILE_HEADER_PLACEHOLDER_SIZE"); + +/* Sample header field offsets and sizes */ +#define SMP_OFF_THREAD_ID 0 +#define SMP_SIZE_THREAD_ID sizeof(uint64_t) +#define SMP_OFF_INTERPRETER_ID (SMP_OFF_THREAD_ID + SMP_SIZE_THREAD_ID) +#define SMP_SIZE_INTERPRETER_ID sizeof(uint32_t) +#define SMP_OFF_ENCODING (SMP_OFF_INTERPRETER_ID + SMP_SIZE_INTERPRETER_ID) +#define SMP_SIZE_ENCODING sizeof(uint8_t) +#define SAMPLE_HEADER_FIXED_SIZE (SMP_OFF_ENCODING + SMP_SIZE_ENCODING) + +static_assert(SAMPLE_HEADER_FIXED_SIZE == 13, + "SAMPLE_HEADER_FIXED_SIZE must remain 13"); + +/* Footer field offsets and sizes */ +#define FTR_OFF_STRINGS 0 +#define FTR_SIZE_STRINGS sizeof(uint32_t) +#define FTR_OFF_FRAMES (FTR_OFF_STRINGS + FTR_SIZE_STRINGS) +#define FTR_SIZE_FRAMES sizeof(uint32_t) +#define FTR_OFF_FILE_SIZE (FTR_OFF_FRAMES + FTR_SIZE_FRAMES) +#define FTR_SIZE_FILE_SIZE sizeof(uint64_t) +#define FTR_OFF_CHECKSUM (FTR_OFF_FILE_SIZE + FTR_SIZE_FILE_SIZE) +#define FTR_SIZE_CHECKSUM (2 * sizeof(uint64_t)) +#define FILE_FOOTER_SIZE (FTR_OFF_CHECKSUM + FTR_SIZE_CHECKSUM) + +static_assert(FILE_FOOTER_SIZE == 32, + "FILE_FOOTER_SIZE must remain 32"); + +/* Buffer sizes: 512KB balances syscall amortization against memory use, + * and aligns well with filesystem block sizes and zstd dictionary windows */ +#define WRITE_BUFFER_SIZE (512 * 1024) +#define COMPRESSED_BUFFER_SIZE (512 * 1024) + +/* Compression types */ +#define COMPRESSION_NONE 0 +#define COMPRESSION_ZSTD 1 + +/* Stack encoding types for delta compression */ +#define STACK_REPEAT 0x00 /* RLE: identical to previous, with count */ +#define STACK_FULL 0x01 /* Full stack (first sample or no match) */ +#define STACK_SUFFIX 0x02 /* Shares N frames from bottom */ +#define STACK_POP_PUSH 0x03 /* Remove M frames, add N frames */ + +/* Maximum stack depth we'll buffer for delta encoding */ +#define MAX_STACK_DEPTH 256 + +/* Initial capacity for RLE pending buffer */ +#define INITIAL_RLE_CAPACITY 64 + +/* Initial capacities for dynamic arrays - sized to reduce reallocations */ +#define INITIAL_STRING_CAPACITY 4096 +#define INITIAL_FRAME_CAPACITY 4096 +#define INITIAL_THREAD_CAPACITY 256 + +/* ============================================================================ + * STATISTICS STRUCTURES + * ============================================================================ */ + +/* Writer statistics - tracks encoding efficiency */ +typedef struct { + uint64_t repeat_records; /* Number of RLE repeat records written */ + uint64_t repeat_samples; /* Total samples encoded via RLE */ + uint64_t full_records; /* Number of full stack records */ + uint64_t suffix_records; /* Number of suffix match records */ + uint64_t pop_push_records; /* Number of pop-push records */ + uint64_t total_frames_written;/* Total frame indices written */ + uint64_t frames_saved; /* Frames avoided due to delta encoding */ + uint64_t bytes_written; /* Total bytes written (before compression) */ +} BinaryWriterStats; + +/* Reader statistics - tracks reconstruction performance */ +typedef struct { + uint64_t repeat_records; /* RLE records decoded */ + uint64_t repeat_samples; /* Samples decoded from RLE */ + uint64_t full_records; /* Full stack records decoded */ + uint64_t suffix_records; /* Suffix match records decoded */ + uint64_t pop_push_records; /* Pop-push records decoded */ + uint64_t total_samples; /* Total samples reconstructed */ + uint64_t stack_reconstructions; /* Number of stack array reconstructions */ +} BinaryReaderStats; + +/* ============================================================================ + * PLATFORM ABSTRACTION + * ============================================================================ */ + +#if defined(__linux__) || defined(__APPLE__) + #include <sys/mman.h> + #include <unistd.h> + #include <sys/stat.h> + #include <fcntl.h> + #define USE_MMAP 1 +#else + #define USE_MMAP 0 +#endif + +/* 64-bit file position support for files larger than 2GB. + * On POSIX: use ftello/fseeko with off_t (already 64-bit on 64-bit systems) + * On Windows: use _ftelli64/_fseeki64 with __int64 */ +#if defined(_WIN32) || defined(_WIN64) + #include <io.h> + typedef __int64 file_offset_t; + #define FTELL64(fp) _ftelli64(fp) + #define FSEEK64(fp, offset, whence) _fseeki64(fp, offset, whence) +#else + /* POSIX - off_t is 64-bit on 64-bit systems, ftello/fseeko handle large files */ + typedef off_t file_offset_t; + #define FTELL64(fp) ftello(fp) + #define FSEEK64(fp, offset, whence) fseeko(fp, offset, whence) +#endif + +/* Forward declare zstd types if available */ +#ifdef HAVE_ZSTD +#include <zstd.h> +#endif + +/* Branch prediction hints - same as Objects/obmalloc.c */ +#if (defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 2))) && defined(__OPTIMIZE__) +# define UNLIKELY(value) __builtin_expect((value), 0) +# define LIKELY(value) __builtin_expect((value), 1) +#else +# define UNLIKELY(value) (value) +# define LIKELY(value) (value) +#endif + +/* ============================================================================ + * BINARY WRITER STRUCTURES + * ============================================================================ */ + +/* zstd compression state (only used if HAVE_ZSTD defined) */ +typedef struct { +#ifdef HAVE_ZSTD + ZSTD_CCtx *cctx; /* Modern API: CCtx and CStream are the same since v1.3.0 */ +#else + void *cctx; /* Placeholder */ +#endif + uint8_t *compressed_buffer; + size_t compressed_buffer_size; +} ZstdCompressor; + +/* Frame entry - combines all frame data for better cache locality. + * Stores full source position (line, end_line, column, end_column) and opcode. + * Delta values are computed during serialization for efficiency. */ +typedef struct { + uint32_t filename_idx; + uint32_t funcname_idx; + int32_t lineno; /* Start line number (-1 for synthetic frames) */ + int32_t end_lineno; /* End line number (-1 if not available) */ + int32_t column; /* Start column in UTF-8 bytes (-1 if not available) */ + int32_t end_column; /* End column in UTF-8 bytes (-1 if not available) */ + uint8_t opcode; /* Python opcode (0-254) or OPCODE_NONE (255) */ +} FrameEntry; + +/* Frame key for hash table lookup - includes all fields for proper deduplication */ +typedef struct { + uint32_t filename_idx; + uint32_t funcname_idx; + int32_t lineno; + int32_t end_lineno; + int32_t column; + int32_t end_column; + uint8_t opcode; +} FrameKey; + +/* Pending RLE sample - buffered for run-length encoding */ +typedef struct { + uint64_t timestamp_delta; + uint8_t status; +} PendingRLESample; + +/* Thread entry - tracks per-thread state for delta encoding */ +typedef struct { + uint64_t thread_id; + uint64_t prev_timestamp; + uint32_t interpreter_id; + + /* Previous stack for delta encoding (frame indices, innermost first) */ + uint32_t *prev_stack; + size_t prev_stack_depth; + size_t prev_stack_capacity; + + /* RLE pending buffer - samples waiting to be written as a repeat group */ + PendingRLESample *pending_rle; + size_t pending_rle_count; + size_t pending_rle_capacity; + int has_pending_rle; /* Flag: do we have buffered repeats? */ +} ThreadEntry; + +/* Main binary writer structure */ +typedef struct { + FILE *fp; + + /* Write buffer for batched I/O */ + uint8_t *write_buffer; + size_t buffer_pos; + size_t buffer_size; + + /* Compression */ + int compression_type; + ZstdCompressor zstd; + + /* Metadata */ + uint64_t start_time_us; + uint64_t sample_interval_us; + uint32_t total_samples; + + /* String hash table: PyObject* -> uint32_t index */ + _Py_hashtable_t *string_hash; + /* String storage: array of UTF-8 encoded strings */ + char **strings; + size_t *string_lengths; + size_t string_count; + size_t string_capacity; + + /* Frame hash table: FrameKey* -> uint32_t index */ + _Py_hashtable_t *frame_hash; + /* Frame storage: combined struct for better cache locality */ + FrameEntry *frame_entries; + size_t frame_count; + size_t frame_capacity; + + /* Thread timestamp tracking for delta encoding - combined for cache locality */ + ThreadEntry *thread_entries; + size_t thread_count; + size_t thread_capacity; + + /* Statistics */ + BinaryWriterStats stats; +} BinaryWriter; + +/* ============================================================================ + * BINARY READER STRUCTURES + * ============================================================================ */ + +/* Per-thread state for stack reconstruction during replay */ +typedef struct { + uint64_t thread_id; + uint32_t interpreter_id; + uint64_t prev_timestamp; + + /* Reconstructed stack buffer (frame indices, innermost first) */ + uint32_t *current_stack; + size_t current_stack_depth; + size_t current_stack_capacity; +} ReaderThreadState; + +/* Main binary reader structure */ +typedef struct { +#if USE_MMAP + uint8_t *mapped_data; + size_t mapped_size; +#else + FILE *fp; + uint8_t *file_data; + size_t file_size; +#endif + + /* Decompression state */ + int compression_type; + /* Note: ZSTD_DCtx is not stored - created/freed during decompression */ + uint8_t *decompressed_data; + size_t decompressed_size; + + /* Header metadata */ + uint8_t py_major; + uint8_t py_minor; + uint8_t py_micro; + int needs_swap; /* Non-zero if file was written on different-endian system */ + uint64_t start_time_us; + uint64_t sample_interval_us; + uint32_t sample_count; + uint32_t thread_count; + uint64_t string_table_offset; + uint64_t frame_table_offset; + + /* Parsed string table: array of Python string objects */ + PyObject **strings; + uint32_t strings_count; + + /* Parsed frame table: array of FrameEntry structures */ + FrameEntry *frames; + uint32_t frames_count; + + /* Sample data region */ + uint8_t *sample_data; + size_t sample_data_size; + + /* Per-thread state for stack reconstruction (used during replay) */ + ReaderThreadState *thread_states; + size_t thread_state_count; + size_t thread_state_capacity; + + /* Statistics */ + BinaryReaderStats stats; +} BinaryReader; + +/* ============================================================================ + * VARINT ENCODING/DECODING (INLINE FOR PERFORMANCE) + * ============================================================================ */ + +/* Encode unsigned 64-bit varint (LEB128). Returns bytes written. */ +static inline size_t +encode_varint_u64(uint8_t *buf, uint64_t value) +{ + /* Fast path for single-byte values (0-127) - very common case */ + if (value < 0x80) { + buf[0] = (uint8_t)value; + return 1; + } + + size_t i = 0; + while (value >= 0x80) { + buf[i++] = (uint8_t)((value & 0x7F) | 0x80); + value >>= 7; + } + buf[i++] = (uint8_t)(value & 0x7F); + return i; +} + +/* Encode unsigned 32-bit varint. Returns bytes written. */ +static inline size_t +encode_varint_u32(uint8_t *buf, uint32_t value) +{ + return encode_varint_u64(buf, value); +} + +/* Encode signed 32-bit varint (zigzag encoding). Returns bytes written. */ +static inline size_t +encode_varint_i32(uint8_t *buf, int32_t value) +{ + /* Zigzag encode: map signed to unsigned */ + uint32_t zigzag = ((uint32_t)value << 1) ^ (uint32_t)(value >> 31); + return encode_varint_u32(buf, zigzag); +} + +/* Decode unsigned 64-bit varint (LEB128). Updates offset only on success. + * On error (overflow or incomplete), offset is NOT updated, allowing callers + * to detect errors via (offset == prev_offset) check. Sets PyErr on error. */ +static inline uint64_t +decode_varint_u64(const uint8_t *data, size_t *offset, size_t max_size) +{ + size_t pos = *offset; + uint64_t result = 0; + int shift = 0; + + /* Fast path for single-byte varints (0-127) - most common case */ + if (LIKELY(pos < max_size && (data[pos] & 0x80) == 0)) { + *offset = pos + 1; + return data[pos]; + } + + while (pos < max_size) { + uint8_t byte = data[pos++]; + result |= (uint64_t)(byte & 0x7F) << shift; + if ((byte & 0x80) == 0) { + *offset = pos; + return result; + } + shift += 7; + if (UNLIKELY(shift >= 64)) { + PyErr_SetString(PyExc_ValueError, "Invalid or incomplete varint in binary data"); + return 0; + } + } + + PyErr_SetString(PyExc_ValueError, "Invalid or incomplete varint in binary data"); + return 0; +} + +/* Decode unsigned 32-bit varint. If value exceeds UINT32_MAX, treats as error. */ +static inline uint32_t +decode_varint_u32(const uint8_t *data, size_t *offset, size_t max_size) +{ + size_t saved_offset = *offset; + uint64_t value = decode_varint_u64(data, offset, max_size); + if (*offset == saved_offset) { + return 0; /* decode_varint_u64 already set PyErr */ + } + if (UNLIKELY(value > UINT32_MAX)) { + *offset = saved_offset; + PyErr_SetString(PyExc_ValueError, "Invalid or incomplete varint in binary data"); + return 0; + } + return (uint32_t)value; +} + +/* Decode signed 32-bit varint (zigzag encoding). */ +static inline int32_t +decode_varint_i32(const uint8_t *data, size_t *offset, size_t max_size) +{ + size_t saved_offset = *offset; + uint32_t zigzag = decode_varint_u32(data, offset, max_size); + if (*offset == saved_offset) { + return 0; /* decode_varint_u32 already set PyErr */ + } + return (int32_t)((zigzag >> 1) ^ -(int32_t)(zigzag & 1)); +} + +/* ============================================================================ + * SHARED UTILITY FUNCTIONS + * ============================================================================ */ + +/* Generic array growth - returns new pointer or NULL (sets PyErr_NoMemory) + * Includes overflow checking for capacity doubling and allocation size. */ +static inline void * +grow_array(void *ptr, size_t *capacity, size_t elem_size) +{ + size_t old_cap = *capacity; + + /* Check for overflow when doubling capacity */ + if (old_cap > SIZE_MAX / 2) { + PyErr_SetString(PyExc_OverflowError, "Array capacity overflow"); + return NULL; + } + size_t new_cap = old_cap * 2; + + /* Check for overflow when calculating allocation size */ + if (new_cap > SIZE_MAX / elem_size) { + PyErr_SetString(PyExc_OverflowError, "Array allocation size overflow"); + return NULL; + } + + void *new_ptr = PyMem_Realloc(ptr, new_cap * elem_size); + if (new_ptr) { + *capacity = new_cap; + } else { + PyErr_NoMemory(); + } + return new_ptr; +} + +static inline int +grow_array_inplace(void **ptr_addr, size_t count, size_t *capacity, size_t elem_size) +{ + if (count < *capacity) { + return 0; + } + void *tmp = grow_array(*ptr_addr, capacity, elem_size); + if (tmp == NULL) { + return -1; + } + *ptr_addr = tmp; + return 0; +} + +#define GROW_ARRAY(ptr, count, cap, type) \ + grow_array_inplace((void**)&(ptr), (count), &(cap), sizeof(type)) + +/* ============================================================================ + * BINARY WRITER API + * ============================================================================ */ + +/* + * Create a new binary writer. + * + * Arguments: + * path: Path to output file + * sample_interval_us: Sampling interval in microseconds + * compression_type: COMPRESSION_NONE or COMPRESSION_ZSTD + * start_time_us: Start timestamp in microseconds (from time.monotonic() * 1e6) + * + * Returns: + * New BinaryWriter* on success, NULL on failure (PyErr set) + */ +BinaryWriter *binary_writer_create( + PyObject *path, + uint64_t sample_interval_us, + int compression_type, + uint64_t start_time_us +); + +/* + * Write a sample to the binary file. + * + * Arguments: + * writer: Writer from binary_writer_create + * stack_frames: List of InterpreterInfo struct sequences + * timestamp_us: Current timestamp in microseconds (from time.monotonic() * 1e6) + * + * Returns: + * 0 on success, -1 on failure (PyErr set) + */ +int binary_writer_write_sample( + BinaryWriter *writer, + PyObject *stack_frames, + uint64_t timestamp_us +); + +/* + * Finalize and close the binary file. + * Writes string/frame tables, footer, and updates header. + * + * Arguments: + * writer: Writer to finalize + * + * Returns: + * 0 on success, -1 on failure (PyErr set) + */ +int binary_writer_finalize(BinaryWriter *writer); + +/* + * Destroy a binary writer and free all resources. + * Safe to call even if writer is partially initialized. + * + * Arguments: + * writer: Writer to destroy (may be NULL) + */ +void binary_writer_destroy(BinaryWriter *writer); + +/* ============================================================================ + * BINARY READER API + * ============================================================================ */ + +/* + * Open a binary file for reading. + * + * Arguments: + * path: Path to input file + * + * Returns: + * New BinaryReader* on success, NULL on failure (PyErr set) + */ +BinaryReader *binary_reader_open(PyObject *path); + +/* + * Replay samples from binary file through a collector. + * + * Arguments: + * reader: Reader from binary_reader_open + * collector: Python collector with collect() method + * progress_callback: Optional callable(current, total) or NULL + * + * Returns: + * Number of samples replayed on success, -1 on failure (PyErr set) + */ +Py_ssize_t binary_reader_replay( + BinaryReader *reader, + PyObject *collector, + PyObject *progress_callback +); + +/* + * Get metadata about the binary file. + * + * Arguments: + * reader: Reader from binary_reader_open + * + * Returns: + * Dict with file metadata on success, NULL on failure (PyErr set) + */ +PyObject *binary_reader_get_info(BinaryReader *reader); + +/* + * Close a binary reader and free all resources. + * + * Arguments: + * reader: Reader to close (may be NULL) + */ +void binary_reader_close(BinaryReader *reader); + +/* ============================================================================ + * STATISTICS FUNCTIONS + * ============================================================================ */ + +/* + * Get writer statistics as a Python dict. + * + * Arguments: + * writer: Writer to get stats from + * + * Returns: + * Dict with statistics on success, NULL on failure (PyErr set) + */ +PyObject *binary_writer_get_stats(BinaryWriter *writer); + +/* + * Get reader statistics as a Python dict. + * + * Arguments: + * reader: Reader to get stats from + * + * Returns: + * Dict with statistics on success, NULL on failure (PyErr set) + */ +PyObject *binary_reader_get_stats(BinaryReader *reader); + +/* ============================================================================ + * UTILITY FUNCTIONS + * ============================================================================ */ + +/* + * Check if zstd compression is available. + * + * Returns: + * 1 if zstd available, 0 otherwise + */ +int binary_io_zstd_available(void); + +/* + * Get the best available compression type. + * + * Returns: + * COMPRESSION_ZSTD if available, COMPRESSION_NONE otherwise + */ +int binary_io_get_best_compression(void); + +#ifdef __cplusplus +} +#endif + +#endif /* Py_BINARY_IO_H */ diff --git a/Modules/_remote_debugging/binary_io_reader.c b/Modules/_remote_debugging/binary_io_reader.c new file mode 100644 index 00000000000000..ce1c3d232c94e0 --- /dev/null +++ b/Modules/_remote_debugging/binary_io_reader.c @@ -0,0 +1,1359 @@ +/****************************************************************************** + * Python Remote Debugging Module - Binary Reader Implementation + * + * High-performance binary file reader for profiling data with optional zstd + * decompression. + ******************************************************************************/ + +#ifndef Py_BUILD_CORE_MODULE +# define Py_BUILD_CORE_MODULE +#endif + +#include "binary_io.h" +#include "_remote_debugging.h" +#include "pycore_bitutils.h" /* _Py_bswap32, _Py_bswap64 for cross-endian reading */ +#include <string.h> + +#ifdef HAVE_ZSTD +#include <zstd.h> +#endif + +/* ============================================================================ + * CONSTANTS FOR BINARY FORMAT SIZES + * ============================================================================ */ + +/* File structure sizes */ +#define MIN_DECOMPRESS_BUFFER_SIZE (64 * 1024) /* Minimum decompression buffer */ + +/* Progress callback frequency */ +#define PROGRESS_CALLBACK_INTERVAL 1000 + +/* ============================================================================ + * BINARY READER IMPLEMENTATION + * ============================================================================ */ + +static inline int +reader_parse_header(BinaryReader *reader, const uint8_t *data, size_t file_size) +{ + if (file_size < FILE_HEADER_PLACEHOLDER_SIZE) { + PyErr_SetString(PyExc_ValueError, "File too small for header"); + return -1; + } + + /* Use memcpy to avoid strict aliasing violations and unaligned access */ + uint32_t magic; + uint32_t version; + memcpy(&magic, &data[HDR_OFF_MAGIC], HDR_SIZE_MAGIC); + memcpy(&version, &data[HDR_OFF_VERSION], HDR_SIZE_VERSION); + + /* Detect endianness from magic number */ + if (magic == BINARY_FORMAT_MAGIC) { + reader->needs_swap = 0; + } else if (magic == BINARY_FORMAT_MAGIC_SWAPPED) { + reader->needs_swap = 1; + version = _Py_bswap32(version); + } else { + PyErr_Format(PyExc_ValueError, "Invalid magic number: 0x%08x", magic); + return -1; + } + + if (version != BINARY_FORMAT_VERSION) { + if (version > BINARY_FORMAT_VERSION && file_size >= HDR_OFF_PY_MICRO + 1) { + /* Newer format - try to read Python version for better error */ + uint8_t py_major = data[HDR_OFF_PY_MAJOR]; + uint8_t py_minor = data[HDR_OFF_PY_MINOR]; + uint8_t py_micro = data[HDR_OFF_PY_MICRO]; + PyErr_Format(PyExc_ValueError, + "Binary file was created with Python %u.%u.%u (format version %u), " + "but this is Python %d.%d.%d (format version %d)", + py_major, py_minor, py_micro, version, + PY_MAJOR_VERSION, PY_MINOR_VERSION, PY_MICRO_VERSION, + BINARY_FORMAT_VERSION); + } else { + PyErr_Format(PyExc_ValueError, + "Unsupported format version %u (this reader supports version %d)", + version, BINARY_FORMAT_VERSION); + } + return -1; + } + + reader->py_major = data[HDR_OFF_PY_MAJOR]; + reader->py_minor = data[HDR_OFF_PY_MINOR]; + reader->py_micro = data[HDR_OFF_PY_MICRO]; + + /* Read header fields with byte-swapping if needed */ + uint64_t start_time_us, sample_interval_us, string_table_offset, frame_table_offset; + uint32_t sample_count, thread_count, compression_type; + + memcpy(&start_time_us, &data[HDR_OFF_START_TIME], HDR_SIZE_START_TIME); + memcpy(&sample_interval_us, &data[HDR_OFF_INTERVAL], HDR_SIZE_INTERVAL); + memcpy(&sample_count, &data[HDR_OFF_SAMPLES], HDR_SIZE_SAMPLES); + memcpy(&thread_count, &data[HDR_OFF_THREADS], HDR_SIZE_THREADS); + memcpy(&string_table_offset, &data[HDR_OFF_STR_TABLE], HDR_SIZE_STR_TABLE); + memcpy(&frame_table_offset, &data[HDR_OFF_FRAME_TABLE], HDR_SIZE_FRAME_TABLE); + memcpy(&compression_type, &data[HDR_OFF_COMPRESSION], HDR_SIZE_COMPRESSION); + + reader->start_time_us = SWAP64_IF(reader->needs_swap, start_time_us); + reader->sample_interval_us = SWAP64_IF(reader->needs_swap, sample_interval_us); + reader->sample_count = SWAP32_IF(reader->needs_swap, sample_count); + reader->thread_count = SWAP32_IF(reader->needs_swap, thread_count); + reader->string_table_offset = SWAP64_IF(reader->needs_swap, string_table_offset); + reader->frame_table_offset = SWAP64_IF(reader->needs_swap, frame_table_offset); + reader->compression_type = (int)SWAP32_IF(reader->needs_swap, compression_type); + + return 0; +} + +static inline int +reader_parse_footer(BinaryReader *reader, const uint8_t *data, size_t file_size) +{ + if (file_size < FILE_FOOTER_SIZE) { + PyErr_SetString(PyExc_ValueError, "File too small for footer"); + return -1; + } + + const uint8_t *footer = data + file_size - FILE_FOOTER_SIZE; + /* Use memcpy to avoid strict aliasing violations */ + uint32_t strings_count, frames_count; + memcpy(&strings_count, &footer[FTR_OFF_STRINGS], FTR_SIZE_STRINGS); + memcpy(&frames_count, &footer[FTR_OFF_FRAMES], FTR_SIZE_FRAMES); + + reader->strings_count = SWAP32_IF(reader->needs_swap, strings_count); + reader->frames_count = SWAP32_IF(reader->needs_swap, frames_count); + + return 0; +} + +#ifdef HAVE_ZSTD +/* Maximum decompression buffer size to prevent memory exhaustion (1GB) */ +#define MAX_DECOMPRESS_SIZE (1ULL << 30) + +static inline int +reader_decompress_samples(BinaryReader *reader, const uint8_t *data) +{ + size_t compressed_size = reader->string_table_offset - FILE_HEADER_PLACEHOLDER_SIZE; + const uint8_t *compressed_data = data + FILE_HEADER_PLACEHOLDER_SIZE; + + /* Validate compressed data region */ + if (reader->string_table_offset < FILE_HEADER_PLACEHOLDER_SIZE) { + PyErr_SetString(PyExc_ValueError, "Invalid string table offset"); + return -1; + } + + ZSTD_DCtx *dctx = ZSTD_createDCtx(); + if (!dctx) { + PyErr_SetString(PyExc_MemoryError, "Failed to create zstd decompression context"); + return -1; + } + + /* Try to get exact decompressed size from frame header for optimal allocation */ + unsigned long long frame_content_size = ZSTD_getFrameContentSize(compressed_data, compressed_size); + size_t alloc_size; + + if (frame_content_size == ZSTD_CONTENTSIZE_ERROR) { + /* Corrupted frame header - fail early */ + ZSTD_freeDCtx(dctx); + PyErr_SetString(PyExc_ValueError, "Corrupted zstd frame header"); + return -1; + } else if (frame_content_size != ZSTD_CONTENTSIZE_UNKNOWN && + frame_content_size <= SIZE_MAX && + frame_content_size <= MAX_DECOMPRESS_SIZE) { + alloc_size = (size_t)frame_content_size; + } else { + alloc_size = ZSTD_DStreamOutSize() * 4; + if (alloc_size < MIN_DECOMPRESS_BUFFER_SIZE) { + alloc_size = MIN_DECOMPRESS_BUFFER_SIZE; + } + } + + reader->decompressed_data = PyMem_Malloc(alloc_size); + if (!reader->decompressed_data) { + ZSTD_freeDCtx(dctx); + PyErr_NoMemory(); + return -1; + } + + ZSTD_inBuffer input = { compressed_data, compressed_size, 0 }; + size_t total_output = 0; + size_t last_result = 0; + + while (input.pos < input.size) { + if (total_output >= alloc_size) { + /* Check for overflow before doubling */ + if (alloc_size > SIZE_MAX / 2 || alloc_size * 2 > MAX_DECOMPRESS_SIZE) { + PyMem_Free(reader->decompressed_data); + reader->decompressed_data = NULL; + ZSTD_freeDCtx(dctx); + PyErr_SetString(PyExc_MemoryError, "Decompressed data exceeds maximum size"); + return -1; + } + size_t new_size = alloc_size * 2; + uint8_t *new_buf = PyMem_Realloc(reader->decompressed_data, new_size); + if (!new_buf) { + PyMem_Free(reader->decompressed_data); + reader->decompressed_data = NULL; + ZSTD_freeDCtx(dctx); + PyErr_NoMemory(); + return -1; + } + reader->decompressed_data = new_buf; + alloc_size = new_size; + } + + ZSTD_outBuffer output = { + reader->decompressed_data + total_output, + alloc_size - total_output, + 0 + }; + + last_result = ZSTD_decompressStream(dctx, &output, &input); + if (ZSTD_isError(last_result)) { + PyMem_Free(reader->decompressed_data); + reader->decompressed_data = NULL; + ZSTD_freeDCtx(dctx); + PyErr_Format(PyExc_ValueError, "zstd decompression error: %s", + ZSTD_getErrorName(last_result)); + return -1; + } + + total_output += output.pos; + } + + /* Verify decompression is complete (last_result == 0 means frame is complete) */ + if (last_result != 0) { + PyMem_Free(reader->decompressed_data); + reader->decompressed_data = NULL; + ZSTD_freeDCtx(dctx); + PyErr_SetString(PyExc_ValueError, "Incomplete zstd frame: data may be truncated"); + return -1; + } + + ZSTD_freeDCtx(dctx); + reader->decompressed_size = total_output; + reader->sample_data = reader->decompressed_data; + reader->sample_data_size = reader->decompressed_size; + + return 0; +} +#endif + +static inline int +reader_parse_string_table(BinaryReader *reader, const uint8_t *data, size_t file_size) +{ + reader->strings = PyMem_Calloc(reader->strings_count, sizeof(PyObject *)); + if (!reader->strings && reader->strings_count > 0) { + PyErr_NoMemory(); + return -1; + } + + size_t offset = reader->string_table_offset; + for (uint32_t i = 0; i < reader->strings_count; i++) { + size_t prev_offset = offset; + uint32_t str_len = decode_varint_u32(data, &offset, file_size); + if (offset == prev_offset) { + PyErr_SetString(PyExc_ValueError, "Malformed varint in string table"); + return -1; + } + if (offset > file_size || str_len > file_size - offset) { + PyErr_SetString(PyExc_ValueError, "String table overflow"); + return -1; + } + + reader->strings[i] = PyUnicode_DecodeUTF8((char *)&data[offset], str_len, "replace"); + if (!reader->strings[i]) { + return -1; + } + offset += str_len; + } + + return 0; +} + +static inline int +reader_parse_frame_table(BinaryReader *reader, const uint8_t *data, size_t file_size) +{ + /* Check for integer overflow in allocation size calculation. */ +#if SIZEOF_SIZE_T < 8 + if (reader->frames_count > SIZE_MAX / sizeof(FrameEntry)) { + PyErr_SetString(PyExc_OverflowError, "Frame count too large for allocation"); + return -1; + } +#endif + + size_t alloc_size = (size_t)reader->frames_count * sizeof(FrameEntry); + reader->frames = PyMem_Malloc(alloc_size); + if (!reader->frames && reader->frames_count > 0) { + PyErr_NoMemory(); + return -1; + } + + size_t offset = reader->frame_table_offset; + for (uint32_t i = 0; i < reader->frames_count; i++) { + FrameEntry *frame = &reader->frames[i]; + size_t prev_offset; + + prev_offset = offset; + frame->filename_idx = decode_varint_u32(data, &offset, file_size); + if (offset == prev_offset) { + PyErr_SetString(PyExc_ValueError, "Malformed varint in frame table (filename)"); + return -1; + } + + prev_offset = offset; + frame->funcname_idx = decode_varint_u32(data, &offset, file_size); + if (offset == prev_offset) { + PyErr_SetString(PyExc_ValueError, "Malformed varint in frame table (funcname)"); + return -1; + } + + prev_offset = offset; + frame->lineno = decode_varint_i32(data, &offset, file_size); + if (offset == prev_offset) { + PyErr_SetString(PyExc_ValueError, "Malformed varint in frame table (lineno)"); + return -1; + } + + prev_offset = offset; + int32_t end_lineno_delta = decode_varint_i32(data, &offset, file_size); + if (offset == prev_offset) { + PyErr_SetString(PyExc_ValueError, "Malformed varint in frame table (end_lineno_delta)"); + return -1; + } + /* Reconstruct end_lineno from delta. If lineno is -1, result is -1. */ + if (frame->lineno == LOCATION_NOT_AVAILABLE) { + frame->end_lineno = LOCATION_NOT_AVAILABLE; + } else { + frame->end_lineno = frame->lineno + end_lineno_delta; + } + + prev_offset = offset; + frame->column = decode_varint_i32(data, &offset, file_size); + if (offset == prev_offset) { + PyErr_SetString(PyExc_ValueError, "Malformed varint in frame table (column)"); + return -1; + } + + prev_offset = offset; + int32_t end_column_delta = decode_varint_i32(data, &offset, file_size); + if (offset == prev_offset) { + PyErr_SetString(PyExc_ValueError, "Malformed varint in frame table (end_column_delta)"); + return -1; + } + /* Reconstruct end_column from delta. If column is -1, result is -1. */ + if (frame->column == LOCATION_NOT_AVAILABLE) { + frame->end_column = LOCATION_NOT_AVAILABLE; + } else { + frame->end_column = frame->column + end_column_delta; + } + + /* Read opcode byte */ + if (offset >= file_size) { + PyErr_SetString(PyExc_ValueError, "Unexpected end of frame table (opcode)"); + return -1; + } + frame->opcode = data[offset++]; + } + + return 0; +} + +BinaryReader * +binary_reader_open(PyObject *path) +{ + BinaryReader *reader = PyMem_Calloc(1, sizeof(BinaryReader)); + if (!reader) { + PyErr_NoMemory(); + return NULL; + } + +#if USE_MMAP + /* Open with mmap on Unix */ + FILE *fp = Py_fopen(path, "rb"); + if (!fp) { + goto error; + } + int fd = fileno(fp); + + struct stat st; + if (fstat(fd, &st) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + Py_fclose(fp); + goto error; + } + if (st.st_size < 0) { + PyErr_SetString(PyExc_IOError, "Invalid negative file size"); + Py_fclose(fp); + goto error; + } + if ((uintmax_t)st.st_size > SIZE_MAX) { + PyErr_SetString(PyExc_OverflowError, "File is too large to map"); + Py_fclose(fp); + goto error; + } + reader->mapped_size = st.st_size; + if (reader->mapped_size == 0) { + PyErr_SetString(PyExc_ValueError, "File too small for header"); + Py_fclose(fp); + goto error; + } + + /* Map the file into memory. + * MAP_POPULATE (Linux-only) pre-faults all pages at mmap time, which: + * - Catches issues (e.g., file truncation) immediately rather than as SIGBUS during reads + * - Eliminates page faults during subsequent reads for better performance + */ +#ifdef __linux__ + reader->mapped_data = mmap(NULL, reader->mapped_size, PROT_READ, + MAP_PRIVATE | MAP_POPULATE, fd, 0); +#else + reader->mapped_data = mmap(NULL, reader->mapped_size, PROT_READ, + MAP_PRIVATE, fd, 0); +#endif + if (reader->mapped_data == MAP_FAILED) { + reader->mapped_data = NULL; + PyErr_SetFromErrno(PyExc_IOError); + Py_fclose(fp); + goto error; + } + + /* Hint sequential access pattern - failures are non-fatal */ + (void)madvise(reader->mapped_data, reader->mapped_size, MADV_SEQUENTIAL); + + /* Pre-fetch pages into memory - failures are non-fatal. + * Complements MAP_POPULATE on Linux, provides benefit on macOS. */ + (void)madvise(reader->mapped_data, reader->mapped_size, MADV_WILLNEED); + + /* Use transparent huge pages for large files to reduce TLB misses. + * Only beneficial for files >= 32MB where TLB pressure matters. */ +#ifdef MADV_HUGEPAGE + if (reader->mapped_size >= (32 * 1024 * 1024)) { + (void)madvise(reader->mapped_data, reader->mapped_size, MADV_HUGEPAGE); + } +#endif + + /* Add file descriptor-level hints for better kernel I/O scheduling */ +#if defined(__linux__) && defined(POSIX_FADV_SEQUENTIAL) + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); + if (reader->mapped_size > (64 * 1024 * 1024)) { + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED); + } +#endif + + if (Py_fclose(fp) != 0) { + PyErr_SetFromErrno(PyExc_IOError); + goto error; + } + + uint8_t *data = reader->mapped_data; + size_t file_size = reader->mapped_size; +#else + /* Use stdio on Windows */ + reader->fp = Py_fopen(path, "rb"); + if (!reader->fp) { + goto error; + } + + if (FSEEK64(reader->fp, 0, SEEK_END) != 0) { + PyErr_SetFromErrno(PyExc_IOError); + goto error; + } + file_offset_t file_size_off = FTELL64(reader->fp); + if (file_size_off < 0) { + PyErr_SetFromErrno(PyExc_IOError); + goto error; + } + if ((uint64_t)file_size_off > SIZE_MAX) { + PyErr_SetString(PyExc_OverflowError, "File is too large to read"); + goto error; + } + reader->file_size = (size_t)file_size_off; + if (reader->file_size == 0) { + PyErr_SetString(PyExc_ValueError, "File too small for header"); + goto error; + } + if (FSEEK64(reader->fp, 0, SEEK_SET) != 0) { + PyErr_SetFromErrno(PyExc_IOError); + goto error; + } + + reader->file_data = PyMem_Malloc(reader->file_size); + if (!reader->file_data) { + PyErr_NoMemory(); + goto error; + } + + size_t nread = fread(reader->file_data, 1, reader->file_size, reader->fp); + if (nread != reader->file_size) { + int err = errno; + if (ferror(reader->fp) && err != 0) { + errno = err; + PyErr_SetFromErrno(PyExc_IOError); + } + else { + PyErr_Format(PyExc_ValueError, + "Unexpected end of file: read %zu of %zu bytes", + nread, reader->file_size); + } + goto error; + } + + uint8_t *data = reader->file_data; + size_t file_size = reader->file_size; +#endif + + /* Parse header and footer */ + if (reader_parse_header(reader, data, file_size) < 0) { + goto error; + } + if (reader_parse_footer(reader, data, file_size) < 0) { + goto error; + } + + /* Validate table offsets are within file bounds */ + if (reader->string_table_offset > file_size) { + PyErr_Format(PyExc_ValueError, + "Invalid string table offset: %llu exceeds file size %zu", + (unsigned long long)reader->string_table_offset, file_size); + goto error; + } + if (reader->frame_table_offset > file_size) { + PyErr_Format(PyExc_ValueError, + "Invalid frame table offset: %llu exceeds file size %zu", + (unsigned long long)reader->frame_table_offset, file_size); + goto error; + } + if (reader->string_table_offset < FILE_HEADER_PLACEHOLDER_SIZE) { + PyErr_Format(PyExc_ValueError, + "Invalid string table offset: %llu is before data section", + (unsigned long long)reader->string_table_offset); + goto error; + } + if (reader->frame_table_offset < FILE_HEADER_PLACEHOLDER_SIZE) { + PyErr_Format(PyExc_ValueError, + "Invalid frame table offset: %llu is before data section", + (unsigned long long)reader->frame_table_offset); + goto error; + } + if (reader->string_table_offset > reader->frame_table_offset) { + PyErr_Format(PyExc_ValueError, + "Invalid table offsets: string table (%llu) is after frame table (%llu)", + (unsigned long long)reader->string_table_offset, + (unsigned long long)reader->frame_table_offset); + goto error; + } + + /* Handle compressed data */ + if (reader->compression_type == COMPRESSION_ZSTD) { +#ifdef HAVE_ZSTD + if (reader_decompress_samples(reader, data) < 0) { + goto error; + } +#else + PyErr_SetString(PyExc_RuntimeError, + "File uses zstd compression but zstd support not compiled in"); + goto error; +#endif + } else { + reader->sample_data = data + FILE_HEADER_PLACEHOLDER_SIZE; + reader->sample_data_size = reader->string_table_offset - FILE_HEADER_PLACEHOLDER_SIZE; + } + + /* Parse string and frame tables */ + if (reader_parse_string_table(reader, data, file_size) < 0) { + goto error; + } + if (reader_parse_frame_table(reader, data, file_size) < 0) { + goto error; + } + + return reader; + +error: + binary_reader_close(reader); + return NULL; +} + +/* Get or create reader thread state for stack reconstruction */ +static ReaderThreadState * +reader_get_or_create_thread_state(BinaryReader *reader, uint64_t thread_id, + uint32_t interpreter_id) +{ + /* Search existing threads (key is thread_id + interpreter_id) */ + for (size_t i = 0; i < reader->thread_state_count; i++) { + if (reader->thread_states[i].thread_id == thread_id && + reader->thread_states[i].interpreter_id == interpreter_id) { + return &reader->thread_states[i]; + } + } + + if (reader->thread_state_count >= reader->thread_count) { + PyErr_Format(PyExc_ValueError, + "Invalid thread count: sample data contains more unique threads than declared in header " + "(declared %u, found at least %zu)", + reader->thread_count, reader->thread_state_count + 1); + return NULL; + } + + if (!reader->thread_states) { + reader->thread_state_capacity = 16; + reader->thread_states = PyMem_Calloc(reader->thread_state_capacity, sizeof(ReaderThreadState)); + if (!reader->thread_states) { + PyErr_NoMemory(); + return NULL; + } + } else if (reader->thread_state_count >= reader->thread_state_capacity) { + ReaderThreadState *new_states = grow_array(reader->thread_states, + &reader->thread_state_capacity, + sizeof(ReaderThreadState)); + if (!new_states) { + return NULL; + } + reader->thread_states = new_states; + } + + ReaderThreadState *ts = &reader->thread_states[reader->thread_state_count]; + memset(ts, 0, sizeof(ReaderThreadState)); + ts->thread_id = thread_id; + ts->interpreter_id = interpreter_id; + ts->prev_timestamp = reader->start_time_us; + ts->current_stack_capacity = MAX_STACK_DEPTH; + ts->current_stack = PyMem_Malloc(ts->current_stack_capacity * sizeof(uint32_t)); + if (!ts->current_stack) { + PyErr_NoMemory(); + return NULL; + } + // Increment count only after successful allocation to avoid + // leaving a half-initialized entry visible to future lookups + reader->thread_state_count++; + return ts; +} + +/* ============================================================================ + * STACK DECODING HELPERS + * ============================================================================ */ + +/* Validate that final_depth fits in the stack buffer. + * Uses uint64_t to prevent overflow on 32-bit platforms. */ +static inline int +validate_stack_depth(ReaderThreadState *ts, uint64_t final_depth) +{ + if (final_depth > ts->current_stack_capacity) { + PyErr_Format(PyExc_ValueError, + "Final stack depth %llu exceeds capacity %zu", + (unsigned long long)final_depth, ts->current_stack_capacity); + return -1; + } + return 0; +} + +/* Decode a full stack from sample data. + * Updates ts->current_stack and ts->current_stack_depth. + * Returns 0 on success, -1 on error (bounds violation). */ +static inline int +decode_stack_full(ReaderThreadState *ts, const uint8_t *data, + size_t *offset, size_t max_size) +{ + size_t prev_offset = *offset; + uint32_t depth = decode_varint_u32(data, offset, max_size); + if (*offset == prev_offset) { + return -1; + } + + /* Validate depth against capacity to prevent buffer overflow */ + if (depth > ts->current_stack_capacity) { + PyErr_Format(PyExc_ValueError, + "Stack depth %u exceeds capacity %zu", depth, ts->current_stack_capacity); + return -1; + } + + ts->current_stack_depth = depth; + for (uint32_t i = 0; i < depth; i++) { + size_t prev_offset = *offset; + ts->current_stack[i] = decode_varint_u32(data, offset, max_size); + if (*offset == prev_offset) { + return -1; + } + } + return 0; +} + +/* Decode a suffix-encoded stack from sample data. + * The suffix encoding shares frames from the bottom of the previous stack. + * Returns 0 on success, -1 on error (bounds violation). */ +static inline int +decode_stack_suffix(ReaderThreadState *ts, const uint8_t *data, + size_t *offset, size_t max_size) +{ + size_t prev_offset = *offset; + uint32_t shared = decode_varint_u32(data, offset, max_size); + if (*offset == prev_offset) { + return -1; + } + prev_offset = *offset; + uint32_t new_count = decode_varint_u32(data, offset, max_size); + if (*offset == prev_offset) { + return -1; + } + + /* Validate shared doesn't exceed current stack depth */ + if (shared > ts->current_stack_depth) { + PyErr_Format(PyExc_ValueError, + "Shared count %u exceeds current stack depth %zu", + shared, ts->current_stack_depth); + return -1; + } + + /* Use uint64_t to prevent overflow on 32-bit platforms */ + uint64_t final_depth = (uint64_t)shared + new_count; + if (validate_stack_depth(ts, final_depth) < 0) { + return -1; + } + + /* Move shared frames (from bottom of stack) to make room for new frames at the top */ + if (new_count > 0 && shared > 0) { + /* Defensive check: ensure subtraction won't underflow. + * This should already be guaranteed by the check above, but we add + * this assertion as defense-in-depth against stack corruption. */ + if (ts->current_stack_depth < shared) { + PyErr_SetString(PyExc_ValueError, + "Internal error: stack corruption detected in suffix decoding"); + return -1; + } + size_t prev_shared_start = ts->current_stack_depth - shared; + memmove(&ts->current_stack[new_count], + &ts->current_stack[prev_shared_start], + shared * sizeof(uint32_t)); + } + + for (uint32_t i = 0; i < new_count; i++) { + size_t prev_offset = *offset; + ts->current_stack[i] = decode_varint_u32(data, offset, max_size); + if (*offset == prev_offset) { + return -1; + } + } + ts->current_stack_depth = final_depth; + return 0; +} + +/* Decode a pop-push encoded stack from sample data. + * Pops frames from the top and pushes new frames. + * Returns 0 on success, -1 on error (bounds violation). */ +static inline int +decode_stack_pop_push(ReaderThreadState *ts, const uint8_t *data, + size_t *offset, size_t max_size) +{ + size_t prev_offset = *offset; + uint32_t pop = decode_varint_u32(data, offset, max_size); + if (*offset == prev_offset) { + return -1; + } + prev_offset = *offset; + uint32_t push = decode_varint_u32(data, offset, max_size); + if (*offset == prev_offset) { + return -1; + } + size_t keep = (ts->current_stack_depth > pop) ? ts->current_stack_depth - pop : 0; + + /* Use uint64_t to prevent overflow on 32-bit platforms */ + uint64_t final_depth = (uint64_t)keep + push; + if (validate_stack_depth(ts, final_depth) < 0) { + return -1; + } + + /* Move kept frames (from bottom of stack) to make room for new frames at the top. + * Even when push == 0, we need to move kept frames to index 0 if pop > 0. */ + if (keep > 0) { + memmove(&ts->current_stack[push], + &ts->current_stack[pop], + keep * sizeof(uint32_t)); + } + + for (uint32_t i = 0; i < push; i++) { + size_t prev_offset = *offset; + ts->current_stack[i] = decode_varint_u32(data, offset, max_size); + /* If offset didn't advance, varint decoding failed */ + if (*offset == prev_offset) { + return -1; + } + } + ts->current_stack_depth = final_depth; + return 0; +} + +/* Build a Python list of FrameInfo objects from frame indices */ +static PyObject * +build_frame_list(RemoteDebuggingState *state, BinaryReader *reader, + const uint32_t *frame_indices, size_t stack_depth) +{ + PyObject *frame_list = PyList_New(stack_depth); + if (!frame_list) { + return NULL; + } + + for (size_t k = 0; k < stack_depth; k++) { + uint32_t frame_idx = frame_indices[k]; + if (frame_idx >= reader->frames_count) { + PyErr_Format(PyExc_ValueError, "Invalid frame index: %u", frame_idx); + goto error; + } + + FrameEntry *frame = &reader->frames[frame_idx]; + + if (frame->filename_idx >= reader->strings_count || + frame->funcname_idx >= reader->strings_count) { + PyErr_SetString(PyExc_ValueError, "Invalid string index in frame"); + goto error; + } + + PyObject *frame_info = PyStructSequence_New(state->FrameInfo_Type); + if (!frame_info) { + goto error; + } + + /* Build location tuple with full position info */ + PyObject *location; + if (frame->lineno != LOCATION_NOT_AVAILABLE) { + location = Py_BuildValue("(iiii)", + frame->lineno, + frame->end_lineno, + frame->column, + frame->end_column); + if (!location) { + Py_DECREF(frame_info); + goto error; + } + } + else { + location = Py_NewRef(Py_None); + } + + /* Build opcode object */ + PyObject *opcode_obj; + if (frame->opcode != OPCODE_NONE) { + opcode_obj = PyLong_FromLong(frame->opcode); + if (!opcode_obj) { + Py_DECREF(location); + Py_DECREF(frame_info); + goto error; + } + } + else { + opcode_obj = Py_NewRef(Py_None); + } + + PyStructSequence_SetItem(frame_info, 0, Py_NewRef(reader->strings[frame->filename_idx])); + PyStructSequence_SetItem(frame_info, 1, location); + PyStructSequence_SetItem(frame_info, 2, Py_NewRef(reader->strings[frame->funcname_idx])); + PyStructSequence_SetItem(frame_info, 3, opcode_obj); + PyList_SET_ITEM(frame_list, k, frame_info); + } + + return frame_list; + +error: + Py_DECREF(frame_list); + return NULL; +} + +/* Helper to build sample_list from frame indices (shared by emit functions) */ +static PyObject * +build_sample_list(RemoteDebuggingState *state, BinaryReader *reader, + uint64_t thread_id, uint32_t interpreter_id, uint8_t status, + const uint32_t *frame_indices, size_t stack_depth) +{ + PyObject *frame_list = NULL, *thread_info = NULL, *thread_list = NULL; + PyObject *interp_info = NULL, *sample_list = NULL; + + frame_list = build_frame_list(state, reader, frame_indices, stack_depth); + if (!frame_list) { + goto error; + } + + thread_info = PyStructSequence_New(state->ThreadInfo_Type); + if (!thread_info) { + goto error; + } + PyObject *tid = PyLong_FromUnsignedLongLong(thread_id); + if (!tid) { + goto error; + } + PyObject *st = PyLong_FromLong(status); + if (!st) { + Py_DECREF(tid); + goto error; + } + PyStructSequence_SetItem(thread_info, 0, tid); + PyStructSequence_SetItem(thread_info, 1, st); + PyStructSequence_SetItem(thread_info, 2, frame_list); + frame_list = NULL; /* ownership transferred */ + + thread_list = PyList_New(1); + if (!thread_list) { + goto error; + } + PyList_SET_ITEM(thread_list, 0, thread_info); + thread_info = NULL; + + interp_info = PyStructSequence_New(state->InterpreterInfo_Type); + if (!interp_info) { + goto error; + } + PyObject *iid = PyLong_FromUnsignedLong(interpreter_id); + if (!iid) { + goto error; + } + PyStructSequence_SetItem(interp_info, 0, iid); + PyStructSequence_SetItem(interp_info, 1, thread_list); + thread_list = NULL; + + sample_list = PyList_New(1); + if (!sample_list) { + goto error; + } + PyList_SET_ITEM(sample_list, 0, interp_info); + return sample_list; + +error: + Py_XDECREF(sample_list); + Py_XDECREF(interp_info); + Py_XDECREF(thread_list); + Py_XDECREF(thread_info); + Py_XDECREF(frame_list); + return NULL; +} + +/* Helper to emit a sample to the collector. timestamps_list is borrowed. */ +static int +emit_sample(RemoteDebuggingState *state, PyObject *collector, + uint64_t thread_id, uint32_t interpreter_id, uint8_t status, + const uint32_t *frame_indices, size_t stack_depth, + BinaryReader *reader, PyObject *timestamps_list) +{ + PyObject *sample_list = build_sample_list(state, reader, thread_id, + interpreter_id, status, + frame_indices, stack_depth); + if (!sample_list) { + return -1; + } + + PyObject *result = PyObject_CallMethod(collector, "collect", "OO", sample_list, timestamps_list); + Py_DECREF(sample_list); + + if (!result) { + return -1; + } + Py_DECREF(result); + return 0; +} + +/* Helper to trim timestamp list and emit batch. Returns 0 on success, -1 on error. */ +static int +emit_batch(RemoteDebuggingState *state, PyObject *collector, + uint64_t thread_id, uint32_t interpreter_id, uint8_t status, + const uint32_t *frame_indices, size_t stack_depth, + BinaryReader *reader, PyObject *timestamps_list, Py_ssize_t actual_size) +{ + /* Trim list to actual size */ + if (PyList_SetSlice(timestamps_list, actual_size, PyList_GET_SIZE(timestamps_list), NULL) < 0) { + return -1; + } + return emit_sample(state, collector, thread_id, interpreter_id, status, + frame_indices, stack_depth, reader, timestamps_list); +} + +/* Helper to invoke progress callback, returns -1 on error */ +static inline int +invoke_progress_callback(PyObject *callback, Py_ssize_t current, uint32_t total) +{ + if (callback && callback != Py_None) { + PyObject *result = PyObject_CallFunction(callback, "nI", current, total); + if (result) { + Py_DECREF(result); + } else { + return -1; + } + } + return 0; +} + +Py_ssize_t +binary_reader_replay(BinaryReader *reader, PyObject *collector, PyObject *progress_callback) +{ + PyObject *collect_method; + int has_collect = PyObject_GetOptionalAttrString(collector, "collect", &collect_method); + if (has_collect < 0) { + return -1; + } + if (has_collect == 0) { + PyErr_SetString(PyExc_TypeError, "Collector must have a collect() method"); + return -1; + } + Py_DECREF(collect_method); + + /* Get module state for struct sequence types */ + PyObject *module = PyImport_ImportModule("_remote_debugging"); + if (!module) { + return -1; + } + RemoteDebuggingState *state = RemoteDebugging_GetState(module); + Py_DECREF(module); + + if (!state) { + PyErr_SetString(PyExc_RuntimeError, "Failed to get module state"); + return -1; + } + + size_t offset = 0; + Py_ssize_t replayed = 0; + + /* Initial progress callback at 0% */ + if (invoke_progress_callback(progress_callback, 0, reader->sample_count) < 0) { + return -1; + } + + while (offset < reader->sample_data_size) { + /* Read thread_id (8 bytes) + interpreter_id (4 bytes) + encoding byte */ + if (reader->sample_data_size - offset < SAMPLE_HEADER_FIXED_SIZE) { + PyErr_Format(PyExc_ValueError, + "Truncated sample data: %zu trailing bytes", + reader->sample_data_size - offset); + return -1; + } + + /* Use memcpy to avoid strict aliasing violations, then byte-swap if needed */ + uint64_t thread_id_raw; + uint32_t interpreter_id_raw; + memcpy(&thread_id_raw, &reader->sample_data[offset], SMP_SIZE_THREAD_ID); + offset += SMP_SIZE_THREAD_ID; + + memcpy(&interpreter_id_raw, &reader->sample_data[offset], SMP_SIZE_INTERPRETER_ID); + offset += SMP_SIZE_INTERPRETER_ID; + + uint64_t thread_id = SWAP64_IF(reader->needs_swap, thread_id_raw); + uint32_t interpreter_id = SWAP32_IF(reader->needs_swap, interpreter_id_raw); + + /* Get or create thread state for reconstruction */ + ReaderThreadState *ts = reader_get_or_create_thread_state(reader, thread_id, interpreter_id); + if (!ts) { + return -1; + } + + /* Read encoding byte */ + uint8_t encoding = reader->sample_data[offset++]; + + switch (encoding) { + case STACK_REPEAT: { + /* RLE repeat: [count: varint] [delta: varint, status: 1]... */ + size_t prev_offset = offset; + uint32_t count = decode_varint_u32(reader->sample_data, &offset, reader->sample_data_size); + /* Detect varint decode failure */ + if (offset == prev_offset) { + PyErr_SetString(PyExc_ValueError, "Malformed varint for RLE count"); + return -1; + } + + /* Validate RLE count to prevent DoS from malicious files. + * Each RLE sample needs at least 2 bytes (1 byte min varint + 1 status byte). + * Also reject absurdly large counts that would exhaust memory. */ + size_t remaining_data = reader->sample_data_size - offset; + size_t max_possible_samples = remaining_data / 2; + if (count > max_possible_samples) { + PyErr_Format(PyExc_ValueError, + "Invalid RLE count %u exceeds maximum possible %zu for remaining data", + count, max_possible_samples); + return -1; + } + if ((uint64_t)count > (uint64_t)PY_SSIZE_T_MAX - (uint64_t)replayed) { + PyErr_SetString(PyExc_OverflowError, + "Sample count exceeds Py_ssize_t maximum"); + return -1; + } + + reader->stats.repeat_records++; + reader->stats.repeat_samples += count; + + /* Process RLE samples, batching by status */ + PyObject *timestamps_list = NULL; + uint8_t batch_status = 0; + Py_ssize_t batch_idx = 0; + + for (uint32_t i = 0; i < count; i++) { + size_t delta_prev_offset = offset; + uint64_t delta = decode_varint_u64(reader->sample_data, &offset, reader->sample_data_size); + if (offset == delta_prev_offset) { + Py_XDECREF(timestamps_list); + PyErr_SetString(PyExc_ValueError, "Malformed varint in RLE sample data"); + return -1; + } + if (offset >= reader->sample_data_size) { + Py_XDECREF(timestamps_list); + PyErr_SetString(PyExc_ValueError, "Unexpected end of sample data in RLE"); + return -1; + } + uint8_t status = reader->sample_data[offset++]; + ts->prev_timestamp += delta; + + /* Start new batch on first sample or status change */ + if (i == 0 || status != batch_status) { + if (timestamps_list) { + int rc = emit_batch(state, collector, thread_id, interpreter_id, + batch_status, ts->current_stack, ts->current_stack_depth, + reader, timestamps_list, batch_idx); + Py_DECREF(timestamps_list); + if (rc < 0) { + return -1; + } + } + timestamps_list = PyList_New(count - i); + if (!timestamps_list) { + return -1; + } + batch_status = status; + batch_idx = 0; + } + + PyObject *ts_obj = PyLong_FromUnsignedLongLong(ts->prev_timestamp); + if (!ts_obj) { + Py_DECREF(timestamps_list); + return -1; + } + PyList_SET_ITEM(timestamps_list, batch_idx++, ts_obj); + } + + /* Emit final batch */ + if (timestamps_list) { + int rc = emit_batch(state, collector, thread_id, interpreter_id, + batch_status, ts->current_stack, ts->current_stack_depth, + reader, timestamps_list, batch_idx); + Py_DECREF(timestamps_list); + if (rc < 0) { + return -1; + } + } + + replayed += count; + reader->stats.total_samples += count; + + /* Progress callback after batch */ + if (replayed % PROGRESS_CALLBACK_INTERVAL < count) { + if (invoke_progress_callback(progress_callback, replayed, reader->sample_count) < 0) { + return -1; + } + } + break; + } + + case STACK_FULL: + case STACK_SUFFIX: + case STACK_POP_PUSH: { + /* All three encodings share: [delta: varint] [status: 1] ... */ + size_t prev_offset = offset; + uint64_t delta = decode_varint_u64(reader->sample_data, &offset, reader->sample_data_size); + /* Detect varint decode failure: offset unchanged means error */ + if (offset == prev_offset) { + PyErr_SetString(PyExc_ValueError, "Malformed varint in sample data"); + return -1; + } + if (offset >= reader->sample_data_size) { + PyErr_SetString(PyExc_ValueError, "Unexpected end of sample data"); + return -1; + } + uint8_t status = reader->sample_data[offset++]; + ts->prev_timestamp += delta; + + if (encoding == STACK_FULL) { + if (decode_stack_full(ts, reader->sample_data, &offset, reader->sample_data_size) < 0) { + return -1; + } + reader->stats.full_records++; + } else if (encoding == STACK_SUFFIX) { + if (decode_stack_suffix(ts, reader->sample_data, &offset, reader->sample_data_size) < 0) { + return -1; + } + reader->stats.suffix_records++; + } else { /* STACK_POP_PUSH */ + if (decode_stack_pop_push(ts, reader->sample_data, &offset, reader->sample_data_size) < 0) { + return -1; + } + reader->stats.pop_push_records++; + } + reader->stats.stack_reconstructions++; + + /* Build single-element timestamp list */ + PyObject *ts_obj = PyLong_FromUnsignedLongLong(ts->prev_timestamp); + if (!ts_obj) { + return -1; + } + PyObject *timestamps_list = PyList_New(1); + if (!timestamps_list) { + Py_DECREF(ts_obj); + return -1; + } + PyList_SET_ITEM(timestamps_list, 0, ts_obj); + + if (emit_sample(state, collector, thread_id, interpreter_id, status, + ts->current_stack, ts->current_stack_depth, reader, + timestamps_list) < 0) { + Py_DECREF(timestamps_list); + return -1; + } + Py_DECREF(timestamps_list); + if (replayed == PY_SSIZE_T_MAX) { + PyErr_SetString(PyExc_OverflowError, + "Sample count exceeds Py_ssize_t maximum"); + return -1; + } + replayed++; + reader->stats.total_samples++; + break; + } + + default: + PyErr_Format(PyExc_ValueError, "Unknown stack encoding: %u", encoding); + return -1; + } + + /* Progress callback */ + if (replayed % PROGRESS_CALLBACK_INTERVAL == 0) { + if (invoke_progress_callback(progress_callback, replayed, reader->sample_count) < 0) { + return -1; + } + } + } + + if ((uint64_t)replayed != reader->sample_count) { + PyErr_Format(PyExc_ValueError, + "Sample count mismatch: header declares %u samples but replay decoded %zd", + reader->sample_count, replayed); + return -1; + } + + /* Final progress callback at 100% */ + if (invoke_progress_callback(progress_callback, replayed, reader->sample_count) < 0) { + return -1; + } + + return replayed; +} + +PyObject * +binary_reader_get_info(BinaryReader *reader) +{ + PyObject *py_version = Py_BuildValue("(B,B,B)", + reader->py_major, reader->py_minor, reader->py_micro); + if (py_version == NULL) { + return NULL; + } + return Py_BuildValue( + "{s:I, s:N, s:K, s:K, s:I, s:I, s:I, s:I, s:i}", + "version", BINARY_FORMAT_VERSION, + "python_version", py_version, + "start_time_us", reader->start_time_us, + "sample_interval_us", reader->sample_interval_us, + "sample_count", reader->sample_count, + "thread_count", reader->thread_count, + "string_count", reader->strings_count, + "frame_count", reader->frames_count, + "compression_type", reader->compression_type + ); +} + +PyObject * +binary_writer_get_stats(BinaryWriter *writer) +{ + BinaryWriterStats *s = &writer->stats; + + /* Calculate derived stats */ + uint64_t total_records = s->repeat_records + s->full_records + + s->suffix_records + s->pop_push_records; + uint64_t total_samples = writer->total_samples; + uint64_t potential_frames = s->total_frames_written + s->frames_saved; + double compression_ratio = (potential_frames > 0) ? + (double)s->frames_saved / potential_frames * 100.0 : 0.0; + + return Py_BuildValue( + "{s:K, s:K, s:K, s:K, s:K, s:K, s:K, s:K, s:K, s:K, s:d}", + "repeat_records", s->repeat_records, + "repeat_samples", s->repeat_samples, + "full_records", s->full_records, + "suffix_records", s->suffix_records, + "pop_push_records", s->pop_push_records, + "total_records", total_records, + "total_samples", total_samples, + "total_frames_written", s->total_frames_written, + "frames_saved", s->frames_saved, + "bytes_written", s->bytes_written, + "frame_compression_pct", compression_ratio + ); +} + +PyObject * +binary_reader_get_stats(BinaryReader *reader) +{ + BinaryReaderStats *s = &reader->stats; + + uint64_t total_records = s->repeat_records + s->full_records + + s->suffix_records + s->pop_push_records; + + return Py_BuildValue( + "{s:K, s:K, s:K, s:K, s:K, s:K, s:K, s:K}", + "repeat_records", s->repeat_records, + "repeat_samples", s->repeat_samples, + "full_records", s->full_records, + "suffix_records", s->suffix_records, + "pop_push_records", s->pop_push_records, + "total_records", total_records, + "total_samples", s->total_samples, + "stack_reconstructions", s->stack_reconstructions + ); +} + +void +binary_reader_close(BinaryReader *reader) +{ + if (!reader) { + return; + } + +#if USE_MMAP + if (reader->mapped_data) { + munmap(reader->mapped_data, reader->mapped_size); + reader->mapped_data = NULL; /* Prevent use-after-free */ + reader->mapped_size = 0; + } + /* Clear sample_data which may point into the now-unmapped region */ + reader->sample_data = NULL; + reader->sample_data_size = 0; +#else + if (reader->fp) { + Py_fclose(reader->fp); + reader->fp = NULL; + } + if (reader->file_data) { + PyMem_Free(reader->file_data); + reader->file_data = NULL; + reader->file_size = 0; + } +#endif + + PyMem_Free(reader->decompressed_data); + + if (reader->strings) { + for (uint32_t i = 0; i < reader->strings_count; i++) { + Py_XDECREF(reader->strings[i]); + } + PyMem_Free(reader->strings); + } + + PyMem_Free(reader->frames); + + if (reader->thread_states) { + for (size_t i = 0; i < reader->thread_state_count; i++) { + PyMem_Free(reader->thread_states[i].current_stack); + } + PyMem_Free(reader->thread_states); + } + + PyMem_Free(reader); +} diff --git a/Modules/_remote_debugging/binary_io_writer.c b/Modules/_remote_debugging/binary_io_writer.c new file mode 100644 index 00000000000000..341f9f7dc8ac45 --- /dev/null +++ b/Modules/_remote_debugging/binary_io_writer.c @@ -0,0 +1,1284 @@ +/****************************************************************************** + * Python Remote Debugging Module - Binary Writer Implementation + * + * High-performance binary file writer for profiling data with optional zstd + * streaming compression. + ******************************************************************************/ + +#ifndef Py_BUILD_CORE_MODULE +# define Py_BUILD_CORE_MODULE +#endif + +#include "binary_io.h" +#include "_remote_debugging.h" +#include "pycore_opcode_utils.h" // MAX_REAL_OPCODE +#include <string.h> + +#ifdef HAVE_ZSTD +#include <zstd.h> +#endif + +/* ============================================================================ + * CONSTANTS FOR BINARY FORMAT SIZES + * ============================================================================ */ + +/* Sample header sizes */ +#define SAMPLE_HEADER_MAX_SIZE 26 /* fixed + max_varint(10) + status(1) + margin */ +#define MAX_VARINT_SIZE 10 /* Maximum bytes for a varint64 */ +#define MAX_VARINT_SIZE_U32 5 /* Maximum bytes for a varint32 */ +/* Frame buffer: depth varint (max 2 bytes for 256) + 256 frames * 5 bytes/varint + margin */ +#define MAX_FRAME_BUFFER_SIZE ((MAX_STACK_DEPTH * MAX_VARINT_SIZE_U32) + MAX_VARINT_SIZE_U32 + 16) + +/* Helper macro: convert PyLong to int32, using default_val if conversion fails */ +#define PYLONG_TO_INT32_OR_DEFAULT(obj, var, default_val) \ + do { \ + (var) = (int32_t)PyLong_AsLong(obj); \ + if (UNLIKELY(PyErr_Occurred() != NULL)) { \ + PyErr_Clear(); \ + (var) = (default_val); \ + } \ + } while (0) + +/* ============================================================================ + * WRITER-SPECIFIC UTILITY HELPERS + * ============================================================================ */ + +/* Grow two parallel arrays together (e.g., strings and string_lengths). + * Returns 0 on success, -1 on error (sets PyErr). + * On error, original arrays are preserved (truly atomic update). */ +static inline int +grow_parallel_arrays(void **array1, void **array2, size_t *capacity, + size_t elem_size1, size_t elem_size2) +{ + size_t old_cap = *capacity; + + if (old_cap > SIZE_MAX / 2) { + PyErr_SetString(PyExc_OverflowError, "Array capacity overflow"); + return -1; + } + size_t new_cap = old_cap * 2; + + if (new_cap > SIZE_MAX / elem_size1 || new_cap > SIZE_MAX / elem_size2) { + PyErr_SetString(PyExc_OverflowError, "Array allocation size overflow"); + return -1; + } + + size_t new_size1 = new_cap * elem_size1; + size_t new_size2 = new_cap * elem_size2; + size_t old_size1 = old_cap * elem_size1; + size_t old_size2 = old_cap * elem_size2; + + /* Allocate fresh memory blocks (not realloc) to ensure atomicity. + * If either allocation fails, original arrays are completely unchanged. */ + void *new_array1 = PyMem_Malloc(new_size1); + if (!new_array1) { + PyErr_NoMemory(); + return -1; + } + + void *new_array2 = PyMem_Malloc(new_size2); + if (!new_array2) { + /* Second allocation failed - free first and return with no state change */ + PyMem_Free(new_array1); + PyErr_NoMemory(); + return -1; + } + + /* Both allocations succeeded - copy data and update pointers atomically */ + memcpy(new_array1, *array1, old_size1); + memcpy(new_array2, *array2, old_size2); + + PyMem_Free(*array1); + PyMem_Free(*array2); + + *array1 = new_array1; + *array2 = new_array2; + *capacity = new_cap; + return 0; +} + +/* Checked fwrite with GIL release - returns 0 on success, -1 on error (sets PyErr). + * This version releases the GIL during the write operation to allow other Python + * threads to run during potentially blocking I/O. */ +static inline int +fwrite_checked_allow_threads(const void *data, size_t size, FILE *fp) +{ + size_t written; + Py_BEGIN_ALLOW_THREADS + written = fwrite(data, 1, size, fp); + Py_END_ALLOW_THREADS + if (written != size) { + int err = errno; + if (ferror(fp) && err != 0) { + errno = err; + PyErr_SetFromErrno(PyExc_IOError); + } + else { + PyErr_Format(PyExc_IOError, + "short write: wrote %zu of %zu bytes", written, size); + } + return -1; + } + return 0; +} + +/* Forward declaration for writer_write_bytes */ +static inline int writer_write_bytes(BinaryWriter *writer, const void *data, size_t size); + +/* Encode and write a varint u32 - returns 0 on success, -1 on error */ +static inline int +writer_write_varint_u32(BinaryWriter *writer, uint32_t value) +{ + uint8_t buf[MAX_VARINT_SIZE]; + size_t len = encode_varint_u32(buf, value); + return writer_write_bytes(writer, buf, len); +} + +/* Encode and write a varint u64 - returns 0 on success, -1 on error */ +static inline int +writer_write_varint_u64(BinaryWriter *writer, uint64_t value) +{ + uint8_t buf[MAX_VARINT_SIZE]; + size_t len = encode_varint_u64(buf, value); + return writer_write_bytes(writer, buf, len); +} + + +/* ============================================================================ + * UTILITY FUNCTIONS + * ============================================================================ */ + +int +binary_io_zstd_available(void) +{ +#ifdef HAVE_ZSTD + return 1; +#else + return 0; +#endif +} + +int +binary_io_get_best_compression(void) +{ +#ifdef HAVE_ZSTD + return COMPRESSION_ZSTD; +#else + return COMPRESSION_NONE; +#endif +} + +/* ============================================================================ + * BINARY WRITER IMPLEMENTATION + * ============================================================================ */ + +static int +writer_init_zstd(BinaryWriter *writer) +{ +#ifdef HAVE_ZSTD + writer->zstd.cctx = ZSTD_createCCtx(); + if (!writer->zstd.cctx) { + PyErr_SetString(PyExc_MemoryError, "Failed to create zstd compression context"); + return -1; + } + + /* Compression level 5: better ratio for repetitive profiling data */ + size_t result = ZSTD_CCtx_setParameter(writer->zstd.cctx, + ZSTD_c_compressionLevel, 5); + if (ZSTD_isError(result)) { + PyErr_Format(PyExc_RuntimeError, "Failed to set zstd compression level: %s", + ZSTD_getErrorName(result)); + ZSTD_freeCCtx(writer->zstd.cctx); + writer->zstd.cctx = NULL; + return -1; + } + + /* Use large buffer (512KB) for fewer I/O syscalls */ + writer->zstd.compressed_buffer = PyMem_Malloc(COMPRESSED_BUFFER_SIZE); + if (!writer->zstd.compressed_buffer) { + ZSTD_freeCCtx(writer->zstd.cctx); + writer->zstd.cctx = NULL; + PyErr_NoMemory(); + return -1; + } + writer->zstd.compressed_buffer_size = COMPRESSED_BUFFER_SIZE; + + return 0; +#else + PyErr_SetString(PyExc_RuntimeError, + "zstd compression requested but not available (HAVE_ZSTD not defined)"); + return -1; +#endif +} + +static int +writer_flush_buffer(BinaryWriter *writer) +{ + if (writer->buffer_pos == 0) { + return 0; + } + +#ifdef HAVE_ZSTD + if (writer->compression_type == COMPRESSION_ZSTD) { + ZSTD_inBuffer input = { writer->write_buffer, writer->buffer_pos, 0 }; + + while (input.pos < input.size) { + ZSTD_outBuffer output = { + writer->zstd.compressed_buffer, + writer->zstd.compressed_buffer_size, + 0 + }; + + size_t result = ZSTD_compressStream2( + writer->zstd.cctx, &output, &input, ZSTD_e_continue + ); + + if (ZSTD_isError(result)) { + PyErr_Format(PyExc_IOError, "zstd compression error: %s", + ZSTD_getErrorName(result)); + return -1; + } + + if (output.pos > 0) { + if (fwrite_checked_allow_threads(writer->zstd.compressed_buffer, output.pos, writer->fp) < 0) { + return -1; + } + } + } + } else +#endif + { + if (fwrite_checked_allow_threads(writer->write_buffer, writer->buffer_pos, writer->fp) < 0) { + return -1; + } + } + + writer->buffer_pos = 0; + return 0; +} + +static inline int +writer_write_bytes(BinaryWriter *writer, const void *data, size_t size) +{ + const uint8_t *src = (const uint8_t *)data; + size_t original_size = size; + + while (size > 0) { + size_t space = writer->buffer_size - writer->buffer_pos; + size_t to_copy = (size < space) ? size : space; + + memcpy(writer->write_buffer + writer->buffer_pos, src, to_copy); + writer->buffer_pos += to_copy; + src += to_copy; + size -= to_copy; + + if (writer->buffer_pos == writer->buffer_size) { + if (writer_flush_buffer(writer) < 0) { + return -1; + } + } + } + + writer->stats.bytes_written += original_size; + return 0; +} + +/* ============================================================================ + * HASH TABLE SUPPORT FUNCTIONS (using _Py_hashtable) + * ============================================================================ */ + +/* Hash function for Python strings - uses Python's cached hash */ +static Py_uhash_t +string_hash_func(const void *key) +{ + PyObject *str = (PyObject *)key; + Py_hash_t hash = PyObject_Hash(str); + if (hash == -1) { + PyErr_Clear(); + return 0; + } + return (Py_uhash_t)hash; +} + +static int +string_compare_func(const void *key1, const void *key2) +{ + PyObject *str1 = (PyObject *)key1; + PyObject *str2 = (PyObject *)key2; + if (str1 == str2) { + return 1; + } + int result = PyObject_RichCompareBool(str1, str2, Py_EQ); + if (result == -1) { + PyErr_Clear(); + return 0; + } + return result; +} + +static void +string_key_destroy(void *key) +{ + Py_XDECREF((PyObject *)key); +} + +static Py_uhash_t +frame_key_hash_func(const void *key) +{ + const FrameKey *fk = (const FrameKey *)key; + /* FNV-1a style hash combining all fields */ + Py_uhash_t hash = 2166136261u; + hash ^= fk->filename_idx; + hash *= 16777619u; + hash ^= fk->funcname_idx; + hash *= 16777619u; + hash ^= (uint32_t)fk->lineno; + hash *= 16777619u; + hash ^= (uint32_t)fk->end_lineno; + hash *= 16777619u; + hash ^= (uint32_t)fk->column; + hash *= 16777619u; + hash ^= (uint32_t)fk->end_column; + hash *= 16777619u; + hash ^= fk->opcode; + hash *= 16777619u; + return hash; +} + +static int +frame_key_compare_func(const void *key1, const void *key2) +{ + const FrameKey *fk1 = (const FrameKey *)key1; + const FrameKey *fk2 = (const FrameKey *)key2; + return (fk1->filename_idx == fk2->filename_idx && + fk1->funcname_idx == fk2->funcname_idx && + fk1->lineno == fk2->lineno && + fk1->end_lineno == fk2->end_lineno && + fk1->column == fk2->column && + fk1->end_column == fk2->end_column && + fk1->opcode == fk2->opcode); +} + +static void +frame_key_destroy(void *key) +{ + PyMem_Free(key); +} + +static inline int +writer_intern_string(BinaryWriter *writer, PyObject *string, uint32_t *index) +{ + void *existing = _Py_hashtable_get(writer->string_hash, string); + if (existing != NULL) { + *index = (uint32_t)(uintptr_t)existing - 1; /* index+1 stored to distinguish from NULL */ + return 0; + } + + if (writer->string_count >= UINT32_MAX) { + PyErr_SetString(PyExc_OverflowError, + "too many strings for binary format"); + return -1; + } + if (writer->string_count >= writer->string_capacity) { + if (grow_parallel_arrays((void **)&writer->strings, + (void **)&writer->string_lengths, + &writer->string_capacity, + sizeof(char *), sizeof(size_t)) < 0) { + return -1; + } + } + + Py_ssize_t str_len; + const char *str_data = PyUnicode_AsUTF8AndSize(string, &str_len); + if (!str_data) { + return -1; + } + if ((uintmax_t)str_len > UINT32_MAX) { + PyErr_Format(PyExc_OverflowError, + "string length %zd exceeds binary format maximum %u", + str_len, UINT32_MAX); + return -1; + } + + char *str_copy = PyMem_Malloc(str_len + 1); + if (!str_copy) { + PyErr_NoMemory(); + return -1; + } + memcpy(str_copy, str_data, str_len + 1); + + *index = (uint32_t)writer->string_count; + + /* Add to hash table FIRST to ensure atomic rollback on failure */ + Py_INCREF(string); + if (_Py_hashtable_set(writer->string_hash, string, (void *)(uintptr_t)(*index + 1)) < 0) { + Py_DECREF(string); + PyMem_Free(str_copy); + PyErr_NoMemory(); + return -1; + } + + writer->strings[writer->string_count] = str_copy; + writer->string_lengths[writer->string_count] = str_len; + writer->string_count++; + + return 0; +} + +static inline int +writer_intern_frame(BinaryWriter *writer, const FrameEntry *entry, uint32_t *index) +{ + FrameKey lookup_key = { + entry->filename_idx, entry->funcname_idx, + entry->lineno, entry->end_lineno, + entry->column, entry->end_column, + entry->opcode + }; + + void *existing = _Py_hashtable_get(writer->frame_hash, &lookup_key); + if (existing != NULL) { + *index = (uint32_t)(uintptr_t)existing - 1; /* index+1 stored to distinguish from NULL */ + return 0; + } + + if (writer->frame_count >= UINT32_MAX) { + PyErr_SetString(PyExc_OverflowError, + "too many frames for binary format"); + return -1; + } + if (GROW_ARRAY(writer->frame_entries, writer->frame_count, + writer->frame_capacity, FrameEntry) < 0) { + return -1; + } + + FrameKey *key = PyMem_Malloc(sizeof(FrameKey)); + if (!key) { + PyErr_NoMemory(); + return -1; + } + *key = lookup_key; + + *index = (uint32_t)writer->frame_count; + writer->frame_entries[writer->frame_count] = *entry; + + if (_Py_hashtable_set(writer->frame_hash, key, (void *)(uintptr_t)(*index + 1)) < 0) { + PyMem_Free(key); + PyErr_NoMemory(); + return -1; + } + + writer->frame_count++; + return 0; +} + +/* Get or create a thread entry for the given thread_id. + * Returns pointer to ThreadEntry, or NULL on allocation failure. + * If is_new is non-NULL, sets it to 1 if this is a new thread, 0 otherwise. */ +static ThreadEntry * +writer_get_or_create_thread_entry(BinaryWriter *writer, uint64_t thread_id, + uint32_t interpreter_id, int *is_new) +{ + /* Linear search is OK for small number of threads. + * Key is (thread_id, interpreter_id) since same thread_id can exist in different interpreters. */ + for (size_t i = 0; i < writer->thread_count; i++) { + if (writer->thread_entries[i].thread_id == thread_id && + writer->thread_entries[i].interpreter_id == interpreter_id) { + if (is_new) { + *is_new = 0; + } + return &writer->thread_entries[i]; + } + } + + if (writer->thread_count >= UINT32_MAX) { + PyErr_SetString(PyExc_OverflowError, + "too many threads for binary format"); + return NULL; + } + if (writer->thread_count >= writer->thread_capacity) { + ThreadEntry *new_entries = grow_array(writer->thread_entries, + &writer->thread_capacity, + sizeof(ThreadEntry)); + if (!new_entries) { + return NULL; + } + writer->thread_entries = new_entries; + } + + ThreadEntry *entry = &writer->thread_entries[writer->thread_count]; + memset(entry, 0, sizeof(ThreadEntry)); + entry->thread_id = thread_id; + entry->interpreter_id = interpreter_id; + entry->prev_timestamp = writer->start_time_us; + entry->prev_stack_capacity = MAX_STACK_DEPTH; + entry->pending_rle_capacity = INITIAL_RLE_CAPACITY; + + entry->prev_stack = PyMem_Calloc(entry->prev_stack_capacity, sizeof(uint32_t)); + if (!entry->prev_stack) { + PyErr_NoMemory(); + return NULL; + } + + entry->pending_rle = PyMem_Malloc(entry->pending_rle_capacity * sizeof(PendingRLESample)); + if (!entry->pending_rle) { + PyMem_Free(entry->prev_stack); + PyErr_NoMemory(); + return NULL; + } + + writer->thread_count++; + if (is_new) { + *is_new = 1; + } + return entry; +} + +/* Compare two stacks and return the encoding type and parameters. + * Sets: + * - shared_count: number of frames matching from bottom of stack + * - pop_count: frames to remove from prev stack + * - push_count: new frames to add + * + * Returns the best encoding type to use. */ +static int +compare_stacks(const uint32_t *prev_stack, size_t prev_depth, + const uint32_t *curr_stack, size_t curr_depth, + size_t *shared_count, size_t *pop_count, size_t *push_count) +{ + if (prev_depth == curr_depth) { + int identical = 1; + for (size_t i = 0; i < prev_depth; i++) { + if (prev_stack[i] != curr_stack[i]) { + identical = 0; + break; + } + } + if (identical) { + *shared_count = prev_depth; + *pop_count = 0; + *push_count = 0; + return STACK_REPEAT; + } + } + + /* Find longest common suffix (frames at the bottom/outer part of stack). + * Stacks are stored innermost-first, so suffix is at the end. */ + size_t suffix_len = 0; + size_t min_depth = (prev_depth < curr_depth) ? prev_depth : curr_depth; + + for (size_t i = 0; i < min_depth; i++) { + size_t prev_idx = prev_depth - 1 - i; + size_t curr_idx = curr_depth - 1 - i; + if (prev_stack[prev_idx] == curr_stack[curr_idx]) { + suffix_len++; + } else { + break; + } + } + + *shared_count = suffix_len; + *pop_count = prev_depth - suffix_len; + *push_count = curr_depth - suffix_len; + + /* Choose best encoding based on byte cost */ + /* STACK_FULL: 1 (type) + 1-2 (depth) + sum(frame varints) */ + /* STACK_SUFFIX: 1 (type) + 1-2 (shared) + 1-2 (new_count) + sum(new frame varints) */ + /* STACK_POP_PUSH: 1 (type) + 1-2 (pop) + 1-2 (push) + sum(new frame varints) */ + + /* If no common suffix, use full stack */ + if (suffix_len == 0) { + return STACK_FULL; + } + + /* If only adding frames (suffix == prev_depth), use SUFFIX */ + if (*pop_count == 0 && *push_count > 0) { + return STACK_SUFFIX; + } + + /* If popping and/or pushing, use POP_PUSH if it saves bytes */ + /* Heuristic: POP_PUSH is better when we're modifying top frames */ + if (*pop_count > 0 || *push_count > 0) { + /* Use full stack if sharing less than half the frames */ + if (suffix_len < curr_depth / 2) { + return STACK_FULL; + } + return STACK_POP_PUSH; + } + + return STACK_FULL; +} + +/* Write common sample header: thread_id(8) + interpreter_id(4) + encoding(1). + * Returns 0 on success, -1 on failure. */ +static inline int +write_sample_header(BinaryWriter *writer, ThreadEntry *entry, uint8_t encoding) +{ + uint8_t header[SAMPLE_HEADER_FIXED_SIZE]; + memcpy(header + SMP_OFF_THREAD_ID, &entry->thread_id, SMP_SIZE_THREAD_ID); + memcpy(header + SMP_OFF_INTERPRETER_ID, &entry->interpreter_id, SMP_SIZE_INTERPRETER_ID); + header[SMP_OFF_ENCODING] = encoding; + return writer_write_bytes(writer, header, SAMPLE_HEADER_FIXED_SIZE); +} + +/* Flush pending RLE samples for a thread. + * Writes the RLE record to the output buffer. + * Returns 0 on success, -1 on failure. */ +static int +flush_pending_rle(BinaryWriter *writer, ThreadEntry *entry) +{ + if (!entry->has_pending_rle || entry->pending_rle_count == 0) { + return 0; + } + if (entry->pending_rle_count > UINT32_MAX - writer->total_samples) { + PyErr_SetString(PyExc_OverflowError, + "too many samples for binary format"); + return -1; + } + + /* Write RLE record: + * [thread_id: 8] [interpreter_id: 4] [STACK_REPEAT: 1] [count: varint] + * [timestamp_delta_1: varint] [status_1: 1] ... [timestamp_delta_N: varint] [status_N: 1] + */ + + if (write_sample_header(writer, entry, STACK_REPEAT) < 0) { + return -1; + } + + if (writer_write_varint_u32(writer, (uint32_t)entry->pending_rle_count) < 0) { + return -1; + } + + for (size_t i = 0; i < entry->pending_rle_count; i++) { + if (writer_write_varint_u64(writer, entry->pending_rle[i].timestamp_delta) < 0) { + return -1; + } + if (writer_write_bytes(writer, &entry->pending_rle[i].status, 1) < 0) { + return -1; + } + writer->total_samples++; + } + + writer->stats.repeat_records++; + writer->stats.repeat_samples += entry->pending_rle_count; + /* Each RLE sample saves writing the entire stack */ + writer->stats.frames_saved += entry->pending_rle_count * entry->prev_stack_depth; + + entry->pending_rle_count = 0; + entry->has_pending_rle = 0; + + return 0; +} + +/* Write a single sample with the specified encoding. + * Returns 0 on success, -1 on failure. */ +static int +write_sample_with_encoding(BinaryWriter *writer, ThreadEntry *entry, + uint64_t timestamp_delta, uint8_t status, + int encoding_type, + const uint32_t *frame_indices, size_t stack_depth, + size_t shared_count, size_t pop_count, size_t push_count) +{ + if (writer->total_samples == UINT32_MAX) { + PyErr_SetString(PyExc_OverflowError, + "too many samples for binary format"); + return -1; + } + + /* Header: thread_id(8) + interpreter_id(4) + encoding(1) + delta(varint) + status(1) */ + uint8_t header_buf[SAMPLE_HEADER_MAX_SIZE]; + memcpy(header_buf + SMP_OFF_THREAD_ID, &entry->thread_id, SMP_SIZE_THREAD_ID); + memcpy(header_buf + SMP_OFF_INTERPRETER_ID, &entry->interpreter_id, SMP_SIZE_INTERPRETER_ID); + header_buf[SMP_OFF_ENCODING] = (uint8_t)encoding_type; + size_t varint_len = encode_varint_u64( + header_buf + SAMPLE_HEADER_FIXED_SIZE, + timestamp_delta); + header_buf[SAMPLE_HEADER_FIXED_SIZE + varint_len] = status; + + if (writer_write_bytes(writer, header_buf, + SAMPLE_HEADER_FIXED_SIZE + varint_len + 1) < 0) { + return -1; + } + + uint8_t frame_buf[MAX_FRAME_BUFFER_SIZE]; + size_t frame_buf_pos = 0; + size_t frames_written = 0; + + switch (encoding_type) { + case STACK_FULL: + /* [depth: varint] [frame_idx: varint]... */ + frame_buf_pos += encode_varint_u32(frame_buf, (uint32_t)stack_depth); + for (size_t i = 0; i < stack_depth; i++) { + frame_buf_pos += encode_varint_u32(frame_buf + frame_buf_pos, frame_indices[i]); + } + frames_written = stack_depth; + writer->stats.full_records++; + break; + + case STACK_SUFFIX: + /* [shared_count: varint] [new_count: varint] [new_frame_idx: varint]... */ + frame_buf_pos += encode_varint_u32(frame_buf, (uint32_t)shared_count); + frame_buf_pos += encode_varint_u32(frame_buf + frame_buf_pos, (uint32_t)push_count); + /* New frames are at the top (beginning) of current stack */ + for (size_t i = 0; i < push_count; i++) { + frame_buf_pos += encode_varint_u32(frame_buf + frame_buf_pos, frame_indices[i]); + } + frames_written = push_count; + writer->stats.suffix_records++; + /* Saved writing shared_count frames */ + writer->stats.frames_saved += shared_count; + break; + + case STACK_POP_PUSH: + /* [pop_count: varint] [push_count: varint] [new_frame_idx: varint]... */ + frame_buf_pos += encode_varint_u32(frame_buf, (uint32_t)pop_count); + frame_buf_pos += encode_varint_u32(frame_buf + frame_buf_pos, (uint32_t)push_count); + /* New frames are at the top (beginning) of current stack */ + for (size_t i = 0; i < push_count; i++) { + frame_buf_pos += encode_varint_u32(frame_buf + frame_buf_pos, frame_indices[i]); + } + frames_written = push_count; + writer->stats.pop_push_records++; + /* Saved writing shared_count frames (stack_depth - push_count if we had written full) */ + writer->stats.frames_saved += shared_count; + break; + + default: + PyErr_SetString(PyExc_RuntimeError, "Invalid stack encoding type"); + return -1; + } + + if (writer_write_bytes(writer, frame_buf, frame_buf_pos) < 0) { + return -1; + } + + writer->stats.total_frames_written += frames_written; + writer->total_samples++; + return 0; +} + +BinaryWriter * +binary_writer_create(PyObject *path, uint64_t sample_interval_us, int compression_type, + uint64_t start_time_us) +{ + BinaryWriter *writer = PyMem_Calloc(1, sizeof(BinaryWriter)); + if (!writer) { + PyErr_NoMemory(); + return NULL; + } + + writer->start_time_us = start_time_us; + writer->sample_interval_us = sample_interval_us; + writer->compression_type = compression_type; + + writer->write_buffer = PyMem_Malloc(WRITE_BUFFER_SIZE); + if (!writer->write_buffer) { + PyErr_NoMemory(); + goto error; + } + writer->buffer_size = WRITE_BUFFER_SIZE; + + writer->string_hash = _Py_hashtable_new_full( + string_hash_func, + string_compare_func, + string_key_destroy, /* Key destroy: decref the Python string */ + NULL, /* Value destroy: values are just indices, not pointers */ + NULL /* Use default allocator */ + ); + if (!writer->string_hash) { + PyErr_NoMemory(); + goto error; + } + writer->strings = PyMem_Malloc(INITIAL_STRING_CAPACITY * sizeof(char *)); + if (!writer->strings) { + PyErr_NoMemory(); + goto error; + } + writer->string_lengths = PyMem_Malloc(INITIAL_STRING_CAPACITY * sizeof(size_t)); + if (!writer->string_lengths) { + PyErr_NoMemory(); + goto error; + } + writer->string_capacity = INITIAL_STRING_CAPACITY; + + writer->frame_hash = _Py_hashtable_new_full( + frame_key_hash_func, + frame_key_compare_func, + frame_key_destroy, /* Key destroy: free the FrameKey */ + NULL, /* Value destroy: values are just indices, not pointers */ + NULL /* Use default allocator */ + ); + if (!writer->frame_hash) { + PyErr_NoMemory(); + goto error; + } + writer->frame_entries = PyMem_Malloc(INITIAL_FRAME_CAPACITY * sizeof(FrameEntry)); + if (!writer->frame_entries) { + PyErr_NoMemory(); + goto error; + } + writer->frame_capacity = INITIAL_FRAME_CAPACITY; + + writer->thread_entries = PyMem_Malloc(INITIAL_THREAD_CAPACITY * sizeof(ThreadEntry)); + if (!writer->thread_entries) { + PyErr_NoMemory(); + goto error; + } + writer->thread_capacity = INITIAL_THREAD_CAPACITY; + + if (compression_type == COMPRESSION_ZSTD) { + if (writer_init_zstd(writer) < 0) { + goto error; + } + } + + writer->fp = Py_fopen(path, "wb"); + if (!writer->fp) { + goto error; + } + + /* Hint sequential write pattern to kernel for better I/O scheduling */ +#if defined(__linux__) && defined(POSIX_FADV_SEQUENTIAL) + { + int fd = fileno(writer->fp); + if (fd >= 0) { + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); + } + } +#endif + + uint8_t header[FILE_HEADER_PLACEHOLDER_SIZE] = {0}; + if (fwrite_checked_allow_threads(header, FILE_HEADER_PLACEHOLDER_SIZE, writer->fp) < 0) { + goto error; + } + + return writer; + +error: + binary_writer_destroy(writer); + return NULL; +} + +/* Build a frame stack from Python frame list by interning all strings and frames. + * Returns 0 on success, -1 on error. */ +static int +build_frame_stack(BinaryWriter *writer, PyObject *frame_list, + uint32_t *curr_stack, size_t *curr_depth) +{ + Py_ssize_t stack_depth = PyList_Size(frame_list); + *curr_depth = (stack_depth < MAX_STACK_DEPTH) ? stack_depth : MAX_STACK_DEPTH; + + for (Py_ssize_t k = 0; k < (Py_ssize_t)*curr_depth; k++) { + /* Use unchecked accessors since we control the data structures */ + PyObject *frame_info = PyList_GET_ITEM(frame_list, k); + + /* Get filename, location, funcname, opcode from FrameInfo using unchecked access */ + PyObject *filename = PyStructSequence_GET_ITEM(frame_info, 0); + PyObject *location = PyStructSequence_GET_ITEM(frame_info, 1); + PyObject *funcname = PyStructSequence_GET_ITEM(frame_info, 2); + PyObject *opcode_obj = PyStructSequence_GET_ITEM(frame_info, 3); + + /* Extract location fields (can be None for synthetic frames) */ + int32_t lineno = LOCATION_NOT_AVAILABLE; + int32_t end_lineno = LOCATION_NOT_AVAILABLE; + int32_t column = LOCATION_NOT_AVAILABLE; + int32_t end_column = LOCATION_NOT_AVAILABLE; + + if (location != Py_None) { + /* LocationInfo is a struct sequence or tuple with: + * (lineno, end_lineno, column, end_column) */ + PyObject *lineno_obj = PyTuple_Check(location) ? + PyTuple_GET_ITEM(location, 0) : + PyStructSequence_GET_ITEM(location, 0); + PyObject *end_lineno_obj = PyTuple_Check(location) ? + PyTuple_GET_ITEM(location, 1) : + PyStructSequence_GET_ITEM(location, 1); + PyObject *column_obj = PyTuple_Check(location) ? + PyTuple_GET_ITEM(location, 2) : + PyStructSequence_GET_ITEM(location, 2); + PyObject *end_column_obj = PyTuple_Check(location) ? + PyTuple_GET_ITEM(location, 3) : + PyStructSequence_GET_ITEM(location, 3); + + PYLONG_TO_INT32_OR_DEFAULT(lineno_obj, lineno, LOCATION_NOT_AVAILABLE); + PYLONG_TO_INT32_OR_DEFAULT(end_lineno_obj, end_lineno, LOCATION_NOT_AVAILABLE); + PYLONG_TO_INT32_OR_DEFAULT(column_obj, column, LOCATION_NOT_AVAILABLE); + PYLONG_TO_INT32_OR_DEFAULT(end_column_obj, end_column, LOCATION_NOT_AVAILABLE); + } + + /* Extract opcode (can be None) */ + uint8_t opcode = OPCODE_NONE; + if (opcode_obj != Py_None) { + long opcode_long = PyLong_AsLong(opcode_obj); + if (UNLIKELY(PyErr_Occurred() != NULL)) { + PyErr_Clear(); + opcode = OPCODE_NONE; + } else if (opcode_long >= 0 && opcode_long <= MAX_REAL_OPCODE) { + opcode = (uint8_t)opcode_long; + } + } + + /* Intern filename */ + uint32_t filename_idx; + if (writer_intern_string(writer, filename, &filename_idx) < 0) { + return -1; + } + + /* Intern funcname */ + uint32_t funcname_idx; + if (writer_intern_string(writer, funcname, &funcname_idx) < 0) { + return -1; + } + + /* Intern frame with full location info */ + FrameEntry frame_entry = { + .filename_idx = filename_idx, + .funcname_idx = funcname_idx, + .lineno = lineno, + .end_lineno = end_lineno, + .column = column, + .end_column = end_column, + .opcode = opcode + }; + uint32_t frame_idx; + if (writer_intern_frame(writer, &frame_entry, &frame_idx) < 0) { + return -1; + } + + curr_stack[k] = frame_idx; + } + return 0; +} + +/* Process a single thread's sample. + * Returns 0 on success, -1 on error. */ +static int +process_thread_sample(BinaryWriter *writer, PyObject *thread_info, + uint32_t interpreter_id, uint64_t timestamp_us) +{ + PyObject *thread_id_obj = PyStructSequence_GET_ITEM(thread_info, 0); + PyObject *status_obj = PyStructSequence_GET_ITEM(thread_info, 1); + PyObject *frame_list = PyStructSequence_GET_ITEM(thread_info, 2); + + uint64_t thread_id = PyLong_AsUnsignedLongLong(thread_id_obj); + if (thread_id == (uint64_t)-1 && PyErr_Occurred()) { + return -1; + } + long status_long = PyLong_AsLong(status_obj); + if (status_long == -1 && PyErr_Occurred()) { + return -1; + } + uint8_t status = (uint8_t)status_long; + + ThreadEntry *entry = writer_get_or_create_thread_entry( + writer, thread_id, interpreter_id, NULL); + if (!entry) { + return -1; + } + + /* Calculate timestamp delta */ + uint64_t delta = timestamp_us - entry->prev_timestamp; + entry->prev_timestamp = timestamp_us; + + /* Process frames and build current stack */ + uint32_t curr_stack[MAX_STACK_DEPTH]; + size_t curr_depth; + if (build_frame_stack(writer, frame_list, curr_stack, &curr_depth) < 0) { + return -1; + } + + /* Compare with previous stack to determine encoding */ + size_t shared_count, pop_count, push_count; + int encoding = compare_stacks( + entry->prev_stack, entry->prev_stack_depth, + curr_stack, curr_depth, + &shared_count, &pop_count, &push_count); + + if (encoding == STACK_REPEAT) { + /* Buffer this sample for RLE. + * + * STACK_REPEAT also covers the "first sample for a fresh thread, + * empty stack" case: a new ThreadEntry has prev_stack_depth == 0 + * and a zero-initialized prev_stack, so compare_stacks() returns + * STACK_REPEAT against an empty curr_stack (depth 0). Buffering + * it here is correct; the RLE flush path emits it as a normal + * STACK_REPEAT record. */ + if (GROW_ARRAY(entry->pending_rle, entry->pending_rle_count, + entry->pending_rle_capacity, PendingRLESample) < 0) { + return -1; + } + entry->pending_rle[entry->pending_rle_count].timestamp_delta = delta; + entry->pending_rle[entry->pending_rle_count].status = status; + entry->pending_rle_count++; + entry->has_pending_rle = 1; + } else { + /* Stack changed - flush any pending RLE first */ + if (entry->has_pending_rle) { + if (flush_pending_rle(writer, entry) < 0) { + return -1; + } + } + + if (write_sample_with_encoding(writer, entry, delta, status, encoding, + curr_stack, curr_depth, + shared_count, pop_count, push_count) < 0) { + return -1; + } + + memcpy(entry->prev_stack, curr_stack, curr_depth * sizeof(uint32_t)); + entry->prev_stack_depth = curr_depth; + } + + return 0; +} + +int +binary_writer_write_sample(BinaryWriter *writer, PyObject *stack_frames, uint64_t timestamp_us) +{ + if (!PyList_Check(stack_frames)) { + PyErr_SetString(PyExc_TypeError, "stack_frames must be a list"); + return -1; + } + + Py_ssize_t num_interpreters = PyList_GET_SIZE(stack_frames); + for (Py_ssize_t i = 0; i < num_interpreters; i++) { + PyObject *interp_info = PyList_GET_ITEM(stack_frames, i); + + PyObject *interp_id_obj = PyStructSequence_GET_ITEM(interp_info, 0); + PyObject *threads = PyStructSequence_GET_ITEM(interp_info, 1); + + unsigned long interp_id_long = PyLong_AsUnsignedLong(interp_id_obj); + if (interp_id_long == (unsigned long)-1 && PyErr_Occurred()) { + return -1; + } + /* Bounds check: interpreter_id is stored as uint32_t in binary format */ + if (interp_id_long > UINT32_MAX) { + PyErr_Format(PyExc_OverflowError, + "interpreter_id %lu exceeds maximum value %lu", + interp_id_long, (unsigned long)UINT32_MAX); + return -1; + } + uint32_t interpreter_id = (uint32_t)interp_id_long; + + Py_ssize_t num_threads = PyList_GET_SIZE(threads); + for (Py_ssize_t j = 0; j < num_threads; j++) { + PyObject *thread_info = PyList_GET_ITEM(threads, j); + if (process_thread_sample(writer, thread_info, interpreter_id, timestamp_us) < 0) { + return -1; + } + } + } + + return 0; +} + +int +binary_writer_finalize(BinaryWriter *writer) +{ + for (size_t i = 0; i < writer->thread_count; i++) { + if (writer->thread_entries[i].has_pending_rle) { + if (flush_pending_rle(writer, &writer->thread_entries[i]) < 0) { + return -1; + } + } + } + + if (writer_flush_buffer(writer) < 0) { + return -1; + } + +#ifdef HAVE_ZSTD + /* Finalize compression stream */ + if (writer->compression_type == COMPRESSION_ZSTD && writer->zstd.cctx) { + ZSTD_inBuffer input = { NULL, 0, 0 }; + size_t remaining; + + do { + ZSTD_outBuffer output = { + writer->zstd.compressed_buffer, + writer->zstd.compressed_buffer_size, + 0 + }; + + remaining = ZSTD_compressStream2(writer->zstd.cctx, &output, &input, ZSTD_e_end); + + if (ZSTD_isError(remaining)) { + PyErr_Format(PyExc_IOError, "zstd finalization error: %s", + ZSTD_getErrorName(remaining)); + return -1; + } + + if (output.pos > 0) { + if (fwrite_checked_allow_threads(writer->zstd.compressed_buffer, output.pos, writer->fp) < 0) { + return -1; + } + } + } while (remaining > 0); + } +#endif + + /* Use 64-bit file position for >2GB files */ + file_offset_t string_table_offset = FTELL64(writer->fp); + if (string_table_offset < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + + /* Release GIL during potentially large writes */ + for (size_t i = 0; i < writer->string_count; i++) { + uint8_t len_buf[10]; + size_t len_size = encode_varint_u32(len_buf, (uint32_t)writer->string_lengths[i]); + if (fwrite_checked_allow_threads(len_buf, len_size, writer->fp) < 0 || + fwrite_checked_allow_threads(writer->strings[i], writer->string_lengths[i], writer->fp) < 0) { + return -1; + } + } + + file_offset_t frame_table_offset = FTELL64(writer->fp); + if (frame_table_offset < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + + for (size_t i = 0; i < writer->frame_count; i++) { + FrameEntry *entry = &writer->frame_entries[i]; + uint8_t buf[64]; /* Increased buffer for additional fields */ + size_t pos = encode_varint_u32(buf, entry->filename_idx); + pos += encode_varint_u32(buf + pos, entry->funcname_idx); + pos += encode_varint_i32(buf + pos, entry->lineno); + + /* Delta encode end_lineno: store (end_lineno - lineno) as zigzag. + * When lineno is -1, store delta as 0 (result will be -1). */ + int32_t end_lineno_delta = 0; + if (entry->lineno != LOCATION_NOT_AVAILABLE && + entry->end_lineno != LOCATION_NOT_AVAILABLE) { + end_lineno_delta = entry->end_lineno - entry->lineno; + } + pos += encode_varint_i32(buf + pos, end_lineno_delta); + + pos += encode_varint_i32(buf + pos, entry->column); + + /* Delta encode end_column: store (end_column - column) as zigzag. + * When column is -1, store delta as 0 (result will be -1). */ + int32_t end_column_delta = 0; + if (entry->column != LOCATION_NOT_AVAILABLE && + entry->end_column != LOCATION_NOT_AVAILABLE) { + end_column_delta = entry->end_column - entry->column; + } + pos += encode_varint_i32(buf + pos, end_column_delta); + + buf[pos++] = entry->opcode; + + if (fwrite_checked_allow_threads(buf, pos, writer->fp) < 0) { + return -1; + } + } + + /* Footer: string_count(4) + frame_count(4) + file_size(8) + checksum(16) */ + file_offset_t footer_offset = FTELL64(writer->fp); + if (footer_offset < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + uint64_t file_size = (uint64_t)footer_offset + FILE_FOOTER_SIZE; + uint8_t footer[FILE_FOOTER_SIZE] = {0}; + /* Cast size_t to uint32_t before memcpy to ensure correct bytes are copied + * on both little-endian and big-endian systems (size_t is 8 bytes on 64-bit) */ + uint32_t string_count_u32 = (uint32_t)writer->string_count; + uint32_t frame_count_u32 = (uint32_t)writer->frame_count; + memcpy(footer + FTR_OFF_STRINGS, &string_count_u32, FTR_SIZE_STRINGS); + memcpy(footer + FTR_OFF_FRAMES, &frame_count_u32, FTR_SIZE_FRAMES); + memcpy(footer + FTR_OFF_FILE_SIZE, &file_size, FTR_SIZE_FILE_SIZE); + /* checksum (FTR_OFF_CHECKSUM..FILE_FOOTER_SIZE-1): placeholder zeros */ + if (fwrite_checked_allow_threads(footer, FILE_FOOTER_SIZE, writer->fp) < 0) { + return -1; + } + + if (FSEEK64(writer->fp, 0, SEEK_SET) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + + /* Convert file offsets and counts to fixed-width types for portable header format. + * This ensures correct behavior on both little-endian and big-endian systems. */ + uint64_t string_table_offset_u64 = (uint64_t)string_table_offset; + uint64_t frame_table_offset_u64 = (uint64_t)frame_table_offset; + uint32_t thread_count_u32 = (uint32_t)writer->thread_count; + uint32_t compression_type_u32 = (uint32_t)writer->compression_type; + + uint8_t header[FILE_HEADER_SIZE] = {0}; + uint32_t magic = BINARY_FORMAT_MAGIC; + uint32_t version = BINARY_FORMAT_VERSION; + memcpy(header + HDR_OFF_MAGIC, &magic, HDR_SIZE_MAGIC); + memcpy(header + HDR_OFF_VERSION, &version, HDR_SIZE_VERSION); + header[HDR_OFF_PY_MAJOR] = PY_MAJOR_VERSION; + header[HDR_OFF_PY_MINOR] = PY_MINOR_VERSION; + header[HDR_OFF_PY_MICRO] = PY_MICRO_VERSION; + memcpy(header + HDR_OFF_START_TIME, &writer->start_time_us, HDR_SIZE_START_TIME); + memcpy(header + HDR_OFF_INTERVAL, &writer->sample_interval_us, HDR_SIZE_INTERVAL); + memcpy(header + HDR_OFF_SAMPLES, &writer->total_samples, HDR_SIZE_SAMPLES); + memcpy(header + HDR_OFF_THREADS, &thread_count_u32, HDR_SIZE_THREADS); + memcpy(header + HDR_OFF_STR_TABLE, &string_table_offset_u64, HDR_SIZE_STR_TABLE); + memcpy(header + HDR_OFF_FRAME_TABLE, &frame_table_offset_u64, HDR_SIZE_FRAME_TABLE); + memcpy(header + HDR_OFF_COMPRESSION, &compression_type_u32, HDR_SIZE_COMPRESSION); + if (fwrite_checked_allow_threads(header, FILE_HEADER_SIZE, writer->fp) < 0) { + return -1; + } + + if (Py_fclose(writer->fp) != 0) { + writer->fp = NULL; + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + writer->fp = NULL; + + return 0; +} + +void +binary_writer_destroy(BinaryWriter *writer) +{ + if (!writer) { + return; + } + + if (writer->fp) { + Py_fclose(writer->fp); + } + + PyMem_Free(writer->write_buffer); + +#ifdef HAVE_ZSTD + if (writer->zstd.cctx) { + ZSTD_freeCCtx(writer->zstd.cctx); + } + PyMem_Free(writer->zstd.compressed_buffer); +#endif + + if (writer->string_hash) { + _Py_hashtable_destroy(writer->string_hash); + } + if (writer->strings) { + for (size_t i = 0; i < writer->string_count; i++) { + PyMem_Free(writer->strings[i]); + } + PyMem_Free(writer->strings); + } + PyMem_Free(writer->string_lengths); + + if (writer->frame_hash) { + _Py_hashtable_destroy(writer->frame_hash); + } + PyMem_Free(writer->frame_entries); + + if (writer->thread_entries) { + for (size_t i = 0; i < writer->thread_count; i++) { + PyMem_Free(writer->thread_entries[i].prev_stack); + PyMem_Free(writer->thread_entries[i].pending_rle); + } + PyMem_Free(writer->thread_entries); + } + + PyMem_Free(writer); +} + +#undef PYLONG_TO_INT32_OR_DEFAULT diff --git a/Modules/_remote_debugging/clinic/module.c.h b/Modules/_remote_debugging/clinic/module.c.h new file mode 100644 index 00000000000000..d01f3d13e85f09 --- /dev/null +++ b/Modules/_remote_debugging/clinic/module.c.h @@ -0,0 +1,1591 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() +#include "pycore_long.h" // _PyLong_UnsignedLongLong_Converter() +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +PyDoc_STRVAR(_remote_debugging_RemoteUnwinder___init____doc__, +"RemoteUnwinder(pid, *, all_threads=False, only_active_thread=False,\n" +" mode=0, debug=False, skip_non_matching_threads=True,\n" +" native=False, gc=False, opcodes=False,\n" +" cache_frames=False, stats=False)\n" +"--\n" +"\n" +"Initialize a new RemoteUnwinder object for debugging a remote Python process.\n" +"\n" +"Args:\n" +" pid: Process ID of the target Python process to debug\n" +" all_threads: If True, initialize state for all threads in the\n" +" process. If False, only initialize for the main thread.\n" +" only_active_thread: If True, only sample the thread holding the GIL.\n" +" mode: Profiling mode: 0=WALL (wall-time), 1=CPU (cpu-time), 2=GIL\n" +" (gil-time). Cannot be used together with all_threads=True.\n" +" debug: If True, chain exceptions to explain the sequence of events\n" +" that lead to the exception.\n" +" skip_non_matching_threads: If True, skip threads that don\'t match\n" +" the selected mode. If False, include all threads regardless of\n" +" mode.\n" +" native: If True, include artificial \"<native>\" frames to denote\n" +" calls to non-Python code.\n" +" gc: If True, include artificial \"<GC>\" frames to denote active\n" +" garbage collection.\n" +" opcodes: If True, gather bytecode opcode information for\n" +" instruction-level profiling.\n" +" cache_frames: If True, enable frame caching optimization to avoid\n" +" re-reading unchanged parent frames between samples.\n" +" stats: If True, collect statistics about cache hits, memory reads,\n" +" etc. Use get_stats() to retrieve the collected statistics.\n" +"\n" +"The RemoteUnwinder provides functionality to inspect and debug a running\n" +"Python process, including examining thread states, stack frames and\n" +"other runtime data.\n" +"\n" +"Raises:\n" +" PermissionError: If access to the target process is denied\n" +" OSError: If unable to attach to the target process or access its\n" +" memory\n" +" RuntimeError: If unable to read debug information from the target\n" +" process\n" +" ValueError: If both all_threads and only_active_thread are True"); + +static int +_remote_debugging_RemoteUnwinder___init___impl(RemoteUnwinderObject *self, + int pid, int all_threads, + int only_active_thread, + int mode, int debug, + int skip_non_matching_threads, + int native, int gc, + int opcodes, int cache_frames, + int stats); + +static int +_remote_debugging_RemoteUnwinder___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 11 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(pid), &_Py_ID(all_threads), &_Py_ID(only_active_thread), &_Py_ID(mode), &_Py_ID(debug), &_Py_ID(skip_non_matching_threads), &_Py_ID(native), &_Py_ID(gc), &_Py_ID(opcodes), &_Py_ID(cache_frames), &_Py_ID(stats), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"pid", "all_threads", "only_active_thread", "mode", "debug", "skip_non_matching_threads", "native", "gc", "opcodes", "cache_frames", "stats", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "RemoteUnwinder", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[11]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + int pid; + int all_threads = 0; + int only_active_thread = 0; + int mode = 0; + int debug = 0; + int skip_non_matching_threads = 1; + int native = 0; + int gc = 0; + int opcodes = 0; + int cache_frames = 0; + int stats = 0; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + pid = PyLong_AsInt(fastargs[0]); + if (pid == -1 && PyErr_Occurred()) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (fastargs[1]) { + all_threads = PyObject_IsTrue(fastargs[1]); + if (all_threads < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[2]) { + only_active_thread = PyObject_IsTrue(fastargs[2]); + if (only_active_thread < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[3]) { + mode = PyLong_AsInt(fastargs[3]); + if (mode == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[4]) { + debug = PyObject_IsTrue(fastargs[4]); + if (debug < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[5]) { + skip_non_matching_threads = PyObject_IsTrue(fastargs[5]); + if (skip_non_matching_threads < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[6]) { + native = PyObject_IsTrue(fastargs[6]); + if (native < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[7]) { + gc = PyObject_IsTrue(fastargs[7]); + if (gc < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[8]) { + opcodes = PyObject_IsTrue(fastargs[8]); + if (opcodes < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[9]) { + cache_frames = PyObject_IsTrue(fastargs[9]); + if (cache_frames < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + stats = PyObject_IsTrue(fastargs[10]); + if (stats < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _remote_debugging_RemoteUnwinder___init___impl((RemoteUnwinderObject *)self, pid, all_threads, only_active_thread, mode, debug, skip_non_matching_threads, native, gc, opcodes, cache_frames, stats); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_RemoteUnwinder_get_stack_trace__doc__, +"get_stack_trace($self, /)\n" +"--\n" +"\n" +"Returns stack traces for all interpreters and threads in process.\n" +"\n" +"Each element in the returned list is a tuple of (interpreter_id,\n" +"thread_list), where:\n" +"- interpreter_id is the interpreter identifier\n" +"- thread_list is a list of tuples (thread_id, frame_list) for\n" +" threads in that interpreter\n" +" - thread_id is the OS thread identifier\n" +" - frame_list is a list of tuples (function_name, filename,\n" +" line_number) representing the Python stack frames for that\n" +" thread, ordered from most recent to oldest\n" +"\n" +"The threads returned depend on the initialization parameters:\n" +"- If only_active_thread was True: returns only the thread holding\n" +" the GIL across all interpreters\n" +"- If all_threads was True: returns all threads across all\n" +" interpreters\n" +"- Otherwise: returns only the main thread of each interpreter\n" +"\n" +"Example:\n" +" [\n" +" (0, [ # Main interpreter\n" +" (1234, [\n" +" (\'process_data\', \'worker.py\', 127),\n" +" (\'run_worker\', \'worker.py\', 45),\n" +" (\'main\', \'app.py\', 23)\n" +" ]),\n" +" (1235, [\n" +" (\'handle_request\', \'server.py\', 89),\n" +" (\'serve_forever\', \'server.py\', 52)\n" +" ])\n" +" ]),\n" +" (1, [ # Sub-interpreter\n" +" (1236, [\n" +" (\'sub_worker\', \'sub.py\', 15)\n" +" ])\n" +" ])\n" +" ]\n" +"\n" +"Raises:\n" +" RuntimeError: If there is an error copying memory from the\n" +" target process\n" +" OSError: If there is an error accessing the target process\n" +" PermissionError: If access to the target process is denied\n" +" UnicodeDecodeError: If there is an error decoding strings from\n" +" the target process"); + +#define _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_STACK_TRACE_METHODDEF \ + {"get_stack_trace", (PyCFunction)_remote_debugging_RemoteUnwinder_get_stack_trace, METH_NOARGS, _remote_debugging_RemoteUnwinder_get_stack_trace__doc__}, + +static PyObject * +_remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self); + +static PyObject * +_remote_debugging_RemoteUnwinder_get_stack_trace(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _remote_debugging_RemoteUnwinder_get_stack_trace_impl((RemoteUnwinderObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_RemoteUnwinder_get_all_awaited_by__doc__, +"get_all_awaited_by($self, /)\n" +"--\n" +"\n" +"Get all tasks and their awaited_by relationships from the remote process.\n" +"\n" +"This provides a tree structure showing which tasks are waiting for\n" +"other tasks.\n" +"\n" +"For each task, returns:\n" +"1. The call stack frames leading to where the task is currently\n" +" executing\n" +"2. The name of the task\n" +"3. A list of tasks that this task is waiting for, with their own\n" +" frames/names/etc\n" +"\n" +"Returns a list of [frames, task_name, subtasks] where:\n" +"- frames: List of (func_name, filename, lineno) showing the call\n" +" stack\n" +"- task_name: String identifier for the task\n" +"- subtasks: List of tasks being awaited by this task, in same format\n" +"\n" +"Raises:\n" +" RuntimeError: If AsyncioDebug section is not available in the\n" +" remote process\n" +" MemoryError: If memory allocation fails\n" +" OSError: If reading from the remote process fails\n" +"\n" +"Example output:\n" +"[\n" +" [\n" +" [(\"c5\", \"script.py\", 10), (\"c4\", \"script.py\", 14)],\n" +" \"c2_root\",\n" +" [\n" +" [\n" +" [(\"c1\", \"script.py\", 23)],\n" +" \"sub_main_2\",\n" +" [...]\n" +" ],\n" +" [...]\n" +" ]\n" +" ]\n" +"]"); + +#define _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_ALL_AWAITED_BY_METHODDEF \ + {"get_all_awaited_by", (PyCFunction)_remote_debugging_RemoteUnwinder_get_all_awaited_by, METH_NOARGS, _remote_debugging_RemoteUnwinder_get_all_awaited_by__doc__}, + +static PyObject * +_remote_debugging_RemoteUnwinder_get_all_awaited_by_impl(RemoteUnwinderObject *self); + +static PyObject * +_remote_debugging_RemoteUnwinder_get_all_awaited_by(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _remote_debugging_RemoteUnwinder_get_all_awaited_by_impl((RemoteUnwinderObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_RemoteUnwinder_get_async_stack_trace__doc__, +"get_async_stack_trace($self, /)\n" +"--\n" +"\n" +"Get the currently running async tasks and their dependency graphs from the remote process.\n" +"\n" +"This returns information about running tasks and all tasks that are\n" +"waiting for them, forming a complete dependency graph for each\n" +"thread\'s active task.\n" +"\n" +"For each thread with a running task, returns the running task plus\n" +"all tasks that transitively depend on it (tasks waiting for the\n" +"running task, tasks waiting for those tasks, etc.).\n" +"\n" +"Returns a list of per-thread results, where each thread result\n" +"contains:\n" +"- Thread ID\n" +"- List of task information for the running task and all its waiters\n" +"\n" +"Each task info contains:\n" +"- Task ID (memory address)\n" +"- Task name\n" +"- Call stack frames: List of (func_name, filename, lineno)\n" +"- List of tasks waiting for this task (recursive structure)\n" +"\n" +"Raises:\n" +" RuntimeError: If AsyncioDebug section is not available in the\n" +" target process\n" +" MemoryError: If memory allocation fails\n" +" OSError: If reading from the remote process fails\n" +"\n" +"Example output (similar structure to get_all_awaited_by but only for\n" +"running tasks):\n" +"[\n" +" (140234, [\n" +" (4345585712, \'main_task\',\n" +" [(\"run_server\", \"server.py\", 127), (\"main\", \"app.py\", 23)],\n" +" [\n" +" (4345585800, \'worker_1\', [...], [...]),\n" +" (4345585900, \'worker_2\', [...], [...])\n" +" ])\n" +" ])\n" +"]"); + +#define _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_ASYNC_STACK_TRACE_METHODDEF \ + {"get_async_stack_trace", (PyCFunction)_remote_debugging_RemoteUnwinder_get_async_stack_trace, METH_NOARGS, _remote_debugging_RemoteUnwinder_get_async_stack_trace__doc__}, + +static PyObject * +_remote_debugging_RemoteUnwinder_get_async_stack_trace_impl(RemoteUnwinderObject *self); + +static PyObject * +_remote_debugging_RemoteUnwinder_get_async_stack_trace(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _remote_debugging_RemoteUnwinder_get_async_stack_trace_impl((RemoteUnwinderObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_RemoteUnwinder_get_stats__doc__, +"get_stats($self, /)\n" +"--\n" +"\n" +"Get collected statistics about profiling performance.\n" +"\n" +"Returns a dictionary containing statistics about cache performance,\n" +"memory reads, and other profiling metrics. Only available if the\n" +"RemoteUnwinder was created with stats=True.\n" +"\n" +"Returns:\n" +" dict: A dictionary containing:\n" +" - total_samples: Total number of get_stack_trace calls\n" +" - frame_cache_hits: Full cache hits (entire stack unchanged)\n" +" - frame_cache_misses: Cache misses requiring full walk\n" +" - frame_cache_partial_hits: Partial hits (stopped at cached\n" +" frame)\n" +" - frames_read_from_cache: Total frames retrieved from cache\n" +" - frames_read_from_memory: Total frames read from remote\n" +" memory\n" +" - memory_reads: Total remote memory read operations\n" +" - memory_bytes_read: Total bytes read from remote memory\n" +" - code_object_cache_hits: Code object cache hits\n" +" - code_object_cache_misses: Code object cache misses\n" +" - stale_cache_invalidations: Times stale cache entries were\n" +" cleared\n" +" - batched_read_attempts: Batched remote-read attempts\n" +" - batched_read_successes: Attempts that read all requested\n" +" segments\n" +" - batched_read_misses: Attempts that fell back or partially\n" +" read\n" +" - batched_read_segments_requested: Segments requested by\n" +" batched reads\n" +" - batched_read_segments_completed: Segments completed by\n" +" batched reads\n" +" - frame_cache_hit_rate: Percentage of samples that hit the\n" +" cache\n" +" - code_object_cache_hit_rate: Percentage of code object\n" +" lookups that hit cache\n" +" - batched_read_success_rate: Percentage of batched reads\n" +" that completed all segments\n" +" - batched_read_segment_completion_rate: Percentage of\n" +" requested segments read by batched reads\n" +"\n" +"Raises:\n" +" RuntimeError: If stats collection was not enabled (stats=False)"); + +#define _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_STATS_METHODDEF \ + {"get_stats", (PyCFunction)_remote_debugging_RemoteUnwinder_get_stats, METH_NOARGS, _remote_debugging_RemoteUnwinder_get_stats__doc__}, + +static PyObject * +_remote_debugging_RemoteUnwinder_get_stats_impl(RemoteUnwinderObject *self); + +static PyObject * +_remote_debugging_RemoteUnwinder_get_stats(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _remote_debugging_RemoteUnwinder_get_stats_impl((RemoteUnwinderObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_RemoteUnwinder_pause_threads__doc__, +"pause_threads($self, /)\n" +"--\n" +"\n" +"Pause all threads in the target process.\n" +"\n" +"This stops all threads in the target process to allow for consistent\n" +"memory reads during sampling. Must be paired with a call to\n" +"resume_threads().\n" +"\n" +"Returns True if threads were successfully paused, False if they were\n" +"already paused.\n" +"\n" +"Raises:\n" +" RuntimeError: If there is an error stopping the threads"); + +#define _REMOTE_DEBUGGING_REMOTEUNWINDER_PAUSE_THREADS_METHODDEF \ + {"pause_threads", (PyCFunction)_remote_debugging_RemoteUnwinder_pause_threads, METH_NOARGS, _remote_debugging_RemoteUnwinder_pause_threads__doc__}, + +static PyObject * +_remote_debugging_RemoteUnwinder_pause_threads_impl(RemoteUnwinderObject *self); + +static PyObject * +_remote_debugging_RemoteUnwinder_pause_threads(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _remote_debugging_RemoteUnwinder_pause_threads_impl((RemoteUnwinderObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_RemoteUnwinder_resume_threads__doc__, +"resume_threads($self, /)\n" +"--\n" +"\n" +"Resume all threads in the target process.\n" +"\n" +"This resumes threads that were previously paused with\n" +"pause_threads().\n" +"\n" +"Returns True if threads were successfully resumed, False if they\n" +"were not paused."); + +#define _REMOTE_DEBUGGING_REMOTEUNWINDER_RESUME_THREADS_METHODDEF \ + {"resume_threads", (PyCFunction)_remote_debugging_RemoteUnwinder_resume_threads, METH_NOARGS, _remote_debugging_RemoteUnwinder_resume_threads__doc__}, + +static PyObject * +_remote_debugging_RemoteUnwinder_resume_threads_impl(RemoteUnwinderObject *self); + +static PyObject * +_remote_debugging_RemoteUnwinder_resume_threads(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _remote_debugging_RemoteUnwinder_resume_threads_impl((RemoteUnwinderObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_GCMonitor___init____doc__, +"GCMonitor(pid, *, debug=False)\n" +"--\n" +"\n" +"Initialize a new GCMonitor object for monitoring GC events from remote process.\n" +"\n" +"Args:\n" +" pid: Process ID of the target Python process to monitor\n" +" debug: If True, chain exceptions to explain the sequence of\n" +" events that lead to the exception.\n" +"\n" +"The GCMonitor provides functionality to read GC statistics from\n" +"a running Python process.\n" +"\n" +"Raises:\n" +" PermissionError: If access to the target process is denied\n" +" OSError: If unable to attach to the target process or access\n" +" its memory\n" +" RuntimeError: If unable to read debug information from the\n" +" target process"); + +static int +_remote_debugging_GCMonitor___init___impl(GCMonitorObject *self, int pid, + int debug); + +static int +_remote_debugging_GCMonitor___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(pid), &_Py_ID(debug), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"pid", "debug", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "GCMonitor", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + int pid; + int debug = 0; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + pid = PyLong_AsInt(fastargs[0]); + if (pid == -1 && PyErr_Occurred()) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + debug = PyObject_IsTrue(fastargs[1]); + if (debug < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _remote_debugging_GCMonitor___init___impl((GCMonitorObject *)self, pid, debug); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_GCMonitor_get_gc_stats__doc__, +"get_gc_stats($self, /, all_interpreters=False)\n" +"--\n" +"\n" +"Get garbage collector statistics from external Python process.\n" +"\n" +" all_interpreters\n" +" If True, return GC statistics from all interpreters.\n" +" If False, return only from main interpreter.\n" +"\n" +"Returns a list of GCStatsInfo objects with GC statistics data.\n" +"\n" +"Returns:\n" +" list of GCStatsInfo: A list of stats samples containing:\n" +" - gen: GC generation number.\n" +" - iid: Interpreter ID.\n" +" - ts_start: Raw timestamp at collection start.\n" +" - ts_stop: Raw timestamp at collection stop.\n" +" - collections: Total number of collections.\n" +" - collected: Total number of collected objects.\n" +" - uncollectable: Total number of uncollectable objects.\n" +" - candidates: Total objects considered and traversed.\n" +" - heap_size: number of live objects.\n" +" - duration: Total collection time, in seconds.\n" +"\n" +"Raises:\n" +" RuntimeError: If the target process cannot be inspected or if\n" +" its debug offsets or GC stats layout are incompatible."); + +#define _REMOTE_DEBUGGING_GCMONITOR_GET_GC_STATS_METHODDEF \ + {"get_gc_stats", _PyCFunction_CAST(_remote_debugging_GCMonitor_get_gc_stats), METH_FASTCALL|METH_KEYWORDS, _remote_debugging_GCMonitor_get_gc_stats__doc__}, + +static PyObject * +_remote_debugging_GCMonitor_get_gc_stats_impl(GCMonitorObject *self, + int all_interpreters); + +static PyObject * +_remote_debugging_GCMonitor_get_gc_stats(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(all_interpreters), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"all_interpreters", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "get_gc_stats", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int all_interpreters = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + all_interpreters = PyObject_IsTrue(args[0]); + if (all_interpreters < 0) { + goto exit; + } +skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _remote_debugging_GCMonitor_get_gc_stats_impl((GCMonitorObject *)self, all_interpreters); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_BinaryWriter___init____doc__, +"BinaryWriter(filename, sample_interval_us, start_time_us, *,\n" +" compression=0)\n" +"--\n" +"\n" +"High-performance binary writer for profiling data.\n" +"\n" +"Arguments:\n" +" filename: Path to output file\n" +" sample_interval_us: Sampling interval in microseconds\n" +" start_time_us: Start timestamp in microseconds (from\n" +" time.monotonic() * 1e6)\n" +" compression: 0=none, 1=zstd (default: 0)\n" +"\n" +"Use as a context manager or call finalize() when done."); + +static int +_remote_debugging_BinaryWriter___init___impl(BinaryWriterObject *self, + PyObject *filename, + unsigned long long sample_interval_us, + unsigned long long start_time_us, + int compression); + +static int +_remote_debugging_BinaryWriter___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(filename), &_Py_ID(sample_interval_us), &_Py_ID(start_time_us), &_Py_ID(compression), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"filename", "sample_interval_us", "start_time_us", "compression", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "BinaryWriter", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 3; + PyObject *filename; + unsigned long long sample_interval_us; + unsigned long long start_time_us; + int compression = 0; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + filename = fastargs[0]; + if (!_PyLong_UnsignedLongLong_Converter(fastargs[1], &sample_interval_us)) { + goto exit; + } + if (!_PyLong_UnsignedLongLong_Converter(fastargs[2], &start_time_us)) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + compression = PyLong_AsInt(fastargs[3]); + if (compression == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_kwonly: + return_value = _remote_debugging_BinaryWriter___init___impl((BinaryWriterObject *)self, filename, sample_interval_us, start_time_us, compression); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_BinaryWriter_write_sample__doc__, +"write_sample($self, /, stack_frames, timestamp_us)\n" +"--\n" +"\n" +"Write a sample to the binary file.\n" +"\n" +"Arguments:\n" +" stack_frames: List of InterpreterInfo objects\n" +" timestamp_us: Current timestamp in microseconds (from\n" +" time.monotonic() * 1e6)"); + +#define _REMOTE_DEBUGGING_BINARYWRITER_WRITE_SAMPLE_METHODDEF \ + {"write_sample", _PyCFunction_CAST(_remote_debugging_BinaryWriter_write_sample), METH_FASTCALL|METH_KEYWORDS, _remote_debugging_BinaryWriter_write_sample__doc__}, + +static PyObject * +_remote_debugging_BinaryWriter_write_sample_impl(BinaryWriterObject *self, + PyObject *stack_frames, + unsigned long long timestamp_us); + +static PyObject * +_remote_debugging_BinaryWriter_write_sample(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(stack_frames), &_Py_ID(timestamp_us), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"stack_frames", "timestamp_us", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "write_sample", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject *stack_frames; + unsigned long long timestamp_us; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + stack_frames = args[0]; + if (!_PyLong_UnsignedLongLong_Converter(args[1], &timestamp_us)) { + goto exit; + } + return_value = _remote_debugging_BinaryWriter_write_sample_impl((BinaryWriterObject *)self, stack_frames, timestamp_us); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_BinaryWriter_finalize__doc__, +"finalize($self, /)\n" +"--\n" +"\n" +"Finalize and close the binary file.\n" +"\n" +"Writes string/frame tables, footer, and updates header."); + +#define _REMOTE_DEBUGGING_BINARYWRITER_FINALIZE_METHODDEF \ + {"finalize", (PyCFunction)_remote_debugging_BinaryWriter_finalize, METH_NOARGS, _remote_debugging_BinaryWriter_finalize__doc__}, + +static PyObject * +_remote_debugging_BinaryWriter_finalize_impl(BinaryWriterObject *self); + +static PyObject * +_remote_debugging_BinaryWriter_finalize(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _remote_debugging_BinaryWriter_finalize_impl((BinaryWriterObject *)self); +} + +PyDoc_STRVAR(_remote_debugging_BinaryWriter_close__doc__, +"close($self, /)\n" +"--\n" +"\n" +"Close the writer without finalizing (discards data)."); + +#define _REMOTE_DEBUGGING_BINARYWRITER_CLOSE_METHODDEF \ + {"close", (PyCFunction)_remote_debugging_BinaryWriter_close, METH_NOARGS, _remote_debugging_BinaryWriter_close__doc__}, + +static PyObject * +_remote_debugging_BinaryWriter_close_impl(BinaryWriterObject *self); + +static PyObject * +_remote_debugging_BinaryWriter_close(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _remote_debugging_BinaryWriter_close_impl((BinaryWriterObject *)self); +} + +PyDoc_STRVAR(_remote_debugging_BinaryWriter___enter____doc__, +"__enter__($self, /)\n" +"--\n" +"\n" +"Enter context manager."); + +#define _REMOTE_DEBUGGING_BINARYWRITER___ENTER___METHODDEF \ + {"__enter__", (PyCFunction)_remote_debugging_BinaryWriter___enter__, METH_NOARGS, _remote_debugging_BinaryWriter___enter____doc__}, + +static PyObject * +_remote_debugging_BinaryWriter___enter___impl(BinaryWriterObject *self); + +static PyObject * +_remote_debugging_BinaryWriter___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _remote_debugging_BinaryWriter___enter___impl((BinaryWriterObject *)self); +} + +PyDoc_STRVAR(_remote_debugging_BinaryWriter___exit____doc__, +"__exit__($self, /, exc_type=None, exc_val=None, exc_tb=None)\n" +"--\n" +"\n" +"Exit context manager, finalizing the file."); + +#define _REMOTE_DEBUGGING_BINARYWRITER___EXIT___METHODDEF \ + {"__exit__", _PyCFunction_CAST(_remote_debugging_BinaryWriter___exit__), METH_FASTCALL|METH_KEYWORDS, _remote_debugging_BinaryWriter___exit____doc__}, + +static PyObject * +_remote_debugging_BinaryWriter___exit___impl(BinaryWriterObject *self, + PyObject *exc_type, + PyObject *exc_val, + PyObject *exc_tb); + +static PyObject * +_remote_debugging_BinaryWriter___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(exc_type), &_Py_ID(exc_val), &_Py_ID(exc_tb), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"exc_type", "exc_val", "exc_tb", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "__exit__", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *exc_type = Py_None; + PyObject *exc_val = Py_None; + PyObject *exc_tb = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + exc_type = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[1]) { + exc_val = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + exc_tb = args[2]; +skip_optional_pos: + return_value = _remote_debugging_BinaryWriter___exit___impl((BinaryWriterObject *)self, exc_type, exc_val, exc_tb); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_BinaryWriter_get_stats__doc__, +"get_stats($self, /)\n" +"--\n" +"\n" +"Get encoding statistics for the writer.\n" +"\n" +"Returns a dict with encoding statistics including\n" +"repeat/full/suffix/pop-push record counts, frames written/saved, and\n" +"compression ratio."); + +#define _REMOTE_DEBUGGING_BINARYWRITER_GET_STATS_METHODDEF \ + {"get_stats", (PyCFunction)_remote_debugging_BinaryWriter_get_stats, METH_NOARGS, _remote_debugging_BinaryWriter_get_stats__doc__}, + +static PyObject * +_remote_debugging_BinaryWriter_get_stats_impl(BinaryWriterObject *self); + +static PyObject * +_remote_debugging_BinaryWriter_get_stats(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _remote_debugging_BinaryWriter_get_stats_impl((BinaryWriterObject *)self); +} + +PyDoc_STRVAR(_remote_debugging_BinaryReader___init____doc__, +"BinaryReader(filename)\n" +"--\n" +"\n" +"High-performance binary reader for profiling data.\n" +"\n" +"Arguments:\n" +" filename: Path to input file\n" +"\n" +"Use as a context manager or call close() when done."); + +static int +_remote_debugging_BinaryReader___init___impl(BinaryReaderObject *self, + PyObject *filename); + +static int +_remote_debugging_BinaryReader___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(filename), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"filename", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "BinaryReader", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + PyObject *filename; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + filename = fastargs[0]; + return_value = _remote_debugging_BinaryReader___init___impl((BinaryReaderObject *)self, filename); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_BinaryReader_replay__doc__, +"replay($self, /, collector, progress_callback=None)\n" +"--\n" +"\n" +"Replay samples through a collector.\n" +"\n" +"Arguments:\n" +" collector: Collector object with collect() method\n" +" progress_callback: Optional callable(current, total)\n" +"\n" +"Returns:\n" +" Number of samples replayed"); + +#define _REMOTE_DEBUGGING_BINARYREADER_REPLAY_METHODDEF \ + {"replay", _PyCFunction_CAST(_remote_debugging_BinaryReader_replay), METH_FASTCALL|METH_KEYWORDS, _remote_debugging_BinaryReader_replay__doc__}, + +static PyObject * +_remote_debugging_BinaryReader_replay_impl(BinaryReaderObject *self, + PyObject *collector, + PyObject *progress_callback); + +static PyObject * +_remote_debugging_BinaryReader_replay(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(collector), &_Py_ID(progress_callback), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"collector", "progress_callback", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "replay", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *collector; + PyObject *progress_callback = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + collector = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + progress_callback = args[1]; +skip_optional_pos: + return_value = _remote_debugging_BinaryReader_replay_impl((BinaryReaderObject *)self, collector, progress_callback); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_BinaryReader_get_info__doc__, +"get_info($self, /)\n" +"--\n" +"\n" +"Get metadata about the binary file.\n" +"\n" +"Returns:\n" +" Dict with file metadata"); + +#define _REMOTE_DEBUGGING_BINARYREADER_GET_INFO_METHODDEF \ + {"get_info", (PyCFunction)_remote_debugging_BinaryReader_get_info, METH_NOARGS, _remote_debugging_BinaryReader_get_info__doc__}, + +static PyObject * +_remote_debugging_BinaryReader_get_info_impl(BinaryReaderObject *self); + +static PyObject * +_remote_debugging_BinaryReader_get_info(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _remote_debugging_BinaryReader_get_info_impl((BinaryReaderObject *)self); +} + +PyDoc_STRVAR(_remote_debugging_BinaryReader_get_stats__doc__, +"get_stats($self, /)\n" +"--\n" +"\n" +"Get reconstruction statistics from replay.\n" +"\n" +"Returns a dict with statistics about record types decoded and\n" +"samples reconstructed during replay."); + +#define _REMOTE_DEBUGGING_BINARYREADER_GET_STATS_METHODDEF \ + {"get_stats", (PyCFunction)_remote_debugging_BinaryReader_get_stats, METH_NOARGS, _remote_debugging_BinaryReader_get_stats__doc__}, + +static PyObject * +_remote_debugging_BinaryReader_get_stats_impl(BinaryReaderObject *self); + +static PyObject * +_remote_debugging_BinaryReader_get_stats(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _remote_debugging_BinaryReader_get_stats_impl((BinaryReaderObject *)self); +} + +PyDoc_STRVAR(_remote_debugging_BinaryReader_close__doc__, +"close($self, /)\n" +"--\n" +"\n" +"Close the reader and free resources."); + +#define _REMOTE_DEBUGGING_BINARYREADER_CLOSE_METHODDEF \ + {"close", (PyCFunction)_remote_debugging_BinaryReader_close, METH_NOARGS, _remote_debugging_BinaryReader_close__doc__}, + +static PyObject * +_remote_debugging_BinaryReader_close_impl(BinaryReaderObject *self); + +static PyObject * +_remote_debugging_BinaryReader_close(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _remote_debugging_BinaryReader_close_impl((BinaryReaderObject *)self); +} + +PyDoc_STRVAR(_remote_debugging_BinaryReader___enter____doc__, +"__enter__($self, /)\n" +"--\n" +"\n" +"Enter context manager."); + +#define _REMOTE_DEBUGGING_BINARYREADER___ENTER___METHODDEF \ + {"__enter__", (PyCFunction)_remote_debugging_BinaryReader___enter__, METH_NOARGS, _remote_debugging_BinaryReader___enter____doc__}, + +static PyObject * +_remote_debugging_BinaryReader___enter___impl(BinaryReaderObject *self); + +static PyObject * +_remote_debugging_BinaryReader___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _remote_debugging_BinaryReader___enter___impl((BinaryReaderObject *)self); +} + +PyDoc_STRVAR(_remote_debugging_BinaryReader___exit____doc__, +"__exit__($self, /, exc_type=None, exc_val=None, exc_tb=None)\n" +"--\n" +"\n" +"Exit context manager, closing the file."); + +#define _REMOTE_DEBUGGING_BINARYREADER___EXIT___METHODDEF \ + {"__exit__", _PyCFunction_CAST(_remote_debugging_BinaryReader___exit__), METH_FASTCALL|METH_KEYWORDS, _remote_debugging_BinaryReader___exit____doc__}, + +static PyObject * +_remote_debugging_BinaryReader___exit___impl(BinaryReaderObject *self, + PyObject *exc_type, + PyObject *exc_val, + PyObject *exc_tb); + +static PyObject * +_remote_debugging_BinaryReader___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(exc_type), &_Py_ID(exc_val), &_Py_ID(exc_tb), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"exc_type", "exc_val", "exc_tb", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "__exit__", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *exc_type = Py_None; + PyObject *exc_val = Py_None; + PyObject *exc_tb = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + exc_type = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[1]) { + exc_val = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + exc_tb = args[2]; +skip_optional_pos: + return_value = _remote_debugging_BinaryReader___exit___impl((BinaryReaderObject *)self, exc_type, exc_val, exc_tb); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_zstd_available__doc__, +"zstd_available($module, /)\n" +"--\n" +"\n" +"Check if zstd compression is available.\n" +"\n" +"Returns:\n" +" True if zstd available, False otherwise"); + +#define _REMOTE_DEBUGGING_ZSTD_AVAILABLE_METHODDEF \ + {"zstd_available", (PyCFunction)_remote_debugging_zstd_available, METH_NOARGS, _remote_debugging_zstd_available__doc__}, + +static PyObject * +_remote_debugging_zstd_available_impl(PyObject *module); + +static PyObject * +_remote_debugging_zstd_available(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _remote_debugging_zstd_available_impl(module); +} + +PyDoc_STRVAR(_remote_debugging_get_child_pids__doc__, +"get_child_pids($module, /, pid, *, recursive=True)\n" +"--\n" +"\n" +"Get all child process IDs of the given process.\n" +"\n" +" pid\n" +" Process ID of the parent process\n" +" recursive\n" +" If True, return all descendants (children, grandchildren, etc.).\n" +" If False, return only direct children.\n" +"\n" +"Returns a list of child process IDs. Returns an empty list if no\n" +"children are found.\n" +"\n" +"This function provides a snapshot of child processes at a moment in\n" +"time. Child processes may exit or new ones may be created after the\n" +"list is returned.\n" +"\n" +"Raises:\n" +" OSError: If unable to enumerate processes\n" +" NotImplementedError: If not supported on this platform"); + +#define _REMOTE_DEBUGGING_GET_CHILD_PIDS_METHODDEF \ + {"get_child_pids", _PyCFunction_CAST(_remote_debugging_get_child_pids), METH_FASTCALL|METH_KEYWORDS, _remote_debugging_get_child_pids__doc__}, + +static PyObject * +_remote_debugging_get_child_pids_impl(PyObject *module, int pid, + int recursive); + +static PyObject * +_remote_debugging_get_child_pids(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(pid), &_Py_ID(recursive), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"pid", "recursive", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "get_child_pids", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + int pid; + int recursive = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + pid = PyLong_AsInt(args[0]); + if (pid == -1 && PyErr_Occurred()) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + recursive = PyObject_IsTrue(args[1]); + if (recursive < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _remote_debugging_get_child_pids_impl(module, pid, recursive); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_is_python_process__doc__, +"is_python_process($module, /, pid)\n" +"--\n" +"\n" +"Check if a process is a Python process."); + +#define _REMOTE_DEBUGGING_IS_PYTHON_PROCESS_METHODDEF \ + {"is_python_process", _PyCFunction_CAST(_remote_debugging_is_python_process), METH_FASTCALL|METH_KEYWORDS, _remote_debugging_is_python_process__doc__}, + +static PyObject * +_remote_debugging_is_python_process_impl(PyObject *module, int pid); + +static PyObject * +_remote_debugging_is_python_process(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(pid), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"pid", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_python_process", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int pid; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + pid = PyLong_AsInt(args[0]); + if (pid == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _remote_debugging_is_python_process_impl(module, pid); + +exit: + return return_value; +} + +PyDoc_STRVAR(_remote_debugging_get_gc_stats__doc__, +"get_gc_stats($module, /, pid, *, all_interpreters=False)\n" +"--\n" +"\n" +"Get garbage collector statistics from external Python process.\n" +"\n" +" all_interpreters\n" +" If True, return GC statistics from all interpreters.\n" +" If False, return only from main interpreter.\n" +"\n" +"Returns:\n" +" list of GCStatsInfo: A list of stats samples containing:\n" +" - gen: GC generation number.\n" +" - iid: Interpreter ID.\n" +" - ts_start: Raw timestamp at collection start.\n" +" - ts_stop: Raw timestamp at collection stop.\n" +" - collections: Total number of collections.\n" +" - collected: Total number of collected objects.\n" +" - uncollectable: Total number of uncollectable objects.\n" +" - candidates: Total objects considered and traversed.\n" +" - duration: Total collection time, in seconds.\n" +"\n" +"Raises:\n" +" RuntimeError: If the target process cannot be inspected or if its\n" +" debug offsets or GC stats layout are incompatible."); + +#define _REMOTE_DEBUGGING_GET_GC_STATS_METHODDEF \ + {"get_gc_stats", _PyCFunction_CAST(_remote_debugging_get_gc_stats), METH_FASTCALL|METH_KEYWORDS, _remote_debugging_get_gc_stats__doc__}, + +static PyObject * +_remote_debugging_get_gc_stats_impl(PyObject *module, int pid, + int all_interpreters); + +static PyObject * +_remote_debugging_get_gc_stats(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(pid), &_Py_ID(all_interpreters), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"pid", "all_interpreters", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "get_gc_stats", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + int pid; + int all_interpreters = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + pid = PyLong_AsInt(args[0]); + if (pid == -1 && PyErr_Occurred()) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + all_interpreters = PyObject_IsTrue(args[1]); + if (all_interpreters < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _remote_debugging_get_gc_stats_impl(module, pid, all_interpreters); + +exit: + return return_value; +} +/*[clinic end generated code: output=a3df14a6ab7f2998 input=a9049054013a1b77]*/ diff --git a/Modules/_remote_debugging/code_objects.c b/Modules/_remote_debugging/code_objects.c new file mode 100644 index 00000000000000..ab889a130ee4e7 --- /dev/null +++ b/Modules/_remote_debugging/code_objects.c @@ -0,0 +1,551 @@ +/****************************************************************************** + * Remote Debugging Module - Code Object Functions + * + * This file contains functions for parsing code objects and line tables + * from remote process memory. + ******************************************************************************/ + +#include "_remote_debugging.h" + +/* ============================================================================ + * TLBC CACHING FUNCTIONS (Py_GIL_DISABLED only) + * ============================================================================ */ + +#ifdef Py_GIL_DISABLED + +void +tlbc_cache_entry_destroy(void *ptr) +{ + TLBCCacheEntry *entry = (TLBCCacheEntry *)ptr; + if (entry->tlbc_array) { + PyMem_RawFree(entry->tlbc_array); + } + PyMem_RawFree(entry); +} + +TLBCCacheEntry * +get_tlbc_cache_entry(RemoteUnwinderObject *self, uintptr_t code_addr, uint32_t current_generation) +{ + void *key = (void *)code_addr; + TLBCCacheEntry *entry = _Py_hashtable_get(self->tlbc_cache, key); + + if (entry && entry->generation != current_generation) { + // Entry is stale, remove it by setting to NULL + _Py_hashtable_set(self->tlbc_cache, key, NULL); + entry = NULL; + } + + return entry; +} + +int +cache_tlbc_array(RemoteUnwinderObject *unwinder, uintptr_t code_addr, uintptr_t tlbc_array_addr, uint32_t generation) +{ + uintptr_t tlbc_array_ptr; + void *tlbc_array = NULL; + TLBCCacheEntry *entry = NULL; + + // Read the TLBC array pointer + if (read_ptr(unwinder, tlbc_array_addr, &tlbc_array_ptr) != 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read TLBC array pointer"); + return 0; // Read error + } + + // Validate TLBC array pointer + if (tlbc_array_ptr == 0) { + PyErr_SetString(PyExc_RuntimeError, "TLBC array pointer is NULL"); + return 0; // No TLBC array + } + + // Read the TLBC array size + Py_ssize_t tlbc_size; + if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, tlbc_array_ptr, sizeof(tlbc_size), &tlbc_size) != 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read TLBC array size"); + return 0; // Read error + } + + // Validate TLBC array size + if (tlbc_size <= 0) { + PyErr_SetString(PyExc_RuntimeError, "Invalid TLBC array size"); + return 0; // Invalid size + } + + if (tlbc_size > MAX_TLBC_SIZE) { + PyErr_SetString(PyExc_RuntimeError, "TLBC array size exceeds maximum limit"); + return 0; // Invalid size + } + assert(tlbc_size > 0 && tlbc_size <= MAX_TLBC_SIZE); + + // Allocate and read the entire TLBC array + size_t array_data_size = tlbc_size * sizeof(void*); + tlbc_array = PyMem_RawMalloc(sizeof(Py_ssize_t) + array_data_size); + if (!tlbc_array) { + PyErr_NoMemory(); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate TLBC array"); + return 0; // Memory error + } + + if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, tlbc_array_ptr, sizeof(Py_ssize_t) + array_data_size, tlbc_array) != 0) { + PyMem_RawFree(tlbc_array); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read TLBC array data"); + return 0; // Read error + } + + // Create cache entry + entry = PyMem_RawMalloc(sizeof(TLBCCacheEntry)); + if (!entry) { + PyErr_NoMemory(); + PyMem_RawFree(tlbc_array); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate TLBC cache entry"); + return 0; // Memory error + } + + entry->tlbc_array = tlbc_array; + entry->tlbc_array_size = tlbc_size; + entry->generation = generation; + + // Store in cache + void *key = (void *)code_addr; + if (_Py_hashtable_set(unwinder->tlbc_cache, key, entry) < 0) { + tlbc_cache_entry_destroy(entry); + PyErr_NoMemory(); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to store TLBC entry in cache"); + return 0; // Cache error + } + + return 1; // Success +} + +#endif + +/* ============================================================================ + * LINE TABLE PARSING FUNCTIONS + * ============================================================================ */ + +// Inline helper for bounds-checked byte reading (no function call overhead) +static inline int +read_byte(const uint8_t **ptr, const uint8_t *end, uint8_t *out) +{ + if (*ptr >= end) { + return -1; + } + *out = *(*ptr)++; + return 0; +} + +static int +scan_varint(const uint8_t **ptr, const uint8_t *end) +{ + uint8_t read; + if (read_byte(ptr, end, &read) < 0) { + return -1; + } + unsigned int val = read & 63; + unsigned int shift = 0; + while (read & 64) { + if (read_byte(ptr, end, &read) < 0) { + return -1; + } + shift += 6; + // Prevent infinite loop on malformed data (shift overflow) + if (shift > 28) { + return -1; + } + val |= (read & 63) << shift; + } + return (int)val; +} + +static int +scan_signed_varint(const uint8_t **ptr, const uint8_t *end) +{ + int uval = scan_varint(ptr, end); + if (uval < 0) { + return INT_MIN; // Error sentinel (valid signed varints won't be INT_MIN) + } + if (uval & 1) { + return -(int)((unsigned int)uval >> 1); + } + else { + return (int)((unsigned int)uval >> 1); + } +} + +bool +parse_linetable(const uintptr_t addrq, const char* linetable, Py_ssize_t linetable_size, + int firstlineno, LocationInfo* info) +{ + // Reject garbage: zero or negative size + if (linetable_size <= 0) { + return false; + } + + const uint8_t* ptr = (const uint8_t*)(linetable); + const uint8_t* end = ptr + linetable_size; + uintptr_t addr = 0; + int computed_line = firstlineno; // Running accumulator, separate from output + int val; // Temporary for varint results + uint8_t byte; // Temporary for byte reads + const size_t MAX_LINETABLE_ENTRIES = 65536; + size_t entry_count = 0; + + while (ptr < end && *ptr != '\0' && entry_count < MAX_LINETABLE_ENTRIES) { + entry_count++; + uint8_t first_byte = *(ptr++); + uint8_t code = (first_byte >> 3) & 15; + size_t length = (first_byte & 7) + 1; + uintptr_t end_addr = addr + length; + + switch (code) { + case PY_CODE_LOCATION_INFO_NONE: + info->lineno = info->end_lineno = -1; + info->column = info->end_column = -1; + break; + case PY_CODE_LOCATION_INFO_LONG: + val = scan_signed_varint(&ptr, end); + if (val == INT_MIN) { + return false; + } + computed_line += val; + info->lineno = computed_line; + val = scan_varint(&ptr, end); + if (val < 0) { + return false; + } + info->end_lineno = computed_line + val; + val = scan_varint(&ptr, end); + if (val < 0) { + return false; + } + info->column = val - 1; + val = scan_varint(&ptr, end); + if (val < 0) { + return false; + } + info->end_column = val - 1; + break; + case PY_CODE_LOCATION_INFO_NO_COLUMNS: + val = scan_signed_varint(&ptr, end); + if (val == INT_MIN) { + return false; + } + computed_line += val; + info->lineno = info->end_lineno = computed_line; + info->column = info->end_column = -1; + break; + case PY_CODE_LOCATION_INFO_ONE_LINE0: + case PY_CODE_LOCATION_INFO_ONE_LINE1: + case PY_CODE_LOCATION_INFO_ONE_LINE2: + computed_line += code - 10; + info->lineno = info->end_lineno = computed_line; + if (read_byte(&ptr, end, &byte) < 0) { + return false; + } + info->column = byte; + if (read_byte(&ptr, end, &byte) < 0) { + return false; + } + info->end_column = byte; + break; + default: { + if (read_byte(&ptr, end, &byte) < 0) { + return false; + } + if ((byte & 128) != 0) { + return false; + } + info->lineno = info->end_lineno = computed_line; + info->column = code << 3 | (byte >> 4); + info->end_column = info->column + (byte & 15); + break; + } + } + if (addr <= addrq && end_addr > addrq) { + return true; + } + addr = end_addr; + } + return false; +} + +/* ============================================================================ + * CODE OBJECT AND FRAME INFO FUNCTIONS + * ============================================================================ */ + +PyObject * +make_location_info(RemoteUnwinderObject *unwinder, int lineno, int end_lineno, + int col_offset, int end_col_offset) +{ + RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); + PyObject *info = PyStructSequence_New(state->LocationInfo_Type); + if (info == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create LocationInfo"); + return NULL; + } + + PyObject *py_lineno = PyLong_FromLong(lineno); + if (py_lineno == NULL) { + Py_DECREF(info); + return NULL; + } + PyStructSequence_SetItem(info, 0, py_lineno); // steals reference + + PyObject *py_end_lineno = PyLong_FromLong(end_lineno); + if (py_end_lineno == NULL) { + Py_DECREF(info); + return NULL; + } + PyStructSequence_SetItem(info, 1, py_end_lineno); // steals reference + + PyObject *py_col_offset = PyLong_FromLong(col_offset); + if (py_col_offset == NULL) { + Py_DECREF(info); + return NULL; + } + PyStructSequence_SetItem(info, 2, py_col_offset); // steals reference + + PyObject *py_end_col_offset = PyLong_FromLong(end_col_offset); + if (py_end_col_offset == NULL) { + Py_DECREF(info); + return NULL; + } + PyStructSequence_SetItem(info, 3, py_end_col_offset); // steals reference + + return info; +} + +PyObject * +make_frame_info(RemoteUnwinderObject *unwinder, PyObject *file, PyObject *location, + PyObject *func, PyObject *opcode) +{ + RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); + PyObject *info = PyStructSequence_New(state->FrameInfo_Type); + if (info == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create FrameInfo"); + return NULL; + } + Py_INCREF(file); + Py_INCREF(location); + Py_INCREF(func); + Py_INCREF(opcode); + PyStructSequence_SetItem(info, 0, file); + PyStructSequence_SetItem(info, 1, location); + PyStructSequence_SetItem(info, 2, func); + PyStructSequence_SetItem(info, 3, opcode); + return info; +} + +int +parse_code_object(RemoteUnwinderObject *unwinder, + PyObject **result, + const CodeObjectContext *ctx) +{ + void *key = (void *)ctx->code_addr; + CachedCodeMetadata *meta = NULL; + PyObject *func = NULL; + PyObject *file = NULL; + PyObject *linetable = NULL; + +#ifdef Py_GIL_DISABLED + // In free threading builds, code object addresses might have the low bit set + // as a flag, so we need to mask it off to get the real address + uintptr_t real_address = ctx->code_addr & (~1); +#else + uintptr_t real_address = ctx->code_addr; +#endif + + if (unwinder && unwinder->code_object_cache != NULL) { + meta = _Py_hashtable_get(unwinder->code_object_cache, key); + if (meta) { + STATS_INC(unwinder, code_object_cache_hits); + } else { + STATS_INC(unwinder, code_object_cache_misses); + } + } + + if (meta == NULL) { + char code_object[SIZEOF_CODE_OBJ]; + if (_Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, real_address, SIZEOF_CODE_OBJ, code_object) < 0) + { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read code object"); + goto error; + } + + func = read_py_str(unwinder, + GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.qualname), 1024); + if (!func) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read function name from code object"); + goto error; + } + + file = read_py_str(unwinder, + GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.filename), 1024); + if (!file) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read filename from code object"); + goto error; + } + + linetable = read_py_bytes(unwinder, + GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable), 4096); + if (!linetable) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read linetable from code object"); + goto error; + } + + meta = PyMem_RawMalloc(sizeof(CachedCodeMetadata)); + if (!meta) { + PyErr_NoMemory(); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate cached code metadata"); + goto error; + } + + meta->func_name = func; + meta->file_name = file; + meta->linetable = linetable; + meta->last_frame_info = NULL; + meta->last_addrq = -1; + meta->first_lineno = GET_MEMBER(int, code_object, unwinder->debug_offsets.code_object.firstlineno); + meta->addr_code_adaptive = real_address + (uintptr_t)unwinder->debug_offsets.code_object.co_code_adaptive; + + if (unwinder && unwinder->code_object_cache && _Py_hashtable_set(unwinder->code_object_cache, key, meta) < 0) { + // Ownership of func/file/linetable was transferred to meta, + // so NULL them before destroying meta to prevent double-free + // in the error label's Py_XDECREF calls. + func = NULL; + file = NULL; + linetable = NULL; + cached_code_metadata_destroy(meta); + PyErr_NoMemory(); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to cache code metadata"); + goto error; + } + + // Ownership transferred to meta + func = NULL; + file = NULL; + linetable = NULL; + } + + uintptr_t ip = ctx->instruction_pointer; + ptrdiff_t addrq; + +#ifdef Py_GIL_DISABLED + // Handle thread-local bytecode (TLBC) in free threading builds + if (ctx->tlbc_index == 0 || unwinder == NULL || unwinder->debug_offsets.code_object.co_tlbc == 0) { + // No TLBC or no unwinder - use main bytecode directly + addrq = (uint16_t *)ip - (uint16_t *)meta->addr_code_adaptive; + goto done_tlbc; + } + + // Try to get TLBC data from cache (we'll get generation from the caller) + TLBCCacheEntry *tlbc_entry = get_tlbc_cache_entry(unwinder, real_address, unwinder->tlbc_generation); + + if (!tlbc_entry) { + // Cache miss - try to read and cache TLBC array + if (!cache_tlbc_array(unwinder, real_address, real_address + unwinder->debug_offsets.code_object.co_tlbc, unwinder->tlbc_generation)) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to cache TLBC array"); + goto error; + } + tlbc_entry = get_tlbc_cache_entry(unwinder, real_address, unwinder->tlbc_generation); + } + + // Validate tlbc_index and check TLBC cache + if (tlbc_entry) { + // Validate index bounds (also catches negative values since tlbc_index is signed) + if (ctx->tlbc_index < 0 || ctx->tlbc_index >= tlbc_entry->tlbc_array_size) { + PyErr_Format(PyExc_RuntimeError, + "Invalid tlbc_index %d (array size %zd, corrupted remote memory)", + ctx->tlbc_index, tlbc_entry->tlbc_array_size); + set_exception_cause(unwinder, PyExc_RuntimeError, + "Invalid tlbc_index (corrupted remote memory)"); + goto error; + } + assert(tlbc_entry->tlbc_array_size > 0); + // Use cached TLBC data + uintptr_t *entries = (uintptr_t *)((char *)tlbc_entry->tlbc_array + sizeof(Py_ssize_t)); + uintptr_t tlbc_bytecode_addr = entries[ctx->tlbc_index]; + + if (tlbc_bytecode_addr != 0) { + // Calculate offset from TLBC bytecode + addrq = (uint16_t *)ip - (uint16_t *)tlbc_bytecode_addr; + goto done_tlbc; + } + } + + // Fall back to main bytecode (no tlbc_entry or tlbc_bytecode_addr was 0) + addrq = (uint16_t *)ip - (uint16_t *)meta->addr_code_adaptive; + +done_tlbc: +#else + // Non-free-threaded build, always use the main bytecode + addrq = (uint16_t *)ip - (uint16_t *)meta->addr_code_adaptive; +#endif + ; // Empty statement to avoid C23 extension warning + + if (!unwinder->opcodes && meta->last_frame_info != NULL && meta->last_addrq == addrq) { + *result = Py_NewRef(meta->last_frame_info); + return 0; + } + + LocationInfo info = {0}; + bool ok = parse_linetable(addrq, PyBytes_AS_STRING(meta->linetable), + PyBytes_GET_SIZE(meta->linetable), + meta->first_lineno, &info); + if (!ok) { + info.lineno = -1; + info.end_lineno = -1; + info.column = -1; + info.end_column = -1; + } + + // Create the LocationInfo structseq: (lineno, end_lineno, col_offset, end_col_offset) + PyObject *location = make_location_info(unwinder, + info.lineno, + info.end_lineno, + info.column, + info.end_column); + if (!location) { + goto error; + } + + // Read the instruction opcode from target process if opcodes flag is set + PyObject *opcode_obj = NULL; + if (unwinder->opcodes) { + uint16_t instruction_word = 0; + if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, ip, + sizeof(uint16_t), &instruction_word) == 0) { + opcode_obj = PyLong_FromLong(instruction_word & 0xFF); + if (!opcode_obj) { + Py_DECREF(location); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create opcode object"); + goto error; + } + } else { + // Opcode read failed - clear the exception since opcode is optional + PyErr_Clear(); + } + } + + PyObject *tuple = make_frame_info(unwinder, meta->file_name, location, + meta->func_name, opcode_obj ? opcode_obj : Py_None); + Py_DECREF(location); + Py_XDECREF(opcode_obj); + if (!tuple) { + goto error; + } + + if (!unwinder->opcodes) { + Py_XSETREF(meta->last_frame_info, Py_NewRef(tuple)); + meta->last_addrq = addrq; + } + + *result = tuple; + return 0; + +error: + Py_XDECREF(func); + Py_XDECREF(file); + Py_XDECREF(linetable); + return -1; +} diff --git a/Modules/_remote_debugging/debug_offsets_validation.h b/Modules/_remote_debugging/debug_offsets_validation.h new file mode 100644 index 00000000000000..f070f03ac459dc --- /dev/null +++ b/Modules/_remote_debugging/debug_offsets_validation.h @@ -0,0 +1,499 @@ +#ifndef Py_REMOTE_DEBUG_OFFSETS_VALIDATION_H +#define Py_REMOTE_DEBUG_OFFSETS_VALIDATION_H + +/* + * The remote debugging tables are read from the target process and must be + * treated as untrusted input. This header centralizes the one-time validation + * that runs immediately after those tables are read, before the unwinder uses + * any reported sizes or offsets to copy remote structs into fixed local + * buffers or to interpret those local copies. + * + * The key rule is simple: every offset that is later dereferenced against a + * local buffer or local object view must appear in one of the field lists + * below. Validation then checks two bounds for each field: + * + * 1. The field must fit within the section size reported by the target. + * 2. The same field must also fit within the local buffer or local layout the + * debugger will actually use. + * + * Sections that are copied into fixed local buffers also have their reported + * size checked against the corresponding local buffer size up front. + * + * This is intentionally front-loaded. Once validation succeeds, the hot path + * can keep using the raw offsets without adding per-sample bounds checks. + * + * Maintenance rule: if either exported table grows, the static_asserts below + * should yell at you. When that happens, update the matching field lists in + * this file in the same change. And if you add a new field that the unwinder + * is going to poke at later, put it in the right list here too, so nobody has + * to rediscover this the annoying way. + */ +#define FIELD_SIZE(type, member) sizeof(((type *)0)->member) + +enum { + PY_REMOTE_DEBUG_OFFSETS_TOTAL_SIZE = 880, + PY_REMOTE_ASYNC_DEBUG_OFFSETS_TOTAL_SIZE = 104, +}; + +/* + * These asserts are the coordination tripwire for table growth. If either + * exported table changes size, update the validation lists below in the same + * change. + */ +static_assert( + sizeof(_Py_DebugOffsets) == PY_REMOTE_DEBUG_OFFSETS_TOTAL_SIZE, + "Update _remote_debugging validation for _Py_DebugOffsets"); +static_assert( + sizeof(struct _Py_AsyncioModuleDebugOffsets) == + PY_REMOTE_ASYNC_DEBUG_OFFSETS_TOTAL_SIZE, + "Update _remote_debugging validation for _Py_AsyncioModuleDebugOffsets"); + +/* + * This logic lives in a private header because it is shared by module.c and + * asyncio.c. Keep the helpers static inline so they stay local to those users + * without adding another compilation unit or exported symbols. + */ +static inline int +validate_section_size(const char *section_name, uint64_t size) +{ + if (size == 0) { + PyErr_Format( + PyExc_RuntimeError, + "Invalid debug offsets: %s.size must be greater than zero", + section_name); + return -1; + } + return 0; +} + +static inline int +validate_read_size(const char *section_name, uint64_t size, size_t buffer_size) +{ + if (validate_section_size(section_name, size) < 0) { + return -1; + } + if (size > buffer_size) { + PyErr_Format( + PyExc_RuntimeError, + "Invalid debug offsets: %s.size=%llu exceeds local buffer size %zu", + section_name, + (unsigned long long)size, + buffer_size); + return -1; + } + return 0; +} + +static inline int +validate_span( + const char *field_name, + uint64_t offset, + size_t width, + uint64_t limit, + const char *limit_name) +{ + uint64_t span = (uint64_t)width; + if (span > limit || offset > limit - span) { + PyErr_Format( + PyExc_RuntimeError, + "Invalid debug offsets: %s=%llu with width %zu exceeds %s %llu", + field_name, + (unsigned long long)offset, + width, + limit_name, + (unsigned long long)limit); + return -1; + } + return 0; +} + +static inline int +validate_alignment( + const char *field_name, + uint64_t offset, + size_t alignment) +{ + if (alignment > 1 && offset % alignment != 0) { + PyErr_Format( + PyExc_RuntimeError, + "Invalid debug offsets: %s=%llu is not aligned to %zu bytes", + field_name, + (unsigned long long)offset, + alignment); + return -1; + } + return 0; +} + +static inline int +validate_field( + const char *field_name, + uint64_t reported_size, + uint64_t offset, + size_t width, + size_t alignment, + size_t buffer_size) +{ + if (validate_alignment(field_name, offset, alignment) < 0) { + return -1; + } + if (validate_span(field_name, offset, width, reported_size, "reported size") < 0) { + return -1; + } + return validate_span(field_name, offset, width, buffer_size, "local buffer size"); +} + +static inline int +validate_nested_field( + const char *field_name, + uint64_t reported_size, + uint64_t base_offset, + uint64_t nested_offset, + size_t width, + size_t alignment, + size_t buffer_size) +{ + if (base_offset > UINT64_MAX - nested_offset) { + PyErr_Format( + PyExc_RuntimeError, + "Invalid debug offsets: %s overflows the offset calculation", + field_name); + return -1; + } + return validate_field( + field_name, + reported_size, + base_offset + nested_offset, + width, + alignment, + buffer_size); +} + +static inline int +validate_fixed_field( + const char *field_name, + uint64_t offset, + size_t width, + size_t alignment, + size_t buffer_size) +{ + if (validate_alignment(field_name, offset, alignment) < 0) { + return -1; + } + return validate_span(field_name, offset, width, buffer_size, "local buffer size"); +} + +#define PY_REMOTE_DEBUG_VALIDATE_SECTION(section) \ + do { \ + if (validate_section_size(#section, debug_offsets->section.size) < 0) { \ + return -1; \ + } \ + } while (0) + +#define PY_REMOTE_DEBUG_VALIDATE_READ_SECTION(section, buffer_size) \ + do { \ + if (validate_read_size(#section, debug_offsets->section.size, buffer_size) < 0) { \ + return -1; \ + } \ + } while (0) + +#define PY_REMOTE_DEBUG_VALIDATE_FIELD(section, field, field_size, field_alignment, buffer_size) \ + do { \ + if (validate_field( \ + #section "." #field, \ + debug_offsets->section.size, \ + debug_offsets->section.field, \ + field_size, \ + field_alignment, \ + buffer_size) < 0) { \ + return -1; \ + } \ + } while (0) + +#define PY_REMOTE_DEBUG_VALIDATE_NESTED_FIELD(section, base, nested_section, field, field_size, field_alignment, buffer_size) \ + do { \ + if (validate_nested_field( \ + #section "." #base "." #field, \ + debug_offsets->section.size, \ + debug_offsets->section.base, \ + debug_offsets->nested_section.field, \ + field_size, \ + field_alignment, \ + buffer_size) < 0) { \ + return -1; \ + } \ + } while (0) + +#define PY_REMOTE_DEBUG_VALIDATE_FIXED_FIELD(section, field, field_size, field_alignment, buffer_size) \ + do { \ + if (validate_fixed_field( \ + #section "." #field, \ + debug_offsets->section.field, \ + field_size, \ + field_alignment, \ + buffer_size) < 0) { \ + return -1; \ + } \ + } while (0) + +/* + * Each list below must include every offset that is later dereferenced against + * a local buffer or local object view. The validator checks that each field + * stays within both the remote table's reported section size and the local + * buffer size we use when reading that section. If a new dereferenced field is + * added to the offset tables, add it to the matching list here. + * + * Sections not listed here are present in the offset tables but not used by + * the unwinder, so no validation is needed for them. + */ +#define PY_REMOTE_DEBUG_RUNTIME_STATE_FIELDS(APPLY, buffer_size) \ + APPLY(runtime_state, interpreters_head, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size) + +#define PY_REMOTE_DEBUG_THREAD_STATE_FIELDS(APPLY, buffer_size) \ + APPLY(thread_state, native_thread_id, sizeof(unsigned long), _Alignof(long), buffer_size); \ + APPLY(thread_state, interp, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(thread_state, datastack_chunk, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(thread_state, status, FIELD_SIZE(PyThreadState, _status), _Alignof(unsigned int), buffer_size); \ + APPLY(thread_state, holds_gil, sizeof(int), _Alignof(int), buffer_size); \ + APPLY(thread_state, gil_requested, sizeof(int), _Alignof(int), buffer_size); \ + APPLY(thread_state, current_exception, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(thread_state, thread_id, sizeof(unsigned long), _Alignof(long), buffer_size); \ + APPLY(thread_state, next, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(thread_state, current_frame, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(thread_state, base_frame, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(thread_state, last_profiled_frame, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size) + +#define PY_REMOTE_DEBUG_INTERPRETER_STATE_FIELDS(APPLY, buffer_size) \ + APPLY(interpreter_state, id, sizeof(int64_t), _Alignof(int64_t), buffer_size); \ + APPLY(interpreter_state, next, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(interpreter_state, threads_head, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(interpreter_state, threads_main, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(interpreter_state, gil_runtime_state_locked, sizeof(int), _Alignof(int), buffer_size); \ + APPLY(interpreter_state, gil_runtime_state_holder, sizeof(PyThreadState *), _Alignof(PyThreadState *), buffer_size); \ + APPLY(interpreter_state, code_object_generation, sizeof(uint64_t), _Alignof(uint64_t), buffer_size); \ + APPLY(interpreter_state, tlbc_generation, sizeof(uint32_t), _Alignof(uint32_t), buffer_size) + +#define PY_REMOTE_DEBUG_INTERPRETER_FRAME_FIELDS(APPLY, buffer_size) \ + APPLY(interpreter_frame, previous, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(interpreter_frame, executable, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(interpreter_frame, instr_ptr, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(interpreter_frame, owner, sizeof(char), _Alignof(char), buffer_size); \ + APPLY(interpreter_frame, stackpointer, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(interpreter_frame, tlbc_index, sizeof(int32_t), _Alignof(int32_t), buffer_size) + +#define PY_REMOTE_DEBUG_CODE_OBJECT_FIELDS(APPLY, buffer_size) \ + APPLY(code_object, qualname, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(code_object, filename, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(code_object, linetable, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(code_object, firstlineno, sizeof(int), _Alignof(int), buffer_size); \ + APPLY(code_object, co_code_adaptive, sizeof(char), _Alignof(char), buffer_size); \ + APPLY(code_object, co_tlbc, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size) + +#define PY_REMOTE_DEBUG_SET_OBJECT_FIELDS(APPLY, buffer_size) \ + APPLY(set_object, used, sizeof(Py_ssize_t), _Alignof(Py_ssize_t), buffer_size); \ + APPLY(set_object, mask, sizeof(Py_ssize_t), _Alignof(Py_ssize_t), buffer_size); \ + APPLY(set_object, table, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size) + +#define PY_REMOTE_DEBUG_LONG_OBJECT_FIELDS(APPLY, buffer_size) \ + APPLY(long_object, lv_tag, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(long_object, ob_digit, sizeof(digit), _Alignof(digit), buffer_size) + +#define PY_REMOTE_DEBUG_BYTES_OBJECT_FIELDS(APPLY, buffer_size) \ + APPLY(bytes_object, ob_size, sizeof(Py_ssize_t), _Alignof(Py_ssize_t), buffer_size); \ + APPLY(bytes_object, ob_sval, sizeof(char), _Alignof(char), buffer_size) + +#define PY_REMOTE_DEBUG_UNICODE_OBJECT_FIELDS(APPLY, buffer_size) \ + APPLY(unicode_object, length, sizeof(Py_ssize_t), _Alignof(Py_ssize_t), buffer_size); \ + APPLY(unicode_object, state, sizeof(struct _PyUnicodeObject_state), _Alignof(struct _PyUnicodeObject_state), buffer_size); \ + APPLY(unicode_object, asciiobject_size, sizeof(char), _Alignof(char), buffer_size); \ + APPLY(unicode_object, compactunicodeobject_size, sizeof(char), _Alignof(char), buffer_size) + +#define PY_REMOTE_DEBUG_GEN_OBJECT_FIELDS(APPLY, buffer_size) \ + APPLY(gen_object, gi_frame_state, sizeof(int8_t), _Alignof(int8_t), buffer_size); \ + APPLY(gen_object, gi_iframe, FIELD_SIZE(PyGenObject, gi_iframe), _Alignof(_PyInterpreterFrame), buffer_size) + +#define PY_REMOTE_DEBUG_TASK_OBJECT_FIELDS(APPLY, buffer_size) \ + APPLY(asyncio_task_object, task_name, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(asyncio_task_object, task_awaited_by, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(asyncio_task_object, task_is_task, sizeof(char), _Alignof(char), buffer_size); \ + APPLY(asyncio_task_object, task_awaited_by_is_set, sizeof(char), _Alignof(char), buffer_size); \ + APPLY(asyncio_task_object, task_coro, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(asyncio_task_object, task_node, SIZEOF_LLIST_NODE, _Alignof(struct llist_node), buffer_size) + +#define PY_REMOTE_DEBUG_ASYNC_INTERPRETER_STATE_FIELDS(APPLY, buffer_size) \ + APPLY(asyncio_interpreter_state, asyncio_tasks_head, SIZEOF_LLIST_NODE, _Alignof(struct llist_node), buffer_size) + +#define PY_REMOTE_DEBUG_ASYNC_THREAD_STATE_FIELDS(APPLY, buffer_size) \ + APPLY(asyncio_thread_state, asyncio_running_loop, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(asyncio_thread_state, asyncio_running_task, sizeof(uintptr_t), _Alignof(uintptr_t), buffer_size); \ + APPLY(asyncio_thread_state, asyncio_tasks_head, SIZEOF_LLIST_NODE, _Alignof(struct llist_node), buffer_size) + +/* Called once after reading _Py_DebugOffsets, before any hot-path use. */ +static inline int +_PyRemoteDebug_ValidateDebugOffsetsLayout(struct _Py_DebugOffsets *debug_offsets) +{ + /* Validate every field the unwinder dereferences against a local buffer + * or local object view. Fields used only for remote address arithmetic + * (e.g. runtime_state.interpreters_head) are also checked as a sanity + * bound on the offset value. */ + PY_REMOTE_DEBUG_VALIDATE_SECTION(runtime_state); + PY_REMOTE_DEBUG_RUNTIME_STATE_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + sizeof(_PyRuntimeState)); + + PY_REMOTE_DEBUG_VALIDATE_SECTION(interpreter_state); + PY_REMOTE_DEBUG_INTERPRETER_STATE_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + INTERP_STATE_BUFFER_SIZE); + + PY_REMOTE_DEBUG_VALIDATE_READ_SECTION(thread_state, SIZEOF_THREAD_STATE); + PY_REMOTE_DEBUG_THREAD_STATE_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + SIZEOF_THREAD_STATE); + PY_REMOTE_DEBUG_VALIDATE_FIXED_FIELD( + err_stackitem, + exc_value, + sizeof(uintptr_t), + _Alignof(uintptr_t), + sizeof(_PyErr_StackItem)); + PY_REMOTE_DEBUG_VALIDATE_NESTED_FIELD( + thread_state, + exc_state, + err_stackitem, + exc_value, + sizeof(uintptr_t), + _Alignof(uintptr_t), + SIZEOF_THREAD_STATE); + + PY_REMOTE_DEBUG_VALIDATE_READ_SECTION(gc, SIZEOF_GC_RUNTIME_STATE); + PY_REMOTE_DEBUG_VALIDATE_FIELD( + gc, + frame, + sizeof(uintptr_t), + _Alignof(uintptr_t), + SIZEOF_GC_RUNTIME_STATE); + PY_REMOTE_DEBUG_VALIDATE_FIELD( + gc, + generation_stats, + sizeof(uintptr_t), + _Alignof(uintptr_t), + SIZEOF_GC_RUNTIME_STATE); + PY_REMOTE_DEBUG_VALIDATE_NESTED_FIELD( + interpreter_state, + gc, + gc, + frame, + sizeof(uintptr_t), + _Alignof(uintptr_t), + INTERP_STATE_BUFFER_SIZE); + PY_REMOTE_DEBUG_VALIDATE_NESTED_FIELD( + interpreter_state, + gc, + gc, + generation_stats, + sizeof(uintptr_t), + _Alignof(uintptr_t), + INTERP_STATE_BUFFER_SIZE); + + PY_REMOTE_DEBUG_VALIDATE_SECTION(interpreter_frame); + PY_REMOTE_DEBUG_INTERPRETER_FRAME_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + SIZEOF_INTERP_FRAME); + + PY_REMOTE_DEBUG_VALIDATE_SECTION(code_object); + PY_REMOTE_DEBUG_CODE_OBJECT_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + SIZEOF_CODE_OBJ); + + PY_REMOTE_DEBUG_VALIDATE_SECTION(pyobject); + PY_REMOTE_DEBUG_VALIDATE_FIELD( + pyobject, + ob_type, + sizeof(uintptr_t), + _Alignof(uintptr_t), + SIZEOF_PYOBJECT); + + PY_REMOTE_DEBUG_VALIDATE_SECTION(type_object); + PY_REMOTE_DEBUG_VALIDATE_FIELD( + type_object, + tp_flags, + sizeof(unsigned long), + _Alignof(unsigned long), + SIZEOF_TYPE_OBJ); + + PY_REMOTE_DEBUG_VALIDATE_SECTION(set_object); + PY_REMOTE_DEBUG_SET_OBJECT_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + SIZEOF_SET_OBJ); + + PY_REMOTE_DEBUG_VALIDATE_READ_SECTION(long_object, SIZEOF_LONG_OBJ); + PY_REMOTE_DEBUG_LONG_OBJECT_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + SIZEOF_LONG_OBJ); + + PY_REMOTE_DEBUG_VALIDATE_SECTION(bytes_object); + PY_REMOTE_DEBUG_BYTES_OBJECT_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + SIZEOF_BYTES_OBJ); + + PY_REMOTE_DEBUG_VALIDATE_SECTION(unicode_object); + PY_REMOTE_DEBUG_UNICODE_OBJECT_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + SIZEOF_UNICODE_OBJ); + + PY_REMOTE_DEBUG_VALIDATE_SECTION(gen_object); + PY_REMOTE_DEBUG_GEN_OBJECT_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + SIZEOF_GEN_OBJ); + + PY_REMOTE_DEBUG_VALIDATE_FIXED_FIELD( + llist_node, + next, + sizeof(uintptr_t), + _Alignof(uintptr_t), + SIZEOF_LLIST_NODE); + + return 0; +} + +/* Called once when AsyncioDebug is loaded, before any task inspection uses it. */ +static inline int +_PyRemoteDebug_ValidateAsyncDebugOffsets( + struct _Py_AsyncioModuleDebugOffsets *debug_offsets) +{ + PY_REMOTE_DEBUG_VALIDATE_READ_SECTION(asyncio_task_object, SIZEOF_TASK_OBJ); + PY_REMOTE_DEBUG_TASK_OBJECT_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + SIZEOF_TASK_OBJ); + PY_REMOTE_DEBUG_VALIDATE_SECTION(asyncio_interpreter_state); + PY_REMOTE_DEBUG_ASYNC_INTERPRETER_STATE_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + sizeof(PyInterpreterState)); + PY_REMOTE_DEBUG_VALIDATE_SECTION(asyncio_thread_state); + PY_REMOTE_DEBUG_ASYNC_THREAD_STATE_FIELDS( + PY_REMOTE_DEBUG_VALIDATE_FIELD, + sizeof(_PyThreadStateImpl)); + return 0; +} + +#undef PY_REMOTE_DEBUG_VALIDATE_SECTION +#undef PY_REMOTE_DEBUG_VALIDATE_READ_SECTION +#undef PY_REMOTE_DEBUG_VALIDATE_FIELD +#undef PY_REMOTE_DEBUG_VALIDATE_NESTED_FIELD +#undef PY_REMOTE_DEBUG_VALIDATE_FIXED_FIELD +#undef PY_REMOTE_DEBUG_RUNTIME_STATE_FIELDS +#undef PY_REMOTE_DEBUG_THREAD_STATE_FIELDS +#undef PY_REMOTE_DEBUG_INTERPRETER_STATE_FIELDS +#undef PY_REMOTE_DEBUG_INTERPRETER_FRAME_FIELDS +#undef PY_REMOTE_DEBUG_CODE_OBJECT_FIELDS +#undef PY_REMOTE_DEBUG_SET_OBJECT_FIELDS +#undef PY_REMOTE_DEBUG_LONG_OBJECT_FIELDS +#undef PY_REMOTE_DEBUG_BYTES_OBJECT_FIELDS +#undef PY_REMOTE_DEBUG_UNICODE_OBJECT_FIELDS +#undef PY_REMOTE_DEBUG_GEN_OBJECT_FIELDS +#undef PY_REMOTE_DEBUG_TASK_OBJECT_FIELDS +#undef PY_REMOTE_DEBUG_ASYNC_INTERPRETER_STATE_FIELDS +#undef PY_REMOTE_DEBUG_ASYNC_THREAD_STATE_FIELDS +#undef FIELD_SIZE + +#endif diff --git a/Modules/_remote_debugging/frame_cache.c b/Modules/_remote_debugging/frame_cache.c new file mode 100644 index 00000000000000..19fc406bca9ac9 --- /dev/null +++ b/Modules/_remote_debugging/frame_cache.c @@ -0,0 +1,292 @@ +/****************************************************************************** + * Remote Debugging Module - Frame Cache + * + * This file contains functions for caching frame information to optimize + * repeated stack unwinding for profiling. + ******************************************************************************/ + +#include "_remote_debugging.h" + +/* ============================================================================ + * FRAME CACHE - stores (address, frame_info) pairs per thread + * Uses preallocated fixed-size arrays for efficiency and bounded memory. + * ============================================================================ */ + +int +frame_cache_init(RemoteUnwinderObject *unwinder) +{ + unwinder->frame_cache = PyMem_Calloc(FRAME_CACHE_MAX_THREADS, sizeof(FrameCacheEntry)); + if (!unwinder->frame_cache) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + +void +frame_cache_cleanup(RemoteUnwinderObject *unwinder) +{ + if (!unwinder->frame_cache) { + return; + } + for (int i = 0; i < FRAME_CACHE_MAX_THREADS; i++) { + Py_CLEAR(unwinder->frame_cache[i].thread_id_obj); + Py_CLEAR(unwinder->frame_cache[i].frame_list); + } + PyMem_Free(unwinder->frame_cache); + unwinder->frame_cache = NULL; +} + +// Find cache entry by thread_id +FrameCacheEntry * +frame_cache_find(RemoteUnwinderObject *unwinder, uint64_t thread_id) +{ + if (!unwinder->frame_cache || thread_id == 0) { + return NULL; + } + for (int i = 0; i < FRAME_CACHE_MAX_THREADS; i++) { + assert(i >= 0 && i < FRAME_CACHE_MAX_THREADS); + if (unwinder->frame_cache[i].thread_id == thread_id) { + assert(unwinder->frame_cache[i].num_addrs <= FRAME_CACHE_MAX_FRAMES); + return &unwinder->frame_cache[i]; + } + } + return NULL; +} + +FrameCacheEntry * +frame_cache_find_by_tstate(RemoteUnwinderObject *unwinder, uintptr_t tstate_addr) +{ + if (!unwinder->frame_cache || tstate_addr == 0) { + return NULL; + } + for (int i = 0; i < FRAME_CACHE_MAX_THREADS; i++) { + if (unwinder->frame_cache[i].thread_state_addr == tstate_addr) { + assert(unwinder->frame_cache[i].num_addrs <= FRAME_CACHE_MAX_FRAMES); + return &unwinder->frame_cache[i]; + } + } + return NULL; +} + +// Allocate a cache slot for a thread +// Returns NULL if cache is full (graceful degradation) +static FrameCacheEntry * +frame_cache_alloc_slot(RemoteUnwinderObject *unwinder, uint64_t thread_id) +{ + if (!unwinder->frame_cache || thread_id == 0) { + return NULL; + } + // First check if thread already has an entry + for (int i = 0; i < FRAME_CACHE_MAX_THREADS; i++) { + if (unwinder->frame_cache[i].thread_id == thread_id) { + return &unwinder->frame_cache[i]; + } + } + // Find empty slot + for (int i = 0; i < FRAME_CACHE_MAX_THREADS; i++) { + if (unwinder->frame_cache[i].thread_id == 0) { + return &unwinder->frame_cache[i]; + } + } + // Cache full - graceful degradation + return NULL; +} + +// Remove cache entries for threads not seen in the result +// result structure: list of InterpreterInfo, where InterpreterInfo[1] is threads list, +// and ThreadInfo[0] is the thread_id +void +frame_cache_invalidate_stale(RemoteUnwinderObject *unwinder, PyObject *result) +{ + if (!unwinder->frame_cache || !result || !PyList_Check(result)) { + return; + } + + // Build array of seen thread IDs from result + uint64_t seen_threads[FRAME_CACHE_MAX_THREADS]; + int num_seen = 0; + + Py_ssize_t num_interps = PyList_GET_SIZE(result); + for (Py_ssize_t i = 0; i < num_interps && num_seen < FRAME_CACHE_MAX_THREADS; i++) { + PyObject *interp_info = PyList_GET_ITEM(result, i); + PyObject *threads = PyStructSequence_GetItem(interp_info, 1); + if (!threads || !PyList_Check(threads)) { + continue; + } + Py_ssize_t num_threads = PyList_GET_SIZE(threads); + for (Py_ssize_t j = 0; j < num_threads && num_seen < FRAME_CACHE_MAX_THREADS; j++) { + PyObject *thread_info = PyList_GET_ITEM(threads, j); + PyObject *tid_obj = PyStructSequence_GetItem(thread_info, 0); + if (tid_obj) { + uint64_t tid = PyLong_AsUnsignedLongLong(tid_obj); + if (!PyErr_Occurred()) { + seen_threads[num_seen++] = tid; + } else { + PyErr_Clear(); + } + } + } + } + + // Invalidate entries not in seen list + for (int i = 0; i < FRAME_CACHE_MAX_THREADS; i++) { + if (unwinder->frame_cache[i].thread_id == 0) { + continue; + } + int found = 0; + for (int j = 0; j < num_seen; j++) { + if (unwinder->frame_cache[i].thread_id == seen_threads[j]) { + found = 1; + break; + } + } + if (!found) { + // Clear this entry + Py_CLEAR(unwinder->frame_cache[i].thread_id_obj); + Py_CLEAR(unwinder->frame_cache[i].frame_list); + unwinder->frame_cache[i].thread_id = 0; + unwinder->frame_cache[i].thread_state_addr = 0; + unwinder->frame_cache[i].num_addrs = 0; + STATS_INC(unwinder, stale_cache_invalidations); + } + } +} + +// Find last_profiled_frame in cache and extend frame_info with cached continuation +// If frame_addrs is provided (not NULL), also extends it with cached addresses +int +frame_cache_lookup_and_extend( + RemoteUnwinderObject *unwinder, + uint64_t thread_id, + uintptr_t last_profiled_frame, + PyObject *frame_info, + uintptr_t *frame_addrs, + Py_ssize_t *num_addrs, + Py_ssize_t max_addrs) +{ + if (!unwinder->frame_cache || last_profiled_frame == 0) { + return 0; + } + + FrameCacheEntry *entry = frame_cache_find(unwinder, thread_id); + if (!entry || !entry->frame_list) { + return 0; + } + + assert(entry->num_addrs >= 0 && entry->num_addrs <= FRAME_CACHE_MAX_FRAMES); + + // Find the index where last_profiled_frame matches + Py_ssize_t start_idx = -1; + for (Py_ssize_t i = 0; i < entry->num_addrs; i++) { + if (entry->addrs[i] == last_profiled_frame) { + start_idx = i; + break; + } + } + + if (start_idx < 0) { + return 0; // Not found + } + assert(start_idx < entry->num_addrs); + + Py_ssize_t num_frames = PyList_GET_SIZE(entry->frame_list); + + // Extend frame_info with frames ABOVE start_idx (not including it). + // The frame at start_idx (last_profiled_frame) was the executing frame + // in the previous sample and its line number may have changed. + // Only frames above it (its callers) are frozen at their call sites. + Py_ssize_t cache_start = start_idx + 1; + if (cache_start >= num_frames) { + return 0; // Nothing above last_profiled_frame to extend with + } + + PyObject *slice = PyList_GetSlice(entry->frame_list, cache_start, num_frames); + if (!slice) { + return -1; + } + + Py_ssize_t cur_size = PyList_GET_SIZE(frame_info); + int result = PyList_SetSlice(frame_info, cur_size, cur_size, slice); + Py_DECREF(slice); + + if (result < 0) { + return -1; + } + + // Also extend frame_addrs with cached addresses (above last_profiled_frame) + if (frame_addrs) { + for (Py_ssize_t i = cache_start; i < entry->num_addrs && *num_addrs < max_addrs; i++) { + frame_addrs[(*num_addrs)++] = entry->addrs[i]; + } + } + + return 1; +} + +// Store frame list with addresses in cache +// Only stores complete stacks that reach base_frame_addr (validation done internally) +// Returns: 1 = stored successfully, 0 = not stored (graceful degradation), -1 = error +int +frame_cache_store( + RemoteUnwinderObject *unwinder, + uint64_t thread_id, + PyObject *frame_list, + const uintptr_t *addrs, + Py_ssize_t num_addrs, + uintptr_t thread_state_addr, + uintptr_t base_frame_addr, + uintptr_t last_frame_visited) +{ + if (!unwinder->frame_cache || thread_id == 0) { + return 0; + } + + // Validate we have a complete stack before caching. + // Only cache if last_frame_visited matches base_frame_addr (the sentinel + // at the bottom of the stack). Note: we use last_frame_visited rather than + // addrs[num_addrs-1] because the base frame is visited but not added to the + // addrs array (it returns frame==NULL from is_frame_valid due to + // owner==FRAME_OWNED_BY_INTERPRETER). + if (base_frame_addr != 0 && last_frame_visited != base_frame_addr) { + // Incomplete stack - don't cache (graceful degradation) + return 0; + } + + // Clamp to max frames + if (num_addrs > FRAME_CACHE_MAX_FRAMES) { + num_addrs = FRAME_CACHE_MAX_FRAMES; + } + assert(num_addrs >= 0 && num_addrs <= FRAME_CACHE_MAX_FRAMES); + + FrameCacheEntry *entry = frame_cache_alloc_slot(unwinder, thread_id); + if (!entry) { + // Cache full - graceful degradation + return 0; + } + + // Clear old frame_list if replacing + Py_CLEAR(entry->frame_list); + + // Store full frame list (don't truncate to num_addrs - frames beyond the + // address array limit are still valid and needed for full cache hits) + Py_ssize_t num_frames = PyList_GET_SIZE(frame_list); + entry->frame_list = PyList_GetSlice(frame_list, 0, num_frames); + if (!entry->frame_list) { + return -1; + } + entry->thread_id = thread_id; + entry->thread_state_addr = thread_state_addr; + if (entry->thread_id_obj == NULL) { + entry->thread_id_obj = PyLong_FromUnsignedLongLong(thread_id); + if (entry->thread_id_obj == NULL) { + return -1; + } + } + memcpy(entry->addrs, addrs, num_addrs * sizeof(uintptr_t)); + entry->num_addrs = num_addrs; + assert(entry->num_addrs == num_addrs); + assert(entry->thread_id == thread_id); + + return 1; +} diff --git a/Modules/_remote_debugging/frames.c b/Modules/_remote_debugging/frames.c new file mode 100644 index 00000000000000..e7d2a276439026 --- /dev/null +++ b/Modules/_remote_debugging/frames.c @@ -0,0 +1,649 @@ +/****************************************************************************** + * Remote Debugging Module - Frame Functions + * + * This file contains functions for parsing interpreter frames and + * managing stack chunks from remote process memory. + ******************************************************************************/ + +#include "_remote_debugging.h" + +/* ============================================================================ + * STACK CHUNK MANAGEMENT FUNCTIONS + * ============================================================================ */ + +void +cleanup_stack_chunks(StackChunkList *chunks) +{ + for (size_t i = 0; i < chunks->count; ++i) { + PyMem_RawFree(chunks->chunks[i].local_copy); + } + PyMem_RawFree(chunks->chunks); +} + +static int +process_single_stack_chunk( + RemoteUnwinderObject *unwinder, + uintptr_t chunk_addr, + StackChunkInfo *chunk_info +) { + // Start with default size assumption + size_t current_size = _PY_DATA_STACK_CHUNK_SIZE; + + char *this_chunk = PyMem_RawMalloc(current_size); + if (!this_chunk) { + PyErr_NoMemory(); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate stack chunk buffer"); + return -1; + } + + if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, chunk_addr, current_size, this_chunk) < 0) { + PyMem_RawFree(this_chunk); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read stack chunk"); + return -1; + } + + // Check actual size and reread if necessary + size_t actual_size = GET_MEMBER(size_t, this_chunk, offsetof(_PyStackChunk, size)); + if (actual_size != current_size) { + // Validate size: reject garbage (too small or unreasonably large) + // Size must be at least enough for the header and reasonably bounded + if (actual_size <= offsetof(_PyStackChunk, data) || actual_size > MAX_STACK_CHUNK_SIZE) { + PyMem_RawFree(this_chunk); + PyErr_Format(PyExc_RuntimeError, + "Invalid stack chunk size %zu (corrupted remote memory)", actual_size); + set_exception_cause(unwinder, PyExc_RuntimeError, + "Invalid stack chunk size (corrupted remote memory)"); + return -1; + } + + char *tmp = PyMem_RawRealloc(this_chunk, actual_size); + if (!tmp) { + PyMem_RawFree(this_chunk); + PyErr_NoMemory(); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to reallocate stack chunk buffer"); + return -1; + } + this_chunk = tmp; + + if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, chunk_addr, actual_size, this_chunk) < 0) { + PyMem_RawFree(this_chunk); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to reread stack chunk with correct size"); + return -1; + } + current_size = actual_size; + } + + chunk_info->remote_addr = chunk_addr; + chunk_info->size = current_size; + chunk_info->local_copy = this_chunk; + return 0; +} + +int +copy_stack_chunks(RemoteUnwinderObject *unwinder, + uintptr_t tstate_addr, + StackChunkList *out_chunks) +{ + uintptr_t chunk_addr; + StackChunkInfo *chunks = NULL; + size_t count = 0; + size_t max_chunks = 16; + + if (read_ptr(unwinder, tstate_addr + (uintptr_t)unwinder->debug_offsets.thread_state.datastack_chunk, &chunk_addr)) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read initial stack chunk address"); + return -1; + } + + chunks = PyMem_RawMalloc(max_chunks * sizeof(StackChunkInfo)); + if (!chunks) { + PyErr_NoMemory(); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate stack chunks array"); + return -1; + } + + const size_t MAX_STACK_CHUNKS = 4096; + while (chunk_addr != 0 && count < MAX_STACK_CHUNKS) { + // Grow array if needed + if (count >= max_chunks) { + max_chunks *= 2; + StackChunkInfo *new_chunks = PyMem_RawRealloc(chunks, max_chunks * sizeof(StackChunkInfo)); + if (!new_chunks) { + PyErr_NoMemory(); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to grow stack chunks array"); + goto error; + } + chunks = new_chunks; + } + + // Process this chunk + if (process_single_stack_chunk(unwinder, chunk_addr, &chunks[count]) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to process stack chunk"); + goto error; + } + + // Get next chunk address and increment count + chunk_addr = GET_MEMBER(uintptr_t, chunks[count].local_copy, offsetof(_PyStackChunk, previous)); + count++; + } + + out_chunks->chunks = chunks; + out_chunks->count = count; + return 0; + +error: + for (size_t i = 0; i < count; ++i) { + PyMem_RawFree(chunks[i].local_copy); + } + PyMem_RawFree(chunks); + return -1; +} + +void * +find_frame_in_chunks(StackChunkList *chunks, uintptr_t remote_ptr) +{ + for (size_t i = 0; i < chunks->count; ++i) { + // Validate size: reject garbage that would cause underflow + if (chunks->chunks[i].size <= offsetof(_PyStackChunk, data)) { + // Skip this chunk - corrupted size from remote memory + continue; + } + uintptr_t base = chunks->chunks[i].remote_addr + offsetof(_PyStackChunk, data); + size_t payload = chunks->chunks[i].size - offsetof(_PyStackChunk, data); + + if (payload >= SIZEOF_INTERP_FRAME && + remote_ptr >= base && + remote_ptr <= base + payload - SIZEOF_INTERP_FRAME) { + return (char *)chunks->chunks[i].local_copy + (remote_ptr - chunks->chunks[i].remote_addr); + } + } + return NULL; +} + +/* ============================================================================ + * FRAME PARSING FUNCTIONS + * ============================================================================ */ + +int +is_frame_valid( + RemoteUnwinderObject *unwinder, + uintptr_t frame_addr, + uintptr_t code_object_addr +) { + if ((void*)code_object_addr == NULL) { + return 0; + } + + void* frame = (void*)frame_addr; + + char owner = GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner); + if (owner == FRAME_OWNED_BY_INTERPRETER) { + return 0; // C frame or sentinel base frame + } + + if (owner != FRAME_OWNED_BY_GENERATOR && owner != FRAME_OWNED_BY_THREAD) { + PyErr_Format(PyExc_RuntimeError, "Unhandled frame owner %d.\n", owner); + set_exception_cause(unwinder, PyExc_RuntimeError, "Unhandled frame owner type in async frame"); + return -1; + } + return 1; +} + +static int +parse_frame_buffer( + RemoteUnwinderObject *unwinder, + PyObject** result, + const char *frame, + uintptr_t* address_of_code_object, + uintptr_t* previous_frame +) { + *address_of_code_object = 0; + + *previous_frame = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.previous); + uintptr_t code_object = GET_MEMBER_NO_TAG(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable); + int frame_valid = is_frame_valid(unwinder, (uintptr_t)frame, code_object); + if (frame_valid != 1) { + return frame_valid; + } + + uintptr_t instruction_pointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.instr_ptr); + + // Get tlbc_index for free threading builds + int32_t tlbc_index = 0; +#ifdef Py_GIL_DISABLED + if (unwinder->debug_offsets.interpreter_frame.tlbc_index != 0) { + tlbc_index = GET_MEMBER(int32_t, frame, unwinder->debug_offsets.interpreter_frame.tlbc_index); + } +#endif + + *address_of_code_object = code_object; + + CodeObjectContext code_ctx = { + .code_addr = code_object, + .instruction_pointer = instruction_pointer, + .tlbc_index = tlbc_index, + }; + return parse_code_object(unwinder, result, &code_ctx); +} + +int +parse_frame_object( + RemoteUnwinderObject *unwinder, + PyObject** result, + uintptr_t address, + uintptr_t* address_of_code_object, + uintptr_t* previous_frame +) { + char frame[SIZEOF_INTERP_FRAME]; + Py_ssize_t bytes_read = _Py_RemoteDebug_ReadRemoteMemory( + &unwinder->handle, + address, + SIZEOF_INTERP_FRAME, + frame + ); + if (bytes_read < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read interpreter frame"); + return -1; + } + STATS_INC(unwinder, memory_reads); + STATS_ADD(unwinder, memory_bytes_read, SIZEOF_INTERP_FRAME); + + return parse_frame_buffer(unwinder, result, frame, address_of_code_object, previous_frame); +} + +int +parse_frame_from_chunks( + RemoteUnwinderObject *unwinder, + PyObject **result, + uintptr_t address, + uintptr_t *previous_frame, + uintptr_t *stackpointer, + StackChunkList *chunks +) { + void *frame_ptr = find_frame_in_chunks(chunks, address); + if (!frame_ptr) { + PyErr_Format(PyExc_RuntimeError, "Frame at address 0x%lx not found in stack chunks", address); + set_exception_cause(unwinder, PyExc_RuntimeError, "Frame not found in stack chunks"); + return -1; + } + + char *frame = (char *)frame_ptr; + *previous_frame = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.previous); + *stackpointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.stackpointer); + uintptr_t code_object = GET_MEMBER_NO_TAG(uintptr_t, frame_ptr, unwinder->debug_offsets.interpreter_frame.executable); + int frame_valid = is_frame_valid(unwinder, (uintptr_t)frame, code_object); + if (frame_valid != 1) { + return frame_valid; + } + + uintptr_t instruction_pointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.instr_ptr); + + // Get tlbc_index for free threading builds + int32_t tlbc_index = 0; +#ifdef Py_GIL_DISABLED + if (unwinder->debug_offsets.interpreter_frame.tlbc_index != 0) { + tlbc_index = GET_MEMBER(int32_t, frame, unwinder->debug_offsets.interpreter_frame.tlbc_index); + } +#endif + + CodeObjectContext code_ctx = { + .code_addr = code_object, + .instruction_pointer = instruction_pointer, + .tlbc_index = tlbc_index, + }; + return parse_code_object(unwinder, result, &code_ctx); +} + +/* ============================================================================ + * FRAME CHAIN PROCESSING + * ============================================================================ */ + +int +process_frame_chain( + RemoteUnwinderObject *unwinder, + FrameWalkContext *ctx) +{ + uintptr_t frame_addr = ctx->frame_addr; + uintptr_t prev_frame_addr = 0; + uintptr_t last_frame_addr = 0; + const size_t MAX_FRAMES = 1024 + 512; + size_t frame_count = 0; + assert(MAX_FRAMES > 0 && MAX_FRAMES < 10000); + + ctx->stopped_at_cached_frame = 0; + ctx->last_frame_visited = 0; + + while ((void*)frame_addr != NULL) { + PyObject *frame = NULL; + uintptr_t next_frame_addr = 0; + uintptr_t stackpointer = 0; + last_frame_addr = frame_addr; + + if (++frame_count > MAX_FRAMES) { + PyErr_SetString(PyExc_RuntimeError, "Too many stack frames (possible infinite loop)"); + set_exception_cause(unwinder, PyExc_RuntimeError, "Frame chain iteration limit exceeded"); + return -1; + } + assert(frame_count <= MAX_FRAMES); + + if (ctx->chunks && ctx->chunks->count > 0) { + if (parse_frame_from_chunks(unwinder, &frame, frame_addr, &next_frame_addr, &stackpointer, ctx->chunks) == 0) { + goto parsed_frame; + } + PyErr_Clear(); + } + { + uintptr_t address_of_code_object = 0; + int parse_result; + if (ctx->prefetch.frame && ctx->prefetch.frame_addr == frame_addr) { + parse_result = parse_frame_buffer( + unwinder, &frame, ctx->prefetch.frame, + &address_of_code_object, &next_frame_addr); + } + else { + parse_result = parse_frame_object( + unwinder, &frame, frame_addr, + &address_of_code_object, &next_frame_addr); + } + if (parse_result < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse frame object in chain"); + return -1; + } + } + +parsed_frame: + // Skip first frame if requested (used for cache miss continuation) + if (ctx->skip_first_frame && frame_count == 1) { + Py_XDECREF(frame); + frame_addr = next_frame_addr; + continue; + } + + if (frame == NULL && PyList_GET_SIZE(ctx->frame_info) == 0) { + const char *e = "Failed to parse initial frame in chain"; + PyErr_SetString(PyExc_RuntimeError, e); + return -1; + } + PyObject *extra_frame = NULL; + if (unwinder->gc && frame_addr == ctx->gc_frame) { + _Py_DECLARE_STR(gc, "<GC>"); + extra_frame = &_Py_STR(gc); + } + else if (unwinder->native && + frame == NULL && + next_frame_addr && + !(unwinder->gc && next_frame_addr == ctx->gc_frame)) + { + _Py_DECLARE_STR(native, "<native>"); + extra_frame = &_Py_STR(native); + } + if (extra_frame) { + PyObject *extra_frame_info = make_frame_info( + unwinder, _Py_LATIN1_CHR('~'), Py_None, extra_frame, Py_None); + if (extra_frame_info == NULL) { + Py_XDECREF(frame); + return -1; + } + if (PyList_Append(ctx->frame_info, extra_frame_info) < 0) { + Py_DECREF(extra_frame_info); + Py_XDECREF(frame); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append extra frame"); + return -1; + } + if (ctx->frame_addrs && ctx->num_addrs < ctx->max_addrs) { + assert(ctx->num_addrs >= 0); + ctx->frame_addrs[ctx->num_addrs++] = 0; + } + Py_DECREF(extra_frame_info); + } + if (frame) { + if (prev_frame_addr && frame_addr != prev_frame_addr) { + const char *f = "Broken frame chain: expected frame at 0x%lx, got 0x%lx"; + PyErr_Format(PyExc_RuntimeError, f, prev_frame_addr, frame_addr); + Py_DECREF(frame); + set_exception_cause(unwinder, PyExc_RuntimeError, "Frame chain consistency check failed"); + return -1; + } + + if (PyList_Append(ctx->frame_info, frame) < 0) { + Py_DECREF(frame); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame"); + return -1; + } + if (ctx->frame_addrs && ctx->num_addrs < ctx->max_addrs) { + assert(ctx->num_addrs >= 0); + ctx->frame_addrs[ctx->num_addrs++] = frame_addr; + } + Py_DECREF(frame); + } + + if (ctx->last_profiled_frame != 0 && frame_addr == ctx->last_profiled_frame) { + ctx->stopped_at_cached_frame = 1; + break; + } + + prev_frame_addr = next_frame_addr; + frame_addr = next_frame_addr; + } + + if (!ctx->stopped_at_cached_frame && ctx->base_frame_addr != 0 && last_frame_addr != ctx->base_frame_addr) { + PyErr_Format(PyExc_RuntimeError, + "Incomplete sample: did not reach base frame (expected 0x%lx, got 0x%lx)", + ctx->base_frame_addr, last_frame_addr); + return -1; + } + + ctx->last_frame_visited = last_frame_addr; + + return 0; +} + +// Clear last_profiled_frame for all threads in the target process. +// This must be called at the start of profiling to avoid stale values +// from previous profilers causing us to stop frame walking early. +int +clear_last_profiled_frames(RemoteUnwinderObject *unwinder) +{ + uintptr_t current_interp = unwinder->interpreter_addr; + uintptr_t zero = 0; + const size_t MAX_INTERPRETERS = 256; + size_t interp_count = 0; + + while (current_interp != 0 && interp_count < MAX_INTERPRETERS) { + interp_count++; + // Get first thread in this interpreter + uintptr_t tstate_addr; + if (_Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + current_interp + unwinder->debug_offsets.interpreter_state.threads_head, + sizeof(void*), + &tstate_addr) < 0) { + // Non-fatal: just skip clearing + PyErr_Clear(); + return 0; + } + + // Iterate all threads in this interpreter + const size_t MAX_THREADS_PER_INTERP = 8192; + size_t thread_count = 0; + while (tstate_addr != 0 && thread_count < MAX_THREADS_PER_INTERP) { + thread_count++; + // Clear last_profiled_frame + uintptr_t lpf_addr = tstate_addr + unwinder->debug_offsets.thread_state.last_profiled_frame; + if (_Py_RemoteDebug_WriteRemoteMemory(&unwinder->handle, lpf_addr, + sizeof(uintptr_t), &zero) < 0) { + // Non-fatal: just continue + PyErr_Clear(); + } + + // Move to next thread + if (_Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + tstate_addr + unwinder->debug_offsets.thread_state.next, + sizeof(void*), + &tstate_addr) < 0) { + PyErr_Clear(); + break; + } + } + + // Move to next interpreter + if (_Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + current_interp + unwinder->debug_offsets.interpreter_state.next, + sizeof(void*), + &current_interp) < 0) { + PyErr_Clear(); + break; + } + } + + return 0; +} + +// Fast path: check if we have a full cache hit (parent stack unchanged) +// A "full hit" means current frame == last profiled frame, so we can reuse +// cached parent frames. We always read the current frame from memory to get +// updated line numbers (the line within a frame can change between samples). +// Returns: 1 if full hit (frame_info populated with current frame + cached parents), +// 0 if miss, -1 on error +static int +try_full_cache_hit( + RemoteUnwinderObject *unwinder, + const FrameWalkContext *ctx, + uint64_t thread_id) +{ + if (!unwinder->frame_cache || ctx->last_profiled_frame == 0) { + return 0; + } + if (ctx->frame_addr != ctx->last_profiled_frame) { + return 0; + } + + FrameCacheEntry *entry = frame_cache_find(unwinder, thread_id); + if (!entry || !entry->frame_list) { + return 0; + } + + if (entry->num_addrs == 0 || entry->addrs[0] != ctx->frame_addr) { + return 0; + } + + PyObject *current_frame = NULL; + uintptr_t code_object_addr = 0; + uintptr_t previous_frame = 0; + int parse_result; + if (ctx->prefetch.frame && ctx->prefetch.frame_addr == ctx->frame_addr) { + parse_result = parse_frame_buffer(unwinder, &current_frame, + ctx->prefetch.frame, + &code_object_addr, &previous_frame); + } + else { + parse_result = parse_frame_object(unwinder, &current_frame, ctx->frame_addr, + &code_object_addr, &previous_frame); + } + if (parse_result < 0) { + return -1; + } + + if (current_frame != NULL) { + if (PyList_Append(ctx->frame_info, current_frame) < 0) { + Py_DECREF(current_frame); + return -1; + } + Py_DECREF(current_frame); + STATS_ADD(unwinder, frames_read_from_memory, 1); + } + + Py_ssize_t cached_size = PyList_GET_SIZE(entry->frame_list); + for (Py_ssize_t i = 1; i < cached_size; i++) { + PyObject *cached_frame = PyList_GET_ITEM(entry->frame_list, i); + if (PyList_Append(ctx->frame_info, cached_frame) < 0) { + return -1; + } + } + STATS_ADD(unwinder, frames_read_from_cache, cached_size > 1 ? cached_size - 1 : 0); + + STATS_INC(unwinder, frame_cache_hits); + return 1; +} + +// High-level helper: collect frames with cache optimization +// Returns complete frame_info list, handling all cache logic internally +int +collect_frames_with_cache( + RemoteUnwinderObject *unwinder, + FrameWalkContext *ctx, + uint64_t thread_id) +{ + int full_hit = try_full_cache_hit(unwinder, ctx, thread_id); + if (full_hit != 0) { + return full_hit < 0 ? -1 : 0; + } + + assert(ctx->chunks != NULL); + + if (ctx->chunks->count == 0) { + if (copy_stack_chunks(unwinder, ctx->thread_state_addr, ctx->chunks) < 0) { + PyErr_Clear(); + } + } + + Py_ssize_t frames_before = PyList_GET_SIZE(ctx->frame_info); + + if (process_frame_chain(unwinder, ctx) < 0) { + return -1; + } + + STATS_ADD(unwinder, frames_read_from_memory, PyList_GET_SIZE(ctx->frame_info) - frames_before); + + if (ctx->stopped_at_cached_frame) { + Py_ssize_t frames_before_cache = PyList_GET_SIZE(ctx->frame_info); + int cache_result = frame_cache_lookup_and_extend(unwinder, thread_id, ctx->last_profiled_frame, + ctx->frame_info, ctx->frame_addrs, &ctx->num_addrs, + ctx->max_addrs); + if (cache_result < 0) { + return -1; + } + if (cache_result == 0) { + STATS_INC(unwinder, frame_cache_misses); + + // Continue walking from last_profiled_frame, skipping it (already processed) + Py_ssize_t frames_before_walk = PyList_GET_SIZE(ctx->frame_info); + FrameWalkContext continue_ctx = { + .frame_addr = ctx->last_profiled_frame, + .base_frame_addr = ctx->base_frame_addr, + .gc_frame = ctx->gc_frame, + .chunks = ctx->chunks, + .skip_first_frame = 1, + .frame_info = ctx->frame_info, + .frame_addrs = ctx->frame_addrs, + .num_addrs = ctx->num_addrs, + .max_addrs = ctx->max_addrs, + }; + if (process_frame_chain(unwinder, &continue_ctx) < 0) { + return -1; + } + ctx->num_addrs = continue_ctx.num_addrs; + ctx->last_frame_visited = continue_ctx.last_frame_visited; + STATS_ADD(unwinder, frames_read_from_memory, PyList_GET_SIZE(ctx->frame_info) - frames_before_walk); + } else { + // Partial cache hit - cached stack was validated as complete when stored, + // so set last_frame_visited to base_frame_addr for validation in frame_cache_store + ctx->last_frame_visited = ctx->base_frame_addr; + STATS_INC(unwinder, frame_cache_partial_hits); + STATS_ADD(unwinder, frames_read_from_cache, PyList_GET_SIZE(ctx->frame_info) - frames_before_cache); + } + } else { + if (ctx->last_profiled_frame == 0) { + STATS_INC(unwinder, frame_cache_misses); + } + } + + if (frame_cache_store(unwinder, thread_id, ctx->frame_info, ctx->frame_addrs, ctx->num_addrs, + ctx->thread_state_addr, ctx->base_frame_addr, + ctx->last_frame_visited) < 0) { + return -1; + } + + return 0; +} diff --git a/Modules/_remote_debugging/gc_stats.c b/Modules/_remote_debugging/gc_stats.c new file mode 100644 index 00000000000000..d5d05edb8ecf5e --- /dev/null +++ b/Modules/_remote_debugging/gc_stats.c @@ -0,0 +1,154 @@ +/****************************************************************************** + * Remote Debugging Module - GC Stats Functions + * + * This file contains functions for reading GC stats from interpreter state. + ******************************************************************************/ + +#include "gc_stats.h" + +typedef struct { + PyObject *result; + PyTypeObject *gc_stats_info_type; + bool all_interpreters; +} GetGCStatsContext; + +static int +read_gc_stats(struct gc_stats *stats, int64_t iid, PyObject *result, + PyTypeObject *gc_stats_info_type) +{ +#define SET_FIELD(converter, expr) do { \ + PyObject *value = converter(expr); \ + if (value == NULL) { \ + goto error; \ + } \ + PyStructSequence_SetItem(item, field++, value); \ +} while (0) + + PyObject *item = NULL; + + for (unsigned long gen = 0; gen < NUM_GENERATIONS; gen++) { + struct gc_generation_stats *items; + int size; + if (gen == 0) { + items = (struct gc_generation_stats *)stats->young.items; + size = GC_YOUNG_STATS_SIZE; + } + else { + items = (struct gc_generation_stats *)stats->old[gen-1].items; + size = GC_OLD_STATS_SIZE; + } + for (int i = 0; i < size; i++, items++) { + item = PyStructSequence_New(gc_stats_info_type); + if (item == NULL) { + goto error; + } + Py_ssize_t field = 0; + + SET_FIELD(PyLong_FromUnsignedLong, gen); + SET_FIELD(PyLong_FromInt64, iid); + + SET_FIELD(PyLong_FromInt64, items->ts_start); + SET_FIELD(PyLong_FromInt64, items->ts_stop); + SET_FIELD(PyLong_FromSsize_t, items->collections); + SET_FIELD(PyLong_FromSsize_t, items->collected); + SET_FIELD(PyLong_FromSsize_t, items->uncollectable); + SET_FIELD(PyLong_FromSsize_t, items->candidates); + SET_FIELD(PyLong_FromSsize_t, items->heap_size); + + SET_FIELD(PyFloat_FromDouble, items->duration); + + int rc = PyList_Append(result, item); + Py_CLEAR(item); + if (rc < 0) { + goto error; + } + } + } + +#undef SET_FIELD + + return 0; + +error: + Py_XDECREF(item); + + return -1; +} + +static int +get_gc_stats_from_interpreter_state(RuntimeOffsets *offsets, + uintptr_t interpreter_state_addr, + int64_t iid, + void *context) +{ + GetGCStatsContext *ctx = (GetGCStatsContext *)context; + if (!ctx->all_interpreters && iid > 0) { + return 0; + } + + uintptr_t gc_stats_addr = 0; + uintptr_t gc_stats_pointer_address = interpreter_state_addr + + offsets->debug_offsets.interpreter_state.gc + + offsets->debug_offsets.gc.generation_stats; + if (_Py_RemoteDebug_ReadRemoteMemory(&offsets->handle, + gc_stats_pointer_address, + sizeof(gc_stats_addr), + &gc_stats_addr) < 0) { + set_exception_cause(offsets, PyExc_RuntimeError, "Failed to read GC state address"); + return -1; + } + if (gc_stats_addr == 0) { + PyErr_SetString(PyExc_RuntimeError, "GC state address is NULL"); + return -1; + } + + struct gc_stats stats; + if (_Py_RemoteDebug_ReadRemoteMemory(&offsets->handle, + gc_stats_addr, + sizeof(stats), + &stats) < 0) { + set_exception_cause(offsets, PyExc_RuntimeError, "Failed to read GC state"); + return -1; + } + + if (read_gc_stats(&stats, iid, ctx->result, + ctx->gc_stats_info_type) < 0) { + set_exception_cause(offsets, PyExc_RuntimeError, "Failed to populate GC stats result"); + return -1; + } + + return 0; +} + +PyObject * +get_gc_stats(RuntimeOffsets *offsets, bool all_interpreters, + PyTypeObject *gc_stats_info_type) +{ + uint64_t gc_stats_size = offsets->debug_offsets.gc.generation_stats_size; + if (gc_stats_size != sizeof(struct gc_stats)) { + PyErr_Format(PyExc_RuntimeError, + "Remote gc_stats size (%llu) does not match " + "local size (%zu)", + (unsigned long long)gc_stats_size, + sizeof(struct gc_stats)); + set_exception_cause(offsets, PyExc_RuntimeError, "Remote gc_stats size mismatch"); + return NULL; + } + + PyObject *result = PyList_New(0); + if (result == NULL) { + return NULL; + } + GetGCStatsContext ctx = { + .result = result, + .gc_stats_info_type = gc_stats_info_type, + .all_interpreters = all_interpreters, + }; + if (iterate_interpreters(offsets, get_gc_stats_from_interpreter_state, + &ctx) < 0) { + Py_CLEAR(result); + return NULL; + } + + return result; +} diff --git a/Modules/_remote_debugging/gc_stats.h b/Modules/_remote_debugging/gc_stats.h new file mode 100644 index 00000000000000..959a49c59dcee1 --- /dev/null +++ b/Modules/_remote_debugging/gc_stats.h @@ -0,0 +1,24 @@ +/****************************************************************************** + * Remote Debugging Module - GC Stats Functions + * + * This file contains declarations for reading GC stats from interpreter state. + ******************************************************************************/ + +#ifndef Py_REMOTE_DEBUGGING_GC_STATS_H +#define Py_REMOTE_DEBUGGING_GC_STATS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "_remote_debugging.h" + +PyObject * +get_gc_stats(RuntimeOffsets *offsets, bool all_interpreters, + PyTypeObject *gc_stats_info_type); + +#ifdef __cplusplus +} +#endif + +#endif /* Py_REMOTE_DEBUGGING_GC_STATS_H */ diff --git a/Modules/_remote_debugging/interpreters.c b/Modules/_remote_debugging/interpreters.c new file mode 100644 index 00000000000000..55ebcb1d7816a6 --- /dev/null +++ b/Modules/_remote_debugging/interpreters.c @@ -0,0 +1,66 @@ +/****************************************************************************** + * Remote Debugging Module - Interpreters Functions + * + * This file contains function for iterating interpreters. + ******************************************************************************/ + +#include "_remote_debugging.h" + +int +iterate_interpreters( + RuntimeOffsets *offsets, + interpreter_processor_func processor, + void *context +) { + uintptr_t interpreters_head_addr = + offsets->runtime_start_address + + (uintptr_t)offsets->debug_offsets.runtime_state.interpreters_head; + uintptr_t interpreter_id_offset = + (uintptr_t)offsets->debug_offsets.interpreter_state.id; + uintptr_t interpreter_next_offset = + (uintptr_t)offsets->debug_offsets.interpreter_state.next; + + uintptr_t interpreter_state_addr; + if (_Py_RemoteDebug_ReadRemoteMemory(&offsets->handle, + interpreters_head_addr, + sizeof(void*), + &interpreter_state_addr) < 0) { + set_exception_cause(offsets, PyExc_RuntimeError, "Failed to read interpreter state address"); + return -1; + } + + if (interpreter_state_addr == 0) { + PyErr_SetString(PyExc_RuntimeError, "No interpreter state found"); + return -1; + } + + int64_t iid = 0; + static_assert( + sizeof((((PyInterpreterState*)NULL)->id)) == sizeof(iid), + "Sizeof of PyInterpreterState.id mismatch with local iid value"); + while (interpreter_state_addr != 0) { + if (_Py_RemoteDebug_ReadRemoteMemory( + &offsets->handle, + interpreter_state_addr + interpreter_id_offset, + sizeof(iid), + &iid) < 0) { + set_exception_cause(offsets, PyExc_RuntimeError, "Failed to read interpreter id"); + return -1; + } + + if (processor(offsets, interpreter_state_addr, iid, context) < 0) { + return -1; + } + + if (_Py_RemoteDebug_ReadRemoteMemory( + &offsets->handle, + interpreter_state_addr + interpreter_next_offset, + sizeof(void*), + &interpreter_state_addr) < 0) { + set_exception_cause(offsets, PyExc_RuntimeError, "Failed to read next interpreter state"); + return -1; + } + } + + return 0; +} diff --git a/Modules/_remote_debugging/module.c b/Modules/_remote_debugging/module.c new file mode 100644 index 00000000000000..984213d1881752 --- /dev/null +++ b/Modules/_remote_debugging/module.c @@ -0,0 +1,2368 @@ +/****************************************************************************** + * Remote Debugging Module - Main Module Implementation + * + * This file contains the main module initialization, the RemoteUnwinder + * class implementation, and utility functions. + ******************************************************************************/ + +#include "_remote_debugging.h" +#include "binary_io.h" +#include "debug_offsets_validation.h" +#include "gc_stats.h" + +/* Forward declarations for clinic-generated code */ +typedef struct { + PyObject_HEAD + BinaryWriter *writer; + uint32_t cached_total_samples; /* Preserved after finalize */ +} BinaryWriterObject; + +typedef struct { + PyObject_HEAD + BinaryReader *reader; +} BinaryReaderObject; + +#include "clinic/module.c.h" + +/* ============================================================================ + * STRUCTSEQ TYPE DEFINITIONS + * ============================================================================ */ + +// TaskInfo structseq type +static PyStructSequence_Field TaskInfo_fields[] = { + {"task_id", "Task ID (memory address)"}, + {"task_name", "Task name"}, + {"coroutine_stack", "Coroutine call stack"}, + {"awaited_by", "Tasks awaiting this task"}, + {NULL} +}; + +PyStructSequence_Desc TaskInfo_desc = { + "_remote_debugging.TaskInfo", + "Information about an asyncio task", + TaskInfo_fields, + 4 +}; + +// LocationInfo structseq type +static PyStructSequence_Field LocationInfo_fields[] = { + {"lineno", "Line number"}, + {"end_lineno", "End line number"}, + {"col_offset", "Column offset"}, + {"end_col_offset", "End column offset"}, + {NULL} +}; + +PyStructSequence_Desc LocationInfo_desc = { + "_remote_debugging.LocationInfo", + "Source location information: (lineno, end_lineno, col_offset, end_col_offset)", + LocationInfo_fields, + 4 +}; + +// FrameInfo structseq type +static PyStructSequence_Field FrameInfo_fields[] = { + {"filename", "Source code filename"}, + {"location", "LocationInfo structseq or None for synthetic frames"}, + {"funcname", "Function name"}, + {"opcode", "Opcode being executed (None if not gathered)"}, + {NULL} +}; + +PyStructSequence_Desc FrameInfo_desc = { + "_remote_debugging.FrameInfo", + "Information about a frame", + FrameInfo_fields, + 4 +}; + +// CoroInfo structseq type +static PyStructSequence_Field CoroInfo_fields[] = { + {"call_stack", "Coroutine call stack"}, + {"task_name", "Task name"}, + {NULL} +}; + +PyStructSequence_Desc CoroInfo_desc = { + "_remote_debugging.CoroInfo", + "Information about a coroutine", + CoroInfo_fields, + 2 +}; + +// ThreadInfo structseq type +static PyStructSequence_Field ThreadInfo_fields[] = { + {"thread_id", "Thread ID"}, + {"status", "Thread status (flags: HAS_GIL, ON_CPU, UNKNOWN or legacy enum)"}, + {"frame_info", "Frame information"}, + {NULL} +}; + +PyStructSequence_Desc ThreadInfo_desc = { + "_remote_debugging.ThreadInfo", + "Information about a thread", + ThreadInfo_fields, + 3 +}; + +// InterpreterInfo structseq type +static PyStructSequence_Field InterpreterInfo_fields[] = { + {"interpreter_id", "Interpreter ID"}, + {"threads", "List of threads in this interpreter"}, + {NULL} +}; + +PyStructSequence_Desc InterpreterInfo_desc = { + "_remote_debugging.InterpreterInfo", + "Information about an interpreter", + InterpreterInfo_fields, + 2 +}; + +// AwaitedInfo structseq type +static PyStructSequence_Field AwaitedInfo_fields[] = { + {"thread_id", "Thread ID"}, + {"awaited_by", "List of tasks awaited by this thread"}, + {NULL} +}; + +PyStructSequence_Desc AwaitedInfo_desc = { + "_remote_debugging.AwaitedInfo", + "Information about what a thread is awaiting", + AwaitedInfo_fields, + 2 +}; + +// GCStatsInfo structseq type +static PyStructSequence_Field GCStatsInfo_fields[] = { + {"gen", "GC generation number"}, + {"iid", "Interpreter ID"}, + {"ts_start", "Raw timestamp at collection start"}, + {"ts_stop", "Raw timestamp at collection stop"}, + {"collections", "Total number of collections"}, + {"collected", "Total number of collected objects"}, + {"uncollectable", "Total number of uncollectable objects"}, + {"candidates", "Total objects considered and traversed"}, + {"heap_size", "Number of live objects"}, + {"duration", "Total collection time, in seconds"}, + {NULL} +}; + +PyStructSequence_Desc GCStatsInfo_desc = { + "_remote_debugging.GCStatsInfo", + "Information about a garbage collector stats sample", + GCStatsInfo_fields, + 10 +}; + +/* ============================================================================ + * UTILITY FUNCTIONS + * ============================================================================ */ + +void +cached_code_metadata_destroy(void *ptr) +{ + CachedCodeMetadata *meta = (CachedCodeMetadata *)ptr; + Py_DECREF(meta->func_name); + Py_DECREF(meta->file_name); + Py_DECREF(meta->linetable); + Py_XDECREF(meta->last_frame_info); + PyMem_RawFree(meta); +} + +RemoteDebuggingState * +RemoteDebugging_GetState(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (RemoteDebuggingState *)state; +} + +RemoteDebuggingState * +RemoteDebugging_GetStateFromType(PyTypeObject *type) +{ + PyObject *module = PyType_GetModule(type); + assert(module != NULL); + return RemoteDebugging_GetState(module); +} + +RemoteDebuggingState * +RemoteDebugging_GetStateFromObject(PyObject *obj) +{ + RemoteUnwinderObject *unwinder = (RemoteUnwinderObject *)obj; + if (unwinder->cached_state == NULL) { + unwinder->cached_state = RemoteDebugging_GetStateFromType(Py_TYPE(obj)); + } + return unwinder->cached_state; +} + +int +RemoteDebugging_InitState(RemoteDebuggingState *st) +{ + return 0; +} + +int +is_prerelease_version(uint64_t version) +{ + return (version & 0xF0) != 0xF0; +} + +int +validate_debug_offsets(struct _Py_DebugOffsets *debug_offsets) +{ + if (memcmp(debug_offsets->cookie, _Py_Debug_Cookie, sizeof(debug_offsets->cookie)) != 0) { + // The remote is probably running a Python version predating debug offsets. + PyErr_SetString( + PyExc_RuntimeError, + "Can't determine the Python version of the remote process"); + return -1; + } + + // Assume debug offsets could change from one pre-release version to another, + // or one minor version to another, but are stable across patch versions. + if (is_prerelease_version(Py_Version) && Py_Version != debug_offsets->version) { + PyErr_SetString( + PyExc_RuntimeError, + "Can't attach from a pre-release Python interpreter" + " to a process running a different Python version"); + return -1; + } + + if (is_prerelease_version(debug_offsets->version) && Py_Version != debug_offsets->version) { + PyErr_SetString( + PyExc_RuntimeError, + "Can't attach to a pre-release Python interpreter" + " from a process running a different Python version"); + return -1; + } + + unsigned int remote_major = (debug_offsets->version >> 24) & 0xFF; + unsigned int remote_minor = (debug_offsets->version >> 16) & 0xFF; + + if (PY_MAJOR_VERSION != remote_major || PY_MINOR_VERSION != remote_minor) { + PyErr_Format( + PyExc_RuntimeError, + "Can't attach from a Python %d.%d process to a Python %d.%d process", + PY_MAJOR_VERSION, PY_MINOR_VERSION, remote_major, remote_minor); + return -1; + } + + // The debug offsets differ between free threaded and non-free threaded builds. + if (_Py_Debug_Free_Threaded && !debug_offsets->free_threaded) { + PyErr_SetString( + PyExc_RuntimeError, + "Cannot attach from a free-threaded Python process" + " to a process running a non-free-threaded version"); + return -1; + } + + if (!_Py_Debug_Free_Threaded && debug_offsets->free_threaded) { + PyErr_SetString( + PyExc_RuntimeError, + "Cannot attach to a free-threaded Python process" + " from a process running a non-free-threaded version"); + return -1; + } + + return _PyRemoteDebug_ValidateDebugOffsetsLayout(debug_offsets); +} + +/* ============================================================================ + * REMOTEUNWINDER CLASS IMPLEMENTATION + * ============================================================================ */ + +/*[clinic input] +module _remote_debugging +class _remote_debugging.RemoteUnwinder "RemoteUnwinderObject *" "&RemoteUnwinder_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=12b4dce200381115]*/ + +/*[clinic input] +@permit_long_summary +_remote_debugging.RemoteUnwinder.__init__ + pid: int + * + all_threads: bool = False + only_active_thread: bool = False + mode: int = 0 + debug: bool = False + skip_non_matching_threads: bool = True + native: bool = False + gc: bool = False + opcodes: bool = False + cache_frames: bool = False + stats: bool = False + +Initialize a new RemoteUnwinder object for debugging a remote Python process. + +Args: + pid: Process ID of the target Python process to debug + all_threads: If True, initialize state for all threads in the + process. If False, only initialize for the main thread. + only_active_thread: If True, only sample the thread holding the GIL. + mode: Profiling mode: 0=WALL (wall-time), 1=CPU (cpu-time), 2=GIL + (gil-time). Cannot be used together with all_threads=True. + debug: If True, chain exceptions to explain the sequence of events + that lead to the exception. + skip_non_matching_threads: If True, skip threads that don't match + the selected mode. If False, include all threads regardless of + mode. + native: If True, include artificial "<native>" frames to denote + calls to non-Python code. + gc: If True, include artificial "<GC>" frames to denote active + garbage collection. + opcodes: If True, gather bytecode opcode information for + instruction-level profiling. + cache_frames: If True, enable frame caching optimization to avoid + re-reading unchanged parent frames between samples. + stats: If True, collect statistics about cache hits, memory reads, + etc. Use get_stats() to retrieve the collected statistics. + +The RemoteUnwinder provides functionality to inspect and debug a running +Python process, including examining thread states, stack frames and +other runtime data. + +Raises: + PermissionError: If access to the target process is denied + OSError: If unable to attach to the target process or access its + memory + RuntimeError: If unable to read debug information from the target + process + ValueError: If both all_threads and only_active_thread are True +[clinic start generated code]*/ + +static int +_remote_debugging_RemoteUnwinder___init___impl(RemoteUnwinderObject *self, + int pid, int all_threads, + int only_active_thread, + int mode, int debug, + int skip_non_matching_threads, + int native, int gc, + int opcodes, int cache_frames, + int stats) +/*[clinic end generated code: output=0031f743f4b9ad52 input=9d25ae328d62626d]*/ +{ + // Validate that all_threads and only_active_thread are not both True + if (all_threads && only_active_thread) { + PyErr_SetString(PyExc_ValueError, + "all_threads and only_active_thread cannot both be True"); + return -1; + } + +#ifdef Py_GIL_DISABLED + if (only_active_thread) { + PyErr_SetString(PyExc_ValueError, + "only_active_thread is not supported in free-threaded builds"); + return -1; + } +#endif + + self->native = native; + self->gc = gc; + self->opcodes = opcodes; + self->cache_frames = cache_frames; + self->collect_stats = stats; + self->stale_invalidation_counter = 0; + self->cached_tstate_interpreter_addr = 0; + self->cached_tstate_addr = 0; + memset(self->cached_tstates, 0, sizeof(self->cached_tstates)); + memset(self->cached_generations, 0, sizeof(self->cached_generations)); + self->debug = debug; + self->only_active_thread = only_active_thread; + self->mode = mode; + self->skip_non_matching_threads = skip_non_matching_threads; + self->cached_state = NULL; + self->frame_cache = NULL; +#ifdef Py_REMOTE_DEBUG_SUPPORTS_BLOCKING + self->threads_stopped = 0; +#endif + // Initialize stats to zero + memset(&self->stats, 0, sizeof(self->stats)); + if (_Py_RemoteDebug_InitProcHandle(&self->handle, pid) < 0) { + set_exception_cause(self, PyExc_RuntimeError, "Failed to initialize process handle"); + return -1; + } + + self->runtime_start_address = _Py_RemoteDebug_GetPyRuntimeAddress(&self->handle); + if (self->runtime_start_address == 0) { + set_exception_cause(self, PyExc_RuntimeError, "Failed to get Python runtime address"); + return -1; + } + + if (_Py_RemoteDebug_ReadDebugOffsets(&self->handle, + &self->runtime_start_address, + &self->debug_offsets) < 0) + { + set_exception_cause(self, PyExc_RuntimeError, "Failed to read debug offsets"); + return -1; + } + + // Validate that the debug offsets are valid + if (validate_debug_offsets(&self->debug_offsets) == -1) { + set_exception_cause(self, PyExc_RuntimeError, "Invalid debug offsets found"); + return -1; + } + + // Try to read async debug offsets, but don't fail if they're not available + self->async_debug_offsets_available = 1; + int async_debug_result = read_async_debug(self); + if (async_debug_result == PY_REMOTE_DEBUG_INVALID_ASYNC_DEBUG_OFFSETS) { + return -1; + } + if (async_debug_result < 0) { + if (_Py_RemoteDebug_HasPermissionError()) { + return -1; + } + PyErr_Clear(); + memset(&self->async_debug_offsets, 0, sizeof(self->async_debug_offsets)); + self->async_debug_offsets_available = 0; + } + + if (populate_initial_state_data(all_threads, self, self->runtime_start_address, + &self->interpreter_addr ,&self->tstate_addr) < 0) + { + set_exception_cause(self, PyExc_RuntimeError, "Failed to populate initial state data"); + return -1; + } + + self->code_object_cache = _Py_hashtable_new_full( + _Py_hashtable_hash_ptr, + _Py_hashtable_compare_direct, + NULL, // keys are stable pointers, don't destroy + cached_code_metadata_destroy, + NULL + ); + if (self->code_object_cache == NULL) { + PyErr_NoMemory(); + set_exception_cause(self, PyExc_MemoryError, "Failed to create code object cache"); + return -1; + } + +#ifdef Py_GIL_DISABLED + // Initialize TLBC cache + self->tlbc_generation = 0; + self->tlbc_cache = _Py_hashtable_new_full( + _Py_hashtable_hash_ptr, + _Py_hashtable_compare_direct, + NULL, // keys are stable pointers, don't destroy + tlbc_cache_entry_destroy, + NULL + ); + if (self->tlbc_cache == NULL) { + _Py_hashtable_destroy(self->code_object_cache); + PyErr_NoMemory(); + set_exception_cause(self, PyExc_MemoryError, "Failed to create TLBC cache"); + return -1; + } +#endif + +#if defined(__APPLE__) + self->thread_id_offset = 0; + self->thread_id_offset_initialized = 0; +#endif + +#ifdef MS_WINDOWS + self->win_process_buffer = NULL; + self->win_process_buffer_size = 0; +#endif +#ifdef __linux__ + self->thread_tids = NULL; + self->thread_tids_capacity = 0; +#endif + + if (cache_frames && frame_cache_init(self) < 0) { + return -1; + } + + // Clear stale last_profiled_frame values from previous profilers + // This prevents us from stopping frame walking early due to stale values + if (cache_frames) { + clear_last_profiled_frames(self); + } + + return 0; +} + +static inline size_t +interpreter_thread_cache_index(uintptr_t interpreter_addr) +{ + // Direct-mapped table indexed by the remote interpreter address. Each entry + // stores the full address and verifies it on lookup, so hash collisions + // degrade to misses and cannot return a value from the wrong interpreter. + return (size_t)_Py_HashPointerRaw((const void *)interpreter_addr) + & (INTERPRETER_THREAD_CACHE_SIZE - 1); +} + +static inline uintptr_t +get_cached_tstate_for_interpreter( + RemoteUnwinderObject *self, + uintptr_t interpreter_addr) +{ + if (interpreter_addr == 0) { + return 0; + } + + if (self->cached_tstate_interpreter_addr == interpreter_addr) { + return self->cached_tstate_addr; + } + + InterpreterTstateCacheEntry *entry = + &self->cached_tstates[interpreter_thread_cache_index(interpreter_addr)]; + if (entry->interpreter_addr == interpreter_addr) { + self->cached_tstate_interpreter_addr = interpreter_addr; + self->cached_tstate_addr = entry->thread_state_addr; + return entry->thread_state_addr; + } + return 0; +} + +static inline void +set_cached_tstate_for_interpreter( + RemoteUnwinderObject *self, + uintptr_t interpreter_addr, + uintptr_t thread_state_addr) +{ + if (interpreter_addr == 0 || thread_state_addr == 0) { + return; + } + + self->cached_tstate_interpreter_addr = interpreter_addr; + self->cached_tstate_addr = thread_state_addr; + + InterpreterTstateCacheEntry *entry = + &self->cached_tstates[interpreter_thread_cache_index(interpreter_addr)]; + entry->interpreter_addr = interpreter_addr; + entry->thread_state_addr = thread_state_addr; +} + +static void +refresh_generation_caches_from_interp_state( + RemoteUnwinderObject *self, + uintptr_t interpreter_addr, + const char *interp_state_buffer) +{ + uint64_t code_object_generation = GET_MEMBER(uint64_t, interp_state_buffer, + self->debug_offsets.interpreter_state.code_object_generation); + + if (self->cached_generation_interpreter_addr == interpreter_addr) { + if (code_object_generation != self->cached_code_object_generation) { + self->cached_code_object_generation = code_object_generation; + _Py_hashtable_clear(self->code_object_cache); + } + } + else { + InterpreterGenerationCacheEntry *entry = + &self->cached_generations[interpreter_thread_cache_index(interpreter_addr)]; + // A slot rebound from another interpreter must be treated as changed: + // the code_object_cache is global, so even if the new generation + // numerically matches what the previous occupant had, stale entries + // from that occupant could still be served. + int changed = entry->interpreter_addr != interpreter_addr + || entry->code_object_generation != code_object_generation; + entry->interpreter_addr = interpreter_addr; + entry->code_object_generation = code_object_generation; + if (changed) { + _Py_hashtable_clear(self->code_object_cache); + } + self->cached_generation_interpreter_addr = interpreter_addr; + self->cached_code_object_generation = code_object_generation; + } + +#ifdef Py_GIL_DISABLED + uint32_t current_tlbc_generation = GET_MEMBER(uint32_t, interp_state_buffer, + self->debug_offsets.interpreter_state.tlbc_generation); + if (current_tlbc_generation != self->tlbc_generation) { + self->tlbc_generation = current_tlbc_generation; + _Py_hashtable_clear(self->tlbc_cache); + } +#endif +} + +static int +refresh_generation_caches_for_interpreter( + RemoteUnwinderObject *self, + uintptr_t interpreter_addr) +{ + char interp_state_buffer[INTERP_STATE_BUFFER_SIZE]; + if (_Py_RemoteDebug_ReadRemoteMemory( + &self->handle, + interpreter_addr, + INTERP_STATE_BUFFER_SIZE, + interp_state_buffer) < 0) { + set_exception_cause(self, PyExc_RuntimeError, + "Failed to read interpreter state buffer"); + return -1; + } + refresh_generation_caches_from_interp_state(self, interpreter_addr, interp_state_buffer); + return 0; +} + +static int +read_interp_state_and_maybe_thread_frame( + RemoteUnwinderObject *unwinder, + uintptr_t interpreter_addr, + char *interp_state_buffer, + char *tstate_buffer, + char *frame_buffer, + RemoteReadPrefetch *prefetch) +{ + prefetch->tstate = NULL; + prefetch->frame = NULL; + if (prefetch->tstate_addr != 0) { + size_t tstate_size = (size_t)unwinder->debug_offsets.thread_state.size; + _Py_RemoteReadSegment segments[3] = { + {interpreter_addr, interp_state_buffer, INTERP_STATE_BUFFER_SIZE}, + {prefetch->tstate_addr, tstate_buffer, tstate_size}, + {prefetch->frame_addr, frame_buffer, SIZEOF_INTERP_FRAME}, + }; + int nsegs = prefetch->frame_addr != 0 ? 3 : 2; + Py_ssize_t nread = _Py_RemoteDebug_BatchedReadRemoteMemory( + &unwinder->handle, segments, nsegs); + int completed = 0; + if (nread >= (Py_ssize_t)INTERP_STATE_BUFFER_SIZE) { + completed = 1; + Py_ssize_t with_tstate = (Py_ssize_t)INTERP_STATE_BUFFER_SIZE + + (Py_ssize_t)tstate_size; + if (nread >= with_tstate) { + completed = 2; + } + if (nsegs == 3 + && nread == with_tstate + (Py_ssize_t)SIZEOF_INTERP_FRAME) { + completed = 3; + } + } + STATS_BATCHED_READ(unwinder, nsegs, completed); + if (completed >= 1) { + if (completed >= 2) { + prefetch->tstate = tstate_buffer; + } + if (completed >= 3) { + prefetch->frame = frame_buffer; + } + return 0; + } + } + return _Py_RemoteDebug_ReadRemoteMemory( + &unwinder->handle, + interpreter_addr, + INTERP_STATE_BUFFER_SIZE, + interp_state_buffer); +} + +/*[clinic input] +@critical_section +_remote_debugging.RemoteUnwinder.get_stack_trace + +Returns stack traces for all interpreters and threads in process. + +Each element in the returned list is a tuple of (interpreter_id, +thread_list), where: +- interpreter_id is the interpreter identifier +- thread_list is a list of tuples (thread_id, frame_list) for + threads in that interpreter + - thread_id is the OS thread identifier + - frame_list is a list of tuples (function_name, filename, + line_number) representing the Python stack frames for that + thread, ordered from most recent to oldest + +The threads returned depend on the initialization parameters: +- If only_active_thread was True: returns only the thread holding + the GIL across all interpreters +- If all_threads was True: returns all threads across all + interpreters +- Otherwise: returns only the main thread of each interpreter + +Example: + [ + (0, [ # Main interpreter + (1234, [ + ('process_data', 'worker.py', 127), + ('run_worker', 'worker.py', 45), + ('main', 'app.py', 23) + ]), + (1235, [ + ('handle_request', 'server.py', 89), + ('serve_forever', 'server.py', 52) + ]) + ]), + (1, [ # Sub-interpreter + (1236, [ + ('sub_worker', 'sub.py', 15) + ]) + ]) + ] + +Raises: + RuntimeError: If there is an error copying memory from the + target process + OSError: If there is an error accessing the target process + PermissionError: If access to the target process is denied + UnicodeDecodeError: If there is an error decoding strings from + the target process + +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self) +/*[clinic end generated code: output=666192b90c69d567 input=86a992b853f48aa9]*/ +{ + STATS_INC(self, total_samples); + + PyObject* result = PyList_New(0); + if (!result) { + set_exception_cause(self, PyExc_MemoryError, "Failed to create stack trace result list"); + return NULL; + } + + // Iterate over all interpreters + uintptr_t current_interpreter = self->interpreter_addr; + while (current_interpreter != 0) { + // Read interpreter state to get the interpreter ID + char interp_state_buffer[INTERP_STATE_BUFFER_SIZE]; + char prefetched_tstate[SIZEOF_THREAD_STATE]; + char prefetched_frame[SIZEOF_INTERP_FRAME]; + RemoteReadPrefetch prefetch = {0}; + if (self->cache_frames) { + prefetch.tstate_addr = get_cached_tstate_for_interpreter( + self, current_interpreter); + } + if (prefetch.tstate_addr != 0) { + FrameCacheEntry *entry = frame_cache_find_by_tstate(self, prefetch.tstate_addr); + if (entry && entry->num_addrs > 0) { + prefetch.frame_addr = entry->addrs[0]; + } + } + + if (read_interp_state_and_maybe_thread_frame( + self, + current_interpreter, + interp_state_buffer, + prefetched_tstate, + prefetched_frame, + &prefetch) < 0) { + set_exception_cause(self, PyExc_RuntimeError, "Failed to read interpreter state buffer"); + Py_CLEAR(result); + goto exit; + } + refresh_generation_caches_from_interp_state(self, current_interpreter, interp_state_buffer); + + uintptr_t gc_frame = 0; + if (self->gc) { + gc_frame = GET_MEMBER(uintptr_t, interp_state_buffer, + self->debug_offsets.interpreter_state.gc + + self->debug_offsets.gc.frame); + } + + int64_t interpreter_id = GET_MEMBER(int64_t, interp_state_buffer, + self->debug_offsets.interpreter_state.id); + + // Create a list to hold threads for this interpreter + PyObject *interpreter_threads = PyList_New(0); + if (!interpreter_threads) { + set_exception_cause(self, PyExc_MemoryError, "Failed to create interpreter threads list"); + Py_CLEAR(result); + goto exit; + } + + // Get the GIL holder for this interpreter (needed for GIL_WAIT logic) + uintptr_t gil_holder_tstate = 0; + int gil_locked = GET_MEMBER(int, interp_state_buffer, + self->debug_offsets.interpreter_state.gil_runtime_state_locked); + if (gil_locked) { + gil_holder_tstate = (uintptr_t)GET_MEMBER(PyThreadState*, interp_state_buffer, + self->debug_offsets.interpreter_state.gil_runtime_state_holder); + } + + uintptr_t current_tstate; + if (self->only_active_thread) { + // Find the GIL holder for THIS interpreter + if (!gil_locked) { + // This interpreter's GIL is not locked, skip it + Py_DECREF(interpreter_threads); + goto next_interpreter; + } + + current_tstate = gil_holder_tstate; + } else if (self->tstate_addr == 0) { + // Get all threads for this interpreter + current_tstate = GET_MEMBER(uintptr_t, interp_state_buffer, + self->debug_offsets.interpreter_state.threads_head); + } else { + // Target specific thread (only process first interpreter) + current_tstate = self->tstate_addr; + } + if (current_tstate != 0 && self->cache_frames) { + set_cached_tstate_for_interpreter(self, current_interpreter, current_tstate); + } + + // Acquire main thread state information + uintptr_t main_thread_tstate = GET_MEMBER(uintptr_t, interp_state_buffer, + self->debug_offsets.interpreter_state.threads_main); + + while (current_tstate != 0) { + uintptr_t prev_tstate = current_tstate; + PyObject* frame_info = unwind_stack_for_thread(self, &current_tstate, + gil_holder_tstate, + gc_frame, + main_thread_tstate, + &prefetch); + if (!frame_info) { + // Check if this was an intentional skip due to mode-based filtering + if ((self->mode == PROFILING_MODE_CPU || self->mode == PROFILING_MODE_GIL || + self->mode == PROFILING_MODE_EXCEPTION) && !PyErr_Occurred()) { + // Detect cycle: if current_tstate didn't advance, we have corrupted data + if (current_tstate == prev_tstate) { + Py_DECREF(interpreter_threads); + PyErr_Format(PyExc_RuntimeError, + "Thread list cycle detected at address 0x%lx (corrupted remote memory)", + current_tstate); + set_exception_cause(self, PyExc_RuntimeError, + "Thread list cycle detected (corrupted remote memory)"); + Py_CLEAR(result); + goto exit; + } + // Thread was skipped due to mode filtering, continue to next thread + continue; + } + // This was an actual error + Py_DECREF(interpreter_threads); + set_exception_cause(self, PyExc_RuntimeError, "Failed to unwind stack for thread"); + Py_CLEAR(result); + goto exit; + } + + if (PyList_Append(interpreter_threads, frame_info) == -1) { + Py_DECREF(frame_info); + Py_DECREF(interpreter_threads); + set_exception_cause(self, PyExc_RuntimeError, "Failed to append thread frame info"); + Py_CLEAR(result); + goto exit; + } + Py_DECREF(frame_info); + + // If targeting specific thread or only active thread, process just one + if (self->tstate_addr || self->only_active_thread) { + break; + } + } + + // Create the InterpreterInfo StructSequence + RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)self); + PyObject *interpreter_info = PyStructSequence_New(state->InterpreterInfo_Type); + if (!interpreter_info) { + Py_DECREF(interpreter_threads); + set_exception_cause(self, PyExc_MemoryError, "Failed to create InterpreterInfo"); + Py_CLEAR(result); + goto exit; + } + + PyObject *interp_id = PyLong_FromLongLong(interpreter_id); + if (!interp_id) { + Py_DECREF(interpreter_threads); + Py_DECREF(interpreter_info); + set_exception_cause(self, PyExc_MemoryError, "Failed to create interpreter ID"); + Py_CLEAR(result); + goto exit; + } + + PyStructSequence_SetItem(interpreter_info, 0, interp_id); // steals reference + PyStructSequence_SetItem(interpreter_info, 1, interpreter_threads); // steals reference + + // Add this interpreter to the result list + if (PyList_Append(result, interpreter_info) == -1) { + Py_DECREF(interpreter_info); + set_exception_cause(self, PyExc_RuntimeError, "Failed to append interpreter info"); + Py_CLEAR(result); + goto exit; + } + Py_DECREF(interpreter_info); + +next_interpreter: + + // Get the next interpreter address + current_interpreter = GET_MEMBER(uintptr_t, interp_state_buffer, + self->debug_offsets.interpreter_state.next); + + // If we're targeting a specific thread, stop after first interpreter + if (self->tstate_addr != 0) { + break; + } + } + +exit: + // Invalidate cache entries for threads not seen in this sample. + // Only do this every 1024 iterations to avoid performance overhead. + if (self->cache_frames && result) { + if (++self->stale_invalidation_counter >= 1024) { + self->stale_invalidation_counter = 0; + frame_cache_invalidate_stale(self, result); + } + } + _Py_RemoteDebug_ClearCache(&self->handle); + return result; +} + +/*[clinic input] +@permit_long_summary +@critical_section +_remote_debugging.RemoteUnwinder.get_all_awaited_by + +Get all tasks and their awaited_by relationships from the remote process. + +This provides a tree structure showing which tasks are waiting for +other tasks. + +For each task, returns: +1. The call stack frames leading to where the task is currently + executing +2. The name of the task +3. A list of tasks that this task is waiting for, with their own + frames/names/etc + +Returns a list of [frames, task_name, subtasks] where: +- frames: List of (func_name, filename, lineno) showing the call + stack +- task_name: String identifier for the task +- subtasks: List of tasks being awaited by this task, in same format + +Raises: + RuntimeError: If AsyncioDebug section is not available in the + remote process + MemoryError: If memory allocation fails + OSError: If reading from the remote process fails + +Example output: +[ + # Task c2_root waiting for two subtasks + [ + # Call stack of c2_root + [("c5", "script.py", 10), ("c4", "script.py", 14)], + "c2_root", + [ + # First subtask (sub_main_2) and what it's waiting for + [ + [("c1", "script.py", 23)], + "sub_main_2", + [...] + ], + # Second subtask and its waiters + [...] + ] + ] +] +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_RemoteUnwinder_get_all_awaited_by_impl(RemoteUnwinderObject *self) +/*[clinic end generated code: output=6a49cd345e8aec53 input=c22bfee0612e0b69]*/ +{ + if (ensure_async_debug_offsets(self) < 0) { + return NULL; + } + if (refresh_generation_caches_for_interpreter(self, self->interpreter_addr) < 0) { + return NULL; + } + + PyObject *result = PyList_New(0); + if (result == NULL) { + set_exception_cause(self, PyExc_MemoryError, "Failed to create awaited_by result list"); + goto result_err; + } + + // Process all threads + if (iterate_threads(self, process_thread_for_awaited_by, result) < 0) { + goto result_err; + } + + uintptr_t head_addr = self->interpreter_addr + + (uintptr_t)self->async_debug_offsets.asyncio_interpreter_state.asyncio_tasks_head; + + // On top of a per-thread task lists used by default by asyncio to avoid + // contention, there is also a fallback per-interpreter list of tasks; + // any tasks still pending when a thread is destroyed will be moved to the + // per-interpreter task list. It's unlikely we'll find anything here, but + // interesting for debugging. + if (append_awaited_by(self, 0, head_addr, result)) + { + set_exception_cause(self, PyExc_RuntimeError, "Failed to append interpreter awaited_by in get_all_awaited_by"); + goto result_err; + } + + _Py_RemoteDebug_ClearCache(&self->handle); + return result; + +result_err: + _Py_RemoteDebug_ClearCache(&self->handle); + Py_XDECREF(result); + return NULL; +} + +/*[clinic input] +@permit_long_summary +@critical_section +_remote_debugging.RemoteUnwinder.get_async_stack_trace + +Get the currently running async tasks and their dependency graphs from the remote process. + +This returns information about running tasks and all tasks that are +waiting for them, forming a complete dependency graph for each +thread's active task. + +For each thread with a running task, returns the running task plus +all tasks that transitively depend on it (tasks waiting for the +running task, tasks waiting for those tasks, etc.). + +Returns a list of per-thread results, where each thread result +contains: +- Thread ID +- List of task information for the running task and all its waiters + +Each task info contains: +- Task ID (memory address) +- Task name +- Call stack frames: List of (func_name, filename, lineno) +- List of tasks waiting for this task (recursive structure) + +Raises: + RuntimeError: If AsyncioDebug section is not available in the + target process + MemoryError: If memory allocation fails + OSError: If reading from the remote process fails + +Example output (similar structure to get_all_awaited_by but only for +running tasks): +[ + # Thread 140234 results + (140234, [ + # Running task and its complete waiter dependency graph + (4345585712, 'main_task', + [("run_server", "server.py", 127), ("main", "app.py", 23)], + [ + # Tasks waiting for main_task + (4345585800, 'worker_1', [...], [...]), + (4345585900, 'worker_2', [...], [...]) + ]) + ]) +] + +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_RemoteUnwinder_get_async_stack_trace_impl(RemoteUnwinderObject *self) +/*[clinic end generated code: output=6433d52b55e87bbe input=bc802e4221c99399]*/ +{ + if (ensure_async_debug_offsets(self) < 0) { + return NULL; + } + if (refresh_generation_caches_for_interpreter(self, self->interpreter_addr) < 0) { + return NULL; + } + + PyObject *result = PyList_New(0); + if (result == NULL) { + set_exception_cause(self, PyExc_MemoryError, "Failed to create result list in get_async_stack_trace"); + return NULL; + } + + // Process all threads + if (iterate_threads(self, process_thread_for_async_stack_trace, result) < 0) { + goto result_err; + } + + _Py_RemoteDebug_ClearCache(&self->handle); + return result; +result_err: + _Py_RemoteDebug_ClearCache(&self->handle); + Py_XDECREF(result); + return NULL; +} + +/*[clinic input] +@critical_section +_remote_debugging.RemoteUnwinder.get_stats + +Get collected statistics about profiling performance. + +Returns a dictionary containing statistics about cache performance, +memory reads, and other profiling metrics. Only available if the +RemoteUnwinder was created with stats=True. + +Returns: + dict: A dictionary containing: + - total_samples: Total number of get_stack_trace calls + - frame_cache_hits: Full cache hits (entire stack unchanged) + - frame_cache_misses: Cache misses requiring full walk + - frame_cache_partial_hits: Partial hits (stopped at cached + frame) + - frames_read_from_cache: Total frames retrieved from cache + - frames_read_from_memory: Total frames read from remote + memory + - memory_reads: Total remote memory read operations + - memory_bytes_read: Total bytes read from remote memory + - code_object_cache_hits: Code object cache hits + - code_object_cache_misses: Code object cache misses + - stale_cache_invalidations: Times stale cache entries were + cleared + - batched_read_attempts: Batched remote-read attempts + - batched_read_successes: Attempts that read all requested + segments + - batched_read_misses: Attempts that fell back or partially + read + - batched_read_segments_requested: Segments requested by + batched reads + - batched_read_segments_completed: Segments completed by + batched reads + - frame_cache_hit_rate: Percentage of samples that hit the + cache + - code_object_cache_hit_rate: Percentage of code object + lookups that hit cache + - batched_read_success_rate: Percentage of batched reads + that completed all segments + - batched_read_segment_completion_rate: Percentage of + requested segments read by batched reads + +Raises: + RuntimeError: If stats collection was not enabled (stats=False) +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_RemoteUnwinder_get_stats_impl(RemoteUnwinderObject *self) +/*[clinic end generated code: output=21e36477122be2a0 input=87905c65038fb06e]*/ +{ + if (!self->collect_stats) { + PyErr_SetString(PyExc_RuntimeError, + "Statistics collection was not enabled. " + "Create RemoteUnwinder with stats=True to collect statistics."); + return NULL; + } + + PyObject *result = PyDict_New(); + if (!result) { + return NULL; + } + +#define ADD_STAT(name) do { \ + PyObject *val = PyLong_FromUnsignedLongLong(self->stats.name); \ + if (!val || PyDict_SetItemString(result, #name, val) < 0) { \ + Py_XDECREF(val); \ + Py_DECREF(result); \ + return NULL; \ + } \ + Py_DECREF(val); \ +} while(0) + + ADD_STAT(total_samples); + ADD_STAT(frame_cache_hits); + ADD_STAT(frame_cache_misses); + ADD_STAT(frame_cache_partial_hits); + ADD_STAT(frames_read_from_cache); + ADD_STAT(frames_read_from_memory); + ADD_STAT(memory_reads); + ADD_STAT(memory_bytes_read); + ADD_STAT(code_object_cache_hits); + ADD_STAT(code_object_cache_misses); + ADD_STAT(stale_cache_invalidations); + ADD_STAT(batched_read_attempts); + ADD_STAT(batched_read_successes); + ADD_STAT(batched_read_misses); + ADD_STAT(batched_read_segments_requested); + ADD_STAT(batched_read_segments_completed); + +#undef ADD_STAT + +#define ADD_DERIVED_STAT(name, value) do { \ + PyObject *val = PyFloat_FromDouble(value); \ + if (!val || PyDict_SetItemString(result, name, val) < 0) { \ + Py_XDECREF(val); \ + Py_DECREF(result); \ + return NULL; \ + } \ + Py_DECREF(val); \ +} while(0) + + // Calculate and add derived statistics + // Hit rate is calculated as (hits + partial_hits) / total_cache_lookups + double frame_cache_hit_rate = 0.0; + uint64_t total_cache_lookups = self->stats.frame_cache_hits + self->stats.frame_cache_partial_hits + self->stats.frame_cache_misses; + if (total_cache_lookups > 0) { + frame_cache_hit_rate = 100.0 * (double)(self->stats.frame_cache_hits + self->stats.frame_cache_partial_hits) + / (double)total_cache_lookups; + } + ADD_DERIVED_STAT("frame_cache_hit_rate", frame_cache_hit_rate); + + double code_object_hit_rate = 0.0; + uint64_t total_code_lookups = self->stats.code_object_cache_hits + self->stats.code_object_cache_misses; + if (total_code_lookups > 0) { + code_object_hit_rate = 100.0 * (double)self->stats.code_object_cache_hits / (double)total_code_lookups; + } + ADD_DERIVED_STAT("code_object_cache_hit_rate", code_object_hit_rate); + + double batched_read_success_rate = 0.0; + if (self->stats.batched_read_attempts > 0) { + batched_read_success_rate = + 100.0 * (double)self->stats.batched_read_successes + / (double)self->stats.batched_read_attempts; + } + ADD_DERIVED_STAT("batched_read_success_rate", batched_read_success_rate); + + double batched_read_segment_completion_rate = 0.0; + if (self->stats.batched_read_segments_requested > 0) { + batched_read_segment_completion_rate = + 100.0 * (double)self->stats.batched_read_segments_completed + / (double)self->stats.batched_read_segments_requested; + } + ADD_DERIVED_STAT("batched_read_segment_completion_rate", + batched_read_segment_completion_rate); + +#undef ADD_DERIVED_STAT + + return result; +} + +/*[clinic input] +@critical_section +_remote_debugging.RemoteUnwinder.pause_threads + +Pause all threads in the target process. + +This stops all threads in the target process to allow for consistent +memory reads during sampling. Must be paired with a call to +resume_threads(). + +Returns True if threads were successfully paused, False if they were +already paused. + +Raises: + RuntimeError: If there is an error stopping the threads +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_RemoteUnwinder_pause_threads_impl(RemoteUnwinderObject *self) +/*[clinic end generated code: output=aaf2bdc0a725750c input=b91dde5517c9dde2]*/ +{ +#ifdef Py_REMOTE_DEBUG_SUPPORTS_BLOCKING + if (self->threads_stopped) { + Py_RETURN_FALSE; + } + + _Py_RemoteDebug_InitThreadsState(self, &self->threads_state); + if (_Py_RemoteDebug_StopAllThreads(self, &self->threads_state) < 0) { + return NULL; + } + + self->threads_stopped = 1; + Py_RETURN_TRUE; +#else + PyErr_SetString(PyExc_NotImplementedError, + "pause_threads is not supported on this platform"); + return NULL; +#endif +} + +/*[clinic input] +@critical_section +_remote_debugging.RemoteUnwinder.resume_threads + +Resume all threads in the target process. + +This resumes threads that were previously paused with +pause_threads(). + +Returns True if threads were successfully resumed, False if they +were not paused. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_RemoteUnwinder_resume_threads_impl(RemoteUnwinderObject *self) +/*[clinic end generated code: output=8d6781ea37095536 input=130758d55d46897a]*/ +{ +#ifdef Py_REMOTE_DEBUG_SUPPORTS_BLOCKING + if (!self->threads_stopped) { + Py_RETURN_FALSE; + } + + _Py_RemoteDebug_ResumeAllThreads(self, &self->threads_state); + self->threads_stopped = 0; + Py_RETURN_TRUE; +#else + PyErr_SetString(PyExc_NotImplementedError, + "resume_threads is not supported on this platform"); + return NULL; +#endif +} + +static PyMethodDef RemoteUnwinder_methods[] = { + _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_STACK_TRACE_METHODDEF + _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_ALL_AWAITED_BY_METHODDEF + _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_ASYNC_STACK_TRACE_METHODDEF + _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_STATS_METHODDEF + _REMOTE_DEBUGGING_REMOTEUNWINDER_PAUSE_THREADS_METHODDEF + _REMOTE_DEBUGGING_REMOTEUNWINDER_RESUME_THREADS_METHODDEF + {NULL, NULL} +}; + +static void +RemoteUnwinder_dealloc(PyObject *op) +{ + RemoteUnwinderObject *self = RemoteUnwinder_CAST(op); + PyTypeObject *tp = Py_TYPE(self); + +#ifdef Py_REMOTE_DEBUG_SUPPORTS_BLOCKING + if (self->threads_stopped) { + _Py_RemoteDebug_ResumeAllThreads(self, &self->threads_state); + self->threads_stopped = 0; + } +#endif +#ifdef __linux__ + if (self->thread_tids != NULL) { + PyMem_RawFree(self->thread_tids); + self->thread_tids = NULL; + } +#endif + + if (self->code_object_cache) { + _Py_hashtable_destroy(self->code_object_cache); + } +#ifdef MS_WINDOWS + if (self->win_process_buffer != NULL) { + PyMem_Free(self->win_process_buffer); + } +#endif + +#ifdef Py_GIL_DISABLED + if (self->tlbc_cache) { + _Py_hashtable_destroy(self->tlbc_cache); + } +#endif + if (self->handle.pid != 0) { + _Py_RemoteDebug_ClearCache(&self->handle); + _Py_RemoteDebug_CleanupProcHandle(&self->handle); + } + frame_cache_cleanup(self); + PyObject_Del(self); + Py_DECREF(tp); +} + +static PyType_Slot RemoteUnwinder_slots[] = { + {Py_tp_doc, (void *)"RemoteUnwinder(pid): Inspect stack of a remote Python process."}, + {Py_tp_methods, RemoteUnwinder_methods}, + {Py_tp_init, _remote_debugging_RemoteUnwinder___init__}, + {Py_tp_dealloc, RemoteUnwinder_dealloc}, + {0, NULL} +}; + +static PyType_Spec RemoteUnwinder_spec = { + .name = "_remote_debugging.RemoteUnwinder", + .basicsize = sizeof(RemoteUnwinderObject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = RemoteUnwinder_slots, +}; + +/* ============================================================================ + * GCMONITOR CLASS IMPLEMENTATION + * ============================================================================ */ + +static void +cleanup_runtime_offsets(RuntimeOffsets *offsets) +{ + if (offsets->handle.pid != 0) { + _Py_RemoteDebug_ClearCache(&offsets->handle); + _Py_RemoteDebug_CleanupProcHandle(&offsets->handle); + } +} + +static int +init_runtime_offsets(RuntimeOffsets *offsets, int pid, int debug) +{ + offsets->debug = debug; + if (_Py_RemoteDebug_InitProcHandle(&offsets->handle, pid) < 0) { + set_exception_cause(offsets, PyExc_RuntimeError, "Failed to initialize process handle"); + return -1; + } + offsets->runtime_start_address = _Py_RemoteDebug_GetPyRuntimeAddress(&offsets->handle); + if (offsets->runtime_start_address == 0) { + set_exception_cause(offsets, PyExc_RuntimeError, "Failed to get Python runtime address"); + goto error; + } + if (_Py_RemoteDebug_ReadDebugOffsets(&offsets->handle, + &offsets->runtime_start_address, + &offsets->debug_offsets) < 0) + { + set_exception_cause(offsets, PyExc_RuntimeError, "Failed to read debug offsets"); + goto error; + } + if (validate_debug_offsets(&offsets->debug_offsets) == -1) { + set_exception_cause(offsets, PyExc_RuntimeError, "Invalid debug offsets found"); + goto error; + } + return 0; + +error: + cleanup_runtime_offsets(offsets); + return -1; +} + +/*[clinic input] +class _remote_debugging.GCMonitor "GCMonitorObject *" "&GCMonitor_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ebc229325a5e5154]*/ + +/*[clinic input] +@permit_long_summary +_remote_debugging.GCMonitor.__init__ + pid: int + * + debug: bool = False + +Initialize a new GCMonitor object for monitoring GC events from remote process. + +Args: + pid: Process ID of the target Python process to monitor + debug: If True, chain exceptions to explain the sequence of + events that lead to the exception. + +The GCMonitor provides functionality to read GC statistics from +a running Python process. + +Raises: + PermissionError: If access to the target process is denied + OSError: If unable to attach to the target process or access + its memory + RuntimeError: If unable to read debug information from the + target process +[clinic start generated code]*/ + +static int +_remote_debugging_GCMonitor___init___impl(GCMonitorObject *self, int pid, + int debug) +/*[clinic end generated code: output=2cdf351c2f6335db input=03da0b2d3282ae1b]*/ +{ + return init_runtime_offsets(&self->offsets, pid, debug); +} + +/*[clinic input] +@critical_section +_remote_debugging.GCMonitor.get_gc_stats + + all_interpreters: bool = False + If True, return GC statistics from all interpreters. + If False, return only from main interpreter. + +Get garbage collector statistics from external Python process. + +Returns a list of GCStatsInfo objects with GC statistics data. + +Returns: + list of GCStatsInfo: A list of stats samples containing: + - gen: GC generation number. + - iid: Interpreter ID. + - ts_start: Raw timestamp at collection start. + - ts_stop: Raw timestamp at collection stop. + - collections: Total number of collections. + - collected: Total number of collected objects. + - uncollectable: Total number of uncollectable objects. + - candidates: Total objects considered and traversed. + - heap_size: number of live objects. + - duration: Total collection time, in seconds. + +Raises: + RuntimeError: If the target process cannot be inspected or if + its debug offsets or GC stats layout are incompatible. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_GCMonitor_get_gc_stats_impl(GCMonitorObject *self, + int all_interpreters) +/*[clinic end generated code: output=f73f365725224f7a input=ec016bc4be6dd003]*/ +{ + RemoteDebuggingState *st = RemoteDebugging_GetStateFromType(Py_TYPE(self)); + return get_gc_stats(&self->offsets, all_interpreters, st->GCStatsInfo_Type); +} + +static PyMethodDef GCMonitor_methods[] = { + _REMOTE_DEBUGGING_GCMONITOR_GET_GC_STATS_METHODDEF + {NULL, NULL} +}; + +static void +GCMonitor_dealloc(PyObject *op) +{ + GCMonitorObject *self = GCMonitor_CAST(op); + PyTypeObject *tp = Py_TYPE(self); + + cleanup_runtime_offsets(&self->offsets); + PyObject_Del(self); + Py_DECREF(tp); +} + +static PyType_Slot GCMonitor_slots[] = { + {Py_tp_doc, (void *)"GCMonitor(pid): Monitor GC events of a remote Python process."}, + {Py_tp_methods, GCMonitor_methods}, + {Py_tp_init, _remote_debugging_GCMonitor___init__}, + {Py_tp_dealloc, GCMonitor_dealloc}, + {0, NULL} +}; + +static PyType_Spec GCMonitor_spec = { + .name = "_remote_debugging.GCMonitor", + .basicsize = sizeof(GCMonitorObject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = GCMonitor_slots, +}; + +/* Forward declarations for type specs defined later */ +static PyType_Spec BinaryWriter_spec; +static PyType_Spec BinaryReader_spec; + +/* ============================================================================ + * MODULE INITIALIZATION + * ============================================================================ */ + +static int +_remote_debugging_exec(PyObject *m) +{ + RemoteDebuggingState *st = RemoteDebugging_GetState(m); +#define CREATE_TYPE(mod, type, spec) \ + do { \ + type = (PyTypeObject *)PyType_FromMetaclass(NULL, mod, spec, NULL); \ + if (type == NULL) { \ + return -1; \ + } \ + } while (0) + + CREATE_TYPE(m, st->RemoteDebugging_Type, &RemoteUnwinder_spec); + + if (PyModule_AddType(m, st->RemoteDebugging_Type) < 0) { + return -1; + } + + CREATE_TYPE(m, st->GCMonitor_Type, &GCMonitor_spec); + if (PyModule_AddType(m, st->GCMonitor_Type) < 0) { + return -1; + } + + // Initialize structseq types + st->TaskInfo_Type = PyStructSequence_NewType(&TaskInfo_desc); + if (st->TaskInfo_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, st->TaskInfo_Type) < 0) { + return -1; + } + + st->LocationInfo_Type = PyStructSequence_NewType(&LocationInfo_desc); + if (st->LocationInfo_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, st->LocationInfo_Type) < 0) { + return -1; + } + + st->FrameInfo_Type = PyStructSequence_NewType(&FrameInfo_desc); + if (st->FrameInfo_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, st->FrameInfo_Type) < 0) { + return -1; + } + + st->CoroInfo_Type = PyStructSequence_NewType(&CoroInfo_desc); + if (st->CoroInfo_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, st->CoroInfo_Type) < 0) { + return -1; + } + + st->ThreadInfo_Type = PyStructSequence_NewType(&ThreadInfo_desc); + if (st->ThreadInfo_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, st->ThreadInfo_Type) < 0) { + return -1; + } + + st->InterpreterInfo_Type = PyStructSequence_NewType(&InterpreterInfo_desc); + if (st->InterpreterInfo_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, st->InterpreterInfo_Type) < 0) { + return -1; + } + + st->AwaitedInfo_Type = PyStructSequence_NewType(&AwaitedInfo_desc); + if (st->AwaitedInfo_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, st->AwaitedInfo_Type) < 0) { + return -1; + } + + st->GCStatsInfo_Type = PyStructSequence_NewType(&GCStatsInfo_desc); + if (st->GCStatsInfo_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, st->GCStatsInfo_Type) < 0) { + return -1; + } + + // Create BinaryWriter and BinaryReader types + CREATE_TYPE(m, st->BinaryWriter_Type, &BinaryWriter_spec); + if (PyModule_AddType(m, st->BinaryWriter_Type) < 0) { + return -1; + } + + CREATE_TYPE(m, st->BinaryReader_Type, &BinaryReader_spec); + if (PyModule_AddType(m, st->BinaryReader_Type) < 0) { + return -1; + } + +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); +#endif + int rc = PyModule_AddIntConstant(m, "PROCESS_VM_READV_SUPPORTED", HAVE_PROCESS_VM_READV); + if (rc < 0) { + return -1; + } + + // Add thread status flag constants + if (PyModule_AddIntConstant(m, "THREAD_STATUS_HAS_GIL", THREAD_STATUS_HAS_GIL) < 0) { + return -1; + } + if (PyModule_AddIntConstant(m, "THREAD_STATUS_ON_CPU", THREAD_STATUS_ON_CPU) < 0) { + return -1; + } + if (PyModule_AddIntConstant(m, "THREAD_STATUS_UNKNOWN", THREAD_STATUS_UNKNOWN) < 0) { + return -1; + } + if (PyModule_AddIntConstant(m, "THREAD_STATUS_GIL_REQUESTED", THREAD_STATUS_GIL_REQUESTED) < 0) { + return -1; + } + if (PyModule_AddIntConstant(m, "THREAD_STATUS_HAS_EXCEPTION", THREAD_STATUS_HAS_EXCEPTION) < 0) { + return -1; + } + if (PyModule_AddIntConstant(m, "THREAD_STATUS_MAIN_THREAD", THREAD_STATUS_MAIN_THREAD) < 0) { + return -1; + } + + if (RemoteDebugging_InitState(st) < 0) { + return -1; + } + return 0; +} + +static int +remote_debugging_traverse(PyObject *mod, visitproc visit, void *arg) +{ + RemoteDebuggingState *state = RemoteDebugging_GetState(mod); + Py_VISIT(state->RemoteDebugging_Type); + Py_VISIT(state->TaskInfo_Type); + Py_VISIT(state->LocationInfo_Type); + Py_VISIT(state->FrameInfo_Type); + Py_VISIT(state->CoroInfo_Type); + Py_VISIT(state->ThreadInfo_Type); + Py_VISIT(state->InterpreterInfo_Type); + Py_VISIT(state->AwaitedInfo_Type); + Py_VISIT(state->GCStatsInfo_Type); + Py_VISIT(state->BinaryWriter_Type); + Py_VISIT(state->BinaryReader_Type); + Py_VISIT(state->GCMonitor_Type); + return 0; +} + +static int +remote_debugging_clear(PyObject *mod) +{ + RemoteDebuggingState *state = RemoteDebugging_GetState(mod); + Py_CLEAR(state->RemoteDebugging_Type); + Py_CLEAR(state->TaskInfo_Type); + Py_CLEAR(state->LocationInfo_Type); + Py_CLEAR(state->FrameInfo_Type); + Py_CLEAR(state->CoroInfo_Type); + Py_CLEAR(state->ThreadInfo_Type); + Py_CLEAR(state->InterpreterInfo_Type); + Py_CLEAR(state->AwaitedInfo_Type); + Py_CLEAR(state->GCStatsInfo_Type); + Py_CLEAR(state->BinaryWriter_Type); + Py_CLEAR(state->BinaryReader_Type); + Py_CLEAR(state->GCMonitor_Type); + return 0; +} + +static void +remote_debugging_free(void *mod) +{ + (void)remote_debugging_clear((PyObject *)mod); +} + +/* ============================================================================ + * BINARY WRITER CLASS + * ============================================================================ */ + +#define BinaryWriter_CAST(op) ((BinaryWriterObject *)(op)) + +/*[clinic input] +class _remote_debugging.BinaryWriter "BinaryWriterObject *" "&PyBinaryWriter_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e948838b90a2003c]*/ + +/*[clinic input] +_remote_debugging.BinaryWriter.__init__ + filename: object + sample_interval_us: unsigned_long_long + start_time_us: unsigned_long_long + * + compression: int = 0 + +High-performance binary writer for profiling data. + +Arguments: + filename: Path to output file + sample_interval_us: Sampling interval in microseconds + start_time_us: Start timestamp in microseconds (from + time.monotonic() * 1e6) + compression: 0=none, 1=zstd (default: 0) + +Use as a context manager or call finalize() when done. +[clinic start generated code]*/ + +static int +_remote_debugging_BinaryWriter___init___impl(BinaryWriterObject *self, + PyObject *filename, + unsigned long long sample_interval_us, + unsigned long long start_time_us, + int compression) +/*[clinic end generated code: output=00446656ea2e5986 input=2e3f298c69fc7666]*/ +{ + if (self->writer) { + binary_writer_destroy(self->writer); + } + + self->writer = binary_writer_create(filename, sample_interval_us, compression, start_time_us); + if (!self->writer) { + return -1; + } + + return 0; +} + +/*[clinic input] +_remote_debugging.BinaryWriter.write_sample + stack_frames: object + timestamp_us: unsigned_long_long + +Write a sample to the binary file. + +Arguments: + stack_frames: List of InterpreterInfo objects + timestamp_us: Current timestamp in microseconds (from + time.monotonic() * 1e6) +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryWriter_write_sample_impl(BinaryWriterObject *self, + PyObject *stack_frames, + unsigned long long timestamp_us) +/*[clinic end generated code: output=24d5b86679b4128f input=5033f1ae7fa135f1]*/ +{ + if (!self->writer) { + PyErr_SetString(PyExc_ValueError, "Writer is closed"); + return NULL; + } + + if (binary_writer_write_sample(self->writer, stack_frames, timestamp_us) < 0) { + return NULL; + } + + Py_RETURN_NONE; +} + +/* Finalize the writer, cache total_samples, and destroy it. + * + * The cache assignment must happen AFTER binary_writer_finalize(): finalize + * flushes pending RLE samples via flush_pending_rle(), which increments + * writer->total_samples for each one. Caching before finalize would lose + * those trailing samples. */ +static int +binary_writer_finalize_and_cache(BinaryWriterObject *self) +{ + if (binary_writer_finalize(self->writer) < 0) { + return -1; + } + self->cached_total_samples = self->writer->total_samples; + binary_writer_destroy(self->writer); + self->writer = NULL; + return 0; +} + +/*[clinic input] +_remote_debugging.BinaryWriter.finalize + +Finalize and close the binary file. + +Writes string/frame tables, footer, and updates header. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryWriter_finalize_impl(BinaryWriterObject *self) +/*[clinic end generated code: output=3534b88c6628de88 input=c02191750682f6a2]*/ +{ + if (!self->writer) { + PyErr_SetString(PyExc_ValueError, "Writer is already closed"); + return NULL; + } + + if (binary_writer_finalize_and_cache(self) < 0) { + return NULL; + } + + Py_RETURN_NONE; +} + +/*[clinic input] +_remote_debugging.BinaryWriter.close + +Close the writer without finalizing (discards data). +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryWriter_close_impl(BinaryWriterObject *self) +/*[clinic end generated code: output=9571bb2256fd1fd2 input=6e0da206e60daf16]*/ +{ + if (self->writer) { + binary_writer_destroy(self->writer); + self->writer = NULL; + } + Py_RETURN_NONE; +} + +/*[clinic input] +_remote_debugging.BinaryWriter.__enter__ + +Enter context manager. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryWriter___enter___impl(BinaryWriterObject *self) +/*[clinic end generated code: output=8eb95f61daf2d120 input=8ef14ee18da561d2]*/ +{ + Py_INCREF(self); + return (PyObject *)self; +} + +/*[clinic input] +_remote_debugging.BinaryWriter.__exit__ + exc_type: object = None + exc_val: object = None + exc_tb: object = None + +Exit context manager, finalizing the file. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryWriter___exit___impl(BinaryWriterObject *self, + PyObject *exc_type, + PyObject *exc_val, + PyObject *exc_tb) +/*[clinic end generated code: output=61831f47c72a53c6 input=12334ce1009af37f]*/ +{ + if (self->writer) { + /* Only finalize on normal exit (no exception) */ + if (exc_type == Py_None) { + if (binary_writer_finalize_and_cache(self) < 0) { + if (self->writer) { + binary_writer_destroy(self->writer); + self->writer = NULL; + } + return NULL; + } + } + else { + binary_writer_destroy(self->writer); + self->writer = NULL; + } + } + Py_RETURN_FALSE; +} + +/*[clinic input] +_remote_debugging.BinaryWriter.get_stats + +Get encoding statistics for the writer. + +Returns a dict with encoding statistics including +repeat/full/suffix/pop-push record counts, frames written/saved, and +compression ratio. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryWriter_get_stats_impl(BinaryWriterObject *self) +/*[clinic end generated code: output=06522cd52544df89 input=a8bb8c8682ccd34b]*/ +{ + if (!self->writer) { + PyErr_SetString(PyExc_ValueError, "Writer is closed"); + return NULL; + } + return binary_writer_get_stats(self->writer); +} + +static PyObject * +BinaryWriter_get_total_samples(PyObject *op, void *closure) +{ + BinaryWriterObject *self = BinaryWriter_CAST(op); + if (!self->writer) { + /* Use cached value after finalize/close */ + return PyLong_FromUnsignedLong(self->cached_total_samples); + } + return PyLong_FromUnsignedLong(self->writer->total_samples); +} + +static PyGetSetDef BinaryWriter_getset[] = { + {"total_samples", BinaryWriter_get_total_samples, NULL, "Total samples written", NULL}, + {NULL} +}; + +static PyMethodDef BinaryWriter_methods[] = { + _REMOTE_DEBUGGING_BINARYWRITER_WRITE_SAMPLE_METHODDEF + _REMOTE_DEBUGGING_BINARYWRITER_FINALIZE_METHODDEF + _REMOTE_DEBUGGING_BINARYWRITER_CLOSE_METHODDEF + _REMOTE_DEBUGGING_BINARYWRITER___ENTER___METHODDEF + _REMOTE_DEBUGGING_BINARYWRITER___EXIT___METHODDEF + _REMOTE_DEBUGGING_BINARYWRITER_GET_STATS_METHODDEF + {NULL, NULL, 0, NULL} +}; + +static void +BinaryWriter_dealloc(PyObject *op) +{ + BinaryWriterObject *self = BinaryWriter_CAST(op); + PyTypeObject *tp = Py_TYPE(self); + if (self->writer) { + binary_writer_destroy(self->writer); + } + tp->tp_free(self); + Py_DECREF(tp); +} + +static PyType_Slot BinaryWriter_slots[] = { + {Py_tp_getset, BinaryWriter_getset}, + {Py_tp_methods, BinaryWriter_methods}, + {Py_tp_init, _remote_debugging_BinaryWriter___init__}, + {Py_tp_dealloc, BinaryWriter_dealloc}, + {0, NULL} +}; + +static PyType_Spec BinaryWriter_spec = { + .name = "_remote_debugging.BinaryWriter", + .basicsize = sizeof(BinaryWriterObject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = BinaryWriter_slots, +}; + +/* ============================================================================ + * BINARY READER CLASS + * ============================================================================ */ + +#define BinaryReader_CAST(op) ((BinaryReaderObject *)(op)) + +/*[clinic input] +class _remote_debugging.BinaryReader "BinaryReaderObject *" "&PyBinaryReader_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=36400aaf6f53216d]*/ + +/*[clinic input] +_remote_debugging.BinaryReader.__init__ + filename: object + +High-performance binary reader for profiling data. + +Arguments: + filename: Path to input file + +Use as a context manager or call close() when done. +[clinic start generated code]*/ + +static int +_remote_debugging_BinaryReader___init___impl(BinaryReaderObject *self, + PyObject *filename) +/*[clinic end generated code: output=f04b33ee5c5e6dbf input=9d7cbe8b4f1a97c9]*/ +{ + if (self->reader) { + binary_reader_close(self->reader); + } + + self->reader = binary_reader_open(filename); + if (!self->reader) { + return -1; + } + + return 0; +} + +/*[clinic input] +_remote_debugging.BinaryReader.replay + collector: object + progress_callback: object = None + +Replay samples through a collector. + +Arguments: + collector: Collector object with collect() method + progress_callback: Optional callable(current, total) + +Returns: + Number of samples replayed +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryReader_replay_impl(BinaryReaderObject *self, + PyObject *collector, + PyObject *progress_callback) +/*[clinic end generated code: output=442345562574b61c input=ebb687aed3e0f4f1]*/ +{ + if (!self->reader) { + PyErr_SetString(PyExc_ValueError, "Reader is closed"); + return NULL; + } + + Py_ssize_t replayed = binary_reader_replay(self->reader, collector, progress_callback); + if (replayed < 0) { + return NULL; + } + + return PyLong_FromSsize_t(replayed); +} + +/*[clinic input] +_remote_debugging.BinaryReader.get_info + +Get metadata about the binary file. + +Returns: + Dict with file metadata +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryReader_get_info_impl(BinaryReaderObject *self) +/*[clinic end generated code: output=7f641fbd39147391 input=02e75e39c8a6cd1f]*/ +{ + if (!self->reader) { + PyErr_SetString(PyExc_ValueError, "Reader is closed"); + return NULL; + } + + return binary_reader_get_info(self->reader); +} + +/*[clinic input] +_remote_debugging.BinaryReader.get_stats + +Get reconstruction statistics from replay. + +Returns a dict with statistics about record types decoded and +samples reconstructed during replay. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryReader_get_stats_impl(BinaryReaderObject *self) +/*[clinic end generated code: output=628b9ab5e4c4fd36 input=15b8d8f89ccf3726]*/ +{ + if (!self->reader) { + PyErr_SetString(PyExc_ValueError, "Reader is closed"); + return NULL; + } + return binary_reader_get_stats(self->reader); +} + +/*[clinic input] +_remote_debugging.BinaryReader.close + +Close the reader and free resources. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryReader_close_impl(BinaryReaderObject *self) +/*[clinic end generated code: output=ad0238cf5240b4f8 input=b919a66c737712d5]*/ +{ + if (self->reader) { + binary_reader_close(self->reader); + self->reader = NULL; + } + Py_RETURN_NONE; +} + +/*[clinic input] +_remote_debugging.BinaryReader.__enter__ + +Enter context manager. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryReader___enter___impl(BinaryReaderObject *self) +/*[clinic end generated code: output=fade133538e93817 input=4794844c9efdc4f6]*/ +{ + Py_INCREF(self); + return (PyObject *)self; +} + +/*[clinic input] +_remote_debugging.BinaryReader.__exit__ + exc_type: object = None + exc_val: object = None + exc_tb: object = None + +Exit context manager, closing the file. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_BinaryReader___exit___impl(BinaryReaderObject *self, + PyObject *exc_type, + PyObject *exc_val, + PyObject *exc_tb) +/*[clinic end generated code: output=2acdd36cfdc14e4a input=87284243d7935835]*/ +{ + if (self->reader) { + binary_reader_close(self->reader); + self->reader = NULL; + } + Py_RETURN_FALSE; +} + +static PyObject * +BinaryReader_get_sample_count(BinaryReaderObject *self, void *closure) +{ + if (!self->reader) { + return PyLong_FromLong(0); + } + return PyLong_FromUnsignedLong(self->reader->sample_count); +} + +static PyObject * +BinaryReader_get_sample_interval_us(BinaryReaderObject *self, void *closure) +{ + if (!self->reader) { + return PyLong_FromLong(0); + } + return PyLong_FromUnsignedLongLong(self->reader->sample_interval_us); +} + +static PyGetSetDef BinaryReader_getset[] = { + {"sample_count", (getter)BinaryReader_get_sample_count, NULL, "Number of samples in file", NULL}, + {"sample_interval_us", (getter)BinaryReader_get_sample_interval_us, NULL, "Sample interval in microseconds", NULL}, + {NULL} +}; + +static PyMethodDef BinaryReader_methods[] = { + _REMOTE_DEBUGGING_BINARYREADER_REPLAY_METHODDEF + _REMOTE_DEBUGGING_BINARYREADER_GET_INFO_METHODDEF + _REMOTE_DEBUGGING_BINARYREADER_GET_STATS_METHODDEF + _REMOTE_DEBUGGING_BINARYREADER_CLOSE_METHODDEF + _REMOTE_DEBUGGING_BINARYREADER___ENTER___METHODDEF + _REMOTE_DEBUGGING_BINARYREADER___EXIT___METHODDEF + {NULL, NULL, 0, NULL} +}; + +static void +BinaryReader_dealloc(PyObject *op) +{ + BinaryReaderObject *self = BinaryReader_CAST(op); + PyTypeObject *tp = Py_TYPE(self); + if (self->reader) { + binary_reader_close(self->reader); + } + tp->tp_free(self); + Py_DECREF(tp); +} + +static PyType_Slot BinaryReader_slots[] = { + {Py_tp_getset, BinaryReader_getset}, + {Py_tp_methods, BinaryReader_methods}, + {Py_tp_init, _remote_debugging_BinaryReader___init__}, + {Py_tp_dealloc, BinaryReader_dealloc}, + {0, NULL} +}; + +static PyType_Spec BinaryReader_spec = { + .name = "_remote_debugging.BinaryReader", + .basicsize = sizeof(BinaryReaderObject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = BinaryReader_slots, +}; + +/* ============================================================================ + * MODULE METHODS + * ============================================================================ */ + +/*[clinic input] +_remote_debugging.zstd_available + +Check if zstd compression is available. + +Returns: + True if zstd available, False otherwise +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_zstd_available_impl(PyObject *module) +/*[clinic end generated code: output=55e35a70ef280cdd input=a1b4d41bc09c7cf9]*/ +{ + return PyBool_FromLong(binary_io_zstd_available()); +} + +/* ============================================================================ + * MODULE-LEVEL FUNCTIONS + * ============================================================================ */ + +/*[clinic input] +_remote_debugging.get_child_pids + + pid: int + Process ID of the parent process + * + recursive: bool = True + If True, return all descendants (children, grandchildren, etc.). + If False, return only direct children. + +Get all child process IDs of the given process. + +Returns a list of child process IDs. Returns an empty list if no +children are found. + +This function provides a snapshot of child processes at a moment in +time. Child processes may exit or new ones may be created after the +list is returned. + +Raises: + OSError: If unable to enumerate processes + NotImplementedError: If not supported on this platform +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_get_child_pids_impl(PyObject *module, int pid, + int recursive) +/*[clinic end generated code: output=1ae2289c6b953e4b input=c6437b52e2fdd880]*/ +{ + return enumerate_child_pids((pid_t)pid, recursive); +} + +/*[clinic input] +_remote_debugging.is_python_process + + pid: int + +Check if a process is a Python process. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_is_python_process_impl(PyObject *module, int pid) +/*[clinic end generated code: output=22947dc8afcac362 input=13488e28c7295d84]*/ +{ + proc_handle_t handle; + + if (_Py_RemoteDebug_InitProcHandle(&handle, pid) < 0) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + + uintptr_t runtime_start_address = _Py_RemoteDebug_GetPyRuntimeAddress(&handle); + _Py_RemoteDebug_CleanupProcHandle(&handle); + + if (runtime_start_address == 0) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + + Py_RETURN_TRUE; +} + +/*[clinic input] +_remote_debugging.get_gc_stats + + pid: int + * + all_interpreters: bool = False + If True, return GC statistics from all interpreters. + If False, return only from main interpreter. + +Get garbage collector statistics from external Python process. + +Returns: + list of GCStatsInfo: A list of stats samples containing: + - gen: GC generation number. + - iid: Interpreter ID. + - ts_start: Raw timestamp at collection start. + - ts_stop: Raw timestamp at collection stop. + - collections: Total number of collections. + - collected: Total number of collected objects. + - uncollectable: Total number of uncollectable objects. + - candidates: Total objects considered and traversed. + - duration: Total collection time, in seconds. + +Raises: + RuntimeError: If the target process cannot be inspected or if its + debug offsets or GC stats layout are incompatible. +[clinic start generated code]*/ + +static PyObject * +_remote_debugging_get_gc_stats_impl(PyObject *module, int pid, + int all_interpreters) +/*[clinic end generated code: output=d9dce5f7add149bb input=a2a08a45a8f0b119]*/ +{ + RuntimeOffsets offsets; + if (init_runtime_offsets(&offsets, pid, /*debug=*/1) < 0) { + return NULL; + } + + RemoteDebuggingState *st = RemoteDebugging_GetState(module); + PyObject *result = get_gc_stats(&offsets, all_interpreters, + st->GCStatsInfo_Type); + + cleanup_runtime_offsets(&offsets); + return result; +} + +static PyMethodDef remote_debugging_methods[] = { + _REMOTE_DEBUGGING_ZSTD_AVAILABLE_METHODDEF + _REMOTE_DEBUGGING_GET_CHILD_PIDS_METHODDEF + _REMOTE_DEBUGGING_IS_PYTHON_PROCESS_METHODDEF + _REMOTE_DEBUGGING_GET_GC_STATS_METHODDEF + {NULL, NULL, 0, NULL}, +}; + +static PyModuleDef_Slot remote_debugging_slots[] = { + _Py_ABI_SLOT, + {Py_mod_exec, _remote_debugging_exec}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0, NULL}, +}; + +static struct PyModuleDef remote_debugging_module = { + PyModuleDef_HEAD_INIT, + .m_name = "_remote_debugging", + .m_size = sizeof(RemoteDebuggingState), + .m_methods = remote_debugging_methods, + .m_slots = remote_debugging_slots, + .m_traverse = remote_debugging_traverse, + .m_clear = remote_debugging_clear, + .m_free = remote_debugging_free, +}; + +PyMODINIT_FUNC +PyInit__remote_debugging(void) +{ + return PyModuleDef_Init(&remote_debugging_module); +} diff --git a/Modules/_remote_debugging/object_reading.c b/Modules/_remote_debugging/object_reading.c new file mode 100644 index 00000000000000..1cea96a2151fcc --- /dev/null +++ b/Modules/_remote_debugging/object_reading.c @@ -0,0 +1,323 @@ +/****************************************************************************** + * Remote Debugging Module - Object Reading Functions + * + * This file contains functions for reading Python objects from remote + * process memory, including strings, bytes, and integers. + ******************************************************************************/ + +#include "_remote_debugging.h" +#include <limits.h> + +/* ============================================================================ + * MEMORY READING FUNCTIONS + * ============================================================================ */ + +#define DEFINE_MEMORY_READER(type_name, c_type, error_msg) \ +int \ +read_##type_name(RemoteUnwinderObject *unwinder, uintptr_t address, c_type *result) \ +{ \ + int res = _Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, address, sizeof(c_type), result); \ + if (res < 0) { \ + set_exception_cause(unwinder, PyExc_RuntimeError, error_msg); \ + return -1; \ + } \ + return 0; \ +} + +DEFINE_MEMORY_READER(ptr, uintptr_t, "Failed to read pointer from remote memory") +DEFINE_MEMORY_READER(Py_ssize_t, Py_ssize_t, "Failed to read Py_ssize_t from remote memory") +DEFINE_MEMORY_READER(char, char, "Failed to read char from remote memory") + +int +read_py_ptr(RemoteUnwinderObject *unwinder, uintptr_t address, uintptr_t *ptr_addr) +{ + if (read_ptr(unwinder, address, ptr_addr)) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read Python pointer"); + return -1; + } + *ptr_addr &= ~Py_TAG_BITS; + return 0; +} + +/* ============================================================================ + * PYTHON OBJECT READING FUNCTIONS + * ============================================================================ */ + +PyObject * +read_py_str( + RemoteUnwinderObject *unwinder, + uintptr_t address, + Py_ssize_t max_len +) { + // Read the entire PyUnicodeObject at once; for short strings the data + // is inline right after the header and we'll already have (some of) it. + char unicode_obj[SIZEOF_UNICODE_OBJ]; + int res = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + address, + SIZEOF_UNICODE_OBJ, + unicode_obj + ); + if (res < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read PyUnicodeObject"); + return NULL; + } + + Py_ssize_t len = GET_MEMBER(Py_ssize_t, unicode_obj, unwinder->debug_offsets.unicode_object.length); + if (len < 0 || len > max_len) { + PyErr_Format(PyExc_RuntimeError, + "Invalid string length (%zd) at 0x%lx", len, address); + set_exception_cause(unwinder, PyExc_RuntimeError, "Invalid string length in remote Unicode object"); + return NULL; + } + + // Inspect state to pick the right data offset and character width. + // We rely on the remote process sharing this Python version's + // PyASCIIObject layout, the same assumption already used for `length`. + struct _PyUnicodeObject_state state = GET_MEMBER( + struct _PyUnicodeObject_state, + unicode_obj, + unwinder->debug_offsets.unicode_object.state); + + if (!state.compact) { + PyErr_Format(PyExc_RuntimeError, + "Cannot read non-compact Unicode object at 0x%lx", address); + set_exception_cause(unwinder, PyExc_RuntimeError, + "Legacy (non-compact) Unicode objects are not supported"); + return NULL; + } + + int kind = (int)state.kind; + Py_UCS4 max_char; + switch (kind) { + case PyUnicode_1BYTE_KIND: + max_char = state.ascii ? 0x7F : 0xFF; + break; + case PyUnicode_2BYTE_KIND: + max_char = 0xFFFF; + break; + case PyUnicode_4BYTE_KIND: + max_char = 0x10FFFF; + break; + default: + PyErr_Format(PyExc_RuntimeError, + "Invalid Unicode kind %d at 0x%lx", kind, address); + set_exception_cause(unwinder, PyExc_RuntimeError, + "Invalid kind in remote Unicode object"); + return NULL; + } + + size_t header_size = state.ascii + ? (size_t)unwinder->debug_offsets.unicode_object.asciiobject_size + : (size_t)unwinder->debug_offsets.unicode_object.compactunicodeobject_size; + + // len * kind is bounded by max_len * 4 (kind <= 4, len <= max_len), so + // the multiplication can't overflow for any caller-sane max_len, but the + // explicit cap here keeps a corrupted remote `length` from later turning + // into a giant allocation. + size_t nbytes = (size_t)len * (size_t)kind; + if ((size_t)len > (SIZE_MAX / 4) || nbytes > (size_t)max_len * 4) { + PyErr_Format(PyExc_RuntimeError, + "Implausible Unicode byte size %zu at 0x%lx", nbytes, address); + set_exception_cause(unwinder, PyExc_RuntimeError, + "Garbage byte size in remote Unicode object"); + return NULL; + } + + PyObject *result = PyUnicode_New(len, max_char); + if (result == NULL) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to allocate PyUnicode for remote string"); + return NULL; + } + if (nbytes == 0) { + return result; + } + + void *data = PyUnicode_DATA(result); + + // Reuse data already present in the header read; only round-trip for + // whatever spills past it. + size_t inline_avail = (header_size < SIZEOF_UNICODE_OBJ) + ? SIZEOF_UNICODE_OBJ - header_size + : 0; + size_t inline_bytes = nbytes < inline_avail ? nbytes : inline_avail; + if (inline_bytes > 0) { + memcpy(data, unicode_obj + header_size, inline_bytes); + } + + if (nbytes > inline_bytes) { + res = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + address + header_size + inline_bytes, + nbytes - inline_bytes, + (char *)data + inline_bytes); + if (res < 0) { + Py_DECREF(result); + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read string data from remote memory"); + return NULL; + } + } + + return result; +} + +PyObject * +read_py_bytes( + RemoteUnwinderObject *unwinder, + uintptr_t address, + Py_ssize_t max_len +) { + PyObject *result = NULL; + char *buf = NULL; + + // Read the entire PyBytesObject at once + char bytes_obj[SIZEOF_BYTES_OBJ]; + int res = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + address, + SIZEOF_BYTES_OBJ, + bytes_obj + ); + if (res < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read PyBytesObject"); + goto err; + } + + Py_ssize_t len = GET_MEMBER(Py_ssize_t, bytes_obj, unwinder->debug_offsets.bytes_object.ob_size); + if (len < 0 || len > max_len) { + PyErr_Format(PyExc_RuntimeError, + "Invalid bytes length (%zd) at 0x%lx", len, address); + set_exception_cause(unwinder, PyExc_RuntimeError, "Invalid bytes length in remote bytes object"); + return NULL; + } + + buf = (char *)PyMem_RawMalloc(len+1); + if (buf == NULL) { + PyErr_NoMemory(); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate buffer for bytes reading"); + return NULL; + } + + size_t offset = (size_t)unwinder->debug_offsets.bytes_object.ob_sval; + res = _Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, address + offset, len, buf); + if (res < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read bytes data from remote memory"); + goto err; + } + buf[len] = '\0'; + + result = PyBytes_FromStringAndSize(buf, len); + if (result == NULL) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create PyBytes from remote bytes data"); + goto err; + } + + PyMem_RawFree(buf); + assert(result != NULL); + return result; + +err: + if (buf != NULL) { + PyMem_RawFree(buf); + } + return NULL; +} + +long +read_py_long( + RemoteUnwinderObject *unwinder, + uintptr_t address +) +{ + unsigned int shift = PYLONG_BITS_IN_DIGIT; + + // Read the entire PyLongObject at once + char long_obj[SIZEOF_LONG_OBJ]; + int bytes_read = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + address, + (size_t)unwinder->debug_offsets.long_object.size, + long_obj); + if (bytes_read < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read PyLongObject"); + return -1; + } + + uintptr_t lv_tag = GET_MEMBER(uintptr_t, long_obj, unwinder->debug_offsets.long_object.lv_tag); + int negative = (lv_tag & 3) == 2; + Py_ssize_t size = lv_tag >> 3; + + if (size == 0) { + return 0; + } + + // Validate size: reject garbage (negative or unreasonably large) + if (size < 0 || size > MAX_LONG_DIGITS) { + PyErr_Format(PyExc_RuntimeError, + "Invalid PyLong digit count: %zd (expected 0-%d)", size, MAX_LONG_DIGITS); + set_exception_cause(unwinder, PyExc_RuntimeError, + "Invalid PyLong size (corrupted remote memory)"); + return -1; + } + + // Calculate how many digits fit inline in our local buffer + Py_ssize_t ob_digit_offset = unwinder->debug_offsets.long_object.ob_digit; + Py_ssize_t inline_digits_space = SIZEOF_LONG_OBJ - ob_digit_offset; + Py_ssize_t max_inline_digits = inline_digits_space / (Py_ssize_t)sizeof(digit); + + digit *digits = (digit *)PyMem_RawMalloc(size * sizeof(digit)); + if (!digits) { + PyErr_NoMemory(); + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate digits for PyLong"); + return -1; + } + + if (size <= max_inline_digits && size <= _PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS) { + memcpy(digits, long_obj + ob_digit_offset, size * sizeof(digit)); + } else { + bytes_read = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + address + (uintptr_t)unwinder->debug_offsets.long_object.ob_digit, + sizeof(digit) * size, + digits + ); + if (bytes_read < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read PyLong digits from remote memory"); + goto error; + } + } + + unsigned long limit = negative + ? (unsigned long)LONG_MAX + 1UL + : (unsigned long)LONG_MAX; + unsigned long value = 0; + + for (Py_ssize_t i = size; i-- > 0;) { + if (digits[i] >= PyLong_BASE) { + PyErr_Format(PyExc_RuntimeError, + "Invalid PyLong digit: %u (base %u)", digits[i], PyLong_BASE); + set_exception_cause(unwinder, PyExc_RuntimeError, + "Invalid PyLong digit (corrupted remote memory)"); + goto error; + } + if (value > ((limit - (unsigned long)digits[i]) >> shift)) { + PyErr_SetString(PyExc_OverflowError, + "Remote PyLong value does not fit in C long"); + set_exception_cause(unwinder, PyExc_OverflowError, + "Remote PyLong value is too large"); + goto error; + } + value = (value << shift) | (unsigned long)digits[i]; + } + PyMem_RawFree(digits); + if (negative) { + if (value == (unsigned long)LONG_MAX + 1UL) { + return LONG_MIN; + } + return -(long)value; + } + return (long)value; +error: + PyMem_RawFree(digits); + return -1; +} diff --git a/Modules/_remote_debugging/subprocess.c b/Modules/_remote_debugging/subprocess.c new file mode 100644 index 00000000000000..cdad75e318be91 --- /dev/null +++ b/Modules/_remote_debugging/subprocess.c @@ -0,0 +1,489 @@ +/****************************************************************************** + * Remote Debugging Module - Subprocess Enumeration + * + * This file contains platform-specific functions for enumerating child + * processes of a given PID. + ******************************************************************************/ + +#include "_remote_debugging.h" + +#ifndef MS_WINDOWS +#include <unistd.h> +#include <dirent.h> +#endif + +#ifdef MS_WINDOWS +#include <tlhelp32.h> +#endif + +/* ============================================================================ + * INTERNAL DATA STRUCTURES + * ============================================================================ */ + +/* Simple dynamic array for collecting PIDs */ +typedef struct { + pid_t *pids; + size_t count; + size_t capacity; +} pid_array_t; + +static int +pid_array_init(pid_array_t *arr) +{ + arr->capacity = 64; + arr->count = 0; + arr->pids = (pid_t *)PyMem_Malloc(arr->capacity * sizeof(pid_t)); + if (arr->pids == NULL) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + +static void +pid_array_cleanup(pid_array_t *arr) +{ + if (arr->pids != NULL) { + PyMem_Free(arr->pids); + arr->pids = NULL; + } + arr->count = 0; + arr->capacity = 0; +} + +static int +pid_array_append(pid_array_t *arr, pid_t pid) +{ + if (arr->count >= arr->capacity) { + /* Check for overflow before multiplication */ + if (arr->capacity > SIZE_MAX / 2) { + PyErr_SetString(PyExc_OverflowError, "PID array capacity overflow"); + return -1; + } + size_t new_capacity = arr->capacity * 2; + /* Check allocation size won't overflow */ + if (new_capacity > SIZE_MAX / sizeof(pid_t)) { + PyErr_SetString(PyExc_OverflowError, "PID array size overflow"); + return -1; + } + pid_t *new_pids = (pid_t *)PyMem_Realloc(arr->pids, new_capacity * sizeof(pid_t)); + if (new_pids == NULL) { + PyErr_NoMemory(); + return -1; + } + arr->pids = new_pids; + arr->capacity = new_capacity; + } + arr->pids[arr->count++] = pid; + return 0; +} + +static int +pid_array_contains(pid_array_t *arr, pid_t pid) +{ + for (size_t i = 0; i < arr->count; i++) { + if (arr->pids[i] == pid) { + return 1; + } + } + return 0; +} + +/* ============================================================================ + * SHARED BFS HELPER + * ============================================================================ */ + +/* Find child PIDs using BFS traversal of the pid->ppid mapping. + * all_pids and ppids must have the same count (parallel arrays). + * Returns 0 on success, -1 on error. */ +static int +find_children_bfs(pid_t target_pid, int recursive, + pid_t *all_pids, pid_t *ppids, size_t pid_count, + pid_array_t *result) +{ + int retval = -1; + pid_array_t to_process = {0}; + + if (pid_array_init(&to_process) < 0) { + goto done; + } + if (pid_array_append(&to_process, target_pid) < 0) { + goto done; + } + + size_t process_idx = 0; + while (process_idx < to_process.count) { + pid_t current_pid = to_process.pids[process_idx++]; + + for (size_t i = 0; i < pid_count; i++) { + if (ppids[i] != current_pid) { + continue; + } + pid_t child_pid = all_pids[i]; + if (pid_array_contains(result, child_pid)) { + continue; + } + if (pid_array_append(result, child_pid) < 0) { + goto done; + } + if (recursive && pid_array_append(&to_process, child_pid) < 0) { + goto done; + } + } + + if (!recursive) { + break; + } + } + + retval = 0; + +done: + pid_array_cleanup(&to_process); + return retval; +} + +/* ============================================================================ + * LINUX IMPLEMENTATION + * ============================================================================ */ + +#if defined(__linux__) + +/* Parse /proc/{pid}/stat to get parent PID */ +static pid_t +get_ppid_linux(pid_t pid) +{ + char stat_path[64]; + char buffer[2048]; + + snprintf(stat_path, sizeof(stat_path), "/proc/%d/stat", (int)pid); + + int fd = open(stat_path, O_RDONLY); + if (fd == -1) { + return -1; + } + + ssize_t n = read(fd, buffer, sizeof(buffer) - 1); + close(fd); + + if (n <= 0) { + return -1; + } + buffer[n] = '\0'; + + /* Find closing paren of comm field - stat format: pid (comm) state ppid ... */ + char *p = strrchr(buffer, ')'); + if (!p) { + return -1; + } + + /* Skip ") " with bounds checking */ + char *end = buffer + n; + p += 2; + if (p >= end) { + return -1; + } + if (*p == ' ') { + p++; + if (p >= end) { + return -1; + } + } + + /* Parse: state ppid */ + char state; + int ppid; + if (sscanf(p, "%c %d", &state, &ppid) != 2) { + return -1; + } + + return (pid_t)ppid; +} + +static int +get_child_pids_platform(pid_t target_pid, int recursive, pid_array_t *result) +{ + int retval = -1; + pid_array_t all_pids = {0}; + pid_array_t ppids = {0}; + DIR *proc_dir = NULL; + + if (pid_array_init(&all_pids) < 0) { + goto done; + } + + if (pid_array_init(&ppids) < 0) { + goto done; + } + + proc_dir = opendir("/proc"); + if (!proc_dir) { + PyErr_SetFromErrnoWithFilename(PyExc_OSError, "/proc"); + goto done; + } + + /* Single pass: collect PIDs and their PPIDs together */ + for (;;) { + errno = 0; + struct dirent *entry = readdir(proc_dir); + if (entry == NULL) { + if (errno != 0) { + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, "/proc", + "Failed to read process directory '/proc': %s", + strerror(err)); + goto done; + } + break; + } + /* Skip non-numeric entries (also skips . and ..) */ + if (entry->d_name[0] < '1' || entry->d_name[0] > '9') { + continue; + } + char *endptr; + long pid_long = strtol(entry->d_name, &endptr, 10); + if (*endptr != '\0' || pid_long <= 0) { + continue; + } + pid_t pid = (pid_t)pid_long; + pid_t ppid = get_ppid_linux(pid); + if (ppid < 0) { + continue; + } + if (pid_array_append(&all_pids, pid) < 0 || + pid_array_append(&ppids, ppid) < 0) { + goto done; + } + } + + if (closedir(proc_dir) != 0) { + int err = errno; + proc_dir = NULL; + _set_debug_oserror_from_errno_with_filename(err, "/proc", + "Failed to close process directory '/proc': %s", + strerror(err)); + goto done; + } + proc_dir = NULL; + + if (find_children_bfs(target_pid, recursive, + all_pids.pids, ppids.pids, all_pids.count, + result) < 0) { + goto done; + } + + retval = 0; + +done: + if (proc_dir) { + closedir(proc_dir); + } + pid_array_cleanup(&all_pids); + pid_array_cleanup(&ppids); + return retval; +} + +#endif /* __linux__ */ + +/* ============================================================================ + * MACOS IMPLEMENTATION + * ============================================================================ */ + +#if defined(__APPLE__) && TARGET_OS_OSX + +#include <libproc.h> +#include <sys/proc_info.h> + +static int +get_child_pids_platform(pid_t target_pid, int recursive, pid_array_t *result) +{ + int retval = -1; + pid_t *pid_list = NULL; + pid_t *ppids = NULL; + + /* Get count of all PIDs */ + int n_pids = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0); + if (n_pids <= 0) { + PyErr_SetString(PyExc_OSError, "Failed to get process count"); + goto done; + } + + /* Allocate buffer for PIDs (add some slack for new processes) */ + int buffer_size = n_pids + 64; + pid_list = (pid_t *)PyMem_Malloc(buffer_size * sizeof(pid_t)); + if (!pid_list) { + PyErr_NoMemory(); + goto done; + } + + /* Get actual PIDs */ + int actual = proc_listpids(PROC_ALL_PIDS, 0, pid_list, buffer_size * sizeof(pid_t)); + if (actual <= 0) { + PyErr_SetString(PyExc_OSError, "Failed to list PIDs"); + goto done; + } + + /* Build pid -> ppid mapping */ + ppids = (pid_t *)PyMem_Malloc(actual * sizeof(pid_t)); + if (!ppids) { + PyErr_NoMemory(); + goto done; + } + + /* Get parent PIDs for each process */ + int valid_count = 0; + for (int i = 0; i < actual; i++) { + struct proc_bsdinfo proc_info; + int ret = proc_pidinfo(pid_list[i], PROC_PIDTBSDINFO, 0, + &proc_info, sizeof(proc_info)); + if (ret != sizeof(proc_info)) { + continue; + } + pid_list[valid_count] = pid_list[i]; + ppids[valid_count] = proc_info.pbi_ppid; + valid_count++; + } + + if (find_children_bfs(target_pid, recursive, + pid_list, ppids, valid_count, + result) < 0) { + goto done; + } + + retval = 0; + +done: + PyMem_Free(pid_list); + PyMem_Free(ppids); + return retval; +} + +#endif /* __APPLE__ && TARGET_OS_OSX */ + +/* ============================================================================ + * WINDOWS IMPLEMENTATION + * ============================================================================ */ + +#ifdef MS_WINDOWS + +static int +get_child_pids_platform(pid_t target_pid, int recursive, pid_array_t *result) +{ + int retval = -1; + pid_array_t all_pids = {0}; + pid_array_t ppids = {0}; + HANDLE snapshot = INVALID_HANDLE_VALUE; + + snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) { + DWORD error = GetLastError(); + PyErr_SetFromWindowsErr(error); + goto done; + } + + if (pid_array_init(&all_pids) < 0) { + goto done; + } + + if (pid_array_init(&ppids) < 0) { + goto done; + } + + /* Single pass: collect PIDs and PPIDs together */ + PROCESSENTRY32 pe; + pe.dwSize = sizeof(PROCESSENTRY32); + if (!Process32First(snapshot, &pe)) { + DWORD error = GetLastError(); + PyErr_SetFromWindowsErr(error); + goto done; + } + + do { + if (pid_array_append(&all_pids, (pid_t)pe.th32ProcessID) < 0 || + pid_array_append(&ppids, (pid_t)pe.th32ParentProcessID) < 0) { + goto done; + } + } while (Process32Next(snapshot, &pe)); + + DWORD error = GetLastError(); + if (error != ERROR_NO_MORE_FILES) { + PyErr_SetFromWindowsErr(error); + goto done; + } + + CloseHandle(snapshot); + snapshot = INVALID_HANDLE_VALUE; + + if (find_children_bfs(target_pid, recursive, + all_pids.pids, ppids.pids, all_pids.count, + result) < 0) { + goto done; + } + + retval = 0; + +done: + if (snapshot != INVALID_HANDLE_VALUE) { + CloseHandle(snapshot); + } + pid_array_cleanup(&all_pids); + pid_array_cleanup(&ppids); + return retval; +} + +#endif /* MS_WINDOWS */ + +/* ============================================================================ + * UNSUPPORTED PLATFORM STUB + * ============================================================================ */ + +#if !defined(__linux__) && !(defined(__APPLE__) && TARGET_OS_OSX) && !defined(MS_WINDOWS) + +static int +get_child_pids_platform(pid_t target_pid, int recursive, pid_array_t *result) +{ + PyErr_SetString(PyExc_NotImplementedError, + "Subprocess enumeration not supported on this platform"); + return -1; +} + +#endif + +/* ============================================================================ + * PUBLIC API + * ============================================================================ */ + +PyObject * +enumerate_child_pids(pid_t target_pid, int recursive) +{ + pid_array_t result; + + if (pid_array_init(&result) < 0) { + return NULL; + } + + if (get_child_pids_platform(target_pid, recursive, &result) < 0) { + pid_array_cleanup(&result); + return NULL; + } + + /* Convert to Python list */ + PyObject *list = PyList_New(result.count); + if (list == NULL) { + pid_array_cleanup(&result); + return NULL; + } + + for (size_t i = 0; i < result.count; i++) { + PyObject *pid_obj = PyLong_FromLong((long)result.pids[i]); + if (pid_obj == NULL) { + Py_DECREF(list); + pid_array_cleanup(&result); + return NULL; + } + PyList_SET_ITEM(list, i, pid_obj); + } + + pid_array_cleanup(&result); + return list; +} diff --git a/Modules/_remote_debugging/threads.c b/Modules/_remote_debugging/threads.c new file mode 100644 index 00000000000000..81735e85395ac9 --- /dev/null +++ b/Modules/_remote_debugging/threads.c @@ -0,0 +1,918 @@ +/****************************************************************************** + * Remote Debugging Module - Thread Functions + * + * This file contains functions for iterating threads and determining + * thread status in remote process memory. + ******************************************************************************/ + +#include "_remote_debugging.h" + +#ifndef MS_WINDOWS +#include <unistd.h> +#endif + +#ifdef __linux__ +#include <dirent.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#endif + +/* ============================================================================ + * THREAD ITERATION FUNCTIONS + * ============================================================================ */ + +int +iterate_threads( + RemoteUnwinderObject *unwinder, + thread_processor_func processor, + void *context +) { + uintptr_t thread_state_addr; + unsigned long tid = 0; + const size_t MAX_THREADS = 8192; + size_t thread_count = 0; + + if (0 > _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + unwinder->interpreter_addr + (uintptr_t)unwinder->debug_offsets.interpreter_state.threads_head, + sizeof(void*), + &thread_state_addr)) + { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read threads head"); + return -1; + } + + while (thread_state_addr != 0 && thread_count < MAX_THREADS) { + thread_count++; + if (0 > _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + thread_state_addr + (uintptr_t)unwinder->debug_offsets.thread_state.native_thread_id, + sizeof(tid), + &tid)) + { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read thread ID"); + return -1; + } + + // Call the processor function for this thread + if (processor(unwinder, thread_state_addr, tid, context) < 0) { + return -1; + } + + // Move to next thread + if (0 > _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + thread_state_addr + (uintptr_t)unwinder->debug_offsets.thread_state.next, + sizeof(void*), + &thread_state_addr)) + { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read next thread state"); + return -1; + } + } + + return 0; +} + +/* ============================================================================ + * INTERPRETER STATE AND THREAD DISCOVERY FUNCTIONS + * ============================================================================ */ + +int +populate_initial_state_data( + int all_threads, + RemoteUnwinderObject *unwinder, + uintptr_t runtime_start_address, + uintptr_t *interpreter_state, + uintptr_t *tstate +) { + uintptr_t interpreter_state_list_head = + (uintptr_t)unwinder->debug_offsets.runtime_state.interpreters_head; + + uintptr_t address_of_interpreter_state; + int bytes_read = _Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + runtime_start_address + interpreter_state_list_head, + sizeof(void*), + &address_of_interpreter_state); + if (bytes_read < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read interpreter state address"); + return -1; + } + + if (address_of_interpreter_state == 0) { + PyErr_SetString(PyExc_RuntimeError, "No interpreter state found"); + set_exception_cause(unwinder, PyExc_RuntimeError, "Interpreter state is NULL"); + return -1; + } + + *interpreter_state = address_of_interpreter_state; + + if (all_threads) { + *tstate = 0; + return 0; + } + + uintptr_t address_of_thread = address_of_interpreter_state + + (uintptr_t)unwinder->debug_offsets.interpreter_state.threads_main; + + if (_Py_RemoteDebug_PagedReadRemoteMemory( + &unwinder->handle, + address_of_thread, + sizeof(void*), + tstate) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read main thread state address"); + return -1; + } + + return 0; +} + +int +find_running_frame( + RemoteUnwinderObject *unwinder, + uintptr_t address_of_thread, + uintptr_t *frame +) { + if ((void*)address_of_thread != NULL) { + int err = read_ptr( + unwinder, + address_of_thread + (uintptr_t)unwinder->debug_offsets.thread_state.current_frame, + frame); + if (err) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read current frame pointer"); + return -1; + } + return 0; + } + + *frame = (uintptr_t)NULL; + return 0; +} + +/* ============================================================================ + * THREAD STATUS FUNCTIONS + * ============================================================================ */ + +int +get_thread_status(RemoteUnwinderObject *unwinder, uint64_t tid, uint64_t pthread_id) { +#if defined(__APPLE__) && TARGET_OS_OSX + if (!unwinder->thread_id_offset_initialized) { + uint64_t *tids = (uint64_t *)PyMem_Malloc(MAX_NATIVE_THREADS * sizeof(uint64_t)); + if (!tids) { + // Non-fatal: thread status is best-effort + return THREAD_STATE_UNKNOWN; + } + int n = proc_pidinfo(unwinder->handle.pid, PROC_PIDLISTTHREADS, 0, tids, MAX_NATIVE_THREADS * sizeof(uint64_t)) / sizeof(uint64_t); + if (n <= 0) { + PyMem_Free(tids); + return THREAD_STATE_UNKNOWN; + } + uint64_t min_offset = UINT64_MAX; + for (int i = 0; i < n; i++) { + uint64_t offset = tids[i] - pthread_id; + if (offset < min_offset) { + min_offset = offset; + } + } + unwinder->thread_id_offset = min_offset; + unwinder->thread_id_offset_initialized = 1; + PyMem_Free(tids); + } + struct proc_threadinfo ti; + uint64_t tid_with_offset = pthread_id + unwinder->thread_id_offset; + if (proc_pidinfo(unwinder->handle.pid, PROC_PIDTHREADINFO, tid_with_offset, &ti, sizeof(ti)) != sizeof(ti)) { + return THREAD_STATE_UNKNOWN; + } + if (ti.pth_run_state == TH_STATE_RUNNING) { + return THREAD_STATE_RUNNING; + } + return THREAD_STATE_IDLE; +#elif defined(__linux__) + char stat_path[256]; + char buffer[2048] = ""; + + snprintf(stat_path, sizeof(stat_path), "/proc/%d/task/%" PRIu64 "/stat", unwinder->handle.pid, tid); + + int fd = open(stat_path, O_RDONLY); + if (fd == -1) { + return THREAD_STATE_UNKNOWN; + } + + if (read(fd, buffer, 2047) == 0) { + close(fd); + return THREAD_STATE_UNKNOWN; + } + close(fd); + + char *p = strchr(buffer, ')'); + if (!p) { + return THREAD_STATE_UNKNOWN; + } + + p += 2; // Skip ") " + if (*p == ' ') { + p++; + } + + switch (*p) { + case 'R': // Running + return THREAD_STATE_RUNNING; + case 'S': // Interruptible sleep + case 'D': // Uninterruptible sleep + case 'T': // Stopped + case 'Z': // Zombie + case 'I': // Idle kernel thread + return THREAD_STATE_IDLE; + default: + return THREAD_STATE_UNKNOWN; + } +#elif defined(MS_WINDOWS) + ULONG n; + NTSTATUS status = NtQuerySystemInformation( + SystemProcessInformation, + unwinder->win_process_buffer, + unwinder->win_process_buffer_size, + &n + ); + if (status == STATUS_INFO_LENGTH_MISMATCH) { + // Buffer was too small so we reallocate a larger one and try again. + unwinder->win_process_buffer_size = n; + PVOID new_buffer = PyMem_Realloc(unwinder->win_process_buffer, n); + if (!new_buffer) { + // Match Linux/macOS: degrade gracefully on alloc failure + return THREAD_STATE_UNKNOWN; + } + unwinder->win_process_buffer = new_buffer; + return get_thread_status(unwinder, tid, pthread_id); + } + if (status != STATUS_SUCCESS) { + return THREAD_STATE_UNKNOWN; + } + + SYSTEM_PROCESS_INFORMATION *pi = (SYSTEM_PROCESS_INFORMATION *)unwinder->win_process_buffer; + while ((ULONG)(ULONG_PTR)pi->UniqueProcessId != unwinder->handle.pid) { + if (pi->NextEntryOffset == 0) { + // Process not found (may have exited) + return THREAD_STATE_UNKNOWN; + } + pi = (SYSTEM_PROCESS_INFORMATION *)(((BYTE *)pi) + pi->NextEntryOffset); + } + + SYSTEM_THREAD_INFORMATION *ti = (SYSTEM_THREAD_INFORMATION *)((char *)pi + sizeof(SYSTEM_PROCESS_INFORMATION)); + for (size_t i = 0; i < pi->NumberOfThreads; i++, ti++) { + if (ti->ClientId.UniqueThread == (HANDLE)tid) { + return ti->ThreadState != WIN32_THREADSTATE_RUNNING ? THREAD_STATE_IDLE : THREAD_STATE_RUNNING; + } + } + + // Thread not found (may have exited) + return THREAD_STATE_UNKNOWN; +#else + return THREAD_STATE_UNKNOWN; +#endif +} + +/* ============================================================================ + * STACK UNWINDING FUNCTIONS + * ============================================================================ */ + +typedef struct { + unsigned int initialized:1; + unsigned int bound:1; + unsigned int unbound:1; + unsigned int bound_gilstate:1; + unsigned int active:1; + unsigned int finalizing:1; + unsigned int cleared:1; + unsigned int finalized:1; + unsigned int :24; +} _thread_status; + +static int +read_thread_state_and_maybe_frame( + RemoteUnwinderObject *unwinder, + uintptr_t tstate_addr, + size_t tstate_size, + char *tstate_buffer, + uintptr_t predicted_frame_addr, + char *frame_buffer, + int *frame_read) +{ + *frame_read = 0; + if (predicted_frame_addr != 0) { + _Py_RemoteReadSegment segments[2] = { + {tstate_addr, tstate_buffer, tstate_size}, + {predicted_frame_addr, frame_buffer, SIZEOF_INTERP_FRAME}, + }; + Py_ssize_t nread = _Py_RemoteDebug_BatchedReadRemoteMemory( + &unwinder->handle, segments, 2); + int completed = 0; + if (nread >= (Py_ssize_t)tstate_size) { + completed = 1; + if (nread == (Py_ssize_t)(tstate_size + SIZEOF_INTERP_FRAME)) { + completed = 2; + } + } + STATS_BATCHED_READ(unwinder, 2, completed); + if (completed >= 1) { + *frame_read = completed == 2; + return 0; + } + } + return _Py_RemoteDebug_ReadRemoteMemory( + &unwinder->handle, tstate_addr, tstate_size, tstate_buffer); +} + +PyObject* +unwind_stack_for_thread( + RemoteUnwinderObject *unwinder, + uintptr_t *current_tstate, + uintptr_t gil_holder_tstate, + uintptr_t gc_frame, + uintptr_t main_thread_tstate, + const RemoteReadPrefetch *prefetch +) { + PyObject *frame_info = NULL; + PyObject *thread_id = NULL; + PyObject *result = NULL; + StackChunkList chunks = {0}; + + char local_ts[SIZEOF_THREAD_STATE]; + char local_prefetched_frame[SIZEOF_INTERP_FRAME]; + const char *ts; + RemoteReadPrefetch ctx_prefetch = {0}; + if (prefetch->tstate && prefetch->tstate_addr == *current_tstate) { + ts = prefetch->tstate; + if (prefetch->frame) { + ctx_prefetch.frame = prefetch->frame; + ctx_prefetch.frame_addr = prefetch->frame_addr; + } + } + else if (unwinder->cache_frames) { + uintptr_t predicted_frame_addr = 0; + int have_prefetched_frame = 0; + FrameCacheEntry *entry = frame_cache_find_by_tstate(unwinder, *current_tstate); + if (entry && entry->num_addrs > 0) { + predicted_frame_addr = entry->addrs[0]; + } + + int rc = read_thread_state_and_maybe_frame( + unwinder, + *current_tstate, + (size_t)unwinder->debug_offsets.thread_state.size, + local_ts, + predicted_frame_addr, + local_prefetched_frame, + &have_prefetched_frame); + if (rc < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read thread state"); + goto error; + } + ts = local_ts; + if (have_prefetched_frame) { + ctx_prefetch.frame = local_prefetched_frame; + ctx_prefetch.frame_addr = predicted_frame_addr; + } + } + else { + int rc = _Py_RemoteDebug_ReadRemoteMemory( + &unwinder->handle, + *current_tstate, + (size_t)unwinder->debug_offsets.thread_state.size, + local_ts); + if (rc < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read thread state"); + goto error; + } + ts = local_ts; + } + STATS_INC(unwinder, memory_reads); + STATS_ADD(unwinder, memory_bytes_read, unwinder->debug_offsets.thread_state.size); + if (ctx_prefetch.frame) { + STATS_INC(unwinder, memory_reads); + STATS_ADD(unwinder, memory_bytes_read, SIZEOF_INTERP_FRAME); + } + + long tid = GET_MEMBER(long, ts, unwinder->debug_offsets.thread_state.native_thread_id); + + // Calculate thread status using flags (always) + int status_flags = 0; + + // Check GIL status + int has_gil = 0; + int gil_requested = 0; +#ifdef Py_GIL_DISABLED + int active = GET_MEMBER(_thread_status, ts, unwinder->debug_offsets.thread_state.status).active; + has_gil = active; + (void)gil_requested; // unused +#else + // Read holds_gil directly from thread state + has_gil = GET_MEMBER(int, ts, unwinder->debug_offsets.thread_state.holds_gil); + + // Check if thread is actively requesting the GIL + if (unwinder->debug_offsets.thread_state.gil_requested != 0) { + gil_requested = GET_MEMBER(int, ts, unwinder->debug_offsets.thread_state.gil_requested); + } + + // Set GIL_REQUESTED flag if thread is waiting + if (!has_gil && gil_requested) { + status_flags |= THREAD_STATUS_GIL_REQUESTED; + } +#endif + if (has_gil) { + status_flags |= THREAD_STATUS_HAS_GIL; + // gh-142207 for remote debugging. + gil_requested = 0; + } + + // Check exception state (both raised and handled exceptions) + int has_exception = 0; + + // Check current_exception (exception being raised/propagated) + uintptr_t current_exception = GET_MEMBER(uintptr_t, ts, + unwinder->debug_offsets.thread_state.current_exception); + if (current_exception != 0) { + has_exception = 1; + } + + // Check exc_state.exc_value (exception being handled in except block) + // exc_state is embedded in PyThreadState, so we read it directly from + // the thread state buffer. This catches most cases; nested exception + // handlers where exc_info points elsewhere are rare. + if (!has_exception) { + uintptr_t exc_value = GET_MEMBER(uintptr_t, ts, + unwinder->debug_offsets.thread_state.exc_state + + unwinder->debug_offsets.err_stackitem.exc_value); + if (exc_value != 0) { + has_exception = 1; + } + } + + if (has_exception) { + status_flags |= THREAD_STATUS_HAS_EXCEPTION; + } + + // Check CPU status + long pthread_id = GET_MEMBER(long, ts, unwinder->debug_offsets.thread_state.thread_id); + + // Optimization: only check CPU status if needed by mode because it's expensive + int cpu_status = THREAD_STATE_UNKNOWN; + if (unwinder->mode == PROFILING_MODE_CPU || unwinder->mode == PROFILING_MODE_ALL) { + cpu_status = get_thread_status(unwinder, tid, pthread_id); + } + + if (cpu_status == THREAD_STATE_UNKNOWN) { + status_flags |= THREAD_STATUS_UNKNOWN; + } else if (cpu_status == THREAD_STATE_RUNNING) { + status_flags |= THREAD_STATUS_ON_CPU; + } + + if (*current_tstate == main_thread_tstate) { + status_flags |= THREAD_STATUS_MAIN_THREAD; + } + + // Check if we should skip this thread based on mode + int should_skip = 0; + if (unwinder->skip_non_matching_threads) { + if (unwinder->mode == PROFILING_MODE_CPU) { + // Skip if not on CPU + should_skip = !(status_flags & THREAD_STATUS_ON_CPU); + } else if (unwinder->mode == PROFILING_MODE_GIL) { + // Skip if doesn't have GIL + should_skip = !(status_flags & THREAD_STATUS_HAS_GIL); + } else if (unwinder->mode == PROFILING_MODE_EXCEPTION) { + // Skip if thread doesn't have an exception active + should_skip = !(status_flags & THREAD_STATUS_HAS_EXCEPTION); + } + // PROFILING_MODE_WALL and PROFILING_MODE_ALL never skip + } + + if (should_skip) { + // Advance to next thread and return NULL to skip processing + *current_tstate = GET_MEMBER(uintptr_t, ts, unwinder->debug_offsets.thread_state.next); + return NULL; + } + + uintptr_t frame_addr = GET_MEMBER(uintptr_t, ts, unwinder->debug_offsets.thread_state.current_frame); + uintptr_t base_frame_addr = GET_MEMBER(uintptr_t, ts, unwinder->debug_offsets.thread_state.base_frame); + + frame_info = PyList_New(0); + if (!frame_info) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create frame info list"); + goto error; + } + + // In cache mode, copying stack chunks is more expensive than direct memory reads + if (!unwinder->cache_frames) { + if (copy_stack_chunks(unwinder, *current_tstate, &chunks) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to copy stack chunks"); + goto error; + } + } + + uintptr_t addrs[FRAME_CACHE_MAX_FRAMES]; + FrameWalkContext ctx = { + .frame_addr = frame_addr, + .thread_state_addr = *current_tstate, + .base_frame_addr = base_frame_addr, + .gc_frame = gc_frame, + .chunks = &chunks, + .prefetch = ctx_prefetch, + .frame_info = frame_info, + .frame_addrs = addrs, + .num_addrs = 0, + .max_addrs = FRAME_CACHE_MAX_FRAMES, + }; + assert(ctx.max_addrs == FRAME_CACHE_MAX_FRAMES); + + if (unwinder->cache_frames) { + // Use cache to avoid re-reading unchanged parent frames + ctx.last_profiled_frame = GET_MEMBER(uintptr_t, ts, + unwinder->debug_offsets.thread_state.last_profiled_frame); + if (collect_frames_with_cache(unwinder, &ctx, tid) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to collect frames"); + goto error; + } + // Update last_profiled_frame for next sample if it changed + if (frame_addr != ctx.last_profiled_frame) { + uintptr_t lpf_addr = + *current_tstate + (uintptr_t)unwinder->debug_offsets.thread_state.last_profiled_frame; + if (_Py_RemoteDebug_WriteRemoteMemory(&unwinder->handle, lpf_addr, + sizeof(uintptr_t), &frame_addr) < 0) { + PyErr_Clear(); // Non-fatal + } + } + } else { + // No caching - process entire frame chain with base_frame validation + if (process_frame_chain(unwinder, &ctx) < 0) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to process frame chain"); + goto error; + } + } + + *current_tstate = GET_MEMBER(uintptr_t, ts, unwinder->debug_offsets.thread_state.next); + + if (unwinder->cache_frames) { + FrameCacheEntry *entry = frame_cache_find(unwinder, (uint64_t)tid); + if (entry && entry->thread_id_obj) { + thread_id = Py_NewRef(entry->thread_id_obj); + } + } + if (thread_id == NULL) { + thread_id = PyLong_FromLongLong(tid); + if (thread_id == NULL) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create thread ID"); + goto error; + } + } + + RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); + result = PyStructSequence_New(state->ThreadInfo_Type); + if (result == NULL) { + set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create ThreadInfo"); + goto error; + } + + // Always use status_flags + PyObject *py_status = PyLong_FromLong(status_flags); + if (py_status == NULL) { + set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create thread status"); + goto error; + } + + // py_status contains status flags (bitfield) + PyStructSequence_SetItem(result, 0, thread_id); + PyStructSequence_SetItem(result, 1, py_status); // Steals reference + PyStructSequence_SetItem(result, 2, frame_info); // Steals reference + + cleanup_stack_chunks(&chunks); + return result; + +error: + Py_XDECREF(frame_info); + Py_XDECREF(thread_id); + Py_XDECREF(result); + cleanup_stack_chunks(&chunks); + return NULL; +} + +/* ============================================================================ + * PROCESS STOP FUNCTIONS + * ============================================================================ */ + +#if defined(__APPLE__) && TARGET_OS_OSX + +void +_Py_RemoteDebug_InitThreadsState(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + st->task = MACH_PORT_NULL; + st->suspended = 0; +} + +int +_Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + kern_return_t kr = task_suspend(unwinder->handle.task); + if (kr != KERN_SUCCESS) { + if (kr == MACH_SEND_INVALID_DEST) { + PyErr_Format(PyExc_ProcessLookupError, + "Process %d has terminated", unwinder->handle.pid); + } else { + PyErr_Format(PyExc_RuntimeError, + "task_suspend failed for PID %d: kern_return_t %d", + unwinder->handle.pid, kr); + } + return -1; + } + + st->task = unwinder->handle.task; + st->suspended = 1; + _Py_RemoteDebug_ClearCache(&unwinder->handle); + return 0; +} + +void +_Py_RemoteDebug_ResumeAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + if (!st->suspended || st->task == MACH_PORT_NULL) { + return; + } + task_resume(st->task); + st->task = MACH_PORT_NULL; + st->suspended = 0; +} + +#elif defined(__linux__) + +void +_Py_RemoteDebug_InitThreadsState(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + st->tids = NULL; + st->count = 0; +} + +static int +read_thread_ids(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + char task_path[64]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", unwinder->handle.pid); + + DIR *dir = opendir(task_path); + if (dir == NULL) { + _Py_RemoteDebug_InitThreadsState(unwinder, st); + if (errno == ENOENT || errno == ESRCH) { + PyErr_Format(PyExc_ProcessLookupError, + "Process %d has terminated", unwinder->handle.pid); + } else { + PyErr_SetFromErrnoWithFilename(PyExc_OSError, task_path); + } + return -1; + } + + st->count = 0; + + for (;;) { + errno = 0; + struct dirent *entry = readdir(dir); + if (entry == NULL) { + if (errno != 0) { + int err = errno; + closedir(dir); + _Py_RemoteDebug_InitThreadsState(unwinder, st); + _set_debug_oserror_from_errno_with_filename(err, task_path, + "Failed to read process task directory '%s': %s", + task_path, strerror(err)); + return -1; + } + break; + } + if (entry->d_name[0] < '1' || entry->d_name[0] > '9') { + continue; + } + char *endptr; + long tid = strtol(entry->d_name, &endptr, 10); + if (*endptr != '\0' || tid <= 0) { + continue; + } + if (st->count >= unwinder->thread_tids_capacity) { + size_t new_cap = unwinder->thread_tids_capacity == 0 ? 64 : unwinder->thread_tids_capacity * 2; + pid_t *new_tids = PyMem_RawRealloc(unwinder->thread_tids, new_cap * sizeof(pid_t)); + if (new_tids == NULL) { + closedir(dir); + _Py_RemoteDebug_InitThreadsState(unwinder, st); + PyErr_NoMemory(); + return -1; + } + unwinder->thread_tids = new_tids; + unwinder->thread_tids_capacity = new_cap; + } + unwinder->thread_tids[st->count++] = (pid_t)tid; + } + + if (closedir(dir) != 0) { + int err = errno; + _Py_RemoteDebug_InitThreadsState(unwinder, st); + _set_debug_oserror_from_errno_with_filename(err, task_path, + "Failed to close process task directory '%s': %s", + task_path, strerror(err)); + return -1; + } + st->tids = unwinder->thread_tids; + return 0; +} + +static inline void +detach_threads(_Py_RemoteDebug_ThreadsState *st, size_t up_to) +{ + for (size_t j = 0; j < up_to; j++) { + ptrace(PTRACE_DETACH, st->tids[j], NULL, NULL); + } +} + +static int +seize_thread(pid_t tid, int *err) +{ + if (ptrace(PTRACE_SEIZE, tid, NULL, 0) == 0) { + return 0; + } + *err = errno; + if (*err == ESRCH) { + return 1; // Thread gone, skip + } + if (*err == EPERM) { + // Thread may have exited, be in a special state, or already be traced. + // Skip rather than fail - this avoids endless retry loops when + // threads transiently become inaccessible. + return 1; + } + if (*err == EINVAL || *err == EIO) { + // Fallback for older kernels + if (ptrace(PTRACE_ATTACH, tid, NULL, NULL) == 0) { + int status; + waitpid(tid, &status, __WALL); + return 0; + } + *err = errno; + if (*err == ESRCH || *err == EPERM) { + return 1; // Thread gone or inaccessible + } + } + return -1; // Real error +} + +int +_Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + if (read_thread_ids(unwinder, st) < 0) { + return -1; + } + + size_t n_tids = st->count; + size_t seized = 0; + for (size_t i = 0; i < n_tids; i++) { + pid_t tid = st->tids[i]; + + int err = 0; + int ret = seize_thread(tid, &err); + if (ret == 1) { + continue; // Thread gone, skip + } + if (ret < 0) { + detach_threads(st, seized); + _set_debug_oserror_from_errno(err, + "Failed to seize thread %d: %s", tid, strerror(err)); + _Py_RemoteDebug_InitThreadsState(unwinder, st); + return -1; + } + st->tids[seized++] = tid; + + if (ptrace(PTRACE_INTERRUPT, tid, NULL, NULL) == -1) { + err = errno; + if (err != ESRCH) { + detach_threads(st, seized); + _set_debug_oserror_from_errno(err, + "Failed to interrupt thread %d: %s", tid, strerror(err)); + _Py_RemoteDebug_InitThreadsState(unwinder, st); + return -1; + } + } + + int status; + if (waitpid(tid, &status, __WALL) == -1) { + err = errno; + if (err != ECHILD && err != ESRCH) { + detach_threads(st, seized); + _set_debug_oserror_from_errno(err, + "waitpid failed for thread %d: %s", tid, strerror(err)); + _Py_RemoteDebug_InitThreadsState(unwinder, st); + return -1; + } + } + } + + st->count = seized; + return 0; +} + +void +_Py_RemoteDebug_ResumeAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + if (st->tids == NULL || st->count == 0) { + return; + } + detach_threads(st, st->count); + st->tids = NULL; + st->count = 0; +} + +#elif defined(MS_WINDOWS) + +void +_Py_RemoteDebug_InitThreadsState(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + st->hProcess = NULL; + st->suspended = 0; +} + +int +_Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + static NtSuspendProcessFunc pNtSuspendProcess = NULL; + static int tried_load = 0; + + if (!tried_load) { + HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); + if (hNtdll) { + pNtSuspendProcess = (NtSuspendProcessFunc)GetProcAddress(hNtdll, "NtSuspendProcess"); + } + tried_load = 1; + } + + if (pNtSuspendProcess == NULL) { + PyErr_SetString(PyExc_RuntimeError, "NtSuspendProcess not available"); + return -1; + } + + NTSTATUS status = pNtSuspendProcess(unwinder->handle.hProcess); + if (status >= 0) { + st->hProcess = unwinder->handle.hProcess; + st->suspended = 1; + _Py_RemoteDebug_ClearCache(&unwinder->handle); + return 0; + } + + PyErr_Format(PyExc_RuntimeError, "NtSuspendProcess failed: 0x%lx", status); + return -1; +} + +void +_Py_RemoteDebug_ResumeAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + if (!st->suspended || st->hProcess == NULL) { + return; + } + + static NtResumeProcessFunc pNtResumeProcess = NULL; + static int tried_load = 0; + + if (!tried_load) { + HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); + if (hNtdll) { + pNtResumeProcess = (NtResumeProcessFunc)GetProcAddress(hNtdll, "NtResumeProcess"); + } + tried_load = 1; + } + + if (pNtResumeProcess != NULL) { + pNtResumeProcess(st->hProcess); + } + st->hProcess = NULL; + st->suspended = 0; +} + +#else + +void +_Py_RemoteDebug_InitThreadsState(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + (void)unwinder; + (void)st; +} + +int +_Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + (void)unwinder; + (void)st; + return 0; +} + +void +_Py_RemoteDebug_ResumeAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st) +{ + (void)unwinder; + (void)st; +} + +#endif diff --git a/Modules/_remote_debugging_module.c b/Modules/_remote_debugging_module.c deleted file mode 100644 index b50e5e403a1a19..00000000000000 --- a/Modules/_remote_debugging_module.c +++ /dev/null @@ -1,3105 +0,0 @@ -/****************************************************************************** - * Python Remote Debugging Module - * - * This module provides functionality to debug Python processes remotely by - * reading their memory and reconstructing stack traces and asyncio task states. - ******************************************************************************/ - -#define _GNU_SOURCE - -/* ============================================================================ - * HEADERS AND INCLUDES - * ============================================================================ */ - -#include <errno.h> -#include <fcntl.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#ifndef Py_BUILD_CORE_BUILTIN -# define Py_BUILD_CORE_MODULE 1 -#endif -#include "Python.h" -#include <internal/pycore_debug_offsets.h> // _Py_DebugOffsets -#include <internal/pycore_frame.h> // FRAME_SUSPENDED_YIELD_FROM -#include <internal/pycore_interpframe.h> // FRAME_OWNED_BY_CSTACK -#include <internal/pycore_llist.h> // struct llist_node -#include <internal/pycore_stackref.h> // Py_TAG_BITS -#include "../Python/remote_debug.h" - -#ifndef HAVE_PROCESS_VM_READV -# define HAVE_PROCESS_VM_READV 0 -#endif - -/* ============================================================================ - * TYPE DEFINITIONS AND STRUCTURES - * ============================================================================ */ - -#define GET_MEMBER(type, obj, offset) (*(type*)((char*)(obj) + (offset))) -#define CLEAR_PTR_TAG(ptr) (((uintptr_t)(ptr) & ~Py_TAG_BITS)) -#define GET_MEMBER_NO_TAG(type, obj, offset) (type)(CLEAR_PTR_TAG(*(type*)((char*)(obj) + (offset)))) - -/* Size macros for opaque buffers */ -#define SIZEOF_BYTES_OBJ sizeof(PyBytesObject) -#define SIZEOF_CODE_OBJ sizeof(PyCodeObject) -#define SIZEOF_GEN_OBJ sizeof(PyGenObject) -#define SIZEOF_INTERP_FRAME sizeof(_PyInterpreterFrame) -#define SIZEOF_LLIST_NODE sizeof(struct llist_node) -#define SIZEOF_PAGE_CACHE_ENTRY sizeof(page_cache_entry_t) -#define SIZEOF_PYOBJECT sizeof(PyObject) -#define SIZEOF_SET_OBJ sizeof(PySetObject) -#define SIZEOF_TASK_OBJ 4096 -#define SIZEOF_THREAD_STATE sizeof(PyThreadState) -#define SIZEOF_TYPE_OBJ sizeof(PyTypeObject) -#define SIZEOF_UNICODE_OBJ sizeof(PyUnicodeObject) -#define SIZEOF_LONG_OBJ sizeof(PyLongObject) - -// Calculate the minimum buffer size needed to read interpreter state fields -// We need to read code_object_generation and potentially tlbc_generation -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif - -#ifdef Py_GIL_DISABLED -#define INTERP_STATE_MIN_SIZE MAX(MAX(MAX(offsetof(PyInterpreterState, _code_object_generation) + sizeof(uint64_t), \ - offsetof(PyInterpreterState, tlbc_indices.tlbc_generation) + sizeof(uint32_t)), \ - offsetof(PyInterpreterState, threads.head) + sizeof(void*)), \ - offsetof(PyInterpreterState, _gil.last_holder) + sizeof(PyThreadState*)) -#else -#define INTERP_STATE_MIN_SIZE MAX(MAX(offsetof(PyInterpreterState, _code_object_generation) + sizeof(uint64_t), \ - offsetof(PyInterpreterState, threads.head) + sizeof(void*)), \ - offsetof(PyInterpreterState, _gil.last_holder) + sizeof(PyThreadState*)) -#endif -#define INTERP_STATE_BUFFER_SIZE MAX(INTERP_STATE_MIN_SIZE, 256) - - - -// Copied from Modules/_asynciomodule.c because it's not exported - -struct _Py_AsyncioModuleDebugOffsets { - struct _asyncio_task_object { - uint64_t size; - uint64_t task_name; - uint64_t task_awaited_by; - uint64_t task_is_task; - uint64_t task_awaited_by_is_set; - uint64_t task_coro; - uint64_t task_node; - } asyncio_task_object; - struct _asyncio_interpreter_state { - uint64_t size; - uint64_t asyncio_tasks_head; - } asyncio_interpreter_state; - struct _asyncio_thread_state { - uint64_t size; - uint64_t asyncio_running_loop; - uint64_t asyncio_running_task; - uint64_t asyncio_tasks_head; - } asyncio_thread_state; -}; - -/* ============================================================================ - * STRUCTSEQ TYPE DEFINITIONS - * ============================================================================ */ - -// TaskInfo structseq type - replaces 4-tuple (task_id, task_name, coroutine_stack, awaited_by) -static PyStructSequence_Field TaskInfo_fields[] = { - {"task_id", "Task ID (memory address)"}, - {"task_name", "Task name"}, - {"coroutine_stack", "Coroutine call stack"}, - {"awaited_by", "Tasks awaiting this task"}, - {NULL} -}; - -static PyStructSequence_Desc TaskInfo_desc = { - "_remote_debugging.TaskInfo", - "Information about an asyncio task", - TaskInfo_fields, - 4 -}; - -// FrameInfo structseq type - replaces 3-tuple (filename, lineno, funcname) -static PyStructSequence_Field FrameInfo_fields[] = { - {"filename", "Source code filename"}, - {"lineno", "Line number"}, - {"funcname", "Function name"}, - {NULL} -}; - -static PyStructSequence_Desc FrameInfo_desc = { - "_remote_debugging.FrameInfo", - "Information about a frame", - FrameInfo_fields, - 3 -}; - -// CoroInfo structseq type - replaces 2-tuple (call_stack, task_name) -static PyStructSequence_Field CoroInfo_fields[] = { - {"call_stack", "Coroutine call stack"}, - {"task_name", "Task name"}, - {NULL} -}; - -static PyStructSequence_Desc CoroInfo_desc = { - "_remote_debugging.CoroInfo", - "Information about a coroutine", - CoroInfo_fields, - 2 -}; - -// ThreadInfo structseq type - replaces 2-tuple (thread_id, frame_info) -static PyStructSequence_Field ThreadInfo_fields[] = { - {"thread_id", "Thread ID"}, - {"frame_info", "Frame information"}, - {NULL} -}; - -static PyStructSequence_Desc ThreadInfo_desc = { - "_remote_debugging.ThreadInfo", - "Information about a thread", - ThreadInfo_fields, - 2 -}; - -// AwaitedInfo structseq type - replaces 2-tuple (tid, awaited_by_list) -static PyStructSequence_Field AwaitedInfo_fields[] = { - {"thread_id", "Thread ID"}, - {"awaited_by", "List of tasks awaited by this thread"}, - {NULL} -}; - -static PyStructSequence_Desc AwaitedInfo_desc = { - "_remote_debugging.AwaitedInfo", - "Information about what a thread is awaiting", - AwaitedInfo_fields, - 2 -}; - -typedef struct { - PyObject *func_name; - PyObject *file_name; - int first_lineno; - PyObject *linetable; // bytes - uintptr_t addr_code_adaptive; -} CachedCodeMetadata; - -typedef struct { - /* Types */ - PyTypeObject *RemoteDebugging_Type; - PyTypeObject *TaskInfo_Type; - PyTypeObject *FrameInfo_Type; - PyTypeObject *CoroInfo_Type; - PyTypeObject *ThreadInfo_Type; - PyTypeObject *AwaitedInfo_Type; -} RemoteDebuggingState; - -typedef struct { - PyObject_HEAD - proc_handle_t handle; - uintptr_t runtime_start_address; - struct _Py_DebugOffsets debug_offsets; - int async_debug_offsets_available; - struct _Py_AsyncioModuleDebugOffsets async_debug_offsets; - uintptr_t interpreter_addr; - uintptr_t tstate_addr; - uint64_t code_object_generation; - _Py_hashtable_t *code_object_cache; - int debug; - int only_active_thread; - RemoteDebuggingState *cached_state; // Cached module state -#ifdef Py_GIL_DISABLED - // TLBC cache invalidation tracking - uint32_t tlbc_generation; // Track TLBC index pool changes - _Py_hashtable_t *tlbc_cache; // Cache of TLBC arrays by code object address -#endif -} RemoteUnwinderObject; - -#define RemoteUnwinder_CAST(op) ((RemoteUnwinderObject *)(op)) - -typedef struct -{ - int lineno; - int end_lineno; - int column; - int end_column; -} LocationInfo; - -typedef struct { - uintptr_t remote_addr; - size_t size; - void *local_copy; -} StackChunkInfo; - -typedef struct { - StackChunkInfo *chunks; - size_t count; -} StackChunkList; - -#include "clinic/_remote_debugging_module.c.h" - -/*[clinic input] -module _remote_debugging -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5f507d5b2e76a7f7]*/ - - -/* ============================================================================ - * FORWARD DECLARATIONS - * ============================================================================ */ - -static inline int -is_frame_valid( - RemoteUnwinderObject *unwinder, - uintptr_t frame_addr, - uintptr_t code_object_addr -); - -typedef int (*thread_processor_func)( - RemoteUnwinderObject *unwinder, - uintptr_t thread_state_addr, - unsigned long tid, - void *context -); - -typedef int (*set_entry_processor_func)( - RemoteUnwinderObject *unwinder, - uintptr_t key_addr, - void *context -); - - -static int -parse_task( - RemoteUnwinderObject *unwinder, - uintptr_t task_address, - PyObject *render_to -); - -static int -parse_coro_chain( - RemoteUnwinderObject *unwinder, - uintptr_t coro_address, - PyObject *render_to -); - -/* Forward declarations for task parsing functions */ -static int parse_frame_object( - RemoteUnwinderObject *unwinder, - PyObject** result, - uintptr_t address, - uintptr_t* address_of_code_object, - uintptr_t* previous_frame -); - -static int -parse_async_frame_chain( - RemoteUnwinderObject *unwinder, - PyObject *calls, - uintptr_t address_of_thread, - uintptr_t running_task_code_obj -); - -static int read_py_ptr(RemoteUnwinderObject *unwinder, uintptr_t address, uintptr_t *ptr_addr); -static int read_Py_ssize_t(RemoteUnwinderObject *unwinder, uintptr_t address, Py_ssize_t *size); - -static int process_task_and_waiters(RemoteUnwinderObject *unwinder, uintptr_t task_addr, PyObject *result); -static int process_task_awaited_by(RemoteUnwinderObject *unwinder, uintptr_t task_address, set_entry_processor_func processor, void *context); -static int find_running_task_in_thread(RemoteUnwinderObject *unwinder, uintptr_t thread_state_addr, uintptr_t *running_task_addr); -static int get_task_code_object(RemoteUnwinderObject *unwinder, uintptr_t task_addr, uintptr_t *code_obj_addr); -static int append_awaited_by(RemoteUnwinderObject *unwinder, unsigned long tid, uintptr_t head_addr, PyObject *result); - -/* ============================================================================ - * UTILITY FUNCTIONS AND HELPERS - * ============================================================================ */ - -#define set_exception_cause(unwinder, exc_type, message) \ - if (unwinder->debug) { \ - _set_debug_exception_cause(exc_type, message); \ - } - -static void -cached_code_metadata_destroy(void *ptr) -{ - CachedCodeMetadata *meta = (CachedCodeMetadata *)ptr; - Py_DECREF(meta->func_name); - Py_DECREF(meta->file_name); - Py_DECREF(meta->linetable); - PyMem_RawFree(meta); -} - -static inline RemoteDebuggingState * -RemoteDebugging_GetState(PyObject *module) -{ - void *state = _PyModule_GetState(module); - assert(state != NULL); - return (RemoteDebuggingState *)state; -} - -static inline RemoteDebuggingState * -RemoteDebugging_GetStateFromType(PyTypeObject *type) -{ - PyObject *module = PyType_GetModule(type); - assert(module != NULL); - return RemoteDebugging_GetState(module); -} - -static inline RemoteDebuggingState * -RemoteDebugging_GetStateFromObject(PyObject *obj) -{ - RemoteUnwinderObject *unwinder = (RemoteUnwinderObject *)obj; - if (unwinder->cached_state == NULL) { - unwinder->cached_state = RemoteDebugging_GetStateFromType(Py_TYPE(obj)); - } - return unwinder->cached_state; -} - -static inline int -RemoteDebugging_InitState(RemoteDebuggingState *st) -{ - return 0; -} - -static int -is_prerelease_version(uint64_t version) -{ - return (version & 0xF0) != 0xF0; -} - -static inline int -validate_debug_offsets(struct _Py_DebugOffsets *debug_offsets) -{ - if (memcmp(debug_offsets->cookie, _Py_Debug_Cookie, sizeof(debug_offsets->cookie)) != 0) { - // The remote is probably running a Python version predating debug offsets. - PyErr_SetString( - PyExc_RuntimeError, - "Can't determine the Python version of the remote process"); - return -1; - } - - // Assume debug offsets could change from one pre-release version to another, - // or one minor version to another, but are stable across patch versions. - if (is_prerelease_version(Py_Version) && Py_Version != debug_offsets->version) { - PyErr_SetString( - PyExc_RuntimeError, - "Can't attach from a pre-release Python interpreter" - " to a process running a different Python version"); - return -1; - } - - if (is_prerelease_version(debug_offsets->version) && Py_Version != debug_offsets->version) { - PyErr_SetString( - PyExc_RuntimeError, - "Can't attach to a pre-release Python interpreter" - " from a process running a different Python version"); - return -1; - } - - unsigned int remote_major = (debug_offsets->version >> 24) & 0xFF; - unsigned int remote_minor = (debug_offsets->version >> 16) & 0xFF; - - if (PY_MAJOR_VERSION != remote_major || PY_MINOR_VERSION != remote_minor) { - PyErr_Format( - PyExc_RuntimeError, - "Can't attach from a Python %d.%d process to a Python %d.%d process", - PY_MAJOR_VERSION, PY_MINOR_VERSION, remote_major, remote_minor); - return -1; - } - - // The debug offsets differ between free threaded and non-free threaded builds. - if (_Py_Debug_Free_Threaded && !debug_offsets->free_threaded) { - PyErr_SetString( - PyExc_RuntimeError, - "Cannot attach from a free-threaded Python process" - " to a process running a non-free-threaded version"); - return -1; - } - - if (!_Py_Debug_Free_Threaded && debug_offsets->free_threaded) { - PyErr_SetString( - PyExc_RuntimeError, - "Cannot attach to a free-threaded Python process" - " from a process running a non-free-threaded version"); - return -1; - } - - return 0; -} - -// Generic function to iterate through all threads -static int -iterate_threads( - RemoteUnwinderObject *unwinder, - thread_processor_func processor, - void *context -) { - uintptr_t thread_state_addr; - unsigned long tid = 0; - - if (0 > _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - unwinder->interpreter_addr + unwinder->debug_offsets.interpreter_state.threads_main, - sizeof(void*), - &thread_state_addr)) - { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read main thread state"); - return -1; - } - - while (thread_state_addr != 0) { - if (0 > _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - thread_state_addr + unwinder->debug_offsets.thread_state.native_thread_id, - sizeof(tid), - &tid)) - { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read thread ID"); - return -1; - } - - // Call the processor function for this thread - if (processor(unwinder, thread_state_addr, tid, context) < 0) { - return -1; - } - - // Move to next thread - if (0 > _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - thread_state_addr + unwinder->debug_offsets.thread_state.next, - sizeof(void*), - &thread_state_addr)) - { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read next thread state"); - return -1; - } - } - - return 0; -} - -// Generic function to iterate through set entries -static int -iterate_set_entries( - RemoteUnwinderObject *unwinder, - uintptr_t set_addr, - set_entry_processor_func processor, - void *context -) { - char set_object[SIZEOF_SET_OBJ]; - if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, set_addr, - SIZEOF_SET_OBJ, set_object) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read set object"); - return -1; - } - - Py_ssize_t num_els = GET_MEMBER(Py_ssize_t, set_object, unwinder->debug_offsets.set_object.used); - Py_ssize_t set_len = GET_MEMBER(Py_ssize_t, set_object, unwinder->debug_offsets.set_object.mask) + 1; - uintptr_t table_ptr = GET_MEMBER(uintptr_t, set_object, unwinder->debug_offsets.set_object.table); - - Py_ssize_t i = 0; - Py_ssize_t els = 0; - while (i < set_len && els < num_els) { - uintptr_t key_addr; - if (read_py_ptr(unwinder, table_ptr, &key_addr) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read set entry key"); - return -1; - } - - if ((void*)key_addr != NULL) { - Py_ssize_t ref_cnt; - if (read_Py_ssize_t(unwinder, table_ptr, &ref_cnt) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read set entry ref count"); - return -1; - } - - if (ref_cnt) { - // Process this valid set entry - if (processor(unwinder, key_addr, context) < 0) { - return -1; - } - els++; - } - } - table_ptr += sizeof(void*) * 2; - i++; - } - - return 0; -} - -// Processor function for task waiters -static int -process_waiter_task( - RemoteUnwinderObject *unwinder, - uintptr_t key_addr, - void *context -) { - PyObject *result = (PyObject *)context; - return process_task_and_waiters(unwinder, key_addr, result); -} - -// Processor function for parsing tasks in sets -static int -process_task_parser( - RemoteUnwinderObject *unwinder, - uintptr_t key_addr, - void *context -) { - PyObject *awaited_by = (PyObject *)context; - return parse_task(unwinder, key_addr, awaited_by); -} - -/* ============================================================================ - * MEMORY READING FUNCTIONS - * ============================================================================ */ - -#define DEFINE_MEMORY_READER(type_name, c_type, error_msg) \ -static inline int \ -read_##type_name(RemoteUnwinderObject *unwinder, uintptr_t address, c_type *result) \ -{ \ - int res = _Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, address, sizeof(c_type), result); \ - if (res < 0) { \ - set_exception_cause(unwinder, PyExc_RuntimeError, error_msg); \ - return -1; \ - } \ - return 0; \ -} - -DEFINE_MEMORY_READER(ptr, uintptr_t, "Failed to read pointer from remote memory") -DEFINE_MEMORY_READER(Py_ssize_t, Py_ssize_t, "Failed to read Py_ssize_t from remote memory") -DEFINE_MEMORY_READER(char, char, "Failed to read char from remote memory") - -static int -read_py_ptr(RemoteUnwinderObject *unwinder, uintptr_t address, uintptr_t *ptr_addr) -{ - if (read_ptr(unwinder, address, ptr_addr)) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read Python pointer"); - return -1; - } - *ptr_addr &= ~Py_TAG_BITS; - return 0; -} - -/* ============================================================================ - * PYTHON OBJECT READING FUNCTIONS - * ============================================================================ */ - -static PyObject * -read_py_str( - RemoteUnwinderObject *unwinder, - uintptr_t address, - Py_ssize_t max_len -) { - PyObject *result = NULL; - char *buf = NULL; - - // Read the entire PyUnicodeObject at once - char unicode_obj[SIZEOF_UNICODE_OBJ]; - int res = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - address, - SIZEOF_UNICODE_OBJ, - unicode_obj - ); - if (res < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read PyUnicodeObject"); - goto err; - } - - Py_ssize_t len = GET_MEMBER(Py_ssize_t, unicode_obj, unwinder->debug_offsets.unicode_object.length); - if (len < 0 || len > max_len) { - PyErr_Format(PyExc_RuntimeError, - "Invalid string length (%zd) at 0x%lx", len, address); - set_exception_cause(unwinder, PyExc_RuntimeError, "Invalid string length in remote Unicode object"); - return NULL; - } - - buf = (char *)PyMem_RawMalloc(len+1); - if (buf == NULL) { - PyErr_NoMemory(); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate buffer for string reading"); - return NULL; - } - - size_t offset = unwinder->debug_offsets.unicode_object.asciiobject_size; - res = _Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, address + offset, len, buf); - if (res < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read string data from remote memory"); - goto err; - } - buf[len] = '\0'; - - result = PyUnicode_FromStringAndSize(buf, len); - if (result == NULL) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create PyUnicode from remote string data"); - goto err; - } - - PyMem_RawFree(buf); - assert(result != NULL); - return result; - -err: - if (buf != NULL) { - PyMem_RawFree(buf); - } - return NULL; -} - -static PyObject * -read_py_bytes( - RemoteUnwinderObject *unwinder, - uintptr_t address, - Py_ssize_t max_len -) { - PyObject *result = NULL; - char *buf = NULL; - - // Read the entire PyBytesObject at once - char bytes_obj[SIZEOF_BYTES_OBJ]; - int res = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - address, - SIZEOF_BYTES_OBJ, - bytes_obj - ); - if (res < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read PyBytesObject"); - goto err; - } - - Py_ssize_t len = GET_MEMBER(Py_ssize_t, bytes_obj, unwinder->debug_offsets.bytes_object.ob_size); - if (len < 0 || len > max_len) { - PyErr_Format(PyExc_RuntimeError, - "Invalid bytes length (%zd) at 0x%lx", len, address); - set_exception_cause(unwinder, PyExc_RuntimeError, "Invalid bytes length in remote bytes object"); - return NULL; - } - - buf = (char *)PyMem_RawMalloc(len+1); - if (buf == NULL) { - PyErr_NoMemory(); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate buffer for bytes reading"); - return NULL; - } - - size_t offset = unwinder->debug_offsets.bytes_object.ob_sval; - res = _Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, address + offset, len, buf); - if (res < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read bytes data from remote memory"); - goto err; - } - buf[len] = '\0'; - - result = PyBytes_FromStringAndSize(buf, len); - if (result == NULL) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create PyBytes from remote bytes data"); - goto err; - } - - PyMem_RawFree(buf); - assert(result != NULL); - return result; - -err: - if (buf != NULL) { - PyMem_RawFree(buf); - } - return NULL; -} - -static long -read_py_long( - RemoteUnwinderObject *unwinder, - uintptr_t address -) -{ - unsigned int shift = PYLONG_BITS_IN_DIGIT; - - // Read the entire PyLongObject at once - char long_obj[SIZEOF_LONG_OBJ]; - int bytes_read = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - address, - unwinder->debug_offsets.long_object.size, - long_obj); - if (bytes_read < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read PyLongObject"); - return -1; - } - - uintptr_t lv_tag = GET_MEMBER(uintptr_t, long_obj, unwinder->debug_offsets.long_object.lv_tag); - int negative = (lv_tag & 3) == 2; - Py_ssize_t size = lv_tag >> 3; - - if (size == 0) { - return 0; - } - - // If the long object has inline digits, use them directly - digit *digits; - if (size <= _PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS) { - // For small integers, digits are inline in the long_value.ob_digit array - digits = (digit *)PyMem_RawMalloc(size * sizeof(digit)); - if (!digits) { - PyErr_NoMemory(); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate digits for small PyLong"); - return -1; - } - memcpy(digits, long_obj + unwinder->debug_offsets.long_object.ob_digit, size * sizeof(digit)); - } else { - // For larger integers, we need to read the digits separately - digits = (digit *)PyMem_RawMalloc(size * sizeof(digit)); - if (!digits) { - PyErr_NoMemory(); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate digits for large PyLong"); - return -1; - } - - bytes_read = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - address + unwinder->debug_offsets.long_object.ob_digit, - sizeof(digit) * size, - digits - ); - if (bytes_read < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read PyLong digits from remote memory"); - goto error; - } - } - - long long value = 0; - - // In theory this can overflow, but because of llvm/llvm-project#16778 - // we can't use __builtin_mul_overflow because it fails to link with - // __muloti4 on aarch64. In practice this is fine because all we're - // testing here are task numbers that would fit in a single byte. - for (Py_ssize_t i = 0; i < size; ++i) { - long long factor = digits[i] * (1UL << (Py_ssize_t)(shift * i)); - value += factor; - } - PyMem_RawFree(digits); - if (negative) { - value *= -1; - } - return (long)value; -error: - PyMem_RawFree(digits); - return -1; -} - -/* ============================================================================ - * ASYNCIO DEBUG FUNCTIONS - * ============================================================================ */ - -// Get the PyAsyncioDebug section address for any platform -static uintptr_t -_Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t* handle) -{ - uintptr_t address; - -#ifdef MS_WINDOWS - // On Windows, search for asyncio debug in executable or DLL - address = search_windows_map_for_section(handle, "AsyncioD", L"_asyncio"); - if (address == 0) { - // Error out: 'python' substring covers both executable and DLL - PyObject *exc = PyErr_GetRaisedException(); - PyErr_SetString(PyExc_RuntimeError, "Failed to find the AsyncioDebug section in the process."); - _PyErr_ChainExceptions1(exc); - } -#elif defined(__linux__) - // On Linux, search for asyncio debug in executable or DLL - address = search_linux_map_for_section(handle, "AsyncioDebug", "python"); - if (address == 0) { - // Error out: 'python' substring covers both executable and DLL - PyObject *exc = PyErr_GetRaisedException(); - PyErr_SetString(PyExc_RuntimeError, "Failed to find the AsyncioDebug section in the process."); - _PyErr_ChainExceptions1(exc); - } -#elif defined(__APPLE__) && TARGET_OS_OSX - // On macOS, try libpython first, then fall back to python - address = search_map_for_section(handle, "AsyncioDebug", "libpython"); - if (address == 0) { - PyErr_Clear(); - address = search_map_for_section(handle, "AsyncioDebug", "python"); - } - if (address == 0) { - // Error out: 'python' substring covers both executable and DLL - PyObject *exc = PyErr_GetRaisedException(); - PyErr_SetString(PyExc_RuntimeError, "Failed to find the AsyncioDebug section in the process."); - _PyErr_ChainExceptions1(exc); - } -#else - Py_UNREACHABLE(); -#endif - - return address; -} - -static int -read_async_debug( - RemoteUnwinderObject *unwinder -) { - uintptr_t async_debug_addr = _Py_RemoteDebug_GetAsyncioDebugAddress(&unwinder->handle); - if (!async_debug_addr) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to get AsyncioDebug address"); - return -1; - } - - size_t size = sizeof(struct _Py_AsyncioModuleDebugOffsets); - int result = _Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, async_debug_addr, size, &unwinder->async_debug_offsets); - if (result < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read AsyncioDebug offsets"); - } - return result; -} - -/* ============================================================================ - * ASYNCIO TASK PARSING FUNCTIONS - * ============================================================================ */ - -static PyObject * -parse_task_name( - RemoteUnwinderObject *unwinder, - uintptr_t task_address -) { - // Read the entire TaskObj at once - char task_obj[SIZEOF_TASK_OBJ]; - int err = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - task_address, - unwinder->async_debug_offsets.asyncio_task_object.size, - task_obj); - if (err < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task object"); - return NULL; - } - - uintptr_t task_name_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_name); - - // The task name can be a long or a string so we need to check the type - char task_name_obj[SIZEOF_PYOBJECT]; - err = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - task_name_addr, - SIZEOF_PYOBJECT, - task_name_obj); - if (err < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task name object"); - return NULL; - } - - // Now read the type object to get the flags - char type_obj[SIZEOF_TYPE_OBJ]; - err = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - GET_MEMBER(uintptr_t, task_name_obj, unwinder->debug_offsets.pyobject.ob_type), - SIZEOF_TYPE_OBJ, - type_obj); - if (err < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task name type object"); - return NULL; - } - - if ((GET_MEMBER(unsigned long, type_obj, unwinder->debug_offsets.type_object.tp_flags) & Py_TPFLAGS_LONG_SUBCLASS)) { - long res = read_py_long(unwinder, task_name_addr); - if (res == -1) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Task name PyLong parsing failed"); - return NULL; - } - return PyUnicode_FromFormat("Task-%d", res); - } - - if(!(GET_MEMBER(unsigned long, type_obj, unwinder->debug_offsets.type_object.tp_flags) & Py_TPFLAGS_UNICODE_SUBCLASS)) { - PyErr_SetString(PyExc_RuntimeError, "Invalid task name object"); - set_exception_cause(unwinder, PyExc_RuntimeError, "Task name object is neither long nor unicode"); - return NULL; - } - - return read_py_str( - unwinder, - task_name_addr, - 255 - ); -} - -static int parse_task_awaited_by( - RemoteUnwinderObject *unwinder, - uintptr_t task_address, - PyObject *awaited_by -) { - return process_task_awaited_by(unwinder, task_address, process_task_parser, awaited_by); -} - -static int -handle_yield_from_frame( - RemoteUnwinderObject *unwinder, - uintptr_t gi_iframe_addr, - uintptr_t gen_type_addr, - PyObject *render_to -) { - // Read the entire interpreter frame at once - char iframe[SIZEOF_INTERP_FRAME]; - int err = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - gi_iframe_addr, - SIZEOF_INTERP_FRAME, - iframe); - if (err < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read interpreter frame in yield_from handler"); - return -1; - } - - if (GET_MEMBER(char, iframe, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_GENERATOR) { - PyErr_SetString( - PyExc_RuntimeError, - "generator doesn't own its frame \\_o_/"); - set_exception_cause(unwinder, PyExc_RuntimeError, "Frame ownership mismatch in yield_from"); - return -1; - } - - uintptr_t stackpointer_addr = GET_MEMBER_NO_TAG(uintptr_t, iframe, unwinder->debug_offsets.interpreter_frame.stackpointer); - - if ((void*)stackpointer_addr != NULL) { - uintptr_t gi_await_addr; - err = read_py_ptr( - unwinder, - stackpointer_addr - sizeof(void*), - &gi_await_addr); - if (err) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read gi_await address"); - return -1; - } - - if ((void*)gi_await_addr != NULL) { - uintptr_t gi_await_addr_type_addr; - err = read_ptr( - unwinder, - gi_await_addr + unwinder->debug_offsets.pyobject.ob_type, - &gi_await_addr_type_addr); - if (err) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read gi_await type address"); - return -1; - } - - if (gen_type_addr == gi_await_addr_type_addr) { - /* This needs an explanation. We always start with parsing - native coroutine / generator frames. Ultimately they - are awaiting on something. That something can be - a native coroutine frame or... an iterator. - If it's the latter -- we can't continue building - our chain. So the condition to bail out of this is - to do that when the type of the current coroutine - doesn't match the type of whatever it points to - in its cr_await. - */ - err = parse_coro_chain(unwinder, gi_await_addr, render_to); - if (err) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse coroutine chain in yield_from"); - return -1; - } - } - } - } - - return 0; -} - -static int -parse_coro_chain( - RemoteUnwinderObject *unwinder, - uintptr_t coro_address, - PyObject *render_to -) { - assert((void*)coro_address != NULL); - - // Read the entire generator object at once - char gen_object[SIZEOF_GEN_OBJ]; - int err = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - coro_address, - SIZEOF_GEN_OBJ, - gen_object); - if (err < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read generator object in coro chain"); - return -1; - } - - int8_t frame_state = GET_MEMBER(int8_t, gen_object, unwinder->debug_offsets.gen_object.gi_frame_state); - if (frame_state == FRAME_CLEARED) { - return 0; - } - - uintptr_t gen_type_addr = GET_MEMBER(uintptr_t, gen_object, unwinder->debug_offsets.pyobject.ob_type); - - PyObject* name = NULL; - - // Parse the previous frame using the gi_iframe from local copy - uintptr_t prev_frame; - uintptr_t gi_iframe_addr = coro_address + unwinder->debug_offsets.gen_object.gi_iframe; - uintptr_t address_of_code_object = 0; - if (parse_frame_object(unwinder, &name, gi_iframe_addr, &address_of_code_object, &prev_frame) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse frame object in coro chain"); - return -1; - } - - if (!name) { - return 0; - } - - if (PyList_Append(render_to, name)) { - Py_DECREF(name); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame to coro chain"); - return -1; - } - Py_DECREF(name); - - if (frame_state == FRAME_SUSPENDED_YIELD_FROM) { - return handle_yield_from_frame(unwinder, gi_iframe_addr, gen_type_addr, render_to); - } - - return 0; -} - -static PyObject* -create_task_result( - RemoteUnwinderObject *unwinder, - uintptr_t task_address -) { - PyObject* result = NULL; - PyObject *call_stack = NULL; - PyObject *tn = NULL; - char task_obj[SIZEOF_TASK_OBJ]; - uintptr_t coro_addr; - - // Create call_stack first since it's the first tuple element - call_stack = PyList_New(0); - if (call_stack == NULL) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create call stack list"); - goto error; - } - - // Create task name/address for second tuple element - tn = PyLong_FromUnsignedLongLong(task_address); - if (tn == NULL) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create task name/address"); - goto error; - } - - // Parse coroutine chain - if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, task_address, - unwinder->async_debug_offsets.asyncio_task_object.size, - task_obj) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task object for coro chain"); - goto error; - } - - coro_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_coro); - - if ((void*)coro_addr != NULL) { - if (parse_coro_chain(unwinder, coro_addr, call_stack) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse coroutine chain"); - goto error; - } - - if (PyList_Reverse(call_stack)) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to reverse call stack"); - goto error; - } - } - - // Create final CoroInfo result - RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); - result = PyStructSequence_New(state->CoroInfo_Type); - if (result == NULL) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create CoroInfo"); - goto error; - } - - // PyStructSequence_SetItem steals references, so we don't need to DECREF on success - PyStructSequence_SetItem(result, 0, call_stack); // This steals the reference - PyStructSequence_SetItem(result, 1, tn); // This steals the reference - - return result; - -error: - Py_XDECREF(result); - Py_XDECREF(call_stack); - Py_XDECREF(tn); - return NULL; -} - -static int -parse_task( - RemoteUnwinderObject *unwinder, - uintptr_t task_address, - PyObject *render_to -) { - char is_task; - PyObject* result = NULL; - int err; - - err = read_char( - unwinder, - task_address + unwinder->async_debug_offsets.asyncio_task_object.task_is_task, - &is_task); - if (err) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read is_task flag"); - goto error; - } - - if (is_task) { - result = create_task_result(unwinder, task_address); - if (!result) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create task result"); - goto error; - } - } else { - // Create an empty CoroInfo for non-task objects - RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); - result = PyStructSequence_New(state->CoroInfo_Type); - if (result == NULL) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create empty CoroInfo"); - goto error; - } - PyObject *empty_list = PyList_New(0); - if (empty_list == NULL) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create empty list"); - goto error; - } - PyObject *task_name = PyLong_FromUnsignedLongLong(task_address); - if (task_name == NULL) { - Py_DECREF(empty_list); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create task name"); - goto error; - } - PyStructSequence_SetItem(result, 0, empty_list); // This steals the reference - PyStructSequence_SetItem(result, 1, task_name); // This steals the reference - } - if (PyList_Append(render_to, result)) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append task result to render list"); - goto error; - } - - Py_DECREF(result); - return 0; - -error: - Py_XDECREF(result); - return -1; -} - -static int -process_single_task_node( - RemoteUnwinderObject *unwinder, - uintptr_t task_addr, - PyObject **task_info, - PyObject *result -) { - PyObject *tn = NULL; - PyObject *current_awaited_by = NULL; - PyObject *task_id = NULL; - PyObject *result_item = NULL; - PyObject *coroutine_stack = NULL; - - tn = parse_task_name(unwinder, task_addr); - if (tn == NULL) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse task name in single task node"); - goto error; - } - - current_awaited_by = PyList_New(0); - if (current_awaited_by == NULL) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create awaited_by list in single task node"); - goto error; - } - - // Extract the coroutine stack for this task - coroutine_stack = PyList_New(0); - if (coroutine_stack == NULL) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create coroutine stack list in single task node"); - goto error; - } - - if (parse_task(unwinder, task_addr, coroutine_stack) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse task coroutine stack in single task node"); - goto error; - } - - task_id = PyLong_FromUnsignedLongLong(task_addr); - if (task_id == NULL) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create task ID in single task node"); - goto error; - } - - RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); - result_item = PyStructSequence_New(state->TaskInfo_Type); - if (result_item == NULL) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create TaskInfo in single task node"); - goto error; - } - - PyStructSequence_SetItem(result_item, 0, task_id); // steals ref - PyStructSequence_SetItem(result_item, 1, tn); // steals ref - PyStructSequence_SetItem(result_item, 2, coroutine_stack); // steals ref - PyStructSequence_SetItem(result_item, 3, current_awaited_by); // steals ref - - // References transferred to tuple - task_id = NULL; - tn = NULL; - coroutine_stack = NULL; - current_awaited_by = NULL; - - if (PyList_Append(result, result_item)) { - Py_DECREF(result_item); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append result item in single task node"); - return -1; - } - if (task_info != NULL) { - *task_info = result_item; - } - Py_DECREF(result_item); - - // Get back current_awaited_by reference for parse_task_awaited_by - current_awaited_by = PyStructSequence_GetItem(result_item, 3); - if (parse_task_awaited_by(unwinder, task_addr, current_awaited_by) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse awaited_by in single task node"); - // No cleanup needed here since all references were transferred to result_item - // and result_item was already added to result list and decreffed - return -1; - } - - return 0; - -error: - Py_XDECREF(tn); - Py_XDECREF(current_awaited_by); - Py_XDECREF(task_id); - Py_XDECREF(result_item); - Py_XDECREF(coroutine_stack); - return -1; -} - -// Thread processor for get_all_awaited_by -static int -process_thread_for_awaited_by( - RemoteUnwinderObject *unwinder, - uintptr_t thread_state_addr, - unsigned long tid, - void *context -) { - PyObject *result = (PyObject *)context; - uintptr_t head_addr = thread_state_addr + unwinder->async_debug_offsets.asyncio_thread_state.asyncio_tasks_head; - return append_awaited_by(unwinder, tid, head_addr, result); -} - -// Generic function to process task awaited_by -static int -process_task_awaited_by( - RemoteUnwinderObject *unwinder, - uintptr_t task_address, - set_entry_processor_func processor, - void *context -) { - // Read the entire TaskObj at once - char task_obj[SIZEOF_TASK_OBJ]; - if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, task_address, - unwinder->async_debug_offsets.asyncio_task_object.size, - task_obj) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task object"); - return -1; - } - - uintptr_t task_ab_addr = GET_MEMBER_NO_TAG(uintptr_t, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_awaited_by); - if ((void*)task_ab_addr == NULL) { - return 0; // No tasks waiting for this one - } - - char awaited_by_is_a_set = GET_MEMBER(char, task_obj, unwinder->async_debug_offsets.asyncio_task_object.task_awaited_by_is_set); - - if (awaited_by_is_a_set) { - return iterate_set_entries(unwinder, task_ab_addr, processor, context); - } else { - // Single task waiting - return processor(unwinder, task_ab_addr, context); - } -} - -static int -process_running_task_chain( - RemoteUnwinderObject *unwinder, - uintptr_t running_task_addr, - uintptr_t thread_state_addr, - PyObject *result -) { - uintptr_t running_task_code_obj = 0; - if(get_task_code_object(unwinder, running_task_addr, &running_task_code_obj) < 0) { - return -1; - } - - // First, add this task to the result - PyObject *task_info = NULL; - if (process_single_task_node(unwinder, running_task_addr, &task_info, result) < 0) { - return -1; - } - - // Get the chain from the current frame to this task - PyObject *coro_chain = PyStructSequence_GET_ITEM(task_info, 2); - assert(coro_chain != NULL); - if (PyList_GET_SIZE(coro_chain) != 1) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Coro chain is not a single item"); - return -1; - } - PyObject *coro_info = PyList_GET_ITEM(coro_chain, 0); - assert(coro_info != NULL); - PyObject *frame_chain = PyStructSequence_GET_ITEM(coro_info, 0); - assert(frame_chain != NULL); - - // Clear the coro_chain - if (PyList_Clear(frame_chain) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to clear coroutine chain"); - return -1; - } - - // Add the chain from the current frame to this task - if (parse_async_frame_chain(unwinder, frame_chain, thread_state_addr, running_task_code_obj) < 0) { - return -1; - } - - // Now find all tasks that are waiting for this task and process them - if (process_task_awaited_by(unwinder, running_task_addr, process_waiter_task, result) < 0) { - return -1; - } - - return 0; -} - -// Thread processor for get_async_stack_trace -static int -process_thread_for_async_stack_trace( - RemoteUnwinderObject *unwinder, - uintptr_t thread_state_addr, - unsigned long tid, - void *context -) { - PyObject *result = (PyObject *)context; - - // Find running task in this thread - uintptr_t running_task_addr; - if (find_running_task_in_thread(unwinder, thread_state_addr, &running_task_addr) < 0) { - return 0; - } - - // If we found a running task, process it and its waiters - if ((void*)running_task_addr != NULL) { - PyObject *task_list = PyList_New(0); - if (task_list == NULL) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create task list for thread"); - return -1; - } - - if (process_running_task_chain(unwinder, running_task_addr, thread_state_addr, task_list) < 0) { - Py_DECREF(task_list); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to process running task chain"); - return -1; - } - - // Create AwaitedInfo structure for this thread - PyObject *tid_py = PyLong_FromUnsignedLong(tid); - if (tid_py == NULL) { - Py_DECREF(task_list); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create thread ID"); - return -1; - } - - RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); - PyObject *awaited_info = PyStructSequence_New(state->AwaitedInfo_Type); - if (awaited_info == NULL) { - Py_DECREF(tid_py); - Py_DECREF(task_list); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create AwaitedInfo"); - return -1; - } - - PyStructSequence_SetItem(awaited_info, 0, tid_py); // steals ref - PyStructSequence_SetItem(awaited_info, 1, task_list); // steals ref - - if (PyList_Append(result, awaited_info)) { - Py_DECREF(awaited_info); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append AwaitedInfo to result"); - return -1; - } - Py_DECREF(awaited_info); - } - - return 0; -} - -static int -process_task_and_waiters( - RemoteUnwinderObject *unwinder, - uintptr_t task_addr, - PyObject *result -) { - // First, add this task to the result - if (process_single_task_node(unwinder, task_addr, NULL, result) < 0) { - return -1; - } - - // Now find all tasks that are waiting for this task and process them - return process_task_awaited_by(unwinder, task_addr, process_waiter_task, result); -} - -static int -find_running_task_in_thread( - RemoteUnwinderObject *unwinder, - uintptr_t thread_state_addr, - uintptr_t *running_task_addr -) { - *running_task_addr = (uintptr_t)NULL; - - uintptr_t address_of_running_loop; - int bytes_read = read_py_ptr( - unwinder, - thread_state_addr + unwinder->async_debug_offsets.asyncio_thread_state.asyncio_running_loop, - &address_of_running_loop); - if (bytes_read == -1) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read running loop address"); - return -1; - } - - // no asyncio loop is now running - if ((void*)address_of_running_loop == NULL) { - return 0; - } - - int err = read_ptr( - unwinder, - thread_state_addr + unwinder->async_debug_offsets.asyncio_thread_state.asyncio_running_task, - running_task_addr); - if (err) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read running task address"); - return -1; - } - - return 0; -} - -static int -get_task_code_object(RemoteUnwinderObject *unwinder, uintptr_t task_addr, uintptr_t *code_obj_addr) { - uintptr_t running_coro_addr = 0; - - if(read_py_ptr( - unwinder, - task_addr + unwinder->async_debug_offsets.asyncio_task_object.task_coro, - &running_coro_addr) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Running task coro read failed"); - return -1; - } - - if (running_coro_addr == 0) { - PyErr_SetString(PyExc_RuntimeError, "Running task coro is NULL"); - set_exception_cause(unwinder, PyExc_RuntimeError, "Running task coro address is NULL"); - return -1; - } - - // note: genobject's gi_iframe is an embedded struct so the address to - // the offset leads directly to its first field: f_executable - if (read_py_ptr( - unwinder, - running_coro_addr + unwinder->debug_offsets.gen_object.gi_iframe, code_obj_addr) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read running task code object"); - return -1; - } - - if (*code_obj_addr == 0) { - PyErr_SetString(PyExc_RuntimeError, "Running task code object is NULL"); - set_exception_cause(unwinder, PyExc_RuntimeError, "Running task code object address is NULL"); - return -1; - } - - return 0; -} - -/* ============================================================================ - * TLBC CACHING FUNCTIONS - * ============================================================================ */ - -#ifdef Py_GIL_DISABLED - -typedef struct { - void *tlbc_array; // Local copy of the TLBC array - Py_ssize_t tlbc_array_size; // Size of the TLBC array - uint32_t generation; // Generation when this was cached -} TLBCCacheEntry; - -static void -tlbc_cache_entry_destroy(void *ptr) -{ - TLBCCacheEntry *entry = (TLBCCacheEntry *)ptr; - if (entry->tlbc_array) { - PyMem_RawFree(entry->tlbc_array); - } - PyMem_RawFree(entry); -} - -static TLBCCacheEntry * -get_tlbc_cache_entry(RemoteUnwinderObject *self, uintptr_t code_addr, uint32_t current_generation) -{ - void *key = (void *)code_addr; - TLBCCacheEntry *entry = _Py_hashtable_get(self->tlbc_cache, key); - - if (entry && entry->generation != current_generation) { - // Entry is stale, remove it by setting to NULL - _Py_hashtable_set(self->tlbc_cache, key, NULL); - entry = NULL; - } - - return entry; -} - -static int -cache_tlbc_array(RemoteUnwinderObject *unwinder, uintptr_t code_addr, uintptr_t tlbc_array_addr, uint32_t generation) -{ - uintptr_t tlbc_array_ptr; - void *tlbc_array = NULL; - TLBCCacheEntry *entry = NULL; - - // Read the TLBC array pointer - if (read_ptr(unwinder, tlbc_array_addr, &tlbc_array_ptr) != 0 || tlbc_array_ptr == 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read TLBC array pointer"); - return 0; // No TLBC array - } - - // Read the TLBC array size - Py_ssize_t tlbc_size; - if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, tlbc_array_ptr, sizeof(tlbc_size), &tlbc_size) != 0 || tlbc_size <= 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read TLBC array size"); - return 0; // Invalid size - } - - // Allocate and read the entire TLBC array - size_t array_data_size = tlbc_size * sizeof(void*); - tlbc_array = PyMem_RawMalloc(sizeof(Py_ssize_t) + array_data_size); - if (!tlbc_array) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate TLBC array"); - return 0; // Memory error - } - - if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, tlbc_array_ptr, sizeof(Py_ssize_t) + array_data_size, tlbc_array) != 0) { - PyMem_RawFree(tlbc_array); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read TLBC array data"); - return 0; // Read error - } - - // Create cache entry - entry = PyMem_RawMalloc(sizeof(TLBCCacheEntry)); - if (!entry) { - PyMem_RawFree(tlbc_array); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate TLBC cache entry"); - return 0; // Memory error - } - - entry->tlbc_array = tlbc_array; - entry->tlbc_array_size = tlbc_size; - entry->generation = generation; - - // Store in cache - void *key = (void *)code_addr; - if (_Py_hashtable_set(unwinder->tlbc_cache, key, entry) < 0) { - tlbc_cache_entry_destroy(entry); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to store TLBC entry in cache"); - return 0; // Cache error - } - - return 1; // Success -} - - - -#endif - -/* ============================================================================ - * LINE TABLE PARSING FUNCTIONS - * ============================================================================ */ - -static int -scan_varint(const uint8_t **ptr) -{ - unsigned int read = **ptr; - *ptr = *ptr + 1; - unsigned int val = read & 63; - unsigned int shift = 0; - while (read & 64) { - read = **ptr; - *ptr = *ptr + 1; - shift += 6; - val |= (read & 63) << shift; - } - return val; -} - -static int -scan_signed_varint(const uint8_t **ptr) -{ - unsigned int uval = scan_varint(ptr); - if (uval & 1) { - return -(int)(uval >> 1); - } - else { - return uval >> 1; - } -} - -static bool -parse_linetable(const uintptr_t addrq, const char* linetable, int firstlineno, LocationInfo* info) -{ - const uint8_t* ptr = (const uint8_t*)(linetable); - uint64_t addr = 0; - info->lineno = firstlineno; - - while (*ptr != '\0') { - // See InternalDocs/code_objects.md for where these magic numbers are from - // and for the decoding algorithm. - uint8_t first_byte = *(ptr++); - uint8_t code = (first_byte >> 3) & 15; - size_t length = (first_byte & 7) + 1; - uintptr_t end_addr = addr + length; - switch (code) { - case PY_CODE_LOCATION_INFO_NONE: { - break; - } - case PY_CODE_LOCATION_INFO_LONG: { - int line_delta = scan_signed_varint(&ptr); - info->lineno += line_delta; - info->end_lineno = info->lineno + scan_varint(&ptr); - info->column = scan_varint(&ptr) - 1; - info->end_column = scan_varint(&ptr) - 1; - break; - } - case PY_CODE_LOCATION_INFO_NO_COLUMNS: { - int line_delta = scan_signed_varint(&ptr); - info->lineno += line_delta; - info->column = info->end_column = -1; - break; - } - case PY_CODE_LOCATION_INFO_ONE_LINE0: - case PY_CODE_LOCATION_INFO_ONE_LINE1: - case PY_CODE_LOCATION_INFO_ONE_LINE2: { - int line_delta = code - 10; - info->lineno += line_delta; - info->end_lineno = info->lineno; - info->column = *(ptr++); - info->end_column = *(ptr++); - break; - } - default: { - uint8_t second_byte = *(ptr++); - if ((second_byte & 128) != 0) { - return false; - } - info->column = code << 3 | (second_byte >> 4); - info->end_column = info->column + (second_byte & 15); - break; - } - } - if (addr <= addrq && end_addr > addrq) { - return true; - } - addr = end_addr; - } - return false; -} - -/* ============================================================================ - * CODE OBJECT AND FRAME PARSING FUNCTIONS - * ============================================================================ */ - -static int -parse_code_object(RemoteUnwinderObject *unwinder, - PyObject **result, - uintptr_t address, - uintptr_t instruction_pointer, - uintptr_t *previous_frame, - int32_t tlbc_index) -{ - void *key = (void *)address; - CachedCodeMetadata *meta = NULL; - PyObject *func = NULL; - PyObject *file = NULL; - PyObject *linetable = NULL; - PyObject *lineno = NULL; - PyObject *tuple = NULL; - -#ifdef Py_GIL_DISABLED - // In free threading builds, code object addresses might have the low bit set - // as a flag, so we need to mask it off to get the real address - uintptr_t real_address = address & (~1); -#else - uintptr_t real_address = address; -#endif - - if (unwinder && unwinder->code_object_cache != NULL) { - meta = _Py_hashtable_get(unwinder->code_object_cache, key); - } - - if (meta == NULL) { - char code_object[SIZEOF_CODE_OBJ]; - if (_Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, real_address, SIZEOF_CODE_OBJ, code_object) < 0) - { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read code object"); - goto error; - } - - func = read_py_str(unwinder, - GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.qualname), 1024); - if (!func) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read function name from code object"); - goto error; - } - - file = read_py_str(unwinder, - GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.filename), 1024); - if (!file) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read filename from code object"); - goto error; - } - - linetable = read_py_bytes(unwinder, - GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable), 4096); - if (!linetable) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read linetable from code object"); - goto error; - } - - meta = PyMem_RawMalloc(sizeof(CachedCodeMetadata)); - if (!meta) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate cached code metadata"); - goto error; - } - - meta->func_name = func; - meta->file_name = file; - meta->linetable = linetable; - meta->first_lineno = GET_MEMBER(int, code_object, unwinder->debug_offsets.code_object.firstlineno); - meta->addr_code_adaptive = real_address + unwinder->debug_offsets.code_object.co_code_adaptive; - - if (unwinder && unwinder->code_object_cache && _Py_hashtable_set(unwinder->code_object_cache, key, meta) < 0) { - cached_code_metadata_destroy(meta); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to cache code metadata"); - goto error; - } - - // Ownership transferred to meta - func = NULL; - file = NULL; - linetable = NULL; - } - - uintptr_t ip = instruction_pointer; - ptrdiff_t addrq; - -#ifdef Py_GIL_DISABLED - // Handle thread-local bytecode (TLBC) in free threading builds - if (tlbc_index == 0 || unwinder->debug_offsets.code_object.co_tlbc == 0 || unwinder == NULL) { - // No TLBC or no unwinder - use main bytecode directly - addrq = (uint16_t *)ip - (uint16_t *)meta->addr_code_adaptive; - goto done_tlbc; - } - - // Try to get TLBC data from cache (we'll get generation from the caller) - TLBCCacheEntry *tlbc_entry = get_tlbc_cache_entry(unwinder, real_address, unwinder->tlbc_generation); - - if (!tlbc_entry) { - // Cache miss - try to read and cache TLBC array - if (!cache_tlbc_array(unwinder, real_address, real_address + unwinder->debug_offsets.code_object.co_tlbc, unwinder->tlbc_generation)) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to cache TLBC array"); - goto error; - } - tlbc_entry = get_tlbc_cache_entry(unwinder, real_address, unwinder->tlbc_generation); - } - - if (tlbc_entry && tlbc_index < tlbc_entry->tlbc_array_size) { - // Use cached TLBC data - uintptr_t *entries = (uintptr_t *)((char *)tlbc_entry->tlbc_array + sizeof(Py_ssize_t)); - uintptr_t tlbc_bytecode_addr = entries[tlbc_index]; - - if (tlbc_bytecode_addr != 0) { - // Calculate offset from TLBC bytecode - addrq = (uint16_t *)ip - (uint16_t *)tlbc_bytecode_addr; - goto done_tlbc; - } - } - - // Fall back to main bytecode - addrq = (uint16_t *)ip - (uint16_t *)meta->addr_code_adaptive; - -done_tlbc: -#else - // Non-free-threaded build, always use the main bytecode - (void)tlbc_index; // Suppress unused parameter warning - (void)unwinder; // Suppress unused parameter warning - addrq = (uint16_t *)ip - (uint16_t *)meta->addr_code_adaptive; -#endif - ; // Empty statement to avoid C23 extension warning - LocationInfo info = {0}; - bool ok = parse_linetable(addrq, PyBytes_AS_STRING(meta->linetable), - meta->first_lineno, &info); - if (!ok) { - info.lineno = -1; - } - - lineno = PyLong_FromLong(info.lineno); - if (!lineno) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create line number object"); - goto error; - } - - RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); - tuple = PyStructSequence_New(state->FrameInfo_Type); - if (!tuple) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create FrameInfo for code object"); - goto error; - } - - Py_INCREF(meta->func_name); - Py_INCREF(meta->file_name); - PyStructSequence_SetItem(tuple, 0, meta->file_name); - PyStructSequence_SetItem(tuple, 1, lineno); - PyStructSequence_SetItem(tuple, 2, meta->func_name); - - *result = tuple; - return 0; - -error: - Py_XDECREF(func); - Py_XDECREF(file); - Py_XDECREF(linetable); - Py_XDECREF(lineno); - Py_XDECREF(tuple); - return -1; -} - -/* ============================================================================ - * STACK CHUNK MANAGEMENT FUNCTIONS - * ============================================================================ */ - -static void -cleanup_stack_chunks(StackChunkList *chunks) -{ - for (size_t i = 0; i < chunks->count; ++i) { - PyMem_RawFree(chunks->chunks[i].local_copy); - } - PyMem_RawFree(chunks->chunks); -} - -static int -process_single_stack_chunk( - RemoteUnwinderObject *unwinder, - uintptr_t chunk_addr, - StackChunkInfo *chunk_info -) { - // Start with default size assumption - size_t current_size = _PY_DATA_STACK_CHUNK_SIZE; - - char *this_chunk = PyMem_RawMalloc(current_size); - if (!this_chunk) { - PyErr_NoMemory(); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate stack chunk buffer"); - return -1; - } - - if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, chunk_addr, current_size, this_chunk) < 0) { - PyMem_RawFree(this_chunk); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read stack chunk"); - return -1; - } - - // Check actual size and reread if necessary - size_t actual_size = GET_MEMBER(size_t, this_chunk, offsetof(_PyStackChunk, size)); - if (actual_size != current_size) { - this_chunk = PyMem_RawRealloc(this_chunk, actual_size); - if (!this_chunk) { - PyErr_NoMemory(); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to reallocate stack chunk buffer"); - return -1; - } - - if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, chunk_addr, actual_size, this_chunk) < 0) { - PyMem_RawFree(this_chunk); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to reread stack chunk with correct size"); - return -1; - } - current_size = actual_size; - } - - chunk_info->remote_addr = chunk_addr; - chunk_info->size = current_size; - chunk_info->local_copy = this_chunk; - return 0; -} - -static int -copy_stack_chunks(RemoteUnwinderObject *unwinder, - uintptr_t tstate_addr, - StackChunkList *out_chunks) -{ - uintptr_t chunk_addr; - StackChunkInfo *chunks = NULL; - size_t count = 0; - size_t max_chunks = 16; - - if (read_ptr(unwinder, tstate_addr + unwinder->debug_offsets.thread_state.datastack_chunk, &chunk_addr)) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read initial stack chunk address"); - return -1; - } - - chunks = PyMem_RawMalloc(max_chunks * sizeof(StackChunkInfo)); - if (!chunks) { - PyErr_NoMemory(); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate stack chunks array"); - return -1; - } - - while (chunk_addr != 0) { - // Grow array if needed - if (count >= max_chunks) { - max_chunks *= 2; - StackChunkInfo *new_chunks = PyMem_RawRealloc(chunks, max_chunks * sizeof(StackChunkInfo)); - if (!new_chunks) { - PyErr_NoMemory(); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to grow stack chunks array"); - goto error; - } - chunks = new_chunks; - } - - // Process this chunk - if (process_single_stack_chunk(unwinder, chunk_addr, &chunks[count]) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to process stack chunk"); - goto error; - } - - // Get next chunk address and increment count - chunk_addr = GET_MEMBER(uintptr_t, chunks[count].local_copy, offsetof(_PyStackChunk, previous)); - count++; - } - - out_chunks->chunks = chunks; - out_chunks->count = count; - return 0; - -error: - for (size_t i = 0; i < count; ++i) { - PyMem_RawFree(chunks[i].local_copy); - } - PyMem_RawFree(chunks); - return -1; -} - -static void * -find_frame_in_chunks(StackChunkList *chunks, uintptr_t remote_ptr) -{ - for (size_t i = 0; i < chunks->count; ++i) { - uintptr_t base = chunks->chunks[i].remote_addr + offsetof(_PyStackChunk, data); - size_t payload = chunks->chunks[i].size - offsetof(_PyStackChunk, data); - - if (remote_ptr >= base && remote_ptr < base + payload) { - return (char *)chunks->chunks[i].local_copy + (remote_ptr - chunks->chunks[i].remote_addr); - } - } - return NULL; -} - -static int -parse_frame_from_chunks( - RemoteUnwinderObject *unwinder, - PyObject **result, - uintptr_t address, - uintptr_t *previous_frame, - StackChunkList *chunks -) { - void *frame_ptr = find_frame_in_chunks(chunks, address); - if (!frame_ptr) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Frame not found in stack chunks"); - return -1; - } - - char *frame = (char *)frame_ptr; - *previous_frame = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.previous); - uintptr_t code_object = GET_MEMBER_NO_TAG(uintptr_t, frame_ptr, unwinder->debug_offsets.interpreter_frame.executable); - int frame_valid = is_frame_valid(unwinder, (uintptr_t)frame, code_object); - if (frame_valid != 1) { - return frame_valid; - } - - uintptr_t instruction_pointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.instr_ptr); - - // Get tlbc_index for free threading builds - int32_t tlbc_index = 0; -#ifdef Py_GIL_DISABLED - if (unwinder->debug_offsets.interpreter_frame.tlbc_index != 0) { - tlbc_index = GET_MEMBER(int32_t, frame, unwinder->debug_offsets.interpreter_frame.tlbc_index); - } -#endif - - return parse_code_object(unwinder, result, code_object, instruction_pointer, previous_frame, tlbc_index); -} - -/* ============================================================================ - * INTERPRETER STATE AND THREAD DISCOVERY FUNCTIONS - * ============================================================================ */ - -static int -populate_initial_state_data( - int all_threads, - RemoteUnwinderObject *unwinder, - uintptr_t runtime_start_address, - uintptr_t *interpreter_state, - uintptr_t *tstate -) { - uint64_t interpreter_state_list_head = - unwinder->debug_offsets.runtime_state.interpreters_head; - - uintptr_t address_of_interpreter_state; - int bytes_read = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - runtime_start_address + interpreter_state_list_head, - sizeof(void*), - &address_of_interpreter_state); - if (bytes_read < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read interpreter state address"); - return -1; - } - - if (address_of_interpreter_state == 0) { - PyErr_SetString(PyExc_RuntimeError, "No interpreter state found"); - set_exception_cause(unwinder, PyExc_RuntimeError, "Interpreter state is NULL"); - return -1; - } - - *interpreter_state = address_of_interpreter_state; - - if (all_threads) { - *tstate = 0; - return 0; - } - - uintptr_t address_of_thread = address_of_interpreter_state + - unwinder->debug_offsets.interpreter_state.threads_main; - - if (_Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - address_of_thread, - sizeof(void*), - tstate) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read main thread state address"); - return -1; - } - - return 0; -} - - -static int -find_running_frame( - RemoteUnwinderObject *unwinder, - uintptr_t address_of_thread, - uintptr_t *frame -) { - if ((void*)address_of_thread != NULL) { - int err = read_ptr( - unwinder, - address_of_thread + unwinder->debug_offsets.thread_state.current_frame, - frame); - if (err) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read current frame pointer"); - return -1; - } - return 0; - } - - *frame = (uintptr_t)NULL; - return 0; -} - -/* ============================================================================ - * FRAME PARSING FUNCTIONS - * ============================================================================ */ - -static inline int -is_frame_valid( - RemoteUnwinderObject *unwinder, - uintptr_t frame_addr, - uintptr_t code_object_addr -) { - if ((void*)code_object_addr == NULL) { - return 0; - } - - void* frame = (void*)frame_addr; - - if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) == FRAME_OWNED_BY_CSTACK || - GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) == FRAME_OWNED_BY_INTERPRETER) { - return 0; // C frame - } - - if (GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_GENERATOR - && GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner) != FRAME_OWNED_BY_THREAD) { - PyErr_Format(PyExc_RuntimeError, "Unhandled frame owner %d.\n", - GET_MEMBER(char, frame, unwinder->debug_offsets.interpreter_frame.owner)); - set_exception_cause(unwinder, PyExc_RuntimeError, "Unhandled frame owner type in async frame"); - return -1; - } - return 1; -} - -static int -parse_frame_object( - RemoteUnwinderObject *unwinder, - PyObject** result, - uintptr_t address, - uintptr_t* address_of_code_object, - uintptr_t* previous_frame -) { - char frame[SIZEOF_INTERP_FRAME]; - *address_of_code_object = 0; - - Py_ssize_t bytes_read = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - address, - SIZEOF_INTERP_FRAME, - frame - ); - if (bytes_read < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read interpreter frame"); - return -1; - } - - *previous_frame = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.previous); - uintptr_t code_object = GET_MEMBER_NO_TAG(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.executable); - int frame_valid = is_frame_valid(unwinder, (uintptr_t)frame, code_object); - if (frame_valid != 1) { - return frame_valid; - } - - uintptr_t instruction_pointer = GET_MEMBER(uintptr_t, frame, unwinder->debug_offsets.interpreter_frame.instr_ptr); - - // Get tlbc_index for free threading builds - int32_t tlbc_index = 0; -#ifdef Py_GIL_DISABLED - if (unwinder->debug_offsets.interpreter_frame.tlbc_index != 0) { - tlbc_index = GET_MEMBER(int32_t, frame, unwinder->debug_offsets.interpreter_frame.tlbc_index); - } -#endif - - *address_of_code_object = code_object; - return parse_code_object(unwinder, result, code_object, instruction_pointer, previous_frame, tlbc_index); -} - -static int - parse_async_frame_chain( - RemoteUnwinderObject *unwinder, - PyObject *calls, - uintptr_t address_of_thread, - uintptr_t running_task_code_obj -) { - uintptr_t address_of_current_frame; - if (find_running_frame(unwinder, address_of_thread, &address_of_current_frame) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Running frame search failed in async chain"); - return -1; - } - - while ((void*)address_of_current_frame != NULL) { - PyObject* frame_info = NULL; - uintptr_t address_of_code_object; - int res = parse_frame_object( - unwinder, - &frame_info, - address_of_current_frame, - &address_of_code_object, - &address_of_current_frame - ); - - if (res < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Async frame object parsing failed in chain"); - return -1; - } - - if (!frame_info) { - continue; - } - - if (PyList_Append(calls, frame_info) == -1) { - Py_DECREF(frame_info); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame info to async chain"); - return -1; - } - - Py_DECREF(frame_info); - - if (address_of_code_object == running_task_code_obj) { - break; - } - } - - return 0; -} - -/* ============================================================================ - * AWAITED BY PARSING FUNCTIONS - * ============================================================================ */ - -static int -append_awaited_by_for_thread( - RemoteUnwinderObject *unwinder, - uintptr_t head_addr, - PyObject *result -) { - char task_node[SIZEOF_LLIST_NODE]; - - if (_Py_RemoteDebug_PagedReadRemoteMemory(&unwinder->handle, head_addr, - sizeof(task_node), task_node) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read task node head"); - return -1; - } - - size_t iteration_count = 0; - const size_t MAX_ITERATIONS = 2 << 15; // A reasonable upper bound - - while (GET_MEMBER(uintptr_t, task_node, unwinder->debug_offsets.llist_node.next) != head_addr) { - if (++iteration_count > MAX_ITERATIONS) { - PyErr_SetString(PyExc_RuntimeError, "Task list appears corrupted"); - set_exception_cause(unwinder, PyExc_RuntimeError, "Task list iteration limit exceeded"); - return -1; - } - - if (GET_MEMBER(uintptr_t, task_node, unwinder->debug_offsets.llist_node.next) == 0) { - PyErr_SetString(PyExc_RuntimeError, - "Invalid linked list structure reading remote memory"); - set_exception_cause(unwinder, PyExc_RuntimeError, "NULL pointer in task linked list"); - return -1; - } - - uintptr_t task_addr = (uintptr_t)GET_MEMBER(uintptr_t, task_node, unwinder->debug_offsets.llist_node.next) - - unwinder->async_debug_offsets.asyncio_task_object.task_node; - - if (process_single_task_node(unwinder, task_addr, NULL, result) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to process task node in awaited_by"); - return -1; - } - - // Read next node - if (_Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, - (uintptr_t)GET_MEMBER(uintptr_t, task_node, unwinder->debug_offsets.llist_node.next), - sizeof(task_node), - task_node) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read next task node in awaited_by"); - return -1; - } - } - - return 0; -} - -static int -append_awaited_by( - RemoteUnwinderObject *unwinder, - unsigned long tid, - uintptr_t head_addr, - PyObject *result) -{ - PyObject *tid_py = PyLong_FromUnsignedLong(tid); - if (tid_py == NULL) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create thread ID object"); - return -1; - } - - PyObject* awaited_by_for_thread = PyList_New(0); - if (awaited_by_for_thread == NULL) { - Py_DECREF(tid_py); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create awaited_by thread list"); - return -1; - } - - RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); - PyObject *result_item = PyStructSequence_New(state->AwaitedInfo_Type); - if (result_item == NULL) { - Py_DECREF(tid_py); - Py_DECREF(awaited_by_for_thread); - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create AwaitedInfo"); - return -1; - } - - PyStructSequence_SetItem(result_item, 0, tid_py); // steals ref - PyStructSequence_SetItem(result_item, 1, awaited_by_for_thread); // steals ref - if (PyList_Append(result, result_item)) { - Py_DECREF(result_item); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append awaited_by result item"); - return -1; - } - Py_DECREF(result_item); - - if (append_awaited_by_for_thread(unwinder, head_addr, awaited_by_for_thread)) - { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append awaited_by for thread"); - return -1; - } - - return 0; -} - -/* ============================================================================ - * STACK UNWINDING FUNCTIONS - * ============================================================================ */ - -static int -process_frame_chain( - RemoteUnwinderObject *unwinder, - uintptr_t initial_frame_addr, - StackChunkList *chunks, - PyObject *frame_info -) { - uintptr_t frame_addr = initial_frame_addr; - uintptr_t prev_frame_addr = 0; - const size_t MAX_FRAMES = 1024; - size_t frame_count = 0; - - while ((void*)frame_addr != NULL) { - PyObject *frame = NULL; - uintptr_t next_frame_addr = 0; - - if (++frame_count > MAX_FRAMES) { - PyErr_SetString(PyExc_RuntimeError, "Too many stack frames (possible infinite loop)"); - set_exception_cause(unwinder, PyExc_RuntimeError, "Frame chain iteration limit exceeded"); - return -1; - } - - // Try chunks first, fallback to direct memory read - if (parse_frame_from_chunks(unwinder, &frame, frame_addr, &next_frame_addr, chunks) < 0) { - PyErr_Clear(); - uintptr_t address_of_code_object = 0; - if (parse_frame_object(unwinder, &frame, frame_addr, &address_of_code_object ,&next_frame_addr) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to parse frame object in chain"); - return -1; - } - } - - if (!frame) { - break; - } - - if (prev_frame_addr && frame_addr != prev_frame_addr) { - PyErr_Format(PyExc_RuntimeError, - "Broken frame chain: expected frame at 0x%lx, got 0x%lx", - prev_frame_addr, frame_addr); - Py_DECREF(frame); - set_exception_cause(unwinder, PyExc_RuntimeError, "Frame chain consistency check failed"); - return -1; - } - - if (PyList_Append(frame_info, frame) == -1) { - Py_DECREF(frame); - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to append frame to frame info list"); - return -1; - } - Py_DECREF(frame); - - prev_frame_addr = next_frame_addr; - frame_addr = next_frame_addr; - } - - return 0; -} - -static PyObject* -unwind_stack_for_thread( - RemoteUnwinderObject *unwinder, - uintptr_t *current_tstate -) { - PyObject *frame_info = NULL; - PyObject *thread_id = NULL; - PyObject *result = NULL; - StackChunkList chunks = {0}; - - char ts[SIZEOF_THREAD_STATE]; - int bytes_read = _Py_RemoteDebug_PagedReadRemoteMemory( - &unwinder->handle, *current_tstate, unwinder->debug_offsets.thread_state.size, ts); - if (bytes_read < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read thread state"); - goto error; - } - - uintptr_t frame_addr = GET_MEMBER(uintptr_t, ts, unwinder->debug_offsets.thread_state.current_frame); - - frame_info = PyList_New(0); - if (!frame_info) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create frame info list"); - goto error; - } - - if (copy_stack_chunks(unwinder, *current_tstate, &chunks) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to copy stack chunks"); - goto error; - } - - if (process_frame_chain(unwinder, frame_addr, &chunks, frame_info) < 0) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to process frame chain"); - goto error; - } - - *current_tstate = GET_MEMBER(uintptr_t, ts, unwinder->debug_offsets.thread_state.next); - - thread_id = PyLong_FromLongLong( - GET_MEMBER(long, ts, unwinder->debug_offsets.thread_state.native_thread_id)); - if (thread_id == NULL) { - set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to create thread ID"); - goto error; - } - - RemoteDebuggingState *state = RemoteDebugging_GetStateFromObject((PyObject*)unwinder); - result = PyStructSequence_New(state->ThreadInfo_Type); - if (result == NULL) { - set_exception_cause(unwinder, PyExc_MemoryError, "Failed to create ThreadInfo"); - goto error; - } - - PyStructSequence_SetItem(result, 0, thread_id); // Steals reference - PyStructSequence_SetItem(result, 1, frame_info); // Steals reference - - cleanup_stack_chunks(&chunks); - return result; - -error: - Py_XDECREF(frame_info); - Py_XDECREF(thread_id); - Py_XDECREF(result); - cleanup_stack_chunks(&chunks); - return NULL; -} - - -/* ============================================================================ - * REMOTEUNWINDER CLASS IMPLEMENTATION - * ============================================================================ */ - -/*[clinic input] -class _remote_debugging.RemoteUnwinder "RemoteUnwinderObject *" "&RemoteUnwinder_Type" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=55f164d8803318be]*/ - -/*[clinic input] -_remote_debugging.RemoteUnwinder.__init__ - pid: int - * - all_threads: bool = False - only_active_thread: bool = False - debug: bool = False - -Initialize a new RemoteUnwinder object for debugging a remote Python process. - -Args: - pid: Process ID of the target Python process to debug - all_threads: If True, initialize state for all threads in the process. - If False, only initialize for the main thread. - only_active_thread: If True, only sample the thread holding the GIL. - Cannot be used together with all_threads=True. - debug: If True, chain exceptions to explain the sequence of events that - lead to the exception. - -The RemoteUnwinder provides functionality to inspect and debug a running Python -process, including examining thread states, stack frames and other runtime data. - -Raises: - PermissionError: If access to the target process is denied - OSError: If unable to attach to the target process or access its memory - RuntimeError: If unable to read debug information from the target process - ValueError: If both all_threads and only_active_thread are True -[clinic start generated code]*/ - -static int -_remote_debugging_RemoteUnwinder___init___impl(RemoteUnwinderObject *self, - int pid, int all_threads, - int only_active_thread, - int debug) -/*[clinic end generated code: output=13ba77598ecdcbe1 input=8f8f12504e17da04]*/ -{ - // Validate that all_threads and only_active_thread are not both True - if (all_threads && only_active_thread) { - PyErr_SetString(PyExc_ValueError, - "all_threads and only_active_thread cannot both be True"); - return -1; - } - -#ifdef Py_GIL_DISABLED - if (only_active_thread) { - PyErr_SetString(PyExc_ValueError, - "only_active_thread is not supported when Py_GIL_DISABLED is not defined"); - return -1; - } -#endif - - self->debug = debug; - self->only_active_thread = only_active_thread; - self->cached_state = NULL; - if (_Py_RemoteDebug_InitProcHandle(&self->handle, pid) < 0) { - set_exception_cause(self, PyExc_RuntimeError, "Failed to initialize process handle"); - return -1; - } - - self->runtime_start_address = _Py_RemoteDebug_GetPyRuntimeAddress(&self->handle); - if (self->runtime_start_address == 0) { - set_exception_cause(self, PyExc_RuntimeError, "Failed to get Python runtime address"); - return -1; - } - - if (_Py_RemoteDebug_ReadDebugOffsets(&self->handle, - &self->runtime_start_address, - &self->debug_offsets) < 0) - { - set_exception_cause(self, PyExc_RuntimeError, "Failed to read debug offsets"); - return -1; - } - - // Validate that the debug offsets are valid - if(validate_debug_offsets(&self->debug_offsets) == -1) { - set_exception_cause(self, PyExc_RuntimeError, "Invalid debug offsets found"); - return -1; - } - - // Try to read async debug offsets, but don't fail if they're not available - self->async_debug_offsets_available = 1; - if (read_async_debug(self) < 0) { - PyErr_Clear(); - memset(&self->async_debug_offsets, 0, sizeof(self->async_debug_offsets)); - self->async_debug_offsets_available = 0; - } - - if (populate_initial_state_data(all_threads, self, self->runtime_start_address, - &self->interpreter_addr ,&self->tstate_addr) < 0) - { - set_exception_cause(self, PyExc_RuntimeError, "Failed to populate initial state data"); - return -1; - } - - self->code_object_cache = _Py_hashtable_new_full( - _Py_hashtable_hash_ptr, - _Py_hashtable_compare_direct, - NULL, // keys are stable pointers, don't destroy - cached_code_metadata_destroy, - NULL - ); - if (self->code_object_cache == NULL) { - PyErr_NoMemory(); - set_exception_cause(self, PyExc_MemoryError, "Failed to create code object cache"); - return -1; - } - -#ifdef Py_GIL_DISABLED - // Initialize TLBC cache - self->tlbc_generation = 0; - self->tlbc_cache = _Py_hashtable_new_full( - _Py_hashtable_hash_ptr, - _Py_hashtable_compare_direct, - NULL, // keys are stable pointers, don't destroy - tlbc_cache_entry_destroy, - NULL - ); - if (self->tlbc_cache == NULL) { - _Py_hashtable_destroy(self->code_object_cache); - PyErr_NoMemory(); - set_exception_cause(self, PyExc_MemoryError, "Failed to create TLBC cache"); - return -1; - } -#endif - - return 0; -} - -/*[clinic input] -@critical_section -_remote_debugging.RemoteUnwinder.get_stack_trace - -Returns a list of stack traces for threads in the target process. - -Each element in the returned list is a tuple of (thread_id, frame_list), where: -- thread_id is the OS thread identifier -- frame_list is a list of tuples (function_name, filename, line_number) representing - the Python stack frames for that thread, ordered from most recent to oldest - -The threads returned depend on the initialization parameters: -- If only_active_thread was True: returns only the thread holding the GIL -- If all_threads was True: returns all threads -- Otherwise: returns only the main thread - -Example: - [ - (1234, [ - ('process_data', 'worker.py', 127), - ('run_worker', 'worker.py', 45), - ('main', 'app.py', 23) - ]), - (1235, [ - ('handle_request', 'server.py', 89), - ('serve_forever', 'server.py', 52) - ]) - ] - -Raises: - RuntimeError: If there is an error copying memory from the target process - OSError: If there is an error accessing the target process - PermissionError: If access to the target process is denied - UnicodeDecodeError: If there is an error decoding strings from the target process - -[clinic start generated code]*/ - -static PyObject * -_remote_debugging_RemoteUnwinder_get_stack_trace_impl(RemoteUnwinderObject *self) -/*[clinic end generated code: output=666192b90c69d567 input=f756f341206f9116]*/ -{ - PyObject* result = NULL; - // Read interpreter state into opaque buffer - char interp_state_buffer[INTERP_STATE_BUFFER_SIZE]; - if (_Py_RemoteDebug_PagedReadRemoteMemory( - &self->handle, - self->interpreter_addr, - INTERP_STATE_BUFFER_SIZE, - interp_state_buffer) < 0) { - set_exception_cause(self, PyExc_RuntimeError, "Failed to read interpreter state buffer"); - goto exit; - } - - // Get code object generation from buffer - uint64_t code_object_generation = GET_MEMBER(uint64_t, interp_state_buffer, - self->debug_offsets.interpreter_state.code_object_generation); - - if (code_object_generation != self->code_object_generation) { - self->code_object_generation = code_object_generation; - _Py_hashtable_clear(self->code_object_cache); - } - - // If only_active_thread is true, we need to determine which thread holds the GIL - PyThreadState* gil_holder = NULL; - if (self->only_active_thread) { - // The GIL state is already in interp_state_buffer, just read from there - // Check if GIL is locked - int gil_locked = GET_MEMBER(int, interp_state_buffer, - self->debug_offsets.interpreter_state.gil_runtime_state_locked); - - if (gil_locked) { - // Get the last holder (current holder when GIL is locked) - gil_holder = GET_MEMBER(PyThreadState*, interp_state_buffer, - self->debug_offsets.interpreter_state.gil_runtime_state_holder); - } else { - // GIL is not locked, return empty list - result = PyList_New(0); - if (!result) { - set_exception_cause(self, PyExc_MemoryError, "Failed to create empty result list"); - } - goto exit; - } - } - -#ifdef Py_GIL_DISABLED - // Check TLBC generation and invalidate cache if needed - uint32_t current_tlbc_generation = GET_MEMBER(uint32_t, interp_state_buffer, - self->debug_offsets.interpreter_state.tlbc_generation); - if (current_tlbc_generation != self->tlbc_generation) { - self->tlbc_generation = current_tlbc_generation; - _Py_hashtable_clear(self->tlbc_cache); - } -#endif - - uintptr_t current_tstate; - if (self->only_active_thread && gil_holder != NULL) { - // We have the GIL holder, process only that thread - current_tstate = (uintptr_t)gil_holder; - } else if (self->tstate_addr == 0) { - // Get threads head from buffer - current_tstate = GET_MEMBER(uintptr_t, interp_state_buffer, - self->debug_offsets.interpreter_state.threads_head); - } else { - current_tstate = self->tstate_addr; - } - - result = PyList_New(0); - if (!result) { - set_exception_cause(self, PyExc_MemoryError, "Failed to create stack trace result list"); - goto exit; - } - - while (current_tstate != 0) { - PyObject* frame_info = unwind_stack_for_thread(self, &current_tstate); - if (!frame_info) { - Py_CLEAR(result); - set_exception_cause(self, PyExc_RuntimeError, "Failed to unwind stack for thread"); - goto exit; - } - - if (PyList_Append(result, frame_info) == -1) { - Py_DECREF(frame_info); - Py_CLEAR(result); - set_exception_cause(self, PyExc_RuntimeError, "Failed to append thread frame info"); - goto exit; - } - Py_DECREF(frame_info); - - // We are targeting a single tstate, break here - if (self->tstate_addr) { - break; - } - - // If we're only processing the GIL holder, we're done after one iteration - if (self->only_active_thread && gil_holder != NULL) { - break; - } - } - -exit: - _Py_RemoteDebug_ClearCache(&self->handle); - return result; -} - -/*[clinic input] -@critical_section -_remote_debugging.RemoteUnwinder.get_all_awaited_by - -Get all tasks and their awaited_by relationships from the remote process. - -This provides a tree structure showing which tasks are waiting for other tasks. - -For each task, returns: -1. The call stack frames leading to where the task is currently executing -2. The name of the task -3. A list of tasks that this task is waiting for, with their own frames/names/etc - -Returns a list of [frames, task_name, subtasks] where: -- frames: List of (func_name, filename, lineno) showing the call stack -- task_name: String identifier for the task -- subtasks: List of tasks being awaited by this task, in same format - -Raises: - RuntimeError: If AsyncioDebug section is not available in the remote process - MemoryError: If memory allocation fails - OSError: If reading from the remote process fails - -Example output: -[ - # Task c2_root waiting for two subtasks - [ - # Call stack of c2_root - [("c5", "script.py", 10), ("c4", "script.py", 14)], - "c2_root", - [ - # First subtask (sub_main_2) and what it's waiting for - [ - [("c1", "script.py", 23)], - "sub_main_2", - [...] - ], - # Second subtask and its waiters - [...] - ] - ] -] -[clinic start generated code]*/ - -static PyObject * -_remote_debugging_RemoteUnwinder_get_all_awaited_by_impl(RemoteUnwinderObject *self) -/*[clinic end generated code: output=6a49cd345e8aec53 input=a452c652bb00701a]*/ -{ - if (!self->async_debug_offsets_available) { - PyErr_SetString(PyExc_RuntimeError, "AsyncioDebug section not available"); - set_exception_cause(self, PyExc_RuntimeError, "AsyncioDebug section unavailable in get_all_awaited_by"); - return NULL; - } - - PyObject *result = PyList_New(0); - if (result == NULL) { - set_exception_cause(self, PyExc_MemoryError, "Failed to create awaited_by result list"); - goto result_err; - } - - // Process all threads - if (iterate_threads(self, process_thread_for_awaited_by, result) < 0) { - goto result_err; - } - - uintptr_t head_addr = self->interpreter_addr - + self->async_debug_offsets.asyncio_interpreter_state.asyncio_tasks_head; - - // On top of a per-thread task lists used by default by asyncio to avoid - // contention, there is also a fallback per-interpreter list of tasks; - // any tasks still pending when a thread is destroyed will be moved to the - // per-interpreter task list. It's unlikely we'll find anything here, but - // interesting for debugging. - if (append_awaited_by(self, 0, head_addr, result)) - { - set_exception_cause(self, PyExc_RuntimeError, "Failed to append interpreter awaited_by in get_all_awaited_by"); - goto result_err; - } - - _Py_RemoteDebug_ClearCache(&self->handle); - return result; - -result_err: - _Py_RemoteDebug_ClearCache(&self->handle); - Py_XDECREF(result); - return NULL; -} - -/*[clinic input] -@critical_section -_remote_debugging.RemoteUnwinder.get_async_stack_trace - -Get the currently running async tasks and their dependency graphs from the remote process. - -This returns information about running tasks and all tasks that are waiting for them, -forming a complete dependency graph for each thread's active task. - -For each thread with a running task, returns the running task plus all tasks that -transitively depend on it (tasks waiting for the running task, tasks waiting for -those tasks, etc.). - -Returns a list of per-thread results, where each thread result contains: -- Thread ID -- List of task information for the running task and all its waiters - -Each task info contains: -- Task ID (memory address) -- Task name -- Call stack frames: List of (func_name, filename, lineno) -- List of tasks waiting for this task (recursive structure) - -Raises: - RuntimeError: If AsyncioDebug section is not available in the target process - MemoryError: If memory allocation fails - OSError: If reading from the remote process fails - -Example output (similar structure to get_all_awaited_by but only for running tasks): -[ - # Thread 140234 results - (140234, [ - # Running task and its complete waiter dependency graph - (4345585712, 'main_task', - [("run_server", "server.py", 127), ("main", "app.py", 23)], - [ - # Tasks waiting for main_task - (4345585800, 'worker_1', [...], [...]), - (4345585900, 'worker_2', [...], [...]) - ]) - ]) -] - -[clinic start generated code]*/ - -static PyObject * -_remote_debugging_RemoteUnwinder_get_async_stack_trace_impl(RemoteUnwinderObject *self) -/*[clinic end generated code: output=6433d52b55e87bbe input=8744b47c9ec2220a]*/ -{ - if (!self->async_debug_offsets_available) { - PyErr_SetString(PyExc_RuntimeError, "AsyncioDebug section not available"); - set_exception_cause(self, PyExc_RuntimeError, "AsyncioDebug section unavailable in get_async_stack_trace"); - return NULL; - } - - PyObject *result = PyList_New(0); - if (result == NULL) { - set_exception_cause(self, PyExc_MemoryError, "Failed to create result list in get_async_stack_trace"); - return NULL; - } - - // Process all threads - if (iterate_threads(self, process_thread_for_async_stack_trace, result) < 0) { - goto result_err; - } - - _Py_RemoteDebug_ClearCache(&self->handle); - return result; -result_err: - _Py_RemoteDebug_ClearCache(&self->handle); - Py_XDECREF(result); - return NULL; -} - -static PyMethodDef RemoteUnwinder_methods[] = { - _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_STACK_TRACE_METHODDEF - _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_ALL_AWAITED_BY_METHODDEF - _REMOTE_DEBUGGING_REMOTEUNWINDER_GET_ASYNC_STACK_TRACE_METHODDEF - {NULL, NULL} -}; - -static void -RemoteUnwinder_dealloc(PyObject *op) -{ - RemoteUnwinderObject *self = RemoteUnwinder_CAST(op); - PyTypeObject *tp = Py_TYPE(self); - if (self->code_object_cache) { - _Py_hashtable_destroy(self->code_object_cache); - } -#ifdef Py_GIL_DISABLED - if (self->tlbc_cache) { - _Py_hashtable_destroy(self->tlbc_cache); - } -#endif - if (self->handle.pid != 0) { - _Py_RemoteDebug_ClearCache(&self->handle); - _Py_RemoteDebug_CleanupProcHandle(&self->handle); - } - PyObject_Del(self); - Py_DECREF(tp); -} - -static PyType_Slot RemoteUnwinder_slots[] = { - {Py_tp_doc, (void *)"RemoteUnwinder(pid): Inspect stack of a remote Python process."}, - {Py_tp_methods, RemoteUnwinder_methods}, - {Py_tp_init, _remote_debugging_RemoteUnwinder___init__}, - {Py_tp_dealloc, RemoteUnwinder_dealloc}, - {0, NULL} -}; - -static PyType_Spec RemoteUnwinder_spec = { - .name = "_remote_debugging.RemoteUnwinder", - .basicsize = sizeof(RemoteUnwinderObject), - .flags = Py_TPFLAGS_DEFAULT, - .slots = RemoteUnwinder_slots, -}; - -/* ============================================================================ - * MODULE INITIALIZATION - * ============================================================================ */ - -static int -_remote_debugging_exec(PyObject *m) -{ - RemoteDebuggingState *st = RemoteDebugging_GetState(m); -#define CREATE_TYPE(mod, type, spec) \ - do { \ - type = (PyTypeObject *)PyType_FromMetaclass(NULL, mod, spec, NULL); \ - if (type == NULL) { \ - return -1; \ - } \ - } while (0) - - CREATE_TYPE(m, st->RemoteDebugging_Type, &RemoteUnwinder_spec); - - if (PyModule_AddType(m, st->RemoteDebugging_Type) < 0) { - return -1; - } - - // Initialize structseq types - st->TaskInfo_Type = PyStructSequence_NewType(&TaskInfo_desc); - if (st->TaskInfo_Type == NULL) { - return -1; - } - if (PyModule_AddType(m, st->TaskInfo_Type) < 0) { - return -1; - } - - st->FrameInfo_Type = PyStructSequence_NewType(&FrameInfo_desc); - if (st->FrameInfo_Type == NULL) { - return -1; - } - if (PyModule_AddType(m, st->FrameInfo_Type) < 0) { - return -1; - } - - st->CoroInfo_Type = PyStructSequence_NewType(&CoroInfo_desc); - if (st->CoroInfo_Type == NULL) { - return -1; - } - if (PyModule_AddType(m, st->CoroInfo_Type) < 0) { - return -1; - } - - st->ThreadInfo_Type = PyStructSequence_NewType(&ThreadInfo_desc); - if (st->ThreadInfo_Type == NULL) { - return -1; - } - if (PyModule_AddType(m, st->ThreadInfo_Type) < 0) { - return -1; - } - - st->AwaitedInfo_Type = PyStructSequence_NewType(&AwaitedInfo_desc); - if (st->AwaitedInfo_Type == NULL) { - return -1; - } - if (PyModule_AddType(m, st->AwaitedInfo_Type) < 0) { - return -1; - } -#ifdef Py_GIL_DISABLED - PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); -#endif - int rc = PyModule_AddIntConstant(m, "PROCESS_VM_READV_SUPPORTED", HAVE_PROCESS_VM_READV); - if (rc < 0) { - return -1; - } - if (RemoteDebugging_InitState(st) < 0) { - return -1; - } - return 0; -} - -static int -remote_debugging_traverse(PyObject *mod, visitproc visit, void *arg) -{ - RemoteDebuggingState *state = RemoteDebugging_GetState(mod); - Py_VISIT(state->RemoteDebugging_Type); - Py_VISIT(state->TaskInfo_Type); - Py_VISIT(state->FrameInfo_Type); - Py_VISIT(state->CoroInfo_Type); - Py_VISIT(state->ThreadInfo_Type); - Py_VISIT(state->AwaitedInfo_Type); - return 0; -} - -static int -remote_debugging_clear(PyObject *mod) -{ - RemoteDebuggingState *state = RemoteDebugging_GetState(mod); - Py_CLEAR(state->RemoteDebugging_Type); - Py_CLEAR(state->TaskInfo_Type); - Py_CLEAR(state->FrameInfo_Type); - Py_CLEAR(state->CoroInfo_Type); - Py_CLEAR(state->ThreadInfo_Type); - Py_CLEAR(state->AwaitedInfo_Type); - return 0; -} - -static void -remote_debugging_free(void *mod) -{ - (void)remote_debugging_clear((PyObject *)mod); -} - -static PyModuleDef_Slot remote_debugging_slots[] = { - {Py_mod_exec, _remote_debugging_exec}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0, NULL}, -}; - -static PyMethodDef remote_debugging_methods[] = { - {NULL, NULL, 0, NULL}, -}; - -static struct PyModuleDef remote_debugging_module = { - PyModuleDef_HEAD_INIT, - .m_name = "_remote_debugging", - .m_size = sizeof(RemoteDebuggingState), - .m_methods = remote_debugging_methods, - .m_slots = remote_debugging_slots, - .m_traverse = remote_debugging_traverse, - .m_clear = remote_debugging_clear, - .m_free = remote_debugging_free, -}; - -PyMODINIT_FUNC -PyInit__remote_debugging(void) -{ - return PyModuleDef_Init(&remote_debugging_module); -} diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c index aafefbf316e03d..d81784409e5d91 100644 --- a/Modules/_sqlite/blob.c +++ b/Modules/_sqlite/blob.c @@ -118,7 +118,9 @@ static void blob_seterror(pysqlite_Blob *self, int rc) { assert(self->connection != NULL); - set_error_from_db(self->connection->state, self->connection->db); + assert(rc != SQLITE_OK); + set_error_from_code(self->connection->state, rc); + assert(PyErr_Occurred()); } static PyObject * @@ -143,23 +145,23 @@ read_multiple(pysqlite_Blob *self, Py_ssize_t length, Py_ssize_t offset) assert(length <= sqlite3_blob_bytes(self->blob)); assert(offset < sqlite3_blob_bytes(self->blob)); - PyObject *buffer = PyBytes_FromStringAndSize(NULL, length); - if (buffer == NULL) { + PyBytesWriter *writer = PyBytesWriter_Create(length); + if (writer == NULL) { return NULL; } + char *raw_buffer = PyBytesWriter_GetData(writer); - char *raw_buffer = PyBytes_AS_STRING(buffer); int rc; Py_BEGIN_ALLOW_THREADS rc = sqlite3_blob_read(self->blob, raw_buffer, (int)length, (int)offset); Py_END_ALLOW_THREADS if (rc != SQLITE_OK) { - Py_DECREF(buffer); + PyBytesWriter_Discard(writer); blob_seterror(self, rc); return NULL; } - return buffer; + return PyBytesWriter_Finish(writer); } @@ -172,14 +174,14 @@ _sqlite3.Blob.read as blob_read Read data at the current offset position. -If the end of the blob is reached, the data up to end of file will be returned. -When length is not specified, or is negative, Blob.read() will read until the -end of the blob. +If the end of the blob is reached, the data up to end of file will +be returned. When length is not specified, or is negative, +Blob.read() will read until the end of the blob. [clinic start generated code]*/ static PyObject * blob_read_impl(pysqlite_Blob *self, int length) -/*[clinic end generated code: output=1fc99b2541360dde input=f2e4aa4378837250]*/ +/*[clinic end generated code: output=1fc99b2541360dde input=6b745ad37720e556]*/ { if (!check_blob(self)) { return NULL; @@ -195,7 +197,7 @@ blob_read_impl(pysqlite_Blob *self, int length) assert(length >= 0); if (length == 0) { - return PyBytes_FromStringAndSize(NULL, 0); + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } PyObject *buffer = read_multiple(self, length, self->offset); @@ -239,13 +241,13 @@ _sqlite3.Blob.write as blob_write Write data at the current offset. -This function cannot change the blob length. Writing beyond the end of the -blob will result in an exception being raised. +This function cannot change the blob length. Writing beyond the end +of the blob will result in an exception being raised. [clinic start generated code]*/ static PyObject * blob_write_impl(pysqlite_Blob *self, Py_buffer *data) -/*[clinic end generated code: output=b34cf22601b570b2 input=a84712f24a028e6d]*/ +/*[clinic end generated code: output=b34cf22601b570b2 input=0d372cb0240a5d49]*/ { if (!check_blob(self)) { return NULL; @@ -269,14 +271,15 @@ _sqlite3.Blob.seek as blob_seek Set the current access position to offset. -The origin argument defaults to os.SEEK_SET (absolute blob positioning). -Other values for origin are os.SEEK_CUR (seek relative to the current position) -and os.SEEK_END (seek relative to the blob's end). +The origin argument defaults to os.SEEK_SET (absolute blob +positioning). Other values for origin are os.SEEK_CUR (seek +relative to the current position) and os.SEEK_END (seek relative to +the blob's end). [clinic start generated code]*/ static PyObject * blob_seek_impl(pysqlite_Blob *self, int offset, int origin) -/*[clinic end generated code: output=854c5a0e208547a5 input=5da9a07e55fe6bb6]*/ +/*[clinic end generated code: output=854c5a0e208547a5 input=84aea1b6b48607dd]*/ { if (!check_blob(self)) { return NULL; @@ -434,23 +437,32 @@ subscript_slice(pysqlite_Blob *self, PyObject *item) return NULL; } + if (len == 0) { + return PyBytes_FromStringAndSize(NULL, 0); + } + if (step == 1) { return read_multiple(self, len, start); } + PyObject *blob = read_multiple(self, stop - start, start); if (blob == NULL) { return NULL; } - PyObject *result = PyBytes_FromStringAndSize(NULL, len); - if (result != NULL) { - char *blob_buf = PyBytes_AS_STRING(blob); - char *res_buf = PyBytes_AS_STRING(result); - for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) { - res_buf[i] = blob_buf[j]; - } + + PyBytesWriter *writer = PyBytesWriter_Create(len); + if (writer == NULL) { Py_DECREF(blob); + return NULL; + } + char *res_buf = PyBytesWriter_GetData(writer); + + char *blob_buf = PyBytes_AS_STRING(blob); + for (Py_ssize_t i = 0, j = 0; i < len; i++, j += step) { + res_buf[i] = blob_buf[j]; } - return result; + Py_DECREF(blob); + return PyBytesWriter_Finish(writer); } static PyObject * @@ -519,21 +531,25 @@ ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value) return -1; } - if (len == 0) { - return 0; - } - Py_buffer vbuf; if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0) { return -1; } - int rc = -1; if (vbuf.len != len) { PyErr_SetString(PyExc_IndexError, "Blob slice assignment is wrong size"); + PyBuffer_Release(&vbuf); + return -1; } - else if (step == 1) { + + if (len == 0) { + PyBuffer_Release(&vbuf); + return 0; + } + + int rc = -1; + if (step == 1) { rc = inner_write(self, vbuf.buf, len, start); } else { diff --git a/Modules/_sqlite/clinic/blob.c.h b/Modules/_sqlite/clinic/blob.c.h index 921e7cbd7ffcab..929703257f04be 100644 --- a/Modules/_sqlite/clinic/blob.c.h +++ b/Modules/_sqlite/clinic/blob.c.h @@ -31,9 +31,9 @@ PyDoc_STRVAR(blob_read__doc__, " length\n" " Read length in bytes.\n" "\n" -"If the end of the blob is reached, the data up to end of file will be returned.\n" -"When length is not specified, or is negative, Blob.read() will read until the\n" -"end of the blob."); +"If the end of the blob is reached, the data up to end of file will\n" +"be returned. When length is not specified, or is negative,\n" +"Blob.read() will read until the end of the blob."); #define BLOB_READ_METHODDEF \ {"read", _PyCFunction_CAST(blob_read), METH_FASTCALL, blob_read__doc__}, @@ -70,8 +70,8 @@ PyDoc_STRVAR(blob_write__doc__, "\n" "Write data at the current offset.\n" "\n" -"This function cannot change the blob length. Writing beyond the end of the\n" -"blob will result in an exception being raised."); +"This function cannot change the blob length. Writing beyond the end\n" +"of the blob will result in an exception being raised."); #define BLOB_WRITE_METHODDEF \ {"write", (PyCFunction)blob_write, METH_O, blob_write__doc__}, @@ -105,9 +105,10 @@ PyDoc_STRVAR(blob_seek__doc__, "\n" "Set the current access position to offset.\n" "\n" -"The origin argument defaults to os.SEEK_SET (absolute blob positioning).\n" -"Other values for origin are os.SEEK_CUR (seek relative to the current position)\n" -"and os.SEEK_END (seek relative to the blob\'s end)."); +"The origin argument defaults to os.SEEK_SET (absolute blob\n" +"positioning). Other values for origin are os.SEEK_CUR (seek\n" +"relative to the current position) and os.SEEK_END (seek relative to\n" +"the blob\'s end)."); #define BLOB_SEEK_METHODDEF \ {"seek", _PyCFunction_CAST(blob_seek), METH_FASTCALL, blob_seek__doc__}, @@ -211,4 +212,4 @@ blob_exit(PyObject *self, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=f03f4ba622b67ae0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b0e3d38063739b17 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/connection.c.h b/Modules/_sqlite/clinic/connection.c.h index f0e9fdb889413f..b645bf3464bcea 100644 --- a/Modules/_sqlite/clinic/connection.c.h +++ b/Modules/_sqlite/clinic/connection.c.h @@ -209,7 +209,7 @@ pysqlite_connection_cursor(PyObject *self, PyObject *const *args, Py_ssize_t nar } PyDoc_STRVAR(blobopen__doc__, -"blobopen($self, table, column, row, /, *, readonly=False, name=\'main\')\n" +"blobopen($self, table, column, rowid, /, *, readonly=False, name=\'main\')\n" "--\n" "\n" "Open and return a BLOB object.\n" @@ -218,8 +218,8 @@ PyDoc_STRVAR(blobopen__doc__, " Table name.\n" " column\n" " Column name.\n" -" row\n" -" Row index.\n" +" rowid\n" +" Row id.\n" " readonly\n" " Open the BLOB without write permissions.\n" " name\n" @@ -685,13 +685,14 @@ PyDoc_STRVAR(pysqlite_connection_set_progress_handler__doc__, "\n" " progress_handler\n" " A callable that takes no arguments.\n" -" If the callable returns non-zero, the current query is terminated,\n" -" and an exception is raised.\n" +" If the callable returns non-zero, the current query is\n" +" terminated, and an exception is raised.\n" " n\n" " The number of SQLite virtual machine instructions that are\n" " executed between invocations of \'progress_handler\'.\n" "\n" -"If \'progress_handler\' is None or \'n\' is 0, the progress handler is disabled."); +"If \'progress_handler\' is None or \'n\' is 0, the progress handler is\n" +"disabled."); #define PYSQLITE_CONNECTION_SET_PROGRESS_HANDLER_METHODDEF \ {"set_progress_handler", _PyCFunction_CAST(pysqlite_connection_set_progress_handler), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pysqlite_connection_set_progress_handler__doc__}, @@ -1303,10 +1304,10 @@ PyDoc_STRVAR(serialize__doc__, " name\n" " Which database to serialize.\n" "\n" -"For an ordinary on-disk database file, the serialization is just a copy of the\n" -"disk file. For an in-memory database or a \"temp\" database, the serialization is\n" -"the same sequence of bytes which would be written to disk if that database\n" -"were backed up to disk."); +"For an ordinary on-disk database file, the serialization is just\n" +"a copy of the disk file. For an in-memory database or a \"temp\"\n" +"database, the serialization is the same sequence of bytes which\n" +"would be written to disk if that database were backed up to disk."); #define SERIALIZE_METHODDEF \ {"serialize", _PyCFunction_CAST(serialize), METH_FASTCALL|METH_KEYWORDS, serialize__doc__}, @@ -1392,12 +1393,13 @@ PyDoc_STRVAR(deserialize__doc__, " name\n" " Which database to reopen with the deserialization.\n" "\n" -"The deserialize interface causes the database connection to disconnect from the\n" -"target database, and then reopen it as an in-memory database based on the given\n" -"serialized data.\n" +"The deserialize interface causes the database connection to\n" +"disconnect from the target database, and then reopen it as\n" +"an in-memory database based on the given serialized data.\n" "\n" -"The deserialize interface will fail with SQLITE_BUSY if the database is\n" -"currently in a read transaction or is involved in a backup operation."); +"The deserialize interface will fail with SQLITE_BUSY if the database\n" +"is currently in a read transaction or is involved in a backup\n" +"operation."); #define DESERIALIZE_METHODDEF \ {"deserialize", _PyCFunction_CAST(deserialize), METH_FASTCALL|METH_KEYWORDS, deserialize__doc__}, @@ -1518,7 +1520,8 @@ PyDoc_STRVAR(pysqlite_connection_exit__doc__, "\n" "Called when the connection is used as a context manager.\n" "\n" -"If there was any exception, a rollback takes place; otherwise we commit."); +"If there was any exception, a rollback takes place; otherwise we\n" +"commit."); #define PYSQLITE_CONNECTION_EXIT_METHODDEF \ {"__exit__", _PyCFunction_CAST(pysqlite_connection_exit), METH_FASTCALL, pysqlite_connection_exit__doc__}, @@ -1556,12 +1559,12 @@ PyDoc_STRVAR(setlimit__doc__, " category\n" " The limit category to be set.\n" " limit\n" -" The new limit. If the new limit is a negative number, the limit is\n" -" unchanged.\n" +" The new limit. If the new limit is a negative number, the limit\n" +" is unchanged.\n" "\n" -"Attempts to increase a limit above its hard upper bound are silently truncated\n" -"to the hard upper bound. Regardless of whether or not the limit was changed,\n" -"the prior value of the limit is returned."); +"Attempts to increase a limit above its hard upper bound are silently\n" +"truncated to the hard upper bound. Regardless of whether or not the\n" +"limit was changed, the prior value of the limit is returned."); #define SETLIMIT_METHODDEF \ {"setlimit", _PyCFunction_CAST(setlimit), METH_FASTCALL, setlimit__doc__}, @@ -1722,4 +1725,4 @@ getconfig(PyObject *self, PyObject *arg) #ifndef DESERIALIZE_METHODDEF #define DESERIALIZE_METHODDEF #endif /* !defined(DESERIALIZE_METHODDEF) */ -/*[clinic end generated code: output=6cb96e557133d553 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1418b72751ef68fc input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/cursor.c.h b/Modules/_sqlite/clinic/cursor.c.h index 350577f488df4b..3cad9f3aef5ecd 100644 --- a/Modules/_sqlite/clinic/cursor.c.h +++ b/Modules/_sqlite/clinic/cursor.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_long.h" // _PyLong_UInt32_Converter() #include "pycore_modsupport.h" // _PyArg_CheckPositional() static int @@ -181,7 +182,7 @@ PyDoc_STRVAR(pysqlite_cursor_fetchmany__doc__, {"fetchmany", _PyCFunction_CAST(pysqlite_cursor_fetchmany), METH_FASTCALL|METH_KEYWORDS, pysqlite_cursor_fetchmany__doc__}, static PyObject * -pysqlite_cursor_fetchmany_impl(pysqlite_Cursor *self, int maxrows); +pysqlite_cursor_fetchmany_impl(pysqlite_Cursor *self, uint32_t maxrows); static PyObject * pysqlite_cursor_fetchmany(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -216,7 +217,7 @@ pysqlite_cursor_fetchmany(PyObject *self, PyObject *const *args, Py_ssize_t narg #undef KWTUPLE PyObject *argsbuf[1]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - int maxrows = ((pysqlite_Cursor *)self)->arraysize; + uint32_t maxrows = ((pysqlite_Cursor *)self)->arraysize; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -226,8 +227,7 @@ pysqlite_cursor_fetchmany(PyObject *self, PyObject *const *args, Py_ssize_t narg if (!noptargs) { goto skip_optional_pos; } - maxrows = PyLong_AsInt(args[0]); - if (maxrows == -1 && PyErr_Occurred()) { + if (!_PyLong_UInt32_Converter(args[0], &maxrows)) { goto exit; } skip_optional_pos: @@ -329,4 +329,46 @@ pysqlite_cursor_close(PyObject *self, PyObject *Py_UNUSED(ignored)) { return pysqlite_cursor_close_impl((pysqlite_Cursor *)self); } -/*[clinic end generated code: output=d05c7cbbc8bcab26 input=a9049054013a1b77]*/ + +#if !defined(_sqlite3_Cursor_arraysize_DOCSTR) +# define _sqlite3_Cursor_arraysize_DOCSTR NULL +#endif +#if defined(_SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF) +# undef _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF +# define _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF {"arraysize", (getter)_sqlite3_Cursor_arraysize_get, (setter)_sqlite3_Cursor_arraysize_set, _sqlite3_Cursor_arraysize_DOCSTR}, +#else +# define _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF {"arraysize", (getter)_sqlite3_Cursor_arraysize_get, NULL, _sqlite3_Cursor_arraysize_DOCSTR}, +#endif + +static PyObject * +_sqlite3_Cursor_arraysize_get_impl(pysqlite_Cursor *self); + +static PyObject * +_sqlite3_Cursor_arraysize_get(PyObject *self, void *Py_UNUSED(context)) +{ + return _sqlite3_Cursor_arraysize_get_impl((pysqlite_Cursor *)self); +} + +#if !defined(_sqlite3_Cursor_arraysize_DOCSTR) +# define _sqlite3_Cursor_arraysize_DOCSTR NULL +#endif +#if defined(_SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF) +# undef _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF +# define _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF {"arraysize", (getter)_sqlite3_Cursor_arraysize_get, (setter)_sqlite3_Cursor_arraysize_set, _sqlite3_Cursor_arraysize_DOCSTR}, +#else +# define _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF {"arraysize", NULL, (setter)_sqlite3_Cursor_arraysize_set, NULL}, +#endif + +static int +_sqlite3_Cursor_arraysize_set_impl(pysqlite_Cursor *self, PyObject *value); + +static int +_sqlite3_Cursor_arraysize_set(PyObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + return_value = _sqlite3_Cursor_arraysize_set_impl((pysqlite_Cursor *)self, value); + + return return_value; +} +/*[clinic end generated code: output=a0e3ebba9e4d0ece input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 16ec6efc85022b..892740b05e55c9 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -38,7 +38,6 @@ #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() #include "pycore_unicodeobject.h" // _PyUnicode_AsUTF8NoNUL -#include "pycore_weakref.h" #include <stdbool.h> @@ -144,8 +143,8 @@ class _sqlite3.Connection "pysqlite_Connection *" "clinic_state()->ConnectionTyp [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=67369db2faf80891]*/ -static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self); -static void free_callback_context(callback_context *ctx); +static void incref_callback_context(callback_context *ctx); +static void decref_callback_context(callback_context *ctx); static void set_callback_context(callback_context **ctx_pp, callback_context *ctx); static int connection_close(pysqlite_Connection *self); @@ -284,17 +283,10 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database, goto error; } - /* Create lists of weak references to cursors and blobs */ - PyObject *cursors = PyList_New(0); - if (cursors == NULL) { - Py_DECREF(statement_cache); - goto error; - } - + /* Create lists of weak references to blobs */ PyObject *blobs = PyList_New(0); if (blobs == NULL) { Py_DECREF(statement_cache); - Py_DECREF(cursors); goto error; } @@ -307,9 +299,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database, self->check_same_thread = check_same_thread; self->thread_ident = PyThread_get_thread_ident(); self->statement_cache = statement_cache; - self->cursors = cursors; self->blobs = blobs; - self->created_cursors = 0; self->row_factory = Py_NewRef(Py_None); self->text_factory = Py_NewRef(&PyUnicode_Type); self->trace_ctx = NULL; @@ -391,7 +381,6 @@ connection_traverse(PyObject *op, visitproc visit, void *arg) pysqlite_Connection *self = _pysqlite_Connection_CAST(op); Py_VISIT(Py_TYPE(self)); Py_VISIT(self->statement_cache); - Py_VISIT(self->cursors); Py_VISIT(self->blobs); Py_VISIT(self->row_factory); Py_VISIT(self->text_factory); @@ -416,7 +405,6 @@ connection_clear(PyObject *op) { pysqlite_Connection *self = _pysqlite_Connection_CAST(op); Py_CLEAR(self->statement_cache); - Py_CLEAR(self->cursors); Py_CLEAR(self->blobs); Py_CLEAR(self->row_factory); Py_CLEAR(self->text_factory); @@ -561,8 +549,6 @@ pysqlite_connection_cursor_impl(pysqlite_Connection *self, PyObject *factory) return NULL; } - _pysqlite_drop_unused_cursor_references(self); - if (cursor && self->row_factory != Py_None) { Py_INCREF(self->row_factory); Py_XSETREF(((pysqlite_Cursor *)cursor)->row_factory, self->row_factory); @@ -571,6 +557,47 @@ pysqlite_connection_cursor_impl(pysqlite_Connection *self, PyObject *factory) return cursor; } +static PyObject * +connection_get_row_factory(PyObject *op, void *closure) +{ + pysqlite_Connection *self = (pysqlite_Connection *)op; + return Py_NewRef(self->row_factory); +} + +static int +connection_set_row_factory(PyObject *op, PyObject *value, void *closure) +{ + pysqlite_Connection *self = (pysqlite_Connection *)op; + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, + "cannot delete row_factory attribute"); + return -1; + } + Py_XSETREF(self->row_factory, Py_NewRef(value)); + return 0; +} + +static PyObject * +connection_get_text_factory(PyObject *op, void *closure) +{ + pysqlite_Connection *self = (pysqlite_Connection *)op; + return Py_NewRef(self->text_factory); +} + +static int +connection_set_text_factory(PyObject *op, PyObject *value, void *closure) +{ + pysqlite_Connection *self = (pysqlite_Connection *)op; + if (value == NULL) { + PyErr_SetString(PyExc_AttributeError, + "cannot delete text_factory attribute"); + return -1; + } + Py_XSETREF(self->text_factory, Py_NewRef(value)); + return 0; +} + + /*[clinic input] _sqlite3.Connection.blobopen as blobopen @@ -578,8 +605,8 @@ _sqlite3.Connection.blobopen as blobopen Table name. column as col: str Column name. - row: sqlite3_int64 - Row index. + rowid as row: sqlite3_int64 + Row id. / * readonly: bool = False @@ -593,7 +620,7 @@ Open and return a BLOB object. static PyObject * blobopen_impl(pysqlite_Connection *self, const char *table, const char *col, sqlite3_int64 row, int readonly, const char *name) -/*[clinic end generated code: output=6a02d43efb885d1c input=23576bd1108d8774]*/ +/*[clinic end generated code: output=6a02d43efb885d1c input=cc3d4b47dac08401]*/ { if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; @@ -933,8 +960,9 @@ func_callback(sqlite3_context *context, int argc, sqlite3_value **argv) args = _pysqlite_build_py_params(context, argc, argv); if (args) { callback_context *ctx = (callback_context *)sqlite3_user_data(context); - assert(ctx != NULL); + incref_callback_context(ctx); py_retval = PyObject_CallObject(ctx->callable, args); + decref_callback_context(ctx); Py_DECREF(args); } @@ -961,7 +989,7 @@ step_callback(sqlite3_context *context, int argc, sqlite3_value **params) PyObject* stepmethod = NULL; callback_context *ctx = (callback_context *)sqlite3_user_data(context); - assert(ctx != NULL); + incref_callback_context(ctx); aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*)); if (aggregate_instance == NULL) { @@ -999,6 +1027,7 @@ step_callback(sqlite3_context *context, int argc, sqlite3_value **params) } error: + decref_callback_context(ctx); Py_XDECREF(stepmethod); Py_XDECREF(function_result); @@ -1030,9 +1059,10 @@ final_callback(sqlite3_context *context) PyObject *exc = PyErr_GetRaisedException(); callback_context *ctx = (callback_context *)sqlite3_user_data(context); - assert(ctx != NULL); + incref_callback_context(ctx); function_result = PyObject_CallMethodNoArgs(*aggregate_instance, ctx->state->str_finalize); + decref_callback_context(ctx); Py_DECREF(*aggregate_instance); ok = 0; @@ -1060,34 +1090,6 @@ final_callback(sqlite3_context *context) PyGILState_Release(threadstate); } -static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self) -{ - /* we only need to do this once in a while */ - if (self->created_cursors++ < 200) { - return; - } - - self->created_cursors = 0; - - PyObject* new_list = PyList_New(0); - if (!new_list) { - return; - } - - for (Py_ssize_t i = 0; i < PyList_Size(self->cursors); i++) { - PyObject* weakref = PyList_GetItem(self->cursors, i); - if (_PyWeakref_IsDead(weakref)) { - continue; - } - if (PyList_Append(new_list, weakref) != 0) { - Py_DECREF(new_list); - return; - } - } - - Py_SETREF(self->cursors, new_list); -} - /* Allocate a UDF/callback context structure. In order to ensure that the state * pointer always outlives the callback context, we make sure it owns a * reference to the module itself. create_callback_context() is always called @@ -1098,12 +1100,16 @@ static callback_context * create_callback_context(PyTypeObject *cls, PyObject *callable) { callback_context *ctx = PyMem_Malloc(sizeof(callback_context)); - if (ctx != NULL) { - PyObject *module = PyType_GetModule(cls); - ctx->callable = Py_NewRef(callable); - ctx->module = Py_NewRef(module); - ctx->state = pysqlite_get_state(module); + if (ctx == NULL) { + PyErr_NoMemory(); + return NULL; } + + PyObject *module = PyType_GetModule(cls); + ctx->refcount = 1; + ctx->callable = Py_NewRef(callable); + ctx->module = Py_NewRef(module); + ctx->state = pysqlite_get_state(module); return ctx; } @@ -1111,11 +1117,33 @@ static void free_callback_context(callback_context *ctx) { assert(ctx != NULL); + assert(ctx->refcount == 0); Py_XDECREF(ctx->callable); Py_XDECREF(ctx->module); PyMem_Free(ctx); } +static inline void +incref_callback_context(callback_context *ctx) +{ + assert(PyGILState_Check()); + assert(ctx != NULL); + assert(ctx->refcount > 0); + ctx->refcount++; +} + +static inline void +decref_callback_context(callback_context *ctx) +{ + assert(PyGILState_Check()); + assert(ctx != NULL); + assert(ctx->refcount > 0); + ctx->refcount--; + if (ctx->refcount == 0) { + free_callback_context(ctx); + } +} + static void set_callback_context(callback_context **ctx_pp, callback_context *ctx) { @@ -1123,7 +1151,7 @@ set_callback_context(callback_context **ctx_pp, callback_context *ctx) callback_context *tmp = *ctx_pp; *ctx_pp = ctx; if (tmp != NULL) { - free_callback_context(tmp); + decref_callback_context(tmp); } } @@ -1134,7 +1162,7 @@ destructor_callback(void *ctx) // This function may be called without the GIL held, so we need to // ensure that we destroy 'ctx' with the GIL held. PyGILState_STATE gstate = PyGILState_Ensure(); - free_callback_context((callback_context *)ctx); + decref_callback_context((callback_context *)ctx); PyGILState_Release(gstate); } } @@ -1195,7 +1223,7 @@ pysqlite_connection_create_function_impl(pysqlite_Connection *self, func_callback, NULL, NULL, - &destructor_callback); // will decref func + &destructor_callback); // will free 'ctx' if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ @@ -1219,7 +1247,7 @@ inverse_callback(sqlite3_context *context, int argc, sqlite3_value **params) PyGILState_STATE gilstate = PyGILState_Ensure(); callback_context *ctx = (callback_context *)sqlite3_user_data(context); - assert(ctx != NULL); + incref_callback_context(ctx); int size = sizeof(PyObject *); PyObject **cls = (PyObject **)sqlite3_aggregate_context(context, size); @@ -1251,6 +1279,7 @@ inverse_callback(sqlite3_context *context, int argc, sqlite3_value **params) Py_DECREF(res); exit: + decref_callback_context(ctx); Py_XDECREF(method); PyGILState_Release(gilstate); } @@ -1267,7 +1296,7 @@ value_callback(sqlite3_context *context) PyGILState_STATE gilstate = PyGILState_Ensure(); callback_context *ctx = (callback_context *)sqlite3_user_data(context); - assert(ctx != NULL); + incref_callback_context(ctx); int size = sizeof(PyObject *); PyObject **cls = (PyObject **)sqlite3_aggregate_context(context, size); @@ -1275,6 +1304,8 @@ value_callback(sqlite3_context *context) assert(*cls != NULL); PyObject *res = PyObject_CallMethodNoArgs(*cls, ctx->state->str_value); + decref_callback_context(ctx); + if (res == NULL) { int attr_err = PyErr_ExceptionMatches(PyExc_AttributeError); set_sqlite_error(context, attr_err @@ -1396,7 +1427,7 @@ pysqlite_connection_create_aggregate_impl(pysqlite_Connection *self, 0, &step_callback, &final_callback, - &destructor_callback); // will decref func + &destructor_callback); // will free 'ctx' if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ PyErr_SetString(self->OperationalError, "Error creating aggregate"); @@ -1406,7 +1437,7 @@ pysqlite_connection_create_aggregate_impl(pysqlite_Connection *self, } static int -authorizer_callback(void *ctx, int action, const char *arg1, +authorizer_callback(void *ctx_vp, int action, const char *arg1, const char *arg2 , const char *dbname, const char *access_attempt_source) { @@ -1415,8 +1446,9 @@ authorizer_callback(void *ctx, int action, const char *arg1, PyObject *ret; int rc = SQLITE_DENY; - assert(ctx != NULL); - PyObject *callable = ((callback_context *)ctx)->callable; + callback_context *ctx = (callback_context *)ctx_vp; + incref_callback_context(ctx); + PyObject *callable = ctx->callable; ret = PyObject_CallFunction(callable, "issss", action, arg1, arg2, dbname, access_attempt_source); @@ -1438,21 +1470,23 @@ authorizer_callback(void *ctx, int action, const char *arg1, Py_DECREF(ret); } + decref_callback_context(ctx); PyGILState_Release(gilstate); return rc; } static int -progress_callback(void *ctx) +progress_callback(void *ctx_vp) { PyGILState_STATE gilstate = PyGILState_Ensure(); int rc; PyObject *ret; - assert(ctx != NULL); - PyObject *callable = ((callback_context *)ctx)->callable; - ret = PyObject_CallNoArgs(callable); + callback_context *ctx = (callback_context *)ctx_vp; + incref_callback_context(ctx); + + ret = PyObject_CallNoArgs(ctx->callable); if (!ret) { /* abort query if error occurred */ rc = -1; @@ -1465,6 +1499,7 @@ progress_callback(void *ctx) print_or_clear_traceback(ctx); } + decref_callback_context(ctx); PyGILState_Release(gilstate); return rc; } @@ -1476,7 +1511,7 @@ progress_callback(void *ctx) * to ensure future compatibility. */ static int -trace_callback(unsigned int type, void *ctx, void *stmt, void *sql) +trace_callback(unsigned int type, void *ctx_vp, void *stmt, void *sql) { if (type != SQLITE_TRACE_STMT) { return 0; @@ -1484,8 +1519,9 @@ trace_callback(unsigned int type, void *ctx, void *stmt, void *sql) PyGILState_STATE gilstate = PyGILState_Ensure(); - assert(ctx != NULL); - pysqlite_state *state = ((callback_context *)ctx)->state; + callback_context *ctx = (callback_context *)ctx_vp; + incref_callback_context(ctx); + pysqlite_state *state = ctx->state; assert(state != NULL); PyObject *py_statement = NULL; @@ -1499,7 +1535,7 @@ trace_callback(unsigned int type, void *ctx, void *stmt, void *sql) PyErr_SetString(state->DataError, "Expanded SQL string exceeds the maximum string length"); - print_or_clear_traceback((callback_context *)ctx); + print_or_clear_traceback(ctx); // Fall back to unexpanded sql py_statement = PyUnicode_FromString((const char *)sql); @@ -1509,16 +1545,16 @@ trace_callback(unsigned int type, void *ctx, void *stmt, void *sql) sqlite3_free((void *)expanded_sql); } if (py_statement) { - PyObject *callable = ((callback_context *)ctx)->callable; - PyObject *ret = PyObject_CallOneArg(callable, py_statement); + PyObject *ret = PyObject_CallOneArg(ctx->callable, py_statement); Py_DECREF(py_statement); Py_XDECREF(ret); } if (PyErr_Occurred()) { - print_or_clear_traceback((callback_context *)ctx); + print_or_clear_traceback(ctx); } exit: + decref_callback_context(ctx); PyGILState_Release(gilstate); return 0; } @@ -1571,8 +1607,8 @@ _sqlite3.Connection.set_progress_handler as pysqlite_connection_set_progress_han cls: defining_class progress_handler as callable: object A callable that takes no arguments. - If the callable returns non-zero, the current query is terminated, - and an exception is raised. + If the callable returns non-zero, the current query is + terminated, and an exception is raised. / n: int The number of SQLite virtual machine instructions that are @@ -1580,14 +1616,15 @@ _sqlite3.Connection.set_progress_handler as pysqlite_connection_set_progress_han Set progress handler callback. -If 'progress_handler' is None or 'n' is 0, the progress handler is disabled. +If 'progress_handler' is None or 'n' is 0, the progress handler is +disabled. [clinic start generated code]*/ static PyObject * pysqlite_connection_set_progress_handler_impl(pysqlite_Connection *self, PyTypeObject *cls, PyObject *callable, int n) -/*[clinic end generated code: output=0739957fd8034a50 input=74c943f1ae7d8880]*/ +/*[clinic end generated code: output=0739957fd8034a50 input=fd0d5abb004f370f]*/ { if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; @@ -1610,6 +1647,7 @@ pysqlite_connection_set_progress_handler_impl(pysqlite_Connection *self, } /*[clinic input] +@permit_long_summary _sqlite3.Connection.set_trace_callback as pysqlite_connection_set_trace_callback cls: defining_class @@ -1623,7 +1661,7 @@ static PyObject * pysqlite_connection_set_trace_callback_impl(pysqlite_Connection *self, PyTypeObject *cls, PyObject *callable) -/*[clinic end generated code: output=d91048c03bfcee05 input=f4f59bf2f87f2026]*/ +/*[clinic end generated code: output=d91048c03bfcee05 input=edfd7d890200a9cb]*/ { if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; @@ -1942,6 +1980,8 @@ collation_callback(void *context, int text1_length, const void *text1_data, PyObject* retval = NULL; long longval; int result = 0; + callback_context *ctx = (callback_context *)context; + incref_callback_context(ctx); /* This callback may be executed multiple times per sqlite3_step(). Bail if * the previous call failed */ @@ -1958,8 +1998,6 @@ collation_callback(void *context, int text1_length, const void *text1_data, goto finally; } - callback_context *ctx = (callback_context *)context; - assert(ctx != NULL); PyObject *args[] = { NULL, string1, string2 }; // Borrowed refs. size_t nargsf = 2 | PY_VECTORCALL_ARGUMENTS_OFFSET; retval = PyObject_Vectorcall(ctx->callable, args + 1, nargsf, NULL); @@ -1981,6 +2019,7 @@ collation_callback(void *context, int text1_length, const void *text1_data, } finally: + decref_callback_context(ctx); Py_XDECREF(string1); Py_XDECREF(string2); Py_XDECREF(retval); @@ -2204,7 +2243,7 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self, * the context before returning. */ if (callable != Py_None) { - free_callback_context(ctx); + decref_callback_context(ctx); } set_error_from_db(self->state, self->db); return NULL; @@ -2223,15 +2262,15 @@ _sqlite3.Connection.serialize as serialize Serialize a database into a byte string. -For an ordinary on-disk database file, the serialization is just a copy of the -disk file. For an in-memory database or a "temp" database, the serialization is -the same sequence of bytes which would be written to disk if that database -were backed up to disk. +For an ordinary on-disk database file, the serialization is just +a copy of the disk file. For an in-memory database or a "temp" +database, the serialization is the same sequence of bytes which +would be written to disk if that database were backed up to disk. [clinic start generated code]*/ static PyObject * serialize_impl(pysqlite_Connection *self, const char *name) -/*[clinic end generated code: output=97342b0e55239dd3 input=d2eb5194a65abe2b]*/ +/*[clinic end generated code: output=97342b0e55239dd3 input=7e48654e8e082fa8]*/ { if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; @@ -2276,18 +2315,19 @@ _sqlite3.Connection.deserialize as deserialize Load a serialized database. -The deserialize interface causes the database connection to disconnect from the -target database, and then reopen it as an in-memory database based on the given -serialized data. +The deserialize interface causes the database connection to +disconnect from the target database, and then reopen it as +an in-memory database based on the given serialized data. -The deserialize interface will fail with SQLITE_BUSY if the database is -currently in a read transaction or is involved in a backup operation. +The deserialize interface will fail with SQLITE_BUSY if the database +is currently in a read transaction or is involved in a backup +operation. [clinic start generated code]*/ static PyObject * deserialize_impl(pysqlite_Connection *self, Py_buffer *data, const char *name) -/*[clinic end generated code: output=e394c798b98bad89 input=1be4ca1faacf28f2]*/ +/*[clinic end generated code: output=e394c798b98bad89 input=5d20e028d98c0686]*/ { if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; @@ -2360,13 +2400,14 @@ _sqlite3.Connection.__exit__ as pysqlite_connection_exit Called when the connection is used as a context manager. -If there was any exception, a rollback takes place; otherwise we commit. +If there was any exception, a rollback takes place; otherwise we +commit. [clinic start generated code]*/ static PyObject * pysqlite_connection_exit_impl(pysqlite_Connection *self, PyObject *exc_type, PyObject *exc_value, PyObject *exc_tb) -/*[clinic end generated code: output=0705200e9321202a input=bd66f1532c9c54a7]*/ +/*[clinic end generated code: output=0705200e9321202a input=8fdb0392ee6f3466]*/ { int commit = 0; PyObject* result; @@ -2406,20 +2447,20 @@ _sqlite3.Connection.setlimit as setlimit category: int The limit category to be set. limit: int - The new limit. If the new limit is a negative number, the limit is - unchanged. + The new limit. If the new limit is a negative number, the limit + is unchanged. / Set connection run-time limits. -Attempts to increase a limit above its hard upper bound are silently truncated -to the hard upper bound. Regardless of whether or not the limit was changed, -the prior value of the limit is returned. +Attempts to increase a limit above its hard upper bound are silently +truncated to the hard upper bound. Regardless of whether or not the +limit was changed, the prior value of the limit is returned. [clinic start generated code]*/ static PyObject * setlimit_impl(pysqlite_Connection *self, int category, int limit) -/*[clinic end generated code: output=0d208213f8d68ccd input=9bd469537e195635]*/ +/*[clinic end generated code: output=0d208213f8d68ccd input=5c2e430091206677]*/ { if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; @@ -2620,6 +2661,10 @@ static PyGetSetDef connection_getset[] = { {"in_transaction", pysqlite_connection_get_in_transaction, NULL}, {"autocommit", get_autocommit, set_autocommit}, {"__text_signature__", get_sig, NULL}, + {"row_factory", connection_get_row_factory, + connection_set_row_factory}, + {"text_factory", connection_get_text_factory, + connection_set_text_factory}, {NULL} }; @@ -2667,8 +2712,6 @@ static struct PyMemberDef connection_members[] = {"InternalError", _Py_T_OBJECT, offsetof(pysqlite_Connection, InternalError), Py_READONLY}, {"ProgrammingError", _Py_T_OBJECT, offsetof(pysqlite_Connection, ProgrammingError), Py_READONLY}, {"NotSupportedError", _Py_T_OBJECT, offsetof(pysqlite_Connection, NotSupportedError), Py_READONLY}, - {"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Connection, row_factory)}, - {"text_factory", _Py_T_OBJECT, offsetof(pysqlite_Connection, text_factory)}, {NULL} }; diff --git a/Modules/_sqlite/connection.h b/Modules/_sqlite/connection.h index 7a748ee3ea0c58..a2241bd540669c 100644 --- a/Modules/_sqlite/connection.h +++ b/Modules/_sqlite/connection.h @@ -36,6 +36,7 @@ typedef struct _callback_context PyObject *callable; PyObject *module; pysqlite_state *state; + Py_ssize_t refcount; } callback_context; enum autocommit_mode { @@ -69,14 +70,9 @@ typedef struct PyObject *statement_cache; - /* Lists of weak references to cursors and blobs used within this connection */ - PyObject *cursors; + /* Lists of weak references to blobs used within this connection */ PyObject *blobs; - /* Counters for how many cursors were created in the connection. May be - * reset to 0 at certain intervals */ - int created_cursors; - PyObject* row_factory; /* Determines how bytestrings from SQLite are converted to Python objects: diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 0c3f43d0e50b43..5a61e43617984d 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -58,34 +58,47 @@ check_cursor_locked(pysqlite_Cursor *cur) return 1; } -/*[clinic input] -module _sqlite3 -class _sqlite3.Cursor "pysqlite_Cursor *" "clinic_state()->CursorType" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=3c5b8115c5cf30f1]*/ - -/* - * Registers a cursor with the connection. - * - * 0 => error; 1 => ok - */ -static int -register_cursor(pysqlite_Connection *connection, PyObject *cursor) +static pysqlite_state * +get_module_state_by_cursor(pysqlite_Cursor *cursor) { - PyObject *weakref = PyWeakref_NewRef((PyObject *)cursor, NULL); - if (weakref == NULL) { - return 0; + if (cursor->connection != NULL && cursor->connection->state != NULL) { + return cursor->connection->state; } + return pysqlite_get_state_by_type(Py_TYPE(cursor)); +} - if (PyList_Append(connection->cursors, weakref) < 0) { - Py_CLEAR(weakref); - return 0; +static void +cursor_sqlite3_internal_error(pysqlite_Cursor *cursor, + const char *error_message, + int chain_exceptions) +{ + pysqlite_state *state = get_module_state_by_cursor(cursor); + if (chain_exceptions) { + assert(PyErr_Occurred()); + PyObject *exc = PyErr_GetRaisedException(); + PyErr_SetString(state->InternalError, error_message); + _PyErr_ChainExceptions1(exc); } + else { + assert(!PyErr_Occurred()); + PyErr_SetString(state->InternalError, error_message); + } +} - Py_DECREF(weakref); - return 1; +static void +cursor_cannot_reset_stmt_error(pysqlite_Cursor *cursor, int chain_exceptions) +{ + cursor_sqlite3_internal_error(cursor, + "cannot reset statement", + chain_exceptions); } +/*[clinic input] +module _sqlite3 +class _sqlite3.Cursor "pysqlite_Cursor *" "clinic_state()->CursorType" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=3c5b8115c5cf30f1]*/ + /*[clinic input] _sqlite3.Cursor.__init__ as pysqlite_cursor_init @@ -125,10 +138,6 @@ pysqlite_cursor_init_impl(pysqlite_Cursor *self, return -1; } - if (!register_cursor(connection, (PyObject *)self)) { - return -1; - } - self->initialized = 1; return 0; @@ -173,8 +182,12 @@ cursor_clear(PyObject *op) Py_CLEAR(self->row_factory); if (self->statement) { /* Reset the statement if the user has not closed the cursor */ - stmt_reset(self->statement); + int rc = stmt_reset(self->statement); Py_CLEAR(self->statement); + if (rc != SQLITE_OK) { + cursor_cannot_reset_stmt_error(self, 0); + PyErr_FormatUnraisable("Exception ignored in cursor_clear()"); + } } return 0; @@ -471,6 +484,9 @@ static int check_cursor(pysqlite_Cursor* cur) return 0; } + assert(cur->connection != NULL); + assert(cur->connection->state != NULL); + if (cur->closed) { PyErr_SetString(cur->connection->state->ProgrammingError, "Cannot operate on a closed cursor."); @@ -567,43 +583,40 @@ bind_param(pysqlite_state *state, pysqlite_Statement *self, int pos, switch (paramtype) { case TYPE_LONG: { sqlite_int64 value = _pysqlite_long_as_int64(parameter); - if (value == -1 && PyErr_Occurred()) - rc = -1; - else - rc = sqlite3_bind_int64(self->st, pos, value); + rc = (value == -1 && PyErr_Occurred()) + ? SQLITE_ERROR + : sqlite3_bind_int64(self->st, pos, value); break; } case TYPE_FLOAT: { double value = PyFloat_AsDouble(parameter); - if (value == -1 && PyErr_Occurred()) { - rc = -1; - } - else { - rc = sqlite3_bind_double(self->st, pos, value); - } + rc = (value == -1 && PyErr_Occurred()) + ? SQLITE_ERROR + : sqlite3_bind_double(self->st, pos, value); break; } case TYPE_UNICODE: string = PyUnicode_AsUTF8AndSize(parameter, &buflen); - if (string == NULL) - return -1; + if (string == NULL) { + return SQLITE_ERROR; + } if (buflen > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "string longer than INT_MAX bytes"); - return -1; + return SQLITE_ERROR; } rc = sqlite3_bind_text(self->st, pos, string, (int)buflen, SQLITE_TRANSIENT); break; case TYPE_BUFFER: { Py_buffer view; if (PyObject_GetBuffer(parameter, &view, PyBUF_SIMPLE) != 0) { - return -1; + return SQLITE_ERROR; } if (view.len > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "BLOB longer than INT_MAX bytes"); PyBuffer_Release(&view); - return -1; + return SQLITE_ERROR; } rc = sqlite3_bind_blob(self->st, pos, view.buf, (int)view.len, SQLITE_TRANSIENT); PyBuffer_Release(&view); @@ -613,7 +626,7 @@ bind_param(pysqlite_state *state, pysqlite_Statement *self, int pos, PyErr_Format(state->ProgrammingError, "Error binding parameter %d: type '%s' is not supported", pos, Py_TYPE(parameter)->tp_name); - rc = -1; + rc = SQLITE_ERROR; } final: @@ -733,14 +746,17 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self, } binding_name++; /* skip first char (the colon) */ - PyObject *current_param; - (void)PyMapping_GetOptionalItemString(parameters, binding_name, &current_param); - if (!current_param) { - if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_LookupError)) { - PyErr_Format(state->ProgrammingError, - "You did not supply a value for binding " - "parameter :%s.", binding_name); - } + PyObject *current_param = NULL; + int found = PyMapping_GetOptionalItemString(parameters, + binding_name, + &current_param); + if (found == -1) { + return; + } + else if (found == 0) { + PyErr_Format(state->ProgrammingError, + "You did not supply a value for binding " + "parameter :%s.", binding_name); return; } @@ -834,7 +850,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation if (self->statement) { // Reset pending statements on this cursor. - (void)stmt_reset(self->statement); + if (stmt_reset(self->statement) != SQLITE_OK) { + goto reset_failure; + } } PyObject *stmt = get_statement_from_cache(self, operation); @@ -858,7 +876,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation } } - (void)stmt_reset(self->statement); + if (stmt_reset(self->statement) != SQLITE_OK) { + goto reset_failure; + } self->rowcount = self->statement->is_dml ? 0L : -1L; /* We start a transaction implicitly before a DML statement. @@ -940,7 +960,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation if (self->statement->is_dml) { self->rowcount += (long)sqlite3_changes(self->connection->db); } - stmt_reset(self->statement); + if (stmt_reset(self->statement) != SQLITE_OK) { + goto reset_failure; + } } Py_XDECREF(parameters); } @@ -965,8 +987,15 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation if (PyErr_Occurred()) { if (self->statement) { - (void)stmt_reset(self->statement); + sqlite3 *db = sqlite3_db_handle(self->statement->st); + int sqlite3_state = sqlite3_errcode(db); + // stmt_reset() may return a previously set exception, + // either triggered because of Python or sqlite3. + rc = stmt_reset(self->statement); Py_CLEAR(self->statement); + if (sqlite3_state == SQLITE_OK && rc != SQLITE_OK) { + cursor_cannot_reset_stmt_error(self, 1); + } } self->rowcount = -1L; return NULL; @@ -975,6 +1004,20 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation Py_CLEAR(self->statement); } return Py_NewRef((PyObject *)self); + +reset_failure: + /* suite to execute when stmt_reset() failed and no exception is set */ + assert(!PyErr_Occurred()); + + Py_XDECREF(parameters); + Py_XDECREF(parameters_iter); + Py_XDECREF(parameters_list); + + self->locked = 0; + self->rowcount = -1L; + Py_CLEAR(self->statement); + cursor_cannot_reset_stmt_error(self, 0); + return NULL; } /*[clinic input] @@ -1117,14 +1160,20 @@ pysqlite_cursor_iternext(PyObject *op) if (self->statement->is_dml) { self->rowcount = (long)sqlite3_changes(self->connection->db); } - (void)stmt_reset(self->statement); + rc = stmt_reset(self->statement); Py_CLEAR(self->statement); + if (rc != SQLITE_OK) { + goto reset_failure; + } } else if (rc != SQLITE_ROW) { - set_error_from_db(self->connection->state, self->connection->db); - (void)stmt_reset(self->statement); + rc = set_error_from_db(self->connection->state, self->connection->db); + int reset_rc = stmt_reset(self->statement); Py_CLEAR(self->statement); Py_DECREF(row); + if (rc == SQLITE_OK && reset_rc != SQLITE_OK) { + goto reset_failure; + } return NULL; } if (!Py_IsNone(self->row_factory)) { @@ -1134,6 +1183,10 @@ pysqlite_cursor_iternext(PyObject *op) Py_SETREF(row, new_row); } return row; + +reset_failure: + cursor_cannot_reset_stmt_error(self, 0); + return NULL; } /*[clinic input] @@ -1159,35 +1212,31 @@ pysqlite_cursor_fetchone_impl(pysqlite_Cursor *self) /*[clinic input] _sqlite3.Cursor.fetchmany as pysqlite_cursor_fetchmany - size as maxrows: int(c_default='((pysqlite_Cursor *)self)->arraysize') = 1 + size as maxrows: uint32(c_default='((pysqlite_Cursor *)self)->arraysize') = 1 The default value is set by the Cursor.arraysize attribute. Fetches several rows from the resultset. [clinic start generated code]*/ static PyObject * -pysqlite_cursor_fetchmany_impl(pysqlite_Cursor *self, int maxrows) -/*[clinic end generated code: output=a8ef31fea64d0906 input=035dbe44a1005bf2]*/ +pysqlite_cursor_fetchmany_impl(pysqlite_Cursor *self, uint32_t maxrows) +/*[clinic end generated code: output=3325f2b477c71baf input=a509c412aa70b27e]*/ { PyObject* row; PyObject* list; - int counter = 0; list = PyList_New(0); if (!list) { return NULL; } - while ((row = pysqlite_cursor_iternext((PyObject *)self))) { - if (PyList_Append(list, row) < 0) { - Py_DECREF(row); - break; - } + while (maxrows > 0 && (row = pysqlite_cursor_iternext((PyObject *)self))) { + int rc = PyList_Append(list, row); Py_DECREF(row); - - if (++counter == maxrows) { + if (rc < 0) { break; } + maxrows--; } if (PyErr_Occurred()) { @@ -1292,8 +1341,15 @@ pysqlite_cursor_close_impl(pysqlite_Cursor *self) } if (self->statement) { - (void)stmt_reset(self->statement); + int rc = stmt_reset(self->statement); + // Force self->statement to be NULL even if stmt_reset() may have + // failed to avoid a possible double-free if someone calls close() + // twice as a leak here would be better than a double-free. Py_CLEAR(self->statement); + if (rc != SQLITE_OK) { + cursor_cannot_reset_stmt_error(self, 0); + return NULL; + } } self->closed = 1; @@ -1301,6 +1357,30 @@ pysqlite_cursor_close_impl(pysqlite_Cursor *self) Py_RETURN_NONE; } +/*[clinic input] +@getter +_sqlite3.Cursor.arraysize +[clinic start generated code]*/ + +static PyObject * +_sqlite3_Cursor_arraysize_get_impl(pysqlite_Cursor *self) +/*[clinic end generated code: output=e0919d97175e6c50 input=3278f8d3ecbd90e3]*/ +{ + return PyLong_FromUInt32(self->arraysize); +} + +/*[clinic input] +@setter +_sqlite3.Cursor.arraysize +[clinic start generated code]*/ + +static int +_sqlite3_Cursor_arraysize_set_impl(pysqlite_Cursor *self, PyObject *value) +/*[clinic end generated code: output=af59a6b09f8cce6e input=ace48cb114e26060]*/ +{ + return PyLong_AsUInt32(value, &self->arraysize); +} + static PyMethodDef cursor_methods[] = { PYSQLITE_CURSOR_CLOSE_METHODDEF PYSQLITE_CURSOR_EXECUTEMANY_METHODDEF @@ -1318,7 +1398,6 @@ static struct PyMemberDef cursor_members[] = { {"connection", _Py_T_OBJECT, offsetof(pysqlite_Cursor, connection), Py_READONLY}, {"description", _Py_T_OBJECT, offsetof(pysqlite_Cursor, description), Py_READONLY}, - {"arraysize", Py_T_INT, offsetof(pysqlite_Cursor, arraysize), 0}, {"lastrowid", _Py_T_OBJECT, offsetof(pysqlite_Cursor, lastrowid), Py_READONLY}, {"rowcount", Py_T_LONG, offsetof(pysqlite_Cursor, rowcount), Py_READONLY}, {"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Cursor, row_factory), 0}, @@ -1326,6 +1405,11 @@ static struct PyMemberDef cursor_members[] = {NULL} }; +static struct PyGetSetDef cursor_getsets[] = { + _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF + {NULL}, +}; + static const char cursor_doc[] = PyDoc_STR("SQLite database cursor class."); @@ -1336,6 +1420,7 @@ static PyType_Slot cursor_slots[] = { {Py_tp_iternext, pysqlite_cursor_iternext}, {Py_tp_methods, cursor_methods}, {Py_tp_members, cursor_members}, + {Py_tp_getset, cursor_getsets}, {Py_tp_init, pysqlite_cursor_init}, {Py_tp_traverse, cursor_traverse}, {Py_tp_clear, cursor_clear}, diff --git a/Modules/_sqlite/cursor.h b/Modules/_sqlite/cursor.h index 42f817af7c54ad..c840a3d7ed0d15 100644 --- a/Modules/_sqlite/cursor.h +++ b/Modules/_sqlite/cursor.h @@ -35,7 +35,7 @@ typedef struct pysqlite_Connection* connection; PyObject* description; PyObject* row_cast_map; - int arraysize; + uint32_t arraysize; PyObject* lastrowid; long rowcount; PyObject* row_factory; diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 5464fd1227ad20..512d9744d57416 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -774,11 +774,11 @@ module_exec(PyObject *module) return 0; error: - sqlite3_shutdown(); return -1; } static struct PyModuleDef_Slot module_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c index 31092417cb480d..0e2812e1e4ffa8 100644 --- a/Modules/_sqlite/prepare_protocol.c +++ b/Modules/_sqlite/prepare_protocol.c @@ -21,18 +21,18 @@ * 3. This notice may not be removed or altered from any source distribution. */ +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + #include "prepare_protocol.h" -static int -pysqlite_prepare_protocol_init(PyObject *self, PyObject *args, PyObject *kwargs) -{ - return 0; -} +#include "pycore_object.h" // _PyObject_VisitType() + static int -pysqlite_prepare_protocol_traverse(PyObject *self, visitproc visit, void *arg) +pysqlite_prepare_protocol_init(PyObject *self, PyObject *args, PyObject *kwargs) { - Py_VISIT(Py_TYPE(self)); return 0; } @@ -50,7 +50,7 @@ PyDoc_STRVAR(doc, "PEP 246 style object adaption protocol type."); static PyType_Slot type_slots[] = { {Py_tp_dealloc, pysqlite_prepare_protocol_dealloc}, {Py_tp_init, pysqlite_prepare_protocol_init}, - {Py_tp_traverse, pysqlite_prepare_protocol_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_doc, (void *)doc}, {0, NULL}, }; diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index 736e60fd778287..f31c699482f601 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -21,10 +21,17 @@ * 3. This notice may not be removed or altered from any source distribution. */ +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + #include "connection.h" #include "statement.h" #include "util.h" +#include "pycore_object.h" // _PyObject_VisitType() + + #define _pysqlite_Statement_CAST(op) ((pysqlite_Statement *)(op)) /* prototypes */ @@ -96,7 +103,12 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql) return self; error: - (void)sqlite3_finalize(stmt); + assert(PyErr_Occurred()); + if (sqlite3_finalize(stmt) != SQLITE_OK) { + PyObject *exc = PyErr_GetRaisedException(); + PyErr_SetString(connection->InternalError, "cannot finalize statement"); + _PyErr_ChainExceptions1(exc); + } return NULL; } @@ -107,22 +119,21 @@ stmt_dealloc(PyObject *op) PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(op); if (self->st) { + int rc; Py_BEGIN_ALLOW_THREADS - sqlite3_finalize(self->st); + rc = sqlite3_finalize(self->st); Py_END_ALLOW_THREADS - self->st = 0; + self->st = NULL; + if (rc != SQLITE_OK) { + pysqlite_state *state = PyType_GetModuleState(Py_TYPE(op)); + PyErr_SetString(state->InternalError, "cannot finalize statement"); + PyErr_FormatUnraisable("Exception ignored in stmt_dealloc()"); + } } tp->tp_free(self); Py_DECREF(tp); } -static int -stmt_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - /* * Strip leading whitespace and comments from incoming SQL (null terminated C * string) and return a pointer to the first non-whitespace, non-comment @@ -183,7 +194,7 @@ lstrip_sql(const char *sql) static PyType_Slot stmt_slots[] = { {Py_tp_dealloc, stmt_dealloc}, - {Py_tp_traverse, stmt_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {0, NULL}, }; diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c index 103248ff55aa0c..177e0f668a0c59 100644 --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -135,14 +135,14 @@ set_error_from_code(pysqlite_state *state, int code) /** * Checks the SQLite error code and sets the appropriate DB-API exception. */ -void +int set_error_from_db(pysqlite_state *state, sqlite3 *db) { int errorcode = sqlite3_errcode(db); PyObject *exc_class = get_exception_class(state, errorcode); if (exc_class == NULL) { // No new exception need be raised. - return; + return SQLITE_OK; } /* Create and set the exception. */ @@ -150,6 +150,7 @@ set_error_from_db(pysqlite_state *state, sqlite3 *db) // sqlite3_errmsg() always returns an UTF-8 encoded message const char *errmsg = sqlite3_errmsg(db); raise_exception(exc_class, extended_errcode, errmsg); + return errorcode; } #ifdef WORDS_BIGENDIAN diff --git a/Modules/_sqlite/util.h b/Modules/_sqlite/util.h index f8e45baffaefe3..c00369496f9968 100644 --- a/Modules/_sqlite/util.h +++ b/Modules/_sqlite/util.h @@ -31,7 +31,7 @@ /** * Checks the SQLite error code and sets the appropriate DB-API exception. */ -void set_error_from_db(pysqlite_state *state, sqlite3 *db); +int set_error_from_db(pysqlite_state *state, sqlite3 *db); void set_error_from_code(pysqlite_state *state, int code); sqlite_int64 _pysqlite_long_as_int64(PyObject * value); diff --git a/Modules/_sre/clinic/sre.c.h b/Modules/_sre/clinic/sre.c.h index d2f25a71495cda..b49bf4e058b69b 100644 --- a/Modules/_sre/clinic/sre.c.h +++ b/Modules/_sre/clinic/sre.c.h @@ -164,22 +164,22 @@ _sre_unicode_tolower(PyObject *module, PyObject *arg) return return_value; } -PyDoc_STRVAR(_sre_SRE_Pattern_match__doc__, -"match($self, /, string, pos=0, endpos=sys.maxsize)\n" +PyDoc_STRVAR(_sre_SRE_Pattern_prefixmatch__doc__, +"prefixmatch($self, /, string, pos=0, endpos=sys.maxsize)\n" "--\n" "\n" "Matches zero or more characters at the beginning of the string."); -#define _SRE_SRE_PATTERN_MATCH_METHODDEF \ - {"match", _PyCFunction_CAST(_sre_SRE_Pattern_match), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _sre_SRE_Pattern_match__doc__}, +#define _SRE_SRE_PATTERN_PREFIXMATCH_METHODDEF \ + {"prefixmatch", _PyCFunction_CAST(_sre_SRE_Pattern_prefixmatch), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _sre_SRE_Pattern_prefixmatch__doc__}, static PyObject * -_sre_SRE_Pattern_match_impl(PatternObject *self, PyTypeObject *cls, - PyObject *string, Py_ssize_t pos, - Py_ssize_t endpos); +_sre_SRE_Pattern_prefixmatch_impl(PatternObject *self, PyTypeObject *cls, + PyObject *string, Py_ssize_t pos, + Py_ssize_t endpos); static PyObject * -_sre_SRE_Pattern_match(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +_sre_SRE_Pattern_prefixmatch(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -205,7 +205,7 @@ _sre_SRE_Pattern_match(PyObject *self, PyTypeObject *cls, PyObject *const *args, static const char * const _keywords[] = {"string", "pos", "endpos", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, - .fname = "match", + .fname = "prefixmatch", .kwtuple = KWTUPLE, }; #undef KWTUPLE @@ -254,7 +254,7 @@ _sre_SRE_Pattern_match(PyObject *self, PyTypeObject *cls, PyObject *const *args, endpos = ival; } skip_optional_pos: - return_value = _sre_SRE_Pattern_match_impl((PatternObject *)self, cls, string, pos, endpos); + return_value = _sre_SRE_Pattern_prefixmatch_impl((PatternObject *)self, cls, string, pos, endpos); exit: return return_value; @@ -1523,25 +1523,25 @@ _sre_SRE_Match___deepcopy__(PyObject *self, PyObject *memo) return return_value; } -PyDoc_STRVAR(_sre_SRE_Scanner_match__doc__, -"match($self, /)\n" +PyDoc_STRVAR(_sre_SRE_Scanner_prefixmatch__doc__, +"prefixmatch($self, /)\n" "--\n" "\n"); -#define _SRE_SRE_SCANNER_MATCH_METHODDEF \ - {"match", _PyCFunction_CAST(_sre_SRE_Scanner_match), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _sre_SRE_Scanner_match__doc__}, +#define _SRE_SRE_SCANNER_PREFIXMATCH_METHODDEF \ + {"prefixmatch", _PyCFunction_CAST(_sre_SRE_Scanner_prefixmatch), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _sre_SRE_Scanner_prefixmatch__doc__}, static PyObject * -_sre_SRE_Scanner_match_impl(ScannerObject *self, PyTypeObject *cls); +_sre_SRE_Scanner_prefixmatch_impl(ScannerObject *self, PyTypeObject *cls); static PyObject * -_sre_SRE_Scanner_match(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +_sre_SRE_Scanner_prefixmatch(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { - PyErr_SetString(PyExc_TypeError, "match() takes no arguments"); + PyErr_SetString(PyExc_TypeError, "prefixmatch() takes no arguments"); return NULL; } - return _sre_SRE_Scanner_match_impl((ScannerObject *)self, cls); + return _sre_SRE_Scanner_prefixmatch_impl((ScannerObject *)self, cls); } PyDoc_STRVAR(_sre_SRE_Scanner_search__doc__, @@ -1568,4 +1568,4 @@ _sre_SRE_Scanner_search(PyObject *self, PyTypeObject *cls, PyObject *const *args #ifndef _SRE_SRE_PATTERN__FAIL_AFTER_METHODDEF #define _SRE_SRE_PATTERN__FAIL_AFTER_METHODDEF #endif /* !defined(_SRE_SRE_PATTERN__FAIL_AFTER_METHODDEF) */ -/*[clinic end generated code: output=bbf42e1de3bdd3ae input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0c867efb64e020aa input=a9049054013a1b77]*/ diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index e8943920043906..32aa06bed4a409 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -42,7 +42,9 @@ static const char copyright[] = #include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION #include "pycore_dict.h" // _PyDict_Next() #include "pycore_long.h" // _PyLong_GetZero() +#include "pycore_list.h" // _PyList_AppendTakeRef() #include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #include "pycore_unicodeobject.h" // _PyUnicode_Copy #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() @@ -547,10 +549,16 @@ state_init(SRE_STATE* state, PatternObject* pattern, PyObject* string, memset(state, 0, sizeof(SRE_STATE)); - state->mark = PyMem_New(const void *, pattern->groups * 2); - if (!state->mark) { - PyErr_NoMemory(); - goto err; + /* Patterns with no capturing groups never emit MARK opcodes and never + read state->mark (group 0's span comes from state->start/ptr), so skip + the allocation entirely -- state->mark stays NULL, which both the err + path and state_fini already free safely. */ + if (pattern->groups) { + state->mark = PyMem_New(const void *, pattern->groups * 2); + if (!state->mark) { + PyErr_NoMemory(); + goto err; + } } state->lastmark = -1; state->lastindex = -1; @@ -766,7 +774,7 @@ sre_search(SRE_STATE* state, SRE_CODE* pattern) } /*[clinic input] -_sre.SRE_Pattern.match +_sre.SRE_Pattern.prefixmatch cls: defining_class / @@ -778,10 +786,10 @@ Matches zero or more characters at the beginning of the string. [clinic start generated code]*/ static PyObject * -_sre_SRE_Pattern_match_impl(PatternObject *self, PyTypeObject *cls, - PyObject *string, Py_ssize_t pos, - Py_ssize_t endpos) -/*[clinic end generated code: output=ec6208ea58a0cca0 input=4bdb9c3e564d13ac]*/ +_sre_SRE_Pattern_prefixmatch_impl(PatternObject *self, PyTypeObject *cls, + PyObject *string, Py_ssize_t pos, + Py_ssize_t endpos) +/*[clinic end generated code: output=a0e079fb4f875240 input=e2a7e68ea47d048c]*/ { _sremodulestate *module_state = get_sre_module_state_by_class(cls); SRE_STATE state; @@ -809,6 +817,7 @@ _sre_SRE_Pattern_match_impl(PatternObject *self, PyTypeObject *cls, return match; } + /*[clinic input] _sre.SRE_Pattern.fullmatch @@ -855,6 +864,7 @@ _sre_SRE_Pattern_fullmatch_impl(PatternObject *self, PyTypeObject *cls, } /*[clinic input] +@permit_long_summary _sre.SRE_Pattern.search cls: defining_class @@ -872,7 +882,7 @@ static PyObject * _sre_SRE_Pattern_search_impl(PatternObject *self, PyTypeObject *cls, PyObject *string, Py_ssize_t pos, Py_ssize_t endpos) -/*[clinic end generated code: output=bd7f2d9d583e1463 input=afa9afb66a74a4b3]*/ +/*[clinic end generated code: output=bd7f2d9d583e1463 input=05e9feee0334c156]*/ { _sremodulestate *module_state = get_sre_module_state_by_class(cls); SRE_STATE state; @@ -977,8 +987,7 @@ _sre_SRE_Pattern_findall_impl(PatternObject *self, PyObject *string, break; } - status = PyList_Append(list, item); - Py_DECREF(item); + status = _PyList_AppendTakeRef((PyListObject *)list, item); if (status < 0) goto error; @@ -997,6 +1006,7 @@ _sre_SRE_Pattern_findall_impl(PatternObject *self, PyObject *string, } /*[clinic input] +@permit_long_summary _sre.SRE_Pattern.finditer cls: defining_class @@ -1014,7 +1024,7 @@ static PyObject * _sre_SRE_Pattern_finditer_impl(PatternObject *self, PyTypeObject *cls, PyObject *string, Py_ssize_t pos, Py_ssize_t endpos) -/*[clinic end generated code: output=1791dbf3618ade56 input=812e332a4848cbaf]*/ +/*[clinic end generated code: output=1791dbf3618ade56 input=ee28865796048023]*/ { _sremodulestate *module_state = get_sre_module_state_by_class(cls); PyObject* scanner; @@ -1323,8 +1333,7 @@ pattern_subx(_sremodulestate* module_state, string, i, b); if (!item) goto error; - status = PyList_Append(list, item); - Py_DECREF(item); + status = _PyList_AppendTakeRef((PyListObject *)list, item); if (status < 0) goto error; @@ -1353,8 +1362,7 @@ pattern_subx(_sremodulestate* module_state, /* add to list */ if (item != Py_None) { - status = PyList_Append(list, item); - Py_DECREF(item); + status = _PyList_AppendTakeRef((PyListObject *)list, item); if (status < 0) goto error; } @@ -1371,8 +1379,7 @@ pattern_subx(_sremodulestate* module_state, string, i, state.endpos); if (!item) goto error; - status = PyList_Append(list, item); - Py_DECREF(item); + status = _PyList_AppendTakeRef((PyListObject *)list, item); if (status < 0) goto error; } @@ -1416,6 +1423,7 @@ pattern_subx(_sremodulestate* module_state, } /*[clinic input] +@permit_long_summary _sre.SRE_Pattern.sub cls: defining_class @@ -1430,7 +1438,7 @@ Return the string obtained by replacing the leftmost non-overlapping occurrences static PyObject * _sre_SRE_Pattern_sub_impl(PatternObject *self, PyTypeObject *cls, PyObject *repl, PyObject *string, Py_ssize_t count) -/*[clinic end generated code: output=4be141ab04bca60d input=d8d1d4ac2311a07c]*/ +/*[clinic end generated code: output=4be141ab04bca60d input=eba511fd1c4908b7]*/ { _sremodulestate *module_state = get_sre_module_state_by_class(cls); @@ -1438,6 +1446,7 @@ _sre_SRE_Pattern_sub_impl(PatternObject *self, PyTypeObject *cls, } /*[clinic input] +@permit_long_summary _sre.SRE_Pattern.subn cls: defining_class @@ -1453,7 +1462,7 @@ static PyObject * _sre_SRE_Pattern_subn_impl(PatternObject *self, PyTypeObject *cls, PyObject *repl, PyObject *string, Py_ssize_t count) -/*[clinic end generated code: output=da02fd85258b1e1f input=8b78a65b8302e58d]*/ +/*[clinic end generated code: output=da02fd85258b1e1f input=6a5bb5b61717abf0]*/ { _sremodulestate *module_state = get_sre_module_state_by_class(cls); @@ -1942,7 +1951,7 @@ _validate_inner(SRE_CODE *code, SRE_CODE *end, Py_ssize_t groups) sre_match() code is robust even if they don't, and the worst you can get is nonsensical match results. */ GET_ARG; - if (arg > 2 * (size_t)groups + 1) { + if (arg >= 2 * (size_t)groups) { VTRACE(("arg=%d, groups=%d\n", (int)arg, (int)groups)); FAIL; } @@ -2355,7 +2364,7 @@ match_getindex(MatchObject* self, PyObject* index) } // Check that i*2 cannot overflow to make static analyzers happy - assert(i <= SRE_MAXGROUPS); + assert((size_t)i <= SRE_MAXGROUPS); return i; } @@ -2372,6 +2381,7 @@ match_getslice(MatchObject* self, PyObject* index, PyObject* def) } /*[clinic input] +@permit_long_summary _sre.SRE_Match.expand template: object @@ -2381,7 +2391,7 @@ Return the string obtained by doing backslash substitution on the string templat static PyObject * _sre_SRE_Match_expand_impl(MatchObject *self, PyObject *template) -/*[clinic end generated code: output=931b58ccc323c3a1 input=4bfdb22c2f8b146a]*/ +/*[clinic end generated code: output=931b58ccc323c3a1 input=dc74d81265376ac3]*/ { _sremodulestate *module_state = get_sre_module_state_by_class(Py_TYPE(self)); PyObject *filter = compile_template(module_state, self->pattern, template); @@ -2470,6 +2480,7 @@ _sre_SRE_Match_groups_impl(MatchObject *self, PyObject *default_value) } /*[clinic input] +@permit_long_summary _sre.SRE_Match.groupdict default: object = None @@ -2480,7 +2491,7 @@ Return a dictionary containing all the named subgroups of the match, keyed by th static PyObject * _sre_SRE_Match_groupdict_impl(MatchObject *self, PyObject *default_value) -/*[clinic end generated code: output=29917c9073e41757 input=0ded7960b23780aa]*/ +/*[clinic end generated code: output=29917c9073e41757 input=a8d3a1dc80336872]*/ { PyObject *result; PyObject *key; @@ -2565,31 +2576,21 @@ _sre_SRE_Match_end_impl(MatchObject *self, PyObject *group) LOCAL(PyObject*) _pair(Py_ssize_t i1, Py_ssize_t i2) { - PyObject* pair; - PyObject* item; - - pair = PyTuple_New(2); - if (!pair) + PyObject* item1 = PyLong_FromSsize_t(i1); + if (!item1) { return NULL; + } + PyObject* item2 = PyLong_FromSsize_t(i2); + if(!item2) { + Py_DECREF(item1); + return NULL; + } - item = PyLong_FromSsize_t(i1); - if (!item) - goto error; - PyTuple_SET_ITEM(pair, 0, item); - - item = PyLong_FromSsize_t(i2); - if (!item) - goto error; - PyTuple_SET_ITEM(pair, 1, item); - - return pair; - - error: - Py_DECREF(pair); - return NULL; + return _PyTuple_FromPairSteal(item1, item2); } /*[clinic input] +@permit_long_summary _sre.SRE_Match.span group: object(c_default="NULL") = 0 @@ -2600,7 +2601,7 @@ For match object m, return the 2-tuple (m.start(group), m.end(group)). static PyObject * _sre_SRE_Match_span_impl(MatchObject *self, PyObject *group) -/*[clinic end generated code: output=f02ae40594d14fe6 input=8fa6014e982d71d4]*/ +/*[clinic end generated code: output=f02ae40594d14fe6 input=834cfe444f0f55cf]*/ { Py_ssize_t index = match_getindex(self, group); @@ -2665,7 +2666,7 @@ _sre_SRE_Match___deepcopy___impl(MatchObject *self, PyObject *memo) } PyDoc_STRVAR(match_doc, -"The result of re.match() and re.search().\n\ +"The result of re.search(), re.prefixmatch(), and re.fullmatch().\n\ Match objects always have a boolean value of True."); PyDoc_STRVAR(match_group_doc, @@ -2835,24 +2836,29 @@ scanner_dealloc(PyObject *self) static int scanner_begin(ScannerObject* self) { - if (self->executing) { +#ifdef Py_GIL_DISABLED + int was_executing = _Py_atomic_exchange_int(&self->executing, 1); +#else + int was_executing = self->executing; + self->executing = 1; +#endif + if (was_executing) { PyErr_SetString(PyExc_ValueError, "regular expression scanner already executing"); return 0; } - self->executing = 1; return 1; } static void scanner_end(ScannerObject* self) { - assert(self->executing); - self->executing = 0; + assert(FT_ATOMIC_LOAD_INT_RELAXED(self->executing)); + FT_ATOMIC_STORE_INT(self->executing, 0); } /*[clinic input] -_sre.SRE_Scanner.match +_sre.SRE_Scanner.prefixmatch cls: defining_class / @@ -2860,8 +2866,8 @@ _sre.SRE_Scanner.match [clinic start generated code]*/ static PyObject * -_sre_SRE_Scanner_match_impl(ScannerObject *self, PyTypeObject *cls) -/*[clinic end generated code: output=6e22c149dc0f0325 input=b5146e1f30278cb7]*/ +_sre_SRE_Scanner_prefixmatch_impl(ScannerObject *self, PyTypeObject *cls) +/*[clinic end generated code: output=02b3b9d2954a2157 input=3049b20466c56a8e]*/ { _sremodulestate *module_state = get_sre_module_state_by_class(cls); SRE_STATE* state = &self->state; @@ -3159,7 +3165,12 @@ pattern_richcompare(PyObject *lefto, PyObject *righto, int op) #include "clinic/sre.c.h" static PyMethodDef pattern_methods[] = { - _SRE_SRE_PATTERN_MATCH_METHODDEF + _SRE_SRE_PATTERN_PREFIXMATCH_METHODDEF + /* "match" reuses the prefixmatch Clinic-generated parser and impl + * to avoid duplicating the argument parsing boilerplate code. */ + {"match", _PyCFunction_CAST(_sre_SRE_Pattern_prefixmatch), + METH_METHOD|METH_FASTCALL|METH_KEYWORDS, + _sre_SRE_Pattern_prefixmatch__doc__}, _SRE_SRE_PATTERN_FULLMATCH_METHODDEF _SRE_SRE_PATTERN_SEARCH_METHODDEF _SRE_SRE_PATTERN_SUB_METHODDEF @@ -3172,7 +3183,7 @@ static PyMethodDef pattern_methods[] = { _SRE_SRE_PATTERN___DEEPCOPY___METHODDEF _SRE_SRE_PATTERN__FAIL_AFTER_METHODDEF {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, - PyDoc_STR("See PEP 585")}, + PyDoc_STR("Patterns are generic over the type of string they handle (str or bytes)")}, {NULL, NULL} }; @@ -3228,7 +3239,7 @@ static PyMethodDef match_methods[] = { _SRE_SRE_MATCH___COPY___METHODDEF _SRE_SRE_MATCH___DEEPCOPY___METHODDEF {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, - PyDoc_STR("See PEP 585")}, + PyDoc_STR("Matches are generic over the type of string which was matched (str or bytes)")}, {NULL, NULL} }; @@ -3286,7 +3297,12 @@ static PyType_Spec match_spec = { }; static PyMethodDef scanner_methods[] = { - _SRE_SRE_SCANNER_MATCH_METHODDEF + _SRE_SRE_SCANNER_PREFIXMATCH_METHODDEF + /* "match" reuses the prefixmatch Clinic-generated parser and impl + * to avoid duplicating the argument parsing boilerplate code. */ + {"match", _PyCFunction_CAST(_sre_SRE_Scanner_prefixmatch), + METH_METHOD|METH_FASTCALL|METH_KEYWORDS, + _sre_SRE_Scanner_prefixmatch__doc__}, _SRE_SRE_SCANNER_SEARCH_METHODDEF {NULL, NULL} }; @@ -3390,11 +3406,31 @@ do { \ } \ } while (0) + +#ifdef Py_DEBUG +static void +_assert_match_aliases_prefixmatch(PyMethodDef *methods) +{ + PyMethodDef *prefixmatch_md = &methods[0]; + PyMethodDef *match_md = &methods[1]; + assert(strcmp(prefixmatch_md->ml_name, "prefixmatch") == 0); + assert(strcmp(match_md->ml_name, "match") == 0); + assert(match_md->ml_meth == prefixmatch_md->ml_meth); + assert(match_md->ml_flags == prefixmatch_md->ml_flags); + assert(match_md->ml_doc == prefixmatch_md->ml_doc); +} +#endif + static int sre_exec(PyObject *m) { _sremodulestate *state; +#ifdef Py_DEBUG + _assert_match_aliases_prefixmatch(pattern_methods); + _assert_match_aliases_prefixmatch(scanner_methods); +#endif + /* Create heap types */ state = get_sre_module_state(m); CREATE_TYPE(m, state->Pattern_Type, &pattern_spec); @@ -3424,6 +3460,7 @@ sre_exec(PyObject *m) } static PyModuleDef_Slot sre_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, sre_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 24c243e330d4bf..f451c0ce7364ab 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -26,10 +26,12 @@ #define OPENSSL_NO_DEPRECATED 1 #include "Python.h" +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() #include "pycore_fileutils.h" // _PyIsSelectable_fd() #include "pycore_long.h" // _PyLong_UnsignedLongLong_Converter() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_time.h" // _PyDeadline_Init() +#include "pycore_tuple.h" // _PyTuple_FromPair /* Include symbols from _socket module */ #include "socketmodule.h" @@ -47,10 +49,13 @@ do { (save) = PyEval_SaveThread(); } while(0) #define PySSL_END_ALLOW_THREADS_S(save) \ do { PyEval_RestoreThread(save); _PySSL_FIX_ERRNO; } while(0) -#define PySSL_BEGIN_ALLOW_THREADS { \ +#define PySSL_BEGIN_ALLOW_THREADS(self) { \ PyThreadState *_save = NULL; \ - PySSL_BEGIN_ALLOW_THREADS_S(_save); -#define PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS_S(_save); } + PySSL_BEGIN_ALLOW_THREADS_S(_save); \ + PyMutex_Lock(&(self)->tstate_mutex); +#define PySSL_END_ALLOW_THREADS(self) \ + PyMutex_Unlock(&(self)->tstate_mutex); \ + PySSL_END_ALLOW_THREADS_S(_save); } #if defined(HAVE_POLL_H) #include <poll.h> @@ -148,17 +153,30 @@ static void _PySSLFixErrno(void) { #endif /* Include generated data (error codes) */ -/* See make_ssl_data.h for notes on adding a new version. */ -#if (OPENSSL_VERSION_NUMBER >= 0x30401000L) -#include "_ssl_data_34.h" +/* See Tools/ssl/make_ssl_data.py for notes on adding a new version. */ +#if (OPENSSL_VERSION_NUMBER >= 0x40000000L) +# include "_ssl_data_40.h" +#elif (OPENSSL_VERSION_NUMBER >= 0x30401000L) +# include "_ssl_data_36.h" #elif (OPENSSL_VERSION_NUMBER >= 0x30100000L) -#include "_ssl_data_340.h" +# include "_ssl_data_340.h" #elif (OPENSSL_VERSION_NUMBER >= 0x30000000L) -#include "_ssl_data_300.h" +# include "_ssl_data_300.h" #elif (OPENSSL_VERSION_NUMBER >= 0x10101000L) -#include "_ssl_data_111.h" +# include "_ssl_data_111.h" #else -#error Unsupported OpenSSL version +# error Unsupported OpenSSL version +#endif + +#if (OPENSSL_VERSION_NUMBER >= 0x40000000L) +# define OPENSSL_NO_SSL3 +# define OPENSSL_NO_TLS1 +# define OPENSSL_NO_TLS1_1 +# define OPENSSL_NO_TLS1_2 +# define OPENSSL_NO_SSL3_METHOD +# define OPENSSL_NO_TLS1_METHOD +# define OPENSSL_NO_TLS1_1_METHOD +# define OPENSSL_NO_TLS1_2_METHOD #endif /* OpenSSL API 1.1.0+ does not include version methods */ @@ -328,7 +346,7 @@ typedef struct { int post_handshake_auth; #endif PyObject *msg_cb; - PyObject *keylog_filename; + PyObject *keylog_filename; // can be anything accepted by Py_fopen() BIO *keylog_bio; /* Cached module state, also used in SSLSocket and SSLSession code. */ _sslmodulestate *state; @@ -336,6 +354,9 @@ typedef struct { PyObject *psk_client_callback; PyObject *psk_server_callback; #endif + /* Lock to synchronize calls when the thread state is detached. + See also gh-134698. */ + PyMutex tstate_mutex; } PySSLContext; #define PySSLContext_CAST(op) ((PySSLContext *)(op)) @@ -355,14 +376,18 @@ typedef struct { PySSLContext *ctx; /* weakref to SSL context */ char shutdown_seen_zero; enum py_ssl_server_or_client socket_type; - PyObject *owner; /* Python level "owner" passed to servername callback */ + PyObject *owner; /* weakref to Python level "owner" passed to servername callback */ PyObject *server_hostname; - _PySSLError err; /* last seen error from various sources */ - /* Some SSL callbacks don't have error reporting. Callback wrappers - * store exception information on the socket. The handshake, read, write, - * and shutdown methods check for chained exceptions. - */ - PyObject *exc; + // gh-148292: If non-zero, read(), sendfile(), write() and do_handshake() + // methods raise SSLEOFError without calling the underlying OpenSSL + // function. Set to 1 on PY_SSL_ERROR_EOF error. + // + // On OpenSSL 4, if SSL_read_ex() fails with + // SSL_R_UNEXPECTED_EOF_WHILE_READING, the following SSL_read_ex() call + // fails with a generic protocol error (ERR_peek_last_error() returns 0). + // Use got_eof_error to have the same behavior on OpenSSL 4 and newer and + // on OpenSSL 3 and older. + int got_eof_error; } PySSLSocket; #define PySSLSocket_CAST(op) ((PySSLSocket *)(op)) @@ -423,26 +448,6 @@ typedef enum { #define ERRSTR1(x,y,z) (x ":" y ": " z) #define ERRSTR(x) ERRSTR1("_ssl.c", Py_STRINGIFY(__LINE__), x) -// Get the socket from a PySSLSocket, if it has one. -// Return a borrowed reference. -static inline PySocketSockObject* GET_SOCKET(PySSLSocket *obj) { - if (obj->Socket) { - PyObject *sock; - if (PyWeakref_GetRef(obj->Socket, &sock)) { - // GET_SOCKET() returns a borrowed reference - Py_DECREF(sock); - } - else { - // dead weak reference - sock = Py_None; - } - return (PySocketSockObject *)sock; // borrowed reference - } - else { - return NULL; - } -} - /* If sock is NULL, use a timeout of 0 second */ #define GET_SOCKET_TIMEOUT(sock) \ ((sock != NULL) ? (sock)->sock_timeout : 0) @@ -510,6 +515,10 @@ fill_and_set_sslerror(_sslmodulestate *state, PyObject *init_value, *msg, *key; PyUnicodeWriter *writer = NULL; + if (ssl_errno == PY_SSL_ERROR_EOF && sslsock != NULL) { + sslsock->got_eof_error = 1; + } + if (errcode != 0) { int lib, reason; @@ -601,7 +610,7 @@ fill_and_set_sslerror(_sslmodulestate *state, } else { if (PyUnicodeWriter_Format( - writer, "unknown error (0x%x)", errcode) < 0) { + writer, "unknown error (0x%lx)", errcode) < 0) { goto fail; } } @@ -655,22 +664,27 @@ fill_and_set_sslerror(_sslmodulestate *state, PyUnicodeWriter_Discard(writer); } -static int -PySSL_ChainExceptions(PySSLSocket *sslsock) { - if (sslsock->exc == NULL) - return 0; - _PyErr_ChainExceptions1(sslsock->exc); - sslsock->exc = NULL; - return -1; +static void +set_eof_error(PySSLSocket *sslsock) +{ + _sslmodulestate *state = get_state_sock(sslsock); + fill_and_set_sslerror(state, sslsock, state->PySSLEOFErrorObject, + PY_SSL_ERROR_EOF, + "EOF occurred in violation of protocol", + __LINE__, 0); } + +// Set the appropriate SSL error exception. +// err - error information from SSL and libc +// exc - if not NULL, an exception from _debughelpers.c callback to be chained static PyObject * -PySSL_SetError(PySSLSocket *sslsock, const char *filename, int lineno) +PySSL_SetError(PySSLSocket *sslsock, _PySSLError err, PyObject *exc, + const char *filename, int lineno) { PyObject *type; char *errstr = NULL; - _PySSLError err; enum py_ssl_error p = PY_SSL_ERROR_NONE; unsigned long e = 0; @@ -683,8 +697,6 @@ PySSL_SetError(PySSLSocket *sslsock, const char *filename, int lineno) e = ERR_peek_last_error(); if (sslsock->ssl != NULL) { - err = sslsock->err; - switch (err.ssl) { case SSL_ERROR_ZERO_RETURN: errstr = "TLS/SSL connection has been closed (EOF)"; @@ -777,7 +789,7 @@ PySSL_SetError(PySSLSocket *sslsock, const char *filename, int lineno) } fill_and_set_sslerror(state, sslsock, type, p, errstr, lineno, e); ERR_clear_error(); - PySSL_ChainExceptions(sslsock); + _PyErr_ChainExceptions1(exc); // chain any exceptions from callbacks return NULL; } @@ -803,6 +815,35 @@ _ssl_deprecated(const char* msg, int stacklevel) { #define PY_SSL_DEPRECATED(name, stacklevel, ret) \ if (_ssl_deprecated((name), (stacklevel)) == -1) return (ret) +// Get the socket from a PySSLSocket, if it has one. +// Stores a strong reference in out_sock. +static int +get_socket(PySSLSocket *obj, PySocketSockObject **out_sock, + const char *filename, int lineno) +{ + if (!obj->Socket) { + *out_sock = NULL; + return 0; + } + PySocketSockObject *sock; + int res = PyWeakref_GetRef(obj->Socket, (PyObject **)&sock); + if (res == 0 || sock->sock_fd == INVALID_SOCKET) { + _setSSLError(get_state_sock(obj), + "Underlying socket connection gone", + PY_SSL_ERROR_NO_SOCKET, filename, lineno); + *out_sock = NULL; + return -1; + } + if (sock != NULL) { + /* just in case the blocking state of the socket has been changed */ + int nonblocking = (sock->sock_timeout >= 0); + BIO_set_nbio(SSL_get_rbio(obj->ssl), nonblocking); + BIO_set_nbio(SSL_get_wbio(obj->ssl), nonblocking); + } + *out_sock = sock; + return res; +} + /* * SSL objects */ @@ -882,7 +923,6 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, { PySSLSocket *self; SSL_CTX *ctx = sslctx->ctx; - _PySSLError err = { 0 }; if ((socket_type == PY_SSL_SERVER) && (sslctx->protocol == PY_SSL_VERSION_TLS_CLIENT)) { @@ -910,18 +950,17 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, self->shutdown_seen_zero = 0; self->owner = NULL; self->server_hostname = NULL; - self->err = err; - self->exc = NULL; + self->got_eof_error = 0; /* Make sure the SSL error state is initialized */ ERR_clear_error(); - PySSL_BEGIN_ALLOW_THREADS + PySSL_BEGIN_ALLOW_THREADS(sslctx) self->ssl = SSL_new(ctx); - PySSL_END_ALLOW_THREADS + PySSL_END_ALLOW_THREADS(sslctx) if (self->ssl == NULL) { + _setSSLError(get_state_ctx(sslctx), NULL, 0, __FILE__, __LINE__); Py_DECREF(self); - _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); return NULL; } @@ -934,7 +973,7 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, } /* bpo43522 and OpenSSL < 1.1.1l: copy hostflags manually */ -#if OPENSSL_VERSION < 0x101010cf +#if OPENSSL_VERSION_NUMBER < 0x101010cf X509_VERIFY_PARAM *ssl_verification_params = SSL_get0_param(self->ssl); X509_VERIFY_PARAM *ssl_ctx_verification_params = SSL_CTX_get0_param(ctx); @@ -987,12 +1026,12 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, BIO_set_nbio(SSL_get_wbio(self->ssl), 1); } - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; if (socket_type == PY_SSL_CLIENT) SSL_set_connect_state(self->ssl); else SSL_set_accept_state(self->ssl); - PySSL_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; self->socket_type = socket_type; if (sock != NULL) { @@ -1032,24 +1071,19 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self) { int ret; _PySSLError err; - int sockstate, nonblocking; - PySocketSockObject *sock = GET_SOCKET(self); + PyObject *exc = NULL; + int sockstate; PyTime_t timeout, deadline = 0; int has_timeout; - if (sock) { - if (((PyObject*)sock) == Py_None) { - _setSSLError(get_state_sock(self), - "Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); + PySocketSockObject *sock = NULL; + if (get_socket(self, &sock, __FILE__, __LINE__) < 0) { + return NULL; + } - /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + if (self->got_eof_error) { + set_eof_error(self); + goto error; } timeout = GET_SOCKET_TIMEOUT(sock); @@ -1061,11 +1095,17 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self) /* Actually negotiate SSL connection */ /* XXX If SSL_do_handshake() returns 0, it's also a failure. */ do { - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS ret = SSL_do_handshake(self->ssl); err = _PySSL_errno(ret < 1, self->ssl, ret); - PySSL_END_ALLOW_THREADS - self->err = err; + Py_END_ALLOW_THREADS; + _PySSL_FIX_ERRNO; + + // Get any exception that occurred in a debughelpers.c callback + exc = PyErr_GetRaisedException(); + if (exc != NULL) { + break; + } if (PyErr_CheckSignals()) goto error; @@ -1101,13 +1141,15 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self) Py_XDECREF(sock); if (ret < 1) - return PySSL_SetError(self, __FILE__, __LINE__); - if (PySSL_ChainExceptions(self) < 0) + return PySSL_SetError(self, err, exc, __FILE__, __LINE__); + if (exc != NULL) { + PyErr_SetRaisedException(exc); return NULL; + } Py_RETURN_NONE; error: + assert(exc == NULL); Py_XDECREF(sock); - PySSL_ChainExceptions(self); return NULL; } @@ -1156,7 +1198,7 @@ _asn1obj2py(_sslmodulestate *state, const ASN1_OBJECT *name, int no_name) static PyObject * _create_tuple_for_attribute(_sslmodulestate *state, - ASN1_OBJECT *name, ASN1_STRING *value) + const ASN1_OBJECT *name, const ASN1_STRING *value) { Py_ssize_t buflen; PyObject *pyattr; @@ -1185,16 +1227,16 @@ _create_tuple_for_attribute(_sslmodulestate *state, } static PyObject * -_create_tuple_for_X509_NAME (_sslmodulestate *state, X509_NAME *xname) +_create_tuple_for_X509_NAME(_sslmodulestate *state, const X509_NAME *xname) { PyObject *dn = NULL; /* tuple which represents the "distinguished name" */ PyObject *rdn = NULL; /* tuple to hold a "relative distinguished name" */ PyObject *rdnt; PyObject *attr = NULL; /* tuple to hold an attribute */ int entry_count = X509_NAME_entry_count(xname); - X509_NAME_ENTRY *entry; - ASN1_OBJECT *name; - ASN1_STRING *value; + const X509_NAME_ENTRY *entry; + const ASN1_OBJECT *name; + const ASN1_STRING *value; int index_counter; int rdn_level = -1; int retcode; @@ -1433,14 +1475,14 @@ _get_peer_alt_names (_sslmodulestate *state, X509 *certificate) { } PyTuple_SET_ITEM(t, 0, v); - if (name->d.ip->length == 4) { - unsigned char *p = name->d.ip->data; + if (ASN1_STRING_length(name->d.ip) == 4) { + const unsigned char *p = ASN1_STRING_get0_data(name->d.ip); v = PyUnicode_FromFormat( "%d.%d.%d.%d", p[0], p[1], p[2], p[3] ); - } else if (name->d.ip->length == 16) { - unsigned char *p = name->d.ip->data; + } else if (ASN1_STRING_length(name->d.ip) == 16) { + const unsigned char *p = ASN1_STRING_get0_data(name->d.ip); v = PyUnicode_FromFormat( "%X:%X:%X:%X:%X:%X:%X:%X", p[0] << 8 | p[1], @@ -1571,8 +1613,9 @@ _get_aia_uri(X509 *certificate, int nid) { continue; } uri = ad->location->d.uniformResourceIdentifier; - ostr = PyUnicode_FromStringAndSize((char *)uri->data, - uri->length); + ostr = PyUnicode_FromStringAndSize( + (const char *)ASN1_STRING_get0_data(uri), + ASN1_STRING_length(uri)); if (ostr == NULL) { goto fail; } @@ -1638,8 +1681,9 @@ _get_crl_dp(X509 *certificate) { continue; } uri = gn->d.uniformResourceIdentifier; - ouri = PyUnicode_FromStringAndSize((char *)uri->data, - uri->length); + ouri = PyUnicode_FromStringAndSize( + (const char *)ASN1_STRING_get0_data(uri), + ASN1_STRING_length(uri)); if (ouri == NULL) goto done; @@ -1854,14 +1898,14 @@ _certificate_to_der(_sslmodulestate *state, X509 *certificate) /*[clinic input] _ssl._test_decode_cert - path: object(converter="PyUnicode_FSConverter") + path: unicode_fs_encoded / [clinic start generated code]*/ static PyObject * _ssl__test_decode_cert_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=96becb9abb23c091 input=cdeaaf02d4346628]*/ +/*[clinic end generated code: output=96becb9abb23c091 input=cb4988d5e651a4f8]*/ { PyObject *retval = NULL; X509 *x=NULL; @@ -1891,7 +1935,6 @@ _ssl__test_decode_cert_impl(PyObject *module, PyObject *path) X509_free(x); fail0: - Py_DECREF(path); if (cert != NULL) BIO_free(cert); return retval; } @@ -2169,6 +2212,83 @@ _ssl__SSLSocket_cipher_impl(PySSLSocket *self) return cipher_to_tuple(current); } +/*[clinic input] +@critical_section +_ssl._SSLSocket.group +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_group_impl(PySSLSocket *self) +/*[clinic end generated code: output=9c168ee877017b95 input=5f187d8bf0d433b7]*/ +{ +#if OPENSSL_VERSION_NUMBER >= 0x30200000L + const char *group_name; + + if (self->ssl == NULL) { + Py_RETURN_NONE; + } + group_name = SSL_get0_group_name(self->ssl); + if (group_name == NULL) { + Py_RETURN_NONE; + } + return PyUnicode_DecodeFSDefault(group_name); +#else + PyErr_SetString(PyExc_NotImplementedError, + "Getting selected group requires OpenSSL 3.2 or later."); + return NULL; +#endif +} + +static PyObject * +ssl_socket_signame_impl(PySSLSocket *socket, + enum py_ssl_server_or_client self_socket_type) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30500000L + int ret; + const char *sigalg; + + if (socket->ssl == NULL) { + Py_RETURN_NONE; + } + ret = (socket->socket_type == self_socket_type) + ? SSL_get0_signature_name(socket->ssl, &sigalg) + : SSL_get0_peer_signature_name(socket->ssl, &sigalg); + if (ret == 0) { + Py_RETURN_NONE; + } + assert(sigalg != NULL); + return PyUnicode_DecodeFSDefault(sigalg); +#else + PyErr_SetString(PyExc_NotImplementedError, + "Getting sig algorithms requires OpenSSL 3.5 or later."); + return NULL; +#endif +} + +/*[clinic input] +@critical_section +_ssl._SSLSocket.client_sigalg +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_client_sigalg_impl(PySSLSocket *self) +/*[clinic end generated code: output=499dd7fbf021a47b input=a0d9696b5414c627]*/ +{ + return ssl_socket_signame_impl(self, PY_SSL_CLIENT); +} + +/*[clinic input] +@critical_section +_ssl._SSLSocket.server_sigalg +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_server_sigalg_impl(PySSLSocket *self) +/*[clinic end generated code: output=c508a766a8e275dc input=9063e562a1e6b946]*/ +{ + return ssl_socket_signame_impl(self, PY_SSL_SERVER); +} + /*[clinic input] @critical_section _ssl._SSLSocket.version @@ -2244,14 +2364,15 @@ _ssl._SSLSocket.context This changes the context associated with the SSLSocket. -This is typically used from within a callback function set by the sni_callback -on the SSLContext to change the certificate information associated with the -SSLSocket before the cryptographic exchange handshake messages. +This is typically used from within a callback function set by the +sni_callback on the SSLContext to change the certificate information +associated with the SSLSocket before the cryptographic exchange +handshake messages. [clinic start generated code]*/ static PyObject * _ssl__SSLSocket_context_get_impl(PySSLSocket *self) -/*[clinic end generated code: output=d23e82f72f32e3d7 input=7cbb97407c2ace30]*/ +/*[clinic end generated code: output=d23e82f72f32e3d7 input=b845dea1f9710ebe]*/ { return Py_NewRef(self->ctx); } @@ -2357,9 +2478,7 @@ _ssl__SSLSocket_owner_set_impl(PySSLSocket *self, PyObject *value) static int PySSL_traverse(PyObject *op, visitproc visit, void *arg) { - PySSLSocket *self = PySSLSocket_CAST(op); - Py_VISIT(self->exc); - Py_VISIT(Py_TYPE(self)); + Py_VISIT(Py_TYPE(op)); return 0; } @@ -2367,7 +2486,10 @@ static int PySSL_clear(PyObject *op) { PySSLSocket *self = PySSLSocket_CAST(op); - Py_CLEAR(self->exc); + Py_CLEAR(self->Socket); + Py_CLEAR(self->ctx); + Py_CLEAR(self->owner); + Py_CLEAR(self->server_hostname); return 0; } @@ -2391,10 +2513,7 @@ PySSL_dealloc(PyObject *op) SSL_set_shutdown(self->ssl, SSL_SENT_SHUTDOWN | SSL_get_shutdown(self->ssl)); SSL_free(self->ssl); } - Py_XDECREF(self->Socket); - Py_XDECREF(self->ctx); - Py_XDECREF(self->server_hostname); - Py_XDECREF(self->owner); + (void)PySSL_clear(op); PyObject_GC_Del(self); Py_DECREF(tp); } @@ -2441,9 +2560,10 @@ PySSL_select(PySocketSockObject *s, int writing, PyTime_t timeout) ms = (int)_PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING); assert(ms <= INT_MAX); - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS rc = poll(&pollfd, 1, (int)ms); - PySSL_END_ALLOW_THREADS + Py_END_ALLOW_THREADS + _PySSL_FIX_ERRNO; #else /* Guard against socket too large for select*/ if (!_PyIsSelectable_fd(s->sock_fd)) @@ -2455,13 +2575,14 @@ PySSL_select(PySocketSockObject *s, int writing, PyTime_t timeout) FD_SET(s->sock_fd, &fds); /* Wait until the socket becomes ready */ - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS nfds = Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int); if (writing) rc = select(nfds, NULL, &fds, NULL, &tv); else rc = select(nfds, &fds, NULL, NULL, &tv); - PySSL_END_ALLOW_THREADS + Py_END_ALLOW_THREADS + _PySSL_FIX_ERRNO; #endif /* Return SOCKET_TIMED_OUT on timeout, SOCKET_OPERATION_OK otherwise @@ -2513,6 +2634,7 @@ _ssl__SSLSocket_uses_ktls_for_recv_impl(PySSLSocket *self) #ifdef BIO_get_ktls_send /*[clinic input] +@permit_long_summary @critical_section _ssl._SSLSocket.sendfile fd: int @@ -2523,9 +2645,9 @@ _ssl._SSLSocket.sendfile Write size bytes from offset in the file descriptor fd to the SSL connection. -This method uses the zero-copy technique and returns the number of bytes -written. It should be called only when Kernel TLS is used for sending data in -the connection. +This method uses the zero-copy technique and returns the number of +bytes written. It should be called only when Kernel TLS is used for +sending data in the connection. The meaning of flags is platform dependent. [clinic start generated code]*/ @@ -2533,27 +2655,23 @@ The meaning of flags is platform dependent. static PyObject * _ssl__SSLSocket_sendfile_impl(PySSLSocket *self, int fd, Py_off_t offset, size_t size, int flags) -/*[clinic end generated code: output=0c6815b0719ca8d5 input=dfc1b162bb020de1]*/ +/*[clinic end generated code: output=0c6815b0719ca8d5 input=68c7fbf90c9a8a1b]*/ { Py_ssize_t retval; int sockstate; _PySSLError err; - PySocketSockObject *sock = GET_SOCKET(self); + PyObject *exc = NULL; PyTime_t timeout, deadline = 0; int has_timeout; - if (sock != NULL) { - if ((PyObject *)sock == Py_None) { - _setSSLError(get_state_sock(self), - "Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); - /* just in case the blocking state of the socket has been changed */ - int nonblocking = (sock->sock_timeout >= 0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + PySocketSockObject *sock = NULL; + if (get_socket(self, &sock, __FILE__, __LINE__) < 0) { + return NULL; + } + + if (self->got_eof_error) { + set_eof_error(self); + goto error; } timeout = GET_SOCKET_TIMEOUT(sock); @@ -2579,11 +2697,16 @@ _ssl__SSLSocket_sendfile_impl(PySSLSocket *self, int fd, Py_off_t offset, } do { - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS retval = SSL_sendfile(self->ssl, fd, (off_t)offset, size, flags); err = _PySSL_errno(retval < 0, self->ssl, (int)retval); - PySSL_END_ALLOW_THREADS - self->err = err; + Py_END_ALLOW_THREADS; + _PySSL_FIX_ERRNO; + + exc = PyErr_GetRaisedException(); + if (exc != NULL) { + break; + } if (PyErr_CheckSignals()) { goto error; @@ -2634,15 +2757,18 @@ _ssl__SSLSocket_sendfile_impl(PySSLSocket *self, int fd, Py_off_t offset, } Py_XDECREF(sock); if (retval < 0) { - return PySSL_SetError(self, __FILE__, __LINE__); + return PySSL_SetError(self, err, exc, __FILE__, __LINE__); } - if (PySSL_ChainExceptions(self) < 0) { + if (exc != NULL) { + PyErr_SetRaisedException(exc); return NULL; } return PyLong_FromSize_t(retval); error: Py_XDECREF(sock); - (void)PySSL_ChainExceptions(self); + if (exc != NULL) { + _PyErr_ChainExceptions1(exc); + } return NULL; } #endif /* BIO_get_ktls_send */ @@ -2666,26 +2792,18 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) int retval; int sockstate; _PySSLError err; - int nonblocking; - PySocketSockObject *sock = GET_SOCKET(self); + PyObject *exc = NULL; PyTime_t timeout, deadline = 0; int has_timeout; - if (sock != NULL) { - if (((PyObject*)sock) == Py_None) { - _setSSLError(get_state_sock(self), - "Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); + PySocketSockObject *sock = NULL; + if (get_socket(self, &sock, __FILE__, __LINE__) < 0) { + return NULL; } - if (sock != NULL) { - /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + if (self->got_eof_error) { + set_eof_error(self); + goto error; } timeout = GET_SOCKET_TIMEOUT(sock); @@ -2710,11 +2828,16 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) } do { - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; retval = SSL_write_ex(self->ssl, b->buf, (size_t)b->len, &count); err = _PySSL_errno(retval == 0, self->ssl, retval); - PySSL_END_ALLOW_THREADS - self->err = err; + Py_END_ALLOW_THREADS; + _PySSL_FIX_ERRNO; + + exc = PyErr_GetRaisedException(); + if (exc != NULL) { + break; + } if (PyErr_CheckSignals()) goto error; @@ -2747,17 +2870,20 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) Py_XDECREF(sock); if (retval == 0) - return PySSL_SetError(self, __FILE__, __LINE__); - if (PySSL_ChainExceptions(self) < 0) + return PySSL_SetError(self, err, exc, __FILE__, __LINE__); + if (exc != NULL) { + PyErr_SetRaisedException(exc); return NULL; + } return PyLong_FromSize_t(count); error: + assert(exc == NULL); Py_XDECREF(sock); - PySSL_ChainExceptions(self); return NULL; } /*[clinic input] +@permit_long_summary @critical_section _ssl._SSLSocket.pending @@ -2766,19 +2892,19 @@ Returns the number of already decrypted bytes available for read, pending on the static PyObject * _ssl__SSLSocket_pending_impl(PySSLSocket *self) -/*[clinic end generated code: output=983d9fecdc308a83 input=32ab982a254e8866]*/ +/*[clinic end generated code: output=983d9fecdc308a83 input=042dcc48bdf3e312]*/ { int count = 0; _PySSLError err; - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; count = SSL_pending(self->ssl); err = _PySSL_errno(count < 0, self->ssl, count); - PySSL_END_ALLOW_THREADS - self->err = err; + Py_END_ALLOW_THREADS; + _PySSL_FIX_ERRNO; if (count < 0) - return PySSL_SetError(self, __FILE__, __LINE__); + return PySSL_SetError(self, err, NULL, __FILE__, __LINE__); else return PyLong_FromLong(count); } @@ -2800,14 +2926,13 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, int group_right_1, Py_buffer *buffer) /*[clinic end generated code: output=49b16e6406023734 input=80ed30436df01a71]*/ { - PyObject *dest = NULL; + PyBytesWriter *writer = NULL; char *mem; size_t count = 0; int retval; int sockstate; _PySSLError err; - int nonblocking; - PySocketSockObject *sock = GET_SOCKET(self); + PyObject *exc = NULL; PyTime_t timeout, deadline = 0; int has_timeout; @@ -2816,25 +2941,27 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, return NULL; } - if (sock != NULL) { - if (((PyObject*)sock) == Py_None) { - _setSSLError(get_state_sock(self), - "Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); + PySocketSockObject *sock = NULL; + if (get_socket(self, &sock, __FILE__, __LINE__) < 0) { + return NULL; + } + + if (self->got_eof_error) { + set_eof_error(self); + goto error; } if (!group_right_1) { - dest = PyBytes_FromStringAndSize(NULL, len); - if (dest == NULL) - goto error; if (len == 0) { Py_XDECREF(sock); - return dest; + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } + + writer = PyBytesWriter_Create(len); + if (writer == NULL) { + goto error; } - mem = PyBytes_AS_STRING(dest); + mem = PyBytesWriter_GetData(writer); } else { mem = buffer->buf; @@ -2852,24 +2979,22 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, } } - if (sock != NULL) { - /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); - } - timeout = GET_SOCKET_TIMEOUT(sock); has_timeout = (timeout > 0); if (has_timeout) deadline = _PyDeadline_Init(timeout); do { - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; retval = SSL_read_ex(self->ssl, mem, (size_t)len, &count); err = _PySSL_errno(retval == 0, self->ssl, retval); - PySSL_END_ALLOW_THREADS - self->err = err; + Py_END_ALLOW_THREADS; + _PySSL_FIX_ERRNO; + + exc = PyErr_GetRaisedException(); + if (exc != NULL) { + break; + } if (PyErr_CheckSignals()) goto error; @@ -2902,27 +3027,32 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, err.ssl == SSL_ERROR_WANT_WRITE); if (retval == 0) { - PySSL_SetError(self, __FILE__, __LINE__); + PySSL_SetError(self, err, exc, __FILE__, __LINE__); + exc = NULL; goto error; } - if (self->exc != NULL) + else if (exc != NULL) { + PyErr_SetRaisedException(exc); + exc = NULL; goto error; + } done: + assert(exc == NULL); Py_XDECREF(sock); if (!group_right_1) { - _PyBytes_Resize(&dest, count); - return dest; + return PyBytesWriter_FinishWithSize(writer, count); } else { return PyLong_FromSize_t(count); } error: - PySSL_ChainExceptions(self); + assert(exc == NULL); Py_XDECREF(sock); - if (!group_right_1) - Py_XDECREF(dest); + if (!group_right_1) { + PyBytesWriter_Discard(writer); + } return NULL; } @@ -2938,26 +3068,15 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) /*[clinic end generated code: output=ca1aa7ed9d25ca42 input=98d9635cd4e16514]*/ { _PySSLError err; - int sockstate, nonblocking, ret; + PyObject *exc = NULL; + int sockstate, ret; int zeros = 0; - PySocketSockObject *sock = GET_SOCKET(self); PyTime_t timeout, deadline = 0; int has_timeout; - if (sock != NULL) { - /* Guard against closed socket */ - if ((((PyObject*)sock) == Py_None) || (sock->sock_fd == INVALID_SOCKET)) { - _setSSLError(get_state_sock(self), - "Underlying socket connection gone", - PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); - return NULL; - } - Py_INCREF(sock); - - /* Just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0); - BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); - BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); + PySocketSockObject *sock = NULL; + if (get_socket(self, &sock, __FILE__, __LINE__) < 0) { + return NULL; } timeout = GET_SOCKET_TIMEOUT(sock); @@ -2967,7 +3086,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) } while (1) { - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS; /* Disable read-ahead so that unwrap can work correctly. * Otherwise OpenSSL might read in too much data, * eating clear text data that happens to be @@ -2980,8 +3099,13 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) SSL_set_read_ahead(self->ssl, 0); ret = SSL_shutdown(self->ssl); err = _PySSL_errno(ret < 0, self->ssl, ret); - PySSL_END_ALLOW_THREADS - self->err = err; + Py_END_ALLOW_THREADS; + _PySSL_FIX_ERRNO; + + exc = PyErr_GetRaisedException(); + if (exc != NULL) { + break; + } /* If err == 1, a secure shutdown with SSL_shutdown() is complete */ if (ret > 0) @@ -3029,11 +3153,14 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) } if (ret < 0) { Py_XDECREF(sock); - PySSL_SetError(self, __FILE__, __LINE__); + PySSL_SetError(self, err, exc, __FILE__, __LINE__); + return NULL; + } + else if (exc != NULL) { + Py_XDECREF(sock); + PyErr_SetRaisedException(exc); return NULL; } - if (self->exc != NULL) - goto error; if (sock) /* It's already INCREF'ed */ return (PyObject *) sock; @@ -3041,8 +3168,8 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) Py_RETURN_NONE; error: + assert(exc == NULL); Py_XDECREF(sock); - PySSL_ChainExceptions(self); return NULL; } @@ -3053,15 +3180,16 @@ _ssl._SSLSocket.get_channel_binding Get channel binding data for current connection. -Raise ValueError if the requested `cb_type` is not supported. Return bytes -of the data or None if the data is not available (e.g. before the handshake). +Raise ValueError if the requested `cb_type` is not supported. +Return bytes of the data or None if the data is not available (e.g. +before the handshake). Only 'tls-unique' channel binding data from RFC 5929 is supported. [clinic start generated code]*/ static PyObject * _ssl__SSLSocket_get_channel_binding_impl(PySSLSocket *self, const char *cb_type) -/*[clinic end generated code: output=34bac9acb6a61d31 input=e008004fc08744db]*/ +/*[clinic end generated code: output=34bac9acb6a61d31 input=bed81ef7936535a0]*/ { char buf[PySSL_CB_MAXLEN]; size_t len; @@ -3231,6 +3359,9 @@ static PyMethodDef PySSLMethods[] = { _SSL__SSLSOCKET_GETPEERCERT_METHODDEF _SSL__SSLSOCKET_GET_CHANNEL_BINDING_METHODDEF _SSL__SSLSOCKET_CIPHER_METHODDEF + _SSL__SSLSOCKET_GROUP_METHODDEF + _SSL__SSLSOCKET_CLIENT_SIGALG_METHODDEF + _SSL__SSLSOCKET_SERVER_SIGALG_METHODDEF _SSL__SSLSOCKET_SHARED_CIPHERS_METHODDEF _SSL__SSLSOCKET_VERSION_METHODDEF _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF @@ -3247,7 +3378,6 @@ static PyType_Slot PySSLSocket_slots[] = { {Py_tp_getset, ssl_getsetlist}, {Py_tp_dealloc, PySSL_dealloc}, {Py_tp_traverse, PySSL_traverse}, - {Py_tp_clear, PySSL_clear}, {0, 0}, }; @@ -3375,9 +3505,10 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) // no other thread can be touching this object yet. // (Technically, we can't even lock if we wanted to, as the // lock hasn't been initialized yet.) - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS ctx = SSL_CTX_new(method); - PySSL_END_ALLOW_THREADS + Py_END_ALLOW_THREADS + _PySSL_FIX_ERRNO; if (ctx == NULL) { _setSSLError(get_ssl_state(module), NULL, 0, __FILE__, __LINE__); @@ -3402,6 +3533,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) self->psk_client_callback = NULL; self->psk_server_callback = NULL; #endif + self->tstate_mutex = (PyMutex){0}; /* Don't check host name by default */ if (proto_version == PY_SSL_VERSION_TLS_CLIENT) { @@ -3504,6 +3636,11 @@ context_traverse(PyObject *op, visitproc visit, void *arg) PySSLContext *self = PySSLContext_CAST(op); Py_VISIT(self->set_sni_cb); Py_VISIT(self->msg_cb); + Py_VISIT(self->keylog_filename); +#ifndef OPENSSL_NO_PSK + Py_VISIT(self->psk_client_callback); + Py_VISIT(self->psk_server_callback); +#endif Py_VISIT(Py_TYPE(self)); return 0; } @@ -3520,9 +3657,10 @@ context_clear(PyObject *op) Py_CLEAR(self->psk_server_callback); #endif if (self->keylog_bio != NULL) { - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS BIO_free_all(self->keylog_bio); - PySSL_END_ALLOW_THREADS + Py_END_ALLOW_THREADS + _PySSL_FIX_ERRNO; self->keylog_bio = NULL; } return 0; @@ -3566,6 +3704,25 @@ _ssl__SSLContext_set_ciphers_impl(PySSLContext *self, const char *cipherlist) Py_RETURN_NONE; } +/*[clinic input] +@critical_section +_ssl._SSLContext.set_ciphersuites + ciphersuites: str + / +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_set_ciphersuites_impl(PySSLContext *self, + const char *ciphersuites) +/*[clinic end generated code: output=9915bec58e54d76d input=2afcc3693392be41]*/ +{ + if (!SSL_CTX_set_ciphersuites(self->ctx, ciphersuites)) { + _setSSLError(get_state_ctx(self), "No cipher suite can be selected.", 0, __FILE__, __LINE__); + return NULL; + } + Py_RETURN_NONE; +} + /*[clinic input] @critical_section _ssl._SSLContext.get_ciphers @@ -3610,6 +3767,129 @@ _ssl__SSLContext_get_ciphers_impl(PySSLContext *self) } +/*[clinic input] +@critical_section +_ssl._SSLContext.set_groups + grouplist: str + / +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_set_groups_impl(PySSLContext *self, const char *grouplist) +/*[clinic end generated code: output=0b5d05dfd371ffd0 input=2cc64cef21930741]*/ +{ + if (!SSL_CTX_set1_groups_list(self->ctx, grouplist)) { + _setSSLError(get_state_ctx(self), "unrecognized group", 0, __FILE__, __LINE__); + return NULL; + } + Py_RETURN_NONE; +} + +/*[clinic input] +@critical_section +_ssl._SSLContext.get_groups + * + include_aliases: bool = False +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_get_groups_impl(PySSLContext *self, int include_aliases) +/*[clinic end generated code: output=6d6209dd1051529b input=3e8ee5deb277dcc5]*/ +{ +#if OPENSSL_VERSION_NUMBER >= 0x30500000L + STACK_OF(OPENSSL_CSTRING) *groups = NULL; + const char *group; + int i, num; + PyObject *item, *result = NULL; + + // This "groups" object is dynamically allocated, but the strings inside + // it are internal constants which shouldn't ever be modified or freed. + if ((groups = sk_OPENSSL_CSTRING_new_null()) == NULL) { + _setSSLError(get_state_ctx(self), "Can't allocate stack", 0, __FILE__, __LINE__); + goto error; + } + + if (!SSL_CTX_get0_implemented_groups(self->ctx, include_aliases, groups)) { + _setSSLError(get_state_ctx(self), "Can't get groups", 0, __FILE__, __LINE__); + goto error; + } + + num = sk_OPENSSL_CSTRING_num(groups); + result = PyList_New(num); + if (result == NULL) { + goto error; + } + + for (i = 0; i < num; ++i) { + // There's no allocation here, so group won't ever be NULL. + group = sk_OPENSSL_CSTRING_value(groups, i); + assert(group != NULL); + + // Group names are plain ASCII, so there's no chance of a decoding + // error here. However, an allocation failure could occur when + // constructing the Unicode version of the names. + if ((item = PyUnicode_DecodeFSDefault(group)) == NULL) { + goto error; + } + + PyList_SET_ITEM(result, i, item); + } + + sk_OPENSSL_CSTRING_free(groups); + return result; +error: + Py_XDECREF(result); + sk_OPENSSL_CSTRING_free(groups); + return NULL; +#else + PyErr_SetString(PyExc_NotImplementedError, + "Getting implemented groups requires OpenSSL 3.5 or later."); + return NULL; +#endif +} + +/*[clinic input] +@critical_section +_ssl._SSLContext.set_client_sigalgs + sigalgslist: str + / +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_set_client_sigalgs_impl(PySSLContext *self, + const char *sigalgslist) +/*[clinic end generated code: output=f4f5be160a29c7d6 input=500d853ce9fd94ff]*/ +{ +#ifdef OPENSSL_IS_AWSLC + _setSSLError(get_state_ctx(self), "can't set client sigalgs on AWS-LC", 0, __FILE__, __LINE__); + return NULL; +#else + if (!SSL_CTX_set1_client_sigalgs_list(self->ctx, sigalgslist)) { + _setSSLError(get_state_ctx(self), "unrecognized signature algorithm", 0, __FILE__, __LINE__); + return NULL; + } + Py_RETURN_NONE; +#endif +} + +/*[clinic input] +@critical_section +_ssl._SSLContext.set_server_sigalgs + sigalgslist: str + / +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_set_server_sigalgs_impl(PySSLContext *self, + const char *sigalgslist) +/*[clinic end generated code: output=31ecb1d310285644 input=653b752e4f8d801b]*/ +{ + if (!SSL_CTX_set1_sigalgs_list(self->ctx, sigalgslist)) { + _setSSLError(get_state_ctx(self), "unrecognized signature algorithm", 0, __FILE__, __LINE__); + return NULL; + } + Py_RETURN_NONE; +} static int do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen, @@ -3785,15 +4065,11 @@ _ssl__SSLContext_verify_flags_set_impl(PySSLContext *self, PyObject *value) static int set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what) { - long v; + int v; int result; - if (!PyArg_Parse(arg, "l", &v)) - return -1; - if (v > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, "Option is too long"); + if (!PyArg_Parse(arg, "i", &v)) return -1; - } switch(self->protocol) { case PY_SSL_VERSION_TLS_CLIENT: _Py_FALLTHROUGH; @@ -3828,7 +4104,7 @@ set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what) break; default: PyErr_Format(PyExc_ValueError, - "Unsupported TLS/SSL version 0x%x", v); + "Unsupported TLS/SSL version 0x%x", (unsigned)v); return -1; } @@ -3862,7 +4138,7 @@ set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what) } if (result == 0) { PyErr_Format(PyExc_ValueError, - "Unsupported protocol version 0x%x", v); + "Unsupported protocol version 0x%x", (unsigned)v); return -1; } return 0; @@ -4245,7 +4521,8 @@ _password_callback(char *buf, int size, int rwflag, void *userdata) _PySSLPasswordInfo *pw_info = (_PySSLPasswordInfo*) userdata; PyObject *fn_ret = NULL; - PySSL_END_ALLOW_THREADS_S(pw_info->thread_state); + pw_info->thread_state = PyThreadState_Swap(pw_info->thread_state); + _PySSL_FIX_ERRNO; if (pw_info->error) { /* already failed previously. OpenSSL 3.0.0-alpha14 invokes the @@ -4275,76 +4552,42 @@ _password_callback(char *buf, int size, int rwflag, void *userdata) goto error; } - PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state); + pw_info->thread_state = PyThreadState_Swap(pw_info->thread_state); memcpy(buf, pw_info->password, pw_info->size); return pw_info->size; error: Py_XDECREF(fn_ret); - PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state); + pw_info->thread_state = PyThreadState_Swap(pw_info->thread_state); pw_info->error = 1; return -1; } -/*[clinic input] -@critical_section -_ssl._SSLContext.load_cert_chain - certfile: object - keyfile: object = None - password: object = None - -[clinic start generated code]*/ - static PyObject * -_ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, - PyObject *keyfile, PyObject *password) -/*[clinic end generated code: output=9480bc1c380e2095 input=6c7c5e8b73e4264b]*/ +load_cert_chain_lock_held(PySSLContext *self, _PySSLPasswordInfo *pw_info, + PyObject *certfile_bytes, PyObject *keyfile_bytes) { - PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL; + int r; + PyObject *ret = NULL; + pem_password_cb *orig_passwd_cb = SSL_CTX_get_default_passwd_cb(self->ctx); void *orig_passwd_userdata = SSL_CTX_get_default_passwd_cb_userdata(self->ctx); - _PySSLPasswordInfo pw_info = { NULL, NULL, NULL, 0, 0 }; - int r; - errno = 0; - ERR_clear_error(); - if (keyfile == Py_None) - keyfile = NULL; - if (!PyUnicode_FSConverter(certfile, &certfile_bytes)) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_SetString(PyExc_TypeError, - "certfile should be a valid filesystem path"); - } - return NULL; - } - if (keyfile && !PyUnicode_FSConverter(keyfile, &keyfile_bytes)) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_SetString(PyExc_TypeError, - "keyfile should be a valid filesystem path"); - } - goto error; - } - if (password != Py_None) { - if (PyCallable_Check(password)) { - pw_info.callable = password; - } else if (!_pwinfo_set(&pw_info, password, - "password should be a string or callable")) { - goto error; - } - SSL_CTX_set_default_passwd_cb(self->ctx, _password_callback); - SSL_CTX_set_default_passwd_cb_userdata(self->ctx, &pw_info); - } - PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); + SSL_CTX_set_default_passwd_cb(self->ctx, _password_callback); + SSL_CTX_set_default_passwd_cb_userdata(self->ctx, pw_info); + + PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state); r = SSL_CTX_use_certificate_chain_file(self->ctx, PyBytes_AS_STRING(certfile_bytes)); - PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); + PySSL_END_ALLOW_THREADS_S(pw_info->thread_state); if (r != 1) { - if (pw_info.error) { + if (pw_info->error) { ERR_clear_error(); /* the password callback has already set the error information */ } else if (errno != 0) { - PyErr_SetFromErrno(PyExc_OSError); + PyErr_SetFromErrnoWithFilename(PyExc_OSError, + PyBytes_AS_STRING(certfile_bytes)); ERR_clear_error(); } else { @@ -4352,20 +4595,20 @@ _ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, } goto error; } - PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); + + PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state); r = SSL_CTX_use_PrivateKey_file(self->ctx, - PyBytes_AS_STRING(keyfile ? keyfile_bytes : certfile_bytes), + PyBytes_AS_STRING(keyfile_bytes ? keyfile_bytes : certfile_bytes), SSL_FILETYPE_PEM); - PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); - Py_CLEAR(keyfile_bytes); - Py_CLEAR(certfile_bytes); + PySSL_END_ALLOW_THREADS_S(pw_info->thread_state); if (r != 1) { - if (pw_info.error) { + if (pw_info->error) { ERR_clear_error(); /* the password callback has already set the error information */ } else if (errno != 0) { - PyErr_SetFromErrno(PyExc_OSError); + PyErr_SetFromErrnoWithFilename(PyExc_OSError, + PyBytes_AS_STRING(keyfile_bytes ? keyfile_bytes : certfile_bytes)); ERR_clear_error(); } else { @@ -4373,25 +4616,74 @@ _ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, } goto error; } - PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); + + PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state); r = SSL_CTX_check_private_key(self->ctx); - PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); + PySSL_END_ALLOW_THREADS_S(pw_info->thread_state); if (r != 1) { _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); goto error; } - SSL_CTX_set_default_passwd_cb(self->ctx, orig_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(self->ctx, orig_passwd_userdata); - PyMem_Free(pw_info.password); - Py_RETURN_NONE; - + ret = Py_None; error: SSL_CTX_set_default_passwd_cb(self->ctx, orig_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(self->ctx, orig_passwd_userdata); + return ret; +} + +/*[clinic input] +_ssl._SSLContext.load_cert_chain + certfile: object + keyfile: object = None + password: object = None + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile, + PyObject *keyfile, PyObject *password) +/*[clinic end generated code: output=9480bc1c380e2095 input=30bc7e967ea01a58]*/ +{ + PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL; + _PySSLPasswordInfo pw_info = { NULL, NULL, NULL, 0, 0 }; + PyObject *ret = NULL; + + errno = 0; + ERR_clear_error(); + if (keyfile == Py_None) + keyfile = NULL; + if (!PyUnicode_FSConverter(certfile, &certfile_bytes)) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_SetString(PyExc_TypeError, + "certfile should be a valid filesystem path"); + } + return NULL; + } + if (keyfile && !PyUnicode_FSConverter(keyfile, &keyfile_bytes)) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_SetString(PyExc_TypeError, + "keyfile should be a valid filesystem path"); + } + goto done; + } + if (password != Py_None) { + if (PyCallable_Check(password)) { + pw_info.callable = password; + } else if (!_pwinfo_set(&pw_info, password, + "password should be a string or callable")) { + goto done; + } + } + + PyMutex_Lock(&self->tstate_mutex); + ret = load_cert_chain_lock_held(self, &pw_info, certfile_bytes, keyfile_bytes); + PyMutex_Unlock(&self->tstate_mutex); + +done: PyMem_Free(pw_info.password); Py_XDECREF(keyfile_bytes); Py_XDECREF(certfile_bytes); - return NULL; + return ret; } /* internal helper function, returns -1 on error @@ -4592,9 +4884,9 @@ _ssl__SSLContext_load_verify_locations_impl(PySSLContext *self, cafile_buf = PyBytes_AS_STRING(cafile_bytes); if (capath) capath_buf = PyBytes_AS_STRING(capath_bytes); - PySSL_BEGIN_ALLOW_THREADS + PySSL_BEGIN_ALLOW_THREADS(self) r = SSL_CTX_load_verify_locations(self->ctx, cafile_buf, capath_buf); - PySSL_END_ALLOW_THREADS + PySSL_END_ALLOW_THREADS(self) if (r != 1) { if (errno != 0) { PyErr_SetFromErrno(PyExc_OSError); @@ -4646,10 +4938,11 @@ _ssl__SSLContext_load_dh_params_impl(PySSLContext *self, PyObject *filepath) return NULL; errno = 0; - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS dh = PEM_read_DHparams(f, NULL, NULL, NULL); fclose(f); - PySSL_END_ALLOW_THREADS + Py_END_ALLOW_THREADS + _PySSL_FIX_ERRNO; if (dh == NULL) { if (errno != 0) { PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath); @@ -4801,6 +5094,7 @@ _ssl__SSLContext_set_default_verify_paths_impl(PySSLContext *self) Py_BEGIN_ALLOW_THREADS rc = SSL_CTX_set_default_verify_paths(self->ctx); Py_END_ALLOW_THREADS + _PySSL_FIX_ERRNO; if (!rc) { _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); return NULL; @@ -4858,12 +5152,15 @@ _servername_callback(SSL *s, int *al, void *args) PyObject *result; /* The high-level ssl.SSLSocket object */ PyObject *ssl_socket; + PyObject *sni_cb; const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); PyGILState_STATE gstate = PyGILState_Ensure(); - if (sslctx->set_sni_cb == NULL) { - /* remove race condition in this the call back while if removing the - * callback is in progress */ + Py_BEGIN_CRITICAL_SECTION(sslctx); + sni_cb = Py_XNewRef(sslctx->set_sni_cb); + Py_END_CRITICAL_SECTION(); + + if (sni_cb == NULL) { PyGILState_Release(gstate); return SSL_TLSEXT_ERR_OK; } @@ -4890,7 +5187,7 @@ _servername_callback(SSL *s, int *al, void *args) goto error; if (servername == NULL) { - result = PyObject_CallFunctionObjArgs(sslctx->set_sni_cb, ssl_socket, + result = PyObject_CallFunctionObjArgs(sni_cb, ssl_socket, Py_None, sslctx, NULL); } else { @@ -4917,7 +5214,7 @@ _servername_callback(SSL *s, int *al, void *args) } Py_DECREF(servername_bytes); result = PyObject_CallFunctionObjArgs( - sslctx->set_sni_cb, ssl_socket, servername_str, + sni_cb, ssl_socket, servername_str, sslctx, NULL); Py_DECREF(servername_str); } @@ -4927,7 +5224,7 @@ _servername_callback(SSL *s, int *al, void *args) PyErr_FormatUnraisable("Exception ignored " "in ssl servername callback " "while calling set SNI callback %R", - sslctx->set_sni_cb); + sni_cb); *al = SSL_AD_HANDSHAKE_FAILURE; ret = SSL_TLSEXT_ERR_ALERT_FATAL; } @@ -4952,11 +5249,13 @@ _servername_callback(SSL *s, int *al, void *args) Py_DECREF(result); } + Py_DECREF(sni_cb); PyGILState_Release(gstate); return ret; error: - Py_DECREF(ssl_socket); + Py_XDECREF(ssl_socket); + Py_XDECREF(sni_cb); *al = SSL_AD_INTERNAL_ERROR; ret = SSL_TLSEXT_ERR_ALERT_FATAL; PyGILState_Release(gstate); @@ -4964,21 +5263,23 @@ _servername_callback(SSL *s, int *al, void *args) } /*[clinic input] +@permit_long_summary @critical_section @getter _ssl._SSLContext.sni_callback Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension. -If the argument is None then the callback is disabled. The method is called -with the SSLSocket, the server name as a string, and the SSLContext object. +If the argument is None then the callback is disabled. The method +is called with the SSLSocket, the server name as a string, and the +SSLContext object. See RFC 6066 for details of the SNI extension. [clinic start generated code]*/ static PyObject * _ssl__SSLContext_sni_callback_get_impl(PySSLContext *self) -/*[clinic end generated code: output=961e6575cdfaf036 input=9b2473c5e984cfe6]*/ +/*[clinic end generated code: output=961e6575cdfaf036 input=a319bc8fc15d6fc8]*/ { PyObject *cb = self->set_sni_cb; if (cb == NULL) { @@ -4988,6 +5289,7 @@ _ssl__SSLContext_sni_callback_get_impl(PySSLContext *self) } /*[clinic input] +@permit_long_summary @critical_section @setter _ssl._SSLContext.sni_callback @@ -4995,27 +5297,25 @@ _ssl._SSLContext.sni_callback static int _ssl__SSLContext_sni_callback_set_impl(PySSLContext *self, PyObject *value) -/*[clinic end generated code: output=b32736c6b891f61a input=c3c4ff33540b3c85]*/ +/*[clinic end generated code: output=b32736c6b891f61a input=402b43fb06c1139e]*/ { if (self->protocol == PY_SSL_VERSION_TLS_CLIENT) { PyErr_SetString(PyExc_ValueError, "sni_callback cannot be set on TLS_CLIENT context"); return -1; } - Py_CLEAR(self->set_sni_cb); - if (value == Py_None) { + if (!PyCallable_Check(value)) { SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); - } - else { - if (!PyCallable_Check(value)) { - SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); - PyErr_SetString(PyExc_TypeError, - "not a callable object"); + Py_CLEAR(self->set_sni_cb); + if (value != Py_None) { + PyErr_SetString(PyExc_TypeError, "not a callable object"); return -1; } - self->set_sni_cb = Py_NewRef(value); - SSL_CTX_set_tlsext_servername_callback(self->ctx, _servername_callback); + } + else { + Py_XSETREF(self->set_sni_cb, Py_NewRef(value)); SSL_CTX_set_tlsext_servername_arg(self->ctx, self); + SSL_CTX_set_tlsext_servername_callback(self->ctx, _servername_callback); } return 0; } @@ -5070,16 +5370,16 @@ _ssl._SSLContext.cert_store_stats Returns quantities of loaded X.509 certificates. -X.509 certificates with a CA extension and certificate revocation lists -inside the context's cert store. +X.509 certificates with a CA extension and certificate revocation +lists inside the context's cert store. -NOTE: Certificates in a capath directory aren't loaded unless they have -been used at least once. +NOTE: Certificates in a capath directory aren't loaded unless they +have been used at least once. [clinic start generated code]*/ static PyObject * _ssl__SSLContext_cert_store_stats_impl(PySSLContext *self) -/*[clinic end generated code: output=5f356f4d9cca874d input=d13c6e3f2b48539b]*/ +/*[clinic end generated code: output=5f356f4d9cca874d input=9e5094e094b892a3]*/ { X509_STORE *store; STACK_OF(X509_OBJECT) *objs; @@ -5122,16 +5422,16 @@ _ssl._SSLContext.get_ca_certs Returns a list of dicts with information of loaded CA certs. -If the optional argument is True, returns a DER-encoded copy of the CA -certificate. +If the optional argument is True, returns a DER-encoded copy of the +CA certificate. -NOTE: Certificates in a capath directory aren't loaded unless they have -been used at least once. +NOTE: Certificates in a capath directory aren't loaded unless they +have been used at least once. [clinic start generated code]*/ static PyObject * _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form) -/*[clinic end generated code: output=0d58f148f37e2938 input=eb0592909c9ad6e7]*/ +/*[clinic end generated code: output=0d58f148f37e2938 input=9f71af5aa4e67076]*/ { X509_STORE *store; STACK_OF(X509_OBJECT) *objs; @@ -5457,6 +5757,10 @@ static struct PyMethodDef context_methods[] = { _SSL__SSLCONTEXT__WRAP_SOCKET_METHODDEF _SSL__SSLCONTEXT__WRAP_BIO_METHODDEF _SSL__SSLCONTEXT_SET_CIPHERS_METHODDEF + _SSL__SSLCONTEXT_SET_CIPHERSUITES_METHODDEF + _SSL__SSLCONTEXT_SET_GROUPS_METHODDEF + _SSL__SSLCONTEXT_SET_CLIENT_SIGALGS_METHODDEF + _SSL__SSLCONTEXT_SET_SERVER_SIGALGS_METHODDEF _SSL__SSLCONTEXT__SET_ALPN_PROTOCOLS_METHODDEF _SSL__SSLCONTEXT_LOAD_CERT_CHAIN_METHODDEF _SSL__SSLCONTEXT_LOAD_DH_PARAMS_METHODDEF @@ -5467,6 +5771,7 @@ static struct PyMethodDef context_methods[] = { _SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF _SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF + _SSL__SSLCONTEXT_GET_GROUPS_METHODDEF _SSL__SSLCONTEXT_SET_PSK_CLIENT_CALLBACK_METHODDEF _SSL__SSLCONTEXT_SET_PSK_SERVER_CALLBACK_METHODDEF {NULL, NULL} /* sentinel */ @@ -5532,19 +5837,11 @@ _ssl_MemoryBIO_impl(PyTypeObject *type) return (PyObject *) self; } -static int -memory_bio_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static void memory_bio_dealloc(PyObject *op) { PySSLMemoryBIO *self = PySSLMemoryBIO_CAST(op); PyTypeObject *tp = Py_TYPE(self); - PyObject_GC_UnTrack(self); (void)BIO_free(self->bio); tp->tp_free(self); Py_DECREF(tp); @@ -5601,30 +5898,29 @@ _ssl_MemoryBIO_read_impl(PySSLMemoryBIO *self, int len) /*[clinic end generated code: output=a657aa1e79cd01b3 input=21046f2d7dac3a90]*/ { int avail, nbytes; - PyObject *result; avail = (int)Py_MIN(BIO_ctrl_pending(self->bio), INT_MAX); if ((len < 0) || (len > avail)) len = avail; - result = PyBytes_FromStringAndSize(NULL, len); - if ((result == NULL) || (len == 0)) - return result; + if (len == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } - nbytes = BIO_read(self->bio, PyBytes_AS_STRING(result), len); + PyBytesWriter *writer = PyBytesWriter_Create(len); + if (writer == NULL) { + return NULL; + } + + nbytes = BIO_read(self->bio, PyBytesWriter_GetData(writer), len); if (nbytes < 0) { _sslmodulestate *state = get_state_mbio(self); - Py_DECREF(result); + PyBytesWriter_Discard(writer); _setSSLError(state, NULL, 0, __FILE__, __LINE__); return NULL; } - /* There should never be any short reads but check anyway. */ - if (nbytes < len) { - _PyBytes_Resize(&result, nbytes); - } - - return result; + return PyBytesWriter_FinishWithSize(writer, nbytes); } /*[clinic input] @@ -5709,15 +6005,13 @@ static PyType_Slot PySSLMemoryBIO_slots[] = { {Py_tp_getset, memory_bio_getsetlist}, {Py_tp_new, _ssl_MemoryBIO}, {Py_tp_dealloc, memory_bio_dealloc}, - {Py_tp_traverse, memory_bio_traverse}, {0, 0}, }; static PyType_Spec PySSLMemoryBIO_spec = { .name = "_ssl.MemoryBIO", .basicsize = sizeof(PySSLMemoryBIO), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | - Py_TPFLAGS_HAVE_GC), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE), .slots = PySSLMemoryBIO_slots, }; @@ -6020,23 +6314,25 @@ _ssl_RAND_bytes_impl(PyObject *module, int n) /*[clinic input] +@permit_long_summary @critical_section _ssl.RAND_status Returns True if the OpenSSL PRNG has been seeded with enough data and False if not. -It is necessary to seed the PRNG with RAND_add() on some platforms before -using the ssl() function. +It is necessary to seed the PRNG with RAND_add() on some platforms +before using the ssl() function. [clinic start generated code]*/ static PyObject * _ssl_RAND_status_impl(PyObject *module) -/*[clinic end generated code: output=7e0aaa2d39fdc1ad input=636fb5659ea2e727]*/ +/*[clinic end generated code: output=7e0aaa2d39fdc1ad input=52b061f4a24ff3a1]*/ { return PyBool_FromLong(RAND_status()); } /*[clinic input] +@permit_long_summary @critical_section _ssl.get_default_verify_paths @@ -6047,7 +6343,7 @@ The values are 'cert_file_env', 'cert_file', 'cert_dir_env', 'cert_dir'. static PyObject * _ssl_get_default_verify_paths_impl(PyObject *module) -/*[clinic end generated code: output=e5b62a466271928b input=c6ae00bc04eb2b6e]*/ +/*[clinic end generated code: output=e5b62a466271928b input=255507e1be890095]*/ { PyObject *ofile_env = NULL; PyObject *ofile = NULL; @@ -6079,6 +6375,39 @@ _ssl_get_default_verify_paths_impl(PyObject *module) return NULL; } +/*[clinic input] +_ssl.get_sigalgs +[clinic start generated code]*/ + +static PyObject * +_ssl_get_sigalgs_impl(PyObject *module) +/*[clinic end generated code: output=ab0791b63856854b input=d96dd6cefec3f86b]*/ +{ +#if OPENSSL_VERSION_NUMBER >= 0x30400000L + const char *sigalgs; + PyObject *sigalgs_str, *sigalgs_list; + + if ((sigalgs = SSL_get1_builtin_sigalgs(NULL)) == NULL) { + PyErr_NoMemory(); + return NULL; + } + + if ((sigalgs_str = PyUnicode_DecodeFSDefault(sigalgs)) == NULL) { + OPENSSL_free((void *)sigalgs); + return NULL; + } + + OPENSSL_free((void *)sigalgs); + sigalgs_list = PyUnicode_Split(sigalgs_str, _Py_LATIN1_CHR(':'), -1); + Py_DECREF(sigalgs_str); + return sigalgs_list; +#else + PyErr_SetString(PyExc_NotImplementedError, + "Getting signature algorithms requires OpenSSL 3.4 or later."); + return NULL; +#endif +} + static PyObject* asn1obj2py(_sslmodulestate *state, ASN1_OBJECT *obj) { @@ -6293,16 +6622,16 @@ _ssl.enum_certificates Retrieve certificates from Windows' cert store. -store_name may be one of 'CA', 'ROOT' or 'MY'. The system may provide -more cert storages, too. The function returns a list of (bytes, -encoding_type, trust) tuples. The encoding_type flag can be interpreted -with X509_ASN_ENCODING or PKCS_7_ASN_ENCODING. The trust setting is either -a set of OIDs or the boolean True. +store_name may be one of 'CA', 'ROOT' or 'MY'. The system may +provide more cert storages, too. The function returns a list of +(bytes, encoding_type, trust) tuples. The encoding_type flag can be +interpreted with X509_ASN_ENCODING or PKCS_7_ASN_ENCODING. The +trust setting is either a set of OIDs or the boolean True. [clinic start generated code]*/ static PyObject * _ssl_enum_certificates_impl(PyObject *module, const char *store_name) -/*[clinic end generated code: output=5134dc8bb3a3c893 input=263c22e6c6988cf3]*/ +/*[clinic end generated code: output=5134dc8bb3a3c893 input=ef81b4bd1b7ab8e9]*/ { HCERTSTORE hCollectionStore = NULL; PCCERT_CONTEXT pCertCtx = NULL; @@ -6482,6 +6811,7 @@ static PyMethodDef PySSL_methods[] = { _SSL_RAND_BYTES_METHODDEF _SSL_RAND_STATUS_METHODDEF _SSL_GET_DEFAULT_VERIFY_PATHS_METHODDEF + _SSL_GET_SIGALGS_METHODDEF _SSL_ENUM_CERTIFICATES_METHODDEF _SSL_ENUM_CRLS_METHODDEF _SSL_TXT2OBJ_METHODDEF @@ -6517,7 +6847,7 @@ do { \ } /* ssl.CertificateError used to be a subclass of ValueError */ - bases = PyTuple_Pack(2, state->PySSLErrorObject, PyExc_ValueError); + bases = _PyTuple_FromPair(state->PySSLErrorObject, PyExc_ValueError); if (bases == NULL) { goto error; } @@ -6695,9 +7025,15 @@ sslmodule_init_constants(PyObject *m) ADD_INT_CONST("PROTOCOL_TLS", PY_SSL_VERSION_TLS); ADD_INT_CONST("PROTOCOL_TLS_CLIENT", PY_SSL_VERSION_TLS_CLIENT); ADD_INT_CONST("PROTOCOL_TLS_SERVER", PY_SSL_VERSION_TLS_SERVER); +#ifndef OPENSSL_NO_TLS1 ADD_INT_CONST("PROTOCOL_TLSv1", PY_SSL_VERSION_TLS1); +#endif +#ifndef OPENSSL_NO_TLS1_1 ADD_INT_CONST("PROTOCOL_TLSv1_1", PY_SSL_VERSION_TLS1_1); +#endif +#ifndef OPENSSL_NO_TLS1_2 ADD_INT_CONST("PROTOCOL_TLSv1_2", PY_SSL_VERSION_TLS1_2); +#endif #define ADD_OPTION(NAME, VALUE) if (sslmodule_add_option(m, NAME, (VALUE)) < 0) return -1 @@ -7040,6 +7376,7 @@ sslmodule_init_lock(PyObject *module) } static PyModuleDef_Slot sslmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, sslmodule_init_types}, {Py_mod_exec, sslmodule_init_exceptions}, {Py_mod_exec, sslmodule_init_socketapi}, diff --git a/Modules/_ssl/cert.c b/Modules/_ssl/cert.c index f2e7be896687c8..061b0fb31716a4 100644 --- a/Modules/_ssl/cert.c +++ b/Modules/_ssl/cert.c @@ -128,7 +128,8 @@ _ssl_Certificate_get_info_impl(PySSLCertificate *self) } static PyObject* -_x509name_print(_sslmodulestate *state, X509_NAME *name, int indent, unsigned long flags) +_x509name_print(_sslmodulestate *state, const X509_NAME *name, + int indent, unsigned long flags) { PyObject *res; BIO *biobuf; diff --git a/Modules/_ssl/debughelpers.c b/Modules/_ssl/debughelpers.c index f0a0a1674f32bd..e0cb7ca9a09f91 100644 --- a/Modules/_ssl/debughelpers.c +++ b/Modules/_ssl/debughelpers.c @@ -26,6 +26,8 @@ _PySSL_msg_callback(int write_p, int version, int content_type, return; } + PyObject *exc = PyErr_GetRaisedException(); + PyObject *ssl_socket; /* ssl.SSLSocket or ssl.SSLObject */ if (ssl_obj->owner) PyWeakref_GetRef(ssl_obj->owner, &ssl_socket); @@ -73,13 +75,13 @@ _PySSL_msg_callback(int write_p, int version, int content_type, version, content_type, msg_type, buf, len ); - if (res == NULL) { - ssl_obj->exc = PyErr_GetRaisedException(); - } else { - Py_DECREF(res); - } + Py_XDECREF(res); Py_XDECREF(ssl_socket); + if (exc != NULL) { + _PyErr_ChainExceptions1(exc); + } + PyGILState_Release(threadstate); } @@ -122,16 +124,19 @@ _PySSL_keylog_callback(const SSL *ssl, const char *line) { PyGILState_STATE threadstate; PySSLSocket *ssl_obj = NULL; /* ssl._SSLSocket, borrowed ref */ + PyObject *exc; int res, e; threadstate = PyGILState_Ensure(); + exc = PyErr_GetRaisedException(); + ssl_obj = (PySSLSocket *)SSL_get_app_data(ssl); assert(Py_IS_TYPE(ssl_obj, get_state_sock(ssl_obj)->PySSLSocket_Type)); PyThread_type_lock lock = get_state_sock(ssl_obj)->keylog_lock; assert(lock != NULL); if (ssl_obj->ctx->keylog_bio == NULL) { - return; + goto done; } /* * The lock is neither released on exit nor on fork(). The lock is @@ -140,19 +145,24 @@ _PySSL_keylog_callback(const SSL *ssl, const char *line) * critical debug helper. */ - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS PyThread_acquire_lock(lock, 1); res = BIO_printf(ssl_obj->ctx->keylog_bio, "%s\n", line); e = errno; (void)BIO_flush(ssl_obj->ctx->keylog_bio); PyThread_release_lock(lock); - PySSL_END_ALLOW_THREADS + Py_END_ALLOW_THREADS + _PySSL_FIX_ERRNO; if (res == -1) { errno = e; PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, ssl_obj->ctx->keylog_filename); - ssl_obj->exc = PyErr_GetRaisedException(); + } + +done: + if (exc != NULL) { + _PyErr_ChainExceptions1(exc); } PyGILState_Release(threadstate); } @@ -187,9 +197,10 @@ _PySSLContext_set_keylog_filename(PyObject *op, PyObject *arg, if (self->keylog_bio != NULL) { BIO *bio = self->keylog_bio; self->keylog_bio = NULL; - PySSL_BEGIN_ALLOW_THREADS + Py_BEGIN_ALLOW_THREADS BIO_free_all(bio); - PySSL_END_ALLOW_THREADS + Py_END_ALLOW_THREADS + _PySSL_FIX_ERRNO; } if (arg == Py_None) { @@ -211,13 +222,13 @@ _PySSLContext_set_keylog_filename(PyObject *op, PyObject *arg, self->keylog_filename = Py_NewRef(arg); /* Write a header for seekable, empty files (this excludes pipes). */ - PySSL_BEGIN_ALLOW_THREADS + PySSL_BEGIN_ALLOW_THREADS(self) if (BIO_tell(self->keylog_bio) == 0) { BIO_puts(self->keylog_bio, "# TLS secrets log file, generated by OpenSSL / Python\n"); (void)BIO_flush(self->keylog_bio); } - PySSL_END_ALLOW_THREADS + PySSL_END_ALLOW_THREADS(self) SSL_CTX_set_keylog_callback(self->ctx, _PySSL_keylog_callback); return 0; } diff --git a/Modules/_ssl_data_34.h b/Modules/_ssl_data_34.h deleted file mode 100644 index 99718c5e605acf..00000000000000 --- a/Modules/_ssl_data_34.h +++ /dev/null @@ -1,9267 +0,0 @@ -/* File generated by Tools/ssl/make_ssl_data.py */ -/* Generated on 2025-03-26T13:47:34.223146+00:00 */ -/* Generated from Git commit openssl-3.4.1-0-ga26d85337d */ - -/* generated from args.lib2errnum */ -static struct py_ssl_library_code library_codes[] = { -#ifdef ERR_LIB_ASN1 - {"ASN1", ERR_LIB_ASN1}, -#endif -#ifdef ERR_LIB_ASYNC - {"ASYNC", ERR_LIB_ASYNC}, -#endif -#ifdef ERR_LIB_BIO - {"BIO", ERR_LIB_BIO}, -#endif -#ifdef ERR_LIB_BN - {"BN", ERR_LIB_BN}, -#endif -#ifdef ERR_LIB_BUF - {"BUF", ERR_LIB_BUF}, -#endif -#ifdef ERR_LIB_CMP - {"CMP", ERR_LIB_CMP}, -#endif -#ifdef ERR_LIB_CMS - {"CMS", ERR_LIB_CMS}, -#endif -#ifdef ERR_LIB_COMP - {"COMP", ERR_LIB_COMP}, -#endif -#ifdef ERR_LIB_CONF - {"CONF", ERR_LIB_CONF}, -#endif -#ifdef ERR_LIB_CRMF - {"CRMF", ERR_LIB_CRMF}, -#endif -#ifdef ERR_LIB_CRYPTO - {"CRYPTO", ERR_LIB_CRYPTO}, -#endif -#ifdef ERR_LIB_CT - {"CT", ERR_LIB_CT}, -#endif -#ifdef ERR_LIB_DH - {"DH", ERR_LIB_DH}, -#endif -#ifdef ERR_LIB_DSA - {"DSA", ERR_LIB_DSA}, -#endif -#ifdef ERR_LIB_DSO - {"DSO", ERR_LIB_DSO}, -#endif -#ifdef ERR_LIB_EC - {"EC", ERR_LIB_EC}, -#endif -#ifdef ERR_LIB_ECDH - {"ECDH", ERR_LIB_ECDH}, -#endif -#ifdef ERR_LIB_ECDSA - {"ECDSA", ERR_LIB_ECDSA}, -#endif -#ifdef ERR_LIB_ENGINE - {"ENGINE", ERR_LIB_ENGINE}, -#endif -#ifdef ERR_LIB_ESS - {"ESS", ERR_LIB_ESS}, -#endif -#ifdef ERR_LIB_EVP - {"EVP", ERR_LIB_EVP}, -#endif -#ifdef ERR_LIB_FIPS - {"FIPS", ERR_LIB_FIPS}, -#endif -#ifdef ERR_LIB_HMAC - {"HMAC", ERR_LIB_HMAC}, -#endif -#ifdef ERR_LIB_HTTP - {"HTTP", ERR_LIB_HTTP}, -#endif -#ifdef ERR_LIB_JPAKE - {"JPAKE", ERR_LIB_JPAKE}, -#endif -#ifdef ERR_LIB_KDF - {"KDF", ERR_LIB_KDF}, -#endif -#ifdef ERR_LIB_METH - {"METH", ERR_LIB_METH}, -#endif -#ifdef ERR_LIB_NONE - {"NONE", ERR_LIB_NONE}, -#endif -#ifdef ERR_LIB_OBJ - {"OBJ", ERR_LIB_OBJ}, -#endif -#ifdef ERR_LIB_OCSP - {"OCSP", ERR_LIB_OCSP}, -#endif -#ifdef ERR_LIB_OSSL_DECODER - {"OSSL_DECODER", ERR_LIB_OSSL_DECODER}, -#endif -#ifdef ERR_LIB_OSSL_ENCODER - {"OSSL_ENCODER", ERR_LIB_OSSL_ENCODER}, -#endif -#ifdef ERR_LIB_OSSL_STORE - {"OSSL_STORE", ERR_LIB_OSSL_STORE}, -#endif -#ifdef ERR_LIB_PEM - {"PEM", ERR_LIB_PEM}, -#endif -#ifdef ERR_LIB_PKCS12 - {"PKCS12", ERR_LIB_PKCS12}, -#endif -#ifdef ERR_LIB_PKCS7 - {"PKCS7", ERR_LIB_PKCS7}, -#endif -#ifdef ERR_LIB_PROP - {"PROP", ERR_LIB_PROP}, -#endif -#ifdef ERR_LIB_PROV - {"PROV", ERR_LIB_PROV}, -#endif -#ifdef ERR_LIB_PROXY - {"PROXY", ERR_LIB_PROXY}, -#endif -#ifdef ERR_LIB_RAND - {"RAND", ERR_LIB_RAND}, -#endif -#ifdef ERR_LIB_RSA - {"RSA", ERR_LIB_RSA}, -#endif -#ifdef ERR_LIB_RSAREF - {"RSAREF", ERR_LIB_RSAREF}, -#endif -#ifdef ERR_LIB_SM2 - {"SM2", ERR_LIB_SM2}, -#endif -#ifdef ERR_LIB_SSL - {"SSL", ERR_LIB_SSL}, -#endif -#ifdef ERR_LIB_SSL2 - {"SSL2", ERR_LIB_SSL2}, -#endif -#ifdef ERR_LIB_SSL23 - {"SSL23", ERR_LIB_SSL23}, -#endif -#ifdef ERR_LIB_SSL3 - {"SSL3", ERR_LIB_SSL3}, -#endif -#ifdef ERR_LIB_SYS - {"SYS", ERR_LIB_SYS}, -#endif -#ifdef ERR_LIB_TS - {"TS", ERR_LIB_TS}, -#endif -#ifdef ERR_LIB_UI - {"UI", ERR_LIB_UI}, -#endif -#ifdef ERR_LIB_USER - {"USER", ERR_LIB_USER}, -#endif -#ifdef ERR_LIB_X509 - {"X509", ERR_LIB_X509}, -#endif -#ifdef ERR_LIB_X509V3 - {"X509V3", ERR_LIB_X509V3}, -#endif - {NULL, 0} /* sentinel */ -}; - -/* generated from args.reasons */ -static struct py_ssl_error_code error_codes[] = { - #ifdef ASN1_R_ADDING_OBJECT - {"ADDING_OBJECT", ERR_LIB_ASN1, ASN1_R_ADDING_OBJECT}, - #else - {"ADDING_OBJECT", 13, 171}, - #endif - #ifdef ASN1_R_ASN1_PARSE_ERROR - {"ASN1_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_ASN1_PARSE_ERROR}, - #else - {"ASN1_PARSE_ERROR", 13, 203}, - #endif - #ifdef ASN1_R_ASN1_SIG_PARSE_ERROR - {"ASN1_SIG_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_ASN1_SIG_PARSE_ERROR}, - #else - {"ASN1_SIG_PARSE_ERROR", 13, 204}, - #endif - #ifdef ASN1_R_AUX_ERROR - {"AUX_ERROR", ERR_LIB_ASN1, ASN1_R_AUX_ERROR}, - #else - {"AUX_ERROR", 13, 100}, - #endif - #ifdef ASN1_R_BAD_OBJECT_HEADER - {"BAD_OBJECT_HEADER", ERR_LIB_ASN1, ASN1_R_BAD_OBJECT_HEADER}, - #else - {"BAD_OBJECT_HEADER", 13, 102}, - #endif - #ifdef ASN1_R_BAD_TEMPLATE - {"BAD_TEMPLATE", ERR_LIB_ASN1, ASN1_R_BAD_TEMPLATE}, - #else - {"BAD_TEMPLATE", 13, 230}, - #endif - #ifdef ASN1_R_BMPSTRING_IS_WRONG_LENGTH - {"BMPSTRING_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_BMPSTRING_IS_WRONG_LENGTH}, - #else - {"BMPSTRING_IS_WRONG_LENGTH", 13, 214}, - #endif - #ifdef ASN1_R_BN_LIB - {"BN_LIB", ERR_LIB_ASN1, ASN1_R_BN_LIB}, - #else - {"BN_LIB", 13, 105}, - #endif - #ifdef ASN1_R_BOOLEAN_IS_WRONG_LENGTH - {"BOOLEAN_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_BOOLEAN_IS_WRONG_LENGTH}, - #else - {"BOOLEAN_IS_WRONG_LENGTH", 13, 106}, - #endif - #ifdef ASN1_R_BUFFER_TOO_SMALL - {"BUFFER_TOO_SMALL", ERR_LIB_ASN1, ASN1_R_BUFFER_TOO_SMALL}, - #else - {"BUFFER_TOO_SMALL", 13, 107}, - #endif - #ifdef ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER - {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", ERR_LIB_ASN1, ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER}, - #else - {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", 13, 108}, - #endif - #ifdef ASN1_R_CONTEXT_NOT_INITIALISED - {"CONTEXT_NOT_INITIALISED", ERR_LIB_ASN1, ASN1_R_CONTEXT_NOT_INITIALISED}, - #else - {"CONTEXT_NOT_INITIALISED", 13, 217}, - #endif - #ifdef ASN1_R_DATA_IS_WRONG - {"DATA_IS_WRONG", ERR_LIB_ASN1, ASN1_R_DATA_IS_WRONG}, - #else - {"DATA_IS_WRONG", 13, 109}, - #endif - #ifdef ASN1_R_DECODE_ERROR - {"DECODE_ERROR", ERR_LIB_ASN1, ASN1_R_DECODE_ERROR}, - #else - {"DECODE_ERROR", 13, 110}, - #endif - #ifdef ASN1_R_DEPTH_EXCEEDED - {"DEPTH_EXCEEDED", ERR_LIB_ASN1, ASN1_R_DEPTH_EXCEEDED}, - #else - {"DEPTH_EXCEEDED", 13, 174}, - #endif - #ifdef ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED - {"DIGEST_AND_KEY_TYPE_NOT_SUPPORTED", ERR_LIB_ASN1, ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED}, - #else - {"DIGEST_AND_KEY_TYPE_NOT_SUPPORTED", 13, 198}, - #endif - #ifdef ASN1_R_ENCODE_ERROR - {"ENCODE_ERROR", ERR_LIB_ASN1, ASN1_R_ENCODE_ERROR}, - #else - {"ENCODE_ERROR", 13, 112}, - #endif - #ifdef ASN1_R_ERROR_GETTING_TIME - {"ERROR_GETTING_TIME", ERR_LIB_ASN1, ASN1_R_ERROR_GETTING_TIME}, - #else - {"ERROR_GETTING_TIME", 13, 173}, - #endif - #ifdef ASN1_R_ERROR_LOADING_SECTION - {"ERROR_LOADING_SECTION", ERR_LIB_ASN1, ASN1_R_ERROR_LOADING_SECTION}, - #else - {"ERROR_LOADING_SECTION", 13, 172}, - #endif - #ifdef ASN1_R_ERROR_SETTING_CIPHER_PARAMS - {"ERROR_SETTING_CIPHER_PARAMS", ERR_LIB_ASN1, ASN1_R_ERROR_SETTING_CIPHER_PARAMS}, - #else - {"ERROR_SETTING_CIPHER_PARAMS", 13, 114}, - #endif - #ifdef ASN1_R_EXPECTING_AN_INTEGER - {"EXPECTING_AN_INTEGER", ERR_LIB_ASN1, ASN1_R_EXPECTING_AN_INTEGER}, - #else - {"EXPECTING_AN_INTEGER", 13, 115}, - #endif - #ifdef ASN1_R_EXPECTING_AN_OBJECT - {"EXPECTING_AN_OBJECT", ERR_LIB_ASN1, ASN1_R_EXPECTING_AN_OBJECT}, - #else - {"EXPECTING_AN_OBJECT", 13, 116}, - #endif - #ifdef ASN1_R_EXPLICIT_LENGTH_MISMATCH - {"EXPLICIT_LENGTH_MISMATCH", ERR_LIB_ASN1, ASN1_R_EXPLICIT_LENGTH_MISMATCH}, - #else - {"EXPLICIT_LENGTH_MISMATCH", 13, 119}, - #endif - #ifdef ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED - {"EXPLICIT_TAG_NOT_CONSTRUCTED", ERR_LIB_ASN1, ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED}, - #else - {"EXPLICIT_TAG_NOT_CONSTRUCTED", 13, 120}, - #endif - #ifdef ASN1_R_FIELD_MISSING - {"FIELD_MISSING", ERR_LIB_ASN1, ASN1_R_FIELD_MISSING}, - #else - {"FIELD_MISSING", 13, 121}, - #endif - #ifdef ASN1_R_FIRST_NUM_TOO_LARGE - {"FIRST_NUM_TOO_LARGE", ERR_LIB_ASN1, ASN1_R_FIRST_NUM_TOO_LARGE}, - #else - {"FIRST_NUM_TOO_LARGE", 13, 122}, - #endif - #ifdef ASN1_R_GENERALIZEDTIME_IS_TOO_SHORT - {"GENERALIZEDTIME_IS_TOO_SHORT", ERR_LIB_ASN1, ASN1_R_GENERALIZEDTIME_IS_TOO_SHORT}, - #else - {"GENERALIZEDTIME_IS_TOO_SHORT", 13, 232}, - #endif - #ifdef ASN1_R_HEADER_TOO_LONG - {"HEADER_TOO_LONG", ERR_LIB_ASN1, ASN1_R_HEADER_TOO_LONG}, - #else - {"HEADER_TOO_LONG", 13, 123}, - #endif - #ifdef ASN1_R_ILLEGAL_BITSTRING_FORMAT - {"ILLEGAL_BITSTRING_FORMAT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_BITSTRING_FORMAT}, - #else - {"ILLEGAL_BITSTRING_FORMAT", 13, 175}, - #endif - #ifdef ASN1_R_ILLEGAL_BOOLEAN - {"ILLEGAL_BOOLEAN", ERR_LIB_ASN1, ASN1_R_ILLEGAL_BOOLEAN}, - #else - {"ILLEGAL_BOOLEAN", 13, 176}, - #endif - #ifdef ASN1_R_ILLEGAL_CHARACTERS - {"ILLEGAL_CHARACTERS", ERR_LIB_ASN1, ASN1_R_ILLEGAL_CHARACTERS}, - #else - {"ILLEGAL_CHARACTERS", 13, 124}, - #endif - #ifdef ASN1_R_ILLEGAL_FORMAT - {"ILLEGAL_FORMAT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_FORMAT}, - #else - {"ILLEGAL_FORMAT", 13, 177}, - #endif - #ifdef ASN1_R_ILLEGAL_HEX - {"ILLEGAL_HEX", ERR_LIB_ASN1, ASN1_R_ILLEGAL_HEX}, - #else - {"ILLEGAL_HEX", 13, 178}, - #endif - #ifdef ASN1_R_ILLEGAL_IMPLICIT_TAG - {"ILLEGAL_IMPLICIT_TAG", ERR_LIB_ASN1, ASN1_R_ILLEGAL_IMPLICIT_TAG}, - #else - {"ILLEGAL_IMPLICIT_TAG", 13, 179}, - #endif - #ifdef ASN1_R_ILLEGAL_INTEGER - {"ILLEGAL_INTEGER", ERR_LIB_ASN1, ASN1_R_ILLEGAL_INTEGER}, - #else - {"ILLEGAL_INTEGER", 13, 180}, - #endif - #ifdef ASN1_R_ILLEGAL_NEGATIVE_VALUE - {"ILLEGAL_NEGATIVE_VALUE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NEGATIVE_VALUE}, - #else - {"ILLEGAL_NEGATIVE_VALUE", 13, 226}, - #endif - #ifdef ASN1_R_ILLEGAL_NESTED_TAGGING - {"ILLEGAL_NESTED_TAGGING", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NESTED_TAGGING}, - #else - {"ILLEGAL_NESTED_TAGGING", 13, 181}, - #endif - #ifdef ASN1_R_ILLEGAL_NULL - {"ILLEGAL_NULL", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NULL}, - #else - {"ILLEGAL_NULL", 13, 125}, - #endif - #ifdef ASN1_R_ILLEGAL_NULL_VALUE - {"ILLEGAL_NULL_VALUE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NULL_VALUE}, - #else - {"ILLEGAL_NULL_VALUE", 13, 182}, - #endif - #ifdef ASN1_R_ILLEGAL_OBJECT - {"ILLEGAL_OBJECT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_OBJECT}, - #else - {"ILLEGAL_OBJECT", 13, 183}, - #endif - #ifdef ASN1_R_ILLEGAL_OPTIONAL_ANY - {"ILLEGAL_OPTIONAL_ANY", ERR_LIB_ASN1, ASN1_R_ILLEGAL_OPTIONAL_ANY}, - #else - {"ILLEGAL_OPTIONAL_ANY", 13, 126}, - #endif - #ifdef ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE - {"ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE}, - #else - {"ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE", 13, 170}, - #endif - #ifdef ASN1_R_ILLEGAL_PADDING - {"ILLEGAL_PADDING", ERR_LIB_ASN1, ASN1_R_ILLEGAL_PADDING}, - #else - {"ILLEGAL_PADDING", 13, 221}, - #endif - #ifdef ASN1_R_ILLEGAL_TAGGED_ANY - {"ILLEGAL_TAGGED_ANY", ERR_LIB_ASN1, ASN1_R_ILLEGAL_TAGGED_ANY}, - #else - {"ILLEGAL_TAGGED_ANY", 13, 127}, - #endif - #ifdef ASN1_R_ILLEGAL_TIME_VALUE - {"ILLEGAL_TIME_VALUE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_TIME_VALUE}, - #else - {"ILLEGAL_TIME_VALUE", 13, 184}, - #endif - #ifdef ASN1_R_ILLEGAL_ZERO_CONTENT - {"ILLEGAL_ZERO_CONTENT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_ZERO_CONTENT}, - #else - {"ILLEGAL_ZERO_CONTENT", 13, 222}, - #endif - #ifdef ASN1_R_INTEGER_NOT_ASCII_FORMAT - {"INTEGER_NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_INTEGER_NOT_ASCII_FORMAT}, - #else - {"INTEGER_NOT_ASCII_FORMAT", 13, 185}, - #endif - #ifdef ASN1_R_INTEGER_TOO_LARGE_FOR_LONG - {"INTEGER_TOO_LARGE_FOR_LONG", ERR_LIB_ASN1, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG}, - #else - {"INTEGER_TOO_LARGE_FOR_LONG", 13, 128}, - #endif - #ifdef ASN1_R_INVALID_BIT_STRING_BITS_LEFT - {"INVALID_BIT_STRING_BITS_LEFT", ERR_LIB_ASN1, ASN1_R_INVALID_BIT_STRING_BITS_LEFT}, - #else - {"INVALID_BIT_STRING_BITS_LEFT", 13, 220}, - #endif - #ifdef ASN1_R_INVALID_BMPSTRING_LENGTH - {"INVALID_BMPSTRING_LENGTH", ERR_LIB_ASN1, ASN1_R_INVALID_BMPSTRING_LENGTH}, - #else - {"INVALID_BMPSTRING_LENGTH", 13, 129}, - #endif - #ifdef ASN1_R_INVALID_DIGIT - {"INVALID_DIGIT", ERR_LIB_ASN1, ASN1_R_INVALID_DIGIT}, - #else - {"INVALID_DIGIT", 13, 130}, - #endif - #ifdef ASN1_R_INVALID_MIME_TYPE - {"INVALID_MIME_TYPE", ERR_LIB_ASN1, ASN1_R_INVALID_MIME_TYPE}, - #else - {"INVALID_MIME_TYPE", 13, 205}, - #endif - #ifdef ASN1_R_INVALID_MODIFIER - {"INVALID_MODIFIER", ERR_LIB_ASN1, ASN1_R_INVALID_MODIFIER}, - #else - {"INVALID_MODIFIER", 13, 186}, - #endif - #ifdef ASN1_R_INVALID_NUMBER - {"INVALID_NUMBER", ERR_LIB_ASN1, ASN1_R_INVALID_NUMBER}, - #else - {"INVALID_NUMBER", 13, 187}, - #endif - #ifdef ASN1_R_INVALID_OBJECT_ENCODING - {"INVALID_OBJECT_ENCODING", ERR_LIB_ASN1, ASN1_R_INVALID_OBJECT_ENCODING}, - #else - {"INVALID_OBJECT_ENCODING", 13, 216}, - #endif - #ifdef ASN1_R_INVALID_SCRYPT_PARAMETERS - {"INVALID_SCRYPT_PARAMETERS", ERR_LIB_ASN1, ASN1_R_INVALID_SCRYPT_PARAMETERS}, - #else - {"INVALID_SCRYPT_PARAMETERS", 13, 227}, - #endif - #ifdef ASN1_R_INVALID_SEPARATOR - {"INVALID_SEPARATOR", ERR_LIB_ASN1, ASN1_R_INVALID_SEPARATOR}, - #else - {"INVALID_SEPARATOR", 13, 131}, - #endif - #ifdef ASN1_R_INVALID_STRING_TABLE_VALUE - {"INVALID_STRING_TABLE_VALUE", ERR_LIB_ASN1, ASN1_R_INVALID_STRING_TABLE_VALUE}, - #else - {"INVALID_STRING_TABLE_VALUE", 13, 218}, - #endif - #ifdef ASN1_R_INVALID_UNIVERSALSTRING_LENGTH - {"INVALID_UNIVERSALSTRING_LENGTH", ERR_LIB_ASN1, ASN1_R_INVALID_UNIVERSALSTRING_LENGTH}, - #else - {"INVALID_UNIVERSALSTRING_LENGTH", 13, 133}, - #endif - #ifdef ASN1_R_INVALID_UTF8STRING - {"INVALID_UTF8STRING", ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING}, - #else - {"INVALID_UTF8STRING", 13, 134}, - #endif - #ifdef ASN1_R_INVALID_VALUE - {"INVALID_VALUE", ERR_LIB_ASN1, ASN1_R_INVALID_VALUE}, - #else - {"INVALID_VALUE", 13, 219}, - #endif - #ifdef ASN1_R_LENGTH_TOO_LONG - {"LENGTH_TOO_LONG", ERR_LIB_ASN1, ASN1_R_LENGTH_TOO_LONG}, - #else - {"LENGTH_TOO_LONG", 13, 231}, - #endif - #ifdef ASN1_R_LIST_ERROR - {"LIST_ERROR", ERR_LIB_ASN1, ASN1_R_LIST_ERROR}, - #else - {"LIST_ERROR", 13, 188}, - #endif - #ifdef ASN1_R_MIME_NO_CONTENT_TYPE - {"MIME_NO_CONTENT_TYPE", ERR_LIB_ASN1, ASN1_R_MIME_NO_CONTENT_TYPE}, - #else - {"MIME_NO_CONTENT_TYPE", 13, 206}, - #endif - #ifdef ASN1_R_MIME_PARSE_ERROR - {"MIME_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_MIME_PARSE_ERROR}, - #else - {"MIME_PARSE_ERROR", 13, 207}, - #endif - #ifdef ASN1_R_MIME_SIG_PARSE_ERROR - {"MIME_SIG_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_MIME_SIG_PARSE_ERROR}, - #else - {"MIME_SIG_PARSE_ERROR", 13, 208}, - #endif - #ifdef ASN1_R_MISSING_EOC - {"MISSING_EOC", ERR_LIB_ASN1, ASN1_R_MISSING_EOC}, - #else - {"MISSING_EOC", 13, 137}, - #endif - #ifdef ASN1_R_MISSING_SECOND_NUMBER - {"MISSING_SECOND_NUMBER", ERR_LIB_ASN1, ASN1_R_MISSING_SECOND_NUMBER}, - #else - {"MISSING_SECOND_NUMBER", 13, 138}, - #endif - #ifdef ASN1_R_MISSING_VALUE - {"MISSING_VALUE", ERR_LIB_ASN1, ASN1_R_MISSING_VALUE}, - #else - {"MISSING_VALUE", 13, 189}, - #endif - #ifdef ASN1_R_MSTRING_NOT_UNIVERSAL - {"MSTRING_NOT_UNIVERSAL", ERR_LIB_ASN1, ASN1_R_MSTRING_NOT_UNIVERSAL}, - #else - {"MSTRING_NOT_UNIVERSAL", 13, 139}, - #endif - #ifdef ASN1_R_MSTRING_WRONG_TAG - {"MSTRING_WRONG_TAG", ERR_LIB_ASN1, ASN1_R_MSTRING_WRONG_TAG}, - #else - {"MSTRING_WRONG_TAG", 13, 140}, - #endif - #ifdef ASN1_R_NESTED_ASN1_STRING - {"NESTED_ASN1_STRING", ERR_LIB_ASN1, ASN1_R_NESTED_ASN1_STRING}, - #else - {"NESTED_ASN1_STRING", 13, 197}, - #endif - #ifdef ASN1_R_NESTED_TOO_DEEP - {"NESTED_TOO_DEEP", ERR_LIB_ASN1, ASN1_R_NESTED_TOO_DEEP}, - #else - {"NESTED_TOO_DEEP", 13, 201}, - #endif - #ifdef ASN1_R_NON_HEX_CHARACTERS - {"NON_HEX_CHARACTERS", ERR_LIB_ASN1, ASN1_R_NON_HEX_CHARACTERS}, - #else - {"NON_HEX_CHARACTERS", 13, 141}, - #endif - #ifdef ASN1_R_NOT_ASCII_FORMAT - {"NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_NOT_ASCII_FORMAT}, - #else - {"NOT_ASCII_FORMAT", 13, 190}, - #endif - #ifdef ASN1_R_NOT_ENOUGH_DATA - {"NOT_ENOUGH_DATA", ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA}, - #else - {"NOT_ENOUGH_DATA", 13, 142}, - #endif - #ifdef ASN1_R_NO_CONTENT_TYPE - {"NO_CONTENT_TYPE", ERR_LIB_ASN1, ASN1_R_NO_CONTENT_TYPE}, - #else - {"NO_CONTENT_TYPE", 13, 209}, - #endif - #ifdef ASN1_R_NO_MATCHING_CHOICE_TYPE - {"NO_MATCHING_CHOICE_TYPE", ERR_LIB_ASN1, ASN1_R_NO_MATCHING_CHOICE_TYPE}, - #else - {"NO_MATCHING_CHOICE_TYPE", 13, 143}, - #endif - #ifdef ASN1_R_NO_MULTIPART_BODY_FAILURE - {"NO_MULTIPART_BODY_FAILURE", ERR_LIB_ASN1, ASN1_R_NO_MULTIPART_BODY_FAILURE}, - #else - {"NO_MULTIPART_BODY_FAILURE", 13, 210}, - #endif - #ifdef ASN1_R_NO_MULTIPART_BOUNDARY - {"NO_MULTIPART_BOUNDARY", ERR_LIB_ASN1, ASN1_R_NO_MULTIPART_BOUNDARY}, - #else - {"NO_MULTIPART_BOUNDARY", 13, 211}, - #endif - #ifdef ASN1_R_NO_SIG_CONTENT_TYPE - {"NO_SIG_CONTENT_TYPE", ERR_LIB_ASN1, ASN1_R_NO_SIG_CONTENT_TYPE}, - #else - {"NO_SIG_CONTENT_TYPE", 13, 212}, - #endif - #ifdef ASN1_R_NULL_IS_WRONG_LENGTH - {"NULL_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_NULL_IS_WRONG_LENGTH}, - #else - {"NULL_IS_WRONG_LENGTH", 13, 144}, - #endif - #ifdef ASN1_R_OBJECT_NOT_ASCII_FORMAT - {"OBJECT_NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_OBJECT_NOT_ASCII_FORMAT}, - #else - {"OBJECT_NOT_ASCII_FORMAT", 13, 191}, - #endif - #ifdef ASN1_R_ODD_NUMBER_OF_CHARS - {"ODD_NUMBER_OF_CHARS", ERR_LIB_ASN1, ASN1_R_ODD_NUMBER_OF_CHARS}, - #else - {"ODD_NUMBER_OF_CHARS", 13, 145}, - #endif - #ifdef ASN1_R_SECOND_NUMBER_TOO_LARGE - {"SECOND_NUMBER_TOO_LARGE", ERR_LIB_ASN1, ASN1_R_SECOND_NUMBER_TOO_LARGE}, - #else - {"SECOND_NUMBER_TOO_LARGE", 13, 147}, - #endif - #ifdef ASN1_R_SEQUENCE_LENGTH_MISMATCH - {"SEQUENCE_LENGTH_MISMATCH", ERR_LIB_ASN1, ASN1_R_SEQUENCE_LENGTH_MISMATCH}, - #else - {"SEQUENCE_LENGTH_MISMATCH", 13, 148}, - #endif - #ifdef ASN1_R_SEQUENCE_NOT_CONSTRUCTED - {"SEQUENCE_NOT_CONSTRUCTED", ERR_LIB_ASN1, ASN1_R_SEQUENCE_NOT_CONSTRUCTED}, - #else - {"SEQUENCE_NOT_CONSTRUCTED", 13, 149}, - #endif - #ifdef ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG - {"SEQUENCE_OR_SET_NEEDS_CONFIG", ERR_LIB_ASN1, ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG}, - #else - {"SEQUENCE_OR_SET_NEEDS_CONFIG", 13, 192}, - #endif - #ifdef ASN1_R_SHORT_LINE - {"SHORT_LINE", ERR_LIB_ASN1, ASN1_R_SHORT_LINE}, - #else - {"SHORT_LINE", 13, 150}, - #endif - #ifdef ASN1_R_SIG_INVALID_MIME_TYPE - {"SIG_INVALID_MIME_TYPE", ERR_LIB_ASN1, ASN1_R_SIG_INVALID_MIME_TYPE}, - #else - {"SIG_INVALID_MIME_TYPE", 13, 213}, - #endif - #ifdef ASN1_R_STREAMING_NOT_SUPPORTED - {"STREAMING_NOT_SUPPORTED", ERR_LIB_ASN1, ASN1_R_STREAMING_NOT_SUPPORTED}, - #else - {"STREAMING_NOT_SUPPORTED", 13, 202}, - #endif - #ifdef ASN1_R_STRING_TOO_LONG - {"STRING_TOO_LONG", ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG}, - #else - {"STRING_TOO_LONG", 13, 151}, - #endif - #ifdef ASN1_R_STRING_TOO_SHORT - {"STRING_TOO_SHORT", ERR_LIB_ASN1, ASN1_R_STRING_TOO_SHORT}, - #else - {"STRING_TOO_SHORT", 13, 152}, - #endif - #ifdef ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD - {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", ERR_LIB_ASN1, ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD}, - #else - {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", 13, 154}, - #endif - #ifdef ASN1_R_TIME_NOT_ASCII_FORMAT - {"TIME_NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_TIME_NOT_ASCII_FORMAT}, - #else - {"TIME_NOT_ASCII_FORMAT", 13, 193}, - #endif - #ifdef ASN1_R_TOO_LARGE - {"TOO_LARGE", ERR_LIB_ASN1, ASN1_R_TOO_LARGE}, - #else - {"TOO_LARGE", 13, 223}, - #endif - #ifdef ASN1_R_TOO_LONG - {"TOO_LONG", ERR_LIB_ASN1, ASN1_R_TOO_LONG}, - #else - {"TOO_LONG", 13, 155}, - #endif - #ifdef ASN1_R_TOO_SMALL - {"TOO_SMALL", ERR_LIB_ASN1, ASN1_R_TOO_SMALL}, - #else - {"TOO_SMALL", 13, 224}, - #endif - #ifdef ASN1_R_TYPE_NOT_CONSTRUCTED - {"TYPE_NOT_CONSTRUCTED", ERR_LIB_ASN1, ASN1_R_TYPE_NOT_CONSTRUCTED}, - #else - {"TYPE_NOT_CONSTRUCTED", 13, 156}, - #endif - #ifdef ASN1_R_TYPE_NOT_PRIMITIVE - {"TYPE_NOT_PRIMITIVE", ERR_LIB_ASN1, ASN1_R_TYPE_NOT_PRIMITIVE}, - #else - {"TYPE_NOT_PRIMITIVE", 13, 195}, - #endif - #ifdef ASN1_R_UNEXPECTED_EOC - {"UNEXPECTED_EOC", ERR_LIB_ASN1, ASN1_R_UNEXPECTED_EOC}, - #else - {"UNEXPECTED_EOC", 13, 159}, - #endif - #ifdef ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH - {"UNIVERSALSTRING_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH}, - #else - {"UNIVERSALSTRING_IS_WRONG_LENGTH", 13, 215}, - #endif - #ifdef ASN1_R_UNKNOWN_DIGEST - {"UNKNOWN_DIGEST", ERR_LIB_ASN1, ASN1_R_UNKNOWN_DIGEST}, - #else - {"UNKNOWN_DIGEST", 13, 229}, - #endif - #ifdef ASN1_R_UNKNOWN_FORMAT - {"UNKNOWN_FORMAT", ERR_LIB_ASN1, ASN1_R_UNKNOWN_FORMAT}, - #else - {"UNKNOWN_FORMAT", 13, 160}, - #endif - #ifdef ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM - {"UNKNOWN_MESSAGE_DIGEST_ALGORITHM", ERR_LIB_ASN1, ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM}, - #else - {"UNKNOWN_MESSAGE_DIGEST_ALGORITHM", 13, 161}, - #endif - #ifdef ASN1_R_UNKNOWN_OBJECT_TYPE - {"UNKNOWN_OBJECT_TYPE", ERR_LIB_ASN1, ASN1_R_UNKNOWN_OBJECT_TYPE}, - #else - {"UNKNOWN_OBJECT_TYPE", 13, 162}, - #endif - #ifdef ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE - {"UNKNOWN_PUBLIC_KEY_TYPE", ERR_LIB_ASN1, ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE}, - #else - {"UNKNOWN_PUBLIC_KEY_TYPE", 13, 163}, - #endif - #ifdef ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM - {"UNKNOWN_SIGNATURE_ALGORITHM", ERR_LIB_ASN1, ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM}, - #else - {"UNKNOWN_SIGNATURE_ALGORITHM", 13, 199}, - #endif - #ifdef ASN1_R_UNKNOWN_TAG - {"UNKNOWN_TAG", ERR_LIB_ASN1, ASN1_R_UNKNOWN_TAG}, - #else - {"UNKNOWN_TAG", 13, 194}, - #endif - #ifdef ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE - {"UNSUPPORTED_ANY_DEFINED_BY_TYPE", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE}, - #else - {"UNSUPPORTED_ANY_DEFINED_BY_TYPE", 13, 164}, - #endif - #ifdef ASN1_R_UNSUPPORTED_CIPHER - {"UNSUPPORTED_CIPHER", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_CIPHER}, - #else - {"UNSUPPORTED_CIPHER", 13, 228}, - #endif - #ifdef ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE - {"UNSUPPORTED_PUBLIC_KEY_TYPE", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE}, - #else - {"UNSUPPORTED_PUBLIC_KEY_TYPE", 13, 167}, - #endif - #ifdef ASN1_R_UNSUPPORTED_TYPE - {"UNSUPPORTED_TYPE", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_TYPE}, - #else - {"UNSUPPORTED_TYPE", 13, 196}, - #endif - #ifdef ASN1_R_UTCTIME_IS_TOO_SHORT - {"UTCTIME_IS_TOO_SHORT", ERR_LIB_ASN1, ASN1_R_UTCTIME_IS_TOO_SHORT}, - #else - {"UTCTIME_IS_TOO_SHORT", 13, 233}, - #endif - #ifdef ASN1_R_WRONG_INTEGER_TYPE - {"WRONG_INTEGER_TYPE", ERR_LIB_ASN1, ASN1_R_WRONG_INTEGER_TYPE}, - #else - {"WRONG_INTEGER_TYPE", 13, 225}, - #endif - #ifdef ASN1_R_WRONG_PUBLIC_KEY_TYPE - {"WRONG_PUBLIC_KEY_TYPE", ERR_LIB_ASN1, ASN1_R_WRONG_PUBLIC_KEY_TYPE}, - #else - {"WRONG_PUBLIC_KEY_TYPE", 13, 200}, - #endif - #ifdef ASN1_R_WRONG_TAG - {"WRONG_TAG", ERR_LIB_ASN1, ASN1_R_WRONG_TAG}, - #else - {"WRONG_TAG", 13, 168}, - #endif - #ifdef ASYNC_R_FAILED_TO_SET_POOL - {"FAILED_TO_SET_POOL", ERR_LIB_ASYNC, ASYNC_R_FAILED_TO_SET_POOL}, - #else - {"FAILED_TO_SET_POOL", 51, 101}, - #endif - #ifdef ASYNC_R_FAILED_TO_SWAP_CONTEXT - {"FAILED_TO_SWAP_CONTEXT", ERR_LIB_ASYNC, ASYNC_R_FAILED_TO_SWAP_CONTEXT}, - #else - {"FAILED_TO_SWAP_CONTEXT", 51, 102}, - #endif - #ifdef ASYNC_R_INIT_FAILED - {"INIT_FAILED", ERR_LIB_ASYNC, ASYNC_R_INIT_FAILED}, - #else - {"INIT_FAILED", 51, 105}, - #endif - #ifdef ASYNC_R_INVALID_POOL_SIZE - {"INVALID_POOL_SIZE", ERR_LIB_ASYNC, ASYNC_R_INVALID_POOL_SIZE}, - #else - {"INVALID_POOL_SIZE", 51, 103}, - #endif - #ifdef BIO_R_ACCEPT_ERROR - {"ACCEPT_ERROR", ERR_LIB_BIO, BIO_R_ACCEPT_ERROR}, - #else - {"ACCEPT_ERROR", 32, 100}, - #endif - #ifdef BIO_R_ADDRINFO_ADDR_IS_NOT_AF_INET - {"ADDRINFO_ADDR_IS_NOT_AF_INET", ERR_LIB_BIO, BIO_R_ADDRINFO_ADDR_IS_NOT_AF_INET}, - #else - {"ADDRINFO_ADDR_IS_NOT_AF_INET", 32, 141}, - #endif - #ifdef BIO_R_AMBIGUOUS_HOST_OR_SERVICE - {"AMBIGUOUS_HOST_OR_SERVICE", ERR_LIB_BIO, BIO_R_AMBIGUOUS_HOST_OR_SERVICE}, - #else - {"AMBIGUOUS_HOST_OR_SERVICE", 32, 129}, - #endif - #ifdef BIO_R_BAD_FOPEN_MODE - {"BAD_FOPEN_MODE", ERR_LIB_BIO, BIO_R_BAD_FOPEN_MODE}, - #else - {"BAD_FOPEN_MODE", 32, 101}, - #endif - #ifdef BIO_R_BROKEN_PIPE - {"BROKEN_PIPE", ERR_LIB_BIO, BIO_R_BROKEN_PIPE}, - #else - {"BROKEN_PIPE", 32, 124}, - #endif - #ifdef BIO_R_CONNECT_ERROR - {"CONNECT_ERROR", ERR_LIB_BIO, BIO_R_CONNECT_ERROR}, - #else - {"CONNECT_ERROR", 32, 103}, - #endif - #ifdef BIO_R_CONNECT_TIMEOUT - {"CONNECT_TIMEOUT", ERR_LIB_BIO, BIO_R_CONNECT_TIMEOUT}, - #else - {"CONNECT_TIMEOUT", 32, 147}, - #endif - #ifdef BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET - {"GETHOSTBYNAME_ADDR_IS_NOT_AF_INET", ERR_LIB_BIO, BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET}, - #else - {"GETHOSTBYNAME_ADDR_IS_NOT_AF_INET", 32, 107}, - #endif - #ifdef BIO_R_GETSOCKNAME_ERROR - {"GETSOCKNAME_ERROR", ERR_LIB_BIO, BIO_R_GETSOCKNAME_ERROR}, - #else - {"GETSOCKNAME_ERROR", 32, 132}, - #endif - #ifdef BIO_R_GETSOCKNAME_TRUNCATED_ADDRESS - {"GETSOCKNAME_TRUNCATED_ADDRESS", ERR_LIB_BIO, BIO_R_GETSOCKNAME_TRUNCATED_ADDRESS}, - #else - {"GETSOCKNAME_TRUNCATED_ADDRESS", 32, 133}, - #endif - #ifdef BIO_R_GETTING_SOCKTYPE - {"GETTING_SOCKTYPE", ERR_LIB_BIO, BIO_R_GETTING_SOCKTYPE}, - #else - {"GETTING_SOCKTYPE", 32, 134}, - #endif - #ifdef BIO_R_INVALID_ARGUMENT - {"INVALID_ARGUMENT", ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT}, - #else - {"INVALID_ARGUMENT", 32, 125}, - #endif - #ifdef BIO_R_INVALID_SOCKET - {"INVALID_SOCKET", ERR_LIB_BIO, BIO_R_INVALID_SOCKET}, - #else - {"INVALID_SOCKET", 32, 135}, - #endif - #ifdef BIO_R_IN_USE - {"IN_USE", ERR_LIB_BIO, BIO_R_IN_USE}, - #else - {"IN_USE", 32, 123}, - #endif - #ifdef BIO_R_LENGTH_TOO_LONG - {"LENGTH_TOO_LONG", ERR_LIB_BIO, BIO_R_LENGTH_TOO_LONG}, - #else - {"LENGTH_TOO_LONG", 32, 102}, - #endif - #ifdef BIO_R_LISTEN_V6_ONLY - {"LISTEN_V6_ONLY", ERR_LIB_BIO, BIO_R_LISTEN_V6_ONLY}, - #else - {"LISTEN_V6_ONLY", 32, 136}, - #endif - #ifdef BIO_R_LOCAL_ADDR_NOT_AVAILABLE - {"LOCAL_ADDR_NOT_AVAILABLE", ERR_LIB_BIO, BIO_R_LOCAL_ADDR_NOT_AVAILABLE}, - #else - {"LOCAL_ADDR_NOT_AVAILABLE", 32, 111}, - #endif - #ifdef BIO_R_LOOKUP_RETURNED_NOTHING - {"LOOKUP_RETURNED_NOTHING", ERR_LIB_BIO, BIO_R_LOOKUP_RETURNED_NOTHING}, - #else - {"LOOKUP_RETURNED_NOTHING", 32, 142}, - #endif - #ifdef BIO_R_MALFORMED_HOST_OR_SERVICE - {"MALFORMED_HOST_OR_SERVICE", ERR_LIB_BIO, BIO_R_MALFORMED_HOST_OR_SERVICE}, - #else - {"MALFORMED_HOST_OR_SERVICE", 32, 130}, - #endif - #ifdef BIO_R_NBIO_CONNECT_ERROR - {"NBIO_CONNECT_ERROR", ERR_LIB_BIO, BIO_R_NBIO_CONNECT_ERROR}, - #else - {"NBIO_CONNECT_ERROR", 32, 110}, - #endif - #ifdef BIO_R_NON_FATAL - {"NON_FATAL", ERR_LIB_BIO, BIO_R_NON_FATAL}, - #else - {"NON_FATAL", 32, 112}, - #endif - #ifdef BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED - {"NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED", ERR_LIB_BIO, BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED}, - #else - {"NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED", 32, 143}, - #endif - #ifdef BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED - {"NO_HOSTNAME_OR_SERVICE_SPECIFIED", ERR_LIB_BIO, BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED}, - #else - {"NO_HOSTNAME_OR_SERVICE_SPECIFIED", 32, 144}, - #endif - #ifdef BIO_R_NO_PORT_DEFINED - {"NO_PORT_DEFINED", ERR_LIB_BIO, BIO_R_NO_PORT_DEFINED}, - #else - {"NO_PORT_DEFINED", 32, 113}, - #endif - #ifdef BIO_R_NO_SUCH_FILE - {"NO_SUCH_FILE", ERR_LIB_BIO, BIO_R_NO_SUCH_FILE}, - #else - {"NO_SUCH_FILE", 32, 128}, - #endif - #ifdef BIO_R_PEER_ADDR_NOT_AVAILABLE - {"PEER_ADDR_NOT_AVAILABLE", ERR_LIB_BIO, BIO_R_PEER_ADDR_NOT_AVAILABLE}, - #else - {"PEER_ADDR_NOT_AVAILABLE", 32, 114}, - #endif - #ifdef BIO_R_PORT_MISMATCH - {"PORT_MISMATCH", ERR_LIB_BIO, BIO_R_PORT_MISMATCH}, - #else - {"PORT_MISMATCH", 32, 150}, - #endif - #ifdef BIO_R_TFO_DISABLED - {"TFO_DISABLED", ERR_LIB_BIO, BIO_R_TFO_DISABLED}, - #else - {"TFO_DISABLED", 32, 106}, - #endif - #ifdef BIO_R_TFO_NO_KERNEL_SUPPORT - {"TFO_NO_KERNEL_SUPPORT", ERR_LIB_BIO, BIO_R_TFO_NO_KERNEL_SUPPORT}, - #else - {"TFO_NO_KERNEL_SUPPORT", 32, 108}, - #endif - #ifdef BIO_R_TRANSFER_ERROR - {"TRANSFER_ERROR", ERR_LIB_BIO, BIO_R_TRANSFER_ERROR}, - #else - {"TRANSFER_ERROR", 32, 104}, - #endif - #ifdef BIO_R_TRANSFER_TIMEOUT - {"TRANSFER_TIMEOUT", ERR_LIB_BIO, BIO_R_TRANSFER_TIMEOUT}, - #else - {"TRANSFER_TIMEOUT", 32, 105}, - #endif - #ifdef BIO_R_UNABLE_TO_BIND_SOCKET - {"UNABLE_TO_BIND_SOCKET", ERR_LIB_BIO, BIO_R_UNABLE_TO_BIND_SOCKET}, - #else - {"UNABLE_TO_BIND_SOCKET", 32, 117}, - #endif - #ifdef BIO_R_UNABLE_TO_CREATE_SOCKET - {"UNABLE_TO_CREATE_SOCKET", ERR_LIB_BIO, BIO_R_UNABLE_TO_CREATE_SOCKET}, - #else - {"UNABLE_TO_CREATE_SOCKET", 32, 118}, - #endif - #ifdef BIO_R_UNABLE_TO_KEEPALIVE - {"UNABLE_TO_KEEPALIVE", ERR_LIB_BIO, BIO_R_UNABLE_TO_KEEPALIVE}, - #else - {"UNABLE_TO_KEEPALIVE", 32, 137}, - #endif - #ifdef BIO_R_UNABLE_TO_LISTEN_SOCKET - {"UNABLE_TO_LISTEN_SOCKET", ERR_LIB_BIO, BIO_R_UNABLE_TO_LISTEN_SOCKET}, - #else - {"UNABLE_TO_LISTEN_SOCKET", 32, 119}, - #endif - #ifdef BIO_R_UNABLE_TO_NODELAY - {"UNABLE_TO_NODELAY", ERR_LIB_BIO, BIO_R_UNABLE_TO_NODELAY}, - #else - {"UNABLE_TO_NODELAY", 32, 138}, - #endif - #ifdef BIO_R_UNABLE_TO_REUSEADDR - {"UNABLE_TO_REUSEADDR", ERR_LIB_BIO, BIO_R_UNABLE_TO_REUSEADDR}, - #else - {"UNABLE_TO_REUSEADDR", 32, 139}, - #endif - #ifdef BIO_R_UNABLE_TO_TFO - {"UNABLE_TO_TFO", ERR_LIB_BIO, BIO_R_UNABLE_TO_TFO}, - #else - {"UNABLE_TO_TFO", 32, 109}, - #endif - #ifdef BIO_R_UNAVAILABLE_IP_FAMILY - {"UNAVAILABLE_IP_FAMILY", ERR_LIB_BIO, BIO_R_UNAVAILABLE_IP_FAMILY}, - #else - {"UNAVAILABLE_IP_FAMILY", 32, 145}, - #endif - #ifdef BIO_R_UNINITIALIZED - {"UNINITIALIZED", ERR_LIB_BIO, BIO_R_UNINITIALIZED}, - #else - {"UNINITIALIZED", 32, 120}, - #endif - #ifdef BIO_R_UNKNOWN_INFO_TYPE - {"UNKNOWN_INFO_TYPE", ERR_LIB_BIO, BIO_R_UNKNOWN_INFO_TYPE}, - #else - {"UNKNOWN_INFO_TYPE", 32, 140}, - #endif - #ifdef BIO_R_UNSUPPORTED_IP_FAMILY - {"UNSUPPORTED_IP_FAMILY", ERR_LIB_BIO, BIO_R_UNSUPPORTED_IP_FAMILY}, - #else - {"UNSUPPORTED_IP_FAMILY", 32, 146}, - #endif - #ifdef BIO_R_UNSUPPORTED_METHOD - {"UNSUPPORTED_METHOD", ERR_LIB_BIO, BIO_R_UNSUPPORTED_METHOD}, - #else - {"UNSUPPORTED_METHOD", 32, 121}, - #endif - #ifdef BIO_R_UNSUPPORTED_PROTOCOL_FAMILY - {"UNSUPPORTED_PROTOCOL_FAMILY", ERR_LIB_BIO, BIO_R_UNSUPPORTED_PROTOCOL_FAMILY}, - #else - {"UNSUPPORTED_PROTOCOL_FAMILY", 32, 131}, - #endif - #ifdef BIO_R_WRITE_TO_READ_ONLY_BIO - {"WRITE_TO_READ_ONLY_BIO", ERR_LIB_BIO, BIO_R_WRITE_TO_READ_ONLY_BIO}, - #else - {"WRITE_TO_READ_ONLY_BIO", 32, 126}, - #endif - #ifdef BIO_R_WSASTARTUP - {"WSASTARTUP", ERR_LIB_BIO, BIO_R_WSASTARTUP}, - #else - {"WSASTARTUP", 32, 122}, - #endif - #ifdef BN_R_ARG2_LT_ARG3 - {"ARG2_LT_ARG3", ERR_LIB_BN, BN_R_ARG2_LT_ARG3}, - #else - {"ARG2_LT_ARG3", 3, 100}, - #endif - #ifdef BN_R_BAD_RECIPROCAL - {"BAD_RECIPROCAL", ERR_LIB_BN, BN_R_BAD_RECIPROCAL}, - #else - {"BAD_RECIPROCAL", 3, 101}, - #endif - #ifdef BN_R_BIGNUM_TOO_LONG - {"BIGNUM_TOO_LONG", ERR_LIB_BN, BN_R_BIGNUM_TOO_LONG}, - #else - {"BIGNUM_TOO_LONG", 3, 114}, - #endif - #ifdef BN_R_BITS_TOO_SMALL - {"BITS_TOO_SMALL", ERR_LIB_BN, BN_R_BITS_TOO_SMALL}, - #else - {"BITS_TOO_SMALL", 3, 118}, - #endif - #ifdef BN_R_CALLED_WITH_EVEN_MODULUS - {"CALLED_WITH_EVEN_MODULUS", ERR_LIB_BN, BN_R_CALLED_WITH_EVEN_MODULUS}, - #else - {"CALLED_WITH_EVEN_MODULUS", 3, 102}, - #endif - #ifdef BN_R_DIV_BY_ZERO - {"DIV_BY_ZERO", ERR_LIB_BN, BN_R_DIV_BY_ZERO}, - #else - {"DIV_BY_ZERO", 3, 103}, - #endif - #ifdef BN_R_ENCODING_ERROR - {"ENCODING_ERROR", ERR_LIB_BN, BN_R_ENCODING_ERROR}, - #else - {"ENCODING_ERROR", 3, 104}, - #endif - #ifdef BN_R_EXPAND_ON_STATIC_BIGNUM_DATA - {"EXPAND_ON_STATIC_BIGNUM_DATA", ERR_LIB_BN, BN_R_EXPAND_ON_STATIC_BIGNUM_DATA}, - #else - {"EXPAND_ON_STATIC_BIGNUM_DATA", 3, 105}, - #endif - #ifdef BN_R_INPUT_NOT_REDUCED - {"INPUT_NOT_REDUCED", ERR_LIB_BN, BN_R_INPUT_NOT_REDUCED}, - #else - {"INPUT_NOT_REDUCED", 3, 110}, - #endif - #ifdef BN_R_INVALID_LENGTH - {"INVALID_LENGTH", ERR_LIB_BN, BN_R_INVALID_LENGTH}, - #else - {"INVALID_LENGTH", 3, 106}, - #endif - #ifdef BN_R_INVALID_RANGE - {"INVALID_RANGE", ERR_LIB_BN, BN_R_INVALID_RANGE}, - #else - {"INVALID_RANGE", 3, 115}, - #endif - #ifdef BN_R_INVALID_SHIFT - {"INVALID_SHIFT", ERR_LIB_BN, BN_R_INVALID_SHIFT}, - #else - {"INVALID_SHIFT", 3, 119}, - #endif - #ifdef BN_R_NOT_A_SQUARE - {"NOT_A_SQUARE", ERR_LIB_BN, BN_R_NOT_A_SQUARE}, - #else - {"NOT_A_SQUARE", 3, 111}, - #endif - #ifdef BN_R_NOT_INITIALIZED - {"NOT_INITIALIZED", ERR_LIB_BN, BN_R_NOT_INITIALIZED}, - #else - {"NOT_INITIALIZED", 3, 107}, - #endif - #ifdef BN_R_NO_INVERSE - {"NO_INVERSE", ERR_LIB_BN, BN_R_NO_INVERSE}, - #else - {"NO_INVERSE", 3, 108}, - #endif - #ifdef BN_R_NO_PRIME_CANDIDATE - {"NO_PRIME_CANDIDATE", ERR_LIB_BN, BN_R_NO_PRIME_CANDIDATE}, - #else - {"NO_PRIME_CANDIDATE", 3, 121}, - #endif - #ifdef BN_R_NO_SOLUTION - {"NO_SOLUTION", ERR_LIB_BN, BN_R_NO_SOLUTION}, - #else - {"NO_SOLUTION", 3, 116}, - #endif - #ifdef BN_R_NO_SUITABLE_DIGEST - {"NO_SUITABLE_DIGEST", ERR_LIB_BN, BN_R_NO_SUITABLE_DIGEST}, - #else - {"NO_SUITABLE_DIGEST", 3, 120}, - #endif - #ifdef BN_R_PRIVATE_KEY_TOO_LARGE - {"PRIVATE_KEY_TOO_LARGE", ERR_LIB_BN, BN_R_PRIVATE_KEY_TOO_LARGE}, - #else - {"PRIVATE_KEY_TOO_LARGE", 3, 117}, - #endif - #ifdef BN_R_P_IS_NOT_PRIME - {"P_IS_NOT_PRIME", ERR_LIB_BN, BN_R_P_IS_NOT_PRIME}, - #else - {"P_IS_NOT_PRIME", 3, 112}, - #endif - #ifdef BN_R_TOO_MANY_ITERATIONS - {"TOO_MANY_ITERATIONS", ERR_LIB_BN, BN_R_TOO_MANY_ITERATIONS}, - #else - {"TOO_MANY_ITERATIONS", 3, 113}, - #endif - #ifdef BN_R_TOO_MANY_TEMPORARY_VARIABLES - {"TOO_MANY_TEMPORARY_VARIABLES", ERR_LIB_BN, BN_R_TOO_MANY_TEMPORARY_VARIABLES}, - #else - {"TOO_MANY_TEMPORARY_VARIABLES", 3, 109}, - #endif - #ifdef CMP_R_ALGORITHM_NOT_SUPPORTED - {"ALGORITHM_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_ALGORITHM_NOT_SUPPORTED}, - #else - {"ALGORITHM_NOT_SUPPORTED", 58, 139}, - #endif - #ifdef CMP_R_BAD_CHECKAFTER_IN_POLLREP - {"BAD_CHECKAFTER_IN_POLLREP", ERR_LIB_CMP, CMP_R_BAD_CHECKAFTER_IN_POLLREP}, - #else - {"BAD_CHECKAFTER_IN_POLLREP", 58, 167}, - #endif - #ifdef CMP_R_BAD_REQUEST_ID - {"BAD_REQUEST_ID", ERR_LIB_CMP, CMP_R_BAD_REQUEST_ID}, - #else - {"BAD_REQUEST_ID", 58, 108}, - #endif - #ifdef CMP_R_CERTHASH_UNMATCHED - {"CERTHASH_UNMATCHED", ERR_LIB_CMP, CMP_R_CERTHASH_UNMATCHED}, - #else - {"CERTHASH_UNMATCHED", 58, 156}, - #endif - #ifdef CMP_R_CERTID_NOT_FOUND - {"CERTID_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTID_NOT_FOUND}, - #else - {"CERTID_NOT_FOUND", 58, 109}, - #endif - #ifdef CMP_R_CERTIFICATE_NOT_ACCEPTED - {"CERTIFICATE_NOT_ACCEPTED", ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_ACCEPTED}, - #else - {"CERTIFICATE_NOT_ACCEPTED", 58, 169}, - #endif - #ifdef CMP_R_CERTIFICATE_NOT_FOUND - {"CERTIFICATE_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_FOUND}, - #else - {"CERTIFICATE_NOT_FOUND", 58, 112}, - #endif - #ifdef CMP_R_CERTREQMSG_NOT_FOUND - {"CERTREQMSG_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTREQMSG_NOT_FOUND}, - #else - {"CERTREQMSG_NOT_FOUND", 58, 157}, - #endif - #ifdef CMP_R_CERTRESPONSE_NOT_FOUND - {"CERTRESPONSE_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTRESPONSE_NOT_FOUND}, - #else - {"CERTRESPONSE_NOT_FOUND", 58, 113}, - #endif - #ifdef CMP_R_CERT_AND_KEY_DO_NOT_MATCH - {"CERT_AND_KEY_DO_NOT_MATCH", ERR_LIB_CMP, CMP_R_CERT_AND_KEY_DO_NOT_MATCH}, - #else - {"CERT_AND_KEY_DO_NOT_MATCH", 58, 114}, - #endif - #ifdef CMP_R_CHECKAFTER_OUT_OF_RANGE - {"CHECKAFTER_OUT_OF_RANGE", ERR_LIB_CMP, CMP_R_CHECKAFTER_OUT_OF_RANGE}, - #else - {"CHECKAFTER_OUT_OF_RANGE", 58, 181}, - #endif - #ifdef CMP_R_ENCOUNTERED_KEYUPDATEWARNING - {"ENCOUNTERED_KEYUPDATEWARNING", ERR_LIB_CMP, CMP_R_ENCOUNTERED_KEYUPDATEWARNING}, - #else - {"ENCOUNTERED_KEYUPDATEWARNING", 58, 176}, - #endif - #ifdef CMP_R_ENCOUNTERED_WAITING - {"ENCOUNTERED_WAITING", ERR_LIB_CMP, CMP_R_ENCOUNTERED_WAITING}, - #else - {"ENCOUNTERED_WAITING", 58, 162}, - #endif - #ifdef CMP_R_ERROR_CALCULATING_PROTECTION - {"ERROR_CALCULATING_PROTECTION", ERR_LIB_CMP, CMP_R_ERROR_CALCULATING_PROTECTION}, - #else - {"ERROR_CALCULATING_PROTECTION", 58, 115}, - #endif - #ifdef CMP_R_ERROR_CREATING_CERTCONF - {"ERROR_CREATING_CERTCONF", ERR_LIB_CMP, CMP_R_ERROR_CREATING_CERTCONF}, - #else - {"ERROR_CREATING_CERTCONF", 58, 116}, - #endif - #ifdef CMP_R_ERROR_CREATING_CERTREP - {"ERROR_CREATING_CERTREP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_CERTREP}, - #else - {"ERROR_CREATING_CERTREP", 58, 117}, - #endif - #ifdef CMP_R_ERROR_CREATING_CERTREQ - {"ERROR_CREATING_CERTREQ", ERR_LIB_CMP, CMP_R_ERROR_CREATING_CERTREQ}, - #else - {"ERROR_CREATING_CERTREQ", 58, 163}, - #endif - #ifdef CMP_R_ERROR_CREATING_ERROR - {"ERROR_CREATING_ERROR", ERR_LIB_CMP, CMP_R_ERROR_CREATING_ERROR}, - #else - {"ERROR_CREATING_ERROR", 58, 118}, - #endif - #ifdef CMP_R_ERROR_CREATING_GENM - {"ERROR_CREATING_GENM", ERR_LIB_CMP, CMP_R_ERROR_CREATING_GENM}, - #else - {"ERROR_CREATING_GENM", 58, 119}, - #endif - #ifdef CMP_R_ERROR_CREATING_GENP - {"ERROR_CREATING_GENP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_GENP}, - #else - {"ERROR_CREATING_GENP", 58, 120}, - #endif - #ifdef CMP_R_ERROR_CREATING_PKICONF - {"ERROR_CREATING_PKICONF", ERR_LIB_CMP, CMP_R_ERROR_CREATING_PKICONF}, - #else - {"ERROR_CREATING_PKICONF", 58, 122}, - #endif - #ifdef CMP_R_ERROR_CREATING_POLLREP - {"ERROR_CREATING_POLLREP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_POLLREP}, - #else - {"ERROR_CREATING_POLLREP", 58, 123}, - #endif - #ifdef CMP_R_ERROR_CREATING_POLLREQ - {"ERROR_CREATING_POLLREQ", ERR_LIB_CMP, CMP_R_ERROR_CREATING_POLLREQ}, - #else - {"ERROR_CREATING_POLLREQ", 58, 124}, - #endif - #ifdef CMP_R_ERROR_CREATING_RP - {"ERROR_CREATING_RP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_RP}, - #else - {"ERROR_CREATING_RP", 58, 125}, - #endif - #ifdef CMP_R_ERROR_CREATING_RR - {"ERROR_CREATING_RR", ERR_LIB_CMP, CMP_R_ERROR_CREATING_RR}, - #else - {"ERROR_CREATING_RR", 58, 126}, - #endif - #ifdef CMP_R_ERROR_PARSING_PKISTATUS - {"ERROR_PARSING_PKISTATUS", ERR_LIB_CMP, CMP_R_ERROR_PARSING_PKISTATUS}, - #else - {"ERROR_PARSING_PKISTATUS", 58, 107}, - #endif - #ifdef CMP_R_ERROR_PROCESSING_MESSAGE - {"ERROR_PROCESSING_MESSAGE", ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE}, - #else - {"ERROR_PROCESSING_MESSAGE", 58, 158}, - #endif - #ifdef CMP_R_ERROR_PROTECTING_MESSAGE - {"ERROR_PROTECTING_MESSAGE", ERR_LIB_CMP, CMP_R_ERROR_PROTECTING_MESSAGE}, - #else - {"ERROR_PROTECTING_MESSAGE", 58, 127}, - #endif - #ifdef CMP_R_ERROR_SETTING_CERTHASH - {"ERROR_SETTING_CERTHASH", ERR_LIB_CMP, CMP_R_ERROR_SETTING_CERTHASH}, - #else - {"ERROR_SETTING_CERTHASH", 58, 128}, - #endif - #ifdef CMP_R_ERROR_UNEXPECTED_CERTCONF - {"ERROR_UNEXPECTED_CERTCONF", ERR_LIB_CMP, CMP_R_ERROR_UNEXPECTED_CERTCONF}, - #else - {"ERROR_UNEXPECTED_CERTCONF", 58, 160}, - #endif - #ifdef CMP_R_ERROR_VALIDATING_PROTECTION - {"ERROR_VALIDATING_PROTECTION", ERR_LIB_CMP, CMP_R_ERROR_VALIDATING_PROTECTION}, - #else - {"ERROR_VALIDATING_PROTECTION", 58, 140}, - #endif - #ifdef CMP_R_ERROR_VALIDATING_SIGNATURE - {"ERROR_VALIDATING_SIGNATURE", ERR_LIB_CMP, CMP_R_ERROR_VALIDATING_SIGNATURE}, - #else - {"ERROR_VALIDATING_SIGNATURE", 58, 171}, - #endif - #ifdef CMP_R_EXPECTED_POLLREQ - {"EXPECTED_POLLREQ", ERR_LIB_CMP, CMP_R_EXPECTED_POLLREQ}, - #else - {"EXPECTED_POLLREQ", 58, 104}, - #endif - #ifdef CMP_R_FAILED_BUILDING_OWN_CHAIN - {"FAILED_BUILDING_OWN_CHAIN", ERR_LIB_CMP, CMP_R_FAILED_BUILDING_OWN_CHAIN}, - #else - {"FAILED_BUILDING_OWN_CHAIN", 58, 164}, - #endif - #ifdef CMP_R_FAILED_EXTRACTING_PUBKEY - {"FAILED_EXTRACTING_PUBKEY", ERR_LIB_CMP, CMP_R_FAILED_EXTRACTING_PUBKEY}, - #else - {"FAILED_EXTRACTING_PUBKEY", 58, 141}, - #endif - #ifdef CMP_R_FAILURE_OBTAINING_RANDOM - {"FAILURE_OBTAINING_RANDOM", ERR_LIB_CMP, CMP_R_FAILURE_OBTAINING_RANDOM}, - #else - {"FAILURE_OBTAINING_RANDOM", 58, 110}, - #endif - #ifdef CMP_R_FAIL_INFO_OUT_OF_RANGE - {"FAIL_INFO_OUT_OF_RANGE", ERR_LIB_CMP, CMP_R_FAIL_INFO_OUT_OF_RANGE}, - #else - {"FAIL_INFO_OUT_OF_RANGE", 58, 129}, - #endif - #ifdef CMP_R_GENERATE_CERTREQTEMPLATE - {"GENERATE_CERTREQTEMPLATE", ERR_LIB_CMP, CMP_R_GENERATE_CERTREQTEMPLATE}, - #else - {"GENERATE_CERTREQTEMPLATE", 58, 197}, - #endif - #ifdef CMP_R_GENERATE_CRLSTATUS - {"GENERATE_CRLSTATUS", ERR_LIB_CMP, CMP_R_GENERATE_CRLSTATUS}, - #else - {"GENERATE_CRLSTATUS", 58, 198}, - #endif - #ifdef CMP_R_GETTING_GENP - {"GETTING_GENP", ERR_LIB_CMP, CMP_R_GETTING_GENP}, - #else - {"GETTING_GENP", 58, 192}, - #endif - #ifdef CMP_R_GET_ITAV - {"GET_ITAV", ERR_LIB_CMP, CMP_R_GET_ITAV}, - #else - {"GET_ITAV", 58, 199}, - #endif - #ifdef CMP_R_INVALID_ARGS - {"INVALID_ARGS", ERR_LIB_CMP, CMP_R_INVALID_ARGS}, - #else - {"INVALID_ARGS", 58, 100}, - #endif - #ifdef CMP_R_INVALID_GENP - {"INVALID_GENP", ERR_LIB_CMP, CMP_R_INVALID_GENP}, - #else - {"INVALID_GENP", 58, 193}, - #endif - #ifdef CMP_R_INVALID_KEYSPEC - {"INVALID_KEYSPEC", ERR_LIB_CMP, CMP_R_INVALID_KEYSPEC}, - #else - {"INVALID_KEYSPEC", 58, 202}, - #endif - #ifdef CMP_R_INVALID_OPTION - {"INVALID_OPTION", ERR_LIB_CMP, CMP_R_INVALID_OPTION}, - #else - {"INVALID_OPTION", 58, 174}, - #endif - #ifdef CMP_R_INVALID_ROOTCAKEYUPDATE - {"INVALID_ROOTCAKEYUPDATE", ERR_LIB_CMP, CMP_R_INVALID_ROOTCAKEYUPDATE}, - #else - {"INVALID_ROOTCAKEYUPDATE", 58, 195}, - #endif - #ifdef CMP_R_MISSING_CERTID - {"MISSING_CERTID", ERR_LIB_CMP, CMP_R_MISSING_CERTID}, - #else - {"MISSING_CERTID", 58, 165}, - #endif - #ifdef CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION - {"MISSING_KEY_INPUT_FOR_CREATING_PROTECTION", ERR_LIB_CMP, CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION}, - #else - {"MISSING_KEY_INPUT_FOR_CREATING_PROTECTION", 58, 130}, - #endif - #ifdef CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE - {"MISSING_KEY_USAGE_DIGITALSIGNATURE", ERR_LIB_CMP, CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE}, - #else - {"MISSING_KEY_USAGE_DIGITALSIGNATURE", 58, 142}, - #endif - #ifdef CMP_R_MISSING_P10CSR - {"MISSING_P10CSR", ERR_LIB_CMP, CMP_R_MISSING_P10CSR}, - #else - {"MISSING_P10CSR", 58, 121}, - #endif - #ifdef CMP_R_MISSING_PBM_SECRET - {"MISSING_PBM_SECRET", ERR_LIB_CMP, CMP_R_MISSING_PBM_SECRET}, - #else - {"MISSING_PBM_SECRET", 58, 166}, - #endif - #ifdef CMP_R_MISSING_PRIVATE_KEY - {"MISSING_PRIVATE_KEY", ERR_LIB_CMP, CMP_R_MISSING_PRIVATE_KEY}, - #else - {"MISSING_PRIVATE_KEY", 58, 131}, - #endif - #ifdef CMP_R_MISSING_PRIVATE_KEY_FOR_POPO - {"MISSING_PRIVATE_KEY_FOR_POPO", ERR_LIB_CMP, CMP_R_MISSING_PRIVATE_KEY_FOR_POPO}, - #else - {"MISSING_PRIVATE_KEY_FOR_POPO", 58, 190}, - #endif - #ifdef CMP_R_MISSING_PROTECTION - {"MISSING_PROTECTION", ERR_LIB_CMP, CMP_R_MISSING_PROTECTION}, - #else - {"MISSING_PROTECTION", 58, 143}, - #endif - #ifdef CMP_R_MISSING_PUBLIC_KEY - {"MISSING_PUBLIC_KEY", ERR_LIB_CMP, CMP_R_MISSING_PUBLIC_KEY}, - #else - {"MISSING_PUBLIC_KEY", 58, 183}, - #endif - #ifdef CMP_R_MISSING_REFERENCE_CERT - {"MISSING_REFERENCE_CERT", ERR_LIB_CMP, CMP_R_MISSING_REFERENCE_CERT}, - #else - {"MISSING_REFERENCE_CERT", 58, 168}, - #endif - #ifdef CMP_R_MISSING_SECRET - {"MISSING_SECRET", ERR_LIB_CMP, CMP_R_MISSING_SECRET}, - #else - {"MISSING_SECRET", 58, 178}, - #endif - #ifdef CMP_R_MISSING_SENDER_IDENTIFICATION - {"MISSING_SENDER_IDENTIFICATION", ERR_LIB_CMP, CMP_R_MISSING_SENDER_IDENTIFICATION}, - #else - {"MISSING_SENDER_IDENTIFICATION", 58, 111}, - #endif - #ifdef CMP_R_MISSING_TRUST_ANCHOR - {"MISSING_TRUST_ANCHOR", ERR_LIB_CMP, CMP_R_MISSING_TRUST_ANCHOR}, - #else - {"MISSING_TRUST_ANCHOR", 58, 179}, - #endif - #ifdef CMP_R_MISSING_TRUST_STORE - {"MISSING_TRUST_STORE", ERR_LIB_CMP, CMP_R_MISSING_TRUST_STORE}, - #else - {"MISSING_TRUST_STORE", 58, 144}, - #endif - #ifdef CMP_R_MULTIPLE_REQUESTS_NOT_SUPPORTED - {"MULTIPLE_REQUESTS_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_MULTIPLE_REQUESTS_NOT_SUPPORTED}, - #else - {"MULTIPLE_REQUESTS_NOT_SUPPORTED", 58, 161}, - #endif - #ifdef CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED - {"MULTIPLE_RESPONSES_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED}, - #else - {"MULTIPLE_RESPONSES_NOT_SUPPORTED", 58, 170}, - #endif - #ifdef CMP_R_MULTIPLE_SAN_SOURCES - {"MULTIPLE_SAN_SOURCES", ERR_LIB_CMP, CMP_R_MULTIPLE_SAN_SOURCES}, - #else - {"MULTIPLE_SAN_SOURCES", 58, 102}, - #endif - #ifdef CMP_R_NO_STDIO - {"NO_STDIO", ERR_LIB_CMP, CMP_R_NO_STDIO}, - #else - {"NO_STDIO", 58, 194}, - #endif - #ifdef CMP_R_NO_SUITABLE_SENDER_CERT - {"NO_SUITABLE_SENDER_CERT", ERR_LIB_CMP, CMP_R_NO_SUITABLE_SENDER_CERT}, - #else - {"NO_SUITABLE_SENDER_CERT", 58, 145}, - #endif - #ifdef CMP_R_NULL_ARGUMENT - {"NULL_ARGUMENT", ERR_LIB_CMP, CMP_R_NULL_ARGUMENT}, - #else - {"NULL_ARGUMENT", 58, 103}, - #endif - #ifdef CMP_R_PKIBODY_ERROR - {"PKIBODY_ERROR", ERR_LIB_CMP, CMP_R_PKIBODY_ERROR}, - #else - {"PKIBODY_ERROR", 58, 146}, - #endif - #ifdef CMP_R_PKISTATUSINFO_NOT_FOUND - {"PKISTATUSINFO_NOT_FOUND", ERR_LIB_CMP, CMP_R_PKISTATUSINFO_NOT_FOUND}, - #else - {"PKISTATUSINFO_NOT_FOUND", 58, 132}, - #endif - #ifdef CMP_R_POLLING_FAILED - {"POLLING_FAILED", ERR_LIB_CMP, CMP_R_POLLING_FAILED}, - #else - {"POLLING_FAILED", 58, 172}, - #endif - #ifdef CMP_R_POTENTIALLY_INVALID_CERTIFICATE - {"POTENTIALLY_INVALID_CERTIFICATE", ERR_LIB_CMP, CMP_R_POTENTIALLY_INVALID_CERTIFICATE}, - #else - {"POTENTIALLY_INVALID_CERTIFICATE", 58, 147}, - #endif - #ifdef CMP_R_RECEIVED_ERROR - {"RECEIVED_ERROR", ERR_LIB_CMP, CMP_R_RECEIVED_ERROR}, - #else - {"RECEIVED_ERROR", 58, 180}, - #endif - #ifdef CMP_R_RECIPNONCE_UNMATCHED - {"RECIPNONCE_UNMATCHED", ERR_LIB_CMP, CMP_R_RECIPNONCE_UNMATCHED}, - #else - {"RECIPNONCE_UNMATCHED", 58, 148}, - #endif - #ifdef CMP_R_REQUEST_NOT_ACCEPTED - {"REQUEST_NOT_ACCEPTED", ERR_LIB_CMP, CMP_R_REQUEST_NOT_ACCEPTED}, - #else - {"REQUEST_NOT_ACCEPTED", 58, 149}, - #endif - #ifdef CMP_R_REQUEST_REJECTED_BY_SERVER - {"REQUEST_REJECTED_BY_SERVER", ERR_LIB_CMP, CMP_R_REQUEST_REJECTED_BY_SERVER}, - #else - {"REQUEST_REJECTED_BY_SERVER", 58, 182}, - #endif - #ifdef CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED - {"SENDER_GENERALNAME_TYPE_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED}, - #else - {"SENDER_GENERALNAME_TYPE_NOT_SUPPORTED", 58, 150}, - #endif - #ifdef CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG - {"SRVCERT_DOES_NOT_VALIDATE_MSG", ERR_LIB_CMP, CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG}, - #else - {"SRVCERT_DOES_NOT_VALIDATE_MSG", 58, 151}, - #endif - #ifdef CMP_R_TOTAL_TIMEOUT - {"TOTAL_TIMEOUT", ERR_LIB_CMP, CMP_R_TOTAL_TIMEOUT}, - #else - {"TOTAL_TIMEOUT", 58, 184}, - #endif - #ifdef CMP_R_TRANSACTIONID_UNMATCHED - {"TRANSACTIONID_UNMATCHED", ERR_LIB_CMP, CMP_R_TRANSACTIONID_UNMATCHED}, - #else - {"TRANSACTIONID_UNMATCHED", 58, 152}, - #endif - #ifdef CMP_R_TRANSFER_ERROR - {"TRANSFER_ERROR", ERR_LIB_CMP, CMP_R_TRANSFER_ERROR}, - #else - {"TRANSFER_ERROR", 58, 159}, - #endif - #ifdef CMP_R_UNCLEAN_CTX - {"UNCLEAN_CTX", ERR_LIB_CMP, CMP_R_UNCLEAN_CTX}, - #else - {"UNCLEAN_CTX", 58, 191}, - #endif - #ifdef CMP_R_UNEXPECTED_CERTPROFILE - {"UNEXPECTED_CERTPROFILE", ERR_LIB_CMP, CMP_R_UNEXPECTED_CERTPROFILE}, - #else - {"UNEXPECTED_CERTPROFILE", 58, 196}, - #endif - #ifdef CMP_R_UNEXPECTED_CRLSTATUSLIST - {"UNEXPECTED_CRLSTATUSLIST", ERR_LIB_CMP, CMP_R_UNEXPECTED_CRLSTATUSLIST}, - #else - {"UNEXPECTED_CRLSTATUSLIST", 58, 201}, - #endif - #ifdef CMP_R_UNEXPECTED_PKIBODY - {"UNEXPECTED_PKIBODY", ERR_LIB_CMP, CMP_R_UNEXPECTED_PKIBODY}, - #else - {"UNEXPECTED_PKIBODY", 58, 133}, - #endif - #ifdef CMP_R_UNEXPECTED_PKISTATUS - {"UNEXPECTED_PKISTATUS", ERR_LIB_CMP, CMP_R_UNEXPECTED_PKISTATUS}, - #else - {"UNEXPECTED_PKISTATUS", 58, 185}, - #endif - #ifdef CMP_R_UNEXPECTED_POLLREQ - {"UNEXPECTED_POLLREQ", ERR_LIB_CMP, CMP_R_UNEXPECTED_POLLREQ}, - #else - {"UNEXPECTED_POLLREQ", 58, 105}, - #endif - #ifdef CMP_R_UNEXPECTED_PVNO - {"UNEXPECTED_PVNO", ERR_LIB_CMP, CMP_R_UNEXPECTED_PVNO}, - #else - {"UNEXPECTED_PVNO", 58, 153}, - #endif - #ifdef CMP_R_UNEXPECTED_SENDER - {"UNEXPECTED_SENDER", ERR_LIB_CMP, CMP_R_UNEXPECTED_SENDER}, - #else - {"UNEXPECTED_SENDER", 58, 106}, - #endif - #ifdef CMP_R_UNKNOWN_ALGORITHM_ID - {"UNKNOWN_ALGORITHM_ID", ERR_LIB_CMP, CMP_R_UNKNOWN_ALGORITHM_ID}, - #else - {"UNKNOWN_ALGORITHM_ID", 58, 134}, - #endif - #ifdef CMP_R_UNKNOWN_CERT_TYPE - {"UNKNOWN_CERT_TYPE", ERR_LIB_CMP, CMP_R_UNKNOWN_CERT_TYPE}, - #else - {"UNKNOWN_CERT_TYPE", 58, 135}, - #endif - #ifdef CMP_R_UNKNOWN_CRL_ISSUER - {"UNKNOWN_CRL_ISSUER", ERR_LIB_CMP, CMP_R_UNKNOWN_CRL_ISSUER}, - #else - {"UNKNOWN_CRL_ISSUER", 58, 200}, - #endif - #ifdef CMP_R_UNKNOWN_PKISTATUS - {"UNKNOWN_PKISTATUS", ERR_LIB_CMP, CMP_R_UNKNOWN_PKISTATUS}, - #else - {"UNKNOWN_PKISTATUS", 58, 186}, - #endif - #ifdef CMP_R_UNSUPPORTED_ALGORITHM - {"UNSUPPORTED_ALGORITHM", ERR_LIB_CMP, CMP_R_UNSUPPORTED_ALGORITHM}, - #else - {"UNSUPPORTED_ALGORITHM", 58, 136}, - #endif - #ifdef CMP_R_UNSUPPORTED_KEY_TYPE - {"UNSUPPORTED_KEY_TYPE", ERR_LIB_CMP, CMP_R_UNSUPPORTED_KEY_TYPE}, - #else - {"UNSUPPORTED_KEY_TYPE", 58, 137}, - #endif - #ifdef CMP_R_UNSUPPORTED_PKIBODY - {"UNSUPPORTED_PKIBODY", ERR_LIB_CMP, CMP_R_UNSUPPORTED_PKIBODY}, - #else - {"UNSUPPORTED_PKIBODY", 58, 101}, - #endif - #ifdef CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC - {"UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC", ERR_LIB_CMP, CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC}, - #else - {"UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC", 58, 154}, - #endif - #ifdef CMP_R_VALUE_TOO_LARGE - {"VALUE_TOO_LARGE", ERR_LIB_CMP, CMP_R_VALUE_TOO_LARGE}, - #else - {"VALUE_TOO_LARGE", 58, 175}, - #endif - #ifdef CMP_R_VALUE_TOO_SMALL - {"VALUE_TOO_SMALL", ERR_LIB_CMP, CMP_R_VALUE_TOO_SMALL}, - #else - {"VALUE_TOO_SMALL", 58, 177}, - #endif - #ifdef CMP_R_WRONG_ALGORITHM_OID - {"WRONG_ALGORITHM_OID", ERR_LIB_CMP, CMP_R_WRONG_ALGORITHM_OID}, - #else - {"WRONG_ALGORITHM_OID", 58, 138}, - #endif - #ifdef CMP_R_WRONG_CERTID - {"WRONG_CERTID", ERR_LIB_CMP, CMP_R_WRONG_CERTID}, - #else - {"WRONG_CERTID", 58, 189}, - #endif - #ifdef CMP_R_WRONG_CERTID_IN_RP - {"WRONG_CERTID_IN_RP", ERR_LIB_CMP, CMP_R_WRONG_CERTID_IN_RP}, - #else - {"WRONG_CERTID_IN_RP", 58, 187}, - #endif - #ifdef CMP_R_WRONG_PBM_VALUE - {"WRONG_PBM_VALUE", ERR_LIB_CMP, CMP_R_WRONG_PBM_VALUE}, - #else - {"WRONG_PBM_VALUE", 58, 155}, - #endif - #ifdef CMP_R_WRONG_RP_COMPONENT_COUNT - {"WRONG_RP_COMPONENT_COUNT", ERR_LIB_CMP, CMP_R_WRONG_RP_COMPONENT_COUNT}, - #else - {"WRONG_RP_COMPONENT_COUNT", 58, 188}, - #endif - #ifdef CMP_R_WRONG_SERIAL_IN_RP - {"WRONG_SERIAL_IN_RP", ERR_LIB_CMP, CMP_R_WRONG_SERIAL_IN_RP}, - #else - {"WRONG_SERIAL_IN_RP", 58, 173}, - #endif - #ifdef CMS_R_ADD_SIGNER_ERROR - {"ADD_SIGNER_ERROR", ERR_LIB_CMS, CMS_R_ADD_SIGNER_ERROR}, - #else - {"ADD_SIGNER_ERROR", 46, 99}, - #endif - #ifdef CMS_R_ATTRIBUTE_ERROR - {"ATTRIBUTE_ERROR", ERR_LIB_CMS, CMS_R_ATTRIBUTE_ERROR}, - #else - {"ATTRIBUTE_ERROR", 46, 161}, - #endif - #ifdef CMS_R_CERTIFICATE_ALREADY_PRESENT - {"CERTIFICATE_ALREADY_PRESENT", ERR_LIB_CMS, CMS_R_CERTIFICATE_ALREADY_PRESENT}, - #else - {"CERTIFICATE_ALREADY_PRESENT", 46, 175}, - #endif - #ifdef CMS_R_CERTIFICATE_HAS_NO_KEYID - {"CERTIFICATE_HAS_NO_KEYID", ERR_LIB_CMS, CMS_R_CERTIFICATE_HAS_NO_KEYID}, - #else - {"CERTIFICATE_HAS_NO_KEYID", 46, 160}, - #endif - #ifdef CMS_R_CERTIFICATE_VERIFY_ERROR - {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_CMS, CMS_R_CERTIFICATE_VERIFY_ERROR}, - #else - {"CERTIFICATE_VERIFY_ERROR", 46, 100}, - #endif - #ifdef CMS_R_CIPHER_AEAD_SET_TAG_ERROR - {"CIPHER_AEAD_SET_TAG_ERROR", ERR_LIB_CMS, CMS_R_CIPHER_AEAD_SET_TAG_ERROR}, - #else - {"CIPHER_AEAD_SET_TAG_ERROR", 46, 184}, - #endif - #ifdef CMS_R_CIPHER_GET_TAG - {"CIPHER_GET_TAG", ERR_LIB_CMS, CMS_R_CIPHER_GET_TAG}, - #else - {"CIPHER_GET_TAG", 46, 185}, - #endif - #ifdef CMS_R_CIPHER_INITIALISATION_ERROR - {"CIPHER_INITIALISATION_ERROR", ERR_LIB_CMS, CMS_R_CIPHER_INITIALISATION_ERROR}, - #else - {"CIPHER_INITIALISATION_ERROR", 46, 101}, - #endif - #ifdef CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR - {"CIPHER_PARAMETER_INITIALISATION_ERROR", ERR_LIB_CMS, CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR}, - #else - {"CIPHER_PARAMETER_INITIALISATION_ERROR", 46, 102}, - #endif - #ifdef CMS_R_CMS_DATAFINAL_ERROR - {"CMS_DATAFINAL_ERROR", ERR_LIB_CMS, CMS_R_CMS_DATAFINAL_ERROR}, - #else - {"CMS_DATAFINAL_ERROR", 46, 103}, - #endif - #ifdef CMS_R_CMS_LIB - {"CMS_LIB", ERR_LIB_CMS, CMS_R_CMS_LIB}, - #else - {"CMS_LIB", 46, 104}, - #endif - #ifdef CMS_R_CONTENTIDENTIFIER_MISMATCH - {"CONTENTIDENTIFIER_MISMATCH", ERR_LIB_CMS, CMS_R_CONTENTIDENTIFIER_MISMATCH}, - #else - {"CONTENTIDENTIFIER_MISMATCH", 46, 170}, - #endif - #ifdef CMS_R_CONTENT_NOT_FOUND - {"CONTENT_NOT_FOUND", ERR_LIB_CMS, CMS_R_CONTENT_NOT_FOUND}, - #else - {"CONTENT_NOT_FOUND", 46, 105}, - #endif - #ifdef CMS_R_CONTENT_TYPE_MISMATCH - {"CONTENT_TYPE_MISMATCH", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_MISMATCH}, - #else - {"CONTENT_TYPE_MISMATCH", 46, 171}, - #endif - #ifdef CMS_R_CONTENT_TYPE_NOT_COMPRESSED_DATA - {"CONTENT_TYPE_NOT_COMPRESSED_DATA", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_NOT_COMPRESSED_DATA}, - #else - {"CONTENT_TYPE_NOT_COMPRESSED_DATA", 46, 106}, - #endif - #ifdef CMS_R_CONTENT_TYPE_NOT_ENVELOPED_DATA - {"CONTENT_TYPE_NOT_ENVELOPED_DATA", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_NOT_ENVELOPED_DATA}, - #else - {"CONTENT_TYPE_NOT_ENVELOPED_DATA", 46, 107}, - #endif - #ifdef CMS_R_CONTENT_TYPE_NOT_SIGNED_DATA - {"CONTENT_TYPE_NOT_SIGNED_DATA", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_NOT_SIGNED_DATA}, - #else - {"CONTENT_TYPE_NOT_SIGNED_DATA", 46, 108}, - #endif - #ifdef CMS_R_CONTENT_VERIFY_ERROR - {"CONTENT_VERIFY_ERROR", ERR_LIB_CMS, CMS_R_CONTENT_VERIFY_ERROR}, - #else - {"CONTENT_VERIFY_ERROR", 46, 109}, - #endif - #ifdef CMS_R_CTRL_ERROR - {"CTRL_ERROR", ERR_LIB_CMS, CMS_R_CTRL_ERROR}, - #else - {"CTRL_ERROR", 46, 110}, - #endif - #ifdef CMS_R_CTRL_FAILURE - {"CTRL_FAILURE", ERR_LIB_CMS, CMS_R_CTRL_FAILURE}, - #else - {"CTRL_FAILURE", 46, 111}, - #endif - #ifdef CMS_R_DECODE_ERROR - {"DECODE_ERROR", ERR_LIB_CMS, CMS_R_DECODE_ERROR}, - #else - {"DECODE_ERROR", 46, 187}, - #endif - #ifdef CMS_R_DECRYPT_ERROR - {"DECRYPT_ERROR", ERR_LIB_CMS, CMS_R_DECRYPT_ERROR}, - #else - {"DECRYPT_ERROR", 46, 112}, - #endif - #ifdef CMS_R_ERROR_GETTING_PUBLIC_KEY - {"ERROR_GETTING_PUBLIC_KEY", ERR_LIB_CMS, CMS_R_ERROR_GETTING_PUBLIC_KEY}, - #else - {"ERROR_GETTING_PUBLIC_KEY", 46, 113}, - #endif - #ifdef CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE - {"ERROR_READING_MESSAGEDIGEST_ATTRIBUTE", ERR_LIB_CMS, CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE}, - #else - {"ERROR_READING_MESSAGEDIGEST_ATTRIBUTE", 46, 114}, - #endif - #ifdef CMS_R_ERROR_SETTING_KEY - {"ERROR_SETTING_KEY", ERR_LIB_CMS, CMS_R_ERROR_SETTING_KEY}, - #else - {"ERROR_SETTING_KEY", 46, 115}, - #endif - #ifdef CMS_R_ERROR_SETTING_RECIPIENTINFO - {"ERROR_SETTING_RECIPIENTINFO", ERR_LIB_CMS, CMS_R_ERROR_SETTING_RECIPIENTINFO}, - #else - {"ERROR_SETTING_RECIPIENTINFO", 46, 116}, - #endif - #ifdef CMS_R_ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT - {"ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT", ERR_LIB_CMS, CMS_R_ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT}, - #else - {"ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT", 46, 196}, - #endif - #ifdef CMS_R_ESS_SIGNING_CERTID_MISMATCH_ERROR - {"ESS_SIGNING_CERTID_MISMATCH_ERROR", ERR_LIB_CMS, CMS_R_ESS_SIGNING_CERTID_MISMATCH_ERROR}, - #else - {"ESS_SIGNING_CERTID_MISMATCH_ERROR", 46, 183}, - #endif - #ifdef CMS_R_INVALID_ENCRYPTED_KEY_LENGTH - {"INVALID_ENCRYPTED_KEY_LENGTH", ERR_LIB_CMS, CMS_R_INVALID_ENCRYPTED_KEY_LENGTH}, - #else - {"INVALID_ENCRYPTED_KEY_LENGTH", 46, 117}, - #endif - #ifdef CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER - {"INVALID_KEY_ENCRYPTION_PARAMETER", ERR_LIB_CMS, CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER}, - #else - {"INVALID_KEY_ENCRYPTION_PARAMETER", 46, 176}, - #endif - #ifdef CMS_R_INVALID_KEY_LENGTH - {"INVALID_KEY_LENGTH", ERR_LIB_CMS, CMS_R_INVALID_KEY_LENGTH}, - #else - {"INVALID_KEY_LENGTH", 46, 118}, - #endif - #ifdef CMS_R_INVALID_LABEL - {"INVALID_LABEL", ERR_LIB_CMS, CMS_R_INVALID_LABEL}, - #else - {"INVALID_LABEL", 46, 190}, - #endif - #ifdef CMS_R_INVALID_OAEP_PARAMETERS - {"INVALID_OAEP_PARAMETERS", ERR_LIB_CMS, CMS_R_INVALID_OAEP_PARAMETERS}, - #else - {"INVALID_OAEP_PARAMETERS", 46, 191}, - #endif - #ifdef CMS_R_KDF_PARAMETER_ERROR - {"KDF_PARAMETER_ERROR", ERR_LIB_CMS, CMS_R_KDF_PARAMETER_ERROR}, - #else - {"KDF_PARAMETER_ERROR", 46, 186}, - #endif - #ifdef CMS_R_MD_BIO_INIT_ERROR - {"MD_BIO_INIT_ERROR", ERR_LIB_CMS, CMS_R_MD_BIO_INIT_ERROR}, - #else - {"MD_BIO_INIT_ERROR", 46, 119}, - #endif - #ifdef CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH - {"MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH", ERR_LIB_CMS, CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH}, - #else - {"MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH", 46, 120}, - #endif - #ifdef CMS_R_MESSAGEDIGEST_WRONG_LENGTH - {"MESSAGEDIGEST_WRONG_LENGTH", ERR_LIB_CMS, CMS_R_MESSAGEDIGEST_WRONG_LENGTH}, - #else - {"MESSAGEDIGEST_WRONG_LENGTH", 46, 121}, - #endif - #ifdef CMS_R_MSGSIGDIGEST_ERROR - {"MSGSIGDIGEST_ERROR", ERR_LIB_CMS, CMS_R_MSGSIGDIGEST_ERROR}, - #else - {"MSGSIGDIGEST_ERROR", 46, 172}, - #endif - #ifdef CMS_R_MSGSIGDIGEST_VERIFICATION_FAILURE - {"MSGSIGDIGEST_VERIFICATION_FAILURE", ERR_LIB_CMS, CMS_R_MSGSIGDIGEST_VERIFICATION_FAILURE}, - #else - {"MSGSIGDIGEST_VERIFICATION_FAILURE", 46, 162}, - #endif - #ifdef CMS_R_MSGSIGDIGEST_WRONG_LENGTH - {"MSGSIGDIGEST_WRONG_LENGTH", ERR_LIB_CMS, CMS_R_MSGSIGDIGEST_WRONG_LENGTH}, - #else - {"MSGSIGDIGEST_WRONG_LENGTH", 46, 163}, - #endif - #ifdef CMS_R_NEED_ONE_SIGNER - {"NEED_ONE_SIGNER", ERR_LIB_CMS, CMS_R_NEED_ONE_SIGNER}, - #else - {"NEED_ONE_SIGNER", 46, 164}, - #endif - #ifdef CMS_R_NOT_A_SIGNED_RECEIPT - {"NOT_A_SIGNED_RECEIPT", ERR_LIB_CMS, CMS_R_NOT_A_SIGNED_RECEIPT}, - #else - {"NOT_A_SIGNED_RECEIPT", 46, 165}, - #endif - #ifdef CMS_R_NOT_ENCRYPTED_DATA - {"NOT_ENCRYPTED_DATA", ERR_LIB_CMS, CMS_R_NOT_ENCRYPTED_DATA}, - #else - {"NOT_ENCRYPTED_DATA", 46, 122}, - #endif - #ifdef CMS_R_NOT_KEK - {"NOT_KEK", ERR_LIB_CMS, CMS_R_NOT_KEK}, - #else - {"NOT_KEK", 46, 123}, - #endif - #ifdef CMS_R_NOT_KEY_AGREEMENT - {"NOT_KEY_AGREEMENT", ERR_LIB_CMS, CMS_R_NOT_KEY_AGREEMENT}, - #else - {"NOT_KEY_AGREEMENT", 46, 181}, - #endif - #ifdef CMS_R_NOT_KEY_TRANSPORT - {"NOT_KEY_TRANSPORT", ERR_LIB_CMS, CMS_R_NOT_KEY_TRANSPORT}, - #else - {"NOT_KEY_TRANSPORT", 46, 124}, - #endif - #ifdef CMS_R_NOT_PWRI - {"NOT_PWRI", ERR_LIB_CMS, CMS_R_NOT_PWRI}, - #else - {"NOT_PWRI", 46, 177}, - #endif - #ifdef CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE - {"NOT_SUPPORTED_FOR_THIS_KEY_TYPE", ERR_LIB_CMS, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE}, - #else - {"NOT_SUPPORTED_FOR_THIS_KEY_TYPE", 46, 125}, - #endif - #ifdef CMS_R_NO_CIPHER - {"NO_CIPHER", ERR_LIB_CMS, CMS_R_NO_CIPHER}, - #else - {"NO_CIPHER", 46, 126}, - #endif - #ifdef CMS_R_NO_CONTENT - {"NO_CONTENT", ERR_LIB_CMS, CMS_R_NO_CONTENT}, - #else - {"NO_CONTENT", 46, 127}, - #endif - #ifdef CMS_R_NO_CONTENT_TYPE - {"NO_CONTENT_TYPE", ERR_LIB_CMS, CMS_R_NO_CONTENT_TYPE}, - #else - {"NO_CONTENT_TYPE", 46, 173}, - #endif - #ifdef CMS_R_NO_DEFAULT_DIGEST - {"NO_DEFAULT_DIGEST", ERR_LIB_CMS, CMS_R_NO_DEFAULT_DIGEST}, - #else - {"NO_DEFAULT_DIGEST", 46, 128}, - #endif - #ifdef CMS_R_NO_DIGEST_SET - {"NO_DIGEST_SET", ERR_LIB_CMS, CMS_R_NO_DIGEST_SET}, - #else - {"NO_DIGEST_SET", 46, 129}, - #endif - #ifdef CMS_R_NO_KEY - {"NO_KEY", ERR_LIB_CMS, CMS_R_NO_KEY}, - #else - {"NO_KEY", 46, 130}, - #endif - #ifdef CMS_R_NO_KEY_OR_CERT - {"NO_KEY_OR_CERT", ERR_LIB_CMS, CMS_R_NO_KEY_OR_CERT}, - #else - {"NO_KEY_OR_CERT", 46, 174}, - #endif - #ifdef CMS_R_NO_MATCHING_DIGEST - {"NO_MATCHING_DIGEST", ERR_LIB_CMS, CMS_R_NO_MATCHING_DIGEST}, - #else - {"NO_MATCHING_DIGEST", 46, 131}, - #endif - #ifdef CMS_R_NO_MATCHING_RECIPIENT - {"NO_MATCHING_RECIPIENT", ERR_LIB_CMS, CMS_R_NO_MATCHING_RECIPIENT}, - #else - {"NO_MATCHING_RECIPIENT", 46, 132}, - #endif - #ifdef CMS_R_NO_MATCHING_SIGNATURE - {"NO_MATCHING_SIGNATURE", ERR_LIB_CMS, CMS_R_NO_MATCHING_SIGNATURE}, - #else - {"NO_MATCHING_SIGNATURE", 46, 166}, - #endif - #ifdef CMS_R_NO_MSGSIGDIGEST - {"NO_MSGSIGDIGEST", ERR_LIB_CMS, CMS_R_NO_MSGSIGDIGEST}, - #else - {"NO_MSGSIGDIGEST", 46, 167}, - #endif - #ifdef CMS_R_NO_PASSWORD - {"NO_PASSWORD", ERR_LIB_CMS, CMS_R_NO_PASSWORD}, - #else - {"NO_PASSWORD", 46, 178}, - #endif - #ifdef CMS_R_NO_PRIVATE_KEY - {"NO_PRIVATE_KEY", ERR_LIB_CMS, CMS_R_NO_PRIVATE_KEY}, - #else - {"NO_PRIVATE_KEY", 46, 133}, - #endif - #ifdef CMS_R_NO_PUBLIC_KEY - {"NO_PUBLIC_KEY", ERR_LIB_CMS, CMS_R_NO_PUBLIC_KEY}, - #else - {"NO_PUBLIC_KEY", 46, 134}, - #endif - #ifdef CMS_R_NO_RECEIPT_REQUEST - {"NO_RECEIPT_REQUEST", ERR_LIB_CMS, CMS_R_NO_RECEIPT_REQUEST}, - #else - {"NO_RECEIPT_REQUEST", 46, 168}, - #endif - #ifdef CMS_R_NO_SIGNERS - {"NO_SIGNERS", ERR_LIB_CMS, CMS_R_NO_SIGNERS}, - #else - {"NO_SIGNERS", 46, 135}, - #endif - #ifdef CMS_R_OPERATION_UNSUPPORTED - {"OPERATION_UNSUPPORTED", ERR_LIB_CMS, CMS_R_OPERATION_UNSUPPORTED}, - #else - {"OPERATION_UNSUPPORTED", 46, 182}, - #endif - #ifdef CMS_R_PEER_KEY_ERROR - {"PEER_KEY_ERROR", ERR_LIB_CMS, CMS_R_PEER_KEY_ERROR}, - #else - {"PEER_KEY_ERROR", 46, 188}, - #endif - #ifdef CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE - {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_CMS, CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, - #else - {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 46, 136}, - #endif - #ifdef CMS_R_RECEIPT_DECODE_ERROR - {"RECEIPT_DECODE_ERROR", ERR_LIB_CMS, CMS_R_RECEIPT_DECODE_ERROR}, - #else - {"RECEIPT_DECODE_ERROR", 46, 169}, - #endif - #ifdef CMS_R_RECIPIENT_ERROR - {"RECIPIENT_ERROR", ERR_LIB_CMS, CMS_R_RECIPIENT_ERROR}, - #else - {"RECIPIENT_ERROR", 46, 137}, - #endif - #ifdef CMS_R_SHARED_INFO_ERROR - {"SHARED_INFO_ERROR", ERR_LIB_CMS, CMS_R_SHARED_INFO_ERROR}, - #else - {"SHARED_INFO_ERROR", 46, 189}, - #endif - #ifdef CMS_R_SIGNER_CERTIFICATE_NOT_FOUND - {"SIGNER_CERTIFICATE_NOT_FOUND", ERR_LIB_CMS, CMS_R_SIGNER_CERTIFICATE_NOT_FOUND}, - #else - {"SIGNER_CERTIFICATE_NOT_FOUND", 46, 138}, - #endif - #ifdef CMS_R_SIGNFINAL_ERROR - {"SIGNFINAL_ERROR", ERR_LIB_CMS, CMS_R_SIGNFINAL_ERROR}, - #else - {"SIGNFINAL_ERROR", 46, 139}, - #endif - #ifdef CMS_R_SMIME_TEXT_ERROR - {"SMIME_TEXT_ERROR", ERR_LIB_CMS, CMS_R_SMIME_TEXT_ERROR}, - #else - {"SMIME_TEXT_ERROR", 46, 140}, - #endif - #ifdef CMS_R_STORE_INIT_ERROR - {"STORE_INIT_ERROR", ERR_LIB_CMS, CMS_R_STORE_INIT_ERROR}, - #else - {"STORE_INIT_ERROR", 46, 141}, - #endif - #ifdef CMS_R_TYPE_NOT_COMPRESSED_DATA - {"TYPE_NOT_COMPRESSED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_COMPRESSED_DATA}, - #else - {"TYPE_NOT_COMPRESSED_DATA", 46, 142}, - #endif - #ifdef CMS_R_TYPE_NOT_DATA - {"TYPE_NOT_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_DATA}, - #else - {"TYPE_NOT_DATA", 46, 143}, - #endif - #ifdef CMS_R_TYPE_NOT_DIGESTED_DATA - {"TYPE_NOT_DIGESTED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_DIGESTED_DATA}, - #else - {"TYPE_NOT_DIGESTED_DATA", 46, 144}, - #endif - #ifdef CMS_R_TYPE_NOT_ENCRYPTED_DATA - {"TYPE_NOT_ENCRYPTED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_ENCRYPTED_DATA}, - #else - {"TYPE_NOT_ENCRYPTED_DATA", 46, 145}, - #endif - #ifdef CMS_R_TYPE_NOT_ENVELOPED_DATA - {"TYPE_NOT_ENVELOPED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_ENVELOPED_DATA}, - #else - {"TYPE_NOT_ENVELOPED_DATA", 46, 146}, - #endif - #ifdef CMS_R_UNABLE_TO_FINALIZE_CONTEXT - {"UNABLE_TO_FINALIZE_CONTEXT", ERR_LIB_CMS, CMS_R_UNABLE_TO_FINALIZE_CONTEXT}, - #else - {"UNABLE_TO_FINALIZE_CONTEXT", 46, 147}, - #endif - #ifdef CMS_R_UNKNOWN_CIPHER - {"UNKNOWN_CIPHER", ERR_LIB_CMS, CMS_R_UNKNOWN_CIPHER}, - #else - {"UNKNOWN_CIPHER", 46, 148}, - #endif - #ifdef CMS_R_UNKNOWN_DIGEST_ALGORITHM - {"UNKNOWN_DIGEST_ALGORITHM", ERR_LIB_CMS, CMS_R_UNKNOWN_DIGEST_ALGORITHM}, - #else - {"UNKNOWN_DIGEST_ALGORITHM", 46, 149}, - #endif - #ifdef CMS_R_UNKNOWN_ID - {"UNKNOWN_ID", ERR_LIB_CMS, CMS_R_UNKNOWN_ID}, - #else - {"UNKNOWN_ID", 46, 150}, - #endif - #ifdef CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM - {"UNSUPPORTED_COMPRESSION_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM}, - #else - {"UNSUPPORTED_COMPRESSION_ALGORITHM", 46, 151}, - #endif - #ifdef CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM - {"UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM}, - #else - {"UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM", 46, 194}, - #endif - #ifdef CMS_R_UNSUPPORTED_CONTENT_TYPE - {"UNSUPPORTED_CONTENT_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_CONTENT_TYPE}, - #else - {"UNSUPPORTED_CONTENT_TYPE", 46, 152}, - #endif - #ifdef CMS_R_UNSUPPORTED_ENCRYPTION_TYPE - {"UNSUPPORTED_ENCRYPTION_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_ENCRYPTION_TYPE}, - #else - {"UNSUPPORTED_ENCRYPTION_TYPE", 46, 192}, - #endif - #ifdef CMS_R_UNSUPPORTED_KEK_ALGORITHM - {"UNSUPPORTED_KEK_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_KEK_ALGORITHM}, - #else - {"UNSUPPORTED_KEK_ALGORITHM", 46, 153}, - #endif - #ifdef CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM - {"UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM}, - #else - {"UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM", 46, 179}, - #endif - #ifdef CMS_R_UNSUPPORTED_LABEL_SOURCE - {"UNSUPPORTED_LABEL_SOURCE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_LABEL_SOURCE}, - #else - {"UNSUPPORTED_LABEL_SOURCE", 46, 193}, - #endif - #ifdef CMS_R_UNSUPPORTED_RECIPIENTINFO_TYPE - {"UNSUPPORTED_RECIPIENTINFO_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_RECIPIENTINFO_TYPE}, - #else - {"UNSUPPORTED_RECIPIENTINFO_TYPE", 46, 155}, - #endif - #ifdef CMS_R_UNSUPPORTED_RECIPIENT_TYPE - {"UNSUPPORTED_RECIPIENT_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_RECIPIENT_TYPE}, - #else - {"UNSUPPORTED_RECIPIENT_TYPE", 46, 154}, - #endif - #ifdef CMS_R_UNSUPPORTED_SIGNATURE_ALGORITHM - {"UNSUPPORTED_SIGNATURE_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_SIGNATURE_ALGORITHM}, - #else - {"UNSUPPORTED_SIGNATURE_ALGORITHM", 46, 195}, - #endif - #ifdef CMS_R_UNSUPPORTED_TYPE - {"UNSUPPORTED_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_TYPE}, - #else - {"UNSUPPORTED_TYPE", 46, 156}, - #endif - #ifdef CMS_R_UNWRAP_ERROR - {"UNWRAP_ERROR", ERR_LIB_CMS, CMS_R_UNWRAP_ERROR}, - #else - {"UNWRAP_ERROR", 46, 157}, - #endif - #ifdef CMS_R_UNWRAP_FAILURE - {"UNWRAP_FAILURE", ERR_LIB_CMS, CMS_R_UNWRAP_FAILURE}, - #else - {"UNWRAP_FAILURE", 46, 180}, - #endif - #ifdef CMS_R_VERIFICATION_FAILURE - {"VERIFICATION_FAILURE", ERR_LIB_CMS, CMS_R_VERIFICATION_FAILURE}, - #else - {"VERIFICATION_FAILURE", 46, 158}, - #endif - #ifdef CMS_R_WRAP_ERROR - {"WRAP_ERROR", ERR_LIB_CMS, CMS_R_WRAP_ERROR}, - #else - {"WRAP_ERROR", 46, 159}, - #endif - #ifdef COMP_R_BROTLI_DECODE_ERROR - {"BROTLI_DECODE_ERROR", ERR_LIB_COMP, COMP_R_BROTLI_DECODE_ERROR}, - #else - {"BROTLI_DECODE_ERROR", 41, 102}, - #endif - #ifdef COMP_R_BROTLI_ENCODE_ERROR - {"BROTLI_ENCODE_ERROR", ERR_LIB_COMP, COMP_R_BROTLI_ENCODE_ERROR}, - #else - {"BROTLI_ENCODE_ERROR", 41, 103}, - #endif - #ifdef COMP_R_BROTLI_NOT_SUPPORTED - {"BROTLI_NOT_SUPPORTED", ERR_LIB_COMP, COMP_R_BROTLI_NOT_SUPPORTED}, - #else - {"BROTLI_NOT_SUPPORTED", 41, 104}, - #endif - #ifdef COMP_R_ZLIB_DEFLATE_ERROR - {"ZLIB_DEFLATE_ERROR", ERR_LIB_COMP, COMP_R_ZLIB_DEFLATE_ERROR}, - #else - {"ZLIB_DEFLATE_ERROR", 41, 99}, - #endif - #ifdef COMP_R_ZLIB_INFLATE_ERROR - {"ZLIB_INFLATE_ERROR", ERR_LIB_COMP, COMP_R_ZLIB_INFLATE_ERROR}, - #else - {"ZLIB_INFLATE_ERROR", 41, 100}, - #endif - #ifdef COMP_R_ZLIB_NOT_SUPPORTED - {"ZLIB_NOT_SUPPORTED", ERR_LIB_COMP, COMP_R_ZLIB_NOT_SUPPORTED}, - #else - {"ZLIB_NOT_SUPPORTED", 41, 101}, - #endif - #ifdef COMP_R_ZSTD_COMPRESS_ERROR - {"ZSTD_COMPRESS_ERROR", ERR_LIB_COMP, COMP_R_ZSTD_COMPRESS_ERROR}, - #else - {"ZSTD_COMPRESS_ERROR", 41, 105}, - #endif - #ifdef COMP_R_ZSTD_DECODE_ERROR - {"ZSTD_DECODE_ERROR", ERR_LIB_COMP, COMP_R_ZSTD_DECODE_ERROR}, - #else - {"ZSTD_DECODE_ERROR", 41, 106}, - #endif - #ifdef COMP_R_ZSTD_DECOMPRESS_ERROR - {"ZSTD_DECOMPRESS_ERROR", ERR_LIB_COMP, COMP_R_ZSTD_DECOMPRESS_ERROR}, - #else - {"ZSTD_DECOMPRESS_ERROR", 41, 107}, - #endif - #ifdef COMP_R_ZSTD_NOT_SUPPORTED - {"ZSTD_NOT_SUPPORTED", ERR_LIB_COMP, COMP_R_ZSTD_NOT_SUPPORTED}, - #else - {"ZSTD_NOT_SUPPORTED", 41, 108}, - #endif - #ifdef CONF_R_ERROR_LOADING_DSO - {"ERROR_LOADING_DSO", ERR_LIB_CONF, CONF_R_ERROR_LOADING_DSO}, - #else - {"ERROR_LOADING_DSO", 14, 110}, - #endif - #ifdef CONF_R_INVALID_PRAGMA - {"INVALID_PRAGMA", ERR_LIB_CONF, CONF_R_INVALID_PRAGMA}, - #else - {"INVALID_PRAGMA", 14, 122}, - #endif - #ifdef CONF_R_LIST_CANNOT_BE_NULL - {"LIST_CANNOT_BE_NULL", ERR_LIB_CONF, CONF_R_LIST_CANNOT_BE_NULL}, - #else - {"LIST_CANNOT_BE_NULL", 14, 115}, - #endif - #ifdef CONF_R_MANDATORY_BRACES_IN_VARIABLE_EXPANSION - {"MANDATORY_BRACES_IN_VARIABLE_EXPANSION", ERR_LIB_CONF, CONF_R_MANDATORY_BRACES_IN_VARIABLE_EXPANSION}, - #else - {"MANDATORY_BRACES_IN_VARIABLE_EXPANSION", 14, 123}, - #endif - #ifdef CONF_R_MISSING_CLOSE_SQUARE_BRACKET - {"MISSING_CLOSE_SQUARE_BRACKET", ERR_LIB_CONF, CONF_R_MISSING_CLOSE_SQUARE_BRACKET}, - #else - {"MISSING_CLOSE_SQUARE_BRACKET", 14, 100}, - #endif - #ifdef CONF_R_MISSING_EQUAL_SIGN - {"MISSING_EQUAL_SIGN", ERR_LIB_CONF, CONF_R_MISSING_EQUAL_SIGN}, - #else - {"MISSING_EQUAL_SIGN", 14, 101}, - #endif - #ifdef CONF_R_MISSING_INIT_FUNCTION - {"MISSING_INIT_FUNCTION", ERR_LIB_CONF, CONF_R_MISSING_INIT_FUNCTION}, - #else - {"MISSING_INIT_FUNCTION", 14, 112}, - #endif - #ifdef CONF_R_MODULE_INITIALIZATION_ERROR - {"MODULE_INITIALIZATION_ERROR", ERR_LIB_CONF, CONF_R_MODULE_INITIALIZATION_ERROR}, - #else - {"MODULE_INITIALIZATION_ERROR", 14, 109}, - #endif - #ifdef CONF_R_NO_CLOSE_BRACE - {"NO_CLOSE_BRACE", ERR_LIB_CONF, CONF_R_NO_CLOSE_BRACE}, - #else - {"NO_CLOSE_BRACE", 14, 102}, - #endif - #ifdef CONF_R_NO_CONF - {"NO_CONF", ERR_LIB_CONF, CONF_R_NO_CONF}, - #else - {"NO_CONF", 14, 105}, - #endif - #ifdef CONF_R_NO_CONF_OR_ENVIRONMENT_VARIABLE - {"NO_CONF_OR_ENVIRONMENT_VARIABLE", ERR_LIB_CONF, CONF_R_NO_CONF_OR_ENVIRONMENT_VARIABLE}, - #else - {"NO_CONF_OR_ENVIRONMENT_VARIABLE", 14, 106}, - #endif - #ifdef CONF_R_NO_SECTION - {"NO_SECTION", ERR_LIB_CONF, CONF_R_NO_SECTION}, - #else - {"NO_SECTION", 14, 107}, - #endif - #ifdef CONF_R_NO_SUCH_FILE - {"NO_SUCH_FILE", ERR_LIB_CONF, CONF_R_NO_SUCH_FILE}, - #else - {"NO_SUCH_FILE", 14, 114}, - #endif - #ifdef CONF_R_NO_VALUE - {"NO_VALUE", ERR_LIB_CONF, CONF_R_NO_VALUE}, - #else - {"NO_VALUE", 14, 108}, - #endif - #ifdef CONF_R_NUMBER_TOO_LARGE - {"NUMBER_TOO_LARGE", ERR_LIB_CONF, CONF_R_NUMBER_TOO_LARGE}, - #else - {"NUMBER_TOO_LARGE", 14, 121}, - #endif - #ifdef CONF_R_OPENSSL_CONF_REFERENCES_MISSING_SECTION - {"OPENSSL_CONF_REFERENCES_MISSING_SECTION", ERR_LIB_CONF, CONF_R_OPENSSL_CONF_REFERENCES_MISSING_SECTION}, - #else - {"OPENSSL_CONF_REFERENCES_MISSING_SECTION", 14, 124}, - #endif - #ifdef CONF_R_RECURSIVE_DIRECTORY_INCLUDE - {"RECURSIVE_DIRECTORY_INCLUDE", ERR_LIB_CONF, CONF_R_RECURSIVE_DIRECTORY_INCLUDE}, - #else - {"RECURSIVE_DIRECTORY_INCLUDE", 14, 111}, - #endif - #ifdef CONF_R_RECURSIVE_SECTION_REFERENCE - {"RECURSIVE_SECTION_REFERENCE", ERR_LIB_CONF, CONF_R_RECURSIVE_SECTION_REFERENCE}, - #else - {"RECURSIVE_SECTION_REFERENCE", 14, 126}, - #endif - #ifdef CONF_R_RELATIVE_PATH - {"RELATIVE_PATH", ERR_LIB_CONF, CONF_R_RELATIVE_PATH}, - #else - {"RELATIVE_PATH", 14, 125}, - #endif - #ifdef CONF_R_SSL_COMMAND_SECTION_EMPTY - {"SSL_COMMAND_SECTION_EMPTY", ERR_LIB_CONF, CONF_R_SSL_COMMAND_SECTION_EMPTY}, - #else - {"SSL_COMMAND_SECTION_EMPTY", 14, 117}, - #endif - #ifdef CONF_R_SSL_COMMAND_SECTION_NOT_FOUND - {"SSL_COMMAND_SECTION_NOT_FOUND", ERR_LIB_CONF, CONF_R_SSL_COMMAND_SECTION_NOT_FOUND}, - #else - {"SSL_COMMAND_SECTION_NOT_FOUND", 14, 118}, - #endif - #ifdef CONF_R_SSL_SECTION_EMPTY - {"SSL_SECTION_EMPTY", ERR_LIB_CONF, CONF_R_SSL_SECTION_EMPTY}, - #else - {"SSL_SECTION_EMPTY", 14, 119}, - #endif - #ifdef CONF_R_SSL_SECTION_NOT_FOUND - {"SSL_SECTION_NOT_FOUND", ERR_LIB_CONF, CONF_R_SSL_SECTION_NOT_FOUND}, - #else - {"SSL_SECTION_NOT_FOUND", 14, 120}, - #endif - #ifdef CONF_R_UNABLE_TO_CREATE_NEW_SECTION - {"UNABLE_TO_CREATE_NEW_SECTION", ERR_LIB_CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION}, - #else - {"UNABLE_TO_CREATE_NEW_SECTION", 14, 103}, - #endif - #ifdef CONF_R_UNKNOWN_MODULE_NAME - {"UNKNOWN_MODULE_NAME", ERR_LIB_CONF, CONF_R_UNKNOWN_MODULE_NAME}, - #else - {"UNKNOWN_MODULE_NAME", 14, 113}, - #endif - #ifdef CONF_R_VARIABLE_EXPANSION_TOO_LONG - {"VARIABLE_EXPANSION_TOO_LONG", ERR_LIB_CONF, CONF_R_VARIABLE_EXPANSION_TOO_LONG}, - #else - {"VARIABLE_EXPANSION_TOO_LONG", 14, 116}, - #endif - #ifdef CONF_R_VARIABLE_HAS_NO_VALUE - {"VARIABLE_HAS_NO_VALUE", ERR_LIB_CONF, CONF_R_VARIABLE_HAS_NO_VALUE}, - #else - {"VARIABLE_HAS_NO_VALUE", 14, 104}, - #endif - #ifdef CRMF_R_BAD_PBM_ITERATIONCOUNT - {"BAD_PBM_ITERATIONCOUNT", ERR_LIB_CRMF, CRMF_R_BAD_PBM_ITERATIONCOUNT}, - #else - {"BAD_PBM_ITERATIONCOUNT", 56, 100}, - #endif - #ifdef CRMF_R_CRMFERROR - {"CRMFERROR", ERR_LIB_CRMF, CRMF_R_CRMFERROR}, - #else - {"CRMFERROR", 56, 102}, - #endif - #ifdef CRMF_R_ERROR - {"ERROR", ERR_LIB_CRMF, CRMF_R_ERROR}, - #else - {"ERROR", 56, 103}, - #endif - #ifdef CRMF_R_ERROR_DECODING_CERTIFICATE - {"ERROR_DECODING_CERTIFICATE", ERR_LIB_CRMF, CRMF_R_ERROR_DECODING_CERTIFICATE}, - #else - {"ERROR_DECODING_CERTIFICATE", 56, 104}, - #endif - #ifdef CRMF_R_ERROR_DECRYPTING_CERTIFICATE - {"ERROR_DECRYPTING_CERTIFICATE", ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_CERTIFICATE}, - #else - {"ERROR_DECRYPTING_CERTIFICATE", 56, 105}, - #endif - #ifdef CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY - {"ERROR_DECRYPTING_SYMMETRIC_KEY", ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY}, - #else - {"ERROR_DECRYPTING_SYMMETRIC_KEY", 56, 106}, - #endif - #ifdef CRMF_R_FAILURE_OBTAINING_RANDOM - {"FAILURE_OBTAINING_RANDOM", ERR_LIB_CRMF, CRMF_R_FAILURE_OBTAINING_RANDOM}, - #else - {"FAILURE_OBTAINING_RANDOM", 56, 107}, - #endif - #ifdef CRMF_R_ITERATIONCOUNT_BELOW_100 - {"ITERATIONCOUNT_BELOW_100", ERR_LIB_CRMF, CRMF_R_ITERATIONCOUNT_BELOW_100}, - #else - {"ITERATIONCOUNT_BELOW_100", 56, 108}, - #endif - #ifdef CRMF_R_MALFORMED_IV - {"MALFORMED_IV", ERR_LIB_CRMF, CRMF_R_MALFORMED_IV}, - #else - {"MALFORMED_IV", 56, 101}, - #endif - #ifdef CRMF_R_NULL_ARGUMENT - {"NULL_ARGUMENT", ERR_LIB_CRMF, CRMF_R_NULL_ARGUMENT}, - #else - {"NULL_ARGUMENT", 56, 109}, - #endif - #ifdef CRMF_R_POPOSKINPUT_NOT_SUPPORTED - {"POPOSKINPUT_NOT_SUPPORTED", ERR_LIB_CRMF, CRMF_R_POPOSKINPUT_NOT_SUPPORTED}, - #else - {"POPOSKINPUT_NOT_SUPPORTED", 56, 113}, - #endif - #ifdef CRMF_R_POPO_INCONSISTENT_PUBLIC_KEY - {"POPO_INCONSISTENT_PUBLIC_KEY", ERR_LIB_CRMF, CRMF_R_POPO_INCONSISTENT_PUBLIC_KEY}, - #else - {"POPO_INCONSISTENT_PUBLIC_KEY", 56, 117}, - #endif - #ifdef CRMF_R_POPO_MISSING - {"POPO_MISSING", ERR_LIB_CRMF, CRMF_R_POPO_MISSING}, - #else - {"POPO_MISSING", 56, 121}, - #endif - #ifdef CRMF_R_POPO_MISSING_PUBLIC_KEY - {"POPO_MISSING_PUBLIC_KEY", ERR_LIB_CRMF, CRMF_R_POPO_MISSING_PUBLIC_KEY}, - #else - {"POPO_MISSING_PUBLIC_KEY", 56, 118}, - #endif - #ifdef CRMF_R_POPO_MISSING_SUBJECT - {"POPO_MISSING_SUBJECT", ERR_LIB_CRMF, CRMF_R_POPO_MISSING_SUBJECT}, - #else - {"POPO_MISSING_SUBJECT", 56, 119}, - #endif - #ifdef CRMF_R_POPO_RAVERIFIED_NOT_ACCEPTED - {"POPO_RAVERIFIED_NOT_ACCEPTED", ERR_LIB_CRMF, CRMF_R_POPO_RAVERIFIED_NOT_ACCEPTED}, - #else - {"POPO_RAVERIFIED_NOT_ACCEPTED", 56, 120}, - #endif - #ifdef CRMF_R_SETTING_MAC_ALGOR_FAILURE - {"SETTING_MAC_ALGOR_FAILURE", ERR_LIB_CRMF, CRMF_R_SETTING_MAC_ALGOR_FAILURE}, - #else - {"SETTING_MAC_ALGOR_FAILURE", 56, 110}, - #endif - #ifdef CRMF_R_SETTING_OWF_ALGOR_FAILURE - {"SETTING_OWF_ALGOR_FAILURE", ERR_LIB_CRMF, CRMF_R_SETTING_OWF_ALGOR_FAILURE}, - #else - {"SETTING_OWF_ALGOR_FAILURE", 56, 111}, - #endif - #ifdef CRMF_R_UNSUPPORTED_ALGORITHM - {"UNSUPPORTED_ALGORITHM", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_ALGORITHM}, - #else - {"UNSUPPORTED_ALGORITHM", 56, 112}, - #endif - #ifdef CRMF_R_UNSUPPORTED_CIPHER - {"UNSUPPORTED_CIPHER", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_CIPHER}, - #else - {"UNSUPPORTED_CIPHER", 56, 114}, - #endif - #ifdef CRMF_R_UNSUPPORTED_METHOD_FOR_CREATING_POPO - {"UNSUPPORTED_METHOD_FOR_CREATING_POPO", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_METHOD_FOR_CREATING_POPO}, - #else - {"UNSUPPORTED_METHOD_FOR_CREATING_POPO", 56, 115}, - #endif - #ifdef CRMF_R_UNSUPPORTED_POPO_METHOD - {"UNSUPPORTED_POPO_METHOD", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_POPO_METHOD}, - #else - {"UNSUPPORTED_POPO_METHOD", 56, 116}, - #endif - #ifdef CRYPTO_R_BAD_ALGORITHM_NAME - {"BAD_ALGORITHM_NAME", ERR_LIB_CRYPTO, CRYPTO_R_BAD_ALGORITHM_NAME}, - #else - {"BAD_ALGORITHM_NAME", 15, 117}, - #endif - #ifdef CRYPTO_R_CONFLICTING_NAMES - {"CONFLICTING_NAMES", ERR_LIB_CRYPTO, CRYPTO_R_CONFLICTING_NAMES}, - #else - {"CONFLICTING_NAMES", 15, 118}, - #endif - #ifdef CRYPTO_R_HEX_STRING_TOO_SHORT - {"HEX_STRING_TOO_SHORT", ERR_LIB_CRYPTO, CRYPTO_R_HEX_STRING_TOO_SHORT}, - #else - {"HEX_STRING_TOO_SHORT", 15, 121}, - #endif - #ifdef CRYPTO_R_ILLEGAL_HEX_DIGIT - {"ILLEGAL_HEX_DIGIT", ERR_LIB_CRYPTO, CRYPTO_R_ILLEGAL_HEX_DIGIT}, - #else - {"ILLEGAL_HEX_DIGIT", 15, 102}, - #endif - #ifdef CRYPTO_R_INSUFFICIENT_DATA_SPACE - {"INSUFFICIENT_DATA_SPACE", ERR_LIB_CRYPTO, CRYPTO_R_INSUFFICIENT_DATA_SPACE}, - #else - {"INSUFFICIENT_DATA_SPACE", 15, 106}, - #endif - #ifdef CRYPTO_R_INSUFFICIENT_PARAM_SIZE - {"INSUFFICIENT_PARAM_SIZE", ERR_LIB_CRYPTO, CRYPTO_R_INSUFFICIENT_PARAM_SIZE}, - #else - {"INSUFFICIENT_PARAM_SIZE", 15, 107}, - #endif - #ifdef CRYPTO_R_INSUFFICIENT_SECURE_DATA_SPACE - {"INSUFFICIENT_SECURE_DATA_SPACE", ERR_LIB_CRYPTO, CRYPTO_R_INSUFFICIENT_SECURE_DATA_SPACE}, - #else - {"INSUFFICIENT_SECURE_DATA_SPACE", 15, 108}, - #endif - #ifdef CRYPTO_R_INTEGER_OVERFLOW - {"INTEGER_OVERFLOW", ERR_LIB_CRYPTO, CRYPTO_R_INTEGER_OVERFLOW}, - #else - {"INTEGER_OVERFLOW", 15, 127}, - #endif - #ifdef CRYPTO_R_INVALID_NEGATIVE_VALUE - {"INVALID_NEGATIVE_VALUE", ERR_LIB_CRYPTO, CRYPTO_R_INVALID_NEGATIVE_VALUE}, - #else - {"INVALID_NEGATIVE_VALUE", 15, 122}, - #endif - #ifdef CRYPTO_R_INVALID_NULL_ARGUMENT - {"INVALID_NULL_ARGUMENT", ERR_LIB_CRYPTO, CRYPTO_R_INVALID_NULL_ARGUMENT}, - #else - {"INVALID_NULL_ARGUMENT", 15, 109}, - #endif - #ifdef CRYPTO_R_INVALID_OSSL_PARAM_TYPE - {"INVALID_OSSL_PARAM_TYPE", ERR_LIB_CRYPTO, CRYPTO_R_INVALID_OSSL_PARAM_TYPE}, - #else - {"INVALID_OSSL_PARAM_TYPE", 15, 110}, - #endif - #ifdef CRYPTO_R_NO_PARAMS_TO_MERGE - {"NO_PARAMS_TO_MERGE", ERR_LIB_CRYPTO, CRYPTO_R_NO_PARAMS_TO_MERGE}, - #else - {"NO_PARAMS_TO_MERGE", 15, 131}, - #endif - #ifdef CRYPTO_R_NO_SPACE_FOR_TERMINATING_NULL - {"NO_SPACE_FOR_TERMINATING_NULL", ERR_LIB_CRYPTO, CRYPTO_R_NO_SPACE_FOR_TERMINATING_NULL}, - #else - {"NO_SPACE_FOR_TERMINATING_NULL", 15, 128}, - #endif - #ifdef CRYPTO_R_ODD_NUMBER_OF_DIGITS - {"ODD_NUMBER_OF_DIGITS", ERR_LIB_CRYPTO, CRYPTO_R_ODD_NUMBER_OF_DIGITS}, - #else - {"ODD_NUMBER_OF_DIGITS", 15, 103}, - #endif - #ifdef CRYPTO_R_PARAM_CANNOT_BE_REPRESENTED_EXACTLY - {"PARAM_CANNOT_BE_REPRESENTED_EXACTLY", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_CANNOT_BE_REPRESENTED_EXACTLY}, - #else - {"PARAM_CANNOT_BE_REPRESENTED_EXACTLY", 15, 123}, - #endif - #ifdef CRYPTO_R_PARAM_NOT_INTEGER_TYPE - {"PARAM_NOT_INTEGER_TYPE", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_NOT_INTEGER_TYPE}, - #else - {"PARAM_NOT_INTEGER_TYPE", 15, 124}, - #endif - #ifdef CRYPTO_R_PARAM_OF_INCOMPATIBLE_TYPE - {"PARAM_OF_INCOMPATIBLE_TYPE", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_OF_INCOMPATIBLE_TYPE}, - #else - {"PARAM_OF_INCOMPATIBLE_TYPE", 15, 129}, - #endif - #ifdef CRYPTO_R_PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED - {"PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED}, - #else - {"PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED", 15, 125}, - #endif - #ifdef CRYPTO_R_PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT - {"PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT}, - #else - {"PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT", 15, 130}, - #endif - #ifdef CRYPTO_R_PARAM_VALUE_TOO_LARGE_FOR_DESTINATION - {"PARAM_VALUE_TOO_LARGE_FOR_DESTINATION", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_VALUE_TOO_LARGE_FOR_DESTINATION}, - #else - {"PARAM_VALUE_TOO_LARGE_FOR_DESTINATION", 15, 126}, - #endif - #ifdef CRYPTO_R_PROVIDER_ALREADY_EXISTS - {"PROVIDER_ALREADY_EXISTS", ERR_LIB_CRYPTO, CRYPTO_R_PROVIDER_ALREADY_EXISTS}, - #else - {"PROVIDER_ALREADY_EXISTS", 15, 104}, - #endif - #ifdef CRYPTO_R_PROVIDER_SECTION_ERROR - {"PROVIDER_SECTION_ERROR", ERR_LIB_CRYPTO, CRYPTO_R_PROVIDER_SECTION_ERROR}, - #else - {"PROVIDER_SECTION_ERROR", 15, 105}, - #endif - #ifdef CRYPTO_R_RANDOM_SECTION_ERROR - {"RANDOM_SECTION_ERROR", ERR_LIB_CRYPTO, CRYPTO_R_RANDOM_SECTION_ERROR}, - #else - {"RANDOM_SECTION_ERROR", 15, 119}, - #endif - #ifdef CRYPTO_R_SECURE_MALLOC_FAILURE - {"SECURE_MALLOC_FAILURE", ERR_LIB_CRYPTO, CRYPTO_R_SECURE_MALLOC_FAILURE}, - #else - {"SECURE_MALLOC_FAILURE", 15, 111}, - #endif - #ifdef CRYPTO_R_STRING_TOO_LONG - {"STRING_TOO_LONG", ERR_LIB_CRYPTO, CRYPTO_R_STRING_TOO_LONG}, - #else - {"STRING_TOO_LONG", 15, 112}, - #endif - #ifdef CRYPTO_R_TOO_MANY_BYTES - {"TOO_MANY_BYTES", ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_BYTES}, - #else - {"TOO_MANY_BYTES", 15, 113}, - #endif - #ifdef CRYPTO_R_TOO_MANY_NAMES - {"TOO_MANY_NAMES", ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_NAMES}, - #else - {"TOO_MANY_NAMES", 15, 132}, - #endif - #ifdef CRYPTO_R_TOO_MANY_RECORDS - {"TOO_MANY_RECORDS", ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_RECORDS}, - #else - {"TOO_MANY_RECORDS", 15, 114}, - #endif - #ifdef CRYPTO_R_TOO_SMALL_BUFFER - {"TOO_SMALL_BUFFER", ERR_LIB_CRYPTO, CRYPTO_R_TOO_SMALL_BUFFER}, - #else - {"TOO_SMALL_BUFFER", 15, 116}, - #endif - #ifdef CRYPTO_R_UNKNOWN_NAME_IN_RANDOM_SECTION - {"UNKNOWN_NAME_IN_RANDOM_SECTION", ERR_LIB_CRYPTO, CRYPTO_R_UNKNOWN_NAME_IN_RANDOM_SECTION}, - #else - {"UNKNOWN_NAME_IN_RANDOM_SECTION", 15, 120}, - #endif - #ifdef CRYPTO_R_ZERO_LENGTH_NUMBER - {"ZERO_LENGTH_NUMBER", ERR_LIB_CRYPTO, CRYPTO_R_ZERO_LENGTH_NUMBER}, - #else - {"ZERO_LENGTH_NUMBER", 15, 115}, - #endif - #ifdef CT_R_BASE64_DECODE_ERROR - {"BASE64_DECODE_ERROR", ERR_LIB_CT, CT_R_BASE64_DECODE_ERROR}, - #else - {"BASE64_DECODE_ERROR", 50, 108}, - #endif - #ifdef CT_R_INVALID_LOG_ID_LENGTH - {"INVALID_LOG_ID_LENGTH", ERR_LIB_CT, CT_R_INVALID_LOG_ID_LENGTH}, - #else - {"INVALID_LOG_ID_LENGTH", 50, 100}, - #endif - #ifdef CT_R_LOG_CONF_INVALID - {"LOG_CONF_INVALID", ERR_LIB_CT, CT_R_LOG_CONF_INVALID}, - #else - {"LOG_CONF_INVALID", 50, 109}, - #endif - #ifdef CT_R_LOG_CONF_INVALID_KEY - {"LOG_CONF_INVALID_KEY", ERR_LIB_CT, CT_R_LOG_CONF_INVALID_KEY}, - #else - {"LOG_CONF_INVALID_KEY", 50, 110}, - #endif - #ifdef CT_R_LOG_CONF_MISSING_DESCRIPTION - {"LOG_CONF_MISSING_DESCRIPTION", ERR_LIB_CT, CT_R_LOG_CONF_MISSING_DESCRIPTION}, - #else - {"LOG_CONF_MISSING_DESCRIPTION", 50, 111}, - #endif - #ifdef CT_R_LOG_CONF_MISSING_KEY - {"LOG_CONF_MISSING_KEY", ERR_LIB_CT, CT_R_LOG_CONF_MISSING_KEY}, - #else - {"LOG_CONF_MISSING_KEY", 50, 112}, - #endif - #ifdef CT_R_LOG_KEY_INVALID - {"LOG_KEY_INVALID", ERR_LIB_CT, CT_R_LOG_KEY_INVALID}, - #else - {"LOG_KEY_INVALID", 50, 113}, - #endif - #ifdef CT_R_SCT_FUTURE_TIMESTAMP - {"SCT_FUTURE_TIMESTAMP", ERR_LIB_CT, CT_R_SCT_FUTURE_TIMESTAMP}, - #else - {"SCT_FUTURE_TIMESTAMP", 50, 116}, - #endif - #ifdef CT_R_SCT_INVALID - {"SCT_INVALID", ERR_LIB_CT, CT_R_SCT_INVALID}, - #else - {"SCT_INVALID", 50, 104}, - #endif - #ifdef CT_R_SCT_INVALID_SIGNATURE - {"SCT_INVALID_SIGNATURE", ERR_LIB_CT, CT_R_SCT_INVALID_SIGNATURE}, - #else - {"SCT_INVALID_SIGNATURE", 50, 107}, - #endif - #ifdef CT_R_SCT_LIST_INVALID - {"SCT_LIST_INVALID", ERR_LIB_CT, CT_R_SCT_LIST_INVALID}, - #else - {"SCT_LIST_INVALID", 50, 105}, - #endif - #ifdef CT_R_SCT_LOG_ID_MISMATCH - {"SCT_LOG_ID_MISMATCH", ERR_LIB_CT, CT_R_SCT_LOG_ID_MISMATCH}, - #else - {"SCT_LOG_ID_MISMATCH", 50, 114}, - #endif - #ifdef CT_R_SCT_NOT_SET - {"SCT_NOT_SET", ERR_LIB_CT, CT_R_SCT_NOT_SET}, - #else - {"SCT_NOT_SET", 50, 106}, - #endif - #ifdef CT_R_SCT_UNSUPPORTED_VERSION - {"SCT_UNSUPPORTED_VERSION", ERR_LIB_CT, CT_R_SCT_UNSUPPORTED_VERSION}, - #else - {"SCT_UNSUPPORTED_VERSION", 50, 115}, - #endif - #ifdef CT_R_UNRECOGNIZED_SIGNATURE_NID - {"UNRECOGNIZED_SIGNATURE_NID", ERR_LIB_CT, CT_R_UNRECOGNIZED_SIGNATURE_NID}, - #else - {"UNRECOGNIZED_SIGNATURE_NID", 50, 101}, - #endif - #ifdef CT_R_UNSUPPORTED_ENTRY_TYPE - {"UNSUPPORTED_ENTRY_TYPE", ERR_LIB_CT, CT_R_UNSUPPORTED_ENTRY_TYPE}, - #else - {"UNSUPPORTED_ENTRY_TYPE", 50, 102}, - #endif - #ifdef CT_R_UNSUPPORTED_VERSION - {"UNSUPPORTED_VERSION", ERR_LIB_CT, CT_R_UNSUPPORTED_VERSION}, - #else - {"UNSUPPORTED_VERSION", 50, 103}, - #endif - #ifdef DH_R_BAD_FFC_PARAMETERS - {"BAD_FFC_PARAMETERS", ERR_LIB_DH, DH_R_BAD_FFC_PARAMETERS}, - #else - {"BAD_FFC_PARAMETERS", 5, 127}, - #endif - #ifdef DH_R_BAD_GENERATOR - {"BAD_GENERATOR", ERR_LIB_DH, DH_R_BAD_GENERATOR}, - #else - {"BAD_GENERATOR", 5, 101}, - #endif - #ifdef DH_R_BN_DECODE_ERROR - {"BN_DECODE_ERROR", ERR_LIB_DH, DH_R_BN_DECODE_ERROR}, - #else - {"BN_DECODE_ERROR", 5, 109}, - #endif - #ifdef DH_R_BN_ERROR - {"BN_ERROR", ERR_LIB_DH, DH_R_BN_ERROR}, - #else - {"BN_ERROR", 5, 106}, - #endif - #ifdef DH_R_CHECK_INVALID_J_VALUE - {"CHECK_INVALID_J_VALUE", ERR_LIB_DH, DH_R_CHECK_INVALID_J_VALUE}, - #else - {"CHECK_INVALID_J_VALUE", 5, 115}, - #endif - #ifdef DH_R_CHECK_INVALID_Q_VALUE - {"CHECK_INVALID_Q_VALUE", ERR_LIB_DH, DH_R_CHECK_INVALID_Q_VALUE}, - #else - {"CHECK_INVALID_Q_VALUE", 5, 116}, - #endif - #ifdef DH_R_CHECK_PUBKEY_INVALID - {"CHECK_PUBKEY_INVALID", ERR_LIB_DH, DH_R_CHECK_PUBKEY_INVALID}, - #else - {"CHECK_PUBKEY_INVALID", 5, 122}, - #endif - #ifdef DH_R_CHECK_PUBKEY_TOO_LARGE - {"CHECK_PUBKEY_TOO_LARGE", ERR_LIB_DH, DH_R_CHECK_PUBKEY_TOO_LARGE}, - #else - {"CHECK_PUBKEY_TOO_LARGE", 5, 123}, - #endif - #ifdef DH_R_CHECK_PUBKEY_TOO_SMALL - {"CHECK_PUBKEY_TOO_SMALL", ERR_LIB_DH, DH_R_CHECK_PUBKEY_TOO_SMALL}, - #else - {"CHECK_PUBKEY_TOO_SMALL", 5, 124}, - #endif - #ifdef DH_R_CHECK_P_NOT_PRIME - {"CHECK_P_NOT_PRIME", ERR_LIB_DH, DH_R_CHECK_P_NOT_PRIME}, - #else - {"CHECK_P_NOT_PRIME", 5, 117}, - #endif - #ifdef DH_R_CHECK_P_NOT_SAFE_PRIME - {"CHECK_P_NOT_SAFE_PRIME", ERR_LIB_DH, DH_R_CHECK_P_NOT_SAFE_PRIME}, - #else - {"CHECK_P_NOT_SAFE_PRIME", 5, 118}, - #endif - #ifdef DH_R_CHECK_Q_NOT_PRIME - {"CHECK_Q_NOT_PRIME", ERR_LIB_DH, DH_R_CHECK_Q_NOT_PRIME}, - #else - {"CHECK_Q_NOT_PRIME", 5, 119}, - #endif - #ifdef DH_R_DECODE_ERROR - {"DECODE_ERROR", ERR_LIB_DH, DH_R_DECODE_ERROR}, - #else - {"DECODE_ERROR", 5, 104}, - #endif - #ifdef DH_R_INVALID_PARAMETER_NAME - {"INVALID_PARAMETER_NAME", ERR_LIB_DH, DH_R_INVALID_PARAMETER_NAME}, - #else - {"INVALID_PARAMETER_NAME", 5, 110}, - #endif - #ifdef DH_R_INVALID_PARAMETER_NID - {"INVALID_PARAMETER_NID", ERR_LIB_DH, DH_R_INVALID_PARAMETER_NID}, - #else - {"INVALID_PARAMETER_NID", 5, 114}, - #endif - #ifdef DH_R_INVALID_PUBKEY - {"INVALID_PUBKEY", ERR_LIB_DH, DH_R_INVALID_PUBKEY}, - #else - {"INVALID_PUBKEY", 5, 102}, - #endif - #ifdef DH_R_INVALID_SECRET - {"INVALID_SECRET", ERR_LIB_DH, DH_R_INVALID_SECRET}, - #else - {"INVALID_SECRET", 5, 128}, - #endif - #ifdef DH_R_INVALID_SIZE - {"INVALID_SIZE", ERR_LIB_DH, DH_R_INVALID_SIZE}, - #else - {"INVALID_SIZE", 5, 129}, - #endif - #ifdef DH_R_KDF_PARAMETER_ERROR - {"KDF_PARAMETER_ERROR", ERR_LIB_DH, DH_R_KDF_PARAMETER_ERROR}, - #else - {"KDF_PARAMETER_ERROR", 5, 112}, - #endif - #ifdef DH_R_KEYS_NOT_SET - {"KEYS_NOT_SET", ERR_LIB_DH, DH_R_KEYS_NOT_SET}, - #else - {"KEYS_NOT_SET", 5, 108}, - #endif - #ifdef DH_R_MISSING_PUBKEY - {"MISSING_PUBKEY", ERR_LIB_DH, DH_R_MISSING_PUBKEY}, - #else - {"MISSING_PUBKEY", 5, 125}, - #endif - #ifdef DH_R_MODULUS_TOO_LARGE - {"MODULUS_TOO_LARGE", ERR_LIB_DH, DH_R_MODULUS_TOO_LARGE}, - #else - {"MODULUS_TOO_LARGE", 5, 103}, - #endif - #ifdef DH_R_MODULUS_TOO_SMALL - {"MODULUS_TOO_SMALL", ERR_LIB_DH, DH_R_MODULUS_TOO_SMALL}, - #else - {"MODULUS_TOO_SMALL", 5, 126}, - #endif - #ifdef DH_R_NOT_SUITABLE_GENERATOR - {"NOT_SUITABLE_GENERATOR", ERR_LIB_DH, DH_R_NOT_SUITABLE_GENERATOR}, - #else - {"NOT_SUITABLE_GENERATOR", 5, 120}, - #endif - #ifdef DH_R_NO_PARAMETERS_SET - {"NO_PARAMETERS_SET", ERR_LIB_DH, DH_R_NO_PARAMETERS_SET}, - #else - {"NO_PARAMETERS_SET", 5, 107}, - #endif - #ifdef DH_R_NO_PRIVATE_VALUE - {"NO_PRIVATE_VALUE", ERR_LIB_DH, DH_R_NO_PRIVATE_VALUE}, - #else - {"NO_PRIVATE_VALUE", 5, 100}, - #endif - #ifdef DH_R_PARAMETER_ENCODING_ERROR - {"PARAMETER_ENCODING_ERROR", ERR_LIB_DH, DH_R_PARAMETER_ENCODING_ERROR}, - #else - {"PARAMETER_ENCODING_ERROR", 5, 105}, - #endif - #ifdef DH_R_PEER_KEY_ERROR - {"PEER_KEY_ERROR", ERR_LIB_DH, DH_R_PEER_KEY_ERROR}, - #else - {"PEER_KEY_ERROR", 5, 111}, - #endif - #ifdef DH_R_Q_TOO_LARGE - {"Q_TOO_LARGE", ERR_LIB_DH, DH_R_Q_TOO_LARGE}, - #else - {"Q_TOO_LARGE", 5, 130}, - #endif - #ifdef DH_R_SHARED_INFO_ERROR - {"SHARED_INFO_ERROR", ERR_LIB_DH, DH_R_SHARED_INFO_ERROR}, - #else - {"SHARED_INFO_ERROR", 5, 113}, - #endif - #ifdef DH_R_UNABLE_TO_CHECK_GENERATOR - {"UNABLE_TO_CHECK_GENERATOR", ERR_LIB_DH, DH_R_UNABLE_TO_CHECK_GENERATOR}, - #else - {"UNABLE_TO_CHECK_GENERATOR", 5, 121}, - #endif - #ifdef DSA_R_BAD_FFC_PARAMETERS - {"BAD_FFC_PARAMETERS", ERR_LIB_DSA, DSA_R_BAD_FFC_PARAMETERS}, - #else - {"BAD_FFC_PARAMETERS", 10, 114}, - #endif - #ifdef DSA_R_BAD_Q_VALUE - {"BAD_Q_VALUE", ERR_LIB_DSA, DSA_R_BAD_Q_VALUE}, - #else - {"BAD_Q_VALUE", 10, 102}, - #endif - #ifdef DSA_R_BN_DECODE_ERROR - {"BN_DECODE_ERROR", ERR_LIB_DSA, DSA_R_BN_DECODE_ERROR}, - #else - {"BN_DECODE_ERROR", 10, 108}, - #endif - #ifdef DSA_R_BN_ERROR - {"BN_ERROR", ERR_LIB_DSA, DSA_R_BN_ERROR}, - #else - {"BN_ERROR", 10, 109}, - #endif - #ifdef DSA_R_DECODE_ERROR - {"DECODE_ERROR", ERR_LIB_DSA, DSA_R_DECODE_ERROR}, - #else - {"DECODE_ERROR", 10, 104}, - #endif - #ifdef DSA_R_INVALID_DIGEST_TYPE - {"INVALID_DIGEST_TYPE", ERR_LIB_DSA, DSA_R_INVALID_DIGEST_TYPE}, - #else - {"INVALID_DIGEST_TYPE", 10, 106}, - #endif - #ifdef DSA_R_INVALID_PARAMETERS - {"INVALID_PARAMETERS", ERR_LIB_DSA, DSA_R_INVALID_PARAMETERS}, - #else - {"INVALID_PARAMETERS", 10, 112}, - #endif - #ifdef DSA_R_MISSING_PARAMETERS - {"MISSING_PARAMETERS", ERR_LIB_DSA, DSA_R_MISSING_PARAMETERS}, - #else - {"MISSING_PARAMETERS", 10, 101}, - #endif - #ifdef DSA_R_MISSING_PRIVATE_KEY - {"MISSING_PRIVATE_KEY", ERR_LIB_DSA, DSA_R_MISSING_PRIVATE_KEY}, - #else - {"MISSING_PRIVATE_KEY", 10, 111}, - #endif - #ifdef DSA_R_MODULUS_TOO_LARGE - {"MODULUS_TOO_LARGE", ERR_LIB_DSA, DSA_R_MODULUS_TOO_LARGE}, - #else - {"MODULUS_TOO_LARGE", 10, 103}, - #endif - #ifdef DSA_R_NO_PARAMETERS_SET - {"NO_PARAMETERS_SET", ERR_LIB_DSA, DSA_R_NO_PARAMETERS_SET}, - #else - {"NO_PARAMETERS_SET", 10, 107}, - #endif - #ifdef DSA_R_PARAMETER_ENCODING_ERROR - {"PARAMETER_ENCODING_ERROR", ERR_LIB_DSA, DSA_R_PARAMETER_ENCODING_ERROR}, - #else - {"PARAMETER_ENCODING_ERROR", 10, 105}, - #endif - #ifdef DSA_R_P_NOT_PRIME - {"P_NOT_PRIME", ERR_LIB_DSA, DSA_R_P_NOT_PRIME}, - #else - {"P_NOT_PRIME", 10, 115}, - #endif - #ifdef DSA_R_Q_NOT_PRIME - {"Q_NOT_PRIME", ERR_LIB_DSA, DSA_R_Q_NOT_PRIME}, - #else - {"Q_NOT_PRIME", 10, 113}, - #endif - #ifdef DSA_R_SEED_LEN_SMALL - {"SEED_LEN_SMALL", ERR_LIB_DSA, DSA_R_SEED_LEN_SMALL}, - #else - {"SEED_LEN_SMALL", 10, 110}, - #endif - #ifdef DSA_R_TOO_MANY_RETRIES - {"TOO_MANY_RETRIES", ERR_LIB_DSA, DSA_R_TOO_MANY_RETRIES}, - #else - {"TOO_MANY_RETRIES", 10, 116}, - #endif - #ifdef DSO_R_CTRL_FAILED - {"CTRL_FAILED", ERR_LIB_DSO, DSO_R_CTRL_FAILED}, - #else - {"CTRL_FAILED", 37, 100}, - #endif - #ifdef DSO_R_DSO_ALREADY_LOADED - {"DSO_ALREADY_LOADED", ERR_LIB_DSO, DSO_R_DSO_ALREADY_LOADED}, - #else - {"DSO_ALREADY_LOADED", 37, 110}, - #endif - #ifdef DSO_R_EMPTY_FILE_STRUCTURE - {"EMPTY_FILE_STRUCTURE", ERR_LIB_DSO, DSO_R_EMPTY_FILE_STRUCTURE}, - #else - {"EMPTY_FILE_STRUCTURE", 37, 113}, - #endif - #ifdef DSO_R_FAILURE - {"FAILURE", ERR_LIB_DSO, DSO_R_FAILURE}, - #else - {"FAILURE", 37, 114}, - #endif - #ifdef DSO_R_FILENAME_TOO_BIG - {"FILENAME_TOO_BIG", ERR_LIB_DSO, DSO_R_FILENAME_TOO_BIG}, - #else - {"FILENAME_TOO_BIG", 37, 101}, - #endif - #ifdef DSO_R_FINISH_FAILED - {"FINISH_FAILED", ERR_LIB_DSO, DSO_R_FINISH_FAILED}, - #else - {"FINISH_FAILED", 37, 102}, - #endif - #ifdef DSO_R_INCORRECT_FILE_SYNTAX - {"INCORRECT_FILE_SYNTAX", ERR_LIB_DSO, DSO_R_INCORRECT_FILE_SYNTAX}, - #else - {"INCORRECT_FILE_SYNTAX", 37, 115}, - #endif - #ifdef DSO_R_LOAD_FAILED - {"LOAD_FAILED", ERR_LIB_DSO, DSO_R_LOAD_FAILED}, - #else - {"LOAD_FAILED", 37, 103}, - #endif - #ifdef DSO_R_NAME_TRANSLATION_FAILED - {"NAME_TRANSLATION_FAILED", ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED}, - #else - {"NAME_TRANSLATION_FAILED", 37, 109}, - #endif - #ifdef DSO_R_NO_FILENAME - {"NO_FILENAME", ERR_LIB_DSO, DSO_R_NO_FILENAME}, - #else - {"NO_FILENAME", 37, 111}, - #endif - #ifdef DSO_R_NULL_HANDLE - {"NULL_HANDLE", ERR_LIB_DSO, DSO_R_NULL_HANDLE}, - #else - {"NULL_HANDLE", 37, 104}, - #endif - #ifdef DSO_R_SET_FILENAME_FAILED - {"SET_FILENAME_FAILED", ERR_LIB_DSO, DSO_R_SET_FILENAME_FAILED}, - #else - {"SET_FILENAME_FAILED", 37, 112}, - #endif - #ifdef DSO_R_STACK_ERROR - {"STACK_ERROR", ERR_LIB_DSO, DSO_R_STACK_ERROR}, - #else - {"STACK_ERROR", 37, 105}, - #endif - #ifdef DSO_R_SYM_FAILURE - {"SYM_FAILURE", ERR_LIB_DSO, DSO_R_SYM_FAILURE}, - #else - {"SYM_FAILURE", 37, 106}, - #endif - #ifdef DSO_R_UNLOAD_FAILED - {"UNLOAD_FAILED", ERR_LIB_DSO, DSO_R_UNLOAD_FAILED}, - #else - {"UNLOAD_FAILED", 37, 107}, - #endif - #ifdef DSO_R_UNSUPPORTED - {"UNSUPPORTED", ERR_LIB_DSO, DSO_R_UNSUPPORTED}, - #else - {"UNSUPPORTED", 37, 108}, - #endif - #ifdef EC_R_ASN1_ERROR - {"ASN1_ERROR", ERR_LIB_EC, EC_R_ASN1_ERROR}, - #else - {"ASN1_ERROR", 16, 115}, - #endif - #ifdef EC_R_BAD_SIGNATURE - {"BAD_SIGNATURE", ERR_LIB_EC, EC_R_BAD_SIGNATURE}, - #else - {"BAD_SIGNATURE", 16, 156}, - #endif - #ifdef EC_R_BIGNUM_OUT_OF_RANGE - {"BIGNUM_OUT_OF_RANGE", ERR_LIB_EC, EC_R_BIGNUM_OUT_OF_RANGE}, - #else - {"BIGNUM_OUT_OF_RANGE", 16, 144}, - #endif - #ifdef EC_R_BUFFER_TOO_SMALL - {"BUFFER_TOO_SMALL", ERR_LIB_EC, EC_R_BUFFER_TOO_SMALL}, - #else - {"BUFFER_TOO_SMALL", 16, 100}, - #endif - #ifdef EC_R_CANNOT_INVERT - {"CANNOT_INVERT", ERR_LIB_EC, EC_R_CANNOT_INVERT}, - #else - {"CANNOT_INVERT", 16, 165}, - #endif - #ifdef EC_R_COORDINATES_OUT_OF_RANGE - {"COORDINATES_OUT_OF_RANGE", ERR_LIB_EC, EC_R_COORDINATES_OUT_OF_RANGE}, - #else - {"COORDINATES_OUT_OF_RANGE", 16, 146}, - #endif - #ifdef EC_R_CURVE_DOES_NOT_SUPPORT_ECDH - {"CURVE_DOES_NOT_SUPPORT_ECDH", ERR_LIB_EC, EC_R_CURVE_DOES_NOT_SUPPORT_ECDH}, - #else - {"CURVE_DOES_NOT_SUPPORT_ECDH", 16, 160}, - #endif - #ifdef EC_R_CURVE_DOES_NOT_SUPPORT_ECDSA - {"CURVE_DOES_NOT_SUPPORT_ECDSA", ERR_LIB_EC, EC_R_CURVE_DOES_NOT_SUPPORT_ECDSA}, - #else - {"CURVE_DOES_NOT_SUPPORT_ECDSA", 16, 170}, - #endif - #ifdef EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING - {"CURVE_DOES_NOT_SUPPORT_SIGNING", ERR_LIB_EC, EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING}, - #else - {"CURVE_DOES_NOT_SUPPORT_SIGNING", 16, 159}, - #endif - #ifdef EC_R_DECODE_ERROR - {"DECODE_ERROR", ERR_LIB_EC, EC_R_DECODE_ERROR}, - #else - {"DECODE_ERROR", 16, 142}, - #endif - #ifdef EC_R_DISCRIMINANT_IS_ZERO - {"DISCRIMINANT_IS_ZERO", ERR_LIB_EC, EC_R_DISCRIMINANT_IS_ZERO}, - #else - {"DISCRIMINANT_IS_ZERO", 16, 118}, - #endif - #ifdef EC_R_EC_GROUP_NEW_BY_NAME_FAILURE - {"EC_GROUP_NEW_BY_NAME_FAILURE", ERR_LIB_EC, EC_R_EC_GROUP_NEW_BY_NAME_FAILURE}, - #else - {"EC_GROUP_NEW_BY_NAME_FAILURE", 16, 119}, - #endif - #ifdef EC_R_EXPLICIT_PARAMS_NOT_SUPPORTED - {"EXPLICIT_PARAMS_NOT_SUPPORTED", ERR_LIB_EC, EC_R_EXPLICIT_PARAMS_NOT_SUPPORTED}, - #else - {"EXPLICIT_PARAMS_NOT_SUPPORTED", 16, 127}, - #endif - #ifdef EC_R_FAILED_MAKING_PUBLIC_KEY - {"FAILED_MAKING_PUBLIC_KEY", ERR_LIB_EC, EC_R_FAILED_MAKING_PUBLIC_KEY}, - #else - {"FAILED_MAKING_PUBLIC_KEY", 16, 166}, - #endif - #ifdef EC_R_FIELD_TOO_LARGE - {"FIELD_TOO_LARGE", ERR_LIB_EC, EC_R_FIELD_TOO_LARGE}, - #else - {"FIELD_TOO_LARGE", 16, 143}, - #endif - #ifdef EC_R_GF2M_NOT_SUPPORTED - {"GF2M_NOT_SUPPORTED", ERR_LIB_EC, EC_R_GF2M_NOT_SUPPORTED}, - #else - {"GF2M_NOT_SUPPORTED", 16, 147}, - #endif - #ifdef EC_R_GROUP2PKPARAMETERS_FAILURE - {"GROUP2PKPARAMETERS_FAILURE", ERR_LIB_EC, EC_R_GROUP2PKPARAMETERS_FAILURE}, - #else - {"GROUP2PKPARAMETERS_FAILURE", 16, 120}, - #endif - #ifdef EC_R_I2D_ECPKPARAMETERS_FAILURE - {"I2D_ECPKPARAMETERS_FAILURE", ERR_LIB_EC, EC_R_I2D_ECPKPARAMETERS_FAILURE}, - #else - {"I2D_ECPKPARAMETERS_FAILURE", 16, 121}, - #endif - #ifdef EC_R_INCOMPATIBLE_OBJECTS - {"INCOMPATIBLE_OBJECTS", ERR_LIB_EC, EC_R_INCOMPATIBLE_OBJECTS}, - #else - {"INCOMPATIBLE_OBJECTS", 16, 101}, - #endif - #ifdef EC_R_INVALID_A - {"INVALID_A", ERR_LIB_EC, EC_R_INVALID_A}, - #else - {"INVALID_A", 16, 168}, - #endif - #ifdef EC_R_INVALID_ARGUMENT - {"INVALID_ARGUMENT", ERR_LIB_EC, EC_R_INVALID_ARGUMENT}, - #else - {"INVALID_ARGUMENT", 16, 112}, - #endif - #ifdef EC_R_INVALID_B - {"INVALID_B", ERR_LIB_EC, EC_R_INVALID_B}, - #else - {"INVALID_B", 16, 169}, - #endif - #ifdef EC_R_INVALID_COFACTOR - {"INVALID_COFACTOR", ERR_LIB_EC, EC_R_INVALID_COFACTOR}, - #else - {"INVALID_COFACTOR", 16, 171}, - #endif - #ifdef EC_R_INVALID_COMPRESSED_POINT - {"INVALID_COMPRESSED_POINT", ERR_LIB_EC, EC_R_INVALID_COMPRESSED_POINT}, - #else - {"INVALID_COMPRESSED_POINT", 16, 110}, - #endif - #ifdef EC_R_INVALID_COMPRESSION_BIT - {"INVALID_COMPRESSION_BIT", ERR_LIB_EC, EC_R_INVALID_COMPRESSION_BIT}, - #else - {"INVALID_COMPRESSION_BIT", 16, 109}, - #endif - #ifdef EC_R_INVALID_CURVE - {"INVALID_CURVE", ERR_LIB_EC, EC_R_INVALID_CURVE}, - #else - {"INVALID_CURVE", 16, 141}, - #endif - #ifdef EC_R_INVALID_DIGEST - {"INVALID_DIGEST", ERR_LIB_EC, EC_R_INVALID_DIGEST}, - #else - {"INVALID_DIGEST", 16, 151}, - #endif - #ifdef EC_R_INVALID_DIGEST_TYPE - {"INVALID_DIGEST_TYPE", ERR_LIB_EC, EC_R_INVALID_DIGEST_TYPE}, - #else - {"INVALID_DIGEST_TYPE", 16, 138}, - #endif - #ifdef EC_R_INVALID_ENCODING - {"INVALID_ENCODING", ERR_LIB_EC, EC_R_INVALID_ENCODING}, - #else - {"INVALID_ENCODING", 16, 102}, - #endif - #ifdef EC_R_INVALID_FIELD - {"INVALID_FIELD", ERR_LIB_EC, EC_R_INVALID_FIELD}, - #else - {"INVALID_FIELD", 16, 103}, - #endif - #ifdef EC_R_INVALID_FORM - {"INVALID_FORM", ERR_LIB_EC, EC_R_INVALID_FORM}, - #else - {"INVALID_FORM", 16, 104}, - #endif - #ifdef EC_R_INVALID_GENERATOR - {"INVALID_GENERATOR", ERR_LIB_EC, EC_R_INVALID_GENERATOR}, - #else - {"INVALID_GENERATOR", 16, 173}, - #endif - #ifdef EC_R_INVALID_GROUP_ORDER - {"INVALID_GROUP_ORDER", ERR_LIB_EC, EC_R_INVALID_GROUP_ORDER}, - #else - {"INVALID_GROUP_ORDER", 16, 122}, - #endif - #ifdef EC_R_INVALID_KEY - {"INVALID_KEY", ERR_LIB_EC, EC_R_INVALID_KEY}, - #else - {"INVALID_KEY", 16, 116}, - #endif - #ifdef EC_R_INVALID_LENGTH - {"INVALID_LENGTH", ERR_LIB_EC, EC_R_INVALID_LENGTH}, - #else - {"INVALID_LENGTH", 16, 117}, - #endif - #ifdef EC_R_INVALID_NAMED_GROUP_CONVERSION - {"INVALID_NAMED_GROUP_CONVERSION", ERR_LIB_EC, EC_R_INVALID_NAMED_GROUP_CONVERSION}, - #else - {"INVALID_NAMED_GROUP_CONVERSION", 16, 174}, - #endif - #ifdef EC_R_INVALID_OUTPUT_LENGTH - {"INVALID_OUTPUT_LENGTH", ERR_LIB_EC, EC_R_INVALID_OUTPUT_LENGTH}, - #else - {"INVALID_OUTPUT_LENGTH", 16, 161}, - #endif - #ifdef EC_R_INVALID_P - {"INVALID_P", ERR_LIB_EC, EC_R_INVALID_P}, - #else - {"INVALID_P", 16, 172}, - #endif - #ifdef EC_R_INVALID_PEER_KEY - {"INVALID_PEER_KEY", ERR_LIB_EC, EC_R_INVALID_PEER_KEY}, - #else - {"INVALID_PEER_KEY", 16, 133}, - #endif - #ifdef EC_R_INVALID_PENTANOMIAL_BASIS - {"INVALID_PENTANOMIAL_BASIS", ERR_LIB_EC, EC_R_INVALID_PENTANOMIAL_BASIS}, - #else - {"INVALID_PENTANOMIAL_BASIS", 16, 132}, - #endif - #ifdef EC_R_INVALID_PRIVATE_KEY - {"INVALID_PRIVATE_KEY", ERR_LIB_EC, EC_R_INVALID_PRIVATE_KEY}, - #else - {"INVALID_PRIVATE_KEY", 16, 123}, - #endif - #ifdef EC_R_INVALID_SEED - {"INVALID_SEED", ERR_LIB_EC, EC_R_INVALID_SEED}, - #else - {"INVALID_SEED", 16, 175}, - #endif - #ifdef EC_R_INVALID_TRINOMIAL_BASIS - {"INVALID_TRINOMIAL_BASIS", ERR_LIB_EC, EC_R_INVALID_TRINOMIAL_BASIS}, - #else - {"INVALID_TRINOMIAL_BASIS", 16, 137}, - #endif - #ifdef EC_R_KDF_PARAMETER_ERROR - {"KDF_PARAMETER_ERROR", ERR_LIB_EC, EC_R_KDF_PARAMETER_ERROR}, - #else - {"KDF_PARAMETER_ERROR", 16, 148}, - #endif - #ifdef EC_R_KEYS_NOT_SET - {"KEYS_NOT_SET", ERR_LIB_EC, EC_R_KEYS_NOT_SET}, - #else - {"KEYS_NOT_SET", 16, 140}, - #endif - #ifdef EC_R_LADDER_POST_FAILURE - {"LADDER_POST_FAILURE", ERR_LIB_EC, EC_R_LADDER_POST_FAILURE}, - #else - {"LADDER_POST_FAILURE", 16, 136}, - #endif - #ifdef EC_R_LADDER_PRE_FAILURE - {"LADDER_PRE_FAILURE", ERR_LIB_EC, EC_R_LADDER_PRE_FAILURE}, - #else - {"LADDER_PRE_FAILURE", 16, 153}, - #endif - #ifdef EC_R_LADDER_STEP_FAILURE - {"LADDER_STEP_FAILURE", ERR_LIB_EC, EC_R_LADDER_STEP_FAILURE}, - #else - {"LADDER_STEP_FAILURE", 16, 162}, - #endif - #ifdef EC_R_MISSING_OID - {"MISSING_OID", ERR_LIB_EC, EC_R_MISSING_OID}, - #else - {"MISSING_OID", 16, 167}, - #endif - #ifdef EC_R_MISSING_PARAMETERS - {"MISSING_PARAMETERS", ERR_LIB_EC, EC_R_MISSING_PARAMETERS}, - #else - {"MISSING_PARAMETERS", 16, 124}, - #endif - #ifdef EC_R_MISSING_PRIVATE_KEY - {"MISSING_PRIVATE_KEY", ERR_LIB_EC, EC_R_MISSING_PRIVATE_KEY}, - #else - {"MISSING_PRIVATE_KEY", 16, 125}, - #endif - #ifdef EC_R_NEED_NEW_SETUP_VALUES - {"NEED_NEW_SETUP_VALUES", ERR_LIB_EC, EC_R_NEED_NEW_SETUP_VALUES}, - #else - {"NEED_NEW_SETUP_VALUES", 16, 157}, - #endif - #ifdef EC_R_NOT_A_NIST_PRIME - {"NOT_A_NIST_PRIME", ERR_LIB_EC, EC_R_NOT_A_NIST_PRIME}, - #else - {"NOT_A_NIST_PRIME", 16, 135}, - #endif - #ifdef EC_R_NOT_IMPLEMENTED - {"NOT_IMPLEMENTED", ERR_LIB_EC, EC_R_NOT_IMPLEMENTED}, - #else - {"NOT_IMPLEMENTED", 16, 126}, - #endif - #ifdef EC_R_NOT_INITIALIZED - {"NOT_INITIALIZED", ERR_LIB_EC, EC_R_NOT_INITIALIZED}, - #else - {"NOT_INITIALIZED", 16, 111}, - #endif - #ifdef EC_R_NO_PARAMETERS_SET - {"NO_PARAMETERS_SET", ERR_LIB_EC, EC_R_NO_PARAMETERS_SET}, - #else - {"NO_PARAMETERS_SET", 16, 139}, - #endif - #ifdef EC_R_NO_PRIVATE_VALUE - {"NO_PRIVATE_VALUE", ERR_LIB_EC, EC_R_NO_PRIVATE_VALUE}, - #else - {"NO_PRIVATE_VALUE", 16, 154}, - #endif - #ifdef EC_R_OPERATION_NOT_SUPPORTED - {"OPERATION_NOT_SUPPORTED", ERR_LIB_EC, EC_R_OPERATION_NOT_SUPPORTED}, - #else - {"OPERATION_NOT_SUPPORTED", 16, 152}, - #endif - #ifdef EC_R_PASSED_NULL_PARAMETER - {"PASSED_NULL_PARAMETER", ERR_LIB_EC, EC_R_PASSED_NULL_PARAMETER}, - #else - {"PASSED_NULL_PARAMETER", 16, 134}, - #endif - #ifdef EC_R_PEER_KEY_ERROR - {"PEER_KEY_ERROR", ERR_LIB_EC, EC_R_PEER_KEY_ERROR}, - #else - {"PEER_KEY_ERROR", 16, 149}, - #endif - #ifdef EC_R_POINT_ARITHMETIC_FAILURE - {"POINT_ARITHMETIC_FAILURE", ERR_LIB_EC, EC_R_POINT_ARITHMETIC_FAILURE}, - #else - {"POINT_ARITHMETIC_FAILURE", 16, 155}, - #endif - #ifdef EC_R_POINT_AT_INFINITY - {"POINT_AT_INFINITY", ERR_LIB_EC, EC_R_POINT_AT_INFINITY}, - #else - {"POINT_AT_INFINITY", 16, 106}, - #endif - #ifdef EC_R_POINT_COORDINATES_BLIND_FAILURE - {"POINT_COORDINATES_BLIND_FAILURE", ERR_LIB_EC, EC_R_POINT_COORDINATES_BLIND_FAILURE}, - #else - {"POINT_COORDINATES_BLIND_FAILURE", 16, 163}, - #endif - #ifdef EC_R_POINT_IS_NOT_ON_CURVE - {"POINT_IS_NOT_ON_CURVE", ERR_LIB_EC, EC_R_POINT_IS_NOT_ON_CURVE}, - #else - {"POINT_IS_NOT_ON_CURVE", 16, 107}, - #endif - #ifdef EC_R_RANDOM_NUMBER_GENERATION_FAILED - {"RANDOM_NUMBER_GENERATION_FAILED", ERR_LIB_EC, EC_R_RANDOM_NUMBER_GENERATION_FAILED}, - #else - {"RANDOM_NUMBER_GENERATION_FAILED", 16, 158}, - #endif - #ifdef EC_R_SHARED_INFO_ERROR - {"SHARED_INFO_ERROR", ERR_LIB_EC, EC_R_SHARED_INFO_ERROR}, - #else - {"SHARED_INFO_ERROR", 16, 150}, - #endif - #ifdef EC_R_SLOT_FULL - {"SLOT_FULL", ERR_LIB_EC, EC_R_SLOT_FULL}, - #else - {"SLOT_FULL", 16, 108}, - #endif - #ifdef EC_R_TOO_MANY_RETRIES - {"TOO_MANY_RETRIES", ERR_LIB_EC, EC_R_TOO_MANY_RETRIES}, - #else - {"TOO_MANY_RETRIES", 16, 176}, - #endif - #ifdef EC_R_UNDEFINED_GENERATOR - {"UNDEFINED_GENERATOR", ERR_LIB_EC, EC_R_UNDEFINED_GENERATOR}, - #else - {"UNDEFINED_GENERATOR", 16, 113}, - #endif - #ifdef EC_R_UNDEFINED_ORDER - {"UNDEFINED_ORDER", ERR_LIB_EC, EC_R_UNDEFINED_ORDER}, - #else - {"UNDEFINED_ORDER", 16, 128}, - #endif - #ifdef EC_R_UNKNOWN_COFACTOR - {"UNKNOWN_COFACTOR", ERR_LIB_EC, EC_R_UNKNOWN_COFACTOR}, - #else - {"UNKNOWN_COFACTOR", 16, 164}, - #endif - #ifdef EC_R_UNKNOWN_GROUP - {"UNKNOWN_GROUP", ERR_LIB_EC, EC_R_UNKNOWN_GROUP}, - #else - {"UNKNOWN_GROUP", 16, 129}, - #endif - #ifdef EC_R_UNKNOWN_ORDER - {"UNKNOWN_ORDER", ERR_LIB_EC, EC_R_UNKNOWN_ORDER}, - #else - {"UNKNOWN_ORDER", 16, 114}, - #endif - #ifdef EC_R_UNSUPPORTED_FIELD - {"UNSUPPORTED_FIELD", ERR_LIB_EC, EC_R_UNSUPPORTED_FIELD}, - #else - {"UNSUPPORTED_FIELD", 16, 131}, - #endif - #ifdef EC_R_WRONG_CURVE_PARAMETERS - {"WRONG_CURVE_PARAMETERS", ERR_LIB_EC, EC_R_WRONG_CURVE_PARAMETERS}, - #else - {"WRONG_CURVE_PARAMETERS", 16, 145}, - #endif - #ifdef EC_R_WRONG_ORDER - {"WRONG_ORDER", ERR_LIB_EC, EC_R_WRONG_ORDER}, - #else - {"WRONG_ORDER", 16, 130}, - #endif - #ifdef ENGINE_R_ALREADY_LOADED - {"ALREADY_LOADED", ERR_LIB_ENGINE, ENGINE_R_ALREADY_LOADED}, - #else - {"ALREADY_LOADED", 38, 100}, - #endif - #ifdef ENGINE_R_ARGUMENT_IS_NOT_A_NUMBER - {"ARGUMENT_IS_NOT_A_NUMBER", ERR_LIB_ENGINE, ENGINE_R_ARGUMENT_IS_NOT_A_NUMBER}, - #else - {"ARGUMENT_IS_NOT_A_NUMBER", 38, 133}, - #endif - #ifdef ENGINE_R_CMD_NOT_EXECUTABLE - {"CMD_NOT_EXECUTABLE", ERR_LIB_ENGINE, ENGINE_R_CMD_NOT_EXECUTABLE}, - #else - {"CMD_NOT_EXECUTABLE", 38, 134}, - #endif - #ifdef ENGINE_R_COMMAND_TAKES_INPUT - {"COMMAND_TAKES_INPUT", ERR_LIB_ENGINE, ENGINE_R_COMMAND_TAKES_INPUT}, - #else - {"COMMAND_TAKES_INPUT", 38, 135}, - #endif - #ifdef ENGINE_R_COMMAND_TAKES_NO_INPUT - {"COMMAND_TAKES_NO_INPUT", ERR_LIB_ENGINE, ENGINE_R_COMMAND_TAKES_NO_INPUT}, - #else - {"COMMAND_TAKES_NO_INPUT", 38, 136}, - #endif - #ifdef ENGINE_R_CONFLICTING_ENGINE_ID - {"CONFLICTING_ENGINE_ID", ERR_LIB_ENGINE, ENGINE_R_CONFLICTING_ENGINE_ID}, - #else - {"CONFLICTING_ENGINE_ID", 38, 103}, - #endif - #ifdef ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED - {"CTRL_COMMAND_NOT_IMPLEMENTED", ERR_LIB_ENGINE, ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED}, - #else - {"CTRL_COMMAND_NOT_IMPLEMENTED", 38, 119}, - #endif - #ifdef ENGINE_R_DSO_FAILURE - {"DSO_FAILURE", ERR_LIB_ENGINE, ENGINE_R_DSO_FAILURE}, - #else - {"DSO_FAILURE", 38, 104}, - #endif - #ifdef ENGINE_R_DSO_NOT_FOUND - {"DSO_NOT_FOUND", ERR_LIB_ENGINE, ENGINE_R_DSO_NOT_FOUND}, - #else - {"DSO_NOT_FOUND", 38, 132}, - #endif - #ifdef ENGINE_R_ENGINES_SECTION_ERROR - {"ENGINES_SECTION_ERROR", ERR_LIB_ENGINE, ENGINE_R_ENGINES_SECTION_ERROR}, - #else - {"ENGINES_SECTION_ERROR", 38, 148}, - #endif - #ifdef ENGINE_R_ENGINE_CONFIGURATION_ERROR - {"ENGINE_CONFIGURATION_ERROR", ERR_LIB_ENGINE, ENGINE_R_ENGINE_CONFIGURATION_ERROR}, - #else - {"ENGINE_CONFIGURATION_ERROR", 38, 102}, - #endif - #ifdef ENGINE_R_ENGINE_IS_NOT_IN_LIST - {"ENGINE_IS_NOT_IN_LIST", ERR_LIB_ENGINE, ENGINE_R_ENGINE_IS_NOT_IN_LIST}, - #else - {"ENGINE_IS_NOT_IN_LIST", 38, 105}, - #endif - #ifdef ENGINE_R_ENGINE_SECTION_ERROR - {"ENGINE_SECTION_ERROR", ERR_LIB_ENGINE, ENGINE_R_ENGINE_SECTION_ERROR}, - #else - {"ENGINE_SECTION_ERROR", 38, 149}, - #endif - #ifdef ENGINE_R_FAILED_LOADING_PRIVATE_KEY - {"FAILED_LOADING_PRIVATE_KEY", ERR_LIB_ENGINE, ENGINE_R_FAILED_LOADING_PRIVATE_KEY}, - #else - {"FAILED_LOADING_PRIVATE_KEY", 38, 128}, - #endif - #ifdef ENGINE_R_FAILED_LOADING_PUBLIC_KEY - {"FAILED_LOADING_PUBLIC_KEY", ERR_LIB_ENGINE, ENGINE_R_FAILED_LOADING_PUBLIC_KEY}, - #else - {"FAILED_LOADING_PUBLIC_KEY", 38, 129}, - #endif - #ifdef ENGINE_R_FINISH_FAILED - {"FINISH_FAILED", ERR_LIB_ENGINE, ENGINE_R_FINISH_FAILED}, - #else - {"FINISH_FAILED", 38, 106}, - #endif - #ifdef ENGINE_R_ID_OR_NAME_MISSING - {"ID_OR_NAME_MISSING", ERR_LIB_ENGINE, ENGINE_R_ID_OR_NAME_MISSING}, - #else - {"ID_OR_NAME_MISSING", 38, 108}, - #endif - #ifdef ENGINE_R_INIT_FAILED - {"INIT_FAILED", ERR_LIB_ENGINE, ENGINE_R_INIT_FAILED}, - #else - {"INIT_FAILED", 38, 109}, - #endif - #ifdef ENGINE_R_INTERNAL_LIST_ERROR - {"INTERNAL_LIST_ERROR", ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR}, - #else - {"INTERNAL_LIST_ERROR", 38, 110}, - #endif - #ifdef ENGINE_R_INVALID_ARGUMENT - {"INVALID_ARGUMENT", ERR_LIB_ENGINE, ENGINE_R_INVALID_ARGUMENT}, - #else - {"INVALID_ARGUMENT", 38, 143}, - #endif - #ifdef ENGINE_R_INVALID_CMD_NAME - {"INVALID_CMD_NAME", ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NAME}, - #else - {"INVALID_CMD_NAME", 38, 137}, - #endif - #ifdef ENGINE_R_INVALID_CMD_NUMBER - {"INVALID_CMD_NUMBER", ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NUMBER}, - #else - {"INVALID_CMD_NUMBER", 38, 138}, - #endif - #ifdef ENGINE_R_INVALID_INIT_VALUE - {"INVALID_INIT_VALUE", ERR_LIB_ENGINE, ENGINE_R_INVALID_INIT_VALUE}, - #else - {"INVALID_INIT_VALUE", 38, 151}, - #endif - #ifdef ENGINE_R_INVALID_STRING - {"INVALID_STRING", ERR_LIB_ENGINE, ENGINE_R_INVALID_STRING}, - #else - {"INVALID_STRING", 38, 150}, - #endif - #ifdef ENGINE_R_NOT_INITIALISED - {"NOT_INITIALISED", ERR_LIB_ENGINE, ENGINE_R_NOT_INITIALISED}, - #else - {"NOT_INITIALISED", 38, 117}, - #endif - #ifdef ENGINE_R_NOT_LOADED - {"NOT_LOADED", ERR_LIB_ENGINE, ENGINE_R_NOT_LOADED}, - #else - {"NOT_LOADED", 38, 112}, - #endif - #ifdef ENGINE_R_NO_CONTROL_FUNCTION - {"NO_CONTROL_FUNCTION", ERR_LIB_ENGINE, ENGINE_R_NO_CONTROL_FUNCTION}, - #else - {"NO_CONTROL_FUNCTION", 38, 120}, - #endif - #ifdef ENGINE_R_NO_INDEX - {"NO_INDEX", ERR_LIB_ENGINE, ENGINE_R_NO_INDEX}, - #else - {"NO_INDEX", 38, 144}, - #endif - #ifdef ENGINE_R_NO_LOAD_FUNCTION - {"NO_LOAD_FUNCTION", ERR_LIB_ENGINE, ENGINE_R_NO_LOAD_FUNCTION}, - #else - {"NO_LOAD_FUNCTION", 38, 125}, - #endif - #ifdef ENGINE_R_NO_REFERENCE - {"NO_REFERENCE", ERR_LIB_ENGINE, ENGINE_R_NO_REFERENCE}, - #else - {"NO_REFERENCE", 38, 130}, - #endif - #ifdef ENGINE_R_NO_SUCH_ENGINE - {"NO_SUCH_ENGINE", ERR_LIB_ENGINE, ENGINE_R_NO_SUCH_ENGINE}, - #else - {"NO_SUCH_ENGINE", 38, 116}, - #endif - #ifdef ENGINE_R_UNIMPLEMENTED_CIPHER - {"UNIMPLEMENTED_CIPHER", ERR_LIB_ENGINE, ENGINE_R_UNIMPLEMENTED_CIPHER}, - #else - {"UNIMPLEMENTED_CIPHER", 38, 146}, - #endif - #ifdef ENGINE_R_UNIMPLEMENTED_DIGEST - {"UNIMPLEMENTED_DIGEST", ERR_LIB_ENGINE, ENGINE_R_UNIMPLEMENTED_DIGEST}, - #else - {"UNIMPLEMENTED_DIGEST", 38, 147}, - #endif - #ifdef ENGINE_R_UNIMPLEMENTED_PUBLIC_KEY_METHOD - {"UNIMPLEMENTED_PUBLIC_KEY_METHOD", ERR_LIB_ENGINE, ENGINE_R_UNIMPLEMENTED_PUBLIC_KEY_METHOD}, - #else - {"UNIMPLEMENTED_PUBLIC_KEY_METHOD", 38, 101}, - #endif - #ifdef ENGINE_R_VERSION_INCOMPATIBILITY - {"VERSION_INCOMPATIBILITY", ERR_LIB_ENGINE, ENGINE_R_VERSION_INCOMPATIBILITY}, - #else - {"VERSION_INCOMPATIBILITY", 38, 145}, - #endif - #ifdef ESS_R_EMPTY_ESS_CERT_ID_LIST - {"EMPTY_ESS_CERT_ID_LIST", ERR_LIB_ESS, ESS_R_EMPTY_ESS_CERT_ID_LIST}, - #else - {"EMPTY_ESS_CERT_ID_LIST", 54, 107}, - #endif - #ifdef ESS_R_ESS_CERT_DIGEST_ERROR - {"ESS_CERT_DIGEST_ERROR", ERR_LIB_ESS, ESS_R_ESS_CERT_DIGEST_ERROR}, - #else - {"ESS_CERT_DIGEST_ERROR", 54, 103}, - #endif - #ifdef ESS_R_ESS_CERT_ID_NOT_FOUND - {"ESS_CERT_ID_NOT_FOUND", ERR_LIB_ESS, ESS_R_ESS_CERT_ID_NOT_FOUND}, - #else - {"ESS_CERT_ID_NOT_FOUND", 54, 104}, - #endif - #ifdef ESS_R_ESS_CERT_ID_WRONG_ORDER - {"ESS_CERT_ID_WRONG_ORDER", ERR_LIB_ESS, ESS_R_ESS_CERT_ID_WRONG_ORDER}, - #else - {"ESS_CERT_ID_WRONG_ORDER", 54, 105}, - #endif - #ifdef ESS_R_ESS_DIGEST_ALG_UNKNOWN - {"ESS_DIGEST_ALG_UNKNOWN", ERR_LIB_ESS, ESS_R_ESS_DIGEST_ALG_UNKNOWN}, - #else - {"ESS_DIGEST_ALG_UNKNOWN", 54, 106}, - #endif - #ifdef ESS_R_ESS_SIGNING_CERTIFICATE_ERROR - {"ESS_SIGNING_CERTIFICATE_ERROR", ERR_LIB_ESS, ESS_R_ESS_SIGNING_CERTIFICATE_ERROR}, - #else - {"ESS_SIGNING_CERTIFICATE_ERROR", 54, 102}, - #endif - #ifdef ESS_R_ESS_SIGNING_CERT_ADD_ERROR - {"ESS_SIGNING_CERT_ADD_ERROR", ERR_LIB_ESS, ESS_R_ESS_SIGNING_CERT_ADD_ERROR}, - #else - {"ESS_SIGNING_CERT_ADD_ERROR", 54, 100}, - #endif - #ifdef ESS_R_ESS_SIGNING_CERT_V2_ADD_ERROR - {"ESS_SIGNING_CERT_V2_ADD_ERROR", ERR_LIB_ESS, ESS_R_ESS_SIGNING_CERT_V2_ADD_ERROR}, - #else - {"ESS_SIGNING_CERT_V2_ADD_ERROR", 54, 101}, - #endif - #ifdef ESS_R_MISSING_SIGNING_CERTIFICATE_ATTRIBUTE - {"MISSING_SIGNING_CERTIFICATE_ATTRIBUTE", ERR_LIB_ESS, ESS_R_MISSING_SIGNING_CERTIFICATE_ATTRIBUTE}, - #else - {"MISSING_SIGNING_CERTIFICATE_ATTRIBUTE", 54, 108}, - #endif - #ifdef EVP_R_AES_KEY_SETUP_FAILED - {"AES_KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_AES_KEY_SETUP_FAILED}, - #else - {"AES_KEY_SETUP_FAILED", 6, 143}, - #endif - #ifdef EVP_R_ARIA_KEY_SETUP_FAILED - {"ARIA_KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_ARIA_KEY_SETUP_FAILED}, - #else - {"ARIA_KEY_SETUP_FAILED", 6, 176}, - #endif - #ifdef EVP_R_BAD_ALGORITHM_NAME - {"BAD_ALGORITHM_NAME", ERR_LIB_EVP, EVP_R_BAD_ALGORITHM_NAME}, - #else - {"BAD_ALGORITHM_NAME", 6, 200}, - #endif - #ifdef EVP_R_BAD_DECRYPT - {"BAD_DECRYPT", ERR_LIB_EVP, EVP_R_BAD_DECRYPT}, - #else - {"BAD_DECRYPT", 6, 100}, - #endif - #ifdef EVP_R_BAD_KEY_LENGTH - {"BAD_KEY_LENGTH", ERR_LIB_EVP, EVP_R_BAD_KEY_LENGTH}, - #else - {"BAD_KEY_LENGTH", 6, 195}, - #endif - #ifdef EVP_R_BUFFER_TOO_SMALL - {"BUFFER_TOO_SMALL", ERR_LIB_EVP, EVP_R_BUFFER_TOO_SMALL}, - #else - {"BUFFER_TOO_SMALL", 6, 155}, - #endif - #ifdef EVP_R_CACHE_CONSTANTS_FAILED - {"CACHE_CONSTANTS_FAILED", ERR_LIB_EVP, EVP_R_CACHE_CONSTANTS_FAILED}, - #else - {"CACHE_CONSTANTS_FAILED", 6, 225}, - #endif - #ifdef EVP_R_CAMELLIA_KEY_SETUP_FAILED - {"CAMELLIA_KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_CAMELLIA_KEY_SETUP_FAILED}, - #else - {"CAMELLIA_KEY_SETUP_FAILED", 6, 157}, - #endif - #ifdef EVP_R_CANNOT_GET_PARAMETERS - {"CANNOT_GET_PARAMETERS", ERR_LIB_EVP, EVP_R_CANNOT_GET_PARAMETERS}, - #else - {"CANNOT_GET_PARAMETERS", 6, 197}, - #endif - #ifdef EVP_R_CANNOT_SET_PARAMETERS - {"CANNOT_SET_PARAMETERS", ERR_LIB_EVP, EVP_R_CANNOT_SET_PARAMETERS}, - #else - {"CANNOT_SET_PARAMETERS", 6, 198}, - #endif - #ifdef EVP_R_CIPHER_NOT_GCM_MODE - {"CIPHER_NOT_GCM_MODE", ERR_LIB_EVP, EVP_R_CIPHER_NOT_GCM_MODE}, - #else - {"CIPHER_NOT_GCM_MODE", 6, 184}, - #endif - #ifdef EVP_R_CIPHER_PARAMETER_ERROR - {"CIPHER_PARAMETER_ERROR", ERR_LIB_EVP, EVP_R_CIPHER_PARAMETER_ERROR}, - #else - {"CIPHER_PARAMETER_ERROR", 6, 122}, - #endif - #ifdef EVP_R_COMMAND_NOT_SUPPORTED - {"COMMAND_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED}, - #else - {"COMMAND_NOT_SUPPORTED", 6, 147}, - #endif - #ifdef EVP_R_CONFLICTING_ALGORITHM_NAME - {"CONFLICTING_ALGORITHM_NAME", ERR_LIB_EVP, EVP_R_CONFLICTING_ALGORITHM_NAME}, - #else - {"CONFLICTING_ALGORITHM_NAME", 6, 201}, - #endif - #ifdef EVP_R_COPY_ERROR - {"COPY_ERROR", ERR_LIB_EVP, EVP_R_COPY_ERROR}, - #else - {"COPY_ERROR", 6, 173}, - #endif - #ifdef EVP_R_CTRL_NOT_IMPLEMENTED - {"CTRL_NOT_IMPLEMENTED", ERR_LIB_EVP, EVP_R_CTRL_NOT_IMPLEMENTED}, - #else - {"CTRL_NOT_IMPLEMENTED", 6, 132}, - #endif - #ifdef EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED - {"CTRL_OPERATION_NOT_IMPLEMENTED", ERR_LIB_EVP, EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED}, - #else - {"CTRL_OPERATION_NOT_IMPLEMENTED", 6, 133}, - #endif - #ifdef EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH - {"DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH", ERR_LIB_EVP, EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH}, - #else - {"DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH", 6, 138}, - #endif - #ifdef EVP_R_DECODE_ERROR - {"DECODE_ERROR", ERR_LIB_EVP, EVP_R_DECODE_ERROR}, - #else - {"DECODE_ERROR", 6, 114}, - #endif - #ifdef EVP_R_DEFAULT_QUERY_PARSE_ERROR - {"DEFAULT_QUERY_PARSE_ERROR", ERR_LIB_EVP, EVP_R_DEFAULT_QUERY_PARSE_ERROR}, - #else - {"DEFAULT_QUERY_PARSE_ERROR", 6, 210}, - #endif - #ifdef EVP_R_DIFFERENT_KEY_TYPES - {"DIFFERENT_KEY_TYPES", ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES}, - #else - {"DIFFERENT_KEY_TYPES", 6, 101}, - #endif - #ifdef EVP_R_DIFFERENT_PARAMETERS - {"DIFFERENT_PARAMETERS", ERR_LIB_EVP, EVP_R_DIFFERENT_PARAMETERS}, - #else - {"DIFFERENT_PARAMETERS", 6, 153}, - #endif - #ifdef EVP_R_ERROR_LOADING_SECTION - {"ERROR_LOADING_SECTION", ERR_LIB_EVP, EVP_R_ERROR_LOADING_SECTION}, - #else - {"ERROR_LOADING_SECTION", 6, 165}, - #endif - #ifdef EVP_R_EXPECTING_AN_HMAC_KEY - {"EXPECTING_AN_HMAC_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_AN_HMAC_KEY}, - #else - {"EXPECTING_AN_HMAC_KEY", 6, 174}, - #endif - #ifdef EVP_R_EXPECTING_AN_RSA_KEY - {"EXPECTING_AN_RSA_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_AN_RSA_KEY}, - #else - {"EXPECTING_AN_RSA_KEY", 6, 127}, - #endif - #ifdef EVP_R_EXPECTING_A_DH_KEY - {"EXPECTING_A_DH_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_DH_KEY}, - #else - {"EXPECTING_A_DH_KEY", 6, 128}, - #endif - #ifdef EVP_R_EXPECTING_A_DSA_KEY - {"EXPECTING_A_DSA_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_DSA_KEY}, - #else - {"EXPECTING_A_DSA_KEY", 6, 129}, - #endif - #ifdef EVP_R_EXPECTING_A_ECX_KEY - {"EXPECTING_A_ECX_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_ECX_KEY}, - #else - {"EXPECTING_A_ECX_KEY", 6, 219}, - #endif - #ifdef EVP_R_EXPECTING_A_EC_KEY - {"EXPECTING_A_EC_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_EC_KEY}, - #else - {"EXPECTING_A_EC_KEY", 6, 142}, - #endif - #ifdef EVP_R_EXPECTING_A_POLY1305_KEY - {"EXPECTING_A_POLY1305_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_POLY1305_KEY}, - #else - {"EXPECTING_A_POLY1305_KEY", 6, 164}, - #endif - #ifdef EVP_R_EXPECTING_A_SIPHASH_KEY - {"EXPECTING_A_SIPHASH_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_SIPHASH_KEY}, - #else - {"EXPECTING_A_SIPHASH_KEY", 6, 175}, - #endif - #ifdef EVP_R_FINAL_ERROR - {"FINAL_ERROR", ERR_LIB_EVP, EVP_R_FINAL_ERROR}, - #else - {"FINAL_ERROR", 6, 188}, - #endif - #ifdef EVP_R_GENERATE_ERROR - {"GENERATE_ERROR", ERR_LIB_EVP, EVP_R_GENERATE_ERROR}, - #else - {"GENERATE_ERROR", 6, 214}, - #endif - #ifdef EVP_R_GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED - {"GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED}, - #else - {"GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED", 6, 229}, - #endif - #ifdef EVP_R_GET_RAW_KEY_FAILED - {"GET_RAW_KEY_FAILED", ERR_LIB_EVP, EVP_R_GET_RAW_KEY_FAILED}, - #else - {"GET_RAW_KEY_FAILED", 6, 182}, - #endif - #ifdef EVP_R_ILLEGAL_SCRYPT_PARAMETERS - {"ILLEGAL_SCRYPT_PARAMETERS", ERR_LIB_EVP, EVP_R_ILLEGAL_SCRYPT_PARAMETERS}, - #else - {"ILLEGAL_SCRYPT_PARAMETERS", 6, 171}, - #endif - #ifdef EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS - {"INACCESSIBLE_DOMAIN_PARAMETERS", ERR_LIB_EVP, EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS}, - #else - {"INACCESSIBLE_DOMAIN_PARAMETERS", 6, 204}, - #endif - #ifdef EVP_R_INACCESSIBLE_KEY - {"INACCESSIBLE_KEY", ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY}, - #else - {"INACCESSIBLE_KEY", 6, 203}, - #endif - #ifdef EVP_R_INITIALIZATION_ERROR - {"INITIALIZATION_ERROR", ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR}, - #else - {"INITIALIZATION_ERROR", 6, 134}, - #endif - #ifdef EVP_R_INPUT_NOT_INITIALIZED - {"INPUT_NOT_INITIALIZED", ERR_LIB_EVP, EVP_R_INPUT_NOT_INITIALIZED}, - #else - {"INPUT_NOT_INITIALIZED", 6, 111}, - #endif - #ifdef EVP_R_INVALID_CUSTOM_LENGTH - {"INVALID_CUSTOM_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_CUSTOM_LENGTH}, - #else - {"INVALID_CUSTOM_LENGTH", 6, 185}, - #endif - #ifdef EVP_R_INVALID_DIGEST - {"INVALID_DIGEST", ERR_LIB_EVP, EVP_R_INVALID_DIGEST}, - #else - {"INVALID_DIGEST", 6, 152}, - #endif - #ifdef EVP_R_INVALID_IV_LENGTH - {"INVALID_IV_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_IV_LENGTH}, - #else - {"INVALID_IV_LENGTH", 6, 194}, - #endif - #ifdef EVP_R_INVALID_KEY - {"INVALID_KEY", ERR_LIB_EVP, EVP_R_INVALID_KEY}, - #else - {"INVALID_KEY", 6, 163}, - #endif - #ifdef EVP_R_INVALID_KEY_LENGTH - {"INVALID_KEY_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_KEY_LENGTH}, - #else - {"INVALID_KEY_LENGTH", 6, 130}, - #endif - #ifdef EVP_R_INVALID_LENGTH - {"INVALID_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_LENGTH}, - #else - {"INVALID_LENGTH", 6, 221}, - #endif - #ifdef EVP_R_INVALID_NULL_ALGORITHM - {"INVALID_NULL_ALGORITHM", ERR_LIB_EVP, EVP_R_INVALID_NULL_ALGORITHM}, - #else - {"INVALID_NULL_ALGORITHM", 6, 218}, - #endif - #ifdef EVP_R_INVALID_OPERATION - {"INVALID_OPERATION", ERR_LIB_EVP, EVP_R_INVALID_OPERATION}, - #else - {"INVALID_OPERATION", 6, 148}, - #endif - #ifdef EVP_R_INVALID_PROVIDER_FUNCTIONS - {"INVALID_PROVIDER_FUNCTIONS", ERR_LIB_EVP, EVP_R_INVALID_PROVIDER_FUNCTIONS}, - #else - {"INVALID_PROVIDER_FUNCTIONS", 6, 193}, - #endif - #ifdef EVP_R_INVALID_SALT_LENGTH - {"INVALID_SALT_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_SALT_LENGTH}, - #else - {"INVALID_SALT_LENGTH", 6, 186}, - #endif - #ifdef EVP_R_INVALID_SECRET_LENGTH - {"INVALID_SECRET_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_SECRET_LENGTH}, - #else - {"INVALID_SECRET_LENGTH", 6, 223}, - #endif - #ifdef EVP_R_INVALID_SEED_LENGTH - {"INVALID_SEED_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_SEED_LENGTH}, - #else - {"INVALID_SEED_LENGTH", 6, 220}, - #endif - #ifdef EVP_R_INVALID_VALUE - {"INVALID_VALUE", ERR_LIB_EVP, EVP_R_INVALID_VALUE}, - #else - {"INVALID_VALUE", 6, 222}, - #endif - #ifdef EVP_R_KEYMGMT_EXPORT_FAILURE - {"KEYMGMT_EXPORT_FAILURE", ERR_LIB_EVP, EVP_R_KEYMGMT_EXPORT_FAILURE}, - #else - {"KEYMGMT_EXPORT_FAILURE", 6, 205}, - #endif - #ifdef EVP_R_KEY_SETUP_FAILED - {"KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_KEY_SETUP_FAILED}, - #else - {"KEY_SETUP_FAILED", 6, 180}, - #endif - #ifdef EVP_R_LOCKING_NOT_SUPPORTED - {"LOCKING_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_LOCKING_NOT_SUPPORTED}, - #else - {"LOCKING_NOT_SUPPORTED", 6, 213}, - #endif - #ifdef EVP_R_MEMORY_LIMIT_EXCEEDED - {"MEMORY_LIMIT_EXCEEDED", ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED}, - #else - {"MEMORY_LIMIT_EXCEEDED", 6, 172}, - #endif - #ifdef EVP_R_MESSAGE_DIGEST_IS_NULL - {"MESSAGE_DIGEST_IS_NULL", ERR_LIB_EVP, EVP_R_MESSAGE_DIGEST_IS_NULL}, - #else - {"MESSAGE_DIGEST_IS_NULL", 6, 159}, - #endif - #ifdef EVP_R_METHOD_NOT_SUPPORTED - {"METHOD_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_METHOD_NOT_SUPPORTED}, - #else - {"METHOD_NOT_SUPPORTED", 6, 144}, - #endif - #ifdef EVP_R_MISSING_PARAMETERS - {"MISSING_PARAMETERS", ERR_LIB_EVP, EVP_R_MISSING_PARAMETERS}, - #else - {"MISSING_PARAMETERS", 6, 103}, - #endif - #ifdef EVP_R_NOT_ABLE_TO_COPY_CTX - {"NOT_ABLE_TO_COPY_CTX", ERR_LIB_EVP, EVP_R_NOT_ABLE_TO_COPY_CTX}, - #else - {"NOT_ABLE_TO_COPY_CTX", 6, 190}, - #endif - #ifdef EVP_R_NOT_XOF_OR_INVALID_LENGTH - {"NOT_XOF_OR_INVALID_LENGTH", ERR_LIB_EVP, EVP_R_NOT_XOF_OR_INVALID_LENGTH}, - #else - {"NOT_XOF_OR_INVALID_LENGTH", 6, 178}, - #endif - #ifdef EVP_R_NO_CIPHER_SET - {"NO_CIPHER_SET", ERR_LIB_EVP, EVP_R_NO_CIPHER_SET}, - #else - {"NO_CIPHER_SET", 6, 131}, - #endif - #ifdef EVP_R_NO_DEFAULT_DIGEST - {"NO_DEFAULT_DIGEST", ERR_LIB_EVP, EVP_R_NO_DEFAULT_DIGEST}, - #else - {"NO_DEFAULT_DIGEST", 6, 158}, - #endif - #ifdef EVP_R_NO_DIGEST_SET - {"NO_DIGEST_SET", ERR_LIB_EVP, EVP_R_NO_DIGEST_SET}, - #else - {"NO_DIGEST_SET", 6, 139}, - #endif - #ifdef EVP_R_NO_IMPORT_FUNCTION - {"NO_IMPORT_FUNCTION", ERR_LIB_EVP, EVP_R_NO_IMPORT_FUNCTION}, - #else - {"NO_IMPORT_FUNCTION", 6, 206}, - #endif - #ifdef EVP_R_NO_KEYMGMT_AVAILABLE - {"NO_KEYMGMT_AVAILABLE", ERR_LIB_EVP, EVP_R_NO_KEYMGMT_AVAILABLE}, - #else - {"NO_KEYMGMT_AVAILABLE", 6, 199}, - #endif - #ifdef EVP_R_NO_KEYMGMT_PRESENT - {"NO_KEYMGMT_PRESENT", ERR_LIB_EVP, EVP_R_NO_KEYMGMT_PRESENT}, - #else - {"NO_KEYMGMT_PRESENT", 6, 196}, - #endif - #ifdef EVP_R_NO_KEY_SET - {"NO_KEY_SET", ERR_LIB_EVP, EVP_R_NO_KEY_SET}, - #else - {"NO_KEY_SET", 6, 154}, - #endif - #ifdef EVP_R_NO_OPERATION_SET - {"NO_OPERATION_SET", ERR_LIB_EVP, EVP_R_NO_OPERATION_SET}, - #else - {"NO_OPERATION_SET", 6, 149}, - #endif - #ifdef EVP_R_NULL_MAC_PKEY_CTX - {"NULL_MAC_PKEY_CTX", ERR_LIB_EVP, EVP_R_NULL_MAC_PKEY_CTX}, - #else - {"NULL_MAC_PKEY_CTX", 6, 208}, - #endif - #ifdef EVP_R_ONLY_ONESHOT_SUPPORTED - {"ONLY_ONESHOT_SUPPORTED", ERR_LIB_EVP, EVP_R_ONLY_ONESHOT_SUPPORTED}, - #else - {"ONLY_ONESHOT_SUPPORTED", 6, 177}, - #endif - #ifdef EVP_R_OPERATION_NOT_INITIALIZED - {"OPERATION_NOT_INITIALIZED", ERR_LIB_EVP, EVP_R_OPERATION_NOT_INITIALIZED}, - #else - {"OPERATION_NOT_INITIALIZED", 6, 151}, - #endif - #ifdef EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE - {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE}, - #else - {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", 6, 150}, - #endif - #ifdef EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE - {"OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE", ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE}, - #else - {"OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE", 6, 226}, - #endif - #ifdef EVP_R_OUTPUT_WOULD_OVERFLOW - {"OUTPUT_WOULD_OVERFLOW", ERR_LIB_EVP, EVP_R_OUTPUT_WOULD_OVERFLOW}, - #else - {"OUTPUT_WOULD_OVERFLOW", 6, 202}, - #endif - #ifdef EVP_R_PARAMETER_TOO_LARGE - {"PARAMETER_TOO_LARGE", ERR_LIB_EVP, EVP_R_PARAMETER_TOO_LARGE}, - #else - {"PARAMETER_TOO_LARGE", 6, 187}, - #endif - #ifdef EVP_R_PARTIALLY_OVERLAPPING - {"PARTIALLY_OVERLAPPING", ERR_LIB_EVP, EVP_R_PARTIALLY_OVERLAPPING}, - #else - {"PARTIALLY_OVERLAPPING", 6, 162}, - #endif - #ifdef EVP_R_PBKDF2_ERROR - {"PBKDF2_ERROR", ERR_LIB_EVP, EVP_R_PBKDF2_ERROR}, - #else - {"PBKDF2_ERROR", 6, 181}, - #endif - #ifdef EVP_R_PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED - {"PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED", ERR_LIB_EVP, EVP_R_PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED}, - #else - {"PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED", 6, 179}, - #endif - #ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR - {"PRIVATE_KEY_DECODE_ERROR", ERR_LIB_EVP, EVP_R_PRIVATE_KEY_DECODE_ERROR}, - #else - {"PRIVATE_KEY_DECODE_ERROR", 6, 145}, - #endif - #ifdef EVP_R_PRIVATE_KEY_ENCODE_ERROR - {"PRIVATE_KEY_ENCODE_ERROR", ERR_LIB_EVP, EVP_R_PRIVATE_KEY_ENCODE_ERROR}, - #else - {"PRIVATE_KEY_ENCODE_ERROR", 6, 146}, - #endif - #ifdef EVP_R_PUBLIC_KEY_NOT_RSA - {"PUBLIC_KEY_NOT_RSA", ERR_LIB_EVP, EVP_R_PUBLIC_KEY_NOT_RSA}, - #else - {"PUBLIC_KEY_NOT_RSA", 6, 106}, - #endif - #ifdef EVP_R_SETTING_XOF_FAILED - {"SETTING_XOF_FAILED", ERR_LIB_EVP, EVP_R_SETTING_XOF_FAILED}, - #else - {"SETTING_XOF_FAILED", 6, 227}, - #endif - #ifdef EVP_R_SET_DEFAULT_PROPERTY_FAILURE - {"SET_DEFAULT_PROPERTY_FAILURE", ERR_LIB_EVP, EVP_R_SET_DEFAULT_PROPERTY_FAILURE}, - #else - {"SET_DEFAULT_PROPERTY_FAILURE", 6, 209}, - #endif - #ifdef EVP_R_SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE - {"SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE", ERR_LIB_EVP, EVP_R_SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE}, - #else - {"SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE", 6, 228}, - #endif - #ifdef EVP_R_TOO_MANY_RECORDS - {"TOO_MANY_RECORDS", ERR_LIB_EVP, EVP_R_TOO_MANY_RECORDS}, - #else - {"TOO_MANY_RECORDS", 6, 183}, - #endif - #ifdef EVP_R_UNABLE_TO_ENABLE_LOCKING - {"UNABLE_TO_ENABLE_LOCKING", ERR_LIB_EVP, EVP_R_UNABLE_TO_ENABLE_LOCKING}, - #else - {"UNABLE_TO_ENABLE_LOCKING", 6, 212}, - #endif - #ifdef EVP_R_UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE - {"UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE", ERR_LIB_EVP, EVP_R_UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE}, - #else - {"UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE", 6, 215}, - #endif - #ifdef EVP_R_UNABLE_TO_GET_RANDOM_STRENGTH - {"UNABLE_TO_GET_RANDOM_STRENGTH", ERR_LIB_EVP, EVP_R_UNABLE_TO_GET_RANDOM_STRENGTH}, - #else - {"UNABLE_TO_GET_RANDOM_STRENGTH", 6, 216}, - #endif - #ifdef EVP_R_UNABLE_TO_LOCK_CONTEXT - {"UNABLE_TO_LOCK_CONTEXT", ERR_LIB_EVP, EVP_R_UNABLE_TO_LOCK_CONTEXT}, - #else - {"UNABLE_TO_LOCK_CONTEXT", 6, 211}, - #endif - #ifdef EVP_R_UNABLE_TO_SET_CALLBACKS - {"UNABLE_TO_SET_CALLBACKS", ERR_LIB_EVP, EVP_R_UNABLE_TO_SET_CALLBACKS}, - #else - {"UNABLE_TO_SET_CALLBACKS", 6, 217}, - #endif - #ifdef EVP_R_UNKNOWN_BITS - {"UNKNOWN_BITS", ERR_LIB_EVP, EVP_R_UNKNOWN_BITS}, - #else - {"UNKNOWN_BITS", 6, 166}, - #endif - #ifdef EVP_R_UNKNOWN_CIPHER - {"UNKNOWN_CIPHER", ERR_LIB_EVP, EVP_R_UNKNOWN_CIPHER}, - #else - {"UNKNOWN_CIPHER", 6, 160}, - #endif - #ifdef EVP_R_UNKNOWN_DIGEST - {"UNKNOWN_DIGEST", ERR_LIB_EVP, EVP_R_UNKNOWN_DIGEST}, - #else - {"UNKNOWN_DIGEST", 6, 161}, - #endif - #ifdef EVP_R_UNKNOWN_KEY_TYPE - {"UNKNOWN_KEY_TYPE", ERR_LIB_EVP, EVP_R_UNKNOWN_KEY_TYPE}, - #else - {"UNKNOWN_KEY_TYPE", 6, 207}, - #endif - #ifdef EVP_R_UNKNOWN_MAX_SIZE - {"UNKNOWN_MAX_SIZE", ERR_LIB_EVP, EVP_R_UNKNOWN_MAX_SIZE}, - #else - {"UNKNOWN_MAX_SIZE", 6, 167}, - #endif - #ifdef EVP_R_UNKNOWN_OPTION - {"UNKNOWN_OPTION", ERR_LIB_EVP, EVP_R_UNKNOWN_OPTION}, - #else - {"UNKNOWN_OPTION", 6, 169}, - #endif - #ifdef EVP_R_UNKNOWN_PBE_ALGORITHM - {"UNKNOWN_PBE_ALGORITHM", ERR_LIB_EVP, EVP_R_UNKNOWN_PBE_ALGORITHM}, - #else - {"UNKNOWN_PBE_ALGORITHM", 6, 121}, - #endif - #ifdef EVP_R_UNKNOWN_SECURITY_BITS - {"UNKNOWN_SECURITY_BITS", ERR_LIB_EVP, EVP_R_UNKNOWN_SECURITY_BITS}, - #else - {"UNKNOWN_SECURITY_BITS", 6, 168}, - #endif - #ifdef EVP_R_UNSUPPORTED_ALGORITHM - {"UNSUPPORTED_ALGORITHM", ERR_LIB_EVP, EVP_R_UNSUPPORTED_ALGORITHM}, - #else - {"UNSUPPORTED_ALGORITHM", 6, 156}, - #endif - #ifdef EVP_R_UNSUPPORTED_CIPHER - {"UNSUPPORTED_CIPHER", ERR_LIB_EVP, EVP_R_UNSUPPORTED_CIPHER}, - #else - {"UNSUPPORTED_CIPHER", 6, 107}, - #endif - #ifdef EVP_R_UNSUPPORTED_KEYLENGTH - {"UNSUPPORTED_KEYLENGTH", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEYLENGTH}, - #else - {"UNSUPPORTED_KEYLENGTH", 6, 123}, - #endif - #ifdef EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION - {"UNSUPPORTED_KEY_DERIVATION_FUNCTION", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION}, - #else - {"UNSUPPORTED_KEY_DERIVATION_FUNCTION", 6, 124}, - #endif - #ifdef EVP_R_UNSUPPORTED_KEY_SIZE - {"UNSUPPORTED_KEY_SIZE", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_SIZE}, - #else - {"UNSUPPORTED_KEY_SIZE", 6, 108}, - #endif - #ifdef EVP_R_UNSUPPORTED_KEY_TYPE - {"UNSUPPORTED_KEY_TYPE", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_TYPE}, - #else - {"UNSUPPORTED_KEY_TYPE", 6, 224}, - #endif - #ifdef EVP_R_UNSUPPORTED_NUMBER_OF_ROUNDS - {"UNSUPPORTED_NUMBER_OF_ROUNDS", ERR_LIB_EVP, EVP_R_UNSUPPORTED_NUMBER_OF_ROUNDS}, - #else - {"UNSUPPORTED_NUMBER_OF_ROUNDS", 6, 135}, - #endif - #ifdef EVP_R_UNSUPPORTED_PRF - {"UNSUPPORTED_PRF", ERR_LIB_EVP, EVP_R_UNSUPPORTED_PRF}, - #else - {"UNSUPPORTED_PRF", 6, 125}, - #endif - #ifdef EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM - {"UNSUPPORTED_PRIVATE_KEY_ALGORITHM", ERR_LIB_EVP, EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM}, - #else - {"UNSUPPORTED_PRIVATE_KEY_ALGORITHM", 6, 118}, - #endif - #ifdef EVP_R_UNSUPPORTED_SALT_TYPE - {"UNSUPPORTED_SALT_TYPE", ERR_LIB_EVP, EVP_R_UNSUPPORTED_SALT_TYPE}, - #else - {"UNSUPPORTED_SALT_TYPE", 6, 126}, - #endif - #ifdef EVP_R_UPDATE_ERROR - {"UPDATE_ERROR", ERR_LIB_EVP, EVP_R_UPDATE_ERROR}, - #else - {"UPDATE_ERROR", 6, 189}, - #endif - #ifdef EVP_R_WRAP_MODE_NOT_ALLOWED - {"WRAP_MODE_NOT_ALLOWED", ERR_LIB_EVP, EVP_R_WRAP_MODE_NOT_ALLOWED}, - #else - {"WRAP_MODE_NOT_ALLOWED", 6, 170}, - #endif - #ifdef EVP_R_WRONG_FINAL_BLOCK_LENGTH - {"WRONG_FINAL_BLOCK_LENGTH", ERR_LIB_EVP, EVP_R_WRONG_FINAL_BLOCK_LENGTH}, - #else - {"WRONG_FINAL_BLOCK_LENGTH", 6, 109}, - #endif - #ifdef EVP_R_XTS_DATA_UNIT_IS_TOO_LARGE - {"XTS_DATA_UNIT_IS_TOO_LARGE", ERR_LIB_EVP, EVP_R_XTS_DATA_UNIT_IS_TOO_LARGE}, - #else - {"XTS_DATA_UNIT_IS_TOO_LARGE", 6, 191}, - #endif - #ifdef EVP_R_XTS_DUPLICATED_KEYS - {"XTS_DUPLICATED_KEYS", ERR_LIB_EVP, EVP_R_XTS_DUPLICATED_KEYS}, - #else - {"XTS_DUPLICATED_KEYS", 6, 192}, - #endif - #ifdef HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN - {"ASN1_LEN_EXCEEDS_MAX_RESP_LEN", ERR_LIB_HTTP, HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN}, - #else - {"ASN1_LEN_EXCEEDS_MAX_RESP_LEN", 61, 108}, - #endif - #ifdef HTTP_R_CONNECT_FAILURE - {"CONNECT_FAILURE", ERR_LIB_HTTP, HTTP_R_CONNECT_FAILURE}, - #else - {"CONNECT_FAILURE", 61, 100}, - #endif - #ifdef HTTP_R_ERROR_PARSING_ASN1_LENGTH - {"ERROR_PARSING_ASN1_LENGTH", ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_ASN1_LENGTH}, - #else - {"ERROR_PARSING_ASN1_LENGTH", 61, 109}, - #endif - #ifdef HTTP_R_ERROR_PARSING_CONTENT_LENGTH - {"ERROR_PARSING_CONTENT_LENGTH", ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_CONTENT_LENGTH}, - #else - {"ERROR_PARSING_CONTENT_LENGTH", 61, 119}, - #endif - #ifdef HTTP_R_ERROR_PARSING_URL - {"ERROR_PARSING_URL", ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_URL}, - #else - {"ERROR_PARSING_URL", 61, 101}, - #endif - #ifdef HTTP_R_ERROR_RECEIVING - {"ERROR_RECEIVING", ERR_LIB_HTTP, HTTP_R_ERROR_RECEIVING}, - #else - {"ERROR_RECEIVING", 61, 103}, - #endif - #ifdef HTTP_R_ERROR_SENDING - {"ERROR_SENDING", ERR_LIB_HTTP, HTTP_R_ERROR_SENDING}, - #else - {"ERROR_SENDING", 61, 102}, - #endif - #ifdef HTTP_R_FAILED_READING_DATA - {"FAILED_READING_DATA", ERR_LIB_HTTP, HTTP_R_FAILED_READING_DATA}, - #else - {"FAILED_READING_DATA", 61, 128}, - #endif - #ifdef HTTP_R_HEADER_PARSE_ERROR - {"HEADER_PARSE_ERROR", ERR_LIB_HTTP, HTTP_R_HEADER_PARSE_ERROR}, - #else - {"HEADER_PARSE_ERROR", 61, 126}, - #endif - #ifdef HTTP_R_INCONSISTENT_CONTENT_LENGTH - {"INCONSISTENT_CONTENT_LENGTH", ERR_LIB_HTTP, HTTP_R_INCONSISTENT_CONTENT_LENGTH}, - #else - {"INCONSISTENT_CONTENT_LENGTH", 61, 120}, - #endif - #ifdef HTTP_R_INVALID_PORT_NUMBER - {"INVALID_PORT_NUMBER", ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER}, - #else - {"INVALID_PORT_NUMBER", 61, 123}, - #endif - #ifdef HTTP_R_INVALID_URL_PATH - {"INVALID_URL_PATH", ERR_LIB_HTTP, HTTP_R_INVALID_URL_PATH}, - #else - {"INVALID_URL_PATH", 61, 125}, - #endif - #ifdef HTTP_R_INVALID_URL_SCHEME - {"INVALID_URL_SCHEME", ERR_LIB_HTTP, HTTP_R_INVALID_URL_SCHEME}, - #else - {"INVALID_URL_SCHEME", 61, 124}, - #endif - #ifdef HTTP_R_MAX_RESP_LEN_EXCEEDED - {"MAX_RESP_LEN_EXCEEDED", ERR_LIB_HTTP, HTTP_R_MAX_RESP_LEN_EXCEEDED}, - #else - {"MAX_RESP_LEN_EXCEEDED", 61, 117}, - #endif - #ifdef HTTP_R_MISSING_ASN1_ENCODING - {"MISSING_ASN1_ENCODING", ERR_LIB_HTTP, HTTP_R_MISSING_ASN1_ENCODING}, - #else - {"MISSING_ASN1_ENCODING", 61, 110}, - #endif - #ifdef HTTP_R_MISSING_CONTENT_TYPE - {"MISSING_CONTENT_TYPE", ERR_LIB_HTTP, HTTP_R_MISSING_CONTENT_TYPE}, - #else - {"MISSING_CONTENT_TYPE", 61, 121}, - #endif - #ifdef HTTP_R_MISSING_REDIRECT_LOCATION - {"MISSING_REDIRECT_LOCATION", ERR_LIB_HTTP, HTTP_R_MISSING_REDIRECT_LOCATION}, - #else - {"MISSING_REDIRECT_LOCATION", 61, 111}, - #endif - #ifdef HTTP_R_RECEIVED_ERROR - {"RECEIVED_ERROR", ERR_LIB_HTTP, HTTP_R_RECEIVED_ERROR}, - #else - {"RECEIVED_ERROR", 61, 105}, - #endif - #ifdef HTTP_R_RECEIVED_WRONG_HTTP_VERSION - {"RECEIVED_WRONG_HTTP_VERSION", ERR_LIB_HTTP, HTTP_R_RECEIVED_WRONG_HTTP_VERSION}, - #else - {"RECEIVED_WRONG_HTTP_VERSION", 61, 106}, - #endif - #ifdef HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP - {"REDIRECTION_FROM_HTTPS_TO_HTTP", ERR_LIB_HTTP, HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP}, - #else - {"REDIRECTION_FROM_HTTPS_TO_HTTP", 61, 112}, - #endif - #ifdef HTTP_R_REDIRECTION_NOT_ENABLED - {"REDIRECTION_NOT_ENABLED", ERR_LIB_HTTP, HTTP_R_REDIRECTION_NOT_ENABLED}, - #else - {"REDIRECTION_NOT_ENABLED", 61, 116}, - #endif - #ifdef HTTP_R_RESPONSE_LINE_TOO_LONG - {"RESPONSE_LINE_TOO_LONG", ERR_LIB_HTTP, HTTP_R_RESPONSE_LINE_TOO_LONG}, - #else - {"RESPONSE_LINE_TOO_LONG", 61, 113}, - #endif - #ifdef HTTP_R_RESPONSE_PARSE_ERROR - {"RESPONSE_PARSE_ERROR", ERR_LIB_HTTP, HTTP_R_RESPONSE_PARSE_ERROR}, - #else - {"RESPONSE_PARSE_ERROR", 61, 104}, - #endif - #ifdef HTTP_R_RESPONSE_TOO_MANY_HDRLINES - {"RESPONSE_TOO_MANY_HDRLINES", ERR_LIB_HTTP, HTTP_R_RESPONSE_TOO_MANY_HDRLINES}, - #else - {"RESPONSE_TOO_MANY_HDRLINES", 61, 130}, - #endif - #ifdef HTTP_R_RETRY_TIMEOUT - {"RETRY_TIMEOUT", ERR_LIB_HTTP, HTTP_R_RETRY_TIMEOUT}, - #else - {"RETRY_TIMEOUT", 61, 129}, - #endif - #ifdef HTTP_R_SERVER_CANCELED_CONNECTION - {"SERVER_CANCELED_CONNECTION", ERR_LIB_HTTP, HTTP_R_SERVER_CANCELED_CONNECTION}, - #else - {"SERVER_CANCELED_CONNECTION", 61, 127}, - #endif - #ifdef HTTP_R_SOCK_NOT_SUPPORTED - {"SOCK_NOT_SUPPORTED", ERR_LIB_HTTP, HTTP_R_SOCK_NOT_SUPPORTED}, - #else - {"SOCK_NOT_SUPPORTED", 61, 122}, - #endif - #ifdef HTTP_R_STATUS_CODE_UNSUPPORTED - {"STATUS_CODE_UNSUPPORTED", ERR_LIB_HTTP, HTTP_R_STATUS_CODE_UNSUPPORTED}, - #else - {"STATUS_CODE_UNSUPPORTED", 61, 114}, - #endif - #ifdef HTTP_R_TLS_NOT_ENABLED - {"TLS_NOT_ENABLED", ERR_LIB_HTTP, HTTP_R_TLS_NOT_ENABLED}, - #else - {"TLS_NOT_ENABLED", 61, 107}, - #endif - #ifdef HTTP_R_TOO_MANY_REDIRECTIONS - {"TOO_MANY_REDIRECTIONS", ERR_LIB_HTTP, HTTP_R_TOO_MANY_REDIRECTIONS}, - #else - {"TOO_MANY_REDIRECTIONS", 61, 115}, - #endif - #ifdef HTTP_R_UNEXPECTED_CONTENT_TYPE - {"UNEXPECTED_CONTENT_TYPE", ERR_LIB_HTTP, HTTP_R_UNEXPECTED_CONTENT_TYPE}, - #else - {"UNEXPECTED_CONTENT_TYPE", 61, 118}, - #endif - #ifdef OBJ_R_OID_EXISTS - {"OID_EXISTS", ERR_LIB_OBJ, OBJ_R_OID_EXISTS}, - #else - {"OID_EXISTS", 8, 102}, - #endif - #ifdef OBJ_R_UNKNOWN_NID - {"UNKNOWN_NID", ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID}, - #else - {"UNKNOWN_NID", 8, 101}, - #endif - #ifdef OBJ_R_UNKNOWN_OBJECT_NAME - {"UNKNOWN_OBJECT_NAME", ERR_LIB_OBJ, OBJ_R_UNKNOWN_OBJECT_NAME}, - #else - {"UNKNOWN_OBJECT_NAME", 8, 103}, - #endif - #ifdef OCSP_R_CERTIFICATE_VERIFY_ERROR - {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_OCSP, OCSP_R_CERTIFICATE_VERIFY_ERROR}, - #else - {"CERTIFICATE_VERIFY_ERROR", 39, 101}, - #endif - #ifdef OCSP_R_DIGEST_ERR - {"DIGEST_ERR", ERR_LIB_OCSP, OCSP_R_DIGEST_ERR}, - #else - {"DIGEST_ERR", 39, 102}, - #endif - #ifdef OCSP_R_DIGEST_NAME_ERR - {"DIGEST_NAME_ERR", ERR_LIB_OCSP, OCSP_R_DIGEST_NAME_ERR}, - #else - {"DIGEST_NAME_ERR", 39, 106}, - #endif - #ifdef OCSP_R_DIGEST_SIZE_ERR - {"DIGEST_SIZE_ERR", ERR_LIB_OCSP, OCSP_R_DIGEST_SIZE_ERR}, - #else - {"DIGEST_SIZE_ERR", 39, 107}, - #endif - #ifdef OCSP_R_ERROR_IN_NEXTUPDATE_FIELD - {"ERROR_IN_NEXTUPDATE_FIELD", ERR_LIB_OCSP, OCSP_R_ERROR_IN_NEXTUPDATE_FIELD}, - #else - {"ERROR_IN_NEXTUPDATE_FIELD", 39, 122}, - #endif - #ifdef OCSP_R_ERROR_IN_THISUPDATE_FIELD - {"ERROR_IN_THISUPDATE_FIELD", ERR_LIB_OCSP, OCSP_R_ERROR_IN_THISUPDATE_FIELD}, - #else - {"ERROR_IN_THISUPDATE_FIELD", 39, 123}, - #endif - #ifdef OCSP_R_MISSING_OCSPSIGNING_USAGE - {"MISSING_OCSPSIGNING_USAGE", ERR_LIB_OCSP, OCSP_R_MISSING_OCSPSIGNING_USAGE}, - #else - {"MISSING_OCSPSIGNING_USAGE", 39, 103}, - #endif - #ifdef OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE - {"NEXTUPDATE_BEFORE_THISUPDATE", ERR_LIB_OCSP, OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE}, - #else - {"NEXTUPDATE_BEFORE_THISUPDATE", 39, 124}, - #endif - #ifdef OCSP_R_NOT_BASIC_RESPONSE - {"NOT_BASIC_RESPONSE", ERR_LIB_OCSP, OCSP_R_NOT_BASIC_RESPONSE}, - #else - {"NOT_BASIC_RESPONSE", 39, 104}, - #endif - #ifdef OCSP_R_NO_CERTIFICATES_IN_CHAIN - {"NO_CERTIFICATES_IN_CHAIN", ERR_LIB_OCSP, OCSP_R_NO_CERTIFICATES_IN_CHAIN}, - #else - {"NO_CERTIFICATES_IN_CHAIN", 39, 105}, - #endif - #ifdef OCSP_R_NO_RESPONSE_DATA - {"NO_RESPONSE_DATA", ERR_LIB_OCSP, OCSP_R_NO_RESPONSE_DATA}, - #else - {"NO_RESPONSE_DATA", 39, 108}, - #endif - #ifdef OCSP_R_NO_REVOKED_TIME - {"NO_REVOKED_TIME", ERR_LIB_OCSP, OCSP_R_NO_REVOKED_TIME}, - #else - {"NO_REVOKED_TIME", 39, 109}, - #endif - #ifdef OCSP_R_NO_SIGNER_KEY - {"NO_SIGNER_KEY", ERR_LIB_OCSP, OCSP_R_NO_SIGNER_KEY}, - #else - {"NO_SIGNER_KEY", 39, 130}, - #endif - #ifdef OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE - {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_OCSP, OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, - #else - {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 39, 110}, - #endif - #ifdef OCSP_R_REQUEST_NOT_SIGNED - {"REQUEST_NOT_SIGNED", ERR_LIB_OCSP, OCSP_R_REQUEST_NOT_SIGNED}, - #else - {"REQUEST_NOT_SIGNED", 39, 128}, - #endif - #ifdef OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA - {"RESPONSE_CONTAINS_NO_REVOCATION_DATA", ERR_LIB_OCSP, OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA}, - #else - {"RESPONSE_CONTAINS_NO_REVOCATION_DATA", 39, 111}, - #endif - #ifdef OCSP_R_ROOT_CA_NOT_TRUSTED - {"ROOT_CA_NOT_TRUSTED", ERR_LIB_OCSP, OCSP_R_ROOT_CA_NOT_TRUSTED}, - #else - {"ROOT_CA_NOT_TRUSTED", 39, 112}, - #endif - #ifdef OCSP_R_SIGNATURE_FAILURE - {"SIGNATURE_FAILURE", ERR_LIB_OCSP, OCSP_R_SIGNATURE_FAILURE}, - #else - {"SIGNATURE_FAILURE", 39, 117}, - #endif - #ifdef OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND - {"SIGNER_CERTIFICATE_NOT_FOUND", ERR_LIB_OCSP, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND}, - #else - {"SIGNER_CERTIFICATE_NOT_FOUND", 39, 118}, - #endif - #ifdef OCSP_R_STATUS_EXPIRED - {"STATUS_EXPIRED", ERR_LIB_OCSP, OCSP_R_STATUS_EXPIRED}, - #else - {"STATUS_EXPIRED", 39, 125}, - #endif - #ifdef OCSP_R_STATUS_NOT_YET_VALID - {"STATUS_NOT_YET_VALID", ERR_LIB_OCSP, OCSP_R_STATUS_NOT_YET_VALID}, - #else - {"STATUS_NOT_YET_VALID", 39, 126}, - #endif - #ifdef OCSP_R_STATUS_TOO_OLD - {"STATUS_TOO_OLD", ERR_LIB_OCSP, OCSP_R_STATUS_TOO_OLD}, - #else - {"STATUS_TOO_OLD", 39, 127}, - #endif - #ifdef OCSP_R_UNKNOWN_MESSAGE_DIGEST - {"UNKNOWN_MESSAGE_DIGEST", ERR_LIB_OCSP, OCSP_R_UNKNOWN_MESSAGE_DIGEST}, - #else - {"UNKNOWN_MESSAGE_DIGEST", 39, 119}, - #endif - #ifdef OCSP_R_UNKNOWN_NID - {"UNKNOWN_NID", ERR_LIB_OCSP, OCSP_R_UNKNOWN_NID}, - #else - {"UNKNOWN_NID", 39, 120}, - #endif - #ifdef OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE - {"UNSUPPORTED_REQUESTORNAME_TYPE", ERR_LIB_OCSP, OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE}, - #else - {"UNSUPPORTED_REQUESTORNAME_TYPE", 39, 129}, - #endif - #ifdef OSSL_DECODER_R_COULD_NOT_DECODE_OBJECT - {"COULD_NOT_DECODE_OBJECT", ERR_LIB_OSSL_DECODER, OSSL_DECODER_R_COULD_NOT_DECODE_OBJECT}, - #else - {"COULD_NOT_DECODE_OBJECT", 60, 101}, - #endif - #ifdef OSSL_DECODER_R_DECODER_NOT_FOUND - {"DECODER_NOT_FOUND", ERR_LIB_OSSL_DECODER, OSSL_DECODER_R_DECODER_NOT_FOUND}, - #else - {"DECODER_NOT_FOUND", 60, 102}, - #endif - #ifdef OSSL_DECODER_R_MISSING_GET_PARAMS - {"MISSING_GET_PARAMS", ERR_LIB_OSSL_DECODER, OSSL_DECODER_R_MISSING_GET_PARAMS}, - #else - {"MISSING_GET_PARAMS", 60, 100}, - #endif - #ifdef OSSL_ENCODER_R_ENCODER_NOT_FOUND - {"ENCODER_NOT_FOUND", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_ENCODER_NOT_FOUND}, - #else - {"ENCODER_NOT_FOUND", 59, 101}, - #endif - #ifdef OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY - {"INCORRECT_PROPERTY_QUERY", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY}, - #else - {"INCORRECT_PROPERTY_QUERY", 59, 100}, - #endif - #ifdef OSSL_ENCODER_R_MISSING_GET_PARAMS - {"MISSING_GET_PARAMS", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_MISSING_GET_PARAMS}, - #else - {"MISSING_GET_PARAMS", 59, 102}, - #endif - #ifdef OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE - {"AMBIGUOUS_CONTENT_TYPE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE}, - #else - {"AMBIGUOUS_CONTENT_TYPE", 44, 107}, - #endif - #ifdef OSSL_STORE_R_BAD_PASSWORD_READ - {"BAD_PASSWORD_READ", ERR_LIB_OSSL_STORE, OSSL_STORE_R_BAD_PASSWORD_READ}, - #else - {"BAD_PASSWORD_READ", 44, 115}, - #endif - #ifdef OSSL_STORE_R_ERROR_VERIFYING_PKCS12_MAC - {"ERROR_VERIFYING_PKCS12_MAC", ERR_LIB_OSSL_STORE, OSSL_STORE_R_ERROR_VERIFYING_PKCS12_MAC}, - #else - {"ERROR_VERIFYING_PKCS12_MAC", 44, 113}, - #endif - #ifdef OSSL_STORE_R_FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST - {"FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST", ERR_LIB_OSSL_STORE, OSSL_STORE_R_FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST}, - #else - {"FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST", 44, 121}, - #endif - #ifdef OSSL_STORE_R_INVALID_SCHEME - {"INVALID_SCHEME", ERR_LIB_OSSL_STORE, OSSL_STORE_R_INVALID_SCHEME}, - #else - {"INVALID_SCHEME", 44, 106}, - #endif - #ifdef OSSL_STORE_R_IS_NOT_A - {"IS_NOT_A", ERR_LIB_OSSL_STORE, OSSL_STORE_R_IS_NOT_A}, - #else - {"IS_NOT_A", 44, 112}, - #endif - #ifdef OSSL_STORE_R_LOADER_INCOMPLETE - {"LOADER_INCOMPLETE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADER_INCOMPLETE}, - #else - {"LOADER_INCOMPLETE", 44, 116}, - #endif - #ifdef OSSL_STORE_R_LOADING_STARTED - {"LOADING_STARTED", ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADING_STARTED}, - #else - {"LOADING_STARTED", 44, 117}, - #endif - #ifdef OSSL_STORE_R_NOT_A_CERTIFICATE - {"NOT_A_CERTIFICATE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_CERTIFICATE}, - #else - {"NOT_A_CERTIFICATE", 44, 100}, - #endif - #ifdef OSSL_STORE_R_NOT_A_CRL - {"NOT_A_CRL", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_CRL}, - #else - {"NOT_A_CRL", 44, 101}, - #endif - #ifdef OSSL_STORE_R_NOT_A_NAME - {"NOT_A_NAME", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_NAME}, - #else - {"NOT_A_NAME", 44, 103}, - #endif - #ifdef OSSL_STORE_R_NOT_A_PRIVATE_KEY - {"NOT_A_PRIVATE_KEY", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_PRIVATE_KEY}, - #else - {"NOT_A_PRIVATE_KEY", 44, 102}, - #endif - #ifdef OSSL_STORE_R_NOT_A_PUBLIC_KEY - {"NOT_A_PUBLIC_KEY", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_PUBLIC_KEY}, - #else - {"NOT_A_PUBLIC_KEY", 44, 122}, - #endif - #ifdef OSSL_STORE_R_NOT_PARAMETERS - {"NOT_PARAMETERS", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_PARAMETERS}, - #else - {"NOT_PARAMETERS", 44, 104}, - #endif - #ifdef OSSL_STORE_R_NO_LOADERS_FOUND - {"NO_LOADERS_FOUND", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NO_LOADERS_FOUND}, - #else - {"NO_LOADERS_FOUND", 44, 123}, - #endif - #ifdef OSSL_STORE_R_PASSPHRASE_CALLBACK_ERROR - {"PASSPHRASE_CALLBACK_ERROR", ERR_LIB_OSSL_STORE, OSSL_STORE_R_PASSPHRASE_CALLBACK_ERROR}, - #else - {"PASSPHRASE_CALLBACK_ERROR", 44, 114}, - #endif - #ifdef OSSL_STORE_R_PATH_MUST_BE_ABSOLUTE - {"PATH_MUST_BE_ABSOLUTE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_PATH_MUST_BE_ABSOLUTE}, - #else - {"PATH_MUST_BE_ABSOLUTE", 44, 108}, - #endif - #ifdef OSSL_STORE_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES - {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", ERR_LIB_OSSL_STORE, OSSL_STORE_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES}, - #else - {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", 44, 119}, - #endif - #ifdef OSSL_STORE_R_UI_PROCESS_INTERRUPTED_OR_CANCELLED - {"UI_PROCESS_INTERRUPTED_OR_CANCELLED", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UI_PROCESS_INTERRUPTED_OR_CANCELLED}, - #else - {"UI_PROCESS_INTERRUPTED_OR_CANCELLED", 44, 109}, - #endif - #ifdef OSSL_STORE_R_UNREGISTERED_SCHEME - {"UNREGISTERED_SCHEME", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNREGISTERED_SCHEME}, - #else - {"UNREGISTERED_SCHEME", 44, 105}, - #endif - #ifdef OSSL_STORE_R_UNSUPPORTED_CONTENT_TYPE - {"UNSUPPORTED_CONTENT_TYPE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_CONTENT_TYPE}, - #else - {"UNSUPPORTED_CONTENT_TYPE", 44, 110}, - #endif - #ifdef OSSL_STORE_R_UNSUPPORTED_OPERATION - {"UNSUPPORTED_OPERATION", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_OPERATION}, - #else - {"UNSUPPORTED_OPERATION", 44, 118}, - #endif - #ifdef OSSL_STORE_R_UNSUPPORTED_SEARCH_TYPE - {"UNSUPPORTED_SEARCH_TYPE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_SEARCH_TYPE}, - #else - {"UNSUPPORTED_SEARCH_TYPE", 44, 120}, - #endif - #ifdef OSSL_STORE_R_URI_AUTHORITY_UNSUPPORTED - {"URI_AUTHORITY_UNSUPPORTED", ERR_LIB_OSSL_STORE, OSSL_STORE_R_URI_AUTHORITY_UNSUPPORTED}, - #else - {"URI_AUTHORITY_UNSUPPORTED", 44, 111}, - #endif - #ifdef PEM_R_BAD_BASE64_DECODE - {"BAD_BASE64_DECODE", ERR_LIB_PEM, PEM_R_BAD_BASE64_DECODE}, - #else - {"BAD_BASE64_DECODE", 9, 100}, - #endif - #ifdef PEM_R_BAD_DECRYPT - {"BAD_DECRYPT", ERR_LIB_PEM, PEM_R_BAD_DECRYPT}, - #else - {"BAD_DECRYPT", 9, 101}, - #endif - #ifdef PEM_R_BAD_END_LINE - {"BAD_END_LINE", ERR_LIB_PEM, PEM_R_BAD_END_LINE}, - #else - {"BAD_END_LINE", 9, 102}, - #endif - #ifdef PEM_R_BAD_IV_CHARS - {"BAD_IV_CHARS", ERR_LIB_PEM, PEM_R_BAD_IV_CHARS}, - #else - {"BAD_IV_CHARS", 9, 103}, - #endif - #ifdef PEM_R_BAD_MAGIC_NUMBER - {"BAD_MAGIC_NUMBER", ERR_LIB_PEM, PEM_R_BAD_MAGIC_NUMBER}, - #else - {"BAD_MAGIC_NUMBER", 9, 116}, - #endif - #ifdef PEM_R_BAD_PASSWORD_READ - {"BAD_PASSWORD_READ", ERR_LIB_PEM, PEM_R_BAD_PASSWORD_READ}, - #else - {"BAD_PASSWORD_READ", 9, 104}, - #endif - #ifdef PEM_R_BAD_VERSION_NUMBER - {"BAD_VERSION_NUMBER", ERR_LIB_PEM, PEM_R_BAD_VERSION_NUMBER}, - #else - {"BAD_VERSION_NUMBER", 9, 117}, - #endif - #ifdef PEM_R_BIO_WRITE_FAILURE - {"BIO_WRITE_FAILURE", ERR_LIB_PEM, PEM_R_BIO_WRITE_FAILURE}, - #else - {"BIO_WRITE_FAILURE", 9, 118}, - #endif - #ifdef PEM_R_CIPHER_IS_NULL - {"CIPHER_IS_NULL", ERR_LIB_PEM, PEM_R_CIPHER_IS_NULL}, - #else - {"CIPHER_IS_NULL", 9, 127}, - #endif - #ifdef PEM_R_ERROR_CONVERTING_PRIVATE_KEY - {"ERROR_CONVERTING_PRIVATE_KEY", ERR_LIB_PEM, PEM_R_ERROR_CONVERTING_PRIVATE_KEY}, - #else - {"ERROR_CONVERTING_PRIVATE_KEY", 9, 115}, - #endif - #ifdef PEM_R_EXPECTING_DSS_KEY_BLOB - {"EXPECTING_DSS_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_DSS_KEY_BLOB}, - #else - {"EXPECTING_DSS_KEY_BLOB", 9, 131}, - #endif - #ifdef PEM_R_EXPECTING_PRIVATE_KEY_BLOB - {"EXPECTING_PRIVATE_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_PRIVATE_KEY_BLOB}, - #else - {"EXPECTING_PRIVATE_KEY_BLOB", 9, 119}, - #endif - #ifdef PEM_R_EXPECTING_PUBLIC_KEY_BLOB - {"EXPECTING_PUBLIC_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_PUBLIC_KEY_BLOB}, - #else - {"EXPECTING_PUBLIC_KEY_BLOB", 9, 120}, - #endif - #ifdef PEM_R_EXPECTING_RSA_KEY_BLOB - {"EXPECTING_RSA_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_RSA_KEY_BLOB}, - #else - {"EXPECTING_RSA_KEY_BLOB", 9, 132}, - #endif - #ifdef PEM_R_HEADER_TOO_LONG - {"HEADER_TOO_LONG", ERR_LIB_PEM, PEM_R_HEADER_TOO_LONG}, - #else - {"HEADER_TOO_LONG", 9, 128}, - #endif - #ifdef PEM_R_INCONSISTENT_HEADER - {"INCONSISTENT_HEADER", ERR_LIB_PEM, PEM_R_INCONSISTENT_HEADER}, - #else - {"INCONSISTENT_HEADER", 9, 121}, - #endif - #ifdef PEM_R_KEYBLOB_HEADER_PARSE_ERROR - {"KEYBLOB_HEADER_PARSE_ERROR", ERR_LIB_PEM, PEM_R_KEYBLOB_HEADER_PARSE_ERROR}, - #else - {"KEYBLOB_HEADER_PARSE_ERROR", 9, 122}, - #endif - #ifdef PEM_R_KEYBLOB_TOO_SHORT - {"KEYBLOB_TOO_SHORT", ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT}, - #else - {"KEYBLOB_TOO_SHORT", 9, 123}, - #endif - #ifdef PEM_R_MISSING_DEK_IV - {"MISSING_DEK_IV", ERR_LIB_PEM, PEM_R_MISSING_DEK_IV}, - #else - {"MISSING_DEK_IV", 9, 129}, - #endif - #ifdef PEM_R_NOT_DEK_INFO - {"NOT_DEK_INFO", ERR_LIB_PEM, PEM_R_NOT_DEK_INFO}, - #else - {"NOT_DEK_INFO", 9, 105}, - #endif - #ifdef PEM_R_NOT_ENCRYPTED - {"NOT_ENCRYPTED", ERR_LIB_PEM, PEM_R_NOT_ENCRYPTED}, - #else - {"NOT_ENCRYPTED", 9, 106}, - #endif - #ifdef PEM_R_NOT_PROC_TYPE - {"NOT_PROC_TYPE", ERR_LIB_PEM, PEM_R_NOT_PROC_TYPE}, - #else - {"NOT_PROC_TYPE", 9, 107}, - #endif - #ifdef PEM_R_NO_START_LINE - {"NO_START_LINE", ERR_LIB_PEM, PEM_R_NO_START_LINE}, - #else - {"NO_START_LINE", 9, 108}, - #endif - #ifdef PEM_R_PROBLEMS_GETTING_PASSWORD - {"PROBLEMS_GETTING_PASSWORD", ERR_LIB_PEM, PEM_R_PROBLEMS_GETTING_PASSWORD}, - #else - {"PROBLEMS_GETTING_PASSWORD", 9, 109}, - #endif - #ifdef PEM_R_PVK_DATA_TOO_SHORT - {"PVK_DATA_TOO_SHORT", ERR_LIB_PEM, PEM_R_PVK_DATA_TOO_SHORT}, - #else - {"PVK_DATA_TOO_SHORT", 9, 124}, - #endif - #ifdef PEM_R_PVK_TOO_SHORT - {"PVK_TOO_SHORT", ERR_LIB_PEM, PEM_R_PVK_TOO_SHORT}, - #else - {"PVK_TOO_SHORT", 9, 125}, - #endif - #ifdef PEM_R_READ_KEY - {"READ_KEY", ERR_LIB_PEM, PEM_R_READ_KEY}, - #else - {"READ_KEY", 9, 111}, - #endif - #ifdef PEM_R_SHORT_HEADER - {"SHORT_HEADER", ERR_LIB_PEM, PEM_R_SHORT_HEADER}, - #else - {"SHORT_HEADER", 9, 112}, - #endif - #ifdef PEM_R_UNEXPECTED_DEK_IV - {"UNEXPECTED_DEK_IV", ERR_LIB_PEM, PEM_R_UNEXPECTED_DEK_IV}, - #else - {"UNEXPECTED_DEK_IV", 9, 130}, - #endif - #ifdef PEM_R_UNSUPPORTED_CIPHER - {"UNSUPPORTED_CIPHER", ERR_LIB_PEM, PEM_R_UNSUPPORTED_CIPHER}, - #else - {"UNSUPPORTED_CIPHER", 9, 113}, - #endif - #ifdef PEM_R_UNSUPPORTED_ENCRYPTION - {"UNSUPPORTED_ENCRYPTION", ERR_LIB_PEM, PEM_R_UNSUPPORTED_ENCRYPTION}, - #else - {"UNSUPPORTED_ENCRYPTION", 9, 114}, - #endif - #ifdef PEM_R_UNSUPPORTED_KEY_COMPONENTS - {"UNSUPPORTED_KEY_COMPONENTS", ERR_LIB_PEM, PEM_R_UNSUPPORTED_KEY_COMPONENTS}, - #else - {"UNSUPPORTED_KEY_COMPONENTS", 9, 126}, - #endif - #ifdef PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE - {"UNSUPPORTED_PUBLIC_KEY_TYPE", ERR_LIB_PEM, PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE}, - #else - {"UNSUPPORTED_PUBLIC_KEY_TYPE", 9, 110}, - #endif - #ifdef PKCS12_R_CALLBACK_FAILED - {"CALLBACK_FAILED", ERR_LIB_PKCS12, PKCS12_R_CALLBACK_FAILED}, - #else - {"CALLBACK_FAILED", 35, 115}, - #endif - #ifdef PKCS12_R_CANT_PACK_STRUCTURE - {"CANT_PACK_STRUCTURE", ERR_LIB_PKCS12, PKCS12_R_CANT_PACK_STRUCTURE}, - #else - {"CANT_PACK_STRUCTURE", 35, 100}, - #endif - #ifdef PKCS12_R_CONTENT_TYPE_NOT_DATA - {"CONTENT_TYPE_NOT_DATA", ERR_LIB_PKCS12, PKCS12_R_CONTENT_TYPE_NOT_DATA}, - #else - {"CONTENT_TYPE_NOT_DATA", 35, 121}, - #endif - #ifdef PKCS12_R_DECODE_ERROR - {"DECODE_ERROR", ERR_LIB_PKCS12, PKCS12_R_DECODE_ERROR}, - #else - {"DECODE_ERROR", 35, 101}, - #endif - #ifdef PKCS12_R_ENCODE_ERROR - {"ENCODE_ERROR", ERR_LIB_PKCS12, PKCS12_R_ENCODE_ERROR}, - #else - {"ENCODE_ERROR", 35, 102}, - #endif - #ifdef PKCS12_R_ENCRYPT_ERROR - {"ENCRYPT_ERROR", ERR_LIB_PKCS12, PKCS12_R_ENCRYPT_ERROR}, - #else - {"ENCRYPT_ERROR", 35, 103}, - #endif - #ifdef PKCS12_R_ERROR_SETTING_ENCRYPTED_DATA_TYPE - {"ERROR_SETTING_ENCRYPTED_DATA_TYPE", ERR_LIB_PKCS12, PKCS12_R_ERROR_SETTING_ENCRYPTED_DATA_TYPE}, - #else - {"ERROR_SETTING_ENCRYPTED_DATA_TYPE", 35, 120}, - #endif - #ifdef PKCS12_R_INVALID_NULL_ARGUMENT - {"INVALID_NULL_ARGUMENT", ERR_LIB_PKCS12, PKCS12_R_INVALID_NULL_ARGUMENT}, - #else - {"INVALID_NULL_ARGUMENT", 35, 104}, - #endif - #ifdef PKCS12_R_INVALID_NULL_PKCS12_POINTER - {"INVALID_NULL_PKCS12_POINTER", ERR_LIB_PKCS12, PKCS12_R_INVALID_NULL_PKCS12_POINTER}, - #else - {"INVALID_NULL_PKCS12_POINTER", 35, 105}, - #endif - #ifdef PKCS12_R_INVALID_TYPE - {"INVALID_TYPE", ERR_LIB_PKCS12, PKCS12_R_INVALID_TYPE}, - #else - {"INVALID_TYPE", 35, 112}, - #endif - #ifdef PKCS12_R_IV_GEN_ERROR - {"IV_GEN_ERROR", ERR_LIB_PKCS12, PKCS12_R_IV_GEN_ERROR}, - #else - {"IV_GEN_ERROR", 35, 106}, - #endif - #ifdef PKCS12_R_KEY_GEN_ERROR - {"KEY_GEN_ERROR", ERR_LIB_PKCS12, PKCS12_R_KEY_GEN_ERROR}, - #else - {"KEY_GEN_ERROR", 35, 107}, - #endif - #ifdef PKCS12_R_MAC_ABSENT - {"MAC_ABSENT", ERR_LIB_PKCS12, PKCS12_R_MAC_ABSENT}, - #else - {"MAC_ABSENT", 35, 108}, - #endif - #ifdef PKCS12_R_MAC_GENERATION_ERROR - {"MAC_GENERATION_ERROR", ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR}, - #else - {"MAC_GENERATION_ERROR", 35, 109}, - #endif - #ifdef PKCS12_R_MAC_SETUP_ERROR - {"MAC_SETUP_ERROR", ERR_LIB_PKCS12, PKCS12_R_MAC_SETUP_ERROR}, - #else - {"MAC_SETUP_ERROR", 35, 110}, - #endif - #ifdef PKCS12_R_MAC_STRING_SET_ERROR - {"MAC_STRING_SET_ERROR", ERR_LIB_PKCS12, PKCS12_R_MAC_STRING_SET_ERROR}, - #else - {"MAC_STRING_SET_ERROR", 35, 111}, - #endif - #ifdef PKCS12_R_MAC_VERIFY_FAILURE - {"MAC_VERIFY_FAILURE", ERR_LIB_PKCS12, PKCS12_R_MAC_VERIFY_FAILURE}, - #else - {"MAC_VERIFY_FAILURE", 35, 113}, - #endif - #ifdef PKCS12_R_PARSE_ERROR - {"PARSE_ERROR", ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR}, - #else - {"PARSE_ERROR", 35, 114}, - #endif - #ifdef PKCS12_R_PKCS12_CIPHERFINAL_ERROR - {"PKCS12_CIPHERFINAL_ERROR", ERR_LIB_PKCS12, PKCS12_R_PKCS12_CIPHERFINAL_ERROR}, - #else - {"PKCS12_CIPHERFINAL_ERROR", 35, 116}, - #endif - #ifdef PKCS12_R_UNKNOWN_DIGEST_ALGORITHM - {"UNKNOWN_DIGEST_ALGORITHM", ERR_LIB_PKCS12, PKCS12_R_UNKNOWN_DIGEST_ALGORITHM}, - #else - {"UNKNOWN_DIGEST_ALGORITHM", 35, 118}, - #endif - #ifdef PKCS12_R_UNSUPPORTED_PKCS12_MODE - {"UNSUPPORTED_PKCS12_MODE", ERR_LIB_PKCS12, PKCS12_R_UNSUPPORTED_PKCS12_MODE}, - #else - {"UNSUPPORTED_PKCS12_MODE", 35, 119}, - #endif - #ifdef PKCS7_R_CERTIFICATE_VERIFY_ERROR - {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_PKCS7, PKCS7_R_CERTIFICATE_VERIFY_ERROR}, - #else - {"CERTIFICATE_VERIFY_ERROR", 33, 117}, - #endif - #ifdef PKCS7_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER - {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", ERR_LIB_PKCS7, PKCS7_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER}, - #else - {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", 33, 144}, - #endif - #ifdef PKCS7_R_CIPHER_NOT_INITIALIZED - {"CIPHER_NOT_INITIALIZED", ERR_LIB_PKCS7, PKCS7_R_CIPHER_NOT_INITIALIZED}, - #else - {"CIPHER_NOT_INITIALIZED", 33, 116}, - #endif - #ifdef PKCS7_R_CONTENT_AND_DATA_PRESENT - {"CONTENT_AND_DATA_PRESENT", ERR_LIB_PKCS7, PKCS7_R_CONTENT_AND_DATA_PRESENT}, - #else - {"CONTENT_AND_DATA_PRESENT", 33, 118}, - #endif - #ifdef PKCS7_R_CTRL_ERROR - {"CTRL_ERROR", ERR_LIB_PKCS7, PKCS7_R_CTRL_ERROR}, - #else - {"CTRL_ERROR", 33, 152}, - #endif - #ifdef PKCS7_R_DECRYPT_ERROR - {"DECRYPT_ERROR", ERR_LIB_PKCS7, PKCS7_R_DECRYPT_ERROR}, - #else - {"DECRYPT_ERROR", 33, 119}, - #endif - #ifdef PKCS7_R_DIGEST_FAILURE - {"DIGEST_FAILURE", ERR_LIB_PKCS7, PKCS7_R_DIGEST_FAILURE}, - #else - {"DIGEST_FAILURE", 33, 101}, - #endif - #ifdef PKCS7_R_ENCRYPTION_CTRL_FAILURE - {"ENCRYPTION_CTRL_FAILURE", ERR_LIB_PKCS7, PKCS7_R_ENCRYPTION_CTRL_FAILURE}, - #else - {"ENCRYPTION_CTRL_FAILURE", 33, 149}, - #endif - #ifdef PKCS7_R_ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE - {"ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", ERR_LIB_PKCS7, PKCS7_R_ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE}, - #else - {"ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", 33, 150}, - #endif - #ifdef PKCS7_R_ERROR_ADDING_RECIPIENT - {"ERROR_ADDING_RECIPIENT", ERR_LIB_PKCS7, PKCS7_R_ERROR_ADDING_RECIPIENT}, - #else - {"ERROR_ADDING_RECIPIENT", 33, 120}, - #endif - #ifdef PKCS7_R_ERROR_SETTING_CIPHER - {"ERROR_SETTING_CIPHER", ERR_LIB_PKCS7, PKCS7_R_ERROR_SETTING_CIPHER}, - #else - {"ERROR_SETTING_CIPHER", 33, 121}, - #endif - #ifdef PKCS7_R_INVALID_NULL_POINTER - {"INVALID_NULL_POINTER", ERR_LIB_PKCS7, PKCS7_R_INVALID_NULL_POINTER}, - #else - {"INVALID_NULL_POINTER", 33, 143}, - #endif - #ifdef PKCS7_R_INVALID_SIGNED_DATA_TYPE - {"INVALID_SIGNED_DATA_TYPE", ERR_LIB_PKCS7, PKCS7_R_INVALID_SIGNED_DATA_TYPE}, - #else - {"INVALID_SIGNED_DATA_TYPE", 33, 155}, - #endif - #ifdef PKCS7_R_NO_CONTENT - {"NO_CONTENT", ERR_LIB_PKCS7, PKCS7_R_NO_CONTENT}, - #else - {"NO_CONTENT", 33, 122}, - #endif - #ifdef PKCS7_R_NO_DEFAULT_DIGEST - {"NO_DEFAULT_DIGEST", ERR_LIB_PKCS7, PKCS7_R_NO_DEFAULT_DIGEST}, - #else - {"NO_DEFAULT_DIGEST", 33, 151}, - #endif - #ifdef PKCS7_R_NO_MATCHING_DIGEST_TYPE_FOUND - {"NO_MATCHING_DIGEST_TYPE_FOUND", ERR_LIB_PKCS7, PKCS7_R_NO_MATCHING_DIGEST_TYPE_FOUND}, - #else - {"NO_MATCHING_DIGEST_TYPE_FOUND", 33, 154}, - #endif - #ifdef PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE - {"NO_RECIPIENT_MATCHES_CERTIFICATE", ERR_LIB_PKCS7, PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE}, - #else - {"NO_RECIPIENT_MATCHES_CERTIFICATE", 33, 115}, - #endif - #ifdef PKCS7_R_NO_SIGNATURES_ON_DATA - {"NO_SIGNATURES_ON_DATA", ERR_LIB_PKCS7, PKCS7_R_NO_SIGNATURES_ON_DATA}, - #else - {"NO_SIGNATURES_ON_DATA", 33, 123}, - #endif - #ifdef PKCS7_R_NO_SIGNERS - {"NO_SIGNERS", ERR_LIB_PKCS7, PKCS7_R_NO_SIGNERS}, - #else - {"NO_SIGNERS", 33, 142}, - #endif - #ifdef PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE - {"OPERATION_NOT_SUPPORTED_ON_THIS_TYPE", ERR_LIB_PKCS7, PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE}, - #else - {"OPERATION_NOT_SUPPORTED_ON_THIS_TYPE", 33, 104}, - #endif - #ifdef PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR - {"PKCS7_ADD_SIGNATURE_ERROR", ERR_LIB_PKCS7, PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR}, - #else - {"PKCS7_ADD_SIGNATURE_ERROR", 33, 124}, - #endif - #ifdef PKCS7_R_PKCS7_ADD_SIGNER_ERROR - {"PKCS7_ADD_SIGNER_ERROR", ERR_LIB_PKCS7, PKCS7_R_PKCS7_ADD_SIGNER_ERROR}, - #else - {"PKCS7_ADD_SIGNER_ERROR", 33, 153}, - #endif - #ifdef PKCS7_R_PKCS7_DATASIGN - {"PKCS7_DATASIGN", ERR_LIB_PKCS7, PKCS7_R_PKCS7_DATASIGN}, - #else - {"PKCS7_DATASIGN", 33, 145}, - #endif - #ifdef PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE - {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_PKCS7, PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, - #else - {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 33, 127}, - #endif - #ifdef PKCS7_R_SIGNATURE_FAILURE - {"SIGNATURE_FAILURE", ERR_LIB_PKCS7, PKCS7_R_SIGNATURE_FAILURE}, - #else - {"SIGNATURE_FAILURE", 33, 105}, - #endif - #ifdef PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND - {"SIGNER_CERTIFICATE_NOT_FOUND", ERR_LIB_PKCS7, PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND}, - #else - {"SIGNER_CERTIFICATE_NOT_FOUND", 33, 128}, - #endif - #ifdef PKCS7_R_SIGNING_CTRL_FAILURE - {"SIGNING_CTRL_FAILURE", ERR_LIB_PKCS7, PKCS7_R_SIGNING_CTRL_FAILURE}, - #else - {"SIGNING_CTRL_FAILURE", 33, 147}, - #endif - #ifdef PKCS7_R_SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE - {"SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", ERR_LIB_PKCS7, PKCS7_R_SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE}, - #else - {"SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", 33, 148}, - #endif - #ifdef PKCS7_R_SMIME_TEXT_ERROR - {"SMIME_TEXT_ERROR", ERR_LIB_PKCS7, PKCS7_R_SMIME_TEXT_ERROR}, - #else - {"SMIME_TEXT_ERROR", 33, 129}, - #endif - #ifdef PKCS7_R_UNABLE_TO_FIND_CERTIFICATE - {"UNABLE_TO_FIND_CERTIFICATE", ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_CERTIFICATE}, - #else - {"UNABLE_TO_FIND_CERTIFICATE", 33, 106}, - #endif - #ifdef PKCS7_R_UNABLE_TO_FIND_MEM_BIO - {"UNABLE_TO_FIND_MEM_BIO", ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MEM_BIO}, - #else - {"UNABLE_TO_FIND_MEM_BIO", 33, 107}, - #endif - #ifdef PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST - {"UNABLE_TO_FIND_MESSAGE_DIGEST", ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST}, - #else - {"UNABLE_TO_FIND_MESSAGE_DIGEST", 33, 108}, - #endif - #ifdef PKCS7_R_UNKNOWN_DIGEST_TYPE - {"UNKNOWN_DIGEST_TYPE", ERR_LIB_PKCS7, PKCS7_R_UNKNOWN_DIGEST_TYPE}, - #else - {"UNKNOWN_DIGEST_TYPE", 33, 109}, - #endif - #ifdef PKCS7_R_UNKNOWN_OPERATION - {"UNKNOWN_OPERATION", ERR_LIB_PKCS7, PKCS7_R_UNKNOWN_OPERATION}, - #else - {"UNKNOWN_OPERATION", 33, 110}, - #endif - #ifdef PKCS7_R_UNSUPPORTED_CIPHER_TYPE - {"UNSUPPORTED_CIPHER_TYPE", ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CIPHER_TYPE}, - #else - {"UNSUPPORTED_CIPHER_TYPE", 33, 111}, - #endif - #ifdef PKCS7_R_UNSUPPORTED_CONTENT_TYPE - {"UNSUPPORTED_CONTENT_TYPE", ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE}, - #else - {"UNSUPPORTED_CONTENT_TYPE", 33, 112}, - #endif - #ifdef PKCS7_R_WRONG_CONTENT_TYPE - {"WRONG_CONTENT_TYPE", ERR_LIB_PKCS7, PKCS7_R_WRONG_CONTENT_TYPE}, - #else - {"WRONG_CONTENT_TYPE", 33, 113}, - #endif - #ifdef PKCS7_R_WRONG_PKCS7_TYPE - {"WRONG_PKCS7_TYPE", ERR_LIB_PKCS7, PKCS7_R_WRONG_PKCS7_TYPE}, - #else - {"WRONG_PKCS7_TYPE", 33, 114}, - #endif - #ifdef PROP_R_NAME_TOO_LONG - {"NAME_TOO_LONG", ERR_LIB_PROP, PROP_R_NAME_TOO_LONG}, - #else - {"NAME_TOO_LONG", 55, 100}, - #endif - #ifdef PROP_R_NOT_AN_ASCII_CHARACTER - {"NOT_AN_ASCII_CHARACTER", ERR_LIB_PROP, PROP_R_NOT_AN_ASCII_CHARACTER}, - #else - {"NOT_AN_ASCII_CHARACTER", 55, 101}, - #endif - #ifdef PROP_R_NOT_AN_HEXADECIMAL_DIGIT - {"NOT_AN_HEXADECIMAL_DIGIT", ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT}, - #else - {"NOT_AN_HEXADECIMAL_DIGIT", 55, 102}, - #endif - #ifdef PROP_R_NOT_AN_IDENTIFIER - {"NOT_AN_IDENTIFIER", ERR_LIB_PROP, PROP_R_NOT_AN_IDENTIFIER}, - #else - {"NOT_AN_IDENTIFIER", 55, 103}, - #endif - #ifdef PROP_R_NOT_AN_OCTAL_DIGIT - {"NOT_AN_OCTAL_DIGIT", ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT}, - #else - {"NOT_AN_OCTAL_DIGIT", 55, 104}, - #endif - #ifdef PROP_R_NOT_A_DECIMAL_DIGIT - {"NOT_A_DECIMAL_DIGIT", ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT}, - #else - {"NOT_A_DECIMAL_DIGIT", 55, 105}, - #endif - #ifdef PROP_R_NO_MATCHING_STRING_DELIMITER - {"NO_MATCHING_STRING_DELIMITER", ERR_LIB_PROP, PROP_R_NO_MATCHING_STRING_DELIMITER}, - #else - {"NO_MATCHING_STRING_DELIMITER", 55, 106}, - #endif - #ifdef PROP_R_NO_VALUE - {"NO_VALUE", ERR_LIB_PROP, PROP_R_NO_VALUE}, - #else - {"NO_VALUE", 55, 107}, - #endif - #ifdef PROP_R_PARSE_FAILED - {"PARSE_FAILED", ERR_LIB_PROP, PROP_R_PARSE_FAILED}, - #else - {"PARSE_FAILED", 55, 108}, - #endif - #ifdef PROP_R_STRING_TOO_LONG - {"STRING_TOO_LONG", ERR_LIB_PROP, PROP_R_STRING_TOO_LONG}, - #else - {"STRING_TOO_LONG", 55, 109}, - #endif - #ifdef PROP_R_TRAILING_CHARACTERS - {"TRAILING_CHARACTERS", ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS}, - #else - {"TRAILING_CHARACTERS", 55, 110}, - #endif - #ifdef PROV_R_ADDITIONAL_INPUT_TOO_LONG - {"ADDITIONAL_INPUT_TOO_LONG", ERR_LIB_PROV, PROV_R_ADDITIONAL_INPUT_TOO_LONG}, - #else - {"ADDITIONAL_INPUT_TOO_LONG", 57, 184}, - #endif - #ifdef PROV_R_ALGORITHM_MISMATCH - {"ALGORITHM_MISMATCH", ERR_LIB_PROV, PROV_R_ALGORITHM_MISMATCH}, - #else - {"ALGORITHM_MISMATCH", 57, 173}, - #endif - #ifdef PROV_R_ALREADY_INSTANTIATED - {"ALREADY_INSTANTIATED", ERR_LIB_PROV, PROV_R_ALREADY_INSTANTIATED}, - #else - {"ALREADY_INSTANTIATED", 57, 185}, - #endif - #ifdef PROV_R_BAD_DECRYPT - {"BAD_DECRYPT", ERR_LIB_PROV, PROV_R_BAD_DECRYPT}, - #else - {"BAD_DECRYPT", 57, 100}, - #endif - #ifdef PROV_R_BAD_ENCODING - {"BAD_ENCODING", ERR_LIB_PROV, PROV_R_BAD_ENCODING}, - #else - {"BAD_ENCODING", 57, 141}, - #endif - #ifdef PROV_R_BAD_LENGTH - {"BAD_LENGTH", ERR_LIB_PROV, PROV_R_BAD_LENGTH}, - #else - {"BAD_LENGTH", 57, 142}, - #endif - #ifdef PROV_R_BAD_TLS_CLIENT_VERSION - {"BAD_TLS_CLIENT_VERSION", ERR_LIB_PROV, PROV_R_BAD_TLS_CLIENT_VERSION}, - #else - {"BAD_TLS_CLIENT_VERSION", 57, 161}, - #endif - #ifdef PROV_R_BN_ERROR - {"BN_ERROR", ERR_LIB_PROV, PROV_R_BN_ERROR}, - #else - {"BN_ERROR", 57, 160}, - #endif - #ifdef PROV_R_CIPHER_OPERATION_FAILED - {"CIPHER_OPERATION_FAILED", ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED}, - #else - {"CIPHER_OPERATION_FAILED", 57, 102}, - #endif - #ifdef PROV_R_COFACTOR_REQUIRED - {"COFACTOR_REQUIRED", ERR_LIB_PROV, PROV_R_COFACTOR_REQUIRED}, - #else - {"COFACTOR_REQUIRED", 57, 236}, - #endif - #ifdef PROV_R_DERIVATION_FUNCTION_INIT_FAILED - {"DERIVATION_FUNCTION_INIT_FAILED", ERR_LIB_PROV, PROV_R_DERIVATION_FUNCTION_INIT_FAILED}, - #else - {"DERIVATION_FUNCTION_INIT_FAILED", 57, 205}, - #endif - #ifdef PROV_R_DIGEST_NOT_ALLOWED - {"DIGEST_NOT_ALLOWED", ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED}, - #else - {"DIGEST_NOT_ALLOWED", 57, 174}, - #endif - #ifdef PROV_R_EMS_NOT_ENABLED - {"EMS_NOT_ENABLED", ERR_LIB_PROV, PROV_R_EMS_NOT_ENABLED}, - #else - {"EMS_NOT_ENABLED", 57, 233}, - #endif - #ifdef PROV_R_ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS - {"ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS", ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS}, - #else - {"ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS", 57, 244}, - #endif - #ifdef PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK - {"ENTROPY_SOURCE_STRENGTH_TOO_WEAK", ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK}, - #else - {"ENTROPY_SOURCE_STRENGTH_TOO_WEAK", 57, 186}, - #endif - #ifdef PROV_R_ERROR_INSTANTIATING_DRBG - {"ERROR_INSTANTIATING_DRBG", ERR_LIB_PROV, PROV_R_ERROR_INSTANTIATING_DRBG}, - #else - {"ERROR_INSTANTIATING_DRBG", 57, 188}, - #endif - #ifdef PROV_R_ERROR_RETRIEVING_ENTROPY - {"ERROR_RETRIEVING_ENTROPY", ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_ENTROPY}, - #else - {"ERROR_RETRIEVING_ENTROPY", 57, 189}, - #endif - #ifdef PROV_R_ERROR_RETRIEVING_NONCE - {"ERROR_RETRIEVING_NONCE", ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE}, - #else - {"ERROR_RETRIEVING_NONCE", 57, 190}, - #endif - #ifdef PROV_R_FAILED_DURING_DERIVATION - {"FAILED_DURING_DERIVATION", ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION}, - #else - {"FAILED_DURING_DERIVATION", 57, 164}, - #endif - #ifdef PROV_R_FAILED_TO_CREATE_LOCK - {"FAILED_TO_CREATE_LOCK", ERR_LIB_PROV, PROV_R_FAILED_TO_CREATE_LOCK}, - #else - {"FAILED_TO_CREATE_LOCK", 57, 180}, - #endif - #ifdef PROV_R_FAILED_TO_DECRYPT - {"FAILED_TO_DECRYPT", ERR_LIB_PROV, PROV_R_FAILED_TO_DECRYPT}, - #else - {"FAILED_TO_DECRYPT", 57, 162}, - #endif - #ifdef PROV_R_FAILED_TO_GENERATE_KEY - {"FAILED_TO_GENERATE_KEY", ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY}, - #else - {"FAILED_TO_GENERATE_KEY", 57, 121}, - #endif - #ifdef PROV_R_FAILED_TO_GET_PARAMETER - {"FAILED_TO_GET_PARAMETER", ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER}, - #else - {"FAILED_TO_GET_PARAMETER", 57, 103}, - #endif - #ifdef PROV_R_FAILED_TO_SET_PARAMETER - {"FAILED_TO_SET_PARAMETER", ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER}, - #else - {"FAILED_TO_SET_PARAMETER", 57, 104}, - #endif - #ifdef PROV_R_FAILED_TO_SIGN - {"FAILED_TO_SIGN", ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN}, - #else - {"FAILED_TO_SIGN", 57, 175}, - #endif - #ifdef PROV_R_FINAL_CALL_OUT_OF_ORDER - {"FINAL_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_FINAL_CALL_OUT_OF_ORDER}, - #else - {"FINAL_CALL_OUT_OF_ORDER", 57, 237}, - #endif - #ifdef PROV_R_FIPS_MODULE_CONDITIONAL_ERROR - {"FIPS_MODULE_CONDITIONAL_ERROR", ERR_LIB_PROV, PROV_R_FIPS_MODULE_CONDITIONAL_ERROR}, - #else - {"FIPS_MODULE_CONDITIONAL_ERROR", 57, 227}, - #endif - #ifdef PROV_R_FIPS_MODULE_ENTERING_ERROR_STATE - {"FIPS_MODULE_ENTERING_ERROR_STATE", ERR_LIB_PROV, PROV_R_FIPS_MODULE_ENTERING_ERROR_STATE}, - #else - {"FIPS_MODULE_ENTERING_ERROR_STATE", 57, 224}, - #endif - #ifdef PROV_R_FIPS_MODULE_IN_ERROR_STATE - {"FIPS_MODULE_IN_ERROR_STATE", ERR_LIB_PROV, PROV_R_FIPS_MODULE_IN_ERROR_STATE}, - #else - {"FIPS_MODULE_IN_ERROR_STATE", 57, 225}, - #endif - #ifdef PROV_R_GENERATE_ERROR - {"GENERATE_ERROR", ERR_LIB_PROV, PROV_R_GENERATE_ERROR}, - #else - {"GENERATE_ERROR", 57, 191}, - #endif - #ifdef PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE - {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", ERR_LIB_PROV, PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE}, - #else - {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", 57, 165}, - #endif - #ifdef PROV_R_INDICATOR_INTEGRITY_FAILURE - {"INDICATOR_INTEGRITY_FAILURE", ERR_LIB_PROV, PROV_R_INDICATOR_INTEGRITY_FAILURE}, - #else - {"INDICATOR_INTEGRITY_FAILURE", 57, 210}, - #endif - #ifdef PROV_R_INIT_CALL_OUT_OF_ORDER - {"INIT_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_INIT_CALL_OUT_OF_ORDER}, - #else - {"INIT_CALL_OUT_OF_ORDER", 57, 238}, - #endif - #ifdef PROV_R_INSUFFICIENT_DRBG_STRENGTH - {"INSUFFICIENT_DRBG_STRENGTH", ERR_LIB_PROV, PROV_R_INSUFFICIENT_DRBG_STRENGTH}, - #else - {"INSUFFICIENT_DRBG_STRENGTH", 57, 181}, - #endif - #ifdef PROV_R_INVALID_AAD - {"INVALID_AAD", ERR_LIB_PROV, PROV_R_INVALID_AAD}, - #else - {"INVALID_AAD", 57, 108}, - #endif - #ifdef PROV_R_INVALID_AEAD - {"INVALID_AEAD", ERR_LIB_PROV, PROV_R_INVALID_AEAD}, - #else - {"INVALID_AEAD", 57, 231}, - #endif - #ifdef PROV_R_INVALID_CONFIG_DATA - {"INVALID_CONFIG_DATA", ERR_LIB_PROV, PROV_R_INVALID_CONFIG_DATA}, - #else - {"INVALID_CONFIG_DATA", 57, 211}, - #endif - #ifdef PROV_R_INVALID_CONSTANT_LENGTH - {"INVALID_CONSTANT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_CONSTANT_LENGTH}, - #else - {"INVALID_CONSTANT_LENGTH", 57, 157}, - #endif - #ifdef PROV_R_INVALID_CURVE - {"INVALID_CURVE", ERR_LIB_PROV, PROV_R_INVALID_CURVE}, - #else - {"INVALID_CURVE", 57, 176}, - #endif - #ifdef PROV_R_INVALID_CUSTOM_LENGTH - {"INVALID_CUSTOM_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_CUSTOM_LENGTH}, - #else - {"INVALID_CUSTOM_LENGTH", 57, 111}, - #endif - #ifdef PROV_R_INVALID_DATA - {"INVALID_DATA", ERR_LIB_PROV, PROV_R_INVALID_DATA}, - #else - {"INVALID_DATA", 57, 115}, - #endif - #ifdef PROV_R_INVALID_DIGEST - {"INVALID_DIGEST", ERR_LIB_PROV, PROV_R_INVALID_DIGEST}, - #else - {"INVALID_DIGEST", 57, 122}, - #endif - #ifdef PROV_R_INVALID_DIGEST_LENGTH - {"INVALID_DIGEST_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH}, - #else - {"INVALID_DIGEST_LENGTH", 57, 166}, - #endif - #ifdef PROV_R_INVALID_DIGEST_SIZE - {"INVALID_DIGEST_SIZE", ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE}, - #else - {"INVALID_DIGEST_SIZE", 57, 218}, - #endif - #ifdef PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION - {"INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION", ERR_LIB_PROV, PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION}, - #else - {"INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION", 57, 243}, - #endif - #ifdef PROV_R_INVALID_INPUT_LENGTH - {"INVALID_INPUT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH}, - #else - {"INVALID_INPUT_LENGTH", 57, 230}, - #endif - #ifdef PROV_R_INVALID_ITERATION_COUNT - {"INVALID_ITERATION_COUNT", ERR_LIB_PROV, PROV_R_INVALID_ITERATION_COUNT}, - #else - {"INVALID_ITERATION_COUNT", 57, 123}, - #endif - #ifdef PROV_R_INVALID_IV_LENGTH - {"INVALID_IV_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH}, - #else - {"INVALID_IV_LENGTH", 57, 109}, - #endif - #ifdef PROV_R_INVALID_KDF - {"INVALID_KDF", ERR_LIB_PROV, PROV_R_INVALID_KDF}, - #else - {"INVALID_KDF", 57, 232}, - #endif - #ifdef PROV_R_INVALID_KEY - {"INVALID_KEY", ERR_LIB_PROV, PROV_R_INVALID_KEY}, - #else - {"INVALID_KEY", 57, 158}, - #endif - #ifdef PROV_R_INVALID_KEY_LENGTH - {"INVALID_KEY_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH}, - #else - {"INVALID_KEY_LENGTH", 57, 105}, - #endif - #ifdef PROV_R_INVALID_MAC - {"INVALID_MAC", ERR_LIB_PROV, PROV_R_INVALID_MAC}, - #else - {"INVALID_MAC", 57, 151}, - #endif - #ifdef PROV_R_INVALID_MEMORY_SIZE - {"INVALID_MEMORY_SIZE", ERR_LIB_PROV, PROV_R_INVALID_MEMORY_SIZE}, - #else - {"INVALID_MEMORY_SIZE", 57, 235}, - #endif - #ifdef PROV_R_INVALID_MGF1_MD - {"INVALID_MGF1_MD", ERR_LIB_PROV, PROV_R_INVALID_MGF1_MD}, - #else - {"INVALID_MGF1_MD", 57, 167}, - #endif - #ifdef PROV_R_INVALID_MODE - {"INVALID_MODE", ERR_LIB_PROV, PROV_R_INVALID_MODE}, - #else - {"INVALID_MODE", 57, 125}, - #endif - #ifdef PROV_R_INVALID_OUTPUT_LENGTH - {"INVALID_OUTPUT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH}, - #else - {"INVALID_OUTPUT_LENGTH", 57, 217}, - #endif - #ifdef PROV_R_INVALID_PADDING_MODE - {"INVALID_PADDING_MODE", ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE}, - #else - {"INVALID_PADDING_MODE", 57, 168}, - #endif - #ifdef PROV_R_INVALID_PREHASHED_DIGEST_LENGTH - {"INVALID_PREHASHED_DIGEST_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_PREHASHED_DIGEST_LENGTH}, - #else - {"INVALID_PREHASHED_DIGEST_LENGTH", 57, 241}, - #endif - #ifdef PROV_R_INVALID_PUBINFO - {"INVALID_PUBINFO", ERR_LIB_PROV, PROV_R_INVALID_PUBINFO}, - #else - {"INVALID_PUBINFO", 57, 198}, - #endif - #ifdef PROV_R_INVALID_SALT_LENGTH - {"INVALID_SALT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH}, - #else - {"INVALID_SALT_LENGTH", 57, 112}, - #endif - #ifdef PROV_R_INVALID_SEED_LENGTH - {"INVALID_SEED_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH}, - #else - {"INVALID_SEED_LENGTH", 57, 154}, - #endif - #ifdef PROV_R_INVALID_SIGNATURE_SIZE - {"INVALID_SIGNATURE_SIZE", ERR_LIB_PROV, PROV_R_INVALID_SIGNATURE_SIZE}, - #else - {"INVALID_SIGNATURE_SIZE", 57, 179}, - #endif - #ifdef PROV_R_INVALID_STATE - {"INVALID_STATE", ERR_LIB_PROV, PROV_R_INVALID_STATE}, - #else - {"INVALID_STATE", 57, 212}, - #endif - #ifdef PROV_R_INVALID_TAG - {"INVALID_TAG", ERR_LIB_PROV, PROV_R_INVALID_TAG}, - #else - {"INVALID_TAG", 57, 110}, - #endif - #ifdef PROV_R_INVALID_TAG_LENGTH - {"INVALID_TAG_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH}, - #else - {"INVALID_TAG_LENGTH", 57, 118}, - #endif - #ifdef PROV_R_INVALID_THREAD_POOL_SIZE - {"INVALID_THREAD_POOL_SIZE", ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE}, - #else - {"INVALID_THREAD_POOL_SIZE", 57, 234}, - #endif - #ifdef PROV_R_INVALID_UKM_LENGTH - {"INVALID_UKM_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_UKM_LENGTH}, - #else - {"INVALID_UKM_LENGTH", 57, 200}, - #endif - #ifdef PROV_R_INVALID_X931_DIGEST - {"INVALID_X931_DIGEST", ERR_LIB_PROV, PROV_R_INVALID_X931_DIGEST}, - #else - {"INVALID_X931_DIGEST", 57, 170}, - #endif - #ifdef PROV_R_IN_ERROR_STATE - {"IN_ERROR_STATE", ERR_LIB_PROV, PROV_R_IN_ERROR_STATE}, - #else - {"IN_ERROR_STATE", 57, 192}, - #endif - #ifdef PROV_R_KEY_SETUP_FAILED - {"KEY_SETUP_FAILED", ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED}, - #else - {"KEY_SETUP_FAILED", 57, 101}, - #endif - #ifdef PROV_R_KEY_SIZE_TOO_SMALL - {"KEY_SIZE_TOO_SMALL", ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL}, - #else - {"KEY_SIZE_TOO_SMALL", 57, 171}, - #endif - #ifdef PROV_R_LENGTH_TOO_LARGE - {"LENGTH_TOO_LARGE", ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE}, - #else - {"LENGTH_TOO_LARGE", 57, 202}, - #endif - #ifdef PROV_R_MISMATCHING_DOMAIN_PARAMETERS - {"MISMATCHING_DOMAIN_PARAMETERS", ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS}, - #else - {"MISMATCHING_DOMAIN_PARAMETERS", 57, 203}, - #endif - #ifdef PROV_R_MISSING_CEK_ALG - {"MISSING_CEK_ALG", ERR_LIB_PROV, PROV_R_MISSING_CEK_ALG}, - #else - {"MISSING_CEK_ALG", 57, 144}, - #endif - #ifdef PROV_R_MISSING_CIPHER - {"MISSING_CIPHER", ERR_LIB_PROV, PROV_R_MISSING_CIPHER}, - #else - {"MISSING_CIPHER", 57, 155}, - #endif - #ifdef PROV_R_MISSING_CONFIG_DATA - {"MISSING_CONFIG_DATA", ERR_LIB_PROV, PROV_R_MISSING_CONFIG_DATA}, - #else - {"MISSING_CONFIG_DATA", 57, 213}, - #endif - #ifdef PROV_R_MISSING_CONSTANT - {"MISSING_CONSTANT", ERR_LIB_PROV, PROV_R_MISSING_CONSTANT}, - #else - {"MISSING_CONSTANT", 57, 156}, - #endif - #ifdef PROV_R_MISSING_KEY - {"MISSING_KEY", ERR_LIB_PROV, PROV_R_MISSING_KEY}, - #else - {"MISSING_KEY", 57, 128}, - #endif - #ifdef PROV_R_MISSING_MAC - {"MISSING_MAC", ERR_LIB_PROV, PROV_R_MISSING_MAC}, - #else - {"MISSING_MAC", 57, 150}, - #endif - #ifdef PROV_R_MISSING_MESSAGE_DIGEST - {"MISSING_MESSAGE_DIGEST", ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST}, - #else - {"MISSING_MESSAGE_DIGEST", 57, 129}, - #endif - #ifdef PROV_R_MISSING_OID - {"MISSING_OID", ERR_LIB_PROV, PROV_R_MISSING_OID}, - #else - {"MISSING_OID", 57, 209}, - #endif - #ifdef PROV_R_MISSING_PASS - {"MISSING_PASS", ERR_LIB_PROV, PROV_R_MISSING_PASS}, - #else - {"MISSING_PASS", 57, 130}, - #endif - #ifdef PROV_R_MISSING_SALT - {"MISSING_SALT", ERR_LIB_PROV, PROV_R_MISSING_SALT}, - #else - {"MISSING_SALT", 57, 131}, - #endif - #ifdef PROV_R_MISSING_SECRET - {"MISSING_SECRET", ERR_LIB_PROV, PROV_R_MISSING_SECRET}, - #else - {"MISSING_SECRET", 57, 132}, - #endif - #ifdef PROV_R_MISSING_SEED - {"MISSING_SEED", ERR_LIB_PROV, PROV_R_MISSING_SEED}, - #else - {"MISSING_SEED", 57, 140}, - #endif - #ifdef PROV_R_MISSING_SESSION_ID - {"MISSING_SESSION_ID", ERR_LIB_PROV, PROV_R_MISSING_SESSION_ID}, - #else - {"MISSING_SESSION_ID", 57, 133}, - #endif - #ifdef PROV_R_MISSING_TYPE - {"MISSING_TYPE", ERR_LIB_PROV, PROV_R_MISSING_TYPE}, - #else - {"MISSING_TYPE", 57, 134}, - #endif - #ifdef PROV_R_MISSING_XCGHASH - {"MISSING_XCGHASH", ERR_LIB_PROV, PROV_R_MISSING_XCGHASH}, - #else - {"MISSING_XCGHASH", 57, 135}, - #endif - #ifdef PROV_R_MODULE_INTEGRITY_FAILURE - {"MODULE_INTEGRITY_FAILURE", ERR_LIB_PROV, PROV_R_MODULE_INTEGRITY_FAILURE}, - #else - {"MODULE_INTEGRITY_FAILURE", 57, 214}, - #endif - #ifdef PROV_R_NOT_A_PRIVATE_KEY - {"NOT_A_PRIVATE_KEY", ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY}, - #else - {"NOT_A_PRIVATE_KEY", 57, 221}, - #endif - #ifdef PROV_R_NOT_A_PUBLIC_KEY - {"NOT_A_PUBLIC_KEY", ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY}, - #else - {"NOT_A_PUBLIC_KEY", 57, 220}, - #endif - #ifdef PROV_R_NOT_INSTANTIATED - {"NOT_INSTANTIATED", ERR_LIB_PROV, PROV_R_NOT_INSTANTIATED}, - #else - {"NOT_INSTANTIATED", 57, 193}, - #endif - #ifdef PROV_R_NOT_PARAMETERS - {"NOT_PARAMETERS", ERR_LIB_PROV, PROV_R_NOT_PARAMETERS}, - #else - {"NOT_PARAMETERS", 57, 226}, - #endif - #ifdef PROV_R_NOT_SUPPORTED - {"NOT_SUPPORTED", ERR_LIB_PROV, PROV_R_NOT_SUPPORTED}, - #else - {"NOT_SUPPORTED", 57, 136}, - #endif - #ifdef PROV_R_NOT_XOF_OR_INVALID_LENGTH - {"NOT_XOF_OR_INVALID_LENGTH", ERR_LIB_PROV, PROV_R_NOT_XOF_OR_INVALID_LENGTH}, - #else - {"NOT_XOF_OR_INVALID_LENGTH", 57, 113}, - #endif - #ifdef PROV_R_NO_INSTANCE_ALLOWED - {"NO_INSTANCE_ALLOWED", ERR_LIB_PROV, PROV_R_NO_INSTANCE_ALLOWED}, - #else - {"NO_INSTANCE_ALLOWED", 57, 242}, - #endif - #ifdef PROV_R_NO_KEY_SET - {"NO_KEY_SET", ERR_LIB_PROV, PROV_R_NO_KEY_SET}, - #else - {"NO_KEY_SET", 57, 114}, - #endif - #ifdef PROV_R_NO_PARAMETERS_SET - {"NO_PARAMETERS_SET", ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET}, - #else - {"NO_PARAMETERS_SET", 57, 177}, - #endif - #ifdef PROV_R_ONESHOT_CALL_OUT_OF_ORDER - {"ONESHOT_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_ONESHOT_CALL_OUT_OF_ORDER}, - #else - {"ONESHOT_CALL_OUT_OF_ORDER", 57, 239}, - #endif - #ifdef PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE - {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", ERR_LIB_PROV, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE}, - #else - {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", 57, 178}, - #endif - #ifdef PROV_R_OUTPUT_BUFFER_TOO_SMALL - {"OUTPUT_BUFFER_TOO_SMALL", ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL}, - #else - {"OUTPUT_BUFFER_TOO_SMALL", 57, 106}, - #endif - #ifdef PROV_R_PARENT_CANNOT_GENERATE_RANDOM_NUMBERS - {"PARENT_CANNOT_GENERATE_RANDOM_NUMBERS", ERR_LIB_PROV, PROV_R_PARENT_CANNOT_GENERATE_RANDOM_NUMBERS}, - #else - {"PARENT_CANNOT_GENERATE_RANDOM_NUMBERS", 57, 228}, - #endif - #ifdef PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED - {"PARENT_CANNOT_SUPPLY_ENTROPY_SEED", ERR_LIB_PROV, PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED}, - #else - {"PARENT_CANNOT_SUPPLY_ENTROPY_SEED", 57, 187}, - #endif - #ifdef PROV_R_PARENT_LOCKING_NOT_ENABLED - {"PARENT_LOCKING_NOT_ENABLED", ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED}, - #else - {"PARENT_LOCKING_NOT_ENABLED", 57, 182}, - #endif - #ifdef PROV_R_PARENT_STRENGTH_TOO_WEAK - {"PARENT_STRENGTH_TOO_WEAK", ERR_LIB_PROV, PROV_R_PARENT_STRENGTH_TOO_WEAK}, - #else - {"PARENT_STRENGTH_TOO_WEAK", 57, 194}, - #endif - #ifdef PROV_R_PATH_MUST_BE_ABSOLUTE - {"PATH_MUST_BE_ABSOLUTE", ERR_LIB_PROV, PROV_R_PATH_MUST_BE_ABSOLUTE}, - #else - {"PATH_MUST_BE_ABSOLUTE", 57, 219}, - #endif - #ifdef PROV_R_PERSONALISATION_STRING_TOO_LONG - {"PERSONALISATION_STRING_TOO_LONG", ERR_LIB_PROV, PROV_R_PERSONALISATION_STRING_TOO_LONG}, - #else - {"PERSONALISATION_STRING_TOO_LONG", 57, 195}, - #endif - #ifdef PROV_R_PSS_SALTLEN_TOO_SMALL - {"PSS_SALTLEN_TOO_SMALL", ERR_LIB_PROV, PROV_R_PSS_SALTLEN_TOO_SMALL}, - #else - {"PSS_SALTLEN_TOO_SMALL", 57, 172}, - #endif - #ifdef PROV_R_REQUEST_TOO_LARGE_FOR_DRBG - {"REQUEST_TOO_LARGE_FOR_DRBG", ERR_LIB_PROV, PROV_R_REQUEST_TOO_LARGE_FOR_DRBG}, - #else - {"REQUEST_TOO_LARGE_FOR_DRBG", 57, 196}, - #endif - #ifdef PROV_R_REQUIRE_CTR_MODE_CIPHER - {"REQUIRE_CTR_MODE_CIPHER", ERR_LIB_PROV, PROV_R_REQUIRE_CTR_MODE_CIPHER}, - #else - {"REQUIRE_CTR_MODE_CIPHER", 57, 206}, - #endif - #ifdef PROV_R_RESEED_ERROR - {"RESEED_ERROR", ERR_LIB_PROV, PROV_R_RESEED_ERROR}, - #else - {"RESEED_ERROR", 57, 197}, - #endif - #ifdef PROV_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES - {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", ERR_LIB_PROV, PROV_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES}, - #else - {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", 57, 222}, - #endif - #ifdef PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT - {"SEED_SOURCES_MUST_NOT_HAVE_A_PARENT", ERR_LIB_PROV, PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT}, - #else - {"SEED_SOURCES_MUST_NOT_HAVE_A_PARENT", 57, 229}, - #endif - #ifdef PROV_R_SELF_TEST_KAT_FAILURE - {"SELF_TEST_KAT_FAILURE", ERR_LIB_PROV, PROV_R_SELF_TEST_KAT_FAILURE}, - #else - {"SELF_TEST_KAT_FAILURE", 57, 215}, - #endif - #ifdef PROV_R_SELF_TEST_POST_FAILURE - {"SELF_TEST_POST_FAILURE", ERR_LIB_PROV, PROV_R_SELF_TEST_POST_FAILURE}, - #else - {"SELF_TEST_POST_FAILURE", 57, 216}, - #endif - #ifdef PROV_R_TAG_NOT_NEEDED - {"TAG_NOT_NEEDED", ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED}, - #else - {"TAG_NOT_NEEDED", 57, 120}, - #endif - #ifdef PROV_R_TAG_NOT_SET - {"TAG_NOT_SET", ERR_LIB_PROV, PROV_R_TAG_NOT_SET}, - #else - {"TAG_NOT_SET", 57, 119}, - #endif - #ifdef PROV_R_TOO_MANY_RECORDS - {"TOO_MANY_RECORDS", ERR_LIB_PROV, PROV_R_TOO_MANY_RECORDS}, - #else - {"TOO_MANY_RECORDS", 57, 126}, - #endif - #ifdef PROV_R_UNABLE_TO_FIND_CIPHERS - {"UNABLE_TO_FIND_CIPHERS", ERR_LIB_PROV, PROV_R_UNABLE_TO_FIND_CIPHERS}, - #else - {"UNABLE_TO_FIND_CIPHERS", 57, 207}, - #endif - #ifdef PROV_R_UNABLE_TO_GET_PARENT_STRENGTH - {"UNABLE_TO_GET_PARENT_STRENGTH", ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PARENT_STRENGTH}, - #else - {"UNABLE_TO_GET_PARENT_STRENGTH", 57, 199}, - #endif - #ifdef PROV_R_UNABLE_TO_GET_PASSPHRASE - {"UNABLE_TO_GET_PASSPHRASE", ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PASSPHRASE}, - #else - {"UNABLE_TO_GET_PASSPHRASE", 57, 159}, - #endif - #ifdef PROV_R_UNABLE_TO_INITIALISE_CIPHERS - {"UNABLE_TO_INITIALISE_CIPHERS", ERR_LIB_PROV, PROV_R_UNABLE_TO_INITIALISE_CIPHERS}, - #else - {"UNABLE_TO_INITIALISE_CIPHERS", 57, 208}, - #endif - #ifdef PROV_R_UNABLE_TO_LOAD_SHA256 - {"UNABLE_TO_LOAD_SHA256", ERR_LIB_PROV, PROV_R_UNABLE_TO_LOAD_SHA256}, - #else - {"UNABLE_TO_LOAD_SHA256", 57, 147}, - #endif - #ifdef PROV_R_UNABLE_TO_LOCK_PARENT - {"UNABLE_TO_LOCK_PARENT", ERR_LIB_PROV, PROV_R_UNABLE_TO_LOCK_PARENT}, - #else - {"UNABLE_TO_LOCK_PARENT", 57, 201}, - #endif - #ifdef PROV_R_UNABLE_TO_RESEED - {"UNABLE_TO_RESEED", ERR_LIB_PROV, PROV_R_UNABLE_TO_RESEED}, - #else - {"UNABLE_TO_RESEED", 57, 204}, - #endif - #ifdef PROV_R_UNSUPPORTED_CEK_ALG - {"UNSUPPORTED_CEK_ALG", ERR_LIB_PROV, PROV_R_UNSUPPORTED_CEK_ALG}, - #else - {"UNSUPPORTED_CEK_ALG", 57, 145}, - #endif - #ifdef PROV_R_UNSUPPORTED_KEY_SIZE - {"UNSUPPORTED_KEY_SIZE", ERR_LIB_PROV, PROV_R_UNSUPPORTED_KEY_SIZE}, - #else - {"UNSUPPORTED_KEY_SIZE", 57, 153}, - #endif - #ifdef PROV_R_UNSUPPORTED_MAC_TYPE - {"UNSUPPORTED_MAC_TYPE", ERR_LIB_PROV, PROV_R_UNSUPPORTED_MAC_TYPE}, - #else - {"UNSUPPORTED_MAC_TYPE", 57, 137}, - #endif - #ifdef PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS - {"UNSUPPORTED_NUMBER_OF_ROUNDS", ERR_LIB_PROV, PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS}, - #else - {"UNSUPPORTED_NUMBER_OF_ROUNDS", 57, 152}, - #endif - #ifdef PROV_R_UPDATE_CALL_OUT_OF_ORDER - {"UPDATE_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER}, - #else - {"UPDATE_CALL_OUT_OF_ORDER", 57, 240}, - #endif - #ifdef PROV_R_URI_AUTHORITY_UNSUPPORTED - {"URI_AUTHORITY_UNSUPPORTED", ERR_LIB_PROV, PROV_R_URI_AUTHORITY_UNSUPPORTED}, - #else - {"URI_AUTHORITY_UNSUPPORTED", 57, 223}, - #endif - #ifdef PROV_R_VALUE_ERROR - {"VALUE_ERROR", ERR_LIB_PROV, PROV_R_VALUE_ERROR}, - #else - {"VALUE_ERROR", 57, 138}, - #endif - #ifdef PROV_R_WRONG_FINAL_BLOCK_LENGTH - {"WRONG_FINAL_BLOCK_LENGTH", ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH}, - #else - {"WRONG_FINAL_BLOCK_LENGTH", 57, 107}, - #endif - #ifdef PROV_R_WRONG_OUTPUT_BUFFER_SIZE - {"WRONG_OUTPUT_BUFFER_SIZE", ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE}, - #else - {"WRONG_OUTPUT_BUFFER_SIZE", 57, 139}, - #endif - #ifdef PROV_R_XOF_DIGESTS_NOT_ALLOWED - {"XOF_DIGESTS_NOT_ALLOWED", ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED}, - #else - {"XOF_DIGESTS_NOT_ALLOWED", 57, 183}, - #endif - #ifdef PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE - {"XTS_DATA_UNIT_IS_TOO_LARGE", ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE}, - #else - {"XTS_DATA_UNIT_IS_TOO_LARGE", 57, 148}, - #endif - #ifdef PROV_R_XTS_DUPLICATED_KEYS - {"XTS_DUPLICATED_KEYS", ERR_LIB_PROV, PROV_R_XTS_DUPLICATED_KEYS}, - #else - {"XTS_DUPLICATED_KEYS", 57, 149}, - #endif - #ifdef RAND_R_ADDITIONAL_INPUT_TOO_LONG - {"ADDITIONAL_INPUT_TOO_LONG", ERR_LIB_RAND, RAND_R_ADDITIONAL_INPUT_TOO_LONG}, - #else - {"ADDITIONAL_INPUT_TOO_LONG", 36, 102}, - #endif - #ifdef RAND_R_ALREADY_INSTANTIATED - {"ALREADY_INSTANTIATED", ERR_LIB_RAND, RAND_R_ALREADY_INSTANTIATED}, - #else - {"ALREADY_INSTANTIATED", 36, 103}, - #endif - #ifdef RAND_R_ARGUMENT_OUT_OF_RANGE - {"ARGUMENT_OUT_OF_RANGE", ERR_LIB_RAND, RAND_R_ARGUMENT_OUT_OF_RANGE}, - #else - {"ARGUMENT_OUT_OF_RANGE", 36, 105}, - #endif - #ifdef RAND_R_CANNOT_OPEN_FILE - {"CANNOT_OPEN_FILE", ERR_LIB_RAND, RAND_R_CANNOT_OPEN_FILE}, - #else - {"CANNOT_OPEN_FILE", 36, 121}, - #endif - #ifdef RAND_R_DRBG_ALREADY_INITIALIZED - {"DRBG_ALREADY_INITIALIZED", ERR_LIB_RAND, RAND_R_DRBG_ALREADY_INITIALIZED}, - #else - {"DRBG_ALREADY_INITIALIZED", 36, 129}, - #endif - #ifdef RAND_R_DRBG_NOT_INITIALISED - {"DRBG_NOT_INITIALISED", ERR_LIB_RAND, RAND_R_DRBG_NOT_INITIALISED}, - #else - {"DRBG_NOT_INITIALISED", 36, 104}, - #endif - #ifdef RAND_R_ENTROPY_INPUT_TOO_LONG - {"ENTROPY_INPUT_TOO_LONG", ERR_LIB_RAND, RAND_R_ENTROPY_INPUT_TOO_LONG}, - #else - {"ENTROPY_INPUT_TOO_LONG", 36, 106}, - #endif - #ifdef RAND_R_ENTROPY_OUT_OF_RANGE - {"ENTROPY_OUT_OF_RANGE", ERR_LIB_RAND, RAND_R_ENTROPY_OUT_OF_RANGE}, - #else - {"ENTROPY_OUT_OF_RANGE", 36, 124}, - #endif - #ifdef RAND_R_ERROR_ENTROPY_POOL_WAS_IGNORED - {"ERROR_ENTROPY_POOL_WAS_IGNORED", ERR_LIB_RAND, RAND_R_ERROR_ENTROPY_POOL_WAS_IGNORED}, - #else - {"ERROR_ENTROPY_POOL_WAS_IGNORED", 36, 127}, - #endif - #ifdef RAND_R_ERROR_INITIALISING_DRBG - {"ERROR_INITIALISING_DRBG", ERR_LIB_RAND, RAND_R_ERROR_INITIALISING_DRBG}, - #else - {"ERROR_INITIALISING_DRBG", 36, 107}, - #endif - #ifdef RAND_R_ERROR_INSTANTIATING_DRBG - {"ERROR_INSTANTIATING_DRBG", ERR_LIB_RAND, RAND_R_ERROR_INSTANTIATING_DRBG}, - #else - {"ERROR_INSTANTIATING_DRBG", 36, 108}, - #endif - #ifdef RAND_R_ERROR_RETRIEVING_ADDITIONAL_INPUT - {"ERROR_RETRIEVING_ADDITIONAL_INPUT", ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ADDITIONAL_INPUT}, - #else - {"ERROR_RETRIEVING_ADDITIONAL_INPUT", 36, 109}, - #endif - #ifdef RAND_R_ERROR_RETRIEVING_ENTROPY - {"ERROR_RETRIEVING_ENTROPY", ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ENTROPY}, - #else - {"ERROR_RETRIEVING_ENTROPY", 36, 110}, - #endif - #ifdef RAND_R_ERROR_RETRIEVING_NONCE - {"ERROR_RETRIEVING_NONCE", ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_NONCE}, - #else - {"ERROR_RETRIEVING_NONCE", 36, 111}, - #endif - #ifdef RAND_R_FAILED_TO_CREATE_LOCK - {"FAILED_TO_CREATE_LOCK", ERR_LIB_RAND, RAND_R_FAILED_TO_CREATE_LOCK}, - #else - {"FAILED_TO_CREATE_LOCK", 36, 126}, - #endif - #ifdef RAND_R_FUNC_NOT_IMPLEMENTED - {"FUNC_NOT_IMPLEMENTED", ERR_LIB_RAND, RAND_R_FUNC_NOT_IMPLEMENTED}, - #else - {"FUNC_NOT_IMPLEMENTED", 36, 101}, - #endif - #ifdef RAND_R_FWRITE_ERROR - {"FWRITE_ERROR", ERR_LIB_RAND, RAND_R_FWRITE_ERROR}, - #else - {"FWRITE_ERROR", 36, 123}, - #endif - #ifdef RAND_R_GENERATE_ERROR - {"GENERATE_ERROR", ERR_LIB_RAND, RAND_R_GENERATE_ERROR}, - #else - {"GENERATE_ERROR", 36, 112}, - #endif - #ifdef RAND_R_INSUFFICIENT_DRBG_STRENGTH - {"INSUFFICIENT_DRBG_STRENGTH", ERR_LIB_RAND, RAND_R_INSUFFICIENT_DRBG_STRENGTH}, - #else - {"INSUFFICIENT_DRBG_STRENGTH", 36, 139}, - #endif - #ifdef RAND_R_INTERNAL_ERROR - {"INTERNAL_ERROR", ERR_LIB_RAND, RAND_R_INTERNAL_ERROR}, - #else - {"INTERNAL_ERROR", 36, 113}, - #endif - #ifdef RAND_R_INVALID_PROPERTY_QUERY - {"INVALID_PROPERTY_QUERY", ERR_LIB_RAND, RAND_R_INVALID_PROPERTY_QUERY}, - #else - {"INVALID_PROPERTY_QUERY", 36, 137}, - #endif - #ifdef RAND_R_IN_ERROR_STATE - {"IN_ERROR_STATE", ERR_LIB_RAND, RAND_R_IN_ERROR_STATE}, - #else - {"IN_ERROR_STATE", 36, 114}, - #endif - #ifdef RAND_R_NOT_A_REGULAR_FILE - {"NOT_A_REGULAR_FILE", ERR_LIB_RAND, RAND_R_NOT_A_REGULAR_FILE}, - #else - {"NOT_A_REGULAR_FILE", 36, 122}, - #endif - #ifdef RAND_R_NOT_INSTANTIATED - {"NOT_INSTANTIATED", ERR_LIB_RAND, RAND_R_NOT_INSTANTIATED}, - #else - {"NOT_INSTANTIATED", 36, 115}, - #endif - #ifdef RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED - {"NO_DRBG_IMPLEMENTATION_SELECTED", ERR_LIB_RAND, RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED}, - #else - {"NO_DRBG_IMPLEMENTATION_SELECTED", 36, 128}, - #endif - #ifdef RAND_R_PARENT_LOCKING_NOT_ENABLED - {"PARENT_LOCKING_NOT_ENABLED", ERR_LIB_RAND, RAND_R_PARENT_LOCKING_NOT_ENABLED}, - #else - {"PARENT_LOCKING_NOT_ENABLED", 36, 130}, - #endif - #ifdef RAND_R_PARENT_STRENGTH_TOO_WEAK - {"PARENT_STRENGTH_TOO_WEAK", ERR_LIB_RAND, RAND_R_PARENT_STRENGTH_TOO_WEAK}, - #else - {"PARENT_STRENGTH_TOO_WEAK", 36, 131}, - #endif - #ifdef RAND_R_PERSONALISATION_STRING_TOO_LONG - {"PERSONALISATION_STRING_TOO_LONG", ERR_LIB_RAND, RAND_R_PERSONALISATION_STRING_TOO_LONG}, - #else - {"PERSONALISATION_STRING_TOO_LONG", 36, 116}, - #endif - #ifdef RAND_R_PREDICTION_RESISTANCE_NOT_SUPPORTED - {"PREDICTION_RESISTANCE_NOT_SUPPORTED", ERR_LIB_RAND, RAND_R_PREDICTION_RESISTANCE_NOT_SUPPORTED}, - #else - {"PREDICTION_RESISTANCE_NOT_SUPPORTED", 36, 133}, - #endif - #ifdef RAND_R_PRNG_NOT_SEEDED - {"PRNG_NOT_SEEDED", ERR_LIB_RAND, RAND_R_PRNG_NOT_SEEDED}, - #else - {"PRNG_NOT_SEEDED", 36, 100}, - #endif - #ifdef RAND_R_RANDOM_POOL_OVERFLOW - {"RANDOM_POOL_OVERFLOW", ERR_LIB_RAND, RAND_R_RANDOM_POOL_OVERFLOW}, - #else - {"RANDOM_POOL_OVERFLOW", 36, 125}, - #endif - #ifdef RAND_R_RANDOM_POOL_UNDERFLOW - {"RANDOM_POOL_UNDERFLOW", ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW}, - #else - {"RANDOM_POOL_UNDERFLOW", 36, 134}, - #endif - #ifdef RAND_R_REQUEST_TOO_LARGE_FOR_DRBG - {"REQUEST_TOO_LARGE_FOR_DRBG", ERR_LIB_RAND, RAND_R_REQUEST_TOO_LARGE_FOR_DRBG}, - #else - {"REQUEST_TOO_LARGE_FOR_DRBG", 36, 117}, - #endif - #ifdef RAND_R_RESEED_ERROR - {"RESEED_ERROR", ERR_LIB_RAND, RAND_R_RESEED_ERROR}, - #else - {"RESEED_ERROR", 36, 118}, - #endif - #ifdef RAND_R_SELFTEST_FAILURE - {"SELFTEST_FAILURE", ERR_LIB_RAND, RAND_R_SELFTEST_FAILURE}, - #else - {"SELFTEST_FAILURE", 36, 119}, - #endif - #ifdef RAND_R_TOO_LITTLE_NONCE_REQUESTED - {"TOO_LITTLE_NONCE_REQUESTED", ERR_LIB_RAND, RAND_R_TOO_LITTLE_NONCE_REQUESTED}, - #else - {"TOO_LITTLE_NONCE_REQUESTED", 36, 135}, - #endif - #ifdef RAND_R_TOO_MUCH_NONCE_REQUESTED - {"TOO_MUCH_NONCE_REQUESTED", ERR_LIB_RAND, RAND_R_TOO_MUCH_NONCE_REQUESTED}, - #else - {"TOO_MUCH_NONCE_REQUESTED", 36, 136}, - #endif - #ifdef RAND_R_UNABLE_TO_CREATE_DRBG - {"UNABLE_TO_CREATE_DRBG", ERR_LIB_RAND, RAND_R_UNABLE_TO_CREATE_DRBG}, - #else - {"UNABLE_TO_CREATE_DRBG", 36, 143}, - #endif - #ifdef RAND_R_UNABLE_TO_FETCH_DRBG - {"UNABLE_TO_FETCH_DRBG", ERR_LIB_RAND, RAND_R_UNABLE_TO_FETCH_DRBG}, - #else - {"UNABLE_TO_FETCH_DRBG", 36, 144}, - #endif - #ifdef RAND_R_UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER - {"UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER", ERR_LIB_RAND, RAND_R_UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER}, - #else - {"UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER", 36, 141}, - #endif - #ifdef RAND_R_UNABLE_TO_GET_PARENT_STRENGTH - {"UNABLE_TO_GET_PARENT_STRENGTH", ERR_LIB_RAND, RAND_R_UNABLE_TO_GET_PARENT_STRENGTH}, - #else - {"UNABLE_TO_GET_PARENT_STRENGTH", 36, 138}, - #endif - #ifdef RAND_R_UNABLE_TO_LOCK_PARENT - {"UNABLE_TO_LOCK_PARENT", ERR_LIB_RAND, RAND_R_UNABLE_TO_LOCK_PARENT}, - #else - {"UNABLE_TO_LOCK_PARENT", 36, 140}, - #endif - #ifdef RAND_R_UNSUPPORTED_DRBG_FLAGS - {"UNSUPPORTED_DRBG_FLAGS", ERR_LIB_RAND, RAND_R_UNSUPPORTED_DRBG_FLAGS}, - #else - {"UNSUPPORTED_DRBG_FLAGS", 36, 132}, - #endif - #ifdef RAND_R_UNSUPPORTED_DRBG_TYPE - {"UNSUPPORTED_DRBG_TYPE", ERR_LIB_RAND, RAND_R_UNSUPPORTED_DRBG_TYPE}, - #else - {"UNSUPPORTED_DRBG_TYPE", 36, 120}, - #endif - #ifdef RSA_R_ALGORITHM_MISMATCH - {"ALGORITHM_MISMATCH", ERR_LIB_RSA, RSA_R_ALGORITHM_MISMATCH}, - #else - {"ALGORITHM_MISMATCH", 4, 100}, - #endif - #ifdef RSA_R_BAD_E_VALUE - {"BAD_E_VALUE", ERR_LIB_RSA, RSA_R_BAD_E_VALUE}, - #else - {"BAD_E_VALUE", 4, 101}, - #endif - #ifdef RSA_R_BAD_FIXED_HEADER_DECRYPT - {"BAD_FIXED_HEADER_DECRYPT", ERR_LIB_RSA, RSA_R_BAD_FIXED_HEADER_DECRYPT}, - #else - {"BAD_FIXED_HEADER_DECRYPT", 4, 102}, - #endif - #ifdef RSA_R_BAD_PAD_BYTE_COUNT - {"BAD_PAD_BYTE_COUNT", ERR_LIB_RSA, RSA_R_BAD_PAD_BYTE_COUNT}, - #else - {"BAD_PAD_BYTE_COUNT", 4, 103}, - #endif - #ifdef RSA_R_BAD_SIGNATURE - {"BAD_SIGNATURE", ERR_LIB_RSA, RSA_R_BAD_SIGNATURE}, - #else - {"BAD_SIGNATURE", 4, 104}, - #endif - #ifdef RSA_R_BLOCK_TYPE_IS_NOT_01 - {"BLOCK_TYPE_IS_NOT_01", ERR_LIB_RSA, RSA_R_BLOCK_TYPE_IS_NOT_01}, - #else - {"BLOCK_TYPE_IS_NOT_01", 4, 106}, - #endif - #ifdef RSA_R_BLOCK_TYPE_IS_NOT_02 - {"BLOCK_TYPE_IS_NOT_02", ERR_LIB_RSA, RSA_R_BLOCK_TYPE_IS_NOT_02}, - #else - {"BLOCK_TYPE_IS_NOT_02", 4, 107}, - #endif - #ifdef RSA_R_DATA_GREATER_THAN_MOD_LEN - {"DATA_GREATER_THAN_MOD_LEN", ERR_LIB_RSA, RSA_R_DATA_GREATER_THAN_MOD_LEN}, - #else - {"DATA_GREATER_THAN_MOD_LEN", 4, 108}, - #endif - #ifdef RSA_R_DATA_TOO_LARGE - {"DATA_TOO_LARGE", ERR_LIB_RSA, RSA_R_DATA_TOO_LARGE}, - #else - {"DATA_TOO_LARGE", 4, 109}, - #endif - #ifdef RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE - {"DATA_TOO_LARGE_FOR_KEY_SIZE", ERR_LIB_RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE}, - #else - {"DATA_TOO_LARGE_FOR_KEY_SIZE", 4, 110}, - #endif - #ifdef RSA_R_DATA_TOO_LARGE_FOR_MODULUS - {"DATA_TOO_LARGE_FOR_MODULUS", ERR_LIB_RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS}, - #else - {"DATA_TOO_LARGE_FOR_MODULUS", 4, 132}, - #endif - #ifdef RSA_R_DATA_TOO_SMALL - {"DATA_TOO_SMALL", ERR_LIB_RSA, RSA_R_DATA_TOO_SMALL}, - #else - {"DATA_TOO_SMALL", 4, 111}, - #endif - #ifdef RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE - {"DATA_TOO_SMALL_FOR_KEY_SIZE", ERR_LIB_RSA, RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE}, - #else - {"DATA_TOO_SMALL_FOR_KEY_SIZE", 4, 122}, - #endif - #ifdef RSA_R_DIGEST_DOES_NOT_MATCH - {"DIGEST_DOES_NOT_MATCH", ERR_LIB_RSA, RSA_R_DIGEST_DOES_NOT_MATCH}, - #else - {"DIGEST_DOES_NOT_MATCH", 4, 158}, - #endif - #ifdef RSA_R_DIGEST_NOT_ALLOWED - {"DIGEST_NOT_ALLOWED", ERR_LIB_RSA, RSA_R_DIGEST_NOT_ALLOWED}, - #else - {"DIGEST_NOT_ALLOWED", 4, 145}, - #endif - #ifdef RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY - {"DIGEST_TOO_BIG_FOR_RSA_KEY", ERR_LIB_RSA, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY}, - #else - {"DIGEST_TOO_BIG_FOR_RSA_KEY", 4, 112}, - #endif - #ifdef RSA_R_DMP1_NOT_CONGRUENT_TO_D - {"DMP1_NOT_CONGRUENT_TO_D", ERR_LIB_RSA, RSA_R_DMP1_NOT_CONGRUENT_TO_D}, - #else - {"DMP1_NOT_CONGRUENT_TO_D", 4, 124}, - #endif - #ifdef RSA_R_DMQ1_NOT_CONGRUENT_TO_D - {"DMQ1_NOT_CONGRUENT_TO_D", ERR_LIB_RSA, RSA_R_DMQ1_NOT_CONGRUENT_TO_D}, - #else - {"DMQ1_NOT_CONGRUENT_TO_D", 4, 125}, - #endif - #ifdef RSA_R_D_E_NOT_CONGRUENT_TO_1 - {"D_E_NOT_CONGRUENT_TO_1", ERR_LIB_RSA, RSA_R_D_E_NOT_CONGRUENT_TO_1}, - #else - {"D_E_NOT_CONGRUENT_TO_1", 4, 123}, - #endif - #ifdef RSA_R_FIRST_OCTET_INVALID - {"FIRST_OCTET_INVALID", ERR_LIB_RSA, RSA_R_FIRST_OCTET_INVALID}, - #else - {"FIRST_OCTET_INVALID", 4, 133}, - #endif - #ifdef RSA_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE - {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", ERR_LIB_RSA, RSA_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE}, - #else - {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", 4, 144}, - #endif - #ifdef RSA_R_INVALID_DIGEST - {"INVALID_DIGEST", ERR_LIB_RSA, RSA_R_INVALID_DIGEST}, - #else - {"INVALID_DIGEST", 4, 157}, - #endif - #ifdef RSA_R_INVALID_DIGEST_LENGTH - {"INVALID_DIGEST_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_DIGEST_LENGTH}, - #else - {"INVALID_DIGEST_LENGTH", 4, 143}, - #endif - #ifdef RSA_R_INVALID_HEADER - {"INVALID_HEADER", ERR_LIB_RSA, RSA_R_INVALID_HEADER}, - #else - {"INVALID_HEADER", 4, 137}, - #endif - #ifdef RSA_R_INVALID_KEYPAIR - {"INVALID_KEYPAIR", ERR_LIB_RSA, RSA_R_INVALID_KEYPAIR}, - #else - {"INVALID_KEYPAIR", 4, 171}, - #endif - #ifdef RSA_R_INVALID_KEY_LENGTH - {"INVALID_KEY_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_KEY_LENGTH}, - #else - {"INVALID_KEY_LENGTH", 4, 173}, - #endif - #ifdef RSA_R_INVALID_LABEL - {"INVALID_LABEL", ERR_LIB_RSA, RSA_R_INVALID_LABEL}, - #else - {"INVALID_LABEL", 4, 160}, - #endif - #ifdef RSA_R_INVALID_LENGTH - {"INVALID_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_LENGTH}, - #else - {"INVALID_LENGTH", 4, 181}, - #endif - #ifdef RSA_R_INVALID_MESSAGE_LENGTH - {"INVALID_MESSAGE_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_MESSAGE_LENGTH}, - #else - {"INVALID_MESSAGE_LENGTH", 4, 131}, - #endif - #ifdef RSA_R_INVALID_MGF1_MD - {"INVALID_MGF1_MD", ERR_LIB_RSA, RSA_R_INVALID_MGF1_MD}, - #else - {"INVALID_MGF1_MD", 4, 156}, - #endif - #ifdef RSA_R_INVALID_MODULUS - {"INVALID_MODULUS", ERR_LIB_RSA, RSA_R_INVALID_MODULUS}, - #else - {"INVALID_MODULUS", 4, 174}, - #endif - #ifdef RSA_R_INVALID_MULTI_PRIME_KEY - {"INVALID_MULTI_PRIME_KEY", ERR_LIB_RSA, RSA_R_INVALID_MULTI_PRIME_KEY}, - #else - {"INVALID_MULTI_PRIME_KEY", 4, 167}, - #endif - #ifdef RSA_R_INVALID_OAEP_PARAMETERS - {"INVALID_OAEP_PARAMETERS", ERR_LIB_RSA, RSA_R_INVALID_OAEP_PARAMETERS}, - #else - {"INVALID_OAEP_PARAMETERS", 4, 161}, - #endif - #ifdef RSA_R_INVALID_PADDING - {"INVALID_PADDING", ERR_LIB_RSA, RSA_R_INVALID_PADDING}, - #else - {"INVALID_PADDING", 4, 138}, - #endif - #ifdef RSA_R_INVALID_PADDING_MODE - {"INVALID_PADDING_MODE", ERR_LIB_RSA, RSA_R_INVALID_PADDING_MODE}, - #else - {"INVALID_PADDING_MODE", 4, 141}, - #endif - #ifdef RSA_R_INVALID_PSS_PARAMETERS - {"INVALID_PSS_PARAMETERS", ERR_LIB_RSA, RSA_R_INVALID_PSS_PARAMETERS}, - #else - {"INVALID_PSS_PARAMETERS", 4, 149}, - #endif - #ifdef RSA_R_INVALID_PSS_SALTLEN - {"INVALID_PSS_SALTLEN", ERR_LIB_RSA, RSA_R_INVALID_PSS_SALTLEN}, - #else - {"INVALID_PSS_SALTLEN", 4, 146}, - #endif - #ifdef RSA_R_INVALID_REQUEST - {"INVALID_REQUEST", ERR_LIB_RSA, RSA_R_INVALID_REQUEST}, - #else - {"INVALID_REQUEST", 4, 175}, - #endif - #ifdef RSA_R_INVALID_SALT_LENGTH - {"INVALID_SALT_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_SALT_LENGTH}, - #else - {"INVALID_SALT_LENGTH", 4, 150}, - #endif - #ifdef RSA_R_INVALID_STRENGTH - {"INVALID_STRENGTH", ERR_LIB_RSA, RSA_R_INVALID_STRENGTH}, - #else - {"INVALID_STRENGTH", 4, 176}, - #endif - #ifdef RSA_R_INVALID_TRAILER - {"INVALID_TRAILER", ERR_LIB_RSA, RSA_R_INVALID_TRAILER}, - #else - {"INVALID_TRAILER", 4, 139}, - #endif - #ifdef RSA_R_INVALID_X931_DIGEST - {"INVALID_X931_DIGEST", ERR_LIB_RSA, RSA_R_INVALID_X931_DIGEST}, - #else - {"INVALID_X931_DIGEST", 4, 142}, - #endif - #ifdef RSA_R_IQMP_NOT_INVERSE_OF_Q - {"IQMP_NOT_INVERSE_OF_Q", ERR_LIB_RSA, RSA_R_IQMP_NOT_INVERSE_OF_Q}, - #else - {"IQMP_NOT_INVERSE_OF_Q", 4, 126}, - #endif - #ifdef RSA_R_KEY_PRIME_NUM_INVALID - {"KEY_PRIME_NUM_INVALID", ERR_LIB_RSA, RSA_R_KEY_PRIME_NUM_INVALID}, - #else - {"KEY_PRIME_NUM_INVALID", 4, 165}, - #endif - #ifdef RSA_R_KEY_SIZE_TOO_SMALL - {"KEY_SIZE_TOO_SMALL", ERR_LIB_RSA, RSA_R_KEY_SIZE_TOO_SMALL}, - #else - {"KEY_SIZE_TOO_SMALL", 4, 120}, - #endif - #ifdef RSA_R_LAST_OCTET_INVALID - {"LAST_OCTET_INVALID", ERR_LIB_RSA, RSA_R_LAST_OCTET_INVALID}, - #else - {"LAST_OCTET_INVALID", 4, 134}, - #endif - #ifdef RSA_R_MGF1_DIGEST_NOT_ALLOWED - {"MGF1_DIGEST_NOT_ALLOWED", ERR_LIB_RSA, RSA_R_MGF1_DIGEST_NOT_ALLOWED}, - #else - {"MGF1_DIGEST_NOT_ALLOWED", 4, 152}, - #endif - #ifdef RSA_R_MISSING_PRIVATE_KEY - {"MISSING_PRIVATE_KEY", ERR_LIB_RSA, RSA_R_MISSING_PRIVATE_KEY}, - #else - {"MISSING_PRIVATE_KEY", 4, 179}, - #endif - #ifdef RSA_R_MODULUS_TOO_LARGE - {"MODULUS_TOO_LARGE", ERR_LIB_RSA, RSA_R_MODULUS_TOO_LARGE}, - #else - {"MODULUS_TOO_LARGE", 4, 105}, - #endif - #ifdef RSA_R_MP_COEFFICIENT_NOT_INVERSE_OF_R - {"MP_COEFFICIENT_NOT_INVERSE_OF_R", ERR_LIB_RSA, RSA_R_MP_COEFFICIENT_NOT_INVERSE_OF_R}, - #else - {"MP_COEFFICIENT_NOT_INVERSE_OF_R", 4, 168}, - #endif - #ifdef RSA_R_MP_EXPONENT_NOT_CONGRUENT_TO_D - {"MP_EXPONENT_NOT_CONGRUENT_TO_D", ERR_LIB_RSA, RSA_R_MP_EXPONENT_NOT_CONGRUENT_TO_D}, - #else - {"MP_EXPONENT_NOT_CONGRUENT_TO_D", 4, 169}, - #endif - #ifdef RSA_R_MP_R_NOT_PRIME - {"MP_R_NOT_PRIME", ERR_LIB_RSA, RSA_R_MP_R_NOT_PRIME}, - #else - {"MP_R_NOT_PRIME", 4, 170}, - #endif - #ifdef RSA_R_NO_PUBLIC_EXPONENT - {"NO_PUBLIC_EXPONENT", ERR_LIB_RSA, RSA_R_NO_PUBLIC_EXPONENT}, - #else - {"NO_PUBLIC_EXPONENT", 4, 140}, - #endif - #ifdef RSA_R_NULL_BEFORE_BLOCK_MISSING - {"NULL_BEFORE_BLOCK_MISSING", ERR_LIB_RSA, RSA_R_NULL_BEFORE_BLOCK_MISSING}, - #else - {"NULL_BEFORE_BLOCK_MISSING", 4, 113}, - #endif - #ifdef RSA_R_N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES - {"N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES", ERR_LIB_RSA, RSA_R_N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES}, - #else - {"N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES", 4, 172}, - #endif - #ifdef RSA_R_N_DOES_NOT_EQUAL_P_Q - {"N_DOES_NOT_EQUAL_P_Q", ERR_LIB_RSA, RSA_R_N_DOES_NOT_EQUAL_P_Q}, - #else - {"N_DOES_NOT_EQUAL_P_Q", 4, 127}, - #endif - #ifdef RSA_R_OAEP_DECODING_ERROR - {"OAEP_DECODING_ERROR", ERR_LIB_RSA, RSA_R_OAEP_DECODING_ERROR}, - #else - {"OAEP_DECODING_ERROR", 4, 121}, - #endif - #ifdef RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE - {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", ERR_LIB_RSA, RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE}, - #else - {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", 4, 148}, - #endif - #ifdef RSA_R_PADDING_CHECK_FAILED - {"PADDING_CHECK_FAILED", ERR_LIB_RSA, RSA_R_PADDING_CHECK_FAILED}, - #else - {"PADDING_CHECK_FAILED", 4, 114}, - #endif - #ifdef RSA_R_PAIRWISE_TEST_FAILURE - {"PAIRWISE_TEST_FAILURE", ERR_LIB_RSA, RSA_R_PAIRWISE_TEST_FAILURE}, - #else - {"PAIRWISE_TEST_FAILURE", 4, 177}, - #endif - #ifdef RSA_R_PKCS_DECODING_ERROR - {"PKCS_DECODING_ERROR", ERR_LIB_RSA, RSA_R_PKCS_DECODING_ERROR}, - #else - {"PKCS_DECODING_ERROR", 4, 159}, - #endif - #ifdef RSA_R_PSS_SALTLEN_TOO_SMALL - {"PSS_SALTLEN_TOO_SMALL", ERR_LIB_RSA, RSA_R_PSS_SALTLEN_TOO_SMALL}, - #else - {"PSS_SALTLEN_TOO_SMALL", 4, 164}, - #endif - #ifdef RSA_R_PUB_EXPONENT_OUT_OF_RANGE - {"PUB_EXPONENT_OUT_OF_RANGE", ERR_LIB_RSA, RSA_R_PUB_EXPONENT_OUT_OF_RANGE}, - #else - {"PUB_EXPONENT_OUT_OF_RANGE", 4, 178}, - #endif - #ifdef RSA_R_P_NOT_PRIME - {"P_NOT_PRIME", ERR_LIB_RSA, RSA_R_P_NOT_PRIME}, - #else - {"P_NOT_PRIME", 4, 128}, - #endif - #ifdef RSA_R_Q_NOT_PRIME - {"Q_NOT_PRIME", ERR_LIB_RSA, RSA_R_Q_NOT_PRIME}, - #else - {"Q_NOT_PRIME", 4, 129}, - #endif - #ifdef RSA_R_RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT - {"RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT", ERR_LIB_RSA, RSA_R_RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT}, - #else - {"RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT", 4, 180}, - #endif - #ifdef RSA_R_RSA_OPERATIONS_NOT_SUPPORTED - {"RSA_OPERATIONS_NOT_SUPPORTED", ERR_LIB_RSA, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED}, - #else - {"RSA_OPERATIONS_NOT_SUPPORTED", 4, 130}, - #endif - #ifdef RSA_R_SLEN_CHECK_FAILED - {"SLEN_CHECK_FAILED", ERR_LIB_RSA, RSA_R_SLEN_CHECK_FAILED}, - #else - {"SLEN_CHECK_FAILED", 4, 136}, - #endif - #ifdef RSA_R_SLEN_RECOVERY_FAILED - {"SLEN_RECOVERY_FAILED", ERR_LIB_RSA, RSA_R_SLEN_RECOVERY_FAILED}, - #else - {"SLEN_RECOVERY_FAILED", 4, 135}, - #endif - #ifdef RSA_R_SSLV3_ROLLBACK_ATTACK - {"SSLV3_ROLLBACK_ATTACK", ERR_LIB_RSA, RSA_R_SSLV3_ROLLBACK_ATTACK}, - #else - {"SSLV3_ROLLBACK_ATTACK", 4, 115}, - #endif - #ifdef RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD - {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", ERR_LIB_RSA, RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD}, - #else - {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", 4, 116}, - #endif - #ifdef RSA_R_UNKNOWN_ALGORITHM_TYPE - {"UNKNOWN_ALGORITHM_TYPE", ERR_LIB_RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE}, - #else - {"UNKNOWN_ALGORITHM_TYPE", 4, 117}, - #endif - #ifdef RSA_R_UNKNOWN_DIGEST - {"UNKNOWN_DIGEST", ERR_LIB_RSA, RSA_R_UNKNOWN_DIGEST}, - #else - {"UNKNOWN_DIGEST", 4, 166}, - #endif - #ifdef RSA_R_UNKNOWN_MASK_DIGEST - {"UNKNOWN_MASK_DIGEST", ERR_LIB_RSA, RSA_R_UNKNOWN_MASK_DIGEST}, - #else - {"UNKNOWN_MASK_DIGEST", 4, 151}, - #endif - #ifdef RSA_R_UNKNOWN_PADDING_TYPE - {"UNKNOWN_PADDING_TYPE", ERR_LIB_RSA, RSA_R_UNKNOWN_PADDING_TYPE}, - #else - {"UNKNOWN_PADDING_TYPE", 4, 118}, - #endif - #ifdef RSA_R_UNSUPPORTED_ENCRYPTION_TYPE - {"UNSUPPORTED_ENCRYPTION_TYPE", ERR_LIB_RSA, RSA_R_UNSUPPORTED_ENCRYPTION_TYPE}, - #else - {"UNSUPPORTED_ENCRYPTION_TYPE", 4, 162}, - #endif - #ifdef RSA_R_UNSUPPORTED_LABEL_SOURCE - {"UNSUPPORTED_LABEL_SOURCE", ERR_LIB_RSA, RSA_R_UNSUPPORTED_LABEL_SOURCE}, - #else - {"UNSUPPORTED_LABEL_SOURCE", 4, 163}, - #endif - #ifdef RSA_R_UNSUPPORTED_MASK_ALGORITHM - {"UNSUPPORTED_MASK_ALGORITHM", ERR_LIB_RSA, RSA_R_UNSUPPORTED_MASK_ALGORITHM}, - #else - {"UNSUPPORTED_MASK_ALGORITHM", 4, 153}, - #endif - #ifdef RSA_R_UNSUPPORTED_MASK_PARAMETER - {"UNSUPPORTED_MASK_PARAMETER", ERR_LIB_RSA, RSA_R_UNSUPPORTED_MASK_PARAMETER}, - #else - {"UNSUPPORTED_MASK_PARAMETER", 4, 154}, - #endif - #ifdef RSA_R_UNSUPPORTED_SIGNATURE_TYPE - {"UNSUPPORTED_SIGNATURE_TYPE", ERR_LIB_RSA, RSA_R_UNSUPPORTED_SIGNATURE_TYPE}, - #else - {"UNSUPPORTED_SIGNATURE_TYPE", 4, 155}, - #endif - #ifdef RSA_R_VALUE_MISSING - {"VALUE_MISSING", ERR_LIB_RSA, RSA_R_VALUE_MISSING}, - #else - {"VALUE_MISSING", 4, 147}, - #endif - #ifdef RSA_R_WRONG_SIGNATURE_LENGTH - {"WRONG_SIGNATURE_LENGTH", ERR_LIB_RSA, RSA_R_WRONG_SIGNATURE_LENGTH}, - #else - {"WRONG_SIGNATURE_LENGTH", 4, 119}, - #endif - #ifdef SM2_R_ASN1_ERROR - {"ASN1_ERROR", ERR_LIB_SM2, SM2_R_ASN1_ERROR}, - #else - {"ASN1_ERROR", 53, 100}, - #endif - #ifdef SM2_R_BAD_SIGNATURE - {"BAD_SIGNATURE", ERR_LIB_SM2, SM2_R_BAD_SIGNATURE}, - #else - {"BAD_SIGNATURE", 53, 101}, - #endif - #ifdef SM2_R_BUFFER_TOO_SMALL - {"BUFFER_TOO_SMALL", ERR_LIB_SM2, SM2_R_BUFFER_TOO_SMALL}, - #else - {"BUFFER_TOO_SMALL", 53, 107}, - #endif - #ifdef SM2_R_DIST_ID_TOO_LARGE - {"DIST_ID_TOO_LARGE", ERR_LIB_SM2, SM2_R_DIST_ID_TOO_LARGE}, - #else - {"DIST_ID_TOO_LARGE", 53, 110}, - #endif - #ifdef SM2_R_ID_NOT_SET - {"ID_NOT_SET", ERR_LIB_SM2, SM2_R_ID_NOT_SET}, - #else - {"ID_NOT_SET", 53, 112}, - #endif - #ifdef SM2_R_ID_TOO_LARGE - {"ID_TOO_LARGE", ERR_LIB_SM2, SM2_R_ID_TOO_LARGE}, - #else - {"ID_TOO_LARGE", 53, 111}, - #endif - #ifdef SM2_R_INVALID_CURVE - {"INVALID_CURVE", ERR_LIB_SM2, SM2_R_INVALID_CURVE}, - #else - {"INVALID_CURVE", 53, 108}, - #endif - #ifdef SM2_R_INVALID_DIGEST - {"INVALID_DIGEST", ERR_LIB_SM2, SM2_R_INVALID_DIGEST}, - #else - {"INVALID_DIGEST", 53, 102}, - #endif - #ifdef SM2_R_INVALID_DIGEST_TYPE - {"INVALID_DIGEST_TYPE", ERR_LIB_SM2, SM2_R_INVALID_DIGEST_TYPE}, - #else - {"INVALID_DIGEST_TYPE", 53, 103}, - #endif - #ifdef SM2_R_INVALID_ENCODING - {"INVALID_ENCODING", ERR_LIB_SM2, SM2_R_INVALID_ENCODING}, - #else - {"INVALID_ENCODING", 53, 104}, - #endif - #ifdef SM2_R_INVALID_FIELD - {"INVALID_FIELD", ERR_LIB_SM2, SM2_R_INVALID_FIELD}, - #else - {"INVALID_FIELD", 53, 105}, - #endif - #ifdef SM2_R_INVALID_PRIVATE_KEY - {"INVALID_PRIVATE_KEY", ERR_LIB_SM2, SM2_R_INVALID_PRIVATE_KEY}, - #else - {"INVALID_PRIVATE_KEY", 53, 113}, - #endif - #ifdef SM2_R_NO_PARAMETERS_SET - {"NO_PARAMETERS_SET", ERR_LIB_SM2, SM2_R_NO_PARAMETERS_SET}, - #else - {"NO_PARAMETERS_SET", 53, 109}, - #endif - #ifdef SM2_R_USER_ID_TOO_LARGE - {"USER_ID_TOO_LARGE", ERR_LIB_SM2, SM2_R_USER_ID_TOO_LARGE}, - #else - {"USER_ID_TOO_LARGE", 53, 106}, - #endif - #ifdef SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY - {"APPLICATION_DATA_AFTER_CLOSE_NOTIFY", ERR_LIB_SSL, SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY}, - #else - {"APPLICATION_DATA_AFTER_CLOSE_NOTIFY", 20, 291}, - #endif - #ifdef SSL_R_APP_DATA_IN_HANDSHAKE - {"APP_DATA_IN_HANDSHAKE", ERR_LIB_SSL, SSL_R_APP_DATA_IN_HANDSHAKE}, - #else - {"APP_DATA_IN_HANDSHAKE", 20, 100}, - #endif - #ifdef SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT - {"ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT", ERR_LIB_SSL, SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT}, - #else - {"ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT", 20, 272}, - #endif - #ifdef SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE - {"AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE", ERR_LIB_SSL, SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE}, - #else - {"AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE", 20, 158}, - #endif - #ifdef SSL_R_BAD_CERTIFICATE - {"BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_BAD_CERTIFICATE}, - #else - {"BAD_CERTIFICATE", 20, 348}, - #endif - #ifdef SSL_R_BAD_CHANGE_CIPHER_SPEC - {"BAD_CHANGE_CIPHER_SPEC", ERR_LIB_SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC}, - #else - {"BAD_CHANGE_CIPHER_SPEC", 20, 103}, - #endif - #ifdef SSL_R_BAD_CIPHER - {"BAD_CIPHER", ERR_LIB_SSL, SSL_R_BAD_CIPHER}, - #else - {"BAD_CIPHER", 20, 186}, - #endif - #ifdef SSL_R_BAD_COMPRESSION_ALGORITHM - {"BAD_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_BAD_COMPRESSION_ALGORITHM}, - #else - {"BAD_COMPRESSION_ALGORITHM", 20, 326}, - #endif - #ifdef SSL_R_BAD_DATA - {"BAD_DATA", ERR_LIB_SSL, SSL_R_BAD_DATA}, - #else - {"BAD_DATA", 20, 390}, - #endif - #ifdef SSL_R_BAD_DATA_RETURNED_BY_CALLBACK - {"BAD_DATA_RETURNED_BY_CALLBACK", ERR_LIB_SSL, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK}, - #else - {"BAD_DATA_RETURNED_BY_CALLBACK", 20, 106}, - #endif - #ifdef SSL_R_BAD_DECOMPRESSION - {"BAD_DECOMPRESSION", ERR_LIB_SSL, SSL_R_BAD_DECOMPRESSION}, - #else - {"BAD_DECOMPRESSION", 20, 107}, - #endif - #ifdef SSL_R_BAD_DH_VALUE - {"BAD_DH_VALUE", ERR_LIB_SSL, SSL_R_BAD_DH_VALUE}, - #else - {"BAD_DH_VALUE", 20, 102}, - #endif - #ifdef SSL_R_BAD_DIGEST_LENGTH - {"BAD_DIGEST_LENGTH", ERR_LIB_SSL, SSL_R_BAD_DIGEST_LENGTH}, - #else - {"BAD_DIGEST_LENGTH", 20, 111}, - #endif - #ifdef SSL_R_BAD_EARLY_DATA - {"BAD_EARLY_DATA", ERR_LIB_SSL, SSL_R_BAD_EARLY_DATA}, - #else - {"BAD_EARLY_DATA", 20, 233}, - #endif - #ifdef SSL_R_BAD_ECC_CERT - {"BAD_ECC_CERT", ERR_LIB_SSL, SSL_R_BAD_ECC_CERT}, - #else - {"BAD_ECC_CERT", 20, 304}, - #endif - #ifdef SSL_R_BAD_ECPOINT - {"BAD_ECPOINT", ERR_LIB_SSL, SSL_R_BAD_ECPOINT}, - #else - {"BAD_ECPOINT", 20, 306}, - #endif - #ifdef SSL_R_BAD_EXTENSION - {"BAD_EXTENSION", ERR_LIB_SSL, SSL_R_BAD_EXTENSION}, - #else - {"BAD_EXTENSION", 20, 110}, - #endif - #ifdef SSL_R_BAD_HANDSHAKE_LENGTH - {"BAD_HANDSHAKE_LENGTH", ERR_LIB_SSL, SSL_R_BAD_HANDSHAKE_LENGTH}, - #else - {"BAD_HANDSHAKE_LENGTH", 20, 332}, - #endif - #ifdef SSL_R_BAD_HANDSHAKE_STATE - {"BAD_HANDSHAKE_STATE", ERR_LIB_SSL, SSL_R_BAD_HANDSHAKE_STATE}, - #else - {"BAD_HANDSHAKE_STATE", 20, 236}, - #endif - #ifdef SSL_R_BAD_HELLO_REQUEST - {"BAD_HELLO_REQUEST", ERR_LIB_SSL, SSL_R_BAD_HELLO_REQUEST}, - #else - {"BAD_HELLO_REQUEST", 20, 105}, - #endif - #ifdef SSL_R_BAD_HRR_VERSION - {"BAD_HRR_VERSION", ERR_LIB_SSL, SSL_R_BAD_HRR_VERSION}, - #else - {"BAD_HRR_VERSION", 20, 263}, - #endif - #ifdef SSL_R_BAD_KEY_SHARE - {"BAD_KEY_SHARE", ERR_LIB_SSL, SSL_R_BAD_KEY_SHARE}, - #else - {"BAD_KEY_SHARE", 20, 108}, - #endif - #ifdef SSL_R_BAD_KEY_UPDATE - {"BAD_KEY_UPDATE", ERR_LIB_SSL, SSL_R_BAD_KEY_UPDATE}, - #else - {"BAD_KEY_UPDATE", 20, 122}, - #endif - #ifdef SSL_R_BAD_LEGACY_VERSION - {"BAD_LEGACY_VERSION", ERR_LIB_SSL, SSL_R_BAD_LEGACY_VERSION}, - #else - {"BAD_LEGACY_VERSION", 20, 292}, - #endif - #ifdef SSL_R_BAD_LENGTH - {"BAD_LENGTH", ERR_LIB_SSL, SSL_R_BAD_LENGTH}, - #else - {"BAD_LENGTH", 20, 271}, - #endif - #ifdef SSL_R_BAD_PACKET - {"BAD_PACKET", ERR_LIB_SSL, SSL_R_BAD_PACKET}, - #else - {"BAD_PACKET", 20, 240}, - #endif - #ifdef SSL_R_BAD_PACKET_LENGTH - {"BAD_PACKET_LENGTH", ERR_LIB_SSL, SSL_R_BAD_PACKET_LENGTH}, - #else - {"BAD_PACKET_LENGTH", 20, 115}, - #endif - #ifdef SSL_R_BAD_PROTOCOL_VERSION_NUMBER - {"BAD_PROTOCOL_VERSION_NUMBER", ERR_LIB_SSL, SSL_R_BAD_PROTOCOL_VERSION_NUMBER}, - #else - {"BAD_PROTOCOL_VERSION_NUMBER", 20, 116}, - #endif - #ifdef SSL_R_BAD_PSK - {"BAD_PSK", ERR_LIB_SSL, SSL_R_BAD_PSK}, - #else - {"BAD_PSK", 20, 219}, - #endif - #ifdef SSL_R_BAD_PSK_IDENTITY - {"BAD_PSK_IDENTITY", ERR_LIB_SSL, SSL_R_BAD_PSK_IDENTITY}, - #else - {"BAD_PSK_IDENTITY", 20, 114}, - #endif - #ifdef SSL_R_BAD_RECORD_TYPE - {"BAD_RECORD_TYPE", ERR_LIB_SSL, SSL_R_BAD_RECORD_TYPE}, - #else - {"BAD_RECORD_TYPE", 20, 443}, - #endif - #ifdef SSL_R_BAD_RSA_ENCRYPT - {"BAD_RSA_ENCRYPT", ERR_LIB_SSL, SSL_R_BAD_RSA_ENCRYPT}, - #else - {"BAD_RSA_ENCRYPT", 20, 119}, - #endif - #ifdef SSL_R_BAD_SIGNATURE - {"BAD_SIGNATURE", ERR_LIB_SSL, SSL_R_BAD_SIGNATURE}, - #else - {"BAD_SIGNATURE", 20, 123}, - #endif - #ifdef SSL_R_BAD_SRP_A_LENGTH - {"BAD_SRP_A_LENGTH", ERR_LIB_SSL, SSL_R_BAD_SRP_A_LENGTH}, - #else - {"BAD_SRP_A_LENGTH", 20, 347}, - #endif - #ifdef SSL_R_BAD_SRP_PARAMETERS - {"BAD_SRP_PARAMETERS", ERR_LIB_SSL, SSL_R_BAD_SRP_PARAMETERS}, - #else - {"BAD_SRP_PARAMETERS", 20, 371}, - #endif - #ifdef SSL_R_BAD_SRTP_MKI_VALUE - {"BAD_SRTP_MKI_VALUE", ERR_LIB_SSL, SSL_R_BAD_SRTP_MKI_VALUE}, - #else - {"BAD_SRTP_MKI_VALUE", 20, 352}, - #endif - #ifdef SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST - {"BAD_SRTP_PROTECTION_PROFILE_LIST", ERR_LIB_SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST}, - #else - {"BAD_SRTP_PROTECTION_PROFILE_LIST", 20, 353}, - #endif - #ifdef SSL_R_BAD_SSL_FILETYPE - {"BAD_SSL_FILETYPE", ERR_LIB_SSL, SSL_R_BAD_SSL_FILETYPE}, - #else - {"BAD_SSL_FILETYPE", 20, 124}, - #endif - #ifdef SSL_R_BAD_VALUE - {"BAD_VALUE", ERR_LIB_SSL, SSL_R_BAD_VALUE}, - #else - {"BAD_VALUE", 20, 384}, - #endif - #ifdef SSL_R_BAD_WRITE_RETRY - {"BAD_WRITE_RETRY", ERR_LIB_SSL, SSL_R_BAD_WRITE_RETRY}, - #else - {"BAD_WRITE_RETRY", 20, 127}, - #endif - #ifdef SSL_R_BINDER_DOES_NOT_VERIFY - {"BINDER_DOES_NOT_VERIFY", ERR_LIB_SSL, SSL_R_BINDER_DOES_NOT_VERIFY}, - #else - {"BINDER_DOES_NOT_VERIFY", 20, 253}, - #endif - #ifdef SSL_R_BIO_NOT_SET - {"BIO_NOT_SET", ERR_LIB_SSL, SSL_R_BIO_NOT_SET}, - #else - {"BIO_NOT_SET", 20, 128}, - #endif - #ifdef SSL_R_BLOCK_CIPHER_PAD_IS_WRONG - {"BLOCK_CIPHER_PAD_IS_WRONG", ERR_LIB_SSL, SSL_R_BLOCK_CIPHER_PAD_IS_WRONG}, - #else - {"BLOCK_CIPHER_PAD_IS_WRONG", 20, 129}, - #endif - #ifdef SSL_R_BN_LIB - {"BN_LIB", ERR_LIB_SSL, SSL_R_BN_LIB}, - #else - {"BN_LIB", 20, 130}, - #endif - #ifdef SSL_R_CALLBACK_FAILED - {"CALLBACK_FAILED", ERR_LIB_SSL, SSL_R_CALLBACK_FAILED}, - #else - {"CALLBACK_FAILED", 20, 234}, - #endif - #ifdef SSL_R_CANNOT_CHANGE_CIPHER - {"CANNOT_CHANGE_CIPHER", ERR_LIB_SSL, SSL_R_CANNOT_CHANGE_CIPHER}, - #else - {"CANNOT_CHANGE_CIPHER", 20, 109}, - #endif - #ifdef SSL_R_CANNOT_GET_GROUP_NAME - {"CANNOT_GET_GROUP_NAME", ERR_LIB_SSL, SSL_R_CANNOT_GET_GROUP_NAME}, - #else - {"CANNOT_GET_GROUP_NAME", 20, 299}, - #endif - #ifdef SSL_R_CA_DN_LENGTH_MISMATCH - {"CA_DN_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_CA_DN_LENGTH_MISMATCH}, - #else - {"CA_DN_LENGTH_MISMATCH", 20, 131}, - #endif - #ifdef SSL_R_CA_KEY_TOO_SMALL - {"CA_KEY_TOO_SMALL", ERR_LIB_SSL, SSL_R_CA_KEY_TOO_SMALL}, - #else - {"CA_KEY_TOO_SMALL", 20, 397}, - #endif - #ifdef SSL_R_CA_MD_TOO_WEAK - {"CA_MD_TOO_WEAK", ERR_LIB_SSL, SSL_R_CA_MD_TOO_WEAK}, - #else - {"CA_MD_TOO_WEAK", 20, 398}, - #endif - #ifdef SSL_R_CCS_RECEIVED_EARLY - {"CCS_RECEIVED_EARLY", ERR_LIB_SSL, SSL_R_CCS_RECEIVED_EARLY}, - #else - {"CCS_RECEIVED_EARLY", 20, 133}, - #endif - #ifdef SSL_R_CERTIFICATE_VERIFY_FAILED - {"CERTIFICATE_VERIFY_FAILED", ERR_LIB_SSL, SSL_R_CERTIFICATE_VERIFY_FAILED}, - #else - {"CERTIFICATE_VERIFY_FAILED", 20, 134}, - #endif - #ifdef SSL_R_CERT_CB_ERROR - {"CERT_CB_ERROR", ERR_LIB_SSL, SSL_R_CERT_CB_ERROR}, - #else - {"CERT_CB_ERROR", 20, 377}, - #endif - #ifdef SSL_R_CERT_LENGTH_MISMATCH - {"CERT_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_CERT_LENGTH_MISMATCH}, - #else - {"CERT_LENGTH_MISMATCH", 20, 135}, - #endif - #ifdef SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED - {"CIPHERSUITE_DIGEST_HAS_CHANGED", ERR_LIB_SSL, SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED}, - #else - {"CIPHERSUITE_DIGEST_HAS_CHANGED", 20, 218}, - #endif - #ifdef SSL_R_CIPHER_CODE_WRONG_LENGTH - {"CIPHER_CODE_WRONG_LENGTH", ERR_LIB_SSL, SSL_R_CIPHER_CODE_WRONG_LENGTH}, - #else - {"CIPHER_CODE_WRONG_LENGTH", 20, 137}, - #endif - #ifdef SSL_R_CLIENTHELLO_TLSEXT - {"CLIENTHELLO_TLSEXT", ERR_LIB_SSL, SSL_R_CLIENTHELLO_TLSEXT}, - #else - {"CLIENTHELLO_TLSEXT", 20, 226}, - #endif - #ifdef SSL_R_COMPRESSED_LENGTH_TOO_LONG - {"COMPRESSED_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_COMPRESSED_LENGTH_TOO_LONG}, - #else - {"COMPRESSED_LENGTH_TOO_LONG", 20, 140}, - #endif - #ifdef SSL_R_COMPRESSION_DISABLED - {"COMPRESSION_DISABLED", ERR_LIB_SSL, SSL_R_COMPRESSION_DISABLED}, - #else - {"COMPRESSION_DISABLED", 20, 343}, - #endif - #ifdef SSL_R_COMPRESSION_FAILURE - {"COMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_COMPRESSION_FAILURE}, - #else - {"COMPRESSION_FAILURE", 20, 141}, - #endif - #ifdef SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE - {"COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE", ERR_LIB_SSL, SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE}, - #else - {"COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE", 20, 307}, - #endif - #ifdef SSL_R_COMPRESSION_LIBRARY_ERROR - {"COMPRESSION_LIBRARY_ERROR", ERR_LIB_SSL, SSL_R_COMPRESSION_LIBRARY_ERROR}, - #else - {"COMPRESSION_LIBRARY_ERROR", 20, 142}, - #endif - #ifdef SSL_R_CONNECTION_TYPE_NOT_SET - {"CONNECTION_TYPE_NOT_SET", ERR_LIB_SSL, SSL_R_CONNECTION_TYPE_NOT_SET}, - #else - {"CONNECTION_TYPE_NOT_SET", 20, 144}, - #endif - #ifdef SSL_R_CONN_USE_ONLY - {"CONN_USE_ONLY", ERR_LIB_SSL, SSL_R_CONN_USE_ONLY}, - #else - {"CONN_USE_ONLY", 20, 356}, - #endif - #ifdef SSL_R_CONTEXT_NOT_DANE_ENABLED - {"CONTEXT_NOT_DANE_ENABLED", ERR_LIB_SSL, SSL_R_CONTEXT_NOT_DANE_ENABLED}, - #else - {"CONTEXT_NOT_DANE_ENABLED", 20, 167}, - #endif - #ifdef SSL_R_COOKIE_GEN_CALLBACK_FAILURE - {"COOKIE_GEN_CALLBACK_FAILURE", ERR_LIB_SSL, SSL_R_COOKIE_GEN_CALLBACK_FAILURE}, - #else - {"COOKIE_GEN_CALLBACK_FAILURE", 20, 400}, - #endif - #ifdef SSL_R_COOKIE_MISMATCH - {"COOKIE_MISMATCH", ERR_LIB_SSL, SSL_R_COOKIE_MISMATCH}, - #else - {"COOKIE_MISMATCH", 20, 308}, - #endif - #ifdef SSL_R_COPY_PARAMETERS_FAILED - {"COPY_PARAMETERS_FAILED", ERR_LIB_SSL, SSL_R_COPY_PARAMETERS_FAILED}, - #else - {"COPY_PARAMETERS_FAILED", 20, 296}, - #endif - #ifdef SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED - {"CUSTOM_EXT_HANDLER_ALREADY_INSTALLED", ERR_LIB_SSL, SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED}, - #else - {"CUSTOM_EXT_HANDLER_ALREADY_INSTALLED", 20, 206}, - #endif - #ifdef SSL_R_DANE_ALREADY_ENABLED - {"DANE_ALREADY_ENABLED", ERR_LIB_SSL, SSL_R_DANE_ALREADY_ENABLED}, - #else - {"DANE_ALREADY_ENABLED", 20, 172}, - #endif - #ifdef SSL_R_DANE_CANNOT_OVERRIDE_MTYPE_FULL - {"DANE_CANNOT_OVERRIDE_MTYPE_FULL", ERR_LIB_SSL, SSL_R_DANE_CANNOT_OVERRIDE_MTYPE_FULL}, - #else - {"DANE_CANNOT_OVERRIDE_MTYPE_FULL", 20, 173}, - #endif - #ifdef SSL_R_DANE_NOT_ENABLED - {"DANE_NOT_ENABLED", ERR_LIB_SSL, SSL_R_DANE_NOT_ENABLED}, - #else - {"DANE_NOT_ENABLED", 20, 175}, - #endif - #ifdef SSL_R_DANE_TLSA_BAD_CERTIFICATE - {"DANE_TLSA_BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_CERTIFICATE}, - #else - {"DANE_TLSA_BAD_CERTIFICATE", 20, 180}, - #endif - #ifdef SSL_R_DANE_TLSA_BAD_CERTIFICATE_USAGE - {"DANE_TLSA_BAD_CERTIFICATE_USAGE", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_CERTIFICATE_USAGE}, - #else - {"DANE_TLSA_BAD_CERTIFICATE_USAGE", 20, 184}, - #endif - #ifdef SSL_R_DANE_TLSA_BAD_DATA_LENGTH - {"DANE_TLSA_BAD_DATA_LENGTH", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_DATA_LENGTH}, - #else - {"DANE_TLSA_BAD_DATA_LENGTH", 20, 189}, - #endif - #ifdef SSL_R_DANE_TLSA_BAD_DIGEST_LENGTH - {"DANE_TLSA_BAD_DIGEST_LENGTH", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_DIGEST_LENGTH}, - #else - {"DANE_TLSA_BAD_DIGEST_LENGTH", 20, 192}, - #endif - #ifdef SSL_R_DANE_TLSA_BAD_MATCHING_TYPE - {"DANE_TLSA_BAD_MATCHING_TYPE", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_MATCHING_TYPE}, - #else - {"DANE_TLSA_BAD_MATCHING_TYPE", 20, 200}, - #endif - #ifdef SSL_R_DANE_TLSA_BAD_PUBLIC_KEY - {"DANE_TLSA_BAD_PUBLIC_KEY", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_PUBLIC_KEY}, - #else - {"DANE_TLSA_BAD_PUBLIC_KEY", 20, 201}, - #endif - #ifdef SSL_R_DANE_TLSA_BAD_SELECTOR - {"DANE_TLSA_BAD_SELECTOR", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_SELECTOR}, - #else - {"DANE_TLSA_BAD_SELECTOR", 20, 202}, - #endif - #ifdef SSL_R_DANE_TLSA_NULL_DATA - {"DANE_TLSA_NULL_DATA", ERR_LIB_SSL, SSL_R_DANE_TLSA_NULL_DATA}, - #else - {"DANE_TLSA_NULL_DATA", 20, 203}, - #endif - #ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED - {"DATA_BETWEEN_CCS_AND_FINISHED", ERR_LIB_SSL, SSL_R_DATA_BETWEEN_CCS_AND_FINISHED}, - #else - {"DATA_BETWEEN_CCS_AND_FINISHED", 20, 145}, - #endif - #ifdef SSL_R_DATA_LENGTH_TOO_LONG - {"DATA_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_DATA_LENGTH_TOO_LONG}, - #else - {"DATA_LENGTH_TOO_LONG", 20, 146}, - #endif - #ifdef SSL_R_DECRYPTION_FAILED - {"DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_DECRYPTION_FAILED}, - #else - {"DECRYPTION_FAILED", 20, 147}, - #endif - #ifdef SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC - {"DECRYPTION_FAILED_OR_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC}, - #else - {"DECRYPTION_FAILED_OR_BAD_RECORD_MAC", 20, 281}, - #endif - #ifdef SSL_R_DH_KEY_TOO_SMALL - {"DH_KEY_TOO_SMALL", ERR_LIB_SSL, SSL_R_DH_KEY_TOO_SMALL}, - #else - {"DH_KEY_TOO_SMALL", 20, 394}, - #endif - #ifdef SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG - {"DH_PUBLIC_VALUE_LENGTH_IS_WRONG", ERR_LIB_SSL, SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG}, - #else - {"DH_PUBLIC_VALUE_LENGTH_IS_WRONG", 20, 148}, - #endif - #ifdef SSL_R_DIGEST_CHECK_FAILED - {"DIGEST_CHECK_FAILED", ERR_LIB_SSL, SSL_R_DIGEST_CHECK_FAILED}, - #else - {"DIGEST_CHECK_FAILED", 20, 149}, - #endif - #ifdef SSL_R_DTLS_MESSAGE_TOO_BIG - {"DTLS_MESSAGE_TOO_BIG", ERR_LIB_SSL, SSL_R_DTLS_MESSAGE_TOO_BIG}, - #else - {"DTLS_MESSAGE_TOO_BIG", 20, 334}, - #endif - #ifdef SSL_R_DUPLICATE_COMPRESSION_ID - {"DUPLICATE_COMPRESSION_ID", ERR_LIB_SSL, SSL_R_DUPLICATE_COMPRESSION_ID}, - #else - {"DUPLICATE_COMPRESSION_ID", 20, 309}, - #endif - #ifdef SSL_R_ECC_CERT_NOT_FOR_SIGNING - {"ECC_CERT_NOT_FOR_SIGNING", ERR_LIB_SSL, SSL_R_ECC_CERT_NOT_FOR_SIGNING}, - #else - {"ECC_CERT_NOT_FOR_SIGNING", 20, 318}, - #endif - #ifdef SSL_R_ECDH_REQUIRED_FOR_SUITEB_MODE - {"ECDH_REQUIRED_FOR_SUITEB_MODE", ERR_LIB_SSL, SSL_R_ECDH_REQUIRED_FOR_SUITEB_MODE}, - #else - {"ECDH_REQUIRED_FOR_SUITEB_MODE", 20, 374}, - #endif - #ifdef SSL_R_EE_KEY_TOO_SMALL - {"EE_KEY_TOO_SMALL", ERR_LIB_SSL, SSL_R_EE_KEY_TOO_SMALL}, - #else - {"EE_KEY_TOO_SMALL", 20, 399}, - #endif - #ifdef SSL_R_EMPTY_RAW_PUBLIC_KEY - {"EMPTY_RAW_PUBLIC_KEY", ERR_LIB_SSL, SSL_R_EMPTY_RAW_PUBLIC_KEY}, - #else - {"EMPTY_RAW_PUBLIC_KEY", 20, 349}, - #endif - #ifdef SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST - {"EMPTY_SRTP_PROTECTION_PROFILE_LIST", ERR_LIB_SSL, SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST}, - #else - {"EMPTY_SRTP_PROTECTION_PROFILE_LIST", 20, 354}, - #endif - #ifdef SSL_R_ENCRYPTED_LENGTH_TOO_LONG - {"ENCRYPTED_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_ENCRYPTED_LENGTH_TOO_LONG}, - #else - {"ENCRYPTED_LENGTH_TOO_LONG", 20, 150}, - #endif - #ifdef SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST - {"ERROR_IN_RECEIVED_CIPHER_LIST", ERR_LIB_SSL, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST}, - #else - {"ERROR_IN_RECEIVED_CIPHER_LIST", 20, 151}, - #endif - #ifdef SSL_R_ERROR_IN_SYSTEM_DEFAULT_CONFIG - {"ERROR_IN_SYSTEM_DEFAULT_CONFIG", ERR_LIB_SSL, SSL_R_ERROR_IN_SYSTEM_DEFAULT_CONFIG}, - #else - {"ERROR_IN_SYSTEM_DEFAULT_CONFIG", 20, 419}, - #endif - #ifdef SSL_R_ERROR_SETTING_TLSA_BASE_DOMAIN - {"ERROR_SETTING_TLSA_BASE_DOMAIN", ERR_LIB_SSL, SSL_R_ERROR_SETTING_TLSA_BASE_DOMAIN}, - #else - {"ERROR_SETTING_TLSA_BASE_DOMAIN", 20, 204}, - #endif - #ifdef SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE - {"EXCEEDS_MAX_FRAGMENT_SIZE", ERR_LIB_SSL, SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE}, - #else - {"EXCEEDS_MAX_FRAGMENT_SIZE", 20, 194}, - #endif - #ifdef SSL_R_EXCESSIVE_MESSAGE_SIZE - {"EXCESSIVE_MESSAGE_SIZE", ERR_LIB_SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE}, - #else - {"EXCESSIVE_MESSAGE_SIZE", 20, 152}, - #endif - #ifdef SSL_R_EXTENSION_NOT_RECEIVED - {"EXTENSION_NOT_RECEIVED", ERR_LIB_SSL, SSL_R_EXTENSION_NOT_RECEIVED}, - #else - {"EXTENSION_NOT_RECEIVED", 20, 279}, - #endif - #ifdef SSL_R_EXTRA_DATA_IN_MESSAGE - {"EXTRA_DATA_IN_MESSAGE", ERR_LIB_SSL, SSL_R_EXTRA_DATA_IN_MESSAGE}, - #else - {"EXTRA_DATA_IN_MESSAGE", 20, 153}, - #endif - #ifdef SSL_R_EXT_LENGTH_MISMATCH - {"EXT_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_EXT_LENGTH_MISMATCH}, - #else - {"EXT_LENGTH_MISMATCH", 20, 163}, - #endif - #ifdef SSL_R_FAILED_TO_GET_PARAMETER - {"FAILED_TO_GET_PARAMETER", ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER}, - #else - {"FAILED_TO_GET_PARAMETER", 20, 316}, - #endif - #ifdef SSL_R_FAILED_TO_INIT_ASYNC - {"FAILED_TO_INIT_ASYNC", ERR_LIB_SSL, SSL_R_FAILED_TO_INIT_ASYNC}, - #else - {"FAILED_TO_INIT_ASYNC", 20, 405}, - #endif - #ifdef SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE - {"FEATURE_NEGOTIATION_NOT_COMPLETE", ERR_LIB_SSL, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE}, - #else - {"FEATURE_NEGOTIATION_NOT_COMPLETE", 20, 417}, - #endif - #ifdef SSL_R_FEATURE_NOT_RENEGOTIABLE - {"FEATURE_NOT_RENEGOTIABLE", ERR_LIB_SSL, SSL_R_FEATURE_NOT_RENEGOTIABLE}, - #else - {"FEATURE_NOT_RENEGOTIABLE", 20, 413}, - #endif - #ifdef SSL_R_FRAGMENTED_CLIENT_HELLO - {"FRAGMENTED_CLIENT_HELLO", ERR_LIB_SSL, SSL_R_FRAGMENTED_CLIENT_HELLO}, - #else - {"FRAGMENTED_CLIENT_HELLO", 20, 401}, - #endif - #ifdef SSL_R_GOT_A_FIN_BEFORE_A_CCS - {"GOT_A_FIN_BEFORE_A_CCS", ERR_LIB_SSL, SSL_R_GOT_A_FIN_BEFORE_A_CCS}, - #else - {"GOT_A_FIN_BEFORE_A_CCS", 20, 154}, - #endif - #ifdef SSL_R_HTTPS_PROXY_REQUEST - {"HTTPS_PROXY_REQUEST", ERR_LIB_SSL, SSL_R_HTTPS_PROXY_REQUEST}, - #else - {"HTTPS_PROXY_REQUEST", 20, 155}, - #endif - #ifdef SSL_R_HTTP_REQUEST - {"HTTP_REQUEST", ERR_LIB_SSL, SSL_R_HTTP_REQUEST}, - #else - {"HTTP_REQUEST", 20, 156}, - #endif - #ifdef SSL_R_ILLEGAL_POINT_COMPRESSION - {"ILLEGAL_POINT_COMPRESSION", ERR_LIB_SSL, SSL_R_ILLEGAL_POINT_COMPRESSION}, - #else - {"ILLEGAL_POINT_COMPRESSION", 20, 162}, - #endif - #ifdef SSL_R_ILLEGAL_SUITEB_DIGEST - {"ILLEGAL_SUITEB_DIGEST", ERR_LIB_SSL, SSL_R_ILLEGAL_SUITEB_DIGEST}, - #else - {"ILLEGAL_SUITEB_DIGEST", 20, 380}, - #endif - #ifdef SSL_R_INAPPROPRIATE_FALLBACK - {"INAPPROPRIATE_FALLBACK", ERR_LIB_SSL, SSL_R_INAPPROPRIATE_FALLBACK}, - #else - {"INAPPROPRIATE_FALLBACK", 20, 373}, - #endif - #ifdef SSL_R_INCONSISTENT_COMPRESSION - {"INCONSISTENT_COMPRESSION", ERR_LIB_SSL, SSL_R_INCONSISTENT_COMPRESSION}, - #else - {"INCONSISTENT_COMPRESSION", 20, 340}, - #endif - #ifdef SSL_R_INCONSISTENT_EARLY_DATA_ALPN - {"INCONSISTENT_EARLY_DATA_ALPN", ERR_LIB_SSL, SSL_R_INCONSISTENT_EARLY_DATA_ALPN}, - #else - {"INCONSISTENT_EARLY_DATA_ALPN", 20, 222}, - #endif - #ifdef SSL_R_INCONSISTENT_EARLY_DATA_SNI - {"INCONSISTENT_EARLY_DATA_SNI", ERR_LIB_SSL, SSL_R_INCONSISTENT_EARLY_DATA_SNI}, - #else - {"INCONSISTENT_EARLY_DATA_SNI", 20, 231}, - #endif - #ifdef SSL_R_INCONSISTENT_EXTMS - {"INCONSISTENT_EXTMS", ERR_LIB_SSL, SSL_R_INCONSISTENT_EXTMS}, - #else - {"INCONSISTENT_EXTMS", 20, 104}, - #endif - #ifdef SSL_R_INSUFFICIENT_SECURITY - {"INSUFFICIENT_SECURITY", ERR_LIB_SSL, SSL_R_INSUFFICIENT_SECURITY}, - #else - {"INSUFFICIENT_SECURITY", 20, 241}, - #endif - #ifdef SSL_R_INVALID_ALERT - {"INVALID_ALERT", ERR_LIB_SSL, SSL_R_INVALID_ALERT}, - #else - {"INVALID_ALERT", 20, 205}, - #endif - #ifdef SSL_R_INVALID_CCS_MESSAGE - {"INVALID_CCS_MESSAGE", ERR_LIB_SSL, SSL_R_INVALID_CCS_MESSAGE}, - #else - {"INVALID_CCS_MESSAGE", 20, 260}, - #endif - #ifdef SSL_R_INVALID_CERTIFICATE_OR_ALG - {"INVALID_CERTIFICATE_OR_ALG", ERR_LIB_SSL, SSL_R_INVALID_CERTIFICATE_OR_ALG}, - #else - {"INVALID_CERTIFICATE_OR_ALG", 20, 238}, - #endif - #ifdef SSL_R_INVALID_COMMAND - {"INVALID_COMMAND", ERR_LIB_SSL, SSL_R_INVALID_COMMAND}, - #else - {"INVALID_COMMAND", 20, 280}, - #endif - #ifdef SSL_R_INVALID_COMPRESSION_ALGORITHM - {"INVALID_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_INVALID_COMPRESSION_ALGORITHM}, - #else - {"INVALID_COMPRESSION_ALGORITHM", 20, 341}, - #endif - #ifdef SSL_R_INVALID_CONFIG - {"INVALID_CONFIG", ERR_LIB_SSL, SSL_R_INVALID_CONFIG}, - #else - {"INVALID_CONFIG", 20, 283}, - #endif - #ifdef SSL_R_INVALID_CONFIGURATION_NAME - {"INVALID_CONFIGURATION_NAME", ERR_LIB_SSL, SSL_R_INVALID_CONFIGURATION_NAME}, - #else - {"INVALID_CONFIGURATION_NAME", 20, 113}, - #endif - #ifdef SSL_R_INVALID_CONTEXT - {"INVALID_CONTEXT", ERR_LIB_SSL, SSL_R_INVALID_CONTEXT}, - #else - {"INVALID_CONTEXT", 20, 282}, - #endif - #ifdef SSL_R_INVALID_CT_VALIDATION_TYPE - {"INVALID_CT_VALIDATION_TYPE", ERR_LIB_SSL, SSL_R_INVALID_CT_VALIDATION_TYPE}, - #else - {"INVALID_CT_VALIDATION_TYPE", 20, 212}, - #endif - #ifdef SSL_R_INVALID_KEY_UPDATE_TYPE - {"INVALID_KEY_UPDATE_TYPE", ERR_LIB_SSL, SSL_R_INVALID_KEY_UPDATE_TYPE}, - #else - {"INVALID_KEY_UPDATE_TYPE", 20, 120}, - #endif - #ifdef SSL_R_INVALID_MAX_EARLY_DATA - {"INVALID_MAX_EARLY_DATA", ERR_LIB_SSL, SSL_R_INVALID_MAX_EARLY_DATA}, - #else - {"INVALID_MAX_EARLY_DATA", 20, 174}, - #endif - #ifdef SSL_R_INVALID_NULL_CMD_NAME - {"INVALID_NULL_CMD_NAME", ERR_LIB_SSL, SSL_R_INVALID_NULL_CMD_NAME}, - #else - {"INVALID_NULL_CMD_NAME", 20, 385}, - #endif - #ifdef SSL_R_INVALID_RAW_PUBLIC_KEY - {"INVALID_RAW_PUBLIC_KEY", ERR_LIB_SSL, SSL_R_INVALID_RAW_PUBLIC_KEY}, - #else - {"INVALID_RAW_PUBLIC_KEY", 20, 350}, - #endif - #ifdef SSL_R_INVALID_RECORD - {"INVALID_RECORD", ERR_LIB_SSL, SSL_R_INVALID_RECORD}, - #else - {"INVALID_RECORD", 20, 317}, - #endif - #ifdef SSL_R_INVALID_SEQUENCE_NUMBER - {"INVALID_SEQUENCE_NUMBER", ERR_LIB_SSL, SSL_R_INVALID_SEQUENCE_NUMBER}, - #else - {"INVALID_SEQUENCE_NUMBER", 20, 402}, - #endif - #ifdef SSL_R_INVALID_SERVERINFO_DATA - {"INVALID_SERVERINFO_DATA", ERR_LIB_SSL, SSL_R_INVALID_SERVERINFO_DATA}, - #else - {"INVALID_SERVERINFO_DATA", 20, 388}, - #endif - #ifdef SSL_R_INVALID_SESSION_ID - {"INVALID_SESSION_ID", ERR_LIB_SSL, SSL_R_INVALID_SESSION_ID}, - #else - {"INVALID_SESSION_ID", 20, 999}, - #endif - #ifdef SSL_R_INVALID_SRP_USERNAME - {"INVALID_SRP_USERNAME", ERR_LIB_SSL, SSL_R_INVALID_SRP_USERNAME}, - #else - {"INVALID_SRP_USERNAME", 20, 357}, - #endif - #ifdef SSL_R_INVALID_STATUS_RESPONSE - {"INVALID_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_INVALID_STATUS_RESPONSE}, - #else - {"INVALID_STATUS_RESPONSE", 20, 328}, - #endif - #ifdef SSL_R_INVALID_TICKET_KEYS_LENGTH - {"INVALID_TICKET_KEYS_LENGTH", ERR_LIB_SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH}, - #else - {"INVALID_TICKET_KEYS_LENGTH", 20, 325}, - #endif - #ifdef SSL_R_LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED - {"LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED", ERR_LIB_SSL, SSL_R_LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED}, - #else - {"LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED", 20, 333}, - #endif - #ifdef SSL_R_LENGTH_MISMATCH - {"LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_LENGTH_MISMATCH}, - #else - {"LENGTH_MISMATCH", 20, 159}, - #endif - #ifdef SSL_R_LENGTH_TOO_LONG - {"LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_LENGTH_TOO_LONG}, - #else - {"LENGTH_TOO_LONG", 20, 404}, - #endif - #ifdef SSL_R_LENGTH_TOO_SHORT - {"LENGTH_TOO_SHORT", ERR_LIB_SSL, SSL_R_LENGTH_TOO_SHORT}, - #else - {"LENGTH_TOO_SHORT", 20, 160}, - #endif - #ifdef SSL_R_LIBRARY_BUG - {"LIBRARY_BUG", ERR_LIB_SSL, SSL_R_LIBRARY_BUG}, - #else - {"LIBRARY_BUG", 20, 274}, - #endif - #ifdef SSL_R_LIBRARY_HAS_NO_CIPHERS - {"LIBRARY_HAS_NO_CIPHERS", ERR_LIB_SSL, SSL_R_LIBRARY_HAS_NO_CIPHERS}, - #else - {"LIBRARY_HAS_NO_CIPHERS", 20, 161}, - #endif - #ifdef SSL_R_MAXIMUM_ENCRYPTED_PKTS_REACHED - {"MAXIMUM_ENCRYPTED_PKTS_REACHED", ERR_LIB_SSL, SSL_R_MAXIMUM_ENCRYPTED_PKTS_REACHED}, - #else - {"MAXIMUM_ENCRYPTED_PKTS_REACHED", 20, 395}, - #endif - #ifdef SSL_R_MISSING_DSA_SIGNING_CERT - {"MISSING_DSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_DSA_SIGNING_CERT}, - #else - {"MISSING_DSA_SIGNING_CERT", 20, 165}, - #endif - #ifdef SSL_R_MISSING_ECDSA_SIGNING_CERT - {"MISSING_ECDSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_ECDSA_SIGNING_CERT}, - #else - {"MISSING_ECDSA_SIGNING_CERT", 20, 381}, - #endif - #ifdef SSL_R_MISSING_FATAL - {"MISSING_FATAL", ERR_LIB_SSL, SSL_R_MISSING_FATAL}, - #else - {"MISSING_FATAL", 20, 256}, - #endif - #ifdef SSL_R_MISSING_PARAMETERS - {"MISSING_PARAMETERS", ERR_LIB_SSL, SSL_R_MISSING_PARAMETERS}, - #else - {"MISSING_PARAMETERS", 20, 290}, - #endif - #ifdef SSL_R_MISSING_PSK_KEX_MODES_EXTENSION - {"MISSING_PSK_KEX_MODES_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_PSK_KEX_MODES_EXTENSION}, - #else - {"MISSING_PSK_KEX_MODES_EXTENSION", 20, 310}, - #endif - #ifdef SSL_R_MISSING_RSA_CERTIFICATE - {"MISSING_RSA_CERTIFICATE", ERR_LIB_SSL, SSL_R_MISSING_RSA_CERTIFICATE}, - #else - {"MISSING_RSA_CERTIFICATE", 20, 168}, - #endif - #ifdef SSL_R_MISSING_RSA_ENCRYPTING_CERT - {"MISSING_RSA_ENCRYPTING_CERT", ERR_LIB_SSL, SSL_R_MISSING_RSA_ENCRYPTING_CERT}, - #else - {"MISSING_RSA_ENCRYPTING_CERT", 20, 169}, - #endif - #ifdef SSL_R_MISSING_RSA_SIGNING_CERT - {"MISSING_RSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_RSA_SIGNING_CERT}, - #else - {"MISSING_RSA_SIGNING_CERT", 20, 170}, - #endif - #ifdef SSL_R_MISSING_SIGALGS_EXTENSION - {"MISSING_SIGALGS_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_SIGALGS_EXTENSION}, - #else - {"MISSING_SIGALGS_EXTENSION", 20, 112}, - #endif - #ifdef SSL_R_MISSING_SIGNING_CERT - {"MISSING_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_SIGNING_CERT}, - #else - {"MISSING_SIGNING_CERT", 20, 221}, - #endif - #ifdef SSL_R_MISSING_SRP_PARAM - {"MISSING_SRP_PARAM", ERR_LIB_SSL, SSL_R_MISSING_SRP_PARAM}, - #else - {"MISSING_SRP_PARAM", 20, 358}, - #endif - #ifdef SSL_R_MISSING_SUPPORTED_GROUPS_EXTENSION - {"MISSING_SUPPORTED_GROUPS_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_SUPPORTED_GROUPS_EXTENSION}, - #else - {"MISSING_SUPPORTED_GROUPS_EXTENSION", 20, 209}, - #endif - #ifdef SSL_R_MISSING_SUPPORTED_VERSIONS_EXTENSION - {"MISSING_SUPPORTED_VERSIONS_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_SUPPORTED_VERSIONS_EXTENSION}, - #else - {"MISSING_SUPPORTED_VERSIONS_EXTENSION", 20, 420}, - #endif - #ifdef SSL_R_MISSING_TMP_DH_KEY - {"MISSING_TMP_DH_KEY", ERR_LIB_SSL, SSL_R_MISSING_TMP_DH_KEY}, - #else - {"MISSING_TMP_DH_KEY", 20, 171}, - #endif - #ifdef SSL_R_MISSING_TMP_ECDH_KEY - {"MISSING_TMP_ECDH_KEY", ERR_LIB_SSL, SSL_R_MISSING_TMP_ECDH_KEY}, - #else - {"MISSING_TMP_ECDH_KEY", 20, 311}, - #endif - #ifdef SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA - {"MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA", ERR_LIB_SSL, SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA}, - #else - {"MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA", 20, 293}, - #endif - #ifdef SSL_R_NOT_ON_RECORD_BOUNDARY - {"NOT_ON_RECORD_BOUNDARY", ERR_LIB_SSL, SSL_R_NOT_ON_RECORD_BOUNDARY}, - #else - {"NOT_ON_RECORD_BOUNDARY", 20, 182}, - #endif - #ifdef SSL_R_NOT_REPLACING_CERTIFICATE - {"NOT_REPLACING_CERTIFICATE", ERR_LIB_SSL, SSL_R_NOT_REPLACING_CERTIFICATE}, - #else - {"NOT_REPLACING_CERTIFICATE", 20, 289}, - #endif - #ifdef SSL_R_NOT_SERVER - {"NOT_SERVER", ERR_LIB_SSL, SSL_R_NOT_SERVER}, - #else - {"NOT_SERVER", 20, 284}, - #endif - #ifdef SSL_R_NO_APPLICATION_PROTOCOL - {"NO_APPLICATION_PROTOCOL", ERR_LIB_SSL, SSL_R_NO_APPLICATION_PROTOCOL}, - #else - {"NO_APPLICATION_PROTOCOL", 20, 235}, - #endif - #ifdef SSL_R_NO_CERTIFICATES_RETURNED - {"NO_CERTIFICATES_RETURNED", ERR_LIB_SSL, SSL_R_NO_CERTIFICATES_RETURNED}, - #else - {"NO_CERTIFICATES_RETURNED", 20, 176}, - #endif - #ifdef SSL_R_NO_CERTIFICATE_ASSIGNED - {"NO_CERTIFICATE_ASSIGNED", ERR_LIB_SSL, SSL_R_NO_CERTIFICATE_ASSIGNED}, - #else - {"NO_CERTIFICATE_ASSIGNED", 20, 177}, - #endif - #ifdef SSL_R_NO_CERTIFICATE_SET - {"NO_CERTIFICATE_SET", ERR_LIB_SSL, SSL_R_NO_CERTIFICATE_SET}, - #else - {"NO_CERTIFICATE_SET", 20, 179}, - #endif - #ifdef SSL_R_NO_CHANGE_FOLLOWING_HRR - {"NO_CHANGE_FOLLOWING_HRR", ERR_LIB_SSL, SSL_R_NO_CHANGE_FOLLOWING_HRR}, - #else - {"NO_CHANGE_FOLLOWING_HRR", 20, 214}, - #endif - #ifdef SSL_R_NO_CIPHERS_AVAILABLE - {"NO_CIPHERS_AVAILABLE", ERR_LIB_SSL, SSL_R_NO_CIPHERS_AVAILABLE}, - #else - {"NO_CIPHERS_AVAILABLE", 20, 181}, - #endif - #ifdef SSL_R_NO_CIPHERS_SPECIFIED - {"NO_CIPHERS_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_CIPHERS_SPECIFIED}, - #else - {"NO_CIPHERS_SPECIFIED", 20, 183}, - #endif - #ifdef SSL_R_NO_CIPHER_MATCH - {"NO_CIPHER_MATCH", ERR_LIB_SSL, SSL_R_NO_CIPHER_MATCH}, - #else - {"NO_CIPHER_MATCH", 20, 185}, - #endif - #ifdef SSL_R_NO_CLIENT_CERT_METHOD - {"NO_CLIENT_CERT_METHOD", ERR_LIB_SSL, SSL_R_NO_CLIENT_CERT_METHOD}, - #else - {"NO_CLIENT_CERT_METHOD", 20, 331}, - #endif - #ifdef SSL_R_NO_COMPRESSION_SPECIFIED - {"NO_COMPRESSION_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_COMPRESSION_SPECIFIED}, - #else - {"NO_COMPRESSION_SPECIFIED", 20, 187}, - #endif - #ifdef SSL_R_NO_COOKIE_CALLBACK_SET - {"NO_COOKIE_CALLBACK_SET", ERR_LIB_SSL, SSL_R_NO_COOKIE_CALLBACK_SET}, - #else - {"NO_COOKIE_CALLBACK_SET", 20, 287}, - #endif - #ifdef SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER - {"NO_GOST_CERTIFICATE_SENT_BY_PEER", ERR_LIB_SSL, SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER}, - #else - {"NO_GOST_CERTIFICATE_SENT_BY_PEER", 20, 330}, - #endif - #ifdef SSL_R_NO_METHOD_SPECIFIED - {"NO_METHOD_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_METHOD_SPECIFIED}, - #else - {"NO_METHOD_SPECIFIED", 20, 188}, - #endif - #ifdef SSL_R_NO_PEM_EXTENSIONS - {"NO_PEM_EXTENSIONS", ERR_LIB_SSL, SSL_R_NO_PEM_EXTENSIONS}, - #else - {"NO_PEM_EXTENSIONS", 20, 389}, - #endif - #ifdef SSL_R_NO_PRIVATE_KEY_ASSIGNED - {"NO_PRIVATE_KEY_ASSIGNED", ERR_LIB_SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED}, - #else - {"NO_PRIVATE_KEY_ASSIGNED", 20, 190}, - #endif - #ifdef SSL_R_NO_PROTOCOLS_AVAILABLE - {"NO_PROTOCOLS_AVAILABLE", ERR_LIB_SSL, SSL_R_NO_PROTOCOLS_AVAILABLE}, - #else - {"NO_PROTOCOLS_AVAILABLE", 20, 191}, - #endif - #ifdef SSL_R_NO_RENEGOTIATION - {"NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_NO_RENEGOTIATION}, - #else - {"NO_RENEGOTIATION", 20, 339}, - #endif - #ifdef SSL_R_NO_REQUIRED_DIGEST - {"NO_REQUIRED_DIGEST", ERR_LIB_SSL, SSL_R_NO_REQUIRED_DIGEST}, - #else - {"NO_REQUIRED_DIGEST", 20, 324}, - #endif - #ifdef SSL_R_NO_SHARED_CIPHER - {"NO_SHARED_CIPHER", ERR_LIB_SSL, SSL_R_NO_SHARED_CIPHER}, - #else - {"NO_SHARED_CIPHER", 20, 193}, - #endif - #ifdef SSL_R_NO_SHARED_GROUPS - {"NO_SHARED_GROUPS", ERR_LIB_SSL, SSL_R_NO_SHARED_GROUPS}, - #else - {"NO_SHARED_GROUPS", 20, 410}, - #endif - #ifdef SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS - {"NO_SHARED_SIGNATURE_ALGORITHMS", ERR_LIB_SSL, SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS}, - #else - {"NO_SHARED_SIGNATURE_ALGORITHMS", 20, 376}, - #endif - #ifdef SSL_R_NO_SRTP_PROFILES - {"NO_SRTP_PROFILES", ERR_LIB_SSL, SSL_R_NO_SRTP_PROFILES}, - #else - {"NO_SRTP_PROFILES", 20, 359}, - #endif - #ifdef SSL_R_NO_STREAM - {"NO_STREAM", ERR_LIB_SSL, SSL_R_NO_STREAM}, - #else - {"NO_STREAM", 20, 355}, - #endif - #ifdef SSL_R_NO_SUITABLE_DIGEST_ALGORITHM - {"NO_SUITABLE_DIGEST_ALGORITHM", ERR_LIB_SSL, SSL_R_NO_SUITABLE_DIGEST_ALGORITHM}, - #else - {"NO_SUITABLE_DIGEST_ALGORITHM", 20, 297}, - #endif - #ifdef SSL_R_NO_SUITABLE_GROUPS - {"NO_SUITABLE_GROUPS", ERR_LIB_SSL, SSL_R_NO_SUITABLE_GROUPS}, - #else - {"NO_SUITABLE_GROUPS", 20, 295}, - #endif - #ifdef SSL_R_NO_SUITABLE_KEY_SHARE - {"NO_SUITABLE_KEY_SHARE", ERR_LIB_SSL, SSL_R_NO_SUITABLE_KEY_SHARE}, - #else - {"NO_SUITABLE_KEY_SHARE", 20, 101}, - #endif - #ifdef SSL_R_NO_SUITABLE_RECORD_LAYER - {"NO_SUITABLE_RECORD_LAYER", ERR_LIB_SSL, SSL_R_NO_SUITABLE_RECORD_LAYER}, - #else - {"NO_SUITABLE_RECORD_LAYER", 20, 322}, - #endif - #ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM - {"NO_SUITABLE_SIGNATURE_ALGORITHM", ERR_LIB_SSL, SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM}, - #else - {"NO_SUITABLE_SIGNATURE_ALGORITHM", 20, 118}, - #endif - #ifdef SSL_R_NO_VALID_SCTS - {"NO_VALID_SCTS", ERR_LIB_SSL, SSL_R_NO_VALID_SCTS}, - #else - {"NO_VALID_SCTS", 20, 216}, - #endif - #ifdef SSL_R_NO_VERIFY_COOKIE_CALLBACK - {"NO_VERIFY_COOKIE_CALLBACK", ERR_LIB_SSL, SSL_R_NO_VERIFY_COOKIE_CALLBACK}, - #else - {"NO_VERIFY_COOKIE_CALLBACK", 20, 403}, - #endif - #ifdef SSL_R_NULL_SSL_CTX - {"NULL_SSL_CTX", ERR_LIB_SSL, SSL_R_NULL_SSL_CTX}, - #else - {"NULL_SSL_CTX", 20, 195}, - #endif - #ifdef SSL_R_NULL_SSL_METHOD_PASSED - {"NULL_SSL_METHOD_PASSED", ERR_LIB_SSL, SSL_R_NULL_SSL_METHOD_PASSED}, - #else - {"NULL_SSL_METHOD_PASSED", 20, 196}, - #endif - #ifdef SSL_R_OCSP_CALLBACK_FAILURE - {"OCSP_CALLBACK_FAILURE", ERR_LIB_SSL, SSL_R_OCSP_CALLBACK_FAILURE}, - #else - {"OCSP_CALLBACK_FAILURE", 20, 305}, - #endif - #ifdef SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED - {"OLD_SESSION_CIPHER_NOT_RETURNED", ERR_LIB_SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED}, - #else - {"OLD_SESSION_CIPHER_NOT_RETURNED", 20, 197}, - #endif - #ifdef SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED - {"OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED", ERR_LIB_SSL, SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED}, - #else - {"OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED", 20, 344}, - #endif - #ifdef SSL_R_OVERFLOW_ERROR - {"OVERFLOW_ERROR", ERR_LIB_SSL, SSL_R_OVERFLOW_ERROR}, - #else - {"OVERFLOW_ERROR", 20, 237}, - #endif - #ifdef SSL_R_PACKET_LENGTH_TOO_LONG - {"PACKET_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_PACKET_LENGTH_TOO_LONG}, - #else - {"PACKET_LENGTH_TOO_LONG", 20, 198}, - #endif - #ifdef SSL_R_PARSE_TLSEXT - {"PARSE_TLSEXT", ERR_LIB_SSL, SSL_R_PARSE_TLSEXT}, - #else - {"PARSE_TLSEXT", 20, 227}, - #endif - #ifdef SSL_R_PATH_TOO_LONG - {"PATH_TOO_LONG", ERR_LIB_SSL, SSL_R_PATH_TOO_LONG}, - #else - {"PATH_TOO_LONG", 20, 270}, - #endif - #ifdef SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE - {"PEER_DID_NOT_RETURN_A_CERTIFICATE", ERR_LIB_SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE}, - #else - {"PEER_DID_NOT_RETURN_A_CERTIFICATE", 20, 199}, - #endif - #ifdef SSL_R_PEM_NAME_BAD_PREFIX - {"PEM_NAME_BAD_PREFIX", ERR_LIB_SSL, SSL_R_PEM_NAME_BAD_PREFIX}, - #else - {"PEM_NAME_BAD_PREFIX", 20, 391}, - #endif - #ifdef SSL_R_PEM_NAME_TOO_SHORT - {"PEM_NAME_TOO_SHORT", ERR_LIB_SSL, SSL_R_PEM_NAME_TOO_SHORT}, - #else - {"PEM_NAME_TOO_SHORT", 20, 392}, - #endif - #ifdef SSL_R_PIPELINE_FAILURE - {"PIPELINE_FAILURE", ERR_LIB_SSL, SSL_R_PIPELINE_FAILURE}, - #else - {"PIPELINE_FAILURE", 20, 406}, - #endif - #ifdef SSL_R_POLL_REQUEST_NOT_SUPPORTED - {"POLL_REQUEST_NOT_SUPPORTED", ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED}, - #else - {"POLL_REQUEST_NOT_SUPPORTED", 20, 418}, - #endif - #ifdef SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR - {"POST_HANDSHAKE_AUTH_ENCODING_ERR", ERR_LIB_SSL, SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR}, - #else - {"POST_HANDSHAKE_AUTH_ENCODING_ERR", 20, 278}, - #endif - #ifdef SSL_R_PRIVATE_KEY_MISMATCH - {"PRIVATE_KEY_MISMATCH", ERR_LIB_SSL, SSL_R_PRIVATE_KEY_MISMATCH}, - #else - {"PRIVATE_KEY_MISMATCH", 20, 288}, - #endif - #ifdef SSL_R_PROTOCOL_IS_SHUTDOWN - {"PROTOCOL_IS_SHUTDOWN", ERR_LIB_SSL, SSL_R_PROTOCOL_IS_SHUTDOWN}, - #else - {"PROTOCOL_IS_SHUTDOWN", 20, 207}, - #endif - #ifdef SSL_R_PSK_IDENTITY_NOT_FOUND - {"PSK_IDENTITY_NOT_FOUND", ERR_LIB_SSL, SSL_R_PSK_IDENTITY_NOT_FOUND}, - #else - {"PSK_IDENTITY_NOT_FOUND", 20, 223}, - #endif - #ifdef SSL_R_PSK_NO_CLIENT_CB - {"PSK_NO_CLIENT_CB", ERR_LIB_SSL, SSL_R_PSK_NO_CLIENT_CB}, - #else - {"PSK_NO_CLIENT_CB", 20, 224}, - #endif - #ifdef SSL_R_PSK_NO_SERVER_CB - {"PSK_NO_SERVER_CB", ERR_LIB_SSL, SSL_R_PSK_NO_SERVER_CB}, - #else - {"PSK_NO_SERVER_CB", 20, 225}, - #endif - #ifdef SSL_R_QUIC_HANDSHAKE_LAYER_ERROR - {"QUIC_HANDSHAKE_LAYER_ERROR", ERR_LIB_SSL, SSL_R_QUIC_HANDSHAKE_LAYER_ERROR}, - #else - {"QUIC_HANDSHAKE_LAYER_ERROR", 20, 393}, - #endif - #ifdef SSL_R_QUIC_NETWORK_ERROR - {"QUIC_NETWORK_ERROR", ERR_LIB_SSL, SSL_R_QUIC_NETWORK_ERROR}, - #else - {"QUIC_NETWORK_ERROR", 20, 387}, - #endif - #ifdef SSL_R_QUIC_PROTOCOL_ERROR - {"QUIC_PROTOCOL_ERROR", ERR_LIB_SSL, SSL_R_QUIC_PROTOCOL_ERROR}, - #else - {"QUIC_PROTOCOL_ERROR", 20, 382}, - #endif - #ifdef SSL_R_READ_BIO_NOT_SET - {"READ_BIO_NOT_SET", ERR_LIB_SSL, SSL_R_READ_BIO_NOT_SET}, - #else - {"READ_BIO_NOT_SET", 20, 211}, - #endif - #ifdef SSL_R_READ_TIMEOUT_EXPIRED - {"READ_TIMEOUT_EXPIRED", ERR_LIB_SSL, SSL_R_READ_TIMEOUT_EXPIRED}, - #else - {"READ_TIMEOUT_EXPIRED", 20, 312}, - #endif - #ifdef SSL_R_RECORDS_NOT_RELEASED - {"RECORDS_NOT_RELEASED", ERR_LIB_SSL, SSL_R_RECORDS_NOT_RELEASED}, - #else - {"RECORDS_NOT_RELEASED", 20, 321}, - #endif - #ifdef SSL_R_RECORD_LAYER_FAILURE - {"RECORD_LAYER_FAILURE", ERR_LIB_SSL, SSL_R_RECORD_LAYER_FAILURE}, - #else - {"RECORD_LAYER_FAILURE", 20, 313}, - #endif - #ifdef SSL_R_RECORD_LENGTH_MISMATCH - {"RECORD_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_RECORD_LENGTH_MISMATCH}, - #else - {"RECORD_LENGTH_MISMATCH", 20, 213}, - #endif - #ifdef SSL_R_RECORD_TOO_SMALL - {"RECORD_TOO_SMALL", ERR_LIB_SSL, SSL_R_RECORD_TOO_SMALL}, - #else - {"RECORD_TOO_SMALL", 20, 298}, - #endif - #ifdef SSL_R_REMOTE_PEER_ADDRESS_NOT_SET - {"REMOTE_PEER_ADDRESS_NOT_SET", ERR_LIB_SSL, SSL_R_REMOTE_PEER_ADDRESS_NOT_SET}, - #else - {"REMOTE_PEER_ADDRESS_NOT_SET", 20, 346}, - #endif - #ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG - {"RENEGOTIATE_EXT_TOO_LONG", ERR_LIB_SSL, SSL_R_RENEGOTIATE_EXT_TOO_LONG}, - #else - {"RENEGOTIATE_EXT_TOO_LONG", 20, 335}, - #endif - #ifdef SSL_R_RENEGOTIATION_ENCODING_ERR - {"RENEGOTIATION_ENCODING_ERR", ERR_LIB_SSL, SSL_R_RENEGOTIATION_ENCODING_ERR}, - #else - {"RENEGOTIATION_ENCODING_ERR", 20, 336}, - #endif - #ifdef SSL_R_RENEGOTIATION_MISMATCH - {"RENEGOTIATION_MISMATCH", ERR_LIB_SSL, SSL_R_RENEGOTIATION_MISMATCH}, - #else - {"RENEGOTIATION_MISMATCH", 20, 337}, - #endif - #ifdef SSL_R_REQUEST_PENDING - {"REQUEST_PENDING", ERR_LIB_SSL, SSL_R_REQUEST_PENDING}, - #else - {"REQUEST_PENDING", 20, 285}, - #endif - #ifdef SSL_R_REQUEST_SENT - {"REQUEST_SENT", ERR_LIB_SSL, SSL_R_REQUEST_SENT}, - #else - {"REQUEST_SENT", 20, 286}, - #endif - #ifdef SSL_R_REQUIRED_CIPHER_MISSING - {"REQUIRED_CIPHER_MISSING", ERR_LIB_SSL, SSL_R_REQUIRED_CIPHER_MISSING}, - #else - {"REQUIRED_CIPHER_MISSING", 20, 215}, - #endif - #ifdef SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING - {"REQUIRED_COMPRESSION_ALGORITHM_MISSING", ERR_LIB_SSL, SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING}, - #else - {"REQUIRED_COMPRESSION_ALGORITHM_MISSING", 20, 342}, - #endif - #ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING - {"SCSV_RECEIVED_WHEN_RENEGOTIATING", ERR_LIB_SSL, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING}, - #else - {"SCSV_RECEIVED_WHEN_RENEGOTIATING", 20, 345}, - #endif - #ifdef SSL_R_SCT_VERIFICATION_FAILED - {"SCT_VERIFICATION_FAILED", ERR_LIB_SSL, SSL_R_SCT_VERIFICATION_FAILED}, - #else - {"SCT_VERIFICATION_FAILED", 20, 208}, - #endif - #ifdef SSL_R_SEQUENCE_CTR_WRAPPED - {"SEQUENCE_CTR_WRAPPED", ERR_LIB_SSL, SSL_R_SEQUENCE_CTR_WRAPPED}, - #else - {"SEQUENCE_CTR_WRAPPED", 20, 327}, - #endif - #ifdef SSL_R_SERVERHELLO_TLSEXT - {"SERVERHELLO_TLSEXT", ERR_LIB_SSL, SSL_R_SERVERHELLO_TLSEXT}, - #else - {"SERVERHELLO_TLSEXT", 20, 275}, - #endif - #ifdef SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED - {"SESSION_ID_CONTEXT_UNINITIALIZED", ERR_LIB_SSL, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED}, - #else - {"SESSION_ID_CONTEXT_UNINITIALIZED", 20, 277}, - #endif - #ifdef SSL_R_SHUTDOWN_WHILE_IN_INIT - {"SHUTDOWN_WHILE_IN_INIT", ERR_LIB_SSL, SSL_R_SHUTDOWN_WHILE_IN_INIT}, - #else - {"SHUTDOWN_WHILE_IN_INIT", 20, 407}, - #endif - #ifdef SSL_R_SIGNATURE_ALGORITHMS_ERROR - {"SIGNATURE_ALGORITHMS_ERROR", ERR_LIB_SSL, SSL_R_SIGNATURE_ALGORITHMS_ERROR}, - #else - {"SIGNATURE_ALGORITHMS_ERROR", 20, 360}, - #endif - #ifdef SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE - {"SIGNATURE_FOR_NON_SIGNING_CERTIFICATE", ERR_LIB_SSL, SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE}, - #else - {"SIGNATURE_FOR_NON_SIGNING_CERTIFICATE", 20, 220}, - #endif - #ifdef SSL_R_SRP_A_CALC - {"SRP_A_CALC", ERR_LIB_SSL, SSL_R_SRP_A_CALC}, - #else - {"SRP_A_CALC", 20, 361}, - #endif - #ifdef SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES - {"SRTP_COULD_NOT_ALLOCATE_PROFILES", ERR_LIB_SSL, SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES}, - #else - {"SRTP_COULD_NOT_ALLOCATE_PROFILES", 20, 362}, - #endif - #ifdef SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG - {"SRTP_PROTECTION_PROFILE_LIST_TOO_LONG", ERR_LIB_SSL, SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG}, - #else - {"SRTP_PROTECTION_PROFILE_LIST_TOO_LONG", 20, 363}, - #endif - #ifdef SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE - {"SRTP_UNKNOWN_PROTECTION_PROFILE", ERR_LIB_SSL, SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE}, - #else - {"SRTP_UNKNOWN_PROTECTION_PROFILE", 20, 364}, - #endif - #ifdef SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH - {"SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH", ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH}, - #else - {"SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH", 20, 232}, - #endif - #ifdef SSL_R_SSL3_EXT_INVALID_SERVERNAME - {"SSL3_EXT_INVALID_SERVERNAME", ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME}, - #else - {"SSL3_EXT_INVALID_SERVERNAME", 20, 319}, - #endif - #ifdef SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE - {"SSL3_EXT_INVALID_SERVERNAME_TYPE", ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE}, - #else - {"SSL3_EXT_INVALID_SERVERNAME_TYPE", 20, 320}, - #endif - #ifdef SSL_R_SSL3_SESSION_ID_TOO_LONG - {"SSL3_SESSION_ID_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL3_SESSION_ID_TOO_LONG}, - #else - {"SSL3_SESSION_ID_TOO_LONG", 20, 300}, - #endif - #ifdef SSL_R_SSLV3_ALERT_BAD_CERTIFICATE - {"SSLV3_ALERT_BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE}, - #else - {"SSLV3_ALERT_BAD_CERTIFICATE", 20, 1042}, - #endif - #ifdef SSL_R_SSLV3_ALERT_BAD_CERTIFICATE - {"SSLV3_ALERT_BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE}, - #else - {"SSLV3_ALERT_BAD_CERTIFICATE", 20, 1042}, - #endif - #ifdef SSL_R_SSLV3_ALERT_BAD_RECORD_MAC - {"SSLV3_ALERT_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_BAD_RECORD_MAC}, - #else - {"SSLV3_ALERT_BAD_RECORD_MAC", 20, 1020}, - #endif - #ifdef SSL_R_SSLV3_ALERT_BAD_RECORD_MAC - {"SSLV3_ALERT_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_BAD_RECORD_MAC}, - #else - {"SSLV3_ALERT_BAD_RECORD_MAC", 20, 1020}, - #endif - #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED - {"SSLV3_ALERT_CERTIFICATE_EXPIRED", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED}, - #else - {"SSLV3_ALERT_CERTIFICATE_EXPIRED", 20, 1045}, - #endif - #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED - {"SSLV3_ALERT_CERTIFICATE_EXPIRED", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED}, - #else - {"SSLV3_ALERT_CERTIFICATE_EXPIRED", 20, 1045}, - #endif - #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED - {"SSLV3_ALERT_CERTIFICATE_REVOKED", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED}, - #else - {"SSLV3_ALERT_CERTIFICATE_REVOKED", 20, 1044}, - #endif - #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED - {"SSLV3_ALERT_CERTIFICATE_REVOKED", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED}, - #else - {"SSLV3_ALERT_CERTIFICATE_REVOKED", 20, 1044}, - #endif - #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN - {"SSLV3_ALERT_CERTIFICATE_UNKNOWN", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN}, - #else - {"SSLV3_ALERT_CERTIFICATE_UNKNOWN", 20, 1046}, - #endif - #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN - {"SSLV3_ALERT_CERTIFICATE_UNKNOWN", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN}, - #else - {"SSLV3_ALERT_CERTIFICATE_UNKNOWN", 20, 1046}, - #endif - #ifdef SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE - {"SSLV3_ALERT_DECOMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE}, - #else - {"SSLV3_ALERT_DECOMPRESSION_FAILURE", 20, 1030}, - #endif - #ifdef SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE - {"SSLV3_ALERT_DECOMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE}, - #else - {"SSLV3_ALERT_DECOMPRESSION_FAILURE", 20, 1030}, - #endif - #ifdef SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE - {"SSLV3_ALERT_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE}, - #else - {"SSLV3_ALERT_HANDSHAKE_FAILURE", 20, 1040}, - #endif - #ifdef SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE - {"SSLV3_ALERT_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE}, - #else - {"SSLV3_ALERT_HANDSHAKE_FAILURE", 20, 1040}, - #endif - #ifdef SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER - {"SSLV3_ALERT_ILLEGAL_PARAMETER", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER}, - #else - {"SSLV3_ALERT_ILLEGAL_PARAMETER", 20, 1047}, - #endif - #ifdef SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER - {"SSLV3_ALERT_ILLEGAL_PARAMETER", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER}, - #else - {"SSLV3_ALERT_ILLEGAL_PARAMETER", 20, 1047}, - #endif - #ifdef SSL_R_SSLV3_ALERT_NO_CERTIFICATE - {"SSLV3_ALERT_NO_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_NO_CERTIFICATE}, - #else - {"SSLV3_ALERT_NO_CERTIFICATE", 20, 1041}, - #endif - #ifdef SSL_R_SSLV3_ALERT_NO_CERTIFICATE - {"SSLV3_ALERT_NO_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_NO_CERTIFICATE}, - #else - {"SSLV3_ALERT_NO_CERTIFICATE", 20, 1041}, - #endif - #ifdef SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE - {"SSLV3_ALERT_UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE}, - #else - {"SSLV3_ALERT_UNEXPECTED_MESSAGE", 20, 1010}, - #endif - #ifdef SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE - {"SSLV3_ALERT_UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE}, - #else - {"SSLV3_ALERT_UNEXPECTED_MESSAGE", 20, 1010}, - #endif - #ifdef SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE - {"SSLV3_ALERT_UNSUPPORTED_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE}, - #else - {"SSLV3_ALERT_UNSUPPORTED_CERTIFICATE", 20, 1043}, - #endif - #ifdef SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE - {"SSLV3_ALERT_UNSUPPORTED_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE}, - #else - {"SSLV3_ALERT_UNSUPPORTED_CERTIFICATE", 20, 1043}, - #endif - #ifdef SSL_R_SSL_COMMAND_SECTION_EMPTY - {"SSL_COMMAND_SECTION_EMPTY", ERR_LIB_SSL, SSL_R_SSL_COMMAND_SECTION_EMPTY}, - #else - {"SSL_COMMAND_SECTION_EMPTY", 20, 117}, - #endif - #ifdef SSL_R_SSL_COMMAND_SECTION_NOT_FOUND - {"SSL_COMMAND_SECTION_NOT_FOUND", ERR_LIB_SSL, SSL_R_SSL_COMMAND_SECTION_NOT_FOUND}, - #else - {"SSL_COMMAND_SECTION_NOT_FOUND", 20, 125}, - #endif - #ifdef SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION - {"SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION", ERR_LIB_SSL, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION}, - #else - {"SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION", 20, 228}, - #endif - #ifdef SSL_R_SSL_HANDSHAKE_FAILURE - {"SSL_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_SSL_HANDSHAKE_FAILURE}, - #else - {"SSL_HANDSHAKE_FAILURE", 20, 229}, - #endif - #ifdef SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS - {"SSL_LIBRARY_HAS_NO_CIPHERS", ERR_LIB_SSL, SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS}, - #else - {"SSL_LIBRARY_HAS_NO_CIPHERS", 20, 230}, - #endif - #ifdef SSL_R_SSL_NEGATIVE_LENGTH - {"SSL_NEGATIVE_LENGTH", ERR_LIB_SSL, SSL_R_SSL_NEGATIVE_LENGTH}, - #else - {"SSL_NEGATIVE_LENGTH", 20, 372}, - #endif - #ifdef SSL_R_SSL_SECTION_EMPTY - {"SSL_SECTION_EMPTY", ERR_LIB_SSL, SSL_R_SSL_SECTION_EMPTY}, - #else - {"SSL_SECTION_EMPTY", 20, 126}, - #endif - #ifdef SSL_R_SSL_SECTION_NOT_FOUND - {"SSL_SECTION_NOT_FOUND", ERR_LIB_SSL, SSL_R_SSL_SECTION_NOT_FOUND}, - #else - {"SSL_SECTION_NOT_FOUND", 20, 136}, - #endif - #ifdef SSL_R_SSL_SESSION_ID_CALLBACK_FAILED - {"SSL_SESSION_ID_CALLBACK_FAILED", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CALLBACK_FAILED}, - #else - {"SSL_SESSION_ID_CALLBACK_FAILED", 20, 301}, - #endif - #ifdef SSL_R_SSL_SESSION_ID_CONFLICT - {"SSL_SESSION_ID_CONFLICT", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CONFLICT}, - #else - {"SSL_SESSION_ID_CONFLICT", 20, 302}, - #endif - #ifdef SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG - {"SSL_SESSION_ID_CONTEXT_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG}, - #else - {"SSL_SESSION_ID_CONTEXT_TOO_LONG", 20, 273}, - #endif - #ifdef SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH - {"SSL_SESSION_ID_HAS_BAD_LENGTH", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH}, - #else - {"SSL_SESSION_ID_HAS_BAD_LENGTH", 20, 303}, - #endif - #ifdef SSL_R_SSL_SESSION_ID_TOO_LONG - {"SSL_SESSION_ID_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_TOO_LONG}, - #else - {"SSL_SESSION_ID_TOO_LONG", 20, 408}, - #endif - #ifdef SSL_R_SSL_SESSION_VERSION_MISMATCH - {"SSL_SESSION_VERSION_MISMATCH", ERR_LIB_SSL, SSL_R_SSL_SESSION_VERSION_MISMATCH}, - #else - {"SSL_SESSION_VERSION_MISMATCH", 20, 210}, - #endif - #ifdef SSL_R_STILL_IN_INIT - {"STILL_IN_INIT", ERR_LIB_SSL, SSL_R_STILL_IN_INIT}, - #else - {"STILL_IN_INIT", 20, 121}, - #endif - #ifdef SSL_R_STREAM_COUNT_LIMITED - {"STREAM_COUNT_LIMITED", ERR_LIB_SSL, SSL_R_STREAM_COUNT_LIMITED}, - #else - {"STREAM_COUNT_LIMITED", 20, 411}, - #endif - #ifdef SSL_R_STREAM_FINISHED - {"STREAM_FINISHED", ERR_LIB_SSL, SSL_R_STREAM_FINISHED}, - #else - {"STREAM_FINISHED", 20, 365}, - #endif - #ifdef SSL_R_STREAM_RECV_ONLY - {"STREAM_RECV_ONLY", ERR_LIB_SSL, SSL_R_STREAM_RECV_ONLY}, - #else - {"STREAM_RECV_ONLY", 20, 366}, - #endif - #ifdef SSL_R_STREAM_RESET - {"STREAM_RESET", ERR_LIB_SSL, SSL_R_STREAM_RESET}, - #else - {"STREAM_RESET", 20, 375}, - #endif - #ifdef SSL_R_STREAM_SEND_ONLY - {"STREAM_SEND_ONLY", ERR_LIB_SSL, SSL_R_STREAM_SEND_ONLY}, - #else - {"STREAM_SEND_ONLY", 20, 379}, - #endif - #ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED - {"TLSV13_ALERT_CERTIFICATE_REQUIRED", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED}, - #else - {"TLSV13_ALERT_CERTIFICATE_REQUIRED", 20, 1116}, - #endif - #ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED - {"TLSV13_ALERT_CERTIFICATE_REQUIRED", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED}, - #else - {"TLSV13_ALERT_CERTIFICATE_REQUIRED", 20, 1116}, - #endif - #ifdef SSL_R_TLSV13_ALERT_MISSING_EXTENSION - {"TLSV13_ALERT_MISSING_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_MISSING_EXTENSION}, - #else - {"TLSV13_ALERT_MISSING_EXTENSION", 20, 1109}, - #endif - #ifdef SSL_R_TLSV13_ALERT_MISSING_EXTENSION - {"TLSV13_ALERT_MISSING_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_MISSING_EXTENSION}, - #else - {"TLSV13_ALERT_MISSING_EXTENSION", 20, 1109}, - #endif - #ifdef SSL_R_TLSV1_ALERT_ACCESS_DENIED - {"TLSV1_ALERT_ACCESS_DENIED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_ACCESS_DENIED}, - #else - {"TLSV1_ALERT_ACCESS_DENIED", 20, 1049}, - #endif - #ifdef SSL_R_TLSV1_ALERT_ACCESS_DENIED - {"TLSV1_ALERT_ACCESS_DENIED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_ACCESS_DENIED}, - #else - {"TLSV1_ALERT_ACCESS_DENIED", 20, 1049}, - #endif - #ifdef SSL_R_TLSV1_ALERT_DECODE_ERROR - {"TLSV1_ALERT_DECODE_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECODE_ERROR}, - #else - {"TLSV1_ALERT_DECODE_ERROR", 20, 1050}, - #endif - #ifdef SSL_R_TLSV1_ALERT_DECODE_ERROR - {"TLSV1_ALERT_DECODE_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECODE_ERROR}, - #else - {"TLSV1_ALERT_DECODE_ERROR", 20, 1050}, - #endif - #ifdef SSL_R_TLSV1_ALERT_DECRYPTION_FAILED - {"TLSV1_ALERT_DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPTION_FAILED}, - #else - {"TLSV1_ALERT_DECRYPTION_FAILED", 20, 1021}, - #endif - #ifdef SSL_R_TLSV1_ALERT_DECRYPTION_FAILED - {"TLSV1_ALERT_DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPTION_FAILED}, - #else - {"TLSV1_ALERT_DECRYPTION_FAILED", 20, 1021}, - #endif - #ifdef SSL_R_TLSV1_ALERT_DECRYPT_ERROR - {"TLSV1_ALERT_DECRYPT_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPT_ERROR}, - #else - {"TLSV1_ALERT_DECRYPT_ERROR", 20, 1051}, - #endif - #ifdef SSL_R_TLSV1_ALERT_DECRYPT_ERROR - {"TLSV1_ALERT_DECRYPT_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPT_ERROR}, - #else - {"TLSV1_ALERT_DECRYPT_ERROR", 20, 1051}, - #endif - #ifdef SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION - {"TLSV1_ALERT_EXPORT_RESTRICTION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION}, - #else - {"TLSV1_ALERT_EXPORT_RESTRICTION", 20, 1060}, - #endif - #ifdef SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION - {"TLSV1_ALERT_EXPORT_RESTRICTION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION}, - #else - {"TLSV1_ALERT_EXPORT_RESTRICTION", 20, 1060}, - #endif - #ifdef SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK - {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK}, - #else - {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", 20, 1086}, - #endif - #ifdef SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK - {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK}, - #else - {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", 20, 1086}, - #endif - #ifdef SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY - {"TLSV1_ALERT_INSUFFICIENT_SECURITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY}, - #else - {"TLSV1_ALERT_INSUFFICIENT_SECURITY", 20, 1071}, - #endif - #ifdef SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY - {"TLSV1_ALERT_INSUFFICIENT_SECURITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY}, - #else - {"TLSV1_ALERT_INSUFFICIENT_SECURITY", 20, 1071}, - #endif - #ifdef SSL_R_TLSV1_ALERT_INTERNAL_ERROR - {"TLSV1_ALERT_INTERNAL_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INTERNAL_ERROR}, - #else - {"TLSV1_ALERT_INTERNAL_ERROR", 20, 1080}, - #endif - #ifdef SSL_R_TLSV1_ALERT_INTERNAL_ERROR - {"TLSV1_ALERT_INTERNAL_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INTERNAL_ERROR}, - #else - {"TLSV1_ALERT_INTERNAL_ERROR", 20, 1080}, - #endif - #ifdef SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL - {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL}, - #else - {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", 20, 1120}, - #endif - #ifdef SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL - {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL}, - #else - {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", 20, 1120}, - #endif - #ifdef SSL_R_TLSV1_ALERT_NO_RENEGOTIATION - {"TLSV1_ALERT_NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_RENEGOTIATION}, - #else - {"TLSV1_ALERT_NO_RENEGOTIATION", 20, 1100}, - #endif - #ifdef SSL_R_TLSV1_ALERT_NO_RENEGOTIATION - {"TLSV1_ALERT_NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_RENEGOTIATION}, - #else - {"TLSV1_ALERT_NO_RENEGOTIATION", 20, 1100}, - #endif - #ifdef SSL_R_TLSV1_ALERT_PROTOCOL_VERSION - {"TLSV1_ALERT_PROTOCOL_VERSION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_PROTOCOL_VERSION}, - #else - {"TLSV1_ALERT_PROTOCOL_VERSION", 20, 1070}, - #endif - #ifdef SSL_R_TLSV1_ALERT_PROTOCOL_VERSION - {"TLSV1_ALERT_PROTOCOL_VERSION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_PROTOCOL_VERSION}, - #else - {"TLSV1_ALERT_PROTOCOL_VERSION", 20, 1070}, - #endif - #ifdef SSL_R_TLSV1_ALERT_RECORD_OVERFLOW - {"TLSV1_ALERT_RECORD_OVERFLOW", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_RECORD_OVERFLOW}, - #else - {"TLSV1_ALERT_RECORD_OVERFLOW", 20, 1022}, - #endif - #ifdef SSL_R_TLSV1_ALERT_RECORD_OVERFLOW - {"TLSV1_ALERT_RECORD_OVERFLOW", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_RECORD_OVERFLOW}, - #else - {"TLSV1_ALERT_RECORD_OVERFLOW", 20, 1022}, - #endif - #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA - {"TLSV1_ALERT_UNKNOWN_CA", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_CA}, - #else - {"TLSV1_ALERT_UNKNOWN_CA", 20, 1048}, - #endif - #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA - {"TLSV1_ALERT_UNKNOWN_CA", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_CA}, - #else - {"TLSV1_ALERT_UNKNOWN_CA", 20, 1048}, - #endif - #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY - {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY}, - #else - {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", 20, 1115}, - #endif - #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY - {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY}, - #else - {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", 20, 1115}, - #endif - #ifdef SSL_R_TLSV1_ALERT_USER_CANCELLED - {"TLSV1_ALERT_USER_CANCELLED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_USER_CANCELLED}, - #else - {"TLSV1_ALERT_USER_CANCELLED", 20, 1090}, - #endif - #ifdef SSL_R_TLSV1_ALERT_USER_CANCELLED - {"TLSV1_ALERT_USER_CANCELLED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_USER_CANCELLED}, - #else - {"TLSV1_ALERT_USER_CANCELLED", 20, 1090}, - #endif - #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE - {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE}, - #else - {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", 20, 1114}, - #endif - #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE - {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE}, - #else - {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", 20, 1114}, - #endif - #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE - {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE}, - #else - {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", 20, 1113}, - #endif - #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE - {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE}, - #else - {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", 20, 1113}, - #endif - #ifdef SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE - {"TLSV1_CERTIFICATE_UNOBTAINABLE", ERR_LIB_SSL, SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE}, - #else - {"TLSV1_CERTIFICATE_UNOBTAINABLE", 20, 1111}, - #endif - #ifdef SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE - {"TLSV1_CERTIFICATE_UNOBTAINABLE", ERR_LIB_SSL, SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE}, - #else - {"TLSV1_CERTIFICATE_UNOBTAINABLE", 20, 1111}, - #endif - #ifdef SSL_R_TLSV1_UNRECOGNIZED_NAME - {"TLSV1_UNRECOGNIZED_NAME", ERR_LIB_SSL, SSL_R_TLSV1_UNRECOGNIZED_NAME}, - #else - {"TLSV1_UNRECOGNIZED_NAME", 20, 1112}, - #endif - #ifdef SSL_R_TLSV1_UNRECOGNIZED_NAME - {"TLSV1_UNRECOGNIZED_NAME", ERR_LIB_SSL, SSL_R_TLSV1_UNRECOGNIZED_NAME}, - #else - {"TLSV1_UNRECOGNIZED_NAME", 20, 1112}, - #endif - #ifdef SSL_R_TLSV1_UNSUPPORTED_EXTENSION - {"TLSV1_UNSUPPORTED_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV1_UNSUPPORTED_EXTENSION}, - #else - {"TLSV1_UNSUPPORTED_EXTENSION", 20, 1110}, - #endif - #ifdef SSL_R_TLSV1_UNSUPPORTED_EXTENSION - {"TLSV1_UNSUPPORTED_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV1_UNSUPPORTED_EXTENSION}, - #else - {"TLSV1_UNSUPPORTED_EXTENSION", 20, 1110}, - #endif - #ifdef SSL_R_TLS_ILLEGAL_EXPORTER_LABEL - {"TLS_ILLEGAL_EXPORTER_LABEL", ERR_LIB_SSL, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL}, - #else - {"TLS_ILLEGAL_EXPORTER_LABEL", 20, 367}, - #endif - #ifdef SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST - {"TLS_INVALID_ECPOINTFORMAT_LIST", ERR_LIB_SSL, SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST}, - #else - {"TLS_INVALID_ECPOINTFORMAT_LIST", 20, 157}, - #endif - #ifdef SSL_R_TOO_MANY_KEY_UPDATES - {"TOO_MANY_KEY_UPDATES", ERR_LIB_SSL, SSL_R_TOO_MANY_KEY_UPDATES}, - #else - {"TOO_MANY_KEY_UPDATES", 20, 132}, - #endif - #ifdef SSL_R_TOO_MANY_WARN_ALERTS - {"TOO_MANY_WARN_ALERTS", ERR_LIB_SSL, SSL_R_TOO_MANY_WARN_ALERTS}, - #else - {"TOO_MANY_WARN_ALERTS", 20, 409}, - #endif - #ifdef SSL_R_TOO_MUCH_EARLY_DATA - {"TOO_MUCH_EARLY_DATA", ERR_LIB_SSL, SSL_R_TOO_MUCH_EARLY_DATA}, - #else - {"TOO_MUCH_EARLY_DATA", 20, 164}, - #endif - #ifdef SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS - {"UNABLE_TO_FIND_ECDH_PARAMETERS", ERR_LIB_SSL, SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS}, - #else - {"UNABLE_TO_FIND_ECDH_PARAMETERS", 20, 314}, - #endif - #ifdef SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS - {"UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS", ERR_LIB_SSL, SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS}, - #else - {"UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS", 20, 239}, - #endif - #ifdef SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES - {"UNABLE_TO_LOAD_SSL3_MD5_ROUTINES", ERR_LIB_SSL, SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES}, - #else - {"UNABLE_TO_LOAD_SSL3_MD5_ROUTINES", 20, 242}, - #endif - #ifdef SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES - {"UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES", ERR_LIB_SSL, SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES}, - #else - {"UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES", 20, 243}, - #endif - #ifdef SSL_R_UNEXPECTED_CCS_MESSAGE - {"UNEXPECTED_CCS_MESSAGE", ERR_LIB_SSL, SSL_R_UNEXPECTED_CCS_MESSAGE}, - #else - {"UNEXPECTED_CCS_MESSAGE", 20, 262}, - #endif - #ifdef SSL_R_UNEXPECTED_END_OF_EARLY_DATA - {"UNEXPECTED_END_OF_EARLY_DATA", ERR_LIB_SSL, SSL_R_UNEXPECTED_END_OF_EARLY_DATA}, - #else - {"UNEXPECTED_END_OF_EARLY_DATA", 20, 178}, - #endif - #ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING - {"UNEXPECTED_EOF_WHILE_READING", ERR_LIB_SSL, SSL_R_UNEXPECTED_EOF_WHILE_READING}, - #else - {"UNEXPECTED_EOF_WHILE_READING", 20, 294}, - #endif - #ifdef SSL_R_UNEXPECTED_MESSAGE - {"UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_UNEXPECTED_MESSAGE}, - #else - {"UNEXPECTED_MESSAGE", 20, 244}, - #endif - #ifdef SSL_R_UNEXPECTED_RECORD - {"UNEXPECTED_RECORD", ERR_LIB_SSL, SSL_R_UNEXPECTED_RECORD}, - #else - {"UNEXPECTED_RECORD", 20, 245}, - #endif - #ifdef SSL_R_UNINITIALIZED - {"UNINITIALIZED", ERR_LIB_SSL, SSL_R_UNINITIALIZED}, - #else - {"UNINITIALIZED", 20, 276}, - #endif - #ifdef SSL_R_UNKNOWN_ALERT_TYPE - {"UNKNOWN_ALERT_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_ALERT_TYPE}, - #else - {"UNKNOWN_ALERT_TYPE", 20, 246}, - #endif - #ifdef SSL_R_UNKNOWN_CERTIFICATE_TYPE - {"UNKNOWN_CERTIFICATE_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE}, - #else - {"UNKNOWN_CERTIFICATE_TYPE", 20, 247}, - #endif - #ifdef SSL_R_UNKNOWN_CIPHER_RETURNED - {"UNKNOWN_CIPHER_RETURNED", ERR_LIB_SSL, SSL_R_UNKNOWN_CIPHER_RETURNED}, - #else - {"UNKNOWN_CIPHER_RETURNED", 20, 248}, - #endif - #ifdef SSL_R_UNKNOWN_CIPHER_TYPE - {"UNKNOWN_CIPHER_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_CIPHER_TYPE}, - #else - {"UNKNOWN_CIPHER_TYPE", 20, 249}, - #endif - #ifdef SSL_R_UNKNOWN_CMD_NAME - {"UNKNOWN_CMD_NAME", ERR_LIB_SSL, SSL_R_UNKNOWN_CMD_NAME}, - #else - {"UNKNOWN_CMD_NAME", 20, 386}, - #endif - #ifdef SSL_R_UNKNOWN_COMMAND - {"UNKNOWN_COMMAND", ERR_LIB_SSL, SSL_R_UNKNOWN_COMMAND}, - #else - {"UNKNOWN_COMMAND", 20, 139}, - #endif - #ifdef SSL_R_UNKNOWN_DIGEST - {"UNKNOWN_DIGEST", ERR_LIB_SSL, SSL_R_UNKNOWN_DIGEST}, - #else - {"UNKNOWN_DIGEST", 20, 368}, - #endif - #ifdef SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE - {"UNKNOWN_KEY_EXCHANGE_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE}, - #else - {"UNKNOWN_KEY_EXCHANGE_TYPE", 20, 250}, - #endif - #ifdef SSL_R_UNKNOWN_MANDATORY_PARAMETER - {"UNKNOWN_MANDATORY_PARAMETER", ERR_LIB_SSL, SSL_R_UNKNOWN_MANDATORY_PARAMETER}, - #else - {"UNKNOWN_MANDATORY_PARAMETER", 20, 323}, - #endif - #ifdef SSL_R_UNKNOWN_PKEY_TYPE - {"UNKNOWN_PKEY_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_PKEY_TYPE}, - #else - {"UNKNOWN_PKEY_TYPE", 20, 251}, - #endif - #ifdef SSL_R_UNKNOWN_PROTOCOL - {"UNKNOWN_PROTOCOL", ERR_LIB_SSL, SSL_R_UNKNOWN_PROTOCOL}, - #else - {"UNKNOWN_PROTOCOL", 20, 252}, - #endif - #ifdef SSL_R_UNKNOWN_SSL_VERSION - {"UNKNOWN_SSL_VERSION", ERR_LIB_SSL, SSL_R_UNKNOWN_SSL_VERSION}, - #else - {"UNKNOWN_SSL_VERSION", 20, 254}, - #endif - #ifdef SSL_R_UNKNOWN_STATE - {"UNKNOWN_STATE", ERR_LIB_SSL, SSL_R_UNKNOWN_STATE}, - #else - {"UNKNOWN_STATE", 20, 255}, - #endif - #ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED - {"UNSAFE_LEGACY_RENEGOTIATION_DISABLED", ERR_LIB_SSL, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED}, - #else - {"UNSAFE_LEGACY_RENEGOTIATION_DISABLED", 20, 338}, - #endif - #ifdef SSL_R_UNSOLICITED_EXTENSION - {"UNSOLICITED_EXTENSION", ERR_LIB_SSL, SSL_R_UNSOLICITED_EXTENSION}, - #else - {"UNSOLICITED_EXTENSION", 20, 217}, - #endif - #ifdef SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM - {"UNSUPPORTED_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM}, - #else - {"UNSUPPORTED_COMPRESSION_ALGORITHM", 20, 257}, - #endif - #ifdef SSL_R_UNSUPPORTED_CONFIG_VALUE - {"UNSUPPORTED_CONFIG_VALUE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_CONFIG_VALUE}, - #else - {"UNSUPPORTED_CONFIG_VALUE", 20, 414}, - #endif - #ifdef SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS - {"UNSUPPORTED_CONFIG_VALUE_CLASS", ERR_LIB_SSL, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS}, - #else - {"UNSUPPORTED_CONFIG_VALUE_CLASS", 20, 415}, - #endif - #ifdef SSL_R_UNSUPPORTED_CONFIG_VALUE_OP - {"UNSUPPORTED_CONFIG_VALUE_OP", ERR_LIB_SSL, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP}, - #else - {"UNSUPPORTED_CONFIG_VALUE_OP", 20, 416}, - #endif - #ifdef SSL_R_UNSUPPORTED_ELLIPTIC_CURVE - {"UNSUPPORTED_ELLIPTIC_CURVE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE}, - #else - {"UNSUPPORTED_ELLIPTIC_CURVE", 20, 315}, - #endif - #ifdef SSL_R_UNSUPPORTED_PROTOCOL - {"UNSUPPORTED_PROTOCOL", ERR_LIB_SSL, SSL_R_UNSUPPORTED_PROTOCOL}, - #else - {"UNSUPPORTED_PROTOCOL", 20, 258}, - #endif - #ifdef SSL_R_UNSUPPORTED_SSL_VERSION - {"UNSUPPORTED_SSL_VERSION", ERR_LIB_SSL, SSL_R_UNSUPPORTED_SSL_VERSION}, - #else - {"UNSUPPORTED_SSL_VERSION", 20, 259}, - #endif - #ifdef SSL_R_UNSUPPORTED_STATUS_TYPE - {"UNSUPPORTED_STATUS_TYPE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_STATUS_TYPE}, - #else - {"UNSUPPORTED_STATUS_TYPE", 20, 329}, - #endif - #ifdef SSL_R_UNSUPPORTED_WRITE_FLAG - {"UNSUPPORTED_WRITE_FLAG", ERR_LIB_SSL, SSL_R_UNSUPPORTED_WRITE_FLAG}, - #else - {"UNSUPPORTED_WRITE_FLAG", 20, 412}, - #endif - #ifdef SSL_R_USE_SRTP_NOT_NEGOTIATED - {"USE_SRTP_NOT_NEGOTIATED", ERR_LIB_SSL, SSL_R_USE_SRTP_NOT_NEGOTIATED}, - #else - {"USE_SRTP_NOT_NEGOTIATED", 20, 369}, - #endif - #ifdef SSL_R_VERSION_TOO_HIGH - {"VERSION_TOO_HIGH", ERR_LIB_SSL, SSL_R_VERSION_TOO_HIGH}, - #else - {"VERSION_TOO_HIGH", 20, 166}, - #endif - #ifdef SSL_R_VERSION_TOO_LOW - {"VERSION_TOO_LOW", ERR_LIB_SSL, SSL_R_VERSION_TOO_LOW}, - #else - {"VERSION_TOO_LOW", 20, 396}, - #endif - #ifdef SSL_R_WRONG_CERTIFICATE_TYPE - {"WRONG_CERTIFICATE_TYPE", ERR_LIB_SSL, SSL_R_WRONG_CERTIFICATE_TYPE}, - #else - {"WRONG_CERTIFICATE_TYPE", 20, 383}, - #endif - #ifdef SSL_R_WRONG_CIPHER_RETURNED - {"WRONG_CIPHER_RETURNED", ERR_LIB_SSL, SSL_R_WRONG_CIPHER_RETURNED}, - #else - {"WRONG_CIPHER_RETURNED", 20, 261}, - #endif - #ifdef SSL_R_WRONG_CURVE - {"WRONG_CURVE", ERR_LIB_SSL, SSL_R_WRONG_CURVE}, - #else - {"WRONG_CURVE", 20, 378}, - #endif - #ifdef SSL_R_WRONG_RPK_TYPE - {"WRONG_RPK_TYPE", ERR_LIB_SSL, SSL_R_WRONG_RPK_TYPE}, - #else - {"WRONG_RPK_TYPE", 20, 351}, - #endif - #ifdef SSL_R_WRONG_SIGNATURE_LENGTH - {"WRONG_SIGNATURE_LENGTH", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_LENGTH}, - #else - {"WRONG_SIGNATURE_LENGTH", 20, 264}, - #endif - #ifdef SSL_R_WRONG_SIGNATURE_SIZE - {"WRONG_SIGNATURE_SIZE", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_SIZE}, - #else - {"WRONG_SIGNATURE_SIZE", 20, 265}, - #endif - #ifdef SSL_R_WRONG_SIGNATURE_TYPE - {"WRONG_SIGNATURE_TYPE", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_TYPE}, - #else - {"WRONG_SIGNATURE_TYPE", 20, 370}, - #endif - #ifdef SSL_R_WRONG_SSL_VERSION - {"WRONG_SSL_VERSION", ERR_LIB_SSL, SSL_R_WRONG_SSL_VERSION}, - #else - {"WRONG_SSL_VERSION", 20, 266}, - #endif - #ifdef SSL_R_WRONG_VERSION_NUMBER - {"WRONG_VERSION_NUMBER", ERR_LIB_SSL, SSL_R_WRONG_VERSION_NUMBER}, - #else - {"WRONG_VERSION_NUMBER", 20, 267}, - #endif - #ifdef SSL_R_X509_LIB - {"X509_LIB", ERR_LIB_SSL, SSL_R_X509_LIB}, - #else - {"X509_LIB", 20, 268}, - #endif - #ifdef SSL_R_X509_VERIFICATION_SETUP_PROBLEMS - {"X509_VERIFICATION_SETUP_PROBLEMS", ERR_LIB_SSL, SSL_R_X509_VERIFICATION_SETUP_PROBLEMS}, - #else - {"X509_VERIFICATION_SETUP_PROBLEMS", 20, 269}, - #endif - #ifdef TS_R_BAD_PKCS7_TYPE - {"BAD_PKCS7_TYPE", ERR_LIB_TS, TS_R_BAD_PKCS7_TYPE}, - #else - {"BAD_PKCS7_TYPE", 47, 132}, - #endif - #ifdef TS_R_BAD_TYPE - {"BAD_TYPE", ERR_LIB_TS, TS_R_BAD_TYPE}, - #else - {"BAD_TYPE", 47, 133}, - #endif - #ifdef TS_R_CANNOT_LOAD_CERT - {"CANNOT_LOAD_CERT", ERR_LIB_TS, TS_R_CANNOT_LOAD_CERT}, - #else - {"CANNOT_LOAD_CERT", 47, 137}, - #endif - #ifdef TS_R_CANNOT_LOAD_KEY - {"CANNOT_LOAD_KEY", ERR_LIB_TS, TS_R_CANNOT_LOAD_KEY}, - #else - {"CANNOT_LOAD_KEY", 47, 138}, - #endif - #ifdef TS_R_CERTIFICATE_VERIFY_ERROR - {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_TS, TS_R_CERTIFICATE_VERIFY_ERROR}, - #else - {"CERTIFICATE_VERIFY_ERROR", 47, 100}, - #endif - #ifdef TS_R_COULD_NOT_SET_ENGINE - {"COULD_NOT_SET_ENGINE", ERR_LIB_TS, TS_R_COULD_NOT_SET_ENGINE}, - #else - {"COULD_NOT_SET_ENGINE", 47, 127}, - #endif - #ifdef TS_R_COULD_NOT_SET_TIME - {"COULD_NOT_SET_TIME", ERR_LIB_TS, TS_R_COULD_NOT_SET_TIME}, - #else - {"COULD_NOT_SET_TIME", 47, 115}, - #endif - #ifdef TS_R_DETACHED_CONTENT - {"DETACHED_CONTENT", ERR_LIB_TS, TS_R_DETACHED_CONTENT}, - #else - {"DETACHED_CONTENT", 47, 134}, - #endif - #ifdef TS_R_ESS_ADD_SIGNING_CERT_ERROR - {"ESS_ADD_SIGNING_CERT_ERROR", ERR_LIB_TS, TS_R_ESS_ADD_SIGNING_CERT_ERROR}, - #else - {"ESS_ADD_SIGNING_CERT_ERROR", 47, 116}, - #endif - #ifdef TS_R_ESS_ADD_SIGNING_CERT_V2_ERROR - {"ESS_ADD_SIGNING_CERT_V2_ERROR", ERR_LIB_TS, TS_R_ESS_ADD_SIGNING_CERT_V2_ERROR}, - #else - {"ESS_ADD_SIGNING_CERT_V2_ERROR", 47, 139}, - #endif - #ifdef TS_R_ESS_SIGNING_CERTIFICATE_ERROR - {"ESS_SIGNING_CERTIFICATE_ERROR", ERR_LIB_TS, TS_R_ESS_SIGNING_CERTIFICATE_ERROR}, - #else - {"ESS_SIGNING_CERTIFICATE_ERROR", 47, 101}, - #endif - #ifdef TS_R_INVALID_NULL_POINTER - {"INVALID_NULL_POINTER", ERR_LIB_TS, TS_R_INVALID_NULL_POINTER}, - #else - {"INVALID_NULL_POINTER", 47, 102}, - #endif - #ifdef TS_R_INVALID_SIGNER_CERTIFICATE_PURPOSE - {"INVALID_SIGNER_CERTIFICATE_PURPOSE", ERR_LIB_TS, TS_R_INVALID_SIGNER_CERTIFICATE_PURPOSE}, - #else - {"INVALID_SIGNER_CERTIFICATE_PURPOSE", 47, 117}, - #endif - #ifdef TS_R_MESSAGE_IMPRINT_MISMATCH - {"MESSAGE_IMPRINT_MISMATCH", ERR_LIB_TS, TS_R_MESSAGE_IMPRINT_MISMATCH}, - #else - {"MESSAGE_IMPRINT_MISMATCH", 47, 103}, - #endif - #ifdef TS_R_NONCE_MISMATCH - {"NONCE_MISMATCH", ERR_LIB_TS, TS_R_NONCE_MISMATCH}, - #else - {"NONCE_MISMATCH", 47, 104}, - #endif - #ifdef TS_R_NONCE_NOT_RETURNED - {"NONCE_NOT_RETURNED", ERR_LIB_TS, TS_R_NONCE_NOT_RETURNED}, - #else - {"NONCE_NOT_RETURNED", 47, 105}, - #endif - #ifdef TS_R_NO_CONTENT - {"NO_CONTENT", ERR_LIB_TS, TS_R_NO_CONTENT}, - #else - {"NO_CONTENT", 47, 106}, - #endif - #ifdef TS_R_NO_TIME_STAMP_TOKEN - {"NO_TIME_STAMP_TOKEN", ERR_LIB_TS, TS_R_NO_TIME_STAMP_TOKEN}, - #else - {"NO_TIME_STAMP_TOKEN", 47, 107}, - #endif - #ifdef TS_R_PKCS7_ADD_SIGNATURE_ERROR - {"PKCS7_ADD_SIGNATURE_ERROR", ERR_LIB_TS, TS_R_PKCS7_ADD_SIGNATURE_ERROR}, - #else - {"PKCS7_ADD_SIGNATURE_ERROR", 47, 118}, - #endif - #ifdef TS_R_PKCS7_ADD_SIGNED_ATTR_ERROR - {"PKCS7_ADD_SIGNED_ATTR_ERROR", ERR_LIB_TS, TS_R_PKCS7_ADD_SIGNED_ATTR_ERROR}, - #else - {"PKCS7_ADD_SIGNED_ATTR_ERROR", 47, 119}, - #endif - #ifdef TS_R_PKCS7_TO_TS_TST_INFO_FAILED - {"PKCS7_TO_TS_TST_INFO_FAILED", ERR_LIB_TS, TS_R_PKCS7_TO_TS_TST_INFO_FAILED}, - #else - {"PKCS7_TO_TS_TST_INFO_FAILED", 47, 129}, - #endif - #ifdef TS_R_POLICY_MISMATCH - {"POLICY_MISMATCH", ERR_LIB_TS, TS_R_POLICY_MISMATCH}, - #else - {"POLICY_MISMATCH", 47, 108}, - #endif - #ifdef TS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE - {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_TS, TS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, - #else - {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 47, 120}, - #endif - #ifdef TS_R_RESPONSE_SETUP_ERROR - {"RESPONSE_SETUP_ERROR", ERR_LIB_TS, TS_R_RESPONSE_SETUP_ERROR}, - #else - {"RESPONSE_SETUP_ERROR", 47, 121}, - #endif - #ifdef TS_R_SIGNATURE_FAILURE - {"SIGNATURE_FAILURE", ERR_LIB_TS, TS_R_SIGNATURE_FAILURE}, - #else - {"SIGNATURE_FAILURE", 47, 109}, - #endif - #ifdef TS_R_THERE_MUST_BE_ONE_SIGNER - {"THERE_MUST_BE_ONE_SIGNER", ERR_LIB_TS, TS_R_THERE_MUST_BE_ONE_SIGNER}, - #else - {"THERE_MUST_BE_ONE_SIGNER", 47, 110}, - #endif - #ifdef TS_R_TIME_SYSCALL_ERROR - {"TIME_SYSCALL_ERROR", ERR_LIB_TS, TS_R_TIME_SYSCALL_ERROR}, - #else - {"TIME_SYSCALL_ERROR", 47, 122}, - #endif - #ifdef TS_R_TOKEN_NOT_PRESENT - {"TOKEN_NOT_PRESENT", ERR_LIB_TS, TS_R_TOKEN_NOT_PRESENT}, - #else - {"TOKEN_NOT_PRESENT", 47, 130}, - #endif - #ifdef TS_R_TOKEN_PRESENT - {"TOKEN_PRESENT", ERR_LIB_TS, TS_R_TOKEN_PRESENT}, - #else - {"TOKEN_PRESENT", 47, 131}, - #endif - #ifdef TS_R_TSA_NAME_MISMATCH - {"TSA_NAME_MISMATCH", ERR_LIB_TS, TS_R_TSA_NAME_MISMATCH}, - #else - {"TSA_NAME_MISMATCH", 47, 111}, - #endif - #ifdef TS_R_TSA_UNTRUSTED - {"TSA_UNTRUSTED", ERR_LIB_TS, TS_R_TSA_UNTRUSTED}, - #else - {"TSA_UNTRUSTED", 47, 112}, - #endif - #ifdef TS_R_TST_INFO_SETUP_ERROR - {"TST_INFO_SETUP_ERROR", ERR_LIB_TS, TS_R_TST_INFO_SETUP_ERROR}, - #else - {"TST_INFO_SETUP_ERROR", 47, 123}, - #endif - #ifdef TS_R_TS_DATASIGN - {"TS_DATASIGN", ERR_LIB_TS, TS_R_TS_DATASIGN}, - #else - {"TS_DATASIGN", 47, 124}, - #endif - #ifdef TS_R_UNACCEPTABLE_POLICY - {"UNACCEPTABLE_POLICY", ERR_LIB_TS, TS_R_UNACCEPTABLE_POLICY}, - #else - {"UNACCEPTABLE_POLICY", 47, 125}, - #endif - #ifdef TS_R_UNSUPPORTED_MD_ALGORITHM - {"UNSUPPORTED_MD_ALGORITHM", ERR_LIB_TS, TS_R_UNSUPPORTED_MD_ALGORITHM}, - #else - {"UNSUPPORTED_MD_ALGORITHM", 47, 126}, - #endif - #ifdef TS_R_UNSUPPORTED_VERSION - {"UNSUPPORTED_VERSION", ERR_LIB_TS, TS_R_UNSUPPORTED_VERSION}, - #else - {"UNSUPPORTED_VERSION", 47, 113}, - #endif - #ifdef TS_R_VAR_BAD_VALUE - {"VAR_BAD_VALUE", ERR_LIB_TS, TS_R_VAR_BAD_VALUE}, - #else - {"VAR_BAD_VALUE", 47, 135}, - #endif - #ifdef TS_R_VAR_LOOKUP_FAILURE - {"VAR_LOOKUP_FAILURE", ERR_LIB_TS, TS_R_VAR_LOOKUP_FAILURE}, - #else - {"VAR_LOOKUP_FAILURE", 47, 136}, - #endif - #ifdef TS_R_WRONG_CONTENT_TYPE - {"WRONG_CONTENT_TYPE", ERR_LIB_TS, TS_R_WRONG_CONTENT_TYPE}, - #else - {"WRONG_CONTENT_TYPE", 47, 114}, - #endif - #ifdef UI_R_COMMON_OK_AND_CANCEL_CHARACTERS - {"COMMON_OK_AND_CANCEL_CHARACTERS", ERR_LIB_UI, UI_R_COMMON_OK_AND_CANCEL_CHARACTERS}, - #else - {"COMMON_OK_AND_CANCEL_CHARACTERS", 40, 104}, - #endif - #ifdef UI_R_INDEX_TOO_LARGE - {"INDEX_TOO_LARGE", ERR_LIB_UI, UI_R_INDEX_TOO_LARGE}, - #else - {"INDEX_TOO_LARGE", 40, 102}, - #endif - #ifdef UI_R_INDEX_TOO_SMALL - {"INDEX_TOO_SMALL", ERR_LIB_UI, UI_R_INDEX_TOO_SMALL}, - #else - {"INDEX_TOO_SMALL", 40, 103}, - #endif - #ifdef UI_R_NO_RESULT_BUFFER - {"NO_RESULT_BUFFER", ERR_LIB_UI, UI_R_NO_RESULT_BUFFER}, - #else - {"NO_RESULT_BUFFER", 40, 105}, - #endif - #ifdef UI_R_PROCESSING_ERROR - {"PROCESSING_ERROR", ERR_LIB_UI, UI_R_PROCESSING_ERROR}, - #else - {"PROCESSING_ERROR", 40, 107}, - #endif - #ifdef UI_R_RESULT_TOO_LARGE - {"RESULT_TOO_LARGE", ERR_LIB_UI, UI_R_RESULT_TOO_LARGE}, - #else - {"RESULT_TOO_LARGE", 40, 100}, - #endif - #ifdef UI_R_RESULT_TOO_SMALL - {"RESULT_TOO_SMALL", ERR_LIB_UI, UI_R_RESULT_TOO_SMALL}, - #else - {"RESULT_TOO_SMALL", 40, 101}, - #endif - #ifdef UI_R_SYSASSIGN_ERROR - {"SYSASSIGN_ERROR", ERR_LIB_UI, UI_R_SYSASSIGN_ERROR}, - #else - {"SYSASSIGN_ERROR", 40, 109}, - #endif - #ifdef UI_R_SYSDASSGN_ERROR - {"SYSDASSGN_ERROR", ERR_LIB_UI, UI_R_SYSDASSGN_ERROR}, - #else - {"SYSDASSGN_ERROR", 40, 110}, - #endif - #ifdef UI_R_SYSQIOW_ERROR - {"SYSQIOW_ERROR", ERR_LIB_UI, UI_R_SYSQIOW_ERROR}, - #else - {"SYSQIOW_ERROR", 40, 111}, - #endif - #ifdef UI_R_UNKNOWN_CONTROL_COMMAND - {"UNKNOWN_CONTROL_COMMAND", ERR_LIB_UI, UI_R_UNKNOWN_CONTROL_COMMAND}, - #else - {"UNKNOWN_CONTROL_COMMAND", 40, 106}, - #endif - #ifdef UI_R_UNKNOWN_TTYGET_ERRNO_VALUE - {"UNKNOWN_TTYGET_ERRNO_VALUE", ERR_LIB_UI, UI_R_UNKNOWN_TTYGET_ERRNO_VALUE}, - #else - {"UNKNOWN_TTYGET_ERRNO_VALUE", 40, 108}, - #endif - #ifdef UI_R_USER_DATA_DUPLICATION_UNSUPPORTED - {"USER_DATA_DUPLICATION_UNSUPPORTED", ERR_LIB_UI, UI_R_USER_DATA_DUPLICATION_UNSUPPORTED}, - #else - {"USER_DATA_DUPLICATION_UNSUPPORTED", 40, 112}, - #endif - #ifdef X509V3_R_BAD_IP_ADDRESS - {"BAD_IP_ADDRESS", ERR_LIB_X509V3, X509V3_R_BAD_IP_ADDRESS}, - #else - {"BAD_IP_ADDRESS", 34, 118}, - #endif - #ifdef X509V3_R_BAD_OBJECT - {"BAD_OBJECT", ERR_LIB_X509V3, X509V3_R_BAD_OBJECT}, - #else - {"BAD_OBJECT", 34, 119}, - #endif - #ifdef X509V3_R_BAD_OPTION - {"BAD_OPTION", ERR_LIB_X509V3, X509V3_R_BAD_OPTION}, - #else - {"BAD_OPTION", 34, 170}, - #endif - #ifdef X509V3_R_BAD_VALUE - {"BAD_VALUE", ERR_LIB_X509V3, X509V3_R_BAD_VALUE}, - #else - {"BAD_VALUE", 34, 171}, - #endif - #ifdef X509V3_R_BN_DEC2BN_ERROR - {"BN_DEC2BN_ERROR", ERR_LIB_X509V3, X509V3_R_BN_DEC2BN_ERROR}, - #else - {"BN_DEC2BN_ERROR", 34, 100}, - #endif - #ifdef X509V3_R_BN_TO_ASN1_INTEGER_ERROR - {"BN_TO_ASN1_INTEGER_ERROR", ERR_LIB_X509V3, X509V3_R_BN_TO_ASN1_INTEGER_ERROR}, - #else - {"BN_TO_ASN1_INTEGER_ERROR", 34, 101}, - #endif - #ifdef X509V3_R_DIRNAME_ERROR - {"DIRNAME_ERROR", ERR_LIB_X509V3, X509V3_R_DIRNAME_ERROR}, - #else - {"DIRNAME_ERROR", 34, 149}, - #endif - #ifdef X509V3_R_DISTPOINT_ALREADY_SET - {"DISTPOINT_ALREADY_SET", ERR_LIB_X509V3, X509V3_R_DISTPOINT_ALREADY_SET}, - #else - {"DISTPOINT_ALREADY_SET", 34, 160}, - #endif - #ifdef X509V3_R_DUPLICATE_ZONE_ID - {"DUPLICATE_ZONE_ID", ERR_LIB_X509V3, X509V3_R_DUPLICATE_ZONE_ID}, - #else - {"DUPLICATE_ZONE_ID", 34, 133}, - #endif - #ifdef X509V3_R_EMPTY_KEY_USAGE - {"EMPTY_KEY_USAGE", ERR_LIB_X509V3, X509V3_R_EMPTY_KEY_USAGE}, - #else - {"EMPTY_KEY_USAGE", 34, 169}, - #endif - #ifdef X509V3_R_ERROR_CONVERTING_ZONE - {"ERROR_CONVERTING_ZONE", ERR_LIB_X509V3, X509V3_R_ERROR_CONVERTING_ZONE}, - #else - {"ERROR_CONVERTING_ZONE", 34, 131}, - #endif - #ifdef X509V3_R_ERROR_CREATING_EXTENSION - {"ERROR_CREATING_EXTENSION", ERR_LIB_X509V3, X509V3_R_ERROR_CREATING_EXTENSION}, - #else - {"ERROR_CREATING_EXTENSION", 34, 144}, - #endif - #ifdef X509V3_R_ERROR_IN_EXTENSION - {"ERROR_IN_EXTENSION", ERR_LIB_X509V3, X509V3_R_ERROR_IN_EXTENSION}, - #else - {"ERROR_IN_EXTENSION", 34, 128}, - #endif - #ifdef X509V3_R_EXPECTED_A_SECTION_NAME - {"EXPECTED_A_SECTION_NAME", ERR_LIB_X509V3, X509V3_R_EXPECTED_A_SECTION_NAME}, - #else - {"EXPECTED_A_SECTION_NAME", 34, 137}, - #endif - #ifdef X509V3_R_EXTENSION_EXISTS - {"EXTENSION_EXISTS", ERR_LIB_X509V3, X509V3_R_EXTENSION_EXISTS}, - #else - {"EXTENSION_EXISTS", 34, 145}, - #endif - #ifdef X509V3_R_EXTENSION_NAME_ERROR - {"EXTENSION_NAME_ERROR", ERR_LIB_X509V3, X509V3_R_EXTENSION_NAME_ERROR}, - #else - {"EXTENSION_NAME_ERROR", 34, 115}, - #endif - #ifdef X509V3_R_EXTENSION_NOT_FOUND - {"EXTENSION_NOT_FOUND", ERR_LIB_X509V3, X509V3_R_EXTENSION_NOT_FOUND}, - #else - {"EXTENSION_NOT_FOUND", 34, 102}, - #endif - #ifdef X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED - {"EXTENSION_SETTING_NOT_SUPPORTED", ERR_LIB_X509V3, X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED}, - #else - {"EXTENSION_SETTING_NOT_SUPPORTED", 34, 103}, - #endif - #ifdef X509V3_R_EXTENSION_VALUE_ERROR - {"EXTENSION_VALUE_ERROR", ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR}, - #else - {"EXTENSION_VALUE_ERROR", 34, 116}, - #endif - #ifdef X509V3_R_ILLEGAL_EMPTY_EXTENSION - {"ILLEGAL_EMPTY_EXTENSION", ERR_LIB_X509V3, X509V3_R_ILLEGAL_EMPTY_EXTENSION}, - #else - {"ILLEGAL_EMPTY_EXTENSION", 34, 151}, - #endif - #ifdef X509V3_R_INCORRECT_POLICY_SYNTAX_TAG - {"INCORRECT_POLICY_SYNTAX_TAG", ERR_LIB_X509V3, X509V3_R_INCORRECT_POLICY_SYNTAX_TAG}, - #else - {"INCORRECT_POLICY_SYNTAX_TAG", 34, 152}, - #endif - #ifdef X509V3_R_INVALID_ASNUMBER - {"INVALID_ASNUMBER", ERR_LIB_X509V3, X509V3_R_INVALID_ASNUMBER}, - #else - {"INVALID_ASNUMBER", 34, 162}, - #endif - #ifdef X509V3_R_INVALID_ASRANGE - {"INVALID_ASRANGE", ERR_LIB_X509V3, X509V3_R_INVALID_ASRANGE}, - #else - {"INVALID_ASRANGE", 34, 163}, - #endif - #ifdef X509V3_R_INVALID_BOOLEAN_STRING - {"INVALID_BOOLEAN_STRING", ERR_LIB_X509V3, X509V3_R_INVALID_BOOLEAN_STRING}, - #else - {"INVALID_BOOLEAN_STRING", 34, 104}, - #endif - #ifdef X509V3_R_INVALID_CERTIFICATE - {"INVALID_CERTIFICATE", ERR_LIB_X509V3, X509V3_R_INVALID_CERTIFICATE}, - #else - {"INVALID_CERTIFICATE", 34, 158}, - #endif - #ifdef X509V3_R_INVALID_EMPTY_NAME - {"INVALID_EMPTY_NAME", ERR_LIB_X509V3, X509V3_R_INVALID_EMPTY_NAME}, - #else - {"INVALID_EMPTY_NAME", 34, 108}, - #endif - #ifdef X509V3_R_INVALID_EXTENSION_STRING - {"INVALID_EXTENSION_STRING", ERR_LIB_X509V3, X509V3_R_INVALID_EXTENSION_STRING}, - #else - {"INVALID_EXTENSION_STRING", 34, 105}, - #endif - #ifdef X509V3_R_INVALID_INHERITANCE - {"INVALID_INHERITANCE", ERR_LIB_X509V3, X509V3_R_INVALID_INHERITANCE}, - #else - {"INVALID_INHERITANCE", 34, 165}, - #endif - #ifdef X509V3_R_INVALID_IPADDRESS - {"INVALID_IPADDRESS", ERR_LIB_X509V3, X509V3_R_INVALID_IPADDRESS}, - #else - {"INVALID_IPADDRESS", 34, 166}, - #endif - #ifdef X509V3_R_INVALID_MULTIPLE_RDNS - {"INVALID_MULTIPLE_RDNS", ERR_LIB_X509V3, X509V3_R_INVALID_MULTIPLE_RDNS}, - #else - {"INVALID_MULTIPLE_RDNS", 34, 161}, - #endif - #ifdef X509V3_R_INVALID_NAME - {"INVALID_NAME", ERR_LIB_X509V3, X509V3_R_INVALID_NAME}, - #else - {"INVALID_NAME", 34, 106}, - #endif - #ifdef X509V3_R_INVALID_NULL_ARGUMENT - {"INVALID_NULL_ARGUMENT", ERR_LIB_X509V3, X509V3_R_INVALID_NULL_ARGUMENT}, - #else - {"INVALID_NULL_ARGUMENT", 34, 107}, - #endif - #ifdef X509V3_R_INVALID_NULL_VALUE - {"INVALID_NULL_VALUE", ERR_LIB_X509V3, X509V3_R_INVALID_NULL_VALUE}, - #else - {"INVALID_NULL_VALUE", 34, 109}, - #endif - #ifdef X509V3_R_INVALID_NUMBER - {"INVALID_NUMBER", ERR_LIB_X509V3, X509V3_R_INVALID_NUMBER}, - #else - {"INVALID_NUMBER", 34, 140}, - #endif - #ifdef X509V3_R_INVALID_NUMBERS - {"INVALID_NUMBERS", ERR_LIB_X509V3, X509V3_R_INVALID_NUMBERS}, - #else - {"INVALID_NUMBERS", 34, 141}, - #endif - #ifdef X509V3_R_INVALID_OBJECT_IDENTIFIER - {"INVALID_OBJECT_IDENTIFIER", ERR_LIB_X509V3, X509V3_R_INVALID_OBJECT_IDENTIFIER}, - #else - {"INVALID_OBJECT_IDENTIFIER", 34, 110}, - #endif - #ifdef X509V3_R_INVALID_OPTION - {"INVALID_OPTION", ERR_LIB_X509V3, X509V3_R_INVALID_OPTION}, - #else - {"INVALID_OPTION", 34, 138}, - #endif - #ifdef X509V3_R_INVALID_POLICY_IDENTIFIER - {"INVALID_POLICY_IDENTIFIER", ERR_LIB_X509V3, X509V3_R_INVALID_POLICY_IDENTIFIER}, - #else - {"INVALID_POLICY_IDENTIFIER", 34, 134}, - #endif - #ifdef X509V3_R_INVALID_PROXY_POLICY_SETTING - {"INVALID_PROXY_POLICY_SETTING", ERR_LIB_X509V3, X509V3_R_INVALID_PROXY_POLICY_SETTING}, - #else - {"INVALID_PROXY_POLICY_SETTING", 34, 153}, - #endif - #ifdef X509V3_R_INVALID_PURPOSE - {"INVALID_PURPOSE", ERR_LIB_X509V3, X509V3_R_INVALID_PURPOSE}, - #else - {"INVALID_PURPOSE", 34, 146}, - #endif - #ifdef X509V3_R_INVALID_SAFI - {"INVALID_SAFI", ERR_LIB_X509V3, X509V3_R_INVALID_SAFI}, - #else - {"INVALID_SAFI", 34, 164}, - #endif - #ifdef X509V3_R_INVALID_SECTION - {"INVALID_SECTION", ERR_LIB_X509V3, X509V3_R_INVALID_SECTION}, - #else - {"INVALID_SECTION", 34, 135}, - #endif - #ifdef X509V3_R_INVALID_SYNTAX - {"INVALID_SYNTAX", ERR_LIB_X509V3, X509V3_R_INVALID_SYNTAX}, - #else - {"INVALID_SYNTAX", 34, 143}, - #endif - #ifdef X509V3_R_ISSUER_DECODE_ERROR - {"ISSUER_DECODE_ERROR", ERR_LIB_X509V3, X509V3_R_ISSUER_DECODE_ERROR}, - #else - {"ISSUER_DECODE_ERROR", 34, 126}, - #endif - #ifdef X509V3_R_MISSING_VALUE - {"MISSING_VALUE", ERR_LIB_X509V3, X509V3_R_MISSING_VALUE}, - #else - {"MISSING_VALUE", 34, 124}, - #endif - #ifdef X509V3_R_NEED_ORGANIZATION_AND_NUMBERS - {"NEED_ORGANIZATION_AND_NUMBERS", ERR_LIB_X509V3, X509V3_R_NEED_ORGANIZATION_AND_NUMBERS}, - #else - {"NEED_ORGANIZATION_AND_NUMBERS", 34, 142}, - #endif - #ifdef X509V3_R_NEGATIVE_PATHLEN - {"NEGATIVE_PATHLEN", ERR_LIB_X509V3, X509V3_R_NEGATIVE_PATHLEN}, - #else - {"NEGATIVE_PATHLEN", 34, 168}, - #endif - #ifdef X509V3_R_NO_CONFIG_DATABASE - {"NO_CONFIG_DATABASE", ERR_LIB_X509V3, X509V3_R_NO_CONFIG_DATABASE}, - #else - {"NO_CONFIG_DATABASE", 34, 136}, - #endif - #ifdef X509V3_R_NO_ISSUER_CERTIFICATE - {"NO_ISSUER_CERTIFICATE", ERR_LIB_X509V3, X509V3_R_NO_ISSUER_CERTIFICATE}, - #else - {"NO_ISSUER_CERTIFICATE", 34, 121}, - #endif - #ifdef X509V3_R_NO_ISSUER_DETAILS - {"NO_ISSUER_DETAILS", ERR_LIB_X509V3, X509V3_R_NO_ISSUER_DETAILS}, - #else - {"NO_ISSUER_DETAILS", 34, 127}, - #endif - #ifdef X509V3_R_NO_POLICY_IDENTIFIER - {"NO_POLICY_IDENTIFIER", ERR_LIB_X509V3, X509V3_R_NO_POLICY_IDENTIFIER}, - #else - {"NO_POLICY_IDENTIFIER", 34, 139}, - #endif - #ifdef X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED - {"NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED", ERR_LIB_X509V3, X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED}, - #else - {"NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED", 34, 154}, - #endif - #ifdef X509V3_R_NO_PUBLIC_KEY - {"NO_PUBLIC_KEY", ERR_LIB_X509V3, X509V3_R_NO_PUBLIC_KEY}, - #else - {"NO_PUBLIC_KEY", 34, 114}, - #endif - #ifdef X509V3_R_NO_SUBJECT_DETAILS - {"NO_SUBJECT_DETAILS", ERR_LIB_X509V3, X509V3_R_NO_SUBJECT_DETAILS}, - #else - {"NO_SUBJECT_DETAILS", 34, 125}, - #endif - #ifdef X509V3_R_OPERATION_NOT_DEFINED - {"OPERATION_NOT_DEFINED", ERR_LIB_X509V3, X509V3_R_OPERATION_NOT_DEFINED}, - #else - {"OPERATION_NOT_DEFINED", 34, 148}, - #endif - #ifdef X509V3_R_OTHERNAME_ERROR - {"OTHERNAME_ERROR", ERR_LIB_X509V3, X509V3_R_OTHERNAME_ERROR}, - #else - {"OTHERNAME_ERROR", 34, 147}, - #endif - #ifdef X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED - {"POLICY_LANGUAGE_ALREADY_DEFINED", ERR_LIB_X509V3, X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED}, - #else - {"POLICY_LANGUAGE_ALREADY_DEFINED", 34, 155}, - #endif - #ifdef X509V3_R_POLICY_PATH_LENGTH - {"POLICY_PATH_LENGTH", ERR_LIB_X509V3, X509V3_R_POLICY_PATH_LENGTH}, - #else - {"POLICY_PATH_LENGTH", 34, 156}, - #endif - #ifdef X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED - {"POLICY_PATH_LENGTH_ALREADY_DEFINED", ERR_LIB_X509V3, X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED}, - #else - {"POLICY_PATH_LENGTH_ALREADY_DEFINED", 34, 157}, - #endif - #ifdef X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY - {"POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY", ERR_LIB_X509V3, X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY}, - #else - {"POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY", 34, 159}, - #endif - #ifdef X509V3_R_SECTION_NOT_FOUND - {"SECTION_NOT_FOUND", ERR_LIB_X509V3, X509V3_R_SECTION_NOT_FOUND}, - #else - {"SECTION_NOT_FOUND", 34, 150}, - #endif - #ifdef X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS - {"UNABLE_TO_GET_ISSUER_DETAILS", ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS}, - #else - {"UNABLE_TO_GET_ISSUER_DETAILS", 34, 122}, - #endif - #ifdef X509V3_R_UNABLE_TO_GET_ISSUER_KEYID - {"UNABLE_TO_GET_ISSUER_KEYID", ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_KEYID}, - #else - {"UNABLE_TO_GET_ISSUER_KEYID", 34, 123}, - #endif - #ifdef X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT - {"UNKNOWN_BIT_STRING_ARGUMENT", ERR_LIB_X509V3, X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT}, - #else - {"UNKNOWN_BIT_STRING_ARGUMENT", 34, 111}, - #endif - #ifdef X509V3_R_UNKNOWN_EXTENSION - {"UNKNOWN_EXTENSION", ERR_LIB_X509V3, X509V3_R_UNKNOWN_EXTENSION}, - #else - {"UNKNOWN_EXTENSION", 34, 129}, - #endif - #ifdef X509V3_R_UNKNOWN_EXTENSION_NAME - {"UNKNOWN_EXTENSION_NAME", ERR_LIB_X509V3, X509V3_R_UNKNOWN_EXTENSION_NAME}, - #else - {"UNKNOWN_EXTENSION_NAME", 34, 130}, - #endif - #ifdef X509V3_R_UNKNOWN_OPTION - {"UNKNOWN_OPTION", ERR_LIB_X509V3, X509V3_R_UNKNOWN_OPTION}, - #else - {"UNKNOWN_OPTION", 34, 120}, - #endif - #ifdef X509V3_R_UNKNOWN_VALUE - {"UNKNOWN_VALUE", ERR_LIB_X509V3, X509V3_R_UNKNOWN_VALUE}, - #else - {"UNKNOWN_VALUE", 34, 172}, - #endif - #ifdef X509V3_R_UNSUPPORTED_OPTION - {"UNSUPPORTED_OPTION", ERR_LIB_X509V3, X509V3_R_UNSUPPORTED_OPTION}, - #else - {"UNSUPPORTED_OPTION", 34, 117}, - #endif - #ifdef X509V3_R_UNSUPPORTED_TYPE - {"UNSUPPORTED_TYPE", ERR_LIB_X509V3, X509V3_R_UNSUPPORTED_TYPE}, - #else - {"UNSUPPORTED_TYPE", 34, 167}, - #endif - #ifdef X509V3_R_USER_TOO_LONG - {"USER_TOO_LONG", ERR_LIB_X509V3, X509V3_R_USER_TOO_LONG}, - #else - {"USER_TOO_LONG", 34, 132}, - #endif - #ifdef X509_R_AKID_MISMATCH - {"AKID_MISMATCH", ERR_LIB_X509, X509_R_AKID_MISMATCH}, - #else - {"AKID_MISMATCH", 11, 110}, - #endif - #ifdef X509_R_BAD_SELECTOR - {"BAD_SELECTOR", ERR_LIB_X509, X509_R_BAD_SELECTOR}, - #else - {"BAD_SELECTOR", 11, 133}, - #endif - #ifdef X509_R_BAD_X509_FILETYPE - {"BAD_X509_FILETYPE", ERR_LIB_X509, X509_R_BAD_X509_FILETYPE}, - #else - {"BAD_X509_FILETYPE", 11, 100}, - #endif - #ifdef X509_R_BASE64_DECODE_ERROR - {"BASE64_DECODE_ERROR", ERR_LIB_X509, X509_R_BASE64_DECODE_ERROR}, - #else - {"BASE64_DECODE_ERROR", 11, 118}, - #endif - #ifdef X509_R_CANT_CHECK_DH_KEY - {"CANT_CHECK_DH_KEY", ERR_LIB_X509, X509_R_CANT_CHECK_DH_KEY}, - #else - {"CANT_CHECK_DH_KEY", 11, 114}, - #endif - #ifdef X509_R_CERTIFICATE_VERIFICATION_FAILED - {"CERTIFICATE_VERIFICATION_FAILED", ERR_LIB_X509, X509_R_CERTIFICATE_VERIFICATION_FAILED}, - #else - {"CERTIFICATE_VERIFICATION_FAILED", 11, 139}, - #endif - #ifdef X509_R_CERT_ALREADY_IN_HASH_TABLE - {"CERT_ALREADY_IN_HASH_TABLE", ERR_LIB_X509, X509_R_CERT_ALREADY_IN_HASH_TABLE}, - #else - {"CERT_ALREADY_IN_HASH_TABLE", 11, 101}, - #endif - #ifdef X509_R_CRL_ALREADY_DELTA - {"CRL_ALREADY_DELTA", ERR_LIB_X509, X509_R_CRL_ALREADY_DELTA}, - #else - {"CRL_ALREADY_DELTA", 11, 127}, - #endif - #ifdef X509_R_CRL_VERIFY_FAILURE - {"CRL_VERIFY_FAILURE", ERR_LIB_X509, X509_R_CRL_VERIFY_FAILURE}, - #else - {"CRL_VERIFY_FAILURE", 11, 131}, - #endif - #ifdef X509_R_DUPLICATE_ATTRIBUTE - {"DUPLICATE_ATTRIBUTE", ERR_LIB_X509, X509_R_DUPLICATE_ATTRIBUTE}, - #else - {"DUPLICATE_ATTRIBUTE", 11, 140}, - #endif - #ifdef X509_R_ERROR_GETTING_MD_BY_NID - {"ERROR_GETTING_MD_BY_NID", ERR_LIB_X509, X509_R_ERROR_GETTING_MD_BY_NID}, - #else - {"ERROR_GETTING_MD_BY_NID", 11, 141}, - #endif - #ifdef X509_R_ERROR_USING_SIGINF_SET - {"ERROR_USING_SIGINF_SET", ERR_LIB_X509, X509_R_ERROR_USING_SIGINF_SET}, - #else - {"ERROR_USING_SIGINF_SET", 11, 142}, - #endif - #ifdef X509_R_IDP_MISMATCH - {"IDP_MISMATCH", ERR_LIB_X509, X509_R_IDP_MISMATCH}, - #else - {"IDP_MISMATCH", 11, 128}, - #endif - #ifdef X509_R_INVALID_ATTRIBUTES - {"INVALID_ATTRIBUTES", ERR_LIB_X509, X509_R_INVALID_ATTRIBUTES}, - #else - {"INVALID_ATTRIBUTES", 11, 138}, - #endif - #ifdef X509_R_INVALID_DIRECTORY - {"INVALID_DIRECTORY", ERR_LIB_X509, X509_R_INVALID_DIRECTORY}, - #else - {"INVALID_DIRECTORY", 11, 113}, - #endif - #ifdef X509_R_INVALID_DISTPOINT - {"INVALID_DISTPOINT", ERR_LIB_X509, X509_R_INVALID_DISTPOINT}, - #else - {"INVALID_DISTPOINT", 11, 143}, - #endif - #ifdef X509_R_INVALID_FIELD_NAME - {"INVALID_FIELD_NAME", ERR_LIB_X509, X509_R_INVALID_FIELD_NAME}, - #else - {"INVALID_FIELD_NAME", 11, 119}, - #endif - #ifdef X509_R_INVALID_TRUST - {"INVALID_TRUST", ERR_LIB_X509, X509_R_INVALID_TRUST}, - #else - {"INVALID_TRUST", 11, 123}, - #endif - #ifdef X509_R_ISSUER_MISMATCH - {"ISSUER_MISMATCH", ERR_LIB_X509, X509_R_ISSUER_MISMATCH}, - #else - {"ISSUER_MISMATCH", 11, 129}, - #endif - #ifdef X509_R_KEY_TYPE_MISMATCH - {"KEY_TYPE_MISMATCH", ERR_LIB_X509, X509_R_KEY_TYPE_MISMATCH}, - #else - {"KEY_TYPE_MISMATCH", 11, 115}, - #endif - #ifdef X509_R_KEY_VALUES_MISMATCH - {"KEY_VALUES_MISMATCH", ERR_LIB_X509, X509_R_KEY_VALUES_MISMATCH}, - #else - {"KEY_VALUES_MISMATCH", 11, 116}, - #endif - #ifdef X509_R_LOADING_CERT_DIR - {"LOADING_CERT_DIR", ERR_LIB_X509, X509_R_LOADING_CERT_DIR}, - #else - {"LOADING_CERT_DIR", 11, 103}, - #endif - #ifdef X509_R_LOADING_DEFAULTS - {"LOADING_DEFAULTS", ERR_LIB_X509, X509_R_LOADING_DEFAULTS}, - #else - {"LOADING_DEFAULTS", 11, 104}, - #endif - #ifdef X509_R_METHOD_NOT_SUPPORTED - {"METHOD_NOT_SUPPORTED", ERR_LIB_X509, X509_R_METHOD_NOT_SUPPORTED}, - #else - {"METHOD_NOT_SUPPORTED", 11, 124}, - #endif - #ifdef X509_R_NAME_TOO_LONG - {"NAME_TOO_LONG", ERR_LIB_X509, X509_R_NAME_TOO_LONG}, - #else - {"NAME_TOO_LONG", 11, 134}, - #endif - #ifdef X509_R_NEWER_CRL_NOT_NEWER - {"NEWER_CRL_NOT_NEWER", ERR_LIB_X509, X509_R_NEWER_CRL_NOT_NEWER}, - #else - {"NEWER_CRL_NOT_NEWER", 11, 132}, - #endif - #ifdef X509_R_NO_CERTIFICATE_FOUND - {"NO_CERTIFICATE_FOUND", ERR_LIB_X509, X509_R_NO_CERTIFICATE_FOUND}, - #else - {"NO_CERTIFICATE_FOUND", 11, 135}, - #endif - #ifdef X509_R_NO_CERTIFICATE_OR_CRL_FOUND - {"NO_CERTIFICATE_OR_CRL_FOUND", ERR_LIB_X509, X509_R_NO_CERTIFICATE_OR_CRL_FOUND}, - #else - {"NO_CERTIFICATE_OR_CRL_FOUND", 11, 136}, - #endif - #ifdef X509_R_NO_CERT_SET_FOR_US_TO_VERIFY - {"NO_CERT_SET_FOR_US_TO_VERIFY", ERR_LIB_X509, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY}, - #else - {"NO_CERT_SET_FOR_US_TO_VERIFY", 11, 105}, - #endif - #ifdef X509_R_NO_CRL_FOUND - {"NO_CRL_FOUND", ERR_LIB_X509, X509_R_NO_CRL_FOUND}, - #else - {"NO_CRL_FOUND", 11, 137}, - #endif - #ifdef X509_R_NO_CRL_NUMBER - {"NO_CRL_NUMBER", ERR_LIB_X509, X509_R_NO_CRL_NUMBER}, - #else - {"NO_CRL_NUMBER", 11, 130}, - #endif - #ifdef X509_R_PUBLIC_KEY_DECODE_ERROR - {"PUBLIC_KEY_DECODE_ERROR", ERR_LIB_X509, X509_R_PUBLIC_KEY_DECODE_ERROR}, - #else - {"PUBLIC_KEY_DECODE_ERROR", 11, 125}, - #endif - #ifdef X509_R_PUBLIC_KEY_ENCODE_ERROR - {"PUBLIC_KEY_ENCODE_ERROR", ERR_LIB_X509, X509_R_PUBLIC_KEY_ENCODE_ERROR}, - #else - {"PUBLIC_KEY_ENCODE_ERROR", 11, 126}, - #endif - #ifdef X509_R_SHOULD_RETRY - {"SHOULD_RETRY", ERR_LIB_X509, X509_R_SHOULD_RETRY}, - #else - {"SHOULD_RETRY", 11, 106}, - #endif - #ifdef X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN - {"UNABLE_TO_FIND_PARAMETERS_IN_CHAIN", ERR_LIB_X509, X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN}, - #else - {"UNABLE_TO_FIND_PARAMETERS_IN_CHAIN", 11, 107}, - #endif - #ifdef X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY - {"UNABLE_TO_GET_CERTS_PUBLIC_KEY", ERR_LIB_X509, X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY}, - #else - {"UNABLE_TO_GET_CERTS_PUBLIC_KEY", 11, 108}, - #endif - #ifdef X509_R_UNKNOWN_KEY_TYPE - {"UNKNOWN_KEY_TYPE", ERR_LIB_X509, X509_R_UNKNOWN_KEY_TYPE}, - #else - {"UNKNOWN_KEY_TYPE", 11, 117}, - #endif - #ifdef X509_R_UNKNOWN_NID - {"UNKNOWN_NID", ERR_LIB_X509, X509_R_UNKNOWN_NID}, - #else - {"UNKNOWN_NID", 11, 109}, - #endif - #ifdef X509_R_UNKNOWN_PURPOSE_ID - {"UNKNOWN_PURPOSE_ID", ERR_LIB_X509, X509_R_UNKNOWN_PURPOSE_ID}, - #else - {"UNKNOWN_PURPOSE_ID", 11, 121}, - #endif - #ifdef X509_R_UNKNOWN_SIGID_ALGS - {"UNKNOWN_SIGID_ALGS", ERR_LIB_X509, X509_R_UNKNOWN_SIGID_ALGS}, - #else - {"UNKNOWN_SIGID_ALGS", 11, 144}, - #endif - #ifdef X509_R_UNKNOWN_TRUST_ID - {"UNKNOWN_TRUST_ID", ERR_LIB_X509, X509_R_UNKNOWN_TRUST_ID}, - #else - {"UNKNOWN_TRUST_ID", 11, 120}, - #endif - #ifdef X509_R_UNSUPPORTED_ALGORITHM - {"UNSUPPORTED_ALGORITHM", ERR_LIB_X509, X509_R_UNSUPPORTED_ALGORITHM}, - #else - {"UNSUPPORTED_ALGORITHM", 11, 111}, - #endif - #ifdef X509_R_UNSUPPORTED_VERSION - {"UNSUPPORTED_VERSION", ERR_LIB_X509, X509_R_UNSUPPORTED_VERSION}, - #else - {"UNSUPPORTED_VERSION", 11, 145}, - #endif - #ifdef X509_R_WRONG_LOOKUP_TYPE - {"WRONG_LOOKUP_TYPE", ERR_LIB_X509, X509_R_WRONG_LOOKUP_TYPE}, - #else - {"WRONG_LOOKUP_TYPE", 11, 112}, - #endif - #ifdef X509_R_WRONG_TYPE - {"WRONG_TYPE", ERR_LIB_X509, X509_R_WRONG_TYPE}, - #else - {"WRONG_TYPE", 11, 122}, - #endif - {NULL, 0, 0} /* sentinel */ -}; diff --git a/Modules/_ssl_data_36.h b/Modules/_ssl_data_36.h new file mode 100644 index 00000000000000..e1c1eb30ff6a7b --- /dev/null +++ b/Modules/_ssl_data_36.h @@ -0,0 +1,9457 @@ +/* File generated by Tools/ssl/make_ssl_data.py */ +/* Generated on 2026-05-03T19:50:43.034653+00:00 */ +/* Generated from Git commit openssl-3.6.2-0-gfe686e15d */ + +/* generated from args.lib2errnum */ +static struct py_ssl_library_code library_codes[] = { +#ifdef ERR_LIB_ASN1 + {"ASN1", ERR_LIB_ASN1}, +#endif +#ifdef ERR_LIB_ASYNC + {"ASYNC", ERR_LIB_ASYNC}, +#endif +#ifdef ERR_LIB_BIO + {"BIO", ERR_LIB_BIO}, +#endif +#ifdef ERR_LIB_BN + {"BN", ERR_LIB_BN}, +#endif +#ifdef ERR_LIB_BUF + {"BUF", ERR_LIB_BUF}, +#endif +#ifdef ERR_LIB_CMP + {"CMP", ERR_LIB_CMP}, +#endif +#ifdef ERR_LIB_CMS + {"CMS", ERR_LIB_CMS}, +#endif +#ifdef ERR_LIB_COMP + {"COMP", ERR_LIB_COMP}, +#endif +#ifdef ERR_LIB_CONF + {"CONF", ERR_LIB_CONF}, +#endif +#ifdef ERR_LIB_CRMF + {"CRMF", ERR_LIB_CRMF}, +#endif +#ifdef ERR_LIB_CRYPTO + {"CRYPTO", ERR_LIB_CRYPTO}, +#endif +#ifdef ERR_LIB_CT + {"CT", ERR_LIB_CT}, +#endif +#ifdef ERR_LIB_DH + {"DH", ERR_LIB_DH}, +#endif +#ifdef ERR_LIB_DSA + {"DSA", ERR_LIB_DSA}, +#endif +#ifdef ERR_LIB_DSO + {"DSO", ERR_LIB_DSO}, +#endif +#ifdef ERR_LIB_EC + {"EC", ERR_LIB_EC}, +#endif +#ifdef ERR_LIB_ECDH + {"ECDH", ERR_LIB_ECDH}, +#endif +#ifdef ERR_LIB_ECDSA + {"ECDSA", ERR_LIB_ECDSA}, +#endif +#ifdef ERR_LIB_ENGINE + {"ENGINE", ERR_LIB_ENGINE}, +#endif +#ifdef ERR_LIB_ESS + {"ESS", ERR_LIB_ESS}, +#endif +#ifdef ERR_LIB_EVP + {"EVP", ERR_LIB_EVP}, +#endif +#ifdef ERR_LIB_FIPS + {"FIPS", ERR_LIB_FIPS}, +#endif +#ifdef ERR_LIB_HMAC + {"HMAC", ERR_LIB_HMAC}, +#endif +#ifdef ERR_LIB_HTTP + {"HTTP", ERR_LIB_HTTP}, +#endif +#ifdef ERR_LIB_JPAKE + {"JPAKE", ERR_LIB_JPAKE}, +#endif +#ifdef ERR_LIB_KDF + {"KDF", ERR_LIB_KDF}, +#endif +#ifdef ERR_LIB_METH + {"METH", ERR_LIB_METH}, +#endif +#ifdef ERR_LIB_NONE + {"NONE", ERR_LIB_NONE}, +#endif +#ifdef ERR_LIB_OBJ + {"OBJ", ERR_LIB_OBJ}, +#endif +#ifdef ERR_LIB_OCSP + {"OCSP", ERR_LIB_OCSP}, +#endif +#ifdef ERR_LIB_OSSL_DECODER + {"OSSL_DECODER", ERR_LIB_OSSL_DECODER}, +#endif +#ifdef ERR_LIB_OSSL_ENCODER + {"OSSL_ENCODER", ERR_LIB_OSSL_ENCODER}, +#endif +#ifdef ERR_LIB_OSSL_STORE + {"OSSL_STORE", ERR_LIB_OSSL_STORE}, +#endif +#ifdef ERR_LIB_PEM + {"PEM", ERR_LIB_PEM}, +#endif +#ifdef ERR_LIB_PKCS12 + {"PKCS12", ERR_LIB_PKCS12}, +#endif +#ifdef ERR_LIB_PKCS7 + {"PKCS7", ERR_LIB_PKCS7}, +#endif +#ifdef ERR_LIB_PROP + {"PROP", ERR_LIB_PROP}, +#endif +#ifdef ERR_LIB_PROV + {"PROV", ERR_LIB_PROV}, +#endif +#ifdef ERR_LIB_PROXY + {"PROXY", ERR_LIB_PROXY}, +#endif +#ifdef ERR_LIB_RAND + {"RAND", ERR_LIB_RAND}, +#endif +#ifdef ERR_LIB_RSA + {"RSA", ERR_LIB_RSA}, +#endif +#ifdef ERR_LIB_RSAREF + {"RSAREF", ERR_LIB_RSAREF}, +#endif +#ifdef ERR_LIB_SM2 + {"SM2", ERR_LIB_SM2}, +#endif +#ifdef ERR_LIB_SSL + {"SSL", ERR_LIB_SSL}, +#endif +#ifdef ERR_LIB_SSL2 + {"SSL2", ERR_LIB_SSL2}, +#endif +#ifdef ERR_LIB_SSL23 + {"SSL23", ERR_LIB_SSL23}, +#endif +#ifdef ERR_LIB_SSL3 + {"SSL3", ERR_LIB_SSL3}, +#endif +#ifdef ERR_LIB_SYS + {"SYS", ERR_LIB_SYS}, +#endif +#ifdef ERR_LIB_TS + {"TS", ERR_LIB_TS}, +#endif +#ifdef ERR_LIB_UI + {"UI", ERR_LIB_UI}, +#endif +#ifdef ERR_LIB_USER + {"USER", ERR_LIB_USER}, +#endif +#ifdef ERR_LIB_X509 + {"X509", ERR_LIB_X509}, +#endif +#ifdef ERR_LIB_X509V3 + {"X509V3", ERR_LIB_X509V3}, +#endif + {NULL, 0} /* sentinel */ +}; + +/* generated from args.reasons */ +static struct py_ssl_error_code error_codes[] = { + #ifdef ASN1_R_ADDING_OBJECT + {"ADDING_OBJECT", ERR_LIB_ASN1, ASN1_R_ADDING_OBJECT}, + #else + {"ADDING_OBJECT", 13, 171}, + #endif + #ifdef ASN1_R_ASN1_PARSE_ERROR + {"ASN1_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_ASN1_PARSE_ERROR}, + #else + {"ASN1_PARSE_ERROR", 13, 203}, + #endif + #ifdef ASN1_R_ASN1_SIG_PARSE_ERROR + {"ASN1_SIG_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_ASN1_SIG_PARSE_ERROR}, + #else + {"ASN1_SIG_PARSE_ERROR", 13, 204}, + #endif + #ifdef ASN1_R_AUX_ERROR + {"AUX_ERROR", ERR_LIB_ASN1, ASN1_R_AUX_ERROR}, + #else + {"AUX_ERROR", 13, 100}, + #endif + #ifdef ASN1_R_BAD_OBJECT_HEADER + {"BAD_OBJECT_HEADER", ERR_LIB_ASN1, ASN1_R_BAD_OBJECT_HEADER}, + #else + {"BAD_OBJECT_HEADER", 13, 102}, + #endif + #ifdef ASN1_R_BAD_TEMPLATE + {"BAD_TEMPLATE", ERR_LIB_ASN1, ASN1_R_BAD_TEMPLATE}, + #else + {"BAD_TEMPLATE", 13, 230}, + #endif + #ifdef ASN1_R_BMPSTRING_IS_WRONG_LENGTH + {"BMPSTRING_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_BMPSTRING_IS_WRONG_LENGTH}, + #else + {"BMPSTRING_IS_WRONG_LENGTH", 13, 214}, + #endif + #ifdef ASN1_R_BN_LIB + {"BN_LIB", ERR_LIB_ASN1, ASN1_R_BN_LIB}, + #else + {"BN_LIB", 13, 105}, + #endif + #ifdef ASN1_R_BOOLEAN_IS_WRONG_LENGTH + {"BOOLEAN_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_BOOLEAN_IS_WRONG_LENGTH}, + #else + {"BOOLEAN_IS_WRONG_LENGTH", 13, 106}, + #endif + #ifdef ASN1_R_BUFFER_TOO_SMALL + {"BUFFER_TOO_SMALL", ERR_LIB_ASN1, ASN1_R_BUFFER_TOO_SMALL}, + #else + {"BUFFER_TOO_SMALL", 13, 107}, + #endif + #ifdef ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER + {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", ERR_LIB_ASN1, ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER}, + #else + {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", 13, 108}, + #endif + #ifdef ASN1_R_CONTEXT_NOT_INITIALISED + {"CONTEXT_NOT_INITIALISED", ERR_LIB_ASN1, ASN1_R_CONTEXT_NOT_INITIALISED}, + #else + {"CONTEXT_NOT_INITIALISED", 13, 217}, + #endif + #ifdef ASN1_R_DATA_IS_WRONG + {"DATA_IS_WRONG", ERR_LIB_ASN1, ASN1_R_DATA_IS_WRONG}, + #else + {"DATA_IS_WRONG", 13, 109}, + #endif + #ifdef ASN1_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_ASN1, ASN1_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 13, 110}, + #endif + #ifdef ASN1_R_DEPTH_EXCEEDED + {"DEPTH_EXCEEDED", ERR_LIB_ASN1, ASN1_R_DEPTH_EXCEEDED}, + #else + {"DEPTH_EXCEEDED", 13, 174}, + #endif + #ifdef ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED + {"DIGEST_AND_KEY_TYPE_NOT_SUPPORTED", ERR_LIB_ASN1, ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED}, + #else + {"DIGEST_AND_KEY_TYPE_NOT_SUPPORTED", 13, 198}, + #endif + #ifdef ASN1_R_ENCODE_ERROR + {"ENCODE_ERROR", ERR_LIB_ASN1, ASN1_R_ENCODE_ERROR}, + #else + {"ENCODE_ERROR", 13, 112}, + #endif + #ifdef ASN1_R_ERROR_GETTING_TIME + {"ERROR_GETTING_TIME", ERR_LIB_ASN1, ASN1_R_ERROR_GETTING_TIME}, + #else + {"ERROR_GETTING_TIME", 13, 173}, + #endif + #ifdef ASN1_R_ERROR_LOADING_SECTION + {"ERROR_LOADING_SECTION", ERR_LIB_ASN1, ASN1_R_ERROR_LOADING_SECTION}, + #else + {"ERROR_LOADING_SECTION", 13, 172}, + #endif + #ifdef ASN1_R_ERROR_SETTING_CIPHER_PARAMS + {"ERROR_SETTING_CIPHER_PARAMS", ERR_LIB_ASN1, ASN1_R_ERROR_SETTING_CIPHER_PARAMS}, + #else + {"ERROR_SETTING_CIPHER_PARAMS", 13, 114}, + #endif + #ifdef ASN1_R_EXPECTING_AN_INTEGER + {"EXPECTING_AN_INTEGER", ERR_LIB_ASN1, ASN1_R_EXPECTING_AN_INTEGER}, + #else + {"EXPECTING_AN_INTEGER", 13, 115}, + #endif + #ifdef ASN1_R_EXPECTING_AN_OBJECT + {"EXPECTING_AN_OBJECT", ERR_LIB_ASN1, ASN1_R_EXPECTING_AN_OBJECT}, + #else + {"EXPECTING_AN_OBJECT", 13, 116}, + #endif + #ifdef ASN1_R_EXPLICIT_LENGTH_MISMATCH + {"EXPLICIT_LENGTH_MISMATCH", ERR_LIB_ASN1, ASN1_R_EXPLICIT_LENGTH_MISMATCH}, + #else + {"EXPLICIT_LENGTH_MISMATCH", 13, 119}, + #endif + #ifdef ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED + {"EXPLICIT_TAG_NOT_CONSTRUCTED", ERR_LIB_ASN1, ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED}, + #else + {"EXPLICIT_TAG_NOT_CONSTRUCTED", 13, 120}, + #endif + #ifdef ASN1_R_FIELD_MISSING + {"FIELD_MISSING", ERR_LIB_ASN1, ASN1_R_FIELD_MISSING}, + #else + {"FIELD_MISSING", 13, 121}, + #endif + #ifdef ASN1_R_FIRST_NUM_TOO_LARGE + {"FIRST_NUM_TOO_LARGE", ERR_LIB_ASN1, ASN1_R_FIRST_NUM_TOO_LARGE}, + #else + {"FIRST_NUM_TOO_LARGE", 13, 122}, + #endif + #ifdef ASN1_R_GENERALIZEDTIME_IS_TOO_SHORT + {"GENERALIZEDTIME_IS_TOO_SHORT", ERR_LIB_ASN1, ASN1_R_GENERALIZEDTIME_IS_TOO_SHORT}, + #else + {"GENERALIZEDTIME_IS_TOO_SHORT", 13, 232}, + #endif + #ifdef ASN1_R_HEADER_TOO_LONG + {"HEADER_TOO_LONG", ERR_LIB_ASN1, ASN1_R_HEADER_TOO_LONG}, + #else + {"HEADER_TOO_LONG", 13, 123}, + #endif + #ifdef ASN1_R_ILLEGAL_BITSTRING_FORMAT + {"ILLEGAL_BITSTRING_FORMAT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_BITSTRING_FORMAT}, + #else + {"ILLEGAL_BITSTRING_FORMAT", 13, 175}, + #endif + #ifdef ASN1_R_ILLEGAL_BOOLEAN + {"ILLEGAL_BOOLEAN", ERR_LIB_ASN1, ASN1_R_ILLEGAL_BOOLEAN}, + #else + {"ILLEGAL_BOOLEAN", 13, 176}, + #endif + #ifdef ASN1_R_ILLEGAL_CHARACTERS + {"ILLEGAL_CHARACTERS", ERR_LIB_ASN1, ASN1_R_ILLEGAL_CHARACTERS}, + #else + {"ILLEGAL_CHARACTERS", 13, 124}, + #endif + #ifdef ASN1_R_ILLEGAL_FORMAT + {"ILLEGAL_FORMAT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_FORMAT}, + #else + {"ILLEGAL_FORMAT", 13, 177}, + #endif + #ifdef ASN1_R_ILLEGAL_HEX + {"ILLEGAL_HEX", ERR_LIB_ASN1, ASN1_R_ILLEGAL_HEX}, + #else + {"ILLEGAL_HEX", 13, 178}, + #endif + #ifdef ASN1_R_ILLEGAL_IMPLICIT_TAG + {"ILLEGAL_IMPLICIT_TAG", ERR_LIB_ASN1, ASN1_R_ILLEGAL_IMPLICIT_TAG}, + #else + {"ILLEGAL_IMPLICIT_TAG", 13, 179}, + #endif + #ifdef ASN1_R_ILLEGAL_INTEGER + {"ILLEGAL_INTEGER", ERR_LIB_ASN1, ASN1_R_ILLEGAL_INTEGER}, + #else + {"ILLEGAL_INTEGER", 13, 180}, + #endif + #ifdef ASN1_R_ILLEGAL_NEGATIVE_VALUE + {"ILLEGAL_NEGATIVE_VALUE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NEGATIVE_VALUE}, + #else + {"ILLEGAL_NEGATIVE_VALUE", 13, 226}, + #endif + #ifdef ASN1_R_ILLEGAL_NESTED_TAGGING + {"ILLEGAL_NESTED_TAGGING", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NESTED_TAGGING}, + #else + {"ILLEGAL_NESTED_TAGGING", 13, 181}, + #endif + #ifdef ASN1_R_ILLEGAL_NULL + {"ILLEGAL_NULL", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NULL}, + #else + {"ILLEGAL_NULL", 13, 125}, + #endif + #ifdef ASN1_R_ILLEGAL_NULL_VALUE + {"ILLEGAL_NULL_VALUE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NULL_VALUE}, + #else + {"ILLEGAL_NULL_VALUE", 13, 182}, + #endif + #ifdef ASN1_R_ILLEGAL_OBJECT + {"ILLEGAL_OBJECT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_OBJECT}, + #else + {"ILLEGAL_OBJECT", 13, 183}, + #endif + #ifdef ASN1_R_ILLEGAL_OPTIONAL_ANY + {"ILLEGAL_OPTIONAL_ANY", ERR_LIB_ASN1, ASN1_R_ILLEGAL_OPTIONAL_ANY}, + #else + {"ILLEGAL_OPTIONAL_ANY", 13, 126}, + #endif + #ifdef ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE + {"ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE}, + #else + {"ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE", 13, 170}, + #endif + #ifdef ASN1_R_ILLEGAL_PADDING + {"ILLEGAL_PADDING", ERR_LIB_ASN1, ASN1_R_ILLEGAL_PADDING}, + #else + {"ILLEGAL_PADDING", 13, 221}, + #endif + #ifdef ASN1_R_ILLEGAL_TAGGED_ANY + {"ILLEGAL_TAGGED_ANY", ERR_LIB_ASN1, ASN1_R_ILLEGAL_TAGGED_ANY}, + #else + {"ILLEGAL_TAGGED_ANY", 13, 127}, + #endif + #ifdef ASN1_R_ILLEGAL_TIME_VALUE + {"ILLEGAL_TIME_VALUE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_TIME_VALUE}, + #else + {"ILLEGAL_TIME_VALUE", 13, 184}, + #endif + #ifdef ASN1_R_ILLEGAL_ZERO_CONTENT + {"ILLEGAL_ZERO_CONTENT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_ZERO_CONTENT}, + #else + {"ILLEGAL_ZERO_CONTENT", 13, 222}, + #endif + #ifdef ASN1_R_INTEGER_NOT_ASCII_FORMAT + {"INTEGER_NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_INTEGER_NOT_ASCII_FORMAT}, + #else + {"INTEGER_NOT_ASCII_FORMAT", 13, 185}, + #endif + #ifdef ASN1_R_INTEGER_TOO_LARGE_FOR_LONG + {"INTEGER_TOO_LARGE_FOR_LONG", ERR_LIB_ASN1, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG}, + #else + {"INTEGER_TOO_LARGE_FOR_LONG", 13, 128}, + #endif + #ifdef ASN1_R_INVALID_BIT_STRING_BITS_LEFT + {"INVALID_BIT_STRING_BITS_LEFT", ERR_LIB_ASN1, ASN1_R_INVALID_BIT_STRING_BITS_LEFT}, + #else + {"INVALID_BIT_STRING_BITS_LEFT", 13, 220}, + #endif + #ifdef ASN1_R_INVALID_BMPSTRING_LENGTH + {"INVALID_BMPSTRING_LENGTH", ERR_LIB_ASN1, ASN1_R_INVALID_BMPSTRING_LENGTH}, + #else + {"INVALID_BMPSTRING_LENGTH", 13, 129}, + #endif + #ifdef ASN1_R_INVALID_DIGIT + {"INVALID_DIGIT", ERR_LIB_ASN1, ASN1_R_INVALID_DIGIT}, + #else + {"INVALID_DIGIT", 13, 130}, + #endif + #ifdef ASN1_R_INVALID_MIME_TYPE + {"INVALID_MIME_TYPE", ERR_LIB_ASN1, ASN1_R_INVALID_MIME_TYPE}, + #else + {"INVALID_MIME_TYPE", 13, 205}, + #endif + #ifdef ASN1_R_INVALID_MODIFIER + {"INVALID_MODIFIER", ERR_LIB_ASN1, ASN1_R_INVALID_MODIFIER}, + #else + {"INVALID_MODIFIER", 13, 186}, + #endif + #ifdef ASN1_R_INVALID_NUMBER + {"INVALID_NUMBER", ERR_LIB_ASN1, ASN1_R_INVALID_NUMBER}, + #else + {"INVALID_NUMBER", 13, 187}, + #endif + #ifdef ASN1_R_INVALID_OBJECT_ENCODING + {"INVALID_OBJECT_ENCODING", ERR_LIB_ASN1, ASN1_R_INVALID_OBJECT_ENCODING}, + #else + {"INVALID_OBJECT_ENCODING", 13, 216}, + #endif + #ifdef ASN1_R_INVALID_SCRYPT_PARAMETERS + {"INVALID_SCRYPT_PARAMETERS", ERR_LIB_ASN1, ASN1_R_INVALID_SCRYPT_PARAMETERS}, + #else + {"INVALID_SCRYPT_PARAMETERS", 13, 227}, + #endif + #ifdef ASN1_R_INVALID_SEPARATOR + {"INVALID_SEPARATOR", ERR_LIB_ASN1, ASN1_R_INVALID_SEPARATOR}, + #else + {"INVALID_SEPARATOR", 13, 131}, + #endif + #ifdef ASN1_R_INVALID_STRING_TABLE_VALUE + {"INVALID_STRING_TABLE_VALUE", ERR_LIB_ASN1, ASN1_R_INVALID_STRING_TABLE_VALUE}, + #else + {"INVALID_STRING_TABLE_VALUE", 13, 218}, + #endif + #ifdef ASN1_R_INVALID_UNIVERSALSTRING_LENGTH + {"INVALID_UNIVERSALSTRING_LENGTH", ERR_LIB_ASN1, ASN1_R_INVALID_UNIVERSALSTRING_LENGTH}, + #else + {"INVALID_UNIVERSALSTRING_LENGTH", 13, 133}, + #endif + #ifdef ASN1_R_INVALID_UTF8STRING + {"INVALID_UTF8STRING", ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING}, + #else + {"INVALID_UTF8STRING", 13, 134}, + #endif + #ifdef ASN1_R_INVALID_VALUE + {"INVALID_VALUE", ERR_LIB_ASN1, ASN1_R_INVALID_VALUE}, + #else + {"INVALID_VALUE", 13, 219}, + #endif + #ifdef ASN1_R_LENGTH_TOO_LONG + {"LENGTH_TOO_LONG", ERR_LIB_ASN1, ASN1_R_LENGTH_TOO_LONG}, + #else + {"LENGTH_TOO_LONG", 13, 231}, + #endif + #ifdef ASN1_R_LIST_ERROR + {"LIST_ERROR", ERR_LIB_ASN1, ASN1_R_LIST_ERROR}, + #else + {"LIST_ERROR", 13, 188}, + #endif + #ifdef ASN1_R_MIME_NO_CONTENT_TYPE + {"MIME_NO_CONTENT_TYPE", ERR_LIB_ASN1, ASN1_R_MIME_NO_CONTENT_TYPE}, + #else + {"MIME_NO_CONTENT_TYPE", 13, 206}, + #endif + #ifdef ASN1_R_MIME_PARSE_ERROR + {"MIME_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_MIME_PARSE_ERROR}, + #else + {"MIME_PARSE_ERROR", 13, 207}, + #endif + #ifdef ASN1_R_MIME_SIG_PARSE_ERROR + {"MIME_SIG_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_MIME_SIG_PARSE_ERROR}, + #else + {"MIME_SIG_PARSE_ERROR", 13, 208}, + #endif + #ifdef ASN1_R_MISSING_EOC + {"MISSING_EOC", ERR_LIB_ASN1, ASN1_R_MISSING_EOC}, + #else + {"MISSING_EOC", 13, 137}, + #endif + #ifdef ASN1_R_MISSING_SECOND_NUMBER + {"MISSING_SECOND_NUMBER", ERR_LIB_ASN1, ASN1_R_MISSING_SECOND_NUMBER}, + #else + {"MISSING_SECOND_NUMBER", 13, 138}, + #endif + #ifdef ASN1_R_MISSING_VALUE + {"MISSING_VALUE", ERR_LIB_ASN1, ASN1_R_MISSING_VALUE}, + #else + {"MISSING_VALUE", 13, 189}, + #endif + #ifdef ASN1_R_MSTRING_NOT_UNIVERSAL + {"MSTRING_NOT_UNIVERSAL", ERR_LIB_ASN1, ASN1_R_MSTRING_NOT_UNIVERSAL}, + #else + {"MSTRING_NOT_UNIVERSAL", 13, 139}, + #endif + #ifdef ASN1_R_MSTRING_WRONG_TAG + {"MSTRING_WRONG_TAG", ERR_LIB_ASN1, ASN1_R_MSTRING_WRONG_TAG}, + #else + {"MSTRING_WRONG_TAG", 13, 140}, + #endif + #ifdef ASN1_R_NESTED_ASN1_STRING + {"NESTED_ASN1_STRING", ERR_LIB_ASN1, ASN1_R_NESTED_ASN1_STRING}, + #else + {"NESTED_ASN1_STRING", 13, 197}, + #endif + #ifdef ASN1_R_NESTED_TOO_DEEP + {"NESTED_TOO_DEEP", ERR_LIB_ASN1, ASN1_R_NESTED_TOO_DEEP}, + #else + {"NESTED_TOO_DEEP", 13, 201}, + #endif + #ifdef ASN1_R_NON_HEX_CHARACTERS + {"NON_HEX_CHARACTERS", ERR_LIB_ASN1, ASN1_R_NON_HEX_CHARACTERS}, + #else + {"NON_HEX_CHARACTERS", 13, 141}, + #endif + #ifdef ASN1_R_NOT_ASCII_FORMAT + {"NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_NOT_ASCII_FORMAT}, + #else + {"NOT_ASCII_FORMAT", 13, 190}, + #endif + #ifdef ASN1_R_NOT_ENOUGH_DATA + {"NOT_ENOUGH_DATA", ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA}, + #else + {"NOT_ENOUGH_DATA", 13, 142}, + #endif + #ifdef ASN1_R_NO_CONTENT_TYPE + {"NO_CONTENT_TYPE", ERR_LIB_ASN1, ASN1_R_NO_CONTENT_TYPE}, + #else + {"NO_CONTENT_TYPE", 13, 209}, + #endif + #ifdef ASN1_R_NO_MATCHING_CHOICE_TYPE + {"NO_MATCHING_CHOICE_TYPE", ERR_LIB_ASN1, ASN1_R_NO_MATCHING_CHOICE_TYPE}, + #else + {"NO_MATCHING_CHOICE_TYPE", 13, 143}, + #endif + #ifdef ASN1_R_NO_MULTIPART_BODY_FAILURE + {"NO_MULTIPART_BODY_FAILURE", ERR_LIB_ASN1, ASN1_R_NO_MULTIPART_BODY_FAILURE}, + #else + {"NO_MULTIPART_BODY_FAILURE", 13, 210}, + #endif + #ifdef ASN1_R_NO_MULTIPART_BOUNDARY + {"NO_MULTIPART_BOUNDARY", ERR_LIB_ASN1, ASN1_R_NO_MULTIPART_BOUNDARY}, + #else + {"NO_MULTIPART_BOUNDARY", 13, 211}, + #endif + #ifdef ASN1_R_NO_SIG_CONTENT_TYPE + {"NO_SIG_CONTENT_TYPE", ERR_LIB_ASN1, ASN1_R_NO_SIG_CONTENT_TYPE}, + #else + {"NO_SIG_CONTENT_TYPE", 13, 212}, + #endif + #ifdef ASN1_R_NULL_IS_WRONG_LENGTH + {"NULL_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_NULL_IS_WRONG_LENGTH}, + #else + {"NULL_IS_WRONG_LENGTH", 13, 144}, + #endif + #ifdef ASN1_R_OBJECT_NOT_ASCII_FORMAT + {"OBJECT_NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_OBJECT_NOT_ASCII_FORMAT}, + #else + {"OBJECT_NOT_ASCII_FORMAT", 13, 191}, + #endif + #ifdef ASN1_R_ODD_NUMBER_OF_CHARS + {"ODD_NUMBER_OF_CHARS", ERR_LIB_ASN1, ASN1_R_ODD_NUMBER_OF_CHARS}, + #else + {"ODD_NUMBER_OF_CHARS", 13, 145}, + #endif + #ifdef ASN1_R_SECOND_NUMBER_TOO_LARGE + {"SECOND_NUMBER_TOO_LARGE", ERR_LIB_ASN1, ASN1_R_SECOND_NUMBER_TOO_LARGE}, + #else + {"SECOND_NUMBER_TOO_LARGE", 13, 147}, + #endif + #ifdef ASN1_R_SEQUENCE_LENGTH_MISMATCH + {"SEQUENCE_LENGTH_MISMATCH", ERR_LIB_ASN1, ASN1_R_SEQUENCE_LENGTH_MISMATCH}, + #else + {"SEQUENCE_LENGTH_MISMATCH", 13, 148}, + #endif + #ifdef ASN1_R_SEQUENCE_NOT_CONSTRUCTED + {"SEQUENCE_NOT_CONSTRUCTED", ERR_LIB_ASN1, ASN1_R_SEQUENCE_NOT_CONSTRUCTED}, + #else + {"SEQUENCE_NOT_CONSTRUCTED", 13, 149}, + #endif + #ifdef ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG + {"SEQUENCE_OR_SET_NEEDS_CONFIG", ERR_LIB_ASN1, ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG}, + #else + {"SEQUENCE_OR_SET_NEEDS_CONFIG", 13, 192}, + #endif + #ifdef ASN1_R_SHORT_LINE + {"SHORT_LINE", ERR_LIB_ASN1, ASN1_R_SHORT_LINE}, + #else + {"SHORT_LINE", 13, 150}, + #endif + #ifdef ASN1_R_SIG_INVALID_MIME_TYPE + {"SIG_INVALID_MIME_TYPE", ERR_LIB_ASN1, ASN1_R_SIG_INVALID_MIME_TYPE}, + #else + {"SIG_INVALID_MIME_TYPE", 13, 213}, + #endif + #ifdef ASN1_R_STREAMING_NOT_SUPPORTED + {"STREAMING_NOT_SUPPORTED", ERR_LIB_ASN1, ASN1_R_STREAMING_NOT_SUPPORTED}, + #else + {"STREAMING_NOT_SUPPORTED", 13, 202}, + #endif + #ifdef ASN1_R_STRING_TOO_LONG + {"STRING_TOO_LONG", ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG}, + #else + {"STRING_TOO_LONG", 13, 151}, + #endif + #ifdef ASN1_R_STRING_TOO_SHORT + {"STRING_TOO_SHORT", ERR_LIB_ASN1, ASN1_R_STRING_TOO_SHORT}, + #else + {"STRING_TOO_SHORT", 13, 152}, + #endif + #ifdef ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD + {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", ERR_LIB_ASN1, ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD}, + #else + {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", 13, 154}, + #endif + #ifdef ASN1_R_TIME_NOT_ASCII_FORMAT + {"TIME_NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_TIME_NOT_ASCII_FORMAT}, + #else + {"TIME_NOT_ASCII_FORMAT", 13, 193}, + #endif + #ifdef ASN1_R_TOO_LARGE + {"TOO_LARGE", ERR_LIB_ASN1, ASN1_R_TOO_LARGE}, + #else + {"TOO_LARGE", 13, 223}, + #endif + #ifdef ASN1_R_TOO_LONG + {"TOO_LONG", ERR_LIB_ASN1, ASN1_R_TOO_LONG}, + #else + {"TOO_LONG", 13, 155}, + #endif + #ifdef ASN1_R_TOO_SMALL + {"TOO_SMALL", ERR_LIB_ASN1, ASN1_R_TOO_SMALL}, + #else + {"TOO_SMALL", 13, 224}, + #endif + #ifdef ASN1_R_TYPE_NOT_CONSTRUCTED + {"TYPE_NOT_CONSTRUCTED", ERR_LIB_ASN1, ASN1_R_TYPE_NOT_CONSTRUCTED}, + #else + {"TYPE_NOT_CONSTRUCTED", 13, 156}, + #endif + #ifdef ASN1_R_TYPE_NOT_PRIMITIVE + {"TYPE_NOT_PRIMITIVE", ERR_LIB_ASN1, ASN1_R_TYPE_NOT_PRIMITIVE}, + #else + {"TYPE_NOT_PRIMITIVE", 13, 195}, + #endif + #ifdef ASN1_R_UNEXPECTED_EOC + {"UNEXPECTED_EOC", ERR_LIB_ASN1, ASN1_R_UNEXPECTED_EOC}, + #else + {"UNEXPECTED_EOC", 13, 159}, + #endif + #ifdef ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH + {"UNIVERSALSTRING_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH}, + #else + {"UNIVERSALSTRING_IS_WRONG_LENGTH", 13, 215}, + #endif + #ifdef ASN1_R_UNKNOWN_DIGEST + {"UNKNOWN_DIGEST", ERR_LIB_ASN1, ASN1_R_UNKNOWN_DIGEST}, + #else + {"UNKNOWN_DIGEST", 13, 229}, + #endif + #ifdef ASN1_R_UNKNOWN_FORMAT + {"UNKNOWN_FORMAT", ERR_LIB_ASN1, ASN1_R_UNKNOWN_FORMAT}, + #else + {"UNKNOWN_FORMAT", 13, 160}, + #endif + #ifdef ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM + {"UNKNOWN_MESSAGE_DIGEST_ALGORITHM", ERR_LIB_ASN1, ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM}, + #else + {"UNKNOWN_MESSAGE_DIGEST_ALGORITHM", 13, 161}, + #endif + #ifdef ASN1_R_UNKNOWN_OBJECT_TYPE + {"UNKNOWN_OBJECT_TYPE", ERR_LIB_ASN1, ASN1_R_UNKNOWN_OBJECT_TYPE}, + #else + {"UNKNOWN_OBJECT_TYPE", 13, 162}, + #endif + #ifdef ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE + {"UNKNOWN_PUBLIC_KEY_TYPE", ERR_LIB_ASN1, ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE}, + #else + {"UNKNOWN_PUBLIC_KEY_TYPE", 13, 163}, + #endif + #ifdef ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM + {"UNKNOWN_SIGNATURE_ALGORITHM", ERR_LIB_ASN1, ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM}, + #else + {"UNKNOWN_SIGNATURE_ALGORITHM", 13, 199}, + #endif + #ifdef ASN1_R_UNKNOWN_TAG + {"UNKNOWN_TAG", ERR_LIB_ASN1, ASN1_R_UNKNOWN_TAG}, + #else + {"UNKNOWN_TAG", 13, 194}, + #endif + #ifdef ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE + {"UNSUPPORTED_ANY_DEFINED_BY_TYPE", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE}, + #else + {"UNSUPPORTED_ANY_DEFINED_BY_TYPE", 13, 164}, + #endif + #ifdef ASN1_R_UNSUPPORTED_CIPHER + {"UNSUPPORTED_CIPHER", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_CIPHER}, + #else + {"UNSUPPORTED_CIPHER", 13, 228}, + #endif + #ifdef ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE + {"UNSUPPORTED_PUBLIC_KEY_TYPE", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE}, + #else + {"UNSUPPORTED_PUBLIC_KEY_TYPE", 13, 167}, + #endif + #ifdef ASN1_R_UNSUPPORTED_TYPE + {"UNSUPPORTED_TYPE", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_TYPE}, + #else + {"UNSUPPORTED_TYPE", 13, 196}, + #endif + #ifdef ASN1_R_UTCTIME_IS_TOO_SHORT + {"UTCTIME_IS_TOO_SHORT", ERR_LIB_ASN1, ASN1_R_UTCTIME_IS_TOO_SHORT}, + #else + {"UTCTIME_IS_TOO_SHORT", 13, 233}, + #endif + #ifdef ASN1_R_WRONG_INTEGER_TYPE + {"WRONG_INTEGER_TYPE", ERR_LIB_ASN1, ASN1_R_WRONG_INTEGER_TYPE}, + #else + {"WRONG_INTEGER_TYPE", 13, 225}, + #endif + #ifdef ASN1_R_WRONG_PUBLIC_KEY_TYPE + {"WRONG_PUBLIC_KEY_TYPE", ERR_LIB_ASN1, ASN1_R_WRONG_PUBLIC_KEY_TYPE}, + #else + {"WRONG_PUBLIC_KEY_TYPE", 13, 200}, + #endif + #ifdef ASN1_R_WRONG_TAG + {"WRONG_TAG", ERR_LIB_ASN1, ASN1_R_WRONG_TAG}, + #else + {"WRONG_TAG", 13, 168}, + #endif + #ifdef ASYNC_R_FAILED_TO_SET_POOL + {"FAILED_TO_SET_POOL", ERR_LIB_ASYNC, ASYNC_R_FAILED_TO_SET_POOL}, + #else + {"FAILED_TO_SET_POOL", 51, 101}, + #endif + #ifdef ASYNC_R_FAILED_TO_SWAP_CONTEXT + {"FAILED_TO_SWAP_CONTEXT", ERR_LIB_ASYNC, ASYNC_R_FAILED_TO_SWAP_CONTEXT}, + #else + {"FAILED_TO_SWAP_CONTEXT", 51, 102}, + #endif + #ifdef ASYNC_R_INIT_FAILED + {"INIT_FAILED", ERR_LIB_ASYNC, ASYNC_R_INIT_FAILED}, + #else + {"INIT_FAILED", 51, 105}, + #endif + #ifdef ASYNC_R_INVALID_POOL_SIZE + {"INVALID_POOL_SIZE", ERR_LIB_ASYNC, ASYNC_R_INVALID_POOL_SIZE}, + #else + {"INVALID_POOL_SIZE", 51, 103}, + #endif + #ifdef BIO_R_ACCEPT_ERROR + {"ACCEPT_ERROR", ERR_LIB_BIO, BIO_R_ACCEPT_ERROR}, + #else + {"ACCEPT_ERROR", 32, 100}, + #endif + #ifdef BIO_R_ADDRINFO_ADDR_IS_NOT_AF_INET + {"ADDRINFO_ADDR_IS_NOT_AF_INET", ERR_LIB_BIO, BIO_R_ADDRINFO_ADDR_IS_NOT_AF_INET}, + #else + {"ADDRINFO_ADDR_IS_NOT_AF_INET", 32, 141}, + #endif + #ifdef BIO_R_AMBIGUOUS_HOST_OR_SERVICE + {"AMBIGUOUS_HOST_OR_SERVICE", ERR_LIB_BIO, BIO_R_AMBIGUOUS_HOST_OR_SERVICE}, + #else + {"AMBIGUOUS_HOST_OR_SERVICE", 32, 129}, + #endif + #ifdef BIO_R_BAD_FOPEN_MODE + {"BAD_FOPEN_MODE", ERR_LIB_BIO, BIO_R_BAD_FOPEN_MODE}, + #else + {"BAD_FOPEN_MODE", 32, 101}, + #endif + #ifdef BIO_R_BROKEN_PIPE + {"BROKEN_PIPE", ERR_LIB_BIO, BIO_R_BROKEN_PIPE}, + #else + {"BROKEN_PIPE", 32, 124}, + #endif + #ifdef BIO_R_CONNECT_ERROR + {"CONNECT_ERROR", ERR_LIB_BIO, BIO_R_CONNECT_ERROR}, + #else + {"CONNECT_ERROR", 32, 103}, + #endif + #ifdef BIO_R_CONNECT_TIMEOUT + {"CONNECT_TIMEOUT", ERR_LIB_BIO, BIO_R_CONNECT_TIMEOUT}, + #else + {"CONNECT_TIMEOUT", 32, 147}, + #endif + #ifdef BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET + {"GETHOSTBYNAME_ADDR_IS_NOT_AF_INET", ERR_LIB_BIO, BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET}, + #else + {"GETHOSTBYNAME_ADDR_IS_NOT_AF_INET", 32, 107}, + #endif + #ifdef BIO_R_GETSOCKNAME_ERROR + {"GETSOCKNAME_ERROR", ERR_LIB_BIO, BIO_R_GETSOCKNAME_ERROR}, + #else + {"GETSOCKNAME_ERROR", 32, 132}, + #endif + #ifdef BIO_R_GETSOCKNAME_TRUNCATED_ADDRESS + {"GETSOCKNAME_TRUNCATED_ADDRESS", ERR_LIB_BIO, BIO_R_GETSOCKNAME_TRUNCATED_ADDRESS}, + #else + {"GETSOCKNAME_TRUNCATED_ADDRESS", 32, 133}, + #endif + #ifdef BIO_R_GETTING_SOCKTYPE + {"GETTING_SOCKTYPE", ERR_LIB_BIO, BIO_R_GETTING_SOCKTYPE}, + #else + {"GETTING_SOCKTYPE", 32, 134}, + #endif + #ifdef BIO_R_INVALID_ARGUMENT + {"INVALID_ARGUMENT", ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT}, + #else + {"INVALID_ARGUMENT", 32, 125}, + #endif + #ifdef BIO_R_INVALID_SOCKET + {"INVALID_SOCKET", ERR_LIB_BIO, BIO_R_INVALID_SOCKET}, + #else + {"INVALID_SOCKET", 32, 135}, + #endif + #ifdef BIO_R_IN_USE + {"IN_USE", ERR_LIB_BIO, BIO_R_IN_USE}, + #else + {"IN_USE", 32, 123}, + #endif + #ifdef BIO_R_LENGTH_TOO_LONG + {"LENGTH_TOO_LONG", ERR_LIB_BIO, BIO_R_LENGTH_TOO_LONG}, + #else + {"LENGTH_TOO_LONG", 32, 102}, + #endif + #ifdef BIO_R_LISTEN_V6_ONLY + {"LISTEN_V6_ONLY", ERR_LIB_BIO, BIO_R_LISTEN_V6_ONLY}, + #else + {"LISTEN_V6_ONLY", 32, 136}, + #endif + #ifdef BIO_R_LOCAL_ADDR_NOT_AVAILABLE + {"LOCAL_ADDR_NOT_AVAILABLE", ERR_LIB_BIO, BIO_R_LOCAL_ADDR_NOT_AVAILABLE}, + #else + {"LOCAL_ADDR_NOT_AVAILABLE", 32, 111}, + #endif + #ifdef BIO_R_LOOKUP_RETURNED_NOTHING + {"LOOKUP_RETURNED_NOTHING", ERR_LIB_BIO, BIO_R_LOOKUP_RETURNED_NOTHING}, + #else + {"LOOKUP_RETURNED_NOTHING", 32, 142}, + #endif + #ifdef BIO_R_MALFORMED_HOST_OR_SERVICE + {"MALFORMED_HOST_OR_SERVICE", ERR_LIB_BIO, BIO_R_MALFORMED_HOST_OR_SERVICE}, + #else + {"MALFORMED_HOST_OR_SERVICE", 32, 130}, + #endif + #ifdef BIO_R_NBIO_CONNECT_ERROR + {"NBIO_CONNECT_ERROR", ERR_LIB_BIO, BIO_R_NBIO_CONNECT_ERROR}, + #else + {"NBIO_CONNECT_ERROR", 32, 110}, + #endif + #ifdef BIO_R_NON_FATAL + {"NON_FATAL", ERR_LIB_BIO, BIO_R_NON_FATAL}, + #else + {"NON_FATAL", 32, 112}, + #endif + #ifdef BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED + {"NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED", ERR_LIB_BIO, BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED}, + #else + {"NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED", 32, 143}, + #endif + #ifdef BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED + {"NO_HOSTNAME_OR_SERVICE_SPECIFIED", ERR_LIB_BIO, BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED}, + #else + {"NO_HOSTNAME_OR_SERVICE_SPECIFIED", 32, 144}, + #endif + #ifdef BIO_R_NO_PORT_DEFINED + {"NO_PORT_DEFINED", ERR_LIB_BIO, BIO_R_NO_PORT_DEFINED}, + #else + {"NO_PORT_DEFINED", 32, 113}, + #endif + #ifdef BIO_R_NO_SUCH_FILE + {"NO_SUCH_FILE", ERR_LIB_BIO, BIO_R_NO_SUCH_FILE}, + #else + {"NO_SUCH_FILE", 32, 128}, + #endif + #ifdef BIO_R_PEER_ADDR_NOT_AVAILABLE + {"PEER_ADDR_NOT_AVAILABLE", ERR_LIB_BIO, BIO_R_PEER_ADDR_NOT_AVAILABLE}, + #else + {"PEER_ADDR_NOT_AVAILABLE", 32, 114}, + #endif + #ifdef BIO_R_PORT_MISMATCH + {"PORT_MISMATCH", ERR_LIB_BIO, BIO_R_PORT_MISMATCH}, + #else + {"PORT_MISMATCH", 32, 150}, + #endif + #ifdef BIO_R_TFO_DISABLED + {"TFO_DISABLED", ERR_LIB_BIO, BIO_R_TFO_DISABLED}, + #else + {"TFO_DISABLED", 32, 106}, + #endif + #ifdef BIO_R_TFO_NO_KERNEL_SUPPORT + {"TFO_NO_KERNEL_SUPPORT", ERR_LIB_BIO, BIO_R_TFO_NO_KERNEL_SUPPORT}, + #else + {"TFO_NO_KERNEL_SUPPORT", 32, 108}, + #endif + #ifdef BIO_R_TRANSFER_ERROR + {"TRANSFER_ERROR", ERR_LIB_BIO, BIO_R_TRANSFER_ERROR}, + #else + {"TRANSFER_ERROR", 32, 104}, + #endif + #ifdef BIO_R_TRANSFER_TIMEOUT + {"TRANSFER_TIMEOUT", ERR_LIB_BIO, BIO_R_TRANSFER_TIMEOUT}, + #else + {"TRANSFER_TIMEOUT", 32, 105}, + #endif + #ifdef BIO_R_UNABLE_TO_BIND_SOCKET + {"UNABLE_TO_BIND_SOCKET", ERR_LIB_BIO, BIO_R_UNABLE_TO_BIND_SOCKET}, + #else + {"UNABLE_TO_BIND_SOCKET", 32, 117}, + #endif + #ifdef BIO_R_UNABLE_TO_CREATE_SOCKET + {"UNABLE_TO_CREATE_SOCKET", ERR_LIB_BIO, BIO_R_UNABLE_TO_CREATE_SOCKET}, + #else + {"UNABLE_TO_CREATE_SOCKET", 32, 118}, + #endif + #ifdef BIO_R_UNABLE_TO_KEEPALIVE + {"UNABLE_TO_KEEPALIVE", ERR_LIB_BIO, BIO_R_UNABLE_TO_KEEPALIVE}, + #else + {"UNABLE_TO_KEEPALIVE", 32, 137}, + #endif + #ifdef BIO_R_UNABLE_TO_LISTEN_SOCKET + {"UNABLE_TO_LISTEN_SOCKET", ERR_LIB_BIO, BIO_R_UNABLE_TO_LISTEN_SOCKET}, + #else + {"UNABLE_TO_LISTEN_SOCKET", 32, 119}, + #endif + #ifdef BIO_R_UNABLE_TO_NODELAY + {"UNABLE_TO_NODELAY", ERR_LIB_BIO, BIO_R_UNABLE_TO_NODELAY}, + #else + {"UNABLE_TO_NODELAY", 32, 138}, + #endif + #ifdef BIO_R_UNABLE_TO_REUSEADDR + {"UNABLE_TO_REUSEADDR", ERR_LIB_BIO, BIO_R_UNABLE_TO_REUSEADDR}, + #else + {"UNABLE_TO_REUSEADDR", 32, 139}, + #endif + #ifdef BIO_R_UNABLE_TO_TFO + {"UNABLE_TO_TFO", ERR_LIB_BIO, BIO_R_UNABLE_TO_TFO}, + #else + {"UNABLE_TO_TFO", 32, 109}, + #endif + #ifdef BIO_R_UNAVAILABLE_IP_FAMILY + {"UNAVAILABLE_IP_FAMILY", ERR_LIB_BIO, BIO_R_UNAVAILABLE_IP_FAMILY}, + #else + {"UNAVAILABLE_IP_FAMILY", 32, 145}, + #endif + #ifdef BIO_R_UNINITIALIZED + {"UNINITIALIZED", ERR_LIB_BIO, BIO_R_UNINITIALIZED}, + #else + {"UNINITIALIZED", 32, 120}, + #endif + #ifdef BIO_R_UNKNOWN_INFO_TYPE + {"UNKNOWN_INFO_TYPE", ERR_LIB_BIO, BIO_R_UNKNOWN_INFO_TYPE}, + #else + {"UNKNOWN_INFO_TYPE", 32, 140}, + #endif + #ifdef BIO_R_UNSUPPORTED_IP_FAMILY + {"UNSUPPORTED_IP_FAMILY", ERR_LIB_BIO, BIO_R_UNSUPPORTED_IP_FAMILY}, + #else + {"UNSUPPORTED_IP_FAMILY", 32, 146}, + #endif + #ifdef BIO_R_UNSUPPORTED_METHOD + {"UNSUPPORTED_METHOD", ERR_LIB_BIO, BIO_R_UNSUPPORTED_METHOD}, + #else + {"UNSUPPORTED_METHOD", 32, 121}, + #endif + #ifdef BIO_R_UNSUPPORTED_PROTOCOL_FAMILY + {"UNSUPPORTED_PROTOCOL_FAMILY", ERR_LIB_BIO, BIO_R_UNSUPPORTED_PROTOCOL_FAMILY}, + #else + {"UNSUPPORTED_PROTOCOL_FAMILY", 32, 131}, + #endif + #ifdef BIO_R_WRITE_TO_READ_ONLY_BIO + {"WRITE_TO_READ_ONLY_BIO", ERR_LIB_BIO, BIO_R_WRITE_TO_READ_ONLY_BIO}, + #else + {"WRITE_TO_READ_ONLY_BIO", 32, 126}, + #endif + #ifdef BIO_R_WSASTARTUP + {"WSASTARTUP", ERR_LIB_BIO, BIO_R_WSASTARTUP}, + #else + {"WSASTARTUP", 32, 122}, + #endif + #ifdef BN_R_ARG2_LT_ARG3 + {"ARG2_LT_ARG3", ERR_LIB_BN, BN_R_ARG2_LT_ARG3}, + #else + {"ARG2_LT_ARG3", 3, 100}, + #endif + #ifdef BN_R_BAD_RECIPROCAL + {"BAD_RECIPROCAL", ERR_LIB_BN, BN_R_BAD_RECIPROCAL}, + #else + {"BAD_RECIPROCAL", 3, 101}, + #endif + #ifdef BN_R_BIGNUM_TOO_LONG + {"BIGNUM_TOO_LONG", ERR_LIB_BN, BN_R_BIGNUM_TOO_LONG}, + #else + {"BIGNUM_TOO_LONG", 3, 114}, + #endif + #ifdef BN_R_BITS_TOO_SMALL + {"BITS_TOO_SMALL", ERR_LIB_BN, BN_R_BITS_TOO_SMALL}, + #else + {"BITS_TOO_SMALL", 3, 118}, + #endif + #ifdef BN_R_CALLED_WITH_EVEN_MODULUS + {"CALLED_WITH_EVEN_MODULUS", ERR_LIB_BN, BN_R_CALLED_WITH_EVEN_MODULUS}, + #else + {"CALLED_WITH_EVEN_MODULUS", 3, 102}, + #endif + #ifdef BN_R_DIV_BY_ZERO + {"DIV_BY_ZERO", ERR_LIB_BN, BN_R_DIV_BY_ZERO}, + #else + {"DIV_BY_ZERO", 3, 103}, + #endif + #ifdef BN_R_ENCODING_ERROR + {"ENCODING_ERROR", ERR_LIB_BN, BN_R_ENCODING_ERROR}, + #else + {"ENCODING_ERROR", 3, 104}, + #endif + #ifdef BN_R_EXPAND_ON_STATIC_BIGNUM_DATA + {"EXPAND_ON_STATIC_BIGNUM_DATA", ERR_LIB_BN, BN_R_EXPAND_ON_STATIC_BIGNUM_DATA}, + #else + {"EXPAND_ON_STATIC_BIGNUM_DATA", 3, 105}, + #endif + #ifdef BN_R_INPUT_NOT_REDUCED + {"INPUT_NOT_REDUCED", ERR_LIB_BN, BN_R_INPUT_NOT_REDUCED}, + #else + {"INPUT_NOT_REDUCED", 3, 110}, + #endif + #ifdef BN_R_INVALID_LENGTH + {"INVALID_LENGTH", ERR_LIB_BN, BN_R_INVALID_LENGTH}, + #else + {"INVALID_LENGTH", 3, 106}, + #endif + #ifdef BN_R_INVALID_RANGE + {"INVALID_RANGE", ERR_LIB_BN, BN_R_INVALID_RANGE}, + #else + {"INVALID_RANGE", 3, 115}, + #endif + #ifdef BN_R_INVALID_SHIFT + {"INVALID_SHIFT", ERR_LIB_BN, BN_R_INVALID_SHIFT}, + #else + {"INVALID_SHIFT", 3, 119}, + #endif + #ifdef BN_R_NOT_A_SQUARE + {"NOT_A_SQUARE", ERR_LIB_BN, BN_R_NOT_A_SQUARE}, + #else + {"NOT_A_SQUARE", 3, 111}, + #endif + #ifdef BN_R_NOT_INITIALIZED + {"NOT_INITIALIZED", ERR_LIB_BN, BN_R_NOT_INITIALIZED}, + #else + {"NOT_INITIALIZED", 3, 107}, + #endif + #ifdef BN_R_NO_INVERSE + {"NO_INVERSE", ERR_LIB_BN, BN_R_NO_INVERSE}, + #else + {"NO_INVERSE", 3, 108}, + #endif + #ifdef BN_R_NO_PRIME_CANDIDATE + {"NO_PRIME_CANDIDATE", ERR_LIB_BN, BN_R_NO_PRIME_CANDIDATE}, + #else + {"NO_PRIME_CANDIDATE", 3, 121}, + #endif + #ifdef BN_R_NO_SOLUTION + {"NO_SOLUTION", ERR_LIB_BN, BN_R_NO_SOLUTION}, + #else + {"NO_SOLUTION", 3, 116}, + #endif + #ifdef BN_R_NO_SUITABLE_DIGEST + {"NO_SUITABLE_DIGEST", ERR_LIB_BN, BN_R_NO_SUITABLE_DIGEST}, + #else + {"NO_SUITABLE_DIGEST", 3, 120}, + #endif + #ifdef BN_R_PRIVATE_KEY_TOO_LARGE + {"PRIVATE_KEY_TOO_LARGE", ERR_LIB_BN, BN_R_PRIVATE_KEY_TOO_LARGE}, + #else + {"PRIVATE_KEY_TOO_LARGE", 3, 117}, + #endif + #ifdef BN_R_P_IS_NOT_PRIME + {"P_IS_NOT_PRIME", ERR_LIB_BN, BN_R_P_IS_NOT_PRIME}, + #else + {"P_IS_NOT_PRIME", 3, 112}, + #endif + #ifdef BN_R_TOO_MANY_ITERATIONS + {"TOO_MANY_ITERATIONS", ERR_LIB_BN, BN_R_TOO_MANY_ITERATIONS}, + #else + {"TOO_MANY_ITERATIONS", 3, 113}, + #endif + #ifdef BN_R_TOO_MANY_TEMPORARY_VARIABLES + {"TOO_MANY_TEMPORARY_VARIABLES", ERR_LIB_BN, BN_R_TOO_MANY_TEMPORARY_VARIABLES}, + #else + {"TOO_MANY_TEMPORARY_VARIABLES", 3, 109}, + #endif + #ifdef CMP_R_ALGORITHM_NOT_SUPPORTED + {"ALGORITHM_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_ALGORITHM_NOT_SUPPORTED}, + #else + {"ALGORITHM_NOT_SUPPORTED", 58, 139}, + #endif + #ifdef CMP_R_BAD_CHECKAFTER_IN_POLLREP + {"BAD_CHECKAFTER_IN_POLLREP", ERR_LIB_CMP, CMP_R_BAD_CHECKAFTER_IN_POLLREP}, + #else + {"BAD_CHECKAFTER_IN_POLLREP", 58, 167}, + #endif + #ifdef CMP_R_BAD_REQUEST_ID + {"BAD_REQUEST_ID", ERR_LIB_CMP, CMP_R_BAD_REQUEST_ID}, + #else + {"BAD_REQUEST_ID", 58, 108}, + #endif + #ifdef CMP_R_CERTHASH_UNMATCHED + {"CERTHASH_UNMATCHED", ERR_LIB_CMP, CMP_R_CERTHASH_UNMATCHED}, + #else + {"CERTHASH_UNMATCHED", 58, 156}, + #endif + #ifdef CMP_R_CERTID_NOT_FOUND + {"CERTID_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTID_NOT_FOUND}, + #else + {"CERTID_NOT_FOUND", 58, 109}, + #endif + #ifdef CMP_R_CERTIFICATE_NOT_ACCEPTED + {"CERTIFICATE_NOT_ACCEPTED", ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_ACCEPTED}, + #else + {"CERTIFICATE_NOT_ACCEPTED", 58, 169}, + #endif + #ifdef CMP_R_CERTIFICATE_NOT_FOUND + {"CERTIFICATE_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_FOUND}, + #else + {"CERTIFICATE_NOT_FOUND", 58, 112}, + #endif + #ifdef CMP_R_CERTREQMSG_NOT_FOUND + {"CERTREQMSG_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTREQMSG_NOT_FOUND}, + #else + {"CERTREQMSG_NOT_FOUND", 58, 157}, + #endif + #ifdef CMP_R_CERTRESPONSE_NOT_FOUND + {"CERTRESPONSE_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTRESPONSE_NOT_FOUND}, + #else + {"CERTRESPONSE_NOT_FOUND", 58, 113}, + #endif + #ifdef CMP_R_CERT_AND_KEY_DO_NOT_MATCH + {"CERT_AND_KEY_DO_NOT_MATCH", ERR_LIB_CMP, CMP_R_CERT_AND_KEY_DO_NOT_MATCH}, + #else + {"CERT_AND_KEY_DO_NOT_MATCH", 58, 114}, + #endif + #ifdef CMP_R_CHECKAFTER_OUT_OF_RANGE + {"CHECKAFTER_OUT_OF_RANGE", ERR_LIB_CMP, CMP_R_CHECKAFTER_OUT_OF_RANGE}, + #else + {"CHECKAFTER_OUT_OF_RANGE", 58, 181}, + #endif + #ifdef CMP_R_ENCOUNTERED_KEYUPDATEWARNING + {"ENCOUNTERED_KEYUPDATEWARNING", ERR_LIB_CMP, CMP_R_ENCOUNTERED_KEYUPDATEWARNING}, + #else + {"ENCOUNTERED_KEYUPDATEWARNING", 58, 176}, + #endif + #ifdef CMP_R_ENCOUNTERED_WAITING + {"ENCOUNTERED_WAITING", ERR_LIB_CMP, CMP_R_ENCOUNTERED_WAITING}, + #else + {"ENCOUNTERED_WAITING", 58, 162}, + #endif + #ifdef CMP_R_ERROR_CALCULATING_PROTECTION + {"ERROR_CALCULATING_PROTECTION", ERR_LIB_CMP, CMP_R_ERROR_CALCULATING_PROTECTION}, + #else + {"ERROR_CALCULATING_PROTECTION", 58, 115}, + #endif + #ifdef CMP_R_ERROR_CREATING_CERTCONF + {"ERROR_CREATING_CERTCONF", ERR_LIB_CMP, CMP_R_ERROR_CREATING_CERTCONF}, + #else + {"ERROR_CREATING_CERTCONF", 58, 116}, + #endif + #ifdef CMP_R_ERROR_CREATING_CERTREP + {"ERROR_CREATING_CERTREP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_CERTREP}, + #else + {"ERROR_CREATING_CERTREP", 58, 117}, + #endif + #ifdef CMP_R_ERROR_CREATING_CERTREQ + {"ERROR_CREATING_CERTREQ", ERR_LIB_CMP, CMP_R_ERROR_CREATING_CERTREQ}, + #else + {"ERROR_CREATING_CERTREQ", 58, 163}, + #endif + #ifdef CMP_R_ERROR_CREATING_ERROR + {"ERROR_CREATING_ERROR", ERR_LIB_CMP, CMP_R_ERROR_CREATING_ERROR}, + #else + {"ERROR_CREATING_ERROR", 58, 118}, + #endif + #ifdef CMP_R_ERROR_CREATING_GENM + {"ERROR_CREATING_GENM", ERR_LIB_CMP, CMP_R_ERROR_CREATING_GENM}, + #else + {"ERROR_CREATING_GENM", 58, 119}, + #endif + #ifdef CMP_R_ERROR_CREATING_GENP + {"ERROR_CREATING_GENP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_GENP}, + #else + {"ERROR_CREATING_GENP", 58, 120}, + #endif + #ifdef CMP_R_ERROR_CREATING_PKICONF + {"ERROR_CREATING_PKICONF", ERR_LIB_CMP, CMP_R_ERROR_CREATING_PKICONF}, + #else + {"ERROR_CREATING_PKICONF", 58, 122}, + #endif + #ifdef CMP_R_ERROR_CREATING_POLLREP + {"ERROR_CREATING_POLLREP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_POLLREP}, + #else + {"ERROR_CREATING_POLLREP", 58, 123}, + #endif + #ifdef CMP_R_ERROR_CREATING_POLLREQ + {"ERROR_CREATING_POLLREQ", ERR_LIB_CMP, CMP_R_ERROR_CREATING_POLLREQ}, + #else + {"ERROR_CREATING_POLLREQ", 58, 124}, + #endif + #ifdef CMP_R_ERROR_CREATING_RP + {"ERROR_CREATING_RP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_RP}, + #else + {"ERROR_CREATING_RP", 58, 125}, + #endif + #ifdef CMP_R_ERROR_CREATING_RR + {"ERROR_CREATING_RR", ERR_LIB_CMP, CMP_R_ERROR_CREATING_RR}, + #else + {"ERROR_CREATING_RR", 58, 126}, + #endif + #ifdef CMP_R_ERROR_PARSING_PKISTATUS + {"ERROR_PARSING_PKISTATUS", ERR_LIB_CMP, CMP_R_ERROR_PARSING_PKISTATUS}, + #else + {"ERROR_PARSING_PKISTATUS", 58, 107}, + #endif + #ifdef CMP_R_ERROR_PROCESSING_MESSAGE + {"ERROR_PROCESSING_MESSAGE", ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE}, + #else + {"ERROR_PROCESSING_MESSAGE", 58, 158}, + #endif + #ifdef CMP_R_ERROR_PROTECTING_MESSAGE + {"ERROR_PROTECTING_MESSAGE", ERR_LIB_CMP, CMP_R_ERROR_PROTECTING_MESSAGE}, + #else + {"ERROR_PROTECTING_MESSAGE", 58, 127}, + #endif + #ifdef CMP_R_ERROR_SETTING_CERTHASH + {"ERROR_SETTING_CERTHASH", ERR_LIB_CMP, CMP_R_ERROR_SETTING_CERTHASH}, + #else + {"ERROR_SETTING_CERTHASH", 58, 128}, + #endif + #ifdef CMP_R_ERROR_UNEXPECTED_CERTCONF + {"ERROR_UNEXPECTED_CERTCONF", ERR_LIB_CMP, CMP_R_ERROR_UNEXPECTED_CERTCONF}, + #else + {"ERROR_UNEXPECTED_CERTCONF", 58, 160}, + #endif + #ifdef CMP_R_ERROR_VALIDATING_PROTECTION + {"ERROR_VALIDATING_PROTECTION", ERR_LIB_CMP, CMP_R_ERROR_VALIDATING_PROTECTION}, + #else + {"ERROR_VALIDATING_PROTECTION", 58, 140}, + #endif + #ifdef CMP_R_ERROR_VALIDATING_SIGNATURE + {"ERROR_VALIDATING_SIGNATURE", ERR_LIB_CMP, CMP_R_ERROR_VALIDATING_SIGNATURE}, + #else + {"ERROR_VALIDATING_SIGNATURE", 58, 171}, + #endif + #ifdef CMP_R_EXPECTED_POLLREQ + {"EXPECTED_POLLREQ", ERR_LIB_CMP, CMP_R_EXPECTED_POLLREQ}, + #else + {"EXPECTED_POLLREQ", 58, 104}, + #endif + #ifdef CMP_R_FAILED_BUILDING_OWN_CHAIN + {"FAILED_BUILDING_OWN_CHAIN", ERR_LIB_CMP, CMP_R_FAILED_BUILDING_OWN_CHAIN}, + #else + {"FAILED_BUILDING_OWN_CHAIN", 58, 164}, + #endif + #ifdef CMP_R_FAILED_EXTRACTING_CENTRAL_GEN_KEY + {"FAILED_EXTRACTING_CENTRAL_GEN_KEY", ERR_LIB_CMP, CMP_R_FAILED_EXTRACTING_CENTRAL_GEN_KEY}, + #else + {"FAILED_EXTRACTING_CENTRAL_GEN_KEY", 58, 203}, + #endif + #ifdef CMP_R_FAILED_EXTRACTING_PUBKEY + {"FAILED_EXTRACTING_PUBKEY", ERR_LIB_CMP, CMP_R_FAILED_EXTRACTING_PUBKEY}, + #else + {"FAILED_EXTRACTING_PUBKEY", 58, 141}, + #endif + #ifdef CMP_R_FAILURE_OBTAINING_RANDOM + {"FAILURE_OBTAINING_RANDOM", ERR_LIB_CMP, CMP_R_FAILURE_OBTAINING_RANDOM}, + #else + {"FAILURE_OBTAINING_RANDOM", 58, 110}, + #endif + #ifdef CMP_R_FAIL_INFO_OUT_OF_RANGE + {"FAIL_INFO_OUT_OF_RANGE", ERR_LIB_CMP, CMP_R_FAIL_INFO_OUT_OF_RANGE}, + #else + {"FAIL_INFO_OUT_OF_RANGE", 58, 129}, + #endif + #ifdef CMP_R_GENERATE_CERTREQTEMPLATE + {"GENERATE_CERTREQTEMPLATE", ERR_LIB_CMP, CMP_R_GENERATE_CERTREQTEMPLATE}, + #else + {"GENERATE_CERTREQTEMPLATE", 58, 197}, + #endif + #ifdef CMP_R_GENERATE_CRLSTATUS + {"GENERATE_CRLSTATUS", ERR_LIB_CMP, CMP_R_GENERATE_CRLSTATUS}, + #else + {"GENERATE_CRLSTATUS", 58, 198}, + #endif + #ifdef CMP_R_GETTING_GENP + {"GETTING_GENP", ERR_LIB_CMP, CMP_R_GETTING_GENP}, + #else + {"GETTING_GENP", 58, 192}, + #endif + #ifdef CMP_R_GET_ITAV + {"GET_ITAV", ERR_LIB_CMP, CMP_R_GET_ITAV}, + #else + {"GET_ITAV", 58, 199}, + #endif + #ifdef CMP_R_INVALID_ARGS + {"INVALID_ARGS", ERR_LIB_CMP, CMP_R_INVALID_ARGS}, + #else + {"INVALID_ARGS", 58, 100}, + #endif + #ifdef CMP_R_INVALID_GENP + {"INVALID_GENP", ERR_LIB_CMP, CMP_R_INVALID_GENP}, + #else + {"INVALID_GENP", 58, 193}, + #endif + #ifdef CMP_R_INVALID_KEYSPEC + {"INVALID_KEYSPEC", ERR_LIB_CMP, CMP_R_INVALID_KEYSPEC}, + #else + {"INVALID_KEYSPEC", 58, 202}, + #endif + #ifdef CMP_R_INVALID_OPTION + {"INVALID_OPTION", ERR_LIB_CMP, CMP_R_INVALID_OPTION}, + #else + {"INVALID_OPTION", 58, 174}, + #endif + #ifdef CMP_R_INVALID_ROOTCAKEYUPDATE + {"INVALID_ROOTCAKEYUPDATE", ERR_LIB_CMP, CMP_R_INVALID_ROOTCAKEYUPDATE}, + #else + {"INVALID_ROOTCAKEYUPDATE", 58, 195}, + #endif + #ifdef CMP_R_MISSING_CENTRAL_GEN_KEY + {"MISSING_CENTRAL_GEN_KEY", ERR_LIB_CMP, CMP_R_MISSING_CENTRAL_GEN_KEY}, + #else + {"MISSING_CENTRAL_GEN_KEY", 58, 204}, + #endif + #ifdef CMP_R_MISSING_CERTID + {"MISSING_CERTID", ERR_LIB_CMP, CMP_R_MISSING_CERTID}, + #else + {"MISSING_CERTID", 58, 165}, + #endif + #ifdef CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION + {"MISSING_KEY_INPUT_FOR_CREATING_PROTECTION", ERR_LIB_CMP, CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION}, + #else + {"MISSING_KEY_INPUT_FOR_CREATING_PROTECTION", 58, 130}, + #endif + #ifdef CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE + {"MISSING_KEY_USAGE_DIGITALSIGNATURE", ERR_LIB_CMP, CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE}, + #else + {"MISSING_KEY_USAGE_DIGITALSIGNATURE", 58, 142}, + #endif + #ifdef CMP_R_MISSING_P10CSR + {"MISSING_P10CSR", ERR_LIB_CMP, CMP_R_MISSING_P10CSR}, + #else + {"MISSING_P10CSR", 58, 121}, + #endif + #ifdef CMP_R_MISSING_PBM_SECRET + {"MISSING_PBM_SECRET", ERR_LIB_CMP, CMP_R_MISSING_PBM_SECRET}, + #else + {"MISSING_PBM_SECRET", 58, 166}, + #endif + #ifdef CMP_R_MISSING_PRIVATE_KEY + {"MISSING_PRIVATE_KEY", ERR_LIB_CMP, CMP_R_MISSING_PRIVATE_KEY}, + #else + {"MISSING_PRIVATE_KEY", 58, 131}, + #endif + #ifdef CMP_R_MISSING_PRIVATE_KEY_FOR_POPO + {"MISSING_PRIVATE_KEY_FOR_POPO", ERR_LIB_CMP, CMP_R_MISSING_PRIVATE_KEY_FOR_POPO}, + #else + {"MISSING_PRIVATE_KEY_FOR_POPO", 58, 190}, + #endif + #ifdef CMP_R_MISSING_PROTECTION + {"MISSING_PROTECTION", ERR_LIB_CMP, CMP_R_MISSING_PROTECTION}, + #else + {"MISSING_PROTECTION", 58, 143}, + #endif + #ifdef CMP_R_MISSING_PUBLIC_KEY + {"MISSING_PUBLIC_KEY", ERR_LIB_CMP, CMP_R_MISSING_PUBLIC_KEY}, + #else + {"MISSING_PUBLIC_KEY", 58, 183}, + #endif + #ifdef CMP_R_MISSING_REFERENCE_CERT + {"MISSING_REFERENCE_CERT", ERR_LIB_CMP, CMP_R_MISSING_REFERENCE_CERT}, + #else + {"MISSING_REFERENCE_CERT", 58, 168}, + #endif + #ifdef CMP_R_MISSING_SECRET + {"MISSING_SECRET", ERR_LIB_CMP, CMP_R_MISSING_SECRET}, + #else + {"MISSING_SECRET", 58, 178}, + #endif + #ifdef CMP_R_MISSING_SENDER_IDENTIFICATION + {"MISSING_SENDER_IDENTIFICATION", ERR_LIB_CMP, CMP_R_MISSING_SENDER_IDENTIFICATION}, + #else + {"MISSING_SENDER_IDENTIFICATION", 58, 111}, + #endif + #ifdef CMP_R_MISSING_TRUST_ANCHOR + {"MISSING_TRUST_ANCHOR", ERR_LIB_CMP, CMP_R_MISSING_TRUST_ANCHOR}, + #else + {"MISSING_TRUST_ANCHOR", 58, 179}, + #endif + #ifdef CMP_R_MISSING_TRUST_STORE + {"MISSING_TRUST_STORE", ERR_LIB_CMP, CMP_R_MISSING_TRUST_STORE}, + #else + {"MISSING_TRUST_STORE", 58, 144}, + #endif + #ifdef CMP_R_MULTIPLE_REQUESTS_NOT_SUPPORTED + {"MULTIPLE_REQUESTS_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_MULTIPLE_REQUESTS_NOT_SUPPORTED}, + #else + {"MULTIPLE_REQUESTS_NOT_SUPPORTED", 58, 161}, + #endif + #ifdef CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED + {"MULTIPLE_RESPONSES_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED}, + #else + {"MULTIPLE_RESPONSES_NOT_SUPPORTED", 58, 170}, + #endif + #ifdef CMP_R_MULTIPLE_SAN_SOURCES + {"MULTIPLE_SAN_SOURCES", ERR_LIB_CMP, CMP_R_MULTIPLE_SAN_SOURCES}, + #else + {"MULTIPLE_SAN_SOURCES", 58, 102}, + #endif + #ifdef CMP_R_NO_STDIO + {"NO_STDIO", ERR_LIB_CMP, CMP_R_NO_STDIO}, + #else + {"NO_STDIO", 58, 194}, + #endif + #ifdef CMP_R_NO_SUITABLE_SENDER_CERT + {"NO_SUITABLE_SENDER_CERT", ERR_LIB_CMP, CMP_R_NO_SUITABLE_SENDER_CERT}, + #else + {"NO_SUITABLE_SENDER_CERT", 58, 145}, + #endif + #ifdef CMP_R_NULL_ARGUMENT + {"NULL_ARGUMENT", ERR_LIB_CMP, CMP_R_NULL_ARGUMENT}, + #else + {"NULL_ARGUMENT", 58, 103}, + #endif + #ifdef CMP_R_PKIBODY_ERROR + {"PKIBODY_ERROR", ERR_LIB_CMP, CMP_R_PKIBODY_ERROR}, + #else + {"PKIBODY_ERROR", 58, 146}, + #endif + #ifdef CMP_R_PKISTATUSINFO_NOT_FOUND + {"PKISTATUSINFO_NOT_FOUND", ERR_LIB_CMP, CMP_R_PKISTATUSINFO_NOT_FOUND}, + #else + {"PKISTATUSINFO_NOT_FOUND", 58, 132}, + #endif + #ifdef CMP_R_POLLING_FAILED + {"POLLING_FAILED", ERR_LIB_CMP, CMP_R_POLLING_FAILED}, + #else + {"POLLING_FAILED", 58, 172}, + #endif + #ifdef CMP_R_POTENTIALLY_INVALID_CERTIFICATE + {"POTENTIALLY_INVALID_CERTIFICATE", ERR_LIB_CMP, CMP_R_POTENTIALLY_INVALID_CERTIFICATE}, + #else + {"POTENTIALLY_INVALID_CERTIFICATE", 58, 147}, + #endif + #ifdef CMP_R_RECEIVED_ERROR + {"RECEIVED_ERROR", ERR_LIB_CMP, CMP_R_RECEIVED_ERROR}, + #else + {"RECEIVED_ERROR", 58, 180}, + #endif + #ifdef CMP_R_RECIPNONCE_UNMATCHED + {"RECIPNONCE_UNMATCHED", ERR_LIB_CMP, CMP_R_RECIPNONCE_UNMATCHED}, + #else + {"RECIPNONCE_UNMATCHED", 58, 148}, + #endif + #ifdef CMP_R_REQUEST_NOT_ACCEPTED + {"REQUEST_NOT_ACCEPTED", ERR_LIB_CMP, CMP_R_REQUEST_NOT_ACCEPTED}, + #else + {"REQUEST_NOT_ACCEPTED", 58, 149}, + #endif + #ifdef CMP_R_REQUEST_REJECTED_BY_SERVER + {"REQUEST_REJECTED_BY_SERVER", ERR_LIB_CMP, CMP_R_REQUEST_REJECTED_BY_SERVER}, + #else + {"REQUEST_REJECTED_BY_SERVER", 58, 182}, + #endif + #ifdef CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED + {"SENDER_GENERALNAME_TYPE_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED}, + #else + {"SENDER_GENERALNAME_TYPE_NOT_SUPPORTED", 58, 150}, + #endif + #ifdef CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG + {"SRVCERT_DOES_NOT_VALIDATE_MSG", ERR_LIB_CMP, CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG}, + #else + {"SRVCERT_DOES_NOT_VALIDATE_MSG", 58, 151}, + #endif + #ifdef CMP_R_TOTAL_TIMEOUT + {"TOTAL_TIMEOUT", ERR_LIB_CMP, CMP_R_TOTAL_TIMEOUT}, + #else + {"TOTAL_TIMEOUT", 58, 184}, + #endif + #ifdef CMP_R_TRANSACTIONID_UNMATCHED + {"TRANSACTIONID_UNMATCHED", ERR_LIB_CMP, CMP_R_TRANSACTIONID_UNMATCHED}, + #else + {"TRANSACTIONID_UNMATCHED", 58, 152}, + #endif + #ifdef CMP_R_TRANSFER_ERROR + {"TRANSFER_ERROR", ERR_LIB_CMP, CMP_R_TRANSFER_ERROR}, + #else + {"TRANSFER_ERROR", 58, 159}, + #endif + #ifdef CMP_R_UNCLEAN_CTX + {"UNCLEAN_CTX", ERR_LIB_CMP, CMP_R_UNCLEAN_CTX}, + #else + {"UNCLEAN_CTX", 58, 191}, + #endif + #ifdef CMP_R_UNEXPECTED_CENTRAL_GEN_KEY + {"UNEXPECTED_CENTRAL_GEN_KEY", ERR_LIB_CMP, CMP_R_UNEXPECTED_CENTRAL_GEN_KEY}, + #else + {"UNEXPECTED_CENTRAL_GEN_KEY", 58, 205}, + #endif + #ifdef CMP_R_UNEXPECTED_CERTPROFILE + {"UNEXPECTED_CERTPROFILE", ERR_LIB_CMP, CMP_R_UNEXPECTED_CERTPROFILE}, + #else + {"UNEXPECTED_CERTPROFILE", 58, 196}, + #endif + #ifdef CMP_R_UNEXPECTED_CRLSTATUSLIST + {"UNEXPECTED_CRLSTATUSLIST", ERR_LIB_CMP, CMP_R_UNEXPECTED_CRLSTATUSLIST}, + #else + {"UNEXPECTED_CRLSTATUSLIST", 58, 201}, + #endif + #ifdef CMP_R_UNEXPECTED_PKIBODY + {"UNEXPECTED_PKIBODY", ERR_LIB_CMP, CMP_R_UNEXPECTED_PKIBODY}, + #else + {"UNEXPECTED_PKIBODY", 58, 133}, + #endif + #ifdef CMP_R_UNEXPECTED_PKISTATUS + {"UNEXPECTED_PKISTATUS", ERR_LIB_CMP, CMP_R_UNEXPECTED_PKISTATUS}, + #else + {"UNEXPECTED_PKISTATUS", 58, 185}, + #endif + #ifdef CMP_R_UNEXPECTED_POLLREQ + {"UNEXPECTED_POLLREQ", ERR_LIB_CMP, CMP_R_UNEXPECTED_POLLREQ}, + #else + {"UNEXPECTED_POLLREQ", 58, 105}, + #endif + #ifdef CMP_R_UNEXPECTED_PVNO + {"UNEXPECTED_PVNO", ERR_LIB_CMP, CMP_R_UNEXPECTED_PVNO}, + #else + {"UNEXPECTED_PVNO", 58, 153}, + #endif + #ifdef CMP_R_UNEXPECTED_SENDER + {"UNEXPECTED_SENDER", ERR_LIB_CMP, CMP_R_UNEXPECTED_SENDER}, + #else + {"UNEXPECTED_SENDER", 58, 106}, + #endif + #ifdef CMP_R_UNKNOWN_ALGORITHM_ID + {"UNKNOWN_ALGORITHM_ID", ERR_LIB_CMP, CMP_R_UNKNOWN_ALGORITHM_ID}, + #else + {"UNKNOWN_ALGORITHM_ID", 58, 134}, + #endif + #ifdef CMP_R_UNKNOWN_CERT_TYPE + {"UNKNOWN_CERT_TYPE", ERR_LIB_CMP, CMP_R_UNKNOWN_CERT_TYPE}, + #else + {"UNKNOWN_CERT_TYPE", 58, 135}, + #endif + #ifdef CMP_R_UNKNOWN_CRL_ISSUER + {"UNKNOWN_CRL_ISSUER", ERR_LIB_CMP, CMP_R_UNKNOWN_CRL_ISSUER}, + #else + {"UNKNOWN_CRL_ISSUER", 58, 200}, + #endif + #ifdef CMP_R_UNKNOWN_PKISTATUS + {"UNKNOWN_PKISTATUS", ERR_LIB_CMP, CMP_R_UNKNOWN_PKISTATUS}, + #else + {"UNKNOWN_PKISTATUS", 58, 186}, + #endif + #ifdef CMP_R_UNSUPPORTED_ALGORITHM + {"UNSUPPORTED_ALGORITHM", ERR_LIB_CMP, CMP_R_UNSUPPORTED_ALGORITHM}, + #else + {"UNSUPPORTED_ALGORITHM", 58, 136}, + #endif + #ifdef CMP_R_UNSUPPORTED_KEY_TYPE + {"UNSUPPORTED_KEY_TYPE", ERR_LIB_CMP, CMP_R_UNSUPPORTED_KEY_TYPE}, + #else + {"UNSUPPORTED_KEY_TYPE", 58, 137}, + #endif + #ifdef CMP_R_UNSUPPORTED_PKIBODY + {"UNSUPPORTED_PKIBODY", ERR_LIB_CMP, CMP_R_UNSUPPORTED_PKIBODY}, + #else + {"UNSUPPORTED_PKIBODY", 58, 101}, + #endif + #ifdef CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC + {"UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC", ERR_LIB_CMP, CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC}, + #else + {"UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC", 58, 154}, + #endif + #ifdef CMP_R_VALUE_TOO_LARGE + {"VALUE_TOO_LARGE", ERR_LIB_CMP, CMP_R_VALUE_TOO_LARGE}, + #else + {"VALUE_TOO_LARGE", 58, 175}, + #endif + #ifdef CMP_R_VALUE_TOO_SMALL + {"VALUE_TOO_SMALL", ERR_LIB_CMP, CMP_R_VALUE_TOO_SMALL}, + #else + {"VALUE_TOO_SMALL", 58, 177}, + #endif + #ifdef CMP_R_WRONG_ALGORITHM_OID + {"WRONG_ALGORITHM_OID", ERR_LIB_CMP, CMP_R_WRONG_ALGORITHM_OID}, + #else + {"WRONG_ALGORITHM_OID", 58, 138}, + #endif + #ifdef CMP_R_WRONG_CERTID + {"WRONG_CERTID", ERR_LIB_CMP, CMP_R_WRONG_CERTID}, + #else + {"WRONG_CERTID", 58, 189}, + #endif + #ifdef CMP_R_WRONG_CERTID_IN_RP + {"WRONG_CERTID_IN_RP", ERR_LIB_CMP, CMP_R_WRONG_CERTID_IN_RP}, + #else + {"WRONG_CERTID_IN_RP", 58, 187}, + #endif + #ifdef CMP_R_WRONG_PBM_VALUE + {"WRONG_PBM_VALUE", ERR_LIB_CMP, CMP_R_WRONG_PBM_VALUE}, + #else + {"WRONG_PBM_VALUE", 58, 155}, + #endif + #ifdef CMP_R_WRONG_RP_COMPONENT_COUNT + {"WRONG_RP_COMPONENT_COUNT", ERR_LIB_CMP, CMP_R_WRONG_RP_COMPONENT_COUNT}, + #else + {"WRONG_RP_COMPONENT_COUNT", 58, 188}, + #endif + #ifdef CMP_R_WRONG_SERIAL_IN_RP + {"WRONG_SERIAL_IN_RP", ERR_LIB_CMP, CMP_R_WRONG_SERIAL_IN_RP}, + #else + {"WRONG_SERIAL_IN_RP", 58, 173}, + #endif + #ifdef CMS_R_ADD_SIGNER_ERROR + {"ADD_SIGNER_ERROR", ERR_LIB_CMS, CMS_R_ADD_SIGNER_ERROR}, + #else + {"ADD_SIGNER_ERROR", 46, 99}, + #endif + #ifdef CMS_R_ATTRIBUTE_ERROR + {"ATTRIBUTE_ERROR", ERR_LIB_CMS, CMS_R_ATTRIBUTE_ERROR}, + #else + {"ATTRIBUTE_ERROR", 46, 161}, + #endif + #ifdef CMS_R_CERTIFICATE_ALREADY_PRESENT + {"CERTIFICATE_ALREADY_PRESENT", ERR_LIB_CMS, CMS_R_CERTIFICATE_ALREADY_PRESENT}, + #else + {"CERTIFICATE_ALREADY_PRESENT", 46, 175}, + #endif + #ifdef CMS_R_CERTIFICATE_HAS_NO_KEYID + {"CERTIFICATE_HAS_NO_KEYID", ERR_LIB_CMS, CMS_R_CERTIFICATE_HAS_NO_KEYID}, + #else + {"CERTIFICATE_HAS_NO_KEYID", 46, 160}, + #endif + #ifdef CMS_R_CERTIFICATE_VERIFY_ERROR + {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_CMS, CMS_R_CERTIFICATE_VERIFY_ERROR}, + #else + {"CERTIFICATE_VERIFY_ERROR", 46, 100}, + #endif + #ifdef CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA + {"CIPHER_AEAD_IN_ENVELOPED_DATA", ERR_LIB_CMS, CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA}, + #else + {"CIPHER_AEAD_IN_ENVELOPED_DATA", 46, 200}, + #endif + #ifdef CMS_R_CIPHER_AEAD_SET_TAG_ERROR + {"CIPHER_AEAD_SET_TAG_ERROR", ERR_LIB_CMS, CMS_R_CIPHER_AEAD_SET_TAG_ERROR}, + #else + {"CIPHER_AEAD_SET_TAG_ERROR", 46, 184}, + #endif + #ifdef CMS_R_CIPHER_GET_TAG + {"CIPHER_GET_TAG", ERR_LIB_CMS, CMS_R_CIPHER_GET_TAG}, + #else + {"CIPHER_GET_TAG", 46, 185}, + #endif + #ifdef CMS_R_CIPHER_INITIALISATION_ERROR + {"CIPHER_INITIALISATION_ERROR", ERR_LIB_CMS, CMS_R_CIPHER_INITIALISATION_ERROR}, + #else + {"CIPHER_INITIALISATION_ERROR", 46, 101}, + #endif + #ifdef CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR + {"CIPHER_PARAMETER_INITIALISATION_ERROR", ERR_LIB_CMS, CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR}, + #else + {"CIPHER_PARAMETER_INITIALISATION_ERROR", 46, 102}, + #endif + #ifdef CMS_R_CMS_DATAFINAL_ERROR + {"CMS_DATAFINAL_ERROR", ERR_LIB_CMS, CMS_R_CMS_DATAFINAL_ERROR}, + #else + {"CMS_DATAFINAL_ERROR", 46, 103}, + #endif + #ifdef CMS_R_CMS_LIB + {"CMS_LIB", ERR_LIB_CMS, CMS_R_CMS_LIB}, + #else + {"CMS_LIB", 46, 104}, + #endif + #ifdef CMS_R_CONTENTIDENTIFIER_MISMATCH + {"CONTENTIDENTIFIER_MISMATCH", ERR_LIB_CMS, CMS_R_CONTENTIDENTIFIER_MISMATCH}, + #else + {"CONTENTIDENTIFIER_MISMATCH", 46, 170}, + #endif + #ifdef CMS_R_CONTENT_NOT_FOUND + {"CONTENT_NOT_FOUND", ERR_LIB_CMS, CMS_R_CONTENT_NOT_FOUND}, + #else + {"CONTENT_NOT_FOUND", 46, 105}, + #endif + #ifdef CMS_R_CONTENT_TYPE_MISMATCH + {"CONTENT_TYPE_MISMATCH", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_MISMATCH}, + #else + {"CONTENT_TYPE_MISMATCH", 46, 171}, + #endif + #ifdef CMS_R_CONTENT_TYPE_NOT_COMPRESSED_DATA + {"CONTENT_TYPE_NOT_COMPRESSED_DATA", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_NOT_COMPRESSED_DATA}, + #else + {"CONTENT_TYPE_NOT_COMPRESSED_DATA", 46, 106}, + #endif + #ifdef CMS_R_CONTENT_TYPE_NOT_ENVELOPED_DATA + {"CONTENT_TYPE_NOT_ENVELOPED_DATA", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_NOT_ENVELOPED_DATA}, + #else + {"CONTENT_TYPE_NOT_ENVELOPED_DATA", 46, 107}, + #endif + #ifdef CMS_R_CONTENT_TYPE_NOT_SIGNED_DATA + {"CONTENT_TYPE_NOT_SIGNED_DATA", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_NOT_SIGNED_DATA}, + #else + {"CONTENT_TYPE_NOT_SIGNED_DATA", 46, 108}, + #endif + #ifdef CMS_R_CONTENT_VERIFY_ERROR + {"CONTENT_VERIFY_ERROR", ERR_LIB_CMS, CMS_R_CONTENT_VERIFY_ERROR}, + #else + {"CONTENT_VERIFY_ERROR", 46, 109}, + #endif + #ifdef CMS_R_CTRL_ERROR + {"CTRL_ERROR", ERR_LIB_CMS, CMS_R_CTRL_ERROR}, + #else + {"CTRL_ERROR", 46, 110}, + #endif + #ifdef CMS_R_CTRL_FAILURE + {"CTRL_FAILURE", ERR_LIB_CMS, CMS_R_CTRL_FAILURE}, + #else + {"CTRL_FAILURE", 46, 111}, + #endif + #ifdef CMS_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_CMS, CMS_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 46, 187}, + #endif + #ifdef CMS_R_DECRYPT_ERROR + {"DECRYPT_ERROR", ERR_LIB_CMS, CMS_R_DECRYPT_ERROR}, + #else + {"DECRYPT_ERROR", 46, 112}, + #endif + #ifdef CMS_R_ERROR_GETTING_PUBLIC_KEY + {"ERROR_GETTING_PUBLIC_KEY", ERR_LIB_CMS, CMS_R_ERROR_GETTING_PUBLIC_KEY}, + #else + {"ERROR_GETTING_PUBLIC_KEY", 46, 113}, + #endif + #ifdef CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE + {"ERROR_READING_MESSAGEDIGEST_ATTRIBUTE", ERR_LIB_CMS, CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE}, + #else + {"ERROR_READING_MESSAGEDIGEST_ATTRIBUTE", 46, 114}, + #endif + #ifdef CMS_R_ERROR_SETTING_KEY + {"ERROR_SETTING_KEY", ERR_LIB_CMS, CMS_R_ERROR_SETTING_KEY}, + #else + {"ERROR_SETTING_KEY", 46, 115}, + #endif + #ifdef CMS_R_ERROR_SETTING_RECIPIENTINFO + {"ERROR_SETTING_RECIPIENTINFO", ERR_LIB_CMS, CMS_R_ERROR_SETTING_RECIPIENTINFO}, + #else + {"ERROR_SETTING_RECIPIENTINFO", 46, 116}, + #endif + #ifdef CMS_R_ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT + {"ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT", ERR_LIB_CMS, CMS_R_ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT}, + #else + {"ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT", 46, 196}, + #endif + #ifdef CMS_R_ESS_SIGNING_CERTID_MISMATCH_ERROR + {"ESS_SIGNING_CERTID_MISMATCH_ERROR", ERR_LIB_CMS, CMS_R_ESS_SIGNING_CERTID_MISMATCH_ERROR}, + #else + {"ESS_SIGNING_CERTID_MISMATCH_ERROR", 46, 183}, + #endif + #ifdef CMS_R_INVALID_ENCRYPTED_KEY_LENGTH + {"INVALID_ENCRYPTED_KEY_LENGTH", ERR_LIB_CMS, CMS_R_INVALID_ENCRYPTED_KEY_LENGTH}, + #else + {"INVALID_ENCRYPTED_KEY_LENGTH", 46, 117}, + #endif + #ifdef CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER + {"INVALID_KEY_ENCRYPTION_PARAMETER", ERR_LIB_CMS, CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER}, + #else + {"INVALID_KEY_ENCRYPTION_PARAMETER", 46, 176}, + #endif + #ifdef CMS_R_INVALID_KEY_LENGTH + {"INVALID_KEY_LENGTH", ERR_LIB_CMS, CMS_R_INVALID_KEY_LENGTH}, + #else + {"INVALID_KEY_LENGTH", 46, 118}, + #endif + #ifdef CMS_R_INVALID_LABEL + {"INVALID_LABEL", ERR_LIB_CMS, CMS_R_INVALID_LABEL}, + #else + {"INVALID_LABEL", 46, 190}, + #endif + #ifdef CMS_R_INVALID_OAEP_PARAMETERS + {"INVALID_OAEP_PARAMETERS", ERR_LIB_CMS, CMS_R_INVALID_OAEP_PARAMETERS}, + #else + {"INVALID_OAEP_PARAMETERS", 46, 191}, + #endif + #ifdef CMS_R_KDF_PARAMETER_ERROR + {"KDF_PARAMETER_ERROR", ERR_LIB_CMS, CMS_R_KDF_PARAMETER_ERROR}, + #else + {"KDF_PARAMETER_ERROR", 46, 186}, + #endif + #ifdef CMS_R_MD_BIO_INIT_ERROR + {"MD_BIO_INIT_ERROR", ERR_LIB_CMS, CMS_R_MD_BIO_INIT_ERROR}, + #else + {"MD_BIO_INIT_ERROR", 46, 119}, + #endif + #ifdef CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH + {"MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH", ERR_LIB_CMS, CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH}, + #else + {"MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH", 46, 120}, + #endif + #ifdef CMS_R_MESSAGEDIGEST_WRONG_LENGTH + {"MESSAGEDIGEST_WRONG_LENGTH", ERR_LIB_CMS, CMS_R_MESSAGEDIGEST_WRONG_LENGTH}, + #else + {"MESSAGEDIGEST_WRONG_LENGTH", 46, 121}, + #endif + #ifdef CMS_R_MSGSIGDIGEST_ERROR + {"MSGSIGDIGEST_ERROR", ERR_LIB_CMS, CMS_R_MSGSIGDIGEST_ERROR}, + #else + {"MSGSIGDIGEST_ERROR", 46, 172}, + #endif + #ifdef CMS_R_MSGSIGDIGEST_VERIFICATION_FAILURE + {"MSGSIGDIGEST_VERIFICATION_FAILURE", ERR_LIB_CMS, CMS_R_MSGSIGDIGEST_VERIFICATION_FAILURE}, + #else + {"MSGSIGDIGEST_VERIFICATION_FAILURE", 46, 162}, + #endif + #ifdef CMS_R_MSGSIGDIGEST_WRONG_LENGTH + {"MSGSIGDIGEST_WRONG_LENGTH", ERR_LIB_CMS, CMS_R_MSGSIGDIGEST_WRONG_LENGTH}, + #else + {"MSGSIGDIGEST_WRONG_LENGTH", 46, 163}, + #endif + #ifdef CMS_R_NEED_ONE_SIGNER + {"NEED_ONE_SIGNER", ERR_LIB_CMS, CMS_R_NEED_ONE_SIGNER}, + #else + {"NEED_ONE_SIGNER", 46, 164}, + #endif + #ifdef CMS_R_NOT_A_SIGNED_RECEIPT + {"NOT_A_SIGNED_RECEIPT", ERR_LIB_CMS, CMS_R_NOT_A_SIGNED_RECEIPT}, + #else + {"NOT_A_SIGNED_RECEIPT", 46, 165}, + #endif + #ifdef CMS_R_NOT_ENCRYPTED_DATA + {"NOT_ENCRYPTED_DATA", ERR_LIB_CMS, CMS_R_NOT_ENCRYPTED_DATA}, + #else + {"NOT_ENCRYPTED_DATA", 46, 122}, + #endif + #ifdef CMS_R_NOT_KEK + {"NOT_KEK", ERR_LIB_CMS, CMS_R_NOT_KEK}, + #else + {"NOT_KEK", 46, 123}, + #endif + #ifdef CMS_R_NOT_KEM + {"NOT_KEM", ERR_LIB_CMS, CMS_R_NOT_KEM}, + #else + {"NOT_KEM", 46, 197}, + #endif + #ifdef CMS_R_NOT_KEY_AGREEMENT + {"NOT_KEY_AGREEMENT", ERR_LIB_CMS, CMS_R_NOT_KEY_AGREEMENT}, + #else + {"NOT_KEY_AGREEMENT", 46, 181}, + #endif + #ifdef CMS_R_NOT_KEY_TRANSPORT + {"NOT_KEY_TRANSPORT", ERR_LIB_CMS, CMS_R_NOT_KEY_TRANSPORT}, + #else + {"NOT_KEY_TRANSPORT", 46, 124}, + #endif + #ifdef CMS_R_NOT_PWRI + {"NOT_PWRI", ERR_LIB_CMS, CMS_R_NOT_PWRI}, + #else + {"NOT_PWRI", 46, 177}, + #endif + #ifdef CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE + {"NOT_SUPPORTED_FOR_THIS_KEY_TYPE", ERR_LIB_CMS, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE}, + #else + {"NOT_SUPPORTED_FOR_THIS_KEY_TYPE", 46, 125}, + #endif + #ifdef CMS_R_NO_CIPHER + {"NO_CIPHER", ERR_LIB_CMS, CMS_R_NO_CIPHER}, + #else + {"NO_CIPHER", 46, 126}, + #endif + #ifdef CMS_R_NO_CONTENT + {"NO_CONTENT", ERR_LIB_CMS, CMS_R_NO_CONTENT}, + #else + {"NO_CONTENT", 46, 127}, + #endif + #ifdef CMS_R_NO_CONTENT_TYPE + {"NO_CONTENT_TYPE", ERR_LIB_CMS, CMS_R_NO_CONTENT_TYPE}, + #else + {"NO_CONTENT_TYPE", 46, 173}, + #endif + #ifdef CMS_R_NO_DEFAULT_DIGEST + {"NO_DEFAULT_DIGEST", ERR_LIB_CMS, CMS_R_NO_DEFAULT_DIGEST}, + #else + {"NO_DEFAULT_DIGEST", 46, 128}, + #endif + #ifdef CMS_R_NO_DIGEST_SET + {"NO_DIGEST_SET", ERR_LIB_CMS, CMS_R_NO_DIGEST_SET}, + #else + {"NO_DIGEST_SET", 46, 129}, + #endif + #ifdef CMS_R_NO_KEY + {"NO_KEY", ERR_LIB_CMS, CMS_R_NO_KEY}, + #else + {"NO_KEY", 46, 130}, + #endif + #ifdef CMS_R_NO_KEY_OR_CERT + {"NO_KEY_OR_CERT", ERR_LIB_CMS, CMS_R_NO_KEY_OR_CERT}, + #else + {"NO_KEY_OR_CERT", 46, 174}, + #endif + #ifdef CMS_R_NO_MATCHING_DIGEST + {"NO_MATCHING_DIGEST", ERR_LIB_CMS, CMS_R_NO_MATCHING_DIGEST}, + #else + {"NO_MATCHING_DIGEST", 46, 131}, + #endif + #ifdef CMS_R_NO_MATCHING_RECIPIENT + {"NO_MATCHING_RECIPIENT", ERR_LIB_CMS, CMS_R_NO_MATCHING_RECIPIENT}, + #else + {"NO_MATCHING_RECIPIENT", 46, 132}, + #endif + #ifdef CMS_R_NO_MATCHING_SIGNATURE + {"NO_MATCHING_SIGNATURE", ERR_LIB_CMS, CMS_R_NO_MATCHING_SIGNATURE}, + #else + {"NO_MATCHING_SIGNATURE", 46, 166}, + #endif + #ifdef CMS_R_NO_MSGSIGDIGEST + {"NO_MSGSIGDIGEST", ERR_LIB_CMS, CMS_R_NO_MSGSIGDIGEST}, + #else + {"NO_MSGSIGDIGEST", 46, 167}, + #endif + #ifdef CMS_R_NO_PASSWORD + {"NO_PASSWORD", ERR_LIB_CMS, CMS_R_NO_PASSWORD}, + #else + {"NO_PASSWORD", 46, 178}, + #endif + #ifdef CMS_R_NO_PRIVATE_KEY + {"NO_PRIVATE_KEY", ERR_LIB_CMS, CMS_R_NO_PRIVATE_KEY}, + #else + {"NO_PRIVATE_KEY", 46, 133}, + #endif + #ifdef CMS_R_NO_PUBLIC_KEY + {"NO_PUBLIC_KEY", ERR_LIB_CMS, CMS_R_NO_PUBLIC_KEY}, + #else + {"NO_PUBLIC_KEY", 46, 134}, + #endif + #ifdef CMS_R_NO_RECEIPT_REQUEST + {"NO_RECEIPT_REQUEST", ERR_LIB_CMS, CMS_R_NO_RECEIPT_REQUEST}, + #else + {"NO_RECEIPT_REQUEST", 46, 168}, + #endif + #ifdef CMS_R_NO_SIGNERS + {"NO_SIGNERS", ERR_LIB_CMS, CMS_R_NO_SIGNERS}, + #else + {"NO_SIGNERS", 46, 135}, + #endif + #ifdef CMS_R_OPERATION_UNSUPPORTED + {"OPERATION_UNSUPPORTED", ERR_LIB_CMS, CMS_R_OPERATION_UNSUPPORTED}, + #else + {"OPERATION_UNSUPPORTED", 46, 182}, + #endif + #ifdef CMS_R_PEER_KEY_ERROR + {"PEER_KEY_ERROR", ERR_LIB_CMS, CMS_R_PEER_KEY_ERROR}, + #else + {"PEER_KEY_ERROR", 46, 188}, + #endif + #ifdef CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_CMS, CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, + #else + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 46, 136}, + #endif + #ifdef CMS_R_RECEIPT_DECODE_ERROR + {"RECEIPT_DECODE_ERROR", ERR_LIB_CMS, CMS_R_RECEIPT_DECODE_ERROR}, + #else + {"RECEIPT_DECODE_ERROR", 46, 169}, + #endif + #ifdef CMS_R_RECIPIENT_ERROR + {"RECIPIENT_ERROR", ERR_LIB_CMS, CMS_R_RECIPIENT_ERROR}, + #else + {"RECIPIENT_ERROR", 46, 137}, + #endif + #ifdef CMS_R_SHARED_INFO_ERROR + {"SHARED_INFO_ERROR", ERR_LIB_CMS, CMS_R_SHARED_INFO_ERROR}, + #else + {"SHARED_INFO_ERROR", 46, 189}, + #endif + #ifdef CMS_R_SIGNER_CERTIFICATE_NOT_FOUND + {"SIGNER_CERTIFICATE_NOT_FOUND", ERR_LIB_CMS, CMS_R_SIGNER_CERTIFICATE_NOT_FOUND}, + #else + {"SIGNER_CERTIFICATE_NOT_FOUND", 46, 138}, + #endif + #ifdef CMS_R_SIGNFINAL_ERROR + {"SIGNFINAL_ERROR", ERR_LIB_CMS, CMS_R_SIGNFINAL_ERROR}, + #else + {"SIGNFINAL_ERROR", 46, 139}, + #endif + #ifdef CMS_R_SMIME_TEXT_ERROR + {"SMIME_TEXT_ERROR", ERR_LIB_CMS, CMS_R_SMIME_TEXT_ERROR}, + #else + {"SMIME_TEXT_ERROR", 46, 140}, + #endif + #ifdef CMS_R_STORE_INIT_ERROR + {"STORE_INIT_ERROR", ERR_LIB_CMS, CMS_R_STORE_INIT_ERROR}, + #else + {"STORE_INIT_ERROR", 46, 141}, + #endif + #ifdef CMS_R_TYPE_NOT_COMPRESSED_DATA + {"TYPE_NOT_COMPRESSED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_COMPRESSED_DATA}, + #else + {"TYPE_NOT_COMPRESSED_DATA", 46, 142}, + #endif + #ifdef CMS_R_TYPE_NOT_DATA + {"TYPE_NOT_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_DATA}, + #else + {"TYPE_NOT_DATA", 46, 143}, + #endif + #ifdef CMS_R_TYPE_NOT_DIGESTED_DATA + {"TYPE_NOT_DIGESTED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_DIGESTED_DATA}, + #else + {"TYPE_NOT_DIGESTED_DATA", 46, 144}, + #endif + #ifdef CMS_R_TYPE_NOT_ENCRYPTED_DATA + {"TYPE_NOT_ENCRYPTED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_ENCRYPTED_DATA}, + #else + {"TYPE_NOT_ENCRYPTED_DATA", 46, 145}, + #endif + #ifdef CMS_R_TYPE_NOT_ENVELOPED_DATA + {"TYPE_NOT_ENVELOPED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_ENVELOPED_DATA}, + #else + {"TYPE_NOT_ENVELOPED_DATA", 46, 146}, + #endif + #ifdef CMS_R_UNABLE_TO_FINALIZE_CONTEXT + {"UNABLE_TO_FINALIZE_CONTEXT", ERR_LIB_CMS, CMS_R_UNABLE_TO_FINALIZE_CONTEXT}, + #else + {"UNABLE_TO_FINALIZE_CONTEXT", 46, 147}, + #endif + #ifdef CMS_R_UNKNOWN_CIPHER + {"UNKNOWN_CIPHER", ERR_LIB_CMS, CMS_R_UNKNOWN_CIPHER}, + #else + {"UNKNOWN_CIPHER", 46, 148}, + #endif + #ifdef CMS_R_UNKNOWN_DIGEST_ALGORITHM + {"UNKNOWN_DIGEST_ALGORITHM", ERR_LIB_CMS, CMS_R_UNKNOWN_DIGEST_ALGORITHM}, + #else + {"UNKNOWN_DIGEST_ALGORITHM", 46, 149}, + #endif + #ifdef CMS_R_UNKNOWN_ID + {"UNKNOWN_ID", ERR_LIB_CMS, CMS_R_UNKNOWN_ID}, + #else + {"UNKNOWN_ID", 46, 150}, + #endif + #ifdef CMS_R_UNKNOWN_KDF_ALGORITHM + {"UNKNOWN_KDF_ALGORITHM", ERR_LIB_CMS, CMS_R_UNKNOWN_KDF_ALGORITHM}, + #else + {"UNKNOWN_KDF_ALGORITHM", 46, 198}, + #endif + #ifdef CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM + {"UNSUPPORTED_COMPRESSION_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM}, + #else + {"UNSUPPORTED_COMPRESSION_ALGORITHM", 46, 151}, + #endif + #ifdef CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM + {"UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM}, + #else + {"UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM", 46, 194}, + #endif + #ifdef CMS_R_UNSUPPORTED_CONTENT_TYPE + {"UNSUPPORTED_CONTENT_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_CONTENT_TYPE}, + #else + {"UNSUPPORTED_CONTENT_TYPE", 46, 152}, + #endif + #ifdef CMS_R_UNSUPPORTED_ENCRYPTION_TYPE + {"UNSUPPORTED_ENCRYPTION_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_ENCRYPTION_TYPE}, + #else + {"UNSUPPORTED_ENCRYPTION_TYPE", 46, 192}, + #endif + #ifdef CMS_R_UNSUPPORTED_KDF_ALGORITHM + {"UNSUPPORTED_KDF_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_KDF_ALGORITHM}, + #else + {"UNSUPPORTED_KDF_ALGORITHM", 46, 199}, + #endif + #ifdef CMS_R_UNSUPPORTED_KEK_ALGORITHM + {"UNSUPPORTED_KEK_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_KEK_ALGORITHM}, + #else + {"UNSUPPORTED_KEK_ALGORITHM", 46, 153}, + #endif + #ifdef CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM + {"UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM}, + #else + {"UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM", 46, 179}, + #endif + #ifdef CMS_R_UNSUPPORTED_LABEL_SOURCE + {"UNSUPPORTED_LABEL_SOURCE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_LABEL_SOURCE}, + #else + {"UNSUPPORTED_LABEL_SOURCE", 46, 193}, + #endif + #ifdef CMS_R_UNSUPPORTED_RECIPIENTINFO_TYPE + {"UNSUPPORTED_RECIPIENTINFO_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_RECIPIENTINFO_TYPE}, + #else + {"UNSUPPORTED_RECIPIENTINFO_TYPE", 46, 155}, + #endif + #ifdef CMS_R_UNSUPPORTED_RECIPIENT_TYPE + {"UNSUPPORTED_RECIPIENT_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_RECIPIENT_TYPE}, + #else + {"UNSUPPORTED_RECIPIENT_TYPE", 46, 154}, + #endif + #ifdef CMS_R_UNSUPPORTED_SIGNATURE_ALGORITHM + {"UNSUPPORTED_SIGNATURE_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_SIGNATURE_ALGORITHM}, + #else + {"UNSUPPORTED_SIGNATURE_ALGORITHM", 46, 195}, + #endif + #ifdef CMS_R_UNSUPPORTED_TYPE + {"UNSUPPORTED_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_TYPE}, + #else + {"UNSUPPORTED_TYPE", 46, 156}, + #endif + #ifdef CMS_R_UNWRAP_ERROR + {"UNWRAP_ERROR", ERR_LIB_CMS, CMS_R_UNWRAP_ERROR}, + #else + {"UNWRAP_ERROR", 46, 157}, + #endif + #ifdef CMS_R_UNWRAP_FAILURE + {"UNWRAP_FAILURE", ERR_LIB_CMS, CMS_R_UNWRAP_FAILURE}, + #else + {"UNWRAP_FAILURE", 46, 180}, + #endif + #ifdef CMS_R_VERIFICATION_FAILURE + {"VERIFICATION_FAILURE", ERR_LIB_CMS, CMS_R_VERIFICATION_FAILURE}, + #else + {"VERIFICATION_FAILURE", 46, 158}, + #endif + #ifdef CMS_R_WRAP_ERROR + {"WRAP_ERROR", ERR_LIB_CMS, CMS_R_WRAP_ERROR}, + #else + {"WRAP_ERROR", 46, 159}, + #endif + #ifdef COMP_R_BROTLI_DECODE_ERROR + {"BROTLI_DECODE_ERROR", ERR_LIB_COMP, COMP_R_BROTLI_DECODE_ERROR}, + #else + {"BROTLI_DECODE_ERROR", 41, 102}, + #endif + #ifdef COMP_R_BROTLI_ENCODE_ERROR + {"BROTLI_ENCODE_ERROR", ERR_LIB_COMP, COMP_R_BROTLI_ENCODE_ERROR}, + #else + {"BROTLI_ENCODE_ERROR", 41, 103}, + #endif + #ifdef COMP_R_BROTLI_NOT_SUPPORTED + {"BROTLI_NOT_SUPPORTED", ERR_LIB_COMP, COMP_R_BROTLI_NOT_SUPPORTED}, + #else + {"BROTLI_NOT_SUPPORTED", 41, 104}, + #endif + #ifdef COMP_R_ZLIB_DEFLATE_ERROR + {"ZLIB_DEFLATE_ERROR", ERR_LIB_COMP, COMP_R_ZLIB_DEFLATE_ERROR}, + #else + {"ZLIB_DEFLATE_ERROR", 41, 99}, + #endif + #ifdef COMP_R_ZLIB_INFLATE_ERROR + {"ZLIB_INFLATE_ERROR", ERR_LIB_COMP, COMP_R_ZLIB_INFLATE_ERROR}, + #else + {"ZLIB_INFLATE_ERROR", 41, 100}, + #endif + #ifdef COMP_R_ZLIB_NOT_SUPPORTED + {"ZLIB_NOT_SUPPORTED", ERR_LIB_COMP, COMP_R_ZLIB_NOT_SUPPORTED}, + #else + {"ZLIB_NOT_SUPPORTED", 41, 101}, + #endif + #ifdef COMP_R_ZSTD_COMPRESS_ERROR + {"ZSTD_COMPRESS_ERROR", ERR_LIB_COMP, COMP_R_ZSTD_COMPRESS_ERROR}, + #else + {"ZSTD_COMPRESS_ERROR", 41, 105}, + #endif + #ifdef COMP_R_ZSTD_DECODE_ERROR + {"ZSTD_DECODE_ERROR", ERR_LIB_COMP, COMP_R_ZSTD_DECODE_ERROR}, + #else + {"ZSTD_DECODE_ERROR", 41, 106}, + #endif + #ifdef COMP_R_ZSTD_DECOMPRESS_ERROR + {"ZSTD_DECOMPRESS_ERROR", ERR_LIB_COMP, COMP_R_ZSTD_DECOMPRESS_ERROR}, + #else + {"ZSTD_DECOMPRESS_ERROR", 41, 107}, + #endif + #ifdef COMP_R_ZSTD_NOT_SUPPORTED + {"ZSTD_NOT_SUPPORTED", ERR_LIB_COMP, COMP_R_ZSTD_NOT_SUPPORTED}, + #else + {"ZSTD_NOT_SUPPORTED", 41, 108}, + #endif + #ifdef CONF_R_ERROR_LOADING_DSO + {"ERROR_LOADING_DSO", ERR_LIB_CONF, CONF_R_ERROR_LOADING_DSO}, + #else + {"ERROR_LOADING_DSO", 14, 110}, + #endif + #ifdef CONF_R_INVALID_PRAGMA + {"INVALID_PRAGMA", ERR_LIB_CONF, CONF_R_INVALID_PRAGMA}, + #else + {"INVALID_PRAGMA", 14, 122}, + #endif + #ifdef CONF_R_LIST_CANNOT_BE_NULL + {"LIST_CANNOT_BE_NULL", ERR_LIB_CONF, CONF_R_LIST_CANNOT_BE_NULL}, + #else + {"LIST_CANNOT_BE_NULL", 14, 115}, + #endif + #ifdef CONF_R_MANDATORY_BRACES_IN_VARIABLE_EXPANSION + {"MANDATORY_BRACES_IN_VARIABLE_EXPANSION", ERR_LIB_CONF, CONF_R_MANDATORY_BRACES_IN_VARIABLE_EXPANSION}, + #else + {"MANDATORY_BRACES_IN_VARIABLE_EXPANSION", 14, 123}, + #endif + #ifdef CONF_R_MISSING_CLOSE_SQUARE_BRACKET + {"MISSING_CLOSE_SQUARE_BRACKET", ERR_LIB_CONF, CONF_R_MISSING_CLOSE_SQUARE_BRACKET}, + #else + {"MISSING_CLOSE_SQUARE_BRACKET", 14, 100}, + #endif + #ifdef CONF_R_MISSING_EQUAL_SIGN + {"MISSING_EQUAL_SIGN", ERR_LIB_CONF, CONF_R_MISSING_EQUAL_SIGN}, + #else + {"MISSING_EQUAL_SIGN", 14, 101}, + #endif + #ifdef CONF_R_MISSING_INIT_FUNCTION + {"MISSING_INIT_FUNCTION", ERR_LIB_CONF, CONF_R_MISSING_INIT_FUNCTION}, + #else + {"MISSING_INIT_FUNCTION", 14, 112}, + #endif + #ifdef CONF_R_MODULE_INITIALIZATION_ERROR + {"MODULE_INITIALIZATION_ERROR", ERR_LIB_CONF, CONF_R_MODULE_INITIALIZATION_ERROR}, + #else + {"MODULE_INITIALIZATION_ERROR", 14, 109}, + #endif + #ifdef CONF_R_NO_CLOSE_BRACE + {"NO_CLOSE_BRACE", ERR_LIB_CONF, CONF_R_NO_CLOSE_BRACE}, + #else + {"NO_CLOSE_BRACE", 14, 102}, + #endif + #ifdef CONF_R_NO_CONF + {"NO_CONF", ERR_LIB_CONF, CONF_R_NO_CONF}, + #else + {"NO_CONF", 14, 105}, + #endif + #ifdef CONF_R_NO_CONF_OR_ENVIRONMENT_VARIABLE + {"NO_CONF_OR_ENVIRONMENT_VARIABLE", ERR_LIB_CONF, CONF_R_NO_CONF_OR_ENVIRONMENT_VARIABLE}, + #else + {"NO_CONF_OR_ENVIRONMENT_VARIABLE", 14, 106}, + #endif + #ifdef CONF_R_NO_SECTION + {"NO_SECTION", ERR_LIB_CONF, CONF_R_NO_SECTION}, + #else + {"NO_SECTION", 14, 107}, + #endif + #ifdef CONF_R_NO_SUCH_FILE + {"NO_SUCH_FILE", ERR_LIB_CONF, CONF_R_NO_SUCH_FILE}, + #else + {"NO_SUCH_FILE", 14, 114}, + #endif + #ifdef CONF_R_NO_VALUE + {"NO_VALUE", ERR_LIB_CONF, CONF_R_NO_VALUE}, + #else + {"NO_VALUE", 14, 108}, + #endif + #ifdef CONF_R_NUMBER_TOO_LARGE + {"NUMBER_TOO_LARGE", ERR_LIB_CONF, CONF_R_NUMBER_TOO_LARGE}, + #else + {"NUMBER_TOO_LARGE", 14, 121}, + #endif + #ifdef CONF_R_OPENSSL_CONF_REFERENCES_MISSING_SECTION + {"OPENSSL_CONF_REFERENCES_MISSING_SECTION", ERR_LIB_CONF, CONF_R_OPENSSL_CONF_REFERENCES_MISSING_SECTION}, + #else + {"OPENSSL_CONF_REFERENCES_MISSING_SECTION", 14, 124}, + #endif + #ifdef CONF_R_RECURSIVE_DIRECTORY_INCLUDE + {"RECURSIVE_DIRECTORY_INCLUDE", ERR_LIB_CONF, CONF_R_RECURSIVE_DIRECTORY_INCLUDE}, + #else + {"RECURSIVE_DIRECTORY_INCLUDE", 14, 111}, + #endif + #ifdef CONF_R_RECURSIVE_SECTION_REFERENCE + {"RECURSIVE_SECTION_REFERENCE", ERR_LIB_CONF, CONF_R_RECURSIVE_SECTION_REFERENCE}, + #else + {"RECURSIVE_SECTION_REFERENCE", 14, 126}, + #endif + #ifdef CONF_R_RELATIVE_PATH + {"RELATIVE_PATH", ERR_LIB_CONF, CONF_R_RELATIVE_PATH}, + #else + {"RELATIVE_PATH", 14, 125}, + #endif + #ifdef CONF_R_SSL_COMMAND_SECTION_EMPTY + {"SSL_COMMAND_SECTION_EMPTY", ERR_LIB_CONF, CONF_R_SSL_COMMAND_SECTION_EMPTY}, + #else + {"SSL_COMMAND_SECTION_EMPTY", 14, 117}, + #endif + #ifdef CONF_R_SSL_COMMAND_SECTION_NOT_FOUND + {"SSL_COMMAND_SECTION_NOT_FOUND", ERR_LIB_CONF, CONF_R_SSL_COMMAND_SECTION_NOT_FOUND}, + #else + {"SSL_COMMAND_SECTION_NOT_FOUND", 14, 118}, + #endif + #ifdef CONF_R_SSL_SECTION_EMPTY + {"SSL_SECTION_EMPTY", ERR_LIB_CONF, CONF_R_SSL_SECTION_EMPTY}, + #else + {"SSL_SECTION_EMPTY", 14, 119}, + #endif + #ifdef CONF_R_SSL_SECTION_NOT_FOUND + {"SSL_SECTION_NOT_FOUND", ERR_LIB_CONF, CONF_R_SSL_SECTION_NOT_FOUND}, + #else + {"SSL_SECTION_NOT_FOUND", 14, 120}, + #endif + #ifdef CONF_R_UNABLE_TO_CREATE_NEW_SECTION + {"UNABLE_TO_CREATE_NEW_SECTION", ERR_LIB_CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION}, + #else + {"UNABLE_TO_CREATE_NEW_SECTION", 14, 103}, + #endif + #ifdef CONF_R_UNKNOWN_MODULE_NAME + {"UNKNOWN_MODULE_NAME", ERR_LIB_CONF, CONF_R_UNKNOWN_MODULE_NAME}, + #else + {"UNKNOWN_MODULE_NAME", 14, 113}, + #endif + #ifdef CONF_R_VARIABLE_EXPANSION_TOO_LONG + {"VARIABLE_EXPANSION_TOO_LONG", ERR_LIB_CONF, CONF_R_VARIABLE_EXPANSION_TOO_LONG}, + #else + {"VARIABLE_EXPANSION_TOO_LONG", 14, 116}, + #endif + #ifdef CONF_R_VARIABLE_HAS_NO_VALUE + {"VARIABLE_HAS_NO_VALUE", ERR_LIB_CONF, CONF_R_VARIABLE_HAS_NO_VALUE}, + #else + {"VARIABLE_HAS_NO_VALUE", 14, 104}, + #endif + #ifdef CRMF_R_BAD_PBM_ITERATIONCOUNT + {"BAD_PBM_ITERATIONCOUNT", ERR_LIB_CRMF, CRMF_R_BAD_PBM_ITERATIONCOUNT}, + #else + {"BAD_PBM_ITERATIONCOUNT", 56, 100}, + #endif + #ifdef CRMF_R_CMS_NOT_SUPPORTED + {"CMS_NOT_SUPPORTED", ERR_LIB_CRMF, CRMF_R_CMS_NOT_SUPPORTED}, + #else + {"CMS_NOT_SUPPORTED", 56, 122}, + #endif + #ifdef CRMF_R_CRMFERROR + {"CRMFERROR", ERR_LIB_CRMF, CRMF_R_CRMFERROR}, + #else + {"CRMFERROR", 56, 102}, + #endif + #ifdef CRMF_R_ERROR + {"ERROR", ERR_LIB_CRMF, CRMF_R_ERROR}, + #else + {"ERROR", 56, 103}, + #endif + #ifdef CRMF_R_ERROR_DECODING_CERTIFICATE + {"ERROR_DECODING_CERTIFICATE", ERR_LIB_CRMF, CRMF_R_ERROR_DECODING_CERTIFICATE}, + #else + {"ERROR_DECODING_CERTIFICATE", 56, 104}, + #endif + #ifdef CRMF_R_ERROR_DECODING_ENCRYPTEDKEY + {"ERROR_DECODING_ENCRYPTEDKEY", ERR_LIB_CRMF, CRMF_R_ERROR_DECODING_ENCRYPTEDKEY}, + #else + {"ERROR_DECODING_ENCRYPTEDKEY", 56, 123}, + #endif + #ifdef CRMF_R_ERROR_DECRYPTING_CERTIFICATE + {"ERROR_DECRYPTING_CERTIFICATE", ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_CERTIFICATE}, + #else + {"ERROR_DECRYPTING_CERTIFICATE", 56, 105}, + #endif + #ifdef CRMF_R_ERROR_DECRYPTING_ENCRYPTEDKEY + {"ERROR_DECRYPTING_ENCRYPTEDKEY", ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_ENCRYPTEDKEY}, + #else + {"ERROR_DECRYPTING_ENCRYPTEDKEY", 56, 124}, + #endif + #ifdef CRMF_R_ERROR_DECRYPTING_ENCRYPTEDVALUE + {"ERROR_DECRYPTING_ENCRYPTEDVALUE", ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_ENCRYPTEDVALUE}, + #else + {"ERROR_DECRYPTING_ENCRYPTEDVALUE", 56, 125}, + #endif + #ifdef CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY + {"ERROR_DECRYPTING_SYMMETRIC_KEY", ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY}, + #else + {"ERROR_DECRYPTING_SYMMETRIC_KEY", 56, 106}, + #endif + #ifdef CRMF_R_ERROR_SETTING_PURPOSE + {"ERROR_SETTING_PURPOSE", ERR_LIB_CRMF, CRMF_R_ERROR_SETTING_PURPOSE}, + #else + {"ERROR_SETTING_PURPOSE", 56, 126}, + #endif + #ifdef CRMF_R_ERROR_VERIFYING_ENCRYPTEDKEY + {"ERROR_VERIFYING_ENCRYPTEDKEY", ERR_LIB_CRMF, CRMF_R_ERROR_VERIFYING_ENCRYPTEDKEY}, + #else + {"ERROR_VERIFYING_ENCRYPTEDKEY", 56, 127}, + #endif + #ifdef CRMF_R_FAILURE_OBTAINING_RANDOM + {"FAILURE_OBTAINING_RANDOM", ERR_LIB_CRMF, CRMF_R_FAILURE_OBTAINING_RANDOM}, + #else + {"FAILURE_OBTAINING_RANDOM", 56, 107}, + #endif + #ifdef CRMF_R_ITERATIONCOUNT_BELOW_100 + {"ITERATIONCOUNT_BELOW_100", ERR_LIB_CRMF, CRMF_R_ITERATIONCOUNT_BELOW_100}, + #else + {"ITERATIONCOUNT_BELOW_100", 56, 108}, + #endif + #ifdef CRMF_R_MALFORMED_IV + {"MALFORMED_IV", ERR_LIB_CRMF, CRMF_R_MALFORMED_IV}, + #else + {"MALFORMED_IV", 56, 101}, + #endif + #ifdef CRMF_R_NULL_ARGUMENT + {"NULL_ARGUMENT", ERR_LIB_CRMF, CRMF_R_NULL_ARGUMENT}, + #else + {"NULL_ARGUMENT", 56, 109}, + #endif + #ifdef CRMF_R_POPOSKINPUT_NOT_SUPPORTED + {"POPOSKINPUT_NOT_SUPPORTED", ERR_LIB_CRMF, CRMF_R_POPOSKINPUT_NOT_SUPPORTED}, + #else + {"POPOSKINPUT_NOT_SUPPORTED", 56, 113}, + #endif + #ifdef CRMF_R_POPO_INCONSISTENT_CENTRAL_KEYGEN + {"POPO_INCONSISTENT_CENTRAL_KEYGEN", ERR_LIB_CRMF, CRMF_R_POPO_INCONSISTENT_CENTRAL_KEYGEN}, + #else + {"POPO_INCONSISTENT_CENTRAL_KEYGEN", 56, 128}, + #endif + #ifdef CRMF_R_POPO_INCONSISTENT_PUBLIC_KEY + {"POPO_INCONSISTENT_PUBLIC_KEY", ERR_LIB_CRMF, CRMF_R_POPO_INCONSISTENT_PUBLIC_KEY}, + #else + {"POPO_INCONSISTENT_PUBLIC_KEY", 56, 117}, + #endif + #ifdef CRMF_R_POPO_MISSING + {"POPO_MISSING", ERR_LIB_CRMF, CRMF_R_POPO_MISSING}, + #else + {"POPO_MISSING", 56, 121}, + #endif + #ifdef CRMF_R_POPO_MISSING_PUBLIC_KEY + {"POPO_MISSING_PUBLIC_KEY", ERR_LIB_CRMF, CRMF_R_POPO_MISSING_PUBLIC_KEY}, + #else + {"POPO_MISSING_PUBLIC_KEY", 56, 118}, + #endif + #ifdef CRMF_R_POPO_MISSING_SUBJECT + {"POPO_MISSING_SUBJECT", ERR_LIB_CRMF, CRMF_R_POPO_MISSING_SUBJECT}, + #else + {"POPO_MISSING_SUBJECT", 56, 119}, + #endif + #ifdef CRMF_R_POPO_RAVERIFIED_NOT_ACCEPTED + {"POPO_RAVERIFIED_NOT_ACCEPTED", ERR_LIB_CRMF, CRMF_R_POPO_RAVERIFIED_NOT_ACCEPTED}, + #else + {"POPO_RAVERIFIED_NOT_ACCEPTED", 56, 120}, + #endif + #ifdef CRMF_R_SETTING_MAC_ALGOR_FAILURE + {"SETTING_MAC_ALGOR_FAILURE", ERR_LIB_CRMF, CRMF_R_SETTING_MAC_ALGOR_FAILURE}, + #else + {"SETTING_MAC_ALGOR_FAILURE", 56, 110}, + #endif + #ifdef CRMF_R_SETTING_OWF_ALGOR_FAILURE + {"SETTING_OWF_ALGOR_FAILURE", ERR_LIB_CRMF, CRMF_R_SETTING_OWF_ALGOR_FAILURE}, + #else + {"SETTING_OWF_ALGOR_FAILURE", 56, 111}, + #endif + #ifdef CRMF_R_UNSUPPORTED_ALGORITHM + {"UNSUPPORTED_ALGORITHM", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_ALGORITHM}, + #else + {"UNSUPPORTED_ALGORITHM", 56, 112}, + #endif + #ifdef CRMF_R_UNSUPPORTED_CIPHER + {"UNSUPPORTED_CIPHER", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_CIPHER}, + #else + {"UNSUPPORTED_CIPHER", 56, 114}, + #endif + #ifdef CRMF_R_UNSUPPORTED_METHOD_FOR_CREATING_POPO + {"UNSUPPORTED_METHOD_FOR_CREATING_POPO", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_METHOD_FOR_CREATING_POPO}, + #else + {"UNSUPPORTED_METHOD_FOR_CREATING_POPO", 56, 115}, + #endif + #ifdef CRMF_R_UNSUPPORTED_POPO_METHOD + {"UNSUPPORTED_POPO_METHOD", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_POPO_METHOD}, + #else + {"UNSUPPORTED_POPO_METHOD", 56, 116}, + #endif + #ifdef CRYPTO_R_BAD_ALGORITHM_NAME + {"BAD_ALGORITHM_NAME", ERR_LIB_CRYPTO, CRYPTO_R_BAD_ALGORITHM_NAME}, + #else + {"BAD_ALGORITHM_NAME", 15, 117}, + #endif + #ifdef CRYPTO_R_CONFLICTING_NAMES + {"CONFLICTING_NAMES", ERR_LIB_CRYPTO, CRYPTO_R_CONFLICTING_NAMES}, + #else + {"CONFLICTING_NAMES", 15, 118}, + #endif + #ifdef CRYPTO_R_HEX_STRING_TOO_SHORT + {"HEX_STRING_TOO_SHORT", ERR_LIB_CRYPTO, CRYPTO_R_HEX_STRING_TOO_SHORT}, + #else + {"HEX_STRING_TOO_SHORT", 15, 121}, + #endif + #ifdef CRYPTO_R_ILLEGAL_HEX_DIGIT + {"ILLEGAL_HEX_DIGIT", ERR_LIB_CRYPTO, CRYPTO_R_ILLEGAL_HEX_DIGIT}, + #else + {"ILLEGAL_HEX_DIGIT", 15, 102}, + #endif + #ifdef CRYPTO_R_INSUFFICIENT_DATA_SPACE + {"INSUFFICIENT_DATA_SPACE", ERR_LIB_CRYPTO, CRYPTO_R_INSUFFICIENT_DATA_SPACE}, + #else + {"INSUFFICIENT_DATA_SPACE", 15, 106}, + #endif + #ifdef CRYPTO_R_INSUFFICIENT_PARAM_SIZE + {"INSUFFICIENT_PARAM_SIZE", ERR_LIB_CRYPTO, CRYPTO_R_INSUFFICIENT_PARAM_SIZE}, + #else + {"INSUFFICIENT_PARAM_SIZE", 15, 107}, + #endif + #ifdef CRYPTO_R_INSUFFICIENT_SECURE_DATA_SPACE + {"INSUFFICIENT_SECURE_DATA_SPACE", ERR_LIB_CRYPTO, CRYPTO_R_INSUFFICIENT_SECURE_DATA_SPACE}, + #else + {"INSUFFICIENT_SECURE_DATA_SPACE", 15, 108}, + #endif + #ifdef CRYPTO_R_INTEGER_OVERFLOW + {"INTEGER_OVERFLOW", ERR_LIB_CRYPTO, CRYPTO_R_INTEGER_OVERFLOW}, + #else + {"INTEGER_OVERFLOW", 15, 127}, + #endif + #ifdef CRYPTO_R_INVALID_NEGATIVE_VALUE + {"INVALID_NEGATIVE_VALUE", ERR_LIB_CRYPTO, CRYPTO_R_INVALID_NEGATIVE_VALUE}, + #else + {"INVALID_NEGATIVE_VALUE", 15, 122}, + #endif + #ifdef CRYPTO_R_INVALID_NULL_ARGUMENT + {"INVALID_NULL_ARGUMENT", ERR_LIB_CRYPTO, CRYPTO_R_INVALID_NULL_ARGUMENT}, + #else + {"INVALID_NULL_ARGUMENT", 15, 109}, + #endif + #ifdef CRYPTO_R_INVALID_OSSL_PARAM_TYPE + {"INVALID_OSSL_PARAM_TYPE", ERR_LIB_CRYPTO, CRYPTO_R_INVALID_OSSL_PARAM_TYPE}, + #else + {"INVALID_OSSL_PARAM_TYPE", 15, 110}, + #endif + #ifdef CRYPTO_R_NO_PARAMS_TO_MERGE + {"NO_PARAMS_TO_MERGE", ERR_LIB_CRYPTO, CRYPTO_R_NO_PARAMS_TO_MERGE}, + #else + {"NO_PARAMS_TO_MERGE", 15, 131}, + #endif + #ifdef CRYPTO_R_NO_SPACE_FOR_TERMINATING_NULL + {"NO_SPACE_FOR_TERMINATING_NULL", ERR_LIB_CRYPTO, CRYPTO_R_NO_SPACE_FOR_TERMINATING_NULL}, + #else + {"NO_SPACE_FOR_TERMINATING_NULL", 15, 128}, + #endif + #ifdef CRYPTO_R_ODD_NUMBER_OF_DIGITS + {"ODD_NUMBER_OF_DIGITS", ERR_LIB_CRYPTO, CRYPTO_R_ODD_NUMBER_OF_DIGITS}, + #else + {"ODD_NUMBER_OF_DIGITS", 15, 103}, + #endif + #ifdef CRYPTO_R_PARAM_CANNOT_BE_REPRESENTED_EXACTLY + {"PARAM_CANNOT_BE_REPRESENTED_EXACTLY", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_CANNOT_BE_REPRESENTED_EXACTLY}, + #else + {"PARAM_CANNOT_BE_REPRESENTED_EXACTLY", 15, 123}, + #endif + #ifdef CRYPTO_R_PARAM_NOT_INTEGER_TYPE + {"PARAM_NOT_INTEGER_TYPE", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_NOT_INTEGER_TYPE}, + #else + {"PARAM_NOT_INTEGER_TYPE", 15, 124}, + #endif + #ifdef CRYPTO_R_PARAM_OF_INCOMPATIBLE_TYPE + {"PARAM_OF_INCOMPATIBLE_TYPE", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_OF_INCOMPATIBLE_TYPE}, + #else + {"PARAM_OF_INCOMPATIBLE_TYPE", 15, 129}, + #endif + #ifdef CRYPTO_R_PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED + {"PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED}, + #else + {"PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED", 15, 125}, + #endif + #ifdef CRYPTO_R_PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT + {"PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT}, + #else + {"PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT", 15, 130}, + #endif + #ifdef CRYPTO_R_PARAM_VALUE_TOO_LARGE_FOR_DESTINATION + {"PARAM_VALUE_TOO_LARGE_FOR_DESTINATION", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_VALUE_TOO_LARGE_FOR_DESTINATION}, + #else + {"PARAM_VALUE_TOO_LARGE_FOR_DESTINATION", 15, 126}, + #endif + #ifdef CRYPTO_R_PROVIDER_ALREADY_EXISTS + {"PROVIDER_ALREADY_EXISTS", ERR_LIB_CRYPTO, CRYPTO_R_PROVIDER_ALREADY_EXISTS}, + #else + {"PROVIDER_ALREADY_EXISTS", 15, 104}, + #endif + #ifdef CRYPTO_R_PROVIDER_SECTION_ERROR + {"PROVIDER_SECTION_ERROR", ERR_LIB_CRYPTO, CRYPTO_R_PROVIDER_SECTION_ERROR}, + #else + {"PROVIDER_SECTION_ERROR", 15, 105}, + #endif + #ifdef CRYPTO_R_RANDOM_SECTION_ERROR + {"RANDOM_SECTION_ERROR", ERR_LIB_CRYPTO, CRYPTO_R_RANDOM_SECTION_ERROR}, + #else + {"RANDOM_SECTION_ERROR", 15, 119}, + #endif + #ifdef CRYPTO_R_SECURE_MALLOC_FAILURE + {"SECURE_MALLOC_FAILURE", ERR_LIB_CRYPTO, CRYPTO_R_SECURE_MALLOC_FAILURE}, + #else + {"SECURE_MALLOC_FAILURE", 15, 111}, + #endif + #ifdef CRYPTO_R_STRING_TOO_LONG + {"STRING_TOO_LONG", ERR_LIB_CRYPTO, CRYPTO_R_STRING_TOO_LONG}, + #else + {"STRING_TOO_LONG", 15, 112}, + #endif + #ifdef CRYPTO_R_TOO_MANY_BYTES + {"TOO_MANY_BYTES", ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_BYTES}, + #else + {"TOO_MANY_BYTES", 15, 113}, + #endif + #ifdef CRYPTO_R_TOO_MANY_NAMES + {"TOO_MANY_NAMES", ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_NAMES}, + #else + {"TOO_MANY_NAMES", 15, 132}, + #endif + #ifdef CRYPTO_R_TOO_MANY_RECORDS + {"TOO_MANY_RECORDS", ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_RECORDS}, + #else + {"TOO_MANY_RECORDS", 15, 114}, + #endif + #ifdef CRYPTO_R_TOO_SMALL_BUFFER + {"TOO_SMALL_BUFFER", ERR_LIB_CRYPTO, CRYPTO_R_TOO_SMALL_BUFFER}, + #else + {"TOO_SMALL_BUFFER", 15, 116}, + #endif + #ifdef CRYPTO_R_UNKNOWN_NAME_IN_RANDOM_SECTION + {"UNKNOWN_NAME_IN_RANDOM_SECTION", ERR_LIB_CRYPTO, CRYPTO_R_UNKNOWN_NAME_IN_RANDOM_SECTION}, + #else + {"UNKNOWN_NAME_IN_RANDOM_SECTION", 15, 120}, + #endif + #ifdef CRYPTO_R_ZERO_LENGTH_NUMBER + {"ZERO_LENGTH_NUMBER", ERR_LIB_CRYPTO, CRYPTO_R_ZERO_LENGTH_NUMBER}, + #else + {"ZERO_LENGTH_NUMBER", 15, 115}, + #endif + #ifdef CT_R_BASE64_DECODE_ERROR + {"BASE64_DECODE_ERROR", ERR_LIB_CT, CT_R_BASE64_DECODE_ERROR}, + #else + {"BASE64_DECODE_ERROR", 50, 108}, + #endif + #ifdef CT_R_INVALID_LOG_ID_LENGTH + {"INVALID_LOG_ID_LENGTH", ERR_LIB_CT, CT_R_INVALID_LOG_ID_LENGTH}, + #else + {"INVALID_LOG_ID_LENGTH", 50, 100}, + #endif + #ifdef CT_R_LOG_CONF_INVALID + {"LOG_CONF_INVALID", ERR_LIB_CT, CT_R_LOG_CONF_INVALID}, + #else + {"LOG_CONF_INVALID", 50, 109}, + #endif + #ifdef CT_R_LOG_CONF_INVALID_KEY + {"LOG_CONF_INVALID_KEY", ERR_LIB_CT, CT_R_LOG_CONF_INVALID_KEY}, + #else + {"LOG_CONF_INVALID_KEY", 50, 110}, + #endif + #ifdef CT_R_LOG_CONF_MISSING_DESCRIPTION + {"LOG_CONF_MISSING_DESCRIPTION", ERR_LIB_CT, CT_R_LOG_CONF_MISSING_DESCRIPTION}, + #else + {"LOG_CONF_MISSING_DESCRIPTION", 50, 111}, + #endif + #ifdef CT_R_LOG_CONF_MISSING_KEY + {"LOG_CONF_MISSING_KEY", ERR_LIB_CT, CT_R_LOG_CONF_MISSING_KEY}, + #else + {"LOG_CONF_MISSING_KEY", 50, 112}, + #endif + #ifdef CT_R_LOG_KEY_INVALID + {"LOG_KEY_INVALID", ERR_LIB_CT, CT_R_LOG_KEY_INVALID}, + #else + {"LOG_KEY_INVALID", 50, 113}, + #endif + #ifdef CT_R_SCT_FUTURE_TIMESTAMP + {"SCT_FUTURE_TIMESTAMP", ERR_LIB_CT, CT_R_SCT_FUTURE_TIMESTAMP}, + #else + {"SCT_FUTURE_TIMESTAMP", 50, 116}, + #endif + #ifdef CT_R_SCT_INVALID + {"SCT_INVALID", ERR_LIB_CT, CT_R_SCT_INVALID}, + #else + {"SCT_INVALID", 50, 104}, + #endif + #ifdef CT_R_SCT_INVALID_SIGNATURE + {"SCT_INVALID_SIGNATURE", ERR_LIB_CT, CT_R_SCT_INVALID_SIGNATURE}, + #else + {"SCT_INVALID_SIGNATURE", 50, 107}, + #endif + #ifdef CT_R_SCT_LIST_INVALID + {"SCT_LIST_INVALID", ERR_LIB_CT, CT_R_SCT_LIST_INVALID}, + #else + {"SCT_LIST_INVALID", 50, 105}, + #endif + #ifdef CT_R_SCT_LOG_ID_MISMATCH + {"SCT_LOG_ID_MISMATCH", ERR_LIB_CT, CT_R_SCT_LOG_ID_MISMATCH}, + #else + {"SCT_LOG_ID_MISMATCH", 50, 114}, + #endif + #ifdef CT_R_SCT_NOT_SET + {"SCT_NOT_SET", ERR_LIB_CT, CT_R_SCT_NOT_SET}, + #else + {"SCT_NOT_SET", 50, 106}, + #endif + #ifdef CT_R_SCT_UNSUPPORTED_VERSION + {"SCT_UNSUPPORTED_VERSION", ERR_LIB_CT, CT_R_SCT_UNSUPPORTED_VERSION}, + #else + {"SCT_UNSUPPORTED_VERSION", 50, 115}, + #endif + #ifdef CT_R_UNRECOGNIZED_SIGNATURE_NID + {"UNRECOGNIZED_SIGNATURE_NID", ERR_LIB_CT, CT_R_UNRECOGNIZED_SIGNATURE_NID}, + #else + {"UNRECOGNIZED_SIGNATURE_NID", 50, 101}, + #endif + #ifdef CT_R_UNSUPPORTED_ENTRY_TYPE + {"UNSUPPORTED_ENTRY_TYPE", ERR_LIB_CT, CT_R_UNSUPPORTED_ENTRY_TYPE}, + #else + {"UNSUPPORTED_ENTRY_TYPE", 50, 102}, + #endif + #ifdef CT_R_UNSUPPORTED_VERSION + {"UNSUPPORTED_VERSION", ERR_LIB_CT, CT_R_UNSUPPORTED_VERSION}, + #else + {"UNSUPPORTED_VERSION", 50, 103}, + #endif + #ifdef DH_R_BAD_FFC_PARAMETERS + {"BAD_FFC_PARAMETERS", ERR_LIB_DH, DH_R_BAD_FFC_PARAMETERS}, + #else + {"BAD_FFC_PARAMETERS", 5, 127}, + #endif + #ifdef DH_R_BAD_GENERATOR + {"BAD_GENERATOR", ERR_LIB_DH, DH_R_BAD_GENERATOR}, + #else + {"BAD_GENERATOR", 5, 101}, + #endif + #ifdef DH_R_BN_DECODE_ERROR + {"BN_DECODE_ERROR", ERR_LIB_DH, DH_R_BN_DECODE_ERROR}, + #else + {"BN_DECODE_ERROR", 5, 109}, + #endif + #ifdef DH_R_BN_ERROR + {"BN_ERROR", ERR_LIB_DH, DH_R_BN_ERROR}, + #else + {"BN_ERROR", 5, 106}, + #endif + #ifdef DH_R_CHECK_INVALID_J_VALUE + {"CHECK_INVALID_J_VALUE", ERR_LIB_DH, DH_R_CHECK_INVALID_J_VALUE}, + #else + {"CHECK_INVALID_J_VALUE", 5, 115}, + #endif + #ifdef DH_R_CHECK_INVALID_Q_VALUE + {"CHECK_INVALID_Q_VALUE", ERR_LIB_DH, DH_R_CHECK_INVALID_Q_VALUE}, + #else + {"CHECK_INVALID_Q_VALUE", 5, 116}, + #endif + #ifdef DH_R_CHECK_PUBKEY_INVALID + {"CHECK_PUBKEY_INVALID", ERR_LIB_DH, DH_R_CHECK_PUBKEY_INVALID}, + #else + {"CHECK_PUBKEY_INVALID", 5, 122}, + #endif + #ifdef DH_R_CHECK_PUBKEY_TOO_LARGE + {"CHECK_PUBKEY_TOO_LARGE", ERR_LIB_DH, DH_R_CHECK_PUBKEY_TOO_LARGE}, + #else + {"CHECK_PUBKEY_TOO_LARGE", 5, 123}, + #endif + #ifdef DH_R_CHECK_PUBKEY_TOO_SMALL + {"CHECK_PUBKEY_TOO_SMALL", ERR_LIB_DH, DH_R_CHECK_PUBKEY_TOO_SMALL}, + #else + {"CHECK_PUBKEY_TOO_SMALL", 5, 124}, + #endif + #ifdef DH_R_CHECK_P_NOT_PRIME + {"CHECK_P_NOT_PRIME", ERR_LIB_DH, DH_R_CHECK_P_NOT_PRIME}, + #else + {"CHECK_P_NOT_PRIME", 5, 117}, + #endif + #ifdef DH_R_CHECK_P_NOT_SAFE_PRIME + {"CHECK_P_NOT_SAFE_PRIME", ERR_LIB_DH, DH_R_CHECK_P_NOT_SAFE_PRIME}, + #else + {"CHECK_P_NOT_SAFE_PRIME", 5, 118}, + #endif + #ifdef DH_R_CHECK_Q_NOT_PRIME + {"CHECK_Q_NOT_PRIME", ERR_LIB_DH, DH_R_CHECK_Q_NOT_PRIME}, + #else + {"CHECK_Q_NOT_PRIME", 5, 119}, + #endif + #ifdef DH_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_DH, DH_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 5, 104}, + #endif + #ifdef DH_R_INVALID_PARAMETER_NAME + {"INVALID_PARAMETER_NAME", ERR_LIB_DH, DH_R_INVALID_PARAMETER_NAME}, + #else + {"INVALID_PARAMETER_NAME", 5, 110}, + #endif + #ifdef DH_R_INVALID_PARAMETER_NID + {"INVALID_PARAMETER_NID", ERR_LIB_DH, DH_R_INVALID_PARAMETER_NID}, + #else + {"INVALID_PARAMETER_NID", 5, 114}, + #endif + #ifdef DH_R_INVALID_PUBKEY + {"INVALID_PUBKEY", ERR_LIB_DH, DH_R_INVALID_PUBKEY}, + #else + {"INVALID_PUBKEY", 5, 102}, + #endif + #ifdef DH_R_INVALID_SECRET + {"INVALID_SECRET", ERR_LIB_DH, DH_R_INVALID_SECRET}, + #else + {"INVALID_SECRET", 5, 128}, + #endif + #ifdef DH_R_INVALID_SIZE + {"INVALID_SIZE", ERR_LIB_DH, DH_R_INVALID_SIZE}, + #else + {"INVALID_SIZE", 5, 129}, + #endif + #ifdef DH_R_KDF_PARAMETER_ERROR + {"KDF_PARAMETER_ERROR", ERR_LIB_DH, DH_R_KDF_PARAMETER_ERROR}, + #else + {"KDF_PARAMETER_ERROR", 5, 112}, + #endif + #ifdef DH_R_KEYS_NOT_SET + {"KEYS_NOT_SET", ERR_LIB_DH, DH_R_KEYS_NOT_SET}, + #else + {"KEYS_NOT_SET", 5, 108}, + #endif + #ifdef DH_R_MISSING_PUBKEY + {"MISSING_PUBKEY", ERR_LIB_DH, DH_R_MISSING_PUBKEY}, + #else + {"MISSING_PUBKEY", 5, 125}, + #endif + #ifdef DH_R_MODULUS_TOO_LARGE + {"MODULUS_TOO_LARGE", ERR_LIB_DH, DH_R_MODULUS_TOO_LARGE}, + #else + {"MODULUS_TOO_LARGE", 5, 103}, + #endif + #ifdef DH_R_MODULUS_TOO_SMALL + {"MODULUS_TOO_SMALL", ERR_LIB_DH, DH_R_MODULUS_TOO_SMALL}, + #else + {"MODULUS_TOO_SMALL", 5, 126}, + #endif + #ifdef DH_R_NOT_SUITABLE_GENERATOR + {"NOT_SUITABLE_GENERATOR", ERR_LIB_DH, DH_R_NOT_SUITABLE_GENERATOR}, + #else + {"NOT_SUITABLE_GENERATOR", 5, 120}, + #endif + #ifdef DH_R_NO_PARAMETERS_SET + {"NO_PARAMETERS_SET", ERR_LIB_DH, DH_R_NO_PARAMETERS_SET}, + #else + {"NO_PARAMETERS_SET", 5, 107}, + #endif + #ifdef DH_R_NO_PRIVATE_VALUE + {"NO_PRIVATE_VALUE", ERR_LIB_DH, DH_R_NO_PRIVATE_VALUE}, + #else + {"NO_PRIVATE_VALUE", 5, 100}, + #endif + #ifdef DH_R_PARAMETER_ENCODING_ERROR + {"PARAMETER_ENCODING_ERROR", ERR_LIB_DH, DH_R_PARAMETER_ENCODING_ERROR}, + #else + {"PARAMETER_ENCODING_ERROR", 5, 105}, + #endif + #ifdef DH_R_PEER_KEY_ERROR + {"PEER_KEY_ERROR", ERR_LIB_DH, DH_R_PEER_KEY_ERROR}, + #else + {"PEER_KEY_ERROR", 5, 111}, + #endif + #ifdef DH_R_Q_TOO_LARGE + {"Q_TOO_LARGE", ERR_LIB_DH, DH_R_Q_TOO_LARGE}, + #else + {"Q_TOO_LARGE", 5, 130}, + #endif + #ifdef DH_R_SHARED_INFO_ERROR + {"SHARED_INFO_ERROR", ERR_LIB_DH, DH_R_SHARED_INFO_ERROR}, + #else + {"SHARED_INFO_ERROR", 5, 113}, + #endif + #ifdef DH_R_UNABLE_TO_CHECK_GENERATOR + {"UNABLE_TO_CHECK_GENERATOR", ERR_LIB_DH, DH_R_UNABLE_TO_CHECK_GENERATOR}, + #else + {"UNABLE_TO_CHECK_GENERATOR", 5, 121}, + #endif + #ifdef DSA_R_BAD_FFC_PARAMETERS + {"BAD_FFC_PARAMETERS", ERR_LIB_DSA, DSA_R_BAD_FFC_PARAMETERS}, + #else + {"BAD_FFC_PARAMETERS", 10, 114}, + #endif + #ifdef DSA_R_BAD_Q_VALUE + {"BAD_Q_VALUE", ERR_LIB_DSA, DSA_R_BAD_Q_VALUE}, + #else + {"BAD_Q_VALUE", 10, 102}, + #endif + #ifdef DSA_R_BN_DECODE_ERROR + {"BN_DECODE_ERROR", ERR_LIB_DSA, DSA_R_BN_DECODE_ERROR}, + #else + {"BN_DECODE_ERROR", 10, 108}, + #endif + #ifdef DSA_R_BN_ERROR + {"BN_ERROR", ERR_LIB_DSA, DSA_R_BN_ERROR}, + #else + {"BN_ERROR", 10, 109}, + #endif + #ifdef DSA_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_DSA, DSA_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 10, 104}, + #endif + #ifdef DSA_R_INVALID_DIGEST_TYPE + {"INVALID_DIGEST_TYPE", ERR_LIB_DSA, DSA_R_INVALID_DIGEST_TYPE}, + #else + {"INVALID_DIGEST_TYPE", 10, 106}, + #endif + #ifdef DSA_R_INVALID_PARAMETERS + {"INVALID_PARAMETERS", ERR_LIB_DSA, DSA_R_INVALID_PARAMETERS}, + #else + {"INVALID_PARAMETERS", 10, 112}, + #endif + #ifdef DSA_R_MISSING_PARAMETERS + {"MISSING_PARAMETERS", ERR_LIB_DSA, DSA_R_MISSING_PARAMETERS}, + #else + {"MISSING_PARAMETERS", 10, 101}, + #endif + #ifdef DSA_R_MISSING_PRIVATE_KEY + {"MISSING_PRIVATE_KEY", ERR_LIB_DSA, DSA_R_MISSING_PRIVATE_KEY}, + #else + {"MISSING_PRIVATE_KEY", 10, 111}, + #endif + #ifdef DSA_R_MODULUS_TOO_LARGE + {"MODULUS_TOO_LARGE", ERR_LIB_DSA, DSA_R_MODULUS_TOO_LARGE}, + #else + {"MODULUS_TOO_LARGE", 10, 103}, + #endif + #ifdef DSA_R_NO_PARAMETERS_SET + {"NO_PARAMETERS_SET", ERR_LIB_DSA, DSA_R_NO_PARAMETERS_SET}, + #else + {"NO_PARAMETERS_SET", 10, 107}, + #endif + #ifdef DSA_R_PARAMETER_ENCODING_ERROR + {"PARAMETER_ENCODING_ERROR", ERR_LIB_DSA, DSA_R_PARAMETER_ENCODING_ERROR}, + #else + {"PARAMETER_ENCODING_ERROR", 10, 105}, + #endif + #ifdef DSA_R_P_NOT_PRIME + {"P_NOT_PRIME", ERR_LIB_DSA, DSA_R_P_NOT_PRIME}, + #else + {"P_NOT_PRIME", 10, 115}, + #endif + #ifdef DSA_R_Q_NOT_PRIME + {"Q_NOT_PRIME", ERR_LIB_DSA, DSA_R_Q_NOT_PRIME}, + #else + {"Q_NOT_PRIME", 10, 113}, + #endif + #ifdef DSA_R_SEED_LEN_SMALL + {"SEED_LEN_SMALL", ERR_LIB_DSA, DSA_R_SEED_LEN_SMALL}, + #else + {"SEED_LEN_SMALL", 10, 110}, + #endif + #ifdef DSA_R_TOO_MANY_RETRIES + {"TOO_MANY_RETRIES", ERR_LIB_DSA, DSA_R_TOO_MANY_RETRIES}, + #else + {"TOO_MANY_RETRIES", 10, 116}, + #endif + #ifdef DSO_R_CTRL_FAILED + {"CTRL_FAILED", ERR_LIB_DSO, DSO_R_CTRL_FAILED}, + #else + {"CTRL_FAILED", 37, 100}, + #endif + #ifdef DSO_R_DSO_ALREADY_LOADED + {"DSO_ALREADY_LOADED", ERR_LIB_DSO, DSO_R_DSO_ALREADY_LOADED}, + #else + {"DSO_ALREADY_LOADED", 37, 110}, + #endif + #ifdef DSO_R_EMPTY_FILE_STRUCTURE + {"EMPTY_FILE_STRUCTURE", ERR_LIB_DSO, DSO_R_EMPTY_FILE_STRUCTURE}, + #else + {"EMPTY_FILE_STRUCTURE", 37, 113}, + #endif + #ifdef DSO_R_FAILURE + {"FAILURE", ERR_LIB_DSO, DSO_R_FAILURE}, + #else + {"FAILURE", 37, 114}, + #endif + #ifdef DSO_R_FILENAME_TOO_BIG + {"FILENAME_TOO_BIG", ERR_LIB_DSO, DSO_R_FILENAME_TOO_BIG}, + #else + {"FILENAME_TOO_BIG", 37, 101}, + #endif + #ifdef DSO_R_FINISH_FAILED + {"FINISH_FAILED", ERR_LIB_DSO, DSO_R_FINISH_FAILED}, + #else + {"FINISH_FAILED", 37, 102}, + #endif + #ifdef DSO_R_INCORRECT_FILE_SYNTAX + {"INCORRECT_FILE_SYNTAX", ERR_LIB_DSO, DSO_R_INCORRECT_FILE_SYNTAX}, + #else + {"INCORRECT_FILE_SYNTAX", 37, 115}, + #endif + #ifdef DSO_R_LOAD_FAILED + {"LOAD_FAILED", ERR_LIB_DSO, DSO_R_LOAD_FAILED}, + #else + {"LOAD_FAILED", 37, 103}, + #endif + #ifdef DSO_R_NAME_TRANSLATION_FAILED + {"NAME_TRANSLATION_FAILED", ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED}, + #else + {"NAME_TRANSLATION_FAILED", 37, 109}, + #endif + #ifdef DSO_R_NO_FILENAME + {"NO_FILENAME", ERR_LIB_DSO, DSO_R_NO_FILENAME}, + #else + {"NO_FILENAME", 37, 111}, + #endif + #ifdef DSO_R_NULL_HANDLE + {"NULL_HANDLE", ERR_LIB_DSO, DSO_R_NULL_HANDLE}, + #else + {"NULL_HANDLE", 37, 104}, + #endif + #ifdef DSO_R_SET_FILENAME_FAILED + {"SET_FILENAME_FAILED", ERR_LIB_DSO, DSO_R_SET_FILENAME_FAILED}, + #else + {"SET_FILENAME_FAILED", 37, 112}, + #endif + #ifdef DSO_R_STACK_ERROR + {"STACK_ERROR", ERR_LIB_DSO, DSO_R_STACK_ERROR}, + #else + {"STACK_ERROR", 37, 105}, + #endif + #ifdef DSO_R_SYM_FAILURE + {"SYM_FAILURE", ERR_LIB_DSO, DSO_R_SYM_FAILURE}, + #else + {"SYM_FAILURE", 37, 106}, + #endif + #ifdef DSO_R_UNLOAD_FAILED + {"UNLOAD_FAILED", ERR_LIB_DSO, DSO_R_UNLOAD_FAILED}, + #else + {"UNLOAD_FAILED", 37, 107}, + #endif + #ifdef DSO_R_UNSUPPORTED + {"UNSUPPORTED", ERR_LIB_DSO, DSO_R_UNSUPPORTED}, + #else + {"UNSUPPORTED", 37, 108}, + #endif + #ifdef EC_R_ASN1_ERROR + {"ASN1_ERROR", ERR_LIB_EC, EC_R_ASN1_ERROR}, + #else + {"ASN1_ERROR", 16, 115}, + #endif + #ifdef EC_R_BAD_SIGNATURE + {"BAD_SIGNATURE", ERR_LIB_EC, EC_R_BAD_SIGNATURE}, + #else + {"BAD_SIGNATURE", 16, 156}, + #endif + #ifdef EC_R_BIGNUM_OUT_OF_RANGE + {"BIGNUM_OUT_OF_RANGE", ERR_LIB_EC, EC_R_BIGNUM_OUT_OF_RANGE}, + #else + {"BIGNUM_OUT_OF_RANGE", 16, 144}, + #endif + #ifdef EC_R_BUFFER_TOO_SMALL + {"BUFFER_TOO_SMALL", ERR_LIB_EC, EC_R_BUFFER_TOO_SMALL}, + #else + {"BUFFER_TOO_SMALL", 16, 100}, + #endif + #ifdef EC_R_CANNOT_INVERT + {"CANNOT_INVERT", ERR_LIB_EC, EC_R_CANNOT_INVERT}, + #else + {"CANNOT_INVERT", 16, 165}, + #endif + #ifdef EC_R_COORDINATES_OUT_OF_RANGE + {"COORDINATES_OUT_OF_RANGE", ERR_LIB_EC, EC_R_COORDINATES_OUT_OF_RANGE}, + #else + {"COORDINATES_OUT_OF_RANGE", 16, 146}, + #endif + #ifdef EC_R_CURVE_DOES_NOT_SUPPORT_ECDH + {"CURVE_DOES_NOT_SUPPORT_ECDH", ERR_LIB_EC, EC_R_CURVE_DOES_NOT_SUPPORT_ECDH}, + #else + {"CURVE_DOES_NOT_SUPPORT_ECDH", 16, 160}, + #endif + #ifdef EC_R_CURVE_DOES_NOT_SUPPORT_ECDSA + {"CURVE_DOES_NOT_SUPPORT_ECDSA", ERR_LIB_EC, EC_R_CURVE_DOES_NOT_SUPPORT_ECDSA}, + #else + {"CURVE_DOES_NOT_SUPPORT_ECDSA", 16, 170}, + #endif + #ifdef EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING + {"CURVE_DOES_NOT_SUPPORT_SIGNING", ERR_LIB_EC, EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING}, + #else + {"CURVE_DOES_NOT_SUPPORT_SIGNING", 16, 159}, + #endif + #ifdef EC_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_EC, EC_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 16, 142}, + #endif + #ifdef EC_R_DISCRIMINANT_IS_ZERO + {"DISCRIMINANT_IS_ZERO", ERR_LIB_EC, EC_R_DISCRIMINANT_IS_ZERO}, + #else + {"DISCRIMINANT_IS_ZERO", 16, 118}, + #endif + #ifdef EC_R_EC_GROUP_NEW_BY_NAME_FAILURE + {"EC_GROUP_NEW_BY_NAME_FAILURE", ERR_LIB_EC, EC_R_EC_GROUP_NEW_BY_NAME_FAILURE}, + #else + {"EC_GROUP_NEW_BY_NAME_FAILURE", 16, 119}, + #endif + #ifdef EC_R_EXPLICIT_PARAMS_NOT_SUPPORTED + {"EXPLICIT_PARAMS_NOT_SUPPORTED", ERR_LIB_EC, EC_R_EXPLICIT_PARAMS_NOT_SUPPORTED}, + #else + {"EXPLICIT_PARAMS_NOT_SUPPORTED", 16, 127}, + #endif + #ifdef EC_R_FAILED_MAKING_PUBLIC_KEY + {"FAILED_MAKING_PUBLIC_KEY", ERR_LIB_EC, EC_R_FAILED_MAKING_PUBLIC_KEY}, + #else + {"FAILED_MAKING_PUBLIC_KEY", 16, 166}, + #endif + #ifdef EC_R_FIELD_TOO_LARGE + {"FIELD_TOO_LARGE", ERR_LIB_EC, EC_R_FIELD_TOO_LARGE}, + #else + {"FIELD_TOO_LARGE", 16, 143}, + #endif + #ifdef EC_R_GF2M_NOT_SUPPORTED + {"GF2M_NOT_SUPPORTED", ERR_LIB_EC, EC_R_GF2M_NOT_SUPPORTED}, + #else + {"GF2M_NOT_SUPPORTED", 16, 147}, + #endif + #ifdef EC_R_GROUP2PKPARAMETERS_FAILURE + {"GROUP2PKPARAMETERS_FAILURE", ERR_LIB_EC, EC_R_GROUP2PKPARAMETERS_FAILURE}, + #else + {"GROUP2PKPARAMETERS_FAILURE", 16, 120}, + #endif + #ifdef EC_R_I2D_ECPKPARAMETERS_FAILURE + {"I2D_ECPKPARAMETERS_FAILURE", ERR_LIB_EC, EC_R_I2D_ECPKPARAMETERS_FAILURE}, + #else + {"I2D_ECPKPARAMETERS_FAILURE", 16, 121}, + #endif + #ifdef EC_R_INCOMPATIBLE_OBJECTS + {"INCOMPATIBLE_OBJECTS", ERR_LIB_EC, EC_R_INCOMPATIBLE_OBJECTS}, + #else + {"INCOMPATIBLE_OBJECTS", 16, 101}, + #endif + #ifdef EC_R_INVALID_A + {"INVALID_A", ERR_LIB_EC, EC_R_INVALID_A}, + #else + {"INVALID_A", 16, 168}, + #endif + #ifdef EC_R_INVALID_ARGUMENT + {"INVALID_ARGUMENT", ERR_LIB_EC, EC_R_INVALID_ARGUMENT}, + #else + {"INVALID_ARGUMENT", 16, 112}, + #endif + #ifdef EC_R_INVALID_B + {"INVALID_B", ERR_LIB_EC, EC_R_INVALID_B}, + #else + {"INVALID_B", 16, 169}, + #endif + #ifdef EC_R_INVALID_COFACTOR + {"INVALID_COFACTOR", ERR_LIB_EC, EC_R_INVALID_COFACTOR}, + #else + {"INVALID_COFACTOR", 16, 171}, + #endif + #ifdef EC_R_INVALID_COMPRESSED_POINT + {"INVALID_COMPRESSED_POINT", ERR_LIB_EC, EC_R_INVALID_COMPRESSED_POINT}, + #else + {"INVALID_COMPRESSED_POINT", 16, 110}, + #endif + #ifdef EC_R_INVALID_COMPRESSION_BIT + {"INVALID_COMPRESSION_BIT", ERR_LIB_EC, EC_R_INVALID_COMPRESSION_BIT}, + #else + {"INVALID_COMPRESSION_BIT", 16, 109}, + #endif + #ifdef EC_R_INVALID_CURVE + {"INVALID_CURVE", ERR_LIB_EC, EC_R_INVALID_CURVE}, + #else + {"INVALID_CURVE", 16, 141}, + #endif + #ifdef EC_R_INVALID_DIGEST + {"INVALID_DIGEST", ERR_LIB_EC, EC_R_INVALID_DIGEST}, + #else + {"INVALID_DIGEST", 16, 151}, + #endif + #ifdef EC_R_INVALID_DIGEST_TYPE + {"INVALID_DIGEST_TYPE", ERR_LIB_EC, EC_R_INVALID_DIGEST_TYPE}, + #else + {"INVALID_DIGEST_TYPE", 16, 138}, + #endif + #ifdef EC_R_INVALID_ENCODING + {"INVALID_ENCODING", ERR_LIB_EC, EC_R_INVALID_ENCODING}, + #else + {"INVALID_ENCODING", 16, 102}, + #endif + #ifdef EC_R_INVALID_FIELD + {"INVALID_FIELD", ERR_LIB_EC, EC_R_INVALID_FIELD}, + #else + {"INVALID_FIELD", 16, 103}, + #endif + #ifdef EC_R_INVALID_FORM + {"INVALID_FORM", ERR_LIB_EC, EC_R_INVALID_FORM}, + #else + {"INVALID_FORM", 16, 104}, + #endif + #ifdef EC_R_INVALID_GENERATOR + {"INVALID_GENERATOR", ERR_LIB_EC, EC_R_INVALID_GENERATOR}, + #else + {"INVALID_GENERATOR", 16, 173}, + #endif + #ifdef EC_R_INVALID_GROUP_ORDER + {"INVALID_GROUP_ORDER", ERR_LIB_EC, EC_R_INVALID_GROUP_ORDER}, + #else + {"INVALID_GROUP_ORDER", 16, 122}, + #endif + #ifdef EC_R_INVALID_KEY + {"INVALID_KEY", ERR_LIB_EC, EC_R_INVALID_KEY}, + #else + {"INVALID_KEY", 16, 116}, + #endif + #ifdef EC_R_INVALID_LENGTH + {"INVALID_LENGTH", ERR_LIB_EC, EC_R_INVALID_LENGTH}, + #else + {"INVALID_LENGTH", 16, 117}, + #endif + #ifdef EC_R_INVALID_NAMED_GROUP_CONVERSION + {"INVALID_NAMED_GROUP_CONVERSION", ERR_LIB_EC, EC_R_INVALID_NAMED_GROUP_CONVERSION}, + #else + {"INVALID_NAMED_GROUP_CONVERSION", 16, 174}, + #endif + #ifdef EC_R_INVALID_OUTPUT_LENGTH + {"INVALID_OUTPUT_LENGTH", ERR_LIB_EC, EC_R_INVALID_OUTPUT_LENGTH}, + #else + {"INVALID_OUTPUT_LENGTH", 16, 161}, + #endif + #ifdef EC_R_INVALID_P + {"INVALID_P", ERR_LIB_EC, EC_R_INVALID_P}, + #else + {"INVALID_P", 16, 172}, + #endif + #ifdef EC_R_INVALID_PEER_KEY + {"INVALID_PEER_KEY", ERR_LIB_EC, EC_R_INVALID_PEER_KEY}, + #else + {"INVALID_PEER_KEY", 16, 133}, + #endif + #ifdef EC_R_INVALID_PENTANOMIAL_BASIS + {"INVALID_PENTANOMIAL_BASIS", ERR_LIB_EC, EC_R_INVALID_PENTANOMIAL_BASIS}, + #else + {"INVALID_PENTANOMIAL_BASIS", 16, 132}, + #endif + #ifdef EC_R_INVALID_PRIVATE_KEY + {"INVALID_PRIVATE_KEY", ERR_LIB_EC, EC_R_INVALID_PRIVATE_KEY}, + #else + {"INVALID_PRIVATE_KEY", 16, 123}, + #endif + #ifdef EC_R_INVALID_SEED + {"INVALID_SEED", ERR_LIB_EC, EC_R_INVALID_SEED}, + #else + {"INVALID_SEED", 16, 175}, + #endif + #ifdef EC_R_INVALID_TRINOMIAL_BASIS + {"INVALID_TRINOMIAL_BASIS", ERR_LIB_EC, EC_R_INVALID_TRINOMIAL_BASIS}, + #else + {"INVALID_TRINOMIAL_BASIS", 16, 137}, + #endif + #ifdef EC_R_KDF_PARAMETER_ERROR + {"KDF_PARAMETER_ERROR", ERR_LIB_EC, EC_R_KDF_PARAMETER_ERROR}, + #else + {"KDF_PARAMETER_ERROR", 16, 148}, + #endif + #ifdef EC_R_KEYS_NOT_SET + {"KEYS_NOT_SET", ERR_LIB_EC, EC_R_KEYS_NOT_SET}, + #else + {"KEYS_NOT_SET", 16, 140}, + #endif + #ifdef EC_R_LADDER_POST_FAILURE + {"LADDER_POST_FAILURE", ERR_LIB_EC, EC_R_LADDER_POST_FAILURE}, + #else + {"LADDER_POST_FAILURE", 16, 136}, + #endif + #ifdef EC_R_LADDER_PRE_FAILURE + {"LADDER_PRE_FAILURE", ERR_LIB_EC, EC_R_LADDER_PRE_FAILURE}, + #else + {"LADDER_PRE_FAILURE", 16, 153}, + #endif + #ifdef EC_R_LADDER_STEP_FAILURE + {"LADDER_STEP_FAILURE", ERR_LIB_EC, EC_R_LADDER_STEP_FAILURE}, + #else + {"LADDER_STEP_FAILURE", 16, 162}, + #endif + #ifdef EC_R_MISSING_OID + {"MISSING_OID", ERR_LIB_EC, EC_R_MISSING_OID}, + #else + {"MISSING_OID", 16, 167}, + #endif + #ifdef EC_R_MISSING_PARAMETERS + {"MISSING_PARAMETERS", ERR_LIB_EC, EC_R_MISSING_PARAMETERS}, + #else + {"MISSING_PARAMETERS", 16, 124}, + #endif + #ifdef EC_R_MISSING_PRIVATE_KEY + {"MISSING_PRIVATE_KEY", ERR_LIB_EC, EC_R_MISSING_PRIVATE_KEY}, + #else + {"MISSING_PRIVATE_KEY", 16, 125}, + #endif + #ifdef EC_R_NEED_NEW_SETUP_VALUES + {"NEED_NEW_SETUP_VALUES", ERR_LIB_EC, EC_R_NEED_NEW_SETUP_VALUES}, + #else + {"NEED_NEW_SETUP_VALUES", 16, 157}, + #endif + #ifdef EC_R_NOT_A_NIST_PRIME + {"NOT_A_NIST_PRIME", ERR_LIB_EC, EC_R_NOT_A_NIST_PRIME}, + #else + {"NOT_A_NIST_PRIME", 16, 135}, + #endif + #ifdef EC_R_NOT_IMPLEMENTED + {"NOT_IMPLEMENTED", ERR_LIB_EC, EC_R_NOT_IMPLEMENTED}, + #else + {"NOT_IMPLEMENTED", 16, 126}, + #endif + #ifdef EC_R_NOT_INITIALIZED + {"NOT_INITIALIZED", ERR_LIB_EC, EC_R_NOT_INITIALIZED}, + #else + {"NOT_INITIALIZED", 16, 111}, + #endif + #ifdef EC_R_NO_PARAMETERS_SET + {"NO_PARAMETERS_SET", ERR_LIB_EC, EC_R_NO_PARAMETERS_SET}, + #else + {"NO_PARAMETERS_SET", 16, 139}, + #endif + #ifdef EC_R_NO_PRIVATE_VALUE + {"NO_PRIVATE_VALUE", ERR_LIB_EC, EC_R_NO_PRIVATE_VALUE}, + #else + {"NO_PRIVATE_VALUE", 16, 154}, + #endif + #ifdef EC_R_OPERATION_NOT_SUPPORTED + {"OPERATION_NOT_SUPPORTED", ERR_LIB_EC, EC_R_OPERATION_NOT_SUPPORTED}, + #else + {"OPERATION_NOT_SUPPORTED", 16, 152}, + #endif + #ifdef EC_R_PASSED_NULL_PARAMETER + {"PASSED_NULL_PARAMETER", ERR_LIB_EC, EC_R_PASSED_NULL_PARAMETER}, + #else + {"PASSED_NULL_PARAMETER", 16, 134}, + #endif + #ifdef EC_R_PEER_KEY_ERROR + {"PEER_KEY_ERROR", ERR_LIB_EC, EC_R_PEER_KEY_ERROR}, + #else + {"PEER_KEY_ERROR", 16, 149}, + #endif + #ifdef EC_R_POINT_ARITHMETIC_FAILURE + {"POINT_ARITHMETIC_FAILURE", ERR_LIB_EC, EC_R_POINT_ARITHMETIC_FAILURE}, + #else + {"POINT_ARITHMETIC_FAILURE", 16, 155}, + #endif + #ifdef EC_R_POINT_AT_INFINITY + {"POINT_AT_INFINITY", ERR_LIB_EC, EC_R_POINT_AT_INFINITY}, + #else + {"POINT_AT_INFINITY", 16, 106}, + #endif + #ifdef EC_R_POINT_COORDINATES_BLIND_FAILURE + {"POINT_COORDINATES_BLIND_FAILURE", ERR_LIB_EC, EC_R_POINT_COORDINATES_BLIND_FAILURE}, + #else + {"POINT_COORDINATES_BLIND_FAILURE", 16, 163}, + #endif + #ifdef EC_R_POINT_IS_NOT_ON_CURVE + {"POINT_IS_NOT_ON_CURVE", ERR_LIB_EC, EC_R_POINT_IS_NOT_ON_CURVE}, + #else + {"POINT_IS_NOT_ON_CURVE", 16, 107}, + #endif + #ifdef EC_R_RANDOM_NUMBER_GENERATION_FAILED + {"RANDOM_NUMBER_GENERATION_FAILED", ERR_LIB_EC, EC_R_RANDOM_NUMBER_GENERATION_FAILED}, + #else + {"RANDOM_NUMBER_GENERATION_FAILED", 16, 158}, + #endif + #ifdef EC_R_SHARED_INFO_ERROR + {"SHARED_INFO_ERROR", ERR_LIB_EC, EC_R_SHARED_INFO_ERROR}, + #else + {"SHARED_INFO_ERROR", 16, 150}, + #endif + #ifdef EC_R_SLOT_FULL + {"SLOT_FULL", ERR_LIB_EC, EC_R_SLOT_FULL}, + #else + {"SLOT_FULL", 16, 108}, + #endif + #ifdef EC_R_TOO_MANY_RETRIES + {"TOO_MANY_RETRIES", ERR_LIB_EC, EC_R_TOO_MANY_RETRIES}, + #else + {"TOO_MANY_RETRIES", 16, 176}, + #endif + #ifdef EC_R_UNDEFINED_GENERATOR + {"UNDEFINED_GENERATOR", ERR_LIB_EC, EC_R_UNDEFINED_GENERATOR}, + #else + {"UNDEFINED_GENERATOR", 16, 113}, + #endif + #ifdef EC_R_UNDEFINED_ORDER + {"UNDEFINED_ORDER", ERR_LIB_EC, EC_R_UNDEFINED_ORDER}, + #else + {"UNDEFINED_ORDER", 16, 128}, + #endif + #ifdef EC_R_UNKNOWN_COFACTOR + {"UNKNOWN_COFACTOR", ERR_LIB_EC, EC_R_UNKNOWN_COFACTOR}, + #else + {"UNKNOWN_COFACTOR", 16, 164}, + #endif + #ifdef EC_R_UNKNOWN_GROUP + {"UNKNOWN_GROUP", ERR_LIB_EC, EC_R_UNKNOWN_GROUP}, + #else + {"UNKNOWN_GROUP", 16, 129}, + #endif + #ifdef EC_R_UNKNOWN_ORDER + {"UNKNOWN_ORDER", ERR_LIB_EC, EC_R_UNKNOWN_ORDER}, + #else + {"UNKNOWN_ORDER", 16, 114}, + #endif + #ifdef EC_R_UNSUPPORTED_FIELD + {"UNSUPPORTED_FIELD", ERR_LIB_EC, EC_R_UNSUPPORTED_FIELD}, + #else + {"UNSUPPORTED_FIELD", 16, 131}, + #endif + #ifdef EC_R_WRONG_CURVE_PARAMETERS + {"WRONG_CURVE_PARAMETERS", ERR_LIB_EC, EC_R_WRONG_CURVE_PARAMETERS}, + #else + {"WRONG_CURVE_PARAMETERS", 16, 145}, + #endif + #ifdef EC_R_WRONG_ORDER + {"WRONG_ORDER", ERR_LIB_EC, EC_R_WRONG_ORDER}, + #else + {"WRONG_ORDER", 16, 130}, + #endif + #ifdef ENGINE_R_ALREADY_LOADED + {"ALREADY_LOADED", ERR_LIB_ENGINE, ENGINE_R_ALREADY_LOADED}, + #else + {"ALREADY_LOADED", 38, 100}, + #endif + #ifdef ENGINE_R_ARGUMENT_IS_NOT_A_NUMBER + {"ARGUMENT_IS_NOT_A_NUMBER", ERR_LIB_ENGINE, ENGINE_R_ARGUMENT_IS_NOT_A_NUMBER}, + #else + {"ARGUMENT_IS_NOT_A_NUMBER", 38, 133}, + #endif + #ifdef ENGINE_R_CMD_NOT_EXECUTABLE + {"CMD_NOT_EXECUTABLE", ERR_LIB_ENGINE, ENGINE_R_CMD_NOT_EXECUTABLE}, + #else + {"CMD_NOT_EXECUTABLE", 38, 134}, + #endif + #ifdef ENGINE_R_COMMAND_TAKES_INPUT + {"COMMAND_TAKES_INPUT", ERR_LIB_ENGINE, ENGINE_R_COMMAND_TAKES_INPUT}, + #else + {"COMMAND_TAKES_INPUT", 38, 135}, + #endif + #ifdef ENGINE_R_COMMAND_TAKES_NO_INPUT + {"COMMAND_TAKES_NO_INPUT", ERR_LIB_ENGINE, ENGINE_R_COMMAND_TAKES_NO_INPUT}, + #else + {"COMMAND_TAKES_NO_INPUT", 38, 136}, + #endif + #ifdef ENGINE_R_CONFLICTING_ENGINE_ID + {"CONFLICTING_ENGINE_ID", ERR_LIB_ENGINE, ENGINE_R_CONFLICTING_ENGINE_ID}, + #else + {"CONFLICTING_ENGINE_ID", 38, 103}, + #endif + #ifdef ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED + {"CTRL_COMMAND_NOT_IMPLEMENTED", ERR_LIB_ENGINE, ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED}, + #else + {"CTRL_COMMAND_NOT_IMPLEMENTED", 38, 119}, + #endif + #ifdef ENGINE_R_DSO_FAILURE + {"DSO_FAILURE", ERR_LIB_ENGINE, ENGINE_R_DSO_FAILURE}, + #else + {"DSO_FAILURE", 38, 104}, + #endif + #ifdef ENGINE_R_DSO_NOT_FOUND + {"DSO_NOT_FOUND", ERR_LIB_ENGINE, ENGINE_R_DSO_NOT_FOUND}, + #else + {"DSO_NOT_FOUND", 38, 132}, + #endif + #ifdef ENGINE_R_ENGINES_SECTION_ERROR + {"ENGINES_SECTION_ERROR", ERR_LIB_ENGINE, ENGINE_R_ENGINES_SECTION_ERROR}, + #else + {"ENGINES_SECTION_ERROR", 38, 148}, + #endif + #ifdef ENGINE_R_ENGINE_CONFIGURATION_ERROR + {"ENGINE_CONFIGURATION_ERROR", ERR_LIB_ENGINE, ENGINE_R_ENGINE_CONFIGURATION_ERROR}, + #else + {"ENGINE_CONFIGURATION_ERROR", 38, 102}, + #endif + #ifdef ENGINE_R_ENGINE_IS_NOT_IN_LIST + {"ENGINE_IS_NOT_IN_LIST", ERR_LIB_ENGINE, ENGINE_R_ENGINE_IS_NOT_IN_LIST}, + #else + {"ENGINE_IS_NOT_IN_LIST", 38, 105}, + #endif + #ifdef ENGINE_R_ENGINE_SECTION_ERROR + {"ENGINE_SECTION_ERROR", ERR_LIB_ENGINE, ENGINE_R_ENGINE_SECTION_ERROR}, + #else + {"ENGINE_SECTION_ERROR", 38, 149}, + #endif + #ifdef ENGINE_R_FAILED_LOADING_PRIVATE_KEY + {"FAILED_LOADING_PRIVATE_KEY", ERR_LIB_ENGINE, ENGINE_R_FAILED_LOADING_PRIVATE_KEY}, + #else + {"FAILED_LOADING_PRIVATE_KEY", 38, 128}, + #endif + #ifdef ENGINE_R_FAILED_LOADING_PUBLIC_KEY + {"FAILED_LOADING_PUBLIC_KEY", ERR_LIB_ENGINE, ENGINE_R_FAILED_LOADING_PUBLIC_KEY}, + #else + {"FAILED_LOADING_PUBLIC_KEY", 38, 129}, + #endif + #ifdef ENGINE_R_FINISH_FAILED + {"FINISH_FAILED", ERR_LIB_ENGINE, ENGINE_R_FINISH_FAILED}, + #else + {"FINISH_FAILED", 38, 106}, + #endif + #ifdef ENGINE_R_ID_OR_NAME_MISSING + {"ID_OR_NAME_MISSING", ERR_LIB_ENGINE, ENGINE_R_ID_OR_NAME_MISSING}, + #else + {"ID_OR_NAME_MISSING", 38, 108}, + #endif + #ifdef ENGINE_R_INIT_FAILED + {"INIT_FAILED", ERR_LIB_ENGINE, ENGINE_R_INIT_FAILED}, + #else + {"INIT_FAILED", 38, 109}, + #endif + #ifdef ENGINE_R_INTERNAL_LIST_ERROR + {"INTERNAL_LIST_ERROR", ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR}, + #else + {"INTERNAL_LIST_ERROR", 38, 110}, + #endif + #ifdef ENGINE_R_INVALID_ARGUMENT + {"INVALID_ARGUMENT", ERR_LIB_ENGINE, ENGINE_R_INVALID_ARGUMENT}, + #else + {"INVALID_ARGUMENT", 38, 143}, + #endif + #ifdef ENGINE_R_INVALID_CMD_NAME + {"INVALID_CMD_NAME", ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NAME}, + #else + {"INVALID_CMD_NAME", 38, 137}, + #endif + #ifdef ENGINE_R_INVALID_CMD_NUMBER + {"INVALID_CMD_NUMBER", ERR_LIB_ENGINE, ENGINE_R_INVALID_CMD_NUMBER}, + #else + {"INVALID_CMD_NUMBER", 38, 138}, + #endif + #ifdef ENGINE_R_INVALID_INIT_VALUE + {"INVALID_INIT_VALUE", ERR_LIB_ENGINE, ENGINE_R_INVALID_INIT_VALUE}, + #else + {"INVALID_INIT_VALUE", 38, 151}, + #endif + #ifdef ENGINE_R_INVALID_STRING + {"INVALID_STRING", ERR_LIB_ENGINE, ENGINE_R_INVALID_STRING}, + #else + {"INVALID_STRING", 38, 150}, + #endif + #ifdef ENGINE_R_NOT_INITIALISED + {"NOT_INITIALISED", ERR_LIB_ENGINE, ENGINE_R_NOT_INITIALISED}, + #else + {"NOT_INITIALISED", 38, 117}, + #endif + #ifdef ENGINE_R_NOT_LOADED + {"NOT_LOADED", ERR_LIB_ENGINE, ENGINE_R_NOT_LOADED}, + #else + {"NOT_LOADED", 38, 112}, + #endif + #ifdef ENGINE_R_NO_CONTROL_FUNCTION + {"NO_CONTROL_FUNCTION", ERR_LIB_ENGINE, ENGINE_R_NO_CONTROL_FUNCTION}, + #else + {"NO_CONTROL_FUNCTION", 38, 120}, + #endif + #ifdef ENGINE_R_NO_INDEX + {"NO_INDEX", ERR_LIB_ENGINE, ENGINE_R_NO_INDEX}, + #else + {"NO_INDEX", 38, 144}, + #endif + #ifdef ENGINE_R_NO_LOAD_FUNCTION + {"NO_LOAD_FUNCTION", ERR_LIB_ENGINE, ENGINE_R_NO_LOAD_FUNCTION}, + #else + {"NO_LOAD_FUNCTION", 38, 125}, + #endif + #ifdef ENGINE_R_NO_REFERENCE + {"NO_REFERENCE", ERR_LIB_ENGINE, ENGINE_R_NO_REFERENCE}, + #else + {"NO_REFERENCE", 38, 130}, + #endif + #ifdef ENGINE_R_NO_SUCH_ENGINE + {"NO_SUCH_ENGINE", ERR_LIB_ENGINE, ENGINE_R_NO_SUCH_ENGINE}, + #else + {"NO_SUCH_ENGINE", 38, 116}, + #endif + #ifdef ENGINE_R_UNIMPLEMENTED_CIPHER + {"UNIMPLEMENTED_CIPHER", ERR_LIB_ENGINE, ENGINE_R_UNIMPLEMENTED_CIPHER}, + #else + {"UNIMPLEMENTED_CIPHER", 38, 146}, + #endif + #ifdef ENGINE_R_UNIMPLEMENTED_DIGEST + {"UNIMPLEMENTED_DIGEST", ERR_LIB_ENGINE, ENGINE_R_UNIMPLEMENTED_DIGEST}, + #else + {"UNIMPLEMENTED_DIGEST", 38, 147}, + #endif + #ifdef ENGINE_R_UNIMPLEMENTED_PUBLIC_KEY_METHOD + {"UNIMPLEMENTED_PUBLIC_KEY_METHOD", ERR_LIB_ENGINE, ENGINE_R_UNIMPLEMENTED_PUBLIC_KEY_METHOD}, + #else + {"UNIMPLEMENTED_PUBLIC_KEY_METHOD", 38, 101}, + #endif + #ifdef ENGINE_R_VERSION_INCOMPATIBILITY + {"VERSION_INCOMPATIBILITY", ERR_LIB_ENGINE, ENGINE_R_VERSION_INCOMPATIBILITY}, + #else + {"VERSION_INCOMPATIBILITY", 38, 145}, + #endif + #ifdef ESS_R_EMPTY_ESS_CERT_ID_LIST + {"EMPTY_ESS_CERT_ID_LIST", ERR_LIB_ESS, ESS_R_EMPTY_ESS_CERT_ID_LIST}, + #else + {"EMPTY_ESS_CERT_ID_LIST", 54, 107}, + #endif + #ifdef ESS_R_ESS_CERT_DIGEST_ERROR + {"ESS_CERT_DIGEST_ERROR", ERR_LIB_ESS, ESS_R_ESS_CERT_DIGEST_ERROR}, + #else + {"ESS_CERT_DIGEST_ERROR", 54, 103}, + #endif + #ifdef ESS_R_ESS_CERT_ID_NOT_FOUND + {"ESS_CERT_ID_NOT_FOUND", ERR_LIB_ESS, ESS_R_ESS_CERT_ID_NOT_FOUND}, + #else + {"ESS_CERT_ID_NOT_FOUND", 54, 104}, + #endif + #ifdef ESS_R_ESS_CERT_ID_WRONG_ORDER + {"ESS_CERT_ID_WRONG_ORDER", ERR_LIB_ESS, ESS_R_ESS_CERT_ID_WRONG_ORDER}, + #else + {"ESS_CERT_ID_WRONG_ORDER", 54, 105}, + #endif + #ifdef ESS_R_ESS_DIGEST_ALG_UNKNOWN + {"ESS_DIGEST_ALG_UNKNOWN", ERR_LIB_ESS, ESS_R_ESS_DIGEST_ALG_UNKNOWN}, + #else + {"ESS_DIGEST_ALG_UNKNOWN", 54, 106}, + #endif + #ifdef ESS_R_ESS_SIGNING_CERTIFICATE_ERROR + {"ESS_SIGNING_CERTIFICATE_ERROR", ERR_LIB_ESS, ESS_R_ESS_SIGNING_CERTIFICATE_ERROR}, + #else + {"ESS_SIGNING_CERTIFICATE_ERROR", 54, 102}, + #endif + #ifdef ESS_R_ESS_SIGNING_CERT_ADD_ERROR + {"ESS_SIGNING_CERT_ADD_ERROR", ERR_LIB_ESS, ESS_R_ESS_SIGNING_CERT_ADD_ERROR}, + #else + {"ESS_SIGNING_CERT_ADD_ERROR", 54, 100}, + #endif + #ifdef ESS_R_ESS_SIGNING_CERT_V2_ADD_ERROR + {"ESS_SIGNING_CERT_V2_ADD_ERROR", ERR_LIB_ESS, ESS_R_ESS_SIGNING_CERT_V2_ADD_ERROR}, + #else + {"ESS_SIGNING_CERT_V2_ADD_ERROR", 54, 101}, + #endif + #ifdef ESS_R_MISSING_SIGNING_CERTIFICATE_ATTRIBUTE + {"MISSING_SIGNING_CERTIFICATE_ATTRIBUTE", ERR_LIB_ESS, ESS_R_MISSING_SIGNING_CERTIFICATE_ATTRIBUTE}, + #else + {"MISSING_SIGNING_CERTIFICATE_ATTRIBUTE", 54, 108}, + #endif + #ifdef EVP_R_AES_KEY_SETUP_FAILED + {"AES_KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_AES_KEY_SETUP_FAILED}, + #else + {"AES_KEY_SETUP_FAILED", 6, 143}, + #endif + #ifdef EVP_R_ARIA_KEY_SETUP_FAILED + {"ARIA_KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_ARIA_KEY_SETUP_FAILED}, + #else + {"ARIA_KEY_SETUP_FAILED", 6, 176}, + #endif + #ifdef EVP_R_BAD_ALGORITHM_NAME + {"BAD_ALGORITHM_NAME", ERR_LIB_EVP, EVP_R_BAD_ALGORITHM_NAME}, + #else + {"BAD_ALGORITHM_NAME", 6, 200}, + #endif + #ifdef EVP_R_BAD_DECRYPT + {"BAD_DECRYPT", ERR_LIB_EVP, EVP_R_BAD_DECRYPT}, + #else + {"BAD_DECRYPT", 6, 100}, + #endif + #ifdef EVP_R_BAD_KEY_LENGTH + {"BAD_KEY_LENGTH", ERR_LIB_EVP, EVP_R_BAD_KEY_LENGTH}, + #else + {"BAD_KEY_LENGTH", 6, 195}, + #endif + #ifdef EVP_R_BUFFER_TOO_SMALL + {"BUFFER_TOO_SMALL", ERR_LIB_EVP, EVP_R_BUFFER_TOO_SMALL}, + #else + {"BUFFER_TOO_SMALL", 6, 155}, + #endif + #ifdef EVP_R_CACHE_CONSTANTS_FAILED + {"CACHE_CONSTANTS_FAILED", ERR_LIB_EVP, EVP_R_CACHE_CONSTANTS_FAILED}, + #else + {"CACHE_CONSTANTS_FAILED", 6, 225}, + #endif + #ifdef EVP_R_CAMELLIA_KEY_SETUP_FAILED + {"CAMELLIA_KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_CAMELLIA_KEY_SETUP_FAILED}, + #else + {"CAMELLIA_KEY_SETUP_FAILED", 6, 157}, + #endif + #ifdef EVP_R_CANNOT_GET_PARAMETERS + {"CANNOT_GET_PARAMETERS", ERR_LIB_EVP, EVP_R_CANNOT_GET_PARAMETERS}, + #else + {"CANNOT_GET_PARAMETERS", 6, 197}, + #endif + #ifdef EVP_R_CANNOT_SET_PARAMETERS + {"CANNOT_SET_PARAMETERS", ERR_LIB_EVP, EVP_R_CANNOT_SET_PARAMETERS}, + #else + {"CANNOT_SET_PARAMETERS", 6, 198}, + #endif + #ifdef EVP_R_CIPHER_NOT_GCM_MODE + {"CIPHER_NOT_GCM_MODE", ERR_LIB_EVP, EVP_R_CIPHER_NOT_GCM_MODE}, + #else + {"CIPHER_NOT_GCM_MODE", 6, 184}, + #endif + #ifdef EVP_R_CIPHER_PARAMETER_ERROR + {"CIPHER_PARAMETER_ERROR", ERR_LIB_EVP, EVP_R_CIPHER_PARAMETER_ERROR}, + #else + {"CIPHER_PARAMETER_ERROR", 6, 122}, + #endif + #ifdef EVP_R_COMMAND_NOT_SUPPORTED + {"COMMAND_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED}, + #else + {"COMMAND_NOT_SUPPORTED", 6, 147}, + #endif + #ifdef EVP_R_CONFLICTING_ALGORITHM_NAME + {"CONFLICTING_ALGORITHM_NAME", ERR_LIB_EVP, EVP_R_CONFLICTING_ALGORITHM_NAME}, + #else + {"CONFLICTING_ALGORITHM_NAME", 6, 201}, + #endif + #ifdef EVP_R_COPY_ERROR + {"COPY_ERROR", ERR_LIB_EVP, EVP_R_COPY_ERROR}, + #else + {"COPY_ERROR", 6, 173}, + #endif + #ifdef EVP_R_CTRL_NOT_IMPLEMENTED + {"CTRL_NOT_IMPLEMENTED", ERR_LIB_EVP, EVP_R_CTRL_NOT_IMPLEMENTED}, + #else + {"CTRL_NOT_IMPLEMENTED", 6, 132}, + #endif + #ifdef EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED + {"CTRL_OPERATION_NOT_IMPLEMENTED", ERR_LIB_EVP, EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED}, + #else + {"CTRL_OPERATION_NOT_IMPLEMENTED", 6, 133}, + #endif + #ifdef EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH + {"DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH", ERR_LIB_EVP, EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH}, + #else + {"DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH", 6, 138}, + #endif + #ifdef EVP_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_EVP, EVP_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 6, 114}, + #endif + #ifdef EVP_R_DEFAULT_QUERY_PARSE_ERROR + {"DEFAULT_QUERY_PARSE_ERROR", ERR_LIB_EVP, EVP_R_DEFAULT_QUERY_PARSE_ERROR}, + #else + {"DEFAULT_QUERY_PARSE_ERROR", 6, 210}, + #endif + #ifdef EVP_R_DIFFERENT_KEY_TYPES + {"DIFFERENT_KEY_TYPES", ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES}, + #else + {"DIFFERENT_KEY_TYPES", 6, 101}, + #endif + #ifdef EVP_R_DIFFERENT_PARAMETERS + {"DIFFERENT_PARAMETERS", ERR_LIB_EVP, EVP_R_DIFFERENT_PARAMETERS}, + #else + {"DIFFERENT_PARAMETERS", 6, 153}, + #endif + #ifdef EVP_R_ERROR_LOADING_SECTION + {"ERROR_LOADING_SECTION", ERR_LIB_EVP, EVP_R_ERROR_LOADING_SECTION}, + #else + {"ERROR_LOADING_SECTION", 6, 165}, + #endif + #ifdef EVP_R_EXPECTING_AN_HMAC_KEY + {"EXPECTING_AN_HMAC_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_AN_HMAC_KEY}, + #else + {"EXPECTING_AN_HMAC_KEY", 6, 174}, + #endif + #ifdef EVP_R_EXPECTING_AN_RSA_KEY + {"EXPECTING_AN_RSA_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_AN_RSA_KEY}, + #else + {"EXPECTING_AN_RSA_KEY", 6, 127}, + #endif + #ifdef EVP_R_EXPECTING_A_DH_KEY + {"EXPECTING_A_DH_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_DH_KEY}, + #else + {"EXPECTING_A_DH_KEY", 6, 128}, + #endif + #ifdef EVP_R_EXPECTING_A_DSA_KEY + {"EXPECTING_A_DSA_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_DSA_KEY}, + #else + {"EXPECTING_A_DSA_KEY", 6, 129}, + #endif + #ifdef EVP_R_EXPECTING_A_ECX_KEY + {"EXPECTING_A_ECX_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_ECX_KEY}, + #else + {"EXPECTING_A_ECX_KEY", 6, 219}, + #endif + #ifdef EVP_R_EXPECTING_A_EC_KEY + {"EXPECTING_A_EC_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_EC_KEY}, + #else + {"EXPECTING_A_EC_KEY", 6, 142}, + #endif + #ifdef EVP_R_EXPECTING_A_POLY1305_KEY + {"EXPECTING_A_POLY1305_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_POLY1305_KEY}, + #else + {"EXPECTING_A_POLY1305_KEY", 6, 164}, + #endif + #ifdef EVP_R_EXPECTING_A_SIPHASH_KEY + {"EXPECTING_A_SIPHASH_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_SIPHASH_KEY}, + #else + {"EXPECTING_A_SIPHASH_KEY", 6, 175}, + #endif + #ifdef EVP_R_FINAL_ERROR + {"FINAL_ERROR", ERR_LIB_EVP, EVP_R_FINAL_ERROR}, + #else + {"FINAL_ERROR", 6, 188}, + #endif + #ifdef EVP_R_GENERATE_ERROR + {"GENERATE_ERROR", ERR_LIB_EVP, EVP_R_GENERATE_ERROR}, + #else + {"GENERATE_ERROR", 6, 214}, + #endif + #ifdef EVP_R_GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED + {"GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED}, + #else + {"GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED", 6, 229}, + #endif + #ifdef EVP_R_GET_RAW_KEY_FAILED + {"GET_RAW_KEY_FAILED", ERR_LIB_EVP, EVP_R_GET_RAW_KEY_FAILED}, + #else + {"GET_RAW_KEY_FAILED", 6, 182}, + #endif + #ifdef EVP_R_ILLEGAL_SCRYPT_PARAMETERS + {"ILLEGAL_SCRYPT_PARAMETERS", ERR_LIB_EVP, EVP_R_ILLEGAL_SCRYPT_PARAMETERS}, + #else + {"ILLEGAL_SCRYPT_PARAMETERS", 6, 171}, + #endif + #ifdef EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS + {"INACCESSIBLE_DOMAIN_PARAMETERS", ERR_LIB_EVP, EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS}, + #else + {"INACCESSIBLE_DOMAIN_PARAMETERS", 6, 204}, + #endif + #ifdef EVP_R_INACCESSIBLE_KEY + {"INACCESSIBLE_KEY", ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY}, + #else + {"INACCESSIBLE_KEY", 6, 203}, + #endif + #ifdef EVP_R_INITIALIZATION_ERROR + {"INITIALIZATION_ERROR", ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR}, + #else + {"INITIALIZATION_ERROR", 6, 134}, + #endif + #ifdef EVP_R_INPUT_NOT_INITIALIZED + {"INPUT_NOT_INITIALIZED", ERR_LIB_EVP, EVP_R_INPUT_NOT_INITIALIZED}, + #else + {"INPUT_NOT_INITIALIZED", 6, 111}, + #endif + #ifdef EVP_R_INVALID_CUSTOM_LENGTH + {"INVALID_CUSTOM_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_CUSTOM_LENGTH}, + #else + {"INVALID_CUSTOM_LENGTH", 6, 185}, + #endif + #ifdef EVP_R_INVALID_DIGEST + {"INVALID_DIGEST", ERR_LIB_EVP, EVP_R_INVALID_DIGEST}, + #else + {"INVALID_DIGEST", 6, 152}, + #endif + #ifdef EVP_R_INVALID_IV_LENGTH + {"INVALID_IV_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_IV_LENGTH}, + #else + {"INVALID_IV_LENGTH", 6, 194}, + #endif + #ifdef EVP_R_INVALID_KEY + {"INVALID_KEY", ERR_LIB_EVP, EVP_R_INVALID_KEY}, + #else + {"INVALID_KEY", 6, 163}, + #endif + #ifdef EVP_R_INVALID_KEY_LENGTH + {"INVALID_KEY_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_KEY_LENGTH}, + #else + {"INVALID_KEY_LENGTH", 6, 130}, + #endif + #ifdef EVP_R_INVALID_LENGTH + {"INVALID_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_LENGTH}, + #else + {"INVALID_LENGTH", 6, 221}, + #endif + #ifdef EVP_R_INVALID_NULL_ALGORITHM + {"INVALID_NULL_ALGORITHM", ERR_LIB_EVP, EVP_R_INVALID_NULL_ALGORITHM}, + #else + {"INVALID_NULL_ALGORITHM", 6, 218}, + #endif + #ifdef EVP_R_INVALID_OPERATION + {"INVALID_OPERATION", ERR_LIB_EVP, EVP_R_INVALID_OPERATION}, + #else + {"INVALID_OPERATION", 6, 148}, + #endif + #ifdef EVP_R_INVALID_PROVIDER_FUNCTIONS + {"INVALID_PROVIDER_FUNCTIONS", ERR_LIB_EVP, EVP_R_INVALID_PROVIDER_FUNCTIONS}, + #else + {"INVALID_PROVIDER_FUNCTIONS", 6, 193}, + #endif + #ifdef EVP_R_INVALID_SALT_LENGTH + {"INVALID_SALT_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_SALT_LENGTH}, + #else + {"INVALID_SALT_LENGTH", 6, 186}, + #endif + #ifdef EVP_R_INVALID_SECRET_LENGTH + {"INVALID_SECRET_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_SECRET_LENGTH}, + #else + {"INVALID_SECRET_LENGTH", 6, 223}, + #endif + #ifdef EVP_R_INVALID_SEED_LENGTH + {"INVALID_SEED_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_SEED_LENGTH}, + #else + {"INVALID_SEED_LENGTH", 6, 220}, + #endif + #ifdef EVP_R_INVALID_VALUE + {"INVALID_VALUE", ERR_LIB_EVP, EVP_R_INVALID_VALUE}, + #else + {"INVALID_VALUE", 6, 222}, + #endif + #ifdef EVP_R_KEYMGMT_EXPORT_FAILURE + {"KEYMGMT_EXPORT_FAILURE", ERR_LIB_EVP, EVP_R_KEYMGMT_EXPORT_FAILURE}, + #else + {"KEYMGMT_EXPORT_FAILURE", 6, 205}, + #endif + #ifdef EVP_R_KEY_SETUP_FAILED + {"KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_KEY_SETUP_FAILED}, + #else + {"KEY_SETUP_FAILED", 6, 180}, + #endif + #ifdef EVP_R_LOCKING_NOT_SUPPORTED + {"LOCKING_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_LOCKING_NOT_SUPPORTED}, + #else + {"LOCKING_NOT_SUPPORTED", 6, 213}, + #endif + #ifdef EVP_R_MEMORY_LIMIT_EXCEEDED + {"MEMORY_LIMIT_EXCEEDED", ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED}, + #else + {"MEMORY_LIMIT_EXCEEDED", 6, 172}, + #endif + #ifdef EVP_R_MESSAGE_DIGEST_IS_NULL + {"MESSAGE_DIGEST_IS_NULL", ERR_LIB_EVP, EVP_R_MESSAGE_DIGEST_IS_NULL}, + #else + {"MESSAGE_DIGEST_IS_NULL", 6, 159}, + #endif + #ifdef EVP_R_METHOD_NOT_SUPPORTED + {"METHOD_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_METHOD_NOT_SUPPORTED}, + #else + {"METHOD_NOT_SUPPORTED", 6, 144}, + #endif + #ifdef EVP_R_MISSING_PARAMETERS + {"MISSING_PARAMETERS", ERR_LIB_EVP, EVP_R_MISSING_PARAMETERS}, + #else + {"MISSING_PARAMETERS", 6, 103}, + #endif + #ifdef EVP_R_NOT_ABLE_TO_COPY_CTX + {"NOT_ABLE_TO_COPY_CTX", ERR_LIB_EVP, EVP_R_NOT_ABLE_TO_COPY_CTX}, + #else + {"NOT_ABLE_TO_COPY_CTX", 6, 190}, + #endif + #ifdef EVP_R_NOT_XOF_OR_INVALID_LENGTH + {"NOT_XOF_OR_INVALID_LENGTH", ERR_LIB_EVP, EVP_R_NOT_XOF_OR_INVALID_LENGTH}, + #else + {"NOT_XOF_OR_INVALID_LENGTH", 6, 178}, + #endif + #ifdef EVP_R_NO_CIPHER_SET + {"NO_CIPHER_SET", ERR_LIB_EVP, EVP_R_NO_CIPHER_SET}, + #else + {"NO_CIPHER_SET", 6, 131}, + #endif + #ifdef EVP_R_NO_DEFAULT_DIGEST + {"NO_DEFAULT_DIGEST", ERR_LIB_EVP, EVP_R_NO_DEFAULT_DIGEST}, + #else + {"NO_DEFAULT_DIGEST", 6, 158}, + #endif + #ifdef EVP_R_NO_DIGEST_SET + {"NO_DIGEST_SET", ERR_LIB_EVP, EVP_R_NO_DIGEST_SET}, + #else + {"NO_DIGEST_SET", 6, 139}, + #endif + #ifdef EVP_R_NO_IMPORT_FUNCTION + {"NO_IMPORT_FUNCTION", ERR_LIB_EVP, EVP_R_NO_IMPORT_FUNCTION}, + #else + {"NO_IMPORT_FUNCTION", 6, 206}, + #endif + #ifdef EVP_R_NO_KEYMGMT_AVAILABLE + {"NO_KEYMGMT_AVAILABLE", ERR_LIB_EVP, EVP_R_NO_KEYMGMT_AVAILABLE}, + #else + {"NO_KEYMGMT_AVAILABLE", 6, 199}, + #endif + #ifdef EVP_R_NO_KEYMGMT_PRESENT + {"NO_KEYMGMT_PRESENT", ERR_LIB_EVP, EVP_R_NO_KEYMGMT_PRESENT}, + #else + {"NO_KEYMGMT_PRESENT", 6, 196}, + #endif + #ifdef EVP_R_NO_KEY_SET + {"NO_KEY_SET", ERR_LIB_EVP, EVP_R_NO_KEY_SET}, + #else + {"NO_KEY_SET", 6, 154}, + #endif + #ifdef EVP_R_NO_OPERATION_SET + {"NO_OPERATION_SET", ERR_LIB_EVP, EVP_R_NO_OPERATION_SET}, + #else + {"NO_OPERATION_SET", 6, 149}, + #endif + #ifdef EVP_R_NULL_MAC_PKEY_CTX + {"NULL_MAC_PKEY_CTX", ERR_LIB_EVP, EVP_R_NULL_MAC_PKEY_CTX}, + #else + {"NULL_MAC_PKEY_CTX", 6, 208}, + #endif + #ifdef EVP_R_ONLY_ONESHOT_SUPPORTED + {"ONLY_ONESHOT_SUPPORTED", ERR_LIB_EVP, EVP_R_ONLY_ONESHOT_SUPPORTED}, + #else + {"ONLY_ONESHOT_SUPPORTED", 6, 177}, + #endif + #ifdef EVP_R_OPERATION_NOT_INITIALIZED + {"OPERATION_NOT_INITIALIZED", ERR_LIB_EVP, EVP_R_OPERATION_NOT_INITIALIZED}, + #else + {"OPERATION_NOT_INITIALIZED", 6, 151}, + #endif + #ifdef EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE}, + #else + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", 6, 150}, + #endif + #ifdef EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE + {"OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE", ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE}, + #else + {"OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE", 6, 226}, + #endif + #ifdef EVP_R_OUTPUT_WOULD_OVERFLOW + {"OUTPUT_WOULD_OVERFLOW", ERR_LIB_EVP, EVP_R_OUTPUT_WOULD_OVERFLOW}, + #else + {"OUTPUT_WOULD_OVERFLOW", 6, 202}, + #endif + #ifdef EVP_R_PARAMETER_TOO_LARGE + {"PARAMETER_TOO_LARGE", ERR_LIB_EVP, EVP_R_PARAMETER_TOO_LARGE}, + #else + {"PARAMETER_TOO_LARGE", 6, 187}, + #endif + #ifdef EVP_R_PARTIALLY_OVERLAPPING + {"PARTIALLY_OVERLAPPING", ERR_LIB_EVP, EVP_R_PARTIALLY_OVERLAPPING}, + #else + {"PARTIALLY_OVERLAPPING", 6, 162}, + #endif + #ifdef EVP_R_PBKDF2_ERROR + {"PBKDF2_ERROR", ERR_LIB_EVP, EVP_R_PBKDF2_ERROR}, + #else + {"PBKDF2_ERROR", 6, 181}, + #endif + #ifdef EVP_R_PIPELINE_NOT_SUPPORTED + {"PIPELINE_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_PIPELINE_NOT_SUPPORTED}, + #else + {"PIPELINE_NOT_SUPPORTED", 6, 230}, + #endif + #ifdef EVP_R_PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED + {"PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED", ERR_LIB_EVP, EVP_R_PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED}, + #else + {"PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED", 6, 179}, + #endif + #ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR + {"PRIVATE_KEY_DECODE_ERROR", ERR_LIB_EVP, EVP_R_PRIVATE_KEY_DECODE_ERROR}, + #else + {"PRIVATE_KEY_DECODE_ERROR", 6, 145}, + #endif + #ifdef EVP_R_PRIVATE_KEY_ENCODE_ERROR + {"PRIVATE_KEY_ENCODE_ERROR", ERR_LIB_EVP, EVP_R_PRIVATE_KEY_ENCODE_ERROR}, + #else + {"PRIVATE_KEY_ENCODE_ERROR", 6, 146}, + #endif + #ifdef EVP_R_PROVIDER_ASYM_CIPHER_FAILURE + {"PROVIDER_ASYM_CIPHER_FAILURE", ERR_LIB_EVP, EVP_R_PROVIDER_ASYM_CIPHER_FAILURE}, + #else + {"PROVIDER_ASYM_CIPHER_FAILURE", 6, 232}, + #endif + #ifdef EVP_R_PROVIDER_ASYM_CIPHER_NOT_SUPPORTED + {"PROVIDER_ASYM_CIPHER_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_PROVIDER_ASYM_CIPHER_NOT_SUPPORTED}, + #else + {"PROVIDER_ASYM_CIPHER_NOT_SUPPORTED", 6, 235}, + #endif + #ifdef EVP_R_PROVIDER_KEYMGMT_FAILURE + {"PROVIDER_KEYMGMT_FAILURE", ERR_LIB_EVP, EVP_R_PROVIDER_KEYMGMT_FAILURE}, + #else + {"PROVIDER_KEYMGMT_FAILURE", 6, 233}, + #endif + #ifdef EVP_R_PROVIDER_KEYMGMT_NOT_SUPPORTED + {"PROVIDER_KEYMGMT_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_PROVIDER_KEYMGMT_NOT_SUPPORTED}, + #else + {"PROVIDER_KEYMGMT_NOT_SUPPORTED", 6, 236}, + #endif + #ifdef EVP_R_PROVIDER_SIGNATURE_FAILURE + {"PROVIDER_SIGNATURE_FAILURE", ERR_LIB_EVP, EVP_R_PROVIDER_SIGNATURE_FAILURE}, + #else + {"PROVIDER_SIGNATURE_FAILURE", 6, 234}, + #endif + #ifdef EVP_R_PROVIDER_SIGNATURE_NOT_SUPPORTED + {"PROVIDER_SIGNATURE_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_PROVIDER_SIGNATURE_NOT_SUPPORTED}, + #else + {"PROVIDER_SIGNATURE_NOT_SUPPORTED", 6, 237}, + #endif + #ifdef EVP_R_PUBLIC_KEY_NOT_RSA + {"PUBLIC_KEY_NOT_RSA", ERR_LIB_EVP, EVP_R_PUBLIC_KEY_NOT_RSA}, + #else + {"PUBLIC_KEY_NOT_RSA", 6, 106}, + #endif + #ifdef EVP_R_SETTING_XOF_FAILED + {"SETTING_XOF_FAILED", ERR_LIB_EVP, EVP_R_SETTING_XOF_FAILED}, + #else + {"SETTING_XOF_FAILED", 6, 227}, + #endif + #ifdef EVP_R_SET_DEFAULT_PROPERTY_FAILURE + {"SET_DEFAULT_PROPERTY_FAILURE", ERR_LIB_EVP, EVP_R_SET_DEFAULT_PROPERTY_FAILURE}, + #else + {"SET_DEFAULT_PROPERTY_FAILURE", 6, 209}, + #endif + #ifdef EVP_R_SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE + {"SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE", ERR_LIB_EVP, EVP_R_SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE}, + #else + {"SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE", 6, 228}, + #endif + #ifdef EVP_R_TOO_MANY_PIPES + {"TOO_MANY_PIPES", ERR_LIB_EVP, EVP_R_TOO_MANY_PIPES}, + #else + {"TOO_MANY_PIPES", 6, 231}, + #endif + #ifdef EVP_R_TOO_MANY_RECORDS + {"TOO_MANY_RECORDS", ERR_LIB_EVP, EVP_R_TOO_MANY_RECORDS}, + #else + {"TOO_MANY_RECORDS", 6, 183}, + #endif + #ifdef EVP_R_UNABLE_TO_ENABLE_LOCKING + {"UNABLE_TO_ENABLE_LOCKING", ERR_LIB_EVP, EVP_R_UNABLE_TO_ENABLE_LOCKING}, + #else + {"UNABLE_TO_ENABLE_LOCKING", 6, 212}, + #endif + #ifdef EVP_R_UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE + {"UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE", ERR_LIB_EVP, EVP_R_UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE}, + #else + {"UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE", 6, 215}, + #endif + #ifdef EVP_R_UNABLE_TO_GET_RANDOM_STRENGTH + {"UNABLE_TO_GET_RANDOM_STRENGTH", ERR_LIB_EVP, EVP_R_UNABLE_TO_GET_RANDOM_STRENGTH}, + #else + {"UNABLE_TO_GET_RANDOM_STRENGTH", 6, 216}, + #endif + #ifdef EVP_R_UNABLE_TO_LOCK_CONTEXT + {"UNABLE_TO_LOCK_CONTEXT", ERR_LIB_EVP, EVP_R_UNABLE_TO_LOCK_CONTEXT}, + #else + {"UNABLE_TO_LOCK_CONTEXT", 6, 211}, + #endif + #ifdef EVP_R_UNABLE_TO_SET_CALLBACKS + {"UNABLE_TO_SET_CALLBACKS", ERR_LIB_EVP, EVP_R_UNABLE_TO_SET_CALLBACKS}, + #else + {"UNABLE_TO_SET_CALLBACKS", 6, 217}, + #endif + #ifdef EVP_R_UNKNOWN_BITS + {"UNKNOWN_BITS", ERR_LIB_EVP, EVP_R_UNKNOWN_BITS}, + #else + {"UNKNOWN_BITS", 6, 166}, + #endif + #ifdef EVP_R_UNKNOWN_CIPHER + {"UNKNOWN_CIPHER", ERR_LIB_EVP, EVP_R_UNKNOWN_CIPHER}, + #else + {"UNKNOWN_CIPHER", 6, 160}, + #endif + #ifdef EVP_R_UNKNOWN_DIGEST + {"UNKNOWN_DIGEST", ERR_LIB_EVP, EVP_R_UNKNOWN_DIGEST}, + #else + {"UNKNOWN_DIGEST", 6, 161}, + #endif + #ifdef EVP_R_UNKNOWN_KEY_TYPE + {"UNKNOWN_KEY_TYPE", ERR_LIB_EVP, EVP_R_UNKNOWN_KEY_TYPE}, + #else + {"UNKNOWN_KEY_TYPE", 6, 207}, + #endif + #ifdef EVP_R_UNKNOWN_MAX_SIZE + {"UNKNOWN_MAX_SIZE", ERR_LIB_EVP, EVP_R_UNKNOWN_MAX_SIZE}, + #else + {"UNKNOWN_MAX_SIZE", 6, 167}, + #endif + #ifdef EVP_R_UNKNOWN_OPTION + {"UNKNOWN_OPTION", ERR_LIB_EVP, EVP_R_UNKNOWN_OPTION}, + #else + {"UNKNOWN_OPTION", 6, 169}, + #endif + #ifdef EVP_R_UNKNOWN_PBE_ALGORITHM + {"UNKNOWN_PBE_ALGORITHM", ERR_LIB_EVP, EVP_R_UNKNOWN_PBE_ALGORITHM}, + #else + {"UNKNOWN_PBE_ALGORITHM", 6, 121}, + #endif + #ifdef EVP_R_UNKNOWN_SECURITY_BITS + {"UNKNOWN_SECURITY_BITS", ERR_LIB_EVP, EVP_R_UNKNOWN_SECURITY_BITS}, + #else + {"UNKNOWN_SECURITY_BITS", 6, 168}, + #endif + #ifdef EVP_R_UNSUPPORTED_ALGORITHM + {"UNSUPPORTED_ALGORITHM", ERR_LIB_EVP, EVP_R_UNSUPPORTED_ALGORITHM}, + #else + {"UNSUPPORTED_ALGORITHM", 6, 156}, + #endif + #ifdef EVP_R_UNSUPPORTED_CIPHER + {"UNSUPPORTED_CIPHER", ERR_LIB_EVP, EVP_R_UNSUPPORTED_CIPHER}, + #else + {"UNSUPPORTED_CIPHER", 6, 107}, + #endif + #ifdef EVP_R_UNSUPPORTED_KEYLENGTH + {"UNSUPPORTED_KEYLENGTH", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEYLENGTH}, + #else + {"UNSUPPORTED_KEYLENGTH", 6, 123}, + #endif + #ifdef EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION + {"UNSUPPORTED_KEY_DERIVATION_FUNCTION", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION}, + #else + {"UNSUPPORTED_KEY_DERIVATION_FUNCTION", 6, 124}, + #endif + #ifdef EVP_R_UNSUPPORTED_KEY_SIZE + {"UNSUPPORTED_KEY_SIZE", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_SIZE}, + #else + {"UNSUPPORTED_KEY_SIZE", 6, 108}, + #endif + #ifdef EVP_R_UNSUPPORTED_KEY_TYPE + {"UNSUPPORTED_KEY_TYPE", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_TYPE}, + #else + {"UNSUPPORTED_KEY_TYPE", 6, 224}, + #endif + #ifdef EVP_R_UNSUPPORTED_NUMBER_OF_ROUNDS + {"UNSUPPORTED_NUMBER_OF_ROUNDS", ERR_LIB_EVP, EVP_R_UNSUPPORTED_NUMBER_OF_ROUNDS}, + #else + {"UNSUPPORTED_NUMBER_OF_ROUNDS", 6, 135}, + #endif + #ifdef EVP_R_UNSUPPORTED_PRF + {"UNSUPPORTED_PRF", ERR_LIB_EVP, EVP_R_UNSUPPORTED_PRF}, + #else + {"UNSUPPORTED_PRF", 6, 125}, + #endif + #ifdef EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM + {"UNSUPPORTED_PRIVATE_KEY_ALGORITHM", ERR_LIB_EVP, EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM}, + #else + {"UNSUPPORTED_PRIVATE_KEY_ALGORITHM", 6, 118}, + #endif + #ifdef EVP_R_UNSUPPORTED_SALT_TYPE + {"UNSUPPORTED_SALT_TYPE", ERR_LIB_EVP, EVP_R_UNSUPPORTED_SALT_TYPE}, + #else + {"UNSUPPORTED_SALT_TYPE", 6, 126}, + #endif + #ifdef EVP_R_UPDATE_ERROR + {"UPDATE_ERROR", ERR_LIB_EVP, EVP_R_UPDATE_ERROR}, + #else + {"UPDATE_ERROR", 6, 189}, + #endif + #ifdef EVP_R_WRAP_MODE_NOT_ALLOWED + {"WRAP_MODE_NOT_ALLOWED", ERR_LIB_EVP, EVP_R_WRAP_MODE_NOT_ALLOWED}, + #else + {"WRAP_MODE_NOT_ALLOWED", 6, 170}, + #endif + #ifdef EVP_R_WRONG_FINAL_BLOCK_LENGTH + {"WRONG_FINAL_BLOCK_LENGTH", ERR_LIB_EVP, EVP_R_WRONG_FINAL_BLOCK_LENGTH}, + #else + {"WRONG_FINAL_BLOCK_LENGTH", 6, 109}, + #endif + #ifdef EVP_R_XTS_DATA_UNIT_IS_TOO_LARGE + {"XTS_DATA_UNIT_IS_TOO_LARGE", ERR_LIB_EVP, EVP_R_XTS_DATA_UNIT_IS_TOO_LARGE}, + #else + {"XTS_DATA_UNIT_IS_TOO_LARGE", 6, 191}, + #endif + #ifdef EVP_R_XTS_DUPLICATED_KEYS + {"XTS_DUPLICATED_KEYS", ERR_LIB_EVP, EVP_R_XTS_DUPLICATED_KEYS}, + #else + {"XTS_DUPLICATED_KEYS", 6, 192}, + #endif + #ifdef HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN + {"ASN1_LEN_EXCEEDS_MAX_RESP_LEN", ERR_LIB_HTTP, HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN}, + #else + {"ASN1_LEN_EXCEEDS_MAX_RESP_LEN", 61, 108}, + #endif + #ifdef HTTP_R_CONNECT_FAILURE + {"CONNECT_FAILURE", ERR_LIB_HTTP, HTTP_R_CONNECT_FAILURE}, + #else + {"CONNECT_FAILURE", 61, 100}, + #endif + #ifdef HTTP_R_CONTENT_TYPE_MISMATCH + {"CONTENT_TYPE_MISMATCH", ERR_LIB_HTTP, HTTP_R_CONTENT_TYPE_MISMATCH}, + #else + {"CONTENT_TYPE_MISMATCH", 61, 131}, + #endif + #ifdef HTTP_R_ERROR_PARSING_ASN1_LENGTH + {"ERROR_PARSING_ASN1_LENGTH", ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_ASN1_LENGTH}, + #else + {"ERROR_PARSING_ASN1_LENGTH", 61, 109}, + #endif + #ifdef HTTP_R_ERROR_PARSING_CONTENT_LENGTH + {"ERROR_PARSING_CONTENT_LENGTH", ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_CONTENT_LENGTH}, + #else + {"ERROR_PARSING_CONTENT_LENGTH", 61, 119}, + #endif + #ifdef HTTP_R_ERROR_PARSING_URL + {"ERROR_PARSING_URL", ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_URL}, + #else + {"ERROR_PARSING_URL", 61, 101}, + #endif + #ifdef HTTP_R_ERROR_RECEIVING + {"ERROR_RECEIVING", ERR_LIB_HTTP, HTTP_R_ERROR_RECEIVING}, + #else + {"ERROR_RECEIVING", 61, 103}, + #endif + #ifdef HTTP_R_ERROR_SENDING + {"ERROR_SENDING", ERR_LIB_HTTP, HTTP_R_ERROR_SENDING}, + #else + {"ERROR_SENDING", 61, 102}, + #endif + #ifdef HTTP_R_FAILED_READING_DATA + {"FAILED_READING_DATA", ERR_LIB_HTTP, HTTP_R_FAILED_READING_DATA}, + #else + {"FAILED_READING_DATA", 61, 128}, + #endif + #ifdef HTTP_R_HEADER_PARSE_ERROR + {"HEADER_PARSE_ERROR", ERR_LIB_HTTP, HTTP_R_HEADER_PARSE_ERROR}, + #else + {"HEADER_PARSE_ERROR", 61, 126}, + #endif + #ifdef HTTP_R_INCONSISTENT_CONTENT_LENGTH + {"INCONSISTENT_CONTENT_LENGTH", ERR_LIB_HTTP, HTTP_R_INCONSISTENT_CONTENT_LENGTH}, + #else + {"INCONSISTENT_CONTENT_LENGTH", 61, 120}, + #endif + #ifdef HTTP_R_INVALID_PORT_NUMBER + {"INVALID_PORT_NUMBER", ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER}, + #else + {"INVALID_PORT_NUMBER", 61, 123}, + #endif + #ifdef HTTP_R_INVALID_URL_PATH + {"INVALID_URL_PATH", ERR_LIB_HTTP, HTTP_R_INVALID_URL_PATH}, + #else + {"INVALID_URL_PATH", 61, 125}, + #endif + #ifdef HTTP_R_INVALID_URL_SCHEME + {"INVALID_URL_SCHEME", ERR_LIB_HTTP, HTTP_R_INVALID_URL_SCHEME}, + #else + {"INVALID_URL_SCHEME", 61, 124}, + #endif + #ifdef HTTP_R_MAX_RESP_LEN_EXCEEDED + {"MAX_RESP_LEN_EXCEEDED", ERR_LIB_HTTP, HTTP_R_MAX_RESP_LEN_EXCEEDED}, + #else + {"MAX_RESP_LEN_EXCEEDED", 61, 117}, + #endif + #ifdef HTTP_R_MISSING_ASN1_ENCODING + {"MISSING_ASN1_ENCODING", ERR_LIB_HTTP, HTTP_R_MISSING_ASN1_ENCODING}, + #else + {"MISSING_ASN1_ENCODING", 61, 110}, + #endif + #ifdef HTTP_R_MISSING_CONTENT_TYPE + {"MISSING_CONTENT_TYPE", ERR_LIB_HTTP, HTTP_R_MISSING_CONTENT_TYPE}, + #else + {"MISSING_CONTENT_TYPE", 61, 121}, + #endif + #ifdef HTTP_R_MISSING_REDIRECT_LOCATION + {"MISSING_REDIRECT_LOCATION", ERR_LIB_HTTP, HTTP_R_MISSING_REDIRECT_LOCATION}, + #else + {"MISSING_REDIRECT_LOCATION", 61, 111}, + #endif + #ifdef HTTP_R_RECEIVED_ERROR + {"RECEIVED_ERROR", ERR_LIB_HTTP, HTTP_R_RECEIVED_ERROR}, + #else + {"RECEIVED_ERROR", 61, 105}, + #endif + #ifdef HTTP_R_RECEIVED_WRONG_HTTP_VERSION + {"RECEIVED_WRONG_HTTP_VERSION", ERR_LIB_HTTP, HTTP_R_RECEIVED_WRONG_HTTP_VERSION}, + #else + {"RECEIVED_WRONG_HTTP_VERSION", 61, 106}, + #endif + #ifdef HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP + {"REDIRECTION_FROM_HTTPS_TO_HTTP", ERR_LIB_HTTP, HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP}, + #else + {"REDIRECTION_FROM_HTTPS_TO_HTTP", 61, 112}, + #endif + #ifdef HTTP_R_REDIRECTION_NOT_ENABLED + {"REDIRECTION_NOT_ENABLED", ERR_LIB_HTTP, HTTP_R_REDIRECTION_NOT_ENABLED}, + #else + {"REDIRECTION_NOT_ENABLED", 61, 116}, + #endif + #ifdef HTTP_R_RESPONSE_LINE_TOO_LONG + {"RESPONSE_LINE_TOO_LONG", ERR_LIB_HTTP, HTTP_R_RESPONSE_LINE_TOO_LONG}, + #else + {"RESPONSE_LINE_TOO_LONG", 61, 113}, + #endif + #ifdef HTTP_R_RESPONSE_PARSE_ERROR + {"RESPONSE_PARSE_ERROR", ERR_LIB_HTTP, HTTP_R_RESPONSE_PARSE_ERROR}, + #else + {"RESPONSE_PARSE_ERROR", 61, 104}, + #endif + #ifdef HTTP_R_RESPONSE_TOO_MANY_HDRLINES + {"RESPONSE_TOO_MANY_HDRLINES", ERR_LIB_HTTP, HTTP_R_RESPONSE_TOO_MANY_HDRLINES}, + #else + {"RESPONSE_TOO_MANY_HDRLINES", 61, 130}, + #endif + #ifdef HTTP_R_RETRY_TIMEOUT + {"RETRY_TIMEOUT", ERR_LIB_HTTP, HTTP_R_RETRY_TIMEOUT}, + #else + {"RETRY_TIMEOUT", 61, 129}, + #endif + #ifdef HTTP_R_SERVER_CANCELED_CONNECTION + {"SERVER_CANCELED_CONNECTION", ERR_LIB_HTTP, HTTP_R_SERVER_CANCELED_CONNECTION}, + #else + {"SERVER_CANCELED_CONNECTION", 61, 127}, + #endif + #ifdef HTTP_R_SOCK_NOT_SUPPORTED + {"SOCK_NOT_SUPPORTED", ERR_LIB_HTTP, HTTP_R_SOCK_NOT_SUPPORTED}, + #else + {"SOCK_NOT_SUPPORTED", 61, 122}, + #endif + #ifdef HTTP_R_STATUS_CODE_UNSUPPORTED + {"STATUS_CODE_UNSUPPORTED", ERR_LIB_HTTP, HTTP_R_STATUS_CODE_UNSUPPORTED}, + #else + {"STATUS_CODE_UNSUPPORTED", 61, 114}, + #endif + #ifdef HTTP_R_TLS_NOT_ENABLED + {"TLS_NOT_ENABLED", ERR_LIB_HTTP, HTTP_R_TLS_NOT_ENABLED}, + #else + {"TLS_NOT_ENABLED", 61, 107}, + #endif + #ifdef HTTP_R_TOO_MANY_REDIRECTIONS + {"TOO_MANY_REDIRECTIONS", ERR_LIB_HTTP, HTTP_R_TOO_MANY_REDIRECTIONS}, + #else + {"TOO_MANY_REDIRECTIONS", 61, 115}, + #endif + #ifdef HTTP_R_UNEXPECTED_CONTENT_TYPE + {"UNEXPECTED_CONTENT_TYPE", ERR_LIB_HTTP, HTTP_R_UNEXPECTED_CONTENT_TYPE}, + #else + {"UNEXPECTED_CONTENT_TYPE", 61, 118}, + #endif + #ifdef OBJ_R_OID_EXISTS + {"OID_EXISTS", ERR_LIB_OBJ, OBJ_R_OID_EXISTS}, + #else + {"OID_EXISTS", 8, 102}, + #endif + #ifdef OBJ_R_UNKNOWN_NID + {"UNKNOWN_NID", ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID}, + #else + {"UNKNOWN_NID", 8, 101}, + #endif + #ifdef OBJ_R_UNKNOWN_OBJECT_NAME + {"UNKNOWN_OBJECT_NAME", ERR_LIB_OBJ, OBJ_R_UNKNOWN_OBJECT_NAME}, + #else + {"UNKNOWN_OBJECT_NAME", 8, 103}, + #endif + #ifdef OCSP_R_CERTIFICATE_VERIFY_ERROR + {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_OCSP, OCSP_R_CERTIFICATE_VERIFY_ERROR}, + #else + {"CERTIFICATE_VERIFY_ERROR", 39, 101}, + #endif + #ifdef OCSP_R_DIGEST_ERR + {"DIGEST_ERR", ERR_LIB_OCSP, OCSP_R_DIGEST_ERR}, + #else + {"DIGEST_ERR", 39, 102}, + #endif + #ifdef OCSP_R_DIGEST_NAME_ERR + {"DIGEST_NAME_ERR", ERR_LIB_OCSP, OCSP_R_DIGEST_NAME_ERR}, + #else + {"DIGEST_NAME_ERR", 39, 106}, + #endif + #ifdef OCSP_R_DIGEST_SIZE_ERR + {"DIGEST_SIZE_ERR", ERR_LIB_OCSP, OCSP_R_DIGEST_SIZE_ERR}, + #else + {"DIGEST_SIZE_ERR", 39, 107}, + #endif + #ifdef OCSP_R_ERROR_IN_NEXTUPDATE_FIELD + {"ERROR_IN_NEXTUPDATE_FIELD", ERR_LIB_OCSP, OCSP_R_ERROR_IN_NEXTUPDATE_FIELD}, + #else + {"ERROR_IN_NEXTUPDATE_FIELD", 39, 122}, + #endif + #ifdef OCSP_R_ERROR_IN_THISUPDATE_FIELD + {"ERROR_IN_THISUPDATE_FIELD", ERR_LIB_OCSP, OCSP_R_ERROR_IN_THISUPDATE_FIELD}, + #else + {"ERROR_IN_THISUPDATE_FIELD", 39, 123}, + #endif + #ifdef OCSP_R_MISSING_OCSPSIGNING_USAGE + {"MISSING_OCSPSIGNING_USAGE", ERR_LIB_OCSP, OCSP_R_MISSING_OCSPSIGNING_USAGE}, + #else + {"MISSING_OCSPSIGNING_USAGE", 39, 103}, + #endif + #ifdef OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE + {"NEXTUPDATE_BEFORE_THISUPDATE", ERR_LIB_OCSP, OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE}, + #else + {"NEXTUPDATE_BEFORE_THISUPDATE", 39, 124}, + #endif + #ifdef OCSP_R_NOT_BASIC_RESPONSE + {"NOT_BASIC_RESPONSE", ERR_LIB_OCSP, OCSP_R_NOT_BASIC_RESPONSE}, + #else + {"NOT_BASIC_RESPONSE", 39, 104}, + #endif + #ifdef OCSP_R_NO_CERTIFICATES_IN_CHAIN + {"NO_CERTIFICATES_IN_CHAIN", ERR_LIB_OCSP, OCSP_R_NO_CERTIFICATES_IN_CHAIN}, + #else + {"NO_CERTIFICATES_IN_CHAIN", 39, 105}, + #endif + #ifdef OCSP_R_NO_RESPONSE_DATA + {"NO_RESPONSE_DATA", ERR_LIB_OCSP, OCSP_R_NO_RESPONSE_DATA}, + #else + {"NO_RESPONSE_DATA", 39, 108}, + #endif + #ifdef OCSP_R_NO_REVOKED_TIME + {"NO_REVOKED_TIME", ERR_LIB_OCSP, OCSP_R_NO_REVOKED_TIME}, + #else + {"NO_REVOKED_TIME", 39, 109}, + #endif + #ifdef OCSP_R_NO_SIGNER_KEY + {"NO_SIGNER_KEY", ERR_LIB_OCSP, OCSP_R_NO_SIGNER_KEY}, + #else + {"NO_SIGNER_KEY", 39, 130}, + #endif + #ifdef OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_OCSP, OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, + #else + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 39, 110}, + #endif + #ifdef OCSP_R_REQUEST_NOT_SIGNED + {"REQUEST_NOT_SIGNED", ERR_LIB_OCSP, OCSP_R_REQUEST_NOT_SIGNED}, + #else + {"REQUEST_NOT_SIGNED", 39, 128}, + #endif + #ifdef OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA + {"RESPONSE_CONTAINS_NO_REVOCATION_DATA", ERR_LIB_OCSP, OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA}, + #else + {"RESPONSE_CONTAINS_NO_REVOCATION_DATA", 39, 111}, + #endif + #ifdef OCSP_R_ROOT_CA_NOT_TRUSTED + {"ROOT_CA_NOT_TRUSTED", ERR_LIB_OCSP, OCSP_R_ROOT_CA_NOT_TRUSTED}, + #else + {"ROOT_CA_NOT_TRUSTED", 39, 112}, + #endif + #ifdef OCSP_R_SIGNATURE_FAILURE + {"SIGNATURE_FAILURE", ERR_LIB_OCSP, OCSP_R_SIGNATURE_FAILURE}, + #else + {"SIGNATURE_FAILURE", 39, 117}, + #endif + #ifdef OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND + {"SIGNER_CERTIFICATE_NOT_FOUND", ERR_LIB_OCSP, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND}, + #else + {"SIGNER_CERTIFICATE_NOT_FOUND", 39, 118}, + #endif + #ifdef OCSP_R_STATUS_EXPIRED + {"STATUS_EXPIRED", ERR_LIB_OCSP, OCSP_R_STATUS_EXPIRED}, + #else + {"STATUS_EXPIRED", 39, 125}, + #endif + #ifdef OCSP_R_STATUS_NOT_YET_VALID + {"STATUS_NOT_YET_VALID", ERR_LIB_OCSP, OCSP_R_STATUS_NOT_YET_VALID}, + #else + {"STATUS_NOT_YET_VALID", 39, 126}, + #endif + #ifdef OCSP_R_STATUS_TOO_OLD + {"STATUS_TOO_OLD", ERR_LIB_OCSP, OCSP_R_STATUS_TOO_OLD}, + #else + {"STATUS_TOO_OLD", 39, 127}, + #endif + #ifdef OCSP_R_UNKNOWN_MESSAGE_DIGEST + {"UNKNOWN_MESSAGE_DIGEST", ERR_LIB_OCSP, OCSP_R_UNKNOWN_MESSAGE_DIGEST}, + #else + {"UNKNOWN_MESSAGE_DIGEST", 39, 119}, + #endif + #ifdef OCSP_R_UNKNOWN_NID + {"UNKNOWN_NID", ERR_LIB_OCSP, OCSP_R_UNKNOWN_NID}, + #else + {"UNKNOWN_NID", 39, 120}, + #endif + #ifdef OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE + {"UNSUPPORTED_REQUESTORNAME_TYPE", ERR_LIB_OCSP, OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE}, + #else + {"UNSUPPORTED_REQUESTORNAME_TYPE", 39, 129}, + #endif + #ifdef OSSL_DECODER_R_COULD_NOT_DECODE_OBJECT + {"COULD_NOT_DECODE_OBJECT", ERR_LIB_OSSL_DECODER, OSSL_DECODER_R_COULD_NOT_DECODE_OBJECT}, + #else + {"COULD_NOT_DECODE_OBJECT", 60, 101}, + #endif + #ifdef OSSL_DECODER_R_DECODER_NOT_FOUND + {"DECODER_NOT_FOUND", ERR_LIB_OSSL_DECODER, OSSL_DECODER_R_DECODER_NOT_FOUND}, + #else + {"DECODER_NOT_FOUND", 60, 102}, + #endif + #ifdef OSSL_DECODER_R_MISSING_GET_PARAMS + {"MISSING_GET_PARAMS", ERR_LIB_OSSL_DECODER, OSSL_DECODER_R_MISSING_GET_PARAMS}, + #else + {"MISSING_GET_PARAMS", 60, 100}, + #endif + #ifdef OSSL_ENCODER_R_ENCODER_NOT_FOUND + {"ENCODER_NOT_FOUND", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_ENCODER_NOT_FOUND}, + #else + {"ENCODER_NOT_FOUND", 59, 101}, + #endif + #ifdef OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY + {"INCORRECT_PROPERTY_QUERY", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY}, + #else + {"INCORRECT_PROPERTY_QUERY", 59, 100}, + #endif + #ifdef OSSL_ENCODER_R_MISSING_GET_PARAMS + {"MISSING_GET_PARAMS", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_MISSING_GET_PARAMS}, + #else + {"MISSING_GET_PARAMS", 59, 102}, + #endif + #ifdef OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE + {"AMBIGUOUS_CONTENT_TYPE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE}, + #else + {"AMBIGUOUS_CONTENT_TYPE", 44, 107}, + #endif + #ifdef OSSL_STORE_R_BAD_PASSWORD_READ + {"BAD_PASSWORD_READ", ERR_LIB_OSSL_STORE, OSSL_STORE_R_BAD_PASSWORD_READ}, + #else + {"BAD_PASSWORD_READ", 44, 115}, + #endif + #ifdef OSSL_STORE_R_ERROR_VERIFYING_PKCS12_MAC + {"ERROR_VERIFYING_PKCS12_MAC", ERR_LIB_OSSL_STORE, OSSL_STORE_R_ERROR_VERIFYING_PKCS12_MAC}, + #else + {"ERROR_VERIFYING_PKCS12_MAC", 44, 113}, + #endif + #ifdef OSSL_STORE_R_FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST + {"FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST", ERR_LIB_OSSL_STORE, OSSL_STORE_R_FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST}, + #else + {"FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST", 44, 121}, + #endif + #ifdef OSSL_STORE_R_INVALID_SCHEME + {"INVALID_SCHEME", ERR_LIB_OSSL_STORE, OSSL_STORE_R_INVALID_SCHEME}, + #else + {"INVALID_SCHEME", 44, 106}, + #endif + #ifdef OSSL_STORE_R_IS_NOT_A + {"IS_NOT_A", ERR_LIB_OSSL_STORE, OSSL_STORE_R_IS_NOT_A}, + #else + {"IS_NOT_A", 44, 112}, + #endif + #ifdef OSSL_STORE_R_LOADER_INCOMPLETE + {"LOADER_INCOMPLETE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADER_INCOMPLETE}, + #else + {"LOADER_INCOMPLETE", 44, 116}, + #endif + #ifdef OSSL_STORE_R_LOADING_STARTED + {"LOADING_STARTED", ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADING_STARTED}, + #else + {"LOADING_STARTED", 44, 117}, + #endif + #ifdef OSSL_STORE_R_NOT_A_CERTIFICATE + {"NOT_A_CERTIFICATE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_CERTIFICATE}, + #else + {"NOT_A_CERTIFICATE", 44, 100}, + #endif + #ifdef OSSL_STORE_R_NOT_A_CRL + {"NOT_A_CRL", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_CRL}, + #else + {"NOT_A_CRL", 44, 101}, + #endif + #ifdef OSSL_STORE_R_NOT_A_NAME + {"NOT_A_NAME", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_NAME}, + #else + {"NOT_A_NAME", 44, 103}, + #endif + #ifdef OSSL_STORE_R_NOT_A_PRIVATE_KEY + {"NOT_A_PRIVATE_KEY", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_PRIVATE_KEY}, + #else + {"NOT_A_PRIVATE_KEY", 44, 102}, + #endif + #ifdef OSSL_STORE_R_NOT_A_PUBLIC_KEY + {"NOT_A_PUBLIC_KEY", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_PUBLIC_KEY}, + #else + {"NOT_A_PUBLIC_KEY", 44, 122}, + #endif + #ifdef OSSL_STORE_R_NOT_PARAMETERS + {"NOT_PARAMETERS", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_PARAMETERS}, + #else + {"NOT_PARAMETERS", 44, 104}, + #endif + #ifdef OSSL_STORE_R_NO_LOADERS_FOUND + {"NO_LOADERS_FOUND", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NO_LOADERS_FOUND}, + #else + {"NO_LOADERS_FOUND", 44, 123}, + #endif + #ifdef OSSL_STORE_R_PASSPHRASE_CALLBACK_ERROR + {"PASSPHRASE_CALLBACK_ERROR", ERR_LIB_OSSL_STORE, OSSL_STORE_R_PASSPHRASE_CALLBACK_ERROR}, + #else + {"PASSPHRASE_CALLBACK_ERROR", 44, 114}, + #endif + #ifdef OSSL_STORE_R_PATH_MUST_BE_ABSOLUTE + {"PATH_MUST_BE_ABSOLUTE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_PATH_MUST_BE_ABSOLUTE}, + #else + {"PATH_MUST_BE_ABSOLUTE", 44, 108}, + #endif + #ifdef OSSL_STORE_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES + {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", ERR_LIB_OSSL_STORE, OSSL_STORE_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES}, + #else + {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", 44, 119}, + #endif + #ifdef OSSL_STORE_R_UI_PROCESS_INTERRUPTED_OR_CANCELLED + {"UI_PROCESS_INTERRUPTED_OR_CANCELLED", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UI_PROCESS_INTERRUPTED_OR_CANCELLED}, + #else + {"UI_PROCESS_INTERRUPTED_OR_CANCELLED", 44, 109}, + #endif + #ifdef OSSL_STORE_R_UNREGISTERED_SCHEME + {"UNREGISTERED_SCHEME", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNREGISTERED_SCHEME}, + #else + {"UNREGISTERED_SCHEME", 44, 105}, + #endif + #ifdef OSSL_STORE_R_UNSUPPORTED_CONTENT_TYPE + {"UNSUPPORTED_CONTENT_TYPE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_CONTENT_TYPE}, + #else + {"UNSUPPORTED_CONTENT_TYPE", 44, 110}, + #endif + #ifdef OSSL_STORE_R_UNSUPPORTED_OPERATION + {"UNSUPPORTED_OPERATION", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_OPERATION}, + #else + {"UNSUPPORTED_OPERATION", 44, 118}, + #endif + #ifdef OSSL_STORE_R_UNSUPPORTED_SEARCH_TYPE + {"UNSUPPORTED_SEARCH_TYPE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_SEARCH_TYPE}, + #else + {"UNSUPPORTED_SEARCH_TYPE", 44, 120}, + #endif + #ifdef OSSL_STORE_R_URI_AUTHORITY_UNSUPPORTED + {"URI_AUTHORITY_UNSUPPORTED", ERR_LIB_OSSL_STORE, OSSL_STORE_R_URI_AUTHORITY_UNSUPPORTED}, + #else + {"URI_AUTHORITY_UNSUPPORTED", 44, 111}, + #endif + #ifdef PEM_R_BAD_BASE64_DECODE + {"BAD_BASE64_DECODE", ERR_LIB_PEM, PEM_R_BAD_BASE64_DECODE}, + #else + {"BAD_BASE64_DECODE", 9, 100}, + #endif + #ifdef PEM_R_BAD_DECRYPT + {"BAD_DECRYPT", ERR_LIB_PEM, PEM_R_BAD_DECRYPT}, + #else + {"BAD_DECRYPT", 9, 101}, + #endif + #ifdef PEM_R_BAD_END_LINE + {"BAD_END_LINE", ERR_LIB_PEM, PEM_R_BAD_END_LINE}, + #else + {"BAD_END_LINE", 9, 102}, + #endif + #ifdef PEM_R_BAD_IV_CHARS + {"BAD_IV_CHARS", ERR_LIB_PEM, PEM_R_BAD_IV_CHARS}, + #else + {"BAD_IV_CHARS", 9, 103}, + #endif + #ifdef PEM_R_BAD_MAGIC_NUMBER + {"BAD_MAGIC_NUMBER", ERR_LIB_PEM, PEM_R_BAD_MAGIC_NUMBER}, + #else + {"BAD_MAGIC_NUMBER", 9, 116}, + #endif + #ifdef PEM_R_BAD_PASSWORD_READ + {"BAD_PASSWORD_READ", ERR_LIB_PEM, PEM_R_BAD_PASSWORD_READ}, + #else + {"BAD_PASSWORD_READ", 9, 104}, + #endif + #ifdef PEM_R_BAD_VERSION_NUMBER + {"BAD_VERSION_NUMBER", ERR_LIB_PEM, PEM_R_BAD_VERSION_NUMBER}, + #else + {"BAD_VERSION_NUMBER", 9, 117}, + #endif + #ifdef PEM_R_BIO_WRITE_FAILURE + {"BIO_WRITE_FAILURE", ERR_LIB_PEM, PEM_R_BIO_WRITE_FAILURE}, + #else + {"BIO_WRITE_FAILURE", 9, 118}, + #endif + #ifdef PEM_R_CIPHER_IS_NULL + {"CIPHER_IS_NULL", ERR_LIB_PEM, PEM_R_CIPHER_IS_NULL}, + #else + {"CIPHER_IS_NULL", 9, 127}, + #endif + #ifdef PEM_R_ERROR_CONVERTING_PRIVATE_KEY + {"ERROR_CONVERTING_PRIVATE_KEY", ERR_LIB_PEM, PEM_R_ERROR_CONVERTING_PRIVATE_KEY}, + #else + {"ERROR_CONVERTING_PRIVATE_KEY", 9, 115}, + #endif + #ifdef PEM_R_EXPECTING_DSS_KEY_BLOB + {"EXPECTING_DSS_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_DSS_KEY_BLOB}, + #else + {"EXPECTING_DSS_KEY_BLOB", 9, 131}, + #endif + #ifdef PEM_R_EXPECTING_PRIVATE_KEY_BLOB + {"EXPECTING_PRIVATE_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_PRIVATE_KEY_BLOB}, + #else + {"EXPECTING_PRIVATE_KEY_BLOB", 9, 119}, + #endif + #ifdef PEM_R_EXPECTING_PUBLIC_KEY_BLOB + {"EXPECTING_PUBLIC_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_PUBLIC_KEY_BLOB}, + #else + {"EXPECTING_PUBLIC_KEY_BLOB", 9, 120}, + #endif + #ifdef PEM_R_EXPECTING_RSA_KEY_BLOB + {"EXPECTING_RSA_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_RSA_KEY_BLOB}, + #else + {"EXPECTING_RSA_KEY_BLOB", 9, 132}, + #endif + #ifdef PEM_R_HEADER_TOO_LONG + {"HEADER_TOO_LONG", ERR_LIB_PEM, PEM_R_HEADER_TOO_LONG}, + #else + {"HEADER_TOO_LONG", 9, 128}, + #endif + #ifdef PEM_R_INCONSISTENT_HEADER + {"INCONSISTENT_HEADER", ERR_LIB_PEM, PEM_R_INCONSISTENT_HEADER}, + #else + {"INCONSISTENT_HEADER", 9, 121}, + #endif + #ifdef PEM_R_KEYBLOB_HEADER_PARSE_ERROR + {"KEYBLOB_HEADER_PARSE_ERROR", ERR_LIB_PEM, PEM_R_KEYBLOB_HEADER_PARSE_ERROR}, + #else + {"KEYBLOB_HEADER_PARSE_ERROR", 9, 122}, + #endif + #ifdef PEM_R_KEYBLOB_TOO_SHORT + {"KEYBLOB_TOO_SHORT", ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT}, + #else + {"KEYBLOB_TOO_SHORT", 9, 123}, + #endif + #ifdef PEM_R_MISSING_DEK_IV + {"MISSING_DEK_IV", ERR_LIB_PEM, PEM_R_MISSING_DEK_IV}, + #else + {"MISSING_DEK_IV", 9, 129}, + #endif + #ifdef PEM_R_NOT_DEK_INFO + {"NOT_DEK_INFO", ERR_LIB_PEM, PEM_R_NOT_DEK_INFO}, + #else + {"NOT_DEK_INFO", 9, 105}, + #endif + #ifdef PEM_R_NOT_ENCRYPTED + {"NOT_ENCRYPTED", ERR_LIB_PEM, PEM_R_NOT_ENCRYPTED}, + #else + {"NOT_ENCRYPTED", 9, 106}, + #endif + #ifdef PEM_R_NOT_PROC_TYPE + {"NOT_PROC_TYPE", ERR_LIB_PEM, PEM_R_NOT_PROC_TYPE}, + #else + {"NOT_PROC_TYPE", 9, 107}, + #endif + #ifdef PEM_R_NO_START_LINE + {"NO_START_LINE", ERR_LIB_PEM, PEM_R_NO_START_LINE}, + #else + {"NO_START_LINE", 9, 108}, + #endif + #ifdef PEM_R_PROBLEMS_GETTING_PASSWORD + {"PROBLEMS_GETTING_PASSWORD", ERR_LIB_PEM, PEM_R_PROBLEMS_GETTING_PASSWORD}, + #else + {"PROBLEMS_GETTING_PASSWORD", 9, 109}, + #endif + #ifdef PEM_R_PVK_DATA_TOO_SHORT + {"PVK_DATA_TOO_SHORT", ERR_LIB_PEM, PEM_R_PVK_DATA_TOO_SHORT}, + #else + {"PVK_DATA_TOO_SHORT", 9, 124}, + #endif + #ifdef PEM_R_PVK_TOO_SHORT + {"PVK_TOO_SHORT", ERR_LIB_PEM, PEM_R_PVK_TOO_SHORT}, + #else + {"PVK_TOO_SHORT", 9, 125}, + #endif + #ifdef PEM_R_READ_KEY + {"READ_KEY", ERR_LIB_PEM, PEM_R_READ_KEY}, + #else + {"READ_KEY", 9, 111}, + #endif + #ifdef PEM_R_SHORT_HEADER + {"SHORT_HEADER", ERR_LIB_PEM, PEM_R_SHORT_HEADER}, + #else + {"SHORT_HEADER", 9, 112}, + #endif + #ifdef PEM_R_UNEXPECTED_DEK_IV + {"UNEXPECTED_DEK_IV", ERR_LIB_PEM, PEM_R_UNEXPECTED_DEK_IV}, + #else + {"UNEXPECTED_DEK_IV", 9, 130}, + #endif + #ifdef PEM_R_UNSUPPORTED_CIPHER + {"UNSUPPORTED_CIPHER", ERR_LIB_PEM, PEM_R_UNSUPPORTED_CIPHER}, + #else + {"UNSUPPORTED_CIPHER", 9, 113}, + #endif + #ifdef PEM_R_UNSUPPORTED_ENCRYPTION + {"UNSUPPORTED_ENCRYPTION", ERR_LIB_PEM, PEM_R_UNSUPPORTED_ENCRYPTION}, + #else + {"UNSUPPORTED_ENCRYPTION", 9, 114}, + #endif + #ifdef PEM_R_UNSUPPORTED_KEY_COMPONENTS + {"UNSUPPORTED_KEY_COMPONENTS", ERR_LIB_PEM, PEM_R_UNSUPPORTED_KEY_COMPONENTS}, + #else + {"UNSUPPORTED_KEY_COMPONENTS", 9, 126}, + #endif + #ifdef PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE + {"UNSUPPORTED_PUBLIC_KEY_TYPE", ERR_LIB_PEM, PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE}, + #else + {"UNSUPPORTED_PUBLIC_KEY_TYPE", 9, 110}, + #endif + #ifdef PEM_R_UNSUPPORTED_PVK_KEY_TYPE + {"UNSUPPORTED_PVK_KEY_TYPE", ERR_LIB_PEM, PEM_R_UNSUPPORTED_PVK_KEY_TYPE}, + #else + {"UNSUPPORTED_PVK_KEY_TYPE", 9, 133}, + #endif + #ifdef PKCS12_R_CALLBACK_FAILED + {"CALLBACK_FAILED", ERR_LIB_PKCS12, PKCS12_R_CALLBACK_FAILED}, + #else + {"CALLBACK_FAILED", 35, 115}, + #endif + #ifdef PKCS12_R_CANT_PACK_STRUCTURE + {"CANT_PACK_STRUCTURE", ERR_LIB_PKCS12, PKCS12_R_CANT_PACK_STRUCTURE}, + #else + {"CANT_PACK_STRUCTURE", 35, 100}, + #endif + #ifdef PKCS12_R_CONTENT_TYPE_NOT_DATA + {"CONTENT_TYPE_NOT_DATA", ERR_LIB_PKCS12, PKCS12_R_CONTENT_TYPE_NOT_DATA}, + #else + {"CONTENT_TYPE_NOT_DATA", 35, 121}, + #endif + #ifdef PKCS12_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_PKCS12, PKCS12_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 35, 101}, + #endif + #ifdef PKCS12_R_ENCODE_ERROR + {"ENCODE_ERROR", ERR_LIB_PKCS12, PKCS12_R_ENCODE_ERROR}, + #else + {"ENCODE_ERROR", 35, 102}, + #endif + #ifdef PKCS12_R_ENCRYPT_ERROR + {"ENCRYPT_ERROR", ERR_LIB_PKCS12, PKCS12_R_ENCRYPT_ERROR}, + #else + {"ENCRYPT_ERROR", 35, 103}, + #endif + #ifdef PKCS12_R_ERROR_SETTING_ENCRYPTED_DATA_TYPE + {"ERROR_SETTING_ENCRYPTED_DATA_TYPE", ERR_LIB_PKCS12, PKCS12_R_ERROR_SETTING_ENCRYPTED_DATA_TYPE}, + #else + {"ERROR_SETTING_ENCRYPTED_DATA_TYPE", 35, 120}, + #endif + #ifdef PKCS12_R_INVALID_NULL_ARGUMENT + {"INVALID_NULL_ARGUMENT", ERR_LIB_PKCS12, PKCS12_R_INVALID_NULL_ARGUMENT}, + #else + {"INVALID_NULL_ARGUMENT", 35, 104}, + #endif + #ifdef PKCS12_R_INVALID_NULL_PKCS12_POINTER + {"INVALID_NULL_PKCS12_POINTER", ERR_LIB_PKCS12, PKCS12_R_INVALID_NULL_PKCS12_POINTER}, + #else + {"INVALID_NULL_PKCS12_POINTER", 35, 105}, + #endif + #ifdef PKCS12_R_INVALID_TYPE + {"INVALID_TYPE", ERR_LIB_PKCS12, PKCS12_R_INVALID_TYPE}, + #else + {"INVALID_TYPE", 35, 112}, + #endif + #ifdef PKCS12_R_IV_GEN_ERROR + {"IV_GEN_ERROR", ERR_LIB_PKCS12, PKCS12_R_IV_GEN_ERROR}, + #else + {"IV_GEN_ERROR", 35, 106}, + #endif + #ifdef PKCS12_R_KEY_GEN_ERROR + {"KEY_GEN_ERROR", ERR_LIB_PKCS12, PKCS12_R_KEY_GEN_ERROR}, + #else + {"KEY_GEN_ERROR", 35, 107}, + #endif + #ifdef PKCS12_R_MAC_ABSENT + {"MAC_ABSENT", ERR_LIB_PKCS12, PKCS12_R_MAC_ABSENT}, + #else + {"MAC_ABSENT", 35, 108}, + #endif + #ifdef PKCS12_R_MAC_GENERATION_ERROR + {"MAC_GENERATION_ERROR", ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR}, + #else + {"MAC_GENERATION_ERROR", 35, 109}, + #endif + #ifdef PKCS12_R_MAC_SETUP_ERROR + {"MAC_SETUP_ERROR", ERR_LIB_PKCS12, PKCS12_R_MAC_SETUP_ERROR}, + #else + {"MAC_SETUP_ERROR", 35, 110}, + #endif + #ifdef PKCS12_R_MAC_STRING_SET_ERROR + {"MAC_STRING_SET_ERROR", ERR_LIB_PKCS12, PKCS12_R_MAC_STRING_SET_ERROR}, + #else + {"MAC_STRING_SET_ERROR", 35, 111}, + #endif + #ifdef PKCS12_R_MAC_VERIFY_FAILURE + {"MAC_VERIFY_FAILURE", ERR_LIB_PKCS12, PKCS12_R_MAC_VERIFY_FAILURE}, + #else + {"MAC_VERIFY_FAILURE", 35, 113}, + #endif + #ifdef PKCS12_R_PARSE_ERROR + {"PARSE_ERROR", ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR}, + #else + {"PARSE_ERROR", 35, 114}, + #endif + #ifdef PKCS12_R_PKCS12_CIPHERFINAL_ERROR + {"PKCS12_CIPHERFINAL_ERROR", ERR_LIB_PKCS12, PKCS12_R_PKCS12_CIPHERFINAL_ERROR}, + #else + {"PKCS12_CIPHERFINAL_ERROR", 35, 116}, + #endif + #ifdef PKCS12_R_UNKNOWN_DIGEST_ALGORITHM + {"UNKNOWN_DIGEST_ALGORITHM", ERR_LIB_PKCS12, PKCS12_R_UNKNOWN_DIGEST_ALGORITHM}, + #else + {"UNKNOWN_DIGEST_ALGORITHM", 35, 118}, + #endif + #ifdef PKCS12_R_UNSUPPORTED_PKCS12_MODE + {"UNSUPPORTED_PKCS12_MODE", ERR_LIB_PKCS12, PKCS12_R_UNSUPPORTED_PKCS12_MODE}, + #else + {"UNSUPPORTED_PKCS12_MODE", 35, 119}, + #endif + #ifdef PKCS7_R_CERTIFICATE_VERIFY_ERROR + {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_PKCS7, PKCS7_R_CERTIFICATE_VERIFY_ERROR}, + #else + {"CERTIFICATE_VERIFY_ERROR", 33, 117}, + #endif + #ifdef PKCS7_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER + {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", ERR_LIB_PKCS7, PKCS7_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER}, + #else + {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", 33, 144}, + #endif + #ifdef PKCS7_R_CIPHER_NOT_INITIALIZED + {"CIPHER_NOT_INITIALIZED", ERR_LIB_PKCS7, PKCS7_R_CIPHER_NOT_INITIALIZED}, + #else + {"CIPHER_NOT_INITIALIZED", 33, 116}, + #endif + #ifdef PKCS7_R_CONTENT_AND_DATA_PRESENT + {"CONTENT_AND_DATA_PRESENT", ERR_LIB_PKCS7, PKCS7_R_CONTENT_AND_DATA_PRESENT}, + #else + {"CONTENT_AND_DATA_PRESENT", 33, 118}, + #endif + #ifdef PKCS7_R_CTRL_ERROR + {"CTRL_ERROR", ERR_LIB_PKCS7, PKCS7_R_CTRL_ERROR}, + #else + {"CTRL_ERROR", 33, 152}, + #endif + #ifdef PKCS7_R_DECRYPT_ERROR + {"DECRYPT_ERROR", ERR_LIB_PKCS7, PKCS7_R_DECRYPT_ERROR}, + #else + {"DECRYPT_ERROR", 33, 119}, + #endif + #ifdef PKCS7_R_DIGEST_FAILURE + {"DIGEST_FAILURE", ERR_LIB_PKCS7, PKCS7_R_DIGEST_FAILURE}, + #else + {"DIGEST_FAILURE", 33, 101}, + #endif + #ifdef PKCS7_R_ENCRYPTION_CTRL_FAILURE + {"ENCRYPTION_CTRL_FAILURE", ERR_LIB_PKCS7, PKCS7_R_ENCRYPTION_CTRL_FAILURE}, + #else + {"ENCRYPTION_CTRL_FAILURE", 33, 149}, + #endif + #ifdef PKCS7_R_ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE + {"ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", ERR_LIB_PKCS7, PKCS7_R_ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE}, + #else + {"ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", 33, 150}, + #endif + #ifdef PKCS7_R_ERROR_ADDING_RECIPIENT + {"ERROR_ADDING_RECIPIENT", ERR_LIB_PKCS7, PKCS7_R_ERROR_ADDING_RECIPIENT}, + #else + {"ERROR_ADDING_RECIPIENT", 33, 120}, + #endif + #ifdef PKCS7_R_ERROR_SETTING_CIPHER + {"ERROR_SETTING_CIPHER", ERR_LIB_PKCS7, PKCS7_R_ERROR_SETTING_CIPHER}, + #else + {"ERROR_SETTING_CIPHER", 33, 121}, + #endif + #ifdef PKCS7_R_INVALID_NULL_POINTER + {"INVALID_NULL_POINTER", ERR_LIB_PKCS7, PKCS7_R_INVALID_NULL_POINTER}, + #else + {"INVALID_NULL_POINTER", 33, 143}, + #endif + #ifdef PKCS7_R_INVALID_SIGNED_DATA_TYPE + {"INVALID_SIGNED_DATA_TYPE", ERR_LIB_PKCS7, PKCS7_R_INVALID_SIGNED_DATA_TYPE}, + #else + {"INVALID_SIGNED_DATA_TYPE", 33, 155}, + #endif + #ifdef PKCS7_R_NO_CONTENT + {"NO_CONTENT", ERR_LIB_PKCS7, PKCS7_R_NO_CONTENT}, + #else + {"NO_CONTENT", 33, 122}, + #endif + #ifdef PKCS7_R_NO_DEFAULT_DIGEST + {"NO_DEFAULT_DIGEST", ERR_LIB_PKCS7, PKCS7_R_NO_DEFAULT_DIGEST}, + #else + {"NO_DEFAULT_DIGEST", 33, 151}, + #endif + #ifdef PKCS7_R_NO_MATCHING_DIGEST_TYPE_FOUND + {"NO_MATCHING_DIGEST_TYPE_FOUND", ERR_LIB_PKCS7, PKCS7_R_NO_MATCHING_DIGEST_TYPE_FOUND}, + #else + {"NO_MATCHING_DIGEST_TYPE_FOUND", 33, 154}, + #endif + #ifdef PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE + {"NO_RECIPIENT_MATCHES_CERTIFICATE", ERR_LIB_PKCS7, PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE}, + #else + {"NO_RECIPIENT_MATCHES_CERTIFICATE", 33, 115}, + #endif + #ifdef PKCS7_R_NO_SIGNATURES_ON_DATA + {"NO_SIGNATURES_ON_DATA", ERR_LIB_PKCS7, PKCS7_R_NO_SIGNATURES_ON_DATA}, + #else + {"NO_SIGNATURES_ON_DATA", 33, 123}, + #endif + #ifdef PKCS7_R_NO_SIGNERS + {"NO_SIGNERS", ERR_LIB_PKCS7, PKCS7_R_NO_SIGNERS}, + #else + {"NO_SIGNERS", 33, 142}, + #endif + #ifdef PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE + {"OPERATION_NOT_SUPPORTED_ON_THIS_TYPE", ERR_LIB_PKCS7, PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE}, + #else + {"OPERATION_NOT_SUPPORTED_ON_THIS_TYPE", 33, 104}, + #endif + #ifdef PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR + {"PKCS7_ADD_SIGNATURE_ERROR", ERR_LIB_PKCS7, PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR}, + #else + {"PKCS7_ADD_SIGNATURE_ERROR", 33, 124}, + #endif + #ifdef PKCS7_R_PKCS7_ADD_SIGNER_ERROR + {"PKCS7_ADD_SIGNER_ERROR", ERR_LIB_PKCS7, PKCS7_R_PKCS7_ADD_SIGNER_ERROR}, + #else + {"PKCS7_ADD_SIGNER_ERROR", 33, 153}, + #endif + #ifdef PKCS7_R_PKCS7_DATASIGN + {"PKCS7_DATASIGN", ERR_LIB_PKCS7, PKCS7_R_PKCS7_DATASIGN}, + #else + {"PKCS7_DATASIGN", 33, 145}, + #endif + #ifdef PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_PKCS7, PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, + #else + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 33, 127}, + #endif + #ifdef PKCS7_R_SIGNATURE_FAILURE + {"SIGNATURE_FAILURE", ERR_LIB_PKCS7, PKCS7_R_SIGNATURE_FAILURE}, + #else + {"SIGNATURE_FAILURE", 33, 105}, + #endif + #ifdef PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND + {"SIGNER_CERTIFICATE_NOT_FOUND", ERR_LIB_PKCS7, PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND}, + #else + {"SIGNER_CERTIFICATE_NOT_FOUND", 33, 128}, + #endif + #ifdef PKCS7_R_SIGNING_CTRL_FAILURE + {"SIGNING_CTRL_FAILURE", ERR_LIB_PKCS7, PKCS7_R_SIGNING_CTRL_FAILURE}, + #else + {"SIGNING_CTRL_FAILURE", 33, 147}, + #endif + #ifdef PKCS7_R_SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE + {"SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", ERR_LIB_PKCS7, PKCS7_R_SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE}, + #else + {"SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", 33, 148}, + #endif + #ifdef PKCS7_R_SMIME_TEXT_ERROR + {"SMIME_TEXT_ERROR", ERR_LIB_PKCS7, PKCS7_R_SMIME_TEXT_ERROR}, + #else + {"SMIME_TEXT_ERROR", 33, 129}, + #endif + #ifdef PKCS7_R_UNABLE_TO_FIND_CERTIFICATE + {"UNABLE_TO_FIND_CERTIFICATE", ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_CERTIFICATE}, + #else + {"UNABLE_TO_FIND_CERTIFICATE", 33, 106}, + #endif + #ifdef PKCS7_R_UNABLE_TO_FIND_MEM_BIO + {"UNABLE_TO_FIND_MEM_BIO", ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MEM_BIO}, + #else + {"UNABLE_TO_FIND_MEM_BIO", 33, 107}, + #endif + #ifdef PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST + {"UNABLE_TO_FIND_MESSAGE_DIGEST", ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST}, + #else + {"UNABLE_TO_FIND_MESSAGE_DIGEST", 33, 108}, + #endif + #ifdef PKCS7_R_UNKNOWN_DIGEST_TYPE + {"UNKNOWN_DIGEST_TYPE", ERR_LIB_PKCS7, PKCS7_R_UNKNOWN_DIGEST_TYPE}, + #else + {"UNKNOWN_DIGEST_TYPE", 33, 109}, + #endif + #ifdef PKCS7_R_UNKNOWN_OPERATION + {"UNKNOWN_OPERATION", ERR_LIB_PKCS7, PKCS7_R_UNKNOWN_OPERATION}, + #else + {"UNKNOWN_OPERATION", 33, 110}, + #endif + #ifdef PKCS7_R_UNSUPPORTED_CIPHER_TYPE + {"UNSUPPORTED_CIPHER_TYPE", ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CIPHER_TYPE}, + #else + {"UNSUPPORTED_CIPHER_TYPE", 33, 111}, + #endif + #ifdef PKCS7_R_UNSUPPORTED_CONTENT_TYPE + {"UNSUPPORTED_CONTENT_TYPE", ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE}, + #else + {"UNSUPPORTED_CONTENT_TYPE", 33, 112}, + #endif + #ifdef PKCS7_R_WRONG_CONTENT_TYPE + {"WRONG_CONTENT_TYPE", ERR_LIB_PKCS7, PKCS7_R_WRONG_CONTENT_TYPE}, + #else + {"WRONG_CONTENT_TYPE", 33, 113}, + #endif + #ifdef PKCS7_R_WRONG_PKCS7_TYPE + {"WRONG_PKCS7_TYPE", ERR_LIB_PKCS7, PKCS7_R_WRONG_PKCS7_TYPE}, + #else + {"WRONG_PKCS7_TYPE", 33, 114}, + #endif + #ifdef PROP_R_NAME_TOO_LONG + {"NAME_TOO_LONG", ERR_LIB_PROP, PROP_R_NAME_TOO_LONG}, + #else + {"NAME_TOO_LONG", 55, 100}, + #endif + #ifdef PROP_R_NOT_AN_ASCII_CHARACTER + {"NOT_AN_ASCII_CHARACTER", ERR_LIB_PROP, PROP_R_NOT_AN_ASCII_CHARACTER}, + #else + {"NOT_AN_ASCII_CHARACTER", 55, 101}, + #endif + #ifdef PROP_R_NOT_AN_HEXADECIMAL_DIGIT + {"NOT_AN_HEXADECIMAL_DIGIT", ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT}, + #else + {"NOT_AN_HEXADECIMAL_DIGIT", 55, 102}, + #endif + #ifdef PROP_R_NOT_AN_IDENTIFIER + {"NOT_AN_IDENTIFIER", ERR_LIB_PROP, PROP_R_NOT_AN_IDENTIFIER}, + #else + {"NOT_AN_IDENTIFIER", 55, 103}, + #endif + #ifdef PROP_R_NOT_AN_OCTAL_DIGIT + {"NOT_AN_OCTAL_DIGIT", ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT}, + #else + {"NOT_AN_OCTAL_DIGIT", 55, 104}, + #endif + #ifdef PROP_R_NOT_A_DECIMAL_DIGIT + {"NOT_A_DECIMAL_DIGIT", ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT}, + #else + {"NOT_A_DECIMAL_DIGIT", 55, 105}, + #endif + #ifdef PROP_R_NO_MATCHING_STRING_DELIMITER + {"NO_MATCHING_STRING_DELIMITER", ERR_LIB_PROP, PROP_R_NO_MATCHING_STRING_DELIMITER}, + #else + {"NO_MATCHING_STRING_DELIMITER", 55, 106}, + #endif + #ifdef PROP_R_NO_VALUE + {"NO_VALUE", ERR_LIB_PROP, PROP_R_NO_VALUE}, + #else + {"NO_VALUE", 55, 107}, + #endif + #ifdef PROP_R_PARSE_FAILED + {"PARSE_FAILED", ERR_LIB_PROP, PROP_R_PARSE_FAILED}, + #else + {"PARSE_FAILED", 55, 108}, + #endif + #ifdef PROP_R_STRING_TOO_LONG + {"STRING_TOO_LONG", ERR_LIB_PROP, PROP_R_STRING_TOO_LONG}, + #else + {"STRING_TOO_LONG", 55, 109}, + #endif + #ifdef PROP_R_TRAILING_CHARACTERS + {"TRAILING_CHARACTERS", ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS}, + #else + {"TRAILING_CHARACTERS", 55, 110}, + #endif + #ifdef PROV_R_ADDITIONAL_INPUT_TOO_LONG + {"ADDITIONAL_INPUT_TOO_LONG", ERR_LIB_PROV, PROV_R_ADDITIONAL_INPUT_TOO_LONG}, + #else + {"ADDITIONAL_INPUT_TOO_LONG", 57, 184}, + #endif + #ifdef PROV_R_ALGORITHM_MISMATCH + {"ALGORITHM_MISMATCH", ERR_LIB_PROV, PROV_R_ALGORITHM_MISMATCH}, + #else + {"ALGORITHM_MISMATCH", 57, 173}, + #endif + #ifdef PROV_R_ALREADY_INSTANTIATED + {"ALREADY_INSTANTIATED", ERR_LIB_PROV, PROV_R_ALREADY_INSTANTIATED}, + #else + {"ALREADY_INSTANTIATED", 57, 185}, + #endif + #ifdef PROV_R_BAD_DECRYPT + {"BAD_DECRYPT", ERR_LIB_PROV, PROV_R_BAD_DECRYPT}, + #else + {"BAD_DECRYPT", 57, 100}, + #endif + #ifdef PROV_R_BAD_ENCODING + {"BAD_ENCODING", ERR_LIB_PROV, PROV_R_BAD_ENCODING}, + #else + {"BAD_ENCODING", 57, 141}, + #endif + #ifdef PROV_R_BAD_LENGTH + {"BAD_LENGTH", ERR_LIB_PROV, PROV_R_BAD_LENGTH}, + #else + {"BAD_LENGTH", 57, 142}, + #endif + #ifdef PROV_R_BAD_TLS_CLIENT_VERSION + {"BAD_TLS_CLIENT_VERSION", ERR_LIB_PROV, PROV_R_BAD_TLS_CLIENT_VERSION}, + #else + {"BAD_TLS_CLIENT_VERSION", 57, 161}, + #endif + #ifdef PROV_R_BN_ERROR + {"BN_ERROR", ERR_LIB_PROV, PROV_R_BN_ERROR}, + #else + {"BN_ERROR", 57, 160}, + #endif + #ifdef PROV_R_CIPHER_OPERATION_FAILED + {"CIPHER_OPERATION_FAILED", ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED}, + #else + {"CIPHER_OPERATION_FAILED", 57, 102}, + #endif + #ifdef PROV_R_COFACTOR_REQUIRED + {"COFACTOR_REQUIRED", ERR_LIB_PROV, PROV_R_COFACTOR_REQUIRED}, + #else + {"COFACTOR_REQUIRED", 57, 236}, + #endif + #ifdef PROV_R_DERIVATION_FUNCTION_INIT_FAILED + {"DERIVATION_FUNCTION_INIT_FAILED", ERR_LIB_PROV, PROV_R_DERIVATION_FUNCTION_INIT_FAILED}, + #else + {"DERIVATION_FUNCTION_INIT_FAILED", 57, 205}, + #endif + #ifdef PROV_R_DIGEST_NOT_ALLOWED + {"DIGEST_NOT_ALLOWED", ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED}, + #else + {"DIGEST_NOT_ALLOWED", 57, 174}, + #endif + #ifdef PROV_R_EMS_NOT_ENABLED + {"EMS_NOT_ENABLED", ERR_LIB_PROV, PROV_R_EMS_NOT_ENABLED}, + #else + {"EMS_NOT_ENABLED", 57, 233}, + #endif + #ifdef PROV_R_ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS + {"ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS", ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS}, + #else + {"ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS", 57, 244}, + #endif + #ifdef PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK + {"ENTROPY_SOURCE_STRENGTH_TOO_WEAK", ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK}, + #else + {"ENTROPY_SOURCE_STRENGTH_TOO_WEAK", 57, 186}, + #endif + #ifdef PROV_R_ERROR_INSTANTIATING_DRBG + {"ERROR_INSTANTIATING_DRBG", ERR_LIB_PROV, PROV_R_ERROR_INSTANTIATING_DRBG}, + #else + {"ERROR_INSTANTIATING_DRBG", 57, 188}, + #endif + #ifdef PROV_R_ERROR_RETRIEVING_ENTROPY + {"ERROR_RETRIEVING_ENTROPY", ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_ENTROPY}, + #else + {"ERROR_RETRIEVING_ENTROPY", 57, 189}, + #endif + #ifdef PROV_R_ERROR_RETRIEVING_NONCE + {"ERROR_RETRIEVING_NONCE", ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE}, + #else + {"ERROR_RETRIEVING_NONCE", 57, 190}, + #endif + #ifdef PROV_R_FAILED_DURING_DERIVATION + {"FAILED_DURING_DERIVATION", ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION}, + #else + {"FAILED_DURING_DERIVATION", 57, 164}, + #endif + #ifdef PROV_R_FAILED_TO_CREATE_LOCK + {"FAILED_TO_CREATE_LOCK", ERR_LIB_PROV, PROV_R_FAILED_TO_CREATE_LOCK}, + #else + {"FAILED_TO_CREATE_LOCK", 57, 180}, + #endif + #ifdef PROV_R_FAILED_TO_DECRYPT + {"FAILED_TO_DECRYPT", ERR_LIB_PROV, PROV_R_FAILED_TO_DECRYPT}, + #else + {"FAILED_TO_DECRYPT", 57, 162}, + #endif + #ifdef PROV_R_FAILED_TO_GENERATE_KEY + {"FAILED_TO_GENERATE_KEY", ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY}, + #else + {"FAILED_TO_GENERATE_KEY", 57, 121}, + #endif + #ifdef PROV_R_FAILED_TO_GET_PARAMETER + {"FAILED_TO_GET_PARAMETER", ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER}, + #else + {"FAILED_TO_GET_PARAMETER", 57, 103}, + #endif + #ifdef PROV_R_FAILED_TO_SET_PARAMETER + {"FAILED_TO_SET_PARAMETER", ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER}, + #else + {"FAILED_TO_SET_PARAMETER", 57, 104}, + #endif + #ifdef PROV_R_FAILED_TO_SIGN + {"FAILED_TO_SIGN", ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN}, + #else + {"FAILED_TO_SIGN", 57, 175}, + #endif + #ifdef PROV_R_FINAL_CALL_OUT_OF_ORDER + {"FINAL_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_FINAL_CALL_OUT_OF_ORDER}, + #else + {"FINAL_CALL_OUT_OF_ORDER", 57, 237}, + #endif + #ifdef PROV_R_FIPS_MODULE_CONDITIONAL_ERROR + {"FIPS_MODULE_CONDITIONAL_ERROR", ERR_LIB_PROV, PROV_R_FIPS_MODULE_CONDITIONAL_ERROR}, + #else + {"FIPS_MODULE_CONDITIONAL_ERROR", 57, 227}, + #endif + #ifdef PROV_R_FIPS_MODULE_ENTERING_ERROR_STATE + {"FIPS_MODULE_ENTERING_ERROR_STATE", ERR_LIB_PROV, PROV_R_FIPS_MODULE_ENTERING_ERROR_STATE}, + #else + {"FIPS_MODULE_ENTERING_ERROR_STATE", 57, 224}, + #endif + #ifdef PROV_R_FIPS_MODULE_IMPORT_PCT_ERROR + {"FIPS_MODULE_IMPORT_PCT_ERROR", ERR_LIB_PROV, PROV_R_FIPS_MODULE_IMPORT_PCT_ERROR}, + #else + {"FIPS_MODULE_IMPORT_PCT_ERROR", 57, 253}, + #endif + #ifdef PROV_R_FIPS_MODULE_IN_ERROR_STATE + {"FIPS_MODULE_IN_ERROR_STATE", ERR_LIB_PROV, PROV_R_FIPS_MODULE_IN_ERROR_STATE}, + #else + {"FIPS_MODULE_IN_ERROR_STATE", 57, 225}, + #endif + #ifdef PROV_R_GENERATE_ERROR + {"GENERATE_ERROR", ERR_LIB_PROV, PROV_R_GENERATE_ERROR}, + #else + {"GENERATE_ERROR", 57, 191}, + #endif + #ifdef PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE + {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", ERR_LIB_PROV, PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE}, + #else + {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", 57, 165}, + #endif + #ifdef PROV_R_INDICATOR_INTEGRITY_FAILURE + {"INDICATOR_INTEGRITY_FAILURE", ERR_LIB_PROV, PROV_R_INDICATOR_INTEGRITY_FAILURE}, + #else + {"INDICATOR_INTEGRITY_FAILURE", 57, 210}, + #endif + #ifdef PROV_R_INIT_CALL_OUT_OF_ORDER + {"INIT_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_INIT_CALL_OUT_OF_ORDER}, + #else + {"INIT_CALL_OUT_OF_ORDER", 57, 238}, + #endif + #ifdef PROV_R_INSUFFICIENT_DRBG_STRENGTH + {"INSUFFICIENT_DRBG_STRENGTH", ERR_LIB_PROV, PROV_R_INSUFFICIENT_DRBG_STRENGTH}, + #else + {"INSUFFICIENT_DRBG_STRENGTH", 57, 181}, + #endif + #ifdef PROV_R_INVALID_AAD + {"INVALID_AAD", ERR_LIB_PROV, PROV_R_INVALID_AAD}, + #else + {"INVALID_AAD", 57, 108}, + #endif + #ifdef PROV_R_INVALID_AEAD + {"INVALID_AEAD", ERR_LIB_PROV, PROV_R_INVALID_AEAD}, + #else + {"INVALID_AEAD", 57, 231}, + #endif + #ifdef PROV_R_INVALID_CONFIG_DATA + {"INVALID_CONFIG_DATA", ERR_LIB_PROV, PROV_R_INVALID_CONFIG_DATA}, + #else + {"INVALID_CONFIG_DATA", 57, 211}, + #endif + #ifdef PROV_R_INVALID_CONSTANT_LENGTH + {"INVALID_CONSTANT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_CONSTANT_LENGTH}, + #else + {"INVALID_CONSTANT_LENGTH", 57, 157}, + #endif + #ifdef PROV_R_INVALID_CURVE + {"INVALID_CURVE", ERR_LIB_PROV, PROV_R_INVALID_CURVE}, + #else + {"INVALID_CURVE", 57, 176}, + #endif + #ifdef PROV_R_INVALID_CUSTOM_LENGTH + {"INVALID_CUSTOM_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_CUSTOM_LENGTH}, + #else + {"INVALID_CUSTOM_LENGTH", 57, 111}, + #endif + #ifdef PROV_R_INVALID_DATA + {"INVALID_DATA", ERR_LIB_PROV, PROV_R_INVALID_DATA}, + #else + {"INVALID_DATA", 57, 115}, + #endif + #ifdef PROV_R_INVALID_DIGEST + {"INVALID_DIGEST", ERR_LIB_PROV, PROV_R_INVALID_DIGEST}, + #else + {"INVALID_DIGEST", 57, 122}, + #endif + #ifdef PROV_R_INVALID_DIGEST_LENGTH + {"INVALID_DIGEST_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH}, + #else + {"INVALID_DIGEST_LENGTH", 57, 166}, + #endif + #ifdef PROV_R_INVALID_DIGEST_SIZE + {"INVALID_DIGEST_SIZE", ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE}, + #else + {"INVALID_DIGEST_SIZE", 57, 218}, + #endif + #ifdef PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION + {"INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION", ERR_LIB_PROV, PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION}, + #else + {"INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION", 57, 243}, + #endif + #ifdef PROV_R_INVALID_INPUT_LENGTH + {"INVALID_INPUT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH}, + #else + {"INVALID_INPUT_LENGTH", 57, 230}, + #endif + #ifdef PROV_R_INVALID_ITERATION_COUNT + {"INVALID_ITERATION_COUNT", ERR_LIB_PROV, PROV_R_INVALID_ITERATION_COUNT}, + #else + {"INVALID_ITERATION_COUNT", 57, 123}, + #endif + #ifdef PROV_R_INVALID_IV_LENGTH + {"INVALID_IV_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH}, + #else + {"INVALID_IV_LENGTH", 57, 109}, + #endif + #ifdef PROV_R_INVALID_KDF + {"INVALID_KDF", ERR_LIB_PROV, PROV_R_INVALID_KDF}, + #else + {"INVALID_KDF", 57, 232}, + #endif + #ifdef PROV_R_INVALID_KEY + {"INVALID_KEY", ERR_LIB_PROV, PROV_R_INVALID_KEY}, + #else + {"INVALID_KEY", 57, 158}, + #endif + #ifdef PROV_R_INVALID_KEY_LENGTH + {"INVALID_KEY_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH}, + #else + {"INVALID_KEY_LENGTH", 57, 105}, + #endif + #ifdef PROV_R_INVALID_MAC + {"INVALID_MAC", ERR_LIB_PROV, PROV_R_INVALID_MAC}, + #else + {"INVALID_MAC", 57, 151}, + #endif + #ifdef PROV_R_INVALID_MEMORY_SIZE + {"INVALID_MEMORY_SIZE", ERR_LIB_PROV, PROV_R_INVALID_MEMORY_SIZE}, + #else + {"INVALID_MEMORY_SIZE", 57, 235}, + #endif + #ifdef PROV_R_INVALID_MGF1_MD + {"INVALID_MGF1_MD", ERR_LIB_PROV, PROV_R_INVALID_MGF1_MD}, + #else + {"INVALID_MGF1_MD", 57, 167}, + #endif + #ifdef PROV_R_INVALID_MODE + {"INVALID_MODE", ERR_LIB_PROV, PROV_R_INVALID_MODE}, + #else + {"INVALID_MODE", 57, 125}, + #endif + #ifdef PROV_R_INVALID_OUTPUT_LENGTH + {"INVALID_OUTPUT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH}, + #else + {"INVALID_OUTPUT_LENGTH", 57, 217}, + #endif + #ifdef PROV_R_INVALID_PADDING_MODE + {"INVALID_PADDING_MODE", ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE}, + #else + {"INVALID_PADDING_MODE", 57, 168}, + #endif + #ifdef PROV_R_INVALID_PREHASHED_DIGEST_LENGTH + {"INVALID_PREHASHED_DIGEST_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_PREHASHED_DIGEST_LENGTH}, + #else + {"INVALID_PREHASHED_DIGEST_LENGTH", 57, 241}, + #endif + #ifdef PROV_R_INVALID_PUBINFO + {"INVALID_PUBINFO", ERR_LIB_PROV, PROV_R_INVALID_PUBINFO}, + #else + {"INVALID_PUBINFO", 57, 198}, + #endif + #ifdef PROV_R_INVALID_SALT_LENGTH + {"INVALID_SALT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH}, + #else + {"INVALID_SALT_LENGTH", 57, 112}, + #endif + #ifdef PROV_R_INVALID_SEED_LENGTH + {"INVALID_SEED_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH}, + #else + {"INVALID_SEED_LENGTH", 57, 154}, + #endif + #ifdef PROV_R_INVALID_SIGNATURE_SIZE + {"INVALID_SIGNATURE_SIZE", ERR_LIB_PROV, PROV_R_INVALID_SIGNATURE_SIZE}, + #else + {"INVALID_SIGNATURE_SIZE", 57, 179}, + #endif + #ifdef PROV_R_INVALID_STATE + {"INVALID_STATE", ERR_LIB_PROV, PROV_R_INVALID_STATE}, + #else + {"INVALID_STATE", 57, 212}, + #endif + #ifdef PROV_R_INVALID_TAG + {"INVALID_TAG", ERR_LIB_PROV, PROV_R_INVALID_TAG}, + #else + {"INVALID_TAG", 57, 110}, + #endif + #ifdef PROV_R_INVALID_TAG_LENGTH + {"INVALID_TAG_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH}, + #else + {"INVALID_TAG_LENGTH", 57, 118}, + #endif + #ifdef PROV_R_INVALID_THREAD_POOL_SIZE + {"INVALID_THREAD_POOL_SIZE", ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE}, + #else + {"INVALID_THREAD_POOL_SIZE", 57, 234}, + #endif + #ifdef PROV_R_INVALID_UKM_LENGTH + {"INVALID_UKM_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_UKM_LENGTH}, + #else + {"INVALID_UKM_LENGTH", 57, 200}, + #endif + #ifdef PROV_R_INVALID_X931_DIGEST + {"INVALID_X931_DIGEST", ERR_LIB_PROV, PROV_R_INVALID_X931_DIGEST}, + #else + {"INVALID_X931_DIGEST", 57, 170}, + #endif + #ifdef PROV_R_IN_ERROR_STATE + {"IN_ERROR_STATE", ERR_LIB_PROV, PROV_R_IN_ERROR_STATE}, + #else + {"IN_ERROR_STATE", 57, 192}, + #endif + #ifdef PROV_R_KEY_SETUP_FAILED + {"KEY_SETUP_FAILED", ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED}, + #else + {"KEY_SETUP_FAILED", 57, 101}, + #endif + #ifdef PROV_R_KEY_SIZE_TOO_SMALL + {"KEY_SIZE_TOO_SMALL", ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL}, + #else + {"KEY_SIZE_TOO_SMALL", 57, 171}, + #endif + #ifdef PROV_R_LENGTH_TOO_LARGE + {"LENGTH_TOO_LARGE", ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE}, + #else + {"LENGTH_TOO_LARGE", 57, 202}, + #endif + #ifdef PROV_R_MISMATCHING_DOMAIN_PARAMETERS + {"MISMATCHING_DOMAIN_PARAMETERS", ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS}, + #else + {"MISMATCHING_DOMAIN_PARAMETERS", 57, 203}, + #endif + #ifdef PROV_R_MISSING_CEK_ALG + {"MISSING_CEK_ALG", ERR_LIB_PROV, PROV_R_MISSING_CEK_ALG}, + #else + {"MISSING_CEK_ALG", 57, 144}, + #endif + #ifdef PROV_R_MISSING_CIPHER + {"MISSING_CIPHER", ERR_LIB_PROV, PROV_R_MISSING_CIPHER}, + #else + {"MISSING_CIPHER", 57, 155}, + #endif + #ifdef PROV_R_MISSING_CONFIG_DATA + {"MISSING_CONFIG_DATA", ERR_LIB_PROV, PROV_R_MISSING_CONFIG_DATA}, + #else + {"MISSING_CONFIG_DATA", 57, 213}, + #endif + #ifdef PROV_R_MISSING_CONSTANT + {"MISSING_CONSTANT", ERR_LIB_PROV, PROV_R_MISSING_CONSTANT}, + #else + {"MISSING_CONSTANT", 57, 156}, + #endif + #ifdef PROV_R_MISSING_KEY + {"MISSING_KEY", ERR_LIB_PROV, PROV_R_MISSING_KEY}, + #else + {"MISSING_KEY", 57, 128}, + #endif + #ifdef PROV_R_MISSING_MAC + {"MISSING_MAC", ERR_LIB_PROV, PROV_R_MISSING_MAC}, + #else + {"MISSING_MAC", 57, 150}, + #endif + #ifdef PROV_R_MISSING_MESSAGE_DIGEST + {"MISSING_MESSAGE_DIGEST", ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST}, + #else + {"MISSING_MESSAGE_DIGEST", 57, 129}, + #endif + #ifdef PROV_R_MISSING_OID + {"MISSING_OID", ERR_LIB_PROV, PROV_R_MISSING_OID}, + #else + {"MISSING_OID", 57, 209}, + #endif + #ifdef PROV_R_MISSING_PASS + {"MISSING_PASS", ERR_LIB_PROV, PROV_R_MISSING_PASS}, + #else + {"MISSING_PASS", 57, 130}, + #endif + #ifdef PROV_R_MISSING_SALT + {"MISSING_SALT", ERR_LIB_PROV, PROV_R_MISSING_SALT}, + #else + {"MISSING_SALT", 57, 131}, + #endif + #ifdef PROV_R_MISSING_SECRET + {"MISSING_SECRET", ERR_LIB_PROV, PROV_R_MISSING_SECRET}, + #else + {"MISSING_SECRET", 57, 132}, + #endif + #ifdef PROV_R_MISSING_SEED + {"MISSING_SEED", ERR_LIB_PROV, PROV_R_MISSING_SEED}, + #else + {"MISSING_SEED", 57, 140}, + #endif + #ifdef PROV_R_MISSING_SESSION_ID + {"MISSING_SESSION_ID", ERR_LIB_PROV, PROV_R_MISSING_SESSION_ID}, + #else + {"MISSING_SESSION_ID", 57, 133}, + #endif + #ifdef PROV_R_MISSING_TYPE + {"MISSING_TYPE", ERR_LIB_PROV, PROV_R_MISSING_TYPE}, + #else + {"MISSING_TYPE", 57, 134}, + #endif + #ifdef PROV_R_MISSING_XCGHASH + {"MISSING_XCGHASH", ERR_LIB_PROV, PROV_R_MISSING_XCGHASH}, + #else + {"MISSING_XCGHASH", 57, 135}, + #endif + #ifdef PROV_R_ML_DSA_NO_FORMAT + {"ML_DSA_NO_FORMAT", ERR_LIB_PROV, PROV_R_ML_DSA_NO_FORMAT}, + #else + {"ML_DSA_NO_FORMAT", 57, 245}, + #endif + #ifdef PROV_R_ML_KEM_NO_FORMAT + {"ML_KEM_NO_FORMAT", ERR_LIB_PROV, PROV_R_ML_KEM_NO_FORMAT}, + #else + {"ML_KEM_NO_FORMAT", 57, 246}, + #endif + #ifdef PROV_R_MODULE_INTEGRITY_FAILURE + {"MODULE_INTEGRITY_FAILURE", ERR_LIB_PROV, PROV_R_MODULE_INTEGRITY_FAILURE}, + #else + {"MODULE_INTEGRITY_FAILURE", 57, 214}, + #endif + #ifdef PROV_R_NOT_A_PRIVATE_KEY + {"NOT_A_PRIVATE_KEY", ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY}, + #else + {"NOT_A_PRIVATE_KEY", 57, 221}, + #endif + #ifdef PROV_R_NOT_A_PUBLIC_KEY + {"NOT_A_PUBLIC_KEY", ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY}, + #else + {"NOT_A_PUBLIC_KEY", 57, 220}, + #endif + #ifdef PROV_R_NOT_INSTANTIATED + {"NOT_INSTANTIATED", ERR_LIB_PROV, PROV_R_NOT_INSTANTIATED}, + #else + {"NOT_INSTANTIATED", 57, 193}, + #endif + #ifdef PROV_R_NOT_PARAMETERS + {"NOT_PARAMETERS", ERR_LIB_PROV, PROV_R_NOT_PARAMETERS}, + #else + {"NOT_PARAMETERS", 57, 226}, + #endif + #ifdef PROV_R_NOT_SUPPORTED + {"NOT_SUPPORTED", ERR_LIB_PROV, PROV_R_NOT_SUPPORTED}, + #else + {"NOT_SUPPORTED", 57, 136}, + #endif + #ifdef PROV_R_NOT_XOF_OR_INVALID_LENGTH + {"NOT_XOF_OR_INVALID_LENGTH", ERR_LIB_PROV, PROV_R_NOT_XOF_OR_INVALID_LENGTH}, + #else + {"NOT_XOF_OR_INVALID_LENGTH", 57, 113}, + #endif + #ifdef PROV_R_NO_INSTANCE_ALLOWED + {"NO_INSTANCE_ALLOWED", ERR_LIB_PROV, PROV_R_NO_INSTANCE_ALLOWED}, + #else + {"NO_INSTANCE_ALLOWED", 57, 242}, + #endif + #ifdef PROV_R_NO_KEY_SET + {"NO_KEY_SET", ERR_LIB_PROV, PROV_R_NO_KEY_SET}, + #else + {"NO_KEY_SET", 57, 114}, + #endif + #ifdef PROV_R_NO_PARAMETERS_SET + {"NO_PARAMETERS_SET", ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET}, + #else + {"NO_PARAMETERS_SET", 57, 177}, + #endif + #ifdef PROV_R_NULL_LENGTH_POINTER + {"NULL_LENGTH_POINTER", ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER}, + #else + {"NULL_LENGTH_POINTER", 57, 247}, + #endif + #ifdef PROV_R_NULL_OUTPUT_BUFFER + {"NULL_OUTPUT_BUFFER", ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER}, + #else + {"NULL_OUTPUT_BUFFER", 57, 248}, + #endif + #ifdef PROV_R_ONESHOT_CALL_OUT_OF_ORDER + {"ONESHOT_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_ONESHOT_CALL_OUT_OF_ORDER}, + #else + {"ONESHOT_CALL_OUT_OF_ORDER", 57, 239}, + #endif + #ifdef PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", ERR_LIB_PROV, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE}, + #else + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", 57, 178}, + #endif + #ifdef PROV_R_OUTPUT_BUFFER_TOO_SMALL + {"OUTPUT_BUFFER_TOO_SMALL", ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL}, + #else + {"OUTPUT_BUFFER_TOO_SMALL", 57, 106}, + #endif + #ifdef PROV_R_PARENT_CANNOT_GENERATE_RANDOM_NUMBERS + {"PARENT_CANNOT_GENERATE_RANDOM_NUMBERS", ERR_LIB_PROV, PROV_R_PARENT_CANNOT_GENERATE_RANDOM_NUMBERS}, + #else + {"PARENT_CANNOT_GENERATE_RANDOM_NUMBERS", 57, 228}, + #endif + #ifdef PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED + {"PARENT_CANNOT_SUPPLY_ENTROPY_SEED", ERR_LIB_PROV, PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED}, + #else + {"PARENT_CANNOT_SUPPLY_ENTROPY_SEED", 57, 187}, + #endif + #ifdef PROV_R_PARENT_LOCKING_NOT_ENABLED + {"PARENT_LOCKING_NOT_ENABLED", ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED}, + #else + {"PARENT_LOCKING_NOT_ENABLED", 57, 182}, + #endif + #ifdef PROV_R_PARENT_STRENGTH_TOO_WEAK + {"PARENT_STRENGTH_TOO_WEAK", ERR_LIB_PROV, PROV_R_PARENT_STRENGTH_TOO_WEAK}, + #else + {"PARENT_STRENGTH_TOO_WEAK", 57, 194}, + #endif + #ifdef PROV_R_PATH_MUST_BE_ABSOLUTE + {"PATH_MUST_BE_ABSOLUTE", ERR_LIB_PROV, PROV_R_PATH_MUST_BE_ABSOLUTE}, + #else + {"PATH_MUST_BE_ABSOLUTE", 57, 219}, + #endif + #ifdef PROV_R_PERSONALISATION_STRING_TOO_LONG + {"PERSONALISATION_STRING_TOO_LONG", ERR_LIB_PROV, PROV_R_PERSONALISATION_STRING_TOO_LONG}, + #else + {"PERSONALISATION_STRING_TOO_LONG", 57, 195}, + #endif + #ifdef PROV_R_PSS_SALTLEN_TOO_SMALL + {"PSS_SALTLEN_TOO_SMALL", ERR_LIB_PROV, PROV_R_PSS_SALTLEN_TOO_SMALL}, + #else + {"PSS_SALTLEN_TOO_SMALL", 57, 172}, + #endif + #ifdef PROV_R_REPEATED_PARAMETER + {"REPEATED_PARAMETER", ERR_LIB_PROV, PROV_R_REPEATED_PARAMETER}, + #else + {"REPEATED_PARAMETER", 57, 252}, + #endif + #ifdef PROV_R_REQUEST_TOO_LARGE_FOR_DRBG + {"REQUEST_TOO_LARGE_FOR_DRBG", ERR_LIB_PROV, PROV_R_REQUEST_TOO_LARGE_FOR_DRBG}, + #else + {"REQUEST_TOO_LARGE_FOR_DRBG", 57, 196}, + #endif + #ifdef PROV_R_REQUIRE_CTR_MODE_CIPHER + {"REQUIRE_CTR_MODE_CIPHER", ERR_LIB_PROV, PROV_R_REQUIRE_CTR_MODE_CIPHER}, + #else + {"REQUIRE_CTR_MODE_CIPHER", 57, 206}, + #endif + #ifdef PROV_R_RESEED_ERROR + {"RESEED_ERROR", ERR_LIB_PROV, PROV_R_RESEED_ERROR}, + #else + {"RESEED_ERROR", 57, 197}, + #endif + #ifdef PROV_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES + {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", ERR_LIB_PROV, PROV_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES}, + #else + {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", 57, 222}, + #endif + #ifdef PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT + {"SEED_SOURCES_MUST_NOT_HAVE_A_PARENT", ERR_LIB_PROV, PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT}, + #else + {"SEED_SOURCES_MUST_NOT_HAVE_A_PARENT", 57, 229}, + #endif + #ifdef PROV_R_SELF_TEST_KAT_FAILURE + {"SELF_TEST_KAT_FAILURE", ERR_LIB_PROV, PROV_R_SELF_TEST_KAT_FAILURE}, + #else + {"SELF_TEST_KAT_FAILURE", 57, 215}, + #endif + #ifdef PROV_R_SELF_TEST_POST_FAILURE + {"SELF_TEST_POST_FAILURE", ERR_LIB_PROV, PROV_R_SELF_TEST_POST_FAILURE}, + #else + {"SELF_TEST_POST_FAILURE", 57, 216}, + #endif + #ifdef PROV_R_TAG_NOT_NEEDED + {"TAG_NOT_NEEDED", ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED}, + #else + {"TAG_NOT_NEEDED", 57, 120}, + #endif + #ifdef PROV_R_TAG_NOT_SET + {"TAG_NOT_SET", ERR_LIB_PROV, PROV_R_TAG_NOT_SET}, + #else + {"TAG_NOT_SET", 57, 119}, + #endif + #ifdef PROV_R_TOO_MANY_RECORDS + {"TOO_MANY_RECORDS", ERR_LIB_PROV, PROV_R_TOO_MANY_RECORDS}, + #else + {"TOO_MANY_RECORDS", 57, 126}, + #endif + #ifdef PROV_R_UNABLE_TO_FIND_CIPHERS + {"UNABLE_TO_FIND_CIPHERS", ERR_LIB_PROV, PROV_R_UNABLE_TO_FIND_CIPHERS}, + #else + {"UNABLE_TO_FIND_CIPHERS", 57, 207}, + #endif + #ifdef PROV_R_UNABLE_TO_GET_PARENT_STRENGTH + {"UNABLE_TO_GET_PARENT_STRENGTH", ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PARENT_STRENGTH}, + #else + {"UNABLE_TO_GET_PARENT_STRENGTH", 57, 199}, + #endif + #ifdef PROV_R_UNABLE_TO_GET_PASSPHRASE + {"UNABLE_TO_GET_PASSPHRASE", ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PASSPHRASE}, + #else + {"UNABLE_TO_GET_PASSPHRASE", 57, 159}, + #endif + #ifdef PROV_R_UNABLE_TO_INITIALISE_CIPHERS + {"UNABLE_TO_INITIALISE_CIPHERS", ERR_LIB_PROV, PROV_R_UNABLE_TO_INITIALISE_CIPHERS}, + #else + {"UNABLE_TO_INITIALISE_CIPHERS", 57, 208}, + #endif + #ifdef PROV_R_UNABLE_TO_LOAD_SHA256 + {"UNABLE_TO_LOAD_SHA256", ERR_LIB_PROV, PROV_R_UNABLE_TO_LOAD_SHA256}, + #else + {"UNABLE_TO_LOAD_SHA256", 57, 147}, + #endif + #ifdef PROV_R_UNABLE_TO_LOCK_PARENT + {"UNABLE_TO_LOCK_PARENT", ERR_LIB_PROV, PROV_R_UNABLE_TO_LOCK_PARENT}, + #else + {"UNABLE_TO_LOCK_PARENT", 57, 201}, + #endif + #ifdef PROV_R_UNABLE_TO_RESEED + {"UNABLE_TO_RESEED", ERR_LIB_PROV, PROV_R_UNABLE_TO_RESEED}, + #else + {"UNABLE_TO_RESEED", 57, 204}, + #endif + #ifdef PROV_R_UNEXPECTED_KEY_PARAMETERS + {"UNEXPECTED_KEY_PARAMETERS", ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS}, + #else + {"UNEXPECTED_KEY_PARAMETERS", 57, 249}, + #endif + #ifdef PROV_R_UNSUPPORTED_CEK_ALG + {"UNSUPPORTED_CEK_ALG", ERR_LIB_PROV, PROV_R_UNSUPPORTED_CEK_ALG}, + #else + {"UNSUPPORTED_CEK_ALG", 57, 145}, + #endif + #ifdef PROV_R_UNSUPPORTED_KEY_SIZE + {"UNSUPPORTED_KEY_SIZE", ERR_LIB_PROV, PROV_R_UNSUPPORTED_KEY_SIZE}, + #else + {"UNSUPPORTED_KEY_SIZE", 57, 153}, + #endif + #ifdef PROV_R_UNSUPPORTED_MAC_TYPE + {"UNSUPPORTED_MAC_TYPE", ERR_LIB_PROV, PROV_R_UNSUPPORTED_MAC_TYPE}, + #else + {"UNSUPPORTED_MAC_TYPE", 57, 137}, + #endif + #ifdef PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS + {"UNSUPPORTED_NUMBER_OF_ROUNDS", ERR_LIB_PROV, PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS}, + #else + {"UNSUPPORTED_NUMBER_OF_ROUNDS", 57, 152}, + #endif + #ifdef PROV_R_UNSUPPORTED_SELECTION + {"UNSUPPORTED_SELECTION", ERR_LIB_PROV, PROV_R_UNSUPPORTED_SELECTION}, + #else + {"UNSUPPORTED_SELECTION", 57, 250}, + #endif + #ifdef PROV_R_UPDATE_CALL_OUT_OF_ORDER + {"UPDATE_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER}, + #else + {"UPDATE_CALL_OUT_OF_ORDER", 57, 240}, + #endif + #ifdef PROV_R_URI_AUTHORITY_UNSUPPORTED + {"URI_AUTHORITY_UNSUPPORTED", ERR_LIB_PROV, PROV_R_URI_AUTHORITY_UNSUPPORTED}, + #else + {"URI_AUTHORITY_UNSUPPORTED", 57, 223}, + #endif + #ifdef PROV_R_VALUE_ERROR + {"VALUE_ERROR", ERR_LIB_PROV, PROV_R_VALUE_ERROR}, + #else + {"VALUE_ERROR", 57, 138}, + #endif + #ifdef PROV_R_WRONG_CIPHERTEXT_SIZE + {"WRONG_CIPHERTEXT_SIZE", ERR_LIB_PROV, PROV_R_WRONG_CIPHERTEXT_SIZE}, + #else + {"WRONG_CIPHERTEXT_SIZE", 57, 251}, + #endif + #ifdef PROV_R_WRONG_FINAL_BLOCK_LENGTH + {"WRONG_FINAL_BLOCK_LENGTH", ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH}, + #else + {"WRONG_FINAL_BLOCK_LENGTH", 57, 107}, + #endif + #ifdef PROV_R_WRONG_OUTPUT_BUFFER_SIZE + {"WRONG_OUTPUT_BUFFER_SIZE", ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE}, + #else + {"WRONG_OUTPUT_BUFFER_SIZE", 57, 139}, + #endif + #ifdef PROV_R_XOF_DIGESTS_NOT_ALLOWED + {"XOF_DIGESTS_NOT_ALLOWED", ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED}, + #else + {"XOF_DIGESTS_NOT_ALLOWED", 57, 183}, + #endif + #ifdef PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE + {"XTS_DATA_UNIT_IS_TOO_LARGE", ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE}, + #else + {"XTS_DATA_UNIT_IS_TOO_LARGE", 57, 148}, + #endif + #ifdef PROV_R_XTS_DUPLICATED_KEYS + {"XTS_DUPLICATED_KEYS", ERR_LIB_PROV, PROV_R_XTS_DUPLICATED_KEYS}, + #else + {"XTS_DUPLICATED_KEYS", 57, 149}, + #endif + #ifdef RAND_R_ADDITIONAL_INPUT_TOO_LONG + {"ADDITIONAL_INPUT_TOO_LONG", ERR_LIB_RAND, RAND_R_ADDITIONAL_INPUT_TOO_LONG}, + #else + {"ADDITIONAL_INPUT_TOO_LONG", 36, 102}, + #endif + #ifdef RAND_R_ALREADY_INSTANTIATED + {"ALREADY_INSTANTIATED", ERR_LIB_RAND, RAND_R_ALREADY_INSTANTIATED}, + #else + {"ALREADY_INSTANTIATED", 36, 103}, + #endif + #ifdef RAND_R_ARGUMENT_OUT_OF_RANGE + {"ARGUMENT_OUT_OF_RANGE", ERR_LIB_RAND, RAND_R_ARGUMENT_OUT_OF_RANGE}, + #else + {"ARGUMENT_OUT_OF_RANGE", 36, 105}, + #endif + #ifdef RAND_R_CANNOT_OPEN_FILE + {"CANNOT_OPEN_FILE", ERR_LIB_RAND, RAND_R_CANNOT_OPEN_FILE}, + #else + {"CANNOT_OPEN_FILE", 36, 121}, + #endif + #ifdef RAND_R_DRBG_ALREADY_INITIALIZED + {"DRBG_ALREADY_INITIALIZED", ERR_LIB_RAND, RAND_R_DRBG_ALREADY_INITIALIZED}, + #else + {"DRBG_ALREADY_INITIALIZED", 36, 129}, + #endif + #ifdef RAND_R_DRBG_NOT_INITIALISED + {"DRBG_NOT_INITIALISED", ERR_LIB_RAND, RAND_R_DRBG_NOT_INITIALISED}, + #else + {"DRBG_NOT_INITIALISED", 36, 104}, + #endif + #ifdef RAND_R_ENTROPY_INPUT_TOO_LONG + {"ENTROPY_INPUT_TOO_LONG", ERR_LIB_RAND, RAND_R_ENTROPY_INPUT_TOO_LONG}, + #else + {"ENTROPY_INPUT_TOO_LONG", 36, 106}, + #endif + #ifdef RAND_R_ENTROPY_OUT_OF_RANGE + {"ENTROPY_OUT_OF_RANGE", ERR_LIB_RAND, RAND_R_ENTROPY_OUT_OF_RANGE}, + #else + {"ENTROPY_OUT_OF_RANGE", 36, 124}, + #endif + #ifdef RAND_R_ERROR_ENTROPY_POOL_WAS_IGNORED + {"ERROR_ENTROPY_POOL_WAS_IGNORED", ERR_LIB_RAND, RAND_R_ERROR_ENTROPY_POOL_WAS_IGNORED}, + #else + {"ERROR_ENTROPY_POOL_WAS_IGNORED", 36, 127}, + #endif + #ifdef RAND_R_ERROR_INITIALISING_DRBG + {"ERROR_INITIALISING_DRBG", ERR_LIB_RAND, RAND_R_ERROR_INITIALISING_DRBG}, + #else + {"ERROR_INITIALISING_DRBG", 36, 107}, + #endif + #ifdef RAND_R_ERROR_INSTANTIATING_DRBG + {"ERROR_INSTANTIATING_DRBG", ERR_LIB_RAND, RAND_R_ERROR_INSTANTIATING_DRBG}, + #else + {"ERROR_INSTANTIATING_DRBG", 36, 108}, + #endif + #ifdef RAND_R_ERROR_RETRIEVING_ADDITIONAL_INPUT + {"ERROR_RETRIEVING_ADDITIONAL_INPUT", ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ADDITIONAL_INPUT}, + #else + {"ERROR_RETRIEVING_ADDITIONAL_INPUT", 36, 109}, + #endif + #ifdef RAND_R_ERROR_RETRIEVING_ENTROPY + {"ERROR_RETRIEVING_ENTROPY", ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ENTROPY}, + #else + {"ERROR_RETRIEVING_ENTROPY", 36, 110}, + #endif + #ifdef RAND_R_ERROR_RETRIEVING_NONCE + {"ERROR_RETRIEVING_NONCE", ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_NONCE}, + #else + {"ERROR_RETRIEVING_NONCE", 36, 111}, + #endif + #ifdef RAND_R_FAILED_TO_CREATE_LOCK + {"FAILED_TO_CREATE_LOCK", ERR_LIB_RAND, RAND_R_FAILED_TO_CREATE_LOCK}, + #else + {"FAILED_TO_CREATE_LOCK", 36, 126}, + #endif + #ifdef RAND_R_FUNC_NOT_IMPLEMENTED + {"FUNC_NOT_IMPLEMENTED", ERR_LIB_RAND, RAND_R_FUNC_NOT_IMPLEMENTED}, + #else + {"FUNC_NOT_IMPLEMENTED", 36, 101}, + #endif + #ifdef RAND_R_FWRITE_ERROR + {"FWRITE_ERROR", ERR_LIB_RAND, RAND_R_FWRITE_ERROR}, + #else + {"FWRITE_ERROR", 36, 123}, + #endif + #ifdef RAND_R_GENERATE_ERROR + {"GENERATE_ERROR", ERR_LIB_RAND, RAND_R_GENERATE_ERROR}, + #else + {"GENERATE_ERROR", 36, 112}, + #endif + #ifdef RAND_R_INSUFFICIENT_DRBG_STRENGTH + {"INSUFFICIENT_DRBG_STRENGTH", ERR_LIB_RAND, RAND_R_INSUFFICIENT_DRBG_STRENGTH}, + #else + {"INSUFFICIENT_DRBG_STRENGTH", 36, 139}, + #endif + #ifdef RAND_R_INTERNAL_ERROR + {"INTERNAL_ERROR", ERR_LIB_RAND, RAND_R_INTERNAL_ERROR}, + #else + {"INTERNAL_ERROR", 36, 113}, + #endif + #ifdef RAND_R_INVALID_PROPERTY_QUERY + {"INVALID_PROPERTY_QUERY", ERR_LIB_RAND, RAND_R_INVALID_PROPERTY_QUERY}, + #else + {"INVALID_PROPERTY_QUERY", 36, 137}, + #endif + #ifdef RAND_R_IN_ERROR_STATE + {"IN_ERROR_STATE", ERR_LIB_RAND, RAND_R_IN_ERROR_STATE}, + #else + {"IN_ERROR_STATE", 36, 114}, + #endif + #ifdef RAND_R_NOT_A_REGULAR_FILE + {"NOT_A_REGULAR_FILE", ERR_LIB_RAND, RAND_R_NOT_A_REGULAR_FILE}, + #else + {"NOT_A_REGULAR_FILE", 36, 122}, + #endif + #ifdef RAND_R_NOT_INSTANTIATED + {"NOT_INSTANTIATED", ERR_LIB_RAND, RAND_R_NOT_INSTANTIATED}, + #else + {"NOT_INSTANTIATED", 36, 115}, + #endif + #ifdef RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED + {"NO_DRBG_IMPLEMENTATION_SELECTED", ERR_LIB_RAND, RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED}, + #else + {"NO_DRBG_IMPLEMENTATION_SELECTED", 36, 128}, + #endif + #ifdef RAND_R_PARENT_LOCKING_NOT_ENABLED + {"PARENT_LOCKING_NOT_ENABLED", ERR_LIB_RAND, RAND_R_PARENT_LOCKING_NOT_ENABLED}, + #else + {"PARENT_LOCKING_NOT_ENABLED", 36, 130}, + #endif + #ifdef RAND_R_PARENT_STRENGTH_TOO_WEAK + {"PARENT_STRENGTH_TOO_WEAK", ERR_LIB_RAND, RAND_R_PARENT_STRENGTH_TOO_WEAK}, + #else + {"PARENT_STRENGTH_TOO_WEAK", 36, 131}, + #endif + #ifdef RAND_R_PERSONALISATION_STRING_TOO_LONG + {"PERSONALISATION_STRING_TOO_LONG", ERR_LIB_RAND, RAND_R_PERSONALISATION_STRING_TOO_LONG}, + #else + {"PERSONALISATION_STRING_TOO_LONG", 36, 116}, + #endif + #ifdef RAND_R_PREDICTION_RESISTANCE_NOT_SUPPORTED + {"PREDICTION_RESISTANCE_NOT_SUPPORTED", ERR_LIB_RAND, RAND_R_PREDICTION_RESISTANCE_NOT_SUPPORTED}, + #else + {"PREDICTION_RESISTANCE_NOT_SUPPORTED", 36, 133}, + #endif + #ifdef RAND_R_PRNG_NOT_SEEDED + {"PRNG_NOT_SEEDED", ERR_LIB_RAND, RAND_R_PRNG_NOT_SEEDED}, + #else + {"PRNG_NOT_SEEDED", 36, 100}, + #endif + #ifdef RAND_R_RANDOM_POOL_IS_EMPTY + {"RANDOM_POOL_IS_EMPTY", ERR_LIB_RAND, RAND_R_RANDOM_POOL_IS_EMPTY}, + #else + {"RANDOM_POOL_IS_EMPTY", 36, 142}, + #endif + #ifdef RAND_R_RANDOM_POOL_OVERFLOW + {"RANDOM_POOL_OVERFLOW", ERR_LIB_RAND, RAND_R_RANDOM_POOL_OVERFLOW}, + #else + {"RANDOM_POOL_OVERFLOW", 36, 125}, + #endif + #ifdef RAND_R_RANDOM_POOL_UNDERFLOW + {"RANDOM_POOL_UNDERFLOW", ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW}, + #else + {"RANDOM_POOL_UNDERFLOW", 36, 134}, + #endif + #ifdef RAND_R_REQUEST_TOO_LARGE_FOR_DRBG + {"REQUEST_TOO_LARGE_FOR_DRBG", ERR_LIB_RAND, RAND_R_REQUEST_TOO_LARGE_FOR_DRBG}, + #else + {"REQUEST_TOO_LARGE_FOR_DRBG", 36, 117}, + #endif + #ifdef RAND_R_RESEED_ERROR + {"RESEED_ERROR", ERR_LIB_RAND, RAND_R_RESEED_ERROR}, + #else + {"RESEED_ERROR", 36, 118}, + #endif + #ifdef RAND_R_SELFTEST_FAILURE + {"SELFTEST_FAILURE", ERR_LIB_RAND, RAND_R_SELFTEST_FAILURE}, + #else + {"SELFTEST_FAILURE", 36, 119}, + #endif + #ifdef RAND_R_TOO_LITTLE_NONCE_REQUESTED + {"TOO_LITTLE_NONCE_REQUESTED", ERR_LIB_RAND, RAND_R_TOO_LITTLE_NONCE_REQUESTED}, + #else + {"TOO_LITTLE_NONCE_REQUESTED", 36, 135}, + #endif + #ifdef RAND_R_TOO_MUCH_NONCE_REQUESTED + {"TOO_MUCH_NONCE_REQUESTED", ERR_LIB_RAND, RAND_R_TOO_MUCH_NONCE_REQUESTED}, + #else + {"TOO_MUCH_NONCE_REQUESTED", 36, 136}, + #endif + #ifdef RAND_R_UNABLE_TO_CREATE_DRBG + {"UNABLE_TO_CREATE_DRBG", ERR_LIB_RAND, RAND_R_UNABLE_TO_CREATE_DRBG}, + #else + {"UNABLE_TO_CREATE_DRBG", 36, 143}, + #endif + #ifdef RAND_R_UNABLE_TO_FETCH_DRBG + {"UNABLE_TO_FETCH_DRBG", ERR_LIB_RAND, RAND_R_UNABLE_TO_FETCH_DRBG}, + #else + {"UNABLE_TO_FETCH_DRBG", 36, 144}, + #endif + #ifdef RAND_R_UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER + {"UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER", ERR_LIB_RAND, RAND_R_UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER}, + #else + {"UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER", 36, 141}, + #endif + #ifdef RAND_R_UNABLE_TO_GET_PARENT_STRENGTH + {"UNABLE_TO_GET_PARENT_STRENGTH", ERR_LIB_RAND, RAND_R_UNABLE_TO_GET_PARENT_STRENGTH}, + #else + {"UNABLE_TO_GET_PARENT_STRENGTH", 36, 138}, + #endif + #ifdef RAND_R_UNABLE_TO_LOCK_PARENT + {"UNABLE_TO_LOCK_PARENT", ERR_LIB_RAND, RAND_R_UNABLE_TO_LOCK_PARENT}, + #else + {"UNABLE_TO_LOCK_PARENT", 36, 140}, + #endif + #ifdef RAND_R_UNSUPPORTED_DRBG_FLAGS + {"UNSUPPORTED_DRBG_FLAGS", ERR_LIB_RAND, RAND_R_UNSUPPORTED_DRBG_FLAGS}, + #else + {"UNSUPPORTED_DRBG_FLAGS", 36, 132}, + #endif + #ifdef RAND_R_UNSUPPORTED_DRBG_TYPE + {"UNSUPPORTED_DRBG_TYPE", ERR_LIB_RAND, RAND_R_UNSUPPORTED_DRBG_TYPE}, + #else + {"UNSUPPORTED_DRBG_TYPE", 36, 120}, + #endif + #ifdef RSA_R_ALGORITHM_MISMATCH + {"ALGORITHM_MISMATCH", ERR_LIB_RSA, RSA_R_ALGORITHM_MISMATCH}, + #else + {"ALGORITHM_MISMATCH", 4, 100}, + #endif + #ifdef RSA_R_BAD_E_VALUE + {"BAD_E_VALUE", ERR_LIB_RSA, RSA_R_BAD_E_VALUE}, + #else + {"BAD_E_VALUE", 4, 101}, + #endif + #ifdef RSA_R_BAD_FIXED_HEADER_DECRYPT + {"BAD_FIXED_HEADER_DECRYPT", ERR_LIB_RSA, RSA_R_BAD_FIXED_HEADER_DECRYPT}, + #else + {"BAD_FIXED_HEADER_DECRYPT", 4, 102}, + #endif + #ifdef RSA_R_BAD_PAD_BYTE_COUNT + {"BAD_PAD_BYTE_COUNT", ERR_LIB_RSA, RSA_R_BAD_PAD_BYTE_COUNT}, + #else + {"BAD_PAD_BYTE_COUNT", 4, 103}, + #endif + #ifdef RSA_R_BAD_SIGNATURE + {"BAD_SIGNATURE", ERR_LIB_RSA, RSA_R_BAD_SIGNATURE}, + #else + {"BAD_SIGNATURE", 4, 104}, + #endif + #ifdef RSA_R_BLOCK_TYPE_IS_NOT_01 + {"BLOCK_TYPE_IS_NOT_01", ERR_LIB_RSA, RSA_R_BLOCK_TYPE_IS_NOT_01}, + #else + {"BLOCK_TYPE_IS_NOT_01", 4, 106}, + #endif + #ifdef RSA_R_BLOCK_TYPE_IS_NOT_02 + {"BLOCK_TYPE_IS_NOT_02", ERR_LIB_RSA, RSA_R_BLOCK_TYPE_IS_NOT_02}, + #else + {"BLOCK_TYPE_IS_NOT_02", 4, 107}, + #endif + #ifdef RSA_R_DATA_GREATER_THAN_MOD_LEN + {"DATA_GREATER_THAN_MOD_LEN", ERR_LIB_RSA, RSA_R_DATA_GREATER_THAN_MOD_LEN}, + #else + {"DATA_GREATER_THAN_MOD_LEN", 4, 108}, + #endif + #ifdef RSA_R_DATA_TOO_LARGE + {"DATA_TOO_LARGE", ERR_LIB_RSA, RSA_R_DATA_TOO_LARGE}, + #else + {"DATA_TOO_LARGE", 4, 109}, + #endif + #ifdef RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE + {"DATA_TOO_LARGE_FOR_KEY_SIZE", ERR_LIB_RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE}, + #else + {"DATA_TOO_LARGE_FOR_KEY_SIZE", 4, 110}, + #endif + #ifdef RSA_R_DATA_TOO_LARGE_FOR_MODULUS + {"DATA_TOO_LARGE_FOR_MODULUS", ERR_LIB_RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS}, + #else + {"DATA_TOO_LARGE_FOR_MODULUS", 4, 132}, + #endif + #ifdef RSA_R_DATA_TOO_SMALL + {"DATA_TOO_SMALL", ERR_LIB_RSA, RSA_R_DATA_TOO_SMALL}, + #else + {"DATA_TOO_SMALL", 4, 111}, + #endif + #ifdef RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE + {"DATA_TOO_SMALL_FOR_KEY_SIZE", ERR_LIB_RSA, RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE}, + #else + {"DATA_TOO_SMALL_FOR_KEY_SIZE", 4, 122}, + #endif + #ifdef RSA_R_DIGEST_DOES_NOT_MATCH + {"DIGEST_DOES_NOT_MATCH", ERR_LIB_RSA, RSA_R_DIGEST_DOES_NOT_MATCH}, + #else + {"DIGEST_DOES_NOT_MATCH", 4, 158}, + #endif + #ifdef RSA_R_DIGEST_NOT_ALLOWED + {"DIGEST_NOT_ALLOWED", ERR_LIB_RSA, RSA_R_DIGEST_NOT_ALLOWED}, + #else + {"DIGEST_NOT_ALLOWED", 4, 145}, + #endif + #ifdef RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY + {"DIGEST_TOO_BIG_FOR_RSA_KEY", ERR_LIB_RSA, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY}, + #else + {"DIGEST_TOO_BIG_FOR_RSA_KEY", 4, 112}, + #endif + #ifdef RSA_R_DMP1_NOT_CONGRUENT_TO_D + {"DMP1_NOT_CONGRUENT_TO_D", ERR_LIB_RSA, RSA_R_DMP1_NOT_CONGRUENT_TO_D}, + #else + {"DMP1_NOT_CONGRUENT_TO_D", 4, 124}, + #endif + #ifdef RSA_R_DMQ1_NOT_CONGRUENT_TO_D + {"DMQ1_NOT_CONGRUENT_TO_D", ERR_LIB_RSA, RSA_R_DMQ1_NOT_CONGRUENT_TO_D}, + #else + {"DMQ1_NOT_CONGRUENT_TO_D", 4, 125}, + #endif + #ifdef RSA_R_D_E_NOT_CONGRUENT_TO_1 + {"D_E_NOT_CONGRUENT_TO_1", ERR_LIB_RSA, RSA_R_D_E_NOT_CONGRUENT_TO_1}, + #else + {"D_E_NOT_CONGRUENT_TO_1", 4, 123}, + #endif + #ifdef RSA_R_FIRST_OCTET_INVALID + {"FIRST_OCTET_INVALID", ERR_LIB_RSA, RSA_R_FIRST_OCTET_INVALID}, + #else + {"FIRST_OCTET_INVALID", 4, 133}, + #endif + #ifdef RSA_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE + {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", ERR_LIB_RSA, RSA_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE}, + #else + {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", 4, 144}, + #endif + #ifdef RSA_R_INVALID_DIGEST + {"INVALID_DIGEST", ERR_LIB_RSA, RSA_R_INVALID_DIGEST}, + #else + {"INVALID_DIGEST", 4, 157}, + #endif + #ifdef RSA_R_INVALID_DIGEST_LENGTH + {"INVALID_DIGEST_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_DIGEST_LENGTH}, + #else + {"INVALID_DIGEST_LENGTH", 4, 143}, + #endif + #ifdef RSA_R_INVALID_HEADER + {"INVALID_HEADER", ERR_LIB_RSA, RSA_R_INVALID_HEADER}, + #else + {"INVALID_HEADER", 4, 137}, + #endif + #ifdef RSA_R_INVALID_KEYPAIR + {"INVALID_KEYPAIR", ERR_LIB_RSA, RSA_R_INVALID_KEYPAIR}, + #else + {"INVALID_KEYPAIR", 4, 171}, + #endif + #ifdef RSA_R_INVALID_KEY_LENGTH + {"INVALID_KEY_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_KEY_LENGTH}, + #else + {"INVALID_KEY_LENGTH", 4, 173}, + #endif + #ifdef RSA_R_INVALID_LABEL + {"INVALID_LABEL", ERR_LIB_RSA, RSA_R_INVALID_LABEL}, + #else + {"INVALID_LABEL", 4, 160}, + #endif + #ifdef RSA_R_INVALID_LENGTH + {"INVALID_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_LENGTH}, + #else + {"INVALID_LENGTH", 4, 181}, + #endif + #ifdef RSA_R_INVALID_MESSAGE_LENGTH + {"INVALID_MESSAGE_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_MESSAGE_LENGTH}, + #else + {"INVALID_MESSAGE_LENGTH", 4, 131}, + #endif + #ifdef RSA_R_INVALID_MGF1_MD + {"INVALID_MGF1_MD", ERR_LIB_RSA, RSA_R_INVALID_MGF1_MD}, + #else + {"INVALID_MGF1_MD", 4, 156}, + #endif + #ifdef RSA_R_INVALID_MODULUS + {"INVALID_MODULUS", ERR_LIB_RSA, RSA_R_INVALID_MODULUS}, + #else + {"INVALID_MODULUS", 4, 174}, + #endif + #ifdef RSA_R_INVALID_MULTI_PRIME_KEY + {"INVALID_MULTI_PRIME_KEY", ERR_LIB_RSA, RSA_R_INVALID_MULTI_PRIME_KEY}, + #else + {"INVALID_MULTI_PRIME_KEY", 4, 167}, + #endif + #ifdef RSA_R_INVALID_OAEP_PARAMETERS + {"INVALID_OAEP_PARAMETERS", ERR_LIB_RSA, RSA_R_INVALID_OAEP_PARAMETERS}, + #else + {"INVALID_OAEP_PARAMETERS", 4, 161}, + #endif + #ifdef RSA_R_INVALID_PADDING + {"INVALID_PADDING", ERR_LIB_RSA, RSA_R_INVALID_PADDING}, + #else + {"INVALID_PADDING", 4, 138}, + #endif + #ifdef RSA_R_INVALID_PADDING_MODE + {"INVALID_PADDING_MODE", ERR_LIB_RSA, RSA_R_INVALID_PADDING_MODE}, + #else + {"INVALID_PADDING_MODE", 4, 141}, + #endif + #ifdef RSA_R_INVALID_PSS_PARAMETERS + {"INVALID_PSS_PARAMETERS", ERR_LIB_RSA, RSA_R_INVALID_PSS_PARAMETERS}, + #else + {"INVALID_PSS_PARAMETERS", 4, 149}, + #endif + #ifdef RSA_R_INVALID_PSS_SALTLEN + {"INVALID_PSS_SALTLEN", ERR_LIB_RSA, RSA_R_INVALID_PSS_SALTLEN}, + #else + {"INVALID_PSS_SALTLEN", 4, 146}, + #endif + #ifdef RSA_R_INVALID_REQUEST + {"INVALID_REQUEST", ERR_LIB_RSA, RSA_R_INVALID_REQUEST}, + #else + {"INVALID_REQUEST", 4, 175}, + #endif + #ifdef RSA_R_INVALID_SALT_LENGTH + {"INVALID_SALT_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_SALT_LENGTH}, + #else + {"INVALID_SALT_LENGTH", 4, 150}, + #endif + #ifdef RSA_R_INVALID_STRENGTH + {"INVALID_STRENGTH", ERR_LIB_RSA, RSA_R_INVALID_STRENGTH}, + #else + {"INVALID_STRENGTH", 4, 176}, + #endif + #ifdef RSA_R_INVALID_TRAILER + {"INVALID_TRAILER", ERR_LIB_RSA, RSA_R_INVALID_TRAILER}, + #else + {"INVALID_TRAILER", 4, 139}, + #endif + #ifdef RSA_R_INVALID_X931_DIGEST + {"INVALID_X931_DIGEST", ERR_LIB_RSA, RSA_R_INVALID_X931_DIGEST}, + #else + {"INVALID_X931_DIGEST", 4, 142}, + #endif + #ifdef RSA_R_IQMP_NOT_INVERSE_OF_Q + {"IQMP_NOT_INVERSE_OF_Q", ERR_LIB_RSA, RSA_R_IQMP_NOT_INVERSE_OF_Q}, + #else + {"IQMP_NOT_INVERSE_OF_Q", 4, 126}, + #endif + #ifdef RSA_R_KEY_PRIME_NUM_INVALID + {"KEY_PRIME_NUM_INVALID", ERR_LIB_RSA, RSA_R_KEY_PRIME_NUM_INVALID}, + #else + {"KEY_PRIME_NUM_INVALID", 4, 165}, + #endif + #ifdef RSA_R_KEY_SIZE_TOO_SMALL + {"KEY_SIZE_TOO_SMALL", ERR_LIB_RSA, RSA_R_KEY_SIZE_TOO_SMALL}, + #else + {"KEY_SIZE_TOO_SMALL", 4, 120}, + #endif + #ifdef RSA_R_LAST_OCTET_INVALID + {"LAST_OCTET_INVALID", ERR_LIB_RSA, RSA_R_LAST_OCTET_INVALID}, + #else + {"LAST_OCTET_INVALID", 4, 134}, + #endif + #ifdef RSA_R_MGF1_DIGEST_NOT_ALLOWED + {"MGF1_DIGEST_NOT_ALLOWED", ERR_LIB_RSA, RSA_R_MGF1_DIGEST_NOT_ALLOWED}, + #else + {"MGF1_DIGEST_NOT_ALLOWED", 4, 152}, + #endif + #ifdef RSA_R_MISSING_PRIVATE_KEY + {"MISSING_PRIVATE_KEY", ERR_LIB_RSA, RSA_R_MISSING_PRIVATE_KEY}, + #else + {"MISSING_PRIVATE_KEY", 4, 179}, + #endif + #ifdef RSA_R_MODULUS_TOO_LARGE + {"MODULUS_TOO_LARGE", ERR_LIB_RSA, RSA_R_MODULUS_TOO_LARGE}, + #else + {"MODULUS_TOO_LARGE", 4, 105}, + #endif + #ifdef RSA_R_MP_COEFFICIENT_NOT_INVERSE_OF_R + {"MP_COEFFICIENT_NOT_INVERSE_OF_R", ERR_LIB_RSA, RSA_R_MP_COEFFICIENT_NOT_INVERSE_OF_R}, + #else + {"MP_COEFFICIENT_NOT_INVERSE_OF_R", 4, 168}, + #endif + #ifdef RSA_R_MP_EXPONENT_NOT_CONGRUENT_TO_D + {"MP_EXPONENT_NOT_CONGRUENT_TO_D", ERR_LIB_RSA, RSA_R_MP_EXPONENT_NOT_CONGRUENT_TO_D}, + #else + {"MP_EXPONENT_NOT_CONGRUENT_TO_D", 4, 169}, + #endif + #ifdef RSA_R_MP_R_NOT_PRIME + {"MP_R_NOT_PRIME", ERR_LIB_RSA, RSA_R_MP_R_NOT_PRIME}, + #else + {"MP_R_NOT_PRIME", 4, 170}, + #endif + #ifdef RSA_R_NO_PUBLIC_EXPONENT + {"NO_PUBLIC_EXPONENT", ERR_LIB_RSA, RSA_R_NO_PUBLIC_EXPONENT}, + #else + {"NO_PUBLIC_EXPONENT", 4, 140}, + #endif + #ifdef RSA_R_NULL_BEFORE_BLOCK_MISSING + {"NULL_BEFORE_BLOCK_MISSING", ERR_LIB_RSA, RSA_R_NULL_BEFORE_BLOCK_MISSING}, + #else + {"NULL_BEFORE_BLOCK_MISSING", 4, 113}, + #endif + #ifdef RSA_R_N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES + {"N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES", ERR_LIB_RSA, RSA_R_N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES}, + #else + {"N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES", 4, 172}, + #endif + #ifdef RSA_R_N_DOES_NOT_EQUAL_P_Q + {"N_DOES_NOT_EQUAL_P_Q", ERR_LIB_RSA, RSA_R_N_DOES_NOT_EQUAL_P_Q}, + #else + {"N_DOES_NOT_EQUAL_P_Q", 4, 127}, + #endif + #ifdef RSA_R_OAEP_DECODING_ERROR + {"OAEP_DECODING_ERROR", ERR_LIB_RSA, RSA_R_OAEP_DECODING_ERROR}, + #else + {"OAEP_DECODING_ERROR", 4, 121}, + #endif + #ifdef RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", ERR_LIB_RSA, RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE}, + #else + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", 4, 148}, + #endif + #ifdef RSA_R_PADDING_CHECK_FAILED + {"PADDING_CHECK_FAILED", ERR_LIB_RSA, RSA_R_PADDING_CHECK_FAILED}, + #else + {"PADDING_CHECK_FAILED", 4, 114}, + #endif + #ifdef RSA_R_PAIRWISE_TEST_FAILURE + {"PAIRWISE_TEST_FAILURE", ERR_LIB_RSA, RSA_R_PAIRWISE_TEST_FAILURE}, + #else + {"PAIRWISE_TEST_FAILURE", 4, 177}, + #endif + #ifdef RSA_R_PKCS_DECODING_ERROR + {"PKCS_DECODING_ERROR", ERR_LIB_RSA, RSA_R_PKCS_DECODING_ERROR}, + #else + {"PKCS_DECODING_ERROR", 4, 159}, + #endif + #ifdef RSA_R_PSS_SALTLEN_TOO_SMALL + {"PSS_SALTLEN_TOO_SMALL", ERR_LIB_RSA, RSA_R_PSS_SALTLEN_TOO_SMALL}, + #else + {"PSS_SALTLEN_TOO_SMALL", 4, 164}, + #endif + #ifdef RSA_R_PUB_EXPONENT_OUT_OF_RANGE + {"PUB_EXPONENT_OUT_OF_RANGE", ERR_LIB_RSA, RSA_R_PUB_EXPONENT_OUT_OF_RANGE}, + #else + {"PUB_EXPONENT_OUT_OF_RANGE", 4, 178}, + #endif + #ifdef RSA_R_P_NOT_PRIME + {"P_NOT_PRIME", ERR_LIB_RSA, RSA_R_P_NOT_PRIME}, + #else + {"P_NOT_PRIME", 4, 128}, + #endif + #ifdef RSA_R_Q_NOT_PRIME + {"Q_NOT_PRIME", ERR_LIB_RSA, RSA_R_Q_NOT_PRIME}, + #else + {"Q_NOT_PRIME", 4, 129}, + #endif + #ifdef RSA_R_RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT + {"RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT", ERR_LIB_RSA, RSA_R_RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT}, + #else + {"RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT", 4, 180}, + #endif + #ifdef RSA_R_RSA_OPERATIONS_NOT_SUPPORTED + {"RSA_OPERATIONS_NOT_SUPPORTED", ERR_LIB_RSA, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED}, + #else + {"RSA_OPERATIONS_NOT_SUPPORTED", 4, 130}, + #endif + #ifdef RSA_R_SLEN_CHECK_FAILED + {"SLEN_CHECK_FAILED", ERR_LIB_RSA, RSA_R_SLEN_CHECK_FAILED}, + #else + {"SLEN_CHECK_FAILED", 4, 136}, + #endif + #ifdef RSA_R_SLEN_RECOVERY_FAILED + {"SLEN_RECOVERY_FAILED", ERR_LIB_RSA, RSA_R_SLEN_RECOVERY_FAILED}, + #else + {"SLEN_RECOVERY_FAILED", 4, 135}, + #endif + #ifdef RSA_R_SSLV3_ROLLBACK_ATTACK + {"SSLV3_ROLLBACK_ATTACK", ERR_LIB_RSA, RSA_R_SSLV3_ROLLBACK_ATTACK}, + #else + {"SSLV3_ROLLBACK_ATTACK", 4, 115}, + #endif + #ifdef RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD + {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", ERR_LIB_RSA, RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD}, + #else + {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", 4, 116}, + #endif + #ifdef RSA_R_UNKNOWN_ALGORITHM_TYPE + {"UNKNOWN_ALGORITHM_TYPE", ERR_LIB_RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE}, + #else + {"UNKNOWN_ALGORITHM_TYPE", 4, 117}, + #endif + #ifdef RSA_R_UNKNOWN_DIGEST + {"UNKNOWN_DIGEST", ERR_LIB_RSA, RSA_R_UNKNOWN_DIGEST}, + #else + {"UNKNOWN_DIGEST", 4, 166}, + #endif + #ifdef RSA_R_UNKNOWN_MASK_DIGEST + {"UNKNOWN_MASK_DIGEST", ERR_LIB_RSA, RSA_R_UNKNOWN_MASK_DIGEST}, + #else + {"UNKNOWN_MASK_DIGEST", 4, 151}, + #endif + #ifdef RSA_R_UNKNOWN_PADDING_TYPE + {"UNKNOWN_PADDING_TYPE", ERR_LIB_RSA, RSA_R_UNKNOWN_PADDING_TYPE}, + #else + {"UNKNOWN_PADDING_TYPE", 4, 118}, + #endif + #ifdef RSA_R_UNSUPPORTED_ENCRYPTION_TYPE + {"UNSUPPORTED_ENCRYPTION_TYPE", ERR_LIB_RSA, RSA_R_UNSUPPORTED_ENCRYPTION_TYPE}, + #else + {"UNSUPPORTED_ENCRYPTION_TYPE", 4, 162}, + #endif + #ifdef RSA_R_UNSUPPORTED_LABEL_SOURCE + {"UNSUPPORTED_LABEL_SOURCE", ERR_LIB_RSA, RSA_R_UNSUPPORTED_LABEL_SOURCE}, + #else + {"UNSUPPORTED_LABEL_SOURCE", 4, 163}, + #endif + #ifdef RSA_R_UNSUPPORTED_MASK_ALGORITHM + {"UNSUPPORTED_MASK_ALGORITHM", ERR_LIB_RSA, RSA_R_UNSUPPORTED_MASK_ALGORITHM}, + #else + {"UNSUPPORTED_MASK_ALGORITHM", 4, 153}, + #endif + #ifdef RSA_R_UNSUPPORTED_MASK_PARAMETER + {"UNSUPPORTED_MASK_PARAMETER", ERR_LIB_RSA, RSA_R_UNSUPPORTED_MASK_PARAMETER}, + #else + {"UNSUPPORTED_MASK_PARAMETER", 4, 154}, + #endif + #ifdef RSA_R_UNSUPPORTED_SIGNATURE_TYPE + {"UNSUPPORTED_SIGNATURE_TYPE", ERR_LIB_RSA, RSA_R_UNSUPPORTED_SIGNATURE_TYPE}, + #else + {"UNSUPPORTED_SIGNATURE_TYPE", 4, 155}, + #endif + #ifdef RSA_R_VALUE_MISSING + {"VALUE_MISSING", ERR_LIB_RSA, RSA_R_VALUE_MISSING}, + #else + {"VALUE_MISSING", 4, 147}, + #endif + #ifdef RSA_R_WRONG_SIGNATURE_LENGTH + {"WRONG_SIGNATURE_LENGTH", ERR_LIB_RSA, RSA_R_WRONG_SIGNATURE_LENGTH}, + #else + {"WRONG_SIGNATURE_LENGTH", 4, 119}, + #endif + #ifdef SM2_R_ASN1_ERROR + {"ASN1_ERROR", ERR_LIB_SM2, SM2_R_ASN1_ERROR}, + #else + {"ASN1_ERROR", 53, 100}, + #endif + #ifdef SM2_R_BAD_SIGNATURE + {"BAD_SIGNATURE", ERR_LIB_SM2, SM2_R_BAD_SIGNATURE}, + #else + {"BAD_SIGNATURE", 53, 101}, + #endif + #ifdef SM2_R_BUFFER_TOO_SMALL + {"BUFFER_TOO_SMALL", ERR_LIB_SM2, SM2_R_BUFFER_TOO_SMALL}, + #else + {"BUFFER_TOO_SMALL", 53, 107}, + #endif + #ifdef SM2_R_DIST_ID_TOO_LARGE + {"DIST_ID_TOO_LARGE", ERR_LIB_SM2, SM2_R_DIST_ID_TOO_LARGE}, + #else + {"DIST_ID_TOO_LARGE", 53, 110}, + #endif + #ifdef SM2_R_ID_NOT_SET + {"ID_NOT_SET", ERR_LIB_SM2, SM2_R_ID_NOT_SET}, + #else + {"ID_NOT_SET", 53, 112}, + #endif + #ifdef SM2_R_ID_TOO_LARGE + {"ID_TOO_LARGE", ERR_LIB_SM2, SM2_R_ID_TOO_LARGE}, + #else + {"ID_TOO_LARGE", 53, 111}, + #endif + #ifdef SM2_R_INVALID_CURVE + {"INVALID_CURVE", ERR_LIB_SM2, SM2_R_INVALID_CURVE}, + #else + {"INVALID_CURVE", 53, 108}, + #endif + #ifdef SM2_R_INVALID_DIGEST + {"INVALID_DIGEST", ERR_LIB_SM2, SM2_R_INVALID_DIGEST}, + #else + {"INVALID_DIGEST", 53, 102}, + #endif + #ifdef SM2_R_INVALID_DIGEST_TYPE + {"INVALID_DIGEST_TYPE", ERR_LIB_SM2, SM2_R_INVALID_DIGEST_TYPE}, + #else + {"INVALID_DIGEST_TYPE", 53, 103}, + #endif + #ifdef SM2_R_INVALID_ENCODING + {"INVALID_ENCODING", ERR_LIB_SM2, SM2_R_INVALID_ENCODING}, + #else + {"INVALID_ENCODING", 53, 104}, + #endif + #ifdef SM2_R_INVALID_FIELD + {"INVALID_FIELD", ERR_LIB_SM2, SM2_R_INVALID_FIELD}, + #else + {"INVALID_FIELD", 53, 105}, + #endif + #ifdef SM2_R_INVALID_PRIVATE_KEY + {"INVALID_PRIVATE_KEY", ERR_LIB_SM2, SM2_R_INVALID_PRIVATE_KEY}, + #else + {"INVALID_PRIVATE_KEY", 53, 113}, + #endif + #ifdef SM2_R_NO_PARAMETERS_SET + {"NO_PARAMETERS_SET", ERR_LIB_SM2, SM2_R_NO_PARAMETERS_SET}, + #else + {"NO_PARAMETERS_SET", 53, 109}, + #endif + #ifdef SM2_R_USER_ID_TOO_LARGE + {"USER_ID_TOO_LARGE", ERR_LIB_SM2, SM2_R_USER_ID_TOO_LARGE}, + #else + {"USER_ID_TOO_LARGE", 53, 106}, + #endif + #ifdef SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY + {"APPLICATION_DATA_AFTER_CLOSE_NOTIFY", ERR_LIB_SSL, SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY}, + #else + {"APPLICATION_DATA_AFTER_CLOSE_NOTIFY", 20, 291}, + #endif + #ifdef SSL_R_APP_DATA_IN_HANDSHAKE + {"APP_DATA_IN_HANDSHAKE", ERR_LIB_SSL, SSL_R_APP_DATA_IN_HANDSHAKE}, + #else + {"APP_DATA_IN_HANDSHAKE", 20, 100}, + #endif + #ifdef SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT + {"ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT", ERR_LIB_SSL, SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT}, + #else + {"ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT", 20, 272}, + #endif + #ifdef SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE + {"AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE", ERR_LIB_SSL, SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE}, + #else + {"AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE", 20, 158}, + #endif + #ifdef SSL_R_BAD_CERTIFICATE + {"BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_BAD_CERTIFICATE}, + #else + {"BAD_CERTIFICATE", 20, 348}, + #endif + #ifdef SSL_R_BAD_CHANGE_CIPHER_SPEC + {"BAD_CHANGE_CIPHER_SPEC", ERR_LIB_SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC}, + #else + {"BAD_CHANGE_CIPHER_SPEC", 20, 103}, + #endif + #ifdef SSL_R_BAD_CIPHER + {"BAD_CIPHER", ERR_LIB_SSL, SSL_R_BAD_CIPHER}, + #else + {"BAD_CIPHER", 20, 186}, + #endif + #ifdef SSL_R_BAD_COMPRESSION_ALGORITHM + {"BAD_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_BAD_COMPRESSION_ALGORITHM}, + #else + {"BAD_COMPRESSION_ALGORITHM", 20, 326}, + #endif + #ifdef SSL_R_BAD_DATA + {"BAD_DATA", ERR_LIB_SSL, SSL_R_BAD_DATA}, + #else + {"BAD_DATA", 20, 390}, + #endif + #ifdef SSL_R_BAD_DATA_RETURNED_BY_CALLBACK + {"BAD_DATA_RETURNED_BY_CALLBACK", ERR_LIB_SSL, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK}, + #else + {"BAD_DATA_RETURNED_BY_CALLBACK", 20, 106}, + #endif + #ifdef SSL_R_BAD_DECOMPRESSION + {"BAD_DECOMPRESSION", ERR_LIB_SSL, SSL_R_BAD_DECOMPRESSION}, + #else + {"BAD_DECOMPRESSION", 20, 107}, + #endif + #ifdef SSL_R_BAD_DH_VALUE + {"BAD_DH_VALUE", ERR_LIB_SSL, SSL_R_BAD_DH_VALUE}, + #else + {"BAD_DH_VALUE", 20, 102}, + #endif + #ifdef SSL_R_BAD_DIGEST_LENGTH + {"BAD_DIGEST_LENGTH", ERR_LIB_SSL, SSL_R_BAD_DIGEST_LENGTH}, + #else + {"BAD_DIGEST_LENGTH", 20, 111}, + #endif + #ifdef SSL_R_BAD_EARLY_DATA + {"BAD_EARLY_DATA", ERR_LIB_SSL, SSL_R_BAD_EARLY_DATA}, + #else + {"BAD_EARLY_DATA", 20, 233}, + #endif + #ifdef SSL_R_BAD_ECC_CERT + {"BAD_ECC_CERT", ERR_LIB_SSL, SSL_R_BAD_ECC_CERT}, + #else + {"BAD_ECC_CERT", 20, 304}, + #endif + #ifdef SSL_R_BAD_ECPOINT + {"BAD_ECPOINT", ERR_LIB_SSL, SSL_R_BAD_ECPOINT}, + #else + {"BAD_ECPOINT", 20, 306}, + #endif + #ifdef SSL_R_BAD_EXTENSION + {"BAD_EXTENSION", ERR_LIB_SSL, SSL_R_BAD_EXTENSION}, + #else + {"BAD_EXTENSION", 20, 110}, + #endif + #ifdef SSL_R_BAD_HANDSHAKE_LENGTH + {"BAD_HANDSHAKE_LENGTH", ERR_LIB_SSL, SSL_R_BAD_HANDSHAKE_LENGTH}, + #else + {"BAD_HANDSHAKE_LENGTH", 20, 332}, + #endif + #ifdef SSL_R_BAD_HANDSHAKE_STATE + {"BAD_HANDSHAKE_STATE", ERR_LIB_SSL, SSL_R_BAD_HANDSHAKE_STATE}, + #else + {"BAD_HANDSHAKE_STATE", 20, 236}, + #endif + #ifdef SSL_R_BAD_HELLO_REQUEST + {"BAD_HELLO_REQUEST", ERR_LIB_SSL, SSL_R_BAD_HELLO_REQUEST}, + #else + {"BAD_HELLO_REQUEST", 20, 105}, + #endif + #ifdef SSL_R_BAD_HRR_VERSION + {"BAD_HRR_VERSION", ERR_LIB_SSL, SSL_R_BAD_HRR_VERSION}, + #else + {"BAD_HRR_VERSION", 20, 263}, + #endif + #ifdef SSL_R_BAD_KEY_SHARE + {"BAD_KEY_SHARE", ERR_LIB_SSL, SSL_R_BAD_KEY_SHARE}, + #else + {"BAD_KEY_SHARE", 20, 108}, + #endif + #ifdef SSL_R_BAD_KEY_UPDATE + {"BAD_KEY_UPDATE", ERR_LIB_SSL, SSL_R_BAD_KEY_UPDATE}, + #else + {"BAD_KEY_UPDATE", 20, 122}, + #endif + #ifdef SSL_R_BAD_LEGACY_VERSION + {"BAD_LEGACY_VERSION", ERR_LIB_SSL, SSL_R_BAD_LEGACY_VERSION}, + #else + {"BAD_LEGACY_VERSION", 20, 292}, + #endif + #ifdef SSL_R_BAD_LENGTH + {"BAD_LENGTH", ERR_LIB_SSL, SSL_R_BAD_LENGTH}, + #else + {"BAD_LENGTH", 20, 271}, + #endif + #ifdef SSL_R_BAD_PACKET + {"BAD_PACKET", ERR_LIB_SSL, SSL_R_BAD_PACKET}, + #else + {"BAD_PACKET", 20, 240}, + #endif + #ifdef SSL_R_BAD_PACKET_LENGTH + {"BAD_PACKET_LENGTH", ERR_LIB_SSL, SSL_R_BAD_PACKET_LENGTH}, + #else + {"BAD_PACKET_LENGTH", 20, 115}, + #endif + #ifdef SSL_R_BAD_PROTOCOL_VERSION_NUMBER + {"BAD_PROTOCOL_VERSION_NUMBER", ERR_LIB_SSL, SSL_R_BAD_PROTOCOL_VERSION_NUMBER}, + #else + {"BAD_PROTOCOL_VERSION_NUMBER", 20, 116}, + #endif + #ifdef SSL_R_BAD_PSK + {"BAD_PSK", ERR_LIB_SSL, SSL_R_BAD_PSK}, + #else + {"BAD_PSK", 20, 219}, + #endif + #ifdef SSL_R_BAD_PSK_IDENTITY + {"BAD_PSK_IDENTITY", ERR_LIB_SSL, SSL_R_BAD_PSK_IDENTITY}, + #else + {"BAD_PSK_IDENTITY", 20, 114}, + #endif + #ifdef SSL_R_BAD_RECORD_TYPE + {"BAD_RECORD_TYPE", ERR_LIB_SSL, SSL_R_BAD_RECORD_TYPE}, + #else + {"BAD_RECORD_TYPE", 20, 443}, + #endif + #ifdef SSL_R_BAD_RSA_ENCRYPT + {"BAD_RSA_ENCRYPT", ERR_LIB_SSL, SSL_R_BAD_RSA_ENCRYPT}, + #else + {"BAD_RSA_ENCRYPT", 20, 119}, + #endif + #ifdef SSL_R_BAD_SIGNATURE + {"BAD_SIGNATURE", ERR_LIB_SSL, SSL_R_BAD_SIGNATURE}, + #else + {"BAD_SIGNATURE", 20, 123}, + #endif + #ifdef SSL_R_BAD_SRP_A_LENGTH + {"BAD_SRP_A_LENGTH", ERR_LIB_SSL, SSL_R_BAD_SRP_A_LENGTH}, + #else + {"BAD_SRP_A_LENGTH", 20, 347}, + #endif + #ifdef SSL_R_BAD_SRP_PARAMETERS + {"BAD_SRP_PARAMETERS", ERR_LIB_SSL, SSL_R_BAD_SRP_PARAMETERS}, + #else + {"BAD_SRP_PARAMETERS", 20, 371}, + #endif + #ifdef SSL_R_BAD_SRTP_MKI_VALUE + {"BAD_SRTP_MKI_VALUE", ERR_LIB_SSL, SSL_R_BAD_SRTP_MKI_VALUE}, + #else + {"BAD_SRTP_MKI_VALUE", 20, 352}, + #endif + #ifdef SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST + {"BAD_SRTP_PROTECTION_PROFILE_LIST", ERR_LIB_SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST}, + #else + {"BAD_SRTP_PROTECTION_PROFILE_LIST", 20, 353}, + #endif + #ifdef SSL_R_BAD_SSL_FILETYPE + {"BAD_SSL_FILETYPE", ERR_LIB_SSL, SSL_R_BAD_SSL_FILETYPE}, + #else + {"BAD_SSL_FILETYPE", 20, 124}, + #endif + #ifdef SSL_R_BAD_VALUE + {"BAD_VALUE", ERR_LIB_SSL, SSL_R_BAD_VALUE}, + #else + {"BAD_VALUE", 20, 384}, + #endif + #ifdef SSL_R_BAD_WRITE_RETRY + {"BAD_WRITE_RETRY", ERR_LIB_SSL, SSL_R_BAD_WRITE_RETRY}, + #else + {"BAD_WRITE_RETRY", 20, 127}, + #endif + #ifdef SSL_R_BINDER_DOES_NOT_VERIFY + {"BINDER_DOES_NOT_VERIFY", ERR_LIB_SSL, SSL_R_BINDER_DOES_NOT_VERIFY}, + #else + {"BINDER_DOES_NOT_VERIFY", 20, 253}, + #endif + #ifdef SSL_R_BIO_NOT_SET + {"BIO_NOT_SET", ERR_LIB_SSL, SSL_R_BIO_NOT_SET}, + #else + {"BIO_NOT_SET", 20, 128}, + #endif + #ifdef SSL_R_BLOCK_CIPHER_PAD_IS_WRONG + {"BLOCK_CIPHER_PAD_IS_WRONG", ERR_LIB_SSL, SSL_R_BLOCK_CIPHER_PAD_IS_WRONG}, + #else + {"BLOCK_CIPHER_PAD_IS_WRONG", 20, 129}, + #endif + #ifdef SSL_R_BN_LIB + {"BN_LIB", ERR_LIB_SSL, SSL_R_BN_LIB}, + #else + {"BN_LIB", 20, 130}, + #endif + #ifdef SSL_R_CALLBACK_FAILED + {"CALLBACK_FAILED", ERR_LIB_SSL, SSL_R_CALLBACK_FAILED}, + #else + {"CALLBACK_FAILED", 20, 234}, + #endif + #ifdef SSL_R_CANNOT_CHANGE_CIPHER + {"CANNOT_CHANGE_CIPHER", ERR_LIB_SSL, SSL_R_CANNOT_CHANGE_CIPHER}, + #else + {"CANNOT_CHANGE_CIPHER", 20, 109}, + #endif + #ifdef SSL_R_CANNOT_GET_GROUP_NAME + {"CANNOT_GET_GROUP_NAME", ERR_LIB_SSL, SSL_R_CANNOT_GET_GROUP_NAME}, + #else + {"CANNOT_GET_GROUP_NAME", 20, 299}, + #endif + #ifdef SSL_R_CA_DN_LENGTH_MISMATCH + {"CA_DN_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_CA_DN_LENGTH_MISMATCH}, + #else + {"CA_DN_LENGTH_MISMATCH", 20, 131}, + #endif + #ifdef SSL_R_CA_KEY_TOO_SMALL + {"CA_KEY_TOO_SMALL", ERR_LIB_SSL, SSL_R_CA_KEY_TOO_SMALL}, + #else + {"CA_KEY_TOO_SMALL", 20, 397}, + #endif + #ifdef SSL_R_CA_MD_TOO_WEAK + {"CA_MD_TOO_WEAK", ERR_LIB_SSL, SSL_R_CA_MD_TOO_WEAK}, + #else + {"CA_MD_TOO_WEAK", 20, 398}, + #endif + #ifdef SSL_R_CCS_RECEIVED_EARLY + {"CCS_RECEIVED_EARLY", ERR_LIB_SSL, SSL_R_CCS_RECEIVED_EARLY}, + #else + {"CCS_RECEIVED_EARLY", 20, 133}, + #endif + #ifdef SSL_R_CERTIFICATE_VERIFY_FAILED + {"CERTIFICATE_VERIFY_FAILED", ERR_LIB_SSL, SSL_R_CERTIFICATE_VERIFY_FAILED}, + #else + {"CERTIFICATE_VERIFY_FAILED", 20, 134}, + #endif + #ifdef SSL_R_CERT_CB_ERROR + {"CERT_CB_ERROR", ERR_LIB_SSL, SSL_R_CERT_CB_ERROR}, + #else + {"CERT_CB_ERROR", 20, 377}, + #endif + #ifdef SSL_R_CERT_LENGTH_MISMATCH + {"CERT_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_CERT_LENGTH_MISMATCH}, + #else + {"CERT_LENGTH_MISMATCH", 20, 135}, + #endif + #ifdef SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED + {"CIPHERSUITE_DIGEST_HAS_CHANGED", ERR_LIB_SSL, SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED}, + #else + {"CIPHERSUITE_DIGEST_HAS_CHANGED", 20, 218}, + #endif + #ifdef SSL_R_CIPHER_CODE_WRONG_LENGTH + {"CIPHER_CODE_WRONG_LENGTH", ERR_LIB_SSL, SSL_R_CIPHER_CODE_WRONG_LENGTH}, + #else + {"CIPHER_CODE_WRONG_LENGTH", 20, 137}, + #endif + #ifdef SSL_R_CLIENTHELLO_TLSEXT + {"CLIENTHELLO_TLSEXT", ERR_LIB_SSL, SSL_R_CLIENTHELLO_TLSEXT}, + #else + {"CLIENTHELLO_TLSEXT", 20, 226}, + #endif + #ifdef SSL_R_COMPRESSED_LENGTH_TOO_LONG + {"COMPRESSED_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_COMPRESSED_LENGTH_TOO_LONG}, + #else + {"COMPRESSED_LENGTH_TOO_LONG", 20, 140}, + #endif + #ifdef SSL_R_COMPRESSION_DISABLED + {"COMPRESSION_DISABLED", ERR_LIB_SSL, SSL_R_COMPRESSION_DISABLED}, + #else + {"COMPRESSION_DISABLED", 20, 343}, + #endif + #ifdef SSL_R_COMPRESSION_FAILURE + {"COMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_COMPRESSION_FAILURE}, + #else + {"COMPRESSION_FAILURE", 20, 141}, + #endif + #ifdef SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE + {"COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE", ERR_LIB_SSL, SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE}, + #else + {"COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE", 20, 307}, + #endif + #ifdef SSL_R_COMPRESSION_LIBRARY_ERROR + {"COMPRESSION_LIBRARY_ERROR", ERR_LIB_SSL, SSL_R_COMPRESSION_LIBRARY_ERROR}, + #else + {"COMPRESSION_LIBRARY_ERROR", 20, 142}, + #endif + #ifdef SSL_R_CONNECTION_TYPE_NOT_SET + {"CONNECTION_TYPE_NOT_SET", ERR_LIB_SSL, SSL_R_CONNECTION_TYPE_NOT_SET}, + #else + {"CONNECTION_TYPE_NOT_SET", 20, 144}, + #endif + #ifdef SSL_R_CONN_USE_ONLY + {"CONN_USE_ONLY", ERR_LIB_SSL, SSL_R_CONN_USE_ONLY}, + #else + {"CONN_USE_ONLY", 20, 356}, + #endif + #ifdef SSL_R_CONTEXT_NOT_DANE_ENABLED + {"CONTEXT_NOT_DANE_ENABLED", ERR_LIB_SSL, SSL_R_CONTEXT_NOT_DANE_ENABLED}, + #else + {"CONTEXT_NOT_DANE_ENABLED", 20, 167}, + #endif + #ifdef SSL_R_COOKIE_GEN_CALLBACK_FAILURE + {"COOKIE_GEN_CALLBACK_FAILURE", ERR_LIB_SSL, SSL_R_COOKIE_GEN_CALLBACK_FAILURE}, + #else + {"COOKIE_GEN_CALLBACK_FAILURE", 20, 400}, + #endif + #ifdef SSL_R_COOKIE_MISMATCH + {"COOKIE_MISMATCH", ERR_LIB_SSL, SSL_R_COOKIE_MISMATCH}, + #else + {"COOKIE_MISMATCH", 20, 308}, + #endif + #ifdef SSL_R_COPY_PARAMETERS_FAILED + {"COPY_PARAMETERS_FAILED", ERR_LIB_SSL, SSL_R_COPY_PARAMETERS_FAILED}, + #else + {"COPY_PARAMETERS_FAILED", 20, 296}, + #endif + #ifdef SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED + {"CUSTOM_EXT_HANDLER_ALREADY_INSTALLED", ERR_LIB_SSL, SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED}, + #else + {"CUSTOM_EXT_HANDLER_ALREADY_INSTALLED", 20, 206}, + #endif + #ifdef SSL_R_DANE_ALREADY_ENABLED + {"DANE_ALREADY_ENABLED", ERR_LIB_SSL, SSL_R_DANE_ALREADY_ENABLED}, + #else + {"DANE_ALREADY_ENABLED", 20, 172}, + #endif + #ifdef SSL_R_DANE_CANNOT_OVERRIDE_MTYPE_FULL + {"DANE_CANNOT_OVERRIDE_MTYPE_FULL", ERR_LIB_SSL, SSL_R_DANE_CANNOT_OVERRIDE_MTYPE_FULL}, + #else + {"DANE_CANNOT_OVERRIDE_MTYPE_FULL", 20, 173}, + #endif + #ifdef SSL_R_DANE_NOT_ENABLED + {"DANE_NOT_ENABLED", ERR_LIB_SSL, SSL_R_DANE_NOT_ENABLED}, + #else + {"DANE_NOT_ENABLED", 20, 175}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_CERTIFICATE + {"DANE_TLSA_BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_CERTIFICATE}, + #else + {"DANE_TLSA_BAD_CERTIFICATE", 20, 180}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_CERTIFICATE_USAGE + {"DANE_TLSA_BAD_CERTIFICATE_USAGE", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_CERTIFICATE_USAGE}, + #else + {"DANE_TLSA_BAD_CERTIFICATE_USAGE", 20, 184}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_DATA_LENGTH + {"DANE_TLSA_BAD_DATA_LENGTH", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_DATA_LENGTH}, + #else + {"DANE_TLSA_BAD_DATA_LENGTH", 20, 189}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_DIGEST_LENGTH + {"DANE_TLSA_BAD_DIGEST_LENGTH", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_DIGEST_LENGTH}, + #else + {"DANE_TLSA_BAD_DIGEST_LENGTH", 20, 192}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_MATCHING_TYPE + {"DANE_TLSA_BAD_MATCHING_TYPE", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_MATCHING_TYPE}, + #else + {"DANE_TLSA_BAD_MATCHING_TYPE", 20, 200}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_PUBLIC_KEY + {"DANE_TLSA_BAD_PUBLIC_KEY", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_PUBLIC_KEY}, + #else + {"DANE_TLSA_BAD_PUBLIC_KEY", 20, 201}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_SELECTOR + {"DANE_TLSA_BAD_SELECTOR", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_SELECTOR}, + #else + {"DANE_TLSA_BAD_SELECTOR", 20, 202}, + #endif + #ifdef SSL_R_DANE_TLSA_NULL_DATA + {"DANE_TLSA_NULL_DATA", ERR_LIB_SSL, SSL_R_DANE_TLSA_NULL_DATA}, + #else + {"DANE_TLSA_NULL_DATA", 20, 203}, + #endif + #ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED + {"DATA_BETWEEN_CCS_AND_FINISHED", ERR_LIB_SSL, SSL_R_DATA_BETWEEN_CCS_AND_FINISHED}, + #else + {"DATA_BETWEEN_CCS_AND_FINISHED", 20, 145}, + #endif + #ifdef SSL_R_DATA_LENGTH_TOO_LONG + {"DATA_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_DATA_LENGTH_TOO_LONG}, + #else + {"DATA_LENGTH_TOO_LONG", 20, 146}, + #endif + #ifdef SSL_R_DECRYPTION_FAILED + {"DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_DECRYPTION_FAILED}, + #else + {"DECRYPTION_FAILED", 20, 147}, + #endif + #ifdef SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC + {"DECRYPTION_FAILED_OR_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC}, + #else + {"DECRYPTION_FAILED_OR_BAD_RECORD_MAC", 20, 281}, + #endif + #ifdef SSL_R_DH_KEY_TOO_SMALL + {"DH_KEY_TOO_SMALL", ERR_LIB_SSL, SSL_R_DH_KEY_TOO_SMALL}, + #else + {"DH_KEY_TOO_SMALL", 20, 394}, + #endif + #ifdef SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG + {"DH_PUBLIC_VALUE_LENGTH_IS_WRONG", ERR_LIB_SSL, SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG}, + #else + {"DH_PUBLIC_VALUE_LENGTH_IS_WRONG", 20, 148}, + #endif + #ifdef SSL_R_DIGEST_CHECK_FAILED + {"DIGEST_CHECK_FAILED", ERR_LIB_SSL, SSL_R_DIGEST_CHECK_FAILED}, + #else + {"DIGEST_CHECK_FAILED", 20, 149}, + #endif + #ifdef SSL_R_DOMAIN_USE_ONLY + {"DOMAIN_USE_ONLY", ERR_LIB_SSL, SSL_R_DOMAIN_USE_ONLY}, + #else + {"DOMAIN_USE_ONLY", 20, 422}, + #endif + #ifdef SSL_R_DTLS_MESSAGE_TOO_BIG + {"DTLS_MESSAGE_TOO_BIG", ERR_LIB_SSL, SSL_R_DTLS_MESSAGE_TOO_BIG}, + #else + {"DTLS_MESSAGE_TOO_BIG", 20, 334}, + #endif + #ifdef SSL_R_DUPLICATE_COMPRESSION_ID + {"DUPLICATE_COMPRESSION_ID", ERR_LIB_SSL, SSL_R_DUPLICATE_COMPRESSION_ID}, + #else + {"DUPLICATE_COMPRESSION_ID", 20, 309}, + #endif + #ifdef SSL_R_ECC_CERT_NOT_FOR_SIGNING + {"ECC_CERT_NOT_FOR_SIGNING", ERR_LIB_SSL, SSL_R_ECC_CERT_NOT_FOR_SIGNING}, + #else + {"ECC_CERT_NOT_FOR_SIGNING", 20, 318}, + #endif + #ifdef SSL_R_ECDH_REQUIRED_FOR_SUITEB_MODE + {"ECDH_REQUIRED_FOR_SUITEB_MODE", ERR_LIB_SSL, SSL_R_ECDH_REQUIRED_FOR_SUITEB_MODE}, + #else + {"ECDH_REQUIRED_FOR_SUITEB_MODE", 20, 374}, + #endif + #ifdef SSL_R_EE_KEY_TOO_SMALL + {"EE_KEY_TOO_SMALL", ERR_LIB_SSL, SSL_R_EE_KEY_TOO_SMALL}, + #else + {"EE_KEY_TOO_SMALL", 20, 399}, + #endif + #ifdef SSL_R_EMPTY_RAW_PUBLIC_KEY + {"EMPTY_RAW_PUBLIC_KEY", ERR_LIB_SSL, SSL_R_EMPTY_RAW_PUBLIC_KEY}, + #else + {"EMPTY_RAW_PUBLIC_KEY", 20, 349}, + #endif + #ifdef SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST + {"EMPTY_SRTP_PROTECTION_PROFILE_LIST", ERR_LIB_SSL, SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST}, + #else + {"EMPTY_SRTP_PROTECTION_PROFILE_LIST", 20, 354}, + #endif + #ifdef SSL_R_ENCRYPTED_LENGTH_TOO_LONG + {"ENCRYPTED_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_ENCRYPTED_LENGTH_TOO_LONG}, + #else + {"ENCRYPTED_LENGTH_TOO_LONG", 20, 150}, + #endif + #ifdef SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST + {"ERROR_IN_RECEIVED_CIPHER_LIST", ERR_LIB_SSL, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST}, + #else + {"ERROR_IN_RECEIVED_CIPHER_LIST", 20, 151}, + #endif + #ifdef SSL_R_ERROR_IN_SYSTEM_DEFAULT_CONFIG + {"ERROR_IN_SYSTEM_DEFAULT_CONFIG", ERR_LIB_SSL, SSL_R_ERROR_IN_SYSTEM_DEFAULT_CONFIG}, + #else + {"ERROR_IN_SYSTEM_DEFAULT_CONFIG", 20, 419}, + #endif + #ifdef SSL_R_ERROR_SETTING_TLSA_BASE_DOMAIN + {"ERROR_SETTING_TLSA_BASE_DOMAIN", ERR_LIB_SSL, SSL_R_ERROR_SETTING_TLSA_BASE_DOMAIN}, + #else + {"ERROR_SETTING_TLSA_BASE_DOMAIN", 20, 204}, + #endif + #ifdef SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE + {"EXCEEDS_MAX_FRAGMENT_SIZE", ERR_LIB_SSL, SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE}, + #else + {"EXCEEDS_MAX_FRAGMENT_SIZE", 20, 194}, + #endif + #ifdef SSL_R_EXCESSIVE_MESSAGE_SIZE + {"EXCESSIVE_MESSAGE_SIZE", ERR_LIB_SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE}, + #else + {"EXCESSIVE_MESSAGE_SIZE", 20, 152}, + #endif + #ifdef SSL_R_EXTENSION_NOT_RECEIVED + {"EXTENSION_NOT_RECEIVED", ERR_LIB_SSL, SSL_R_EXTENSION_NOT_RECEIVED}, + #else + {"EXTENSION_NOT_RECEIVED", 20, 279}, + #endif + #ifdef SSL_R_EXTRA_DATA_IN_MESSAGE + {"EXTRA_DATA_IN_MESSAGE", ERR_LIB_SSL, SSL_R_EXTRA_DATA_IN_MESSAGE}, + #else + {"EXTRA_DATA_IN_MESSAGE", 20, 153}, + #endif + #ifdef SSL_R_EXT_LENGTH_MISMATCH + {"EXT_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_EXT_LENGTH_MISMATCH}, + #else + {"EXT_LENGTH_MISMATCH", 20, 163}, + #endif + #ifdef SSL_R_FAILED_TO_GET_PARAMETER + {"FAILED_TO_GET_PARAMETER", ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER}, + #else + {"FAILED_TO_GET_PARAMETER", 20, 316}, + #endif + #ifdef SSL_R_FAILED_TO_INIT_ASYNC + {"FAILED_TO_INIT_ASYNC", ERR_LIB_SSL, SSL_R_FAILED_TO_INIT_ASYNC}, + #else + {"FAILED_TO_INIT_ASYNC", 20, 405}, + #endif + #ifdef SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE + {"FEATURE_NEGOTIATION_NOT_COMPLETE", ERR_LIB_SSL, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE}, + #else + {"FEATURE_NEGOTIATION_NOT_COMPLETE", 20, 417}, + #endif + #ifdef SSL_R_FEATURE_NOT_RENEGOTIABLE + {"FEATURE_NOT_RENEGOTIABLE", ERR_LIB_SSL, SSL_R_FEATURE_NOT_RENEGOTIABLE}, + #else + {"FEATURE_NOT_RENEGOTIABLE", 20, 413}, + #endif + #ifdef SSL_R_FRAGMENTED_CLIENT_HELLO + {"FRAGMENTED_CLIENT_HELLO", ERR_LIB_SSL, SSL_R_FRAGMENTED_CLIENT_HELLO}, + #else + {"FRAGMENTED_CLIENT_HELLO", 20, 401}, + #endif + #ifdef SSL_R_GOT_A_FIN_BEFORE_A_CCS + {"GOT_A_FIN_BEFORE_A_CCS", ERR_LIB_SSL, SSL_R_GOT_A_FIN_BEFORE_A_CCS}, + #else + {"GOT_A_FIN_BEFORE_A_CCS", 20, 154}, + #endif + #ifdef SSL_R_HTTPS_PROXY_REQUEST + {"HTTPS_PROXY_REQUEST", ERR_LIB_SSL, SSL_R_HTTPS_PROXY_REQUEST}, + #else + {"HTTPS_PROXY_REQUEST", 20, 155}, + #endif + #ifdef SSL_R_HTTP_REQUEST + {"HTTP_REQUEST", ERR_LIB_SSL, SSL_R_HTTP_REQUEST}, + #else + {"HTTP_REQUEST", 20, 156}, + #endif + #ifdef SSL_R_ILLEGAL_POINT_COMPRESSION + {"ILLEGAL_POINT_COMPRESSION", ERR_LIB_SSL, SSL_R_ILLEGAL_POINT_COMPRESSION}, + #else + {"ILLEGAL_POINT_COMPRESSION", 20, 162}, + #endif + #ifdef SSL_R_ILLEGAL_SUITEB_DIGEST + {"ILLEGAL_SUITEB_DIGEST", ERR_LIB_SSL, SSL_R_ILLEGAL_SUITEB_DIGEST}, + #else + {"ILLEGAL_SUITEB_DIGEST", 20, 380}, + #endif + #ifdef SSL_R_INAPPROPRIATE_FALLBACK + {"INAPPROPRIATE_FALLBACK", ERR_LIB_SSL, SSL_R_INAPPROPRIATE_FALLBACK}, + #else + {"INAPPROPRIATE_FALLBACK", 20, 373}, + #endif + #ifdef SSL_R_INCONSISTENT_COMPRESSION + {"INCONSISTENT_COMPRESSION", ERR_LIB_SSL, SSL_R_INCONSISTENT_COMPRESSION}, + #else + {"INCONSISTENT_COMPRESSION", 20, 340}, + #endif + #ifdef SSL_R_INCONSISTENT_EARLY_DATA_ALPN + {"INCONSISTENT_EARLY_DATA_ALPN", ERR_LIB_SSL, SSL_R_INCONSISTENT_EARLY_DATA_ALPN}, + #else + {"INCONSISTENT_EARLY_DATA_ALPN", 20, 222}, + #endif + #ifdef SSL_R_INCONSISTENT_EARLY_DATA_SNI + {"INCONSISTENT_EARLY_DATA_SNI", ERR_LIB_SSL, SSL_R_INCONSISTENT_EARLY_DATA_SNI}, + #else + {"INCONSISTENT_EARLY_DATA_SNI", 20, 231}, + #endif + #ifdef SSL_R_INCONSISTENT_EXTMS + {"INCONSISTENT_EXTMS", ERR_LIB_SSL, SSL_R_INCONSISTENT_EXTMS}, + #else + {"INCONSISTENT_EXTMS", 20, 104}, + #endif + #ifdef SSL_R_INSUFFICIENT_SECURITY + {"INSUFFICIENT_SECURITY", ERR_LIB_SSL, SSL_R_INSUFFICIENT_SECURITY}, + #else + {"INSUFFICIENT_SECURITY", 20, 241}, + #endif + #ifdef SSL_R_INVALID_ALERT + {"INVALID_ALERT", ERR_LIB_SSL, SSL_R_INVALID_ALERT}, + #else + {"INVALID_ALERT", 20, 205}, + #endif + #ifdef SSL_R_INVALID_CCS_MESSAGE + {"INVALID_CCS_MESSAGE", ERR_LIB_SSL, SSL_R_INVALID_CCS_MESSAGE}, + #else + {"INVALID_CCS_MESSAGE", 20, 260}, + #endif + #ifdef SSL_R_INVALID_CERTIFICATE_OR_ALG + {"INVALID_CERTIFICATE_OR_ALG", ERR_LIB_SSL, SSL_R_INVALID_CERTIFICATE_OR_ALG}, + #else + {"INVALID_CERTIFICATE_OR_ALG", 20, 238}, + #endif + #ifdef SSL_R_INVALID_COMMAND + {"INVALID_COMMAND", ERR_LIB_SSL, SSL_R_INVALID_COMMAND}, + #else + {"INVALID_COMMAND", 20, 280}, + #endif + #ifdef SSL_R_INVALID_COMPRESSION_ALGORITHM + {"INVALID_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_INVALID_COMPRESSION_ALGORITHM}, + #else + {"INVALID_COMPRESSION_ALGORITHM", 20, 341}, + #endif + #ifdef SSL_R_INVALID_CONFIG + {"INVALID_CONFIG", ERR_LIB_SSL, SSL_R_INVALID_CONFIG}, + #else + {"INVALID_CONFIG", 20, 283}, + #endif + #ifdef SSL_R_INVALID_CONFIGURATION_NAME + {"INVALID_CONFIGURATION_NAME", ERR_LIB_SSL, SSL_R_INVALID_CONFIGURATION_NAME}, + #else + {"INVALID_CONFIGURATION_NAME", 20, 113}, + #endif + #ifdef SSL_R_INVALID_CONTEXT + {"INVALID_CONTEXT", ERR_LIB_SSL, SSL_R_INVALID_CONTEXT}, + #else + {"INVALID_CONTEXT", 20, 282}, + #endif + #ifdef SSL_R_INVALID_CT_VALIDATION_TYPE + {"INVALID_CT_VALIDATION_TYPE", ERR_LIB_SSL, SSL_R_INVALID_CT_VALIDATION_TYPE}, + #else + {"INVALID_CT_VALIDATION_TYPE", 20, 212}, + #endif + #ifdef SSL_R_INVALID_KEY_UPDATE_TYPE + {"INVALID_KEY_UPDATE_TYPE", ERR_LIB_SSL, SSL_R_INVALID_KEY_UPDATE_TYPE}, + #else + {"INVALID_KEY_UPDATE_TYPE", 20, 120}, + #endif + #ifdef SSL_R_INVALID_MAX_EARLY_DATA + {"INVALID_MAX_EARLY_DATA", ERR_LIB_SSL, SSL_R_INVALID_MAX_EARLY_DATA}, + #else + {"INVALID_MAX_EARLY_DATA", 20, 174}, + #endif + #ifdef SSL_R_INVALID_NULL_CMD_NAME + {"INVALID_NULL_CMD_NAME", ERR_LIB_SSL, SSL_R_INVALID_NULL_CMD_NAME}, + #else + {"INVALID_NULL_CMD_NAME", 20, 385}, + #endif + #ifdef SSL_R_INVALID_RAW_PUBLIC_KEY + {"INVALID_RAW_PUBLIC_KEY", ERR_LIB_SSL, SSL_R_INVALID_RAW_PUBLIC_KEY}, + #else + {"INVALID_RAW_PUBLIC_KEY", 20, 350}, + #endif + #ifdef SSL_R_INVALID_RECORD + {"INVALID_RECORD", ERR_LIB_SSL, SSL_R_INVALID_RECORD}, + #else + {"INVALID_RECORD", 20, 317}, + #endif + #ifdef SSL_R_INVALID_SEQUENCE_NUMBER + {"INVALID_SEQUENCE_NUMBER", ERR_LIB_SSL, SSL_R_INVALID_SEQUENCE_NUMBER}, + #else + {"INVALID_SEQUENCE_NUMBER", 20, 402}, + #endif + #ifdef SSL_R_INVALID_SERVERINFO_DATA + {"INVALID_SERVERINFO_DATA", ERR_LIB_SSL, SSL_R_INVALID_SERVERINFO_DATA}, + #else + {"INVALID_SERVERINFO_DATA", 20, 388}, + #endif + #ifdef SSL_R_INVALID_SESSION_ID + {"INVALID_SESSION_ID", ERR_LIB_SSL, SSL_R_INVALID_SESSION_ID}, + #else + {"INVALID_SESSION_ID", 20, 999}, + #endif + #ifdef SSL_R_INVALID_SRP_USERNAME + {"INVALID_SRP_USERNAME", ERR_LIB_SSL, SSL_R_INVALID_SRP_USERNAME}, + #else + {"INVALID_SRP_USERNAME", 20, 357}, + #endif + #ifdef SSL_R_INVALID_STATUS_RESPONSE + {"INVALID_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_INVALID_STATUS_RESPONSE}, + #else + {"INVALID_STATUS_RESPONSE", 20, 328}, + #endif + #ifdef SSL_R_INVALID_TICKET_KEYS_LENGTH + {"INVALID_TICKET_KEYS_LENGTH", ERR_LIB_SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH}, + #else + {"INVALID_TICKET_KEYS_LENGTH", 20, 325}, + #endif + #ifdef SSL_R_LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED + {"LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED", ERR_LIB_SSL, SSL_R_LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED}, + #else + {"LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED", 20, 333}, + #endif + #ifdef SSL_R_LENGTH_MISMATCH + {"LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_LENGTH_MISMATCH}, + #else + {"LENGTH_MISMATCH", 20, 159}, + #endif + #ifdef SSL_R_LENGTH_TOO_LONG + {"LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_LENGTH_TOO_LONG}, + #else + {"LENGTH_TOO_LONG", 20, 404}, + #endif + #ifdef SSL_R_LENGTH_TOO_SHORT + {"LENGTH_TOO_SHORT", ERR_LIB_SSL, SSL_R_LENGTH_TOO_SHORT}, + #else + {"LENGTH_TOO_SHORT", 20, 160}, + #endif + #ifdef SSL_R_LIBRARY_BUG + {"LIBRARY_BUG", ERR_LIB_SSL, SSL_R_LIBRARY_BUG}, + #else + {"LIBRARY_BUG", 20, 274}, + #endif + #ifdef SSL_R_LIBRARY_HAS_NO_CIPHERS + {"LIBRARY_HAS_NO_CIPHERS", ERR_LIB_SSL, SSL_R_LIBRARY_HAS_NO_CIPHERS}, + #else + {"LIBRARY_HAS_NO_CIPHERS", 20, 161}, + #endif + #ifdef SSL_R_LISTENER_USE_ONLY + {"LISTENER_USE_ONLY", ERR_LIB_SSL, SSL_R_LISTENER_USE_ONLY}, + #else + {"LISTENER_USE_ONLY", 20, 421}, + #endif + #ifdef SSL_R_MAXIMUM_ENCRYPTED_PKTS_REACHED + {"MAXIMUM_ENCRYPTED_PKTS_REACHED", ERR_LIB_SSL, SSL_R_MAXIMUM_ENCRYPTED_PKTS_REACHED}, + #else + {"MAXIMUM_ENCRYPTED_PKTS_REACHED", 20, 395}, + #endif + #ifdef SSL_R_MISSING_DSA_SIGNING_CERT + {"MISSING_DSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_DSA_SIGNING_CERT}, + #else + {"MISSING_DSA_SIGNING_CERT", 20, 165}, + #endif + #ifdef SSL_R_MISSING_ECDSA_SIGNING_CERT + {"MISSING_ECDSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_ECDSA_SIGNING_CERT}, + #else + {"MISSING_ECDSA_SIGNING_CERT", 20, 381}, + #endif + #ifdef SSL_R_MISSING_FATAL + {"MISSING_FATAL", ERR_LIB_SSL, SSL_R_MISSING_FATAL}, + #else + {"MISSING_FATAL", 20, 256}, + #endif + #ifdef SSL_R_MISSING_PARAMETERS + {"MISSING_PARAMETERS", ERR_LIB_SSL, SSL_R_MISSING_PARAMETERS}, + #else + {"MISSING_PARAMETERS", 20, 290}, + #endif + #ifdef SSL_R_MISSING_PSK_KEX_MODES_EXTENSION + {"MISSING_PSK_KEX_MODES_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_PSK_KEX_MODES_EXTENSION}, + #else + {"MISSING_PSK_KEX_MODES_EXTENSION", 20, 310}, + #endif + #ifdef SSL_R_MISSING_QUIC_TLS_FUNCTIONS + {"MISSING_QUIC_TLS_FUNCTIONS", ERR_LIB_SSL, SSL_R_MISSING_QUIC_TLS_FUNCTIONS}, + #else + {"MISSING_QUIC_TLS_FUNCTIONS", 20, 423}, + #endif + #ifdef SSL_R_MISSING_RSA_CERTIFICATE + {"MISSING_RSA_CERTIFICATE", ERR_LIB_SSL, SSL_R_MISSING_RSA_CERTIFICATE}, + #else + {"MISSING_RSA_CERTIFICATE", 20, 168}, + #endif + #ifdef SSL_R_MISSING_RSA_ENCRYPTING_CERT + {"MISSING_RSA_ENCRYPTING_CERT", ERR_LIB_SSL, SSL_R_MISSING_RSA_ENCRYPTING_CERT}, + #else + {"MISSING_RSA_ENCRYPTING_CERT", 20, 169}, + #endif + #ifdef SSL_R_MISSING_RSA_SIGNING_CERT + {"MISSING_RSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_RSA_SIGNING_CERT}, + #else + {"MISSING_RSA_SIGNING_CERT", 20, 170}, + #endif + #ifdef SSL_R_MISSING_SIGALGS_EXTENSION + {"MISSING_SIGALGS_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_SIGALGS_EXTENSION}, + #else + {"MISSING_SIGALGS_EXTENSION", 20, 112}, + #endif + #ifdef SSL_R_MISSING_SIGNING_CERT + {"MISSING_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_SIGNING_CERT}, + #else + {"MISSING_SIGNING_CERT", 20, 221}, + #endif + #ifdef SSL_R_MISSING_SRP_PARAM + {"MISSING_SRP_PARAM", ERR_LIB_SSL, SSL_R_MISSING_SRP_PARAM}, + #else + {"MISSING_SRP_PARAM", 20, 358}, + #endif + #ifdef SSL_R_MISSING_SUPPORTED_GROUPS_EXTENSION + {"MISSING_SUPPORTED_GROUPS_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_SUPPORTED_GROUPS_EXTENSION}, + #else + {"MISSING_SUPPORTED_GROUPS_EXTENSION", 20, 209}, + #endif + #ifdef SSL_R_MISSING_SUPPORTED_VERSIONS_EXTENSION + {"MISSING_SUPPORTED_VERSIONS_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_SUPPORTED_VERSIONS_EXTENSION}, + #else + {"MISSING_SUPPORTED_VERSIONS_EXTENSION", 20, 420}, + #endif + #ifdef SSL_R_MISSING_TMP_DH_KEY + {"MISSING_TMP_DH_KEY", ERR_LIB_SSL, SSL_R_MISSING_TMP_DH_KEY}, + #else + {"MISSING_TMP_DH_KEY", 20, 171}, + #endif + #ifdef SSL_R_MISSING_TMP_ECDH_KEY + {"MISSING_TMP_ECDH_KEY", ERR_LIB_SSL, SSL_R_MISSING_TMP_ECDH_KEY}, + #else + {"MISSING_TMP_ECDH_KEY", 20, 311}, + #endif + #ifdef SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA + {"MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA", ERR_LIB_SSL, SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA}, + #else + {"MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA", 20, 293}, + #endif + #ifdef SSL_R_NOT_ON_RECORD_BOUNDARY + {"NOT_ON_RECORD_BOUNDARY", ERR_LIB_SSL, SSL_R_NOT_ON_RECORD_BOUNDARY}, + #else + {"NOT_ON_RECORD_BOUNDARY", 20, 182}, + #endif + #ifdef SSL_R_NOT_REPLACING_CERTIFICATE + {"NOT_REPLACING_CERTIFICATE", ERR_LIB_SSL, SSL_R_NOT_REPLACING_CERTIFICATE}, + #else + {"NOT_REPLACING_CERTIFICATE", 20, 289}, + #endif + #ifdef SSL_R_NOT_SERVER + {"NOT_SERVER", ERR_LIB_SSL, SSL_R_NOT_SERVER}, + #else + {"NOT_SERVER", 20, 284}, + #endif + #ifdef SSL_R_NO_APPLICATION_PROTOCOL + {"NO_APPLICATION_PROTOCOL", ERR_LIB_SSL, SSL_R_NO_APPLICATION_PROTOCOL}, + #else + {"NO_APPLICATION_PROTOCOL", 20, 235}, + #endif + #ifdef SSL_R_NO_CERTIFICATES_RETURNED + {"NO_CERTIFICATES_RETURNED", ERR_LIB_SSL, SSL_R_NO_CERTIFICATES_RETURNED}, + #else + {"NO_CERTIFICATES_RETURNED", 20, 176}, + #endif + #ifdef SSL_R_NO_CERTIFICATE_ASSIGNED + {"NO_CERTIFICATE_ASSIGNED", ERR_LIB_SSL, SSL_R_NO_CERTIFICATE_ASSIGNED}, + #else + {"NO_CERTIFICATE_ASSIGNED", 20, 177}, + #endif + #ifdef SSL_R_NO_CERTIFICATE_SET + {"NO_CERTIFICATE_SET", ERR_LIB_SSL, SSL_R_NO_CERTIFICATE_SET}, + #else + {"NO_CERTIFICATE_SET", 20, 179}, + #endif + #ifdef SSL_R_NO_CHANGE_FOLLOWING_HRR + {"NO_CHANGE_FOLLOWING_HRR", ERR_LIB_SSL, SSL_R_NO_CHANGE_FOLLOWING_HRR}, + #else + {"NO_CHANGE_FOLLOWING_HRR", 20, 214}, + #endif + #ifdef SSL_R_NO_CIPHERS_AVAILABLE + {"NO_CIPHERS_AVAILABLE", ERR_LIB_SSL, SSL_R_NO_CIPHERS_AVAILABLE}, + #else + {"NO_CIPHERS_AVAILABLE", 20, 181}, + #endif + #ifdef SSL_R_NO_CIPHERS_SPECIFIED + {"NO_CIPHERS_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_CIPHERS_SPECIFIED}, + #else + {"NO_CIPHERS_SPECIFIED", 20, 183}, + #endif + #ifdef SSL_R_NO_CIPHER_MATCH + {"NO_CIPHER_MATCH", ERR_LIB_SSL, SSL_R_NO_CIPHER_MATCH}, + #else + {"NO_CIPHER_MATCH", 20, 185}, + #endif + #ifdef SSL_R_NO_CLIENT_CERT_METHOD + {"NO_CLIENT_CERT_METHOD", ERR_LIB_SSL, SSL_R_NO_CLIENT_CERT_METHOD}, + #else + {"NO_CLIENT_CERT_METHOD", 20, 331}, + #endif + #ifdef SSL_R_NO_COMPRESSION_SPECIFIED + {"NO_COMPRESSION_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_COMPRESSION_SPECIFIED}, + #else + {"NO_COMPRESSION_SPECIFIED", 20, 187}, + #endif + #ifdef SSL_R_NO_COOKIE_CALLBACK_SET + {"NO_COOKIE_CALLBACK_SET", ERR_LIB_SSL, SSL_R_NO_COOKIE_CALLBACK_SET}, + #else + {"NO_COOKIE_CALLBACK_SET", 20, 287}, + #endif + #ifdef SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER + {"NO_GOST_CERTIFICATE_SENT_BY_PEER", ERR_LIB_SSL, SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER}, + #else + {"NO_GOST_CERTIFICATE_SENT_BY_PEER", 20, 330}, + #endif + #ifdef SSL_R_NO_METHOD_SPECIFIED + {"NO_METHOD_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_METHOD_SPECIFIED}, + #else + {"NO_METHOD_SPECIFIED", 20, 188}, + #endif + #ifdef SSL_R_NO_PEM_EXTENSIONS + {"NO_PEM_EXTENSIONS", ERR_LIB_SSL, SSL_R_NO_PEM_EXTENSIONS}, + #else + {"NO_PEM_EXTENSIONS", 20, 389}, + #endif + #ifdef SSL_R_NO_PRIVATE_KEY_ASSIGNED + {"NO_PRIVATE_KEY_ASSIGNED", ERR_LIB_SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED}, + #else + {"NO_PRIVATE_KEY_ASSIGNED", 20, 190}, + #endif + #ifdef SSL_R_NO_PROTOCOLS_AVAILABLE + {"NO_PROTOCOLS_AVAILABLE", ERR_LIB_SSL, SSL_R_NO_PROTOCOLS_AVAILABLE}, + #else + {"NO_PROTOCOLS_AVAILABLE", 20, 191}, + #endif + #ifdef SSL_R_NO_RENEGOTIATION + {"NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_NO_RENEGOTIATION}, + #else + {"NO_RENEGOTIATION", 20, 339}, + #endif + #ifdef SSL_R_NO_REQUIRED_DIGEST + {"NO_REQUIRED_DIGEST", ERR_LIB_SSL, SSL_R_NO_REQUIRED_DIGEST}, + #else + {"NO_REQUIRED_DIGEST", 20, 324}, + #endif + #ifdef SSL_R_NO_SHARED_CIPHER + {"NO_SHARED_CIPHER", ERR_LIB_SSL, SSL_R_NO_SHARED_CIPHER}, + #else + {"NO_SHARED_CIPHER", 20, 193}, + #endif + #ifdef SSL_R_NO_SHARED_GROUPS + {"NO_SHARED_GROUPS", ERR_LIB_SSL, SSL_R_NO_SHARED_GROUPS}, + #else + {"NO_SHARED_GROUPS", 20, 410}, + #endif + #ifdef SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS + {"NO_SHARED_SIGNATURE_ALGORITHMS", ERR_LIB_SSL, SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS}, + #else + {"NO_SHARED_SIGNATURE_ALGORITHMS", 20, 376}, + #endif + #ifdef SSL_R_NO_SRTP_PROFILES + {"NO_SRTP_PROFILES", ERR_LIB_SSL, SSL_R_NO_SRTP_PROFILES}, + #else + {"NO_SRTP_PROFILES", 20, 359}, + #endif + #ifdef SSL_R_NO_STREAM + {"NO_STREAM", ERR_LIB_SSL, SSL_R_NO_STREAM}, + #else + {"NO_STREAM", 20, 355}, + #endif + #ifdef SSL_R_NO_SUITABLE_DIGEST_ALGORITHM + {"NO_SUITABLE_DIGEST_ALGORITHM", ERR_LIB_SSL, SSL_R_NO_SUITABLE_DIGEST_ALGORITHM}, + #else + {"NO_SUITABLE_DIGEST_ALGORITHM", 20, 297}, + #endif + #ifdef SSL_R_NO_SUITABLE_GROUPS + {"NO_SUITABLE_GROUPS", ERR_LIB_SSL, SSL_R_NO_SUITABLE_GROUPS}, + #else + {"NO_SUITABLE_GROUPS", 20, 295}, + #endif + #ifdef SSL_R_NO_SUITABLE_KEY_SHARE + {"NO_SUITABLE_KEY_SHARE", ERR_LIB_SSL, SSL_R_NO_SUITABLE_KEY_SHARE}, + #else + {"NO_SUITABLE_KEY_SHARE", 20, 101}, + #endif + #ifdef SSL_R_NO_SUITABLE_RECORD_LAYER + {"NO_SUITABLE_RECORD_LAYER", ERR_LIB_SSL, SSL_R_NO_SUITABLE_RECORD_LAYER}, + #else + {"NO_SUITABLE_RECORD_LAYER", 20, 322}, + #endif + #ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM + {"NO_SUITABLE_SIGNATURE_ALGORITHM", ERR_LIB_SSL, SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM}, + #else + {"NO_SUITABLE_SIGNATURE_ALGORITHM", 20, 118}, + #endif + #ifdef SSL_R_NO_VALID_SCTS + {"NO_VALID_SCTS", ERR_LIB_SSL, SSL_R_NO_VALID_SCTS}, + #else + {"NO_VALID_SCTS", 20, 216}, + #endif + #ifdef SSL_R_NO_VERIFY_COOKIE_CALLBACK + {"NO_VERIFY_COOKIE_CALLBACK", ERR_LIB_SSL, SSL_R_NO_VERIFY_COOKIE_CALLBACK}, + #else + {"NO_VERIFY_COOKIE_CALLBACK", 20, 403}, + #endif + #ifdef SSL_R_NULL_SSL_CTX + {"NULL_SSL_CTX", ERR_LIB_SSL, SSL_R_NULL_SSL_CTX}, + #else + {"NULL_SSL_CTX", 20, 195}, + #endif + #ifdef SSL_R_NULL_SSL_METHOD_PASSED + {"NULL_SSL_METHOD_PASSED", ERR_LIB_SSL, SSL_R_NULL_SSL_METHOD_PASSED}, + #else + {"NULL_SSL_METHOD_PASSED", 20, 196}, + #endif + #ifdef SSL_R_OCSP_CALLBACK_FAILURE + {"OCSP_CALLBACK_FAILURE", ERR_LIB_SSL, SSL_R_OCSP_CALLBACK_FAILURE}, + #else + {"OCSP_CALLBACK_FAILURE", 20, 305}, + #endif + #ifdef SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED + {"OLD_SESSION_CIPHER_NOT_RETURNED", ERR_LIB_SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED}, + #else + {"OLD_SESSION_CIPHER_NOT_RETURNED", 20, 197}, + #endif + #ifdef SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED + {"OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED", ERR_LIB_SSL, SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED}, + #else + {"OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED", 20, 344}, + #endif + #ifdef SSL_R_OVERFLOW_ERROR + {"OVERFLOW_ERROR", ERR_LIB_SSL, SSL_R_OVERFLOW_ERROR}, + #else + {"OVERFLOW_ERROR", 20, 237}, + #endif + #ifdef SSL_R_PACKET_LENGTH_TOO_LONG + {"PACKET_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_PACKET_LENGTH_TOO_LONG}, + #else + {"PACKET_LENGTH_TOO_LONG", 20, 198}, + #endif + #ifdef SSL_R_PARSE_TLSEXT + {"PARSE_TLSEXT", ERR_LIB_SSL, SSL_R_PARSE_TLSEXT}, + #else + {"PARSE_TLSEXT", 20, 227}, + #endif + #ifdef SSL_R_PATH_TOO_LONG + {"PATH_TOO_LONG", ERR_LIB_SSL, SSL_R_PATH_TOO_LONG}, + #else + {"PATH_TOO_LONG", 20, 270}, + #endif + #ifdef SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE + {"PEER_DID_NOT_RETURN_A_CERTIFICATE", ERR_LIB_SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE}, + #else + {"PEER_DID_NOT_RETURN_A_CERTIFICATE", 20, 199}, + #endif + #ifdef SSL_R_PEM_NAME_BAD_PREFIX + {"PEM_NAME_BAD_PREFIX", ERR_LIB_SSL, SSL_R_PEM_NAME_BAD_PREFIX}, + #else + {"PEM_NAME_BAD_PREFIX", 20, 391}, + #endif + #ifdef SSL_R_PEM_NAME_TOO_SHORT + {"PEM_NAME_TOO_SHORT", ERR_LIB_SSL, SSL_R_PEM_NAME_TOO_SHORT}, + #else + {"PEM_NAME_TOO_SHORT", 20, 392}, + #endif + #ifdef SSL_R_PIPELINE_FAILURE + {"PIPELINE_FAILURE", ERR_LIB_SSL, SSL_R_PIPELINE_FAILURE}, + #else + {"PIPELINE_FAILURE", 20, 406}, + #endif + #ifdef SSL_R_POLL_REQUEST_NOT_SUPPORTED + {"POLL_REQUEST_NOT_SUPPORTED", ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED}, + #else + {"POLL_REQUEST_NOT_SUPPORTED", 20, 418}, + #endif + #ifdef SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR + {"POST_HANDSHAKE_AUTH_ENCODING_ERR", ERR_LIB_SSL, SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR}, + #else + {"POST_HANDSHAKE_AUTH_ENCODING_ERR", 20, 278}, + #endif + #ifdef SSL_R_PRIVATE_KEY_MISMATCH + {"PRIVATE_KEY_MISMATCH", ERR_LIB_SSL, SSL_R_PRIVATE_KEY_MISMATCH}, + #else + {"PRIVATE_KEY_MISMATCH", 20, 288}, + #endif + #ifdef SSL_R_PROTOCOL_IS_SHUTDOWN + {"PROTOCOL_IS_SHUTDOWN", ERR_LIB_SSL, SSL_R_PROTOCOL_IS_SHUTDOWN}, + #else + {"PROTOCOL_IS_SHUTDOWN", 20, 207}, + #endif + #ifdef SSL_R_PSK_IDENTITY_NOT_FOUND + {"PSK_IDENTITY_NOT_FOUND", ERR_LIB_SSL, SSL_R_PSK_IDENTITY_NOT_FOUND}, + #else + {"PSK_IDENTITY_NOT_FOUND", 20, 223}, + #endif + #ifdef SSL_R_PSK_NO_CLIENT_CB + {"PSK_NO_CLIENT_CB", ERR_LIB_SSL, SSL_R_PSK_NO_CLIENT_CB}, + #else + {"PSK_NO_CLIENT_CB", 20, 224}, + #endif + #ifdef SSL_R_PSK_NO_SERVER_CB + {"PSK_NO_SERVER_CB", ERR_LIB_SSL, SSL_R_PSK_NO_SERVER_CB}, + #else + {"PSK_NO_SERVER_CB", 20, 225}, + #endif + #ifdef SSL_R_QUIC_HANDSHAKE_LAYER_ERROR + {"QUIC_HANDSHAKE_LAYER_ERROR", ERR_LIB_SSL, SSL_R_QUIC_HANDSHAKE_LAYER_ERROR}, + #else + {"QUIC_HANDSHAKE_LAYER_ERROR", 20, 393}, + #endif + #ifdef SSL_R_QUIC_NETWORK_ERROR + {"QUIC_NETWORK_ERROR", ERR_LIB_SSL, SSL_R_QUIC_NETWORK_ERROR}, + #else + {"QUIC_NETWORK_ERROR", 20, 387}, + #endif + #ifdef SSL_R_QUIC_PROTOCOL_ERROR + {"QUIC_PROTOCOL_ERROR", ERR_LIB_SSL, SSL_R_QUIC_PROTOCOL_ERROR}, + #else + {"QUIC_PROTOCOL_ERROR", 20, 382}, + #endif + #ifdef SSL_R_READ_BIO_NOT_SET + {"READ_BIO_NOT_SET", ERR_LIB_SSL, SSL_R_READ_BIO_NOT_SET}, + #else + {"READ_BIO_NOT_SET", 20, 211}, + #endif + #ifdef SSL_R_READ_TIMEOUT_EXPIRED + {"READ_TIMEOUT_EXPIRED", ERR_LIB_SSL, SSL_R_READ_TIMEOUT_EXPIRED}, + #else + {"READ_TIMEOUT_EXPIRED", 20, 312}, + #endif + #ifdef SSL_R_RECORDS_NOT_RELEASED + {"RECORDS_NOT_RELEASED", ERR_LIB_SSL, SSL_R_RECORDS_NOT_RELEASED}, + #else + {"RECORDS_NOT_RELEASED", 20, 321}, + #endif + #ifdef SSL_R_RECORD_LAYER_FAILURE + {"RECORD_LAYER_FAILURE", ERR_LIB_SSL, SSL_R_RECORD_LAYER_FAILURE}, + #else + {"RECORD_LAYER_FAILURE", 20, 313}, + #endif + #ifdef SSL_R_RECORD_LENGTH_MISMATCH + {"RECORD_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_RECORD_LENGTH_MISMATCH}, + #else + {"RECORD_LENGTH_MISMATCH", 20, 213}, + #endif + #ifdef SSL_R_RECORD_TOO_SMALL + {"RECORD_TOO_SMALL", ERR_LIB_SSL, SSL_R_RECORD_TOO_SMALL}, + #else + {"RECORD_TOO_SMALL", 20, 298}, + #endif + #ifdef SSL_R_REMOTE_PEER_ADDRESS_NOT_SET + {"REMOTE_PEER_ADDRESS_NOT_SET", ERR_LIB_SSL, SSL_R_REMOTE_PEER_ADDRESS_NOT_SET}, + #else + {"REMOTE_PEER_ADDRESS_NOT_SET", 20, 346}, + #endif + #ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG + {"RENEGOTIATE_EXT_TOO_LONG", ERR_LIB_SSL, SSL_R_RENEGOTIATE_EXT_TOO_LONG}, + #else + {"RENEGOTIATE_EXT_TOO_LONG", 20, 335}, + #endif + #ifdef SSL_R_RENEGOTIATION_ENCODING_ERR + {"RENEGOTIATION_ENCODING_ERR", ERR_LIB_SSL, SSL_R_RENEGOTIATION_ENCODING_ERR}, + #else + {"RENEGOTIATION_ENCODING_ERR", 20, 336}, + #endif + #ifdef SSL_R_RENEGOTIATION_MISMATCH + {"RENEGOTIATION_MISMATCH", ERR_LIB_SSL, SSL_R_RENEGOTIATION_MISMATCH}, + #else + {"RENEGOTIATION_MISMATCH", 20, 337}, + #endif + #ifdef SSL_R_REQUEST_PENDING + {"REQUEST_PENDING", ERR_LIB_SSL, SSL_R_REQUEST_PENDING}, + #else + {"REQUEST_PENDING", 20, 285}, + #endif + #ifdef SSL_R_REQUEST_SENT + {"REQUEST_SENT", ERR_LIB_SSL, SSL_R_REQUEST_SENT}, + #else + {"REQUEST_SENT", 20, 286}, + #endif + #ifdef SSL_R_REQUIRED_CIPHER_MISSING + {"REQUIRED_CIPHER_MISSING", ERR_LIB_SSL, SSL_R_REQUIRED_CIPHER_MISSING}, + #else + {"REQUIRED_CIPHER_MISSING", 20, 215}, + #endif + #ifdef SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING + {"REQUIRED_COMPRESSION_ALGORITHM_MISSING", ERR_LIB_SSL, SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING}, + #else + {"REQUIRED_COMPRESSION_ALGORITHM_MISSING", 20, 342}, + #endif + #ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING + {"SCSV_RECEIVED_WHEN_RENEGOTIATING", ERR_LIB_SSL, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING}, + #else + {"SCSV_RECEIVED_WHEN_RENEGOTIATING", 20, 345}, + #endif + #ifdef SSL_R_SCT_VERIFICATION_FAILED + {"SCT_VERIFICATION_FAILED", ERR_LIB_SSL, SSL_R_SCT_VERIFICATION_FAILED}, + #else + {"SCT_VERIFICATION_FAILED", 20, 208}, + #endif + #ifdef SSL_R_SEQUENCE_CTR_WRAPPED + {"SEQUENCE_CTR_WRAPPED", ERR_LIB_SSL, SSL_R_SEQUENCE_CTR_WRAPPED}, + #else + {"SEQUENCE_CTR_WRAPPED", 20, 327}, + #endif + #ifdef SSL_R_SERVERHELLO_TLSEXT + {"SERVERHELLO_TLSEXT", ERR_LIB_SSL, SSL_R_SERVERHELLO_TLSEXT}, + #else + {"SERVERHELLO_TLSEXT", 20, 275}, + #endif + #ifdef SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED + {"SESSION_ID_CONTEXT_UNINITIALIZED", ERR_LIB_SSL, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED}, + #else + {"SESSION_ID_CONTEXT_UNINITIALIZED", 20, 277}, + #endif + #ifdef SSL_R_SHUTDOWN_WHILE_IN_INIT + {"SHUTDOWN_WHILE_IN_INIT", ERR_LIB_SSL, SSL_R_SHUTDOWN_WHILE_IN_INIT}, + #else + {"SHUTDOWN_WHILE_IN_INIT", 20, 407}, + #endif + #ifdef SSL_R_SIGNATURE_ALGORITHMS_ERROR + {"SIGNATURE_ALGORITHMS_ERROR", ERR_LIB_SSL, SSL_R_SIGNATURE_ALGORITHMS_ERROR}, + #else + {"SIGNATURE_ALGORITHMS_ERROR", 20, 360}, + #endif + #ifdef SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE + {"SIGNATURE_FOR_NON_SIGNING_CERTIFICATE", ERR_LIB_SSL, SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE}, + #else + {"SIGNATURE_FOR_NON_SIGNING_CERTIFICATE", 20, 220}, + #endif + #ifdef SSL_R_SRP_A_CALC + {"SRP_A_CALC", ERR_LIB_SSL, SSL_R_SRP_A_CALC}, + #else + {"SRP_A_CALC", 20, 361}, + #endif + #ifdef SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES + {"SRTP_COULD_NOT_ALLOCATE_PROFILES", ERR_LIB_SSL, SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES}, + #else + {"SRTP_COULD_NOT_ALLOCATE_PROFILES", 20, 362}, + #endif + #ifdef SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG + {"SRTP_PROTECTION_PROFILE_LIST_TOO_LONG", ERR_LIB_SSL, SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG}, + #else + {"SRTP_PROTECTION_PROFILE_LIST_TOO_LONG", 20, 363}, + #endif + #ifdef SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE + {"SRTP_UNKNOWN_PROTECTION_PROFILE", ERR_LIB_SSL, SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE}, + #else + {"SRTP_UNKNOWN_PROTECTION_PROFILE", 20, 364}, + #endif + #ifdef SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH + {"SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH", ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH}, + #else + {"SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH", 20, 232}, + #endif + #ifdef SSL_R_SSL3_EXT_INVALID_SERVERNAME + {"SSL3_EXT_INVALID_SERVERNAME", ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME}, + #else + {"SSL3_EXT_INVALID_SERVERNAME", 20, 319}, + #endif + #ifdef SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE + {"SSL3_EXT_INVALID_SERVERNAME_TYPE", ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE}, + #else + {"SSL3_EXT_INVALID_SERVERNAME_TYPE", 20, 320}, + #endif + #ifdef SSL_R_SSL3_SESSION_ID_TOO_LONG + {"SSL3_SESSION_ID_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL3_SESSION_ID_TOO_LONG}, + #else + {"SSL3_SESSION_ID_TOO_LONG", 20, 300}, + #endif + #ifdef SSL_R_SSLV3_ALERT_BAD_CERTIFICATE + {"SSLV3_ALERT_BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE}, + #else + {"SSLV3_ALERT_BAD_CERTIFICATE", 20, 1042}, + #endif + #ifdef SSL_R_SSLV3_ALERT_BAD_CERTIFICATE + {"SSLV3_ALERT_BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE}, + #else + {"SSLV3_ALERT_BAD_CERTIFICATE", 20, 1042}, + #endif + #ifdef SSL_R_SSLV3_ALERT_BAD_RECORD_MAC + {"SSLV3_ALERT_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_BAD_RECORD_MAC}, + #else + {"SSLV3_ALERT_BAD_RECORD_MAC", 20, 1020}, + #endif + #ifdef SSL_R_SSLV3_ALERT_BAD_RECORD_MAC + {"SSLV3_ALERT_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_BAD_RECORD_MAC}, + #else + {"SSLV3_ALERT_BAD_RECORD_MAC", 20, 1020}, + #endif + #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED + {"SSLV3_ALERT_CERTIFICATE_EXPIRED", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED}, + #else + {"SSLV3_ALERT_CERTIFICATE_EXPIRED", 20, 1045}, + #endif + #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED + {"SSLV3_ALERT_CERTIFICATE_EXPIRED", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED}, + #else + {"SSLV3_ALERT_CERTIFICATE_EXPIRED", 20, 1045}, + #endif + #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED + {"SSLV3_ALERT_CERTIFICATE_REVOKED", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED}, + #else + {"SSLV3_ALERT_CERTIFICATE_REVOKED", 20, 1044}, + #endif + #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED + {"SSLV3_ALERT_CERTIFICATE_REVOKED", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED}, + #else + {"SSLV3_ALERT_CERTIFICATE_REVOKED", 20, 1044}, + #endif + #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN + {"SSLV3_ALERT_CERTIFICATE_UNKNOWN", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN}, + #else + {"SSLV3_ALERT_CERTIFICATE_UNKNOWN", 20, 1046}, + #endif + #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN + {"SSLV3_ALERT_CERTIFICATE_UNKNOWN", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN}, + #else + {"SSLV3_ALERT_CERTIFICATE_UNKNOWN", 20, 1046}, + #endif + #ifdef SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE + {"SSLV3_ALERT_DECOMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE}, + #else + {"SSLV3_ALERT_DECOMPRESSION_FAILURE", 20, 1030}, + #endif + #ifdef SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE + {"SSLV3_ALERT_DECOMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE}, + #else + {"SSLV3_ALERT_DECOMPRESSION_FAILURE", 20, 1030}, + #endif + #ifdef SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE + {"SSLV3_ALERT_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE}, + #else + {"SSLV3_ALERT_HANDSHAKE_FAILURE", 20, 1040}, + #endif + #ifdef SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE + {"SSLV3_ALERT_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE}, + #else + {"SSLV3_ALERT_HANDSHAKE_FAILURE", 20, 1040}, + #endif + #ifdef SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER + {"SSLV3_ALERT_ILLEGAL_PARAMETER", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER}, + #else + {"SSLV3_ALERT_ILLEGAL_PARAMETER", 20, 1047}, + #endif + #ifdef SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER + {"SSLV3_ALERT_ILLEGAL_PARAMETER", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER}, + #else + {"SSLV3_ALERT_ILLEGAL_PARAMETER", 20, 1047}, + #endif + #ifdef SSL_R_SSLV3_ALERT_NO_CERTIFICATE + {"SSLV3_ALERT_NO_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_NO_CERTIFICATE}, + #else + {"SSLV3_ALERT_NO_CERTIFICATE", 20, 1041}, + #endif + #ifdef SSL_R_SSLV3_ALERT_NO_CERTIFICATE + {"SSLV3_ALERT_NO_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_NO_CERTIFICATE}, + #else + {"SSLV3_ALERT_NO_CERTIFICATE", 20, 1041}, + #endif + #ifdef SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE + {"SSLV3_ALERT_UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE}, + #else + {"SSLV3_ALERT_UNEXPECTED_MESSAGE", 20, 1010}, + #endif + #ifdef SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE + {"SSLV3_ALERT_UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE}, + #else + {"SSLV3_ALERT_UNEXPECTED_MESSAGE", 20, 1010}, + #endif + #ifdef SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE + {"SSLV3_ALERT_UNSUPPORTED_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE}, + #else + {"SSLV3_ALERT_UNSUPPORTED_CERTIFICATE", 20, 1043}, + #endif + #ifdef SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE + {"SSLV3_ALERT_UNSUPPORTED_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE}, + #else + {"SSLV3_ALERT_UNSUPPORTED_CERTIFICATE", 20, 1043}, + #endif + #ifdef SSL_R_SSL_COMMAND_SECTION_EMPTY + {"SSL_COMMAND_SECTION_EMPTY", ERR_LIB_SSL, SSL_R_SSL_COMMAND_SECTION_EMPTY}, + #else + {"SSL_COMMAND_SECTION_EMPTY", 20, 117}, + #endif + #ifdef SSL_R_SSL_COMMAND_SECTION_NOT_FOUND + {"SSL_COMMAND_SECTION_NOT_FOUND", ERR_LIB_SSL, SSL_R_SSL_COMMAND_SECTION_NOT_FOUND}, + #else + {"SSL_COMMAND_SECTION_NOT_FOUND", 20, 125}, + #endif + #ifdef SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION + {"SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION", ERR_LIB_SSL, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION}, + #else + {"SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION", 20, 228}, + #endif + #ifdef SSL_R_SSL_HANDSHAKE_FAILURE + {"SSL_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_SSL_HANDSHAKE_FAILURE}, + #else + {"SSL_HANDSHAKE_FAILURE", 20, 229}, + #endif + #ifdef SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS + {"SSL_LIBRARY_HAS_NO_CIPHERS", ERR_LIB_SSL, SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS}, + #else + {"SSL_LIBRARY_HAS_NO_CIPHERS", 20, 230}, + #endif + #ifdef SSL_R_SSL_NEGATIVE_LENGTH + {"SSL_NEGATIVE_LENGTH", ERR_LIB_SSL, SSL_R_SSL_NEGATIVE_LENGTH}, + #else + {"SSL_NEGATIVE_LENGTH", 20, 372}, + #endif + #ifdef SSL_R_SSL_SECTION_EMPTY + {"SSL_SECTION_EMPTY", ERR_LIB_SSL, SSL_R_SSL_SECTION_EMPTY}, + #else + {"SSL_SECTION_EMPTY", 20, 126}, + #endif + #ifdef SSL_R_SSL_SECTION_NOT_FOUND + {"SSL_SECTION_NOT_FOUND", ERR_LIB_SSL, SSL_R_SSL_SECTION_NOT_FOUND}, + #else + {"SSL_SECTION_NOT_FOUND", 20, 136}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_CALLBACK_FAILED + {"SSL_SESSION_ID_CALLBACK_FAILED", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CALLBACK_FAILED}, + #else + {"SSL_SESSION_ID_CALLBACK_FAILED", 20, 301}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_CONFLICT + {"SSL_SESSION_ID_CONFLICT", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CONFLICT}, + #else + {"SSL_SESSION_ID_CONFLICT", 20, 302}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG + {"SSL_SESSION_ID_CONTEXT_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG}, + #else + {"SSL_SESSION_ID_CONTEXT_TOO_LONG", 20, 273}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH + {"SSL_SESSION_ID_HAS_BAD_LENGTH", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH}, + #else + {"SSL_SESSION_ID_HAS_BAD_LENGTH", 20, 303}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_TOO_LONG + {"SSL_SESSION_ID_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_TOO_LONG}, + #else + {"SSL_SESSION_ID_TOO_LONG", 20, 408}, + #endif + #ifdef SSL_R_SSL_SESSION_VERSION_MISMATCH + {"SSL_SESSION_VERSION_MISMATCH", ERR_LIB_SSL, SSL_R_SSL_SESSION_VERSION_MISMATCH}, + #else + {"SSL_SESSION_VERSION_MISMATCH", 20, 210}, + #endif + #ifdef SSL_R_STILL_IN_INIT + {"STILL_IN_INIT", ERR_LIB_SSL, SSL_R_STILL_IN_INIT}, + #else + {"STILL_IN_INIT", 20, 121}, + #endif + #ifdef SSL_R_STREAM_COUNT_LIMITED + {"STREAM_COUNT_LIMITED", ERR_LIB_SSL, SSL_R_STREAM_COUNT_LIMITED}, + #else + {"STREAM_COUNT_LIMITED", 20, 411}, + #endif + #ifdef SSL_R_STREAM_FINISHED + {"STREAM_FINISHED", ERR_LIB_SSL, SSL_R_STREAM_FINISHED}, + #else + {"STREAM_FINISHED", 20, 365}, + #endif + #ifdef SSL_R_STREAM_RECV_ONLY + {"STREAM_RECV_ONLY", ERR_LIB_SSL, SSL_R_STREAM_RECV_ONLY}, + #else + {"STREAM_RECV_ONLY", 20, 366}, + #endif + #ifdef SSL_R_STREAM_RESET + {"STREAM_RESET", ERR_LIB_SSL, SSL_R_STREAM_RESET}, + #else + {"STREAM_RESET", 20, 375}, + #endif + #ifdef SSL_R_STREAM_SEND_ONLY + {"STREAM_SEND_ONLY", ERR_LIB_SSL, SSL_R_STREAM_SEND_ONLY}, + #else + {"STREAM_SEND_ONLY", 20, 379}, + #endif + #ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED + {"TLSV13_ALERT_CERTIFICATE_REQUIRED", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED}, + #else + {"TLSV13_ALERT_CERTIFICATE_REQUIRED", 20, 1116}, + #endif + #ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED + {"TLSV13_ALERT_CERTIFICATE_REQUIRED", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED}, + #else + {"TLSV13_ALERT_CERTIFICATE_REQUIRED", 20, 1116}, + #endif + #ifdef SSL_R_TLSV13_ALERT_MISSING_EXTENSION + {"TLSV13_ALERT_MISSING_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_MISSING_EXTENSION}, + #else + {"TLSV13_ALERT_MISSING_EXTENSION", 20, 1109}, + #endif + #ifdef SSL_R_TLSV13_ALERT_MISSING_EXTENSION + {"TLSV13_ALERT_MISSING_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_MISSING_EXTENSION}, + #else + {"TLSV13_ALERT_MISSING_EXTENSION", 20, 1109}, + #endif + #ifdef SSL_R_TLSV1_ALERT_ACCESS_DENIED + {"TLSV1_ALERT_ACCESS_DENIED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_ACCESS_DENIED}, + #else + {"TLSV1_ALERT_ACCESS_DENIED", 20, 1049}, + #endif + #ifdef SSL_R_TLSV1_ALERT_ACCESS_DENIED + {"TLSV1_ALERT_ACCESS_DENIED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_ACCESS_DENIED}, + #else + {"TLSV1_ALERT_ACCESS_DENIED", 20, 1049}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECODE_ERROR + {"TLSV1_ALERT_DECODE_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECODE_ERROR}, + #else + {"TLSV1_ALERT_DECODE_ERROR", 20, 1050}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECODE_ERROR + {"TLSV1_ALERT_DECODE_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECODE_ERROR}, + #else + {"TLSV1_ALERT_DECODE_ERROR", 20, 1050}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECRYPTION_FAILED + {"TLSV1_ALERT_DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPTION_FAILED}, + #else + {"TLSV1_ALERT_DECRYPTION_FAILED", 20, 1021}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECRYPTION_FAILED + {"TLSV1_ALERT_DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPTION_FAILED}, + #else + {"TLSV1_ALERT_DECRYPTION_FAILED", 20, 1021}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECRYPT_ERROR + {"TLSV1_ALERT_DECRYPT_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPT_ERROR}, + #else + {"TLSV1_ALERT_DECRYPT_ERROR", 20, 1051}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECRYPT_ERROR + {"TLSV1_ALERT_DECRYPT_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPT_ERROR}, + #else + {"TLSV1_ALERT_DECRYPT_ERROR", 20, 1051}, + #endif + #ifdef SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION + {"TLSV1_ALERT_EXPORT_RESTRICTION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION}, + #else + {"TLSV1_ALERT_EXPORT_RESTRICTION", 20, 1060}, + #endif + #ifdef SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION + {"TLSV1_ALERT_EXPORT_RESTRICTION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION}, + #else + {"TLSV1_ALERT_EXPORT_RESTRICTION", 20, 1060}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK + {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK}, + #else + {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", 20, 1086}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK + {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK}, + #else + {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", 20, 1086}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY + {"TLSV1_ALERT_INSUFFICIENT_SECURITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY}, + #else + {"TLSV1_ALERT_INSUFFICIENT_SECURITY", 20, 1071}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY + {"TLSV1_ALERT_INSUFFICIENT_SECURITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY}, + #else + {"TLSV1_ALERT_INSUFFICIENT_SECURITY", 20, 1071}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INTERNAL_ERROR + {"TLSV1_ALERT_INTERNAL_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INTERNAL_ERROR}, + #else + {"TLSV1_ALERT_INTERNAL_ERROR", 20, 1080}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INTERNAL_ERROR + {"TLSV1_ALERT_INTERNAL_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INTERNAL_ERROR}, + #else + {"TLSV1_ALERT_INTERNAL_ERROR", 20, 1080}, + #endif + #ifdef SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL + {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL}, + #else + {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", 20, 1120}, + #endif + #ifdef SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL + {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL}, + #else + {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", 20, 1120}, + #endif + #ifdef SSL_R_TLSV1_ALERT_NO_RENEGOTIATION + {"TLSV1_ALERT_NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_RENEGOTIATION}, + #else + {"TLSV1_ALERT_NO_RENEGOTIATION", 20, 1100}, + #endif + #ifdef SSL_R_TLSV1_ALERT_NO_RENEGOTIATION + {"TLSV1_ALERT_NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_RENEGOTIATION}, + #else + {"TLSV1_ALERT_NO_RENEGOTIATION", 20, 1100}, + #endif + #ifdef SSL_R_TLSV1_ALERT_PROTOCOL_VERSION + {"TLSV1_ALERT_PROTOCOL_VERSION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_PROTOCOL_VERSION}, + #else + {"TLSV1_ALERT_PROTOCOL_VERSION", 20, 1070}, + #endif + #ifdef SSL_R_TLSV1_ALERT_PROTOCOL_VERSION + {"TLSV1_ALERT_PROTOCOL_VERSION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_PROTOCOL_VERSION}, + #else + {"TLSV1_ALERT_PROTOCOL_VERSION", 20, 1070}, + #endif + #ifdef SSL_R_TLSV1_ALERT_RECORD_OVERFLOW + {"TLSV1_ALERT_RECORD_OVERFLOW", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_RECORD_OVERFLOW}, + #else + {"TLSV1_ALERT_RECORD_OVERFLOW", 20, 1022}, + #endif + #ifdef SSL_R_TLSV1_ALERT_RECORD_OVERFLOW + {"TLSV1_ALERT_RECORD_OVERFLOW", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_RECORD_OVERFLOW}, + #else + {"TLSV1_ALERT_RECORD_OVERFLOW", 20, 1022}, + #endif + #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA + {"TLSV1_ALERT_UNKNOWN_CA", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_CA}, + #else + {"TLSV1_ALERT_UNKNOWN_CA", 20, 1048}, + #endif + #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA + {"TLSV1_ALERT_UNKNOWN_CA", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_CA}, + #else + {"TLSV1_ALERT_UNKNOWN_CA", 20, 1048}, + #endif + #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY + {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY}, + #else + {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", 20, 1115}, + #endif + #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY + {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY}, + #else + {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", 20, 1115}, + #endif + #ifdef SSL_R_TLSV1_ALERT_USER_CANCELLED + {"TLSV1_ALERT_USER_CANCELLED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_USER_CANCELLED}, + #else + {"TLSV1_ALERT_USER_CANCELLED", 20, 1090}, + #endif + #ifdef SSL_R_TLSV1_ALERT_USER_CANCELLED + {"TLSV1_ALERT_USER_CANCELLED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_USER_CANCELLED}, + #else + {"TLSV1_ALERT_USER_CANCELLED", 20, 1090}, + #endif + #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE + {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE}, + #else + {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", 20, 1114}, + #endif + #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE + {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE}, + #else + {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", 20, 1114}, + #endif + #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE + {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE}, + #else + {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", 20, 1113}, + #endif + #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE + {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE}, + #else + {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", 20, 1113}, + #endif + #ifdef SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE + {"TLSV1_CERTIFICATE_UNOBTAINABLE", ERR_LIB_SSL, SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE}, + #else + {"TLSV1_CERTIFICATE_UNOBTAINABLE", 20, 1111}, + #endif + #ifdef SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE + {"TLSV1_CERTIFICATE_UNOBTAINABLE", ERR_LIB_SSL, SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE}, + #else + {"TLSV1_CERTIFICATE_UNOBTAINABLE", 20, 1111}, + #endif + #ifdef SSL_R_TLSV1_UNRECOGNIZED_NAME + {"TLSV1_UNRECOGNIZED_NAME", ERR_LIB_SSL, SSL_R_TLSV1_UNRECOGNIZED_NAME}, + #else + {"TLSV1_UNRECOGNIZED_NAME", 20, 1112}, + #endif + #ifdef SSL_R_TLSV1_UNRECOGNIZED_NAME + {"TLSV1_UNRECOGNIZED_NAME", ERR_LIB_SSL, SSL_R_TLSV1_UNRECOGNIZED_NAME}, + #else + {"TLSV1_UNRECOGNIZED_NAME", 20, 1112}, + #endif + #ifdef SSL_R_TLSV1_UNSUPPORTED_EXTENSION + {"TLSV1_UNSUPPORTED_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV1_UNSUPPORTED_EXTENSION}, + #else + {"TLSV1_UNSUPPORTED_EXTENSION", 20, 1110}, + #endif + #ifdef SSL_R_TLSV1_UNSUPPORTED_EXTENSION + {"TLSV1_UNSUPPORTED_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV1_UNSUPPORTED_EXTENSION}, + #else + {"TLSV1_UNSUPPORTED_EXTENSION", 20, 1110}, + #endif + #ifdef SSL_R_TLS_ILLEGAL_EXPORTER_LABEL + {"TLS_ILLEGAL_EXPORTER_LABEL", ERR_LIB_SSL, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL}, + #else + {"TLS_ILLEGAL_EXPORTER_LABEL", 20, 367}, + #endif + #ifdef SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST + {"TLS_INVALID_ECPOINTFORMAT_LIST", ERR_LIB_SSL, SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST}, + #else + {"TLS_INVALID_ECPOINTFORMAT_LIST", 20, 157}, + #endif + #ifdef SSL_R_TOO_MANY_KEY_UPDATES + {"TOO_MANY_KEY_UPDATES", ERR_LIB_SSL, SSL_R_TOO_MANY_KEY_UPDATES}, + #else + {"TOO_MANY_KEY_UPDATES", 20, 132}, + #endif + #ifdef SSL_R_TOO_MANY_WARN_ALERTS + {"TOO_MANY_WARN_ALERTS", ERR_LIB_SSL, SSL_R_TOO_MANY_WARN_ALERTS}, + #else + {"TOO_MANY_WARN_ALERTS", 20, 409}, + #endif + #ifdef SSL_R_TOO_MUCH_EARLY_DATA + {"TOO_MUCH_EARLY_DATA", ERR_LIB_SSL, SSL_R_TOO_MUCH_EARLY_DATA}, + #else + {"TOO_MUCH_EARLY_DATA", 20, 164}, + #endif + #ifdef SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS + {"UNABLE_TO_FIND_ECDH_PARAMETERS", ERR_LIB_SSL, SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS}, + #else + {"UNABLE_TO_FIND_ECDH_PARAMETERS", 20, 314}, + #endif + #ifdef SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS + {"UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS", ERR_LIB_SSL, SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS}, + #else + {"UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS", 20, 239}, + #endif + #ifdef SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES + {"UNABLE_TO_LOAD_SSL3_MD5_ROUTINES", ERR_LIB_SSL, SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES}, + #else + {"UNABLE_TO_LOAD_SSL3_MD5_ROUTINES", 20, 242}, + #endif + #ifdef SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES + {"UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES", ERR_LIB_SSL, SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES}, + #else + {"UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES", 20, 243}, + #endif + #ifdef SSL_R_UNEXPECTED_CCS_MESSAGE + {"UNEXPECTED_CCS_MESSAGE", ERR_LIB_SSL, SSL_R_UNEXPECTED_CCS_MESSAGE}, + #else + {"UNEXPECTED_CCS_MESSAGE", 20, 262}, + #endif + #ifdef SSL_R_UNEXPECTED_END_OF_EARLY_DATA + {"UNEXPECTED_END_OF_EARLY_DATA", ERR_LIB_SSL, SSL_R_UNEXPECTED_END_OF_EARLY_DATA}, + #else + {"UNEXPECTED_END_OF_EARLY_DATA", 20, 178}, + #endif + #ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING + {"UNEXPECTED_EOF_WHILE_READING", ERR_LIB_SSL, SSL_R_UNEXPECTED_EOF_WHILE_READING}, + #else + {"UNEXPECTED_EOF_WHILE_READING", 20, 294}, + #endif + #ifdef SSL_R_UNEXPECTED_MESSAGE + {"UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_UNEXPECTED_MESSAGE}, + #else + {"UNEXPECTED_MESSAGE", 20, 244}, + #endif + #ifdef SSL_R_UNEXPECTED_RECORD + {"UNEXPECTED_RECORD", ERR_LIB_SSL, SSL_R_UNEXPECTED_RECORD}, + #else + {"UNEXPECTED_RECORD", 20, 245}, + #endif + #ifdef SSL_R_UNINITIALIZED + {"UNINITIALIZED", ERR_LIB_SSL, SSL_R_UNINITIALIZED}, + #else + {"UNINITIALIZED", 20, 276}, + #endif + #ifdef SSL_R_UNKNOWN_ALERT_TYPE + {"UNKNOWN_ALERT_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_ALERT_TYPE}, + #else + {"UNKNOWN_ALERT_TYPE", 20, 246}, + #endif + #ifdef SSL_R_UNKNOWN_CERTIFICATE_TYPE + {"UNKNOWN_CERTIFICATE_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE}, + #else + {"UNKNOWN_CERTIFICATE_TYPE", 20, 247}, + #endif + #ifdef SSL_R_UNKNOWN_CIPHER_RETURNED + {"UNKNOWN_CIPHER_RETURNED", ERR_LIB_SSL, SSL_R_UNKNOWN_CIPHER_RETURNED}, + #else + {"UNKNOWN_CIPHER_RETURNED", 20, 248}, + #endif + #ifdef SSL_R_UNKNOWN_CIPHER_TYPE + {"UNKNOWN_CIPHER_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_CIPHER_TYPE}, + #else + {"UNKNOWN_CIPHER_TYPE", 20, 249}, + #endif + #ifdef SSL_R_UNKNOWN_CMD_NAME + {"UNKNOWN_CMD_NAME", ERR_LIB_SSL, SSL_R_UNKNOWN_CMD_NAME}, + #else + {"UNKNOWN_CMD_NAME", 20, 386}, + #endif + #ifdef SSL_R_UNKNOWN_COMMAND + {"UNKNOWN_COMMAND", ERR_LIB_SSL, SSL_R_UNKNOWN_COMMAND}, + #else + {"UNKNOWN_COMMAND", 20, 139}, + #endif + #ifdef SSL_R_UNKNOWN_DIGEST + {"UNKNOWN_DIGEST", ERR_LIB_SSL, SSL_R_UNKNOWN_DIGEST}, + #else + {"UNKNOWN_DIGEST", 20, 368}, + #endif + #ifdef SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE + {"UNKNOWN_KEY_EXCHANGE_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE}, + #else + {"UNKNOWN_KEY_EXCHANGE_TYPE", 20, 250}, + #endif + #ifdef SSL_R_UNKNOWN_MANDATORY_PARAMETER + {"UNKNOWN_MANDATORY_PARAMETER", ERR_LIB_SSL, SSL_R_UNKNOWN_MANDATORY_PARAMETER}, + #else + {"UNKNOWN_MANDATORY_PARAMETER", 20, 323}, + #endif + #ifdef SSL_R_UNKNOWN_PKEY_TYPE + {"UNKNOWN_PKEY_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_PKEY_TYPE}, + #else + {"UNKNOWN_PKEY_TYPE", 20, 251}, + #endif + #ifdef SSL_R_UNKNOWN_PROTOCOL + {"UNKNOWN_PROTOCOL", ERR_LIB_SSL, SSL_R_UNKNOWN_PROTOCOL}, + #else + {"UNKNOWN_PROTOCOL", 20, 252}, + #endif + #ifdef SSL_R_UNKNOWN_SSL_VERSION + {"UNKNOWN_SSL_VERSION", ERR_LIB_SSL, SSL_R_UNKNOWN_SSL_VERSION}, + #else + {"UNKNOWN_SSL_VERSION", 20, 254}, + #endif + #ifdef SSL_R_UNKNOWN_STATE + {"UNKNOWN_STATE", ERR_LIB_SSL, SSL_R_UNKNOWN_STATE}, + #else + {"UNKNOWN_STATE", 20, 255}, + #endif + #ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED + {"UNSAFE_LEGACY_RENEGOTIATION_DISABLED", ERR_LIB_SSL, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED}, + #else + {"UNSAFE_LEGACY_RENEGOTIATION_DISABLED", 20, 338}, + #endif + #ifdef SSL_R_UNSOLICITED_EXTENSION + {"UNSOLICITED_EXTENSION", ERR_LIB_SSL, SSL_R_UNSOLICITED_EXTENSION}, + #else + {"UNSOLICITED_EXTENSION", 20, 217}, + #endif + #ifdef SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM + {"UNSUPPORTED_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM}, + #else + {"UNSUPPORTED_COMPRESSION_ALGORITHM", 20, 257}, + #endif + #ifdef SSL_R_UNSUPPORTED_CONFIG_VALUE + {"UNSUPPORTED_CONFIG_VALUE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_CONFIG_VALUE}, + #else + {"UNSUPPORTED_CONFIG_VALUE", 20, 414}, + #endif + #ifdef SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS + {"UNSUPPORTED_CONFIG_VALUE_CLASS", ERR_LIB_SSL, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS}, + #else + {"UNSUPPORTED_CONFIG_VALUE_CLASS", 20, 415}, + #endif + #ifdef SSL_R_UNSUPPORTED_CONFIG_VALUE_OP + {"UNSUPPORTED_CONFIG_VALUE_OP", ERR_LIB_SSL, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP}, + #else + {"UNSUPPORTED_CONFIG_VALUE_OP", 20, 416}, + #endif + #ifdef SSL_R_UNSUPPORTED_ELLIPTIC_CURVE + {"UNSUPPORTED_ELLIPTIC_CURVE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE}, + #else + {"UNSUPPORTED_ELLIPTIC_CURVE", 20, 315}, + #endif + #ifdef SSL_R_UNSUPPORTED_PROTOCOL + {"UNSUPPORTED_PROTOCOL", ERR_LIB_SSL, SSL_R_UNSUPPORTED_PROTOCOL}, + #else + {"UNSUPPORTED_PROTOCOL", 20, 258}, + #endif + #ifdef SSL_R_UNSUPPORTED_SSL_VERSION + {"UNSUPPORTED_SSL_VERSION", ERR_LIB_SSL, SSL_R_UNSUPPORTED_SSL_VERSION}, + #else + {"UNSUPPORTED_SSL_VERSION", 20, 259}, + #endif + #ifdef SSL_R_UNSUPPORTED_STATUS_TYPE + {"UNSUPPORTED_STATUS_TYPE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_STATUS_TYPE}, + #else + {"UNSUPPORTED_STATUS_TYPE", 20, 329}, + #endif + #ifdef SSL_R_UNSUPPORTED_WRITE_FLAG + {"UNSUPPORTED_WRITE_FLAG", ERR_LIB_SSL, SSL_R_UNSUPPORTED_WRITE_FLAG}, + #else + {"UNSUPPORTED_WRITE_FLAG", 20, 412}, + #endif + #ifdef SSL_R_USE_SRTP_NOT_NEGOTIATED + {"USE_SRTP_NOT_NEGOTIATED", ERR_LIB_SSL, SSL_R_USE_SRTP_NOT_NEGOTIATED}, + #else + {"USE_SRTP_NOT_NEGOTIATED", 20, 369}, + #endif + #ifdef SSL_R_VERSION_TOO_HIGH + {"VERSION_TOO_HIGH", ERR_LIB_SSL, SSL_R_VERSION_TOO_HIGH}, + #else + {"VERSION_TOO_HIGH", 20, 166}, + #endif + #ifdef SSL_R_VERSION_TOO_LOW + {"VERSION_TOO_LOW", ERR_LIB_SSL, SSL_R_VERSION_TOO_LOW}, + #else + {"VERSION_TOO_LOW", 20, 396}, + #endif + #ifdef SSL_R_WRONG_CERTIFICATE_TYPE + {"WRONG_CERTIFICATE_TYPE", ERR_LIB_SSL, SSL_R_WRONG_CERTIFICATE_TYPE}, + #else + {"WRONG_CERTIFICATE_TYPE", 20, 383}, + #endif + #ifdef SSL_R_WRONG_CIPHER_RETURNED + {"WRONG_CIPHER_RETURNED", ERR_LIB_SSL, SSL_R_WRONG_CIPHER_RETURNED}, + #else + {"WRONG_CIPHER_RETURNED", 20, 261}, + #endif + #ifdef SSL_R_WRONG_CURVE + {"WRONG_CURVE", ERR_LIB_SSL, SSL_R_WRONG_CURVE}, + #else + {"WRONG_CURVE", 20, 378}, + #endif + #ifdef SSL_R_WRONG_RPK_TYPE + {"WRONG_RPK_TYPE", ERR_LIB_SSL, SSL_R_WRONG_RPK_TYPE}, + #else + {"WRONG_RPK_TYPE", 20, 351}, + #endif + #ifdef SSL_R_WRONG_SIGNATURE_LENGTH + {"WRONG_SIGNATURE_LENGTH", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_LENGTH}, + #else + {"WRONG_SIGNATURE_LENGTH", 20, 264}, + #endif + #ifdef SSL_R_WRONG_SIGNATURE_SIZE + {"WRONG_SIGNATURE_SIZE", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_SIZE}, + #else + {"WRONG_SIGNATURE_SIZE", 20, 265}, + #endif + #ifdef SSL_R_WRONG_SIGNATURE_TYPE + {"WRONG_SIGNATURE_TYPE", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_TYPE}, + #else + {"WRONG_SIGNATURE_TYPE", 20, 370}, + #endif + #ifdef SSL_R_WRONG_SSL_VERSION + {"WRONG_SSL_VERSION", ERR_LIB_SSL, SSL_R_WRONG_SSL_VERSION}, + #else + {"WRONG_SSL_VERSION", 20, 266}, + #endif + #ifdef SSL_R_WRONG_VERSION_NUMBER + {"WRONG_VERSION_NUMBER", ERR_LIB_SSL, SSL_R_WRONG_VERSION_NUMBER}, + #else + {"WRONG_VERSION_NUMBER", 20, 267}, + #endif + #ifdef SSL_R_X509_LIB + {"X509_LIB", ERR_LIB_SSL, SSL_R_X509_LIB}, + #else + {"X509_LIB", 20, 268}, + #endif + #ifdef SSL_R_X509_VERIFICATION_SETUP_PROBLEMS + {"X509_VERIFICATION_SETUP_PROBLEMS", ERR_LIB_SSL, SSL_R_X509_VERIFICATION_SETUP_PROBLEMS}, + #else + {"X509_VERIFICATION_SETUP_PROBLEMS", 20, 269}, + #endif + #ifdef TS_R_BAD_PKCS7_TYPE + {"BAD_PKCS7_TYPE", ERR_LIB_TS, TS_R_BAD_PKCS7_TYPE}, + #else + {"BAD_PKCS7_TYPE", 47, 132}, + #endif + #ifdef TS_R_BAD_TYPE + {"BAD_TYPE", ERR_LIB_TS, TS_R_BAD_TYPE}, + #else + {"BAD_TYPE", 47, 133}, + #endif + #ifdef TS_R_CANNOT_LOAD_CERT + {"CANNOT_LOAD_CERT", ERR_LIB_TS, TS_R_CANNOT_LOAD_CERT}, + #else + {"CANNOT_LOAD_CERT", 47, 137}, + #endif + #ifdef TS_R_CANNOT_LOAD_KEY + {"CANNOT_LOAD_KEY", ERR_LIB_TS, TS_R_CANNOT_LOAD_KEY}, + #else + {"CANNOT_LOAD_KEY", 47, 138}, + #endif + #ifdef TS_R_CERTIFICATE_VERIFY_ERROR + {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_TS, TS_R_CERTIFICATE_VERIFY_ERROR}, + #else + {"CERTIFICATE_VERIFY_ERROR", 47, 100}, + #endif + #ifdef TS_R_COULD_NOT_SET_ENGINE + {"COULD_NOT_SET_ENGINE", ERR_LIB_TS, TS_R_COULD_NOT_SET_ENGINE}, + #else + {"COULD_NOT_SET_ENGINE", 47, 127}, + #endif + #ifdef TS_R_COULD_NOT_SET_TIME + {"COULD_NOT_SET_TIME", ERR_LIB_TS, TS_R_COULD_NOT_SET_TIME}, + #else + {"COULD_NOT_SET_TIME", 47, 115}, + #endif + #ifdef TS_R_DETACHED_CONTENT + {"DETACHED_CONTENT", ERR_LIB_TS, TS_R_DETACHED_CONTENT}, + #else + {"DETACHED_CONTENT", 47, 134}, + #endif + #ifdef TS_R_ESS_ADD_SIGNING_CERT_ERROR + {"ESS_ADD_SIGNING_CERT_ERROR", ERR_LIB_TS, TS_R_ESS_ADD_SIGNING_CERT_ERROR}, + #else + {"ESS_ADD_SIGNING_CERT_ERROR", 47, 116}, + #endif + #ifdef TS_R_ESS_ADD_SIGNING_CERT_V2_ERROR + {"ESS_ADD_SIGNING_CERT_V2_ERROR", ERR_LIB_TS, TS_R_ESS_ADD_SIGNING_CERT_V2_ERROR}, + #else + {"ESS_ADD_SIGNING_CERT_V2_ERROR", 47, 139}, + #endif + #ifdef TS_R_ESS_SIGNING_CERTIFICATE_ERROR + {"ESS_SIGNING_CERTIFICATE_ERROR", ERR_LIB_TS, TS_R_ESS_SIGNING_CERTIFICATE_ERROR}, + #else + {"ESS_SIGNING_CERTIFICATE_ERROR", 47, 101}, + #endif + #ifdef TS_R_INVALID_NULL_POINTER + {"INVALID_NULL_POINTER", ERR_LIB_TS, TS_R_INVALID_NULL_POINTER}, + #else + {"INVALID_NULL_POINTER", 47, 102}, + #endif + #ifdef TS_R_INVALID_SIGNER_CERTIFICATE_PURPOSE + {"INVALID_SIGNER_CERTIFICATE_PURPOSE", ERR_LIB_TS, TS_R_INVALID_SIGNER_CERTIFICATE_PURPOSE}, + #else + {"INVALID_SIGNER_CERTIFICATE_PURPOSE", 47, 117}, + #endif + #ifdef TS_R_MESSAGE_IMPRINT_MISMATCH + {"MESSAGE_IMPRINT_MISMATCH", ERR_LIB_TS, TS_R_MESSAGE_IMPRINT_MISMATCH}, + #else + {"MESSAGE_IMPRINT_MISMATCH", 47, 103}, + #endif + #ifdef TS_R_NONCE_MISMATCH + {"NONCE_MISMATCH", ERR_LIB_TS, TS_R_NONCE_MISMATCH}, + #else + {"NONCE_MISMATCH", 47, 104}, + #endif + #ifdef TS_R_NONCE_NOT_RETURNED + {"NONCE_NOT_RETURNED", ERR_LIB_TS, TS_R_NONCE_NOT_RETURNED}, + #else + {"NONCE_NOT_RETURNED", 47, 105}, + #endif + #ifdef TS_R_NO_CONTENT + {"NO_CONTENT", ERR_LIB_TS, TS_R_NO_CONTENT}, + #else + {"NO_CONTENT", 47, 106}, + #endif + #ifdef TS_R_NO_TIME_STAMP_TOKEN + {"NO_TIME_STAMP_TOKEN", ERR_LIB_TS, TS_R_NO_TIME_STAMP_TOKEN}, + #else + {"NO_TIME_STAMP_TOKEN", 47, 107}, + #endif + #ifdef TS_R_PKCS7_ADD_SIGNATURE_ERROR + {"PKCS7_ADD_SIGNATURE_ERROR", ERR_LIB_TS, TS_R_PKCS7_ADD_SIGNATURE_ERROR}, + #else + {"PKCS7_ADD_SIGNATURE_ERROR", 47, 118}, + #endif + #ifdef TS_R_PKCS7_ADD_SIGNED_ATTR_ERROR + {"PKCS7_ADD_SIGNED_ATTR_ERROR", ERR_LIB_TS, TS_R_PKCS7_ADD_SIGNED_ATTR_ERROR}, + #else + {"PKCS7_ADD_SIGNED_ATTR_ERROR", 47, 119}, + #endif + #ifdef TS_R_PKCS7_TO_TS_TST_INFO_FAILED + {"PKCS7_TO_TS_TST_INFO_FAILED", ERR_LIB_TS, TS_R_PKCS7_TO_TS_TST_INFO_FAILED}, + #else + {"PKCS7_TO_TS_TST_INFO_FAILED", 47, 129}, + #endif + #ifdef TS_R_POLICY_MISMATCH + {"POLICY_MISMATCH", ERR_LIB_TS, TS_R_POLICY_MISMATCH}, + #else + {"POLICY_MISMATCH", 47, 108}, + #endif + #ifdef TS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_TS, TS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, + #else + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 47, 120}, + #endif + #ifdef TS_R_RESPONSE_SETUP_ERROR + {"RESPONSE_SETUP_ERROR", ERR_LIB_TS, TS_R_RESPONSE_SETUP_ERROR}, + #else + {"RESPONSE_SETUP_ERROR", 47, 121}, + #endif + #ifdef TS_R_SIGNATURE_FAILURE + {"SIGNATURE_FAILURE", ERR_LIB_TS, TS_R_SIGNATURE_FAILURE}, + #else + {"SIGNATURE_FAILURE", 47, 109}, + #endif + #ifdef TS_R_THERE_MUST_BE_ONE_SIGNER + {"THERE_MUST_BE_ONE_SIGNER", ERR_LIB_TS, TS_R_THERE_MUST_BE_ONE_SIGNER}, + #else + {"THERE_MUST_BE_ONE_SIGNER", 47, 110}, + #endif + #ifdef TS_R_TIME_SYSCALL_ERROR + {"TIME_SYSCALL_ERROR", ERR_LIB_TS, TS_R_TIME_SYSCALL_ERROR}, + #else + {"TIME_SYSCALL_ERROR", 47, 122}, + #endif + #ifdef TS_R_TOKEN_NOT_PRESENT + {"TOKEN_NOT_PRESENT", ERR_LIB_TS, TS_R_TOKEN_NOT_PRESENT}, + #else + {"TOKEN_NOT_PRESENT", 47, 130}, + #endif + #ifdef TS_R_TOKEN_PRESENT + {"TOKEN_PRESENT", ERR_LIB_TS, TS_R_TOKEN_PRESENT}, + #else + {"TOKEN_PRESENT", 47, 131}, + #endif + #ifdef TS_R_TSA_NAME_MISMATCH + {"TSA_NAME_MISMATCH", ERR_LIB_TS, TS_R_TSA_NAME_MISMATCH}, + #else + {"TSA_NAME_MISMATCH", 47, 111}, + #endif + #ifdef TS_R_TSA_UNTRUSTED + {"TSA_UNTRUSTED", ERR_LIB_TS, TS_R_TSA_UNTRUSTED}, + #else + {"TSA_UNTRUSTED", 47, 112}, + #endif + #ifdef TS_R_TST_INFO_SETUP_ERROR + {"TST_INFO_SETUP_ERROR", ERR_LIB_TS, TS_R_TST_INFO_SETUP_ERROR}, + #else + {"TST_INFO_SETUP_ERROR", 47, 123}, + #endif + #ifdef TS_R_TS_DATASIGN + {"TS_DATASIGN", ERR_LIB_TS, TS_R_TS_DATASIGN}, + #else + {"TS_DATASIGN", 47, 124}, + #endif + #ifdef TS_R_UNACCEPTABLE_POLICY + {"UNACCEPTABLE_POLICY", ERR_LIB_TS, TS_R_UNACCEPTABLE_POLICY}, + #else + {"UNACCEPTABLE_POLICY", 47, 125}, + #endif + #ifdef TS_R_UNSUPPORTED_MD_ALGORITHM + {"UNSUPPORTED_MD_ALGORITHM", ERR_LIB_TS, TS_R_UNSUPPORTED_MD_ALGORITHM}, + #else + {"UNSUPPORTED_MD_ALGORITHM", 47, 126}, + #endif + #ifdef TS_R_UNSUPPORTED_VERSION + {"UNSUPPORTED_VERSION", ERR_LIB_TS, TS_R_UNSUPPORTED_VERSION}, + #else + {"UNSUPPORTED_VERSION", 47, 113}, + #endif + #ifdef TS_R_VAR_BAD_VALUE + {"VAR_BAD_VALUE", ERR_LIB_TS, TS_R_VAR_BAD_VALUE}, + #else + {"VAR_BAD_VALUE", 47, 135}, + #endif + #ifdef TS_R_VAR_LOOKUP_FAILURE + {"VAR_LOOKUP_FAILURE", ERR_LIB_TS, TS_R_VAR_LOOKUP_FAILURE}, + #else + {"VAR_LOOKUP_FAILURE", 47, 136}, + #endif + #ifdef TS_R_WRONG_CONTENT_TYPE + {"WRONG_CONTENT_TYPE", ERR_LIB_TS, TS_R_WRONG_CONTENT_TYPE}, + #else + {"WRONG_CONTENT_TYPE", 47, 114}, + #endif + #ifdef UI_R_COMMON_OK_AND_CANCEL_CHARACTERS + {"COMMON_OK_AND_CANCEL_CHARACTERS", ERR_LIB_UI, UI_R_COMMON_OK_AND_CANCEL_CHARACTERS}, + #else + {"COMMON_OK_AND_CANCEL_CHARACTERS", 40, 104}, + #endif + #ifdef UI_R_INDEX_TOO_LARGE + {"INDEX_TOO_LARGE", ERR_LIB_UI, UI_R_INDEX_TOO_LARGE}, + #else + {"INDEX_TOO_LARGE", 40, 102}, + #endif + #ifdef UI_R_INDEX_TOO_SMALL + {"INDEX_TOO_SMALL", ERR_LIB_UI, UI_R_INDEX_TOO_SMALL}, + #else + {"INDEX_TOO_SMALL", 40, 103}, + #endif + #ifdef UI_R_NO_RESULT_BUFFER + {"NO_RESULT_BUFFER", ERR_LIB_UI, UI_R_NO_RESULT_BUFFER}, + #else + {"NO_RESULT_BUFFER", 40, 105}, + #endif + #ifdef UI_R_PROCESSING_ERROR + {"PROCESSING_ERROR", ERR_LIB_UI, UI_R_PROCESSING_ERROR}, + #else + {"PROCESSING_ERROR", 40, 107}, + #endif + #ifdef UI_R_RESULT_TOO_LARGE + {"RESULT_TOO_LARGE", ERR_LIB_UI, UI_R_RESULT_TOO_LARGE}, + #else + {"RESULT_TOO_LARGE", 40, 100}, + #endif + #ifdef UI_R_RESULT_TOO_SMALL + {"RESULT_TOO_SMALL", ERR_LIB_UI, UI_R_RESULT_TOO_SMALL}, + #else + {"RESULT_TOO_SMALL", 40, 101}, + #endif + #ifdef UI_R_SYSASSIGN_ERROR + {"SYSASSIGN_ERROR", ERR_LIB_UI, UI_R_SYSASSIGN_ERROR}, + #else + {"SYSASSIGN_ERROR", 40, 109}, + #endif + #ifdef UI_R_SYSDASSGN_ERROR + {"SYSDASSGN_ERROR", ERR_LIB_UI, UI_R_SYSDASSGN_ERROR}, + #else + {"SYSDASSGN_ERROR", 40, 110}, + #endif + #ifdef UI_R_SYSQIOW_ERROR + {"SYSQIOW_ERROR", ERR_LIB_UI, UI_R_SYSQIOW_ERROR}, + #else + {"SYSQIOW_ERROR", 40, 111}, + #endif + #ifdef UI_R_UNKNOWN_CONTROL_COMMAND + {"UNKNOWN_CONTROL_COMMAND", ERR_LIB_UI, UI_R_UNKNOWN_CONTROL_COMMAND}, + #else + {"UNKNOWN_CONTROL_COMMAND", 40, 106}, + #endif + #ifdef UI_R_UNKNOWN_TTYGET_ERRNO_VALUE + {"UNKNOWN_TTYGET_ERRNO_VALUE", ERR_LIB_UI, UI_R_UNKNOWN_TTYGET_ERRNO_VALUE}, + #else + {"UNKNOWN_TTYGET_ERRNO_VALUE", 40, 108}, + #endif + #ifdef UI_R_USER_DATA_DUPLICATION_UNSUPPORTED + {"USER_DATA_DUPLICATION_UNSUPPORTED", ERR_LIB_UI, UI_R_USER_DATA_DUPLICATION_UNSUPPORTED}, + #else + {"USER_DATA_DUPLICATION_UNSUPPORTED", 40, 112}, + #endif + #ifdef X509V3_R_BAD_IP_ADDRESS + {"BAD_IP_ADDRESS", ERR_LIB_X509V3, X509V3_R_BAD_IP_ADDRESS}, + #else + {"BAD_IP_ADDRESS", 34, 118}, + #endif + #ifdef X509V3_R_BAD_OBJECT + {"BAD_OBJECT", ERR_LIB_X509V3, X509V3_R_BAD_OBJECT}, + #else + {"BAD_OBJECT", 34, 119}, + #endif + #ifdef X509V3_R_BAD_OPTION + {"BAD_OPTION", ERR_LIB_X509V3, X509V3_R_BAD_OPTION}, + #else + {"BAD_OPTION", 34, 170}, + #endif + #ifdef X509V3_R_BAD_VALUE + {"BAD_VALUE", ERR_LIB_X509V3, X509V3_R_BAD_VALUE}, + #else + {"BAD_VALUE", 34, 171}, + #endif + #ifdef X509V3_R_BN_DEC2BN_ERROR + {"BN_DEC2BN_ERROR", ERR_LIB_X509V3, X509V3_R_BN_DEC2BN_ERROR}, + #else + {"BN_DEC2BN_ERROR", 34, 100}, + #endif + #ifdef X509V3_R_BN_TO_ASN1_INTEGER_ERROR + {"BN_TO_ASN1_INTEGER_ERROR", ERR_LIB_X509V3, X509V3_R_BN_TO_ASN1_INTEGER_ERROR}, + #else + {"BN_TO_ASN1_INTEGER_ERROR", 34, 101}, + #endif + #ifdef X509V3_R_DIRNAME_ERROR + {"DIRNAME_ERROR", ERR_LIB_X509V3, X509V3_R_DIRNAME_ERROR}, + #else + {"DIRNAME_ERROR", 34, 149}, + #endif + #ifdef X509V3_R_DISTPOINT_ALREADY_SET + {"DISTPOINT_ALREADY_SET", ERR_LIB_X509V3, X509V3_R_DISTPOINT_ALREADY_SET}, + #else + {"DISTPOINT_ALREADY_SET", 34, 160}, + #endif + #ifdef X509V3_R_DUPLICATE_ZONE_ID + {"DUPLICATE_ZONE_ID", ERR_LIB_X509V3, X509V3_R_DUPLICATE_ZONE_ID}, + #else + {"DUPLICATE_ZONE_ID", 34, 133}, + #endif + #ifdef X509V3_R_EMPTY_KEY_USAGE + {"EMPTY_KEY_USAGE", ERR_LIB_X509V3, X509V3_R_EMPTY_KEY_USAGE}, + #else + {"EMPTY_KEY_USAGE", 34, 169}, + #endif + #ifdef X509V3_R_ERROR_CONVERTING_ZONE + {"ERROR_CONVERTING_ZONE", ERR_LIB_X509V3, X509V3_R_ERROR_CONVERTING_ZONE}, + #else + {"ERROR_CONVERTING_ZONE", 34, 131}, + #endif + #ifdef X509V3_R_ERROR_CREATING_EXTENSION + {"ERROR_CREATING_EXTENSION", ERR_LIB_X509V3, X509V3_R_ERROR_CREATING_EXTENSION}, + #else + {"ERROR_CREATING_EXTENSION", 34, 144}, + #endif + #ifdef X509V3_R_ERROR_IN_EXTENSION + {"ERROR_IN_EXTENSION", ERR_LIB_X509V3, X509V3_R_ERROR_IN_EXTENSION}, + #else + {"ERROR_IN_EXTENSION", 34, 128}, + #endif + #ifdef X509V3_R_EXPECTED_A_SECTION_NAME + {"EXPECTED_A_SECTION_NAME", ERR_LIB_X509V3, X509V3_R_EXPECTED_A_SECTION_NAME}, + #else + {"EXPECTED_A_SECTION_NAME", 34, 137}, + #endif + #ifdef X509V3_R_EXTENSION_EXISTS + {"EXTENSION_EXISTS", ERR_LIB_X509V3, X509V3_R_EXTENSION_EXISTS}, + #else + {"EXTENSION_EXISTS", 34, 145}, + #endif + #ifdef X509V3_R_EXTENSION_NAME_ERROR + {"EXTENSION_NAME_ERROR", ERR_LIB_X509V3, X509V3_R_EXTENSION_NAME_ERROR}, + #else + {"EXTENSION_NAME_ERROR", 34, 115}, + #endif + #ifdef X509V3_R_EXTENSION_NOT_FOUND + {"EXTENSION_NOT_FOUND", ERR_LIB_X509V3, X509V3_R_EXTENSION_NOT_FOUND}, + #else + {"EXTENSION_NOT_FOUND", 34, 102}, + #endif + #ifdef X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED + {"EXTENSION_SETTING_NOT_SUPPORTED", ERR_LIB_X509V3, X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED}, + #else + {"EXTENSION_SETTING_NOT_SUPPORTED", 34, 103}, + #endif + #ifdef X509V3_R_EXTENSION_VALUE_ERROR + {"EXTENSION_VALUE_ERROR", ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR}, + #else + {"EXTENSION_VALUE_ERROR", 34, 116}, + #endif + #ifdef X509V3_R_ILLEGAL_EMPTY_EXTENSION + {"ILLEGAL_EMPTY_EXTENSION", ERR_LIB_X509V3, X509V3_R_ILLEGAL_EMPTY_EXTENSION}, + #else + {"ILLEGAL_EMPTY_EXTENSION", 34, 151}, + #endif + #ifdef X509V3_R_INCORRECT_POLICY_SYNTAX_TAG + {"INCORRECT_POLICY_SYNTAX_TAG", ERR_LIB_X509V3, X509V3_R_INCORRECT_POLICY_SYNTAX_TAG}, + #else + {"INCORRECT_POLICY_SYNTAX_TAG", 34, 152}, + #endif + #ifdef X509V3_R_INVALID_ASNUMBER + {"INVALID_ASNUMBER", ERR_LIB_X509V3, X509V3_R_INVALID_ASNUMBER}, + #else + {"INVALID_ASNUMBER", 34, 162}, + #endif + #ifdef X509V3_R_INVALID_ASRANGE + {"INVALID_ASRANGE", ERR_LIB_X509V3, X509V3_R_INVALID_ASRANGE}, + #else + {"INVALID_ASRANGE", 34, 163}, + #endif + #ifdef X509V3_R_INVALID_BOOLEAN_STRING + {"INVALID_BOOLEAN_STRING", ERR_LIB_X509V3, X509V3_R_INVALID_BOOLEAN_STRING}, + #else + {"INVALID_BOOLEAN_STRING", 34, 104}, + #endif + #ifdef X509V3_R_INVALID_CERTIFICATE + {"INVALID_CERTIFICATE", ERR_LIB_X509V3, X509V3_R_INVALID_CERTIFICATE}, + #else + {"INVALID_CERTIFICATE", 34, 158}, + #endif + #ifdef X509V3_R_INVALID_EMPTY_NAME + {"INVALID_EMPTY_NAME", ERR_LIB_X509V3, X509V3_R_INVALID_EMPTY_NAME}, + #else + {"INVALID_EMPTY_NAME", 34, 108}, + #endif + #ifdef X509V3_R_INVALID_EXTENSION_STRING + {"INVALID_EXTENSION_STRING", ERR_LIB_X509V3, X509V3_R_INVALID_EXTENSION_STRING}, + #else + {"INVALID_EXTENSION_STRING", 34, 105}, + #endif + #ifdef X509V3_R_INVALID_INHERITANCE + {"INVALID_INHERITANCE", ERR_LIB_X509V3, X509V3_R_INVALID_INHERITANCE}, + #else + {"INVALID_INHERITANCE", 34, 165}, + #endif + #ifdef X509V3_R_INVALID_IPADDRESS + {"INVALID_IPADDRESS", ERR_LIB_X509V3, X509V3_R_INVALID_IPADDRESS}, + #else + {"INVALID_IPADDRESS", 34, 166}, + #endif + #ifdef X509V3_R_INVALID_MULTIPLE_RDNS + {"INVALID_MULTIPLE_RDNS", ERR_LIB_X509V3, X509V3_R_INVALID_MULTIPLE_RDNS}, + #else + {"INVALID_MULTIPLE_RDNS", 34, 161}, + #endif + #ifdef X509V3_R_INVALID_NAME + {"INVALID_NAME", ERR_LIB_X509V3, X509V3_R_INVALID_NAME}, + #else + {"INVALID_NAME", 34, 106}, + #endif + #ifdef X509V3_R_INVALID_NULL_ARGUMENT + {"INVALID_NULL_ARGUMENT", ERR_LIB_X509V3, X509V3_R_INVALID_NULL_ARGUMENT}, + #else + {"INVALID_NULL_ARGUMENT", 34, 107}, + #endif + #ifdef X509V3_R_INVALID_NULL_VALUE + {"INVALID_NULL_VALUE", ERR_LIB_X509V3, X509V3_R_INVALID_NULL_VALUE}, + #else + {"INVALID_NULL_VALUE", 34, 109}, + #endif + #ifdef X509V3_R_INVALID_NUMBER + {"INVALID_NUMBER", ERR_LIB_X509V3, X509V3_R_INVALID_NUMBER}, + #else + {"INVALID_NUMBER", 34, 140}, + #endif + #ifdef X509V3_R_INVALID_NUMBERS + {"INVALID_NUMBERS", ERR_LIB_X509V3, X509V3_R_INVALID_NUMBERS}, + #else + {"INVALID_NUMBERS", 34, 141}, + #endif + #ifdef X509V3_R_INVALID_OBJECT_IDENTIFIER + {"INVALID_OBJECT_IDENTIFIER", ERR_LIB_X509V3, X509V3_R_INVALID_OBJECT_IDENTIFIER}, + #else + {"INVALID_OBJECT_IDENTIFIER", 34, 110}, + #endif + #ifdef X509V3_R_INVALID_OPTION + {"INVALID_OPTION", ERR_LIB_X509V3, X509V3_R_INVALID_OPTION}, + #else + {"INVALID_OPTION", 34, 138}, + #endif + #ifdef X509V3_R_INVALID_POLICY_IDENTIFIER + {"INVALID_POLICY_IDENTIFIER", ERR_LIB_X509V3, X509V3_R_INVALID_POLICY_IDENTIFIER}, + #else + {"INVALID_POLICY_IDENTIFIER", 34, 134}, + #endif + #ifdef X509V3_R_INVALID_PROXY_POLICY_SETTING + {"INVALID_PROXY_POLICY_SETTING", ERR_LIB_X509V3, X509V3_R_INVALID_PROXY_POLICY_SETTING}, + #else + {"INVALID_PROXY_POLICY_SETTING", 34, 153}, + #endif + #ifdef X509V3_R_INVALID_PURPOSE + {"INVALID_PURPOSE", ERR_LIB_X509V3, X509V3_R_INVALID_PURPOSE}, + #else + {"INVALID_PURPOSE", 34, 146}, + #endif + #ifdef X509V3_R_INVALID_SAFI + {"INVALID_SAFI", ERR_LIB_X509V3, X509V3_R_INVALID_SAFI}, + #else + {"INVALID_SAFI", 34, 164}, + #endif + #ifdef X509V3_R_INVALID_SECTION + {"INVALID_SECTION", ERR_LIB_X509V3, X509V3_R_INVALID_SECTION}, + #else + {"INVALID_SECTION", 34, 135}, + #endif + #ifdef X509V3_R_INVALID_SYNTAX + {"INVALID_SYNTAX", ERR_LIB_X509V3, X509V3_R_INVALID_SYNTAX}, + #else + {"INVALID_SYNTAX", 34, 143}, + #endif + #ifdef X509V3_R_ISSUER_DECODE_ERROR + {"ISSUER_DECODE_ERROR", ERR_LIB_X509V3, X509V3_R_ISSUER_DECODE_ERROR}, + #else + {"ISSUER_DECODE_ERROR", 34, 126}, + #endif + #ifdef X509V3_R_MISSING_VALUE + {"MISSING_VALUE", ERR_LIB_X509V3, X509V3_R_MISSING_VALUE}, + #else + {"MISSING_VALUE", 34, 124}, + #endif + #ifdef X509V3_R_NEED_ORGANIZATION_AND_NUMBERS + {"NEED_ORGANIZATION_AND_NUMBERS", ERR_LIB_X509V3, X509V3_R_NEED_ORGANIZATION_AND_NUMBERS}, + #else + {"NEED_ORGANIZATION_AND_NUMBERS", 34, 142}, + #endif + #ifdef X509V3_R_NEGATIVE_PATHLEN + {"NEGATIVE_PATHLEN", ERR_LIB_X509V3, X509V3_R_NEGATIVE_PATHLEN}, + #else + {"NEGATIVE_PATHLEN", 34, 168}, + #endif + #ifdef X509V3_R_NO_CONFIG_DATABASE + {"NO_CONFIG_DATABASE", ERR_LIB_X509V3, X509V3_R_NO_CONFIG_DATABASE}, + #else + {"NO_CONFIG_DATABASE", 34, 136}, + #endif + #ifdef X509V3_R_NO_ISSUER_CERTIFICATE + {"NO_ISSUER_CERTIFICATE", ERR_LIB_X509V3, X509V3_R_NO_ISSUER_CERTIFICATE}, + #else + {"NO_ISSUER_CERTIFICATE", 34, 121}, + #endif + #ifdef X509V3_R_NO_ISSUER_DETAILS + {"NO_ISSUER_DETAILS", ERR_LIB_X509V3, X509V3_R_NO_ISSUER_DETAILS}, + #else + {"NO_ISSUER_DETAILS", 34, 127}, + #endif + #ifdef X509V3_R_NO_POLICY_IDENTIFIER + {"NO_POLICY_IDENTIFIER", ERR_LIB_X509V3, X509V3_R_NO_POLICY_IDENTIFIER}, + #else + {"NO_POLICY_IDENTIFIER", 34, 139}, + #endif + #ifdef X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED + {"NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED", ERR_LIB_X509V3, X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED}, + #else + {"NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED", 34, 154}, + #endif + #ifdef X509V3_R_NO_PUBLIC_KEY + {"NO_PUBLIC_KEY", ERR_LIB_X509V3, X509V3_R_NO_PUBLIC_KEY}, + #else + {"NO_PUBLIC_KEY", 34, 114}, + #endif + #ifdef X509V3_R_NO_SUBJECT_DETAILS + {"NO_SUBJECT_DETAILS", ERR_LIB_X509V3, X509V3_R_NO_SUBJECT_DETAILS}, + #else + {"NO_SUBJECT_DETAILS", 34, 125}, + #endif + #ifdef X509V3_R_OPERATION_NOT_DEFINED + {"OPERATION_NOT_DEFINED", ERR_LIB_X509V3, X509V3_R_OPERATION_NOT_DEFINED}, + #else + {"OPERATION_NOT_DEFINED", 34, 148}, + #endif + #ifdef X509V3_R_OTHERNAME_ERROR + {"OTHERNAME_ERROR", ERR_LIB_X509V3, X509V3_R_OTHERNAME_ERROR}, + #else + {"OTHERNAME_ERROR", 34, 147}, + #endif + #ifdef X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED + {"POLICY_LANGUAGE_ALREADY_DEFINED", ERR_LIB_X509V3, X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED}, + #else + {"POLICY_LANGUAGE_ALREADY_DEFINED", 34, 155}, + #endif + #ifdef X509V3_R_POLICY_PATH_LENGTH + {"POLICY_PATH_LENGTH", ERR_LIB_X509V3, X509V3_R_POLICY_PATH_LENGTH}, + #else + {"POLICY_PATH_LENGTH", 34, 156}, + #endif + #ifdef X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED + {"POLICY_PATH_LENGTH_ALREADY_DEFINED", ERR_LIB_X509V3, X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED}, + #else + {"POLICY_PATH_LENGTH_ALREADY_DEFINED", 34, 157}, + #endif + #ifdef X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY + {"POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY", ERR_LIB_X509V3, X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY}, + #else + {"POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY", 34, 159}, + #endif + #ifdef X509V3_R_PURPOSE_NOT_UNIQUE + {"PURPOSE_NOT_UNIQUE", ERR_LIB_X509V3, X509V3_R_PURPOSE_NOT_UNIQUE}, + #else + {"PURPOSE_NOT_UNIQUE", 34, 173}, + #endif + #ifdef X509V3_R_SECTION_NOT_FOUND + {"SECTION_NOT_FOUND", ERR_LIB_X509V3, X509V3_R_SECTION_NOT_FOUND}, + #else + {"SECTION_NOT_FOUND", 34, 150}, + #endif + #ifdef X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS + {"UNABLE_TO_GET_ISSUER_DETAILS", ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS}, + #else + {"UNABLE_TO_GET_ISSUER_DETAILS", 34, 122}, + #endif + #ifdef X509V3_R_UNABLE_TO_GET_ISSUER_KEYID + {"UNABLE_TO_GET_ISSUER_KEYID", ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_KEYID}, + #else + {"UNABLE_TO_GET_ISSUER_KEYID", 34, 123}, + #endif + #ifdef X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT + {"UNKNOWN_BIT_STRING_ARGUMENT", ERR_LIB_X509V3, X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT}, + #else + {"UNKNOWN_BIT_STRING_ARGUMENT", 34, 111}, + #endif + #ifdef X509V3_R_UNKNOWN_EXTENSION + {"UNKNOWN_EXTENSION", ERR_LIB_X509V3, X509V3_R_UNKNOWN_EXTENSION}, + #else + {"UNKNOWN_EXTENSION", 34, 129}, + #endif + #ifdef X509V3_R_UNKNOWN_EXTENSION_NAME + {"UNKNOWN_EXTENSION_NAME", ERR_LIB_X509V3, X509V3_R_UNKNOWN_EXTENSION_NAME}, + #else + {"UNKNOWN_EXTENSION_NAME", 34, 130}, + #endif + #ifdef X509V3_R_UNKNOWN_OPTION + {"UNKNOWN_OPTION", ERR_LIB_X509V3, X509V3_R_UNKNOWN_OPTION}, + #else + {"UNKNOWN_OPTION", 34, 120}, + #endif + #ifdef X509V3_R_UNKNOWN_VALUE + {"UNKNOWN_VALUE", ERR_LIB_X509V3, X509V3_R_UNKNOWN_VALUE}, + #else + {"UNKNOWN_VALUE", 34, 172}, + #endif + #ifdef X509V3_R_UNSUPPORTED_OPTION + {"UNSUPPORTED_OPTION", ERR_LIB_X509V3, X509V3_R_UNSUPPORTED_OPTION}, + #else + {"UNSUPPORTED_OPTION", 34, 117}, + #endif + #ifdef X509V3_R_UNSUPPORTED_TYPE + {"UNSUPPORTED_TYPE", ERR_LIB_X509V3, X509V3_R_UNSUPPORTED_TYPE}, + #else + {"UNSUPPORTED_TYPE", 34, 167}, + #endif + #ifdef X509V3_R_USER_TOO_LONG + {"USER_TOO_LONG", ERR_LIB_X509V3, X509V3_R_USER_TOO_LONG}, + #else + {"USER_TOO_LONG", 34, 132}, + #endif + #ifdef X509_R_AKID_MISMATCH + {"AKID_MISMATCH", ERR_LIB_X509, X509_R_AKID_MISMATCH}, + #else + {"AKID_MISMATCH", 11, 110}, + #endif + #ifdef X509_R_BAD_SELECTOR + {"BAD_SELECTOR", ERR_LIB_X509, X509_R_BAD_SELECTOR}, + #else + {"BAD_SELECTOR", 11, 133}, + #endif + #ifdef X509_R_BAD_X509_FILETYPE + {"BAD_X509_FILETYPE", ERR_LIB_X509, X509_R_BAD_X509_FILETYPE}, + #else + {"BAD_X509_FILETYPE", 11, 100}, + #endif + #ifdef X509_R_BASE64_DECODE_ERROR + {"BASE64_DECODE_ERROR", ERR_LIB_X509, X509_R_BASE64_DECODE_ERROR}, + #else + {"BASE64_DECODE_ERROR", 11, 118}, + #endif + #ifdef X509_R_CANT_CHECK_DH_KEY + {"CANT_CHECK_DH_KEY", ERR_LIB_X509, X509_R_CANT_CHECK_DH_KEY}, + #else + {"CANT_CHECK_DH_KEY", 11, 114}, + #endif + #ifdef X509_R_CERTIFICATE_VERIFICATION_FAILED + {"CERTIFICATE_VERIFICATION_FAILED", ERR_LIB_X509, X509_R_CERTIFICATE_VERIFICATION_FAILED}, + #else + {"CERTIFICATE_VERIFICATION_FAILED", 11, 139}, + #endif + #ifdef X509_R_CERT_ALREADY_IN_HASH_TABLE + {"CERT_ALREADY_IN_HASH_TABLE", ERR_LIB_X509, X509_R_CERT_ALREADY_IN_HASH_TABLE}, + #else + {"CERT_ALREADY_IN_HASH_TABLE", 11, 101}, + #endif + #ifdef X509_R_CRL_ALREADY_DELTA + {"CRL_ALREADY_DELTA", ERR_LIB_X509, X509_R_CRL_ALREADY_DELTA}, + #else + {"CRL_ALREADY_DELTA", 11, 127}, + #endif + #ifdef X509_R_CRL_VERIFY_FAILURE + {"CRL_VERIFY_FAILURE", ERR_LIB_X509, X509_R_CRL_VERIFY_FAILURE}, + #else + {"CRL_VERIFY_FAILURE", 11, 131}, + #endif + #ifdef X509_R_DUPLICATE_ATTRIBUTE + {"DUPLICATE_ATTRIBUTE", ERR_LIB_X509, X509_R_DUPLICATE_ATTRIBUTE}, + #else + {"DUPLICATE_ATTRIBUTE", 11, 140}, + #endif + #ifdef X509_R_ERROR_GETTING_MD_BY_NID + {"ERROR_GETTING_MD_BY_NID", ERR_LIB_X509, X509_R_ERROR_GETTING_MD_BY_NID}, + #else + {"ERROR_GETTING_MD_BY_NID", 11, 141}, + #endif + #ifdef X509_R_ERROR_USING_SIGINF_SET + {"ERROR_USING_SIGINF_SET", ERR_LIB_X509, X509_R_ERROR_USING_SIGINF_SET}, + #else + {"ERROR_USING_SIGINF_SET", 11, 142}, + #endif + #ifdef X509_R_IDP_MISMATCH + {"IDP_MISMATCH", ERR_LIB_X509, X509_R_IDP_MISMATCH}, + #else + {"IDP_MISMATCH", 11, 128}, + #endif + #ifdef X509_R_INVALID_ATTRIBUTES + {"INVALID_ATTRIBUTES", ERR_LIB_X509, X509_R_INVALID_ATTRIBUTES}, + #else + {"INVALID_ATTRIBUTES", 11, 138}, + #endif + #ifdef X509_R_INVALID_DIRECTORY + {"INVALID_DIRECTORY", ERR_LIB_X509, X509_R_INVALID_DIRECTORY}, + #else + {"INVALID_DIRECTORY", 11, 113}, + #endif + #ifdef X509_R_INVALID_DISTPOINT + {"INVALID_DISTPOINT", ERR_LIB_X509, X509_R_INVALID_DISTPOINT}, + #else + {"INVALID_DISTPOINT", 11, 143}, + #endif + #ifdef X509_R_INVALID_FIELD_NAME + {"INVALID_FIELD_NAME", ERR_LIB_X509, X509_R_INVALID_FIELD_NAME}, + #else + {"INVALID_FIELD_NAME", 11, 119}, + #endif + #ifdef X509_R_INVALID_TRUST + {"INVALID_TRUST", ERR_LIB_X509, X509_R_INVALID_TRUST}, + #else + {"INVALID_TRUST", 11, 123}, + #endif + #ifdef X509_R_ISSUER_MISMATCH + {"ISSUER_MISMATCH", ERR_LIB_X509, X509_R_ISSUER_MISMATCH}, + #else + {"ISSUER_MISMATCH", 11, 129}, + #endif + #ifdef X509_R_KEY_TYPE_MISMATCH + {"KEY_TYPE_MISMATCH", ERR_LIB_X509, X509_R_KEY_TYPE_MISMATCH}, + #else + {"KEY_TYPE_MISMATCH", 11, 115}, + #endif + #ifdef X509_R_KEY_VALUES_MISMATCH + {"KEY_VALUES_MISMATCH", ERR_LIB_X509, X509_R_KEY_VALUES_MISMATCH}, + #else + {"KEY_VALUES_MISMATCH", 11, 116}, + #endif + #ifdef X509_R_LOADING_CERT_DIR + {"LOADING_CERT_DIR", ERR_LIB_X509, X509_R_LOADING_CERT_DIR}, + #else + {"LOADING_CERT_DIR", 11, 103}, + #endif + #ifdef X509_R_LOADING_DEFAULTS + {"LOADING_DEFAULTS", ERR_LIB_X509, X509_R_LOADING_DEFAULTS}, + #else + {"LOADING_DEFAULTS", 11, 104}, + #endif + #ifdef X509_R_METHOD_NOT_SUPPORTED + {"METHOD_NOT_SUPPORTED", ERR_LIB_X509, X509_R_METHOD_NOT_SUPPORTED}, + #else + {"METHOD_NOT_SUPPORTED", 11, 124}, + #endif + #ifdef X509_R_NAME_TOO_LONG + {"NAME_TOO_LONG", ERR_LIB_X509, X509_R_NAME_TOO_LONG}, + #else + {"NAME_TOO_LONG", 11, 134}, + #endif + #ifdef X509_R_NEWER_CRL_NOT_NEWER + {"NEWER_CRL_NOT_NEWER", ERR_LIB_X509, X509_R_NEWER_CRL_NOT_NEWER}, + #else + {"NEWER_CRL_NOT_NEWER", 11, 132}, + #endif + #ifdef X509_R_NO_CERTIFICATE_FOUND + {"NO_CERTIFICATE_FOUND", ERR_LIB_X509, X509_R_NO_CERTIFICATE_FOUND}, + #else + {"NO_CERTIFICATE_FOUND", 11, 135}, + #endif + #ifdef X509_R_NO_CERTIFICATE_OR_CRL_FOUND + {"NO_CERTIFICATE_OR_CRL_FOUND", ERR_LIB_X509, X509_R_NO_CERTIFICATE_OR_CRL_FOUND}, + #else + {"NO_CERTIFICATE_OR_CRL_FOUND", 11, 136}, + #endif + #ifdef X509_R_NO_CERT_SET_FOR_US_TO_VERIFY + {"NO_CERT_SET_FOR_US_TO_VERIFY", ERR_LIB_X509, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY}, + #else + {"NO_CERT_SET_FOR_US_TO_VERIFY", 11, 105}, + #endif + #ifdef X509_R_NO_CRL_FOUND + {"NO_CRL_FOUND", ERR_LIB_X509, X509_R_NO_CRL_FOUND}, + #else + {"NO_CRL_FOUND", 11, 137}, + #endif + #ifdef X509_R_NO_CRL_NUMBER + {"NO_CRL_NUMBER", ERR_LIB_X509, X509_R_NO_CRL_NUMBER}, + #else + {"NO_CRL_NUMBER", 11, 130}, + #endif + #ifdef X509_R_PUBLIC_KEY_DECODE_ERROR + {"PUBLIC_KEY_DECODE_ERROR", ERR_LIB_X509, X509_R_PUBLIC_KEY_DECODE_ERROR}, + #else + {"PUBLIC_KEY_DECODE_ERROR", 11, 125}, + #endif + #ifdef X509_R_PUBLIC_KEY_ENCODE_ERROR + {"PUBLIC_KEY_ENCODE_ERROR", ERR_LIB_X509, X509_R_PUBLIC_KEY_ENCODE_ERROR}, + #else + {"PUBLIC_KEY_ENCODE_ERROR", 11, 126}, + #endif + #ifdef X509_R_SHOULD_RETRY + {"SHOULD_RETRY", ERR_LIB_X509, X509_R_SHOULD_RETRY}, + #else + {"SHOULD_RETRY", 11, 106}, + #endif + #ifdef X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN + {"UNABLE_TO_FIND_PARAMETERS_IN_CHAIN", ERR_LIB_X509, X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN}, + #else + {"UNABLE_TO_FIND_PARAMETERS_IN_CHAIN", 11, 107}, + #endif + #ifdef X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY + {"UNABLE_TO_GET_CERTS_PUBLIC_KEY", ERR_LIB_X509, X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY}, + #else + {"UNABLE_TO_GET_CERTS_PUBLIC_KEY", 11, 108}, + #endif + #ifdef X509_R_UNKNOWN_KEY_TYPE + {"UNKNOWN_KEY_TYPE", ERR_LIB_X509, X509_R_UNKNOWN_KEY_TYPE}, + #else + {"UNKNOWN_KEY_TYPE", 11, 117}, + #endif + #ifdef X509_R_UNKNOWN_NID + {"UNKNOWN_NID", ERR_LIB_X509, X509_R_UNKNOWN_NID}, + #else + {"UNKNOWN_NID", 11, 109}, + #endif + #ifdef X509_R_UNKNOWN_PURPOSE_ID + {"UNKNOWN_PURPOSE_ID", ERR_LIB_X509, X509_R_UNKNOWN_PURPOSE_ID}, + #else + {"UNKNOWN_PURPOSE_ID", 11, 121}, + #endif + #ifdef X509_R_UNKNOWN_SIGID_ALGS + {"UNKNOWN_SIGID_ALGS", ERR_LIB_X509, X509_R_UNKNOWN_SIGID_ALGS}, + #else + {"UNKNOWN_SIGID_ALGS", 11, 144}, + #endif + #ifdef X509_R_UNKNOWN_TRUST_ID + {"UNKNOWN_TRUST_ID", ERR_LIB_X509, X509_R_UNKNOWN_TRUST_ID}, + #else + {"UNKNOWN_TRUST_ID", 11, 120}, + #endif + #ifdef X509_R_UNSUPPORTED_ALGORITHM + {"UNSUPPORTED_ALGORITHM", ERR_LIB_X509, X509_R_UNSUPPORTED_ALGORITHM}, + #else + {"UNSUPPORTED_ALGORITHM", 11, 111}, + #endif + #ifdef X509_R_UNSUPPORTED_VERSION + {"UNSUPPORTED_VERSION", ERR_LIB_X509, X509_R_UNSUPPORTED_VERSION}, + #else + {"UNSUPPORTED_VERSION", 11, 145}, + #endif + #ifdef X509_R_WRONG_LOOKUP_TYPE + {"WRONG_LOOKUP_TYPE", ERR_LIB_X509, X509_R_WRONG_LOOKUP_TYPE}, + #else + {"WRONG_LOOKUP_TYPE", 11, 112}, + #endif + #ifdef X509_R_WRONG_TYPE + {"WRONG_TYPE", ERR_LIB_X509, X509_R_WRONG_TYPE}, + #else + {"WRONG_TYPE", 11, 122}, + #endif + {NULL, 0, 0} /* sentinel */ +}; diff --git a/Modules/_ssl_data_40.h b/Modules/_ssl_data_40.h new file mode 100644 index 00000000000000..ea615f655bb595 --- /dev/null +++ b/Modules/_ssl_data_40.h @@ -0,0 +1,9357 @@ +/* File generated by Tools/ssl/make_ssl_data.py */ +/* Generated on 2026-04-15T09:18:34.696139+00:00 */ +/* Generated from Git commit v3.15.0a8-132-gd14e31ed683 */ + +/* generated from args.lib2errnum */ +static struct py_ssl_library_code library_codes[] = { +#ifdef ERR_LIB_ASN1 + {"ASN1", ERR_LIB_ASN1}, +#endif +#ifdef ERR_LIB_ASYNC + {"ASYNC", ERR_LIB_ASYNC}, +#endif +#ifdef ERR_LIB_BIO + {"BIO", ERR_LIB_BIO}, +#endif +#ifdef ERR_LIB_BN + {"BN", ERR_LIB_BN}, +#endif +#ifdef ERR_LIB_BUF + {"BUF", ERR_LIB_BUF}, +#endif +#ifdef ERR_LIB_CMP + {"CMP", ERR_LIB_CMP}, +#endif +#ifdef ERR_LIB_CMS + {"CMS", ERR_LIB_CMS}, +#endif +#ifdef ERR_LIB_COMP + {"COMP", ERR_LIB_COMP}, +#endif +#ifdef ERR_LIB_CONF + {"CONF", ERR_LIB_CONF}, +#endif +#ifdef ERR_LIB_CRMF + {"CRMF", ERR_LIB_CRMF}, +#endif +#ifdef ERR_LIB_CRYPTO + {"CRYPTO", ERR_LIB_CRYPTO}, +#endif +#ifdef ERR_LIB_CT + {"CT", ERR_LIB_CT}, +#endif +#ifdef ERR_LIB_DH + {"DH", ERR_LIB_DH}, +#endif +#ifdef ERR_LIB_DSA + {"DSA", ERR_LIB_DSA}, +#endif +#ifdef ERR_LIB_DSO + {"DSO", ERR_LIB_DSO}, +#endif +#ifdef ERR_LIB_EC + {"EC", ERR_LIB_EC}, +#endif +#ifdef ERR_LIB_ECDH + {"ECDH", ERR_LIB_ECDH}, +#endif +#ifdef ERR_LIB_ECDSA + {"ECDSA", ERR_LIB_ECDSA}, +#endif +#ifdef ERR_LIB_ENGINE + {"ENGINE", ERR_LIB_ENGINE}, +#endif +#ifdef ERR_LIB_ESS + {"ESS", ERR_LIB_ESS}, +#endif +#ifdef ERR_LIB_EVP + {"EVP", ERR_LIB_EVP}, +#endif +#ifdef ERR_LIB_FIPS + {"FIPS", ERR_LIB_FIPS}, +#endif +#ifdef ERR_LIB_HMAC + {"HMAC", ERR_LIB_HMAC}, +#endif +#ifdef ERR_LIB_HTTP + {"HTTP", ERR_LIB_HTTP}, +#endif +#ifdef ERR_LIB_JPAKE + {"JPAKE", ERR_LIB_JPAKE}, +#endif +#ifdef ERR_LIB_KDF + {"KDF", ERR_LIB_KDF}, +#endif +#ifdef ERR_LIB_METH + {"METH", ERR_LIB_METH}, +#endif +#ifdef ERR_LIB_NONE + {"NONE", ERR_LIB_NONE}, +#endif +#ifdef ERR_LIB_OBJ + {"OBJ", ERR_LIB_OBJ}, +#endif +#ifdef ERR_LIB_OCSP + {"OCSP", ERR_LIB_OCSP}, +#endif +#ifdef ERR_LIB_OSSL_DECODER + {"OSSL_DECODER", ERR_LIB_OSSL_DECODER}, +#endif +#ifdef ERR_LIB_OSSL_ENCODER + {"OSSL_ENCODER", ERR_LIB_OSSL_ENCODER}, +#endif +#ifdef ERR_LIB_OSSL_STORE + {"OSSL_STORE", ERR_LIB_OSSL_STORE}, +#endif +#ifdef ERR_LIB_PEM + {"PEM", ERR_LIB_PEM}, +#endif +#ifdef ERR_LIB_PKCS12 + {"PKCS12", ERR_LIB_PKCS12}, +#endif +#ifdef ERR_LIB_PKCS7 + {"PKCS7", ERR_LIB_PKCS7}, +#endif +#ifdef ERR_LIB_PROP + {"PROP", ERR_LIB_PROP}, +#endif +#ifdef ERR_LIB_PROV + {"PROV", ERR_LIB_PROV}, +#endif +#ifdef ERR_LIB_PROXY + {"PROXY", ERR_LIB_PROXY}, +#endif +#ifdef ERR_LIB_RAND + {"RAND", ERR_LIB_RAND}, +#endif +#ifdef ERR_LIB_RSA + {"RSA", ERR_LIB_RSA}, +#endif +#ifdef ERR_LIB_RSAREF + {"RSAREF", ERR_LIB_RSAREF}, +#endif +#ifdef ERR_LIB_SM2 + {"SM2", ERR_LIB_SM2}, +#endif +#ifdef ERR_LIB_SSL + {"SSL", ERR_LIB_SSL}, +#endif +#ifdef ERR_LIB_SSL2 + {"SSL2", ERR_LIB_SSL2}, +#endif +#ifdef ERR_LIB_SSL23 + {"SSL23", ERR_LIB_SSL23}, +#endif +#ifdef ERR_LIB_SSL3 + {"SSL3", ERR_LIB_SSL3}, +#endif +#ifdef ERR_LIB_SYS + {"SYS", ERR_LIB_SYS}, +#endif +#ifdef ERR_LIB_TS + {"TS", ERR_LIB_TS}, +#endif +#ifdef ERR_LIB_UI + {"UI", ERR_LIB_UI}, +#endif +#ifdef ERR_LIB_USER + {"USER", ERR_LIB_USER}, +#endif +#ifdef ERR_LIB_X509 + {"X509", ERR_LIB_X509}, +#endif +#ifdef ERR_LIB_X509V3 + {"X509V3", ERR_LIB_X509V3}, +#endif + {NULL, 0} /* sentinel */ +}; + +/* generated from args.reasons */ +static struct py_ssl_error_code error_codes[] = { + #ifdef ASN1_R_ADDING_OBJECT + {"ADDING_OBJECT", ERR_LIB_ASN1, ASN1_R_ADDING_OBJECT}, + #else + {"ADDING_OBJECT", 13, 171}, + #endif + #ifdef ASN1_R_ASN1_PARSE_ERROR + {"ASN1_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_ASN1_PARSE_ERROR}, + #else + {"ASN1_PARSE_ERROR", 13, 203}, + #endif + #ifdef ASN1_R_ASN1_SIG_PARSE_ERROR + {"ASN1_SIG_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_ASN1_SIG_PARSE_ERROR}, + #else + {"ASN1_SIG_PARSE_ERROR", 13, 204}, + #endif + #ifdef ASN1_R_AUX_ERROR + {"AUX_ERROR", ERR_LIB_ASN1, ASN1_R_AUX_ERROR}, + #else + {"AUX_ERROR", 13, 100}, + #endif + #ifdef ASN1_R_BAD_OBJECT_HEADER + {"BAD_OBJECT_HEADER", ERR_LIB_ASN1, ASN1_R_BAD_OBJECT_HEADER}, + #else + {"BAD_OBJECT_HEADER", 13, 102}, + #endif + #ifdef ASN1_R_BAD_TEMPLATE + {"BAD_TEMPLATE", ERR_LIB_ASN1, ASN1_R_BAD_TEMPLATE}, + #else + {"BAD_TEMPLATE", 13, 230}, + #endif + #ifdef ASN1_R_BMPSTRING_IS_WRONG_LENGTH + {"BMPSTRING_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_BMPSTRING_IS_WRONG_LENGTH}, + #else + {"BMPSTRING_IS_WRONG_LENGTH", 13, 214}, + #endif + #ifdef ASN1_R_BN_LIB + {"BN_LIB", ERR_LIB_ASN1, ASN1_R_BN_LIB}, + #else + {"BN_LIB", 13, 105}, + #endif + #ifdef ASN1_R_BOOLEAN_IS_WRONG_LENGTH + {"BOOLEAN_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_BOOLEAN_IS_WRONG_LENGTH}, + #else + {"BOOLEAN_IS_WRONG_LENGTH", 13, 106}, + #endif + #ifdef ASN1_R_BUFFER_TOO_SMALL + {"BUFFER_TOO_SMALL", ERR_LIB_ASN1, ASN1_R_BUFFER_TOO_SMALL}, + #else + {"BUFFER_TOO_SMALL", 13, 107}, + #endif + #ifdef ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER + {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", ERR_LIB_ASN1, ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER}, + #else + {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", 13, 108}, + #endif + #ifdef ASN1_R_CONTEXT_NOT_INITIALISED + {"CONTEXT_NOT_INITIALISED", ERR_LIB_ASN1, ASN1_R_CONTEXT_NOT_INITIALISED}, + #else + {"CONTEXT_NOT_INITIALISED", 13, 217}, + #endif + #ifdef ASN1_R_DATA_IS_WRONG + {"DATA_IS_WRONG", ERR_LIB_ASN1, ASN1_R_DATA_IS_WRONG}, + #else + {"DATA_IS_WRONG", 13, 109}, + #endif + #ifdef ASN1_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_ASN1, ASN1_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 13, 110}, + #endif + #ifdef ASN1_R_DEPTH_EXCEEDED + {"DEPTH_EXCEEDED", ERR_LIB_ASN1, ASN1_R_DEPTH_EXCEEDED}, + #else + {"DEPTH_EXCEEDED", 13, 174}, + #endif + #ifdef ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED + {"DIGEST_AND_KEY_TYPE_NOT_SUPPORTED", ERR_LIB_ASN1, ASN1_R_DIGEST_AND_KEY_TYPE_NOT_SUPPORTED}, + #else + {"DIGEST_AND_KEY_TYPE_NOT_SUPPORTED", 13, 198}, + #endif + #ifdef ASN1_R_ENCODE_ERROR + {"ENCODE_ERROR", ERR_LIB_ASN1, ASN1_R_ENCODE_ERROR}, + #else + {"ENCODE_ERROR", 13, 112}, + #endif + #ifdef ASN1_R_ERROR_GETTING_TIME + {"ERROR_GETTING_TIME", ERR_LIB_ASN1, ASN1_R_ERROR_GETTING_TIME}, + #else + {"ERROR_GETTING_TIME", 13, 173}, + #endif + #ifdef ASN1_R_ERROR_LOADING_SECTION + {"ERROR_LOADING_SECTION", ERR_LIB_ASN1, ASN1_R_ERROR_LOADING_SECTION}, + #else + {"ERROR_LOADING_SECTION", 13, 172}, + #endif + #ifdef ASN1_R_ERROR_SETTING_CIPHER_PARAMS + {"ERROR_SETTING_CIPHER_PARAMS", ERR_LIB_ASN1, ASN1_R_ERROR_SETTING_CIPHER_PARAMS}, + #else + {"ERROR_SETTING_CIPHER_PARAMS", 13, 114}, + #endif + #ifdef ASN1_R_EXPECTING_AN_INTEGER + {"EXPECTING_AN_INTEGER", ERR_LIB_ASN1, ASN1_R_EXPECTING_AN_INTEGER}, + #else + {"EXPECTING_AN_INTEGER", 13, 115}, + #endif + #ifdef ASN1_R_EXPECTING_AN_OBJECT + {"EXPECTING_AN_OBJECT", ERR_LIB_ASN1, ASN1_R_EXPECTING_AN_OBJECT}, + #else + {"EXPECTING_AN_OBJECT", 13, 116}, + #endif + #ifdef ASN1_R_EXPLICIT_LENGTH_MISMATCH + {"EXPLICIT_LENGTH_MISMATCH", ERR_LIB_ASN1, ASN1_R_EXPLICIT_LENGTH_MISMATCH}, + #else + {"EXPLICIT_LENGTH_MISMATCH", 13, 119}, + #endif + #ifdef ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED + {"EXPLICIT_TAG_NOT_CONSTRUCTED", ERR_LIB_ASN1, ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED}, + #else + {"EXPLICIT_TAG_NOT_CONSTRUCTED", 13, 120}, + #endif + #ifdef ASN1_R_FIELD_MISSING + {"FIELD_MISSING", ERR_LIB_ASN1, ASN1_R_FIELD_MISSING}, + #else + {"FIELD_MISSING", 13, 121}, + #endif + #ifdef ASN1_R_FIRST_NUM_TOO_LARGE + {"FIRST_NUM_TOO_LARGE", ERR_LIB_ASN1, ASN1_R_FIRST_NUM_TOO_LARGE}, + #else + {"FIRST_NUM_TOO_LARGE", 13, 122}, + #endif + #ifdef ASN1_R_GENERALIZEDTIME_IS_TOO_SHORT + {"GENERALIZEDTIME_IS_TOO_SHORT", ERR_LIB_ASN1, ASN1_R_GENERALIZEDTIME_IS_TOO_SHORT}, + #else + {"GENERALIZEDTIME_IS_TOO_SHORT", 13, 232}, + #endif + #ifdef ASN1_R_HEADER_TOO_LONG + {"HEADER_TOO_LONG", ERR_LIB_ASN1, ASN1_R_HEADER_TOO_LONG}, + #else + {"HEADER_TOO_LONG", 13, 123}, + #endif + #ifdef ASN1_R_ILLEGAL_BITSTRING_FORMAT + {"ILLEGAL_BITSTRING_FORMAT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_BITSTRING_FORMAT}, + #else + {"ILLEGAL_BITSTRING_FORMAT", 13, 175}, + #endif + #ifdef ASN1_R_ILLEGAL_BOOLEAN + {"ILLEGAL_BOOLEAN", ERR_LIB_ASN1, ASN1_R_ILLEGAL_BOOLEAN}, + #else + {"ILLEGAL_BOOLEAN", 13, 176}, + #endif + #ifdef ASN1_R_ILLEGAL_CHARACTERS + {"ILLEGAL_CHARACTERS", ERR_LIB_ASN1, ASN1_R_ILLEGAL_CHARACTERS}, + #else + {"ILLEGAL_CHARACTERS", 13, 124}, + #endif + #ifdef ASN1_R_ILLEGAL_FORMAT + {"ILLEGAL_FORMAT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_FORMAT}, + #else + {"ILLEGAL_FORMAT", 13, 177}, + #endif + #ifdef ASN1_R_ILLEGAL_HEX + {"ILLEGAL_HEX", ERR_LIB_ASN1, ASN1_R_ILLEGAL_HEX}, + #else + {"ILLEGAL_HEX", 13, 178}, + #endif + #ifdef ASN1_R_ILLEGAL_IMPLICIT_TAG + {"ILLEGAL_IMPLICIT_TAG", ERR_LIB_ASN1, ASN1_R_ILLEGAL_IMPLICIT_TAG}, + #else + {"ILLEGAL_IMPLICIT_TAG", 13, 179}, + #endif + #ifdef ASN1_R_ILLEGAL_INTEGER + {"ILLEGAL_INTEGER", ERR_LIB_ASN1, ASN1_R_ILLEGAL_INTEGER}, + #else + {"ILLEGAL_INTEGER", 13, 180}, + #endif + #ifdef ASN1_R_ILLEGAL_NEGATIVE_VALUE + {"ILLEGAL_NEGATIVE_VALUE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NEGATIVE_VALUE}, + #else + {"ILLEGAL_NEGATIVE_VALUE", 13, 226}, + #endif + #ifdef ASN1_R_ILLEGAL_NESTED_TAGGING + {"ILLEGAL_NESTED_TAGGING", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NESTED_TAGGING}, + #else + {"ILLEGAL_NESTED_TAGGING", 13, 181}, + #endif + #ifdef ASN1_R_ILLEGAL_NULL + {"ILLEGAL_NULL", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NULL}, + #else + {"ILLEGAL_NULL", 13, 125}, + #endif + #ifdef ASN1_R_ILLEGAL_NULL_VALUE + {"ILLEGAL_NULL_VALUE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_NULL_VALUE}, + #else + {"ILLEGAL_NULL_VALUE", 13, 182}, + #endif + #ifdef ASN1_R_ILLEGAL_OBJECT + {"ILLEGAL_OBJECT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_OBJECT}, + #else + {"ILLEGAL_OBJECT", 13, 183}, + #endif + #ifdef ASN1_R_ILLEGAL_OPTIONAL_ANY + {"ILLEGAL_OPTIONAL_ANY", ERR_LIB_ASN1, ASN1_R_ILLEGAL_OPTIONAL_ANY}, + #else + {"ILLEGAL_OPTIONAL_ANY", 13, 126}, + #endif + #ifdef ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE + {"ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE}, + #else + {"ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE", 13, 170}, + #endif + #ifdef ASN1_R_ILLEGAL_PADDING + {"ILLEGAL_PADDING", ERR_LIB_ASN1, ASN1_R_ILLEGAL_PADDING}, + #else + {"ILLEGAL_PADDING", 13, 221}, + #endif + #ifdef ASN1_R_ILLEGAL_TAGGED_ANY + {"ILLEGAL_TAGGED_ANY", ERR_LIB_ASN1, ASN1_R_ILLEGAL_TAGGED_ANY}, + #else + {"ILLEGAL_TAGGED_ANY", 13, 127}, + #endif + #ifdef ASN1_R_ILLEGAL_TIME_VALUE + {"ILLEGAL_TIME_VALUE", ERR_LIB_ASN1, ASN1_R_ILLEGAL_TIME_VALUE}, + #else + {"ILLEGAL_TIME_VALUE", 13, 184}, + #endif + #ifdef ASN1_R_ILLEGAL_ZERO_CONTENT + {"ILLEGAL_ZERO_CONTENT", ERR_LIB_ASN1, ASN1_R_ILLEGAL_ZERO_CONTENT}, + #else + {"ILLEGAL_ZERO_CONTENT", 13, 222}, + #endif + #ifdef ASN1_R_INTEGER_NOT_ASCII_FORMAT + {"INTEGER_NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_INTEGER_NOT_ASCII_FORMAT}, + #else + {"INTEGER_NOT_ASCII_FORMAT", 13, 185}, + #endif + #ifdef ASN1_R_INTEGER_TOO_LARGE_FOR_LONG + {"INTEGER_TOO_LARGE_FOR_LONG", ERR_LIB_ASN1, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG}, + #else + {"INTEGER_TOO_LARGE_FOR_LONG", 13, 128}, + #endif + #ifdef ASN1_R_INVALID_BIT_STRING_BITS_LEFT + {"INVALID_BIT_STRING_BITS_LEFT", ERR_LIB_ASN1, ASN1_R_INVALID_BIT_STRING_BITS_LEFT}, + #else + {"INVALID_BIT_STRING_BITS_LEFT", 13, 220}, + #endif + #ifdef ASN1_R_INVALID_BMPSTRING_LENGTH + {"INVALID_BMPSTRING_LENGTH", ERR_LIB_ASN1, ASN1_R_INVALID_BMPSTRING_LENGTH}, + #else + {"INVALID_BMPSTRING_LENGTH", 13, 129}, + #endif + #ifdef ASN1_R_INVALID_DIGIT + {"INVALID_DIGIT", ERR_LIB_ASN1, ASN1_R_INVALID_DIGIT}, + #else + {"INVALID_DIGIT", 13, 130}, + #endif + #ifdef ASN1_R_INVALID_MIME_TYPE + {"INVALID_MIME_TYPE", ERR_LIB_ASN1, ASN1_R_INVALID_MIME_TYPE}, + #else + {"INVALID_MIME_TYPE", 13, 205}, + #endif + #ifdef ASN1_R_INVALID_MODIFIER + {"INVALID_MODIFIER", ERR_LIB_ASN1, ASN1_R_INVALID_MODIFIER}, + #else + {"INVALID_MODIFIER", 13, 186}, + #endif + #ifdef ASN1_R_INVALID_NUMBER + {"INVALID_NUMBER", ERR_LIB_ASN1, ASN1_R_INVALID_NUMBER}, + #else + {"INVALID_NUMBER", 13, 187}, + #endif + #ifdef ASN1_R_INVALID_OBJECT_ENCODING + {"INVALID_OBJECT_ENCODING", ERR_LIB_ASN1, ASN1_R_INVALID_OBJECT_ENCODING}, + #else + {"INVALID_OBJECT_ENCODING", 13, 216}, + #endif + #ifdef ASN1_R_INVALID_SCRYPT_PARAMETERS + {"INVALID_SCRYPT_PARAMETERS", ERR_LIB_ASN1, ASN1_R_INVALID_SCRYPT_PARAMETERS}, + #else + {"INVALID_SCRYPT_PARAMETERS", 13, 227}, + #endif + #ifdef ASN1_R_INVALID_SEPARATOR + {"INVALID_SEPARATOR", ERR_LIB_ASN1, ASN1_R_INVALID_SEPARATOR}, + #else + {"INVALID_SEPARATOR", 13, 131}, + #endif + #ifdef ASN1_R_INVALID_STRING_TABLE_VALUE + {"INVALID_STRING_TABLE_VALUE", ERR_LIB_ASN1, ASN1_R_INVALID_STRING_TABLE_VALUE}, + #else + {"INVALID_STRING_TABLE_VALUE", 13, 218}, + #endif + #ifdef ASN1_R_INVALID_UNIVERSALSTRING_LENGTH + {"INVALID_UNIVERSALSTRING_LENGTH", ERR_LIB_ASN1, ASN1_R_INVALID_UNIVERSALSTRING_LENGTH}, + #else + {"INVALID_UNIVERSALSTRING_LENGTH", 13, 133}, + #endif + #ifdef ASN1_R_INVALID_UTF8STRING + {"INVALID_UTF8STRING", ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING}, + #else + {"INVALID_UTF8STRING", 13, 134}, + #endif + #ifdef ASN1_R_INVALID_VALUE + {"INVALID_VALUE", ERR_LIB_ASN1, ASN1_R_INVALID_VALUE}, + #else + {"INVALID_VALUE", 13, 219}, + #endif + #ifdef ASN1_R_LENGTH_TOO_LONG + {"LENGTH_TOO_LONG", ERR_LIB_ASN1, ASN1_R_LENGTH_TOO_LONG}, + #else + {"LENGTH_TOO_LONG", 13, 231}, + #endif + #ifdef ASN1_R_LIST_ERROR + {"LIST_ERROR", ERR_LIB_ASN1, ASN1_R_LIST_ERROR}, + #else + {"LIST_ERROR", 13, 188}, + #endif + #ifdef ASN1_R_MIME_NO_CONTENT_TYPE + {"MIME_NO_CONTENT_TYPE", ERR_LIB_ASN1, ASN1_R_MIME_NO_CONTENT_TYPE}, + #else + {"MIME_NO_CONTENT_TYPE", 13, 206}, + #endif + #ifdef ASN1_R_MIME_PARSE_ERROR + {"MIME_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_MIME_PARSE_ERROR}, + #else + {"MIME_PARSE_ERROR", 13, 207}, + #endif + #ifdef ASN1_R_MIME_SIG_PARSE_ERROR + {"MIME_SIG_PARSE_ERROR", ERR_LIB_ASN1, ASN1_R_MIME_SIG_PARSE_ERROR}, + #else + {"MIME_SIG_PARSE_ERROR", 13, 208}, + #endif + #ifdef ASN1_R_MISSING_EOC + {"MISSING_EOC", ERR_LIB_ASN1, ASN1_R_MISSING_EOC}, + #else + {"MISSING_EOC", 13, 137}, + #endif + #ifdef ASN1_R_MISSING_SECOND_NUMBER + {"MISSING_SECOND_NUMBER", ERR_LIB_ASN1, ASN1_R_MISSING_SECOND_NUMBER}, + #else + {"MISSING_SECOND_NUMBER", 13, 138}, + #endif + #ifdef ASN1_R_MISSING_VALUE + {"MISSING_VALUE", ERR_LIB_ASN1, ASN1_R_MISSING_VALUE}, + #else + {"MISSING_VALUE", 13, 189}, + #endif + #ifdef ASN1_R_MSTRING_NOT_UNIVERSAL + {"MSTRING_NOT_UNIVERSAL", ERR_LIB_ASN1, ASN1_R_MSTRING_NOT_UNIVERSAL}, + #else + {"MSTRING_NOT_UNIVERSAL", 13, 139}, + #endif + #ifdef ASN1_R_MSTRING_WRONG_TAG + {"MSTRING_WRONG_TAG", ERR_LIB_ASN1, ASN1_R_MSTRING_WRONG_TAG}, + #else + {"MSTRING_WRONG_TAG", 13, 140}, + #endif + #ifdef ASN1_R_NESTED_ASN1_STRING + {"NESTED_ASN1_STRING", ERR_LIB_ASN1, ASN1_R_NESTED_ASN1_STRING}, + #else + {"NESTED_ASN1_STRING", 13, 197}, + #endif + #ifdef ASN1_R_NESTED_TOO_DEEP + {"NESTED_TOO_DEEP", ERR_LIB_ASN1, ASN1_R_NESTED_TOO_DEEP}, + #else + {"NESTED_TOO_DEEP", 13, 201}, + #endif + #ifdef ASN1_R_NON_HEX_CHARACTERS + {"NON_HEX_CHARACTERS", ERR_LIB_ASN1, ASN1_R_NON_HEX_CHARACTERS}, + #else + {"NON_HEX_CHARACTERS", 13, 141}, + #endif + #ifdef ASN1_R_NOT_ASCII_FORMAT + {"NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_NOT_ASCII_FORMAT}, + #else + {"NOT_ASCII_FORMAT", 13, 190}, + #endif + #ifdef ASN1_R_NOT_ENOUGH_DATA + {"NOT_ENOUGH_DATA", ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA}, + #else + {"NOT_ENOUGH_DATA", 13, 142}, + #endif + #ifdef ASN1_R_NO_CONTENT_TYPE + {"NO_CONTENT_TYPE", ERR_LIB_ASN1, ASN1_R_NO_CONTENT_TYPE}, + #else + {"NO_CONTENT_TYPE", 13, 209}, + #endif + #ifdef ASN1_R_NO_MATCHING_CHOICE_TYPE + {"NO_MATCHING_CHOICE_TYPE", ERR_LIB_ASN1, ASN1_R_NO_MATCHING_CHOICE_TYPE}, + #else + {"NO_MATCHING_CHOICE_TYPE", 13, 143}, + #endif + #ifdef ASN1_R_NO_MULTIPART_BODY_FAILURE + {"NO_MULTIPART_BODY_FAILURE", ERR_LIB_ASN1, ASN1_R_NO_MULTIPART_BODY_FAILURE}, + #else + {"NO_MULTIPART_BODY_FAILURE", 13, 210}, + #endif + #ifdef ASN1_R_NO_MULTIPART_BOUNDARY + {"NO_MULTIPART_BOUNDARY", ERR_LIB_ASN1, ASN1_R_NO_MULTIPART_BOUNDARY}, + #else + {"NO_MULTIPART_BOUNDARY", 13, 211}, + #endif + #ifdef ASN1_R_NO_SIG_CONTENT_TYPE + {"NO_SIG_CONTENT_TYPE", ERR_LIB_ASN1, ASN1_R_NO_SIG_CONTENT_TYPE}, + #else + {"NO_SIG_CONTENT_TYPE", 13, 212}, + #endif + #ifdef ASN1_R_NULL_IS_WRONG_LENGTH + {"NULL_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_NULL_IS_WRONG_LENGTH}, + #else + {"NULL_IS_WRONG_LENGTH", 13, 144}, + #endif + #ifdef ASN1_R_OBJECT_NOT_ASCII_FORMAT + {"OBJECT_NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_OBJECT_NOT_ASCII_FORMAT}, + #else + {"OBJECT_NOT_ASCII_FORMAT", 13, 191}, + #endif + #ifdef ASN1_R_ODD_NUMBER_OF_CHARS + {"ODD_NUMBER_OF_CHARS", ERR_LIB_ASN1, ASN1_R_ODD_NUMBER_OF_CHARS}, + #else + {"ODD_NUMBER_OF_CHARS", 13, 145}, + #endif + #ifdef ASN1_R_SECOND_NUMBER_TOO_LARGE + {"SECOND_NUMBER_TOO_LARGE", ERR_LIB_ASN1, ASN1_R_SECOND_NUMBER_TOO_LARGE}, + #else + {"SECOND_NUMBER_TOO_LARGE", 13, 147}, + #endif + #ifdef ASN1_R_SEQUENCE_LENGTH_MISMATCH + {"SEQUENCE_LENGTH_MISMATCH", ERR_LIB_ASN1, ASN1_R_SEQUENCE_LENGTH_MISMATCH}, + #else + {"SEQUENCE_LENGTH_MISMATCH", 13, 148}, + #endif + #ifdef ASN1_R_SEQUENCE_NOT_CONSTRUCTED + {"SEQUENCE_NOT_CONSTRUCTED", ERR_LIB_ASN1, ASN1_R_SEQUENCE_NOT_CONSTRUCTED}, + #else + {"SEQUENCE_NOT_CONSTRUCTED", 13, 149}, + #endif + #ifdef ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG + {"SEQUENCE_OR_SET_NEEDS_CONFIG", ERR_LIB_ASN1, ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG}, + #else + {"SEQUENCE_OR_SET_NEEDS_CONFIG", 13, 192}, + #endif + #ifdef ASN1_R_SHORT_LINE + {"SHORT_LINE", ERR_LIB_ASN1, ASN1_R_SHORT_LINE}, + #else + {"SHORT_LINE", 13, 150}, + #endif + #ifdef ASN1_R_SIG_INVALID_MIME_TYPE + {"SIG_INVALID_MIME_TYPE", ERR_LIB_ASN1, ASN1_R_SIG_INVALID_MIME_TYPE}, + #else + {"SIG_INVALID_MIME_TYPE", 13, 213}, + #endif + #ifdef ASN1_R_STREAMING_NOT_SUPPORTED + {"STREAMING_NOT_SUPPORTED", ERR_LIB_ASN1, ASN1_R_STREAMING_NOT_SUPPORTED}, + #else + {"STREAMING_NOT_SUPPORTED", 13, 202}, + #endif + #ifdef ASN1_R_STRING_TOO_LONG + {"STRING_TOO_LONG", ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG}, + #else + {"STRING_TOO_LONG", 13, 151}, + #endif + #ifdef ASN1_R_STRING_TOO_SHORT + {"STRING_TOO_SHORT", ERR_LIB_ASN1, ASN1_R_STRING_TOO_SHORT}, + #else + {"STRING_TOO_SHORT", 13, 152}, + #endif + #ifdef ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD + {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", ERR_LIB_ASN1, ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD}, + #else + {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", 13, 154}, + #endif + #ifdef ASN1_R_TIME_NOT_ASCII_FORMAT + {"TIME_NOT_ASCII_FORMAT", ERR_LIB_ASN1, ASN1_R_TIME_NOT_ASCII_FORMAT}, + #else + {"TIME_NOT_ASCII_FORMAT", 13, 193}, + #endif + #ifdef ASN1_R_TOO_LARGE + {"TOO_LARGE", ERR_LIB_ASN1, ASN1_R_TOO_LARGE}, + #else + {"TOO_LARGE", 13, 223}, + #endif + #ifdef ASN1_R_TOO_LONG + {"TOO_LONG", ERR_LIB_ASN1, ASN1_R_TOO_LONG}, + #else + {"TOO_LONG", 13, 155}, + #endif + #ifdef ASN1_R_TOO_SMALL + {"TOO_SMALL", ERR_LIB_ASN1, ASN1_R_TOO_SMALL}, + #else + {"TOO_SMALL", 13, 224}, + #endif + #ifdef ASN1_R_TYPE_NOT_CONSTRUCTED + {"TYPE_NOT_CONSTRUCTED", ERR_LIB_ASN1, ASN1_R_TYPE_NOT_CONSTRUCTED}, + #else + {"TYPE_NOT_CONSTRUCTED", 13, 156}, + #endif + #ifdef ASN1_R_TYPE_NOT_PRIMITIVE + {"TYPE_NOT_PRIMITIVE", ERR_LIB_ASN1, ASN1_R_TYPE_NOT_PRIMITIVE}, + #else + {"TYPE_NOT_PRIMITIVE", 13, 195}, + #endif + #ifdef ASN1_R_UNEXPECTED_EOC + {"UNEXPECTED_EOC", ERR_LIB_ASN1, ASN1_R_UNEXPECTED_EOC}, + #else + {"UNEXPECTED_EOC", 13, 159}, + #endif + #ifdef ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH + {"UNIVERSALSTRING_IS_WRONG_LENGTH", ERR_LIB_ASN1, ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH}, + #else + {"UNIVERSALSTRING_IS_WRONG_LENGTH", 13, 215}, + #endif + #ifdef ASN1_R_UNKNOWN_DIGEST + {"UNKNOWN_DIGEST", ERR_LIB_ASN1, ASN1_R_UNKNOWN_DIGEST}, + #else + {"UNKNOWN_DIGEST", 13, 229}, + #endif + #ifdef ASN1_R_UNKNOWN_FORMAT + {"UNKNOWN_FORMAT", ERR_LIB_ASN1, ASN1_R_UNKNOWN_FORMAT}, + #else + {"UNKNOWN_FORMAT", 13, 160}, + #endif + #ifdef ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM + {"UNKNOWN_MESSAGE_DIGEST_ALGORITHM", ERR_LIB_ASN1, ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM}, + #else + {"UNKNOWN_MESSAGE_DIGEST_ALGORITHM", 13, 161}, + #endif + #ifdef ASN1_R_UNKNOWN_OBJECT_TYPE + {"UNKNOWN_OBJECT_TYPE", ERR_LIB_ASN1, ASN1_R_UNKNOWN_OBJECT_TYPE}, + #else + {"UNKNOWN_OBJECT_TYPE", 13, 162}, + #endif + #ifdef ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE + {"UNKNOWN_PUBLIC_KEY_TYPE", ERR_LIB_ASN1, ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE}, + #else + {"UNKNOWN_PUBLIC_KEY_TYPE", 13, 163}, + #endif + #ifdef ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM + {"UNKNOWN_SIGNATURE_ALGORITHM", ERR_LIB_ASN1, ASN1_R_UNKNOWN_SIGNATURE_ALGORITHM}, + #else + {"UNKNOWN_SIGNATURE_ALGORITHM", 13, 199}, + #endif + #ifdef ASN1_R_UNKNOWN_TAG + {"UNKNOWN_TAG", ERR_LIB_ASN1, ASN1_R_UNKNOWN_TAG}, + #else + {"UNKNOWN_TAG", 13, 194}, + #endif + #ifdef ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE + {"UNSUPPORTED_ANY_DEFINED_BY_TYPE", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE}, + #else + {"UNSUPPORTED_ANY_DEFINED_BY_TYPE", 13, 164}, + #endif + #ifdef ASN1_R_UNSUPPORTED_CIPHER + {"UNSUPPORTED_CIPHER", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_CIPHER}, + #else + {"UNSUPPORTED_CIPHER", 13, 228}, + #endif + #ifdef ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE + {"UNSUPPORTED_PUBLIC_KEY_TYPE", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE}, + #else + {"UNSUPPORTED_PUBLIC_KEY_TYPE", 13, 167}, + #endif + #ifdef ASN1_R_UNSUPPORTED_TYPE + {"UNSUPPORTED_TYPE", ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_TYPE}, + #else + {"UNSUPPORTED_TYPE", 13, 196}, + #endif + #ifdef ASN1_R_UTCTIME_IS_TOO_SHORT + {"UTCTIME_IS_TOO_SHORT", ERR_LIB_ASN1, ASN1_R_UTCTIME_IS_TOO_SHORT}, + #else + {"UTCTIME_IS_TOO_SHORT", 13, 233}, + #endif + #ifdef ASN1_R_WRONG_INTEGER_TYPE + {"WRONG_INTEGER_TYPE", ERR_LIB_ASN1, ASN1_R_WRONG_INTEGER_TYPE}, + #else + {"WRONG_INTEGER_TYPE", 13, 225}, + #endif + #ifdef ASN1_R_WRONG_PUBLIC_KEY_TYPE + {"WRONG_PUBLIC_KEY_TYPE", ERR_LIB_ASN1, ASN1_R_WRONG_PUBLIC_KEY_TYPE}, + #else + {"WRONG_PUBLIC_KEY_TYPE", 13, 200}, + #endif + #ifdef ASN1_R_WRONG_TAG + {"WRONG_TAG", ERR_LIB_ASN1, ASN1_R_WRONG_TAG}, + #else + {"WRONG_TAG", 13, 168}, + #endif + #ifdef ASYNC_R_FAILED_TO_SET_POOL + {"FAILED_TO_SET_POOL", ERR_LIB_ASYNC, ASYNC_R_FAILED_TO_SET_POOL}, + #else + {"FAILED_TO_SET_POOL", 51, 101}, + #endif + #ifdef ASYNC_R_FAILED_TO_SWAP_CONTEXT + {"FAILED_TO_SWAP_CONTEXT", ERR_LIB_ASYNC, ASYNC_R_FAILED_TO_SWAP_CONTEXT}, + #else + {"FAILED_TO_SWAP_CONTEXT", 51, 102}, + #endif + #ifdef ASYNC_R_INIT_FAILED + {"INIT_FAILED", ERR_LIB_ASYNC, ASYNC_R_INIT_FAILED}, + #else + {"INIT_FAILED", 51, 105}, + #endif + #ifdef ASYNC_R_INVALID_POOL_SIZE + {"INVALID_POOL_SIZE", ERR_LIB_ASYNC, ASYNC_R_INVALID_POOL_SIZE}, + #else + {"INVALID_POOL_SIZE", 51, 103}, + #endif + #ifdef BIO_R_ACCEPT_ERROR + {"ACCEPT_ERROR", ERR_LIB_BIO, BIO_R_ACCEPT_ERROR}, + #else + {"ACCEPT_ERROR", 32, 100}, + #endif + #ifdef BIO_R_ADDRINFO_ADDR_IS_NOT_AF_INET + {"ADDRINFO_ADDR_IS_NOT_AF_INET", ERR_LIB_BIO, BIO_R_ADDRINFO_ADDR_IS_NOT_AF_INET}, + #else + {"ADDRINFO_ADDR_IS_NOT_AF_INET", 32, 141}, + #endif + #ifdef BIO_R_AMBIGUOUS_HOST_OR_SERVICE + {"AMBIGUOUS_HOST_OR_SERVICE", ERR_LIB_BIO, BIO_R_AMBIGUOUS_HOST_OR_SERVICE}, + #else + {"AMBIGUOUS_HOST_OR_SERVICE", 32, 129}, + #endif + #ifdef BIO_R_BAD_FOPEN_MODE + {"BAD_FOPEN_MODE", ERR_LIB_BIO, BIO_R_BAD_FOPEN_MODE}, + #else + {"BAD_FOPEN_MODE", 32, 101}, + #endif + #ifdef BIO_R_BROKEN_PIPE + {"BROKEN_PIPE", ERR_LIB_BIO, BIO_R_BROKEN_PIPE}, + #else + {"BROKEN_PIPE", 32, 124}, + #endif + #ifdef BIO_R_CONNECT_ERROR + {"CONNECT_ERROR", ERR_LIB_BIO, BIO_R_CONNECT_ERROR}, + #else + {"CONNECT_ERROR", 32, 103}, + #endif + #ifdef BIO_R_CONNECT_TIMEOUT + {"CONNECT_TIMEOUT", ERR_LIB_BIO, BIO_R_CONNECT_TIMEOUT}, + #else + {"CONNECT_TIMEOUT", 32, 147}, + #endif + #ifdef BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET + {"GETHOSTBYNAME_ADDR_IS_NOT_AF_INET", ERR_LIB_BIO, BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET}, + #else + {"GETHOSTBYNAME_ADDR_IS_NOT_AF_INET", 32, 107}, + #endif + #ifdef BIO_R_GETSOCKNAME_ERROR + {"GETSOCKNAME_ERROR", ERR_LIB_BIO, BIO_R_GETSOCKNAME_ERROR}, + #else + {"GETSOCKNAME_ERROR", 32, 132}, + #endif + #ifdef BIO_R_GETSOCKNAME_TRUNCATED_ADDRESS + {"GETSOCKNAME_TRUNCATED_ADDRESS", ERR_LIB_BIO, BIO_R_GETSOCKNAME_TRUNCATED_ADDRESS}, + #else + {"GETSOCKNAME_TRUNCATED_ADDRESS", 32, 133}, + #endif + #ifdef BIO_R_GETTING_SOCKTYPE + {"GETTING_SOCKTYPE", ERR_LIB_BIO, BIO_R_GETTING_SOCKTYPE}, + #else + {"GETTING_SOCKTYPE", 32, 134}, + #endif + #ifdef BIO_R_INVALID_ARGUMENT + {"INVALID_ARGUMENT", ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT}, + #else + {"INVALID_ARGUMENT", 32, 125}, + #endif + #ifdef BIO_R_INVALID_SOCKET + {"INVALID_SOCKET", ERR_LIB_BIO, BIO_R_INVALID_SOCKET}, + #else + {"INVALID_SOCKET", 32, 135}, + #endif + #ifdef BIO_R_IN_USE + {"IN_USE", ERR_LIB_BIO, BIO_R_IN_USE}, + #else + {"IN_USE", 32, 123}, + #endif + #ifdef BIO_R_LENGTH_TOO_LONG + {"LENGTH_TOO_LONG", ERR_LIB_BIO, BIO_R_LENGTH_TOO_LONG}, + #else + {"LENGTH_TOO_LONG", 32, 102}, + #endif + #ifdef BIO_R_LISTEN_V6_ONLY + {"LISTEN_V6_ONLY", ERR_LIB_BIO, BIO_R_LISTEN_V6_ONLY}, + #else + {"LISTEN_V6_ONLY", 32, 136}, + #endif + #ifdef BIO_R_LOCAL_ADDR_NOT_AVAILABLE + {"LOCAL_ADDR_NOT_AVAILABLE", ERR_LIB_BIO, BIO_R_LOCAL_ADDR_NOT_AVAILABLE}, + #else + {"LOCAL_ADDR_NOT_AVAILABLE", 32, 111}, + #endif + #ifdef BIO_R_LOOKUP_RETURNED_NOTHING + {"LOOKUP_RETURNED_NOTHING", ERR_LIB_BIO, BIO_R_LOOKUP_RETURNED_NOTHING}, + #else + {"LOOKUP_RETURNED_NOTHING", 32, 142}, + #endif + #ifdef BIO_R_MALFORMED_HOST_OR_SERVICE + {"MALFORMED_HOST_OR_SERVICE", ERR_LIB_BIO, BIO_R_MALFORMED_HOST_OR_SERVICE}, + #else + {"MALFORMED_HOST_OR_SERVICE", 32, 130}, + #endif + #ifdef BIO_R_NBIO_CONNECT_ERROR + {"NBIO_CONNECT_ERROR", ERR_LIB_BIO, BIO_R_NBIO_CONNECT_ERROR}, + #else + {"NBIO_CONNECT_ERROR", 32, 110}, + #endif + #ifdef BIO_R_NON_FATAL + {"NON_FATAL", ERR_LIB_BIO, BIO_R_NON_FATAL}, + #else + {"NON_FATAL", 32, 112}, + #endif + #ifdef BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED + {"NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED", ERR_LIB_BIO, BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED}, + #else + {"NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED", 32, 143}, + #endif + #ifdef BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED + {"NO_HOSTNAME_OR_SERVICE_SPECIFIED", ERR_LIB_BIO, BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED}, + #else + {"NO_HOSTNAME_OR_SERVICE_SPECIFIED", 32, 144}, + #endif + #ifdef BIO_R_NO_PORT_DEFINED + {"NO_PORT_DEFINED", ERR_LIB_BIO, BIO_R_NO_PORT_DEFINED}, + #else + {"NO_PORT_DEFINED", 32, 113}, + #endif + #ifdef BIO_R_NO_SUCH_FILE + {"NO_SUCH_FILE", ERR_LIB_BIO, BIO_R_NO_SUCH_FILE}, + #else + {"NO_SUCH_FILE", 32, 128}, + #endif + #ifdef BIO_R_PEER_ADDR_NOT_AVAILABLE + {"PEER_ADDR_NOT_AVAILABLE", ERR_LIB_BIO, BIO_R_PEER_ADDR_NOT_AVAILABLE}, + #else + {"PEER_ADDR_NOT_AVAILABLE", 32, 114}, + #endif + #ifdef BIO_R_PORT_MISMATCH + {"PORT_MISMATCH", ERR_LIB_BIO, BIO_R_PORT_MISMATCH}, + #else + {"PORT_MISMATCH", 32, 150}, + #endif + #ifdef BIO_R_TFO_DISABLED + {"TFO_DISABLED", ERR_LIB_BIO, BIO_R_TFO_DISABLED}, + #else + {"TFO_DISABLED", 32, 106}, + #endif + #ifdef BIO_R_TFO_NO_KERNEL_SUPPORT + {"TFO_NO_KERNEL_SUPPORT", ERR_LIB_BIO, BIO_R_TFO_NO_KERNEL_SUPPORT}, + #else + {"TFO_NO_KERNEL_SUPPORT", 32, 108}, + #endif + #ifdef BIO_R_TRANSFER_ERROR + {"TRANSFER_ERROR", ERR_LIB_BIO, BIO_R_TRANSFER_ERROR}, + #else + {"TRANSFER_ERROR", 32, 104}, + #endif + #ifdef BIO_R_TRANSFER_TIMEOUT + {"TRANSFER_TIMEOUT", ERR_LIB_BIO, BIO_R_TRANSFER_TIMEOUT}, + #else + {"TRANSFER_TIMEOUT", 32, 105}, + #endif + #ifdef BIO_R_UNABLE_TO_BIND_SOCKET + {"UNABLE_TO_BIND_SOCKET", ERR_LIB_BIO, BIO_R_UNABLE_TO_BIND_SOCKET}, + #else + {"UNABLE_TO_BIND_SOCKET", 32, 117}, + #endif + #ifdef BIO_R_UNABLE_TO_CREATE_SOCKET + {"UNABLE_TO_CREATE_SOCKET", ERR_LIB_BIO, BIO_R_UNABLE_TO_CREATE_SOCKET}, + #else + {"UNABLE_TO_CREATE_SOCKET", 32, 118}, + #endif + #ifdef BIO_R_UNABLE_TO_KEEPALIVE + {"UNABLE_TO_KEEPALIVE", ERR_LIB_BIO, BIO_R_UNABLE_TO_KEEPALIVE}, + #else + {"UNABLE_TO_KEEPALIVE", 32, 137}, + #endif + #ifdef BIO_R_UNABLE_TO_LISTEN_SOCKET + {"UNABLE_TO_LISTEN_SOCKET", ERR_LIB_BIO, BIO_R_UNABLE_TO_LISTEN_SOCKET}, + #else + {"UNABLE_TO_LISTEN_SOCKET", 32, 119}, + #endif + #ifdef BIO_R_UNABLE_TO_NODELAY + {"UNABLE_TO_NODELAY", ERR_LIB_BIO, BIO_R_UNABLE_TO_NODELAY}, + #else + {"UNABLE_TO_NODELAY", 32, 138}, + #endif + #ifdef BIO_R_UNABLE_TO_REUSEADDR + {"UNABLE_TO_REUSEADDR", ERR_LIB_BIO, BIO_R_UNABLE_TO_REUSEADDR}, + #else + {"UNABLE_TO_REUSEADDR", 32, 139}, + #endif + #ifdef BIO_R_UNABLE_TO_TFO + {"UNABLE_TO_TFO", ERR_LIB_BIO, BIO_R_UNABLE_TO_TFO}, + #else + {"UNABLE_TO_TFO", 32, 109}, + #endif + #ifdef BIO_R_UNAVAILABLE_IP_FAMILY + {"UNAVAILABLE_IP_FAMILY", ERR_LIB_BIO, BIO_R_UNAVAILABLE_IP_FAMILY}, + #else + {"UNAVAILABLE_IP_FAMILY", 32, 145}, + #endif + #ifdef BIO_R_UNINITIALIZED + {"UNINITIALIZED", ERR_LIB_BIO, BIO_R_UNINITIALIZED}, + #else + {"UNINITIALIZED", 32, 120}, + #endif + #ifdef BIO_R_UNKNOWN_INFO_TYPE + {"UNKNOWN_INFO_TYPE", ERR_LIB_BIO, BIO_R_UNKNOWN_INFO_TYPE}, + #else + {"UNKNOWN_INFO_TYPE", 32, 140}, + #endif + #ifdef BIO_R_UNSUPPORTED_IP_FAMILY + {"UNSUPPORTED_IP_FAMILY", ERR_LIB_BIO, BIO_R_UNSUPPORTED_IP_FAMILY}, + #else + {"UNSUPPORTED_IP_FAMILY", 32, 146}, + #endif + #ifdef BIO_R_UNSUPPORTED_METHOD + {"UNSUPPORTED_METHOD", ERR_LIB_BIO, BIO_R_UNSUPPORTED_METHOD}, + #else + {"UNSUPPORTED_METHOD", 32, 121}, + #endif + #ifdef BIO_R_UNSUPPORTED_PROTOCOL_FAMILY + {"UNSUPPORTED_PROTOCOL_FAMILY", ERR_LIB_BIO, BIO_R_UNSUPPORTED_PROTOCOL_FAMILY}, + #else + {"UNSUPPORTED_PROTOCOL_FAMILY", 32, 131}, + #endif + #ifdef BIO_R_WRITE_TO_READ_ONLY_BIO + {"WRITE_TO_READ_ONLY_BIO", ERR_LIB_BIO, BIO_R_WRITE_TO_READ_ONLY_BIO}, + #else + {"WRITE_TO_READ_ONLY_BIO", 32, 126}, + #endif + #ifdef BIO_R_WSASTARTUP + {"WSASTARTUP", ERR_LIB_BIO, BIO_R_WSASTARTUP}, + #else + {"WSASTARTUP", 32, 122}, + #endif + #ifdef BN_R_ARG2_LT_ARG3 + {"ARG2_LT_ARG3", ERR_LIB_BN, BN_R_ARG2_LT_ARG3}, + #else + {"ARG2_LT_ARG3", 3, 100}, + #endif + #ifdef BN_R_BAD_RECIPROCAL + {"BAD_RECIPROCAL", ERR_LIB_BN, BN_R_BAD_RECIPROCAL}, + #else + {"BAD_RECIPROCAL", 3, 101}, + #endif + #ifdef BN_R_BIGNUM_TOO_LONG + {"BIGNUM_TOO_LONG", ERR_LIB_BN, BN_R_BIGNUM_TOO_LONG}, + #else + {"BIGNUM_TOO_LONG", 3, 114}, + #endif + #ifdef BN_R_BITS_TOO_SMALL + {"BITS_TOO_SMALL", ERR_LIB_BN, BN_R_BITS_TOO_SMALL}, + #else + {"BITS_TOO_SMALL", 3, 118}, + #endif + #ifdef BN_R_CALLED_WITH_EVEN_MODULUS + {"CALLED_WITH_EVEN_MODULUS", ERR_LIB_BN, BN_R_CALLED_WITH_EVEN_MODULUS}, + #else + {"CALLED_WITH_EVEN_MODULUS", 3, 102}, + #endif + #ifdef BN_R_DIV_BY_ZERO + {"DIV_BY_ZERO", ERR_LIB_BN, BN_R_DIV_BY_ZERO}, + #else + {"DIV_BY_ZERO", 3, 103}, + #endif + #ifdef BN_R_ENCODING_ERROR + {"ENCODING_ERROR", ERR_LIB_BN, BN_R_ENCODING_ERROR}, + #else + {"ENCODING_ERROR", 3, 104}, + #endif + #ifdef BN_R_EXPAND_ON_STATIC_BIGNUM_DATA + {"EXPAND_ON_STATIC_BIGNUM_DATA", ERR_LIB_BN, BN_R_EXPAND_ON_STATIC_BIGNUM_DATA}, + #else + {"EXPAND_ON_STATIC_BIGNUM_DATA", 3, 105}, + #endif + #ifdef BN_R_INPUT_NOT_REDUCED + {"INPUT_NOT_REDUCED", ERR_LIB_BN, BN_R_INPUT_NOT_REDUCED}, + #else + {"INPUT_NOT_REDUCED", 3, 110}, + #endif + #ifdef BN_R_INVALID_LENGTH + {"INVALID_LENGTH", ERR_LIB_BN, BN_R_INVALID_LENGTH}, + #else + {"INVALID_LENGTH", 3, 106}, + #endif + #ifdef BN_R_INVALID_RANGE + {"INVALID_RANGE", ERR_LIB_BN, BN_R_INVALID_RANGE}, + #else + {"INVALID_RANGE", 3, 115}, + #endif + #ifdef BN_R_INVALID_SHIFT + {"INVALID_SHIFT", ERR_LIB_BN, BN_R_INVALID_SHIFT}, + #else + {"INVALID_SHIFT", 3, 119}, + #endif + #ifdef BN_R_NOT_A_SQUARE + {"NOT_A_SQUARE", ERR_LIB_BN, BN_R_NOT_A_SQUARE}, + #else + {"NOT_A_SQUARE", 3, 111}, + #endif + #ifdef BN_R_NOT_INITIALIZED + {"NOT_INITIALIZED", ERR_LIB_BN, BN_R_NOT_INITIALIZED}, + #else + {"NOT_INITIALIZED", 3, 107}, + #endif + #ifdef BN_R_NO_INVERSE + {"NO_INVERSE", ERR_LIB_BN, BN_R_NO_INVERSE}, + #else + {"NO_INVERSE", 3, 108}, + #endif + #ifdef BN_R_NO_PRIME_CANDIDATE + {"NO_PRIME_CANDIDATE", ERR_LIB_BN, BN_R_NO_PRIME_CANDIDATE}, + #else + {"NO_PRIME_CANDIDATE", 3, 121}, + #endif + #ifdef BN_R_NO_SOLUTION + {"NO_SOLUTION", ERR_LIB_BN, BN_R_NO_SOLUTION}, + #else + {"NO_SOLUTION", 3, 116}, + #endif + #ifdef BN_R_NO_SUITABLE_DIGEST + {"NO_SUITABLE_DIGEST", ERR_LIB_BN, BN_R_NO_SUITABLE_DIGEST}, + #else + {"NO_SUITABLE_DIGEST", 3, 120}, + #endif + #ifdef BN_R_PRIVATE_KEY_TOO_LARGE + {"PRIVATE_KEY_TOO_LARGE", ERR_LIB_BN, BN_R_PRIVATE_KEY_TOO_LARGE}, + #else + {"PRIVATE_KEY_TOO_LARGE", 3, 117}, + #endif + #ifdef BN_R_P_IS_NOT_PRIME + {"P_IS_NOT_PRIME", ERR_LIB_BN, BN_R_P_IS_NOT_PRIME}, + #else + {"P_IS_NOT_PRIME", 3, 112}, + #endif + #ifdef BN_R_TOO_MANY_ITERATIONS + {"TOO_MANY_ITERATIONS", ERR_LIB_BN, BN_R_TOO_MANY_ITERATIONS}, + #else + {"TOO_MANY_ITERATIONS", 3, 113}, + #endif + #ifdef BN_R_TOO_MANY_TEMPORARY_VARIABLES + {"TOO_MANY_TEMPORARY_VARIABLES", ERR_LIB_BN, BN_R_TOO_MANY_TEMPORARY_VARIABLES}, + #else + {"TOO_MANY_TEMPORARY_VARIABLES", 3, 109}, + #endif + #ifdef CMP_R_ALGORITHM_NOT_SUPPORTED + {"ALGORITHM_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_ALGORITHM_NOT_SUPPORTED}, + #else + {"ALGORITHM_NOT_SUPPORTED", 58, 139}, + #endif + #ifdef CMP_R_BAD_CHECKAFTER_IN_POLLREP + {"BAD_CHECKAFTER_IN_POLLREP", ERR_LIB_CMP, CMP_R_BAD_CHECKAFTER_IN_POLLREP}, + #else + {"BAD_CHECKAFTER_IN_POLLREP", 58, 167}, + #endif + #ifdef CMP_R_BAD_REQUEST_ID + {"BAD_REQUEST_ID", ERR_LIB_CMP, CMP_R_BAD_REQUEST_ID}, + #else + {"BAD_REQUEST_ID", 58, 108}, + #endif + #ifdef CMP_R_CERTHASH_UNMATCHED + {"CERTHASH_UNMATCHED", ERR_LIB_CMP, CMP_R_CERTHASH_UNMATCHED}, + #else + {"CERTHASH_UNMATCHED", 58, 156}, + #endif + #ifdef CMP_R_CERTID_NOT_FOUND + {"CERTID_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTID_NOT_FOUND}, + #else + {"CERTID_NOT_FOUND", 58, 109}, + #endif + #ifdef CMP_R_CERTIFICATE_NOT_ACCEPTED + {"CERTIFICATE_NOT_ACCEPTED", ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_ACCEPTED}, + #else + {"CERTIFICATE_NOT_ACCEPTED", 58, 169}, + #endif + #ifdef CMP_R_CERTIFICATE_NOT_FOUND + {"CERTIFICATE_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_FOUND}, + #else + {"CERTIFICATE_NOT_FOUND", 58, 112}, + #endif + #ifdef CMP_R_CERTREQMSG_NOT_FOUND + {"CERTREQMSG_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTREQMSG_NOT_FOUND}, + #else + {"CERTREQMSG_NOT_FOUND", 58, 157}, + #endif + #ifdef CMP_R_CERTRESPONSE_NOT_FOUND + {"CERTRESPONSE_NOT_FOUND", ERR_LIB_CMP, CMP_R_CERTRESPONSE_NOT_FOUND}, + #else + {"CERTRESPONSE_NOT_FOUND", 58, 113}, + #endif + #ifdef CMP_R_CERT_AND_KEY_DO_NOT_MATCH + {"CERT_AND_KEY_DO_NOT_MATCH", ERR_LIB_CMP, CMP_R_CERT_AND_KEY_DO_NOT_MATCH}, + #else + {"CERT_AND_KEY_DO_NOT_MATCH", 58, 114}, + #endif + #ifdef CMP_R_CHECKAFTER_OUT_OF_RANGE + {"CHECKAFTER_OUT_OF_RANGE", ERR_LIB_CMP, CMP_R_CHECKAFTER_OUT_OF_RANGE}, + #else + {"CHECKAFTER_OUT_OF_RANGE", 58, 181}, + #endif + #ifdef CMP_R_ENCOUNTERED_KEYUPDATEWARNING + {"ENCOUNTERED_KEYUPDATEWARNING", ERR_LIB_CMP, CMP_R_ENCOUNTERED_KEYUPDATEWARNING}, + #else + {"ENCOUNTERED_KEYUPDATEWARNING", 58, 176}, + #endif + #ifdef CMP_R_ENCOUNTERED_WAITING + {"ENCOUNTERED_WAITING", ERR_LIB_CMP, CMP_R_ENCOUNTERED_WAITING}, + #else + {"ENCOUNTERED_WAITING", 58, 162}, + #endif + #ifdef CMP_R_ERROR_CALCULATING_PROTECTION + {"ERROR_CALCULATING_PROTECTION", ERR_LIB_CMP, CMP_R_ERROR_CALCULATING_PROTECTION}, + #else + {"ERROR_CALCULATING_PROTECTION", 58, 115}, + #endif + #ifdef CMP_R_ERROR_CREATING_CERTCONF + {"ERROR_CREATING_CERTCONF", ERR_LIB_CMP, CMP_R_ERROR_CREATING_CERTCONF}, + #else + {"ERROR_CREATING_CERTCONF", 58, 116}, + #endif + #ifdef CMP_R_ERROR_CREATING_CERTREP + {"ERROR_CREATING_CERTREP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_CERTREP}, + #else + {"ERROR_CREATING_CERTREP", 58, 117}, + #endif + #ifdef CMP_R_ERROR_CREATING_CERTREQ + {"ERROR_CREATING_CERTREQ", ERR_LIB_CMP, CMP_R_ERROR_CREATING_CERTREQ}, + #else + {"ERROR_CREATING_CERTREQ", 58, 163}, + #endif + #ifdef CMP_R_ERROR_CREATING_ERROR + {"ERROR_CREATING_ERROR", ERR_LIB_CMP, CMP_R_ERROR_CREATING_ERROR}, + #else + {"ERROR_CREATING_ERROR", 58, 118}, + #endif + #ifdef CMP_R_ERROR_CREATING_GENM + {"ERROR_CREATING_GENM", ERR_LIB_CMP, CMP_R_ERROR_CREATING_GENM}, + #else + {"ERROR_CREATING_GENM", 58, 119}, + #endif + #ifdef CMP_R_ERROR_CREATING_GENP + {"ERROR_CREATING_GENP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_GENP}, + #else + {"ERROR_CREATING_GENP", 58, 120}, + #endif + #ifdef CMP_R_ERROR_CREATING_PKICONF + {"ERROR_CREATING_PKICONF", ERR_LIB_CMP, CMP_R_ERROR_CREATING_PKICONF}, + #else + {"ERROR_CREATING_PKICONF", 58, 122}, + #endif + #ifdef CMP_R_ERROR_CREATING_POLLREP + {"ERROR_CREATING_POLLREP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_POLLREP}, + #else + {"ERROR_CREATING_POLLREP", 58, 123}, + #endif + #ifdef CMP_R_ERROR_CREATING_POLLREQ + {"ERROR_CREATING_POLLREQ", ERR_LIB_CMP, CMP_R_ERROR_CREATING_POLLREQ}, + #else + {"ERROR_CREATING_POLLREQ", 58, 124}, + #endif + #ifdef CMP_R_ERROR_CREATING_RP + {"ERROR_CREATING_RP", ERR_LIB_CMP, CMP_R_ERROR_CREATING_RP}, + #else + {"ERROR_CREATING_RP", 58, 125}, + #endif + #ifdef CMP_R_ERROR_CREATING_RR + {"ERROR_CREATING_RR", ERR_LIB_CMP, CMP_R_ERROR_CREATING_RR}, + #else + {"ERROR_CREATING_RR", 58, 126}, + #endif + #ifdef CMP_R_ERROR_PARSING_PKISTATUS + {"ERROR_PARSING_PKISTATUS", ERR_LIB_CMP, CMP_R_ERROR_PARSING_PKISTATUS}, + #else + {"ERROR_PARSING_PKISTATUS", 58, 107}, + #endif + #ifdef CMP_R_ERROR_PROCESSING_MESSAGE + {"ERROR_PROCESSING_MESSAGE", ERR_LIB_CMP, CMP_R_ERROR_PROCESSING_MESSAGE}, + #else + {"ERROR_PROCESSING_MESSAGE", 58, 158}, + #endif + #ifdef CMP_R_ERROR_PROTECTING_MESSAGE + {"ERROR_PROTECTING_MESSAGE", ERR_LIB_CMP, CMP_R_ERROR_PROTECTING_MESSAGE}, + #else + {"ERROR_PROTECTING_MESSAGE", 58, 127}, + #endif + #ifdef CMP_R_ERROR_SETTING_CERTHASH + {"ERROR_SETTING_CERTHASH", ERR_LIB_CMP, CMP_R_ERROR_SETTING_CERTHASH}, + #else + {"ERROR_SETTING_CERTHASH", 58, 128}, + #endif + #ifdef CMP_R_ERROR_UNEXPECTED_CERTCONF + {"ERROR_UNEXPECTED_CERTCONF", ERR_LIB_CMP, CMP_R_ERROR_UNEXPECTED_CERTCONF}, + #else + {"ERROR_UNEXPECTED_CERTCONF", 58, 160}, + #endif + #ifdef CMP_R_ERROR_VALIDATING_PROTECTION + {"ERROR_VALIDATING_PROTECTION", ERR_LIB_CMP, CMP_R_ERROR_VALIDATING_PROTECTION}, + #else + {"ERROR_VALIDATING_PROTECTION", 58, 140}, + #endif + #ifdef CMP_R_ERROR_VALIDATING_SIGNATURE + {"ERROR_VALIDATING_SIGNATURE", ERR_LIB_CMP, CMP_R_ERROR_VALIDATING_SIGNATURE}, + #else + {"ERROR_VALIDATING_SIGNATURE", 58, 171}, + #endif + #ifdef CMP_R_EXPECTED_POLLREQ + {"EXPECTED_POLLREQ", ERR_LIB_CMP, CMP_R_EXPECTED_POLLREQ}, + #else + {"EXPECTED_POLLREQ", 58, 104}, + #endif + #ifdef CMP_R_FAILED_BUILDING_OWN_CHAIN + {"FAILED_BUILDING_OWN_CHAIN", ERR_LIB_CMP, CMP_R_FAILED_BUILDING_OWN_CHAIN}, + #else + {"FAILED_BUILDING_OWN_CHAIN", 58, 164}, + #endif + #ifdef CMP_R_FAILED_EXTRACTING_CENTRAL_GEN_KEY + {"FAILED_EXTRACTING_CENTRAL_GEN_KEY", ERR_LIB_CMP, CMP_R_FAILED_EXTRACTING_CENTRAL_GEN_KEY}, + #else + {"FAILED_EXTRACTING_CENTRAL_GEN_KEY", 58, 203}, + #endif + #ifdef CMP_R_FAILED_EXTRACTING_PUBKEY + {"FAILED_EXTRACTING_PUBKEY", ERR_LIB_CMP, CMP_R_FAILED_EXTRACTING_PUBKEY}, + #else + {"FAILED_EXTRACTING_PUBKEY", 58, 141}, + #endif + #ifdef CMP_R_FAILURE_OBTAINING_RANDOM + {"FAILURE_OBTAINING_RANDOM", ERR_LIB_CMP, CMP_R_FAILURE_OBTAINING_RANDOM}, + #else + {"FAILURE_OBTAINING_RANDOM", 58, 110}, + #endif + #ifdef CMP_R_FAIL_INFO_OUT_OF_RANGE + {"FAIL_INFO_OUT_OF_RANGE", ERR_LIB_CMP, CMP_R_FAIL_INFO_OUT_OF_RANGE}, + #else + {"FAIL_INFO_OUT_OF_RANGE", 58, 129}, + #endif + #ifdef CMP_R_GENERATE_CERTREQTEMPLATE + {"GENERATE_CERTREQTEMPLATE", ERR_LIB_CMP, CMP_R_GENERATE_CERTREQTEMPLATE}, + #else + {"GENERATE_CERTREQTEMPLATE", 58, 197}, + #endif + #ifdef CMP_R_GENERATE_CRLSTATUS + {"GENERATE_CRLSTATUS", ERR_LIB_CMP, CMP_R_GENERATE_CRLSTATUS}, + #else + {"GENERATE_CRLSTATUS", 58, 198}, + #endif + #ifdef CMP_R_GETTING_GENP + {"GETTING_GENP", ERR_LIB_CMP, CMP_R_GETTING_GENP}, + #else + {"GETTING_GENP", 58, 192}, + #endif + #ifdef CMP_R_GET_ITAV + {"GET_ITAV", ERR_LIB_CMP, CMP_R_GET_ITAV}, + #else + {"GET_ITAV", 58, 199}, + #endif + #ifdef CMP_R_INVALID_ARGS + {"INVALID_ARGS", ERR_LIB_CMP, CMP_R_INVALID_ARGS}, + #else + {"INVALID_ARGS", 58, 100}, + #endif + #ifdef CMP_R_INVALID_GENP + {"INVALID_GENP", ERR_LIB_CMP, CMP_R_INVALID_GENP}, + #else + {"INVALID_GENP", 58, 193}, + #endif + #ifdef CMP_R_INVALID_KEYSPEC + {"INVALID_KEYSPEC", ERR_LIB_CMP, CMP_R_INVALID_KEYSPEC}, + #else + {"INVALID_KEYSPEC", 58, 202}, + #endif + #ifdef CMP_R_INVALID_OPTION + {"INVALID_OPTION", ERR_LIB_CMP, CMP_R_INVALID_OPTION}, + #else + {"INVALID_OPTION", 58, 174}, + #endif + #ifdef CMP_R_INVALID_ROOTCAKEYUPDATE + {"INVALID_ROOTCAKEYUPDATE", ERR_LIB_CMP, CMP_R_INVALID_ROOTCAKEYUPDATE}, + #else + {"INVALID_ROOTCAKEYUPDATE", 58, 195}, + #endif + #ifdef CMP_R_MISSING_CENTRAL_GEN_KEY + {"MISSING_CENTRAL_GEN_KEY", ERR_LIB_CMP, CMP_R_MISSING_CENTRAL_GEN_KEY}, + #else + {"MISSING_CENTRAL_GEN_KEY", 58, 204}, + #endif + #ifdef CMP_R_MISSING_CERTID + {"MISSING_CERTID", ERR_LIB_CMP, CMP_R_MISSING_CERTID}, + #else + {"MISSING_CERTID", 58, 165}, + #endif + #ifdef CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION + {"MISSING_KEY_INPUT_FOR_CREATING_PROTECTION", ERR_LIB_CMP, CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION}, + #else + {"MISSING_KEY_INPUT_FOR_CREATING_PROTECTION", 58, 130}, + #endif + #ifdef CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE + {"MISSING_KEY_USAGE_DIGITALSIGNATURE", ERR_LIB_CMP, CMP_R_MISSING_KEY_USAGE_DIGITALSIGNATURE}, + #else + {"MISSING_KEY_USAGE_DIGITALSIGNATURE", 58, 142}, + #endif + #ifdef CMP_R_MISSING_P10CSR + {"MISSING_P10CSR", ERR_LIB_CMP, CMP_R_MISSING_P10CSR}, + #else + {"MISSING_P10CSR", 58, 121}, + #endif + #ifdef CMP_R_MISSING_PBM_SECRET + {"MISSING_PBM_SECRET", ERR_LIB_CMP, CMP_R_MISSING_PBM_SECRET}, + #else + {"MISSING_PBM_SECRET", 58, 166}, + #endif + #ifdef CMP_R_MISSING_PRIVATE_KEY + {"MISSING_PRIVATE_KEY", ERR_LIB_CMP, CMP_R_MISSING_PRIVATE_KEY}, + #else + {"MISSING_PRIVATE_KEY", 58, 131}, + #endif + #ifdef CMP_R_MISSING_PRIVATE_KEY_FOR_POPO + {"MISSING_PRIVATE_KEY_FOR_POPO", ERR_LIB_CMP, CMP_R_MISSING_PRIVATE_KEY_FOR_POPO}, + #else + {"MISSING_PRIVATE_KEY_FOR_POPO", 58, 190}, + #endif + #ifdef CMP_R_MISSING_PROTECTION + {"MISSING_PROTECTION", ERR_LIB_CMP, CMP_R_MISSING_PROTECTION}, + #else + {"MISSING_PROTECTION", 58, 143}, + #endif + #ifdef CMP_R_MISSING_PUBLIC_KEY + {"MISSING_PUBLIC_KEY", ERR_LIB_CMP, CMP_R_MISSING_PUBLIC_KEY}, + #else + {"MISSING_PUBLIC_KEY", 58, 183}, + #endif + #ifdef CMP_R_MISSING_REFERENCE_CERT + {"MISSING_REFERENCE_CERT", ERR_LIB_CMP, CMP_R_MISSING_REFERENCE_CERT}, + #else + {"MISSING_REFERENCE_CERT", 58, 168}, + #endif + #ifdef CMP_R_MISSING_SECRET + {"MISSING_SECRET", ERR_LIB_CMP, CMP_R_MISSING_SECRET}, + #else + {"MISSING_SECRET", 58, 178}, + #endif + #ifdef CMP_R_MISSING_SENDER_IDENTIFICATION + {"MISSING_SENDER_IDENTIFICATION", ERR_LIB_CMP, CMP_R_MISSING_SENDER_IDENTIFICATION}, + #else + {"MISSING_SENDER_IDENTIFICATION", 58, 111}, + #endif + #ifdef CMP_R_MISSING_TRUST_ANCHOR + {"MISSING_TRUST_ANCHOR", ERR_LIB_CMP, CMP_R_MISSING_TRUST_ANCHOR}, + #else + {"MISSING_TRUST_ANCHOR", 58, 179}, + #endif + #ifdef CMP_R_MISSING_TRUST_STORE + {"MISSING_TRUST_STORE", ERR_LIB_CMP, CMP_R_MISSING_TRUST_STORE}, + #else + {"MISSING_TRUST_STORE", 58, 144}, + #endif + #ifdef CMP_R_MULTIPLE_REQUESTS_NOT_SUPPORTED + {"MULTIPLE_REQUESTS_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_MULTIPLE_REQUESTS_NOT_SUPPORTED}, + #else + {"MULTIPLE_REQUESTS_NOT_SUPPORTED", 58, 161}, + #endif + #ifdef CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED + {"MULTIPLE_RESPONSES_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_MULTIPLE_RESPONSES_NOT_SUPPORTED}, + #else + {"MULTIPLE_RESPONSES_NOT_SUPPORTED", 58, 170}, + #endif + #ifdef CMP_R_MULTIPLE_SAN_SOURCES + {"MULTIPLE_SAN_SOURCES", ERR_LIB_CMP, CMP_R_MULTIPLE_SAN_SOURCES}, + #else + {"MULTIPLE_SAN_SOURCES", 58, 102}, + #endif + #ifdef CMP_R_NO_STDIO + {"NO_STDIO", ERR_LIB_CMP, CMP_R_NO_STDIO}, + #else + {"NO_STDIO", 58, 194}, + #endif + #ifdef CMP_R_NO_SUITABLE_SENDER_CERT + {"NO_SUITABLE_SENDER_CERT", ERR_LIB_CMP, CMP_R_NO_SUITABLE_SENDER_CERT}, + #else + {"NO_SUITABLE_SENDER_CERT", 58, 145}, + #endif + #ifdef CMP_R_NULL_ARGUMENT + {"NULL_ARGUMENT", ERR_LIB_CMP, CMP_R_NULL_ARGUMENT}, + #else + {"NULL_ARGUMENT", 58, 103}, + #endif + #ifdef CMP_R_PKIBODY_ERROR + {"PKIBODY_ERROR", ERR_LIB_CMP, CMP_R_PKIBODY_ERROR}, + #else + {"PKIBODY_ERROR", 58, 146}, + #endif + #ifdef CMP_R_PKISTATUSINFO_NOT_FOUND + {"PKISTATUSINFO_NOT_FOUND", ERR_LIB_CMP, CMP_R_PKISTATUSINFO_NOT_FOUND}, + #else + {"PKISTATUSINFO_NOT_FOUND", 58, 132}, + #endif + #ifdef CMP_R_POLLING_FAILED + {"POLLING_FAILED", ERR_LIB_CMP, CMP_R_POLLING_FAILED}, + #else + {"POLLING_FAILED", 58, 172}, + #endif + #ifdef CMP_R_POTENTIALLY_INVALID_CERTIFICATE + {"POTENTIALLY_INVALID_CERTIFICATE", ERR_LIB_CMP, CMP_R_POTENTIALLY_INVALID_CERTIFICATE}, + #else + {"POTENTIALLY_INVALID_CERTIFICATE", 58, 147}, + #endif + #ifdef CMP_R_RECEIVED_ERROR + {"RECEIVED_ERROR", ERR_LIB_CMP, CMP_R_RECEIVED_ERROR}, + #else + {"RECEIVED_ERROR", 58, 180}, + #endif + #ifdef CMP_R_RECIPNONCE_UNMATCHED + {"RECIPNONCE_UNMATCHED", ERR_LIB_CMP, CMP_R_RECIPNONCE_UNMATCHED}, + #else + {"RECIPNONCE_UNMATCHED", 58, 148}, + #endif + #ifdef CMP_R_REQUEST_NOT_ACCEPTED + {"REQUEST_NOT_ACCEPTED", ERR_LIB_CMP, CMP_R_REQUEST_NOT_ACCEPTED}, + #else + {"REQUEST_NOT_ACCEPTED", 58, 149}, + #endif + #ifdef CMP_R_REQUEST_REJECTED_BY_SERVER + {"REQUEST_REJECTED_BY_SERVER", ERR_LIB_CMP, CMP_R_REQUEST_REJECTED_BY_SERVER}, + #else + {"REQUEST_REJECTED_BY_SERVER", 58, 182}, + #endif + #ifdef CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED + {"SENDER_GENERALNAME_TYPE_NOT_SUPPORTED", ERR_LIB_CMP, CMP_R_SENDER_GENERALNAME_TYPE_NOT_SUPPORTED}, + #else + {"SENDER_GENERALNAME_TYPE_NOT_SUPPORTED", 58, 150}, + #endif + #ifdef CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG + {"SRVCERT_DOES_NOT_VALIDATE_MSG", ERR_LIB_CMP, CMP_R_SRVCERT_DOES_NOT_VALIDATE_MSG}, + #else + {"SRVCERT_DOES_NOT_VALIDATE_MSG", 58, 151}, + #endif + #ifdef CMP_R_TOTAL_TIMEOUT + {"TOTAL_TIMEOUT", ERR_LIB_CMP, CMP_R_TOTAL_TIMEOUT}, + #else + {"TOTAL_TIMEOUT", 58, 184}, + #endif + #ifdef CMP_R_TRANSACTIONID_UNMATCHED + {"TRANSACTIONID_UNMATCHED", ERR_LIB_CMP, CMP_R_TRANSACTIONID_UNMATCHED}, + #else + {"TRANSACTIONID_UNMATCHED", 58, 152}, + #endif + #ifdef CMP_R_TRANSFER_ERROR + {"TRANSFER_ERROR", ERR_LIB_CMP, CMP_R_TRANSFER_ERROR}, + #else + {"TRANSFER_ERROR", 58, 159}, + #endif + #ifdef CMP_R_UNCLEAN_CTX + {"UNCLEAN_CTX", ERR_LIB_CMP, CMP_R_UNCLEAN_CTX}, + #else + {"UNCLEAN_CTX", 58, 191}, + #endif + #ifdef CMP_R_UNEXPECTED_CENTRAL_GEN_KEY + {"UNEXPECTED_CENTRAL_GEN_KEY", ERR_LIB_CMP, CMP_R_UNEXPECTED_CENTRAL_GEN_KEY}, + #else + {"UNEXPECTED_CENTRAL_GEN_KEY", 58, 205}, + #endif + #ifdef CMP_R_UNEXPECTED_CERTPROFILE + {"UNEXPECTED_CERTPROFILE", ERR_LIB_CMP, CMP_R_UNEXPECTED_CERTPROFILE}, + #else + {"UNEXPECTED_CERTPROFILE", 58, 196}, + #endif + #ifdef CMP_R_UNEXPECTED_CRLSTATUSLIST + {"UNEXPECTED_CRLSTATUSLIST", ERR_LIB_CMP, CMP_R_UNEXPECTED_CRLSTATUSLIST}, + #else + {"UNEXPECTED_CRLSTATUSLIST", 58, 201}, + #endif + #ifdef CMP_R_UNEXPECTED_PKIBODY + {"UNEXPECTED_PKIBODY", ERR_LIB_CMP, CMP_R_UNEXPECTED_PKIBODY}, + #else + {"UNEXPECTED_PKIBODY", 58, 133}, + #endif + #ifdef CMP_R_UNEXPECTED_PKISTATUS + {"UNEXPECTED_PKISTATUS", ERR_LIB_CMP, CMP_R_UNEXPECTED_PKISTATUS}, + #else + {"UNEXPECTED_PKISTATUS", 58, 185}, + #endif + #ifdef CMP_R_UNEXPECTED_POLLREQ + {"UNEXPECTED_POLLREQ", ERR_LIB_CMP, CMP_R_UNEXPECTED_POLLREQ}, + #else + {"UNEXPECTED_POLLREQ", 58, 105}, + #endif + #ifdef CMP_R_UNEXPECTED_PVNO + {"UNEXPECTED_PVNO", ERR_LIB_CMP, CMP_R_UNEXPECTED_PVNO}, + #else + {"UNEXPECTED_PVNO", 58, 153}, + #endif + #ifdef CMP_R_UNEXPECTED_SENDER + {"UNEXPECTED_SENDER", ERR_LIB_CMP, CMP_R_UNEXPECTED_SENDER}, + #else + {"UNEXPECTED_SENDER", 58, 106}, + #endif + #ifdef CMP_R_UNKNOWN_ALGORITHM_ID + {"UNKNOWN_ALGORITHM_ID", ERR_LIB_CMP, CMP_R_UNKNOWN_ALGORITHM_ID}, + #else + {"UNKNOWN_ALGORITHM_ID", 58, 134}, + #endif + #ifdef CMP_R_UNKNOWN_CERT_TYPE + {"UNKNOWN_CERT_TYPE", ERR_LIB_CMP, CMP_R_UNKNOWN_CERT_TYPE}, + #else + {"UNKNOWN_CERT_TYPE", 58, 135}, + #endif + #ifdef CMP_R_UNKNOWN_CRL_ISSUER + {"UNKNOWN_CRL_ISSUER", ERR_LIB_CMP, CMP_R_UNKNOWN_CRL_ISSUER}, + #else + {"UNKNOWN_CRL_ISSUER", 58, 200}, + #endif + #ifdef CMP_R_UNKNOWN_PKISTATUS + {"UNKNOWN_PKISTATUS", ERR_LIB_CMP, CMP_R_UNKNOWN_PKISTATUS}, + #else + {"UNKNOWN_PKISTATUS", 58, 186}, + #endif + #ifdef CMP_R_UNSUPPORTED_ALGORITHM + {"UNSUPPORTED_ALGORITHM", ERR_LIB_CMP, CMP_R_UNSUPPORTED_ALGORITHM}, + #else + {"UNSUPPORTED_ALGORITHM", 58, 136}, + #endif + #ifdef CMP_R_UNSUPPORTED_KEY_TYPE + {"UNSUPPORTED_KEY_TYPE", ERR_LIB_CMP, CMP_R_UNSUPPORTED_KEY_TYPE}, + #else + {"UNSUPPORTED_KEY_TYPE", 58, 137}, + #endif + #ifdef CMP_R_UNSUPPORTED_PKIBODY + {"UNSUPPORTED_PKIBODY", ERR_LIB_CMP, CMP_R_UNSUPPORTED_PKIBODY}, + #else + {"UNSUPPORTED_PKIBODY", 58, 101}, + #endif + #ifdef CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC + {"UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC", ERR_LIB_CMP, CMP_R_UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC}, + #else + {"UNSUPPORTED_PROTECTION_ALG_DHBASEDMAC", 58, 154}, + #endif + #ifdef CMP_R_VALUE_TOO_LARGE + {"VALUE_TOO_LARGE", ERR_LIB_CMP, CMP_R_VALUE_TOO_LARGE}, + #else + {"VALUE_TOO_LARGE", 58, 175}, + #endif + #ifdef CMP_R_VALUE_TOO_SMALL + {"VALUE_TOO_SMALL", ERR_LIB_CMP, CMP_R_VALUE_TOO_SMALL}, + #else + {"VALUE_TOO_SMALL", 58, 177}, + #endif + #ifdef CMP_R_WRONG_ALGORITHM_OID + {"WRONG_ALGORITHM_OID", ERR_LIB_CMP, CMP_R_WRONG_ALGORITHM_OID}, + #else + {"WRONG_ALGORITHM_OID", 58, 138}, + #endif + #ifdef CMP_R_WRONG_CERTID + {"WRONG_CERTID", ERR_LIB_CMP, CMP_R_WRONG_CERTID}, + #else + {"WRONG_CERTID", 58, 189}, + #endif + #ifdef CMP_R_WRONG_CERTID_IN_RP + {"WRONG_CERTID_IN_RP", ERR_LIB_CMP, CMP_R_WRONG_CERTID_IN_RP}, + #else + {"WRONG_CERTID_IN_RP", 58, 187}, + #endif + #ifdef CMP_R_WRONG_PBM_VALUE + {"WRONG_PBM_VALUE", ERR_LIB_CMP, CMP_R_WRONG_PBM_VALUE}, + #else + {"WRONG_PBM_VALUE", 58, 155}, + #endif + #ifdef CMP_R_WRONG_RP_COMPONENT_COUNT + {"WRONG_RP_COMPONENT_COUNT", ERR_LIB_CMP, CMP_R_WRONG_RP_COMPONENT_COUNT}, + #else + {"WRONG_RP_COMPONENT_COUNT", 58, 188}, + #endif + #ifdef CMP_R_WRONG_SERIAL_IN_RP + {"WRONG_SERIAL_IN_RP", ERR_LIB_CMP, CMP_R_WRONG_SERIAL_IN_RP}, + #else + {"WRONG_SERIAL_IN_RP", 58, 173}, + #endif + #ifdef CMS_R_ADD_SIGNER_ERROR + {"ADD_SIGNER_ERROR", ERR_LIB_CMS, CMS_R_ADD_SIGNER_ERROR}, + #else + {"ADD_SIGNER_ERROR", 46, 99}, + #endif + #ifdef CMS_R_ATTRIBUTE_ERROR + {"ATTRIBUTE_ERROR", ERR_LIB_CMS, CMS_R_ATTRIBUTE_ERROR}, + #else + {"ATTRIBUTE_ERROR", 46, 161}, + #endif + #ifdef CMS_R_CERTIFICATE_ALREADY_PRESENT + {"CERTIFICATE_ALREADY_PRESENT", ERR_LIB_CMS, CMS_R_CERTIFICATE_ALREADY_PRESENT}, + #else + {"CERTIFICATE_ALREADY_PRESENT", 46, 175}, + #endif + #ifdef CMS_R_CERTIFICATE_HAS_NO_KEYID + {"CERTIFICATE_HAS_NO_KEYID", ERR_LIB_CMS, CMS_R_CERTIFICATE_HAS_NO_KEYID}, + #else + {"CERTIFICATE_HAS_NO_KEYID", 46, 160}, + #endif + #ifdef CMS_R_CERTIFICATE_VERIFY_ERROR + {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_CMS, CMS_R_CERTIFICATE_VERIFY_ERROR}, + #else + {"CERTIFICATE_VERIFY_ERROR", 46, 100}, + #endif + #ifdef CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA + {"CIPHER_AEAD_IN_ENVELOPED_DATA", ERR_LIB_CMS, CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA}, + #else + {"CIPHER_AEAD_IN_ENVELOPED_DATA", 46, 200}, + #endif + #ifdef CMS_R_CIPHER_AEAD_SET_TAG_ERROR + {"CIPHER_AEAD_SET_TAG_ERROR", ERR_LIB_CMS, CMS_R_CIPHER_AEAD_SET_TAG_ERROR}, + #else + {"CIPHER_AEAD_SET_TAG_ERROR", 46, 184}, + #endif + #ifdef CMS_R_CIPHER_GET_TAG + {"CIPHER_GET_TAG", ERR_LIB_CMS, CMS_R_CIPHER_GET_TAG}, + #else + {"CIPHER_GET_TAG", 46, 185}, + #endif + #ifdef CMS_R_CIPHER_INITIALISATION_ERROR + {"CIPHER_INITIALISATION_ERROR", ERR_LIB_CMS, CMS_R_CIPHER_INITIALISATION_ERROR}, + #else + {"CIPHER_INITIALISATION_ERROR", 46, 101}, + #endif + #ifdef CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR + {"CIPHER_PARAMETER_INITIALISATION_ERROR", ERR_LIB_CMS, CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR}, + #else + {"CIPHER_PARAMETER_INITIALISATION_ERROR", 46, 102}, + #endif + #ifdef CMS_R_CMS_DATAFINAL_ERROR + {"CMS_DATAFINAL_ERROR", ERR_LIB_CMS, CMS_R_CMS_DATAFINAL_ERROR}, + #else + {"CMS_DATAFINAL_ERROR", 46, 103}, + #endif + #ifdef CMS_R_CMS_LIB + {"CMS_LIB", ERR_LIB_CMS, CMS_R_CMS_LIB}, + #else + {"CMS_LIB", 46, 104}, + #endif + #ifdef CMS_R_CONTENTIDENTIFIER_MISMATCH + {"CONTENTIDENTIFIER_MISMATCH", ERR_LIB_CMS, CMS_R_CONTENTIDENTIFIER_MISMATCH}, + #else + {"CONTENTIDENTIFIER_MISMATCH", 46, 170}, + #endif + #ifdef CMS_R_CONTENT_NOT_FOUND + {"CONTENT_NOT_FOUND", ERR_LIB_CMS, CMS_R_CONTENT_NOT_FOUND}, + #else + {"CONTENT_NOT_FOUND", 46, 105}, + #endif + #ifdef CMS_R_CONTENT_TYPE_MISMATCH + {"CONTENT_TYPE_MISMATCH", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_MISMATCH}, + #else + {"CONTENT_TYPE_MISMATCH", 46, 171}, + #endif + #ifdef CMS_R_CONTENT_TYPE_NOT_COMPRESSED_DATA + {"CONTENT_TYPE_NOT_COMPRESSED_DATA", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_NOT_COMPRESSED_DATA}, + #else + {"CONTENT_TYPE_NOT_COMPRESSED_DATA", 46, 106}, + #endif + #ifdef CMS_R_CONTENT_TYPE_NOT_ENVELOPED_DATA + {"CONTENT_TYPE_NOT_ENVELOPED_DATA", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_NOT_ENVELOPED_DATA}, + #else + {"CONTENT_TYPE_NOT_ENVELOPED_DATA", 46, 107}, + #endif + #ifdef CMS_R_CONTENT_TYPE_NOT_SIGNED_DATA + {"CONTENT_TYPE_NOT_SIGNED_DATA", ERR_LIB_CMS, CMS_R_CONTENT_TYPE_NOT_SIGNED_DATA}, + #else + {"CONTENT_TYPE_NOT_SIGNED_DATA", 46, 108}, + #endif + #ifdef CMS_R_CONTENT_VERIFY_ERROR + {"CONTENT_VERIFY_ERROR", ERR_LIB_CMS, CMS_R_CONTENT_VERIFY_ERROR}, + #else + {"CONTENT_VERIFY_ERROR", 46, 109}, + #endif + #ifdef CMS_R_CTRL_ERROR + {"CTRL_ERROR", ERR_LIB_CMS, CMS_R_CTRL_ERROR}, + #else + {"CTRL_ERROR", 46, 110}, + #endif + #ifdef CMS_R_CTRL_FAILURE + {"CTRL_FAILURE", ERR_LIB_CMS, CMS_R_CTRL_FAILURE}, + #else + {"CTRL_FAILURE", 46, 111}, + #endif + #ifdef CMS_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_CMS, CMS_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 46, 187}, + #endif + #ifdef CMS_R_DECRYPT_ERROR + {"DECRYPT_ERROR", ERR_LIB_CMS, CMS_R_DECRYPT_ERROR}, + #else + {"DECRYPT_ERROR", 46, 112}, + #endif + #ifdef CMS_R_ERROR_GETTING_PUBLIC_KEY + {"ERROR_GETTING_PUBLIC_KEY", ERR_LIB_CMS, CMS_R_ERROR_GETTING_PUBLIC_KEY}, + #else + {"ERROR_GETTING_PUBLIC_KEY", 46, 113}, + #endif + #ifdef CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE + {"ERROR_READING_MESSAGEDIGEST_ATTRIBUTE", ERR_LIB_CMS, CMS_R_ERROR_READING_MESSAGEDIGEST_ATTRIBUTE}, + #else + {"ERROR_READING_MESSAGEDIGEST_ATTRIBUTE", 46, 114}, + #endif + #ifdef CMS_R_ERROR_SETTING_KEY + {"ERROR_SETTING_KEY", ERR_LIB_CMS, CMS_R_ERROR_SETTING_KEY}, + #else + {"ERROR_SETTING_KEY", 46, 115}, + #endif + #ifdef CMS_R_ERROR_SETTING_RECIPIENTINFO + {"ERROR_SETTING_RECIPIENTINFO", ERR_LIB_CMS, CMS_R_ERROR_SETTING_RECIPIENTINFO}, + #else + {"ERROR_SETTING_RECIPIENTINFO", 46, 116}, + #endif + #ifdef CMS_R_ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT + {"ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT", ERR_LIB_CMS, CMS_R_ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT}, + #else + {"ERROR_UNSUPPORTED_STATIC_KEY_AGREEMENT", 46, 196}, + #endif + #ifdef CMS_R_ESS_SIGNING_CERTID_MISMATCH_ERROR + {"ESS_SIGNING_CERTID_MISMATCH_ERROR", ERR_LIB_CMS, CMS_R_ESS_SIGNING_CERTID_MISMATCH_ERROR}, + #else + {"ESS_SIGNING_CERTID_MISMATCH_ERROR", 46, 183}, + #endif + #ifdef CMS_R_INVALID_ENCRYPTED_KEY_LENGTH + {"INVALID_ENCRYPTED_KEY_LENGTH", ERR_LIB_CMS, CMS_R_INVALID_ENCRYPTED_KEY_LENGTH}, + #else + {"INVALID_ENCRYPTED_KEY_LENGTH", 46, 117}, + #endif + #ifdef CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER + {"INVALID_KEY_ENCRYPTION_PARAMETER", ERR_LIB_CMS, CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER}, + #else + {"INVALID_KEY_ENCRYPTION_PARAMETER", 46, 176}, + #endif + #ifdef CMS_R_INVALID_KEY_LENGTH + {"INVALID_KEY_LENGTH", ERR_LIB_CMS, CMS_R_INVALID_KEY_LENGTH}, + #else + {"INVALID_KEY_LENGTH", 46, 118}, + #endif + #ifdef CMS_R_INVALID_LABEL + {"INVALID_LABEL", ERR_LIB_CMS, CMS_R_INVALID_LABEL}, + #else + {"INVALID_LABEL", 46, 190}, + #endif + #ifdef CMS_R_INVALID_OAEP_PARAMETERS + {"INVALID_OAEP_PARAMETERS", ERR_LIB_CMS, CMS_R_INVALID_OAEP_PARAMETERS}, + #else + {"INVALID_OAEP_PARAMETERS", 46, 191}, + #endif + #ifdef CMS_R_KDF_PARAMETER_ERROR + {"KDF_PARAMETER_ERROR", ERR_LIB_CMS, CMS_R_KDF_PARAMETER_ERROR}, + #else + {"KDF_PARAMETER_ERROR", 46, 186}, + #endif + #ifdef CMS_R_MD_BIO_INIT_ERROR + {"MD_BIO_INIT_ERROR", ERR_LIB_CMS, CMS_R_MD_BIO_INIT_ERROR}, + #else + {"MD_BIO_INIT_ERROR", 46, 119}, + #endif + #ifdef CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH + {"MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH", ERR_LIB_CMS, CMS_R_MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH}, + #else + {"MESSAGEDIGEST_ATTRIBUTE_WRONG_LENGTH", 46, 120}, + #endif + #ifdef CMS_R_MESSAGEDIGEST_WRONG_LENGTH + {"MESSAGEDIGEST_WRONG_LENGTH", ERR_LIB_CMS, CMS_R_MESSAGEDIGEST_WRONG_LENGTH}, + #else + {"MESSAGEDIGEST_WRONG_LENGTH", 46, 121}, + #endif + #ifdef CMS_R_MSGSIGDIGEST_ERROR + {"MSGSIGDIGEST_ERROR", ERR_LIB_CMS, CMS_R_MSGSIGDIGEST_ERROR}, + #else + {"MSGSIGDIGEST_ERROR", 46, 172}, + #endif + #ifdef CMS_R_MSGSIGDIGEST_VERIFICATION_FAILURE + {"MSGSIGDIGEST_VERIFICATION_FAILURE", ERR_LIB_CMS, CMS_R_MSGSIGDIGEST_VERIFICATION_FAILURE}, + #else + {"MSGSIGDIGEST_VERIFICATION_FAILURE", 46, 162}, + #endif + #ifdef CMS_R_MSGSIGDIGEST_WRONG_LENGTH + {"MSGSIGDIGEST_WRONG_LENGTH", ERR_LIB_CMS, CMS_R_MSGSIGDIGEST_WRONG_LENGTH}, + #else + {"MSGSIGDIGEST_WRONG_LENGTH", 46, 163}, + #endif + #ifdef CMS_R_NEED_ONE_SIGNER + {"NEED_ONE_SIGNER", ERR_LIB_CMS, CMS_R_NEED_ONE_SIGNER}, + #else + {"NEED_ONE_SIGNER", 46, 164}, + #endif + #ifdef CMS_R_NOT_A_SIGNED_RECEIPT + {"NOT_A_SIGNED_RECEIPT", ERR_LIB_CMS, CMS_R_NOT_A_SIGNED_RECEIPT}, + #else + {"NOT_A_SIGNED_RECEIPT", 46, 165}, + #endif + #ifdef CMS_R_NOT_ENCRYPTED_DATA + {"NOT_ENCRYPTED_DATA", ERR_LIB_CMS, CMS_R_NOT_ENCRYPTED_DATA}, + #else + {"NOT_ENCRYPTED_DATA", 46, 122}, + #endif + #ifdef CMS_R_NOT_KEK + {"NOT_KEK", ERR_LIB_CMS, CMS_R_NOT_KEK}, + #else + {"NOT_KEK", 46, 123}, + #endif + #ifdef CMS_R_NOT_KEM + {"NOT_KEM", ERR_LIB_CMS, CMS_R_NOT_KEM}, + #else + {"NOT_KEM", 46, 197}, + #endif + #ifdef CMS_R_NOT_KEY_AGREEMENT + {"NOT_KEY_AGREEMENT", ERR_LIB_CMS, CMS_R_NOT_KEY_AGREEMENT}, + #else + {"NOT_KEY_AGREEMENT", 46, 181}, + #endif + #ifdef CMS_R_NOT_KEY_TRANSPORT + {"NOT_KEY_TRANSPORT", ERR_LIB_CMS, CMS_R_NOT_KEY_TRANSPORT}, + #else + {"NOT_KEY_TRANSPORT", 46, 124}, + #endif + #ifdef CMS_R_NOT_PWRI + {"NOT_PWRI", ERR_LIB_CMS, CMS_R_NOT_PWRI}, + #else + {"NOT_PWRI", 46, 177}, + #endif + #ifdef CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE + {"NOT_SUPPORTED_FOR_THIS_KEY_TYPE", ERR_LIB_CMS, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE}, + #else + {"NOT_SUPPORTED_FOR_THIS_KEY_TYPE", 46, 125}, + #endif + #ifdef CMS_R_NO_CIPHER + {"NO_CIPHER", ERR_LIB_CMS, CMS_R_NO_CIPHER}, + #else + {"NO_CIPHER", 46, 126}, + #endif + #ifdef CMS_R_NO_CONTENT + {"NO_CONTENT", ERR_LIB_CMS, CMS_R_NO_CONTENT}, + #else + {"NO_CONTENT", 46, 127}, + #endif + #ifdef CMS_R_NO_CONTENT_TYPE + {"NO_CONTENT_TYPE", ERR_LIB_CMS, CMS_R_NO_CONTENT_TYPE}, + #else + {"NO_CONTENT_TYPE", 46, 173}, + #endif + #ifdef CMS_R_NO_DEFAULT_DIGEST + {"NO_DEFAULT_DIGEST", ERR_LIB_CMS, CMS_R_NO_DEFAULT_DIGEST}, + #else + {"NO_DEFAULT_DIGEST", 46, 128}, + #endif + #ifdef CMS_R_NO_DIGEST_SET + {"NO_DIGEST_SET", ERR_LIB_CMS, CMS_R_NO_DIGEST_SET}, + #else + {"NO_DIGEST_SET", 46, 129}, + #endif + #ifdef CMS_R_NO_KEY + {"NO_KEY", ERR_LIB_CMS, CMS_R_NO_KEY}, + #else + {"NO_KEY", 46, 130}, + #endif + #ifdef CMS_R_NO_KEY_OR_CERT + {"NO_KEY_OR_CERT", ERR_LIB_CMS, CMS_R_NO_KEY_OR_CERT}, + #else + {"NO_KEY_OR_CERT", 46, 174}, + #endif + #ifdef CMS_R_NO_MATCHING_DIGEST + {"NO_MATCHING_DIGEST", ERR_LIB_CMS, CMS_R_NO_MATCHING_DIGEST}, + #else + {"NO_MATCHING_DIGEST", 46, 131}, + #endif + #ifdef CMS_R_NO_MATCHING_RECIPIENT + {"NO_MATCHING_RECIPIENT", ERR_LIB_CMS, CMS_R_NO_MATCHING_RECIPIENT}, + #else + {"NO_MATCHING_RECIPIENT", 46, 132}, + #endif + #ifdef CMS_R_NO_MATCHING_SIGNATURE + {"NO_MATCHING_SIGNATURE", ERR_LIB_CMS, CMS_R_NO_MATCHING_SIGNATURE}, + #else + {"NO_MATCHING_SIGNATURE", 46, 166}, + #endif + #ifdef CMS_R_NO_MSGSIGDIGEST + {"NO_MSGSIGDIGEST", ERR_LIB_CMS, CMS_R_NO_MSGSIGDIGEST}, + #else + {"NO_MSGSIGDIGEST", 46, 167}, + #endif + #ifdef CMS_R_NO_PASSWORD + {"NO_PASSWORD", ERR_LIB_CMS, CMS_R_NO_PASSWORD}, + #else + {"NO_PASSWORD", 46, 178}, + #endif + #ifdef CMS_R_NO_PRIVATE_KEY + {"NO_PRIVATE_KEY", ERR_LIB_CMS, CMS_R_NO_PRIVATE_KEY}, + #else + {"NO_PRIVATE_KEY", 46, 133}, + #endif + #ifdef CMS_R_NO_PUBLIC_KEY + {"NO_PUBLIC_KEY", ERR_LIB_CMS, CMS_R_NO_PUBLIC_KEY}, + #else + {"NO_PUBLIC_KEY", 46, 134}, + #endif + #ifdef CMS_R_NO_RECEIPT_REQUEST + {"NO_RECEIPT_REQUEST", ERR_LIB_CMS, CMS_R_NO_RECEIPT_REQUEST}, + #else + {"NO_RECEIPT_REQUEST", 46, 168}, + #endif + #ifdef CMS_R_NO_SIGNERS + {"NO_SIGNERS", ERR_LIB_CMS, CMS_R_NO_SIGNERS}, + #else + {"NO_SIGNERS", 46, 135}, + #endif + #ifdef CMS_R_OPERATION_UNSUPPORTED + {"OPERATION_UNSUPPORTED", ERR_LIB_CMS, CMS_R_OPERATION_UNSUPPORTED}, + #else + {"OPERATION_UNSUPPORTED", 46, 182}, + #endif + #ifdef CMS_R_PEER_KEY_ERROR + {"PEER_KEY_ERROR", ERR_LIB_CMS, CMS_R_PEER_KEY_ERROR}, + #else + {"PEER_KEY_ERROR", 46, 188}, + #endif + #ifdef CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_CMS, CMS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, + #else + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 46, 136}, + #endif + #ifdef CMS_R_RECEIPT_DECODE_ERROR + {"RECEIPT_DECODE_ERROR", ERR_LIB_CMS, CMS_R_RECEIPT_DECODE_ERROR}, + #else + {"RECEIPT_DECODE_ERROR", 46, 169}, + #endif + #ifdef CMS_R_RECIPIENT_ERROR + {"RECIPIENT_ERROR", ERR_LIB_CMS, CMS_R_RECIPIENT_ERROR}, + #else + {"RECIPIENT_ERROR", 46, 137}, + #endif + #ifdef CMS_R_SHARED_INFO_ERROR + {"SHARED_INFO_ERROR", ERR_LIB_CMS, CMS_R_SHARED_INFO_ERROR}, + #else + {"SHARED_INFO_ERROR", 46, 189}, + #endif + #ifdef CMS_R_SIGNER_CERTIFICATE_NOT_FOUND + {"SIGNER_CERTIFICATE_NOT_FOUND", ERR_LIB_CMS, CMS_R_SIGNER_CERTIFICATE_NOT_FOUND}, + #else + {"SIGNER_CERTIFICATE_NOT_FOUND", 46, 138}, + #endif + #ifdef CMS_R_SIGNFINAL_ERROR + {"SIGNFINAL_ERROR", ERR_LIB_CMS, CMS_R_SIGNFINAL_ERROR}, + #else + {"SIGNFINAL_ERROR", 46, 139}, + #endif + #ifdef CMS_R_SMIME_TEXT_ERROR + {"SMIME_TEXT_ERROR", ERR_LIB_CMS, CMS_R_SMIME_TEXT_ERROR}, + #else + {"SMIME_TEXT_ERROR", 46, 140}, + #endif + #ifdef CMS_R_STORE_INIT_ERROR + {"STORE_INIT_ERROR", ERR_LIB_CMS, CMS_R_STORE_INIT_ERROR}, + #else + {"STORE_INIT_ERROR", 46, 141}, + #endif + #ifdef CMS_R_TYPE_NOT_COMPRESSED_DATA + {"TYPE_NOT_COMPRESSED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_COMPRESSED_DATA}, + #else + {"TYPE_NOT_COMPRESSED_DATA", 46, 142}, + #endif + #ifdef CMS_R_TYPE_NOT_DATA + {"TYPE_NOT_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_DATA}, + #else + {"TYPE_NOT_DATA", 46, 143}, + #endif + #ifdef CMS_R_TYPE_NOT_DIGESTED_DATA + {"TYPE_NOT_DIGESTED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_DIGESTED_DATA}, + #else + {"TYPE_NOT_DIGESTED_DATA", 46, 144}, + #endif + #ifdef CMS_R_TYPE_NOT_ENCRYPTED_DATA + {"TYPE_NOT_ENCRYPTED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_ENCRYPTED_DATA}, + #else + {"TYPE_NOT_ENCRYPTED_DATA", 46, 145}, + #endif + #ifdef CMS_R_TYPE_NOT_ENVELOPED_DATA + {"TYPE_NOT_ENVELOPED_DATA", ERR_LIB_CMS, CMS_R_TYPE_NOT_ENVELOPED_DATA}, + #else + {"TYPE_NOT_ENVELOPED_DATA", 46, 146}, + #endif + #ifdef CMS_R_UNABLE_TO_FINALIZE_CONTEXT + {"UNABLE_TO_FINALIZE_CONTEXT", ERR_LIB_CMS, CMS_R_UNABLE_TO_FINALIZE_CONTEXT}, + #else + {"UNABLE_TO_FINALIZE_CONTEXT", 46, 147}, + #endif + #ifdef CMS_R_UNKNOWN_CIPHER + {"UNKNOWN_CIPHER", ERR_LIB_CMS, CMS_R_UNKNOWN_CIPHER}, + #else + {"UNKNOWN_CIPHER", 46, 148}, + #endif + #ifdef CMS_R_UNKNOWN_DIGEST_ALGORITHM + {"UNKNOWN_DIGEST_ALGORITHM", ERR_LIB_CMS, CMS_R_UNKNOWN_DIGEST_ALGORITHM}, + #else + {"UNKNOWN_DIGEST_ALGORITHM", 46, 149}, + #endif + #ifdef CMS_R_UNKNOWN_ID + {"UNKNOWN_ID", ERR_LIB_CMS, CMS_R_UNKNOWN_ID}, + #else + {"UNKNOWN_ID", 46, 150}, + #endif + #ifdef CMS_R_UNKNOWN_KDF_ALGORITHM + {"UNKNOWN_KDF_ALGORITHM", ERR_LIB_CMS, CMS_R_UNKNOWN_KDF_ALGORITHM}, + #else + {"UNKNOWN_KDF_ALGORITHM", 46, 198}, + #endif + #ifdef CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM + {"UNSUPPORTED_COMPRESSION_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_COMPRESSION_ALGORITHM}, + #else + {"UNSUPPORTED_COMPRESSION_ALGORITHM", 46, 151}, + #endif + #ifdef CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM + {"UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM}, + #else + {"UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM", 46, 194}, + #endif + #ifdef CMS_R_UNSUPPORTED_CONTENT_TYPE + {"UNSUPPORTED_CONTENT_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_CONTENT_TYPE}, + #else + {"UNSUPPORTED_CONTENT_TYPE", 46, 152}, + #endif + #ifdef CMS_R_UNSUPPORTED_ENCRYPTION_TYPE + {"UNSUPPORTED_ENCRYPTION_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_ENCRYPTION_TYPE}, + #else + {"UNSUPPORTED_ENCRYPTION_TYPE", 46, 192}, + #endif + #ifdef CMS_R_UNSUPPORTED_KDF_ALGORITHM + {"UNSUPPORTED_KDF_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_KDF_ALGORITHM}, + #else + {"UNSUPPORTED_KDF_ALGORITHM", 46, 199}, + #endif + #ifdef CMS_R_UNSUPPORTED_KEK_ALGORITHM + {"UNSUPPORTED_KEK_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_KEK_ALGORITHM}, + #else + {"UNSUPPORTED_KEK_ALGORITHM", 46, 153}, + #endif + #ifdef CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM + {"UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM}, + #else + {"UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM", 46, 179}, + #endif + #ifdef CMS_R_UNSUPPORTED_LABEL_SOURCE + {"UNSUPPORTED_LABEL_SOURCE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_LABEL_SOURCE}, + #else + {"UNSUPPORTED_LABEL_SOURCE", 46, 193}, + #endif + #ifdef CMS_R_UNSUPPORTED_RECIPIENTINFO_TYPE + {"UNSUPPORTED_RECIPIENTINFO_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_RECIPIENTINFO_TYPE}, + #else + {"UNSUPPORTED_RECIPIENTINFO_TYPE", 46, 155}, + #endif + #ifdef CMS_R_UNSUPPORTED_RECIPIENT_TYPE + {"UNSUPPORTED_RECIPIENT_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_RECIPIENT_TYPE}, + #else + {"UNSUPPORTED_RECIPIENT_TYPE", 46, 154}, + #endif + #ifdef CMS_R_UNSUPPORTED_SIGNATURE_ALGORITHM + {"UNSUPPORTED_SIGNATURE_ALGORITHM", ERR_LIB_CMS, CMS_R_UNSUPPORTED_SIGNATURE_ALGORITHM}, + #else + {"UNSUPPORTED_SIGNATURE_ALGORITHM", 46, 195}, + #endif + #ifdef CMS_R_UNSUPPORTED_TYPE + {"UNSUPPORTED_TYPE", ERR_LIB_CMS, CMS_R_UNSUPPORTED_TYPE}, + #else + {"UNSUPPORTED_TYPE", 46, 156}, + #endif + #ifdef CMS_R_UNWRAP_ERROR + {"UNWRAP_ERROR", ERR_LIB_CMS, CMS_R_UNWRAP_ERROR}, + #else + {"UNWRAP_ERROR", 46, 157}, + #endif + #ifdef CMS_R_UNWRAP_FAILURE + {"UNWRAP_FAILURE", ERR_LIB_CMS, CMS_R_UNWRAP_FAILURE}, + #else + {"UNWRAP_FAILURE", 46, 180}, + #endif + #ifdef CMS_R_VERIFICATION_FAILURE + {"VERIFICATION_FAILURE", ERR_LIB_CMS, CMS_R_VERIFICATION_FAILURE}, + #else + {"VERIFICATION_FAILURE", 46, 158}, + #endif + #ifdef CMS_R_WRAP_ERROR + {"WRAP_ERROR", ERR_LIB_CMS, CMS_R_WRAP_ERROR}, + #else + {"WRAP_ERROR", 46, 159}, + #endif + #ifdef COMP_R_BROTLI_DECODE_ERROR + {"BROTLI_DECODE_ERROR", ERR_LIB_COMP, COMP_R_BROTLI_DECODE_ERROR}, + #else + {"BROTLI_DECODE_ERROR", 41, 102}, + #endif + #ifdef COMP_R_BROTLI_ENCODE_ERROR + {"BROTLI_ENCODE_ERROR", ERR_LIB_COMP, COMP_R_BROTLI_ENCODE_ERROR}, + #else + {"BROTLI_ENCODE_ERROR", 41, 103}, + #endif + #ifdef COMP_R_BROTLI_NOT_SUPPORTED + {"BROTLI_NOT_SUPPORTED", ERR_LIB_COMP, COMP_R_BROTLI_NOT_SUPPORTED}, + #else + {"BROTLI_NOT_SUPPORTED", 41, 104}, + #endif + #ifdef COMP_R_ZLIB_DEFLATE_ERROR + {"ZLIB_DEFLATE_ERROR", ERR_LIB_COMP, COMP_R_ZLIB_DEFLATE_ERROR}, + #else + {"ZLIB_DEFLATE_ERROR", 41, 99}, + #endif + #ifdef COMP_R_ZLIB_INFLATE_ERROR + {"ZLIB_INFLATE_ERROR", ERR_LIB_COMP, COMP_R_ZLIB_INFLATE_ERROR}, + #else + {"ZLIB_INFLATE_ERROR", 41, 100}, + #endif + #ifdef COMP_R_ZLIB_NOT_SUPPORTED + {"ZLIB_NOT_SUPPORTED", ERR_LIB_COMP, COMP_R_ZLIB_NOT_SUPPORTED}, + #else + {"ZLIB_NOT_SUPPORTED", 41, 101}, + #endif + #ifdef COMP_R_ZSTD_COMPRESS_ERROR + {"ZSTD_COMPRESS_ERROR", ERR_LIB_COMP, COMP_R_ZSTD_COMPRESS_ERROR}, + #else + {"ZSTD_COMPRESS_ERROR", 41, 105}, + #endif + #ifdef COMP_R_ZSTD_DECODE_ERROR + {"ZSTD_DECODE_ERROR", ERR_LIB_COMP, COMP_R_ZSTD_DECODE_ERROR}, + #else + {"ZSTD_DECODE_ERROR", 41, 106}, + #endif + #ifdef COMP_R_ZSTD_DECOMPRESS_ERROR + {"ZSTD_DECOMPRESS_ERROR", ERR_LIB_COMP, COMP_R_ZSTD_DECOMPRESS_ERROR}, + #else + {"ZSTD_DECOMPRESS_ERROR", 41, 107}, + #endif + #ifdef COMP_R_ZSTD_NOT_SUPPORTED + {"ZSTD_NOT_SUPPORTED", ERR_LIB_COMP, COMP_R_ZSTD_NOT_SUPPORTED}, + #else + {"ZSTD_NOT_SUPPORTED", 41, 108}, + #endif + #ifdef CONF_R_ERROR_LOADING_DSO + {"ERROR_LOADING_DSO", ERR_LIB_CONF, CONF_R_ERROR_LOADING_DSO}, + #else + {"ERROR_LOADING_DSO", 14, 110}, + #endif + #ifdef CONF_R_INVALID_PRAGMA + {"INVALID_PRAGMA", ERR_LIB_CONF, CONF_R_INVALID_PRAGMA}, + #else + {"INVALID_PRAGMA", 14, 122}, + #endif + #ifdef CONF_R_LIST_CANNOT_BE_NULL + {"LIST_CANNOT_BE_NULL", ERR_LIB_CONF, CONF_R_LIST_CANNOT_BE_NULL}, + #else + {"LIST_CANNOT_BE_NULL", 14, 115}, + #endif + #ifdef CONF_R_MANDATORY_BRACES_IN_VARIABLE_EXPANSION + {"MANDATORY_BRACES_IN_VARIABLE_EXPANSION", ERR_LIB_CONF, CONF_R_MANDATORY_BRACES_IN_VARIABLE_EXPANSION}, + #else + {"MANDATORY_BRACES_IN_VARIABLE_EXPANSION", 14, 123}, + #endif + #ifdef CONF_R_MISSING_CLOSE_SQUARE_BRACKET + {"MISSING_CLOSE_SQUARE_BRACKET", ERR_LIB_CONF, CONF_R_MISSING_CLOSE_SQUARE_BRACKET}, + #else + {"MISSING_CLOSE_SQUARE_BRACKET", 14, 100}, + #endif + #ifdef CONF_R_MISSING_EQUAL_SIGN + {"MISSING_EQUAL_SIGN", ERR_LIB_CONF, CONF_R_MISSING_EQUAL_SIGN}, + #else + {"MISSING_EQUAL_SIGN", 14, 101}, + #endif + #ifdef CONF_R_MISSING_INIT_FUNCTION + {"MISSING_INIT_FUNCTION", ERR_LIB_CONF, CONF_R_MISSING_INIT_FUNCTION}, + #else + {"MISSING_INIT_FUNCTION", 14, 112}, + #endif + #ifdef CONF_R_MODULE_INITIALIZATION_ERROR + {"MODULE_INITIALIZATION_ERROR", ERR_LIB_CONF, CONF_R_MODULE_INITIALIZATION_ERROR}, + #else + {"MODULE_INITIALIZATION_ERROR", 14, 109}, + #endif + #ifdef CONF_R_NO_CLOSE_BRACE + {"NO_CLOSE_BRACE", ERR_LIB_CONF, CONF_R_NO_CLOSE_BRACE}, + #else + {"NO_CLOSE_BRACE", 14, 102}, + #endif + #ifdef CONF_R_NO_CONF + {"NO_CONF", ERR_LIB_CONF, CONF_R_NO_CONF}, + #else + {"NO_CONF", 14, 105}, + #endif + #ifdef CONF_R_NO_CONF_OR_ENVIRONMENT_VARIABLE + {"NO_CONF_OR_ENVIRONMENT_VARIABLE", ERR_LIB_CONF, CONF_R_NO_CONF_OR_ENVIRONMENT_VARIABLE}, + #else + {"NO_CONF_OR_ENVIRONMENT_VARIABLE", 14, 106}, + #endif + #ifdef CONF_R_NO_SECTION + {"NO_SECTION", ERR_LIB_CONF, CONF_R_NO_SECTION}, + #else + {"NO_SECTION", 14, 107}, + #endif + #ifdef CONF_R_NO_SUCH_FILE + {"NO_SUCH_FILE", ERR_LIB_CONF, CONF_R_NO_SUCH_FILE}, + #else + {"NO_SUCH_FILE", 14, 114}, + #endif + #ifdef CONF_R_NO_VALUE + {"NO_VALUE", ERR_LIB_CONF, CONF_R_NO_VALUE}, + #else + {"NO_VALUE", 14, 108}, + #endif + #ifdef CONF_R_NUMBER_TOO_LARGE + {"NUMBER_TOO_LARGE", ERR_LIB_CONF, CONF_R_NUMBER_TOO_LARGE}, + #else + {"NUMBER_TOO_LARGE", 14, 121}, + #endif + #ifdef CONF_R_OPENSSL_CONF_REFERENCES_MISSING_SECTION + {"OPENSSL_CONF_REFERENCES_MISSING_SECTION", ERR_LIB_CONF, CONF_R_OPENSSL_CONF_REFERENCES_MISSING_SECTION}, + #else + {"OPENSSL_CONF_REFERENCES_MISSING_SECTION", 14, 124}, + #endif + #ifdef CONF_R_RECURSIVE_DIRECTORY_INCLUDE + {"RECURSIVE_DIRECTORY_INCLUDE", ERR_LIB_CONF, CONF_R_RECURSIVE_DIRECTORY_INCLUDE}, + #else + {"RECURSIVE_DIRECTORY_INCLUDE", 14, 111}, + #endif + #ifdef CONF_R_RECURSIVE_SECTION_REFERENCE + {"RECURSIVE_SECTION_REFERENCE", ERR_LIB_CONF, CONF_R_RECURSIVE_SECTION_REFERENCE}, + #else + {"RECURSIVE_SECTION_REFERENCE", 14, 126}, + #endif + #ifdef CONF_R_RELATIVE_PATH + {"RELATIVE_PATH", ERR_LIB_CONF, CONF_R_RELATIVE_PATH}, + #else + {"RELATIVE_PATH", 14, 125}, + #endif + #ifdef CONF_R_SSL_COMMAND_SECTION_EMPTY + {"SSL_COMMAND_SECTION_EMPTY", ERR_LIB_CONF, CONF_R_SSL_COMMAND_SECTION_EMPTY}, + #else + {"SSL_COMMAND_SECTION_EMPTY", 14, 117}, + #endif + #ifdef CONF_R_SSL_COMMAND_SECTION_NOT_FOUND + {"SSL_COMMAND_SECTION_NOT_FOUND", ERR_LIB_CONF, CONF_R_SSL_COMMAND_SECTION_NOT_FOUND}, + #else + {"SSL_COMMAND_SECTION_NOT_FOUND", 14, 118}, + #endif + #ifdef CONF_R_SSL_SECTION_EMPTY + {"SSL_SECTION_EMPTY", ERR_LIB_CONF, CONF_R_SSL_SECTION_EMPTY}, + #else + {"SSL_SECTION_EMPTY", 14, 119}, + #endif + #ifdef CONF_R_SSL_SECTION_NOT_FOUND + {"SSL_SECTION_NOT_FOUND", ERR_LIB_CONF, CONF_R_SSL_SECTION_NOT_FOUND}, + #else + {"SSL_SECTION_NOT_FOUND", 14, 120}, + #endif + #ifdef CONF_R_UNABLE_TO_CREATE_NEW_SECTION + {"UNABLE_TO_CREATE_NEW_SECTION", ERR_LIB_CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION}, + #else + {"UNABLE_TO_CREATE_NEW_SECTION", 14, 103}, + #endif + #ifdef CONF_R_UNKNOWN_MODULE_NAME + {"UNKNOWN_MODULE_NAME", ERR_LIB_CONF, CONF_R_UNKNOWN_MODULE_NAME}, + #else + {"UNKNOWN_MODULE_NAME", 14, 113}, + #endif + #ifdef CONF_R_VARIABLE_EXPANSION_TOO_LONG + {"VARIABLE_EXPANSION_TOO_LONG", ERR_LIB_CONF, CONF_R_VARIABLE_EXPANSION_TOO_LONG}, + #else + {"VARIABLE_EXPANSION_TOO_LONG", 14, 116}, + #endif + #ifdef CONF_R_VARIABLE_HAS_NO_VALUE + {"VARIABLE_HAS_NO_VALUE", ERR_LIB_CONF, CONF_R_VARIABLE_HAS_NO_VALUE}, + #else + {"VARIABLE_HAS_NO_VALUE", 14, 104}, + #endif + #ifdef CRMF_R_BAD_PBM_ITERATIONCOUNT + {"BAD_PBM_ITERATIONCOUNT", ERR_LIB_CRMF, CRMF_R_BAD_PBM_ITERATIONCOUNT}, + #else + {"BAD_PBM_ITERATIONCOUNT", 56, 100}, + #endif + #ifdef CRMF_R_CMS_NOT_SUPPORTED + {"CMS_NOT_SUPPORTED", ERR_LIB_CRMF, CRMF_R_CMS_NOT_SUPPORTED}, + #else + {"CMS_NOT_SUPPORTED", 56, 122}, + #endif + #ifdef CRMF_R_CRMFERROR + {"CRMFERROR", ERR_LIB_CRMF, CRMF_R_CRMFERROR}, + #else + {"CRMFERROR", 56, 102}, + #endif + #ifdef CRMF_R_ERROR + {"ERROR", ERR_LIB_CRMF, CRMF_R_ERROR}, + #else + {"ERROR", 56, 103}, + #endif + #ifdef CRMF_R_ERROR_DECODING_CERTIFICATE + {"ERROR_DECODING_CERTIFICATE", ERR_LIB_CRMF, CRMF_R_ERROR_DECODING_CERTIFICATE}, + #else + {"ERROR_DECODING_CERTIFICATE", 56, 104}, + #endif + #ifdef CRMF_R_ERROR_DECODING_ENCRYPTEDKEY + {"ERROR_DECODING_ENCRYPTEDKEY", ERR_LIB_CRMF, CRMF_R_ERROR_DECODING_ENCRYPTEDKEY}, + #else + {"ERROR_DECODING_ENCRYPTEDKEY", 56, 123}, + #endif + #ifdef CRMF_R_ERROR_DECRYPTING_CERTIFICATE + {"ERROR_DECRYPTING_CERTIFICATE", ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_CERTIFICATE}, + #else + {"ERROR_DECRYPTING_CERTIFICATE", 56, 105}, + #endif + #ifdef CRMF_R_ERROR_DECRYPTING_ENCRYPTEDKEY + {"ERROR_DECRYPTING_ENCRYPTEDKEY", ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_ENCRYPTEDKEY}, + #else + {"ERROR_DECRYPTING_ENCRYPTEDKEY", 56, 124}, + #endif + #ifdef CRMF_R_ERROR_DECRYPTING_ENCRYPTEDVALUE + {"ERROR_DECRYPTING_ENCRYPTEDVALUE", ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_ENCRYPTEDVALUE}, + #else + {"ERROR_DECRYPTING_ENCRYPTEDVALUE", 56, 125}, + #endif + #ifdef CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY + {"ERROR_DECRYPTING_SYMMETRIC_KEY", ERR_LIB_CRMF, CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY}, + #else + {"ERROR_DECRYPTING_SYMMETRIC_KEY", 56, 106}, + #endif + #ifdef CRMF_R_ERROR_SETTING_PURPOSE + {"ERROR_SETTING_PURPOSE", ERR_LIB_CRMF, CRMF_R_ERROR_SETTING_PURPOSE}, + #else + {"ERROR_SETTING_PURPOSE", 56, 126}, + #endif + #ifdef CRMF_R_ERROR_SIGNING_POPO + {"ERROR_SIGNING_POPO", ERR_LIB_CRMF, CRMF_R_ERROR_SIGNING_POPO}, + #else + {"ERROR_SIGNING_POPO", 56, 129}, + #endif + #ifdef CRMF_R_ERROR_VERIFYING_ENCRYPTEDKEY + {"ERROR_VERIFYING_ENCRYPTEDKEY", ERR_LIB_CRMF, CRMF_R_ERROR_VERIFYING_ENCRYPTEDKEY}, + #else + {"ERROR_VERIFYING_ENCRYPTEDKEY", 56, 127}, + #endif + #ifdef CRMF_R_FAILURE_OBTAINING_RANDOM + {"FAILURE_OBTAINING_RANDOM", ERR_LIB_CRMF, CRMF_R_FAILURE_OBTAINING_RANDOM}, + #else + {"FAILURE_OBTAINING_RANDOM", 56, 107}, + #endif + #ifdef CRMF_R_ITERATIONCOUNT_BELOW_100 + {"ITERATIONCOUNT_BELOW_100", ERR_LIB_CRMF, CRMF_R_ITERATIONCOUNT_BELOW_100}, + #else + {"ITERATIONCOUNT_BELOW_100", 56, 108}, + #endif + #ifdef CRMF_R_MALFORMED_IV + {"MALFORMED_IV", ERR_LIB_CRMF, CRMF_R_MALFORMED_IV}, + #else + {"MALFORMED_IV", 56, 101}, + #endif + #ifdef CRMF_R_NULL_ARGUMENT + {"NULL_ARGUMENT", ERR_LIB_CRMF, CRMF_R_NULL_ARGUMENT}, + #else + {"NULL_ARGUMENT", 56, 109}, + #endif + #ifdef CRMF_R_POPOSKINPUT_NOT_SUPPORTED + {"POPOSKINPUT_NOT_SUPPORTED", ERR_LIB_CRMF, CRMF_R_POPOSKINPUT_NOT_SUPPORTED}, + #else + {"POPOSKINPUT_NOT_SUPPORTED", 56, 113}, + #endif + #ifdef CRMF_R_POPO_INCONSISTENT_CENTRAL_KEYGEN + {"POPO_INCONSISTENT_CENTRAL_KEYGEN", ERR_LIB_CRMF, CRMF_R_POPO_INCONSISTENT_CENTRAL_KEYGEN}, + #else + {"POPO_INCONSISTENT_CENTRAL_KEYGEN", 56, 128}, + #endif + #ifdef CRMF_R_POPO_INCONSISTENT_PUBLIC_KEY + {"POPO_INCONSISTENT_PUBLIC_KEY", ERR_LIB_CRMF, CRMF_R_POPO_INCONSISTENT_PUBLIC_KEY}, + #else + {"POPO_INCONSISTENT_PUBLIC_KEY", 56, 117}, + #endif + #ifdef CRMF_R_POPO_MISSING + {"POPO_MISSING", ERR_LIB_CRMF, CRMF_R_POPO_MISSING}, + #else + {"POPO_MISSING", 56, 121}, + #endif + #ifdef CRMF_R_POPO_MISSING_PUBLIC_KEY + {"POPO_MISSING_PUBLIC_KEY", ERR_LIB_CRMF, CRMF_R_POPO_MISSING_PUBLIC_KEY}, + #else + {"POPO_MISSING_PUBLIC_KEY", 56, 118}, + #endif + #ifdef CRMF_R_POPO_MISSING_SUBJECT + {"POPO_MISSING_SUBJECT", ERR_LIB_CRMF, CRMF_R_POPO_MISSING_SUBJECT}, + #else + {"POPO_MISSING_SUBJECT", 56, 119}, + #endif + #ifdef CRMF_R_POPO_RAVERIFIED_NOT_ACCEPTED + {"POPO_RAVERIFIED_NOT_ACCEPTED", ERR_LIB_CRMF, CRMF_R_POPO_RAVERIFIED_NOT_ACCEPTED}, + #else + {"POPO_RAVERIFIED_NOT_ACCEPTED", 56, 120}, + #endif + #ifdef CRMF_R_SETTING_MAC_ALGOR_FAILURE + {"SETTING_MAC_ALGOR_FAILURE", ERR_LIB_CRMF, CRMF_R_SETTING_MAC_ALGOR_FAILURE}, + #else + {"SETTING_MAC_ALGOR_FAILURE", 56, 110}, + #endif + #ifdef CRMF_R_SETTING_OWF_ALGOR_FAILURE + {"SETTING_OWF_ALGOR_FAILURE", ERR_LIB_CRMF, CRMF_R_SETTING_OWF_ALGOR_FAILURE}, + #else + {"SETTING_OWF_ALGOR_FAILURE", 56, 111}, + #endif + #ifdef CRMF_R_UNSUPPORTED_ALGORITHM + {"UNSUPPORTED_ALGORITHM", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_ALGORITHM}, + #else + {"UNSUPPORTED_ALGORITHM", 56, 112}, + #endif + #ifdef CRMF_R_UNSUPPORTED_CIPHER + {"UNSUPPORTED_CIPHER", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_CIPHER}, + #else + {"UNSUPPORTED_CIPHER", 56, 114}, + #endif + #ifdef CRMF_R_UNSUPPORTED_METHOD_FOR_CREATING_POPO + {"UNSUPPORTED_METHOD_FOR_CREATING_POPO", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_METHOD_FOR_CREATING_POPO}, + #else + {"UNSUPPORTED_METHOD_FOR_CREATING_POPO", 56, 115}, + #endif + #ifdef CRMF_R_UNSUPPORTED_POPO_METHOD + {"UNSUPPORTED_POPO_METHOD", ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_POPO_METHOD}, + #else + {"UNSUPPORTED_POPO_METHOD", 56, 116}, + #endif + #ifdef CRYPTO_R_BAD_ALGORITHM_NAME + {"BAD_ALGORITHM_NAME", ERR_LIB_CRYPTO, CRYPTO_R_BAD_ALGORITHM_NAME}, + #else + {"BAD_ALGORITHM_NAME", 15, 117}, + #endif + #ifdef CRYPTO_R_CONFLICTING_NAMES + {"CONFLICTING_NAMES", ERR_LIB_CRYPTO, CRYPTO_R_CONFLICTING_NAMES}, + #else + {"CONFLICTING_NAMES", 15, 118}, + #endif + #ifdef CRYPTO_R_HEX_STRING_TOO_SHORT + {"HEX_STRING_TOO_SHORT", ERR_LIB_CRYPTO, CRYPTO_R_HEX_STRING_TOO_SHORT}, + #else + {"HEX_STRING_TOO_SHORT", 15, 121}, + #endif + #ifdef CRYPTO_R_ILLEGAL_HEX_DIGIT + {"ILLEGAL_HEX_DIGIT", ERR_LIB_CRYPTO, CRYPTO_R_ILLEGAL_HEX_DIGIT}, + #else + {"ILLEGAL_HEX_DIGIT", 15, 102}, + #endif + #ifdef CRYPTO_R_INSUFFICIENT_DATA_SPACE + {"INSUFFICIENT_DATA_SPACE", ERR_LIB_CRYPTO, CRYPTO_R_INSUFFICIENT_DATA_SPACE}, + #else + {"INSUFFICIENT_DATA_SPACE", 15, 106}, + #endif + #ifdef CRYPTO_R_INSUFFICIENT_PARAM_SIZE + {"INSUFFICIENT_PARAM_SIZE", ERR_LIB_CRYPTO, CRYPTO_R_INSUFFICIENT_PARAM_SIZE}, + #else + {"INSUFFICIENT_PARAM_SIZE", 15, 107}, + #endif + #ifdef CRYPTO_R_INSUFFICIENT_SECURE_DATA_SPACE + {"INSUFFICIENT_SECURE_DATA_SPACE", ERR_LIB_CRYPTO, CRYPTO_R_INSUFFICIENT_SECURE_DATA_SPACE}, + #else + {"INSUFFICIENT_SECURE_DATA_SPACE", 15, 108}, + #endif + #ifdef CRYPTO_R_INTEGER_OVERFLOW + {"INTEGER_OVERFLOW", ERR_LIB_CRYPTO, CRYPTO_R_INTEGER_OVERFLOW}, + #else + {"INTEGER_OVERFLOW", 15, 127}, + #endif + #ifdef CRYPTO_R_INVALID_NEGATIVE_VALUE + {"INVALID_NEGATIVE_VALUE", ERR_LIB_CRYPTO, CRYPTO_R_INVALID_NEGATIVE_VALUE}, + #else + {"INVALID_NEGATIVE_VALUE", 15, 122}, + #endif + #ifdef CRYPTO_R_INVALID_NULL_ARGUMENT + {"INVALID_NULL_ARGUMENT", ERR_LIB_CRYPTO, CRYPTO_R_INVALID_NULL_ARGUMENT}, + #else + {"INVALID_NULL_ARGUMENT", 15, 109}, + #endif + #ifdef CRYPTO_R_INVALID_OSSL_PARAM_TYPE + {"INVALID_OSSL_PARAM_TYPE", ERR_LIB_CRYPTO, CRYPTO_R_INVALID_OSSL_PARAM_TYPE}, + #else + {"INVALID_OSSL_PARAM_TYPE", 15, 110}, + #endif + #ifdef CRYPTO_R_NO_PARAMS_TO_MERGE + {"NO_PARAMS_TO_MERGE", ERR_LIB_CRYPTO, CRYPTO_R_NO_PARAMS_TO_MERGE}, + #else + {"NO_PARAMS_TO_MERGE", 15, 131}, + #endif + #ifdef CRYPTO_R_NO_SPACE_FOR_TERMINATING_NULL + {"NO_SPACE_FOR_TERMINATING_NULL", ERR_LIB_CRYPTO, CRYPTO_R_NO_SPACE_FOR_TERMINATING_NULL}, + #else + {"NO_SPACE_FOR_TERMINATING_NULL", 15, 128}, + #endif + #ifdef CRYPTO_R_ODD_NUMBER_OF_DIGITS + {"ODD_NUMBER_OF_DIGITS", ERR_LIB_CRYPTO, CRYPTO_R_ODD_NUMBER_OF_DIGITS}, + #else + {"ODD_NUMBER_OF_DIGITS", 15, 103}, + #endif + #ifdef CRYPTO_R_PARAM_CANNOT_BE_REPRESENTED_EXACTLY + {"PARAM_CANNOT_BE_REPRESENTED_EXACTLY", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_CANNOT_BE_REPRESENTED_EXACTLY}, + #else + {"PARAM_CANNOT_BE_REPRESENTED_EXACTLY", 15, 123}, + #endif + #ifdef CRYPTO_R_PARAM_NOT_INTEGER_TYPE + {"PARAM_NOT_INTEGER_TYPE", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_NOT_INTEGER_TYPE}, + #else + {"PARAM_NOT_INTEGER_TYPE", 15, 124}, + #endif + #ifdef CRYPTO_R_PARAM_OF_INCOMPATIBLE_TYPE + {"PARAM_OF_INCOMPATIBLE_TYPE", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_OF_INCOMPATIBLE_TYPE}, + #else + {"PARAM_OF_INCOMPATIBLE_TYPE", 15, 129}, + #endif + #ifdef CRYPTO_R_PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED + {"PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED}, + #else + {"PARAM_UNSIGNED_INTEGER_NEGATIVE_VALUE_UNSUPPORTED", 15, 125}, + #endif + #ifdef CRYPTO_R_PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT + {"PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT}, + #else + {"PARAM_UNSUPPORTED_FLOATING_POINT_FORMAT", 15, 130}, + #endif + #ifdef CRYPTO_R_PARAM_VALUE_TOO_LARGE_FOR_DESTINATION + {"PARAM_VALUE_TOO_LARGE_FOR_DESTINATION", ERR_LIB_CRYPTO, CRYPTO_R_PARAM_VALUE_TOO_LARGE_FOR_DESTINATION}, + #else + {"PARAM_VALUE_TOO_LARGE_FOR_DESTINATION", 15, 126}, + #endif + #ifdef CRYPTO_R_PROVIDER_ALREADY_EXISTS + {"PROVIDER_ALREADY_EXISTS", ERR_LIB_CRYPTO, CRYPTO_R_PROVIDER_ALREADY_EXISTS}, + #else + {"PROVIDER_ALREADY_EXISTS", 15, 104}, + #endif + #ifdef CRYPTO_R_PROVIDER_SECTION_ERROR + {"PROVIDER_SECTION_ERROR", ERR_LIB_CRYPTO, CRYPTO_R_PROVIDER_SECTION_ERROR}, + #else + {"PROVIDER_SECTION_ERROR", 15, 105}, + #endif + #ifdef CRYPTO_R_RANDOM_SECTION_ERROR + {"RANDOM_SECTION_ERROR", ERR_LIB_CRYPTO, CRYPTO_R_RANDOM_SECTION_ERROR}, + #else + {"RANDOM_SECTION_ERROR", 15, 119}, + #endif + #ifdef CRYPTO_R_SECURE_MALLOC_FAILURE + {"SECURE_MALLOC_FAILURE", ERR_LIB_CRYPTO, CRYPTO_R_SECURE_MALLOC_FAILURE}, + #else + {"SECURE_MALLOC_FAILURE", 15, 111}, + #endif + #ifdef CRYPTO_R_STRING_TOO_LONG + {"STRING_TOO_LONG", ERR_LIB_CRYPTO, CRYPTO_R_STRING_TOO_LONG}, + #else + {"STRING_TOO_LONG", 15, 112}, + #endif + #ifdef CRYPTO_R_TOO_MANY_BYTES + {"TOO_MANY_BYTES", ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_BYTES}, + #else + {"TOO_MANY_BYTES", 15, 113}, + #endif + #ifdef CRYPTO_R_TOO_MANY_NAMES + {"TOO_MANY_NAMES", ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_NAMES}, + #else + {"TOO_MANY_NAMES", 15, 132}, + #endif + #ifdef CRYPTO_R_TOO_MANY_RECORDS + {"TOO_MANY_RECORDS", ERR_LIB_CRYPTO, CRYPTO_R_TOO_MANY_RECORDS}, + #else + {"TOO_MANY_RECORDS", 15, 114}, + #endif + #ifdef CRYPTO_R_TOO_SMALL_BUFFER + {"TOO_SMALL_BUFFER", ERR_LIB_CRYPTO, CRYPTO_R_TOO_SMALL_BUFFER}, + #else + {"TOO_SMALL_BUFFER", 15, 116}, + #endif + #ifdef CRYPTO_R_UNKNOWN_NAME_IN_RANDOM_SECTION + {"UNKNOWN_NAME_IN_RANDOM_SECTION", ERR_LIB_CRYPTO, CRYPTO_R_UNKNOWN_NAME_IN_RANDOM_SECTION}, + #else + {"UNKNOWN_NAME_IN_RANDOM_SECTION", 15, 120}, + #endif + #ifdef CRYPTO_R_ZERO_LENGTH_NUMBER + {"ZERO_LENGTH_NUMBER", ERR_LIB_CRYPTO, CRYPTO_R_ZERO_LENGTH_NUMBER}, + #else + {"ZERO_LENGTH_NUMBER", 15, 115}, + #endif + #ifdef CT_R_BASE64_DECODE_ERROR + {"BASE64_DECODE_ERROR", ERR_LIB_CT, CT_R_BASE64_DECODE_ERROR}, + #else + {"BASE64_DECODE_ERROR", 50, 108}, + #endif + #ifdef CT_R_INVALID_LOG_ID_LENGTH + {"INVALID_LOG_ID_LENGTH", ERR_LIB_CT, CT_R_INVALID_LOG_ID_LENGTH}, + #else + {"INVALID_LOG_ID_LENGTH", 50, 100}, + #endif + #ifdef CT_R_LOG_CONF_INVALID + {"LOG_CONF_INVALID", ERR_LIB_CT, CT_R_LOG_CONF_INVALID}, + #else + {"LOG_CONF_INVALID", 50, 109}, + #endif + #ifdef CT_R_LOG_CONF_INVALID_KEY + {"LOG_CONF_INVALID_KEY", ERR_LIB_CT, CT_R_LOG_CONF_INVALID_KEY}, + #else + {"LOG_CONF_INVALID_KEY", 50, 110}, + #endif + #ifdef CT_R_LOG_CONF_MISSING_DESCRIPTION + {"LOG_CONF_MISSING_DESCRIPTION", ERR_LIB_CT, CT_R_LOG_CONF_MISSING_DESCRIPTION}, + #else + {"LOG_CONF_MISSING_DESCRIPTION", 50, 111}, + #endif + #ifdef CT_R_LOG_CONF_MISSING_KEY + {"LOG_CONF_MISSING_KEY", ERR_LIB_CT, CT_R_LOG_CONF_MISSING_KEY}, + #else + {"LOG_CONF_MISSING_KEY", 50, 112}, + #endif + #ifdef CT_R_LOG_KEY_INVALID + {"LOG_KEY_INVALID", ERR_LIB_CT, CT_R_LOG_KEY_INVALID}, + #else + {"LOG_KEY_INVALID", 50, 113}, + #endif + #ifdef CT_R_SCT_FUTURE_TIMESTAMP + {"SCT_FUTURE_TIMESTAMP", ERR_LIB_CT, CT_R_SCT_FUTURE_TIMESTAMP}, + #else + {"SCT_FUTURE_TIMESTAMP", 50, 116}, + #endif + #ifdef CT_R_SCT_INVALID + {"SCT_INVALID", ERR_LIB_CT, CT_R_SCT_INVALID}, + #else + {"SCT_INVALID", 50, 104}, + #endif + #ifdef CT_R_SCT_INVALID_SIGNATURE + {"SCT_INVALID_SIGNATURE", ERR_LIB_CT, CT_R_SCT_INVALID_SIGNATURE}, + #else + {"SCT_INVALID_SIGNATURE", 50, 107}, + #endif + #ifdef CT_R_SCT_LIST_INVALID + {"SCT_LIST_INVALID", ERR_LIB_CT, CT_R_SCT_LIST_INVALID}, + #else + {"SCT_LIST_INVALID", 50, 105}, + #endif + #ifdef CT_R_SCT_LOG_ID_MISMATCH + {"SCT_LOG_ID_MISMATCH", ERR_LIB_CT, CT_R_SCT_LOG_ID_MISMATCH}, + #else + {"SCT_LOG_ID_MISMATCH", 50, 114}, + #endif + #ifdef CT_R_SCT_NOT_SET + {"SCT_NOT_SET", ERR_LIB_CT, CT_R_SCT_NOT_SET}, + #else + {"SCT_NOT_SET", 50, 106}, + #endif + #ifdef CT_R_SCT_UNSUPPORTED_VERSION + {"SCT_UNSUPPORTED_VERSION", ERR_LIB_CT, CT_R_SCT_UNSUPPORTED_VERSION}, + #else + {"SCT_UNSUPPORTED_VERSION", 50, 115}, + #endif + #ifdef CT_R_UNRECOGNIZED_SIGNATURE_NID + {"UNRECOGNIZED_SIGNATURE_NID", ERR_LIB_CT, CT_R_UNRECOGNIZED_SIGNATURE_NID}, + #else + {"UNRECOGNIZED_SIGNATURE_NID", 50, 101}, + #endif + #ifdef CT_R_UNSUPPORTED_ENTRY_TYPE + {"UNSUPPORTED_ENTRY_TYPE", ERR_LIB_CT, CT_R_UNSUPPORTED_ENTRY_TYPE}, + #else + {"UNSUPPORTED_ENTRY_TYPE", 50, 102}, + #endif + #ifdef CT_R_UNSUPPORTED_VERSION + {"UNSUPPORTED_VERSION", ERR_LIB_CT, CT_R_UNSUPPORTED_VERSION}, + #else + {"UNSUPPORTED_VERSION", 50, 103}, + #endif + #ifdef DH_R_BAD_FFC_PARAMETERS + {"BAD_FFC_PARAMETERS", ERR_LIB_DH, DH_R_BAD_FFC_PARAMETERS}, + #else + {"BAD_FFC_PARAMETERS", 5, 127}, + #endif + #ifdef DH_R_BAD_GENERATOR + {"BAD_GENERATOR", ERR_LIB_DH, DH_R_BAD_GENERATOR}, + #else + {"BAD_GENERATOR", 5, 101}, + #endif + #ifdef DH_R_BN_DECODE_ERROR + {"BN_DECODE_ERROR", ERR_LIB_DH, DH_R_BN_DECODE_ERROR}, + #else + {"BN_DECODE_ERROR", 5, 109}, + #endif + #ifdef DH_R_BN_ERROR + {"BN_ERROR", ERR_LIB_DH, DH_R_BN_ERROR}, + #else + {"BN_ERROR", 5, 106}, + #endif + #ifdef DH_R_CHECK_INVALID_J_VALUE + {"CHECK_INVALID_J_VALUE", ERR_LIB_DH, DH_R_CHECK_INVALID_J_VALUE}, + #else + {"CHECK_INVALID_J_VALUE", 5, 115}, + #endif + #ifdef DH_R_CHECK_INVALID_Q_VALUE + {"CHECK_INVALID_Q_VALUE", ERR_LIB_DH, DH_R_CHECK_INVALID_Q_VALUE}, + #else + {"CHECK_INVALID_Q_VALUE", 5, 116}, + #endif + #ifdef DH_R_CHECK_PUBKEY_INVALID + {"CHECK_PUBKEY_INVALID", ERR_LIB_DH, DH_R_CHECK_PUBKEY_INVALID}, + #else + {"CHECK_PUBKEY_INVALID", 5, 122}, + #endif + #ifdef DH_R_CHECK_PUBKEY_TOO_LARGE + {"CHECK_PUBKEY_TOO_LARGE", ERR_LIB_DH, DH_R_CHECK_PUBKEY_TOO_LARGE}, + #else + {"CHECK_PUBKEY_TOO_LARGE", 5, 123}, + #endif + #ifdef DH_R_CHECK_PUBKEY_TOO_SMALL + {"CHECK_PUBKEY_TOO_SMALL", ERR_LIB_DH, DH_R_CHECK_PUBKEY_TOO_SMALL}, + #else + {"CHECK_PUBKEY_TOO_SMALL", 5, 124}, + #endif + #ifdef DH_R_CHECK_P_NOT_PRIME + {"CHECK_P_NOT_PRIME", ERR_LIB_DH, DH_R_CHECK_P_NOT_PRIME}, + #else + {"CHECK_P_NOT_PRIME", 5, 117}, + #endif + #ifdef DH_R_CHECK_P_NOT_SAFE_PRIME + {"CHECK_P_NOT_SAFE_PRIME", ERR_LIB_DH, DH_R_CHECK_P_NOT_SAFE_PRIME}, + #else + {"CHECK_P_NOT_SAFE_PRIME", 5, 118}, + #endif + #ifdef DH_R_CHECK_Q_NOT_PRIME + {"CHECK_Q_NOT_PRIME", ERR_LIB_DH, DH_R_CHECK_Q_NOT_PRIME}, + #else + {"CHECK_Q_NOT_PRIME", 5, 119}, + #endif + #ifdef DH_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_DH, DH_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 5, 104}, + #endif + #ifdef DH_R_INVALID_PARAMETER_NAME + {"INVALID_PARAMETER_NAME", ERR_LIB_DH, DH_R_INVALID_PARAMETER_NAME}, + #else + {"INVALID_PARAMETER_NAME", 5, 110}, + #endif + #ifdef DH_R_INVALID_PARAMETER_NID + {"INVALID_PARAMETER_NID", ERR_LIB_DH, DH_R_INVALID_PARAMETER_NID}, + #else + {"INVALID_PARAMETER_NID", 5, 114}, + #endif + #ifdef DH_R_INVALID_PUBKEY + {"INVALID_PUBKEY", ERR_LIB_DH, DH_R_INVALID_PUBKEY}, + #else + {"INVALID_PUBKEY", 5, 102}, + #endif + #ifdef DH_R_INVALID_SECRET + {"INVALID_SECRET", ERR_LIB_DH, DH_R_INVALID_SECRET}, + #else + {"INVALID_SECRET", 5, 128}, + #endif + #ifdef DH_R_INVALID_SIZE + {"INVALID_SIZE", ERR_LIB_DH, DH_R_INVALID_SIZE}, + #else + {"INVALID_SIZE", 5, 129}, + #endif + #ifdef DH_R_KDF_PARAMETER_ERROR + {"KDF_PARAMETER_ERROR", ERR_LIB_DH, DH_R_KDF_PARAMETER_ERROR}, + #else + {"KDF_PARAMETER_ERROR", 5, 112}, + #endif + #ifdef DH_R_KEYS_NOT_SET + {"KEYS_NOT_SET", ERR_LIB_DH, DH_R_KEYS_NOT_SET}, + #else + {"KEYS_NOT_SET", 5, 108}, + #endif + #ifdef DH_R_MISSING_PUBKEY + {"MISSING_PUBKEY", ERR_LIB_DH, DH_R_MISSING_PUBKEY}, + #else + {"MISSING_PUBKEY", 5, 125}, + #endif + #ifdef DH_R_MODULUS_TOO_LARGE + {"MODULUS_TOO_LARGE", ERR_LIB_DH, DH_R_MODULUS_TOO_LARGE}, + #else + {"MODULUS_TOO_LARGE", 5, 103}, + #endif + #ifdef DH_R_MODULUS_TOO_SMALL + {"MODULUS_TOO_SMALL", ERR_LIB_DH, DH_R_MODULUS_TOO_SMALL}, + #else + {"MODULUS_TOO_SMALL", 5, 126}, + #endif + #ifdef DH_R_NOT_SUITABLE_GENERATOR + {"NOT_SUITABLE_GENERATOR", ERR_LIB_DH, DH_R_NOT_SUITABLE_GENERATOR}, + #else + {"NOT_SUITABLE_GENERATOR", 5, 120}, + #endif + #ifdef DH_R_NO_PARAMETERS_SET + {"NO_PARAMETERS_SET", ERR_LIB_DH, DH_R_NO_PARAMETERS_SET}, + #else + {"NO_PARAMETERS_SET", 5, 107}, + #endif + #ifdef DH_R_NO_PRIVATE_VALUE + {"NO_PRIVATE_VALUE", ERR_LIB_DH, DH_R_NO_PRIVATE_VALUE}, + #else + {"NO_PRIVATE_VALUE", 5, 100}, + #endif + #ifdef DH_R_PARAMETER_ENCODING_ERROR + {"PARAMETER_ENCODING_ERROR", ERR_LIB_DH, DH_R_PARAMETER_ENCODING_ERROR}, + #else + {"PARAMETER_ENCODING_ERROR", 5, 105}, + #endif + #ifdef DH_R_PEER_KEY_ERROR + {"PEER_KEY_ERROR", ERR_LIB_DH, DH_R_PEER_KEY_ERROR}, + #else + {"PEER_KEY_ERROR", 5, 111}, + #endif + #ifdef DH_R_Q_TOO_LARGE + {"Q_TOO_LARGE", ERR_LIB_DH, DH_R_Q_TOO_LARGE}, + #else + {"Q_TOO_LARGE", 5, 130}, + #endif + #ifdef DH_R_SHARED_INFO_ERROR + {"SHARED_INFO_ERROR", ERR_LIB_DH, DH_R_SHARED_INFO_ERROR}, + #else + {"SHARED_INFO_ERROR", 5, 113}, + #endif + #ifdef DH_R_UNABLE_TO_CHECK_GENERATOR + {"UNABLE_TO_CHECK_GENERATOR", ERR_LIB_DH, DH_R_UNABLE_TO_CHECK_GENERATOR}, + #else + {"UNABLE_TO_CHECK_GENERATOR", 5, 121}, + #endif + #ifdef DSA_R_BAD_FFC_PARAMETERS + {"BAD_FFC_PARAMETERS", ERR_LIB_DSA, DSA_R_BAD_FFC_PARAMETERS}, + #else + {"BAD_FFC_PARAMETERS", 10, 114}, + #endif + #ifdef DSA_R_BAD_Q_VALUE + {"BAD_Q_VALUE", ERR_LIB_DSA, DSA_R_BAD_Q_VALUE}, + #else + {"BAD_Q_VALUE", 10, 102}, + #endif + #ifdef DSA_R_BN_DECODE_ERROR + {"BN_DECODE_ERROR", ERR_LIB_DSA, DSA_R_BN_DECODE_ERROR}, + #else + {"BN_DECODE_ERROR", 10, 108}, + #endif + #ifdef DSA_R_BN_ERROR + {"BN_ERROR", ERR_LIB_DSA, DSA_R_BN_ERROR}, + #else + {"BN_ERROR", 10, 109}, + #endif + #ifdef DSA_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_DSA, DSA_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 10, 104}, + #endif + #ifdef DSA_R_INVALID_DIGEST_TYPE + {"INVALID_DIGEST_TYPE", ERR_LIB_DSA, DSA_R_INVALID_DIGEST_TYPE}, + #else + {"INVALID_DIGEST_TYPE", 10, 106}, + #endif + #ifdef DSA_R_INVALID_PARAMETERS + {"INVALID_PARAMETERS", ERR_LIB_DSA, DSA_R_INVALID_PARAMETERS}, + #else + {"INVALID_PARAMETERS", 10, 112}, + #endif + #ifdef DSA_R_MISSING_PARAMETERS + {"MISSING_PARAMETERS", ERR_LIB_DSA, DSA_R_MISSING_PARAMETERS}, + #else + {"MISSING_PARAMETERS", 10, 101}, + #endif + #ifdef DSA_R_MISSING_PRIVATE_KEY + {"MISSING_PRIVATE_KEY", ERR_LIB_DSA, DSA_R_MISSING_PRIVATE_KEY}, + #else + {"MISSING_PRIVATE_KEY", 10, 111}, + #endif + #ifdef DSA_R_MODULUS_TOO_LARGE + {"MODULUS_TOO_LARGE", ERR_LIB_DSA, DSA_R_MODULUS_TOO_LARGE}, + #else + {"MODULUS_TOO_LARGE", 10, 103}, + #endif + #ifdef DSA_R_NO_PARAMETERS_SET + {"NO_PARAMETERS_SET", ERR_LIB_DSA, DSA_R_NO_PARAMETERS_SET}, + #else + {"NO_PARAMETERS_SET", 10, 107}, + #endif + #ifdef DSA_R_PARAMETER_ENCODING_ERROR + {"PARAMETER_ENCODING_ERROR", ERR_LIB_DSA, DSA_R_PARAMETER_ENCODING_ERROR}, + #else + {"PARAMETER_ENCODING_ERROR", 10, 105}, + #endif + #ifdef DSA_R_P_NOT_PRIME + {"P_NOT_PRIME", ERR_LIB_DSA, DSA_R_P_NOT_PRIME}, + #else + {"P_NOT_PRIME", 10, 115}, + #endif + #ifdef DSA_R_Q_NOT_PRIME + {"Q_NOT_PRIME", ERR_LIB_DSA, DSA_R_Q_NOT_PRIME}, + #else + {"Q_NOT_PRIME", 10, 113}, + #endif + #ifdef DSA_R_SEED_LEN_SMALL + {"SEED_LEN_SMALL", ERR_LIB_DSA, DSA_R_SEED_LEN_SMALL}, + #else + {"SEED_LEN_SMALL", 10, 110}, + #endif + #ifdef DSA_R_TOO_MANY_RETRIES + {"TOO_MANY_RETRIES", ERR_LIB_DSA, DSA_R_TOO_MANY_RETRIES}, + #else + {"TOO_MANY_RETRIES", 10, 116}, + #endif + #ifdef DSO_R_CTRL_FAILED + {"CTRL_FAILED", ERR_LIB_DSO, DSO_R_CTRL_FAILED}, + #else + {"CTRL_FAILED", 37, 100}, + #endif + #ifdef DSO_R_DSO_ALREADY_LOADED + {"DSO_ALREADY_LOADED", ERR_LIB_DSO, DSO_R_DSO_ALREADY_LOADED}, + #else + {"DSO_ALREADY_LOADED", 37, 110}, + #endif + #ifdef DSO_R_EMPTY_FILE_STRUCTURE + {"EMPTY_FILE_STRUCTURE", ERR_LIB_DSO, DSO_R_EMPTY_FILE_STRUCTURE}, + #else + {"EMPTY_FILE_STRUCTURE", 37, 113}, + #endif + #ifdef DSO_R_FAILURE + {"FAILURE", ERR_LIB_DSO, DSO_R_FAILURE}, + #else + {"FAILURE", 37, 114}, + #endif + #ifdef DSO_R_FILENAME_TOO_BIG + {"FILENAME_TOO_BIG", ERR_LIB_DSO, DSO_R_FILENAME_TOO_BIG}, + #else + {"FILENAME_TOO_BIG", 37, 101}, + #endif + #ifdef DSO_R_FINISH_FAILED + {"FINISH_FAILED", ERR_LIB_DSO, DSO_R_FINISH_FAILED}, + #else + {"FINISH_FAILED", 37, 102}, + #endif + #ifdef DSO_R_INCORRECT_FILE_SYNTAX + {"INCORRECT_FILE_SYNTAX", ERR_LIB_DSO, DSO_R_INCORRECT_FILE_SYNTAX}, + #else + {"INCORRECT_FILE_SYNTAX", 37, 115}, + #endif + #ifdef DSO_R_LOAD_FAILED + {"LOAD_FAILED", ERR_LIB_DSO, DSO_R_LOAD_FAILED}, + #else + {"LOAD_FAILED", 37, 103}, + #endif + #ifdef DSO_R_NAME_TRANSLATION_FAILED + {"NAME_TRANSLATION_FAILED", ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED}, + #else + {"NAME_TRANSLATION_FAILED", 37, 109}, + #endif + #ifdef DSO_R_NO_FILENAME + {"NO_FILENAME", ERR_LIB_DSO, DSO_R_NO_FILENAME}, + #else + {"NO_FILENAME", 37, 111}, + #endif + #ifdef DSO_R_NULL_HANDLE + {"NULL_HANDLE", ERR_LIB_DSO, DSO_R_NULL_HANDLE}, + #else + {"NULL_HANDLE", 37, 104}, + #endif + #ifdef DSO_R_SET_FILENAME_FAILED + {"SET_FILENAME_FAILED", ERR_LIB_DSO, DSO_R_SET_FILENAME_FAILED}, + #else + {"SET_FILENAME_FAILED", 37, 112}, + #endif + #ifdef DSO_R_STACK_ERROR + {"STACK_ERROR", ERR_LIB_DSO, DSO_R_STACK_ERROR}, + #else + {"STACK_ERROR", 37, 105}, + #endif + #ifdef DSO_R_SYM_FAILURE + {"SYM_FAILURE", ERR_LIB_DSO, DSO_R_SYM_FAILURE}, + #else + {"SYM_FAILURE", 37, 106}, + #endif + #ifdef DSO_R_UNLOAD_FAILED + {"UNLOAD_FAILED", ERR_LIB_DSO, DSO_R_UNLOAD_FAILED}, + #else + {"UNLOAD_FAILED", 37, 107}, + #endif + #ifdef DSO_R_UNSUPPORTED + {"UNSUPPORTED", ERR_LIB_DSO, DSO_R_UNSUPPORTED}, + #else + {"UNSUPPORTED", 37, 108}, + #endif + #ifdef EC_R_ASN1_ERROR + {"ASN1_ERROR", ERR_LIB_EC, EC_R_ASN1_ERROR}, + #else + {"ASN1_ERROR", 16, 115}, + #endif + #ifdef EC_R_BAD_SIGNATURE + {"BAD_SIGNATURE", ERR_LIB_EC, EC_R_BAD_SIGNATURE}, + #else + {"BAD_SIGNATURE", 16, 156}, + #endif + #ifdef EC_R_BIGNUM_OUT_OF_RANGE + {"BIGNUM_OUT_OF_RANGE", ERR_LIB_EC, EC_R_BIGNUM_OUT_OF_RANGE}, + #else + {"BIGNUM_OUT_OF_RANGE", 16, 144}, + #endif + #ifdef EC_R_BUFFER_TOO_SMALL + {"BUFFER_TOO_SMALL", ERR_LIB_EC, EC_R_BUFFER_TOO_SMALL}, + #else + {"BUFFER_TOO_SMALL", 16, 100}, + #endif + #ifdef EC_R_CANNOT_INVERT + {"CANNOT_INVERT", ERR_LIB_EC, EC_R_CANNOT_INVERT}, + #else + {"CANNOT_INVERT", 16, 165}, + #endif + #ifdef EC_R_COORDINATES_OUT_OF_RANGE + {"COORDINATES_OUT_OF_RANGE", ERR_LIB_EC, EC_R_COORDINATES_OUT_OF_RANGE}, + #else + {"COORDINATES_OUT_OF_RANGE", 16, 146}, + #endif + #ifdef EC_R_CURVE_DOES_NOT_SUPPORT_ECDH + {"CURVE_DOES_NOT_SUPPORT_ECDH", ERR_LIB_EC, EC_R_CURVE_DOES_NOT_SUPPORT_ECDH}, + #else + {"CURVE_DOES_NOT_SUPPORT_ECDH", 16, 160}, + #endif + #ifdef EC_R_CURVE_DOES_NOT_SUPPORT_ECDSA + {"CURVE_DOES_NOT_SUPPORT_ECDSA", ERR_LIB_EC, EC_R_CURVE_DOES_NOT_SUPPORT_ECDSA}, + #else + {"CURVE_DOES_NOT_SUPPORT_ECDSA", 16, 170}, + #endif + #ifdef EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING + {"CURVE_DOES_NOT_SUPPORT_SIGNING", ERR_LIB_EC, EC_R_CURVE_DOES_NOT_SUPPORT_SIGNING}, + #else + {"CURVE_DOES_NOT_SUPPORT_SIGNING", 16, 159}, + #endif + #ifdef EC_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_EC, EC_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 16, 142}, + #endif + #ifdef EC_R_DISCRIMINANT_IS_ZERO + {"DISCRIMINANT_IS_ZERO", ERR_LIB_EC, EC_R_DISCRIMINANT_IS_ZERO}, + #else + {"DISCRIMINANT_IS_ZERO", 16, 118}, + #endif + #ifdef EC_R_EC_GROUP_NEW_BY_NAME_FAILURE + {"EC_GROUP_NEW_BY_NAME_FAILURE", ERR_LIB_EC, EC_R_EC_GROUP_NEW_BY_NAME_FAILURE}, + #else + {"EC_GROUP_NEW_BY_NAME_FAILURE", 16, 119}, + #endif + #ifdef EC_R_EXPLICIT_PARAMS_NOT_SUPPORTED + {"EXPLICIT_PARAMS_NOT_SUPPORTED", ERR_LIB_EC, EC_R_EXPLICIT_PARAMS_NOT_SUPPORTED}, + #else + {"EXPLICIT_PARAMS_NOT_SUPPORTED", 16, 127}, + #endif + #ifdef EC_R_FAILED_MAKING_PUBLIC_KEY + {"FAILED_MAKING_PUBLIC_KEY", ERR_LIB_EC, EC_R_FAILED_MAKING_PUBLIC_KEY}, + #else + {"FAILED_MAKING_PUBLIC_KEY", 16, 166}, + #endif + #ifdef EC_R_FIELD_TOO_LARGE + {"FIELD_TOO_LARGE", ERR_LIB_EC, EC_R_FIELD_TOO_LARGE}, + #else + {"FIELD_TOO_LARGE", 16, 143}, + #endif + #ifdef EC_R_GF2M_NOT_SUPPORTED + {"GF2M_NOT_SUPPORTED", ERR_LIB_EC, EC_R_GF2M_NOT_SUPPORTED}, + #else + {"GF2M_NOT_SUPPORTED", 16, 147}, + #endif + #ifdef EC_R_GROUP2PKPARAMETERS_FAILURE + {"GROUP2PKPARAMETERS_FAILURE", ERR_LIB_EC, EC_R_GROUP2PKPARAMETERS_FAILURE}, + #else + {"GROUP2PKPARAMETERS_FAILURE", 16, 120}, + #endif + #ifdef EC_R_I2D_ECPKPARAMETERS_FAILURE + {"I2D_ECPKPARAMETERS_FAILURE", ERR_LIB_EC, EC_R_I2D_ECPKPARAMETERS_FAILURE}, + #else + {"I2D_ECPKPARAMETERS_FAILURE", 16, 121}, + #endif + #ifdef EC_R_INCOMPATIBLE_OBJECTS + {"INCOMPATIBLE_OBJECTS", ERR_LIB_EC, EC_R_INCOMPATIBLE_OBJECTS}, + #else + {"INCOMPATIBLE_OBJECTS", 16, 101}, + #endif + #ifdef EC_R_INVALID_A + {"INVALID_A", ERR_LIB_EC, EC_R_INVALID_A}, + #else + {"INVALID_A", 16, 168}, + #endif + #ifdef EC_R_INVALID_ARGUMENT + {"INVALID_ARGUMENT", ERR_LIB_EC, EC_R_INVALID_ARGUMENT}, + #else + {"INVALID_ARGUMENT", 16, 112}, + #endif + #ifdef EC_R_INVALID_B + {"INVALID_B", ERR_LIB_EC, EC_R_INVALID_B}, + #else + {"INVALID_B", 16, 169}, + #endif + #ifdef EC_R_INVALID_COFACTOR + {"INVALID_COFACTOR", ERR_LIB_EC, EC_R_INVALID_COFACTOR}, + #else + {"INVALID_COFACTOR", 16, 171}, + #endif + #ifdef EC_R_INVALID_COMPRESSED_POINT + {"INVALID_COMPRESSED_POINT", ERR_LIB_EC, EC_R_INVALID_COMPRESSED_POINT}, + #else + {"INVALID_COMPRESSED_POINT", 16, 110}, + #endif + #ifdef EC_R_INVALID_COMPRESSION_BIT + {"INVALID_COMPRESSION_BIT", ERR_LIB_EC, EC_R_INVALID_COMPRESSION_BIT}, + #else + {"INVALID_COMPRESSION_BIT", 16, 109}, + #endif + #ifdef EC_R_INVALID_CURVE + {"INVALID_CURVE", ERR_LIB_EC, EC_R_INVALID_CURVE}, + #else + {"INVALID_CURVE", 16, 141}, + #endif + #ifdef EC_R_INVALID_DIGEST + {"INVALID_DIGEST", ERR_LIB_EC, EC_R_INVALID_DIGEST}, + #else + {"INVALID_DIGEST", 16, 151}, + #endif + #ifdef EC_R_INVALID_DIGEST_TYPE + {"INVALID_DIGEST_TYPE", ERR_LIB_EC, EC_R_INVALID_DIGEST_TYPE}, + #else + {"INVALID_DIGEST_TYPE", 16, 138}, + #endif + #ifdef EC_R_INVALID_ENCODING + {"INVALID_ENCODING", ERR_LIB_EC, EC_R_INVALID_ENCODING}, + #else + {"INVALID_ENCODING", 16, 102}, + #endif + #ifdef EC_R_INVALID_FIELD + {"INVALID_FIELD", ERR_LIB_EC, EC_R_INVALID_FIELD}, + #else + {"INVALID_FIELD", 16, 103}, + #endif + #ifdef EC_R_INVALID_FORM + {"INVALID_FORM", ERR_LIB_EC, EC_R_INVALID_FORM}, + #else + {"INVALID_FORM", 16, 104}, + #endif + #ifdef EC_R_INVALID_GENERATOR + {"INVALID_GENERATOR", ERR_LIB_EC, EC_R_INVALID_GENERATOR}, + #else + {"INVALID_GENERATOR", 16, 173}, + #endif + #ifdef EC_R_INVALID_GROUP_ORDER + {"INVALID_GROUP_ORDER", ERR_LIB_EC, EC_R_INVALID_GROUP_ORDER}, + #else + {"INVALID_GROUP_ORDER", 16, 122}, + #endif + #ifdef EC_R_INVALID_KEY + {"INVALID_KEY", ERR_LIB_EC, EC_R_INVALID_KEY}, + #else + {"INVALID_KEY", 16, 116}, + #endif + #ifdef EC_R_INVALID_LENGTH + {"INVALID_LENGTH", ERR_LIB_EC, EC_R_INVALID_LENGTH}, + #else + {"INVALID_LENGTH", 16, 117}, + #endif + #ifdef EC_R_INVALID_NAMED_GROUP_CONVERSION + {"INVALID_NAMED_GROUP_CONVERSION", ERR_LIB_EC, EC_R_INVALID_NAMED_GROUP_CONVERSION}, + #else + {"INVALID_NAMED_GROUP_CONVERSION", 16, 174}, + #endif + #ifdef EC_R_INVALID_OUTPUT_LENGTH + {"INVALID_OUTPUT_LENGTH", ERR_LIB_EC, EC_R_INVALID_OUTPUT_LENGTH}, + #else + {"INVALID_OUTPUT_LENGTH", 16, 161}, + #endif + #ifdef EC_R_INVALID_P + {"INVALID_P", ERR_LIB_EC, EC_R_INVALID_P}, + #else + {"INVALID_P", 16, 172}, + #endif + #ifdef EC_R_INVALID_PEER_KEY + {"INVALID_PEER_KEY", ERR_LIB_EC, EC_R_INVALID_PEER_KEY}, + #else + {"INVALID_PEER_KEY", 16, 133}, + #endif + #ifdef EC_R_INVALID_PENTANOMIAL_BASIS + {"INVALID_PENTANOMIAL_BASIS", ERR_LIB_EC, EC_R_INVALID_PENTANOMIAL_BASIS}, + #else + {"INVALID_PENTANOMIAL_BASIS", 16, 132}, + #endif + #ifdef EC_R_INVALID_PRIVATE_KEY + {"INVALID_PRIVATE_KEY", ERR_LIB_EC, EC_R_INVALID_PRIVATE_KEY}, + #else + {"INVALID_PRIVATE_KEY", 16, 123}, + #endif + #ifdef EC_R_INVALID_SEED + {"INVALID_SEED", ERR_LIB_EC, EC_R_INVALID_SEED}, + #else + {"INVALID_SEED", 16, 175}, + #endif + #ifdef EC_R_INVALID_TRINOMIAL_BASIS + {"INVALID_TRINOMIAL_BASIS", ERR_LIB_EC, EC_R_INVALID_TRINOMIAL_BASIS}, + #else + {"INVALID_TRINOMIAL_BASIS", 16, 137}, + #endif + #ifdef EC_R_KDF_PARAMETER_ERROR + {"KDF_PARAMETER_ERROR", ERR_LIB_EC, EC_R_KDF_PARAMETER_ERROR}, + #else + {"KDF_PARAMETER_ERROR", 16, 148}, + #endif + #ifdef EC_R_KEYS_NOT_SET + {"KEYS_NOT_SET", ERR_LIB_EC, EC_R_KEYS_NOT_SET}, + #else + {"KEYS_NOT_SET", 16, 140}, + #endif + #ifdef EC_R_LADDER_POST_FAILURE + {"LADDER_POST_FAILURE", ERR_LIB_EC, EC_R_LADDER_POST_FAILURE}, + #else + {"LADDER_POST_FAILURE", 16, 136}, + #endif + #ifdef EC_R_LADDER_PRE_FAILURE + {"LADDER_PRE_FAILURE", ERR_LIB_EC, EC_R_LADDER_PRE_FAILURE}, + #else + {"LADDER_PRE_FAILURE", 16, 153}, + #endif + #ifdef EC_R_LADDER_STEP_FAILURE + {"LADDER_STEP_FAILURE", ERR_LIB_EC, EC_R_LADDER_STEP_FAILURE}, + #else + {"LADDER_STEP_FAILURE", 16, 162}, + #endif + #ifdef EC_R_MISSING_OID + {"MISSING_OID", ERR_LIB_EC, EC_R_MISSING_OID}, + #else + {"MISSING_OID", 16, 167}, + #endif + #ifdef EC_R_MISSING_PARAMETERS + {"MISSING_PARAMETERS", ERR_LIB_EC, EC_R_MISSING_PARAMETERS}, + #else + {"MISSING_PARAMETERS", 16, 124}, + #endif + #ifdef EC_R_MISSING_PRIVATE_KEY + {"MISSING_PRIVATE_KEY", ERR_LIB_EC, EC_R_MISSING_PRIVATE_KEY}, + #else + {"MISSING_PRIVATE_KEY", 16, 125}, + #endif + #ifdef EC_R_NEED_NEW_SETUP_VALUES + {"NEED_NEW_SETUP_VALUES", ERR_LIB_EC, EC_R_NEED_NEW_SETUP_VALUES}, + #else + {"NEED_NEW_SETUP_VALUES", 16, 157}, + #endif + #ifdef EC_R_NOT_A_NIST_PRIME + {"NOT_A_NIST_PRIME", ERR_LIB_EC, EC_R_NOT_A_NIST_PRIME}, + #else + {"NOT_A_NIST_PRIME", 16, 135}, + #endif + #ifdef EC_R_NOT_IMPLEMENTED + {"NOT_IMPLEMENTED", ERR_LIB_EC, EC_R_NOT_IMPLEMENTED}, + #else + {"NOT_IMPLEMENTED", 16, 126}, + #endif + #ifdef EC_R_NOT_INITIALIZED + {"NOT_INITIALIZED", ERR_LIB_EC, EC_R_NOT_INITIALIZED}, + #else + {"NOT_INITIALIZED", 16, 111}, + #endif + #ifdef EC_R_NO_PARAMETERS_SET + {"NO_PARAMETERS_SET", ERR_LIB_EC, EC_R_NO_PARAMETERS_SET}, + #else + {"NO_PARAMETERS_SET", 16, 139}, + #endif + #ifdef EC_R_NO_PRIVATE_VALUE + {"NO_PRIVATE_VALUE", ERR_LIB_EC, EC_R_NO_PRIVATE_VALUE}, + #else + {"NO_PRIVATE_VALUE", 16, 154}, + #endif + #ifdef EC_R_OPERATION_NOT_SUPPORTED + {"OPERATION_NOT_SUPPORTED", ERR_LIB_EC, EC_R_OPERATION_NOT_SUPPORTED}, + #else + {"OPERATION_NOT_SUPPORTED", 16, 152}, + #endif + #ifdef EC_R_PASSED_NULL_PARAMETER + {"PASSED_NULL_PARAMETER", ERR_LIB_EC, EC_R_PASSED_NULL_PARAMETER}, + #else + {"PASSED_NULL_PARAMETER", 16, 134}, + #endif + #ifdef EC_R_PEER_KEY_ERROR + {"PEER_KEY_ERROR", ERR_LIB_EC, EC_R_PEER_KEY_ERROR}, + #else + {"PEER_KEY_ERROR", 16, 149}, + #endif + #ifdef EC_R_POINT_ARITHMETIC_FAILURE + {"POINT_ARITHMETIC_FAILURE", ERR_LIB_EC, EC_R_POINT_ARITHMETIC_FAILURE}, + #else + {"POINT_ARITHMETIC_FAILURE", 16, 155}, + #endif + #ifdef EC_R_POINT_AT_INFINITY + {"POINT_AT_INFINITY", ERR_LIB_EC, EC_R_POINT_AT_INFINITY}, + #else + {"POINT_AT_INFINITY", 16, 106}, + #endif + #ifdef EC_R_POINT_COORDINATES_BLIND_FAILURE + {"POINT_COORDINATES_BLIND_FAILURE", ERR_LIB_EC, EC_R_POINT_COORDINATES_BLIND_FAILURE}, + #else + {"POINT_COORDINATES_BLIND_FAILURE", 16, 163}, + #endif + #ifdef EC_R_POINT_IS_NOT_ON_CURVE + {"POINT_IS_NOT_ON_CURVE", ERR_LIB_EC, EC_R_POINT_IS_NOT_ON_CURVE}, + #else + {"POINT_IS_NOT_ON_CURVE", 16, 107}, + #endif + #ifdef EC_R_RANDOM_NUMBER_GENERATION_FAILED + {"RANDOM_NUMBER_GENERATION_FAILED", ERR_LIB_EC, EC_R_RANDOM_NUMBER_GENERATION_FAILED}, + #else + {"RANDOM_NUMBER_GENERATION_FAILED", 16, 158}, + #endif + #ifdef EC_R_SHARED_INFO_ERROR + {"SHARED_INFO_ERROR", ERR_LIB_EC, EC_R_SHARED_INFO_ERROR}, + #else + {"SHARED_INFO_ERROR", 16, 150}, + #endif + #ifdef EC_R_SLOT_FULL + {"SLOT_FULL", ERR_LIB_EC, EC_R_SLOT_FULL}, + #else + {"SLOT_FULL", 16, 108}, + #endif + #ifdef EC_R_TOO_MANY_RETRIES + {"TOO_MANY_RETRIES", ERR_LIB_EC, EC_R_TOO_MANY_RETRIES}, + #else + {"TOO_MANY_RETRIES", 16, 176}, + #endif + #ifdef EC_R_UNDEFINED_GENERATOR + {"UNDEFINED_GENERATOR", ERR_LIB_EC, EC_R_UNDEFINED_GENERATOR}, + #else + {"UNDEFINED_GENERATOR", 16, 113}, + #endif + #ifdef EC_R_UNDEFINED_ORDER + {"UNDEFINED_ORDER", ERR_LIB_EC, EC_R_UNDEFINED_ORDER}, + #else + {"UNDEFINED_ORDER", 16, 128}, + #endif + #ifdef EC_R_UNKNOWN_COFACTOR + {"UNKNOWN_COFACTOR", ERR_LIB_EC, EC_R_UNKNOWN_COFACTOR}, + #else + {"UNKNOWN_COFACTOR", 16, 164}, + #endif + #ifdef EC_R_UNKNOWN_GROUP + {"UNKNOWN_GROUP", ERR_LIB_EC, EC_R_UNKNOWN_GROUP}, + #else + {"UNKNOWN_GROUP", 16, 129}, + #endif + #ifdef EC_R_UNKNOWN_ORDER + {"UNKNOWN_ORDER", ERR_LIB_EC, EC_R_UNKNOWN_ORDER}, + #else + {"UNKNOWN_ORDER", 16, 114}, + #endif + #ifdef EC_R_UNSUPPORTED_FIELD + {"UNSUPPORTED_FIELD", ERR_LIB_EC, EC_R_UNSUPPORTED_FIELD}, + #else + {"UNSUPPORTED_FIELD", 16, 131}, + #endif + #ifdef EC_R_WRONG_CURVE_PARAMETERS + {"WRONG_CURVE_PARAMETERS", ERR_LIB_EC, EC_R_WRONG_CURVE_PARAMETERS}, + #else + {"WRONG_CURVE_PARAMETERS", 16, 145}, + #endif + #ifdef EC_R_WRONG_ORDER + {"WRONG_ORDER", ERR_LIB_EC, EC_R_WRONG_ORDER}, + #else + {"WRONG_ORDER", 16, 130}, + #endif + #ifdef ESS_R_EMPTY_ESS_CERT_ID_LIST + {"EMPTY_ESS_CERT_ID_LIST", ERR_LIB_ESS, ESS_R_EMPTY_ESS_CERT_ID_LIST}, + #else + {"EMPTY_ESS_CERT_ID_LIST", 54, 107}, + #endif + #ifdef ESS_R_ESS_CERT_DIGEST_ERROR + {"ESS_CERT_DIGEST_ERROR", ERR_LIB_ESS, ESS_R_ESS_CERT_DIGEST_ERROR}, + #else + {"ESS_CERT_DIGEST_ERROR", 54, 103}, + #endif + #ifdef ESS_R_ESS_CERT_ID_NOT_FOUND + {"ESS_CERT_ID_NOT_FOUND", ERR_LIB_ESS, ESS_R_ESS_CERT_ID_NOT_FOUND}, + #else + {"ESS_CERT_ID_NOT_FOUND", 54, 104}, + #endif + #ifdef ESS_R_ESS_CERT_ID_WRONG_ORDER + {"ESS_CERT_ID_WRONG_ORDER", ERR_LIB_ESS, ESS_R_ESS_CERT_ID_WRONG_ORDER}, + #else + {"ESS_CERT_ID_WRONG_ORDER", 54, 105}, + #endif + #ifdef ESS_R_ESS_DIGEST_ALG_UNKNOWN + {"ESS_DIGEST_ALG_UNKNOWN", ERR_LIB_ESS, ESS_R_ESS_DIGEST_ALG_UNKNOWN}, + #else + {"ESS_DIGEST_ALG_UNKNOWN", 54, 106}, + #endif + #ifdef ESS_R_ESS_SIGNING_CERTIFICATE_ERROR + {"ESS_SIGNING_CERTIFICATE_ERROR", ERR_LIB_ESS, ESS_R_ESS_SIGNING_CERTIFICATE_ERROR}, + #else + {"ESS_SIGNING_CERTIFICATE_ERROR", 54, 102}, + #endif + #ifdef ESS_R_ESS_SIGNING_CERT_ADD_ERROR + {"ESS_SIGNING_CERT_ADD_ERROR", ERR_LIB_ESS, ESS_R_ESS_SIGNING_CERT_ADD_ERROR}, + #else + {"ESS_SIGNING_CERT_ADD_ERROR", 54, 100}, + #endif + #ifdef ESS_R_ESS_SIGNING_CERT_V2_ADD_ERROR + {"ESS_SIGNING_CERT_V2_ADD_ERROR", ERR_LIB_ESS, ESS_R_ESS_SIGNING_CERT_V2_ADD_ERROR}, + #else + {"ESS_SIGNING_CERT_V2_ADD_ERROR", 54, 101}, + #endif + #ifdef ESS_R_MISSING_SIGNING_CERTIFICATE_ATTRIBUTE + {"MISSING_SIGNING_CERTIFICATE_ATTRIBUTE", ERR_LIB_ESS, ESS_R_MISSING_SIGNING_CERTIFICATE_ATTRIBUTE}, + #else + {"MISSING_SIGNING_CERTIFICATE_ATTRIBUTE", 54, 108}, + #endif + #ifdef EVP_R_AES_KEY_SETUP_FAILED + {"AES_KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_AES_KEY_SETUP_FAILED}, + #else + {"AES_KEY_SETUP_FAILED", 6, 143}, + #endif + #ifdef EVP_R_ARIA_KEY_SETUP_FAILED + {"ARIA_KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_ARIA_KEY_SETUP_FAILED}, + #else + {"ARIA_KEY_SETUP_FAILED", 6, 176}, + #endif + #ifdef EVP_R_BAD_ALGORITHM_NAME + {"BAD_ALGORITHM_NAME", ERR_LIB_EVP, EVP_R_BAD_ALGORITHM_NAME}, + #else + {"BAD_ALGORITHM_NAME", 6, 200}, + #endif + #ifdef EVP_R_BAD_DECRYPT + {"BAD_DECRYPT", ERR_LIB_EVP, EVP_R_BAD_DECRYPT}, + #else + {"BAD_DECRYPT", 6, 100}, + #endif + #ifdef EVP_R_BAD_KEY_LENGTH + {"BAD_KEY_LENGTH", ERR_LIB_EVP, EVP_R_BAD_KEY_LENGTH}, + #else + {"BAD_KEY_LENGTH", 6, 195}, + #endif + #ifdef EVP_R_BUFFER_TOO_SMALL + {"BUFFER_TOO_SMALL", ERR_LIB_EVP, EVP_R_BUFFER_TOO_SMALL}, + #else + {"BUFFER_TOO_SMALL", 6, 155}, + #endif + #ifdef EVP_R_CACHE_CONSTANTS_FAILED + {"CACHE_CONSTANTS_FAILED", ERR_LIB_EVP, EVP_R_CACHE_CONSTANTS_FAILED}, + #else + {"CACHE_CONSTANTS_FAILED", 6, 225}, + #endif + #ifdef EVP_R_CAMELLIA_KEY_SETUP_FAILED + {"CAMELLIA_KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_CAMELLIA_KEY_SETUP_FAILED}, + #else + {"CAMELLIA_KEY_SETUP_FAILED", 6, 157}, + #endif + #ifdef EVP_R_CANNOT_GET_PARAMETERS + {"CANNOT_GET_PARAMETERS", ERR_LIB_EVP, EVP_R_CANNOT_GET_PARAMETERS}, + #else + {"CANNOT_GET_PARAMETERS", 6, 197}, + #endif + #ifdef EVP_R_CANNOT_SET_PARAMETERS + {"CANNOT_SET_PARAMETERS", ERR_LIB_EVP, EVP_R_CANNOT_SET_PARAMETERS}, + #else + {"CANNOT_SET_PARAMETERS", 6, 198}, + #endif + #ifdef EVP_R_CIPHER_NOT_GCM_MODE + {"CIPHER_NOT_GCM_MODE", ERR_LIB_EVP, EVP_R_CIPHER_NOT_GCM_MODE}, + #else + {"CIPHER_NOT_GCM_MODE", 6, 184}, + #endif + #ifdef EVP_R_CIPHER_PARAMETER_ERROR + {"CIPHER_PARAMETER_ERROR", ERR_LIB_EVP, EVP_R_CIPHER_PARAMETER_ERROR}, + #else + {"CIPHER_PARAMETER_ERROR", 6, 122}, + #endif + #ifdef EVP_R_COMMAND_NOT_SUPPORTED + {"COMMAND_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED}, + #else + {"COMMAND_NOT_SUPPORTED", 6, 147}, + #endif + #ifdef EVP_R_CONFLICTING_ALGORITHM_NAME + {"CONFLICTING_ALGORITHM_NAME", ERR_LIB_EVP, EVP_R_CONFLICTING_ALGORITHM_NAME}, + #else + {"CONFLICTING_ALGORITHM_NAME", 6, 201}, + #endif + #ifdef EVP_R_CONTEXT_FINALIZED + {"CONTEXT_FINALIZED", ERR_LIB_EVP, EVP_R_CONTEXT_FINALIZED}, + #else + {"CONTEXT_FINALIZED", 6, 239}, + #endif + #ifdef EVP_R_COPY_ERROR + {"COPY_ERROR", ERR_LIB_EVP, EVP_R_COPY_ERROR}, + #else + {"COPY_ERROR", 6, 173}, + #endif + #ifdef EVP_R_CTRL_NOT_IMPLEMENTED + {"CTRL_NOT_IMPLEMENTED", ERR_LIB_EVP, EVP_R_CTRL_NOT_IMPLEMENTED}, + #else + {"CTRL_NOT_IMPLEMENTED", 6, 132}, + #endif + #ifdef EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED + {"CTRL_OPERATION_NOT_IMPLEMENTED", ERR_LIB_EVP, EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED}, + #else + {"CTRL_OPERATION_NOT_IMPLEMENTED", 6, 133}, + #endif + #ifdef EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH + {"DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH", ERR_LIB_EVP, EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH}, + #else + {"DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH", 6, 138}, + #endif + #ifdef EVP_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_EVP, EVP_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 6, 114}, + #endif + #ifdef EVP_R_DEFAULT_QUERY_PARSE_ERROR + {"DEFAULT_QUERY_PARSE_ERROR", ERR_LIB_EVP, EVP_R_DEFAULT_QUERY_PARSE_ERROR}, + #else + {"DEFAULT_QUERY_PARSE_ERROR", 6, 210}, + #endif + #ifdef EVP_R_DIFFERENT_KEY_TYPES + {"DIFFERENT_KEY_TYPES", ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES}, + #else + {"DIFFERENT_KEY_TYPES", 6, 101}, + #endif + #ifdef EVP_R_DIFFERENT_PARAMETERS + {"DIFFERENT_PARAMETERS", ERR_LIB_EVP, EVP_R_DIFFERENT_PARAMETERS}, + #else + {"DIFFERENT_PARAMETERS", 6, 153}, + #endif + #ifdef EVP_R_ERROR_LOADING_SECTION + {"ERROR_LOADING_SECTION", ERR_LIB_EVP, EVP_R_ERROR_LOADING_SECTION}, + #else + {"ERROR_LOADING_SECTION", 6, 165}, + #endif + #ifdef EVP_R_EXPECTING_AN_HMAC_KEY + {"EXPECTING_AN_HMAC_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_AN_HMAC_KEY}, + #else + {"EXPECTING_AN_HMAC_KEY", 6, 174}, + #endif + #ifdef EVP_R_EXPECTING_AN_RSA_KEY + {"EXPECTING_AN_RSA_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_AN_RSA_KEY}, + #else + {"EXPECTING_AN_RSA_KEY", 6, 127}, + #endif + #ifdef EVP_R_EXPECTING_A_DH_KEY + {"EXPECTING_A_DH_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_DH_KEY}, + #else + {"EXPECTING_A_DH_KEY", 6, 128}, + #endif + #ifdef EVP_R_EXPECTING_A_DSA_KEY + {"EXPECTING_A_DSA_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_DSA_KEY}, + #else + {"EXPECTING_A_DSA_KEY", 6, 129}, + #endif + #ifdef EVP_R_EXPECTING_A_ECX_KEY + {"EXPECTING_A_ECX_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_ECX_KEY}, + #else + {"EXPECTING_A_ECX_KEY", 6, 219}, + #endif + #ifdef EVP_R_EXPECTING_A_EC_KEY + {"EXPECTING_A_EC_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_EC_KEY}, + #else + {"EXPECTING_A_EC_KEY", 6, 142}, + #endif + #ifdef EVP_R_EXPECTING_A_POLY1305_KEY + {"EXPECTING_A_POLY1305_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_POLY1305_KEY}, + #else + {"EXPECTING_A_POLY1305_KEY", 6, 164}, + #endif + #ifdef EVP_R_EXPECTING_A_SIPHASH_KEY + {"EXPECTING_A_SIPHASH_KEY", ERR_LIB_EVP, EVP_R_EXPECTING_A_SIPHASH_KEY}, + #else + {"EXPECTING_A_SIPHASH_KEY", 6, 175}, + #endif + #ifdef EVP_R_FINAL_ERROR + {"FINAL_ERROR", ERR_LIB_EVP, EVP_R_FINAL_ERROR}, + #else + {"FINAL_ERROR", 6, 188}, + #endif + #ifdef EVP_R_GENERATE_ERROR + {"GENERATE_ERROR", ERR_LIB_EVP, EVP_R_GENERATE_ERROR}, + #else + {"GENERATE_ERROR", 6, 214}, + #endif + #ifdef EVP_R_GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED + {"GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED}, + #else + {"GETTING_ALGORITHMIDENTIFIER_NOT_SUPPORTED", 6, 229}, + #endif + #ifdef EVP_R_GET_RAW_KEY_FAILED + {"GET_RAW_KEY_FAILED", ERR_LIB_EVP, EVP_R_GET_RAW_KEY_FAILED}, + #else + {"GET_RAW_KEY_FAILED", 6, 182}, + #endif + #ifdef EVP_R_ILLEGAL_SCRYPT_PARAMETERS + {"ILLEGAL_SCRYPT_PARAMETERS", ERR_LIB_EVP, EVP_R_ILLEGAL_SCRYPT_PARAMETERS}, + #else + {"ILLEGAL_SCRYPT_PARAMETERS", 6, 171}, + #endif + #ifdef EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS + {"INACCESSIBLE_DOMAIN_PARAMETERS", ERR_LIB_EVP, EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS}, + #else + {"INACCESSIBLE_DOMAIN_PARAMETERS", 6, 204}, + #endif + #ifdef EVP_R_INACCESSIBLE_KEY + {"INACCESSIBLE_KEY", ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY}, + #else + {"INACCESSIBLE_KEY", 6, 203}, + #endif + #ifdef EVP_R_INITIALIZATION_ERROR + {"INITIALIZATION_ERROR", ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR}, + #else + {"INITIALIZATION_ERROR", 6, 134}, + #endif + #ifdef EVP_R_INPUT_NOT_INITIALIZED + {"INPUT_NOT_INITIALIZED", ERR_LIB_EVP, EVP_R_INPUT_NOT_INITIALIZED}, + #else + {"INPUT_NOT_INITIALIZED", 6, 111}, + #endif + #ifdef EVP_R_INVALID_CUSTOM_LENGTH + {"INVALID_CUSTOM_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_CUSTOM_LENGTH}, + #else + {"INVALID_CUSTOM_LENGTH", 6, 185}, + #endif + #ifdef EVP_R_INVALID_DIGEST + {"INVALID_DIGEST", ERR_LIB_EVP, EVP_R_INVALID_DIGEST}, + #else + {"INVALID_DIGEST", 6, 152}, + #endif + #ifdef EVP_R_INVALID_IV_LENGTH + {"INVALID_IV_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_IV_LENGTH}, + #else + {"INVALID_IV_LENGTH", 6, 194}, + #endif + #ifdef EVP_R_INVALID_KEY + {"INVALID_KEY", ERR_LIB_EVP, EVP_R_INVALID_KEY}, + #else + {"INVALID_KEY", 6, 163}, + #endif + #ifdef EVP_R_INVALID_KEY_LENGTH + {"INVALID_KEY_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_KEY_LENGTH}, + #else + {"INVALID_KEY_LENGTH", 6, 130}, + #endif + #ifdef EVP_R_INVALID_LENGTH + {"INVALID_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_LENGTH}, + #else + {"INVALID_LENGTH", 6, 221}, + #endif + #ifdef EVP_R_INVALID_NULL_ALGORITHM + {"INVALID_NULL_ALGORITHM", ERR_LIB_EVP, EVP_R_INVALID_NULL_ALGORITHM}, + #else + {"INVALID_NULL_ALGORITHM", 6, 218}, + #endif + #ifdef EVP_R_INVALID_OPERATION + {"INVALID_OPERATION", ERR_LIB_EVP, EVP_R_INVALID_OPERATION}, + #else + {"INVALID_OPERATION", 6, 148}, + #endif + #ifdef EVP_R_INVALID_PROVIDER_FUNCTIONS + {"INVALID_PROVIDER_FUNCTIONS", ERR_LIB_EVP, EVP_R_INVALID_PROVIDER_FUNCTIONS}, + #else + {"INVALID_PROVIDER_FUNCTIONS", 6, 193}, + #endif + #ifdef EVP_R_INVALID_SALT_LENGTH + {"INVALID_SALT_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_SALT_LENGTH}, + #else + {"INVALID_SALT_LENGTH", 6, 186}, + #endif + #ifdef EVP_R_INVALID_SECRET_LENGTH + {"INVALID_SECRET_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_SECRET_LENGTH}, + #else + {"INVALID_SECRET_LENGTH", 6, 223}, + #endif + #ifdef EVP_R_INVALID_SEED_LENGTH + {"INVALID_SEED_LENGTH", ERR_LIB_EVP, EVP_R_INVALID_SEED_LENGTH}, + #else + {"INVALID_SEED_LENGTH", 6, 220}, + #endif + #ifdef EVP_R_INVALID_VALUE + {"INVALID_VALUE", ERR_LIB_EVP, EVP_R_INVALID_VALUE}, + #else + {"INVALID_VALUE", 6, 222}, + #endif + #ifdef EVP_R_KEYMGMT_EXPORT_FAILURE + {"KEYMGMT_EXPORT_FAILURE", ERR_LIB_EVP, EVP_R_KEYMGMT_EXPORT_FAILURE}, + #else + {"KEYMGMT_EXPORT_FAILURE", 6, 205}, + #endif + #ifdef EVP_R_KEY_SETUP_FAILED + {"KEY_SETUP_FAILED", ERR_LIB_EVP, EVP_R_KEY_SETUP_FAILED}, + #else + {"KEY_SETUP_FAILED", 6, 180}, + #endif + #ifdef EVP_R_LOCKING_NOT_SUPPORTED + {"LOCKING_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_LOCKING_NOT_SUPPORTED}, + #else + {"LOCKING_NOT_SUPPORTED", 6, 213}, + #endif + #ifdef EVP_R_MEMORY_LIMIT_EXCEEDED + {"MEMORY_LIMIT_EXCEEDED", ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED}, + #else + {"MEMORY_LIMIT_EXCEEDED", 6, 172}, + #endif + #ifdef EVP_R_MESSAGE_DIGEST_IS_NULL + {"MESSAGE_DIGEST_IS_NULL", ERR_LIB_EVP, EVP_R_MESSAGE_DIGEST_IS_NULL}, + #else + {"MESSAGE_DIGEST_IS_NULL", 6, 159}, + #endif + #ifdef EVP_R_METHOD_NOT_SUPPORTED + {"METHOD_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_METHOD_NOT_SUPPORTED}, + #else + {"METHOD_NOT_SUPPORTED", 6, 144}, + #endif + #ifdef EVP_R_MISSING_PARAMETERS + {"MISSING_PARAMETERS", ERR_LIB_EVP, EVP_R_MISSING_PARAMETERS}, + #else + {"MISSING_PARAMETERS", 6, 103}, + #endif + #ifdef EVP_R_NOT_ABLE_TO_COPY_CTX + {"NOT_ABLE_TO_COPY_CTX", ERR_LIB_EVP, EVP_R_NOT_ABLE_TO_COPY_CTX}, + #else + {"NOT_ABLE_TO_COPY_CTX", 6, 190}, + #endif + #ifdef EVP_R_NOT_XOF_OR_INVALID_LENGTH + {"NOT_XOF_OR_INVALID_LENGTH", ERR_LIB_EVP, EVP_R_NOT_XOF_OR_INVALID_LENGTH}, + #else + {"NOT_XOF_OR_INVALID_LENGTH", 6, 178}, + #endif + #ifdef EVP_R_NO_CIPHER_SET + {"NO_CIPHER_SET", ERR_LIB_EVP, EVP_R_NO_CIPHER_SET}, + #else + {"NO_CIPHER_SET", 6, 131}, + #endif + #ifdef EVP_R_NO_DEFAULT_DIGEST + {"NO_DEFAULT_DIGEST", ERR_LIB_EVP, EVP_R_NO_DEFAULT_DIGEST}, + #else + {"NO_DEFAULT_DIGEST", 6, 158}, + #endif + #ifdef EVP_R_NO_DIGEST_SET + {"NO_DIGEST_SET", ERR_LIB_EVP, EVP_R_NO_DIGEST_SET}, + #else + {"NO_DIGEST_SET", 6, 139}, + #endif + #ifdef EVP_R_NO_IMPORT_FUNCTION + {"NO_IMPORT_FUNCTION", ERR_LIB_EVP, EVP_R_NO_IMPORT_FUNCTION}, + #else + {"NO_IMPORT_FUNCTION", 6, 206}, + #endif + #ifdef EVP_R_NO_KEYMGMT_AVAILABLE + {"NO_KEYMGMT_AVAILABLE", ERR_LIB_EVP, EVP_R_NO_KEYMGMT_AVAILABLE}, + #else + {"NO_KEYMGMT_AVAILABLE", 6, 199}, + #endif + #ifdef EVP_R_NO_KEYMGMT_PRESENT + {"NO_KEYMGMT_PRESENT", ERR_LIB_EVP, EVP_R_NO_KEYMGMT_PRESENT}, + #else + {"NO_KEYMGMT_PRESENT", 6, 196}, + #endif + #ifdef EVP_R_NO_KEY_SET + {"NO_KEY_SET", ERR_LIB_EVP, EVP_R_NO_KEY_SET}, + #else + {"NO_KEY_SET", 6, 154}, + #endif + #ifdef EVP_R_NO_OPERATION_SET + {"NO_OPERATION_SET", ERR_LIB_EVP, EVP_R_NO_OPERATION_SET}, + #else + {"NO_OPERATION_SET", 6, 149}, + #endif + #ifdef EVP_R_NULL_MAC_PKEY_CTX + {"NULL_MAC_PKEY_CTX", ERR_LIB_EVP, EVP_R_NULL_MAC_PKEY_CTX}, + #else + {"NULL_MAC_PKEY_CTX", 6, 208}, + #endif + #ifdef EVP_R_ONLY_ONESHOT_SUPPORTED + {"ONLY_ONESHOT_SUPPORTED", ERR_LIB_EVP, EVP_R_ONLY_ONESHOT_SUPPORTED}, + #else + {"ONLY_ONESHOT_SUPPORTED", 6, 177}, + #endif + #ifdef EVP_R_OPERATION_NOT_INITIALIZED + {"OPERATION_NOT_INITIALIZED", ERR_LIB_EVP, EVP_R_OPERATION_NOT_INITIALIZED}, + #else + {"OPERATION_NOT_INITIALIZED", 6, 151}, + #endif + #ifdef EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE}, + #else + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", 6, 150}, + #endif + #ifdef EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE + {"OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE", ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE}, + #else + {"OPERATION_NOT_SUPPORTED_FOR_THIS_SIGNATURE_TYPE", 6, 226}, + #endif + #ifdef EVP_R_OUTPUT_WOULD_OVERFLOW + {"OUTPUT_WOULD_OVERFLOW", ERR_LIB_EVP, EVP_R_OUTPUT_WOULD_OVERFLOW}, + #else + {"OUTPUT_WOULD_OVERFLOW", 6, 202}, + #endif + #ifdef EVP_R_PARAMETER_TOO_LARGE + {"PARAMETER_TOO_LARGE", ERR_LIB_EVP, EVP_R_PARAMETER_TOO_LARGE}, + #else + {"PARAMETER_TOO_LARGE", 6, 187}, + #endif + #ifdef EVP_R_PARTIALLY_OVERLAPPING + {"PARTIALLY_OVERLAPPING", ERR_LIB_EVP, EVP_R_PARTIALLY_OVERLAPPING}, + #else + {"PARTIALLY_OVERLAPPING", 6, 162}, + #endif + #ifdef EVP_R_PBKDF2_ERROR + {"PBKDF2_ERROR", ERR_LIB_EVP, EVP_R_PBKDF2_ERROR}, + #else + {"PBKDF2_ERROR", 6, 181}, + #endif + #ifdef EVP_R_PIPELINE_NOT_SUPPORTED + {"PIPELINE_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_PIPELINE_NOT_SUPPORTED}, + #else + {"PIPELINE_NOT_SUPPORTED", 6, 230}, + #endif + #ifdef EVP_R_PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED + {"PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED", ERR_LIB_EVP, EVP_R_PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED}, + #else + {"PKEY_APPLICATION_ASN1_METHOD_ALREADY_REGISTERED", 6, 179}, + #endif + #ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR + {"PRIVATE_KEY_DECODE_ERROR", ERR_LIB_EVP, EVP_R_PRIVATE_KEY_DECODE_ERROR}, + #else + {"PRIVATE_KEY_DECODE_ERROR", 6, 145}, + #endif + #ifdef EVP_R_PRIVATE_KEY_ENCODE_ERROR + {"PRIVATE_KEY_ENCODE_ERROR", ERR_LIB_EVP, EVP_R_PRIVATE_KEY_ENCODE_ERROR}, + #else + {"PRIVATE_KEY_ENCODE_ERROR", 6, 146}, + #endif + #ifdef EVP_R_PROVIDER_ASYM_CIPHER_FAILURE + {"PROVIDER_ASYM_CIPHER_FAILURE", ERR_LIB_EVP, EVP_R_PROVIDER_ASYM_CIPHER_FAILURE}, + #else + {"PROVIDER_ASYM_CIPHER_FAILURE", 6, 232}, + #endif + #ifdef EVP_R_PROVIDER_ASYM_CIPHER_NOT_SUPPORTED + {"PROVIDER_ASYM_CIPHER_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_PROVIDER_ASYM_CIPHER_NOT_SUPPORTED}, + #else + {"PROVIDER_ASYM_CIPHER_NOT_SUPPORTED", 6, 235}, + #endif + #ifdef EVP_R_PROVIDER_GET_CTX_PARAMS_NOT_SUPPORTED + {"PROVIDER_GET_CTX_PARAMS_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_PROVIDER_GET_CTX_PARAMS_NOT_SUPPORTED}, + #else + {"PROVIDER_GET_CTX_PARAMS_NOT_SUPPORTED", 6, 238}, + #endif + #ifdef EVP_R_PROVIDER_KEYMGMT_FAILURE + {"PROVIDER_KEYMGMT_FAILURE", ERR_LIB_EVP, EVP_R_PROVIDER_KEYMGMT_FAILURE}, + #else + {"PROVIDER_KEYMGMT_FAILURE", 6, 233}, + #endif + #ifdef EVP_R_PROVIDER_KEYMGMT_NOT_SUPPORTED + {"PROVIDER_KEYMGMT_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_PROVIDER_KEYMGMT_NOT_SUPPORTED}, + #else + {"PROVIDER_KEYMGMT_NOT_SUPPORTED", 6, 236}, + #endif + #ifdef EVP_R_PROVIDER_SIGNATURE_FAILURE + {"PROVIDER_SIGNATURE_FAILURE", ERR_LIB_EVP, EVP_R_PROVIDER_SIGNATURE_FAILURE}, + #else + {"PROVIDER_SIGNATURE_FAILURE", 6, 234}, + #endif + #ifdef EVP_R_PROVIDER_SIGNATURE_NOT_SUPPORTED + {"PROVIDER_SIGNATURE_NOT_SUPPORTED", ERR_LIB_EVP, EVP_R_PROVIDER_SIGNATURE_NOT_SUPPORTED}, + #else + {"PROVIDER_SIGNATURE_NOT_SUPPORTED", 6, 237}, + #endif + #ifdef EVP_R_PUBLIC_KEY_NOT_RSA + {"PUBLIC_KEY_NOT_RSA", ERR_LIB_EVP, EVP_R_PUBLIC_KEY_NOT_RSA}, + #else + {"PUBLIC_KEY_NOT_RSA", 6, 106}, + #endif + #ifdef EVP_R_SETTING_XOF_FAILED + {"SETTING_XOF_FAILED", ERR_LIB_EVP, EVP_R_SETTING_XOF_FAILED}, + #else + {"SETTING_XOF_FAILED", 6, 227}, + #endif + #ifdef EVP_R_SET_DEFAULT_PROPERTY_FAILURE + {"SET_DEFAULT_PROPERTY_FAILURE", ERR_LIB_EVP, EVP_R_SET_DEFAULT_PROPERTY_FAILURE}, + #else + {"SET_DEFAULT_PROPERTY_FAILURE", 6, 209}, + #endif + #ifdef EVP_R_SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE + {"SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE", ERR_LIB_EVP, EVP_R_SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE}, + #else + {"SIGNATURE_TYPE_AND_KEY_TYPE_INCOMPATIBLE", 6, 228}, + #endif + #ifdef EVP_R_TOO_MANY_PIPES + {"TOO_MANY_PIPES", ERR_LIB_EVP, EVP_R_TOO_MANY_PIPES}, + #else + {"TOO_MANY_PIPES", 6, 231}, + #endif + #ifdef EVP_R_TOO_MANY_RECORDS + {"TOO_MANY_RECORDS", ERR_LIB_EVP, EVP_R_TOO_MANY_RECORDS}, + #else + {"TOO_MANY_RECORDS", 6, 183}, + #endif + #ifdef EVP_R_UNABLE_TO_ENABLE_LOCKING + {"UNABLE_TO_ENABLE_LOCKING", ERR_LIB_EVP, EVP_R_UNABLE_TO_ENABLE_LOCKING}, + #else + {"UNABLE_TO_ENABLE_LOCKING", 6, 212}, + #endif + #ifdef EVP_R_UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE + {"UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE", ERR_LIB_EVP, EVP_R_UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE}, + #else + {"UNABLE_TO_GET_MAXIMUM_REQUEST_SIZE", 6, 215}, + #endif + #ifdef EVP_R_UNABLE_TO_GET_RANDOM_STRENGTH + {"UNABLE_TO_GET_RANDOM_STRENGTH", ERR_LIB_EVP, EVP_R_UNABLE_TO_GET_RANDOM_STRENGTH}, + #else + {"UNABLE_TO_GET_RANDOM_STRENGTH", 6, 216}, + #endif + #ifdef EVP_R_UNABLE_TO_LOCK_CONTEXT + {"UNABLE_TO_LOCK_CONTEXT", ERR_LIB_EVP, EVP_R_UNABLE_TO_LOCK_CONTEXT}, + #else + {"UNABLE_TO_LOCK_CONTEXT", 6, 211}, + #endif + #ifdef EVP_R_UNABLE_TO_SET_CALLBACKS + {"UNABLE_TO_SET_CALLBACKS", ERR_LIB_EVP, EVP_R_UNABLE_TO_SET_CALLBACKS}, + #else + {"UNABLE_TO_SET_CALLBACKS", 6, 217}, + #endif + #ifdef EVP_R_UNKNOWN_BITS + {"UNKNOWN_BITS", ERR_LIB_EVP, EVP_R_UNKNOWN_BITS}, + #else + {"UNKNOWN_BITS", 6, 166}, + #endif + #ifdef EVP_R_UNKNOWN_CIPHER + {"UNKNOWN_CIPHER", ERR_LIB_EVP, EVP_R_UNKNOWN_CIPHER}, + #else + {"UNKNOWN_CIPHER", 6, 160}, + #endif + #ifdef EVP_R_UNKNOWN_DIGEST + {"UNKNOWN_DIGEST", ERR_LIB_EVP, EVP_R_UNKNOWN_DIGEST}, + #else + {"UNKNOWN_DIGEST", 6, 161}, + #endif + #ifdef EVP_R_UNKNOWN_KEY_TYPE + {"UNKNOWN_KEY_TYPE", ERR_LIB_EVP, EVP_R_UNKNOWN_KEY_TYPE}, + #else + {"UNKNOWN_KEY_TYPE", 6, 207}, + #endif + #ifdef EVP_R_UNKNOWN_MAX_SIZE + {"UNKNOWN_MAX_SIZE", ERR_LIB_EVP, EVP_R_UNKNOWN_MAX_SIZE}, + #else + {"UNKNOWN_MAX_SIZE", 6, 167}, + #endif + #ifdef EVP_R_UNKNOWN_OPTION + {"UNKNOWN_OPTION", ERR_LIB_EVP, EVP_R_UNKNOWN_OPTION}, + #else + {"UNKNOWN_OPTION", 6, 169}, + #endif + #ifdef EVP_R_UNKNOWN_PBE_ALGORITHM + {"UNKNOWN_PBE_ALGORITHM", ERR_LIB_EVP, EVP_R_UNKNOWN_PBE_ALGORITHM}, + #else + {"UNKNOWN_PBE_ALGORITHM", 6, 121}, + #endif + #ifdef EVP_R_UNKNOWN_SECURITY_BITS + {"UNKNOWN_SECURITY_BITS", ERR_LIB_EVP, EVP_R_UNKNOWN_SECURITY_BITS}, + #else + {"UNKNOWN_SECURITY_BITS", 6, 168}, + #endif + #ifdef EVP_R_UNSUPPORTED_ALGORITHM + {"UNSUPPORTED_ALGORITHM", ERR_LIB_EVP, EVP_R_UNSUPPORTED_ALGORITHM}, + #else + {"UNSUPPORTED_ALGORITHM", 6, 156}, + #endif + #ifdef EVP_R_UNSUPPORTED_CIPHER + {"UNSUPPORTED_CIPHER", ERR_LIB_EVP, EVP_R_UNSUPPORTED_CIPHER}, + #else + {"UNSUPPORTED_CIPHER", 6, 107}, + #endif + #ifdef EVP_R_UNSUPPORTED_KEYLENGTH + {"UNSUPPORTED_KEYLENGTH", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEYLENGTH}, + #else + {"UNSUPPORTED_KEYLENGTH", 6, 123}, + #endif + #ifdef EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION + {"UNSUPPORTED_KEY_DERIVATION_FUNCTION", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION}, + #else + {"UNSUPPORTED_KEY_DERIVATION_FUNCTION", 6, 124}, + #endif + #ifdef EVP_R_UNSUPPORTED_KEY_SIZE + {"UNSUPPORTED_KEY_SIZE", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_SIZE}, + #else + {"UNSUPPORTED_KEY_SIZE", 6, 108}, + #endif + #ifdef EVP_R_UNSUPPORTED_KEY_TYPE + {"UNSUPPORTED_KEY_TYPE", ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_TYPE}, + #else + {"UNSUPPORTED_KEY_TYPE", 6, 224}, + #endif + #ifdef EVP_R_UNSUPPORTED_NUMBER_OF_ROUNDS + {"UNSUPPORTED_NUMBER_OF_ROUNDS", ERR_LIB_EVP, EVP_R_UNSUPPORTED_NUMBER_OF_ROUNDS}, + #else + {"UNSUPPORTED_NUMBER_OF_ROUNDS", 6, 135}, + #endif + #ifdef EVP_R_UNSUPPORTED_PRF + {"UNSUPPORTED_PRF", ERR_LIB_EVP, EVP_R_UNSUPPORTED_PRF}, + #else + {"UNSUPPORTED_PRF", 6, 125}, + #endif + #ifdef EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM + {"UNSUPPORTED_PRIVATE_KEY_ALGORITHM", ERR_LIB_EVP, EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM}, + #else + {"UNSUPPORTED_PRIVATE_KEY_ALGORITHM", 6, 118}, + #endif + #ifdef EVP_R_UNSUPPORTED_SALT_TYPE + {"UNSUPPORTED_SALT_TYPE", ERR_LIB_EVP, EVP_R_UNSUPPORTED_SALT_TYPE}, + #else + {"UNSUPPORTED_SALT_TYPE", 6, 126}, + #endif + #ifdef EVP_R_UPDATE_ERROR + {"UPDATE_ERROR", ERR_LIB_EVP, EVP_R_UPDATE_ERROR}, + #else + {"UPDATE_ERROR", 6, 189}, + #endif + #ifdef EVP_R_WRAP_MODE_NOT_ALLOWED + {"WRAP_MODE_NOT_ALLOWED", ERR_LIB_EVP, EVP_R_WRAP_MODE_NOT_ALLOWED}, + #else + {"WRAP_MODE_NOT_ALLOWED", 6, 170}, + #endif + #ifdef EVP_R_WRONG_FINAL_BLOCK_LENGTH + {"WRONG_FINAL_BLOCK_LENGTH", ERR_LIB_EVP, EVP_R_WRONG_FINAL_BLOCK_LENGTH}, + #else + {"WRONG_FINAL_BLOCK_LENGTH", 6, 109}, + #endif + #ifdef EVP_R_XTS_DATA_UNIT_IS_TOO_LARGE + {"XTS_DATA_UNIT_IS_TOO_LARGE", ERR_LIB_EVP, EVP_R_XTS_DATA_UNIT_IS_TOO_LARGE}, + #else + {"XTS_DATA_UNIT_IS_TOO_LARGE", 6, 191}, + #endif + #ifdef EVP_R_XTS_DUPLICATED_KEYS + {"XTS_DUPLICATED_KEYS", ERR_LIB_EVP, EVP_R_XTS_DUPLICATED_KEYS}, + #else + {"XTS_DUPLICATED_KEYS", 6, 192}, + #endif + #ifdef HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN + {"ASN1_LEN_EXCEEDS_MAX_RESP_LEN", ERR_LIB_HTTP, HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN}, + #else + {"ASN1_LEN_EXCEEDS_MAX_RESP_LEN", 61, 108}, + #endif + #ifdef HTTP_R_CONNECT_FAILURE + {"CONNECT_FAILURE", ERR_LIB_HTTP, HTTP_R_CONNECT_FAILURE}, + #else + {"CONNECT_FAILURE", 61, 100}, + #endif + #ifdef HTTP_R_CONTENT_TYPE_MISMATCH + {"CONTENT_TYPE_MISMATCH", ERR_LIB_HTTP, HTTP_R_CONTENT_TYPE_MISMATCH}, + #else + {"CONTENT_TYPE_MISMATCH", 61, 131}, + #endif + #ifdef HTTP_R_ERROR_PARSING_ASN1_LENGTH + {"ERROR_PARSING_ASN1_LENGTH", ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_ASN1_LENGTH}, + #else + {"ERROR_PARSING_ASN1_LENGTH", 61, 109}, + #endif + #ifdef HTTP_R_ERROR_PARSING_CONTENT_LENGTH + {"ERROR_PARSING_CONTENT_LENGTH", ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_CONTENT_LENGTH}, + #else + {"ERROR_PARSING_CONTENT_LENGTH", 61, 119}, + #endif + #ifdef HTTP_R_ERROR_PARSING_URL + {"ERROR_PARSING_URL", ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_URL}, + #else + {"ERROR_PARSING_URL", 61, 101}, + #endif + #ifdef HTTP_R_ERROR_RECEIVING + {"ERROR_RECEIVING", ERR_LIB_HTTP, HTTP_R_ERROR_RECEIVING}, + #else + {"ERROR_RECEIVING", 61, 103}, + #endif + #ifdef HTTP_R_ERROR_SENDING + {"ERROR_SENDING", ERR_LIB_HTTP, HTTP_R_ERROR_SENDING}, + #else + {"ERROR_SENDING", 61, 102}, + #endif + #ifdef HTTP_R_FAILED_READING_DATA + {"FAILED_READING_DATA", ERR_LIB_HTTP, HTTP_R_FAILED_READING_DATA}, + #else + {"FAILED_READING_DATA", 61, 128}, + #endif + #ifdef HTTP_R_HEADER_PARSE_ERROR + {"HEADER_PARSE_ERROR", ERR_LIB_HTTP, HTTP_R_HEADER_PARSE_ERROR}, + #else + {"HEADER_PARSE_ERROR", 61, 126}, + #endif + #ifdef HTTP_R_INCONSISTENT_CONTENT_LENGTH + {"INCONSISTENT_CONTENT_LENGTH", ERR_LIB_HTTP, HTTP_R_INCONSISTENT_CONTENT_LENGTH}, + #else + {"INCONSISTENT_CONTENT_LENGTH", 61, 120}, + #endif + #ifdef HTTP_R_INVALID_PORT_NUMBER + {"INVALID_PORT_NUMBER", ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER}, + #else + {"INVALID_PORT_NUMBER", 61, 123}, + #endif + #ifdef HTTP_R_INVALID_URL_PATH + {"INVALID_URL_PATH", ERR_LIB_HTTP, HTTP_R_INVALID_URL_PATH}, + #else + {"INVALID_URL_PATH", 61, 125}, + #endif + #ifdef HTTP_R_INVALID_URL_SCHEME + {"INVALID_URL_SCHEME", ERR_LIB_HTTP, HTTP_R_INVALID_URL_SCHEME}, + #else + {"INVALID_URL_SCHEME", 61, 124}, + #endif + #ifdef HTTP_R_MAX_RESP_LEN_EXCEEDED + {"MAX_RESP_LEN_EXCEEDED", ERR_LIB_HTTP, HTTP_R_MAX_RESP_LEN_EXCEEDED}, + #else + {"MAX_RESP_LEN_EXCEEDED", 61, 117}, + #endif + #ifdef HTTP_R_MISSING_ASN1_ENCODING + {"MISSING_ASN1_ENCODING", ERR_LIB_HTTP, HTTP_R_MISSING_ASN1_ENCODING}, + #else + {"MISSING_ASN1_ENCODING", 61, 110}, + #endif + #ifdef HTTP_R_MISSING_CONTENT_TYPE + {"MISSING_CONTENT_TYPE", ERR_LIB_HTTP, HTTP_R_MISSING_CONTENT_TYPE}, + #else + {"MISSING_CONTENT_TYPE", 61, 121}, + #endif + #ifdef HTTP_R_MISSING_REDIRECT_LOCATION + {"MISSING_REDIRECT_LOCATION", ERR_LIB_HTTP, HTTP_R_MISSING_REDIRECT_LOCATION}, + #else + {"MISSING_REDIRECT_LOCATION", 61, 111}, + #endif + #ifdef HTTP_R_RECEIVED_ERROR + {"RECEIVED_ERROR", ERR_LIB_HTTP, HTTP_R_RECEIVED_ERROR}, + #else + {"RECEIVED_ERROR", 61, 105}, + #endif + #ifdef HTTP_R_RECEIVED_WRONG_HTTP_VERSION + {"RECEIVED_WRONG_HTTP_VERSION", ERR_LIB_HTTP, HTTP_R_RECEIVED_WRONG_HTTP_VERSION}, + #else + {"RECEIVED_WRONG_HTTP_VERSION", 61, 106}, + #endif + #ifdef HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP + {"REDIRECTION_FROM_HTTPS_TO_HTTP", ERR_LIB_HTTP, HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP}, + #else + {"REDIRECTION_FROM_HTTPS_TO_HTTP", 61, 112}, + #endif + #ifdef HTTP_R_REDIRECTION_NOT_ENABLED + {"REDIRECTION_NOT_ENABLED", ERR_LIB_HTTP, HTTP_R_REDIRECTION_NOT_ENABLED}, + #else + {"REDIRECTION_NOT_ENABLED", 61, 116}, + #endif + #ifdef HTTP_R_RESPONSE_LINE_TOO_LONG + {"RESPONSE_LINE_TOO_LONG", ERR_LIB_HTTP, HTTP_R_RESPONSE_LINE_TOO_LONG}, + #else + {"RESPONSE_LINE_TOO_LONG", 61, 113}, + #endif + #ifdef HTTP_R_RESPONSE_PARSE_ERROR + {"RESPONSE_PARSE_ERROR", ERR_LIB_HTTP, HTTP_R_RESPONSE_PARSE_ERROR}, + #else + {"RESPONSE_PARSE_ERROR", 61, 104}, + #endif + #ifdef HTTP_R_RESPONSE_TOO_MANY_HDRLINES + {"RESPONSE_TOO_MANY_HDRLINES", ERR_LIB_HTTP, HTTP_R_RESPONSE_TOO_MANY_HDRLINES}, + #else + {"RESPONSE_TOO_MANY_HDRLINES", 61, 130}, + #endif + #ifdef HTTP_R_RETRY_TIMEOUT + {"RETRY_TIMEOUT", ERR_LIB_HTTP, HTTP_R_RETRY_TIMEOUT}, + #else + {"RETRY_TIMEOUT", 61, 129}, + #endif + #ifdef HTTP_R_SERVER_CANCELED_CONNECTION + {"SERVER_CANCELED_CONNECTION", ERR_LIB_HTTP, HTTP_R_SERVER_CANCELED_CONNECTION}, + #else + {"SERVER_CANCELED_CONNECTION", 61, 127}, + #endif + #ifdef HTTP_R_SOCK_NOT_SUPPORTED + {"SOCK_NOT_SUPPORTED", ERR_LIB_HTTP, HTTP_R_SOCK_NOT_SUPPORTED}, + #else + {"SOCK_NOT_SUPPORTED", 61, 122}, + #endif + #ifdef HTTP_R_STATUS_CODE_UNSUPPORTED + {"STATUS_CODE_UNSUPPORTED", ERR_LIB_HTTP, HTTP_R_STATUS_CODE_UNSUPPORTED}, + #else + {"STATUS_CODE_UNSUPPORTED", 61, 114}, + #endif + #ifdef HTTP_R_TLS_NOT_ENABLED + {"TLS_NOT_ENABLED", ERR_LIB_HTTP, HTTP_R_TLS_NOT_ENABLED}, + #else + {"TLS_NOT_ENABLED", 61, 107}, + #endif + #ifdef HTTP_R_TOO_MANY_REDIRECTIONS + {"TOO_MANY_REDIRECTIONS", ERR_LIB_HTTP, HTTP_R_TOO_MANY_REDIRECTIONS}, + #else + {"TOO_MANY_REDIRECTIONS", 61, 115}, + #endif + #ifdef HTTP_R_UNEXPECTED_CONTENT_TYPE + {"UNEXPECTED_CONTENT_TYPE", ERR_LIB_HTTP, HTTP_R_UNEXPECTED_CONTENT_TYPE}, + #else + {"UNEXPECTED_CONTENT_TYPE", 61, 118}, + #endif + #ifdef OBJ_R_OID_EXISTS + {"OID_EXISTS", ERR_LIB_OBJ, OBJ_R_OID_EXISTS}, + #else + {"OID_EXISTS", 8, 102}, + #endif + #ifdef OBJ_R_UNKNOWN_NID + {"UNKNOWN_NID", ERR_LIB_OBJ, OBJ_R_UNKNOWN_NID}, + #else + {"UNKNOWN_NID", 8, 101}, + #endif + #ifdef OBJ_R_UNKNOWN_OBJECT_NAME + {"UNKNOWN_OBJECT_NAME", ERR_LIB_OBJ, OBJ_R_UNKNOWN_OBJECT_NAME}, + #else + {"UNKNOWN_OBJECT_NAME", 8, 103}, + #endif + #ifdef OCSP_R_CERTIFICATE_VERIFY_ERROR + {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_OCSP, OCSP_R_CERTIFICATE_VERIFY_ERROR}, + #else + {"CERTIFICATE_VERIFY_ERROR", 39, 101}, + #endif + #ifdef OCSP_R_DIGEST_ERR + {"DIGEST_ERR", ERR_LIB_OCSP, OCSP_R_DIGEST_ERR}, + #else + {"DIGEST_ERR", 39, 102}, + #endif + #ifdef OCSP_R_DIGEST_NAME_ERR + {"DIGEST_NAME_ERR", ERR_LIB_OCSP, OCSP_R_DIGEST_NAME_ERR}, + #else + {"DIGEST_NAME_ERR", 39, 106}, + #endif + #ifdef OCSP_R_DIGEST_SIZE_ERR + {"DIGEST_SIZE_ERR", ERR_LIB_OCSP, OCSP_R_DIGEST_SIZE_ERR}, + #else + {"DIGEST_SIZE_ERR", 39, 107}, + #endif + #ifdef OCSP_R_ERROR_IN_NEXTUPDATE_FIELD + {"ERROR_IN_NEXTUPDATE_FIELD", ERR_LIB_OCSP, OCSP_R_ERROR_IN_NEXTUPDATE_FIELD}, + #else + {"ERROR_IN_NEXTUPDATE_FIELD", 39, 122}, + #endif + #ifdef OCSP_R_ERROR_IN_THISUPDATE_FIELD + {"ERROR_IN_THISUPDATE_FIELD", ERR_LIB_OCSP, OCSP_R_ERROR_IN_THISUPDATE_FIELD}, + #else + {"ERROR_IN_THISUPDATE_FIELD", 39, 123}, + #endif + #ifdef OCSP_R_MISSING_OCSPSIGNING_USAGE + {"MISSING_OCSPSIGNING_USAGE", ERR_LIB_OCSP, OCSP_R_MISSING_OCSPSIGNING_USAGE}, + #else + {"MISSING_OCSPSIGNING_USAGE", 39, 103}, + #endif + #ifdef OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE + {"NEXTUPDATE_BEFORE_THISUPDATE", ERR_LIB_OCSP, OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE}, + #else + {"NEXTUPDATE_BEFORE_THISUPDATE", 39, 124}, + #endif + #ifdef OCSP_R_NOT_BASIC_RESPONSE + {"NOT_BASIC_RESPONSE", ERR_LIB_OCSP, OCSP_R_NOT_BASIC_RESPONSE}, + #else + {"NOT_BASIC_RESPONSE", 39, 104}, + #endif + #ifdef OCSP_R_NO_CERTIFICATES_IN_CHAIN + {"NO_CERTIFICATES_IN_CHAIN", ERR_LIB_OCSP, OCSP_R_NO_CERTIFICATES_IN_CHAIN}, + #else + {"NO_CERTIFICATES_IN_CHAIN", 39, 105}, + #endif + #ifdef OCSP_R_NO_RESPONSE_DATA + {"NO_RESPONSE_DATA", ERR_LIB_OCSP, OCSP_R_NO_RESPONSE_DATA}, + #else + {"NO_RESPONSE_DATA", 39, 108}, + #endif + #ifdef OCSP_R_NO_REVOKED_TIME + {"NO_REVOKED_TIME", ERR_LIB_OCSP, OCSP_R_NO_REVOKED_TIME}, + #else + {"NO_REVOKED_TIME", 39, 109}, + #endif + #ifdef OCSP_R_NO_SIGNER_KEY + {"NO_SIGNER_KEY", ERR_LIB_OCSP, OCSP_R_NO_SIGNER_KEY}, + #else + {"NO_SIGNER_KEY", 39, 130}, + #endif + #ifdef OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_OCSP, OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, + #else + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 39, 110}, + #endif + #ifdef OCSP_R_REQUEST_NOT_SIGNED + {"REQUEST_NOT_SIGNED", ERR_LIB_OCSP, OCSP_R_REQUEST_NOT_SIGNED}, + #else + {"REQUEST_NOT_SIGNED", 39, 128}, + #endif + #ifdef OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA + {"RESPONSE_CONTAINS_NO_REVOCATION_DATA", ERR_LIB_OCSP, OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA}, + #else + {"RESPONSE_CONTAINS_NO_REVOCATION_DATA", 39, 111}, + #endif + #ifdef OCSP_R_ROOT_CA_NOT_TRUSTED + {"ROOT_CA_NOT_TRUSTED", ERR_LIB_OCSP, OCSP_R_ROOT_CA_NOT_TRUSTED}, + #else + {"ROOT_CA_NOT_TRUSTED", 39, 112}, + #endif + #ifdef OCSP_R_SIGNATURE_FAILURE + {"SIGNATURE_FAILURE", ERR_LIB_OCSP, OCSP_R_SIGNATURE_FAILURE}, + #else + {"SIGNATURE_FAILURE", 39, 117}, + #endif + #ifdef OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND + {"SIGNER_CERTIFICATE_NOT_FOUND", ERR_LIB_OCSP, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND}, + #else + {"SIGNER_CERTIFICATE_NOT_FOUND", 39, 118}, + #endif + #ifdef OCSP_R_STATUS_EXPIRED + {"STATUS_EXPIRED", ERR_LIB_OCSP, OCSP_R_STATUS_EXPIRED}, + #else + {"STATUS_EXPIRED", 39, 125}, + #endif + #ifdef OCSP_R_STATUS_NOT_YET_VALID + {"STATUS_NOT_YET_VALID", ERR_LIB_OCSP, OCSP_R_STATUS_NOT_YET_VALID}, + #else + {"STATUS_NOT_YET_VALID", 39, 126}, + #endif + #ifdef OCSP_R_STATUS_TOO_OLD + {"STATUS_TOO_OLD", ERR_LIB_OCSP, OCSP_R_STATUS_TOO_OLD}, + #else + {"STATUS_TOO_OLD", 39, 127}, + #endif + #ifdef OCSP_R_UNKNOWN_MESSAGE_DIGEST + {"UNKNOWN_MESSAGE_DIGEST", ERR_LIB_OCSP, OCSP_R_UNKNOWN_MESSAGE_DIGEST}, + #else + {"UNKNOWN_MESSAGE_DIGEST", 39, 119}, + #endif + #ifdef OCSP_R_UNKNOWN_NID + {"UNKNOWN_NID", ERR_LIB_OCSP, OCSP_R_UNKNOWN_NID}, + #else + {"UNKNOWN_NID", 39, 120}, + #endif + #ifdef OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE + {"UNSUPPORTED_REQUESTORNAME_TYPE", ERR_LIB_OCSP, OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE}, + #else + {"UNSUPPORTED_REQUESTORNAME_TYPE", 39, 129}, + #endif + #ifdef OSSL_DECODER_R_COULD_NOT_DECODE_OBJECT + {"COULD_NOT_DECODE_OBJECT", ERR_LIB_OSSL_DECODER, OSSL_DECODER_R_COULD_NOT_DECODE_OBJECT}, + #else + {"COULD_NOT_DECODE_OBJECT", 60, 101}, + #endif + #ifdef OSSL_DECODER_R_DECODER_NOT_FOUND + {"DECODER_NOT_FOUND", ERR_LIB_OSSL_DECODER, OSSL_DECODER_R_DECODER_NOT_FOUND}, + #else + {"DECODER_NOT_FOUND", 60, 102}, + #endif + #ifdef OSSL_DECODER_R_MISSING_GET_PARAMS + {"MISSING_GET_PARAMS", ERR_LIB_OSSL_DECODER, OSSL_DECODER_R_MISSING_GET_PARAMS}, + #else + {"MISSING_GET_PARAMS", 60, 100}, + #endif + #ifdef OSSL_ENCODER_R_BAD_PARAMETER_VALUE + {"BAD_PARAMETER_VALUE", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_BAD_PARAMETER_VALUE}, + #else + {"BAD_PARAMETER_VALUE", 59, 103}, + #endif + #ifdef OSSL_ENCODER_R_ENCODER_NOT_FOUND + {"ENCODER_NOT_FOUND", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_ENCODER_NOT_FOUND}, + #else + {"ENCODER_NOT_FOUND", 59, 101}, + #endif + #ifdef OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY + {"INCORRECT_PROPERTY_QUERY", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY}, + #else + {"INCORRECT_PROPERTY_QUERY", 59, 100}, + #endif + #ifdef OSSL_ENCODER_R_MISSING_GET_PARAMS + {"MISSING_GET_PARAMS", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_MISSING_GET_PARAMS}, + #else + {"MISSING_GET_PARAMS", 59, 102}, + #endif + #ifdef OSSL_ENCODER_R_UNKNOWN_PARAMETER_NAME + {"UNKNOWN_PARAMETER_NAME", ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_UNKNOWN_PARAMETER_NAME}, + #else + {"UNKNOWN_PARAMETER_NAME", 59, 104}, + #endif + #ifdef OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE + {"AMBIGUOUS_CONTENT_TYPE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE}, + #else + {"AMBIGUOUS_CONTENT_TYPE", 44, 107}, + #endif + #ifdef OSSL_STORE_R_BAD_PASSWORD_READ + {"BAD_PASSWORD_READ", ERR_LIB_OSSL_STORE, OSSL_STORE_R_BAD_PASSWORD_READ}, + #else + {"BAD_PASSWORD_READ", 44, 115}, + #endif + #ifdef OSSL_STORE_R_ERROR_VERIFYING_PKCS12_MAC + {"ERROR_VERIFYING_PKCS12_MAC", ERR_LIB_OSSL_STORE, OSSL_STORE_R_ERROR_VERIFYING_PKCS12_MAC}, + #else + {"ERROR_VERIFYING_PKCS12_MAC", 44, 113}, + #endif + #ifdef OSSL_STORE_R_FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST + {"FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST", ERR_LIB_OSSL_STORE, OSSL_STORE_R_FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST}, + #else + {"FINGERPRINT_SIZE_DOES_NOT_MATCH_DIGEST", 44, 121}, + #endif + #ifdef OSSL_STORE_R_INVALID_SCHEME + {"INVALID_SCHEME", ERR_LIB_OSSL_STORE, OSSL_STORE_R_INVALID_SCHEME}, + #else + {"INVALID_SCHEME", 44, 106}, + #endif + #ifdef OSSL_STORE_R_IS_NOT_A + {"IS_NOT_A", ERR_LIB_OSSL_STORE, OSSL_STORE_R_IS_NOT_A}, + #else + {"IS_NOT_A", 44, 112}, + #endif + #ifdef OSSL_STORE_R_LOADER_INCOMPLETE + {"LOADER_INCOMPLETE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADER_INCOMPLETE}, + #else + {"LOADER_INCOMPLETE", 44, 116}, + #endif + #ifdef OSSL_STORE_R_LOADING_STARTED + {"LOADING_STARTED", ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADING_STARTED}, + #else + {"LOADING_STARTED", 44, 117}, + #endif + #ifdef OSSL_STORE_R_NOT_A_CERTIFICATE + {"NOT_A_CERTIFICATE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_CERTIFICATE}, + #else + {"NOT_A_CERTIFICATE", 44, 100}, + #endif + #ifdef OSSL_STORE_R_NOT_A_CRL + {"NOT_A_CRL", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_CRL}, + #else + {"NOT_A_CRL", 44, 101}, + #endif + #ifdef OSSL_STORE_R_NOT_A_NAME + {"NOT_A_NAME", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_NAME}, + #else + {"NOT_A_NAME", 44, 103}, + #endif + #ifdef OSSL_STORE_R_NOT_A_PRIVATE_KEY + {"NOT_A_PRIVATE_KEY", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_PRIVATE_KEY}, + #else + {"NOT_A_PRIVATE_KEY", 44, 102}, + #endif + #ifdef OSSL_STORE_R_NOT_A_PUBLIC_KEY + {"NOT_A_PUBLIC_KEY", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_PUBLIC_KEY}, + #else + {"NOT_A_PUBLIC_KEY", 44, 122}, + #endif + #ifdef OSSL_STORE_R_NOT_A_SYMMETRIC_KEY + {"NOT_A_SYMMETRIC_KEY", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_SYMMETRIC_KEY}, + #else + {"NOT_A_SYMMETRIC_KEY", 44, 124}, + #endif + #ifdef OSSL_STORE_R_NOT_PARAMETERS + {"NOT_PARAMETERS", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_PARAMETERS}, + #else + {"NOT_PARAMETERS", 44, 104}, + #endif + #ifdef OSSL_STORE_R_NO_LOADERS_FOUND + {"NO_LOADERS_FOUND", ERR_LIB_OSSL_STORE, OSSL_STORE_R_NO_LOADERS_FOUND}, + #else + {"NO_LOADERS_FOUND", 44, 123}, + #endif + #ifdef OSSL_STORE_R_PASSPHRASE_CALLBACK_ERROR + {"PASSPHRASE_CALLBACK_ERROR", ERR_LIB_OSSL_STORE, OSSL_STORE_R_PASSPHRASE_CALLBACK_ERROR}, + #else + {"PASSPHRASE_CALLBACK_ERROR", 44, 114}, + #endif + #ifdef OSSL_STORE_R_PATH_MUST_BE_ABSOLUTE + {"PATH_MUST_BE_ABSOLUTE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_PATH_MUST_BE_ABSOLUTE}, + #else + {"PATH_MUST_BE_ABSOLUTE", 44, 108}, + #endif + #ifdef OSSL_STORE_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES + {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", ERR_LIB_OSSL_STORE, OSSL_STORE_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES}, + #else + {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", 44, 119}, + #endif + #ifdef OSSL_STORE_R_UI_PROCESS_INTERRUPTED_OR_CANCELLED + {"UI_PROCESS_INTERRUPTED_OR_CANCELLED", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UI_PROCESS_INTERRUPTED_OR_CANCELLED}, + #else + {"UI_PROCESS_INTERRUPTED_OR_CANCELLED", 44, 109}, + #endif + #ifdef OSSL_STORE_R_UNREGISTERED_SCHEME + {"UNREGISTERED_SCHEME", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNREGISTERED_SCHEME}, + #else + {"UNREGISTERED_SCHEME", 44, 105}, + #endif + #ifdef OSSL_STORE_R_UNSUPPORTED_CONTENT_TYPE + {"UNSUPPORTED_CONTENT_TYPE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_CONTENT_TYPE}, + #else + {"UNSUPPORTED_CONTENT_TYPE", 44, 110}, + #endif + #ifdef OSSL_STORE_R_UNSUPPORTED_OPERATION + {"UNSUPPORTED_OPERATION", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_OPERATION}, + #else + {"UNSUPPORTED_OPERATION", 44, 118}, + #endif + #ifdef OSSL_STORE_R_UNSUPPORTED_SEARCH_TYPE + {"UNSUPPORTED_SEARCH_TYPE", ERR_LIB_OSSL_STORE, OSSL_STORE_R_UNSUPPORTED_SEARCH_TYPE}, + #else + {"UNSUPPORTED_SEARCH_TYPE", 44, 120}, + #endif + #ifdef OSSL_STORE_R_URI_AUTHORITY_UNSUPPORTED + {"URI_AUTHORITY_UNSUPPORTED", ERR_LIB_OSSL_STORE, OSSL_STORE_R_URI_AUTHORITY_UNSUPPORTED}, + #else + {"URI_AUTHORITY_UNSUPPORTED", 44, 111}, + #endif + #ifdef PEM_R_BAD_BASE64_DECODE + {"BAD_BASE64_DECODE", ERR_LIB_PEM, PEM_R_BAD_BASE64_DECODE}, + #else + {"BAD_BASE64_DECODE", 9, 100}, + #endif + #ifdef PEM_R_BAD_DECRYPT + {"BAD_DECRYPT", ERR_LIB_PEM, PEM_R_BAD_DECRYPT}, + #else + {"BAD_DECRYPT", 9, 101}, + #endif + #ifdef PEM_R_BAD_END_LINE + {"BAD_END_LINE", ERR_LIB_PEM, PEM_R_BAD_END_LINE}, + #else + {"BAD_END_LINE", 9, 102}, + #endif + #ifdef PEM_R_BAD_IV_CHARS + {"BAD_IV_CHARS", ERR_LIB_PEM, PEM_R_BAD_IV_CHARS}, + #else + {"BAD_IV_CHARS", 9, 103}, + #endif + #ifdef PEM_R_BAD_MAGIC_NUMBER + {"BAD_MAGIC_NUMBER", ERR_LIB_PEM, PEM_R_BAD_MAGIC_NUMBER}, + #else + {"BAD_MAGIC_NUMBER", 9, 116}, + #endif + #ifdef PEM_R_BAD_PASSWORD_READ + {"BAD_PASSWORD_READ", ERR_LIB_PEM, PEM_R_BAD_PASSWORD_READ}, + #else + {"BAD_PASSWORD_READ", 9, 104}, + #endif + #ifdef PEM_R_BAD_VERSION_NUMBER + {"BAD_VERSION_NUMBER", ERR_LIB_PEM, PEM_R_BAD_VERSION_NUMBER}, + #else + {"BAD_VERSION_NUMBER", 9, 117}, + #endif + #ifdef PEM_R_BIO_WRITE_FAILURE + {"BIO_WRITE_FAILURE", ERR_LIB_PEM, PEM_R_BIO_WRITE_FAILURE}, + #else + {"BIO_WRITE_FAILURE", 9, 118}, + #endif + #ifdef PEM_R_CIPHER_IS_NULL + {"CIPHER_IS_NULL", ERR_LIB_PEM, PEM_R_CIPHER_IS_NULL}, + #else + {"CIPHER_IS_NULL", 9, 127}, + #endif + #ifdef PEM_R_ERROR_CONVERTING_PRIVATE_KEY + {"ERROR_CONVERTING_PRIVATE_KEY", ERR_LIB_PEM, PEM_R_ERROR_CONVERTING_PRIVATE_KEY}, + #else + {"ERROR_CONVERTING_PRIVATE_KEY", 9, 115}, + #endif + #ifdef PEM_R_EXPECTING_DSS_KEY_BLOB + {"EXPECTING_DSS_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_DSS_KEY_BLOB}, + #else + {"EXPECTING_DSS_KEY_BLOB", 9, 131}, + #endif + #ifdef PEM_R_EXPECTING_PRIVATE_KEY_BLOB + {"EXPECTING_PRIVATE_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_PRIVATE_KEY_BLOB}, + #else + {"EXPECTING_PRIVATE_KEY_BLOB", 9, 119}, + #endif + #ifdef PEM_R_EXPECTING_PUBLIC_KEY_BLOB + {"EXPECTING_PUBLIC_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_PUBLIC_KEY_BLOB}, + #else + {"EXPECTING_PUBLIC_KEY_BLOB", 9, 120}, + #endif + #ifdef PEM_R_EXPECTING_RSA_KEY_BLOB + {"EXPECTING_RSA_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_RSA_KEY_BLOB}, + #else + {"EXPECTING_RSA_KEY_BLOB", 9, 132}, + #endif + #ifdef PEM_R_HEADER_TOO_LONG + {"HEADER_TOO_LONG", ERR_LIB_PEM, PEM_R_HEADER_TOO_LONG}, + #else + {"HEADER_TOO_LONG", 9, 128}, + #endif + #ifdef PEM_R_INCONSISTENT_HEADER + {"INCONSISTENT_HEADER", ERR_LIB_PEM, PEM_R_INCONSISTENT_HEADER}, + #else + {"INCONSISTENT_HEADER", 9, 121}, + #endif + #ifdef PEM_R_KEYBLOB_HEADER_PARSE_ERROR + {"KEYBLOB_HEADER_PARSE_ERROR", ERR_LIB_PEM, PEM_R_KEYBLOB_HEADER_PARSE_ERROR}, + #else + {"KEYBLOB_HEADER_PARSE_ERROR", 9, 122}, + #endif + #ifdef PEM_R_KEYBLOB_TOO_SHORT + {"KEYBLOB_TOO_SHORT", ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT}, + #else + {"KEYBLOB_TOO_SHORT", 9, 123}, + #endif + #ifdef PEM_R_MISSING_DEK_IV + {"MISSING_DEK_IV", ERR_LIB_PEM, PEM_R_MISSING_DEK_IV}, + #else + {"MISSING_DEK_IV", 9, 129}, + #endif + #ifdef PEM_R_NOT_DEK_INFO + {"NOT_DEK_INFO", ERR_LIB_PEM, PEM_R_NOT_DEK_INFO}, + #else + {"NOT_DEK_INFO", 9, 105}, + #endif + #ifdef PEM_R_NOT_ENCRYPTED + {"NOT_ENCRYPTED", ERR_LIB_PEM, PEM_R_NOT_ENCRYPTED}, + #else + {"NOT_ENCRYPTED", 9, 106}, + #endif + #ifdef PEM_R_NOT_PROC_TYPE + {"NOT_PROC_TYPE", ERR_LIB_PEM, PEM_R_NOT_PROC_TYPE}, + #else + {"NOT_PROC_TYPE", 9, 107}, + #endif + #ifdef PEM_R_NO_START_LINE + {"NO_START_LINE", ERR_LIB_PEM, PEM_R_NO_START_LINE}, + #else + {"NO_START_LINE", 9, 108}, + #endif + #ifdef PEM_R_PROBLEMS_GETTING_PASSWORD + {"PROBLEMS_GETTING_PASSWORD", ERR_LIB_PEM, PEM_R_PROBLEMS_GETTING_PASSWORD}, + #else + {"PROBLEMS_GETTING_PASSWORD", 9, 109}, + #endif + #ifdef PEM_R_PVK_DATA_TOO_SHORT + {"PVK_DATA_TOO_SHORT", ERR_LIB_PEM, PEM_R_PVK_DATA_TOO_SHORT}, + #else + {"PVK_DATA_TOO_SHORT", 9, 124}, + #endif + #ifdef PEM_R_PVK_TOO_SHORT + {"PVK_TOO_SHORT", ERR_LIB_PEM, PEM_R_PVK_TOO_SHORT}, + #else + {"PVK_TOO_SHORT", 9, 125}, + #endif + #ifdef PEM_R_READ_KEY + {"READ_KEY", ERR_LIB_PEM, PEM_R_READ_KEY}, + #else + {"READ_KEY", 9, 111}, + #endif + #ifdef PEM_R_SHORT_HEADER + {"SHORT_HEADER", ERR_LIB_PEM, PEM_R_SHORT_HEADER}, + #else + {"SHORT_HEADER", 9, 112}, + #endif + #ifdef PEM_R_UNEXPECTED_DEK_IV + {"UNEXPECTED_DEK_IV", ERR_LIB_PEM, PEM_R_UNEXPECTED_DEK_IV}, + #else + {"UNEXPECTED_DEK_IV", 9, 130}, + #endif + #ifdef PEM_R_UNSUPPORTED_CIPHER + {"UNSUPPORTED_CIPHER", ERR_LIB_PEM, PEM_R_UNSUPPORTED_CIPHER}, + #else + {"UNSUPPORTED_CIPHER", 9, 113}, + #endif + #ifdef PEM_R_UNSUPPORTED_ENCRYPTION + {"UNSUPPORTED_ENCRYPTION", ERR_LIB_PEM, PEM_R_UNSUPPORTED_ENCRYPTION}, + #else + {"UNSUPPORTED_ENCRYPTION", 9, 114}, + #endif + #ifdef PEM_R_UNSUPPORTED_KEY_COMPONENTS + {"UNSUPPORTED_KEY_COMPONENTS", ERR_LIB_PEM, PEM_R_UNSUPPORTED_KEY_COMPONENTS}, + #else + {"UNSUPPORTED_KEY_COMPONENTS", 9, 126}, + #endif + #ifdef PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE + {"UNSUPPORTED_PUBLIC_KEY_TYPE", ERR_LIB_PEM, PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE}, + #else + {"UNSUPPORTED_PUBLIC_KEY_TYPE", 9, 110}, + #endif + #ifdef PEM_R_UNSUPPORTED_PVK_KEY_TYPE + {"UNSUPPORTED_PVK_KEY_TYPE", ERR_LIB_PEM, PEM_R_UNSUPPORTED_PVK_KEY_TYPE}, + #else + {"UNSUPPORTED_PVK_KEY_TYPE", 9, 133}, + #endif + #ifdef PKCS12_R_CALLBACK_FAILED + {"CALLBACK_FAILED", ERR_LIB_PKCS12, PKCS12_R_CALLBACK_FAILED}, + #else + {"CALLBACK_FAILED", 35, 115}, + #endif + #ifdef PKCS12_R_CANT_PACK_STRUCTURE + {"CANT_PACK_STRUCTURE", ERR_LIB_PKCS12, PKCS12_R_CANT_PACK_STRUCTURE}, + #else + {"CANT_PACK_STRUCTURE", 35, 100}, + #endif + #ifdef PKCS12_R_CONTENT_TYPE_NOT_DATA + {"CONTENT_TYPE_NOT_DATA", ERR_LIB_PKCS12, PKCS12_R_CONTENT_TYPE_NOT_DATA}, + #else + {"CONTENT_TYPE_NOT_DATA", 35, 121}, + #endif + #ifdef PKCS12_R_DECODE_ERROR + {"DECODE_ERROR", ERR_LIB_PKCS12, PKCS12_R_DECODE_ERROR}, + #else + {"DECODE_ERROR", 35, 101}, + #endif + #ifdef PKCS12_R_ENCODE_ERROR + {"ENCODE_ERROR", ERR_LIB_PKCS12, PKCS12_R_ENCODE_ERROR}, + #else + {"ENCODE_ERROR", 35, 102}, + #endif + #ifdef PKCS12_R_ENCRYPT_ERROR + {"ENCRYPT_ERROR", ERR_LIB_PKCS12, PKCS12_R_ENCRYPT_ERROR}, + #else + {"ENCRYPT_ERROR", 35, 103}, + #endif + #ifdef PKCS12_R_ERROR_SETTING_ENCRYPTED_DATA_TYPE + {"ERROR_SETTING_ENCRYPTED_DATA_TYPE", ERR_LIB_PKCS12, PKCS12_R_ERROR_SETTING_ENCRYPTED_DATA_TYPE}, + #else + {"ERROR_SETTING_ENCRYPTED_DATA_TYPE", 35, 120}, + #endif + #ifdef PKCS12_R_INVALID_NULL_ARGUMENT + {"INVALID_NULL_ARGUMENT", ERR_LIB_PKCS12, PKCS12_R_INVALID_NULL_ARGUMENT}, + #else + {"INVALID_NULL_ARGUMENT", 35, 104}, + #endif + #ifdef PKCS12_R_INVALID_NULL_PKCS12_POINTER + {"INVALID_NULL_PKCS12_POINTER", ERR_LIB_PKCS12, PKCS12_R_INVALID_NULL_PKCS12_POINTER}, + #else + {"INVALID_NULL_PKCS12_POINTER", 35, 105}, + #endif + #ifdef PKCS12_R_INVALID_SALT_LENGTH + {"INVALID_SALT_LENGTH", ERR_LIB_PKCS12, PKCS12_R_INVALID_SALT_LENGTH}, + #else + {"INVALID_SALT_LENGTH", 35, 117}, + #endif + #ifdef PKCS12_R_INVALID_TYPE + {"INVALID_TYPE", ERR_LIB_PKCS12, PKCS12_R_INVALID_TYPE}, + #else + {"INVALID_TYPE", 35, 112}, + #endif + #ifdef PKCS12_R_IV_GEN_ERROR + {"IV_GEN_ERROR", ERR_LIB_PKCS12, PKCS12_R_IV_GEN_ERROR}, + #else + {"IV_GEN_ERROR", 35, 106}, + #endif + #ifdef PKCS12_R_KEY_GEN_ERROR + {"KEY_GEN_ERROR", ERR_LIB_PKCS12, PKCS12_R_KEY_GEN_ERROR}, + #else + {"KEY_GEN_ERROR", 35, 107}, + #endif + #ifdef PKCS12_R_MAC_ABSENT + {"MAC_ABSENT", ERR_LIB_PKCS12, PKCS12_R_MAC_ABSENT}, + #else + {"MAC_ABSENT", 35, 108}, + #endif + #ifdef PKCS12_R_MAC_GENERATION_ERROR + {"MAC_GENERATION_ERROR", ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR}, + #else + {"MAC_GENERATION_ERROR", 35, 109}, + #endif + #ifdef PKCS12_R_MAC_SETUP_ERROR + {"MAC_SETUP_ERROR", ERR_LIB_PKCS12, PKCS12_R_MAC_SETUP_ERROR}, + #else + {"MAC_SETUP_ERROR", 35, 110}, + #endif + #ifdef PKCS12_R_MAC_STRING_SET_ERROR + {"MAC_STRING_SET_ERROR", ERR_LIB_PKCS12, PKCS12_R_MAC_STRING_SET_ERROR}, + #else + {"MAC_STRING_SET_ERROR", 35, 111}, + #endif + #ifdef PKCS12_R_MAC_VERIFY_FAILURE + {"MAC_VERIFY_FAILURE", ERR_LIB_PKCS12, PKCS12_R_MAC_VERIFY_FAILURE}, + #else + {"MAC_VERIFY_FAILURE", 35, 113}, + #endif + #ifdef PKCS12_R_PARSE_ERROR + {"PARSE_ERROR", ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR}, + #else + {"PARSE_ERROR", 35, 114}, + #endif + #ifdef PKCS12_R_PKCS12_CIPHERFINAL_ERROR + {"PKCS12_CIPHERFINAL_ERROR", ERR_LIB_PKCS12, PKCS12_R_PKCS12_CIPHERFINAL_ERROR}, + #else + {"PKCS12_CIPHERFINAL_ERROR", 35, 116}, + #endif + #ifdef PKCS12_R_UNKNOWN_DIGEST_ALGORITHM + {"UNKNOWN_DIGEST_ALGORITHM", ERR_LIB_PKCS12, PKCS12_R_UNKNOWN_DIGEST_ALGORITHM}, + #else + {"UNKNOWN_DIGEST_ALGORITHM", 35, 118}, + #endif + #ifdef PKCS12_R_UNSUPPORTED_PKCS12_MODE + {"UNSUPPORTED_PKCS12_MODE", ERR_LIB_PKCS12, PKCS12_R_UNSUPPORTED_PKCS12_MODE}, + #else + {"UNSUPPORTED_PKCS12_MODE", 35, 119}, + #endif + #ifdef PKCS7_R_CERTIFICATE_VERIFY_ERROR + {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_PKCS7, PKCS7_R_CERTIFICATE_VERIFY_ERROR}, + #else + {"CERTIFICATE_VERIFY_ERROR", 33, 117}, + #endif + #ifdef PKCS7_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER + {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", ERR_LIB_PKCS7, PKCS7_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER}, + #else + {"CIPHER_HAS_NO_OBJECT_IDENTIFIER", 33, 144}, + #endif + #ifdef PKCS7_R_CIPHER_NOT_INITIALIZED + {"CIPHER_NOT_INITIALIZED", ERR_LIB_PKCS7, PKCS7_R_CIPHER_NOT_INITIALIZED}, + #else + {"CIPHER_NOT_INITIALIZED", 33, 116}, + #endif + #ifdef PKCS7_R_CONTENT_AND_DATA_PRESENT + {"CONTENT_AND_DATA_PRESENT", ERR_LIB_PKCS7, PKCS7_R_CONTENT_AND_DATA_PRESENT}, + #else + {"CONTENT_AND_DATA_PRESENT", 33, 118}, + #endif + #ifdef PKCS7_R_CTRL_ERROR + {"CTRL_ERROR", ERR_LIB_PKCS7, PKCS7_R_CTRL_ERROR}, + #else + {"CTRL_ERROR", 33, 152}, + #endif + #ifdef PKCS7_R_DECRYPT_ERROR + {"DECRYPT_ERROR", ERR_LIB_PKCS7, PKCS7_R_DECRYPT_ERROR}, + #else + {"DECRYPT_ERROR", 33, 119}, + #endif + #ifdef PKCS7_R_DIGEST_FAILURE + {"DIGEST_FAILURE", ERR_LIB_PKCS7, PKCS7_R_DIGEST_FAILURE}, + #else + {"DIGEST_FAILURE", 33, 101}, + #endif + #ifdef PKCS7_R_ENCRYPTION_CTRL_FAILURE + {"ENCRYPTION_CTRL_FAILURE", ERR_LIB_PKCS7, PKCS7_R_ENCRYPTION_CTRL_FAILURE}, + #else + {"ENCRYPTION_CTRL_FAILURE", 33, 149}, + #endif + #ifdef PKCS7_R_ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE + {"ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", ERR_LIB_PKCS7, PKCS7_R_ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE}, + #else + {"ENCRYPTION_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", 33, 150}, + #endif + #ifdef PKCS7_R_ERROR_ADDING_RECIPIENT + {"ERROR_ADDING_RECIPIENT", ERR_LIB_PKCS7, PKCS7_R_ERROR_ADDING_RECIPIENT}, + #else + {"ERROR_ADDING_RECIPIENT", 33, 120}, + #endif + #ifdef PKCS7_R_ERROR_SETTING_CIPHER + {"ERROR_SETTING_CIPHER", ERR_LIB_PKCS7, PKCS7_R_ERROR_SETTING_CIPHER}, + #else + {"ERROR_SETTING_CIPHER", 33, 121}, + #endif + #ifdef PKCS7_R_INVALID_NULL_POINTER + {"INVALID_NULL_POINTER", ERR_LIB_PKCS7, PKCS7_R_INVALID_NULL_POINTER}, + #else + {"INVALID_NULL_POINTER", 33, 143}, + #endif + #ifdef PKCS7_R_INVALID_SIGNED_DATA_TYPE + {"INVALID_SIGNED_DATA_TYPE", ERR_LIB_PKCS7, PKCS7_R_INVALID_SIGNED_DATA_TYPE}, + #else + {"INVALID_SIGNED_DATA_TYPE", 33, 155}, + #endif + #ifdef PKCS7_R_NO_CONTENT + {"NO_CONTENT", ERR_LIB_PKCS7, PKCS7_R_NO_CONTENT}, + #else + {"NO_CONTENT", 33, 122}, + #endif + #ifdef PKCS7_R_NO_DEFAULT_DIGEST + {"NO_DEFAULT_DIGEST", ERR_LIB_PKCS7, PKCS7_R_NO_DEFAULT_DIGEST}, + #else + {"NO_DEFAULT_DIGEST", 33, 151}, + #endif + #ifdef PKCS7_R_NO_MATCHING_DIGEST_TYPE_FOUND + {"NO_MATCHING_DIGEST_TYPE_FOUND", ERR_LIB_PKCS7, PKCS7_R_NO_MATCHING_DIGEST_TYPE_FOUND}, + #else + {"NO_MATCHING_DIGEST_TYPE_FOUND", 33, 154}, + #endif + #ifdef PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE + {"NO_RECIPIENT_MATCHES_CERTIFICATE", ERR_LIB_PKCS7, PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE}, + #else + {"NO_RECIPIENT_MATCHES_CERTIFICATE", 33, 115}, + #endif + #ifdef PKCS7_R_NO_SIGNATURES_ON_DATA + {"NO_SIGNATURES_ON_DATA", ERR_LIB_PKCS7, PKCS7_R_NO_SIGNATURES_ON_DATA}, + #else + {"NO_SIGNATURES_ON_DATA", 33, 123}, + #endif + #ifdef PKCS7_R_NO_SIGNERS + {"NO_SIGNERS", ERR_LIB_PKCS7, PKCS7_R_NO_SIGNERS}, + #else + {"NO_SIGNERS", 33, 142}, + #endif + #ifdef PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE + {"OPERATION_NOT_SUPPORTED_ON_THIS_TYPE", ERR_LIB_PKCS7, PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE}, + #else + {"OPERATION_NOT_SUPPORTED_ON_THIS_TYPE", 33, 104}, + #endif + #ifdef PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR + {"PKCS7_ADD_SIGNATURE_ERROR", ERR_LIB_PKCS7, PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR}, + #else + {"PKCS7_ADD_SIGNATURE_ERROR", 33, 124}, + #endif + #ifdef PKCS7_R_PKCS7_ADD_SIGNER_ERROR + {"PKCS7_ADD_SIGNER_ERROR", ERR_LIB_PKCS7, PKCS7_R_PKCS7_ADD_SIGNER_ERROR}, + #else + {"PKCS7_ADD_SIGNER_ERROR", 33, 153}, + #endif + #ifdef PKCS7_R_PKCS7_DATASIGN + {"PKCS7_DATASIGN", ERR_LIB_PKCS7, PKCS7_R_PKCS7_DATASIGN}, + #else + {"PKCS7_DATASIGN", 33, 145}, + #endif + #ifdef PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_PKCS7, PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, + #else + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 33, 127}, + #endif + #ifdef PKCS7_R_SIGNATURE_FAILURE + {"SIGNATURE_FAILURE", ERR_LIB_PKCS7, PKCS7_R_SIGNATURE_FAILURE}, + #else + {"SIGNATURE_FAILURE", 33, 105}, + #endif + #ifdef PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND + {"SIGNER_CERTIFICATE_NOT_FOUND", ERR_LIB_PKCS7, PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND}, + #else + {"SIGNER_CERTIFICATE_NOT_FOUND", 33, 128}, + #endif + #ifdef PKCS7_R_SIGNING_CTRL_FAILURE + {"SIGNING_CTRL_FAILURE", ERR_LIB_PKCS7, PKCS7_R_SIGNING_CTRL_FAILURE}, + #else + {"SIGNING_CTRL_FAILURE", 33, 147}, + #endif + #ifdef PKCS7_R_SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE + {"SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", ERR_LIB_PKCS7, PKCS7_R_SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE}, + #else + {"SIGNING_NOT_SUPPORTED_FOR_THIS_KEY_TYPE", 33, 148}, + #endif + #ifdef PKCS7_R_SMIME_TEXT_ERROR + {"SMIME_TEXT_ERROR", ERR_LIB_PKCS7, PKCS7_R_SMIME_TEXT_ERROR}, + #else + {"SMIME_TEXT_ERROR", 33, 129}, + #endif + #ifdef PKCS7_R_UNABLE_TO_FIND_CERTIFICATE + {"UNABLE_TO_FIND_CERTIFICATE", ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_CERTIFICATE}, + #else + {"UNABLE_TO_FIND_CERTIFICATE", 33, 106}, + #endif + #ifdef PKCS7_R_UNABLE_TO_FIND_MEM_BIO + {"UNABLE_TO_FIND_MEM_BIO", ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MEM_BIO}, + #else + {"UNABLE_TO_FIND_MEM_BIO", 33, 107}, + #endif + #ifdef PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST + {"UNABLE_TO_FIND_MESSAGE_DIGEST", ERR_LIB_PKCS7, PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST}, + #else + {"UNABLE_TO_FIND_MESSAGE_DIGEST", 33, 108}, + #endif + #ifdef PKCS7_R_UNKNOWN_DIGEST_TYPE + {"UNKNOWN_DIGEST_TYPE", ERR_LIB_PKCS7, PKCS7_R_UNKNOWN_DIGEST_TYPE}, + #else + {"UNKNOWN_DIGEST_TYPE", 33, 109}, + #endif + #ifdef PKCS7_R_UNKNOWN_OPERATION + {"UNKNOWN_OPERATION", ERR_LIB_PKCS7, PKCS7_R_UNKNOWN_OPERATION}, + #else + {"UNKNOWN_OPERATION", 33, 110}, + #endif + #ifdef PKCS7_R_UNSUPPORTED_CIPHER_TYPE + {"UNSUPPORTED_CIPHER_TYPE", ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CIPHER_TYPE}, + #else + {"UNSUPPORTED_CIPHER_TYPE", 33, 111}, + #endif + #ifdef PKCS7_R_UNSUPPORTED_CONTENT_TYPE + {"UNSUPPORTED_CONTENT_TYPE", ERR_LIB_PKCS7, PKCS7_R_UNSUPPORTED_CONTENT_TYPE}, + #else + {"UNSUPPORTED_CONTENT_TYPE", 33, 112}, + #endif + #ifdef PKCS7_R_WRONG_CONTENT_TYPE + {"WRONG_CONTENT_TYPE", ERR_LIB_PKCS7, PKCS7_R_WRONG_CONTENT_TYPE}, + #else + {"WRONG_CONTENT_TYPE", 33, 113}, + #endif + #ifdef PKCS7_R_WRONG_PKCS7_TYPE + {"WRONG_PKCS7_TYPE", ERR_LIB_PKCS7, PKCS7_R_WRONG_PKCS7_TYPE}, + #else + {"WRONG_PKCS7_TYPE", 33, 114}, + #endif + #ifdef PROP_R_NAME_TOO_LONG + {"NAME_TOO_LONG", ERR_LIB_PROP, PROP_R_NAME_TOO_LONG}, + #else + {"NAME_TOO_LONG", 55, 100}, + #endif + #ifdef PROP_R_NOT_AN_ASCII_CHARACTER + {"NOT_AN_ASCII_CHARACTER", ERR_LIB_PROP, PROP_R_NOT_AN_ASCII_CHARACTER}, + #else + {"NOT_AN_ASCII_CHARACTER", 55, 101}, + #endif + #ifdef PROP_R_NOT_AN_HEXADECIMAL_DIGIT + {"NOT_AN_HEXADECIMAL_DIGIT", ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT}, + #else + {"NOT_AN_HEXADECIMAL_DIGIT", 55, 102}, + #endif + #ifdef PROP_R_NOT_AN_IDENTIFIER + {"NOT_AN_IDENTIFIER", ERR_LIB_PROP, PROP_R_NOT_AN_IDENTIFIER}, + #else + {"NOT_AN_IDENTIFIER", 55, 103}, + #endif + #ifdef PROP_R_NOT_AN_OCTAL_DIGIT + {"NOT_AN_OCTAL_DIGIT", ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT}, + #else + {"NOT_AN_OCTAL_DIGIT", 55, 104}, + #endif + #ifdef PROP_R_NOT_A_DECIMAL_DIGIT + {"NOT_A_DECIMAL_DIGIT", ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT}, + #else + {"NOT_A_DECIMAL_DIGIT", 55, 105}, + #endif + #ifdef PROP_R_NO_MATCHING_STRING_DELIMITER + {"NO_MATCHING_STRING_DELIMITER", ERR_LIB_PROP, PROP_R_NO_MATCHING_STRING_DELIMITER}, + #else + {"NO_MATCHING_STRING_DELIMITER", 55, 106}, + #endif + #ifdef PROP_R_NO_VALUE + {"NO_VALUE", ERR_LIB_PROP, PROP_R_NO_VALUE}, + #else + {"NO_VALUE", 55, 107}, + #endif + #ifdef PROP_R_PARSE_FAILED + {"PARSE_FAILED", ERR_LIB_PROP, PROP_R_PARSE_FAILED}, + #else + {"PARSE_FAILED", 55, 108}, + #endif + #ifdef PROP_R_STRING_TOO_LONG + {"STRING_TOO_LONG", ERR_LIB_PROP, PROP_R_STRING_TOO_LONG}, + #else + {"STRING_TOO_LONG", 55, 109}, + #endif + #ifdef PROP_R_TRAILING_CHARACTERS + {"TRAILING_CHARACTERS", ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS}, + #else + {"TRAILING_CHARACTERS", 55, 110}, + #endif + #ifdef PROV_R_ADDITIONAL_INPUT_TOO_LONG + {"ADDITIONAL_INPUT_TOO_LONG", ERR_LIB_PROV, PROV_R_ADDITIONAL_INPUT_TOO_LONG}, + #else + {"ADDITIONAL_INPUT_TOO_LONG", 57, 184}, + #endif + #ifdef PROV_R_ALGORITHM_MISMATCH + {"ALGORITHM_MISMATCH", ERR_LIB_PROV, PROV_R_ALGORITHM_MISMATCH}, + #else + {"ALGORITHM_MISMATCH", 57, 173}, + #endif + #ifdef PROV_R_ALREADY_INSTANTIATED + {"ALREADY_INSTANTIATED", ERR_LIB_PROV, PROV_R_ALREADY_INSTANTIATED}, + #else + {"ALREADY_INSTANTIATED", 57, 185}, + #endif + #ifdef PROV_R_BAD_DECRYPT + {"BAD_DECRYPT", ERR_LIB_PROV, PROV_R_BAD_DECRYPT}, + #else + {"BAD_DECRYPT", 57, 100}, + #endif + #ifdef PROV_R_BAD_ENCODING + {"BAD_ENCODING", ERR_LIB_PROV, PROV_R_BAD_ENCODING}, + #else + {"BAD_ENCODING", 57, 141}, + #endif + #ifdef PROV_R_BAD_LENGTH + {"BAD_LENGTH", ERR_LIB_PROV, PROV_R_BAD_LENGTH}, + #else + {"BAD_LENGTH", 57, 142}, + #endif + #ifdef PROV_R_BAD_TLS_CLIENT_VERSION + {"BAD_TLS_CLIENT_VERSION", ERR_LIB_PROV, PROV_R_BAD_TLS_CLIENT_VERSION}, + #else + {"BAD_TLS_CLIENT_VERSION", 57, 161}, + #endif + #ifdef PROV_R_BN_ERROR + {"BN_ERROR", ERR_LIB_PROV, PROV_R_BN_ERROR}, + #else + {"BN_ERROR", 57, 160}, + #endif + #ifdef PROV_R_CIPHER_OPERATION_FAILED + {"CIPHER_OPERATION_FAILED", ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED}, + #else + {"CIPHER_OPERATION_FAILED", 57, 102}, + #endif + #ifdef PROV_R_COFACTOR_REQUIRED + {"COFACTOR_REQUIRED", ERR_LIB_PROV, PROV_R_COFACTOR_REQUIRED}, + #else + {"COFACTOR_REQUIRED", 57, 236}, + #endif + #ifdef PROV_R_DERIVATION_FUNCTION_INIT_FAILED + {"DERIVATION_FUNCTION_INIT_FAILED", ERR_LIB_PROV, PROV_R_DERIVATION_FUNCTION_INIT_FAILED}, + #else + {"DERIVATION_FUNCTION_INIT_FAILED", 57, 205}, + #endif + #ifdef PROV_R_DIGEST_NOT_ALLOWED + {"DIGEST_NOT_ALLOWED", ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED}, + #else + {"DIGEST_NOT_ALLOWED", 57, 174}, + #endif + #ifdef PROV_R_EMS_NOT_ENABLED + {"EMS_NOT_ENABLED", ERR_LIB_PROV, PROV_R_EMS_NOT_ENABLED}, + #else + {"EMS_NOT_ENABLED", 57, 233}, + #endif + #ifdef PROV_R_ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS + {"ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS", ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS}, + #else + {"ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS", 57, 244}, + #endif + #ifdef PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK + {"ENTROPY_SOURCE_STRENGTH_TOO_WEAK", ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK}, + #else + {"ENTROPY_SOURCE_STRENGTH_TOO_WEAK", 57, 186}, + #endif + #ifdef PROV_R_ERROR_INSTANTIATING_DRBG + {"ERROR_INSTANTIATING_DRBG", ERR_LIB_PROV, PROV_R_ERROR_INSTANTIATING_DRBG}, + #else + {"ERROR_INSTANTIATING_DRBG", 57, 188}, + #endif + #ifdef PROV_R_ERROR_RETRIEVING_ENTROPY + {"ERROR_RETRIEVING_ENTROPY", ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_ENTROPY}, + #else + {"ERROR_RETRIEVING_ENTROPY", 57, 189}, + #endif + #ifdef PROV_R_ERROR_RETRIEVING_NONCE + {"ERROR_RETRIEVING_NONCE", ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE}, + #else + {"ERROR_RETRIEVING_NONCE", 57, 190}, + #endif + #ifdef PROV_R_FAILED_DURING_DERIVATION + {"FAILED_DURING_DERIVATION", ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION}, + #else + {"FAILED_DURING_DERIVATION", 57, 164}, + #endif + #ifdef PROV_R_FAILED_TO_CREATE_LOCK + {"FAILED_TO_CREATE_LOCK", ERR_LIB_PROV, PROV_R_FAILED_TO_CREATE_LOCK}, + #else + {"FAILED_TO_CREATE_LOCK", 57, 180}, + #endif + #ifdef PROV_R_FAILED_TO_DECRYPT + {"FAILED_TO_DECRYPT", ERR_LIB_PROV, PROV_R_FAILED_TO_DECRYPT}, + #else + {"FAILED_TO_DECRYPT", 57, 162}, + #endif + #ifdef PROV_R_FAILED_TO_GENERATE_KEY + {"FAILED_TO_GENERATE_KEY", ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY}, + #else + {"FAILED_TO_GENERATE_KEY", 57, 121}, + #endif + #ifdef PROV_R_FAILED_TO_GET_PARAMETER + {"FAILED_TO_GET_PARAMETER", ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER}, + #else + {"FAILED_TO_GET_PARAMETER", 57, 103}, + #endif + #ifdef PROV_R_FAILED_TO_SET_PARAMETER + {"FAILED_TO_SET_PARAMETER", ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER}, + #else + {"FAILED_TO_SET_PARAMETER", 57, 104}, + #endif + #ifdef PROV_R_FAILED_TO_SIGN + {"FAILED_TO_SIGN", ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN}, + #else + {"FAILED_TO_SIGN", 57, 175}, + #endif + #ifdef PROV_R_FINAL_CALL_OUT_OF_ORDER + {"FINAL_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_FINAL_CALL_OUT_OF_ORDER}, + #else + {"FINAL_CALL_OUT_OF_ORDER", 57, 237}, + #endif + #ifdef PROV_R_FIPS_MODULE_CONDITIONAL_ERROR + {"FIPS_MODULE_CONDITIONAL_ERROR", ERR_LIB_PROV, PROV_R_FIPS_MODULE_CONDITIONAL_ERROR}, + #else + {"FIPS_MODULE_CONDITIONAL_ERROR", 57, 227}, + #endif + #ifdef PROV_R_FIPS_MODULE_ENTERING_ERROR_STATE + {"FIPS_MODULE_ENTERING_ERROR_STATE", ERR_LIB_PROV, PROV_R_FIPS_MODULE_ENTERING_ERROR_STATE}, + #else + {"FIPS_MODULE_ENTERING_ERROR_STATE", 57, 224}, + #endif + #ifdef PROV_R_FIPS_MODULE_IMPORT_PCT_ERROR + {"FIPS_MODULE_IMPORT_PCT_ERROR", ERR_LIB_PROV, PROV_R_FIPS_MODULE_IMPORT_PCT_ERROR}, + #else + {"FIPS_MODULE_IMPORT_PCT_ERROR", 57, 253}, + #endif + #ifdef PROV_R_FIPS_MODULE_IN_ERROR_STATE + {"FIPS_MODULE_IN_ERROR_STATE", ERR_LIB_PROV, PROV_R_FIPS_MODULE_IN_ERROR_STATE}, + #else + {"FIPS_MODULE_IN_ERROR_STATE", 57, 225}, + #endif + #ifdef PROV_R_GENERATE_ERROR + {"GENERATE_ERROR", ERR_LIB_PROV, PROV_R_GENERATE_ERROR}, + #else + {"GENERATE_ERROR", 57, 191}, + #endif + #ifdef PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE + {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", ERR_LIB_PROV, PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE}, + #else + {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", 57, 165}, + #endif + #ifdef PROV_R_INDICATOR_INTEGRITY_FAILURE + {"INDICATOR_INTEGRITY_FAILURE", ERR_LIB_PROV, PROV_R_INDICATOR_INTEGRITY_FAILURE}, + #else + {"INDICATOR_INTEGRITY_FAILURE", 57, 210}, + #endif + #ifdef PROV_R_INIT_CALL_OUT_OF_ORDER + {"INIT_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_INIT_CALL_OUT_OF_ORDER}, + #else + {"INIT_CALL_OUT_OF_ORDER", 57, 238}, + #endif + #ifdef PROV_R_INSUFFICIENT_DRBG_STRENGTH + {"INSUFFICIENT_DRBG_STRENGTH", ERR_LIB_PROV, PROV_R_INSUFFICIENT_DRBG_STRENGTH}, + #else + {"INSUFFICIENT_DRBG_STRENGTH", 57, 181}, + #endif + #ifdef PROV_R_INVALID_AAD + {"INVALID_AAD", ERR_LIB_PROV, PROV_R_INVALID_AAD}, + #else + {"INVALID_AAD", 57, 108}, + #endif + #ifdef PROV_R_INVALID_AEAD + {"INVALID_AEAD", ERR_LIB_PROV, PROV_R_INVALID_AEAD}, + #else + {"INVALID_AEAD", 57, 231}, + #endif + #ifdef PROV_R_INVALID_CIPHER + {"INVALID_CIPHER", ERR_LIB_PROV, PROV_R_INVALID_CIPHER}, + #else + {"INVALID_CIPHER", 57, 260}, + #endif + #ifdef PROV_R_INVALID_CONFIG_DATA + {"INVALID_CONFIG_DATA", ERR_LIB_PROV, PROV_R_INVALID_CONFIG_DATA}, + #else + {"INVALID_CONFIG_DATA", 57, 211}, + #endif + #ifdef PROV_R_INVALID_CONSTANT_LENGTH + {"INVALID_CONSTANT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_CONSTANT_LENGTH}, + #else + {"INVALID_CONSTANT_LENGTH", 57, 157}, + #endif + #ifdef PROV_R_INVALID_CURVE + {"INVALID_CURVE", ERR_LIB_PROV, PROV_R_INVALID_CURVE}, + #else + {"INVALID_CURVE", 57, 176}, + #endif + #ifdef PROV_R_INVALID_CUSTOM_LENGTH + {"INVALID_CUSTOM_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_CUSTOM_LENGTH}, + #else + {"INVALID_CUSTOM_LENGTH", 57, 111}, + #endif + #ifdef PROV_R_INVALID_DATA + {"INVALID_DATA", ERR_LIB_PROV, PROV_R_INVALID_DATA}, + #else + {"INVALID_DATA", 57, 115}, + #endif + #ifdef PROV_R_INVALID_DIGEST + {"INVALID_DIGEST", ERR_LIB_PROV, PROV_R_INVALID_DIGEST}, + #else + {"INVALID_DIGEST", 57, 122}, + #endif + #ifdef PROV_R_INVALID_DIGEST_LENGTH + {"INVALID_DIGEST_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH}, + #else + {"INVALID_DIGEST_LENGTH", 57, 166}, + #endif + #ifdef PROV_R_INVALID_DIGEST_SIZE + {"INVALID_DIGEST_SIZE", ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE}, + #else + {"INVALID_DIGEST_SIZE", 57, 218}, + #endif + #ifdef PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION + {"INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION", ERR_LIB_PROV, PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION}, + #else + {"INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION", 57, 243}, + #endif + #ifdef PROV_R_INVALID_FUNCTION_NAME + {"INVALID_FUNCTION_NAME", ERR_LIB_PROV, PROV_R_INVALID_FUNCTION_NAME}, + #else + {"INVALID_FUNCTION_NAME", 57, 258}, + #endif + #ifdef PROV_R_INVALID_INDEX_LENGTH + {"INVALID_INDEX_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_INDEX_LENGTH}, + #else + {"INVALID_INDEX_LENGTH", 57, 259}, + #endif + #ifdef PROV_R_INVALID_INPUT_LENGTH + {"INVALID_INPUT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH}, + #else + {"INVALID_INPUT_LENGTH", 57, 230}, + #endif + #ifdef PROV_R_INVALID_ITERATION_COUNT + {"INVALID_ITERATION_COUNT", ERR_LIB_PROV, PROV_R_INVALID_ITERATION_COUNT}, + #else + {"INVALID_ITERATION_COUNT", 57, 123}, + #endif + #ifdef PROV_R_INVALID_IV_LENGTH + {"INVALID_IV_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH}, + #else + {"INVALID_IV_LENGTH", 57, 109}, + #endif + #ifdef PROV_R_INVALID_KDF + {"INVALID_KDF", ERR_LIB_PROV, PROV_R_INVALID_KDF}, + #else + {"INVALID_KDF", 57, 232}, + #endif + #ifdef PROV_R_INVALID_KDR + {"INVALID_KDR", ERR_LIB_PROV, PROV_R_INVALID_KDR}, + #else + {"INVALID_KDR", 57, 256}, + #endif + #ifdef PROV_R_INVALID_KEY + {"INVALID_KEY", ERR_LIB_PROV, PROV_R_INVALID_KEY}, + #else + {"INVALID_KEY", 57, 158}, + #endif + #ifdef PROV_R_INVALID_KEY_LENGTH + {"INVALID_KEY_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH}, + #else + {"INVALID_KEY_LENGTH", 57, 105}, + #endif + #ifdef PROV_R_INVALID_LABEL + {"INVALID_LABEL", ERR_LIB_PROV, PROV_R_INVALID_LABEL}, + #else + {"INVALID_LABEL", 57, 257}, + #endif + #ifdef PROV_R_INVALID_MAC + {"INVALID_MAC", ERR_LIB_PROV, PROV_R_INVALID_MAC}, + #else + {"INVALID_MAC", 57, 151}, + #endif + #ifdef PROV_R_INVALID_MEMORY_SIZE + {"INVALID_MEMORY_SIZE", ERR_LIB_PROV, PROV_R_INVALID_MEMORY_SIZE}, + #else + {"INVALID_MEMORY_SIZE", 57, 235}, + #endif + #ifdef PROV_R_INVALID_MGF1_MD + {"INVALID_MGF1_MD", ERR_LIB_PROV, PROV_R_INVALID_MGF1_MD}, + #else + {"INVALID_MGF1_MD", 57, 167}, + #endif + #ifdef PROV_R_INVALID_MODE + {"INVALID_MODE", ERR_LIB_PROV, PROV_R_INVALID_MODE}, + #else + {"INVALID_MODE", 57, 125}, + #endif + #ifdef PROV_R_INVALID_OUTPUT_LENGTH + {"INVALID_OUTPUT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH}, + #else + {"INVALID_OUTPUT_LENGTH", 57, 217}, + #endif + #ifdef PROV_R_INVALID_PADDING_MODE + {"INVALID_PADDING_MODE", ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE}, + #else + {"INVALID_PADDING_MODE", 57, 168}, + #endif + #ifdef PROV_R_INVALID_PREHASHED_DIGEST_LENGTH + {"INVALID_PREHASHED_DIGEST_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_PREHASHED_DIGEST_LENGTH}, + #else + {"INVALID_PREHASHED_DIGEST_LENGTH", 57, 241}, + #endif + #ifdef PROV_R_INVALID_PUBINFO + {"INVALID_PUBINFO", ERR_LIB_PROV, PROV_R_INVALID_PUBINFO}, + #else + {"INVALID_PUBINFO", 57, 198}, + #endif + #ifdef PROV_R_INVALID_SALT_LENGTH + {"INVALID_SALT_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH}, + #else + {"INVALID_SALT_LENGTH", 57, 112}, + #endif + #ifdef PROV_R_INVALID_SEED_LENGTH + {"INVALID_SEED_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH}, + #else + {"INVALID_SEED_LENGTH", 57, 154}, + #endif + #ifdef PROV_R_INVALID_SIGNATURE_SIZE + {"INVALID_SIGNATURE_SIZE", ERR_LIB_PROV, PROV_R_INVALID_SIGNATURE_SIZE}, + #else + {"INVALID_SIGNATURE_SIZE", 57, 179}, + #endif + #ifdef PROV_R_INVALID_STATE + {"INVALID_STATE", ERR_LIB_PROV, PROV_R_INVALID_STATE}, + #else + {"INVALID_STATE", 57, 212}, + #endif + #ifdef PROV_R_INVALID_TAG + {"INVALID_TAG", ERR_LIB_PROV, PROV_R_INVALID_TAG}, + #else + {"INVALID_TAG", 57, 110}, + #endif + #ifdef PROV_R_INVALID_TAG_LENGTH + {"INVALID_TAG_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH}, + #else + {"INVALID_TAG_LENGTH", 57, 118}, + #endif + #ifdef PROV_R_INVALID_THREAD_POOL_SIZE + {"INVALID_THREAD_POOL_SIZE", ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE}, + #else + {"INVALID_THREAD_POOL_SIZE", 57, 234}, + #endif + #ifdef PROV_R_INVALID_UKM_LENGTH + {"INVALID_UKM_LENGTH", ERR_LIB_PROV, PROV_R_INVALID_UKM_LENGTH}, + #else + {"INVALID_UKM_LENGTH", 57, 200}, + #endif + #ifdef PROV_R_INVALID_X931_DIGEST + {"INVALID_X931_DIGEST", ERR_LIB_PROV, PROV_R_INVALID_X931_DIGEST}, + #else + {"INVALID_X931_DIGEST", 57, 170}, + #endif + #ifdef PROV_R_IN_ERROR_STATE + {"IN_ERROR_STATE", ERR_LIB_PROV, PROV_R_IN_ERROR_STATE}, + #else + {"IN_ERROR_STATE", 57, 192}, + #endif + #ifdef PROV_R_KEY_SETUP_FAILED + {"KEY_SETUP_FAILED", ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED}, + #else + {"KEY_SETUP_FAILED", 57, 101}, + #endif + #ifdef PROV_R_KEY_SIZE_TOO_SMALL + {"KEY_SIZE_TOO_SMALL", ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL}, + #else + {"KEY_SIZE_TOO_SMALL", 57, 171}, + #endif + #ifdef PROV_R_LENGTH_TOO_LARGE + {"LENGTH_TOO_LARGE", ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE}, + #else + {"LENGTH_TOO_LARGE", 57, 202}, + #endif + #ifdef PROV_R_MISMATCHING_DOMAIN_PARAMETERS + {"MISMATCHING_DOMAIN_PARAMETERS", ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS}, + #else + {"MISMATCHING_DOMAIN_PARAMETERS", 57, 203}, + #endif + #ifdef PROV_R_MISSING_CEK_ALG + {"MISSING_CEK_ALG", ERR_LIB_PROV, PROV_R_MISSING_CEK_ALG}, + #else + {"MISSING_CEK_ALG", 57, 144}, + #endif + #ifdef PROV_R_MISSING_CIPHER + {"MISSING_CIPHER", ERR_LIB_PROV, PROV_R_MISSING_CIPHER}, + #else + {"MISSING_CIPHER", 57, 155}, + #endif + #ifdef PROV_R_MISSING_CONFIG_DATA + {"MISSING_CONFIG_DATA", ERR_LIB_PROV, PROV_R_MISSING_CONFIG_DATA}, + #else + {"MISSING_CONFIG_DATA", 57, 213}, + #endif + #ifdef PROV_R_MISSING_CONSTANT + {"MISSING_CONSTANT", ERR_LIB_PROV, PROV_R_MISSING_CONSTANT}, + #else + {"MISSING_CONSTANT", 57, 156}, + #endif + #ifdef PROV_R_MISSING_EID + {"MISSING_EID", ERR_LIB_PROV, PROV_R_MISSING_EID}, + #else + {"MISSING_EID", 57, 255}, + #endif + #ifdef PROV_R_MISSING_KEY + {"MISSING_KEY", ERR_LIB_PROV, PROV_R_MISSING_KEY}, + #else + {"MISSING_KEY", 57, 128}, + #endif + #ifdef PROV_R_MISSING_MAC + {"MISSING_MAC", ERR_LIB_PROV, PROV_R_MISSING_MAC}, + #else + {"MISSING_MAC", 57, 150}, + #endif + #ifdef PROV_R_MISSING_MESSAGE_DIGEST + {"MISSING_MESSAGE_DIGEST", ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST}, + #else + {"MISSING_MESSAGE_DIGEST", 57, 129}, + #endif + #ifdef PROV_R_MISSING_OID + {"MISSING_OID", ERR_LIB_PROV, PROV_R_MISSING_OID}, + #else + {"MISSING_OID", 57, 209}, + #endif + #ifdef PROV_R_MISSING_PASS + {"MISSING_PASS", ERR_LIB_PROV, PROV_R_MISSING_PASS}, + #else + {"MISSING_PASS", 57, 130}, + #endif + #ifdef PROV_R_MISSING_SALT + {"MISSING_SALT", ERR_LIB_PROV, PROV_R_MISSING_SALT}, + #else + {"MISSING_SALT", 57, 131}, + #endif + #ifdef PROV_R_MISSING_SECRET + {"MISSING_SECRET", ERR_LIB_PROV, PROV_R_MISSING_SECRET}, + #else + {"MISSING_SECRET", 57, 132}, + #endif + #ifdef PROV_R_MISSING_SEED + {"MISSING_SEED", ERR_LIB_PROV, PROV_R_MISSING_SEED}, + #else + {"MISSING_SEED", 57, 140}, + #endif + #ifdef PROV_R_MISSING_SESSION_ID + {"MISSING_SESSION_ID", ERR_LIB_PROV, PROV_R_MISSING_SESSION_ID}, + #else + {"MISSING_SESSION_ID", 57, 133}, + #endif + #ifdef PROV_R_MISSING_TYPE + {"MISSING_TYPE", ERR_LIB_PROV, PROV_R_MISSING_TYPE}, + #else + {"MISSING_TYPE", 57, 134}, + #endif + #ifdef PROV_R_MISSING_XCGHASH + {"MISSING_XCGHASH", ERR_LIB_PROV, PROV_R_MISSING_XCGHASH}, + #else + {"MISSING_XCGHASH", 57, 135}, + #endif + #ifdef PROV_R_ML_DSA_NO_FORMAT + {"ML_DSA_NO_FORMAT", ERR_LIB_PROV, PROV_R_ML_DSA_NO_FORMAT}, + #else + {"ML_DSA_NO_FORMAT", 57, 245}, + #endif + #ifdef PROV_R_ML_KEM_NO_FORMAT + {"ML_KEM_NO_FORMAT", ERR_LIB_PROV, PROV_R_ML_KEM_NO_FORMAT}, + #else + {"ML_KEM_NO_FORMAT", 57, 246}, + #endif + #ifdef PROV_R_MODULE_INTEGRITY_FAILURE + {"MODULE_INTEGRITY_FAILURE", ERR_LIB_PROV, PROV_R_MODULE_INTEGRITY_FAILURE}, + #else + {"MODULE_INTEGRITY_FAILURE", 57, 214}, + #endif + #ifdef PROV_R_NOT_A_PRIVATE_KEY + {"NOT_A_PRIVATE_KEY", ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY}, + #else + {"NOT_A_PRIVATE_KEY", 57, 221}, + #endif + #ifdef PROV_R_NOT_A_PUBLIC_KEY + {"NOT_A_PUBLIC_KEY", ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY}, + #else + {"NOT_A_PUBLIC_KEY", 57, 220}, + #endif + #ifdef PROV_R_NOT_INSTANTIATED + {"NOT_INSTANTIATED", ERR_LIB_PROV, PROV_R_NOT_INSTANTIATED}, + #else + {"NOT_INSTANTIATED", 57, 193}, + #endif + #ifdef PROV_R_NOT_PARAMETERS + {"NOT_PARAMETERS", ERR_LIB_PROV, PROV_R_NOT_PARAMETERS}, + #else + {"NOT_PARAMETERS", 57, 226}, + #endif + #ifdef PROV_R_NOT_SUPPORTED + {"NOT_SUPPORTED", ERR_LIB_PROV, PROV_R_NOT_SUPPORTED}, + #else + {"NOT_SUPPORTED", 57, 136}, + #endif + #ifdef PROV_R_NOT_XOF_OR_INVALID_LENGTH + {"NOT_XOF_OR_INVALID_LENGTH", ERR_LIB_PROV, PROV_R_NOT_XOF_OR_INVALID_LENGTH}, + #else + {"NOT_XOF_OR_INVALID_LENGTH", 57, 113}, + #endif + #ifdef PROV_R_NO_INSTANCE_ALLOWED + {"NO_INSTANCE_ALLOWED", ERR_LIB_PROV, PROV_R_NO_INSTANCE_ALLOWED}, + #else + {"NO_INSTANCE_ALLOWED", 57, 242}, + #endif + #ifdef PROV_R_NO_KEY_SET + {"NO_KEY_SET", ERR_LIB_PROV, PROV_R_NO_KEY_SET}, + #else + {"NO_KEY_SET", 57, 114}, + #endif + #ifdef PROV_R_NO_PARAMETERS_SET + {"NO_PARAMETERS_SET", ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET}, + #else + {"NO_PARAMETERS_SET", 57, 177}, + #endif + #ifdef PROV_R_NULL_LENGTH_POINTER + {"NULL_LENGTH_POINTER", ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER}, + #else + {"NULL_LENGTH_POINTER", 57, 247}, + #endif + #ifdef PROV_R_NULL_OUTPUT_BUFFER + {"NULL_OUTPUT_BUFFER", ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER}, + #else + {"NULL_OUTPUT_BUFFER", 57, 248}, + #endif + #ifdef PROV_R_ONESHOT_CALL_OUT_OF_ORDER + {"ONESHOT_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_ONESHOT_CALL_OUT_OF_ORDER}, + #else + {"ONESHOT_CALL_OUT_OF_ORDER", 57, 239}, + #endif + #ifdef PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", ERR_LIB_PROV, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE}, + #else + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", 57, 178}, + #endif + #ifdef PROV_R_OUTPUT_BUFFER_TOO_SMALL + {"OUTPUT_BUFFER_TOO_SMALL", ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL}, + #else + {"OUTPUT_BUFFER_TOO_SMALL", 57, 106}, + #endif + #ifdef PROV_R_PARENT_CANNOT_GENERATE_RANDOM_NUMBERS + {"PARENT_CANNOT_GENERATE_RANDOM_NUMBERS", ERR_LIB_PROV, PROV_R_PARENT_CANNOT_GENERATE_RANDOM_NUMBERS}, + #else + {"PARENT_CANNOT_GENERATE_RANDOM_NUMBERS", 57, 228}, + #endif + #ifdef PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED + {"PARENT_CANNOT_SUPPLY_ENTROPY_SEED", ERR_LIB_PROV, PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED}, + #else + {"PARENT_CANNOT_SUPPLY_ENTROPY_SEED", 57, 187}, + #endif + #ifdef PROV_R_PARENT_LOCKING_NOT_ENABLED + {"PARENT_LOCKING_NOT_ENABLED", ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED}, + #else + {"PARENT_LOCKING_NOT_ENABLED", 57, 182}, + #endif + #ifdef PROV_R_PARENT_STRENGTH_TOO_WEAK + {"PARENT_STRENGTH_TOO_WEAK", ERR_LIB_PROV, PROV_R_PARENT_STRENGTH_TOO_WEAK}, + #else + {"PARENT_STRENGTH_TOO_WEAK", 57, 194}, + #endif + #ifdef PROV_R_PASSWORD_STRENGTH_TOO_WEAK + {"PASSWORD_STRENGTH_TOO_WEAK", ERR_LIB_PROV, PROV_R_PASSWORD_STRENGTH_TOO_WEAK}, + #else + {"PASSWORD_STRENGTH_TOO_WEAK", 57, 254}, + #endif + #ifdef PROV_R_PATH_MUST_BE_ABSOLUTE + {"PATH_MUST_BE_ABSOLUTE", ERR_LIB_PROV, PROV_R_PATH_MUST_BE_ABSOLUTE}, + #else + {"PATH_MUST_BE_ABSOLUTE", 57, 219}, + #endif + #ifdef PROV_R_PERSONALISATION_STRING_TOO_LONG + {"PERSONALISATION_STRING_TOO_LONG", ERR_LIB_PROV, PROV_R_PERSONALISATION_STRING_TOO_LONG}, + #else + {"PERSONALISATION_STRING_TOO_LONG", 57, 195}, + #endif + #ifdef PROV_R_PSS_SALTLEN_TOO_SMALL + {"PSS_SALTLEN_TOO_SMALL", ERR_LIB_PROV, PROV_R_PSS_SALTLEN_TOO_SMALL}, + #else + {"PSS_SALTLEN_TOO_SMALL", 57, 172}, + #endif + #ifdef PROV_R_REPEATED_PARAMETER + {"REPEATED_PARAMETER", ERR_LIB_PROV, PROV_R_REPEATED_PARAMETER}, + #else + {"REPEATED_PARAMETER", 57, 252}, + #endif + #ifdef PROV_R_REQUEST_TOO_LARGE_FOR_DRBG + {"REQUEST_TOO_LARGE_FOR_DRBG", ERR_LIB_PROV, PROV_R_REQUEST_TOO_LARGE_FOR_DRBG}, + #else + {"REQUEST_TOO_LARGE_FOR_DRBG", 57, 196}, + #endif + #ifdef PROV_R_REQUIRE_CTR_MODE_CIPHER + {"REQUIRE_CTR_MODE_CIPHER", ERR_LIB_PROV, PROV_R_REQUIRE_CTR_MODE_CIPHER}, + #else + {"REQUIRE_CTR_MODE_CIPHER", 57, 206}, + #endif + #ifdef PROV_R_RESEED_ERROR + {"RESEED_ERROR", ERR_LIB_PROV, PROV_R_RESEED_ERROR}, + #else + {"RESEED_ERROR", 57, 197}, + #endif + #ifdef PROV_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES + {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", ERR_LIB_PROV, PROV_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES}, + #else + {"SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES", 57, 222}, + #endif + #ifdef PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT + {"SEED_SOURCES_MUST_NOT_HAVE_A_PARENT", ERR_LIB_PROV, PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT}, + #else + {"SEED_SOURCES_MUST_NOT_HAVE_A_PARENT", 57, 229}, + #endif + #ifdef PROV_R_SELF_TEST_KAT_FAILURE + {"SELF_TEST_KAT_FAILURE", ERR_LIB_PROV, PROV_R_SELF_TEST_KAT_FAILURE}, + #else + {"SELF_TEST_KAT_FAILURE", 57, 215}, + #endif + #ifdef PROV_R_SELF_TEST_POST_FAILURE + {"SELF_TEST_POST_FAILURE", ERR_LIB_PROV, PROV_R_SELF_TEST_POST_FAILURE}, + #else + {"SELF_TEST_POST_FAILURE", 57, 216}, + #endif + #ifdef PROV_R_TAG_NOT_NEEDED + {"TAG_NOT_NEEDED", ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED}, + #else + {"TAG_NOT_NEEDED", 57, 120}, + #endif + #ifdef PROV_R_TAG_NOT_SET + {"TAG_NOT_SET", ERR_LIB_PROV, PROV_R_TAG_NOT_SET}, + #else + {"TAG_NOT_SET", 57, 119}, + #endif + #ifdef PROV_R_TOO_MANY_RECORDS + {"TOO_MANY_RECORDS", ERR_LIB_PROV, PROV_R_TOO_MANY_RECORDS}, + #else + {"TOO_MANY_RECORDS", 57, 126}, + #endif + #ifdef PROV_R_UNABLE_TO_FIND_CIPHERS + {"UNABLE_TO_FIND_CIPHERS", ERR_LIB_PROV, PROV_R_UNABLE_TO_FIND_CIPHERS}, + #else + {"UNABLE_TO_FIND_CIPHERS", 57, 207}, + #endif + #ifdef PROV_R_UNABLE_TO_GET_PARENT_STRENGTH + {"UNABLE_TO_GET_PARENT_STRENGTH", ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PARENT_STRENGTH}, + #else + {"UNABLE_TO_GET_PARENT_STRENGTH", 57, 199}, + #endif + #ifdef PROV_R_UNABLE_TO_GET_PASSPHRASE + {"UNABLE_TO_GET_PASSPHRASE", ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PASSPHRASE}, + #else + {"UNABLE_TO_GET_PASSPHRASE", 57, 159}, + #endif + #ifdef PROV_R_UNABLE_TO_INITIALISE_CIPHERS + {"UNABLE_TO_INITIALISE_CIPHERS", ERR_LIB_PROV, PROV_R_UNABLE_TO_INITIALISE_CIPHERS}, + #else + {"UNABLE_TO_INITIALISE_CIPHERS", 57, 208}, + #endif + #ifdef PROV_R_UNABLE_TO_LOAD_SHA256 + {"UNABLE_TO_LOAD_SHA256", ERR_LIB_PROV, PROV_R_UNABLE_TO_LOAD_SHA256}, + #else + {"UNABLE_TO_LOAD_SHA256", 57, 147}, + #endif + #ifdef PROV_R_UNABLE_TO_LOCK_PARENT + {"UNABLE_TO_LOCK_PARENT", ERR_LIB_PROV, PROV_R_UNABLE_TO_LOCK_PARENT}, + #else + {"UNABLE_TO_LOCK_PARENT", 57, 201}, + #endif + #ifdef PROV_R_UNABLE_TO_RESEED + {"UNABLE_TO_RESEED", ERR_LIB_PROV, PROV_R_UNABLE_TO_RESEED}, + #else + {"UNABLE_TO_RESEED", 57, 204}, + #endif + #ifdef PROV_R_UNEXPECTED_KEY_PARAMETERS + {"UNEXPECTED_KEY_PARAMETERS", ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS}, + #else + {"UNEXPECTED_KEY_PARAMETERS", 57, 249}, + #endif + #ifdef PROV_R_UNSUPPORTED_CEK_ALG + {"UNSUPPORTED_CEK_ALG", ERR_LIB_PROV, PROV_R_UNSUPPORTED_CEK_ALG}, + #else + {"UNSUPPORTED_CEK_ALG", 57, 145}, + #endif + #ifdef PROV_R_UNSUPPORTED_KEY_SIZE + {"UNSUPPORTED_KEY_SIZE", ERR_LIB_PROV, PROV_R_UNSUPPORTED_KEY_SIZE}, + #else + {"UNSUPPORTED_KEY_SIZE", 57, 153}, + #endif + #ifdef PROV_R_UNSUPPORTED_MAC_TYPE + {"UNSUPPORTED_MAC_TYPE", ERR_LIB_PROV, PROV_R_UNSUPPORTED_MAC_TYPE}, + #else + {"UNSUPPORTED_MAC_TYPE", 57, 137}, + #endif + #ifdef PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS + {"UNSUPPORTED_NUMBER_OF_ROUNDS", ERR_LIB_PROV, PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS}, + #else + {"UNSUPPORTED_NUMBER_OF_ROUNDS", 57, 152}, + #endif + #ifdef PROV_R_UNSUPPORTED_SELECTION + {"UNSUPPORTED_SELECTION", ERR_LIB_PROV, PROV_R_UNSUPPORTED_SELECTION}, + #else + {"UNSUPPORTED_SELECTION", 57, 250}, + #endif + #ifdef PROV_R_UPDATE_CALL_OUT_OF_ORDER + {"UPDATE_CALL_OUT_OF_ORDER", ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER}, + #else + {"UPDATE_CALL_OUT_OF_ORDER", 57, 240}, + #endif + #ifdef PROV_R_URI_AUTHORITY_UNSUPPORTED + {"URI_AUTHORITY_UNSUPPORTED", ERR_LIB_PROV, PROV_R_URI_AUTHORITY_UNSUPPORTED}, + #else + {"URI_AUTHORITY_UNSUPPORTED", 57, 223}, + #endif + #ifdef PROV_R_VALUE_ERROR + {"VALUE_ERROR", ERR_LIB_PROV, PROV_R_VALUE_ERROR}, + #else + {"VALUE_ERROR", 57, 138}, + #endif + #ifdef PROV_R_WRONG_CIPHERTEXT_SIZE + {"WRONG_CIPHERTEXT_SIZE", ERR_LIB_PROV, PROV_R_WRONG_CIPHERTEXT_SIZE}, + #else + {"WRONG_CIPHERTEXT_SIZE", 57, 251}, + #endif + #ifdef PROV_R_WRONG_FINAL_BLOCK_LENGTH + {"WRONG_FINAL_BLOCK_LENGTH", ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH}, + #else + {"WRONG_FINAL_BLOCK_LENGTH", 57, 107}, + #endif + #ifdef PROV_R_WRONG_OUTPUT_BUFFER_SIZE + {"WRONG_OUTPUT_BUFFER_SIZE", ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE}, + #else + {"WRONG_OUTPUT_BUFFER_SIZE", 57, 139}, + #endif + #ifdef PROV_R_XOF_DIGESTS_NOT_ALLOWED + {"XOF_DIGESTS_NOT_ALLOWED", ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED}, + #else + {"XOF_DIGESTS_NOT_ALLOWED", 57, 183}, + #endif + #ifdef PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE + {"XTS_DATA_UNIT_IS_TOO_LARGE", ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE}, + #else + {"XTS_DATA_UNIT_IS_TOO_LARGE", 57, 148}, + #endif + #ifdef PROV_R_XTS_DUPLICATED_KEYS + {"XTS_DUPLICATED_KEYS", ERR_LIB_PROV, PROV_R_XTS_DUPLICATED_KEYS}, + #else + {"XTS_DUPLICATED_KEYS", 57, 149}, + #endif + #ifdef RAND_R_ADDITIONAL_INPUT_TOO_LONG + {"ADDITIONAL_INPUT_TOO_LONG", ERR_LIB_RAND, RAND_R_ADDITIONAL_INPUT_TOO_LONG}, + #else + {"ADDITIONAL_INPUT_TOO_LONG", 36, 102}, + #endif + #ifdef RAND_R_ALREADY_INSTANTIATED + {"ALREADY_INSTANTIATED", ERR_LIB_RAND, RAND_R_ALREADY_INSTANTIATED}, + #else + {"ALREADY_INSTANTIATED", 36, 103}, + #endif + #ifdef RAND_R_ARGUMENT_OUT_OF_RANGE + {"ARGUMENT_OUT_OF_RANGE", ERR_LIB_RAND, RAND_R_ARGUMENT_OUT_OF_RANGE}, + #else + {"ARGUMENT_OUT_OF_RANGE", 36, 105}, + #endif + #ifdef RAND_R_CANNOT_OPEN_FILE + {"CANNOT_OPEN_FILE", ERR_LIB_RAND, RAND_R_CANNOT_OPEN_FILE}, + #else + {"CANNOT_OPEN_FILE", 36, 121}, + #endif + #ifdef RAND_R_DRBG_ALREADY_INITIALIZED + {"DRBG_ALREADY_INITIALIZED", ERR_LIB_RAND, RAND_R_DRBG_ALREADY_INITIALIZED}, + #else + {"DRBG_ALREADY_INITIALIZED", 36, 129}, + #endif + #ifdef RAND_R_DRBG_NOT_INITIALISED + {"DRBG_NOT_INITIALISED", ERR_LIB_RAND, RAND_R_DRBG_NOT_INITIALISED}, + #else + {"DRBG_NOT_INITIALISED", 36, 104}, + #endif + #ifdef RAND_R_ENTROPY_INPUT_TOO_LONG + {"ENTROPY_INPUT_TOO_LONG", ERR_LIB_RAND, RAND_R_ENTROPY_INPUT_TOO_LONG}, + #else + {"ENTROPY_INPUT_TOO_LONG", 36, 106}, + #endif + #ifdef RAND_R_ENTROPY_OUT_OF_RANGE + {"ENTROPY_OUT_OF_RANGE", ERR_LIB_RAND, RAND_R_ENTROPY_OUT_OF_RANGE}, + #else + {"ENTROPY_OUT_OF_RANGE", 36, 124}, + #endif + #ifdef RAND_R_ERROR_ENTROPY_POOL_WAS_IGNORED + {"ERROR_ENTROPY_POOL_WAS_IGNORED", ERR_LIB_RAND, RAND_R_ERROR_ENTROPY_POOL_WAS_IGNORED}, + #else + {"ERROR_ENTROPY_POOL_WAS_IGNORED", 36, 127}, + #endif + #ifdef RAND_R_ERROR_INITIALISING_DRBG + {"ERROR_INITIALISING_DRBG", ERR_LIB_RAND, RAND_R_ERROR_INITIALISING_DRBG}, + #else + {"ERROR_INITIALISING_DRBG", 36, 107}, + #endif + #ifdef RAND_R_ERROR_INSTANTIATING_DRBG + {"ERROR_INSTANTIATING_DRBG", ERR_LIB_RAND, RAND_R_ERROR_INSTANTIATING_DRBG}, + #else + {"ERROR_INSTANTIATING_DRBG", 36, 108}, + #endif + #ifdef RAND_R_ERROR_RETRIEVING_ADDITIONAL_INPUT + {"ERROR_RETRIEVING_ADDITIONAL_INPUT", ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ADDITIONAL_INPUT}, + #else + {"ERROR_RETRIEVING_ADDITIONAL_INPUT", 36, 109}, + #endif + #ifdef RAND_R_ERROR_RETRIEVING_ENTROPY + {"ERROR_RETRIEVING_ENTROPY", ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ENTROPY}, + #else + {"ERROR_RETRIEVING_ENTROPY", 36, 110}, + #endif + #ifdef RAND_R_ERROR_RETRIEVING_NONCE + {"ERROR_RETRIEVING_NONCE", ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_NONCE}, + #else + {"ERROR_RETRIEVING_NONCE", 36, 111}, + #endif + #ifdef RAND_R_FAILED_TO_CREATE_LOCK + {"FAILED_TO_CREATE_LOCK", ERR_LIB_RAND, RAND_R_FAILED_TO_CREATE_LOCK}, + #else + {"FAILED_TO_CREATE_LOCK", 36, 126}, + #endif + #ifdef RAND_R_FUNC_NOT_IMPLEMENTED + {"FUNC_NOT_IMPLEMENTED", ERR_LIB_RAND, RAND_R_FUNC_NOT_IMPLEMENTED}, + #else + {"FUNC_NOT_IMPLEMENTED", 36, 101}, + #endif + #ifdef RAND_R_FWRITE_ERROR + {"FWRITE_ERROR", ERR_LIB_RAND, RAND_R_FWRITE_ERROR}, + #else + {"FWRITE_ERROR", 36, 123}, + #endif + #ifdef RAND_R_GENERATE_ERROR + {"GENERATE_ERROR", ERR_LIB_RAND, RAND_R_GENERATE_ERROR}, + #else + {"GENERATE_ERROR", 36, 112}, + #endif + #ifdef RAND_R_INSUFFICIENT_DRBG_STRENGTH + {"INSUFFICIENT_DRBG_STRENGTH", ERR_LIB_RAND, RAND_R_INSUFFICIENT_DRBG_STRENGTH}, + #else + {"INSUFFICIENT_DRBG_STRENGTH", 36, 139}, + #endif + #ifdef RAND_R_INTERNAL_ERROR + {"INTERNAL_ERROR", ERR_LIB_RAND, RAND_R_INTERNAL_ERROR}, + #else + {"INTERNAL_ERROR", 36, 113}, + #endif + #ifdef RAND_R_INVALID_PROPERTY_QUERY + {"INVALID_PROPERTY_QUERY", ERR_LIB_RAND, RAND_R_INVALID_PROPERTY_QUERY}, + #else + {"INVALID_PROPERTY_QUERY", 36, 137}, + #endif + #ifdef RAND_R_IN_ERROR_STATE + {"IN_ERROR_STATE", ERR_LIB_RAND, RAND_R_IN_ERROR_STATE}, + #else + {"IN_ERROR_STATE", 36, 114}, + #endif + #ifdef RAND_R_NOT_A_REGULAR_FILE + {"NOT_A_REGULAR_FILE", ERR_LIB_RAND, RAND_R_NOT_A_REGULAR_FILE}, + #else + {"NOT_A_REGULAR_FILE", 36, 122}, + #endif + #ifdef RAND_R_NOT_INSTANTIATED + {"NOT_INSTANTIATED", ERR_LIB_RAND, RAND_R_NOT_INSTANTIATED}, + #else + {"NOT_INSTANTIATED", 36, 115}, + #endif + #ifdef RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED + {"NO_DRBG_IMPLEMENTATION_SELECTED", ERR_LIB_RAND, RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED}, + #else + {"NO_DRBG_IMPLEMENTATION_SELECTED", 36, 128}, + #endif + #ifdef RAND_R_PARENT_LOCKING_NOT_ENABLED + {"PARENT_LOCKING_NOT_ENABLED", ERR_LIB_RAND, RAND_R_PARENT_LOCKING_NOT_ENABLED}, + #else + {"PARENT_LOCKING_NOT_ENABLED", 36, 130}, + #endif + #ifdef RAND_R_PARENT_STRENGTH_TOO_WEAK + {"PARENT_STRENGTH_TOO_WEAK", ERR_LIB_RAND, RAND_R_PARENT_STRENGTH_TOO_WEAK}, + #else + {"PARENT_STRENGTH_TOO_WEAK", 36, 131}, + #endif + #ifdef RAND_R_PERSONALISATION_STRING_TOO_LONG + {"PERSONALISATION_STRING_TOO_LONG", ERR_LIB_RAND, RAND_R_PERSONALISATION_STRING_TOO_LONG}, + #else + {"PERSONALISATION_STRING_TOO_LONG", 36, 116}, + #endif + #ifdef RAND_R_PREDICTION_RESISTANCE_NOT_SUPPORTED + {"PREDICTION_RESISTANCE_NOT_SUPPORTED", ERR_LIB_RAND, RAND_R_PREDICTION_RESISTANCE_NOT_SUPPORTED}, + #else + {"PREDICTION_RESISTANCE_NOT_SUPPORTED", 36, 133}, + #endif + #ifdef RAND_R_PRNG_NOT_SEEDED + {"PRNG_NOT_SEEDED", ERR_LIB_RAND, RAND_R_PRNG_NOT_SEEDED}, + #else + {"PRNG_NOT_SEEDED", 36, 100}, + #endif + #ifdef RAND_R_RANDOM_POOL_IS_EMPTY + {"RANDOM_POOL_IS_EMPTY", ERR_LIB_RAND, RAND_R_RANDOM_POOL_IS_EMPTY}, + #else + {"RANDOM_POOL_IS_EMPTY", 36, 142}, + #endif + #ifdef RAND_R_RANDOM_POOL_OVERFLOW + {"RANDOM_POOL_OVERFLOW", ERR_LIB_RAND, RAND_R_RANDOM_POOL_OVERFLOW}, + #else + {"RANDOM_POOL_OVERFLOW", 36, 125}, + #endif + #ifdef RAND_R_RANDOM_POOL_UNDERFLOW + {"RANDOM_POOL_UNDERFLOW", ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW}, + #else + {"RANDOM_POOL_UNDERFLOW", 36, 134}, + #endif + #ifdef RAND_R_REQUEST_TOO_LARGE_FOR_DRBG + {"REQUEST_TOO_LARGE_FOR_DRBG", ERR_LIB_RAND, RAND_R_REQUEST_TOO_LARGE_FOR_DRBG}, + #else + {"REQUEST_TOO_LARGE_FOR_DRBG", 36, 117}, + #endif + #ifdef RAND_R_RESEED_ERROR + {"RESEED_ERROR", ERR_LIB_RAND, RAND_R_RESEED_ERROR}, + #else + {"RESEED_ERROR", 36, 118}, + #endif + #ifdef RAND_R_SELFTEST_FAILURE + {"SELFTEST_FAILURE", ERR_LIB_RAND, RAND_R_SELFTEST_FAILURE}, + #else + {"SELFTEST_FAILURE", 36, 119}, + #endif + #ifdef RAND_R_TOO_LITTLE_NONCE_REQUESTED + {"TOO_LITTLE_NONCE_REQUESTED", ERR_LIB_RAND, RAND_R_TOO_LITTLE_NONCE_REQUESTED}, + #else + {"TOO_LITTLE_NONCE_REQUESTED", 36, 135}, + #endif + #ifdef RAND_R_TOO_MUCH_NONCE_REQUESTED + {"TOO_MUCH_NONCE_REQUESTED", ERR_LIB_RAND, RAND_R_TOO_MUCH_NONCE_REQUESTED}, + #else + {"TOO_MUCH_NONCE_REQUESTED", 36, 136}, + #endif + #ifdef RAND_R_UNABLE_TO_CREATE_DRBG + {"UNABLE_TO_CREATE_DRBG", ERR_LIB_RAND, RAND_R_UNABLE_TO_CREATE_DRBG}, + #else + {"UNABLE_TO_CREATE_DRBG", 36, 143}, + #endif + #ifdef RAND_R_UNABLE_TO_FETCH_DRBG + {"UNABLE_TO_FETCH_DRBG", ERR_LIB_RAND, RAND_R_UNABLE_TO_FETCH_DRBG}, + #else + {"UNABLE_TO_FETCH_DRBG", 36, 144}, + #endif + #ifdef RAND_R_UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER + {"UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER", ERR_LIB_RAND, RAND_R_UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER}, + #else + {"UNABLE_TO_GET_PARENT_RESEED_PROP_COUNTER", 36, 141}, + #endif + #ifdef RAND_R_UNABLE_TO_GET_PARENT_STRENGTH + {"UNABLE_TO_GET_PARENT_STRENGTH", ERR_LIB_RAND, RAND_R_UNABLE_TO_GET_PARENT_STRENGTH}, + #else + {"UNABLE_TO_GET_PARENT_STRENGTH", 36, 138}, + #endif + #ifdef RAND_R_UNABLE_TO_LOCK_PARENT + {"UNABLE_TO_LOCK_PARENT", ERR_LIB_RAND, RAND_R_UNABLE_TO_LOCK_PARENT}, + #else + {"UNABLE_TO_LOCK_PARENT", 36, 140}, + #endif + #ifdef RAND_R_UNSUPPORTED_DRBG_FLAGS + {"UNSUPPORTED_DRBG_FLAGS", ERR_LIB_RAND, RAND_R_UNSUPPORTED_DRBG_FLAGS}, + #else + {"UNSUPPORTED_DRBG_FLAGS", 36, 132}, + #endif + #ifdef RAND_R_UNSUPPORTED_DRBG_TYPE + {"UNSUPPORTED_DRBG_TYPE", ERR_LIB_RAND, RAND_R_UNSUPPORTED_DRBG_TYPE}, + #else + {"UNSUPPORTED_DRBG_TYPE", 36, 120}, + #endif + #ifdef RSA_R_ALGORITHM_MISMATCH + {"ALGORITHM_MISMATCH", ERR_LIB_RSA, RSA_R_ALGORITHM_MISMATCH}, + #else + {"ALGORITHM_MISMATCH", 4, 100}, + #endif + #ifdef RSA_R_BAD_E_VALUE + {"BAD_E_VALUE", ERR_LIB_RSA, RSA_R_BAD_E_VALUE}, + #else + {"BAD_E_VALUE", 4, 101}, + #endif + #ifdef RSA_R_BAD_FIXED_HEADER_DECRYPT + {"BAD_FIXED_HEADER_DECRYPT", ERR_LIB_RSA, RSA_R_BAD_FIXED_HEADER_DECRYPT}, + #else + {"BAD_FIXED_HEADER_DECRYPT", 4, 102}, + #endif + #ifdef RSA_R_BAD_PAD_BYTE_COUNT + {"BAD_PAD_BYTE_COUNT", ERR_LIB_RSA, RSA_R_BAD_PAD_BYTE_COUNT}, + #else + {"BAD_PAD_BYTE_COUNT", 4, 103}, + #endif + #ifdef RSA_R_BAD_SIGNATURE + {"BAD_SIGNATURE", ERR_LIB_RSA, RSA_R_BAD_SIGNATURE}, + #else + {"BAD_SIGNATURE", 4, 104}, + #endif + #ifdef RSA_R_BLOCK_TYPE_IS_NOT_01 + {"BLOCK_TYPE_IS_NOT_01", ERR_LIB_RSA, RSA_R_BLOCK_TYPE_IS_NOT_01}, + #else + {"BLOCK_TYPE_IS_NOT_01", 4, 106}, + #endif + #ifdef RSA_R_BLOCK_TYPE_IS_NOT_02 + {"BLOCK_TYPE_IS_NOT_02", ERR_LIB_RSA, RSA_R_BLOCK_TYPE_IS_NOT_02}, + #else + {"BLOCK_TYPE_IS_NOT_02", 4, 107}, + #endif + #ifdef RSA_R_DATA_GREATER_THAN_MOD_LEN + {"DATA_GREATER_THAN_MOD_LEN", ERR_LIB_RSA, RSA_R_DATA_GREATER_THAN_MOD_LEN}, + #else + {"DATA_GREATER_THAN_MOD_LEN", 4, 108}, + #endif + #ifdef RSA_R_DATA_TOO_LARGE + {"DATA_TOO_LARGE", ERR_LIB_RSA, RSA_R_DATA_TOO_LARGE}, + #else + {"DATA_TOO_LARGE", 4, 109}, + #endif + #ifdef RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE + {"DATA_TOO_LARGE_FOR_KEY_SIZE", ERR_LIB_RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE}, + #else + {"DATA_TOO_LARGE_FOR_KEY_SIZE", 4, 110}, + #endif + #ifdef RSA_R_DATA_TOO_LARGE_FOR_MODULUS + {"DATA_TOO_LARGE_FOR_MODULUS", ERR_LIB_RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS}, + #else + {"DATA_TOO_LARGE_FOR_MODULUS", 4, 132}, + #endif + #ifdef RSA_R_DATA_TOO_SMALL + {"DATA_TOO_SMALL", ERR_LIB_RSA, RSA_R_DATA_TOO_SMALL}, + #else + {"DATA_TOO_SMALL", 4, 111}, + #endif + #ifdef RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE + {"DATA_TOO_SMALL_FOR_KEY_SIZE", ERR_LIB_RSA, RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE}, + #else + {"DATA_TOO_SMALL_FOR_KEY_SIZE", 4, 122}, + #endif + #ifdef RSA_R_DIGEST_DOES_NOT_MATCH + {"DIGEST_DOES_NOT_MATCH", ERR_LIB_RSA, RSA_R_DIGEST_DOES_NOT_MATCH}, + #else + {"DIGEST_DOES_NOT_MATCH", 4, 158}, + #endif + #ifdef RSA_R_DIGEST_NOT_ALLOWED + {"DIGEST_NOT_ALLOWED", ERR_LIB_RSA, RSA_R_DIGEST_NOT_ALLOWED}, + #else + {"DIGEST_NOT_ALLOWED", 4, 145}, + #endif + #ifdef RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY + {"DIGEST_TOO_BIG_FOR_RSA_KEY", ERR_LIB_RSA, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY}, + #else + {"DIGEST_TOO_BIG_FOR_RSA_KEY", 4, 112}, + #endif + #ifdef RSA_R_DMP1_NOT_CONGRUENT_TO_D + {"DMP1_NOT_CONGRUENT_TO_D", ERR_LIB_RSA, RSA_R_DMP1_NOT_CONGRUENT_TO_D}, + #else + {"DMP1_NOT_CONGRUENT_TO_D", 4, 124}, + #endif + #ifdef RSA_R_DMQ1_NOT_CONGRUENT_TO_D + {"DMQ1_NOT_CONGRUENT_TO_D", ERR_LIB_RSA, RSA_R_DMQ1_NOT_CONGRUENT_TO_D}, + #else + {"DMQ1_NOT_CONGRUENT_TO_D", 4, 125}, + #endif + #ifdef RSA_R_D_E_NOT_CONGRUENT_TO_1 + {"D_E_NOT_CONGRUENT_TO_1", ERR_LIB_RSA, RSA_R_D_E_NOT_CONGRUENT_TO_1}, + #else + {"D_E_NOT_CONGRUENT_TO_1", 4, 123}, + #endif + #ifdef RSA_R_FIRST_OCTET_INVALID + {"FIRST_OCTET_INVALID", ERR_LIB_RSA, RSA_R_FIRST_OCTET_INVALID}, + #else + {"FIRST_OCTET_INVALID", 4, 133}, + #endif + #ifdef RSA_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE + {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", ERR_LIB_RSA, RSA_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE}, + #else + {"ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", 4, 144}, + #endif + #ifdef RSA_R_INVALID_DIGEST + {"INVALID_DIGEST", ERR_LIB_RSA, RSA_R_INVALID_DIGEST}, + #else + {"INVALID_DIGEST", 4, 157}, + #endif + #ifdef RSA_R_INVALID_DIGEST_LENGTH + {"INVALID_DIGEST_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_DIGEST_LENGTH}, + #else + {"INVALID_DIGEST_LENGTH", 4, 143}, + #endif + #ifdef RSA_R_INVALID_HEADER + {"INVALID_HEADER", ERR_LIB_RSA, RSA_R_INVALID_HEADER}, + #else + {"INVALID_HEADER", 4, 137}, + #endif + #ifdef RSA_R_INVALID_KEYPAIR + {"INVALID_KEYPAIR", ERR_LIB_RSA, RSA_R_INVALID_KEYPAIR}, + #else + {"INVALID_KEYPAIR", 4, 171}, + #endif + #ifdef RSA_R_INVALID_KEY_LENGTH + {"INVALID_KEY_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_KEY_LENGTH}, + #else + {"INVALID_KEY_LENGTH", 4, 173}, + #endif + #ifdef RSA_R_INVALID_LABEL + {"INVALID_LABEL", ERR_LIB_RSA, RSA_R_INVALID_LABEL}, + #else + {"INVALID_LABEL", 4, 160}, + #endif + #ifdef RSA_R_INVALID_LENGTH + {"INVALID_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_LENGTH}, + #else + {"INVALID_LENGTH", 4, 181}, + #endif + #ifdef RSA_R_INVALID_MESSAGE_LENGTH + {"INVALID_MESSAGE_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_MESSAGE_LENGTH}, + #else + {"INVALID_MESSAGE_LENGTH", 4, 131}, + #endif + #ifdef RSA_R_INVALID_MGF1_MD + {"INVALID_MGF1_MD", ERR_LIB_RSA, RSA_R_INVALID_MGF1_MD}, + #else + {"INVALID_MGF1_MD", 4, 156}, + #endif + #ifdef RSA_R_INVALID_MODULUS + {"INVALID_MODULUS", ERR_LIB_RSA, RSA_R_INVALID_MODULUS}, + #else + {"INVALID_MODULUS", 4, 174}, + #endif + #ifdef RSA_R_INVALID_MULTI_PRIME_KEY + {"INVALID_MULTI_PRIME_KEY", ERR_LIB_RSA, RSA_R_INVALID_MULTI_PRIME_KEY}, + #else + {"INVALID_MULTI_PRIME_KEY", 4, 167}, + #endif + #ifdef RSA_R_INVALID_OAEP_PARAMETERS + {"INVALID_OAEP_PARAMETERS", ERR_LIB_RSA, RSA_R_INVALID_OAEP_PARAMETERS}, + #else + {"INVALID_OAEP_PARAMETERS", 4, 161}, + #endif + #ifdef RSA_R_INVALID_PADDING + {"INVALID_PADDING", ERR_LIB_RSA, RSA_R_INVALID_PADDING}, + #else + {"INVALID_PADDING", 4, 138}, + #endif + #ifdef RSA_R_INVALID_PADDING_MODE + {"INVALID_PADDING_MODE", ERR_LIB_RSA, RSA_R_INVALID_PADDING_MODE}, + #else + {"INVALID_PADDING_MODE", 4, 141}, + #endif + #ifdef RSA_R_INVALID_PSS_PARAMETERS + {"INVALID_PSS_PARAMETERS", ERR_LIB_RSA, RSA_R_INVALID_PSS_PARAMETERS}, + #else + {"INVALID_PSS_PARAMETERS", 4, 149}, + #endif + #ifdef RSA_R_INVALID_PSS_SALTLEN + {"INVALID_PSS_SALTLEN", ERR_LIB_RSA, RSA_R_INVALID_PSS_SALTLEN}, + #else + {"INVALID_PSS_SALTLEN", 4, 146}, + #endif + #ifdef RSA_R_INVALID_REQUEST + {"INVALID_REQUEST", ERR_LIB_RSA, RSA_R_INVALID_REQUEST}, + #else + {"INVALID_REQUEST", 4, 175}, + #endif + #ifdef RSA_R_INVALID_SALT_LENGTH + {"INVALID_SALT_LENGTH", ERR_LIB_RSA, RSA_R_INVALID_SALT_LENGTH}, + #else + {"INVALID_SALT_LENGTH", 4, 150}, + #endif + #ifdef RSA_R_INVALID_STRENGTH + {"INVALID_STRENGTH", ERR_LIB_RSA, RSA_R_INVALID_STRENGTH}, + #else + {"INVALID_STRENGTH", 4, 176}, + #endif + #ifdef RSA_R_INVALID_TRAILER + {"INVALID_TRAILER", ERR_LIB_RSA, RSA_R_INVALID_TRAILER}, + #else + {"INVALID_TRAILER", 4, 139}, + #endif + #ifdef RSA_R_INVALID_X931_DIGEST + {"INVALID_X931_DIGEST", ERR_LIB_RSA, RSA_R_INVALID_X931_DIGEST}, + #else + {"INVALID_X931_DIGEST", 4, 142}, + #endif + #ifdef RSA_R_IQMP_NOT_INVERSE_OF_Q + {"IQMP_NOT_INVERSE_OF_Q", ERR_LIB_RSA, RSA_R_IQMP_NOT_INVERSE_OF_Q}, + #else + {"IQMP_NOT_INVERSE_OF_Q", 4, 126}, + #endif + #ifdef RSA_R_KEY_PRIME_NUM_INVALID + {"KEY_PRIME_NUM_INVALID", ERR_LIB_RSA, RSA_R_KEY_PRIME_NUM_INVALID}, + #else + {"KEY_PRIME_NUM_INVALID", 4, 165}, + #endif + #ifdef RSA_R_KEY_SIZE_TOO_SMALL + {"KEY_SIZE_TOO_SMALL", ERR_LIB_RSA, RSA_R_KEY_SIZE_TOO_SMALL}, + #else + {"KEY_SIZE_TOO_SMALL", 4, 120}, + #endif + #ifdef RSA_R_LAST_OCTET_INVALID + {"LAST_OCTET_INVALID", ERR_LIB_RSA, RSA_R_LAST_OCTET_INVALID}, + #else + {"LAST_OCTET_INVALID", 4, 134}, + #endif + #ifdef RSA_R_MGF1_DIGEST_NOT_ALLOWED + {"MGF1_DIGEST_NOT_ALLOWED", ERR_LIB_RSA, RSA_R_MGF1_DIGEST_NOT_ALLOWED}, + #else + {"MGF1_DIGEST_NOT_ALLOWED", 4, 152}, + #endif + #ifdef RSA_R_MISSING_PRIVATE_KEY + {"MISSING_PRIVATE_KEY", ERR_LIB_RSA, RSA_R_MISSING_PRIVATE_KEY}, + #else + {"MISSING_PRIVATE_KEY", 4, 179}, + #endif + #ifdef RSA_R_MODULUS_TOO_LARGE + {"MODULUS_TOO_LARGE", ERR_LIB_RSA, RSA_R_MODULUS_TOO_LARGE}, + #else + {"MODULUS_TOO_LARGE", 4, 105}, + #endif + #ifdef RSA_R_MP_COEFFICIENT_NOT_INVERSE_OF_R + {"MP_COEFFICIENT_NOT_INVERSE_OF_R", ERR_LIB_RSA, RSA_R_MP_COEFFICIENT_NOT_INVERSE_OF_R}, + #else + {"MP_COEFFICIENT_NOT_INVERSE_OF_R", 4, 168}, + #endif + #ifdef RSA_R_MP_EXPONENT_NOT_CONGRUENT_TO_D + {"MP_EXPONENT_NOT_CONGRUENT_TO_D", ERR_LIB_RSA, RSA_R_MP_EXPONENT_NOT_CONGRUENT_TO_D}, + #else + {"MP_EXPONENT_NOT_CONGRUENT_TO_D", 4, 169}, + #endif + #ifdef RSA_R_MP_R_NOT_PRIME + {"MP_R_NOT_PRIME", ERR_LIB_RSA, RSA_R_MP_R_NOT_PRIME}, + #else + {"MP_R_NOT_PRIME", 4, 170}, + #endif + #ifdef RSA_R_NO_PUBLIC_EXPONENT + {"NO_PUBLIC_EXPONENT", ERR_LIB_RSA, RSA_R_NO_PUBLIC_EXPONENT}, + #else + {"NO_PUBLIC_EXPONENT", 4, 140}, + #endif + #ifdef RSA_R_NULL_BEFORE_BLOCK_MISSING + {"NULL_BEFORE_BLOCK_MISSING", ERR_LIB_RSA, RSA_R_NULL_BEFORE_BLOCK_MISSING}, + #else + {"NULL_BEFORE_BLOCK_MISSING", 4, 113}, + #endif + #ifdef RSA_R_N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES + {"N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES", ERR_LIB_RSA, RSA_R_N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES}, + #else + {"N_DOES_NOT_EQUAL_PRODUCT_OF_PRIMES", 4, 172}, + #endif + #ifdef RSA_R_N_DOES_NOT_EQUAL_P_Q + {"N_DOES_NOT_EQUAL_P_Q", ERR_LIB_RSA, RSA_R_N_DOES_NOT_EQUAL_P_Q}, + #else + {"N_DOES_NOT_EQUAL_P_Q", 4, 127}, + #endif + #ifdef RSA_R_OAEP_DECODING_ERROR + {"OAEP_DECODING_ERROR", ERR_LIB_RSA, RSA_R_OAEP_DECODING_ERROR}, + #else + {"OAEP_DECODING_ERROR", 4, 121}, + #endif + #ifdef RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", ERR_LIB_RSA, RSA_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE}, + #else + {"OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE", 4, 148}, + #endif + #ifdef RSA_R_PADDING_CHECK_FAILED + {"PADDING_CHECK_FAILED", ERR_LIB_RSA, RSA_R_PADDING_CHECK_FAILED}, + #else + {"PADDING_CHECK_FAILED", 4, 114}, + #endif + #ifdef RSA_R_PAIRWISE_TEST_FAILURE + {"PAIRWISE_TEST_FAILURE", ERR_LIB_RSA, RSA_R_PAIRWISE_TEST_FAILURE}, + #else + {"PAIRWISE_TEST_FAILURE", 4, 177}, + #endif + #ifdef RSA_R_PKCS_DECODING_ERROR + {"PKCS_DECODING_ERROR", ERR_LIB_RSA, RSA_R_PKCS_DECODING_ERROR}, + #else + {"PKCS_DECODING_ERROR", 4, 159}, + #endif + #ifdef RSA_R_PSS_SALTLEN_TOO_SMALL + {"PSS_SALTLEN_TOO_SMALL", ERR_LIB_RSA, RSA_R_PSS_SALTLEN_TOO_SMALL}, + #else + {"PSS_SALTLEN_TOO_SMALL", 4, 164}, + #endif + #ifdef RSA_R_PUB_EXPONENT_OUT_OF_RANGE + {"PUB_EXPONENT_OUT_OF_RANGE", ERR_LIB_RSA, RSA_R_PUB_EXPONENT_OUT_OF_RANGE}, + #else + {"PUB_EXPONENT_OUT_OF_RANGE", 4, 178}, + #endif + #ifdef RSA_R_P_NOT_PRIME + {"P_NOT_PRIME", ERR_LIB_RSA, RSA_R_P_NOT_PRIME}, + #else + {"P_NOT_PRIME", 4, 128}, + #endif + #ifdef RSA_R_Q_NOT_PRIME + {"Q_NOT_PRIME", ERR_LIB_RSA, RSA_R_Q_NOT_PRIME}, + #else + {"Q_NOT_PRIME", 4, 129}, + #endif + #ifdef RSA_R_RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT + {"RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT", ERR_LIB_RSA, RSA_R_RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT}, + #else + {"RANDOMNESS_SOURCE_STRENGTH_INSUFFICIENT", 4, 180}, + #endif + #ifdef RSA_R_RSA_OPERATIONS_NOT_SUPPORTED + {"RSA_OPERATIONS_NOT_SUPPORTED", ERR_LIB_RSA, RSA_R_RSA_OPERATIONS_NOT_SUPPORTED}, + #else + {"RSA_OPERATIONS_NOT_SUPPORTED", 4, 130}, + #endif + #ifdef RSA_R_SLEN_CHECK_FAILED + {"SLEN_CHECK_FAILED", ERR_LIB_RSA, RSA_R_SLEN_CHECK_FAILED}, + #else + {"SLEN_CHECK_FAILED", 4, 136}, + #endif + #ifdef RSA_R_SLEN_RECOVERY_FAILED + {"SLEN_RECOVERY_FAILED", ERR_LIB_RSA, RSA_R_SLEN_RECOVERY_FAILED}, + #else + {"SLEN_RECOVERY_FAILED", 4, 135}, + #endif + #ifdef RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD + {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", ERR_LIB_RSA, RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD}, + #else + {"THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD", 4, 116}, + #endif + #ifdef RSA_R_UNKNOWN_ALGORITHM_TYPE + {"UNKNOWN_ALGORITHM_TYPE", ERR_LIB_RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE}, + #else + {"UNKNOWN_ALGORITHM_TYPE", 4, 117}, + #endif + #ifdef RSA_R_UNKNOWN_DIGEST + {"UNKNOWN_DIGEST", ERR_LIB_RSA, RSA_R_UNKNOWN_DIGEST}, + #else + {"UNKNOWN_DIGEST", 4, 166}, + #endif + #ifdef RSA_R_UNKNOWN_MASK_DIGEST + {"UNKNOWN_MASK_DIGEST", ERR_LIB_RSA, RSA_R_UNKNOWN_MASK_DIGEST}, + #else + {"UNKNOWN_MASK_DIGEST", 4, 151}, + #endif + #ifdef RSA_R_UNKNOWN_PADDING_TYPE + {"UNKNOWN_PADDING_TYPE", ERR_LIB_RSA, RSA_R_UNKNOWN_PADDING_TYPE}, + #else + {"UNKNOWN_PADDING_TYPE", 4, 118}, + #endif + #ifdef RSA_R_UNSUPPORTED_ENCRYPTION_TYPE + {"UNSUPPORTED_ENCRYPTION_TYPE", ERR_LIB_RSA, RSA_R_UNSUPPORTED_ENCRYPTION_TYPE}, + #else + {"UNSUPPORTED_ENCRYPTION_TYPE", 4, 162}, + #endif + #ifdef RSA_R_UNSUPPORTED_LABEL_SOURCE + {"UNSUPPORTED_LABEL_SOURCE", ERR_LIB_RSA, RSA_R_UNSUPPORTED_LABEL_SOURCE}, + #else + {"UNSUPPORTED_LABEL_SOURCE", 4, 163}, + #endif + #ifdef RSA_R_UNSUPPORTED_MASK_ALGORITHM + {"UNSUPPORTED_MASK_ALGORITHM", ERR_LIB_RSA, RSA_R_UNSUPPORTED_MASK_ALGORITHM}, + #else + {"UNSUPPORTED_MASK_ALGORITHM", 4, 153}, + #endif + #ifdef RSA_R_UNSUPPORTED_MASK_PARAMETER + {"UNSUPPORTED_MASK_PARAMETER", ERR_LIB_RSA, RSA_R_UNSUPPORTED_MASK_PARAMETER}, + #else + {"UNSUPPORTED_MASK_PARAMETER", 4, 154}, + #endif + #ifdef RSA_R_UNSUPPORTED_SIGNATURE_TYPE + {"UNSUPPORTED_SIGNATURE_TYPE", ERR_LIB_RSA, RSA_R_UNSUPPORTED_SIGNATURE_TYPE}, + #else + {"UNSUPPORTED_SIGNATURE_TYPE", 4, 155}, + #endif + #ifdef RSA_R_VALUE_MISSING + {"VALUE_MISSING", ERR_LIB_RSA, RSA_R_VALUE_MISSING}, + #else + {"VALUE_MISSING", 4, 147}, + #endif + #ifdef RSA_R_WRONG_SIGNATURE_LENGTH + {"WRONG_SIGNATURE_LENGTH", ERR_LIB_RSA, RSA_R_WRONG_SIGNATURE_LENGTH}, + #else + {"WRONG_SIGNATURE_LENGTH", 4, 119}, + #endif + #ifdef SM2_R_ASN1_ERROR + {"ASN1_ERROR", ERR_LIB_SM2, SM2_R_ASN1_ERROR}, + #else + {"ASN1_ERROR", 53, 100}, + #endif + #ifdef SM2_R_BAD_SIGNATURE + {"BAD_SIGNATURE", ERR_LIB_SM2, SM2_R_BAD_SIGNATURE}, + #else + {"BAD_SIGNATURE", 53, 101}, + #endif + #ifdef SM2_R_BUFFER_TOO_SMALL + {"BUFFER_TOO_SMALL", ERR_LIB_SM2, SM2_R_BUFFER_TOO_SMALL}, + #else + {"BUFFER_TOO_SMALL", 53, 107}, + #endif + #ifdef SM2_R_DIST_ID_TOO_LARGE + {"DIST_ID_TOO_LARGE", ERR_LIB_SM2, SM2_R_DIST_ID_TOO_LARGE}, + #else + {"DIST_ID_TOO_LARGE", 53, 110}, + #endif + #ifdef SM2_R_ID_NOT_SET + {"ID_NOT_SET", ERR_LIB_SM2, SM2_R_ID_NOT_SET}, + #else + {"ID_NOT_SET", 53, 112}, + #endif + #ifdef SM2_R_ID_TOO_LARGE + {"ID_TOO_LARGE", ERR_LIB_SM2, SM2_R_ID_TOO_LARGE}, + #else + {"ID_TOO_LARGE", 53, 111}, + #endif + #ifdef SM2_R_INVALID_CURVE + {"INVALID_CURVE", ERR_LIB_SM2, SM2_R_INVALID_CURVE}, + #else + {"INVALID_CURVE", 53, 108}, + #endif + #ifdef SM2_R_INVALID_DIGEST + {"INVALID_DIGEST", ERR_LIB_SM2, SM2_R_INVALID_DIGEST}, + #else + {"INVALID_DIGEST", 53, 102}, + #endif + #ifdef SM2_R_INVALID_DIGEST_TYPE + {"INVALID_DIGEST_TYPE", ERR_LIB_SM2, SM2_R_INVALID_DIGEST_TYPE}, + #else + {"INVALID_DIGEST_TYPE", 53, 103}, + #endif + #ifdef SM2_R_INVALID_ENCODING + {"INVALID_ENCODING", ERR_LIB_SM2, SM2_R_INVALID_ENCODING}, + #else + {"INVALID_ENCODING", 53, 104}, + #endif + #ifdef SM2_R_INVALID_FIELD + {"INVALID_FIELD", ERR_LIB_SM2, SM2_R_INVALID_FIELD}, + #else + {"INVALID_FIELD", 53, 105}, + #endif + #ifdef SM2_R_INVALID_PRIVATE_KEY + {"INVALID_PRIVATE_KEY", ERR_LIB_SM2, SM2_R_INVALID_PRIVATE_KEY}, + #else + {"INVALID_PRIVATE_KEY", 53, 113}, + #endif + #ifdef SM2_R_NO_PARAMETERS_SET + {"NO_PARAMETERS_SET", ERR_LIB_SM2, SM2_R_NO_PARAMETERS_SET}, + #else + {"NO_PARAMETERS_SET", 53, 109}, + #endif + #ifdef SM2_R_USER_ID_TOO_LARGE + {"USER_ID_TOO_LARGE", ERR_LIB_SM2, SM2_R_USER_ID_TOO_LARGE}, + #else + {"USER_ID_TOO_LARGE", 53, 106}, + #endif + #ifdef SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY + {"APPLICATION_DATA_AFTER_CLOSE_NOTIFY", ERR_LIB_SSL, SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY}, + #else + {"APPLICATION_DATA_AFTER_CLOSE_NOTIFY", 20, 291}, + #endif + #ifdef SSL_R_APP_DATA_IN_HANDSHAKE + {"APP_DATA_IN_HANDSHAKE", ERR_LIB_SSL, SSL_R_APP_DATA_IN_HANDSHAKE}, + #else + {"APP_DATA_IN_HANDSHAKE", 20, 100}, + #endif + #ifdef SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT + {"ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT", ERR_LIB_SSL, SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT}, + #else + {"ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT", 20, 272}, + #endif + #ifdef SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE + {"AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE", ERR_LIB_SSL, SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE}, + #else + {"AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE", 20, 158}, + #endif + #ifdef SSL_R_BAD_CERTIFICATE + {"BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_BAD_CERTIFICATE}, + #else + {"BAD_CERTIFICATE", 20, 348}, + #endif + #ifdef SSL_R_BAD_CHANGE_CIPHER_SPEC + {"BAD_CHANGE_CIPHER_SPEC", ERR_LIB_SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC}, + #else + {"BAD_CHANGE_CIPHER_SPEC", 20, 103}, + #endif + #ifdef SSL_R_BAD_CIPHER + {"BAD_CIPHER", ERR_LIB_SSL, SSL_R_BAD_CIPHER}, + #else + {"BAD_CIPHER", 20, 186}, + #endif + #ifdef SSL_R_BAD_COMPRESSION_ALGORITHM + {"BAD_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_BAD_COMPRESSION_ALGORITHM}, + #else + {"BAD_COMPRESSION_ALGORITHM", 20, 326}, + #endif + #ifdef SSL_R_BAD_DATA + {"BAD_DATA", ERR_LIB_SSL, SSL_R_BAD_DATA}, + #else + {"BAD_DATA", 20, 390}, + #endif + #ifdef SSL_R_BAD_DATA_RETURNED_BY_CALLBACK + {"BAD_DATA_RETURNED_BY_CALLBACK", ERR_LIB_SSL, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK}, + #else + {"BAD_DATA_RETURNED_BY_CALLBACK", 20, 106}, + #endif + #ifdef SSL_R_BAD_DECOMPRESSION + {"BAD_DECOMPRESSION", ERR_LIB_SSL, SSL_R_BAD_DECOMPRESSION}, + #else + {"BAD_DECOMPRESSION", 20, 107}, + #endif + #ifdef SSL_R_BAD_DH_VALUE + {"BAD_DH_VALUE", ERR_LIB_SSL, SSL_R_BAD_DH_VALUE}, + #else + {"BAD_DH_VALUE", 20, 102}, + #endif + #ifdef SSL_R_BAD_DIGEST_LENGTH + {"BAD_DIGEST_LENGTH", ERR_LIB_SSL, SSL_R_BAD_DIGEST_LENGTH}, + #else + {"BAD_DIGEST_LENGTH", 20, 111}, + #endif + #ifdef SSL_R_BAD_EARLY_DATA + {"BAD_EARLY_DATA", ERR_LIB_SSL, SSL_R_BAD_EARLY_DATA}, + #else + {"BAD_EARLY_DATA", 20, 233}, + #endif + #ifdef SSL_R_BAD_ECC_CERT + {"BAD_ECC_CERT", ERR_LIB_SSL, SSL_R_BAD_ECC_CERT}, + #else + {"BAD_ECC_CERT", 20, 304}, + #endif + #ifdef SSL_R_BAD_ECHCONFIG_EXTENSION + {"BAD_ECHCONFIG_EXTENSION", ERR_LIB_SSL, SSL_R_BAD_ECHCONFIG_EXTENSION}, + #else + {"BAD_ECHCONFIG_EXTENSION", 20, 425}, + #endif + #ifdef SSL_R_BAD_ECPOINT + {"BAD_ECPOINT", ERR_LIB_SSL, SSL_R_BAD_ECPOINT}, + #else + {"BAD_ECPOINT", 20, 306}, + #endif + #ifdef SSL_R_BAD_EXTENSION + {"BAD_EXTENSION", ERR_LIB_SSL, SSL_R_BAD_EXTENSION}, + #else + {"BAD_EXTENSION", 20, 110}, + #endif + #ifdef SSL_R_BAD_HANDSHAKE_LENGTH + {"BAD_HANDSHAKE_LENGTH", ERR_LIB_SSL, SSL_R_BAD_HANDSHAKE_LENGTH}, + #else + {"BAD_HANDSHAKE_LENGTH", 20, 332}, + #endif + #ifdef SSL_R_BAD_HANDSHAKE_STATE + {"BAD_HANDSHAKE_STATE", ERR_LIB_SSL, SSL_R_BAD_HANDSHAKE_STATE}, + #else + {"BAD_HANDSHAKE_STATE", 20, 236}, + #endif + #ifdef SSL_R_BAD_HELLO_REQUEST + {"BAD_HELLO_REQUEST", ERR_LIB_SSL, SSL_R_BAD_HELLO_REQUEST}, + #else + {"BAD_HELLO_REQUEST", 20, 105}, + #endif + #ifdef SSL_R_BAD_HRR_VERSION + {"BAD_HRR_VERSION", ERR_LIB_SSL, SSL_R_BAD_HRR_VERSION}, + #else + {"BAD_HRR_VERSION", 20, 263}, + #endif + #ifdef SSL_R_BAD_KEY_SHARE + {"BAD_KEY_SHARE", ERR_LIB_SSL, SSL_R_BAD_KEY_SHARE}, + #else + {"BAD_KEY_SHARE", 20, 108}, + #endif + #ifdef SSL_R_BAD_KEY_UPDATE + {"BAD_KEY_UPDATE", ERR_LIB_SSL, SSL_R_BAD_KEY_UPDATE}, + #else + {"BAD_KEY_UPDATE", 20, 122}, + #endif + #ifdef SSL_R_BAD_LEGACY_VERSION + {"BAD_LEGACY_VERSION", ERR_LIB_SSL, SSL_R_BAD_LEGACY_VERSION}, + #else + {"BAD_LEGACY_VERSION", 20, 292}, + #endif + #ifdef SSL_R_BAD_LENGTH + {"BAD_LENGTH", ERR_LIB_SSL, SSL_R_BAD_LENGTH}, + #else + {"BAD_LENGTH", 20, 271}, + #endif + #ifdef SSL_R_BAD_PACKET + {"BAD_PACKET", ERR_LIB_SSL, SSL_R_BAD_PACKET}, + #else + {"BAD_PACKET", 20, 240}, + #endif + #ifdef SSL_R_BAD_PACKET_LENGTH + {"BAD_PACKET_LENGTH", ERR_LIB_SSL, SSL_R_BAD_PACKET_LENGTH}, + #else + {"BAD_PACKET_LENGTH", 20, 115}, + #endif + #ifdef SSL_R_BAD_PROTOCOL_VERSION_NUMBER + {"BAD_PROTOCOL_VERSION_NUMBER", ERR_LIB_SSL, SSL_R_BAD_PROTOCOL_VERSION_NUMBER}, + #else + {"BAD_PROTOCOL_VERSION_NUMBER", 20, 116}, + #endif + #ifdef SSL_R_BAD_PSK + {"BAD_PSK", ERR_LIB_SSL, SSL_R_BAD_PSK}, + #else + {"BAD_PSK", 20, 219}, + #endif + #ifdef SSL_R_BAD_PSK_IDENTITY + {"BAD_PSK_IDENTITY", ERR_LIB_SSL, SSL_R_BAD_PSK_IDENTITY}, + #else + {"BAD_PSK_IDENTITY", 20, 114}, + #endif + #ifdef SSL_R_BAD_RECORD_TYPE + {"BAD_RECORD_TYPE", ERR_LIB_SSL, SSL_R_BAD_RECORD_TYPE}, + #else + {"BAD_RECORD_TYPE", 20, 443}, + #endif + #ifdef SSL_R_BAD_RSA_ENCRYPT + {"BAD_RSA_ENCRYPT", ERR_LIB_SSL, SSL_R_BAD_RSA_ENCRYPT}, + #else + {"BAD_RSA_ENCRYPT", 20, 119}, + #endif + #ifdef SSL_R_BAD_SIGNATURE + {"BAD_SIGNATURE", ERR_LIB_SSL, SSL_R_BAD_SIGNATURE}, + #else + {"BAD_SIGNATURE", 20, 123}, + #endif + #ifdef SSL_R_BAD_SRP_A_LENGTH + {"BAD_SRP_A_LENGTH", ERR_LIB_SSL, SSL_R_BAD_SRP_A_LENGTH}, + #else + {"BAD_SRP_A_LENGTH", 20, 347}, + #endif + #ifdef SSL_R_BAD_SRP_PARAMETERS + {"BAD_SRP_PARAMETERS", ERR_LIB_SSL, SSL_R_BAD_SRP_PARAMETERS}, + #else + {"BAD_SRP_PARAMETERS", 20, 371}, + #endif + #ifdef SSL_R_BAD_SRTP_MKI_VALUE + {"BAD_SRTP_MKI_VALUE", ERR_LIB_SSL, SSL_R_BAD_SRTP_MKI_VALUE}, + #else + {"BAD_SRTP_MKI_VALUE", 20, 352}, + #endif + #ifdef SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST + {"BAD_SRTP_PROTECTION_PROFILE_LIST", ERR_LIB_SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST}, + #else + {"BAD_SRTP_PROTECTION_PROFILE_LIST", 20, 353}, + #endif + #ifdef SSL_R_BAD_SSL_FILETYPE + {"BAD_SSL_FILETYPE", ERR_LIB_SSL, SSL_R_BAD_SSL_FILETYPE}, + #else + {"BAD_SSL_FILETYPE", 20, 124}, + #endif + #ifdef SSL_R_BAD_VALUE + {"BAD_VALUE", ERR_LIB_SSL, SSL_R_BAD_VALUE}, + #else + {"BAD_VALUE", 20, 384}, + #endif + #ifdef SSL_R_BAD_WRITE_RETRY + {"BAD_WRITE_RETRY", ERR_LIB_SSL, SSL_R_BAD_WRITE_RETRY}, + #else + {"BAD_WRITE_RETRY", 20, 127}, + #endif + #ifdef SSL_R_BINDER_DOES_NOT_VERIFY + {"BINDER_DOES_NOT_VERIFY", ERR_LIB_SSL, SSL_R_BINDER_DOES_NOT_VERIFY}, + #else + {"BINDER_DOES_NOT_VERIFY", 20, 253}, + #endif + #ifdef SSL_R_BIO_NOT_SET + {"BIO_NOT_SET", ERR_LIB_SSL, SSL_R_BIO_NOT_SET}, + #else + {"BIO_NOT_SET", 20, 128}, + #endif + #ifdef SSL_R_BLOCK_CIPHER_PAD_IS_WRONG + {"BLOCK_CIPHER_PAD_IS_WRONG", ERR_LIB_SSL, SSL_R_BLOCK_CIPHER_PAD_IS_WRONG}, + #else + {"BLOCK_CIPHER_PAD_IS_WRONG", 20, 129}, + #endif + #ifdef SSL_R_BN_LIB + {"BN_LIB", ERR_LIB_SSL, SSL_R_BN_LIB}, + #else + {"BN_LIB", 20, 130}, + #endif + #ifdef SSL_R_CALLBACK_FAILED + {"CALLBACK_FAILED", ERR_LIB_SSL, SSL_R_CALLBACK_FAILED}, + #else + {"CALLBACK_FAILED", 20, 234}, + #endif + #ifdef SSL_R_CANNOT_CHANGE_CIPHER + {"CANNOT_CHANGE_CIPHER", ERR_LIB_SSL, SSL_R_CANNOT_CHANGE_CIPHER}, + #else + {"CANNOT_CHANGE_CIPHER", 20, 109}, + #endif + #ifdef SSL_R_CANNOT_GET_GROUP_NAME + {"CANNOT_GET_GROUP_NAME", ERR_LIB_SSL, SSL_R_CANNOT_GET_GROUP_NAME}, + #else + {"CANNOT_GET_GROUP_NAME", 20, 299}, + #endif + #ifdef SSL_R_CA_DN_LENGTH_MISMATCH + {"CA_DN_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_CA_DN_LENGTH_MISMATCH}, + #else + {"CA_DN_LENGTH_MISMATCH", 20, 131}, + #endif + #ifdef SSL_R_CA_KEY_TOO_SMALL + {"CA_KEY_TOO_SMALL", ERR_LIB_SSL, SSL_R_CA_KEY_TOO_SMALL}, + #else + {"CA_KEY_TOO_SMALL", 20, 397}, + #endif + #ifdef SSL_R_CA_MD_TOO_WEAK + {"CA_MD_TOO_WEAK", ERR_LIB_SSL, SSL_R_CA_MD_TOO_WEAK}, + #else + {"CA_MD_TOO_WEAK", 20, 398}, + #endif + #ifdef SSL_R_CCS_RECEIVED_EARLY + {"CCS_RECEIVED_EARLY", ERR_LIB_SSL, SSL_R_CCS_RECEIVED_EARLY}, + #else + {"CCS_RECEIVED_EARLY", 20, 133}, + #endif + #ifdef SSL_R_CERTIFICATE_VERIFY_FAILED + {"CERTIFICATE_VERIFY_FAILED", ERR_LIB_SSL, SSL_R_CERTIFICATE_VERIFY_FAILED}, + #else + {"CERTIFICATE_VERIFY_FAILED", 20, 134}, + #endif + #ifdef SSL_R_CERT_CB_ERROR + {"CERT_CB_ERROR", ERR_LIB_SSL, SSL_R_CERT_CB_ERROR}, + #else + {"CERT_CB_ERROR", 20, 377}, + #endif + #ifdef SSL_R_CERT_LENGTH_MISMATCH + {"CERT_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_CERT_LENGTH_MISMATCH}, + #else + {"CERT_LENGTH_MISMATCH", 20, 135}, + #endif + #ifdef SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED + {"CIPHERSUITE_DIGEST_HAS_CHANGED", ERR_LIB_SSL, SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED}, + #else + {"CIPHERSUITE_DIGEST_HAS_CHANGED", 20, 218}, + #endif + #ifdef SSL_R_CIPHER_CODE_WRONG_LENGTH + {"CIPHER_CODE_WRONG_LENGTH", ERR_LIB_SSL, SSL_R_CIPHER_CODE_WRONG_LENGTH}, + #else + {"CIPHER_CODE_WRONG_LENGTH", 20, 137}, + #endif + #ifdef SSL_R_CLIENTHELLO_TLSEXT + {"CLIENTHELLO_TLSEXT", ERR_LIB_SSL, SSL_R_CLIENTHELLO_TLSEXT}, + #else + {"CLIENTHELLO_TLSEXT", 20, 226}, + #endif + #ifdef SSL_R_COMPRESSED_LENGTH_TOO_LONG + {"COMPRESSED_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_COMPRESSED_LENGTH_TOO_LONG}, + #else + {"COMPRESSED_LENGTH_TOO_LONG", 20, 140}, + #endif + #ifdef SSL_R_COMPRESSION_DISABLED + {"COMPRESSION_DISABLED", ERR_LIB_SSL, SSL_R_COMPRESSION_DISABLED}, + #else + {"COMPRESSION_DISABLED", 20, 343}, + #endif + #ifdef SSL_R_COMPRESSION_FAILURE + {"COMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_COMPRESSION_FAILURE}, + #else + {"COMPRESSION_FAILURE", 20, 141}, + #endif + #ifdef SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE + {"COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE", ERR_LIB_SSL, SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE}, + #else + {"COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE", 20, 307}, + #endif + #ifdef SSL_R_COMPRESSION_LIBRARY_ERROR + {"COMPRESSION_LIBRARY_ERROR", ERR_LIB_SSL, SSL_R_COMPRESSION_LIBRARY_ERROR}, + #else + {"COMPRESSION_LIBRARY_ERROR", 20, 142}, + #endif + #ifdef SSL_R_CONNECTION_TYPE_NOT_SET + {"CONNECTION_TYPE_NOT_SET", ERR_LIB_SSL, SSL_R_CONNECTION_TYPE_NOT_SET}, + #else + {"CONNECTION_TYPE_NOT_SET", 20, 144}, + #endif + #ifdef SSL_R_CONN_USE_ONLY + {"CONN_USE_ONLY", ERR_LIB_SSL, SSL_R_CONN_USE_ONLY}, + #else + {"CONN_USE_ONLY", 20, 356}, + #endif + #ifdef SSL_R_CONTEXT_NOT_DANE_ENABLED + {"CONTEXT_NOT_DANE_ENABLED", ERR_LIB_SSL, SSL_R_CONTEXT_NOT_DANE_ENABLED}, + #else + {"CONTEXT_NOT_DANE_ENABLED", 20, 167}, + #endif + #ifdef SSL_R_COOKIE_GEN_CALLBACK_FAILURE + {"COOKIE_GEN_CALLBACK_FAILURE", ERR_LIB_SSL, SSL_R_COOKIE_GEN_CALLBACK_FAILURE}, + #else + {"COOKIE_GEN_CALLBACK_FAILURE", 20, 400}, + #endif + #ifdef SSL_R_COOKIE_MISMATCH + {"COOKIE_MISMATCH", ERR_LIB_SSL, SSL_R_COOKIE_MISMATCH}, + #else + {"COOKIE_MISMATCH", 20, 308}, + #endif + #ifdef SSL_R_COPY_PARAMETERS_FAILED + {"COPY_PARAMETERS_FAILED", ERR_LIB_SSL, SSL_R_COPY_PARAMETERS_FAILED}, + #else + {"COPY_PARAMETERS_FAILED", 20, 296}, + #endif + #ifdef SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED + {"CUSTOM_EXT_HANDLER_ALREADY_INSTALLED", ERR_LIB_SSL, SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED}, + #else + {"CUSTOM_EXT_HANDLER_ALREADY_INSTALLED", 20, 206}, + #endif + #ifdef SSL_R_DANE_ALREADY_ENABLED + {"DANE_ALREADY_ENABLED", ERR_LIB_SSL, SSL_R_DANE_ALREADY_ENABLED}, + #else + {"DANE_ALREADY_ENABLED", 20, 172}, + #endif + #ifdef SSL_R_DANE_CANNOT_OVERRIDE_MTYPE_FULL + {"DANE_CANNOT_OVERRIDE_MTYPE_FULL", ERR_LIB_SSL, SSL_R_DANE_CANNOT_OVERRIDE_MTYPE_FULL}, + #else + {"DANE_CANNOT_OVERRIDE_MTYPE_FULL", 20, 173}, + #endif + #ifdef SSL_R_DANE_NOT_ENABLED + {"DANE_NOT_ENABLED", ERR_LIB_SSL, SSL_R_DANE_NOT_ENABLED}, + #else + {"DANE_NOT_ENABLED", 20, 175}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_CERTIFICATE + {"DANE_TLSA_BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_CERTIFICATE}, + #else + {"DANE_TLSA_BAD_CERTIFICATE", 20, 180}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_CERTIFICATE_USAGE + {"DANE_TLSA_BAD_CERTIFICATE_USAGE", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_CERTIFICATE_USAGE}, + #else + {"DANE_TLSA_BAD_CERTIFICATE_USAGE", 20, 184}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_DATA_LENGTH + {"DANE_TLSA_BAD_DATA_LENGTH", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_DATA_LENGTH}, + #else + {"DANE_TLSA_BAD_DATA_LENGTH", 20, 189}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_DIGEST_LENGTH + {"DANE_TLSA_BAD_DIGEST_LENGTH", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_DIGEST_LENGTH}, + #else + {"DANE_TLSA_BAD_DIGEST_LENGTH", 20, 192}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_MATCHING_TYPE + {"DANE_TLSA_BAD_MATCHING_TYPE", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_MATCHING_TYPE}, + #else + {"DANE_TLSA_BAD_MATCHING_TYPE", 20, 200}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_PUBLIC_KEY + {"DANE_TLSA_BAD_PUBLIC_KEY", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_PUBLIC_KEY}, + #else + {"DANE_TLSA_BAD_PUBLIC_KEY", 20, 201}, + #endif + #ifdef SSL_R_DANE_TLSA_BAD_SELECTOR + {"DANE_TLSA_BAD_SELECTOR", ERR_LIB_SSL, SSL_R_DANE_TLSA_BAD_SELECTOR}, + #else + {"DANE_TLSA_BAD_SELECTOR", 20, 202}, + #endif + #ifdef SSL_R_DANE_TLSA_NULL_DATA + {"DANE_TLSA_NULL_DATA", ERR_LIB_SSL, SSL_R_DANE_TLSA_NULL_DATA}, + #else + {"DANE_TLSA_NULL_DATA", 20, 203}, + #endif + #ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED + {"DATA_BETWEEN_CCS_AND_FINISHED", ERR_LIB_SSL, SSL_R_DATA_BETWEEN_CCS_AND_FINISHED}, + #else + {"DATA_BETWEEN_CCS_AND_FINISHED", 20, 145}, + #endif + #ifdef SSL_R_DATA_LENGTH_TOO_LONG + {"DATA_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_DATA_LENGTH_TOO_LONG}, + #else + {"DATA_LENGTH_TOO_LONG", 20, 146}, + #endif + #ifdef SSL_R_DECRYPTION_FAILED + {"DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_DECRYPTION_FAILED}, + #else + {"DECRYPTION_FAILED", 20, 147}, + #endif + #ifdef SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC + {"DECRYPTION_FAILED_OR_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC}, + #else + {"DECRYPTION_FAILED_OR_BAD_RECORD_MAC", 20, 281}, + #endif + #ifdef SSL_R_DH_KEY_TOO_SMALL + {"DH_KEY_TOO_SMALL", ERR_LIB_SSL, SSL_R_DH_KEY_TOO_SMALL}, + #else + {"DH_KEY_TOO_SMALL", 20, 394}, + #endif + #ifdef SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG + {"DH_PUBLIC_VALUE_LENGTH_IS_WRONG", ERR_LIB_SSL, SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG}, + #else + {"DH_PUBLIC_VALUE_LENGTH_IS_WRONG", 20, 148}, + #endif + #ifdef SSL_R_DIGEST_CHECK_FAILED + {"DIGEST_CHECK_FAILED", ERR_LIB_SSL, SSL_R_DIGEST_CHECK_FAILED}, + #else + {"DIGEST_CHECK_FAILED", 20, 149}, + #endif + #ifdef SSL_R_DOMAIN_USE_ONLY + {"DOMAIN_USE_ONLY", ERR_LIB_SSL, SSL_R_DOMAIN_USE_ONLY}, + #else + {"DOMAIN_USE_ONLY", 20, 422}, + #endif + #ifdef SSL_R_DTLS_MESSAGE_TOO_BIG + {"DTLS_MESSAGE_TOO_BIG", ERR_LIB_SSL, SSL_R_DTLS_MESSAGE_TOO_BIG}, + #else + {"DTLS_MESSAGE_TOO_BIG", 20, 334}, + #endif + #ifdef SSL_R_DUPLICATE_COMPRESSION_ID + {"DUPLICATE_COMPRESSION_ID", ERR_LIB_SSL, SSL_R_DUPLICATE_COMPRESSION_ID}, + #else + {"DUPLICATE_COMPRESSION_ID", 20, 309}, + #endif + #ifdef SSL_R_ECC_CERT_NOT_FOR_SIGNING + {"ECC_CERT_NOT_FOR_SIGNING", ERR_LIB_SSL, SSL_R_ECC_CERT_NOT_FOR_SIGNING}, + #else + {"ECC_CERT_NOT_FOR_SIGNING", 20, 318}, + #endif + #ifdef SSL_R_ECDH_REQUIRED_FOR_SUITEB_MODE + {"ECDH_REQUIRED_FOR_SUITEB_MODE", ERR_LIB_SSL, SSL_R_ECDH_REQUIRED_FOR_SUITEB_MODE}, + #else + {"ECDH_REQUIRED_FOR_SUITEB_MODE", 20, 374}, + #endif + #ifdef SSL_R_ECH_DECODE_ERROR + {"ECH_DECODE_ERROR", ERR_LIB_SSL, SSL_R_ECH_DECODE_ERROR}, + #else + {"ECH_DECODE_ERROR", 20, 426}, + #endif + #ifdef SSL_R_ECH_REQUIRED + {"ECH_REQUIRED", ERR_LIB_SSL, SSL_R_ECH_REQUIRED}, + #else + {"ECH_REQUIRED", 20, 424}, + #endif + #ifdef SSL_R_EE_KEY_TOO_SMALL + {"EE_KEY_TOO_SMALL", ERR_LIB_SSL, SSL_R_EE_KEY_TOO_SMALL}, + #else + {"EE_KEY_TOO_SMALL", 20, 399}, + #endif + #ifdef SSL_R_EMPTY_RAW_PUBLIC_KEY + {"EMPTY_RAW_PUBLIC_KEY", ERR_LIB_SSL, SSL_R_EMPTY_RAW_PUBLIC_KEY}, + #else + {"EMPTY_RAW_PUBLIC_KEY", 20, 349}, + #endif + #ifdef SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST + {"EMPTY_SRTP_PROTECTION_PROFILE_LIST", ERR_LIB_SSL, SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST}, + #else + {"EMPTY_SRTP_PROTECTION_PROFILE_LIST", 20, 354}, + #endif + #ifdef SSL_R_ENCRYPTED_LENGTH_TOO_LONG + {"ENCRYPTED_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_ENCRYPTED_LENGTH_TOO_LONG}, + #else + {"ENCRYPTED_LENGTH_TOO_LONG", 20, 150}, + #endif + #ifdef SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST + {"ERROR_IN_RECEIVED_CIPHER_LIST", ERR_LIB_SSL, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST}, + #else + {"ERROR_IN_RECEIVED_CIPHER_LIST", 20, 151}, + #endif + #ifdef SSL_R_ERROR_IN_SYSTEM_DEFAULT_CONFIG + {"ERROR_IN_SYSTEM_DEFAULT_CONFIG", ERR_LIB_SSL, SSL_R_ERROR_IN_SYSTEM_DEFAULT_CONFIG}, + #else + {"ERROR_IN_SYSTEM_DEFAULT_CONFIG", 20, 419}, + #endif + #ifdef SSL_R_ERROR_SETTING_TLSA_BASE_DOMAIN + {"ERROR_SETTING_TLSA_BASE_DOMAIN", ERR_LIB_SSL, SSL_R_ERROR_SETTING_TLSA_BASE_DOMAIN}, + #else + {"ERROR_SETTING_TLSA_BASE_DOMAIN", 20, 204}, + #endif + #ifdef SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE + {"EXCEEDS_MAX_FRAGMENT_SIZE", ERR_LIB_SSL, SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE}, + #else + {"EXCEEDS_MAX_FRAGMENT_SIZE", 20, 194}, + #endif + #ifdef SSL_R_EXCESSIVE_MESSAGE_SIZE + {"EXCESSIVE_MESSAGE_SIZE", ERR_LIB_SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE}, + #else + {"EXCESSIVE_MESSAGE_SIZE", 20, 152}, + #endif + #ifdef SSL_R_EXTENSION_NOT_RECEIVED + {"EXTENSION_NOT_RECEIVED", ERR_LIB_SSL, SSL_R_EXTENSION_NOT_RECEIVED}, + #else + {"EXTENSION_NOT_RECEIVED", 20, 279}, + #endif + #ifdef SSL_R_EXTRA_DATA_IN_MESSAGE + {"EXTRA_DATA_IN_MESSAGE", ERR_LIB_SSL, SSL_R_EXTRA_DATA_IN_MESSAGE}, + #else + {"EXTRA_DATA_IN_MESSAGE", 20, 153}, + #endif + #ifdef SSL_R_EXT_LENGTH_MISMATCH + {"EXT_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_EXT_LENGTH_MISMATCH}, + #else + {"EXT_LENGTH_MISMATCH", 20, 163}, + #endif + #ifdef SSL_R_FAILED_TO_GET_PARAMETER + {"FAILED_TO_GET_PARAMETER", ERR_LIB_SSL, SSL_R_FAILED_TO_GET_PARAMETER}, + #else + {"FAILED_TO_GET_PARAMETER", 20, 316}, + #endif + #ifdef SSL_R_FAILED_TO_INIT_ASYNC + {"FAILED_TO_INIT_ASYNC", ERR_LIB_SSL, SSL_R_FAILED_TO_INIT_ASYNC}, + #else + {"FAILED_TO_INIT_ASYNC", 20, 405}, + #endif + #ifdef SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE + {"FEATURE_NEGOTIATION_NOT_COMPLETE", ERR_LIB_SSL, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE}, + #else + {"FEATURE_NEGOTIATION_NOT_COMPLETE", 20, 417}, + #endif + #ifdef SSL_R_FEATURE_NOT_RENEGOTIABLE + {"FEATURE_NOT_RENEGOTIABLE", ERR_LIB_SSL, SSL_R_FEATURE_NOT_RENEGOTIABLE}, + #else + {"FEATURE_NOT_RENEGOTIABLE", 20, 413}, + #endif + #ifdef SSL_R_FRAGMENTED_CLIENT_HELLO + {"FRAGMENTED_CLIENT_HELLO", ERR_LIB_SSL, SSL_R_FRAGMENTED_CLIENT_HELLO}, + #else + {"FRAGMENTED_CLIENT_HELLO", 20, 401}, + #endif + #ifdef SSL_R_GOT_A_FIN_BEFORE_A_CCS + {"GOT_A_FIN_BEFORE_A_CCS", ERR_LIB_SSL, SSL_R_GOT_A_FIN_BEFORE_A_CCS}, + #else + {"GOT_A_FIN_BEFORE_A_CCS", 20, 154}, + #endif + #ifdef SSL_R_HTTPS_PROXY_REQUEST + {"HTTPS_PROXY_REQUEST", ERR_LIB_SSL, SSL_R_HTTPS_PROXY_REQUEST}, + #else + {"HTTPS_PROXY_REQUEST", 20, 155}, + #endif + #ifdef SSL_R_HTTP_REQUEST + {"HTTP_REQUEST", ERR_LIB_SSL, SSL_R_HTTP_REQUEST}, + #else + {"HTTP_REQUEST", 20, 156}, + #endif + #ifdef SSL_R_ILLEGAL_POINT_COMPRESSION + {"ILLEGAL_POINT_COMPRESSION", ERR_LIB_SSL, SSL_R_ILLEGAL_POINT_COMPRESSION}, + #else + {"ILLEGAL_POINT_COMPRESSION", 20, 162}, + #endif + #ifdef SSL_R_ILLEGAL_SUITEB_DIGEST + {"ILLEGAL_SUITEB_DIGEST", ERR_LIB_SSL, SSL_R_ILLEGAL_SUITEB_DIGEST}, + #else + {"ILLEGAL_SUITEB_DIGEST", 20, 380}, + #endif + #ifdef SSL_R_INAPPROPRIATE_FALLBACK + {"INAPPROPRIATE_FALLBACK", ERR_LIB_SSL, SSL_R_INAPPROPRIATE_FALLBACK}, + #else + {"INAPPROPRIATE_FALLBACK", 20, 373}, + #endif + #ifdef SSL_R_INCONSISTENT_COMPRESSION + {"INCONSISTENT_COMPRESSION", ERR_LIB_SSL, SSL_R_INCONSISTENT_COMPRESSION}, + #else + {"INCONSISTENT_COMPRESSION", 20, 340}, + #endif + #ifdef SSL_R_INCONSISTENT_EARLY_DATA_ALPN + {"INCONSISTENT_EARLY_DATA_ALPN", ERR_LIB_SSL, SSL_R_INCONSISTENT_EARLY_DATA_ALPN}, + #else + {"INCONSISTENT_EARLY_DATA_ALPN", 20, 222}, + #endif + #ifdef SSL_R_INCONSISTENT_EARLY_DATA_SNI + {"INCONSISTENT_EARLY_DATA_SNI", ERR_LIB_SSL, SSL_R_INCONSISTENT_EARLY_DATA_SNI}, + #else + {"INCONSISTENT_EARLY_DATA_SNI", 20, 231}, + #endif + #ifdef SSL_R_INCONSISTENT_EXTMS + {"INCONSISTENT_EXTMS", ERR_LIB_SSL, SSL_R_INCONSISTENT_EXTMS}, + #else + {"INCONSISTENT_EXTMS", 20, 104}, + #endif + #ifdef SSL_R_INSUFFICIENT_SECURITY + {"INSUFFICIENT_SECURITY", ERR_LIB_SSL, SSL_R_INSUFFICIENT_SECURITY}, + #else + {"INSUFFICIENT_SECURITY", 20, 241}, + #endif + #ifdef SSL_R_INVALID_ALERT + {"INVALID_ALERT", ERR_LIB_SSL, SSL_R_INVALID_ALERT}, + #else + {"INVALID_ALERT", 20, 205}, + #endif + #ifdef SSL_R_INVALID_CCS_MESSAGE + {"INVALID_CCS_MESSAGE", ERR_LIB_SSL, SSL_R_INVALID_CCS_MESSAGE}, + #else + {"INVALID_CCS_MESSAGE", 20, 260}, + #endif + #ifdef SSL_R_INVALID_CERTIFICATE_OR_ALG + {"INVALID_CERTIFICATE_OR_ALG", ERR_LIB_SSL, SSL_R_INVALID_CERTIFICATE_OR_ALG}, + #else + {"INVALID_CERTIFICATE_OR_ALG", 20, 238}, + #endif + #ifdef SSL_R_INVALID_COMMAND + {"INVALID_COMMAND", ERR_LIB_SSL, SSL_R_INVALID_COMMAND}, + #else + {"INVALID_COMMAND", 20, 280}, + #endif + #ifdef SSL_R_INVALID_COMPRESSION_ALGORITHM + {"INVALID_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_INVALID_COMPRESSION_ALGORITHM}, + #else + {"INVALID_COMPRESSION_ALGORITHM", 20, 341}, + #endif + #ifdef SSL_R_INVALID_CONFIG + {"INVALID_CONFIG", ERR_LIB_SSL, SSL_R_INVALID_CONFIG}, + #else + {"INVALID_CONFIG", 20, 283}, + #endif + #ifdef SSL_R_INVALID_CONFIGURATION_NAME + {"INVALID_CONFIGURATION_NAME", ERR_LIB_SSL, SSL_R_INVALID_CONFIGURATION_NAME}, + #else + {"INVALID_CONFIGURATION_NAME", 20, 113}, + #endif + #ifdef SSL_R_INVALID_CONTEXT + {"INVALID_CONTEXT", ERR_LIB_SSL, SSL_R_INVALID_CONTEXT}, + #else + {"INVALID_CONTEXT", 20, 282}, + #endif + #ifdef SSL_R_INVALID_CT_VALIDATION_TYPE + {"INVALID_CT_VALIDATION_TYPE", ERR_LIB_SSL, SSL_R_INVALID_CT_VALIDATION_TYPE}, + #else + {"INVALID_CT_VALIDATION_TYPE", 20, 212}, + #endif + #ifdef SSL_R_INVALID_KEY_UPDATE_TYPE + {"INVALID_KEY_UPDATE_TYPE", ERR_LIB_SSL, SSL_R_INVALID_KEY_UPDATE_TYPE}, + #else + {"INVALID_KEY_UPDATE_TYPE", 20, 120}, + #endif + #ifdef SSL_R_INVALID_MAX_EARLY_DATA + {"INVALID_MAX_EARLY_DATA", ERR_LIB_SSL, SSL_R_INVALID_MAX_EARLY_DATA}, + #else + {"INVALID_MAX_EARLY_DATA", 20, 174}, + #endif + #ifdef SSL_R_INVALID_NULL_CMD_NAME + {"INVALID_NULL_CMD_NAME", ERR_LIB_SSL, SSL_R_INVALID_NULL_CMD_NAME}, + #else + {"INVALID_NULL_CMD_NAME", 20, 385}, + #endif + #ifdef SSL_R_INVALID_RAW_PUBLIC_KEY + {"INVALID_RAW_PUBLIC_KEY", ERR_LIB_SSL, SSL_R_INVALID_RAW_PUBLIC_KEY}, + #else + {"INVALID_RAW_PUBLIC_KEY", 20, 350}, + #endif + #ifdef SSL_R_INVALID_RECORD + {"INVALID_RECORD", ERR_LIB_SSL, SSL_R_INVALID_RECORD}, + #else + {"INVALID_RECORD", 20, 317}, + #endif + #ifdef SSL_R_INVALID_SEQUENCE_NUMBER + {"INVALID_SEQUENCE_NUMBER", ERR_LIB_SSL, SSL_R_INVALID_SEQUENCE_NUMBER}, + #else + {"INVALID_SEQUENCE_NUMBER", 20, 402}, + #endif + #ifdef SSL_R_INVALID_SERVERINFO_DATA + {"INVALID_SERVERINFO_DATA", ERR_LIB_SSL, SSL_R_INVALID_SERVERINFO_DATA}, + #else + {"INVALID_SERVERINFO_DATA", 20, 388}, + #endif + #ifdef SSL_R_INVALID_SESSION_ID + {"INVALID_SESSION_ID", ERR_LIB_SSL, SSL_R_INVALID_SESSION_ID}, + #else + {"INVALID_SESSION_ID", 20, 999}, + #endif + #ifdef SSL_R_INVALID_SRP_USERNAME + {"INVALID_SRP_USERNAME", ERR_LIB_SSL, SSL_R_INVALID_SRP_USERNAME}, + #else + {"INVALID_SRP_USERNAME", 20, 357}, + #endif + #ifdef SSL_R_INVALID_STATUS_RESPONSE + {"INVALID_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_INVALID_STATUS_RESPONSE}, + #else + {"INVALID_STATUS_RESPONSE", 20, 328}, + #endif + #ifdef SSL_R_INVALID_TICKET_KEYS_LENGTH + {"INVALID_TICKET_KEYS_LENGTH", ERR_LIB_SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH}, + #else + {"INVALID_TICKET_KEYS_LENGTH", 20, 325}, + #endif + #ifdef SSL_R_LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED + {"LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED", ERR_LIB_SSL, SSL_R_LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED}, + #else + {"LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED", 20, 333}, + #endif + #ifdef SSL_R_LENGTH_MISMATCH + {"LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_LENGTH_MISMATCH}, + #else + {"LENGTH_MISMATCH", 20, 159}, + #endif + #ifdef SSL_R_LENGTH_TOO_LONG + {"LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_LENGTH_TOO_LONG}, + #else + {"LENGTH_TOO_LONG", 20, 404}, + #endif + #ifdef SSL_R_LENGTH_TOO_SHORT + {"LENGTH_TOO_SHORT", ERR_LIB_SSL, SSL_R_LENGTH_TOO_SHORT}, + #else + {"LENGTH_TOO_SHORT", 20, 160}, + #endif + #ifdef SSL_R_LIBRARY_BUG + {"LIBRARY_BUG", ERR_LIB_SSL, SSL_R_LIBRARY_BUG}, + #else + {"LIBRARY_BUG", 20, 274}, + #endif + #ifdef SSL_R_LIBRARY_HAS_NO_CIPHERS + {"LIBRARY_HAS_NO_CIPHERS", ERR_LIB_SSL, SSL_R_LIBRARY_HAS_NO_CIPHERS}, + #else + {"LIBRARY_HAS_NO_CIPHERS", 20, 161}, + #endif + #ifdef SSL_R_LISTENER_USE_ONLY + {"LISTENER_USE_ONLY", ERR_LIB_SSL, SSL_R_LISTENER_USE_ONLY}, + #else + {"LISTENER_USE_ONLY", 20, 421}, + #endif + #ifdef SSL_R_MAXIMUM_ENCRYPTED_PKTS_REACHED + {"MAXIMUM_ENCRYPTED_PKTS_REACHED", ERR_LIB_SSL, SSL_R_MAXIMUM_ENCRYPTED_PKTS_REACHED}, + #else + {"MAXIMUM_ENCRYPTED_PKTS_REACHED", 20, 395}, + #endif + #ifdef SSL_R_MISSING_DSA_SIGNING_CERT + {"MISSING_DSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_DSA_SIGNING_CERT}, + #else + {"MISSING_DSA_SIGNING_CERT", 20, 165}, + #endif + #ifdef SSL_R_MISSING_ECDSA_SIGNING_CERT + {"MISSING_ECDSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_ECDSA_SIGNING_CERT}, + #else + {"MISSING_ECDSA_SIGNING_CERT", 20, 381}, + #endif + #ifdef SSL_R_MISSING_FATAL + {"MISSING_FATAL", ERR_LIB_SSL, SSL_R_MISSING_FATAL}, + #else + {"MISSING_FATAL", 20, 256}, + #endif + #ifdef SSL_R_MISSING_PARAMETERS + {"MISSING_PARAMETERS", ERR_LIB_SSL, SSL_R_MISSING_PARAMETERS}, + #else + {"MISSING_PARAMETERS", 20, 290}, + #endif + #ifdef SSL_R_MISSING_PSK_KEX_MODES_EXTENSION + {"MISSING_PSK_KEX_MODES_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_PSK_KEX_MODES_EXTENSION}, + #else + {"MISSING_PSK_KEX_MODES_EXTENSION", 20, 310}, + #endif + #ifdef SSL_R_MISSING_QUIC_TLS_FUNCTIONS + {"MISSING_QUIC_TLS_FUNCTIONS", ERR_LIB_SSL, SSL_R_MISSING_QUIC_TLS_FUNCTIONS}, + #else + {"MISSING_QUIC_TLS_FUNCTIONS", 20, 423}, + #endif + #ifdef SSL_R_MISSING_RSA_CERTIFICATE + {"MISSING_RSA_CERTIFICATE", ERR_LIB_SSL, SSL_R_MISSING_RSA_CERTIFICATE}, + #else + {"MISSING_RSA_CERTIFICATE", 20, 168}, + #endif + #ifdef SSL_R_MISSING_RSA_ENCRYPTING_CERT + {"MISSING_RSA_ENCRYPTING_CERT", ERR_LIB_SSL, SSL_R_MISSING_RSA_ENCRYPTING_CERT}, + #else + {"MISSING_RSA_ENCRYPTING_CERT", 20, 169}, + #endif + #ifdef SSL_R_MISSING_RSA_SIGNING_CERT + {"MISSING_RSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_RSA_SIGNING_CERT}, + #else + {"MISSING_RSA_SIGNING_CERT", 20, 170}, + #endif + #ifdef SSL_R_MISSING_SIGALGS_EXTENSION + {"MISSING_SIGALGS_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_SIGALGS_EXTENSION}, + #else + {"MISSING_SIGALGS_EXTENSION", 20, 112}, + #endif + #ifdef SSL_R_MISSING_SIGNING_CERT + {"MISSING_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_SIGNING_CERT}, + #else + {"MISSING_SIGNING_CERT", 20, 221}, + #endif + #ifdef SSL_R_MISSING_SRP_PARAM + {"MISSING_SRP_PARAM", ERR_LIB_SSL, SSL_R_MISSING_SRP_PARAM}, + #else + {"MISSING_SRP_PARAM", 20, 358}, + #endif + #ifdef SSL_R_MISSING_SUPPORTED_GROUPS_EXTENSION + {"MISSING_SUPPORTED_GROUPS_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_SUPPORTED_GROUPS_EXTENSION}, + #else + {"MISSING_SUPPORTED_GROUPS_EXTENSION", 20, 209}, + #endif + #ifdef SSL_R_MISSING_SUPPORTED_VERSIONS_EXTENSION + {"MISSING_SUPPORTED_VERSIONS_EXTENSION", ERR_LIB_SSL, SSL_R_MISSING_SUPPORTED_VERSIONS_EXTENSION}, + #else + {"MISSING_SUPPORTED_VERSIONS_EXTENSION", 20, 420}, + #endif + #ifdef SSL_R_MISSING_TMP_DH_KEY + {"MISSING_TMP_DH_KEY", ERR_LIB_SSL, SSL_R_MISSING_TMP_DH_KEY}, + #else + {"MISSING_TMP_DH_KEY", 20, 171}, + #endif + #ifdef SSL_R_MISSING_TMP_ECDH_KEY + {"MISSING_TMP_ECDH_KEY", ERR_LIB_SSL, SSL_R_MISSING_TMP_ECDH_KEY}, + #else + {"MISSING_TMP_ECDH_KEY", 20, 311}, + #endif + #ifdef SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA + {"MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA", ERR_LIB_SSL, SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA}, + #else + {"MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA", 20, 293}, + #endif + #ifdef SSL_R_NOT_ON_RECORD_BOUNDARY + {"NOT_ON_RECORD_BOUNDARY", ERR_LIB_SSL, SSL_R_NOT_ON_RECORD_BOUNDARY}, + #else + {"NOT_ON_RECORD_BOUNDARY", 20, 182}, + #endif + #ifdef SSL_R_NOT_REPLACING_CERTIFICATE + {"NOT_REPLACING_CERTIFICATE", ERR_LIB_SSL, SSL_R_NOT_REPLACING_CERTIFICATE}, + #else + {"NOT_REPLACING_CERTIFICATE", 20, 289}, + #endif + #ifdef SSL_R_NOT_SERVER + {"NOT_SERVER", ERR_LIB_SSL, SSL_R_NOT_SERVER}, + #else + {"NOT_SERVER", 20, 284}, + #endif + #ifdef SSL_R_NO_APPLICATION_PROTOCOL + {"NO_APPLICATION_PROTOCOL", ERR_LIB_SSL, SSL_R_NO_APPLICATION_PROTOCOL}, + #else + {"NO_APPLICATION_PROTOCOL", 20, 235}, + #endif + #ifdef SSL_R_NO_CERTIFICATES_RETURNED + {"NO_CERTIFICATES_RETURNED", ERR_LIB_SSL, SSL_R_NO_CERTIFICATES_RETURNED}, + #else + {"NO_CERTIFICATES_RETURNED", 20, 176}, + #endif + #ifdef SSL_R_NO_CERTIFICATE_ASSIGNED + {"NO_CERTIFICATE_ASSIGNED", ERR_LIB_SSL, SSL_R_NO_CERTIFICATE_ASSIGNED}, + #else + {"NO_CERTIFICATE_ASSIGNED", 20, 177}, + #endif + #ifdef SSL_R_NO_CERTIFICATE_SET + {"NO_CERTIFICATE_SET", ERR_LIB_SSL, SSL_R_NO_CERTIFICATE_SET}, + #else + {"NO_CERTIFICATE_SET", 20, 179}, + #endif + #ifdef SSL_R_NO_CHANGE_FOLLOWING_HRR + {"NO_CHANGE_FOLLOWING_HRR", ERR_LIB_SSL, SSL_R_NO_CHANGE_FOLLOWING_HRR}, + #else + {"NO_CHANGE_FOLLOWING_HRR", 20, 214}, + #endif + #ifdef SSL_R_NO_CIPHERS_AVAILABLE + {"NO_CIPHERS_AVAILABLE", ERR_LIB_SSL, SSL_R_NO_CIPHERS_AVAILABLE}, + #else + {"NO_CIPHERS_AVAILABLE", 20, 181}, + #endif + #ifdef SSL_R_NO_CIPHERS_SPECIFIED + {"NO_CIPHERS_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_CIPHERS_SPECIFIED}, + #else + {"NO_CIPHERS_SPECIFIED", 20, 183}, + #endif + #ifdef SSL_R_NO_CIPHER_MATCH + {"NO_CIPHER_MATCH", ERR_LIB_SSL, SSL_R_NO_CIPHER_MATCH}, + #else + {"NO_CIPHER_MATCH", 20, 185}, + #endif + #ifdef SSL_R_NO_CLIENT_CERT_METHOD + {"NO_CLIENT_CERT_METHOD", ERR_LIB_SSL, SSL_R_NO_CLIENT_CERT_METHOD}, + #else + {"NO_CLIENT_CERT_METHOD", 20, 331}, + #endif + #ifdef SSL_R_NO_COMPRESSION_SPECIFIED + {"NO_COMPRESSION_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_COMPRESSION_SPECIFIED}, + #else + {"NO_COMPRESSION_SPECIFIED", 20, 187}, + #endif + #ifdef SSL_R_NO_COOKIE_CALLBACK_SET + {"NO_COOKIE_CALLBACK_SET", ERR_LIB_SSL, SSL_R_NO_COOKIE_CALLBACK_SET}, + #else + {"NO_COOKIE_CALLBACK_SET", 20, 287}, + #endif + #ifdef SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER + {"NO_GOST_CERTIFICATE_SENT_BY_PEER", ERR_LIB_SSL, SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER}, + #else + {"NO_GOST_CERTIFICATE_SENT_BY_PEER", 20, 330}, + #endif + #ifdef SSL_R_NO_METHOD_SPECIFIED + {"NO_METHOD_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_METHOD_SPECIFIED}, + #else + {"NO_METHOD_SPECIFIED", 20, 188}, + #endif + #ifdef SSL_R_NO_PEM_EXTENSIONS + {"NO_PEM_EXTENSIONS", ERR_LIB_SSL, SSL_R_NO_PEM_EXTENSIONS}, + #else + {"NO_PEM_EXTENSIONS", 20, 389}, + #endif + #ifdef SSL_R_NO_PRIVATE_KEY_ASSIGNED + {"NO_PRIVATE_KEY_ASSIGNED", ERR_LIB_SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED}, + #else + {"NO_PRIVATE_KEY_ASSIGNED", 20, 190}, + #endif + #ifdef SSL_R_NO_PROTOCOLS_AVAILABLE + {"NO_PROTOCOLS_AVAILABLE", ERR_LIB_SSL, SSL_R_NO_PROTOCOLS_AVAILABLE}, + #else + {"NO_PROTOCOLS_AVAILABLE", 20, 191}, + #endif + #ifdef SSL_R_NO_RENEGOTIATION + {"NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_NO_RENEGOTIATION}, + #else + {"NO_RENEGOTIATION", 20, 339}, + #endif + #ifdef SSL_R_NO_REQUIRED_DIGEST + {"NO_REQUIRED_DIGEST", ERR_LIB_SSL, SSL_R_NO_REQUIRED_DIGEST}, + #else + {"NO_REQUIRED_DIGEST", 20, 324}, + #endif + #ifdef SSL_R_NO_SHARED_CIPHER + {"NO_SHARED_CIPHER", ERR_LIB_SSL, SSL_R_NO_SHARED_CIPHER}, + #else + {"NO_SHARED_CIPHER", 20, 193}, + #endif + #ifdef SSL_R_NO_SHARED_GROUPS + {"NO_SHARED_GROUPS", ERR_LIB_SSL, SSL_R_NO_SHARED_GROUPS}, + #else + {"NO_SHARED_GROUPS", 20, 410}, + #endif + #ifdef SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS + {"NO_SHARED_SIGNATURE_ALGORITHMS", ERR_LIB_SSL, SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS}, + #else + {"NO_SHARED_SIGNATURE_ALGORITHMS", 20, 376}, + #endif + #ifdef SSL_R_NO_SRTP_PROFILES + {"NO_SRTP_PROFILES", ERR_LIB_SSL, SSL_R_NO_SRTP_PROFILES}, + #else + {"NO_SRTP_PROFILES", 20, 359}, + #endif + #ifdef SSL_R_NO_STREAM + {"NO_STREAM", ERR_LIB_SSL, SSL_R_NO_STREAM}, + #else + {"NO_STREAM", 20, 355}, + #endif + #ifdef SSL_R_NO_SUITABLE_DIGEST_ALGORITHM + {"NO_SUITABLE_DIGEST_ALGORITHM", ERR_LIB_SSL, SSL_R_NO_SUITABLE_DIGEST_ALGORITHM}, + #else + {"NO_SUITABLE_DIGEST_ALGORITHM", 20, 297}, + #endif + #ifdef SSL_R_NO_SUITABLE_GROUPS + {"NO_SUITABLE_GROUPS", ERR_LIB_SSL, SSL_R_NO_SUITABLE_GROUPS}, + #else + {"NO_SUITABLE_GROUPS", 20, 295}, + #endif + #ifdef SSL_R_NO_SUITABLE_KEY_SHARE + {"NO_SUITABLE_KEY_SHARE", ERR_LIB_SSL, SSL_R_NO_SUITABLE_KEY_SHARE}, + #else + {"NO_SUITABLE_KEY_SHARE", 20, 101}, + #endif + #ifdef SSL_R_NO_SUITABLE_RECORD_LAYER + {"NO_SUITABLE_RECORD_LAYER", ERR_LIB_SSL, SSL_R_NO_SUITABLE_RECORD_LAYER}, + #else + {"NO_SUITABLE_RECORD_LAYER", 20, 322}, + #endif + #ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM + {"NO_SUITABLE_SIGNATURE_ALGORITHM", ERR_LIB_SSL, SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM}, + #else + {"NO_SUITABLE_SIGNATURE_ALGORITHM", 20, 118}, + #endif + #ifdef SSL_R_NO_VALID_SCTS + {"NO_VALID_SCTS", ERR_LIB_SSL, SSL_R_NO_VALID_SCTS}, + #else + {"NO_VALID_SCTS", 20, 216}, + #endif + #ifdef SSL_R_NO_VERIFY_COOKIE_CALLBACK + {"NO_VERIFY_COOKIE_CALLBACK", ERR_LIB_SSL, SSL_R_NO_VERIFY_COOKIE_CALLBACK}, + #else + {"NO_VERIFY_COOKIE_CALLBACK", 20, 403}, + #endif + #ifdef SSL_R_NULL_SSL_CTX + {"NULL_SSL_CTX", ERR_LIB_SSL, SSL_R_NULL_SSL_CTX}, + #else + {"NULL_SSL_CTX", 20, 195}, + #endif + #ifdef SSL_R_NULL_SSL_METHOD_PASSED + {"NULL_SSL_METHOD_PASSED", ERR_LIB_SSL, SSL_R_NULL_SSL_METHOD_PASSED}, + #else + {"NULL_SSL_METHOD_PASSED", 20, 196}, + #endif + #ifdef SSL_R_OCSP_CALLBACK_FAILURE + {"OCSP_CALLBACK_FAILURE", ERR_LIB_SSL, SSL_R_OCSP_CALLBACK_FAILURE}, + #else + {"OCSP_CALLBACK_FAILURE", 20, 305}, + #endif + #ifdef SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED + {"OLD_SESSION_CIPHER_NOT_RETURNED", ERR_LIB_SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED}, + #else + {"OLD_SESSION_CIPHER_NOT_RETURNED", 20, 197}, + #endif + #ifdef SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED + {"OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED", ERR_LIB_SSL, SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED}, + #else + {"OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED", 20, 344}, + #endif + #ifdef SSL_R_OVERFLOW_ERROR + {"OVERFLOW_ERROR", ERR_LIB_SSL, SSL_R_OVERFLOW_ERROR}, + #else + {"OVERFLOW_ERROR", 20, 237}, + #endif + #ifdef SSL_R_PACKET_LENGTH_TOO_LONG + {"PACKET_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_PACKET_LENGTH_TOO_LONG}, + #else + {"PACKET_LENGTH_TOO_LONG", 20, 198}, + #endif + #ifdef SSL_R_PARSE_TLSEXT + {"PARSE_TLSEXT", ERR_LIB_SSL, SSL_R_PARSE_TLSEXT}, + #else + {"PARSE_TLSEXT", 20, 227}, + #endif + #ifdef SSL_R_PATH_TOO_LONG + {"PATH_TOO_LONG", ERR_LIB_SSL, SSL_R_PATH_TOO_LONG}, + #else + {"PATH_TOO_LONG", 20, 270}, + #endif + #ifdef SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE + {"PEER_DID_NOT_RETURN_A_CERTIFICATE", ERR_LIB_SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE}, + #else + {"PEER_DID_NOT_RETURN_A_CERTIFICATE", 20, 199}, + #endif + #ifdef SSL_R_PEM_NAME_BAD_PREFIX + {"PEM_NAME_BAD_PREFIX", ERR_LIB_SSL, SSL_R_PEM_NAME_BAD_PREFIX}, + #else + {"PEM_NAME_BAD_PREFIX", 20, 391}, + #endif + #ifdef SSL_R_PEM_NAME_TOO_SHORT + {"PEM_NAME_TOO_SHORT", ERR_LIB_SSL, SSL_R_PEM_NAME_TOO_SHORT}, + #else + {"PEM_NAME_TOO_SHORT", 20, 392}, + #endif + #ifdef SSL_R_PIPELINE_FAILURE + {"PIPELINE_FAILURE", ERR_LIB_SSL, SSL_R_PIPELINE_FAILURE}, + #else + {"PIPELINE_FAILURE", 20, 406}, + #endif + #ifdef SSL_R_POLL_REQUEST_NOT_SUPPORTED + {"POLL_REQUEST_NOT_SUPPORTED", ERR_LIB_SSL, SSL_R_POLL_REQUEST_NOT_SUPPORTED}, + #else + {"POLL_REQUEST_NOT_SUPPORTED", 20, 418}, + #endif + #ifdef SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR + {"POST_HANDSHAKE_AUTH_ENCODING_ERR", ERR_LIB_SSL, SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR}, + #else + {"POST_HANDSHAKE_AUTH_ENCODING_ERR", 20, 278}, + #endif + #ifdef SSL_R_PRIVATE_KEY_MISMATCH + {"PRIVATE_KEY_MISMATCH", ERR_LIB_SSL, SSL_R_PRIVATE_KEY_MISMATCH}, + #else + {"PRIVATE_KEY_MISMATCH", 20, 288}, + #endif + #ifdef SSL_R_PROTOCOL_IS_SHUTDOWN + {"PROTOCOL_IS_SHUTDOWN", ERR_LIB_SSL, SSL_R_PROTOCOL_IS_SHUTDOWN}, + #else + {"PROTOCOL_IS_SHUTDOWN", 20, 207}, + #endif + #ifdef SSL_R_PSK_IDENTITY_NOT_FOUND + {"PSK_IDENTITY_NOT_FOUND", ERR_LIB_SSL, SSL_R_PSK_IDENTITY_NOT_FOUND}, + #else + {"PSK_IDENTITY_NOT_FOUND", 20, 223}, + #endif + #ifdef SSL_R_PSK_NO_CLIENT_CB + {"PSK_NO_CLIENT_CB", ERR_LIB_SSL, SSL_R_PSK_NO_CLIENT_CB}, + #else + {"PSK_NO_CLIENT_CB", 20, 224}, + #endif + #ifdef SSL_R_PSK_NO_SERVER_CB + {"PSK_NO_SERVER_CB", ERR_LIB_SSL, SSL_R_PSK_NO_SERVER_CB}, + #else + {"PSK_NO_SERVER_CB", 20, 225}, + #endif + #ifdef SSL_R_QUIC_HANDSHAKE_LAYER_ERROR + {"QUIC_HANDSHAKE_LAYER_ERROR", ERR_LIB_SSL, SSL_R_QUIC_HANDSHAKE_LAYER_ERROR}, + #else + {"QUIC_HANDSHAKE_LAYER_ERROR", 20, 393}, + #endif + #ifdef SSL_R_QUIC_NETWORK_ERROR + {"QUIC_NETWORK_ERROR", ERR_LIB_SSL, SSL_R_QUIC_NETWORK_ERROR}, + #else + {"QUIC_NETWORK_ERROR", 20, 387}, + #endif + #ifdef SSL_R_QUIC_PROTOCOL_ERROR + {"QUIC_PROTOCOL_ERROR", ERR_LIB_SSL, SSL_R_QUIC_PROTOCOL_ERROR}, + #else + {"QUIC_PROTOCOL_ERROR", 20, 382}, + #endif + #ifdef SSL_R_READ_BIO_NOT_SET + {"READ_BIO_NOT_SET", ERR_LIB_SSL, SSL_R_READ_BIO_NOT_SET}, + #else + {"READ_BIO_NOT_SET", 20, 211}, + #endif + #ifdef SSL_R_READ_TIMEOUT_EXPIRED + {"READ_TIMEOUT_EXPIRED", ERR_LIB_SSL, SSL_R_READ_TIMEOUT_EXPIRED}, + #else + {"READ_TIMEOUT_EXPIRED", 20, 312}, + #endif + #ifdef SSL_R_RECORDS_NOT_RELEASED + {"RECORDS_NOT_RELEASED", ERR_LIB_SSL, SSL_R_RECORDS_NOT_RELEASED}, + #else + {"RECORDS_NOT_RELEASED", 20, 321}, + #endif + #ifdef SSL_R_RECORD_LAYER_FAILURE + {"RECORD_LAYER_FAILURE", ERR_LIB_SSL, SSL_R_RECORD_LAYER_FAILURE}, + #else + {"RECORD_LAYER_FAILURE", 20, 313}, + #endif + #ifdef SSL_R_RECORD_LENGTH_MISMATCH + {"RECORD_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_RECORD_LENGTH_MISMATCH}, + #else + {"RECORD_LENGTH_MISMATCH", 20, 213}, + #endif + #ifdef SSL_R_RECORD_TOO_SMALL + {"RECORD_TOO_SMALL", ERR_LIB_SSL, SSL_R_RECORD_TOO_SMALL}, + #else + {"RECORD_TOO_SMALL", 20, 298}, + #endif + #ifdef SSL_R_REMOTE_PEER_ADDRESS_NOT_SET + {"REMOTE_PEER_ADDRESS_NOT_SET", ERR_LIB_SSL, SSL_R_REMOTE_PEER_ADDRESS_NOT_SET}, + #else + {"REMOTE_PEER_ADDRESS_NOT_SET", 20, 346}, + #endif + #ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG + {"RENEGOTIATE_EXT_TOO_LONG", ERR_LIB_SSL, SSL_R_RENEGOTIATE_EXT_TOO_LONG}, + #else + {"RENEGOTIATE_EXT_TOO_LONG", 20, 335}, + #endif + #ifdef SSL_R_RENEGOTIATION_ENCODING_ERR + {"RENEGOTIATION_ENCODING_ERR", ERR_LIB_SSL, SSL_R_RENEGOTIATION_ENCODING_ERR}, + #else + {"RENEGOTIATION_ENCODING_ERR", 20, 336}, + #endif + #ifdef SSL_R_RENEGOTIATION_MISMATCH + {"RENEGOTIATION_MISMATCH", ERR_LIB_SSL, SSL_R_RENEGOTIATION_MISMATCH}, + #else + {"RENEGOTIATION_MISMATCH", 20, 337}, + #endif + #ifdef SSL_R_REQUEST_PENDING + {"REQUEST_PENDING", ERR_LIB_SSL, SSL_R_REQUEST_PENDING}, + #else + {"REQUEST_PENDING", 20, 285}, + #endif + #ifdef SSL_R_REQUEST_SENT + {"REQUEST_SENT", ERR_LIB_SSL, SSL_R_REQUEST_SENT}, + #else + {"REQUEST_SENT", 20, 286}, + #endif + #ifdef SSL_R_REQUIRED_CIPHER_MISSING + {"REQUIRED_CIPHER_MISSING", ERR_LIB_SSL, SSL_R_REQUIRED_CIPHER_MISSING}, + #else + {"REQUIRED_CIPHER_MISSING", 20, 215}, + #endif + #ifdef SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING + {"REQUIRED_COMPRESSION_ALGORITHM_MISSING", ERR_LIB_SSL, SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING}, + #else + {"REQUIRED_COMPRESSION_ALGORITHM_MISSING", 20, 342}, + #endif + #ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING + {"SCSV_RECEIVED_WHEN_RENEGOTIATING", ERR_LIB_SSL, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING}, + #else + {"SCSV_RECEIVED_WHEN_RENEGOTIATING", 20, 345}, + #endif + #ifdef SSL_R_SCT_VERIFICATION_FAILED + {"SCT_VERIFICATION_FAILED", ERR_LIB_SSL, SSL_R_SCT_VERIFICATION_FAILED}, + #else + {"SCT_VERIFICATION_FAILED", 20, 208}, + #endif + #ifdef SSL_R_SEQUENCE_CTR_WRAPPED + {"SEQUENCE_CTR_WRAPPED", ERR_LIB_SSL, SSL_R_SEQUENCE_CTR_WRAPPED}, + #else + {"SEQUENCE_CTR_WRAPPED", 20, 327}, + #endif + #ifdef SSL_R_SERVERHELLO_TLSEXT + {"SERVERHELLO_TLSEXT", ERR_LIB_SSL, SSL_R_SERVERHELLO_TLSEXT}, + #else + {"SERVERHELLO_TLSEXT", 20, 275}, + #endif + #ifdef SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED + {"SESSION_ID_CONTEXT_UNINITIALIZED", ERR_LIB_SSL, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED}, + #else + {"SESSION_ID_CONTEXT_UNINITIALIZED", 20, 277}, + #endif + #ifdef SSL_R_SHUTDOWN_WHILE_IN_INIT + {"SHUTDOWN_WHILE_IN_INIT", ERR_LIB_SSL, SSL_R_SHUTDOWN_WHILE_IN_INIT}, + #else + {"SHUTDOWN_WHILE_IN_INIT", 20, 407}, + #endif + #ifdef SSL_R_SIGNATURE_ALGORITHMS_ERROR + {"SIGNATURE_ALGORITHMS_ERROR", ERR_LIB_SSL, SSL_R_SIGNATURE_ALGORITHMS_ERROR}, + #else + {"SIGNATURE_ALGORITHMS_ERROR", 20, 360}, + #endif + #ifdef SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE + {"SIGNATURE_FOR_NON_SIGNING_CERTIFICATE", ERR_LIB_SSL, SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE}, + #else + {"SIGNATURE_FOR_NON_SIGNING_CERTIFICATE", 20, 220}, + #endif + #ifdef SSL_R_SRP_A_CALC + {"SRP_A_CALC", ERR_LIB_SSL, SSL_R_SRP_A_CALC}, + #else + {"SRP_A_CALC", 20, 361}, + #endif + #ifdef SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES + {"SRTP_COULD_NOT_ALLOCATE_PROFILES", ERR_LIB_SSL, SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES}, + #else + {"SRTP_COULD_NOT_ALLOCATE_PROFILES", 20, 362}, + #endif + #ifdef SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG + {"SRTP_PROTECTION_PROFILE_LIST_TOO_LONG", ERR_LIB_SSL, SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG}, + #else + {"SRTP_PROTECTION_PROFILE_LIST_TOO_LONG", 20, 363}, + #endif + #ifdef SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE + {"SRTP_UNKNOWN_PROTECTION_PROFILE", ERR_LIB_SSL, SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE}, + #else + {"SRTP_UNKNOWN_PROTECTION_PROFILE", 20, 364}, + #endif + #ifdef SSL_R_SSL_COMMAND_SECTION_EMPTY + {"SSL_COMMAND_SECTION_EMPTY", ERR_LIB_SSL, SSL_R_SSL_COMMAND_SECTION_EMPTY}, + #else + {"SSL_COMMAND_SECTION_EMPTY", 20, 117}, + #endif + #ifdef SSL_R_SSL_COMMAND_SECTION_NOT_FOUND + {"SSL_COMMAND_SECTION_NOT_FOUND", ERR_LIB_SSL, SSL_R_SSL_COMMAND_SECTION_NOT_FOUND}, + #else + {"SSL_COMMAND_SECTION_NOT_FOUND", 20, 125}, + #endif + #ifdef SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION + {"SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION", ERR_LIB_SSL, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION}, + #else + {"SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION", 20, 228}, + #endif + #ifdef SSL_R_SSL_HANDSHAKE_FAILURE + {"SSL_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_SSL_HANDSHAKE_FAILURE}, + #else + {"SSL_HANDSHAKE_FAILURE", 20, 229}, + #endif + #ifdef SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS + {"SSL_LIBRARY_HAS_NO_CIPHERS", ERR_LIB_SSL, SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS}, + #else + {"SSL_LIBRARY_HAS_NO_CIPHERS", 20, 230}, + #endif + #ifdef SSL_R_SSL_NEGATIVE_LENGTH + {"SSL_NEGATIVE_LENGTH", ERR_LIB_SSL, SSL_R_SSL_NEGATIVE_LENGTH}, + #else + {"SSL_NEGATIVE_LENGTH", 20, 372}, + #endif + #ifdef SSL_R_SSL_SECTION_EMPTY + {"SSL_SECTION_EMPTY", ERR_LIB_SSL, SSL_R_SSL_SECTION_EMPTY}, + #else + {"SSL_SECTION_EMPTY", 20, 126}, + #endif + #ifdef SSL_R_SSL_SECTION_NOT_FOUND + {"SSL_SECTION_NOT_FOUND", ERR_LIB_SSL, SSL_R_SSL_SECTION_NOT_FOUND}, + #else + {"SSL_SECTION_NOT_FOUND", 20, 136}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_CALLBACK_FAILED + {"SSL_SESSION_ID_CALLBACK_FAILED", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CALLBACK_FAILED}, + #else + {"SSL_SESSION_ID_CALLBACK_FAILED", 20, 301}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_CONFLICT + {"SSL_SESSION_ID_CONFLICT", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CONFLICT}, + #else + {"SSL_SESSION_ID_CONFLICT", 20, 302}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG + {"SSL_SESSION_ID_CONTEXT_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG}, + #else + {"SSL_SESSION_ID_CONTEXT_TOO_LONG", 20, 273}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH + {"SSL_SESSION_ID_HAS_BAD_LENGTH", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH}, + #else + {"SSL_SESSION_ID_HAS_BAD_LENGTH", 20, 303}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_TOO_LONG + {"SSL_SESSION_ID_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_TOO_LONG}, + #else + {"SSL_SESSION_ID_TOO_LONG", 20, 408}, + #endif + #ifdef SSL_R_SSL_SESSION_VERSION_MISMATCH + {"SSL_SESSION_VERSION_MISMATCH", ERR_LIB_SSL, SSL_R_SSL_SESSION_VERSION_MISMATCH}, + #else + {"SSL_SESSION_VERSION_MISMATCH", 20, 210}, + #endif + #ifdef SSL_R_STILL_IN_INIT + {"STILL_IN_INIT", ERR_LIB_SSL, SSL_R_STILL_IN_INIT}, + #else + {"STILL_IN_INIT", 20, 121}, + #endif + #ifdef SSL_R_STREAM_COUNT_LIMITED + {"STREAM_COUNT_LIMITED", ERR_LIB_SSL, SSL_R_STREAM_COUNT_LIMITED}, + #else + {"STREAM_COUNT_LIMITED", 20, 411}, + #endif + #ifdef SSL_R_STREAM_FINISHED + {"STREAM_FINISHED", ERR_LIB_SSL, SSL_R_STREAM_FINISHED}, + #else + {"STREAM_FINISHED", 20, 365}, + #endif + #ifdef SSL_R_STREAM_RECV_ONLY + {"STREAM_RECV_ONLY", ERR_LIB_SSL, SSL_R_STREAM_RECV_ONLY}, + #else + {"STREAM_RECV_ONLY", 20, 366}, + #endif + #ifdef SSL_R_STREAM_RESET + {"STREAM_RESET", ERR_LIB_SSL, SSL_R_STREAM_RESET}, + #else + {"STREAM_RESET", 20, 375}, + #endif + #ifdef SSL_R_STREAM_SEND_ONLY + {"STREAM_SEND_ONLY", ERR_LIB_SSL, SSL_R_STREAM_SEND_ONLY}, + #else + {"STREAM_SEND_ONLY", 20, 379}, + #endif + #ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED + {"TLSV13_ALERT_CERTIFICATE_REQUIRED", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED}, + #else + {"TLSV13_ALERT_CERTIFICATE_REQUIRED", 20, 1116}, + #endif + #ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED + {"TLSV13_ALERT_CERTIFICATE_REQUIRED", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED}, + #else + {"TLSV13_ALERT_CERTIFICATE_REQUIRED", 20, 1116}, + #endif + #ifdef SSL_R_TLSV13_ALERT_MISSING_EXTENSION + {"TLSV13_ALERT_MISSING_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_MISSING_EXTENSION}, + #else + {"TLSV13_ALERT_MISSING_EXTENSION", 20, 1109}, + #endif + #ifdef SSL_R_TLSV13_ALERT_MISSING_EXTENSION + {"TLSV13_ALERT_MISSING_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV13_ALERT_MISSING_EXTENSION}, + #else + {"TLSV13_ALERT_MISSING_EXTENSION", 20, 1109}, + #endif + #ifdef SSL_R_TLSV1_ALERT_ACCESS_DENIED + {"TLSV1_ALERT_ACCESS_DENIED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_ACCESS_DENIED}, + #else + {"TLSV1_ALERT_ACCESS_DENIED", 20, 1049}, + #endif + #ifdef SSL_R_TLSV1_ALERT_ACCESS_DENIED + {"TLSV1_ALERT_ACCESS_DENIED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_ACCESS_DENIED}, + #else + {"TLSV1_ALERT_ACCESS_DENIED", 20, 1049}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECODE_ERROR + {"TLSV1_ALERT_DECODE_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECODE_ERROR}, + #else + {"TLSV1_ALERT_DECODE_ERROR", 20, 1050}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECODE_ERROR + {"TLSV1_ALERT_DECODE_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECODE_ERROR}, + #else + {"TLSV1_ALERT_DECODE_ERROR", 20, 1050}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECRYPTION_FAILED + {"TLSV1_ALERT_DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPTION_FAILED}, + #else + {"TLSV1_ALERT_DECRYPTION_FAILED", 20, 1021}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECRYPTION_FAILED + {"TLSV1_ALERT_DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPTION_FAILED}, + #else + {"TLSV1_ALERT_DECRYPTION_FAILED", 20, 1021}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECRYPT_ERROR + {"TLSV1_ALERT_DECRYPT_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPT_ERROR}, + #else + {"TLSV1_ALERT_DECRYPT_ERROR", 20, 1051}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECRYPT_ERROR + {"TLSV1_ALERT_DECRYPT_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPT_ERROR}, + #else + {"TLSV1_ALERT_DECRYPT_ERROR", 20, 1051}, + #endif + #ifdef SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION + {"TLSV1_ALERT_EXPORT_RESTRICTION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION}, + #else + {"TLSV1_ALERT_EXPORT_RESTRICTION", 20, 1060}, + #endif + #ifdef SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION + {"TLSV1_ALERT_EXPORT_RESTRICTION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION}, + #else + {"TLSV1_ALERT_EXPORT_RESTRICTION", 20, 1060}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK + {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK}, + #else + {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", 20, 1086}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK + {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK}, + #else + {"TLSV1_ALERT_INAPPROPRIATE_FALLBACK", 20, 1086}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY + {"TLSV1_ALERT_INSUFFICIENT_SECURITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY}, + #else + {"TLSV1_ALERT_INSUFFICIENT_SECURITY", 20, 1071}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY + {"TLSV1_ALERT_INSUFFICIENT_SECURITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY}, + #else + {"TLSV1_ALERT_INSUFFICIENT_SECURITY", 20, 1071}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INTERNAL_ERROR + {"TLSV1_ALERT_INTERNAL_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INTERNAL_ERROR}, + #else + {"TLSV1_ALERT_INTERNAL_ERROR", 20, 1080}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INTERNAL_ERROR + {"TLSV1_ALERT_INTERNAL_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INTERNAL_ERROR}, + #else + {"TLSV1_ALERT_INTERNAL_ERROR", 20, 1080}, + #endif + #ifdef SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL + {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL}, + #else + {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", 20, 1120}, + #endif + #ifdef SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL + {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL}, + #else + {"TLSV1_ALERT_NO_APPLICATION_PROTOCOL", 20, 1120}, + #endif + #ifdef SSL_R_TLSV1_ALERT_NO_RENEGOTIATION + {"TLSV1_ALERT_NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_RENEGOTIATION}, + #else + {"TLSV1_ALERT_NO_RENEGOTIATION", 20, 1100}, + #endif + #ifdef SSL_R_TLSV1_ALERT_NO_RENEGOTIATION + {"TLSV1_ALERT_NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_RENEGOTIATION}, + #else + {"TLSV1_ALERT_NO_RENEGOTIATION", 20, 1100}, + #endif + #ifdef SSL_R_TLSV1_ALERT_PROTOCOL_VERSION + {"TLSV1_ALERT_PROTOCOL_VERSION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_PROTOCOL_VERSION}, + #else + {"TLSV1_ALERT_PROTOCOL_VERSION", 20, 1070}, + #endif + #ifdef SSL_R_TLSV1_ALERT_PROTOCOL_VERSION + {"TLSV1_ALERT_PROTOCOL_VERSION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_PROTOCOL_VERSION}, + #else + {"TLSV1_ALERT_PROTOCOL_VERSION", 20, 1070}, + #endif + #ifdef SSL_R_TLSV1_ALERT_RECORD_OVERFLOW + {"TLSV1_ALERT_RECORD_OVERFLOW", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_RECORD_OVERFLOW}, + #else + {"TLSV1_ALERT_RECORD_OVERFLOW", 20, 1022}, + #endif + #ifdef SSL_R_TLSV1_ALERT_RECORD_OVERFLOW + {"TLSV1_ALERT_RECORD_OVERFLOW", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_RECORD_OVERFLOW}, + #else + {"TLSV1_ALERT_RECORD_OVERFLOW", 20, 1022}, + #endif + #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA + {"TLSV1_ALERT_UNKNOWN_CA", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_CA}, + #else + {"TLSV1_ALERT_UNKNOWN_CA", 20, 1048}, + #endif + #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA + {"TLSV1_ALERT_UNKNOWN_CA", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_CA}, + #else + {"TLSV1_ALERT_UNKNOWN_CA", 20, 1048}, + #endif + #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY + {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY}, + #else + {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", 20, 1115}, + #endif + #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY + {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY}, + #else + {"TLSV1_ALERT_UNKNOWN_PSK_IDENTITY", 20, 1115}, + #endif + #ifdef SSL_R_TLSV1_ALERT_USER_CANCELLED + {"TLSV1_ALERT_USER_CANCELLED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_USER_CANCELLED}, + #else + {"TLSV1_ALERT_USER_CANCELLED", 20, 1090}, + #endif + #ifdef SSL_R_TLSV1_ALERT_USER_CANCELLED + {"TLSV1_ALERT_USER_CANCELLED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_USER_CANCELLED}, + #else + {"TLSV1_ALERT_USER_CANCELLED", 20, 1090}, + #endif + #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE + {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE}, + #else + {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", 20, 1114}, + #endif + #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE + {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE}, + #else + {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", 20, 1114}, + #endif + #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE + {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE}, + #else + {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", 20, 1113}, + #endif + #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE + {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE}, + #else + {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", 20, 1113}, + #endif + #ifdef SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE + {"TLSV1_CERTIFICATE_UNOBTAINABLE", ERR_LIB_SSL, SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE}, + #else + {"TLSV1_CERTIFICATE_UNOBTAINABLE", 20, 1111}, + #endif + #ifdef SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE + {"TLSV1_CERTIFICATE_UNOBTAINABLE", ERR_LIB_SSL, SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE}, + #else + {"TLSV1_CERTIFICATE_UNOBTAINABLE", 20, 1111}, + #endif + #ifdef SSL_R_TLSV1_UNRECOGNIZED_NAME + {"TLSV1_UNRECOGNIZED_NAME", ERR_LIB_SSL, SSL_R_TLSV1_UNRECOGNIZED_NAME}, + #else + {"TLSV1_UNRECOGNIZED_NAME", 20, 1112}, + #endif + #ifdef SSL_R_TLSV1_UNRECOGNIZED_NAME + {"TLSV1_UNRECOGNIZED_NAME", ERR_LIB_SSL, SSL_R_TLSV1_UNRECOGNIZED_NAME}, + #else + {"TLSV1_UNRECOGNIZED_NAME", 20, 1112}, + #endif + #ifdef SSL_R_TLSV1_UNSUPPORTED_EXTENSION + {"TLSV1_UNSUPPORTED_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV1_UNSUPPORTED_EXTENSION}, + #else + {"TLSV1_UNSUPPORTED_EXTENSION", 20, 1110}, + #endif + #ifdef SSL_R_TLSV1_UNSUPPORTED_EXTENSION + {"TLSV1_UNSUPPORTED_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV1_UNSUPPORTED_EXTENSION}, + #else + {"TLSV1_UNSUPPORTED_EXTENSION", 20, 1110}, + #endif + #ifdef SSL_R_TLS_ALERT_BAD_CERTIFICATE + {"TLS_ALERT_BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_TLS_ALERT_BAD_CERTIFICATE}, + #else + {"TLS_ALERT_BAD_CERTIFICATE", 20, 1042}, + #endif + #ifdef SSL_R_TLS_ALERT_BAD_CERTIFICATE + {"TLS_ALERT_BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_TLS_ALERT_BAD_CERTIFICATE}, + #else + {"TLS_ALERT_BAD_CERTIFICATE", 20, 1042}, + #endif + #ifdef SSL_R_TLS_ALERT_BAD_RECORD_MAC + {"TLS_ALERT_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_TLS_ALERT_BAD_RECORD_MAC}, + #else + {"TLS_ALERT_BAD_RECORD_MAC", 20, 1020}, + #endif + #ifdef SSL_R_TLS_ALERT_BAD_RECORD_MAC + {"TLS_ALERT_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_TLS_ALERT_BAD_RECORD_MAC}, + #else + {"TLS_ALERT_BAD_RECORD_MAC", 20, 1020}, + #endif + #ifdef SSL_R_TLS_ALERT_CERTIFICATE_EXPIRED + {"TLS_ALERT_CERTIFICATE_EXPIRED", ERR_LIB_SSL, SSL_R_TLS_ALERT_CERTIFICATE_EXPIRED}, + #else + {"TLS_ALERT_CERTIFICATE_EXPIRED", 20, 1045}, + #endif + #ifdef SSL_R_TLS_ALERT_CERTIFICATE_EXPIRED + {"TLS_ALERT_CERTIFICATE_EXPIRED", ERR_LIB_SSL, SSL_R_TLS_ALERT_CERTIFICATE_EXPIRED}, + #else + {"TLS_ALERT_CERTIFICATE_EXPIRED", 20, 1045}, + #endif + #ifdef SSL_R_TLS_ALERT_CERTIFICATE_REVOKED + {"TLS_ALERT_CERTIFICATE_REVOKED", ERR_LIB_SSL, SSL_R_TLS_ALERT_CERTIFICATE_REVOKED}, + #else + {"TLS_ALERT_CERTIFICATE_REVOKED", 20, 1044}, + #endif + #ifdef SSL_R_TLS_ALERT_CERTIFICATE_REVOKED + {"TLS_ALERT_CERTIFICATE_REVOKED", ERR_LIB_SSL, SSL_R_TLS_ALERT_CERTIFICATE_REVOKED}, + #else + {"TLS_ALERT_CERTIFICATE_REVOKED", 20, 1044}, + #endif + #ifdef SSL_R_TLS_ALERT_CERTIFICATE_UNKNOWN + {"TLS_ALERT_CERTIFICATE_UNKNOWN", ERR_LIB_SSL, SSL_R_TLS_ALERT_CERTIFICATE_UNKNOWN}, + #else + {"TLS_ALERT_CERTIFICATE_UNKNOWN", 20, 1046}, + #endif + #ifdef SSL_R_TLS_ALERT_CERTIFICATE_UNKNOWN + {"TLS_ALERT_CERTIFICATE_UNKNOWN", ERR_LIB_SSL, SSL_R_TLS_ALERT_CERTIFICATE_UNKNOWN}, + #else + {"TLS_ALERT_CERTIFICATE_UNKNOWN", 20, 1046}, + #endif + #ifdef SSL_R_TLS_ALERT_DECOMPRESSION_FAILURE + {"TLS_ALERT_DECOMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_TLS_ALERT_DECOMPRESSION_FAILURE}, + #else + {"TLS_ALERT_DECOMPRESSION_FAILURE", 20, 1030}, + #endif + #ifdef SSL_R_TLS_ALERT_DECOMPRESSION_FAILURE + {"TLS_ALERT_DECOMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_TLS_ALERT_DECOMPRESSION_FAILURE}, + #else + {"TLS_ALERT_DECOMPRESSION_FAILURE", 20, 1030}, + #endif + #ifdef SSL_R_TLS_ALERT_HANDSHAKE_FAILURE + {"TLS_ALERT_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_TLS_ALERT_HANDSHAKE_FAILURE}, + #else + {"TLS_ALERT_HANDSHAKE_FAILURE", 20, 1040}, + #endif + #ifdef SSL_R_TLS_ALERT_HANDSHAKE_FAILURE + {"TLS_ALERT_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_TLS_ALERT_HANDSHAKE_FAILURE}, + #else + {"TLS_ALERT_HANDSHAKE_FAILURE", 20, 1040}, + #endif + #ifdef SSL_R_TLS_ALERT_ILLEGAL_PARAMETER + {"TLS_ALERT_ILLEGAL_PARAMETER", ERR_LIB_SSL, SSL_R_TLS_ALERT_ILLEGAL_PARAMETER}, + #else + {"TLS_ALERT_ILLEGAL_PARAMETER", 20, 1047}, + #endif + #ifdef SSL_R_TLS_ALERT_ILLEGAL_PARAMETER + {"TLS_ALERT_ILLEGAL_PARAMETER", ERR_LIB_SSL, SSL_R_TLS_ALERT_ILLEGAL_PARAMETER}, + #else + {"TLS_ALERT_ILLEGAL_PARAMETER", 20, 1047}, + #endif + #ifdef SSL_R_TLS_ALERT_NO_CERTIFICATE + {"TLS_ALERT_NO_CERTIFICATE", ERR_LIB_SSL, SSL_R_TLS_ALERT_NO_CERTIFICATE}, + #else + {"TLS_ALERT_NO_CERTIFICATE", 20, 1041}, + #endif + #ifdef SSL_R_TLS_ALERT_NO_CERTIFICATE + {"TLS_ALERT_NO_CERTIFICATE", ERR_LIB_SSL, SSL_R_TLS_ALERT_NO_CERTIFICATE}, + #else + {"TLS_ALERT_NO_CERTIFICATE", 20, 1041}, + #endif + #ifdef SSL_R_TLS_ALERT_UNEXPECTED_MESSAGE + {"TLS_ALERT_UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_TLS_ALERT_UNEXPECTED_MESSAGE}, + #else + {"TLS_ALERT_UNEXPECTED_MESSAGE", 20, 1010}, + #endif + #ifdef SSL_R_TLS_ALERT_UNEXPECTED_MESSAGE + {"TLS_ALERT_UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_TLS_ALERT_UNEXPECTED_MESSAGE}, + #else + {"TLS_ALERT_UNEXPECTED_MESSAGE", 20, 1010}, + #endif + #ifdef SSL_R_TLS_ALERT_UNSUPPORTED_CERTIFICATE + {"TLS_ALERT_UNSUPPORTED_CERTIFICATE", ERR_LIB_SSL, SSL_R_TLS_ALERT_UNSUPPORTED_CERTIFICATE}, + #else + {"TLS_ALERT_UNSUPPORTED_CERTIFICATE", 20, 1043}, + #endif + #ifdef SSL_R_TLS_ALERT_UNSUPPORTED_CERTIFICATE + {"TLS_ALERT_UNSUPPORTED_CERTIFICATE", ERR_LIB_SSL, SSL_R_TLS_ALERT_UNSUPPORTED_CERTIFICATE}, + #else + {"TLS_ALERT_UNSUPPORTED_CERTIFICATE", 20, 1043}, + #endif + #ifdef SSL_R_TLS_EXT_INVALID_MAX_FRAGMENT_LENGTH + {"TLS_EXT_INVALID_MAX_FRAGMENT_LENGTH", ERR_LIB_SSL, SSL_R_TLS_EXT_INVALID_MAX_FRAGMENT_LENGTH}, + #else + {"TLS_EXT_INVALID_MAX_FRAGMENT_LENGTH", 20, 232}, + #endif + #ifdef SSL_R_TLS_EXT_INVALID_SERVERNAME + {"TLS_EXT_INVALID_SERVERNAME", ERR_LIB_SSL, SSL_R_TLS_EXT_INVALID_SERVERNAME}, + #else + {"TLS_EXT_INVALID_SERVERNAME", 20, 319}, + #endif + #ifdef SSL_R_TLS_EXT_INVALID_SERVERNAME_TYPE + {"TLS_EXT_INVALID_SERVERNAME_TYPE", ERR_LIB_SSL, SSL_R_TLS_EXT_INVALID_SERVERNAME_TYPE}, + #else + {"TLS_EXT_INVALID_SERVERNAME_TYPE", 20, 320}, + #endif + #ifdef SSL_R_TLS_ILLEGAL_EXPORTER_LABEL + {"TLS_ILLEGAL_EXPORTER_LABEL", ERR_LIB_SSL, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL}, + #else + {"TLS_ILLEGAL_EXPORTER_LABEL", 20, 367}, + #endif + #ifdef SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST + {"TLS_INVALID_ECPOINTFORMAT_LIST", ERR_LIB_SSL, SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST}, + #else + {"TLS_INVALID_ECPOINTFORMAT_LIST", 20, 157}, + #endif + #ifdef SSL_R_TLS_SESSION_ID_TOO_LONG + {"TLS_SESSION_ID_TOO_LONG", ERR_LIB_SSL, SSL_R_TLS_SESSION_ID_TOO_LONG}, + #else + {"TLS_SESSION_ID_TOO_LONG", 20, 300}, + #endif + #ifdef SSL_R_TOO_MANY_KEY_UPDATES + {"TOO_MANY_KEY_UPDATES", ERR_LIB_SSL, SSL_R_TOO_MANY_KEY_UPDATES}, + #else + {"TOO_MANY_KEY_UPDATES", 20, 132}, + #endif + #ifdef SSL_R_TOO_MANY_WARN_ALERTS + {"TOO_MANY_WARN_ALERTS", ERR_LIB_SSL, SSL_R_TOO_MANY_WARN_ALERTS}, + #else + {"TOO_MANY_WARN_ALERTS", 20, 409}, + #endif + #ifdef SSL_R_TOO_MUCH_EARLY_DATA + {"TOO_MUCH_EARLY_DATA", ERR_LIB_SSL, SSL_R_TOO_MUCH_EARLY_DATA}, + #else + {"TOO_MUCH_EARLY_DATA", 20, 164}, + #endif + #ifdef SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS + {"UNABLE_TO_FIND_ECDH_PARAMETERS", ERR_LIB_SSL, SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS}, + #else + {"UNABLE_TO_FIND_ECDH_PARAMETERS", 20, 314}, + #endif + #ifdef SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS + {"UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS", ERR_LIB_SSL, SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS}, + #else + {"UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS", 20, 239}, + #endif + #ifdef SSL_R_UNEXPECTED_CCS_MESSAGE + {"UNEXPECTED_CCS_MESSAGE", ERR_LIB_SSL, SSL_R_UNEXPECTED_CCS_MESSAGE}, + #else + {"UNEXPECTED_CCS_MESSAGE", 20, 262}, + #endif + #ifdef SSL_R_UNEXPECTED_END_OF_EARLY_DATA + {"UNEXPECTED_END_OF_EARLY_DATA", ERR_LIB_SSL, SSL_R_UNEXPECTED_END_OF_EARLY_DATA}, + #else + {"UNEXPECTED_END_OF_EARLY_DATA", 20, 178}, + #endif + #ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING + {"UNEXPECTED_EOF_WHILE_READING", ERR_LIB_SSL, SSL_R_UNEXPECTED_EOF_WHILE_READING}, + #else + {"UNEXPECTED_EOF_WHILE_READING", 20, 294}, + #endif + #ifdef SSL_R_UNEXPECTED_MESSAGE + {"UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_UNEXPECTED_MESSAGE}, + #else + {"UNEXPECTED_MESSAGE", 20, 244}, + #endif + #ifdef SSL_R_UNEXPECTED_RECORD + {"UNEXPECTED_RECORD", ERR_LIB_SSL, SSL_R_UNEXPECTED_RECORD}, + #else + {"UNEXPECTED_RECORD", 20, 245}, + #endif + #ifdef SSL_R_UNINITIALIZED + {"UNINITIALIZED", ERR_LIB_SSL, SSL_R_UNINITIALIZED}, + #else + {"UNINITIALIZED", 20, 276}, + #endif + #ifdef SSL_R_UNKNOWN_ALERT_TYPE + {"UNKNOWN_ALERT_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_ALERT_TYPE}, + #else + {"UNKNOWN_ALERT_TYPE", 20, 246}, + #endif + #ifdef SSL_R_UNKNOWN_CERTIFICATE_TYPE + {"UNKNOWN_CERTIFICATE_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE}, + #else + {"UNKNOWN_CERTIFICATE_TYPE", 20, 247}, + #endif + #ifdef SSL_R_UNKNOWN_CIPHER_RETURNED + {"UNKNOWN_CIPHER_RETURNED", ERR_LIB_SSL, SSL_R_UNKNOWN_CIPHER_RETURNED}, + #else + {"UNKNOWN_CIPHER_RETURNED", 20, 248}, + #endif + #ifdef SSL_R_UNKNOWN_CIPHER_TYPE + {"UNKNOWN_CIPHER_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_CIPHER_TYPE}, + #else + {"UNKNOWN_CIPHER_TYPE", 20, 249}, + #endif + #ifdef SSL_R_UNKNOWN_CMD_NAME + {"UNKNOWN_CMD_NAME", ERR_LIB_SSL, SSL_R_UNKNOWN_CMD_NAME}, + #else + {"UNKNOWN_CMD_NAME", 20, 386}, + #endif + #ifdef SSL_R_UNKNOWN_COMMAND + {"UNKNOWN_COMMAND", ERR_LIB_SSL, SSL_R_UNKNOWN_COMMAND}, + #else + {"UNKNOWN_COMMAND", 20, 139}, + #endif + #ifdef SSL_R_UNKNOWN_DIGEST + {"UNKNOWN_DIGEST", ERR_LIB_SSL, SSL_R_UNKNOWN_DIGEST}, + #else + {"UNKNOWN_DIGEST", 20, 368}, + #endif + #ifdef SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE + {"UNKNOWN_KEY_EXCHANGE_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE}, + #else + {"UNKNOWN_KEY_EXCHANGE_TYPE", 20, 250}, + #endif + #ifdef SSL_R_UNKNOWN_MANDATORY_PARAMETER + {"UNKNOWN_MANDATORY_PARAMETER", ERR_LIB_SSL, SSL_R_UNKNOWN_MANDATORY_PARAMETER}, + #else + {"UNKNOWN_MANDATORY_PARAMETER", 20, 323}, + #endif + #ifdef SSL_R_UNKNOWN_PKEY_TYPE + {"UNKNOWN_PKEY_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_PKEY_TYPE}, + #else + {"UNKNOWN_PKEY_TYPE", 20, 251}, + #endif + #ifdef SSL_R_UNKNOWN_PROTOCOL + {"UNKNOWN_PROTOCOL", ERR_LIB_SSL, SSL_R_UNKNOWN_PROTOCOL}, + #else + {"UNKNOWN_PROTOCOL", 20, 252}, + #endif + #ifdef SSL_R_UNKNOWN_SSL_VERSION + {"UNKNOWN_SSL_VERSION", ERR_LIB_SSL, SSL_R_UNKNOWN_SSL_VERSION}, + #else + {"UNKNOWN_SSL_VERSION", 20, 254}, + #endif + #ifdef SSL_R_UNKNOWN_STATE + {"UNKNOWN_STATE", ERR_LIB_SSL, SSL_R_UNKNOWN_STATE}, + #else + {"UNKNOWN_STATE", 20, 255}, + #endif + #ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED + {"UNSAFE_LEGACY_RENEGOTIATION_DISABLED", ERR_LIB_SSL, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED}, + #else + {"UNSAFE_LEGACY_RENEGOTIATION_DISABLED", 20, 338}, + #endif + #ifdef SSL_R_UNSOLICITED_EXTENSION + {"UNSOLICITED_EXTENSION", ERR_LIB_SSL, SSL_R_UNSOLICITED_EXTENSION}, + #else + {"UNSOLICITED_EXTENSION", 20, 217}, + #endif + #ifdef SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM + {"UNSUPPORTED_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM}, + #else + {"UNSUPPORTED_COMPRESSION_ALGORITHM", 20, 257}, + #endif + #ifdef SSL_R_UNSUPPORTED_CONFIG_VALUE + {"UNSUPPORTED_CONFIG_VALUE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_CONFIG_VALUE}, + #else + {"UNSUPPORTED_CONFIG_VALUE", 20, 414}, + #endif + #ifdef SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS + {"UNSUPPORTED_CONFIG_VALUE_CLASS", ERR_LIB_SSL, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS}, + #else + {"UNSUPPORTED_CONFIG_VALUE_CLASS", 20, 415}, + #endif + #ifdef SSL_R_UNSUPPORTED_CONFIG_VALUE_OP + {"UNSUPPORTED_CONFIG_VALUE_OP", ERR_LIB_SSL, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP}, + #else + {"UNSUPPORTED_CONFIG_VALUE_OP", 20, 416}, + #endif + #ifdef SSL_R_UNSUPPORTED_ELLIPTIC_CURVE + {"UNSUPPORTED_ELLIPTIC_CURVE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE}, + #else + {"UNSUPPORTED_ELLIPTIC_CURVE", 20, 315}, + #endif + #ifdef SSL_R_UNSUPPORTED_PROTOCOL + {"UNSUPPORTED_PROTOCOL", ERR_LIB_SSL, SSL_R_UNSUPPORTED_PROTOCOL}, + #else + {"UNSUPPORTED_PROTOCOL", 20, 258}, + #endif + #ifdef SSL_R_UNSUPPORTED_SSL_VERSION + {"UNSUPPORTED_SSL_VERSION", ERR_LIB_SSL, SSL_R_UNSUPPORTED_SSL_VERSION}, + #else + {"UNSUPPORTED_SSL_VERSION", 20, 259}, + #endif + #ifdef SSL_R_UNSUPPORTED_STATUS_TYPE + {"UNSUPPORTED_STATUS_TYPE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_STATUS_TYPE}, + #else + {"UNSUPPORTED_STATUS_TYPE", 20, 329}, + #endif + #ifdef SSL_R_UNSUPPORTED_WRITE_FLAG + {"UNSUPPORTED_WRITE_FLAG", ERR_LIB_SSL, SSL_R_UNSUPPORTED_WRITE_FLAG}, + #else + {"UNSUPPORTED_WRITE_FLAG", 20, 412}, + #endif + #ifdef SSL_R_USE_SRTP_NOT_NEGOTIATED + {"USE_SRTP_NOT_NEGOTIATED", ERR_LIB_SSL, SSL_R_USE_SRTP_NOT_NEGOTIATED}, + #else + {"USE_SRTP_NOT_NEGOTIATED", 20, 369}, + #endif + #ifdef SSL_R_VERSION_TOO_HIGH + {"VERSION_TOO_HIGH", ERR_LIB_SSL, SSL_R_VERSION_TOO_HIGH}, + #else + {"VERSION_TOO_HIGH", 20, 166}, + #endif + #ifdef SSL_R_VERSION_TOO_LOW + {"VERSION_TOO_LOW", ERR_LIB_SSL, SSL_R_VERSION_TOO_LOW}, + #else + {"VERSION_TOO_LOW", 20, 396}, + #endif + #ifdef SSL_R_WRONG_CERTIFICATE_TYPE + {"WRONG_CERTIFICATE_TYPE", ERR_LIB_SSL, SSL_R_WRONG_CERTIFICATE_TYPE}, + #else + {"WRONG_CERTIFICATE_TYPE", 20, 383}, + #endif + #ifdef SSL_R_WRONG_CIPHER_RETURNED + {"WRONG_CIPHER_RETURNED", ERR_LIB_SSL, SSL_R_WRONG_CIPHER_RETURNED}, + #else + {"WRONG_CIPHER_RETURNED", 20, 261}, + #endif + #ifdef SSL_R_WRONG_CURVE + {"WRONG_CURVE", ERR_LIB_SSL, SSL_R_WRONG_CURVE}, + #else + {"WRONG_CURVE", 20, 378}, + #endif + #ifdef SSL_R_WRONG_RPK_TYPE + {"WRONG_RPK_TYPE", ERR_LIB_SSL, SSL_R_WRONG_RPK_TYPE}, + #else + {"WRONG_RPK_TYPE", 20, 351}, + #endif + #ifdef SSL_R_WRONG_SIGNATURE_LENGTH + {"WRONG_SIGNATURE_LENGTH", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_LENGTH}, + #else + {"WRONG_SIGNATURE_LENGTH", 20, 264}, + #endif + #ifdef SSL_R_WRONG_SIGNATURE_SIZE + {"WRONG_SIGNATURE_SIZE", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_SIZE}, + #else + {"WRONG_SIGNATURE_SIZE", 20, 265}, + #endif + #ifdef SSL_R_WRONG_SIGNATURE_TYPE + {"WRONG_SIGNATURE_TYPE", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_TYPE}, + #else + {"WRONG_SIGNATURE_TYPE", 20, 370}, + #endif + #ifdef SSL_R_WRONG_SSL_VERSION + {"WRONG_SSL_VERSION", ERR_LIB_SSL, SSL_R_WRONG_SSL_VERSION}, + #else + {"WRONG_SSL_VERSION", 20, 266}, + #endif + #ifdef SSL_R_WRONG_VERSION_NUMBER + {"WRONG_VERSION_NUMBER", ERR_LIB_SSL, SSL_R_WRONG_VERSION_NUMBER}, + #else + {"WRONG_VERSION_NUMBER", 20, 267}, + #endif + #ifdef SSL_R_X509_LIB + {"X509_LIB", ERR_LIB_SSL, SSL_R_X509_LIB}, + #else + {"X509_LIB", 20, 268}, + #endif + #ifdef SSL_R_X509_VERIFICATION_SETUP_PROBLEMS + {"X509_VERIFICATION_SETUP_PROBLEMS", ERR_LIB_SSL, SSL_R_X509_VERIFICATION_SETUP_PROBLEMS}, + #else + {"X509_VERIFICATION_SETUP_PROBLEMS", 20, 269}, + #endif + #ifdef TS_R_BAD_PKCS7_TYPE + {"BAD_PKCS7_TYPE", ERR_LIB_TS, TS_R_BAD_PKCS7_TYPE}, + #else + {"BAD_PKCS7_TYPE", 47, 132}, + #endif + #ifdef TS_R_BAD_TYPE + {"BAD_TYPE", ERR_LIB_TS, TS_R_BAD_TYPE}, + #else + {"BAD_TYPE", 47, 133}, + #endif + #ifdef TS_R_CANNOT_LOAD_CERT + {"CANNOT_LOAD_CERT", ERR_LIB_TS, TS_R_CANNOT_LOAD_CERT}, + #else + {"CANNOT_LOAD_CERT", 47, 137}, + #endif + #ifdef TS_R_CANNOT_LOAD_KEY + {"CANNOT_LOAD_KEY", ERR_LIB_TS, TS_R_CANNOT_LOAD_KEY}, + #else + {"CANNOT_LOAD_KEY", 47, 138}, + #endif + #ifdef TS_R_CERTIFICATE_VERIFY_ERROR + {"CERTIFICATE_VERIFY_ERROR", ERR_LIB_TS, TS_R_CERTIFICATE_VERIFY_ERROR}, + #else + {"CERTIFICATE_VERIFY_ERROR", 47, 100}, + #endif + #ifdef TS_R_COULD_NOT_SET_ENGINE + {"COULD_NOT_SET_ENGINE", ERR_LIB_TS, TS_R_COULD_NOT_SET_ENGINE}, + #else + {"COULD_NOT_SET_ENGINE", 47, 127}, + #endif + #ifdef TS_R_COULD_NOT_SET_TIME + {"COULD_NOT_SET_TIME", ERR_LIB_TS, TS_R_COULD_NOT_SET_TIME}, + #else + {"COULD_NOT_SET_TIME", 47, 115}, + #endif + #ifdef TS_R_DETACHED_CONTENT + {"DETACHED_CONTENT", ERR_LIB_TS, TS_R_DETACHED_CONTENT}, + #else + {"DETACHED_CONTENT", 47, 134}, + #endif + #ifdef TS_R_ESS_ADD_SIGNING_CERT_ERROR + {"ESS_ADD_SIGNING_CERT_ERROR", ERR_LIB_TS, TS_R_ESS_ADD_SIGNING_CERT_ERROR}, + #else + {"ESS_ADD_SIGNING_CERT_ERROR", 47, 116}, + #endif + #ifdef TS_R_ESS_ADD_SIGNING_CERT_V2_ERROR + {"ESS_ADD_SIGNING_CERT_V2_ERROR", ERR_LIB_TS, TS_R_ESS_ADD_SIGNING_CERT_V2_ERROR}, + #else + {"ESS_ADD_SIGNING_CERT_V2_ERROR", 47, 139}, + #endif + #ifdef TS_R_ESS_SIGNING_CERTIFICATE_ERROR + {"ESS_SIGNING_CERTIFICATE_ERROR", ERR_LIB_TS, TS_R_ESS_SIGNING_CERTIFICATE_ERROR}, + #else + {"ESS_SIGNING_CERTIFICATE_ERROR", 47, 101}, + #endif + #ifdef TS_R_INVALID_NULL_POINTER + {"INVALID_NULL_POINTER", ERR_LIB_TS, TS_R_INVALID_NULL_POINTER}, + #else + {"INVALID_NULL_POINTER", 47, 102}, + #endif + #ifdef TS_R_INVALID_SIGNER_CERTIFICATE_PURPOSE + {"INVALID_SIGNER_CERTIFICATE_PURPOSE", ERR_LIB_TS, TS_R_INVALID_SIGNER_CERTIFICATE_PURPOSE}, + #else + {"INVALID_SIGNER_CERTIFICATE_PURPOSE", 47, 117}, + #endif + #ifdef TS_R_MESSAGE_IMPRINT_MISMATCH + {"MESSAGE_IMPRINT_MISMATCH", ERR_LIB_TS, TS_R_MESSAGE_IMPRINT_MISMATCH}, + #else + {"MESSAGE_IMPRINT_MISMATCH", 47, 103}, + #endif + #ifdef TS_R_NONCE_MISMATCH + {"NONCE_MISMATCH", ERR_LIB_TS, TS_R_NONCE_MISMATCH}, + #else + {"NONCE_MISMATCH", 47, 104}, + #endif + #ifdef TS_R_NONCE_NOT_RETURNED + {"NONCE_NOT_RETURNED", ERR_LIB_TS, TS_R_NONCE_NOT_RETURNED}, + #else + {"NONCE_NOT_RETURNED", 47, 105}, + #endif + #ifdef TS_R_NO_CONTENT + {"NO_CONTENT", ERR_LIB_TS, TS_R_NO_CONTENT}, + #else + {"NO_CONTENT", 47, 106}, + #endif + #ifdef TS_R_NO_TIME_STAMP_TOKEN + {"NO_TIME_STAMP_TOKEN", ERR_LIB_TS, TS_R_NO_TIME_STAMP_TOKEN}, + #else + {"NO_TIME_STAMP_TOKEN", 47, 107}, + #endif + #ifdef TS_R_PKCS7_ADD_SIGNATURE_ERROR + {"PKCS7_ADD_SIGNATURE_ERROR", ERR_LIB_TS, TS_R_PKCS7_ADD_SIGNATURE_ERROR}, + #else + {"PKCS7_ADD_SIGNATURE_ERROR", 47, 118}, + #endif + #ifdef TS_R_PKCS7_ADD_SIGNED_ATTR_ERROR + {"PKCS7_ADD_SIGNED_ATTR_ERROR", ERR_LIB_TS, TS_R_PKCS7_ADD_SIGNED_ATTR_ERROR}, + #else + {"PKCS7_ADD_SIGNED_ATTR_ERROR", 47, 119}, + #endif + #ifdef TS_R_PKCS7_TO_TS_TST_INFO_FAILED + {"PKCS7_TO_TS_TST_INFO_FAILED", ERR_LIB_TS, TS_R_PKCS7_TO_TS_TST_INFO_FAILED}, + #else + {"PKCS7_TO_TS_TST_INFO_FAILED", 47, 129}, + #endif + #ifdef TS_R_POLICY_MISMATCH + {"POLICY_MISMATCH", ERR_LIB_TS, TS_R_POLICY_MISMATCH}, + #else + {"POLICY_MISMATCH", 47, 108}, + #endif + #ifdef TS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", ERR_LIB_TS, TS_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE}, + #else + {"PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE", 47, 120}, + #endif + #ifdef TS_R_RESPONSE_SETUP_ERROR + {"RESPONSE_SETUP_ERROR", ERR_LIB_TS, TS_R_RESPONSE_SETUP_ERROR}, + #else + {"RESPONSE_SETUP_ERROR", 47, 121}, + #endif + #ifdef TS_R_SIGNATURE_FAILURE + {"SIGNATURE_FAILURE", ERR_LIB_TS, TS_R_SIGNATURE_FAILURE}, + #else + {"SIGNATURE_FAILURE", 47, 109}, + #endif + #ifdef TS_R_THERE_MUST_BE_ONE_SIGNER + {"THERE_MUST_BE_ONE_SIGNER", ERR_LIB_TS, TS_R_THERE_MUST_BE_ONE_SIGNER}, + #else + {"THERE_MUST_BE_ONE_SIGNER", 47, 110}, + #endif + #ifdef TS_R_TIME_SYSCALL_ERROR + {"TIME_SYSCALL_ERROR", ERR_LIB_TS, TS_R_TIME_SYSCALL_ERROR}, + #else + {"TIME_SYSCALL_ERROR", 47, 122}, + #endif + #ifdef TS_R_TOKEN_NOT_PRESENT + {"TOKEN_NOT_PRESENT", ERR_LIB_TS, TS_R_TOKEN_NOT_PRESENT}, + #else + {"TOKEN_NOT_PRESENT", 47, 130}, + #endif + #ifdef TS_R_TOKEN_PRESENT + {"TOKEN_PRESENT", ERR_LIB_TS, TS_R_TOKEN_PRESENT}, + #else + {"TOKEN_PRESENT", 47, 131}, + #endif + #ifdef TS_R_TSA_NAME_MISMATCH + {"TSA_NAME_MISMATCH", ERR_LIB_TS, TS_R_TSA_NAME_MISMATCH}, + #else + {"TSA_NAME_MISMATCH", 47, 111}, + #endif + #ifdef TS_R_TSA_UNTRUSTED + {"TSA_UNTRUSTED", ERR_LIB_TS, TS_R_TSA_UNTRUSTED}, + #else + {"TSA_UNTRUSTED", 47, 112}, + #endif + #ifdef TS_R_TST_INFO_SETUP_ERROR + {"TST_INFO_SETUP_ERROR", ERR_LIB_TS, TS_R_TST_INFO_SETUP_ERROR}, + #else + {"TST_INFO_SETUP_ERROR", 47, 123}, + #endif + #ifdef TS_R_TS_DATASIGN + {"TS_DATASIGN", ERR_LIB_TS, TS_R_TS_DATASIGN}, + #else + {"TS_DATASIGN", 47, 124}, + #endif + #ifdef TS_R_UNACCEPTABLE_POLICY + {"UNACCEPTABLE_POLICY", ERR_LIB_TS, TS_R_UNACCEPTABLE_POLICY}, + #else + {"UNACCEPTABLE_POLICY", 47, 125}, + #endif + #ifdef TS_R_UNSUPPORTED_MD_ALGORITHM + {"UNSUPPORTED_MD_ALGORITHM", ERR_LIB_TS, TS_R_UNSUPPORTED_MD_ALGORITHM}, + #else + {"UNSUPPORTED_MD_ALGORITHM", 47, 126}, + #endif + #ifdef TS_R_UNSUPPORTED_VERSION + {"UNSUPPORTED_VERSION", ERR_LIB_TS, TS_R_UNSUPPORTED_VERSION}, + #else + {"UNSUPPORTED_VERSION", 47, 113}, + #endif + #ifdef TS_R_VAR_BAD_VALUE + {"VAR_BAD_VALUE", ERR_LIB_TS, TS_R_VAR_BAD_VALUE}, + #else + {"VAR_BAD_VALUE", 47, 135}, + #endif + #ifdef TS_R_VAR_LOOKUP_FAILURE + {"VAR_LOOKUP_FAILURE", ERR_LIB_TS, TS_R_VAR_LOOKUP_FAILURE}, + #else + {"VAR_LOOKUP_FAILURE", 47, 136}, + #endif + #ifdef TS_R_WRONG_CONTENT_TYPE + {"WRONG_CONTENT_TYPE", ERR_LIB_TS, TS_R_WRONG_CONTENT_TYPE}, + #else + {"WRONG_CONTENT_TYPE", 47, 114}, + #endif + #ifdef UI_R_COMMON_OK_AND_CANCEL_CHARACTERS + {"COMMON_OK_AND_CANCEL_CHARACTERS", ERR_LIB_UI, UI_R_COMMON_OK_AND_CANCEL_CHARACTERS}, + #else + {"COMMON_OK_AND_CANCEL_CHARACTERS", 40, 104}, + #endif + #ifdef UI_R_INDEX_TOO_LARGE + {"INDEX_TOO_LARGE", ERR_LIB_UI, UI_R_INDEX_TOO_LARGE}, + #else + {"INDEX_TOO_LARGE", 40, 102}, + #endif + #ifdef UI_R_INDEX_TOO_SMALL + {"INDEX_TOO_SMALL", ERR_LIB_UI, UI_R_INDEX_TOO_SMALL}, + #else + {"INDEX_TOO_SMALL", 40, 103}, + #endif + #ifdef UI_R_NO_RESULT_BUFFER + {"NO_RESULT_BUFFER", ERR_LIB_UI, UI_R_NO_RESULT_BUFFER}, + #else + {"NO_RESULT_BUFFER", 40, 105}, + #endif + #ifdef UI_R_PROCESSING_ERROR + {"PROCESSING_ERROR", ERR_LIB_UI, UI_R_PROCESSING_ERROR}, + #else + {"PROCESSING_ERROR", 40, 107}, + #endif + #ifdef UI_R_RESULT_TOO_LARGE + {"RESULT_TOO_LARGE", ERR_LIB_UI, UI_R_RESULT_TOO_LARGE}, + #else + {"RESULT_TOO_LARGE", 40, 100}, + #endif + #ifdef UI_R_RESULT_TOO_SMALL + {"RESULT_TOO_SMALL", ERR_LIB_UI, UI_R_RESULT_TOO_SMALL}, + #else + {"RESULT_TOO_SMALL", 40, 101}, + #endif + #ifdef UI_R_SYSASSIGN_ERROR + {"SYSASSIGN_ERROR", ERR_LIB_UI, UI_R_SYSASSIGN_ERROR}, + #else + {"SYSASSIGN_ERROR", 40, 109}, + #endif + #ifdef UI_R_SYSDASSGN_ERROR + {"SYSDASSGN_ERROR", ERR_LIB_UI, UI_R_SYSDASSGN_ERROR}, + #else + {"SYSDASSGN_ERROR", 40, 110}, + #endif + #ifdef UI_R_SYSQIOW_ERROR + {"SYSQIOW_ERROR", ERR_LIB_UI, UI_R_SYSQIOW_ERROR}, + #else + {"SYSQIOW_ERROR", 40, 111}, + #endif + #ifdef UI_R_UNKNOWN_CONTROL_COMMAND + {"UNKNOWN_CONTROL_COMMAND", ERR_LIB_UI, UI_R_UNKNOWN_CONTROL_COMMAND}, + #else + {"UNKNOWN_CONTROL_COMMAND", 40, 106}, + #endif + #ifdef UI_R_UNKNOWN_TTYGET_ERRNO_VALUE + {"UNKNOWN_TTYGET_ERRNO_VALUE", ERR_LIB_UI, UI_R_UNKNOWN_TTYGET_ERRNO_VALUE}, + #else + {"UNKNOWN_TTYGET_ERRNO_VALUE", 40, 108}, + #endif + #ifdef UI_R_USER_DATA_DUPLICATION_UNSUPPORTED + {"USER_DATA_DUPLICATION_UNSUPPORTED", ERR_LIB_UI, UI_R_USER_DATA_DUPLICATION_UNSUPPORTED}, + #else + {"USER_DATA_DUPLICATION_UNSUPPORTED", 40, 112}, + #endif + #ifdef X509V3_R_BAD_IP_ADDRESS + {"BAD_IP_ADDRESS", ERR_LIB_X509V3, X509V3_R_BAD_IP_ADDRESS}, + #else + {"BAD_IP_ADDRESS", 34, 118}, + #endif + #ifdef X509V3_R_BAD_OBJECT + {"BAD_OBJECT", ERR_LIB_X509V3, X509V3_R_BAD_OBJECT}, + #else + {"BAD_OBJECT", 34, 119}, + #endif + #ifdef X509V3_R_BAD_OPTION + {"BAD_OPTION", ERR_LIB_X509V3, X509V3_R_BAD_OPTION}, + #else + {"BAD_OPTION", 34, 170}, + #endif + #ifdef X509V3_R_BAD_VALUE + {"BAD_VALUE", ERR_LIB_X509V3, X509V3_R_BAD_VALUE}, + #else + {"BAD_VALUE", 34, 171}, + #endif + #ifdef X509V3_R_BN_DEC2BN_ERROR + {"BN_DEC2BN_ERROR", ERR_LIB_X509V3, X509V3_R_BN_DEC2BN_ERROR}, + #else + {"BN_DEC2BN_ERROR", 34, 100}, + #endif + #ifdef X509V3_R_BN_TO_ASN1_INTEGER_ERROR + {"BN_TO_ASN1_INTEGER_ERROR", ERR_LIB_X509V3, X509V3_R_BN_TO_ASN1_INTEGER_ERROR}, + #else + {"BN_TO_ASN1_INTEGER_ERROR", 34, 101}, + #endif + #ifdef X509V3_R_DIRNAME_ERROR + {"DIRNAME_ERROR", ERR_LIB_X509V3, X509V3_R_DIRNAME_ERROR}, + #else + {"DIRNAME_ERROR", 34, 149}, + #endif + #ifdef X509V3_R_DISTPOINT_ALREADY_SET + {"DISTPOINT_ALREADY_SET", ERR_LIB_X509V3, X509V3_R_DISTPOINT_ALREADY_SET}, + #else + {"DISTPOINT_ALREADY_SET", 34, 160}, + #endif + #ifdef X509V3_R_DUPLICATE_ZONE_ID + {"DUPLICATE_ZONE_ID", ERR_LIB_X509V3, X509V3_R_DUPLICATE_ZONE_ID}, + #else + {"DUPLICATE_ZONE_ID", 34, 133}, + #endif + #ifdef X509V3_R_EMPTY_KEY_USAGE + {"EMPTY_KEY_USAGE", ERR_LIB_X509V3, X509V3_R_EMPTY_KEY_USAGE}, + #else + {"EMPTY_KEY_USAGE", 34, 169}, + #endif + #ifdef X509V3_R_ERROR_CONVERTING_ZONE + {"ERROR_CONVERTING_ZONE", ERR_LIB_X509V3, X509V3_R_ERROR_CONVERTING_ZONE}, + #else + {"ERROR_CONVERTING_ZONE", 34, 131}, + #endif + #ifdef X509V3_R_ERROR_CREATING_EXTENSION + {"ERROR_CREATING_EXTENSION", ERR_LIB_X509V3, X509V3_R_ERROR_CREATING_EXTENSION}, + #else + {"ERROR_CREATING_EXTENSION", 34, 144}, + #endif + #ifdef X509V3_R_ERROR_IN_EXTENSION + {"ERROR_IN_EXTENSION", ERR_LIB_X509V3, X509V3_R_ERROR_IN_EXTENSION}, + #else + {"ERROR_IN_EXTENSION", 34, 128}, + #endif + #ifdef X509V3_R_EXPECTED_A_SECTION_NAME + {"EXPECTED_A_SECTION_NAME", ERR_LIB_X509V3, X509V3_R_EXPECTED_A_SECTION_NAME}, + #else + {"EXPECTED_A_SECTION_NAME", 34, 137}, + #endif + #ifdef X509V3_R_EXTENSION_EXISTS + {"EXTENSION_EXISTS", ERR_LIB_X509V3, X509V3_R_EXTENSION_EXISTS}, + #else + {"EXTENSION_EXISTS", 34, 145}, + #endif + #ifdef X509V3_R_EXTENSION_NAME_ERROR + {"EXTENSION_NAME_ERROR", ERR_LIB_X509V3, X509V3_R_EXTENSION_NAME_ERROR}, + #else + {"EXTENSION_NAME_ERROR", 34, 115}, + #endif + #ifdef X509V3_R_EXTENSION_NOT_FOUND + {"EXTENSION_NOT_FOUND", ERR_LIB_X509V3, X509V3_R_EXTENSION_NOT_FOUND}, + #else + {"EXTENSION_NOT_FOUND", 34, 102}, + #endif + #ifdef X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED + {"EXTENSION_SETTING_NOT_SUPPORTED", ERR_LIB_X509V3, X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED}, + #else + {"EXTENSION_SETTING_NOT_SUPPORTED", 34, 103}, + #endif + #ifdef X509V3_R_EXTENSION_VALUE_ERROR + {"EXTENSION_VALUE_ERROR", ERR_LIB_X509V3, X509V3_R_EXTENSION_VALUE_ERROR}, + #else + {"EXTENSION_VALUE_ERROR", 34, 116}, + #endif + #ifdef X509V3_R_ILLEGAL_EMPTY_EXTENSION + {"ILLEGAL_EMPTY_EXTENSION", ERR_LIB_X509V3, X509V3_R_ILLEGAL_EMPTY_EXTENSION}, + #else + {"ILLEGAL_EMPTY_EXTENSION", 34, 151}, + #endif + #ifdef X509V3_R_INCORRECT_POLICY_SYNTAX_TAG + {"INCORRECT_POLICY_SYNTAX_TAG", ERR_LIB_X509V3, X509V3_R_INCORRECT_POLICY_SYNTAX_TAG}, + #else + {"INCORRECT_POLICY_SYNTAX_TAG", 34, 152}, + #endif + #ifdef X509V3_R_INVALID_ASNUMBER + {"INVALID_ASNUMBER", ERR_LIB_X509V3, X509V3_R_INVALID_ASNUMBER}, + #else + {"INVALID_ASNUMBER", 34, 162}, + #endif + #ifdef X509V3_R_INVALID_ASRANGE + {"INVALID_ASRANGE", ERR_LIB_X509V3, X509V3_R_INVALID_ASRANGE}, + #else + {"INVALID_ASRANGE", 34, 163}, + #endif + #ifdef X509V3_R_INVALID_BOOLEAN_STRING + {"INVALID_BOOLEAN_STRING", ERR_LIB_X509V3, X509V3_R_INVALID_BOOLEAN_STRING}, + #else + {"INVALID_BOOLEAN_STRING", 34, 104}, + #endif + #ifdef X509V3_R_INVALID_CERTIFICATE + {"INVALID_CERTIFICATE", ERR_LIB_X509V3, X509V3_R_INVALID_CERTIFICATE}, + #else + {"INVALID_CERTIFICATE", 34, 158}, + #endif + #ifdef X509V3_R_INVALID_EMPTY_NAME + {"INVALID_EMPTY_NAME", ERR_LIB_X509V3, X509V3_R_INVALID_EMPTY_NAME}, + #else + {"INVALID_EMPTY_NAME", 34, 108}, + #endif + #ifdef X509V3_R_INVALID_EXTENSION_STRING + {"INVALID_EXTENSION_STRING", ERR_LIB_X509V3, X509V3_R_INVALID_EXTENSION_STRING}, + #else + {"INVALID_EXTENSION_STRING", 34, 105}, + #endif + #ifdef X509V3_R_INVALID_INHERITANCE + {"INVALID_INHERITANCE", ERR_LIB_X509V3, X509V3_R_INVALID_INHERITANCE}, + #else + {"INVALID_INHERITANCE", 34, 165}, + #endif + #ifdef X509V3_R_INVALID_IPADDRESS + {"INVALID_IPADDRESS", ERR_LIB_X509V3, X509V3_R_INVALID_IPADDRESS}, + #else + {"INVALID_IPADDRESS", 34, 166}, + #endif + #ifdef X509V3_R_INVALID_MULTIPLE_RDNS + {"INVALID_MULTIPLE_RDNS", ERR_LIB_X509V3, X509V3_R_INVALID_MULTIPLE_RDNS}, + #else + {"INVALID_MULTIPLE_RDNS", 34, 161}, + #endif + #ifdef X509V3_R_INVALID_NAME + {"INVALID_NAME", ERR_LIB_X509V3, X509V3_R_INVALID_NAME}, + #else + {"INVALID_NAME", 34, 106}, + #endif + #ifdef X509V3_R_INVALID_NULL_ARGUMENT + {"INVALID_NULL_ARGUMENT", ERR_LIB_X509V3, X509V3_R_INVALID_NULL_ARGUMENT}, + #else + {"INVALID_NULL_ARGUMENT", 34, 107}, + #endif + #ifdef X509V3_R_INVALID_NULL_VALUE + {"INVALID_NULL_VALUE", ERR_LIB_X509V3, X509V3_R_INVALID_NULL_VALUE}, + #else + {"INVALID_NULL_VALUE", 34, 109}, + #endif + #ifdef X509V3_R_INVALID_NUMBER + {"INVALID_NUMBER", ERR_LIB_X509V3, X509V3_R_INVALID_NUMBER}, + #else + {"INVALID_NUMBER", 34, 140}, + #endif + #ifdef X509V3_R_INVALID_NUMBERS + {"INVALID_NUMBERS", ERR_LIB_X509V3, X509V3_R_INVALID_NUMBERS}, + #else + {"INVALID_NUMBERS", 34, 141}, + #endif + #ifdef X509V3_R_INVALID_OBJECT_IDENTIFIER + {"INVALID_OBJECT_IDENTIFIER", ERR_LIB_X509V3, X509V3_R_INVALID_OBJECT_IDENTIFIER}, + #else + {"INVALID_OBJECT_IDENTIFIER", 34, 110}, + #endif + #ifdef X509V3_R_INVALID_OPTION + {"INVALID_OPTION", ERR_LIB_X509V3, X509V3_R_INVALID_OPTION}, + #else + {"INVALID_OPTION", 34, 138}, + #endif + #ifdef X509V3_R_INVALID_POLICY_IDENTIFIER + {"INVALID_POLICY_IDENTIFIER", ERR_LIB_X509V3, X509V3_R_INVALID_POLICY_IDENTIFIER}, + #else + {"INVALID_POLICY_IDENTIFIER", 34, 134}, + #endif + #ifdef X509V3_R_INVALID_PROXY_POLICY_SETTING + {"INVALID_PROXY_POLICY_SETTING", ERR_LIB_X509V3, X509V3_R_INVALID_PROXY_POLICY_SETTING}, + #else + {"INVALID_PROXY_POLICY_SETTING", 34, 153}, + #endif + #ifdef X509V3_R_INVALID_PURPOSE + {"INVALID_PURPOSE", ERR_LIB_X509V3, X509V3_R_INVALID_PURPOSE}, + #else + {"INVALID_PURPOSE", 34, 146}, + #endif + #ifdef X509V3_R_INVALID_SAFI + {"INVALID_SAFI", ERR_LIB_X509V3, X509V3_R_INVALID_SAFI}, + #else + {"INVALID_SAFI", 34, 164}, + #endif + #ifdef X509V3_R_INVALID_SECTION + {"INVALID_SECTION", ERR_LIB_X509V3, X509V3_R_INVALID_SECTION}, + #else + {"INVALID_SECTION", 34, 135}, + #endif + #ifdef X509V3_R_INVALID_SYNTAX + {"INVALID_SYNTAX", ERR_LIB_X509V3, X509V3_R_INVALID_SYNTAX}, + #else + {"INVALID_SYNTAX", 34, 143}, + #endif + #ifdef X509V3_R_ISSUER_DECODE_ERROR + {"ISSUER_DECODE_ERROR", ERR_LIB_X509V3, X509V3_R_ISSUER_DECODE_ERROR}, + #else + {"ISSUER_DECODE_ERROR", 34, 126}, + #endif + #ifdef X509V3_R_MISSING_VALUE + {"MISSING_VALUE", ERR_LIB_X509V3, X509V3_R_MISSING_VALUE}, + #else + {"MISSING_VALUE", 34, 124}, + #endif + #ifdef X509V3_R_NEED_ORGANIZATION_AND_NUMBERS + {"NEED_ORGANIZATION_AND_NUMBERS", ERR_LIB_X509V3, X509V3_R_NEED_ORGANIZATION_AND_NUMBERS}, + #else + {"NEED_ORGANIZATION_AND_NUMBERS", 34, 142}, + #endif + #ifdef X509V3_R_NEGATIVE_PATHLEN + {"NEGATIVE_PATHLEN", ERR_LIB_X509V3, X509V3_R_NEGATIVE_PATHLEN}, + #else + {"NEGATIVE_PATHLEN", 34, 168}, + #endif + #ifdef X509V3_R_NO_CONFIG_DATABASE + {"NO_CONFIG_DATABASE", ERR_LIB_X509V3, X509V3_R_NO_CONFIG_DATABASE}, + #else + {"NO_CONFIG_DATABASE", 34, 136}, + #endif + #ifdef X509V3_R_NO_ISSUER_CERTIFICATE + {"NO_ISSUER_CERTIFICATE", ERR_LIB_X509V3, X509V3_R_NO_ISSUER_CERTIFICATE}, + #else + {"NO_ISSUER_CERTIFICATE", 34, 121}, + #endif + #ifdef X509V3_R_NO_ISSUER_DETAILS + {"NO_ISSUER_DETAILS", ERR_LIB_X509V3, X509V3_R_NO_ISSUER_DETAILS}, + #else + {"NO_ISSUER_DETAILS", 34, 127}, + #endif + #ifdef X509V3_R_NO_POLICY_IDENTIFIER + {"NO_POLICY_IDENTIFIER", ERR_LIB_X509V3, X509V3_R_NO_POLICY_IDENTIFIER}, + #else + {"NO_POLICY_IDENTIFIER", 34, 139}, + #endif + #ifdef X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED + {"NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED", ERR_LIB_X509V3, X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED}, + #else + {"NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED", 34, 154}, + #endif + #ifdef X509V3_R_NO_PUBLIC_KEY + {"NO_PUBLIC_KEY", ERR_LIB_X509V3, X509V3_R_NO_PUBLIC_KEY}, + #else + {"NO_PUBLIC_KEY", 34, 114}, + #endif + #ifdef X509V3_R_NO_SUBJECT_DETAILS + {"NO_SUBJECT_DETAILS", ERR_LIB_X509V3, X509V3_R_NO_SUBJECT_DETAILS}, + #else + {"NO_SUBJECT_DETAILS", 34, 125}, + #endif + #ifdef X509V3_R_OPERATION_NOT_DEFINED + {"OPERATION_NOT_DEFINED", ERR_LIB_X509V3, X509V3_R_OPERATION_NOT_DEFINED}, + #else + {"OPERATION_NOT_DEFINED", 34, 148}, + #endif + #ifdef X509V3_R_OTHERNAME_ERROR + {"OTHERNAME_ERROR", ERR_LIB_X509V3, X509V3_R_OTHERNAME_ERROR}, + #else + {"OTHERNAME_ERROR", 34, 147}, + #endif + #ifdef X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED + {"POLICY_LANGUAGE_ALREADY_DEFINED", ERR_LIB_X509V3, X509V3_R_POLICY_LANGUAGE_ALREADY_DEFINED}, + #else + {"POLICY_LANGUAGE_ALREADY_DEFINED", 34, 155}, + #endif + #ifdef X509V3_R_POLICY_PATH_LENGTH + {"POLICY_PATH_LENGTH", ERR_LIB_X509V3, X509V3_R_POLICY_PATH_LENGTH}, + #else + {"POLICY_PATH_LENGTH", 34, 156}, + #endif + #ifdef X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED + {"POLICY_PATH_LENGTH_ALREADY_DEFINED", ERR_LIB_X509V3, X509V3_R_POLICY_PATH_LENGTH_ALREADY_DEFINED}, + #else + {"POLICY_PATH_LENGTH_ALREADY_DEFINED", 34, 157}, + #endif + #ifdef X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY + {"POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY", ERR_LIB_X509V3, X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY}, + #else + {"POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY", 34, 159}, + #endif + #ifdef X509V3_R_PURPOSE_NOT_UNIQUE + {"PURPOSE_NOT_UNIQUE", ERR_LIB_X509V3, X509V3_R_PURPOSE_NOT_UNIQUE}, + #else + {"PURPOSE_NOT_UNIQUE", 34, 173}, + #endif + #ifdef X509V3_R_SECTION_NOT_FOUND + {"SECTION_NOT_FOUND", ERR_LIB_X509V3, X509V3_R_SECTION_NOT_FOUND}, + #else + {"SECTION_NOT_FOUND", 34, 150}, + #endif + #ifdef X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS + {"UNABLE_TO_GET_ISSUER_DETAILS", ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS}, + #else + {"UNABLE_TO_GET_ISSUER_DETAILS", 34, 122}, + #endif + #ifdef X509V3_R_UNABLE_TO_GET_ISSUER_KEYID + {"UNABLE_TO_GET_ISSUER_KEYID", ERR_LIB_X509V3, X509V3_R_UNABLE_TO_GET_ISSUER_KEYID}, + #else + {"UNABLE_TO_GET_ISSUER_KEYID", 34, 123}, + #endif + #ifdef X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT + {"UNKNOWN_BIT_STRING_ARGUMENT", ERR_LIB_X509V3, X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT}, + #else + {"UNKNOWN_BIT_STRING_ARGUMENT", 34, 111}, + #endif + #ifdef X509V3_R_UNKNOWN_EXTENSION + {"UNKNOWN_EXTENSION", ERR_LIB_X509V3, X509V3_R_UNKNOWN_EXTENSION}, + #else + {"UNKNOWN_EXTENSION", 34, 129}, + #endif + #ifdef X509V3_R_UNKNOWN_EXTENSION_NAME + {"UNKNOWN_EXTENSION_NAME", ERR_LIB_X509V3, X509V3_R_UNKNOWN_EXTENSION_NAME}, + #else + {"UNKNOWN_EXTENSION_NAME", 34, 130}, + #endif + #ifdef X509V3_R_UNKNOWN_OPTION + {"UNKNOWN_OPTION", ERR_LIB_X509V3, X509V3_R_UNKNOWN_OPTION}, + #else + {"UNKNOWN_OPTION", 34, 120}, + #endif + #ifdef X509V3_R_UNKNOWN_VALUE + {"UNKNOWN_VALUE", ERR_LIB_X509V3, X509V3_R_UNKNOWN_VALUE}, + #else + {"UNKNOWN_VALUE", 34, 172}, + #endif + #ifdef X509V3_R_UNSUPPORTED_OPTION + {"UNSUPPORTED_OPTION", ERR_LIB_X509V3, X509V3_R_UNSUPPORTED_OPTION}, + #else + {"UNSUPPORTED_OPTION", 34, 117}, + #endif + #ifdef X509V3_R_UNSUPPORTED_TYPE + {"UNSUPPORTED_TYPE", ERR_LIB_X509V3, X509V3_R_UNSUPPORTED_TYPE}, + #else + {"UNSUPPORTED_TYPE", 34, 167}, + #endif + #ifdef X509V3_R_USER_TOO_LONG + {"USER_TOO_LONG", ERR_LIB_X509V3, X509V3_R_USER_TOO_LONG}, + #else + {"USER_TOO_LONG", 34, 132}, + #endif + #ifdef X509_R_AKID_MISMATCH + {"AKID_MISMATCH", ERR_LIB_X509, X509_R_AKID_MISMATCH}, + #else + {"AKID_MISMATCH", 11, 110}, + #endif + #ifdef X509_R_BAD_SELECTOR + {"BAD_SELECTOR", ERR_LIB_X509, X509_R_BAD_SELECTOR}, + #else + {"BAD_SELECTOR", 11, 133}, + #endif + #ifdef X509_R_BAD_X509_FILETYPE + {"BAD_X509_FILETYPE", ERR_LIB_X509, X509_R_BAD_X509_FILETYPE}, + #else + {"BAD_X509_FILETYPE", 11, 100}, + #endif + #ifdef X509_R_BASE64_DECODE_ERROR + {"BASE64_DECODE_ERROR", ERR_LIB_X509, X509_R_BASE64_DECODE_ERROR}, + #else + {"BASE64_DECODE_ERROR", 11, 118}, + #endif + #ifdef X509_R_CANT_CHECK_DH_KEY + {"CANT_CHECK_DH_KEY", ERR_LIB_X509, X509_R_CANT_CHECK_DH_KEY}, + #else + {"CANT_CHECK_DH_KEY", 11, 114}, + #endif + #ifdef X509_R_CERTIFICATE_VERIFICATION_FAILED + {"CERTIFICATE_VERIFICATION_FAILED", ERR_LIB_X509, X509_R_CERTIFICATE_VERIFICATION_FAILED}, + #else + {"CERTIFICATE_VERIFICATION_FAILED", 11, 139}, + #endif + #ifdef X509_R_CERT_ALREADY_IN_HASH_TABLE + {"CERT_ALREADY_IN_HASH_TABLE", ERR_LIB_X509, X509_R_CERT_ALREADY_IN_HASH_TABLE}, + #else + {"CERT_ALREADY_IN_HASH_TABLE", 11, 101}, + #endif + #ifdef X509_R_CRL_ALREADY_DELTA + {"CRL_ALREADY_DELTA", ERR_LIB_X509, X509_R_CRL_ALREADY_DELTA}, + #else + {"CRL_ALREADY_DELTA", 11, 127}, + #endif + #ifdef X509_R_CRL_VERIFY_FAILURE + {"CRL_VERIFY_FAILURE", ERR_LIB_X509, X509_R_CRL_VERIFY_FAILURE}, + #else + {"CRL_VERIFY_FAILURE", 11, 131}, + #endif + #ifdef X509_R_DUPLICATE_ATTRIBUTE + {"DUPLICATE_ATTRIBUTE", ERR_LIB_X509, X509_R_DUPLICATE_ATTRIBUTE}, + #else + {"DUPLICATE_ATTRIBUTE", 11, 140}, + #endif + #ifdef X509_R_ERROR_GETTING_MD_BY_NID + {"ERROR_GETTING_MD_BY_NID", ERR_LIB_X509, X509_R_ERROR_GETTING_MD_BY_NID}, + #else + {"ERROR_GETTING_MD_BY_NID", 11, 141}, + #endif + #ifdef X509_R_ERROR_USING_SIGINF_SET + {"ERROR_USING_SIGINF_SET", ERR_LIB_X509, X509_R_ERROR_USING_SIGINF_SET}, + #else + {"ERROR_USING_SIGINF_SET", 11, 142}, + #endif + #ifdef X509_R_IDP_MISMATCH + {"IDP_MISMATCH", ERR_LIB_X509, X509_R_IDP_MISMATCH}, + #else + {"IDP_MISMATCH", 11, 128}, + #endif + #ifdef X509_R_INVALID_ATTRIBUTES + {"INVALID_ATTRIBUTES", ERR_LIB_X509, X509_R_INVALID_ATTRIBUTES}, + #else + {"INVALID_ATTRIBUTES", 11, 138}, + #endif + #ifdef X509_R_INVALID_DIRECTORY + {"INVALID_DIRECTORY", ERR_LIB_X509, X509_R_INVALID_DIRECTORY}, + #else + {"INVALID_DIRECTORY", 11, 113}, + #endif + #ifdef X509_R_INVALID_DISTPOINT + {"INVALID_DISTPOINT", ERR_LIB_X509, X509_R_INVALID_DISTPOINT}, + #else + {"INVALID_DISTPOINT", 11, 143}, + #endif + #ifdef X509_R_INVALID_EXTENSION + {"INVALID_EXTENSION", ERR_LIB_X509, X509_R_INVALID_EXTENSION}, + #else + {"INVALID_EXTENSION", 11, 146}, + #endif + #ifdef X509_R_INVALID_FIELD_NAME + {"INVALID_FIELD_NAME", ERR_LIB_X509, X509_R_INVALID_FIELD_NAME}, + #else + {"INVALID_FIELD_NAME", 11, 119}, + #endif + #ifdef X509_R_INVALID_TRUST + {"INVALID_TRUST", ERR_LIB_X509, X509_R_INVALID_TRUST}, + #else + {"INVALID_TRUST", 11, 123}, + #endif + #ifdef X509_R_ISSUER_MISMATCH + {"ISSUER_MISMATCH", ERR_LIB_X509, X509_R_ISSUER_MISMATCH}, + #else + {"ISSUER_MISMATCH", 11, 129}, + #endif + #ifdef X509_R_KEY_TYPE_MISMATCH + {"KEY_TYPE_MISMATCH", ERR_LIB_X509, X509_R_KEY_TYPE_MISMATCH}, + #else + {"KEY_TYPE_MISMATCH", 11, 115}, + #endif + #ifdef X509_R_KEY_VALUES_MISMATCH + {"KEY_VALUES_MISMATCH", ERR_LIB_X509, X509_R_KEY_VALUES_MISMATCH}, + #else + {"KEY_VALUES_MISMATCH", 11, 116}, + #endif + #ifdef X509_R_LOADING_CERT_DIR + {"LOADING_CERT_DIR", ERR_LIB_X509, X509_R_LOADING_CERT_DIR}, + #else + {"LOADING_CERT_DIR", 11, 103}, + #endif + #ifdef X509_R_LOADING_DEFAULTS + {"LOADING_DEFAULTS", ERR_LIB_X509, X509_R_LOADING_DEFAULTS}, + #else + {"LOADING_DEFAULTS", 11, 104}, + #endif + #ifdef X509_R_METHOD_NOT_SUPPORTED + {"METHOD_NOT_SUPPORTED", ERR_LIB_X509, X509_R_METHOD_NOT_SUPPORTED}, + #else + {"METHOD_NOT_SUPPORTED", 11, 124}, + #endif + #ifdef X509_R_NAME_TOO_LONG + {"NAME_TOO_LONG", ERR_LIB_X509, X509_R_NAME_TOO_LONG}, + #else + {"NAME_TOO_LONG", 11, 134}, + #endif + #ifdef X509_R_NEWER_CRL_NOT_NEWER + {"NEWER_CRL_NOT_NEWER", ERR_LIB_X509, X509_R_NEWER_CRL_NOT_NEWER}, + #else + {"NEWER_CRL_NOT_NEWER", 11, 132}, + #endif + #ifdef X509_R_NO_CERTIFICATE_FOUND + {"NO_CERTIFICATE_FOUND", ERR_LIB_X509, X509_R_NO_CERTIFICATE_FOUND}, + #else + {"NO_CERTIFICATE_FOUND", 11, 135}, + #endif + #ifdef X509_R_NO_CERTIFICATE_OR_CRL_FOUND + {"NO_CERTIFICATE_OR_CRL_FOUND", ERR_LIB_X509, X509_R_NO_CERTIFICATE_OR_CRL_FOUND}, + #else + {"NO_CERTIFICATE_OR_CRL_FOUND", 11, 136}, + #endif + #ifdef X509_R_NO_CERT_SET_FOR_US_TO_VERIFY + {"NO_CERT_SET_FOR_US_TO_VERIFY", ERR_LIB_X509, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY}, + #else + {"NO_CERT_SET_FOR_US_TO_VERIFY", 11, 105}, + #endif + #ifdef X509_R_NO_CRL_FOUND + {"NO_CRL_FOUND", ERR_LIB_X509, X509_R_NO_CRL_FOUND}, + #else + {"NO_CRL_FOUND", 11, 137}, + #endif + #ifdef X509_R_NO_CRL_NUMBER + {"NO_CRL_NUMBER", ERR_LIB_X509, X509_R_NO_CRL_NUMBER}, + #else + {"NO_CRL_NUMBER", 11, 130}, + #endif + #ifdef X509_R_PUBLIC_KEY_DECODE_ERROR + {"PUBLIC_KEY_DECODE_ERROR", ERR_LIB_X509, X509_R_PUBLIC_KEY_DECODE_ERROR}, + #else + {"PUBLIC_KEY_DECODE_ERROR", 11, 125}, + #endif + #ifdef X509_R_PUBLIC_KEY_ENCODE_ERROR + {"PUBLIC_KEY_ENCODE_ERROR", ERR_LIB_X509, X509_R_PUBLIC_KEY_ENCODE_ERROR}, + #else + {"PUBLIC_KEY_ENCODE_ERROR", 11, 126}, + #endif + #ifdef X509_R_SHOULD_RETRY + {"SHOULD_RETRY", ERR_LIB_X509, X509_R_SHOULD_RETRY}, + #else + {"SHOULD_RETRY", 11, 106}, + #endif + #ifdef X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN + {"UNABLE_TO_FIND_PARAMETERS_IN_CHAIN", ERR_LIB_X509, X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN}, + #else + {"UNABLE_TO_FIND_PARAMETERS_IN_CHAIN", 11, 107}, + #endif + #ifdef X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY + {"UNABLE_TO_GET_CERTS_PUBLIC_KEY", ERR_LIB_X509, X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY}, + #else + {"UNABLE_TO_GET_CERTS_PUBLIC_KEY", 11, 108}, + #endif + #ifdef X509_R_UNKNOWN_KEY_TYPE + {"UNKNOWN_KEY_TYPE", ERR_LIB_X509, X509_R_UNKNOWN_KEY_TYPE}, + #else + {"UNKNOWN_KEY_TYPE", 11, 117}, + #endif + #ifdef X509_R_UNKNOWN_NID + {"UNKNOWN_NID", ERR_LIB_X509, X509_R_UNKNOWN_NID}, + #else + {"UNKNOWN_NID", 11, 109}, + #endif + #ifdef X509_R_UNKNOWN_PURPOSE_ID + {"UNKNOWN_PURPOSE_ID", ERR_LIB_X509, X509_R_UNKNOWN_PURPOSE_ID}, + #else + {"UNKNOWN_PURPOSE_ID", 11, 121}, + #endif + #ifdef X509_R_UNKNOWN_SIGID_ALGS + {"UNKNOWN_SIGID_ALGS", ERR_LIB_X509, X509_R_UNKNOWN_SIGID_ALGS}, + #else + {"UNKNOWN_SIGID_ALGS", 11, 144}, + #endif + #ifdef X509_R_UNKNOWN_TRUST_ID + {"UNKNOWN_TRUST_ID", ERR_LIB_X509, X509_R_UNKNOWN_TRUST_ID}, + #else + {"UNKNOWN_TRUST_ID", 11, 120}, + #endif + #ifdef X509_R_UNSUPPORTED_ALGORITHM + {"UNSUPPORTED_ALGORITHM", ERR_LIB_X509, X509_R_UNSUPPORTED_ALGORITHM}, + #else + {"UNSUPPORTED_ALGORITHM", 11, 111}, + #endif + #ifdef X509_R_UNSUPPORTED_VERSION + {"UNSUPPORTED_VERSION", ERR_LIB_X509, X509_R_UNSUPPORTED_VERSION}, + #else + {"UNSUPPORTED_VERSION", 11, 145}, + #endif + #ifdef X509_R_WRONG_LOOKUP_TYPE + {"WRONG_LOOKUP_TYPE", ERR_LIB_X509, X509_R_WRONG_LOOKUP_TYPE}, + #else + {"WRONG_LOOKUP_TYPE", 11, 112}, + #endif + #ifdef X509_R_WRONG_TYPE + {"WRONG_TYPE", ERR_LIB_X509, X509_R_WRONG_TYPE}, + #else + {"WRONG_TYPE", 11, 122}, + #endif + {NULL, 0, 0} /* sentinel */ +}; diff --git a/Modules/_struct.c b/Modules/_struct.c index 3fad35a8c94ee2..8c611a708d02a9 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -1,7 +1,7 @@ /* struct module -- pack values into and (out of) bytes objects */ /* New version supporting byte order, alignment and size options, - character strings, and unsigned numbers */ + byte strings, and unsigned numbers */ #ifndef Py_BUILD_CORE_BUILTIN # define Py_BUILD_CORE_MODULE 1 @@ -9,6 +9,7 @@ #include "Python.h" #include "pycore_bytesobject.h" // _PyBytesWriter +#include "pycore_lock.h" // _PyOnceFlag_CallOnce() #include "pycore_long.h" // _PyLong_AsByteArray() #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() @@ -44,7 +45,7 @@ static struct PyModuleDef _structmodule; /* The translation function for each format character is table driven */ typedef struct _formatdef { - char format; + const char *format; Py_ssize_t size; Py_ssize_t alignment; PyObject* (*unpack)(_structmodulestate *, const char *, @@ -69,6 +70,7 @@ typedef struct { formatcode *s_codes; PyObject *s_format; PyObject *weakreflist; /* List of weak references */ + bool init_called; } PyStructObject; #define PyStructObject_CAST(op) ((PyStructObject *)(op)) @@ -325,13 +327,13 @@ _range_error(_structmodulestate *state, const formatdef *f, int is_unsigned) assert(f->size >= 1 && f->size <= SIZEOF_SIZE_T); if (is_unsigned) PyErr_Format(state->StructError, - "'%c' format requires 0 <= number <= %zu", + "'%s' format requires 0 <= number <= %zu", f->format, ulargest); else { const Py_ssize_t largest = (Py_ssize_t)(ulargest >> 1); PyErr_Format(state->StructError, - "'%c' format requires %zd <= number <= %zd", + "'%s' format requires %zd <= number <= %zd", f->format, ~ largest, largest); @@ -706,7 +708,7 @@ np_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) if (get_longlong(state, v, &x) < 0) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { PyErr_Format(state->StructError, - "'%c' format requires %lld <= number <= %lld", + "'%s' format requires %lld <= number <= %lld", f->format, LLONG_MIN, LLONG_MAX); @@ -724,7 +726,7 @@ np_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f if (get_ulonglong(state, v, &x) < 0) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) { PyErr_Format(state->StructError, - "'%c' format requires 0 <= number <= %llu", + "'%s' format requires 0 <= number <= %llu", f->format, ULLONG_MAX); } @@ -761,14 +763,13 @@ np_halffloat(_structmodulestate *state, char *p, PyObject *v, const formatdef *f static int np_float(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) { - float x = (float)PyFloat_AsDouble(v); + double x = PyFloat_AsDouble(v); if (x == -1 && PyErr_Occurred()) { PyErr_SetString(state->StructError, "required argument is not a float"); return -1; } - memcpy(p, &x, sizeof x); - return 0; + return PyFloat_Pack4(x, p, PY_LITTLE_ENDIAN); } static int @@ -834,29 +835,31 @@ np_void_p(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) } static const formatdef native_table[] = { - {'x', sizeof(char), 0, NULL}, - {'b', sizeof(char), 0, nu_byte, np_byte}, - {'B', sizeof(char), 0, nu_ubyte, np_ubyte}, - {'c', sizeof(char), 0, nu_char, np_char}, - {'s', sizeof(char), 0, NULL}, - {'p', sizeof(char), 0, NULL}, - {'h', sizeof(short), _Alignof(short), nu_short, np_short}, - {'H', sizeof(short), _Alignof(short), nu_ushort, np_ushort}, - {'i', sizeof(int), _Alignof(int), nu_int, np_int}, - {'I', sizeof(int), _Alignof(int), nu_uint, np_uint}, - {'l', sizeof(long), _Alignof(long), nu_long, np_long}, - {'L', sizeof(long), _Alignof(long), nu_ulong, np_ulong}, - {'n', sizeof(size_t), _Alignof(size_t), nu_ssize_t, np_ssize_t}, - {'N', sizeof(size_t), _Alignof(size_t), nu_size_t, np_size_t}, - {'q', sizeof(long long), _Alignof(long long), nu_longlong, np_longlong}, - {'Q', sizeof(long long), _Alignof(long long), nu_ulonglong,np_ulonglong}, - {'?', sizeof(_Bool), _Alignof(_Bool), nu_bool, np_bool}, - {'e', sizeof(short), _Alignof(short), nu_halffloat, np_halffloat}, - {'f', sizeof(float), _Alignof(float), nu_float, np_float}, - {'d', sizeof(double), _Alignof(double), nu_double, np_double}, - {'F', 2*sizeof(float), _Alignof(float), nu_float_complex, np_float_complex}, - {'D', 2*sizeof(double), _Alignof(double), nu_double_complex, np_double_complex}, - {'P', sizeof(void *), _Alignof(void *), nu_void_p, np_void_p}, + {"x", sizeof(char), 0, NULL}, + {"b", sizeof(char), 0, nu_byte, np_byte}, + {"B", sizeof(char), 0, nu_ubyte, np_ubyte}, + {"c", sizeof(char), 0, nu_char, np_char}, + {"s", sizeof(char), 0, NULL}, + {"p", sizeof(char), 0, NULL}, + {"h", sizeof(short), _Alignof(short), nu_short, np_short}, + {"H", sizeof(short), _Alignof(short), nu_ushort, np_ushort}, + {"i", sizeof(int), _Alignof(int), nu_int, np_int}, + {"I", sizeof(int), _Alignof(int), nu_uint, np_uint}, + {"l", sizeof(long), _Alignof(long), nu_long, np_long}, + {"L", sizeof(long), _Alignof(long), nu_ulong, np_ulong}, + {"n", sizeof(size_t), _Alignof(size_t), nu_ssize_t, np_ssize_t}, + {"N", sizeof(size_t), _Alignof(size_t), nu_size_t, np_size_t}, + {"q", sizeof(long long), _Alignof(long long), nu_longlong, np_longlong}, + {"Q", sizeof(long long), _Alignof(long long), nu_ulonglong,np_ulonglong}, + {"?", sizeof(_Bool), _Alignof(_Bool), nu_bool, np_bool}, + {"e", sizeof(short), _Alignof(short), nu_halffloat, np_halffloat}, + {"f", sizeof(float), _Alignof(float), nu_float, np_float}, + {"d", sizeof(double), _Alignof(double), nu_double, np_double}, + {"F", 2*sizeof(float), _Alignof(float), nu_float_complex, np_float_complex}, + {"D", 2*sizeof(double), _Alignof(double), nu_double_complex, np_double_complex}, + {"Zf", 2*sizeof(float), _Alignof(float), nu_float_complex, np_float_complex}, + {"Zd", 2*sizeof(double), _Alignof(double), nu_double_complex, np_double_complex}, + {"P", sizeof(void *), _Alignof(void *), nu_void_p, np_void_p}, {0} }; @@ -1062,7 +1065,7 @@ bp_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) Py_DECREF(v); if (res < 0) { PyErr_Format(state->StructError, - "'%c' format requires %lld <= number <= %lld", + "'%s' format requires %lld <= number <= %lld", f->format, LLONG_MIN, LLONG_MAX); @@ -1087,7 +1090,7 @@ bp_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f Py_DECREF(v); if (res < 0) { PyErr_Format(state->StructError, - "'%c' format requires 0 <= number <= %llu", + "'%s' format requires 0 <= number <= %llu", f->format, ULLONG_MAX); return -1; @@ -1167,26 +1170,28 @@ bp_bool(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) } static formatdef bigendian_table[] = { - {'x', 1, 0, NULL}, - {'b', 1, 0, nu_byte, np_byte}, - {'B', 1, 0, nu_ubyte, np_ubyte}, - {'c', 1, 0, nu_char, np_char}, - {'s', 1, 0, NULL}, - {'p', 1, 0, NULL}, - {'h', 2, 0, bu_short, bp_int}, - {'H', 2, 0, bu_uint, bp_uint}, - {'i', 4, 0, bu_int, bp_int}, - {'I', 4, 0, bu_uint, bp_uint}, - {'l', 4, 0, bu_int, bp_int}, - {'L', 4, 0, bu_uint, bp_uint}, - {'q', 8, 0, bu_longlong, bp_longlong}, - {'Q', 8, 0, bu_ulonglong, bp_ulonglong}, - {'?', 1, 0, bu_bool, bp_bool}, - {'e', 2, 0, bu_halffloat, bp_halffloat}, - {'f', 4, 0, bu_float, bp_float}, - {'d', 8, 0, bu_double, bp_double}, - {'F', 8, 0, bu_float_complex, bp_float_complex}, - {'D', 16, 0, bu_double_complex, bp_double_complex}, + {"x", 1, 0, NULL}, + {"b", 1, 0, nu_byte, np_byte}, + {"B", 1, 0, nu_ubyte, np_ubyte}, + {"c", 1, 0, nu_char, np_char}, + {"s", 1, 0, NULL}, + {"p", 1, 0, NULL}, + {"h", 2, 0, bu_short, bp_int}, + {"H", 2, 0, bu_uint, bp_uint}, + {"i", 4, 0, bu_int, bp_int}, + {"I", 4, 0, bu_uint, bp_uint}, + {"l", 4, 0, bu_int, bp_int}, + {"L", 4, 0, bu_uint, bp_uint}, + {"q", 8, 0, bu_longlong, bp_longlong}, + {"Q", 8, 0, bu_ulonglong, bp_ulonglong}, + {"?", 1, 0, bu_bool, bp_bool}, + {"e", 2, 0, bu_halffloat, bp_halffloat}, + {"f", 4, 0, bu_float, bp_float}, + {"d", 8, 0, bu_double, bp_double}, + {"F", 8, 0, bu_float_complex, bp_float_complex}, + {"D", 16, 0, bu_double_complex, bp_double_complex}, + {"Zf", 8, 0, bu_float_complex, bp_float_complex}, + {"Zd", 16, 0, bu_double_complex, bp_double_complex}, {0} }; @@ -1386,7 +1391,7 @@ lp_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) Py_DECREF(v); if (res < 0) { PyErr_Format(state->StructError, - "'%c' format requires %lld <= number <= %lld", + "'%s' format requires %lld <= number <= %lld", f->format, LLONG_MIN, LLONG_MAX); @@ -1411,7 +1416,7 @@ lp_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f Py_DECREF(v); if (res < 0) { PyErr_Format(state->StructError, - "'%c' format requires 0 <= number <= %llu", + "'%s' format requires 0 <= number <= %llu", f->format, ULLONG_MAX); return -1; @@ -1481,30 +1486,76 @@ lp_double_complex(_structmodulestate *state, char *p, PyObject *v, const formatd } static formatdef lilendian_table[] = { - {'x', 1, 0, NULL}, - {'b', 1, 0, nu_byte, np_byte}, - {'B', 1, 0, nu_ubyte, np_ubyte}, - {'c', 1, 0, nu_char, np_char}, - {'s', 1, 0, NULL}, - {'p', 1, 0, NULL}, - {'h', 2, 0, lu_short, lp_int}, - {'H', 2, 0, lu_uint, lp_uint}, - {'i', 4, 0, lu_int, lp_int}, - {'I', 4, 0, lu_uint, lp_uint}, - {'l', 4, 0, lu_int, lp_int}, - {'L', 4, 0, lu_uint, lp_uint}, - {'q', 8, 0, lu_longlong, lp_longlong}, - {'Q', 8, 0, lu_ulonglong, lp_ulonglong}, - {'?', 1, 0, bu_bool, bp_bool}, /* Std rep not endian dep, + {"x", 1, 0, NULL}, + {"b", 1, 0, nu_byte, np_byte}, + {"B", 1, 0, nu_ubyte, np_ubyte}, + {"c", 1, 0, nu_char, np_char}, + {"s", 1, 0, NULL}, + {"p", 1, 0, NULL}, + {"h", 2, 0, lu_short, lp_int}, + {"H", 2, 0, lu_uint, lp_uint}, + {"i", 4, 0, lu_int, lp_int}, + {"I", 4, 0, lu_uint, lp_uint}, + {"l", 4, 0, lu_int, lp_int}, + {"L", 4, 0, lu_uint, lp_uint}, + {"q", 8, 0, lu_longlong, lp_longlong}, + {"Q", 8, 0, lu_ulonglong, lp_ulonglong}, + {"?", 1, 0, bu_bool, bp_bool}, /* Std rep not endian dep, but potentially different from native rep -- reuse bx_bool funcs. */ - {'e', 2, 0, lu_halffloat, lp_halffloat}, - {'f', 4, 0, lu_float, lp_float}, - {'d', 8, 0, lu_double, lp_double}, - {'F', 8, 0, lu_float_complex, lp_float_complex}, - {'D', 16, 0, lu_double_complex, lp_double_complex}, + {"e", 2, 0, lu_halffloat, lp_halffloat}, + {"f", 4, 0, lu_float, lp_float}, + {"d", 8, 0, lu_double, lp_double}, + {"F", 8, 0, lu_float_complex, lp_float_complex}, + {"D", 16, 0, lu_double_complex, lp_double_complex}, + {"Zf", 8, 0, lu_float_complex, lp_float_complex}, + {"Zd", 16, 0, lu_double_complex, lp_double_complex}, {0} }; +/* Ensure endian table optimization happens exactly once across all interpreters */ +static _PyOnceFlag endian_tables_init_once = {0}; + +static int +init_endian_tables(void *Py_UNUSED(arg)) +{ + const formatdef *native = native_table; + formatdef *other, *ptr; +#if PY_LITTLE_ENDIAN + other = lilendian_table; +#else + other = bigendian_table; +#endif + /* Scan through the native table, find a matching + entry in the endian table and swap in the + native implementations whenever possible + (64-bit platforms may not have "standard" sizes) */ + while (native->format != NULL && other->format != NULL) { + ptr = other; + while (ptr->format != NULL) { + if (strcmp(ptr->format, native->format) == 0) { + /* Match faster when formats are + listed in the same order */ + if (ptr == other) + other++; + /* Only use the trick if the + size matches */ + if (ptr->size != native->size) + break; + /* Skip _Bool, semantics are different for standard size */ + if (strcmp(ptr->format, "?") == 0) { + break; + } + ptr->pack = native->pack; + ptr->unpack = native->unpack; + break; + } + ptr++; + } + native++; + } + return 0; +} + static const formatdef * whichtable(const char **pfmt) @@ -1532,13 +1583,28 @@ whichtable(const char **pfmt) } +static int +format_equal(const formatdef *e, const char *s) +{ + const char *format = e->format; + size_t i = 0; + while (format[i] == s[i]) { + i++; + if (format[i] == 0) { + return 1; + } + } + return 0; +} + + /* Get the table entry for a format code */ static const formatdef * -getentry(_structmodulestate *state, int c, const formatdef *f) +getentry(_structmodulestate *state, const char *s, const formatdef *f) { - for (; f->format != '\0'; f++) { - if (f->format == c) { + for (; f->format != NULL; f++) { + if (format_equal(f, s)) { return f; } } @@ -1550,11 +1616,11 @@ getentry(_structmodulestate *state, int c, const formatdef *f) /* Align a size according to a format code. Return -1 on overflow. */ static Py_ssize_t -align(Py_ssize_t size, char c, const formatdef *e) +align(Py_ssize_t size, const char *s, const formatdef *e) { Py_ssize_t extra; - if (e->format == c) { + if (format_equal(e, s)) { if (e->alignment && size > 0) { extra = (e->alignment - 1) - (size - 1) % (e->alignment); if (extra > PY_SSIZE_T_MAX - size) @@ -1572,11 +1638,11 @@ align(Py_ssize_t size, char c, const formatdef *e) /* calculate the size of a format string */ static int -prepare_s(PyStructObject *self) +prepare_s(PyStructObject *self, PyObject *format) { const formatdef *f; const formatdef *e; - formatcode *codes; + formatcode *codes, *codes0; const char *s; const char *fmt; @@ -1586,8 +1652,12 @@ prepare_s(PyStructObject *self) _structmodulestate *state = get_struct_state_structinst(self); - fmt = PyBytes_AS_STRING(self->s_format); - if (strlen(fmt) != (size_t)PyBytes_GET_SIZE(self->s_format)) { + if (!PyUnicode_IS_ASCII(format)) { + PyErr_SetString(PyExc_ValueError, "non-ASCII character in struct format"); + return -1; + } + fmt = (const char *)PyUnicode_1BYTE_DATA(format); + if (strlen(fmt) != (size_t)PyUnicode_GET_LENGTH(format)) { PyErr_SetString(state->StructError, "embedded null character"); return -1; @@ -1622,21 +1692,37 @@ prepare_s(PyStructObject *self) else num = 1; - e = getentry(state, c, f); + s--; + e = getentry(state, s, f); if (e == NULL) return -1; switch (c) { case 's': _Py_FALLTHROUGH; - case 'p': len++; ncodes++; break; + case 'p': + if (len == PY_SSIZE_T_MAX) { + goto overflow; + } + len++; + ncodes++; + break; case 'x': break; - default: len += num; if (num) ncodes++; break; + default: + if (num > PY_SSIZE_T_MAX - len) { + goto overflow; + } + len += num; + if (num) { + ncodes++; + } + break; } itemsize = e->size; - size = align(size, c, e); + size = align(size, s, e); if (size == -1) goto overflow; + s += strlen(e->format); /* if (size + num * itemsize > PY_SSIZE_T_MAX) { ... } */ if (num > (PY_SSIZE_T_MAX - size) / itemsize) @@ -1650,18 +1736,12 @@ prepare_s(PyStructObject *self) return -1; } - self->s_size = size; - self->s_len = len; codes = PyMem_Malloc((ncodes + 1) * sizeof(formatcode)); if (codes == NULL) { PyErr_NoMemory(); return -1; } - /* Free any s_codes value left over from a previous initialization. */ - if (self->s_codes != NULL) - PyMem_Free(self->s_codes); - self->s_codes = codes; - + codes0 = codes; s = fmt; size = 0; while ((c = *s++) != '\0') { @@ -1675,9 +1755,11 @@ prepare_s(PyStructObject *self) else num = 1; - e = getentry(state, c, f); + s--; + e = getentry(state, s, f); + size = align(size, s, e); + s += strlen(e->format); - size = align(size, c, e); if (c == 's' || c == 'p') { codes->offset = size; codes->size = num; @@ -1701,6 +1783,14 @@ prepare_s(PyStructObject *self) codes->size = 0; codes->repeat = 0; + /* Free any s_codes value left over from a previous initialization. */ + if (self->s_codes != NULL) + PyMem_Free(self->s_codes); + self->s_codes = codes0; + self->s_size = size; + self->s_len = len; + Py_XSETREF(self->s_format, Py_NewRef(format)); + return 0; overflow: @@ -1709,24 +1799,155 @@ prepare_s(PyStructObject *self) return -1; } +/* This should be moved to Struct_impl() when Struct___init__() and + * s_new() will be removed (see gh-143715 and gh-94532). */ +static int +set_format(PyStructObject *self, PyObject *format) +{ + if (PyUnicode_Check(format)) { + format = PyUnicode_FromObject(format); + } + else if (PyBytes_Check(format)) { + format = PyUnicode_DecodeASCII(PyBytes_AS_STRING(format), + PyBytes_GET_SIZE(format), "surrogateescape"); + } + else { + PyErr_Format(PyExc_TypeError, + "Struct() argument 1 must be a str or bytes object, " + "not %T", format); + return -1; + } + if (format == NULL) { + return -1; + } + if (prepare_s(self, format)) { + Py_DECREF(format); + return -1; + } + Py_DECREF(format); + return 0; +} + +/*[clinic input] +@classmethod +Struct.__new__ + + format: object + +Create a compiled struct object. + +Return a new Struct object which writes and reads binary data according +to the format string. See help(struct) for more on format strings. +[clinic start generated code]*/ + +static PyObject * +Struct_impl(PyTypeObject *type, PyObject *format) +/*[clinic end generated code: output=49468b044e334308 input=8381a9796f20f24e]*/ +{ + PyStructObject *self = (PyStructObject *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } + self->s_format = NULL; + self->s_codes = NULL; + self->s_size = -1; + self->s_len = -1; + self->init_called = false; + if (set_format(self, format) < 0) { + Py_DECREF(self); + return NULL; + } + return (PyObject *)self; +} + + +static int +s_init(PyObject *self, PyObject *args, PyObject *kwargs); + static PyObject * s_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - PyObject *self; + if (type->tp_new != s_new) { + /* Struct.__new__() was called explicitly in a subclass' __new__(). */ + return Struct(type, args, kwds); + } + + PyObject *format = NULL; + if (PyTuple_GET_SIZE(args) == 1 && (kwds == NULL || PyDict_GET_SIZE(kwds) == 0)) { + format = Py_NewRef(PyTuple_GET_ITEM(args, 0)); + } + else if (PyTuple_GET_SIZE(args) == 0 && kwds != NULL && PyDict_GET_SIZE(kwds) == 1) { + if (PyDict_GetItemStringRef(kwds, "format", &format) < 0) { + return NULL; + } + } + if (format == NULL && type->tp_init != s_init) { + Py_ssize_t nargs = PyTuple_GET_SIZE(args) + (kwds ? PyDict_GET_SIZE(kwds) : 0); + if (nargs > 1) { + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "Struct() takes at most 1 argument (%zd given)", nargs)) + { + Py_XDECREF(format); + return NULL; + } + } + else { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Struct() missing required argument 'format' (pos 1)", 1)) + { + Py_XDECREF(format); + return NULL; + } + } + } - assert(type != NULL); - allocfunc alloc_func = PyType_GetSlot(type, Py_tp_alloc); - assert(alloc_func != NULL); + PyStructObject *self = (PyStructObject *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } + self->s_format = NULL; + self->s_codes = NULL; + self->s_size = -1; + self->s_len = -1; + self->init_called = false; + if (format && set_format(self, format) < 0) { + if (type->tp_init == s_init) { + /* No custom __init__() method, so __new__() should do + * all the work. */ + Py_DECREF(format); + Py_DECREF(self); + return NULL; + } + PyObject *exc = PyErr_GetRaisedException(); + Py_CLEAR(self->s_format); + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "Invalid 'format' argument for Struct.__new__(): %S", exc)) + { + Py_DECREF(exc); + Py_DECREF(format); + Py_DECREF(self); + return NULL; + } + Py_DECREF(exc); + } + Py_XDECREF(format); + return (PyObject *)self; +} - self = alloc_func(type, 0); - if (self != NULL) { - PyStructObject *s = (PyStructObject*)self; - s->s_format = Py_NewRef(Py_None); - s->s_codes = NULL; - s->s_size = -1; - s->s_len = -1; +static bool +same_format(PyStructObject *s, PyObject *format) +{ + Py_ssize_t size = PyUnicode_GET_LENGTH(s->s_format); + const void *data = PyUnicode_1BYTE_DATA(s->s_format); + if (PyUnicode_Check(format) && PyUnicode_IS_ASCII(format)) { + return PyUnicode_GET_LENGTH(format) == size + && memcmp(PyUnicode_1BYTE_DATA(format), data, size) == 0; + } + if (PyBytes_Check(format)) { + return PyBytes_GET_SIZE(format) == size + && memcmp(PyBytes_AS_STRING(format), data, size) == 0; } - return self; + return false; } /*[clinic input] @@ -1736,40 +1957,50 @@ Struct.__init__ Create a compiled struct object. -Return a new Struct object which writes and reads binary data according to -the format string. - -See help(struct) for more on format strings. +Return a new Struct object which writes and reads binary data according +to the format string. See help(struct) for more on format strings. [clinic start generated code]*/ static int Struct___init___impl(PyStructObject *self, PyObject *format) -/*[clinic end generated code: output=b8e80862444e92d0 input=192a4575a3dde802]*/ +/*[clinic end generated code: output=b8e80862444e92d0 input=1af78a5f57d82cec]*/ { - int ret = 0; - - if (PyUnicode_Check(format)) { - format = PyUnicode_AsASCIIString(format); - if (format == NULL) + if (self->s_format == NULL) { + if (set_format(self, format) < 0) { return -1; + } } - else { - Py_INCREF(format); + else if (!same_format(self, format)) { + const char *msg = self->init_called + ? "Re-initialization of Struct by calling the __init__() method " + "will not work in future Python versions" + : "Different format arguments for __new__() and __init__() " + "methods of Struct"; + if (PyErr_WarnEx(PyExc_FutureWarning, msg, 1)) { + return -1; + } + if (set_format(self, format) < 0) { + return -1; + } } + self->init_called = true; + return 0; +} - if (!PyBytes_Check(format)) { - Py_DECREF(format); - PyErr_Format(PyExc_TypeError, - "Struct() argument 1 must be a str or bytes object, " - "not %.200s", - _PyType_Name(Py_TYPE(format))); - return -1; +static int +s_init(PyObject *self, PyObject *args, PyObject *kwargs) +{ + if (!((PyStructObject *)self)->init_called + && Py_TYPE(self)->tp_init == s_init + && ((PyStructObject *)self)->s_format != NULL) + { + /* Struct.__init__() was called implicitly. + * __new__() already did all the work. */ + ((PyStructObject *)self)->init_called = true; + return 0; } - - Py_SETREF(self->s_format, format); - - ret = prepare_s(self); - return ret; + /* Struct.__init__() was called explicitly. */ + return Struct___init__(self, args, kwargs); } static int @@ -1819,9 +2050,9 @@ s_unpack_internal(PyStructObject *soself, const char *startfrom, Py_ssize_t j = code->repeat; while (j--) { PyObject *v; - if (e->format == 's') { + if (strcmp(e->format, "s") == 0) { v = PyBytes_FromStringAndSize(res, code->size); - } else if (e->format == 'p') { + } else if (strcmp(e->format, "p") == 0) { Py_ssize_t n; if (code->size == 0) { n = 0; @@ -1849,6 +2080,14 @@ s_unpack_internal(PyStructObject *soself, const char *startfrom, return NULL; } +#define ENSURE_STRUCT_IS_READY(self) \ + do { \ + if (!(self)->s_codes) { \ + PyErr_SetString(PyExc_RuntimeError, \ + "Struct object is not initialized"); \ + return NULL; \ + } \ + } while (0); /*[clinic input] Struct.unpack @@ -1858,18 +2097,17 @@ Struct.unpack Return a tuple containing unpacked values. -Unpack according to the format string Struct.format. The buffer's size -in bytes must be Struct.size. - -See help(struct) for more on format strings. +Unpack according to the struct format string. The buffer's +size in bytes must be the struct size. See help(struct) for more on +format strings. [clinic start generated code]*/ static PyObject * Struct_unpack_impl(PyStructObject *self, Py_buffer *buffer) -/*[clinic end generated code: output=873a24faf02e848a input=3113f8e7038b2f6c]*/ +/*[clinic end generated code: output=873a24faf02e848a input=488843a57c47065a]*/ { _structmodulestate *state = get_struct_state_structinst(self); - assert(self->s_codes != NULL); + ENSURE_STRUCT_IS_READY(self); if (buffer->len != self->s_size) { PyErr_Format(state->StructError, "unpack requires a buffer of %zd bytes", @@ -1887,21 +2125,19 @@ Struct.unpack_from Return a tuple containing unpacked values. -Values are unpacked according to the format string Struct.format. - -The buffer's size in bytes, starting at position offset, must be -at least Struct.size. - -See help(struct) for more on format strings. +Values are unpacked according to the struct format string. The +buffer's size in bytes, starting at position offset, must be at +least the struct size. See help(struct) for more on format +strings. [clinic start generated code]*/ static PyObject * Struct_unpack_from_impl(PyStructObject *self, Py_buffer *buffer, Py_ssize_t offset) -/*[clinic end generated code: output=57fac875e0977316 input=cafd4851d473c894]*/ +/*[clinic end generated code: output=57fac875e0977316 input=57cfcf84c088faa4]*/ { _structmodulestate *state = get_struct_state_structinst(self); - assert(self->s_codes != NULL); + ENSURE_STRUCT_IS_READY(self); if (offset < 0) { if (offset + self->s_size > 0) { @@ -2042,19 +2278,17 @@ Struct.iter_unpack Return an iterator yielding tuples. Tuples are unpacked from the given bytes source, like a repeated -invocation of unpack_from(). - -Requires that the bytes length be a multiple of the struct size. +invocation of unpack_from(). Requires that the bytes length be +a multiple of the struct size. [clinic start generated code]*/ static PyObject * Struct_iter_unpack_impl(PyStructObject *self, PyObject *buffer) -/*[clinic end generated code: output=818f89ad4afa8d64 input=6d65b3f3107dbc99]*/ +/*[clinic end generated code: output=818f89ad4afa8d64 input=9555d2d29d1d9cd2]*/ { _structmodulestate *state = get_struct_state_structinst(self); unpackiterobject *iter; - - assert(self->s_codes != NULL); + ENSURE_STRUCT_IS_READY(self); if (self->s_size == 0) { PyErr_Format(state->StructError, @@ -2089,13 +2323,13 @@ Struct_iter_unpack_impl(PyStructObject *self, PyObject *buffer) * * Takes a struct object, a tuple of arguments, and offset in that tuple of * argument for where to start processing the arguments for packing, and a - * character buffer for writing the packed string. The caller must insure + * character buffer for writing the packed data. The caller must ensure * that the buffer may contain the required length for packing the arguments. * 0 is returned on success, 1 is returned if there is an error. * */ static int -s_pack_internal(PyStructObject *soself, PyObject *const *args, int offset, +s_pack_internal(PyStructObject *soself, PyObject *const *args, char* buf, _structmodulestate *state) { formatcode *code; @@ -2104,14 +2338,14 @@ s_pack_internal(PyStructObject *soself, PyObject *const *args, int offset, Py_ssize_t i; memset(buf, '\0', soself->s_size); - i = offset; + i = 0; for (code = soself->s_codes; code->fmtdef != NULL; code++) { const formatdef *e = code->fmtdef; char *res = buf + code->offset; Py_ssize_t j = code->repeat; while (j--) { PyObject *v = args[i++]; - if (e->format == 's') { + if (strcmp(e->format, "s") == 0) { Py_ssize_t n; int isstring; const void *p; @@ -2133,7 +2367,7 @@ s_pack_internal(PyStructObject *soself, PyObject *const *args, int offset, n = code->size; if (n > 0) memcpy(res, p, n); - } else if (e->format == 'p') { + } else if (strcmp(e->format, "p") == 0) { Py_ssize_t n; int isstring; const void *p; @@ -2179,173 +2413,137 @@ s_pack_internal(PyStructObject *soself, PyObject *const *args, int offset, } -PyDoc_STRVAR(s_pack__doc__, -"S.pack(v1, v2, ...) -> bytes\n\ -\n\ -Return a bytes object containing values v1, v2, ... packed according\n\ -to the format string S.format. See help(struct) for more on format\n\ -strings."); +/*[clinic input] +Struct.pack + + *values: array + +Pack values and return the packed bytes. + +Return a bytes object containing the provided values packed +according to the struct format string. See help(struct) for more on +format strings. +[clinic start generated code]*/ static PyObject * -s_pack(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +Struct_pack_impl(PyStructObject *self, PyObject * const *values, + Py_ssize_t values_length) +/*[clinic end generated code: output=5766e18f596cae9e input=295f4b1a97747458]*/ { - char *buf; - PyStructObject *soself; _structmodulestate *state = get_struct_state_structinst(self); /* Validate arguments. */ - soself = PyStructObject_CAST(self); - assert(PyStruct_Check(self, state)); - assert(soself->s_codes != NULL); - if (nargs != soself->s_len) - { + ENSURE_STRUCT_IS_READY(self); + if (values_length != self->s_len) { PyErr_Format(state->StructError, - "pack expected %zd items for packing (got %zd)", soself->s_len, nargs); + "pack expected %zd items for packing (got %zd)", + self->s_len, values_length); return NULL; } - /* Allocate a new string */ - _PyBytesWriter writer; - _PyBytesWriter_Init(&writer); - buf = _PyBytesWriter_Alloc(&writer, soself->s_size); - if (buf == NULL) { - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter *writer = PyBytesWriter_Create(self->s_size); + if (writer == NULL) { return NULL; } + char *buf = PyBytesWriter_GetData(writer); /* Call the guts */ - if ( s_pack_internal(soself, args, 0, buf, state) != 0 ) { - _PyBytesWriter_Dealloc(&writer); + if (s_pack_internal(self, values, buf, state) != 0) { + PyBytesWriter_Discard(writer); return NULL; } - return _PyBytesWriter_Finish(&writer, buf + soself->s_size); + return PyBytesWriter_FinishWithSize(writer, self->s_size); } -PyDoc_STRVAR(s_pack_into__doc__, -"S.pack_into(buffer, offset, v1, v2, ...)\n\ -\n\ -Pack the values v1, v2, ... according to the format string S.format\n\ -and write the packed bytes into the writable buffer buf starting at\n\ -offset. Note that the offset is a required argument. See\n\ -help(struct) for more on format strings."); +/*[clinic input] +Struct.pack_into + + buffer: Py_buffer(accept={rwbuffer}) + offset: Py_ssize_t + / + *values: array + +Pack values and write the packed bytes into the buffer. + +Pack the provided values according to the struct format string +and write the packed bytes into the writable buffer starting at +offset. Note that the offset is a required argument. See +help(struct) for more on format strings. +[clinic start generated code]*/ static PyObject * -s_pack_into(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +Struct_pack_into_impl(PyStructObject *self, Py_buffer *buffer, + Py_ssize_t offset, PyObject * const *values, + Py_ssize_t values_length) +/*[clinic end generated code: output=aa9d9a93f5f8f77b input=9d842a368ee14245]*/ { - PyStructObject *soself; - Py_buffer buffer; - Py_ssize_t offset; _structmodulestate *state = get_struct_state_structinst(self); - /* Validate arguments. +1 is for the first arg as buffer. */ - soself = PyStructObject_CAST(self); - assert(PyStruct_Check(self, state)); - assert(soself->s_codes != NULL); - if (nargs != (soself->s_len + 2)) - { - if (nargs == 0) { - PyErr_Format(state->StructError, - "pack_into expected buffer argument"); - } - else if (nargs == 1) { - PyErr_Format(state->StructError, - "pack_into expected offset argument"); - } - else { - PyErr_Format(state->StructError, - "pack_into expected %zd items for packing (got %zd)", - soself->s_len, (nargs - 2)); - } - return NULL; - } - - /* Extract a writable memory buffer from the first argument */ - if (!PyArg_Parse(args[0], "w*", &buffer)) - return NULL; - assert(buffer.len >= 0); - - /* Extract the offset from the first argument */ - offset = PyNumber_AsSsize_t(args[1], PyExc_IndexError); - if (offset == -1 && PyErr_Occurred()) { - PyBuffer_Release(&buffer); + ENSURE_STRUCT_IS_READY(self); + if (values_length != self->s_len) { + PyErr_Format(state->StructError, + "pack_into expected %zd items for packing (got %zd)", + self->s_len, values_length); return NULL; } /* Support negative offsets. */ if (offset < 0) { /* Check that negative offset is low enough to fit data */ - if (offset + soself->s_size > 0) { + if (offset + self->s_size > 0) { PyErr_Format(state->StructError, "no space to pack %zd bytes at offset %zd", - soself->s_size, + self->s_size, offset); - PyBuffer_Release(&buffer); return NULL; } /* Check that negative offset is not crossing buffer boundary */ - if (offset + buffer.len < 0) { + if (offset + buffer->len < 0) { PyErr_Format(state->StructError, "offset %zd out of range for %zd-byte buffer", offset, - buffer.len); - PyBuffer_Release(&buffer); + buffer->len); return NULL; } - offset += buffer.len; + offset += buffer->len; } /* Check boundaries */ - if ((buffer.len - offset) < soself->s_size) { + if ((buffer->len - offset) < self->s_size) { assert(offset >= 0); - assert(soself->s_size >= 0); + assert(self->s_size >= 0); PyErr_Format(state->StructError, "pack_into requires a buffer of at least %zu bytes for " "packing %zd bytes at offset %zd " "(actual buffer size is %zd)", - (size_t)soself->s_size + (size_t)offset, - soself->s_size, + (size_t)self->s_size + (size_t)offset, + self->s_size, offset, - buffer.len); - PyBuffer_Release(&buffer); + buffer->len); return NULL; } /* Call the guts */ - if (s_pack_internal(soself, args, 2, (char*)buffer.buf + offset, state) != 0) { - PyBuffer_Release(&buffer); + if (s_pack_internal(self, values, (char*)buffer->buf + offset, state) != 0) { return NULL; } - PyBuffer_Release(&buffer); Py_RETURN_NONE; } -static PyObject * -s_get_format(PyObject *op, void *Py_UNUSED(closure)) -{ - PyStructObject *self = PyStructObject_CAST(op); - return PyUnicode_FromStringAndSize(PyBytes_AS_STRING(self->s_format), - PyBytes_GET_SIZE(self->s_format)); -} - -static PyObject * -s_get_size(PyObject *op, void *Py_UNUSED(closure)) -{ - PyStructObject *self = PyStructObject_CAST(op); - return PyLong_FromSsize_t(self->s_size); -} - -PyDoc_STRVAR(s_sizeof__doc__, -"S.__sizeof__() -> size of S in memory, in bytes"); +/*[clinic input] +Struct.__sizeof__ +[clinic start generated code]*/ static PyObject * -s_sizeof(PyObject *op, PyObject *Py_UNUSED(dummy)) +Struct___sizeof___impl(PyStructObject *self) +/*[clinic end generated code: output=2d0d78900b4cdb4e input=faca5925c1f1ffd0]*/ { - PyStructObject *self = PyStructObject_CAST(op); + ENSURE_STRUCT_IS_READY(self); size_t size = _PyObject_SIZE(Py_TYPE(self)) + sizeof(formatcode); for (formatcode *code = self->s_codes; code->fmtdef != NULL; code++) { size += sizeof(formatcode); @@ -2357,59 +2555,43 @@ static PyObject * s_repr(PyObject *op) { PyStructObject *self = PyStructObject_CAST(op); - PyObject* fmt = PyUnicode_FromStringAndSize( - PyBytes_AS_STRING(self->s_format), PyBytes_GET_SIZE(self->s_format)); - if (fmt == NULL) { - return NULL; - } - PyObject* s = PyUnicode_FromFormat("%s(%R)", _PyType_Name(Py_TYPE(self)), fmt); - Py_DECREF(fmt); - return s; + ENSURE_STRUCT_IS_READY(self); + return PyUnicode_FromFormat("%s(%R)", _PyType_Name(Py_TYPE(self)), self->s_format); } /* List of functions */ static struct PyMethodDef s_methods[] = { STRUCT_ITER_UNPACK_METHODDEF - {"pack", _PyCFunction_CAST(s_pack), METH_FASTCALL, s_pack__doc__}, - {"pack_into", _PyCFunction_CAST(s_pack_into), METH_FASTCALL, s_pack_into__doc__}, + STRUCT_PACK_METHODDEF + STRUCT_PACK_INTO_METHODDEF STRUCT_UNPACK_METHODDEF STRUCT_UNPACK_FROM_METHODDEF - {"__sizeof__", s_sizeof, METH_NOARGS, s_sizeof__doc__}, + STRUCT___SIZEOF___METHODDEF {NULL, NULL} /* sentinel */ }; static PyMemberDef s_members[] = { {"__weaklistoffset__", Py_T_PYSSIZET, offsetof(PyStructObject, weakreflist), Py_READONLY}, + {"format", Py_T_OBJECT_EX, offsetof(PyStructObject, s_format), + Py_READONLY, PyDoc_STR("struct format string")}, + {"size", Py_T_PYSSIZET, offsetof(PyStructObject, s_size), Py_READONLY, + PyDoc_STR("struct size in bytes")}, {NULL} /* sentinel */ }; -static PyGetSetDef s_getsetlist[] = { - {"format", s_get_format, NULL, PyDoc_STR("struct format string"), NULL}, - {"size", s_get_size, NULL, PyDoc_STR("struct size in bytes"), NULL}, - {NULL} /* sentinel */ -}; - -PyDoc_STRVAR(s__doc__, -"Struct(fmt) --> compiled struct object\n" -"\n" -); - static PyType_Slot PyStructType_slots[] = { {Py_tp_dealloc, s_dealloc}, {Py_tp_getattro, PyObject_GenericGetAttr}, {Py_tp_setattro, PyObject_GenericSetAttr}, {Py_tp_repr, s_repr}, - {Py_tp_doc, (void*)s__doc__}, + {Py_tp_doc, (void*)Struct___init____doc__}, {Py_tp_traverse, s_traverse}, {Py_tp_clear, s_clear}, {Py_tp_methods, s_methods}, {Py_tp_members, s_members}, - {Py_tp_getset, s_getsetlist}, - {Py_tp_init, Struct___init__}, - {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_init, s_init}, {Py_tp_new, s_new}, - {Py_tp_free, PyObject_GC_Del}, {0, 0}, }; @@ -2490,61 +2672,56 @@ calcsize_impl(PyObject *module, PyStructObject *s_object) return s_object->s_size; } -PyDoc_STRVAR(pack_doc, -"pack(format, v1, v2, ...) -> bytes\n\ -\n\ -Return a bytes object containing the values v1, v2, ... packed according\n\ -to the format string. See help(struct) for more on format strings."); +/*[clinic input] +pack + + format as s_object: cache_struct + / + *values: array + +Pack values and return the packed bytes. + +Return a bytes object containing the provided values packed according +to the format string. See help(struct) for more on format strings. +[clinic start generated code]*/ static PyObject * -pack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +pack_impl(PyObject *module, PyStructObject *s_object, + PyObject * const *values, Py_ssize_t values_length) +/*[clinic end generated code: output=2f874191ddecdec0 input=8144972342391de1]*/ { - PyObject *s_object = NULL; - PyObject *format, *result; + return Struct_pack_impl(s_object, values, values_length); +} - if (nargs == 0) { - PyErr_SetString(PyExc_TypeError, "missing format argument"); - return NULL; - } - format = args[0]; +/*[clinic input] +pack_into - if (!cache_struct_converter(module, format, (PyStructObject **)&s_object)) { - return NULL; - } - result = s_pack(s_object, args + 1, nargs - 1); - Py_DECREF(s_object); - return result; -} + format as s_object: cache_struct + buffer: Py_buffer(accept={rwbuffer}) + offset: Py_ssize_t + / + *values: array -PyDoc_STRVAR(pack_into_doc, -"pack_into(format, buffer, offset, v1, v2, ...)\n\ -\n\ -Pack the values v1, v2, ... according to the format string and write\n\ -the packed bytes into the writable buffer buf starting at offset. Note\n\ -that the offset is a required argument. See help(struct) for more\n\ -on format strings."); +Pack values and write the packed bytes into the buffer. + +Pack the provided values according to the format string and write the +packed bytes into the writable buffer starting at offset. Note that the +offset is a required argument. See help(struct) for more on format +strings. +[clinic start generated code]*/ static PyObject * -pack_into(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +pack_into_impl(PyObject *module, PyStructObject *s_object, Py_buffer *buffer, + Py_ssize_t offset, PyObject * const *values, + Py_ssize_t values_length) +/*[clinic end generated code: output=e8bf7d422b2088ef input=086867c0f5d8a8e4]*/ { - PyObject *s_object = NULL; - PyObject *format, *result; - - if (nargs == 0) { - PyErr_SetString(PyExc_TypeError, "missing format argument"); - return NULL; - } - format = args[0]; - - if (!cache_struct_converter(module, format, (PyStructObject **)&s_object)) { - return NULL; - } - result = s_pack_into(s_object, args + 1, nargs - 1); - Py_DECREF(s_object); - return result; + return Struct_pack_into_impl(s_object, buffer, offset, + values, values_length); } /*[clinic input] +@permit_long_summary unpack format as s_object: cache_struct @@ -2553,19 +2730,19 @@ unpack Return a tuple containing values unpacked according to the format string. -The buffer's size in bytes must be calcsize(format). - -See help(struct) for more on format strings. +The buffer's size in bytes must be calcsize(format). See help(struct) +for more on format strings. [clinic start generated code]*/ static PyObject * unpack_impl(PyObject *module, PyStructObject *s_object, Py_buffer *buffer) -/*[clinic end generated code: output=48ddd4d88eca8551 input=05fa3b91678da727]*/ +/*[clinic end generated code: output=48ddd4d88eca8551 input=53a60a65830bd1e1]*/ { return Struct_unpack_impl(s_object, buffer); } /*[clinic input] +@permit_long_summary unpack_from format as s_object: cache_struct @@ -2575,15 +2752,14 @@ unpack_from Return a tuple containing values unpacked according to the format string. -The buffer's size, minus offset, must be at least calcsize(format). - -See help(struct) for more on format strings. +The buffer's size, minus offset, must be at least calcsize(format). See +help(struct) for more on format strings. [clinic start generated code]*/ static PyObject * unpack_from_impl(PyObject *module, PyStructObject *s_object, Py_buffer *buffer, Py_ssize_t offset) -/*[clinic end generated code: output=1042631674c6e0d3 input=6e80a5398e985025]*/ +/*[clinic end generated code: output=1042631674c6e0d3 input=3e46619756fb0293]*/ { return Struct_unpack_from_impl(s_object, buffer, offset); } @@ -2597,26 +2773,25 @@ iter_unpack Return an iterator yielding tuples unpacked from the given bytes. -The bytes are unpacked according to the format string, like -a repeated invocation of unpack_from(). - -Requires that the bytes length be a multiple of the format struct size. +The bytes are unpacked according to the format string, like a repeated +invocation of unpack_from(). Requires that the bytes length be +a multiple of calcsize(format). [clinic start generated code]*/ static PyObject * iter_unpack_impl(PyObject *module, PyStructObject *s_object, PyObject *buffer) -/*[clinic end generated code: output=0ae50e250d20e74d input=b214a58869a3c98d]*/ +/*[clinic end generated code: output=0ae50e250d20e74d input=ac5086c5c4ed68bb]*/ { - return Struct_iter_unpack((PyObject*)s_object, buffer); + return Struct_iter_unpack_impl(s_object, buffer); } static struct PyMethodDef module_functions[] = { _CLEARCACHE_METHODDEF CALCSIZE_METHODDEF ITER_UNPACK_METHODDEF - {"pack", _PyCFunction_CAST(pack), METH_FASTCALL, pack_doc}, - {"pack_into", _PyCFunction_CAST(pack_into), METH_FASTCALL, pack_into_doc}, + PACK_METHODDEF + PACK_INTO_METHODDEF UNPACK_METHODDEF UNPACK_FROM_METHODDEF {NULL, NULL} /* sentinel */ @@ -2627,8 +2802,8 @@ static struct PyMethodDef module_functions[] = { PyDoc_STRVAR(module_doc, "Functions to convert between Python values and C structs.\n\ -Python bytes objects are used to hold the data representing the C struct\n\ -and also as format strings (explained below) to describe the layout of data\n\ +Python bytes objects are used to hold the data representing the C struct.\n\ +The format string (explained below) describes the layout of data\n\ in the C struct.\n\ \n\ The optional first format char indicates byte order, size and alignment:\n\ @@ -2638,19 +2813,18 @@ The optional first format char indicates byte order, size and alignment:\n\ >: big-endian, std. size & alignment\n\ !: same as >\n\ \n\ -The remaining chars indicate types of args and must match exactly;\n\ +The remaining characters indicate types of args and must match exactly;\n\ these can be preceded by a decimal repeat count:\n\ - x: pad byte (no data); c:char; b:signed byte; B:unsigned byte;\n\ - ?: _Bool (requires C99; if not available, char is used instead)\n\ - h:short; H:unsigned short; i:int; I:unsigned int;\n\ - l:long; L:unsigned long; f:float; d:double; e:half-float.\n\ + x: pad byte (no data); c: char; b: signed byte; B: unsigned byte;\n\ + ?: _Bool; h: short; H: unsigned short; i: int; I: unsigned int;\n\ + l: long; L: unsigned long; q: long long; Q: unsigned long long;\n\ + f: float; d: double; e: half-float;\n\ + F: float complex; D: double complex.\n\ Special cases (preceding decimal count indicates length):\n\ - s:string (array of char); p: pascal string (with count byte).\n\ + s: byte string (array of char); p: Pascal string (with count byte).\n\ Special cases (only available in native format):\n\ - n:ssize_t; N:size_t;\n\ - P:an integer type that is wide enough to hold a pointer.\n\ -Special case (not in native mode unless 'long long' in platform C):\n\ - q:long long; Q:unsigned long long\n\ + n: ssize_t; N: size_t;\n\ + P: an integer type that is wide enough to hold a pointer.\n\ Whitespace between formats is ignored.\n\ \n\ The variable struct.error is an exception raised on errors.\n"); @@ -2713,47 +2887,8 @@ _structmodule_exec(PyObject *m) return -1; } - /* Check endian and swap in faster functions */ - { - const formatdef *native = native_table; - formatdef *other, *ptr; -#if PY_LITTLE_ENDIAN - other = lilendian_table; -#else - other = bigendian_table; -#endif - /* Scan through the native table, find a matching - entry in the endian table and swap in the - native implementations whenever possible - (64-bit platforms may not have "standard" sizes) */ - while (native->format != '\0' && other->format != '\0') { - ptr = other; - while (ptr->format != '\0') { - if (ptr->format == native->format) { - /* Match faster when formats are - listed in the same order */ - if (ptr == other) - other++; - /* Only use the trick if the - size matches */ - if (ptr->size != native->size) - break; - /* Skip float and double, could be - "unknown" float format */ - if (ptr->format == 'd' || ptr->format == 'f') - break; - /* Skip _Bool, semantics are different for standard size */ - if (ptr->format == '?') - break; - ptr->pack = native->pack; - ptr->unpack = native->unpack; - break; - } - ptr++; - } - native++; - } - } + /* init cannot fail */ + (void)_PyOnceFlag_CallOnce(&endian_tables_init_once, init_endian_tables, NULL); /* Add some symbolic constants to the module */ state->StructError = PyErr_NewException("struct.error", NULL, NULL); @@ -2768,6 +2903,7 @@ _structmodule_exec(PyObject *m) } static PyModuleDef_Slot _structmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _structmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_suggestions.c b/Modules/_suggestions.c index b8bc6db2477281..db1efa7841f995 100644 --- a/Modules/_suggestions.c +++ b/Modules/_suggestions.c @@ -8,6 +8,7 @@ module _suggestions /*[clinic end generated code: output=da39a3ee5e6b4b0d input=e58d81fafad5637b]*/ /*[clinic input] +@critical_section candidates _suggestions._generate_suggestions candidates: object item: unicode @@ -18,7 +19,7 @@ Returns the candidate in candidates that's closest to item static PyObject * _suggestions__generate_suggestions_impl(PyObject *module, PyObject *candidates, PyObject *item) -/*[clinic end generated code: output=79be7b653ae5e7ca input=ba2a8dddc654e33a]*/ +/*[clinic end generated code: output=79be7b653ae5e7ca input=92861a6c9bd8f667]*/ { // Check if dir is a list if (!PyList_CheckExact(candidates)) { @@ -29,7 +30,7 @@ _suggestions__generate_suggestions_impl(PyObject *module, // Check if all elements in the list are Unicode Py_ssize_t size = PyList_Size(candidates); for (Py_ssize_t i = 0; i < size; ++i) { - PyObject *elem = PyList_GetItem(candidates, i); + PyObject *elem = PyList_GET_ITEM(candidates, i); if (!PyUnicode_Check(elem)) { PyErr_SetString(PyExc_TypeError, "all elements in 'candidates' must be strings"); return NULL; @@ -50,6 +51,7 @@ static PyMethodDef module_methods[] = { }; static PyModuleDef_Slot module_slots[] = { + _Py_ABI_SLOT, {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, diff --git a/Modules/_sysconfig.c b/Modules/_sysconfig.c index e2d141b5910b6d..ff22739610e794 100644 --- a/Modules/_sysconfig.c +++ b/Modules/_sysconfig.c @@ -32,6 +32,7 @@ add_string_value(PyObject *dict, const char *key, const char *str_value) #endif /*[clinic input] +@permit_long_summary _sysconfig.config_vars Returns a dictionary containing build variables intended to be exposed by sysconfig. @@ -39,7 +40,7 @@ Returns a dictionary containing build variables intended to be exposed by syscon static PyObject * _sysconfig_config_vars_impl(PyObject *module) -/*[clinic end generated code: output=9c41cdee63ea9487 input=391ff42f3af57d01]*/ +/*[clinic end generated code: output=9c41cdee63ea9487 input=fdda9cab12ca19fe]*/ { PyObject *config = PyDict_New(); if (config == NULL) { @@ -80,15 +81,53 @@ _sysconfig_config_vars_impl(PyObject *module) return config; } +#ifdef MS_WINDOWS +/*[clinic input] +_sysconfig.get_platform + +Return a string that identifies the current platform. +[clinic start generated code]*/ + +static PyObject * +_sysconfig_get_platform_impl(PyObject *module) +/*[clinic end generated code: output=4ecbbe2b77633f3e input=c0b43abda44f9a01]*/ +{ +#ifdef MS_WIN64 +# if defined(_M_X64) || defined(_M_AMD64) +# define SYSCONFIG_PLATFORM "win-amd64" +# elif defined(_M_ARM64) +# define SYSCONFIG_PLATFORM "win-arm64" +# endif +#endif + +#if defined(MS_WIN32) && !defined(MS_WIN64) +# if defined(_M_IX86) +# define SYSCONFIG_PLATFORM "win32" +# elif defined(_M_ARM) +# define SYSCONFIG_PLATFORM "win-arm32" +# endif +#endif + +#ifdef SYSCONFIG_PLATFORM + return PyUnicode_FromString(SYSCONFIG_PLATFORM); +#else + Py_RETURN_NONE; +#endif +} +#endif // MS_WINDOWS + + PyDoc_STRVAR(sysconfig__doc__, "A helper for the sysconfig module."); static struct PyMethodDef sysconfig_methods[] = { _SYSCONFIG_CONFIG_VARS_METHODDEF + _SYSCONFIG_GET_PLATFORM_METHODDEF {NULL, NULL} }; static PyModuleDef_Slot sysconfig_slots[] = { + _Py_ABI_SLOT, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} diff --git a/Modules/_testbuffer.c b/Modules/_testbuffer.c index d2e61e9d6acf24..8b6b617aafa427 100644 --- a/Modules/_testbuffer.c +++ b/Modules/_testbuffer.c @@ -351,7 +351,7 @@ pack_from_list(PyObject *obj, PyObject *items, PyObject *format, item = PySequence_Fast_GET_ITEM(items, i); if ((PyBytes_Check(item) || PyLong_Check(item) || - PyFloat_Check(item)) && nmemb == 1) { + PyFloat_Check(item) || PyComplex_Check(item)) && nmemb == 1) { PyTuple_SET_ITEM(args, 2, item); } else if ((PyList_Check(item) || PyTuple_Check(item)) && @@ -433,7 +433,7 @@ pack_single(char *ptr, PyObject *item, const char *fmt, Py_ssize_t itemsize) PyTuple_SET_ITEM(args, 1, zero); if ((PyBytes_Check(item) || PyLong_Check(item) || - PyFloat_Check(item)) && nmemb == 1) { + PyFloat_Check(item) || PyComplex_Check(item)) && nmemb == 1) { PyTuple_SET_ITEM(args, 2, item); } else if ((PyList_Check(item) || PyTuple_Check(item)) && diff --git a/Modules/_testcapi/buffer.c b/Modules/_testcapi/buffer.c index e63d4179824529..48393a3dd530c6 100644 --- a/Modules/_testcapi/buffer.c +++ b/Modules/_testcapi/buffer.c @@ -98,6 +98,27 @@ static PyTypeObject testBufType = { .tp_members = testbuf_members }; +/* Get the pointer from a buffer-supporting object as a PyLong. + * + * Used to test alignment properties. */ +static PyObject * +buffer_pointer_as_int(PyObject *Py_UNUSED(module), PyObject *obj) +{ + PyObject *out; + Py_buffer view; + if (PyObject_GetBuffer(obj, &view, PyBUF_SIMPLE) != 0) { + return NULL; + } + out = PyLong_FromVoidPtr(view.buf); + PyBuffer_Release(&view); + return out; +} + +static PyMethodDef test_methods[] = { + {"buffer_pointer_as_int", buffer_pointer_as_int, METH_O}, + {NULL}, +}; + int _PyTestCapi_Init_Buffer(PyObject *m) { if (PyType_Ready(&testBufType) < 0) { @@ -106,6 +127,9 @@ _PyTestCapi_Init_Buffer(PyObject *m) { if (PyModule_AddObjectRef(m, "testBuf", (PyObject *)&testBufType)) { return -1; } + if (PyModule_AddFunctions(m, test_methods) < 0) { + return -1; + } return 0; } diff --git a/Modules/_testcapi/bytes.c b/Modules/_testcapi/bytes.c index 33903de14ba68d..f12fc7f5f3a2a8 100644 --- a/Modules/_testcapi/bytes.c +++ b/Modules/_testcapi/bytes.c @@ -1,6 +1,11 @@ +// Use pycore_bytes.h +#define PYTESTCAPI_NEED_INTERNAL_API + #include "parts.h" #include "util.h" +#include "pycore_bytesobject.h" // _PyBytesWriter_CreateByteArray() + /* Test _PyBytes_Resize() */ static PyObject * @@ -51,9 +56,307 @@ bytes_join(PyObject *Py_UNUSED(module), PyObject *args) } +// --- PyBytesWriter type --------------------------------------------------- + +typedef struct { + PyObject_HEAD + PyBytesWriter *writer; +} WriterObject; + + +static PyObject * +writer_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + WriterObject *self = (WriterObject *)type->tp_alloc(type, 0); + if (!self) { + return NULL; + } + self->writer = NULL; + return (PyObject*)self; +} + + +static int +writer_init(PyObject *self_raw, PyObject *args, PyObject *kwargs) +{ + if (kwargs && PyDict_GET_SIZE(kwargs)) { + PyErr_Format(PyExc_TypeError, + "PyBytesWriter() takes exactly no keyword arguments"); + return -1; + } + + Py_ssize_t alloc; + char *str; + Py_ssize_t str_size; + int use_bytearray; + if (!PyArg_ParseTuple(args, "ny#i", + &alloc, &str, &str_size, &use_bytearray)) { + return -1; + } + + WriterObject *self = (WriterObject *)self_raw; + if (self->writer) { + PyBytesWriter_Discard(self->writer); + } + if (use_bytearray) { + self->writer = _PyBytesWriter_CreateByteArray(alloc); + } + else { + self->writer = PyBytesWriter_Create(alloc); + } + if (self->writer == NULL) { + return -1; + } + + if (str_size) { + char *buf = PyBytesWriter_GetData(self->writer); + memcpy(buf, str, str_size); + } + + return 0; +} + + +static void +writer_dealloc(PyObject *self_raw) +{ + WriterObject *self = (WriterObject *)self_raw; + PyTypeObject *tp = Py_TYPE(self); + if (self->writer) { + PyBytesWriter_Discard(self->writer); + } + tp->tp_free(self); + Py_DECREF(tp); +} + + +static inline int +writer_check(WriterObject *self) +{ + if (self->writer == NULL) { + PyErr_SetString(PyExc_ValueError, "operation on finished writer"); + return -1; + } + return 0; +} + + +static PyObject* +writer_write_bytes(PyObject *self_raw, PyObject *args) +{ + WriterObject *self = (WriterObject *)self_raw; + if (writer_check(self) < 0) { + return NULL; + } + + char *bytes; + Py_ssize_t size; + if (!PyArg_ParseTuple(args, "yn", &bytes, &size)) { + return NULL; + } + + if (PyBytesWriter_WriteBytes(self->writer, bytes, size) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + + +static PyObject* +writer_format_i(PyObject *self_raw, PyObject *args) +{ + WriterObject *self = (WriterObject *)self_raw; + if (writer_check(self) < 0) { + return NULL; + } + + char *format; + int value; + if (!PyArg_ParseTuple(args, "yi", &format, &value)) { + return NULL; + } + + if (PyBytesWriter_Format(self->writer, format, value) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + + +static PyObject* +writer_resize(PyObject *self_raw, PyObject *args) +{ + WriterObject *self = (WriterObject *)self_raw; + if (writer_check(self) < 0) { + return NULL; + } + + Py_ssize_t size; + char *str; + Py_ssize_t str_size; + if (!PyArg_ParseTuple(args, + "ny#", + &size, &str, &str_size)) { + return NULL; + } + assert(size >= str_size); + + Py_ssize_t pos = PyBytesWriter_GetSize(self->writer); + if (PyBytesWriter_Resize(self->writer, size) < 0) { + return NULL; + } + + char *buf = PyBytesWriter_GetData(self->writer); + memcpy(buf + pos, str, str_size); + + Py_RETURN_NONE; +} + + +static PyObject* +writer_get_size(PyObject *self_raw, PyObject *Py_UNUSED(args)) +{ + WriterObject *self = (WriterObject *)self_raw; + if (writer_check(self) < 0) { + return NULL; + } + + Py_ssize_t alloc = PyBytesWriter_GetSize(self->writer); + return PyLong_FromSsize_t(alloc); +} + + +static PyObject* +writer_finish(PyObject *self_raw, PyObject *Py_UNUSED(args)) +{ + WriterObject *self = (WriterObject *)self_raw; + if (writer_check(self) < 0) { + return NULL; + } + + PyObject *str = PyBytesWriter_Finish(self->writer); + self->writer = NULL; + return str; +} + + +static PyObject* +writer_finish_with_size(PyObject *self_raw, PyObject *args) +{ + WriterObject *self = (WriterObject *)self_raw; + if (writer_check(self) < 0) { + return NULL; + } + + Py_ssize_t size; + if (!PyArg_ParseTuple(args, "n", &size)) { + return NULL; + } + + PyObject *str = PyBytesWriter_FinishWithSize(self->writer, size); + self->writer = NULL; + return str; +} + + +static PyMethodDef writer_methods[] = { + {"write_bytes", _PyCFunction_CAST(writer_write_bytes), METH_VARARGS}, + {"format_i", _PyCFunction_CAST(writer_format_i), METH_VARARGS}, + {"resize", _PyCFunction_CAST(writer_resize), METH_VARARGS}, + {"get_size", _PyCFunction_CAST(writer_get_size), METH_NOARGS}, + {"finish", _PyCFunction_CAST(writer_finish), METH_NOARGS}, + {"finish_with_size", _PyCFunction_CAST(writer_finish_with_size), METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyType_Slot Writer_Type_slots[] = { + {Py_tp_new, writer_new}, + {Py_tp_init, writer_init}, + {Py_tp_dealloc, writer_dealloc}, + {Py_tp_methods, writer_methods}, + {0, 0}, /* sentinel */ +}; + +static PyType_Spec Writer_spec = { + .name = "_testcapi.PyBytesWriter", + .basicsize = sizeof(WriterObject), + .flags = Py_TPFLAGS_DEFAULT, + .slots = Writer_Type_slots, +}; + + +static PyObject * +byteswriter_abc(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + PyBytesWriter *writer = PyBytesWriter_Create(3); + if (writer == NULL) { + return NULL; + } + + char *str = PyBytesWriter_GetData(writer); + memcpy(str, "abc", 3); + + return PyBytesWriter_Finish(writer); +} + + +static PyObject * +byteswriter_resize(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + // Allocate 10 bytes + PyBytesWriter *writer = PyBytesWriter_Create(10); + if (writer == NULL) { + return NULL; + } + char *buf = PyBytesWriter_GetData(writer); + + // Write some bytes + memcpy(buf, "Hello ", strlen("Hello ")); + buf += strlen("Hello "); + + // Allocate 10 more bytes + buf = PyBytesWriter_GrowAndUpdatePointer(writer, 10, buf); + if (buf == NULL) { + PyBytesWriter_Discard(writer); + return NULL; + } + + // Write more bytes + memcpy(buf, "World", strlen("World")); + buf += strlen("World"); + + // Truncate to the exact size and create a bytes object + return PyBytesWriter_FinishWithPointer(writer, buf); +} + + +static PyObject * +byteswriter_highlevel(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + PyBytesWriter *writer = PyBytesWriter_Create(0); + if (writer == NULL) { + goto error; + } + if (PyBytesWriter_WriteBytes(writer, "Hello", -1) < 0) { + goto error; + } + if (PyBytesWriter_Format(writer, " %s!", "World") < 0) { + goto error; + } + return PyBytesWriter_Finish(writer); + +error: + PyBytesWriter_Discard(writer); + return NULL; +} + + static PyMethodDef test_methods[] = { {"bytes_resize", bytes_resize, METH_VARARGS}, {"bytes_join", bytes_join, METH_VARARGS}, + {"byteswriter_abc", byteswriter_abc, METH_NOARGS}, + {"byteswriter_resize", byteswriter_resize, METH_NOARGS}, + {"byteswriter_highlevel", byteswriter_highlevel, METH_NOARGS}, {NULL}, }; @@ -64,5 +367,15 @@ _PyTestCapi_Init_Bytes(PyObject *m) return -1; } + PyTypeObject *writer_type = (PyTypeObject *)PyType_FromSpec(&Writer_spec); + if (writer_type == NULL) { + return -1; + } + if (PyModule_AddType(m, writer_type) < 0) { + Py_DECREF(writer_type); + return -1; + } + Py_DECREF(writer_type); + return 0; } diff --git a/Modules/_testcapi/dict.c b/Modules/_testcapi/dict.c index b7c73d7332bd4e..172591b03182ab 100644 --- a/Modules/_testcapi/dict.c +++ b/Modules/_testcapi/dict.c @@ -258,6 +258,43 @@ test_dict_iteration(PyObject* self, PyObject *Py_UNUSED(ignored)) } +static PyObject * +frozendict_check(PyObject *self, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyFrozenDict_Check(obj)); +} + +static PyObject * +frozendict_checkexact(PyObject *self, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyFrozenDict_CheckExact(obj)); +} + +static PyObject * +anydict_check(PyObject *self, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyAnyDict_Check(obj)); +} + +static PyObject * +anydict_checkexact(PyObject *self, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyAnyDict_CheckExact(obj)); +} + + +static PyObject * +frozendict_new(PyObject *self, PyObject *obj) +{ + NULLABLE(obj); + return PyFrozenDict_New(obj); +} + + static PyMethodDef test_methods[] = { {"dict_containsstring", dict_containsstring, METH_VARARGS}, {"dict_getitemref", dict_getitemref, METH_VARARGS}, @@ -269,6 +306,11 @@ static PyMethodDef test_methods[] = { {"dict_popstring", dict_popstring, METH_VARARGS}, {"dict_popstring_null", dict_popstring_null, METH_VARARGS}, {"test_dict_iteration", test_dict_iteration, METH_NOARGS}, + {"frozendict_check", frozendict_check, METH_O}, + {"frozendict_checkexact", frozendict_checkexact, METH_O}, + {"anydict_check", anydict_check, METH_O}, + {"anydict_checkexact", anydict_checkexact, METH_O}, + {"frozendict_new", frozendict_new, METH_O}, {NULL}, }; diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c index 0604b413e33f61..c0254e044bc2d5 100644 --- a/Modules/_testcapi/exceptions.c +++ b/Modules/_testcapi/exceptions.c @@ -82,6 +82,7 @@ _testcapi_exception_print_impl(PyObject *module, PyObject *exc, int legacy) } /*[clinic input] +@permit_long_summary _testcapi.make_exception_with_doc name: str doc: str = NULL @@ -95,7 +96,7 @@ static PyObject * _testcapi_make_exception_with_doc_impl(PyObject *module, const char *name, const char *doc, PyObject *base, PyObject *dict) -/*[clinic end generated code: output=439f0d963c1ce2c4 input=23a73013f8a8795a]*/ +/*[clinic end generated code: output=439f0d963c1ce2c4 input=508b420b7f9253ed]*/ { return PyErr_NewExceptionWithDoc(name, doc, base, dict); } diff --git a/Modules/_testcapi/float.c b/Modules/_testcapi/float.c index e3869134c84d43..63de77ca6b8651 100644 --- a/Modules/_testcapi/float.c +++ b/Modules/_testcapi/float.c @@ -171,5 +171,9 @@ _PyTestCapi_Init_Float(PyObject *mod) return -1; } - return 0; +#if (defined(__mips__) && !defined(__mips_nan2008)) || defined(__hppa__) + return PyModule_Add(mod, "nan_msb_is_signaling", PyBool_FromLong(1)); +#else + return PyModule_Add(mod, "nan_msb_is_signaling", PyBool_FromLong(0)); +#endif } diff --git a/Modules/_testcapi/function.c b/Modules/_testcapi/function.c index ec1ba508df2ce9..40767adbd3f14a 100644 --- a/Modules/_testcapi/function.c +++ b/Modules/_testcapi/function.c @@ -123,6 +123,13 @@ function_set_closure(PyObject *self, PyObject *args) } +static PyObject * +function_get_annotations(PyObject *self, PyObject *func) +{ + return Py_XNewRef(PyFunction_GetAnnotations(func)); +} + + static PyMethodDef test_methods[] = { {"function_get_code", function_get_code, METH_O, NULL}, {"function_get_globals", function_get_globals, METH_O, NULL}, @@ -133,6 +140,7 @@ static PyMethodDef test_methods[] = { {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL}, {"function_get_closure", function_get_closure, METH_O, NULL}, {"function_set_closure", function_set_closure, METH_VARARGS, NULL}, + {"function_get_annotations", function_get_annotations, METH_O, NULL}, {NULL}, }; diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c index 257e0256655976..963f464c47b902 100644 --- a/Modules/_testcapi/heaptype.c +++ b/Modules/_testcapi/heaptype.c @@ -403,6 +403,7 @@ static PyObject * pyobject_getitemdata(PyObject *self, PyObject *o) { void *pointer = PyObject_GetItemData(o); + assert(pointer == PyObject_GetItemData_DuringGC(o)); if (pointer == NULL) { return NULL; } @@ -485,17 +486,27 @@ pytype_getbasebytoken(PyObject *self, PyObject *args) mro_save = type->tp_mro; type->tp_mro = NULL; } - void *token = PyLong_AsVoidPtr(py_token); + if (PyErr_Occurred()) { + return NULL; + } + + void *result_duringgc; + int ret_duringgc = PyType_GetBaseByToken_DuringGC( + type, token, (PyTypeObject **)&result_duringgc); + assert(!PyErr_Occurred()); + PyObject *result; int ret; if (need_result == Py_True) { ret = PyType_GetBaseByToken(type, token, (PyTypeObject **)&result); + assert(result == result_duringgc); } else { result = NULL; ret = PyType_GetBaseByToken(type, token, NULL); } + assert(ret == ret_duringgc); if (use_mro != Py_True) { type->tp_mro = mro_save; @@ -518,6 +529,7 @@ pytype_getbasebytoken(PyObject *self, PyObject *args) error: Py_XDECREF(py_ret); Py_XDECREF(result); + assert(PyErr_Occurred()); return NULL; } @@ -525,9 +537,46 @@ static PyObject * pytype_getmodulebydef(PyObject *self, PyObject *type) { PyObject *mod = PyType_GetModuleByDef((PyTypeObject *)type, _testcapimodule); + assert(mod == PyType_GetModuleByToken_DuringGC((PyTypeObject *)type, _testcapimodule)); return Py_XNewRef(mod); } +static PyObject * +pytype_getmodulebytoken(PyObject *self, PyObject *args) +{ + PyObject *type; + PyObject *py_token; + if (!PyArg_ParseTuple(args, "OO", &type, &py_token)) { + return NULL; + } + void *token = PyLong_AsVoidPtr(py_token); + if ((!token) && PyErr_Occurred()) { + return NULL; + } + PyObject *result = PyType_GetModuleByToken((PyTypeObject *)type, token); + assert(result == PyType_GetModuleByToken_DuringGC((PyTypeObject *)type, token)); + return result; +} + +static PyType_Slot HeapCTypeWithBasesSlotNone_slots[] = { + {Py_tp_bases, NULL}, /* filled out with Py_None in runtime */ + {0, 0}, +}; + +static PyType_Spec HeapCTypeWithBasesSlotNone_spec = { + .name = "_testcapi.HeapCTypeWithBasesSlotNone", + .basicsize = sizeof(PyObject), + .flags = Py_TPFLAGS_DEFAULT, + .slots = HeapCTypeWithBasesSlotNone_slots +}; + +static PyObject * +create_heapctype_with_none_bases_slot(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + HeapCTypeWithBasesSlotNone_slots[0].pfunc = Py_None; + return PyType_FromSpec(&HeapCTypeWithBasesSlotNone_spec); +} + static PyMethodDef TestMethods[] = { {"pytype_fromspec_meta", pytype_fromspec_meta, METH_O}, @@ -546,6 +595,9 @@ static PyMethodDef TestMethods[] = { {"get_tp_token", get_tp_token, METH_O}, {"pytype_getbasebytoken", pytype_getbasebytoken, METH_VARARGS}, {"pytype_getmodulebydef", pytype_getmodulebydef, METH_O}, + {"pytype_getmodulebytoken", pytype_getmodulebytoken, METH_VARARGS}, + {"create_heapctype_with_none_bases_slot", + create_heapctype_with_none_bases_slot, METH_NOARGS}, {NULL}, }; @@ -782,10 +834,8 @@ heapctypesubclasswithfinalizer_finalize(PyObject *self) /* Save the current exception, if any. */ PyObject *exc = PyErr_GetRaisedException(); - if (_testcapimodule == NULL) { - goto cleanup_finalize; - } - PyObject *m = PyState_FindModule(_testcapimodule); + PyObject *m = PyType_GetModule(Py_TYPE(self)); + assert(m == PyType_GetModule_DuringGC(Py_TYPE(self))); if (m == NULL) { goto cleanup_finalize; } @@ -879,6 +929,18 @@ static PyType_Spec HeapCTypeMetaclassNullNew_spec = { .slots = empty_type_slots }; +static PyType_Slot HeapCTypeWithBasesSlot_slots[] = { + {Py_tp_bases, NULL}, /* filled out in module init function */ + {0, 0}, +}; + +static PyType_Spec HeapCTypeWithBasesSlot_spec = { + .name = "_testcapi.HeapCTypeWithBasesSlot", + .basicsize = sizeof(PyLongObject), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = HeapCTypeWithBasesSlot_slots +}; + typedef struct { PyObject_HEAD @@ -1237,6 +1299,7 @@ HeapCCollection_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) goto finally; } PyObject **data = PyObject_GetItemData(self); + assert(data == PyObject_GetItemData_DuringGC(self)); if (!data) { goto finally; } @@ -1266,6 +1329,7 @@ HeapCCollection_item(PyObject *self, Py_ssize_t i) return PyErr_Format(PyExc_IndexError, "index %zd out of range", i); } PyObject **data = PyObject_GetItemData(self); + assert(data == PyObject_GetItemData_DuringGC(self)); if (!data) { return NULL; } @@ -1276,6 +1340,7 @@ static int HeapCCollection_traverse(PyObject *self, visitproc visit, void *arg) { PyObject **data = PyObject_GetItemData(self); + assert(data == PyObject_GetItemData_DuringGC(self)); if (!data) { return -1; } @@ -1289,6 +1354,7 @@ static int HeapCCollection_clear(PyObject *self) { PyObject **data = PyObject_GetItemData(self); + assert(data == PyObject_GetItemData_DuringGC(self)); if (!data) { return -1; } @@ -1402,8 +1468,8 @@ _PyTestCapi_Init_Heaptype(PyObject *m) { if (subclass_with_finalizer_bases == NULL) { return -1; } - PyObject *HeapCTypeSubclassWithFinalizer = PyType_FromSpecWithBases( - &HeapCTypeSubclassWithFinalizer_spec, subclass_with_finalizer_bases); + PyObject *HeapCTypeSubclassWithFinalizer = PyType_FromModuleAndSpec( + m, &HeapCTypeSubclassWithFinalizer_spec, subclass_with_finalizer_bases); Py_DECREF(subclass_with_finalizer_bases); ADD("HeapCTypeSubclassWithFinalizer", HeapCTypeSubclassWithFinalizer); @@ -1419,6 +1485,18 @@ _PyTestCapi_Init_Heaptype(PyObject *m) { &PyType_Type, m, &HeapCTypeMetaclassNullNew_spec, (PyObject *) &PyType_Type); ADD("HeapCTypeMetaclassNullNew", HeapCTypeMetaclassNullNew); + PyObject *bases = PyTuple_Pack(1, &PyLong_Type); + if (bases == NULL) { + return -1; + } + HeapCTypeWithBasesSlot_slots[0].pfunc = bases; + PyObject *HeapCTypeWithBasesSlot = PyType_FromSpec(&HeapCTypeWithBasesSlot_spec); + Py_DECREF(bases); + if (HeapCTypeWithBasesSlot == NULL) { + return -1; + } + ADD("HeapCTypeWithBasesSlot", HeapCTypeWithBasesSlot); + ADD("Py_TP_USE_SPEC", PyLong_FromVoidPtr(Py_TP_USE_SPEC)); PyObject *HeapCCollection = PyType_FromMetaclass( diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index 0663c3781d426a..1c87025594a48b 100644 --- a/Modules/_testcapi/immortal.c +++ b/Modules/_testcapi/immortal.c @@ -28,16 +28,16 @@ test_immortal_builtins(PyObject *self, PyObject *Py_UNUSED(ignored)) static PyObject * test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored)) { - for (int i = -5; i <= 256; i++) { + for (int i = -5; i <= 1024; i++) { PyObject *obj = PyLong_FromLong(i); assert(verify_immortality(obj)); - int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK; + int has_int_immortal_bit = _PyLong_IsSmallInt((PyLongObject *)obj); assert(has_int_immortal_bit); } - for (int i = 257; i <= 260; i++) { + for (int i = 1025; i <= 1030; i++) { PyObject *obj = PyLong_FromLong(i); assert(obj); - int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK; + int has_int_immortal_bit = _PyLong_IsSmallInt((PyLongObject *)obj); assert(!has_int_immortal_bit); Py_DECREF(obj); } diff --git a/Modules/_testcapi/import.c b/Modules/_testcapi/import.c index 27d37498f3cd83..384a8f52da4b98 100644 --- a/Modules/_testcapi/import.c +++ b/Modules/_testcapi/import.c @@ -30,9 +30,71 @@ pyimport_importmoduleattrstring(PyObject *self, PyObject *args) } +static PyObject * +pyimport_setlazyimportsmode(PyObject *self, PyObject *args) +{ + PyObject *mode; + if (!PyArg_ParseTuple(args, "U", &mode)) { + return NULL; + } + if (strcmp(PyUnicode_AsUTF8(mode), "normal") == 0) { + PyImport_SetLazyImportsMode(PyImport_LAZY_NORMAL); + } else if (strcmp(PyUnicode_AsUTF8(mode), "all") == 0) { + PyImport_SetLazyImportsMode(PyImport_LAZY_ALL); + } else { + PyErr_SetString(PyExc_ValueError, "invalid mode"); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject * +pyimport_getlazyimportsmode(PyObject *self, PyObject *args) +{ + switch (PyImport_GetLazyImportsMode()) { + case PyImport_LAZY_NORMAL: + return PyUnicode_FromString("normal"); + case PyImport_LAZY_ALL: + return PyUnicode_FromString("all"); + default: + PyErr_SetString(PyExc_ValueError, "unknown mode"); + return NULL; + } +} + +static PyObject * +pyimport_setlazyimportsfilter(PyObject *self, PyObject *args) +{ + PyObject *filter; + if (!PyArg_ParseTuple(args, "O", &filter)) { + return NULL; + } + + if (PyImport_SetLazyImportsFilter(filter) < 0) { + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject * +pyimport_getlazyimportsfilter(PyObject *self, PyObject *args) +{ + PyObject *res = PyImport_GetLazyImportsFilter(); + if (res == NULL) { + Py_RETURN_NONE; + } + return res; +} + static PyMethodDef test_methods[] = { {"PyImport_ImportModuleAttr", pyimport_importmoduleattr, METH_VARARGS}, {"PyImport_ImportModuleAttrString", pyimport_importmoduleattrstring, METH_VARARGS}, + {"PyImport_SetLazyImportsMode", pyimport_setlazyimportsmode, METH_VARARGS}, + {"PyImport_GetLazyImportsMode", pyimport_getlazyimportsmode, METH_NOARGS}, + {"PyImport_SetLazyImportsFilter", pyimport_setlazyimportsfilter, METH_VARARGS}, + {"PyImport_GetLazyImportsFilter", pyimport_getlazyimportsfilter, METH_NOARGS}, {NULL}, }; @@ -41,4 +103,3 @@ _PyTestCapi_Init_Import(PyObject *m) { return PyModule_AddFunctions(m, test_methods); } - diff --git a/Modules/_testcapi/long.c b/Modules/_testcapi/long.c index 6313abf5485fff..008a7d37726869 100644 --- a/Modules/_testcapi/long.c +++ b/Modules/_testcapi/long.c @@ -254,6 +254,25 @@ pylongwriter_create(PyObject *module, PyObject *args) } +static PyObject * +pylongwriter_finish_bug(PyObject *module, PyObject *Py_UNUSED(args)) +{ + void *writer_digits; + PyLongWriter *writer = PyLongWriter_Create(0, 3, &writer_digits); + if (writer == NULL) { + return NULL; + } + + assert(PyLong_GetNativeLayout()->digit_size == sizeof(digit)); + digit *digits = writer_digits; + digits[0] = 1; + digits[1] = 1; + // Oops, digits[2] is left uninitialized on purpose + // to test PyLongWriter_Finish() + return PyLongWriter_Finish(writer); +} + + static PyObject * get_pylong_layout(PyObject *module, PyObject *Py_UNUSED(args)) { @@ -271,6 +290,7 @@ static PyMethodDef test_methods[] = { {"pylong_aspid", pylong_aspid, METH_O}, {"pylong_export", pylong_export, METH_O}, {"pylongwriter_create", pylongwriter_create, METH_VARARGS}, + {"pylongwriter_finish_bug", pylongwriter_finish_bug, METH_NOARGS}, {"get_pylong_layout", get_pylong_layout, METH_NOARGS}, {"pylong_ispositive", pylong_ispositive, METH_O}, {"pylong_isnegative", pylong_isnegative, METH_O}, diff --git a/Modules/_testcapi/mem.c b/Modules/_testcapi/mem.c index b4896f984510bd..28e89275d61705 100644 --- a/Modules/_testcapi/mem.c +++ b/Modules/_testcapi/mem.c @@ -2,6 +2,28 @@ #include <stddef.h> +#if defined(__APPLE__) +# include <TargetConditionals.h> + // Older macOS SDKs do not define TARGET_OS_OSX +# if !defined(TARGET_OS_OSX) +# define TARGET_OS_OSX 1 +# endif +# if TARGET_OS_OSX +# include <errno.h> // errno, ESRCH +# include <libproc.h> // proc_pidinfo(), PROC_PIDTASKINFO +# include <sys/proc_info.h> // struct proc_taskinfo +# endif +#endif + +#ifdef __FreeBSD__ +# include <fcntl.h> // O_RDONLY +# include <kvm.h> // kvm_openfiles() +# include <limits.h> // _POSIX2_LINE_MAX +# include <sys/sysctl.h> // KERN_PROC_PID +# include <sys/user.h> // kinfo_proc definition +# include <unistd.h> // sysconf() +#endif + typedef struct { PyMemAllocatorEx alloc; @@ -323,6 +345,53 @@ test_setallocators(PyMemAllocatorDomain domain) goto fail; } + /* realloc(NULL, size) should behave like malloc(size) */ + size_t size3 = 100; + void *ptr3; + switch(domain) { + case PYMEM_DOMAIN_RAW: + ptr3 = PyMem_RawRealloc(NULL, size3); + break; + case PYMEM_DOMAIN_MEM: + ptr3 = PyMem_Realloc(NULL, size3); + break; + case PYMEM_DOMAIN_OBJ: + ptr3 = PyObject_Realloc(NULL, size3); + break; + default: + ptr3 = NULL; + break; + } + + CHECK_CTX("realloc(NULL, size)"); + if (ptr3 == NULL) { + error_msg = "realloc(NULL, size) failed"; + goto fail; + } + if (hook.realloc_ptr != NULL || hook.realloc_new_size != size3) { + error_msg = "realloc(NULL, size) invalid parameters"; + goto fail; + } + + hook.free_ptr = NULL; + switch(domain) { + case PYMEM_DOMAIN_RAW: + PyMem_RawFree(ptr3); + break; + case PYMEM_DOMAIN_MEM: + PyMem_Free(ptr3); + break; + case PYMEM_DOMAIN_OBJ: + PyObject_Free(ptr3); + break; + } + + CHECK_CTX("realloc(NULL, size) free"); + if (hook.free_ptr != ptr3) { + error_msg = "unexpected pointer passed to free"; + goto fail; + } + res = Py_NewRef(Py_None); goto finally; @@ -684,6 +753,74 @@ tracemalloc_track_race(PyObject *self, PyObject *args) } +#if TARGET_OS_OSX || defined(__FreeBSD__) +// Return RSS only. Per-process swap usage isn't readily available +static PyObject* +get_process_memory_usage(PyObject *self, PyObject *args) +{ + int pid; + if (!PyArg_ParseTuple(args, "i", &pid)) { + return NULL; + } + +#if TARGET_OS_OSX + // macOS: proc_pidinfo(PROC_PIDTASKINFO).pti_resident_size + struct proc_taskinfo pti; + int ret = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti)); + if (ret <= 0) { + if (errno == 0) { + // proc_pidinfo() can return 0 without setting errno when the + // process does not exist. + errno = ESRCH; + } + return PyErr_SetFromErrno(PyExc_OSError); + } + + return PyLong_FromUnsignedLongLong(pti.pti_resident_size); +#else + // FreeBSD: kvm_getprocs(KERN_PROC_PID) and ki_rssize * page_size + long page_size = sysconf(_SC_PAGESIZE); + if (page_size <= 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + + // Using /dev/null for vmcore avoids needing dump file. + // NULL for kernel file uses running kernel. + char errbuf[_POSIX2_LINE_MAX]; + kvm_t *kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf); + if (kd == NULL) { + return PyErr_SetFromErrno(PyExc_OSError); + } + + // KERN_PROC_PID filters for the specific process ID. + int n_procs; + struct kinfo_proc *kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &n_procs); + if (kp == NULL) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (n_procs <= 0) { + // Process with PID not found + errno = ESRCH; + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + assert(n_procs == 1); + + // ki_rssize is in pages. Convert to bytes. + size_t rss = (size_t)kp[0].ki_rssize * page_size; + kvm_close(kd); + + return PyLong_FromSize_t(rss); + +error: + kvm_close(kd); + return NULL; +#endif +} +#endif + + static PyMethodDef test_methods[] = { {"pymem_api_misuse", pymem_api_misuse, METH_NOARGS}, {"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS}, @@ -698,6 +835,9 @@ static PyMethodDef test_methods[] = { {"test_pymem_setrawallocators", test_pymem_setrawallocators, METH_NOARGS}, {"test_pyobject_new", test_pyobject_new, METH_NOARGS}, {"test_pyobject_setallocators", test_pyobject_setallocators, METH_NOARGS}, +#if TARGET_OS_OSX || defined(__FreeBSD__) + {"get_process_memory_usage", get_process_memory_usage, METH_VARARGS}, +#endif // Tracemalloc tests {"tracemalloc_track", tracemalloc_track, METH_VARARGS}, diff --git a/Modules/_testcapi/modsupport.c b/Modules/_testcapi/modsupport.c new file mode 100644 index 00000000000000..151e4aa19afe11 --- /dev/null +++ b/Modules/_testcapi/modsupport.c @@ -0,0 +1,83 @@ +#include "parts.h" + + + +static PyObject * +pyabiinfo_check(PyObject *Py_UNUSED(module), PyObject *args) +{ + const char *modname; + unsigned long maj, min, flags, buildver, abiver; + + if (!PyArg_ParseTuple(args, "zkkkkk", + &modname, &maj, &min, &flags, &buildver, &abiver)) + { + return NULL; + } + PyABIInfo info = { + .abiinfo_major_version = (uint8_t)maj, + .abiinfo_minor_version = (uint8_t)min, + .flags = (uint16_t)flags, + .build_version = (uint32_t)buildver, + .abi_version = (uint32_t)abiver}; + if (PyABIInfo_Check(&info, modname) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +pyarg_parsearray(PyObject* self, PyObject* const* args, Py_ssize_t nargs) +{ + int a, b, c = 0; + if (!PyArg_ParseArray(args, nargs, "ii|i", &a, &b, &c)) { + return NULL; + } + return Py_BuildValue("iii", a, b, c); +} + +static PyObject * +pyarg_parsearrayandkeywords(PyObject* self, PyObject* const* args, + Py_ssize_t nargs, PyObject* kwnames) +{ + int a, b, c = 0; + const char *kwlist[] = {"a", "b", "c", NULL}; + if (!PyArg_ParseArrayAndKeywords(args, nargs, kwnames, + "ii|i", kwlist, + &a, &b, &c)) { + return NULL; + } + return Py_BuildValue("iii", a, b, c); +} + +static PyMethodDef TestMethods[] = { + {"pyabiinfo_check", pyabiinfo_check, METH_VARARGS}, + {"pyarg_parsearray", _PyCFunction_CAST(pyarg_parsearray), METH_FASTCALL}, + {"pyarg_parsearrayandkeywords", + _PyCFunction_CAST(pyarg_parsearrayandkeywords), + METH_FASTCALL | METH_KEYWORDS}, + {NULL}, +}; + +int +_PyTestCapi_Init_Modsupport(PyObject *m) +{ + if (PyModule_AddIntMacro(m, PyABIInfo_STABLE) < 0) { + return -1; + } + if (PyModule_AddIntMacro(m, PyABIInfo_INTERNAL) < 0) { + return -1; + } + if (PyModule_AddIntMacro(m, PyABIInfo_GIL) < 0) { + return -1; + } + if (PyModule_AddIntMacro(m, PyABIInfo_FREETHREADED) < 0) { + return -1; + } + if (PyModule_AddIntMacro(m, PyABIInfo_FREETHREADING_AGNOSTIC) < 0) { + return -1; + } + if (PyModule_AddFunctions(m, TestMethods) < 0) { + return -1; + } + return 0; +} diff --git a/Modules/_testcapi/module.c b/Modules/_testcapi/module.c new file mode 100644 index 00000000000000..b0b4a4f4c39d8f --- /dev/null +++ b/Modules/_testcapi/module.c @@ -0,0 +1,567 @@ +#include "parts.h" +#include "util.h" +#include <stdbool.h> + +// Test PyModule_* API + +/* unittest Cases that use these functions are in: + * Lib/test/test_capi/test_module.py + */ + +PyABIInfo_VAR(abi_info); + +static PyObject * +module_from_slots_empty(PyObject *self, PyObject *spec) +{ + PySlot slots[] = { + PySlot_END, + }; + return PyModule_FromSlotsAndSpec(slots, spec); +} + +static PyObject * +module_from_slots_minimal(PyObject *self, PyObject *spec) +{ + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_END, + }; + return PyModule_FromSlotsAndSpec(slots, spec); +} + +static PyObject * +module_from_slots_null(PyObject *self, PyObject *spec) +{ + return PyModule_FromSlotsAndSpec(NULL, spec); +} + +static PyObject * +module_from_slots_name(PyObject *self, PyObject *spec) +{ + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "currently ignored..."), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return PyModule_FromSlotsAndSpec(slots, spec); +} + +static PyObject * +module_from_slots_doc(PyObject *self, PyObject *spec) +{ + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_doc, "the docstring"), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return PyModule_FromSlotsAndSpec(slots, spec); +} + +static PyObject * +module_from_slots_size(PyObject *self, PyObject *spec) +{ + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_SIZE(Py_mod_state_size, (void*)123), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec); + if (!mod) { + return NULL; + } + return mod; +} + +static PyObject * +a_method(PyObject *self, PyObject *arg) +{ + return PyTuple_Pack(2, self, arg); +} + +static PyMethodDef a_methoddef_array[] = { + {"a_method", a_method, METH_O}, + {0}, +}; + +static PyObject * +module_from_slots_methods(PyObject *self, PyObject *spec) +{ + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_STATIC_DATA(Py_mod_methods, a_methoddef_array), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return PyModule_FromSlotsAndSpec(slots, spec); +} + +static int noop_traverse(PyObject *self, visitproc visit, void *arg) { + return 0; +} +static int noop_clear(PyObject *self) { return 0; } +static void noop_free(void *self) { } + +static PyObject * +module_from_slots_gc(PyObject *self, PyObject *spec) +{ + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_FUNC(Py_mod_state_traverse, noop_traverse), + PySlot_FUNC(Py_mod_state_clear, noop_clear), + PySlot_FUNC(Py_mod_state_free, noop_free), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec); + if (!mod) { + return NULL; + } + if (PyModule_Add(mod, "traverse", PyLong_FromVoidPtr(&noop_traverse)) < 0) { + Py_DECREF(mod); + return NULL; + } + if (PyModule_Add(mod, "clear", PyLong_FromVoidPtr(&noop_clear)) < 0) { + Py_DECREF(mod); + return NULL; + } + if (PyModule_Add(mod, "free", PyLong_FromVoidPtr(&noop_free)) < 0) { + Py_DECREF(mod); + return NULL; + } + return mod; +} + +static const char test_token; + +static PyObject * +module_from_slots_token(PyObject *self, PyObject *spec) +{ + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_token, (void*)&test_token), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec); + if (!mod) { + return NULL; + } + void *got_token; + if (PyModule_GetToken(mod, &got_token) < 0) { + Py_DECREF(mod); + return NULL; + } + assert(got_token == &test_token); + assert(PyModule_GetToken_DuringGC(mod, &got_token) >= 0); + assert(got_token == &test_token); + return mod; +} + +static int +simple_exec(PyObject *module) +{ + return PyModule_AddIntConstant(module, "a_number", 456); +} + +static PyObject * +module_from_slots_exec(PyObject *self, PyObject *spec) +{ + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_FUNC(Py_mod_exec, simple_exec), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec); + if (!mod) { + return NULL; + } + int res = PyObject_HasAttrStringWithError(mod, "a_number"); + if (res < 0) { + Py_DECREF(mod); + return NULL; + } + assert(res == 0); + if (PyModule_Exec(mod) < 0) { + Py_DECREF(mod); + return NULL; + } + return mod; +} + +static PyObject * +create_attr_from_spec(PyObject *spec, PyModuleDef *def) +{ + assert(!def); + return PyObject_GetAttrString(spec, "_gimme_this"); +} + +static PyObject * +module_from_slots_create(PyObject *self, PyObject *spec) +{ + const PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_FUNC(Py_mod_create, create_attr_from_spec), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return PyModule_FromSlotsAndSpec(slots, spec); +} + + +static int +slot_from_object(PyObject *obj) +{ + PyObject *slot_id_obj = PyObject_GetAttrString(obj, "_test_slot_id"); + if (slot_id_obj == NULL) { + return -1; + } + int slot_id = PyLong_AsInt(slot_id_obj); + if (PyErr_Occurred()) { + return -1; + } + return slot_id; +} + +static PyObject * +module_from_slots_repeat_slot(PyObject *self, PyObject *spec) +{ + int slot_id = slot_from_object(spec); + if (slot_id < 0) { + return NULL; + } + const PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_PTR_STATIC(slot_id, "anything"), + PySlot_PTR_STATIC(slot_id, "anything_else"), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return PyModule_FromSlotsAndSpec(slots, spec); +} + +static PyObject * +module_from_slots_null_slot(PyObject *self, PyObject *spec) +{ + int slot_id = slot_from_object(spec); + if (slot_id < 0) { + return NULL; + } + const PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_PTR_STATIC(slot_id, NULL), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return PyModule_FromSlotsAndSpec(slots, spec); +} + +static PyObject * +module_from_def_slot(PyObject *self, PyObject *spec) +{ + int slot_id = slot_from_object(spec); + if (slot_id < 0) { + return NULL; + } + PyModuleDef_Slot slots[] = { + {slot_id, "anything"}, + {Py_mod_abi, &abi_info}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0}, + }; + PyModuleDef def = { + PyModuleDef_HEAD_INIT, + .m_name = "currently ignored", + .m_slots = slots, + }; + // PyModuleDef is normally static; the real requirement is that it + // must outlive its module. + // Here, module creation fails, so it's fine on the stack. + PyObject *result = PyModule_FromDefAndSpec(&def, spec); + assert(result == NULL); + return result; +} + +static const char parrot_name[] = "test_capi/parrot"; +static const char parrot_doc[] = "created from redundant information"; +static PyModuleDef parrot_def = { + PyModuleDef_HEAD_INIT, + .m_name = (void*)parrot_name, + .m_doc = (void*)parrot_doc, + .m_size = 123, + .m_methods = a_methoddef_array, + .m_traverse = noop_traverse, + .m_clear = noop_clear, + .m_free = (void*)noop_free, + .m_slots = NULL /* set below */, +}; +static PyModuleDef_Slot parrot_slots[] = { + {Py_mod_abi, &abi_info}, + {Py_mod_name, (void*)parrot_name}, + {Py_mod_doc, (void*)parrot_doc}, + {Py_mod_state_size, (void*)123}, + {Py_mod_methods, a_methoddef_array}, + {Py_mod_state_traverse, noop_traverse}, + {Py_mod_state_clear, noop_clear}, + {Py_mod_state_free, (void*)noop_free}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_token, &parrot_def}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0}, +}; + +static PyObject * +module_from_def_slot_parrot(PyObject *self, PyObject *spec) +{ + parrot_def.m_slots = parrot_slots; + PyObject *module = PyModule_FromDefAndSpec(&parrot_def, spec); + if (!module || (PyModule_Exec(module) < 0)) { + return NULL; + } + Py_ssize_t size; + assert(PyModule_GetStateSize(module, &size) == 0); + assert(size == 123); + PyModuleDef *def = PyModule_GetDef(module); + assert(def == &parrot_def); + return module; +} + +static PyObject * +module_from_bad_abiinfo(PyObject *self, PyObject *spec) +{ + PyABIInfo bad_abi_info = { + 1, 0, + .abi_version=0x02080000, + }; + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_abi, &bad_abi_info), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return PyModule_FromSlotsAndSpec(slots, spec); +} + +static PyObject * +module_from_multiple_abiinfo(PyObject *self, PyObject *spec) +{ + PyABIInfo extra_abi_info = { + 1, 0, + .flags=PyABIInfo_STABLE | PyABIInfo_FREETHREADING_AGNOSTIC, + .abi_version=0x03040000, + }; + PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_abi, &extra_abi_info), + PySlot_DATA(Py_mod_abi, &extra_abi_info), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return PyModule_FromSlotsAndSpec(slots, spec); +} + +static int +another_exec(PyObject *module) +{ + /* Make sure simple_exec was called */ + assert(PyObject_HasAttrString(module, "a_number")); + + /* Add or negate a global called 'another_number' */ + PyObject *another_number; + if (PyObject_GetOptionalAttrString(module, "another_number", + &another_number) < 0) { + return -1; + } + if (!another_number) { + return PyModule_AddIntConstant(module, "another_number", 789); + } + PyObject *neg_number = PyNumber_Negative(another_number); + Py_DECREF(another_number); + if (!neg_number) { + return -1; + } + int result = PyObject_SetAttrString(module, "another_number", + neg_number); + Py_DECREF(neg_number); + return result; +} + +static PyObject * +module_from_def_multiple_exec(PyObject *self, PyObject *spec) +{ + static PyModuleDef_Slot slots[] = { + {Py_mod_abi, &abi_info}, + {Py_mod_exec, simple_exec}, + {Py_mod_exec, another_exec}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0}, + }; + static PyModuleDef def = { + PyModuleDef_HEAD_INIT, + .m_name = "currently ignored", + .m_slots = slots, + }; + return PyModule_FromDefAndSpec(&def, spec); +} + +static PyObject * +pymodule_exec(PyObject *self, PyObject *module) +{ + if (PyModule_Exec(module) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +pymodule_get_token(PyObject *self, PyObject *module) +{ + void *token; + int res = PyModule_GetToken(module, &token); + void *token_duringgc; + int res_duringgc = PyModule_GetToken_DuringGC(module, &token_duringgc); + assert(res == res_duringgc); + assert(token == token_duringgc); + if (res < 0) { + return NULL; + } + return PyLong_FromVoidPtr(token); +} + +static PyObject * +pymodule_get_def(PyObject *self, PyObject *module) +{ + PyModuleDef *def = PyModule_GetDef(module); + if (!def && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromVoidPtr(def); +} + +static PyObject * +pymodule_get_state_size(PyObject *self, PyObject *module) +{ + Py_ssize_t size; + if (PyModule_GetStateSize(module, &size) < 0) { + return NULL; + } + return PyLong_FromSsize_t(size); +} + +static PyObject * +module_from_null_def_slot(PyObject* Py_UNUSED(module), PyObject *args) +{ + long slot_number; + PyObject *spec; + if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) { + return NULL; + } + static PyModuleDef_Slot slots[] = { + {0, NULL}, + {0}, + }; + static PyModuleDef def = { + PyModuleDef_HEAD_INIT, + .m_name = "mymod", + .m_slots = slots, + }; + // hack: def is supposed to be constant. + // Don't do this at home; use PyModule_FromSlotsAndSpec throwaway + // definitions! + slots[0].slot = slot_number; + return PyModule_FromDefAndSpec(&def, spec); +} + +static PyObject * +module_from_def_nonstatic_nested(PyObject* Py_UNUSED(module), PyObject *spec) +{ + static PyModuleDef_Slot subsubslots[] = { + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0}, + }; + static PySlot subslots[] = { + PySlot_DATA(Py_mod_slots, subsubslots), + PySlot_END, + }; + static PyModuleDef_Slot slots[] = { + {Py_slot_subslots, subslots}, + {0}, + }; + static PyModuleDef def = { + PyModuleDef_HEAD_INIT, + .m_name = "mymod", + .m_slots = slots, + }; + return PyModule_FromDefAndSpec(&def, spec); +} + +static PyMethodDef test_methods[] = { + {"module_from_slots_empty", module_from_slots_empty, METH_O}, + {"module_from_slots_minimal", module_from_slots_minimal, METH_O}, + {"module_from_slots_null", module_from_slots_null, METH_O}, + {"module_from_slots_name", module_from_slots_name, METH_O}, + {"module_from_slots_doc", module_from_slots_doc, METH_O}, + {"module_from_slots_size", module_from_slots_size, METH_O}, + {"module_from_slots_methods", module_from_slots_methods, METH_O}, + {"module_from_slots_gc", module_from_slots_gc, METH_O}, + {"module_from_slots_token", module_from_slots_token, METH_O}, + {"module_from_slots_exec", module_from_slots_exec, METH_O}, + {"module_from_slots_create", module_from_slots_create, METH_O}, + {"module_from_slots_repeat_slot", module_from_slots_repeat_slot, METH_O}, + {"module_from_slots_null_slot", module_from_slots_null_slot, METH_O}, + {"module_from_def_multiple_exec", module_from_def_multiple_exec, METH_O}, + {"module_from_def_slot", module_from_def_slot, METH_O}, + {"module_from_def_slot_parrot", module_from_def_slot_parrot, METH_O}, + {"module_from_bad_abiinfo", module_from_bad_abiinfo, METH_O}, + {"module_from_multiple_abiinfo", module_from_multiple_abiinfo, METH_O}, + {"pymodule_get_token", pymodule_get_token, METH_O}, + {"pymodule_get_def", pymodule_get_def, METH_O}, + {"pymodule_get_state_size", pymodule_get_state_size, METH_O}, + {"pymodule_exec", pymodule_exec, METH_O}, + {"module_from_null_def_slot", module_from_null_def_slot, METH_VARARGS}, + {"module_from_def_nonstatic_nested", module_from_def_nonstatic_nested, METH_O}, + {NULL}, +}; + +int +_PyTestCapi_Init_Module(PyObject *m) +{ +#define ADD_INT_MACRO(C) if (PyModule_AddIntConstant(m, #C, C) < 0) return -1; + ADD_INT_MACRO(Py_mod_create); + ADD_INT_MACRO(Py_mod_exec); + ADD_INT_MACRO(Py_mod_multiple_interpreters); + ADD_INT_MACRO(Py_mod_gil); + ADD_INT_MACRO(Py_mod_name); + ADD_INT_MACRO(Py_mod_doc); + ADD_INT_MACRO(Py_mod_state_size); + ADD_INT_MACRO(Py_mod_methods); + ADD_INT_MACRO(Py_mod_state_traverse); + ADD_INT_MACRO(Py_mod_state_clear); + ADD_INT_MACRO(Py_mod_state_free); + ADD_INT_MACRO(Py_mod_token); +#undef ADD_INT_MACRO + if (PyModule_Add(m, "module_test_token", + PyLong_FromVoidPtr((void*)&test_token)) < 0) + { + return -1; + } + return PyModule_AddFunctions(m, test_methods); +} diff --git a/Modules/_testcapi/monitoring.c b/Modules/_testcapi/monitoring.c index e041943492d61f..3f99836c1ebd83 100644 --- a/Modules/_testcapi/monitoring.c +++ b/Modules/_testcapi/monitoring.c @@ -1,8 +1,6 @@ #include "parts.h" #include "util.h" -#include "monitoring.h" - #define Py_BUILD_CORE #include "internal/pycore_instruments.h" diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 798ef97c495aeb..09a548fd2e2448 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -138,6 +138,15 @@ pyobject_is_unique_temporary(PyObject *self, PyObject *obj) return PyLong_FromLong(result); } +static PyObject * +pyobject_is_unique_temporary_new_object(PyObject *self, PyObject *unused) +{ + PyObject *obj = PyList_New(0); + int result = PyUnstable_Object_IsUniqueReferencedTemporary(obj); + Py_DECREF(obj); + return PyLong_FromLong(result); +} + static int MyObject_dealloc_called = 0; static void @@ -192,6 +201,44 @@ test_py_try_inc_ref(PyObject *self, PyObject *unused) Py_RETURN_NONE; } +static PyObject * +test_py_set_immortal(PyObject *self, PyObject *unused) +{ + // the object is allocated on C stack as otherwise, + // it would trip the refleak checker when the object + // is made immortal and leak memory, for the same + // reason we cannot call PyObject_Init() on it. + PyObject object = {0}; +#ifdef Py_GIL_DISABLED + object.ob_tid = _Py_ThreadId(); + object.ob_gc_bits = 0; + object.ob_ref_local = 1; + object.ob_ref_shared = 0; +#else + object.ob_refcnt = 1; +#endif + object.ob_type = &PyBaseObject_Type; + + assert(!PyUnstable_IsImmortal(&object)); + int rc = PyUnstable_SetImmortal(&object); + assert(rc == 1); + assert(PyUnstable_IsImmortal(&object)); + Py_DECREF(&object); // should not dealloc + assert(PyUnstable_IsImmortal(&object)); + + // Check already immortal object + rc = PyUnstable_SetImmortal(&object); + assert(rc == 0); + + // Check unicode objects + PyObject *unicode = PyUnicode_FromString("test"); + assert(!PyUnstable_IsImmortal(unicode)); + rc = PyUnstable_SetImmortal(unicode); + assert(rc == 0); + assert(!PyUnstable_IsImmortal(unicode)); + Py_DECREF(unicode); + Py_RETURN_NONE; +} static PyObject * _test_incref(PyObject *ob) @@ -485,6 +532,54 @@ is_uniquely_referenced(PyObject *self, PyObject *op) } +static PyObject * +pyobject_dump(PyObject *self, PyObject *args) +{ + PyObject *op; + int release_gil = 0; + + if (!PyArg_ParseTuple(args, "O|i", &op, &release_gil)) { + return NULL; + } + NULLABLE(op); + + if (release_gil) { + Py_BEGIN_ALLOW_THREADS + PyObject_Dump(op); + Py_END_ALLOW_THREADS + + } + else { + PyObject_Dump(op); + } + Py_RETURN_NONE; +} + +static PyObject * +pysentinel_new(PyObject *self, PyObject *args) +{ + const char *name; + const char *module_name = NULL; + const char *repr = NULL; + if (!PyArg_ParseTuple(args, "s|ss", &name, &module_name, &repr)) { + return NULL; + } + return PySentinel_New(name, module_name, repr); +} + +static PyObject * +pysentinel_check(PyObject *self, PyObject *obj) +{ + return PyBool_FromLong(PySentinel_Check(obj)); +} + +static PyObject * +pysentinel_checkexact(PyObject *self, PyObject *obj) +{ + return PyBool_FromLong(PySentinel_CheckExact(obj)); +} + + static PyMethodDef test_methods[] = { {"call_pyobject_print", call_pyobject_print, METH_VARARGS}, {"pyobject_print_null", pyobject_print_null, METH_VARARGS}, @@ -493,7 +588,9 @@ static PyMethodDef test_methods[] = { {"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O}, {"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O}, {"pyobject_is_unique_temporary", pyobject_is_unique_temporary, METH_O}, + {"pyobject_is_unique_temporary_new_object", pyobject_is_unique_temporary_new_object, METH_NOARGS}, {"test_py_try_inc_ref", test_py_try_inc_ref, METH_NOARGS}, + {"test_py_set_immortal", test_py_set_immortal, METH_NOARGS}, {"test_xincref_doesnt_leak",test_xincref_doesnt_leak, METH_NOARGS}, {"test_incref_doesnt_leak", test_incref_doesnt_leak, METH_NOARGS}, {"test_xdecref_doesnt_leak",test_xdecref_doesnt_leak, METH_NOARGS}, @@ -511,6 +608,10 @@ static PyMethodDef test_methods[] = { {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, {"clear_managed_dict", clear_managed_dict, METH_O, NULL}, {"is_uniquely_referenced", is_uniquely_referenced, METH_O}, + {"pyobject_dump", pyobject_dump, METH_VARARGS}, + {"pysentinel_new", pysentinel_new, METH_VARARGS}, + {"pysentinel_check", pysentinel_check, METH_O}, + {"pysentinel_checkexact", pysentinel_checkexact, METH_O}, {NULL}, }; diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index af6400162daf2b..98b5dd47accde3 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -58,6 +58,7 @@ int _PyTestCapi_Init_Immortal(PyObject *module); int _PyTestCapi_Init_GC(PyObject *module); int _PyTestCapi_Init_Hash(PyObject *module); int _PyTestCapi_Init_Time(PyObject *module); +int _PyTestCapi_Init_Modsupport(PyObject *module); int _PyTestCapi_Init_Monitoring(PyObject *module); int _PyTestCapi_Init_Object(PyObject *module); int _PyTestCapi_Init_Config(PyObject *mod); @@ -65,5 +66,7 @@ int _PyTestCapi_Init_Import(PyObject *mod); int _PyTestCapi_Init_Frame(PyObject *mod); int _PyTestCapi_Init_Type(PyObject *mod); int _PyTestCapi_Init_Function(PyObject *mod); +int _PyTestCapi_Init_Module(PyObject *mod); +int _PyTestCapi_Init_Weakref(PyObject *mod); #endif // Py_TESTCAPI_PARTS_H diff --git a/Modules/_testcapi/tuple.c b/Modules/_testcapi/tuple.c index d9c02ba0ff04fe..5de1c494c0a8c0 100644 --- a/Modules/_testcapi/tuple.c +++ b/Modules/_testcapi/tuple.c @@ -104,12 +104,40 @@ _check_tuple_item_is_NULL(PyObject *Py_UNUSED(module), PyObject *args) } +static PyObject * +tuple_fromarray(PyObject* Py_UNUSED(module), PyObject *args) +{ + PyObject *src; + Py_ssize_t size = UNINITIALIZED_SIZE; + if (!PyArg_ParseTuple(args, "O|n", &src, &size)) { + return NULL; + } + if (src != Py_None && !PyTuple_Check(src)) { + PyErr_SetString(PyExc_TypeError, "expect a tuple"); + return NULL; + } + + PyObject **items; + if (src != Py_None) { + items = &PyTuple_GET_ITEM(src, 0); + if (size == UNINITIALIZED_SIZE) { + size = PyTuple_GET_SIZE(src); + } + } + else { + items = NULL; + } + return PyTuple_FromArray(items, size); +} + + static PyMethodDef test_methods[] = { {"tuple_get_size", tuple_get_size, METH_O}, {"tuple_get_item", tuple_get_item, METH_VARARGS}, {"tuple_set_item", tuple_set_item, METH_VARARGS}, {"_tuple_resize", _tuple_resize, METH_VARARGS}, {"_check_tuple_item_is_NULL", _check_tuple_item_is_NULL, METH_VARARGS}, + {"tuple_fromarray", tuple_fromarray, METH_VARARGS}, {NULL}, }; diff --git a/Modules/_testcapi/type.c b/Modules/_testcapi/type.c index 9bef58d1f83668..f566efa0ca15ae 100644 --- a/Modules/_testcapi/type.c +++ b/Modules/_testcapi/type.c @@ -110,9 +110,9 @@ test_get_statictype_slots(PyObject *self, PyObject *Py_UNUSED(ignored)) return NULL; } - void *over_value = PyType_GetSlot(&PyLong_Type, Py_bf_releasebuffer + 1); + void *over_value = PyType_GetSlot(&PyLong_Type, Py_mod_name + 1); if (over_value != NULL) { - PyErr_SetString(PyExc_AssertionError, "mismatch: max+1 of long"); + PyErr_SetString(PyExc_AssertionError, "mismatch: mod_name of long"); return NULL; } diff --git a/Modules/_testcapi/unicode.c b/Modules/_testcapi/unicode.c index 203282dd53dd0a..915c9230f66b52 100644 --- a/Modules/_testcapi/unicode.c +++ b/Modules/_testcapi/unicode.c @@ -301,16 +301,12 @@ writer_write_char(PyObject *self_raw, PyObject *args) return NULL; } - PyObject *str; - if (!PyArg_ParseTuple(args, "U", &str)) { + unsigned int ch; + if (!PyArg_ParseTuple(args, "I", &ch)) { return NULL; } - if (PyUnicode_GET_LENGTH(str) != 1) { - PyErr_SetString(PyExc_ValueError, "expect a single character"); - } - Py_UCS4 ch = PyUnicode_READ_CHAR(str, 0); - if (PyUnicodeWriter_WriteChar(self->writer, ch) < 0) { + if (PyUnicodeWriter_WriteChar(self->writer, (Py_UCS4)ch) < 0) { return NULL; } Py_RETURN_NONE; @@ -325,9 +321,9 @@ writer_write_utf8(PyObject *self_raw, PyObject *args) return NULL; } - char *str; - Py_ssize_t size; - if (!PyArg_ParseTuple(args, "yn", &str, &size)) { + const char *str; + Py_ssize_t bsize, size; + if (!PyArg_ParseTuple(args, "z#n", &str, &bsize, &size)) { return NULL; } @@ -346,9 +342,9 @@ writer_write_ascii(PyObject *self_raw, PyObject *args) return NULL; } - char *str; - Py_ssize_t size; - if (!PyArg_ParseTuple(args, "yn", &str, &size)) { + const char *str; + Py_ssize_t bsize, size; + if (!PyArg_ParseTuple(args, "z#n", &str, &bsize, &size)) { return NULL; } @@ -367,19 +363,23 @@ writer_write_widechar(PyObject *self_raw, PyObject *args) return NULL; } - PyObject *str; - if (!PyArg_ParseTuple(args, "U", &str)) { - return NULL; - } + const char *s; + Py_ssize_t bsize; + Py_ssize_t size = -100; - Py_ssize_t size; - wchar_t *wstr = PyUnicode_AsWideCharString(str, &size); - if (wstr == NULL) { + if (!PyArg_ParseTuple(args, "z#|n", &s, &bsize, &size)) { return NULL; } + if (size == -100) { + if (bsize % SIZEOF_WCHAR_T) { + PyErr_SetString(PyExc_AssertionError, + "invalid size in writer.write_widechar()"); + return NULL; + } + size = bsize / SIZEOF_WCHAR_T; + } - int res = PyUnicodeWriter_WriteWideChar(self->writer, wstr, size); - PyMem_Free(wstr); + int res = PyUnicodeWriter_WriteWideChar(self->writer, (const wchar_t *)s, size); if (res < 0) { return NULL; } @@ -395,21 +395,23 @@ writer_write_ucs4(PyObject *self_raw, PyObject *args) return NULL; } - PyObject *str; - Py_ssize_t size; - if (!PyArg_ParseTuple(args, "Un", &str, &size)) { - return NULL; - } - Py_ssize_t len = PyUnicode_GET_LENGTH(str); - size = Py_MIN(size, len); + const char *s; + Py_ssize_t bsize; + Py_ssize_t size = -100; - Py_UCS4 *ucs4 = PyUnicode_AsUCS4Copy(str); - if (ucs4 == NULL) { + if (!PyArg_ParseTuple(args, "z#|n", &s, &bsize, &size)) { return NULL; } + if (size == -100) { + if (bsize % sizeof(Py_UCS4)) { + PyErr_SetString(PyExc_AssertionError, + "invalid size in writer.write_ucs4()"); + return NULL; + } + size = bsize / sizeof(Py_UCS4); + } - int res = PyUnicodeWriter_WriteUCS4(self->writer, ucs4, size); - PyMem_Free(ucs4); + int res = PyUnicodeWriter_WriteUCS4(self->writer, (const Py_UCS4 *)s, size); if (res < 0) { return NULL; } @@ -418,18 +420,14 @@ writer_write_ucs4(PyObject *self_raw, PyObject *args) static PyObject* -writer_write_str(PyObject *self_raw, PyObject *args) +writer_write_str(PyObject *self_raw, PyObject *obj) { WriterObject *self = (WriterObject *)self_raw; if (writer_check(self) < 0) { return NULL; } - PyObject *obj; - if (!PyArg_ParseTuple(args, "O", &obj)) { - return NULL; - } - + NULLABLE(obj); if (PyUnicodeWriter_WriteStr(self->writer, obj) < 0) { return NULL; } @@ -438,18 +436,14 @@ writer_write_str(PyObject *self_raw, PyObject *args) static PyObject* -writer_write_repr(PyObject *self_raw, PyObject *args) +writer_write_repr(PyObject *self_raw, PyObject *obj) { WriterObject *self = (WriterObject *)self_raw; if (writer_check(self) < 0) { return NULL; } - PyObject *obj; - if (!PyArg_ParseTuple(args, "O", &obj)) { - return NULL; - } - + NULLABLE(obj); if (PyUnicodeWriter_WriteRepr(self->writer, obj) < 0) { return NULL; } @@ -467,9 +461,10 @@ writer_write_substring(PyObject *self_raw, PyObject *args) PyObject *str; Py_ssize_t start, end; - if (!PyArg_ParseTuple(args, "Unn", &str, &start, &end)) { + if (!PyArg_ParseTuple(args, "Onn", &str, &start, &end)) { return NULL; } + NULLABLE(str); if (PyUnicodeWriter_WriteSubstring(self->writer, str, start, end) < 0) { return NULL; @@ -487,10 +482,10 @@ writer_decodeutf8stateful(PyObject *self_raw, PyObject *args) } const char *str; - Py_ssize_t len; + Py_ssize_t bsize, len; const char *errors; int use_consumed = 0; - if (!PyArg_ParseTuple(args, "yny|i", &str, &len, &errors, &use_consumed)) { + if (!PyArg_ParseTuple(args, "z#nz#|p", &str, &bsize, &len, &errors, &bsize, &use_consumed)) { return NULL; } @@ -543,8 +538,8 @@ static PyMethodDef writer_methods[] = { {"write_ascii", _PyCFunction_CAST(writer_write_ascii), METH_VARARGS}, {"write_widechar", _PyCFunction_CAST(writer_write_widechar), METH_VARARGS}, {"write_ucs4", _PyCFunction_CAST(writer_write_ucs4), METH_VARARGS}, - {"write_str", _PyCFunction_CAST(writer_write_str), METH_VARARGS}, - {"write_repr", _PyCFunction_CAST(writer_write_repr), METH_VARARGS}, + {"write_str", _PyCFunction_CAST(writer_write_str), METH_O}, + {"write_repr", _PyCFunction_CAST(writer_write_repr), METH_O}, {"write_substring", _PyCFunction_CAST(writer_write_substring), METH_VARARGS}, {"decodeutf8stateful", _PyCFunction_CAST(writer_decodeutf8stateful), METH_VARARGS}, {"get_pointer", _PyCFunction_CAST(writer_get_pointer), METH_VARARGS}, diff --git a/Modules/_testcapi/vectorcall.c b/Modules/_testcapi/vectorcall.c index f89dcb6c4cf03c..a578c6da71bb75 100644 --- a/Modules/_testcapi/vectorcall.c +++ b/Modules/_testcapi/vectorcall.c @@ -163,6 +163,7 @@ class _testcapi.VectorCallClass "PyObject *" "&PyType_Type" /*[clinic end generated code: output=da39a3ee5e6b4b0d input=95c63c1a47f9a995]*/ /*[clinic input] +@permit_long_summary _testcapi.VectorCallClass.set_vectorcall type: object(subclass_of="&PyType_Type", type="PyTypeObject *") @@ -174,7 +175,7 @@ Set self's vectorcall function for `type` to one that returns "vectorcall" static PyObject * _testcapi_VectorCallClass_set_vectorcall_impl(PyObject *self, PyTypeObject *type) -/*[clinic end generated code: output=b37f0466f15da903 input=840de66182c7d71a]*/ +/*[clinic end generated code: output=b37f0466f15da903 input=170fefc7ee77fd36]*/ { if (!PyObject_TypeCheck(self, type)) { return PyErr_Format( diff --git a/Modules/_testcapi/watchers.c b/Modules/_testcapi/watchers.c index 6d061bb8d51040..71cdc54009017a 100644 --- a/Modules/_testcapi/watchers.c +++ b/Modules/_testcapi/watchers.c @@ -9,6 +9,7 @@ #include "pycore_function.h" // FUNC_MAX_WATCHERS #include "pycore_interp_structs.h" // CODE_MAX_WATCHERS #include "pycore_context.h" // CONTEXT_MAX_WATCHERS +#include "pycore_lock.h" // _PyOnceFlag /*[clinic input] module _testcapi @@ -18,6 +19,14 @@ module _testcapi // Test dict watching static PyObject *g_dict_watch_events = NULL; static int g_dict_watchers_installed = 0; +static _PyOnceFlag g_dict_watch_once = {0}; + +static int +_init_dict_watch_events(void *arg) +{ + g_dict_watch_events = PyList_New(0); + return g_dict_watch_events ? 0 : -1; +} static int dict_watch_callback(PyDict_WatchEvent event, @@ -106,13 +115,10 @@ add_dict_watcher(PyObject *self, PyObject *kind) if (watcher_id < 0) { return NULL; } - if (!g_dict_watchers_installed) { - assert(!g_dict_watch_events); - if (!(g_dict_watch_events = PyList_New(0))) { - return NULL; - } + if (_PyOnceFlag_CallOnce(&g_dict_watch_once, _init_dict_watch_events, NULL) < 0) { + return NULL; } - g_dict_watchers_installed++; + _Py_atomic_add_int(&g_dict_watchers_installed, 1); return PyLong_FromLong(watcher_id); } @@ -122,10 +128,8 @@ clear_dict_watcher(PyObject *self, PyObject *watcher_id) if (PyDict_ClearWatcher(PyLong_AsLong(watcher_id))) { return NULL; } - g_dict_watchers_installed--; - if (!g_dict_watchers_installed) { - assert(g_dict_watch_events); - Py_CLEAR(g_dict_watch_events); + if (_Py_atomic_add_int(&g_dict_watchers_installed, -1) == 1) { + PyList_Clear(g_dict_watch_events); } Py_RETURN_NONE; } @@ -164,7 +168,7 @@ _testcapi_unwatch_dict_impl(PyObject *module, int watcher_id, PyObject *dict) static PyObject * get_dict_watcher_events(PyObject *self, PyObject *Py_UNUSED(args)) { - if (!g_dict_watch_events) { + if (_Py_atomic_load_int(&g_dict_watchers_installed) <= 0) { PyErr_SetString(PyExc_RuntimeError, "no watchers active"); return NULL; } @@ -212,13 +216,32 @@ type_modified_callback_error(PyTypeObject *type) return -1; } +static int +type_modified_callback_name(PyTypeObject *type) +{ + assert(PyList_Check(g_type_modified_events)); + PyObject *name = PyUnicode_FromString(type->tp_name); + if (name == NULL) { + return -1; + } + if (PyList_Append(g_type_modified_events, name) < 0) { + Py_DECREF(name); + return -1; + } + Py_DECREF(name); + return 0; +} + static PyObject * add_type_watcher(PyObject *self, PyObject *kind) { int watcher_id; assert(PyLong_Check(kind)); long kind_l = PyLong_AsLong(kind); - if (kind_l == 2) { + if (kind_l == 3) { + watcher_id = PyType_AddWatcher(type_modified_callback_name); + } + else if (kind_l == 2) { watcher_id = PyType_AddWatcher(type_modified_callback_wrap); } else if (kind_l == 1) { @@ -364,7 +387,7 @@ add_code_watcher(PyObject *self, PyObject *which_watcher) watcher_id = PyCode_AddWatcher(error_code_event_handler); } else { - PyErr_Format(PyExc_ValueError, "invalid watcher %d", which_l); + PyErr_Format(PyExc_ValueError, "invalid watcher %ld", which_l); return NULL; } if (watcher_id < 0) { @@ -673,7 +696,7 @@ add_context_watcher(PyObject *self, PyObject *which_watcher) assert(PyLong_Check(which_watcher)); long which_l = PyLong_AsLong(which_watcher); if (which_l < 0 || which_l >= (long)Py_ARRAY_LENGTH(callbacks)) { - PyErr_Format(PyExc_ValueError, "invalid watcher %d", which_l); + PyErr_Format(PyExc_ValueError, "invalid watcher %ld", which_l); return NULL; } int watcher_id = PyContext_AddWatcher(callbacks[which_l]); diff --git a/Modules/_testcapi/weakref.c b/Modules/_testcapi/weakref.c new file mode 100644 index 00000000000000..7c3ad8565991b7 --- /dev/null +++ b/Modules/_testcapi/weakref.c @@ -0,0 +1,46 @@ +#include "parts.h" +#include "util.h" + + +static PyObject * +pyweakref_getref(PyObject *module, PyObject *ref) +{ + NULLABLE(ref); + PyObject *obj = UNINITIALIZED_PTR; + int rc = PyWeakref_GetRef(ref, &obj); + if (rc == -1 && PyErr_Occurred()) { + assert(obj == NULL); + return NULL; + } + if (obj == NULL) { + return Py_BuildValue("i", rc); + } + else { + assert(obj != UNINITIALIZED_PTR); + return Py_BuildValue("iN", rc, obj); + } +} + +static PyObject * +pyweakref_isdead(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + int rc = PyWeakref_IsDead(obj); + if (rc == -1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromLong(rc); +} + + +static PyMethodDef test_methods[] = { + {"pyweakref_getref", pyweakref_getref, METH_O}, + {"pyweakref_isdead", pyweakref_isdead, METH_O}, + {NULL}, +}; + +int +_PyTestCapi_Init_Weakref(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index d0c0b45c20cb36..9c90d1fc36f398 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -116,8 +116,8 @@ test_sizeof_c_types(PyObject *self, PyObject *Py_UNUSED(ignored)) do { \ if (EXPECTED != sizeof(TYPE)) { \ PyErr_Format(get_testerror(self), \ - "sizeof(%s) = %u instead of %u", \ - #TYPE, sizeof(TYPE), EXPECTED); \ + "sizeof(%s) = %zu instead of %u", \ + #TYPE, sizeof(TYPE), (unsigned)(EXPECTED)); \ return (PyObject*)NULL; \ } \ } while (0) @@ -226,6 +226,18 @@ pycompilestring(PyObject* self, PyObject *obj) { return Py_CompileString(the_string, "<string>", Py_file_input); } +static PyObject* +pycompilestringexflags(PyObject *self, PyObject *args) { + const char *the_string, *filename; + int start, flags; + if (!PyArg_ParseTuple(args, "ysii", &the_string, &filename, &start, &flags)) { + return NULL; + } + PyCompilerFlags cf = _PyCompilerFlags_INIT; + cf.cf_flags = flags; + return Py_CompileStringExFlags(the_string, filename, start, &cf, -1); +} + static PyObject* test_lazy_hash_inheritance(PyObject* self, PyObject *Py_UNUSED(ignored)) { @@ -1583,9 +1595,9 @@ getitem_with_error(PyObject *self, PyObject *args) static PyObject * raise_SIGINT_then_send_None(PyObject *self, PyObject *args) { - PyGenObject *gen; + PyObject *gen; - if (!PyArg_ParseTuple(args, "O!", &PyGen_Type, &gen)) + if (!PyArg_ParseTuple(args, "O", &gen)) return NULL; /* This is used in a test to check what happens if a signal arrives just @@ -1599,7 +1611,7 @@ raise_SIGINT_then_send_None(PyObject *self, PyObject *args) because we check for signals before every bytecode operation. */ raise(SIGINT); - return PyObject_CallMethod((PyObject *)gen, "send", "O", Py_None); + return PyObject_CallMethod(gen, "send", "O", Py_None); } @@ -2206,9 +2218,8 @@ test_macros(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { - // Ignore PyWeakref_GetObject() deprecation, we test it on purpose - _Py_COMP_DIAG_PUSH - _Py_COMP_DIAG_IGNORE_DEPR_DECLS + // Get the function (removed in 3.15) from the stable ABI. + PyAPI_FUNC(PyObject *) PyWeakref_GetObject(PyObject *); // Create a new heap type, create an instance of this type, and delete the // type. This object supports weak references. @@ -2249,19 +2260,12 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) ref = PyWeakref_GetObject(weakref); // borrowed ref assert(ref == obj); - // test PyWeakref_GET_OBJECT(), reference is alive - ref = PyWeakref_GET_OBJECT(weakref); // borrowed ref - assert(ref == obj); - // delete the referenced object: clear the weakref assert(Py_REFCNT(obj) == 1); Py_DECREF(obj); assert(PyWeakref_IsDead(weakref)); - // test PyWeakref_GET_OBJECT(), reference is dead - assert(PyWeakref_GET_OBJECT(weakref) == Py_None); - // test PyWeakref_GetRef(), reference is dead ref = UNINITIALIZED_PTR; assert(PyWeakref_GetRef(weakref, &ref) == 0); @@ -2312,13 +2316,12 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) Py_DECREF(weakref); Py_RETURN_NONE; - - _Py_COMP_DIAG_POP } struct simpletracer_data { int create_count; int destroy_count; + int tracker_removed; void* addresses[10]; }; @@ -2326,10 +2329,18 @@ static int _simpletracer(PyObject *obj, PyRefTracerEvent event, void* data) { struct simpletracer_data* the_data = (struct simpletracer_data*)data; assert(the_data->create_count + the_data->destroy_count < (int)Py_ARRAY_LENGTH(the_data->addresses)); the_data->addresses[the_data->create_count + the_data->destroy_count] = obj; - if (event == PyRefTracer_CREATE) { - the_data->create_count++; - } else { - the_data->destroy_count++; + switch (event) { + case PyRefTracer_CREATE: + the_data->create_count++; + break; + case PyRefTracer_DESTROY: + the_data->destroy_count++; + break; + case PyRefTracer_TRACKER_REMOVED: + the_data->tracker_removed++; + break; + default: + return -1; } return 0; } @@ -2393,6 +2404,10 @@ test_reftracer(PyObject *ob, PyObject *Py_UNUSED(ignored)) PyErr_SetString(PyExc_ValueError, "The object destruction was not correctly traced"); goto failed; } + if (tracer_data.tracker_removed != 1) { + PyErr_SetString(PyExc_ValueError, "The tracker removal was not correctly traced"); + goto failed; + } PyRefTracer_SetTracer(current_tracer, current_data); Py_RETURN_NONE; failed: @@ -2533,11 +2548,15 @@ code_offset_to_line(PyObject* self, PyObject* const* args, Py_ssize_t nargsf) static int _reftrace_printer(PyObject *obj, PyRefTracerEvent event, void *counter_data) { - if (event == PyRefTracer_CREATE) { - printf("CREATE %s\n", Py_TYPE(obj)->tp_name); - } - else { // PyRefTracer_DESTROY - printf("DESTROY %s\n", Py_TYPE(obj)->tp_name); + switch (event) { + case PyRefTracer_CREATE: + printf("CREATE %s\n", Py_TYPE(obj)->tp_name); + break; + case PyRefTracer_DESTROY: + printf("DESTROY %s\n", Py_TYPE(obj)->tp_name); + break; + case PyRefTracer_TRACKER_REMOVED: + return 0; } return 0; } @@ -2555,6 +2574,402 @@ toggle_reftrace_printer(PyObject *ob, PyObject *arg) Py_RETURN_NONE; } + +typedef struct { + PyObject_HEAD +} ManagedWeakrefNoGCObject; + +static void +ManagedWeakrefNoGC_dealloc(PyObject *self) +{ + PyObject_ClearWeakRefs(self); + PyTypeObject *tp = Py_TYPE(self); + tp->tp_free(self); + Py_DECREF(tp); +} + +static PyType_Slot ManagedWeakrefNoGC_slots[] = { + {Py_tp_dealloc, ManagedWeakrefNoGC_dealloc}, + {0, 0} +}; + +static PyType_Spec ManagedWeakrefNoGC_spec = { + .name = "_testcapi.ManagedWeakrefNoGCType", + .basicsize = sizeof(ManagedWeakrefNoGCObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_MANAGED_WEAKREF), + .slots = ManagedWeakrefNoGC_slots, +}; + +static PyObject * +create_managed_weakref_nogc_type(PyObject *self, PyObject *Py_UNUSED(args)) +{ + return PyType_FromSpec(&ManagedWeakrefNoGC_spec); +} + +static void +test_interp_guards_common(void) +{ + PyInterpreterGuard *guard = PyInterpreterGuard_FromCurrent(); + assert(guard != NULL); + + PyInterpreterGuard *guard_2 = PyInterpreterGuard_FromCurrent(); + assert(guard_2 != NULL); + + // We can close the guards in any order + PyInterpreterGuard_Close(guard_2); + PyInterpreterGuard_Close(guard); +} + +static PyObject * +test_interpreter_guards(PyObject *self, PyObject *unused) +{ + // Test the main interpreter + test_interp_guards_common(); + + // Test a (legacy) subinterpreter + PyThreadState *save_tstate = PyThreadState_Swap(NULL); + PyThreadState *interp_tstate = Py_NewInterpreter(); + // Note: For these tests, we don't bother adding error paths, because + // there's no realistic case where interpreter creation would fail here. + assert(interp_tstate != NULL); + test_interp_guards_common(); + Py_EndInterpreter(interp_tstate); + + // Test an isolated subinterpreter + PyInterpreterConfig config = { + .gil = PyInterpreterConfig_OWN_GIL, + .check_multi_interp_extensions = 1 + }; + + PyThreadState *isolated_interp_tstate; + PyStatus status = Py_NewInterpreterFromConfig(&isolated_interp_tstate, &config); + assert(!PyStatus_Exception(status)); + + test_interp_guards_common(); + Py_EndInterpreter(isolated_interp_tstate); + PyThreadState_Swap(save_tstate); + Py_RETURN_NONE; +} + +static PyObject * +test_thread_state_ensure_nested(PyObject *self, PyObject *unused) +{ + PyInterpreterGuard *guard = PyInterpreterGuard_FromCurrent(); + assert(guard != NULL); + + PyThreadState *save_tstate = PyThreadState_Swap(NULL); + assert(PyGILState_GetThisThreadState() == save_tstate); + PyThreadStateToken *tokens[10]; + + for (int i = 0; i < 10; ++i) { + // Test reactivation of the detached tstate. + tokens[i] = PyThreadState_Ensure(guard); + assert(tokens[i] != NULL); + + // No new thread state should've been created. + assert(PyThreadState_Get() == save_tstate); + PyThreadState_Release(tokens[i]); + } + + assert(PyThreadState_GetUnchecked() == NULL); + + // Similarly, test ensuring with deep nesting and *then* releasing. + // If the (detached) gilstate matches the interpreter, then it shouldn't + // create a new thread state. + for (int i = 0; i < 10; ++i) { + tokens[i] = PyThreadState_Ensure(guard); + assert(tokens[i] != NULL); + assert(PyThreadState_Get() == save_tstate); + } + + for (int i = 9; i >= 0; --i) { + assert(PyThreadState_Get() == save_tstate); + PyThreadState_Release(tokens[i]); + } + + assert(PyThreadState_GetUnchecked() == NULL); + PyInterpreterGuard_Close(guard); + PyThreadState_Swap(save_tstate); + Py_RETURN_NONE; +} + +static PyObject * +test_thread_state_ensure_crossinterp(PyObject *self, PyObject *unused) +{ + PyInterpreterGuard *guard = PyInterpreterGuard_FromCurrent(); + PyThreadState *save_tstate = PyThreadState_Swap(NULL); + PyThreadState *interp_tstate = Py_NewInterpreter(); + assert(interp_tstate != NULL); + + /* This should create a new thread state for the calling interpreter, *not* + reactivate the old one. In a real-world scenario, this would arise in + something like this: + + def some_func(): + import something + # This re-enters the main interpreter, but we + # shouldn't have access to prior thread-locals. + something.call_something() + + interp = interpreters.create() + interp.exec(some_func) + */ + PyThreadStateToken *token = PyThreadState_Ensure(guard); + assert(token != NULL); + + PyThreadState *ensured_tstate = PyThreadState_Get(); + assert(ensured_tstate != save_tstate); + assert(PyGILState_GetThisThreadState() == ensured_tstate); + + // Now though, we should reactivate the thread state + PyThreadStateToken *other_token = PyThreadState_Ensure(guard); + assert(other_token != NULL); + assert(PyThreadState_Get() == ensured_tstate); + + PyThreadState_Release(other_token); + + // Ensure that we're restoring the prior thread state + PyThreadState_Release(token); + assert(PyThreadState_Get() == interp_tstate); + assert(PyGILState_GetThisThreadState() == interp_tstate); + + PyThreadState_Swap(interp_tstate); + Py_EndInterpreter(interp_tstate); + + PyInterpreterGuard_Close(guard); + PyThreadState_Swap(save_tstate); + Py_RETURN_NONE; +} + +static PyObject * +test_interp_view_after_shutdown(PyObject *self, PyObject *unused) +{ + PyThreadState *save_tstate = PyThreadState_Swap(NULL); + PyThreadState *interp_tstate = Py_NewInterpreter(); + if (interp_tstate == NULL) { + PyThreadState_Swap(save_tstate); + return PyErr_NoMemory(); + } + + PyInterpreterView *view = PyInterpreterView_FromCurrent(); + if (view == NULL) { + Py_EndInterpreter(interp_tstate); + PyThreadState_Swap(save_tstate); + return PyErr_NoMemory(); + } + + // As a sanity check, ensure that the view actually works + PyInterpreterGuard *guard = PyInterpreterGuard_FromView(view); + PyInterpreterGuard_Close(guard); + + // Now, destroy the interpreter and try to acquire a lock from a view. + // It should fail. + Py_EndInterpreter(interp_tstate); + guard = PyInterpreterGuard_FromView(view); + assert(guard == NULL); + + PyThreadState_Swap(save_tstate); + Py_RETURN_NONE; +} + +static PyObject * +test_thread_state_ensure_view(PyObject *self, PyObject *unused) +{ + // For simplicity's sake, we assume that functions won't fail due to being + // out of memory. + PyThreadState *save_tstate = PyThreadState_Swap(NULL); + PyThreadState *interp_tstate = Py_NewInterpreter(); + assert(interp_tstate != NULL); + assert(PyInterpreterState_Get() == PyThreadState_GetInterpreter(interp_tstate)); + + PyInterpreterView *main_view = PyInterpreterView_FromMain(); + assert(main_view != NULL); + + PyInterpreterView *view = PyInterpreterView_FromCurrent(); + assert(view != NULL); + + Py_BEGIN_ALLOW_THREADS; + PyThreadStateToken *token = PyThreadState_EnsureFromView(view); + assert(token != NULL); + assert(PyThreadState_Get() == interp_tstate); + + // Test a nested call + PyThreadStateToken *token2 = PyThreadState_EnsureFromView(view); + assert(PyThreadState_Get() == interp_tstate); + + // We're in a new interpreter now. PyThreadState_EnsureFromView() should + // now create a new thread state. + PyThreadStateToken *main_token = PyThreadState_EnsureFromView(main_view); + assert(main_token == (PyThreadStateToken*)interp_tstate); // The old thread state + assert(PyInterpreterState_Get() == PyInterpreterState_Main()); + + // Going back to the old interpreter should create a new thread state again. + PyThreadStateToken *token3 = PyThreadState_EnsureFromView(view); + assert(PyInterpreterState_Get() == PyThreadState_GetInterpreter(interp_tstate)); + assert(PyThreadState_Get() != interp_tstate); + PyThreadState_Release(token3); + PyThreadState_Release(main_token); + + // We're back in the original interpreter. PyThreadState_EnsureFromView() should + // no longer create a new thread state. + assert(PyThreadState_Get() == interp_tstate); + PyThreadStateToken *token4 = PyThreadState_EnsureFromView(view); + assert(PyThreadState_Get() == interp_tstate); + PyThreadState_Release(token4); + PyThreadState_Release(token2); + PyThreadState_Release(token); + assert(PyThreadState_GetUnchecked() == NULL); + Py_END_ALLOW_THREADS; + + assert(PyThreadState_Get() == interp_tstate); + PyInterpreterView_Close(view); + PyInterpreterView_Close(main_view); + Py_EndInterpreter(interp_tstate); + PyThreadState_Swap(save_tstate); + + Py_RETURN_NONE; +} + +static PyObject * +test_thread_state_ensure_detachment(PyObject *self, PyObject *unused) +{ + PyThreadState *before = PyThreadState_Get(); + assert(before != NULL); + + PyInterpreterGuard *guard = PyInterpreterGuard_FromCurrent(); + assert(guard != NULL); + + PyThreadStateToken *token = PyThreadState_Ensure(guard); + assert(token != NULL); + /* Ensure took the fast path; tstate is unchanged. */ + assert(PyThreadState_Get() == before); + + PyThreadState_Release(token); + + PyThreadState *after = PyThreadState_GetUnchecked(); + assert(after != NULL); + + PyInterpreterGuard_Close(guard); + Py_RETURN_NONE; +} + +static PyObject * +test_thread_state_ensure_detached_gilstate(PyObject *self, PyObject *unused) +{ + PyInterpreterGuard *guard = PyInterpreterGuard_FromCurrent(); + PyThreadState *gilstate = PyGILState_GetThisThreadState(); + + PyThreadStateToken *token1 = PyThreadState_Ensure(guard); + assert(PyThreadState_Get() == gilstate); + + Py_BEGIN_ALLOW_THREADS + assert(PyThreadState_GetUnchecked() == NULL); + PyThreadStateToken *token2 = PyThreadState_Ensure(guard); + assert(PyThreadState_Get() == gilstate); + PyThreadState_Release(token2); + assert(PyThreadState_GetUnchecked() == NULL); + Py_END_ALLOW_THREADS + assert(PyThreadState_Get() == gilstate); + + PyThreadState_Release(token1); + assert(PyThreadState_Get() == gilstate); + + PyInterpreterGuard_Close(guard); + + Py_RETURN_NONE; +} + +/* A capsule destructor that calls Ensure/Release while the tstate is being + * cleared by PyThreadState_Release. */ +static void +tstate_ensure_capsule_destructor(PyObject *capsule) +{ + assert(capsule != NULL); + PyInterpreterGuard *guard = PyCapsule_GetPointer(capsule, "x"); + PyThreadStateToken *token = PyThreadState_Ensure(guard); + assert(token != NULL); + PyThreadState_Release(token); +} + +static PyObject * +test_thread_state_release_with_destructor(PyObject *self, PyObject *unused) +{ + PyInterpreterGuard *guard = PyInterpreterGuard_FromCurrent(); + assert(guard != NULL); + + // We need to use a fresh thread state in order to control the lifetime of + // it. If we used the current thread state, it wouldn't be cleared until + // the end of the program, which is after the guard has been closed. + PyThreadState *fresh_tstate = PyThreadState_New(PyInterpreterState_Get()); + assert(fresh_tstate != NULL); + + PyThreadState *save_tstate = PyThreadState_Swap(fresh_tstate); + assert(save_tstate != NULL); + + /* Triggers fresh tstate path */ + PyThreadStateToken *token = PyThreadState_Ensure(guard); + assert(token != NULL); + + /* Stash a capsule whose destructor will run during PyThreadState_Clear. */ + PyObject *capsule = PyCapsule_New(guard, "x", tstate_ensure_capsule_destructor); + assert(capsule != NULL); + + /* We need to put it somewhere it gets cleaned up at PyThreadState_Clear. + * tstate->dict is cleared during PyThreadState_Clear. */ + PyObject *dict = PyThreadState_GetDict(); + assert(dict != NULL); + int res = PyDict_SetItemString(dict, "key", capsule); + assert(res == 0); + Py_DECREF(capsule); + + PyThreadState_Release(token); + + // This will trigger the destructor + PyThreadState_Clear(fresh_tstate); + PyThreadState_DeleteCurrent(); + + PyInterpreterGuard_Close(guard); + PyThreadState_Swap(save_tstate); + + Py_RETURN_NONE; +} + + +static PyObject* +test_soft_deprecated_macros(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) +{ + // Test soft-deprecated macros + Py_ALIGNED(64) char buf[4]; + #ifdef __GNUC__ + // Py_ALIGNED must compile everywhere, but only does something + // on "supported" compilers, i.e. GCC + Py_BUILD_ASSERT(__extension__ __alignof__(buf) >= 64); + #endif + assert(strcmp(PY_FORMAT_SIZE_T, "z") == 0); + Py_BUILD_ASSERT(Py_LL(123) == 123LL); + Py_BUILD_ASSERT(sizeof(Py_LL(123)) == sizeof(long long)); + Py_BUILD_ASSERT(sizeof(Py_ULL(123)) == sizeof(unsigned long long)); + Py_BUILD_ASSERT(sizeof(PY_LONG_LONG) == sizeof(long long)); + Py_BUILD_ASSERT(sizeof(PY_INT32_T) == sizeof(int32_t)); + Py_BUILD_ASSERT(sizeof(PY_UINT32_T) == sizeof(uint32_t)); + Py_BUILD_ASSERT(sizeof(PY_INT64_T) == sizeof(int64_t)); + Py_BUILD_ASSERT(sizeof(PY_UINT64_T) == sizeof(uint64_t)); + Py_BUILD_ASSERT(PY_LLONG_MIN == LLONG_MIN); + Py_BUILD_ASSERT(PY_LLONG_MAX == LLONG_MAX); + Py_BUILD_ASSERT(PY_ULLONG_MAX == ULLONG_MAX); + Py_BUILD_ASSERT(PY_SIZE_MAX == SIZE_MAX); + Py_BUILD_ASSERT(PY_LLONG_MIN == LLONG_MIN); + Py_MEMCPY(buf, "abc", 4); + assert(strcmp(buf, "abc") == 0); + Py_BUILD_ASSERT(Py_UNICODE_SIZE == sizeof(wchar_t)); + #ifdef Py_UNICODE_WIDE + Py_BUILD_ASSERT(sizeof(wchar_t) >= 4); + #else + Py_BUILD_ASSERT(sizeof(wchar_t) < 4); + #endif + Py_RETURN_NONE; +} + static PyMethodDef TestMethods[] = { {"set_errno", set_errno, METH_VARARGS}, {"test_config", test_config, METH_NOARGS}, @@ -2619,6 +3034,7 @@ static PyMethodDef TestMethods[] = { {"return_result_with_error", return_result_with_error, METH_NOARGS}, {"getitem_with_error", getitem_with_error, METH_VARARGS}, {"Py_CompileString", pycompilestring, METH_O}, + {"Py_CompileStringExFlags", pycompilestringexflags, METH_VARARGS}, {"raise_SIGINT_then_send_None", raise_SIGINT_then_send_None, METH_VARARGS}, {"stack_pointer", stack_pointer, METH_NOARGS}, #ifdef W_STOPCODE @@ -2649,6 +3065,17 @@ static PyMethodDef TestMethods[] = { {"test_atexit", test_atexit, METH_NOARGS}, {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, {"toggle_reftrace_printer", toggle_reftrace_printer, METH_O}, + {"create_managed_weakref_nogc_type", + create_managed_weakref_nogc_type, METH_NOARGS}, + {"test_soft_deprecated_macros", test_soft_deprecated_macros, METH_NOARGS}, + {"test_interpreter_guards", test_interpreter_guards, METH_NOARGS}, + {"test_thread_state_ensure_nested", test_thread_state_ensure_nested, METH_NOARGS}, + {"test_thread_state_ensure_crossinterp", test_thread_state_ensure_crossinterp, METH_NOARGS}, + {"test_interp_view_after_shutdown", test_interp_view_after_shutdown, METH_NOARGS}, + {"test_thread_state_ensure_view", test_thread_state_ensure_view, METH_NOARGS}, + {"test_thread_state_ensure_detachment", test_thread_state_ensure_detachment, METH_NOARGS}, + {"test_thread_state_ensure_detached_gilstate", test_thread_state_ensure_detached_gilstate, METH_NOARGS}, + {"test_thread_state_release_with_destructor", test_thread_state_release_with_destructor, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; @@ -3189,9 +3616,8 @@ typedef struct { } ManagedDictObject; int ManagedDict_traverse(PyObject *self, visitproc visit, void *arg) { - PyObject_VisitManagedDict(self, visit, arg); Py_VISIT(Py_TYPE(self)); - return 0; + return PyObject_VisitManagedDict(self, visit, arg); } int ManagedDict_clear(PyObject *self) { @@ -3226,75 +3652,56 @@ create_managed_dict_type(void) return PyType_FromSpec(&ManagedDict_spec); } -static struct PyModuleDef _testcapimodule = { - PyModuleDef_HEAD_INIT, - .m_name = "_testcapi", - .m_size = sizeof(testcapistate_t), - .m_methods = TestMethods, -}; - -/* Per PEP 489, this module will not be converted to multi-phase initialization - */ - -PyMODINIT_FUNC -PyInit__testcapi(void) +static int +_testcapi_exec(PyObject *m) { - PyObject *m; - - m = PyModule_Create(&_testcapimodule); - if (m == NULL) - return NULL; -#ifdef Py_GIL_DISABLED - PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); -#endif - Py_SET_TYPE(&_HashInheritanceTester_Type, &PyType_Type); if (PyType_Ready(&_HashInheritanceTester_Type) < 0) { - return NULL; + return -1; } if (PyType_Ready(&matmulType) < 0) - return NULL; + return -1; Py_INCREF(&matmulType); PyModule_AddObject(m, "matmulType", (PyObject *)&matmulType); if (PyType_Ready(&ipowType) < 0) { - return NULL; + return -1; } Py_INCREF(&ipowType); PyModule_AddObject(m, "ipowType", (PyObject *)&ipowType); if (PyType_Ready(&awaitType) < 0) - return NULL; + return -1; Py_INCREF(&awaitType); PyModule_AddObject(m, "awaitType", (PyObject *)&awaitType); MyList_Type.tp_base = &PyList_Type; if (PyType_Ready(&MyList_Type) < 0) - return NULL; + return -1; Py_INCREF(&MyList_Type); PyModule_AddObject(m, "MyList", (PyObject *)&MyList_Type); if (PyType_Ready(&GenericAlias_Type) < 0) - return NULL; + return -1; Py_INCREF(&GenericAlias_Type); PyModule_AddObject(m, "GenericAlias", (PyObject *)&GenericAlias_Type); if (PyType_Ready(&Generic_Type) < 0) - return NULL; + return -1; Py_INCREF(&Generic_Type); PyModule_AddObject(m, "Generic", (PyObject *)&Generic_Type); if (PyType_Ready(&MethInstance_Type) < 0) - return NULL; + return -1; Py_INCREF(&MethInstance_Type); PyModule_AddObject(m, "MethInstance", (PyObject *)&MethInstance_Type); if (PyType_Ready(&MethClass_Type) < 0) - return NULL; + return -1; Py_INCREF(&MethClass_Type); PyModule_AddObject(m, "MethClass", (PyObject *)&MethClass_Type); if (PyType_Ready(&MethStatic_Type) < 0) - return NULL; + return -1; Py_INCREF(&MethStatic_Type); PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type); @@ -3336,14 +3743,24 @@ PyInit__testcapi(void) PyModule_AddObject(m, "INT64_MAX", PyLong_FromInt64(INT64_MAX)); PyModule_AddObject(m, "UINT64_MAX", PyLong_FromUInt64(UINT64_MAX)); +#ifdef HAVE_PPOLL + if (PyModule_AddObjectRef(m, "HAVE_PPOLL", Py_True) < 0) { + return -1; + } +#endif + + if (PyModule_AddIntMacro(m, _Py_STACK_GROWS_DOWN)) { + return -1; + } + if (PyModule_AddIntMacro(m, Py_single_input)) { - return NULL; + return -1; } if (PyModule_AddIntMacro(m, Py_file_input)) { - return NULL; + return -1; } if (PyModule_AddIntMacro(m, Py_eval_input)) { - return NULL; + return -1; } testcapistate_t *state = get_testcapi_state(m); @@ -3351,142 +3768,174 @@ PyInit__testcapi(void) PyModule_AddObject(m, "error", state->error); if (PyType_Ready(&ContainerNoGC_type) < 0) { - return NULL; + return -1; } Py_INCREF(&ContainerNoGC_type); if (PyModule_AddObject(m, "ContainerNoGC", (PyObject *) &ContainerNoGC_type) < 0) - return NULL; + return -1; PyObject *manual_heap_type = create_manual_heap_type(); if (manual_heap_type == NULL) { - return NULL; + return -1; } if (PyModule_Add(m, "ManualHeapType", manual_heap_type) < 0) { - return NULL; + return -1; } PyObject *managed_dict_type = create_managed_dict_type(); if (managed_dict_type == NULL) { - return NULL; + return -1; } if (PyModule_Add(m, "ManagedDictType", managed_dict_type) < 0) { - return NULL; + return -1; } /* Include tests from the _testcapi/ directory */ if (_PyTestCapi_Init_Vectorcall(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Heaptype(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Abstract(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Bytes(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Unicode(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_GetArgs(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_DateTime(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Docstring(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Mem(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Watchers(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Long(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Float(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Complex(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Numbers(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Dict(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Set(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_List(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Tuple(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Structmember(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Exceptions(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Code(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Buffer(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_File(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Codec(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Immortal(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_GC(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_PyAtomic(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Run(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Hash(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Time(m) < 0) { - return NULL; + return -1; + } + if (_PyTestCapi_Init_Modsupport(m) < 0) { + return -1; } if (_PyTestCapi_Init_Monitoring(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Object(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Config(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Import(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Frame(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Type(m) < 0) { - return NULL; + return -1; } if (_PyTestCapi_Init_Function(m) < 0) { - return NULL; + return -1; + } + if (_PyTestCapi_Init_Module(m) < 0) { + return -1; } + if (_PyTestCapi_Init_Weakref(m) < 0) { + return -1; + } + + return 0; +} + +PyABIInfo_VAR(abi_info); + +static PyModuleDef_Slot _testcapi_slots[] = { + {Py_mod_abi, &abi_info}, + {Py_mod_exec, _testcapi_exec}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {0, NULL}, +}; + +static struct PyModuleDef _testcapimodule = { + PyModuleDef_HEAD_INIT, + .m_name = "_testcapi", + .m_size = sizeof(testcapistate_t), + .m_methods = TestMethods, + .m_slots = _testcapi_slots +}; - PyState_AddModule(m, &_testcapimodule); - return m; +PyMODINIT_FUNC +PyInit__testcapi(void) +{ + return PyModuleDef_Init(&_testcapimodule); } diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index 3e903b6d87d89f..66a375589ba38e 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -63,7 +63,7 @@ pack_arguments_2pos_varpos(PyObject *a, PyObject *b, PyObject * const *args, Py_ssize_t args_length) /*[clinic end generated code: output=267032f41bd039cc input=86ee3064b7853e86]*/ { - PyObject *tuple = _PyTuple_FromArray(args, args_length); + PyObject *tuple = PyTuple_FromArray(args, args_length); if (tuple == NULL) { return NULL; } @@ -334,14 +334,14 @@ int_converter a: int = 12 b: int(accept={int}) = 34 - c: int(accept={str}) = 45 + c: int(accept={str}) = '-' / [clinic start generated code]*/ static PyObject * int_converter_impl(PyObject *module, int a, int b, int c) -/*[clinic end generated code: output=8e56b59be7d0c306 input=a1dbc6344853db7a]*/ +/*[clinic end generated code: output=8e56b59be7d0c306 input=9a306d4dc907e339]*/ { RETURN_PACKED_ARGS(3, PyLong_FromLong, long, a, b, c); } @@ -443,16 +443,21 @@ py_ssize_t_converter a: Py_ssize_t = 12 b: Py_ssize_t(accept={int}) = 34 c: Py_ssize_t(accept={int, NoneType}) = 56 + d: Py_ssize_t(accept={int}, allow_negative=False) = 78 + e: Py_ssize_t(accept={int, NoneType}, allow_negative=False) = 90 + f: Py_ssize_t(accept={int}, allow_negative=False) = -12 + g: Py_ssize_t(accept={int, NoneType}, py_default="-34", allow_negative=False) = -34 / [clinic start generated code]*/ static PyObject * py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b, - Py_ssize_t c) -/*[clinic end generated code: output=ce252143e0ed0372 input=76d0f342e9317a1f]*/ + Py_ssize_t c, Py_ssize_t d, Py_ssize_t e, + Py_ssize_t f, Py_ssize_t g) +/*[clinic end generated code: output=ecf8e1a4a9abc95e input=7b7fa954780c1cb0]*/ { - RETURN_PACKED_ARGS(3, PyLong_FromSsize_t, Py_ssize_t, a, b, c); + RETURN_PACKED_ARGS(7, PyLong_FromSsize_t, Py_ssize_t, a, b, c, d, e, f, g); } @@ -658,16 +663,16 @@ str_converter_encoding_impl(PyObject *module, char *a, char *b, char *c, static PyObject * bytes_from_buffer(Py_buffer *buf) { - PyObject *bytes_obj = PyBytes_FromStringAndSize(NULL, buf->len); - if (!bytes_obj) { + PyBytesWriter *writer = PyBytesWriter_Create(buf->len); + if (writer == NULL) { return NULL; } - void *bytes_obj_buf = ((PyBytesObject *)bytes_obj)->ob_sval; - if (PyBuffer_ToContiguous(bytes_obj_buf, buf, buf->len, 'C') < 0) { - Py_DECREF(bytes_obj); + void *data = PyBytesWriter_GetData(writer); + if (PyBuffer_ToContiguous(data, buf, buf->len, 'C') < 0) { + PyBytesWriter_Discard(writer); return NULL; } - return bytes_obj; + return PyBytesWriter_Finish(writer); } /*[clinic input] @@ -1169,7 +1174,7 @@ varpos_array_impl(PyObject *module, PyObject * const *args, Py_ssize_t args_length) /*[clinic end generated code: output=a25f42f39c9b13ad input=97b8bdcf87e019c7]*/ { - return _PyTuple_FromArray(args, args_length); + return PyTuple_FromArray(args, args_length); } @@ -1360,6 +1365,7 @@ clone_f2_impl(PyObject *module, const char *path) class custom_t_converter(CConverter): type = 'custom_t' converter = 'custom_converter' + c_init_default = "<placeholder>" # overridden in pre_render(() def pre_render(self): self.c_default = f'''{{ @@ -1367,7 +1373,7 @@ class custom_t_converter(CConverter): }}''' [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=b2fb801e99a06bf6]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=78fe84e5ecc0481b]*/ /*[clinic input] @@ -1605,7 +1611,7 @@ _testclinic_TestClass_varpos_array_no_fastcall_impl(PyTypeObject *type, Py_ssize_t args_length) /*[clinic end generated code: output=27c9da663e942617 input=9ba5ae1f1eb58777]*/ { - return _PyTuple_FromArray(args, args_length); + return PyTuple_FromArray(args, args_length); } @@ -2303,6 +2309,88 @@ depr_multi_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c, #undef _SAVED_PY_VERSION +/*[clinic input] +output pop +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e7c7c42daced52b0]*/ + + +/*[clinic input] +output push +destination kwarg new file '{dirname}/clinic/_testclinic_kwds.c.h' +output everything kwarg +output docstring_prototype suppress +output parser_prototype suppress +output impl_definition block +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=02965b54b3981cc4]*/ + +#include "clinic/_testclinic_kwds.c.h" + + +/*[clinic input] +lone_kwds + **kwds: dict +[clinic start generated code]*/ + +static PyObject * +lone_kwds_impl(PyObject *module, PyObject *kwds) +/*[clinic end generated code: output=572549c687a0432e input=6ef338b913ecae17]*/ +{ + return pack_arguments_newref(1, kwds); +} + + +/*[clinic input] +kwds_with_pos_only + a: object + b: object + / + **kwds: dict +[clinic start generated code]*/ + +static PyObject * +kwds_with_pos_only_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *kwds) +/*[clinic end generated code: output=573096d3a7efcce5 input=da081a5d9ae8878a]*/ +{ + return pack_arguments_newref(3, a, b, kwds); +} + + +/*[clinic input] +kwds_with_stararg + *args: tuple + **kwds: dict +[clinic start generated code]*/ + +static PyObject * +kwds_with_stararg_impl(PyObject *module, PyObject *args, PyObject *kwds) +/*[clinic end generated code: output=d4b0064626a25208 input=1be404572d685859]*/ +{ + return pack_arguments_newref(2, args, kwds); +} + + +/*[clinic input] +kwds_with_pos_only_and_stararg + a: object + b: object + / + *args: tuple + **kwds: dict +[clinic start generated code]*/ + +static PyObject * +kwds_with_pos_only_and_stararg_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *args, + PyObject *kwds) +/*[clinic end generated code: output=af7df7640c792246 input=2fe330c7981f0829]*/ +{ + return pack_arguments_newref(4, a, b, args, kwds); +} + + /*[clinic input] output pop [clinic start generated code]*/ @@ -2399,6 +2487,12 @@ static PyMethodDef tester_methods[] = { DEPR_KWD_NOINLINE_METHODDEF DEPR_KWD_MULTI_METHODDEF DEPR_MULTI_METHODDEF + + LONE_KWDS_METHODDEF + KWDS_WITH_POS_ONLY_METHODDEF + KWDS_WITH_STARARG_METHODDEF + KWDS_WITH_POS_ONLY_AND_STARARG_METHODDEF + {NULL, NULL} }; diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 243c7346576fc6..d0d1f1f1bc8e53 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -10,6 +10,7 @@ #undef NDEBUG #include "Python.h" +#include <string.h> #include "pycore_backoff.h" // JUMP_BACKWARD_INITIAL_VALUE #include "pycore_bitutils.h" // _Py_bswap32() #include "pycore_bytesobject.h" // _PyBytes_Find() @@ -28,12 +29,14 @@ #include "pycore_initconfig.h" // _Py_GetConfigsAsDict() #include "pycore_instruction_sequence.h" // _PyInstructionSequence_New() #include "pycore_interpframe.h" // _PyFrame_GetFunction() +#include "pycore_jit.h" // _PyJIT_AddressInJitCode() #include "pycore_object.h" // _PyObject_IsFreed() #include "pycore_optimizer.h" // _Py_Executor_DependsOn #include "pycore_pathconfig.h" // _PyPathConfig_ClearGlobal() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pylifecycle.h" // _PyInterpreterConfig_InitFromDict() #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_runtime_structs.h" // _PY_NSMALLPOSINTS #include "pycore_unicodeobject.h" // _PyUnicode_TransformDecimalAndSpaceToASCII() #include "clinic/_testinternalcapi.c.h" @@ -41,10 +44,66 @@ // Include test definitions from _testinternalcapi/ #include "_testinternalcapi/parts.h" +#if defined(HAVE_DLADDR) && !defined(__wasi__) +# include <dlfcn.h> +#endif +#if defined(HAVE_EXECINFO_H) +# include <execinfo.h> +#endif +#ifdef MS_WINDOWS +# include <windows.h> +# include <intrin.h> +# include <winnt.h> +# include <wchar.h> +#endif #define MODULE_NAME "_testinternalcapi" +static const uintptr_t min_frame_pointer_addr = 0x1000; +#define MAX_UNWIND_FRAMES 200 + +#ifdef __s390x__ +// Linux's s390 "Stack Frame Layout" table documents that z/Architecture +// backchain frames start with the backchain at offset 0 and store "saved r14 +// of caller function" at offset 112. The same document's register table +// identifies r14 as the return-address register, so this backchain unwinder +// reads the return address from fp + 112. +// https://www.kernel.org/doc/html/v5.3/s390/debugging390.html#stack-frame-layout +// +// This is only for Linux s390x backchain frames. The s390x ELF ABI does not +// generally mandate where RA and FP are saved, or whether they are saved at all. +// https://sourceware.org/binutils/docs/sframe-spec.html#s390x +# define S390X_FRAME_RETURN_ADDRESS_OFFSET 112 +#endif + +// The generic manual unwinder treats the frame pointer as a two-word record: +// fp[0] is the previous frame pointer and fp[1] is the return address. That is +// not true for every architecture, even with frame pointers enabled, so these +// offsets describe the actual slots used by each supported frame layout. +#if defined(__arm__) && !defined(__thumb__) && !defined(__clang__) +// GCC ARM mode keeps the caller's fp one word below fp and the saved LR at +// fp[0], so the return address is not in the generic fp[1] slot. +# define FRAME_POINTER_NEXT_OFFSET (-1) +# define FRAME_POINTER_RETURN_OFFSET 0 +#elif defined(__s390x__) +// s390x backchain frames keep the previous frame pointer at fp[0], but save the +// return-address register in the ABI register save area rather than fp[1]. +# define FRAME_POINTER_NEXT_OFFSET 0 +# define FRAME_POINTER_RETURN_OFFSET \ + (S390X_FRAME_RETURN_ADDRESS_OFFSET / (Py_ssize_t)sizeof(uintptr_t)) +#elif defined(__powerpc64__) || defined(__ppc64__) +// ppc64le puts the return address at fp[2]; it saves the Condition Register +// in fp[1]. See: +// https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#STACK +# define FRAME_POINTER_NEXT_OFFSET 0 +# define FRAME_POINTER_RETURN_OFFSET 2 +#else +# define FRAME_POINTER_NEXT_OFFSET 0 +# define FRAME_POINTER_RETURN_OFFSET 1 +#endif + + static PyObject * _get_current_module(void) { @@ -125,6 +184,415 @@ get_c_recursion_remaining(PyObject *self, PyObject *Py_UNUSED(args)) return PyLong_FromLong(remaining); } +static PyObject* +get_stack_pointer(PyObject *self, PyObject *Py_UNUSED(args)) +{ + uintptr_t here_addr = _Py_get_machine_stack_pointer(); + return PyLong_FromSize_t(here_addr); +} + +static PyObject* +get_stack_margin(PyObject *self, PyObject *Py_UNUSED(args)) +{ + return PyLong_FromSize_t(_PyOS_STACK_MARGIN_BYTES); +} + +#ifdef MS_WINDOWS +static const char * +classify_address(uintptr_t addr, int jit_enabled, PyInterpreterState *interp) +{ + HMODULE module = NULL; + if (GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS + | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCWSTR)addr, + &module)) { + wchar_t path[MAX_PATH]; + DWORD len = GetModuleFileNameW(module, path, Py_ARRAY_LENGTH(path)); + if (len > 0 && len < Py_ARRAY_LENGTH(path)) { + const wchar_t *base = wcsrchr(path, L'\\'); + base = base ? base + 1 : path; + if (_wcsnicmp(base, L"python", 6) == 0) { + return "python"; + } + return "other"; + } + /* Module resolved but path unavailable: treat as non-JIT. */ + return "other"; + } +#ifdef _Py_JIT + if (jit_enabled && _PyJIT_AddressInJitCode(interp, addr)) { + return "jit"; + } +#endif + return "other"; +} +#elif defined(HAVE_DLADDR) && !defined(__wasi__) +static const char * +classify_address(uintptr_t addr, int jit_enabled, PyInterpreterState *interp) +{ + Dl_info info; + if (dladdr((void *)addr, &info) != 0 + && info.dli_fname != NULL + && info.dli_fname[0] != '\0') { + const char *base = strrchr(info.dli_fname, '/'); + base = base ? base + 1 : info.dli_fname; + if (strncmp(base, "python", 6) == 0) { + return "python"; + } +#ifdef __CYGWIN__ + // Match Cygwin "cygpython3.16.dll" + if (strncmp(base, "cygpython", 9) == 0) { + return "python"; + } +#else + // Match "libpython3.15.so.1.0" + if (strncmp(base, "libpython", 9) == 0) { + return "python"; + } +#endif + return "other"; + } +#ifdef _Py_JIT + if (jit_enabled && _PyJIT_AddressInJitCode(interp, addr)) { + return "jit"; + } +#endif + return "other"; +} +#else +static const char * +classify_address(uintptr_t addr, int jit_enabled, PyInterpreterState *interp) +{ +#ifdef _Py_JIT + if (jit_enabled && _PyJIT_AddressInJitCode(interp, addr)) { + return "jit"; + } +#endif + return "other"; +} +#endif + +static PyObject * +classify_stack_addresses(PyObject *self, PyObject *args) +{ + PyObject *seq = NULL; + int jit_enabled = 0; + + if (!PyArg_ParseTuple(args, "O|p:classify_stack_addresses", + &seq, &jit_enabled)) { + return NULL; + } + PyObject *fast = PySequence_Fast(seq, "addresses must be iterable"); + if (fast == NULL) { + return NULL; + } + Py_ssize_t n = PySequence_Fast_GET_SIZE(fast); + PyObject *labels = PyList_New(n); + if (labels == NULL) { + Py_DECREF(fast); + return NULL; + } + PyThreadState *tstate = _PyThreadState_GET(); + PyInterpreterState *interp = tstate ? tstate->interp : NULL; + PyObject **items = PySequence_Fast_ITEMS(fast); + for (Py_ssize_t i = 0; i < n; i++) { + unsigned long long value = PyLong_AsUnsignedLongLong(items[i]); + if (PyErr_Occurred()) { + Py_DECREF(labels); + Py_DECREF(fast); + return NULL; + } + const char *label = classify_address((uintptr_t)value, jit_enabled, interp); + PyObject *label_obj = PyUnicode_FromString(label); + if (label_obj == NULL) { + Py_DECREF(labels); + Py_DECREF(fast); + return NULL; + } + PyList_SET_ITEM(labels, i, label_obj); + } + Py_DECREF(fast); + return labels; +} + +static PyObject * +get_jit_code_ranges(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyObject *ranges = PyList_New(0); + if (ranges == NULL) { + return NULL; + } +#ifdef _Py_JIT + PyThreadState *tstate = _PyThreadState_GET(); + PyInterpreterState *interp = tstate ? tstate->interp : NULL; + if (interp == NULL) { + return ranges; + } + for (size_t i = 0; i < interp->executor_count; i++) { + _PyExecutorObject *exec = interp->executor_ptrs[i]; + if (exec->jit_code == NULL || exec->jit_size == 0) { + continue; + } + uintptr_t start = (uintptr_t)exec->jit_code; + uintptr_t end = start + exec->jit_size; + PyObject *start_obj = PyLong_FromUnsignedLongLong(start); + PyObject *end_obj = PyLong_FromUnsignedLongLong(end); + if (start_obj == NULL || end_obj == NULL) { + Py_XDECREF(start_obj); + Py_XDECREF(end_obj); + Py_DECREF(ranges); + return NULL; + } + PyObject *pair = PyTuple_New(2); + if (pair == NULL) { + Py_DECREF(start_obj); + Py_DECREF(end_obj); + Py_DECREF(ranges); + return NULL; + } + PyTuple_SET_ITEM(pair, 0, start_obj); + PyTuple_SET_ITEM(pair, 1, end_obj); + if (PyList_Append(ranges, pair) < 0) { + Py_DECREF(pair); + Py_DECREF(ranges); + return NULL; + } + Py_DECREF(pair); + } +#endif + return ranges; +} + +static PyObject * +get_jit_backend(PyObject *self, PyObject *Py_UNUSED(args)) +{ +#ifdef _Py_JIT + return PyUnicode_FromString("jit"); +#elif defined(_Py_TIER2) + return PyUnicode_FromString("interpreter"); +#else + Py_RETURN_NONE; +#endif +} + +static int +stack_address_is_valid(uintptr_t addr, uintptr_t stack_min, uintptr_t stack_max) +{ + if (addr < min_frame_pointer_addr) { + return 0; + } + if (stack_min != 0 && (addr < stack_min || addr >= stack_max)) { + return 0; + } + return 1; +} + +static int +frame_pointer_slot_is_valid(uintptr_t *frame_pointer, Py_ssize_t offset, + uintptr_t stack_min, uintptr_t stack_max) +{ + uintptr_t fp_addr = (uintptr_t)frame_pointer; + uintptr_t slot_addr; + uintptr_t delta = (uintptr_t)Py_ABS(offset) * sizeof(uintptr_t); + if (offset < 0) { + if (fp_addr < delta) { + return 0; + } + slot_addr = fp_addr - delta; + } + else { + if (fp_addr > UINTPTR_MAX - delta) { + return 0; + } + slot_addr = fp_addr + delta; + } + if (!stack_address_is_valid(slot_addr, stack_min, stack_max)) { + return 0; + } + if (stack_max != 0) { + if (slot_addr > UINTPTR_MAX - sizeof(uintptr_t)) { + return 0; + } + if (slot_addr + sizeof(uintptr_t) > stack_max) { + return 0; + } + } + return 1; +} + +static int +next_frame_pointer_is_valid(uintptr_t *frame_pointer, uintptr_t *next_fp, + uintptr_t stack_min, uintptr_t stack_max) +{ + uintptr_t fp_addr = (uintptr_t)frame_pointer; + uintptr_t next_addr = (uintptr_t)next_fp; + if (!stack_address_is_valid(next_addr, stack_min, stack_max)) { + return 0; + } + if ((next_addr % sizeof(uintptr_t)) != 0) { + return 0; + } +#if _Py_STACK_GROWS_DOWN + return next_addr > fp_addr; +#else + return next_addr < fp_addr; +#endif +} + +static PyObject * +manual_unwind_from_fp(uintptr_t *frame_pointer) +{ + uintptr_t stack_min = 0; + uintptr_t stack_max = 0; + +#ifdef __s390x__ + Py_BUILD_ASSERT(S390X_FRAME_RETURN_ADDRESS_OFFSET % sizeof(uintptr_t) == 0); +#endif + + if (frame_pointer == NULL) { + return PyList_New(0); + } + + PyThreadState *tstate = _PyThreadState_GET(); + if (tstate != NULL) { + _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate; +#if _Py_STACK_GROWS_DOWN + stack_min = tstate_impl->c_stack_hard_limit; + stack_max = tstate_impl->c_stack_top; +#else + stack_min = tstate_impl->c_stack_top; + stack_max = tstate_impl->c_stack_hard_limit; +#endif + } + + PyObject *result = PyList_New(0); + if (result == NULL) { + return NULL; + } + + Py_ssize_t depth = 0; + while (frame_pointer != NULL) { + uintptr_t fp_addr = (uintptr_t)frame_pointer; + if ((fp_addr % sizeof(uintptr_t)) != 0) { + break; + } + if (depth >= MAX_UNWIND_FRAMES) { + Py_DECREF(result); + PyErr_Format( + PyExc_RuntimeError, + "manual frame pointer unwind returned more than %d frames", + MAX_UNWIND_FRAMES); + return NULL; + } + if (!stack_address_is_valid(fp_addr, stack_min, stack_max)) { + break; + } + if (!frame_pointer_slot_is_valid(frame_pointer, + FRAME_POINTER_NEXT_OFFSET, + stack_min, stack_max)) { + break; + } + if (!frame_pointer_slot_is_valid(frame_pointer, + FRAME_POINTER_RETURN_OFFSET, + stack_min, stack_max)) { + break; + } + uintptr_t *next_fp = (uintptr_t *)frame_pointer[FRAME_POINTER_NEXT_OFFSET]; + uintptr_t return_addr = frame_pointer[FRAME_POINTER_RETURN_OFFSET]; + + PyObject *addr_obj = PyLong_FromUnsignedLongLong(return_addr); + if (addr_obj == NULL) { + Py_DECREF(result); + return NULL; + } + if (PyList_Append(result, addr_obj) < 0) { + Py_DECREF(addr_obj); + Py_DECREF(result); + return NULL; + } + Py_DECREF(addr_obj); + depth++; + + if (!next_frame_pointer_is_valid(frame_pointer, next_fp, + stack_min, stack_max)) { + break; + } + frame_pointer = next_fp; + } + + return result; +} + +#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) +static PyObject * +gnu_backtrace_unwind(PyObject *self, PyObject *Py_UNUSED(args)) +{ + void *addresses[MAX_UNWIND_FRAMES + 1]; + int frame_count = backtrace(addresses, (int)Py_ARRAY_LENGTH(addresses)); + if (frame_count < 0) { + PyErr_SetString(PyExc_RuntimeError, "backtrace() failed"); + return NULL; + } + if (frame_count > MAX_UNWIND_FRAMES) { + PyErr_Format( + PyExc_RuntimeError, + "backtrace() returned more than %d frames", + MAX_UNWIND_FRAMES); + return NULL; + } + + PyObject *result = PyList_New(frame_count); + if (result == NULL) { + return NULL; + } + for (int i = 0; i < frame_count; i++) { + PyObject *addr_obj = PyLong_FromUnsignedLongLong((uintptr_t)addresses[i]); + if (addr_obj == NULL) { + Py_DECREF(result); + return NULL; + } + PyList_SET_ITEM(result, i, addr_obj); + } + return result; +} +#else +static PyObject * +gnu_backtrace_unwind(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyErr_SetString(PyExc_RuntimeError, + "gnu_backtrace_unwind is not supported on this platform"); + return NULL; +} +#endif + +#if defined(__GNUC__) || defined(__clang__) +static PyObject * +manual_frame_pointer_unwind(PyObject *self, PyObject *args) +{ + uintptr_t *frame_pointer = (uintptr_t *)__builtin_frame_address(0); + return manual_unwind_from_fp(frame_pointer); +} +#elif defined(MS_WINDOWS) && defined(_M_ARM64) +static PyObject * +manual_frame_pointer_unwind(PyObject *self, PyObject *args) +{ + CONTEXT ctx; + uintptr_t *frame_pointer = NULL; + + RtlCaptureContext(&ctx); + frame_pointer = (uintptr_t *)ctx.Fp; + return manual_unwind_from_fp(frame_pointer); +} +#else +static PyObject * +manual_frame_pointer_unwind(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyErr_SetString(PyExc_RuntimeError, + "manual_frame_pointer_unwind is not supported on this platform"); + return NULL; +} +#endif static PyObject* test_bswap(PyObject *self, PyObject *Py_UNUSED(args)) @@ -132,14 +600,14 @@ test_bswap(PyObject *self, PyObject *Py_UNUSED(args)) uint16_t u16 = _Py_bswap16(UINT16_C(0x3412)); if (u16 != UINT16_C(0x1234)) { PyErr_Format(PyExc_AssertionError, - "_Py_bswap16(0x3412) returns %u", u16); + "_Py_bswap16(0x3412) returns %d", u16); return NULL; } uint32_t u32 = _Py_bswap32(UINT32_C(0x78563412)); if (u32 != UINT32_C(0x12345678)) { PyErr_Format(PyExc_AssertionError, - "_Py_bswap32(0x78563412) returns %lu", u32); + "_Py_bswap32(0x78563412) returns %u", u32); return NULL; } @@ -418,7 +886,7 @@ test_edit_cost(PyObject *self, PyObject *Py_UNUSED(args)) static int check_bytes_find(const char *haystack0, const char *needle0, - int offset, Py_ssize_t expected) + Py_ssize_t offset, Py_ssize_t expected) { Py_ssize_t len_haystack = strlen(haystack0); Py_ssize_t len_needle = strlen(needle0); @@ -644,7 +1112,7 @@ static PyObject * set_eval_frame_default(PyObject *self, PyObject *Py_UNUSED(args)) { module_state *state = get_module_state(self); - _PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), _PyEval_EvalFrameDefault); + _PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), NULL); Py_CLEAR(state->record_list); Py_RETURN_NONE; } @@ -680,6 +1148,82 @@ set_eval_frame_record(PyObject *self, PyObject *list) Py_RETURN_NONE; } +// Defined in interpreter.c +extern PyObject* +Test_EvalFrame(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag); +extern int Test_EvalFrame_Resumes, Test_EvalFrame_Loads; + +static PyObject * +get_eval_frame_stats(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyObject *res = PyDict_New(); + if (res == NULL) { + return NULL; + } + PyObject *resumes = PyLong_FromLong(Test_EvalFrame_Resumes); + if (resumes == NULL || PyDict_SetItemString(res, "resumes", resumes) < 0) { + Py_XDECREF(resumes); + Py_DECREF(res); + return NULL; + } + Py_DECREF(resumes); + PyObject *loads = PyLong_FromLong(Test_EvalFrame_Loads); + if (loads == NULL || PyDict_SetItemString(res, "loads", loads) < 0) { + Py_XDECREF(loads); + Py_DECREF(res); + return NULL; + } + Py_DECREF(loads); + Test_EvalFrame_Resumes = Test_EvalFrame_Loads = 0; + return res; +} + +static PyObject * +record_eval_interp(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc) +{ + if (PyStackRef_FunctionCheck(f->f_funcobj)) { + PyFunctionObject *func = _PyFrame_GetFunction(f); + PyObject *module = _get_current_module(); + assert(module != NULL); + module_state *state = get_module_state(module); + Py_DECREF(module); + int res = PyList_Append(state->record_list, func->func_name); + if (res < 0) { + return NULL; + } + } + + return Test_EvalFrame(tstate, f, exc); +} + +static PyObject * +set_eval_frame_interp(PyObject *self, PyObject *args) +{ + if (PyTuple_GET_SIZE(args) == 1) { + module_state *state = get_module_state(self); + PyObject *list = PyTuple_GET_ITEM(args, 0); + if (!PyList_Check(list)) { + PyErr_SetString(PyExc_TypeError, "argument must be a list"); + return NULL; + } + Py_XSETREF(state->record_list, Py_NewRef(list)); + _PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), record_eval_interp); + _PyInterpreterState_SetEvalFrameAllowSpecialization(_PyInterpreterState_GET(), 1); + } else { + _PyInterpreterState_SetEvalFrameFunc(_PyInterpreterState_GET(), Test_EvalFrame); + _PyInterpreterState_SetEvalFrameAllowSpecialization(_PyInterpreterState_GET(), 1); + } + + Py_RETURN_NONE; +} + +static PyObject * +is_specialization_enabled(PyObject *self, PyObject *Py_UNUSED(args)) +{ + return PyBool_FromLong( + _PyInterpreterState_IsSpecializationEnabled(_PyInterpreterState_GET())); +} + /*[clinic input] _testinternalcapi.compiler_cleandoc -> object @@ -720,13 +1264,17 @@ _testinternalcapi.compiler_codegen -> object compile_mode: int = 0 Apply compiler code generation to an AST. + +Return (instruction_sequence, metadata). metadata maps "argcount", +"posonlyargcount", "kwonlyargcount" to ints and "consts" to the list of +constants in LOAD_CONST index order (for use with optimize_cfg). [clinic start generated code]*/ static PyObject * _testinternalcapi_compiler_codegen_impl(PyObject *module, PyObject *ast, PyObject *filename, int optimize, int compile_mode) -/*[clinic end generated code: output=40a68f6e13951cc8 input=a0e00784f1517cd7]*/ +/*[clinic end generated code: output=40a68f6e13951cc8 input=e0c65e5c80efe30e]*/ { PyCompilerFlags *flags = NULL; return _PyCompile_CodeGen(ast, filename, flags, optimize, compile_mode); @@ -742,12 +1290,15 @@ _testinternalcapi.optimize_cfg -> object nlocals: int Apply compiler optimizations to an instruction list. + +consts must be a list aligned with LOAD_CONST opargs (the "consts" entry +from the metadata dict returned by compiler_codegen for the same unit). [clinic start generated code]*/ static PyObject * _testinternalcapi_optimize_cfg_impl(PyObject *module, PyObject *instructions, PyObject *consts, int nlocals) -/*[clinic end generated code: output=57c53c3a3dfd1df0 input=6a96d1926d58d7e5]*/ +/*[clinic end generated code: output=57c53c3a3dfd1df0 input=905c3d935e063b27]*/ { return _PyCompile_OptimizeCfg(instructions, consts, nlocals); } @@ -836,7 +1387,7 @@ get_interp_settings(PyObject *self, PyObject *args) } else { PyErr_Format(PyExc_NotImplementedError, - "%zd", interpid); + "%d", interpid); return NULL; } assert(interp != NULL); @@ -888,16 +1439,22 @@ write_perf_map_entry(PyObject *self, PyObject *args) { PyObject *code_addr_v; const void *code_addr; - unsigned int code_size; + PyObject *code_size_s; + size_t code_size; const char *entry_name; - if (!PyArg_ParseTuple(args, "OIs", &code_addr_v, &code_size, &entry_name)) + if (!PyArg_ParseTuple(args, "OOs", &code_addr_v, &code_size_s, &entry_name)) return NULL; code_addr = PyLong_AsVoidPtr(code_addr_v); if (code_addr == NULL) { return NULL; } + code_size = PyLong_AsSize_t(code_size_s); + if (code_size == (size_t)-1 && PyErr_Occurred()) { + return NULL; + } + int ret = PyUnstable_WritePerfMapEntry(code_addr, code_size, entry_name); if (ret < 0) { PyErr_SetFromErrno(PyExc_OSError); @@ -1232,6 +1789,30 @@ invalidate_executors(PyObject *self, PyObject *obj) Py_RETURN_NONE; } +static PyObject * +clear_executor_deletion_list(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyInterpreterState *interp = PyInterpreterState_Get(); + _Py_ClearExecutorDeletionList(interp); + Py_RETURN_NONE; +} + +static PyObject * +get_exit_executor(PyObject *self, PyObject *arg) +{ + if (!PyLong_CheckExact(arg)) { + PyErr_SetString(PyExc_TypeError, "argument must be an ID to an _PyExitData"); + return NULL; + } + uint64_t ptr; + if (PyLong_AsUInt64(arg, &ptr) < 0) { + // Error set by PyLong API + return NULL; + } + _PyExitData *exit = (_PyExitData *)ptr; + return Py_NewRef(exit->executor); +} + #endif static int _pending_callback(void *arg) @@ -2237,6 +2818,13 @@ get_tlbc_id(PyObject *Py_UNUSED(module), PyObject *obj) } return PyLong_FromVoidPtr(bc); } + +static PyObject * +get_long_lived_total(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyLong_FromInt64(PyInterpreterState_Get()->gc.long_lived_total); +} + #endif static PyObject * @@ -2376,10 +2964,250 @@ emscripten_set_up_async_input_device(PyObject *self, PyObject *Py_UNUSED(ignored } #endif +static PyObject * +simple_pending_call(PyObject *self, PyObject *callable) +{ + if (_PyEval_AddPendingCall(_PyInterpreterState_GET(), _pending_callback, Py_NewRef(callable), 0) < 0) { + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject * +vectorcall_nop(PyObject *callable, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ + Py_RETURN_NONE; +} + +static PyObject * +set_vectorcall_nop(PyObject *self, PyObject *func) +{ + if (!PyFunction_Check(func)) { + PyErr_SetString(PyExc_TypeError, "expected function"); + return NULL; + } + + ((PyFunctionObject*)func)->vectorcall = vectorcall_nop; + Py_RETURN_NONE; +} + +static PyObject * +module_get_gc_hooks(PyObject *self, PyObject *arg) +{ + PyModuleObject *mod = (PyModuleObject *)arg; + PyObject *traverse = NULL; + PyObject *clear = NULL; + PyObject *free = NULL; + PyObject *result = NULL; + traverse = PyLong_FromVoidPtr(mod->md_state_traverse); + if (!traverse) { + goto finally; + } + clear = PyLong_FromVoidPtr(mod->md_state_clear); + if (!clear) { + goto finally; + } + free = PyLong_FromVoidPtr(mod->md_state_free); + if (!free) { + goto finally; + } + result = PyTuple_FromArray((PyObject*[]){ traverse, clear, free }, 3); +finally: + Py_XDECREF(traverse); + Py_XDECREF(clear); + Py_XDECREF(free); + return result; +} + + +static void +check_threadstate_set_stack_protection(PyThreadState *tstate, + void *start, size_t size) +{ + assert(PyUnstable_ThreadState_SetStackProtection(tstate, start, size) == 0); + assert(!PyErr_Occurred()); + + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate; + assert(ts->c_stack_top == (uintptr_t)start + size); + assert(ts->c_stack_hard_limit <= ts->c_stack_soft_limit); + assert(ts->c_stack_soft_limit < ts->c_stack_top); +} + + +static PyObject * +test_threadstate_set_stack_protection(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyThreadState *tstate = PyThreadState_GET(); + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate; + assert(!PyErr_Occurred()); + + uintptr_t init_base = ts->c_stack_init_base; + size_t init_top = ts->c_stack_init_top; + + // Test the minimum stack size + size_t size = _PyOS_MIN_STACK_SIZE; + void *start = (void*)(_Py_get_machine_stack_pointer() - size); + check_threadstate_set_stack_protection(tstate, start, size); + + // Test a larger size + size = 7654321; + assert(size > _PyOS_MIN_STACK_SIZE); + start = (void*)(_Py_get_machine_stack_pointer() - size); + check_threadstate_set_stack_protection(tstate, start, size); + + // Test invalid size (too small) + size = 5; + start = (void*)(_Py_get_machine_stack_pointer() - size); + assert(PyUnstable_ThreadState_SetStackProtection(tstate, start, size) == -1); + assert(PyErr_ExceptionMatches(PyExc_ValueError)); + PyErr_Clear(); + + // Test PyUnstable_ThreadState_ResetStackProtection() + PyUnstable_ThreadState_ResetStackProtection(tstate); + assert(ts->c_stack_init_base == init_base); + assert(ts->c_stack_init_top == init_top); + + Py_RETURN_NONE; +} + +#define NUM_GUARDS 100 + +static PyObject * +test_interp_guard_countdown(PyObject *self, PyObject *unused) +{ + PyThreadState *save_tstate = PyThreadState_Swap(NULL); + + // This test assumes that the interpreter has no guards active. + // While this is currently true for the main interpreter as of writing, + // this won't necessarily be true in the future. For the sake of + // maintainance, we create a new interpreter to be sure that there aren't + // any other guards. + PyThreadState *interp_tstate = Py_NewInterpreter(); + assert(interp_tstate != NULL); + PyInterpreterState *interp = PyInterpreterState_Get(); + assert(_PyInterpreterState_GuardCountdown(interp) == 0); + + PyInterpreterGuard *guards[NUM_GUARDS]; + for (int i = 0; i < NUM_GUARDS; ++i) { + guards[i] = PyInterpreterGuard_FromCurrent(); + assert(guards[i] != 0); + assert(_PyInterpreterState_GuardCountdown(interp) == i + 1); + } + + for (int i = 0; i < NUM_GUARDS; ++i) { + PyInterpreterGuard_Close(guards[i]); + assert(_PyInterpreterState_GuardCountdown(interp) == (NUM_GUARDS - i - 1)); + } + + Py_EndInterpreter(interp_tstate); + PyThreadState_Swap(save_tstate); + Py_RETURN_NONE; +} + +static PyObject * +test_interp_view_countdown(PyObject *self, PyObject *unused) +{ + PyInterpreterState *interp = PyInterpreterState_Get(); + PyInterpreterView *view = PyInterpreterView_FromCurrent(); + if (view == NULL) { + return NULL; + } + assert(_PyInterpreterState_GuardCountdown(interp) == 0); + + PyInterpreterGuard *guards[NUM_GUARDS]; + + for (int i = 0; i < NUM_GUARDS; ++i) { + guards[i] = PyInterpreterGuard_FromView(view); + assert(guards[i] != 0); + assert(_PyInterpreterGuard_GetInterpreter(guards[i]) == interp); + assert(_PyInterpreterState_GuardCountdown(interp) == i + 1); + } + + for (int i = 0; i < NUM_GUARDS; ++i) { + PyInterpreterGuard_Close(guards[i]); + assert(_PyInterpreterState_GuardCountdown(interp) == (NUM_GUARDS - i - 1)); + } + + PyInterpreterView_Close(view); + Py_RETURN_NONE; +} + +#undef NUM_LOCKS + +static PyObject * +_pyerr_setkeyerror(PyObject *self, PyObject *arg) +{ + // Test that _PyErr_SetKeyError() overrides the current exception + // if an exception is set + PyErr_NoMemory(); + + _PyErr_SetKeyError(arg); + + assert(PyErr_Occurred()); + return NULL; +} + +static PyObject * +test_thread_state_ensure_from_view_interp_switch(PyObject *self, PyObject *unused) +{ + /* The main tstate is already attached and was NOT created by + * PyThreadState_Ensure, so delete_on_release == 0. */ + PyInterpreterState *interp = _PyInterpreterState_GET(); + assert(interp != NULL); + PyInterpreterView *view = PyInterpreterView_FromCurrent(); + assert(view != NULL); + + /* First Ensure/Release pair on this pre-existing tstate. */ + assert(_PyThreadState_GET() != NULL); + PyThreadStateToken *t1 = PyThreadState_EnsureFromView(view); + assert(t1 != NULL); + assert(_PyInterpreterState_GuardCountdown(interp) == 1); + PyThreadState_Release(t1); + assert(_PyInterpreterState_GuardCountdown(interp) == 0); + assert(_PyThreadState_GET() != NULL); + + /* tstate->ensure.owned_guard now points at the freed guard. */ + + /* Re-attach: Bug B detaches us as a side effect (separate repro). */ + PyThreadState *save = PyThreadState_Swap(NULL); + + PyThreadStateToken *t2 = PyThreadState_EnsureFromView(view); + assert(_PyInterpreterState_GuardCountdown(interp) == 1); + assert(t2 != NULL); + PyThreadState_Release(t2); + assert(_PyInterpreterState_GuardCountdown(interp) == 0); + assert(_PyThreadState_GET() == NULL); + + PyThreadState_Swap(save); + + /* In a release build (no assertion) the second Ensure silently + * skipped storing its guard and Release decremented the global + * counter from 0, wrapping it to GUARDS_NOT_ALLOWED. All future + * guard acquisitions then fail: */ + PyInterpreterGuard *g = PyInterpreterGuard_FromCurrent(); + assert(g != NULL); + assert(_PyInterpreterState_GuardCountdown(interp) == 1); + PyInterpreterGuard_Close(g); + assert(_PyInterpreterState_GuardCountdown(interp) == 0); + + PyInterpreterView_Close(view); + Py_RETURN_NONE; +} + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, + {"get_eval_frame_stats", get_eval_frame_stats, METH_NOARGS, NULL}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, {"get_c_recursion_remaining", get_c_recursion_remaining, METH_NOARGS}, + {"get_stack_pointer", get_stack_pointer, METH_NOARGS}, + {"get_stack_margin", get_stack_margin, METH_NOARGS}, + {"classify_stack_addresses", classify_stack_addresses, METH_VARARGS}, + {"get_jit_code_ranges", get_jit_code_ranges, METH_NOARGS}, + {"get_jit_backend", get_jit_backend, METH_NOARGS}, + {"gnu_backtrace_unwind", gnu_backtrace_unwind, METH_NOARGS}, + {"manual_frame_pointer_unwind", manual_frame_pointer_unwind, METH_NOARGS}, {"test_bswap", test_bswap, METH_NOARGS}, {"test_popcount", test_popcount, METH_NOARGS}, {"test_bit_length", test_bit_length, METH_NOARGS}, @@ -2392,7 +3220,9 @@ static PyMethodDef module_functions[] = { {"EncodeLocaleEx", encode_locale_ex, METH_VARARGS}, {"DecodeLocaleEx", decode_locale_ex, METH_VARARGS}, {"set_eval_frame_default", set_eval_frame_default, METH_NOARGS, NULL}, + {"set_eval_frame_interp", set_eval_frame_interp, METH_VARARGS, NULL}, {"set_eval_frame_record", set_eval_frame_record, METH_O, NULL}, + {"is_specialization_enabled", is_specialization_enabled, METH_NOARGS, NULL}, _TESTINTERNALCAPI_COMPILER_CLEANDOC_METHODDEF _TESTINTERNALCAPI_NEW_INSTRUCTION_SEQUENCE_METHODDEF _TESTINTERNALCAPI_COMPILER_CODEGEN_METHODDEF @@ -2415,6 +3245,8 @@ static PyMethodDef module_functions[] = { #ifdef _Py_TIER2 {"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL}, {"invalidate_executors", invalidate_executors, METH_O, NULL}, + {"clear_executor_deletion_list", clear_executor_deletion_list, METH_NOARGS, NULL}, + {"get_exit_executor", get_exit_executor, METH_O, NULL}, #endif {"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc), METH_VARARGS | METH_KEYWORDS}, @@ -2466,6 +3298,7 @@ static PyMethodDef module_functions[] = { {"py_thread_id", get_py_thread_id, METH_NOARGS}, {"get_tlbc", get_tlbc, METH_O, NULL}, {"get_tlbc_id", get_tlbc_id, METH_O, NULL}, + {"get_long_lived_total", get_long_lived_total, METH_NOARGS}, #endif #ifdef _Py_TIER2 {"uop_symbols_test", _Py_uop_symbols_test, METH_NOARGS}, @@ -2481,6 +3314,15 @@ static PyMethodDef module_functions[] = { #ifdef __EMSCRIPTEN__ {"emscripten_set_up_async_input_device", emscripten_set_up_async_input_device, METH_NOARGS}, #endif + {"simple_pending_call", simple_pending_call, METH_O}, + {"set_vectorcall_nop", set_vectorcall_nop, METH_O}, + {"module_get_gc_hooks", module_get_gc_hooks, METH_O}, + {"test_threadstate_set_stack_protection", + test_threadstate_set_stack_protection, METH_NOARGS}, + {"_pyerr_setkeyerror", _pyerr_setkeyerror, METH_O}, + {"test_interp_guard_countdown", test_interp_guard_countdown, METH_NOARGS}, + {"test_interp_view_countdown", test_interp_view_countdown, METH_NOARGS}, + {"test_thread_state_ensure_from_view_interp_switch", test_thread_state_ensure_from_view_interp_switch, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; @@ -2490,6 +3332,8 @@ static PyMethodDef module_functions[] = { static int module_exec(PyObject *module) { + PyInterpreterState *interp = PyInterpreterState_Get(); + if (_PyTestInternalCapi_Init_Lock(module) < 0) { return 1; } @@ -2505,6 +3349,9 @@ module_exec(PyObject *module) if (_PyTestInternalCapi_Init_CriticalSection(module) < 0) { return 1; } + if (_PyTestInternalCapi_Init_Tuple(module) < 0) { + return 1; + } Py_ssize_t sizeof_gc_head = 0; #ifndef Py_GIL_DISABLED @@ -2531,8 +3378,18 @@ module_exec(PyObject *module) return 1; } + // + 1 more due to one loop spent on tracing. + unsigned long threshold = interp->opt_config.jump_backward_initial_value + 2; if (PyModule_Add(module, "TIER2_THRESHOLD", - PyLong_FromLong(JUMP_BACKWARD_INITIAL_VALUE + 1)) < 0) { + PyLong_FromUnsignedLong(threshold)) < 0) { + return 1; + } + + // + 1 to specialize from RESUME to RESUME_CHECK_JIT + // + 1 more due to one loop spent on tracing. + long resume_threshold = interp->opt_config.resume_initial_value + 2; + if (PyModule_Add(module, "TIER2_RESUME_THRESHOLD", + PyLong_FromLong(resume_threshold)) < 0) { return 1; } @@ -2551,10 +3408,23 @@ module_exec(PyObject *module) return 1; } + if (PyModule_AddIntMacro(module, _PY_NSMALLPOSINTS) < 0) { + return 1; + } + +#ifdef _Py_WITH_FRAME_POINTERS + if (PyModule_AddIntMacro(module, _Py_WITH_FRAME_POINTERS) < 0) { + return 1; + } +#endif + return 0; } +PyABIInfo_VAR(abi_info); + static struct PyModuleDef_Slot module_slots[] = { + {Py_mod_abi, &abi_info}, {Py_mod_exec, module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_testinternalcapi/clinic/test_lock.c.h b/Modules/_testinternalcapi/clinic/test_lock.c.h index 86875767343cd2..6e989a777ac7f0 100644 --- a/Modules/_testinternalcapi/clinic/test_lock.c.h +++ b/Modules/_testinternalcapi/clinic/test_lock.c.h @@ -6,8 +6,9 @@ preserve #include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(_testinternalcapi_benchmark_locks__doc__, -"benchmark_locks($module, num_threads, use_pymutex=True,\n" -" critical_section_length=1, time_ms=1000, /)\n" +"benchmark_locks($module, num_threads, work_inside=1, work_outside=0,\n" +" time_ms=1000, num_acquisitions=1, total_iters=0,\n" +" num_locks=1, random_locks=False, /)\n" "--\n" "\n"); @@ -17,20 +18,26 @@ PyDoc_STRVAR(_testinternalcapi_benchmark_locks__doc__, static PyObject * _testinternalcapi_benchmark_locks_impl(PyObject *module, Py_ssize_t num_threads, - int use_pymutex, - int critical_section_length, - int time_ms); + int work_inside, int work_outside, + int time_ms, int num_acquisitions, + Py_ssize_t total_iters, + Py_ssize_t num_locks, + int random_locks); static PyObject * _testinternalcapi_benchmark_locks(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; Py_ssize_t num_threads; - int use_pymutex = 1; - int critical_section_length = 1; + int work_inside = 1; + int work_outside = 0; int time_ms = 1000; + int num_acquisitions = 1; + Py_ssize_t total_iters = 0; + Py_ssize_t num_locks = 1; + int random_locks = 0; - if (!_PyArg_CheckPositional("benchmark_locks", nargs, 1, 4)) { + if (!_PyArg_CheckPositional("benchmark_locks", nargs, 1, 8)) { goto exit; } { @@ -48,15 +55,15 @@ _testinternalcapi_benchmark_locks(PyObject *module, PyObject *const *args, Py_ss if (nargs < 2) { goto skip_optional; } - use_pymutex = PyObject_IsTrue(args[1]); - if (use_pymutex < 0) { + work_inside = PyLong_AsInt(args[1]); + if (work_inside == -1 && PyErr_Occurred()) { goto exit; } if (nargs < 3) { goto skip_optional; } - critical_section_length = PyLong_AsInt(args[2]); - if (critical_section_length == -1 && PyErr_Occurred()) { + work_outside = PyLong_AsInt(args[2]); + if (work_outside == -1 && PyErr_Occurred()) { goto exit; } if (nargs < 4) { @@ -66,10 +73,54 @@ _testinternalcapi_benchmark_locks(PyObject *module, PyObject *const *args, Py_ss if (time_ms == -1 && PyErr_Occurred()) { goto exit; } + if (nargs < 5) { + goto skip_optional; + } + num_acquisitions = PyLong_AsInt(args[4]); + if (num_acquisitions == -1 && PyErr_Occurred()) { + goto exit; + } + if (nargs < 6) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[5]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + total_iters = ival; + } + if (nargs < 7) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[6]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + num_locks = ival; + } + if (nargs < 8) { + goto skip_optional; + } + random_locks = PyObject_IsTrue(args[7]); + if (random_locks < 0) { + goto exit; + } skip_optional: - return_value = _testinternalcapi_benchmark_locks_impl(module, num_threads, use_pymutex, critical_section_length, time_ms); + return_value = _testinternalcapi_benchmark_locks_impl(module, num_threads, work_inside, work_outside, time_ms, num_acquisitions, total_iters, num_locks, random_locks); exit: return return_value; } -/*[clinic end generated code: output=105105d759c0c271 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6cfed9fc081313ef input=a9049054013a1b77]*/ diff --git a/Modules/_testinternalcapi/interpreter.c b/Modules/_testinternalcapi/interpreter.c new file mode 100644 index 00000000000000..99dcd18393fb87 --- /dev/null +++ b/Modules/_testinternalcapi/interpreter.c @@ -0,0 +1,156 @@ +#ifndef Py_BUILD_CORE +#define Py_BUILD_CORE +#endif +#ifndef Py_BUILD_CORE_MODULE +#define Py_BUILD_CORE_MODULE +#endif + +#include "../../Python/ceval.h" + +#include "../../Python/ceval_macros.h" + +#undef IS_PEP523_HOOKED +#define IS_PEP523_HOOKED(tstate) (tstate->interp->eval_frame != NULL && !tstate->interp->eval_frame_allow_specialization) + +int Test_EvalFrame_Resumes, Test_EvalFrame_Loads; + +#ifdef _Py_TIER2 +static int +stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) +{ + (void)(tstate); + (void)(frame); + return 0; +} +#endif + +_PyJitEntryFuncPtr _Py_jit_entry; + +#if _Py_TAIL_CALL_INTERP +#include "test_targets.h" +#include "test_cases.c.h" +#endif + +PyObject* _Py_HOT_FUNCTION +Test_EvalFrame(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) +{ + _Py_EnsureTstateNotNULL(tstate); + check_invalid_reentrancy(); + CALL_STAT_INC(pyeval_calls); + +#if USE_COMPUTED_GOTOS && !_Py_TAIL_CALL_INTERP +/* Import the static jump table */ +#include "test_targets.h" + void **opcode_targets = opcode_targets_table; +#endif + +#ifdef Py_STATS + int lastopcode = 0; +#endif +#if !_Py_TAIL_CALL_INTERP + uint8_t opcode; /* Current opcode */ + int oparg; /* Current opcode argument, if any */ + assert(tstate->current_frame == NULL || tstate->current_frame->stackpointer != NULL); +#if !USE_COMPUTED_GOTOS + uint8_t tracing_mode = 0; + uint8_t dispatch_code; +#endif +#endif + _PyEntryFrame entry; + + if (_Py_EnterRecursiveCallTstate(tstate, "")) { + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + _PyEval_FrameClearAndPop(tstate, frame); + return NULL; + } + + /* Local "register" variables. + * These are cached values from the frame and code object. */ + _Py_CODEUNIT *next_instr; + _PyStackRef *stack_pointer; + entry.stack[0] = PyStackRef_NULL; +#ifdef Py_STACKREF_DEBUG + entry.frame.f_funcobj = PyStackRef_None; +#elif defined(Py_DEBUG) + /* Set these to invalid but identifiable values for debugging. */ + entry.frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0}; + entry.frame.f_locals = (PyObject*)0xaaa1; + entry.frame.frame_obj = (PyFrameObject*)0xaaa2; + entry.frame.f_globals = (PyObject*)0xaaa3; + entry.frame.f_builtins = (PyObject*)0xaaa4; +#endif + entry.frame.f_executable = PyStackRef_None; + entry.frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR + 1; + entry.frame.stackpointer = entry.stack; + entry.frame.owner = FRAME_OWNED_BY_INTERPRETER; + entry.frame.visited = 0; + entry.frame.return_offset = 0; +#ifdef Py_DEBUG + entry.frame.lltrace = 0; +#endif + /* Push frame */ + entry.frame.previous = tstate->current_frame; + frame->previous = &entry.frame; + tstate->current_frame = frame; + entry.frame.localsplus[0] = PyStackRef_NULL; + + /* support for generator.throw() */ + if (throwflag) { + if (_Py_EnterRecursivePy(tstate)) { + goto early_exit; + } +#ifdef Py_GIL_DISABLED + /* Load thread-local bytecode */ + if (frame->tlbc_index != ((_PyThreadStateImpl *)tstate)->tlbc_index) { + _Py_CODEUNIT *bytecode = + _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); + if (bytecode == NULL) { + goto early_exit; + } + ptrdiff_t off = frame->instr_ptr - _PyFrame_GetBytecode(frame); + frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; + frame->instr_ptr = bytecode + off; + } +#endif + /* Because this avoids the RESUME, we need to update instrumentation */ + _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); + next_instr = frame->instr_ptr; + monitor_throw(tstate, frame, next_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); +#if _Py_TAIL_CALL_INTERP +# if Py_STATS + return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, instruction_funcptr_handler_table, 0, lastopcode); +# else + return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, instruction_funcptr_handler_table, 0); +# endif +#else + goto error; +#endif + } + +#if _Py_TAIL_CALL_INTERP +# if Py_STATS + return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, instruction_funcptr_handler_table, 0, lastopcode); +# else + return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, instruction_funcptr_handler_table, 0); +# endif +#else + goto start_frame; +#include "test_cases.c.h" +#endif + + +early_exit: + assert(_PyErr_Occurred(tstate)); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + frame->return_offset = 0; + assert(frame->owner == FRAME_OWNED_BY_INTERPRETER); + /* Restore previous frame and exit */ + tstate->current_frame = frame->previous; + return NULL; +} diff --git a/Modules/_testinternalcapi/parts.h b/Modules/_testinternalcapi/parts.h index 03557d5bf5957f..81f536c3babb18 100644 --- a/Modules/_testinternalcapi/parts.h +++ b/Modules/_testinternalcapi/parts.h @@ -15,5 +15,6 @@ int _PyTestInternalCapi_Init_PyTime(PyObject *module); int _PyTestInternalCapi_Init_Set(PyObject *module); int _PyTestInternalCapi_Init_Complex(PyObject *module); int _PyTestInternalCapi_Init_CriticalSection(PyObject *module); +int _PyTestInternalCapi_Init_Tuple(PyObject *module); #endif // Py_TESTINTERNALCAPI_PARTS_H diff --git a/Modules/_testinternalcapi/pytime.c b/Modules/_testinternalcapi/pytime.c index 2b0a205d158a96..7fb100c41a1b2b 100644 --- a/Modules/_testinternalcapi/pytime.c +++ b/Modules/_testinternalcapi/pytime.c @@ -17,7 +17,7 @@ test_pytime_fromseconds(PyObject *self, PyObject *args) return NULL; } PyTime_t ts = _PyTime_FromSeconds(seconds); - return _PyTime_AsLong(ts); + return PyLong_FromInt64(ts); } static int @@ -49,7 +49,7 @@ test_pytime_fromsecondsobject(PyObject *self, PyObject *args) if (_PyTime_FromSecondsObject(&ts, obj, round) == -1) { return NULL; } - return _PyTime_AsLong(ts); + return PyLong_FromInt64(ts); } static PyObject * @@ -64,7 +64,7 @@ test_PyTime_AsTimeval(PyObject *self, PyObject *args) return NULL; } PyTime_t t; - if (_PyTime_FromLong(&t, obj) < 0) { + if (PyLong_AsInt64(obj, &t) < 0) { return NULL; } struct timeval tv; @@ -91,7 +91,7 @@ test_PyTime_AsTimeval_clamp(PyObject *self, PyObject *args) return NULL; } PyTime_t t; - if (_PyTime_FromLong(&t, obj) < 0) { + if (PyLong_AsInt64(obj, &t) < 0) { return NULL; } struct timeval tv; @@ -113,7 +113,7 @@ test_PyTime_AsTimespec(PyObject *self, PyObject *args) return NULL; } PyTime_t t; - if (_PyTime_FromLong(&t, obj) < 0) { + if (PyLong_AsInt64(obj, &t) < 0) { return NULL; } struct timespec ts; @@ -131,7 +131,7 @@ test_PyTime_AsTimespec_clamp(PyObject *self, PyObject *args) return NULL; } PyTime_t t; - if (_PyTime_FromLong(&t, obj) < 0) { + if (PyLong_AsInt64(obj, &t) < 0) { return NULL; } struct timespec ts; @@ -149,14 +149,14 @@ test_PyTime_AsMilliseconds(PyObject *self, PyObject *args) return NULL; } PyTime_t t; - if (_PyTime_FromLong(&t, obj) < 0) { + if (PyLong_AsInt64(obj, &t) < 0) { return NULL; } if (check_time_rounding(round) < 0) { return NULL; } PyTime_t ms = _PyTime_AsMilliseconds(t, round); - return _PyTime_AsLong(ms); + return PyLong_FromInt64(ms); } static PyObject * @@ -168,14 +168,14 @@ test_PyTime_AsMicroseconds(PyObject *self, PyObject *args) return NULL; } PyTime_t t; - if (_PyTime_FromLong(&t, obj) < 0) { + if (PyLong_AsInt64(obj, &t) < 0) { return NULL; } if (check_time_rounding(round) < 0) { return NULL; } PyTime_t us = _PyTime_AsMicroseconds(t, round); - return _PyTime_AsLong(us); + return PyLong_FromInt64(us); } static PyObject * diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h new file mode 100644 index 00000000000000..503f566c9ae86a --- /dev/null +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -0,0 +1,13258 @@ +// This file is generated by Tools/cases_generator/tier1_generator.py +// from: +// Python/bytecodes.c, Modules/_testinternalcapi/testbytecodes.c +// Do not edit! + +#ifdef TIER_TWO + #error "This file is for Tier 1 only" +#endif +#define TIER_ONE 1 + +#if !_Py_TAIL_CALL_INTERP +#if !USE_COMPUTED_GOTOS + dispatch_opcode: + switch (dispatch_code) +#endif + { +#endif /* _Py_TAIL_CALL_INTERP */ + /* BEGIN INSTRUCTIONS */ + + + TARGET(BINARY_OP) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP); + PREDICTED_BINARY_OP:; + _Py_CODEUNIT* const this_instr = next_instr - 6; + (void)this_instr; + _PyStackRef lhs; + _PyStackRef rhs; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; + // _SPECIALIZE_BINARY_OP + { + rhs = stack_pointer[-1]; + lhs = stack_pointer[-2]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, LOCALS_ARRAY); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(BINARY_OP); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + assert(NB_ADD <= oparg); + assert(oparg <= NB_OPARG_LAST); + } + /* Skip 4 cache entries */ + // _BINARY_OP + { + PyObject *lhs_o = PyStackRef_AsPyObjectBorrow(lhs); + PyObject *rhs_o = PyStackRef_AsPyObjectBorrow(rhs); + assert(_PyEval_BinaryOps[oparg]); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyEval_BinaryOps[oparg](lhs_o, rhs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + l = lhs; + r = rhs; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = res; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(BINARY_OP_ADD_FLOAT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_ADD_FLOAT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_ADD_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_FLOAT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyFloat_CheckExact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_FLOAT + { + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!PyFloat_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_ADD_FLOAT + { + right = value; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval + + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + } + // _POP_TOP_FLOAT + { + value = r; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + // _POP_TOP_FLOAT + { + value = l; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_OP_ADD_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_ADD_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_ADD_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_INT + { + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!_PyLong_CheckExactAndCompact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_ADD_INT + { + right = value; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + l = left; + r = right; + } + // _POP_TOP_INT + { + value = r; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_INT + { + value = l; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_OP_ADD_UNICODE) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_ADD_UNICODE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_ADD_UNICODE); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef nos; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_UNICODE + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyUnicode_CheckExact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_UNICODE + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_ADD_UNICODE + { + right = value; + left = nos; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + PyObject *res_o = PyUnicode_Concat(left_o, right_o); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + } + // _POP_TOP_UNICODE + { + value = r; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + // _POP_TOP_UNICODE + { + value = l; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_OP_EXTEND) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_EXTEND; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_EXTEND); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; + /* Skip 1 cache entry */ + // _GUARD_BINARY_OP_EXTEND + { + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *descr = read_obj(&this_instr[2].cache); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d != NULL); + _PyFrame_SetStackPointer(frame, stack_pointer); + int match = (d->guard != NULL) + ? d->guard(left_o, right_o) + : (Py_TYPE(left_o) == d->lhs_type && Py_TYPE(right_o) == d->rhs_type); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (!match) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip -4 cache entry */ + // _BINARY_OP_EXTEND + { + PyObject *descr = read_obj(&this_instr[2].cache); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + STAT_INC(BINARY_OP, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = d->action(left_o, right_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + assert(d->result_type == NULL || Py_TYPE(res_o) == d->result_type); + assert(!d->result_unique || Py_REFCNT(res_o) == 1 || _Py_IsImmortal(res_o)); + assert(!PyFloat_CheckExact(res_o) || Py_REFCNT(res_o) == 1); + res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = res; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_INPLACE_ADD_UNICODE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_INPLACE_ADD_UNICODE); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef nos; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + // _GUARD_TOS_UNICODE + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyUnicode_CheckExact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_UNICODE + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_INPLACE_ADD_UNICODE + { + right = value; + left = nos; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(right))); + int next_oparg; + #if TIER_ONE + assert(next_instr->op.code == STORE_FAST); + next_oparg = next_instr->op.arg; + #else + next_oparg = (int)CURRENT_OPERAND0_16(); + #endif + _PyStackRef *target_local = &GETLOCAL(next_oparg); + assert(PyUnicode_CheckExact(left_o)); + if (PyStackRef_AsPyObjectBorrow(*target_local) != left_o) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + STAT_INC(BINARY_OP, hit); + assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left)); + PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); + PyObject *right_o = PyStackRef_AsPyObjectSteal(right); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyUnicode_Append(&temp, right_o); + _Py_DECREF_SPECIALIZED(right_o, _PyUnicode_ExactDealloc); + stack_pointer = _PyFrame_GetStackPointer(frame); + *target_local = PyStackRef_NULL; + if (temp == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(temp); + } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_OP_MULTIPLY_FLOAT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_MULTIPLY_FLOAT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_MULTIPLY_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_FLOAT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyFloat_CheckExact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_FLOAT + { + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!PyFloat_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_MULTIPLY_FLOAT + { + right = value; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval * + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + } + // _POP_TOP_FLOAT + { + value = r; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + // _POP_TOP_FLOAT + { + value = l; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_OP_MULTIPLY_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_MULTIPLY_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_MULTIPLY_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_INT + { + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!_PyLong_CheckExactAndCompact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_MULTIPLY_INT + { + right = value; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + l = left; + r = right; + } + // _POP_TOP_INT + { + value = r; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_INT + { + value = l; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_OP_SUBSCR_DICT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBSCR_DICT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_DICT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef nos; + _PyStackRef dict_st; + _PyStackRef sub_st; + _PyStackRef res; + _PyStackRef ds; + _PyStackRef ss; + _PyStackRef value; + // _GUARD_NOS_DICT_SUBSCRIPT + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!Py_TYPE(o)->tp_as_mapping) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBSCR_DICT + { + sub_st = stack_pointer[-1]; + dict_st = nos; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(Py_TYPE(dict)->tp_as_mapping->mp_subscript == _PyDict_Subscript); + STAT_INC(BINARY_OP, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyDict_Subscript(dict, sub); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + ds = dict_st; + ss = sub_st; + } + // _POP_TOP + { + value = ss; + stack_pointer[-2] = res; + stack_pointer[-1] = ds; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = ds; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(BINARY_OP_SUBSCR_GETITEM) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBSCR_GETITEM; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_GETITEM); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef container; + _PyStackRef getitem; + _PyStackRef sub; + _PyStackRef new_frame; + /* Skip 5 cache entries */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _BINARY_OP_SUBSCR_CHECK_FUNC + { + container = stack_pointer[-2]; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); + if (!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; + PyObject *getitem_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(ht->_spec_cache.getitem); + if (getitem_o == NULL) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + assert(PyFunction_Check(getitem_o)); + uint32_t cached_version = FT_ATOMIC_LOAD_UINT32_RELAXED(ht->_spec_cache.getitem_version); + if (((PyFunctionObject *)getitem_o)->func_version != cached_version) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem_o); + assert(code->co_argcount == 2); + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + getitem = PyStackRef_FromPyObjectNew(getitem_o); + } + // _BINARY_OP_SUBSCR_INIT_CALL + { + sub = stack_pointer[-1]; + STAT_INC(BINARY_OP, hit); + _PyInterpreterFrame* pushed_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); + pushed_frame->localsplus[0] = container; + pushed_frame->localsplus[1] = sub; + frame->return_offset = 6u ; + new_frame = PyStackRef_Wrap(pushed_frame); + } + // _PUSH_FRAME + { + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(BINARY_OP_SUBSCR_LIST_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBSCR_LIST_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_LIST_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef nos; + _PyStackRef list_st; + _PyStackRef sub_st; + _PyStackRef res; + _PyStackRef ls; + _PyStackRef ss; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_LIST + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyList_CheckExact(o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBSCR_LIST_INT + { + sub_st = value; + list_st = nos; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); + assert(PyLong_CheckExact(sub)); + assert(PyList_CheckExact(list)); + Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub); + if (index < 0) { + index += PyList_GET_SIZE(list); + } + #ifdef Py_GIL_DISABLED + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyList_GetItemRef((PyListObject*)list, index); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + res = PyStackRef_FromPyObjectSteal(res_o); + #else + if (index < 0 || index >= PyList_GET_SIZE(list)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + PyObject *res_o = PyList_GET_ITEM(list, index); + assert(res_o != NULL); + res = PyStackRef_FromPyObjectNew(res_o); + #endif + STAT_INC(BINARY_OP, hit); + ls = list_st; + ss = sub_st; + } + // _POP_TOP_INT + { + value = ss; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP + { + value = ls; + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(BINARY_OP_SUBSCR_LIST_SLICE) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBSCR_LIST_SLICE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_LIST_SLICE); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef tos; + _PyStackRef nos; + _PyStackRef list_st; + _PyStackRef sub_st; + _PyStackRef res; + _PyStackRef ls; + _PyStackRef ss; + _PyStackRef value; + // _GUARD_TOS_SLICE + { + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PySlice_Check(o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_LIST + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyList_CheckExact(o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBSCR_LIST_SLICE + { + sub_st = tos; + list_st = nos; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); + assert(PySlice_Check(sub)); + assert(PyList_CheckExact(list)); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyList_SliceSubscript(list, sub); + stack_pointer = _PyFrame_GetStackPointer(frame); + STAT_INC(BINARY_OP, hit); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + ls = list_st; + ss = sub_st; + } + // _POP_TOP + { + value = ss; + stack_pointer[-2] = res; + stack_pointer[-1] = ls; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = ls; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(BINARY_OP_SUBSCR_STR_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBSCR_STR_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_STR_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef nos; + _PyStackRef str_st; + _PyStackRef sub_st; + _PyStackRef res; + _PyStackRef s; + _PyStackRef i; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_COMPACT_ASCII + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (!PyUnicode_IS_COMPACT_ASCII(o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBSCR_STR_INT + { + sub_st = value; + str_st = nos; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); + assert(PyLong_CheckExact(sub)); + assert(PyUnicode_CheckExact(str)); + if (!_PyLong_IsNonNegativeCompact((PyLongObject*)sub)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + if (PyUnicode_GET_LENGTH(str) <= index) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + uint8_t c = PyUnicode_1BYTE_DATA(str)[index]; + assert(c < 128); + STAT_INC(BINARY_OP, hit); + PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; + s = str_st; + i = sub_st; + res = PyStackRef_FromPyObjectBorrow(res_o); + } + // _POP_TOP_INT + { + value = i; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_UNICODE + { + value = s; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_OP_SUBSCR_TUPLE_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBSCR_TUPLE_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_TUPLE_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef nos; + _PyStackRef tuple_st; + _PyStackRef sub_st; + _PyStackRef res; + _PyStackRef ts; + _PyStackRef ss; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_TUPLE + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyTuple_CheckExact(o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS + { + sub_st = value; + tuple_st = nos; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); + if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + if (index >= PyTuple_GET_SIZE(tuple)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBSCR_TUPLE_INT + { + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); + STAT_INC(BINARY_OP, hit); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + PyObject *res_o = PyTuple_GET_ITEM(tuple, index); + assert(res_o != NULL); + res = PyStackRef_FromPyObjectNew(res_o); + ts = tuple_st; + ss = sub_st; + } + // _POP_TOP_INT + { + value = ss; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP + { + value = ts; + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(BINARY_OP_SUBSCR_USTR_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBSCR_USTR_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBSCR_USTR_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef nos; + _PyStackRef str_st; + _PyStackRef sub_st; + _PyStackRef res; + _PyStackRef s; + _PyStackRef i; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_UNICODE + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBSCR_USTR_INT + { + sub_st = value; + str_st = nos; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); + assert(PyLong_CheckExact(sub)); + assert(PyUnicode_CheckExact(str)); + if (!_PyLong_IsNonNegativeCompact((PyLongObject*)sub)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + if (PyUnicode_GET_LENGTH(str) <= index) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + Py_UCS4 c = PyUnicode_READ_CHAR(str, index); + if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + STAT_INC(BINARY_OP, hit); + PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; + s = str_st; + i = sub_st; + res = PyStackRef_FromPyObjectBorrow(res_o); + } + // _POP_TOP_INT + { + value = i; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_UNICODE + { + value = s; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_OP_SUBTRACT_FLOAT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBTRACT_FLOAT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_FLOAT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyFloat_CheckExact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_FLOAT + { + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!PyFloat_CheckExact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBTRACT_FLOAT + { + right = value; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval - + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + } + // _POP_TOP_FLOAT + { + value = r; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + // _POP_TOP_FLOAT + { + value = l; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_OP_SUBTRACT_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBTRACT_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_INT + { + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!_PyLong_CheckExactAndCompact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBTRACT_INT + { + right = value; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + l = left; + r = right; + } + // _POP_TOP_INT + { + value = r; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_INT + { + value = l; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_SLICE) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_SLICE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BINARY_SLICE); + _PyStackRef container; + _PyStackRef start; + _PyStackRef stop; + _PyStackRef res; + // _SPECIALIZE_BINARY_SLICE + { + #if ENABLE_SPECIALIZATION + OPCODE_DEFERRED_INC(BINARY_SLICE); + #endif /* ENABLE_SPECIALIZATION */ + } + // _BINARY_SLICE + { + stop = stack_pointer[-1]; + start = stack_pointer[-2]; + container = stack_pointer[-3]; + PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); + PyObject *start_o = PyStackRef_AsPyObjectBorrow(start); + PyObject *stop_o = PyStackRef_AsPyObjectBorrow(stop); + PyObject *res_o; + if (PyList_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyList_BinarySlice(container_o, start_o, stop_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else if (PyTuple_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyTuple_BinarySlice(container_o, start_o, stop_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else if (PyUnicode_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyUnicode_BinarySlice(container_o, start_o, stop_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + PyObject *slice = PySlice_New(start_o, stop_o, NULL); + if (slice == NULL) { + res_o = NULL; + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyObject_GetItem(container_o, slice); + Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = stop; + stop = PyStackRef_NULL; + stack_pointer[-1] = stop; + PyStackRef_CLOSE(tmp); + tmp = start; + start = PyStackRef_NULL; + stack_pointer[-2] = start; + PyStackRef_CLOSE(tmp); + tmp = container; + container = PyStackRef_NULL; + stack_pointer[-3] = container; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BUILD_INTERPOLATION) { + #if _Py_TAIL_CALL_INTERP + int opcode = BUILD_INTERPOLATION; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BUILD_INTERPOLATION); + _PyStackRef value; + _PyStackRef str; + _PyStackRef *format; + _PyStackRef interpolation; + format = &stack_pointer[-(oparg & 1)]; + str = stack_pointer[-1 - (oparg & 1)]; + value = stack_pointer[-2 - (oparg & 1)]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + PyObject *str_o = PyStackRef_AsPyObjectBorrow(str); + int conversion = oparg >> 2; + PyObject *format_o; + if (oparg & 1) { + format_o = PyStackRef_AsPyObjectBorrow(format[0]); + } + else { + format_o = &_Py_STR(empty); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (oparg & 1) { + stack_pointer += -(oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(format[0]); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + stack_pointer += -(oparg & 1); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(str); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (interpolation_o == NULL) { + JUMP_TO_LABEL(error); + } + interpolation = PyStackRef_FromPyObjectSteal(interpolation_o); + stack_pointer[0] = interpolation; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BUILD_LIST) { + #if _Py_TAIL_CALL_INTERP + int opcode = BUILD_LIST; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BUILD_LIST); + _PyStackRef *values; + _PyStackRef list; + values = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *list_o = _PyList_FromStackRefStealOnSuccess(values, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (list_o == NULL) { + JUMP_TO_LABEL(error); + } + list = PyStackRef_FromPyObjectStealMortal(list_o); + stack_pointer[-oparg] = list; + stack_pointer += 1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BUILD_MAP) { + #if _Py_TAIL_CALL_INTERP + int opcode = BUILD_MAP; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BUILD_MAP); + _PyStackRef *values; + _PyStackRef map; + values = &stack_pointer[-oparg*2]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *map_o = _Py_BuildMap_StackRefSteal(values, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (map_o == NULL) { + stack_pointer += -oparg*2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + map = PyStackRef_FromPyObjectStealMortal(map_o); + stack_pointer[-oparg*2] = map; + stack_pointer += 1 - oparg*2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BUILD_SET) { + #if _Py_TAIL_CALL_INTERP + int opcode = BUILD_SET; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BUILD_SET); + _PyStackRef *values; + _PyStackRef set; + values = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *set_o = PySet_New(NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (set_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp; + for (int _i = oparg; --_i >= 0;) { + tmp = values[_i]; + values[_i] = PyStackRef_NULL; + PyStackRef_CLOSE(tmp); + } + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + int err = 0; + for (Py_ssize_t i = 0; i < oparg; i++) { + _PyStackRef value = values[i]; + values[i] = PyStackRef_NULL; + if (err == 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + err = _PySet_AddTakeRef((PySetObject *)set_o, PyStackRef_AsPyObjectSteal(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + if (err) { + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(set_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + set = PyStackRef_FromPyObjectStealMortal(set_o); + stack_pointer[-oparg] = set; + stack_pointer += 1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BUILD_SLICE) { + #if _Py_TAIL_CALL_INTERP + int opcode = BUILD_SLICE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BUILD_SLICE); + _PyStackRef *args; + _PyStackRef slice; + args = &stack_pointer[-oparg]; + PyObject *start_o = PyStackRef_AsPyObjectBorrow(args[0]); + PyObject *stop_o = PyStackRef_AsPyObjectBorrow(args[1]); + PyObject *step_o = oparg == 3 ? PyStackRef_AsPyObjectBorrow(args[2]) : NULL; + PyObject *slice_o = PySlice_New(start_o, stop_o, step_o); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp; + for (int _i = oparg; --_i >= 0;) { + tmp = args[_i]; + args[_i] = PyStackRef_NULL; + PyStackRef_CLOSE(tmp); + } + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (slice_o == NULL) { + JUMP_TO_LABEL(error); + } + slice = PyStackRef_FromPyObjectStealMortal(slice_o); + stack_pointer[0] = slice; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BUILD_STRING) { + #if _Py_TAIL_CALL_INTERP + int opcode = BUILD_STRING; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BUILD_STRING); + _PyStackRef *pieces; + _PyStackRef str; + pieces = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *str_o = _Py_BuildString_StackRefSteal(pieces, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (str_o == NULL) { + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + str = PyStackRef_FromPyObjectSteal(str_o); + stack_pointer[-oparg] = str; + stack_pointer += 1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BUILD_TEMPLATE) { + #if _Py_TAIL_CALL_INTERP + int opcode = BUILD_TEMPLATE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BUILD_TEMPLATE); + _PyStackRef strings; + _PyStackRef interpolations; + _PyStackRef template; + interpolations = stack_pointer[-1]; + strings = stack_pointer[-2]; + PyObject *strings_o = PyStackRef_AsPyObjectBorrow(strings); + PyObject *interpolations_o = PyStackRef_AsPyObjectBorrow(interpolations); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(interpolations); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(strings); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (template_o == NULL) { + JUMP_TO_LABEL(error); + } + template = PyStackRef_FromPyObjectSteal(template_o); + stack_pointer[0] = template; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BUILD_TUPLE) { + #if _Py_TAIL_CALL_INTERP + int opcode = BUILD_TUPLE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(BUILD_TUPLE); + _PyStackRef *values; + _PyStackRef tup; + values = &stack_pointer[-oparg]; + PyObject *tup_o = _PyTuple_FromStackRefStealOnSuccess(values, oparg); + if (tup_o == NULL) { + JUMP_TO_LABEL(error); + } + tup = PyStackRef_FromPyObjectStealMortal(tup_o); + stack_pointer[-oparg] = tup; + stack_pointer += 1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(CACHE) { + #if _Py_TAIL_CALL_INTERP + int opcode = CACHE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CACHE); + assert(0 && "Executing a cache."); + Py_FatalError("Executing a cache."); + DISPATCH(); + } + + TARGET(CALL) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL); + PREDICTED_CALL:; + _Py_CODEUNIT* const this_instr = next_instr - 4; + (void)this_instr; + opcode = CALL; + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef res; + // _SPECIALIZE_CALL + { + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_Call(callable, self_or_null, next_instr, oparg + !PyStackRef_IsNull(self_or_null)); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(CALL); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + /* Skip 2 cache entries */ + // _MAYBE_EXPAND_METHOD + { + if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *self = ((PyMethodObject *)callable_o)->im_self; + self_or_null = PyStackRef_FromPyObjectNew(self); + PyObject *method = ((PyMethodObject *)callable_o)->im_func; + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(method); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _DO_CALL + { + args = &stack_pointer[-oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + if (Py_TYPE(callable_o) == &PyFunction_Type && + !IS_PEP523_HOOKED(tstate) && + ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) + { + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, callable, locals, + arguments, total_args, NULL, frame + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (new_frame == NULL) { + JUMP_TO_LABEL(error); + } + frame->return_offset = 4u ; + DISPATCH_INLINED(new_frame); + } + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject* res_o = _Py_VectorCallInstrumentation_StackRefSteal( + callable, + arguments, + total_args, + PyStackRef_NULL, + opcode == INSTRUMENTED_CALL, + frame, + this_instr, + tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _CHECK_PERIODIC_AT_END + { + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_ALLOC_AND_ENTER_INIT) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_ALLOC_AND_ENTER_INIT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_ALLOC_AND_ENTER_INIT); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef init; + _PyStackRef self; + _PyStackRef *args; + _PyStackRef init_frame; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_OBJECT + { + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyStackRef_IsNull(self_or_null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (!PyType_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + PyTypeObject *tp = (PyTypeObject *)callable_o; + if (FT_ATOMIC_LOAD_UINT32_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_RECURSION_REMAINING + { + if (tstate->py_recursion_remaining <= 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _ALLOCATE_OBJECT + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(PyStackRef_IsNull(self_or_null)); + assert(PyType_Check(callable_o)); + PyTypeObject *tp = (PyTypeObject *)callable_o; + assert(tp->tp_new == PyBaseObject_Type.tp_new); + assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); + assert(tp->tp_alloc == PyType_GenericAlloc); + PyHeapTypeObject *cls = (PyHeapTypeObject *)callable_o; + PyFunctionObject *init_func = (PyFunctionObject *)FT_ATOMIC_LOAD_PTR_ACQUIRE(cls->_spec_cache.init); + PyCodeObject *code = (PyCodeObject *)init_func->func_code; + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *self_o = PyType_GenericAlloc(tp, 0); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (self_o == NULL) { + JUMP_TO_LABEL(error); + } + self_or_null = PyStackRef_FromPyObjectSteal(self_o); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(init_func); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CREATE_INIT_FRAME + { + args = &stack_pointer[-oparg]; + self = self_or_null; + init = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( + tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK); + assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE); + shim->localsplus[0] = PyStackRef_DUP(self); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( + tstate, init, NULL, args-1, oparg+1, NULL, shim); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (temp == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FrameClearAndPop(tstate, shim); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL; + tstate->py_recursion_remaining--; + init_frame = PyStackRef_Wrap(temp); + } + // _PUSH_FRAME + { + new_frame = init_frame; + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_BOUND_METHOD_EXACT_ARGS; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_BOUND_METHOD_EXACT_ARGS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef null; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_CALL_BOUND_METHOD_EXACT_ARGS + { + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (Py_TYPE(PyStackRef_AsPyObjectBorrow(callable)) != &PyMethod_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _INIT_CALL_BOUND_METHOD_EXACT_ARGS + { + self_or_null = null; + assert(PyStackRef_IsNull(self_or_null)); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + STAT_INC(CALL, hit); + self_or_null = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // flush + // _CHECK_FUNCTION_VERSION + { + uint32_t func_version = read_u32(&this_instr[2].cache); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_FUNCTION_EXACT_ARGS + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(PyFunction_Check(callable_o)); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + PyCodeObject *code = (PyCodeObject *)func->func_code; + if (code->co_argcount != oparg + (!PyStackRef_IsNull(self_or_null))) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_STACK_SPACE + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + PyCodeObject *code = (PyCodeObject *)func->func_code; + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_RECURSION_REMAINING + { + if (tstate->py_recursion_remaining <= 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _INIT_CALL_PY_EXACT_ARGS + { + args = &stack_pointer[-oparg]; + int has_self = !PyStackRef_IsNull(self_or_null); + STAT_INC(CALL, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); + _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; + pushed_frame->localsplus[0] = self_or_null; + for (int i = 0; i < oparg; i++) { + first_non_self_local[i] = args[i]; + } + new_frame = PyStackRef_Wrap(pushed_frame); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(CALL_BOUND_METHOD_GENERAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_BOUND_METHOD_GENERAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_BOUND_METHOD_GENERAL); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef null; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_METHOD_VERSION + { + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = read_u32(&this_instr[2].cache); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (Py_TYPE(callable_o) != &PyMethod_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + PyObject *func = ((PyMethodObject *)callable_o)->im_func; + if (!PyFunction_Check(func)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (((PyFunctionObject *)func)->func_version != func_version) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _EXPAND_METHOD + { + self_or_null = null; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(PyStackRef_IsNull(self_or_null)); + assert(Py_TYPE(callable_o) == &PyMethod_Type); + self_or_null = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + assert(PyStackRef_FunctionCheck(callable)); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // flush + // _CHECK_RECURSION_REMAINING + { + if (tstate->py_recursion_remaining <= 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _PY_FRAME_GENERAL + { + args = &stack_pointer[-oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + args--; + total_args++; + } + assert(Py_TYPE(callable_o) == &PyFunction_Type); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( + tstate, callable, locals, + args, total_args, NULL, frame + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (temp == NULL) { + JUMP_TO_LABEL(error); + } + new_frame = PyStackRef_Wrap(temp); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(CALL_BUILTIN_CLASS) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_BUILTIN_CLASS; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_CLASS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_CALLABLE_BUILTIN_CLASS + { + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyType_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + PyTypeObject *tp = (PyTypeObject *)callable_o; + if (tp->tp_vectorcall == NULL) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_BUILTIN_CLASS + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _Py_CallBuiltinClass_StackRef( + callable, + arguments, + total_args); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP_OPARG + { + args = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef_CloseStack(args, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = self_or_null; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_BUILTIN_FAST) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_BUILTIN_FAST; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_FAST); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_CALLABLE_BUILTIN_FAST + { + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyCFunction_CheckExact(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (PyCFunction_GET_FLAGS(callable_o) != METH_FASTCALL) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_BUILTIN_FAST + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _Py_BuiltinCallFast_StackRef( + callable, + arguments, + total_args + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP_OPARG + { + args = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef_CloseStack(args, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = self_or_null; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_BUILTIN_FAST_WITH_KEYWORDS; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_FAST_WITH_KEYWORDS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_CALLABLE_BUILTIN_FAST_WITH_KEYWORDS + { + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyCFunction_CheckExact(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (PyCFunction_GET_FLAGS(callable_o) != (METH_FASTCALL | METH_KEYWORDS)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_BUILTIN_FAST_WITH_KEYWORDS + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _Py_BuiltinCallFastWithKeywords_StackRef(callable, arguments, total_args); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP_OPARG + { + args = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef_CloseStack(args, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = self_or_null; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_BUILTIN_O) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_BUILTIN_O; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_BUILTIN_O); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_CALLABLE_BUILTIN_O + { + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyCFunction_CheckExact(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (PyCFunction_GET_FLAGS(callable_o) != METH_O) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args != 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_RECURSION_LIMIT + { + if (_Py_ReachedRecursionLimit(tstate)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_BUILTIN_O + { + args = &stack_pointer[-oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyStackRef_IsNull(self_or_null)) { + args--; + } + STAT_INC(CALL, hit); + PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); + _PyStackRef arg = args[0]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable_o), PyStackRef_AsPyObjectBorrow(arg)); + stack_pointer = _PyFrame_GetStackPointer(frame); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + c = callable; + s = args[0]; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = s; + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = c; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_EX_NON_PY_GENERAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_EX_NON_PY_GENERAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(CALL_EX_NON_PY_GENERAL); + static_assert(INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX == 1, "incorrect cache size"); + _PyStackRef func_st; + _PyStackRef func; + _PyStackRef callargs; + _PyStackRef null; + _PyStackRef callargs_st; + _PyStackRef kwargs_st; + _PyStackRef result; + /* Skip 1 cache entry */ + // _CHECK_IS_NOT_PY_CALLABLE_EX + { + func_st = stack_pointer[-4]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) == &PyFunction_Type && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { + UPDATE_MISS_STATS(CALL_FUNCTION_EX); + assert(_PyOpcode_Deopt[opcode] == (CALL_FUNCTION_EX)); + JUMP_TO_PREDICTED(CALL_FUNCTION_EX); + } + } + // _MAKE_CALLARGS_A_TUPLE + { + callargs = stack_pointer[-2]; + func = func_st; + PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); + if (!PyTuple_CheckExact(callargs_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *tuple_o = PySequence_Tuple(callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (tuple_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callargs; + callargs = PyStackRef_FromPyObjectSteal(tuple_o); + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _CALL_FUNCTION_EX_NON_PY_GENERAL + { + kwargs_st = stack_pointer[-1]; + callargs_st = callargs; + null = stack_pointer[-3]; + func_st = func; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + (void)null; + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + stack_pointer[-2] = callargs_st; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *result_o = PyObject_Call(func, callargs, kwargs); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(kwargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(func_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + JUMP_TO_LABEL(error); + } + result = PyStackRef_FromPyObjectSteal(result_o); + } + // _CHECK_PERIODIC_AT_END + { + stack_pointer[0] = result; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_EX_PY) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_EX_PY; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(CALL_EX_PY); + static_assert(INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX == 1, "incorrect cache size"); + _PyStackRef func; + _PyStackRef callargs; + _PyStackRef func_st; + _PyStackRef callargs_st; + _PyStackRef kwargs_st; + _PyStackRef ex_frame; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(CALL_FUNCTION_EX); + assert(_PyOpcode_Deopt[opcode] == (CALL_FUNCTION_EX)); + JUMP_TO_PREDICTED(CALL_FUNCTION_EX); + } + } + // _MAKE_CALLARGS_A_TUPLE + { + callargs = stack_pointer[-2]; + func = stack_pointer[-4]; + PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); + if (!PyTuple_CheckExact(callargs_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *tuple_o = PySequence_Tuple(callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (tuple_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callargs; + callargs = PyStackRef_FromPyObjectSteal(tuple_o); + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _CHECK_IS_PY_CALLABLE_EX + { + func_st = func; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) != &PyFunction_Type) { + UPDATE_MISS_STATS(CALL_FUNCTION_EX); + assert(_PyOpcode_Deopt[opcode] == (CALL_FUNCTION_EX)); + JUMP_TO_PREDICTED(CALL_FUNCTION_EX); + } + if (((PyFunctionObject *)func)->vectorcall != _PyFunction_Vectorcall) { + UPDATE_MISS_STATS(CALL_FUNCTION_EX); + assert(_PyOpcode_Deopt[opcode] == (CALL_FUNCTION_EX)); + JUMP_TO_PREDICTED(CALL_FUNCTION_EX); + } + } + // _PY_FRAME_EX + { + kwargs_st = stack_pointer[-1]; + callargs_st = callargs; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); + assert(PyTuple_CheckExact(callargs)); + assert(Py_TYPE(func) == &PyFunction_Type); + assert(((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall); + PyObject *kwargs = PyStackRef_IsNull(kwargs_st) ? NULL : PyStackRef_AsPyObjectSteal(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); + int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( + tstate, func_st, locals, + nargs, callargs, kwargs, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (new_frame == NULL) { + JUMP_TO_LABEL(error); + } + ex_frame = PyStackRef_Wrap(new_frame); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + new_frame = ex_frame; + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(CALL_FUNCTION_EX) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_FUNCTION_EX; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(CALL_FUNCTION_EX); + PREDICTED_CALL_FUNCTION_EX:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + opcode = CALL_FUNCTION_EX; + _PyStackRef func; + _PyStackRef callargs; + _PyStackRef func_st; + _PyStackRef null; + _PyStackRef callargs_st; + _PyStackRef kwargs_st; + _PyStackRef result; + // _SPECIALIZE_CALL_FUNCTION_EX + { + func = stack_pointer[-4]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_CallFunctionEx(func, next_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(CALL_FUNCTION_EX); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + // _MAKE_CALLARGS_A_TUPLE + { + callargs = stack_pointer[-2]; + PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); + if (!PyTuple_CheckExact(callargs_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *tuple_o = PySequence_Tuple(callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (tuple_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callargs; + callargs = PyStackRef_FromPyObjectSteal(tuple_o); + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _DO_CALL_FUNCTION_EX + { + kwargs_st = stack_pointer[-1]; + callargs_st = callargs; + null = stack_pointer[-3]; + func_st = func; + (void)null; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func); + PyObject *result_o; + assert(!_PyErr_Occurred(tstate)); + if (opcode == INSTRUMENTED_CALL_FUNCTION_EX) { + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + assert(PyTuple_CheckExact(callargs)); + PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ? + PyTuple_GET_ITEM(callargs, 0) : &_PyInstrumentation_MISSING; + stack_pointer[-2] = callargs_st; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, func, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + result_o = PyObject_Call(func, callargs, kwargs); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (!PyFunction_Check(func) && !PyMethod_Check(func)) { + if (result_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, func, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, func, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_CLEAR(result_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + } + } + else { + if (Py_TYPE(func) == &PyFunction_Type && + !IS_PEP523_HOOKED(tstate) && + ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { + PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_IsNull(kwargs_st) ? NULL : PyStackRef_AsPyObjectSteal(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); + int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( + tstate, func_st, locals, + nargs, callargs, kwargs, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (new_frame == NULL) { + JUMP_TO_LABEL(error); + } + assert( 2u == 1 + INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX); + frame->return_offset = 2u ; + DISPATCH_INLINED(new_frame); + } + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + stack_pointer[-2] = callargs_st; + _PyFrame_SetStackPointer(frame, stack_pointer); + result_o = PyObject_Call(func, callargs, kwargs); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(kwargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(func_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + JUMP_TO_LABEL(error); + } + result = PyStackRef_FromPyObjectSteal(result_o); + } + // _CHECK_PERIODIC_AT_END + { + stack_pointer[0] = result; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_INTRINSIC_1) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_INTRINSIC_1; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CALL_INTRINSIC_1); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + // _CALL_INTRINSIC_1 + { + value = stack_pointer[-1]; + assert(oparg <= MAX_INTRINSIC_1); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + v = value; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = v; + stack_pointer[-1] = res; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(CALL_INTRINSIC_2) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_INTRINSIC_2; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CALL_INTRINSIC_2); + _PyStackRef value2_st; + _PyStackRef value1_st; + _PyStackRef res; + _PyStackRef vs1; + _PyStackRef vs2; + _PyStackRef value; + // _CALL_INTRINSIC_2 + { + value1_st = stack_pointer[-1]; + value2_st = stack_pointer[-2]; + assert(oparg <= MAX_INTRINSIC_2); + PyObject *value1 = PyStackRef_AsPyObjectBorrow(value1_st); + PyObject *value2 = PyStackRef_AsPyObjectBorrow(value2_st); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyIntrinsics_BinaryFunctions[oparg].func(tstate, value2, value1); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + vs1 = value1_st; + vs2 = value2_st; + } + // _POP_TOP + { + value = vs2; + stack_pointer[-2] = res; + stack_pointer[-1] = vs1; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = vs1; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(CALL_ISINSTANCE) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_ISINSTANCE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_ISINSTANCE); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef null; + _PyStackRef callable; + _PyStackRef instance; + _PyStackRef cls; + _PyStackRef res; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_THIRD_NULL + { + null = stack_pointer[-3]; + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _GUARD_CALLABLE_ISINSTANCE + { + callable = stack_pointer[-4]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.isinstance) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_ISINSTANCE + { + cls = stack_pointer[-1]; + instance = stack_pointer[-2]; + STAT_INC(CALL, hit); + PyObject *inst_o = PyStackRef_AsPyObjectBorrow(instance); + PyObject *cls_o = PyStackRef_AsPyObjectBorrow(cls); + _PyFrame_SetStackPointer(frame, stack_pointer); + int retval = PyObject_IsInstance(inst_o, cls_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (retval < 0) { + JUMP_TO_LABEL(error); + } + (void)null; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(cls); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(instance); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callable); + stack_pointer = _PyFrame_GetStackPointer(frame); + res = retval ? PyStackRef_True : PyStackRef_False; + assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); + } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(CALL_KW) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_KW; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_KW); + PREDICTED_CALL_KW:; + _Py_CODEUNIT* const this_instr = next_instr - 4; + (void)this_instr; + opcode = CALL_KW; + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef kwnames; + _PyStackRef res; + // _SPECIALIZE_CALL_KW + { + self_or_null = stack_pointer[-2 - oparg]; + callable = stack_pointer[-3 - oparg]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_CallKw(callable, next_instr, oparg + !PyStackRef_IsNull(self_or_null)); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(CALL_KW); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + /* Skip 2 cache entries */ + // _MAYBE_EXPAND_METHOD_KW + { + if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *self = ((PyMethodObject *)callable_o)->im_self; + self_or_null = PyStackRef_FromPyObjectNew(self); + PyObject *method = ((PyMethodObject *)callable_o)->im_func; + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(method); + stack_pointer[-3 - oparg] = callable; + stack_pointer[-2 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _DO_CALL_KW + { + kwnames = stack_pointer[-1]; + args = &stack_pointer[-1 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); + if (Py_TYPE(callable_o) == &PyFunction_Type && + !IS_PEP523_HOOKED(tstate) && + ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) + { + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); + stack_pointer[-3 - oparg] = callable; + stack_pointer[-2 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, callable, locals, + arguments, positional_args, kwnames_o, frame + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (new_frame == NULL) { + JUMP_TO_LABEL(error); + } + assert( 4u == 1 + INLINE_CACHE_ENTRIES_CALL_KW); + frame->return_offset = 4u ; + DISPATCH_INLINED(new_frame); + } + stack_pointer[-3 - oparg] = callable; + stack_pointer[-2 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject* res_o = _Py_VectorCallInstrumentation_StackRefSteal( + callable, + arguments, + total_args, + kwnames, + opcode == INSTRUMENTED_CALL_KW, + frame, + this_instr, + tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + stack_pointer += -3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + stack_pointer[-3 - oparg] = res; + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(CALL_KW_BOUND_METHOD) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_KW_BOUND_METHOD; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_KW_BOUND_METHOD); + static_assert(INLINE_CACHE_ENTRIES_CALL_KW == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef null; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef kwnames; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + } + // _CHECK_METHOD_VERSION_KW + { + null = stack_pointer[-2 - oparg]; + callable = stack_pointer[-3 - oparg]; + uint32_t func_version = read_u32(&this_instr[2].cache); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (Py_TYPE(callable_o) != &PyMethod_Type) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + PyObject *func = ((PyMethodObject *)callable_o)->im_func; + if (!PyFunction_Check(func)) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + if (((PyFunctionObject *)func)->func_version != func_version) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + } + // _EXPAND_METHOD_KW + { + self_or_null = null; + assert(PyStackRef_IsNull(self_or_null)); + _PyStackRef callable_s = callable; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(Py_TYPE(callable_o) == &PyMethod_Type); + self_or_null = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + callable = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + assert(PyStackRef_FunctionCheck(callable)); + stack_pointer[-3 - oparg] = callable; + stack_pointer[-2 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callable_s); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // flush + // _PY_FRAME_KW + { + kwnames = stack_pointer[-1]; + args = &stack_pointer[-1 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); + int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); + assert(Py_TYPE(callable_o) == &PyFunction_Type); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( + tstate, callable, locals, + arguments, positional_args, kwnames_o, frame + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (temp == NULL) { + JUMP_TO_LABEL(error); + } + new_frame = PyStackRef_Wrap(temp); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(CALL_KW_NON_PY) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_KW_NON_PY; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_KW_NON_PY); + opcode = CALL_KW_NON_PY; + static_assert(INLINE_CACHE_ENTRIES_CALL_KW == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef kwnames; + _PyStackRef res; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _CHECK_IS_NOT_PY_CALLABLE_KW + { + callable = stack_pointer[-3 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + if (Py_TYPE(callable_o) == &PyMethod_Type) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + } + // _CALL_KW_NON_PY + { + kwnames = stack_pointer[-1]; + args = &stack_pointer[-1 - oparg]; + self_or_null = stack_pointer[-2 - oparg]; + #if TIER_ONE + assert(opcode != INSTRUMENTED_CALL); + #endif + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _Py_VectorCall_StackRefSteal( + callable, + arguments, + total_args, + kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + stack_pointer += -3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _CHECK_PERIODIC_AT_END + { + stack_pointer[-3 - oparg] = res; + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_KW_PY) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_KW_PY; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_KW_PY); + static_assert(INLINE_CACHE_ENTRIES_CALL_KW == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef kwnames; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + } + // _CHECK_FUNCTION_VERSION_KW + { + callable = stack_pointer[-3 - oparg]; + uint32_t func_version = read_u32(&this_instr[2].cache); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + } + // _CHECK_RECURSION_REMAINING + { + if (tstate->py_recursion_remaining <= 1) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + } + // _PY_FRAME_KW + { + kwnames = stack_pointer[-1]; + args = &stack_pointer[-1 - oparg]; + self_or_null = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); + int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); + assert(Py_TYPE(callable_o) == &PyFunction_Type); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( + tstate, callable, locals, + arguments, positional_args, kwnames_o, frame + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (temp == NULL) { + JUMP_TO_LABEL(error); + } + new_frame = PyStackRef_Wrap(temp); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(CALL_LEN) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_LEN; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_LEN); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef null; + _PyStackRef callable; + _PyStackRef arg; + _PyStackRef res; + _PyStackRef a; + _PyStackRef c; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_NOS_NULL + { + null = stack_pointer[-2]; + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _GUARD_CALLABLE_LEN + { + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.len) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_LEN + { + arg = stack_pointer[-1]; + STAT_INC(CALL, hit); + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_ssize_t len_i = PyObject_Length(arg_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (len_i < 0) { + JUMP_TO_LABEL(error); + } + PyObject *res_o = PyLong_FromSsize_t(len_i); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + a = arg; + c = callable; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = c; + stack_pointer[-3] = res; + stack_pointer[-2] = a; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = a; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(CALL_LIST_APPEND) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_LIST_APPEND; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_LIST_APPEND); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef nos; + _PyStackRef self; + _PyStackRef arg; + _PyStackRef none; + _PyStackRef c; + _PyStackRef s; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_CALLABLE_LIST_APPEND + { + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.list_append) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _GUARD_NOS_NOT_NULL + { + nos = stack_pointer[-2]; + if (PyStackRef_IsNull(nos)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _GUARD_NOS_LIST + { + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyList_CheckExact(o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_LIST_APPEND + { + arg = stack_pointer[-1]; + self = nos; + assert(oparg == 1); + PyObject *self_o = PyStackRef_AsPyObjectBorrow(self); + if (!LOCK_OBJECT(self_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + STAT_INC(CALL, hit); + int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); + UNLOCK_OBJECT(self_o); + if (err) { + JUMP_TO_LABEL(error); + } + c = callable; + s = self; + none = PyStackRef_None; + } + // _POP_TOP + { + value = s; + stack_pointer[-3] = none; + stack_pointer[-2] = c; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = c; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(CALL_METHOD_DESCRIPTOR_FAST) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_METHOD_DESCRIPTOR_FAST; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (method->d_method->ml_flags != METH_FASTCALL) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args == 0) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_METHOD_DESCRIPTOR_FAST + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + assert(self != NULL); + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyCFunctionFast cfunc = _PyCFunctionFast_CAST(method->d_method->ml_meth); + PyObject *res_o = _PyCallMethodDescriptorFast_StackRef( + callable, + cfunc, + self, + arguments, + total_args + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP_OPARG + { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef_CloseStack(args, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = self_or_null; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (method->d_method->ml_flags != (METH_FASTCALL|METH_KEYWORDS)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + if (total_args == 0) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + assert(self != NULL); + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyCFunctionFastWithKeywords cfunc = _PyCFunctionFastWithKeywords_CAST(method->d_method->ml_meth); + PyObject *res_o = _PyCallMethodDescriptorFastWithKeywords_StackRef( + callable, + cfunc, + self, + arguments, + total_args + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP_OPARG + { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef_CloseStack(args, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = self_or_null; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_METHOD_DESCRIPTOR_NOARGS) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_METHOD_DESCRIPTOR_NOARGS; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_NOARGS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (method->d_method->ml_flags != METH_NOARGS) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args != 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_RECURSION_LIMIT + { + if (_Py_ReachedRecursionLimit(tstate)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_METHOD_DESCRIPTOR_NOARGS + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + assert(oparg == 1 || !PyStackRef_IsNull(self_or_null)); + if (!PyStackRef_IsNull(self_or_null)) { + args--; + } + _PyStackRef self_stackref = args[0]; + PyObject *self = PyStackRef_AsPyObjectBorrow(self_stackref); + STAT_INC(CALL, hit); + PyCFunction cfunc = method->d_method->ml_meth; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, self, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + c = callable; + s = args[0]; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = s; + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = c; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_METHOD_DESCRIPTOR_O) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_METHOD_DESCRIPTOR_O; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_METHOD_DESCRIPTOR_O); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef a; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_CALLABLE_METHOD_DESCRIPTOR_O + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (method->d_method->ml_flags != METH_O) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args != 2) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_RECURSION_LIMIT + { + if (_Py_ReachedRecursionLimit(tstate)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_METHOD_DESCRIPTOR_O + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + } + STAT_INC(CALL, hit); + PyCFunction cfunc = method->d_method->ml_meth; + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + PyObject *arg = PyStackRef_AsPyObjectBorrow(arguments[1]); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, self, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + c = callable; + s = arguments[0]; + a = arguments[1]; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = a; + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer[-oparg] = s; + stack_pointer += 1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = s; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = c; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_NON_PY_GENERAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_NON_PY_GENERAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_NON_PY_GENERAL); + opcode = CALL_NON_PY_GENERAL; + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef res; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _CHECK_IS_NOT_PY_CALLABLE + { + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (Py_TYPE(callable_o) == &PyMethod_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_NON_PY_GENERAL + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + #if TIER_ONE + assert(opcode != INSTRUMENTED_CALL); + #endif + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _Py_VectorCall_StackRefSteal( + callable, + arguments, + total_args, + PyStackRef_NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _CHECK_PERIODIC_AT_END + { + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_PY_EXACT_ARGS) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_PY_EXACT_ARGS; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_PY_EXACT_ARGS); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_FUNCTION_VERSION + { + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = read_u32(&this_instr[2].cache); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_FUNCTION_EXACT_ARGS + { + self_or_null = stack_pointer[-1 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(PyFunction_Check(callable_o)); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + PyCodeObject *code = (PyCodeObject *)func->func_code; + if (code->co_argcount != oparg + (!PyStackRef_IsNull(self_or_null))) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_STACK_SPACE + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + PyCodeObject *code = (PyCodeObject *)func->func_code; + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_RECURSION_REMAINING + { + if (tstate->py_recursion_remaining <= 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _INIT_CALL_PY_EXACT_ARGS + { + args = &stack_pointer[-oparg]; + int has_self = !PyStackRef_IsNull(self_or_null); + STAT_INC(CALL, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); + _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; + pushed_frame->localsplus[0] = self_or_null; + for (int i = 0; i < oparg; i++) { + first_non_self_local[i] = args[i]; + } + new_frame = PyStackRef_Wrap(pushed_frame); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(CALL_PY_GENERAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_PY_GENERAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_PY_GENERAL); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_FUNCTION_VERSION + { + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = read_u32(&this_instr[2].cache); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyFunction_Check(callable_o)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CHECK_RECURSION_REMAINING + { + if (tstate->py_recursion_remaining <= 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _PY_FRAME_GENERAL + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + args--; + total_args++; + } + assert(Py_TYPE(callable_o) == &PyFunction_Type); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( + tstate, callable, locals, + args, total_args, NULL, frame + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (temp == NULL) { + JUMP_TO_LABEL(error); + } + new_frame = PyStackRef_Wrap(temp); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(CALL_STR_1) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_STR_1; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_STR_1); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef null; + _PyStackRef callable; + _PyStackRef arg; + _PyStackRef res; + _PyStackRef a; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_NOS_NULL + { + null = stack_pointer[-2]; + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _GUARD_CALLABLE_STR_1 + { + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyUnicode_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_STR_1 + { + arg = stack_pointer[-1]; + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + assert(oparg == 1); + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyObject_Str(arg_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + a = arg; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = a; + stack_pointer[-3] = res; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_TUPLE_1) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_TUPLE_1; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_TUPLE_1); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef null; + _PyStackRef callable; + _PyStackRef arg; + _PyStackRef res; + _PyStackRef a; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_NOS_NULL + { + null = stack_pointer[-2]; + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _GUARD_CALLABLE_TUPLE_1 + { + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyTuple_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_TUPLE_1 + { + arg = stack_pointer[-1]; + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + assert(oparg == 1); + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PySequence_Tuple(arg_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + a = arg; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = a; + stack_pointer[-3] = res; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(CALL_TYPE_1) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_TYPE_1; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(CALL_TYPE_1); + static_assert(INLINE_CACHE_ENTRIES_CALL == 3, "incorrect cache size"); + _PyStackRef null; + _PyStackRef callable; + _PyStackRef arg; + _PyStackRef res; + _PyStackRef a; + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _GUARD_NOS_NULL + { + null = stack_pointer[-2]; + if (!PyStackRef_IsNull(null)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _GUARD_CALLABLE_TYPE_1 + { + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyType_Type) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_TYPE_1 + { + arg = stack_pointer[-1]; + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + assert(oparg == 1); + STAT_INC(CALL, hit); + a = arg; + res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); + } + // _POP_TOP + { + value = a; + stack_pointer[-3] = res; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(CHECK_EG_MATCH) { + #if _Py_TAIL_CALL_INTERP + int opcode = CHECK_EG_MATCH; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CHECK_EG_MATCH); + _PyStackRef exc_value_st; + _PyStackRef match_type_st; + _PyStackRef rest; + _PyStackRef match; + match_type_st = stack_pointer[-1]; + exc_value_st = stack_pointer[-2]; + PyObject *exc_value = PyStackRef_AsPyObjectBorrow(exc_value_st); + PyObject *match_type = PyStackRef_AsPyObjectBorrow(match_type_st); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyEval_CheckExceptStarTypeValid(tstate, match_type); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = match_type_st; + match_type_st = PyStackRef_NULL; + stack_pointer[-1] = match_type_st; + PyStackRef_CLOSE(tmp); + tmp = exc_value_st; + exc_value_st = PyStackRef_NULL; + stack_pointer[-2] = exc_value_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + PyObject *match_o = NULL; + PyObject *rest_o = NULL; + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = _PyEval_ExceptionGroupMatch(frame, exc_value, match_type, + &match_o, &rest_o); + _PyStackRef tmp = match_type_st; + match_type_st = PyStackRef_NULL; + stack_pointer[-1] = match_type_st; + PyStackRef_CLOSE(tmp); + tmp = exc_value_st; + exc_value_st = PyStackRef_NULL; + stack_pointer[-2] = exc_value_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (res < 0) { + JUMP_TO_LABEL(error); + } + assert((match_o == NULL) == (rest_o == NULL)); + if (match_o == NULL) { + JUMP_TO_LABEL(error); + } + if (!Py_IsNone(match_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyErr_SetHandledException(match_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + rest = PyStackRef_FromPyObjectSteal(rest_o); + match = PyStackRef_FromPyObjectSteal(match_o); + stack_pointer[0] = rest; + stack_pointer[1] = match; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(CHECK_EXC_MATCH) { + #if _Py_TAIL_CALL_INTERP + int opcode = CHECK_EXC_MATCH; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CHECK_EXC_MATCH); + _PyStackRef left; + _PyStackRef right; + _PyStackRef b; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyExceptionInstance_Check(left_o)); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyEval_CheckExceptTypeValid(tstate, right_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = PyErr_GivenExceptionMatches(left_o, right_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(right); + stack_pointer = _PyFrame_GetStackPointer(frame); + b = res ? PyStackRef_True : PyStackRef_False; + stack_pointer[0] = b; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(CLEANUP_THROW) { + #if _Py_TAIL_CALL_INTERP + int opcode = CLEANUP_THROW; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CLEANUP_THROW); + _PyStackRef sub_iter; + _PyStackRef null_in; + _PyStackRef last_sent_val; + _PyStackRef exc_value_st; + _PyStackRef none; + _PyStackRef null_out; + _PyStackRef value; + exc_value_st = stack_pointer[-1]; + last_sent_val = stack_pointer[-2]; + null_in = stack_pointer[-3]; + sub_iter = stack_pointer[-4]; + PyObject *exc_value = PyStackRef_AsPyObjectBorrow(exc_value_st); + #if !_Py_TAIL_CALL_INTERP + assert(throwflag); + #endif + assert(exc_value && PyExceptionInstance_Check(exc_value)); + _PyFrame_SetStackPointer(frame, stack_pointer); + int matches = PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (matches) { + value = PyStackRef_FromPyObjectNew(((PyStopIterationObject *)exc_value)->value); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = sub_iter; + sub_iter = value; + stack_pointer[-4] = sub_iter; + PyStackRef_CLOSE(tmp); + tmp = exc_value_st; + exc_value_st = PyStackRef_NULL; + stack_pointer[-1] = exc_value_st; + PyStackRef_CLOSE(tmp); + tmp = last_sent_val; + last_sent_val = PyStackRef_NULL; + stack_pointer[-2] = last_sent_val; + PyStackRef_CLOSE(tmp); + tmp = null_in; + null_in = PyStackRef_NULL; + stack_pointer[-3] = null_in; + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -4; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + null_out = null_in; + none = PyStackRef_None; + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value)); + monitor_reraise(tstate, frame, this_instr); + JUMP_TO_LABEL(exception_unwind); + } + stack_pointer[0] = none; + stack_pointer[1] = null_out; + stack_pointer[2] = value; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(COMPARE_OP) { + #if _Py_TAIL_CALL_INTERP + int opcode = COMPARE_OP; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(COMPARE_OP); + PREDICTED_COMPARE_OP:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + // _SPECIALIZE_COMPARE_OP + { + right = stack_pointer[-1]; + left = stack_pointer[-2]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_CompareOp(left, right, next_instr, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(COMPARE_OP); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + // _COMPARE_OP + { + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert((oparg >> 5) <= Py_GE); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyObject_RichCompare(left_o, right_o, oparg >> 5); + _PyStackRef tmp = right; + right = PyStackRef_NULL; + stack_pointer[-1] = right; + PyStackRef_CLOSE(tmp); + tmp = left; + left = PyStackRef_NULL; + stack_pointer[-2] = left; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + if (oparg & 16) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int res_bool = PyObject_IsTrue(res_o); + Py_DECREF(res_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_bool < 0) { + JUMP_TO_LABEL(error); + } + res = res_bool ? PyStackRef_True : PyStackRef_False; + } + else { + res = PyStackRef_FromPyObjectSteal(res_o); + } + } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(COMPARE_OP_FLOAT) { + #if _Py_TAIL_CALL_INTERP + int opcode = COMPARE_OP_FLOAT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(COMPARE_OP_FLOAT); + static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); + _PyStackRef value; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_FLOAT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyFloat_CheckExact(value_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } + } + // _GUARD_NOS_FLOAT + { + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!PyFloat_CheckExact(left_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } + } + /* Skip 1 cache entry */ + // _COMPARE_OP_FLOAT + { + right = value; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + STAT_INC(COMPARE_OP, hit); + double dleft = PyFloat_AS_DOUBLE(left_o); + double dright = PyFloat_AS_DOUBLE(right_o); + int sign_ish = COMPARISON_BIT(dleft, dright); + l = left; + r = right; + res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; + } + // _POP_TOP_FLOAT + { + value = r; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + // _POP_TOP_FLOAT + { + value = l; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(COMPARE_OP_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = COMPARE_OP_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(COMPARE_OP_INT); + static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); + _PyStackRef value; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } + } + // _GUARD_NOS_INT + { + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!_PyLong_CheckExactAndCompact(left_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } + } + /* Skip 1 cache entry */ + // _COMPARE_OP_INT + { + right = value; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(_PyLong_IsCompact((PyLongObject *)left_o)); + assert(_PyLong_IsCompact((PyLongObject *)right_o)); + STAT_INC(COMPARE_OP, hit); + assert(_PyLong_DigitCount((PyLongObject *)left_o) <= 1 && + _PyLong_DigitCount((PyLongObject *)right_o) <= 1); + Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left_o); + Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o); + int sign_ish = COMPARISON_BIT(ileft, iright); + l = left; + r = right; + res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; + } + // _POP_TOP_INT + { + value = r; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_INT + { + value = l; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(COMPARE_OP_STR) { + #if _Py_TAIL_CALL_INTERP + int opcode = COMPARE_OP_STR; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(COMPARE_OP_STR); + static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size"); + _PyStackRef value; + _PyStackRef nos; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_UNICODE + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyUnicode_CheckExact(value_o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } + } + // _GUARD_NOS_UNICODE + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UPDATE_MISS_STATS(COMPARE_OP); + assert(_PyOpcode_Deopt[opcode] == (COMPARE_OP)); + JUMP_TO_PREDICTED(COMPARE_OP); + } + } + /* Skip 1 cache entry */ + // _COMPARE_OP_STR + { + right = value; + left = nos; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + STAT_INC(COMPARE_OP, hit); + int eq = _PyUnicode_Equal(left_o, right_o); + assert((oparg >> 5) == Py_EQ || (oparg >> 5) == Py_NE); + l = left; + r = right; + assert(eq == 0 || eq == 1); + assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); + assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); + res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? PyStackRef_True : PyStackRef_False; + } + // _POP_TOP_UNICODE + { + value = r; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + // _POP_TOP_UNICODE + { + value = l; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(CONTAINS_OP) { + #if _Py_TAIL_CALL_INTERP + int opcode = CONTAINS_OP; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(CONTAINS_OP); + PREDICTED_CONTAINS_OP:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + _PyStackRef right; + _PyStackRef left; + _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; + // _SPECIALIZE_CONTAINS_OP + { + right = stack_pointer[-1]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_ContainsOp(right, next_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(CONTAINS_OP); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + // _CONTAINS_OP + { + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = PySequence_Contains(right_o, left_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res < 0) { + JUMP_TO_LABEL(error); + } + b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = b; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(CONTAINS_OP_DICT) { + #if _Py_TAIL_CALL_INTERP + int opcode = CONTAINS_OP_DICT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(CONTAINS_OP_DICT); + static_assert(INLINE_CACHE_ENTRIES_CONTAINS_OP == 1, "incorrect cache size"); + _PyStackRef tos; + _PyStackRef left; + _PyStackRef right; + _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; + // _GUARD_TOS_ANY_DICT + { + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyAnyDict_CheckExact(o)) { + UPDATE_MISS_STATS(CONTAINS_OP); + assert(_PyOpcode_Deopt[opcode] == (CONTAINS_OP)); + JUMP_TO_PREDICTED(CONTAINS_OP); + } + } + /* Skip 1 cache entry */ + // _CONTAINS_OP_DICT + { + right = tos; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyAnyDict_CheckExact(right_o)); + STAT_INC(CONTAINS_OP, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = PyDict_Contains(right_o, left_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res < 0) { + JUMP_TO_LABEL(error); + } + b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = b; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(CONTAINS_OP_SET) { + #if _Py_TAIL_CALL_INTERP + int opcode = CONTAINS_OP_SET; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(CONTAINS_OP_SET); + static_assert(INLINE_CACHE_ENTRIES_CONTAINS_OP == 1, "incorrect cache size"); + _PyStackRef tos; + _PyStackRef left; + _PyStackRef right; + _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; + // _GUARD_TOS_ANY_SET + { + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyAnySet_CheckExact(o)) { + UPDATE_MISS_STATS(CONTAINS_OP); + assert(_PyOpcode_Deopt[opcode] == (CONTAINS_OP)); + JUMP_TO_PREDICTED(CONTAINS_OP); + } + } + /* Skip 1 cache entry */ + // _CONTAINS_OP_SET + { + right = tos; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyAnySet_CheckExact(right_o)); + STAT_INC(CONTAINS_OP, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = _PySet_Contains((PySetObject *)right_o, left_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res < 0) { + JUMP_TO_LABEL(error); + } + b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = b; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(CONVERT_VALUE) { + #if _Py_TAIL_CALL_INTERP + int opcode = CONVERT_VALUE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(CONVERT_VALUE); + _PyStackRef value; + _PyStackRef result; + value = stack_pointer[-1]; + conversion_func conv_fn; + assert(oparg >= FVC_STR && oparg <= FVC_ASCII); + conv_fn = _PyEval_ConversionFuncs[oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *result_o = conv_fn(PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + JUMP_TO_LABEL(error); + } + result = PyStackRef_FromPyObjectSteal(result_o); + stack_pointer[0] = result; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(COPY) { + #if _Py_TAIL_CALL_INTERP + int opcode = COPY; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(COPY); + _PyStackRef bottom; + _PyStackRef top; + bottom = stack_pointer[-1 - (oparg-1)]; + top = PyStackRef_DUP(bottom); + stack_pointer[0] = top; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(COPY_FREE_VARS) { + #if _Py_TAIL_CALL_INTERP + int opcode = COPY_FREE_VARS; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(COPY_FREE_VARS); + PyCodeObject *co = _PyFrame_GetCode(frame); + assert(PyStackRef_FunctionCheck(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); + PyObject *closure = func->func_closure; + assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; + for (int i = 0; i < oparg; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + frame->localsplus[offset + i] = PyStackRef_FromPyObjectNew(o); + } + DISPATCH(); + } + + TARGET(DELETE_ATTR) { + #if _Py_TAIL_CALL_INTERP + int opcode = DELETE_ATTR; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DELETE_ATTR); + _PyStackRef owner; + owner = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyObject_DelAttr(PyStackRef_AsPyObjectBorrow(owner), name); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + DISPATCH(); + } + + TARGET(DELETE_DEREF) { + #if _Py_TAIL_CALL_INTERP + int opcode = DELETE_DEREF; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DELETE_DEREF); + PyObject *cell = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + PyObject *oldobj = PyCell_SwapTakeRef((PyCellObject *)cell, NULL); + if (oldobj == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(oldobj); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(DELETE_FAST) { + #if _Py_TAIL_CALL_INTERP + int opcode = DELETE_FAST; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DELETE_FAST); + _PyStackRef v = GETLOCAL(oparg); + if (PyStackRef_IsNull(v)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_NULL; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(DELETE_GLOBAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = DELETE_GLOBAL; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DELETE_GLOBAL); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyDict_Pop(GLOBALS(), name, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + if (err == 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + DISPATCH(); + } + + TARGET(DELETE_NAME) { + #if _Py_TAIL_CALL_INTERP + int opcode = DELETE_NAME; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DELETE_NAME); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *ns = LOCALS(); + int err; + if (ns == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_SystemError, + "no locals when deleting %R", name); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + err = PyObject_DelItem(ns, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, + name); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + DISPATCH(); + } + + TARGET(DELETE_SUBSCR) { + #if _Py_TAIL_CALL_INTERP + int opcode = DELETE_SUBSCR; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DELETE_SUBSCR); + _PyStackRef container; + _PyStackRef sub; + sub = stack_pointer[-1]; + container = stack_pointer[-2]; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyObject_DelItem(PyStackRef_AsPyObjectBorrow(container), + PyStackRef_AsPyObjectBorrow(sub)); + _PyStackRef tmp = sub; + sub = PyStackRef_NULL; + stack_pointer[-1] = sub; + PyStackRef_CLOSE(tmp); + tmp = container; + container = PyStackRef_NULL; + stack_pointer[-2] = container; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (err) { + JUMP_TO_LABEL(error); + } + DISPATCH(); + } + + TARGET(DICT_MERGE) { + #if _Py_TAIL_CALL_INTERP + int opcode = DICT_MERGE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DICT_MERGE); + _PyStackRef callable; + _PyStackRef dict; + _PyStackRef update; + _PyStackRef u; + _PyStackRef value; + // _DICT_MERGE + { + update = stack_pointer[-1]; + dict = stack_pointer[-2 - (oparg - 1)]; + callable = stack_pointer[-5 - (oparg - 1)]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); + PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); + PyObject *dupkey = NULL; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyDict_MergeUniq(dict_o, update_o, &dupkey); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatKwargsError(tstate, callable_o, update_o, dupkey); + Py_XDECREF(dupkey); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + u = update; + } + // _POP_TOP + { + value = u; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(DICT_UPDATE) { + #if _Py_TAIL_CALL_INTERP + int opcode = DICT_UPDATE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(DICT_UPDATE); + _PyStackRef dict; + _PyStackRef update; + _PyStackRef upd; + _PyStackRef value; + // _DICT_UPDATE + { + update = stack_pointer[-1]; + dict = stack_pointer[-2 - (oparg - 1)]; + PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); + PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyDict_Update(dict_o, update_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + int matches = _PyErr_ExceptionMatches(tstate, PyExc_AttributeError); + if (matches) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *exc = _PyErr_GetRaisedException(tstate); + int has_keys = PyObject_HasAttrWithError(update_o, &_Py_ID(keys)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (has_keys == 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, + "'%T' object is not a mapping", + update_o); + Py_DECREF(exc); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_ChainExceptions1(exc); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + JUMP_TO_LABEL(error); + } + upd = update; + } + // _POP_TOP + { + value = upd; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(END_ASYNC_FOR) { + #if _Py_TAIL_CALL_INTERP + int opcode = END_ASYNC_FOR; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(END_ASYNC_FOR); + _PyStackRef awaitable_st; + _PyStackRef exc_st; + exc_st = stack_pointer[-1]; + awaitable_st = stack_pointer[-2]; + JUMPBY(0); + (void)oparg; + PyObject *exc = PyStackRef_AsPyObjectBorrow(exc_st); + assert(exc && PyExceptionInstance_Check(exc)); + _PyFrame_SetStackPointer(frame, stack_pointer); + int matches = PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (matches) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = exc_st; + exc_st = PyStackRef_NULL; + stack_pointer[-1] = exc_st; + PyStackRef_CLOSE(tmp); + tmp = awaitable_st; + awaitable_st = PyStackRef_NULL; + stack_pointer[-2] = awaitable_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + } + else { + Py_INCREF(exc); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetRaisedException(tstate, exc); + monitor_reraise(tstate, frame, this_instr); + JUMP_TO_LABEL(exception_unwind); + } + DISPATCH(); + } + + TARGET(END_FOR) { + #if _Py_TAIL_CALL_INTERP + int opcode = END_FOR; + (void)(opcode); + #endif + next_instr += 1; + INSTRUCTION_STATS(END_FOR); + _PyStackRef value; + value = stack_pointer[-1]; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(END_SEND) { + #if _Py_TAIL_CALL_INTERP + int opcode = END_SEND; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(END_SEND); + _PyStackRef receiver; + _PyStackRef index_or_null; + _PyStackRef value; + _PyStackRef val; + value = stack_pointer[-1]; + index_or_null = stack_pointer[-2]; + receiver = stack_pointer[-3]; + val = value; + (void)index_or_null; + stack_pointer[-3] = val; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(receiver); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(ENTER_EXECUTOR) { + #if _Py_TAIL_CALL_INTERP + int opcode = ENTER_EXECUTOR; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(ENTER_EXECUTOR); + opcode = ENTER_EXECUTOR; + #ifdef _Py_TIER2 + PyCodeObject *code = _PyFrame_GetCode(frame); + _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; + if (IS_JIT_TRACING()) { + int og_opcode = executor->vm_data.opcode; + int og_oparg = (oparg & ~255) | executor->vm_data.oparg; + next_instr = this_instr; + if (_PyJit_EnterExecutorShouldStopTracing(og_opcode)) { + if (_PyOpcode_Caches[_PyOpcode_Deopt[og_opcode]]) { + PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter); + } + opcode = og_opcode; + oparg = og_oparg; + DISPATCH_GOTO_NON_TRACING(); + } + JUMP_TO_LABEL(stop_tracing); + } + assert(executor->vm_data.index == INSTR_OFFSET() - 1); + assert(executor->vm_data.code == code); + assert(executor->vm_data.valid); + assert(tstate->current_executor == NULL); + uintptr_t iversion = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(code->_co_instrumentation_version); + if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) != iversion) { + opcode = executor->vm_data.opcode; + oparg = (oparg & ~255) | executor->vm_data.oparg; + next_instr = this_instr; + if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]]) { + PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter); + } + DISPATCH_GOTO(); + } + assert(executor != tstate->interp->cold_executor); + tstate->jit_exit = NULL; + TIER1_TO_TIER2(executor); + #else + Py_FatalError("ENTER_EXECUTOR is not supported in this build"); + #endif /* _Py_TIER2 */ + } + + TARGET(EXIT_INIT_CHECK) { + #if _Py_TAIL_CALL_INTERP + int opcode = EXIT_INIT_CHECK; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(EXIT_INIT_CHECK); + _PyStackRef should_be_none; + should_be_none = stack_pointer[-1]; + if (!PyStackRef_IsNone(should_be_none)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyErr_Format(PyExc_TypeError, + "__init__() should return None, not '%.200s'", + Py_TYPE(PyStackRef_AsPyObjectBorrow(should_be_none))->tp_name); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(EXTENDED_ARG) { + #if _Py_TAIL_CALL_INTERP + int opcode = EXTENDED_ARG; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(EXTENDED_ARG); + opcode = EXTENDED_ARG; + assert(oparg); + opcode = next_instr->op.code; + oparg = oparg << 8 | next_instr->op.arg; + PRE_DISPATCH_GOTO(); + DISPATCH_GOTO(); + } + + TARGET(FORMAT_SIMPLE) { + #if _Py_TAIL_CALL_INTERP + int opcode = FORMAT_SIMPLE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(FORMAT_SIMPLE); + _PyStackRef value; + _PyStackRef res; + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyUnicode_CheckExact(value_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyObject_Format(value_o, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + else { + res = value; + stack_pointer += -1; + } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(FORMAT_WITH_SPEC) { + #if _Py_TAIL_CALL_INTERP + int opcode = FORMAT_WITH_SPEC; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(FORMAT_WITH_SPEC); + _PyStackRef value; + _PyStackRef fmt_spec; + _PyStackRef res; + fmt_spec = stack_pointer[-1]; + value = stack_pointer[-2]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyObject_Format(PyStackRef_AsPyObjectBorrow(value), PyStackRef_AsPyObjectBorrow(fmt_spec)); + _PyStackRef tmp = fmt_spec; + fmt_spec = PyStackRef_NULL; + stack_pointer[-1] = fmt_spec; + PyStackRef_CLOSE(tmp); + tmp = value; + value = PyStackRef_NULL; + stack_pointer[-2] = value; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(FOR_ITER) { + #if _Py_TAIL_CALL_INTERP + int opcode = FOR_ITER; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER); + PREDICTED_FOR_ITER:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + _PyStackRef iter; + _PyStackRef null_or_index; + _PyStackRef next; + // _SPECIALIZE_FOR_ITER + { + null_or_index = stack_pointer[-1]; + iter = stack_pointer[-2]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_ForIter(iter, null_or_index, next_instr, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(FOR_ITER); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + // _FOR_ITER + { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef item = _PyForIter_VirtualIteratorNext(tstate, frame, iter, &null_or_index); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (!PyStackRef_IsValid(item)) { + if (PyStackRef_IsError(item)) { + JUMP_TO_LABEL(error); + } + JUMPBY(oparg + 1); + stack_pointer[-1] = null_or_index; + DISPATCH(); + } + next = item; + } + stack_pointer[-1] = null_or_index; + stack_pointer[0] = next; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(FOR_ITER_GEN) { + #if _Py_TAIL_CALL_INTERP + int opcode = FOR_ITER_GEN; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_GEN); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + _PyStackRef iter; + _PyStackRef gen_frame; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + } + // _FOR_ITER_GEN_FRAME + { + iter = stack_pointer[-2]; + PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(gen) != &PyGen_Type) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + if (!gen_try_set_executing((PyGenObject *)gen)) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + STAT_INC(FOR_ITER, hit); + _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; + _PyFrame_StackPush(pushed_frame, PyStackRef_None); + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + pushed_frame->previous = frame; + frame->return_offset = (uint16_t)( 2u + oparg); + gen_frame = PyStackRef_Wrap(pushed_frame); + } + // _PUSH_FRAME + { + new_frame = gen_frame; + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(FOR_ITER_LIST) { + #if _Py_TAIL_CALL_INTERP + int opcode = FOR_ITER_LIST; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_LIST); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + _PyStackRef iter; + _PyStackRef null_or_index; + _PyStackRef next; + /* Skip 1 cache entry */ + // _ITER_CHECK_LIST + { + null_or_index = stack_pointer[-1]; + iter = stack_pointer[-2]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != &PyList_Type) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + assert(PyStackRef_IsTaggedInt(null_or_index)); + #ifdef Py_GIL_DISABLED + if (!_Py_IsOwnedByCurrentThread(iter_o) && !_PyObject_GC_IS_SHARED(iter_o)) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + #endif + } + // _ITER_JUMP_LIST + { + #ifdef Py_GIL_DISABLED + + #else + PyObject *list_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(list_o) == &PyList_Type); + STAT_INC(FOR_ITER, hit); + if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyList_GET_SIZE(list_o)) { + null_or_index = PyStackRef_TagInt(-1); + JUMPBY(oparg + 1); + stack_pointer[-1] = null_or_index; + DISPATCH(); + } + #endif + } + // _ITER_NEXT_LIST + { + PyObject *list_o = PyStackRef_AsPyObjectBorrow(iter); + assert(PyList_CheckExact(list_o)); + #ifdef Py_GIL_DISABLED + assert(_Py_IsOwnedByCurrentThread(list_o) || + _PyObject_GC_IS_SHARED(list_o)); + STAT_INC(FOR_ITER, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + int result = _PyList_GetItemRefNoLock((PyListObject *)list_o, PyStackRef_UntagInt(null_or_index), &next); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result < 0) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + if (result == 0) { + null_or_index = PyStackRef_TagInt(-1); + JUMPBY(oparg + 1); + stack_pointer[-1] = null_or_index; + DISPATCH(); + } + #else + next = PyStackRef_FromPyObjectNew(PyList_GET_ITEM(list_o, PyStackRef_UntagInt(null_or_index))); + #endif + null_or_index = PyStackRef_IncrementTaggedIntNoOverflow(null_or_index); + } + stack_pointer[-1] = null_or_index; + stack_pointer[0] = next; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(FOR_ITER_RANGE) { + #if _Py_TAIL_CALL_INTERP + int opcode = FOR_ITER_RANGE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_RANGE); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + _PyStackRef iter; + _PyStackRef next; + /* Skip 1 cache entry */ + // _ITER_CHECK_RANGE + { + iter = stack_pointer[-2]; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(r) != &PyRangeIter_Type) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + #ifdef Py_GIL_DISABLED + if (!_PyObject_IsUniquelyReferenced((PyObject *)r)) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + #endif + } + // _ITER_JUMP_RANGE + { + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(r) == &PyRangeIter_Type); + #ifdef Py_GIL_DISABLED + assert(_PyObject_IsUniquelyReferenced((PyObject *)r)); + #endif + STAT_INC(FOR_ITER, hit); + if (r->len <= 0) { + JUMPBY(oparg + 1); + DISPATCH(); + } + } + // _ITER_NEXT_RANGE + { + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(r) == &PyRangeIter_Type); + #ifdef Py_GIL_DISABLED + assert(_PyObject_IsUniquelyReferenced((PyObject *)r)); + #endif + assert(r->len > 0); + long value = r->start; + r->start = value + r->step; + r->len--; + PyObject *res = PyLong_FromLong(value); + if (res == NULL) { + JUMP_TO_LABEL(error); + } + next = PyStackRef_FromPyObjectSteal(res); + } + stack_pointer[0] = next; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(FOR_ITER_TUPLE) { + #if _Py_TAIL_CALL_INTERP + int opcode = FOR_ITER_TUPLE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_TUPLE); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + _PyStackRef iter; + _PyStackRef null_or_index; + _PyStackRef next; + /* Skip 1 cache entry */ + // _ITER_CHECK_TUPLE + { + null_or_index = stack_pointer[-1]; + iter = stack_pointer[-2]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != &PyTuple_Type) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + assert(PyStackRef_IsTaggedInt(null_or_index)); + } + // _ITER_JUMP_TUPLE + { + PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); + (void)tuple_o; + assert(Py_TYPE(tuple_o) == &PyTuple_Type); + STAT_INC(FOR_ITER, hit); + if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyTuple_GET_SIZE(tuple_o)) { + null_or_index = PyStackRef_TagInt(-1); + JUMPBY(oparg + 1); + stack_pointer[-1] = null_or_index; + DISPATCH(); + } + } + // _ITER_NEXT_TUPLE + { + PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(tuple_o) == &PyTuple_Type); + uintptr_t i = PyStackRef_UntagInt(null_or_index); + assert((size_t)i < (size_t)PyTuple_GET_SIZE(tuple_o)); + next = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(tuple_o, i)); + null_or_index = PyStackRef_IncrementTaggedIntNoOverflow(null_or_index); + } + stack_pointer[-1] = null_or_index; + stack_pointer[0] = next; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(FOR_ITER_VIRTUAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = FOR_ITER_VIRTUAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_VIRTUAL); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + /* Skip 1 cache entry */ + // _GUARD_TOS_NOT_NULL + { + null_or_index = stack_pointer[-1]; + if (PyStackRef_IsNull(null_or_index)) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + } + // _FOR_ITER_VIRTUAL + { + iter = stack_pointer[-2]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyObjectIndexPair next_index = Py_TYPE(iter_o)->_tp_iteritem(iter_o, index); + stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + JUMP_TO_LABEL(error); + } + JUMPBY(oparg + 1); + DISPATCH(); + } + null_or_index = PyStackRef_TagInt(index); + next = PyStackRef_FromPyObjectSteal(next_o); + } + stack_pointer[-1] = null_or_index; + stack_pointer[0] = next; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(GET_AITER) { + #if _Py_TAIL_CALL_INTERP + int opcode = GET_AITER; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(GET_AITER); + _PyStackRef obj; + _PyStackRef iter; + obj = stack_pointer[-1]; + unaryfunc getter = NULL; + PyObject *obj_o = PyStackRef_AsPyObjectBorrow(obj); + PyObject *iter_o; + PyTypeObject *type = Py_TYPE(obj_o); + if (type->tp_as_async != NULL) { + getter = type->tp_as_async->am_aiter; + } + if (getter == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, + "'async for' requires an object with " + "__aiter__ method, got %.100s", + type->tp_name); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(obj); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + iter_o = (*getter)(obj_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(obj); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (iter_o == NULL) { + JUMP_TO_LABEL(error); + } + if (Py_TYPE(iter_o)->tp_as_async == NULL || + Py_TYPE(iter_o)->tp_as_async->am_anext == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, + "'async for' received an object from __aiter__ " + "that does not implement __anext__: %.100s", + Py_TYPE(iter_o)->tp_name); + Py_DECREF(iter_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + iter = PyStackRef_FromPyObjectSteal(iter_o); + stack_pointer[0] = iter; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(GET_ANEXT) { + #if _Py_TAIL_CALL_INTERP + int opcode = GET_ANEXT; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(GET_ANEXT); + _PyStackRef aiter; + _PyStackRef awaitable; + aiter = stack_pointer[-1]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *awaitable_o = _PyEval_GetANext(PyStackRef_AsPyObjectBorrow(aiter)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (awaitable_o == NULL) { + JUMP_TO_LABEL(error); + } + awaitable = PyStackRef_FromPyObjectSteal(awaitable_o); + stack_pointer[0] = awaitable; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(GET_AWAITABLE) { + #if _Py_TAIL_CALL_INTERP + int opcode = GET_AWAITABLE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(GET_AWAITABLE); + _PyStackRef iterable; + _PyStackRef iter; + iterable = stack_pointer[-1]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *iter_o = _PyEval_GetAwaitable(PyStackRef_AsPyObjectBorrow(iterable), oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(iterable); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (iter_o == NULL) { + JUMP_TO_LABEL(error); + } + iter = PyStackRef_FromPyObjectSteal(iter_o); + stack_pointer[0] = iter; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(GET_ITER) { + #if _Py_TAIL_CALL_INTERP + int opcode = GET_ITER; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(GET_ITER); + PREDICTED_GET_ITER:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + _PyStackRef iterable; + _PyStackRef iter; + _PyStackRef index_or_null; + // _SPECIALIZE_GET_ITER + { + iterable = stack_pointer[-1]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_GetIter(iterable, next_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(GET_ITER); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + // _GET_ITER + { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef result = _PyEval_GetIter(iterable, &index_or_null, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (PyStackRef_IsError(result)) { + JUMP_TO_LABEL(pop_1_error); + } + iter = result; + } + stack_pointer[-1] = iter; + stack_pointer[0] = index_or_null; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(GET_ITER_SELF) { + #if _Py_TAIL_CALL_INTERP + int opcode = GET_ITER_SELF; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(GET_ITER_SELF); + static_assert(INLINE_CACHE_ENTRIES_GET_ITER == 1, "incorrect cache size"); + _PyStackRef iterable; + _PyStackRef res; + /* Skip 1 cache entry */ + // _GUARD_ITERATOR + { + iterable = stack_pointer[-1]; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->tp_iter != PyObject_SelfIter) { + UPDATE_MISS_STATS(GET_ITER); + assert(_PyOpcode_Deopt[opcode] == (GET_ITER)); + JUMP_TO_PREDICTED(GET_ITER); + } + STAT_INC(GET_ITER, hit); + } + // _PUSH_NULL + { + res = PyStackRef_NULL; + } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(GET_ITER_VIRTUAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = GET_ITER_VIRTUAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(GET_ITER_VIRTUAL); + static_assert(INLINE_CACHE_ENTRIES_GET_ITER == 1, "incorrect cache size"); + _PyStackRef iterable; + _PyStackRef zero; + /* Skip 1 cache entry */ + // _GUARD_ITER_VIRTUAL + { + iterable = stack_pointer[-1]; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->_tp_iteritem == NULL) { + UPDATE_MISS_STATS(GET_ITER); + assert(_PyOpcode_Deopt[opcode] == (GET_ITER)); + JUMP_TO_PREDICTED(GET_ITER); + } + STAT_INC(GET_ITER, hit); + } + // _PUSH_TAGGED_ZERO + { + zero = PyStackRef_TagInt(0); + } + stack_pointer[0] = zero; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(GET_LEN) { + #if _Py_TAIL_CALL_INTERP + int opcode = GET_LEN; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(GET_LEN); + _PyStackRef obj; + _PyStackRef len; + obj = stack_pointer[-1]; + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_ssize_t len_i = PyObject_Length(PyStackRef_AsPyObjectBorrow(obj)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (len_i < 0) { + JUMP_TO_LABEL(error); + } + PyObject *len_o = PyLong_FromSsize_t(len_i); + if (len_o == NULL) { + JUMP_TO_LABEL(error); + } + len = PyStackRef_FromPyObjectSteal(len_o); + stack_pointer[0] = len; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(IMPORT_FROM) { + #if _Py_TAIL_CALL_INTERP + int opcode = IMPORT_FROM; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(IMPORT_FROM); + _PyStackRef from; + _PyStackRef res; + from = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *res_o; + if (PyLazyImport_CheckExact(PyStackRef_AsPyObjectBorrow(from))) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_LazyImportFrom( + tstate, frame, PyStackRef_AsPyObjectBorrow(from), name); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_ImportFrom( + tstate, PyStackRef_AsPyObjectBorrow(from), name); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(IMPORT_NAME) { + #if _Py_TAIL_CALL_INTERP + int opcode = IMPORT_NAME; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(IMPORT_NAME); + _PyStackRef level; + _PyStackRef fromlist; + _PyStackRef res; + fromlist = stack_pointer[-1]; + level = stack_pointer[-2]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + PyObject *res_o; + if (!(oparg & 0x02)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_LazyImportName(tstate, BUILTINS(), GLOBALS(), + LOCALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level), + oparg & 0x01); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_ImportName(tstate, BUILTINS(), GLOBALS(), + LOCALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = fromlist; + fromlist = PyStackRef_NULL; + stack_pointer[-1] = fromlist; + PyStackRef_CLOSE(tmp); + tmp = level; + level = PyStackRef_NULL; + stack_pointer[-2] = level; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(INSTRUMENTED_CALL) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_CALL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(INSTRUMENTED_CALL); + opcode = INSTRUMENTED_CALL; + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef func; + _PyStackRef maybe_self; + _PyStackRef *args; + _PyStackRef res; + /* Skip 3 cache entries */ + // _MAYBE_EXPAND_METHOD + { + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *self = ((PyMethodObject *)callable_o)->im_self; + self_or_null = PyStackRef_FromPyObjectNew(self); + PyObject *method = ((PyMethodObject *)callable_o)->im_func; + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(method); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _MONITOR_CALL + { + args = &stack_pointer[-oparg]; + maybe_self = self_or_null; + func = callable; + int is_meth = !PyStackRef_IsNull(maybe_self); + PyObject *function = PyStackRef_AsPyObjectBorrow(func); + PyObject *arg0; + if (is_meth) { + arg0 = PyStackRef_AsPyObjectBorrow(maybe_self); + } + else if (oparg) { + arg0 = PyStackRef_AsPyObjectBorrow(args[0]); + } + else { + arg0 = &_PyInstrumentation_MISSING; + } + stack_pointer[-2 - oparg] = func; + stack_pointer[-1 - oparg] = maybe_self; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, function, arg0 + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + } + // _DO_CALL + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + if (Py_TYPE(callable_o) == &PyFunction_Type && + !IS_PEP523_HOOKED(tstate) && + ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) + { + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, callable, locals, + arguments, total_args, NULL, frame + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (new_frame == NULL) { + JUMP_TO_LABEL(error); + } + frame->return_offset = 4u ; + DISPATCH_INLINED(new_frame); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject* res_o = _Py_VectorCallInstrumentation_StackRefSteal( + callable, + arguments, + total_args, + PyStackRef_NULL, + opcode == INSTRUMENTED_CALL, + frame, + this_instr, + tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _CHECK_PERIODIC_AT_END + { + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(INSTRUMENTED_CALL_FUNCTION_EX) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_CALL_FUNCTION_EX; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_CALL_FUNCTION_EX); + opcode = INSTRUMENTED_CALL_FUNCTION_EX; + _PyStackRef func; + _PyStackRef callargs; + _PyStackRef func_st; + _PyStackRef null; + _PyStackRef callargs_st; + _PyStackRef kwargs_st; + _PyStackRef result; + /* Skip 1 cache entry */ + // _MAKE_CALLARGS_A_TUPLE + { + callargs = stack_pointer[-2]; + func = stack_pointer[-4]; + PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); + if (!PyTuple_CheckExact(callargs_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *tuple_o = PySequence_Tuple(callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (tuple_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callargs; + callargs = PyStackRef_FromPyObjectSteal(tuple_o); + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _DO_CALL_FUNCTION_EX + { + kwargs_st = stack_pointer[-1]; + callargs_st = callargs; + null = stack_pointer[-3]; + func_st = func; + (void)null; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func); + PyObject *result_o; + assert(!_PyErr_Occurred(tstate)); + if (opcode == INSTRUMENTED_CALL_FUNCTION_EX) { + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + assert(PyTuple_CheckExact(callargs)); + PyObject *arg = PyTuple_GET_SIZE(callargs) > 0 ? + PyTuple_GET_ITEM(callargs, 0) : &_PyInstrumentation_MISSING; + stack_pointer[-2] = callargs_st; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, func, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + result_o = PyObject_Call(func, callargs, kwargs); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (!PyFunction_Check(func) && !PyMethod_Check(func)) { + if (result_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, func, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, func, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_CLEAR(result_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + } + } + else { + if (Py_TYPE(func) == &PyFunction_Type && + !IS_PEP523_HOOKED(tstate) && + ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { + PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_IsNull(kwargs_st) ? NULL : PyStackRef_AsPyObjectSteal(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); + int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( + tstate, func_st, locals, + nargs, callargs, kwargs, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (new_frame == NULL) { + JUMP_TO_LABEL(error); + } + assert( 2u == 1 + INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX); + frame->return_offset = 2u ; + DISPATCH_INLINED(new_frame); + } + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + stack_pointer[-2] = callargs_st; + _PyFrame_SetStackPointer(frame, stack_pointer); + result_o = PyObject_Call(func, callargs, kwargs); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(kwargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(func_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + JUMP_TO_LABEL(error); + } + result = PyStackRef_FromPyObjectSteal(result_o); + } + // _CHECK_PERIODIC_AT_END + { + stack_pointer[0] = result; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(INSTRUMENTED_CALL_KW) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_CALL_KW; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(INSTRUMENTED_CALL_KW); + opcode = INSTRUMENTED_CALL_KW; + _PyStackRef callable; + _PyStackRef self_or_null; + _PyStackRef *args; + _PyStackRef kwnames; + _PyStackRef res; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _MAYBE_EXPAND_METHOD_KW + { + self_or_null = stack_pointer[-2 - oparg]; + callable = stack_pointer[-3 - oparg]; + if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *self = ((PyMethodObject *)callable_o)->im_self; + self_or_null = PyStackRef_FromPyObjectNew(self); + PyObject *method = ((PyMethodObject *)callable_o)->im_func; + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(method); + stack_pointer[-3 - oparg] = callable; + stack_pointer[-2 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _MONITOR_CALL_KW + { + args = &stack_pointer[-1 - oparg]; + int is_meth = !PyStackRef_IsNull(self_or_null); + PyObject *arg; + if (is_meth) { + arg = PyStackRef_AsPyObjectBorrow(self_or_null); + } + else if (args) { + arg = PyStackRef_AsPyObjectBorrow(args[0]); + } + else { + arg = &_PyInstrumentation_MISSING; + } + PyObject *function = PyStackRef_AsPyObjectBorrow(callable); + stack_pointer[-3 - oparg] = callable; + stack_pointer[-2 - oparg] = self_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, function, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + } + // _DO_CALL_KW + { + kwnames = stack_pointer[-1]; + args = &stack_pointer[-1 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); + if (Py_TYPE(callable_o) == &PyFunction_Type && + !IS_PEP523_HOOKED(tstate) && + ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) + { + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( + tstate, callable, locals, + arguments, positional_args, kwnames_o, frame + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (new_frame == NULL) { + JUMP_TO_LABEL(error); + } + assert( 4u == 1 + INLINE_CACHE_ENTRIES_CALL_KW); + frame->return_offset = 4u ; + DISPATCH_INLINED(new_frame); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject* res_o = _Py_VectorCallInstrumentation_StackRefSteal( + callable, + arguments, + total_args, + kwnames, + opcode == INSTRUMENTED_CALL_KW, + frame, + this_instr, + tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + stack_pointer += -3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + stack_pointer[-3 - oparg] = res; + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(INSTRUMENTED_END_ASYNC_FOR) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_END_ASYNC_FOR; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_END_ASYNC_FOR); + _PyStackRef awaitable_st; + _PyStackRef exc_st; + // _MONITOR_END_ASYNC_FOR + { + assert((next_instr-oparg)->op.code == END_SEND || (next_instr-oparg)->op.code >= MIN_INSTRUMENTED_OPCODE); + INSTRUMENTED_JUMP(next_instr-oparg, this_instr+1, PY_MONITORING_EVENT_BRANCH_RIGHT); + } + // _END_ASYNC_FOR + { + exc_st = stack_pointer[-1]; + awaitable_st = stack_pointer[-2]; + JUMPBY(0); + (void)oparg; + PyObject *exc = PyStackRef_AsPyObjectBorrow(exc_st); + assert(exc && PyExceptionInstance_Check(exc)); + _PyFrame_SetStackPointer(frame, stack_pointer); + int matches = PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (matches) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = exc_st; + exc_st = PyStackRef_NULL; + stack_pointer[-1] = exc_st; + PyStackRef_CLOSE(tmp); + tmp = awaitable_st; + awaitable_st = PyStackRef_NULL; + stack_pointer[-2] = awaitable_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + } + else { + Py_INCREF(exc); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetRaisedException(tstate, exc); + monitor_reraise(tstate, frame, this_instr); + JUMP_TO_LABEL(exception_unwind); + } + } + DISPATCH(); + } + + TARGET(INSTRUMENTED_END_FOR) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_END_FOR; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_END_FOR); + _PyStackRef receiver; + _PyStackRef value; + value = stack_pointer[-1]; + receiver = stack_pointer[-3]; + if (PyStackRef_GenCheck(receiver)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = monitor_stop_iteration(tstate, frame, this_instr, PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(INSTRUMENTED_END_SEND) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_END_SEND; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_END_SEND); + _PyStackRef receiver; + _PyStackRef index_or_null; + _PyStackRef value; + _PyStackRef val; + value = stack_pointer[-1]; + index_or_null = stack_pointer[-2]; + receiver = stack_pointer[-3]; + PyObject *receiver_o = PyStackRef_AsPyObjectBorrow(receiver); + if (PyGen_Check(receiver_o) || PyCoro_CheckExact(receiver_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = monitor_stop_iteration(tstate, frame, this_instr, PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + } + val = value; + (void)index_or_null; + stack_pointer[-3] = val; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(receiver); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(INSTRUMENTED_FOR_ITER) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_FOR_ITER; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_FOR_ITER); + _PyStackRef iter; + _PyStackRef null_or_index; + _PyStackRef next; + /* Skip 1 cache entry */ + null_or_index = stack_pointer[-1]; + iter = stack_pointer[-2]; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef item = _PyForIter_VirtualIteratorNext(tstate, frame, iter, &null_or_index); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (!PyStackRef_IsValid(item)) { + if (PyStackRef_IsError(item)) { + JUMP_TO_LABEL(error); + } + JUMPBY(oparg + 1); + stack_pointer[-1] = null_or_index; + DISPATCH(); + } + next = item; + INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT); + stack_pointer[-1] = null_or_index; + stack_pointer[0] = next; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(INSTRUMENTED_INSTRUCTION) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_INSTRUCTION; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_INSTRUCTION); + opcode = INSTRUMENTED_INSTRUCTION; + _PyFrame_SetStackPointer(frame, stack_pointer); + int next_opcode = _Py_call_instrumentation_instruction( + tstate, frame, this_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (next_opcode < 0) { + JUMP_TO_LABEL(error); + } + next_instr = this_instr; + if (_PyOpcode_Caches[next_opcode]) { + PAUSE_ADAPTIVE_COUNTER(next_instr[1].counter); + } + assert(next_opcode > 0 && next_opcode < 256); + opcode = next_opcode; + DISPATCH_GOTO(); + } + + TARGET(INSTRUMENTED_JUMP_BACKWARD) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_JUMP_BACKWARD; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_JUMP_BACKWARD); + /* Skip 1 cache entry */ + // _CHECK_PERIODIC + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + // _MONITOR_JUMP_BACKWARD + { + INSTRUMENTED_JUMP(this_instr, next_instr - oparg, PY_MONITORING_EVENT_JUMP); + } + DISPATCH(); + } + + TARGET(INSTRUMENTED_JUMP_FORWARD) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_JUMP_FORWARD; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_JUMP_FORWARD); + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_JUMP); + DISPATCH(); + } + + TARGET(INSTRUMENTED_LINE) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_LINE; + (void)(opcode); + #endif + _Py_CODEUNIT* const prev_instr = frame->instr_ptr; + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_LINE); + opcode = INSTRUMENTED_LINE; + int original_opcode = 0; + if (tstate->tracing) { + PyCodeObject *code = _PyFrame_GetCode(frame); + int index = (int)(this_instr - _PyFrame_GetBytecode(frame)); + original_opcode = code->_co_monitoring->lines->data[index*code->_co_monitoring->lines->bytes_per_entry]; + next_instr = this_instr; + } else { + _PyFrame_SetStackPointer(frame, stack_pointer); + original_opcode = _Py_call_instrumentation_line( + tstate, frame, this_instr, prev_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (original_opcode < 0) { + next_instr = this_instr+1; + JUMP_TO_LABEL(error); + } + next_instr = frame->instr_ptr; + if (next_instr != this_instr) { + DISPATCH(); + } + } + if (_PyOpcode_Caches[original_opcode]) { + _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1); + PAUSE_ADAPTIVE_COUNTER(cache->counter); + } + opcode = original_opcode; + DISPATCH_GOTO(); + } + + TARGET(INSTRUMENTED_LOAD_SUPER_ATTR) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_LOAD_SUPER_ATTR; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_LOAD_SUPER_ATTR); + opcode = INSTRUMENTED_LOAD_SUPER_ATTR; + _PyStackRef global_super_st; + _PyStackRef class_st; + _PyStackRef self_st; + _PyStackRef attr; + _PyStackRef *null; + /* Skip 1 cache entry */ + // _LOAD_SUPER_ATTR + { + self_st = stack_pointer[-1]; + class_st = stack_pointer[-2]; + global_super_st = stack_pointer[-3]; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { + PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, global_super, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = self_st; + self_st = PyStackRef_NULL; + stack_pointer[-1] = self_st; + PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-2] = class_st; + PyStackRef_CLOSE(tmp); + tmp = global_super_st; + global_super_st = PyStackRef_NULL; + stack_pointer[-3] = global_super_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + } + PyObject *super; + { + PyObject *stack[] = {class, self}; + _PyFrame_SetStackPointer(frame, stack_pointer); + super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { + PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; + if (super == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, global_super, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, global_super, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_CLEAR(super); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + } + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = self_st; + self_st = PyStackRef_NULL; + stack_pointer[-1] = self_st; + PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-2] = class_st; + PyStackRef_CLOSE(tmp); + tmp = global_super_st; + global_super_st = PyStackRef_NULL; + stack_pointer[-3] = global_super_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (super == NULL) { + JUMP_TO_LABEL(error); + } + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *attr_o = PyObject_GetAttr(super, name); + Py_DECREF(super); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (attr_o == NULL) { + JUMP_TO_LABEL(error); + } + attr = PyStackRef_FromPyObjectSteal(attr_o); + } + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[1]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer[0] = attr; + stack_pointer += 1 + (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(INSTRUMENTED_NOT_TAKEN) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_NOT_TAKEN; + (void)(opcode); + #endif + _Py_CODEUNIT* const prev_instr = frame->instr_ptr; + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_NOT_TAKEN); + (void)this_instr; + INSTRUMENTED_JUMP(prev_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT); + DISPATCH(); + } + + TARGET(INSTRUMENTED_POP_ITER) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_POP_ITER; + (void)(opcode); + #endif + _Py_CODEUNIT* const prev_instr = frame->instr_ptr; + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_POP_ITER); + _PyStackRef iter; + _PyStackRef index_or_null; + index_or_null = stack_pointer[-1]; + iter = stack_pointer[-2]; + (void)index_or_null; + INSTRUMENTED_JUMP(prev_instr, this_instr+1, PY_MONITORING_EVENT_BRANCH_RIGHT); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(iter); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_POP_JUMP_IF_FALSE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_FALSE); + _PyStackRef cond; + /* Skip 1 cache entry */ + cond = stack_pointer[-1]; + assert(PyStackRef_BoolCheck(cond)); + int jump = PyStackRef_IsFalse(cond); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(INSTRUMENTED_POP_JUMP_IF_NONE) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_POP_JUMP_IF_NONE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NONE); + _PyStackRef value; + /* Skip 1 cache entry */ + value = stack_pointer[-1]; + int jump = PyStackRef_IsNone(value); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); + } + else { + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += 1; + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(INSTRUMENTED_POP_JUMP_IF_NOT_NONE) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_POP_JUMP_IF_NOT_NONE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_NOT_NONE); + _PyStackRef value; + /* Skip 1 cache entry */ + value = stack_pointer[-1]; + int jump = !PyStackRef_IsNone(value); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); + } + else { + stack_pointer += -1; + } + DISPATCH(); + } + + TARGET(INSTRUMENTED_POP_JUMP_IF_TRUE) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_POP_JUMP_IF_TRUE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_POP_JUMP_IF_TRUE); + _PyStackRef cond; + /* Skip 1 cache entry */ + cond = stack_pointer[-1]; + assert(PyStackRef_BoolCheck(cond)); + int jump = PyStackRef_IsTrue(cond); + RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); + if (jump) { + INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(INSTRUMENTED_RESUME) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_RESUME; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(INSTRUMENTED_RESUME); + /* Skip 1 cache entry */ + // _LOAD_BYTECODE + { + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_CODEUNIT *bytecode = + _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (bytecode == NULL) { + JUMP_TO_LABEL(error); + } + ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); + frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; + frame->instr_ptr = bytecode + off; + next_instr = frame->instr_ptr; + DISPATCH(); + } + #endif + } + // _MAYBE_INSTRUMENT + { + #ifdef Py_GIL_DISABLED + + int check_instrumentation = 1; + #else + int check_instrumentation = (tstate->tracing == 0); + #endif + if (check_instrumentation) { + uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & ~_PY_EVAL_EVENTS_MASK; + uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + if (code_version != global_version) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + next_instr = this_instr; + DISPATCH(); + } + } + } + // _CHECK_PERIODIC_IF_NOT_YIELD_FROM + { + Test_EvalFrame_Resumes++; + if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + } + // _MONITOR_RESUME + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation( + tstate, oparg == 0 ? PY_MONITORING_EVENT_PY_START : PY_MONITORING_EVENT_PY_RESUME, frame, this_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + if (frame->instr_ptr != this_instr) { + next_instr = frame->instr_ptr; + } + } + DISPATCH(); + } + + TARGET(INSTRUMENTED_RETURN_VALUE) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_RETURN_VALUE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_RETURN_VALUE); + _PyStackRef val; + _PyStackRef value; + _PyStackRef retval; + _PyStackRef res; + // _RETURN_VALUE_EVENT + { + val = stack_pointer[-1]; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_arg( + tstate, PY_MONITORING_EVENT_PY_RETURN, + frame, this_instr, PyStackRef_AsPyObjectBorrow(val)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + } + // _MAKE_HEAP_SAFE + { + value = val; + value = PyStackRef_MakeHeapSafe(value); + } + // _RETURN_VALUE + { + retval = value; + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + _PyStackRef temp = retval; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + stack_pointer = _PyFrame_GetStackPointer(frame); + LOAD_IP(frame->return_offset); + res = temp; + LLTRACE_RESUME_FRAME(); + } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(INSTRUMENTED_YIELD_VALUE) { + #if _Py_TAIL_CALL_INTERP + int opcode = INSTRUMENTED_YIELD_VALUE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INSTRUMENTED_YIELD_VALUE); + opcode = INSTRUMENTED_YIELD_VALUE; + _PyStackRef val; + _PyStackRef value; + _PyStackRef retval; + // _YIELD_VALUE_EVENT + { + val = stack_pointer[-1]; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_arg( + tstate, PY_MONITORING_EVENT_PY_YIELD, + frame, this_instr, PyStackRef_AsPyObjectBorrow(val)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + if (frame->instr_ptr != this_instr) { + next_instr = frame->instr_ptr; + DISPATCH(); + } + } + // _MAKE_HEAP_SAFE + { + value = val; + value = PyStackRef_MakeHeapSafe(value); + } + // _YIELD_VALUE + { + retval = value; + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + frame->instr_ptr++; + PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); + assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); + assert(oparg == 0 || oparg == 1); + _PyStackRef temp = retval; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + DTRACE_FUNCTION_RETURN(); + tstate->exc_info = gen->gi_exc_state.previous_item; + gen->gi_exc_state.previous_item = NULL; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; + frame = tstate->current_frame = frame->previous; + gen_frame->previous = NULL; + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); + assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + #if TIER_ONE && defined(Py_DEBUG) + if (!PyStackRef_IsNone(frame->f_executable)) { + Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); + assert(i >= 0 && i <= INT_MAX); + int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; + assert(opcode == SEND || opcode == FOR_ITER); + } + #endif + stack_pointer = _PyFrame_GetStackPointer(frame); + LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); + value = temp; + LLTRACE_RESUME_FRAME(); + } + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(INTERPRETER_EXIT) { + #if _Py_TAIL_CALL_INTERP + int opcode = INTERPRETER_EXIT; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(INTERPRETER_EXIT); + _PyStackRef retval; + retval = stack_pointer[-1]; + assert(frame->owner == FRAME_OWNED_BY_INTERPRETER); + assert(_PyFrame_IsIncomplete(frame)); + tstate->current_frame = frame->previous; + assert(!_PyErr_Occurred(tstate)); + PyObject *result = PyStackRef_AsPyObjectSteal(retval); + #if !_Py_TAIL_CALL_INTERP + assert(frame == &entry.frame); + #endif + #ifdef _Py_TIER2 + _PyStackRef executor = frame->localsplus[0]; + assert(tstate->current_executor == NULL); + if (!PyStackRef_IsNull(executor)) { + tstate->current_executor = PyStackRef_AsPyObjectBorrow(executor); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(executor); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += 1; + } + #endif + LLTRACE_RESUME_FRAME(); + return result; + } + + TARGET(IS_OP) { + #if _Py_TAIL_CALL_INTERP + int opcode = IS_OP; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(IS_OP); + _PyStackRef left; + _PyStackRef right; + _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; + // _IS_OP + { + right = stack_pointer[-1]; + left = stack_pointer[-2]; + int res = Py_Is(PyStackRef_AsPyObjectBorrow(left), PyStackRef_AsPyObjectBorrow(right)) ^ oparg; + b = res ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = b; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(JUMP_BACKWARD) { + #if _Py_TAIL_CALL_INTERP + int opcode = JUMP_BACKWARD; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(JUMP_BACKWARD); + PREDICTED_JUMP_BACKWARD:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + /* Skip 1 cache entry */ + // _SPECIALIZE_JUMP_BACKWARD + { + #if ENABLE_SPECIALIZATION + if (this_instr->op.code == JUMP_BACKWARD) { + uint8_t desired = tstate->interp->jit ? JUMP_BACKWARD_JIT : JUMP_BACKWARD_NO_JIT; + FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, desired); + next_instr = this_instr; + DISPATCH_SAME_OPARG(); + } + #endif + } + // _CHECK_PERIODIC + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + // _JUMP_BACKWARD_NO_INTERRUPT + { + assert(oparg <= INSTR_OFFSET()); + JUMPBY(-oparg); + } + DISPATCH(); + } + + TARGET(JUMP_BACKWARD_JIT) { + #if _Py_TAIL_CALL_INTERP + int opcode = JUMP_BACKWARD_JIT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(JUMP_BACKWARD_JIT); + static_assert(1 == 1, "incorrect cache size"); + /* Skip 1 cache entry */ + // _CHECK_PERIODIC + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + // _JUMP_BACKWARD_NO_INTERRUPT + { + assert(oparg <= INSTR_OFFSET()); + JUMPBY(-oparg); + } + // _JIT + { + #ifdef _Py_TIER2 + bool is_resume = this_instr->op.code == RESUME_CHECK_JIT; + _Py_BackoffCounter counter = this_instr[1].counter; + if ((backoff_counter_triggers(counter) && + !IS_JIT_TRACING() && + (this_instr->op.code == JUMP_BACKWARD_JIT || is_resume)) && + next_instr->op.code != ENTER_EXECUTOR) { + _Py_CODEUNIT *insert_exec_at = this_instr; + while (oparg > 255) { + oparg >>= 8; + insert_exec_at--; + } + int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, + is_resume ? insert_exec_at : next_instr, stack_pointer, 0, NULL, oparg, NULL); + if (succ) { + ENTER_TRACING(); + } + else { + this_instr[1].counter = restart_backoff_counter(counter); + } + } + else { + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + } + #endif + } + DISPATCH(); + } + + TARGET(JUMP_BACKWARD_NO_INTERRUPT) { + #if _Py_TAIL_CALL_INTERP + int opcode = JUMP_BACKWARD_NO_INTERRUPT; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(JUMP_BACKWARD_NO_INTERRUPT); + assert(oparg <= INSTR_OFFSET()); + JUMPBY(-oparg); + DISPATCH(); + } + + TARGET(JUMP_BACKWARD_NO_JIT) { + #if _Py_TAIL_CALL_INTERP + int opcode = JUMP_BACKWARD_NO_JIT; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(JUMP_BACKWARD_NO_JIT); + static_assert(1 == 1, "incorrect cache size"); + /* Skip 1 cache entry */ + // _CHECK_PERIODIC + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + // _JUMP_BACKWARD_NO_INTERRUPT + { + assert(oparg <= INSTR_OFFSET()); + JUMPBY(-oparg); + } + DISPATCH(); + } + + TARGET(JUMP_FORWARD) { + #if _Py_TAIL_CALL_INTERP + int opcode = JUMP_FORWARD; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(JUMP_FORWARD); + JUMPBY(oparg); + DISPATCH(); + } + + TARGET(LIST_APPEND) { + #if _Py_TAIL_CALL_INTERP + int opcode = LIST_APPEND; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LIST_APPEND); + _PyStackRef list; + _PyStackRef v; + v = stack_pointer[-1]; + list = stack_pointer[-2 - (oparg-1)]; + int err = _PyList_AppendTakeRef((PyListObject *)PyStackRef_AsPyObjectBorrow(list), + PyStackRef_AsPyObjectSteal(v)); + if (err < 0) { + JUMP_TO_LABEL(pop_1_error); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LIST_EXTEND) { + #if _Py_TAIL_CALL_INTERP + int opcode = LIST_EXTEND; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LIST_EXTEND); + _PyStackRef list_st; + _PyStackRef iterable_st; + _PyStackRef i; + _PyStackRef value; + // _LIST_EXTEND + { + iterable_st = stack_pointer[-1]; + list_st = stack_pointer[-2 - (oparg-1)]; + PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); + PyObject *iterable = PyStackRef_AsPyObjectBorrow(iterable_st); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (none_val == NULL) { + int matches = _PyErr_ExceptionMatches(tstate, PyExc_TypeError); + if (matches && + (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) + { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Clear(tstate); + _PyErr_Format(tstate, PyExc_TypeError, + "Value after * must be an iterable, not %.200s", + Py_TYPE(iterable)->tp_name); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + JUMP_TO_LABEL(error); + } + assert(Py_IsNone(none_val)); + i = iterable_st; + } + // _POP_TOP + { + value = i; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(LOAD_ATTR) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR); + PREDICTED_LOAD_ATTR:; + _Py_CODEUNIT* const this_instr = next_instr - 10; + (void)this_instr; + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef *self_or_null; + // _SPECIALIZE_LOAD_ATTR + { + owner = stack_pointer[-1]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_LoadAttr(owner, next_instr, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(LOAD_ATTR); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + /* Skip 8 cache entries */ + // _LOAD_ATTR + { + self_or_null = &stack_pointer[0]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); + if (oparg & 1) { + _PyFrame_SetStackPointer(frame, stack_pointer); + attr = _Py_LoadAttr_StackRefSteal(tstate, owner, name, self_or_null); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (PyStackRef_IsNull(attr)) { + JUMP_TO_LABEL(pop_1_error); + } + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + attr = _PyObject_GetAttrStackRef(PyStackRef_AsPyObjectBorrow(owner), name); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer[-1] = attr; + stack_pointer += (oparg&1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (PyStackRef_IsNull(attr)) { + JUMP_TO_LABEL(error); + } + stack_pointer += -(oparg&1); + } + } + stack_pointer[-1] = attr; + stack_pointer += (oparg&1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_CLASS) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_CLASS; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_CLASS); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef *null; + /* Skip 1 cache entry */ + // _CHECK_ATTR_CLASS + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (!PyType_Check(owner_o)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + /* Skip 2 cache entries */ + // _LOAD_ATTR_CLASS + { + PyObject *descr = read_obj(&this_instr[6].cache); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + attr = PyStackRef_FromPyObjectNew(descr); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = owner; + owner = attr; + stack_pointer[-1] = owner; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer += (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_CLASS_WITH_METACLASS_CHECK) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_CLASS_WITH_METACLASS_CHECK; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_CLASS_WITH_METACLASS_CHECK); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef *null; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _CHECK_ATTR_CLASS + { + uint32_t type_version = read_u32(&this_instr[4].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (!PyType_Check(owner_o)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _LOAD_ATTR_CLASS + { + PyObject *descr = read_obj(&this_instr[6].cache); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + attr = PyStackRef_FromPyObjectNew(descr); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = owner; + owner = attr; + stack_pointer[-1] = owner; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer += (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME + { + uint32_t func_version = read_u32(&this_instr[4].cache); + PyObject *getattribute = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)getattribute; + assert(func_version != 0); + if (f->func_version != func_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + PyCodeObject *code = (PyCodeObject *)f->func_code; + assert(code->co_argcount == 2); + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + STAT_INC(LOAD_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked( + tstate, PyStackRef_FromPyObjectNew(f), 2, frame); + pushed_frame->localsplus[0] = owner; + pushed_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name); + new_frame = PyStackRef_Wrap(pushed_frame); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(LOAD_ATTR_INSTANCE_VALUE) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_INSTANCE_VALUE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_INSTANCE_VALUE); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef value; + _PyStackRef *null; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _CHECK_MANAGED_OBJECT_HAS_VALUES + { + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_dictoffset < 0); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _LOAD_ATTR_INSTANCE_VALUE + { + uint16_t offset = read_u16(&this_instr[4].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); + if (attr_o == NULL) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr); + if (!increfed) { + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + } + // _POP_TOP + { + value = o; + stack_pointer[-1] = attr; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + /* Skip 5 cache entries */ + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer += (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_METHOD_LAZY_DICT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_METHOD_LAZY_DICT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _CHECK_ATTR_METHOD_LAZY_DICT + { + uint16_t dictoffset = read_u16(&this_instr[4].cache); + char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); + if (dict != NULL) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + /* Skip 1 cache entry */ + // _LOAD_ATTR_METHOD_LAZY_DICT + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert(oparg & 1); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + } + stack_pointer[-1] = attr; + stack_pointer[0] = self; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_METHOD_NO_DICT) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_METHOD_NO_DICT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_METHOD_NO_DICT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + /* Skip 2 cache entries */ + // _LOAD_ATTR_METHOD_NO_DICT + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert(oparg & 1); + assert(Py_TYPE(PyStackRef_AsPyObjectBorrow(owner))->tp_dictoffset == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + } + stack_pointer[-1] = attr; + stack_pointer[0] = self; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_METHOD_WITH_VALUES; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_METHOD_WITH_VALUES); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + { + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + PyDictValues *ivs = _PyObject_InlineValues(owner_o); + if (!FT_ATOMIC_LOAD_UINT8(ivs->valid)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + /* Skip 2 cache entries */ + // _LOAD_ATTR_METHOD_WITH_VALUES + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert(oparg & 1); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + } + stack_pointer[-1] = attr; + stack_pointer[0] = self; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_MODULE) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_MODULE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_MODULE); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef value; + _PyStackRef *null; + /* Skip 1 cache entry */ + // _LOAD_ATTR_MODULE + { + owner = stack_pointer[-1]; + uint32_t dict_version = read_u32(&this_instr[2].cache); + uint16_t index = read_u16(&this_instr[4].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; + assert(dict != NULL); + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries)); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index; + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); + if (attr_o == NULL) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); + if (!increfed) { + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + } + // _POP_TOP + { + value = o; + stack_pointer[-1] = attr; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + /* Skip 5 cache entries */ + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer += (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_NONDESCRIPTOR_NO_DICT) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_NONDESCRIPTOR_NO_DICT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_NO_DICT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + /* Skip 2 cache entries */ + // _LOAD_ATTR_NONDESCRIPTOR_NO_DICT + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + assert(Py_TYPE(PyStackRef_AsPyObjectBorrow(owner))->tp_dictoffset == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); + stack_pointer = _PyFrame_GetStackPointer(frame); + attr = PyStackRef_FromPyObjectNew(descr); + } + stack_pointer[0] = attr; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + { + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + PyDictValues *ivs = _PyObject_InlineValues(owner_o); + if (!FT_ATOMIC_LOAD_UINT8(ivs->valid)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + /* Skip 2 cache entries */ + // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES + { + PyObject *descr = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); + stack_pointer = _PyFrame_GetStackPointer(frame); + attr = PyStackRef_FromPyObjectNew(descr); + } + stack_pointer[0] = attr; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_PROPERTY) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_PROPERTY; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_PROPERTY); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _LOAD_ATTR_PROPERTY_FRAME + { + uint32_t func_version = read_u32(&this_instr[4].cache); + PyObject *fget = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + assert(Py_IS_TYPE(fget, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)fget; + if (f->func_version != func_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + PyCodeObject *code = (PyCodeObject *)f->func_code; + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + STAT_INC(LOAD_ATTR, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(fget), 1, frame); + pushed_frame->localsplus[0] = owner; + new_frame = PyStackRef_Wrap(pushed_frame); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(LOAD_ATTR_SLOT) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_SLOT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_SLOT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef value; + _PyStackRef *null; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _LOAD_ATTR_SLOT + { + uint16_t index = read_u16(&this_instr[4].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **addr = (PyObject **)((char *)owner_o + index); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); + if (attr_o == NULL) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); + if (!increfed) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + } + // _POP_TOP + { + value = o; + stack_pointer[-1] = attr; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + /* Skip 5 cache entries */ + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer += (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_ATTR_WITH_HINT) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_ATTR_WITH_HINT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 10; + INSTRUCTION_STATS(LOAD_ATTR_WITH_HINT); + static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef value; + _PyStackRef *null; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _LOAD_ATTR_WITH_HINT + { + uint16_t hint = read_u16(&this_instr[4].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictObject *dict = _PyObject_GetManagedDict(owner_o); + if (dict == NULL) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + PyDictKeysObject *dk = FT_ATOMIC_LOAD_PTR(dict->ma_keys); + assert(PyDict_CheckExact((PyObject *)dict)); + #ifdef Py_GIL_DISABLED + if (!_Py_IsOwnedByCurrentThread((PyObject *)dict) && !_PyObject_GC_IS_SHARED(dict)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + #endif + PyObject *attr_o; + if (hint >= (size_t)FT_ATOMIC_LOAD_SSIZE_RELAXED(dk->dk_nentries)) { + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + if (dk->dk_kind != DICT_KEYS_UNICODE) { + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dk) + hint; + if (FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_key) != name) { + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + attr_o = FT_ATOMIC_LOAD_PTR(ep->me_value); + if (attr_o == NULL) { + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + STAT_INC(LOAD_ATTR, hit); + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); + if (!increfed) { + if (true) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + o = owner; + } + // _POP_TOP + { + value = o; + stack_pointer[-1] = attr; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + /* Skip 5 cache entries */ + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer += (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_BUILD_CLASS) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_BUILD_CLASS; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_BUILD_CLASS); + _PyStackRef bc; + int err; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *bc_o = _PyMapping_GetOptionalItem2(BUILTINS(), &_Py_ID(__build_class__), &err); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + if (bc_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetString(tstate, PyExc_NameError, + "__build_class__ not found"); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + bc = PyStackRef_FromPyObjectSteal(bc_o); + stack_pointer[0] = bc; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_COMMON_CONSTANT) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_COMMON_CONSTANT; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_COMMON_CONSTANT); + _PyStackRef value; + assert(oparg < NUM_COMMON_CONSTANTS); + value = PyStackRef_DupImmortal(tstate->interp->common_consts[oparg]); + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_CONST) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_CONST; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_CONST); + _PyStackRef value; + Test_EvalFrame_Loads++; + PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); + value = PyStackRef_FromPyObjectBorrow(obj); + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_DEREF) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_DEREF; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_DEREF); + _PyStackRef value; + PyCellObject *cell = (PyCellObject *)PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + _PyFrame_SetStackPointer(frame, stack_pointer); + value = _PyCell_GetStackRef(cell); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (PyStackRef_IsNull(value)) { + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_FAST) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_FAST; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_FAST); + _PyStackRef value; + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_DUP(GETLOCAL(oparg)); + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_FAST_AND_CLEAR) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_FAST_AND_CLEAR; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_FAST_AND_CLEAR); + _PyStackRef value; + value = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_NULL; + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_FAST_BORROW) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_FAST_BORROW; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_FAST_BORROW); + _PyStackRef value; + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_FAST_BORROW_LOAD_FAST_BORROW) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_FAST_BORROW_LOAD_FAST_BORROW; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_FAST_BORROW_LOAD_FAST_BORROW); + _PyStackRef value1; + _PyStackRef value2; + uint32_t oparg1 = oparg >> 4; + uint32_t oparg2 = oparg & 15; + value1 = PyStackRef_Borrow(GETLOCAL(oparg1)); + value2 = PyStackRef_Borrow(GETLOCAL(oparg2)); + stack_pointer[0] = value1; + stack_pointer[1] = value2; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_FAST_CHECK) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_FAST_CHECK; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_FAST_CHECK); + _PyStackRef value; + _PyStackRef value_s = GETLOCAL(oparg); + if (PyStackRef_IsNull(value_s)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + value = PyStackRef_DUP(value_s); + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_FAST_LOAD_FAST) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_FAST_LOAD_FAST; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_FAST_LOAD_FAST); + _PyStackRef value1; + _PyStackRef value2; + uint32_t oparg1 = oparg >> 4; + uint32_t oparg2 = oparg & 15; + value1 = PyStackRef_DUP(GETLOCAL(oparg1)); + value2 = PyStackRef_DUP(GETLOCAL(oparg2)); + stack_pointer[0] = value1; + stack_pointer[1] = value2; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_FROM_DICT_OR_DEREF) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_FROM_DICT_OR_DEREF; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_FROM_DICT_OR_DEREF); + _PyStackRef class_dict_st; + _PyStackRef value; + class_dict_st = stack_pointer[-1]; + PyObject *name; + PyObject *class_dict = PyStackRef_AsPyObjectBorrow(class_dict_st); + assert(class_dict); + assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus); + name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); + int err; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject* value_o = _PyMapping_GetOptionalItem2(class_dict, name, &err); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + if (!value_o) { + PyCellObject *cell = (PyCellObject *)PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + value_o = PyCell_GetRef(cell); + if (value_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(class_dict_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + value = PyStackRef_FromPyObjectSteal(value_o); + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_FROM_DICT_OR_GLOBALS) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_FROM_DICT_OR_GLOBALS; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_FROM_DICT_OR_GLOBALS); + _PyStackRef mod_or_class_dict; + _PyStackRef v; + mod_or_class_dict = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + int err; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *v_o = _PyMapping_GetOptionalItem2(PyStackRef_AsPyObjectBorrow(mod_or_class_dict), name, &err); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(mod_or_class_dict); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + if (v_o == NULL) { + if (PyDict_CheckExact(GLOBALS()) + && PyDict_CheckExact(BUILTINS())) + { + _PyFrame_SetStackPointer(frame, stack_pointer); + v_o = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(), + (PyDictObject *)BUILTINS(), + name); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (v_o == NULL) { + if (!_PyErr_Occurred(tstate)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + JUMP_TO_LABEL(error); + } + if (PyLazyImport_CheckExact(v_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + Py_SETREF(v_o, l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (v_o == NULL) { + JUMP_TO_LABEL(error); + } + } + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + v_o = _PyMapping_GetOptionalItem2(GLOBALS(), name, &err); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + if (v_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + v_o = _PyMapping_GetOptionalItem2(BUILTINS(), name, &err); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + if (v_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + } + if (PyLazyImport_CheckExact(v_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + Py_SETREF(v_o, l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (v_o == NULL) { + JUMP_TO_LABEL(error); + } + } + } + } + v = PyStackRef_FromPyObjectSteal(v_o); + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_GLOBAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_GLOBAL; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(LOAD_GLOBAL); + PREDICTED_LOAD_GLOBAL:; + _Py_CODEUNIT* const this_instr = next_instr - 5; + (void)this_instr; + _PyStackRef *res; + _PyStackRef *null; + // _SPECIALIZE_LOAD_GLOBAL + { + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(LOAD_GLOBAL); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + /* Skip 1 cache entry */ + /* Skip 1 cache entry */ + /* Skip 1 cache entry */ + // _LOAD_GLOBAL + { + res = &stack_pointer[0]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (PyStackRef_IsNull(*res)) { + JUMP_TO_LABEL(error); + } + } + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[1]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer += 1 + (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_GLOBAL_BUILTIN) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_GLOBAL_BUILTIN; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(LOAD_GLOBAL_BUILTIN); + static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); + _PyStackRef res; + _PyStackRef *null; + /* Skip 1 cache entry */ + // _GUARD_GLOBALS_VERSION + { + uint16_t version = read_u16(&this_instr[2].cache); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + if (!PyDict_CheckExact(dict)) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + } + // _LOAD_GLOBAL_BUILTINS + { + uint16_t version = read_u16(&this_instr[3].cache); + uint16_t index = read_u16(&this_instr[4].cache); + PyDictObject *dict = (PyDictObject *)BUILTINS(); + if (!PyDict_CheckExact(dict)) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + if (res_o == NULL) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } + #if Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res); + if (!increfed) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } + #else + res = PyStackRef_FromPyObjectNew(res_o); + #endif + STAT_INC(LOAD_GLOBAL, hit); + } + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[1]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer[0] = res; + stack_pointer += 1 + (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_GLOBAL_MODULE) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_GLOBAL_MODULE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(LOAD_GLOBAL_MODULE); + static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); + _PyStackRef res; + _PyStackRef *null; + /* Skip 1 cache entry */ + // _NOP + { + } + // _LOAD_GLOBAL_MODULE + { + uint16_t version = read_u16(&this_instr[2].cache); + uint16_t index = read_u16(&this_instr[4].cache); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + if (!PyDict_CheckExact(dict)) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); + assert(index < DK_SIZE(keys)); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + if (res_o == NULL) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } + #if Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res); + if (!increfed) { + UPDATE_MISS_STATS(LOAD_GLOBAL); + assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); + JUMP_TO_PREDICTED(LOAD_GLOBAL); + } + #else + res = PyStackRef_FromPyObjectNew(res_o); + #endif + STAT_INC(LOAD_GLOBAL, hit); + } + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[1]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer[0] = res; + stack_pointer += 1 + (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_LOCALS) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_LOCALS; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_LOCALS); + _PyStackRef locals; + PyObject *l = LOCALS(); + if (l == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetString(tstate, PyExc_SystemError, + "no locals found"); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + locals = PyStackRef_FromPyObjectNew(l); + stack_pointer[0] = locals; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_NAME) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_NAME; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_NAME); + _PyStackRef v; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *v_o = _PyEval_LoadName(tstate, frame, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (v_o == NULL) { + JUMP_TO_LABEL(error); + } + if (PyLazyImport_CheckExact(v_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (l_v == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(v_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyDict_SetItem(GLOBALS(), name, l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(v_o); + Py_DECREF(l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_SETREF(v_o, l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + v = PyStackRef_FromPyObjectSteal(v_o); + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_SMALL_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_SMALL_INT; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_SMALL_INT); + _PyStackRef value; + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_SPECIAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_SPECIAL; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(LOAD_SPECIAL); + _PyStackRef self; + _PyStackRef *method_and_self; + // _INSERT_NULL + { + self = stack_pointer[-1]; + method_and_self = &stack_pointer[-1]; + method_and_self[1] = self; + method_and_self[0] = PyStackRef_NULL; + } + // _LOAD_SPECIAL + { + method_and_self = &stack_pointer[-1]; + PyObject *name = _Py_SpecialMethods[oparg].name; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyObject_LookupSpecialMethod(name, method_and_self); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err <= 0) { + if (err == 0) { + PyObject *owner = PyStackRef_AsPyObjectBorrow(method_and_self[1]); + _PyFrame_SetStackPointer(frame, stack_pointer); + const char *errfmt = _PyEval_SpecialMethodCanSuggest(owner, oparg) + ? _Py_SpecialMethods[oparg].error_suggestion + : _Py_SpecialMethods[oparg].error; + stack_pointer = _PyFrame_GetStackPointer(frame); + assert(!_PyErr_Occurred(tstate)); + assert(errfmt != NULL); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, errfmt, owner); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(LOAD_SUPER_ATTR) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_SUPER_ATTR; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(LOAD_SUPER_ATTR); + PREDICTED_LOAD_SUPER_ATTR:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + opcode = LOAD_SUPER_ATTR; + _PyStackRef global_super_st; + _PyStackRef class_st; + _PyStackRef self_st; + _PyStackRef attr; + _PyStackRef *null; + // _SPECIALIZE_LOAD_SUPER_ATTR + { + class_st = stack_pointer[-2]; + global_super_st = stack_pointer[-3]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + int load_method = oparg & 1; + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_LoadSuperAttr(global_super_st, class_st, next_instr, load_method); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(LOAD_SUPER_ATTR); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + // _LOAD_SUPER_ATTR + { + self_st = stack_pointer[-1]; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { + PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_CALL, + frame, this_instr, global_super, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = self_st; + self_st = PyStackRef_NULL; + stack_pointer[-1] = self_st; + PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-2] = class_st; + PyStackRef_CLOSE(tmp); + tmp = global_super_st; + global_super_st = PyStackRef_NULL; + stack_pointer[-3] = global_super_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_LABEL(error); + } + } + PyObject *super; + { + PyObject *stack[] = {class, self}; + _PyFrame_SetStackPointer(frame, stack_pointer); + super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { + PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; + if (super == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, global_super, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, global_super, arg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_CLEAR(super); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + } + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = self_st; + self_st = PyStackRef_NULL; + stack_pointer[-1] = self_st; + PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-2] = class_st; + PyStackRef_CLOSE(tmp); + tmp = global_super_st; + global_super_st = PyStackRef_NULL; + stack_pointer[-3] = global_super_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (super == NULL) { + JUMP_TO_LABEL(error); + } + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *attr_o = PyObject_GetAttr(super, name); + Py_DECREF(super); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (attr_o == NULL) { + JUMP_TO_LABEL(error); + } + attr = PyStackRef_FromPyObjectSteal(attr_o); + } + // _PUSH_NULL_CONDITIONAL + { + null = &stack_pointer[1]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + } + stack_pointer[0] = attr; + stack_pointer += 1 + (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_SUPER_ATTR_ATTR) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_SUPER_ATTR_ATTR; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(LOAD_SUPER_ATTR_ATTR); + static_assert(INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR == 1, "incorrect cache size"); + _PyStackRef global_super_st; + _PyStackRef class_st; + _PyStackRef self_st; + _PyStackRef attr_st; + /* Skip 1 cache entry */ + self_st = stack_pointer[-1]; + class_st = stack_pointer[-2]; + global_super_st = stack_pointer[-3]; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + assert(!(oparg & 1)); + if (global_super != (PyObject *)&PySuper_Type) { + UPDATE_MISS_STATS(LOAD_SUPER_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); + JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); + } + if (!PyType_Check(class)) { + UPDATE_MISS_STATS(LOAD_SUPER_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); + JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); + } + STAT_INC(LOAD_SUPER_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *attr = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL); + _PyStackRef tmp = self_st; + self_st = PyStackRef_NULL; + stack_pointer[-1] = self_st; + PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-2] = class_st; + PyStackRef_CLOSE(tmp); + tmp = global_super_st; + global_super_st = PyStackRef_NULL; + stack_pointer[-3] = global_super_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (attr == NULL) { + JUMP_TO_LABEL(error); + } + attr_st = PyStackRef_FromPyObjectSteal(attr); + stack_pointer[0] = attr_st; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(LOAD_SUPER_ATTR_METHOD) { + #if _Py_TAIL_CALL_INTERP + int opcode = LOAD_SUPER_ATTR_METHOD; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(LOAD_SUPER_ATTR_METHOD); + static_assert(INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR == 1, "incorrect cache size"); + _PyStackRef global_super_st; + _PyStackRef class_st; + _PyStackRef self_st; + _PyStackRef attr; + _PyStackRef self_or_null; + /* Skip 1 cache entry */ + // _GUARD_LOAD_SUPER_ATTR_METHOD + { + class_st = stack_pointer[-2]; + global_super_st = stack_pointer[-3]; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + assert(oparg & 1); + if (global_super != (PyObject *)&PySuper_Type) { + UPDATE_MISS_STATS(LOAD_SUPER_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); + JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); + } + if (!PyType_Check(class)) { + UPDATE_MISS_STATS(LOAD_SUPER_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); + JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); + } + } + // _LOAD_SUPER_ATTR_METHOD + { + self_st = stack_pointer[-1]; + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + STAT_INC(LOAD_SUPER_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + PyTypeObject *cls = (PyTypeObject *)class; + int method_found = 0; + PyObject *attr_o; + { + int *method_found_ptr = &method_found; + _PyFrame_SetStackPointer(frame, stack_pointer); + attr_o = _PySuper_Lookup(cls, self, name, + Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? method_found_ptr : NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + if (attr_o == NULL) { + JUMP_TO_LABEL(error); + } + if (method_found) { + self_or_null = self_st; + } else { + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(self_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + self_or_null = PyStackRef_NULL; + stack_pointer += 1; + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = global_super_st; + global_super_st = self_or_null; + stack_pointer[-2] = global_super_st; + PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-1] = class_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + attr = PyStackRef_FromPyObjectSteal(attr_o); + } + stack_pointer[0] = attr; + stack_pointer[1] = self_or_null; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(MAKE_CELL) { + #if _Py_TAIL_CALL_INTERP + int opcode = MAKE_CELL; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MAKE_CELL); + PyObject *initial = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + PyObject *cell = PyCell_New(initial); + if (cell == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_FromPyObjectSteal(cell); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(MAKE_FUNCTION) { + #if _Py_TAIL_CALL_INTERP + int opcode = MAKE_FUNCTION; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MAKE_FUNCTION); + _PyStackRef codeobj_st; + _PyStackRef func; + _PyStackRef co; + _PyStackRef value; + // _MAKE_FUNCTION + { + codeobj_st = stack_pointer[-1]; + PyObject *codeobj = PyStackRef_AsPyObjectBorrow(codeobj_st); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyFunctionObject *func_obj = (PyFunctionObject *) + PyFunction_New(codeobj, GLOBALS()); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (func_obj == NULL) { + JUMP_TO_LABEL(error); + } + co = codeobj_st; + _PyFunction_SetVersion( + func_obj, ((PyCodeObject *)codeobj)->co_version); + func = PyStackRef_FromPyObjectSteal((PyObject *)func_obj); + } + // _POP_TOP + { + value = co; + stack_pointer[-1] = func; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(MAP_ADD) { + #if _Py_TAIL_CALL_INTERP + int opcode = MAP_ADD; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MAP_ADD); + _PyStackRef dict_st; + _PyStackRef key; + _PyStackRef value; + value = stack_pointer[-1]; + key = stack_pointer[-2]; + dict_st = stack_pointer[-3 - (oparg - 1)]; + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(PyDict_CheckExact(dict)); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyDict_SetItem_Take2( + (PyDictObject *)dict, + PyStackRef_AsPyObjectSteal(key), + PyStackRef_AsPyObjectSteal(value) + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(pop_2_error); + } + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(MATCH_CLASS) { + #if _Py_TAIL_CALL_INTERP + int opcode = MATCH_CLASS; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MATCH_CLASS); + _PyStackRef subject; + _PyStackRef type; + _PyStackRef names; + _PyStackRef attrs; + _PyStackRef s; + _PyStackRef tp; + _PyStackRef n; + _PyStackRef value; + // _MATCH_CLASS + { + names = stack_pointer[-1]; + type = stack_pointer[-2]; + subject = stack_pointer[-3]; + assert(PyTuple_CheckExact(PyStackRef_AsPyObjectBorrow(names))); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *attrs_o = _PyEval_MatchClass(tstate, + PyStackRef_AsPyObjectBorrow(subject), + PyStackRef_AsPyObjectBorrow(type), oparg, + PyStackRef_AsPyObjectBorrow(names)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (attrs_o) { + assert(PyTuple_CheckExact(attrs_o)); + attrs = PyStackRef_FromPyObjectSteal(attrs_o); + } + else { + if (_PyErr_Occurred(tstate)) { + JUMP_TO_LABEL(error); + } + attrs = PyStackRef_None; + } + s = subject; + tp = type; + n = names; + } + // _POP_TOP + { + value = n; + stack_pointer[-3] = attrs; + stack_pointer[-2] = s; + stack_pointer[-1] = tp; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = tp; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = s; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(MATCH_KEYS) { + #if _Py_TAIL_CALL_INTERP + int opcode = MATCH_KEYS; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MATCH_KEYS); + _PyStackRef subject; + _PyStackRef keys; + _PyStackRef values_or_none; + keys = stack_pointer[-1]; + subject = stack_pointer[-2]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *values_or_none_o = _PyEval_MatchKeys(tstate, + PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(keys)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (values_or_none_o == NULL) { + JUMP_TO_LABEL(error); + } + values_or_none = PyStackRef_FromPyObjectSteal(values_or_none_o); + stack_pointer[0] = values_or_none; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(MATCH_MAPPING) { + #if _Py_TAIL_CALL_INTERP + int opcode = MATCH_MAPPING; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MATCH_MAPPING); + _PyStackRef subject; + _PyStackRef res; + subject = stack_pointer[-1]; + int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; + res = match ? PyStackRef_True : PyStackRef_False; + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(MATCH_SEQUENCE) { + #if _Py_TAIL_CALL_INTERP + int opcode = MATCH_SEQUENCE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(MATCH_SEQUENCE); + _PyStackRef subject; + _PyStackRef res; + subject = stack_pointer[-1]; + int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; + res = match ? PyStackRef_True : PyStackRef_False; + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(NOP) { + #if _Py_TAIL_CALL_INTERP + int opcode = NOP; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(NOP); + DISPATCH(); + } + + TARGET(NOT_TAKEN) { + #if _Py_TAIL_CALL_INTERP + int opcode = NOT_TAKEN; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(NOT_TAKEN); + DISPATCH(); + } + + TARGET(POP_EXCEPT) { + #if _Py_TAIL_CALL_INTERP + int opcode = POP_EXCEPT; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(POP_EXCEPT); + _PyStackRef exc_value; + exc_value = stack_pointer[-1]; + _PyErr_StackItem *exc_info = tstate->exc_info; + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XSETREF(exc_info->exc_value, + PyStackRef_IsNone(exc_value) + ? NULL : PyStackRef_AsPyObjectSteal(exc_value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(POP_ITER) { + #if _Py_TAIL_CALL_INTERP + int opcode = POP_ITER; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(POP_ITER); + _PyStackRef iter; + _PyStackRef index_or_null; + index_or_null = stack_pointer[-1]; + iter = stack_pointer[-2]; + (void)index_or_null; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(iter); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(POP_JUMP_IF_FALSE) { + #if _Py_TAIL_CALL_INTERP + int opcode = POP_JUMP_IF_FALSE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_FALSE); + _PyStackRef cond; + /* Skip 1 cache entry */ + cond = stack_pointer[-1]; + assert(PyStackRef_BoolCheck(cond)); + int flag = PyStackRef_IsFalse(cond); + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); + JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(POP_JUMP_IF_NONE) { + #if _Py_TAIL_CALL_INTERP + int opcode = POP_JUMP_IF_NONE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_NONE); + _PyStackRef value; + _PyStackRef b; + _PyStackRef cond; + /* Skip 1 cache entry */ + // _IS_NONE + { + value = stack_pointer[-1]; + if (PyStackRef_IsNone(value)) { + b = PyStackRef_True; + } + else { + b = PyStackRef_False; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = value; + value = b; + stack_pointer[-1] = value; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _POP_JUMP_IF_TRUE + { + cond = b; + assert(PyStackRef_BoolCheck(cond)); + int flag = PyStackRef_IsTrue(cond); + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); + JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(POP_JUMP_IF_NOT_NONE) { + #if _Py_TAIL_CALL_INTERP + int opcode = POP_JUMP_IF_NOT_NONE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_NOT_NONE); + _PyStackRef value; + _PyStackRef b; + _PyStackRef cond; + /* Skip 1 cache entry */ + // _IS_NONE + { + value = stack_pointer[-1]; + if (PyStackRef_IsNone(value)) { + b = PyStackRef_True; + } + else { + b = PyStackRef_False; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = value; + value = b; + stack_pointer[-1] = value; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _POP_JUMP_IF_FALSE + { + cond = b; + assert(PyStackRef_BoolCheck(cond)); + int flag = PyStackRef_IsFalse(cond); + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); + JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(POP_JUMP_IF_TRUE) { + #if _Py_TAIL_CALL_INTERP + int opcode = POP_JUMP_IF_TRUE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(POP_JUMP_IF_TRUE); + _PyStackRef cond; + /* Skip 1 cache entry */ + cond = stack_pointer[-1]; + assert(PyStackRef_BoolCheck(cond)); + int flag = PyStackRef_IsTrue(cond); + RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); + JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(POP_TOP) { + #if _Py_TAIL_CALL_INTERP + int opcode = POP_TOP; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(POP_TOP); + _PyStackRef value; + value = stack_pointer[-1]; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(PUSH_EXC_INFO) { + #if _Py_TAIL_CALL_INTERP + int opcode = PUSH_EXC_INFO; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(PUSH_EXC_INFO); + _PyStackRef exc; + _PyStackRef prev_exc; + _PyStackRef new_exc; + exc = stack_pointer[-1]; + _PyErr_StackItem *exc_info = tstate->exc_info; + if (exc_info->exc_value != NULL) { + prev_exc = PyStackRef_FromPyObjectSteal(exc_info->exc_value); + } + else { + prev_exc = PyStackRef_None; + } + assert(PyStackRef_ExceptionInstanceCheck(exc)); + exc_info->exc_value = PyStackRef_AsPyObjectNew(exc); + new_exc = exc; + stack_pointer[-1] = prev_exc; + stack_pointer[0] = new_exc; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(PUSH_NULL) { + #if _Py_TAIL_CALL_INTERP + int opcode = PUSH_NULL; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(PUSH_NULL); + _PyStackRef res; + res = PyStackRef_NULL; + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(RAISE_VARARGS) { + #if _Py_TAIL_CALL_INTERP + int opcode = RAISE_VARARGS; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RAISE_VARARGS); + _PyStackRef *args; + args = &stack_pointer[-oparg]; + assert(oparg < 3); + PyObject *cause = oparg == 2 ? PyStackRef_AsPyObjectSteal(args[1]) : NULL; + PyObject *exc = oparg > 0 ? PyStackRef_AsPyObjectSteal(args[0]) : NULL; + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = do_raise(tstate, exc, cause); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + assert(oparg == 0); + _PyFrame_SetStackPointer(frame, stack_pointer); + monitor_reraise(tstate, frame, this_instr); + JUMP_TO_LABEL(exception_unwind); + } + JUMP_TO_LABEL(error); + } + + TARGET(RERAISE) { + #if _Py_TAIL_CALL_INTERP + int opcode = RERAISE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RERAISE); + _PyStackRef *values; + _PyStackRef exc_st; + exc_st = stack_pointer[-1]; + values = &stack_pointer[-1 - oparg]; + PyObject *exc = PyStackRef_AsPyObjectSteal(exc_st); + assert(oparg >= 0 && oparg <= 2); + if (oparg) { + frame->instr_ptr = _PyFrame_GetBytecode(frame) + PyStackRef_UntagInt(values[0]); + } + assert(exc && PyExceptionInstance_Check(exc)); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetRaisedException(tstate, exc); + monitor_reraise(tstate, frame, this_instr); + JUMP_TO_LABEL(exception_unwind); + } + + TARGET(RESERVED) { + #if _Py_TAIL_CALL_INTERP + int opcode = RESERVED; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RESERVED); + assert(0 && "Executing RESERVED instruction."); + Py_FatalError("Executing RESERVED instruction."); + DISPATCH(); + } + + TARGET(RESUME) { + #if _Py_TAIL_CALL_INTERP + int opcode = RESUME; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(RESUME); + PREDICTED_RESUME:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + // _LOAD_BYTECODE + { + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_CODEUNIT *bytecode = + _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (bytecode == NULL) { + JUMP_TO_LABEL(error); + } + ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame); + frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; + frame->instr_ptr = bytecode + off; + next_instr = frame->instr_ptr; + DISPATCH(); + } + #endif + } + // _MAYBE_INSTRUMENT + { + #ifdef Py_GIL_DISABLED + + int check_instrumentation = 1; + #else + int check_instrumentation = (tstate->tracing == 0); + #endif + if (check_instrumentation) { + uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & ~_PY_EVAL_EVENTS_MASK; + uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + if (code_version != global_version) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + next_instr = this_instr; + DISPATCH(); + } + } + } + // _QUICKEN_RESUME + { + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_Resume(this_instr, tstate, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_IF_NOT_YIELD_FROM + { + Test_EvalFrame_Resumes++; + if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); + } + } + } + DISPATCH(); + } + + TARGET(RESUME_CHECK) { + #if _Py_TAIL_CALL_INTERP + int opcode = RESUME_CHECK; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(RESUME_CHECK); + static_assert(1 == 1, "incorrect cache size"); + /* Skip 1 cache entry */ + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + uintptr_t version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + assert((version & _PY_EVAL_EVENTS_MASK) == 0); + if (eval_breaker != version) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } + #endif + DISPATCH(); + } + + TARGET(RESUME_CHECK_JIT) { + #if _Py_TAIL_CALL_INTERP + int opcode = RESUME_CHECK_JIT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(RESUME_CHECK_JIT); + static_assert(1 == 1, "incorrect cache size"); + /* Skip 1 cache entry */ + // _RESUME_CHECK + { + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + uintptr_t version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + assert((version & _PY_EVAL_EVENTS_MASK) == 0); + if (eval_breaker != version) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } + #endif + } + // _JIT + { + #ifdef _Py_TIER2 + bool is_resume = this_instr->op.code == RESUME_CHECK_JIT; + _Py_BackoffCounter counter = this_instr[1].counter; + if ((backoff_counter_triggers(counter) && + !IS_JIT_TRACING() && + (this_instr->op.code == JUMP_BACKWARD_JIT || is_resume)) && + next_instr->op.code != ENTER_EXECUTOR) { + _Py_CODEUNIT *insert_exec_at = this_instr; + while (oparg > 255) { + oparg >>= 8; + insert_exec_at--; + } + int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, + is_resume ? insert_exec_at : next_instr, stack_pointer, 0, NULL, oparg, NULL); + if (succ) { + ENTER_TRACING(); + } + else { + this_instr[1].counter = restart_backoff_counter(counter); + } + } + else { + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + } + #endif + } + DISPATCH(); + } + + TARGET(RETURN_GENERATOR) { + #if _Py_TAIL_CALL_INTERP + int opcode = RETURN_GENERATOR; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RETURN_GENERATOR); + _PyStackRef res; + assert(PyStackRef_FunctionCheck(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (gen == NULL) { + JUMP_TO_LABEL(error); + } + assert(STACK_LEVEL() <= 2); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *gen_frame = &gen->gi_iframe; + frame->instr_ptr++; + _PyFrame_Copy(frame, gen_frame); + assert(frame->frame_obj == NULL); + gen->gi_frame_state = FRAME_CREATED; + gen_frame->owner = FRAME_OWNED_BY_GENERATOR; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *prev = frame->previous; + _PyThreadState_PopFrame(tstate, frame); + frame = tstate->current_frame = prev; + LOAD_IP(frame->return_offset); + stack_pointer = _PyFrame_GetStackPointer(frame); + res = PyStackRef_FromPyObjectStealMortal((PyObject *)gen); + LLTRACE_RESUME_FRAME(); + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(RETURN_VALUE) { + #if _Py_TAIL_CALL_INTERP + int opcode = RETURN_VALUE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(RETURN_VALUE); + _PyStackRef value; + _PyStackRef retval; + _PyStackRef res; + // _MAKE_HEAP_SAFE + { + value = stack_pointer[-1]; + value = PyStackRef_MakeHeapSafe(value); + } + // _RETURN_VALUE + { + retval = value; + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + _PyStackRef temp = retval; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + stack_pointer = _PyFrame_GetStackPointer(frame); + LOAD_IP(frame->return_offset); + res = temp; + LLTRACE_RESUME_FRAME(); + } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(SEND) { + #if _Py_TAIL_CALL_INTERP + int opcode = SEND; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(SEND); + PREDICTED_SEND:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + _PyStackRef receiver; + _PyStackRef null_or_index; + _PyStackRef v; + _PyStackRef retval; + // _SPECIALIZE_SEND + { + receiver = stack_pointer[-3]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_Send(receiver, next_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(SEND); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + // _SEND + { + v = stack_pointer[-1]; + null_or_index = stack_pointer[-2]; + PyObject *receiver_o = PyStackRef_AsPyObjectBorrow(receiver); + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + if (!IS_PEP523_HOOKED(tstate) && + (Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) && + gen_try_set_executing((PyGenObject *)receiver_o)) + { + PyGenObject *gen = (PyGenObject *)receiver_o; + _PyInterpreterFrame *gen_frame = &gen->gi_iframe; + _PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v)); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert( 2u + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)( 2u + oparg); + assert(gen_frame->previous == NULL); + gen_frame->previous = frame; + DISPATCH_INLINED(gen_frame); + } + if (!PyStackRef_IsNull(null_or_index) && PyStackRef_IsNone(v)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef item = _PyForIter_VirtualIteratorNext(tstate, frame, receiver, &null_or_index); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (!PyStackRef_IsValid(item)) { + if (PyStackRef_IsError(item)) { + JUMP_TO_LABEL(error); + } + JUMPBY(oparg); + stack_pointer[-2] = null_or_index; + DISPATCH(); + } + retval = item; + } + else { + PyObject *v_o = PyStackRef_AsPyObjectBorrow(v); + _PyFrame_SetStackPointer(frame, stack_pointer); + PySendResultPair res = _PyIter_Send(receiver_o, v_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res.kind == PYGEN_ERROR) { + JUMP_TO_LABEL(error); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + retval = PyStackRef_FromPyObjectSteal(res.object); + if (res.kind == PYGEN_RETURN) { + JUMPBY(oparg); + } + stack_pointer += 1; + } + } + stack_pointer[-2] = null_or_index; + stack_pointer[-1] = retval; + DISPATCH(); + } + + TARGET(SEND_ASYNC_GEN) { + #if _Py_TAIL_CALL_INTERP + int opcode = SEND_ASYNC_GEN; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(SEND_ASYNC_GEN); + static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); + _PyStackRef iter; + _PyStackRef null_in; + _PyStackRef v; + _PyStackRef asend; + _PyStackRef null_out; + _PyStackRef retval; + /* Skip 1 cache entry */ + // _GUARD_3OS_ASYNC_GEN_ASEND + { + iter = stack_pointer[-3]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (!PyAsyncGenASend_CheckExact(iter_o)) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } + } + // _SEND_ASYNC_GEN + { + v = stack_pointer[-1]; + null_in = stack_pointer[-2]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + assert(PyAsyncGenASend_CheckExact(iter_o)); + PyObject *val = PyStackRef_AsPyObjectBorrow(v); + PyObject *retval_o; + _PyFrame_SetStackPointer(frame, stack_pointer); + PySendResult what = _PyAsyncGenASend_Send(iter_o, val, &retval_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (what == PYGEN_ERROR) { + JUMP_TO_LABEL(error); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + asend = iter; + null_out = null_in; + retval = PyStackRef_FromPyObjectSteal(retval_o); + if (what == PYGEN_RETURN) { + JUMPBY(oparg); + } + } + stack_pointer[-2] = asend; + stack_pointer[-1] = null_out; + stack_pointer[0] = retval; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(SEND_GEN) { + #if _Py_TAIL_CALL_INTERP + int opcode = SEND_GEN; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(SEND_GEN); + static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); + _PyStackRef receiver; + _PyStackRef v; + _PyStackRef gen_frame; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } + } + // _SEND_GEN_FRAME + { + v = stack_pointer[-1]; + receiver = stack_pointer[-3]; + PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver); + if (Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } + if (!gen_try_set_executing((PyGenObject *)gen)) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } + STAT_INC(SEND, hit); + _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; + _PyFrame_StackPush(pushed_frame, PyStackRef_MakeHeapSafe(v)); + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert( 2u + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)( 2u + oparg); + pushed_frame->previous = frame; + gen_frame = PyStackRef_Wrap(pushed_frame); + } + // _PUSH_FRAME + { + new_frame = gen_frame; + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(SEND_VIRTUAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = SEND_VIRTUAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(SEND_VIRTUAL); + static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); + _PyStackRef val; + _PyStackRef nos; + _PyStackRef iter; + _PyStackRef null_or_index; + _PyStackRef none; + _PyStackRef next; + /* Skip 1 cache entry */ + // _GUARD_TOS_IS_NONE + { + val = stack_pointer[-1]; + if (!PyStackRef_IsNone(val)) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } + } + // _GUARD_NOS_NOT_NULL + { + nos = stack_pointer[-2]; + if (PyStackRef_IsNull(nos)) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } + } + // _SEND_VIRTUAL + { + none = val; + null_or_index = nos; + iter = stack_pointer[-3]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyObjectIndexPair next_index = Py_TYPE(iter_o)->_tp_iteritem(iter_o, index); + stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + JUMP_TO_LABEL(error); + } + next = none; + JUMPBY(oparg); + DISPATCH(); + } + next = PyStackRef_FromPyObjectSteal(next_o); + null_or_index = PyStackRef_TagInt(index); + } + stack_pointer[-2] = null_or_index; + stack_pointer[-1] = next; + DISPATCH(); + } + + TARGET(SETUP_ANNOTATIONS) { + #if _Py_TAIL_CALL_INTERP + int opcode = SETUP_ANNOTATIONS; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(SETUP_ANNOTATIONS); + if (LOCALS() == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_SystemError, + "no locals found when setting up annotations"); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + int err; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject* ann_dict = _PyMapping_GetOptionalItem2(LOCALS(), &_Py_ID(__annotations__), &err); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + if (ann_dict == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + ann_dict = PyDict_New(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (ann_dict == NULL) { + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), + ann_dict); + Py_DECREF(ann_dict); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(ann_dict); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(SET_ADD) { + #if _Py_TAIL_CALL_INTERP + int opcode = SET_ADD; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(SET_ADD); + _PyStackRef set; + _PyStackRef v; + v = stack_pointer[-1]; + set = stack_pointer[-2 - (oparg-1)]; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PySet_AddTakeRef((PySetObject *)PyStackRef_AsPyObjectBorrow(set), + PyStackRef_AsPyObjectSteal(v)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(pop_1_error); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(SET_FUNCTION_ATTRIBUTE) { + #if _Py_TAIL_CALL_INTERP + int opcode = SET_FUNCTION_ATTRIBUTE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(SET_FUNCTION_ATTRIBUTE); + _PyStackRef attr_st; + _PyStackRef func_in; + _PyStackRef func_out; + func_in = stack_pointer[-1]; + attr_st = stack_pointer[-2]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_in); + PyObject *attr = PyStackRef_AsPyObjectSteal(attr_st); + func_out = func_in; + assert(PyFunction_Check(func)); + size_t offset = _Py_FunctionAttributeOffsets[oparg]; + assert(offset != 0); + PyObject **ptr = (PyObject **)(((char *)func) + offset); + assert(*ptr == NULL); + *ptr = attr; + stack_pointer[-2] = func_out; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(SET_UPDATE) { + #if _Py_TAIL_CALL_INTERP + int opcode = SET_UPDATE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(SET_UPDATE); + _PyStackRef set; + _PyStackRef iterable; + _PyStackRef i; + _PyStackRef value; + // _SET_UPDATE + { + iterable = stack_pointer[-1]; + set = stack_pointer[-2 - (oparg-1)]; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PySet_Update(PyStackRef_AsPyObjectBorrow(set), + PyStackRef_AsPyObjectBorrow(iterable)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + i = iterable; + } + // _POP_TOP + { + value = i; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(STORE_ATTR) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_ATTR; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR); + PREDICTED_STORE_ATTR:; + _Py_CODEUNIT* const this_instr = next_instr - 5; + (void)this_instr; + _PyStackRef owner; + _PyStackRef v; + // _SPECIALIZE_STORE_ATTR + { + owner = stack_pointer[-1]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_StoreAttr(owner, next_instr, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(STORE_ATTR); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + /* Skip 3 cache entries */ + // _STORE_ATTR + { + v = stack_pointer[-2]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyObject_SetAttr(PyStackRef_AsPyObjectBorrow(owner), + name, PyStackRef_AsPyObjectBorrow(v)); + _PyStackRef tmp = owner; + owner = PyStackRef_NULL; + stack_pointer[-1] = owner; + PyStackRef_CLOSE(tmp); + tmp = v; + v = PyStackRef_NULL; + stack_pointer[-2] = v; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (err) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(STORE_ATTR_INSTANCE_VALUE) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_ATTR_INSTANCE_VALUE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR_INSTANCE_VALUE); + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); + _PyStackRef value; + _PyStackRef owner; + _PyStackRef o; + /* Skip 1 cache entry */ + // _LOCK_OBJECT + { + value = stack_pointer[-1]; + if (!LOCK_OBJECT(PyStackRef_AsPyObjectBorrow(value))) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + } + // _GUARD_TYPE_VERSION_LOCKED + { + owner = value; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(type_version != 0); + PyTypeObject *tp = Py_TYPE(owner_o); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UNLOCK_OBJECT(owner_o); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + } + } + // _GUARD_DORV_NO_DICT + { + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_dictoffset < 0); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (_PyObject_GetManagedDict(owner_o) || + !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UNLOCK_OBJECT(owner_o); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + } + } + // _STORE_ATTR_INSTANCE_VALUE + { + value = stack_pointer[-2]; + uint16_t offset = read_u16(&this_instr[4].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + STAT_INC(STORE_ATTR, hit); + assert(_PyObject_GetManagedDict(owner_o) == NULL); + PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); + PyObject *old_value = *value_ptr; + FT_ATOMIC_STORE_PTR_RELEASE(*value_ptr, PyStackRef_AsPyObjectSteal(value)); + if (old_value == NULL) { + PyDictValues *values = _PyObject_InlineValues(owner_o); + Py_ssize_t index = value_ptr - values->values; + _PyDictValues_AddToInsertionOrder(values, index); + } + UNLOCK_OBJECT(owner_o); + o = owner; + stack_pointer[-2] = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(STORE_ATTR_SLOT) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_ATTR_SLOT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR_SLOT); + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef value; + _PyStackRef o; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + } + // _STORE_ATTR_SLOT + { + value = stack_pointer[-2]; + uint16_t index = read_u16(&this_instr[4].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (!LOCK_OBJECT(owner_o)) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + char *addr = (char *)owner_o + index; + STAT_INC(STORE_ATTR, hit); + PyObject *old_value = *(PyObject **)addr; + FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); + UNLOCK_OBJECT(owner_o); + o = owner; + stack_pointer[-2] = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(STORE_ATTR_WITH_HINT) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_ATTR_WITH_HINT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 5; + INSTRUCTION_STATS(STORE_ATTR_WITH_HINT); + static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef value; + _PyStackRef o; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + } + // _STORE_ATTR_WITH_HINT + { + value = stack_pointer[-2]; + uint16_t hint = read_u16(&this_instr[4].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictObject *dict = _PyObject_GetManagedDict(owner_o); + if (dict == NULL) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + if (!LOCK_OBJECT(dict)) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + assert(PyDict_CheckExact((PyObject *)dict)); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + if (hint >= (size_t)dict->ma_keys->dk_nentries || + dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) { + UNLOCK_OBJECT(dict); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + } + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; + if (ep->me_key != name) { + UNLOCK_OBJECT(dict); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + } + PyObject *old_value = ep->me_value; + if (old_value == NULL) { + UNLOCK_OBJECT(dict); + if (true) { + UPDATE_MISS_STATS(STORE_ATTR); + assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); + JUMP_TO_PREDICTED(STORE_ATTR); + } + } + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); + UNLOCK_OBJECT(dict); + STAT_INC(STORE_ATTR, hit); + o = owner; + stack_pointer[-2] = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(STORE_DEREF) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_DEREF; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(STORE_DEREF); + _PyStackRef v; + v = stack_pointer[-1]; + PyCellObject *cell = (PyCellObject *)PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyCell_SetTakeRef(cell, PyStackRef_AsPyObjectSteal(v)); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(STORE_FAST) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_FAST; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(STORE_FAST); + _PyStackRef value; + _PyStackRef trash; + // _SWAP_FAST + { + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + } + // _POP_TOP + { + value = trash; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(STORE_FAST_LOAD_FAST) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_FAST_LOAD_FAST; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(STORE_FAST_LOAD_FAST); + _PyStackRef value1; + _PyStackRef value2; + value1 = stack_pointer[-1]; + uint32_t oparg1 = oparg >> 4; + uint32_t oparg2 = oparg & 15; + _PyStackRef tmp = GETLOCAL(oparg1); + GETLOCAL(oparg1) = value1; + value2 = PyStackRef_DUP(GETLOCAL(oparg2)); + stack_pointer[-1] = value2; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(STORE_FAST_STORE_FAST) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_FAST_STORE_FAST; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(STORE_FAST_STORE_FAST); + _PyStackRef value2; + _PyStackRef value1; + value1 = stack_pointer[-1]; + value2 = stack_pointer[-2]; + uint32_t oparg1 = oparg >> 4; + uint32_t oparg2 = oparg & 15; + _PyStackRef tmp = GETLOCAL(oparg1); + GETLOCAL(oparg1) = value1; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + tmp = GETLOCAL(oparg2); + GETLOCAL(oparg2) = value2; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH(); + } + + TARGET(STORE_GLOBAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_GLOBAL; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(STORE_GLOBAL); + _PyStackRef v; + v = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + DISPATCH(); + } + + TARGET(STORE_NAME) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_NAME; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(STORE_NAME); + _PyStackRef v; + v = stack_pointer[-1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *ns = LOCALS(); + int err; + if (ns == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_SystemError, + "no locals found when storing %R", name); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + if (PyDict_CheckExact(ns)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + err = PyDict_SetItem(ns, name, PyStackRef_AsPyObjectBorrow(v)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + err = PyObject_SetItem(ns, name, PyStackRef_AsPyObjectBorrow(v)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + JUMP_TO_LABEL(error); + } + DISPATCH(); + } + + TARGET(STORE_SLICE) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_SLICE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(STORE_SLICE); + _PyStackRef v; + _PyStackRef container; + _PyStackRef start; + _PyStackRef stop; + // _SPECIALIZE_STORE_SLICE + { + #if ENABLE_SPECIALIZATION + OPCODE_DEFERRED_INC(STORE_SLICE); + #endif /* ENABLE_SPECIALIZATION */ + } + // _STORE_SLICE + { + stop = stack_pointer[-1]; + start = stack_pointer[-2]; + container = stack_pointer[-3]; + v = stack_pointer[-4]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), + PyStackRef_AsPyObjectSteal(stop), + Py_None); + stack_pointer = _PyFrame_GetStackPointer(frame); + int err; + if (slice == NULL) { + err = 1; + } + else { + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), slice, PyStackRef_AsPyObjectBorrow(v)); + Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += 2; + } + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = container; + container = PyStackRef_NULL; + stack_pointer[-3] = container; + PyStackRef_CLOSE(tmp); + tmp = v; + v = PyStackRef_NULL; + stack_pointer[-4] = v; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -4; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (err) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(STORE_SUBSCR) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_SUBSCR; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(STORE_SUBSCR); + PREDICTED_STORE_SUBSCR:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + _PyStackRef container; + _PyStackRef sub; + _PyStackRef v; + // _SPECIALIZE_STORE_SUBSCR + { + sub = stack_pointer[-1]; + container = stack_pointer[-2]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_StoreSubscr(container, sub, next_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(STORE_SUBSCR); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + // _STORE_SUBSCR + { + v = stack_pointer[-3]; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), PyStackRef_AsPyObjectBorrow(sub), PyStackRef_AsPyObjectBorrow(v)); + _PyStackRef tmp = sub; + sub = PyStackRef_NULL; + stack_pointer[-1] = sub; + PyStackRef_CLOSE(tmp); + tmp = container; + container = PyStackRef_NULL; + stack_pointer[-2] = container; + PyStackRef_CLOSE(tmp); + tmp = v; + v = PyStackRef_NULL; + stack_pointer[-3] = v; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (err) { + JUMP_TO_LABEL(error); + } + } + DISPATCH(); + } + + TARGET(STORE_SUBSCR_DICT) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_SUBSCR_DICT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(STORE_SUBSCR_DICT); + static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); + _PyStackRef nos; + _PyStackRef value; + _PyStackRef dict_st; + _PyStackRef sub; + _PyStackRef st; + // _GUARD_NOS_DICT_STORE_SUBSCRIPT + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!Py_TYPE(o)->tp_as_mapping) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } + if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } + } + /* Skip 1 cache entry */ + // _STORE_SUBSCR_DICT + { + sub = stack_pointer[-1]; + dict_st = nos; + value = stack_pointer[-3]; + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(Py_TYPE(dict)->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript); + STAT_INC(STORE_SUBSCR, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyDict_SetItem_Take2((PyDictObject *)dict, + PyStackRef_AsPyObjectSteal(sub), + PyStackRef_AsPyObjectSteal(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(dict_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + st = dict_st; + } + // _POP_TOP + { + value = st; + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(STORE_SUBSCR_LIST_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = STORE_SUBSCR_LIST_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(STORE_SUBSCR_LIST_INT); + static_assert(INLINE_CACHE_ENTRIES_STORE_SUBSCR == 1, "incorrect cache size"); + _PyStackRef value; + _PyStackRef nos; + _PyStackRef list_st; + _PyStackRef sub_st; + _PyStackRef ls; + _PyStackRef ss; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } + } + // _GUARD_NOS_LIST + { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyList_CheckExact(o)) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } + } + /* Skip 1 cache entry */ + // _STORE_SUBSCR_LIST_INT + { + sub_st = value; + list_st = nos; + value = stack_pointer[-3]; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); + assert(PyLong_CheckExact(sub)); + assert(PyList_CheckExact(list)); + Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub); + if (!LOCK_OBJECT(list)) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } + Py_ssize_t len = PyList_GET_SIZE(list); + if (index < 0) { + index += len; + } + if (index < 0 || index >= len) { + UNLOCK_OBJECT(list); + if (true) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } + } + STAT_INC(STORE_SUBSCR, hit); + PyObject *old_value = PyList_GET_ITEM(list, index); + FT_ATOMIC_STORE_PTR_RELEASE(_PyList_ITEMS(list)[index], + PyStackRef_AsPyObjectSteal(value)); + assert(old_value != NULL); + UNLOCK_OBJECT(list); + ls = list_st; + ss = sub_st; + stack_pointer[-3] = ls; + stack_pointer[-2] = ss; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP_INT + { + value = ss; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP + { + value = ls; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(SWAP) { + #if _Py_TAIL_CALL_INTERP + int opcode = SWAP; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(SWAP); + _PyStackRef bottom; + _PyStackRef top; + top = stack_pointer[-1]; + bottom = stack_pointer[-2 - (oparg-2)]; + _PyStackRef temp = bottom; + bottom = top; + top = temp; + stack_pointer[-2 - (oparg-2)] = bottom; + stack_pointer[-1] = top; + DISPATCH(); + } + + TARGET(TO_BOOL) { + #if _Py_TAIL_CALL_INTERP + int opcode = TO_BOOL; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL); + PREDICTED_TO_BOOL:; + _Py_CODEUNIT* const this_instr = next_instr - 4; + (void)this_instr; + _PyStackRef value; + _PyStackRef res; + // _SPECIALIZE_TO_BOOL + { + value = stack_pointer[-1]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_ToBool(value, next_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(TO_BOOL); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + /* Skip 2 cache entries */ + // _TO_BOOL + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyObject_IsTrue(PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + res = err ? PyStackRef_True : PyStackRef_False; + } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(TO_BOOL_ALWAYS_TRUE) { + #if _Py_TAIL_CALL_INTERP + int opcode = TO_BOOL_ALWAYS_TRUE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_ALWAYS_TRUE); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + _PyStackRef owner; + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + /* Skip 1 cache entry */ + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } + } + // _REPLACE_WITH_TRUE + { + value = owner; + res = PyStackRef_True; + v = value; + } + // _POP_TOP + { + value = v; + stack_pointer[-1] = res; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(TO_BOOL_BOOL) { + #if _Py_TAIL_CALL_INTERP + int opcode = TO_BOOL_BOOL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_BOOL); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + _PyStackRef value; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + value = stack_pointer[-1]; + if (!PyStackRef_BoolCheck(value)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } + STAT_INC(TO_BOOL, hit); + DISPATCH(); + } + + TARGET(TO_BOOL_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = TO_BOOL_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_INT); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } + } + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _TO_BOOL_INT + { + STAT_INC(TO_BOOL, hit); + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + res = (_PyLong_IsZero((PyLongObject *)value_o)) ? PyStackRef_False : PyStackRef_True; + v = value; + } + // _POP_TOP_INT + { + value = v; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + stack_pointer[-1] = res; + DISPATCH(); + } + + TARGET(TO_BOOL_LIST) { + #if _Py_TAIL_CALL_INTERP + int opcode = TO_BOOL_LIST; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_LIST); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + _PyStackRef tos; + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + // _GUARD_TOS_LIST + { + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyList_CheckExact(o)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } + } + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _TO_BOOL_LIST + { + value = tos; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + assert(PyList_CheckExact(value_o)); + STAT_INC(TO_BOOL, hit); + res = PyList_GET_SIZE(value_o) ? PyStackRef_True : PyStackRef_False; + v = value; + } + // _POP_TOP + { + value = v; + stack_pointer[-1] = res; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(TO_BOOL_NONE) { + #if _Py_TAIL_CALL_INTERP + int opcode = TO_BOOL_NONE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_NONE); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + _PyStackRef value; + _PyStackRef res; + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + value = stack_pointer[-1]; + if (!PyStackRef_IsNone(value)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } + STAT_INC(TO_BOOL, hit); + res = PyStackRef_False; + stack_pointer[-1] = res; + DISPATCH(); + } + + TARGET(TO_BOOL_STR) { + #if _Py_TAIL_CALL_INTERP + int opcode = TO_BOOL_STR; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(TO_BOOL_STR); + static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + // _GUARD_TOS_UNICODE + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyUnicode_CheckExact(value_o)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } + } + /* Skip 1 cache entry */ + /* Skip 2 cache entries */ + // _TO_BOOL_STR + { + STAT_INC(TO_BOOL, hit); + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + res = value_o == &_Py_STR(empty) ? PyStackRef_False : PyStackRef_True; + v = value; + } + // _POP_TOP_UNICODE + { + value = v; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + stack_pointer[-1] = res; + DISPATCH(); + } + + TARGET(TRACE_RECORD) { + #if _Py_TAIL_CALL_INTERP + int opcode = TRACE_RECORD; + (void)(opcode); + #endif + _Py_CODEUNIT* const prev_instr = frame->instr_ptr; + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(TRACE_RECORD); + opcode = TRACE_RECORD; + #if _Py_TIER2 + assert(IS_JIT_TRACING()); + next_instr = this_instr; + frame->instr_ptr = prev_instr; + opcode = next_instr->op.code; + bool stop_tracing = ( + opcode == WITH_EXCEPT_START || + opcode == RERAISE || + opcode == CLEANUP_THROW || + opcode == PUSH_EXC_INFO || + opcode == INTERPRETER_EXIT || + (opcode >= MIN_INSTRUMENTED_OPCODE && opcode != ENTER_EXECUTOR) + ); + _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; + _PyJitTracerState *tracer = _tstate->jit_tracer_state; + assert(tracer != NULL); + _PyFrame_SetStackPointer(frame, stack_pointer); + int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing ? _DEOPT : 0); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (full) { + LEAVE_TRACING(); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = stop_tracing_and_jit(tstate, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + DISPATCH(); + } + for (int i = 0; i < tracer->prev_state.recorded_count; i++) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_CLEAR(tracer->prev_state.recorded_values[i]); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + tracer->prev_state.recorded_count = 0; + tracer->prev_state.instr = next_instr; + PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + if (tracer->prev_state.instr_code != (PyCodeObject *)prev_code) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_SETREF(tracer->prev_state.instr_code, (PyCodeObject*)Py_NewRef((prev_code))); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + tracer->prev_state.instr_frame = frame; + tracer->prev_state.instr_oparg = oparg; + tracer->prev_state.instr_stacklevel = PyStackRef_IsNone(frame->f_executable) ? 2 : STACK_LEVEL(); + if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] + // Branch opcodes use the cache for branch history, not + // specialization counters. Don't reset it. + && !IS_CONDITIONAL_JUMP_OPCODE(opcode)) { + (&next_instr[1])->counter = trigger_backoff_counter(); + } + const _PyOpcodeRecordEntry *record_entry = &_PyOpcode_RecordEntries[opcode]; + for (int i = 0; i < record_entry->count; i++) { + _Py_RecordFuncPtr doesnt_escape = _PyOpcode_RecordFunctions[record_entry->indices[i]]; + doesnt_escape(frame, stack_pointer, oparg, &tracer->prev_state.recorded_values[i]); + } + tracer->prev_state.recorded_count = record_entry->count; + DISPATCH_GOTO_NON_TRACING(); + #else + (void)prev_instr; + Py_FatalError("JIT instruction executed in non-jit build."); + #endif + } + + TARGET(UNARY_INVERT) { + #if _Py_TAIL_CALL_INTERP + int opcode = UNARY_INVERT; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(UNARY_INVERT); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + // _UNARY_INVERT + { + value = stack_pointer[-1]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyNumber_Invert(PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + v = value; + } + // _POP_TOP + { + value = v; + stack_pointer[-1] = res; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(UNARY_NEGATIVE) { + #if _Py_TAIL_CALL_INTERP + int opcode = UNARY_NEGATIVE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(UNARY_NEGATIVE); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + // _UNARY_NEGATIVE + { + value = stack_pointer[-1]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyNumber_Negative(PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + v = value; + } + // _POP_TOP + { + value = v; + stack_pointer[-1] = res; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(UNARY_NOT) { + #if _Py_TAIL_CALL_INTERP + int opcode = UNARY_NOT; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(UNARY_NOT); + _PyStackRef value; + _PyStackRef res; + value = stack_pointer[-1]; + assert(PyStackRef_BoolCheck(value)); + res = PyStackRef_IsFalse(value) + ? PyStackRef_True : PyStackRef_False; + stack_pointer[-1] = res; + DISPATCH(); + } + + TARGET(UNPACK_EX) { + #if _Py_TAIL_CALL_INTERP + int opcode = UNPACK_EX; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(UNPACK_EX); + _PyStackRef seq; + _PyStackRef *top; + seq = stack_pointer[-1]; + top = &stack_pointer[(oparg & 0xFF) + (oparg >> 8)]; + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = _PyEval_UnpackIterableStackRef(tstate, seq_o, oparg & 0xFF, oparg >> 8, top); + Py_DECREF(seq_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res == 0) { + JUMP_TO_LABEL(error); + } + stack_pointer += 1 + (oparg & 0xFF) + (oparg >> 8); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(UNPACK_SEQUENCE) { + #if _Py_TAIL_CALL_INTERP + int opcode = UNPACK_SEQUENCE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(UNPACK_SEQUENCE); + PREDICTED_UNPACK_SEQUENCE:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; + _PyStackRef seq; + _PyStackRef *top; + // _SPECIALIZE_UNPACK_SEQUENCE + { + seq = stack_pointer[-1]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_UnpackSequence(seq, next_instr, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(UNPACK_SEQUENCE); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + (void)seq; + (void)counter; + } + // _UNPACK_SEQUENCE + { + top = &stack_pointer[-1 + oparg]; + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = _PyEval_UnpackIterableStackRef(tstate, seq_o, oparg, -1, top); + Py_DECREF(seq_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res == 0) { + JUMP_TO_LABEL(error); + } + } + stack_pointer += oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(UNPACK_SEQUENCE_LIST) { + #if _Py_TAIL_CALL_INTERP + int opcode = UNPACK_SEQUENCE_LIST; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(UNPACK_SEQUENCE_LIST); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); + _PyStackRef tos; + _PyStackRef seq; + _PyStackRef *values; + // _GUARD_TOS_LIST + { + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyList_CheckExact(o)) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } + } + /* Skip 1 cache entry */ + // _UNPACK_SEQUENCE_LIST + { + seq = tos; + values = &stack_pointer[-1]; + PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); + assert(PyList_CheckExact(seq_o)); + if (!LOCK_OBJECT(seq_o)) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } + if (PyList_GET_SIZE(seq_o) != oparg) { + UNLOCK_OBJECT(seq_o); + if (true) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } + } + STAT_INC(UNPACK_SEQUENCE, hit); + PyObject **items = _PyList_ITEMS(seq_o); + for (int i = oparg; --i >= 0; ) { + *values++ = PyStackRef_FromPyObjectNew(items[i]); + } + UNLOCK_OBJECT(seq_o); + stack_pointer += -1 + oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(seq); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(UNPACK_SEQUENCE_TUPLE) { + #if _Py_TAIL_CALL_INTERP + int opcode = UNPACK_SEQUENCE_TUPLE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(UNPACK_SEQUENCE_TUPLE); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); + _PyStackRef tos; + _PyStackRef seq; + _PyStackRef *values; + // _GUARD_TOS_TUPLE + { + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyTuple_CheckExact(o)) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } + } + /* Skip 1 cache entry */ + // _UNPACK_SEQUENCE_TUPLE + { + seq = tos; + values = &stack_pointer[-1]; + PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); + assert(PyTuple_CheckExact(seq_o)); + if (PyTuple_GET_SIZE(seq_o) != oparg) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } + STAT_INC(UNPACK_SEQUENCE, hit); + PyObject **items = _PyTuple_ITEMS(seq_o); + for (int i = oparg; --i >= 0; ) { + *values++ = PyStackRef_FromPyObjectNew(items[i]); + } + stack_pointer += -1 + oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(seq); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { + #if _Py_TAIL_CALL_INTERP + int opcode = UNPACK_SEQUENCE_TWO_TUPLE; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(UNPACK_SEQUENCE_TWO_TUPLE); + static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size"); + _PyStackRef tos; + _PyStackRef seq; + _PyStackRef val1; + _PyStackRef val0; + // _GUARD_TOS_TUPLE + { + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyTuple_CheckExact(o)) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } + } + /* Skip 1 cache entry */ + // _UNPACK_SEQUENCE_TWO_TUPLE + { + seq = tos; + assert(oparg == 2); + PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); + assert(PyTuple_CheckExact(seq_o)); + if (PyTuple_GET_SIZE(seq_o) != 2) { + UPDATE_MISS_STATS(UNPACK_SEQUENCE); + assert(_PyOpcode_Deopt[opcode] == (UNPACK_SEQUENCE)); + JUMP_TO_PREDICTED(UNPACK_SEQUENCE); + } + STAT_INC(UNPACK_SEQUENCE, hit); + val0 = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq_o, 0)); + val1 = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq_o, 1)); + stack_pointer[-1] = val1; + stack_pointer[0] = val0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(seq); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + DISPATCH(); + } + + TARGET(WITH_EXCEPT_START) { + #if _Py_TAIL_CALL_INTERP + int opcode = WITH_EXCEPT_START; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(WITH_EXCEPT_START); + _PyStackRef exit_func; + _PyStackRef exit_self; + _PyStackRef lasti; + _PyStackRef val; + _PyStackRef res; + val = stack_pointer[-1]; + lasti = stack_pointer[-3]; + exit_self = stack_pointer[-4]; + exit_func = stack_pointer[-5]; + PyObject *exc, *tb; + PyObject *val_o = PyStackRef_AsPyObjectBorrow(val); + PyObject *exit_func_o = PyStackRef_AsPyObjectBorrow(exit_func); + assert(val_o && PyExceptionInstance_Check(val_o)); + exc = PyExceptionInstance_Class(val_o); + PyObject *original_tb = tb = PyException_GetTraceback(val_o); + if (tb == NULL) { + tb = Py_None; + } + assert(PyStackRef_IsTaggedInt(lasti)); + (void)lasti; + PyObject* res_o; + { + PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb}; + int has_self = !PyStackRef_IsNull(exit_self); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyObject_Vectorcall(exit_func_o, stack + 2 - has_self, + (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(original_tb); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(YIELD_VALUE) { + #if _Py_TAIL_CALL_INTERP + int opcode = YIELD_VALUE; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(YIELD_VALUE); + opcode = YIELD_VALUE; + _PyStackRef value; + _PyStackRef retval; + // _MAKE_HEAP_SAFE + { + value = stack_pointer[-1]; + value = PyStackRef_MakeHeapSafe(value); + } + // _YIELD_VALUE + { + retval = value; + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + frame->instr_ptr++; + PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); + assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); + assert(oparg == 0 || oparg == 1); + _PyStackRef temp = retval; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + DTRACE_FUNCTION_RETURN(); + tstate->exc_info = gen->gi_exc_state.previous_item; + gen->gi_exc_state.previous_item = NULL; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; + frame = tstate->current_frame = frame->previous; + gen_frame->previous = NULL; + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); + assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + #if TIER_ONE && defined(Py_DEBUG) + if (!PyStackRef_IsNone(frame->f_executable)) { + Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); + assert(i >= 0 && i <= INT_MAX); + int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; + assert(opcode == SEND || opcode == FOR_ITER); + } + #endif + stack_pointer = _PyFrame_GetStackPointer(frame); + LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); + value = temp; + LLTRACE_RESUME_FRAME(); + } + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + /* END INSTRUCTIONS */ +#if !_Py_TAIL_CALL_INTERP +#if USE_COMPUTED_GOTOS + _unknown_opcode: +#else + EXTRA_CASES // From pycore_opcode_metadata.h, a 'case' for each unused opcode +#endif + /* Tell C compilers not to hold the opcode variable in the loop. + next_instr points the current instruction without TARGET(). */ + opcode = next_instr->op.code; + _PyErr_Format(tstate, PyExc_SystemError, + "%U:%d: unknown opcode %d", + _PyFrame_GetCode(frame)->co_filename, + PyUnstable_InterpreterFrame_GetLine(frame), + opcode); +JUMP_TO_LABEL(error); + + + } + + /* This should never be reached. Every opcode should end with DISPATCH() + or goto error. */ + Py_UNREACHABLE(); +#endif /* _Py_TAIL_CALL_INTERP */ + /* BEGIN LABELS */ + + LABEL(pop_2_error) + { + stack_pointer -= 2; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_LABEL(error); + } + + LABEL(pop_1_error) + { + stack_pointer -= 1; + assert(WITHIN_STACK_BOUNDS()); + JUMP_TO_LABEL(error); + } + + LABEL(error) + { + #ifdef NDEBUG + if (!_PyErr_Occurred(tstate)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetString(tstate, PyExc_SystemError, + "error return without exception set"); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + #else + assert(_PyErr_Occurred(tstate)); + #endif + _PyFrame_SetStackPointer(frame, stack_pointer); + STOP_TRACING(); + stack_pointer = _PyFrame_GetStackPointer(frame); + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + if (!_PyFrame_IsIncomplete(frame)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyFrameObject *f = _PyFrame_GetFrameObject(frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (f != NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyTraceBack_Here(f); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_MonitorRaise(tstate, frame, next_instr-1); + JUMP_TO_LABEL(exception_unwind); + } + + LABEL(exception_unwind) + { + STOP_TRACING(); + int offset = INSTR_OFFSET()-1; + int level, handler, lasti; + int handled = get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti); + if (handled == 0) { + assert(_PyErr_Occurred(tstate)); + _PyStackRef *stackbase = _PyFrame_Stackbase(frame); + while (frame->stackpointer > stackbase) { + _PyStackRef ref = _PyFrame_StackPop(frame); + PyStackRef_XCLOSE(ref); + } + monitor_unwind(tstate, frame, next_instr-1); + JUMP_TO_LABEL(exit_unwind); + } + assert(STACK_LEVEL() >= level); + _PyStackRef *new_top = _PyFrame_Stackbase(frame) + level; + assert(frame->stackpointer >= new_top); + while (frame->stackpointer > new_top) { + _PyStackRef ref = _PyFrame_StackPop(frame); + PyStackRef_XCLOSE(ref); + } + if (lasti) { + int frame_lasti = _PyInterpreterFrame_LASTI(frame); + _PyStackRef lasti = PyStackRef_TagInt(frame_lasti); + _PyFrame_StackPush(frame, lasti); + } + PyObject *exc = _PyErr_GetRaisedException(tstate); + _PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(exc)); + next_instr = _PyFrame_GetBytecode(frame) + handler; + int err = monitor_handled(tstate, frame, next_instr, exc); + if (err < 0) { + JUMP_TO_LABEL(exception_unwind); + } + #ifdef Py_DEBUG + if (frame->lltrace >= 5) { + lltrace_resume_frame(frame); + } + #endif + stack_pointer = _PyFrame_GetStackPointer(frame); + #if _Py_TAIL_CALL_INTERP + int opcode; + #endif + DISPATCH(); + } + + LABEL(exit_unwind) + { + assert(_PyErr_Occurred(tstate)); + DTRACE_FUNCTION_RETURN(); + JUMP_TO_LABEL(exit_unwind_notrace); + } + + LABEL(exit_unwind_notrace) + { + assert(_PyErr_Occurred(tstate)); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + frame->return_offset = 0; + if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { + tstate->current_frame = frame->previous; + #if !_Py_TAIL_CALL_INTERP + assert(frame == &entry.frame); + #endif + #ifdef _Py_TIER2 + _PyStackRef executor = frame->localsplus[0]; + assert(tstate->current_executor == NULL); + if (!PyStackRef_IsNull(executor)) { + tstate->current_executor = PyStackRef_AsPyObjectBorrow(executor); + PyStackRef_CLOSE(executor); + } + #endif + return NULL; + } + next_instr = frame->instr_ptr; + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + + LABEL(start_frame) + { + int too_deep = _Py_EnterRecursivePy(tstate); + if (too_deep) { + JUMP_TO_LABEL(exit_unwind_notrace); + } + DTRACE_FUNCTION_ENTRY(); + next_instr = frame->instr_ptr; + #ifdef Py_DEBUG + int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); + if (lltrace < 0) { + JUMP_TO_LABEL(exit_unwind); + } + frame->lltrace = lltrace; + assert(!_PyErr_Occurred(tstate)); + #endif + stack_pointer = _PyFrame_GetStackPointer(frame); + #if _Py_TAIL_CALL_INTERP + int opcode; + #endif + DISPATCH(); + } + + #if _Py_TAIL_CALL_INTERP && !defined(_Py_TIER2) + Py_GCC_ATTRIBUTE((unused)) + #endif + LABEL(stop_tracing) + { + #if _Py_TIER2 + assert(IS_JIT_TRACING()); + int opcode = next_instr->op.code; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, _EXIT_TRACE); + stack_pointer = _PyFrame_GetStackPointer(frame); + LEAVE_TRACING(); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = stop_tracing_and_jit(tstate, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + DISPATCH_GOTO_NON_TRACING(); + #else + Py_FatalError("JIT label executed in non-jit build."); + #endif + } + +/* END LABELS */ +#undef TIER_ONE diff --git a/Modules/_testinternalcapi/test_critical_sections.c b/Modules/_testinternalcapi/test_critical_sections.c index e0ba37abcdd332..72a1fa2cdc7224 100644 --- a/Modules/_testinternalcapi/test_critical_sections.c +++ b/Modules/_testinternalcapi/test_critical_sections.c @@ -4,6 +4,8 @@ #include "parts.h" #include "pycore_critical_section.h" +#include "pycore_pystate.h" +#include "pycore_pythread.h" #ifdef MS_WINDOWS # include <windows.h> // Sleep() @@ -284,13 +286,196 @@ test_critical_sections_gc(PyObject *self, PyObject *Py_UNUSED(args)) #endif +#ifdef Py_GIL_DISABLED + +static PyObject * +test_critical_section1_reacquisition(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyObject *a = PyDict_New(); + assert(a != NULL); + + PyCriticalSection cs1, cs2; + // First acquisition of critical section on object locks it + PyCriticalSection_Begin(&cs1, a); + assert(PyMutex_IsLocked(&a->ob_mutex)); + assert(_PyCriticalSection_IsActive(PyThreadState_GET()->critical_section)); + assert(_PyThreadState_GET()->critical_section == (uintptr_t)&cs1); + // Attempting to re-acquire critical section on same object which + // is already locked by top-most critical section is a no-op. + PyCriticalSection_Begin(&cs2, a); + assert(PyMutex_IsLocked(&a->ob_mutex)); + assert(_PyCriticalSection_IsActive(PyThreadState_GET()->critical_section)); + assert(_PyThreadState_GET()->critical_section == (uintptr_t)&cs1); + // Releasing second critical section is a no-op. + PyCriticalSection_End(&cs2); + assert(PyMutex_IsLocked(&a->ob_mutex)); + assert(_PyCriticalSection_IsActive(PyThreadState_GET()->critical_section)); + assert(_PyThreadState_GET()->critical_section == (uintptr_t)&cs1); + // Releasing first critical section unlocks the object + PyCriticalSection_End(&cs1); + assert(!PyMutex_IsLocked(&a->ob_mutex)); + + Py_DECREF(a); + Py_RETURN_NONE; +} + +static PyObject * +test_critical_section2_reacquisition(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyObject *a = PyDict_New(); + assert(a != NULL); + PyObject *b = PyDict_New(); + assert(b != NULL); + + PyCriticalSection2 cs; + // First acquisition of critical section on objects locks them + PyCriticalSection2_Begin(&cs, a, b); + assert(PyMutex_IsLocked(&a->ob_mutex)); + assert(PyMutex_IsLocked(&b->ob_mutex)); + assert(_PyCriticalSection_IsActive(PyThreadState_GET()->critical_section)); + assert((_PyThreadState_GET()->critical_section & + ~_Py_CRITICAL_SECTION_MASK) == (uintptr_t)&cs); + + // Attempting to re-acquire critical section on either of two + // objects already locked by top-most critical section is a no-op. + + // Check re-acquiring on first object + PyCriticalSection a_cs; + PyCriticalSection_Begin(&a_cs, a); + assert(PyMutex_IsLocked(&a->ob_mutex)); + assert(PyMutex_IsLocked(&b->ob_mutex)); + assert(_PyCriticalSection_IsActive(PyThreadState_GET()->critical_section)); + assert((_PyThreadState_GET()->critical_section & + ~_Py_CRITICAL_SECTION_MASK) == (uintptr_t)&cs); + // Releasing critical section on either object is a no-op. + PyCriticalSection_End(&a_cs); + assert(PyMutex_IsLocked(&a->ob_mutex)); + assert(PyMutex_IsLocked(&b->ob_mutex)); + assert(_PyCriticalSection_IsActive(PyThreadState_GET()->critical_section)); + assert((_PyThreadState_GET()->critical_section & + ~_Py_CRITICAL_SECTION_MASK) == (uintptr_t)&cs); + + // Check re-acquiring on second object + PyCriticalSection b_cs; + PyCriticalSection_Begin(&b_cs, b); + assert(PyMutex_IsLocked(&a->ob_mutex)); + assert(PyMutex_IsLocked(&b->ob_mutex)); + assert(_PyCriticalSection_IsActive(PyThreadState_GET()->critical_section)); + assert((_PyThreadState_GET()->critical_section & + ~_Py_CRITICAL_SECTION_MASK) == (uintptr_t)&cs); + // Releasing critical section on either object is a no-op. + PyCriticalSection_End(&b_cs); + assert(PyMutex_IsLocked(&a->ob_mutex)); + assert(PyMutex_IsLocked(&b->ob_mutex)); + assert(_PyCriticalSection_IsActive(PyThreadState_GET()->critical_section)); + assert((_PyThreadState_GET()->critical_section & + ~_Py_CRITICAL_SECTION_MASK) == (uintptr_t)&cs); + + // Releasing critical section on both objects unlocks them + PyCriticalSection2_End(&cs); + assert(!PyMutex_IsLocked(&a->ob_mutex)); + assert(!PyMutex_IsLocked(&b->ob_mutex)); + + Py_DECREF(a); + Py_DECREF(b); + Py_RETURN_NONE; +} + +#endif // Py_GIL_DISABLED + +#ifdef Py_CAN_START_THREADS + +// gh-144513: Test that critical sections don't deadlock with stop-the-world. +// This test is designed to deadlock (timeout) on builds without the fix. +struct test_data_stw { + PyObject *obj; + Py_ssize_t num_threads; + Py_ssize_t started; + PyEvent ready; +}; + +static void +thread_stw(void *arg) +{ + struct test_data_stw *test_data = arg; + PyGILState_STATE gil = PyGILState_Ensure(); + + if (_Py_atomic_add_ssize(&test_data->started, 1) == test_data->num_threads - 1) { + _PyEvent_Notify(&test_data->ready); + } + + // All threads: acquire critical section and hold it long enough to + // trigger TIME_TO_BE_FAIR_NS (1 ms), which causes direct handoff on unlock. + Py_BEGIN_CRITICAL_SECTION(test_data->obj); + pysleep(10); // 10 ms = 10 x TIME_TO_BE_FAIR_NS + Py_END_CRITICAL_SECTION(); + + PyGILState_Release(gil); +} + +static PyObject * +test_critical_sections_stw(PyObject *self, PyObject *Py_UNUSED(args)) +{ + // gh-144513: Test that critical sections don't deadlock during STW. + // + // The deadlock occurs when lock ownership is handed off (due to fairness + // after TIME_TO_BE_FAIR_NS) to a thread that has already suspended for + // stop-the-world. The STW requester then cannot acquire the lock. + // + // With the fix, the STW requester detects world_stopped and skips locking. + + #define STW_NUM_THREADS 2 + struct test_data_stw test_data = { + .obj = PyDict_New(), + .num_threads = STW_NUM_THREADS, + }; + if (test_data.obj == NULL) { + return NULL; + } + + PyThread_handle_t handles[STW_NUM_THREADS]; + PyThread_ident_t idents[STW_NUM_THREADS]; + for (Py_ssize_t i = 0; i < STW_NUM_THREADS; i++) { + PyThread_start_joinable_thread(&thread_stw, &test_data, + &idents[i], &handles[i]); + } + + // Wait for threads to start, then let them compete for the lock + PyEvent_Wait(&test_data.ready); + pysleep(5); + + // Request stop-the-world and try to acquire the critical section. + // Without the fix, this may deadlock. + PyInterpreterState *interp = PyInterpreterState_Get(); + _PyEval_StopTheWorld(interp); + + Py_BEGIN_CRITICAL_SECTION(test_data.obj); + Py_END_CRITICAL_SECTION(); + + _PyEval_StartTheWorld(interp); + + for (Py_ssize_t i = 0; i < STW_NUM_THREADS; i++) { + PyThread_join_thread(handles[i]); + } + #undef STW_NUM_THREADS + Py_DECREF(test_data.obj); + Py_RETURN_NONE; +} + +#endif // Py_CAN_START_THREADS + static PyMethodDef test_methods[] = { {"test_critical_sections", test_critical_sections, METH_NOARGS}, {"test_critical_sections_nest", test_critical_sections_nest, METH_NOARGS}, {"test_critical_sections_suspend", test_critical_sections_suspend, METH_NOARGS}, +#ifdef Py_GIL_DISABLED + {"test_critical_section1_reacquisition", test_critical_section1_reacquisition, METH_NOARGS}, + {"test_critical_section2_reacquisition", test_critical_section2_reacquisition, METH_NOARGS}, +#endif #ifdef Py_CAN_START_THREADS {"test_critical_sections_threads", test_critical_sections_threads, METH_NOARGS}, {"test_critical_sections_gc", test_critical_sections_gc, METH_NOARGS}, + {"test_critical_sections_stw", test_critical_sections_stw, METH_NOARGS}, #endif {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_testinternalcapi/test_lock.c b/Modules/_testinternalcapi/test_lock.c index 8d8cb992b0e07f..596120ef275196 100644 --- a/Modules/_testinternalcapi/test_lock.c +++ b/Modules/_testinternalcapi/test_lock.c @@ -91,7 +91,8 @@ test_lock_two_threads(PyObject *self, PyObject *obj) } while (v != 3 && iters < 200); // both the "locked" and the "has parked" bits should be set - assert(test_data.m._bits == 3); + v = _Py_atomic_load_uint8_relaxed(&test_data.m._bits); + assert(v == 3); PyMutex_Unlock(&test_data.m); PyEvent_Wait(&test_data.done); @@ -193,65 +194,101 @@ test_lock_counter_slow(PyObject *self, PyObject *obj) Py_RETURN_NONE; } -struct bench_data_locks { - int stop; - int use_pymutex; - int critical_section_length; +struct bench_lock { char padding[200]; - PyThread_type_lock lock; PyMutex m; double value; - Py_ssize_t total_iters; +}; + +struct bench_config { + int stop; + int work_inside; + int work_outside; + int num_acquisitions; + int random_locks; + Py_ssize_t target_iters; + Py_ssize_t num_locks; + struct bench_lock *locks; }; struct bench_thread_data { - struct bench_data_locks *bench_data; + struct bench_config *config; + struct bench_lock *lock; + uint64_t rng_state; Py_ssize_t iters; PyEvent done; }; +static uint64_t +splitmix64(uint64_t *state) +{ + uint64_t z = (*state += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); +} + static void thread_benchmark_locks(void *arg) { - struct bench_thread_data *thread_data = arg; - struct bench_data_locks *bench_data = thread_data->bench_data; - int use_pymutex = bench_data->use_pymutex; - int critical_section_length = bench_data->critical_section_length; - + struct bench_thread_data *td = arg; + struct bench_config *config = td->config; + int work_inside = config->work_inside; + int work_outside = config->work_outside; + int num_acquisitions = config->num_acquisitions; + Py_ssize_t target_iters = config->target_iters; + uint64_t rng_state = td->rng_state; + + double local_value = 0.0; double my_value = 1.0; Py_ssize_t iters = 0; - while (!_Py_atomic_load_int_relaxed(&bench_data->stop)) { - if (use_pymutex) { - PyMutex_Lock(&bench_data->m); - for (int i = 0; i < critical_section_length; i++) { - bench_data->value += my_value; - my_value = bench_data->value; + for (;;) { + if (target_iters > 0) { + if (iters >= target_iters) { + break; } - PyMutex_Unlock(&bench_data->m); } - else { - PyThread_acquire_lock(bench_data->lock, 1); - for (int i = 0; i < critical_section_length; i++) { - bench_data->value += my_value; - my_value = bench_data->value; + else if (_Py_atomic_load_int_relaxed(&config->stop)) { + break; + } + struct bench_lock *lock = td->lock; + if (config->random_locks) { + uint32_t r = (uint32_t)splitmix64(&rng_state); + // Fast modulo reduction to pick a random lock, adapted from: + // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ + Py_ssize_t idx = ((uint64_t)r * (uint32_t)config->num_locks) >> 32; + lock = &config->locks[idx]; + } + for (int acq = 0; acq < num_acquisitions; acq++) { + PyMutex_Lock(&lock->m); + for (int i = 0; i < work_inside; i++) { + lock->value += my_value; + my_value = lock->value; } - PyThread_release_lock(bench_data->lock); + PyMutex_Unlock(&lock->m); } - iters++; + for (int i = 0; i < work_outside; i++) { + local_value += my_value; + my_value = local_value; + } + iters += num_acquisitions; } - thread_data->iters = iters; - _Py_atomic_add_ssize(&bench_data->total_iters, iters); - _PyEvent_Notify(&thread_data->done); + td->iters = iters; + _PyEvent_Notify(&td->done); } /*[clinic input] _testinternalcapi.benchmark_locks num_threads: Py_ssize_t - use_pymutex: bool = True - critical_section_length: int = 1 + work_inside: int = 1 + work_outside: int = 0 time_ms: int = 1000 + num_acquisitions: int = 1 + total_iters: Py_ssize_t = 0 + num_locks: Py_ssize_t = 1 + random_locks: bool = False / [clinic start generated code]*/ @@ -259,10 +296,12 @@ _testinternalcapi.benchmark_locks static PyObject * _testinternalcapi_benchmark_locks_impl(PyObject *module, Py_ssize_t num_threads, - int use_pymutex, - int critical_section_length, - int time_ms) -/*[clinic end generated code: output=381df8d7e9a74f18 input=f3aeaf688738c121]*/ + int work_inside, int work_outside, + int time_ms, int num_acquisitions, + Py_ssize_t total_iters, + Py_ssize_t num_locks, + int random_locks) +/*[clinic end generated code: output=6258dc9de8cb9af1 input=d622cf4e1c4d008b]*/ { // Run from Tools/lockbench/lockbench.py // Based on the WebKit lock benchmarks: @@ -270,24 +309,28 @@ _testinternalcapi_benchmark_locks_impl(PyObject *module, // See also https://webkit.org/blog/6161/locking-in-webkit/ PyObject *thread_iters = NULL; PyObject *res = NULL; + struct bench_thread_data *thread_data = NULL; - struct bench_data_locks bench_data; - memset(&bench_data, 0, sizeof(bench_data)); - bench_data.use_pymutex = use_pymutex; - bench_data.critical_section_length = critical_section_length; - - bench_data.lock = PyThread_allocate_lock(); - if (bench_data.lock == NULL) { - return PyErr_NoMemory(); + struct bench_config config = { + .work_inside = work_inside, + .work_outside = work_outside, + .num_acquisitions = num_acquisitions, + .target_iters = total_iters, + .num_locks = num_locks, + .random_locks = random_locks, + }; + + config.locks = PyMem_Calloc(num_locks, sizeof(*config.locks)); + if (config.locks == NULL) { + PyErr_NoMemory(); + goto exit; } - struct bench_thread_data *thread_data = NULL; thread_data = PyMem_Calloc(num_threads, sizeof(*thread_data)); if (thread_data == NULL) { PyErr_NoMemory(); goto exit; } - thread_iters = PyList_New(num_threads); if (thread_iters == NULL) { goto exit; @@ -299,40 +342,43 @@ _testinternalcapi_benchmark_locks_impl(PyObject *module, } for (Py_ssize_t i = 0; i < num_threads; i++) { - thread_data[i].bench_data = &bench_data; + thread_data[i].config = &config; + thread_data[i].lock = &config.locks[i % num_locks]; + thread_data[i].rng_state = (uint64_t)i + 1; PyThread_start_new_thread(thread_benchmark_locks, &thread_data[i]); } - // Let the threads run for `time_ms` milliseconds - pysleep(time_ms); - _Py_atomic_store_int(&bench_data.stop, 1); + if (total_iters == 0) { + pysleep(time_ms); + _Py_atomic_store_int(&config.stop, 1); + } - // Wait for the threads to finish for (Py_ssize_t i = 0; i < num_threads; i++) { PyEvent_Wait(&thread_data[i].done); } - Py_ssize_t total_iters = bench_data.total_iters; if (PyTime_PerfCounter(&end) < 0) { goto exit; } - // Return the total number of acquisitions and the number of acquisitions - // for each thread. + Py_ssize_t sum_iters = 0; for (Py_ssize_t i = 0; i < num_threads; i++) { PyObject *iter = PyLong_FromSsize_t(thread_data[i].iters); if (iter == NULL) { goto exit; } PyList_SET_ITEM(thread_iters, i, iter); + sum_iters += thread_data[i].iters; } assert(end != start); - double rate = total_iters * 1e9 / (end - start); - res = Py_BuildValue("(dO)", rate, thread_iters); + PyTime_t elapsed_ns = end - start; + double rate = sum_iters * 1e9 / elapsed_ns; + res = Py_BuildValue("(dOL)", rate, thread_iters, + (long long)elapsed_ns); exit: - PyThread_free_lock(bench_data.lock); + PyMem_Free(config.locks); PyMem_Free(thread_data); Py_XDECREF(thread_iters); return res; @@ -343,7 +389,7 @@ test_lock_benchmark(PyObject *module, PyObject *obj) { // Just make sure the benchmark runs without crashing PyObject *res = _testinternalcapi_benchmark_locks_impl( - module, 1, 1, 1, 100); + module, 1, 1, 0, 100, 1, 0, 1, 0); if (res == NULL) { return NULL; } diff --git a/Modules/_testinternalcapi/test_targets.h b/Modules/_testinternalcapi/test_targets.h new file mode 100644 index 00000000000000..1a7eb9169fc837 --- /dev/null +++ b/Modules/_testinternalcapi/test_targets.h @@ -0,0 +1,1295 @@ +#if !_Py_TAIL_CALL_INTERP +static void *opcode_targets_table[256] = { + &&TARGET_CACHE, + &&TARGET_BINARY_SLICE, + &&TARGET_BUILD_TEMPLATE, + &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE, + &&TARGET_CALL_FUNCTION_EX, + &&TARGET_CHECK_EG_MATCH, + &&TARGET_CHECK_EXC_MATCH, + &&TARGET_CLEANUP_THROW, + &&TARGET_DELETE_SUBSCR, + &&TARGET_END_FOR, + &&TARGET_END_SEND, + &&TARGET_EXIT_INIT_CHECK, + &&TARGET_FORMAT_SIMPLE, + &&TARGET_FORMAT_WITH_SPEC, + &&TARGET_GET_AITER, + &&TARGET_GET_ANEXT, + &&TARGET_GET_LEN, + &&TARGET_RESERVED, + &&TARGET_INTERPRETER_EXIT, + &&TARGET_LOAD_BUILD_CLASS, + &&TARGET_LOAD_LOCALS, + &&TARGET_MAKE_FUNCTION, + &&TARGET_MATCH_KEYS, + &&TARGET_MATCH_MAPPING, + &&TARGET_MATCH_SEQUENCE, + &&TARGET_NOP, + &&TARGET_NOT_TAKEN, + &&TARGET_POP_EXCEPT, + &&TARGET_POP_ITER, + &&TARGET_POP_TOP, + &&TARGET_PUSH_EXC_INFO, + &&TARGET_PUSH_NULL, + &&TARGET_RETURN_GENERATOR, + &&TARGET_RETURN_VALUE, + &&TARGET_SETUP_ANNOTATIONS, + &&TARGET_STORE_SLICE, + &&TARGET_STORE_SUBSCR, + &&TARGET_TO_BOOL, + &&TARGET_UNARY_INVERT, + &&TARGET_UNARY_NEGATIVE, + &&TARGET_UNARY_NOT, + &&TARGET_WITH_EXCEPT_START, + &&TARGET_BINARY_OP, + &&TARGET_BUILD_INTERPOLATION, + &&TARGET_BUILD_LIST, + &&TARGET_BUILD_MAP, + &&TARGET_BUILD_SET, + &&TARGET_BUILD_SLICE, + &&TARGET_BUILD_STRING, + &&TARGET_BUILD_TUPLE, + &&TARGET_CALL, + &&TARGET_CALL_INTRINSIC_1, + &&TARGET_CALL_INTRINSIC_2, + &&TARGET_CALL_KW, + &&TARGET_COMPARE_OP, + &&TARGET_CONTAINS_OP, + &&TARGET_CONVERT_VALUE, + &&TARGET_COPY, + &&TARGET_COPY_FREE_VARS, + &&TARGET_DELETE_ATTR, + &&TARGET_DELETE_DEREF, + &&TARGET_DELETE_FAST, + &&TARGET_DELETE_GLOBAL, + &&TARGET_DELETE_NAME, + &&TARGET_DICT_MERGE, + &&TARGET_DICT_UPDATE, + &&TARGET_END_ASYNC_FOR, + &&TARGET_EXTENDED_ARG, + &&TARGET_FOR_ITER, + &&TARGET_GET_AWAITABLE, + &&TARGET_GET_ITER, + &&TARGET_IMPORT_FROM, + &&TARGET_IMPORT_NAME, + &&TARGET_IS_OP, + &&TARGET_JUMP_BACKWARD, + &&TARGET_JUMP_BACKWARD_NO_INTERRUPT, + &&TARGET_JUMP_FORWARD, + &&TARGET_LIST_APPEND, + &&TARGET_LIST_EXTEND, + &&TARGET_LOAD_ATTR, + &&TARGET_LOAD_COMMON_CONSTANT, + &&TARGET_LOAD_CONST, + &&TARGET_LOAD_DEREF, + &&TARGET_LOAD_FAST, + &&TARGET_LOAD_FAST_AND_CLEAR, + &&TARGET_LOAD_FAST_BORROW, + &&TARGET_LOAD_FAST_BORROW_LOAD_FAST_BORROW, + &&TARGET_LOAD_FAST_CHECK, + &&TARGET_LOAD_FAST_LOAD_FAST, + &&TARGET_LOAD_FROM_DICT_OR_DEREF, + &&TARGET_LOAD_FROM_DICT_OR_GLOBALS, + &&TARGET_LOAD_GLOBAL, + &&TARGET_LOAD_NAME, + &&TARGET_LOAD_SMALL_INT, + &&TARGET_LOAD_SPECIAL, + &&TARGET_LOAD_SUPER_ATTR, + &&TARGET_MAKE_CELL, + &&TARGET_MAP_ADD, + &&TARGET_MATCH_CLASS, + &&TARGET_POP_JUMP_IF_FALSE, + &&TARGET_POP_JUMP_IF_NONE, + &&TARGET_POP_JUMP_IF_NOT_NONE, + &&TARGET_POP_JUMP_IF_TRUE, + &&TARGET_RAISE_VARARGS, + &&TARGET_RERAISE, + &&TARGET_SEND, + &&TARGET_SET_ADD, + &&TARGET_SET_FUNCTION_ATTRIBUTE, + &&TARGET_SET_UPDATE, + &&TARGET_STORE_ATTR, + &&TARGET_STORE_DEREF, + &&TARGET_STORE_FAST, + &&TARGET_STORE_FAST_LOAD_FAST, + &&TARGET_STORE_FAST_STORE_FAST, + &&TARGET_STORE_GLOBAL, + &&TARGET_STORE_NAME, + &&TARGET_SWAP, + &&TARGET_UNPACK_EX, + &&TARGET_UNPACK_SEQUENCE, + &&TARGET_YIELD_VALUE, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&TARGET_RESUME, + &&TARGET_BINARY_OP_ADD_FLOAT, + &&TARGET_BINARY_OP_ADD_INT, + &&TARGET_BINARY_OP_ADD_UNICODE, + &&TARGET_BINARY_OP_EXTEND, + &&TARGET_BINARY_OP_MULTIPLY_FLOAT, + &&TARGET_BINARY_OP_MULTIPLY_INT, + &&TARGET_BINARY_OP_SUBSCR_DICT, + &&TARGET_BINARY_OP_SUBSCR_GETITEM, + &&TARGET_BINARY_OP_SUBSCR_LIST_INT, + &&TARGET_BINARY_OP_SUBSCR_LIST_SLICE, + &&TARGET_BINARY_OP_SUBSCR_STR_INT, + &&TARGET_BINARY_OP_SUBSCR_TUPLE_INT, + &&TARGET_BINARY_OP_SUBSCR_USTR_INT, + &&TARGET_BINARY_OP_SUBTRACT_FLOAT, + &&TARGET_BINARY_OP_SUBTRACT_INT, + &&TARGET_CALL_ALLOC_AND_ENTER_INIT, + &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS, + &&TARGET_CALL_BOUND_METHOD_GENERAL, + &&TARGET_CALL_BUILTIN_CLASS, + &&TARGET_CALL_BUILTIN_FAST, + &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS, + &&TARGET_CALL_BUILTIN_O, + &&TARGET_CALL_EX_NON_PY_GENERAL, + &&TARGET_CALL_EX_PY, + &&TARGET_CALL_ISINSTANCE, + &&TARGET_CALL_KW_BOUND_METHOD, + &&TARGET_CALL_KW_NON_PY, + &&TARGET_CALL_KW_PY, + &&TARGET_CALL_LEN, + &&TARGET_CALL_LIST_APPEND, + &&TARGET_CALL_METHOD_DESCRIPTOR_FAST, + &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, + &&TARGET_CALL_METHOD_DESCRIPTOR_NOARGS, + &&TARGET_CALL_METHOD_DESCRIPTOR_O, + &&TARGET_CALL_NON_PY_GENERAL, + &&TARGET_CALL_PY_EXACT_ARGS, + &&TARGET_CALL_PY_GENERAL, + &&TARGET_CALL_STR_1, + &&TARGET_CALL_TUPLE_1, + &&TARGET_CALL_TYPE_1, + &&TARGET_COMPARE_OP_FLOAT, + &&TARGET_COMPARE_OP_INT, + &&TARGET_COMPARE_OP_STR, + &&TARGET_CONTAINS_OP_DICT, + &&TARGET_CONTAINS_OP_SET, + &&TARGET_FOR_ITER_GEN, + &&TARGET_FOR_ITER_LIST, + &&TARGET_FOR_ITER_RANGE, + &&TARGET_FOR_ITER_TUPLE, + &&TARGET_FOR_ITER_VIRTUAL, + &&TARGET_GET_ITER_SELF, + &&TARGET_GET_ITER_VIRTUAL, + &&TARGET_JUMP_BACKWARD_JIT, + &&TARGET_JUMP_BACKWARD_NO_JIT, + &&TARGET_LOAD_ATTR_CLASS, + &&TARGET_LOAD_ATTR_CLASS_WITH_METACLASS_CHECK, + &&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, + &&TARGET_LOAD_ATTR_INSTANCE_VALUE, + &&TARGET_LOAD_ATTR_METHOD_LAZY_DICT, + &&TARGET_LOAD_ATTR_METHOD_NO_DICT, + &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, + &&TARGET_LOAD_ATTR_MODULE, + &&TARGET_LOAD_ATTR_NONDESCRIPTOR_NO_DICT, + &&TARGET_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, + &&TARGET_LOAD_ATTR_PROPERTY, + &&TARGET_LOAD_ATTR_SLOT, + &&TARGET_LOAD_ATTR_WITH_HINT, + &&TARGET_LOAD_GLOBAL_BUILTIN, + &&TARGET_LOAD_GLOBAL_MODULE, + &&TARGET_LOAD_SUPER_ATTR_ATTR, + &&TARGET_LOAD_SUPER_ATTR_METHOD, + &&TARGET_RESUME_CHECK, + &&TARGET_RESUME_CHECK_JIT, + &&TARGET_SEND_ASYNC_GEN, + &&TARGET_SEND_GEN, + &&TARGET_SEND_VIRTUAL, + &&TARGET_STORE_ATTR_INSTANCE_VALUE, + &&TARGET_STORE_ATTR_SLOT, + &&TARGET_STORE_ATTR_WITH_HINT, + &&TARGET_STORE_SUBSCR_DICT, + &&TARGET_STORE_SUBSCR_LIST_INT, + &&TARGET_TO_BOOL_ALWAYS_TRUE, + &&TARGET_TO_BOOL_BOOL, + &&TARGET_TO_BOOL_INT, + &&TARGET_TO_BOOL_LIST, + &&TARGET_TO_BOOL_NONE, + &&TARGET_TO_BOOL_STR, + &&TARGET_UNPACK_SEQUENCE_LIST, + &&TARGET_UNPACK_SEQUENCE_TUPLE, + &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&TARGET_INSTRUMENTED_END_FOR, + &&TARGET_INSTRUMENTED_POP_ITER, + &&TARGET_INSTRUMENTED_END_SEND, + &&TARGET_INSTRUMENTED_FOR_ITER, + &&TARGET_INSTRUMENTED_INSTRUCTION, + &&TARGET_INSTRUMENTED_JUMP_FORWARD, + &&TARGET_INSTRUMENTED_NOT_TAKEN, + &&TARGET_INSTRUMENTED_POP_JUMP_IF_TRUE, + &&TARGET_INSTRUMENTED_POP_JUMP_IF_FALSE, + &&TARGET_INSTRUMENTED_POP_JUMP_IF_NONE, + &&TARGET_INSTRUMENTED_POP_JUMP_IF_NOT_NONE, + &&TARGET_INSTRUMENTED_RESUME, + &&TARGET_INSTRUMENTED_RETURN_VALUE, + &&TARGET_INSTRUMENTED_YIELD_VALUE, + &&TARGET_INSTRUMENTED_END_ASYNC_FOR, + &&TARGET_INSTRUMENTED_LOAD_SUPER_ATTR, + &&TARGET_INSTRUMENTED_CALL, + &&TARGET_INSTRUMENTED_CALL_KW, + &&TARGET_INSTRUMENTED_CALL_FUNCTION_EX, + &&TARGET_INSTRUMENTED_JUMP_BACKWARD, + &&TARGET_INSTRUMENTED_LINE, + &&TARGET_ENTER_EXECUTOR, + &&TARGET_TRACE_RECORD, +}; +#if _Py_TIER2 +static void *opcode_tracing_targets_table[256] = { + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, +}; +#endif +#else /* _Py_TAIL_CALL_INTERP */ +static py_tail_call_funcptr instruction_funcptr_handler_table[256]; + +static py_tail_call_funcptr instruction_funcptr_tracing_table[256]; + +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_pop_2_error(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_pop_1_error(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_error(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_exception_unwind(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_exit_unwind(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_exit_unwind_notrace(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_start_frame(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_stop_tracing(TAIL_CALL_PARAMS); + +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_ADD_FLOAT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_ADD_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_ADD_UNICODE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_EXTEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_INPLACE_ADD_UNICODE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_MULTIPLY_FLOAT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_MULTIPLY_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_GETITEM(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_LIST_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_LIST_SLICE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_STR_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_TUPLE_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_USTR_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBTRACT_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_SLICE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_INTERPOLATION(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_LIST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_MAP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_SET(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_SLICE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_STRING(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_TEMPLATE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_TUPLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CACHE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_ALLOC_AND_ENTER_INIT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BOUND_METHOD_EXACT_ARGS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BOUND_METHOD_GENERAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BUILTIN_CLASS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BUILTIN_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BUILTIN_FAST_WITH_KEYWORDS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BUILTIN_O(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_EX_NON_PY_GENERAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_EX_PY(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_FUNCTION_EX(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_INTRINSIC_1(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_INTRINSIC_2(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_ISINSTANCE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_KW(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_KW_BOUND_METHOD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_KW_NON_PY(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_KW_PY(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_LEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_LIST_APPEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_METHOD_DESCRIPTOR_NOARGS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_METHOD_DESCRIPTOR_O(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_NON_PY_GENERAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_PY_EXACT_ARGS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_PY_GENERAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_STR_1(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_TUPLE_1(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_TYPE_1(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CHECK_EG_MATCH(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CHECK_EXC_MATCH(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CLEANUP_THROW(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COMPARE_OP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COMPARE_OP_FLOAT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COMPARE_OP_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COMPARE_OP_STR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CONTAINS_OP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CONTAINS_OP_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CONTAINS_OP_SET(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CONVERT_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COPY(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COPY_FREE_VARS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_DEREF(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_GLOBAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_NAME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_SUBSCR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DICT_MERGE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DICT_UPDATE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_END_ASYNC_FOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_END_FOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_END_SEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_ENTER_EXECUTOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_EXIT_INIT_CHECK(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_EXTENDED_ARG(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FORMAT_SIMPLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FORMAT_WITH_SPEC(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER_GEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER_LIST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER_RANGE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER_TUPLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER_VIRTUAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_AITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_ANEXT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_AWAITABLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_ITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_ITER_SELF(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_ITER_VIRTUAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_LEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_IMPORT_FROM(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_IMPORT_NAME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_CALL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_CALL_FUNCTION_EX(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_CALL_KW(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_END_ASYNC_FOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_END_FOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_END_SEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_FOR_ITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_INSTRUCTION(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_JUMP_BACKWARD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_JUMP_FORWARD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_LINE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_LOAD_SUPER_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_NOT_TAKEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_POP_ITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_FALSE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NONE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NOT_NONE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_TRUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_RESUME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_RETURN_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_YIELD_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INTERPRETER_EXIT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_IS_OP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_JUMP_BACKWARD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_JUMP_BACKWARD_JIT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_JUMP_BACKWARD_NO_INTERRUPT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_JUMP_BACKWARD_NO_JIT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_JUMP_FORWARD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LIST_APPEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LIST_EXTEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_CLASS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_CLASS_WITH_METACLASS_CHECK(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_INSTANCE_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_METHOD_LAZY_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_METHOD_NO_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_METHOD_WITH_VALUES(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_MODULE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_NO_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_PROPERTY(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_SLOT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_WITH_HINT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_BUILD_CLASS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_COMMON_CONSTANT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_CONST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_DEREF(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST_AND_CLEAR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST_BORROW(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST_BORROW_LOAD_FAST_BORROW(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST_CHECK(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST_LOAD_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FROM_DICT_OR_DEREF(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FROM_DICT_OR_GLOBALS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_GLOBAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_GLOBAL_BUILTIN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_GLOBAL_MODULE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_LOCALS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_NAME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_SMALL_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_SPECIAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_SUPER_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_SUPER_ATTR_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_SUPER_ATTR_METHOD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MAKE_CELL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MAKE_FUNCTION(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MAP_ADD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MATCH_CLASS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MATCH_KEYS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MATCH_MAPPING(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MATCH_SEQUENCE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_NOP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_NOT_TAKEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_EXCEPT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_ITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_JUMP_IF_FALSE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_JUMP_IF_NONE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_JUMP_IF_NOT_NONE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_JUMP_IF_TRUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_TOP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_PUSH_EXC_INFO(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_PUSH_NULL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RAISE_VARARGS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RERAISE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RESERVED(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RESUME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RESUME_CHECK(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RESUME_CHECK_JIT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RETURN_GENERATOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RETURN_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SEND_ASYNC_GEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SEND_GEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SEND_VIRTUAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SETUP_ANNOTATIONS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SET_ADD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SET_FUNCTION_ATTRIBUTE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SET_UPDATE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_ATTR_INSTANCE_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_ATTR_SLOT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_ATTR_WITH_HINT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_DEREF(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_FAST_LOAD_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_FAST_STORE_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_GLOBAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_NAME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_SLICE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_SUBSCR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_SUBSCR_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_SUBSCR_LIST_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SWAP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_ALWAYS_TRUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_BOOL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_LIST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_NONE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_STR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TRACE_RECORD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNARY_INVERT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNARY_NEGATIVE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNARY_NOT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNPACK_EX(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNPACK_SEQUENCE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNPACK_SEQUENCE_LIST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNPACK_SEQUENCE_TUPLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNPACK_SEQUENCE_TWO_TUPLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_WITH_EXCEPT_START(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_YIELD_VALUE(TAIL_CALL_PARAMS); + +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNKNOWN_OPCODE(TAIL_CALL_PARAMS) { + int opcode = next_instr->op.code; + _PyErr_Format(tstate, PyExc_SystemError, + "%U:%d: unknown opcode %d", + _PyFrame_GetCode(frame)->co_filename, + PyUnstable_InterpreterFrame_GetLine(frame), + opcode); +JUMP_TO_LABEL(error); +} + +static py_tail_call_funcptr instruction_funcptr_handler_table[256] = { + [BINARY_OP] = _TAIL_CALL_BINARY_OP, + [BINARY_OP_ADD_FLOAT] = _TAIL_CALL_BINARY_OP_ADD_FLOAT, + [BINARY_OP_ADD_INT] = _TAIL_CALL_BINARY_OP_ADD_INT, + [BINARY_OP_ADD_UNICODE] = _TAIL_CALL_BINARY_OP_ADD_UNICODE, + [BINARY_OP_EXTEND] = _TAIL_CALL_BINARY_OP_EXTEND, + [BINARY_OP_INPLACE_ADD_UNICODE] = _TAIL_CALL_BINARY_OP_INPLACE_ADD_UNICODE, + [BINARY_OP_MULTIPLY_FLOAT] = _TAIL_CALL_BINARY_OP_MULTIPLY_FLOAT, + [BINARY_OP_MULTIPLY_INT] = _TAIL_CALL_BINARY_OP_MULTIPLY_INT, + [BINARY_OP_SUBSCR_DICT] = _TAIL_CALL_BINARY_OP_SUBSCR_DICT, + [BINARY_OP_SUBSCR_GETITEM] = _TAIL_CALL_BINARY_OP_SUBSCR_GETITEM, + [BINARY_OP_SUBSCR_LIST_INT] = _TAIL_CALL_BINARY_OP_SUBSCR_LIST_INT, + [BINARY_OP_SUBSCR_LIST_SLICE] = _TAIL_CALL_BINARY_OP_SUBSCR_LIST_SLICE, + [BINARY_OP_SUBSCR_STR_INT] = _TAIL_CALL_BINARY_OP_SUBSCR_STR_INT, + [BINARY_OP_SUBSCR_TUPLE_INT] = _TAIL_CALL_BINARY_OP_SUBSCR_TUPLE_INT, + [BINARY_OP_SUBSCR_USTR_INT] = _TAIL_CALL_BINARY_OP_SUBSCR_USTR_INT, + [BINARY_OP_SUBTRACT_FLOAT] = _TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT, + [BINARY_OP_SUBTRACT_INT] = _TAIL_CALL_BINARY_OP_SUBTRACT_INT, + [BINARY_SLICE] = _TAIL_CALL_BINARY_SLICE, + [BUILD_INTERPOLATION] = _TAIL_CALL_BUILD_INTERPOLATION, + [BUILD_LIST] = _TAIL_CALL_BUILD_LIST, + [BUILD_MAP] = _TAIL_CALL_BUILD_MAP, + [BUILD_SET] = _TAIL_CALL_BUILD_SET, + [BUILD_SLICE] = _TAIL_CALL_BUILD_SLICE, + [BUILD_STRING] = _TAIL_CALL_BUILD_STRING, + [BUILD_TEMPLATE] = _TAIL_CALL_BUILD_TEMPLATE, + [BUILD_TUPLE] = _TAIL_CALL_BUILD_TUPLE, + [CACHE] = _TAIL_CALL_CACHE, + [CALL] = _TAIL_CALL_CALL, + [CALL_ALLOC_AND_ENTER_INIT] = _TAIL_CALL_CALL_ALLOC_AND_ENTER_INIT, + [CALL_BOUND_METHOD_EXACT_ARGS] = _TAIL_CALL_CALL_BOUND_METHOD_EXACT_ARGS, + [CALL_BOUND_METHOD_GENERAL] = _TAIL_CALL_CALL_BOUND_METHOD_GENERAL, + [CALL_BUILTIN_CLASS] = _TAIL_CALL_CALL_BUILTIN_CLASS, + [CALL_BUILTIN_FAST] = _TAIL_CALL_CALL_BUILTIN_FAST, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = _TAIL_CALL_CALL_BUILTIN_FAST_WITH_KEYWORDS, + [CALL_BUILTIN_O] = _TAIL_CALL_CALL_BUILTIN_O, + [CALL_EX_NON_PY_GENERAL] = _TAIL_CALL_CALL_EX_NON_PY_GENERAL, + [CALL_EX_PY] = _TAIL_CALL_CALL_EX_PY, + [CALL_FUNCTION_EX] = _TAIL_CALL_CALL_FUNCTION_EX, + [CALL_INTRINSIC_1] = _TAIL_CALL_CALL_INTRINSIC_1, + [CALL_INTRINSIC_2] = _TAIL_CALL_CALL_INTRINSIC_2, + [CALL_ISINSTANCE] = _TAIL_CALL_CALL_ISINSTANCE, + [CALL_KW] = _TAIL_CALL_CALL_KW, + [CALL_KW_BOUND_METHOD] = _TAIL_CALL_CALL_KW_BOUND_METHOD, + [CALL_KW_NON_PY] = _TAIL_CALL_CALL_KW_NON_PY, + [CALL_KW_PY] = _TAIL_CALL_CALL_KW_PY, + [CALL_LEN] = _TAIL_CALL_CALL_LEN, + [CALL_LIST_APPEND] = _TAIL_CALL_CALL_LIST_APPEND, + [CALL_METHOD_DESCRIPTOR_FAST] = _TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = _TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, + [CALL_METHOD_DESCRIPTOR_NOARGS] = _TAIL_CALL_CALL_METHOD_DESCRIPTOR_NOARGS, + [CALL_METHOD_DESCRIPTOR_O] = _TAIL_CALL_CALL_METHOD_DESCRIPTOR_O, + [CALL_NON_PY_GENERAL] = _TAIL_CALL_CALL_NON_PY_GENERAL, + [CALL_PY_EXACT_ARGS] = _TAIL_CALL_CALL_PY_EXACT_ARGS, + [CALL_PY_GENERAL] = _TAIL_CALL_CALL_PY_GENERAL, + [CALL_STR_1] = _TAIL_CALL_CALL_STR_1, + [CALL_TUPLE_1] = _TAIL_CALL_CALL_TUPLE_1, + [CALL_TYPE_1] = _TAIL_CALL_CALL_TYPE_1, + [CHECK_EG_MATCH] = _TAIL_CALL_CHECK_EG_MATCH, + [CHECK_EXC_MATCH] = _TAIL_CALL_CHECK_EXC_MATCH, + [CLEANUP_THROW] = _TAIL_CALL_CLEANUP_THROW, + [COMPARE_OP] = _TAIL_CALL_COMPARE_OP, + [COMPARE_OP_FLOAT] = _TAIL_CALL_COMPARE_OP_FLOAT, + [COMPARE_OP_INT] = _TAIL_CALL_COMPARE_OP_INT, + [COMPARE_OP_STR] = _TAIL_CALL_COMPARE_OP_STR, + [CONTAINS_OP] = _TAIL_CALL_CONTAINS_OP, + [CONTAINS_OP_DICT] = _TAIL_CALL_CONTAINS_OP_DICT, + [CONTAINS_OP_SET] = _TAIL_CALL_CONTAINS_OP_SET, + [CONVERT_VALUE] = _TAIL_CALL_CONVERT_VALUE, + [COPY] = _TAIL_CALL_COPY, + [COPY_FREE_VARS] = _TAIL_CALL_COPY_FREE_VARS, + [DELETE_ATTR] = _TAIL_CALL_DELETE_ATTR, + [DELETE_DEREF] = _TAIL_CALL_DELETE_DEREF, + [DELETE_FAST] = _TAIL_CALL_DELETE_FAST, + [DELETE_GLOBAL] = _TAIL_CALL_DELETE_GLOBAL, + [DELETE_NAME] = _TAIL_CALL_DELETE_NAME, + [DELETE_SUBSCR] = _TAIL_CALL_DELETE_SUBSCR, + [DICT_MERGE] = _TAIL_CALL_DICT_MERGE, + [DICT_UPDATE] = _TAIL_CALL_DICT_UPDATE, + [END_ASYNC_FOR] = _TAIL_CALL_END_ASYNC_FOR, + [END_FOR] = _TAIL_CALL_END_FOR, + [END_SEND] = _TAIL_CALL_END_SEND, + [ENTER_EXECUTOR] = _TAIL_CALL_ENTER_EXECUTOR, + [EXIT_INIT_CHECK] = _TAIL_CALL_EXIT_INIT_CHECK, + [EXTENDED_ARG] = _TAIL_CALL_EXTENDED_ARG, + [FORMAT_SIMPLE] = _TAIL_CALL_FORMAT_SIMPLE, + [FORMAT_WITH_SPEC] = _TAIL_CALL_FORMAT_WITH_SPEC, + [FOR_ITER] = _TAIL_CALL_FOR_ITER, + [FOR_ITER_GEN] = _TAIL_CALL_FOR_ITER_GEN, + [FOR_ITER_LIST] = _TAIL_CALL_FOR_ITER_LIST, + [FOR_ITER_RANGE] = _TAIL_CALL_FOR_ITER_RANGE, + [FOR_ITER_TUPLE] = _TAIL_CALL_FOR_ITER_TUPLE, + [FOR_ITER_VIRTUAL] = _TAIL_CALL_FOR_ITER_VIRTUAL, + [GET_AITER] = _TAIL_CALL_GET_AITER, + [GET_ANEXT] = _TAIL_CALL_GET_ANEXT, + [GET_AWAITABLE] = _TAIL_CALL_GET_AWAITABLE, + [GET_ITER] = _TAIL_CALL_GET_ITER, + [GET_ITER_SELF] = _TAIL_CALL_GET_ITER_SELF, + [GET_ITER_VIRTUAL] = _TAIL_CALL_GET_ITER_VIRTUAL, + [GET_LEN] = _TAIL_CALL_GET_LEN, + [IMPORT_FROM] = _TAIL_CALL_IMPORT_FROM, + [IMPORT_NAME] = _TAIL_CALL_IMPORT_NAME, + [INSTRUMENTED_CALL] = _TAIL_CALL_INSTRUMENTED_CALL, + [INSTRUMENTED_CALL_FUNCTION_EX] = _TAIL_CALL_INSTRUMENTED_CALL_FUNCTION_EX, + [INSTRUMENTED_CALL_KW] = _TAIL_CALL_INSTRUMENTED_CALL_KW, + [INSTRUMENTED_END_ASYNC_FOR] = _TAIL_CALL_INSTRUMENTED_END_ASYNC_FOR, + [INSTRUMENTED_END_FOR] = _TAIL_CALL_INSTRUMENTED_END_FOR, + [INSTRUMENTED_END_SEND] = _TAIL_CALL_INSTRUMENTED_END_SEND, + [INSTRUMENTED_FOR_ITER] = _TAIL_CALL_INSTRUMENTED_FOR_ITER, + [INSTRUMENTED_INSTRUCTION] = _TAIL_CALL_INSTRUMENTED_INSTRUCTION, + [INSTRUMENTED_JUMP_BACKWARD] = _TAIL_CALL_INSTRUMENTED_JUMP_BACKWARD, + [INSTRUMENTED_JUMP_FORWARD] = _TAIL_CALL_INSTRUMENTED_JUMP_FORWARD, + [INSTRUMENTED_LINE] = _TAIL_CALL_INSTRUMENTED_LINE, + [INSTRUMENTED_LOAD_SUPER_ATTR] = _TAIL_CALL_INSTRUMENTED_LOAD_SUPER_ATTR, + [INSTRUMENTED_NOT_TAKEN] = _TAIL_CALL_INSTRUMENTED_NOT_TAKEN, + [INSTRUMENTED_POP_ITER] = _TAIL_CALL_INSTRUMENTED_POP_ITER, + [INSTRUMENTED_POP_JUMP_IF_FALSE] = _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_FALSE, + [INSTRUMENTED_POP_JUMP_IF_NONE] = _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NONE, + [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NOT_NONE, + [INSTRUMENTED_POP_JUMP_IF_TRUE] = _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_TRUE, + [INSTRUMENTED_RESUME] = _TAIL_CALL_INSTRUMENTED_RESUME, + [INSTRUMENTED_RETURN_VALUE] = _TAIL_CALL_INSTRUMENTED_RETURN_VALUE, + [INSTRUMENTED_YIELD_VALUE] = _TAIL_CALL_INSTRUMENTED_YIELD_VALUE, + [INTERPRETER_EXIT] = _TAIL_CALL_INTERPRETER_EXIT, + [IS_OP] = _TAIL_CALL_IS_OP, + [JUMP_BACKWARD] = _TAIL_CALL_JUMP_BACKWARD, + [JUMP_BACKWARD_JIT] = _TAIL_CALL_JUMP_BACKWARD_JIT, + [JUMP_BACKWARD_NO_INTERRUPT] = _TAIL_CALL_JUMP_BACKWARD_NO_INTERRUPT, + [JUMP_BACKWARD_NO_JIT] = _TAIL_CALL_JUMP_BACKWARD_NO_JIT, + [JUMP_FORWARD] = _TAIL_CALL_JUMP_FORWARD, + [LIST_APPEND] = _TAIL_CALL_LIST_APPEND, + [LIST_EXTEND] = _TAIL_CALL_LIST_EXTEND, + [LOAD_ATTR] = _TAIL_CALL_LOAD_ATTR, + [LOAD_ATTR_CLASS] = _TAIL_CALL_LOAD_ATTR_CLASS, + [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = _TAIL_CALL_LOAD_ATTR_CLASS_WITH_METACLASS_CHECK, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = _TAIL_CALL_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, + [LOAD_ATTR_INSTANCE_VALUE] = _TAIL_CALL_LOAD_ATTR_INSTANCE_VALUE, + [LOAD_ATTR_METHOD_LAZY_DICT] = _TAIL_CALL_LOAD_ATTR_METHOD_LAZY_DICT, + [LOAD_ATTR_METHOD_NO_DICT] = _TAIL_CALL_LOAD_ATTR_METHOD_NO_DICT, + [LOAD_ATTR_METHOD_WITH_VALUES] = _TAIL_CALL_LOAD_ATTR_METHOD_WITH_VALUES, + [LOAD_ATTR_MODULE] = _TAIL_CALL_LOAD_ATTR_MODULE, + [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = _TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_NO_DICT, + [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = _TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, + [LOAD_ATTR_PROPERTY] = _TAIL_CALL_LOAD_ATTR_PROPERTY, + [LOAD_ATTR_SLOT] = _TAIL_CALL_LOAD_ATTR_SLOT, + [LOAD_ATTR_WITH_HINT] = _TAIL_CALL_LOAD_ATTR_WITH_HINT, + [LOAD_BUILD_CLASS] = _TAIL_CALL_LOAD_BUILD_CLASS, + [LOAD_COMMON_CONSTANT] = _TAIL_CALL_LOAD_COMMON_CONSTANT, + [LOAD_CONST] = _TAIL_CALL_LOAD_CONST, + [LOAD_DEREF] = _TAIL_CALL_LOAD_DEREF, + [LOAD_FAST] = _TAIL_CALL_LOAD_FAST, + [LOAD_FAST_AND_CLEAR] = _TAIL_CALL_LOAD_FAST_AND_CLEAR, + [LOAD_FAST_BORROW] = _TAIL_CALL_LOAD_FAST_BORROW, + [LOAD_FAST_BORROW_LOAD_FAST_BORROW] = _TAIL_CALL_LOAD_FAST_BORROW_LOAD_FAST_BORROW, + [LOAD_FAST_CHECK] = _TAIL_CALL_LOAD_FAST_CHECK, + [LOAD_FAST_LOAD_FAST] = _TAIL_CALL_LOAD_FAST_LOAD_FAST, + [LOAD_FROM_DICT_OR_DEREF] = _TAIL_CALL_LOAD_FROM_DICT_OR_DEREF, + [LOAD_FROM_DICT_OR_GLOBALS] = _TAIL_CALL_LOAD_FROM_DICT_OR_GLOBALS, + [LOAD_GLOBAL] = _TAIL_CALL_LOAD_GLOBAL, + [LOAD_GLOBAL_BUILTIN] = _TAIL_CALL_LOAD_GLOBAL_BUILTIN, + [LOAD_GLOBAL_MODULE] = _TAIL_CALL_LOAD_GLOBAL_MODULE, + [LOAD_LOCALS] = _TAIL_CALL_LOAD_LOCALS, + [LOAD_NAME] = _TAIL_CALL_LOAD_NAME, + [LOAD_SMALL_INT] = _TAIL_CALL_LOAD_SMALL_INT, + [LOAD_SPECIAL] = _TAIL_CALL_LOAD_SPECIAL, + [LOAD_SUPER_ATTR] = _TAIL_CALL_LOAD_SUPER_ATTR, + [LOAD_SUPER_ATTR_ATTR] = _TAIL_CALL_LOAD_SUPER_ATTR_ATTR, + [LOAD_SUPER_ATTR_METHOD] = _TAIL_CALL_LOAD_SUPER_ATTR_METHOD, + [MAKE_CELL] = _TAIL_CALL_MAKE_CELL, + [MAKE_FUNCTION] = _TAIL_CALL_MAKE_FUNCTION, + [MAP_ADD] = _TAIL_CALL_MAP_ADD, + [MATCH_CLASS] = _TAIL_CALL_MATCH_CLASS, + [MATCH_KEYS] = _TAIL_CALL_MATCH_KEYS, + [MATCH_MAPPING] = _TAIL_CALL_MATCH_MAPPING, + [MATCH_SEQUENCE] = _TAIL_CALL_MATCH_SEQUENCE, + [NOP] = _TAIL_CALL_NOP, + [NOT_TAKEN] = _TAIL_CALL_NOT_TAKEN, + [POP_EXCEPT] = _TAIL_CALL_POP_EXCEPT, + [POP_ITER] = _TAIL_CALL_POP_ITER, + [POP_JUMP_IF_FALSE] = _TAIL_CALL_POP_JUMP_IF_FALSE, + [POP_JUMP_IF_NONE] = _TAIL_CALL_POP_JUMP_IF_NONE, + [POP_JUMP_IF_NOT_NONE] = _TAIL_CALL_POP_JUMP_IF_NOT_NONE, + [POP_JUMP_IF_TRUE] = _TAIL_CALL_POP_JUMP_IF_TRUE, + [POP_TOP] = _TAIL_CALL_POP_TOP, + [PUSH_EXC_INFO] = _TAIL_CALL_PUSH_EXC_INFO, + [PUSH_NULL] = _TAIL_CALL_PUSH_NULL, + [RAISE_VARARGS] = _TAIL_CALL_RAISE_VARARGS, + [RERAISE] = _TAIL_CALL_RERAISE, + [RESERVED] = _TAIL_CALL_RESERVED, + [RESUME] = _TAIL_CALL_RESUME, + [RESUME_CHECK] = _TAIL_CALL_RESUME_CHECK, + [RESUME_CHECK_JIT] = _TAIL_CALL_RESUME_CHECK_JIT, + [RETURN_GENERATOR] = _TAIL_CALL_RETURN_GENERATOR, + [RETURN_VALUE] = _TAIL_CALL_RETURN_VALUE, + [SEND] = _TAIL_CALL_SEND, + [SEND_ASYNC_GEN] = _TAIL_CALL_SEND_ASYNC_GEN, + [SEND_GEN] = _TAIL_CALL_SEND_GEN, + [SEND_VIRTUAL] = _TAIL_CALL_SEND_VIRTUAL, + [SETUP_ANNOTATIONS] = _TAIL_CALL_SETUP_ANNOTATIONS, + [SET_ADD] = _TAIL_CALL_SET_ADD, + [SET_FUNCTION_ATTRIBUTE] = _TAIL_CALL_SET_FUNCTION_ATTRIBUTE, + [SET_UPDATE] = _TAIL_CALL_SET_UPDATE, + [STORE_ATTR] = _TAIL_CALL_STORE_ATTR, + [STORE_ATTR_INSTANCE_VALUE] = _TAIL_CALL_STORE_ATTR_INSTANCE_VALUE, + [STORE_ATTR_SLOT] = _TAIL_CALL_STORE_ATTR_SLOT, + [STORE_ATTR_WITH_HINT] = _TAIL_CALL_STORE_ATTR_WITH_HINT, + [STORE_DEREF] = _TAIL_CALL_STORE_DEREF, + [STORE_FAST] = _TAIL_CALL_STORE_FAST, + [STORE_FAST_LOAD_FAST] = _TAIL_CALL_STORE_FAST_LOAD_FAST, + [STORE_FAST_STORE_FAST] = _TAIL_CALL_STORE_FAST_STORE_FAST, + [STORE_GLOBAL] = _TAIL_CALL_STORE_GLOBAL, + [STORE_NAME] = _TAIL_CALL_STORE_NAME, + [STORE_SLICE] = _TAIL_CALL_STORE_SLICE, + [STORE_SUBSCR] = _TAIL_CALL_STORE_SUBSCR, + [STORE_SUBSCR_DICT] = _TAIL_CALL_STORE_SUBSCR_DICT, + [STORE_SUBSCR_LIST_INT] = _TAIL_CALL_STORE_SUBSCR_LIST_INT, + [SWAP] = _TAIL_CALL_SWAP, + [TO_BOOL] = _TAIL_CALL_TO_BOOL, + [TO_BOOL_ALWAYS_TRUE] = _TAIL_CALL_TO_BOOL_ALWAYS_TRUE, + [TO_BOOL_BOOL] = _TAIL_CALL_TO_BOOL_BOOL, + [TO_BOOL_INT] = _TAIL_CALL_TO_BOOL_INT, + [TO_BOOL_LIST] = _TAIL_CALL_TO_BOOL_LIST, + [TO_BOOL_NONE] = _TAIL_CALL_TO_BOOL_NONE, + [TO_BOOL_STR] = _TAIL_CALL_TO_BOOL_STR, + [TRACE_RECORD] = _TAIL_CALL_TRACE_RECORD, + [UNARY_INVERT] = _TAIL_CALL_UNARY_INVERT, + [UNARY_NEGATIVE] = _TAIL_CALL_UNARY_NEGATIVE, + [UNARY_NOT] = _TAIL_CALL_UNARY_NOT, + [UNPACK_EX] = _TAIL_CALL_UNPACK_EX, + [UNPACK_SEQUENCE] = _TAIL_CALL_UNPACK_SEQUENCE, + [UNPACK_SEQUENCE_LIST] = _TAIL_CALL_UNPACK_SEQUENCE_LIST, + [UNPACK_SEQUENCE_TUPLE] = _TAIL_CALL_UNPACK_SEQUENCE_TUPLE, + [UNPACK_SEQUENCE_TWO_TUPLE] = _TAIL_CALL_UNPACK_SEQUENCE_TWO_TUPLE, + [WITH_EXCEPT_START] = _TAIL_CALL_WITH_EXCEPT_START, + [YIELD_VALUE] = _TAIL_CALL_YIELD_VALUE, + [120] = _TAIL_CALL_UNKNOWN_OPCODE, + [121] = _TAIL_CALL_UNKNOWN_OPCODE, + [122] = _TAIL_CALL_UNKNOWN_OPCODE, + [123] = _TAIL_CALL_UNKNOWN_OPCODE, + [124] = _TAIL_CALL_UNKNOWN_OPCODE, + [125] = _TAIL_CALL_UNKNOWN_OPCODE, + [126] = _TAIL_CALL_UNKNOWN_OPCODE, + [127] = _TAIL_CALL_UNKNOWN_OPCODE, + [219] = _TAIL_CALL_UNKNOWN_OPCODE, + [220] = _TAIL_CALL_UNKNOWN_OPCODE, + [221] = _TAIL_CALL_UNKNOWN_OPCODE, + [222] = _TAIL_CALL_UNKNOWN_OPCODE, + [223] = _TAIL_CALL_UNKNOWN_OPCODE, + [224] = _TAIL_CALL_UNKNOWN_OPCODE, + [225] = _TAIL_CALL_UNKNOWN_OPCODE, + [226] = _TAIL_CALL_UNKNOWN_OPCODE, + [227] = _TAIL_CALL_UNKNOWN_OPCODE, + [228] = _TAIL_CALL_UNKNOWN_OPCODE, + [229] = _TAIL_CALL_UNKNOWN_OPCODE, + [230] = _TAIL_CALL_UNKNOWN_OPCODE, + [231] = _TAIL_CALL_UNKNOWN_OPCODE, + [232] = _TAIL_CALL_UNKNOWN_OPCODE, +}; +static py_tail_call_funcptr instruction_funcptr_tracing_table[256] = { + [BINARY_OP] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_ADD_FLOAT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_ADD_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_ADD_UNICODE] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_EXTEND] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_INPLACE_ADD_UNICODE] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_MULTIPLY_FLOAT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_MULTIPLY_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_DICT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_GETITEM] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_LIST_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_LIST_SLICE] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_STR_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_TUPLE_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_USTR_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBTRACT_FLOAT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBTRACT_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_SLICE] = _TAIL_CALL_TRACE_RECORD, + [BUILD_INTERPOLATION] = _TAIL_CALL_TRACE_RECORD, + [BUILD_LIST] = _TAIL_CALL_TRACE_RECORD, + [BUILD_MAP] = _TAIL_CALL_TRACE_RECORD, + [BUILD_SET] = _TAIL_CALL_TRACE_RECORD, + [BUILD_SLICE] = _TAIL_CALL_TRACE_RECORD, + [BUILD_STRING] = _TAIL_CALL_TRACE_RECORD, + [BUILD_TEMPLATE] = _TAIL_CALL_TRACE_RECORD, + [BUILD_TUPLE] = _TAIL_CALL_TRACE_RECORD, + [CACHE] = _TAIL_CALL_TRACE_RECORD, + [CALL] = _TAIL_CALL_TRACE_RECORD, + [CALL_ALLOC_AND_ENTER_INIT] = _TAIL_CALL_TRACE_RECORD, + [CALL_BOUND_METHOD_EXACT_ARGS] = _TAIL_CALL_TRACE_RECORD, + [CALL_BOUND_METHOD_GENERAL] = _TAIL_CALL_TRACE_RECORD, + [CALL_BUILTIN_CLASS] = _TAIL_CALL_TRACE_RECORD, + [CALL_BUILTIN_FAST] = _TAIL_CALL_TRACE_RECORD, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = _TAIL_CALL_TRACE_RECORD, + [CALL_BUILTIN_O] = _TAIL_CALL_TRACE_RECORD, + [CALL_EX_NON_PY_GENERAL] = _TAIL_CALL_TRACE_RECORD, + [CALL_EX_PY] = _TAIL_CALL_TRACE_RECORD, + [CALL_FUNCTION_EX] = _TAIL_CALL_TRACE_RECORD, + [CALL_INTRINSIC_1] = _TAIL_CALL_TRACE_RECORD, + [CALL_INTRINSIC_2] = _TAIL_CALL_TRACE_RECORD, + [CALL_ISINSTANCE] = _TAIL_CALL_TRACE_RECORD, + [CALL_KW] = _TAIL_CALL_TRACE_RECORD, + [CALL_KW_BOUND_METHOD] = _TAIL_CALL_TRACE_RECORD, + [CALL_KW_NON_PY] = _TAIL_CALL_TRACE_RECORD, + [CALL_KW_PY] = _TAIL_CALL_TRACE_RECORD, + [CALL_LEN] = _TAIL_CALL_TRACE_RECORD, + [CALL_LIST_APPEND] = _TAIL_CALL_TRACE_RECORD, + [CALL_METHOD_DESCRIPTOR_FAST] = _TAIL_CALL_TRACE_RECORD, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = _TAIL_CALL_TRACE_RECORD, + [CALL_METHOD_DESCRIPTOR_NOARGS] = _TAIL_CALL_TRACE_RECORD, + [CALL_METHOD_DESCRIPTOR_O] = _TAIL_CALL_TRACE_RECORD, + [CALL_NON_PY_GENERAL] = _TAIL_CALL_TRACE_RECORD, + [CALL_PY_EXACT_ARGS] = _TAIL_CALL_TRACE_RECORD, + [CALL_PY_GENERAL] = _TAIL_CALL_TRACE_RECORD, + [CALL_STR_1] = _TAIL_CALL_TRACE_RECORD, + [CALL_TUPLE_1] = _TAIL_CALL_TRACE_RECORD, + [CALL_TYPE_1] = _TAIL_CALL_TRACE_RECORD, + [CHECK_EG_MATCH] = _TAIL_CALL_TRACE_RECORD, + [CHECK_EXC_MATCH] = _TAIL_CALL_TRACE_RECORD, + [CLEANUP_THROW] = _TAIL_CALL_TRACE_RECORD, + [COMPARE_OP] = _TAIL_CALL_TRACE_RECORD, + [COMPARE_OP_FLOAT] = _TAIL_CALL_TRACE_RECORD, + [COMPARE_OP_INT] = _TAIL_CALL_TRACE_RECORD, + [COMPARE_OP_STR] = _TAIL_CALL_TRACE_RECORD, + [CONTAINS_OP] = _TAIL_CALL_TRACE_RECORD, + [CONTAINS_OP_DICT] = _TAIL_CALL_TRACE_RECORD, + [CONTAINS_OP_SET] = _TAIL_CALL_TRACE_RECORD, + [CONVERT_VALUE] = _TAIL_CALL_TRACE_RECORD, + [COPY] = _TAIL_CALL_TRACE_RECORD, + [COPY_FREE_VARS] = _TAIL_CALL_TRACE_RECORD, + [DELETE_ATTR] = _TAIL_CALL_TRACE_RECORD, + [DELETE_DEREF] = _TAIL_CALL_TRACE_RECORD, + [DELETE_FAST] = _TAIL_CALL_TRACE_RECORD, + [DELETE_GLOBAL] = _TAIL_CALL_TRACE_RECORD, + [DELETE_NAME] = _TAIL_CALL_TRACE_RECORD, + [DELETE_SUBSCR] = _TAIL_CALL_TRACE_RECORD, + [DICT_MERGE] = _TAIL_CALL_TRACE_RECORD, + [DICT_UPDATE] = _TAIL_CALL_TRACE_RECORD, + [END_ASYNC_FOR] = _TAIL_CALL_TRACE_RECORD, + [END_FOR] = _TAIL_CALL_TRACE_RECORD, + [END_SEND] = _TAIL_CALL_TRACE_RECORD, + [ENTER_EXECUTOR] = _TAIL_CALL_TRACE_RECORD, + [EXIT_INIT_CHECK] = _TAIL_CALL_TRACE_RECORD, + [EXTENDED_ARG] = _TAIL_CALL_TRACE_RECORD, + [FORMAT_SIMPLE] = _TAIL_CALL_TRACE_RECORD, + [FORMAT_WITH_SPEC] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER_GEN] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER_LIST] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER_RANGE] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER_TUPLE] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER_VIRTUAL] = _TAIL_CALL_TRACE_RECORD, + [GET_AITER] = _TAIL_CALL_TRACE_RECORD, + [GET_ANEXT] = _TAIL_CALL_TRACE_RECORD, + [GET_AWAITABLE] = _TAIL_CALL_TRACE_RECORD, + [GET_ITER] = _TAIL_CALL_TRACE_RECORD, + [GET_ITER_SELF] = _TAIL_CALL_TRACE_RECORD, + [GET_ITER_VIRTUAL] = _TAIL_CALL_TRACE_RECORD, + [GET_LEN] = _TAIL_CALL_TRACE_RECORD, + [IMPORT_FROM] = _TAIL_CALL_TRACE_RECORD, + [IMPORT_NAME] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_CALL] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_CALL_FUNCTION_EX] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_CALL_KW] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_END_ASYNC_FOR] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_END_FOR] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_END_SEND] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_FOR_ITER] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_INSTRUCTION] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_JUMP_BACKWARD] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_JUMP_FORWARD] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_LINE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_LOAD_SUPER_ATTR] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_NOT_TAKEN] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_POP_ITER] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_POP_JUMP_IF_FALSE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_POP_JUMP_IF_NONE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_POP_JUMP_IF_TRUE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_RESUME] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_RETURN_VALUE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_YIELD_VALUE] = _TAIL_CALL_TRACE_RECORD, + [INTERPRETER_EXIT] = _TAIL_CALL_TRACE_RECORD, + [IS_OP] = _TAIL_CALL_TRACE_RECORD, + [JUMP_BACKWARD] = _TAIL_CALL_TRACE_RECORD, + [JUMP_BACKWARD_JIT] = _TAIL_CALL_TRACE_RECORD, + [JUMP_BACKWARD_NO_INTERRUPT] = _TAIL_CALL_TRACE_RECORD, + [JUMP_BACKWARD_NO_JIT] = _TAIL_CALL_TRACE_RECORD, + [JUMP_FORWARD] = _TAIL_CALL_TRACE_RECORD, + [LIST_APPEND] = _TAIL_CALL_TRACE_RECORD, + [LIST_EXTEND] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_CLASS] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_INSTANCE_VALUE] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_METHOD_LAZY_DICT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_METHOD_NO_DICT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_METHOD_WITH_VALUES] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_MODULE] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_PROPERTY] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_SLOT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_WITH_HINT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_BUILD_CLASS] = _TAIL_CALL_TRACE_RECORD, + [LOAD_COMMON_CONSTANT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_CONST] = _TAIL_CALL_TRACE_RECORD, + [LOAD_DEREF] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST_AND_CLEAR] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST_BORROW] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST_BORROW_LOAD_FAST_BORROW] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST_CHECK] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST_LOAD_FAST] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FROM_DICT_OR_DEREF] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FROM_DICT_OR_GLOBALS] = _TAIL_CALL_TRACE_RECORD, + [LOAD_GLOBAL] = _TAIL_CALL_TRACE_RECORD, + [LOAD_GLOBAL_BUILTIN] = _TAIL_CALL_TRACE_RECORD, + [LOAD_GLOBAL_MODULE] = _TAIL_CALL_TRACE_RECORD, + [LOAD_LOCALS] = _TAIL_CALL_TRACE_RECORD, + [LOAD_NAME] = _TAIL_CALL_TRACE_RECORD, + [LOAD_SMALL_INT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_SPECIAL] = _TAIL_CALL_TRACE_RECORD, + [LOAD_SUPER_ATTR] = _TAIL_CALL_TRACE_RECORD, + [LOAD_SUPER_ATTR_ATTR] = _TAIL_CALL_TRACE_RECORD, + [LOAD_SUPER_ATTR_METHOD] = _TAIL_CALL_TRACE_RECORD, + [MAKE_CELL] = _TAIL_CALL_TRACE_RECORD, + [MAKE_FUNCTION] = _TAIL_CALL_TRACE_RECORD, + [MAP_ADD] = _TAIL_CALL_TRACE_RECORD, + [MATCH_CLASS] = _TAIL_CALL_TRACE_RECORD, + [MATCH_KEYS] = _TAIL_CALL_TRACE_RECORD, + [MATCH_MAPPING] = _TAIL_CALL_TRACE_RECORD, + [MATCH_SEQUENCE] = _TAIL_CALL_TRACE_RECORD, + [NOP] = _TAIL_CALL_TRACE_RECORD, + [NOT_TAKEN] = _TAIL_CALL_TRACE_RECORD, + [POP_EXCEPT] = _TAIL_CALL_TRACE_RECORD, + [POP_ITER] = _TAIL_CALL_TRACE_RECORD, + [POP_JUMP_IF_FALSE] = _TAIL_CALL_TRACE_RECORD, + [POP_JUMP_IF_NONE] = _TAIL_CALL_TRACE_RECORD, + [POP_JUMP_IF_NOT_NONE] = _TAIL_CALL_TRACE_RECORD, + [POP_JUMP_IF_TRUE] = _TAIL_CALL_TRACE_RECORD, + [POP_TOP] = _TAIL_CALL_TRACE_RECORD, + [PUSH_EXC_INFO] = _TAIL_CALL_TRACE_RECORD, + [PUSH_NULL] = _TAIL_CALL_TRACE_RECORD, + [RAISE_VARARGS] = _TAIL_CALL_TRACE_RECORD, + [RERAISE] = _TAIL_CALL_TRACE_RECORD, + [RESERVED] = _TAIL_CALL_TRACE_RECORD, + [RESUME] = _TAIL_CALL_TRACE_RECORD, + [RESUME_CHECK] = _TAIL_CALL_TRACE_RECORD, + [RESUME_CHECK_JIT] = _TAIL_CALL_TRACE_RECORD, + [RETURN_GENERATOR] = _TAIL_CALL_TRACE_RECORD, + [RETURN_VALUE] = _TAIL_CALL_TRACE_RECORD, + [SEND] = _TAIL_CALL_TRACE_RECORD, + [SEND_ASYNC_GEN] = _TAIL_CALL_TRACE_RECORD, + [SEND_GEN] = _TAIL_CALL_TRACE_RECORD, + [SEND_VIRTUAL] = _TAIL_CALL_TRACE_RECORD, + [SETUP_ANNOTATIONS] = _TAIL_CALL_TRACE_RECORD, + [SET_ADD] = _TAIL_CALL_TRACE_RECORD, + [SET_FUNCTION_ATTRIBUTE] = _TAIL_CALL_TRACE_RECORD, + [SET_UPDATE] = _TAIL_CALL_TRACE_RECORD, + [STORE_ATTR] = _TAIL_CALL_TRACE_RECORD, + [STORE_ATTR_INSTANCE_VALUE] = _TAIL_CALL_TRACE_RECORD, + [STORE_ATTR_SLOT] = _TAIL_CALL_TRACE_RECORD, + [STORE_ATTR_WITH_HINT] = _TAIL_CALL_TRACE_RECORD, + [STORE_DEREF] = _TAIL_CALL_TRACE_RECORD, + [STORE_FAST] = _TAIL_CALL_TRACE_RECORD, + [STORE_FAST_LOAD_FAST] = _TAIL_CALL_TRACE_RECORD, + [STORE_FAST_STORE_FAST] = _TAIL_CALL_TRACE_RECORD, + [STORE_GLOBAL] = _TAIL_CALL_TRACE_RECORD, + [STORE_NAME] = _TAIL_CALL_TRACE_RECORD, + [STORE_SLICE] = _TAIL_CALL_TRACE_RECORD, + [STORE_SUBSCR] = _TAIL_CALL_TRACE_RECORD, + [STORE_SUBSCR_DICT] = _TAIL_CALL_TRACE_RECORD, + [STORE_SUBSCR_LIST_INT] = _TAIL_CALL_TRACE_RECORD, + [SWAP] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_ALWAYS_TRUE] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_BOOL] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_INT] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_LIST] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_NONE] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_STR] = _TAIL_CALL_TRACE_RECORD, + [TRACE_RECORD] = _TAIL_CALL_TRACE_RECORD, + [UNARY_INVERT] = _TAIL_CALL_TRACE_RECORD, + [UNARY_NEGATIVE] = _TAIL_CALL_TRACE_RECORD, + [UNARY_NOT] = _TAIL_CALL_TRACE_RECORD, + [UNPACK_EX] = _TAIL_CALL_TRACE_RECORD, + [UNPACK_SEQUENCE] = _TAIL_CALL_TRACE_RECORD, + [UNPACK_SEQUENCE_LIST] = _TAIL_CALL_TRACE_RECORD, + [UNPACK_SEQUENCE_TUPLE] = _TAIL_CALL_TRACE_RECORD, + [UNPACK_SEQUENCE_TWO_TUPLE] = _TAIL_CALL_TRACE_RECORD, + [WITH_EXCEPT_START] = _TAIL_CALL_TRACE_RECORD, + [YIELD_VALUE] = _TAIL_CALL_TRACE_RECORD, + [120] = _TAIL_CALL_UNKNOWN_OPCODE, + [121] = _TAIL_CALL_UNKNOWN_OPCODE, + [122] = _TAIL_CALL_UNKNOWN_OPCODE, + [123] = _TAIL_CALL_UNKNOWN_OPCODE, + [124] = _TAIL_CALL_UNKNOWN_OPCODE, + [125] = _TAIL_CALL_UNKNOWN_OPCODE, + [126] = _TAIL_CALL_UNKNOWN_OPCODE, + [127] = _TAIL_CALL_UNKNOWN_OPCODE, + [219] = _TAIL_CALL_UNKNOWN_OPCODE, + [220] = _TAIL_CALL_UNKNOWN_OPCODE, + [221] = _TAIL_CALL_UNKNOWN_OPCODE, + [222] = _TAIL_CALL_UNKNOWN_OPCODE, + [223] = _TAIL_CALL_UNKNOWN_OPCODE, + [224] = _TAIL_CALL_UNKNOWN_OPCODE, + [225] = _TAIL_CALL_UNKNOWN_OPCODE, + [226] = _TAIL_CALL_UNKNOWN_OPCODE, + [227] = _TAIL_CALL_UNKNOWN_OPCODE, + [228] = _TAIL_CALL_UNKNOWN_OPCODE, + [229] = _TAIL_CALL_UNKNOWN_OPCODE, + [230] = _TAIL_CALL_UNKNOWN_OPCODE, + [231] = _TAIL_CALL_UNKNOWN_OPCODE, + [232] = _TAIL_CALL_UNKNOWN_OPCODE, +}; +#endif /* _Py_TAIL_CALL_INTERP */ diff --git a/Modules/_testinternalcapi/testbytecodes.c b/Modules/_testinternalcapi/testbytecodes.c new file mode 100644 index 00000000000000..da3025d1f4fdca --- /dev/null +++ b/Modules/_testinternalcapi/testbytecodes.c @@ -0,0 +1,177 @@ +// This file contains instruction definitions. +// It is read by generators stored in Tools/cases_generator/ +// to generate Python/generated_cases.c.h and others. +// Note that there is some dummy C code at the top and bottom of the file +// to fool text editors like VS Code into believing this is valid C code. +// The actual instruction definitions start at // BEGIN BYTECODES //. +// See Tools/cases_generator/README.md for more information. + +#include "Python.h" +#include "pycore_abstract.h" // _PyIndex_Check() +#include "pycore_audit.h" // _PySys_Audit() +#include "pycore_backoff.h" +#include "pycore_cell.h" // PyCell_GetRef() +#include "pycore_code.h" +#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS +#include "pycore_function.h" +#include "pycore_instruments.h" +#include "pycore_interpolation.h" // _PyInterpolation_Build() +#include "pycore_intrinsics.h" +#include "pycore_long.h" // _PyLong_ExactDealloc(), _PyLong_GetZero() +#include "pycore_moduleobject.h" // PyModuleObject +#include "pycore_object.h" // _PyObject_GC_TRACK() +#include "pycore_opcode_metadata.h" // uop names +#include "pycore_opcode_utils.h" // MAKE_FUNCTION_* +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_* +#include "pycore_pyerrors.h" // _PyErr_GetRaisedException() +#include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_range.h" // _PyRangeIterObject +#include "pycore_setobject.h" // _PySet_NextEntry() +#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs +#include "pycore_stackref.h" +#include "pycore_template.h" // _PyTemplate_Build() +#include "pycore_tuple.h" // _PyTuple_ITEMS() +#include "pycore_typeobject.h" // _PySuper_Lookup() + +#include "pycore_dict.h" +#include "dictobject.h" +#include "pycore_frame.h" +#include "opcode.h" +#include "optimizer.h" +#include "pydtrace.h" +#include "setobject.h" + + +#define USE_COMPUTED_GOTOS 0 +#include "ceval_macros.h" + +/* Flow control macros */ + +#define inst(name, ...) case name: +#define op(name, ...) /* NAME is ignored */ +#define macro(name) static int MACRO_##name +#define super(name) static int SUPER_##name +#define family(name, ...) static int family_##name +#define pseudo(name) static int pseudo_##name +#define label(name) name: + +/* Annotations */ +#define guard +#define override +#define specializing +#define replicate(TIMES) +#define tier1 +#define no_save_ip + +// Dummy variables for stack effects. +static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub; +static PyObject *container, *start, *stop, *v, *lhs, *rhs, *res2; +static PyObject *list, *tuple, *dict, *owner, *set, *str, *tup, *map, *keys; +static PyObject *exit_func, *lasti, *val, *retval, *obj, *iter, *exhausted; +static PyObject *aiter, *awaitable, *iterable, *w, *exc_value, *bc, *locals; +static PyObject *orig, *excs, *update, *b, *fromlist, *level, *from; +static PyObject **pieces, **values; +static size_t jump; +// Dummy variables for cache effects +static uint16_t invert, counter, index, hint; +#define unused 0 // Used in a macro def, can't be static +static uint32_t type_version; +static _PyExecutorObject *current_executor; + +static PyObject * +dummy_func( + PyThreadState *tstate, + _PyInterpreterFrame *frame, + unsigned char opcode, + unsigned int oparg, + _Py_CODEUNIT *next_instr, + PyObject **stack_pointer, + int throwflag, + PyObject *args[] +) +{ + // Dummy labels. + pop_1_error: + // Dummy locals. + PyObject *dummy; + _Py_CODEUNIT *this_instr; + PyObject *attr; + PyObject *attrs; + PyObject *bottom; + PyObject *callable; + PyObject *callargs; + PyObject *codeobj; + PyObject *cond; + PyObject *descr; + PyObject *exc; + PyObject *exit; + PyObject *fget; + PyObject *fmt_spec; + PyObject *func; + uint32_t func_version; + PyObject *getattribute; + PyObject *kwargs; + PyObject *kwdefaults; + PyObject *len_o; + PyObject *match; + PyObject *match_type; + PyObject *method; + PyObject *mgr; + Py_ssize_t min_args; + PyObject *names; + PyObject *new_exc; + PyObject *next; + PyObject *none; + PyObject *null; + PyObject *prev_exc; + PyObject *receiver; + PyObject *rest; + int result; + PyObject *self; + PyObject *seq; + PyObject *slice; + PyObject *step; + PyObject *subject; + PyObject *top; + PyObject *type; + PyObject *typevars; + PyObject *val0; + PyObject *val1; + int values_or_none; + + switch (opcode) { + +// BEGIN BYTECODES // + + // Override an op + override op(_CHECK_PERIODIC_IF_NOT_YIELD_FROM, (--)) { + Test_EvalFrame_Resumes++; + if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { + int err = check_periodics(tstate); + ERROR_IF(err != 0); + } + } + + // Override an inst + override inst(LOAD_CONST, (-- value)) { + Test_EvalFrame_Loads++; + PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); + value = PyStackRef_FromPyObjectBorrow(obj); + } + + + // END BYTECODES // + + } + dispatch_opcode: + error: + exception_unwind: + exit_unwind: + handle_eval_breaker: + resume_frame: + start_frame: + unbound_local_error: + ; +} + +// Future families go below this point // diff --git a/Modules/_testinternalcapi/tuple.c b/Modules/_testinternalcapi/tuple.c new file mode 100644 index 00000000000000..c12ee32deb9164 --- /dev/null +++ b/Modules/_testinternalcapi/tuple.c @@ -0,0 +1,39 @@ +#include "parts.h" + +#include "pycore_tuple.h" + + +static PyObject * +tuple_from_pair(PyObject *Py_UNUSED(module), PyObject *args) +{ + PyObject *first, *second; + if (!PyArg_ParseTuple(args, "OO", &first, &second)) { + return NULL; + } + + return _PyTuple_FromPair(first, second); +} + +static PyObject * +tuple_from_pair_steal(PyObject *Py_UNUSED(module), PyObject *args) +{ + PyObject *first, *second; + if (!PyArg_ParseTuple(args, "OO", &first, &second)) { + return NULL; + } + + return _PyTuple_FromPairSteal(Py_NewRef(first), Py_NewRef(second)); +} + + +static PyMethodDef test_methods[] = { + {"tuple_from_pair", tuple_from_pair, METH_VARARGS}, + {"tuple_from_pair_steal", tuple_from_pair_steal, METH_VARARGS}, + {NULL}, +}; + +int +_PyTestInternalCapi_Init_Tuple(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c index 4dae99ec92a085..9314fccc6c915a 100644 --- a/Modules/_testlimitedcapi.c +++ b/Modules/_testlimitedcapi.c @@ -74,9 +74,15 @@ PyInit__testlimitedcapi(void) if (_PyTestLimitedCAPI_Init_Set(mod) < 0) { return NULL; } + if (_PyTestLimitedCAPI_Init_Slots(mod) < 0) { + return NULL; + } if (_PyTestLimitedCAPI_Init_Sys(mod) < 0) { return NULL; } + if (_PyTestLimitedCAPI_Init_ThreadState(mod) < 0) { + return NULL; + } if (_PyTestLimitedCAPI_Init_Tuple(mod) < 0) { return NULL; } @@ -92,5 +98,8 @@ PyInit__testlimitedcapi(void) if (_PyTestLimitedCAPI_Init_File(mod) < 0) { return NULL; } + if (_PyTestLimitedCAPI_Init_Weakref(mod) < 0) { + return NULL; + } return mod; } diff --git a/Modules/_testlimitedcapi/clinic/long.c.h b/Modules/_testlimitedcapi/clinic/long.c.h index ebaeb53921a82f..f9852aba266a57 100644 --- a/Modules/_testlimitedcapi/clinic/long.c.h +++ b/Modules/_testlimitedcapi/clinic/long.c.h @@ -84,8 +84,8 @@ PyDoc_STRVAR(_testlimitedcapi_test_long_as_size_t__doc__, "\n" "Test the PyLong_As{Size,Ssize}_t API.\n" "\n" -"At present this just tests that non-integer arguments are handled correctly.\n" -"It should be extended to test overflow handling."); +"At present this just tests that non-integer arguments are handled\n" +"correctly. It should be extended to test overflow handling."); #define _TESTLIMITEDCAPI_TEST_LONG_AS_SIZE_T_METHODDEF \ {"test_long_as_size_t", (PyCFunction)_testlimitedcapi_test_long_as_size_t, METH_NOARGS, _testlimitedcapi_test_long_as_size_t__doc__}, @@ -140,4 +140,4 @@ PyDoc_STRVAR(_testlimitedcapi_PyLong_AsInt__doc__, #define _TESTLIMITEDCAPI_PYLONG_ASINT_METHODDEF \ {"PyLong_AsInt", (PyCFunction)_testlimitedcapi_PyLong_AsInt, METH_O, _testlimitedcapi_PyLong_AsInt__doc__}, -/*[clinic end generated code: output=bc52b73c599f96c2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fb5c95bd0a4bdad8 input=a9049054013a1b77]*/ diff --git a/Modules/_testlimitedcapi/dict.c b/Modules/_testlimitedcapi/dict.c index ec32712eef6434..b9acda00897ad4 100644 --- a/Modules/_testlimitedcapi/dict.c +++ b/Modules/_testlimitedcapi/dict.c @@ -32,6 +32,7 @@ dictproxy_new(PyObject *self, PyObject *obj) static PyObject * dict_clear(PyObject *self, PyObject *obj) { + NULLABLE(obj); PyDict_Clear(obj); Py_RETURN_NONE; } diff --git a/Modules/_testlimitedcapi/heaptype_relative.c b/Modules/_testlimitedcapi/heaptype_relative.c index fc278a70b77d31..c02a52368b5324 100644 --- a/Modules/_testlimitedcapi/heaptype_relative.c +++ b/Modules/_testlimitedcapi/heaptype_relative.c @@ -1,7 +1,7 @@ -// Need limited C API version 3.12 for PyType_FromMetaclass() +// Need limited C API version 3.15 for _DuringGC functions #include "pyconfig.h" // Py_GIL_DISABLED #if !defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API) -# define Py_LIMITED_API 0x030c0000 +# define Py_LIMITED_API 0x030f0000 #endif #include "parts.h" @@ -55,6 +55,8 @@ make_sized_heaptypes(PyObject *module, PyObject *args) goto finally; } char *data_ptr = PyObject_GetTypeData(instance, (PyTypeObject *)sub); + assert(data_ptr == PyObject_GetTypeData_DuringGC(instance, + (PyTypeObject *)sub)); if (!data_ptr) { goto finally; } @@ -80,6 +82,7 @@ var_heaptype_set_data_to_3s( PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { void *data_ptr = PyObject_GetTypeData(self, defining_class); + assert(data_ptr == PyObject_GetTypeData_DuringGC(self, defining_class)); if (!data_ptr) { return NULL; } @@ -96,6 +99,7 @@ var_heaptype_get_data(PyObject *self, PyTypeObject *defining_class, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { void *data_ptr = PyObject_GetTypeData(self, defining_class); + assert(data_ptr == PyObject_GetTypeData_DuringGC(self, defining_class)); if (!data_ptr) { return NULL; } @@ -259,6 +263,7 @@ heapctypewithrelativedict_dealloc(PyObject* self) { PyTypeObject *tp = Py_TYPE(self); HeapCTypeWithDictStruct *data = PyObject_GetTypeData(self, tp); + assert(data == PyObject_GetTypeData_DuringGC(self, tp)); Py_XDECREF(data->dict); PyObject_Free(self); Py_DECREF(tp); @@ -297,6 +302,7 @@ heapctypewithrelativeweakref_dealloc(PyObject* self) { PyTypeObject *tp = Py_TYPE(self); HeapCTypeWithWeakrefStruct *data = PyObject_GetTypeData(self, tp); + assert(data == PyObject_GetTypeData_DuringGC(self, tp)); if (data->weakreflist != NULL) { PyObject_ClearWeakRefs(self); } diff --git a/Modules/_testlimitedcapi/import.c b/Modules/_testlimitedcapi/import.c index f85daee57d712e..f572212ba88b76 100644 --- a/Modules/_testlimitedcapi/import.c +++ b/Modules/_testlimitedcapi/import.c @@ -22,7 +22,7 @@ static PyObject * pyimport_getmagictag(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { const char *tag = PyImport_GetMagicTag(); - return PyUnicode_FromString(tag); + return tag ? PyUnicode_FromString(tag) : Py_NewRef(Py_None); } diff --git a/Modules/_testlimitedcapi/long.c b/Modules/_testlimitedcapi/long.c index 34bc7331da9247..99b9e96760d50d 100644 --- a/Modules/_testlimitedcapi/long.c +++ b/Modules/_testlimitedcapi/long.c @@ -451,13 +451,13 @@ _testlimitedcapi.test_long_as_size_t Test the PyLong_As{Size,Ssize}_t API. -At present this just tests that non-integer arguments are handled correctly. -It should be extended to test overflow handling. +At present this just tests that non-integer arguments are handled +correctly. It should be extended to test overflow handling. [clinic start generated code]*/ static PyObject * _testlimitedcapi_test_long_as_size_t_impl(PyObject *module) -/*[clinic end generated code: output=297a9f14a42f55af input=8923d8f2038c46f4]*/ +/*[clinic end generated code: output=297a9f14a42f55af input=692e73744b35bf6e]*/ { size_t out_u; Py_ssize_t out_s; diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h index 60f6f03011a65c..c51d285e19ab0d 100644 --- a/Modules/_testlimitedcapi/parts.h +++ b/Modules/_testlimitedcapi/parts.h @@ -37,11 +37,14 @@ int _PyTestLimitedCAPI_Init_List(PyObject *module); int _PyTestLimitedCAPI_Init_Long(PyObject *module); int _PyTestLimitedCAPI_Init_PyOS(PyObject *module); int _PyTestLimitedCAPI_Init_Set(PyObject *module); +int _PyTestLimitedCAPI_Init_Slots(PyObject *module); int _PyTestLimitedCAPI_Init_Sys(PyObject *module); +int _PyTestLimitedCAPI_Init_ThreadState(PyObject *module); int _PyTestLimitedCAPI_Init_Tuple(PyObject *module); int _PyTestLimitedCAPI_Init_Unicode(PyObject *module); int _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *module); int _PyTestLimitedCAPI_Init_Version(PyObject *module); int _PyTestLimitedCAPI_Init_File(PyObject *module); +int _PyTestLimitedCAPI_Init_Weakref(PyObject *module); #endif // Py_TESTLIMITEDCAPI_PARTS_H diff --git a/Modules/_testlimitedcapi/set.c b/Modules/_testlimitedcapi/set.c index 35da5fa5f008e1..8aade9502b6182 100644 --- a/Modules/_testlimitedcapi/set.c +++ b/Modules/_testlimitedcapi/set.c @@ -155,6 +155,117 @@ test_frozenset_add_in_capi(PyObject *self, PyObject *Py_UNUSED(obj)) return NULL; } +static PyObject * +raiseTestError(const char* test_name, const char* msg) +{ + PyErr_Format(PyExc_AssertionError, "%s: %s", test_name, msg); + return NULL; +} + +static PyObject * +test_frozenset_add_in_capi_tracking_immutable(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + // Test: GC tracking - frozenset with only immutable items should not be tracked + PyObject *frozenset = PyFrozenSet_New(NULL); + if (frozenset == NULL) { + return NULL; + } + if (PySet_Add(frozenset, Py_True) < 0) { + Py_DECREF(frozenset); + return NULL; + } + if (PyObject_GC_IsTracked(frozenset)) { + Py_DECREF(frozenset); + return raiseTestError("test_frozenset_add_in_capi_tracking_immutable", + "frozenset with only bool should not be GC tracked"); + } + Py_DECREF(frozenset); + Py_RETURN_NONE; +} + +static PyObject * +test_frozenset_add_in_capi_tracking(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + // Test: GC tracking - frozenset with tracked object should be tracked + PyObject *frozenset = PyFrozenSet_New(NULL); + if (frozenset == NULL) { + return NULL; + } + + PyObject *tracked_obj = PyErr_NewException("_testlimitedcapi.py_set_add", NULL, NULL); + if (tracked_obj == NULL) { + goto error; + } + if (!PyObject_GC_IsTracked(tracked_obj)) { + Py_DECREF(frozenset); + Py_DECREF(tracked_obj); + return raiseTestError("test_frozenset_add_in_capi_tracking", + "test object should be tracked"); + } + if (PySet_Add(frozenset, tracked_obj) < 0) { + goto error; + } + Py_DECREF(tracked_obj); + if (!PyObject_GC_IsTracked(frozenset)) { + Py_DECREF(frozenset); + return raiseTestError("test_frozenset_add_in_capi_tracking", + "frozenset with with GC tracked object should be tracked"); + } + Py_DECREF(frozenset); + Py_RETURN_NONE; + +error: + Py_DECREF(frozenset); + Py_XDECREF(tracked_obj); + return NULL; +} + + +static PyObject * +test_set_contains_does_not_convert_unhashable_key(PyObject *self, PyObject *Py_UNUSED(obj)) +{ + // See https://docs.python.org/3/c-api/set.html#c.PySet_Contains + PyObject *outer_set = PySet_New(NULL); + + PyObject *needle = PySet_New(NULL); + if (needle == NULL) { + Py_DECREF(outer_set); + return NULL; + } + + PyObject *num = PyLong_FromLong(42); + if (num == NULL) { + Py_DECREF(outer_set); + Py_DECREF(needle); + return NULL; + } + + if (PySet_Add(needle, num) < 0) { + Py_DECREF(outer_set); + Py_DECREF(needle); + Py_DECREF(num); + return NULL; + } + + int result = PySet_Contains(outer_set, needle); + + Py_DECREF(num); + Py_DECREF(needle); + Py_DECREF(outer_set); + + if (result < 0) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Clear(); + Py_RETURN_NONE; + } + return NULL; + } + + PyErr_SetString(PyExc_AssertionError, + "PySet_Contains should have raised TypeError for unhashable key"); + return NULL; +} + static PyMethodDef test_methods[] = { {"set_check", set_check, METH_O}, {"set_checkexact", set_checkexact, METH_O}, @@ -174,6 +285,10 @@ static PyMethodDef test_methods[] = { {"set_clear", set_clear, METH_O}, {"test_frozenset_add_in_capi", test_frozenset_add_in_capi, METH_NOARGS}, + {"test_frozenset_add_in_capi_tracking", test_frozenset_add_in_capi_tracking, METH_NOARGS}, + {"test_frozenset_add_in_capi_tracking_immutable", test_frozenset_add_in_capi_tracking_immutable, METH_NOARGS}, + {"test_set_contains_does_not_convert_unhashable_key", + test_set_contains_does_not_convert_unhashable_key, METH_NOARGS}, {NULL}, }; diff --git a/Modules/_testlimitedcapi/slots.c b/Modules/_testlimitedcapi/slots.c new file mode 100644 index 00000000000000..7a8d6466e53a09 --- /dev/null +++ b/Modules/_testlimitedcapi/slots.c @@ -0,0 +1,629 @@ +#define Py_LIMITED_API 0x030f0000 + +#include "parts.h" + +PyABIInfo_VAR(abi_info); + +/* Define a bunch of (mostly nonsensical) functions to put in slots, so + * Lib/test/test_capi/test_slots.py can verify they've been assigned to + * the right slots. + + * This module is full of "magic constants" which simply need to match + * between the C and Python part of the tests. + */ + +// getbufferproc: export buffer; increment a counter +static int +demo_getbuffer(PyObject *exporter, Py_buffer *view, int flags) +{ + Py_INCREF(exporter); + // PyObject_GetTypeData & Py_TYPE: safe on non-subclassable type + int *data = PyObject_GetTypeData(exporter, Py_TYPE(exporter)); + if (!data) { + return -1; + } + (*data)++; + return PyBuffer_FillInfo(view, exporter, "buf", 4, 1, flags); +} + +// releasebufferproc: release buffer; decrement a counter +static void +demo_releasebuffer(PyObject *exporter, Py_buffer *view) +{ + Py_DECREF(exporter); + // PyObject_GetTypeData & Py_TYPE: safe on non-subclassable type + int *data = PyObject_GetTypeData(exporter, Py_TYPE(exporter)); + if (!data) { + PyErr_WriteUnraisable(exporter); + return; + } + (*data)--; + return; +} + +// objobjargproc: raise KeyError +static int +demo_ass_subscript(PyObject *o, PyObject *key, PyObject *v) +{ + PyErr_Format(PyExc_KeyError, "I don't like that key"); + return -1; +} + +// lenfunc: report 456 +static Py_ssize_t +demo_length(PyObject *o) +{ + return (Py_ssize_t)456; +} + +// binaryfunc; return constant value +static PyObject *binop_123(PyObject* a, PyObject *b) { return PyLong_FromLong(123); } +static PyObject *binop_234(PyObject* a, PyObject *b) { return PyLong_FromLong(234); } +static PyObject *binop_345(PyObject* a, PyObject *b) { return PyLong_FromLong(345); } +static PyObject *binop_456(PyObject* a, PyObject *b) { return PyLong_FromLong(456); } +static PyObject *binop_567(PyObject* a, PyObject *b) { return PyLong_FromLong(567); } +static PyObject *binop_678(PyObject* a, PyObject *b) { return PyLong_FromLong(678); } + +static PyObject * +type_from_slots(PyObject* module, PyObject *args) +{ + char *case_name; + if (!PyArg_ParseTuple(args, "s", &case_name)) { + return NULL; + } +#define CASE(NAME) \ + if (strcmp(case_name, NAME) == 0) { \ + return PyType_FromSlots((PySlot[]) { \ + PySlot_DATA(Py_tp_name, "_testlimitedcapi.MyType"), \ + PySlot_DATA(Py_tp_module, module), \ + ///////////////////////////////////////////////////////////////////////// +#define ENDCASE() \ + PySlot_END \ + }); \ + } \ + ///////////////////////////////////////////////////////////////////////// + + CASE("basic") + ENDCASE() + CASE("foreign_slot") + PySlot_DATA(Py_mod_name, "this is not a module"), + ENDCASE() + CASE("basicsize") + PySlot_SIZE(Py_tp_basicsize, 256), + ENDCASE() + CASE("extra_basicsize") + PySlot_SIZE(Py_tp_extra_basicsize, 256), + ENDCASE() + CASE("itemsize") + PySlot_SIZE(Py_tp_itemsize, 16), + ENDCASE() + CASE("flags") + PySlot_UINT64(Py_tp_flags, + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_BASETYPE), + ENDCASE() + CASE("matmul_123") + PySlot_FUNC(Py_nb_matrix_multiply, binop_123), + ENDCASE() + CASE("optional_end") + {.sl_flags=PySlot_OPTIONAL}, + ENDCASE() + CASE("invalid") + {.sl_id=Py_slot_invalid}, + ENDCASE() + CASE("invalid_fbad") + {.sl_id=0xfbad}, + ENDCASE() + CASE("optional_invalid") + {.sl_id=Py_slot_invalid, .sl_flags=PySlot_OPTIONAL}, + PySlot_SIZE(Py_tp_extra_basicsize, 256), + ENDCASE() + CASE("optional_invalid_fbad") + {.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL}, + PySlot_SIZE(Py_tp_extra_basicsize, 256), + ENDCASE() + CASE("old_slot_numbers") + PySlot_FUNC(1, demo_getbuffer), + PySlot_FUNC(2, demo_releasebuffer), + PySlot_FUNC(3, demo_ass_subscript), + PySlot_FUNC(4, demo_length), + PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)), + PySlot_STATIC_DATA(Py_tp_members, ((PyMemberDef[]) { + {"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET}, + {NULL}, + })), + ENDCASE() + CASE("new_slot_numbers") + PySlot_FUNC(88, demo_getbuffer), + PySlot_FUNC(89, demo_releasebuffer), + PySlot_FUNC(90, demo_ass_subscript), + PySlot_FUNC(91, demo_length), + PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)), + PySlot_STATIC_DATA(Py_tp_members, ((PyMemberDef[]) { + {"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET}, + {NULL}, + })), + ENDCASE() + CASE("nonstatic_tp_members") + PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)), + PySlot_DATA(Py_tp_members, ((PyMemberDef[]) { + {"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET}, + {NULL}, + })), + ENDCASE() + CASE("intptr_flags_macro") + PySlot_PTR(Py_tp_flags, (void*)(intptr_t)Py_TPFLAGS_IMMUTABLETYPE), + ENDCASE() + CASE("intptr_flags_struct") + {.sl_id=Py_tp_flags, + .sl_flags=PySlot_INTPTR, + .sl_ptr=(void*)(intptr_t)Py_TPFLAGS_IMMUTABLETYPE, + }, + ENDCASE() + CASE("intptr_static") + PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)), + PySlot_PTR_STATIC(Py_tp_members, ((PyMemberDef[]) { + {"attribute", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET}, + {NULL}, + })), + ENDCASE() + CASE("nested") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_subtract, binop_234), + PySlot_END, + })), + ENDCASE() + CASE("nested_max") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_subtract, binop_234), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_multiply, binop_345), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_true_divide, binop_456), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_remainder, binop_567), + PySlot_END + })), + PySlot_END, + })), + PySlot_END, + })), + PySlot_END, + })), + ENDCASE() + CASE("nested_over_limit") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_subtract, binop_234), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_multiply, binop_345), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_true_divide, binop_456), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_remainder, binop_567), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_xor, binop_678), + PySlot_END + })), + PySlot_END + })), + PySlot_END, + })), + PySlot_END, + })), + PySlot_END, + })), + ENDCASE() + CASE("nested_old") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_subtract, binop_234}, + {0}, + })), + ENDCASE() + CASE("nested_old_max") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_subtract, binop_234}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_multiply, binop_345}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_true_divide, binop_456}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_remainder, binop_567}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })), + ENDCASE() + CASE("nested_old_over_limit") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_subtract, binop_234}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_multiply, binop_345}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_true_divide, binop_456}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_remainder, binop_567}, + {Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_xor, binop_678}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })), + ENDCASE() + CASE("nested_pingpong") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_subtract, binop_234}, + {Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_multiply, binop_345), + PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) { + {Py_nb_true_divide, binop_456}, + {Py_slot_subslots, ((PySlot[]) { + PySlot_FUNC(Py_nb_remainder, binop_567), + PySlot_END + })}, + {0}, + })), + PySlot_END, + })}, + {0}, + })), + ENDCASE() + CASE("repeat_add") + PySlot_FUNC(Py_nb_add, binop_123), + PySlot_FUNC(Py_nb_add, binop_456), + ENDCASE() + CASE("repeat_module") + PySlot_DATA(Py_tp_module, Py_True), + PySlot_DATA(Py_tp_module, Py_False), + ENDCASE() + +#undef CASE +#undef ENDCASE + PyErr_Format(PyExc_SystemError, "bad case: %s", case_name); + return NULL; +} + +static PyObject * +type_from_null_slot(PyObject* module, PyObject *args) +{ + long slot_number; + if (!PyArg_ParseTuple(args, "l", &slot_number)) { + return NULL; + } + return PyType_FromSlots((PySlot[]) { + PySlot_DATA(Py_tp_name, "_testlimitedcapi.MyType"), + PySlot_DATA(Py_tp_module, module), + PySlot_PTR_STATIC((uint16_t)slot_number, NULL), + PySlot_END + }); +} + +static PyObject * +type_from_null_spec_slot(PyObject* Py_UNUSED(module), PyObject *args) +{ + long slot_number; + if (!PyArg_ParseTuple(args, "l", &slot_number)) { + return NULL; + } + return PyType_FromSpec(&(PyType_Spec) { + .name = "_testlimitedcapi.MyType", + .slots = (PyType_Slot[]) { + {slot_number, NULL}, + {0}, + }, + }); +} + +static PyObject * +demo_create(PyObject *spec, PyModuleDef *def) +{ + assert(def == NULL); + return Py_NewRef(spec); +} + +static int +demo_exec(PyObject *mod) +{ + return PyModule_AddStringConstant(mod, "exec_done", "yes"); +} + +static PyMethodDef *TestMethods; + +static PyObject * +module_from_slots(PyObject* Py_UNUSED(module), PyObject *args) +{ + PyObject *spec; + char *case_name; + if (!PyArg_ParseTuple(args, "sO", &case_name, &spec)) { + return NULL; + } + PyObject *mod = NULL; +#define CASE(NAME) \ + if (strcmp(case_name, NAME) == 0) { \ + mod = PyModule_FromSlotsAndSpec((PySlot[]) { \ + PySlot_DATA(Py_mod_abi, &abi_info), \ + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), \ + ///////////////////////////////////////////////////////////////////////// +#define ENDCASE() \ + PySlot_END \ + }, spec); \ + } \ + ///////////////////////////////////////////////////////////////////////// + + CASE("basic") + ENDCASE() + CASE("foreign_slot") + PySlot_DATA(Py_tp_name, "this is not a type"), + ENDCASE() + CASE("state_size") + PySlot_SIZE(Py_mod_state_size, 42), + ENDCASE() + CASE("multi_interp") + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + ENDCASE() + CASE("exec") + PySlot_FUNC(Py_mod_exec, demo_exec), + ENDCASE() + CASE("optional_end") + {.sl_flags=PySlot_OPTIONAL}, + ENDCASE() + CASE("invalid") + {.sl_id=Py_slot_invalid}, + ENDCASE() + CASE("invalid_fbad") + {.sl_id=0xfbad}, + ENDCASE() + CASE("optional_invalid") + {.sl_id=Py_slot_invalid, .sl_flags=PySlot_OPTIONAL}, + PySlot_SIZE(Py_mod_exec, demo_exec), + ENDCASE() + CASE("optional_invalid_fbad") + {.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL}, + PySlot_SIZE(Py_mod_exec, demo_exec), + ENDCASE() + CASE("old_slot_numbers") + // 1: see old_slot_number_create case + PySlot_FUNC(2, demo_exec), + PySlot_DATA(3, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + // 4: see module_from_gil_slot function + ENDCASE() + CASE("new_slot_numbers") + // 84: see new_slot_number_create case + PySlot_FUNC(85, demo_exec), + PySlot_FUNC(86, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + // 87: see module_from_gil_slot function + ENDCASE() + CASE("old_slot_number_create") + PySlot_FUNC(1, demo_create), + ENDCASE() + CASE("new_slot_number_create") + PySlot_FUNC(84, demo_create), + ENDCASE() + CASE("nonstatic_mod_methods") + PySlot_DATA(Py_mod_methods, TestMethods), + ENDCASE() + CASE("intptr_methods") + PySlot_PTR_STATIC(Py_mod_methods, TestMethods), + ENDCASE() + CASE("nested") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_mod_doc, "doc"), + PySlot_END, + })), + ENDCASE() + CASE("nested_max") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_SIZE(Py_mod_state_size, 53), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_mod_doc, "doc"), + PySlot_END + })), + PySlot_END, + })), + PySlot_END, + })), + PySlot_END, + })), + ENDCASE() + CASE("nested_over_limit") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_SIZE(Py_mod_state_size, 53), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_mod_doc, "doc"), + PySlot_DATA(Py_slot_subslots, ((PySlot[]) { + PySlot_END + })), + PySlot_END + })), + PySlot_END, + })), + PySlot_END, + })), + PySlot_END, + })), + ENDCASE() + CASE("nested_old") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_doc, "doc"}, + {0}, + })), + ENDCASE() + CASE("nested_old_max") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_state_size, (void*)(intptr_t)53}, + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_doc, "doc"}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })), + ENDCASE() + CASE("nested_old_over_limit") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_state_size, (void*)(intptr_t)53}, + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_doc, "doc"}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })}, + {0}, + })), + ENDCASE() + CASE("nested_pingpong") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) { + {Py_mod_state_size, (void*)(intptr_t)53}, + {Py_slot_subslots, ((PySlot[]) { + PySlot_DATA(Py_mod_doc, "doc"), + PySlot_END + })}, + {0}, + })), + PySlot_END, + })}, + {0}, + })), + ENDCASE() + CASE("repeat_create") + PySlot_DATA(Py_mod_create, demo_create), + PySlot_DATA(Py_mod_create, demo_create), + PySlot_DATA(Py_mod_create, demo_create), + ENDCASE() + CASE("repeat_gil") + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + ENDCASE() + CASE("repeat_exec") + PySlot_FUNC(Py_mod_exec, demo_exec), + PySlot_FUNC(Py_mod_exec, demo_exec), + ENDCASE() + +#undef CASE +#undef ENDCASE + if (!mod) { + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_SystemError, "bad case: %s", case_name); + return NULL; + } + return NULL; + } + if (PyModule_Check(mod)) { + Py_ssize_t size; + if (PyModule_GetStateSize(mod, &size) < 0) { + Py_DECREF(mod); + return NULL; + } + if (PyModule_AddIntConstant(mod, "state_size", (long)size) < 0) { + Py_DECREF(mod); + return NULL; + } + if (PyModule_Exec(mod) < 0) { + return NULL; + } + } + return mod; +} + +static PyObject * +module_from_gil_slot(PyObject* Py_UNUSED(module), PyObject *args) +{ + long slot_number; + PyObject *spec; + if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) { + return NULL; + } + return PyModule_FromSlotsAndSpec((PySlot[]) { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_PTR_STATIC((uint16_t)slot_number, Py_MOD_GIL_NOT_USED), + PySlot_END + }, spec); +} + +static PyObject * +module_from_null_slot(PyObject* Py_UNUSED(module), PyObject *args) +{ + long slot_number; + PyObject *spec; + if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) { + return NULL; + } + uint16_t maybe_gil_slot = Py_mod_gil; + if ((slot_number == 4) || (slot_number == 87)) { + // Do not repeat the GIL slot + maybe_gil_slot = Py_slot_invalid; + } + return PyModule_FromSlotsAndSpec((PySlot[]) { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "mymod"), + PySlot_PTR_STATIC((uint16_t)slot_number, NULL), + { + .sl_id=maybe_gil_slot, + .sl_flags=PySlot_OPTIONAL, + .sl_ptr=Py_MOD_GIL_NOT_USED, + }, + PySlot_END + }, spec); +} + +static PyMethodDef _TestMethods[] = { + {"type_from_slots", type_from_slots, METH_VARARGS}, + {"module_from_gil_slot", module_from_gil_slot, METH_VARARGS}, + {"type_from_null_slot", type_from_null_slot, METH_VARARGS}, + {"type_from_null_spec_slot", type_from_null_spec_slot, METH_VARARGS}, + {"module_from_slots", module_from_slots, METH_VARARGS}, + {"module_from_null_slot", module_from_null_slot, METH_VARARGS}, + {NULL}, +}; +static PyMethodDef *TestMethods = _TestMethods; + +int +_PyTestLimitedCAPI_Init_Slots(PyObject *m) +{ + if (PyModule_AddFunctions(m, TestMethods) < 0) { + return -1; + } + + return 0; +} diff --git a/Modules/_testlimitedcapi/threadstate.c b/Modules/_testlimitedcapi/threadstate.c new file mode 100644 index 00000000000000..f2539d97150d69 --- /dev/null +++ b/Modules/_testlimitedcapi/threadstate.c @@ -0,0 +1,25 @@ +#include "parts.h" +#include "util.h" + +static PyObject * +threadstate_set_async_exc(PyObject *module, PyObject *args) +{ + unsigned long id; + PyObject *exc; + if (!PyArg_ParseTuple(args, "kO", &id, &exc)) { + return NULL; + } + int result = PyThreadState_SetAsyncExc(id, exc); + return PyLong_FromLong(result); +} + +static PyMethodDef test_methods[] = { + {"threadstate_set_async_exc", threadstate_set_async_exc, METH_VARARGS, NULL}, + {NULL}, +}; + +int +_PyTestLimitedCAPI_Init_ThreadState(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} diff --git a/Modules/_testlimitedcapi/weakref.c b/Modules/_testlimitedcapi/weakref.c new file mode 100644 index 00000000000000..e7f9d54d1a0d59 --- /dev/null +++ b/Modules/_testlimitedcapi/weakref.c @@ -0,0 +1,78 @@ +#include "pyconfig.h" // Py_GIL_DISABLED +#ifndef Py_GIL_DISABLED + // Need limited C API 3.5 for PyModule_AddFunctions() +# define Py_LIMITED_API 0x03050000 +#endif + +#include "parts.h" +#include "util.h" + + +static PyObject * +pyweakref_check(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyWeakref_Check(obj)); +} + +static PyObject * +pyweakref_checkref(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyWeakref_CheckRef(obj)); +} + +static PyObject * +pyweakref_checkrefexact(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyWeakref_CheckRefExact(obj)); +} + +static PyObject * +pyweakref_checkproxy(PyObject *module, PyObject *obj) +{ + NULLABLE(obj); + return PyLong_FromLong(PyWeakref_CheckProxy(obj)); +} + +static PyObject * +pyweakref_newref(PyObject *module, PyObject *args) +{ + PyObject *obj; + PyObject *callback = NULL; + if (!PyArg_ParseTuple(args, "O|O", &obj, &callback)) { + return NULL; + } + NULLABLE(obj); + return PyWeakref_NewRef(obj, callback); +} + +static PyObject * +pyweakref_newproxy(PyObject *module, PyObject *args) +{ + PyObject *obj; + PyObject *callback = NULL; + if (!PyArg_ParseTuple(args, "O|O", &obj, &callback)) { + return NULL; + } + NULLABLE(obj); + return PyWeakref_NewProxy(obj, callback); +} + + +static PyMethodDef test_methods[] = { + {"pyweakref_check", pyweakref_check, METH_O}, + {"pyweakref_checkref", pyweakref_checkref, METH_O}, + {"pyweakref_checkrefexact", pyweakref_checkrefexact, METH_O}, + {"pyweakref_checkproxy", pyweakref_checkproxy, METH_O}, + {"pyweakref_newref", pyweakref_newref, METH_VARARGS}, + {"pyweakref_newproxy", pyweakref_newproxy, METH_VARARGS}, + {NULL}, +}; + +int +_PyTestLimitedCAPI_Init_Weakref(PyObject *m) +{ + return PyModule_AddFunctions(m, test_methods); +} diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c index bfec0678e2c669..57ba61e67f1d7f 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -141,21 +141,24 @@ _testmultiphase.StateAccessType.get_defining_module Return the module of the defining class. -Also tests that result of PyType_GetModuleByDef matches defining_class's -module. +Also tests that result of PyType_GetModuleByDef matches +defining_class's module. [clinic start generated code]*/ static PyObject * _testmultiphase_StateAccessType_get_defining_module_impl(StateAccessTypeObject *self, PyTypeObject *cls) -/*[clinic end generated code: output=ba2a14284a5d0921 input=d2c7245c8a9d06f8]*/ +/*[clinic end generated code: output=ba2a14284a5d0921 input=903e7f66555d65ae]*/ { PyObject *retval; retval = PyType_GetModule(cls); + assert(retval == PyType_GetModule_DuringGC(cls)); if (retval == NULL) { return NULL; } assert(PyType_GetModuleByDef(Py_TYPE(self), &def_meth_state_access) == retval); + assert(PyType_GetModuleByToken_DuringGC(Py_TYPE(self), &def_meth_state_access) + == retval); return Py_NewRef(retval); } @@ -172,9 +175,14 @@ _testmultiphase_StateAccessType_getmodulebydef_bad_def_impl(StateAccessTypeObjec PyTypeObject *cls) /*[clinic end generated code: output=64509074dfcdbd31 input=edaff09aa4788204]*/ { - PyType_GetModuleByDef(Py_TYPE(self), &def_nonmodule); // should raise + // DuringGC: does not raise + assert(PyType_GetModuleByToken_DuringGC(Py_TYPE(self), &def_nonmodule) == NULL); + assert(!PyErr_Occurred()); + // should raise: + PyObject *m = PyType_GetModuleByDef(Py_TYPE(self), &def_nonmodule); assert(PyErr_Occurred()); - return NULL; + assert(m == NULL); + return m; } /*[clinic input] @@ -200,6 +208,7 @@ _testmultiphase_StateAccessType_increment_count_clinic_impl(StateAccessTypeObjec /*[clinic end generated code: output=3b34f86bc5473204 input=551d482e1fe0b8f5]*/ { meth_state *m_state = PyType_GetModuleState(cls); + assert(m_state == PyType_GetModuleState_DuringGC(cls)); if (twice) { n *= 2; } @@ -249,6 +258,7 @@ _StateAccessType_increment_count_noclinic(PyObject *self, n *= 2; } meth_state *m_state = PyType_GetModuleState(defining_class); + assert(m_state == PyType_GetModuleState_DuringGC(defining_class)); m_state->counter += n; Py_RETURN_NONE; @@ -268,6 +278,7 @@ _testmultiphase_StateAccessType_get_count_impl(StateAccessTypeObject *self, /*[clinic end generated code: output=64600f95b499a319 input=d5d181f12384849f]*/ { meth_state *m_state = PyType_GetModuleState(cls); + assert(m_state == PyType_GetModuleState_DuringGC(cls)); return PyLong_FromLong(m_state->counter); } @@ -435,6 +446,7 @@ static int execfunc(PyObject *m) } static PyModuleDef_Slot main_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, execfunc}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -481,6 +493,7 @@ createfunc_nonmodule(PyObject *spec, PyModuleDef *def) } static PyModuleDef_Slot slots_create_nonmodule[] = { + _Py_ABI_SLOT, {Py_mod_create, createfunc_nonmodule}, {0, NULL}, }; @@ -527,6 +540,7 @@ PyInit__testmultiphase_nonmodule_with_methods(void) /**** Non-ASCII-named modules ****/ static PyModuleDef_Slot nonascii_slots[] = { + _Py_ABI_SLOT, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; @@ -589,7 +603,7 @@ PyInit__testmultiphase_null_slots(void) /**** Problematic modules ****/ static PyModuleDef_Slot slots_bad_large[] = { - {_Py_mod_LAST_SLOT + 1, NULL}, + {Py_slot_invalid, NULL}, {0, NULL}, }; @@ -689,6 +703,7 @@ createfunc_noop(PyObject *spec, PyModuleDef *def) } static PyModuleDef_Slot slots_multiple_create_slots[] = { + _Py_ABI_SLOT, {Py_mod_create, createfunc_noop}, {Py_mod_create, createfunc_noop}, {0, NULL}, @@ -710,6 +725,7 @@ createfunc_null(PyObject *spec, PyModuleDef *def) } static PyModuleDef_Slot slots_create_null[] = { + _Py_ABI_SLOT, {Py_mod_create, createfunc_null}, {0, NULL}, }; @@ -752,6 +768,7 @@ createfunc_unreported_exception(PyObject *spec, PyModuleDef *def) } static PyModuleDef_Slot slots_create_unreported_exception[] = { + _Py_ABI_SLOT, {Py_mod_create, createfunc_unreported_exception}, {0, NULL}, }; @@ -766,6 +783,7 @@ PyInit__testmultiphase_create_unreported_exception(void) } static PyModuleDef_Slot slots_nonmodule_with_exec_slots[] = { + _Py_ABI_SLOT, {Py_mod_create, createfunc_nonmodule}, {Py_mod_exec, execfunc}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, @@ -789,6 +807,7 @@ execfunc_err(PyObject *mod) } static PyModuleDef_Slot slots_exec_err[] = { + _Py_ABI_SLOT, {Py_mod_exec, execfunc_err}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -812,6 +831,7 @@ execfunc_raise(PyObject *spec) } static PyModuleDef_Slot slots_exec_raise[] = { + _Py_ABI_SLOT, {Py_mod_exec, execfunc_raise}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -835,6 +855,7 @@ execfunc_unreported_exception(PyObject *mod) } static PyModuleDef_Slot slots_exec_unreported_exception[] = { + _Py_ABI_SLOT, {Py_mod_exec, execfunc_unreported_exception}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -850,6 +871,28 @@ PyInit__testmultiphase_exec_unreported_exception(void) return PyModuleDef_Init(&def_exec_unreported_exception); } +static int execfn_a1(PyObject*m) { return PyModule_AddIntConstant(m, "a", 1); } +static int execfn_b2(PyObject*m) { return PyModule_AddIntConstant(m, "b", 2); } +static int execfn_c3(PyObject*m) { return PyModule_AddIntConstant(m, "c", 3); } + +PyMODINIT_FUNC +PyInit__testmultiphase_exec_multiple(void) +{ + static PyModuleDef_Slot slots[] = { + {Py_mod_exec, execfn_a1}, + {Py_mod_exec, execfn_b2}, + {Py_mod_exec, execfn_c3}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0} + }; + static PyModuleDef def = { + PyModuleDef_HEAD_INIT, + .m_name="_testmultiphase_exec_multiple", + .m_slots=slots, + }; + return PyModuleDef_Init(&def); +} + static int meth_state_access_exec(PyObject *m) { @@ -857,6 +900,7 @@ meth_state_access_exec(PyObject *m) meth_state *m_state; m_state = PyModule_GetState(m); + assert(m_state == PyModule_GetState_DuringGC(m)); if (m_state == NULL) { return -1; } @@ -871,6 +915,7 @@ meth_state_access_exec(PyObject *m) } static PyModuleDef_Slot meth_state_access_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, meth_state_access_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -921,6 +966,7 @@ PyInit__test_module_state_shared(void) /* multiple interpreters support */ static PyModuleDef_Slot slots_multiple_multiple_interpreters_slots[] = { + _Py_ABI_SLOT, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -939,6 +985,7 @@ PyInit__testmultiphase_multiple_multiple_interpreters_slots(void) } static PyModuleDef_Slot non_isolated_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, execfunc}, {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -957,6 +1004,7 @@ PyInit__test_non_isolated(void) static PyModuleDef_Slot shared_gil_only_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, execfunc}, /* Note that Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED is the default. We put it here explicitly to draw attention to the contrast @@ -978,6 +1026,7 @@ PyInit__test_shared_gil_only(void) static PyModuleDef_Slot no_multiple_interpreter_slot_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, execfunc}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, @@ -993,3 +1042,210 @@ PyInit__test_no_multiple_interpreter_slot(void) { return PyModuleDef_Init(&no_multiple_interpreter_slot_def); } + + +/* PyModExport_* hooks */ + +PyABIInfo_VAR(abi_info); + +PyMODEXPORT_FUNC +PyModExport__test_from_modexport(void) +{ + static PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "_test_from_modexport"), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return slots; +} + +PyMODEXPORT_FUNC +PyModExport__test_from_modexport_gil_used(void) +{ + static PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "_test_from_modexport_gil_used"), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_USED), + PySlot_END, + }; + return slots; +} + +PyMODEXPORT_FUNC +PyModExport__test_from_modexport_null(void) +{ + return NULL; +} + +PyMODINIT_FUNC +PyModInit__test_from_modexport_null(void) +{ + // This is not called as fallback for failed PyModExport_* + assert(0); + PyErr_SetString(PyExc_AssertionError, "PyInit_ fallback called"); + return NULL; +} + +PyMODEXPORT_FUNC +PyModExport__test_from_modexport_exception(void) +{ + PyErr_SetString(PyExc_ValueError, "failed as requested"); + return NULL; +} + +PyMODINIT_FUNC +PyModInit__test_from_modexport_exception(void) +{ + // This is not called as fallback for failed PyModExport_* + assert(0); + PyErr_SetString(PyExc_AssertionError, "PyInit_ fallback called"); + return NULL; +} + +static PyObject * +modexport_create_string(PyObject *spec, PyModuleDef *def) +{ + assert(def == NULL); + return PyUnicode_FromString("is this \xf0\x9f\xa6\x8b... a module?"); +} + +PyMODEXPORT_FUNC +PyModExport__test_from_modexport_create_nonmodule(void) +{ + static PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "_test_from_modexport_create_nonmodule"), + PySlot_FUNC(Py_mod_create, modexport_create_string), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return slots; +} + +PyMODEXPORT_FUNC +PyModExport__test_from_modexport_create_nonmodule_gil_used(void) +{ + static PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "_test_from_modexport_create_nonmodule"), + PySlot_FUNC(Py_mod_create, modexport_create_string), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_USED), + PySlot_END, + }; + return slots; +} + +static PySlot modexport_empty_slots[] = { + PySlot_END, +}; + +PyMODEXPORT_FUNC +PyModExport__test_from_modexport_empty_slots(void) +{ + return modexport_empty_slots; +} + + +static PySlot modexport_minimal_slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_END, +}; + +PyMODEXPORT_FUNC +PyModExport__test_from_modexport_minimal_slots(void) +{ + return modexport_minimal_slots; +} + +static int +modexport_smoke_exec(PyObject *mod) +{ + // "magic" values 147 & 258 are expected in the test + if (PyModule_AddIntConstant(mod, "number", 147) < 0) { + return 0; + } + int *state = PyModule_GetState(mod); + assert(state == PyModule_GetState_DuringGC(mod)); + if (!state) { + return -1; + } + *state = 258; + + PyObject *tp = PyType_FromModuleAndSpec(mod, &StateAccessType_spec, NULL); + if (PyModule_Add(mod, "Example", tp) < 0) { + return -1; + } + + return 0; +} + +static PyObject * +modexport_smoke_get_state_int(PyObject *mod, PyObject *arg) +{ + int *state = PyModule_GetState(mod); + assert(state == PyModule_GetState_DuringGC(mod)); + if (!state) { + return NULL; + } + return PyLong_FromLong(*state); +} + +static const char modexport_smoke_test_token; + +static PyObject * +modexport_smoke_get_test_token(PyObject *mod, PyObject *arg) +{ + return PyLong_FromVoidPtr((void*)&modexport_smoke_test_token); +} + +static PyObject * +modexport_get_minimal_slots(PyObject *mod, PyObject *arg) +{ + /* Get the address of modexport_empty_slots. + * This method would be in the `_test_from_modexport_minimal_slots` module, + * if it had a methods slot. + */ + return PyLong_FromVoidPtr(&modexport_minimal_slots); +} + +static void +modexport_smoke_free(void *op) +{ + PyObject *mod = (PyObject *)op; + int *state = PyModule_GetState(mod); + assert(state == PyModule_GetState_DuringGC(mod)); + if (!state) { + PyErr_FormatUnraisable("Exception ignored in module %R free", mod); + } + assert(*state == 258); +} + +PyMODEXPORT_FUNC +PyModExport__test_from_modexport_smoke(void) +{ + static PyMethodDef methods[] = { + {"get_state_int", modexport_smoke_get_state_int, METH_NOARGS}, + {"get_test_token", modexport_smoke_get_test_token, METH_NOARGS}, + {"get_modexport_minimal_slots", modexport_get_minimal_slots, METH_NOARGS}, + {0}, + }; + static PySlot slots[] = { + PySlot_DATA(Py_mod_abi, &abi_info), + PySlot_DATA(Py_mod_name, "_test_from_modexport_smoke"), + PySlot_DATA(Py_mod_doc, "the expected docstring"), + PySlot_FUNC(Py_mod_exec, modexport_smoke_exec), + PySlot_SIZE(Py_mod_state_size, (void*)sizeof(int)), + PySlot_STATIC_DATA(Py_mod_methods, methods), + PySlot_FUNC(Py_mod_state_free, modexport_smoke_free), + PySlot_DATA(Py_mod_token, (void*)&modexport_smoke_test_token), + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, + }; + return slots; +} diff --git a/Modules/_testsinglephase.c b/Modules/_testsinglephase.c index 2c59085d15b5be..49ab2ad55398f0 100644 --- a/Modules/_testsinglephase.c +++ b/Modules/_testsinglephase.c @@ -244,6 +244,8 @@ static inline module_state * get_module_state(PyObject *module) { PyModuleDef *def = PyModule_GetDef(module); + assert(def); + if (def->m_size == -1) { return &global_state.module; } @@ -252,6 +254,7 @@ get_module_state(PyObject *module) } else { module_state *state = (module_state*)PyModule_GetState(module); + assert(state == PyModule_GetState_DuringGC(module)); assert(state != NULL); return state; } @@ -799,3 +802,11 @@ PyInit__testsinglephase_circular(void) } return Py_NewRef(static_module_circular); } + + +PyMODINIT_FUNC +PyInit__testsinglephase_raise_exception(void) +{ + PyErr_SetString(PyExc_RuntimeError, "evil"); + return NULL; +} diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index f82ad6870f850f..7a34c5a5a0ef6b 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -11,6 +11,7 @@ #include "pycore_pylifecycle.h" #include "pycore_pystate.h" // _PyThreadState_SetCurrent() #include "pycore_time.h" // _PyTime_FromSeconds() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #include "pycore_weakref.h" // _PyWeakref_GET_REF() #include <stddef.h> // offsetof() @@ -38,6 +39,20 @@ typedef struct { struct llist_node shutdown_handles; } thread_module_state; +typedef struct { + PyObject_HEAD + PyMutex lock; +} lockobject; + +#define lockobject_CAST(op) ((lockobject *)(op)) + +typedef struct { + PyObject_HEAD + _PyRecursiveMutex lock; +} rlockobject; + +#define rlockobject_CAST(op) ((rlockobject *)(op)) + static inline thread_module_state* get_thread_state(PyObject *module) { @@ -415,7 +430,7 @@ force_done(void *arg) static int ThreadHandle_start(ThreadHandle *self, PyObject *func, PyObject *args, - PyObject *kwargs) + PyObject *kwargs, int daemon) { // Mark the handle as starting to prevent any other threads from doing so PyMutex_Lock(&self->mutex); @@ -439,7 +454,8 @@ ThreadHandle_start(ThreadHandle *self, PyObject *func, PyObject *args, goto start_failed; } PyInterpreterState *interp = _PyInterpreterState_GET(); - boot->tstate = _PyThreadState_New(interp, _PyThreadState_WHENCE_THREADING); + uint8_t whence = daemon ? _PyThreadState_WHENCE_THREADING_DAEMON : _PyThreadState_WHENCE_THREADING; + boot->tstate = _PyThreadState_New(interp, whence); if (boot->tstate == NULL) { PyMem_RawFree(boot); if (!PyErr_Occurred()) { @@ -641,13 +657,6 @@ PyThreadHandleObject_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)PyThreadHandleObject_new(type); } -static int -PyThreadHandleObject_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static void PyThreadHandleObject_dealloc(PyObject *op) { @@ -704,6 +713,9 @@ PyThreadHandleObject_is_done(PyObject *op, PyObject *Py_UNUSED(dummy)) { PyThreadHandleObject *self = PyThreadHandleObject_CAST(op); if (_PyEvent_IsSet(&self->handle->thread_is_exiting)) { + if (_PyOnceFlag_CallOnce(&self->handle->once, join_thread, self->handle) == -1) { + return NULL; + } Py_RETURN_TRUE; } else { @@ -737,7 +749,7 @@ static PyType_Slot ThreadHandle_Type_slots[] = { {Py_tp_dealloc, PyThreadHandleObject_dealloc}, {Py_tp_repr, PyThreadHandleObject_repr}, {Py_tp_getset, ThreadHandle_getsetlist}, - {Py_tp_traverse, PyThreadHandleObject_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_methods, ThreadHandle_methods}, {Py_tp_new, PyThreadHandleObject_tp_new}, {0, 0} @@ -753,20 +765,6 @@ static PyType_Spec ThreadHandle_Type_spec = { /* Lock objects */ -typedef struct { - PyObject_HEAD - PyMutex lock; -} lockobject; - -#define lockobject_CAST(op) ((lockobject *)(op)) - -static int -lock_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static void lock_dealloc(PyObject *self) { @@ -779,16 +777,8 @@ lock_dealloc(PyObject *self) static int -lock_acquire_parse_args(PyObject *args, PyObject *kwds, - PyTime_t *timeout) +lock_acquire_parse_timeout(PyObject *timeout_obj, int blocking, PyTime_t *timeout) { - char *kwlist[] = {"blocking", "timeout", NULL}; - int blocking = 1; - PyObject *timeout_obj = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|pO:acquire", kwlist, - &blocking, &timeout_obj)) - return -1; - // XXX Use PyThread_ParseTimeoutArg(). const PyTime_t unset_timeout = _PyTime_FromSeconds(-1); @@ -823,14 +813,29 @@ lock_acquire_parse_args(PyObject *args, PyObject *kwds, } return 0; } +/*[clinic input] +_thread.lock.acquire + blocking: bool = True + timeout as timeoutobj: object(py_default="-1") = NULL + +Lock the lock. + +Without argument, this blocks if the lock is already locked +(even by the same thread), waiting for another thread to release +the lock, and return True once the lock is acquired. +With an argument, this will only block if the argument is true, +and the return value reflects whether the lock is acquired. +The blocking operation is interruptible. +[clinic start generated code]*/ static PyObject * -lock_PyThread_acquire_lock(PyObject *op, PyObject *args, PyObject *kwds) +_thread_lock_acquire_impl(lockobject *self, int blocking, + PyObject *timeoutobj) +/*[clinic end generated code: output=569d6b25d508bf6f input=73e75b3d2ec32677]*/ { - lockobject *self = lockobject_CAST(op); - PyTime_t timeout; - if (lock_acquire_parse_args(args, kwds, &timeout) < 0) { + + if (lock_acquire_parse_timeout(timeoutobj, blocking, &timeout) < 0) { return NULL; } @@ -848,33 +853,34 @@ lock_PyThread_acquire_lock(PyObject *op, PyObject *args, PyObject *kwds) return PyBool_FromLong(r == PY_LOCK_ACQUIRED); } -PyDoc_STRVAR(acquire_doc, -"acquire($self, /, blocking=True, timeout=-1)\n\ ---\n\ -\n\ -Lock the lock. Without argument, this blocks if the lock is already\n\ -locked (even by the same thread), waiting for another thread to release\n\ -the lock, and return True once the lock is acquired.\n\ -With an argument, this will only block if the argument is true,\n\ -and the return value reflects whether the lock is acquired.\n\ -The blocking operation is interruptible."); - -PyDoc_STRVAR(acquire_lock_doc, -"acquire_lock($self, /, blocking=True, timeout=-1)\n\ ---\n\ -\n\ -An obsolete synonym of acquire()."); +/*[clinic input] +_thread.lock.acquire_lock = _thread.lock.acquire -PyDoc_STRVAR(enter_doc, -"__enter__($self, /)\n\ ---\n\ -\n\ -Lock the lock."); +An obsolete synonym of acquire(). +[clinic start generated code]*/ static PyObject * -lock_PyThread_release_lock(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_lock_acquire_lock_impl(lockobject *self, int blocking, + PyObject *timeoutobj) +/*[clinic end generated code: output=ea6c87ea13b56694 input=5e65bd56327ebe85]*/ +{ + return _thread_lock_acquire_impl(self, blocking, timeoutobj); +} + +/*[clinic input] +_thread.lock.release + +Release the lock. + +Allows another thread that is blocked waiting for +the lock to acquire the lock. The lock must be in the locked state, +but it needn't be locked by the same thread that unlocks it. +[clinic start generated code]*/ + +static PyObject * +_thread_lock_release_impl(lockobject *self) +/*[clinic end generated code: output=a4ab0d75d6e9fb73 input=dfe48f962dfe99b4]*/ { - lockobject *self = lockobject_CAST(op); /* Sanity check: the lock must be locked */ if (_PyMutex_TryUnlock(&self->lock) < 0) { PyErr_SetString(ThreadError, "release unlocked lock"); @@ -884,44 +890,76 @@ lock_PyThread_release_lock(PyObject *op, PyObject *Py_UNUSED(dummy)) Py_RETURN_NONE; } -PyDoc_STRVAR(release_doc, -"release($self, /)\n\ ---\n\ -\n\ -Release the lock, allowing another thread that is blocked waiting for\n\ -the lock to acquire the lock. The lock must be in the locked state,\n\ -but it needn't be locked by the same thread that unlocks it."); +/*[clinic input] +_thread.lock.release_lock -PyDoc_STRVAR(release_lock_doc, -"release_lock($self, /)\n\ ---\n\ -\n\ -An obsolete synonym of release()."); +An obsolete synonym of release(). +[clinic start generated code]*/ -PyDoc_STRVAR(lock_exit_doc, -"__exit__($self, /, *exc_info)\n\ ---\n\ -\n\ -Release the lock."); +static PyObject * +_thread_lock_release_lock_impl(lockobject *self) +/*[clinic end generated code: output=43025044d51789bb input=74d91374fc601433]*/ +{ + return _thread_lock_release_impl(self); +} + +/*[clinic input] +_thread.lock.__enter__ + +Lock the lock. +[clinic start generated code]*/ static PyObject * -lock_locked_lock(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_lock___enter___impl(lockobject *self) +/*[clinic end generated code: output=f27725de751ae064 input=8f982991608d38e7]*/ +{ + return _thread_lock_acquire_impl(self, 1, NULL); +} + +/*[clinic input] +_thread.lock.__exit__ + exc_type: object + exc_value: object + exc_tb: object + / + +Release the lock. +[clinic start generated code]*/ + +static PyObject * +_thread_lock___exit___impl(lockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb) +/*[clinic end generated code: output=c9e8eefa69beed07 input=c74d4abe15a6c037]*/ +{ + return _thread_lock_release_impl(self); +} + + +/*[clinic input] +_thread.lock.locked + +Return whether the lock is in the locked state. +[clinic start generated code]*/ + +static PyObject * +_thread_lock_locked_impl(lockobject *self) +/*[clinic end generated code: output=63bb94e5a9efa382 input=d8e3d64861bbce73]*/ { - lockobject *self = lockobject_CAST(op); return PyBool_FromLong(PyMutex_IsLocked(&self->lock)); } -PyDoc_STRVAR(locked_doc, -"locked($self, /)\n\ ---\n\ -\n\ -Return whether the lock is in the locked state."); +/*[clinic input] +_thread.lock.locked_lock -PyDoc_STRVAR(locked_lock_doc, -"locked_lock($self, /)\n\ ---\n\ -\n\ -An obsolete synonym of locked()."); +An obsolete synonym of locked(). +[clinic start generated code]*/ + +static PyObject * +_thread_lock_locked_lock_impl(lockobject *self) +/*[clinic end generated code: output=f747c8329e905f8e input=500b0c3592f9bf84]*/ +{ + return _thread_lock_locked_impl(self); +} static PyObject * lock_repr(PyObject *op) @@ -932,10 +970,14 @@ lock_repr(PyObject *op) } #ifdef HAVE_FORK +/*[clinic input] +_thread.lock._at_fork_reinit +[clinic start generated code]*/ + static PyObject * -lock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_lock__at_fork_reinit_impl(lockobject *self) +/*[clinic end generated code: output=d8609f2d3bfa1fd5 input=a970cb76e2a0a131]*/ { - lockobject *self = lockobject_CAST(op); _PyMutex_at_fork_reinit(&self->lock); Py_RETURN_NONE; } @@ -960,25 +1002,16 @@ lock_new_impl(PyTypeObject *type) static PyMethodDef lock_methods[] = { - {"acquire_lock", _PyCFunction_CAST(lock_PyThread_acquire_lock), - METH_VARARGS | METH_KEYWORDS, acquire_lock_doc}, - {"acquire", _PyCFunction_CAST(lock_PyThread_acquire_lock), - METH_VARARGS | METH_KEYWORDS, acquire_doc}, - {"release_lock", lock_PyThread_release_lock, - METH_NOARGS, release_lock_doc}, - {"release", lock_PyThread_release_lock, - METH_NOARGS, release_doc}, - {"locked_lock", lock_locked_lock, - METH_NOARGS, locked_lock_doc}, - {"locked", lock_locked_lock, - METH_NOARGS, locked_doc}, - {"__enter__", _PyCFunction_CAST(lock_PyThread_acquire_lock), - METH_VARARGS | METH_KEYWORDS, enter_doc}, - {"__exit__", lock_PyThread_release_lock, - METH_VARARGS, lock_exit_doc}, + _THREAD_LOCK_ACQUIRE_LOCK_METHODDEF + _THREAD_LOCK_ACQUIRE_METHODDEF + _THREAD_LOCK_RELEASE_LOCK_METHODDEF + _THREAD_LOCK_RELEASE_METHODDEF + _THREAD_LOCK_LOCKED_LOCK_METHODDEF + _THREAD_LOCK_LOCKED_METHODDEF + _THREAD_LOCK___ENTER___METHODDEF + _THREAD_LOCK___EXIT___METHODDEF #ifdef HAVE_FORK - {"_at_fork_reinit", lock__at_fork_reinit, - METH_NOARGS, NULL}, + _THREAD_LOCK__AT_FORK_REINIT_METHODDEF #endif {NULL, NULL} /* sentinel */ }; @@ -1003,7 +1036,7 @@ static PyType_Slot lock_type_slots[] = { {Py_tp_repr, lock_repr}, {Py_tp_doc, (void *)lock_doc}, {Py_tp_methods, lock_methods}, - {Py_tp_traverse, lock_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_new, lock_new}, {0, 0} }; @@ -1018,20 +1051,6 @@ static PyType_Spec lock_type_spec = { /* Recursive lock objects */ -typedef struct { - PyObject_HEAD - _PyRecursiveMutex lock; -} rlockobject; - -#define rlockobject_CAST(op) ((rlockobject *)(op)) - -static int -rlock_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static int rlock_locked_impl(rlockobject *self) { @@ -1048,14 +1067,35 @@ rlock_dealloc(PyObject *self) Py_DECREF(tp); } +/*[clinic input] +_thread.RLock.acquire + blocking: bool = True + timeout as timeoutobj: object(py_default="-1") = NULL + +Lock the lock. + +`blocking` indicates whether we should wait +for the lock to be available or not. If `blocking` is False +and another thread holds the lock, the method will return False +immediately. If `blocking` is True and another thread holds +the lock, the method will wait for the lock to be released, +take it and then return True. +(note: the blocking operation is interruptible.) + +In all other cases, the method will return True immediately. +Precisely, if the current thread already holds the lock, its +internal counter is simply incremented. If nobody holds the lock, +the lock is taken and its internal counter initialized to 1. +[clinic start generated code]*/ static PyObject * -rlock_acquire(PyObject *op, PyObject *args, PyObject *kwds) +_thread_RLock_acquire_impl(rlockobject *self, int blocking, + PyObject *timeoutobj) +/*[clinic end generated code: output=73df5af6f67c1513 input=d55a0f5014522a8d]*/ { - rlockobject *self = rlockobject_CAST(op); PyTime_t timeout; - if (lock_acquire_parse_args(args, kwds, &timeout) < 0) { + if (lock_acquire_parse_timeout(timeoutobj, blocking, &timeout) < 0) { return NULL; } @@ -1073,33 +1113,38 @@ rlock_acquire(PyObject *op, PyObject *args, PyObject *kwds) return PyBool_FromLong(r == PY_LOCK_ACQUIRED); } -PyDoc_STRVAR(rlock_acquire_doc, -"acquire($self, /, blocking=True, timeout=-1)\n\ ---\n\ -\n\ -Lock the lock. `blocking` indicates whether we should wait\n\ -for the lock to be available or not. If `blocking` is False\n\ -and another thread holds the lock, the method will return False\n\ -immediately. If `blocking` is True and another thread holds\n\ -the lock, the method will wait for the lock to be released,\n\ -take it and then return True.\n\ -(note: the blocking operation is interruptible.)\n\ -\n\ -In all other cases, the method will return True immediately.\n\ -Precisely, if the current thread already holds the lock, its\n\ -internal counter is simply incremented. If nobody holds the lock,\n\ -the lock is taken and its internal counter initialized to 1."); +/*[clinic input] +_thread.RLock.__enter__ -PyDoc_STRVAR(rlock_enter_doc, -"__enter__($self, /)\n\ ---\n\ -\n\ -Lock the lock."); +Lock the lock. +[clinic start generated code]*/ static PyObject * -rlock_release(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock___enter___impl(rlockobject *self) +/*[clinic end generated code: output=63135898476bf89f input=33be37f459dca390]*/ +{ + return _thread_RLock_acquire_impl(self, 1, NULL); +} + +/*[clinic input] +_thread.RLock.release + +Release the lock. + +Allows another thread that is blocked waiting for the lock +to acquire the lock. The lock must be in the locked state, +and must be locked by the same thread that unlocks it; otherwise a +`RuntimeError` is raised. + +Do note that if the lock was acquire()d several times in a row by +the current thread, release() needs to be called as many times for +the lock to be available for other threads. +[clinic start generated code]*/ + +static PyObject * +_thread_RLock_release_impl(rlockobject *self) +/*[clinic end generated code: output=51f4a013c5fae2c5 input=7c188f60189be13a]*/ { - rlockobject *self = rlockobject_CAST(op); if (_PyRecursiveMutex_TryUnlock(&self->lock) < 0) { PyErr_SetString(PyExc_RuntimeError, "cannot release un-acquired lock"); @@ -1108,46 +1153,55 @@ rlock_release(PyObject *op, PyObject *Py_UNUSED(dummy)) Py_RETURN_NONE; } -PyDoc_STRVAR(rlock_release_doc, -"release($self, /)\n\ ---\n\ -\n\ -Release the lock, allowing another thread that is blocked waiting for\n\ -the lock to acquire the lock. The lock must be in the locked state,\n\ -and must be locked by the same thread that unlocks it; otherwise a\n\ -`RuntimeError` is raised.\n\ -\n\ -Do note that if the lock was acquire()d several times in a row by the\n\ -current thread, release() needs to be called as many times for the lock\n\ -to be available for other threads."); +/*[clinic input] +_thread.RLock.__exit__ + exc_type: object + exc_value: object + exc_tb: object + / -PyDoc_STRVAR(rlock_exit_doc, -"__exit__($self, /, *exc_info)\n\ ---\n\ -\n\ -Release the lock."); +Release the lock. + +[clinic start generated code]*/ static PyObject * -rlock_locked(PyObject *op, PyObject *Py_UNUSED(ignored)) +_thread_RLock___exit___impl(rlockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb) +/*[clinic end generated code: output=79bb44d551aedeb5 input=79accf0778d91002]*/ +{ + return _thread_RLock_release_impl(self); +} + +/*[clinic input] +_thread.RLock.locked + +Return a boolean indicating whether this object is locked right now. +[clinic start generated code]*/ + +static PyObject * +_thread_RLock_locked_impl(rlockobject *self) +/*[clinic end generated code: output=e9b6060492b3f94e input=8866d9237ba5391b]*/ { - rlockobject *self = rlockobject_CAST(op); int is_locked = rlock_locked_impl(self); return PyBool_FromLong(is_locked); } -PyDoc_STRVAR(rlock_locked_doc, -"locked()\n\ -\n\ -Return a boolean indicating whether this object is locked right now."); +/*[clinic input] +_thread.RLock._acquire_restore + state: object + / + +For internal use by `threading.Condition`. +[clinic start generated code]*/ static PyObject * -rlock_acquire_restore(PyObject *op, PyObject *args) +_thread_RLock__acquire_restore_impl(rlockobject *self, PyObject *state) +/*[clinic end generated code: output=beb8f2713a35e775 input=c8f2094fde059447]*/ { - rlockobject *self = rlockobject_CAST(op); PyThread_ident_t owner; Py_ssize_t count; - if (!PyArg_ParseTuple(args, "(n" Py_PARSE_THREAD_IDENT_T "):_acquire_restore", + if (!PyArg_Parse(state, "(n" Py_PARSE_THREAD_IDENT_T "):_acquire_restore", &count, &owner)) return NULL; @@ -1157,17 +1211,17 @@ rlock_acquire_restore(PyObject *op, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(rlock_acquire_restore_doc, -"_acquire_restore($self, state, /)\n\ ---\n\ -\n\ -For internal use by `threading.Condition`."); + +/*[clinic input] +_thread.RLock._release_save + +For internal use by `threading.Condition`. +[clinic start generated code]*/ static PyObject * -rlock_release_save(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__release_save_impl(rlockobject *self) +/*[clinic end generated code: output=d2916487315bea93 input=809d227cfc4a112c]*/ { - rlockobject *self = rlockobject_CAST(op); - if (!_PyRecursiveMutex_IsLockedByCurrentThread(&self->lock)) { PyErr_SetString(PyExc_RuntimeError, "cannot release un-acquired lock"); @@ -1181,42 +1235,38 @@ rlock_release_save(PyObject *op, PyObject *Py_UNUSED(dummy)) return Py_BuildValue("n" Py_PARSE_THREAD_IDENT_T, count, owner); } -PyDoc_STRVAR(rlock_release_save_doc, -"_release_save($self, /)\n\ ---\n\ -\n\ -For internal use by `threading.Condition`."); + +/*[clinic input] +_thread.RLock._recursion_count + +For internal use by reentrancy checks. +[clinic start generated code]*/ static PyObject * -rlock_recursion_count(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__recursion_count_impl(rlockobject *self) +/*[clinic end generated code: output=7993fb9695ef2c4d input=7fd1834cd7a4b044]*/ { - rlockobject *self = rlockobject_CAST(op); if (_PyRecursiveMutex_IsLockedByCurrentThread(&self->lock)) { return PyLong_FromSize_t(self->lock.level + 1); } return PyLong_FromLong(0); } -PyDoc_STRVAR(rlock_recursion_count_doc, -"_recursion_count($self, /)\n\ ---\n\ -\n\ -For internal use by reentrancy checks."); + +/*[clinic input] +_thread.RLock._is_owned + +For internal use by `threading.Condition`. +[clinic start generated code]*/ static PyObject * -rlock_is_owned(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__is_owned_impl(rlockobject *self) +/*[clinic end generated code: output=bf14268a3cabbe07 input=fba6535538deb858]*/ { - rlockobject *self = rlockobject_CAST(op); long owned = _PyRecursiveMutex_IsLockedByCurrentThread(&self->lock); return PyBool_FromLong(owned); } -PyDoc_STRVAR(rlock_is_owned_doc, -"_is_owned($self, /)\n\ ---\n\ -\n\ -For internal use by `threading.Condition`."); - /*[clinic input] @classmethod _thread.RLock.__new__ as rlock_new @@ -1256,10 +1306,14 @@ rlock_repr(PyObject *op) #ifdef HAVE_FORK +/*[clinic input] +_thread.RLock._at_fork_reinit +[clinic start generated code]*/ + static PyObject * -rlock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy)) +_thread_RLock__at_fork_reinit_impl(rlockobject *self) +/*[clinic end generated code: output=d77a4ce40351817c input=a3b625b026a8df4f]*/ { - rlockobject *self = rlockobject_CAST(op); self->lock = (_PyRecursiveMutex){0}; Py_RETURN_NONE; } @@ -1267,27 +1321,17 @@ rlock__at_fork_reinit(PyObject *op, PyObject *Py_UNUSED(dummy)) static PyMethodDef rlock_methods[] = { - {"acquire", _PyCFunction_CAST(rlock_acquire), - METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc}, - {"release", rlock_release, - METH_NOARGS, rlock_release_doc}, - {"locked", rlock_locked, - METH_NOARGS, rlock_locked_doc}, - {"_is_owned", rlock_is_owned, - METH_NOARGS, rlock_is_owned_doc}, - {"_acquire_restore", rlock_acquire_restore, - METH_VARARGS, rlock_acquire_restore_doc}, - {"_release_save", rlock_release_save, - METH_NOARGS, rlock_release_save_doc}, - {"_recursion_count", rlock_recursion_count, - METH_NOARGS, rlock_recursion_count_doc}, - {"__enter__", _PyCFunction_CAST(rlock_acquire), - METH_VARARGS | METH_KEYWORDS, rlock_enter_doc}, - {"__exit__", rlock_release, - METH_VARARGS, rlock_exit_doc}, + _THREAD_RLOCK_ACQUIRE_METHODDEF + _THREAD_RLOCK_RELEASE_METHODDEF + _THREAD_RLOCK_LOCKED_METHODDEF + _THREAD_RLOCK__IS_OWNED_METHODDEF + _THREAD_RLOCK__ACQUIRE_RESTORE_METHODDEF + _THREAD_RLOCK__RELEASE_SAVE_METHODDEF + _THREAD_RLOCK__RECURSION_COUNT_METHODDEF + _THREAD_RLOCK___ENTER___METHODDEF + _THREAD_RLOCK___EXIT___METHODDEF #ifdef HAVE_FORK - {"_at_fork_reinit", rlock__at_fork_reinit, - METH_NOARGS, NULL}, + _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF #endif {NULL, NULL} /* sentinel */ }; @@ -1299,7 +1343,7 @@ static PyType_Slot rlock_type_slots[] = { {Py_tp_methods, rlock_methods}, {Py_tp_alloc, PyType_GenericAlloc}, {Py_tp_new, rlock_new}, - {Py_tp_traverse, rlock_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {0, 0}, }; @@ -1437,13 +1481,11 @@ create_sentinel_wr(localobject *self) return NULL; } - PyObject *args = PyTuple_New(2); + PyObject *args = _PyTuple_FromPairSteal(self_wr, + Py_NewRef(tstate->threading_local_key)); if (args == NULL) { - Py_DECREF(self_wr); return NULL; } - PyTuple_SET_ITEM(args, 0, self_wr); - PyTuple_SET_ITEM(args, 1, Py_NewRef(tstate->threading_local_key)); PyObject *cb = PyCFunction_New(&wr_callback_def, args); Py_DECREF(args); @@ -1874,7 +1916,7 @@ do_start_new_thread(thread_module_state *state, PyObject *func, PyObject *args, add_to_shutdown_handles(state, handle); } - if (ThreadHandle_start(handle, func, args, kwargs) < 0) { + if (ThreadHandle_start(handle, func, args, kwargs, daemon) < 0) { if (!daemon) { remove_from_shutdown_handles(handle); } @@ -2157,9 +2199,10 @@ thread_stack_size(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "|n:stack_size", &new_size)) return NULL; - if (new_size < 0) { - PyErr_SetString(PyExc_ValueError, - "size must be 0 or a positive value"); + Py_ssize_t min_size = _PyOS_MIN_STACK_SIZE + SYSTEM_PAGE_SIZE; + if (new_size != 0 && new_size < min_size) { + PyErr_Format(PyExc_ValueError, + "size must be at least %zi bytes", min_size); return NULL; } @@ -2386,10 +2429,8 @@ thread_shutdown(PyObject *self, PyObject *args) // Wait for the thread to finish. If we're interrupted, such // as by a ctrl-c we print the error and exit early. if (ThreadHandle_join(handle, -1) < 0) { - PyErr_FormatUnraisable("Exception ignored while joining a thread " - "in _thread._shutdown()"); ThreadHandle_decref(handle); - Py_RETURN_NONE; + return NULL; } ThreadHandle_decref(handle); @@ -2484,7 +2525,9 @@ _thread__get_name_impl(PyObject *module) } #ifdef __sun - return PyUnicode_DecodeUTF8(name, strlen(name), "surrogateescape"); + // gh-138004: Decode Solaris/Illumos (e.g. OpenIndiana) thread names + // from ASCII, since OpenIndiana only supports ASCII names. + return PyUnicode_DecodeASCII(name, strlen(name), "surrogateescape"); #else return PyUnicode_DecodeFSDefault(name); #endif @@ -2522,8 +2565,9 @@ _thread_set_name_impl(PyObject *module, PyObject *name_obj) { #ifndef MS_WINDOWS #ifdef __sun - // Solaris always uses UTF-8 - const char *encoding = "utf-8"; + // gh-138004: Encode Solaris/Illumos thread names to ASCII, + // since OpenIndiana does not support non-ASCII names. + const char *encoding = "ascii"; #else // Encode the thread name to the filesystem encoding using the "replace" // error handler @@ -2812,6 +2856,7 @@ PyDoc_STRVAR(thread_doc, The 'threading' module provides a more convenient interface."); static PyModuleDef_Slot thread_module_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, thread_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 875840bd6a6364..6eca98a3c8033f 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -82,33 +82,10 @@ typedef int Tcl_Size; #ifdef HAVE_CREATEFILEHANDLER -/* This bit is to ensure that TCL_UNIX_FD is defined and doesn't interfere - with the proper calculation of FHANDLETYPE == TCL_UNIX_FD below. */ -#ifndef TCL_UNIX_FD -# ifdef TCL_WIN_SOCKET -# define TCL_UNIX_FD (! TCL_WIN_SOCKET) -# else -# define TCL_UNIX_FD 1 -# endif -#endif - -/* Tcl_CreateFileHandler() changed several times; these macros deal with the - messiness. In Tcl 8.0 and later, it is not available on Windows (and on - Unix, only because Jack added it back); when available on Windows, it only - applies to sockets. */ - -#ifdef MS_WINDOWS -#define FHANDLETYPE TCL_WIN_SOCKET -#else -#define FHANDLETYPE TCL_UNIX_FD -#endif - /* If Tcl can wait for a Unix file descriptor, define the EventHook() routine which uses this to handle Tcl events while the user is typing commands. */ -#if FHANDLETYPE == TCL_UNIX_FD #define WAIT_FOR_STDIN -#endif #endif /* HAVE_CREATEFILEHANDLER */ @@ -151,18 +128,20 @@ _get_tcl_lib_path(void) } /* Check expected location for an installed Python first */ - tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION); - if (tcl_library_path == NULL) { + PyObject* tmp_tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION); + if (tmp_tcl_library_path == NULL) { Py_DECREF(prefix); return NULL; } - tcl_library_path = PyUnicode_Concat(prefix, tcl_library_path); + tcl_library_path = PyUnicode_Concat(prefix, tmp_tcl_library_path); + Py_DECREF(tmp_tcl_library_path); Py_DECREF(prefix); if (tcl_library_path == NULL) { return NULL; } stat_return_value = _Py_stat(tcl_library_path, &stat_buf); if (stat_return_value == -2) { + Py_DECREF(tcl_library_path); return NULL; } if (stat_return_value == -1) { @@ -177,16 +156,17 @@ _get_tcl_lib_path(void) } stat_return_value = _Py_stat(tcl_library_path, &stat_buf); if (stat_return_value == -2) { + Py_DECREF(tcl_library_path); return NULL; } if (stat_return_value == -1) { /* tcltkDir for a repository build doesn't exist either, reset errno and leave Tcl to its own devices */ errno = 0; - tcl_library_path = NULL; + Py_CLEAR(tcl_library_path); } #else - tcl_library_path = NULL; + Py_CLEAR(tcl_library_path); #endif } already_checked = 1; @@ -258,7 +238,6 @@ static PyThread_type_lock tcl_lock = 0; #ifdef TCL_THREADS static Tcl_ThreadDataKey state_key; -typedef PyThreadState *ThreadSpecificData; #define tcl_tstate \ (*(PyThreadState**)Tcl_GetThreadData(&state_key, sizeof(PyThreadState*))) #else @@ -598,8 +577,12 @@ Tkapp_New(const char *screenName, const char *className, v->interp = Tcl_CreateInterp(); v->wantobjects = wantobjects; +#if TCL_MAJOR_VERSION >= 9 + v->threaded = 1; +#else v->threaded = Tcl_GetVar2Ex(v->interp, "tcl_platform", "threaded", TCL_GLOBAL_ONLY) != NULL; +#endif v->thread_id = Tcl_GetCurrentThread(); v->dispatching = 0; v->trace = NULL; @@ -727,11 +710,13 @@ Tkapp_New(const char *screenName, const char *className, if (!ret && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { str_path = _get_tcl_lib_path(); if (str_path == NULL && PyErr_Occurred()) { + Py_DECREF(v); return NULL; } if (str_path != NULL) { utf8_path = PyUnicode_AsUTF8String(str_path); if (utf8_path == NULL) { + Py_DECREF(v); return NULL; } Tcl_SetVar(v->interp, @@ -906,11 +891,14 @@ static PyType_Slot PyTclObject_Type_slots[] = { }; static PyType_Spec PyTclObject_Type_spec = { - "_tkinter.Tcl_Obj", - sizeof(PyTclObject), - 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, - PyTclObject_Type_slots, + .name = "_tkinter.Tcl_Obj", + .basicsize = sizeof(PyTclObject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = PyTclObject_Type_slots, }; @@ -963,6 +951,40 @@ asBignumObj(PyObject *value) return result; } +static Tcl_Obj* AsObj(PyObject *value); + +static Tcl_Obj* +TupleAsObj(PyObject *value, int wrapped) +{ + Tcl_Obj *result = NULL; + Py_ssize_t size = PyTuple_GET_SIZE(value); + if (size == 0) { + return Tcl_NewListObj(0, NULL); + } + if (!CHECK_SIZE(size, sizeof(Tcl_Obj *))) { + PyErr_SetString(PyExc_OverflowError, + wrapped ? "list is too long" : "tuple is too long"); + return NULL; + } + Tcl_Obj **argv = (Tcl_Obj **)PyMem_Malloc(((size_t)size) * sizeof(Tcl_Obj *)); + if (argv == NULL) { + PyErr_NoMemory(); + return NULL; + } + for (Py_ssize_t i = 0; i < size; i++) { + Tcl_Obj *item = AsObj(PyTuple_GET_ITEM(value, i)); + if (item == NULL) { + goto exit; + } + argv[i] = item; + } + result = Tcl_NewListObj((int)size, argv); + +exit: + PyMem_Free(argv); + return result; +} + static Tcl_Obj* AsObj(PyObject *value) { @@ -1009,28 +1031,17 @@ AsObj(PyObject *value) if (PyFloat_Check(value)) return Tcl_NewDoubleObj(PyFloat_AS_DOUBLE(value)); - if (PyTuple_Check(value) || PyList_Check(value)) { - Tcl_Obj **argv; - Py_ssize_t size, i; - - size = PySequence_Fast_GET_SIZE(value); - if (size == 0) - return Tcl_NewListObj(0, NULL); - if (!CHECK_SIZE(size, sizeof(Tcl_Obj *))) { - PyErr_SetString(PyExc_OverflowError, - PyTuple_Check(value) ? "tuple is too long" : - "list is too long"); + if (PyTuple_Check(value)) { + return TupleAsObj(value, false); + } + + if (PyList_Check(value)) { + PyObject *value_as_tuple = PyList_AsTuple(value); + if (value_as_tuple == NULL) { return NULL; } - argv = (Tcl_Obj **) PyMem_Malloc(((size_t)size) * sizeof(Tcl_Obj *)); - if (!argv) { - PyErr_NoMemory(); - return NULL; - } - for (i = 0; i < size; i++) - argv[i] = AsObj(PySequence_Fast_GET_ITEM(value,i)); - result = Tcl_NewListObj((int)size, argv); - PyMem_Free(argv); + result = TupleAsObj(value_as_tuple, true); + Py_DECREF(value_as_tuple); return result; } @@ -3212,6 +3223,7 @@ _tkinter_create_impl(PyObject *module, const char *screenName, } /*[clinic input] +@permit_long_summary _tkinter.setbusywaitinterval new_val: int @@ -3219,12 +3231,13 @@ _tkinter.setbusywaitinterval Set the busy-wait interval in milliseconds between successive calls to Tcl_DoOneEvent in a threaded Python interpreter. -It should be set to a divisor of the maximum time between frames in an animation. +It should be set to a divisor of the maximum time between frames in +an animation. [clinic start generated code]*/ static PyObject * _tkinter_setbusywaitinterval_impl(PyObject *module, int new_val) -/*[clinic end generated code: output=42bf7757dc2d0ab6 input=deca1d6f9e6dae47]*/ +/*[clinic end generated code: output=42bf7757dc2d0ab6 input=0360dd95c8bd8619]*/ { if (new_val < 0) { PyErr_SetString(PyExc_ValueError, @@ -3236,6 +3249,7 @@ _tkinter_setbusywaitinterval_impl(PyObject *module, int new_val) } /*[clinic input] +@permit_long_summary _tkinter.getbusywaitinterval -> int Return the current busy-wait interval between successive calls to Tcl_DoOneEvent in a threaded Python interpreter. @@ -3243,7 +3257,7 @@ Return the current busy-wait interval between successive calls to Tcl_DoOneEvent static int _tkinter_getbusywaitinterval_impl(PyObject *module) -/*[clinic end generated code: output=23b72d552001f5c7 input=a695878d2d576a84]*/ +/*[clinic end generated code: output=23b72d552001f5c7 input=62d5b36ddab3976b]*/ { return Tkinter_busywaitinterval; } @@ -3264,11 +3278,14 @@ static PyType_Slot Tktt_Type_slots[] = { }; static PyType_Spec Tktt_Type_spec = { - "_tkinter.tktimertoken", - sizeof(TkttObject), - 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, - Tktt_Type_slots, + .name = "_tkinter.tktimertoken", + .basicsize = sizeof(TkttObject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = Tktt_Type_slots, }; @@ -3320,11 +3337,14 @@ static PyType_Slot Tkapp_Type_slots[] = { static PyType_Spec Tkapp_Type_spec = { - "_tkinter.tkapp", - sizeof(TkappObject), - 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, - Tkapp_Type_slots, + .name = "_tkinter.tkapp", + .basicsize = sizeof(TkappObject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = Tkapp_Type_slots, }; static PyMethodDef moduleMethods[] = @@ -3461,6 +3481,11 @@ static struct PyModuleDef _tkintermodule = { PyMODINIT_FUNC PyInit__tkinter(void) { + PyABIInfo_VAR(abi_info); + if (PyABIInfo_Check(&abi_info, "_tkinter") < 0) { + return NULL; + } + PyObject *m, *uexe, *cexe; tcl_lock = PyThread_allocate_lock(); diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index be71fc9fc9c341..a97b0e0bfeefba 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -11,6 +11,7 @@ module _tracemalloc /*[clinic input] +@permit_long_summary _tracemalloc.is_tracing Return True if the tracemalloc module is tracing Python memory allocations. @@ -18,7 +19,7 @@ Return True if the tracemalloc module is tracing Python memory allocations. static PyObject * _tracemalloc_is_tracing_impl(PyObject *module) -/*[clinic end generated code: output=2d763b42601cd3ef input=af104b0a00192f63]*/ +/*[clinic end generated code: output=2d763b42601cd3ef input=cac4fc9096babeac]*/ { return PyBool_FromLong(_PyTraceMalloc_IsTracing()); } @@ -153,6 +154,7 @@ _tracemalloc_get_tracemalloc_memory_impl(PyObject *module) /*[clinic input] +@permit_long_summary _tracemalloc.get_traced_memory Get the current size and peak size of memory blocks traced by tracemalloc. @@ -162,12 +164,13 @@ Returns a tuple: (current: int, peak: int). static PyObject * _tracemalloc_get_traced_memory_impl(PyObject *module) -/*[clinic end generated code: output=5b167189adb9e782 input=61ddb5478400ff66]*/ +/*[clinic end generated code: output=5b167189adb9e782 input=b06e7a1a4914fc21]*/ { return _PyTraceMalloc_GetTracedMemory(); } /*[clinic input] +@permit_long_summary _tracemalloc.reset_peak Set the peak size of memory blocks traced by tracemalloc to the current size. @@ -178,7 +181,7 @@ Do nothing if the tracemalloc module is not tracing memory allocations. static PyObject * _tracemalloc_reset_peak_impl(PyObject *module) -/*[clinic end generated code: output=140c2870f691dbb2 input=18afd0635066e9ce]*/ +/*[clinic end generated code: output=140c2870f691dbb2 input=4103319210f46286]*/ { _PyTraceMalloc_ResetPeak(); Py_RETURN_NONE; @@ -215,6 +218,11 @@ static struct PyModuleDef module_def = { PyMODINIT_FUNC PyInit__tracemalloc(void) { + PyABIInfo_VAR(abi_info); + if (PyABIInfo_Check(&abi_info, "_tracemalloc") < 0) { + return NULL; + } + PyObject *mod = PyModule_Create(&module_def); if (mod == NULL) { return NULL; diff --git a/Modules/_typesmodule.c b/Modules/_typesmodule.c index df6b4c93cb87a6..232c6cd46d1aa8 100644 --- a/Modules/_typesmodule.c +++ b/Modules/_typesmodule.c @@ -2,6 +2,7 @@ #include "Python.h" #include "pycore_descrobject.h" // _PyMethodWrapper_Type +#include "pycore_lazyimportobject.h" // PyLazyImport_Type #include "pycore_namespace.h" // _PyNamespace_Type #include "pycore_object.h" // _PyNone_Type, _PyNotImplemented_Type #include "pycore_unionobject.h" // _PyUnion_Type @@ -35,6 +36,7 @@ _types_exec(PyObject *m) EXPORT_STATIC_TYPE("GetSetDescriptorType", PyGetSetDescr_Type); // LambdaType is the same as FunctionType EXPORT_STATIC_TYPE("LambdaType", PyFunction_Type); + EXPORT_STATIC_TYPE("LazyImportType", PyLazyImport_Type); EXPORT_STATIC_TYPE("MappingProxyType", PyDictProxy_Type); EXPORT_STATIC_TYPE("MemberDescriptorType", PyMemberDescr_Type); EXPORT_STATIC_TYPE("MethodDescriptorType", PyMethodDescr_Type); @@ -52,6 +54,7 @@ _types_exec(PyObject *m) } static struct PyModuleDef_Slot _typesmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _types_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_typingmodule.c b/Modules/_typingmodule.c index e51279c808a2e1..9f698d3e48d5f0 100644 --- a/Modules/_typingmodule.c +++ b/Modules/_typingmodule.c @@ -74,6 +74,7 @@ _typing_exec(PyObject *m) } static struct PyModuleDef_Slot _typingmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _typing_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_weakref.c b/Modules/_weakref.c index ecaa08ff60f203..623252728554f7 100644 --- a/Modules/_weakref.c +++ b/Modules/_weakref.c @@ -162,6 +162,7 @@ weakref_exec(PyObject *module) } static struct PyModuleDef_Slot weakref_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, weakref_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_winapi.c b/Modules/_winapi.c index b4cfbebcb1bb5e..369a7400eb63b9 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -49,6 +49,13 @@ #include <crtdbg.h> #include "winreparse.h" +// PSAPI_VERSION=2 redirects GetProcessMemoryInfo() to +// K32GetProcessMemoryInfo() in kernel32.dll, so we don't need to link +// psapi.lib. See: +// https://learn.microsoft.com/windows/win32/api/psapi/nf-psapi-getprocessmemoryinfo +#define PSAPI_VERSION 2 +#include <psapi.h> // GetProcessMemoryInfo() + #if defined(MS_WIN32) && !defined(MS_WIN64) #define HANDLE_TO_PYNUM(handle) \ PyLong_FromUnsignedLong((unsigned long) handle) @@ -1187,8 +1194,10 @@ gethandlelist(PyObject *mapping, const char *name, Py_ssize_t *size) } ret = PyMem_Malloc(*size); - if (ret == NULL) + if (ret == NULL) { + PyErr_NoMemory(); goto cleanup; + } for (i = 0; i < PySequence_Fast_GET_SIZE(value_fast); i++) { ret[i] = PYNUM_TO_HANDLE(PySequence_Fast_GET_ITEM(value_fast, i)); @@ -1271,6 +1280,7 @@ getattributelist(PyObject *obj, const char *name, AttributeList *attribute_list) attribute_list->attribute_list = PyMem_Malloc(attribute_list_size); if (attribute_list->attribute_list == NULL) { ret = -1; + PyErr_NoMemory(); goto cleanup; } @@ -1920,7 +1930,6 @@ static PyObject * _winapi_PeekNamedPipe_impl(PyObject *module, HANDLE handle, int size) /*[clinic end generated code: output=d0c3e29e49d323dd input=c7aa53bfbce69d70]*/ { - PyObject *buf = NULL; DWORD nread, navail, nleft; BOOL ret; @@ -1930,20 +1939,26 @@ _winapi_PeekNamedPipe_impl(PyObject *module, HANDLE handle, int size) } if (size) { - buf = PyBytes_FromStringAndSize(NULL, size); - if (!buf) + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; + } + char *buf = PyBytesWriter_GetData(writer); + Py_BEGIN_ALLOW_THREADS - ret = PeekNamedPipe(handle, PyBytes_AS_STRING(buf), size, &nread, + ret = PeekNamedPipe(handle, buf, size, &nread, &navail, &nleft); Py_END_ALLOW_THREADS if (!ret) { - Py_DECREF(buf); + PyBytesWriter_Discard(writer); return PyErr_SetExcFromWindowsErr(PyExc_OSError, 0); } - if (_PyBytes_Resize(&buf, nread)) + + PyObject *res = PyBytesWriter_FinishWithSize(writer, nread); + if (res == NULL) { return NULL; - return Py_BuildValue("NII", buf, navail, nleft); + } + return Py_BuildValue("NII", res, navail, nleft); } else { Py_BEGIN_ALLOW_THREADS @@ -2977,6 +2992,158 @@ _winapi_CopyFile2_impl(PyObject *module, LPCWSTR existing_file_name, Py_RETURN_NONE; } +/*[clinic input] +_winapi.RegisterEventSource -> HANDLE + + unc_server_name: LPCWSTR(accept={str, NoneType}) + The UNC name of the server on which the event source should be registered. + If None, registers the event source on the local computer. + source_name: LPCWSTR + The name of the event source to register. + / + +Retrieves a registered handle to the specified event log. +[clinic start generated code]*/ + +static HANDLE +_winapi_RegisterEventSource_impl(PyObject *module, LPCWSTR unc_server_name, + LPCWSTR source_name) +/*[clinic end generated code: output=e376c8950a89ae8f input=9d01059ac2156d0c]*/ +{ + HANDLE handle; + + Py_BEGIN_ALLOW_THREADS + handle = RegisterEventSourceW(unc_server_name, source_name); + Py_END_ALLOW_THREADS + + if (handle == NULL) { + PyErr_SetFromWindowsErr(0); + return INVALID_HANDLE_VALUE; + } + + return handle; +} + +/*[clinic input] +_winapi.DeregisterEventSource + + handle: HANDLE + The handle to the event log to be deregistered. + / + +Closes the specified event log. +[clinic start generated code]*/ + +static PyObject * +_winapi_DeregisterEventSource_impl(PyObject *module, HANDLE handle) +/*[clinic end generated code: output=7387ff34c7358bce input=947593cf67641f16]*/ +{ + BOOL success; + + Py_BEGIN_ALLOW_THREADS + success = DeregisterEventSource(handle); + Py_END_ALLOW_THREADS + + if (!success) { + return PyErr_SetFromWindowsErr(0); + } + + Py_RETURN_NONE; +} + +/*[clinic input] +_winapi.ReportEvent + + handle: HANDLE + The handle to the event log. + type: unsigned_short(bitwise=False) + The type of event being reported. + category: unsigned_short(bitwise=False) + The event category. + event_id: unsigned_int(bitwise=False) + The event identifier. + string: LPCWSTR + A string to be inserted into the event message. + / + +Writes an entry at the end of the specified event log. +[clinic start generated code]*/ + +static PyObject * +_winapi_ReportEvent_impl(PyObject *module, HANDLE handle, + unsigned short type, unsigned short category, + unsigned int event_id, LPCWSTR string) +/*[clinic end generated code: output=4281230b70a2470a input=8fb3385b8e7a6d3d]*/ +{ + BOOL success; + + Py_BEGIN_ALLOW_THREADS + success = ReportEventW(handle, type, category, event_id, NULL, 1, 0, + &string, NULL); + Py_END_ALLOW_THREADS + + if (!success) { + return PyErr_SetFromWindowsErr(0); + } + + Py_RETURN_NONE; +} + + +/*[clinic input] +_winapi.GetProcessMemoryInfo + handle: HANDLE + / + +Return the memory usage of the given process handle as a dict. +[clinic start generated code]*/ + +static PyObject * +_winapi_GetProcessMemoryInfo_impl(PyObject *module, HANDLE handle) +/*[clinic end generated code: output=00a5d09732e84120 input=5b90ad61cdc68d2a]*/ +{ + PROCESS_MEMORY_COUNTERS pmc; + if (!GetProcessMemoryInfo(handle, &pmc, sizeof(pmc))) { + return PyErr_SetFromWindowsErr(0); + } + + PyObject *result = PyDict_New(); + if (result == NULL) { + return NULL; + } + +#define ADD(ATTR) \ + do { \ + PyObject *obj = PyLong_FromSize_t(pmc.ATTR); \ + if (obj == NULL) { \ + goto error; \ + } \ + if (PyDict_SetItemString(result, #ATTR, obj) < 0) { \ + Py_DECREF(obj); \ + goto error; \ + } \ + Py_DECREF(obj); \ + } while (0) + + ADD(PageFaultCount); + ADD(PeakWorkingSetSize); + ADD(WorkingSetSize); + ADD(QuotaPeakPagedPoolUsage); + ADD(QuotaPagedPoolUsage); + ADD(QuotaPeakNonPagedPoolUsage); + ADD(QuotaNonPagedPoolUsage); + ADD(PagefileUsage); + ADD(PeakPagefileUsage); + +#undef ADD + + return result; + +error: + Py_DECREF(result); + return NULL; +} + static PyMethodDef winapi_functions[] = { _WINAPI_CLOSEHANDLE_METHODDEF @@ -2989,6 +3156,7 @@ static PyMethodDef winapi_functions[] = { _WINAPI_CREATEPIPE_METHODDEF _WINAPI_CREATEPROCESS_METHODDEF _WINAPI_CREATEJUNCTION_METHODDEF + _WINAPI_DEREGISTEREVENTSOURCE_METHODDEF _WINAPI_DUPLICATEHANDLE_METHODDEF _WINAPI_EXITPROCESS_METHODDEF _WINAPI_GETCURRENTPROCESS_METHODDEF @@ -3005,6 +3173,8 @@ static PyMethodDef winapi_functions[] = { _WINAPI_OPENMUTEXW_METHODDEF _WINAPI_OPENPROCESS_METHODDEF _WINAPI_PEEKNAMEDPIPE_METHODDEF + _WINAPI_REGISTEREVENTSOURCE_METHODDEF + _WINAPI_REPORTEVENT_METHODDEF _WINAPI_LCMAPSTRINGEX_METHODDEF _WINAPI_READFILE_METHODDEF _WINAPI_RELEASEMUTEX_METHODDEF @@ -3025,6 +3195,7 @@ static PyMethodDef winapi_functions[] = { _WINAPI__MIMETYPES_READ_WINDOWS_REGISTRY_METHODDEF _WINAPI_NEEDCURRENTDIRECTORYFOREXEPATH_METHODDEF _WINAPI_COPYFILE2_METHODDEF + _WINAPI_GETPROCESSMEMORYINFO_METHODDEF {NULL, NULL} }; @@ -3068,15 +3239,18 @@ static int winapi_exec(PyObject *m) WINAPI_CONSTANT(F_DWORD, ERROR_MORE_DATA); WINAPI_CONSTANT(F_DWORD, ERROR_NETNAME_DELETED); WINAPI_CONSTANT(F_DWORD, ERROR_NO_SYSTEM_RESOURCES); - WINAPI_CONSTANT(F_DWORD, ERROR_MORE_DATA); - WINAPI_CONSTANT(F_DWORD, ERROR_NETNAME_DELETED); WINAPI_CONSTANT(F_DWORD, ERROR_NO_DATA); - WINAPI_CONSTANT(F_DWORD, ERROR_NO_SYSTEM_RESOURCES); WINAPI_CONSTANT(F_DWORD, ERROR_OPERATION_ABORTED); WINAPI_CONSTANT(F_DWORD, ERROR_PIPE_BUSY); WINAPI_CONSTANT(F_DWORD, ERROR_PIPE_CONNECTED); WINAPI_CONSTANT(F_DWORD, ERROR_PRIVILEGE_NOT_HELD); WINAPI_CONSTANT(F_DWORD, ERROR_SEM_TIMEOUT); + WINAPI_CONSTANT(F_DWORD, EVENTLOG_SUCCESS); + WINAPI_CONSTANT(F_DWORD, EVENTLOG_AUDIT_FAILURE); + WINAPI_CONSTANT(F_DWORD, EVENTLOG_AUDIT_SUCCESS); + WINAPI_CONSTANT(F_DWORD, EVENTLOG_ERROR_TYPE); + WINAPI_CONSTANT(F_DWORD, EVENTLOG_INFORMATION_TYPE); + WINAPI_CONSTANT(F_DWORD, EVENTLOG_WARNING_TYPE); WINAPI_CONSTANT(F_DWORD, FILE_FLAG_FIRST_PIPE_INSTANCE); WINAPI_CONSTANT(F_DWORD, FILE_FLAG_OVERLAPPED); WINAPI_CONSTANT(F_DWORD, FILE_GENERIC_READ); @@ -3118,6 +3292,7 @@ static int winapi_exec(PyObject *m) WINAPI_CONSTANT(F_DWORD, PROCESS_ALL_ACCESS); WINAPI_CONSTANT(F_DWORD, SYNCHRONIZE); WINAPI_CONSTANT(F_DWORD, PROCESS_DUP_HANDLE); + WINAPI_CONSTANT(F_DWORD, PROCESS_QUERY_LIMITED_INFORMATION); WINAPI_CONSTANT(F_DWORD, SEC_COMMIT); WINAPI_CONSTANT(F_DWORD, SEC_IMAGE); WINAPI_CONSTANT(F_DWORD, SEC_LARGE_PAGES); @@ -3220,6 +3395,7 @@ static int winapi_exec(PyObject *m) } static PyModuleDef_Slot winapi_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, winapi_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_xxtestfuzz/README.rst b/Modules/_xxtestfuzz/README.rst index 68d5d589d2a551..8843356d575570 100644 --- a/Modules/_xxtestfuzz/README.rst +++ b/Modules/_xxtestfuzz/README.rst @@ -1,20 +1,22 @@ Fuzz Tests for CPython ====================== -These fuzz tests are designed to be included in Google's `oss-fuzz`_ project. +These fuzz tests are designed to be included in Google's `OSS-Fuzz`_ project. -oss-fuzz works against a library exposing a function of the form +OSS-Fuzz works against a library exposing a function of the form ``int LLVMFuzzerTestOneInput(const uint8_t* data, size_t length)``. We provide -that library (``fuzzer.c``), and include a ``_fuzz`` module for testing with +that library (``fuzzer.c``), and include a ``_xxtestfuzz`` module for testing with some toy values -- no fuzzing occurs in Python's test suite. -oss-fuzz will regularly pull from CPython, discover all the tests in +OSS-Fuzz will regularly pull from CPython, discover all the tests in ``fuzz_tests.txt``, and run them -- so adding a new test here means it will -automatically be run in oss-fuzz, while also being smoke-tested as part of +automatically be run in OSS-Fuzz, while also being smoke-tested as part of CPython's test suite. -In addition, the tests are run on GitHub Actions using CIFuzz for PRs to the -main branch changing relevant files. +In addition, the tests are run on GitHub Actions using `CIFuzz +<https://google.github.io/oss-fuzz/getting-started/continuous-integration/>`_ +for PRs to the ``main`` branch changing relevant files. + Adding a new fuzz test ---------------------- @@ -28,7 +30,6 @@ In ``fuzzer.c``, add a function to be run:: return 0; } - And invoke it from ``LLVMFuzzerTestOneInput``:: #if !defined(_Py_FUZZ_ONE) || defined(_Py_FUZZ_$fuzz_test_name) @@ -37,7 +38,7 @@ And invoke it from ``LLVMFuzzerTestOneInput``:: Don't forget to replace ``$fuzz_test_name`` with your actual test name. -``LLVMFuzzerTestOneInput`` will run in oss-fuzz, with each test in +``LLVMFuzzerTestOneInput`` will run in OSS-Fuzz, with each test in ``fuzz_tests.txt`` run separately. Seed data (corpus) for the test can be provided in a subfolder called @@ -45,17 +46,19 @@ Seed data (corpus) for the test can be provided in a subfolder called of good input samples allows the fuzzer to more easily explore a diverse set of paths and provides a better base to find buggy input from. -Dictionaries of tokens (see oss-fuzz documentation for more details) can -be placed in the ``dictionaries`` folder with the name of the test. +Dictionaries of tokens (see the `libFuzzer documentation +<https://llvm.org/docs/LibFuzzer.html#dictionaries>`_ for more information) can +be placed in the ``dictionaries/`` folder with the name of the test. For example, ``dictionaries/fuzz_json_loads.dict`` contains JSON tokens to guide the fuzzer. + What makes a good fuzz test --------------------------- Libraries written in C that might handle untrusted data are worthwhile. The -more complex the logic (e.g. parsing), the more likely this is to be a useful +more complex the logic (e.g., parsing), the more likely this is to be a useful fuzz test. See the existing examples for reference, and refer to the -`oss-fuzz`_ docs. +`OSS-Fuzz`_ docs. -.. _oss-fuzz: https://github.com/google/oss-fuzz +.. _OSS-Fuzz: https://github.com/google/oss-fuzz diff --git a/Modules/_xxtestfuzz/_xxtestfuzz.c b/Modules/_xxtestfuzz/_xxtestfuzz.c index 0e0ca5f95fa449..a2f01eb2490135 100644 --- a/Modules/_xxtestfuzz/_xxtestfuzz.c +++ b/Modules/_xxtestfuzz/_xxtestfuzz.c @@ -28,7 +28,10 @@ static PyMethodDef module_methods[] = { {NULL}, }; +PyABIInfo_VAR(abi_info); + static PyModuleDef_Slot module_slots[] = { + {Py_mod_abi, &abi_info}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL}, }; diff --git a/Modules/_xxtestfuzz/dictionaries/fuzz_pycompile.dict b/Modules/_xxtestfuzz/dictionaries/fuzz_pycompile.dict index c6a44d946284ef..322d8180f7baa5 100644 --- a/Modules/_xxtestfuzz/dictionaries/fuzz_pycompile.dict +++ b/Modules/_xxtestfuzz/dictionaries/fuzz_pycompile.dict @@ -52,7 +52,9 @@ # whitespace " " +"\\t" ":\\n " +"\\\\n" # type signatures and functions "-> " @@ -88,6 +90,8 @@ # variable names "x" "y" +"_" +"*x" # strings "r'x'" @@ -98,12 +102,24 @@ "br\"x\"" +"u\"x\"" + "f'{x + 5}'" "f\"{x + 5}\"" +"f'{s!r}'" +"f'{s!s}'" +"f'{s!a}'" +"f'{x=}'" + +"t'{s + 5}'" +"t\"{s + 5}\"" +"tr's'" +"rt\"s\"" "'''" "\"\"\"" +"\\N" "\\u" "\\x" @@ -131,6 +147,7 @@ "while " "try: " "except " +"except* " "finally: " "with " "lambda " @@ -148,6 +165,10 @@ "in " "is " "class " +"match " +"case " +"type " +"lazy " # shebangs and encodings "#!" diff --git a/Modules/_xxtestfuzz/fuzzer.c b/Modules/_xxtestfuzz/fuzzer.c index a04f1412eefda1..02afb01d3731fb 100644 --- a/Modules/_xxtestfuzz/fuzzer.c +++ b/Modules/_xxtestfuzz/fuzzer.c @@ -10,8 +10,8 @@ See the source code for LLVMFuzzerTestOneInput for details. */ -#ifndef Py_BUILD_CORE -# define Py_BUILD_CORE 1 +#ifndef Py_BUILD_CORE_MODULE +# define Py_BUILD_CORE_MODULE 1 #endif #include <Python.h> @@ -38,23 +38,18 @@ static int fuzz_builtin_float(const char* data, size_t size) { static int fuzz_builtin_int(const char* data, size_t size) { /* Ignore test cases with very long ints to avoid timeouts int("9" * 1000000) is not a very interesting test caase */ - if (size > MAX_INT_TEST_SIZE) { + if (size < 1 || size > MAX_INT_TEST_SIZE) { return 0; } - /* Pick a random valid base. (When the fuzzed function takes extra - parameters, it's somewhat normal to hash the input to generate those - parameters. We want to exercise all code paths, so we do so here.) */ - int base = Py_HashBuffer(data, size) % 37; + // Use the first byte to pick a base + int base = ((unsigned char) data[0]) % 37; if (base == 1) { // 1 is the only number between 0 and 36 that is not a valid base. base = 0; } - if (base == -1) { - return 0; // An error occurred, bail early. - } - if (base < 0) { - base = -base; - } + + data += 1; + size -= 1; PyObject* s = PyUnicode_FromStringAndSize(data, size); if (s == NULL) { @@ -133,6 +128,10 @@ static int fuzz_struct_unpack(const char* data, size_t size) { if (unpacked == NULL && PyErr_ExceptionMatches(PyExc_SystemError)) { PyErr_Clear(); } + /* Ignore any ValueError, these are triggered by non-ASCII format. */ + if (unpacked == NULL && PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_Clear(); + } /* Ignore any struct.error exceptions, these can be caused by invalid formats or incomplete buffers both of which are common. */ if (unpacked == NULL && PyErr_ExceptionMatches(struct_error)) { @@ -428,6 +427,7 @@ static int fuzz_ast_literal_eval(const char* data, size_t size) { PyErr_ExceptionMatches(PyExc_TypeError) || PyErr_ExceptionMatches(PyExc_SyntaxError) || PyErr_ExceptionMatches(PyExc_MemoryError) || + PyErr_ExceptionMatches(PyExc_OverflowError) || PyErr_ExceptionMatches(PyExc_RecursionError)) ) { PyErr_Clear(); @@ -516,8 +516,8 @@ static int fuzz_pycompile(const char* data, size_t size) { return 0; } - // Need 2 bytes for parameter selection - if (size < 2) { + // Need 3 bytes for parameter selection + if (size < 3) { return 0; } @@ -529,25 +529,39 @@ static int fuzz_pycompile(const char* data, size_t size) { unsigned char optimize_idx = (unsigned char) data[1]; int optimize = optimize_vals[optimize_idx % NUM_OPTIMIZE_VALS]; + // Use third byte to determine compiler flags to use. + unsigned char flags_byte = (unsigned char) data[2]; + PyCompilerFlags flags = _PyCompilerFlags_INIT; + if (flags_byte & 0x01) { + flags.cf_flags |= PyCF_DONT_IMPLY_DEDENT; + } + if (flags_byte & 0x02) { + flags.cf_flags |= PyCF_ONLY_AST; + } + if (flags_byte & 0x04) { + flags.cf_flags |= PyCF_IGNORE_COOKIE; + } + if (flags_byte & 0x08) { + flags.cf_flags |= PyCF_TYPE_COMMENTS; + } + if (flags_byte & 0x10) { + flags.cf_flags |= PyCF_ALLOW_TOP_LEVEL_AWAIT; + } + if (flags_byte & 0x20) { + flags.cf_flags |= PyCF_ALLOW_INCOMPLETE_INPUT; + } + if (flags_byte & 0x40) { + flags.cf_flags |= PyCF_OPTIMIZED_AST; + } + char pycompile_scratch[MAX_PYCOMPILE_TEST_SIZE]; // Create a NUL-terminated C string from the remaining input - memcpy(pycompile_scratch, data + 2, size - 2); + memcpy(pycompile_scratch, data + 3, size - 3); // Put a NUL terminator just after the copied data. (Space was reserved already.) - pycompile_scratch[size - 2] = '\0'; - - // XXX: instead of always using NULL for the `flags` value to - // `Py_CompileStringExFlags`, there are many flags that conditionally - // change parser behavior: - // - // #define PyCF_TYPE_COMMENTS 0x1000 - // #define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000 - // #define PyCF_ONLY_AST 0x0400 - // - // It would be good to test various combinations of these, too. - PyCompilerFlags *flags = NULL; - - PyObject *result = Py_CompileStringExFlags(pycompile_scratch, "<fuzz input>", start, flags, optimize); + pycompile_scratch[size - 3] = '\0'; + + PyObject *result = Py_CompileStringExFlags(pycompile_scratch, "<fuzz input>", start, &flags, optimize); if (result == NULL) { /* Compilation failed, most likely from a syntax error. If it was a SystemError we abort. There's no non-bug reason to raise a diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 5c5383d260a040..eaffd020ed97c0 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -272,6 +272,7 @@ zoneinfo_new_instance(zoneinfo_state *state, PyTypeObject *type, PyObject *key) goto cleanup; error: + assert(PyErr_Occurred()); Py_CLEAR(self); cleanup: if (file_obj != NULL) { @@ -292,16 +293,11 @@ static PyObject * get_weak_cache(zoneinfo_state *state, PyTypeObject *type) { if (type == state->ZoneInfoType) { + Py_INCREF(state->ZONEINFO_WEAK_CACHE); return state->ZONEINFO_WEAK_CACHE; } else { - PyObject *cache = - PyObject_GetAttrString((PyObject *)type, "_weak_cache"); - // We are assuming that the type lives at least as long as the function - // that calls get_weak_cache, and that it holds a reference to the - // cache, so we'll return a "borrowed reference". - Py_XDECREF(cache); - return cache; + return PyObject_GetAttrString((PyObject *)type, "_weak_cache"); } } @@ -326,8 +322,12 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key) } PyObject *weak_cache = get_weak_cache(state, type); + if (weak_cache == NULL) { + return NULL; + } instance = PyObject_CallMethod(weak_cache, "get", "O", key, Py_None); if (instance == NULL) { + Py_DECREF(weak_cache); return NULL; } @@ -335,19 +335,31 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key) Py_DECREF(instance); PyObject *tmp = zoneinfo_new_instance(state, type, key); if (tmp == NULL) { + Py_DECREF(weak_cache); return NULL; } + ((PyZoneInfo_ZoneInfo *)tmp)->source = SOURCE_CACHE; instance = PyObject_CallMethod(weak_cache, "setdefault", "OO", key, tmp); Py_DECREF(tmp); if (instance == NULL) { + Py_DECREF(weak_cache); return NULL; } - ((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE; + } + + if (!PyObject_TypeCheck(instance, type)) { + PyErr_Format(PyExc_RuntimeError, + "Unexpected instance of %T in %s weak cache for key %R", + instance, _PyType_Name(type), key); + Py_DECREF(instance); + Py_DECREF(weak_cache); + return NULL; } update_strong_cache(state, type, key, instance); + Py_DECREF(weak_cache); return instance; } @@ -447,6 +459,7 @@ zoneinfo_ZoneInfo_from_file_impl(PyTypeObject *type, PyTypeObject *cls, return (PyObject *)self; error: + assert(PyErr_Occurred()); Py_XDECREF(file_repr); Py_XDECREF(self); return NULL; @@ -497,6 +510,9 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls, { zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); PyObject *weak_cache = get_weak_cache(state, type); + if (weak_cache == NULL) { + return NULL; + } if (only_keys == NULL || only_keys == Py_None) { PyObject *rv = PyObject_CallMethod(weak_cache, "clear", NULL); @@ -510,12 +526,14 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls, PyObject *item = NULL; PyObject *pop = PyUnicode_FromString("pop"); if (pop == NULL) { + Py_DECREF(weak_cache); return NULL; } PyObject *iter = PyObject_GetIter(only_keys); if (iter == NULL) { Py_DECREF(pop); + Py_DECREF(weak_cache); return NULL; } @@ -540,6 +558,7 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls, Py_DECREF(pop); } + Py_DECREF(weak_cache); if (PyErr_Occurred()) { return NULL; } @@ -548,6 +567,7 @@ zoneinfo_ZoneInfo_clear_cache_impl(PyTypeObject *type, PyTypeObject *cls, } /*[clinic input] +@permit_long_summary zoneinfo.ZoneInfo.utcoffset cls: defining_class @@ -560,7 +580,7 @@ Retrieve a timedelta representing the UTC offset in a zone at the given datetime static PyObject * zoneinfo_ZoneInfo_utcoffset_impl(PyObject *self, PyTypeObject *cls, PyObject *dt) -/*[clinic end generated code: output=b71016c319ba1f91 input=2bb6c5364938f19c]*/ +/*[clinic end generated code: output=b71016c319ba1f91 input=8ce0dc2d179f01c5]*/ { zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); _ttinfo *tti = find_ttinfo(state, PyZoneInfo_ZoneInfo_CAST(self), dt); @@ -571,6 +591,7 @@ zoneinfo_ZoneInfo_utcoffset_impl(PyObject *self, PyTypeObject *cls, } /*[clinic input] +@permit_long_summary zoneinfo.ZoneInfo.dst cls: defining_class @@ -582,7 +603,7 @@ Retrieve a timedelta representing the amount of DST applied in a zone at the giv static PyObject * zoneinfo_ZoneInfo_dst_impl(PyObject *self, PyTypeObject *cls, PyObject *dt) -/*[clinic end generated code: output=cb6168d7723a6ae6 input=2167fb80cf8645c6]*/ +/*[clinic end generated code: output=cb6168d7723a6ae6 input=22b2abdf9388423c]*/ { zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); _ttinfo *tti = find_ttinfo(state, PyZoneInfo_ZoneInfo_CAST(self), dt); @@ -593,6 +614,7 @@ zoneinfo_ZoneInfo_dst_impl(PyObject *self, PyTypeObject *cls, PyObject *dt) } /*[clinic input] +@permit_long_summary zoneinfo.ZoneInfo.tzname cls: defining_class @@ -605,7 +627,7 @@ Retrieve a string containing the abbreviation for the time zone that applies in static PyObject * zoneinfo_ZoneInfo_tzname_impl(PyObject *self, PyTypeObject *cls, PyObject *dt) -/*[clinic end generated code: output=3b6ae6c3053ea75a input=15a59a4f92ed1f1f]*/ +/*[clinic end generated code: output=3b6ae6c3053ea75a input=0882926c4e95a1e2]*/ { zoneinfo_state *state = zoneinfo_get_state_by_cls(cls); _ttinfo *tti = find_ttinfo(state, PyZoneInfo_ZoneInfo_CAST(self), dt); @@ -969,7 +991,7 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) } if (!PyTuple_CheckExact(data_tuple)) { - PyErr_Format(PyExc_TypeError, "Invalid data result type: %r", + PyErr_Format(PyExc_TypeError, "Invalid data result type: %R", data_tuple); goto error; } @@ -1023,10 +1045,12 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) self->trans_list_utc = PyMem_Malloc(self->num_transitions * sizeof(int64_t)); if (self->trans_list_utc == NULL) { + PyErr_NoMemory(); goto error; } trans_idx = PyMem_Malloc(self->num_transitions * sizeof(Py_ssize_t)); if (trans_idx == NULL) { + PyErr_NoMemory(); goto error; } @@ -1051,7 +1075,7 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) } trans_idx[i] = (size_t)cur_trans_idx; - if (trans_idx[i] > self->num_ttinfos) { + if (trans_idx[i] >= self->num_ttinfos) { PyErr_Format( PyExc_ValueError, "Invalid transition index found while reading TZif: %zd", @@ -1066,6 +1090,7 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) isdst = PyMem_Malloc(self->num_ttinfos * sizeof(unsigned char)); if (utcoff == NULL || isdst == NULL) { + PyErr_NoMemory(); goto error; } for (size_t i = 0; i < self->num_ttinfos; ++i) { @@ -1095,6 +1120,7 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) dstoff = PyMem_Calloc(self->num_ttinfos, sizeof(long)); if (dstoff == NULL) { + PyErr_NoMemory(); goto error; } @@ -1111,6 +1137,7 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) // Build _ttinfo objects from utcoff, dstoff and abbr self->_ttinfos = PyMem_Malloc(self->num_ttinfos * sizeof(_ttinfo)); if (self->_ttinfos == NULL) { + PyErr_NoMemory(); goto error; } for (size_t i = 0; i < self->num_ttinfos; ++i) { @@ -1131,6 +1158,7 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) self->trans_ttinfos = PyMem_Calloc(self->num_transitions, sizeof(_ttinfo *)); if (self->trans_ttinfos == NULL) { + PyErr_NoMemory(); goto error; } for (size_t i = 0; i < self->num_transitions; ++i) { @@ -1649,9 +1677,11 @@ parse_tz_str(zoneinfo_state *state, PyObject *tz_str_obj, _tzrule *out) p++; if (parse_transition_rule(&p, transitions[i])) { - PyErr_Format(PyExc_ValueError, - "Malformed transition rule in TZ string: %R", - tz_str_obj); + if (!PyErr_ExceptionMatches(PyExc_MemoryError)) { + PyErr_Format(PyExc_ValueError, + "Malformed transition rule in TZ string: %R", + tz_str_obj); + } goto error; } } @@ -1851,6 +1881,7 @@ parse_transition_rule(const char **p, TransitionRuleType **out) CalendarRule *rv = PyMem_Calloc(1, sizeof(CalendarRule)); if (rv == NULL) { + PyErr_NoMemory(); return -1; } @@ -1882,6 +1913,7 @@ parse_transition_rule(const char **p, TransitionRuleType **out) DayRule *rv = PyMem_Calloc(1, sizeof(DayRule)); if (rv == NULL) { + PyErr_NoMemory(); return -1; } @@ -2049,7 +2081,7 @@ utcoff_to_dstoff(size_t *trans_idx, long *utcoffs, long *dstoffs, dstoff = utcoff - utcoffs[comp_idx]; } - if (!dstoff && idx < (num_ttinfos - 1)) { + if (!dstoff && idx < (num_ttinfos - 1) && i + 1 < num_transitions) { comp_idx = trans_idx[i + 1]; // If the following transition is also DST and we couldn't find @@ -2115,6 +2147,7 @@ ts_to_local(size_t *trans_idx, int64_t *trans_utc, long *utcoff, for (size_t i = 0; i < 2; ++i) { trans_local[i] = PyMem_Malloc(num_transitions * sizeof(int64_t)); if (trans_local[i] == NULL) { + PyErr_NoMemory(); return -1; } @@ -2762,6 +2795,7 @@ zoneinfomodule_exec(PyObject *m) } static PyModuleDef_Slot zoneinfomodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, zoneinfomodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_zstd/_zstdmodule.c b/Modules/_zstd/_zstdmodule.c index 8af6156a0da575..94246dd93b17de 100644 --- a/Modules/_zstd/_zstdmodule.c +++ b/Modules/_zstd/_zstdmodule.c @@ -460,6 +460,7 @@ _zstd_get_param_bounds_impl(PyObject *module, int parameter, int is_compress) } /*[clinic input] +@permit_long_summary _zstd.get_frame_size frame_buffer: Py_buffer @@ -471,7 +472,7 @@ Get the size of a Zstandard frame, including the header and optional checksum. static PyObject * _zstd_get_frame_size_impl(PyObject *module, Py_buffer *frame_buffer) -/*[clinic end generated code: output=a7384c2f8780f442 input=3b9f73f8c8129d38]*/ +/*[clinic end generated code: output=a7384c2f8780f442 input=aac83b33045b5f43]*/ { size_t frame_size; @@ -534,6 +535,7 @@ _zstd_get_frame_info_impl(PyObject *module, Py_buffer *frame_buffer) } /*[clinic input] +@permit_long_summary _zstd.set_parameter_types c_parameter_type: object(subclass_of='&PyType_Type') @@ -547,7 +549,7 @@ Set CompressionParameter and DecompressionParameter types for validity check. static PyObject * _zstd_set_parameter_types_impl(PyObject *module, PyObject *c_parameter_type, PyObject *d_parameter_type) -/*[clinic end generated code: output=f3313b1294f19502 input=75d7a953580fae5f]*/ +/*[clinic end generated code: output=f3313b1294f19502 input=0529e918dfe54863]*/ { _zstd_state* mod_state = get_zstd_state(module); @@ -742,6 +744,7 @@ _zstd_free(void *module) } static struct PyModuleDef_Slot _zstd_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _zstd_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/_zstd/buffer.h b/Modules/_zstd/buffer.h index 0ac7bcb4ddc416..807c72c80dde8b 100644 --- a/Modules/_zstd/buffer.h +++ b/Modules/_zstd/buffer.h @@ -16,8 +16,8 @@ static inline int _OutputBuffer_InitAndGrow(_BlocksOutputBuffer *buffer, ZSTD_outBuffer *ob, Py_ssize_t max_length) { - /* Ensure .list was set to NULL */ - assert(buffer->list == NULL); + /* Ensure .writer was set to NULL */ + assert(buffer->writer == NULL); Py_ssize_t res = _BlocksOutputBuffer_InitAndGrow(buffer, max_length, &ob->dst); @@ -39,8 +39,8 @@ _OutputBuffer_InitWithSize(_BlocksOutputBuffer *buffer, ZSTD_outBuffer *ob, { Py_ssize_t block_size; - /* Ensure .list was set to NULL */ - assert(buffer->list == NULL); + /* Ensure .writer was set to NULL */ + assert(buffer->writer == NULL); /* Get block size */ if (0 <= max_length && max_length < init_size) { diff --git a/Modules/_zstd/clinic/compressor.c.h b/Modules/_zstd/clinic/compressor.c.h index 4f8d93fd9e867c..6775ba4826a652 100644 --- a/Modules/_zstd/clinic/compressor.c.h +++ b/Modules/_zstd/clinic/compressor.c.h @@ -21,8 +21,8 @@ PyDoc_STRVAR(_zstd_ZstdCompressor_new__doc__, " zstd_dict\n" " A ZstdDict object, a pre-trained Zstandard dictionary.\n" "\n" -"Thread-safe at method level. For one-shot compression, use the compress()\n" -"function instead."); +"Thread-safe at method level. For one-shot compression, use the\n" +"compress() function instead."); static PyObject * _zstd_ZstdCompressor_new_impl(PyTypeObject *type, PyObject *level, @@ -105,9 +105,9 @@ PyDoc_STRVAR(_zstd_ZstdCompressor_compress__doc__, " Can be these 3 values ZstdCompressor.CONTINUE,\n" " ZstdCompressor.FLUSH_BLOCK, ZstdCompressor.FLUSH_FRAME\n" "\n" -"Return a chunk of compressed data if possible, or b\'\' otherwise. When you have\n" -"finished providing data to the compressor, call the flush() method to finish\n" -"the compression process."); +"Return a chunk of compressed data if possible, or b\'\' otherwise.\n" +"When you have finished providing data to the compressor, call the\n" +"flush() method to finish the compression process."); #define _ZSTD_ZSTDCOMPRESSOR_COMPRESS_METHODDEF \ {"compress", _PyCFunction_CAST(_zstd_ZstdCompressor_compress), METH_FASTCALL|METH_KEYWORDS, _zstd_ZstdCompressor_compress__doc__}, @@ -189,9 +189,9 @@ PyDoc_STRVAR(_zstd_ZstdCompressor_flush__doc__, " Can be these 2 values ZstdCompressor.FLUSH_FRAME,\n" " ZstdCompressor.FLUSH_BLOCK\n" "\n" -"Flush any remaining data left in internal buffers. Since Zstandard data\n" -"consists of one or more independent frames, the compressor object can still\n" -"be used after this method is called."); +"Flush any remaining data left in internal buffers. Since Zstandard\n" +"data consists of one or more independent frames, the compressor\n" +"object can still be used after this method is called."); #define _ZSTD_ZSTDCOMPRESSOR_FLUSH_METHODDEF \ {"flush", _PyCFunction_CAST(_zstd_ZstdCompressor_flush), METH_FASTCALL|METH_KEYWORDS, _zstd_ZstdCompressor_flush__doc__}, @@ -262,13 +262,14 @@ PyDoc_STRVAR(_zstd_ZstdCompressor_set_pledged_input_size__doc__, " size\n" " The size of the uncompressed data to be provided to the compressor.\n" "\n" -"This method can be used to ensure the header of the frame about to be written\n" -"includes the size of the data, unless the CompressionParameter.content_size_flag\n" -"is set to False. If last_mode != FLUSH_FRAME, then a RuntimeError is raised.\n" +"This method can be used to ensure the header of the frame about to\n" +"be written includes the size of the data, unless the\n" +"CompressionParameter.content_size_flag is set to False.\n" +"If last_mode != FLUSH_FRAME, then a RuntimeError is raised.\n" "\n" -"It is important to ensure that the pledged data size matches the actual data\n" -"size. If they do not match the compressed output data may be corrupted and the\n" -"final chunk written may be lost."); +"It is important to ensure that the pledged data size matches the\n" +"actual data size. If they do not match the compressed output data\n" +"may be corrupted and the final chunk written may be lost."); #define _ZSTD_ZSTDCOMPRESSOR_SET_PLEDGED_INPUT_SIZE_METHODDEF \ {"set_pledged_input_size", (PyCFunction)_zstd_ZstdCompressor_set_pledged_input_size, METH_O, _zstd_ZstdCompressor_set_pledged_input_size__doc__}, @@ -291,4 +292,4 @@ _zstd_ZstdCompressor_set_pledged_input_size(PyObject *self, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=c1d5c2cf06a8becd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1a5e21476885866c input=a9049054013a1b77]*/ diff --git a/Modules/_zstd/clinic/decompressor.c.h b/Modules/_zstd/clinic/decompressor.c.h index c6fdae74ab0447..fe3b76b8bb369d 100644 --- a/Modules/_zstd/clinic/decompressor.c.h +++ b/Modules/_zstd/clinic/decompressor.c.h @@ -20,8 +20,8 @@ PyDoc_STRVAR(_zstd_ZstdDecompressor_new__doc__, " options\n" " A dict object that contains advanced decompression parameters.\n" "\n" -"Thread-safe at method level. For one-shot decompression, use the decompress()\n" -"function instead."); +"Thread-safe at method level. For one-shot decompression, use the\n" +"decompress() function instead."); static PyObject * _zstd_ZstdDecompressor_new_impl(PyTypeObject *type, PyObject *zstd_dict, @@ -91,7 +91,8 @@ PyDoc_STRVAR(_zstd_ZstdDecompressor_unused_data__doc__, "A bytes object of un-consumed input data.\n" "\n" "When ZstdDecompressor object stops after a frame is\n" -"decompressed, unused input data after the frame. Otherwise this will be b\'\'."); +"decompressed, unused input data after the frame. Otherwise this\n" +"will be b\'\'."); #if defined(_zstd_ZstdDecompressor_unused_data_DOCSTR) # undef _zstd_ZstdDecompressor_unused_data_DOCSTR #endif @@ -129,18 +130,19 @@ PyDoc_STRVAR(_zstd_ZstdDecompressor_decompress__doc__, " output buffer is unlimited. When it is nonnegative, returns at\n" " most max_length bytes of decompressed data.\n" "\n" -"If *max_length* is nonnegative, returns at most *max_length* bytes of\n" -"decompressed data. If this limit is reached and further output can be\n" -"produced, *self.needs_input* will be set to ``False``. In this case, the next\n" -"call to *decompress()* may provide *data* as b\'\' to obtain more of the output.\n" +"If *max_length* is nonnegative, returns at most *max_length* bytes\n" +"of decompressed data. If this limit is reached and further output\n" +"can be produced, *self.needs_input* will be set to ``False``. In\n" +"this case, the next call to *decompress()* may provide *data* as b\'\'\n" +"to obtain more of the output.\n" "\n" -"If all of the input data was decompressed and returned (either because this\n" -"was less than *max_length* bytes, or because *max_length* was negative),\n" -"*self.needs_input* will be set to True.\n" +"If all of the input data was decompressed and returned (either\n" +"because this was less than *max_length* bytes, or because\n" +"*max_length* was negative), *self.needs_input* will be set to True.\n" "\n" -"Attempting to decompress data after the end of a frame is reached raises an\n" -"EOFError. Any data found after the end of the frame is ignored and saved in\n" -"the self.unused_data attribute."); +"Attempting to decompress data after the end of a frame is reached\n" +"raises an EOFError. Any data found after the end of the frame is\n" +"ignored and saved in the self.unused_data attribute."); #define _ZSTD_ZSTDDECOMPRESSOR_DECOMPRESS_METHODDEF \ {"decompress", _PyCFunction_CAST(_zstd_ZstdDecompressor_decompress), METH_FASTCALL|METH_KEYWORDS, _zstd_ZstdDecompressor_decompress__doc__}, @@ -220,4 +222,4 @@ _zstd_ZstdDecompressor_decompress(PyObject *self, PyObject *const *args, Py_ssiz return return_value; } -/*[clinic end generated code: output=30c12ef047027ede input=a9049054013a1b77]*/ +/*[clinic end generated code: output=70bc308e86463751 input=a9049054013a1b77]*/ diff --git a/Modules/_zstd/clinic/zstddict.c.h b/Modules/_zstd/clinic/zstddict.c.h index 79db85405d6e6b..18b049e3cbe37e 100644 --- a/Modules/_zstd/clinic/zstddict.c.h +++ b/Modules/_zstd/clinic/zstddict.c.h @@ -21,8 +21,8 @@ PyDoc_STRVAR(_zstd_ZstdDict_new__doc__, " advanced cases. Otherwise, check that the content represents\n" " a Zstandard dictionary created by the zstd library or CLI.\n" "\n" -"The dictionary can be used for compression or decompression, and can be shared\n" -"by multiple ZstdCompressor or ZstdDecompressor objects."); +"The dictionary can be used for compression or decompression, and can be\n" +"shared by multiple ZstdCompressor or ZstdDecompressor objects."); static PyObject * _zstd_ZstdDict_new_impl(PyTypeObject *type, Py_buffer *dict_content, @@ -125,11 +125,11 @@ PyDoc_STRVAR(_zstd_ZstdDict_as_digested_dict__doc__, "Pass this attribute as zstd_dict argument:\n" "compress(dat, zstd_dict=zd.as_digested_dict)\n" "\n" -"1. Some advanced compression parameters of compressor may be overridden\n" -" by parameters of digested dictionary.\n" -"2. ZstdDict has a digested dictionaries cache for each compression level.\n" -" It\'s faster when loading again a digested dictionary with the same\n" -" compression level.\n" +"1. Some advanced compression parameters of compressor may be\n" +" overridden by parameters of digested dictionary.\n" +"2. ZstdDict has a digested dictionaries cache for each compression\n" +" level. It\'s faster when loading again a digested dictionary with\n" +" the same compression level.\n" "3. No need to use this for decompression."); #if defined(_zstd_ZstdDict_as_digested_dict_DOCSTR) # undef _zstd_ZstdDict_as_digested_dict_DOCSTR @@ -161,9 +161,10 @@ PyDoc_STRVAR(_zstd_ZstdDict_as_undigested_dict__doc__, "Pass this attribute as zstd_dict argument:\n" "compress(dat, zstd_dict=zd.as_undigested_dict)\n" "\n" -"1. The advanced compression parameters of compressor will not be overridden.\n" -"2. Loading an undigested dictionary is costly. If load an undigested dictionary\n" -" multiple times, consider reusing a compressor object.\n" +"1. The advanced compression parameters of compressor will not be\n" +" overridden.\n" +"2. Loading an undigested dictionary is costly. If load an undigested\n" +" dictionary multiple times, consider reusing a compressor object.\n" "3. No need to use this for decompression."); #if defined(_zstd_ZstdDict_as_undigested_dict_DOCSTR) # undef _zstd_ZstdDict_as_undigested_dict_DOCSTR @@ -195,10 +196,11 @@ PyDoc_STRVAR(_zstd_ZstdDict_as_prefix__doc__, "Pass this attribute as zstd_dict argument:\n" "compress(dat, zstd_dict=zd.as_prefix)\n" "\n" -"1. Prefix is compatible with long distance matching, while dictionary is not.\n" -"2. It only works for the first frame, then the compressor/decompressor will\n" -" return to no prefix state.\n" -"3. When decompressing, must use the same prefix as when compressing.\""); +"1. Prefix is compatible with long distance matching, while\n" +" dictionary is not.\n" +"2. It only works for the first frame, then the\n" +" compressor/decompressor will return to no prefix state.\n" +"3. When decompressing, must use the same prefix as when compressing."); #if defined(_zstd_ZstdDict_as_prefix_DOCSTR) # undef _zstd_ZstdDict_as_prefix_DOCSTR #endif @@ -222,4 +224,4 @@ _zstd_ZstdDict_as_prefix_get(PyObject *self, void *Py_UNUSED(context)) { return _zstd_ZstdDict_as_prefix_get_impl((ZstdDict *)self); } -/*[clinic end generated code: output=4696cbc722e5fdfc input=a9049054013a1b77]*/ +/*[clinic end generated code: output=49b66061b4fcdb5f input=a9049054013a1b77]*/ diff --git a/Modules/_zstd/compressor.c b/Modules/_zstd/compressor.c index 508b136817872b..b2eb22d9ec8add 100644 --- a/Modules/_zstd/compressor.c +++ b/Modules/_zstd/compressor.c @@ -74,7 +74,7 @@ zstd_contentsize_converter(PyObject *size, unsigned long long *p) if (PyErr_ExceptionMatches(PyExc_OverflowError)) { PyErr_Format(PyExc_ValueError, "size argument should be a positive int less " - "than %ull", ZSTD_CONTENTSIZE_ERROR); + "than %llu", ZSTD_CONTENTSIZE_ERROR); return 0; } return 0; @@ -83,7 +83,7 @@ zstd_contentsize_converter(PyObject *size, unsigned long long *p) *p = ZSTD_CONTENTSIZE_ERROR; PyErr_Format(PyExc_ValueError, "size argument should be a positive int less " - "than %ull", ZSTD_CONTENTSIZE_ERROR); + "than %llu", ZSTD_CONTENTSIZE_ERROR); return 0; } *p = pledged_size; @@ -332,14 +332,14 @@ _zstd.ZstdCompressor.__new__ as _zstd_ZstdCompressor_new Create a compressor object for compressing data incrementally. -Thread-safe at method level. For one-shot compression, use the compress() -function instead. +Thread-safe at method level. For one-shot compression, use the +compress() function instead. [clinic start generated code]*/ static PyObject * _zstd_ZstdCompressor_new_impl(PyTypeObject *type, PyObject *level, PyObject *options, PyObject *zstd_dict) -/*[clinic end generated code: output=cdef61eafecac3d7 input=92de0211ae20ffdc]*/ +/*[clinic end generated code: output=cdef61eafecac3d7 input=bbfeeaa06fd3bd4d]*/ { ZstdCompressor* self = PyObject_GC_New(ZstdCompressor, type); if (self == NULL) { @@ -446,7 +446,7 @@ compress_lock_held(ZstdCompressor *self, Py_buffer *data, assert(PyMutex_IsLocked(&self->lock)); ZSTD_inBuffer in; ZSTD_outBuffer out; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; size_t zstd_ret; PyObject *ret; @@ -527,7 +527,7 @@ compress_mt_continue_lock_held(ZstdCompressor *self, Py_buffer *data) assert(PyMutex_IsLocked(&self->lock)); ZSTD_inBuffer in; ZSTD_outBuffer out; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; size_t zstd_ret; PyObject *ret; @@ -592,15 +592,15 @@ _zstd.ZstdCompressor.compress Provide data to the compressor object. -Return a chunk of compressed data if possible, or b'' otherwise. When you have -finished providing data to the compressor, call the flush() method to finish -the compression process. +Return a chunk of compressed data if possible, or b'' otherwise. +When you have finished providing data to the compressor, call the +flush() method to finish the compression process. [clinic start generated code]*/ static PyObject * _zstd_ZstdCompressor_compress_impl(ZstdCompressor *self, Py_buffer *data, int mode) -/*[clinic end generated code: output=ed7982d1cf7b4f98 input=ac2c21d180f579ea]*/ +/*[clinic end generated code: output=ed7982d1cf7b4f98 input=11726dff64d7b2f9]*/ { PyObject *ret; @@ -650,14 +650,14 @@ _zstd.ZstdCompressor.flush Finish the compression process. -Flush any remaining data left in internal buffers. Since Zstandard data -consists of one or more independent frames, the compressor object can still -be used after this method is called. +Flush any remaining data left in internal buffers. Since Zstandard +data consists of one or more independent frames, the compressor +object can still be used after this method is called. [clinic start generated code]*/ static PyObject * _zstd_ZstdCompressor_flush_impl(ZstdCompressor *self, int mode) -/*[clinic end generated code: output=b7cf2c8d64dcf2e3 input=0ab19627f323cdbc]*/ +/*[clinic end generated code: output=b7cf2c8d64dcf2e3 input=130e0b1eddf0f498]*/ { PyObject *ret; @@ -691,6 +691,7 @@ _zstd_ZstdCompressor_flush_impl(ZstdCompressor *self, int mode) /*[clinic input] +@permit_long_summary _zstd.ZstdCompressor.set_pledged_input_size size: zstd_contentsize @@ -699,21 +700,22 @@ _zstd.ZstdCompressor.set_pledged_input_size Set the uncompressed content size to be written into the frame header. -This method can be used to ensure the header of the frame about to be written -includes the size of the data, unless the CompressionParameter.content_size_flag -is set to False. If last_mode != FLUSH_FRAME, then a RuntimeError is raised. +This method can be used to ensure the header of the frame about to +be written includes the size of the data, unless the +CompressionParameter.content_size_flag is set to False. +If last_mode != FLUSH_FRAME, then a RuntimeError is raised. -It is important to ensure that the pledged data size matches the actual data -size. If they do not match the compressed output data may be corrupted and the -final chunk written may be lost. +It is important to ensure that the pledged data size matches the +actual data size. If they do not match the compressed output data +may be corrupted and the final chunk written may be lost. [clinic start generated code]*/ static PyObject * _zstd_ZstdCompressor_set_pledged_input_size_impl(ZstdCompressor *self, unsigned long long size) -/*[clinic end generated code: output=3a09e55cc0e3b4f9 input=afd8a7d78cff2eb5]*/ +/*[clinic end generated code: output=3a09e55cc0e3b4f9 input=714cd7a9aa10e2a8]*/ { - // Error occured while converting argument, should be unreachable + // Error occurred while converting argument, should be unreachable assert(size != ZSTD_CONTENTSIZE_ERROR); /* Thread-safe code */ diff --git a/Modules/_zstd/decompressor.c b/Modules/_zstd/decompressor.c index b00ee05d2f51bf..cb95ba89eb650a 100644 --- a/Modules/_zstd/decompressor.c +++ b/Modules/_zstd/decompressor.c @@ -101,7 +101,7 @@ _zstd_set_d_parameters(ZstdDecompressor *self, PyObject *options) /* Check key type */ if (Py_TYPE(key) == mod_state->CParameter_type) { PyErr_SetString(PyExc_TypeError, - "compression options dictionary key must not be a " + "decompression options dictionary key must not be a " "CompressionParameter attribute"); return -1; } @@ -111,6 +111,7 @@ _zstd_set_d_parameters(ZstdDecompressor *self, PyObject *options) int key_v = PyLong_AsInt(key); Py_DECREF(key); if (key_v == -1 && PyErr_Occurred()) { + Py_DECREF(value); return -1; } @@ -157,10 +158,7 @@ _zstd_load_impl(ZstdDecompressor *self, ZstdDict *zd, zd->dict_len); } else { - /* Impossible code path */ - PyErr_SetString(PyExc_SystemError, - "load_d_dict() impossible code path"); - return -1; + Py_UNREACHABLE(); } /* Check error */ @@ -219,7 +217,7 @@ decompress_lock_held(ZstdDecompressor *self, ZSTD_inBuffer *in, { size_t zstd_ret; ZSTD_outBuffer out; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; PyObject *ret; /* Initialize the output buffer */ @@ -480,14 +478,14 @@ _zstd.ZstdDecompressor.__new__ as _zstd_ZstdDecompressor_new Create a decompressor object for decompressing data incrementally. -Thread-safe at method level. For one-shot decompression, use the decompress() -function instead. +Thread-safe at method level. For one-shot decompression, use the +decompress() function instead. [clinic start generated code]*/ static PyObject * _zstd_ZstdDecompressor_new_impl(PyTypeObject *type, PyObject *zstd_dict, PyObject *options) -/*[clinic end generated code: output=590ca65c1102ff4a input=213daa57e3ea4062]*/ +/*[clinic end generated code: output=590ca65c1102ff4a input=73879de69bf89f59]*/ { ZstdDecompressor* self = PyObject_GC_New(ZstdDecompressor, type); if (self == NULL) { @@ -578,12 +576,13 @@ _zstd.ZstdDecompressor.unused_data A bytes object of un-consumed input data. When ZstdDecompressor object stops after a frame is -decompressed, unused input data after the frame. Otherwise this will be b''. +decompressed, unused input data after the frame. Otherwise this +will be b''. [clinic start generated code]*/ static PyObject * _zstd_ZstdDecompressor_unused_data_get_impl(ZstdDecompressor *self) -/*[clinic end generated code: output=f3a20940f11b6b09 input=54d41ecd681a3444]*/ +/*[clinic end generated code: output=f3a20940f11b6b09 input=0462065c5e60ba01]*/ { PyObject *ret; @@ -612,6 +611,7 @@ _zstd_ZstdDecompressor_unused_data_get_impl(ZstdDecompressor *self) } /*[clinic input] +@permit_long_summary _zstd.ZstdDecompressor.decompress data: Py_buffer @@ -623,25 +623,26 @@ _zstd.ZstdDecompressor.decompress Decompress *data*, returning uncompressed bytes if possible, or b'' otherwise. -If *max_length* is nonnegative, returns at most *max_length* bytes of -decompressed data. If this limit is reached and further output can be -produced, *self.needs_input* will be set to ``False``. In this case, the next -call to *decompress()* may provide *data* as b'' to obtain more of the output. +If *max_length* is nonnegative, returns at most *max_length* bytes +of decompressed data. If this limit is reached and further output +can be produced, *self.needs_input* will be set to ``False``. In +this case, the next call to *decompress()* may provide *data* as b'' +to obtain more of the output. -If all of the input data was decompressed and returned (either because this -was less than *max_length* bytes, or because *max_length* was negative), -*self.needs_input* will be set to True. +If all of the input data was decompressed and returned (either +because this was less than *max_length* bytes, or because +*max_length* was negative), *self.needs_input* will be set to True. -Attempting to decompress data after the end of a frame is reached raises an -EOFError. Any data found after the end of the frame is ignored and saved in -the self.unused_data attribute. +Attempting to decompress data after the end of a frame is reached +raises an EOFError. Any data found after the end of the frame is +ignored and saved in the self.unused_data attribute. [clinic start generated code]*/ static PyObject * _zstd_ZstdDecompressor_decompress_impl(ZstdDecompressor *self, Py_buffer *data, Py_ssize_t max_length) -/*[clinic end generated code: output=a4302b3c940dbec6 input=6463dfdf98091caa]*/ +/*[clinic end generated code: output=a4302b3c940dbec6 input=4ddda5a0bdd00673]*/ { PyObject *ret; /* Thread-safe code */ diff --git a/Modules/_zstd/zstddict.c b/Modules/_zstd/zstddict.c index 35d6ca8e55a265..e1b9d998e697fb 100644 --- a/Modules/_zstd/zstddict.c +++ b/Modules/_zstd/zstddict.c @@ -36,14 +36,14 @@ _zstd.ZstdDict.__new__ as _zstd_ZstdDict_new Represents a Zstandard dictionary. -The dictionary can be used for compression or decompression, and can be shared -by multiple ZstdCompressor or ZstdDecompressor objects. +The dictionary can be used for compression or decompression, and can be +shared by multiple ZstdCompressor or ZstdDecompressor objects. [clinic start generated code]*/ static PyObject * _zstd_ZstdDict_new_impl(PyTypeObject *type, Py_buffer *dict_content, int is_raw) -/*[clinic end generated code: output=685b7406a48b0949 input=9e8c493e31c98383]*/ +/*[clinic end generated code: output=685b7406a48b0949 input=3bb66063c0240433]*/ { /* All dictionaries must be at least 8 bytes */ if (dict_content->len < 8) { @@ -119,10 +119,10 @@ ZstdDict_dealloc(PyObject *ob) } PyDoc_STRVAR(ZstdDict_dictid_doc, -"the Zstandard dictionary, an int between 0 and 2**32.\n\n" -"A non-zero value represents an ordinary Zstandard dictionary, " +"The Zstandard dictionary, an int between 0 and 2**32.\n\n" +"A non-zero value represents an ordinary Zstandard dictionary,\n" "conforming to the standardised format.\n\n" -"The special value '0' means a 'raw content' dictionary," +"A value of zero indicates a 'raw content' dictionary,\n" "without any restrictions on format or content."); static PyObject * @@ -161,17 +161,17 @@ Load as a digested dictionary to compressor. Pass this attribute as zstd_dict argument: compress(dat, zstd_dict=zd.as_digested_dict) -1. Some advanced compression parameters of compressor may be overridden - by parameters of digested dictionary. -2. ZstdDict has a digested dictionaries cache for each compression level. - It's faster when loading again a digested dictionary with the same - compression level. +1. Some advanced compression parameters of compressor may be + overridden by parameters of digested dictionary. +2. ZstdDict has a digested dictionaries cache for each compression + level. It's faster when loading again a digested dictionary with + the same compression level. 3. No need to use this for decompression. [clinic start generated code]*/ static PyObject * _zstd_ZstdDict_as_digested_dict_get_impl(ZstdDict *self) -/*[clinic end generated code: output=09b086e7a7320dbb input=ee45e1b4a48f6f2c]*/ +/*[clinic end generated code: output=09b086e7a7320dbb input=a9417d40f1d7fedd]*/ { return Py_BuildValue("Oi", self, DICT_TYPE_DIGESTED); } @@ -185,15 +185,16 @@ Load as an undigested dictionary to compressor. Pass this attribute as zstd_dict argument: compress(dat, zstd_dict=zd.as_undigested_dict) -1. The advanced compression parameters of compressor will not be overridden. -2. Loading an undigested dictionary is costly. If load an undigested dictionary - multiple times, consider reusing a compressor object. +1. The advanced compression parameters of compressor will not be + overridden. +2. Loading an undigested dictionary is costly. If load an undigested + dictionary multiple times, consider reusing a compressor object. 3. No need to use this for decompression. [clinic start generated code]*/ static PyObject * _zstd_ZstdDict_as_undigested_dict_get_impl(ZstdDict *self) -/*[clinic end generated code: output=43c7a989e6d4253a input=d39210eedec76fed]*/ +/*[clinic end generated code: output=43c7a989e6d4253a input=56443c9c4e589cd5]*/ { return Py_BuildValue("Oi", self, DICT_TYPE_UNDIGESTED); } @@ -207,15 +208,16 @@ Load as a prefix to compressor/decompressor. Pass this attribute as zstd_dict argument: compress(dat, zstd_dict=zd.as_prefix) -1. Prefix is compatible with long distance matching, while dictionary is not. -2. It only works for the first frame, then the compressor/decompressor will - return to no prefix state. -3. When decompressing, must use the same prefix as when compressing." +1. Prefix is compatible with long distance matching, while + dictionary is not. +2. It only works for the first frame, then the + compressor/decompressor will return to no prefix state. +3. When decompressing, must use the same prefix as when compressing. [clinic start generated code]*/ static PyObject * _zstd_ZstdDict_as_prefix_get_impl(ZstdDict *self) -/*[clinic end generated code: output=6f7130c356595a16 input=d59757b0b5a9551a]*/ +/*[clinic end generated code: output=6f7130c356595a16 input=192681a899c6fad0]*/ { return Py_BuildValue("Oi", self, DICT_TYPE_PREFIX); } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 5d07de2fba9526..9f613927be6315 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -8,11 +8,13 @@ #endif #include "Python.h" -#include "pycore_bytesobject.h" // _PyBytes_Repeat +#include "pycore_bytesobject.h" // _PyBytes_RepeatBuffer #include "pycore_call.h" // _PyObject_CallMethod() #include "pycore_ceval.h" // _PyEval_GetBuiltin() +#include "pycore_floatobject.h" // _PY_FLOAT_BIG_ENDIAN #include "pycore_modsupport.h" // _PyArg_NoKeywords() #include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() #include <stddef.h> // offsetof() @@ -31,12 +33,11 @@ static struct PyModuleDef arraymodule; * functions aren't visible yet. */ struct arraydescr { - char typecode; + char typecode[3]; // big enough to store "Zd\0" int itemsize; PyObject * (*getitem)(struct arrayobject *, Py_ssize_t); int (*setitem)(struct arrayobject *, Py_ssize_t, PyObject *); int (*compareitems)(const void *, const void *, Py_ssize_t); - const char *formats; int is_integer_type; int is_signed; }; @@ -67,6 +68,7 @@ typedef struct { PyObject *str_write; PyObject *str___dict__; PyObject *str_iter; + PyObject *typecodes; } array_state; static array_state * @@ -91,9 +93,6 @@ enum machine_format_code { * instead of using the memory content of the array directly. In that * case, the array_reconstructor mechanism is bypassed completely, and * the standard array constructor is used instead. - * - * This is will most likely occur when the machine doesn't use IEEE - * floating-point numbers. */ UNSIGNED_INT8 = 0, @@ -117,10 +116,16 @@ enum machine_format_code { UTF16_LE = 18, UTF16_BE = 19, UTF32_LE = 20, - UTF32_BE = 21 + UTF32_BE = 21, + IEEE_754_FLOAT_COMPLEX_LE = 22, + IEEE_754_FLOAT_COMPLEX_BE = 23, + IEEE_754_DOUBLE_COMPLEX_LE = 24, + IEEE_754_DOUBLE_COMPLEX_BE = 25, + IEEE_754_FLOAT16_LE = 26, + IEEE_754_FLOAT16_BE = 27 }; #define MACHINE_FORMAT_CODE_MIN 0 -#define MACHINE_FORMAT_CODE_MAX 21 +#define MACHINE_FORMAT_CODE_MAX 27 /* @@ -205,6 +210,33 @@ Note that the basic Get and Set functions do NOT check that the index is in bounds; that's the responsibility of the caller. ****************************************************************************/ +/* Macro to check array buffer validity and bounds after calling + user-defined methods (like __index__ or __float__) that might modify + the array during the call. +*/ +#define CHECK_ARRAY_BOUNDS(OP, IDX) \ + do { \ + if ((IDX) >= 0 && ((OP)->ob_item == NULL || \ + (IDX) >= Py_SIZE((OP)))) { \ + PyErr_SetString(PyExc_IndexError, \ + "array assignment index out of range"); \ + return -1; \ + } \ + } while (0) + +#define CHECK_ARRAY_BOUNDS_WITH_CLEANUP(OP, IDX, VAL, CLEANUP) \ + do { \ + if ((IDX) >= 0 && ((OP)->ob_item == NULL || \ + (IDX) >= Py_SIZE((OP)))) { \ + PyErr_SetString(PyExc_IndexError, \ + "array assignment index out of range"); \ + if (CLEANUP) { \ + Py_DECREF(VAL); \ + } \ + return -1; \ + } \ + } while (0) + static PyObject * b_getitem(arrayobject *ap, Py_ssize_t i) { @@ -221,7 +253,10 @@ b_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) the overflow checking */ if (!PyArg_Parse(v, "h;array item must be integer", &x)) return -1; - else if (x < -128) { + + CHECK_ARRAY_BOUNDS(ap, i); + + if (x < -128) { PyErr_SetString(PyExc_OverflowError, "signed char is less than minimum"); return -1; @@ -250,51 +285,11 @@ BB_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) /* 'B' == unsigned char, maps to PyArg_Parse's 'b' formatter */ if (!PyArg_Parse(v, "b;array item must be integer", &x)) return -1; - if (i >= 0) - ((unsigned char *)ap->ob_item)[i] = x; - return 0; -} - -static PyObject * -u_getitem(arrayobject *ap, Py_ssize_t i) -{ - return PyUnicode_FromOrdinal(((wchar_t *) ap->ob_item)[i]); -} - -static int -u_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) -{ - if (!PyUnicode_Check(v)) { - PyErr_Format(PyExc_TypeError, - "array item must be a unicode character, not %T", - v); - return -1; - } - Py_ssize_t len = PyUnicode_AsWideChar(v, NULL, 0); - if (len != 2) { - if (PyUnicode_GET_LENGTH(v) != 1) { - PyErr_Format(PyExc_TypeError, - "array item must be a unicode character, " - "not a string of length %zd", - PyUnicode_GET_LENGTH(v)); - } - else { - PyErr_Format(PyExc_TypeError, - "string %A cannot be converted to " - "a single wchar_t character", - v); - } - return -1; - } - - wchar_t w; - len = PyUnicode_AsWideChar(v, &w, 1); - assert(len == 1); + CHECK_ARRAY_BOUNDS(ap, i); - if (i >= 0) { - ((wchar_t *)ap->ob_item)[i] = w; - } + if (i >= 0) + ((unsigned char *)ap->ob_item)[i] = x; return 0; } @@ -342,6 +337,9 @@ h_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) /* 'h' == signed short, maps to PyArg_Parse's 'h' formatter */ if (!PyArg_Parse(v, "h;array item must be integer", &x)) return -1; + + CHECK_ARRAY_BOUNDS(ap, i); + if (i >= 0) ((short *)ap->ob_item)[i] = x; return 0; @@ -371,6 +369,9 @@ HH_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) "unsigned short is greater than maximum"); return -1; } + + CHECK_ARRAY_BOUNDS(ap, i); + if (i >= 0) ((short *)ap->ob_item)[i] = (short)x; return 0; @@ -389,6 +390,9 @@ i_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) /* 'i' == signed int, maps to PyArg_Parse's 'i' formatter */ if (!PyArg_Parse(v, "i;array item must be integer", &x)) return -1; + + CHECK_ARRAY_BOUNDS(ap, i); + if (i >= 0) ((int *)ap->ob_item)[i] = x; return 0; @@ -408,10 +412,13 @@ II_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) int do_decref = 0; /* if nb_int was called */ if (!PyLong_Check(v)) { - v = _PyNumber_Index(v); - if (NULL == v) { + Py_INCREF(v); + PyObject *res = _PyNumber_Index(v); + Py_DECREF(v); + if (NULL == res) { return -1; } + v = res; do_decref = 1; } x = PyLong_AsUnsignedLong(v); @@ -429,6 +436,9 @@ II_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) } return -1; } + + CHECK_ARRAY_BOUNDS_WITH_CLEANUP(ap, i, v, do_decref); + if (i >= 0) ((unsigned int *)ap->ob_item)[i] = (unsigned int)x; @@ -450,6 +460,9 @@ l_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) long x; if (!PyArg_Parse(v, "l;array item must be integer", &x)) return -1; + + CHECK_ARRAY_BOUNDS(ap, i); + if (i >= 0) ((long *)ap->ob_item)[i] = x; return 0; @@ -468,10 +481,13 @@ LL_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) int do_decref = 0; /* if nb_int was called */ if (!PyLong_Check(v)) { - v = _PyNumber_Index(v); - if (NULL == v) { + Py_INCREF(v); + PyObject *res = _PyNumber_Index(v); + Py_DECREF(v); + if (NULL == res) { return -1; } + v = res; do_decref = 1; } x = PyLong_AsUnsignedLong(v); @@ -481,6 +497,9 @@ LL_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) } return -1; } + + CHECK_ARRAY_BOUNDS_WITH_CLEANUP(ap, i, v, do_decref); + if (i >= 0) ((unsigned long *)ap->ob_item)[i] = x; @@ -502,6 +521,9 @@ q_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) long long x; if (!PyArg_Parse(v, "L;array item must be integer", &x)) return -1; + + CHECK_ARRAY_BOUNDS(ap, i); + if (i >= 0) ((long long *)ap->ob_item)[i] = x; return 0; @@ -521,10 +543,13 @@ QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) int do_decref = 0; /* if nb_int was called */ if (!PyLong_Check(v)) { - v = _PyNumber_Index(v); - if (NULL == v) { + Py_INCREF(v); + PyObject *res = _PyNumber_Index(v); + Py_DECREF(v); + if (NULL == res) { return -1; } + v = res; do_decref = 1; } x = PyLong_AsUnsignedLongLong(v); @@ -534,6 +559,9 @@ QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) } return -1; } + + CHECK_ARRAY_BOUNDS_WITH_CLEANUP(ap, i, v, do_decref); + if (i >= 0) ((unsigned long long *)ap->ob_item)[i] = x; @@ -543,6 +571,32 @@ QQ_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) return 0; } +static PyObject * +e_getitem(arrayobject *ap, Py_ssize_t i) +{ + double x = PyFloat_Unpack2(ap->ob_item + sizeof(short)*i, + PY_LITTLE_ENDIAN); + + return PyFloat_FromDouble(x); +} + +static int +e_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) +{ + float x; + if (!PyArg_Parse(v, "f;array item must be float", &x)) { + return -1; + } + + CHECK_ARRAY_BOUNDS(ap, i); + + if (i >= 0) { + return PyFloat_Pack2(x, ap->ob_item + sizeof(short)*i, + PY_LITTLE_ENDIAN); + } + return 0; +} + static PyObject * f_getitem(arrayobject *ap, Py_ssize_t i) { @@ -555,6 +609,9 @@ f_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) float x; if (!PyArg_Parse(v, "f;array item must be float", &x)) return -1; + + CHECK_ARRAY_BOUNDS(ap, i); + if (i >= 0) ((float *)ap->ob_item)[i] = x; return 0; @@ -572,11 +629,72 @@ d_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) double x; if (!PyArg_Parse(v, "d;array item must be float", &x)) return -1; + + CHECK_ARRAY_BOUNDS(ap, i); + if (i >= 0) ((double *)ap->ob_item)[i] = x; return 0; } +static PyObject * +cf_getitem(arrayobject *ap, Py_ssize_t i) +{ + float f[2]; + + memcpy(&f, ap->ob_item + i*sizeof(f), sizeof(f)); + return PyComplex_FromDoubles(f[0], f[1]); +} + +static int +cf_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) +{ + Py_complex x; + float f[2]; + + if (!PyArg_Parse(v, "D;array item must be complex", &x)) { + return -1; + } + + CHECK_ARRAY_BOUNDS(ap, i); + + f[0] = (float)x.real; + f[1] = (float)x.imag; + if (i >= 0) { + memcpy(ap->ob_item + i*sizeof(f), &f, sizeof(f)); + } + return 0; +} + +static PyObject * +cd_getitem(arrayobject *ap, Py_ssize_t i) +{ + double f[2]; + + memcpy(&f, ap->ob_item + i*sizeof(f), sizeof(f)); + return PyComplex_FromDoubles(f[0], f[1]); +} + +static int +cd_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) +{ + Py_complex x; + double f[2]; + + if (!PyArg_Parse(v, "D;array item must be complex", &x)) { + return -1; + } + + CHECK_ARRAY_BOUNDS(ap, i); + + f[0] = x.real; + f[1] = x.imag; + if (i >= 0) { + memcpy(ap->ob_item + i*sizeof(f), &f, sizeof(f)); + } + return 0; +} + #define DEFINE_COMPAREITEMS(code, type) \ static int \ code##_compareitems(const void *lhs, const void *rhs, Py_ssize_t length) \ @@ -590,7 +708,6 @@ d_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) DEFINE_COMPAREITEMS(b, signed char) DEFINE_COMPAREITEMS(BB, unsigned char) -DEFINE_COMPAREITEMS(u, wchar_t) DEFINE_COMPAREITEMS(w, Py_UCS4) DEFINE_COMPAREITEMS(h, short) DEFINE_COMPAREITEMS(HH, unsigned short) @@ -607,21 +724,23 @@ DEFINE_COMPAREITEMS(QQ, unsigned long long) * typecode. */ static const struct arraydescr descriptors[] = { - {'b', 1, b_getitem, b_setitem, b_compareitems, "b", 1, 1}, - {'B', 1, BB_getitem, BB_setitem, BB_compareitems, "B", 1, 0}, - {'u', sizeof(wchar_t), u_getitem, u_setitem, u_compareitems, "u", 0, 0}, - {'w', sizeof(Py_UCS4), w_getitem, w_setitem, w_compareitems, "w", 0, 0,}, - {'h', sizeof(short), h_getitem, h_setitem, h_compareitems, "h", 1, 1}, - {'H', sizeof(short), HH_getitem, HH_setitem, HH_compareitems, "H", 1, 0}, - {'i', sizeof(int), i_getitem, i_setitem, i_compareitems, "i", 1, 1}, - {'I', sizeof(int), II_getitem, II_setitem, II_compareitems, "I", 1, 0}, - {'l', sizeof(long), l_getitem, l_setitem, l_compareitems, "l", 1, 1}, - {'L', sizeof(long), LL_getitem, LL_setitem, LL_compareitems, "L", 1, 0}, - {'q', sizeof(long long), q_getitem, q_setitem, q_compareitems, "q", 1, 1}, - {'Q', sizeof(long long), QQ_getitem, QQ_setitem, QQ_compareitems, "Q", 1, 0}, - {'f', sizeof(float), f_getitem, f_setitem, NULL, "f", 0, 0}, - {'d', sizeof(double), d_getitem, d_setitem, NULL, "d", 0, 0}, - {'\0', 0, 0, 0, 0, 0, 0} /* Sentinel */ + {"b", 1, b_getitem, b_setitem, b_compareitems, 1, 1}, + {"B", 1, BB_getitem, BB_setitem, BB_compareitems, 1, 0}, + {"w", sizeof(Py_UCS4), w_getitem, w_setitem, w_compareitems, 0, 0,}, + {"h", sizeof(short), h_getitem, h_setitem, h_compareitems, 1, 1}, + {"H", sizeof(short), HH_getitem, HH_setitem, HH_compareitems, 1, 0}, + {"i", sizeof(int), i_getitem, i_setitem, i_compareitems, 1, 1}, + {"I", sizeof(int), II_getitem, II_setitem, II_compareitems, 1, 0}, + {"l", sizeof(long), l_getitem, l_setitem, l_compareitems, 1, 1}, + {"L", sizeof(long), LL_getitem, LL_setitem, LL_compareitems, 1, 0}, + {"q", sizeof(long long), q_getitem, q_setitem, q_compareitems, 1, 1}, + {"Q", sizeof(long long), QQ_getitem, QQ_setitem, QQ_compareitems, 1, 0}, + {"e", sizeof(short), e_getitem, e_setitem, NULL, 0, 0}, + {"f", sizeof(float), f_getitem, f_setitem, NULL, 0, 0}, + {"d", sizeof(double), d_getitem, d_setitem, NULL, 0, 0}, + {"Zf", 2*sizeof(float), cf_getitem, cf_setitem, NULL, 0, 0}, + {"Zd", 2*sizeof(double), cd_getitem, cd_setitem, NULL, 0, 0}, + {"", 0, 0, 0, 0, 0, 0} /* Sentinel */ }; /**************************************************************************** @@ -714,14 +833,6 @@ ins1(arrayobject *self, Py_ssize_t where, PyObject *v) } /* Methods */ - -static int -array_tp_traverse(PyObject *op, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(op)); - return 0; -} - static void array_dealloc(PyObject *op) { @@ -992,7 +1103,7 @@ array_repeat(PyObject *op, Py_ssize_t n) const Py_ssize_t oldbytes = array_length * a->ob_descr->itemsize; const Py_ssize_t newbytes = oldbytes * n; - _PyBytes_Repeat(np->ob_item, newbytes, a->ob_item, oldbytes); + _PyBytes_RepeatBuffer(np->ob_item, newbytes, a->ob_item, oldbytes); return (PyObject *)np; } @@ -1149,7 +1260,7 @@ array_inplace_repeat(PyObject *op, Py_ssize_t n) if (array_resize(self, n * array_size) == -1) return NULL; - _PyBytes_Repeat(self->ob_item, n*size, self->ob_item, size); + _PyBytes_RepeatBuffer(self->ob_item, n*size, self->ob_item, size); } return Py_NewRef(self); } @@ -1374,39 +1485,30 @@ array_array_insert_impl(arrayobject *self, Py_ssize_t i, PyObject *v) } /*[clinic input] +@permit_long_summary array.array.buffer_info Return a tuple (address, length) giving the current memory address and the length in items of the buffer used to hold array's contents. -The length should be multiplied by the itemsize attribute to calculate -the buffer length in bytes. +The length should be multiplied by the itemsize attribute to +calculate the buffer length in bytes. [clinic start generated code]*/ static PyObject * array_array_buffer_info_impl(arrayobject *self) -/*[clinic end generated code: output=9b2a4ec3ae7e98e7 input=a58bae5c6e1ac6a6]*/ +/*[clinic end generated code: output=9b2a4ec3ae7e98e7 input=c2771b9f6a8e1c86]*/ { - PyObject *retval = NULL, *v; - - retval = PyTuple_New(2); - if (!retval) - return NULL; - - v = PyLong_FromVoidPtr(self->ob_item); - if (v == NULL) { - Py_DECREF(retval); + PyObject* item1 = PyLong_FromVoidPtr(self->ob_item); + if (item1 == NULL) { return NULL; } - PyTuple_SET_ITEM(retval, 0, v); - - v = PyLong_FromSsize_t(Py_SIZE(self)); - if (v == NULL) { - Py_DECREF(retval); + PyObject* item2 = PyLong_FromSsize_t(Py_SIZE(self)); + if (item2 == NULL) { + Py_DECREF(item1); return NULL; } - PyTuple_SET_ITEM(retval, 1, v); - return retval; + return _PyTuple_FromPairSteal(item1, item2); } /*[clinic input] @@ -1430,13 +1532,14 @@ array.array.byteswap Byteswap all items of the array. -If the items in the array are not 1, 2, 4, or 8 bytes in size, RuntimeError is -raised. +If the items in the array are not 1, 2, 4, 8 or 16 bytes in size, +RuntimeError is raised. Note, that for complex types the order of +components (the real part, followed by imaginary part) is preserved. [clinic start generated code]*/ static PyObject * array_array_byteswap_impl(arrayobject *self) -/*[clinic end generated code: output=5f8236cbdf0d90b5 input=6a85591b950a0186]*/ +/*[clinic end generated code: output=5f8236cbdf0d90b5 input=8732f800e1b47bac]*/ { char *p; Py_ssize_t i; @@ -1462,19 +1565,66 @@ array_array_byteswap_impl(arrayobject *self) } break; case 8: + if (strcmp(self->ob_descr->typecode, "Zf") != 0) { + for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) { + char p0 = p[0]; + char p1 = p[1]; + char p2 = p[2]; + char p3 = p[3]; + p[0] = p[7]; + p[1] = p[6]; + p[2] = p[5]; + p[3] = p[4]; + p[4] = p3; + p[5] = p2; + p[6] = p1; + p[7] = p0; + } + } + else { + for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) { + char t0 = p[0]; + char t1 = p[1]; + p[0] = p[3]; + p[1] = p[2]; + p[2] = t1; + p[3] = t0; + t0 = p[4]; + t1 = p[5]; + p[4] = p[7]; + p[5] = p[6]; + p[6] = t1; + p[7] = t0; + } + } + break; + case 16: + assert(strcmp(self->ob_descr->typecode, "Zd") == 0); for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) { - char p0 = p[0]; - char p1 = p[1]; - char p2 = p[2]; - char p3 = p[3]; + char t0 = p[0]; + char t1 = p[1]; + char t2 = p[2]; + char t3 = p[3]; p[0] = p[7]; p[1] = p[6]; p[2] = p[5]; p[3] = p[4]; - p[4] = p3; - p[5] = p2; - p[6] = p1; - p[7] = p0; + p[4] = t3; + p[5] = t2; + p[6] = t1; + p[7] = t0; + t0 = p[8]; + t1 = p[9]; + t2 = p[10]; + t3 = p[11]; + p[8] = p[15]; + p[9] = p[14]; + p[10] = p[13]; + p[11] = p[12]; + p[12] = t3; + p[13] = t2; + p[14] = t1; + p[15] = t0; } break; default: @@ -1519,11 +1669,12 @@ array_array_reverse_impl(arrayobject *self) } /*[clinic input] +@permit_long_summary array.array.fromfile cls: defining_class f: object - n: Py_ssize_t + n: Py_ssize_t(allow_negative=False) / Read n objects from the file object f and append them to the end of the array. @@ -1532,17 +1683,13 @@ Read n objects from the file object f and append them to the end of the array. static PyObject * array_array_fromfile_impl(arrayobject *self, PyTypeObject *cls, PyObject *f, Py_ssize_t n) -/*[clinic end generated code: output=83a667080b345ebc input=3822e907c1c11f1a]*/ +/*[clinic end generated code: output=83a667080b345ebc input=db46b06ac1b6de87]*/ { PyObject *b, *res; Py_ssize_t itemsize = self->ob_descr->itemsize; Py_ssize_t nbytes; int not_enough_bytes; - if (n < 0) { - PyErr_SetString(PyExc_ValueError, "negative count"); - return NULL; - } if (n > PY_SSIZE_T_MAX / itemsize) { PyErr_NoMemory(); return NULL; @@ -1739,6 +1886,7 @@ frombytes(arrayobject *self, Py_buffer *buffer) } /*[clinic input] +@permit_long_summary array.array.frombytes buffer: Py_buffer @@ -1749,12 +1897,13 @@ Appends items from the string, interpreting it as an array of machine values, as static PyObject * array_array_frombytes_impl(arrayobject *self, Py_buffer *buffer) -/*[clinic end generated code: output=d9842c8f7510a516 input=378db226dfac949e]*/ +/*[clinic end generated code: output=d9842c8f7510a516 input=6b90ce5895f677a4]*/ { return frombytes(self, buffer); } /*[clinic input] +@permit_long_summary array.array.tobytes Convert the array to an array of machine values and return the bytes representation. @@ -1762,7 +1911,7 @@ Convert the array to an array of machine values and return the bytes representat static PyObject * array_array_tobytes_impl(arrayobject *self) -/*[clinic end generated code: output=87318e4edcdc2bb6 input=90ee495f96de34f5]*/ +/*[clinic end generated code: output=87318e4edcdc2bb6 input=41081256b97f9d7a]*/ { if (Py_SIZE(self) <= PY_SSIZE_T_MAX / self->ob_descr->itemsize) { return PyBytes_FromStringAndSize(self->ob_item, @@ -1780,57 +1929,40 @@ array.array.fromunicode Extends this array with data from the unicode string ustr. -The array must be a unicode type array; otherwise a ValueError is raised. -Use array.frombytes(ustr.encode(...)) to append Unicode data to an array of -some other type. +The array must be a unicode type array; otherwise a ValueError is +raised. Use array.frombytes(ustr.encode(...)) to append Unicode +data to an array of some other type. [clinic start generated code]*/ static PyObject * array_array_fromunicode_impl(arrayobject *self, PyObject *ustr) -/*[clinic end generated code: output=24359f5e001a7f2b input=025db1fdade7a4ce]*/ +/*[clinic end generated code: output=24359f5e001a7f2b input=01fa592ec7b948b6]*/ { - int typecode = self->ob_descr->typecode; - if (typecode != 'u' && typecode != 'w') { + const char *typecode = self->ob_descr->typecode; + if (strcmp(typecode, "w") != 0) { PyErr_SetString(PyExc_ValueError, "fromunicode() may only be called on " - "unicode type arrays ('u' or 'w')"); + "unicode type ('w') arrays"); return NULL; } - if (typecode == 'u') { - Py_ssize_t ustr_length = PyUnicode_AsWideChar(ustr, NULL, 0); - assert(ustr_length > 0); - if (ustr_length > 1) { - ustr_length--; /* trim trailing NUL character */ - Py_ssize_t old_size = Py_SIZE(self); - if (array_resize(self, old_size + ustr_length) == -1) { - return NULL; - } + Py_ssize_t ustr_length = PyUnicode_GetLength(ustr); + Py_ssize_t old_size = Py_SIZE(self); + Py_ssize_t new_size = old_size + ustr_length; - // must not fail - PyUnicode_AsWideChar( - ustr, ((wchar_t *)self->ob_item) + old_size, ustr_length); - } + if (new_size < 0 || (size_t)new_size > PY_SSIZE_T_MAX / sizeof(Py_UCS4)) { + return PyErr_NoMemory(); } - else { // typecode == 'w' - Py_ssize_t ustr_length = PyUnicode_GetLength(ustr); - Py_ssize_t old_size = Py_SIZE(self); - Py_ssize_t new_size = old_size + ustr_length; - - if (new_size < 0 || (size_t)new_size > PY_SSIZE_T_MAX / sizeof(Py_UCS4)) { - return PyErr_NoMemory(); - } - if (array_resize(self, new_size) == -1) { - return NULL; - } - - // must not fail - Py_UCS4 *u = PyUnicode_AsUCS4(ustr, ((Py_UCS4*)self->ob_item) + old_size, - ustr_length, 0); - assert(u != NULL); - (void)u; // Suppress unused_variable warning. + if (array_resize(self, new_size) == -1) { + return NULL; } + // must not fail + Py_UCS4 *u = PyUnicode_AsUCS4(ustr, ((Py_UCS4*)self->ob_item) + old_size, + ustr_length, 0); + assert(u != NULL); + (void)u; // Suppress unused_variable warning. + Py_RETURN_NONE; } @@ -1839,29 +1971,25 @@ array.array.tounicode Extends this array with data from the unicode string ustr. -Convert the array to a unicode string. The array must be a unicode type array; -otherwise a ValueError is raised. Use array.tobytes().decode() to obtain a -unicode string from an array of some other type. +Convert the array to a unicode string. The array must be a unicode +type array; otherwise a ValueError is raised. Use +array.tobytes().decode() to obtain a unicode string from an array of +some other type. [clinic start generated code]*/ static PyObject * array_array_tounicode_impl(arrayobject *self) -/*[clinic end generated code: output=08e442378336e1ef input=127242eebe70b66d]*/ +/*[clinic end generated code: output=08e442378336e1ef input=d4d5f398aa71a2be]*/ { - int typecode = self->ob_descr->typecode; - if (typecode != 'u' && typecode != 'w') { + const char *typecode = self->ob_descr->typecode; + if (strcmp(typecode, "w") != 0) { PyErr_SetString(PyExc_ValueError, - "tounicode() may only be called on unicode type arrays ('u' or 'w')"); + "tounicode() may only be called on unicode type ('w') arrays"); return NULL; } - if (typecode == 'u') { - return PyUnicode_FromWideChar((wchar_t *) self->ob_item, Py_SIZE(self)); - } - else { // typecode == 'w' - int byteorder = 0; // native byteorder - return PyUnicode_DecodeUTF32((const char *) self->ob_item, Py_SIZE(self) * 4, - NULL, &byteorder); - } + int byteorder = 0; // native byteorder + return PyUnicode_DecodeUTF32((const char *) self->ob_item, Py_SIZE(self) * 4, + NULL, &byteorder); } /*[clinic input] @@ -1908,7 +2036,13 @@ static const struct mformatdescr { {4, 0, 0}, /* 18: UTF16_LE */ {4, 0, 1}, /* 19: UTF16_BE */ {8, 0, 0}, /* 20: UTF32_LE */ - {8, 0, 1} /* 21: UTF32_BE */ + {8, 0, 1}, /* 21: UTF32_BE */ + {8, 0, 0}, /* 22: IEEE_754_FLOAT_COMPLEX_LE */ + {8, 0, 1}, /* 23: IEEE_754_FLOAT_COMPLEX_BE */ + {16, 0, 0}, /* 24: IEEE_754_DOUBLE_COMPLEX_LE */ + {16, 0, 1}, /* 25: IEEE_754_DOUBLE_COMPLEX_BE */ + {2, 0, 0}, /* 26: IEEE_754_FLOAT16_LE */ + {2, 0, 1} /* 27: IEEE_754_FLOAT16_BE */ }; @@ -1918,50 +2052,45 @@ static const struct mformatdescr { * be found. */ static enum machine_format_code -typecode_to_mformat_code(char typecode) +typecode_to_mformat_code(const char *typecode) { const int is_big_endian = PY_BIG_ENDIAN; size_t intsize; int is_signed; - switch (typecode) { + switch (typecode[0]) { case 'b': return SIGNED_INT8; case 'B': return UNSIGNED_INT8; - case 'u': - if (sizeof(wchar_t) == 2) { - return UTF16_LE + is_big_endian; - } - if (sizeof(wchar_t) == 4) { - return UTF32_LE + is_big_endian; - } - return UNKNOWN_FORMAT; - case 'w': return UTF32_LE + is_big_endian; + case 'e': + return _PY_FLOAT_BIG_ENDIAN ? IEEE_754_FLOAT16_BE : IEEE_754_FLOAT16_LE; + case 'f': - if (sizeof(float) == 4) { - const float y = 16711938.0; - if (memcmp(&y, "\x4b\x7f\x01\x02", 4) == 0) - return IEEE_754_FLOAT_BE; - if (memcmp(&y, "\x02\x01\x7f\x4b", 4) == 0) - return IEEE_754_FLOAT_LE; - } - return UNKNOWN_FORMAT; + return _PY_FLOAT_BIG_ENDIAN ? IEEE_754_FLOAT_BE : IEEE_754_FLOAT_LE; case 'd': - if (sizeof(double) == 8) { - const double x = 9006104071832581.0; - if (memcmp(&x, "\x43\x3f\xff\x01\x02\x03\x04\x05", 8) == 0) - return IEEE_754_DOUBLE_BE; - if (memcmp(&x, "\x05\x04\x03\x02\x01\xff\x3f\x43", 8) == 0) - return IEEE_754_DOUBLE_LE; + return _PY_FLOAT_BIG_ENDIAN ? IEEE_754_DOUBLE_BE : IEEE_754_DOUBLE_LE; + + case 'Z': { + switch (typecode[1]) { + case 'f': + return _PY_FLOAT_BIG_ENDIAN ? \ + IEEE_754_FLOAT_COMPLEX_BE : IEEE_754_FLOAT_COMPLEX_LE; + + case 'd': + return _PY_FLOAT_BIG_ENDIAN ? \ + IEEE_754_DOUBLE_COMPLEX_BE : IEEE_754_DOUBLE_COMPLEX_LE; + + default: + return UNKNOWN_FORMAT; } - return UNKNOWN_FORMAT; + } /* Integers */ case 'h': @@ -2018,7 +2147,7 @@ static PyObject *array_new(PyTypeObject *type, PyObject *args, PyObject *kwds); * Internal: This function wraps the array constructor--i.e., array_new()--to * allow the creation of array objects from C code without having to deal * directly the tuple argument of array_new(). The typecode argument is a - * Unicode character value, like 'i' or 'f' for example, representing an array + * string, like "i" or "f" for example, representing an array * type code. The items argument is a bytes or a list object from which * contains the initial value of the array. * @@ -2026,7 +2155,7 @@ static PyObject *array_new(PyTypeObject *type, PyObject *args, PyObject *kwds); * NULL is returned to indicate a failure. */ static PyObject * -make_array(PyTypeObject *arraytype, char typecode, PyObject *items) +make_array(PyTypeObject *arraytype, const char *typecode, PyObject *items) { PyObject *new_args; PyObject *array_obj; @@ -2035,17 +2164,14 @@ make_array(PyTypeObject *arraytype, char typecode, PyObject *items) assert(arraytype != NULL); assert(items != NULL); - typecode_obj = PyUnicode_FromOrdinal(typecode); + typecode_obj = PyUnicode_FromString(typecode); if (typecode_obj == NULL) return NULL; - new_args = PyTuple_New(2); + new_args = _PyTuple_FromPairSteal(typecode_obj, Py_NewRef(items)); if (new_args == NULL) { - Py_DECREF(typecode_obj); return NULL; } - PyTuple_SET_ITEM(new_args, 0, typecode_obj); - PyTuple_SET_ITEM(new_args, 1, Py_NewRef(items)); array_obj = array_new(arraytype, new_args, NULL); Py_DECREF(new_args); @@ -2063,7 +2189,7 @@ make_array(PyTypeObject *arraytype, char typecode, PyObject *items) array._array_reconstructor arraytype: object(type="PyTypeObject *") - typecode: int(accept={str}) + typecode: str mformat_code: int(type="enum machine_format_code") items: object / @@ -2073,10 +2199,10 @@ Internal. Used for pickling support. static PyObject * array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, - int typecode, + const char *typecode, enum machine_format_code mformat_code, PyObject *items) -/*[clinic end generated code: output=e05263141ba28365 input=2464dc8f4c7736b5]*/ +/*[clinic end generated code: output=723e3813e0a18b7b input=9f1c331baae742a6]*/ { array_state *state = get_array_state(module); PyObject *converted_items; @@ -2095,11 +2221,12 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, arraytype->tp_name, state->ArrayType->tp_name); return NULL; } - for (descr = descriptors; descr->typecode != '\0'; descr++) { - if ((int)descr->typecode == typecode) + for (descr = descriptors; descr->typecode[0] != 0; descr++) { + if (strcmp(descr->typecode, typecode) == 0) { break; + } } - if (descr->typecode == '\0') { + if (descr->typecode[0] == 0) { PyErr_SetString(PyExc_ValueError, "second argument must be a valid type code"); return NULL; @@ -2118,9 +2245,9 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, } /* Fast path: No decoding has to be done. */ - if (mformat_code == typecode_to_mformat_code((char)typecode) || + if (mformat_code == typecode_to_mformat_code(typecode) || mformat_code == UNKNOWN_FORMAT) { - return make_array(arraytype, (char)typecode, items); + return make_array(arraytype, typecode, items); } /* Slow path: Decode the byte string according to the given machine @@ -2134,6 +2261,27 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, return NULL; } switch (mformat_code) { + case IEEE_754_FLOAT16_LE: + case IEEE_754_FLOAT16_BE: { + Py_ssize_t i; + int le = (mformat_code == IEEE_754_FLOAT_LE) ? 1 : 0; + Py_ssize_t itemcount = Py_SIZE(items) / 2; + const char *memstr = PyBytes_AS_STRING(items); + + converted_items = PyList_New(itemcount); + if (converted_items == NULL) + return NULL; + for (i = 0; i < itemcount; i++) { + PyObject *pyfloat = PyFloat_FromDouble( + PyFloat_Unpack2(&memstr[i * 2], le)); + if (pyfloat == NULL) { + Py_DECREF(converted_items); + return NULL; + } + PyList_SET_ITEM(converted_items, i, pyfloat); + } + break; + } case IEEE_754_FLOAT_LE: case IEEE_754_FLOAT_BE: { Py_ssize_t i; @@ -2176,6 +2324,52 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, } break; } + case IEEE_754_FLOAT_COMPLEX_LE: + case IEEE_754_FLOAT_COMPLEX_BE: { + Py_ssize_t i; + int le = (mformat_code == IEEE_754_FLOAT_COMPLEX_LE) ? 1 : 0; + Py_ssize_t itemcount = Py_SIZE(items) / 8; + const char *memstr = PyBytes_AS_STRING(items); + + converted_items = PyList_New(itemcount); + if (converted_items == NULL) { + return NULL; + } + for (i = 0; i < itemcount; i++) { + PyObject *pycomplex = PyComplex_FromDoubles( + PyFloat_Unpack4(&memstr[i * 8], le), + PyFloat_Unpack4(&memstr[i * 8] + 4, le)); + if (pycomplex == NULL) { + Py_DECREF(converted_items); + return NULL; + } + PyList_SET_ITEM(converted_items, i, pycomplex); + } + break; + } + case IEEE_754_DOUBLE_COMPLEX_LE: + case IEEE_754_DOUBLE_COMPLEX_BE: { + Py_ssize_t i; + int le = (mformat_code == IEEE_754_DOUBLE_COMPLEX_LE) ? 1 : 0; + Py_ssize_t itemcount = Py_SIZE(items) / 16; + const char *memstr = PyBytes_AS_STRING(items); + + converted_items = PyList_New(itemcount); + if (converted_items == NULL) { + return NULL; + } + for (i = 0; i < itemcount; i++) { + PyObject *pycomplex = PyComplex_FromDoubles( + PyFloat_Unpack8(&memstr[i * 16], le), + PyFloat_Unpack8(&memstr[i * 16] + 8, le)); + if (pycomplex == NULL) { + Py_DECREF(converted_items); + return NULL; + } + PyList_SET_ITEM(converted_items, i, pycomplex); + } + break; + } case UTF16_LE: case UTF16_BE: { int byteorder = (mformat_code == UTF16_LE) ? -1 : 1; @@ -2229,11 +2423,13 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, * * XXX: Is it possible to write a unit test for this? */ - for (descr = descriptors; descr->typecode != '\0'; descr++) { + for (descr = descriptors; descr->typecode[0] != 0; descr++) { if (descr->is_integer_type && (size_t)descr->itemsize == mf_descr.size && descr->is_signed == mf_descr.is_signed) + { typecode = descr->typecode; + } } converted_items = PyList_New(itemcount); @@ -2264,7 +2460,7 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, return NULL; } - result = make_array(arraytype, (char)typecode, converted_items); + result = make_array(arraytype, typecode, converted_items); Py_DECREF(converted_items); return result; } @@ -2287,7 +2483,7 @@ array_array___reduce_ex___impl(arrayobject *self, PyTypeObject *cls, PyObject *dict; PyObject *result; PyObject *array_str; - int typecode = self->ob_descr->typecode; + const char *typecode = self->ob_descr->typecode; int mformat_code; long protocol; @@ -2338,7 +2534,7 @@ array_array___reduce_ex___impl(arrayobject *self, PyTypeObject *cls, return NULL; } result = Py_BuildValue( - "O(CO)O", Py_TYPE(self), typecode, list, dict); + "O(sO)O", Py_TYPE(self), typecode, list, dict); Py_DECREF(list); Py_DECREF(dict); return result; @@ -2352,7 +2548,7 @@ array_array___reduce_ex___impl(arrayobject *self, PyTypeObject *cls, assert(state->array_reconstructor != NULL); result = Py_BuildValue( - "O(OCiN)O", state->array_reconstructor, Py_TYPE(self), typecode, + "O(OsiN)O", state->array_reconstructor, Py_TYPE(self), typecode, mformat_code, array_str, dict); Py_DECREF(dict); return result; @@ -2362,8 +2558,8 @@ static PyObject * array_get_typecode(PyObject *op, void *Py_UNUSED(closure)) { arrayobject *a = arrayobject_CAST(op); - char typecode = a->ob_descr->typecode; - return PyUnicode_FromOrdinal(typecode); + const char *typecode = a->ob_descr->typecode; + return PyUnicode_FromString(typecode); } static PyObject * @@ -2405,14 +2601,15 @@ static PyMethodDef array_methods[] = { ARRAY_ARRAY_TOBYTES_METHODDEF ARRAY_ARRAY_TOUNICODE_METHODDEF ARRAY_ARRAY___SIZEOF___METHODDEF - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("Arrays are generic over the type of their elements")}, {NULL, NULL} /* sentinel */ }; static PyObject * array_repr(PyObject *op) { - char typecode; + const char *typecode; PyObject *s, *v = NULL; Py_ssize_t len; arrayobject *a = arrayobject_CAST(op); @@ -2420,10 +2617,10 @@ array_repr(PyObject *op) len = Py_SIZE(a); typecode = a->ob_descr->typecode; if (len == 0) { - return PyUnicode_FromFormat("%s('%c')", - _PyType_Name(Py_TYPE(a)), (int)typecode); + return PyUnicode_FromFormat("%s('%s')", + _PyType_Name(Py_TYPE(a)), typecode); } - if (typecode == 'u' || typecode == 'w') { + if (strcmp(typecode, "w") == 0) { v = array_array_tounicode_impl(a); } else { v = array_array_tolist_impl(a); @@ -2431,8 +2628,8 @@ array_repr(PyObject *op) if (v == NULL) return NULL; - s = PyUnicode_FromFormat("%s('%c', %R)", - _PyType_Name(Py_TYPE(a)), (int)typecode, v); + s = PyUnicode_FromFormat("%s('%s', %R)", + _PyType_Name(Py_TYPE(a)), typecode, v); Py_DECREF(v); return s; } @@ -2660,7 +2857,7 @@ array_ass_subscr(PyObject *op, PyObject *item, PyObject *value) } } -static const void *emptybuf = ""; +static const _Py_ALIGNED_DEF(ALIGNOF_MAX_ALIGN_T, char) emptybuf[] = ""; static int @@ -2692,12 +2889,7 @@ array_buffer_getbuf(PyObject *op, Py_buffer *view, int flags) view->format = NULL; view->internal = NULL; if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { - view->format = (char *)self->ob_descr->formats; -#ifdef Py_UNICODE_WIDE - if (self->ob_descr->typecode == 'u') { - view->format = "w"; - } -#endif + view->format = (char *)self->ob_descr->typecode; } self->ob_exports++; @@ -2715,7 +2907,7 @@ static PyObject * array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { array_state *state = find_array_state_by_type(type); - int c; + const char *s; PyObject *initial = NULL, *it = NULL; const struct arraydescr *descr; @@ -2724,36 +2916,27 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) !_PyArg_NoKeywords("array.array", kwds)) return NULL; - if (!PyArg_ParseTuple(args, "C|O:array", &c, &initial)) + if (!PyArg_ParseTuple(args, "s|O:array", &s, &initial)) return NULL; - if (PySys_Audit("array.__new__", "CO", - c, initial ? initial : Py_None) < 0) { + if (PySys_Audit("array.__new__", "sO", + s, initial ? initial : Py_None) < 0) { return NULL; } - if (c == 'u') { - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "The 'u' type code is deprecated and " - "will be removed in Python 3.16", - 1)) { - return NULL; - } - } - - bool is_unicode = c == 'u' || c == 'w'; + bool is_unicode = (strcmp(s, "w") == 0); if (initial && !is_unicode) { if (PyUnicode_Check(initial)) { PyErr_Format(PyExc_TypeError, "cannot use a str to initialize " - "an array with typecode '%c'", c); + "an array with typecode '%s'", s); return NULL; } else if (array_Check(initial, state)) { - int ic = ((arrayobject*)initial)->ob_descr->typecode; - if (ic == 'u' || ic == 'w') { + const char *is = ((arrayobject*)initial)->ob_descr->typecode; + if (strcmp(is, "w") == 0) { PyErr_Format(PyExc_TypeError, "cannot use a unicode array to " - "initialize an array with typecode '%c'", c); + "initialize an array with typecode '%s'", s); return NULL; } } @@ -2765,7 +2948,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) || PyTuple_Check(initial) || (is_unicode && PyUnicode_Check(initial)) || (array_Check(initial, state) - && c == ((arrayobject*)initial)->ob_descr->typecode))) { + && strcmp(s, ((arrayobject*)initial)->ob_descr->typecode) == 0))) { it = PyObject_GetIter(initial); if (it == NULL) return NULL; @@ -2776,8 +2959,8 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) */ initial = NULL; } - for (descr = descriptors; descr->typecode != '\0'; descr++) { - if (descr->typecode == c) { + for (descr = descriptors; descr->typecode[0] != 0; descr++) { + if (strcmp(descr->typecode, s) == 0) { PyObject *a; Py_ssize_t len; @@ -2791,8 +2974,10 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) len = 0; a = newarrayobject(type, len, descr); - if (a == NULL) + if (a == NULL) { + Py_XDECREF(it); return NULL; + } if (len > 0 && !array_Check(initial, state)) { Py_ssize_t i; @@ -2801,11 +2986,13 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PySequence_GetItem(initial, i); if (v == NULL) { Py_DECREF(a); + Py_XDECREF(it); return NULL; } if (setarrayitem(a, i, v) != 0) { Py_DECREF(v); Py_DECREF(a); + Py_XDECREF(it); return NULL; } Py_DECREF(v); @@ -2817,43 +3004,27 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) v = array_array_frombytes((PyObject *)a, initial); if (v == NULL) { Py_DECREF(a); + Py_XDECREF(it); return NULL; } Py_DECREF(v); } else if (initial != NULL && PyUnicode_Check(initial)) { - if (c == 'u') { - Py_ssize_t n; - wchar_t *ustr = PyUnicode_AsWideCharString(initial, &n); - if (ustr == NULL) { - Py_DECREF(a); - return NULL; - } - - if (n > 0) { - arrayobject *self = (arrayobject *)a; - // self->ob_item may be NULL but it is safe. - PyMem_Free(self->ob_item); - self->ob_item = (char *)ustr; - Py_SET_SIZE(self, n); - self->allocated = n; - } + assert(strcmp(descr->typecode, "w") == 0); + Py_ssize_t n = PyUnicode_GET_LENGTH(initial); + Py_UCS4 *ustr = PyUnicode_AsUCS4Copy(initial); + if (ustr == NULL) { + Py_DECREF(a); + Py_XDECREF(it); + return NULL; } - else { // c == 'w' - Py_ssize_t n = PyUnicode_GET_LENGTH(initial); - Py_UCS4 *ustr = PyUnicode_AsUCS4Copy(initial); - if (ustr == NULL) { - Py_DECREF(a); - return NULL; - } - arrayobject *self = (arrayobject *)a; - // self->ob_item may be NULL but it is safe. - PyMem_Free(self->ob_item); - self->ob_item = (char *)ustr; - Py_SET_SIZE(self, n); - self->allocated = n; - } + arrayobject *self = (arrayobject *)a; + // self->ob_item may be NULL but it is safe. + PyMem_Free(self->ob_item); + self->ob_item = (char *)ustr; + Py_SET_SIZE(self, n); + self->allocated = n; } else if (initial != NULL && array_Check(initial, state) && len > 0) { arrayobject *self = (arrayobject *)a; @@ -2871,8 +3042,19 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return a; } } - PyErr_SetString(PyExc_ValueError, - "bad typecode (must be b, B, u, w, h, H, i, I, l, L, q, Q, f or d)"); + Py_XDECREF(it); + + PyObject *sep = PyUnicode_FromString(", "); + if (sep == NULL) { + return NULL; + } + PyObject *msg = PyObject_CallMethod(sep, "join", "(O)", state->typecodes); + Py_DECREF(sep); + if (msg == NULL) { + return NULL; + } + PyErr_Format(PyExc_ValueError, "bad typecode (must be: %S)", msg); + Py_DECREF(msg); return NULL; } @@ -2880,7 +3062,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyDoc_STRVAR(module_doc, "This module defines an object type which can efficiently represent\n\ an array of basic values: characters, integers, floating-point\n\ -numbers. Arrays are sequence types and behave very much like lists,\n\ +numbers, complex numbers. Arrays are sequence types and behave very much like lists,\n\ except that the type of objects stored in them is constrained.\n"); PyDoc_STRVAR(arraytype_doc, @@ -2892,13 +3074,12 @@ string or iterable over elements of the appropriate type.\n\ \n\ Arrays represent basic values and behave very much like lists, except\n\ the type of objects stored in them is constrained. The type is specified\n\ -at object creation time by using a type code, which is a single character.\n\ +at object creation time by using a type code, which is a string.\n\ The following type codes are defined:\n\ \n\ Type code C Type Minimum size in bytes\n\ 'b' signed integer 1\n\ 'B' unsigned integer 1\n\ - 'u' Unicode character 2 (see note)\n\ 'h' signed integer 2\n\ 'H' unsigned integer 2\n\ 'i' signed integer 2\n\ @@ -2907,11 +3088,11 @@ The following type codes are defined:\n\ 'L' unsigned integer 4\n\ 'q' signed integer 8 (see note)\n\ 'Q' unsigned integer 8 (see note)\n\ + 'e' 16-bit IEEE floats 2\n\ 'f' floating-point 4\n\ 'd' floating-point 8\n\ -\n\ -NOTE: The 'u' typecode corresponds to Python's unicode character. On\n\ -narrow builds this is 2-bytes on wide builds this is 4-bytes.\n\ + 'Zf' float complex 8\n\ + 'Zd' double complex 16\n\ \n\ NOTE: The 'q' and 'Q' type codes are only available if the platform\n\ C compiler used to build Python supports 'long long', or, on Windows,\n\ @@ -2961,7 +3142,7 @@ static PyType_Slot array_slots[] = { {Py_tp_getset, array_getsets}, {Py_tp_alloc, PyType_GenericAlloc}, {Py_tp_new, array_new}, - {Py_tp_traverse, array_tp_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, /* as sequence */ {Py_sq_length, array_length}, @@ -3154,6 +3335,7 @@ array_traverse(PyObject *module, visitproc visit, void *arg) Py_VISIT(state->ArrayType); Py_VISIT(state->ArrayIterType); Py_VISIT(state->array_reconstructor); + Py_VISIT(state->typecodes); return 0; } @@ -3168,6 +3350,7 @@ array_clear(PyObject *module) Py_CLEAR(state->str_write); Py_CLEAR(state->str___dict__); Py_CLEAR(state->str_iter); + Py_CLEAR(state->typecodes); return 0; } @@ -3204,7 +3387,6 @@ static int array_modexec(PyObject *m) { array_state *state = get_array_state(m); - char buffer[Py_ARRAY_LENGTH(descriptors)], *p; PyObject *typecodes; const struct arraydescr *descr; @@ -3243,12 +3425,30 @@ array_modexec(PyObject *m) return -1; } - p = buffer; - for (descr = descriptors; descr->typecode != '\0'; descr++) { - *p++ = (char)descr->typecode; + typecodes = PyList_New(0); + if (typecodes == NULL) { + return -1; + } + for (descr = descriptors; descr->typecode[0] != 0; descr++) { + PyObject *typecode = PyUnicode_DecodeASCII(descr->typecode, strlen(descr->typecode), NULL); + if (typecode == NULL) { + Py_DECREF(typecodes); + return -1; + } + int res = PyList_Append(typecodes, typecode); + Py_DECREF(typecode); + if (res < 0) { + Py_DECREF(typecodes); + return -1; + } + } + PyObject *tuple = PyList_AsTuple(typecodes); + Py_DECREF(typecodes); + if (tuple == NULL) { + return -1; } - typecodes = PyUnicode_DecodeASCII(buffer, p - buffer, NULL); - if (PyModule_Add(m, "typecodes", typecodes) < 0) { + state->typecodes = Py_NewRef(tuple); + if (PyModule_Add(m, "typecodes", tuple) < 0) { return -1; } @@ -3256,6 +3456,7 @@ array_modexec(PyObject *m) } static PyModuleDef_Slot arrayslots[] = { + _Py_ABI_SLOT, {Py_mod_exec, array_modexec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/atexitmodule.c b/Modules/atexitmodule.c index 4b068967a6ca6e..177b09d3dafbd9 100644 --- a/Modules/atexitmodule.c +++ b/Modules/atexitmodule.c @@ -112,6 +112,7 @@ atexit_callfuncs(struct atexit_state *state) { PyErr_FormatUnraisable("Exception ignored while " "copying atexit callbacks"); + atexit_cleanup(state); return; } @@ -184,6 +185,9 @@ atexit_register(PyObject *module, PyObject *args, PyObject *kwargs) return NULL; } PyObject *func_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); + if (func_args == NULL) { + return NULL; + } PyObject *func_kwargs = kwargs; if (func_kwargs == NULL) @@ -191,6 +195,7 @@ atexit_register(PyObject *module, PyObject *args, PyObject *kwargs) func_kwargs = Py_None; } PyObject *callback = PyTuple_Pack(3, func, func_args, func_kwargs); + Py_DECREF(func_args); if (callback == NULL) { return NULL; @@ -255,21 +260,36 @@ atexit_ncallbacks(PyObject *module, PyObject *Py_UNUSED(dummy)) static int atexit_unregister_locked(PyObject *callbacks, PyObject *func) { - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(callbacks); ++i) { - PyObject *tuple = PyList_GET_ITEM(callbacks, i); + for (Py_ssize_t i = PyList_GET_SIZE(callbacks) - 1; i >= 0; --i) { + PyObject *tuple = Py_NewRef(PyList_GET_ITEM(callbacks, i)); assert(PyTuple_CheckExact(tuple)); PyObject *to_compare = PyTuple_GET_ITEM(tuple, 0); int cmp = PyObject_RichCompareBool(func, to_compare, Py_EQ); - if (cmp < 0) - { + if (cmp < 0) { + Py_DECREF(tuple); return -1; } if (cmp == 1) { // We found a callback! - if (PyList_SetSlice(callbacks, i, i + 1, NULL) < 0) { - return -1; + // But its index could have changed if it or other callbacks were + // unregistered during the comparison. + Py_ssize_t j = PyList_GET_SIZE(callbacks) - 1; + j = Py_MIN(j, i); + for (; j >= 0; --j) { + if (PyList_GET_ITEM(callbacks, j) == tuple) { + // We found the callback index! For real! + if (PyList_SetSlice(callbacks, j, j + 1, NULL) < 0) { + Py_DECREF(tuple); + return -1; + } + i = j; + break; + } } - --i; + } + Py_DECREF(tuple); + if (i >= PyList_GET_SIZE(callbacks)) { + i = PyList_GET_SIZE(callbacks); } } @@ -321,6 +341,7 @@ Two public functions, register and unregister, are defined.\n\ "); static PyModuleDef_Slot atexitmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} diff --git a/Modules/binascii.c b/Modules/binascii.c index 6bb01d148b6faa..0e7af135a6f6ce 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -67,6 +67,7 @@ typedef struct binascii_state { PyObject *Error; PyObject *Incomplete; + PyObject *reverse_table_cache; } binascii_state; static inline binascii_state * @@ -76,11 +77,12 @@ get_binascii_state(PyObject *module) } -static const unsigned char table_a2b_base64[] = { +/* Align to 64 bytes for L1 cache line friendliness */ +static const _Py_ALIGNED_DEF(64, unsigned char) table_a2b_base64[] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, - 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, /* Note PAD->0 */ + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,64,-1,-1, /* PAD->64 detected by fast path */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, @@ -101,9 +103,272 @@ static const unsigned char table_a2b_base64[] = { /* Max binary chunk size; limited only by available memory */ #define BASE64_MAXBIN ((PY_SSIZE_T_MAX - 3) / 2) -static const unsigned char table_b2a_base64[] = +/* + * Fast base64 encoding/decoding helpers. + * + * Process complete groups without loop-carried dependencies. + */ + +/* Align to 64 bytes for L1 cache line friendliness */ +static const _Py_ALIGNED_DEF(64, unsigned char) table_b2a_base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +/* Encode 3 bytes into 4 base64 characters. */ +static inline void +base64_encode_trio(const unsigned char *in, unsigned char *out, + const unsigned char *table) +{ + unsigned int combined = ((unsigned int)in[0] << 16) | + ((unsigned int)in[1] << 8) | + (unsigned int)in[2]; + out[0] = table[(combined >> 18) & 0x3f]; + out[1] = table[(combined >> 12) & 0x3f]; + out[2] = table[(combined >> 6) & 0x3f]; + out[3] = table[combined & 0x3f]; +} + +/* Encode multiple complete 3-byte groups. + * Returns the number of input bytes processed (always a multiple of 3). + */ +static inline Py_ssize_t +base64_encode_fast(const unsigned char *in, Py_ssize_t in_len, + unsigned char *out, const unsigned char *table) +{ + Py_ssize_t n_trios = in_len / 3; + const unsigned char *in_end = in + n_trios * 3; + + while (in < in_end) { + base64_encode_trio(in, out, table); + in += 3; + out += 4; + } + + return n_trios * 3; +} + +/* Decode 4 base64 characters into 3 bytes. + * Returns 1 on success, 0 if any character is invalid. + */ +static inline int +base64_decode_quad(const unsigned char *in, unsigned char *out, + const unsigned char *table) +{ + unsigned char v0 = table[in[0]]; + unsigned char v1 = table[in[1]]; + unsigned char v2 = table[in[2]]; + unsigned char v3 = table[in[3]]; + + if ((v0 | v1 | v2 | v3) & 0xc0) { + return 0; + } + + out[0] = (v0 << 2) | (v1 >> 4); + out[1] = (v1 << 4) | (v2 >> 2); + out[2] = (v2 << 6) | v3; + return 1; +} + +/* Decode multiple complete 4-character groups (no padding allowed). + * Returns the number of input characters processed. + * Stops at the first invalid character, padding, or incomplete group. + */ +static inline Py_ssize_t +base64_decode_fast(const unsigned char *in, Py_ssize_t in_len, + unsigned char *out, const unsigned char *table) +{ + Py_ssize_t n_quads = in_len / 4; + Py_ssize_t i; + + for (i = 0; i < n_quads; i++) { + if (!base64_decode_quad(in + i * 4, out + i * 3, table)) { + break; + } + } + + return i * 4; +} + + +static const _Py_ALIGNED_DEF(64, unsigned char) table_a2b_base85[] = { + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,62,-1,63, 64,65,66,-1, 67,68,69,70, -1,71,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,72, 73,74,75,76, + 77,10,11,12, 13,14,15,16, 17,18,19,20, 21,22,23,24, + 25,26,27,28, 29,30,31,32, 33,34,35,-1, -1,-1,78,79, + 80,36,37,38, 39,40,41,42, 43,44,45,46, 47,48,49,50, + 51,52,53,54, 55,56,57,58, 59,60,61,81, 82,83,84,-1, + + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, +}; + +static const _Py_ALIGNED_DEF(64, unsigned char) table_a2b_base85_a85[] = { + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,26, 27,28,29,30, + 31,32,33,34, 35,36,37,38, 39,40,41,42, 43,44,45,46, + 47,48,49,50, 51,52,53,54, 55,56,57,58, 59,60,61,62, + 63,64,65,66, 67,68,69,70, 71,72,73,74, 75,76,77,78, + 79,80,81,82, 83,84,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, +}; + +static const _Py_ALIGNED_DEF(64, unsigned char) table_b2a_base85[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; + +static const _Py_ALIGNED_DEF(64, unsigned char) table_b2a_base85_a85[] = + "!\"#$%&\'()*+,-./0123456789:;<=>?@" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu"; + + +#define BASE85_A85_PREFIX '<' +#define BASE85_A85_AFFIX '~' +#define BASE85_A85_SUFFIX '>' +#define BASE85_A85_Z 0x00000000 +#define BASE85_A85_Y 0x20202020 + +/* 85**0 through 85**4, used for canonical encoding checks. */ +static const uint32_t pow85[] = {1, 85, 7225, 614125, 52200625}; + + +static const _Py_ALIGNED_DEF(64, unsigned char) table_a2b_base32[] = { + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,26,27, 28,29,30,31, -1,-1,-1,-1, -1,-1,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, +}; + +static const _Py_ALIGNED_DEF(64, unsigned char) table_b2a_base32[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +#define BASE32_PAD '=' + +/* + * Fast base32 encoding/decoding helpers. + * + * Analogous to the helpers for base64. + */ + +/* Encode 5 bytes into 8 base32 characters. */ +static inline void +base32_encode_quint(const unsigned char *in, unsigned char *out, + const unsigned char table[]) +{ + uint64_t combined = ((uint64_t)in[0] << 32) | + ((uint64_t)in[1] << 24) | + ((uint64_t)in[2] << 16) | + ((uint64_t)in[3] << 8) | + (uint64_t)in[4]; + out[0] = table[(combined >> 35) & 0x1f]; + out[1] = table[(combined >> 30) & 0x1f]; + out[2] = table[(combined >> 25) & 0x1f]; + out[3] = table[(combined >> 20) & 0x1f]; + out[4] = table[(combined >> 15) & 0x1f]; + out[5] = table[(combined >> 10) & 0x1f]; + out[6] = table[(combined >> 5) & 0x1f]; + out[7] = table[combined & 0x1f]; +} + +/* + * Encode multiple complete 5-byte groups. + * Returns the number of input bytes processed (always a multiple of 5). + */ +static inline Py_ssize_t +base32_encode_fast(const unsigned char *in, Py_ssize_t in_len, + unsigned char *out, const unsigned char table[]) +{ + Py_ssize_t n_quints = in_len / 5; + const unsigned char *in_end = in + n_quints * 5; + + while (in < in_end) { + base32_encode_quint(in, out, table); + in += 5; + out += 8; + } + + return n_quints * 5; +} + +/* + * Decode 8 base32 characters into 5 bytes. + * Returns 1 on success, 0 if any character is invalid. + */ +static inline int +base32_decode_octa(const unsigned char *in, unsigned char *out, + const unsigned char table[]) +{ + unsigned char v0 = table[in[0]]; + unsigned char v1 = table[in[1]]; + unsigned char v2 = table[in[2]]; + unsigned char v3 = table[in[3]]; + unsigned char v4 = table[in[4]]; + unsigned char v5 = table[in[5]]; + unsigned char v6 = table[in[6]]; + unsigned char v7 = table[in[7]]; + + if ((v0 | v1 | v2 | v3 | v4 | v5 | v6 | v7) & 0xe0) { + return 0; + } + + out[0] = (v0 << 3) | (v1 >> 2); + out[1] = (v1 << 6) | (v2 << 1) | (v3 >> 4); + out[2] = (v3 << 4) | (v4 >> 1); + out[3] = (v4 << 7) | (v5 << 2) | (v6 >> 3); + out[4] = (v6 << 5) | v7; + return 1; +} + +/* + * Decode multiple complete 8-character groups (no padding allowed). + * Returns the number of input characters processed. + * Stops at the first invalid character, padding, or incomplete group. + */ +static inline Py_ssize_t +base32_decode_fast(const unsigned char *in, Py_ssize_t in_len, + unsigned char *out, const unsigned char table[]) +{ + Py_ssize_t n_quints = in_len / 8; + Py_ssize_t i; + + for (i = 0; i < n_quints; i++) { + if (!base32_decode_octa(in + i * 8, out + i * 5, table)) { + break; + } + } + + return i * 8; +} + static const unsigned short crctab_hqx[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, @@ -189,6 +454,32 @@ ascii_buffer_converter(PyObject *arg, Py_buffer *buf) return Py_CLEANUP_SUPPORTED; } +/* The function inserts '\n' each width characters, moving the data right. + * It assumes that we allocated enough space for all of the newlines in data. + * Returns the size of the data including the newlines. + */ +static Py_ssize_t +wraplines(unsigned char *data, Py_ssize_t size, size_t width) +{ + if ((size_t)size <= width) { + return size; + } + unsigned char *src = data + size; + Py_ssize_t newlines = (size - 1) / width; + Py_ssize_t line_len = size - newlines * width; + size += newlines; + unsigned char *dst = data + size; + + while ((src -= line_len) != data) { + dst -= line_len; + memmove(dst, src, line_len); + *--dst = '\n'; + line_len = width; + } + assert(dst == data + width); + return size; +} + #include "clinic/binascii.c.h" /*[clinic input] @@ -205,11 +496,9 @@ binascii_a2b_uu_impl(PyObject *module, Py_buffer *data) /*[clinic end generated code: output=e027f8e0b0598742 input=7cafeaf73df63d1c]*/ { const unsigned char *ascii_data; - unsigned char *bin_data; int leftbits = 0; unsigned char this_ch; unsigned int leftchar = 0; - PyObject *rv; Py_ssize_t ascii_len, bin_len; binascii_state *state; @@ -219,13 +508,23 @@ binascii_a2b_uu_impl(PyObject *module, Py_buffer *data) assert(ascii_len >= 0); /* First byte: binary data length (in bytes) */ + if (ascii_len == 0) { + state = get_binascii_state(module); + if (state == NULL) { + return NULL; + } + PyErr_SetString(state->Error, "Missing length byte"); + return NULL; + } bin_len = (*ascii_data++ - ' ') & 077; ascii_len--; /* Allocate the buffer */ - if ( (rv=PyBytes_FromStringAndSize(NULL, bin_len)) == NULL ) + PyBytesWriter *writer = PyBytesWriter_Create(bin_len); + if (writer == NULL) { return NULL; - bin_data = (unsigned char *)PyBytes_AS_STRING(rv); + } + unsigned char *bin_data = PyBytesWriter_GetData(writer); for( ; bin_len > 0 ; ascii_len--, ascii_data++ ) { /* XXX is it really best to add NULs if there's no more data */ @@ -245,11 +544,10 @@ binascii_a2b_uu_impl(PyObject *module, Py_buffer *data) if ( this_ch < ' ' || this_ch > (' ' + 64)) { state = get_binascii_state(module); if (state == NULL) { - return NULL; + goto error; } PyErr_SetString(state->Error, "Illegal char"); - Py_DECREF(rv); - return NULL; + goto error; } this_ch = (this_ch - ' ') & 077; } @@ -276,317 +574,1416 @@ binascii_a2b_uu_impl(PyObject *module, Py_buffer *data) if ( this_ch != ' ' && this_ch != ' '+64 && this_ch != '\n' && this_ch != '\r' ) { state = get_binascii_state(module); - if (state == NULL) { - return NULL; + if (state == NULL) { + goto error; + } + PyErr_SetString(state->Error, "Trailing garbage"); + goto error; + } + } + return PyBytesWriter_Finish(writer); + +error: + PyBytesWriter_Discard(writer); + return NULL; +} + +/*[clinic input] +binascii.b2a_uu + + data: Py_buffer + / + * + backtick: bool = False + +Uuencode line of data. +[clinic start generated code]*/ + +static PyObject * +binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, int backtick) +/*[clinic end generated code: output=b1b99de62d9bbeb8 input=beb27822241095cd]*/ +{ + const unsigned char *bin_data; + int leftbits = 0; + unsigned char this_ch; + unsigned int leftchar = 0; + binascii_state *state; + Py_ssize_t bin_len; + + bin_data = data->buf; + bin_len = data->len; + if ( bin_len > 45 ) { + /* The 45 is a limit that appears in all uuencode's */ + state = get_binascii_state(module); + if (state == NULL) { + return NULL; + } + PyErr_SetString(state->Error, "At most 45 bytes at once"); + return NULL; + } + + /* We're lazy and allocate to much (fixed up later) */ + Py_ssize_t out_len = 2 + (bin_len + 2) / 3 * 4; + PyBytesWriter *writer = PyBytesWriter_Create(out_len); + if (writer == NULL) { + return NULL; + } + unsigned char *ascii_data = PyBytesWriter_GetData(writer); + + /* Store the length */ + if (backtick && !bin_len) + *ascii_data++ = '`'; + else + *ascii_data++ = ' ' + (unsigned char)bin_len; + + for( ; bin_len > 0 || leftbits != 0 ; bin_len--, bin_data++ ) { + /* Shift the data (or padding) into our buffer */ + if ( bin_len > 0 ) /* Data */ + leftchar = (leftchar << 8) | *bin_data; + else /* Padding */ + leftchar <<= 8; + leftbits += 8; + + /* See if there are 6-bit groups ready */ + while ( leftbits >= 6 ) { + this_ch = (leftchar >> (leftbits-6)) & 0x3f; + leftbits -= 6; + if (backtick && !this_ch) + *ascii_data++ = '`'; + else + *ascii_data++ = this_ch + ' '; + } + } + *ascii_data++ = '\n'; /* Append a courtesy newline */ + + return PyBytesWriter_FinishWithPointer(writer, ascii_data); +} + +static PyObject * +get_reverse_table(binascii_state *state, PyObject *alphabet, int size, int padchar) +{ + PyObject *reverse_table; + if (state == NULL) { + return NULL; + } + if (PyBytes_GET_SIZE(alphabet) != size) { + PyErr_Format(PyExc_ValueError, "alphabet must have length %d", size); + return NULL; + } + if (PyDict_GetItemRef(state->reverse_table_cache, alphabet, &reverse_table) < 0) { + return NULL; + } + if (reverse_table == NULL) { + unsigned char out[256]; + memset(out, (unsigned char)-1, 256); + const unsigned char *in = (const unsigned char *)PyBytes_AS_STRING(alphabet); + for (int i = 0; i < size; i++) { + out[in[i]] = i; + } + if (padchar >= 0) { + assert(padchar < 256); + out[padchar] = size; + } + reverse_table = PyBytes_FromStringAndSize((char *)out, 256); + if (reverse_table == NULL) { + return NULL; + } + if (PyDict_SetItem(state->reverse_table_cache, alphabet, reverse_table) < 0) { + Py_DECREF(reverse_table); + return NULL; + } + } + else { + if (!PyBytes_Check(reverse_table) + || PyBytes_GET_SIZE(reverse_table) != 256) + { + PyErr_SetString(PyExc_RuntimeError, "Broken binascii cache"); + Py_DECREF(reverse_table); + return NULL; + } + } + return reverse_table; +} + +typedef unsigned char ignorecache_t[32]; + +static int +ignorechar(unsigned char c, const Py_buffer *ignorechars, + ignorecache_t ignorecache) +{ + if (ignorechars == NULL) { + return 0; + } + if (ignorecache[c >> 3] & (1 << (c & 7))) { + return 1; + } + if (memchr(ignorechars->buf, c, ignorechars->len)) { + ignorecache[c >> 3] |= 1 << (c & 7); + return 1; + } + return 0; +} + +/*[clinic input] +binascii.a2b_base64 + + data: ascii_buffer + / + * + strict_mode: bool(c_default="-1", py_default="<unrepresentable>") = False + When set to true, bytes that are not part of the base64 standard are + not allowed. The same applies to excess data after padding (= / ==). + Set to True by default if ignorechars is specified, False otherwise. + padded: bool = True + When set to false, padding in input is not required. + alphabet: PyBytesObject(c_default="NULL") = BASE64_ALPHABET + ignorechars: Py_buffer = NULL + A byte string containing characters to ignore from the input when + strict_mode is true. + canonical: bool = False + When set to true, reject non-zero padding bits per RFC 4648 section 3.5. + +Decode a line of base64 data. +[clinic start generated code]*/ + +static PyObject * +binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode, + int padded, PyBytesObject *alphabet, + Py_buffer *ignorechars, int canonical) +/*[clinic end generated code: output=77c46dcbf4239527 input=c99096d071deeec8]*/ +{ + assert(data->len >= 0); + + const unsigned char *ascii_data = data->buf; + size_t ascii_len = data->len; + binascii_state *state = NULL; + PyObject *table_obj = NULL; + const unsigned char *table_a2b = table_a2b_base64; + + if (strict_mode == -1) { + strict_mode = (ignorechars->buf != NULL); + } + if (!strict_mode || ignorechars->buf == NULL || ignorechars->len == 0) { + ignorechars = NULL; + } + ignorecache_t ignorecache; + if (ignorechars != NULL) { + memset(ignorecache, 0, sizeof(ignorecache)); + } + + if (alphabet != NULL) { + state = get_binascii_state(module); + table_obj = get_reverse_table(state, (PyObject *)alphabet, 64, BASE64_PAD); + if (table_obj == NULL) { + return NULL; + } + table_a2b = (const unsigned char *)PyBytes_AS_STRING(table_obj); + } + + /* Allocate the buffer */ + Py_ssize_t bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */ + PyBytesWriter *writer = PyBytesWriter_Create(bin_len); + if (writer == NULL) { + Py_XDECREF(table_obj); + return NULL; + } + unsigned char *bin_data = PyBytesWriter_GetData(writer); + +fastpath: + /* Fast path: use optimized decoder for complete quads. + * This works for both strict and non-strict mode for valid input. + * The fast path stops at padding, invalid chars, or incomplete groups. + */ + if (ascii_len >= 4) { + Py_ssize_t fast_chars = base64_decode_fast(ascii_data, (Py_ssize_t)ascii_len, + bin_data, table_a2b); + if (fast_chars > 0) { + ascii_data += fast_chars; + ascii_len -= fast_chars; + bin_data += (fast_chars / 4) * 3; + } + } + + /* Slow path: handle remaining input (padding, invalid chars, partial groups) */ + int quad_pos = 0; + unsigned char leftchar = 0; + int pads = 0; + for (; ascii_len; ascii_data++, ascii_len--) { + unsigned char this_ch = *ascii_data; + + /* Check for pad sequences and ignore + ** the invalid ones. + */ + if (padded && this_ch == BASE64_PAD) { + pads++; + if (quad_pos >= 2 && quad_pos + pads <= 4) { + continue; + } + // See RFC 4648, section 3.3: "specifications MAY ignore the + // pad character, "=", treating it as non-alphabet data, if + // it is present before the end of the encoded data" and + // "the excess pad characters MAY also be ignored." + if (!strict_mode || ignorechar(BASE64_PAD, ignorechars, ignorecache)) { + continue; + } + if (quad_pos == 1) { + /* Set an error below. */ + break; + } + state = get_binascii_state(module); + if (state) { + unsigned char *bin_data_start = PyBytesWriter_GetData(writer); + PyErr_SetString(state->Error, + (quad_pos == 0 && bin_data == bin_data_start) + ? "Leading padding not allowed" + : "Excess padding not allowed"); + } + goto error_end; + } + + unsigned char v = table_a2b[this_ch]; + if (v >= 64) { + // See RFC 4648, section 3.3. + if (strict_mode && !ignorechar(this_ch, ignorechars, ignorecache)) { + state = get_binascii_state(module); + if (state) { + PyErr_SetString(state->Error, + (this_ch == BASE64_PAD) + ? "Padding not allowed" + : "Only base64 data is allowed"); + } + goto error_end; + } + continue; + } + + // Characters that are not '=', in the middle of the padding, are + // not allowed (except when they are). See RFC 4648, section 3.3. + if (pads && strict_mode && + !ignorechar(BASE64_PAD, ignorechars, ignorecache)) + { + state = get_binascii_state(module); + if (state) { + PyErr_SetString(state->Error, (quad_pos + pads == 4) + ? "Excess data after padding" + : "Discontinuous padding not allowed"); + } + goto error_end; + } + pads = 0; + + switch (quad_pos) { + case 0: + quad_pos = 1; + leftchar = v; + break; + case 1: + quad_pos = 2; + *bin_data++ = (leftchar << 2) | (v >> 4); + leftchar = v & 0x0f; + break; + case 2: + quad_pos = 3; + *bin_data++ = (leftchar << 4) | (v >> 2); + leftchar = v & 0x03; + break; + case 3: + quad_pos = 0; + *bin_data++ = (leftchar << 6) | (v); + leftchar = 0; + ascii_data++; + ascii_len--; + goto fastpath; + } + } + + if (quad_pos == 1) { + /* There is exactly one extra valid, non-padding, base64 character. + ** This is an invalid length, as there is no possible input that + ** could encoded into such a base64 string. + */ + state = get_binascii_state(module); + if (state) { + unsigned char *bin_data_start = PyBytesWriter_GetData(writer); + PyErr_Format(state->Error, + "Invalid base64-encoded string: " + "number of data characters (%zd) cannot be 1 more " + "than a multiple of 4", + (bin_data - bin_data_start) / 3 * 4 + 1); + } + goto error_end; + } + + if (padded && quad_pos != 0 && quad_pos + pads < 4) { + state = get_binascii_state(module); + if (state) { + PyErr_SetString(state->Error, "Incorrect padding"); + } + goto error_end; + } + + /* https://datatracker.ietf.org/doc/html/rfc4648.html#section-3.5 + * Decoders MAY reject non-zero padding bits. */ + if (canonical && leftchar != 0) { + state = get_binascii_state(module); + if (state) { + PyErr_SetString(state->Error, "Non-zero padding bits"); + } + goto error_end; + } + + Py_XDECREF(table_obj); + return PyBytesWriter_FinishWithPointer(writer, bin_data); + +error_end: + Py_XDECREF(table_obj); + PyBytesWriter_Discard(writer); + return NULL; +} + + +/*[clinic input] +binascii.b2a_base64 + + data: Py_buffer + / + * + padded: bool = True + When set to false, omit padding in the output. + wrapcol: size_t = 0 + newline: bool = True + alphabet: Py_buffer(c_default="{NULL, NULL}") = BASE64_ALPHABET + +Base64-code line of data. +[clinic start generated code]*/ + +static PyObject * +binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, int padded, + size_t wrapcol, int newline, Py_buffer *alphabet) +/*[clinic end generated code: output=a2057b906dc201ab input=cfa33ad73051d3f7]*/ +{ + const unsigned char *table_b2a = table_b2a_base64; + const unsigned char *bin_data = data->buf; + Py_ssize_t bin_len = data->len; + assert(bin_len >= 0); + + if (alphabet->buf != NULL) { + if (alphabet->len != 64) { + PyErr_SetString(PyExc_ValueError, "alphabet must have length 64"); + return NULL; + } + table_b2a = alphabet->buf; + } + /* Each group of 3 bytes (rounded up) gets encoded as 4 characters, + * not counting newlines. + * Note that 'b' gets encoded as 'Yg==' (1 in, 4 out). + * + * Use unsigned integer arithmetic to avoid signed integer overflow. + */ + size_t out_len = ((size_t)bin_len + 2u) / 3u * 4u; + unsigned int pads = (3 - (bin_len % 3)) % 3 * 4 / 3; + if (!padded) { + out_len -= pads; + pads = 0; + } + if (wrapcol && out_len) { + /* Each line should encode a whole number of bytes. */ + wrapcol = wrapcol < 4 ? 4 : wrapcol / 4 * 4; + out_len += (out_len - 1u) / wrapcol; + } + if (newline) { + out_len++; + } + if (out_len > PY_SSIZE_T_MAX) { + binascii_state *state = get_binascii_state(module); + if (state == NULL) { + return NULL; + } + PyErr_SetString(state->Error, "Too much data for base64"); + return NULL; + } + PyBytesWriter *writer = PyBytesWriter_Create(out_len); + if (writer == NULL) { + return NULL; + } + unsigned char *ascii_data = PyBytesWriter_GetData(writer); + + /* Use the optimized fast path for complete 3-byte groups */ + Py_ssize_t fast_bytes = base64_encode_fast(bin_data, bin_len, ascii_data, + table_b2a); + bin_data += fast_bytes; + ascii_data += (fast_bytes / 3) * 4; + bin_len -= fast_bytes; + + /* Handle remaining 0-2 bytes */ + if (bin_len == 1) { + /* 1 byte remaining: produces 2 base64 chars + 2 padding */ + assert(!padded || pads == 2); + unsigned int val = bin_data[0]; + *ascii_data++ = table_b2a[(val >> 2) & 0x3f]; + *ascii_data++ = table_b2a[(val << 4) & 0x3f]; + } + else if (bin_len == 2) { + /* 2 bytes remaining: produces 3 base64 chars + 1 padding */ + assert(!padded || pads == 1); + unsigned int val = ((unsigned int)bin_data[0] << 8) | bin_data[1]; + *ascii_data++ = table_b2a[(val >> 10) & 0x3f]; + *ascii_data++ = table_b2a[(val >> 4) & 0x3f]; + *ascii_data++ = table_b2a[(val << 2) & 0x3f]; + } + else { + assert(pads == 0); + } + for (; pads; pads--) { + *ascii_data++ = BASE64_PAD; + } + + if (wrapcol) { + unsigned char *start = PyBytesWriter_GetData(writer); + ascii_data = start + wraplines(start, ascii_data - start, wrapcol); + } + if (newline) + *ascii_data++ = '\n'; /* Append a courtesy newline */ + + return PyBytesWriter_FinishWithPointer(writer, ascii_data); +} + +/*[clinic input] +binascii.a2b_ascii85 + + data: ascii_buffer + / + * + foldspaces: bool = False + Allow 'y' as a short form encoding four spaces. + adobe: bool = False + Expect data to be terminated with '~>' as in Adobe Ascii85, and + optionally accept leading '<~'. + ignorechars: Py_buffer = b'' + A byte string containing characters to ignore from the input. + canonical: bool = False + When set to true, reject non-canonical encodings. + +Decode Ascii85 data. +[clinic start generated code]*/ + +static PyObject * +binascii_a2b_ascii85_impl(PyObject *module, Py_buffer *data, int foldspaces, + int adobe, Py_buffer *ignorechars, int canonical) +/*[clinic end generated code: output=09b35f1eac531357 input=08eab2e53c62f1a8]*/ +{ + const unsigned char *ascii_data = data->buf; + Py_ssize_t ascii_len = data->len; + binascii_state *state = NULL; + + assert(ascii_len >= 0); + + /* Consume Ascii85 prefix and suffix if present. */ + if (adobe) { + if (ascii_len < 2 + || ascii_data[ascii_len - 2] != BASE85_A85_AFFIX + || ascii_data[ascii_len - 1] != BASE85_A85_SUFFIX) + { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_SetString(state->Error, + "Ascii85 encoded byte sequences must end with b'~>'"); + } + return NULL; + } + ascii_len -= 2; + if (ascii_len >= 2 + && ascii_data[0] == BASE85_A85_PREFIX + && ascii_data[1] == BASE85_A85_AFFIX) + { + ascii_data += 2; + ascii_len -= 2; + } + } + + ignorecache_t ignorecache; + memset(ignorecache, 0, sizeof(ignorecache)); + + /* Allocate output buffer. */ + size_t bin_len = ascii_len; + unsigned char this_ch = 0; + size_t count_yz = 0; + for (Py_ssize_t i = 0; i < ascii_len; i++) { + this_ch = ascii_data[i]; + if (this_ch == 'y' || this_ch == 'z') { + count_yz++; + } + } + bin_len = (bin_len - count_yz + 4) / 5 * 4; + if (count_yz > (PY_SSIZE_T_MAX - bin_len) / 4) { + binascii_state *state = get_binascii_state(module); + if (state == NULL) { + return NULL; + } + PyErr_SetString(state->Error, "Too much Ascii85 data"); + return NULL; + } + bin_len += count_yz * 4; + + PyBytesWriter *writer = PyBytesWriter_Create(bin_len); + if (writer == NULL) { + return NULL; + } + unsigned char *bin_data = PyBytesWriter_GetData(writer); + + uint32_t leftchar = 0; + int group_pos = 0; + int from_z = 0; /* true when current group came from 'z' shorthand */ + for (; ascii_len > 0 || group_pos != 0; ascii_len--, ascii_data++) { + /* Shift (in radix-85) data or padding into our buffer. */ + unsigned char this_digit; + if (ascii_len > 0) { + this_ch = *ascii_data; + this_digit = table_a2b_base85_a85[this_ch]; + } + else { + /* Pad with largest radix-85 digit when decoding. */ + this_digit = 84; + } + if (this_digit < 85) { + if (group_pos == 4 + && (leftchar > UINT32_MAX / 85 + || leftchar * 85 > UINT32_MAX - this_digit)) + { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_SetString(state->Error, "Ascii85 overflow"); + } + goto error; + } + leftchar = leftchar * 85 + this_digit; + group_pos++; + } + else if ((this_ch == 'y' && foldspaces) || this_ch == 'z') { + if (group_pos != 0) { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_Format(state->Error, + "'%c' inside Ascii85 5-tuple", this_ch); + } + goto error; + } + leftchar = this_ch == 'y' ? BASE85_A85_Y : BASE85_A85_Z; + from_z = (this_ch == 'z'); + group_pos = 5; + } + else if (!ignorechar(this_ch, ignorechars, ignorecache)) { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_Format(state->Error, + "Non-Ascii85 digit found: %c", this_ch); + } + goto error; + } + + /* Wait until buffer is full. */ + if (group_pos != 5) { + continue; + } + + /* Write current chunk. */ + int chunk_len = ascii_len < 1 ? 3 + (int)ascii_len : 4; + + /* A final partial 5-tuple containing only one character is an + * encoding violation per the PLRM spec; reject unconditionally. */ + if (chunk_len == 0) { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_SetString(state->Error, + "Incomplete Ascii85 group"); + } + goto error; + } + + for (int i = 0; i < chunk_len; i++) { + *bin_data++ = (leftchar >> (24 - 8 * i)) & 0xff; + } + + if (canonical) { + /* The PLRM spec requires all-zero groups to use the 'z' + * abbreviation. Reject '!!!!!' (five zero digits). */ + if (chunk_len == 4 && leftchar == 0 && !from_z) { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_SetString(state->Error, + "Non-canonical encoding, " + "use 'z' for all-zero groups"); + } + goto error; + } + /* Reject non-canonical partial groups. + * + * A partial group of N chars (2-4) encodes N-1 bytes. + * The decoder pads missing chars with digit 84 (the max). + * The encoder produces the unique N chars for those bytes + * by zero-padding the bytes to a uint32 and taking the + * leading N base-85 digits. Two encodings are equivalent + * iff they yield the same quotient when divided by + * 85**(5-N). */ + if (chunk_len < 4) { + int n_pad = 4 - chunk_len; + uint32_t canonical_top = + (leftchar >> (n_pad * 8)) << (n_pad * 8); + if (canonical_top / pow85[n_pad] + != leftchar / pow85[n_pad]) + { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_SetString(state->Error, + "Non-zero padding bits"); + } + goto error; + } + } + } + + from_z = 0; + group_pos = 0; + leftchar = 0; + } + + return PyBytesWriter_FinishWithPointer(writer, bin_data); + +error: + PyBytesWriter_Discard(writer); + return NULL; +} + +/*[clinic input] +binascii.b2a_ascii85 + + data: Py_buffer + / + * + foldspaces: bool = False + Emit 'y' as a short form encoding four spaces. + wrapcol: size_t = 0 + Split result into lines of provided width. + pad: bool = False + Retain zero-padding bytes at end of output. + adobe: bool = False + Wrap result in '<~' and '~>' as in Adobe Ascii85. + +Ascii85-encode data. +[clinic start generated code]*/ + +static PyObject * +binascii_b2a_ascii85_impl(PyObject *module, Py_buffer *data, int foldspaces, + size_t wrapcol, int pad, int adobe) +/*[clinic end generated code: output=5ce8fdee843073f4 input=a77e31d63517bf19]*/ +{ + const unsigned char *bin_data = data->buf; + Py_ssize_t bin_len = data->len; + + assert(bin_len >= 0); + + if (adobe && wrapcol == 1) { + wrapcol = 2; + } + + /* Allocate output buffer. */ + size_t out_len = ((size_t)bin_len + 3) / 4 * 5; + if (adobe) { + out_len += 4; + } + if (!pad && (bin_len % 4)) { + out_len -= 4 - (bin_len % 4); + } + if (wrapcol && out_len && out_len <= PY_SSIZE_T_MAX) { + out_len += (out_len - 1) / wrapcol; + } + if (out_len > PY_SSIZE_T_MAX) { + binascii_state *state = get_binascii_state(module); + if (state == NULL) { + return NULL; + } + PyErr_SetString(state->Error, "Too much data for Ascii85"); + return NULL; + } + + PyBytesWriter *writer = PyBytesWriter_Create(out_len); + if (writer == NULL) { + return NULL; + } + unsigned char *ascii_data = PyBytesWriter_GetData(writer); + + if (adobe) { + *ascii_data++ = BASE85_A85_PREFIX; + *ascii_data++ = BASE85_A85_AFFIX; + } + + /* Encode all full-length chunks. */ + for (; bin_len >= 4; bin_len -= 4, bin_data += 4) { + uint32_t leftchar = ((uint32_t)bin_data[0] << 24) | (bin_data[1] << 16) | + (bin_data[2] << 8) | bin_data[3]; + if (leftchar == BASE85_A85_Z) { + *ascii_data++ = 'z'; + } + else if (foldspaces && leftchar == BASE85_A85_Y) { + *ascii_data++ = 'y'; + } + else { + ascii_data[4] = table_b2a_base85_a85[leftchar % 85]; + leftchar /= 85; + ascii_data[3] = table_b2a_base85_a85[leftchar % 85]; + leftchar /= 85; + ascii_data[2] = table_b2a_base85_a85[leftchar % 85]; + leftchar /= 85; + ascii_data[1] = table_b2a_base85_a85[leftchar % 85]; + leftchar /= 85; + ascii_data[0] = table_b2a_base85_a85[leftchar]; + + ascii_data += 5; + } + } + + /* Encode partial-length final chunk. */ + if (bin_len > 0) { + uint32_t leftchar = 0; + for (Py_ssize_t i = 0; i < 4; i++) { + leftchar <<= 8; /* Pad with zero when encoding. */ + if (i < bin_len) { + leftchar |= *bin_data++; + } + } + if (pad && leftchar == BASE85_A85_Z) { + *ascii_data++ = 'z'; + } + else { + Py_ssize_t group_len = pad ? 5 : bin_len + 1; + for (Py_ssize_t i = 4; i >= 0; i--) { + if (i < group_len) { + ascii_data[i] = table_b2a_base85_a85[leftchar % 85]; + } + leftchar /= 85; + } + ascii_data += group_len; + } + } + + if (adobe) { + *ascii_data++ = BASE85_A85_AFFIX; + *ascii_data++ = BASE85_A85_SUFFIX; + } + + if (wrapcol && out_len) { + unsigned char *start = PyBytesWriter_GetData(writer); + ascii_data = start + wraplines(start, ascii_data - start, wrapcol); + if (adobe && ascii_data[-2] == '\n') { + assert(ascii_data[-1] == BASE85_A85_SUFFIX); + assert(ascii_data[-3] == BASE85_A85_AFFIX); + ascii_data[-3] = '\n'; + ascii_data[-2] = BASE85_A85_AFFIX; + } + } + + return PyBytesWriter_FinishWithPointer(writer, ascii_data); +} + +/*[clinic input] +binascii.a2b_base85 + + data: ascii_buffer + / + * + alphabet: PyBytesObject(c_default="NULL") = BASE85_ALPHABET + ignorechars: Py_buffer = b'' + A byte string containing characters to ignore from the input. + canonical: bool = False + When set to true, reject non-canonical encodings. + +Decode a line of Base85 data. +[clinic start generated code]*/ + +static PyObject * +binascii_a2b_base85_impl(PyObject *module, Py_buffer *data, + PyBytesObject *alphabet, Py_buffer *ignorechars, + int canonical) +/*[clinic end generated code: output=90dfef0c6b51e5f3 input=2819dc8aeffee5a2]*/ +{ + const unsigned char *ascii_data = data->buf; + Py_ssize_t ascii_len = data->len; + binascii_state *state = NULL; + PyObject *table_obj = NULL; + const unsigned char *table_a2b = table_a2b_base85; + + if (alphabet != NULL) { + state = get_binascii_state(module); + table_obj = get_reverse_table(state, (PyObject *)alphabet, 85, -1); + if (table_obj == NULL) { + return NULL; + } + table_a2b = (const unsigned char *)PyBytes_AS_STRING(table_obj); + } + + if (ignorechars->len == 0) { + ignorechars = NULL; + } + ignorecache_t ignorecache; + if (ignorechars != NULL) { + memset(ignorecache, 0, sizeof(ignorecache)); + } + + assert(ascii_len >= 0); + + /* Allocate output buffer. */ + size_t bin_len = ((size_t)ascii_len + 4) / 5 * 4; + PyBytesWriter *writer = PyBytesWriter_Create(bin_len); + if (writer == NULL) { + Py_XDECREF(table_obj); + return NULL; + } + unsigned char *bin_data = PyBytesWriter_GetData(writer); + + uint32_t leftchar = 0; + int group_pos = 0; + for (; ascii_len > 0 || group_pos != 0; ascii_len--, ascii_data++) { + /* Shift (in radix-85) data or padding into our buffer. */ + unsigned char this_ch; + unsigned char this_digit; + if (ascii_len > 0) { + this_ch = *ascii_data; + this_digit = table_a2b[this_ch]; + } + else { + /* Pad with largest radix-85 digit when decoding. */ + this_digit = 84; + } + if (this_digit < 85) { + if (group_pos == 4 + && (leftchar > UINT32_MAX / 85 + || leftchar * 85 > UINT32_MAX - this_digit)) + { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_Format(state->Error, + "Base85 overflow in hunk starting at byte %zd", + (data->len - ascii_len) / 5 * 5); + } + goto error; + } + leftchar = leftchar * 85 + this_digit; + group_pos++; + } + else if (!ignorechar(this_ch, ignorechars, ignorecache)) { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_Format(state->Error, "bad Base85 character at position %zd", + data->len - ascii_len); + } + goto error; + } + + /* Wait until buffer is full. */ + if (group_pos != 5) { + continue; + } + + /* Write current chunk. */ + int chunk_len = ascii_len < 1 ? 3 + (int)ascii_len : 4; + + /* A 1-char final group is an encoding violation (no conforming + * encoder produces it); reject unconditionally. */ + if (chunk_len == 0) { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_SetString(state->Error, + "Incomplete Base85 group"); + } + goto error; + } + + for (int i = 0; i < chunk_len; i++) { + *bin_data++ = (leftchar >> (24 - 8 * i)) & 0xff; + } + + /* Reject non-canonical encodings in the final group. + * See the comment in a2b_ascii85 for the full explanation. */ + if (canonical && chunk_len < 4) { + int n_pad = 4 - chunk_len; + uint32_t canonical_top = + (leftchar >> (n_pad * 8)) << (n_pad * 8); + if (canonical_top / pow85[n_pad] + != leftchar / pow85[n_pad]) + { + state = get_binascii_state(module); + if (state != NULL) { + PyErr_SetString(state->Error, + "Non-zero padding bits"); + } + goto error; } - PyErr_SetString(state->Error, "Trailing garbage"); - Py_DECREF(rv); - return NULL; } + + group_pos = 0; + leftchar = 0; } - return rv; + + Py_XDECREF(table_obj); + return PyBytesWriter_FinishWithPointer(writer, bin_data); + +error: + PyBytesWriter_Discard(writer); + Py_XDECREF(table_obj); + return NULL; } /*[clinic input] -binascii.b2a_uu +binascii.b2a_base85 data: Py_buffer / * - backtick: bool = False + pad: bool = False + Retain zero-padding bytes at end of output. + wrapcol: size_t = 0 + alphabet: Py_buffer(c_default="{NULL, NULL}") = BASE85_ALPHABET -Uuencode line of data. +Base85-code line of data. [clinic start generated code]*/ static PyObject * -binascii_b2a_uu_impl(PyObject *module, Py_buffer *data, int backtick) -/*[clinic end generated code: output=b1b99de62d9bbeb8 input=beb27822241095cd]*/ +binascii_b2a_base85_impl(PyObject *module, Py_buffer *data, int pad, + size_t wrapcol, Py_buffer *alphabet) +/*[clinic end generated code: output=98b962ed52c776a4 input=54886d05128d41a8]*/ { - unsigned char *ascii_data; - const unsigned char *bin_data; - int leftbits = 0; - unsigned char this_ch; - unsigned int leftchar = 0; - binascii_state *state; - Py_ssize_t bin_len, out_len; - _PyBytesWriter writer; + const unsigned char *bin_data = data->buf; + Py_ssize_t bin_len = data->len; + const unsigned char *table_b2a = table_b2a_base85; - _PyBytesWriter_Init(&writer); - bin_data = data->buf; - bin_len = data->len; - if ( bin_len > 45 ) { - /* The 45 is a limit that appears in all uuencode's */ - state = get_binascii_state(module); + if (alphabet->buf != NULL) { + if (alphabet->len != 85) { + PyErr_SetString(PyExc_ValueError, "alphabet must have length 85"); + return NULL; + } + table_b2a = alphabet->buf; + } + + assert(bin_len >= 0); + + /* Allocate output buffer. */ + size_t out_len = ((size_t)bin_len + 3) / 4 * 5; + if (!pad && (bin_len % 4)) { + out_len -= 4 - (bin_len % 4); + } + if (wrapcol && out_len) { + /* Each line should encode a whole number of bytes. */ + wrapcol = wrapcol < 5 ? 5 : wrapcol / 5 * 5; + out_len += (out_len - 1u) / wrapcol; + } + if (out_len > PY_SSIZE_T_MAX) { + binascii_state *state = get_binascii_state(module); if (state == NULL) { return NULL; } - PyErr_SetString(state->Error, "At most 45 bytes at once"); + PyErr_SetString(state->Error, "Too much data for Base85"); return NULL; } - /* We're lazy and allocate to much (fixed up later) */ - out_len = 2 + (bin_len + 2) / 3 * 4; - ascii_data = _PyBytesWriter_Alloc(&writer, out_len); - if (ascii_data == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(out_len); + if (writer == NULL) { return NULL; + } + unsigned char *ascii_data = PyBytesWriter_GetData(writer); + + /* Encode all full-length chunks. */ + for (; bin_len >= 4; bin_len -= 4, bin_data += 4) { + uint32_t leftchar = ((uint32_t)bin_data[0] << 24) | (bin_data[1] << 16) | + (bin_data[2] << 8) | bin_data[3]; + + ascii_data[4] = table_b2a[leftchar % 85]; + leftchar /= 85; + ascii_data[3] = table_b2a[leftchar % 85]; + leftchar /= 85; + ascii_data[2] = table_b2a[leftchar % 85]; + leftchar /= 85; + ascii_data[1] = table_b2a[leftchar % 85]; + leftchar /= 85; + ascii_data[0] = table_b2a[leftchar]; + + ascii_data += 5; + } - /* Store the length */ - if (backtick && !bin_len) - *ascii_data++ = '`'; - else - *ascii_data++ = ' ' + (unsigned char)bin_len; - - for( ; bin_len > 0 || leftbits != 0 ; bin_len--, bin_data++ ) { - /* Shift the data (or padding) into our buffer */ - if ( bin_len > 0 ) /* Data */ - leftchar = (leftchar << 8) | *bin_data; - else /* Padding */ - leftchar <<= 8; - leftbits += 8; - - /* See if there are 6-bit groups ready */ - while ( leftbits >= 6 ) { - this_ch = (leftchar >> (leftbits-6)) & 0x3f; - leftbits -= 6; - if (backtick && !this_ch) - *ascii_data++ = '`'; - else - *ascii_data++ = this_ch + ' '; + /* Encode partial-length final chunk. */ + if (bin_len > 0) { + uint32_t leftchar = 0; + for (Py_ssize_t i = 0; i < 4; i++) { + leftchar <<= 8; /* Pad with zero when encoding. */ + if (i < bin_len) { + leftchar |= *bin_data++; + } } + Py_ssize_t group_len = pad ? 5 : bin_len + 1; + for (Py_ssize_t i = 4; i >= 0; i--) { + if (i < group_len) { + ascii_data[i] = table_b2a[leftchar % 85]; + } + leftchar /= 85; + } + ascii_data += group_len; + } + + if (wrapcol && out_len) { + unsigned char *start = PyBytesWriter_GetData(writer); + ascii_data = start + wraplines(start, ascii_data - start, wrapcol); } - *ascii_data++ = '\n'; /* Append a courtesy newline */ - return _PyBytesWriter_Finish(&writer, ascii_data); + return PyBytesWriter_FinishWithPointer(writer, ascii_data); } /*[clinic input] -binascii.a2b_base64 +binascii.a2b_base32 data: ascii_buffer / * - strict_mode: bool = False - -Decode a line of base64 data. - - strict_mode - When set to True, bytes that are not part of the base64 standard are not allowed. - The same applies to excess data after padding (= / ==). + padded: bool = True + When set to false, padding in input is not required. + alphabet: PyBytesObject(c_default="NULL") = BASE32_ALPHABET + ignorechars: Py_buffer = b'' + A byte string containing characters to ignore from the input. + canonical: bool = False + When set to true, reject non-zero padding bits per RFC 4648 section 3.5. + +Decode a line of base32 data. [clinic start generated code]*/ static PyObject * -binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) -/*[clinic end generated code: output=5409557788d4f975 input=c0c15fd0f8f9a62d]*/ +binascii_a2b_base32_impl(PyObject *module, Py_buffer *data, int padded, + PyBytesObject *alphabet, Py_buffer *ignorechars, + int canonical) +/*[clinic end generated code: output=bc70f2bb6001fb55 input=5bfe6d1ea2f30e3b]*/ { - assert(data->len >= 0); - const unsigned char *ascii_data = data->buf; - size_t ascii_len = data->len; + Py_ssize_t ascii_len = data->len; binascii_state *state = NULL; - char padding_started = 0; + PyObject *table_obj = NULL; + const unsigned char *table_a2b = table_a2b_base32; - /* Allocate the buffer */ - Py_ssize_t bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */ - _PyBytesWriter writer; - _PyBytesWriter_Init(&writer); - unsigned char *bin_data = _PyBytesWriter_Alloc(&writer, bin_len); - if (bin_data == NULL) - return NULL; - unsigned char *bin_data_start = bin_data; + assert(ascii_len >= 0); - if (strict_mode && ascii_len > 0 && ascii_data[0] == '=') { + if (alphabet != NULL) { state = get_binascii_state(module); - if (state) { - PyErr_SetString(state->Error, "Leading padding not allowed"); + table_obj = get_reverse_table(state, (PyObject *)alphabet, 32, BASE32_PAD); + if (table_obj == NULL) { + return NULL; } - goto error_end; + table_a2b = (const unsigned char *)PyBytes_AS_STRING(table_obj); } - int quad_pos = 0; + if (ignorechars->len == 0) { + ignorechars = NULL; + } + ignorecache_t ignorecache; + if (ignorechars != NULL) { + memset(ignorecache, 0, sizeof(ignorecache)); + } + + /* Allocate output buffer. */ + size_t bin_len = ((size_t)ascii_len + 7) / 8 * 5; + PyBytesWriter *writer = PyBytesWriter_Create(bin_len); + if (writer == NULL) { + Py_XDECREF(table_obj); + return NULL; + } + unsigned char *bin_data = PyBytesWriter_GetData(writer); + +fastpath: + /* + * Fast path: use optimized decoder for complete octas (groups of 8 bytes). + * The fast path stops at padding, invalid chars, or incomplete octas. + */ + if (ascii_len >= 8) { + Py_ssize_t fast_chars = base32_decode_fast(ascii_data, ascii_len, + bin_data, table_a2b); + if (fast_chars > 0) { + ascii_data += fast_chars; + ascii_len -= fast_chars; + bin_data += (fast_chars / 8) * 5; + } + } + + /* Slow path: handle remaining input (padding, invalid chars, incomplete octas). */ unsigned char leftchar = 0; + int octa_pos = 0; int pads = 0; - for (size_t i = 0; i < ascii_len; i++) { - unsigned char this_ch = ascii_data[i]; + for (; ascii_len; ascii_len--, ascii_data++) { + unsigned char this_ch = *ascii_data; - /* Check for pad sequences and ignore - ** the invalid ones. - */ - if (this_ch == BASE64_PAD) { - padding_started = 1; + /* Check for pad sequences. They may only occur at certain positions. */ + if (padded && this_ch == BASE32_PAD) { + pads++; - if (strict_mode && quad_pos == 0) { - state = get_binascii_state(module); - if (state) { - PyErr_SetString(state->Error, "Excess padding not allowed"); - } - goto error_end; + if ((octa_pos == 2 || octa_pos == 4 || octa_pos == 5 || octa_pos == 7) + && octa_pos + pads <= 8) + { + continue; } - if (quad_pos >= 2 && quad_pos + ++pads >= 4) { - /* A pad sequence means we should not parse more input. - ** We've already interpreted the data from the quad at this point. - ** in strict mode, an error should raise if there's excess data after the padding. - */ - if (strict_mode && i + 1 < ascii_len) { - state = get_binascii_state(module); - if (state) { - PyErr_SetString(state->Error, "Excess data after padding"); - } - goto error_end; - } - - goto done; + // See RFC 4648, section 3.3: "specifications MAY ignore the + // pad character, "=", treating it as non-alphabet data, if + // it is present before the end of the encoded data" and + // "the excess pad characters MAY also be ignored." + if (ignorechar(BASE32_PAD, ignorechars, ignorecache)) { + continue; } - continue; + if (octa_pos == 1 || octa_pos == 3 || octa_pos == 6) { + /* Set an error below. */ + break; + } + state = get_binascii_state(module); + if (state) { + unsigned char *bin_data_start = PyBytesWriter_GetData(writer); + PyErr_SetString(state->Error, + (octa_pos == 0 && bin_data == bin_data_start) + ? "Leading padding not allowed" + : "Excess padding not allowed"); + } + goto error; } - this_ch = table_a2b_base64[this_ch]; - if (this_ch >= 64) { - if (strict_mode) { + unsigned char v = table_a2b[this_ch]; + if (v >= 32) { + // See RFC 4648, section 3.3. + if (!ignorechar(this_ch, ignorechars, ignorecache)) { state = get_binascii_state(module); if (state) { - PyErr_SetString(state->Error, "Only base64 data is allowed"); + PyErr_SetString(state->Error, + (this_ch == BASE32_PAD) + ? "Padding not allowed" + : "Only base32 data is allowed"); } - goto error_end; + goto error; } continue; } - // Characters that are not '=', in the middle of the padding, are not allowed - if (strict_mode && padding_started) { + // Characters that are not '=', in the middle of the padding, are + // not allowed (except when they are). See RFC 4648, section 3.3. + if (pads && !ignorechar(BASE32_PAD, ignorechars, ignorecache)) { state = get_binascii_state(module); if (state) { - PyErr_SetString(state->Error, "Discontinuous padding not allowed"); + PyErr_SetString(state->Error, (octa_pos + pads == 8) + ? "Excess data after padding" + : "Discontinuous padding not allowed"); } - goto error_end; + goto error; } - pads = 0; - switch (quad_pos) { + switch (octa_pos) { case 0: - quad_pos = 1; - leftchar = this_ch; + octa_pos = 1; + leftchar = v; break; case 1: - quad_pos = 2; - *bin_data++ = (leftchar << 2) | (this_ch >> 4); - leftchar = this_ch & 0x0f; + octa_pos = 2; + *bin_data++ = (leftchar << 3) | (v >> 2); + leftchar = v & 0x03; break; case 2: - quad_pos = 3; - *bin_data++ = (leftchar << 4) | (this_ch >> 2); - leftchar = this_ch & 0x03; + octa_pos = 3; + leftchar = (leftchar << 5) | v; break; case 3: - quad_pos = 0; - *bin_data++ = (leftchar << 6) | (this_ch); - leftchar = 0; + octa_pos = 4; + *bin_data++ = (leftchar << 1) | (v >> 4); + leftchar = v & 0x0f; + break; + case 4: + octa_pos = 5; + *bin_data++ = (leftchar << 4) | (v >> 1); + leftchar = v & 0x01; + break; + case 5: + octa_pos = 6; + leftchar = (leftchar << 5) | v; break; + case 6: + octa_pos = 7; + *bin_data++ = (leftchar << 2) | (v >> 3); + leftchar = v & 0x07; + break; + case 7: + octa_pos = 0; + *bin_data++ = (leftchar << 5) | v; + leftchar = 0; + ascii_data++; + ascii_len--; + goto fastpath; } } - if (quad_pos != 0) { + if (octa_pos == 1 || octa_pos == 3 || octa_pos == 6) { state = get_binascii_state(module); - if (state == NULL) { - /* error already set, from get_binascii_state */ - } else if (quad_pos == 1) { - /* - ** There is exactly one extra valid, non-padding, base64 character. - ** This is an invalid length, as there is no possible input that - ** could encoded into such a base64 string. - */ + if (state) { + unsigned char *bin_data_start = PyBytesWriter_GetData(writer); PyErr_Format(state->Error, - "Invalid base64-encoded string: " - "number of data characters (%zd) cannot be 1 more " - "than a multiple of 4", - (bin_data - bin_data_start) / 3 * 4 + 1); - } else { + "Invalid base32-encoded string: " + "number of data characters (%zd) " + "cannot be 1, 3, or 6 more than a multiple of 8", + (bin_data - bin_data_start) / 5 * 8 + octa_pos); + } + goto error; + } + + if (padded && octa_pos != 0 && octa_pos + pads < 8) { + state = get_binascii_state(module); + if (state) { PyErr_SetString(state->Error, "Incorrect padding"); } - error_end: - _PyBytesWriter_Dealloc(&writer); - return NULL; + goto error; } -done: - return _PyBytesWriter_Finish(&writer, bin_data); -} + /* https://datatracker.ietf.org/doc/html/rfc4648.html#section-3.5 + * Decoders MAY reject non-zero padding bits. */ + if (canonical && leftchar != 0) { + state = get_binascii_state(module); + if (state) { + PyErr_SetString(state->Error, "Non-zero padding bits"); + } + goto error; + } + Py_XDECREF(table_obj); + return PyBytesWriter_FinishWithPointer(writer, bin_data); + +error: + PyBytesWriter_Discard(writer); + Py_XDECREF(table_obj); + return NULL; +} /*[clinic input] -binascii.b2a_base64 +binascii.b2a_base32 data: Py_buffer / * - newline: bool = True + padded: bool = True + When set to false, omit padding in the output. + wrapcol: size_t = 0 + alphabet: Py_buffer(c_default="{NULL, NULL}") = BASE32_ALPHABET -Base64-code line of data. +Base32-code line of data. [clinic start generated code]*/ static PyObject * -binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, int newline) -/*[clinic end generated code: output=4ad62c8e8485d3b3 input=0e20ff59c5f2e3e1]*/ +binascii_b2a_base32_impl(PyObject *module, Py_buffer *data, int padded, + size_t wrapcol, Py_buffer *alphabet) +/*[clinic end generated code: output=acc09e685569aab9 input=1889b0c497a1d3c2]*/ { - unsigned char *ascii_data; - const unsigned char *bin_data; - int leftbits = 0; - unsigned char this_ch; - unsigned int leftchar = 0; - Py_ssize_t bin_len, out_len; - _PyBytesWriter writer; - binascii_state *state; - - bin_data = data->buf; - bin_len = data->len; - _PyBytesWriter_Init(&writer); + const unsigned char *table_b2a = table_b2a_base32; + const unsigned char *bin_data = data->buf; + Py_ssize_t bin_len = data->len; + binascii_state *state = NULL; assert(bin_len >= 0); - if ( bin_len > BASE64_MAXBIN ) { - state = get_binascii_state(module); - if (state == NULL) { + if (alphabet->buf != NULL) { + if (alphabet->len != 32) { + PyErr_SetString(PyExc_ValueError, "alphabet must have length 32"); return NULL; } - PyErr_SetString(state->Error, "Too much data for base64 line"); - return NULL; + table_b2a = alphabet->buf; } - /* We're lazy and allocate too much (fixed up later). - "+2" leaves room for up to two pad characters. - Note that 'b' gets encoded as 'Yg==\n' (1 in, 5 out). */ - out_len = bin_len*2 + 2; - if (newline) - out_len++; - ascii_data = _PyBytesWriter_Alloc(&writer, out_len); - if (ascii_data == NULL) - return NULL; - - for( ; bin_len > 0 ; bin_len--, bin_data++ ) { - /* Shift the data into our buffer */ - leftchar = (leftchar << 8) | *bin_data; - leftbits += 8; - - /* See if there are 6-bit groups ready */ - while ( leftbits >= 6 ) { - this_ch = (leftchar >> (leftbits-6)) & 0x3f; - leftbits -= 6; - *ascii_data++ = table_b2a_base64[this_ch]; + /* + * Each group of 5 bytes (rounded up) gets encoded as 8 characters. + * Use unsigned integer arithmetic to avoid signed integer overflow. + */ + size_t ascii_len = ((size_t)bin_len + 4u) / 5u * 8u; + unsigned int pads = (5 - (bin_len % 5)) % 5 * 8 / 5; + if (!padded) { + ascii_len -= pads; + pads = 0; + } + if (wrapcol && ascii_len) { + /* Each line should encode a whole number of bytes. */ + wrapcol = wrapcol < 8 ? 8 : wrapcol / 8 * 8; + ascii_len += (ascii_len - 1u) / wrapcol; + } + if (ascii_len > PY_SSIZE_T_MAX) { + state = get_binascii_state(module); + if (state) { + PyErr_SetString(state->Error, "Too much data for base32"); } + return NULL; } - if ( leftbits == 2 ) { - *ascii_data++ = table_b2a_base64[(leftchar&3) << 4]; - *ascii_data++ = BASE64_PAD; - *ascii_data++ = BASE64_PAD; - } else if ( leftbits == 4 ) { - *ascii_data++ = table_b2a_base64[(leftchar&0xf) << 2]; - *ascii_data++ = BASE64_PAD; + PyBytesWriter *writer = PyBytesWriter_Create(ascii_len); + if (writer == NULL) { + return NULL; + } + unsigned char *ascii_data = PyBytesWriter_GetData(writer); + + /* Use the optimized fast path for complete 5-byte groups. */ + Py_ssize_t fast_bytes = base32_encode_fast(bin_data, bin_len, ascii_data, + table_b2a); + bin_data += fast_bytes; + ascii_data += (fast_bytes / 5) * 8; + bin_len -= fast_bytes; + + /* Handle the remaining 0-4 bytes. */ + if (bin_len == 1) { + /* 1 byte remaining: produces 2 encoded + 6 padding chars. */ + assert(!padded || pads == 6); + uint32_t val = bin_data[0]; + *ascii_data++ = table_b2a[(val >> 3) & 0x1f]; + *ascii_data++ = table_b2a[(val << 2) & 0x1f]; + } + else if (bin_len == 2) { + /* 2 bytes remaining: produces 4 encoded + 4 padding chars. */ + assert(!padded || pads == 4); + uint32_t val = ((uint32_t)bin_data[0] << 8) | bin_data[1]; + *ascii_data++ = table_b2a[(val >> 11) & 0x1f]; + *ascii_data++ = table_b2a[(val >> 6) & 0x1f]; + *ascii_data++ = table_b2a[(val >> 1) & 0x1f]; + *ascii_data++ = table_b2a[(val << 4) & 0x1f]; + } + else if (bin_len == 3) { + /* 3 bytes remaining: produces 5 encoded + 3 padding chars. */ + assert(!padded || pads == 3); + uint32_t val = ((uint32_t)bin_data[0] << 16) + | ((uint32_t)bin_data[1] << 8) + | bin_data[2]; + *ascii_data++ = table_b2a[(val >> 19) & 0x1f]; + *ascii_data++ = table_b2a[(val >> 14) & 0x1f]; + *ascii_data++ = table_b2a[(val >> 9) & 0x1f]; + *ascii_data++ = table_b2a[(val >> 4) & 0x1f]; + *ascii_data++ = table_b2a[(val << 1) & 0x1f]; + } + else if (bin_len == 4) { + /* 4 bytes remaining: produces 7 encoded + 1 padding chars. */ + assert(!padded || pads == 1); + uint32_t val = ((uint32_t)bin_data[0] << 24) + | ((uint32_t)bin_data[1] << 16) + | ((uint32_t)bin_data[2] << 8) + | bin_data[3]; + *ascii_data++ = table_b2a[(val >> 27) & 0x1f]; + *ascii_data++ = table_b2a[(val >> 22) & 0x1f]; + *ascii_data++ = table_b2a[(val >> 17) & 0x1f]; + *ascii_data++ = table_b2a[(val >> 12) & 0x1f]; + *ascii_data++ = table_b2a[(val >> 7) & 0x1f]; + *ascii_data++ = table_b2a[(val >> 2) & 0x1f]; + *ascii_data++ = table_b2a[(val << 3) & 0x1f]; + } + else { + assert(pads == 0); + } + for (; pads; pads--) { + *ascii_data++ = BASE32_PAD; } - if (newline) - *ascii_data++ = '\n'; /* Append a courtesy newline */ - return _PyBytesWriter_Finish(&writer, ascii_data); -} + if (wrapcol && ascii_len) { + unsigned char *start = PyBytesWriter_GetData(writer); + ascii_data = start + wraplines(start, ascii_data - start, wrapcol); + } + return PyBytesWriter_FinishWithPointer(writer, ascii_data); +} /*[clinic input] binascii.crc_hqx @@ -823,7 +2220,7 @@ binascii.b2a_hex data: Py_buffer sep: object = NULL An optional single character or byte to separate hex bytes. - bytes_per_sep: int = 1 + bytes_per_sep: Py_ssize_t = 1 How many bytes between separators. Positive values count from the right, negative values count from the left. @@ -843,8 +2240,8 @@ b'b9_01ef' static PyObject * binascii_b2a_hex_impl(PyObject *module, Py_buffer *data, PyObject *sep, - int bytes_per_sep) -/*[clinic end generated code: output=a26937946a81d2c7 input=ec0ade6ba2e43543]*/ + Py_ssize_t bytes_per_sep) +/*[clinic end generated code: output=7d703f866f74a813 input=6a1606f01a87118c]*/ { return _Py_strhex_bytes_with_sep((const char *)data->buf, data->len, sep, bytes_per_sep); @@ -861,8 +2258,8 @@ available as "b2a_hex()". static PyObject * binascii_hexlify_impl(PyObject *module, Py_buffer *data, PyObject *sep, - int bytes_per_sep) -/*[clinic end generated code: output=d12aa1b001b15199 input=bc317bd4e241f76b]*/ + Py_ssize_t bytes_per_sep) +/*[clinic end generated code: output=b99b3b39d234a3d4 input=bc317bd4e241f76b]*/ { return _Py_strhex_bytes_with_sep((const char *)data->buf, data->len, sep, bytes_per_sep); @@ -873,6 +2270,9 @@ binascii.a2b_hex hexstr: ascii_buffer / + * + ignorechars: Py_buffer = b'' + A byte string containing characters to ignore from the input. Binary data of hexadecimal representation. @@ -881,57 +2281,71 @@ This function is also available as "unhexlify()". [clinic start generated code]*/ static PyObject * -binascii_a2b_hex_impl(PyObject *module, Py_buffer *hexstr) -/*[clinic end generated code: output=0cc1a139af0eeecb input=9e1e7f2f94db24fd]*/ +binascii_a2b_hex_impl(PyObject *module, Py_buffer *hexstr, + Py_buffer *ignorechars) +/*[clinic end generated code: output=021a7ed5a742cb20 input=6154b3f4e6e2c0c3]*/ { - const char* argbuf; - Py_ssize_t arglen; - PyObject *retval; - char* retbuf; - Py_ssize_t i, j; - binascii_state *state; - - argbuf = hexstr->buf; - arglen = hexstr->len; + const unsigned char *ascii_data = hexstr->buf; + size_t ascii_len = hexstr->len; + binascii_state *state = NULL; - assert(arglen >= 0); + if (ignorechars->len == 0) { + ignorechars = NULL; + } + ignorecache_t ignorecache; + if (ignorechars != NULL) { + memset(ignorecache, 0, sizeof(ignorecache)); + } - /* XXX What should we do about strings with an odd length? Should - * we add an implicit leading zero, or a trailing zero? For now, - * raise an exception. - */ - if (arglen % 2) { - state = get_binascii_state(module); - if (state == NULL) { - return NULL; - } - PyErr_SetString(state->Error, "Odd-length string"); + /* Allocate the buffer */ + Py_ssize_t bin_len = ascii_len/2; + PyBytesWriter *writer = PyBytesWriter_Create(bin_len); + if (writer == NULL) { return NULL; } + unsigned char *bin_data = PyBytesWriter_GetData(writer); - retval = PyBytes_FromStringAndSize(NULL, (arglen/2)); - if (!retval) - return NULL; - retbuf = PyBytes_AS_STRING(retval); + int pair_pos = 0; + unsigned char leftchar = 0; + for (; ascii_len; ascii_data++, ascii_len--) { + unsigned char this_ch = *ascii_data; - for (i=j=0; i < arglen; i += 2) { - unsigned int top = _PyLong_DigitValue[Py_CHARMASK(argbuf[i])]; - unsigned int bot = _PyLong_DigitValue[Py_CHARMASK(argbuf[i+1])]; - if (top >= 16 || bot >= 16) { - state = get_binascii_state(module); - if (state == NULL) { - return NULL; + unsigned char this_digit = _PyLong_DigitValue[this_ch]; + if (this_digit >= 16) { + // See RFC 4648, section 3.3. + if (!ignorechar(this_ch, ignorechars, ignorecache)) { + state = get_binascii_state(module); + if (state) { + PyErr_SetString(state->Error, + "Non-hexadecimal digit found"); + } + goto error; } - PyErr_SetString(state->Error, - "Non-hexadecimal digit found"); - goto finally; + continue; + } + + if (!pair_pos) { + pair_pos = 1; + leftchar = this_digit; } - retbuf[j++] = (top << 4) + bot; + else { + pair_pos = 0; + *bin_data++ = (leftchar << 4) | this_digit; + } + } + + if (pair_pos) { + state = get_binascii_state(module); + if (state) { + PyErr_SetString(state->Error, "Odd number of hexadecimal digits"); + } + goto error; } - return retval; - finally: - Py_DECREF(retval); + return PyBytesWriter_FinishWithPointer(writer, bin_data); + +error: + PyBytesWriter_Discard(writer); return NULL; } @@ -944,10 +2358,11 @@ hexstr must contain an even number of hex digits (upper or lower case). [clinic start generated code]*/ static PyObject * -binascii_unhexlify_impl(PyObject *module, Py_buffer *hexstr) -/*[clinic end generated code: output=51a64c06c79629e3 input=dd8c012725f462da]*/ +binascii_unhexlify_impl(PyObject *module, Py_buffer *hexstr, + Py_buffer *ignorechars) +/*[clinic end generated code: output=40e87f8a0ded5880 input=dd8c012725f462da]*/ { - return binascii_a2b_hex_impl(module, hexstr); + return binascii_a2b_hex_impl(module, hexstr, ignorechars); } #define MAXLINESIZE 76 @@ -1246,6 +2661,12 @@ static struct PyMethodDef binascii_module_methods[] = { BINASCII_B2A_UU_METHODDEF BINASCII_A2B_BASE64_METHODDEF BINASCII_B2A_BASE64_METHODDEF + BINASCII_B2A_ASCII85_METHODDEF + BINASCII_A2B_ASCII85_METHODDEF + BINASCII_A2B_BASE85_METHODDEF + BINASCII_B2A_BASE85_METHODDEF + BINASCII_A2B_BASE32_METHODDEF + BINASCII_B2A_BASE32_METHODDEF BINASCII_A2B_HEX_METHODDEF BINASCII_B2A_HEX_METHODDEF BINASCII_HEXLIFY_METHODDEF @@ -1279,10 +2700,80 @@ binascii_exec(PyObject *module) return -1; } + if (PyModule_Add(module, "BASE64_ALPHABET", + PyBytes_FromStringAndSize((const char *)table_b2a_base64, 64)) < 0) + { + return -1; + } + if (PyModule_Add(module, "URLSAFE_BASE64_ALPHABET", + PyBytes_FromString("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-_")) < 0) + { + return -1; + } + if (PyModule_Add(module, "CRYPT_ALPHABET", + PyBytes_FromString("./0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz")) < 0) + { + return -1; + } + if (PyModule_Add(module, "UU_ALPHABET", + PyBytes_FromString(" !\"#$%&'()*+,-./" + "0123456789:;<=>?@" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "[\\]^_")) < 0) + { + return -1; + } + if (PyModule_Add(module, "BINHEX_ALPHABET", + PyBytes_FromString("!\"#$%&'()*+,-012345689@" + "ABCDEFGHIJKLMNPQRSTUVXYZ[`" + "abcdefhijklmpqr")) < 0) + { + return -1; + } + if (PyModule_Add(module, "BASE85_ALPHABET", + PyBytes_FromStringAndSize((const char *)table_b2a_base85, 85)) < 0) + { + return -1; + } + if (PyModule_Add(module, "ASCII85_ALPHABET", + PyBytes_FromStringAndSize((const char *)table_b2a_base85_a85, 85)) < 0) + { + return -1; + } + if (PyModule_Add(module, "Z85_ALPHABET", + PyBytes_FromString("0123456789" + "abcdefghijklmnopqrstuvwxyz" + /* clinic doesn't like '/' followed by '*' */ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ".-:+=^!/\x2a?&<>()[]{}@%$#")) < 0) + { + return -1; + } + if (PyModule_Add(module, "BASE32_ALPHABET", + PyBytes_FromStringAndSize((const char *)table_b2a_base32, 32)) < 0) + { + return -1; + } + if (PyModule_Add(module, "BASE32HEX_ALPHABET", + PyBytes_FromString("0123456789ABCDEFGHIJKLMNOPQRSTUV")) < 0) + { + return -1; + } + + state->reverse_table_cache = PyDict_New(); + if (state->reverse_table_cache == NULL) { + return -1; + } + return 0; } static PyModuleDef_Slot binascii_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, binascii_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -1295,6 +2786,7 @@ binascii_traverse(PyObject *module, visitproc visit, void *arg) binascii_state *state = get_binascii_state(module); Py_VISIT(state->Error); Py_VISIT(state->Incomplete); + Py_VISIT(state->reverse_table_cache); return 0; } @@ -1304,6 +2796,7 @@ binascii_clear(PyObject *module) binascii_state *state = get_binascii_state(module); Py_CLEAR(state->Error); Py_CLEAR(state->Incomplete); + Py_CLEAR(state->reverse_table_cache); return 0; } diff --git a/Modules/blake2module.c b/Modules/blake2module.c index 163f238a4268d0..b71dd20925611e 100644 --- a/Modules/blake2module.c +++ b/Modules/blake2module.c @@ -16,9 +16,10 @@ #include "Python.h" #include "hashlib.h" -#include "pycore_strhex.h" // _Py_strhex() -#include "pycore_typeobject.h" #include "pycore_moduleobject.h" +#include "pycore_object.h" // _PyObject_VisitType() +#include "pycore_strhex.h" // _Py_strhex() +#include "pycore_typeobject.h" // QUICK CPU AUTODETECTION // @@ -279,6 +280,7 @@ blake2_exec(PyObject *m) } static PyModuleDef_Slot _blake2_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, blake2_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -669,9 +671,9 @@ _blake2.blake2b.__new__ as py_blake2b_new data as data_obj: object(c_default="NULL") = b'' * digest_size: int(c_default="HACL_HASH_BLAKE2B_OUT_BYTES") = _blake2.blake2b.MAX_DIGEST_SIZE - key: Py_buffer(c_default="NULL", py_default="b''") = None - salt: Py_buffer(c_default="NULL", py_default="b''") = None - person: Py_buffer(c_default="NULL", py_default="b''") = None + key: Py_buffer = b'' + salt: Py_buffer = b'' + person: Py_buffer = b'' fanout: int = 1 depth: int = 1 leaf_size: unsigned_long = 0 @@ -692,7 +694,7 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data_obj, int digest_size, unsigned long long node_offset, int node_depth, int inner_size, int last_node, int usedforsecurity, PyObject *string) -/*[clinic end generated code: output=de64bd850606b6a0 input=78cf60a2922d2f90]*/ +/*[clinic end generated code: output=de64bd850606b6a0 input=32832fb37d13c03d]*/ { PyObject *data; if (_Py_hashlib_data_argument(&data, data_obj, string) < 0) { @@ -709,9 +711,9 @@ _blake2.blake2s.__new__ as py_blake2s_new data as data_obj: object(c_default="NULL") = b'' * digest_size: int(c_default="HACL_HASH_BLAKE2S_OUT_BYTES") = _blake2.blake2s.MAX_DIGEST_SIZE - key: Py_buffer(c_default="NULL", py_default="b''") = None - salt: Py_buffer(c_default="NULL", py_default="b''") = None - person: Py_buffer(c_default="NULL", py_default="b''") = None + key: Py_buffer = b'' + salt: Py_buffer = b'' + person: Py_buffer = b'' fanout: int = 1 depth: int = 1 leaf_size: unsigned_long = 0 @@ -732,7 +734,7 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data_obj, int digest_size, unsigned long long node_offset, int node_depth, int inner_size, int last_node, int usedforsecurity, PyObject *string) -/*[clinic end generated code: output=582a0c4295cc3a3c input=6843d6332eefd295]*/ +/*[clinic end generated code: output=582a0c4295cc3a3c input=da467fc9dae646bb]*/ { PyObject *data; if (_Py_hashlib_data_argument(&data, data_obj, string) < 0) { @@ -1008,17 +1010,10 @@ py_blake2_dealloc(PyObject *self) Py_DECREF(type); } -static int -py_blake2_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static PyType_Slot blake2b_type_slots[] = { {Py_tp_clear, py_blake2_clear}, {Py_tp_dealloc, py_blake2_dealloc}, - {Py_tp_traverse, py_blake2_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_doc, (char *)py_blake2b_new__doc__}, {Py_tp_methods, py_blake2b_methods}, {Py_tp_getset, py_blake2b_getsetters}, @@ -1029,7 +1024,7 @@ static PyType_Slot blake2b_type_slots[] = { static PyType_Slot blake2s_type_slots[] = { {Py_tp_clear, py_blake2_clear}, {Py_tp_dealloc, py_blake2_dealloc}, - {Py_tp_traverse, py_blake2_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_doc, (char *)py_blake2s_new__doc__}, {Py_tp_methods, py_blake2b_methods}, {Py_tp_getset, py_blake2b_getsetters}, diff --git a/Modules/cjkcodecs/_codecs_iso2022.c b/Modules/cjkcodecs/_codecs_iso2022.c index ef6faeb71274e1..b1984df2695b17 100644 --- a/Modules/cjkcodecs/_codecs_iso2022.c +++ b/Modules/cjkcodecs/_codecs_iso2022.c @@ -802,10 +802,13 @@ jisx0213_encoder(const MultibyteCodec *codec, const Py_UCS4 *data, return coded; case 2: /* second character of unicode pair */ - coded = find_pairencmap((ucs2_t)data[0], (ucs2_t)data[1], - jisx0213_pair_encmap, JISX0213_ENCPAIRS); - if (coded != DBCINV) - return coded; + if (data[1] != 0) { /* Don't consume null char as part of pair */ + coded = find_pairencmap((ucs2_t)data[0], (ucs2_t)data[1], + jisx0213_pair_encmap, JISX0213_ENCPAIRS); + if (coded != DBCINV) { + return coded; + } + } _Py_FALLTHROUGH; case -1: /* flush unterminated */ diff --git a/Modules/cjkcodecs/_codecs_jp.c b/Modules/cjkcodecs/_codecs_jp.c index f7127487aa5f59..cd77888d5514b8 100644 --- a/Modules/cjkcodecs/_codecs_jp.c +++ b/Modules/cjkcodecs/_codecs_jp.c @@ -192,8 +192,11 @@ ENCODER(euc_jis_2004) JISX0213_ENCPAIRS); if (code == DBCINV) return 1; - } else + } + else if (c2 != 0) { + /* Don't consume null char as part of pair */ insize = 2; + } } } } @@ -611,8 +614,10 @@ ENCODER(shift_jis_2004) if (code == DBCINV) return 1; } - else + else if (ch2 != 0) { + /* Don't consume null char as part of pair */ insize = 2; + } } } } diff --git a/Modules/cjkcodecs/cjkcodecs.h b/Modules/cjkcodecs/cjkcodecs.h index f66412237011d4..9d86396f73b2b5 100644 --- a/Modules/cjkcodecs/cjkcodecs.h +++ b/Modules/cjkcodecs/cjkcodecs.h @@ -500,6 +500,7 @@ static struct PyMethodDef _cjk_methods[] = { }; static PyModuleDef_Slot _cjk_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _cjk_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/cjkcodecs/clinic/multibytecodec.c.h b/Modules/cjkcodecs/clinic/multibytecodec.c.h index b3663180d726e5..32588b0561e1ac 100644 --- a/Modules/cjkcodecs/clinic/multibytecodec.c.h +++ b/Modules/cjkcodecs/clinic/multibytecodec.c.h @@ -14,10 +14,11 @@ PyDoc_STRVAR(_multibytecodec_MultibyteCodec_encode__doc__, "\n" "Return an encoded string version of \'input\'.\n" "\n" -"\'errors\' may be given to set a different error handling scheme. Default is\n" -"\'strict\' meaning that encoding errors raise a UnicodeEncodeError. Other possible\n" -"values are \'ignore\', \'replace\' and \'xmlcharrefreplace\' as well as any other name\n" -"registered with codecs.register_error that can handle UnicodeEncodeErrors."); +"\'errors\' may be given to set a different error handling scheme.\n" +"Default is \'strict\' meaning that encoding errors raise\n" +"a UnicodeEncodeError. Other possible values are \'ignore\', \'replace\'\n" +"and \'xmlcharrefreplace\' as well as any other name registered with\n" +"codecs.register_error that can handle UnicodeEncodeErrors."); #define _MULTIBYTECODEC_MULTIBYTECODEC_ENCODE_METHODDEF \ {"encode", _PyCFunction_CAST(_multibytecodec_MultibyteCodec_encode), METH_FASTCALL|METH_KEYWORDS, _multibytecodec_MultibyteCodec_encode__doc__}, @@ -103,9 +104,10 @@ PyDoc_STRVAR(_multibytecodec_MultibyteCodec_decode__doc__, "\n" "Decodes \'input\'.\n" "\n" -"\'errors\' may be given to set a different error handling scheme. Default is\n" -"\'strict\' meaning that encoding errors raise a UnicodeDecodeError. Other possible\n" -"values are \'ignore\' and \'replace\' as well as any other name registered with\n" +"\'errors\' may be given to set a different error handling scheme.\n" +"Default is \'strict\' meaning that encoding errors raise\n" +"a UnicodeDecodeError. Other possible values are \'ignore\' and\n" +"\'replace\' as well as any other name registered with\n" "codecs.register_error that is able to handle UnicodeDecodeErrors.\""); #define _MULTIBYTECODEC_MULTIBYTECODEC_DECODE_METHODDEF \ @@ -696,4 +698,4 @@ PyDoc_STRVAR(_multibytecodec___create_codec__doc__, #define _MULTIBYTECODEC___CREATE_CODEC_METHODDEF \ {"__create_codec", (PyCFunction)_multibytecodec___create_codec, METH_O, _multibytecodec___create_codec__doc__}, -/*[clinic end generated code: output=014f4f6bb9d29594 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a84b1544d7d01abb input=a9049054013a1b77]*/ diff --git a/Modules/cjkcodecs/multibytecodec.c b/Modules/cjkcodecs/multibytecodec.c index 08b74740bda4bf..32c96c9d2cb3cd 100644 --- a/Modules/cjkcodecs/multibytecodec.c +++ b/Modules/cjkcodecs/multibytecodec.c @@ -12,6 +12,7 @@ #include "multibytecodec.h" #include "clinic/multibytecodec.c.h" +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #include <stddef.h> // offsetof() @@ -102,26 +103,17 @@ static PyObject *multibytecodec_encode(const MultibyteCodec *, static PyObject * make_tuple(PyObject *object, Py_ssize_t len) { - PyObject *v, *w; - - if (object == NULL) - return NULL; - - v = PyTuple_New(2); - if (v == NULL) { - Py_DECREF(object); + if (object == NULL) { return NULL; } - PyTuple_SET_ITEM(v, 0, object); - w = PyLong_FromSsize_t(len); - if (w == NULL) { - Py_DECREF(v); + PyObject* len_obj = PyLong_FromSsize_t(len); + if (len_obj == NULL) { + Py_DECREF(object); return NULL; } - PyTuple_SET_ITEM(v, 1, w); - return v; + return _PyTuple_FromPairSteal(object, len_obj); } static PyObject * @@ -597,17 +589,18 @@ _multibytecodec.MultibyteCodec.encode Return an encoded string version of 'input'. -'errors' may be given to set a different error handling scheme. Default is -'strict' meaning that encoding errors raise a UnicodeEncodeError. Other possible -values are 'ignore', 'replace' and 'xmlcharrefreplace' as well as any other name -registered with codecs.register_error that can handle UnicodeEncodeErrors. +'errors' may be given to set a different error handling scheme. +Default is 'strict' meaning that encoding errors raise +a UnicodeEncodeError. Other possible values are 'ignore', 'replace' +and 'xmlcharrefreplace' as well as any other name registered with +codecs.register_error that can handle UnicodeEncodeErrors. [clinic start generated code]*/ static PyObject * _multibytecodec_MultibyteCodec_encode_impl(MultibyteCodecObject *self, PyObject *input, const char *errors) -/*[clinic end generated code: output=7b26652045ba56a9 input=2841745b95ed338f]*/ +/*[clinic end generated code: output=7b26652045ba56a9 input=980002ed1447697b]*/ { MultibyteCodec_State state; PyObject *errorcb, *r, *ucvt; @@ -662,9 +655,10 @@ _multibytecodec.MultibyteCodec.decode Decodes 'input'. -'errors' may be given to set a different error handling scheme. Default is -'strict' meaning that encoding errors raise a UnicodeDecodeError. Other possible -values are 'ignore' and 'replace' as well as any other name registered with +'errors' may be given to set a different error handling scheme. +Default is 'strict' meaning that encoding errors raise +a UnicodeDecodeError. Other possible values are 'ignore' and +'replace' as well as any other name registered with codecs.register_error that is able to handle UnicodeDecodeErrors." [clinic start generated code]*/ @@ -672,7 +666,7 @@ static PyObject * _multibytecodec_MultibyteCodec_decode_impl(MultibyteCodecObject *self, Py_buffer *input, const char *errors) -/*[clinic end generated code: output=ff419f65bad6cc77 input=e0c78fc7ab190def]*/ +/*[clinic end generated code: output=ff419f65bad6cc77 input=dbf93d8bb98ca440]*/ { MultibyteCodec_State state; MultibyteDecodeBuffer buf; @@ -2119,6 +2113,7 @@ static struct PyMethodDef _multibytecodec_methods[] = { }; static PyModuleDef_Slot _multibytecodec_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _multibytecodec_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/clinic/_abc.c.h b/Modules/clinic/_abc.c.h index 04681fa2206a2a..fa1c57dc26bf85 100644 --- a/Modules/clinic/_abc.c.h +++ b/Modules/clinic/_abc.c.h @@ -146,9 +146,9 @@ PyDoc_STRVAR(_abc_get_cache_token__doc__, "\n" "Returns the current ABC cache token.\n" "\n" -"The token is an opaque object (supporting equality testing) identifying the\n" -"current version of the ABC cache for virtual subclasses. The token changes\n" -"with every call to register() on any ABC."); +"The token is an opaque object (supporting equality testing) identifying\n" +"the current version of the ABC cache for virtual subclasses. The token\n" +"changes with every call to register() on any ABC."); #define _ABC_GET_CACHE_TOKEN_METHODDEF \ {"get_cache_token", (PyCFunction)_abc_get_cache_token, METH_NOARGS, _abc_get_cache_token__doc__}, @@ -161,4 +161,4 @@ _abc_get_cache_token(PyObject *module, PyObject *Py_UNUSED(ignored)) { return _abc_get_cache_token_impl(module); } -/*[clinic end generated code: output=1989b6716c950e17 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b05d599656aeb1e1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h index 66953d74213b66..f07a09df5ac7ae 100644 --- a/Modules/clinic/_asynciomodule.c.h +++ b/Modules/clinic/_asynciomodule.c.h @@ -90,7 +90,8 @@ PyDoc_STRVAR(_asyncio_Future_result__doc__, "\n" "If the future has been cancelled, raises CancelledError. If the\n" "future\'s result isn\'t yet available, raises InvalidStateError. If\n" -"the future is done and has an exception set, this exception is raised."); +"the future is done and has an exception set, this exception is\n" +"raised."); #define _ASYNCIO_FUTURE_RESULT_METHODDEF \ {"result", (PyCFunction)_asyncio_Future_result, METH_NOARGS, _asyncio_Future_result__doc__}, @@ -250,8 +251,8 @@ PyDoc_STRVAR(_asyncio_Future_add_done_callback__doc__, "\n" "Add a callback to be run when the future becomes done.\n" "\n" -"The callback is called with a single argument - the future object. If\n" -"the future is already done when this is called, the callback is\n" +"The callback is called with a single argument - the future object.\n" +"If the future is already done when this is called, the callback is\n" "scheduled with call_soon."); #define _ASYNCIO_FUTURE_ADD_DONE_CALLBACK_METHODDEF \ @@ -371,9 +372,9 @@ PyDoc_STRVAR(_asyncio_Future_cancel__doc__, "\n" "Cancel the future and schedule callbacks.\n" "\n" -"If the future is already done or cancelled, return False. Otherwise,\n" -"change the future\'s state to cancelled, schedule the callbacks and\n" -"return True."); +"If the future is already done or cancelled, return False.\n" +"Otherwise, change the future\'s state to cancelled, schedule the\n" +"callbacks and return True."); #define _ASYNCIO_FUTURE_CANCEL_METHODDEF \ {"cancel", _PyCFunction_CAST(_asyncio_Future_cancel), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _asyncio_Future_cancel__doc__}, @@ -465,8 +466,8 @@ PyDoc_STRVAR(_asyncio_Future_done__doc__, "\n" "Return True if the future is done.\n" "\n" -"Done means either that a result / exception are available, or that the\n" -"future was cancelled."); +"Done means either that a result / exception are available, or that\n" +"the future was cancelled."); #define _ASYNCIO_FUTURE_DONE_METHODDEF \ {"done", (PyCFunction)_asyncio_Future_done, METH_NOARGS, _asyncio_Future_done__doc__}, @@ -2232,4 +2233,4 @@ _asyncio_future_discard_from_awaited_by(PyObject *module, PyObject *const *args, exit: return return_value; } -/*[clinic end generated code: output=b69948ed810591d9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=32996fb47c48245b input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_bisectmodule.c.h b/Modules/clinic/_bisectmodule.c.h index 314208bc41d0c1..454131faf4112b 100644 --- a/Modules/clinic/_bisectmodule.c.h +++ b/Modules/clinic/_bisectmodule.c.h @@ -16,8 +16,8 @@ PyDoc_STRVAR(_bisect_bisect_right__doc__, "Return the index where to insert item x in list a, assuming a is sorted.\n" "\n" "The return value i is such that all e in a[:i] have e <= x, and all e in\n" -"a[i:] have e > x. So if x already appears in the list, a.insert(i, x) will\n" -"insert just after the rightmost x already there.\n" +"a[i:] have e > x. So if x already appears in the list, a.insert(i, x)\n" +"will insert just after the rightmost x already there.\n" "\n" "Optional args lo (default 0) and hi (default len(a)) bound the\n" "slice of a to be searched.\n" @@ -93,6 +93,11 @@ _bisect_bisect_right(PyObject *module, PyObject *const *args, Py_ssize_t nargs, goto exit; } lo = ival; + if (lo < 0) { + PyErr_SetString(PyExc_ValueError, + "lo cannot be negative"); + goto exit; + } } if (!--noptargs) { goto skip_optional_pos; @@ -203,6 +208,11 @@ _bisect_insort_right(PyObject *module, PyObject *const *args, Py_ssize_t nargs, goto exit; } lo = ival; + if (lo < 0) { + PyErr_SetString(PyExc_ValueError, + "lo cannot be negative"); + goto exit; + } } if (!--noptargs) { goto skip_optional_pos; @@ -235,8 +245,8 @@ PyDoc_STRVAR(_bisect_bisect_left__doc__, "Return the index where to insert item x in list a, assuming a is sorted.\n" "\n" "The return value i is such that all e in a[:i] have e < x, and all e in\n" -"a[i:] have e >= x. So if x already appears in the list, a.insert(i, x) will\n" -"insert just before the leftmost x already there.\n" +"a[i:] have e >= x. So if x already appears in the list, a.insert(i, x)\n" +"will insert just before the leftmost x already there.\n" "\n" "Optional args lo (default 0) and hi (default len(a)) bound the\n" "slice of a to be searched.\n" @@ -312,6 +322,11 @@ _bisect_bisect_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P goto exit; } lo = ival; + if (lo < 0) { + PyErr_SetString(PyExc_ValueError, + "lo cannot be negative"); + goto exit; + } } if (!--noptargs) { goto skip_optional_pos; @@ -422,6 +437,11 @@ _bisect_insort_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P goto exit; } lo = ival; + if (lo < 0) { + PyErr_SetString(PyExc_ValueError, + "lo cannot be negative"); + goto exit; + } } if (!--noptargs) { goto skip_optional_pos; @@ -446,4 +466,4 @@ _bisect_insort_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P exit: return return_value; } -/*[clinic end generated code: output=729385c6a23828ab input=a9049054013a1b77]*/ +/*[clinic end generated code: output=62345f14c5c01639 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_bz2module.c.h b/Modules/clinic/_bz2module.c.h index 2bc6524b6a973b..30f2c7965e73ae 100644 --- a/Modules/clinic/_bz2module.c.h +++ b/Modules/clinic/_bz2module.c.h @@ -116,18 +116,19 @@ PyDoc_STRVAR(_bz2_BZ2Decompressor_decompress__doc__, "\n" "Decompress *data*, returning uncompressed data as bytes.\n" "\n" -"If *max_length* is nonnegative, returns at most *max_length* bytes of\n" -"decompressed data. If this limit is reached and further output can be\n" -"produced, *self.needs_input* will be set to ``False``. In this case, the next\n" -"call to *decompress()* may provide *data* as b\'\' to obtain more of the output.\n" +"If *max_length* is nonnegative, returns at most *max_length* bytes\n" +"of decompressed data. If this limit is reached and further output\n" +"can be produced, *self.needs_input* will be set to ``False``. In\n" +"this case, the next call to *decompress()* may provide *data* as b\'\'\n" +"to obtain more of the output.\n" "\n" -"If all of the input data was decompressed and returned (either because this\n" -"was less than *max_length* bytes, or because *max_length* was negative),\n" -"*self.needs_input* will be set to True.\n" +"If all of the input data was decompressed and returned (either\n" +"because this was less than *max_length* bytes, or because\n" +"*max_length* was negative), *self.needs_input* will be set to True.\n" "\n" -"Attempting to decompress data after the end of stream is reached raises an\n" -"EOFError. Any data found after the end of the stream is ignored and saved in\n" -"the unused_data attribute."); +"Attempting to decompress data after the end of stream is reached\n" +"raises an EOFError. Any data found after the end of the stream is\n" +"ignored and saved in the unused_data attribute."); #define _BZ2_BZ2DECOMPRESSOR_DECOMPRESS_METHODDEF \ {"decompress", _PyCFunction_CAST(_bz2_BZ2Decompressor_decompress), METH_FASTCALL|METH_KEYWORDS, _bz2_BZ2Decompressor_decompress__doc__}, @@ -237,4 +238,4 @@ _bz2_BZ2Decompressor(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=552ac6d4c5a101b7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1dce5396d592bad7 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_codecsmodule.c.h b/Modules/clinic/_codecsmodule.c.h index b0310325759326..4a40dc660b621c 100644 --- a/Modules/clinic/_codecsmodule.c.h +++ b/Modules/clinic/_codecsmodule.c.h @@ -14,9 +14,10 @@ PyDoc_STRVAR(_codecs_register__doc__, "\n" "Register a codec search function.\n" "\n" -"Search functions are expected to take one argument, the encoding name in\n" -"all lower case letters, and either return None, or a tuple of functions\n" -"(encoder, decoder, stream_reader, stream_writer) (or a CodecInfo object)."); +"Search functions are expected to take one argument, the encoding\n" +"name in all lower case letters, and either return None, or a tuple\n" +"of functions (encoder, decoder, stream_reader, stream_writer) (or\n" +"a CodecInfo object)."); #define _CODECS_REGISTER_METHODDEF \ {"register", (PyCFunction)_codecs_register, METH_O, _codecs_register__doc__}, @@ -76,10 +77,10 @@ PyDoc_STRVAR(_codecs_encode__doc__, "Encodes obj using the codec registered for encoding.\n" "\n" "The default encoding is \'utf-8\'. errors may be given to set a\n" -"different error handling scheme. Default is \'strict\' meaning that encoding\n" -"errors raise a ValueError. Other possible values are \'ignore\', \'replace\'\n" -"and \'backslashreplace\' as well as any other name registered with\n" -"codecs.register_error that can handle ValueErrors."); +"different error handling scheme. Default is \'strict\' meaning that\n" +"encoding errors raise a ValueError. Other possible values are \'ignore\',\n" +"\'replace\' and \'backslashreplace\' as well as any other name registered\n" +"with codecs.register_error that can handle ValueErrors."); #define _CODECS_ENCODE_METHODDEF \ {"encode", _PyCFunction_CAST(_codecs_encode), METH_FASTCALL|METH_KEYWORDS, _codecs_encode__doc__}, @@ -179,10 +180,10 @@ PyDoc_STRVAR(_codecs_decode__doc__, "Decodes obj using the codec registered for encoding.\n" "\n" "Default encoding is \'utf-8\'. errors may be given to set a\n" -"different error handling scheme. Default is \'strict\' meaning that encoding\n" -"errors raise a ValueError. Other possible values are \'ignore\', \'replace\'\n" -"and \'backslashreplace\' as well as any other name registered with\n" -"codecs.register_error that can handle ValueErrors."); +"different error handling scheme. Default is \'strict\' meaning that\n" +"encoding errors raise a ValueError. Other possible values are \'ignore\',\n" +"\'replace\' and \'backslashreplace\' as well as any other name registered\n" +"with codecs.register_error that can handle ValueErrors."); #define _CODECS_DECODE_METHODDEF \ {"decode", _PyCFunction_CAST(_codecs_decode), METH_FASTCALL|METH_KEYWORDS, _codecs_decode__doc__}, @@ -2649,8 +2650,9 @@ PyDoc_STRVAR(_codecs_register_error__doc__, "Register the specified error handler under the name errors.\n" "\n" "handler must be a callable object, that will be called with an exception\n" -"instance containing information about the location of the encoding/decoding\n" -"error and must return a (replacement, new position) tuple."); +"instance containing information about the location of the\n" +"encoding/decoding error and must return a (replacement, new position)\n" +"tuple."); #define _CODECS_REGISTER_ERROR_METHODDEF \ {"register_error", _PyCFunction_CAST(_codecs_register_error), METH_FASTCALL, _codecs_register_error__doc__}, @@ -2745,8 +2747,8 @@ PyDoc_STRVAR(_codecs_lookup_error__doc__, "\n" "lookup_error(errors) -> handler\n" "\n" -"Return the error handler for the specified error handling name or raise a\n" -"LookupError, if no handler exists under this name."); +"Return the error handler for the specified error handling name or raise\n" +"a LookupError, if no handler exists under this name."); #define _CODECS_LOOKUP_ERROR_METHODDEF \ {"lookup_error", (PyCFunction)_codecs_lookup_error, METH_O, _codecs_lookup_error__doc__}, @@ -2779,6 +2781,70 @@ _codecs_lookup_error(PyObject *module, PyObject *arg) return return_value; } +PyDoc_STRVAR(_codecs__normalize_encoding__doc__, +"_normalize_encoding($module, /, encoding)\n" +"--\n" +"\n" +"Normalize an encoding name *encoding*.\n" +"\n" +"Used for encodings.normalize_encoding. Does not convert to lower case."); + +#define _CODECS__NORMALIZE_ENCODING_METHODDEF \ + {"_normalize_encoding", _PyCFunction_CAST(_codecs__normalize_encoding), METH_FASTCALL|METH_KEYWORDS, _codecs__normalize_encoding__doc__}, + +static PyObject * +_codecs__normalize_encoding_impl(PyObject *module, PyObject *encoding); + +static PyObject * +_codecs__normalize_encoding(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(encoding), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"encoding", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_normalize_encoding", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *encoding; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("_normalize_encoding", "argument 'encoding'", "str", args[0]); + goto exit; + } + encoding = args[0]; + return_value = _codecs__normalize_encoding_impl(module, encoding); + +exit: + return return_value; +} + #ifndef _CODECS_MBCS_DECODE_METHODDEF #define _CODECS_MBCS_DECODE_METHODDEF #endif /* !defined(_CODECS_MBCS_DECODE_METHODDEF) */ @@ -2802,4 +2868,4 @@ _codecs_lookup_error(PyObject *module, PyObject *arg) #ifndef _CODECS_CODE_PAGE_ENCODE_METHODDEF #define _CODECS_CODE_PAGE_ENCODE_METHODDEF #endif /* !defined(_CODECS_CODE_PAGE_ENCODE_METHODDEF) */ -/*[clinic end generated code: output=ed13f20dfb09e306 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=505edef891a06329 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_collectionsmodule.c.h b/Modules/clinic/_collectionsmodule.c.h index b5c315c680e782..6c60678a6fbd51 100644 --- a/Modules/clinic/_collectionsmodule.c.h +++ b/Modules/clinic/_collectionsmodule.c.h @@ -340,7 +340,7 @@ deque_index(PyObject *deque, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *v; Py_ssize_t start = 0; - Py_ssize_t stop = Py_SIZE(deque); + Py_ssize_t stop = PY_SSIZE_T_MAX; if (!_PyArg_CheckPositional("index", nargs, 1, 3)) { goto exit; @@ -632,4 +632,4 @@ tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=b9d4d647c221cb9f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f5a388add99d3d15 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_csv.c.h b/Modules/clinic/_csv.c.h index 416aeafccf917b..b8dd8ac35fac59 100644 --- a/Modules/clinic/_csv.c.h +++ b/Modules/clinic/_csv.c.h @@ -12,9 +12,7 @@ PyDoc_STRVAR(_csv_list_dialects__doc__, "list_dialects($module, /)\n" "--\n" "\n" -"Return a list of all known dialect names.\n" -"\n" -" names = csv.list_dialects()"); +"Return a list of all known dialect names."); #define _CSV_LIST_DIALECTS_METHODDEF \ {"list_dialects", (PyCFunction)_csv_list_dialects, METH_NOARGS, _csv_list_dialects__doc__}, @@ -32,9 +30,7 @@ PyDoc_STRVAR(_csv_unregister_dialect__doc__, "unregister_dialect($module, /, name)\n" "--\n" "\n" -"Delete the name/dialect mapping associated with a string name.\n" -"\n" -" csv.unregister_dialect(name)"); +"Delete the name/dialect mapping associated with a string name."); #define _CSV_UNREGISTER_DIALECT_METHODDEF \ {"unregister_dialect", _PyCFunction_CAST(_csv_unregister_dialect), METH_FASTCALL|METH_KEYWORDS, _csv_unregister_dialect__doc__}, @@ -92,9 +88,7 @@ PyDoc_STRVAR(_csv_get_dialect__doc__, "get_dialect($module, /, name)\n" "--\n" "\n" -"Return the dialect instance associated with name.\n" -"\n" -" dialect = csv.get_dialect(name)"); +"Return the dialect instance associated with name."); #define _CSV_GET_DIALECT_METHODDEF \ {"get_dialect", _PyCFunction_CAST(_csv_get_dialect), METH_FASTCALL|METH_KEYWORDS, _csv_get_dialect__doc__}, @@ -154,8 +148,6 @@ PyDoc_STRVAR(_csv_field_size_limit__doc__, "\n" "Sets an upper limit on parsed fields.\n" "\n" -" csv.field_size_limit([limit])\n" -"\n" "Returns old limit. If limit is not given, no new limit is set and\n" "the old limit is returned"); @@ -215,4 +207,4 @@ _csv_field_size_limit(PyObject *module, PyObject *const *args, Py_ssize_t nargs, exit: return return_value; } -/*[clinic end generated code: output=1fb09d5e7667ad0d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ed77cb69fad9f3b4 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_curses_panel.c.h b/Modules/clinic/_curses_panel.c.h index 75cf067c8aa822..d8b2cba7fd3f89 100644 --- a/Modules/clinic/_curses_panel.c.h +++ b/Modules/clinic/_curses_panel.c.h @@ -28,7 +28,8 @@ PyDoc_STRVAR(_curses_panel_panel_hide__doc__, "\n" "Hide the panel.\n" "\n" -"This does not delete the object, it just makes the window on screen invisible."); +"This does not delete the object, it just makes the window on screen\n" +"invisible."); #define _CURSES_PANEL_PANEL_HIDE_METHODDEF \ {"hide", (PyCFunction)_curses_panel_panel_hide, METH_NOARGS, _curses_panel_panel_hide__doc__}, @@ -328,7 +329,8 @@ PyDoc_STRVAR(_curses_panel_update_panels__doc__, "\n" "Updates the virtual screen after changes in the panel stack.\n" "\n" -"This does not call curses.doupdate(), so you\'ll have to do this yourself."); +"This does not call curses.doupdate(), so you\'ll have to do this\n" +"yourself."); #define _CURSES_PANEL_UPDATE_PANELS_METHODDEF \ {"update_panels", (PyCFunction)_curses_panel_update_panels, METH_NOARGS, _curses_panel_update_panels__doc__}, @@ -341,4 +343,4 @@ _curses_panel_update_panels(PyObject *module, PyObject *Py_UNUSED(ignored)) { return _curses_panel_update_panels_impl(module); } -/*[clinic end generated code: output=db2fe491582784aa input=a9049054013a1b77]*/ +/*[clinic end generated code: output=62f20ef03eefdf44 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index a8c32d6510604a..eec9e82739b778 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -301,7 +301,7 @@ PyDoc_STRVAR(_curses_window_attron__doc__, "attron($self, attr, /)\n" "--\n" "\n" -"Add attribute attr from the \"background\" set."); +"Add attribute attr to the \"background\" set."); #define _CURSES_WINDOW_ATTRON_METHODDEF \ {"attron", (PyCFunction)_curses_window_attron, METH_O, _curses_window_attron__doc__}, @@ -422,10 +422,10 @@ PyDoc_STRVAR(_curses_window_border__doc__, " br\n" " Bottom-right corner.\n" "\n" -"Each parameter specifies the character to use for a specific part of the\n" -"border. The characters can be specified as integers or as one-character\n" -"strings. A 0 value for any parameter will cause the default character to be\n" -"used for that parameter."); +"Each parameter specifies the character to use for a specific part of\n" +"the border. The characters can be specified as integers or as\n" +"one-character strings. A 0 value for any parameter will cause the\n" +"default character to be used for that parameter."); #define _CURSES_WINDOW_BORDER_METHODDEF \ {"border", _PyCFunction_CAST(_curses_window_border), METH_FASTCALL, _curses_window_border__doc__}, @@ -500,8 +500,9 @@ PyDoc_STRVAR(_curses_window_box__doc__, " horch\n" " Top and bottom side.\n" "\n" -"Similar to border(), but both ls and rs are verch and both ts and bs are\n" -"horch. The default corner characters are always used by this function."); +"Similar to border(), but both ls and rs are verch and both ts and bs\n" +"are horch. The default corner characters are always used by this\n" +"function."); #define _CURSES_WINDOW_BOX_METHODDEF \ {"box", (PyCFunction)_curses_window_box, METH_VARARGS, _curses_window_box__doc__}, @@ -593,9 +594,9 @@ PyDoc_STRVAR(_curses_window_derwin__doc__, " begin_x\n" " Left side x-coordinate.\n" "\n" -"derwin() is the same as calling subwin(), except that begin_y and begin_x\n" -"are relative to the origin of the window, rather than relative to the entire\n" -"screen."); +"derwin() is the same as calling subwin(), except that begin_y and\n" +"begin_x are relative to the origin of the window, rather than\n" +"relative to the entire screen."); #define _CURSES_WINDOW_DERWIN_METHODDEF \ {"derwin", (PyCFunction)_curses_window_derwin, METH_VARARGS, _curses_window_derwin__doc__}, @@ -751,9 +752,10 @@ PyDoc_STRVAR(_curses_window_getch__doc__, " x\n" " X-coordinate.\n" "\n" -"The integer returned does not have to be in ASCII range: function keys,\n" -"keypad keys and so on return numbers higher than 256. In no-delay mode, -1\n" -"is returned if there is no input, else getch() waits until a key is pressed."); +"The integer returned does not have to be in ASCII range: function\n" +"keys, keypad keys and so on return numbers higher than 256. In\n" +"no-delay mode, -1 is returned if there is no input, else getch()\n" +"waits until a key is pressed."); #define _CURSES_WINDOW_GETCH_METHODDEF \ {"getch", (PyCFunction)_curses_window_getch, METH_VARARGS, _curses_window_getch__doc__}, @@ -798,9 +800,10 @@ PyDoc_STRVAR(_curses_window_getkey__doc__, " x\n" " X-coordinate.\n" "\n" -"Returning a string instead of an integer, as getch() does. Function keys,\n" -"keypad keys and other special keys return a multibyte string containing the\n" -"key name. In no-delay mode, an exception is raised if there is no input."); +"Returning a string instead of an integer, as getch() does. Function\n" +"keys, keypad keys and other special keys return a multibyte string\n" +"containing the key name. In no-delay mode, an exception is raised\n" +"if there is no input."); #define _CURSES_WINDOW_GETKEY_METHODDEF \ {"getkey", (PyCFunction)_curses_window_getkey, METH_VARARGS, _curses_window_getkey__doc__}, @@ -969,8 +972,8 @@ PyDoc_STRVAR(_curses_window_insch__doc__, " attr\n" " Attributes for the character.\n" "\n" -"All characters to the right of the cursor are shifted one position right, with\n" -"the rightmost characters on the line being lost."); +"All characters to the right of the cursor are shifted one position\n" +"right, with the rightmost characters on the line being lost."); #define _CURSES_WINDOW_INSCH_METHODDEF \ {"insch", (PyCFunction)_curses_window_insch, METH_VARARGS, _curses_window_insch__doc__}, @@ -1035,7 +1038,8 @@ PyDoc_STRVAR(_curses_window_inch__doc__, " x\n" " X-coordinate.\n" "\n" -"The bottom 8 bits are the character proper, and upper bits are the attributes."); +"The bottom 8 bits are the character proper, and upper bits are the\n" +"attributes."); #define _CURSES_WINDOW_INCH_METHODDEF \ {"inch", (PyCFunction)_curses_window_inch, METH_VARARGS, _curses_window_inch__doc__}, @@ -1084,11 +1088,11 @@ PyDoc_STRVAR(_curses_window_insstr__doc__, " attr\n" " Attributes for characters.\n" "\n" -"Insert a character string (as many characters as will fit on the line)\n" -"before the character under the cursor. All characters to the right of\n" -"the cursor are shifted right, with the rightmost characters on the line\n" -"being lost. The cursor position does not change (after moving to y, x,\n" -"if specified)."); +"Insert a character string (as many characters as will fit on the\n" +"line) before the character under the cursor. All characters to the\n" +"right of the cursor are shifted right, with the rightmost characters\n" +"on the line being lost. The cursor position does not change (after\n" +"moving to y, x, if specified)."); #define _CURSES_WINDOW_INSSTR_METHODDEF \ {"insstr", (PyCFunction)_curses_window_insstr, METH_VARARGS, _curses_window_insstr__doc__}, @@ -1159,12 +1163,12 @@ PyDoc_STRVAR(_curses_window_insnstr__doc__, " attr\n" " Attributes for characters.\n" "\n" -"Insert a character string (as many characters as will fit on the line)\n" -"before the character under the cursor, up to n characters. If n is zero\n" -"or negative, the entire string is inserted. All characters to the right\n" -"of the cursor are shifted right, with the rightmost characters on the line\n" -"being lost. The cursor position does not change (after moving to y, x, if\n" -"specified)."); +"Insert a character string (as many characters as will fit on the\n" +"line) before the character under the cursor, up to n characters. If\n" +"n is zero or negative, the entire string is inserted. All\n" +"characters to the right of the cursor are shifted right, with the\n" +"rightmost characters on the line being lost. The cursor position\n" +"does not change (after moving to y, x, if specified)."); #define _CURSES_WINDOW_INSNSTR_METHODDEF \ {"insnstr", (PyCFunction)_curses_window_insnstr, METH_VARARGS, _curses_window_insnstr__doc__}, @@ -1230,7 +1234,8 @@ PyDoc_STRVAR(_curses_window_is_linetouched__doc__, " line\n" " Line number.\n" "\n" -"Raise a curses.error exception if line is not valid for the given window."); +"Raise a curses.error exception if line is not valid for the given\n" +"window."); #define _CURSES_WINDOW_IS_LINETOUCHED_METHODDEF \ {"is_linetouched", (PyCFunction)_curses_window_is_linetouched, METH_O, _curses_window_is_linetouched__doc__}, @@ -1260,9 +1265,9 @@ PyDoc_STRVAR(_curses_window_noutrefresh__doc__, "noutrefresh([pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol])\n" "Mark for refresh but wait.\n" "\n" -"This function updates the data structure representing the desired state of the\n" -"window, but does not force an update of the physical screen. To accomplish\n" -"that, call doupdate()."); +"This function updates the data structure representing the desired\n" +"state of the window, but does not force an update of the physical\n" +"screen. To accomplish that, call doupdate()."); #define _CURSES_WINDOW_NOUTREFRESH_METHODDEF \ {"noutrefresh", (PyCFunction)_curses_window_noutrefresh, METH_VARARGS, _curses_window_noutrefresh__doc__}, @@ -1314,9 +1319,9 @@ PyDoc_STRVAR(_curses_window_noutrefresh__doc__, "\n" "Mark for refresh but wait.\n" "\n" -"This function updates the data structure representing the desired state of the\n" -"window, but does not force an update of the physical screen. To accomplish\n" -"that, call doupdate()."); +"This function updates the data structure representing the desired\n" +"state of the window, but does not force an update of the physical\n" +"screen. To accomplish that, call doupdate()."); #define _CURSES_WINDOW_NOUTREFRESH_METHODDEF \ {"noutrefresh", (PyCFunction)_curses_window_noutrefresh, METH_NOARGS, _curses_window_noutrefresh__doc__}, @@ -1336,14 +1341,15 @@ PyDoc_STRVAR(_curses_window_overlay__doc__, "overlay(destwin, [sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol])\n" "Overlay the window on top of destwin.\n" "\n" -"The windows need not be the same size, only the overlapping region is copied.\n" -"This copy is non-destructive, which means that the current background\n" -"character does not overwrite the old contents of destwin.\n" +"The windows need not be the same size, only the overlapping region\n" +"is copied. This copy is non-destructive, which means that the\n" +"current background character does not overwrite the old contents of\n" +"destwin.\n" "\n" -"To get fine-grained control over the copied region, the second form of\n" -"overlay() can be used. sminrow and smincol are the upper-left coordinates\n" -"of the source window, and the other variables mark a rectangle in the\n" -"destination window."); +"To get fine-grained control over the copied region, the second form\n" +"of overlay() can be used. sminrow and smincol are the upper-left\n" +"coordinates of the source window, and the other variables mark\n" +"a rectangle in the destination window."); #define _CURSES_WINDOW_OVERLAY_METHODDEF \ {"overlay", (PyCFunction)_curses_window_overlay, METH_VARARGS, _curses_window_overlay__doc__}, @@ -1394,14 +1400,15 @@ PyDoc_STRVAR(_curses_window_overwrite__doc__, " dmaxcol])\n" "Overwrite the window on top of destwin.\n" "\n" -"The windows need not be the same size, in which case only the overlapping\n" -"region is copied. This copy is destructive, which means that the current\n" -"background character overwrites the old contents of destwin.\n" +"The windows need not be the same size, in which case only the\n" +"overlapping region is copied. This copy is destructive, which means\n" +"that the current background character overwrites the old contents of\n" +"destwin.\n" "\n" -"To get fine-grained control over the copied region, the second form of\n" -"overwrite() can be used. sminrow and smincol are the upper-left coordinates\n" -"of the source window, the other variables mark a rectangle in the destination\n" -"window."); +"To get fine-grained control over the copied region, the second form\n" +"of overwrite() can be used. sminrow and smincol are the upper-left\n" +"coordinates of the source window, the other variables mark\n" +"a rectangle in the destination window."); #define _CURSES_WINDOW_OVERWRITE_METHODDEF \ {"overwrite", (PyCFunction)_curses_window_overwrite, METH_VARARGS, _curses_window_overwrite__doc__}, @@ -1520,16 +1527,17 @@ PyDoc_STRVAR(_curses_window_refresh__doc__, "Update the display immediately.\n" "\n" "Synchronize actual screen with previous drawing/deleting methods.\n" -"The 6 optional arguments can only be specified when the window is a pad\n" -"created with newpad(). The additional parameters are needed to indicate\n" -"what part of the pad and screen are involved. pminrow and pmincol specify\n" -"the upper left-hand corner of the rectangle to be displayed in the pad.\n" -"sminrow, smincol, smaxrow, and smaxcol specify the edges of the rectangle to\n" -"be displayed on the screen. The lower right-hand corner of the rectangle to\n" -"be displayed in the pad is calculated from the screen coordinates, since the\n" -"rectangles must be the same size. Both rectangles must be entirely contained\n" -"within their respective structures. Negative values of pminrow, pmincol,\n" -"sminrow, or smincol are treated as if they were zero."); +"The 6 optional arguments can only be specified when the window is\n" +"a pad created with newpad(). The additional parameters are needed\n" +"to indicate what part of the pad and screen are involved. pminrow\n" +"and pmincol specify the upper left-hand corner of the rectangle to\n" +"be displayed in the pad. sminrow, smincol, smaxrow, and smaxcol\n" +"specify the edges of the rectangle to be displayed on the screen.\n" +"The lower right-hand corner of the rectangle to be displayed in the\n" +"pad is calculated from the screen coordinates, since the rectangles\n" +"must be the same size. Both rectangles must be entirely contained\n" +"within their respective structures. Negative values of pminrow,\n" +"pmincol, sminrow, or smincol are treated as if they were zero."); #define _CURSES_WINDOW_REFRESH_METHODDEF \ {"refresh", (PyCFunction)_curses_window_refresh, METH_VARARGS, _curses_window_refresh__doc__}, @@ -1627,8 +1635,8 @@ PyDoc_STRVAR(_curses_window_subwin__doc__, " begin_x\n" " Left side x-coordinate.\n" "\n" -"By default, the sub-window will extend from the specified position to the\n" -"lower right corner of the window."); +"By default, the sub-window will extend from the specified position\n" +"to the lower right corner of the window."); #define _CURSES_WINDOW_SUBWIN_METHODDEF \ {"subwin", (PyCFunction)_curses_window_subwin, METH_VARARGS, _curses_window_subwin__doc__}, @@ -1676,7 +1684,8 @@ PyDoc_STRVAR(_curses_window_scroll__doc__, " lines\n" " Number of lines to scroll.\n" "\n" -"Scroll upward if the argument is positive and downward if it is negative."); +"Scroll upward if the argument is positive and downward if it is\n" +"negative."); #define _CURSES_WINDOW_SCROLL_METHODDEF \ {"scroll", (PyCFunction)_curses_window_scroll, METH_VARARGS, _curses_window_scroll__doc__}, @@ -1715,8 +1724,9 @@ PyDoc_STRVAR(_curses_window_touchline__doc__, "touchline(start, count, [changed=True])\n" "Pretend count lines have been changed, starting with line start.\n" "\n" -"If changed is supplied, it specifies whether the affected lines are marked\n" -"as having been changed (changed=True) or unchanged (changed=False)."); +"If changed is supplied, it specifies whether the affected lines are\n" +"marked as having been changed (changed=True) or unchanged\n" +"(changed=False)."); #define _CURSES_WINDOW_TOUCHLINE_METHODDEF \ {"touchline", (PyCFunction)_curses_window_touchline, METH_VARARGS, _curses_window_touchline__doc__}, @@ -1910,11 +1920,12 @@ PyDoc_STRVAR(_curses_cbreak__doc__, " flag\n" " If false, the effect is the same as calling nocbreak().\n" "\n" -"In cbreak mode (sometimes called \"rare\" mode) normal tty line buffering is\n" -"turned off and characters are available to be read one by one. However,\n" -"unlike raw mode, special characters (interrupt, quit, suspend, and flow\n" -"control) retain their effects on the tty driver and calling program.\n" -"Calling first raw() then cbreak() leaves the terminal in cbreak mode."); +"In cbreak mode (sometimes called \"rare\" mode) normal tty line buffering\n" +"is turned off and characters are available to be read one by one.\n" +"However, unlike raw mode, special characters (interrupt, quit, suspend,\n" +"and flow control) retain their effects on the tty driver and calling\n" +"program. Calling first raw() then cbreak() leaves the terminal in\n" +"cbreak mode."); #define _CURSES_CBREAK_METHODDEF \ {"cbreak", _PyCFunction_CAST(_curses_cbreak), METH_FASTCALL, _curses_cbreak__doc__}, @@ -1954,8 +1965,9 @@ PyDoc_STRVAR(_curses_color_content__doc__, " color_number\n" " The number of the color (0 - (COLORS-1)).\n" "\n" -"A 3-tuple is returned, containing the R, G, B values for the given color,\n" -"which will be between 0 (no component) and 1000 (maximum amount of component)."); +"A 3-tuple is returned, containing the R, G, B values for the given\n" +"color, which will be between 0 (no component) and 1000 (maximum amount\n" +"of component)."); #define _CURSES_COLOR_CONTENT_METHODDEF \ {"color_content", (PyCFunction)_curses_color_content, METH_O, _curses_color_content__doc__}, @@ -1988,7 +2000,8 @@ PyDoc_STRVAR(_curses_color_pair__doc__, " The number of the color pair.\n" "\n" "This attribute value can be combined with A_STANDOUT, A_REVERSE, and the\n" -"other A_* attributes. pair_number() is the counterpart to this function."); +"other A_* attributes. pair_number() is the counterpart to this\n" +"function."); #define _CURSES_COLOR_PAIR_METHODDEF \ {"color_pair", (PyCFunction)_curses_color_pair, METH_O, _curses_color_pair__doc__}, @@ -2022,9 +2035,9 @@ PyDoc_STRVAR(_curses_curs_set__doc__, " 0 for invisible, 1 for normal visible, or 2 for very visible.\n" "\n" "If the terminal supports the visibility requested, the previous cursor\n" -"state is returned; otherwise, an exception is raised. On many terminals,\n" -"the \"visible\" mode is an underline cursor and the \"very visible\" mode is\n" -"a block cursor."); +"state is returned; otherwise, an exception is raised. On many\n" +"terminals, the \"visible\" mode is an underline cursor and the \"very\n" +"visible\" mode is a block cursor."); #define _CURSES_CURS_SET_METHODDEF \ {"curs_set", (PyCFunction)_curses_curs_set, METH_O, _curses_curs_set__doc__}, @@ -2076,7 +2089,8 @@ PyDoc_STRVAR(_curses_def_shell_mode__doc__, "\n" "Save the current terminal mode as the \"shell\" mode.\n" "\n" -"The \"shell\" mode is the mode when the running program is not using curses.\n" +"The \"shell\" mode is the mode when the running program is not using\n" +"curses.\n" "\n" "Subsequent calls to reset_shell_mode() will restore this mode."); @@ -2150,7 +2164,8 @@ PyDoc_STRVAR(_curses_echo__doc__, " flag\n" " If false, the effect is the same as calling noecho().\n" "\n" -"In echo mode, each character input is echoed to the screen as it is entered."); +"In echo mode, each character input is echoed to the screen as it is\n" +"entered."); #define _CURSES_ECHO_METHODDEF \ {"echo", _PyCFunction_CAST(_curses_echo), METH_FASTCALL, _curses_echo__doc__}, @@ -2223,7 +2238,8 @@ PyDoc_STRVAR(_curses_flash__doc__, "\n" "Flash the screen.\n" "\n" -"That is, change it to reverse-video and then change it back in a short interval."); +"That is, change it to reverse-video and then change it back in a short\n" +"interval."); #define _CURSES_FLASH_METHODDEF \ {"flash", (PyCFunction)_curses_flash, METH_NOARGS, _curses_flash__doc__}, @@ -2243,8 +2259,8 @@ PyDoc_STRVAR(_curses_flushinp__doc__, "\n" "Flush all input buffers.\n" "\n" -"This throws away any typeahead that has been typed by the user and has not\n" -"yet been processed by the program."); +"This throws away any typeahead that has been typed by the user and has\n" +"not yet been processed by the program."); #define _CURSES_FLUSHINP_METHODDEF \ {"flushinp", (PyCFunction)_curses_flushinp, METH_NOARGS, _curses_flushinp__doc__}, @@ -2614,8 +2630,9 @@ PyDoc_STRVAR(_curses_init_pair__doc__, " bg\n" " Background color number (-1 - (COLORS-1)).\n" "\n" -"If the color-pair was previously initialized, the screen is refreshed and\n" -"all occurrences of that color-pair are changed to the new definition."); +"If the color-pair was previously initialized, the screen is refreshed\n" +"and all occurrences of that color-pair are changed to the new\n" +"definition."); #define _CURSES_INIT_PAIR_METHODDEF \ {"init_pair", _PyCFunction_CAST(_curses_init_pair), METH_FASTCALL, _curses_init_pair__doc__}, @@ -2774,9 +2791,9 @@ PyDoc_STRVAR(_curses_get_escdelay__doc__, "\n" "Gets the curses ESCDELAY setting.\n" "\n" -"Gets the number of milliseconds to wait after reading an escape character,\n" -"to distinguish between an individual escape character entered on the\n" -"keyboard from escape sequences sent by cursor and function keys."); +"Gets the number of milliseconds to wait after reading an escape\n" +"character, to distinguish between an individual escape character entered\n" +"on the keyboard from escape sequences sent by cursor and function keys."); #define _CURSES_GET_ESCDELAY_METHODDEF \ {"get_escdelay", (PyCFunction)_curses_get_escdelay, METH_NOARGS, _curses_get_escdelay__doc__}, @@ -2803,9 +2820,9 @@ PyDoc_STRVAR(_curses_set_escdelay__doc__, " ms\n" " length of the delay in milliseconds.\n" "\n" -"Sets the number of milliseconds to wait after reading an escape character,\n" -"to distinguish between an individual escape character entered on the\n" -"keyboard from escape sequences sent by cursor and function keys."); +"Sets the number of milliseconds to wait after reading an escape\n" +"character, to distinguish between an individual escape character entered\n" +"on the keyboard from escape sequences sent by cursor and function keys."); #define _CURSES_SET_ESCDELAY_METHODDEF \ {"set_escdelay", (PyCFunction)_curses_set_escdelay, METH_O, _curses_set_escdelay__doc__}, @@ -2839,8 +2856,8 @@ PyDoc_STRVAR(_curses_get_tabsize__doc__, "\n" "Gets the curses TABSIZE setting.\n" "\n" -"Gets the number of columns used by the curses library when converting a tab\n" -"character to spaces as it adds the tab to a window."); +"Gets the number of columns used by the curses library when converting\n" +"a tab character to spaces as it adds the tab to a window."); #define _CURSES_GET_TABSIZE_METHODDEF \ {"get_tabsize", (PyCFunction)_curses_get_tabsize, METH_NOARGS, _curses_get_tabsize__doc__}, @@ -2867,8 +2884,8 @@ PyDoc_STRVAR(_curses_set_tabsize__doc__, " size\n" " rendered cell width of a tab character.\n" "\n" -"Sets the number of columns used by the curses library when converting a tab\n" -"character to spaces as it adds the tab to a window."); +"Sets the number of columns used by the curses library when converting\n" +"a tab character to spaces as it adds the tab to a window."); #define _CURSES_SET_TABSIZE_METHODDEF \ {"set_tabsize", (PyCFunction)_curses_set_tabsize, METH_O, _curses_set_tabsize__doc__}, @@ -3039,8 +3056,8 @@ PyDoc_STRVAR(_curses_longname__doc__, "\n" "Return the terminfo long name field describing the current terminal.\n" "\n" -"The maximum length of a verbose description is 128 characters. It is defined\n" -"only after the call to initscr()."); +"The maximum length of a verbose description is 128 characters. It is\n" +"defined only after the call to initscr()."); #define _CURSES_LONGNAME_METHODDEF \ {"longname", (PyCFunction)_curses_longname, METH_NOARGS, _curses_longname__doc__}, @@ -3097,8 +3114,8 @@ PyDoc_STRVAR(_curses_mouseinterval__doc__, " Time in milliseconds.\n" "\n" "Set the maximum time that can elapse between press and release events in\n" -"order for them to be recognized as a click, and return the previous interval\n" -"value."); +"order for them to be recognized as a click, and return the previous\n" +"interval value."); #define _CURSES_MOUSEINTERVAL_METHODDEF \ {"mouseinterval", (PyCFunction)_curses_mouseinterval, METH_O, _curses_mouseinterval__doc__}, @@ -3133,9 +3150,10 @@ PyDoc_STRVAR(_curses_mousemask__doc__, "Set the mouse events to be reported, and return a tuple (availmask, oldmask).\n" "\n" "Return a tuple (availmask, oldmask). availmask indicates which of the\n" -"specified mouse events can be reported; on complete failure it returns 0.\n" -"oldmask is the previous value of the given window\'s mouse event mask.\n" -"If this function is never called, no mouse events are ever reported."); +"specified mouse events can be reported; on complete failure it returns\n" +"0. oldmask is the previous value of the given window\'s mouse event\n" +"mask. If this function is never called, no mouse events are ever\n" +"reported."); #define _CURSES_MOUSEMASK_METHODDEF \ {"mousemask", (PyCFunction)_curses_mousemask, METH_O, _curses_mousemask__doc__}, @@ -3267,8 +3285,8 @@ PyDoc_STRVAR(_curses_newwin__doc__, " begin_x\n" " Left side x-coordinate.\n" "\n" -"By default, the window will extend from the specified position to the lower\n" -"right corner of the screen."); +"By default, the window will extend from the specified position to the\n" +"lower right corner of the screen."); #define _CURSES_NEWWIN_METHODDEF \ {"newwin", (PyCFunction)_curses_newwin, METH_VARARGS, _curses_newwin__doc__}, @@ -3318,8 +3336,9 @@ PyDoc_STRVAR(_curses_nl__doc__, " flag\n" " If false, the effect is the same as calling nonl().\n" "\n" -"This mode translates the return key into newline on input, and translates\n" -"newline into return and line-feed on output. Newline mode is initially on."); +"This mode translates the return key into newline on input, and\n" +"translates newline into return and line-feed on output. Newline mode\n" +"is initially on."); #define _CURSES_NL_METHODDEF \ {"nl", _PyCFunction_CAST(_curses_nl), METH_FASTCALL, _curses_nl__doc__}, @@ -3396,8 +3415,8 @@ PyDoc_STRVAR(_curses_nonl__doc__, "\n" "Leave newline mode.\n" "\n" -"Disable translation of return into newline on input, and disable low-level\n" -"translation of newline into newline/return on output."); +"Disable translation of return into newline on input, and disable\n" +"low-level translation of newline into newline/return on output."); #define _CURSES_NONL_METHODDEF \ {"nonl", (PyCFunction)_curses_nonl, METH_NOARGS, _curses_nonl__doc__}, @@ -3613,8 +3632,8 @@ PyDoc_STRVAR(_curses_raw__doc__, " If false, the effect is the same as calling noraw().\n" "\n" "In raw mode, normal line buffering and processing of interrupt, quit,\n" -"suspend, and flow control keys are turned off; characters are presented to\n" -"curses input functions one by one."); +"suspend, and flow control keys are turned off; characters are presented\n" +"to curses input functions one by one."); #define _CURSES_RAW_METHODDEF \ {"raw", _PyCFunction_CAST(_curses_raw), METH_FASTCALL, _curses_raw__doc__}, @@ -3712,8 +3731,8 @@ PyDoc_STRVAR(_curses_resizeterm__doc__, " ncols\n" " Width.\n" "\n" -"Adjusts other bookkeeping data used by the curses library that record the\n" -"window dimensions (in particular the SIGWINCH handler)."); +"Adjusts other bookkeeping data used by the curses library that record\n" +"the window dimensions (in particular the SIGWINCH handler)."); #define _CURSES_RESIZETERM_METHODDEF \ {"resizeterm", _PyCFunction_CAST(_curses_resizeterm), METH_FASTCALL, _curses_resizeterm__doc__}, @@ -3791,10 +3810,11 @@ PyDoc_STRVAR(_curses_resize_term__doc__, " Width.\n" "\n" "When resizing the windows, resize_term() blank-fills the areas that are\n" -"extended. The calling application should fill in these areas with appropriate\n" -"data. The resize_term() function attempts to resize all windows. However,\n" -"due to the calling convention of pads, it is not possible to resize these\n" -"without additional interaction with the application."); +"extended. The calling application should fill in these areas with\n" +"appropriate data. The resize_term() function attempts to resize all\n" +"windows. However, due to the calling convention of pads, it is not\n" +"possible to resize these without additional interaction with the\n" +"application."); #define _CURSES_RESIZE_TERM_METHODDEF \ {"resize_term", _PyCFunction_CAST(_curses_resize_term), METH_FASTCALL, _curses_resize_term__doc__}, @@ -3929,12 +3949,12 @@ PyDoc_STRVAR(_curses_start_color__doc__, "\n" "Initializes eight basic colors and global variables COLORS and COLOR_PAIRS.\n" "\n" -"Must be called if the programmer wants to use colors, and before any other\n" -"color manipulation routine is called. It is good practice to call this\n" -"routine right after initscr().\n" +"Must be called if the programmer wants to use colors, and before any\n" +"other color manipulation routine is called. It is good practice to call\n" +"this routine right after initscr().\n" "\n" -"It also restores the colors on the terminal to the values they had when the\n" -"terminal was just turned on."); +"It also restores the colors on the terminal to the values they had when\n" +"the terminal was just turned on."); #define _CURSES_START_COLOR_METHODDEF \ {"start_color", (PyCFunction)_curses_start_color, METH_NOARGS, _curses_start_color__doc__}, @@ -4036,8 +4056,8 @@ PyDoc_STRVAR(_curses_tigetnum__doc__, " capname\n" " The terminfo capability name.\n" "\n" -"The value -2 is returned if capname is not a numeric capability, or -1 if\n" -"it is canceled or absent from the terminal description."); +"The value -2 is returned if capname is not a numeric capability, or -1\n" +"if it is canceled or absent from the terminal description."); #define _CURSES_TIGETNUM_METHODDEF \ {"tigetnum", (PyCFunction)_curses_tigetnum, METH_O, _curses_tigetnum__doc__}, @@ -4079,8 +4099,8 @@ PyDoc_STRVAR(_curses_tigetstr__doc__, " capname\n" " The terminfo capability name.\n" "\n" -"None is returned if capname is not a string capability, or is canceled or\n" -"absent from the terminal description."); +"None is returned if capname is not a string capability, or is canceled\n" +"or absent from the terminal description."); #define _CURSES_TIGETSTR_METHODDEF \ {"tigetstr", (PyCFunction)_curses_tigetstr, METH_O, _curses_tigetstr__doc__}, @@ -4234,14 +4254,14 @@ PyDoc_STRVAR(_curses_use_env__doc__, "\n" "Use environment variables LINES and COLUMNS.\n" "\n" -"If used, this function should be called before initscr() or newterm() are\n" -"called.\n" +"If used, this function should be called before initscr() or newterm()\n" +"are called.\n" "\n" -"When flag is False, the values of lines and columns specified in the terminfo\n" -"database will be used, even if environment variables LINES and COLUMNS (used\n" -"by default) are set, or if curses is running in a window (in which case\n" -"default behavior would be to use the window size if LINES and COLUMNS are\n" -"not set)."); +"When flag is False, the values of lines and columns specified in the\n" +"terminfo database will be used, even if environment variables LINES and\n" +"COLUMNS (used by default) are set, or if curses is running in a window\n" +"(in which case default behavior would be to use the window size if LINES\n" +"and COLUMNS are not set)."); #define _CURSES_USE_ENV_METHODDEF \ {"use_env", (PyCFunction)_curses_use_env, METH_O, _curses_use_env__doc__}, @@ -4450,4 +4470,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #define _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_ASSUME_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=79ddaae4da3b80df input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e7c7932f4a4e9bce input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_datetimemodule.c.h b/Modules/clinic/_datetimemodule.c.h index 18e6129fad8a89..fac41e7aefc7f4 100644 --- a/Modules/clinic/_datetimemodule.c.h +++ b/Modules/clinic/_datetimemodule.c.h @@ -8,14 +8,214 @@ preserve #endif #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() +PyDoc_STRVAR(delta_new__doc__, +"timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0,\n" +" hours=0, weeks=0)\n" +"--\n" +"\n" +"Difference between two datetime values.\n" +"\n" +"All arguments are optional and default to 0.\n" +"Arguments may be integers or floats, and may be positive or negative."); + +static PyObject * +delta_new_impl(PyTypeObject *type, PyObject *days, PyObject *seconds, + PyObject *microseconds, PyObject *milliseconds, + PyObject *minutes, PyObject *hours, PyObject *weeks); + +static PyObject * +delta_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 7 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(days), &_Py_ID(seconds), &_Py_ID(microseconds), &_Py_ID(milliseconds), &_Py_ID(minutes), &_Py_ID(hours), &_Py_ID(weeks), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"days", "seconds", "microseconds", "milliseconds", "minutes", "hours", "weeks", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "timedelta", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[7]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; + PyObject *days = NULL; + PyObject *seconds = NULL; + PyObject *microseconds = NULL; + PyObject *milliseconds = NULL; + PyObject *minutes = NULL; + PyObject *hours = NULL; + PyObject *weeks = NULL; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 7, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (fastargs[0]) { + days = fastargs[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[1]) { + seconds = fastargs[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[2]) { + microseconds = fastargs[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[3]) { + milliseconds = fastargs[3]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[4]) { + minutes = fastargs[4]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[5]) { + hours = fastargs[5]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + weeks = fastargs[6]; +skip_optional_pos: + return_value = delta_new_impl(type, days, seconds, microseconds, milliseconds, minutes, hours, weeks); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_date__doc__, +"date(year, month, day)\n" +"--\n" +"\n" +"Concrete date type."); + +static PyObject * +datetime_date_impl(PyTypeObject *type, int year, int month, int day); + +static PyObject * +datetime_date(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(year), &_Py_ID(month), &_Py_ID(day), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"year", "month", "day", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "date", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + int year; + int month; + int day; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + year = PyLong_AsInt(fastargs[0]); + if (year == -1 && PyErr_Occurred()) { + goto exit; + } + month = PyLong_AsInt(fastargs[1]); + if (month == -1 && PyErr_Occurred()) { + goto exit; + } + day = PyLong_AsInt(fastargs[2]); + if (day == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = datetime_date_impl(type, year, month, day); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_date_today__doc__, +"today($type, /)\n" +"--\n" +"\n" +"Current date or datetime.\n" +"\n" +"Equivalent to fromtimestamp(time.time())."); + +#define DATETIME_DATE_TODAY_METHODDEF \ + {"today", (PyCFunction)datetime_date_today, METH_NOARGS|METH_CLASS, datetime_date_today__doc__}, + +static PyObject * +datetime_date_today_impl(PyTypeObject *type); + +static PyObject * +datetime_date_today(PyObject *type, PyObject *Py_UNUSED(ignored)) +{ + return datetime_date_today_impl((PyTypeObject *)type); +} + PyDoc_STRVAR(datetime_date_fromtimestamp__doc__, "fromtimestamp($type, timestamp, /)\n" "--\n" "\n" "Create a date from a POSIX timestamp.\n" "\n" -"The timestamp is a number, e.g. created via time.time(), that is interpreted\n" -"as local time."); +"The timestamp is a number, e.g. created via time.time(), that is\n" +"interpreted as local time."); #define DATETIME_DATE_FROMTIMESTAMP_METHODDEF \ {"fromtimestamp", (PyCFunction)datetime_date_fromtimestamp, METH_O|METH_CLASS, datetime_date_fromtimestamp__doc__}, @@ -33,12 +233,83 @@ datetime_date_fromtimestamp(PyObject *type, PyObject *timestamp) return return_value; } +PyDoc_STRVAR(datetime_date_fromordinal__doc__, +"fromordinal($type, ordinal, /)\n" +"--\n" +"\n" +"Construct a date from a proleptic Gregorian ordinal.\n" +"\n" +"January 1 of year 1 is day 1. Only the year, month and day are\n" +"non-zero in the result."); + +#define DATETIME_DATE_FROMORDINAL_METHODDEF \ + {"fromordinal", (PyCFunction)datetime_date_fromordinal, METH_O|METH_CLASS, datetime_date_fromordinal__doc__}, + static PyObject * -iso_calendar_date_new_impl(PyTypeObject *type, int year, int week, - int weekday); +datetime_date_fromordinal_impl(PyTypeObject *type, int ordinal); static PyObject * -iso_calendar_date_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +datetime_date_fromordinal(PyObject *type, PyObject *arg) +{ + PyObject *return_value = NULL; + int ordinal; + + ordinal = PyLong_AsInt(arg); + if (ordinal == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = datetime_date_fromordinal_impl((PyTypeObject *)type, ordinal); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_date_fromisoformat__doc__, +"fromisoformat($type, string, /)\n" +"--\n" +"\n" +"Construct a date from a string in ISO 8601 format."); + +#define DATETIME_DATE_FROMISOFORMAT_METHODDEF \ + {"fromisoformat", (PyCFunction)datetime_date_fromisoformat, METH_O|METH_CLASS, datetime_date_fromisoformat__doc__}, + +static PyObject * +datetime_date_fromisoformat_impl(PyTypeObject *type, PyObject *string); + +static PyObject * +datetime_date_fromisoformat(PyObject *type, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *string; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("fromisoformat", "argument", "str", arg); + goto exit; + } + string = arg; + return_value = datetime_date_fromisoformat_impl((PyTypeObject *)type, string); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_date_fromisocalendar__doc__, +"fromisocalendar($type, /, year, week, day)\n" +"--\n" +"\n" +"Construct a date from the ISO year, week number and weekday.\n" +"\n" +"This is the inverse of the date.isocalendar() function."); + +#define DATETIME_DATE_FROMISOCALENDAR_METHODDEF \ + {"fromisocalendar", _PyCFunction_CAST(datetime_date_fromisocalendar), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, datetime_date_fromisocalendar__doc__}, + +static PyObject * +datetime_date_fromisocalendar_impl(PyTypeObject *type, int year, int week, + int day); + +static PyObject * +datetime_date_fromisocalendar(PyObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -52,7 +323,7 @@ iso_calendar_date_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) .ob_hash = -1, - .ob_item = { &_Py_ID(year), &_Py_ID(week), &_Py_ID(weekday), }, + .ob_item = { &_Py_ID(year), &_Py_ID(week), &_Py_ID(day), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -61,58 +332,1309 @@ iso_calendar_date_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"year", "week", "weekday", NULL}; + static const char * const _keywords[] = {"year", "week", "day", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, - .fname = "IsoCalendarDate", + .fname = "fromisocalendar", .kwtuple = KWTUPLE, }; #undef KWTUPLE PyObject *argsbuf[3]; - PyObject * const *fastargs; - Py_ssize_t nargs = PyTuple_GET_SIZE(args); int year; int week; - int weekday; + int day; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); - if (!fastargs) { + if (!args) { goto exit; } - year = PyLong_AsInt(fastargs[0]); + year = PyLong_AsInt(args[0]); if (year == -1 && PyErr_Occurred()) { goto exit; } - week = PyLong_AsInt(fastargs[1]); + week = PyLong_AsInt(args[1]); if (week == -1 && PyErr_Occurred()) { goto exit; } - weekday = PyLong_AsInt(fastargs[2]); - if (weekday == -1 && PyErr_Occurred()) { + day = PyLong_AsInt(args[2]); + if (day == -1 && PyErr_Occurred()) { goto exit; } - return_value = iso_calendar_date_new_impl(type, year, week, weekday); + return_value = datetime_date_fromisocalendar_impl((PyTypeObject *)type, year, week, day); exit: return return_value; } -PyDoc_STRVAR(datetime_date_replace__doc__, -"replace($self, /, year=unchanged, month=unchanged, day=unchanged)\n" +PyDoc_STRVAR(datetime_date_strptime__doc__, +"strptime($type, string, format, /)\n" "--\n" "\n" -"Return date with new specified fields."); +"Parse string according to the given date format (like time.strptime()).\n" +"\n" +"For a list of supported format codes, see the documentation:\n" +" https://docs.python.org/3/library/datetime.html#format-codes"); + +#define DATETIME_DATE_STRPTIME_METHODDEF \ + {"strptime", _PyCFunction_CAST(datetime_date_strptime), METH_FASTCALL|METH_CLASS, datetime_date_strptime__doc__}, + +static PyObject * +datetime_date_strptime_impl(PyTypeObject *type, PyObject *string, + PyObject *format); + +static PyObject * +datetime_date_strptime(PyObject *type, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *string; + PyObject *format; + + if (!_PyArg_CheckPositional("strptime", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("strptime", "argument 1", "str", args[0]); + goto exit; + } + string = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("strptime", "argument 2", "str", args[1]); + goto exit; + } + format = args[1]; + return_value = datetime_date_strptime_impl((PyTypeObject *)type, string, format); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_date_strftime__doc__, +"strftime($self, /, format)\n" +"--\n" +"\n" +"Format using strftime().\n" +"\n" +"Example: \"%d/%m/%Y, %H:%M:%S\".\n" +"\n" +"For a list of supported format codes, see the documentation:\n" +" https://docs.python.org/3/library/datetime.html#format-codes"); + +#define DATETIME_DATE_STRFTIME_METHODDEF \ + {"strftime", _PyCFunction_CAST(datetime_date_strftime), METH_FASTCALL|METH_KEYWORDS, datetime_date_strftime__doc__}, + +static PyObject * +datetime_date_strftime_impl(PyObject *self, PyObject *format); + +static PyObject * +datetime_date_strftime(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(format), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"format", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "strftime", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *format; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("strftime", "argument 'format'", "str", args[0]); + goto exit; + } + format = args[0]; + return_value = datetime_date_strftime_impl(self, format); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_date___format____doc__, +"__format__($self, format, /)\n" +"--\n" +"\n" +"Formats self with strftime."); + +#define DATETIME_DATE___FORMAT___METHODDEF \ + {"__format__", (PyCFunction)datetime_date___format__, METH_O, datetime_date___format____doc__}, + +static PyObject * +datetime_date___format___impl(PyObject *self, PyObject *format); + +static PyObject * +datetime_date___format__(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *format; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("__format__", "argument", "str", arg); + goto exit; + } + format = arg; + return_value = datetime_date___format___impl(self, format); + +exit: + return return_value; +} + +static PyObject * +iso_calendar_date_new_impl(PyTypeObject *type, int year, int week, + int weekday); + +static PyObject * +iso_calendar_date_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(year), &_Py_ID(week), &_Py_ID(weekday), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"year", "week", "weekday", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "IsoCalendarDate", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + int year; + int week; + int weekday; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + year = PyLong_AsInt(fastargs[0]); + if (year == -1 && PyErr_Occurred()) { + goto exit; + } + week = PyLong_AsInt(fastargs[1]); + if (week == -1 && PyErr_Occurred()) { + goto exit; + } + weekday = PyLong_AsInt(fastargs[2]); + if (weekday == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = iso_calendar_date_new_impl(type, year, week, weekday); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_date_replace__doc__, +"replace($self, /, year=unchanged, month=unchanged, day=unchanged)\n" +"--\n" +"\n" +"Return date with new specified fields."); + +#define DATETIME_DATE_REPLACE_METHODDEF \ + {"replace", _PyCFunction_CAST(datetime_date_replace), METH_FASTCALL|METH_KEYWORDS, datetime_date_replace__doc__}, + +static PyObject * +datetime_date_replace_impl(PyDateTime_Date *self, int year, int month, + int day); + +static PyObject * +datetime_date_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(year), &_Py_ID(month), &_Py_ID(day), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"year", "month", "day", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "replace", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int year = GET_YEAR(self); + int month = GET_MONTH(self); + int day = GET_DAY(self); + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + year = PyLong_AsInt(args[0]); + if (year == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[1]) { + month = PyLong_AsInt(args[1]); + if (month == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + day = PyLong_AsInt(args[2]); + if (day == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_pos: + return_value = datetime_date_replace_impl((PyDateTime_Date *)self, year, month, day); + +exit: + return return_value; +} + +PyDoc_STRVAR(timezone_new__doc__, +"timezone(offset, name=<unrepresentable>)\n" +"--\n" +"\n" +"Fixed offset from UTC implementation of tzinfo."); + +static PyObject * +timezone_new_impl(PyTypeObject *type, PyObject *offset, PyObject *name); + +static PyObject * +timezone_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(offset), &_Py_ID(name), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"offset", "name", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "timezone", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + PyObject *offset; + PyObject *name = NULL; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!PyObject_TypeCheck(fastargs[0], DELTA_TYPE(NO_STATE))) { + _PyArg_BadArgument("timezone", "argument 'offset'", (DELTA_TYPE(NO_STATE))->tp_name, fastargs[0]); + goto exit; + } + offset = fastargs[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (!PyUnicode_Check(fastargs[1])) { + _PyArg_BadArgument("timezone", "argument 'name'", "str", fastargs[1]); + goto exit; + } + name = fastargs[1]; +skip_optional_pos: + return_value = timezone_new_impl(type, offset, name); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_time__doc__, +"time(hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0)\n" +"--\n" +"\n" +"Time with time zone.\n" +"\n" +"All arguments are optional. tzinfo may be None, or an instance of\n" +"a tzinfo subclass. The remaining arguments may be ints."); + +static PyObject * +datetime_time_impl(PyTypeObject *type, int hour, int minute, int second, + int microsecond, PyObject *tzinfo, int fold); + +static PyObject * +datetime_time(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 6 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(hour), &_Py_ID(minute), &_Py_ID(second), &_Py_ID(microsecond), &_Py_ID(tzinfo), &_Py_ID(fold), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"hour", "minute", "second", "microsecond", "tzinfo", "fold", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "time", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[6]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; + int hour = 0; + int minute = 0; + int second = 0; + int microsecond = 0; + PyObject *tzinfo = Py_None; + int fold = 0; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (fastargs[0]) { + hour = PyLong_AsInt(fastargs[0]); + if (hour == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[1]) { + minute = PyLong_AsInt(fastargs[1]); + if (minute == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[2]) { + second = PyLong_AsInt(fastargs[2]); + if (second == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[3]) { + microsecond = PyLong_AsInt(fastargs[3]); + if (microsecond == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[4]) { + tzinfo = fastargs[4]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + fold = PyLong_AsInt(fastargs[5]); + if (fold == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_kwonly: + return_value = datetime_time_impl(type, hour, minute, second, microsecond, tzinfo, fold); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_time_strptime__doc__, +"strptime($type, string, format, /)\n" +"--\n" +"\n" +"Parse string according to the given time format (like time.strptime()).\n" +"\n" +"For a list of supported format codes, see the documentation:\n" +" https://docs.python.org/3/library/datetime.html#format-codes"); + +#define DATETIME_TIME_STRPTIME_METHODDEF \ + {"strptime", _PyCFunction_CAST(datetime_time_strptime), METH_FASTCALL|METH_CLASS, datetime_time_strptime__doc__}, + +static PyObject * +datetime_time_strptime_impl(PyTypeObject *type, PyObject *string, + PyObject *format); + +static PyObject * +datetime_time_strptime(PyObject *type, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *string; + PyObject *format; + + if (!_PyArg_CheckPositional("strptime", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("strptime", "argument 1", "str", args[0]); + goto exit; + } + string = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("strptime", "argument 2", "str", args[1]); + goto exit; + } + format = args[1]; + return_value = datetime_time_strptime_impl((PyTypeObject *)type, string, format); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_time_isoformat__doc__, +"isoformat($self, /, timespec=\'auto\')\n" +"--\n" +"\n" +"Return the time formatted according to ISO.\n" +"\n" +"The full format is \'HH:MM:SS.mmmmmm+zz:zz\'. By default, the\n" +"fractional part is omitted if self.microsecond == 0.\n" +"\n" +"The optional argument timespec specifies the number of additional\n" +"terms of the time to include. Valid options are \'auto\', \'hours\',\n" +"\'minutes\', \'seconds\', \'milliseconds\' and \'microseconds\'."); + +#define DATETIME_TIME_ISOFORMAT_METHODDEF \ + {"isoformat", _PyCFunction_CAST(datetime_time_isoformat), METH_FASTCALL|METH_KEYWORDS, datetime_time_isoformat__doc__}, + +static PyObject * +datetime_time_isoformat_impl(PyDateTime_Time *self, const char *timespec); + +static PyObject * +datetime_time_isoformat(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(timespec), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"timespec", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "isoformat", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + const char *timespec = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("isoformat", "argument 'timespec'", "str", args[0]); + goto exit; + } + Py_ssize_t timespec_length; + timespec = PyUnicode_AsUTF8AndSize(args[0], &timespec_length); + if (timespec == NULL) { + goto exit; + } + if (strlen(timespec) != (size_t)timespec_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } +skip_optional_pos: + return_value = datetime_time_isoformat_impl((PyDateTime_Time *)self, timespec); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_time_strftime__doc__, +"strftime($self, /, format)\n" +"--\n" +"\n" +"Format using strftime().\n" +"\n" +"The date part of the timestamp passed to underlying strftime should\n" +"not be used.\n" +"\n" +"For a list of supported format codes, see the documentation:\n" +" https://docs.python.org/3/library/datetime.html#format-codes"); + +#define DATETIME_TIME_STRFTIME_METHODDEF \ + {"strftime", _PyCFunction_CAST(datetime_time_strftime), METH_FASTCALL|METH_KEYWORDS, datetime_time_strftime__doc__}, + +static PyObject * +datetime_time_strftime_impl(PyDateTime_Time *self, PyObject *format); + +static PyObject * +datetime_time_strftime(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(format), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"format", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "strftime", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *format; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("strftime", "argument 'format'", "str", args[0]); + goto exit; + } + format = args[0]; + return_value = datetime_time_strftime_impl((PyDateTime_Time *)self, format); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_time___format____doc__, +"__format__($self, format, /)\n" +"--\n" +"\n" +"Formats self with strftime."); + +#define DATETIME_TIME___FORMAT___METHODDEF \ + {"__format__", (PyCFunction)datetime_time___format__, METH_O, datetime_time___format____doc__}, + +static PyObject * +datetime_time___format___impl(PyObject *self, PyObject *format); + +static PyObject * +datetime_time___format__(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *format; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("__format__", "argument", "str", arg); + goto exit; + } + format = arg; + return_value = datetime_time___format___impl(self, format); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_time_replace__doc__, +"replace($self, /, hour=unchanged, minute=unchanged, second=unchanged,\n" +" microsecond=unchanged, tzinfo=unchanged, *, fold=unchanged)\n" +"--\n" +"\n" +"Return time with new specified fields."); + +#define DATETIME_TIME_REPLACE_METHODDEF \ + {"replace", _PyCFunction_CAST(datetime_time_replace), METH_FASTCALL|METH_KEYWORDS, datetime_time_replace__doc__}, + +static PyObject * +datetime_time_replace_impl(PyDateTime_Time *self, int hour, int minute, + int second, int microsecond, PyObject *tzinfo, + int fold); + +static PyObject * +datetime_time_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 6 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(hour), &_Py_ID(minute), &_Py_ID(second), &_Py_ID(microsecond), &_Py_ID(tzinfo), &_Py_ID(fold), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"hour", "minute", "second", "microsecond", "tzinfo", "fold", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "replace", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[6]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int hour = TIME_GET_HOUR(self); + int minute = TIME_GET_MINUTE(self); + int second = TIME_GET_SECOND(self); + int microsecond = TIME_GET_MICROSECOND(self); + PyObject *tzinfo = HASTZINFO(self) ? ((PyDateTime_Time *)self)->tzinfo : Py_None; + int fold = TIME_GET_FOLD(self); + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + hour = PyLong_AsInt(args[0]); + if (hour == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[1]) { + minute = PyLong_AsInt(args[1]); + if (minute == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[2]) { + second = PyLong_AsInt(args[2]); + if (second == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[3]) { + microsecond = PyLong_AsInt(args[3]); + if (microsecond == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[4]) { + tzinfo = args[4]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + fold = PyLong_AsInt(args[5]); + if (fold == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_kwonly: + return_value = datetime_time_replace_impl((PyDateTime_Time *)self, hour, minute, second, microsecond, tzinfo, fold); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_time_fromisoformat__doc__, +"fromisoformat($type, string, /)\n" +"--\n" +"\n" +"Construct a time from a string in ISO 8601 format."); + +#define DATETIME_TIME_FROMISOFORMAT_METHODDEF \ + {"fromisoformat", (PyCFunction)datetime_time_fromisoformat, METH_O|METH_CLASS, datetime_time_fromisoformat__doc__}, + +static PyObject * +datetime_time_fromisoformat_impl(PyTypeObject *type, PyObject *string); + +static PyObject * +datetime_time_fromisoformat(PyObject *type, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *string; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("fromisoformat", "argument", "str", arg); + goto exit; + } + string = arg; + return_value = datetime_time_fromisoformat_impl((PyTypeObject *)type, string); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_time___reduce_ex____doc__, +"__reduce_ex__($self, proto, /)\n" +"--\n" +"\n"); + +#define DATETIME_TIME___REDUCE_EX___METHODDEF \ + {"__reduce_ex__", (PyCFunction)datetime_time___reduce_ex__, METH_O, datetime_time___reduce_ex____doc__}, + +static PyObject * +datetime_time___reduce_ex___impl(PyDateTime_Time *self, int proto); + +static PyObject * +datetime_time___reduce_ex__(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + int proto; + + proto = PyLong_AsInt(arg); + if (proto == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = datetime_time___reduce_ex___impl((PyDateTime_Time *)self, proto); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_time___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n"); + +#define DATETIME_TIME___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)datetime_time___reduce__, METH_NOARGS, datetime_time___reduce____doc__}, + +static PyObject * +datetime_time___reduce___impl(PyDateTime_Time *self); + +static PyObject * +datetime_time___reduce__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return datetime_time___reduce___impl((PyDateTime_Time *)self); +} + +PyDoc_STRVAR(datetime_datetime__doc__, +"datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0,\n" +" tzinfo=None, *, fold=0)\n" +"--\n" +"\n" +"A combination of a date and a time.\n" +"\n" +"The year, month and day arguments are required. tzinfo may be None, or\n" +"an instance of a tzinfo subclass. The remaining arguments may be ints."); + +static PyObject * +datetime_datetime_impl(PyTypeObject *type, int year, int month, int day, + int hour, int minute, int second, int microsecond, + PyObject *tzinfo, int fold); + +static PyObject * +datetime_datetime(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 9 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(year), &_Py_ID(month), &_Py_ID(day), &_Py_ID(hour), &_Py_ID(minute), &_Py_ID(second), &_Py_ID(microsecond), &_Py_ID(tzinfo), &_Py_ID(fold), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"year", "month", "day", "hour", "minute", "second", "microsecond", "tzinfo", "fold", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "datetime", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[9]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 3; + int year; + int month; + int day; + int hour = 0; + int minute = 0; + int second = 0; + int microsecond = 0; + PyObject *tzinfo = Py_None; + int fold = 0; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 3, /*maxpos*/ 8, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + year = PyLong_AsInt(fastargs[0]); + if (year == -1 && PyErr_Occurred()) { + goto exit; + } + month = PyLong_AsInt(fastargs[1]); + if (month == -1 && PyErr_Occurred()) { + goto exit; + } + day = PyLong_AsInt(fastargs[2]); + if (day == -1 && PyErr_Occurred()) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (fastargs[3]) { + hour = PyLong_AsInt(fastargs[3]); + if (hour == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[4]) { + minute = PyLong_AsInt(fastargs[4]); + if (minute == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[5]) { + second = PyLong_AsInt(fastargs[5]); + if (second == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[6]) { + microsecond = PyLong_AsInt(fastargs[6]); + if (microsecond == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[7]) { + tzinfo = fastargs[7]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + fold = PyLong_AsInt(fastargs[8]); + if (fold == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_kwonly: + return_value = datetime_datetime_impl(type, year, month, day, hour, minute, second, microsecond, tzinfo, fold); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_datetime_now__doc__, +"now($type, /, tz=None)\n" +"--\n" +"\n" +"Returns new datetime object representing current time local to tz.\n" +"\n" +" tz\n" +" Timezone object.\n" +"\n" +"If no tz is specified, uses local timezone."); + +#define DATETIME_DATETIME_NOW_METHODDEF \ + {"now", _PyCFunction_CAST(datetime_datetime_now), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, datetime_datetime_now__doc__}, + +static PyObject * +datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz); + +static PyObject * +datetime_datetime_now(PyObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(tz), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"tz", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "now", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *tz = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + tz = args[0]; +skip_optional_pos: + return_value = datetime_datetime_now_impl((PyTypeObject *)type, tz); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_datetime_utcnow__doc__, +"utcnow($type, /)\n" +"--\n" +"\n" +"Return a new datetime representing UTC day and time."); + +#define DATETIME_DATETIME_UTCNOW_METHODDEF \ + {"utcnow", (PyCFunction)datetime_datetime_utcnow, METH_NOARGS|METH_CLASS, datetime_datetime_utcnow__doc__}, + +static PyObject * +datetime_datetime_utcnow_impl(PyTypeObject *type); + +static PyObject * +datetime_datetime_utcnow(PyObject *type, PyObject *Py_UNUSED(ignored)) +{ + return datetime_datetime_utcnow_impl((PyTypeObject *)type); +} + +PyDoc_STRVAR(datetime_datetime_fromtimestamp__doc__, +"fromtimestamp($type, /, timestamp, tz=None)\n" +"--\n" +"\n" +"Create a datetime from a POSIX timestamp.\n" +"\n" +"The timestamp is a number, e.g. created via time.time(), that is\n" +"interpreted as local time."); + +#define DATETIME_DATETIME_FROMTIMESTAMP_METHODDEF \ + {"fromtimestamp", _PyCFunction_CAST(datetime_datetime_fromtimestamp), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, datetime_datetime_fromtimestamp__doc__}, + +static PyObject * +datetime_datetime_fromtimestamp_impl(PyTypeObject *type, PyObject *timestamp, + PyObject *tzinfo); + +static PyObject * +datetime_datetime_fromtimestamp(PyObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(timestamp), &_Py_ID(tz), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"timestamp", "tz", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "fromtimestamp", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *timestamp; + PyObject *tzinfo = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + timestamp = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + tzinfo = args[1]; +skip_optional_pos: + return_value = datetime_datetime_fromtimestamp_impl((PyTypeObject *)type, timestamp, tzinfo); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_datetime_utcfromtimestamp__doc__, +"utcfromtimestamp($type, timestamp, /)\n" +"--\n" +"\n" +"Create a naive UTC datetime from a POSIX timestamp."); + +#define DATETIME_DATETIME_UTCFROMTIMESTAMP_METHODDEF \ + {"utcfromtimestamp", (PyCFunction)datetime_datetime_utcfromtimestamp, METH_O|METH_CLASS, datetime_datetime_utcfromtimestamp__doc__}, + +static PyObject * +datetime_datetime_utcfromtimestamp_impl(PyTypeObject *type, + PyObject *timestamp); + +static PyObject * +datetime_datetime_utcfromtimestamp(PyObject *type, PyObject *timestamp) +{ + PyObject *return_value = NULL; + + return_value = datetime_datetime_utcfromtimestamp_impl((PyTypeObject *)type, timestamp); + + return return_value; +} + +PyDoc_STRVAR(datetime_datetime_strptime__doc__, +"strptime($type, string, format, /)\n" +"--\n" +"\n" +"Parse string according to the given date and time format (like time.strptime()).\n" +"\n" +"For a list of supported format codes, see the documentation:\n" +" https://docs.python.org/3/library/datetime.html#format-codes"); + +#define DATETIME_DATETIME_STRPTIME_METHODDEF \ + {"strptime", _PyCFunction_CAST(datetime_datetime_strptime), METH_FASTCALL|METH_CLASS, datetime_datetime_strptime__doc__}, + +static PyObject * +datetime_datetime_strptime_impl(PyTypeObject *type, PyObject *string, + PyObject *format); + +static PyObject * +datetime_datetime_strptime(PyObject *type, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *string; + PyObject *format; + + if (!_PyArg_CheckPositional("strptime", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("strptime", "argument 1", "str", args[0]); + goto exit; + } + string = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("strptime", "argument 2", "str", args[1]); + goto exit; + } + format = args[1]; + return_value = datetime_datetime_strptime_impl((PyTypeObject *)type, string, format); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_datetime_combine__doc__, +"combine($type, /, date, time, tzinfo=<unrepresentable>)\n" +"--\n" +"\n" +"Construct a datetime from a given date and a given time."); -#define DATETIME_DATE_REPLACE_METHODDEF \ - {"replace", _PyCFunction_CAST(datetime_date_replace), METH_FASTCALL|METH_KEYWORDS, datetime_date_replace__doc__}, +#define DATETIME_DATETIME_COMBINE_METHODDEF \ + {"combine", _PyCFunction_CAST(datetime_datetime_combine), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, datetime_datetime_combine__doc__}, static PyObject * -datetime_date_replace_impl(PyDateTime_Date *self, int year, int month, - int day); +datetime_datetime_combine_impl(PyTypeObject *type, PyObject *date, + PyObject *time, PyObject *tzinfo); static PyObject * -datetime_date_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +datetime_datetime_combine(PyObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -126,7 +1648,7 @@ datetime_date_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, P } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) .ob_hash = -1, - .ob_item = { &_Py_ID(year), &_Py_ID(month), &_Py_ID(day), }, + .ob_item = { &_Py_ID(date), &_Py_ID(time), &_Py_ID(tzinfo), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -135,200 +1657,107 @@ datetime_date_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, P # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"year", "month", "day", NULL}; + static const char * const _keywords[] = {"date", "time", "tzinfo", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, - .fname = "replace", + .fname = "combine", .kwtuple = KWTUPLE, }; #undef KWTUPLE PyObject *argsbuf[3]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - int year = GET_YEAR(self); - int month = GET_MONTH(self); - int day = GET_DAY(self); + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *date; + PyObject *time; + PyObject *tzinfo = NULL; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, - /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - year = PyLong_AsInt(args[0]); - if (year == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[1]) { - month = PyLong_AsInt(args[1]); - if (month == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } + if (!PyObject_TypeCheck(args[0], DATE_TYPE(NO_STATE))) { + _PyArg_BadArgument("combine", "argument 'date'", (DATE_TYPE(NO_STATE))->tp_name, args[0]); + goto exit; } - day = PyLong_AsInt(args[2]); - if (day == -1 && PyErr_Occurred()) { + date = args[0]; + if (!PyObject_TypeCheck(args[1], TIME_TYPE(NO_STATE))) { + _PyArg_BadArgument("combine", "argument 'time'", (TIME_TYPE(NO_STATE))->tp_name, args[1]); goto exit; } + time = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + tzinfo = args[2]; skip_optional_pos: - return_value = datetime_date_replace_impl((PyDateTime_Date *)self, year, month, day); + return_value = datetime_datetime_combine_impl((PyTypeObject *)type, date, time, tzinfo); exit: return return_value; } -PyDoc_STRVAR(datetime_time_replace__doc__, -"replace($self, /, hour=unchanged, minute=unchanged, second=unchanged,\n" -" microsecond=unchanged, tzinfo=unchanged, *, fold=unchanged)\n" +PyDoc_STRVAR(datetime_datetime_fromisoformat__doc__, +"fromisoformat($type, string, /)\n" "--\n" "\n" -"Return time with new specified fields."); +"Construct a date from a string in ISO 8601 format."); -#define DATETIME_TIME_REPLACE_METHODDEF \ - {"replace", _PyCFunction_CAST(datetime_time_replace), METH_FASTCALL|METH_KEYWORDS, datetime_time_replace__doc__}, +#define DATETIME_DATETIME_FROMISOFORMAT_METHODDEF \ + {"fromisoformat", (PyCFunction)datetime_datetime_fromisoformat, METH_O|METH_CLASS, datetime_datetime_fromisoformat__doc__}, static PyObject * -datetime_time_replace_impl(PyDateTime_Time *self, int hour, int minute, - int second, int microsecond, PyObject *tzinfo, - int fold); +datetime_datetime_fromisoformat_impl(PyTypeObject *type, PyObject *string); static PyObject * -datetime_time_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +datetime_datetime_fromisoformat(PyObject *type, PyObject *arg) { PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 6 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - Py_hash_t ob_hash; - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_hash = -1, - .ob_item = { &_Py_ID(hour), &_Py_ID(minute), &_Py_ID(second), &_Py_ID(microsecond), &_Py_ID(tzinfo), &_Py_ID(fold), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"hour", "minute", "second", "microsecond", "tzinfo", "fold", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "replace", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[6]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - int hour = TIME_GET_HOUR(self); - int minute = TIME_GET_MINUTE(self); - int second = TIME_GET_SECOND(self); - int microsecond = TIME_GET_MICROSECOND(self); - PyObject *tzinfo = HASTZINFO(self) ? ((PyDateTime_Time *)self)->tzinfo : Py_None; - int fold = TIME_GET_FOLD(self); + PyObject *string; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, - /*minpos*/ 0, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - hour = PyLong_AsInt(args[0]); - if (hour == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[1]) { - minute = PyLong_AsInt(args[1]); - if (minute == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[2]) { - second = PyLong_AsInt(args[2]); - if (second == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[3]) { - microsecond = PyLong_AsInt(args[3]); - if (microsecond == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[4]) { - tzinfo = args[4]; - if (!--noptargs) { - goto skip_optional_pos; - } - } -skip_optional_pos: - if (!noptargs) { - goto skip_optional_kwonly; - } - fold = PyLong_AsInt(args[5]); - if (fold == -1 && PyErr_Occurred()) { + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("fromisoformat", "argument", "str", arg); goto exit; } -skip_optional_kwonly: - return_value = datetime_time_replace_impl((PyDateTime_Time *)self, hour, minute, second, microsecond, tzinfo, fold); + string = arg; + return_value = datetime_datetime_fromisoformat_impl((PyTypeObject *)type, string); exit: return return_value; } -PyDoc_STRVAR(datetime_datetime_now__doc__, -"now($type, /, tz=None)\n" +PyDoc_STRVAR(datetime_datetime_isoformat__doc__, +"isoformat($self, /, sep=\'T\', timespec=\'auto\')\n" "--\n" "\n" -"Returns new datetime object representing current time local to tz.\n" +"Return the time formatted according to ISO.\n" "\n" -" tz\n" -" Timezone object.\n" +"The full format looks like \'YYYY-MM-DD HH:MM:SS.mmmmmm\'.\n" +"By default, the fractional part is omitted if self.microsecond == 0.\n" "\n" -"If no tz is specified, uses local timezone."); +"If self.tzinfo is not None, the UTC offset is also attached, giving\n" +"a full format of \'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM\'.\n" +"\n" +"Optional argument sep specifies the separator between date and\n" +"time, default \'T\'.\n" +"\n" +"The optional argument timespec specifies the number of additional\n" +"terms of the time to include. Valid options are \'auto\', \'hours\',\n" +"\'minutes\', \'seconds\', \'milliseconds\' and \'microseconds\'."); -#define DATETIME_DATETIME_NOW_METHODDEF \ - {"now", _PyCFunction_CAST(datetime_datetime_now), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, datetime_datetime_now__doc__}, +#define DATETIME_DATETIME_ISOFORMAT_METHODDEF \ + {"isoformat", _PyCFunction_CAST(datetime_datetime_isoformat), METH_FASTCALL|METH_KEYWORDS, datetime_datetime_isoformat__doc__}, static PyObject * -datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz); +datetime_datetime_isoformat_impl(PyDateTime_DateTime *self, int sep, + const char *timespec); static PyObject * -datetime_datetime_now(PyObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +datetime_datetime_isoformat(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 1 + #define NUM_KEYWORDS 2 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD @@ -337,7 +1766,7 @@ datetime_datetime_now(PyObject *type, PyObject *const *args, Py_ssize_t nargs, P } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) .ob_hash = -1, - .ob_item = { &_Py_ID(tz), }, + .ob_item = { &_Py_ID(sep), &_Py_ID(timespec), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -346,28 +1775,58 @@ datetime_datetime_now(PyObject *type, PyObject *const *args, Py_ssize_t nargs, P # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"tz", NULL}; + static const char * const _keywords[] = {"sep", "timespec", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, - .fname = "now", + .fname = "isoformat", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[1]; + PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *tz = Py_None; + int sep = 'T'; + const char *timespec = NULL; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, - /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!args) { goto exit; } if (!noptargs) { goto skip_optional_pos; } - tz = args[0]; + if (args[0]) { + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("isoformat", "argument 'sep'", "a unicode character", args[0]); + goto exit; + } + if (PyUnicode_GET_LENGTH(args[0]) != 1) { + PyErr_Format(PyExc_TypeError, + "isoformat(): argument 'sep' must be a unicode character, " + "not a string of length %zd", + PyUnicode_GET_LENGTH(args[0])); + goto exit; + } + sep = PyUnicode_READ_CHAR(args[0], 0); + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("isoformat", "argument 'timespec'", "str", args[1]); + goto exit; + } + Py_ssize_t timespec_length; + timespec = PyUnicode_AsUTF8AndSize(args[1], &timespec_length); + if (timespec == NULL) { + goto exit; + } + if (strlen(timespec) != (size_t)timespec_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } skip_optional_pos: - return_value = datetime_datetime_now_impl((PyTypeObject *)type, tz); + return_value = datetime_datetime_isoformat_impl((PyDateTime_DateTime *)self, sep, timespec); exit: return return_value; @@ -524,4 +1983,112 @@ datetime_datetime_replace(PyObject *self, PyObject *const *args, Py_ssize_t narg exit: return return_value; } -/*[clinic end generated code: output=809640e747529c72 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(datetime_datetime_astimezone__doc__, +"astimezone($self, /, tz=None)\n" +"--\n" +"\n" +"Convert to local time in new timezone tz."); + +#define DATETIME_DATETIME_ASTIMEZONE_METHODDEF \ + {"astimezone", _PyCFunction_CAST(datetime_datetime_astimezone), METH_FASTCALL|METH_KEYWORDS, datetime_datetime_astimezone__doc__}, + +static PyObject * +datetime_datetime_astimezone_impl(PyDateTime_DateTime *self, + PyObject *tzinfo); + +static PyObject * +datetime_datetime_astimezone(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(tz), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"tz", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "astimezone", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *tzinfo = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + tzinfo = args[0]; +skip_optional_pos: + return_value = datetime_datetime_astimezone_impl((PyDateTime_DateTime *)self, tzinfo); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_datetime___reduce_ex____doc__, +"__reduce_ex__($self, proto, /)\n" +"--\n" +"\n"); + +#define DATETIME_DATETIME___REDUCE_EX___METHODDEF \ + {"__reduce_ex__", (PyCFunction)datetime_datetime___reduce_ex__, METH_O, datetime_datetime___reduce_ex____doc__}, + +static PyObject * +datetime_datetime___reduce_ex___impl(PyDateTime_DateTime *self, int proto); + +static PyObject * +datetime_datetime___reduce_ex__(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + int proto; + + proto = PyLong_AsInt(arg); + if (proto == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = datetime_datetime___reduce_ex___impl((PyDateTime_DateTime *)self, proto); + +exit: + return return_value; +} + +PyDoc_STRVAR(datetime_datetime___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n"); + +#define DATETIME_DATETIME___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)datetime_datetime___reduce__, METH_NOARGS, datetime_datetime___reduce____doc__}, + +static PyObject * +datetime_datetime___reduce___impl(PyDateTime_DateTime *self); + +static PyObject * +datetime_datetime___reduce__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return datetime_datetime___reduce___impl((PyDateTime_DateTime *)self); +} +/*[clinic end generated code: output=8f63509398651723 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_dbmmodule.c.h b/Modules/clinic/_dbmmodule.c.h index 091ce9edc43d4b..6c979a4b0081df 100644 --- a/Modules/clinic/_dbmmodule.c.h +++ b/Modules/clinic/_dbmmodule.c.h @@ -113,7 +113,8 @@ PyDoc_STRVAR(_dbm_dbm_setdefault__doc__, "\n" "Return the value for key if present, otherwise default.\n" "\n" -"If key is not in the database, it is inserted with default as the value."); +"If key is not in the database, it is inserted with default as the\n" +"value."); #define _DBM_DBM_SETDEFAULT_METHODDEF \ {"setdefault", _PyCFunction_CAST(_dbm_dbm_setdefault), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _dbm_dbm_setdefault__doc__}, @@ -246,4 +247,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=279511ea7cda38dd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=677deecf525167a5 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_functoolsmodule.c.h b/Modules/clinic/_functoolsmodule.c.h index 23f66631085031..5f350c864f057b 100644 --- a/Modules/clinic/_functoolsmodule.c.h +++ b/Modules/clinic/_functoolsmodule.c.h @@ -71,14 +71,15 @@ _functools_cmp_to_key(PyObject *module, PyObject *const *args, Py_ssize_t nargs, } PyDoc_STRVAR(_functools_reduce__doc__, -"reduce($module, function, iterable, /, initial=<unrepresentable>)\n" +"reduce($module, function, iterable, /,\n" +" initial=functools._initial_missing)\n" "--\n" "\n" "Apply a function of two arguments cumulatively to the items of an iterable, from left to right.\n" "\n" -"This effectively reduces the iterable to a single value. If initial is present,\n" -"it is placed before the items of the iterable in the calculation, and serves as\n" -"a default when the iterable is empty.\n" +"This effectively reduces the iterable to a single value. If initial is\n" +"present, it is placed before the items of the iterable in the\n" +"calculation, and serves as a default when the iterable is empty.\n" "\n" "For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])\n" "calculates ((((1 + 2) + 3) + 4) + 5)."); @@ -192,4 +193,4 @@ _functools__lru_cache_wrapper_cache_clear(PyObject *self, PyObject *Py_UNUSED(ig return return_value; } -/*[clinic end generated code: output=7f2abc718fcc35d5 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6d8fdaeba4b520fa input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_gdbmmodule.c.h b/Modules/clinic/_gdbmmodule.c.h index 6fd6aa3da50335..fe993cc328fbd2 100644 --- a/Modules/clinic/_gdbmmodule.c.h +++ b/Modules/clinic/_gdbmmodule.c.h @@ -138,9 +138,9 @@ PyDoc_STRVAR(_gdbm_gdbm_firstkey__doc__, "\n" "Return the starting key for the traversal.\n" "\n" -"It\'s possible to loop over every key in the database using this method\n" -"and the nextkey() method. The traversal is ordered by GDBM\'s internal\n" -"hash values, and won\'t be sorted by the key values."); +"It\'s possible to loop over every key in the database using this\n" +"method and the nextkey() method. The traversal is ordered by GDBM\'s\n" +"internal hash values, and won\'t be sorted by the key values."); #define _GDBM_GDBM_FIRSTKEY_METHODDEF \ {"firstkey", _PyCFunction_CAST(_gdbm_gdbm_firstkey), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _gdbm_gdbm_firstkey__doc__}, @@ -171,8 +171,8 @@ PyDoc_STRVAR(_gdbm_gdbm_nextkey__doc__, "\n" "Returns the key that follows key in the traversal.\n" "\n" -"The following code prints every key in the database db, without having\n" -"to create a list in memory that contains them all:\n" +"The following code prints every key in the database db, without\n" +"having to create a list in memory that contains them all:\n" "\n" " k = db.firstkey()\n" " while k is not None:\n" @@ -226,9 +226,9 @@ PyDoc_STRVAR(_gdbm_gdbm_reorganize__doc__, "\n" "If you have carried out a lot of deletions and would like to shrink\n" "the space used by the GDBM file, this routine will reorganize the\n" -"database. GDBM will not shorten the length of a database file except\n" -"by using this reorganization; otherwise, deleted file space will be\n" -"kept and reused as new (key,value) pairs are added."); +"database. GDBM will not shorten the length of a database file\n" +"except by using this reorganization; otherwise, deleted file space\n" +"will be kept and reused as new (key,value) pairs are added."); #define _GDBM_GDBM_REORGANIZE_METHODDEF \ {"reorganize", _PyCFunction_CAST(_gdbm_gdbm_reorganize), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _gdbm_gdbm_reorganize__doc__}, @@ -389,4 +389,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=8bca34ce9d4493dd input=a9049054013a1b77]*/ +/*[clinic end generated code: output=429b5db24568292e input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h index 3d81a6dcce1440..17ed37bbd3b619 100644 --- a/Modules/clinic/_hashopenssl.c.h +++ b/Modules/clinic/_hashopenssl.c.h @@ -150,6 +150,11 @@ _hashlib_HASHXOF_digest(PyObject *self, PyObject *const *args, Py_ssize_t nargs, goto exit; } length = ival; + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length cannot be negative"); + goto exit; + } } return_value = _hashlib_HASHXOF_digest_impl((HASHobject *)self, length); @@ -223,6 +228,11 @@ _hashlib_HASHXOF_hexdigest(PyObject *self, PyObject *const *args, Py_ssize_t nar goto exit; } length = ival; + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length cannot be negative"); + goto exit; + } } return_value = _hashlib_HASHXOF_hexdigest_impl((HASHobject *)self, length); @@ -1855,8 +1865,8 @@ PyDoc_STRVAR(_hashlib_HMAC_hexdigest__doc__, "\n" "Return hexadecimal digest of the bytes passed to the update() method so far.\n" "\n" -"This may be used to exchange the value safely in email or other non-binary\n" -"environments."); +"This may be used to exchange the value safely in email or other\n" +"non-binary environments."); #define _HASHLIB_HMAC_HEXDIGEST_METHODDEF \ {"hexdigest", (PyCFunction)_hashlib_HMAC_hexdigest, METH_NOARGS, _hashlib_HMAC_hexdigest__doc__}, @@ -1877,8 +1887,8 @@ PyDoc_STRVAR(_hashlib_get_fips_mode__doc__, "Determine the OpenSSL FIPS mode of operation.\n" "\n" "For OpenSSL 3.0.0 and newer it returns the state of the default provider\n" -"in the default OSSL context. It\'s not quite the same as FIPS_mode() but good\n" -"enough for unittests.\n" +"in the default OSSL context. It\'s not quite the same as FIPS_mode() but\n" +"good enough for unittests.\n" "\n" "Effectively any non-zero return value indicates FIPS mode;\n" "values other than 1 may have additional significance."); @@ -1976,4 +1986,4 @@ _hashlib_compare_digest(PyObject *module, PyObject *const *args, Py_ssize_t narg #ifndef _HASHLIB_OPENSSL_SHAKE_256_METHODDEF #define _HASHLIB_OPENSSL_SHAKE_256_METHODDEF #endif /* !defined(_HASHLIB_OPENSSL_SHAKE_256_METHODDEF) */ -/*[clinic end generated code: output=cd5ff436f6dc2938 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=cf405e652a340bb2 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_heapqmodule.c.h b/Modules/clinic/_heapqmodule.c.h index b43155b6c24e3c..f3b8256efc0221 100644 --- a/Modules/clinic/_heapqmodule.c.h +++ b/Modules/clinic/_heapqmodule.c.h @@ -326,8 +326,8 @@ PyDoc_STRVAR(_heapq_heappushpop_max__doc__, "\n" "Maxheap variant of heappushpop.\n" "\n" -"The combined action runs more efficiently than heappush_max() followed by\n" -"a separate call to heappop_max()."); +"The combined action runs more efficiently than heappush_max()\n" +"followed by a separate call to heappop_max()."); #define _HEAPQ_HEAPPUSHPOP_MAX_METHODDEF \ {"heappushpop_max", _PyCFunction_CAST(_heapq_heappushpop_max), METH_FASTCALL, _heapq_heappushpop_max__doc__}, @@ -358,4 +358,4 @@ _heapq_heappushpop_max(PyObject *module, PyObject *const *args, Py_ssize_t nargs exit: return return_value; } -/*[clinic end generated code: output=e83d50002c29a96d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=21e4f248ef6e83d6 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_interpqueuesmodule.c.h b/Modules/clinic/_interpqueuesmodule.c.h new file mode 100644 index 00000000000000..6f27ea73b8866f --- /dev/null +++ b/Modules/clinic/_interpqueuesmodule.c.h @@ -0,0 +1,766 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +PyDoc_STRVAR(_interpqueues_create__doc__, +"create($module, /, maxsize, unboundop=-1, fallback=-1)\n" +"--\n" +"\n" +"Create a new cross-interpreter queue and return its unique generated ID.\n" +"\n" +"It is a new reference as though bind() had been called on the queue.\n" +"The caller is responsible for calling destroy() for the new queue\n" +"before the runtime is finalized."); + +#define _INTERPQUEUES_CREATE_METHODDEF \ + {"create", _PyCFunction_CAST(_interpqueues_create), METH_FASTCALL|METH_KEYWORDS, _interpqueues_create__doc__}, + +static PyObject * +_interpqueues_create_impl(PyObject *module, Py_ssize_t maxsize, + int unboundarg, int fallbackarg); + +static PyObject * +_interpqueues_create(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(maxsize), &_Py_ID(unboundop), &_Py_ID(fallback), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"maxsize", "unboundop", "fallback", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "create", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t maxsize; + int unboundarg = -1; + int fallbackarg = -1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + maxsize = ival; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1]) { + unboundarg = PyLong_AsInt(args[1]); + if (unboundarg == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + fallbackarg = PyLong_AsInt(args[2]); + if (fallbackarg == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_pos: + return_value = _interpqueues_create_impl(module, maxsize, unboundarg, fallbackarg); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpqueues_destroy__doc__, +"destroy($module, /, qid)\n" +"--\n" +"\n" +"Clear and destroy the queue.\n" +"\n" +"Afterward attempts to use the queue will behave as though it never\n" +"existed."); + +#define _INTERPQUEUES_DESTROY_METHODDEF \ + {"destroy", _PyCFunction_CAST(_interpqueues_destroy), METH_FASTCALL|METH_KEYWORDS, _interpqueues_destroy__doc__}, + +static PyObject * +_interpqueues_destroy_impl(PyObject *module, int64_t qid); + +static PyObject * +_interpqueues_destroy(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(qid), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"qid", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "destroy", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int64_t qid; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!qidarg_converter(args[0], &qid)) { + goto exit; + } + return_value = _interpqueues_destroy_impl(module, qid); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpqueues_list_all__doc__, +"list_all($module, /)\n" +"--\n" +"\n" +"Return the list of ID triples for all queues.\n" +"\n" +"Each ID triple consists of (ID, default unbound op, default fallback)."); + +#define _INTERPQUEUES_LIST_ALL_METHODDEF \ + {"list_all", (PyCFunction)_interpqueues_list_all, METH_NOARGS, _interpqueues_list_all__doc__}, + +static PyObject * +_interpqueues_list_all_impl(PyObject *module); + +static PyObject * +_interpqueues_list_all(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _interpqueues_list_all_impl(module); +} + +PyDoc_STRVAR(_interpqueues_put__doc__, +"put($module, /, qid, obj, unboundop=-1, fallback=-1)\n" +"--\n" +"\n" +"Add the object\'s data to the queue."); + +#define _INTERPQUEUES_PUT_METHODDEF \ + {"put", _PyCFunction_CAST(_interpqueues_put), METH_FASTCALL|METH_KEYWORDS, _interpqueues_put__doc__}, + +static PyObject * +_interpqueues_put_impl(PyObject *module, int64_t qid, PyObject *obj, + int unboundarg, int fallbackarg); + +static PyObject * +_interpqueues_put(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(qid), &_Py_ID(obj), &_Py_ID(unboundop), &_Py_ID(fallback), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"qid", "obj", "unboundop", "fallback", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "put", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + int64_t qid; + PyObject *obj; + int unboundarg = -1; + int fallbackarg = -1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!qidarg_converter(args[0], &qid)) { + goto exit; + } + obj = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + unboundarg = PyLong_AsInt(args[2]); + if (unboundarg == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + fallbackarg = PyLong_AsInt(args[3]); + if (fallbackarg == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_pos: + return_value = _interpqueues_put_impl(module, qid, obj, unboundarg, fallbackarg); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpqueues_get__doc__, +"get($module, /, qid)\n" +"--\n" +"\n" +"Return the (object, unbound op) from the front of the queue.\n" +"\n" +"If there is nothing to receive then raise QueueEmpty."); + +#define _INTERPQUEUES_GET_METHODDEF \ + {"get", _PyCFunction_CAST(_interpqueues_get), METH_FASTCALL|METH_KEYWORDS, _interpqueues_get__doc__}, + +static PyObject * +_interpqueues_get_impl(PyObject *module, int64_t qid); + +static PyObject * +_interpqueues_get(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(qid), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"qid", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "get", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int64_t qid; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!qidarg_converter(args[0], &qid)) { + goto exit; + } + return_value = _interpqueues_get_impl(module, qid); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpqueues_bind__doc__, +"bind($module, /, qid)\n" +"--\n" +"\n" +"Take a reference to the identified queue.\n" +"\n" +"The queue is not destroyed until there are no references left."); + +#define _INTERPQUEUES_BIND_METHODDEF \ + {"bind", _PyCFunction_CAST(_interpqueues_bind), METH_FASTCALL|METH_KEYWORDS, _interpqueues_bind__doc__}, + +static PyObject * +_interpqueues_bind_impl(PyObject *module, int64_t qid); + +static PyObject * +_interpqueues_bind(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(qid), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"qid", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "bind", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int64_t qid; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!qidarg_converter(args[0], &qid)) { + goto exit; + } + return_value = _interpqueues_bind_impl(module, qid); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpqueues_release__doc__, +"release($module, /, qid)\n" +"--\n" +"\n" +"Release a reference to the queue.\n" +"\n" +"The queue is destroyed once there are no references left."); + +#define _INTERPQUEUES_RELEASE_METHODDEF \ + {"release", _PyCFunction_CAST(_interpqueues_release), METH_FASTCALL|METH_KEYWORDS, _interpqueues_release__doc__}, + +static PyObject * +_interpqueues_release_impl(PyObject *module, int64_t qid); + +static PyObject * +_interpqueues_release(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(qid), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"qid", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "release", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int64_t qid; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!qidarg_converter(args[0], &qid)) { + goto exit; + } + return_value = _interpqueues_release_impl(module, qid); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpqueues_get_maxsize__doc__, +"get_maxsize($module, /, qid)\n" +"--\n" +"\n" +"Return the maximum number of items in the queue."); + +#define _INTERPQUEUES_GET_MAXSIZE_METHODDEF \ + {"get_maxsize", _PyCFunction_CAST(_interpqueues_get_maxsize), METH_FASTCALL|METH_KEYWORDS, _interpqueues_get_maxsize__doc__}, + +static PyObject * +_interpqueues_get_maxsize_impl(PyObject *module, int64_t qid); + +static PyObject * +_interpqueues_get_maxsize(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(qid), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"qid", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "get_maxsize", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int64_t qid; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!qidarg_converter(args[0], &qid)) { + goto exit; + } + return_value = _interpqueues_get_maxsize_impl(module, qid); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpqueues_get_queue_defaults__doc__, +"get_queue_defaults($module, /, qid)\n" +"--\n" +"\n" +"Return the queue\'s default values, set when it was created."); + +#define _INTERPQUEUES_GET_QUEUE_DEFAULTS_METHODDEF \ + {"get_queue_defaults", _PyCFunction_CAST(_interpqueues_get_queue_defaults), METH_FASTCALL|METH_KEYWORDS, _interpqueues_get_queue_defaults__doc__}, + +static PyObject * +_interpqueues_get_queue_defaults_impl(PyObject *module, int64_t qid); + +static PyObject * +_interpqueues_get_queue_defaults(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(qid), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"qid", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "get_queue_defaults", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int64_t qid; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!qidarg_converter(args[0], &qid)) { + goto exit; + } + return_value = _interpqueues_get_queue_defaults_impl(module, qid); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpqueues_is_full__doc__, +"is_full($module, /, qid)\n" +"--\n" +"\n" +"Return true if the queue has a maxsize and has reached it."); + +#define _INTERPQUEUES_IS_FULL_METHODDEF \ + {"is_full", _PyCFunction_CAST(_interpqueues_is_full), METH_FASTCALL|METH_KEYWORDS, _interpqueues_is_full__doc__}, + +static PyObject * +_interpqueues_is_full_impl(PyObject *module, int64_t qid); + +static PyObject * +_interpqueues_is_full(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(qid), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"qid", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_full", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int64_t qid; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!qidarg_converter(args[0], &qid)) { + goto exit; + } + return_value = _interpqueues_is_full_impl(module, qid); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpqueues_get_count__doc__, +"get_count($module, /, qid)\n" +"--\n" +"\n" +"Return the number of items in the queue."); + +#define _INTERPQUEUES_GET_COUNT_METHODDEF \ + {"get_count", _PyCFunction_CAST(_interpqueues_get_count), METH_FASTCALL|METH_KEYWORDS, _interpqueues_get_count__doc__}, + +static PyObject * +_interpqueues_get_count_impl(PyObject *module, int64_t qid); + +static PyObject * +_interpqueues_get_count(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(qid), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"qid", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "get_count", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + int64_t qid; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!qidarg_converter(args[0], &qid)) { + goto exit; + } + return_value = _interpqueues_get_count_impl(module, qid); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpqueues__register_heap_types__doc__, +"_register_heap_types($module, /, queuetype, emptyerror, fullerror)\n" +"--\n" +"\n" +"Return the number of items in the queue."); + +#define _INTERPQUEUES__REGISTER_HEAP_TYPES_METHODDEF \ + {"_register_heap_types", _PyCFunction_CAST(_interpqueues__register_heap_types), METH_FASTCALL|METH_KEYWORDS, _interpqueues__register_heap_types__doc__}, + +static PyObject * +_interpqueues__register_heap_types_impl(PyObject *module, + PyTypeObject *queuetype, + PyObject *emptyerror, + PyObject *fullerror); + +static PyObject * +_interpqueues__register_heap_types(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(queuetype), &_Py_ID(emptyerror), &_Py_ID(fullerror), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"queuetype", "emptyerror", "fullerror", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_register_heap_types", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + PyTypeObject *queuetype; + PyObject *emptyerror; + PyObject *fullerror; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!PyObject_TypeCheck(args[0], &PyType_Type)) { + _PyArg_BadArgument("_register_heap_types", "argument 'queuetype'", (&PyType_Type)->tp_name, args[0]); + goto exit; + } + queuetype = (PyTypeObject *)args[0]; + emptyerror = args[1]; + fullerror = args[2]; + return_value = _interpqueues__register_heap_types_impl(module, queuetype, emptyerror, fullerror); + +exit: + return return_value; +} +/*[clinic end generated code: output=7e56e5b0c684d294 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_interpretersmodule.c.h b/Modules/clinic/_interpretersmodule.c.h new file mode 100644 index 00000000000000..72792f9b583a66 --- /dev/null +++ b/Modules/clinic/_interpretersmodule.c.h @@ -0,0 +1,1203 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +PyDoc_STRVAR(_interpreters_create__doc__, +"create($module, /, config=\'isolated\', *, reqrefs=False)\n" +"--\n" +"\n" +"Create a new interpreter and return a unique generated ID.\n" +"\n" +"The caller is responsible for destroying the interpreter before exiting,\n" +"typically by using _interpreters.destroy(). This can be managed\n" +"automatically by passing \"reqrefs=True\" and then using _incref() and\n" +"_decref() appropriately.\n" +"\n" +"\"config\" must be a valid interpreter config or the name of a\n" +"predefined config (\'isolated\' or \'legacy\'). The default\n" +"is \'isolated\'."); + +#define _INTERPRETERS_CREATE_METHODDEF \ + {"create", _PyCFunction_CAST(_interpreters_create), METH_FASTCALL|METH_KEYWORDS, _interpreters_create__doc__}, + +static PyObject * +_interpreters_create_impl(PyObject *module, PyObject *configobj, int reqrefs); + +static PyObject * +_interpreters_create(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(config), &_Py_ID(reqrefs), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"config", "reqrefs", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "create", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *configobj = NULL; + int reqrefs = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + configobj = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + reqrefs = PyObject_IsTrue(args[1]); + if (reqrefs < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_create_impl(module, configobj, reqrefs); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_destroy__doc__, +"destroy($module, /, id, *, restrict=False)\n" +"--\n" +"\n" +"Destroy the identified interpreter.\n" +"\n" +"Attempting to destroy the current interpreter raises InterpreterError.\n" +"So does an unrecognized ID."); + +#define _INTERPRETERS_DESTROY_METHODDEF \ + {"destroy", _PyCFunction_CAST(_interpreters_destroy), METH_FASTCALL|METH_KEYWORDS, _interpreters_destroy__doc__}, + +static PyObject * +_interpreters_destroy_impl(PyObject *module, PyObject *id, int restricted); + +static PyObject * +_interpreters_destroy(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(restrict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "destroy", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *id; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[1]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_destroy_impl(module, id, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_list_all__doc__, +"list_all($module, /, *, require_ready=False)\n" +"--\n" +"\n" +"Return a list containing the ID of every existing interpreter."); + +#define _INTERPRETERS_LIST_ALL_METHODDEF \ + {"list_all", _PyCFunction_CAST(_interpreters_list_all), METH_FASTCALL|METH_KEYWORDS, _interpreters_list_all__doc__}, + +static PyObject * +_interpreters_list_all_impl(PyObject *module, int reqready); + +static PyObject * +_interpreters_list_all(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(require_ready), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"require_ready", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "list_all", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int reqready = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + reqready = PyObject_IsTrue(args[0]); + if (reqready < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_list_all_impl(module, reqready); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_get_current__doc__, +"get_current($module, /)\n" +"--\n" +"\n" +"Return (ID, whence) of the current interpreter."); + +#define _INTERPRETERS_GET_CURRENT_METHODDEF \ + {"get_current", (PyCFunction)_interpreters_get_current, METH_NOARGS, _interpreters_get_current__doc__}, + +static PyObject * +_interpreters_get_current_impl(PyObject *module); + +static PyObject * +_interpreters_get_current(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _interpreters_get_current_impl(module); +} + +PyDoc_STRVAR(_interpreters_get_main__doc__, +"get_main($module, /)\n" +"--\n" +"\n" +"Return (ID, whence) of the main interpreter."); + +#define _INTERPRETERS_GET_MAIN_METHODDEF \ + {"get_main", (PyCFunction)_interpreters_get_main, METH_NOARGS, _interpreters_get_main__doc__}, + +static PyObject * +_interpreters_get_main_impl(PyObject *module); + +static PyObject * +_interpreters_get_main(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _interpreters_get_main_impl(module); +} + +PyDoc_STRVAR(_interpreters_set___main___attrs__doc__, +"set___main___attrs($module, /, id, updates, *, restrict=False)\n" +"--\n" +"\n" +"Bind the given attributes in the interpreter\'s __main__ module."); + +#define _INTERPRETERS_SET___MAIN___ATTRS_METHODDEF \ + {"set___main___attrs", _PyCFunction_CAST(_interpreters_set___main___attrs), METH_FASTCALL|METH_KEYWORDS, _interpreters_set___main___attrs__doc__}, + +static PyObject * +_interpreters_set___main___attrs_impl(PyObject *module, PyObject *id, + PyObject *updates, int restricted); + +static PyObject * +_interpreters_set___main___attrs(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(updates), &_Py_ID(restrict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", "updates", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "set___main___attrs", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *id; + PyObject *updates; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!PyDict_Check(args[1])) { + _PyArg_BadArgument("set___main___attrs", "argument 'updates'", "dict", args[1]); + goto exit; + } + updates = args[1]; + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[2]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_set___main___attrs_impl(module, id, updates, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_exec__doc__, +"exec($module, /, id, code, shared={}, *, restrict=False)\n" +"--\n" +"\n" +"Execute the provided code in the identified interpreter.\n" +"\n" +"This is equivalent to running the builtin exec() under the target\n" +"interpreter, using the __dict__ of its __main__ module as both\n" +"globals and locals.\n" +"\n" +"\"code\" may be a string containing the text of a Python script.\n" +"\n" +"Functions (and code objects) are also supported, with some restrictions.\n" +"The code/function must not take any arguments or be a closure\n" +"(i.e. have cell vars). Methods and other callables are not supported.\n" +"\n" +"If a function is provided, its code object is used and all its state\n" +"is ignored, including its __globals__ dict."); + +#define _INTERPRETERS_EXEC_METHODDEF \ + {"exec", _PyCFunction_CAST(_interpreters_exec), METH_FASTCALL|METH_KEYWORDS, _interpreters_exec__doc__}, + +static PyObject * +_interpreters_exec_impl(PyObject *module, PyObject *id, PyObject *code, + PyObject *shared, int restricted); + +static PyObject * +_interpreters_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(code), &_Py_ID(shared), &_Py_ID(restrict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", "code", "shared", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "exec", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *id; + PyObject *code; + PyObject *shared = NULL; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + code = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + if (!PyDict_Check(args[2])) { + _PyArg_BadArgument("exec", "argument 'shared'", "dict", args[2]); + goto exit; + } + shared = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[3]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_exec_impl(module, id, code, shared, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_run_string__doc__, +"run_string($module, /, id, script, shared={}, *, restrict=False)\n" +"--\n" +"\n" +"Execute the provided string in the identified interpreter.\n" +"\n" +"(See _interpreters.exec().)"); + +#define _INTERPRETERS_RUN_STRING_METHODDEF \ + {"run_string", _PyCFunction_CAST(_interpreters_run_string), METH_FASTCALL|METH_KEYWORDS, _interpreters_run_string__doc__}, + +static PyObject * +_interpreters_run_string_impl(PyObject *module, PyObject *id, + PyObject *script, PyObject *shared, + int restricted); + +static PyObject * +_interpreters_run_string(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(script), &_Py_ID(shared), &_Py_ID(restrict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", "script", "shared", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "run_string", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *id; + PyObject *script; + PyObject *shared = NULL; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("run_string", "argument 'script'", "str", args[1]); + goto exit; + } + script = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + if (!PyDict_Check(args[2])) { + _PyArg_BadArgument("run_string", "argument 'shared'", "dict", args[2]); + goto exit; + } + shared = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[3]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_run_string_impl(module, id, script, shared, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_run_func__doc__, +"run_func($module, /, id, func, shared={}, *, restrict=False)\n" +"--\n" +"\n" +"Execute the body of the provided function in the identified interpreter.\n" +"\n" +"Code objects are also supported. In both cases, closures and args\n" +"are not supported. Methods and other callables are not supported\n" +"either.\n" +"\n" +"(See _interpreters.exec().)"); + +#define _INTERPRETERS_RUN_FUNC_METHODDEF \ + {"run_func", _PyCFunction_CAST(_interpreters_run_func), METH_FASTCALL|METH_KEYWORDS, _interpreters_run_func__doc__}, + +static PyObject * +_interpreters_run_func_impl(PyObject *module, PyObject *id, PyObject *func, + PyObject *shared, int restricted); + +static PyObject * +_interpreters_run_func(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(func), &_Py_ID(shared), &_Py_ID(restrict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", "func", "shared", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "run_func", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *id; + PyObject *func; + PyObject *shared = NULL; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + func = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + if (!PyDict_Check(args[2])) { + _PyArg_BadArgument("run_func", "argument 'shared'", "dict", args[2]); + goto exit; + } + shared = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[3]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_run_func_impl(module, id, func, shared, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_call__doc__, +"call($module, /, id, callable, args=(), kwargs={}, *,\n" +" preserve_exc=False, restrict=False)\n" +"--\n" +"\n" +"Call the provided object in the identified interpreter.\n" +"\n" +"Pass the given args and kwargs, if possible."); + +#define _INTERPRETERS_CALL_METHODDEF \ + {"call", _PyCFunction_CAST(_interpreters_call), METH_FASTCALL|METH_KEYWORDS, _interpreters_call__doc__}, + +static PyObject * +_interpreters_call_impl(PyObject *module, PyObject *id, PyObject *callable, + PyObject *args, PyObject *kwargs, int preserve_exc, + int restricted); + +static PyObject * +_interpreters_call(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 6 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(callable), &_Py_ID(args), &_Py_ID(kwargs), &_Py_ID(preserve_exc), &_Py_ID(restrict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", "callable", "args", "kwargs", "preserve_exc", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "call", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[6]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + PyObject *id; + PyObject *callable; + PyObject *__clinic_args = NULL; + PyObject *__clinic_kwargs = NULL; + int preserve_exc = 0; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + callable = args[1]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[2]) { + if (!PyTuple_Check(args[2])) { + _PyArg_BadArgument("call", "argument 'args'", "tuple", args[2]); + goto exit; + } + __clinic_args = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[3]) { + if (!PyDict_Check(args[3])) { + _PyArg_BadArgument("call", "argument 'kwargs'", "dict", args[3]); + goto exit; + } + __clinic_kwargs = args[3]; + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[4]) { + preserve_exc = PyObject_IsTrue(args[4]); + if (preserve_exc < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + restricted = PyObject_IsTrue(args[5]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_call_impl(module, id, callable, __clinic_args, __clinic_kwargs, preserve_exc, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_is_shareable__doc__, +"is_shareable($module, /, obj)\n" +"--\n" +"\n" +"Return True if the object\'s data may be shared between interpreters and False otherwise."); + +#define _INTERPRETERS_IS_SHAREABLE_METHODDEF \ + {"is_shareable", _PyCFunction_CAST(_interpreters_is_shareable), METH_FASTCALL|METH_KEYWORDS, _interpreters_is_shareable__doc__}, + +static PyObject * +_interpreters_is_shareable_impl(PyObject *module, PyObject *obj); + +static PyObject * +_interpreters_is_shareable(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(obj), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"obj", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_shareable", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *obj; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + obj = args[0]; + return_value = _interpreters_is_shareable_impl(module, obj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_is_running__doc__, +"is_running($module, /, id, *, restrict=False)\n" +"--\n" +"\n" +"Return whether or not the identified interpreter is running."); + +#define _INTERPRETERS_IS_RUNNING_METHODDEF \ + {"is_running", _PyCFunction_CAST(_interpreters_is_running), METH_FASTCALL|METH_KEYWORDS, _interpreters_is_running__doc__}, + +static PyObject * +_interpreters_is_running_impl(PyObject *module, PyObject *id, int restricted); + +static PyObject * +_interpreters_is_running(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(restrict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "is_running", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *id; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[1]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_is_running_impl(module, id, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_get_config__doc__, +"get_config($module, /, id, *, restrict=False)\n" +"--\n" +"\n" +"Return a representation of the config used to initialize the interpreter."); + +#define _INTERPRETERS_GET_CONFIG_METHODDEF \ + {"get_config", _PyCFunction_CAST(_interpreters_get_config), METH_FASTCALL|METH_KEYWORDS, _interpreters_get_config__doc__}, + +static PyObject * +_interpreters_get_config_impl(PyObject *module, PyObject *id, int restricted); + +static PyObject * +_interpreters_get_config(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(restrict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "get_config", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *id; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[1]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_get_config_impl(module, id, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_whence__doc__, +"whence($module, /, id)\n" +"--\n" +"\n" +"Return an identifier for where the interpreter was created."); + +#define _INTERPRETERS_WHENCE_METHODDEF \ + {"whence", _PyCFunction_CAST(_interpreters_whence), METH_FASTCALL|METH_KEYWORDS, _interpreters_whence__doc__}, + +static PyObject * +_interpreters_whence_impl(PyObject *module, PyObject *id); + +static PyObject * +_interpreters_whence(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "whence", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *id; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + return_value = _interpreters_whence_impl(module, id); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_incref__doc__, +"incref($module, /, id, *, implieslink=False, restrict=False)\n" +"--\n" +"\n"); + +#define _INTERPRETERS_INCREF_METHODDEF \ + {"incref", _PyCFunction_CAST(_interpreters_incref), METH_FASTCALL|METH_KEYWORDS, _interpreters_incref__doc__}, + +static PyObject * +_interpreters_incref_impl(PyObject *module, PyObject *id, int implieslink, + int restricted); + +static PyObject * +_interpreters_incref(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(implieslink), &_Py_ID(restrict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", "implieslink", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "incref", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *id; + int implieslink = 0; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + implieslink = PyObject_IsTrue(args[1]); + if (implieslink < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + restricted = PyObject_IsTrue(args[2]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_incref_impl(module, id, implieslink, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_decref__doc__, +"decref($module, /, id, *, restrict=False)\n" +"--\n" +"\n"); + +#define _INTERPRETERS_DECREF_METHODDEF \ + {"decref", _PyCFunction_CAST(_interpreters_decref), METH_FASTCALL|METH_KEYWORDS, _interpreters_decref__doc__}, + +static PyObject * +_interpreters_decref_impl(PyObject *module, PyObject *id, int restricted); + +static PyObject * +_interpreters_decref(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(id), &_Py_ID(restrict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"id", "restrict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "decref", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *id; + int restricted = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + id = args[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + restricted = PyObject_IsTrue(args[1]); + if (restricted < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = _interpreters_decref_impl(module, id, restricted); + +exit: + return return_value; +} + +PyDoc_STRVAR(_interpreters_capture_exception__doc__, +"capture_exception($module, /, exc=None)\n" +"--\n" +"\n" +"Return a snapshot of an exception.\n" +"\n" +"If \"exc\" is None then the current exception, if any, is used (but not\n" +"cleared). The returned snapshot is the same as what\n" +"_interpreters.exec() returns."); + +#define _INTERPRETERS_CAPTURE_EXCEPTION_METHODDEF \ + {"capture_exception", _PyCFunction_CAST(_interpreters_capture_exception), METH_FASTCALL|METH_KEYWORDS, _interpreters_capture_exception__doc__}, + +static PyObject * +_interpreters_capture_exception_impl(PyObject *module, PyObject *exc_arg); + +static PyObject * +_interpreters_capture_exception(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(exc), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"exc", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "capture_exception", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *exc_arg = Py_None; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + exc_arg = args[0]; +skip_optional_pos: + return_value = _interpreters_capture_exception_impl(module, exc_arg); + +exit: + return return_value; +} +/*[clinic end generated code: output=8c3ca09c304378ad input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_json.c.h b/Modules/clinic/_json.c.h new file mode 100644 index 00000000000000..d3ed1dfe0515a0 --- /dev/null +++ b/Modules/clinic/_json.c.h @@ -0,0 +1,128 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +PyDoc_STRVAR(py_scanstring__doc__, +"scanstring($module, pystr, end, strict=True, /)\n" +"--\n" +"\n" +"Scan the string s for a JSON string.\n" +"\n" +"End is the index of the character in s after the quote that started the\n" +"JSON string. Unescapes all valid JSON string escape sequences and raises\n" +"ValueError on attempt to decode an invalid string. If strict is False\n" +"then literal control characters are allowed in the string.\n" +"\n" +"Returns a tuple of the decoded string and the index of the character in\n" +"s after the end quote."); + +#define PY_SCANSTRING_METHODDEF \ + {"scanstring", _PyCFunction_CAST(py_scanstring), METH_FASTCALL, py_scanstring__doc__}, + +static PyObject * +py_scanstring_impl(PyObject *module, PyObject *pystr, Py_ssize_t end, + int strict); + +static PyObject * +py_scanstring(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *pystr; + Py_ssize_t end; + int strict = 1; + + if (!_PyArg_CheckPositional("scanstring", nargs, 2, 3)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("scanstring", "argument 1", "str", args[0]); + goto exit; + } + pystr = args[0]; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + end = ival; + } + if (nargs < 3) { + goto skip_optional; + } + strict = PyObject_IsTrue(args[2]); + if (strict < 0) { + goto exit; + } +skip_optional: + return_value = py_scanstring_impl(module, pystr, end, strict); + +exit: + return return_value; +} + +PyDoc_STRVAR(py_encode_basestring_ascii__doc__, +"encode_basestring_ascii($module, pystr, /)\n" +"--\n" +"\n" +"Return an ASCII-only JSON representation of a Python string"); + +#define PY_ENCODE_BASESTRING_ASCII_METHODDEF \ + {"encode_basestring_ascii", (PyCFunction)py_encode_basestring_ascii, METH_O, py_encode_basestring_ascii__doc__}, + +static PyObject * +py_encode_basestring_ascii_impl(PyObject *module, PyObject *pystr); + +static PyObject * +py_encode_basestring_ascii(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *pystr; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("encode_basestring_ascii", "argument", "str", arg); + goto exit; + } + pystr = arg; + return_value = py_encode_basestring_ascii_impl(module, pystr); + +exit: + return return_value; +} + +PyDoc_STRVAR(py_encode_basestring__doc__, +"encode_basestring($module, pystr, /)\n" +"--\n" +"\n" +"Return a JSON representation of a Python string"); + +#define PY_ENCODE_BASESTRING_METHODDEF \ + {"encode_basestring", (PyCFunction)py_encode_basestring, METH_O, py_encode_basestring__doc__}, + +static PyObject * +py_encode_basestring_impl(PyObject *module, PyObject *pystr); + +static PyObject * +py_encode_basestring(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *pystr; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("encode_basestring", "argument", "str", arg); + goto exit; + } + pystr = arg; + return_value = py_encode_basestring_impl(module, pystr); + +exit: + return return_value; +} +/*[clinic end generated code: output=ea6e9a271d4ceaf2 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_lsprof.c.h b/Modules/clinic/_lsprof.c.h index c426cd6fe02f39..acb4aaf27e377f 100644 --- a/Modules/clinic/_lsprof.c.h +++ b/Modules/clinic/_lsprof.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(_lsprof_Profiler_getstats__doc__, @@ -45,11 +46,18 @@ _lsprof_Profiler_getstats_impl(ProfilerObject *self, PyTypeObject *cls); static PyObject * _lsprof_Profiler_getstats(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { + PyObject *return_value = NULL; + if (nargs || (kwnames && PyTuple_GET_SIZE(kwnames))) { PyErr_SetString(PyExc_TypeError, "getstats() takes no arguments"); - return NULL; + goto exit; } - return _lsprof_Profiler_getstats_impl((ProfilerObject *)self, cls); + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _lsprof_Profiler_getstats_impl((ProfilerObject *)self, cls); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; } PyDoc_STRVAR(_lsprof_Profiler__pystart_callback__doc__, @@ -76,7 +84,9 @@ _lsprof_Profiler__pystart_callback(PyObject *self, PyObject *const *args, Py_ssi } code = args[0]; instruction_offset = args[1]; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _lsprof_Profiler__pystart_callback_impl((ProfilerObject *)self, code, instruction_offset); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -109,7 +119,9 @@ _lsprof_Profiler__pythrow_callback(PyObject *self, PyObject *const *args, Py_ssi code = args[0]; instruction_offset = args[1]; exception = args[2]; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _lsprof_Profiler__pythrow_callback_impl((ProfilerObject *)self, code, instruction_offset, exception); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -143,7 +155,9 @@ _lsprof_Profiler__pyreturn_callback(PyObject *self, PyObject *const *args, Py_ss code = args[0]; instruction_offset = args[1]; retval = args[2]; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _lsprof_Profiler__pyreturn_callback_impl((ProfilerObject *)self, code, instruction_offset, retval); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -178,7 +192,9 @@ _lsprof_Profiler__ccall_callback(PyObject *self, PyObject *const *args, Py_ssize instruction_offset = args[1]; callable = args[2]; self_arg = args[3]; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _lsprof_Profiler__ccall_callback_impl((ProfilerObject *)self, code, instruction_offset, callable, self_arg); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -215,7 +231,9 @@ _lsprof_Profiler__creturn_callback(PyObject *self, PyObject *const *args, Py_ssi instruction_offset = args[1]; callable = args[2]; self_arg = args[3]; + Py_BEGIN_CRITICAL_SECTION(self); return_value = _lsprof_Profiler__creturn_callback_impl((ProfilerObject *)self, code, instruction_offset, callable, self_arg); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -299,7 +317,9 @@ _lsprof_Profiler_enable(PyObject *self, PyObject *const *args, Py_ssize_t nargs, goto exit; } skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = _lsprof_Profiler_enable_impl((ProfilerObject *)self, subcalls, builtins); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -320,7 +340,13 @@ _lsprof_Profiler_disable_impl(ProfilerObject *self); static PyObject * _lsprof_Profiler_disable(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return _lsprof_Profiler_disable_impl((ProfilerObject *)self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _lsprof_Profiler_disable_impl((ProfilerObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(_lsprof_Profiler_clear__doc__, @@ -338,7 +364,13 @@ _lsprof_Profiler_clear_impl(ProfilerObject *self); static PyObject * _lsprof_Profiler_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return _lsprof_Profiler_clear_impl((ProfilerObject *)self); + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _lsprof_Profiler_clear_impl((ProfilerObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; } PyDoc_STRVAR(profiler_init__doc__, @@ -444,4 +476,4 @@ profiler_init(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=9e46985561166c37 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=af26a0b0ddcc3351 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_lzmamodule.c.h b/Modules/clinic/_lzmamodule.c.h index ebdc81a0dac2f0..bba107e8f806da 100644 --- a/Modules/clinic/_lzmamodule.c.h +++ b/Modules/clinic/_lzmamodule.c.h @@ -74,18 +74,19 @@ PyDoc_STRVAR(_lzma_LZMADecompressor_decompress__doc__, "\n" "Decompress *data*, returning uncompressed data as bytes.\n" "\n" -"If *max_length* is nonnegative, returns at most *max_length* bytes of\n" -"decompressed data. If this limit is reached and further output can be\n" -"produced, *self.needs_input* will be set to ``False``. In this case, the next\n" -"call to *decompress()* may provide *data* as b\'\' to obtain more of the output.\n" +"If *max_length* is nonnegative, returns at most *max_length* bytes\n" +"of decompressed data. If this limit is reached and further output\n" +"can be produced, *self.needs_input* will be set to ``False``. In\n" +"this case, the next call to *decompress()* may provide *data* as b\'\'\n" +"to obtain more of the output.\n" "\n" -"If all of the input data was decompressed and returned (either because this\n" -"was less than *max_length* bytes, or because *max_length* was negative),\n" -"*self.needs_input* will be set to True.\n" +"If all of the input data was decompressed and returned (either\n" +"because this was less than *max_length* bytes, or because\n" +"*max_length* was negative), *self.needs_input* will be set to True.\n" "\n" -"Attempting to decompress data after the end of stream is reached raises an\n" -"EOFError. Any data found after the end of the stream is ignored and saved in\n" -"the unused_data attribute."); +"Attempting to decompress data after the end of stream is reached\n" +"raises an EOFError. Any data found after the end of the stream is\n" +"ignored and saved in the unused_data attribute."); #define _LZMA_LZMADECOMPRESSOR_DECOMPRESS_METHODDEF \ {"decompress", _PyCFunction_CAST(_lzma_LZMADecompressor_decompress), METH_FASTCALL|METH_KEYWORDS, _lzma_LZMADecompressor_decompress__doc__}, @@ -333,4 +334,4 @@ _lzma__decode_filter_properties(PyObject *module, PyObject *const *args, Py_ssiz return return_value; } -/*[clinic end generated code: output=6386084cb43d2533 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ffc6d673d858048c input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_posixsubprocess.c.h b/Modules/clinic/_posixsubprocess.c.h index d52629cf6eaa5b..e7e9707f182a2e 100644 --- a/Modules/clinic/_posixsubprocess.c.h +++ b/Modules/clinic/_posixsubprocess.c.h @@ -14,15 +14,15 @@ PyDoc_STRVAR(subprocess_fork_exec__doc__, "\n" "Spawn a fresh new child process.\n" "\n" -"Fork a child process, close parent file descriptors as appropriate in the\n" -"child and duplicate the few that are needed before calling exec() in the\n" -"child process.\n" +"Fork a child process, close parent file descriptors as appropriate in\n" +"the child and duplicate the few that are needed before calling exec() in\n" +"the child process.\n" "\n" -"If close_fds is True, close file descriptors 3 and higher, except those listed\n" -"in the sorted tuple pass_fds.\n" +"If close_fds is True, close file descriptors 3 and higher, except those\n" +"listed in the sorted tuple pass_fds.\n" "\n" -"The preexec_fn, if supplied, will be called immediately before closing file\n" -"descriptors and exec.\n" +"The preexec_fn, if supplied, will be called immediately before closing\n" +"file descriptors and exec.\n" "\n" "WARNING: preexec_fn is NOT SAFE if your application uses threads.\n" " It may trigger infrequent, difficult to debug deadlocks.\n" @@ -150,4 +150,4 @@ subprocess_fork_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=942bc2748a9c2785 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=138941c284792aa1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_queuemodule.c.h b/Modules/clinic/_queuemodule.c.h index 1751d68716ba5f..b67dd23f260c9e 100644 --- a/Modules/clinic/_queuemodule.c.h +++ b/Modules/clinic/_queuemodule.c.h @@ -44,8 +44,9 @@ PyDoc_STRVAR(_queue_SimpleQueue_put__doc__, "\n" "Put the item on the queue.\n" "\n" -"The optional \'block\' and \'timeout\' arguments are ignored, as this method\n" -"never blocks. They are provided for compatibility with the Queue class."); +"The optional \'block\' and \'timeout\' arguments are ignored, as this\n" +"method never blocks. They are provided for compatibility with the\n" +"Queue class."); #define _QUEUE_SIMPLEQUEUE_PUT_METHODDEF \ {"put", _PyCFunction_CAST(_queue_SimpleQueue_put), METH_FASTCALL|METH_KEYWORDS, _queue_SimpleQueue_put__doc__}, @@ -188,10 +189,11 @@ PyDoc_STRVAR(_queue_SimpleQueue_get__doc__, "\n" "Remove and return an item from the queue.\n" "\n" -"If optional args \'block\' is true and \'timeout\' is None (the default),\n" -"block if necessary until an item is available. If \'timeout\' is\n" -"a non-negative number, it blocks at most \'timeout\' seconds and raises\n" -"the Empty exception if no item was available within that time.\n" +"If optional args \'block\' is true and \'timeout\' is None (the\n" +"default), block if necessary until an item is available. If\n" +"\'timeout\' is a non-negative number, it blocks at most \'timeout\'\n" +"seconds and raises the Empty exception if no item was available\n" +"within that time.\n" "Otherwise (\'block\' is false), return an item if one is immediately\n" "available, else raise the Empty exception (\'timeout\' is ignored\n" "in that case)."); @@ -358,4 +360,34 @@ _queue_SimpleQueue_qsize(PyObject *self, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=1d3efe9df89997cf input=a9049054013a1b77]*/ + +PyDoc_STRVAR(_queue_SimpleQueue___sizeof____doc__, +"__sizeof__($self, /)\n" +"--\n" +"\n" +"Returns size in memory, in bytes."); + +#define _QUEUE_SIMPLEQUEUE___SIZEOF___METHODDEF \ + {"__sizeof__", (PyCFunction)_queue_SimpleQueue___sizeof__, METH_NOARGS, _queue_SimpleQueue___sizeof____doc__}, + +static Py_ssize_t +_queue_SimpleQueue___sizeof___impl(simplequeueobject *self); + +static PyObject * +_queue_SimpleQueue___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + Py_ssize_t _return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + _return_value = _queue_SimpleQueue___sizeof___impl((simplequeueobject *)self); + Py_END_CRITICAL_SECTION(); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromSsize_t(_return_value); + +exit: + return return_value; +} +/*[clinic end generated code: output=8219fe2f2ed5f068 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_randommodule.c.h b/Modules/clinic/_randommodule.c.h index 2563a16aea0b6f..ca9cad7a572dad 100644 --- a/Modules/clinic/_randommodule.c.h +++ b/Modules/clinic/_randommodule.c.h @@ -143,4 +143,35 @@ _random_Random_getrandbits(PyObject *self, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=7ce97b2194eecaf7 input=a9049054013a1b77]*/ + +static int +random_init_impl(RandomObject *self, PyObject *seed); + +static int +random_init(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + PyTypeObject *base_tp = (PyTypeObject *)_randomstate_type(Py_TYPE(self))->Random_Type; + PyObject *seed = NULL; + + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && + !_PyArg_NoKeywords("Random", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("Random", PyTuple_GET_SIZE(args), 0, 1)) { + goto exit; + } + if (PyTuple_GET_SIZE(args) < 1) { + goto skip_optional; + } + seed = PyTuple_GET_ITEM(args, 0); +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = random_init_impl((RandomObject *)self, seed); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} +/*[clinic end generated code: output=ec95f7df0c3f3c19 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_remote_debugging_module.c.h b/Modules/clinic/_remote_debugging_module.c.h index f6a51cdba6b401..60adb357e32e71 100644 --- a/Modules/clinic/_remote_debugging_module.c.h +++ b/Modules/clinic/_remote_debugging_module.c.h @@ -11,7 +11,8 @@ preserve PyDoc_STRVAR(_remote_debugging_RemoteUnwinder___init____doc__, "RemoteUnwinder(pid, *, all_threads=False, only_active_thread=False,\n" -" debug=False)\n" +" mode=0, debug=False, skip_non_matching_threads=True,\n" +" native=False, gc=False)\n" "--\n" "\n" "Initialize a new RemoteUnwinder object for debugging a remote Python process.\n" @@ -21,9 +22,16 @@ PyDoc_STRVAR(_remote_debugging_RemoteUnwinder___init____doc__, " all_threads: If True, initialize state for all threads in the process.\n" " If False, only initialize for the main thread.\n" " only_active_thread: If True, only sample the thread holding the GIL.\n" +" mode: Profiling mode: 0=WALL (wall-time), 1=CPU (cpu-time), 2=GIL (gil-time).\n" " Cannot be used together with all_threads=True.\n" " debug: If True, chain exceptions to explain the sequence of events that\n" " lead to the exception.\n" +" skip_non_matching_threads: If True, skip threads that don\'t match the selected mode.\n" +" If False, include all threads regardless of mode.\n" +" native: If True, include artificial \"<native>\" frames to denote calls to\n" +" non-Python code.\n" +" gc: If True, include artificial \"<GC>\" frames to denote active garbage\n" +" collection.\n" "\n" "The RemoteUnwinder provides functionality to inspect and debug a running Python\n" "process, including examining thread states, stack frames and other runtime data.\n" @@ -38,7 +46,9 @@ static int _remote_debugging_RemoteUnwinder___init___impl(RemoteUnwinderObject *self, int pid, int all_threads, int only_active_thread, - int debug); + int mode, int debug, + int skip_non_matching_threads, + int native, int gc); static int _remote_debugging_RemoteUnwinder___init__(PyObject *self, PyObject *args, PyObject *kwargs) @@ -46,7 +56,7 @@ _remote_debugging_RemoteUnwinder___init__(PyObject *self, PyObject *args, PyObje int return_value = -1; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 4 + #define NUM_KEYWORDS 8 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD @@ -55,7 +65,7 @@ _remote_debugging_RemoteUnwinder___init__(PyObject *self, PyObject *args, PyObje } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) .ob_hash = -1, - .ob_item = { &_Py_ID(pid), &_Py_ID(all_threads), &_Py_ID(only_active_thread), &_Py_ID(debug), }, + .ob_item = { &_Py_ID(pid), &_Py_ID(all_threads), &_Py_ID(only_active_thread), &_Py_ID(mode), &_Py_ID(debug), &_Py_ID(skip_non_matching_threads), &_Py_ID(native), &_Py_ID(gc), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -64,21 +74,25 @@ _remote_debugging_RemoteUnwinder___init__(PyObject *self, PyObject *args, PyObje # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"pid", "all_threads", "only_active_thread", "debug", NULL}; + static const char * const _keywords[] = {"pid", "all_threads", "only_active_thread", "mode", "debug", "skip_non_matching_threads", "native", "gc", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "RemoteUnwinder", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[4]; + PyObject *argsbuf[8]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; int pid; int all_threads = 0; int only_active_thread = 0; + int mode = 0; int debug = 0; + int skip_non_matching_threads = 1; + int native = 0; + int gc = 0; fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -110,12 +124,48 @@ _remote_debugging_RemoteUnwinder___init__(PyObject *self, PyObject *args, PyObje goto skip_optional_kwonly; } } - debug = PyObject_IsTrue(fastargs[3]); - if (debug < 0) { + if (fastargs[3]) { + mode = PyLong_AsInt(fastargs[3]); + if (mode == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[4]) { + debug = PyObject_IsTrue(fastargs[4]); + if (debug < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[5]) { + skip_non_matching_threads = PyObject_IsTrue(fastargs[5]); + if (skip_non_matching_threads < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[6]) { + native = PyObject_IsTrue(fastargs[6]); + if (native < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + gc = PyObject_IsTrue(fastargs[7]); + if (gc < 0) { goto exit; } skip_optional_kwonly: - return_value = _remote_debugging_RemoteUnwinder___init___impl((RemoteUnwinderObject *)self, pid, all_threads, only_active_thread, debug); + return_value = _remote_debugging_RemoteUnwinder___init___impl((RemoteUnwinderObject *)self, pid, all_threads, only_active_thread, mode, debug, skip_non_matching_threads, native, gc); exit: return return_value; @@ -125,28 +175,37 @@ PyDoc_STRVAR(_remote_debugging_RemoteUnwinder_get_stack_trace__doc__, "get_stack_trace($self, /)\n" "--\n" "\n" -"Returns a list of stack traces for threads in the target process.\n" +"Returns stack traces for all interpreters and threads in process.\n" "\n" -"Each element in the returned list is a tuple of (thread_id, frame_list), where:\n" -"- thread_id is the OS thread identifier\n" -"- frame_list is a list of tuples (function_name, filename, line_number) representing\n" -" the Python stack frames for that thread, ordered from most recent to oldest\n" +"Each element in the returned list is a tuple of (interpreter_id, thread_list), where:\n" +"- interpreter_id is the interpreter identifier\n" +"- thread_list is a list of tuples (thread_id, frame_list) for threads in that interpreter\n" +" - thread_id is the OS thread identifier\n" +" - frame_list is a list of tuples (function_name, filename, line_number) representing\n" +" the Python stack frames for that thread, ordered from most recent to oldest\n" "\n" "The threads returned depend on the initialization parameters:\n" -"- If only_active_thread was True: returns only the thread holding the GIL\n" -"- If all_threads was True: returns all threads\n" -"- Otherwise: returns only the main thread\n" +"- If only_active_thread was True: returns only the thread holding the GIL across all interpreters\n" +"- If all_threads was True: returns all threads across all interpreters\n" +"- Otherwise: returns only the main thread of each interpreter\n" "\n" "Example:\n" " [\n" -" (1234, [\n" -" (\'process_data\', \'worker.py\', 127),\n" -" (\'run_worker\', \'worker.py\', 45),\n" -" (\'main\', \'app.py\', 23)\n" +" (0, [ # Main interpreter\n" +" (1234, [\n" +" (\'process_data\', \'worker.py\', 127),\n" +" (\'run_worker\', \'worker.py\', 45),\n" +" (\'main\', \'app.py\', 23)\n" +" ]),\n" +" (1235, [\n" +" (\'handle_request\', \'server.py\', 89),\n" +" (\'serve_forever\', \'server.py\', 52)\n" +" ])\n" " ]),\n" -" (1235, [\n" -" (\'handle_request\', \'server.py\', 89),\n" -" (\'serve_forever\', \'server.py\', 52)\n" +" (1, [ # Sub-interpreter\n" +" (1236, [\n" +" (\'sub_worker\', \'sub.py\', 15)\n" +" ])\n" " ])\n" " ]\n" "\n" @@ -288,4 +347,4 @@ _remote_debugging_RemoteUnwinder_get_async_stack_trace(PyObject *self, PyObject return return_value; } -/*[clinic end generated code: output=0dd1e6e8bab2a8b1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=99fed5c94cf36881 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 7027d87379283d..e337ed2390a1fc 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -48,7 +48,7 @@ static PyObject * _ssl__test_decode_cert(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; - PyObject *path; + PyObject *path = NULL; if (!PyUnicode_FSConverter(arg, &path)) { goto exit; @@ -56,6 +56,9 @@ _ssl__test_decode_cert(PyObject *module, PyObject *arg) return_value = _ssl__test_decode_cert_impl(module, path); exit: + /* Cleanup for path */ + Py_XDECREF(path); + return return_value; } @@ -196,6 +199,75 @@ _ssl__SSLSocket_cipher(PyObject *self, PyObject *Py_UNUSED(ignored)) return return_value; } +PyDoc_STRVAR(_ssl__SSLSocket_group__doc__, +"group($self, /)\n" +"--\n" +"\n"); + +#define _SSL__SSLSOCKET_GROUP_METHODDEF \ + {"group", (PyCFunction)_ssl__SSLSocket_group, METH_NOARGS, _ssl__SSLSocket_group__doc__}, + +static PyObject * +_ssl__SSLSocket_group_impl(PySSLSocket *self); + +static PyObject * +_ssl__SSLSocket_group(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ssl__SSLSocket_group_impl((PySSLSocket *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_ssl__SSLSocket_client_sigalg__doc__, +"client_sigalg($self, /)\n" +"--\n" +"\n"); + +#define _SSL__SSLSOCKET_CLIENT_SIGALG_METHODDEF \ + {"client_sigalg", (PyCFunction)_ssl__SSLSocket_client_sigalg, METH_NOARGS, _ssl__SSLSocket_client_sigalg__doc__}, + +static PyObject * +_ssl__SSLSocket_client_sigalg_impl(PySSLSocket *self); + +static PyObject * +_ssl__SSLSocket_client_sigalg(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ssl__SSLSocket_client_sigalg_impl((PySSLSocket *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(_ssl__SSLSocket_server_sigalg__doc__, +"server_sigalg($self, /)\n" +"--\n" +"\n"); + +#define _SSL__SSLSOCKET_SERVER_SIGALG_METHODDEF \ + {"server_sigalg", (PyCFunction)_ssl__SSLSocket_server_sigalg, METH_NOARGS, _ssl__SSLSocket_server_sigalg__doc__}, + +static PyObject * +_ssl__SSLSocket_server_sigalg_impl(PySSLSocket *self); + +static PyObject * +_ssl__SSLSocket_server_sigalg(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ssl__SSLSocket_server_sigalg_impl((PySSLSocket *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_ssl__SSLSocket_version__doc__, "version($self, /)\n" "--\n" @@ -262,9 +334,10 @@ _ssl__SSLSocket_compression(PyObject *self, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(_ssl__SSLSocket_context__doc__, "This changes the context associated with the SSLSocket.\n" "\n" -"This is typically used from within a callback function set by the sni_callback\n" -"on the SSLContext to change the certificate information associated with the\n" -"SSLSocket before the cryptographic exchange handshake messages."); +"This is typically used from within a callback function set by the\n" +"sni_callback on the SSLContext to change the certificate information\n" +"associated with the SSLSocket before the cryptographic exchange\n" +"handshake messages."); #if defined(_ssl__SSLSocket_context_DOCSTR) # undef _ssl__SSLSocket_context_DOCSTR #endif @@ -499,9 +572,9 @@ PyDoc_STRVAR(_ssl__SSLSocket_sendfile__doc__, "\n" "Write size bytes from offset in the file descriptor fd to the SSL connection.\n" "\n" -"This method uses the zero-copy technique and returns the number of bytes\n" -"written. It should be called only when Kernel TLS is used for sending data in\n" -"the connection.\n" +"This method uses the zero-copy technique and returns the number of\n" +"bytes written. It should be called only when Kernel TLS is used for\n" +"sending data in the connection.\n" "\n" "The meaning of flags is platform dependent."); @@ -690,8 +763,9 @@ PyDoc_STRVAR(_ssl__SSLSocket_get_channel_binding__doc__, "\n" "Get channel binding data for current connection.\n" "\n" -"Raise ValueError if the requested `cb_type` is not supported. Return bytes\n" -"of the data or None if the data is not available (e.g. before the handshake).\n" +"Raise ValueError if the requested `cb_type` is not supported.\n" +"Return bytes of the data or None if the data is not available (e.g.\n" +"before the handshake).\n" "Only \'tls-unique\' channel binding data from RFC 5929 is supported."); #define _SSL__SSLSOCKET_GET_CHANNEL_BINDING_METHODDEF \ @@ -946,6 +1020,45 @@ _ssl__SSLContext_set_ciphers(PyObject *self, PyObject *arg) return return_value; } +PyDoc_STRVAR(_ssl__SSLContext_set_ciphersuites__doc__, +"set_ciphersuites($self, ciphersuites, /)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_SET_CIPHERSUITES_METHODDEF \ + {"set_ciphersuites", (PyCFunction)_ssl__SSLContext_set_ciphersuites, METH_O, _ssl__SSLContext_set_ciphersuites__doc__}, + +static PyObject * +_ssl__SSLContext_set_ciphersuites_impl(PySSLContext *self, + const char *ciphersuites); + +static PyObject * +_ssl__SSLContext_set_ciphersuites(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + const char *ciphersuites; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("set_ciphersuites", "argument", "str", arg); + goto exit; + } + Py_ssize_t ciphersuites_length; + ciphersuites = PyUnicode_AsUTF8AndSize(arg, &ciphersuites_length); + if (ciphersuites == NULL) { + goto exit; + } + if (strlen(ciphersuites) != (size_t)ciphersuites_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ssl__SSLContext_set_ciphersuites_impl((PySSLContext *)self, ciphersuites); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + PyDoc_STRVAR(_ssl__SSLContext_get_ciphers__doc__, "get_ciphers($self, /)\n" "--\n" @@ -969,6 +1082,189 @@ _ssl__SSLContext_get_ciphers(PyObject *self, PyObject *Py_UNUSED(ignored)) return return_value; } +PyDoc_STRVAR(_ssl__SSLContext_set_groups__doc__, +"set_groups($self, grouplist, /)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_SET_GROUPS_METHODDEF \ + {"set_groups", (PyCFunction)_ssl__SSLContext_set_groups, METH_O, _ssl__SSLContext_set_groups__doc__}, + +static PyObject * +_ssl__SSLContext_set_groups_impl(PySSLContext *self, const char *grouplist); + +static PyObject * +_ssl__SSLContext_set_groups(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + const char *grouplist; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("set_groups", "argument", "str", arg); + goto exit; + } + Py_ssize_t grouplist_length; + grouplist = PyUnicode_AsUTF8AndSize(arg, &grouplist_length); + if (grouplist == NULL) { + goto exit; + } + if (strlen(grouplist) != (size_t)grouplist_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ssl__SSLContext_set_groups_impl((PySSLContext *)self, grouplist); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(_ssl__SSLContext_get_groups__doc__, +"get_groups($self, /, *, include_aliases=False)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_GET_GROUPS_METHODDEF \ + {"get_groups", _PyCFunction_CAST(_ssl__SSLContext_get_groups), METH_FASTCALL|METH_KEYWORDS, _ssl__SSLContext_get_groups__doc__}, + +static PyObject * +_ssl__SSLContext_get_groups_impl(PySSLContext *self, int include_aliases); + +static PyObject * +_ssl__SSLContext_get_groups(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(include_aliases), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"include_aliases", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "get_groups", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int include_aliases = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 0, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + include_aliases = PyObject_IsTrue(args[0]); + if (include_aliases < 0) { + goto exit; + } +skip_optional_kwonly: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ssl__SSLContext_get_groups_impl((PySSLContext *)self, include_aliases); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(_ssl__SSLContext_set_client_sigalgs__doc__, +"set_client_sigalgs($self, sigalgslist, /)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_SET_CLIENT_SIGALGS_METHODDEF \ + {"set_client_sigalgs", (PyCFunction)_ssl__SSLContext_set_client_sigalgs, METH_O, _ssl__SSLContext_set_client_sigalgs__doc__}, + +static PyObject * +_ssl__SSLContext_set_client_sigalgs_impl(PySSLContext *self, + const char *sigalgslist); + +static PyObject * +_ssl__SSLContext_set_client_sigalgs(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + const char *sigalgslist; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("set_client_sigalgs", "argument", "str", arg); + goto exit; + } + Py_ssize_t sigalgslist_length; + sigalgslist = PyUnicode_AsUTF8AndSize(arg, &sigalgslist_length); + if (sigalgslist == NULL) { + goto exit; + } + if (strlen(sigalgslist) != (size_t)sigalgslist_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ssl__SSLContext_set_client_sigalgs_impl((PySSLContext *)self, sigalgslist); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(_ssl__SSLContext_set_server_sigalgs__doc__, +"set_server_sigalgs($self, sigalgslist, /)\n" +"--\n" +"\n"); + +#define _SSL__SSLCONTEXT_SET_SERVER_SIGALGS_METHODDEF \ + {"set_server_sigalgs", (PyCFunction)_ssl__SSLContext_set_server_sigalgs, METH_O, _ssl__SSLContext_set_server_sigalgs__doc__}, + +static PyObject * +_ssl__SSLContext_set_server_sigalgs_impl(PySSLContext *self, + const char *sigalgslist); + +static PyObject * +_ssl__SSLContext_set_server_sigalgs(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + const char *sigalgslist; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("set_server_sigalgs", "argument", "str", arg); + goto exit; + } + Py_ssize_t sigalgslist_length; + sigalgslist = PyUnicode_AsUTF8AndSize(arg, &sigalgslist_length); + if (sigalgslist == NULL) { + goto exit; + } + if (strlen(sigalgslist) != (size_t)sigalgslist_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = _ssl__SSLContext_set_server_sigalgs_impl((PySSLContext *)self, sigalgslist); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + PyDoc_STRVAR(_ssl__SSLContext__set_alpn_protocols__doc__, "_set_alpn_protocols($self, protos, /)\n" "--\n" @@ -1535,9 +1831,7 @@ _ssl__SSLContext_load_cert_chain(PyObject *self, PyObject *const *args, Py_ssize } password = args[2]; skip_optional_pos: - Py_BEGIN_CRITICAL_SECTION(self); return_value = _ssl__SSLContext_load_cert_chain_impl((PySSLContext *)self, certfile, keyfile, password); - Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -1918,8 +2212,9 @@ _ssl__SSLContext_set_ecdh_curve(PyObject *self, PyObject *name) PyDoc_STRVAR(_ssl__SSLContext_sni_callback__doc__, "Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n" "\n" -"If the argument is None then the callback is disabled. The method is called\n" -"with the SSLSocket, the server name as a string, and the SSLContext object.\n" +"If the argument is None then the callback is disabled. The method\n" +"is called with the SSLSocket, the server name as a string, and the\n" +"SSLContext object.\n" "\n" "See RFC 6066 for details of the SNI extension."); #if defined(_ssl__SSLContext_sni_callback_DOCSTR) @@ -1983,11 +2278,11 @@ PyDoc_STRVAR(_ssl__SSLContext_cert_store_stats__doc__, "\n" "Returns quantities of loaded X.509 certificates.\n" "\n" -"X.509 certificates with a CA extension and certificate revocation lists\n" -"inside the context\'s cert store.\n" +"X.509 certificates with a CA extension and certificate revocation\n" +"lists inside the context\'s cert store.\n" "\n" -"NOTE: Certificates in a capath directory aren\'t loaded unless they have\n" -"been used at least once."); +"NOTE: Certificates in a capath directory aren\'t loaded unless they\n" +"have been used at least once."); #define _SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF \ {"cert_store_stats", (PyCFunction)_ssl__SSLContext_cert_store_stats, METH_NOARGS, _ssl__SSLContext_cert_store_stats__doc__}, @@ -2013,11 +2308,11 @@ PyDoc_STRVAR(_ssl__SSLContext_get_ca_certs__doc__, "\n" "Returns a list of dicts with information of loaded CA certs.\n" "\n" -"If the optional argument is True, returns a DER-encoded copy of the CA\n" -"certificate.\n" +"If the optional argument is True, returns a DER-encoded copy of the\n" +"CA certificate.\n" "\n" -"NOTE: Certificates in a capath directory aren\'t loaded unless they have\n" -"been used at least once."); +"NOTE: Certificates in a capath directory aren\'t loaded unless they\n" +"have been used at least once."); #define _SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF \ {"get_ca_certs", _PyCFunction_CAST(_ssl__SSLContext_get_ca_certs), METH_FASTCALL|METH_KEYWORDS, _ssl__SSLContext_get_ca_certs__doc__}, @@ -2678,8 +2973,8 @@ PyDoc_STRVAR(_ssl_RAND_status__doc__, "\n" "Returns True if the OpenSSL PRNG has been seeded with enough data and False if not.\n" "\n" -"It is necessary to seed the PRNG with RAND_add() on some platforms before\n" -"using the ssl() function."); +"It is necessary to seed the PRNG with RAND_add() on some platforms\n" +"before using the ssl() function."); #define _SSL_RAND_STATUS_METHODDEF \ {"RAND_status", (PyCFunction)_ssl_RAND_status, METH_NOARGS, _ssl_RAND_status__doc__}, @@ -2725,6 +3020,23 @@ _ssl_get_default_verify_paths(PyObject *module, PyObject *Py_UNUSED(ignored)) return return_value; } +PyDoc_STRVAR(_ssl_get_sigalgs__doc__, +"get_sigalgs($module, /)\n" +"--\n" +"\n"); + +#define _SSL_GET_SIGALGS_METHODDEF \ + {"get_sigalgs", (PyCFunction)_ssl_get_sigalgs, METH_NOARGS, _ssl_get_sigalgs__doc__}, + +static PyObject * +_ssl_get_sigalgs_impl(PyObject *module); + +static PyObject * +_ssl_get_sigalgs(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _ssl_get_sigalgs_impl(module); +} + PyDoc_STRVAR(_ssl_txt2obj__doc__, "txt2obj($module, /, txt, name=False)\n" "--\n" @@ -2848,11 +3160,11 @@ PyDoc_STRVAR(_ssl_enum_certificates__doc__, "\n" "Retrieve certificates from Windows\' cert store.\n" "\n" -"store_name may be one of \'CA\', \'ROOT\' or \'MY\'. The system may provide\n" -"more cert storages, too. The function returns a list of (bytes,\n" -"encoding_type, trust) tuples. The encoding_type flag can be interpreted\n" -"with X509_ASN_ENCODING or PKCS_7_ASN_ENCODING. The trust setting is either\n" -"a set of OIDs or the boolean True."); +"store_name may be one of \'CA\', \'ROOT\' or \'MY\'. The system may\n" +"provide more cert storages, too. The function returns a list of\n" +"(bytes, encoding_type, trust) tuples. The encoding_type flag can be\n" +"interpreted with X509_ASN_ENCODING or PKCS_7_ASN_ENCODING. The\n" +"trust setting is either a set of OIDs or the boolean True."); #define _SSL_ENUM_CERTIFICATES_METHODDEF \ {"enum_certificates", _PyCFunction_CAST(_ssl_enum_certificates), METH_FASTCALL|METH_KEYWORDS, _ssl_enum_certificates__doc__}, @@ -3014,4 +3326,4 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=1adc3780d8ca682a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=aef2e74b706c6106 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_struct.c.h b/Modules/clinic/_struct.c.h index e4eaadb91eb231..e75698e3ed00cc 100644 --- a/Modules/clinic/_struct.c.h +++ b/Modules/clinic/_struct.c.h @@ -9,16 +9,74 @@ preserve #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() -PyDoc_STRVAR(Struct___init____doc__, +PyDoc_STRVAR(Struct__doc__, "Struct(format)\n" "--\n" "\n" "Create a compiled struct object.\n" "\n" -"Return a new Struct object which writes and reads binary data according to\n" -"the format string.\n" +"Return a new Struct object which writes and reads binary data according\n" +"to the format string. See help(struct) for more on format strings."); + +static PyObject * +Struct_impl(PyTypeObject *type, PyObject *format); + +static PyObject * +Struct(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(format), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"format", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "Struct", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + PyObject *format; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + format = fastargs[0]; + return_value = Struct_impl(type, format); + +exit: + return return_value; +} + +PyDoc_STRVAR(Struct___init____doc__, +"Struct(format)\n" +"--\n" +"\n" +"Create a compiled struct object.\n" "\n" -"See help(struct) for more on format strings."); +"Return a new Struct object which writes and reads binary data according\n" +"to the format string. See help(struct) for more on format strings."); static int Struct___init___impl(PyStructObject *self, PyObject *format); @@ -77,10 +135,9 @@ PyDoc_STRVAR(Struct_unpack__doc__, "\n" "Return a tuple containing unpacked values.\n" "\n" -"Unpack according to the format string Struct.format. The buffer\'s size\n" -"in bytes must be Struct.size.\n" -"\n" -"See help(struct) for more on format strings."); +"Unpack according to the struct format string. The buffer\'s\n" +"size in bytes must be the struct size. See help(struct) for more on\n" +"format strings."); #define STRUCT_UNPACK_METHODDEF \ {"unpack", (PyCFunction)Struct_unpack, METH_O, Struct_unpack__doc__}, @@ -114,12 +171,10 @@ PyDoc_STRVAR(Struct_unpack_from__doc__, "\n" "Return a tuple containing unpacked values.\n" "\n" -"Values are unpacked according to the format string Struct.format.\n" -"\n" -"The buffer\'s size in bytes, starting at position offset, must be\n" -"at least Struct.size.\n" -"\n" -"See help(struct) for more on format strings."); +"Values are unpacked according to the struct format string. The\n" +"buffer\'s size in bytes, starting at position offset, must be at\n" +"least the struct size. See help(struct) for more on format\n" +"strings."); #define STRUCT_UNPACK_FROM_METHODDEF \ {"unpack_from", _PyCFunction_CAST(Struct_unpack_from), METH_FASTCALL|METH_KEYWORDS, Struct_unpack_from__doc__}, @@ -206,9 +261,8 @@ PyDoc_STRVAR(Struct_iter_unpack__doc__, "Return an iterator yielding tuples.\n" "\n" "Tuples are unpacked from the given bytes source, like a repeated\n" -"invocation of unpack_from().\n" -"\n" -"Requires that the bytes length be a multiple of the struct size."); +"invocation of unpack_from(). Requires that the bytes length be\n" +"a multiple of the struct size."); #define STRUCT_ITER_UNPACK_METHODDEF \ {"iter_unpack", (PyCFunction)Struct_iter_unpack, METH_O, Struct_iter_unpack__doc__}, @@ -226,6 +280,114 @@ Struct_iter_unpack(PyObject *self, PyObject *buffer) return return_value; } +PyDoc_STRVAR(Struct_pack__doc__, +"pack($self, /, *values)\n" +"--\n" +"\n" +"Pack values and return the packed bytes.\n" +"\n" +"Return a bytes object containing the provided values packed\n" +"according to the struct format string. See help(struct) for more on\n" +"format strings."); + +#define STRUCT_PACK_METHODDEF \ + {"pack", _PyCFunction_CAST(Struct_pack), METH_FASTCALL, Struct_pack__doc__}, + +static PyObject * +Struct_pack_impl(PyStructObject *self, PyObject * const *values, + Py_ssize_t values_length); + +static PyObject * +Struct_pack(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject * const *values; + Py_ssize_t values_length; + + values = args; + values_length = nargs; + return_value = Struct_pack_impl((PyStructObject *)self, values, values_length); + + return return_value; +} + +PyDoc_STRVAR(Struct_pack_into__doc__, +"pack_into($self, buffer, offset, /, *values)\n" +"--\n" +"\n" +"Pack values and write the packed bytes into the buffer.\n" +"\n" +"Pack the provided values according to the struct format string\n" +"and write the packed bytes into the writable buffer starting at\n" +"offset. Note that the offset is a required argument. See\n" +"help(struct) for more on format strings."); + +#define STRUCT_PACK_INTO_METHODDEF \ + {"pack_into", _PyCFunction_CAST(Struct_pack_into), METH_FASTCALL, Struct_pack_into__doc__}, + +static PyObject * +Struct_pack_into_impl(PyStructObject *self, Py_buffer *buffer, + Py_ssize_t offset, PyObject * const *values, + Py_ssize_t values_length); + +static PyObject * +Struct_pack_into(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_buffer buffer = {NULL, NULL}; + Py_ssize_t offset; + PyObject * const *values; + Py_ssize_t values_length; + + if (!_PyArg_CheckPositional("pack_into", nargs, 2, PY_SSIZE_T_MAX)) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &buffer, PyBUF_WRITABLE) < 0) { + _PyArg_BadArgument("pack_into", "argument 1", "read-write bytes-like object", args[0]); + goto exit; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + offset = ival; + } + values = args + 2; + values_length = nargs - 2; + return_value = Struct_pack_into_impl((PyStructObject *)self, &buffer, offset, values, values_length); + +exit: + /* Cleanup for buffer */ + if (buffer.obj) { + PyBuffer_Release(&buffer); + } + + return return_value; +} + +PyDoc_STRVAR(Struct___sizeof____doc__, +"__sizeof__($self, /)\n" +"--\n" +"\n"); + +#define STRUCT___SIZEOF___METHODDEF \ + {"__sizeof__", (PyCFunction)Struct___sizeof__, METH_NOARGS, Struct___sizeof____doc__}, + +static PyObject * +Struct___sizeof___impl(PyStructObject *self); + +static PyObject * +Struct___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return Struct___sizeof___impl((PyStructObject *)self); +} + PyDoc_STRVAR(_clearcache__doc__, "_clearcache($module, /)\n" "--\n" @@ -279,15 +441,121 @@ calcsize(PyObject *module, PyObject *arg) return return_value; } +PyDoc_STRVAR(pack__doc__, +"pack($module, format, /, *values)\n" +"--\n" +"\n" +"Pack values and return the packed bytes.\n" +"\n" +"Return a bytes object containing the provided values packed according\n" +"to the format string. See help(struct) for more on format strings."); + +#define PACK_METHODDEF \ + {"pack", _PyCFunction_CAST(pack), METH_FASTCALL, pack__doc__}, + +static PyObject * +pack_impl(PyObject *module, PyStructObject *s_object, + PyObject * const *values, Py_ssize_t values_length); + +static PyObject * +pack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyStructObject *s_object = NULL; + PyObject * const *values; + Py_ssize_t values_length; + + if (!_PyArg_CheckPositional("pack", nargs, 1, PY_SSIZE_T_MAX)) { + goto exit; + } + if (!cache_struct_converter(module, args[0], &s_object)) { + goto exit; + } + values = args + 1; + values_length = nargs - 1; + return_value = pack_impl(module, s_object, values, values_length); + +exit: + /* Cleanup for s_object */ + Py_XDECREF(s_object); + + return return_value; +} + +PyDoc_STRVAR(pack_into__doc__, +"pack_into($module, format, buffer, offset, /, *values)\n" +"--\n" +"\n" +"Pack values and write the packed bytes into the buffer.\n" +"\n" +"Pack the provided values according to the format string and write the\n" +"packed bytes into the writable buffer starting at offset. Note that the\n" +"offset is a required argument. See help(struct) for more on format\n" +"strings."); + +#define PACK_INTO_METHODDEF \ + {"pack_into", _PyCFunction_CAST(pack_into), METH_FASTCALL, pack_into__doc__}, + +static PyObject * +pack_into_impl(PyObject *module, PyStructObject *s_object, Py_buffer *buffer, + Py_ssize_t offset, PyObject * const *values, + Py_ssize_t values_length); + +static PyObject * +pack_into(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyStructObject *s_object = NULL; + Py_buffer buffer = {NULL, NULL}; + Py_ssize_t offset; + PyObject * const *values; + Py_ssize_t values_length; + + if (!_PyArg_CheckPositional("pack_into", nargs, 3, PY_SSIZE_T_MAX)) { + goto exit; + } + if (!cache_struct_converter(module, args[0], &s_object)) { + goto exit; + } + if (PyObject_GetBuffer(args[1], &buffer, PyBUF_WRITABLE) < 0) { + _PyArg_BadArgument("pack_into", "argument 2", "read-write bytes-like object", args[1]); + goto exit; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + offset = ival; + } + values = args + 3; + values_length = nargs - 3; + return_value = pack_into_impl(module, s_object, &buffer, offset, values, values_length); + +exit: + /* Cleanup for s_object */ + Py_XDECREF(s_object); + /* Cleanup for buffer */ + if (buffer.obj) { + PyBuffer_Release(&buffer); + } + + return return_value; +} + PyDoc_STRVAR(unpack__doc__, "unpack($module, format, buffer, /)\n" "--\n" "\n" "Return a tuple containing values unpacked according to the format string.\n" "\n" -"The buffer\'s size in bytes must be calcsize(format).\n" -"\n" -"See help(struct) for more on format strings."); +"The buffer\'s size in bytes must be calcsize(format). See help(struct)\n" +"for more on format strings."); #define UNPACK_METHODDEF \ {"unpack", _PyCFunction_CAST(unpack), METH_FASTCALL, unpack__doc__}, @@ -330,9 +598,8 @@ PyDoc_STRVAR(unpack_from__doc__, "\n" "Return a tuple containing values unpacked according to the format string.\n" "\n" -"The buffer\'s size, minus offset, must be at least calcsize(format).\n" -"\n" -"See help(struct) for more on format strings."); +"The buffer\'s size, minus offset, must be at least calcsize(format). See\n" +"help(struct) for more on format strings."); #define UNPACK_FROM_METHODDEF \ {"unpack_from", _PyCFunction_CAST(unpack_from), METH_FASTCALL|METH_KEYWORDS, unpack_from__doc__}, @@ -424,10 +691,9 @@ PyDoc_STRVAR(iter_unpack__doc__, "\n" "Return an iterator yielding tuples unpacked from the given bytes.\n" "\n" -"The bytes are unpacked according to the format string, like\n" -"a repeated invocation of unpack_from().\n" -"\n" -"Requires that the bytes length be a multiple of the format struct size."); +"The bytes are unpacked according to the format string, like a repeated\n" +"invocation of unpack_from(). Requires that the bytes length be\n" +"a multiple of calcsize(format)."); #define ITER_UNPACK_METHODDEF \ {"iter_unpack", _PyCFunction_CAST(iter_unpack), METH_FASTCALL, iter_unpack__doc__}, @@ -458,4 +724,4 @@ iter_unpack(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -/*[clinic end generated code: output=caa7f36443e91cb9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0f417d43a2a387c8 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_suggestions.c.h b/Modules/clinic/_suggestions.c.h index 51484b13d5af89..3b3ed5056ac8d0 100644 --- a/Modules/clinic/_suggestions.c.h +++ b/Modules/clinic/_suggestions.c.h @@ -2,6 +2,7 @@ preserve [clinic start generated code]*/ +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(_suggestions__generate_suggestions__doc__, @@ -33,9 +34,11 @@ _suggestions__generate_suggestions(PyObject *module, PyObject *const *args, Py_s goto exit; } item = args[1]; + Py_BEGIN_CRITICAL_SECTION(candidates); return_value = _suggestions__generate_suggestions_impl(module, candidates, item); + Py_END_CRITICAL_SECTION(); exit: return return_value; } -/*[clinic end generated code: output=1d8e963cdae30b13 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1690dd15a464d19c input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_sysconfig.c.h b/Modules/clinic/_sysconfig.c.h index eb3d396298bb21..031a738cf47c7b 100644 --- a/Modules/clinic/_sysconfig.c.h +++ b/Modules/clinic/_sysconfig.c.h @@ -19,4 +19,30 @@ _sysconfig_config_vars(PyObject *module, PyObject *Py_UNUSED(ignored)) { return _sysconfig_config_vars_impl(module); } -/*[clinic end generated code: output=25d395cf02eced1f input=a9049054013a1b77]*/ + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(_sysconfig_get_platform__doc__, +"get_platform($module, /)\n" +"--\n" +"\n" +"Return a string that identifies the current platform."); + +#define _SYSCONFIG_GET_PLATFORM_METHODDEF \ + {"get_platform", (PyCFunction)_sysconfig_get_platform, METH_NOARGS, _sysconfig_get_platform__doc__}, + +static PyObject * +_sysconfig_get_platform_impl(PyObject *module); + +static PyObject * +_sysconfig_get_platform(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return _sysconfig_get_platform_impl(module); +} + +#endif /* defined(MS_WINDOWS) */ + +#ifndef _SYSCONFIG_GET_PLATFORM_METHODDEF + #define _SYSCONFIG_GET_PLATFORM_METHODDEF +#endif /* !defined(_SYSCONFIG_GET_PLATFORM_METHODDEF) */ +/*[clinic end generated code: output=558531e148f92320 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 68c92a86226bc9..05615c1fdd81b9 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -9,7 +9,7 @@ preserve #include "pycore_long.h" // _PyLong_UnsignedShort_Converter() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_runtime.h" // _Py_ID() -#include "pycore_tuple.h" // _PyTuple_FromArray() +#include "pycore_tuple.h" // _PyTuple_ITEMS() PyDoc_STRVAR(test_empty_function__doc__, "test_empty_function($module, /)\n" @@ -273,19 +273,19 @@ char_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; char a = 'A'; - char b = '\x07'; - char c = '\x08'; + char b = '\a'; + char c = '\b'; char d = '\t'; char e = '\n'; - char f = '\x0b'; - char g = '\x0c'; + char f = '\v'; + char g = '\f'; char h = '\r'; char i = '"'; char j = '\''; char k = '?'; char l = '\\'; - char m = '\x00'; - char n = '\xff'; + char m = '\0'; + char n = '\377'; if (!_PyArg_CheckPositional("char_converter", nargs, 0, 14)) { goto exit; @@ -879,7 +879,7 @@ unsigned_short_converter(PyObject *module, PyObject *const *args, Py_ssize_t nar } PyDoc_STRVAR(int_converter__doc__, -"int_converter($module, a=12, b=34, c=45, /)\n" +"int_converter($module, a=12, b=34, c=\'-\', /)\n" "--\n" "\n"); @@ -895,7 +895,7 @@ int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; int a = 12; int b = 34; - int c = 45; + int c = '-'; if (!_PyArg_CheckPositional("int_converter", nargs, 0, 3)) { goto exit; @@ -1196,7 +1196,8 @@ unsigned_long_long_converter(PyObject *module, PyObject *const *args, Py_ssize_t } PyDoc_STRVAR(py_ssize_t_converter__doc__, -"py_ssize_t_converter($module, a=12, b=34, c=56, /)\n" +"py_ssize_t_converter($module, a=12, b=34, c=56, d=78, e=90, f=-12,\n" +" g=-34, /)\n" "--\n" "\n"); @@ -1205,7 +1206,8 @@ PyDoc_STRVAR(py_ssize_t_converter__doc__, static PyObject * py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b, - Py_ssize_t c); + Py_ssize_t c, Py_ssize_t d, Py_ssize_t e, + Py_ssize_t f, Py_ssize_t g); static PyObject * py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) @@ -1214,8 +1216,12 @@ py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) Py_ssize_t a = 12; Py_ssize_t b = 34; Py_ssize_t c = 56; + Py_ssize_t d = 78; + Py_ssize_t e = 90; + Py_ssize_t f = -12; + Py_ssize_t g = -34; - if (!_PyArg_CheckPositional("py_ssize_t_converter", nargs, 0, 3)) { + if (!_PyArg_CheckPositional("py_ssize_t_converter", nargs, 0, 7)) { goto exit; } if (nargs < 1) { @@ -1254,8 +1260,60 @@ py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_Py_convert_optional_to_ssize_t(args[2], &c)) { goto exit; } + if (nargs < 4) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[3]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + d = ival; + if (d < 0) { + PyErr_SetString(PyExc_ValueError, + "d cannot be negative"); + goto exit; + } + } + if (nargs < 5) { + goto skip_optional; + } + if (!_Py_convert_optional_to_non_negative_ssize_t(args[4], &e)) { + goto exit; + } + if (nargs < 6) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[5]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + f = ival; + if (f < 0) { + PyErr_SetString(PyExc_ValueError, + "f cannot be negative"); + goto exit; + } + } + if (nargs < 7) { + goto skip_optional; + } + if (!_Py_convert_optional_to_non_negative_ssize_t(args[6], &g)) { + goto exit; + } skip_optional: - return_value = py_ssize_t_converter_impl(module, a, b, c); + return_value = py_ssize_t_converter_impl(module, a, b, c, d, e, f, g); exit: return return_value; @@ -2706,7 +2764,7 @@ varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *__clinic_args = NULL; - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -2744,7 +2802,7 @@ posonly_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs) } a = args[0]; b = args[1]; - __clinic_args = _PyTuple_FromArray(args + 2, nargs - 2); + __clinic_args = PyTuple_FromArray(args + 2, nargs - 2); if (__clinic_args == NULL) { goto exit; } @@ -2787,7 +2845,7 @@ posonly_req_opt_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs b = args[1]; skip_optional: __clinic_args = nargs > 2 - ? _PyTuple_FromArray(args + 2, nargs - 2) + ? PyTuple_FromArray(args + 2, nargs - 2) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -2858,7 +2916,7 @@ posonly_poskw_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs, a = fastargs[0]; b = fastargs[1]; __clinic_args = nargs > 2 - ? _PyTuple_FromArray(args + 2, nargs - 2) + ? PyTuple_FromArray(args + 2, nargs - 2) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -2926,7 +2984,7 @@ poskw_varpos(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject } a = fastargs[0]; __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -3005,7 +3063,7 @@ poskw_varpos_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t narg } skip_optional_kwonly: __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -3088,7 +3146,7 @@ poskw_varpos_kwonly_opt2(PyObject *module, PyObject *const *args, Py_ssize_t nar c = fastargs[2]; skip_optional_kwonly: __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -3160,7 +3218,7 @@ varpos_kwonly_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO } b = fastargs[0]; skip_optional_kwonly: - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -3241,7 +3299,7 @@ varpos_kwonly_req_opt(PyObject *module, PyObject *const *args, Py_ssize_t nargs, } c = fastargs[2]; skip_optional_kwonly: - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -3491,7 +3549,7 @@ gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject kw2 = fastargs[3]; skip_optional_kwonly: varargs = nargs > 2 - ? _PyTuple_FromArray(args + 2, nargs - 2) + ? PyTuple_FromArray(args + 2, nargs - 2) : PyTuple_New(0); if (varargs == NULL) { goto exit; @@ -3568,7 +3626,7 @@ gh_32092_kw_pass(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb kw = fastargs[1]; skip_optional_kwonly: __clinic_args = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (__clinic_args == NULL) { goto exit; @@ -3600,7 +3658,7 @@ gh_99233_refcount(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *__clinic_args = NULL; - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -3713,7 +3771,7 @@ null_or_tuple_for_varargs(PyObject *module, PyObject *const *args, Py_ssize_t na } skip_optional_kwonly: constraints = nargs > 1 - ? _PyTuple_FromArray(args + 1, nargs - 1) + ? PyTuple_FromArray(args + 1, nargs - 1) : PyTuple_New(0); if (constraints == NULL) { goto exit; @@ -4116,7 +4174,7 @@ _testclinic_TestClass_defclass_varpos(PyObject *self, PyTypeObject *cls, PyObjec if (!fastargs) { goto exit; } - __clinic_args = _PyTuple_FromArray(args, nargs); + __clinic_args = PyTuple_FromArray(args, nargs); if (__clinic_args == NULL) { goto exit; } @@ -4173,7 +4231,7 @@ _testclinic_TestClass_defclass_posonly_varpos(PyObject *self, PyTypeObject *cls, } a = fastargs[0]; b = fastargs[1]; - __clinic_args = _PyTuple_FromArray(args + 2, nargs - 2); + __clinic_args = PyTuple_FromArray(args + 2, nargs - 2); if (__clinic_args == NULL) { goto exit; } @@ -4542,4 +4600,4 @@ _testclinic_TestClass_posonly_poskw_varpos_array_no_fastcall(PyObject *type, PyO exit: return return_value; } -/*[clinic end generated code: output=6b04671afdafbecf input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9971dbbc5f62b8d2 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testclinic_depr.c.h b/Modules/clinic/_testclinic_depr.c.h index a46d238801b321..e2db4fd87ed26b 100644 --- a/Modules/clinic/_testclinic_depr.c.h +++ b/Modules/clinic/_testclinic_depr.c.h @@ -9,7 +9,7 @@ preserve #include "pycore_long.h" // _PyLong_UnsignedShort_Converter() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_runtime.h" // _Py_ID() -#include "pycore_tuple.h" // _PyTuple_FromArray() +#include "pycore_tuple.h" // _PyTuple_ITEMS() PyDoc_STRVAR(depr_star_new__doc__, "DeprStarNew(a=None)\n" @@ -2474,4 +2474,4 @@ depr_multi(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject * exit: return return_value; } -/*[clinic end generated code: output=4e60af44fd6b7b94 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2231bec0ed196830 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testclinic_kwds.c.h b/Modules/clinic/_testclinic_kwds.c.h new file mode 100644 index 00000000000000..86cad50c56cf55 --- /dev/null +++ b/Modules/clinic/_testclinic_kwds.c.h @@ -0,0 +1,184 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +#endif +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_long.h" // _PyLong_UnsignedShort_Converter() +#include "pycore_modsupport.h" // _PyArg_CheckPositional() +#include "pycore_runtime.h" // _Py_ID() +#include "pycore_tuple.h" // _PyTuple_ITEMS() + +PyDoc_STRVAR(lone_kwds__doc__, +"lone_kwds($module, /, **kwds)\n" +"--\n" +"\n"); + +#define LONE_KWDS_METHODDEF \ + {"lone_kwds", _PyCFunction_CAST(lone_kwds), METH_VARARGS|METH_KEYWORDS, lone_kwds__doc__}, + +static PyObject * +lone_kwds_impl(PyObject *module, PyObject *kwds); + +static PyObject * +lone_kwds(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyObject *__clinic_kwds = NULL; + + if (!_PyArg_NoPositional("lone_kwds", args)) { + goto exit; + } + if (kwargs == NULL) { + __clinic_kwds = PyDict_New(); + if (__clinic_kwds == NULL) { + goto exit; + } + } + else { + __clinic_kwds = Py_NewRef(kwargs); + } + return_value = lone_kwds_impl(module, __clinic_kwds); + +exit: + /* Cleanup for kwds */ + Py_XDECREF(__clinic_kwds); + + return return_value; +} + +PyDoc_STRVAR(kwds_with_pos_only__doc__, +"kwds_with_pos_only($module, a, b, /, **kwds)\n" +"--\n" +"\n"); + +#define KWDS_WITH_POS_ONLY_METHODDEF \ + {"kwds_with_pos_only", _PyCFunction_CAST(kwds_with_pos_only), METH_VARARGS|METH_KEYWORDS, kwds_with_pos_only__doc__}, + +static PyObject * +kwds_with_pos_only_impl(PyObject *module, PyObject *a, PyObject *b, + PyObject *kwds); + +static PyObject * +kwds_with_pos_only(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + PyObject *__clinic_kwds = NULL; + + if (!_PyArg_CheckPositional("kwds_with_pos_only", PyTuple_GET_SIZE(args), 2, 2)) { + goto exit; + } + a = PyTuple_GET_ITEM(args, 0); + b = PyTuple_GET_ITEM(args, 1); + if (kwargs == NULL) { + __clinic_kwds = PyDict_New(); + if (__clinic_kwds == NULL) { + goto exit; + } + } + else { + __clinic_kwds = Py_NewRef(kwargs); + } + return_value = kwds_with_pos_only_impl(module, a, b, __clinic_kwds); + +exit: + /* Cleanup for kwds */ + Py_XDECREF(__clinic_kwds); + + return return_value; +} + +PyDoc_STRVAR(kwds_with_stararg__doc__, +"kwds_with_stararg($module, /, *args, **kwds)\n" +"--\n" +"\n"); + +#define KWDS_WITH_STARARG_METHODDEF \ + {"kwds_with_stararg", _PyCFunction_CAST(kwds_with_stararg), METH_VARARGS|METH_KEYWORDS, kwds_with_stararg__doc__}, + +static PyObject * +kwds_with_stararg_impl(PyObject *module, PyObject *args, PyObject *kwds); + +static PyObject * +kwds_with_stararg(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyObject *__clinic_args = NULL; + PyObject *__clinic_kwds = NULL; + + __clinic_args = Py_NewRef(args); + if (kwargs == NULL) { + __clinic_kwds = PyDict_New(); + if (__clinic_kwds == NULL) { + goto exit; + } + } + else { + __clinic_kwds = Py_NewRef(kwargs); + } + return_value = kwds_with_stararg_impl(module, __clinic_args, __clinic_kwds); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + /* Cleanup for kwds */ + Py_XDECREF(__clinic_kwds); + + return return_value; +} + +PyDoc_STRVAR(kwds_with_pos_only_and_stararg__doc__, +"kwds_with_pos_only_and_stararg($module, a, b, /, *args, **kwds)\n" +"--\n" +"\n"); + +#define KWDS_WITH_POS_ONLY_AND_STARARG_METHODDEF \ + {"kwds_with_pos_only_and_stararg", _PyCFunction_CAST(kwds_with_pos_only_and_stararg), METH_VARARGS|METH_KEYWORDS, kwds_with_pos_only_and_stararg__doc__}, + +static PyObject * +kwds_with_pos_only_and_stararg_impl(PyObject *module, PyObject *a, + PyObject *b, PyObject *args, + PyObject *kwds); + +static PyObject * +kwds_with_pos_only_and_stararg(PyObject *module, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + PyObject *__clinic_args = NULL; + PyObject *__clinic_kwds = NULL; + + if (!_PyArg_CheckPositional("kwds_with_pos_only_and_stararg", PyTuple_GET_SIZE(args), 2, PY_SSIZE_T_MAX)) { + goto exit; + } + a = PyTuple_GET_ITEM(args, 0); + b = PyTuple_GET_ITEM(args, 1); + __clinic_args = PyTuple_GetSlice(args, 2, PY_SSIZE_T_MAX); + if (!__clinic_args) { + goto exit; + } + if (kwargs == NULL) { + __clinic_kwds = PyDict_New(); + if (__clinic_kwds == NULL) { + goto exit; + } + } + else { + __clinic_kwds = Py_NewRef(kwargs); + } + return_value = kwds_with_pos_only_and_stararg_impl(module, a, b, __clinic_args, __clinic_kwds); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + /* Cleanup for kwds */ + Py_XDECREF(__clinic_kwds); + + return return_value; +} +/*[clinic end generated code: output=3e5251b10aa44382 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testinternalcapi.c.h b/Modules/clinic/_testinternalcapi.c.h index 21f4ee3201e5bf..85edc6fbb5802f 100644 --- a/Modules/clinic/_testinternalcapi.c.h +++ b/Modules/clinic/_testinternalcapi.c.h @@ -92,7 +92,11 @@ PyDoc_STRVAR(_testinternalcapi_compiler_codegen__doc__, "compiler_codegen($module, /, ast, filename, optimize, compile_mode=0)\n" "--\n" "\n" -"Apply compiler code generation to an AST."); +"Apply compiler code generation to an AST.\n" +"\n" +"Return (instruction_sequence, metadata). metadata maps \"argcount\",\n" +"\"posonlyargcount\", \"kwonlyargcount\" to ints and \"consts\" to the list of\n" +"constants in LOAD_CONST index order (for use with optimize_cfg)."); #define _TESTINTERNALCAPI_COMPILER_CODEGEN_METHODDEF \ {"compiler_codegen", _PyCFunction_CAST(_testinternalcapi_compiler_codegen), METH_FASTCALL|METH_KEYWORDS, _testinternalcapi_compiler_codegen__doc__}, @@ -169,7 +173,10 @@ PyDoc_STRVAR(_testinternalcapi_optimize_cfg__doc__, "optimize_cfg($module, /, instructions, consts, nlocals)\n" "--\n" "\n" -"Apply compiler optimizations to an instruction list."); +"Apply compiler optimizations to an instruction list.\n" +"\n" +"consts must be a list aligned with LOAD_CONST opargs (the \"consts\" entry\n" +"from the metadata dict returned by compiler_codegen for the same unit)."); #define _TESTINTERNALCAPI_OPTIMIZE_CFG_METHODDEF \ {"optimize_cfg", _PyCFunction_CAST(_testinternalcapi_optimize_cfg), METH_FASTCALL|METH_KEYWORDS, _testinternalcapi_optimize_cfg__doc__}, @@ -392,4 +399,4 @@ get_next_dict_keys_version(PyObject *module, PyObject *Py_UNUSED(ignored)) { return get_next_dict_keys_version_impl(module); } -/*[clinic end generated code: output=fbd8b7e0cae8bac7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ecb5d7ac85b153fa input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_testmultiphase.c.h b/Modules/clinic/_testmultiphase.c.h index 311b6409476711..a0e4c66451101e 100644 --- a/Modules/clinic/_testmultiphase.c.h +++ b/Modules/clinic/_testmultiphase.c.h @@ -14,8 +14,8 @@ PyDoc_STRVAR(_testmultiphase_StateAccessType_get_defining_module__doc__, "\n" "Return the module of the defining class.\n" "\n" -"Also tests that result of PyType_GetModuleByDef matches defining_class\'s\n" -"module."); +"Also tests that result of PyType_GetModuleByDef matches\n" +"defining_class\'s module."); #define _TESTMULTIPHASE_STATEACCESSTYPE_GET_DEFINING_MODULE_METHODDEF \ {"get_defining_module", _PyCFunction_CAST(_testmultiphase_StateAccessType_get_defining_module), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _testmultiphase_StateAccessType_get_defining_module__doc__}, @@ -165,4 +165,4 @@ _testmultiphase_StateAccessType_get_count(PyObject *self, PyTypeObject *cls, PyO } return _testmultiphase_StateAccessType_get_count_impl((StateAccessTypeObject *)self, cls); } -/*[clinic end generated code: output=8eed2f14292ec986 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=aff91f6219a7baca input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_threadmodule.c.h b/Modules/clinic/_threadmodule.c.h index fd8e32a2261d38..926bea8e1e419a 100644 --- a/Modules/clinic/_threadmodule.c.h +++ b/Modules/clinic/_threadmodule.c.h @@ -6,7 +6,310 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif -#include "pycore_modsupport.h" // _PyArg_NoKeywords() +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +PyDoc_STRVAR(_thread_lock_acquire__doc__, +"acquire($self, /, blocking=True, timeout=-1)\n" +"--\n" +"\n" +"Lock the lock.\n" +"\n" +"Without argument, this blocks if the lock is already locked\n" +"(even by the same thread), waiting for another thread to release\n" +"the lock, and return True once the lock is acquired.\n" +"With an argument, this will only block if the argument is true,\n" +"and the return value reflects whether the lock is acquired.\n" +"The blocking operation is interruptible."); + +#define _THREAD_LOCK_ACQUIRE_METHODDEF \ + {"acquire", _PyCFunction_CAST(_thread_lock_acquire), METH_FASTCALL|METH_KEYWORDS, _thread_lock_acquire__doc__}, + +static PyObject * +_thread_lock_acquire_impl(lockobject *self, int blocking, + PyObject *timeoutobj); + +static PyObject * +_thread_lock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(blocking), &_Py_ID(timeout), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"blocking", "timeout", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "acquire", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int blocking = 1; + PyObject *timeoutobj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + blocking = PyObject_IsTrue(args[0]); + if (blocking < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + timeoutobj = args[1]; +skip_optional_pos: + return_value = _thread_lock_acquire_impl((lockobject *)self, blocking, timeoutobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_lock_acquire_lock__doc__, +"acquire_lock($self, /, blocking=True, timeout=-1)\n" +"--\n" +"\n" +"An obsolete synonym of acquire()."); + +#define _THREAD_LOCK_ACQUIRE_LOCK_METHODDEF \ + {"acquire_lock", _PyCFunction_CAST(_thread_lock_acquire_lock), METH_FASTCALL|METH_KEYWORDS, _thread_lock_acquire_lock__doc__}, + +static PyObject * +_thread_lock_acquire_lock_impl(lockobject *self, int blocking, + PyObject *timeoutobj); + +static PyObject * +_thread_lock_acquire_lock(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(blocking), &_Py_ID(timeout), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"blocking", "timeout", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "acquire_lock", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int blocking = 1; + PyObject *timeoutobj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + blocking = PyObject_IsTrue(args[0]); + if (blocking < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + timeoutobj = args[1]; +skip_optional_pos: + return_value = _thread_lock_acquire_lock_impl((lockobject *)self, blocking, timeoutobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_lock_release__doc__, +"release($self, /)\n" +"--\n" +"\n" +"Release the lock.\n" +"\n" +"Allows another thread that is blocked waiting for\n" +"the lock to acquire the lock. The lock must be in the locked state,\n" +"but it needn\'t be locked by the same thread that unlocks it."); + +#define _THREAD_LOCK_RELEASE_METHODDEF \ + {"release", (PyCFunction)_thread_lock_release, METH_NOARGS, _thread_lock_release__doc__}, + +static PyObject * +_thread_lock_release_impl(lockobject *self); + +static PyObject * +_thread_lock_release(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_release_impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock_release_lock__doc__, +"release_lock($self, /)\n" +"--\n" +"\n" +"An obsolete synonym of release()."); + +#define _THREAD_LOCK_RELEASE_LOCK_METHODDEF \ + {"release_lock", (PyCFunction)_thread_lock_release_lock, METH_NOARGS, _thread_lock_release_lock__doc__}, + +static PyObject * +_thread_lock_release_lock_impl(lockobject *self); + +static PyObject * +_thread_lock_release_lock(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_release_lock_impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock___enter____doc__, +"__enter__($self, /)\n" +"--\n" +"\n" +"Lock the lock."); + +#define _THREAD_LOCK___ENTER___METHODDEF \ + {"__enter__", (PyCFunction)_thread_lock___enter__, METH_NOARGS, _thread_lock___enter____doc__}, + +static PyObject * +_thread_lock___enter___impl(lockobject *self); + +static PyObject * +_thread_lock___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock___enter___impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock___exit____doc__, +"__exit__($self, exc_type, exc_value, exc_tb, /)\n" +"--\n" +"\n" +"Release the lock."); + +#define _THREAD_LOCK___EXIT___METHODDEF \ + {"__exit__", _PyCFunction_CAST(_thread_lock___exit__), METH_FASTCALL, _thread_lock___exit____doc__}, + +static PyObject * +_thread_lock___exit___impl(lockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb); + +static PyObject * +_thread_lock___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *exc_type; + PyObject *exc_value; + PyObject *exc_tb; + + if (!_PyArg_CheckPositional("__exit__", nargs, 3, 3)) { + goto exit; + } + exc_type = args[0]; + exc_value = args[1]; + exc_tb = args[2]; + return_value = _thread_lock___exit___impl((lockobject *)self, exc_type, exc_value, exc_tb); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_lock_locked__doc__, +"locked($self, /)\n" +"--\n" +"\n" +"Return whether the lock is in the locked state."); + +#define _THREAD_LOCK_LOCKED_METHODDEF \ + {"locked", (PyCFunction)_thread_lock_locked, METH_NOARGS, _thread_lock_locked__doc__}, + +static PyObject * +_thread_lock_locked_impl(lockobject *self); + +static PyObject * +_thread_lock_locked(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_locked_impl((lockobject *)self); +} + +PyDoc_STRVAR(_thread_lock_locked_lock__doc__, +"locked_lock($self, /)\n" +"--\n" +"\n" +"An obsolete synonym of locked()."); + +#define _THREAD_LOCK_LOCKED_LOCK_METHODDEF \ + {"locked_lock", (PyCFunction)_thread_lock_locked_lock, METH_NOARGS, _thread_lock_locked_lock__doc__}, + +static PyObject * +_thread_lock_locked_lock_impl(lockobject *self); + +static PyObject * +_thread_lock_locked_lock(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock_locked_lock_impl((lockobject *)self); +} + +#if defined(HAVE_FORK) + +PyDoc_STRVAR(_thread_lock__at_fork_reinit__doc__, +"_at_fork_reinit($self, /)\n" +"--\n" +"\n"); + +#define _THREAD_LOCK__AT_FORK_REINIT_METHODDEF \ + {"_at_fork_reinit", (PyCFunction)_thread_lock__at_fork_reinit, METH_NOARGS, _thread_lock__at_fork_reinit__doc__}, + +static PyObject * +_thread_lock__at_fork_reinit_impl(lockobject *self); + +static PyObject * +_thread_lock__at_fork_reinit(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_lock__at_fork_reinit_impl((lockobject *)self); +} + +#endif /* defined(HAVE_FORK) */ static PyObject * lock_new_impl(PyTypeObject *type); @@ -31,6 +334,265 @@ lock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return return_value; } +PyDoc_STRVAR(_thread_RLock_acquire__doc__, +"acquire($self, /, blocking=True, timeout=-1)\n" +"--\n" +"\n" +"Lock the lock.\n" +"\n" +"`blocking` indicates whether we should wait\n" +"for the lock to be available or not. If `blocking` is False\n" +"and another thread holds the lock, the method will return False\n" +"immediately. If `blocking` is True and another thread holds\n" +"the lock, the method will wait for the lock to be released,\n" +"take it and then return True.\n" +"(note: the blocking operation is interruptible.)\n" +"\n" +"In all other cases, the method will return True immediately.\n" +"Precisely, if the current thread already holds the lock, its\n" +"internal counter is simply incremented. If nobody holds the lock,\n" +"the lock is taken and its internal counter initialized to 1."); + +#define _THREAD_RLOCK_ACQUIRE_METHODDEF \ + {"acquire", _PyCFunction_CAST(_thread_RLock_acquire), METH_FASTCALL|METH_KEYWORDS, _thread_RLock_acquire__doc__}, + +static PyObject * +_thread_RLock_acquire_impl(rlockobject *self, int blocking, + PyObject *timeoutobj); + +static PyObject * +_thread_RLock_acquire(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(blocking), &_Py_ID(timeout), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"blocking", "timeout", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "acquire", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int blocking = 1; + PyObject *timeoutobj = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + blocking = PyObject_IsTrue(args[0]); + if (blocking < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + timeoutobj = args[1]; +skip_optional_pos: + return_value = _thread_RLock_acquire_impl((rlockobject *)self, blocking, timeoutobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_RLock___enter____doc__, +"__enter__($self, /)\n" +"--\n" +"\n" +"Lock the lock."); + +#define _THREAD_RLOCK___ENTER___METHODDEF \ + {"__enter__", (PyCFunction)_thread_RLock___enter__, METH_NOARGS, _thread_RLock___enter____doc__}, + +static PyObject * +_thread_RLock___enter___impl(rlockobject *self); + +static PyObject * +_thread_RLock___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock___enter___impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock_release__doc__, +"release($self, /)\n" +"--\n" +"\n" +"Release the lock.\n" +"\n" +"Allows another thread that is blocked waiting for the lock\n" +"to acquire the lock. The lock must be in the locked state,\n" +"and must be locked by the same thread that unlocks it; otherwise a\n" +"`RuntimeError` is raised.\n" +"\n" +"Do note that if the lock was acquire()d several times in a row by\n" +"the current thread, release() needs to be called as many times for\n" +"the lock to be available for other threads."); + +#define _THREAD_RLOCK_RELEASE_METHODDEF \ + {"release", (PyCFunction)_thread_RLock_release, METH_NOARGS, _thread_RLock_release__doc__}, + +static PyObject * +_thread_RLock_release_impl(rlockobject *self); + +static PyObject * +_thread_RLock_release(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock_release_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock___exit____doc__, +"__exit__($self, exc_type, exc_value, exc_tb, /)\n" +"--\n" +"\n" +"Release the lock."); + +#define _THREAD_RLOCK___EXIT___METHODDEF \ + {"__exit__", _PyCFunction_CAST(_thread_RLock___exit__), METH_FASTCALL, _thread_RLock___exit____doc__}, + +static PyObject * +_thread_RLock___exit___impl(rlockobject *self, PyObject *exc_type, + PyObject *exc_value, PyObject *exc_tb); + +static PyObject * +_thread_RLock___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *exc_type; + PyObject *exc_value; + PyObject *exc_tb; + + if (!_PyArg_CheckPositional("__exit__", nargs, 3, 3)) { + goto exit; + } + exc_type = args[0]; + exc_value = args[1]; + exc_tb = args[2]; + return_value = _thread_RLock___exit___impl((rlockobject *)self, exc_type, exc_value, exc_tb); + +exit: + return return_value; +} + +PyDoc_STRVAR(_thread_RLock_locked__doc__, +"locked($self, /)\n" +"--\n" +"\n" +"Return a boolean indicating whether this object is locked right now."); + +#define _THREAD_RLOCK_LOCKED_METHODDEF \ + {"locked", (PyCFunction)_thread_RLock_locked, METH_NOARGS, _thread_RLock_locked__doc__}, + +static PyObject * +_thread_RLock_locked_impl(rlockobject *self); + +static PyObject * +_thread_RLock_locked(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock_locked_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock__acquire_restore__doc__, +"_acquire_restore($self, state, /)\n" +"--\n" +"\n" +"For internal use by `threading.Condition`."); + +#define _THREAD_RLOCK__ACQUIRE_RESTORE_METHODDEF \ + {"_acquire_restore", (PyCFunction)_thread_RLock__acquire_restore, METH_O, _thread_RLock__acquire_restore__doc__}, + +static PyObject * +_thread_RLock__acquire_restore_impl(rlockobject *self, PyObject *state); + +static PyObject * +_thread_RLock__acquire_restore(PyObject *self, PyObject *state) +{ + PyObject *return_value = NULL; + + return_value = _thread_RLock__acquire_restore_impl((rlockobject *)self, state); + + return return_value; +} + +PyDoc_STRVAR(_thread_RLock__release_save__doc__, +"_release_save($self, /)\n" +"--\n" +"\n" +"For internal use by `threading.Condition`."); + +#define _THREAD_RLOCK__RELEASE_SAVE_METHODDEF \ + {"_release_save", (PyCFunction)_thread_RLock__release_save, METH_NOARGS, _thread_RLock__release_save__doc__}, + +static PyObject * +_thread_RLock__release_save_impl(rlockobject *self); + +static PyObject * +_thread_RLock__release_save(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__release_save_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock__recursion_count__doc__, +"_recursion_count($self, /)\n" +"--\n" +"\n" +"For internal use by reentrancy checks."); + +#define _THREAD_RLOCK__RECURSION_COUNT_METHODDEF \ + {"_recursion_count", (PyCFunction)_thread_RLock__recursion_count, METH_NOARGS, _thread_RLock__recursion_count__doc__}, + +static PyObject * +_thread_RLock__recursion_count_impl(rlockobject *self); + +static PyObject * +_thread_RLock__recursion_count(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__recursion_count_impl((rlockobject *)self); +} + +PyDoc_STRVAR(_thread_RLock__is_owned__doc__, +"_is_owned($self, /)\n" +"--\n" +"\n" +"For internal use by `threading.Condition`."); + +#define _THREAD_RLOCK__IS_OWNED_METHODDEF \ + {"_is_owned", (PyCFunction)_thread_RLock__is_owned, METH_NOARGS, _thread_RLock__is_owned__doc__}, + +static PyObject * +_thread_RLock__is_owned_impl(rlockobject *self); + +static PyObject * +_thread_RLock__is_owned(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__is_owned_impl((rlockobject *)self); +} + static PyObject * rlock_new_impl(PyTypeObject *type); @@ -54,6 +616,27 @@ rlock_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return return_value; } +#if defined(HAVE_FORK) + +PyDoc_STRVAR(_thread_RLock__at_fork_reinit__doc__, +"_at_fork_reinit($self, /)\n" +"--\n" +"\n"); + +#define _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF \ + {"_at_fork_reinit", (PyCFunction)_thread_RLock__at_fork_reinit, METH_NOARGS, _thread_RLock__at_fork_reinit__doc__}, + +static PyObject * +_thread_RLock__at_fork_reinit_impl(rlockobject *self); + +static PyObject * +_thread_RLock__at_fork_reinit(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _thread_RLock__at_fork_reinit_impl((rlockobject *)self); +} + +#endif /* defined(HAVE_FORK) */ + #if (defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP) || defined(MS_WINDOWS)) PyDoc_STRVAR(_thread__get_name__doc__, @@ -142,6 +725,14 @@ _thread_set_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #endif /* (defined(HAVE_PTHREAD_SETNAME_NP) || defined(HAVE_PTHREAD_SET_NAME_NP) || defined(MS_WINDOWS)) */ +#ifndef _THREAD_LOCK__AT_FORK_REINIT_METHODDEF + #define _THREAD_LOCK__AT_FORK_REINIT_METHODDEF +#endif /* !defined(_THREAD_LOCK__AT_FORK_REINIT_METHODDEF) */ + +#ifndef _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF + #define _THREAD_RLOCK__AT_FORK_REINIT_METHODDEF +#endif /* !defined(_THREAD_RLOCK__AT_FORK_REINIT_METHODDEF) */ + #ifndef _THREAD__GET_NAME_METHODDEF #define _THREAD__GET_NAME_METHODDEF #endif /* !defined(_THREAD__GET_NAME_METHODDEF) */ @@ -149,4 +740,4 @@ _thread_set_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #ifndef _THREAD_SET_NAME_METHODDEF #define _THREAD_SET_NAME_METHODDEF #endif /* !defined(_THREAD_SET_NAME_METHODDEF) */ -/*[clinic end generated code: output=b381ec5e313198e7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0f1707cbafc0e8f2 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_tkinter.c.h b/Modules/clinic/_tkinter.c.h index 352c2b9e3d410c..3147cf7f9d2cff 100644 --- a/Modules/clinic/_tkinter.c.h +++ b/Modules/clinic/_tkinter.c.h @@ -907,7 +907,8 @@ PyDoc_STRVAR(_tkinter_setbusywaitinterval__doc__, "\n" "Set the busy-wait interval in milliseconds between successive calls to Tcl_DoOneEvent in a threaded Python interpreter.\n" "\n" -"It should be set to a divisor of the maximum time between frames in an animation."); +"It should be set to a divisor of the maximum time between frames in\n" +"an animation."); #define _TKINTER_SETBUSYWAITINTERVAL_METHODDEF \ {"setbusywaitinterval", (PyCFunction)_tkinter_setbusywaitinterval, METH_O, _tkinter_setbusywaitinterval__doc__}, @@ -966,4 +967,4 @@ _tkinter_getbusywaitinterval(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef _TKINTER_TKAPP_DELETEFILEHANDLER_METHODDEF #define _TKINTER_TKAPP_DELETEFILEHANDLER_METHODDEF #endif /* !defined(_TKINTER_TKAPP_DELETEFILEHANDLER_METHODDEF) */ -/*[clinic end generated code: output=052c067aa69237be input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c807adb73e305725 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_winapi.c.h b/Modules/clinic/_winapi.c.h index bd685e75d9344f..dd9dbffaa9ac23 100644 --- a/Modules/clinic/_winapi.c.h +++ b/Modules/clinic/_winapi.c.h @@ -2184,7 +2184,181 @@ _winapi_CopyFile2(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO return return_value; } +PyDoc_STRVAR(_winapi_RegisterEventSource__doc__, +"RegisterEventSource($module, unc_server_name, source_name, /)\n" +"--\n" +"\n" +"Retrieves a registered handle to the specified event log.\n" +"\n" +" unc_server_name\n" +" The UNC name of the server on which the event source should be registered.\n" +" If None, registers the event source on the local computer.\n" +" source_name\n" +" The name of the event source to register."); + +#define _WINAPI_REGISTEREVENTSOURCE_METHODDEF \ + {"RegisterEventSource", _PyCFunction_CAST(_winapi_RegisterEventSource), METH_FASTCALL, _winapi_RegisterEventSource__doc__}, + +static HANDLE +_winapi_RegisterEventSource_impl(PyObject *module, LPCWSTR unc_server_name, + LPCWSTR source_name); + +static PyObject * +_winapi_RegisterEventSource(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + LPCWSTR unc_server_name = NULL; + LPCWSTR source_name = NULL; + HANDLE _return_value; + + if (!_PyArg_CheckPositional("RegisterEventSource", nargs, 2, 2)) { + goto exit; + } + if (args[0] == Py_None) { + unc_server_name = NULL; + } + else if (PyUnicode_Check(args[0])) { + unc_server_name = PyUnicode_AsWideCharString(args[0], NULL); + if (unc_server_name == NULL) { + goto exit; + } + } + else { + _PyArg_BadArgument("RegisterEventSource", "argument 1", "str or None", args[0]); + goto exit; + } + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("RegisterEventSource", "argument 2", "str", args[1]); + goto exit; + } + source_name = PyUnicode_AsWideCharString(args[1], NULL); + if (source_name == NULL) { + goto exit; + } + _return_value = _winapi_RegisterEventSource_impl(module, unc_server_name, source_name); + if ((_return_value == INVALID_HANDLE_VALUE) && PyErr_Occurred()) { + goto exit; + } + if (_return_value == NULL) { + Py_RETURN_NONE; + } + return_value = HANDLE_TO_PYNUM(_return_value); + +exit: + /* Cleanup for unc_server_name */ + PyMem_Free((void *)unc_server_name); + /* Cleanup for source_name */ + PyMem_Free((void *)source_name); + + return return_value; +} + +PyDoc_STRVAR(_winapi_DeregisterEventSource__doc__, +"DeregisterEventSource($module, handle, /)\n" +"--\n" +"\n" +"Closes the specified event log.\n" +"\n" +" handle\n" +" The handle to the event log to be deregistered."); + +#define _WINAPI_DEREGISTEREVENTSOURCE_METHODDEF \ + {"DeregisterEventSource", (PyCFunction)_winapi_DeregisterEventSource, METH_O, _winapi_DeregisterEventSource__doc__}, + +static PyObject * +_winapi_DeregisterEventSource_impl(PyObject *module, HANDLE handle); + +static PyObject * +_winapi_DeregisterEventSource(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + HANDLE handle; + + if (!PyArg_Parse(arg, "" F_HANDLE ":DeregisterEventSource", &handle)) { + goto exit; + } + return_value = _winapi_DeregisterEventSource_impl(module, handle); + +exit: + return return_value; +} + +PyDoc_STRVAR(_winapi_ReportEvent__doc__, +"ReportEvent($module, handle, type, category, event_id, string, /)\n" +"--\n" +"\n" +"Writes an entry at the end of the specified event log.\n" +"\n" +" handle\n" +" The handle to the event log.\n" +" type\n" +" The type of event being reported.\n" +" category\n" +" The event category.\n" +" event_id\n" +" The event identifier.\n" +" string\n" +" A string to be inserted into the event message."); + +#define _WINAPI_REPORTEVENT_METHODDEF \ + {"ReportEvent", _PyCFunction_CAST(_winapi_ReportEvent), METH_FASTCALL, _winapi_ReportEvent__doc__}, + +static PyObject * +_winapi_ReportEvent_impl(PyObject *module, HANDLE handle, + unsigned short type, unsigned short category, + unsigned int event_id, LPCWSTR string); + +static PyObject * +_winapi_ReportEvent(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + HANDLE handle; + unsigned short type; + unsigned short category; + unsigned int event_id; + LPCWSTR string = NULL; + + if (!_PyArg_ParseStack(args, nargs, "" F_HANDLE "O&O&O&O&:ReportEvent", + &handle, _PyLong_UnsignedShort_Converter, &type, _PyLong_UnsignedShort_Converter, &category, _PyLong_UnsignedInt_Converter, &event_id, _PyUnicode_WideCharString_Converter, &string)) { + goto exit; + } + return_value = _winapi_ReportEvent_impl(module, handle, type, category, event_id, string); + +exit: + /* Cleanup for string */ + PyMem_Free((void *)string); + + return return_value; +} + +PyDoc_STRVAR(_winapi_GetProcessMemoryInfo__doc__, +"GetProcessMemoryInfo($module, handle, /)\n" +"--\n" +"\n" +"Return the memory usage of the given process handle as a dict."); + +#define _WINAPI_GETPROCESSMEMORYINFO_METHODDEF \ + {"GetProcessMemoryInfo", (PyCFunction)_winapi_GetProcessMemoryInfo, METH_O, _winapi_GetProcessMemoryInfo__doc__}, + +static PyObject * +_winapi_GetProcessMemoryInfo_impl(PyObject *module, HANDLE handle); + +static PyObject * +_winapi_GetProcessMemoryInfo(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + HANDLE handle; + + if (!PyArg_Parse(arg, "" F_HANDLE ":GetProcessMemoryInfo", &handle)) { + goto exit; + } + return_value = _winapi_GetProcessMemoryInfo_impl(module, handle); + +exit: + return return_value; +} + #ifndef _WINAPI_GETSHORTPATHNAME_METHODDEF #define _WINAPI_GETSHORTPATHNAME_METHODDEF #endif /* !defined(_WINAPI_GETSHORTPATHNAME_METHODDEF) */ -/*[clinic end generated code: output=4581fd481c3c6293 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=07dfd4bbacaed4a8 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/arraymodule.c.h b/Modules/clinic/arraymodule.c.h index 97e5ca771f3a90..1e9f317853d6c2 100644 --- a/Modules/clinic/arraymodule.c.h +++ b/Modules/clinic/arraymodule.c.h @@ -292,8 +292,8 @@ PyDoc_STRVAR(array_array_buffer_info__doc__, "\n" "Return a tuple (address, length) giving the current memory address and the length in items of the buffer used to hold array\'s contents.\n" "\n" -"The length should be multiplied by the itemsize attribute to calculate\n" -"the buffer length in bytes."); +"The length should be multiplied by the itemsize attribute to\n" +"calculate the buffer length in bytes."); #define ARRAY_ARRAY_BUFFER_INFO_METHODDEF \ {"buffer_info", (PyCFunction)array_array_buffer_info, METH_NOARGS, array_array_buffer_info__doc__}, @@ -335,8 +335,9 @@ PyDoc_STRVAR(array_array_byteswap__doc__, "\n" "Byteswap all items of the array.\n" "\n" -"If the items in the array are not 1, 2, 4, or 8 bytes in size, RuntimeError is\n" -"raised."); +"If the items in the array are not 1, 2, 4, 8 or 16 bytes in size,\n" +"RuntimeError is raised. Note, that for complex types the order of\n" +"components (the real part, followed by imaginary part) is preserved."); #define ARRAY_ARRAY_BYTESWAP_METHODDEF \ {"byteswap", (PyCFunction)array_array_byteswap, METH_NOARGS, array_array_byteswap__doc__}, @@ -419,6 +420,11 @@ array_array_fromfile(PyObject *self, PyTypeObject *cls, PyObject *const *args, P goto exit; } n = ival; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, + "n cannot be negative"); + goto exit; + } } return_value = array_array_fromfile_impl((arrayobject *)self, cls, f, n); @@ -566,9 +572,9 @@ PyDoc_STRVAR(array_array_fromunicode__doc__, "\n" "Extends this array with data from the unicode string ustr.\n" "\n" -"The array must be a unicode type array; otherwise a ValueError is raised.\n" -"Use array.frombytes(ustr.encode(...)) to append Unicode data to an array of\n" -"some other type."); +"The array must be a unicode type array; otherwise a ValueError is\n" +"raised. Use array.frombytes(ustr.encode(...)) to append Unicode\n" +"data to an array of some other type."); #define ARRAY_ARRAY_FROMUNICODE_METHODDEF \ {"fromunicode", (PyCFunction)array_array_fromunicode, METH_O, array_array_fromunicode__doc__}, @@ -599,9 +605,10 @@ PyDoc_STRVAR(array_array_tounicode__doc__, "\n" "Extends this array with data from the unicode string ustr.\n" "\n" -"Convert the array to a unicode string. The array must be a unicode type array;\n" -"otherwise a ValueError is raised. Use array.tobytes().decode() to obtain a\n" -"unicode string from an array of some other type."); +"Convert the array to a unicode string. The array must be a unicode\n" +"type array; otherwise a ValueError is raised. Use\n" +"array.tobytes().decode() to obtain a unicode string from an array of\n" +"some other type."); #define ARRAY_ARRAY_TOUNICODE_METHODDEF \ {"tounicode", (PyCFunction)array_array_tounicode, METH_NOARGS, array_array_tounicode__doc__}, @@ -645,7 +652,7 @@ PyDoc_STRVAR(array__array_reconstructor__doc__, static PyObject * array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype, - int typecode, + const char *typecode, enum machine_format_code mformat_code, PyObject *items); @@ -654,7 +661,7 @@ array__array_reconstructor(PyObject *module, PyObject *const *args, Py_ssize_t n { PyObject *return_value = NULL; PyTypeObject *arraytype; - int typecode; + const char *typecode; enum machine_format_code mformat_code; PyObject *items; @@ -663,17 +670,18 @@ array__array_reconstructor(PyObject *module, PyObject *const *args, Py_ssize_t n } arraytype = (PyTypeObject *)args[0]; if (!PyUnicode_Check(args[1])) { - _PyArg_BadArgument("_array_reconstructor", "argument 2", "a unicode character", args[1]); + _PyArg_BadArgument("_array_reconstructor", "argument 2", "str", args[1]); + goto exit; + } + Py_ssize_t typecode_length; + typecode = PyUnicode_AsUTF8AndSize(args[1], &typecode_length); + if (typecode == NULL) { goto exit; } - if (PyUnicode_GET_LENGTH(args[1]) != 1) { - PyErr_Format(PyExc_TypeError, - "_array_reconstructor(): argument 2 must be a unicode character, " - "not a string of length %zd", - PyUnicode_GET_LENGTH(args[1])); + if (strlen(typecode) != (size_t)typecode_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); goto exit; } - typecode = PyUnicode_READ_CHAR(args[1], 0); mformat_code = PyLong_AsInt(args[2]); if (mformat_code == -1 && PyErr_Occurred()) { goto exit; @@ -773,4 +781,4 @@ array_arrayiterator___setstate__(PyObject *self, PyObject *state) return return_value; } -/*[clinic end generated code: output=dd49451ac1cc3f39 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=32784678e77ac658 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/binascii.c.h b/Modules/clinic/binascii.c.h index ce29e0d11a45cd..29fa9e87de87c7 100644 --- a/Modules/clinic/binascii.c.h +++ b/Modules/clinic/binascii.c.h @@ -6,6 +6,8 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_long.h" // _PyLong_Size_t_Converter() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(binascii_a2b_uu__doc__, @@ -115,20 +117,32 @@ binascii_b2a_uu(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj } PyDoc_STRVAR(binascii_a2b_base64__doc__, -"a2b_base64($module, data, /, *, strict_mode=False)\n" +"a2b_base64($module, data, /, *, strict_mode=<unrepresentable>,\n" +" padded=True, alphabet=BASE64_ALPHABET,\n" +" ignorechars=<unrepresentable>, canonical=False)\n" "--\n" "\n" "Decode a line of base64 data.\n" "\n" " strict_mode\n" -" When set to True, bytes that are not part of the base64 standard are not allowed.\n" -" The same applies to excess data after padding (= / ==)."); +" When set to true, bytes that are not part of the base64 standard are\n" +" not allowed. The same applies to excess data after padding (= / ==).\n" +" Set to True by default if ignorechars is specified, False otherwise.\n" +" padded\n" +" When set to false, padding in input is not required.\n" +" ignorechars\n" +" A byte string containing characters to ignore from the input when\n" +" strict_mode is true.\n" +" canonical\n" +" When set to true, reject non-zero padding bits per RFC 4648 section 3.5."); #define BINASCII_A2B_BASE64_METHODDEF \ {"a2b_base64", _PyCFunction_CAST(binascii_a2b_base64), METH_FASTCALL|METH_KEYWORDS, binascii_a2b_base64__doc__}, static PyObject * -binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode); +binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode, + int padded, PyBytesObject *alphabet, + Py_buffer *ignorechars, int canonical); static PyObject * binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -136,7 +150,582 @@ binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 1 + #define NUM_KEYWORDS 5 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(strict_mode), &_Py_ID(padded), &_Py_ID(alphabet), &_Py_ID(ignorechars), &_Py_ID(canonical), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "strict_mode", "padded", "alphabet", "ignorechars", "canonical", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "a2b_base64", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[6]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_buffer data = {NULL, NULL}; + int strict_mode = -1; + int padded = 1; + PyBytesObject *alphabet = NULL; + Py_buffer ignorechars = {NULL, NULL}; + int canonical = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!ascii_buffer_converter(args[0], &data)) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + strict_mode = PyObject_IsTrue(args[1]); + if (strict_mode < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[2]) { + padded = PyObject_IsTrue(args[2]); + if (padded < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[3]) { + if (!PyBytes_Check(args[3])) { + _PyArg_BadArgument("a2b_base64", "argument 'alphabet'", "bytes", args[3]); + goto exit; + } + alphabet = (PyBytesObject *)args[3]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[4]) { + if (PyObject_GetBuffer(args[4], &ignorechars, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + canonical = PyObject_IsTrue(args[5]); + if (canonical < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = binascii_a2b_base64_impl(module, &data, strict_mode, padded, alphabet, &ignorechars, canonical); + +exit: + /* Cleanup for data */ + if (data.obj) + PyBuffer_Release(&data); + /* Cleanup for ignorechars */ + if (ignorechars.obj) { + PyBuffer_Release(&ignorechars); + } + + return return_value; +} + +PyDoc_STRVAR(binascii_b2a_base64__doc__, +"b2a_base64($module, data, /, *, padded=True, wrapcol=0, newline=True,\n" +" alphabet=BASE64_ALPHABET)\n" +"--\n" +"\n" +"Base64-code line of data.\n" +"\n" +" padded\n" +" When set to false, omit padding in the output."); + +#define BINASCII_B2A_BASE64_METHODDEF \ + {"b2a_base64", _PyCFunction_CAST(binascii_b2a_base64), METH_FASTCALL|METH_KEYWORDS, binascii_b2a_base64__doc__}, + +static PyObject * +binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, int padded, + size_t wrapcol, int newline, Py_buffer *alphabet); + +static PyObject * +binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(padded), &_Py_ID(wrapcol), &_Py_ID(newline), &_Py_ID(alphabet), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "padded", "wrapcol", "newline", "alphabet", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "b2a_base64", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_buffer data = {NULL, NULL}; + int padded = 1; + size_t wrapcol = 0; + int newline = 1; + Py_buffer alphabet = {NULL, NULL}; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + padded = PyObject_IsTrue(args[1]); + if (padded < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[2]) { + if (!_PyLong_Size_t_Converter(args[2], &wrapcol)) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[3]) { + newline = PyObject_IsTrue(args[3]); + if (newline < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (PyObject_GetBuffer(args[4], &alphabet, PyBUF_SIMPLE) != 0) { + goto exit; + } +skip_optional_kwonly: + return_value = binascii_b2a_base64_impl(module, &data, padded, wrapcol, newline, &alphabet); + +exit: + /* Cleanup for data */ + if (data.obj) { + PyBuffer_Release(&data); + } + /* Cleanup for alphabet */ + if (alphabet.obj) { + PyBuffer_Release(&alphabet); + } + + return return_value; +} + +PyDoc_STRVAR(binascii_a2b_ascii85__doc__, +"a2b_ascii85($module, data, /, *, foldspaces=False, adobe=False,\n" +" ignorechars=b\'\', canonical=False)\n" +"--\n" +"\n" +"Decode Ascii85 data.\n" +"\n" +" foldspaces\n" +" Allow \'y\' as a short form encoding four spaces.\n" +" adobe\n" +" Expect data to be terminated with \'~>\' as in Adobe Ascii85, and\n" +" optionally accept leading \'<~\'.\n" +" ignorechars\n" +" A byte string containing characters to ignore from the input.\n" +" canonical\n" +" When set to true, reject non-canonical encodings."); + +#define BINASCII_A2B_ASCII85_METHODDEF \ + {"a2b_ascii85", _PyCFunction_CAST(binascii_a2b_ascii85), METH_FASTCALL|METH_KEYWORDS, binascii_a2b_ascii85__doc__}, + +static PyObject * +binascii_a2b_ascii85_impl(PyObject *module, Py_buffer *data, int foldspaces, + int adobe, Py_buffer *ignorechars, int canonical); + +static PyObject * +binascii_a2b_ascii85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(foldspaces), &_Py_ID(adobe), &_Py_ID(ignorechars), &_Py_ID(canonical), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "foldspaces", "adobe", "ignorechars", "canonical", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "a2b_ascii85", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_buffer data = {NULL, NULL}; + int foldspaces = 0; + int adobe = 0; + Py_buffer ignorechars = {.buf = "", .obj = NULL, .len = 0}; + int canonical = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!ascii_buffer_converter(args[0], &data)) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + foldspaces = PyObject_IsTrue(args[1]); + if (foldspaces < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[2]) { + adobe = PyObject_IsTrue(args[2]); + if (adobe < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[3]) { + if (PyObject_GetBuffer(args[3], &ignorechars, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + canonical = PyObject_IsTrue(args[4]); + if (canonical < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = binascii_a2b_ascii85_impl(module, &data, foldspaces, adobe, &ignorechars, canonical); + +exit: + /* Cleanup for data */ + if (data.obj) + PyBuffer_Release(&data); + /* Cleanup for ignorechars */ + if (ignorechars.obj) { + PyBuffer_Release(&ignorechars); + } + + return return_value; +} + +PyDoc_STRVAR(binascii_b2a_ascii85__doc__, +"b2a_ascii85($module, data, /, *, foldspaces=False, wrapcol=0,\n" +" pad=False, adobe=False)\n" +"--\n" +"\n" +"Ascii85-encode data.\n" +"\n" +" foldspaces\n" +" Emit \'y\' as a short form encoding four spaces.\n" +" wrapcol\n" +" Split result into lines of provided width.\n" +" pad\n" +" Retain zero-padding bytes at end of output.\n" +" adobe\n" +" Wrap result in \'<~\' and \'~>\' as in Adobe Ascii85."); + +#define BINASCII_B2A_ASCII85_METHODDEF \ + {"b2a_ascii85", _PyCFunction_CAST(binascii_b2a_ascii85), METH_FASTCALL|METH_KEYWORDS, binascii_b2a_ascii85__doc__}, + +static PyObject * +binascii_b2a_ascii85_impl(PyObject *module, Py_buffer *data, int foldspaces, + size_t wrapcol, int pad, int adobe); + +static PyObject * +binascii_b2a_ascii85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(foldspaces), &_Py_ID(wrapcol), &_Py_ID(pad), &_Py_ID(adobe), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "foldspaces", "wrapcol", "pad", "adobe", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "b2a_ascii85", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_buffer data = {NULL, NULL}; + int foldspaces = 0; + size_t wrapcol = 0; + int pad = 0; + int adobe = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + foldspaces = PyObject_IsTrue(args[1]); + if (foldspaces < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[2]) { + if (!_PyLong_Size_t_Converter(args[2], &wrapcol)) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[3]) { + pad = PyObject_IsTrue(args[3]); + if (pad < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + adobe = PyObject_IsTrue(args[4]); + if (adobe < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = binascii_b2a_ascii85_impl(module, &data, foldspaces, wrapcol, pad, adobe); + +exit: + /* Cleanup for data */ + if (data.obj) { + PyBuffer_Release(&data); + } + + return return_value; +} + +PyDoc_STRVAR(binascii_a2b_base85__doc__, +"a2b_base85($module, data, /, *, alphabet=BASE85_ALPHABET,\n" +" ignorechars=b\'\', canonical=False)\n" +"--\n" +"\n" +"Decode a line of Base85 data.\n" +"\n" +" ignorechars\n" +" A byte string containing characters to ignore from the input.\n" +" canonical\n" +" When set to true, reject non-canonical encodings."); + +#define BINASCII_A2B_BASE85_METHODDEF \ + {"a2b_base85", _PyCFunction_CAST(binascii_a2b_base85), METH_FASTCALL|METH_KEYWORDS, binascii_a2b_base85__doc__}, + +static PyObject * +binascii_a2b_base85_impl(PyObject *module, Py_buffer *data, + PyBytesObject *alphabet, Py_buffer *ignorechars, + int canonical); + +static PyObject * +binascii_a2b_base85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(alphabet), &_Py_ID(ignorechars), &_Py_ID(canonical), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "alphabet", "ignorechars", "canonical", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "a2b_base85", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_buffer data = {NULL, NULL}; + PyBytesObject *alphabet = NULL; + Py_buffer ignorechars = {.buf = "", .obj = NULL, .len = 0}; + int canonical = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!ascii_buffer_converter(args[0], &data)) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + if (!PyBytes_Check(args[1])) { + _PyArg_BadArgument("a2b_base85", "argument 'alphabet'", "bytes", args[1]); + goto exit; + } + alphabet = (PyBytesObject *)args[1]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[2]) { + if (PyObject_GetBuffer(args[2], &ignorechars, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + canonical = PyObject_IsTrue(args[3]); + if (canonical < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = binascii_a2b_base85_impl(module, &data, alphabet, &ignorechars, canonical); + +exit: + /* Cleanup for data */ + if (data.obj) + PyBuffer_Release(&data); + /* Cleanup for ignorechars */ + if (ignorechars.obj) { + PyBuffer_Release(&ignorechars); + } + + return return_value; +} + +PyDoc_STRVAR(binascii_b2a_base85__doc__, +"b2a_base85($module, data, /, *, pad=False, wrapcol=0,\n" +" alphabet=BASE85_ALPHABET)\n" +"--\n" +"\n" +"Base85-code line of data.\n" +"\n" +" pad\n" +" Retain zero-padding bytes at end of output."); + +#define BINASCII_B2A_BASE85_METHODDEF \ + {"b2a_base85", _PyCFunction_CAST(binascii_b2a_base85), METH_FASTCALL|METH_KEYWORDS, binascii_b2a_base85__doc__}, + +static PyObject * +binascii_b2a_base85_impl(PyObject *module, Py_buffer *data, int pad, + size_t wrapcol, Py_buffer *alphabet); + +static PyObject * +binascii_b2a_base85(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD @@ -145,7 +734,7 @@ binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) .ob_hash = -1, - .ob_item = { &_Py_ID(strict_mode), }, + .ob_item = { &_Py_ID(pad), &_Py_ID(wrapcol), &_Py_ID(alphabet), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -154,17 +743,127 @@ binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"", "strict_mode", NULL}; + static const char * const _keywords[] = {"", "pad", "wrapcol", "alphabet", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, - .fname = "a2b_base64", + .fname = "b2a_base85", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_buffer data = {NULL, NULL}; + int pad = 0; + size_t wrapcol = 0; + Py_buffer alphabet = {NULL, NULL}; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[1]) { + pad = PyObject_IsTrue(args[1]); + if (pad < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[2]) { + if (!_PyLong_Size_t_Converter(args[2], &wrapcol)) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (PyObject_GetBuffer(args[3], &alphabet, PyBUF_SIMPLE) != 0) { + goto exit; + } +skip_optional_kwonly: + return_value = binascii_b2a_base85_impl(module, &data, pad, wrapcol, &alphabet); + +exit: + /* Cleanup for data */ + if (data.obj) { + PyBuffer_Release(&data); + } + /* Cleanup for alphabet */ + if (alphabet.obj) { + PyBuffer_Release(&alphabet); + } + + return return_value; +} + +PyDoc_STRVAR(binascii_a2b_base32__doc__, +"a2b_base32($module, data, /, *, padded=True, alphabet=BASE32_ALPHABET,\n" +" ignorechars=b\'\', canonical=False)\n" +"--\n" +"\n" +"Decode a line of base32 data.\n" +"\n" +" padded\n" +" When set to false, padding in input is not required.\n" +" ignorechars\n" +" A byte string containing characters to ignore from the input.\n" +" canonical\n" +" When set to true, reject non-zero padding bits per RFC 4648 section 3.5."); + +#define BINASCII_A2B_BASE32_METHODDEF \ + {"a2b_base32", _PyCFunction_CAST(binascii_a2b_base32), METH_FASTCALL|METH_KEYWORDS, binascii_a2b_base32__doc__}, + +static PyObject * +binascii_a2b_base32_impl(PyObject *module, Py_buffer *data, int padded, + PyBytesObject *alphabet, Py_buffer *ignorechars, + int canonical); + +static PyObject * +binascii_a2b_base32(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(padded), &_Py_ID(alphabet), &_Py_ID(ignorechars), &_Py_ID(canonical), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "padded", "alphabet", "ignorechars", "canonical", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "a2b_base32", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; Py_buffer data = {NULL, NULL}; - int strict_mode = 0; + int padded = 1; + PyBytesObject *alphabet = NULL; + Py_buffer ignorechars = {.buf = "", .obj = NULL, .len = 0}; + int canonical = 0; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -177,40 +876,76 @@ binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P if (!noptargs) { goto skip_optional_kwonly; } - strict_mode = PyObject_IsTrue(args[1]); - if (strict_mode < 0) { + if (args[1]) { + padded = PyObject_IsTrue(args[1]); + if (padded < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[2]) { + if (!PyBytes_Check(args[2])) { + _PyArg_BadArgument("a2b_base32", "argument 'alphabet'", "bytes", args[2]); + goto exit; + } + alphabet = (PyBytesObject *)args[2]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[3]) { + if (PyObject_GetBuffer(args[3], &ignorechars, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + canonical = PyObject_IsTrue(args[4]); + if (canonical < 0) { goto exit; } skip_optional_kwonly: - return_value = binascii_a2b_base64_impl(module, &data, strict_mode); + return_value = binascii_a2b_base32_impl(module, &data, padded, alphabet, &ignorechars, canonical); exit: /* Cleanup for data */ if (data.obj) PyBuffer_Release(&data); + /* Cleanup for ignorechars */ + if (ignorechars.obj) { + PyBuffer_Release(&ignorechars); + } return return_value; } -PyDoc_STRVAR(binascii_b2a_base64__doc__, -"b2a_base64($module, data, /, *, newline=True)\n" +PyDoc_STRVAR(binascii_b2a_base32__doc__, +"b2a_base32($module, data, /, *, padded=True, wrapcol=0,\n" +" alphabet=BASE32_ALPHABET)\n" "--\n" "\n" -"Base64-code line of data."); +"Base32-code line of data.\n" +"\n" +" padded\n" +" When set to false, omit padding in the output."); -#define BINASCII_B2A_BASE64_METHODDEF \ - {"b2a_base64", _PyCFunction_CAST(binascii_b2a_base64), METH_FASTCALL|METH_KEYWORDS, binascii_b2a_base64__doc__}, +#define BINASCII_B2A_BASE32_METHODDEF \ + {"b2a_base32", _PyCFunction_CAST(binascii_b2a_base32), METH_FASTCALL|METH_KEYWORDS, binascii_b2a_base32__doc__}, static PyObject * -binascii_b2a_base64_impl(PyObject *module, Py_buffer *data, int newline); +binascii_b2a_base32_impl(PyObject *module, Py_buffer *data, int padded, + size_t wrapcol, Py_buffer *alphabet); static PyObject * -binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +binascii_b2a_base32(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 1 + #define NUM_KEYWORDS 3 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD @@ -219,7 +954,7 @@ binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) .ob_hash = -1, - .ob_item = { &_Py_ID(newline), }, + .ob_item = { &_Py_ID(padded), &_Py_ID(wrapcol), &_Py_ID(alphabet), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -228,17 +963,19 @@ binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"", "newline", NULL}; + static const char * const _keywords[] = {"", "padded", "wrapcol", "alphabet", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, - .fname = "b2a_base64", + .fname = "b2a_base32", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[4]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; Py_buffer data = {NULL, NULL}; - int newline = 1; + int padded = 1; + size_t wrapcol = 0; + Py_buffer alphabet = {NULL, NULL}; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -251,18 +988,38 @@ binascii_b2a_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P if (!noptargs) { goto skip_optional_kwonly; } - newline = PyObject_IsTrue(args[1]); - if (newline < 0) { + if (args[1]) { + padded = PyObject_IsTrue(args[1]); + if (padded < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[2]) { + if (!_PyLong_Size_t_Converter(args[2], &wrapcol)) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (PyObject_GetBuffer(args[3], &alphabet, PyBUF_SIMPLE) != 0) { goto exit; } skip_optional_kwonly: - return_value = binascii_b2a_base64_impl(module, &data, newline); + return_value = binascii_b2a_base32_impl(module, &data, padded, wrapcol, &alphabet); exit: /* Cleanup for data */ if (data.obj) { PyBuffer_Release(&data); } + /* Cleanup for alphabet */ + if (alphabet.obj) { + PyBuffer_Release(&alphabet); + } return return_value; } @@ -408,7 +1165,7 @@ PyDoc_STRVAR(binascii_b2a_hex__doc__, static PyObject * binascii_b2a_hex_impl(PyObject *module, Py_buffer *data, PyObject *sep, - int bytes_per_sep); + Py_ssize_t bytes_per_sep); static PyObject * binascii_b2a_hex(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -445,7 +1202,7 @@ binascii_b2a_hex(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; Py_buffer data = {NULL, NULL}; PyObject *sep = NULL; - int bytes_per_sep = 1; + Py_ssize_t bytes_per_sep = 1; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -464,9 +1221,17 @@ binascii_b2a_hex(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb goto skip_optional_pos; } } - bytes_per_sep = PyLong_AsInt(args[2]); - if (bytes_per_sep == -1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + bytes_per_sep = ival; } skip_optional_pos: return_value = binascii_b2a_hex_impl(module, &data, sep, bytes_per_sep); @@ -500,7 +1265,7 @@ PyDoc_STRVAR(binascii_hexlify__doc__, static PyObject * binascii_hexlify_impl(PyObject *module, Py_buffer *data, PyObject *sep, - int bytes_per_sep); + Py_ssize_t bytes_per_sep); static PyObject * binascii_hexlify(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -537,7 +1302,7 @@ binascii_hexlify(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; Py_buffer data = {NULL, NULL}; PyObject *sep = NULL; - int bytes_per_sep = 1; + Py_ssize_t bytes_per_sep = 1; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 1, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -556,9 +1321,17 @@ binascii_hexlify(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb goto skip_optional_pos; } } - bytes_per_sep = PyLong_AsInt(args[2]); - if (bytes_per_sep == -1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + bytes_per_sep = ival; } skip_optional_pos: return_value = binascii_hexlify_impl(module, &data, sep, bytes_per_sep); @@ -573,68 +1346,168 @@ binascii_hexlify(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb } PyDoc_STRVAR(binascii_a2b_hex__doc__, -"a2b_hex($module, hexstr, /)\n" +"a2b_hex($module, hexstr, /, *, ignorechars=b\'\')\n" "--\n" "\n" "Binary data of hexadecimal representation.\n" "\n" +" ignorechars\n" +" A byte string containing characters to ignore from the input.\n" +"\n" "hexstr must contain an even number of hex digits (upper or lower case).\n" "This function is also available as \"unhexlify()\"."); #define BINASCII_A2B_HEX_METHODDEF \ - {"a2b_hex", (PyCFunction)binascii_a2b_hex, METH_O, binascii_a2b_hex__doc__}, + {"a2b_hex", _PyCFunction_CAST(binascii_a2b_hex), METH_FASTCALL|METH_KEYWORDS, binascii_a2b_hex__doc__}, static PyObject * -binascii_a2b_hex_impl(PyObject *module, Py_buffer *hexstr); +binascii_a2b_hex_impl(PyObject *module, Py_buffer *hexstr, + Py_buffer *ignorechars); static PyObject * -binascii_a2b_hex(PyObject *module, PyObject *arg) +binascii_a2b_hex(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(ignorechars), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "ignorechars", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "a2b_hex", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; Py_buffer hexstr = {NULL, NULL}; + Py_buffer ignorechars = {.buf = "", .obj = NULL, .len = 0}; - if (!ascii_buffer_converter(arg, &hexstr)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { goto exit; } - return_value = binascii_a2b_hex_impl(module, &hexstr); + if (!ascii_buffer_converter(args[0], &hexstr)) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (PyObject_GetBuffer(args[1], &ignorechars, PyBUF_SIMPLE) != 0) { + goto exit; + } +skip_optional_kwonly: + return_value = binascii_a2b_hex_impl(module, &hexstr, &ignorechars); exit: /* Cleanup for hexstr */ if (hexstr.obj) PyBuffer_Release(&hexstr); + /* Cleanup for ignorechars */ + if (ignorechars.obj) { + PyBuffer_Release(&ignorechars); + } return return_value; } PyDoc_STRVAR(binascii_unhexlify__doc__, -"unhexlify($module, hexstr, /)\n" +"unhexlify($module, hexstr, /, *, ignorechars=b\'\')\n" "--\n" "\n" "Binary data of hexadecimal representation.\n" "\n" +" ignorechars\n" +" A byte string containing characters to ignore from the input.\n" +"\n" "hexstr must contain an even number of hex digits (upper or lower case)."); #define BINASCII_UNHEXLIFY_METHODDEF \ - {"unhexlify", (PyCFunction)binascii_unhexlify, METH_O, binascii_unhexlify__doc__}, + {"unhexlify", _PyCFunction_CAST(binascii_unhexlify), METH_FASTCALL|METH_KEYWORDS, binascii_unhexlify__doc__}, static PyObject * -binascii_unhexlify_impl(PyObject *module, Py_buffer *hexstr); +binascii_unhexlify_impl(PyObject *module, Py_buffer *hexstr, + Py_buffer *ignorechars); static PyObject * -binascii_unhexlify(PyObject *module, PyObject *arg) +binascii_unhexlify(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(ignorechars), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "ignorechars", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "unhexlify", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; Py_buffer hexstr = {NULL, NULL}; + Py_buffer ignorechars = {.buf = "", .obj = NULL, .len = 0}; - if (!ascii_buffer_converter(arg, &hexstr)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!ascii_buffer_converter(args[0], &hexstr)) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (PyObject_GetBuffer(args[1], &ignorechars, PyBUF_SIMPLE) != 0) { goto exit; } - return_value = binascii_unhexlify_impl(module, &hexstr); +skip_optional_kwonly: + return_value = binascii_unhexlify_impl(module, &hexstr, &ignorechars); exit: /* Cleanup for hexstr */ if (hexstr.obj) PyBuffer_Release(&hexstr); + /* Cleanup for ignorechars */ + if (ignorechars.obj) { + PyBuffer_Release(&ignorechars); + } return return_value; } @@ -812,4 +1685,4 @@ binascii_b2a_qp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj return return_value; } -/*[clinic end generated code: output=fba6a71e0d7d092f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=42dd48f323cbb118 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/blake2module.c.h b/Modules/clinic/blake2module.c.h index 97d010d03a4b23..dc70abfaf05543 100644 --- a/Modules/clinic/blake2module.c.h +++ b/Modules/clinic/blake2module.c.h @@ -63,9 +63,9 @@ py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; PyObject *data_obj = NULL; int digest_size = HACL_HASH_BLAKE2B_OUT_BYTES; - Py_buffer key = {NULL, NULL}; - Py_buffer salt = {NULL, NULL}; - Py_buffer person = {NULL, NULL}; + Py_buffer key = {.buf = "", .obj = NULL, .len = 0}; + Py_buffer salt = {.buf = "", .obj = NULL, .len = 0}; + Py_buffer person = {.buf = "", .obj = NULL, .len = 0}; int fanout = 1; int depth = 1; unsigned long leaf_size = 0; @@ -272,9 +272,9 @@ py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; PyObject *data_obj = NULL; int digest_size = HACL_HASH_BLAKE2S_OUT_BYTES; - Py_buffer key = {NULL, NULL}; - Py_buffer salt = {NULL, NULL}; - Py_buffer person = {NULL, NULL}; + Py_buffer key = {.buf = "", .obj = NULL, .len = 0}; + Py_buffer salt = {.buf = "", .obj = NULL, .len = 0}; + Py_buffer person = {.buf = "", .obj = NULL, .len = 0}; int fanout = 1; int depth = 1; unsigned long leaf_size = 0; @@ -506,4 +506,4 @@ _blake2_blake2b_hexdigest(PyObject *self, PyObject *Py_UNUSED(ignored)) { return _blake2_blake2b_hexdigest_impl((Blake2Object *)self); } -/*[clinic end generated code: output=60a4abbcb8950fe5 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5f76c18211cd7f8f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/cmathmodule.c.h b/Modules/clinic/cmathmodule.c.h index 7f9e65baf120ea..ecb5257cf6b240 100644 --- a/Modules/clinic/cmathmodule.c.h +++ b/Modules/clinic/cmathmodule.c.h @@ -644,7 +644,8 @@ PyDoc_STRVAR(cmath_log__doc__, "\n" "log(z[, base]) -> the logarithm of z to the given base.\n" "\n" -"If the base is not specified, returns the natural logarithm (base e) of z."); +"If the base is not specified, returns the natural logarithm (base e)\n" +"of z."); #define CMATH_LOG_METHODDEF \ {"log", _PyCFunction_CAST(cmath_log), METH_FASTCALL, cmath_log__doc__}, @@ -882,11 +883,12 @@ PyDoc_STRVAR(cmath_isclose__doc__, "\n" "Return True if a is close in value to b, and False otherwise.\n" "\n" -"For the values to be considered close, the difference between them must be\n" -"smaller than at least one of the tolerances.\n" +"For the values to be considered close, the difference between them must\n" +"be smaller than at least one of the tolerances.\n" "\n" -"-inf, inf and NaN behave similarly to the IEEE 754 Standard. That is, NaN is\n" -"not close to anything, even itself. inf and -inf are only close to themselves."); +"-inf, inf and NaN behave similarly to the IEEE 754 Standard. That is,\n" +"NaN is not close to anything, even itself. inf and -inf are only close\n" +"to themselves."); #define CMATH_ISCLOSE_METHODDEF \ {"isclose", _PyCFunction_CAST(cmath_isclose), METH_FASTCALL|METH_KEYWORDS, cmath_isclose__doc__}, @@ -985,4 +987,4 @@ cmath_isclose(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec exit: return return_value; } -/*[clinic end generated code: output=631db17fb1c79d66 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7d5ad4cf258526cd input=a9049054013a1b77]*/ diff --git a/Modules/clinic/faulthandler.c.h b/Modules/clinic/faulthandler.c.h new file mode 100644 index 00000000000000..07a9dd7dc561e1 --- /dev/null +++ b/Modules/clinic/faulthandler.c.h @@ -0,0 +1,785 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_long.h" // _PyLong_UnsignedInt_Converter() +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +PyDoc_STRVAR(faulthandler_dump_traceback_py__doc__, +"dump_traceback($module, /, file=sys.stderr, all_threads=True, *,\n" +" max_threads=100)\n" +"--\n" +"\n" +"Dump the traceback of the current thread into file.\n" +"\n" +"Dump the traceback of all threads if all_threads is true. max_threads\n" +"caps the number of threads dumped."); + +#define FAULTHANDLER_DUMP_TRACEBACK_PY_METHODDEF \ + {"dump_traceback", _PyCFunction_CAST(faulthandler_dump_traceback_py), METH_FASTCALL|METH_KEYWORDS, faulthandler_dump_traceback_py__doc__}, + +static PyObject * +faulthandler_dump_traceback_py_impl(PyObject *module, PyObject *file, + int all_threads, Py_ssize_t max_threads); + +static PyObject * +faulthandler_dump_traceback_py(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(file), &_Py_ID(all_threads), &_Py_ID(max_threads), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"file", "all_threads", "max_threads", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "dump_traceback", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *file = NULL; + int all_threads = 1; + Py_ssize_t max_threads = 100; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + file = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[1]) { + all_threads = PyObject_IsTrue(args[1]); + if (all_threads < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + max_threads = ival; + } +skip_optional_kwonly: + return_value = faulthandler_dump_traceback_py_impl(module, file, all_threads, max_threads); + +exit: + return return_value; +} + +PyDoc_STRVAR(faulthandler_dump_c_stack_py__doc__, +"dump_c_stack($module, /, file=sys.stderr)\n" +"--\n" +"\n" +"Dump the C stack of the current thread."); + +#define FAULTHANDLER_DUMP_C_STACK_PY_METHODDEF \ + {"dump_c_stack", _PyCFunction_CAST(faulthandler_dump_c_stack_py), METH_FASTCALL|METH_KEYWORDS, faulthandler_dump_c_stack_py__doc__}, + +static PyObject * +faulthandler_dump_c_stack_py_impl(PyObject *module, PyObject *file); + +static PyObject * +faulthandler_dump_c_stack_py(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(file), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"file", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "dump_c_stack", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *file = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + file = args[0]; +skip_optional_pos: + return_value = faulthandler_dump_c_stack_py_impl(module, file); + +exit: + return return_value; +} + +PyDoc_STRVAR(faulthandler_py_enable__doc__, +"enable($module, /, file=sys.stderr, all_threads=True, c_stack=True, *,\n" +" max_threads=100)\n" +"--\n" +"\n" +"Enable the fault handler."); + +#define FAULTHANDLER_PY_ENABLE_METHODDEF \ + {"enable", _PyCFunction_CAST(faulthandler_py_enable), METH_FASTCALL|METH_KEYWORDS, faulthandler_py_enable__doc__}, + +static PyObject * +faulthandler_py_enable_impl(PyObject *module, PyObject *file, + int all_threads, int c_stack, + Py_ssize_t max_threads); + +static PyObject * +faulthandler_py_enable(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 4 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(file), &_Py_ID(all_threads), &_Py_ID(c_stack), &_Py_ID(max_threads), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"file", "all_threads", "c_stack", "max_threads", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "enable", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + PyObject *file = NULL; + int all_threads = 1; + int c_stack = 1; + Py_ssize_t max_threads = 100; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + file = args[0]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[1]) { + all_threads = PyObject_IsTrue(args[1]); + if (all_threads < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[2]) { + c_stack = PyObject_IsTrue(args[2]); + if (c_stack < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[3]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + max_threads = ival; + } +skip_optional_kwonly: + return_value = faulthandler_py_enable_impl(module, file, all_threads, c_stack, max_threads); + +exit: + return return_value; +} + +PyDoc_STRVAR(faulthandler_disable_py__doc__, +"disable($module, /)\n" +"--\n" +"\n" +"Disable the fault handler."); + +#define FAULTHANDLER_DISABLE_PY_METHODDEF \ + {"disable", (PyCFunction)faulthandler_disable_py, METH_NOARGS, faulthandler_disable_py__doc__}, + +static PyObject * +faulthandler_disable_py_impl(PyObject *module); + +static PyObject * +faulthandler_disable_py(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return faulthandler_disable_py_impl(module); +} + +PyDoc_STRVAR(faulthandler_is_enabled__doc__, +"is_enabled($module, /)\n" +"--\n" +"\n" +"Check if the handler is enabled."); + +#define FAULTHANDLER_IS_ENABLED_METHODDEF \ + {"is_enabled", (PyCFunction)faulthandler_is_enabled, METH_NOARGS, faulthandler_is_enabled__doc__}, + +static int +faulthandler_is_enabled_impl(PyObject *module); + +static PyObject * +faulthandler_is_enabled(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + int _return_value; + + _return_value = faulthandler_is_enabled_impl(module); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(faulthandler_dump_traceback_later__doc__, +"dump_traceback_later($module, /, timeout, repeat=False,\n" +" file=sys.stderr, exit=False, *, max_threads=100)\n" +"--\n" +"\n" +"Dump the traceback of all threads in timeout seconds.\n" +"\n" +"If repeat is true, the tracebacks of all threads are dumped every\n" +"timeout seconds. If exit is true, call _exit(1) which is not safe.\n" +"max_threads caps the number of threads dumped."); + +#define FAULTHANDLER_DUMP_TRACEBACK_LATER_METHODDEF \ + {"dump_traceback_later", _PyCFunction_CAST(faulthandler_dump_traceback_later), METH_FASTCALL|METH_KEYWORDS, faulthandler_dump_traceback_later__doc__}, + +static PyObject * +faulthandler_dump_traceback_later_impl(PyObject *module, + PyObject *timeout_obj, int repeat, + PyObject *file, int exit, + Py_ssize_t max_threads); + +static PyObject * +faulthandler_dump_traceback_later(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 5 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(timeout), &_Py_ID(repeat), &_Py_ID(file), &_Py_ID(exit), &_Py_ID(max_threads), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"timeout", "repeat", "file", "exit", "max_threads", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "dump_traceback_later", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *timeout_obj; + int repeat = 0; + PyObject *file = NULL; + int exit = 0; + Py_ssize_t max_threads = 100; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + timeout_obj = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1]) { + repeat = PyObject_IsTrue(args[1]); + if (repeat < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[2]) { + file = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[3]) { + exit = PyObject_IsTrue(args[3]); + if (exit < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[4]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + max_threads = ival; + } +skip_optional_kwonly: + return_value = faulthandler_dump_traceback_later_impl(module, timeout_obj, repeat, file, exit, max_threads); + +exit: + return return_value; +} + +PyDoc_STRVAR(faulthandler_cancel_dump_traceback_later_py__doc__, +"cancel_dump_traceback_later($module, /)\n" +"--\n" +"\n" +"Cancel the previous call to dump_traceback_later()."); + +#define FAULTHANDLER_CANCEL_DUMP_TRACEBACK_LATER_PY_METHODDEF \ + {"cancel_dump_traceback_later", (PyCFunction)faulthandler_cancel_dump_traceback_later_py, METH_NOARGS, faulthandler_cancel_dump_traceback_later_py__doc__}, + +static PyObject * +faulthandler_cancel_dump_traceback_later_py_impl(PyObject *module); + +static PyObject * +faulthandler_cancel_dump_traceback_later_py(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return faulthandler_cancel_dump_traceback_later_py_impl(module); +} + +#if defined(FAULTHANDLER_USER) + +PyDoc_STRVAR(faulthandler_register_py__doc__, +"register($module, /, signum, file=sys.stderr, all_threads=True,\n" +" chain=False, *, max_threads=100)\n" +"--\n" +"\n" +"Register a handler for the signal \'signum\'.\n" +"\n" +"Dump the traceback of the current thread, or of all threads if\n" +"all_threads is True, into file. max_threads caps the number of threads\n" +"dumped."); + +#define FAULTHANDLER_REGISTER_PY_METHODDEF \ + {"register", _PyCFunction_CAST(faulthandler_register_py), METH_FASTCALL|METH_KEYWORDS, faulthandler_register_py__doc__}, + +static PyObject * +faulthandler_register_py_impl(PyObject *module, int signum, PyObject *file, + int all_threads, int chain, + Py_ssize_t max_threads); + +static PyObject * +faulthandler_register_py(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 5 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(signum), &_Py_ID(file), &_Py_ID(all_threads), &_Py_ID(chain), &_Py_ID(max_threads), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"signum", "file", "all_threads", "chain", "max_threads", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "register", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + int signum; + PyObject *file = NULL; + int all_threads = 1; + int chain = 0; + Py_ssize_t max_threads = 100; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + signum = PyLong_AsInt(args[0]); + if (signum == -1 && PyErr_Occurred()) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1]) { + file = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[2]) { + all_threads = PyObject_IsTrue(args[2]); + if (all_threads < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[3]) { + chain = PyObject_IsTrue(args[3]); + if (chain < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[4]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + max_threads = ival; + } +skip_optional_kwonly: + return_value = faulthandler_register_py_impl(module, signum, file, all_threads, chain, max_threads); + +exit: + return return_value; +} + +#endif /* defined(FAULTHANDLER_USER) */ + +#if defined(FAULTHANDLER_USER) + +PyDoc_STRVAR(faulthandler_unregister_py__doc__, +"unregister($module, signum, /)\n" +"--\n" +"\n" +"Unregister the handler of the signal \'signum\' registered by register()."); + +#define FAULTHANDLER_UNREGISTER_PY_METHODDEF \ + {"unregister", (PyCFunction)faulthandler_unregister_py, METH_O, faulthandler_unregister_py__doc__}, + +static PyObject * +faulthandler_unregister_py_impl(PyObject *module, int signum); + +static PyObject * +faulthandler_unregister_py(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int signum; + + signum = PyLong_AsInt(arg); + if (signum == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = faulthandler_unregister_py_impl(module, signum); + +exit: + return return_value; +} + +#endif /* defined(FAULTHANDLER_USER) */ + +PyDoc_STRVAR(faulthandler__sigsegv__doc__, +"_sigsegv($module, release_gil=False, /)\n" +"--\n" +"\n" +"Raise a SIGSEGV signal."); + +#define FAULTHANDLER__SIGSEGV_METHODDEF \ + {"_sigsegv", _PyCFunction_CAST(faulthandler__sigsegv), METH_FASTCALL, faulthandler__sigsegv__doc__}, + +static PyObject * +faulthandler__sigsegv_impl(PyObject *module, int release_gil); + +static PyObject * +faulthandler__sigsegv(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int release_gil = 0; + + if (!_PyArg_CheckPositional("_sigsegv", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + release_gil = PyObject_IsTrue(args[0]); + if (release_gil < 0) { + goto exit; + } +skip_optional: + return_value = faulthandler__sigsegv_impl(module, release_gil); + +exit: + return return_value; +} + +PyDoc_STRVAR(faulthandler__fatal_error_c_thread__doc__, +"_fatal_error_c_thread($module, /)\n" +"--\n" +"\n" +"Call Py_FatalError() in a new C thread."); + +#define FAULTHANDLER__FATAL_ERROR_C_THREAD_METHODDEF \ + {"_fatal_error_c_thread", (PyCFunction)faulthandler__fatal_error_c_thread, METH_NOARGS, faulthandler__fatal_error_c_thread__doc__}, + +static PyObject * +faulthandler__fatal_error_c_thread_impl(PyObject *module); + +static PyObject * +faulthandler__fatal_error_c_thread(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return faulthandler__fatal_error_c_thread_impl(module); +} + +PyDoc_STRVAR(faulthandler__sigfpe__doc__, +"_sigfpe($module, /)\n" +"--\n" +"\n" +"Raise a SIGFPE signal."); + +#define FAULTHANDLER__SIGFPE_METHODDEF \ + {"_sigfpe", (PyCFunction)faulthandler__sigfpe, METH_NOARGS, faulthandler__sigfpe__doc__}, + +static PyObject * +faulthandler__sigfpe_impl(PyObject *module); + +static PyObject * +faulthandler__sigfpe(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return faulthandler__sigfpe_impl(module); +} + +PyDoc_STRVAR(faulthandler__sigabrt__doc__, +"_sigabrt($module, /)\n" +"--\n" +"\n" +"Raise a SIGABRT signal."); + +#define FAULTHANDLER__SIGABRT_METHODDEF \ + {"_sigabrt", (PyCFunction)faulthandler__sigabrt, METH_NOARGS, faulthandler__sigabrt__doc__}, + +static PyObject * +faulthandler__sigabrt_impl(PyObject *module); + +static PyObject * +faulthandler__sigabrt(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return faulthandler__sigabrt_impl(module); +} + +#if defined(FAULTHANDLER_USE_ALT_STACK) + +PyDoc_STRVAR(faulthandler__stack_overflow__doc__, +"_stack_overflow($module, /)\n" +"--\n" +"\n" +"Recursive call to raise a stack overflow."); + +#define FAULTHANDLER__STACK_OVERFLOW_METHODDEF \ + {"_stack_overflow", (PyCFunction)faulthandler__stack_overflow, METH_NOARGS, faulthandler__stack_overflow__doc__}, + +static PyObject * +faulthandler__stack_overflow_impl(PyObject *module); + +static PyObject * +faulthandler__stack_overflow(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return faulthandler__stack_overflow_impl(module); +} + +#endif /* defined(FAULTHANDLER_USE_ALT_STACK) */ + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(faulthandler__raise_exception__doc__, +"_raise_exception($module, code, flags=0, /)\n" +"--\n" +"\n" +"Call RaiseException(code, flags)."); + +#define FAULTHANDLER__RAISE_EXCEPTION_METHODDEF \ + {"_raise_exception", _PyCFunction_CAST(faulthandler__raise_exception), METH_FASTCALL, faulthandler__raise_exception__doc__}, + +static PyObject * +faulthandler__raise_exception_impl(PyObject *module, unsigned int code, + unsigned int flags); + +static PyObject * +faulthandler__raise_exception(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + unsigned int code; + unsigned int flags = 0; + + if (!_PyArg_CheckPositional("_raise_exception", nargs, 1, 2)) { + goto exit; + } + if (!_PyLong_UnsignedInt_Converter(args[0], &code)) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + if (!_PyLong_UnsignedInt_Converter(args[1], &flags)) { + goto exit; + } +skip_optional: + return_value = faulthandler__raise_exception_impl(module, code, flags); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#ifndef FAULTHANDLER_REGISTER_PY_METHODDEF + #define FAULTHANDLER_REGISTER_PY_METHODDEF +#endif /* !defined(FAULTHANDLER_REGISTER_PY_METHODDEF) */ + +#ifndef FAULTHANDLER_UNREGISTER_PY_METHODDEF + #define FAULTHANDLER_UNREGISTER_PY_METHODDEF +#endif /* !defined(FAULTHANDLER_UNREGISTER_PY_METHODDEF) */ + +#ifndef FAULTHANDLER__STACK_OVERFLOW_METHODDEF + #define FAULTHANDLER__STACK_OVERFLOW_METHODDEF +#endif /* !defined(FAULTHANDLER__STACK_OVERFLOW_METHODDEF) */ + +#ifndef FAULTHANDLER__RAISE_EXCEPTION_METHODDEF + #define FAULTHANDLER__RAISE_EXCEPTION_METHODDEF +#endif /* !defined(FAULTHANDLER__RAISE_EXCEPTION_METHODDEF) */ +/*[clinic end generated code: output=14815a5f8afe813f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/fcntlmodule.c.h b/Modules/clinic/fcntlmodule.c.h index 005e9b9e12afd9..718f80bfe736a2 100644 --- a/Modules/clinic/fcntlmodule.c.h +++ b/Modules/clinic/fcntlmodule.c.h @@ -2,24 +2,31 @@ preserve [clinic start generated code]*/ +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + PyDoc_STRVAR(fcntl_fcntl__doc__, "fcntl($module, fd, cmd, arg=0, /)\n" "--\n" "\n" -"Perform the operation `cmd` on file descriptor fd.\n" +"Perform the operation cmd on file descriptor fd.\n" +"\n" +"The values used for cmd are operating system dependent, and are\n" +"available as constants in the fcntl module, using the same names as used\n" +"in the relevant C header files. The argument arg is optional, and\n" +"defaults to 0; it may be an integer, a bytes-like object or a string.\n" +"If arg is given as a string, it will be encoded to binary using the\n" +"UTF-8 encoding.\n" +"\n" +"If the arg given is an integer or if none is specified, the result value\n" +"is an integer corresponding to the return value of the fcntl() call in\n" +"the C code.\n" "\n" -"The values used for `cmd` are operating system dependent, and are available\n" -"as constants in the fcntl module, using the same names as used in\n" -"the relevant C header files. The argument arg is optional, and\n" -"defaults to 0; it may be an int or a string. If arg is given as a string,\n" -"the return value of fcntl is a string of that length, containing the\n" -"resulting value put in the arg buffer by the operating system. The length\n" -"of the arg string is not allowed to exceed 1024 bytes. If the arg given\n" -"is an integer or if none is specified, the result value is an integer\n" -"corresponding to the return value of the fcntl call in the C code."); +"If arg is given as a bytes-like object, the return value of fcntl() is a\n" +"bytes object of that length, containing the resulting value put in the\n" +"arg buffer by the operating system."); #define FCNTL_FCNTL_METHODDEF \ - {"fcntl", (PyCFunction)(void(*)(void))fcntl_fcntl, METH_FASTCALL, fcntl_fcntl__doc__}, + {"fcntl", _PyCFunction_CAST(fcntl_fcntl), METH_FASTCALL, fcntl_fcntl__doc__}, static PyObject * fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg); @@ -32,12 +39,7 @@ fcntl_fcntl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) int code; PyObject *arg = NULL; - if (nargs < 2) { - PyErr_Format(PyExc_TypeError, "fcntl expected at least 2 arguments, got %zd", nargs); - goto exit; - } - if (nargs > 3) { - PyErr_Format(PyExc_TypeError, "fcntl expected at most 3 arguments, got %zd", nargs); + if (!_PyArg_CheckPositional("fcntl", nargs, 2, 3)) { goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -63,37 +65,36 @@ PyDoc_STRVAR(fcntl_ioctl__doc__, "ioctl($module, fd, request, arg=0, mutate_flag=True, /)\n" "--\n" "\n" -"Perform the operation `request` on file descriptor `fd`.\n" +"Perform the operation request on file descriptor fd.\n" "\n" -"The values used for `request` are operating system dependent, and are available\n" -"as constants in the fcntl or termios library modules, using the same names as\n" -"used in the relevant C header files.\n" +"The values used for request are operating system dependent, and are\n" +"available as constants in the fcntl or termios library modules, using\n" +"the same names as used in the relevant C header files.\n" "\n" -"The argument `arg` is optional, and defaults to 0; it may be an int or a\n" -"buffer containing character data (most likely a string or an array).\n" +"The argument arg is optional, and defaults to 0; it may be an integer, a\n" +"bytes-like object or a string. If arg is given as a string, it will be\n" +"encoded to binary using the UTF-8 encoding.\n" "\n" -"If the argument is a mutable buffer (such as an array) and if the\n" -"mutate_flag argument (which is only allowed in this case) is true then the\n" -"buffer is (in effect) passed to the operating system and changes made by\n" -"the OS will be reflected in the contents of the buffer after the call has\n" -"returned. The return value is the integer returned by the ioctl system\n" -"call.\n" +"If the arg given is an integer or if none is specified, the result value\n" +"is an integer corresponding to the return value of the ioctl() call in\n" +"the C code.\n" "\n" -"If the argument is a mutable buffer and the mutable_flag argument is false,\n" -"the behavior is as if a string had been passed.\n" +"If the argument is a mutable buffer (such as a bytearray) and the\n" +"mutate_flag argument is true (default) then the buffer is (in effect)\n" +"passed to the operating system and changes made by the OS will be\n" +"reflected in the contents of the buffer after the call has returned.\n" +"The return value is the integer returned by the ioctl() system call.\n" "\n" -"If the argument is an immutable buffer (most likely a string) then a copy\n" -"of the buffer is passed to the operating system and the return value is a\n" -"string of the same length containing whatever the operating system put in\n" -"the buffer. The length of the arg buffer in this case is not allowed to\n" -"exceed 1024 bytes.\n" +"If the argument is a mutable buffer and the mutable_flag argument is\n" +"false, the behavior is as if an immutable buffer had been passed.\n" "\n" -"If the arg given is an integer or if none is specified, the result value is\n" -"an integer corresponding to the return value of the ioctl call in the C\n" -"code."); +"If the argument is an immutable buffer then a copy of the buffer is\n" +"passed to the operating system and the return value is a bytes object of\n" +"the same length containing whatever the operating system put in the\n" +"buffer."); #define FCNTL_IOCTL_METHODDEF \ - {"ioctl", (PyCFunction)(void(*)(void))fcntl_ioctl, METH_FASTCALL, fcntl_ioctl__doc__}, + {"ioctl", _PyCFunction_CAST(fcntl_ioctl), METH_FASTCALL, fcntl_ioctl__doc__}, static PyObject * fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg, @@ -108,12 +109,7 @@ fcntl_ioctl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *arg = NULL; int mutate_arg = 1; - if (nargs < 2) { - PyErr_Format(PyExc_TypeError, "ioctl expected at least 2 arguments, got %zd", nargs); - goto exit; - } - if (nargs > 4) { - PyErr_Format(PyExc_TypeError, "ioctl expected at most 4 arguments, got %zd", nargs); + if (!_PyArg_CheckPositional("ioctl", nargs, 2, 4)) { goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -121,7 +117,7 @@ fcntl_ioctl(PyObject *module, PyObject *const *args, Py_ssize_t nargs) goto exit; } if (!PyIndex_Check(args[1])) { - PyErr_Format(PyExc_TypeError, "ioctl() argument 2 must be int, not %T", args[1]); + _PyArg_BadArgument("ioctl", "argument 2", "int", args[1]); goto exit; } { @@ -162,13 +158,13 @@ PyDoc_STRVAR(fcntl_flock__doc__, "flock($module, fd, operation, /)\n" "--\n" "\n" -"Perform the lock operation `operation` on file descriptor `fd`.\n" +"Perform the lock operation on file descriptor fd.\n" "\n" "See the Unix manual page for flock(2) for details (On some systems, this\n" "function is emulated using fcntl())."); #define FCNTL_FLOCK_METHODDEF \ - {"flock", (PyCFunction)(void(*)(void))fcntl_flock, METH_FASTCALL, fcntl_flock__doc__}, + {"flock", _PyCFunction_CAST(fcntl_flock), METH_FASTCALL, fcntl_flock__doc__}, static PyObject * fcntl_flock_impl(PyObject *module, int fd, int code); @@ -180,8 +176,7 @@ fcntl_flock(PyObject *module, PyObject *const *args, Py_ssize_t nargs) int fd; int code; - if (nargs != 2) { - PyErr_Format(PyExc_TypeError, "flock expected 2 arguments, got %zd", nargs); + if (!_PyArg_CheckPositional("flock", nargs, 2, 2)) { goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -204,29 +199,29 @@ PyDoc_STRVAR(fcntl_lockf__doc__, "\n" "A wrapper around the fcntl() locking calls.\n" "\n" -"`fd` is the file descriptor of the file to lock or unlock, and operation is one\n" -"of the following values:\n" +"fd is the file descriptor of the file to lock or unlock, and operation\n" +"is one of the following values:\n" "\n" " LOCK_UN - unlock\n" " LOCK_SH - acquire a shared lock\n" " LOCK_EX - acquire an exclusive lock\n" "\n" "When operation is LOCK_SH or LOCK_EX, it can also be bitwise ORed with\n" -"LOCK_NB to avoid blocking on lock acquisition. If LOCK_NB is used and the\n" -"lock cannot be acquired, an OSError will be raised and the exception will\n" -"have an errno attribute set to EACCES or EAGAIN (depending on the operating\n" -"system -- for portability, check for either value).\n" +"LOCK_NB to avoid blocking on lock acquisition. If LOCK_NB is used and\n" +"the lock cannot be acquired, an OSError will be raised and the exception\n" +"will have an errno attribute set to EACCES or EAGAIN (depending on the\n" +"operating system -- for portability, check for either value).\n" "\n" -"`len` is the number of bytes to lock, with the default meaning to lock to\n" -"EOF. `start` is the byte offset, relative to `whence`, to that the lock\n" -"starts. `whence` is as with fileobj.seek(), specifically:\n" +"len is the number of bytes to lock, with the default meaning to lock to\n" +"EOF. start is the byte offset, relative to whence, to that the lock\n" +"starts. whence is as with fileobj.seek(), specifically:\n" "\n" " 0 - relative to the start of the file (SEEK_SET)\n" " 1 - relative to the current buffer position (SEEK_CUR)\n" " 2 - relative to the end of the file (SEEK_END)"); #define FCNTL_LOCKF_METHODDEF \ - {"lockf", (PyCFunction)(void(*)(void))fcntl_lockf, METH_FASTCALL, fcntl_lockf__doc__}, + {"lockf", _PyCFunction_CAST(fcntl_lockf), METH_FASTCALL, fcntl_lockf__doc__}, static PyObject * fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, @@ -242,12 +237,7 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *startobj = NULL; int whence = 0; - if (nargs < 2) { - PyErr_Format(PyExc_TypeError, "lockf expected at least 2 arguments, got %zd", nargs); - goto exit; - } - if (nargs > 5) { - PyErr_Format(PyExc_TypeError, "lockf expected at most 5 arguments, got %zd", nargs); + if (!_PyArg_CheckPositional("lockf", nargs, 2, 5)) { goto exit; } fd = PyObject_AsFileDescriptor(args[0]); @@ -279,4 +269,4 @@ fcntl_lockf(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=bf84289b741e7cf6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c782fcf9dd6690e0 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/gcmodule.c.h b/Modules/clinic/gcmodule.c.h index 53ff9e4faf8b7c..aa743c8f40a565 100644 --- a/Modules/clinic/gcmodule.c.h +++ b/Modules/clinic/gcmodule.c.h @@ -8,7 +8,6 @@ preserve #endif #include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() -#include "pycore_tuple.h" // _PyTuple_FromArray() PyDoc_STRVAR(gc_enable__doc__, "enable($module, /)\n" @@ -324,7 +323,7 @@ gc_get_referrers(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *objs = NULL; - objs = _PyTuple_FromArray(args, nargs); + objs = PyTuple_FromArray(args, nargs); if (objs == NULL) { goto exit; } @@ -355,7 +354,7 @@ gc_get_referents(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *objs = NULL; - objs = _PyTuple_FromArray(args, nargs); + objs = PyTuple_FromArray(args, nargs); if (objs == NULL) { goto exit; } @@ -377,8 +376,8 @@ PyDoc_STRVAR(gc_get_objects__doc__, " generation\n" " Generation to extract the objects from.\n" "\n" -"If generation is not None, return only the objects tracked by the collector\n" -"that are in that generation."); +"If generation is not None, return only the objects tracked by the\n" +"collector that are in that generation."); #define GC_GET_OBJECTS_METHODDEF \ {"get_objects", _PyCFunction_CAST(gc_get_objects), METH_FASTCALL|METH_KEYWORDS, gc_get_objects__doc__}, @@ -521,9 +520,10 @@ PyDoc_STRVAR(gc_freeze__doc__, "\n" "Freeze all current tracked objects and ignore them for future collections.\n" "\n" -"This can be used before a POSIX fork() call to make the gc copy-on-write friendly.\n" -"Note: collection before a POSIX fork() call may free pages for future allocation\n" -"which can cause copy-on-write."); +"This can be used before a POSIX fork() call to make the gc copy-on-write\n" +"friendly.\n" +"Note: collection before a POSIX fork() call may free pages for future\n" +"allocation which can cause copy-on-write."); #define GC_FREEZE_METHODDEF \ {"freeze", (PyCFunction)gc_freeze, METH_NOARGS, gc_freeze__doc__}, @@ -584,4 +584,4 @@ gc_get_freeze_count(PyObject *module, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=96d057eac558e6ca input=a9049054013a1b77]*/ +/*[clinic end generated code: output=756c0e7719b76971 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/hmacmodule.c.h b/Modules/clinic/hmacmodule.c.h index 1ceb2d809e830a..a31d2ab7f04463 100644 --- a/Modules/clinic/hmacmodule.c.h +++ b/Modules/clinic/hmacmodule.c.h @@ -187,8 +187,8 @@ PyDoc_STRVAR(_hmac_HMAC_hexdigest__doc__, "\n" "Return hexadecimal digest of the bytes passed to the update() method so far.\n" "\n" -"This may be used to exchange the value safely in email or other non-binary\n" -"environments.\n" +"This may be used to exchange the value safely in email or other\n" +"non-binary environments.\n" "\n" "This method may raise a MemoryError."); @@ -670,4 +670,4 @@ _hmac_compute_blake2b_32(PyObject *module, PyObject *const *args, Py_ssize_t nar exit: return return_value; } -/*[clinic end generated code: output=30c0614482d963f5 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6ec5948df1c5569a input=a9049054013a1b77]*/ diff --git a/Modules/clinic/itertoolsmodule.c.h b/Modules/clinic/itertoolsmodule.c.h index 0af82e7eb05be8..d9917b47dc9037 100644 --- a/Modules/clinic/itertoolsmodule.c.h +++ b/Modules/clinic/itertoolsmodule.c.h @@ -345,6 +345,11 @@ itertools_tee(PyObject *module, PyObject *const *args, Py_ssize_t nargs) goto exit; } n = ival; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, + "n cannot be negative"); + goto exit; + } } skip_optional: return_value = itertools_tee_impl(module, iterable, n); @@ -569,6 +574,11 @@ itertools_combinations(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto exit; } r = ival; + if (r < 0) { + PyErr_SetString(PyExc_ValueError, + "r cannot be negative"); + goto exit; + } } return_value = itertools_combinations_impl(type, iterable, r); @@ -643,6 +653,11 @@ itertools_combinations_with_replacement(PyTypeObject *type, PyObject *args, PyOb goto exit; } r = ival; + if (r < 0) { + PyErr_SetString(PyExc_ValueError, + "r cannot be negative"); + goto exit; + } } return_value = itertools_combinations_with_replacement_impl(type, iterable, r); @@ -799,8 +814,8 @@ PyDoc_STRVAR(itertools_compress__doc__, "\n" "Return data elements corresponding to true selector elements.\n" "\n" -"Forms a shorter iterator from selected data elements using the selectors to\n" -"choose the data elements."); +"Forms a shorter iterator from selected data elements using the selectors\n" +"to choose the data elements."); static PyObject * itertools_compress_impl(PyTypeObject *type, PyObject *seq1, PyObject *seq2); @@ -965,4 +980,4 @@ itertools_count(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=999758202a532e0a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a34a31f60100e0ff input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mathintegermodule.c.h b/Modules/clinic/mathintegermodule.c.h new file mode 100644 index 00000000000000..29c2a0ac902ea3 --- /dev/null +++ b/Modules/clinic/mathintegermodule.c.h @@ -0,0 +1,159 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +PyDoc_STRVAR(math_integer_gcd__doc__, +"gcd($module, /, *integers)\n" +"--\n" +"\n" +"Greatest Common Divisor."); + +#define MATH_INTEGER_GCD_METHODDEF \ + {"gcd", _PyCFunction_CAST(math_integer_gcd), METH_FASTCALL, math_integer_gcd__doc__}, + +static PyObject * +math_integer_gcd_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length); + +static PyObject * +math_integer_gcd(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject * const *__clinic_args; + Py_ssize_t args_length; + + __clinic_args = args; + args_length = nargs; + return_value = math_integer_gcd_impl(module, __clinic_args, args_length); + + return return_value; +} + +PyDoc_STRVAR(math_integer_lcm__doc__, +"lcm($module, /, *integers)\n" +"--\n" +"\n" +"Least Common Multiple."); + +#define MATH_INTEGER_LCM_METHODDEF \ + {"lcm", _PyCFunction_CAST(math_integer_lcm), METH_FASTCALL, math_integer_lcm__doc__}, + +static PyObject * +math_integer_lcm_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length); + +static PyObject * +math_integer_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject * const *__clinic_args; + Py_ssize_t args_length; + + __clinic_args = args; + args_length = nargs; + return_value = math_integer_lcm_impl(module, __clinic_args, args_length); + + return return_value; +} + +PyDoc_STRVAR(math_integer_isqrt__doc__, +"isqrt($module, n, /)\n" +"--\n" +"\n" +"Return the integer part of the square root of the input."); + +#define MATH_INTEGER_ISQRT_METHODDEF \ + {"isqrt", (PyCFunction)math_integer_isqrt, METH_O, math_integer_isqrt__doc__}, + +PyDoc_STRVAR(math_integer_factorial__doc__, +"factorial($module, n, /)\n" +"--\n" +"\n" +"Find n!."); + +#define MATH_INTEGER_FACTORIAL_METHODDEF \ + {"factorial", (PyCFunction)math_integer_factorial, METH_O, math_integer_factorial__doc__}, + +PyDoc_STRVAR(math_integer_perm__doc__, +"perm($module, n, k=None, /)\n" +"--\n" +"\n" +"Number of ways to choose k items from n items without repetition and with order.\n" +"\n" +"Evaluates to n! / (n - k)! when k <= n and evaluates\n" +"to zero when k > n.\n" +"\n" +"If k is not specified or is None, then k defaults to n\n" +"and the function returns n!.\n" +"\n" +"Raises ValueError if either of the arguments are negative."); + +#define MATH_INTEGER_PERM_METHODDEF \ + {"perm", _PyCFunction_CAST(math_integer_perm), METH_FASTCALL, math_integer_perm__doc__}, + +static PyObject * +math_integer_perm_impl(PyObject *module, PyObject *n, PyObject *k); + +static PyObject * +math_integer_perm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *n; + PyObject *k = Py_None; + + if (!_PyArg_CheckPositional("perm", nargs, 1, 2)) { + goto exit; + } + n = args[0]; + if (nargs < 2) { + goto skip_optional; + } + k = args[1]; +skip_optional: + return_value = math_integer_perm_impl(module, n, k); + +exit: + return return_value; +} + +PyDoc_STRVAR(math_integer_comb__doc__, +"comb($module, n, k, /)\n" +"--\n" +"\n" +"Number of ways to choose k items from n items without repetition and without order.\n" +"\n" +"Evaluates to n! / (k! * (n - k)!) when k <= n and evaluates\n" +"to zero when k > n.\n" +"\n" +"Also called the binomial coefficient because it is equivalent\n" +"to the coefficient of k-th term in polynomial expansion of the\n" +"expression (1 + x)**n.\n" +"\n" +"Raises ValueError if either of the arguments are negative."); + +#define MATH_INTEGER_COMB_METHODDEF \ + {"comb", _PyCFunction_CAST(math_integer_comb), METH_FASTCALL, math_integer_comb__doc__}, + +static PyObject * +math_integer_comb_impl(PyObject *module, PyObject *n, PyObject *k); + +static PyObject * +math_integer_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *n; + PyObject *k; + + if (!_PyArg_CheckPositional("comb", nargs, 2, 2)) { + goto exit; + } + n = args[0]; + k = args[1]; + return_value = math_integer_comb_impl(module, n, k); + +exit: + return return_value; +} +/*[clinic end generated code: output=34697570c923a3af input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h index 246019f2206028..a5e583c180defe 100644 --- a/Modules/clinic/mathmodule.c.h +++ b/Modules/clinic/mathmodule.c.h @@ -8,60 +8,6 @@ preserve #endif #include "pycore_modsupport.h" // _PyArg_CheckPositional() -PyDoc_STRVAR(math_gcd__doc__, -"gcd($module, /, *integers)\n" -"--\n" -"\n" -"Greatest Common Divisor."); - -#define MATH_GCD_METHODDEF \ - {"gcd", _PyCFunction_CAST(math_gcd), METH_FASTCALL, math_gcd__doc__}, - -static PyObject * -math_gcd_impl(PyObject *module, PyObject * const *args, - Py_ssize_t args_length); - -static PyObject * -math_gcd(PyObject *module, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value = NULL; - PyObject * const *__clinic_args; - Py_ssize_t args_length; - - __clinic_args = args; - args_length = nargs; - return_value = math_gcd_impl(module, __clinic_args, args_length); - - return return_value; -} - -PyDoc_STRVAR(math_lcm__doc__, -"lcm($module, /, *integers)\n" -"--\n" -"\n" -"Least Common Multiple."); - -#define MATH_LCM_METHODDEF \ - {"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm__doc__}, - -static PyObject * -math_lcm_impl(PyObject *module, PyObject * const *args, - Py_ssize_t args_length); - -static PyObject * -math_lcm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value = NULL; - PyObject * const *__clinic_args; - Py_ssize_t args_length; - - __clinic_args = args; - args_length = nargs; - return_value = math_lcm_impl(module, __clinic_args, args_length); - - return return_value; -} - PyDoc_STRVAR(math_ceil__doc__, "ceil($module, x, /)\n" "--\n" @@ -235,24 +181,6 @@ PyDoc_STRVAR(math_fsum__doc__, #define MATH_FSUM_METHODDEF \ {"fsum", (PyCFunction)math_fsum, METH_O, math_fsum__doc__}, -PyDoc_STRVAR(math_isqrt__doc__, -"isqrt($module, n, /)\n" -"--\n" -"\n" -"Return the integer part of the square root of the input."); - -#define MATH_ISQRT_METHODDEF \ - {"isqrt", (PyCFunction)math_isqrt, METH_O, math_isqrt__doc__}, - -PyDoc_STRVAR(math_factorial__doc__, -"factorial($module, n, /)\n" -"--\n" -"\n" -"Find n!."); - -#define MATH_FACTORIAL_METHODDEF \ - {"factorial", (PyCFunction)math_factorial, METH_O, math_factorial__doc__}, - PyDoc_STRVAR(math_trunc__doc__, "trunc($module, x, /)\n" "--\n" @@ -270,8 +198,9 @@ PyDoc_STRVAR(math_frexp__doc__, "\n" "Return the mantissa and exponent of x, as pair (m, e).\n" "\n" -"m is a float and e is an int, such that x = m * 2.**e.\n" -"If x is 0, m and e are both 0. Else 0.5 <= abs(m) < 1.0."); +"If x is a finite nonzero number, then m is a float with\n" +"0.5 <= abs(m) < 1.0 and an integer e is such that\n" +"x == m * 2**e exactly. Else, return (x, 0)."); #define MATH_FREXP_METHODDEF \ {"frexp", (PyCFunction)math_frexp, METH_O, math_frexp__doc__}, @@ -1107,89 +1036,6 @@ math_prod(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k return return_value; } -PyDoc_STRVAR(math_perm__doc__, -"perm($module, n, k=None, /)\n" -"--\n" -"\n" -"Number of ways to choose k items from n items without repetition and with order.\n" -"\n" -"Evaluates to n! / (n - k)! when k <= n and evaluates\n" -"to zero when k > n.\n" -"\n" -"If k is not specified or is None, then k defaults to n\n" -"and the function returns n!.\n" -"\n" -"Raises TypeError if either of the arguments are not integers.\n" -"Raises ValueError if either of the arguments are negative."); - -#define MATH_PERM_METHODDEF \ - {"perm", _PyCFunction_CAST(math_perm), METH_FASTCALL, math_perm__doc__}, - -static PyObject * -math_perm_impl(PyObject *module, PyObject *n, PyObject *k); - -static PyObject * -math_perm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value = NULL; - PyObject *n; - PyObject *k = Py_None; - - if (!_PyArg_CheckPositional("perm", nargs, 1, 2)) { - goto exit; - } - n = args[0]; - if (nargs < 2) { - goto skip_optional; - } - k = args[1]; -skip_optional: - return_value = math_perm_impl(module, n, k); - -exit: - return return_value; -} - -PyDoc_STRVAR(math_comb__doc__, -"comb($module, n, k, /)\n" -"--\n" -"\n" -"Number of ways to choose k items from n items without repetition and without order.\n" -"\n" -"Evaluates to n! / (k! * (n - k)!) when k <= n and evaluates\n" -"to zero when k > n.\n" -"\n" -"Also called the binomial coefficient because it is equivalent\n" -"to the coefficient of k-th term in polynomial expansion of the\n" -"expression (1 + x)**n.\n" -"\n" -"Raises TypeError if either of the arguments are not integers.\n" -"Raises ValueError if either of the arguments are negative."); - -#define MATH_COMB_METHODDEF \ - {"comb", _PyCFunction_CAST(math_comb), METH_FASTCALL, math_comb__doc__}, - -static PyObject * -math_comb_impl(PyObject *module, PyObject *n, PyObject *k); - -static PyObject * -math_comb(PyObject *module, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value = NULL; - PyObject *n; - PyObject *k; - - if (!_PyArg_CheckPositional("comb", nargs, 2, 2)) { - goto exit; - } - n = args[0]; - k = args[1]; - return_value = math_comb_impl(module, n, k); - -exit: - return return_value; -} - PyDoc_STRVAR(math_nextafter__doc__, "nextafter($module, x, y, /, *, steps=None)\n" "--\n" @@ -1198,8 +1044,8 @@ PyDoc_STRVAR(math_nextafter__doc__, "\n" "If steps is not specified or is None, it defaults to 1.\n" "\n" -"Raises a TypeError, if x or y is not a double, or if steps is not an integer.\n" -"Raises ValueError if steps is negative."); +"Raises a TypeError, if x or y is not a double, or if steps is not\n" +"an integer. Raises ValueError if steps is negative."); #define MATH_NEXTAFTER_METHODDEF \ {"nextafter", _PyCFunction_CAST(math_nextafter), METH_FASTCALL|METH_KEYWORDS, math_nextafter__doc__}, @@ -1318,4 +1164,4 @@ math_ulp(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=4fb180d4c25ff8fa input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3452ce8caa2d1bd7 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/mmapmodule.c.h b/Modules/clinic/mmapmodule.c.h new file mode 100644 index 00000000000000..98c5bf6a2fb146 --- /dev/null +++ b/Modules/clinic/mmapmodule.c.h @@ -0,0 +1,884 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + +PyDoc_STRVAR(mmap_mmap_close__doc__, +"close($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_CLOSE_METHODDEF \ + {"close", (PyCFunction)mmap_mmap_close, METH_NOARGS, mmap_mmap_close__doc__}, + +static PyObject * +mmap_mmap_close_impl(mmap_object *self); + +static PyObject * +mmap_mmap_close(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_close_impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_read_byte__doc__, +"read_byte($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_READ_BYTE_METHODDEF \ + {"read_byte", (PyCFunction)mmap_mmap_read_byte, METH_NOARGS, mmap_mmap_read_byte__doc__}, + +static PyObject * +mmap_mmap_read_byte_impl(mmap_object *self); + +static PyObject * +mmap_mmap_read_byte(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_read_byte_impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_readline__doc__, +"readline($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_READLINE_METHODDEF \ + {"readline", (PyCFunction)mmap_mmap_readline, METH_NOARGS, mmap_mmap_readline__doc__}, + +static PyObject * +mmap_mmap_readline_impl(mmap_object *self); + +static PyObject * +mmap_mmap_readline(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_readline_impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_read__doc__, +"read($self, n=None, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_READ_METHODDEF \ + {"read", _PyCFunction_CAST(mmap_mmap_read), METH_FASTCALL, mmap_mmap_read__doc__}, + +static PyObject * +mmap_mmap_read_impl(mmap_object *self, Py_ssize_t num_bytes); + +static PyObject * +mmap_mmap_read(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t num_bytes = PY_SSIZE_T_MAX; + + if (!_PyArg_CheckPositional("read", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + if (!_Py_convert_optional_to_ssize_t(args[0], &num_bytes)) { + goto exit; + } +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_read_impl((mmap_object *)self, num_bytes); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_find__doc__, +"find($self, view, start=None, end=None, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_FIND_METHODDEF \ + {"find", _PyCFunction_CAST(mmap_mmap_find), METH_FASTCALL, mmap_mmap_find__doc__}, + +static PyObject * +mmap_mmap_find_impl(mmap_object *self, Py_buffer *view, PyObject *start, + PyObject *end); + +static PyObject * +mmap_mmap_find(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_buffer view = {NULL, NULL}; + PyObject *start = Py_None; + PyObject *end = Py_None; + + if (!_PyArg_CheckPositional("find", nargs, 1, 3)) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &view, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + start = args[1]; + if (nargs < 3) { + goto skip_optional; + } + end = args[2]; +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_find_impl((mmap_object *)self, &view, start, end); + Py_END_CRITICAL_SECTION(); + +exit: + /* Cleanup for view */ + if (view.obj) { + PyBuffer_Release(&view); + } + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_rfind__doc__, +"rfind($self, view, start=None, end=None, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_RFIND_METHODDEF \ + {"rfind", _PyCFunction_CAST(mmap_mmap_rfind), METH_FASTCALL, mmap_mmap_rfind__doc__}, + +static PyObject * +mmap_mmap_rfind_impl(mmap_object *self, Py_buffer *view, PyObject *start, + PyObject *end); + +static PyObject * +mmap_mmap_rfind(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_buffer view = {NULL, NULL}; + PyObject *start = Py_None; + PyObject *end = Py_None; + + if (!_PyArg_CheckPositional("rfind", nargs, 1, 3)) { + goto exit; + } + if (PyObject_GetBuffer(args[0], &view, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + start = args[1]; + if (nargs < 3) { + goto skip_optional; + } + end = args[2]; +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_rfind_impl((mmap_object *)self, &view, start, end); + Py_END_CRITICAL_SECTION(); + +exit: + /* Cleanup for view */ + if (view.obj) { + PyBuffer_Release(&view); + } + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_write__doc__, +"write($self, bytes, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_WRITE_METHODDEF \ + {"write", (PyCFunction)mmap_mmap_write, METH_O, mmap_mmap_write__doc__}, + +static PyObject * +mmap_mmap_write_impl(mmap_object *self, Py_buffer *data); + +static PyObject * +mmap_mmap_write(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_buffer data = {NULL, NULL}; + + if (PyObject_GetBuffer(arg, &data, PyBUF_SIMPLE) != 0) { + goto exit; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_write_impl((mmap_object *)self, &data); + Py_END_CRITICAL_SECTION(); + +exit: + /* Cleanup for data */ + if (data.obj) { + PyBuffer_Release(&data); + } + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_write_byte__doc__, +"write_byte($self, byte, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_WRITE_BYTE_METHODDEF \ + {"write_byte", (PyCFunction)mmap_mmap_write_byte, METH_O, mmap_mmap_write_byte__doc__}, + +static PyObject * +mmap_mmap_write_byte_impl(mmap_object *self, unsigned char value); + +static PyObject * +mmap_mmap_write_byte(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + unsigned char value; + + { + long ival = PyLong_AsLong(arg); + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + else if (ival < 0) { + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is less than minimum"); + goto exit; + } + else if (ival > UCHAR_MAX) { + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is greater than maximum"); + goto exit; + } + else { + value = (unsigned char) ival; + } + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_write_byte_impl((mmap_object *)self, value); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_size__doc__, +"size($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_SIZE_METHODDEF \ + {"size", (PyCFunction)mmap_mmap_size, METH_NOARGS, mmap_mmap_size__doc__}, + +static PyObject * +mmap_mmap_size_impl(mmap_object *self); + +static PyObject * +mmap_mmap_size(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_size_impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if (defined(MS_WINDOWS) || defined(HAVE_MREMAP)) + +PyDoc_STRVAR(mmap_mmap_resize__doc__, +"resize($self, newsize, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_RESIZE_METHODDEF \ + {"resize", (PyCFunction)mmap_mmap_resize, METH_O, mmap_mmap_resize__doc__}, + +static PyObject * +mmap_mmap_resize_impl(mmap_object *self, Py_ssize_t new_size); + +static PyObject * +mmap_mmap_resize(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + Py_ssize_t new_size; + + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(arg); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + new_size = ival; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_resize_impl((mmap_object *)self, new_size); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +#endif /* (defined(MS_WINDOWS) || defined(HAVE_MREMAP)) */ + +PyDoc_STRVAR(mmap_mmap_tell__doc__, +"tell($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_TELL_METHODDEF \ + {"tell", (PyCFunction)mmap_mmap_tell, METH_NOARGS, mmap_mmap_tell__doc__}, + +static PyObject * +mmap_mmap_tell_impl(mmap_object *self); + +static PyObject * +mmap_mmap_tell(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_tell_impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_flush__doc__, +"flush($self, offset=0, size=-1, /, *, flags=0)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_FLUSH_METHODDEF \ + {"flush", _PyCFunction_CAST(mmap_mmap_flush), METH_FASTCALL|METH_KEYWORDS, mmap_mmap_flush__doc__}, + +static PyObject * +mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size, + int flags); + +static PyObject * +mmap_mmap_flush(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(flags), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "", "flags", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "flush", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + Py_ssize_t offset = 0; + Py_ssize_t size = -1; + int flags = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (nargs < 1) { + goto skip_optional_posonly; + } + noptargs--; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + offset = ival; + } + if (nargs < 2) { + goto skip_optional_posonly; + } + noptargs--; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + size = ival; + } +skip_optional_posonly: + if (!noptargs) { + goto skip_optional_kwonly; + } + flags = PyLong_AsInt(args[2]); + if (flags == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_kwonly: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_flush_impl((mmap_object *)self, offset, size, flags); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_seek__doc__, +"seek($self, pos, whence=0, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_SEEK_METHODDEF \ + {"seek", _PyCFunction_CAST(mmap_mmap_seek), METH_FASTCALL, mmap_mmap_seek__doc__}, + +static PyObject * +mmap_mmap_seek_impl(mmap_object *self, Py_ssize_t dist, int how); + +static PyObject * +mmap_mmap_seek(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t dist; + int how = 0; + + if (!_PyArg_CheckPositional("seek", nargs, 1, 2)) { + goto exit; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + dist = ival; + } + if (nargs < 2) { + goto skip_optional; + } + how = PyLong_AsInt(args[1]); + if (how == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_seek_impl((mmap_object *)self, dist, how); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_set_name__doc__, +"set_name($self, name, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_SET_NAME_METHODDEF \ + {"set_name", (PyCFunction)mmap_mmap_set_name, METH_O, mmap_mmap_set_name__doc__}, + +static PyObject * +mmap_mmap_set_name_impl(mmap_object *self, const char *name); + +static PyObject * +mmap_mmap_set_name(PyObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + const char *name; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("set_name", "argument", "str", arg); + goto exit; + } + Py_ssize_t name_length; + name = PyUnicode_AsUTF8AndSize(arg, &name_length); + if (name == NULL) { + goto exit; + } + if (strlen(name) != (size_t)name_length) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + goto exit; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_set_name_impl((mmap_object *)self, name); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap_seekable__doc__, +"seekable($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_SEEKABLE_METHODDEF \ + {"seekable", (PyCFunction)mmap_mmap_seekable, METH_NOARGS, mmap_mmap_seekable__doc__}, + +static PyObject * +mmap_mmap_seekable_impl(mmap_object *self); + +static PyObject * +mmap_mmap_seekable(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return mmap_mmap_seekable_impl((mmap_object *)self); +} + +PyDoc_STRVAR(mmap_mmap_move__doc__, +"move($self, dest, src, count, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_MOVE_METHODDEF \ + {"move", _PyCFunction_CAST(mmap_mmap_move), METH_FASTCALL, mmap_mmap_move__doc__}, + +static PyObject * +mmap_mmap_move_impl(mmap_object *self, Py_ssize_t dest, Py_ssize_t src, + Py_ssize_t cnt); + +static PyObject * +mmap_mmap_move(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t dest; + Py_ssize_t src; + Py_ssize_t cnt; + + if (!_PyArg_CheckPositional("move", nargs, 3, 3)) { + goto exit; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[0]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + dest = ival; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + src = ival; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + cnt = ival; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_move_impl((mmap_object *)self, dest, src, cnt); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(mmap_mmap___enter____doc__, +"__enter__($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP___ENTER___METHODDEF \ + {"__enter__", (PyCFunction)mmap_mmap___enter__, METH_NOARGS, mmap_mmap___enter____doc__}, + +static PyObject * +mmap_mmap___enter___impl(mmap_object *self); + +static PyObject * +mmap_mmap___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap___enter___impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(mmap_mmap___exit____doc__, +"__exit__($self, exc_type, exc_value, traceback, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP___EXIT___METHODDEF \ + {"__exit__", _PyCFunction_CAST(mmap_mmap___exit__), METH_FASTCALL, mmap_mmap___exit____doc__}, + +static PyObject * +mmap_mmap___exit___impl(mmap_object *self, PyObject *exc_type, + PyObject *exc_value, PyObject *traceback); + +static PyObject * +mmap_mmap___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *exc_type; + PyObject *exc_value; + PyObject *traceback; + + if (!_PyArg_CheckPositional("__exit__", nargs, 3, 3)) { + goto exit; + } + exc_type = args[0]; + exc_value = args[1]; + traceback = args[2]; + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap___exit___impl((mmap_object *)self, exc_type, exc_value, traceback); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(mmap_mmap___sizeof____doc__, +"__sizeof__($self, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP___SIZEOF___METHODDEF \ + {"__sizeof__", (PyCFunction)mmap_mmap___sizeof__, METH_NOARGS, mmap_mmap___sizeof____doc__}, + +static PyObject * +mmap_mmap___sizeof___impl(mmap_object *self); + +static PyObject * +mmap_mmap___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap___sizeof___impl((mmap_object *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if (defined(MS_WINDOWS) && defined(Py_DEBUG)) + +PyDoc_STRVAR(mmap_mmap__protect__doc__, +"_protect($self, flNewProtect, start, length, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP__PROTECT_METHODDEF \ + {"_protect", _PyCFunction_CAST(mmap_mmap__protect), METH_FASTCALL, mmap_mmap__protect__doc__}, + +static PyObject * +mmap_mmap__protect_impl(mmap_object *self, unsigned int flNewProtect, + Py_ssize_t start, Py_ssize_t length); + +static PyObject * +mmap_mmap__protect(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + unsigned int flNewProtect; + Py_ssize_t start; + Py_ssize_t length; + + if (!_PyArg_CheckPositional("_protect", nargs, 3, 3)) { + goto exit; + } + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[0], &flNewProtect, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + start = ival; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + length = ival; + } + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap__protect_impl((mmap_object *)self, flNewProtect, start, length); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +#endif /* (defined(MS_WINDOWS) && defined(Py_DEBUG)) */ + +#if defined(HAVE_MADVISE) + +PyDoc_STRVAR(mmap_mmap_madvise__doc__, +"madvise($self, option, start=0, length=None, /)\n" +"--\n" +"\n"); + +#define MMAP_MMAP_MADVISE_METHODDEF \ + {"madvise", _PyCFunction_CAST(mmap_mmap_madvise), METH_FASTCALL, mmap_mmap_madvise__doc__}, + +static PyObject * +mmap_mmap_madvise_impl(mmap_object *self, int option, Py_ssize_t start, + PyObject *length_obj); + +static PyObject * +mmap_mmap_madvise(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + int option; + Py_ssize_t start = 0; + PyObject *length_obj = Py_None; + + if (!_PyArg_CheckPositional("madvise", nargs, 1, 3)) { + goto exit; + } + option = PyLong_AsInt(args[0]); + if (option == -1 && PyErr_Occurred()) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + start = ival; + } + if (nargs < 3) { + goto skip_optional; + } + length_obj = args[2]; +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = mmap_mmap_madvise_impl((mmap_object *)self, option, start, length_obj); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +#endif /* defined(HAVE_MADVISE) */ + +#ifndef MMAP_MMAP_RESIZE_METHODDEF + #define MMAP_MMAP_RESIZE_METHODDEF +#endif /* !defined(MMAP_MMAP_RESIZE_METHODDEF) */ + +#ifndef MMAP_MMAP___SIZEOF___METHODDEF + #define MMAP_MMAP___SIZEOF___METHODDEF +#endif /* !defined(MMAP_MMAP___SIZEOF___METHODDEF) */ + +#ifndef MMAP_MMAP__PROTECT_METHODDEF + #define MMAP_MMAP__PROTECT_METHODDEF +#endif /* !defined(MMAP_MMAP__PROTECT_METHODDEF) */ + +#ifndef MMAP_MMAP_MADVISE_METHODDEF + #define MMAP_MMAP_MADVISE_METHODDEF +#endif /* !defined(MMAP_MMAP_MADVISE_METHODDEF) */ +/*[clinic end generated code: output=1122b93314aebc5c input=a9049054013a1b77]*/ diff --git a/Modules/clinic/overlapped.c.h b/Modules/clinic/overlapped.c.h index 7e2480bdace38d..ba41eab15650e8 100644 --- a/Modules/clinic/overlapped.c.h +++ b/Modules/clinic/overlapped.c.h @@ -529,8 +529,9 @@ PyDoc_STRVAR(_overlapped_Overlapped_getresult__doc__, "\n" "Retrieve result of operation.\n" "\n" -"If wait is true then it blocks until the operation is finished. If wait\n" -"is false and the operation is still pending then an error is raised."); +"If wait is true then it blocks until the operation is finished. If\n" +"wait is false and the operation is still pending then an error is\n" +"raised."); #define _OVERLAPPED_OVERLAPPED_GETRESULT_METHODDEF \ {"getresult", _PyCFunction_CAST(_overlapped_Overlapped_getresult), METH_FASTCALL, _overlapped_Overlapped_getresult__doc__}, @@ -1242,4 +1243,4 @@ _overlapped_Overlapped_WSARecvFromInto(PyObject *self, PyObject *const *args, Py return return_value; } -/*[clinic end generated code: output=3e4cb2b55342cd96 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0ecaf45a09539599 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 8af9e1db781c8f..00a4f6009186af 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -186,6 +186,140 @@ os_lstat(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kw return return_value; } +#if defined(HAVE_STATX) + +PyDoc_STRVAR(os_statx__doc__, +"statx($module, /, path, mask, *, flags=0, dir_fd=None,\n" +" follow_symlinks=True)\n" +"--\n" +"\n" +"Perform a statx system call on the given path.\n" +"\n" +" path\n" +" Path to be examined; can be string, bytes, a path-like object or\n" +" open-file-descriptor int.\n" +" mask\n" +" A bitmask of STATX_* constants defining the requested information.\n" +" flags\n" +" A bitmask of AT_NO_AUTOMOUNT and/or AT_STATX_* flags.\n" +" dir_fd\n" +" If not None, it should be a file descriptor open to a directory,\n" +" and path should be a relative string; path will then be relative to\n" +" that directory.\n" +" follow_symlinks\n" +" If False, and the last element of the path is a symbolic link,\n" +" statx will examine the symbolic link itself instead of the file\n" +" the link points to.\n" +"\n" +"It\'s an error to use dir_fd or follow_symlinks when specifying path as\n" +" an open file descriptor."); + +#define OS_STATX_METHODDEF \ + {"statx", _PyCFunction_CAST(os_statx), METH_FASTCALL|METH_KEYWORDS, os_statx__doc__}, + +static PyObject * +os_statx_impl(PyObject *module, path_t *path, unsigned int mask, int flags, + int dir_fd, int follow_symlinks); + +static PyObject * +os_statx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 5 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(path), &_Py_ID(mask), &_Py_ID(flags), &_Py_ID(dir_fd), &_Py_ID(follow_symlinks), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", "mask", "flags", "dir_fd", "follow_symlinks", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "statx", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + path_t path = PATH_T_INITIALIZE_P("statx", "path", 0, 0, 0, 1); + unsigned int mask; + int flags = 0; + int dir_fd = DEFAULT_DIR_FD; + int follow_symlinks = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!path_converter(args[0], &path)) { + goto exit; + } + { + Py_ssize_t _bytes = PyLong_AsNativeBytes(args[1], &mask, sizeof(unsigned int), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_ALLOW_INDEX | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + if (_bytes < 0) { + goto exit; + } + if ((size_t)_bytes > sizeof(unsigned int)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "integer value out of range", 1) < 0) + { + goto exit; + } + } + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (args[2]) { + flags = PyLong_AsInt(args[2]); + if (flags == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (args[3]) { + if (!dir_fd_converter(args[3], &dir_fd)) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + follow_symlinks = PyObject_IsTrue(args[4]); + if (follow_symlinks < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = os_statx_impl(module, &path, mask, flags, dir_fd, follow_symlinks); + +exit: + /* Cleanup for path */ + path_cleanup(&path); + + return return_value; +} + +#endif /* defined(HAVE_STATX) */ + PyDoc_STRVAR(os_access__doc__, "access($module, /, path, mode, *, dir_fd=None, effective_ids=False,\n" " follow_symlinks=True)\n" @@ -215,8 +349,8 @@ PyDoc_STRVAR(os_access__doc__, " NotImplementedError.\n" "\n" "Note that most operations will use the effective uid/gid, therefore this\n" -" routine can be used in a suid/sgid environment to test if the invoking user\n" -" has the specified access to the path."); +" routine can be used in a suid/sgid environment to test if the invoking\n" +" user has the specified access to the path."); #define OS_ACCESS_METHODDEF \ {"access", _PyCFunction_CAST(os_access), METH_FASTCALL|METH_KEYWORDS, os_access__doc__}, @@ -378,9 +512,9 @@ PyDoc_STRVAR(os_chdir__doc__, "\n" "Change the current working directory to the specified path.\n" "\n" -"path may always be specified as a string.\n" -"On some platforms, path may also be specified as an open file descriptor.\n" -"If this functionality is unavailable, using it raises an exception."); +"path may always be specified as a string. On some platforms, path may\n" +"also be specified as an open file descriptor. If this functionality is\n" +"unavailable, using it raises an exception."); #define OS_CHDIR_METHODDEF \ {"chdir", _PyCFunction_CAST(os_chdir), METH_FASTCALL|METH_KEYWORDS, os_chdir__doc__}, @@ -515,14 +649,15 @@ PyDoc_STRVAR(os_chmod__doc__, "Change the access permissions of a file.\n" "\n" " path\n" -" Path to be modified. May always be specified as a str, bytes, or a path-like object.\n" -" On some platforms, path may also be specified as an open file descriptor.\n" -" If this functionality is unavailable, using it raises an exception.\n" +" Path to be modified. May always be specified as a str, bytes, or\n" +" a path-like object. On some platforms, path may also be specified\n" +" as an open file descriptor. If this functionality is unavailable,\n" +" using it raises an exception.\n" " mode\n" " Operating-system mode bitfield.\n" -" Be careful when using number literals for *mode*. The conventional UNIX notation for\n" -" numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in\n" -" Python.\n" +" Be careful when using number literals for *mode*. The conventional\n" +" UNIX notation for numeric modes uses an octal base, which needs to\n" +" be indicated with a ``0o`` prefix in Python.\n" " dir_fd\n" " If not None, it should be a file descriptor open to a directory,\n" " and path should be relative; path will then be relative to that\n" @@ -631,9 +766,9 @@ PyDoc_STRVAR(os_fchmod__doc__, " The file descriptor of the file to be modified.\n" " mode\n" " Operating-system mode bitfield.\n" -" Be careful when using number literals for *mode*. The conventional UNIX notation for\n" -" numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in\n" -" Python.\n" +" Be careful when using number literals for *mode*. The conventional\n" +" UNIX notation for numeric modes uses an octal base, which needs to\n" +" be indicated with a ``0o`` prefix in Python.\n" "\n" "Equivalent to os.chmod(fd, mode)."); @@ -707,8 +842,8 @@ PyDoc_STRVAR(os_lchmod__doc__, "\n" "Change the access permissions of a file, without following symbolic links.\n" "\n" -"If path is a symlink, this affects the link itself rather than the target.\n" -"Equivalent to chmod(path, mode, follow_symlinks=False).\""); +"If path is a symlink, this affects the link itself rather than the\n" +"target. Equivalent to chmod(path, mode, follow_symlinks=False)."); #define OS_LCHMOD_METHODDEF \ {"lchmod", _PyCFunction_CAST(os_lchmod), METH_FASTCALL|METH_KEYWORDS, os_lchmod__doc__}, @@ -782,9 +917,9 @@ PyDoc_STRVAR(os_chflags__doc__, "\n" "Set file flags.\n" "\n" -"If follow_symlinks is False, and the last element of the path is a symbolic\n" -" link, chflags will change flags on the symbolic link itself instead of the\n" -" file the link points to.\n" +"If follow_symlinks is False, and the last element of the path is\n" +"a symbolic link, chflags() will change flags on the symbolic link itself\n" +"instead of the file the link points to.\n" "follow_symlinks may not be implemented on your platform. If it is\n" "unavailable, using it will raise a NotImplementedError."); @@ -1195,10 +1330,11 @@ PyDoc_STRVAR(os_chown__doc__, "chown($module, /, path, uid, gid, *, dir_fd=None, follow_symlinks=True)\n" "--\n" "\n" -"Change the owner and group id of path to the numeric uid and gid.\\\n" +"Change the owner and group id of path to the numeric uid and gid.\n" "\n" " path\n" -" Path to be examined; can be string, bytes, a path-like object, or open-file-descriptor int.\n" +" Path to be examined; can be string, bytes, a path-like object, or\n" +" open-file-descriptor int.\n" " dir_fd\n" " If not None, it should be a file descriptor open to a directory,\n" " and path should be relative; path will then be relative to that\n" @@ -1208,18 +1344,19 @@ PyDoc_STRVAR(os_chown__doc__, " stat will examine the symbolic link itself instead of the file\n" " the link points to.\n" "\n" -"path may always be specified as a string.\n" -"On some platforms, path may also be specified as an open file descriptor.\n" -" If this functionality is unavailable, using it raises an exception.\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -" and path should be relative; path will then be relative to that directory.\n" -"If follow_symlinks is False, and the last element of the path is a symbolic\n" -" link, chown will modify the symbolic link itself instead of the file the\n" -" link points to.\n" +"path may always be specified as a string. On some platforms, path may\n" +"also be specified as an open file descriptor. If this functionality is\n" +"unavailable, using it raises an exception.\n" +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative to\n" +"that directory.\n" +"If follow_symlinks is False, and the last element of the path is\n" +"a symbolic link, chown will modify the symbolic link itself instead of\n" +"the file the link points to.\n" "It is an error to use dir_fd or follow_symlinks when specifying path as\n" -" an open file descriptor.\n" -"dir_fd and follow_symlinks may not be implemented on your platform.\n" -" If they are unavailable, using them will raise a NotImplementedError."); +"an open file descriptor.\n" +"dir_fd and follow_symlinks may not be implemented on your platform. If\n" +"they are unavailable, using them will raise a NotImplementedError."); #define OS_CHOWN_METHODDEF \ {"chown", _PyCFunction_CAST(os_chown), METH_FASTCALL|METH_KEYWORDS, os_chown__doc__}, @@ -1507,14 +1644,15 @@ PyDoc_STRVAR(os_link__doc__, "Create a hard link to a file.\n" "\n" "If either src_dir_fd or dst_dir_fd is not None, it should be a file\n" -" descriptor open to a directory, and the respective path string (src or dst)\n" -" should be relative; the path will then be relative to that directory.\n" +"descriptor open to a directory, and the respective path string (src or\n" +"dst) should be relative; the path will then be relative to that\n" +"directory.\n" "If follow_symlinks is False, and the last element of src is a symbolic\n" -" link, link will create a link to the symbolic link itself instead of the\n" -" file the link points to.\n" -"src_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on your\n" -" platform. If they are unavailable, using them will raise a\n" -" NotImplementedError."); +"link, link will create a link to the symbolic link itself instead of the\n" +"file the link points to.\n" +"src_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on\n" +"your platform. If they are unavailable, using them will raise\n" +"a NotImplementedError."); #define OS_LINK_METHODDEF \ {"link", _PyCFunction_CAST(os_link), METH_FASTCALL|METH_KEYWORDS, os_link__doc__}, @@ -1616,13 +1754,13 @@ PyDoc_STRVAR(os_listdir__doc__, "\n" "Return a list containing the names of the files in the directory.\n" "\n" -"path can be specified as either str, bytes, or a path-like object. If path is bytes,\n" -" the filenames returned will also be bytes; in all other circumstances\n" -" the filenames returned will be str.\n" +"path can be specified as either str, bytes, or a path-like object. If\n" +"path is bytes, the filenames returned will also be bytes; in all other\n" +"circumstances the filenames returned will be str.\n" "If path is None, uses the path=\'.\'.\n" -"On some platforms, path may also be specified as an open file descriptor;\\\n" -" the file descriptor must refer to a directory.\n" -" If this functionality is unavailable, using it raises NotImplementedError.\n" +"On some platforms, path may also be specified as an open file\n" +"descriptor; the file descriptor must refer to a directory. If this\n" +"functionality is unavailable, using it raises NotImplementedError.\n" "\n" "The list is in arbitrary order. It does not include the special\n" "entries \'.\' and \'..\' even if they are present in the directory."); @@ -2575,13 +2713,14 @@ PyDoc_STRVAR(os_mkdir__doc__, "\n" "Create a directory.\n" "\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -" and path should be relative; path will then be relative to that directory.\n" -"dir_fd may not be implemented on your platform.\n" -" If it is unavailable, using it will raise a NotImplementedError.\n" +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative to\n" +"that directory.\n" +"dir_fd may not be implemented on your platform. If it is unavailable,\n" +"using it will raise a NotImplementedError.\n" "\n" -"The mode argument is ignored on Windows. Where it is used, the current umask\n" -"value is first masked out."); +"The mode argument is ignored on Windows. Where it is used, the current\n" +"umask value is first masked out."); #define OS_MKDIR_METHODDEF \ {"mkdir", _PyCFunction_CAST(os_mkdir), METH_FASTCALL|METH_KEYWORDS, os_mkdir__doc__}, @@ -2847,10 +2986,11 @@ PyDoc_STRVAR(os_rename__doc__, "Rename a file or directory.\n" "\n" "If either src_dir_fd or dst_dir_fd is not None, it should be a file\n" -" descriptor open to a directory, and the respective path string (src or dst)\n" -" should be relative; the path will then be relative to that directory.\n" +"descriptor open to a directory, and the respective path string (src or\n" +"dst) should be relative; the path will then be relative to that\n" +"directory.\n" "src_dir_fd and dst_dir_fd, may not be implemented on your platform.\n" -" If they are unavailable, using them will raise a NotImplementedError."); +"If they are unavailable, using them will raise a NotImplementedError."); #define OS_RENAME_METHODDEF \ {"rename", _PyCFunction_CAST(os_rename), METH_FASTCALL|METH_KEYWORDS, os_rename__doc__}, @@ -2941,10 +3081,11 @@ PyDoc_STRVAR(os_replace__doc__, "Rename a file or directory, overwriting the destination.\n" "\n" "If either src_dir_fd or dst_dir_fd is not None, it should be a file\n" -" descriptor open to a directory, and the respective path string (src or dst)\n" -" should be relative; the path will then be relative to that directory.\n" +"descriptor open to a directory, and the respective path string (src or\n" +"dst) should be relative; the path will then be relative to that\n" +"directory.\n" "src_dir_fd and dst_dir_fd, may not be implemented on your platform.\n" -" If they are unavailable, using them will raise a NotImplementedError."); +"If they are unavailable, using them will raise a NotImplementedError."); #define OS_REPLACE_METHODDEF \ {"replace", _PyCFunction_CAST(os_replace), METH_FASTCALL|METH_KEYWORDS, os_replace__doc__}, @@ -3034,10 +3175,11 @@ PyDoc_STRVAR(os_rmdir__doc__, "\n" "Remove a directory.\n" "\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -" and path should be relative; path will then be relative to that directory.\n" +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative\n" +"to that directory.\n" "dir_fd may not be implemented on your platform.\n" -" If it is unavailable, using it will raise a NotImplementedError."); +"If it is unavailable, using it will raise a NotImplementedError."); #define OS_RMDIR_METHODDEF \ {"rmdir", _PyCFunction_CAST(os_rmdir), METH_FASTCALL|METH_KEYWORDS, os_rmdir__doc__}, @@ -3292,10 +3434,11 @@ PyDoc_STRVAR(os_unlink__doc__, "\n" "Remove a file (same as remove()).\n" "\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -" and path should be relative; path will then be relative to that directory.\n" +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative to\n" +"that directory.\n" "dir_fd may not be implemented on your platform.\n" -" If it is unavailable, using it will raise a NotImplementedError."); +"If it is unavailable, using it will raise a NotImplementedError."); #define OS_UNLINK_METHODDEF \ {"unlink", _PyCFunction_CAST(os_unlink), METH_FASTCALL|METH_KEYWORDS, os_unlink__doc__}, @@ -3369,10 +3512,11 @@ PyDoc_STRVAR(os_remove__doc__, "\n" "Remove a file (same as unlink()).\n" "\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -" and path should be relative; path will then be relative to that directory.\n" +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative\n" +"to that directory.\n" "dir_fd may not be implemented on your platform.\n" -" If it is unavailable, using it will raise a NotImplementedError."); +"If it is unavailable, using it will raise a NotImplementedError."); #define OS_REMOVE_METHODDEF \ {"remove", _PyCFunction_CAST(os_remove), METH_FASTCALL|METH_KEYWORDS, os_remove__doc__}, @@ -3472,27 +3616,28 @@ PyDoc_STRVAR(os_utime__doc__, "\n" "Set the access and modified time of path.\n" "\n" -"path may always be specified as a string.\n" -"On some platforms, path may also be specified as an open file descriptor.\n" -" If this functionality is unavailable, using it raises an exception.\n" +"path may always be specified as a string. On some platforms, path may\n" +"also be specified as an open file descriptor. If this functionality is\n" +"unavailable, using it raises an exception.\n" "\n" "If times is not None, it must be a tuple (atime, mtime);\n" -" atime and mtime should be expressed as float seconds since the epoch.\n" +"atime and mtime should be expressed as float seconds since the epoch.\n" "If ns is specified, it must be a tuple (atime_ns, mtime_ns);\n" -" atime_ns and mtime_ns should be expressed as integer nanoseconds\n" -" since the epoch.\n" +"atime_ns and mtime_ns should be expressed as integer nanoseconds\n" +"since the epoch.\n" "If times is None and ns is unspecified, utime uses the current time.\n" "Specifying tuples for both times and ns is an error.\n" "\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -" and path should be relative; path will then be relative to that directory.\n" -"If follow_symlinks is False, and the last element of the path is a symbolic\n" -" link, utime will modify the symbolic link itself instead of the file the\n" -" link points to.\n" -"It is an error to use dir_fd or follow_symlinks when specifying path\n" -" as an open file descriptor.\n" +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative to\n" +"that directory.\n" +"If follow_symlinks is False, and the last element of the path is\n" +"a symbolic link, utime will modify the symbolic link itself instead of\n" +"the file the link points to.\n" +"It is an error to use dir_fd or follow_symlinks when specifying path as\n" +"an open file descriptor.\n" "dir_fd and follow_symlinks may not be available on your platform.\n" -" If they are unavailable, using them will raise a NotImplementedError."); +"If they are unavailable, using them will raise a NotImplementedError."); #define OS_UTIME_METHODDEF \ {"utime", _PyCFunction_CAST(os_utime), METH_FASTCALL|METH_KEYWORDS, os_utime__doc__}, @@ -3776,8 +3921,8 @@ os_execve(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k PyDoc_STRVAR(os_posix_spawn__doc__, "posix_spawn($module, path, argv, env, /, *, file_actions=(),\n" -" setpgroup=<unrepresentable>, resetids=False, setsid=False,\n" -" setsigmask=(), setsigdef=(), scheduler=<unrepresentable>)\n" +" setpgroup=None, resetids=False, setsid=False,\n" +" setsigmask=(), setsigdef=(), scheduler=None)\n" "--\n" "\n" "Execute the program specified by path in a new process.\n" @@ -3795,7 +3940,8 @@ PyDoc_STRVAR(os_posix_spawn__doc__, " resetids\n" " If the value is `true` the POSIX_SPAWN_RESETIDS will be activated.\n" " setsid\n" -" If the value is `true` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated.\n" +" If the value is `true` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP\n" +" will be activated.\n" " setsigmask\n" " The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\n" " setsigdef\n" @@ -3929,8 +4075,8 @@ os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje PyDoc_STRVAR(os_posix_spawnp__doc__, "posix_spawnp($module, path, argv, env, /, *, file_actions=(),\n" -" setpgroup=<unrepresentable>, resetids=False, setsid=False,\n" -" setsigmask=(), setsigdef=(), scheduler=<unrepresentable>)\n" +" setpgroup=None, resetids=False, setsid=False,\n" +" setsigmask=(), setsigdef=(), scheduler=None)\n" "--\n" "\n" "Execute the program specified by path in a new process.\n" @@ -3948,7 +4094,8 @@ PyDoc_STRVAR(os_posix_spawnp__doc__, " resetids\n" " If the value is `True` the POSIX_SPAWN_RESETIDS will be activated.\n" " setsid\n" -" If the value is `True` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated.\n" +" If the value is `True` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP\n" +" will be activated.\n" " setsigmask\n" " The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\n" " setsigdef\n" @@ -4819,8 +4966,8 @@ PyDoc_STRVAR(os_posix_openpt__doc__, "Open and return a file descriptor for a master pseudo-terminal device.\n" "\n" "Performs a posix_openpt() C function call. The oflag argument is used to\n" -"set file status flags and file access modes as specified in the manual page\n" -"of posix_openpt() of your system."); +"set file status flags and file access modes as specified in the manual\n" +"page of posix_openpt() of your system."); #define OS_POSIX_OPENPT_METHODDEF \ {"posix_openpt", (PyCFunction)os_posix_openpt, METH_O, os_posix_openpt__doc__}, @@ -5035,7 +5182,8 @@ PyDoc_STRVAR(os_forkpty__doc__, "Returns a tuple of (pid, master_fd).\n" "Like fork(), return pid of 0 to the child process,\n" "and pid of child to the parent process.\n" -"To both, return fd of newly opened pseudo-terminal."); +"To both, return fd of newly opened pseudo-terminal.\n" +"The master_fd is non-inheritable."); #define OS_FORKPTY_METHODDEF \ {"forkpty", (PyCFunction)os_forkpty, METH_NOARGS, os_forkpty__doc__}, @@ -5276,9 +5424,9 @@ PyDoc_STRVAR(os_initgroups__doc__, "\n" "Initialize the group access list.\n" "\n" -"Call the system initgroups() to initialize the group access list with all of\n" -"the groups of which the specified username is a member, plus the specified\n" -"group id."); +"Call the system initgroups() to initialize the group access list with\n" +"all of the groups of which the specified username is a member, plus the\n" +"specified group id."); #define OS_INITGROUPS_METHODDEF \ {"initgroups", _PyCFunction_CAST(os_initgroups), METH_FASTCALL, os_initgroups__doc__}, @@ -5322,9 +5470,9 @@ PyDoc_STRVAR(os_initgroups__doc__, "\n" "Initialize the group access list.\n" "\n" -"Call the system initgroups() to initialize the group access list with all of\n" -"the groups of which the specified username is a member, plus the specified\n" -"group id."); +"Call the system initgroups() to initialize the group access list with\n" +"all of the groups of which the specified username is a member, plus the\n" +"specified group id."); #define OS_INITGROUPS_METHODDEF \ {"initgroups", _PyCFunction_CAST(os_initgroups), METH_FASTCALL, os_initgroups__doc__}, @@ -5477,7 +5625,8 @@ PyDoc_STRVAR(os_getppid__doc__, "Return the parent\'s process id.\n" "\n" "If the parent process has already exited, Windows machines will still\n" -"return its id; others systems will return the id of the \'init\' process (1)."); +"return its id; others systems will return the id of the \'init\' proces\n" +"(1)."); #define OS_GETPPID_METHODDEF \ {"getppid", (PyCFunction)os_getppid, METH_NOARGS, os_getppid__doc__}, @@ -6027,8 +6176,8 @@ PyDoc_STRVAR(os_waitid__doc__, " Constructed from the ORing of one or more of WEXITED, WSTOPPED\n" " or WCONTINUED and additionally may be ORed with WNOHANG or WNOWAIT.\n" "\n" -"Returns either waitid_result or None if WNOHANG is specified and there are\n" -"no children in a waitable state."); +"Returns either waitid_result or None if WNOHANG is specified and there\n" +"are no children in a waitable state."); #define OS_WAITID_METHODDEF \ {"waitid", _PyCFunction_CAST(os_waitid), METH_FASTCALL, os_waitid__doc__}, @@ -6189,8 +6338,8 @@ PyDoc_STRVAR(os_pidfd_open__doc__, "\n" "Return a file descriptor referring to the process *pid*.\n" "\n" -"The descriptor can be used to perform process management without races and\n" -"signals."); +"The descriptor can be used to perform process management without races\n" +"and signals."); #define OS_PIDFD_OPEN_METHODDEF \ {"pidfd_open", _PyCFunction_CAST(os_pidfd_open), METH_FASTCALL|METH_KEYWORDS, os_pidfd_open__doc__}, @@ -6258,6 +6407,93 @@ os_pidfd_open(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec #endif /* (defined(__linux__) && defined(__NR_pidfd_open) && !(defined(__ANDROID__) && __ANDROID_API__ < 31)) */ +#if (defined(__linux__) && defined(__NR_pidfd_getfd) && !(defined(__ANDROID__) && __ANDROID_API__ < 31)) + +PyDoc_STRVAR(os_pidfd_getfd__doc__, +"pidfd_getfd($module, /, pidfd, targetfd, *, flags=0)\n" +"--\n" +"\n" +"Duplicate a file descriptor from the process referred to by *pidfd*.\n" +"\n" +" pidfd\n" +" A process file descriptor.\n" +" targetfd\n" +" The file descriptor to duplicate from the target process.\n" +" flags\n" +" Reserved, must be 0."); + +#define OS_PIDFD_GETFD_METHODDEF \ + {"pidfd_getfd", _PyCFunction_CAST(os_pidfd_getfd), METH_FASTCALL|METH_KEYWORDS, os_pidfd_getfd__doc__}, + +static PyObject * +os_pidfd_getfd_impl(PyObject *module, int pidfd, int targetfd, + unsigned int flags); + +static PyObject * +os_pidfd_getfd(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 3 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(pidfd), &_Py_ID(targetfd), &_Py_ID(flags), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"pidfd", "targetfd", "flags", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "pidfd_getfd", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + int pidfd; + int targetfd; + unsigned int flags = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + pidfd = PyLong_AsInt(args[0]); + if (pidfd == -1 && PyErr_Occurred()) { + goto exit; + } + targetfd = PyLong_AsInt(args[1]); + if (targetfd == -1 && PyErr_Occurred()) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + if (!_PyLong_UnsignedInt_Converter(args[2], &flags)) { + goto exit; + } +skip_optional_kwonly: + return_value = os_pidfd_getfd_impl(module, pidfd, targetfd, flags); + +exit: + return return_value; +} + +#endif /* (defined(__linux__) && defined(__NR_pidfd_getfd) && !(defined(__ANDROID__) && __ANDROID_API__ < 31)) */ + #if defined(HAVE_SETNS) PyDoc_STRVAR(os_setns__doc__, @@ -6414,8 +6650,9 @@ PyDoc_STRVAR(os_readlink__doc__, "\n" "Return a string representing the path to which the symbolic link points.\n" "\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -"and path should be relative; path will then be relative to that directory.\n" +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative to\n" +"that directory.\n" "\n" "dir_fd may not be implemented on your platform. If it is unavailable,\n" "using it will raise a NotImplementedError."); @@ -6497,14 +6734,15 @@ PyDoc_STRVAR(os_symlink__doc__, "Create a symbolic link pointing to src named dst.\n" "\n" "target_is_directory is required on Windows if the target is to be\n" -" interpreted as a directory. (On Windows, symlink requires\n" -" Windows 6.0 or greater, and raises a NotImplementedError otherwise.)\n" -" target_is_directory is ignored on non-Windows platforms.\n" +"interpreted as a directory. (On Windows, symlink requires Windows 6.0\n" +"or greater, and raises a NotImplementedError otherwise.)\n" +"target_is_directory is ignored on non-Windows platforms.\n" "\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -" and path should be relative; path will then be relative to that directory.\n" -"dir_fd may not be implemented on your platform.\n" -" If it is unavailable, using it will raise a NotImplementedError."); +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative\n" +"to that directory.\n" +"dir_fd may not be implemented on your platform. If it is unavailable,\n" +"using it will raise a NotImplementedError."); #define OS_SYMLINK_METHODDEF \ {"symlink", _PyCFunction_CAST(os_symlink), METH_FASTCALL|METH_KEYWORDS, os_symlink__doc__}, @@ -7172,10 +7410,11 @@ PyDoc_STRVAR(os_open__doc__, "\n" "Open a file for low level IO. Returns a file descriptor (integer).\n" "\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -" and path should be relative; path will then be relative to that directory.\n" -"dir_fd may not be implemented on your platform.\n" -" If it is unavailable, using it will raise a NotImplementedError."); +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative to\n" +"that directory.\n" +"dir_fd may not be implemented on your platform. If it is unavailable,\n" +"using it will raise a NotImplementedError."); #define OS_OPEN_METHODDEF \ {"open", _PyCFunction_CAST(os_open), METH_FASTCALL|METH_KEYWORDS, os_open__doc__}, @@ -7549,7 +7788,8 @@ PyDoc_STRVAR(os_lseek__doc__, " - SEEK_CUR: seek from the current file position.\n" " - SEEK_END: seek from the end of the file.\n" "\n" -"The return value is the number of bytes relative to the beginning of the file."); +"The return value is the number of bytes relative to the beginning of\n" +"the file."); #define OS_LSEEK_METHODDEF \ {"lseek", _PyCFunction_CAST(os_lseek), METH_FASTCALL, os_lseek__doc__}, @@ -7640,15 +7880,15 @@ PyDoc_STRVAR(os_readinto__doc__, "\n" "Read into a buffer object from a file descriptor.\n" "\n" -"The buffer should be mutable and bytes-like. On success, returns the number of\n" -"bytes read. Less bytes may be read than the size of the buffer. The underlying\n" -"system call will be retried when interrupted by a signal, unless the signal\n" -"handler raises an exception. Other errors will not be retried and an error will\n" -"be raised.\n" +"The buffer should be mutable and bytes-like. On success, returns the\n" +"number of bytes read. Less bytes may be read than the size of the\n" +"buffer. The underlying system call will be retried when interrupted by\n" +"a signal, unless the signal handler raises an exception. Other errors\n" +"will not be retried and an error will be raised.\n" "\n" -"Returns 0 if *fd* is at end of file or if the provided *buffer* has length 0\n" -"(which can be used to check for errors without reading data). Never returns\n" -"negative."); +"Returns 0 if *fd* is at end of file or if the provided *buffer* has\n" +"length 0 (which can be used to check for errors without reading data).\n" +"Never returns negative."); #define OS_READINTO_METHODDEF \ {"readinto", _PyCFunction_CAST(os_readinto), METH_FASTCALL, os_readinto__doc__}, @@ -7803,17 +8043,19 @@ PyDoc_STRVAR(os_preadv__doc__, "\n" "Reads from a file descriptor into a number of mutable bytes-like objects.\n" "\n" -"Combines the functionality of readv() and pread(). As readv(), it will\n" -"transfer data into each buffer until it is full and then move on to the next\n" -"buffer in the sequence to hold the rest of the data. Its fourth argument,\n" -"specifies the file offset at which the input operation is to be performed. It\n" -"will return the total number of bytes read (which can be less than the total\n" -"capacity of all the objects).\n" +"Combines the functionality of readv() and pread(). As readv(), it will\n" +"transfer data into each buffer until it is full and then move on to the\n" +"next buffer in the sequence to hold the rest of the data. Its fourth\n" +"argument, specifies the file offset at which the input operation is to\n" +"be performed. It will return the total number of bytes read (which can\n" +"be less than the total capacity of all the objects).\n" "\n" -"The flags argument contains a bitwise OR of zero or more of the following flags:\n" +"The flags argument contains a bitwise OR of zero or more of the\n" +"following flags:\n" "\n" "- RWF_HIPRI\n" "- RWF_NOWAIT\n" +"- RWF_DONTCACHE\n" "\n" "Using non-zero flags requires Linux 4.6 or newer."); @@ -8100,6 +8342,11 @@ os_sendfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject goto exit; } count = ival; + if (count < 0) { + PyErr_SetString(PyExc_ValueError, + "count cannot be negative"); + goto exit; + } } if (!noptargs) { goto skip_optional_pos; @@ -8206,6 +8453,11 @@ os_sendfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject goto exit; } count = ival; + if (count < 0) { + PyErr_SetString(PyExc_ValueError, + "count cannot be negative"); + goto exit; + } } return_value = os_sendfile_impl(module, out_fd, in_fd, offobj, count); @@ -8533,18 +8785,22 @@ PyDoc_STRVAR(os_pwritev__doc__, "\n" "Writes the contents of bytes-like objects to a file descriptor at a given offset.\n" "\n" -"Combines the functionality of writev() and pwrite(). All buffers must be a sequence\n" -"of bytes-like objects. Buffers are processed in array order. Entire contents of first\n" -"buffer is written before proceeding to second, and so on. The operating system may\n" -"set a limit (sysconf() value SC_IOV_MAX) on the number of buffers that can be used.\n" -"This function writes the contents of each object to the file descriptor and returns\n" -"the total number of bytes written.\n" +"Combines the functionality of writev() and pwrite(). All buffers must be\n" +"a sequence of bytes-like objects. Buffers are processed in array order.\n" +"Entire contents of first buffer is written before proceeding to second,\n" +"and so on. The operating system may set a limit (sysconf() value\n" +"SC_IOV_MAX) on the number of buffers that can be used.\n" +"This function writes the contents of each object to the file descriptor\n" +"and returns the total number of bytes written.\n" "\n" -"The flags argument contains a bitwise OR of zero or more of the following flags:\n" +"The flags argument contains a bitwise OR of zero or more of the\n" +"following flags:\n" "\n" "- RWF_DSYNC\n" "- RWF_SYNC\n" "- RWF_APPEND\n" +"- RWF_DONTCACHE\n" +"- RWF_ATOMIC\n" "\n" "Using non-zero flags requires Linux 4.7 or newer."); @@ -8689,6 +8945,11 @@ os_copy_file_range(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py goto exit; } count = ival; + if (count < 0) { + PyErr_SetString(PyExc_ValueError, + "count cannot be negative"); + goto exit; + } } if (!noptargs) { goto skip_optional_pos; @@ -8807,6 +9068,11 @@ os_splice(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k goto exit; } count = ival; + if (count < 0) { + PyErr_SetString(PyExc_ValueError, + "count cannot be negative"); + goto exit; + } } if (!noptargs) { goto skip_optional_pos; @@ -8843,10 +9109,11 @@ PyDoc_STRVAR(os_mkfifo__doc__, "\n" "Create a \"fifo\" (a POSIX named pipe).\n" "\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -" and path should be relative; path will then be relative to that directory.\n" -"dir_fd may not be implemented on your platform.\n" -" If it is unavailable, using it will raise a NotImplementedError."); +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative to\n" +"that directory.\n" +"dir_fd may not be implemented on your platform. If it is unavailable,\n" +"using it will raise a NotImplementedError."); #define OS_MKFIFO_METHODDEF \ {"mkfifo", _PyCFunction_CAST(os_mkfifo), METH_FASTCALL|METH_KEYWORDS, os_mkfifo__doc__}, @@ -8938,17 +9205,18 @@ PyDoc_STRVAR(os_mknod__doc__, "\n" "Create a node in the file system.\n" "\n" -"Create a node in the file system (file, device special file or named pipe)\n" -"at path. mode specifies both the permissions to use and the\n" +"Create a node in the file system (file, device special file or named\n" +"pipe) at path. mode specifies both the permissions to use and the\n" "type of node to be created, being combined (bitwise OR) with one of\n" -"S_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO. If S_IFCHR or S_IFBLK is set on mode,\n" -"device defines the newly created device special file (probably using\n" -"os.makedev()). Otherwise device is ignored.\n" +"S_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO. If S_IFCHR or S_IFBLK is set\n" +"on mode, device defines the newly created device special file (probably\n" +"using os.makedev()). Otherwise device is ignored.\n" "\n" -"If dir_fd is not None, it should be a file descriptor open to a directory,\n" -" and path should be relative; path will then be relative to that directory.\n" -"dir_fd may not be implemented on your platform.\n" -" If it is unavailable, using it will raise a NotImplementedError."); +"If dir_fd is not None, it should be a file descriptor open to\n" +"a directory, and path should be relative; path will then be relative\n" +"to that directory.\n" +"dir_fd may not be implemented on your platform. If it is unavailable,\n" +"using it will raise a NotImplementedError."); #define OS_MKNOD_METHODDEF \ {"mknod", _PyCFunction_CAST(os_mknod), METH_FASTCALL|METH_KEYWORDS, os_mknod__doc__}, @@ -9194,8 +9462,9 @@ PyDoc_STRVAR(os_truncate__doc__, "\n" "Truncate a file, specified by path, to a specific length.\n" "\n" -"On some platforms, path may also be specified as an open file descriptor.\n" -" If this functionality is unavailable, using it raises an exception."); +"On some platforms, path may also be specified as an open file\n" +"descriptor. If this functionality is unavailable, using it raises\n" +"an exception."); #define OS_TRUNCATE_METHODDEF \ {"truncate", _PyCFunction_CAST(os_truncate), METH_FASTCALL|METH_KEYWORDS, os_truncate__doc__}, @@ -9260,7 +9529,7 @@ os_truncate(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject #endif /* (defined HAVE_TRUNCATE || defined MS_WINDOWS) */ -#if (defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG) && !defined(__wasi__)) +#if (defined(HAVE_POSIX_FALLOCATE) && !defined(__wasi__)) PyDoc_STRVAR(os_posix_fallocate__doc__, "posix_fallocate($module, fd, offset, length, /)\n" @@ -9269,7 +9538,8 @@ PyDoc_STRVAR(os_posix_fallocate__doc__, "Ensure a file has allocated at least a particular number of bytes on disk.\n" "\n" "Ensure that the file specified by fd encompasses a range of bytes\n" -"starting at offset bytes from the beginning and continuing for length bytes."); +"starting at offset bytes from the beginning and continuing for length\n" +"bytes."); #define OS_POSIX_FALLOCATE_METHODDEF \ {"posix_fallocate", _PyCFunction_CAST(os_posix_fallocate), METH_FASTCALL, os_posix_fallocate__doc__}, @@ -9305,9 +9575,9 @@ os_posix_fallocate(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* (defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG) && !defined(__wasi__)) */ +#endif /* (defined(HAVE_POSIX_FALLOCATE) && !defined(__wasi__)) */ -#if (defined(HAVE_POSIX_FADVISE) && !defined(POSIX_FADVISE_AIX_BUG)) +#if defined(HAVE_POSIX_FADVISE) PyDoc_STRVAR(os_posix_fadvise__doc__, "posix_fadvise($module, fd, offset, length, advice, /)\n" @@ -9315,8 +9585,8 @@ PyDoc_STRVAR(os_posix_fadvise__doc__, "\n" "Announce an intention to access data in a specific pattern.\n" "\n" -"Announce an intention to access data in a specific pattern, thus allowing\n" -"the kernel to make optimizations.\n" +"Announce an intention to access data in a specific pattern, thus\n" +"allowing the kernel to make optimizations.\n" "The advice applies to the region of the file specified by fd starting at\n" "offset and continuing for length bytes.\n" "advice is one of POSIX_FADV_NORMAL, POSIX_FADV_SEQUENTIAL,\n" @@ -9362,7 +9632,7 @@ os_posix_fadvise(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* (defined(HAVE_POSIX_FADVISE) && !defined(POSIX_FADVISE_AIX_BUG)) */ +#endif /* defined(HAVE_POSIX_FADVISE) */ #if defined(MS_WINDOWS) @@ -9516,6 +9786,27 @@ os_unsetenv(PyObject *module, PyObject *arg) #endif /* !defined(MS_WINDOWS) */ +#if defined(HAVE_CLEARENV) + +PyDoc_STRVAR(os__clearenv__doc__, +"_clearenv($module, /)\n" +"--\n" +"\n"); + +#define OS__CLEARENV_METHODDEF \ + {"_clearenv", (PyCFunction)os__clearenv, METH_NOARGS, os__clearenv__doc__}, + +static PyObject * +os__clearenv_impl(PyObject *module); + +static PyObject * +os__clearenv(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return os__clearenv_impl(module); +} + +#endif /* defined(HAVE_CLEARENV) */ + PyDoc_STRVAR(os_strerror__doc__, "strerror($module, code, /)\n" "--\n" @@ -10117,8 +10408,9 @@ PyDoc_STRVAR(os_statvfs__doc__, "Perform a statvfs system call on the given path.\n" "\n" "path may always be specified as a string.\n" -"On some platforms, path may also be specified as an open file descriptor.\n" -" If this functionality is unavailable, using it raises an exception."); +"On some platforms, path may also be specified as an open file\n" +"descriptor. If this functionality is unavailable, using it raises\n" +"an exception."); #define OS_STATVFS_METHODDEF \ {"statvfs", _PyCFunction_CAST(os_statvfs), METH_FASTCALL|METH_KEYWORDS, os_statvfs__doc__}, @@ -10301,8 +10593,9 @@ PyDoc_STRVAR(os_pathconf__doc__, "Return the configuration limit name for the file or directory path.\n" "\n" "If there is no limit, return -1.\n" -"On some platforms, path may also be specified as an open file descriptor.\n" -" If this functionality is unavailable, using it raises an exception."); +"On some platforms, path may also be specified as an open file\n" +"descriptor. If this functionality is unavailable, using it raises\n" +"an exception."); #define OS_PATHCONF_METHODDEF \ {"pathconf", _PyCFunction_CAST(os_pathconf), METH_FASTCALL|METH_KEYWORDS, os_pathconf__doc__}, @@ -10445,8 +10738,8 @@ PyDoc_STRVAR(os_abort__doc__, "\n" "Abort the interpreter immediately.\n" "\n" -"This function \'dumps core\' or otherwise fails in the hardest way possible\n" -"on the hosting operating system. This function never returns."); +"This function \'dumps core\' or otherwise fails in the hardest way\n" +"possible on the hosting operating system. This function never returns."); #define OS_ABORT_METHODDEF \ {"abort", (PyCFunction)os_abort, METH_NOARGS, os_abort__doc__}, @@ -10834,10 +11127,11 @@ PyDoc_STRVAR(os_getxattr__doc__, "\n" "Return the value of extended attribute attribute on path.\n" "\n" -"path may be either a string, a path-like object, or an open file descriptor.\n" -"If follow_symlinks is False, and the last element of the path is a symbolic\n" -" link, getxattr will examine the symbolic link itself instead of the file\n" -" the link points to."); +"path may be either a string, a path-like object, or an open file\n" +"descriptor.\n" +"If follow_symlinks is False, and the last element of the path is\n" +"a symbolic link, getxattr will examine the symbolic link itself\n" +"instead of the file the link points to."); #define OS_GETXATTR_METHODDEF \ {"getxattr", _PyCFunction_CAST(os_getxattr), METH_FASTCALL|METH_KEYWORDS, os_getxattr__doc__}, @@ -10924,10 +11218,11 @@ PyDoc_STRVAR(os_setxattr__doc__, "\n" "Set extended attribute attribute on path to value.\n" "\n" -"path may be either a string, a path-like object, or an open file descriptor.\n" -"If follow_symlinks is False, and the last element of the path is a symbolic\n" -" link, setxattr will modify the symbolic link itself instead of the file\n" -" the link points to."); +"path may be either a string, a path-like object, or an open file\n" +"descriptor.\n" +"If follow_symlinks is False, and the last element of the path is\n" +"a symbolic link, setxattr will modify the symbolic link itself instead\n" +"of the file the link points to."); #define OS_SETXATTR_METHODDEF \ {"setxattr", _PyCFunction_CAST(os_setxattr), METH_FASTCALL|METH_KEYWORDS, os_setxattr__doc__}, @@ -11035,10 +11330,11 @@ PyDoc_STRVAR(os_removexattr__doc__, "\n" "Remove extended attribute attribute on path.\n" "\n" -"path may be either a string, a path-like object, or an open file descriptor.\n" -"If follow_symlinks is False, and the last element of the path is a symbolic\n" -" link, removexattr will modify the symbolic link itself instead of the file\n" -" the link points to."); +"path may be either a string, a path-like object, or an open file\n" +"descriptor.\n" +"If follow_symlinks is False, and the last element of the path is\n" +"a symbolic link, removexattr will modify the symbolic link itself\n" +"instead of the file the link points to."); #define OS_REMOVEXATTR_METHODDEF \ {"removexattr", _PyCFunction_CAST(os_removexattr), METH_FASTCALL|METH_KEYWORDS, os_removexattr__doc__}, @@ -11124,11 +11420,12 @@ PyDoc_STRVAR(os_listxattr__doc__, "\n" "Return a list of extended attributes on path.\n" "\n" -"path may be either None, a string, a path-like object, or an open file descriptor.\n" -"if path is None, listxattr will examine the current directory.\n" -"If follow_symlinks is False, and the last element of the path is a symbolic\n" -" link, listxattr will examine the symbolic link itself instead of the file\n" -" the link points to."); +"path may be either None, a string, a path-like object, or an open file\n" +"descriptor. If path is None, listxattr will examine the current\n" +"directory.\n" +"If follow_symlinks is False, and the last element of the path is\n" +"a symbolic link, listxattr will examine the symbolic link itself instead\n" +"of the file the link points to."); #define OS_LISTXATTR_METHODDEF \ {"listxattr", _PyCFunction_CAST(os_listxattr), METH_FASTCALL|METH_KEYWORDS, os_listxattr__doc__}, @@ -11237,6 +11534,11 @@ os_urandom(PyObject *module, PyObject *arg) goto exit; } size = ival; + if (size < 0) { + PyErr_SetString(PyExc_ValueError, + "size cannot be negative"); + goto exit; + } } return_value = os_urandom_impl(module, size); @@ -12145,9 +12447,9 @@ PyDoc_STRVAR(os_scandir__doc__, "\n" "Return an iterator of DirEntry objects for given path.\n" "\n" -"path can be specified as either str, bytes, or a path-like object. If path\n" -"is bytes, the names of yielded DirEntry objects will also be bytes; in\n" -"all other circumstances they will be str.\n" +"path can be specified as either str, bytes, or a path-like object. If\n" +"path is bytes, the names of yielded DirEntry objects will also be bytes;\n" +"in all other circumstances they will be str.\n" "\n" "If path is None, uses the path=\'.\'."); @@ -12219,9 +12521,9 @@ PyDoc_STRVAR(os_fspath__doc__, "\n" "Return the file system path representation of the object.\n" "\n" -"If the object is str or bytes, then allow it to pass through as-is. If the\n" -"object defines __fspath__(), then return the result of that method. All other\n" -"types raise a TypeError."); +"If the object is str or bytes, then allow it to pass through as-is. If\n" +"the object defines __fspath__(), then return the result of that method.\n" +"All other types raise a TypeError."); #define OS_FSPATH_METHODDEF \ {"fspath", _PyCFunction_CAST(os_fspath), METH_FASTCALL|METH_KEYWORDS, os_fspath__doc__}, @@ -12515,8 +12817,8 @@ PyDoc_STRVAR(os_waitstatus_to_exitcode__doc__, "On Windows, return status shifted right by 8 bits.\n" "\n" "On Unix, if the process is being traced or if waitpid() was called with\n" -"WUNTRACED option, the caller must first check if WIFSTOPPED(status) is true.\n" -"This function must not be called if WIFSTOPPED(status) is true."); +"WUNTRACED option, the caller must first check if WIFSTOPPED(status) is\n" +"true. This function must not be called if WIFSTOPPED(status) is true."); #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF \ {"waitstatus_to_exitcode", _PyCFunction_CAST(os_waitstatus_to_exitcode), METH_FASTCALL|METH_KEYWORDS, os_waitstatus_to_exitcode__doc__}, @@ -12598,7 +12900,7 @@ PyDoc_STRVAR(os__inputhook__doc__, "_inputhook($module, /)\n" "--\n" "\n" -"Calls PyOS_CallInputHook droppong the GIL first"); +"Calls PyOS_InputHook dropping the GIL first"); #define OS__INPUTHOOK_METHODDEF \ {"_inputhook", (PyCFunction)os__inputhook, METH_NOARGS, os__inputhook__doc__}, @@ -12616,7 +12918,7 @@ PyDoc_STRVAR(os__is_inputhook_installed__doc__, "_is_inputhook_installed($module, /)\n" "--\n" "\n" -"Checks if PyOS_CallInputHook is set"); +"Checks if PyOS_InputHook is set"); #define OS__IS_INPUTHOOK_INSTALLED_METHODDEF \ {"_is_inputhook_installed", (PyCFunction)os__is_inputhook_installed, METH_NOARGS, os__is_inputhook_installed__doc__}, @@ -12744,6 +13046,10 @@ os__emscripten_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py #endif /* defined(__EMSCRIPTEN__) */ +#ifndef OS_STATX_METHODDEF + #define OS_STATX_METHODDEF +#endif /* !defined(OS_STATX_METHODDEF) */ + #ifndef OS_TTYNAME_METHODDEF #define OS_TTYNAME_METHODDEF #endif /* !defined(OS_TTYNAME_METHODDEF) */ @@ -13108,6 +13414,10 @@ os__emscripten_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py #define OS_PIDFD_OPEN_METHODDEF #endif /* !defined(OS_PIDFD_OPEN_METHODDEF) */ +#ifndef OS_PIDFD_GETFD_METHODDEF + #define OS_PIDFD_GETFD_METHODDEF +#endif /* !defined(OS_PIDFD_GETFD_METHODDEF) */ + #ifndef OS_SETNS_METHODDEF #define OS_SETNS_METHODDEF #endif /* !defined(OS_SETNS_METHODDEF) */ @@ -13264,6 +13574,10 @@ os__emscripten_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py #define OS_UNSETENV_METHODDEF #endif /* !defined(OS_UNSETENV_METHODDEF) */ +#ifndef OS__CLEARENV_METHODDEF + #define OS__CLEARENV_METHODDEF +#endif /* !defined(OS__CLEARENV_METHODDEF) */ + #ifndef OS_WCOREDUMP_METHODDEF #define OS_WCOREDUMP_METHODDEF #endif /* !defined(OS_WCOREDUMP_METHODDEF) */ @@ -13419,4 +13733,4 @@ os__emscripten_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py #ifndef OS__EMSCRIPTEN_LOG_METHODDEF #define OS__EMSCRIPTEN_LOG_METHODDEF #endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */ -/*[clinic end generated code: output=b1e2615384347102 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=250ea2e34fdd133f input=a9049054013a1b77]*/ diff --git a/Modules/clinic/pyexpat.c.h b/Modules/clinic/pyexpat.c.h index 13210e3be0f747..1a07726d303eca 100644 --- a/Modules/clinic/pyexpat.c.h +++ b/Modules/clinic/pyexpat.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_SINGLETON() #endif +#include "pycore_long.h" // _PyLong_UnsignedLongLong_Converter() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(pyexpat_xmlparser_SetReparseDeferralEnabled__doc__, @@ -217,8 +218,9 @@ PyDoc_STRVAR(pyexpat_xmlparser_GetInputContext__doc__, "\n" "Return the untranslated text of the input that caused the current event.\n" "\n" -"If the event was generated by a large amount of text (such as a start tag\n" -"for an element with many attributes), not all of the text may be available."); +"If the event was generated by a large amount of text (such as\n" +"a start tag for an element with many attributes), not all of the\n" +"text may be available."); #define PYEXPAT_XMLPARSER_GETINPUTCONTEXT_METHODDEF \ {"GetInputContext", (PyCFunction)pyexpat_xmlparser_GetInputContext, METH_NOARGS, pyexpat_xmlparser_GetInputContext__doc__}, @@ -356,9 +358,10 @@ PyDoc_STRVAR(pyexpat_xmlparser_UseForeignDTD__doc__, "\n" "Allows the application to provide an artificial external subset if one is not specified as part of the document instance.\n" "\n" -"This readily allows the use of a \'default\' document type controlled by the\n" -"application, while still getting the advantage of providing document type\n" -"information to the parser. \'flag\' defaults to True if not provided."); +"This readily allows the use of a \'default\' document type controlled\n" +"by the application, while still getting the advantage of providing\n" +"document type information to the parser. \'flag\' defaults to True if\n" +"not provided."); #define PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF \ {"UseForeignDTD", _PyCFunction_CAST(pyexpat_xmlparser_UseForeignDTD), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pyexpat_xmlparser_UseForeignDTD__doc__}, @@ -408,6 +411,274 @@ pyexpat_xmlparser_UseForeignDTD(PyObject *self, PyTypeObject *cls, PyObject *con #endif /* (XML_COMBINED_VERSION >= 19505) */ +#if (XML_COMBINED_VERSION >= 20400) + +PyDoc_STRVAR(pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold__doc__, +"SetBillionLaughsAttackProtectionActivationThreshold($self, threshold, /)\n" +"--\n" +"\n" +"Sets the number of output bytes needed to activate protection against billion laughs attacks.\n" +"\n" +"The number of output bytes includes amplification from entity\n" +"expansion and reading DTD files.\n" +"\n" +"Parser objects usually have a protection activation threshold of\n" +"8 MiB, but the actual default value depends on the underlying Expat\n" +"library.\n" +"\n" +"Activation thresholds below 4 MiB are known to break support for\n" +"DITA 1.3 payload and are hence not recommended."); + +#define PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONACTIVATIONTHRESHOLD_METHODDEF \ + {"SetBillionLaughsAttackProtectionActivationThreshold", _PyCFunction_CAST(pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold__doc__}, + +static PyObject * +pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold_impl(xmlparseobject *self, + PyTypeObject *cls, + unsigned long long threshold); + +static PyObject * +pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "SetBillionLaughsAttackProtectionActivationThreshold", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + unsigned long long threshold; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!_PyLong_UnsignedLongLong_Converter(args[0], &threshold)) { + goto exit; + } + return_value = pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold_impl((xmlparseobject *)self, cls, threshold); + +exit: + return return_value; +} + +#endif /* (XML_COMBINED_VERSION >= 20400) */ + +#if (XML_COMBINED_VERSION >= 20400) + +PyDoc_STRVAR(pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification__doc__, +"SetBillionLaughsAttackProtectionMaximumAmplification($self, max_factor,\n" +" /)\n" +"--\n" +"\n" +"Sets the maximum tolerated amplification factor for protection against billion laughs attacks.\n" +"\n" +"The amplification factor is calculated as \"(direct + indirect) /\n" +"direct\" while parsing, where \"direct\" is the number of bytes read\n" +"from the primary document in parsing and \"indirect\" is the number of\n" +"bytes added by expanding entities and reading external DTD files,\n" +"combined.\n" +"\n" +"The \'max_factor\' value must be a non-NaN floating point value\n" +"greater than or equal to 1.0. Amplification factors greater than\n" +"30,000 can be observed in the middle of parsing even with benign\n" +"files in practice. In particular, the activation threshold should\n" +"be carefully chosen to avoid false positives.\n" +"\n" +"Parser objects usually have a maximum amplification factor of 100,\n" +"but the actual default value depends on the underlying Expat\n" +"library."); + +#define PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONMAXIMUMAMPLIFICATION_METHODDEF \ + {"SetBillionLaughsAttackProtectionMaximumAmplification", _PyCFunction_CAST(pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification__doc__}, + +static PyObject * +pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification_impl(xmlparseobject *self, + PyTypeObject *cls, + float max_factor); + +static PyObject * +pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "SetBillionLaughsAttackProtectionMaximumAmplification", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + float max_factor; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (PyFloat_CheckExact(args[0])) { + max_factor = (float) (PyFloat_AS_DOUBLE(args[0])); + } + else + { + max_factor = (float) PyFloat_AsDouble(args[0]); + if (max_factor == -1.0 && PyErr_Occurred()) { + goto exit; + } + } + return_value = pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification_impl((xmlparseobject *)self, cls, max_factor); + +exit: + return return_value; +} + +#endif /* (XML_COMBINED_VERSION >= 20400) */ + +#if (XML_COMBINED_VERSION >= 20702) + +PyDoc_STRVAR(pyexpat_xmlparser_SetAllocTrackerActivationThreshold__doc__, +"SetAllocTrackerActivationThreshold($self, threshold, /)\n" +"--\n" +"\n" +"Sets the number of allocated bytes of dynamic memory needed to activate protection against disproportionate use of RAM.\n" +"\n" +"Parser objects usually have an allocation activation threshold of\n" +"64 MiB, but the actual default value depends on the underlying Expat\n" +"library."); + +#define PYEXPAT_XMLPARSER_SETALLOCTRACKERACTIVATIONTHRESHOLD_METHODDEF \ + {"SetAllocTrackerActivationThreshold", _PyCFunction_CAST(pyexpat_xmlparser_SetAllocTrackerActivationThreshold), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pyexpat_xmlparser_SetAllocTrackerActivationThreshold__doc__}, + +static PyObject * +pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl(xmlparseobject *self, + PyTypeObject *cls, + unsigned long long threshold); + +static PyObject * +pyexpat_xmlparser_SetAllocTrackerActivationThreshold(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "SetAllocTrackerActivationThreshold", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + unsigned long long threshold; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (!_PyLong_UnsignedLongLong_Converter(args[0], &threshold)) { + goto exit; + } + return_value = pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl((xmlparseobject *)self, cls, threshold); + +exit: + return return_value; +} + +#endif /* (XML_COMBINED_VERSION >= 20702) */ + +#if (XML_COMBINED_VERSION >= 20702) + +PyDoc_STRVAR(pyexpat_xmlparser_SetAllocTrackerMaximumAmplification__doc__, +"SetAllocTrackerMaximumAmplification($self, max_factor, /)\n" +"--\n" +"\n" +"Sets the maximum amplification factor between direct input and bytes of dynamic memory allocated.\n" +"\n" +"The amplification factor is calculated as \"allocated / direct\" while\n" +"parsing, where \"direct\" is the number of bytes read from the primary\n" +"document in parsing and \"allocated\" is the number of bytes of\n" +"dynamic memory allocated in the parser hierarchy.\n" +"\n" +"The \'max_factor\' value must be a non-NaN floating point value\n" +"greater than or equal to 1.0. Amplification factors greater than\n" +"100.0 can be observed near the start of parsing even with benign\n" +"files in practice. In particular, the activation threshold should\n" +"be carefully chosen to avoid false positives.\n" +"\n" +"Parser objects usually have a maximum amplification factor of 100,\n" +"but the actual default value depends on the underlying Expat\n" +"library."); + +#define PYEXPAT_XMLPARSER_SETALLOCTRACKERMAXIMUMAMPLIFICATION_METHODDEF \ + {"SetAllocTrackerMaximumAmplification", _PyCFunction_CAST(pyexpat_xmlparser_SetAllocTrackerMaximumAmplification), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, pyexpat_xmlparser_SetAllocTrackerMaximumAmplification__doc__}, + +static PyObject * +pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self, + PyTypeObject *cls, + float max_factor); + +static PyObject * +pyexpat_xmlparser_SetAllocTrackerMaximumAmplification(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + # define KWTUPLE (PyObject *)&_Py_SINGLETON(tuple_empty) + #else + # define KWTUPLE NULL + #endif + + static const char * const _keywords[] = {"", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "SetAllocTrackerMaximumAmplification", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + float max_factor; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + if (PyFloat_CheckExact(args[0])) { + max_factor = (float) (PyFloat_AS_DOUBLE(args[0])); + } + else + { + max_factor = (float) PyFloat_AsDouble(args[0]); + if (max_factor == -1.0 && PyErr_Occurred()) { + goto exit; + } + } + return_value = pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl((xmlparseobject *)self, cls, max_factor); + +exit: + return return_value; +} + +#endif /* (XML_COMBINED_VERSION >= 20702) */ + PyDoc_STRVAR(pyexpat_ParserCreate__doc__, "ParserCreate($module, /, encoding=None, namespace_separator=None,\n" " intern=<unrepresentable>)\n" @@ -552,4 +823,20 @@ pyexpat_ErrorString(PyObject *module, PyObject *arg) #ifndef PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF #define PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF #endif /* !defined(PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF) */ -/*[clinic end generated code: output=4dbdc959c67dc2d5 input=a9049054013a1b77]*/ + +#ifndef PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONACTIVATIONTHRESHOLD_METHODDEF + #define PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONACTIVATIONTHRESHOLD_METHODDEF +#endif /* !defined(PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONACTIVATIONTHRESHOLD_METHODDEF) */ + +#ifndef PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONMAXIMUMAMPLIFICATION_METHODDEF + #define PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONMAXIMUMAMPLIFICATION_METHODDEF +#endif /* !defined(PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONMAXIMUMAMPLIFICATION_METHODDEF) */ + +#ifndef PYEXPAT_XMLPARSER_SETALLOCTRACKERACTIVATIONTHRESHOLD_METHODDEF + #define PYEXPAT_XMLPARSER_SETALLOCTRACKERACTIVATIONTHRESHOLD_METHODDEF +#endif /* !defined(PYEXPAT_XMLPARSER_SETALLOCTRACKERACTIVATIONTHRESHOLD_METHODDEF) */ + +#ifndef PYEXPAT_XMLPARSER_SETALLOCTRACKERMAXIMUMAMPLIFICATION_METHODDEF + #define PYEXPAT_XMLPARSER_SETALLOCTRACKERMAXIMUMAMPLIFICATION_METHODDEF +#endif /* !defined(PYEXPAT_XMLPARSER_SETALLOCTRACKERMAXIMUMAMPLIFICATION_METHODDEF) */ +/*[clinic end generated code: output=270a0bfe3300e8a1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/readline.c.h b/Modules/clinic/readline.c.h index 696475f7d00f5b..dc9381e4b976ac 100644 --- a/Modules/clinic/readline.c.h +++ b/Modules/clinic/readline.c.h @@ -349,6 +349,28 @@ readline_set_pre_input_hook(PyObject *module, PyObject *const *args, Py_ssize_t #endif /* defined(HAVE_RL_PRE_INPUT_HOOK) */ +#if defined(HAVE_RL_PRE_INPUT_HOOK) + +PyDoc_STRVAR(readline_get_pre_input_hook__doc__, +"get_pre_input_hook($module, /)\n" +"--\n" +"\n" +"Get the current pre-input hook function."); + +#define READLINE_GET_PRE_INPUT_HOOK_METHODDEF \ + {"get_pre_input_hook", (PyCFunction)readline_get_pre_input_hook, METH_NOARGS, readline_get_pre_input_hook__doc__}, + +static PyObject * +readline_get_pre_input_hook_impl(PyObject *module); + +static PyObject * +readline_get_pre_input_hook(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return readline_get_pre_input_hook_impl(module); +} + +#endif /* defined(HAVE_RL_PRE_INPUT_HOOK) */ + PyDoc_STRVAR(readline_get_completion_type__doc__, "get_completion_type($module, /)\n" "--\n" @@ -794,7 +816,11 @@ readline_redisplay(PyObject *module, PyObject *Py_UNUSED(ignored)) #define READLINE_SET_PRE_INPUT_HOOK_METHODDEF #endif /* !defined(READLINE_SET_PRE_INPUT_HOOK_METHODDEF) */ +#ifndef READLINE_GET_PRE_INPUT_HOOK_METHODDEF + #define READLINE_GET_PRE_INPUT_HOOK_METHODDEF +#endif /* !defined(READLINE_GET_PRE_INPUT_HOOK_METHODDEF) */ + #ifndef READLINE_CLEAR_HISTORY_METHODDEF #define READLINE_CLEAR_HISTORY_METHODDEF #endif /* !defined(READLINE_CLEAR_HISTORY_METHODDEF) */ -/*[clinic end generated code: output=88d9812b6caa2102 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=4bd95070973cd0e2 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/resource.c.h b/Modules/clinic/resource.c.h index 9eda7de27532a1..e4ef93900d1797 100644 --- a/Modules/clinic/resource.c.h +++ b/Modules/clinic/resource.c.h @@ -2,6 +2,8 @@ preserve [clinic start generated code]*/ +#include "pycore_modsupport.h" // _PyArg_CheckPositional() + #if defined(HAVE_GETRUSAGE) PyDoc_STRVAR(resource_getrusage__doc__, @@ -66,7 +68,7 @@ PyDoc_STRVAR(resource_setrlimit__doc__, "\n"); #define RESOURCE_SETRLIMIT_METHODDEF \ - {"setrlimit", (PyCFunction)(void(*)(void))resource_setrlimit, METH_FASTCALL, resource_setrlimit__doc__}, + {"setrlimit", _PyCFunction_CAST(resource_setrlimit), METH_FASTCALL, resource_setrlimit__doc__}, static PyObject * resource_setrlimit_impl(PyObject *module, int resource, PyObject *limits); @@ -78,8 +80,7 @@ resource_setrlimit(PyObject *module, PyObject *const *args, Py_ssize_t nargs) int resource; PyObject *limits; - if (nargs != 2) { - PyErr_Format(PyExc_TypeError, "setrlimit expected 2 arguments, got %zd", nargs); + if (!_PyArg_CheckPositional("setrlimit", nargs, 2, 2)) { goto exit; } resource = PyLong_AsInt(args[0]); @@ -101,7 +102,7 @@ PyDoc_STRVAR(resource_prlimit__doc__, "\n"); #define RESOURCE_PRLIMIT_METHODDEF \ - {"prlimit", (PyCFunction)(void(*)(void))resource_prlimit, METH_FASTCALL, resource_prlimit__doc__}, + {"prlimit", _PyCFunction_CAST(resource_prlimit), METH_FASTCALL, resource_prlimit__doc__}, static PyObject * resource_prlimit_impl(PyObject *module, pid_t pid, int resource, @@ -115,12 +116,7 @@ resource_prlimit(PyObject *module, PyObject *const *args, Py_ssize_t nargs) int resource; PyObject *limits = Py_None; - if (nargs < 2) { - PyErr_Format(PyExc_TypeError, "prlimit expected at least 2 arguments, got %zd", nargs); - goto exit; - } - if (nargs > 3) { - PyErr_Format(PyExc_TypeError, "prlimit expected at most 3 arguments, got %zd", nargs); + if (!_PyArg_CheckPositional("prlimit", nargs, 2, 3)) { goto exit; } pid = PyLong_AsPid(args[0]); @@ -178,4 +174,4 @@ resource_getpagesize(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef RESOURCE_PRLIMIT_METHODDEF #define RESOURCE_PRLIMIT_METHODDEF #endif /* !defined(RESOURCE_PRLIMIT_METHODDEF) */ -/*[clinic end generated code: output=e45883ace510414a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8e905b2f5c35170e input=a9049054013a1b77]*/ diff --git a/Modules/clinic/selectmodule.c.h b/Modules/clinic/selectmodule.c.h index c0a5f678ad038d..c1c8ad40e724f5 100644 --- a/Modules/clinic/selectmodule.c.h +++ b/Modules/clinic/selectmodule.c.h @@ -16,7 +16,8 @@ PyDoc_STRVAR(select_select__doc__, "\n" "Wait until one or more file descriptors are ready for some kind of I/O.\n" "\n" -"The first three arguments are iterables of file descriptors to be waited for:\n" +"The first three arguments are iterables of file descriptors to be waited\n" +"for:\n" "rlist -- wait until ready for reading\n" "wlist -- wait until ready for writing\n" "xlist -- wait for an \"exceptional condition\"\n" @@ -26,12 +27,12 @@ PyDoc_STRVAR(select_select__doc__, "gotten from a fileno() method call on one of those.\n" "\n" "The optional 4th argument specifies a timeout in seconds; it may be\n" -"a floating-point number to specify fractions of seconds. If it is absent\n" +"a non-integer to specify fractions of seconds. If it is absent\n" "or None, the call will never time out.\n" "\n" -"The return value is a tuple of three lists corresponding to the first three\n" -"arguments; each contains the subset of the corresponding file descriptors\n" -"that are ready.\n" +"The return value is a tuple of three lists corresponding to the first\n" +"three arguments; each contains the subset of the corresponding file\n" +"descriptors that are ready.\n" "\n" "*** IMPORTANT NOTICE ***\n" "On Windows, only sockets are supported; on Unix, all file\n" @@ -70,7 +71,7 @@ select_select(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) PyDoc_STRVAR(select_poll_register__doc__, "register($self, fd,\n" @@ -119,9 +120,9 @@ select_poll_register(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) PyDoc_STRVAR(select_poll_modify__doc__, "modify($self, fd, eventmask, /)\n" @@ -166,9 +167,9 @@ select_poll_modify(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) PyDoc_STRVAR(select_poll_unregister__doc__, "unregister($self, fd, /)\n" @@ -200,9 +201,9 @@ select_poll_unregister(PyObject *self, PyObject *arg) return return_value; } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) PyDoc_STRVAR(select_poll_poll__doc__, "poll($self, timeout=None, /)\n" @@ -214,8 +215,8 @@ PyDoc_STRVAR(select_poll_poll__doc__, " The maximum time to wait in milliseconds, or else None (or a negative\n" " value) to wait indefinitely.\n" "\n" -"Returns a list containing any descriptors that have events or errors to\n" -"report, as a list of (fd, event) 2-tuples."); +"Returns a list containing any descriptors that have events or errors\n" +"to report, as a list of (fd, event) 2-tuples."); #define SELECT_POLL_POLL_METHODDEF \ {"poll", _PyCFunction_CAST(select_poll_poll), METH_FASTCALL, select_poll_poll__doc__}, @@ -245,9 +246,9 @@ select_poll_poll(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) PyDoc_STRVAR(select_devpoll_register__doc__, "register($self, fd,\n" @@ -298,9 +299,9 @@ select_devpoll_register(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) PyDoc_STRVAR(select_devpoll_modify__doc__, "modify($self, fd,\n" @@ -351,9 +352,9 @@ select_devpoll_modify(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) PyDoc_STRVAR(select_devpoll_unregister__doc__, "unregister($self, fd, /)\n" @@ -385,9 +386,9 @@ select_devpoll_unregister(PyObject *self, PyObject *arg) return return_value; } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) PyDoc_STRVAR(select_devpoll_poll__doc__, "poll($self, timeout=None, /)\n" @@ -396,11 +397,11 @@ PyDoc_STRVAR(select_devpoll_poll__doc__, "Polls the set of registered file descriptors.\n" "\n" " timeout\n" -" The maximum time to wait in milliseconds, or else None (or a negative\n" -" value) to wait indefinitely.\n" +" The maximum time to wait in milliseconds, or else None (or\n" +" a negative value) to wait indefinitely.\n" "\n" -"Returns a list containing any descriptors that have events or errors to\n" -"report, as a list of (fd, event) 2-tuples."); +"Returns a list containing any descriptors that have events or errors\n" +"to report, as a list of (fd, event) 2-tuples."); #define SELECT_DEVPOLL_POLL_METHODDEF \ {"poll", _PyCFunction_CAST(select_devpoll_poll), METH_FASTCALL, select_devpoll_poll__doc__}, @@ -430,9 +431,9 @@ select_devpoll_poll(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) PyDoc_STRVAR(select_devpoll_close__doc__, "close($self, /)\n" @@ -460,9 +461,9 @@ select_devpoll_close(PyObject *self, PyObject *Py_UNUSED(ignored)) return return_value; } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) PyDoc_STRVAR(select_devpoll_fileno__doc__, "fileno($self, /)\n" @@ -488,9 +489,9 @@ select_devpoll_fileno(PyObject *self, PyObject *Py_UNUSED(ignored)) return return_value; } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) PyDoc_STRVAR(select_poll__doc__, "poll($module, /)\n" @@ -498,8 +499,8 @@ PyDoc_STRVAR(select_poll__doc__, "\n" "Returns a polling object.\n" "\n" -"This object supports registering and unregistering file descriptors, and then\n" -"polling them for I/O events."); +"This object supports registering and unregistering file descriptors, and\n" +"then polling them for I/O events."); #define SELECT_POLL_METHODDEF \ {"poll", (PyCFunction)select_poll, METH_NOARGS, select_poll__doc__}, @@ -513,9 +514,9 @@ select_poll(PyObject *module, PyObject *Py_UNUSED(ignored)) return select_poll_impl(module); } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) */ -#if (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) +#if ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) PyDoc_STRVAR(select_devpoll__doc__, "devpoll($module, /)\n" @@ -523,8 +524,8 @@ PyDoc_STRVAR(select_devpoll__doc__, "\n" "Returns a polling object.\n" "\n" -"This object supports registering and unregistering file descriptors, and then\n" -"polling them for I/O events."); +"This object supports registering and unregistering file descriptors, and\n" +"then polling them for I/O events."); #define SELECT_DEVPOLL_METHODDEF \ {"devpoll", (PyCFunction)select_devpoll, METH_NOARGS, select_devpoll__doc__}, @@ -538,7 +539,7 @@ select_devpoll(PyObject *module, PyObject *Py_UNUSED(ignored)) return select_devpoll_impl(module); } -#endif /* (defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ +#endif /* ((defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL)) && defined(HAVE_SYS_DEVPOLL_H) */ #if defined(HAVE_EPOLL) @@ -973,13 +974,13 @@ PyDoc_STRVAR(select_epoll_poll__doc__, "Wait for events on the epoll file descriptor.\n" "\n" " timeout\n" -" the maximum time to wait in seconds (as float);\n" +" the maximum time to wait in seconds (with fractions);\n" " a timeout of None or -1 makes poll wait indefinitely\n" " maxevents\n" " the maximum number of events returned; -1 means no limit\n" "\n" -"Returns a list containing any descriptors that have events to report,\n" -"as a list of (fd, events) 2-tuples."); +"Returns a list containing any descriptors that have events to\n" +"report, as a list of (fd, events) 2-tuples."); #define SELECT_EPOLL_POLL_METHODDEF \ {"poll", _PyCFunction_CAST(select_epoll_poll), METH_FASTCALL|METH_KEYWORDS, select_epoll_poll__doc__}, @@ -1262,7 +1263,7 @@ PyDoc_STRVAR(select_kqueue_control__doc__, " The maximum number of events that the kernel will return.\n" " timeout\n" " The maximum time to wait in seconds, or else None to wait forever.\n" -" This accepts floats for smaller timeouts, too."); +" This accepts non-integers for smaller timeouts, too."); #define SELECT_KQUEUE_CONTROL_METHODDEF \ {"control", _PyCFunction_CAST(select_kqueue_control), METH_FASTCALL, select_kqueue_control__doc__}, @@ -1399,4 +1400,4 @@ select_kqueue_control(PyObject *self, PyObject *const *args, Py_ssize_t nargs) #ifndef SELECT_KQUEUE_CONTROL_METHODDEF #define SELECT_KQUEUE_CONTROL_METHODDEF #endif /* !defined(SELECT_KQUEUE_CONTROL_METHODDEF) */ -/*[clinic end generated code: output=2a66dd831f22c696 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a1ac666294fd14bd input=a9049054013a1b77]*/ diff --git a/Modules/clinic/sha3module.c.h b/Modules/clinic/sha3module.c.h index 1f631ff406e25b..7fdc707626c324 100644 --- a/Modules/clinic/sha3module.c.h +++ b/Modules/clinic/sha3module.c.h @@ -235,6 +235,11 @@ _sha3_shake_128_digest(PyObject *self, PyObject *const *args, Py_ssize_t nargs, goto exit; } length = ival; + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length cannot be negative"); + goto exit; + } } return_value = _sha3_shake_128_digest_impl((SHA3object *)self, length); @@ -304,10 +309,15 @@ _sha3_shake_128_hexdigest(PyObject *self, PyObject *const *args, Py_ssize_t narg goto exit; } length = ival; + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length cannot be negative"); + goto exit; + } } return_value = _sha3_shake_128_hexdigest_impl((SHA3object *)self, length); exit: return return_value; } -/*[clinic end generated code: output=48be77f8a31e8a3e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=78284adde71d590c input=a9049054013a1b77]*/ diff --git a/Modules/clinic/signalmodule.c.h b/Modules/clinic/signalmodule.c.h index b0cd9e2e561640..ca47033446074c 100644 --- a/Modules/clinic/signalmodule.c.h +++ b/Modules/clinic/signalmodule.c.h @@ -138,11 +138,12 @@ PyDoc_STRVAR(signal_signal__doc__, "Set the action for the given signal.\n" "\n" "The action can be SIG_DFL, SIG_IGN, or a callable Python object.\n" -"The previous action is returned. See getsignal() for possible return values.\n" +"The previous action is returned. See getsignal() for possible return\n" +"values.\n" "\n" "*** IMPORTANT NOTICE ***\n" -"A signal handler function is called with two arguments:\n" -"the first is the signal number, the second is the interrupted stack frame."); +"A signal handler function is called with two arguments: the first is\n" +"the signal number, the second is the interrupted stack frame."); #define SIGNAL_SIGNAL_METHODDEF \ {"signal", _PyCFunction_CAST(signal_signal), METH_FASTCALL, signal_signal__doc__}, @@ -362,8 +363,8 @@ PyDoc_STRVAR(signal_setitimer__doc__, "\n" "Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL or ITIMER_PROF).\n" "\n" -"The timer will fire after value seconds and after that every interval seconds.\n" -"The itimer can be cleared by setting seconds to zero.\n" +"The timer will fire after value seconds and after that every interval\n" +"seconds. The itimer can be cleared by setting seconds to zero.\n" "\n" "Returns old values as a tuple: (delay, interval)."); @@ -508,8 +509,8 @@ PyDoc_STRVAR(signal_sigwait__doc__, "Wait for a signal.\n" "\n" "Suspend execution of the calling thread until the delivery of one of the\n" -"signals specified in the signal set sigset. The function accepts the signal\n" -"and returns the signal number."); +"signals specified in the signal set sigset. The function accepts the\n" +"signal and returns the signal number."); #define SIGNAL_SIGWAIT_METHODDEF \ {"sigwait", (PyCFunction)signal_sigwait, METH_O, signal_sigwait__doc__}, @@ -600,7 +601,7 @@ PyDoc_STRVAR(signal_sigtimedwait__doc__, "\n" "Like sigwaitinfo(), but with a timeout.\n" "\n" -"The timeout is specified in seconds, with floating-point numbers allowed."); +"The timeout is specified in seconds, rounded up to nanoseconds."); #define SIGNAL_SIGTIMEDWAIT_METHODDEF \ {"sigtimedwait", _PyCFunction_CAST(signal_sigtimedwait), METH_FASTCALL, signal_sigtimedwait__doc__}, @@ -794,4 +795,4 @@ signal_pidfd_send_signal(PyObject *module, PyObject *const *args, Py_ssize_t nar #ifndef SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF #define SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF #endif /* !defined(SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF) */ -/*[clinic end generated code: output=37ae8ebeae4178fa input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0731d6f05c42c09a input=a9049054013a1b77]*/ diff --git a/Modules/clinic/socketmodule.c.h b/Modules/clinic/socketmodule.c.h index 0cedab597db714..b565e7516d50f3 100644 --- a/Modules/clinic/socketmodule.c.h +++ b/Modules/clinic/socketmodule.c.h @@ -36,7 +36,8 @@ PyDoc_STRVAR(_socket_socket_send__doc__, "Send a data string to the socket.\n" "\n" "For the optional flags argument, see the Unix manual.\n" -"Return the number of bytes sent; this may be less than len(data) if the network is busy."); +"Return the number of bytes sent; this may be less than len(data) if\n" +"the network is busy."); #define _SOCKET_SOCKET_SEND_METHODDEF \ {"send", _PyCFunction_CAST(_socket_socket_send), METH_FASTCALL, _socket_socket_send__doc__}, @@ -84,7 +85,8 @@ PyDoc_STRVAR(_socket_socket_sendall__doc__, "\n" "For the optional flags argument, see the Unix manual.\n" "This calls send() repeatedly until all data is sent.\n" -"If an error occurs, it\'s impossible to tell how much data has been sent."); +"If an error occurs, it\'s impossible to tell how much data has been\n" +"sent."); #define _SOCKET_SOCKET_SENDALL_METHODDEF \ {"sendall", _PyCFunction_CAST(_socket_socket_sendall), METH_FASTCALL, _socket_socket_sendall__doc__}, @@ -140,13 +142,13 @@ PyDoc_STRVAR(_socket_socket_sendmsg__doc__, "data as an iterable of bytes-like objects (e.g. bytes objects).\n" "The ancdata argument specifies the ancillary data (control messages)\n" "as an iterable of zero or more tuples (cmsg_level, cmsg_type,\n" -"cmsg_data), where cmsg_level and cmsg_type are integers specifying the\n" -"protocol level and protocol-specific type respectively, and cmsg_data\n" -"is a bytes-like object holding the associated data. The flags\n" -"argument defaults to 0 and has the same meaning as for send(). If\n" -"address is supplied and not None, it sets a destination address for\n" -"the message. The return value is the number of bytes of non-ancillary\n" -"data sent."); +"cmsg_data), where cmsg_level and cmsg_type are integers specifying\n" +"the protocol level and protocol-specific type respectively, and\n" +"cmsg_data is a bytes-like object holding the associated data. The\n" +"flags argument defaults to 0 and has the same meaning as for send().\n" +"If address is supplied and not None, it sets a destination address\n" +"for the message. The return value is the number of bytes of\n" +"non-ancillary data sent."); #define _SOCKET_SOCKET_SENDMSG_METHODDEF \ {"sendmsg", _PyCFunction_CAST(_socket_socket_sendmsg), METH_FASTCALL, _socket_socket_sendmsg__doc__}, @@ -479,7 +481,7 @@ static PyObject * _socket_if_nametoindex(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; - PyObject *oname; + PyObject *oname = NULL; if (!PyUnicode_FSConverter(arg, &oname)) { goto exit; @@ -487,6 +489,9 @@ _socket_if_nametoindex(PyObject *module, PyObject *arg) return_value = _socket_if_nametoindex_impl(module, oname); exit: + /* Cleanup for oname */ + Py_XDECREF(oname); + return return_value; } @@ -538,4 +543,4 @@ _socket_if_indextoname(PyObject *module, PyObject *arg) #ifndef _SOCKET_IF_INDEXTONAME_METHODDEF #define _SOCKET_IF_INDEXTONAME_METHODDEF #endif /* !defined(_SOCKET_IF_INDEXTONAME_METHODDEF) */ -/*[clinic end generated code: output=0376c46b76ae2bce input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0b1fa78ac6589353 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/symtablemodule.c.h b/Modules/clinic/symtablemodule.c.h index 2ecd3afc00d2be..65352593f94802 100644 --- a/Modules/clinic/symtablemodule.c.h +++ b/Modules/clinic/symtablemodule.c.h @@ -2,30 +2,67 @@ preserve [clinic start generated code]*/ -#include "pycore_modsupport.h" // _PyArg_CheckPositional() +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(_symtable_symtable__doc__, -"symtable($module, source, filename, startstr, /)\n" +"symtable($module, source, filename, startstr, /, *, module=None)\n" "--\n" "\n" "Return symbol and scope dictionaries used internally by compiler."); #define _SYMTABLE_SYMTABLE_METHODDEF \ - {"symtable", _PyCFunction_CAST(_symtable_symtable), METH_FASTCALL, _symtable_symtable__doc__}, + {"symtable", _PyCFunction_CAST(_symtable_symtable), METH_FASTCALL|METH_KEYWORDS, _symtable_symtable__doc__}, static PyObject * _symtable_symtable_impl(PyObject *module, PyObject *source, - PyObject *filename, const char *startstr); + PyObject *filename, const char *startstr, + PyObject *modname); static PyObject * -_symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +_symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(module), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "", "", "module", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "symtable", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[4]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 3; PyObject *source; - PyObject *filename; + PyObject *filename = NULL; const char *startstr; + PyObject *modname = Py_None; - if (!_PyArg_CheckPositional("symtable", nargs, 3, 3)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { goto exit; } source = args[0]; @@ -45,9 +82,17 @@ _symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyErr_SetString(PyExc_ValueError, "embedded null character"); goto exit; } - return_value = _symtable_symtable_impl(module, source, filename, startstr); + if (!noptargs) { + goto skip_optional_kwonly; + } + modname = args[3]; +skip_optional_kwonly: + return_value = _symtable_symtable_impl(module, source, filename, startstr, modname); exit: + /* Cleanup for filename */ + Py_XDECREF(filename); + return return_value; } -/*[clinic end generated code: output=931964a76a72f850 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0137be60c487c841 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/termios.c.h b/Modules/clinic/termios.c.h index 83f5a4f6e9f882..35522bef1dcae9 100644 --- a/Modules/clinic/termios.c.h +++ b/Modules/clinic/termios.c.h @@ -270,7 +270,8 @@ PyDoc_STRVAR(termios_tcsetwinsize__doc__, "Set the tty winsize for file descriptor fd.\n" "\n" "The winsize to be set is taken from the winsize argument, which\n" -"is a two-item tuple (ws_row, ws_col) like the one returned by tcgetwinsize()."); +"is a two-item tuple (ws_row, ws_col) like the one returned by\n" +"tcgetwinsize()."); #define TERMIOS_TCSETWINSIZE_METHODDEF \ {"tcsetwinsize", (PyCFunction)(void(*)(void))termios_tcsetwinsize, METH_FASTCALL, termios_tcsetwinsize__doc__}, @@ -299,4 +300,4 @@ termios_tcsetwinsize(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=c6c6192583b0da36 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d2176c4d9043d3cc input=a9049054013a1b77]*/ diff --git a/Modules/clinic/unicodedata.c.h b/Modules/clinic/unicodedata.c.h index 345440eeee89a6..5443893079b1af 100644 --- a/Modules/clinic/unicodedata.c.h +++ b/Modules/clinic/unicodedata.c.h @@ -2,6 +2,7 @@ preserve [clinic start generated code]*/ +#include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_modsupport.h" // _PyArg_CheckPositional() PyDoc_STRVAR(unicodedata_UCD_decimal__doc__, @@ -518,6 +519,78 @@ unicodedata_UCD_name(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } +PyDoc_STRVAR(unicodedata_isxidstart__doc__, +"isxidstart($module, chr, /)\n" +"--\n" +"\n" +"Return True if the character has the XID_Start property, else False."); + +#define UNICODEDATA_ISXIDSTART_METHODDEF \ + {"isxidstart", (PyCFunction)unicodedata_isxidstart, METH_O, unicodedata_isxidstart__doc__}, + +static PyObject * +unicodedata_isxidstart_impl(PyObject *module, int chr); + +static PyObject * +unicodedata_isxidstart(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int chr; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("isxidstart", "argument", "a unicode character", arg); + goto exit; + } + if (PyUnicode_GET_LENGTH(arg) != 1) { + PyErr_Format(PyExc_TypeError, + "isxidstart(): argument must be a unicode character, " + "not a string of length %zd", + PyUnicode_GET_LENGTH(arg)); + goto exit; + } + chr = PyUnicode_READ_CHAR(arg, 0); + return_value = unicodedata_isxidstart_impl(module, chr); + +exit: + return return_value; +} + +PyDoc_STRVAR(unicodedata_isxidcontinue__doc__, +"isxidcontinue($module, chr, /)\n" +"--\n" +"\n" +"Return True if the character has the XID_Continue property, else False."); + +#define UNICODEDATA_ISXIDCONTINUE_METHODDEF \ + {"isxidcontinue", (PyCFunction)unicodedata_isxidcontinue, METH_O, unicodedata_isxidcontinue__doc__}, + +static PyObject * +unicodedata_isxidcontinue_impl(PyObject *module, int chr); + +static PyObject * +unicodedata_isxidcontinue(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int chr; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("isxidcontinue", "argument", "a unicode character", arg); + goto exit; + } + if (PyUnicode_GET_LENGTH(arg) != 1) { + PyErr_Format(PyExc_TypeError, + "isxidcontinue(): argument must be a unicode character, " + "not a string of length %zd", + PyUnicode_GET_LENGTH(arg)); + goto exit; + } + chr = PyUnicode_READ_CHAR(arg, 0); + return_value = unicodedata_isxidcontinue_impl(module, chr); + +exit: + return return_value; +} + PyDoc_STRVAR(unicodedata_UCD_lookup__doc__, "lookup($self, name, /)\n" "--\n" @@ -549,4 +622,216 @@ unicodedata_UCD_lookup(PyObject *self, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=8a59d430cee41058 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(unicodedata_iter_graphemes__doc__, +"iter_graphemes($module, unistr, start=0, end=sys.maxsize, /)\n" +"--\n" +"\n" +"Returns an iterator to iterate over grapheme clusters.\n" +"\n" +"It uses extended grapheme cluster rules from TR29."); + +#define UNICODEDATA_ITER_GRAPHEMES_METHODDEF \ + {"iter_graphemes", _PyCFunction_CAST(unicodedata_iter_graphemes), METH_FASTCALL, unicodedata_iter_graphemes__doc__}, + +static PyObject * +unicodedata_iter_graphemes_impl(PyObject *module, PyObject *unistr, + Py_ssize_t start, Py_ssize_t end); + +static PyObject * +unicodedata_iter_graphemes(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *unistr; + Py_ssize_t start = 0; + Py_ssize_t end = PY_SSIZE_T_MAX; + + if (!_PyArg_CheckPositional("iter_graphemes", nargs, 1, 3)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("iter_graphemes", "argument 1", "str", args[0]); + goto exit; + } + unistr = args[0]; + if (nargs < 2) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + start = ival; + } + if (nargs < 3) { + goto skip_optional; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + end = ival; + } +skip_optional: + return_value = unicodedata_iter_graphemes_impl(module, unistr, start, end); + +exit: + return return_value; +} + +PyDoc_STRVAR(unicodedata_block__doc__, +"block($module, chr, /)\n" +"--\n" +"\n" +"Return block assigned to the character chr."); + +#define UNICODEDATA_BLOCK_METHODDEF \ + {"block", (PyCFunction)unicodedata_block, METH_O, unicodedata_block__doc__}, + +static PyObject * +unicodedata_block_impl(PyObject *module, int chr); + +static PyObject * +unicodedata_block(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int chr; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("block", "argument", "a unicode character", arg); + goto exit; + } + if (PyUnicode_GET_LENGTH(arg) != 1) { + PyErr_Format(PyExc_TypeError, + "block(): argument must be a unicode character, " + "not a string of length %zd", + PyUnicode_GET_LENGTH(arg)); + goto exit; + } + chr = PyUnicode_READ_CHAR(arg, 0); + return_value = unicodedata_block_impl(module, chr); + +exit: + return return_value; +} + +PyDoc_STRVAR(unicodedata_grapheme_cluster_break__doc__, +"grapheme_cluster_break($module, chr, /)\n" +"--\n" +"\n" +"Returns the Grapheme_Cluster_Break property assigned to the character."); + +#define UNICODEDATA_GRAPHEME_CLUSTER_BREAK_METHODDEF \ + {"grapheme_cluster_break", (PyCFunction)unicodedata_grapheme_cluster_break, METH_O, unicodedata_grapheme_cluster_break__doc__}, + +static PyObject * +unicodedata_grapheme_cluster_break_impl(PyObject *module, int chr); + +static PyObject * +unicodedata_grapheme_cluster_break(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int chr; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("grapheme_cluster_break", "argument", "a unicode character", arg); + goto exit; + } + if (PyUnicode_GET_LENGTH(arg) != 1) { + PyErr_Format(PyExc_TypeError, + "grapheme_cluster_break(): argument must be a unicode character, " + "not a string of length %zd", + PyUnicode_GET_LENGTH(arg)); + goto exit; + } + chr = PyUnicode_READ_CHAR(arg, 0); + return_value = unicodedata_grapheme_cluster_break_impl(module, chr); + +exit: + return return_value; +} + +PyDoc_STRVAR(unicodedata_indic_conjunct_break__doc__, +"indic_conjunct_break($module, chr, /)\n" +"--\n" +"\n" +"Returns the Indic_Conjunct_Break property assigned to the character."); + +#define UNICODEDATA_INDIC_CONJUNCT_BREAK_METHODDEF \ + {"indic_conjunct_break", (PyCFunction)unicodedata_indic_conjunct_break, METH_O, unicodedata_indic_conjunct_break__doc__}, + +static PyObject * +unicodedata_indic_conjunct_break_impl(PyObject *module, int chr); + +static PyObject * +unicodedata_indic_conjunct_break(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int chr; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("indic_conjunct_break", "argument", "a unicode character", arg); + goto exit; + } + if (PyUnicode_GET_LENGTH(arg) != 1) { + PyErr_Format(PyExc_TypeError, + "indic_conjunct_break(): argument must be a unicode character, " + "not a string of length %zd", + PyUnicode_GET_LENGTH(arg)); + goto exit; + } + chr = PyUnicode_READ_CHAR(arg, 0); + return_value = unicodedata_indic_conjunct_break_impl(module, chr); + +exit: + return return_value; +} + +PyDoc_STRVAR(unicodedata_extended_pictographic__doc__, +"extended_pictographic($module, chr, /)\n" +"--\n" +"\n" +"Returns the Extended_Pictographic property assigned to the character, as boolean."); + +#define UNICODEDATA_EXTENDED_PICTOGRAPHIC_METHODDEF \ + {"extended_pictographic", (PyCFunction)unicodedata_extended_pictographic, METH_O, unicodedata_extended_pictographic__doc__}, + +static PyObject * +unicodedata_extended_pictographic_impl(PyObject *module, int chr); + +static PyObject * +unicodedata_extended_pictographic(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int chr; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("extended_pictographic", "argument", "a unicode character", arg); + goto exit; + } + if (PyUnicode_GET_LENGTH(arg) != 1) { + PyErr_Format(PyExc_TypeError, + "extended_pictographic(): argument must be a unicode character, " + "not a string of length %zd", + PyUnicode_GET_LENGTH(arg)); + goto exit; + } + chr = PyUnicode_READ_CHAR(arg, 0); + return_value = unicodedata_extended_pictographic_impl(module, chr); + +exit: + return return_value; +} +/*[clinic end generated code: output=482a87df218f07c1 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/zlibmodule.c.h b/Modules/clinic/zlibmodule.c.h index 016af258d63dea..620e483d5a759a 100644 --- a/Modules/clinic/zlibmodule.c.h +++ b/Modules/clinic/zlibmodule.c.h @@ -189,6 +189,11 @@ zlib_decompress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj goto exit; } bufsize = ival; + if (bufsize < 0) { + PyErr_SetString(PyExc_ValueError, + "bufsize cannot be negative"); + goto exit; + } } skip_optional_pos: return_value = zlib_decompress_impl(module, &data, wbits, bufsize); @@ -205,7 +210,7 @@ zlib_decompress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj PyDoc_STRVAR(zlib_compressobj__doc__, "compressobj($module, /, level=Z_DEFAULT_COMPRESSION, method=DEFLATED,\n" " wbits=MAX_WBITS, memLevel=DEF_MEM_LEVEL,\n" -" strategy=Z_DEFAULT_STRATEGY, zdict=None)\n" +" strategy=Z_DEFAULT_STRATEGY, zdict=<unrepresentable>)\n" "--\n" "\n" "Return a compressor object.\n" @@ -498,8 +503,8 @@ PyDoc_STRVAR(zlib_Decompress_decompress__doc__, " Unconsumed input data will be stored in\n" " the unconsumed_tail attribute.\n" "\n" -"After calling this function, some of the input data may still be stored in\n" -"internal buffers for later processing.\n" +"After calling this function, some of the input data may still be\n" +"stored in internal buffers for later processing.\n" "Call the flush() method to clear these buffers."); #define ZLIB_DECOMPRESS_DECOMPRESS_METHODDEF \ @@ -567,6 +572,11 @@ zlib_Decompress_decompress(PyObject *self, PyTypeObject *cls, PyObject *const *a goto exit; } max_length = ival; + if (max_length < 0) { + PyErr_SetString(PyExc_ValueError, + "max_length cannot be negative"); + goto exit; + } } skip_optional_pos: return_value = zlib_Decompress_decompress_impl((compobject *)self, cls, &data, max_length); @@ -898,34 +908,36 @@ zlib_Decompress_flush(PyObject *self, PyTypeObject *cls, PyObject *const *args, return return_value; } -PyDoc_STRVAR(zlib_ZlibDecompressor_decompress__doc__, +PyDoc_STRVAR(zlib__ZlibDecompressor_decompress__doc__, "decompress($self, /, data, max_length=-1)\n" "--\n" "\n" "Decompress *data*, returning uncompressed data as bytes.\n" "\n" -"If *max_length* is nonnegative, returns at most *max_length* bytes of\n" -"decompressed data. If this limit is reached and further output can be\n" -"produced, *self.needs_input* will be set to ``False``. In this case, the next\n" -"call to *decompress()* may provide *data* as b\'\' to obtain more of the output.\n" +"If *max_length* is nonnegative, returns at most *max_length* bytes\n" +"of decompressed data. If this limit is reached and further output\n" +"can be produced, *self.needs_input* will be set to ``False``. In\n" +"this case, the next call to *decompress()* may provide *data* as b\'\'\n" +"to obtain more of the output.\n" "\n" -"If all of the input data was decompressed and returned (either because this\n" -"was less than *max_length* bytes, or because *max_length* was negative),\n" -"*self.needs_input* will be set to True.\n" +"If all of the input data was decompressed and returned (either\n" +"because this was less than *max_length* bytes, or because\n" +"*max_length* was negative), *self.needs_input* will be set to True.\n" "\n" -"Attempting to decompress data after the end of stream is reached raises an\n" -"EOFError. Any data found after the end of the stream is ignored and saved in\n" -"the unused_data attribute."); +"Attempting to decompress data after the end of stream is reached\n" +"raises an EOFError. Any data found after the end of the stream is\n" +"ignored and saved in the unused_data attribute."); -#define ZLIB_ZLIBDECOMPRESSOR_DECOMPRESS_METHODDEF \ - {"decompress", _PyCFunction_CAST(zlib_ZlibDecompressor_decompress), METH_FASTCALL|METH_KEYWORDS, zlib_ZlibDecompressor_decompress__doc__}, +#define ZLIB__ZLIBDECOMPRESSOR_DECOMPRESS_METHODDEF \ + {"decompress", _PyCFunction_CAST(zlib__ZlibDecompressor_decompress), METH_FASTCALL|METH_KEYWORDS, zlib__ZlibDecompressor_decompress__doc__}, static PyObject * -zlib_ZlibDecompressor_decompress_impl(ZlibDecompressor *self, - Py_buffer *data, Py_ssize_t max_length); +zlib__ZlibDecompressor_decompress_impl(ZlibDecompressor *self, + Py_buffer *data, + Py_ssize_t max_length); static PyObject * -zlib_ZlibDecompressor_decompress(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +zlib__ZlibDecompressor_decompress(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -984,7 +996,7 @@ zlib_ZlibDecompressor_decompress(PyObject *self, PyObject *const *args, Py_ssize max_length = ival; } skip_optional_pos: - return_value = zlib_ZlibDecompressor_decompress_impl((ZlibDecompressor *)self, &data, max_length); + return_value = zlib__ZlibDecompressor_decompress_impl((ZlibDecompressor *)self, &data, max_length); exit: /* Cleanup for data */ @@ -995,6 +1007,86 @@ zlib_ZlibDecompressor_decompress(PyObject *self, PyObject *const *args, Py_ssize return return_value; } +PyDoc_STRVAR(zlib__ZlibDecompressor__doc__, +"_ZlibDecompressor(wbits=MAX_WBITS, zdict=b\'\')\n" +"--\n" +"\n" +"Create a decompressor object for decompressing data incrementally.\n" +"\n" +" zdict\n" +" The predefined compression dictionary. This is a sequence of bytes\n" +" (such as a bytes object) containing subsequences that are expected\n" +" to occur frequently in the data that is to be compressed. Those\n" +" subsequences that are expected to be most common should come at the\n" +" end of the dictionary. This must be the same dictionary as used by the\n" +" compressor that produced the input data."); + +static PyObject * +zlib__ZlibDecompressor_impl(PyTypeObject *type, int wbits, PyObject *zdict); + +static PyObject * +zlib__ZlibDecompressor(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(wbits), &_Py_ID(zdict), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"wbits", "zdict", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_ZlibDecompressor", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; + int wbits = MAX_WBITS; + PyObject *zdict = NULL; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (fastargs[0]) { + wbits = PyLong_AsInt(fastargs[0]); + if (wbits == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + zdict = fastargs[1]; +skip_optional_pos: + return_value = zlib__ZlibDecompressor_impl(type, wbits, zdict); + +exit: + return return_value; +} + PyDoc_STRVAR(zlib_adler32__doc__, "adler32($module, data, value=1, /)\n" "--\n" @@ -1311,4 +1403,4 @@ zlib_crc32_combine(PyObject *module, PyObject *const *args, Py_ssize_t nargs) #ifndef ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF #define ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF #endif /* !defined(ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF) */ -/*[clinic end generated code: output=3054c8894aa44568 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c9a60fe6600a2e4d input=a9049054013a1b77]*/ diff --git a/Modules/cmathmodule.c b/Modules/cmathmodule.c index 81cbf0d554de3c..7c736f4610bb98 100644 --- a/Modules/cmathmodule.c +++ b/Modules/cmathmodule.c @@ -150,7 +150,7 @@ special_type(double d) #define P14 0.25*Py_MATH_PI #define P12 0.5*Py_MATH_PI #define P34 0.75*Py_MATH_PI -#define INF Py_INFINITY +#define INF INFINITY #define N Py_NAN #define U -9.5426319407711027e33 /* unlikely value, used as placeholder */ @@ -163,8 +163,15 @@ special_type(double d) raised. */ -static Py_complex acos_special_values[7][7]; - +static Py_complex acos_special_values[7][7] = { + { {P34,INF}, {P,INF}, {P,INF}, {P,-INF}, {P,-INF}, {P34,-INF}, {N,INF} }, + { {P12,INF}, {U,U}, {U,U}, {U,U}, {U,U}, {P12,-INF}, {N,N} }, + { {P12,INF}, {U,U}, {P12,0.}, {P12,-0.}, {U,U}, {P12,-INF}, {P12,N} }, + { {P12,INF}, {U,U}, {P12,0.}, {P12,-0.}, {U,U}, {P12,-INF}, {P12,N} }, + { {P12,INF}, {U,U}, {U,U}, {U,U}, {U,U}, {P12,-INF}, {N,N} }, + { {P14,INF}, {0.,INF}, {0.,INF}, {0.,-INF}, {0.,-INF}, {P14,-INF}, {N,INF} }, + { {N,INF}, {N,N}, {N,N}, {N,N}, {N,N}, {N,-INF}, {N,N} } +}; /*[clinic input] cmath.acos -> Py_complex_protected @@ -202,7 +209,15 @@ cmath_acos_impl(PyObject *module, Py_complex z) } -static Py_complex acosh_special_values[7][7]; +static Py_complex acosh_special_values[7][7] = { + { {INF,-P34}, {INF,-P}, {INF,-P}, {INF,P}, {INF,P}, {INF,P34}, {INF,N} }, + { {INF,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {INF,P12}, {N,N} }, + { {INF,-P12}, {U,U}, {0.,-P12}, {0.,P12}, {U,U}, {INF,P12}, {N,P12} }, + { {INF,-P12}, {U,U}, {0.,-P12}, {0.,P12}, {U,U}, {INF,P12}, {N,P12} }, + { {INF,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {INF,P12}, {N,N} }, + { {INF,-P14}, {INF,-0.}, {INF,-0.}, {INF,0.}, {INF,0.}, {INF,P14}, {INF,N} }, + { {INF,N}, {N,N}, {N,N}, {N,N}, {N,N}, {INF,N}, {N,N} } +}; /*[clinic input] cmath.acosh = cmath.acos @@ -257,7 +272,15 @@ cmath_asin_impl(PyObject *module, Py_complex z) } -static Py_complex asinh_special_values[7][7]; +static Py_complex asinh_special_values[7][7] = { + { {-INF,-P14}, {-INF,-0.}, {-INF,-0.}, {-INF,0.}, {-INF,0.}, {-INF,P14}, {-INF,N} }, + { {-INF,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {-INF,P12}, {N,N} }, + { {-INF,-P12}, {U,U}, {-0.,-0.}, {-0.,0.}, {U,U}, {-INF,P12}, {N,N} }, + { {INF,-P12}, {U,U}, {0.,-0.}, {0.,0.}, {U,U}, {INF,P12}, {N,N} }, + { {INF,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {INF,P12}, {N,N} }, + { {INF,-P14}, {INF,-0.}, {INF,-0.}, {INF,0.}, {INF,0.}, {INF,P14}, {INF,N} }, + { {INF,N}, {N,N}, {N,-0.}, {N,0.}, {N,N}, {INF,N}, {N,N} } +}; /*[clinic input] cmath.asinh = cmath.acos @@ -318,7 +341,15 @@ cmath_atan_impl(PyObject *module, Py_complex z) } -static Py_complex atanh_special_values[7][7]; +static Py_complex atanh_special_values[7][7] = { + { {-0.,-P12}, {-0.,-P12}, {-0.,-P12}, {-0.,P12}, {-0.,P12}, {-0.,P12}, {-0.,N} }, + { {-0.,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {-0.,P12}, {N,N} }, + { {-0.,-P12}, {U,U}, {-0.,-0.}, {-0.,0.}, {U,U}, {-0.,P12}, {-0.,N} }, + { {0.,-P12}, {U,U}, {0.,-0.}, {0.,0.}, {U,U}, {0.,P12}, {0.,N} }, + { {0.,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {0.,P12}, {N,N} }, + { {0.,-P12}, {0.,-P12}, {0.,-P12}, {0.,P12}, {0.,P12}, {0.,P12}, {0.,N} }, + { {0.,-P12}, {N,N}, {N,N}, {N,N}, {N,N}, {0.,P12}, {N,N} } +}; /*[clinic input] cmath.atanh = cmath.acos @@ -391,7 +422,15 @@ cmath_cos_impl(PyObject *module, Py_complex z) /* cosh(infinity + i*y) needs to be dealt with specially */ -static Py_complex cosh_special_values[7][7]; +static Py_complex cosh_special_values[7][7] = { + { {INF,N}, {U,U}, {INF,0.}, {INF,-0.}, {U,U}, {INF,N}, {INF,N} }, + { {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} }, + { {N,0.}, {U,U}, {1.,0.}, {1.,-0.}, {U,U}, {N,0.}, {N,0.} }, + { {N,0.}, {U,U}, {1.,-0.}, {1.,0.}, {U,U}, {N,0.}, {N,0.} }, + { {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} }, + { {INF,N}, {U,U}, {INF,-0.}, {INF,0.}, {U,U}, {INF,N}, {INF,N} }, + { {N,N}, {N,N}, {N,0.}, {N,0.}, {N,N}, {N,N}, {N,N} } +}; /*[clinic input] cmath.cosh = cmath.acos @@ -453,7 +492,15 @@ cmath_cosh_impl(PyObject *module, Py_complex z) /* exp(infinity + i*y) and exp(-infinity + i*y) need special treatment for finite y */ -static Py_complex exp_special_values[7][7]; +static Py_complex exp_special_values[7][7] = { + { {0.,0.}, {U,U}, {0.,-0.}, {0.,0.}, {U,U}, {0.,0.}, {0.,0.} }, + { {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} }, + { {N,N}, {U,U}, {1.,-0.}, {1.,0.}, {U,U}, {N,N}, {N,N} }, + { {N,N}, {U,U}, {1.,-0.}, {1.,0.}, {U,U}, {N,N}, {N,N} }, + { {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} }, + { {INF,N}, {U,U}, {INF,-0.}, {INF,0.}, {U,U}, {INF,N}, {INF,N} }, + { {N,N}, {N,N}, {N,-0.}, {N,0.}, {N,N}, {N,N}, {N,N} } +}; /*[clinic input] cmath.exp = cmath.acos @@ -512,7 +559,15 @@ cmath_exp_impl(PyObject *module, Py_complex z) return r; } -static Py_complex log_special_values[7][7]; +static Py_complex log_special_values[7][7] = { + { {INF,-P34}, {INF,-P}, {INF,-P}, {INF,P}, {INF,P}, {INF,P34}, {INF,N} }, + { {INF,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {INF,P12}, {N,N} }, + { {INF,-P12}, {U,U}, {-INF,-P}, {-INF,P}, {U,U}, {INF,P12}, {N,N} }, + { {INF,-P12}, {U,U}, {-INF,-0.}, {-INF,0.}, {U,U}, {INF,P12}, {N,N} }, + { {INF,-P12}, {U,U}, {U,U}, {U,U}, {U,U}, {INF,P12}, {N,N} }, + { {INF,-P14}, {INF,-0.}, {INF,-0.}, {INF,0.}, {INF,0.}, {INF,P14}, {INF,N} }, + { {INF,N}, {N,N}, {N,N}, {N,N}, {N,N}, {INF,N}, {N,N} } +}; static Py_complex c_log(Py_complex z) @@ -628,7 +683,15 @@ cmath_sin_impl(PyObject *module, Py_complex z) /* sinh(infinity + i*y) needs to be dealt with specially */ -static Py_complex sinh_special_values[7][7]; +static Py_complex sinh_special_values[7][7] = { + { {INF,N}, {U,U}, {-INF,-0.}, {-INF,0.}, {U,U}, {INF,N}, {INF,N} }, + { {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} }, + { {0.,N}, {U,U}, {-0.,-0.}, {-0.,0.}, {U,U}, {0.,N}, {0.,N} }, + { {0.,N}, {U,U}, {0.,-0.}, {0.,0.}, {U,U}, {0.,N}, {0.,N} }, + { {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} }, + { {INF,N}, {U,U}, {INF,-0.}, {INF,0.}, {U,U}, {INF,N}, {INF,N} }, + { {N,N}, {N,N}, {N,-0.}, {N,0.}, {N,N}, {N,N}, {N,N} } +}; /*[clinic input] cmath.sinh = cmath.acos @@ -687,7 +750,15 @@ cmath_sinh_impl(PyObject *module, Py_complex z) } -static Py_complex sqrt_special_values[7][7]; +static Py_complex sqrt_special_values[7][7] = { + { {INF,-INF}, {0.,-INF}, {0.,-INF}, {0.,INF}, {0.,INF}, {INF,INF}, {N,INF} }, + { {INF,-INF}, {U,U}, {U,U}, {U,U}, {U,U}, {INF,INF}, {N,N} }, + { {INF,-INF}, {U,U}, {0.,-0.}, {0.,0.}, {U,U}, {INF,INF}, {N,N} }, + { {INF,-INF}, {U,U}, {0.,-0.}, {0.,0.}, {U,U}, {INF,INF}, {N,N} }, + { {INF,-INF}, {U,U}, {U,U}, {U,U}, {U,U}, {INF,INF}, {N,N} }, + { {INF,-INF}, {INF,-0.}, {INF,-0.}, {INF,0.}, {INF,0.}, {INF,INF}, {INF,N} }, + { {INF,-INF}, {N,N}, {N,N}, {N,N}, {N,N}, {INF,INF}, {N,N} } +}; /*[clinic input] cmath.sqrt = cmath.acos @@ -786,7 +857,15 @@ cmath_tan_impl(PyObject *module, Py_complex z) /* tanh(infinity + i*y) needs to be dealt with specially */ -static Py_complex tanh_special_values[7][7]; +static Py_complex tanh_special_values[7][7] = { + { {-1.,0.}, {U,U}, {-1.,-0.}, {-1.,0.}, {U,U}, {-1.,0.}, {-1.,0.} }, + { {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} }, + { {-0.0,N}, {U,U}, {-0.,-0.}, {-0.,0.}, {U,U}, {-0.0,N}, {-0.,N} }, + { {0.0,N}, {U,U}, {0.,-0.}, {0.,0.}, {U,U}, {0.0,N}, {0.,N} }, + { {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} }, + { {1.,0.}, {U,U}, {1.,-0.}, {1.,0.}, {U,U}, {1.,0.}, {1.,0.} }, + { {N,N}, {N,N}, {N,-0.}, {N,0.}, {N,N}, {N,N}, {N,N} } +}; /*[clinic input] cmath.tanh = cmath.acos @@ -869,12 +948,13 @@ cmath.log log(z[, base]) -> the logarithm of z to the given base. -If the base is not specified, returns the natural logarithm (base e) of z. +If the base is not specified, returns the natural logarithm (base e) +of z. [clinic start generated code]*/ static PyObject * cmath_log_impl(PyObject *module, Py_complex x, PyObject *y_obj) -/*[clinic end generated code: output=4effdb7d258e0d94 input=e1f81d4fcfd26497]*/ +/*[clinic end generated code: output=4effdb7d258e0d94 input=eb25de0757baf4a0]*/ { Py_complex y; @@ -969,7 +1049,15 @@ cmath_polar_impl(PyObject *module, Py_complex z) */ -static Py_complex rect_special_values[7][7]; +static Py_complex rect_special_values[7][7] = { + { {INF,N}, {U,U}, {-INF,0.}, {-INF,-0.}, {U,U}, {INF,N}, {INF,N} }, + { {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} }, + { {0.,0.}, {U,U}, {-0.,0.}, {-0.,-0.}, {U,U}, {0.,0.}, {0.,0.} }, + { {0.,0.}, {U,U}, {0.,-0.}, {0.,0.}, {U,U}, {0.,0.}, {0.,0.} }, + { {N,N}, {U,U}, {U,U}, {U,U}, {U,U}, {N,N}, {N,N} }, + { {INF,N}, {U,U}, {INF,-0.}, {INF,0.}, {U,U}, {INF,N}, {INF,N} }, + { {N,N}, {N,N}, {N,0.}, {N,0.}, {N,N}, {N,N}, {N,N} } +}; /*[clinic input] cmath.rect @@ -1035,6 +1123,7 @@ cmath_rect_impl(PyObject *module, double r, double phi) } /*[clinic input] +@permit_long_summary cmath.isfinite = cmath.polar Return True if both the real and imaginary parts of z are finite, else False. @@ -1042,7 +1131,7 @@ Return True if both the real and imaginary parts of z are finite, else False. static PyObject * cmath_isfinite_impl(PyObject *module, Py_complex z) -/*[clinic end generated code: output=ac76611e2c774a36 input=848e7ee701895815]*/ +/*[clinic end generated code: output=ac76611e2c774a36 input=e224f5c36d94f5da]*/ { return PyBool_FromLong(isfinite(z.real) && isfinite(z.imag)); } @@ -1090,17 +1179,18 @@ Determine whether two complex numbers are close in value. Return True if a is close in value to b, and False otherwise. -For the values to be considered close, the difference between them must be -smaller than at least one of the tolerances. +For the values to be considered close, the difference between them must +be smaller than at least one of the tolerances. --inf, inf and NaN behave similarly to the IEEE 754 Standard. That is, NaN is -not close to anything, even itself. inf and -inf are only close to themselves. +-inf, inf and NaN behave similarly to the IEEE 754 Standard. That is, +NaN is not close to anything, even itself. inf and -inf are only close +to themselves. [clinic start generated code]*/ static int cmath_isclose_impl(PyObject *module, Py_complex a, Py_complex b, double rel_tol, double abs_tol) -/*[clinic end generated code: output=8a2486cc6e0014d1 input=df9636d7de1d4ac3]*/ +/*[clinic end generated code: output=8a2486cc6e0014d1 input=301b56c90d9a79de]*/ { double diff; @@ -1184,11 +1274,11 @@ cmath_exec(PyObject *mod) if (PyModule_Add(mod, "tau", PyFloat_FromDouble(Py_MATH_TAU)) < 0) { return -1; } - if (PyModule_Add(mod, "inf", PyFloat_FromDouble(Py_INFINITY)) < 0) { + if (PyModule_Add(mod, "inf", PyFloat_FromDouble(INFINITY)) < 0) { return -1; } - Py_complex infj = {0.0, Py_INFINITY}; + Py_complex infj = {0.0, INFINITY}; if (PyModule_Add(mod, "infj", PyComplex_FromCComplex(infj)) < 0) { return -1; } @@ -1200,124 +1290,11 @@ cmath_exec(PyObject *mod) return -1; } - /* initialize special value tables */ - -#define INIT_SPECIAL_VALUES(NAME, BODY) { Py_complex* p = (Py_complex*)NAME; BODY } -#define C(REAL, IMAG) p->real = REAL; p->imag = IMAG; ++p; - - INIT_SPECIAL_VALUES(acos_special_values, { - C(P34,INF) C(P,INF) C(P,INF) C(P,-INF) C(P,-INF) C(P34,-INF) C(N,INF) - C(P12,INF) C(U,U) C(U,U) C(U,U) C(U,U) C(P12,-INF) C(N,N) - C(P12,INF) C(U,U) C(P12,0.) C(P12,-0.) C(U,U) C(P12,-INF) C(P12,N) - C(P12,INF) C(U,U) C(P12,0.) C(P12,-0.) C(U,U) C(P12,-INF) C(P12,N) - C(P12,INF) C(U,U) C(U,U) C(U,U) C(U,U) C(P12,-INF) C(N,N) - C(P14,INF) C(0.,INF) C(0.,INF) C(0.,-INF) C(0.,-INF) C(P14,-INF) C(N,INF) - C(N,INF) C(N,N) C(N,N) C(N,N) C(N,N) C(N,-INF) C(N,N) - }) - - INIT_SPECIAL_VALUES(acosh_special_values, { - C(INF,-P34) C(INF,-P) C(INF,-P) C(INF,P) C(INF,P) C(INF,P34) C(INF,N) - C(INF,-P12) C(U,U) C(U,U) C(U,U) C(U,U) C(INF,P12) C(N,N) - C(INF,-P12) C(U,U) C(0.,-P12) C(0.,P12) C(U,U) C(INF,P12) C(N,P12) - C(INF,-P12) C(U,U) C(0.,-P12) C(0.,P12) C(U,U) C(INF,P12) C(N,P12) - C(INF,-P12) C(U,U) C(U,U) C(U,U) C(U,U) C(INF,P12) C(N,N) - C(INF,-P14) C(INF,-0.) C(INF,-0.) C(INF,0.) C(INF,0.) C(INF,P14) C(INF,N) - C(INF,N) C(N,N) C(N,N) C(N,N) C(N,N) C(INF,N) C(N,N) - }) - - INIT_SPECIAL_VALUES(asinh_special_values, { - C(-INF,-P14) C(-INF,-0.) C(-INF,-0.) C(-INF,0.) C(-INF,0.) C(-INF,P14) C(-INF,N) - C(-INF,-P12) C(U,U) C(U,U) C(U,U) C(U,U) C(-INF,P12) C(N,N) - C(-INF,-P12) C(U,U) C(-0.,-0.) C(-0.,0.) C(U,U) C(-INF,P12) C(N,N) - C(INF,-P12) C(U,U) C(0.,-0.) C(0.,0.) C(U,U) C(INF,P12) C(N,N) - C(INF,-P12) C(U,U) C(U,U) C(U,U) C(U,U) C(INF,P12) C(N,N) - C(INF,-P14) C(INF,-0.) C(INF,-0.) C(INF,0.) C(INF,0.) C(INF,P14) C(INF,N) - C(INF,N) C(N,N) C(N,-0.) C(N,0.) C(N,N) C(INF,N) C(N,N) - }) - - INIT_SPECIAL_VALUES(atanh_special_values, { - C(-0.,-P12) C(-0.,-P12) C(-0.,-P12) C(-0.,P12) C(-0.,P12) C(-0.,P12) C(-0.,N) - C(-0.,-P12) C(U,U) C(U,U) C(U,U) C(U,U) C(-0.,P12) C(N,N) - C(-0.,-P12) C(U,U) C(-0.,-0.) C(-0.,0.) C(U,U) C(-0.,P12) C(-0.,N) - C(0.,-P12) C(U,U) C(0.,-0.) C(0.,0.) C(U,U) C(0.,P12) C(0.,N) - C(0.,-P12) C(U,U) C(U,U) C(U,U) C(U,U) C(0.,P12) C(N,N) - C(0.,-P12) C(0.,-P12) C(0.,-P12) C(0.,P12) C(0.,P12) C(0.,P12) C(0.,N) - C(0.,-P12) C(N,N) C(N,N) C(N,N) C(N,N) C(0.,P12) C(N,N) - }) - - INIT_SPECIAL_VALUES(cosh_special_values, { - C(INF,N) C(U,U) C(INF,0.) C(INF,-0.) C(U,U) C(INF,N) C(INF,N) - C(N,N) C(U,U) C(U,U) C(U,U) C(U,U) C(N,N) C(N,N) - C(N,0.) C(U,U) C(1.,0.) C(1.,-0.) C(U,U) C(N,0.) C(N,0.) - C(N,0.) C(U,U) C(1.,-0.) C(1.,0.) C(U,U) C(N,0.) C(N,0.) - C(N,N) C(U,U) C(U,U) C(U,U) C(U,U) C(N,N) C(N,N) - C(INF,N) C(U,U) C(INF,-0.) C(INF,0.) C(U,U) C(INF,N) C(INF,N) - C(N,N) C(N,N) C(N,0.) C(N,0.) C(N,N) C(N,N) C(N,N) - }) - - INIT_SPECIAL_VALUES(exp_special_values, { - C(0.,0.) C(U,U) C(0.,-0.) C(0.,0.) C(U,U) C(0.,0.) C(0.,0.) - C(N,N) C(U,U) C(U,U) C(U,U) C(U,U) C(N,N) C(N,N) - C(N,N) C(U,U) C(1.,-0.) C(1.,0.) C(U,U) C(N,N) C(N,N) - C(N,N) C(U,U) C(1.,-0.) C(1.,0.) C(U,U) C(N,N) C(N,N) - C(N,N) C(U,U) C(U,U) C(U,U) C(U,U) C(N,N) C(N,N) - C(INF,N) C(U,U) C(INF,-0.) C(INF,0.) C(U,U) C(INF,N) C(INF,N) - C(N,N) C(N,N) C(N,-0.) C(N,0.) C(N,N) C(N,N) C(N,N) - }) - - INIT_SPECIAL_VALUES(log_special_values, { - C(INF,-P34) C(INF,-P) C(INF,-P) C(INF,P) C(INF,P) C(INF,P34) C(INF,N) - C(INF,-P12) C(U,U) C(U,U) C(U,U) C(U,U) C(INF,P12) C(N,N) - C(INF,-P12) C(U,U) C(-INF,-P) C(-INF,P) C(U,U) C(INF,P12) C(N,N) - C(INF,-P12) C(U,U) C(-INF,-0.) C(-INF,0.) C(U,U) C(INF,P12) C(N,N) - C(INF,-P12) C(U,U) C(U,U) C(U,U) C(U,U) C(INF,P12) C(N,N) - C(INF,-P14) C(INF,-0.) C(INF,-0.) C(INF,0.) C(INF,0.) C(INF,P14) C(INF,N) - C(INF,N) C(N,N) C(N,N) C(N,N) C(N,N) C(INF,N) C(N,N) - }) - - INIT_SPECIAL_VALUES(sinh_special_values, { - C(INF,N) C(U,U) C(-INF,-0.) C(-INF,0.) C(U,U) C(INF,N) C(INF,N) - C(N,N) C(U,U) C(U,U) C(U,U) C(U,U) C(N,N) C(N,N) - C(0.,N) C(U,U) C(-0.,-0.) C(-0.,0.) C(U,U) C(0.,N) C(0.,N) - C(0.,N) C(U,U) C(0.,-0.) C(0.,0.) C(U,U) C(0.,N) C(0.,N) - C(N,N) C(U,U) C(U,U) C(U,U) C(U,U) C(N,N) C(N,N) - C(INF,N) C(U,U) C(INF,-0.) C(INF,0.) C(U,U) C(INF,N) C(INF,N) - C(N,N) C(N,N) C(N,-0.) C(N,0.) C(N,N) C(N,N) C(N,N) - }) - - INIT_SPECIAL_VALUES(sqrt_special_values, { - C(INF,-INF) C(0.,-INF) C(0.,-INF) C(0.,INF) C(0.,INF) C(INF,INF) C(N,INF) - C(INF,-INF) C(U,U) C(U,U) C(U,U) C(U,U) C(INF,INF) C(N,N) - C(INF,-INF) C(U,U) C(0.,-0.) C(0.,0.) C(U,U) C(INF,INF) C(N,N) - C(INF,-INF) C(U,U) C(0.,-0.) C(0.,0.) C(U,U) C(INF,INF) C(N,N) - C(INF,-INF) C(U,U) C(U,U) C(U,U) C(U,U) C(INF,INF) C(N,N) - C(INF,-INF) C(INF,-0.) C(INF,-0.) C(INF,0.) C(INF,0.) C(INF,INF) C(INF,N) - C(INF,-INF) C(N,N) C(N,N) C(N,N) C(N,N) C(INF,INF) C(N,N) - }) - - INIT_SPECIAL_VALUES(tanh_special_values, { - C(-1.,0.) C(U,U) C(-1.,-0.) C(-1.,0.) C(U,U) C(-1.,0.) C(-1.,0.) - C(N,N) C(U,U) C(U,U) C(U,U) C(U,U) C(N,N) C(N,N) - C(-0.0,N) C(U,U) C(-0.,-0.) C(-0.,0.) C(U,U) C(-0.0,N) C(-0.,N) - C(0.0,N) C(U,U) C(0.,-0.) C(0.,0.) C(U,U) C(0.0,N) C(0.,N) - C(N,N) C(U,U) C(U,U) C(U,U) C(U,U) C(N,N) C(N,N) - C(1.,0.) C(U,U) C(1.,-0.) C(1.,0.) C(U,U) C(1.,0.) C(1.,0.) - C(N,N) C(N,N) C(N,-0.) C(N,0.) C(N,N) C(N,N) C(N,N) - }) - - INIT_SPECIAL_VALUES(rect_special_values, { - C(INF,N) C(U,U) C(-INF,0.) C(-INF,-0.) C(U,U) C(INF,N) C(INF,N) - C(N,N) C(U,U) C(U,U) C(U,U) C(U,U) C(N,N) C(N,N) - C(0.,0.) C(U,U) C(-0.,0.) C(-0.,-0.) C(U,U) C(0.,0.) C(0.,0.) - C(0.,0.) C(U,U) C(0.,-0.) C(0.,0.) C(U,U) C(0.,0.) C(0.,0.) - C(N,N) C(U,U) C(U,U) C(U,U) C(U,U) C(N,N) C(N,N) - C(INF,N) C(U,U) C(INF,-0.) C(INF,0.) C(U,U) C(INF,N) C(INF,N) - C(N,N) C(N,N) C(N,0.) C(N,0.) C(N,N) C(N,N) C(N,N) - }) return 0; } static PyModuleDef_Slot cmath_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, cmath_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/expat/COPYING b/Modules/expat/COPYING index ce9e5939291e45..c6d184a8aae845 100644 --- a/Modules/expat/COPYING +++ b/Modules/expat/COPYING @@ -1,5 +1,5 @@ Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper -Copyright (c) 2001-2022 Expat maintainers +Copyright (c) 2001-2025 Expat maintainers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/Modules/expat/expat.h b/Modules/expat/expat.h index 610e1ddc0e94ba..ec3f58544cb00d 100644 --- a/Modules/expat/expat.h +++ b/Modules/expat/expat.h @@ -11,7 +11,7 @@ Copyright (c) 2000-2005 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net> Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net> - Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2016 Cristian Rodríguez <crrodriguez@opensuse.org> Copyright (c) 2016 Thomas Beutlich <tc@tbeu.de> Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk> @@ -19,6 +19,7 @@ Copyright (c) 2023 Hanno Böck <hanno@gentoo.org> Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com> Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp> + Copyright (c) 2025 Matthew Fernandez <matthew.fernandez@gmail.com> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -42,21 +43,22 @@ */ #ifndef Expat_INCLUDED -#define Expat_INCLUDED 1 +# define Expat_INCLUDED 1 -#include <stdlib.h> -#include "expat_external.h" +# include <stdint.h> // for uint8_t +# include <stdlib.h> +# include "expat_external.h" -#ifdef __cplusplus +# ifdef __cplusplus extern "C" { -#endif +# endif struct XML_ParserStruct; typedef struct XML_ParserStruct *XML_Parser; typedef unsigned char XML_Bool; -#define XML_TRUE ((XML_Bool)1) -#define XML_FALSE ((XML_Bool)0) +# define XML_TRUE ((XML_Bool)1) +# define XML_FALSE ((XML_Bool)0) /* The XML_Status enum gives the possible return values for several API functions. The preprocessor #defines are included so this @@ -73,11 +75,11 @@ typedef unsigned char XML_Bool; */ enum XML_Status { XML_STATUS_ERROR = 0, -#define XML_STATUS_ERROR XML_STATUS_ERROR +# define XML_STATUS_ERROR XML_STATUS_ERROR XML_STATUS_OK = 1, -#define XML_STATUS_OK XML_STATUS_OK +# define XML_STATUS_OK XML_STATUS_OK XML_STATUS_SUSPENDED = 2 -#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED +# define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED }; enum XML_Error { @@ -276,7 +278,7 @@ XML_ParserCreate_MM(const XML_Char *encoding, /* Prepare a parser object to be reused. This is particularly valuable when memory allocation overhead is disproportionately high, - such as when a large number of small documnents need to be parsed. + such as when a large number of small documents need to be parsed. All handlers are cleared from the parser, except for the unknownEncodingHandler. The parser's external state is re-initialized except for the values of ns and ns_triplets. @@ -680,7 +682,7 @@ XMLPARSEAPI(void) XML_SetUserData(XML_Parser parser, void *userData); /* Returns the last value set by XML_SetUserData or NULL. */ -#define XML_GetUserData(parser) (*(void **)(parser)) +# define XML_GetUserData(parser) (*(void **)(parser)) /* This is equivalent to supplying an encoding argument to XML_ParserCreate. On success XML_SetEncoding returns non-zero, @@ -752,7 +754,7 @@ XML_GetSpecifiedAttributeCount(XML_Parser parser); XMLPARSEAPI(int) XML_GetIdAttributeIndex(XML_Parser parser); -#ifdef XML_ATTR_INFO +# ifdef XML_ATTR_INFO /* Source file byte offsets for the start and end of attribute names and values. The value indices are exclusive of surrounding quotes; thus in a UTF-8 source file an attribute value of "blah" will yield: @@ -773,7 +775,7 @@ typedef struct { */ XMLPARSEAPI(const XML_AttrInfo *) XML_GetAttributeInfo(XML_Parser parser); -#endif +# endif /* Parses some input. Returns XML_STATUS_ERROR if a fatal error is detected. The last call to XML_Parse must have isFinal true; len @@ -916,10 +918,21 @@ XML_SetParamEntityParsing(XML_Parser parser, function behavior. This must be called before parsing is started. Returns 1 if successful, 0 when called after parsing has started. Note: If parser == NULL, the function will do nothing and return 0. + DEPRECATED since Expat 2.8.0. */ XMLPARSEAPI(int) XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt); +/* Sets the hash salt to use for internal hash calculations. + Helps in preventing DoS attacks based on predicting hash function behavior. + This must be called before parsing is started. + Returns XML_TRUE if successful, XML_FALSE when called after parsing has + started or when parser is NULL. + Added in Expat 2.8.0. +*/ +XMLPARSEAPI(XML_Bool) +XML_SetHashSalt16Bytes(XML_Parser parser, const uint8_t entropy[16]); + /* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then XML_GetErrorCode returns information about the error. */ @@ -970,9 +983,9 @@ XMLPARSEAPI(const char *) XML_GetInputContext(XML_Parser parser, int *offset, int *size); /* For backwards compatibility with previous versions. */ -#define XML_GetErrorLineNumber XML_GetCurrentLineNumber -#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber -#define XML_GetErrorByteIndex XML_GetCurrentByteIndex +# define XML_GetErrorLineNumber XML_GetCurrentLineNumber +# define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber +# define XML_GetErrorByteIndex XML_GetCurrentByteIndex /* Frees the content model passed to the element declaration handler */ XMLPARSEAPI(void) @@ -1032,7 +1045,10 @@ enum XML_FeatureEnum { XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, /* Added in Expat 2.6.0. */ - XML_FEATURE_GE + XML_FEATURE_GE, + /* Added in Expat 2.7.2. */ + XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, /* Additional features must be added to the end of this enum. */ }; @@ -1045,7 +1061,7 @@ typedef struct { XMLPARSEAPI(const XML_Feature *) XML_GetFeatureList(void); -#if defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1) +# if defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1) /* Added in Expat 2.4.0 for XML_DTD defined and * added in Expat 2.6.0 for XML_GE == 1. */ XMLPARSEAPI(XML_Bool) @@ -1057,7 +1073,17 @@ XML_SetBillionLaughsAttackProtectionMaximumAmplification( XMLPARSEAPI(XML_Bool) XML_SetBillionLaughsAttackProtectionActivationThreshold( XML_Parser parser, unsigned long long activationThresholdBytes); -#endif + +/* Added in Expat 2.7.2. */ +XMLPARSEAPI(XML_Bool) +XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, + float maximumAmplificationFactor); + +/* Added in Expat 2.7.2. */ +XMLPARSEAPI(XML_Bool) +XML_SetAllocTrackerActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes); +# endif /* Added in Expat 2.6.0. */ XMLPARSEAPI(XML_Bool) @@ -1066,12 +1092,12 @@ XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled); /* Expat follows the semantic versioning convention. See https://semver.org */ -#define XML_MAJOR_VERSION 2 -#define XML_MINOR_VERSION 7 -#define XML_MICRO_VERSION 1 +# define XML_MAJOR_VERSION 2 +# define XML_MINOR_VERSION 8 +# define XML_MICRO_VERSION 1 -#ifdef __cplusplus +# ifdef __cplusplus } -#endif +# endif #endif /* not Expat_INCLUDED */ diff --git a/Modules/expat/expat_config.h b/Modules/expat/expat_config.h index e7d9499d9078d9..70df73c8e00a5f 100644 --- a/Modules/expat/expat_config.h +++ b/Modules/expat/expat_config.h @@ -3,7 +3,7 @@ * distribution. */ #ifndef EXPAT_CONFIG_H -#define EXPAT_CONFIG_H +#define EXPAT_CONFIG_H 1 #include <pyconfig.h> #ifdef WORDS_BIGENDIAN @@ -22,5 +22,10 @@ // bpo-30947: Python uses best available entropy sources to // call XML_SetHashSalt(), expat entropy sources are not needed #define XML_POOR_ENTROPY 1 +#undef HAVE_ARC4RANDOM +#undef HAVE_ARC4RANDOM_BUF +#undef HAVE_GETENTROPY +#undef HAVE_GETRANDOM +#undef HAVE_SYSCALL_GETRANDOM #endif /* EXPAT_CONFIG_H */ diff --git a/Modules/expat/expat_external.h b/Modules/expat/expat_external.h index 567872b09836e1..cc945c424e471f 100644 --- a/Modules/expat/expat_external.h +++ b/Modules/expat/expat_external.h @@ -12,9 +12,10 @@ Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net> Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net> Copyright (c) 2016 Cristian Rodríguez <crrodriguez@opensuse.org> - Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk> Copyright (c) 2018 Yury Gribov <tetra2005@gmail.com> + Copyright (c) 2026 Matthew Fernandez <matthew.fernandez@gmail.com> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -38,8 +39,7 @@ */ #ifndef Expat_External_INCLUDED -#define Expat_External_INCLUDED 1 - +# define Expat_External_INCLUDED 1 /* Namespace external symbols to allow multiple libexpat version to co-exist. */ #include "pyexpatns.h" @@ -49,7 +49,7 @@ /* Expat tries very hard to make the API boundary very specifically defined. There are two macros defined to control this boundary; each of these can be defined before including this header to - achieve some different behavior, but doing so it not recommended or + achieve some different behavior, but doing so is not recommended or tested frequently. XMLCALL - The calling convention to use for all calls across the @@ -68,12 +68,12 @@ compiled with the cdecl calling convention as the default since system headers may assume the cdecl convention. */ -#ifndef XMLCALL -# if defined(_MSC_VER) -# define XMLCALL __cdecl -# elif defined(__GNUC__) && defined(__i386) && ! defined(__INTEL_COMPILER) -# define XMLCALL __attribute__((cdecl)) -# else +# ifndef XMLCALL +# if defined(_MSC_VER) +# define XMLCALL __cdecl +# elif defined(__GNUC__) && defined(__i386) && ! defined(__INTEL_COMPILER) +# define XMLCALL __attribute__((cdecl)) +# else /* For any platform which uses this definition and supports more than one calling convention, we need to extend this definition to declare the convention used on that platform, if it's possible to @@ -84,86 +84,86 @@ pre-processor and how to specify the same calling convention as the platform's malloc() implementation. */ -# define XMLCALL -# endif -#endif /* not defined XMLCALL */ +# define XMLCALL +# endif +# endif /* not defined XMLCALL */ -#if ! defined(XML_STATIC) && ! defined(XMLIMPORT) -# ifndef XML_BUILDING_EXPAT +# if ! defined(XML_STATIC) && ! defined(XMLIMPORT) +# ifndef XML_BUILDING_EXPAT /* using Expat from an application */ -# if defined(_MSC_EXTENSIONS) && ! defined(__BEOS__) && ! defined(__CYGWIN__) -# define XMLIMPORT __declspec(dllimport) +# if defined(_MSC_VER) && ! defined(__BEOS__) && ! defined(__CYGWIN__) +# define XMLIMPORT __declspec(dllimport) +# endif + # endif +# endif /* not defined XML_STATIC */ +# ifndef XML_ENABLE_VISIBILITY +# define XML_ENABLE_VISIBILITY 0 # endif -#endif /* not defined XML_STATIC */ - -#ifndef XML_ENABLE_VISIBILITY -# define XML_ENABLE_VISIBILITY 0 -#endif -#if ! defined(XMLIMPORT) && XML_ENABLE_VISIBILITY -# define XMLIMPORT __attribute__((visibility("default"))) -#endif +# if ! defined(XMLIMPORT) && XML_ENABLE_VISIBILITY +# define XMLIMPORT __attribute__((visibility("default"))) +# endif /* If we didn't define it above, define it away: */ -#ifndef XMLIMPORT -# define XMLIMPORT -#endif - -#if defined(__GNUC__) \ - && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)) -# define XML_ATTR_MALLOC __attribute__((__malloc__)) -#else -# define XML_ATTR_MALLOC -#endif - -#if defined(__GNUC__) \ - && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) -# define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x))) -#else -# define XML_ATTR_ALLOC_SIZE(x) -#endif - -#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL - -#ifdef __cplusplus -extern "C" { -#endif +# ifndef XMLIMPORT +# define XMLIMPORT +# endif -#ifdef XML_UNICODE_WCHAR_T -# ifndef XML_UNICODE -# define XML_UNICODE +# if defined(__GNUC__) \ + && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)) +# define XML_ATTR_MALLOC __attribute__((__malloc__)) +# else +# define XML_ATTR_MALLOC # endif -# if defined(__SIZEOF_WCHAR_T__) && (__SIZEOF_WCHAR_T__ != 2) -# error "sizeof(wchar_t) != 2; Need -fshort-wchar for both Expat and libc" + +# if defined(__GNUC__) \ + && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +# define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x))) +# else +# define XML_ATTR_ALLOC_SIZE(x) +# endif + +# define XMLPARSEAPI(type) XMLIMPORT type XMLCALL + +# ifdef __cplusplus +extern "C" { # endif -#endif -#ifdef XML_UNICODE /* Information is UTF-16 encoded. */ # ifdef XML_UNICODE_WCHAR_T +# ifndef XML_UNICODE +# define XML_UNICODE +# endif +# if defined(__SIZEOF_WCHAR_T__) && (__SIZEOF_WCHAR_T__ != 2) +# error "sizeof(wchar_t) != 2; Need -fshort-wchar for both Expat and libc" +# endif +# endif + +# ifdef XML_UNICODE /* Information is UTF-16 encoded. */ +# ifdef XML_UNICODE_WCHAR_T typedef wchar_t XML_Char; typedef wchar_t XML_LChar; -# else +# else typedef unsigned short XML_Char; typedef char XML_LChar; -# endif /* XML_UNICODE_WCHAR_T */ -#else /* Information is UTF-8 encoded. */ +# endif /* XML_UNICODE_WCHAR_T */ +# else /* Information is UTF-8 encoded. */ typedef char XML_Char; typedef char XML_LChar; -#endif /* XML_UNICODE */ +# endif /* XML_UNICODE */ -#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ +# ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ typedef long long XML_Index; typedef unsigned long long XML_Size; -#else +# else typedef long XML_Index; typedef unsigned long XML_Size; -#endif /* XML_LARGE_SIZE */ +# endif /* XML_LARGE_SIZE */ -#ifdef __cplusplus +# ifdef __cplusplus } -#endif +# endif #endif /* not Expat_External_INCLUDED */ diff --git a/Modules/expat/internal.h b/Modules/expat/internal.h index 6bde6ae6b31ddd..420d4217a569b1 100644 --- a/Modules/expat/internal.h +++ b/Modules/expat/internal.h @@ -28,7 +28,7 @@ Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net> Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net> - Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2018 Yury Gribov <tetra2005@gmail.com> Copyright (c) 2019 David Loffredo <loffredo@steptools.com> Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com> @@ -108,10 +108,12 @@ #endif #include <limits.h> // ULONG_MAX +#include <stddef.h> // size_t #if defined(_WIN32) \ && (! defined(__USE_MINGW_ANSI_STDIO) \ || (1 - __USE_MINGW_ANSI_STDIO - 1 == 0)) +# define EXPAT_FMT_LLX(midpart) "%" midpart "I64x" # define EXPAT_FMT_ULL(midpart) "%" midpart "I64u" # if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW # define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d" @@ -121,13 +123,14 @@ # define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u" # endif #else +# define EXPAT_FMT_LLX(midpart) "%" midpart "llx" # define EXPAT_FMT_ULL(midpart) "%" midpart "llu" # if ! defined(ULONG_MAX) # error Compiler did not define ULONG_MAX for us # elif ULONG_MAX == 18446744073709551615u // 2^64-1 # define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld" # define EXPAT_FMT_SIZE_T(midpart) "%" midpart "lu" -# elif defined(EMSCRIPTEN) // 32bit mode Emscripten +# elif defined(__wasm32__) // 32bit mode Emscripten or WASI SDK # define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld" # define EXPAT_FMT_SIZE_T(midpart) "%" midpart "zu" # else @@ -148,6 +151,16 @@ 100.0f #define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \ 8388608 // 8 MiB, 2^23 + +#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f +#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \ + 67108864 // 64 MiB, 2^26 + +// NOTE: If function expat_alloc was user facing, EXPAT_MALLOC_ALIGNMENT would +// have to take sizeof(long double) into account +#define EXPAT_MALLOC_ALIGNMENT sizeof(long long) // largest parser (sub)member +#define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t)) + /* NOTE END */ #include "expat.h" // so we can use type XML_Parser below @@ -171,6 +184,9 @@ extern #endif XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c #if defined(XML_TESTING) +void *expat_malloc(XML_Parser parser, size_t size, int sourceLine); +void expat_free(XML_Parser parser, void *ptr, int sourceLine); +void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine); extern unsigned int g_bytesScanned; // used for testing only #endif diff --git a/Modules/expat/pyexpatns.h b/Modules/expat/pyexpatns.h index 8ee03ef0792815..fc6b482d587e0d 100644 --- a/Modules/expat/pyexpatns.h +++ b/Modules/expat/pyexpatns.h @@ -82,6 +82,8 @@ #define XmlPrologStateInit PyExpat_XmlPrologStateInit #define XmlPrologStateInitExternalEntity PyExpat_XmlPrologStateInitExternalEntity #define XML_ResumeParser PyExpat_XML_ResumeParser +#define XML_SetAllocTrackerActivationThreshold PyExpat_XML_SetAllocTrackerActivationThreshold +#define XML_SetAllocTrackerMaximumAmplification PyExpat_XML_SetAllocTrackerMaximumAmplification #define XML_SetAttlistDeclHandler PyExpat_XML_SetAttlistDeclHandler #define XML_SetBase PyExpat_XML_SetBase #define XML_SetBillionLaughsAttackProtectionActivationThreshold PyExpat_XML_SetBillionLaughsAttackProtectionActivationThreshold diff --git a/Modules/expat/refresh.sh b/Modules/expat/refresh.sh index 3904fc8afd63d2..fa3692f9379510 100755 --- a/Modules/expat/refresh.sh +++ b/Modules/expat/refresh.sh @@ -12,9 +12,9 @@ fi # Update this when updating to a new version after verifying that the changes # the update brings in are good. These values are used for verifying the SBOM, too. -expected_libexpat_tag="R_2_7_1" -expected_libexpat_version="2.7.1" -expected_libexpat_sha256="0cce2e6e69b327fc607b8ff264f4b66bdf71ead55a87ffd5f3143f535f15cfa2" +expected_libexpat_tag="R_2_8_1" +expected_libexpat_version="2.8.1" +expected_libexpat_sha256="a52eb72108be160e190b5cafa5bba8663f1313f2013e26060d1c18e26e31067b" expat_dir="$(realpath "$(dirname -- "${BASH_SOURCE[0]}")")" cd ${expat_dir} @@ -24,6 +24,9 @@ curl --location "https://github.com/libexpat/libexpat/releases/download/${expect echo "${expected_libexpat_sha256} libexpat.tar.gz" | sha256sum --check # Step 2: Pull files from the libexpat distribution + +tar xzvf libexpat.tar.gz "expat-${expected_libexpat_version}/COPYING" --strip-components 2 + declare -a lib_files lib_files=( ascii.h @@ -52,15 +55,37 @@ done rm libexpat.tar.gz # Step 3: Add the namespacing include to expat_external.h -sed -i 's/#define Expat_External_INCLUDED 1/&\n\n\/* Namespace external symbols to allow multiple libexpat version to\n co-exist. \*\/\n#include "pyexpatns.h"/' expat_external.h +sed -i 's/# define Expat_External_INCLUDED 1/&\n\/* Namespace external symbols to allow multiple libexpat version to\n co-exist. \*\/\n#include "pyexpatns.h"/' expat_external.h + +if ! grep -q '#include "pyexpatns\.h"' expat_external.h; then + echo " +Error: namespacing include not found in expat_external.h; +This may be due to source changes and will require updating this script" >&2 + exit 1 +fi + +# Step 4: Skip the Windows rand_s entropy path in xmlparse.c when +# XML_POOR_ENTROPY is set. +sed -z -i 's|#if defined(_WIN32)\n# include "random_rand_s\.h"\n#endif /\* defined(_WIN32) \*/|#if defined(_WIN32) \&\& ! defined(XML_POOR_ENTROPY)\n# include "random_rand_s.h"\n#endif /* defined(_WIN32) \&\& ! defined(XML_POOR_ENTROPY) */|' xmlparse.c +sed -z -i 's|# ifdef _WIN32\n if (writeRandomBytes_rand_s|# if defined(_WIN32) \&\& ! defined(XML_POOR_ENTROPY)\n if (writeRandomBytes_rand_s|' xmlparse.c + +if ! grep -q '#if defined(_WIN32) && ! defined(XML_POOR_ENTROPY)' xmlparse.c; then + echo " +Error: rand_s gate not patched in xmlparse.c; +This may be due to source changes and will require updating this script" >&2 + exit 1 +fi -echo " +echo ' Updated! next steps: - Verify all is okay: git diff git status -- Regenerate the sbom file +- Update the sbom file: + Under the package "SPDXRef-PACKAGE-expat", update the "checksumValue", + "downloadLocation", "referenceLocator", and "versionInfo" fields. +- Regenerate the sbom file: make regen-sbom -- Update warning count in Tools/build/.warningignore_macos +- Update the warning count in Tools/build/.warningignore_macos: (use info from CI if not on a Mac) -" +' diff --git a/Modules/expat/xmlparse.c b/Modules/expat/xmlparse.c index 38a2d9657b6aeb..95d346758563ab 100644 --- a/Modules/expat/xmlparse.c +++ b/Modules/expat/xmlparse.c @@ -1,4 +1,4 @@ -/* d19ae032c224863c1527ba44d228cc34b99192c3a4c5a27af1f4e054d45ee031 (2.7.1+) +/* 75ef4224f81c052e9e5aeea2ac7de75357d2169ff9908e39edc08b9dc3052513 (2.8.1+) __ __ _ ___\ \/ /_ __ __ _| |_ / _ \\ /| '_ \ / _` | __| @@ -13,7 +13,7 @@ Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net> Copyright (c) 2005-2009 Steven Solie <steven@solie.ca> Copyright (c) 2016 Eric Rahm <erahm@mozilla.com> - Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2016-2026 Sebastian Pipping <sebastian@pipping.org> Copyright (c) 2016 Gaurav <g.gupta@samsung.com> Copyright (c) 2016 Thomas Beutlich <tc@tbeu.de> Copyright (c) 2016 Gustavo Grieco <gustavo.grieco@imag.fr> @@ -41,6 +41,12 @@ Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com> Copyright (c) 2024-2025 Berkay Eren Ürün <berkay.ueruen@siemens.com> Copyright (c) 2024 Hanno Böck <hanno@gentoo.org> + Copyright (c) 2025-2026 Matthew Fernandez <matthew.fernandez@gmail.com> + Copyright (c) 2025 Atrem Borovik <polzovatellllk@gmail.com> + Copyright (c) 2025 Alfonso Gregory <gfunni234@gmail.com> + Copyright (c) 2026 Rosen Penev <rosenp@gmail.com> + Copyright (c) 2026 Francesco Bertolaccini + Copyright (c) 2026 Christian Ng <christianrng@berkeley.edu> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -80,28 +86,16 @@ # error XML_CONTEXT_BYTES must be defined, non-empty and >=0 (0 to disable, >=1 to enable; 1024 is a common default) #endif -#if defined(HAVE_SYSCALL_GETRANDOM) -# if ! defined(_GNU_SOURCE) -# define _GNU_SOURCE 1 /* syscall prototype */ -# endif -#endif - -#ifdef _WIN32 -/* force stdlib to define rand_s() */ -# if ! defined(_CRT_RAND_S) -# define _CRT_RAND_S -# endif -#endif - #include <stdbool.h> #include <stddef.h> #include <string.h> /* memset(), memcpy() */ #include <assert.h> -#include <limits.h> /* UINT_MAX */ +#include <limits.h> /* INT_MAX, UINT_MAX */ #include <stdio.h> /* fprintf */ -#include <stdlib.h> /* getenv, rand_s */ -#include <stdint.h> /* uintptr_t */ +#include <stdlib.h> /* getenv */ +#include <stdint.h> /* SIZE_MAX, uintptr_t */ #include <math.h> /* isnan */ +#include <errno.h> #ifdef _WIN32 # define getpid GetCurrentProcessId @@ -121,31 +115,34 @@ #include "expat.h" #include "siphash.h" +#if defined(HAVE_ARC4RANDOM) +# include "random_arc4random.h" +#endif /* defined(HAVE_ARC4RANDOM) */ + +#if defined(HAVE_ARC4RANDOM_BUF) +# include "random_arc4random_buf.h" +#endif // defined(HAVE_ARC4RANDOM_BUF) + +#if defined(XML_DEV_URANDOM) +# include "random_dev_urandom.h" +#endif /* defined(XML_DEV_URANDOM) */ + +#if defined(HAVE_GETENTROPY) +# include "random_getentropy.h" +#endif // defined(HAVE_GETENTROPY) + #if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) -# if defined(HAVE_GETRANDOM) -# include <sys/random.h> /* getrandom */ -# else -# include <unistd.h> /* syscall */ -# include <sys/syscall.h> /* SYS_getrandom */ -# endif -# if ! defined(GRND_NONBLOCK) -# define GRND_NONBLOCK 0x0001 -# endif /* defined(GRND_NONBLOCK) */ -#endif /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */ - -#if defined(HAVE_LIBBSD) \ - && (defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_ARC4RANDOM)) -# include <bsd/stdlib.h> -#endif +# include "random_getrandom.h" +#endif /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */ -#if defined(_WIN32) && ! defined(LOAD_LIBRARY_SEARCH_SYSTEM32) -# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 -#endif +#if defined(_WIN32) && ! defined(XML_POOR_ENTROPY) +# include "random_rand_s.h" +#endif /* defined(_WIN32) && ! defined(XML_POOR_ENTROPY) */ #if ! defined(HAVE_GETRANDOM) && ! defined(HAVE_SYSCALL_GETRANDOM) \ && ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) \ - && ! defined(XML_DEV_URANDOM) && ! defined(_WIN32) \ - && ! defined(XML_POOR_ENTROPY) + && ! defined(HAVE_GETENTROPY) && ! defined(XML_DEV_URANDOM) \ + && ! defined(_WIN32) && ! defined(XML_POOR_ENTROPY) # error You do not have support for any sources of high quality entropy \ enabled. For end user security, that is probably not what you want. \ \ @@ -154,12 +151,11 @@ * Linux >=3.17 + glibc (including <2.25) (syscall SYS_getrandom): HAVE_SYSCALL_GETRANDOM, \ * BSD / macOS >=10.7 / glibc >=2.36 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \ * BSD / macOS (including <10.7) / glibc >=2.36 (arc4random): HAVE_ARC4RANDOM, \ - * libbsd (arc4random_buf): HAVE_ARC4RANDOM_BUF + HAVE_LIBBSD, \ - * libbsd (arc4random): HAVE_ARC4RANDOM + HAVE_LIBBSD, \ + * BSD / macOS >=10.12 / glibc >=2.25 (getentropy): HAVE_GETENTROPY, \ * Linux (including <3.17) / BSD / macOS (including <10.7) / Solaris >=8 (/dev/urandom): XML_DEV_URANDOM, \ * Windows >=Vista (rand_s): _WIN32. \ \ - If insist on not using any of these, bypass this error by defining \ + If you insist on not using any of these, bypass this error by defining \ XML_POOR_ENTROPY; you have been warned. \ \ If you have reasons to patch this detection code away or need changes \ @@ -234,7 +230,7 @@ typedef struct { unsigned char power; size_t size; size_t used; - const XML_Memory_Handling_Suite *mem; + XML_Parser parser; } HASH_TABLE; static size_t keylen(KEY s); @@ -310,8 +306,11 @@ typedef struct tag { const char *rawName; /* tagName in the original encoding */ int rawNameLength; TAG_NAME name; /* tagName in the API encoding */ - char *buf; /* buffer for name components */ - char *bufEnd; /* end of the buffer */ + union { + char *raw; /* for byte-level access (rawName storage) */ + XML_Char *str; /* for character-level access (converted name) */ + } buf; /* buffer for name components */ + char *bufEnd; /* end of the buffer */ BINDING *bindings; } TAG; @@ -348,7 +347,7 @@ typedef struct { typedef struct block { struct block *next; int size; - XML_Char s[1]; + XML_Char s[]; } BLOCK; typedef struct { @@ -357,7 +356,7 @@ typedef struct { const XML_Char *end; XML_Char *ptr; XML_Char *start; - const XML_Memory_Handling_Suite *mem; + XML_Parser parser; } STRING_POOL; /* The XML_Char before the name is used to determine whether @@ -388,6 +387,7 @@ typedef struct { int nDefaultAtts; int allocDefaultAtts; DEFAULT_ATTRIBUTE *defaultAtts; + HASH_TABLE defaultAttsNames; } ELEMENT_TYPE; typedef struct { @@ -452,6 +452,14 @@ typedef struct accounting { unsigned long long activationThresholdBytes; } ACCOUNTING; +typedef struct MALLOC_TRACKER { + XmlBigCount bytesAllocated; + XmlBigCount peakBytesAllocated; // updated live only for debug level >=2 + unsigned long debugLevel; + float maximumAmplificationFactor; // >=1.0 + XmlBigCount activationThresholdBytes; +} MALLOC_TRACKER; + typedef struct entity_stats { unsigned int countEverOpened; unsigned int currentDepth; @@ -555,27 +563,24 @@ static XML_Bool setContext(XML_Parser parser, const XML_Char *context); static void FASTCALL normalizePublicId(XML_Char *s); -static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms); +static DTD *dtdCreate(XML_Parser parser); /* do not call if m_parentParser != NULL */ -static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); -static void dtdDestroy(DTD *p, XML_Bool isDocEntity, - const XML_Memory_Handling_Suite *ms); +static void dtdReset(DTD *p, XML_Parser parser); +static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser); static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, - const XML_Memory_Handling_Suite *ms); + XML_Parser parser); static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, STRING_POOL *newPool, const HASH_TABLE *oldTable); static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize); -static void FASTCALL hashTableInit(HASH_TABLE *table, - const XML_Memory_Handling_Suite *ms); +static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser parser); static void FASTCALL hashTableClear(HASH_TABLE *table); static void FASTCALL hashTableDestroy(HASH_TABLE *table); static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table); static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter); -static void FASTCALL poolInit(STRING_POOL *pool, - const XML_Memory_Handling_Suite *ms); +static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser); static void FASTCALL poolClear(STRING_POOL *pool); static void FASTCALL poolDestroy(STRING_POOL *pool); static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, @@ -585,6 +590,8 @@ static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); static const XML_Char *FASTCALL poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char *FASTCALL poolCopyStringNoFinish(STRING_POOL *pool, + const XML_Char *s); static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); static const XML_Char *FASTCALL poolAppendString(STRING_POOL *pool, @@ -595,15 +602,15 @@ static XML_Content *build_model(XML_Parser parser); static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr, const char *end); -static XML_Char *copyString(const XML_Char *s, - const XML_Memory_Handling_Suite *memsuite); +static XML_Char *copyString(const XML_Char *s, XML_Parser parser); -static unsigned long generate_hash_secret_salt(XML_Parser parser); +static struct sipkey generate_hash_secret_salt(void); static XML_Bool startParsing(XML_Parser parser); static XML_Parser parserCreate(const XML_Char *encodingName, const XML_Memory_Handling_Suite *memsuite, - const XML_Char *nameSep, DTD *dtd); + const XML_Char *nameSep, DTD *dtd, + XML_Parser parentParser); static void parserInit(XML_Parser parser, const XML_Char *encodingName); @@ -627,10 +634,10 @@ static void entityTrackingOnOpen(XML_Parser parser, ENTITY *entity, int sourceLine); static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity, int sourceLine); +#endif /* XML_GE == 1 */ static XML_Parser getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff); -#endif /* XML_GE == 1 */ static unsigned long getDebugLevel(const char *variableName, unsigned long defaultDebugLevel); @@ -770,166 +777,265 @@ struct XML_ParserStruct { XML_Bool m_useForeignDTD; enum XML_ParamEntityParsing m_paramEntityParsing; #endif - unsigned long m_hash_secret_salt; + struct sipkey m_hash_secret_salt_128; + XML_Bool m_hash_secret_salt_set; #if XML_GE == 1 ACCOUNTING m_accounting; + MALLOC_TRACKER m_alloc_tracker; ENTITY_STATS m_entity_stats; #endif XML_Bool m_reenter; }; -#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) -#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) -#define FREE(parser, p) (parser->m_mem.free_fcn((p))) +#if XML_GE == 1 +# define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__)) +# define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__)) +# define FREE(parser, p) (expat_free((parser), (p), __LINE__)) +#else +# define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) +# define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) +# define FREE(parser, p) (parser->m_mem.free_fcn((p))) +#endif -XML_Parser XMLCALL -XML_ParserCreate(const XML_Char *encodingName) { - return XML_ParserCreate_MM(encodingName, NULL, NULL); +#if XML_GE == 1 +static void +expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff, + XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) { + // NOTE: This can be +infinity or -nan + const float amplification + = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; + fprintf( + stderr, + "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL( + "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n", + (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator, + absDiff, newTotal, peakTotal, (double)amplification, sourceLine); } -XML_Parser XMLCALL -XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) { - XML_Char tmp[2] = {nsSep, 0}; - return XML_ParserCreate_MM(encodingName, NULL, tmp); -} +static bool +expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase, + int sourceLine) { + assert(rootParser != NULL); + assert(increase > 0); -// "xml=http://www.w3.org/XML/1998/namespace" -static const XML_Char implicitContext[] - = {ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, - ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, - ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, - ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, - ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, - ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, - ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, - ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, - '\0'}; + XmlBigCount newTotal = 0; + bool tolerable = true; -/* To avoid warnings about unused functions: */ -#if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) + // Detect integer overflow + if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) { + tolerable = false; + } else { + newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase; -# if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) + if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) { + assert(newTotal > 0); + // NOTE: This can be +infinity when dividing by zero but not -nan + const float amplification + = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; + if (amplification + > rootParser->m_alloc_tracker.maximumAmplificationFactor) { + tolerable = false; + } + } + } -/* Obtain entropy on Linux 3.17+ */ -static int -writeRandomBytes_getrandom_nonblock(void *target, size_t count) { - int success = 0; /* full count bytes written? */ - size_t bytesWrittenTotal = 0; - const unsigned int getrandomFlags = GRND_NONBLOCK; + if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) { + expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine); + } - do { - void *const currentTarget = (void *)((char *)target + bytesWrittenTotal); - const size_t bytesToWrite = count - bytesWrittenTotal; + return tolerable; +} - const int bytesWrittenMore = -# if defined(HAVE_GETRANDOM) - getrandom(currentTarget, bytesToWrite, getrandomFlags); -# else - syscall(SYS_getrandom, currentTarget, bytesToWrite, getrandomFlags); -# endif +# if defined(XML_TESTING) +void * +# else +static void * +# endif +expat_malloc(XML_Parser parser, size_t size, int sourceLine) { + // Detect integer overflow + if (SIZE_MAX - size < sizeof(size_t) + EXPAT_MALLOC_PADDING) { + return NULL; + } - if (bytesWrittenMore > 0) { - bytesWrittenTotal += bytesWrittenMore; - if (bytesWrittenTotal >= count) - success = 1; - } - } while (! success && (errno == EINTR)); + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(rootParser->m_parentParser == NULL); - return success; -} + const size_t bytesToAllocate = sizeof(size_t) + EXPAT_MALLOC_PADDING + size; -# endif /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */ + if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated + < bytesToAllocate) { + return NULL; // i.e. signal integer overflow as out-of-memory + } -# if ! defined(_WIN32) && defined(XML_DEV_URANDOM) + if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate, + sourceLine)) { + return NULL; // i.e. signal violation as out-of-memory + } -/* Extract entropy from /dev/urandom */ -static int -writeRandomBytes_dev_urandom(void *target, size_t count) { - int success = 0; /* full count bytes written? */ - size_t bytesWrittenTotal = 0; + // Actually allocate + void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate); - const int fd = open("/dev/urandom", O_RDONLY); - if (fd < 0) { - return 0; + if (mallocedPtr == NULL) { + return NULL; } - do { - void *const currentTarget = (void *)((char *)target + bytesWrittenTotal); - const size_t bytesToWrite = count - bytesWrittenTotal; + // Update in-block recorded size + *(size_t *)mallocedPtr = size; - const ssize_t bytesWrittenMore = read(fd, currentTarget, bytesToWrite); + // Update accounting + rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate; - if (bytesWrittenMore > 0) { - bytesWrittenTotal += bytesWrittenMore; - if (bytesWrittenTotal >= count) - success = 1; + // Report as needed + if (rootParser->m_alloc_tracker.debugLevel >= 2) { + if (rootParser->m_alloc_tracker.bytesAllocated + > rootParser->m_alloc_tracker.peakBytesAllocated) { + rootParser->m_alloc_tracker.peakBytesAllocated + = rootParser->m_alloc_tracker.bytesAllocated; } - } while (! success && (errno == EINTR)); + expat_heap_stat(rootParser, '+', bytesToAllocate, + rootParser->m_alloc_tracker.bytesAllocated, + rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); + } - close(fd); - return success; + return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING; } -# endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */ +# if defined(XML_TESTING) +void +# else +static void +# endif +expat_free(XML_Parser parser, void *ptr, int sourceLine) { + assert(parser != NULL); -#endif /* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */ + if (ptr == NULL) { + return; + } -#if defined(HAVE_ARC4RANDOM) && ! defined(HAVE_ARC4RANDOM_BUF) + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(rootParser->m_parentParser == NULL); -static void -writeRandomBytes_arc4random(void *target, size_t count) { - size_t bytesWrittenTotal = 0; + // Extract size (to the eyes of malloc_fcn/realloc_fcn) and + // the original pointer returned by malloc/realloc + void *const mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t); + const size_t bytesAllocated + = sizeof(size_t) + EXPAT_MALLOC_PADDING + *(size_t *)mallocedPtr; - while (bytesWrittenTotal < count) { - const uint32_t random32 = arc4random(); - size_t i = 0; + // Update accounting + assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated); + rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated; - for (; (i < sizeof(random32)) && (bytesWrittenTotal < count); - i++, bytesWrittenTotal++) { - const uint8_t random8 = (uint8_t)(random32 >> (i * 8)); - ((uint8_t *)target)[bytesWrittenTotal] = random8; - } + // Report as needed + if (rootParser->m_alloc_tracker.debugLevel >= 2) { + expat_heap_stat(rootParser, '-', bytesAllocated, + rootParser->m_alloc_tracker.bytesAllocated, + rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); } + + // NOTE: This may be freeing rootParser, so freeing has to come last + parser->m_mem.free_fcn(mallocedPtr); } -#endif /* defined(HAVE_ARC4RANDOM) && ! defined(HAVE_ARC4RANDOM_BUF) */ +# if defined(XML_TESTING) +void * +# else +static void * +# endif +expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) { + assert(parser != NULL); -#ifdef _WIN32 + if (ptr == NULL) { + return expat_malloc(parser, size, sourceLine); + } -/* Provide declaration of rand_s() for MinGW-32 (not 64, which has it), - as it didn't declare it in its header prior to version 5.3.0 of its - runtime package (mingwrt, containing stdlib.h). The upstream fix - was introduced at https://osdn.net/projects/mingw/ticket/39658 . */ -# if defined(__MINGW32__) && defined(__MINGW32_VERSION) \ - && __MINGW32_VERSION < 5003000L && ! defined(__MINGW64_VERSION_MAJOR) -__declspec(dllimport) int rand_s(unsigned int *); -# endif + if (size == 0) { + expat_free(parser, ptr, sourceLine); + return NULL; + } -/* Obtain entropy on Windows using the rand_s() function which - * generates cryptographically secure random numbers. Internally it - * uses RtlGenRandom API which is present in Windows XP and later. - */ -static int -writeRandomBytes_rand_s(void *target, size_t count) { - size_t bytesWrittenTotal = 0; + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(rootParser->m_parentParser == NULL); - while (bytesWrittenTotal < count) { - unsigned int random32 = 0; - size_t i = 0; + // Extract original size (to the eyes of the caller) and the original + // pointer returned by malloc/realloc + void *mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t); + const size_t prevSize = *(size_t *)mallocedPtr; - if (rand_s(&random32)) - return 0; /* failure */ + // Classify upcoming change + const bool isIncrease = (size > prevSize); + const size_t absDiff + = (size > prevSize) ? (size - prevSize) : (prevSize - size); - for (; (i < sizeof(random32)) && (bytesWrittenTotal < count); - i++, bytesWrittenTotal++) { - const uint8_t random8 = (uint8_t)(random32 >> (i * 8)); - ((uint8_t *)target)[bytesWrittenTotal] = random8; + // Ask for permission from accounting + if (isIncrease) { + if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) { + return NULL; // i.e. signal violation as out-of-memory } } - return 1; /* success */ + + // NOTE: Integer overflow detection has already been done for us + // by expat_heap_increase_tolerable(..) above + assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size); + + // Actually allocate + mallocedPtr = parser->m_mem.realloc_fcn( + mallocedPtr, sizeof(size_t) + EXPAT_MALLOC_PADDING + size); + + if (mallocedPtr == NULL) { + return NULL; + } + + // Update accounting + if (isIncrease) { + assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated + >= absDiff); + rootParser->m_alloc_tracker.bytesAllocated += absDiff; + } else { // i.e. decrease + assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff); + rootParser->m_alloc_tracker.bytesAllocated -= absDiff; + } + + // Report as needed + if (rootParser->m_alloc_tracker.debugLevel >= 2) { + if (rootParser->m_alloc_tracker.bytesAllocated + > rootParser->m_alloc_tracker.peakBytesAllocated) { + rootParser->m_alloc_tracker.peakBytesAllocated + = rootParser->m_alloc_tracker.bytesAllocated; + } + expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff, + rootParser->m_alloc_tracker.bytesAllocated, + rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine); + } + + // Update in-block recorded size + *(size_t *)mallocedPtr = size; + + return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING; } +#endif // XML_GE == 1 -#endif /* _WIN32 */ +XML_Parser XMLCALL +XML_ParserCreate(const XML_Char *encodingName) { + return XML_ParserCreate_MM(encodingName, NULL, NULL); +} + +XML_Parser XMLCALL +XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) { + XML_Char tmp[2] = {nsSep, 0}; + return XML_ParserCreate_MM(encodingName, NULL, tmp); +} + +// "xml=http://www.w3.org/XML/1998/namespace" +static const XML_Char implicitContext[] + = {ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, + ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, + ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, + ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, + ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, + ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, + ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, + ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, + '\0'}; #if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) @@ -958,65 +1064,70 @@ gather_time_entropy(void) { #endif /* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */ -static unsigned long -ENTROPY_DEBUG(const char *label, unsigned long entropy) { +static struct sipkey +ENTROPY_DEBUG(const char *label, struct sipkey entropy_128) { if (getDebugLevel("EXPAT_ENTROPY_DEBUG", 0) >= 1u) { - fprintf(stderr, "expat: Entropy: %s --> 0x%0*lx (%lu bytes)\n", label, - (int)sizeof(entropy) * 2, entropy, (unsigned long)sizeof(entropy)); + fprintf(stderr, + "expat: Entropy: %s --> [0x" EXPAT_FMT_LLX( + "016") ", 0x" EXPAT_FMT_LLX("016") "] (16 bytes)\n", + label, (unsigned long long)entropy_128.k[0], + (unsigned long long)entropy_128.k[1]); } - return entropy; + return entropy_128; } -static unsigned long -generate_hash_secret_salt(XML_Parser parser) { - unsigned long entropy; - (void)parser; +static struct sipkey +generate_hash_secret_salt(void) { + struct sipkey entropy; /* "Failproof" high quality providers: */ #if defined(HAVE_ARC4RANDOM_BUF) - arc4random_buf(&entropy, sizeof(entropy)); + writeRandomBytes_arc4random_buf(&entropy, sizeof(entropy)); return ENTROPY_DEBUG("arc4random_buf", entropy); #elif defined(HAVE_ARC4RANDOM) - writeRandomBytes_arc4random((void *)&entropy, sizeof(entropy)); + writeRandomBytes_arc4random(&entropy, sizeof(entropy)); return ENTROPY_DEBUG("arc4random", entropy); #else /* Try high quality providers first .. */ -# ifdef _WIN32 - if (writeRandomBytes_rand_s((void *)&entropy, sizeof(entropy))) { +# if defined(_WIN32) && ! defined(XML_POOR_ENTROPY) + if (writeRandomBytes_rand_s(&entropy, sizeof(entropy))) { return ENTROPY_DEBUG("rand_s", entropy); } +# elif defined(HAVE_GETENTROPY) + if (writeRandomBytes_getentropy(&entropy, sizeof(entropy))) { + return ENTROPY_DEBUG("getentropy", entropy); + } + errno = 0; # elif defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) - if (writeRandomBytes_getrandom_nonblock((void *)&entropy, sizeof(entropy))) { + if (writeRandomBytes_getrandom_nonblock(&entropy, sizeof(entropy))) { return ENTROPY_DEBUG("getrandom", entropy); } # endif # if ! defined(_WIN32) && defined(XML_DEV_URANDOM) - if (writeRandomBytes_dev_urandom((void *)&entropy, sizeof(entropy))) { + if (writeRandomBytes_dev_urandom(&entropy, sizeof(entropy))) { return ENTROPY_DEBUG("/dev/urandom", entropy); } # endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */ /* .. and self-made low quality for backup: */ + entropy.k[0] = 0; + entropy.k[1] = gather_time_entropy(); +# if ! defined(__wasi__) /* Process ID is 0 bits entropy if attacker has local access */ - entropy = gather_time_entropy() ^ getpid(); + entropy.k[1] ^= getpid(); +# endif /* Factors are 2^31-1 and 2^61-1 (Mersenne primes M31 and M61) */ if (sizeof(unsigned long) == 4) { - return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647); + entropy.k[1] *= 2147483647; + return ENTROPY_DEBUG("fallback(4)", entropy); } else { - return ENTROPY_DEBUG("fallback(8)", - entropy * (unsigned long)2305843009213693951ULL); + entropy.k[1] *= 2305843009213693951ULL; + return ENTROPY_DEBUG("fallback(8)", entropy); } #endif } -static unsigned long -get_hash_secret_salt(XML_Parser parser) { - if (parser->m_parentParser != NULL) - return get_hash_secret_salt(parser->m_parentParser); - return parser->m_hash_secret_salt; -} - static enum XML_Error callProcessor(XML_Parser parser, const char *start, const char *end, const char **endPtr) { @@ -1085,8 +1196,10 @@ callProcessor(XML_Parser parser, const char *start, const char *end, static XML_Bool /* only valid for root parser */ startParsing(XML_Parser parser) { /* hash functions must be initialized before setContext() is called */ - if (parser->m_hash_secret_salt == 0) - parser->m_hash_secret_salt = generate_hash_secret_salt(parser); + if (parser->m_hash_secret_salt_set != XML_TRUE) { + parser->m_hash_secret_salt_128 = generate_hash_secret_salt(); + parser->m_hash_secret_salt_set = XML_TRUE; + } if (parser->m_ns) { /* implicit context only set for root parser, since child parsers (i.e. external entity parsers) will inherit it @@ -1100,19 +1213,43 @@ XML_Parser XMLCALL XML_ParserCreate_MM(const XML_Char *encodingName, const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep) { - return parserCreate(encodingName, memsuite, nameSep, NULL); + return parserCreate(encodingName, memsuite, nameSep, NULL, NULL); } static XML_Parser parserCreate(const XML_Char *encodingName, const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep, - DTD *dtd) { - XML_Parser parser; + DTD *dtd, XML_Parser parentParser) { + XML_Parser parser = NULL; + +#if XML_GE == 1 + const size_t increase + = sizeof(size_t) + EXPAT_MALLOC_PADDING + sizeof(struct XML_ParserStruct); + + if (parentParser != NULL) { + const XML_Parser rootParser = getRootParserOf(parentParser, NULL); + if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) { + return NULL; + } + } +#else + UNUSED_P(parentParser); +#endif if (memsuite) { XML_Memory_Handling_Suite *mtemp; +#if XML_GE == 1 + void *const sizeAndParser + = memsuite->malloc_fcn(sizeof(size_t) + EXPAT_MALLOC_PADDING + + sizeof(struct XML_ParserStruct)); + if (sizeAndParser != NULL) { + *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); + parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t) + + EXPAT_MALLOC_PADDING); +#else parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); if (parser != NULL) { +#endif mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); mtemp->malloc_fcn = memsuite->malloc_fcn; mtemp->realloc_fcn = memsuite->realloc_fcn; @@ -1120,39 +1257,86 @@ parserCreate(const XML_Char *encodingName, } } else { XML_Memory_Handling_Suite *mtemp; - parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); +#if XML_GE == 1 + void *const sizeAndParser = malloc(sizeof(size_t) + EXPAT_MALLOC_PADDING + + sizeof(struct XML_ParserStruct)); + if (sizeAndParser != NULL) { + *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct); + parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t) + + EXPAT_MALLOC_PADDING); +#else + parser = malloc(sizeof(struct XML_ParserStruct)); if (parser != NULL) { +#endif mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); mtemp->malloc_fcn = malloc; mtemp->realloc_fcn = realloc; mtemp->free_fcn = free; } - } + } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck >=2.18.0 if (! parser) return parser; +#if XML_GE == 1 + // Initialize .m_alloc_tracker + memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER)); + if (parentParser == NULL) { + parser->m_alloc_tracker.debugLevel + = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u); + parser->m_alloc_tracker.maximumAmplificationFactor + = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT; + parser->m_alloc_tracker.activationThresholdBytes + = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT; + + // NOTE: This initialization needs to come this early because these fields + // are read by allocation tracking code + parser->m_parentParser = NULL; + parser->m_accounting.countBytesDirect = 0; + } else { + parser->m_parentParser = parentParser; + } + + // Record XML_ParserStruct allocation we did a few lines up before + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(rootParser->m_parentParser == NULL); + assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase); + rootParser->m_alloc_tracker.bytesAllocated += increase; + + // Report on allocation + if (rootParser->m_alloc_tracker.debugLevel >= 2) { + if (rootParser->m_alloc_tracker.bytesAllocated + > rootParser->m_alloc_tracker.peakBytesAllocated) { + rootParser->m_alloc_tracker.peakBytesAllocated + = rootParser->m_alloc_tracker.bytesAllocated; + } + + expat_heap_stat(rootParser, '+', increase, + rootParser->m_alloc_tracker.bytesAllocated, + rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__); + } +#else + parser->m_parentParser = NULL; +#endif // XML_GE == 1 + parser->m_buffer = NULL; parser->m_bufferLim = NULL; parser->m_attsSize = INIT_ATTS_SIZE; - parser->m_atts - = (ATTRIBUTE *)MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE)); + parser->m_atts = MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE)); if (parser->m_atts == NULL) { FREE(parser, parser); return NULL; } #ifdef XML_ATTR_INFO - parser->m_attInfo = (XML_AttrInfo *)MALLOC( - parser, parser->m_attsSize * sizeof(XML_AttrInfo)); + parser->m_attInfo = MALLOC(parser, parser->m_attsSize * sizeof(XML_AttrInfo)); if (parser->m_attInfo == NULL) { FREE(parser, parser->m_atts); FREE(parser, parser); return NULL; } #endif - parser->m_dataBuf - = (XML_Char *)MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + parser->m_dataBuf = MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char)); if (parser->m_dataBuf == NULL) { FREE(parser, parser->m_atts); #ifdef XML_ATTR_INFO @@ -1166,7 +1350,7 @@ parserCreate(const XML_Char *encodingName, if (dtd) parser->m_dtd = dtd; else { - parser->m_dtd = dtdCreate(&parser->m_mem); + parser->m_dtd = dtdCreate(parser); if (parser->m_dtd == NULL) { FREE(parser, parser->m_dataBuf); FREE(parser, parser->m_atts); @@ -1200,8 +1384,8 @@ parserCreate(const XML_Char *encodingName, parser->m_protocolEncodingName = NULL; - poolInit(&parser->m_tempPool, &(parser->m_mem)); - poolInit(&parser->m_temp2Pool, &(parser->m_mem)); + poolInit(&parser->m_tempPool, parser); + poolInit(&parser->m_temp2Pool, parser); parserInit(parser, encodingName); if (encodingName && ! parser->m_protocolEncodingName) { @@ -1233,7 +1417,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { parser->m_processor = prologInitProcessor; XmlPrologStateInit(&parser->m_prologState); if (encodingName != NULL) { - parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); + parser->m_protocolEncodingName = copyString(encodingName, parser); } parser->m_curBase = NULL; XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0); @@ -1295,7 +1479,6 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { parser->m_unknownEncodingMem = NULL; parser->m_unknownEncodingRelease = NULL; parser->m_unknownEncodingData = NULL; - parser->m_parentParser = NULL; parser->m_parsingStatus.parsing = XML_INITIALIZED; // Reentry can only be triggered inside m_processor calls parser->m_reenter = XML_FALSE; @@ -1304,7 +1487,9 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) { parser->m_useForeignDTD = XML_FALSE; parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; #endif - parser->m_hash_secret_salt = 0; + parser->m_hash_secret_salt_128.k[0] = 0; + parser->m_hash_secret_salt_128.k[1] = 0; + parser->m_hash_secret_salt_set = XML_FALSE; #if XML_GE == 1 memset(&parser->m_accounting, 0, sizeof(ACCOUNTING)); @@ -1385,7 +1570,7 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { FREE(parser, (void *)parser->m_protocolEncodingName); parser->m_protocolEncodingName = NULL; parserInit(parser, encodingName); - dtdReset(parser->m_dtd, &parser->m_mem); + dtdReset(parser->m_dtd, parser); return XML_TRUE; } @@ -1421,7 +1606,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { parser->m_protocolEncodingName = NULL; else { /* Copy the new encoding name into allocated memory */ - parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); + parser->m_protocolEncodingName = copyString(encodingName, parser); if (! parser->m_protocolEncodingName) return XML_STATUS_ERROR; } @@ -1450,6 +1635,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, XML_ExternalEntityRefHandler oldExternalEntityRefHandler; XML_SkippedEntityHandler oldSkippedEntityHandler; XML_UnknownEncodingHandler oldUnknownEncodingHandler; + void *oldUnknownEncodingHandlerData; XML_ElementDeclHandler oldElementDeclHandler; XML_AttlistDeclHandler oldAttlistDeclHandler; XML_EntityDeclHandler oldEntityDeclHandler; @@ -1470,7 +1656,8 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, from hash tables associated with either parser without us having to worry which hash secrets each table has. */ - unsigned long oldhash_secret_salt; + struct sipkey oldhash_secret_salt_128; + XML_Bool oldhash_secret_salt_set; XML_Bool oldReparseDeferralEnabled; /* Validate the oldParser parameter before we pull everything out of it */ @@ -1495,6 +1682,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, oldExternalEntityRefHandler = parser->m_externalEntityRefHandler; oldSkippedEntityHandler = parser->m_skippedEntityHandler; oldUnknownEncodingHandler = parser->m_unknownEncodingHandler; + oldUnknownEncodingHandlerData = parser->m_unknownEncodingHandlerData; oldElementDeclHandler = parser->m_elementDeclHandler; oldAttlistDeclHandler = parser->m_attlistDeclHandler; oldEntityDeclHandler = parser->m_entityDeclHandler; @@ -1515,7 +1703,8 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, from hash tables associated with either parser without us having to worry which hash secrets each table has. */ - oldhash_secret_salt = parser->m_hash_secret_salt; + oldhash_secret_salt_128 = parser->m_hash_secret_salt_128; + oldhash_secret_salt_set = parser->m_hash_secret_salt_set; oldReparseDeferralEnabled = parser->m_reparseDeferralEnabled; #ifdef XML_DTD @@ -1530,9 +1719,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, */ if (parser->m_ns) { XML_Char tmp[2] = {parser->m_namespaceSeparator, 0}; - parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); + parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser); } else { - parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); + parser + = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, oldParser); } if (! parser) @@ -1554,6 +1744,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, parser->m_externalEntityRefHandler = oldExternalEntityRefHandler; parser->m_skippedEntityHandler = oldSkippedEntityHandler; parser->m_unknownEncodingHandler = oldUnknownEncodingHandler; + parser->m_unknownEncodingHandlerData = oldUnknownEncodingHandlerData; parser->m_elementDeclHandler = oldElementDeclHandler; parser->m_attlistDeclHandler = oldAttlistDeclHandler; parser->m_entityDeclHandler = oldEntityDeclHandler; @@ -1568,7 +1759,8 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, parser->m_externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities; parser->m_ns_triplets = oldns_triplets; - parser->m_hash_secret_salt = oldhash_secret_salt; + parser->m_hash_secret_salt_128 = oldhash_secret_salt_128; + parser->m_hash_secret_salt_set = oldhash_secret_salt_set; parser->m_reparseDeferralEnabled = oldReparseDeferralEnabled; parser->m_parentParser = oldParser; #ifdef XML_DTD @@ -1576,7 +1768,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, parser->m_prologState.inEntityValue = oldInEntityValue; if (context) { #endif /* XML_DTD */ - if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem) + if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser) || ! setContext(parser, context)) { XML_ParserFree(parser); return NULL; @@ -1629,7 +1821,7 @@ XML_ParserFree(XML_Parser parser) { } p = tagList; tagList = tagList->parent; - FREE(parser, p->buf); + FREE(parser, p->buf.raw); destroyBindings(p->bindings, parser); FREE(parser, p); } @@ -1688,14 +1880,16 @@ XML_ParserFree(XML_Parser parser) { #else if (parser->m_dtd) #endif /* XML_DTD */ - dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, - &parser->m_mem); - FREE(parser, (void *)parser->m_atts); + dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser); + FREE(parser, parser->m_atts); #ifdef XML_ATTR_INFO - FREE(parser, (void *)parser->m_attInfo); + FREE(parser, parser->m_attInfo); #endif FREE(parser, parser->m_groupConnector); - FREE(parser, parser->m_buffer); + // NOTE: We are avoiding FREE(..) here because parser->m_buffer + // is not being allocated with MALLOC(..) but with plain + // .malloc_fcn(..). + parser->m_mem.free_fcn(parser->m_buffer); FREE(parser, parser->m_dataBuf); FREE(parser, parser->m_nsAtts); FREE(parser, parser->m_unknownEncodingMem); @@ -2010,19 +2204,58 @@ XML_SetParamEntityParsing(XML_Parser parser, #endif } +// DEPRECATED since Expat 2.8.0. int XMLCALL XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt) { if (parser == NULL) return 0; - if (parser->m_parentParser) - return XML_SetHashSalt(parser->m_parentParser, hash_salt); + + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(! rootParser->m_parentParser); + /* block after XML_Parse()/XML_ParseBuffer() has been called */ - if (parserBusy(parser)) + if (parserBusy(rootParser)) return 0; - parser->m_hash_secret_salt = hash_salt; + + rootParser->m_hash_secret_salt_128.k[0] = 0; + rootParser->m_hash_secret_salt_128.k[1] = hash_salt; + + if (hash_salt != 0) { // to remain backwards compatible + rootParser->m_hash_secret_salt_set = XML_TRUE; + + if (sizeof(unsigned long) == 4) + ENTROPY_DEBUG("explicit(4)", rootParser->m_hash_secret_salt_128); + else + ENTROPY_DEBUG("explicit(8)", rootParser->m_hash_secret_salt_128); + } + return 1; } +XML_Bool XMLCALL +XML_SetHashSalt16Bytes(XML_Parser parser, const uint8_t entropy[16]) { + if (parser == NULL) + return XML_FALSE; + + if (entropy == NULL) + return XML_FALSE; + + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(! rootParser->m_parentParser); + + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (parserBusy(rootParser)) + return XML_FALSE; + + sip_tokey(&(rootParser->m_hash_secret_salt_128), entropy); + + rootParser->m_hash_secret_salt_set = XML_TRUE; + + ENTROPY_DEBUG("explicit(16)", rootParser->m_hash_secret_salt_128); + + return XML_TRUE; +} + enum XML_Status XMLCALL XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { if ((parser == NULL) || (len < 0) || ((s == NULL) && (len != 0))) { @@ -2287,8 +2520,10 @@ XML_GetBuffer(XML_Parser parser, int len) { parser->m_errorCode = XML_ERROR_NO_MEMORY; return NULL; } - newBuf = (char *)MALLOC(parser, bufferSize); - if (newBuf == 0) { + // NOTE: We are avoiding MALLOC(..) here to leave limiting + // the input size to the application using Expat. + newBuf = parser->m_mem.malloc_fcn(bufferSize); + if (newBuf == NULL) { parser->m_errorCode = XML_ERROR_NO_MEMORY; return NULL; } @@ -2298,7 +2533,10 @@ XML_GetBuffer(XML_Parser parser, int len) { memcpy(newBuf, &parser->m_bufferPtr[-keep], EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) + keep); - FREE(parser, parser->m_buffer); + // NOTE: We are avoiding FREE(..) here because parser->m_buffer + // is not being allocated with MALLOC(..) but with plain + // .malloc_fcn(..). + parser->m_mem.free_fcn(parser->m_buffer); parser->m_buffer = newBuf; parser->m_bufferEnd = parser->m_buffer @@ -2314,7 +2552,10 @@ XML_GetBuffer(XML_Parser parser, int len) { if (parser->m_bufferPtr) { memcpy(newBuf, parser->m_bufferPtr, EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); - FREE(parser, parser->m_buffer); + // NOTE: We are avoiding FREE(..) here because parser->m_buffer + // is not being allocated with MALLOC(..) but with plain + // .malloc_fcn(..). + parser->m_mem.free_fcn(parser->m_buffer); parser->m_bufferEnd = newBuf + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); @@ -2492,28 +2733,43 @@ XML_GetCurrentColumnNumber(XML_Parser parser) { void XMLCALL XML_FreeContentModel(XML_Parser parser, XML_Content *model) { - if (parser != NULL) - FREE(parser, model); + if (parser == NULL) + return; + + // NOTE: We are avoiding FREE(..) here because the content model + // has been created using plain .malloc_fcn(..) rather than MALLOC(..). + parser->m_mem.free_fcn(model); } void *XMLCALL XML_MemMalloc(XML_Parser parser, size_t size) { if (parser == NULL) return NULL; - return MALLOC(parser, size); + + // NOTE: We are avoiding MALLOC(..) here to not include + // user allocations with allocation tracking and limiting. + return parser->m_mem.malloc_fcn(size); } void *XMLCALL XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { if (parser == NULL) return NULL; - return REALLOC(parser, ptr, size); + + // NOTE: We are avoiding REALLOC(..) here to not include + // user allocations with allocation tracking and limiting. + return parser->m_mem.realloc_fcn(ptr, size); } void XMLCALL XML_MemFree(XML_Parser parser, void *ptr) { - if (parser != NULL) - FREE(parser, ptr); + if (parser == NULL) + return; + + // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and + // XML_MemRealloc are not using MALLOC(..) and REALLOC(..) + // but plain .malloc_fcn(..) and .realloc_fcn(..), internally. + parser->m_mem.free_fcn(ptr); } void XMLCALL @@ -2713,6 +2969,13 @@ XML_GetFeatureList(void) { EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT}, /* Added in Expat 2.6.0. */ {XML_FEATURE_GE, XML_L("XML_GE"), 0}, + /* Added in Expat 2.7.2. */ + {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT, + XML_L("XML_AT_MAX_AMP"), + (long int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT}, + {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT, + XML_L("XML_AT_ACT_THRES"), + (long int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT}, #endif {XML_FEATURE_END, NULL, 0}}; @@ -2741,6 +3004,29 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold( parser->m_accounting.activationThresholdBytes = activationThresholdBytes; return XML_TRUE; } + +XML_Bool XMLCALL +XML_SetAllocTrackerMaximumAmplification(XML_Parser parser, + float maximumAmplificationFactor) { + if ((parser == NULL) || (parser->m_parentParser != NULL) + || isnan(maximumAmplificationFactor) + || (maximumAmplificationFactor < 1.0f)) { + return XML_FALSE; + } + parser->m_alloc_tracker.maximumAmplificationFactor + = maximumAmplificationFactor; + return XML_TRUE; +} + +XML_Bool XMLCALL +XML_SetAllocTrackerActivationThreshold( + XML_Parser parser, unsigned long long activationThresholdBytes) { + if ((parser == NULL) || (parser->m_parentParser != NULL)) { + return XML_FALSE; + } + parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes; + return XML_TRUE; +} #endif /* XML_GE == 1 */ XML_Bool XMLCALL @@ -2761,10 +3047,10 @@ static XML_Bool storeRawNames(XML_Parser parser) { TAG *tag = parser->m_tagStack; while (tag) { - int bufSize; - int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); + size_t bufSize; + size_t nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); size_t rawNameLen; - char *rawNameBuf = tag->buf + nameLen; + char *rawNameBuf = tag->buf.raw + nameLen; /* Stop if already stored. Since m_tagStack is a stack, we can stop at the first entry that has already been copied; everything below it in the stack is already been accounted for in a @@ -2779,23 +3065,23 @@ storeRawNames(XML_Parser parser) { /* Detect and prevent integer overflow. */ if (rawNameLen > (size_t)INT_MAX - nameLen) return XML_FALSE; - bufSize = nameLen + (int)rawNameLen; - if (bufSize > tag->bufEnd - tag->buf) { - char *temp = (char *)REALLOC(parser, tag->buf, bufSize); + bufSize = nameLen + rawNameLen; + if (bufSize > (size_t)(tag->bufEnd - tag->buf.raw)) { + char *temp = REALLOC(parser, tag->buf.raw, bufSize); if (temp == NULL) return XML_FALSE; - /* if tag->name.str points to tag->buf (only when namespace + /* if tag->name.str points to tag->buf.str (only when namespace processing is off) then we have to update it */ - if (tag->name.str == (XML_Char *)tag->buf) + if (tag->name.str == tag->buf.str) tag->name.str = (XML_Char *)temp; /* if tag->name.localPart is set (when namespace processing is on) then update it as well, since it will always point into tag->buf */ if (tag->name.localPart) tag->name.localPart - = (XML_Char *)temp + (tag->name.localPart - (XML_Char *)tag->buf); - tag->buf = temp; + = (XML_Char *)temp + (tag->name.localPart - tag->buf.str); + tag->buf.raw = temp; tag->bufEnd = temp + bufSize; rawNameBuf = temp + nameLen; } @@ -3107,15 +3393,15 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, tag = parser->m_freeTagList; parser->m_freeTagList = parser->m_freeTagList->parent; } else { - tag = (TAG *)MALLOC(parser, sizeof(TAG)); + tag = MALLOC(parser, sizeof(TAG)); if (! tag) return XML_ERROR_NO_MEMORY; - tag->buf = (char *)MALLOC(parser, INIT_TAG_BUF_SIZE); - if (! tag->buf) { + tag->buf.raw = MALLOC(parser, INIT_TAG_BUF_SIZE); + if (! tag->buf.raw) { FREE(parser, tag); return XML_ERROR_NO_MEMORY; } - tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + tag->bufEnd = tag->buf.raw + INIT_TAG_BUF_SIZE; } tag->bindings = NULL; tag->parent = parser->m_tagStack; @@ -3128,31 +3414,32 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, { const char *rawNameEnd = tag->rawName + tag->rawNameLength; const char *fromPtr = tag->rawName; - toPtr = (XML_Char *)tag->buf; + toPtr = tag->buf.str; for (;;) { - int bufSize; int convLen; const enum XML_Convert_Result convert_res = XmlConvert(enc, &fromPtr, rawNameEnd, (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); - convLen = (int)(toPtr - (XML_Char *)tag->buf); + convLen = (int)(toPtr - tag->buf.str); if ((fromPtr >= rawNameEnd) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) { tag->name.strLen = convLen; break; } - bufSize = (int)(tag->bufEnd - tag->buf) << 1; + if (SIZE_MAX / 2 < (size_t)(tag->bufEnd - tag->buf.raw)) + return XML_ERROR_NO_MEMORY; + const size_t bufSize = (size_t)(tag->bufEnd - tag->buf.raw) * 2; { - char *temp = (char *)REALLOC(parser, tag->buf, bufSize); + char *temp = REALLOC(parser, tag->buf.raw, bufSize); if (temp == NULL) return XML_ERROR_NO_MEMORY; - tag->buf = temp; + tag->buf.raw = temp; tag->bufEnd = temp + bufSize; toPtr = (XML_Char *)temp + convLen; } } } - tag->name.str = (XML_Char *)tag->buf; + tag->name.str = tag->buf.str; *toPtr = XML_T('\0'); result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account); @@ -3483,6 +3770,8 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, sizeof(ELEMENT_TYPE)); if (! elementType) return XML_ERROR_NO_MEMORY; + if (! elementType->defaultAttsNames.parser) + hashTableInit(&(elementType->defaultAttsNames), parser); if (parser->m_ns && ! setElementTypePrefix(parser, elementType)) return XML_ERROR_NO_MEMORY; } @@ -3516,14 +3805,14 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(ATTRIBUTE)) { + if ((unsigned)parser->m_attsSize > SIZE_MAX / sizeof(ATTRIBUTE)) { parser->m_attsSize = oldAttsSize; return XML_ERROR_NO_MEMORY; } #endif - temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts, - parser->m_attsSize * sizeof(ATTRIBUTE)); + temp = REALLOC(parser, parser->m_atts, + parser->m_attsSize * sizeof(ATTRIBUTE)); if (temp == NULL) { parser->m_attsSize = oldAttsSize; return XML_ERROR_NO_MEMORY; @@ -3535,14 +3824,14 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ # if UINT_MAX >= SIZE_MAX - if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(XML_AttrInfo)) { + if ((unsigned)parser->m_attsSize > SIZE_MAX / sizeof(XML_AttrInfo)) { parser->m_attsSize = oldAttsSize; return XML_ERROR_NO_MEMORY; } # endif - temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo, - parser->m_attsSize * sizeof(XML_AttrInfo)); + temp2 = REALLOC(parser, parser->m_attInfo, + parser->m_attsSize * sizeof(XML_AttrInfo)); if (temp2 == NULL) { parser->m_attsSize = oldAttsSize; return XML_ERROR_NO_MEMORY; @@ -3677,7 +3966,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, and clear flags that say whether attributes were specified */ i = 0; if (nPrefixes) { - int j; /* hash table index */ + unsigned int j; /* hash table index */ unsigned long version = parser->m_nsAttsVersion; /* Detect and prevent invalid shift */ @@ -3711,15 +4000,14 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if (nsAttsSize > (size_t)(-1) / sizeof(NS_ATT)) { + if (nsAttsSize > SIZE_MAX / sizeof(NS_ATT)) { /* Restore actual size of memory in m_nsAtts */ parser->m_nsAttsPower = oldNsAttsPower; return XML_ERROR_NO_MEMORY; } #endif - temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts, - nsAttsSize * sizeof(NS_ATT)); + temp = REALLOC(parser, parser->m_nsAtts, nsAttsSize * sizeof(NS_ATT)); if (! temp) { /* Restore actual size of memory in m_nsAtts */ parser->m_nsAttsPower = oldNsAttsPower; @@ -3772,7 +4060,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, if (! b) return XML_ERROR_UNBOUND_PREFIX; - for (j = 0; j < b->uriLen; j++) { + for (j = 0; j < (unsigned int)b->uriLen; j++) { const XML_Char c = b->uri[j]; if (! poolAppendChar(&parser->m_tempPool, c)) return XML_ERROR_NO_MEMORY; @@ -3866,7 +4154,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, return XML_ERROR_NONE; prefixLen = 0; if (parser->m_ns_triplets && binding->prefix->name) { - for (; binding->prefix->name[prefixLen++];) + while (binding->prefix->name[prefixLen++]) ; /* prefixLen includes null terminator */ } tagNamePtr->localPart = localPart; @@ -3895,12 +4183,12 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if ((unsigned)(n + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) { + if ((unsigned)(n + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) { return XML_ERROR_NO_MEMORY; } #endif - uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char)); + uri = MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char)); if (! uri) return XML_ERROR_NO_MEMORY; binding->uriAlloc = n + EXPAND_SPARE; @@ -4141,13 +4429,13 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) { + if ((unsigned)(len + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) { return XML_ERROR_NO_MEMORY; } #endif - XML_Char *temp = (XML_Char *)REALLOC( - parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE)); + XML_Char *temp + = REALLOC(parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE)); if (temp == NULL) return XML_ERROR_NO_MEMORY; b->uri = temp; @@ -4155,7 +4443,7 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, } parser->m_freeBindingList = b->nextTagBinding; } else { - b = (BINDING *)MALLOC(parser, sizeof(BINDING)); + b = MALLOC(parser, sizeof(BINDING)); if (! b) return XML_ERROR_NO_MEMORY; @@ -4168,13 +4456,12 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) { + if ((unsigned)(len + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) { return XML_ERROR_NO_MEMORY; } #endif - b->uri - = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE)); + b->uri = MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE)); if (! b->uri) { FREE(parser, b); return XML_ERROR_NO_MEMORY; @@ -4720,7 +5007,7 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, } /* If we get this token, we have the start of what might be a normal tag, but not a declaration (i.e. it doesn't begin with - "<!"). In a DTD context, that isn't legal. + "<!" or "<?"). In a DTD context, that isn't legal. */ else if (tok == XML_TOK_INSTANCE_START) { *nextPtr = next; @@ -4809,6 +5096,15 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end, /* found end of entity value - can store it now */ return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT, NULL); } + /* If we get this token, we have the start of what might be a + normal tag, but not a declaration (i.e. it doesn't begin with + "<!" or "<?"). In a DTD context, that isn't legal. + */ + else if (tok == XML_TOK_INSTANCE_START) { + *nextPtr = next; + return XML_ERROR_SYNTAX; + } + start = next; } } @@ -5545,7 +5841,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, return XML_ERROR_NO_MEMORY; } - char *const new_connector = (char *)REALLOC( + char *const new_connector = REALLOC( parser, parser->m_groupConnector, parser->m_groupSize *= 2); if (new_connector == NULL) { parser->m_groupSize /= 2; @@ -5560,20 +5856,22 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if (parser->m_groupSize > (size_t)(-1) / sizeof(int)) { + if (parser->m_groupSize > SIZE_MAX / sizeof(int)) { + parser->m_groupSize /= 2; return XML_ERROR_NO_MEMORY; } #endif - int *const new_scaff_index = (int *)REALLOC( + int *const new_scaff_index = REALLOC( parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int)); - if (new_scaff_index == NULL) + if (new_scaff_index == NULL) { + parser->m_groupSize /= 2; return XML_ERROR_NO_MEMORY; + } dtd->scaffIndex = new_scaff_index; } } else { - parser->m_groupConnector - = (char *)MALLOC(parser, parser->m_groupSize = 32); + parser->m_groupConnector = MALLOC(parser, parser->m_groupSize = 32); if (! parser->m_groupConnector) { parser->m_groupSize = 0; return XML_ERROR_NO_MEMORY; @@ -5730,8 +6028,11 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, case XML_ROLE_CONTENT_EMPTY: if (dtd->in_eldecl) { if (parser->m_elementDeclHandler) { - XML_Content *content - = (XML_Content *)MALLOC(parser, sizeof(XML_Content)); + // NOTE: We are avoiding MALLOC(..) here to so that + // applications that are not using XML_FreeContentModel but + // plain free(..) or .free_fcn() to free the content model's + // memory are safe. + XML_Content *content = parser->m_mem.malloc_fcn(sizeof(XML_Content)); if (! content) return XML_ERROR_NO_MEMORY; content->quant = XML_CQUANT_NONE; @@ -5787,7 +6088,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, name = el->name; dtd->scaffold[myindex].name = name; nameLen = 0; - for (; name[nameLen++];) + while (name[nameLen++]) ; /* Detect and prevent integer overflow */ @@ -6008,8 +6309,7 @@ processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl, openEntity = *freeEntityList; *freeEntityList = openEntity->next; } else { - openEntity - = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY)); + openEntity = MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY)); if (! openEntity) return XML_ERROR_NO_MEMORY; } @@ -6087,6 +6387,10 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end, // process its possible inner entities (which are added to the // m_openInternalEntities during doProlog or doContent calls above) entity->hasMore = XML_FALSE; + if (! entity->is_param + && (openEntity->startTagLevel != parser->m_tagLevel)) { + return XML_ERROR_ASYNC_ENTITY; + } triggerReenter(parser); return result; } // End of entity processing, "if" block will return here @@ -6277,7 +6581,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, case XML_TOK_ENTITY_REF: { const XML_Char *name; ENTITY *entity; - char checkEntityDecl; + bool checkEntityDecl; XML_Char ch = (XML_Char)XmlPredefinedEntityName( enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar); if (ch) { @@ -6415,7 +6719,14 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc, return XML_ERROR_NO_MEMORY; } - const char *next; + const char *next = entityTextPtr; + + /* Nothing to tokenize. */ + if (entityTextPtr >= entityTextEnd) { + result = XML_ERROR_NONE; + goto endEntityValue; + } + for (;;) { next = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ @@ -6794,18 +7105,18 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, if (value || isId) { /* The handling of default attributes gets messed up if we have a default which duplicates a non-default. */ - int i; - for (i = 0; i < type->nDefaultAtts; i++) - if (attId == type->defaultAtts[i].id) - return 1; + NAMED *const nameFound + = (NAMED *)lookup(parser, &(type->defaultAttsNames), attId->name, 0); + if (nameFound) + return 1; if (isId && ! type->idAtt && ! attId->xmlns) type->idAtt = attId; } if (type->nDefaultAtts == type->allocDefaultAtts) { if (type->allocDefaultAtts == 0) { type->allocDefaultAtts = 8; - type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC( - parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + type->defaultAtts + = MALLOC(parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); if (! type->defaultAtts) { type->allocDefaultAtts = 0; return 0; @@ -6825,13 +7136,13 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if ((unsigned)count > (size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE)) { + if ((unsigned)count > SIZE_MAX / sizeof(DEFAULT_ATTRIBUTE)) { return 0; } #endif - temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts, - (count * sizeof(DEFAULT_ATTRIBUTE))); + temp = REALLOC(parser, type->defaultAtts, + (count * sizeof(DEFAULT_ATTRIBUTE))); if (temp == NULL) return 0; type->allocDefaultAtts = count; @@ -6844,6 +7155,12 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, att->isCdata = isCdata; if (! isCdata) attId->maybeTokenized = XML_TRUE; + + NAMED *const nameAddedOrFound = (NAMED *)lookup( + parser, &(type->defaultAttsNames), attId->name, sizeof(NAMED)); + if (! nameAddedOrFound) + return 0; + type->nDefaultAtts += 1; return 1; } @@ -7065,16 +7382,24 @@ setContext(XML_Parser parser, const XML_Char *context) { else { if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return XML_FALSE; - prefix - = (PREFIX *)lookup(parser, &dtd->prefixes, - poolStart(&parser->m_tempPool), sizeof(PREFIX)); - if (! prefix) + const XML_Char *const prefixName = poolCopyStringNoFinish( + &dtd->pool, poolStart(&parser->m_tempPool)); + if (! prefixName) { return XML_FALSE; - if (prefix->name == poolStart(&parser->m_tempPool)) { - prefix->name = poolCopyString(&dtd->pool, prefix->name); - if (! prefix->name) - return XML_FALSE; } + + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, prefixName, + sizeof(PREFIX)); + + const bool prefixNameUsed = prefix && prefix->name == prefixName; + if (prefixNameUsed) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + + if (! prefix) + return XML_FALSE; + poolDiscard(&parser->m_tempPool); } for (context = s + 1; *context != CONTEXT_SEP && *context != XML_T('\0'); @@ -7122,19 +7447,19 @@ normalizePublicId(XML_Char *publicId) { } static DTD * -dtdCreate(const XML_Memory_Handling_Suite *ms) { - DTD *p = ms->malloc_fcn(sizeof(DTD)); +dtdCreate(XML_Parser parser) { + DTD *p = MALLOC(parser, sizeof(DTD)); if (p == NULL) return p; - poolInit(&(p->pool), ms); - poolInit(&(p->entityValuePool), ms); - hashTableInit(&(p->generalEntities), ms); - hashTableInit(&(p->elementTypes), ms); - hashTableInit(&(p->attributeIds), ms); - hashTableInit(&(p->prefixes), ms); + poolInit(&(p->pool), parser); + poolInit(&(p->entityValuePool), parser); + hashTableInit(&(p->generalEntities), parser); + hashTableInit(&(p->elementTypes), parser); + hashTableInit(&(p->attributeIds), parser); + hashTableInit(&(p->prefixes), parser); #ifdef XML_DTD p->paramEntityRead = XML_FALSE; - hashTableInit(&(p->paramEntities), ms); + hashTableInit(&(p->paramEntities), parser); #endif /* XML_DTD */ p->defaultPrefix.name = NULL; p->defaultPrefix.binding = NULL; @@ -7154,15 +7479,16 @@ dtdCreate(const XML_Memory_Handling_Suite *ms) { } static void -dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { +dtdReset(DTD *p, XML_Parser parser) { HASH_TABLE_ITER iter; hashTableIterInit(&iter, &(p->elementTypes)); for (;;) { ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); if (! e) break; + hashTableDestroy(&(e->defaultAttsNames)); if (e->allocDefaultAtts != 0) - ms->free_fcn(e->defaultAtts); + FREE(parser, e->defaultAtts); } hashTableClear(&(p->generalEntities)); #ifdef XML_DTD @@ -7179,9 +7505,9 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { p->in_eldecl = XML_FALSE; - ms->free_fcn(p->scaffIndex); + FREE(parser, p->scaffIndex); p->scaffIndex = NULL; - ms->free_fcn(p->scaffold); + FREE(parser, p->scaffold); p->scaffold = NULL; p->scaffLevel = 0; @@ -7195,15 +7521,16 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { } static void -dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { +dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) { HASH_TABLE_ITER iter; hashTableIterInit(&iter, &(p->elementTypes)); for (;;) { ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); if (! e) break; + hashTableDestroy(&(e->defaultAttsNames)); if (e->allocDefaultAtts != 0) - ms->free_fcn(e->defaultAtts); + FREE(parser, e->defaultAtts); } hashTableDestroy(&(p->generalEntities)); #ifdef XML_DTD @@ -7215,10 +7542,10 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { poolDestroy(&(p->pool)); poolDestroy(&(p->entityValuePool)); if (isDocEntity) { - ms->free_fcn(p->scaffIndex); - ms->free_fcn(p->scaffold); + FREE(parser, p->scaffIndex); + FREE(parser, p->scaffold); } - ms->free_fcn(p); + FREE(parser, p); } /* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. @@ -7226,7 +7553,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { */ static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, - const XML_Memory_Handling_Suite *ms) { + XML_Parser parser) { HASH_TABLE_ITER iter; /* Copy the prefix table. */ @@ -7295,19 +7622,22 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, sizeof(ELEMENT_TYPE)); if (! newE) return 0; + + if (! newE->defaultAttsNames.parser) + hashTableInit(&(newE->defaultAttsNames), parser); + if (oldE->nDefaultAtts) { /* Detect and prevent integer overflow. * The preprocessor guard addresses the "always false" warning * from -Wtype-limits on platforms where * sizeof(int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if ((size_t)oldE->nDefaultAtts - > ((size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE))) { + if ((size_t)oldE->nDefaultAtts > SIZE_MAX / sizeof(DEFAULT_ATTRIBUTE)) { return 0; } #endif newE->defaultAtts - = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + = MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); if (! newE->defaultAtts) { return 0; } @@ -7320,8 +7650,9 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), oldE->prefix->name, 0); for (i = 0; i < newE->nDefaultAtts; i++) { + const XML_Char *const attributeName = oldE->defaultAtts[i].id->name; newE->defaultAtts[i].id = (ATTRIBUTE_ID *)lookup( - oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + oldParser, &(newDtd->attributeIds), attributeName, 0); newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; if (oldE->defaultAtts[i].value) { newE->defaultAtts[i].value @@ -7330,6 +7661,12 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, return 0; } else newE->defaultAtts[i].value = NULL; + + NAMED *const nameAddedOrFound = (NAMED *)lookup( + parser, &(newE->defaultAttsNames), attributeName, sizeof(NAMED)); + if (! nameAddedOrFound) { + return 0; + } } } @@ -7443,8 +7780,10 @@ keylen(KEY s) { static void copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key) { - key->k[0] = 0; - key->k[1] = get_hash_secret_salt(parser); + const XML_Parser rootParser = getRootParserOf(parser, NULL); + assert(! rootParser->m_parentParser); + + *key = rootParser->m_hash_secret_salt_128; } static unsigned long FASTCALL @@ -7469,7 +7808,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { /* table->size is a power of 2 */ table->size = (size_t)1 << INIT_POWER; tsize = table->size * sizeof(NAMED *); - table->v = table->mem->malloc_fcn(tsize); + table->v = MALLOC(table->parser, tsize); if (! table->v) { table->size = 0; return NULL; @@ -7504,12 +7843,12 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { unsigned long newMask = (unsigned long)newSize - 1; /* Detect and prevent integer overflow */ - if (newSize > (size_t)(-1) / sizeof(NAMED *)) { + if (newSize > SIZE_MAX / sizeof(NAMED *)) { return NULL; } size_t tsize = newSize * sizeof(NAMED *); - NAMED **newV = table->mem->malloc_fcn(tsize); + NAMED **newV = MALLOC(table->parser, tsize); if (! newV) return NULL; memset(newV, 0, tsize); @@ -7525,7 +7864,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { } newV[j] = table->v[i]; } - table->mem->free_fcn(table->v); + FREE(table->parser, table->v); table->v = newV; table->power = newPower; table->size = newSize; @@ -7538,7 +7877,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { } } } - table->v[i] = table->mem->malloc_fcn(createSize); + table->v[i] = MALLOC(table->parser, createSize); if (! table->v[i]) return NULL; memset(table->v[i], 0, createSize); @@ -7551,7 +7890,7 @@ static void FASTCALL hashTableClear(HASH_TABLE *table) { size_t i; for (i = 0; i < table->size; i++) { - table->mem->free_fcn(table->v[i]); + FREE(table->parser, table->v[i]); table->v[i] = NULL; } table->used = 0; @@ -7561,17 +7900,17 @@ static void FASTCALL hashTableDestroy(HASH_TABLE *table) { size_t i; for (i = 0; i < table->size; i++) - table->mem->free_fcn(table->v[i]); - table->mem->free_fcn(table->v); + FREE(table->parser, table->v[i]); + FREE(table->parser, table->v); } static void FASTCALL -hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) { +hashTableInit(HASH_TABLE *p, XML_Parser parser) { p->power = 0; p->size = 0; p->used = 0; p->v = NULL; - p->mem = ms; + p->parser = parser; } static void FASTCALL @@ -7591,13 +7930,13 @@ hashTableIterNext(HASH_TABLE_ITER *iter) { } static void FASTCALL -poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) { +poolInit(STRING_POOL *pool, XML_Parser parser) { pool->blocks = NULL; pool->freeBlocks = NULL; pool->start = NULL; pool->ptr = NULL; pool->end = NULL; - pool->mem = ms; + pool->parser = parser; } static void FASTCALL @@ -7624,13 +7963,13 @@ poolDestroy(STRING_POOL *pool) { BLOCK *p = pool->blocks; while (p) { BLOCK *tem = p->next; - pool->mem->free_fcn(p); + FREE(pool->parser, p); p = tem; } p = pool->freeBlocks; while (p) { BLOCK *tem = p->next; - pool->mem->free_fcn(p); + FREE(pool->parser, p); p = tem; } } @@ -7663,6 +8002,23 @@ poolCopyString(STRING_POOL *pool, const XML_Char *s) { return s; } +// A version of `poolCopyString` that does not call `poolFinish` +// and reverts any partial advancement upon failure. +static const XML_Char *FASTCALL +poolCopyStringNoFinish(STRING_POOL *pool, const XML_Char *s) { + const XML_Char *const original = s; + do { + if (! poolAppendChar(pool, *s)) { + // Revert any previously successful advancement + const ptrdiff_t advancedBy = s - original; + if (advancedBy > 0) + pool->ptr -= advancedBy; + return NULL; + } + } while (*s++); + return pool->start; +} + static const XML_Char * poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) { if (! pool->ptr && ! poolGrow(pool)) { @@ -7740,7 +8096,7 @@ poolBytesToAllocateFor(int blockSize) { static XML_Bool FASTCALL poolGrow(STRING_POOL *pool) { if (pool->freeBlocks) { - if (pool->start == 0) { + if (pool->start == NULL) { pool->blocks = pool->freeBlocks; pool->freeBlocks = pool->freeBlocks->next; pool->blocks->next = NULL; @@ -7785,8 +8141,7 @@ poolGrow(STRING_POOL *pool) { if (bytesToAllocate == 0) return XML_FALSE; - temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks, - (unsigned)bytesToAllocate); + temp = REALLOC(pool->parser, pool->blocks, bytesToAllocate); if (temp == NULL) return XML_FALSE; pool->blocks = temp; @@ -7826,7 +8181,7 @@ poolGrow(STRING_POOL *pool) { if (bytesToAllocate == 0) return XML_FALSE; - tem = pool->mem->malloc_fcn(bytesToAllocate); + tem = MALLOC(pool->parser, bytesToAllocate); if (! tem) return XML_FALSE; tem->size = blockSize; @@ -7853,16 +8208,21 @@ nextScaffoldPart(XML_Parser parser) { * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if (parser->m_groupSize > ((size_t)(-1) / sizeof(int))) { + if (parser->m_groupSize > SIZE_MAX / sizeof(int)) { return -1; } #endif - dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int)); + dtd->scaffIndex = MALLOC(parser, parser->m_groupSize * sizeof(int)); if (! dtd->scaffIndex) return -1; dtd->scaffIndex[0] = 0; } + // Will casting to int be safe further down? + if (dtd->scaffCount > INT_MAX) { + return -1; + } + if (dtd->scaffCount >= dtd->scaffSize) { CONTENT_SCAFFOLD *temp; if (dtd->scaffold) { @@ -7875,26 +8235,25 @@ nextScaffoldPart(XML_Parser parser) { * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if (dtd->scaffSize > (size_t)(-1) / 2u / sizeof(CONTENT_SCAFFOLD)) { + if (dtd->scaffSize > SIZE_MAX / 2u / sizeof(CONTENT_SCAFFOLD)) { return -1; } #endif - temp = (CONTENT_SCAFFOLD *)REALLOC( - parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); + temp = REALLOC(parser, dtd->scaffold, + dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); if (temp == NULL) return -1; dtd->scaffSize *= 2; } else { - temp = (CONTENT_SCAFFOLD *)MALLOC(parser, INIT_SCAFFOLD_ELEMENTS - * sizeof(CONTENT_SCAFFOLD)); + temp = MALLOC(parser, INIT_SCAFFOLD_ELEMENTS * sizeof(CONTENT_SCAFFOLD)); if (temp == NULL) return -1; dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; } dtd->scaffold = temp; } - next = dtd->scaffCount++; + next = (int)dtd->scaffCount++; me = &dtd->scaffold[next]; if (dtd->scaffLevel) { CONTENT_SCAFFOLD *parent @@ -7926,22 +8285,25 @@ build_model(XML_Parser parser) { * from -Wtype-limits on platforms where * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX - if (dtd->scaffCount > (size_t)(-1) / sizeof(XML_Content)) { + if (dtd->scaffCount > SIZE_MAX / sizeof(XML_Content)) { return NULL; } - if (dtd->contentStringLen > (size_t)(-1) / sizeof(XML_Char)) { + if (dtd->contentStringLen > SIZE_MAX / sizeof(XML_Char)) { return NULL; } #endif if (dtd->scaffCount * sizeof(XML_Content) - > (size_t)(-1) - dtd->contentStringLen * sizeof(XML_Char)) { + > SIZE_MAX - dtd->contentStringLen * sizeof(XML_Char)) { return NULL; } const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content) + (dtd->contentStringLen * sizeof(XML_Char))); - ret = (XML_Content *)MALLOC(parser, allocsize); + // NOTE: We are avoiding MALLOC(..) here to so that + // applications that are not using XML_FreeContentModel but plain + // free(..) or .free_fcn() to free the content model's memory are safe. + ret = parser->m_mem.malloc_fcn(allocsize); if (! ret) return NULL; @@ -8051,6 +8413,8 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr, sizeof(ELEMENT_TYPE)); if (! ret) return NULL; + if (! ret->defaultAttsNames.parser) + hashTableInit(&(ret->defaultAttsNames), getRootParserOf(parser, NULL)); if (ret->name != name) poolDiscard(&dtd->pool); else { @@ -8062,7 +8426,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr, } static XML_Char * -copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { +copyString(const XML_Char *s, XML_Parser parser) { size_t charsRequired = 0; XML_Char *result; @@ -8074,7 +8438,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { charsRequired++; /* Now allocate space for the copy */ - result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char)); + result = MALLOC(parser, charsRequired * sizeof(XML_Char)); if (result == NULL) return NULL; /* Copy the original into place */ @@ -8093,10 +8457,10 @@ accountingGetCurrentAmplification(XML_Parser rootParser) { + rootParser->m_accounting.countBytesIndirect; const float amplificationFactor = rootParser->m_accounting.countBytesDirect - ? (countBytesOutput + ? ((float)countBytesOutput / (float)(rootParser->m_accounting.countBytesDirect)) - : ((lenOfShortestInclude - + rootParser->m_accounting.countBytesIndirect) + : ((float)(lenOfShortestInclude + + rootParser->m_accounting.countBytesIndirect) / (float)lenOfShortestInclude); assert(! rootParser->m_parentParser); return amplificationFactor; @@ -8280,6 +8644,8 @@ entityTrackingOnClose(XML_Parser originParser, ENTITY *entity, int sourceLine) { rootParser->m_entity_stats.currentDepth--; } +#endif /* XML_GE == 1 */ + static XML_Parser getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) { XML_Parser rootParser = parser; @@ -8295,6 +8661,8 @@ getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) { return rootParser; } +#if XML_GE == 1 + const char * unsignedCharToPrintable(unsigned char c) { switch (c) { diff --git a/Modules/expat/xmlrole.c b/Modules/expat/xmlrole.c index 2c48bf40867953..d56bee82dd2d13 100644 --- a/Modules/expat/xmlrole.c +++ b/Modules/expat/xmlrole.c @@ -16,6 +16,7 @@ Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk> Copyright (c) 2019 David Loffredo <loffredo@steptools.com> Copyright (c) 2021 Donghee Na <donghee.na@python.org> + Copyright (c) 2025 Alfonso Gregory <gfunni234@gmail.com> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -46,7 +47,6 @@ # include "winconfig.h" #endif -#include "expat_external.h" #include "internal.h" #include "xmlrole.h" #include "ascii.h" diff --git a/Modules/expat/xmlrole.h b/Modules/expat/xmlrole.h index a7904274c91d4e..9d0d4ff11b7f98 100644 --- a/Modules/expat/xmlrole.h +++ b/Modules/expat/xmlrole.h @@ -10,7 +10,7 @@ Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net> Copyright (c) 2002 Karl Waclawek <karl@waclawek.net> Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> - Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2017-2025 Sebastian Pipping <sebastian@pipping.org> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -34,19 +34,13 @@ */ #ifndef XmlRole_INCLUDED -#define XmlRole_INCLUDED 1 +# define XmlRole_INCLUDED 1 -#ifdef __VMS -/* 0 1 2 3 0 1 2 3 - 1234567890123456789012345678901 1234567890123456789012345678901 */ -# define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt -#endif +# include "xmltok.h" -#include "xmltok.h" - -#ifdef __cplusplus +# ifdef __cplusplus extern "C" { -#endif +# endif enum { XML_ROLE_ERROR = -1, @@ -107,11 +101,11 @@ enum { XML_ROLE_CONTENT_ELEMENT_PLUS, XML_ROLE_PI, XML_ROLE_COMMENT, -#ifdef XML_DTD +# ifdef XML_DTD XML_ROLE_TEXT_DECL, XML_ROLE_IGNORE_SECT, XML_ROLE_INNER_PARAM_ENTITY_REF, -#endif /* XML_DTD */ +# endif /* XML_DTD */ XML_ROLE_PARAM_ENTITY_REF }; @@ -120,23 +114,23 @@ typedef struct prolog_state { const char *end, const ENCODING *enc); unsigned level; int role_none; -#ifdef XML_DTD +# ifdef XML_DTD unsigned includeLevel; int documentEntity; int inEntityValue; -#endif /* XML_DTD */ +# endif /* XML_DTD */ } PROLOG_STATE; void XmlPrologStateInit(PROLOG_STATE *state); -#ifdef XML_DTD +# ifdef XML_DTD void XmlPrologStateInitExternalEntity(PROLOG_STATE *state); -#endif /* XML_DTD */ +# endif /* XML_DTD */ -#define XmlTokenRole(state, tok, ptr, end, enc) \ - (((state)->handler)(state, tok, ptr, end, enc)) +# define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) -#ifdef __cplusplus +# ifdef __cplusplus } -#endif +# endif #endif /* not XmlRole_INCLUDED */ diff --git a/Modules/expat/xmltok.c b/Modules/expat/xmltok.c index 29a66d72ceea5e..32cd5f147e9322 100644 --- a/Modules/expat/xmltok.c +++ b/Modules/expat/xmltok.c @@ -24,6 +24,7 @@ Copyright (c) 2022 Martin Ettl <ettl.martin78@googlemail.com> Copyright (c) 2022 Sean McBride <sean@rogue-research.com> Copyright (c) 2023 Hanno Böck <hanno@gentoo.org> + Copyright (c) 2025 Alfonso Gregory <gfunni234@gmail.com> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -56,7 +57,6 @@ # include "winconfig.h" #endif -#include "expat_external.h" #include "internal.h" #include "xmltok.h" #include "nametab.h" @@ -1398,7 +1398,7 @@ unknown_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim, } ENCODING * -XmlInitUnknownEncoding(void *mem, int *table, CONVERTER convert, +XmlInitUnknownEncoding(void *mem, const int *table, CONVERTER convert, void *userData) { int i; struct unknown_encoding *e = (struct unknown_encoding *)mem; @@ -1661,7 +1661,7 @@ initScan(const ENCODING *const *encodingTable, const INIT_ENCODING *enc, # undef ns ENCODING * -XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert, +XmlInitUnknownEncodingNS(void *mem, const int *table, CONVERTER convert, void *userData) { ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); if (enc) diff --git a/Modules/expat/xmltok.h b/Modules/expat/xmltok.h index c51fce1ec1518b..79a9fb76871f10 100644 --- a/Modules/expat/xmltok.h +++ b/Modules/expat/xmltok.h @@ -35,113 +35,113 @@ */ #ifndef XmlTok_INCLUDED -#define XmlTok_INCLUDED 1 +# define XmlTok_INCLUDED 1 -#ifdef __cplusplus +# ifdef __cplusplus extern "C" { -#endif +# endif /* The following token may be returned by XmlContentTok */ -#define XML_TOK_TRAILING_RSQB \ - -5 /* ] or ]] at the end of the scan; might be \ - start of illegal ]]> sequence */ +# define XML_TOK_TRAILING_RSQB \ + -5 /* ] or ]] at the end of the scan; might be \ + start of illegal ]]> sequence */ /* The following tokens may be returned by both XmlPrologTok and XmlContentTok. */ -#define XML_TOK_NONE -4 /* The string to be scanned is empty */ -#define XML_TOK_TRAILING_CR \ - -3 /* A CR at the end of the scan; \ - might be part of CRLF sequence */ -#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ -#define XML_TOK_PARTIAL -1 /* only part of a token */ -#define XML_TOK_INVALID 0 +# define XML_TOK_NONE -4 /* The string to be scanned is empty */ +# define XML_TOK_TRAILING_CR \ + -3 /* A CR at the end of the scan; \ + might be part of CRLF sequence */ +# define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +# define XML_TOK_PARTIAL -1 /* only part of a token */ +# define XML_TOK_INVALID 0 /* The following tokens are returned by XmlContentTok; some are also returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok. */ -#define XML_TOK_START_TAG_WITH_ATTS 1 -#define XML_TOK_START_TAG_NO_ATTS 2 -#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */ -#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 -#define XML_TOK_END_TAG 5 -#define XML_TOK_DATA_CHARS 6 -#define XML_TOK_DATA_NEWLINE 7 -#define XML_TOK_CDATA_SECT_OPEN 8 -#define XML_TOK_ENTITY_REF 9 -#define XML_TOK_CHAR_REF 10 /* numeric character reference */ +# define XML_TOK_START_TAG_WITH_ATTS 1 +# define XML_TOK_START_TAG_NO_ATTS 2 +# define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */ +# define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +# define XML_TOK_END_TAG 5 +# define XML_TOK_DATA_CHARS 6 +# define XML_TOK_DATA_NEWLINE 7 +# define XML_TOK_CDATA_SECT_OPEN 8 +# define XML_TOK_ENTITY_REF 9 +# define XML_TOK_CHAR_REF 10 /* numeric character reference */ /* The following tokens may be returned by both XmlPrologTok and XmlContentTok. */ -#define XML_TOK_PI 11 /* processing instruction */ -#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ -#define XML_TOK_COMMENT 13 -#define XML_TOK_BOM 14 /* Byte order mark */ +# define XML_TOK_PI 11 /* processing instruction */ +# define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +# define XML_TOK_COMMENT 13 +# define XML_TOK_BOM 14 /* Byte order mark */ /* The following tokens are returned only by XmlPrologTok */ -#define XML_TOK_PROLOG_S 15 -#define XML_TOK_DECL_OPEN 16 /* <!foo */ -#define XML_TOK_DECL_CLOSE 17 /* > */ -#define XML_TOK_NAME 18 -#define XML_TOK_NMTOKEN 19 -#define XML_TOK_POUND_NAME 20 /* #name */ -#define XML_TOK_OR 21 /* | */ -#define XML_TOK_PERCENT 22 -#define XML_TOK_OPEN_PAREN 23 -#define XML_TOK_CLOSE_PAREN 24 -#define XML_TOK_OPEN_BRACKET 25 -#define XML_TOK_CLOSE_BRACKET 26 -#define XML_TOK_LITERAL 27 -#define XML_TOK_PARAM_ENTITY_REF 28 -#define XML_TOK_INSTANCE_START 29 +# define XML_TOK_PROLOG_S 15 +# define XML_TOK_DECL_OPEN 16 /* <!foo */ +# define XML_TOK_DECL_CLOSE 17 /* > */ +# define XML_TOK_NAME 18 +# define XML_TOK_NMTOKEN 19 +# define XML_TOK_POUND_NAME 20 /* #name */ +# define XML_TOK_OR 21 /* | */ +# define XML_TOK_PERCENT 22 +# define XML_TOK_OPEN_PAREN 23 +# define XML_TOK_CLOSE_PAREN 24 +# define XML_TOK_OPEN_BRACKET 25 +# define XML_TOK_CLOSE_BRACKET 26 +# define XML_TOK_LITERAL 27 +# define XML_TOK_PARAM_ENTITY_REF 28 +# define XML_TOK_INSTANCE_START 29 /* The following occur only in element type declarations */ -#define XML_TOK_NAME_QUESTION 30 /* name? */ -#define XML_TOK_NAME_ASTERISK 31 /* name* */ -#define XML_TOK_NAME_PLUS 32 /* name+ */ -#define XML_TOK_COND_SECT_OPEN 33 /* <![ */ -#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */ -#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ -#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ -#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ -#define XML_TOK_COMMA 38 +# define XML_TOK_NAME_QUESTION 30 /* name? */ +# define XML_TOK_NAME_ASTERISK 31 /* name* */ +# define XML_TOK_NAME_PLUS 32 /* name+ */ +# define XML_TOK_COND_SECT_OPEN 33 /* <![ */ +# define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */ +# define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +# define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +# define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +# define XML_TOK_COMMA 38 /* The following token is returned only by XmlAttributeValueTok */ -#define XML_TOK_ATTRIBUTE_VALUE_S 39 +# define XML_TOK_ATTRIBUTE_VALUE_S 39 /* The following token is returned only by XmlCdataSectionTok */ -#define XML_TOK_CDATA_SECT_CLOSE 40 +# define XML_TOK_CDATA_SECT_CLOSE 40 /* With namespace processing this is returned by XmlPrologTok for a name with a colon. */ -#define XML_TOK_PREFIXED_NAME 41 +# define XML_TOK_PREFIXED_NAME 41 -#ifdef XML_DTD -# define XML_TOK_IGNORE_SECT 42 -#endif /* XML_DTD */ +# ifdef XML_DTD +# define XML_TOK_IGNORE_SECT 42 +# endif /* XML_DTD */ -#ifdef XML_DTD -# define XML_N_STATES 4 -#else /* not XML_DTD */ -# define XML_N_STATES 3 -#endif /* not XML_DTD */ +# ifdef XML_DTD +# define XML_N_STATES 4 +# else /* not XML_DTD */ +# define XML_N_STATES 3 +# endif /* not XML_DTD */ -#define XML_PROLOG_STATE 0 -#define XML_CONTENT_STATE 1 -#define XML_CDATA_SECTION_STATE 2 -#ifdef XML_DTD -# define XML_IGNORE_SECTION_STATE 3 -#endif /* XML_DTD */ +# define XML_PROLOG_STATE 0 +# define XML_CONTENT_STATE 1 +# define XML_CDATA_SECTION_STATE 2 +# ifdef XML_DTD +# define XML_IGNORE_SECTION_STATE 3 +# endif /* XML_DTD */ -#define XML_N_LITERAL_TYPES 2 -#define XML_ATTRIBUTE_VALUE_LITERAL 0 -#define XML_ENTITY_VALUE_LITERAL 1 +# define XML_N_LITERAL_TYPES 2 +# define XML_ATTRIBUTE_VALUE_LITERAL 0 +# define XML_ENTITY_VALUE_LITERAL 1 /* The size of the buffer passed to XmlUtf8Encode must be at least this. */ -#define XML_UTF8_ENCODE_MAX 4 +# define XML_UTF8_ENCODE_MAX 4 /* The size of the buffer passed to XmlUtf16Encode must be at least this. */ -#define XML_UTF16_ENCODE_MAX 2 +# define XML_UTF16_ENCODE_MAX 2 typedef struct position { /* first line and first column are 0 not 1 */ @@ -220,63 +220,63 @@ struct encoding { the prolog outside literals, comments and processing instructions. */ -#define XmlTok(enc, state, ptr, end, nextTokPtr) \ - (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) +# define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) -#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) +# define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) -#define XmlContentTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) +# define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) -#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) +# define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) -#ifdef XML_DTD +# ifdef XML_DTD -# define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) +# define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) -#endif /* XML_DTD */ +# endif /* XML_DTD */ /* This is used for performing a 2nd-level tokenization on the content of a literal that has already been returned by XmlTok. */ -#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ - (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) +# define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) -#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ - XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) +# define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) -#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ - XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) +# define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) -#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ - (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) +# define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) -#define XmlNameLength(enc, ptr) (((enc)->nameLength)(enc, ptr)) +# define XmlNameLength(enc, ptr) (((enc)->nameLength)(enc, ptr)) -#define XmlSkipS(enc, ptr) (((enc)->skipS)(enc, ptr)) +# define XmlSkipS(enc, ptr) (((enc)->skipS)(enc, ptr)) -#define XmlGetAttributes(enc, ptr, attsMax, atts) \ - (((enc)->getAtts)(enc, ptr, attsMax, atts)) +# define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) -#define XmlCharRefNumber(enc, ptr) (((enc)->charRefNumber)(enc, ptr)) +# define XmlCharRefNumber(enc, ptr) (((enc)->charRefNumber)(enc, ptr)) -#define XmlPredefinedEntityName(enc, ptr, end) \ - (((enc)->predefinedEntityName)(enc, ptr, end)) +# define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) -#define XmlUpdatePosition(enc, ptr, end, pos) \ - (((enc)->updatePosition)(enc, ptr, end, pos)) +# define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) -#define XmlIsPublicId(enc, ptr, end, badPtr) \ - (((enc)->isPublicId)(enc, ptr, end, badPtr)) +# define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) -#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ - (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) +# define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) -#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ - (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) +# define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) typedef struct { ENCODING initEnc; @@ -299,7 +299,7 @@ int XmlSizeOfUnknownEncoding(void); typedef int(XMLCALL *CONVERTER)(void *userData, const char *p); -ENCODING *XmlInitUnknownEncoding(void *mem, int *table, CONVERTER convert, +ENCODING *XmlInitUnknownEncoding(void *mem, const int *table, CONVERTER convert, void *userData); int XmlParseXmlDeclNS(int isGeneralTextEntity, const ENCODING *enc, @@ -312,10 +312,10 @@ int XmlInitEncodingNS(INIT_ENCODING *p, const ENCODING **encPtr, const char *name); const ENCODING *XmlGetUtf8InternalEncodingNS(void); const ENCODING *XmlGetUtf16InternalEncodingNS(void); -ENCODING *XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert, - void *userData); -#ifdef __cplusplus +ENCODING *XmlInitUnknownEncodingNS(void *mem, const int *table, + CONVERTER convert, void *userData); +# ifdef __cplusplus } -#endif +# endif #endif /* not XmlTok_INCLUDED */ diff --git a/Modules/expat/xmltok_ns.c b/Modules/expat/xmltok_ns.c index fbdd3e3c7b7999..810ca2c6d0485e 100644 --- a/Modules/expat/xmltok_ns.c +++ b/Modules/expat/xmltok_ns.c @@ -12,6 +12,7 @@ Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net> Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2025 Alfonso Gregory <gfunni234@gmail.com> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -98,13 +99,13 @@ NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) { int i; XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); if (ptr != end) - return 0; + return NULL; *p = 0; if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) return enc; i = getEncodingIndex(buf); if (i == UNKNOWN_ENC) - return 0; + return NULL; return NS(encodings)[i]; } diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index 73bea8172c7253..7c727d8c2d4ff0 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -7,7 +7,7 @@ #include "pycore_runtime.h" // _Py_ID() #include "pycore_signal.h" // Py_NSIG #include "pycore_time.h" // _PyTime_FromSecondsObject() -#include "pycore_traceback.h" // _Py_DumpTracebackThreads +#include "pycore_traceback.h" // _Py_DumpStack() #ifdef HAVE_UNISTD_H # include <unistd.h> // _exit() #endif @@ -30,6 +30,9 @@ #endif +#include "clinic/faulthandler.c.h" + + /* Sentinel to ignore all_threads on free-threading */ #define FT_IGNORE_ALL_THREADS 2 @@ -39,6 +42,12 @@ #define PUTS(fd, str) (void)_Py_write_noraise(fd, str, strlen(str)) +/*[clinic input] +module faulthandler +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c3d4f47c4f3d440f]*/ + + typedef struct { int signum; int enabled; @@ -176,7 +185,8 @@ get_thread_state(void) static void faulthandler_dump_traceback(int fd, int all_threads, - PyInterpreterState *interp) + PyInterpreterState *interp, + Py_ssize_t max_threads) { static volatile int reentrant = 0; @@ -196,14 +206,15 @@ faulthandler_dump_traceback(int fd, int all_threads, PyThreadState *tstate = PyGILState_GetThisThreadState(); if (all_threads == 1) { - (void)_Py_DumpTracebackThreads(fd, NULL, tstate); + (void)PyUnstable_DumpTracebackThreads(fd, NULL, tstate, max_threads); } else { if (all_threads == FT_IGNORE_ALL_THREADS) { PUTS(fd, "<Cannot show all threads while the GIL is disabled>\n"); } - if (tstate != NULL) - _Py_DumpTraceback(fd, tstate); + if (tstate != NULL) { + PyUnstable_DumpTraceback(fd, tstate); + } } reentrant = 0; @@ -228,22 +239,30 @@ faulthandler_dump_c_stack(int fd) reentrant = 0; } -static PyObject* -faulthandler_dump_traceback_py(PyObject *self, - PyObject *args, PyObject *kwargs) + +/*[clinic input] +faulthandler.dump_traceback as faulthandler_dump_traceback_py + + file: object(py_default="sys.stderr") = NULL + all_threads: bool = True + * + max_threads: Py_ssize_t = 100 + +Dump the traceback of the current thread into file. + +Dump the traceback of all threads if all_threads is true. max_threads +caps the number of threads dumped. +[clinic start generated code]*/ + +static PyObject * +faulthandler_dump_traceback_py_impl(PyObject *module, PyObject *file, + int all_threads, Py_ssize_t max_threads) +/*[clinic end generated code: output=ee1bbc2668e56e77 input=38630eb40e641de6]*/ { - static char *kwlist[] = {"file", "all_threads", NULL}; - PyObject *file = NULL; - int all_threads = 1; PyThreadState *tstate; const char *errmsg; int fd; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "|Op:dump_traceback", kwlist, - &file, &all_threads)) - return NULL; - fd = faulthandler_get_fileno(&file); if (fd < 0) return NULL; @@ -259,17 +278,18 @@ faulthandler_dump_traceback_py(PyObject *self, /* gh-128400: Accessing other thread states while they're running * isn't safe if those threads are running. */ _PyEval_StopTheWorld(interp); - errmsg = _Py_DumpTracebackThreads(fd, NULL, tstate); + errmsg = PyUnstable_DumpTracebackThreads(fd, NULL, tstate, max_threads); _PyEval_StartTheWorld(interp); - if (errmsg != NULL) { - PyErr_SetString(PyExc_RuntimeError, errmsg); - Py_XDECREF(file); - return NULL; - } } else { - _Py_DumpTraceback(fd, tstate); + errmsg = PyUnstable_DumpTraceback(fd, tstate); } + if (errmsg != NULL) { + PyErr_SetString(PyExc_RuntimeError, errmsg); + Py_XDECREF(file); + return NULL; + } + Py_XDECREF(file); if (PyErr_CheckSignals()) @@ -278,19 +298,19 @@ faulthandler_dump_traceback_py(PyObject *self, Py_RETURN_NONE; } -static PyObject * -faulthandler_dump_c_stack_py(PyObject *self, - PyObject *args, PyObject *kwargs) -{ - static char *kwlist[] = {"file", NULL}; - PyObject *file = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "|O:dump_c_stack", kwlist, - &file)) { - return NULL; - } +/*[clinic input] +faulthandler.dump_c_stack as faulthandler_dump_c_stack_py + file: object(py_default="sys.stderr") = NULL + +Dump the C stack of the current thread. +[clinic start generated code]*/ + +static PyObject * +faulthandler_dump_c_stack_py_impl(PyObject *module, PyObject *file) +/*[clinic end generated code: output=151d6c95e9f8c0f6 input=10f6b6f29b635109]*/ +{ int fd = faulthandler_get_fileno(&file); if (fd < 0) { return NULL; @@ -395,7 +415,8 @@ faulthandler_fatal_error(int signum) } faulthandler_dump_traceback(fd, deduce_all_threads(), - fatal_error.interp); + fatal_error.interp, + fatal_error.max_threads); faulthandler_dump_c_stack(fd); _Py_DumpExtensionModules(fd, fatal_error.interp); @@ -471,7 +492,8 @@ faulthandler_exc_handler(struct _EXCEPTION_POINTERS *exc_info) } faulthandler_dump_traceback(fd, deduce_all_threads(), - fatal_error.interp); + fatal_error.interp, + fatal_error.max_threads); faulthandler_dump_c_stack(fd); /* call the next exception handler */ @@ -524,6 +546,11 @@ faulthandler_enable(void) } #endif + // gh-137185: Initialize C stack trace dumping outside of the signal + // handler. Specifically, we call backtrace() to ensure that libgcc is + // dynamically loaded outside of the signal handler. + _Py_InitDumpStack(); + for (size_t i=0; i < faulthandler_nsignals; i++) { fault_handler_t *handler; int err; @@ -564,20 +591,28 @@ faulthandler_enable(void) return 0; } -static PyObject* -faulthandler_py_enable(PyObject *self, PyObject *args, PyObject *kwargs) + +/*[clinic input] +faulthandler.enable as faulthandler_py_enable + + file: object(py_default="sys.stderr") = NULL + all_threads: bool = True + c_stack: bool = True + * + max_threads: Py_ssize_t = 100 + +Enable the fault handler. +[clinic start generated code]*/ + +static PyObject * +faulthandler_py_enable_impl(PyObject *module, PyObject *file, + int all_threads, int c_stack, + Py_ssize_t max_threads) +/*[clinic end generated code: output=7ee655332317c47a input=e64759714f27b466]*/ { - static char *kwlist[] = {"file", "all_threads", "c_stack", NULL}; - PyObject *file = NULL; - int all_threads = 1; int fd; - int c_stack = 1; PyThreadState *tstate; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "|Opp:enable", kwlist, &file, &all_threads, &c_stack)) - return NULL; - fd = faulthandler_get_fileno(&file); if (fd < 0) return NULL; @@ -593,6 +628,7 @@ faulthandler_py_enable(PyObject *self, PyObject *args, PyObject *kwargs) fatal_error.all_threads = all_threads; fatal_error.interp = PyThreadState_GetInterpreter(tstate); fatal_error.c_stack = c_stack; + fatal_error.max_threads = max_threads; if (faulthandler_enable() < 0) { return NULL; @@ -621,8 +657,16 @@ faulthandler_disable(void) Py_CLEAR(fatal_error.file); } -static PyObject* -faulthandler_disable_py(PyObject *self, PyObject *Py_UNUSED(ignored)) + +/*[clinic input] +faulthandler.disable as faulthandler_disable_py + +Disable the fault handler. +[clinic start generated code]*/ + +static PyObject * +faulthandler_disable_py_impl(PyObject *module) +/*[clinic end generated code: output=e9087a04535af3cb input=6223eac6804550af]*/ { if (!fatal_error.enabled) { Py_RETURN_FALSE; @@ -631,10 +675,18 @@ faulthandler_disable_py(PyObject *self, PyObject *Py_UNUSED(ignored)) Py_RETURN_TRUE; } -static PyObject* -faulthandler_is_enabled(PyObject *self, PyObject *Py_UNUSED(ignored)) + +/*[clinic input] +faulthandler.is_enabled -> bool + +Check if the handler is enabled. +[clinic start generated code]*/ + +static int +faulthandler_is_enabled_impl(PyObject *module) +/*[clinic end generated code: output=b9f33a3e0f881a23 input=3d5532547eb14bf9]*/ { - return PyBool_FromLong(fatal_error.enabled); + return fatal_error.enabled; } static void @@ -663,7 +715,8 @@ faulthandler_thread(void *unused) (void)_Py_write_noraise(thread.fd, thread.header, (int)thread.header_len); - errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, NULL); + errmsg = PyUnstable_DumpTracebackThreads(thread.fd, thread.interp, NULL, + thread.max_threads); ok = (errmsg == NULL); if (thread.exit) @@ -729,26 +782,37 @@ format_timeout(PyTime_t us) return _PyMem_Strdup(buffer); } -static PyObject* -faulthandler_dump_traceback_later(PyObject *self, - PyObject *args, PyObject *kwargs) + +/*[clinic input] +faulthandler.dump_traceback_later + + timeout as timeout_obj: object + repeat: bool = False + file: object(py_default="sys.stderr") = NULL + exit: bool = False + * + max_threads: Py_ssize_t = 100 + +Dump the traceback of all threads in timeout seconds. + +If repeat is true, the tracebacks of all threads are dumped every +timeout seconds. If exit is true, call _exit(1) which is not safe. +max_threads caps the number of threads dumped. +[clinic start generated code]*/ + +static PyObject * +faulthandler_dump_traceback_later_impl(PyObject *module, + PyObject *timeout_obj, int repeat, + PyObject *file, int exit, + Py_ssize_t max_threads) +/*[clinic end generated code: output=543a0f3807113394 input=32aaf7437d0928db]*/ { - static char *kwlist[] = {"timeout", "repeat", "file", "exit", NULL}; - PyObject *timeout_obj; PyTime_t timeout, timeout_us; - int repeat = 0; - PyObject *file = NULL; int fd; - int exit = 0; PyThreadState *tstate; char *header; size_t header_len; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "O|iOi:dump_traceback_later", kwlist, - &timeout_obj, &repeat, &file, &exit)) - return NULL; - if (_PyTime_FromSecondsObject(&timeout, timeout_obj, _PyTime_ROUND_TIMEOUT) < 0) { return NULL; @@ -814,6 +878,7 @@ faulthandler_dump_traceback_later(PyObject *self, thread.exit = exit; thread.header = header; thread.header_len = header_len; + thread.max_threads = max_threads; /* Arm these locks to serve as events when released */ PyThread_acquire_lock(thread.running, 1); @@ -831,9 +896,16 @@ faulthandler_dump_traceback_later(PyObject *self, Py_RETURN_NONE; } -static PyObject* -faulthandler_cancel_dump_traceback_later_py(PyObject *self, - PyObject *Py_UNUSED(ignored)) + +/*[clinic input] +faulthandler.cancel_dump_traceback_later as faulthandler_cancel_dump_traceback_later_py + +Cancel the previous call to dump_traceback_later(). +[clinic start generated code]*/ + +static PyObject * +faulthandler_cancel_dump_traceback_later_py_impl(PyObject *module) +/*[clinic end generated code: output=2cf303015d39c926 input=51ad64b6ca8412a4]*/ { cancel_dump_traceback_later(); Py_RETURN_NONE; @@ -891,7 +963,8 @@ faulthandler_user(int signum) if (!user->enabled) return; - faulthandler_dump_traceback(user->fd, user->all_threads, user->interp); + faulthandler_dump_traceback(user->fd, user->all_threads, user->interp, + user->max_threads); #ifdef HAVE_SIGACTION if (user->chain) { @@ -933,26 +1006,36 @@ check_signum(int signum) return 1; } -static PyObject* -faulthandler_register_py(PyObject *self, - PyObject *args, PyObject *kwargs) + +/*[clinic input] +faulthandler.register as faulthandler_register_py + + signum: int + file: object(py_default="sys.stderr") = NULL + all_threads: bool = True + chain: bool = False + * + max_threads: Py_ssize_t = 100 + +Register a handler for the signal 'signum'. + +Dump the traceback of the current thread, or of all threads if +all_threads is True, into file. max_threads caps the number of threads +dumped. +[clinic start generated code]*/ + +static PyObject * +faulthandler_register_py_impl(PyObject *module, int signum, PyObject *file, + int all_threads, int chain, + Py_ssize_t max_threads) +/*[clinic end generated code: output=d63a5b4f388dee5f input=c75096a20de502fe]*/ { - static char *kwlist[] = {"signum", "file", "all_threads", "chain", NULL}; - int signum; - PyObject *file = NULL; - int all_threads = 1; - int chain = 0; int fd; user_signal_t *user; _Py_sighandler_t previous; PyThreadState *tstate; int err; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "i|Opp:register", kwlist, - &signum, &file, &all_threads, &chain)) - return NULL; - if (!check_signum(signum)) return NULL; @@ -996,6 +1079,7 @@ faulthandler_register_py(PyObject *self, user->all_threads = all_threads; user->chain = chain; user->interp = PyThreadState_GetInterpreter(tstate); + user->max_threads = max_threads; user->enabled = 1; Py_RETURN_NONE; @@ -1017,16 +1101,23 @@ faulthandler_unregister(user_signal_t *user, int signum) return 1; } -static PyObject* -faulthandler_unregister_py(PyObject *self, PyObject *args) + +/*[clinic input] +faulthandler.unregister as faulthandler_unregister_py + + signum: int + / + +Unregister the handler of the signal 'signum' registered by register(). +[clinic start generated code]*/ + +static PyObject * +faulthandler_unregister_py_impl(PyObject *module, int signum) +/*[clinic end generated code: output=01734423da1155ed input=c016de014495d384]*/ { - int signum; user_signal_t *user; int change; - if (!PyArg_ParseTuple(args, "i:unregister", &signum)) - return NULL; - if (!check_signum(signum)) return NULL; @@ -1092,13 +1183,20 @@ faulthandler_raise_sigsegv(void) #endif } + +/*[clinic input] +faulthandler._sigsegv + + release_gil: bool = False + / + +Raise a SIGSEGV signal. +[clinic start generated code]*/ + static PyObject * -faulthandler_sigsegv(PyObject *self, PyObject *args) +faulthandler__sigsegv_impl(PyObject *module, int release_gil) +/*[clinic end generated code: output=96e5a2f215b01b76 input=c6ad893cf2ea2b41]*/ { - int release_gil = 0; - if (!PyArg_ParseTuple(args, "|i:_sigsegv", &release_gil)) - return NULL; - if (release_gil) { Py_BEGIN_ALLOW_THREADS faulthandler_raise_sigsegv(); @@ -1115,8 +1213,16 @@ faulthandler_fatal_error_thread(void *plock) Py_FatalError("in new thread"); } + +/*[clinic input] +faulthandler._fatal_error_c_thread + +Call Py_FatalError() in a new C thread. +[clinic start generated code]*/ + static PyObject * -faulthandler_fatal_error_c_thread(PyObject *self, PyObject *args) +faulthandler__fatal_error_c_thread_impl(PyObject *module) +/*[clinic end generated code: output=101bc8aaf4a5eec1 input=fbdca6fffd639a39]*/ { long tid; PyThread_type_lock lock; @@ -1145,16 +1251,32 @@ faulthandler_fatal_error_c_thread(PyObject *self, PyObject *args) Py_RETURN_NONE; } -static PyObject* -faulthandler_sigfpe(PyObject *self, PyObject *Py_UNUSED(dummy)) + +/*[clinic input] +faulthandler._sigfpe + +Raise a SIGFPE signal. +[clinic start generated code]*/ + +static PyObject * +faulthandler__sigfpe_impl(PyObject *module) +/*[clinic end generated code: output=dec9c98100e986db input=fd608a92d4421d28]*/ { faulthandler_suppress_crash_report(); raise(SIGFPE); Py_UNREACHABLE(); } + +/*[clinic input] +faulthandler._sigabrt + +Raise a SIGABRT signal. +[clinic start generated code]*/ + static PyObject * -faulthandler_sigabrt(PyObject *self, PyObject *args) +faulthandler__sigabrt_impl(PyObject *module) +/*[clinic end generated code: output=58c1378a0c166682 input=be3e0ecefb8676b8]*/ { faulthandler_suppress_crash_report(); abort(); @@ -1181,8 +1303,16 @@ stack_overflow(uintptr_t min_sp, uintptr_t max_sp, size_t *depth) return stack_overflow(min_sp, max_sp, depth); } + +/*[clinic input] +faulthandler._stack_overflow + +Recursive call to raise a stack overflow. +[clinic start generated code]*/ + static PyObject * -faulthandler_stack_overflow(PyObject *self, PyObject *Py_UNUSED(ignored)) +faulthandler__stack_overflow_impl(PyObject *module) +/*[clinic end generated code: output=efffba4be522d8fb input=4291594a790b6c35]*/ { size_t depth, size; uintptr_t sp = (uintptr_t)&depth; @@ -1219,27 +1349,22 @@ faulthandler_stack_overflow(PyObject *self, PyObject *Py_UNUSED(ignored)) #endif /* defined(FAULTHANDLER_USE_ALT_STACK) && defined(HAVE_SIGACTION) */ -static int -faulthandler_traverse(PyObject *module, visitproc visit, void *arg) -{ - Py_VISIT(thread.file); -#ifdef FAULTHANDLER_USER - if (user_signals != NULL) { - for (size_t signum=0; signum < Py_NSIG; signum++) - Py_VISIT(user_signals[signum].file); - } -#endif - Py_VISIT(fatal_error.file); - return 0; -} - #ifdef MS_WINDOWS +/*[clinic input] +faulthandler._raise_exception + + code: unsigned_int + flags: unsigned_int = 0 + / + +Call RaiseException(code, flags). +[clinic start generated code]*/ + static PyObject * -faulthandler_raise_exception(PyObject *self, PyObject *args) +faulthandler__raise_exception_impl(PyObject *module, unsigned int code, + unsigned int flags) +/*[clinic end generated code: output=2346cf318eab10dc input=43a5ba0eb7794504]*/ { - unsigned int code, flags = 0; - if (!PyArg_ParseTuple(args, "I|I:_raise_exception", &code, &flags)) - return NULL; faulthandler_suppress_crash_report(); RaiseException(code, flags, 0, NULL); Py_RETURN_NONE; @@ -1250,69 +1375,26 @@ PyDoc_STRVAR(module_doc, "faulthandler module."); static PyMethodDef module_methods[] = { - {"enable", - _PyCFunction_CAST(faulthandler_py_enable), METH_VARARGS|METH_KEYWORDS, - PyDoc_STR("enable($module, /, file=sys.stderr, all_threads=True)\n--\n\n" - "Enable the fault handler.")}, - {"disable", faulthandler_disable_py, METH_NOARGS, - PyDoc_STR("disable($module, /)\n--\n\n" - "Disable the fault handler.")}, - {"is_enabled", faulthandler_is_enabled, METH_NOARGS, - PyDoc_STR("is_enabled($module, /)\n--\n\n" - "Check if the handler is enabled.")}, - {"dump_traceback", - _PyCFunction_CAST(faulthandler_dump_traceback_py), METH_VARARGS|METH_KEYWORDS, - PyDoc_STR("dump_traceback($module, /, file=sys.stderr, all_threads=True)\n--\n\n" - "Dump the traceback of the current thread, or of all threads " - "if all_threads is True, into file.")}, - {"dump_c_stack", - _PyCFunction_CAST(faulthandler_dump_c_stack_py), METH_VARARGS|METH_KEYWORDS, - PyDoc_STR("dump_c_stack($module, /, file=sys.stderr)\n--\n\n" - "Dump the C stack of the current thread.")}, - {"dump_traceback_later", - _PyCFunction_CAST(faulthandler_dump_traceback_later), METH_VARARGS|METH_KEYWORDS, - PyDoc_STR("dump_traceback_later($module, /, timeout, repeat=False, file=sys.stderr, exit=False)\n--\n\n" - "Dump the traceback of all threads in timeout seconds,\n" - "or each timeout seconds if repeat is True. If exit is True, " - "call _exit(1) which is not safe.")}, - {"cancel_dump_traceback_later", - faulthandler_cancel_dump_traceback_later_py, METH_NOARGS, - PyDoc_STR("cancel_dump_traceback_later($module, /)\n--\n\n" - "Cancel the previous call to dump_traceback_later().")}, + FAULTHANDLER_PY_ENABLE_METHODDEF + FAULTHANDLER_DISABLE_PY_METHODDEF + FAULTHANDLER_IS_ENABLED_METHODDEF + FAULTHANDLER_DUMP_TRACEBACK_PY_METHODDEF + FAULTHANDLER_DUMP_C_STACK_PY_METHODDEF + FAULTHANDLER_DUMP_TRACEBACK_LATER_METHODDEF + FAULTHANDLER_CANCEL_DUMP_TRACEBACK_LATER_PY_METHODDEF #ifdef FAULTHANDLER_USER - {"register", - _PyCFunction_CAST(faulthandler_register_py), METH_VARARGS|METH_KEYWORDS, - PyDoc_STR("register($module, /, signum, file=sys.stderr, all_threads=True, chain=False)\n--\n\n" - "Register a handler for the signal 'signum': dump the " - "traceback of the current thread, or of all threads if " - "all_threads is True, into file.")}, - {"unregister", - _PyCFunction_CAST(faulthandler_unregister_py), METH_VARARGS, - PyDoc_STR("unregister($module, signum, /)\n--\n\n" - "Unregister the handler of the signal " - "'signum' registered by register().")}, + FAULTHANDLER_REGISTER_PY_METHODDEF + FAULTHANDLER_UNREGISTER_PY_METHODDEF #endif - {"_sigsegv", faulthandler_sigsegv, METH_VARARGS, - PyDoc_STR("_sigsegv($module, release_gil=False, /)\n--\n\n" - "Raise a SIGSEGV signal.")}, - {"_fatal_error_c_thread", faulthandler_fatal_error_c_thread, METH_NOARGS, - PyDoc_STR("_fatal_error_c_thread($module, /)\n--\n\n" - "Call Py_FatalError() in a new C thread.")}, - {"_sigabrt", faulthandler_sigabrt, METH_NOARGS, - PyDoc_STR("_sigabrt($module, /)\n--\n\n" - "Raise a SIGABRT signal.")}, - {"_sigfpe", faulthandler_sigfpe, METH_NOARGS, - PyDoc_STR("_sigfpe($module, /)\n--\n\n" - "Raise a SIGFPE signal.")}, + FAULTHANDLER__SIGSEGV_METHODDEF + FAULTHANDLER__FATAL_ERROR_C_THREAD_METHODDEF + FAULTHANDLER__SIGABRT_METHODDEF + FAULTHANDLER__SIGFPE_METHODDEF #ifdef FAULTHANDLER_STACK_OVERFLOW - {"_stack_overflow", faulthandler_stack_overflow, METH_NOARGS, - PyDoc_STR("_stack_overflow($module, /)\n--\n\n" - "Recursive call to raise a stack overflow.")}, + FAULTHANDLER__STACK_OVERFLOW_METHODDEF #endif #ifdef MS_WINDOWS - {"_raise_exception", faulthandler_raise_exception, METH_VARARGS, - PyDoc_STR("_raise_exception($module, code, flags=0, /)\n--\n\n" - "Call RaiseException(code, flags).")}, + FAULTHANDLER__RAISE_EXCEPTION_METHODDEF #endif {NULL, NULL} /* sentinel */ }; @@ -1322,26 +1404,26 @@ PyExec_faulthandler(PyObject *module) { /* Add constants for unit tests */ #ifdef MS_WINDOWS /* RaiseException() codes (prefixed by an underscore) */ - if (PyModule_AddIntConstant(module, "_EXCEPTION_ACCESS_VIOLATION", - EXCEPTION_ACCESS_VIOLATION)) { + if (PyModule_Add(module, "_EXCEPTION_ACCESS_VIOLATION", + PyLong_FromUnsignedLong(EXCEPTION_ACCESS_VIOLATION))) { return -1; } - if (PyModule_AddIntConstant(module, "_EXCEPTION_INT_DIVIDE_BY_ZERO", - EXCEPTION_INT_DIVIDE_BY_ZERO)) { + if (PyModule_Add(module, "_EXCEPTION_INT_DIVIDE_BY_ZERO", + PyLong_FromUnsignedLong(EXCEPTION_INT_DIVIDE_BY_ZERO))) { return -1; } - if (PyModule_AddIntConstant(module, "_EXCEPTION_STACK_OVERFLOW", - EXCEPTION_STACK_OVERFLOW)) { + if (PyModule_Add(module, "_EXCEPTION_STACK_OVERFLOW", + PyLong_FromUnsignedLong(EXCEPTION_STACK_OVERFLOW))) { return -1; } /* RaiseException() flags (prefixed by an underscore) */ - if (PyModule_AddIntConstant(module, "_EXCEPTION_NONCONTINUABLE", - EXCEPTION_NONCONTINUABLE)) { + if (PyModule_Add(module, "_EXCEPTION_NONCONTINUABLE", + PyLong_FromUnsignedLong(EXCEPTION_NONCONTINUABLE))) { return -1; } - if (PyModule_AddIntConstant(module, "_EXCEPTION_NONCONTINUABLE_EXCEPTION", - EXCEPTION_NONCONTINUABLE_EXCEPTION)) { + if (PyModule_Add(module, "_EXCEPTION_NONCONTINUABLE_EXCEPTION", + PyLong_FromUnsignedLong(EXCEPTION_NONCONTINUABLE_EXCEPTION))) { return -1; } #endif @@ -1349,6 +1431,7 @@ PyExec_faulthandler(PyObject *module) { } static PyModuleDef_Slot faulthandler_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, PyExec_faulthandler}, // XXX gh-103092: fix isolation. //{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, @@ -1361,7 +1444,6 @@ static struct PyModuleDef module_def = { .m_name = "faulthandler", .m_doc = module_doc, .m_methods = module_methods, - .m_traverse = faulthandler_traverse, .m_slots = faulthandler_slots }; diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index 524eb54b984ca8..e6a40ffc5a2614 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -1,9 +1,8 @@ /* fcntl module */ -// Need limited C API version 3.14 for PyLong_AsNativeBytes() in AC code -#include "pyconfig.h" // Py_GIL_DISABLED -#ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030e0000 +// Argument Clinic uses the internal C API +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 #endif #include "Python.h" @@ -41,22 +40,27 @@ fcntl.fcntl arg: object(c_default='NULL') = 0 / -Perform the operation `cmd` on file descriptor fd. - -The values used for `cmd` are operating system dependent, and are available -as constants in the fcntl module, using the same names as used in -the relevant C header files. The argument arg is optional, and -defaults to 0; it may be an int or a string. If arg is given as a string, -the return value of fcntl is a string of that length, containing the -resulting value put in the arg buffer by the operating system. The length -of the arg string is not allowed to exceed 1024 bytes. If the arg given -is an integer or if none is specified, the result value is an integer -corresponding to the return value of the fcntl call in the C code. +Perform the operation cmd on file descriptor fd. + +The values used for cmd are operating system dependent, and are +available as constants in the fcntl module, using the same names as used +in the relevant C header files. The argument arg is optional, and +defaults to 0; it may be an integer, a bytes-like object or a string. +If arg is given as a string, it will be encoded to binary using the +UTF-8 encoding. + +If the arg given is an integer or if none is specified, the result value +is an integer corresponding to the return value of the fcntl() call in +the C code. + +If arg is given as a bytes-like object, the return value of fcntl() is a +bytes object of that length, containing the resulting value put in the +arg buffer by the operating system. [clinic start generated code]*/ static PyObject * fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) -/*[clinic end generated code: output=888fc93b51c295bd input=7955340198e5f334]*/ +/*[clinic end generated code: output=888fc93b51c295bd input=77340720f11665da]*/ { int ret; int async_err = 0; @@ -107,18 +111,22 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL; } if (memcmp(buf + len, guard, GUARDSZ) != 0) { - PyErr_SetString(PyExc_SystemError, "buffer overflow"); + PyErr_SetString(PyExc_SystemError, + "Possible stack corruption in fcntl() due to " + "buffer overflow. " + "Provide an argument of sufficient size as " + "determined by the operation."); return NULL; } return PyBytes_FromStringAndSize(buf, len); } else { - PyObject *result = PyBytes_FromStringAndSize(NULL, len); - if (result == NULL) { + PyBytesWriter *writer = PyBytesWriter_Create(len); + if (writer == NULL) { PyBuffer_Release(&view); return NULL; } - char *ptr = PyBytes_AsString(result); + char *ptr = PyBytesWriter_GetData(writer); memcpy(ptr, view.buf, len); PyBuffer_Release(&view); @@ -131,15 +139,19 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) if (!async_err) { PyErr_SetFromErrno(PyExc_OSError); } - Py_DECREF(result); + PyBytesWriter_Discard(writer); return NULL; } if (ptr[len] != '\0') { - PyErr_SetString(PyExc_SystemError, "buffer overflow"); - Py_DECREF(result); + PyErr_SetString(PyExc_SystemError, + "Memory corruption in fcntl() due to " + "buffer overflow. " + "Provide an argument of sufficient size as " + "determined by the operation."); + PyBytesWriter_Discard(writer); return NULL; } - return result; + return PyBytesWriter_Finish(writer); } #undef FCNTL_BUFSZ } @@ -160,40 +172,39 @@ fcntl.ioctl mutate_flag as mutate_arg: bool = True / -Perform the operation `request` on file descriptor `fd`. +Perform the operation request on file descriptor fd. -The values used for `request` are operating system dependent, and are available -as constants in the fcntl or termios library modules, using the same names as -used in the relevant C header files. +The values used for request are operating system dependent, and are +available as constants in the fcntl or termios library modules, using +the same names as used in the relevant C header files. -The argument `arg` is optional, and defaults to 0; it may be an int or a -buffer containing character data (most likely a string or an array). +The argument arg is optional, and defaults to 0; it may be an integer, a +bytes-like object or a string. If arg is given as a string, it will be +encoded to binary using the UTF-8 encoding. -If the argument is a mutable buffer (such as an array) and if the -mutate_flag argument (which is only allowed in this case) is true then the -buffer is (in effect) passed to the operating system and changes made by -the OS will be reflected in the contents of the buffer after the call has -returned. The return value is the integer returned by the ioctl system -call. +If the arg given is an integer or if none is specified, the result value +is an integer corresponding to the return value of the ioctl() call in +the C code. -If the argument is a mutable buffer and the mutable_flag argument is false, -the behavior is as if a string had been passed. +If the argument is a mutable buffer (such as a bytearray) and the +mutate_flag argument is true (default) then the buffer is (in effect) +passed to the operating system and changes made by the OS will be +reflected in the contents of the buffer after the call has returned. +The return value is the integer returned by the ioctl() system call. -If the argument is an immutable buffer (most likely a string) then a copy -of the buffer is passed to the operating system and the return value is a -string of the same length containing whatever the operating system put in -the buffer. The length of the arg buffer in this case is not allowed to -exceed 1024 bytes. +If the argument is a mutable buffer and the mutable_flag argument is +false, the behavior is as if an immutable buffer had been passed. -If the arg given is an integer or if none is specified, the result value is -an integer corresponding to the return value of the ioctl call in the C -code. +If the argument is an immutable buffer then a copy of the buffer is +passed to the operating system and the return value is a bytes object of +the same length containing whatever the operating system put in the +buffer. [clinic start generated code]*/ static PyObject * fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg, int mutate_arg) -/*[clinic end generated code: output=f72baba2454d7a62 input=9c6cca5e2c339622]*/ +/*[clinic end generated code: output=f72baba2454d7a62 input=954fe75c208cc492]*/ { /* We use the unsigned non-checked 'I' format for the 'code' parameter because the system expects it to be a 32bit bit field value @@ -261,7 +272,12 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg, } PyBuffer_Release(&view); if (ptr == buf && memcmp(buf + len, guard, GUARDSZ) != 0) { - PyErr_SetString(PyExc_SystemError, "buffer overflow"); + PyErr_SetString(PyExc_SystemError, + "Possible stack corruption in ioctl() due to " + "buffer overflow. " + "Provide a writable buffer argument of " + "sufficient size as determined by " + "the operation."); return NULL; } return PyLong_FromLong(ret); @@ -290,18 +306,22 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg, return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL; } if (memcmp(buf + len, guard, GUARDSZ) != 0) { - PyErr_SetString(PyExc_SystemError, "buffer overflow"); + PyErr_SetString(PyExc_SystemError, + "Possible stack corruption in ioctl() due to " + "buffer overflow. " + "Provide an argument of sufficient size as " + "determined by the operation."); return NULL; } return PyBytes_FromStringAndSize(buf, len); } else { - PyObject *result = PyBytes_FromStringAndSize(NULL, len); - if (result == NULL) { + PyBytesWriter *writer = PyBytesWriter_Create(len); + if (writer == NULL) { PyBuffer_Release(&view); return NULL; } - char *ptr = PyBytes_AsString(result); + char *ptr = PyBytesWriter_GetData(writer); memcpy(ptr, view.buf, len); PyBuffer_Release(&view); @@ -314,15 +334,19 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg, if (!async_err) { PyErr_SetFromErrno(PyExc_OSError); } - Py_DECREF(result); + PyBytesWriter_Discard(writer); return NULL; } if (ptr[len] != '\0') { - PyErr_SetString(PyExc_SystemError, "buffer overflow"); - Py_DECREF(result); + PyErr_SetString(PyExc_SystemError, + "Memory corruption in ioctl() due to " + "buffer overflow. " + "Provide an argument of sufficient size as " + "determined by the operation."); + PyBytesWriter_Discard(writer); return NULL; } - return result; + return PyBytesWriter_Finish(writer); } #undef IOCTL_BUFSZ } @@ -340,7 +364,7 @@ fcntl.flock operation as code: int / -Perform the lock operation `operation` on file descriptor `fd`. +Perform the lock operation on file descriptor fd. See the Unix manual page for flock(2) for details (On some systems, this function is emulated using fcntl()). @@ -348,7 +372,7 @@ function is emulated using fcntl()). static PyObject * fcntl_flock_impl(PyObject *module, int fd, int code) -/*[clinic end generated code: output=84059e2b37d2fc64 input=0bfc00f795953452]*/ +/*[clinic end generated code: output=84059e2b37d2fc64 input=ade68943e8599f0a]*/ { int ret; int async_err = 0; @@ -411,22 +435,22 @@ fcntl.lockf A wrapper around the fcntl() locking calls. -`fd` is the file descriptor of the file to lock or unlock, and operation is one -of the following values: +fd is the file descriptor of the file to lock or unlock, and operation +is one of the following values: LOCK_UN - unlock LOCK_SH - acquire a shared lock LOCK_EX - acquire an exclusive lock When operation is LOCK_SH or LOCK_EX, it can also be bitwise ORed with -LOCK_NB to avoid blocking on lock acquisition. If LOCK_NB is used and the -lock cannot be acquired, an OSError will be raised and the exception will -have an errno attribute set to EACCES or EAGAIN (depending on the operating -system -- for portability, check for either value). +LOCK_NB to avoid blocking on lock acquisition. If LOCK_NB is used and +the lock cannot be acquired, an OSError will be raised and the exception +will have an errno attribute set to EACCES or EAGAIN (depending on the +operating system -- for portability, check for either value). -`len` is the number of bytes to lock, with the default meaning to lock to -EOF. `start` is the byte offset, relative to `whence`, to that the lock -starts. `whence` is as with fileobj.seek(), specifically: +len is the number of bytes to lock, with the default meaning to lock to +EOF. start is the byte offset, relative to whence, to that the lock +starts. whence is as with fileobj.seek(), specifically: 0 - relative to the start of the file (SEEK_SET) 1 - relative to the current buffer position (SEEK_CUR) @@ -436,7 +460,7 @@ starts. `whence` is as with fileobj.seek(), specifically: static PyObject * fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, PyObject *startobj, int whence) -/*[clinic end generated code: output=4985e7a172e7461a input=5480479fc63a04b8]*/ +/*[clinic end generated code: output=4985e7a172e7461a input=369bef4d7a1c5ff4]*/ { int ret; int async_err = 0; @@ -811,6 +835,7 @@ fcntl_exec(PyObject *module) } static PyModuleDef_Slot fcntl_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, fcntl_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/gc_weakref.txt b/Modules/gc_weakref.txt index f53fb99dd6cdcb..c3b8cc743ccd21 100644 --- a/Modules/gc_weakref.txt +++ b/Modules/gc_weakref.txt @@ -1,6 +1,16 @@ Intro ===== +************************************************************************** +Note: this document was written long ago, before PEP 442 (safe object +finalization) was implemented. While that has changed some things, this +document is still mostly accurate. Just note that the rules being discussed +here apply to the unreachable set of objects *after* non-legacy finalizers +have been called. Also, the clearing of weakrefs has been changed to happen +later in the collection (after running finalizers but before tp_clear is +called). +************************************************************************** + The basic rule for dealing with weakref callbacks (and __del__ methods too, for that matter) during cyclic gc: diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index ad13496b06deaf..0093995441e390 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -8,7 +8,6 @@ #include "pycore_gc.h" #include "pycore_object.h" // _PyObject_IS_GC() #include "pycore_pystate.h" // _PyInterpreterState_GET() -#include "pycore_tuple.h" // _PyTuple_FromArray() typedef struct _gc_runtime_state GCState; @@ -159,6 +158,17 @@ gc_set_threshold_impl(PyObject *module, int threshold0, int group_right_1, { GCState *gcstate = get_gc_state(); +#ifndef Py_GIL_DISABLED + gcstate->generations[0].threshold = threshold0; + if (group_right_1) { + gcstate->generations[1].threshold = threshold1; + } + if (group_right_2) { + gcstate->generations[2].threshold = threshold2; + } +#else + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); gcstate->young.threshold = threshold0; if (group_right_1) { gcstate->old[0].threshold = threshold1; @@ -166,6 +176,8 @@ gc_set_threshold_impl(PyObject *module, int threshold0, int group_right_1, if (group_right_2) { gcstate->old[1].threshold = threshold2; } + _PyEval_StartTheWorld(interp); +#endif Py_RETURN_NONE; } @@ -180,10 +192,17 @@ gc_get_threshold_impl(PyObject *module) /*[clinic end generated code: output=7902bc9f41ecbbd8 input=286d79918034d6e6]*/ { GCState *gcstate = get_gc_state(); +#ifndef Py_GIL_DISABLED + return Py_BuildValue("(iii)", + gcstate->generations[0].threshold, + gcstate->generations[1].threshold, + gcstate->generations[2].threshold); +#else return Py_BuildValue("(iii)", gcstate->young.threshold, gcstate->old[0].threshold, - 0); + gcstate->old[1].threshold); +#endif } /*[clinic input] @@ -207,10 +226,17 @@ gc_get_count_impl(PyObject *module) gc->alloc_count = 0; #endif +#ifndef Py_GIL_DISABLED + return Py_BuildValue("(iii)", + gcstate->generations[0].count, + gcstate->generations[1].count, + gcstate->generations[2].count); +#else return Py_BuildValue("(iii)", - gcstate->young.count, - gcstate->old[gcstate->visited_space].count, - gcstate->old[gcstate->visited_space^1].count); + _Py_atomic_load_int_relaxed(&gcstate->young.count), + gcstate->old[0].count, + gcstate->old[1].count); +#endif } /*[clinic input] @@ -296,19 +322,20 @@ gc_get_referents_impl(PyObject *module, PyObject *objs) } /*[clinic input] +@permit_long_summary gc.get_objects generation: Py_ssize_t(accept={int, NoneType}, c_default="-1") = None Generation to extract the objects from. Return a list of objects tracked by the collector (excluding the list returned). -If generation is not None, return only the objects tracked by the collector -that are in that generation. +If generation is not None, return only the objects tracked by the +collector that are in that generation. [clinic start generated code]*/ static PyObject * gc_get_objects_impl(PyObject *module, Py_ssize_t generation) -/*[clinic end generated code: output=48b35fea4ba6cb0e input=ef7da9df9806754c]*/ +/*[clinic end generated code: output=48b35fea4ba6cb0e input=89bca0d4a64e0135]*/ { if (PySys_Audit("gc.get_objects", "n", generation) < 0) { return NULL; @@ -347,9 +374,9 @@ gc_get_stats_impl(PyObject *module) /* To get consistent values despite allocations while constructing the result list, we use a snapshot of the running stats. */ GCState *gcstate = get_gc_state(); - for (i = 0; i < NUM_GENERATIONS; i++) { - stats[i] = gcstate->generation_stats[i]; - } + stats[0] = gcstate->generation_stats->young.items[gcstate->generation_stats->young.index]; + stats[1] = gcstate->generation_stats->old[0].items[gcstate->generation_stats->old[0].index]; + stats[2] = gcstate->generation_stats->old[1].items[gcstate->generation_stats->old[1].index]; PyObject *result = PyList_New(0); if (result == NULL) @@ -358,10 +385,12 @@ gc_get_stats_impl(PyObject *module) for (i = 0; i < NUM_GENERATIONS; i++) { PyObject *dict; st = &stats[i]; - dict = Py_BuildValue("{snsnsn}", + dict = Py_BuildValue("{snsnsnsnsd}", "collections", st->collections, "collected", st->collected, - "uncollectable", st->uncollectable + "uncollectable", st->uncollectable, + "candidates", st->candidates, + "duration", st->duration ); if (dict == NULL) goto error; @@ -414,18 +443,20 @@ gc_is_finalized_impl(PyObject *module, PyObject *obj) } /*[clinic input] +@permit_long_summary gc.freeze Freeze all current tracked objects and ignore them for future collections. -This can be used before a POSIX fork() call to make the gc copy-on-write friendly. -Note: collection before a POSIX fork() call may free pages for future allocation -which can cause copy-on-write. +This can be used before a POSIX fork() call to make the gc copy-on-write +friendly. +Note: collection before a POSIX fork() call may free pages for future +allocation which can cause copy-on-write. [clinic start generated code]*/ static PyObject * gc_freeze_impl(PyObject *module) -/*[clinic end generated code: output=502159d9cdc4c139 input=b602b16ac5febbe5]*/ +/*[clinic end generated code: output=502159d9cdc4c139 input=02674706fc9c0de6]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); _PyGC_Freeze(interp); @@ -476,7 +507,7 @@ PyDoc_STRVAR(gc__doc__, "set_debug() -- Set debugging flags.\n" "get_debug() -- Get debugging flags.\n" "set_threshold() -- Set the collection thresholds.\n" -"get_threshold() -- Return the current the collection thresholds.\n" +"get_threshold() -- Return the current collection thresholds.\n" "get_objects() -- Return a list of all objects tracked by the collector.\n" "is_tracked() -- Returns true if a given object is tracked.\n" "is_finalized() -- Returns true if a given object has been already finalized.\n" @@ -535,6 +566,7 @@ gcmodule_exec(PyObject *module) } static PyModuleDef_Slot gcmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, gcmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/getpath.py b/Modules/getpath.py index b89d7427e3febd..4dceb5cdc8dfcf 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -236,6 +236,7 @@ def search_up(prefix, *landmarks, test=isfile): real_executable_dir = None platstdlib_dir = None +stdlib_zip = None # ****************************************************************************** # CALCULATE program_name @@ -697,12 +698,13 @@ def search_up(prefix, *landmarks, test=isfile): library_dir = dirname(library) else: library_dir = executable_dir - pythonpath.append(joinpath(library_dir, ZIP_LANDMARK)) + stdlib_zip = joinpath(library_dir, ZIP_LANDMARK) elif build_prefix: # QUIRK: POSIX uses the default prefix when in the build directory - pythonpath.append(joinpath(PREFIX, ZIP_LANDMARK)) + stdlib_zip = joinpath(PREFIX, ZIP_LANDMARK) else: - pythonpath.append(joinpath(base_prefix, ZIP_LANDMARK)) + stdlib_zip = joinpath(base_prefix, ZIP_LANDMARK) + pythonpath.append(stdlib_zip) if os_name == 'nt' and use_environment and winreg: # QUIRK: Windows also lists paths in the registry. Paths are stored @@ -767,6 +769,23 @@ def search_up(prefix, *landmarks, test=isfile): config['module_search_paths_set'] = 1 +# ****************************************************************************** +# SANITY CHECKS +# ****************************************************************************** + +# Warn if the standard library is missing, unless pythonpath_was_set was set, as +# that skips parts of the stdlib directories calculation — assume the provided +# pythonpath is correct. This is how subinterpreters initialize the path for eg. +if not py_setpath and not pythonpath_was_set: + home_hint = f"The Python 'home' directory was set to {home!r}, is this correct?" + if (not stdlib_zip or not isfile(stdlib_zip)) and (not stdlib_dir or not isdir(stdlib_dir)): + hint = home_hint if home else f'sys.prefix is set to {prefix}, is this correct?' + warn('WARN: Could not find the standard library directory! ' + hint) + elif not platstdlib_dir or not isdir(platstdlib_dir): + hint = home_hint if home else f'sys.exec_prefix is set to {exec_prefix}, is this correct?' + warn('WARN: Could not find the platform standard library directory! ' + hint) + + # ****************************************************************************** # POSIX prefix/exec_prefix QUIRKS # ****************************************************************************** @@ -790,6 +809,7 @@ def search_up(prefix, *landmarks, test=isfile): config['isolated'] = 1 config['use_environment'] = 0 config['site_import'] = 0 + config['user_site_directory'] = 0 config['safe_path'] = 1 pythonpath = [] for line in pth: diff --git a/Modules/grpmodule.c b/Modules/grpmodule.c index 652958618a2c4c..32ead259803614 100644 --- a/Modules/grpmodule.c +++ b/Modules/grpmodule.c @@ -370,6 +370,7 @@ grpmodule_exec(PyObject *module) } static PyModuleDef_Slot grpmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, grpmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/hashlib.h b/Modules/hashlib.h index 5de5922c345047..5ada4ef4b863ec 100644 --- a/Modules/hashlib.h +++ b/Modules/hashlib.h @@ -12,34 +12,51 @@ #define HASHLIB_UNSUPPORTED_STR_ALGORITHM "unsupported hash algorithm %s" /* - * Given a PyObject* obj, fill in the Py_buffer* viewp with the result - * of PyObject_GetBuffer. Sets an exception and issues the erraction - * on any errors, e.g. 'return NULL' or 'goto error'. + * Obtain a buffer view from a buffer-like object 'obj'. + * + * On success, store the result in 'view' and return 0. + * On error, set an exception and return -1. */ -#define GET_BUFFER_VIEW_OR_ERROR(obj, viewp, erraction) do { \ - if (PyUnicode_Check((obj))) { \ - PyErr_SetString(PyExc_TypeError, \ - "Strings must be encoded before hashing");\ - erraction; \ - } \ - if (!PyObject_CheckBuffer((obj))) { \ - PyErr_SetString(PyExc_TypeError, \ - "object supporting the buffer API required"); \ - erraction; \ - } \ - if (PyObject_GetBuffer((obj), (viewp), PyBUF_SIMPLE) == -1) { \ - erraction; \ - } \ - if ((viewp)->ndim > 1) { \ - PyErr_SetString(PyExc_BufferError, \ - "Buffer must be single dimension"); \ - PyBuffer_Release((viewp)); \ - erraction; \ - } \ - } while(0) +static inline int +_Py_hashlib_get_buffer_view(PyObject *obj, Py_buffer *view) +{ + if (PyUnicode_Check(obj)) { + PyErr_SetString(PyExc_TypeError, + "Strings must be encoded before hashing"); + return -1; + } + if (!PyObject_CheckBuffer(obj)) { + PyErr_SetString(PyExc_TypeError, + "object supporting the buffer API required"); + return -1; + } + if (PyObject_GetBuffer(obj, view, PyBUF_SIMPLE) == -1) { + return -1; + } + if (view->ndim > 1) { + PyErr_SetString(PyExc_BufferError, + "Buffer must be single dimension"); + PyBuffer_Release(view); + return -1; + } + return 0; +} + +/* + * Call _Py_hashlib_get_buffer_view() and check if it succeeded. + * + * On error, set an exception and execute the ERRACTION statements. + */ +#define GET_BUFFER_VIEW_OR_ERROR(OBJ, VIEW, ERRACTION) \ + do { \ + if (_Py_hashlib_get_buffer_view(OBJ, VIEW) < 0) { \ + assert(PyErr_Occurred()); \ + ERRACTION; \ + } \ + } while (0) -#define GET_BUFFER_VIEW_OR_ERROUT(obj, viewp) \ - GET_BUFFER_VIEW_OR_ERROR(obj, viewp, return NULL) +#define GET_BUFFER_VIEW_OR_ERROUT(OBJ, VIEW) \ + GET_BUFFER_VIEW_OR_ERROR(OBJ, VIEW, return NULL) /* * Helper code to synchronize access to the hash object when the GIL is diff --git a/Modules/hmacmodule.c b/Modules/hmacmodule.c index b5405c99f1f8ce..0f9eca2f73bd0c 100644 --- a/Modules/hmacmodule.c +++ b/Modules/hmacmodule.c @@ -756,7 +756,7 @@ _hmac_new_impl(PyObject *module, PyObject *keyobj, PyObject *msgobj, return NULL; } - HMACObject *self = PyObject_GC_New(HMACObject, state->hmac_type); + HMACObject *self = PyObject_New(HMACObject, state->hmac_type); if (self == NULL) { return NULL; } @@ -791,7 +791,6 @@ _hmac_new_impl(PyObject *module, PyObject *keyobj, PyObject *msgobj, #endif } assert(rc == 0); - PyObject_GC_Track(self); return (PyObject *)self; error_on_key: @@ -852,7 +851,7 @@ _hmac_HMAC_copy_impl(HMACObject *self, PyTypeObject *cls) /*[clinic end generated code: output=a955bfa55b65b215 input=17b2c0ad0b147e36]*/ { hmacmodule_state *state = get_hmacmodule_state_by_cls(cls); - HMACObject *copy = PyObject_GC_New(HMACObject, state->hmac_type); + HMACObject *copy = PyObject_New(HMACObject, state->hmac_type); if (copy == NULL) { return NULL; } @@ -870,7 +869,6 @@ _hmac_HMAC_copy_impl(HMACObject *self, PyTypeObject *cls) } HASHLIB_INIT_MUTEX(copy); - PyObject_GC_Track(copy); return (PyObject *)copy; } @@ -943,19 +941,20 @@ _hmac_HMAC_digest_impl(HMACObject *self) } /*[clinic input] +@permit_long_summary _hmac.HMAC.hexdigest Return hexadecimal digest of the bytes passed to the update() method so far. -This may be used to exchange the value safely in email or other non-binary -environments. +This may be used to exchange the value safely in email or other +non-binary environments. This method may raise a MemoryError. [clinic start generated code]*/ static PyObject * _hmac_HMAC_hexdigest_impl(HMACObject *self) -/*[clinic end generated code: output=6659807a09ae14ec input=493b2db8013982b9]*/ +/*[clinic end generated code: output=6659807a09ae14ec input=9097dce732ed808f]*/ { assert(self->digest_size <= Py_hmac_hash_max_digest_size); uint8_t digest[Py_hmac_hash_max_digest_size]; @@ -1024,19 +1023,11 @@ static void HMACObject_dealloc(PyObject *op) { PyTypeObject *type = Py_TYPE(op); - PyObject_GC_UnTrack(op); (void)HMACObject_clear(op); type->tp_free(op); Py_DECREF(type); } -static int -HMACObject_traverse(PyObject *op, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(op)); - return 0; -} - static PyMethodDef HMACObject_methods[] = { _HMAC_HMAC_COPY_METHODDEF _HMAC_HMAC_UPDATE_METHODDEF @@ -1056,9 +1047,7 @@ static PyType_Slot HMACObject_Type_slots[] = { {Py_tp_repr, HMACObject_repr}, {Py_tp_methods, HMACObject_methods}, {Py_tp_getset, HMACObject_getsets}, - {Py_tp_clear, HMACObject_clear}, {Py_tp_dealloc, HMACObject_dealloc}, - {Py_tp_traverse, HMACObject_traverse}, {0, NULL} /* sentinel */ }; @@ -1068,8 +1057,7 @@ static PyType_Spec HMAC_Type_spec = { .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_HEAPTYPE - | Py_TPFLAGS_IMMUTABLETYPE - | Py_TPFLAGS_HAVE_GC, + | Py_TPFLAGS_IMMUTABLETYPE, .slots = HMACObject_Type_slots, }; @@ -1099,42 +1087,60 @@ _hmac_compute_digest_impl(PyObject *module, PyObject *key, PyObject *msg, } /* - * One-shot HMAC-HASH using the given HACL_HID. + * Obtain a view for 'key' and 'msg', storing it in 'keyview' and 'msgview'. + * + * Return 0 on success; otherwise set an exception and return -1. * * The length of the key and message buffers must not exceed UINT32_MAX, * lest an OverflowError is raised. The Python implementation takes care * of dispatching to the OpenSSL implementation in this case. */ -#define Py_HMAC_HACL_ONESHOT(HACL_HID, KEY, MSG) \ - do { \ - Py_buffer keyview, msgview; \ - GET_BUFFER_VIEW_OR_ERROUT((KEY), &keyview); \ - if (!has_uint32_t_buffer_length(&keyview)) { \ - PyBuffer_Release(&keyview); \ - set_invalid_key_length_error(); \ - return NULL; \ - } \ - GET_BUFFER_VIEW_OR_ERROR((MSG), &msgview, \ - PyBuffer_Release(&keyview); \ - return NULL); \ - if (!has_uint32_t_buffer_length(&msgview)) { \ - PyBuffer_Release(&msgview); \ - PyBuffer_Release(&keyview); \ - set_invalid_msg_length_error(); \ - return NULL; \ - } \ - uint8_t out[Py_hmac_## HACL_HID ##_digest_size]; \ - Py_hmac_## HACL_HID ##_compute_func( \ - out, \ - (uint8_t *)keyview.buf, (uint32_t)keyview.len, \ - (uint8_t *)msgview.buf, (uint32_t)msgview.len \ - ); \ - PyBuffer_Release(&msgview); \ - PyBuffer_Release(&keyview); \ - return PyBytes_FromStringAndSize( \ - (const char *)out, \ - Py_hmac_## HACL_HID ##_digest_size \ - ); \ +static int +hmac_get_buffer_views(PyObject *key, Py_buffer *keyview, + PyObject *msg, Py_buffer *msgview) +{ + if (_Py_hashlib_get_buffer_view(key, keyview) < 0) { + return -1; + } + if (!has_uint32_t_buffer_length(keyview)) { + PyBuffer_Release(keyview); + set_invalid_key_length_error(); + return -1; + } + if (_Py_hashlib_get_buffer_view(msg, msgview) < 0) { + PyBuffer_Release(keyview); + return -1; + } + if (!has_uint32_t_buffer_length(msgview)) { + PyBuffer_Release(msgview); + PyBuffer_Release(keyview); + set_invalid_msg_length_error(); + return -1; + } + return 0; +} + +/* + * One-shot HMAC-HASH using the given HACL_HID. + */ +#define HACL_HMAC_COMPUTE_NAMED_DIGEST(HACL_HID, KEY, MSG) \ + do { \ + Py_buffer keyview, msgview; \ + if (hmac_get_buffer_views(key, &keyview, msg, &msgview) < 0) { \ + return NULL; \ + } \ + uint8_t out[Py_hmac_## HACL_HID ##_digest_size]; \ + Py_hmac_## HACL_HID ##_compute_func( \ + out, \ + (uint8_t *)keyview.buf, (uint32_t)keyview.len, \ + (uint8_t *)msgview.buf, (uint32_t)msgview.len \ + ); \ + PyBuffer_Release(&msgview); \ + PyBuffer_Release(&keyview); \ + return PyBytes_FromStringAndSize( \ + (const char *)out, \ + Py_hmac_## HACL_HID ##_digest_size \ + ); \ } while (0) /*[clinic input] @@ -1150,7 +1156,7 @@ static PyObject * _hmac_compute_md5_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=7837a4ceccbbf636 input=77a4b774c7d61218]*/ { - Py_HMAC_HACL_ONESHOT(md5, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(md5, key, msg); } /*[clinic input] @@ -1166,7 +1172,7 @@ static PyObject * _hmac_compute_sha1_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=79fd7689c83691d8 input=3b64dccc6bdbe4ba]*/ { - Py_HMAC_HACL_ONESHOT(sha1, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(sha1, key, msg); } /*[clinic input] @@ -1182,7 +1188,7 @@ static PyObject * _hmac_compute_sha2_224_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=7f21f1613e53979e input=a1a75f25f23449af]*/ { - Py_HMAC_HACL_ONESHOT(sha2_224, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(sha2_224, key, msg); } /*[clinic input] @@ -1198,7 +1204,7 @@ static PyObject * _hmac_compute_sha2_256_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=d4a291f7d9a82459 input=5c9ccf2df048ace3]*/ { - Py_HMAC_HACL_ONESHOT(sha2_256, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(sha2_256, key, msg); } /*[clinic input] @@ -1214,7 +1220,7 @@ static PyObject * _hmac_compute_sha2_384_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=f211fa26e3700c27 input=2fee2c14766af231]*/ { - Py_HMAC_HACL_ONESHOT(sha2_384, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(sha2_384, key, msg); } /*[clinic input] @@ -1230,7 +1236,7 @@ static PyObject * _hmac_compute_sha2_512_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=d5c20373762cecca input=3371eaac315c7864]*/ { - Py_HMAC_HACL_ONESHOT(sha2_512, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(sha2_512, key, msg); } /*[clinic input] @@ -1246,7 +1252,7 @@ static PyObject * _hmac_compute_sha3_224_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=a242ccac9ad9c22b input=d0ab0c7d189c3d87]*/ { - Py_HMAC_HACL_ONESHOT(sha3_224, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(sha3_224, key, msg); } /*[clinic input] @@ -1262,7 +1268,7 @@ static PyObject * _hmac_compute_sha3_256_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=b539dbb61af2fe0b input=f05d7b6364b35d02]*/ { - Py_HMAC_HACL_ONESHOT(sha3_256, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(sha3_256, key, msg); } /*[clinic input] @@ -1278,7 +1284,7 @@ static PyObject * _hmac_compute_sha3_384_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=5eb372fb5c4ffd3a input=d842d393e7aa05ae]*/ { - Py_HMAC_HACL_ONESHOT(sha3_384, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(sha3_384, key, msg); } /*[clinic input] @@ -1294,7 +1300,7 @@ static PyObject * _hmac_compute_sha3_512_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=154bcbf8c2eacac1 input=166fe5baaeaabfde]*/ { - Py_HMAC_HACL_ONESHOT(sha3_512, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(sha3_512, key, msg); } /*[clinic input] @@ -1310,7 +1316,7 @@ static PyObject * _hmac_compute_blake2s_32_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=cfc730791bc62361 input=d22c36e7fe31a985]*/ { - Py_HMAC_HACL_ONESHOT(blake2s_32, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(blake2s_32, key, msg); } /*[clinic input] @@ -1326,9 +1332,11 @@ static PyObject * _hmac_compute_blake2b_32_impl(PyObject *module, PyObject *key, PyObject *msg) /*[clinic end generated code: output=765c5c4fb9124636 input=4a35ee058d172f4b]*/ { - Py_HMAC_HACL_ONESHOT(blake2b_32, key, msg); + HACL_HMAC_COMPUTE_NAMED_DIGEST(blake2b_32, key, msg); } +#undef HACL_HMAC_COMPUTE_NAMED_DIGEST + // --- HMAC module methods ---------------------------------------------------- static PyMethodDef hmacmodule_methods[] = { @@ -1369,7 +1377,6 @@ static void py_hmac_hinfo_ht_free(void *hinfo) { py_hmac_hinfo *entry = (py_hmac_hinfo *)hinfo; - assert(entry->display_name != NULL); if (--(entry->refcnt) == 0) { Py_CLEAR(entry->display_name); PyMem_Free(hinfo); @@ -1444,16 +1451,19 @@ py_hmac_hinfo_ht_new(void) assert(value->display_name == NULL); value->refcnt = 0; -#define Py_HMAC_HINFO_LINK(KEY) \ - do { \ - int rc = py_hmac_hinfo_ht_add(table, KEY, value); \ - if (rc < 0) { \ - PyMem_Free(value); \ - goto error; \ - } \ - else if (rc == 1) { \ - value->refcnt++; \ - } \ +#define Py_HMAC_HINFO_LINK(KEY) \ + do { \ + int rc = py_hmac_hinfo_ht_add(table, (KEY), value); \ + if (rc < 0) { \ + /* entry may already be in ht, freed upon exit */ \ + if (value->refcnt == 0) { \ + PyMem_Free(value); \ + } \ + goto error; \ + } \ + else if (rc == 1) { \ + value->refcnt++; \ + } \ } while (0) Py_HMAC_HINFO_LINK(e->name); Py_HMAC_HINFO_LINK(e->hashlib_name); @@ -1465,7 +1475,8 @@ py_hmac_hinfo_ht_new(void) e->hashlib_name == NULL ? e->name : e->hashlib_name ); if (value->display_name == NULL) { - PyMem_Free(value); + /* 'value' is owned by the table (refcnt > 0), + so _Py_hashtable_destroy() will free it. */ goto error; } } @@ -1678,6 +1689,7 @@ hmacmodule_free(void *mod) } static struct PyModuleDef_Slot hmacmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, hmacmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index cc1a558001563c..0dd31dfbc5a346 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -107,6 +107,7 @@ typedef struct { #define batchedobject_CAST(op) ((batchedobject *)(op)) /*[clinic input] +@permit_long_summary @classmethod itertools.batched.__new__ as batched_new iterable: object @@ -136,7 +137,7 @@ than n. static PyObject * batched_new_impl(PyTypeObject *type, PyObject *iterable, Py_ssize_t n, int strict) -/*[clinic end generated code: output=c6de11b061529d3e input=7814b47e222f5467]*/ +/*[clinic end generated code: output=c6de11b061529d3e input=b31d8be8e8577a34]*/ { PyObject *it; batchedobject *bo; @@ -310,7 +311,7 @@ pairwise_new_impl(PyTypeObject *type, PyObject *iterable) } po->it = it; po->old = NULL; - po->result = PyTuple_Pack(2, Py_None, Py_None); + po->result = _PyTuple_FromPairSteal(Py_None, Py_None); if (po->result == NULL) { Py_DECREF(po); return NULL; @@ -376,7 +377,7 @@ pairwise_next(PyObject *op) } result = po->result; - if (Py_REFCNT(result) == 1) { + if (_PyObject_IsUniquelyReferenced(result)) { Py_INCREF(result); PyObject *last_old = PyTuple_GET_ITEM(result, 0); PyObject *last_new = PyTuple_GET_ITEM(result, 1); @@ -389,11 +390,7 @@ pairwise_next(PyObject *op) _PyTuple_Recycle(result); } else { - result = PyTuple_New(2); - if (result != NULL) { - PyTuple_SET_ITEM(result, 0, Py_NewRef(old)); - PyTuple_SET_ITEM(result, 1, Py_NewRef(new)); - } + result = _PyTuple_FromPair(old, new); } Py_XSETREF(po->old, new); @@ -441,6 +438,7 @@ typedef struct { static PyObject *_grouper_create(groupbyobject *, PyObject *); /*[clinic input] +@permit_long_summary @classmethod itertools.groupby.__new__ @@ -456,7 +454,7 @@ make an iterator that returns consecutive keys and groups from the iterable static PyObject * itertools_groupby_impl(PyTypeObject *type, PyObject *it, PyObject *keyfunc) -/*[clinic end generated code: output=cbb1ae3a90fd4141 input=6b3d123e87ff65a1]*/ +/*[clinic end generated code: output=cbb1ae3a90fd4141 input=9f89fe625b20ef1a]*/ { groupbyobject *gbo; @@ -533,7 +531,7 @@ groupby_step(groupbyobject *gbo) static PyObject * groupby_next(PyObject *op) { - PyObject *r, *grouper; + PyObject *grouper; groupbyobject *gbo = groupbyobject_CAST(op); gbo->currgrouper = NULL; @@ -544,9 +542,19 @@ groupby_next(PyObject *op) else if (gbo->tgtkey == NULL) break; else { - int rcmp; + /* A user-defined __eq__ can re-enter groupby and advance the iterator, + mutating gbo->tgtkey / gbo->currkey while we are comparing them. + Take local snapshots and hold strong references so INCREF/DECREF + apply to the same objects even under re-entrancy. */ + PyObject *tgtkey = gbo->tgtkey; + PyObject *currkey = gbo->currkey; + + Py_INCREF(tgtkey); + Py_INCREF(currkey); + int rcmp = PyObject_RichCompareBool(tgtkey, currkey, Py_EQ); + Py_DECREF(tgtkey); + Py_DECREF(currkey); - rcmp = PyObject_RichCompareBool(gbo->tgtkey, gbo->currkey, Py_EQ); if (rcmp == -1) return NULL; else if (rcmp == 0) @@ -563,9 +571,7 @@ groupby_next(PyObject *op) if (grouper == NULL) return NULL; - r = PyTuple_Pack(2, gbo->currkey, grouper); - Py_DECREF(grouper); - return r; + return _PyTuple_FromPairSteal(Py_NewRef(gbo->currkey), grouper); } static PyType_Slot groupby_slots[] = { @@ -668,7 +674,16 @@ _grouper_next(PyObject *op) } assert(gbo->currkey != NULL); - rcmp = PyObject_RichCompareBool(igo->tgtkey, gbo->currkey, Py_EQ); + /* A user-defined __eq__ can re-enter the grouper and advance the iterator, + mutating gbo->currkey while we are comparing them. + Take local snapshots and hold strong references so INCREF/DECREF + apply to the same objects even under re-entrancy. */ + PyObject *tgtkey = Py_NewRef(igo->tgtkey); + PyObject *currkey = Py_NewRef(gbo->currkey); + rcmp = PyObject_RichCompareBool(tgtkey, currkey, Py_EQ); + Py_DECREF(tgtkey); + Py_DECREF(currkey); + if (rcmp <= 0) /* got any error or current group is end */ return NULL; @@ -802,7 +817,7 @@ teedataobject_traverse(PyObject *op, visitproc visit, void * arg) static void teedataobject_safe_decref(PyObject *obj) { - while (obj && Py_REFCNT(obj) == 1) { + while (obj && _PyObject_IsUniquelyReferenced(obj)) { teedataobject *tmp = teedataobject_CAST(obj); PyObject *nextlink = tmp->nextlink; tmp->nextlink = NULL; @@ -1069,22 +1084,18 @@ static PyType_Spec tee_spec = { /*[clinic input] itertools.tee iterable: object - n: Py_ssize_t = 2 + n: Py_ssize_t(allow_negative=False) = 2 / Returns a tuple of n independent iterators. [clinic start generated code]*/ static PyObject * itertools_tee_impl(PyObject *module, PyObject *iterable, Py_ssize_t n) -/*[clinic end generated code: output=1c64519cd859c2f0 input=c99a1472c425d66d]*/ +/*[clinic end generated code: output=1c64519cd859c2f0 input=0f72d78e655f45cb]*/ { Py_ssize_t i; PyObject *it, *to, *result; - if (n < 0) { - PyErr_SetString(PyExc_ValueError, "n must be >= 0"); - return NULL; - } result = PyTuple_New(n); if (result == NULL) return NULL; @@ -1129,6 +1140,7 @@ typedef struct { #define cycleobject_CAST(op) ((cycleobject *)(op)) /*[clinic input] +@permit_long_summary @classmethod itertools.cycle.__new__ iterable: object @@ -1138,7 +1150,7 @@ Return elements from the iterable until it is exhausted. Then repeat the sequenc static PyObject * itertools_cycle_impl(PyTypeObject *type, PyObject *iterable) -/*[clinic end generated code: output=f60e5ec17a45b35c input=9d1d84bcf66e908b]*/ +/*[clinic end generated code: output=f60e5ec17a45b35c input=ead392f4aac7afd8]*/ { PyObject *it; PyObject *saved; @@ -1386,6 +1398,7 @@ typedef struct { #define takewhileobject_CAST(op) ((takewhileobject *)(op)) /*[clinic input] +@permit_long_summary @classmethod itertools.takewhile.__new__ predicate as func: object @@ -1396,7 +1409,7 @@ Return successive entries from an iterable as long as the predicate evaluates to static PyObject * itertools_takewhile_impl(PyTypeObject *type, PyObject *func, PyObject *seq) -/*[clinic end generated code: output=bb179ea7864e2ef6 input=ba5255f7519aa119]*/ +/*[clinic end generated code: output=bb179ea7864e2ef6 input=61e42255dd0a7657]*/ { PyObject *it; takewhileobject *lz; @@ -1695,6 +1708,7 @@ typedef struct { #define starmapobject_CAST(op) ((starmapobject *)(op)) /*[clinic input] +@permit_long_summary @classmethod itertools.starmap.__new__ function as func: object @@ -1705,7 +1719,7 @@ Return an iterator whose values are returned from the function evaluated with an static PyObject * itertools_starmap_impl(PyTypeObject *type, PyObject *func, PyObject *seq) -/*[clinic end generated code: output=79eeb81d452c6e8d input=844766df6a0d4dad]*/ +/*[clinic end generated code: output=79eeb81d452c6e8d input=8c9068da0692d6d2]*/ { PyObject *it; starmapobject *lz; @@ -1838,6 +1852,7 @@ chain_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } /*[clinic input] +@permit_long_summary @classmethod itertools.chain.from_iterable iterable as arg: object @@ -1847,7 +1862,7 @@ Alternative chain() constructor taking a single iterable argument that evaluates static PyObject * itertools_chain_from_iterable_impl(PyTypeObject *type, PyObject *arg) -/*[clinic end generated code: output=3d7ea7d46b9e43f5 input=72c39e3a2ca3be85]*/ +/*[clinic end generated code: output=3d7ea7d46b9e43f5 input=a9bf8227221c75b3]*/ { PyObject *source; @@ -1937,10 +1952,14 @@ Return a chain object whose .__next__() method returns elements from the\n\ first iterable until it is exhausted, then elements from the next\n\ iterable, until all of the iterables are exhausted."); +PyDoc_STRVAR(chain_class_getitem_doc, +"chain is generic over the type of its contents.\n\ +This is the union of the types of the input iterable contents."); + static PyMethodDef chain_methods[] = { ITERTOOLS_CHAIN_FROM_ITERABLE_METHODDEF {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, chain_class_getitem_doc}, {NULL, NULL} /* sentinel */ }; @@ -2129,9 +2148,9 @@ product_next_lock_held(PyObject *op) Py_ssize_t *indices = lz->indices; /* Copy the previous result tuple or re-use it if available */ - if (Py_REFCNT(result) > 1) { + if (!_PyObject_IsUniquelyReferenced(result)) { PyObject *old_result = result; - result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), npools); + result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), npools); if (result == NULL) goto empty; lz->result = result; @@ -2251,7 +2270,7 @@ typedef struct { @classmethod itertools.combinations.__new__ iterable: object - r: Py_ssize_t + r: Py_ssize_t(allow_negative=False) Return successive r-length combinations of elements in the iterable. combinations(range(4), 3) --> (0,1,2), (0,1,3), (0,2,3), (1,2,3) @@ -2260,7 +2279,7 @@ combinations(range(4), 3) --> (0,1,2), (0,1,3), (0,2,3), (1,2,3) static PyObject * itertools_combinations_impl(PyTypeObject *type, PyObject *iterable, Py_ssize_t r) -/*[clinic end generated code: output=87a689b39c40039c input=06bede09e3da20f8]*/ +/*[clinic end generated code: output=87a689b39c40039c input=a32f07a15cfa4676]*/ { combinationsobject *co; Py_ssize_t n; @@ -2272,10 +2291,6 @@ itertools_combinations_impl(PyTypeObject *type, PyObject *iterable, if (pool == NULL) goto error; n = PyTuple_GET_SIZE(pool); - if (r < 0) { - PyErr_SetString(PyExc_ValueError, "r must be non-negative"); - goto error; - } indices = PyMem_New(Py_ssize_t, r); if (indices == NULL) { @@ -2368,9 +2383,9 @@ combinations_next_lock_held(PyObject *op) } } else { /* Copy the previous result tuple or re-use it if available */ - if (Py_REFCNT(result) > 1) { + if (!_PyObject_IsUniquelyReferenced(result)) { PyObject *old_result = result; - result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); + result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); if (result == NULL) goto empty; co->result = result; @@ -2501,10 +2516,12 @@ typedef struct { #define cwrobject_CAST(op) ((cwrobject *)(op)) /*[clinic input] +@permit_long_summary +@permit_long_docstring_body @classmethod itertools.combinations_with_replacement.__new__ iterable: object - r: Py_ssize_t + r: Py_ssize_t(allow_negative=False) Return successive r-length combinations of elements in the iterable allowing individual elements to have successive repeats. combinations_with_replacement('ABC', 2) --> ('A','A'), ('A','B'), ('A','C'), ('B','B'), ('B','C'), ('C','C') @@ -2514,7 +2531,7 @@ static PyObject * itertools_combinations_with_replacement_impl(PyTypeObject *type, PyObject *iterable, Py_ssize_t r) -/*[clinic end generated code: output=48b26856d4e659ca input=1dc58e82a0878fdc]*/ +/*[clinic end generated code: output=48b26856d4e659ca input=828696750169e84f]*/ { cwrobject *co; Py_ssize_t n; @@ -2526,10 +2543,6 @@ itertools_combinations_with_replacement_impl(PyTypeObject *type, if (pool == NULL) goto error; n = PyTuple_GET_SIZE(pool); - if (r < 0) { - PyErr_SetString(PyExc_ValueError, "r must be non-negative"); - goto error; - } indices = PyMem_New(Py_ssize_t, r); if (indices == NULL) { @@ -2593,7 +2606,7 @@ cwr_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -cwr_next(PyObject *op) +cwr_next_lock_held(PyObject *op) { cwrobject *co = cwrobject_CAST(op); PyObject *elem; @@ -2624,9 +2637,9 @@ cwr_next(PyObject *op) } } else { /* Copy the previous result tuple or re-use it if available */ - if (Py_REFCNT(result) > 1) { + if (!_PyObject_IsUniquelyReferenced(result)) { PyObject *old_result = result; - result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); + result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); if (result == NULL) goto empty; co->result = result; @@ -2672,6 +2685,16 @@ cwr_next(PyObject *op) return NULL; } +static PyObject * +cwr_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = cwr_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + static PyMethodDef cwr_methods[] = { {"__sizeof__", cwr_sizeof, METH_NOARGS, sizeof_doc}, {NULL, NULL} /* sentinel */ @@ -2852,7 +2875,7 @@ permutations_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -permutations_next(PyObject *op) +permutations_next_lock_held(PyObject *op) { permutationsobject *po = permutationsobject_CAST(op); PyObject *elem; @@ -2885,9 +2908,9 @@ permutations_next(PyObject *op) goto empty; /* Copy the previous result tuple or re-use it if available */ - if (Py_REFCNT(result) > 1) { + if (!_PyObject_IsUniquelyReferenced(result)) { PyObject *old_result = result; - result = _PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); + result = PyTuple_FromArray(_PyTuple_ITEMS(old_result), r); if (result == NULL) goto empty; po->result = result; @@ -2942,6 +2965,16 @@ permutations_next(PyObject *op) return NULL; } +static PyObject * +permutations_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = permutations_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + static PyMethodDef permuations_methods[] = { {"__sizeof__", permutations_sizeof, METH_NOARGS, sizeof_doc}, {NULL, NULL} /* sentinel */ @@ -3049,7 +3082,7 @@ accumulate_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -accumulate_next(PyObject *op) +accumulate_next_lock_held(PyObject *op) { accumulateobject *lz = accumulateobject_CAST(op); PyObject *val, *newtotal; @@ -3081,6 +3114,16 @@ accumulate_next(PyObject *op) return newtotal; } +static PyObject * +accumulate_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = accumulate_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + static PyType_Slot accumulate_slots[] = { {Py_tp_dealloc, accumulate_dealloc}, {Py_tp_getattro, PyObject_GenericGetAttr}, @@ -3126,13 +3169,13 @@ itertools.compress.__new__ selectors as seq2: object Return data elements corresponding to true selector elements. -Forms a shorter iterator from selected data elements using the selectors to -choose the data elements. +Forms a shorter iterator from selected data elements using the selectors +to choose the data elements. [clinic start generated code]*/ static PyObject * itertools_compress_impl(PyTypeObject *type, PyObject *seq1, PyObject *seq2) -/*[clinic end generated code: output=7e67157212ed09e0 input=79596d7cd20c77e5]*/ +/*[clinic end generated code: output=7e67157212ed09e0 input=32ca4347dbc46749]*/ { PyObject *data=NULL, *selectors=NULL; compressobject *lz; @@ -3390,6 +3433,7 @@ slow_mode: when cnt == PY_SSIZE_T_MAX, step is not int(1), or cnt is a float. */ /*[clinic input] +@permit_long_summary @classmethod itertools.count.__new__ start as long_cnt: object(c_default="NULL") = 0 @@ -3407,7 +3451,7 @@ Equivalent to: static PyObject * itertools_count_impl(PyTypeObject *type, PyObject *long_cnt, PyObject *long_step) -/*[clinic end generated code: output=09a9250aebd00b1c input=d7a85eec18bfcd94]*/ +/*[clinic end generated code: output=09a9250aebd00b1c input=91e4b12c0e88b9f4]*/ { countobject *lz; int fast_mode; @@ -3507,23 +3551,26 @@ count_traverse(PyObject *op, visitproc visit, void *arg) static PyObject * count_nextlong(countobject *lz) { - PyObject *long_cnt; - PyObject *stepped_up; - - long_cnt = lz->long_cnt; - if (long_cnt == NULL) { + if (lz->long_cnt == NULL) { /* Switch to slow_mode */ - long_cnt = PyLong_FromSsize_t(PY_SSIZE_T_MAX); - if (long_cnt == NULL) + lz->long_cnt = PyLong_FromSsize_t(PY_SSIZE_T_MAX); + if (lz->long_cnt == NULL) { return NULL; + } } - assert(lz->cnt == PY_SSIZE_T_MAX && long_cnt != NULL); + assert(lz->cnt == PY_SSIZE_T_MAX && lz->long_cnt != NULL); + + // We hold one reference to "result" (a.k.a. the old value of + // lz->long_cnt); we'll either return it or keep it in lz->long_cnt. + PyObject *result = lz->long_cnt; - stepped_up = PyNumber_Add(long_cnt, lz->long_step); - if (stepped_up == NULL) + PyObject *stepped_up = PyNumber_Add(result, lz->long_step); + if (stepped_up == NULL) { return NULL; + } lz->long_cnt = stepped_up; - return long_cnt; + + return result; } static PyObject * @@ -3839,7 +3886,7 @@ zip_longest_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -zip_longest_next(PyObject *op) +zip_longest_next_lock_held(PyObject *op) { ziplongestobject *lz = ziplongestobject_CAST(op); Py_ssize_t i; @@ -3853,7 +3900,7 @@ zip_longest_next(PyObject *op) return NULL; if (lz->numactive == 0) return NULL; - if (Py_REFCNT(result) == 1) { + if (_PyObject_IsUniquelyReferenced(result)) { Py_INCREF(result); for (i=0 ; i < tuplesize ; i++) { it = PyTuple_GET_ITEM(lz->ittuple, i); @@ -3910,6 +3957,16 @@ zip_longest_next(PyObject *op) return result; } +static PyObject * +zip_longest_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = zip_longest_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + PyDoc_STRVAR(zip_longest_doc, "zip_longest(*iterables, fillvalue=None)\n\ --\n\ @@ -4084,6 +4141,7 @@ itertoolsmodule_exec(PyObject *mod) } static struct PyModuleDef_Slot itertoolsmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, itertoolsmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/main.c b/Modules/main.c index 74e48c94732565..a4dfddd98e257e 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -11,6 +11,7 @@ #include "pycore_pylifecycle.h" // _Py_PreInitializeFromPyArgv() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_pythonrun.h" // _PyRun_AnyFileObject() +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_unicodeobject.h" // _PyUnicode_Dedent() /* Includes for exit_sigint() */ @@ -342,7 +343,7 @@ pymain_run_module(const wchar_t *modname, int set_argv0) Py_DECREF(runmodule); return pymain_exit_err_print(); } - runargs = PyTuple_Pack(2, module, set_argv0 ? Py_True : Py_False); + runargs = _PyTuple_FromPair(module, set_argv0 ? Py_True : Py_False); if (runargs == NULL) { fprintf(stderr, "Could not create arguments for runpy._run_module_as_main\n"); @@ -506,6 +507,7 @@ pymain_run_interactive_hook(int *exitcode) } if (PySys_Audit("cpython.run_interactivehook", "O", hook) < 0) { + Py_DECREF(hook); goto error; } @@ -561,13 +563,25 @@ pymain_run_stdin(PyConfig *config) return pymain_exit_err_print(); } - if (!isatty(fileno(stdin)) - || _Py_GetEnv(config->use_environment, "PYTHON_BASIC_REPL")) { - PyCompilerFlags cf = _PyCompilerFlags_INIT; - int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf); - return (run != 0); + int run; + if (isatty(fileno(stdin)) + && !_Py_GetEnv(config->use_environment, "PYTHON_BASIC_REPL")) { + PyObject *pyrepl = PyImport_ImportModule("_pyrepl"); + if (pyrepl != NULL) { + run = pymain_start_pyrepl(0); + Py_DECREF(pyrepl); + return run; + } + if (!PyErr_ExceptionMatches(PyExc_ModuleNotFoundError)) { + fprintf(stderr, "Could not import _pyrepl.main\n"); + return pymain_exit_err_print(); + } + PyErr_Clear(); } - return pymain_start_pyrepl(0); + + PyCompilerFlags cf = _PyCompilerFlags_INIT; + run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf); + return (run != 0); } @@ -593,14 +607,24 @@ pymain_repl(PyConfig *config, int *exitcode) return; } - if (!isatty(fileno(stdin)) - || _Py_GetEnv(config->use_environment, "PYTHON_BASIC_REPL")) { - PyCompilerFlags cf = _PyCompilerFlags_INIT; - int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf); - *exitcode = (run != 0); - return; + if (isatty(fileno(stdin)) + && !_Py_GetEnv(config->use_environment, "PYTHON_BASIC_REPL")) { + PyObject *pyrepl = PyImport_ImportModule("_pyrepl"); + if (pyrepl != NULL) { + int run = pymain_start_pyrepl(1); + *exitcode = (run != 0); + Py_DECREF(pyrepl); + return; + } + if (!PyErr_ExceptionMatches(PyExc_ModuleNotFoundError)) { + PyErr_Clear(); + fprintf(stderr, "Could not import _pyrepl.main\n"); + return; + } + PyErr_Clear(); } - int run = pymain_start_pyrepl(1); + PyCompilerFlags cf = _PyCompilerFlags_INIT; + int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf); *exitcode = (run != 0); return; } diff --git a/Modules/makesetup b/Modules/makesetup index f6cf695b457cbf..104c824b846540 100755 --- a/Modules/makesetup +++ b/Modules/makesetup @@ -90,6 +90,7 @@ NL='\ # Main loop for i in ${*-Setup} do + echo '' # Add a linebreak so we don't choke on files missing EOL. case $i in -n) echo '*noobjects*';; *) echo '*doconfig*'; cat "$i";; diff --git a/Modules/mathintegermodule.c b/Modules/mathintegermodule.c new file mode 100644 index 00000000000000..cfad4154b2d361 --- /dev/null +++ b/Modules/mathintegermodule.c @@ -0,0 +1,1294 @@ +/* math.integer module -- integer-related mathematical functions */ + +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + +#include "Python.h" +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_bitutils.h" // _Py_bit_length() +#include "pycore_long.h" // _PyLong_GetZero() + +#include "clinic/mathintegermodule.c.h" + +/*[clinic input] +module math +module math.integer +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e3d09c1c90de7fa8]*/ + + +/*[clinic input] +math.integer.gcd + + *integers as args: array + +Greatest Common Divisor. +[clinic start generated code]*/ + +static PyObject * +math_integer_gcd_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=8e9c5bab06bea203 input=a90cde2ac5281551]*/ +{ + // Fast-path for the common case: gcd(int, int) + if (args_length == 2 && PyLong_CheckExact(args[0]) && PyLong_CheckExact(args[1])) + { + return _PyLong_GCD(args[0], args[1]); + } + + if (args_length == 0) { + return PyLong_FromLong(0); + } + + PyObject *res = PyNumber_Index(args[0]); + if (res == NULL) { + return NULL; + } + if (args_length == 1) { + Py_SETREF(res, PyNumber_Absolute(res)); + return res; + } + + PyObject *one = _PyLong_GetOne(); // borrowed ref + for (Py_ssize_t i = 1; i < args_length; i++) { + PyObject *x = _PyNumber_Index(args[i]); + if (x == NULL) { + Py_DECREF(res); + return NULL; + } + if (res == one) { + /* Fast path: just check arguments. + It is okay to use identity comparison here. */ + Py_DECREF(x); + continue; + } + Py_SETREF(res, _PyLong_GCD(res, x)); + Py_DECREF(x); + if (res == NULL) { + return NULL; + } + } + return res; +} + + +static PyObject * +long_lcm(PyObject *a, PyObject *b) +{ + PyObject *g, *m, *f, *ab; + + if (_PyLong_IsZero((PyLongObject *)a) || _PyLong_IsZero((PyLongObject *)b)) { + return PyLong_FromLong(0); + } + g = _PyLong_GCD(a, b); + if (g == NULL) { + return NULL; + } + f = PyNumber_FloorDivide(a, g); + Py_DECREF(g); + if (f == NULL) { + return NULL; + } + m = PyNumber_Multiply(f, b); + Py_DECREF(f); + if (m == NULL) { + return NULL; + } + ab = PyNumber_Absolute(m); + Py_DECREF(m); + return ab; +} + + +/*[clinic input] +math.integer.lcm + + *integers as args: array + +Least Common Multiple. +[clinic start generated code]*/ + +static PyObject * +math_integer_lcm_impl(PyObject *module, PyObject * const *args, + Py_ssize_t args_length) +/*[clinic end generated code: output=3e88889b866ccc28 input=261bddc85a136bdf]*/ +{ + PyObject *res, *x; + Py_ssize_t i; + + if (args_length == 0) { + return PyLong_FromLong(1); + } + res = PyNumber_Index(args[0]); + if (res == NULL) { + return NULL; + } + if (args_length == 1) { + Py_SETREF(res, PyNumber_Absolute(res)); + return res; + } + + PyObject *zero = _PyLong_GetZero(); // borrowed ref + for (i = 1; i < args_length; i++) { + x = PyNumber_Index(args[i]); + if (x == NULL) { + Py_DECREF(res); + return NULL; + } + if (res == zero) { + /* Fast path: just check arguments. + It is okay to use identity comparison here. */ + Py_DECREF(x); + continue; + } + Py_SETREF(res, long_lcm(res, x)); + Py_DECREF(x); + if (res == NULL) { + return NULL; + } + } + return res; +} + + +/* Integer square root + +Given a nonnegative integer `n`, we want to compute the largest integer +`a` for which `a * a <= n`, or equivalently the integer part of the exact +square root of `n`. + +We use an adaptive-precision pure-integer version of Newton's iteration. Given +a positive integer `n`, the algorithm produces at each iteration an integer +approximation `a` to the square root of `n >> s` for some even integer `s`, +with `s` decreasing as the iterations progress. On the final iteration, `s` is +zero and we have an approximation to the square root of `n` itself. + +At every step, the approximation `a` is strictly within 1.0 of the true square +root, so we have + + (a - 1)**2 < (n >> s) < (a + 1)**2 + +After the final iteration, a check-and-correct step is needed to determine +whether `a` or `a - 1` gives the desired integer square root of `n`. + +The algorithm is remarkable in its simplicity. There's no need for a +per-iteration check-and-correct step, and termination is straightforward: the +number of iterations is known in advance (it's exactly `floor(log2(log2(n)))` +for `n > 1`). The only tricky part of the correctness proof is in establishing +that the bound `(a - 1)**2 < (n >> s) < (a + 1)**2` is maintained from one +iteration to the next. A sketch of the proof of this is given below. + +In addition to the proof sketch, a formal, computer-verified proof +of correctness (using Lean) of an equivalent recursive algorithm can be found +here: + + https://github.com/mdickinson/snippets/blob/master/proofs/isqrt/src/isqrt.lean + + +Here's Python code equivalent to the C implementation below: + + def isqrt(n): + """ + Return the integer part of the square root of the input. + """ + n = operator.index(n) + + if n < 0: + raise ValueError("isqrt() argument must be nonnegative") + if n == 0: + return 0 + + c = (n.bit_length() - 1) // 2 + a = 1 + d = 0 + for s in reversed(range(c.bit_length())): + # Loop invariant: (a-1)**2 < (n >> 2*(c - d)) < (a+1)**2 + e = d + d = c >> s + a = (a << d - e - 1) + (n >> 2*c - e - d + 1) // a + + return a - (a*a > n) + + +Sketch of proof of correctness +------------------------------ + +The delicate part of the correctness proof is showing that the loop invariant +is preserved from one iteration to the next. That is, just before the line + + a = (a << d - e - 1) + (n >> 2*c - e - d + 1) // a + +is executed in the above code, we know that + + (1) (a - 1)**2 < (n >> 2*(c - e)) < (a + 1)**2. + +(since `e` is always the value of `d` from the previous iteration). We must +prove that after that line is executed, we have + + (a - 1)**2 < (n >> 2*(c - d)) < (a + 1)**2 + +To facilitate the proof, we make some changes of notation. Write `m` for +`n >> 2*(c-d)`, and write `b` for the new value of `a`, so + + b = (a << d - e - 1) + (n >> 2*c - e - d + 1) // a + +or equivalently: + + (2) b = (a << d - e - 1) + (m >> d - e + 1) // a + +Then we can rewrite (1) as: + + (3) (a - 1)**2 < (m >> 2*(d - e)) < (a + 1)**2 + +and we must show that (b - 1)**2 < m < (b + 1)**2. + +From this point on, we switch to mathematical notation, so `/` means exact +division rather than integer division and `^` is used for exponentiation. We +use the `√` symbol for the exact square root. In (3), we can remove the +implicit floor operation to give: + + (4) (a - 1)^2 < m / 4^(d - e) < (a + 1)^2 + +Taking square roots throughout (4), scaling by `2^(d-e)`, and rearranging gives + + (5) 0 <= | 2^(d-e)a - √m | < 2^(d-e) + +Squaring and dividing through by `2^(d-e+1) a` gives + + (6) 0 <= 2^(d-e-1) a + m / (2^(d-e+1) a) - √m < 2^(d-e-1) / a + +We'll show below that `2^(d-e-1) <= a`. Given that, we can replace the +right-hand side of (6) with `1`, and now replacing the central +term `m / (2^(d-e+1) a)` with its floor in (6) gives + + (7) -1 < 2^(d-e-1) a + m // 2^(d-e+1) a - √m < 1 + +Or equivalently, from (2): + + (7) -1 < b - √m < 1 + +and rearranging gives that `(b-1)^2 < m < (b+1)^2`, which is what we needed +to prove. + +We're not quite done: we still have to prove the inequality `2^(d - e - 1) <= +a` that was used to get line (7) above. From the definition of `c`, we have +`4^c <= n`, which implies + + (8) 4^d <= m + +also, since `e == d >> 1`, `d` is at most `2e + 1`, from which it follows +that `2d - 2e - 1 <= d` and hence that + + (9) 4^(2d - 2e - 1) <= m + +Dividing both sides by `4^(d - e)` gives + + (10) 4^(d - e - 1) <= m / 4^(d - e) + +But we know from (4) that `m / 4^(d-e) < (a + 1)^2`, hence + + (11) 4^(d - e - 1) < (a + 1)^2 + +Now taking square roots of both sides and observing that both `2^(d-e-1)` and +`a` are integers gives `2^(d - e - 1) <= a`, which is what we needed. This +completes the proof sketch. + +*/ + +/* + The _approximate_isqrt_tab table provides approximate square roots for + 16-bit integers. For any n in the range 2**14 <= n < 2**16, the value + + a = _approximate_isqrt_tab[(n >> 8) - 64] + + is an approximate square root of n, satisfying (a - 1)**2 < n < (a + 1)**2. + + The table was computed in Python using the expression: + + [min(round(sqrt(256*n + 128)), 255) for n in range(64, 256)] +*/ + +static const uint8_t _approximate_isqrt_tab[192] = { + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150, + 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159, 160, + 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168, 169, + 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, + 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 186, 186, + 187, 188, 188, 189, 190, 190, 191, 192, 192, 193, 194, 194, + 195, 196, 196, 197, 198, 198, 199, 200, 200, 201, 201, 202, + 203, 203, 204, 205, 205, 206, 206, 207, 208, 208, 209, 210, + 210, 211, 211, 212, 213, 213, 214, 214, 215, 216, 216, 217, + 217, 218, 219, 219, 220, 220, 221, 221, 222, 223, 223, 224, + 224, 225, 225, 226, 227, 227, 228, 228, 229, 229, 230, 230, + 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 237, 237, + 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, + 244, 244, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, + 250, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255, 255, +}; + +/* Approximate square root of a large 64-bit integer. + + Given `n` satisfying `2**62 <= n < 2**64`, return `a` + satisfying `(a - 1)**2 < n < (a + 1)**2`. */ + +static inline uint32_t +_approximate_isqrt(uint64_t n) +{ + uint32_t u = _approximate_isqrt_tab[(n >> 56) - 64]; + u = (u << 7) + (uint32_t)(n >> 41) / u; + return (u << 15) + (uint32_t)((n >> 17) / u); +} + +/*[clinic input] +math.integer.isqrt + + n: object + / + +Return the integer part of the square root of the input. +[clinic start generated code]*/ + +static PyObject * +math_integer_isqrt(PyObject *module, PyObject *n) +/*[clinic end generated code: output=551031e41a0f5d9e input=921ddd9853133d8d]*/ +{ + int a_too_large, c_bit_length; + int64_t c, d; + uint64_t m; + uint32_t u; + PyObject *a = NULL, *b; + + n = _PyNumber_Index(n); + if (n == NULL) { + return NULL; + } + + if (_PyLong_IsNegative((PyLongObject *)n)) { + PyErr_SetString( + PyExc_ValueError, + "isqrt() argument must be nonnegative"); + goto error; + } + if (_PyLong_IsZero((PyLongObject *)n)) { + Py_DECREF(n); + return PyLong_FromLong(0); + } + + /* c = (n.bit_length() - 1) // 2 */ + c = _PyLong_NumBits(n); + assert(c > 0); + assert(!PyErr_Occurred()); + c = (c - 1) / 2; + + /* Fast path: if c <= 31 then n < 2**64 and we can compute directly with a + fast, almost branch-free algorithm. */ + if (c <= 31) { + int shift = 31 - (int)c; + m = (uint64_t)PyLong_AsUnsignedLongLong(n); + Py_DECREF(n); + if (m == (uint64_t)(-1) && PyErr_Occurred()) { + return NULL; + } + u = _approximate_isqrt(m << 2*shift) >> shift; + u -= (uint64_t)u * u > m; + return PyLong_FromUnsignedLong(u); + } + + /* Slow path: n >= 2**64. We perform the first five iterations in C integer + arithmetic, then switch to using Python long integers. */ + + /* From n >= 2**64 it follows that c.bit_length() >= 6. */ + c_bit_length = 6; + while ((c >> c_bit_length) > 0) { + ++c_bit_length; + } + + /* Initialise d and a. */ + d = c >> (c_bit_length - 5); + b = _PyLong_Rshift(n, 2*c - 62); + if (b == NULL) { + goto error; + } + m = (uint64_t)PyLong_AsUnsignedLongLong(b); + Py_DECREF(b); + if (m == (uint64_t)(-1) && PyErr_Occurred()) { + goto error; + } + u = _approximate_isqrt(m) >> (31U - d); + a = PyLong_FromUnsignedLong(u); + if (a == NULL) { + goto error; + } + + for (int s = c_bit_length - 6; s >= 0; --s) { + PyObject *q; + int64_t e = d; + + d = c >> s; + + /* q = (n >> 2*c - e - d + 1) // a */ + q = _PyLong_Rshift(n, 2*c - d - e + 1); + if (q == NULL) { + goto error; + } + Py_SETREF(q, PyNumber_FloorDivide(q, a)); + if (q == NULL) { + goto error; + } + + /* a = (a << d - 1 - e) + q */ + Py_SETREF(a, _PyLong_Lshift(a, d - 1 - e)); + if (a == NULL) { + Py_DECREF(q); + goto error; + } + Py_SETREF(a, PyNumber_Add(a, q)); + Py_DECREF(q); + if (a == NULL) { + goto error; + } + } + + /* The correct result is either a or a - 1. Figure out which, and + decrement a if necessary. */ + + /* a_too_large = n < a * a */ + b = PyNumber_Multiply(a, a); + if (b == NULL) { + goto error; + } + a_too_large = PyObject_RichCompareBool(n, b, Py_LT); + Py_DECREF(b); + if (a_too_large == -1) { + goto error; + } + + if (a_too_large) { + Py_SETREF(a, PyNumber_Subtract(a, _PyLong_GetOne())); + } + Py_DECREF(n); + return a; + + error: + Py_XDECREF(a); + Py_DECREF(n); + return NULL; +} + + +static unsigned long +count_set_bits(unsigned long n) +{ + unsigned long count = 0; + while (n != 0) { + ++count; + n &= n - 1; /* clear least significant bit */ + } + return count; +} + + +/* Divide-and-conquer factorial algorithm + * + * Based on the formula and pseudo-code provided at: + * http://www.luschny.de/math/factorial/binarysplitfact.html + * + * Faster algorithms exist, but they're more complicated and depend on + * a fast prime factorization algorithm. + * + * Notes on the algorithm + * ---------------------- + * + * factorial(n) is written in the form 2**k * m, with m odd. k and m are + * computed separately, and then combined using a left shift. + * + * The function factorial_odd_part computes the odd part m (i.e., the greatest + * odd divisor) of factorial(n), using the formula: + * + * factorial_odd_part(n) = + * + * product_{i >= 0} product_{0 < j <= n / 2**i, j odd} j + * + * Example: factorial_odd_part(20) = + * + * (1) * + * (1) * + * (1 * 3 * 5) * + * (1 * 3 * 5 * 7 * 9) * + * (1 * 3 * 5 * 7 * 9 * 11 * 13 * 15 * 17 * 19) + * + * Here i goes from large to small: the first term corresponds to i=4 (any + * larger i gives an empty product), and the last term corresponds to i=0. + * Each term can be computed from the last by multiplying by the extra odd + * numbers required: e.g., to get from the penultimate term to the last one, + * we multiply by (11 * 13 * 15 * 17 * 19). + * + * To see a hint of why this formula works, here are the same numbers as above + * but with the even parts (i.e., the appropriate powers of 2) included. For + * each subterm in the product for i, we multiply that subterm by 2**i: + * + * factorial(20) = + * + * (16) * + * (8) * + * (4 * 12 * 20) * + * (2 * 6 * 10 * 14 * 18) * + * (1 * 3 * 5 * 7 * 9 * 11 * 13 * 15 * 17 * 19) + * + * The factorial_partial_product function computes the product of all odd j in + * range(start, stop) for given start and stop. It's used to compute the + * partial products like (11 * 13 * 15 * 17 * 19) in the example above. It + * operates recursively, repeatedly splitting the range into two roughly equal + * pieces until the subranges are small enough to be computed using only C + * integer arithmetic. + * + * The two-valuation k (i.e., the exponent of the largest power of 2 dividing + * the factorial) is computed independently in the main math_integer_factorial + * function. By standard results, its value is: + * + * two_valuation = n//2 + n//4 + n//8 + .... + * + * It can be shown (e.g., by complete induction on n) that two_valuation is + * equal to n - count_set_bits(n), where count_set_bits(n) gives the number of + * '1'-bits in the binary expansion of n. + */ + +/* factorial_partial_product: Compute product(range(start, stop, 2)) using + * divide and conquer. Assumes start and stop are odd and stop > start. + * max_bits must be >= bit_length(stop - 2). */ + +static PyObject * +factorial_partial_product(unsigned long start, unsigned long stop, + unsigned long max_bits) +{ + unsigned long midpoint, num_operands; + PyObject *left = NULL, *right = NULL, *result = NULL; + + /* If the return value will fit an unsigned long, then we can + * multiply in a tight, fast loop where each multiply is O(1). + * Compute an upper bound on the number of bits required to store + * the answer. + * + * Storing some integer z requires floor(lg(z))+1 bits, which is + * conveniently the value returned by bit_length(z). The + * product x*y will require at most + * bit_length(x) + bit_length(y) bits to store, based + * on the idea that lg product = lg x + lg y. + * + * We know that stop - 2 is the largest number to be multiplied. From + * there, we have: bit_length(answer) <= num_operands * + * bit_length(stop - 2) + */ + + num_operands = (stop - start) / 2; + /* The "num_operands <= 8 * SIZEOF_LONG" check guards against the + * unlikely case of an overflow in num_operands * max_bits. */ + if (num_operands <= 8 * SIZEOF_LONG && + num_operands * max_bits <= 8 * SIZEOF_LONG) { + unsigned long j, total; + for (total = start, j = start + 2; j < stop; j += 2) + total *= j; + return PyLong_FromUnsignedLong(total); + } + + /* find midpoint of range(start, stop), rounded up to next odd number. */ + midpoint = (start + num_operands) | 1; + left = factorial_partial_product(start, midpoint, + _Py_bit_length(midpoint - 2)); + if (left == NULL) + goto error; + right = factorial_partial_product(midpoint, stop, max_bits); + if (right == NULL) + goto error; + result = PyNumber_Multiply(left, right); + + error: + Py_XDECREF(left); + Py_XDECREF(right); + return result; +} + +/* factorial_odd_part: compute the odd part of factorial(n). */ + +static PyObject * +factorial_odd_part(unsigned long n) +{ + long i; + unsigned long v, lower, upper; + PyObject *partial, *tmp, *inner, *outer; + + inner = PyLong_FromLong(1); + if (inner == NULL) + return NULL; + outer = Py_NewRef(inner); + + upper = 3; + for (i = _Py_bit_length(n) - 2; i >= 0; i--) { + v = n >> i; + if (v <= 2) + continue; + lower = upper; + /* (v + 1) | 1 = least odd integer strictly larger than n / 2**i */ + upper = (v + 1) | 1; + /* Here inner is the product of all odd integers j in the range (0, + n/2**(i+1)]. The factorial_partial_product call below gives the + product of all odd integers j in the range (n/2**(i+1), n/2**i]. */ + partial = factorial_partial_product(lower, upper, _Py_bit_length(upper-2)); + /* inner *= partial */ + if (partial == NULL) + goto error; + tmp = PyNumber_Multiply(inner, partial); + Py_DECREF(partial); + if (tmp == NULL) + goto error; + Py_SETREF(inner, tmp); + /* Now inner is the product of all odd integers j in the range (0, + n/2**i], giving the inner product in the formula above. */ + + /* outer *= inner; */ + tmp = PyNumber_Multiply(outer, inner); + if (tmp == NULL) + goto error; + Py_SETREF(outer, tmp); + } + Py_DECREF(inner); + return outer; + + error: + Py_DECREF(outer); + Py_DECREF(inner); + return NULL; +} + + +/* Lookup table for small factorial values */ + +static const unsigned long SmallFactorials[] = { + 1, 1, 2, 6, 24, 120, 720, 5040, 40320, + 362880, 3628800, 39916800, 479001600, +#if SIZEOF_LONG >= 8 + 6227020800, 87178291200, 1307674368000, + 20922789888000, 355687428096000, 6402373705728000, + 121645100408832000, 2432902008176640000 +#endif +}; + +/*[clinic input] +math.integer.factorial + + n as arg: object + / + +Find n!. +[clinic start generated code]*/ + +static PyObject * +math_integer_factorial(PyObject *module, PyObject *arg) +/*[clinic end generated code: output=131c23fd48650414 input=742f4dfa490a1b07]*/ +{ + long x, two_valuation; + int overflow; + PyObject *result, *odd_part; + + x = PyLong_AsLongAndOverflow(arg, &overflow); + if (x == -1 && PyErr_Occurred()) { + return NULL; + } + else if (overflow == 1) { + PyErr_Format(PyExc_OverflowError, + "factorial() argument should not exceed %ld", + LONG_MAX); + return NULL; + } + else if (overflow == -1 || x < 0) { + PyErr_SetString(PyExc_ValueError, + "factorial() not defined for negative values"); + return NULL; + } + + /* use lookup table if x is small */ + if (x < (long)Py_ARRAY_LENGTH(SmallFactorials)) + return PyLong_FromUnsignedLong(SmallFactorials[x]); + + /* else express in the form odd_part * 2**two_valuation, and compute as + odd_part << two_valuation. */ + odd_part = factorial_odd_part(x); + if (odd_part == NULL) + return NULL; + two_valuation = x - count_set_bits(x); + result = _PyLong_Lshift(odd_part, two_valuation); + Py_DECREF(odd_part); + return result; +} + + +/* least significant 64 bits of the odd part of factorial(n), for n in range(128). + +Python code to generate the values: + + import math.integer + + for n in range(128): + fac = math.integer.factorial(n) + fac_odd_part = fac // (fac & -fac) + reduced_fac_odd_part = fac_odd_part % (2**64) + print(f"{reduced_fac_odd_part:#018x}u") +*/ +static const uint64_t reduced_factorial_odd_part[] = { + 0x0000000000000001u, 0x0000000000000001u, 0x0000000000000001u, 0x0000000000000003u, + 0x0000000000000003u, 0x000000000000000fu, 0x000000000000002du, 0x000000000000013bu, + 0x000000000000013bu, 0x0000000000000b13u, 0x000000000000375fu, 0x0000000000026115u, + 0x000000000007233fu, 0x00000000005cca33u, 0x0000000002898765u, 0x00000000260eeeebu, + 0x00000000260eeeebu, 0x0000000286fddd9bu, 0x00000016beecca73u, 0x000001b02b930689u, + 0x00000870d9df20adu, 0x0000b141df4dae31u, 0x00079dd498567c1bu, 0x00af2e19afc5266du, + 0x020d8a4d0f4f7347u, 0x335281867ec241efu, 0x9b3093d46fdd5923u, 0x5e1f9767cc5866b1u, + 0x92dd23d6966aced7u, 0xa30d0f4f0a196e5bu, 0x8dc3e5a1977d7755u, 0x2ab8ce915831734bu, + 0x2ab8ce915831734bu, 0x81d2a0bc5e5fdcabu, 0x9efcac82445da75bu, 0xbc8b95cf58cde171u, + 0xa0e8444a1f3cecf9u, 0x4191deb683ce3ffdu, 0xddd3878bc84ebfc7u, 0xcb39a64b83ff3751u, + 0xf8203f7993fc1495u, 0xbd2a2a78b35f4bddu, 0x84757be6b6d13921u, 0x3fbbcfc0b524988bu, + 0xbd11ed47c8928df9u, 0x3c26b59e41c2f4c5u, 0x677a5137e883fdb3u, 0xff74e943b03b93ddu, + 0xfe5ebbcb10b2bb97u, 0xb021f1de3235e7e7u, 0x33509eb2e743a58fu, 0x390f9da41279fb7du, + 0xe5cb0154f031c559u, 0x93074695ba4ddb6du, 0x81c471caa636247fu, 0xe1347289b5a1d749u, + 0x286f21c3f76ce2ffu, 0x00be84a2173e8ac7u, 0x1595065ca215b88bu, 0xf95877595b018809u, + 0x9c2efe3c5516f887u, 0x373294604679382bu, 0xaf1ff7a888adcd35u, 0x18ddf279a2c5800bu, + 0x18ddf279a2c5800bu, 0x505a90e2542582cbu, 0x5bacad2cd8d5dc2bu, 0xfe3152bcbff89f41u, + 0xe1467e88bf829351u, 0xb8001adb9e31b4d5u, 0x2803ac06a0cbb91fu, 0x1904b5d698805799u, + 0xe12a648b5c831461u, 0x3516abbd6160cfa9u, 0xac46d25f12fe036du, 0x78bfa1da906b00efu, + 0xf6390338b7f111bdu, 0x0f25f80f538255d9u, 0x4ec8ca55b8db140fu, 0x4ff670740b9b30a1u, + 0x8fd032443a07f325u, 0x80dfe7965c83eeb5u, 0xa3dc1714d1213afdu, 0x205b7bbfcdc62007u, + 0xa78126bbe140a093u, 0x9de1dc61ca7550cfu, 0x84f0046d01b492c5u, 0x2d91810b945de0f3u, + 0xf5408b7f6008aa71u, 0x43707f4863034149u, 0xdac65fb9679279d5u, 0xc48406e7d1114eb7u, + 0xa7dc9ed3c88e1271u, 0xfb25b2efdb9cb30du, 0x1bebda0951c4df63u, 0x5c85e975580ee5bdu, + 0x1591bc60082cb137u, 0x2c38606318ef25d7u, 0x76ca72f7c5c63e27u, 0xf04a75d17baa0915u, + 0x77458175139ae30du, 0x0e6c1330bc1b9421u, 0xdf87d2b5797e8293u, 0xefa5c703e1e68925u, + 0x2b6b1b3278b4f6e1u, 0xceee27b382394249u, 0xd74e3829f5dab91du, 0xfdb17989c26b5f1fu, + 0xc1b7d18781530845u, 0x7b4436b2105a8561u, 0x7ba7c0418372a7d7u, 0x9dbc5c67feb6c639u, + 0x502686d7f6ff6b8fu, 0x6101855406be7a1fu, 0x9956afb5806930e7u, 0xe1f0ee88af40f7c5u, + 0x984b057bda5c1151u, 0x9a49819acc13ea05u, 0x8ef0dead0896ef27u, 0x71f7826efe292b21u, + 0xad80a480e46986efu, 0x01cdc0ebf5e0c6f7u, 0x6e06f839968f68dbu, 0xdd5943ab56e76139u, + 0xcdcf31bf8604c5e7u, 0x7e2b4a847054a1cbu, 0x0ca75697a4d3d0f5u, 0x4703f53ac514a98bu, +}; + +/* inverses of reduced_factorial_odd_part values modulo 2**64. + +Python code to generate the values: + + import math.integer + + for n in range(128): + fac = math.integer.factorial(n) + fac_odd_part = fac // (fac & -fac) + inverted_fac_odd_part = pow(fac_odd_part, -1, 2**64) + print(f"{inverted_fac_odd_part:#018x}u") +*/ +static const uint64_t inverted_factorial_odd_part[] = { + 0x0000000000000001u, 0x0000000000000001u, 0x0000000000000001u, 0xaaaaaaaaaaaaaaabu, + 0xaaaaaaaaaaaaaaabu, 0xeeeeeeeeeeeeeeefu, 0x4fa4fa4fa4fa4fa5u, 0x2ff2ff2ff2ff2ff3u, + 0x2ff2ff2ff2ff2ff3u, 0x938cc70553e3771bu, 0xb71c27cddd93e49fu, 0xb38e3229fcdee63du, + 0xe684bb63544a4cbfu, 0xc2f684917ca340fbu, 0xf747c9cba417526du, 0xbb26eb51d7bd49c3u, + 0xbb26eb51d7bd49c3u, 0xb0a7efb985294093u, 0xbe4b8c69f259eabbu, 0x6854d17ed6dc4fb9u, + 0xe1aa904c915f4325u, 0x3b8206df131cead1u, 0x79c6009fea76fe13u, 0xd8c5d381633cd365u, + 0x4841f12b21144677u, 0x4a91ff68200b0d0fu, 0x8f9513a58c4f9e8bu, 0x2b3e690621a42251u, + 0x4f520f00e03c04e7u, 0x2edf84ee600211d3u, 0xadcaa2764aaacdfdu, 0x161f4f9033f4fe63u, + 0x161f4f9033f4fe63u, 0xbada2932ea4d3e03u, 0xcec189f3efaa30d3u, 0xf7475bb68330bf91u, + 0x37eb7bf7d5b01549u, 0x46b35660a4e91555u, 0xa567c12d81f151f7u, 0x4c724007bb2071b1u, + 0x0f4a0cce58a016bdu, 0xfa21068e66106475u, 0x244ab72b5a318ae1u, 0x366ce67e080d0f23u, + 0xd666fdae5dd2a449u, 0xd740ddd0acc06a0du, 0xb050bbbb28e6f97bu, 0x70b003fe890a5c75u, + 0xd03aabff83037427u, 0x13ec4ca72c783bd7u, 0x90282c06afdbd96fu, 0x4414ddb9db4a95d5u, + 0xa2c68735ae6832e9u, 0xbf72d71455676665u, 0xa8469fab6b759b7fu, 0xc1e55b56e606caf9u, + 0x40455630fc4a1cffu, 0x0120a7b0046d16f7u, 0xa7c3553b08faef23u, 0x9f0bfd1b08d48639u, + 0xa433ffce9a304d37u, 0xa22ad1d53915c683u, 0xcb6cbc723ba5dd1du, 0x547fb1b8ab9d0ba3u, + 0x547fb1b8ab9d0ba3u, 0x8f15a826498852e3u, 0x32e1a03f38880283u, 0x3de4cce63283f0c1u, + 0x5dfe6667e4da95b1u, 0xfda6eeeef479e47du, 0xf14de991cc7882dfu, 0xe68db79247630ca9u, + 0xa7d6db8207ee8fa1u, 0x255e1f0fcf034499u, 0xc9a8990e43dd7e65u, 0x3279b6f289702e0fu, + 0xe7b5905d9b71b195u, 0x03025ba41ff0da69u, 0xb7df3d6d3be55aefu, 0xf89b212ebff2b361u, + 0xfe856d095996f0adu, 0xd6e533e9fdf20f9du, 0xf8c0e84a63da3255u, 0xa677876cd91b4db7u, + 0x07ed4f97780d7d9bu, 0x90a8705f258db62fu, 0xa41bbb2be31b1c0du, 0x6ec28690b038383bu, + 0xdb860c3bb2edd691u, 0x0838286838a980f9u, 0x558417a74b36f77du, 0x71779afc3646ef07u, + 0x743cda377ccb6e91u, 0x7fdf9f3fe89153c5u, 0xdc97d25df49b9a4bu, 0x76321a778eb37d95u, + 0x7cbb5e27da3bd487u, 0x9cff4ade1a009de7u, 0x70eb166d05c15197u, 0xdcf0460b71d5fe3du, + 0x5ac1ee5260b6a3c5u, 0xc922dedfdd78efe1u, 0xe5d381dc3b8eeb9bu, 0xd57e5347bafc6aadu, + 0x86939040983acd21u, 0x395b9d69740a4ff9u, 0x1467299c8e43d135u, 0x5fe440fcad975cdfu, + 0xcaa9a39794a6ca8du, 0xf61dbd640868dea1u, 0xac09d98d74843be7u, 0x2b103b9e1a6b4809u, + 0x2ab92d16960f536fu, 0x6653323d5e3681dfu, 0xefd48c1c0624e2d7u, 0xa496fefe04816f0du, + 0x1754a7b07bbdd7b1u, 0x23353c829a3852cdu, 0xbf831261abd59097u, 0x57a8e656df0618e1u, + 0x16e9206c3100680fu, 0xadad4c6ee921dac7u, 0x635f2b3860265353u, 0xdd6d0059f44b3d09u, + 0xac4dd6b894447dd7u, 0x42ea183eeaa87be3u, 0x15612d1550ee5b5du, 0x226fa19d656cb623u, +}; + +/* exponent of the largest power of 2 dividing factorial(n), for n in range(68) + +Python code to generate the values: + +import math.integer + +for n in range(128): + fac = math.integer.factorial(n) + fac_trailing_zeros = (fac & -fac).bit_length() - 1 + print(fac_trailing_zeros) +*/ + +static const uint8_t factorial_trailing_zeros[] = { + 0, 0, 1, 1, 3, 3, 4, 4, 7, 7, 8, 8, 10, 10, 11, 11, // 0-15 + 15, 15, 16, 16, 18, 18, 19, 19, 22, 22, 23, 23, 25, 25, 26, 26, // 16-31 + 31, 31, 32, 32, 34, 34, 35, 35, 38, 38, 39, 39, 41, 41, 42, 42, // 32-47 + 46, 46, 47, 47, 49, 49, 50, 50, 53, 53, 54, 54, 56, 56, 57, 57, // 48-63 + 63, 63, 64, 64, 66, 66, 67, 67, 70, 70, 71, 71, 73, 73, 74, 74, // 64-79 + 78, 78, 79, 79, 81, 81, 82, 82, 85, 85, 86, 86, 88, 88, 89, 89, // 80-95 + 94, 94, 95, 95, 97, 97, 98, 98, 101, 101, 102, 102, 104, 104, 105, 105, // 96-111 + 109, 109, 110, 110, 112, 112, 113, 113, 116, 116, 117, 117, 119, 119, 120, 120, // 112-127 +}; + +/* Number of permutations and combinations. + * P(n, k) = n! / (n-k)! + * C(n, k) = P(n, k) / k! + */ + +/* Calculate C(n, k) for n in the 63-bit range. */ +static PyObject * +perm_comb_small(unsigned long long n, unsigned long long k, int iscomb) +{ + assert(k != 0); + + /* For small enough n and k the result fits in the 64-bit range and can + * be calculated without allocating intermediate PyLong objects. */ + if (iscomb) { + /* Maps k to the maximal n so that 2*k-1 <= n <= 127 and C(n, k) + * fits into a uint64_t. Exclude k = 1, because the second fast + * path is faster for this case.*/ + static const unsigned char fast_comb_limits1[] = { + 0, 0, 127, 127, 127, 127, 127, 127, // 0-7 + 127, 127, 127, 127, 127, 127, 127, 127, // 8-15 + 116, 105, 97, 91, 86, 82, 78, 76, // 16-23 + 74, 72, 71, 70, 69, 68, 68, 67, // 24-31 + 67, 67, 67, // 32-34 + }; + if (k < Py_ARRAY_LENGTH(fast_comb_limits1) && n <= fast_comb_limits1[k]) { + /* + comb(n, k) fits into a uint64_t. We compute it as + + comb_odd_part << shift + + where 2**shift is the largest power of two dividing comb(n, k) + and comb_odd_part is comb(n, k) >> shift. comb_odd_part can be + calculated efficiently via arithmetic modulo 2**64, using three + lookups and two uint64_t multiplications. + */ + uint64_t comb_odd_part = reduced_factorial_odd_part[n] + * inverted_factorial_odd_part[k] + * inverted_factorial_odd_part[n - k]; + int shift = factorial_trailing_zeros[n] + - factorial_trailing_zeros[k] + - factorial_trailing_zeros[n - k]; + return PyLong_FromUnsignedLongLong(comb_odd_part << shift); + } + + /* Maps k to the maximal n so that 2*k-1 <= n <= 127 and C(n, k)*k + * fits into a long long (which is at least 64 bit). Only contains + * items larger than in fast_comb_limits1. */ + static const unsigned long long fast_comb_limits2[] = { + 0, ULLONG_MAX, 4294967296ULL, 3329022, 102570, 13467, 3612, 1449, // 0-7 + 746, 453, 308, 227, 178, 147, // 8-13 + }; + if (k < Py_ARRAY_LENGTH(fast_comb_limits2) && n <= fast_comb_limits2[k]) { + /* C(n, k) = C(n, k-1) * (n-k+1) / k */ + unsigned long long result = n; + for (unsigned long long i = 1; i < k;) { + result *= --n; + result /= ++i; + } + return PyLong_FromUnsignedLongLong(result); + } + } + else { + /* Maps k to the maximal n so that k <= n and P(n, k) + * fits into a long long (which is at least 64 bit). */ + static const unsigned long long fast_perm_limits[] = { + 0, ULLONG_MAX, 4294967296ULL, 2642246, 65537, 7133, 1627, 568, // 0-7 + 259, 142, 88, 61, 45, 36, 30, 26, // 8-15 + 24, 22, 21, 20, 20, // 16-20 + }; + if (k < Py_ARRAY_LENGTH(fast_perm_limits) && n <= fast_perm_limits[k]) { + if (n <= 127) { + /* P(n, k) fits into a uint64_t. */ + uint64_t perm_odd_part = reduced_factorial_odd_part[n] + * inverted_factorial_odd_part[n - k]; + int shift = factorial_trailing_zeros[n] + - factorial_trailing_zeros[n - k]; + return PyLong_FromUnsignedLongLong(perm_odd_part << shift); + } + + /* P(n, k) = P(n, k-1) * (n-k+1) */ + unsigned long long result = n; + for (unsigned long long i = 1; i < k;) { + result *= --n; + ++i; + } + return PyLong_FromUnsignedLongLong(result); + } + } + + /* For larger n use recursive formulas: + * + * P(n, k) = P(n, j) * P(n-j, k-j) + * C(n, k) = C(n, j) * C(n-j, k-j) // C(k, j) + */ + unsigned long long j = k / 2; + PyObject *a, *b; + a = perm_comb_small(n, j, iscomb); + if (a == NULL) { + return NULL; + } + b = perm_comb_small(n - j, k - j, iscomb); + if (b == NULL) { + goto error; + } + Py_SETREF(a, PyNumber_Multiply(a, b)); + Py_DECREF(b); + if (iscomb && a != NULL) { + b = perm_comb_small(k, j, 1); + if (b == NULL) { + goto error; + } + Py_SETREF(a, PyNumber_FloorDivide(a, b)); + Py_DECREF(b); + } + return a; + +error: + Py_DECREF(a); + return NULL; +} + +/* Calculate P(n, k) or C(n, k) using recursive formulas. + * It is more efficient than sequential multiplication thanks to + * Karatsuba multiplication. + */ +static PyObject * +perm_comb(PyObject *n, unsigned long long k, int iscomb) +{ + if (k == 0) { + return PyLong_FromLong(1); + } + if (k == 1) { + return Py_NewRef(n); + } + + /* P(n, k) = P(n, j) * P(n-j, k-j) */ + /* C(n, k) = C(n, j) * C(n-j, k-j) // C(k, j) */ + unsigned long long j = k / 2; + PyObject *a, *b; + a = perm_comb(n, j, iscomb); + if (a == NULL) { + return NULL; + } + PyObject *t = PyLong_FromUnsignedLongLong(j); + if (t == NULL) { + goto error; + } + n = PyNumber_Subtract(n, t); + Py_DECREF(t); + if (n == NULL) { + goto error; + } + b = perm_comb(n, k - j, iscomb); + Py_DECREF(n); + if (b == NULL) { + goto error; + } + Py_SETREF(a, PyNumber_Multiply(a, b)); + Py_DECREF(b); + if (iscomb && a != NULL) { + b = perm_comb_small(k, j, 1); + if (b == NULL) { + goto error; + } + Py_SETREF(a, PyNumber_FloorDivide(a, b)); + Py_DECREF(b); + } + return a; + +error: + Py_DECREF(a); + return NULL; +} + +/*[clinic input] +@permit_long_summary +math.integer.perm + + n: object + k: object = None + / + +Number of ways to choose k items from n items without repetition and with order. + +Evaluates to n! / (n - k)! when k <= n and evaluates +to zero when k > n. + +If k is not specified or is None, then k defaults to n +and the function returns n!. + +Raises ValueError if either of the arguments are negative. +[clinic start generated code]*/ + +static PyObject * +math_integer_perm_impl(PyObject *module, PyObject *n, PyObject *k) +/*[clinic end generated code: output=9f9b96cd73a94de4 input=fd627e5a09dd5116]*/ +{ + PyObject *result = NULL; + int overflow, cmp; + long long ki, ni; + + if (k == Py_None) { + return math_integer_factorial(module, n); + } + n = PyNumber_Index(n); + if (n == NULL) { + return NULL; + } + k = PyNumber_Index(k); + if (k == NULL) { + Py_DECREF(n); + return NULL; + } + assert(PyLong_CheckExact(n) && PyLong_CheckExact(k)); + + if (_PyLong_IsNegative((PyLongObject *)n)) { + PyErr_SetString(PyExc_ValueError, + "n must be a non-negative integer"); + goto error; + } + if (_PyLong_IsNegative((PyLongObject *)k)) { + PyErr_SetString(PyExc_ValueError, + "k must be a non-negative integer"); + goto error; + } + + cmp = PyObject_RichCompareBool(n, k, Py_LT); + if (cmp != 0) { + if (cmp > 0) { + result = PyLong_FromLong(0); + goto done; + } + goto error; + } + + ki = PyLong_AsLongLongAndOverflow(k, &overflow); + assert(overflow >= 0 && !PyErr_Occurred()); + if (overflow > 0) { + PyErr_Format(PyExc_OverflowError, + "k must not exceed %lld", + LLONG_MAX); + goto error; + } + assert(ki >= 0); + + ni = PyLong_AsLongLongAndOverflow(n, &overflow); + assert(overflow >= 0 && !PyErr_Occurred()); + if (!overflow && ki > 1) { + assert(ni >= 0); + result = perm_comb_small((unsigned long long)ni, + (unsigned long long)ki, 0); + } + else { + result = perm_comb(n, (unsigned long long)ki, 0); + } + +done: + Py_DECREF(n); + Py_DECREF(k); + return result; + +error: + Py_DECREF(n); + Py_DECREF(k); + return NULL; +} + +/*[clinic input] +@permit_long_summary +math.integer.comb + + n: object + k: object + / + +Number of ways to choose k items from n items without repetition and without order. + +Evaluates to n! / (k! * (n - k)!) when k <= n and evaluates +to zero when k > n. + +Also called the binomial coefficient because it is equivalent +to the coefficient of k-th term in polynomial expansion of the +expression (1 + x)**n. + +Raises ValueError if either of the arguments are negative. +[clinic start generated code]*/ + +static PyObject * +math_integer_comb_impl(PyObject *module, PyObject *n, PyObject *k) +/*[clinic end generated code: output=c2c9cdfe0d5dd43f input=8cc12726b682c4a5]*/ +{ + PyObject *result = NULL, *temp; + int overflow, cmp; + long long ki, ni; + + n = PyNumber_Index(n); + if (n == NULL) { + return NULL; + } + k = PyNumber_Index(k); + if (k == NULL) { + Py_DECREF(n); + return NULL; + } + assert(PyLong_CheckExact(n) && PyLong_CheckExact(k)); + + if (_PyLong_IsNegative((PyLongObject *)n)) { + PyErr_SetString(PyExc_ValueError, + "n must be a non-negative integer"); + goto error; + } + if (_PyLong_IsNegative((PyLongObject *)k)) { + PyErr_SetString(PyExc_ValueError, + "k must be a non-negative integer"); + goto error; + } + + ni = PyLong_AsLongLongAndOverflow(n, &overflow); + assert(overflow >= 0 && !PyErr_Occurred()); + if (!overflow) { + assert(ni >= 0); + ki = PyLong_AsLongLongAndOverflow(k, &overflow); + assert(overflow >= 0 && !PyErr_Occurred()); + if (overflow || ki > ni) { + result = PyLong_FromLong(0); + goto done; + } + assert(ki >= 0); + + ki = Py_MIN(ki, ni - ki); + if (ki > 1) { + result = perm_comb_small((unsigned long long)ni, + (unsigned long long)ki, 1); + goto done; + } + /* For k == 1 just return the original n in perm_comb(). */ + } + else { + /* k = min(k, n - k) */ + temp = PyNumber_Subtract(n, k); + if (temp == NULL) { + goto error; + } + assert(PyLong_Check(temp)); + if (_PyLong_IsNegative((PyLongObject *)temp)) { + Py_DECREF(temp); + result = PyLong_FromLong(0); + goto done; + } + cmp = PyObject_RichCompareBool(temp, k, Py_LT); + if (cmp > 0) { + Py_SETREF(k, temp); + } + else { + Py_DECREF(temp); + if (cmp < 0) { + goto error; + } + } + + ki = PyLong_AsLongLongAndOverflow(k, &overflow); + assert(overflow >= 0 && !PyErr_Occurred()); + if (overflow) { + PyErr_Format(PyExc_OverflowError, + "min(n - k, k) must not exceed %lld", + LLONG_MAX); + goto error; + } + assert(ki >= 0); + } + + result = perm_comb(n, (unsigned long long)ki, 1); + +done: + Py_DECREF(n); + Py_DECREF(k); + return result; + +error: + Py_DECREF(n); + Py_DECREF(k); + return NULL; +} + + +static PyMethodDef math_integer_methods[] = { + MATH_INTEGER_COMB_METHODDEF + MATH_INTEGER_FACTORIAL_METHODDEF + MATH_INTEGER_GCD_METHODDEF + MATH_INTEGER_ISQRT_METHODDEF + MATH_INTEGER_LCM_METHODDEF + MATH_INTEGER_PERM_METHODDEF + {NULL, NULL} /* sentinel */ +}; + +static int +math_integer_exec(PyObject *module) +{ + /* Fix the __name__ attribute of the module and the __module__ attribute + * of its functions. + */ + PyObject *name = PyUnicode_FromString("math.integer"); + if (name == NULL) { + return -1; + } + if (PyObject_SetAttrString(module, "__name__", name) < 0) { + Py_DECREF(name); + return -1; + } + for (const PyMethodDef *m = math_integer_methods; m->ml_name; m++) { + PyObject *obj = PyObject_GetAttrString(module, m->ml_name); + if (obj == NULL) { + Py_DECREF(name); + return -1; + } + if (PyObject_SetAttrString(obj, "__module__", name) < 0) { + Py_DECREF(name); + Py_DECREF(obj); + return -1; + } + Py_DECREF(obj); + } + Py_DECREF(name); + return 0; +} + +static PyModuleDef_Slot math_integer_slots[] = { + _Py_ABI_SLOT, + {Py_mod_exec, math_integer_exec}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0, NULL} +}; + +PyDoc_STRVAR(module_doc, +"This module provides access to integer related mathematical functions."); + +static struct PyModuleDef math_integer_module = { + PyModuleDef_HEAD_INIT, + .m_name = "math.integer", + .m_doc = module_doc, + .m_size = 0, + .m_methods = math_integer_methods, + .m_slots = math_integer_slots, +}; + +PyMODINIT_FUNC +PyInit__math_integer(void) +{ + return PyModuleDef_Init(&math_integer_module); +} diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 7c2a421dd6a450..64e5372d73d2f2 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -60,6 +60,7 @@ raised for division by zero and mod by zero. #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_bitutils.h" // _Py_bit_length() #include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_import.h" // _PyImport_SetModuleString() #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_object.h" // _PyObject_LookupSpecial() @@ -186,13 +187,6 @@ tl_to_d(TripleLength total) } -/* - sin(pi*x), giving accurate results for all finite x (especially x - integral or close to an integer). This is here for use in the - reflection formula for the gamma function. It conforms to IEEE - 754-2008 for finite arguments, but not for infinities or nans. -*/ - static const double pi = 3.141592653589793238462643383279502884197; static const double logpi = 1.144729885849400174143427351353058711647; @@ -218,13 +212,120 @@ static const double logpi = 1.144729885849400174143427351353058711647; } \ } +#ifndef HAVE_ACOSPI +/* + acos(x)/pi. It conforms to C23 Annex 'F'. +*/ + +static double +m_acospi(double x) +{ + double r = acos(x)/pi; + if (isgreater(r, 1.0)) { + return 1.0; + } + return r; +} +#else +#define m_acospi acospi +#endif + +#ifndef HAVE_ASINPI +/* + asin(x)/pi. It conforms to C23 Annex 'F'. +*/ + +static double +m_asinpi(double x) +{ + double r = asin(x)/pi; + if (isgreater(fabs(r), 0.5)) { + return copysign(0.5, r); + } + return r; +} +#else +#define m_asinpi asinpi +#endif + +#ifndef HAVE_ATANPI +/* + asin(x)/pi. It conforms to C23 Annex 'F'. +*/ + +static double +m_atanpi(double x) +{ + double r = atan(x)/pi; + if (isgreater(fabs(r), 0.5)) { + return copysign(0.5, r); + } + return r; +} +#else +#define m_atanpi atanpi +#endif + +#ifndef HAVE_ATAN2PI +/* + asin(x)/pi. It conforms to C23 Annex 'F'. +*/ + +static double +m_atan2pi(double y, double x) +{ + double r = atan2(y, x)/pi; + if (isgreater(fabs(r), 1.0)) { + return copysign(1.0, r); + } + return r; +} +#else +#define m_atan2pi atan2pi +#endif + +#ifndef HAVE_COSPI +/* + cos(pi*x), giving accurate results for all finite x (especially x + integral or close to an integer). It conforms to C23 Annex 'F'. +*/ + +static double +m_cospi(double x) +{ + if (!isfinite(x)) { + return cos(x); + } + x = fabs(x - 2.0 * round(0.5 * x)); + if (x <= 0.25) { + return cos(pi * x); + } + if (x == 0.5) { + return 0.0; + } + if (x <= 0.75) { + return sin(pi * (0.5 - x)); + } + return -cos(pi * (1.0 - x)); +} +#else +#define m_cospi cospi +#endif + +#ifndef HAVE_SINPI +/* + sin(pi*x), giving accurate results for all finite x (especially x + integral or close to an integer). It conforms to C23 Annex 'F'. +*/ + static double m_sinpi(double x) { double y, r; int n; - /* this function should only ever be called for finite arguments */ - assert(isfinite(x)); + if (!isfinite(x)) { + return sin(x); + } y = fmod(fabs(x), 2.0); n = (int)round(2.0*y); assert(0 <= n && n <= 4); @@ -251,6 +352,47 @@ m_sinpi(double x) } return copysign(1.0, x)*r; } +#else +#define m_sinpi sinpi +#endif + +#ifndef HAVE_TANPI +/* + tan(pi*x), giving accurate results for all finite x (especially x + integral or close to an integer). It conforms to C23 Annex 'F'. +*/ + +static double +m_tanpi(double x) +{ + double y, absy; + if (!isfinite(x)) { + return tan(x); + } + y = x - 2.0 * round(0.5 * x); + absy = fabs(y); + if (absy == 0.0) { + return copysign(0.0, x); + } + if (absy == 1.0) { + return copysign(0.0, -x); + } + if (absy == 0.5) { + errno = ERANGE; + return 1.0 / copysign(0.0, y); + } + if (absy > 0.5) { + y -= copysign(1.0, y); + absy = fabs(y); + } + if (absy <= 0.25) { + return tan(pi * y); + } + return copysign(1.0 / tan(pi * (0.5 - absy)), y); +} +#else +#define m_tanpi tanpi +#endif /* Implementation of the real gamma function. Kept here to work around issues (see e.g. gh-70309) with quality of libm's tgamma/lgamma implementations @@ -394,7 +536,7 @@ m_tgamma(double x) if (x == 0.0) { errno = EDOM; /* tgamma(+-0.0) = +-inf, divide-by-zero */ - return copysign(Py_INFINITY, x); + return copysign(INFINITY, x); } /* integer arguments */ @@ -425,7 +567,7 @@ m_tgamma(double x) } else { errno = ERANGE; - return Py_INFINITY; + return INFINITY; } } @@ -489,14 +631,14 @@ m_lgamma(double x) if (isnan(x)) return x; /* lgamma(nan) = nan */ else - return Py_INFINITY; /* lgamma(+-inf) = +inf */ + return INFINITY; /* lgamma(+-inf) = +inf */ } /* integer arguments */ if (x == floor(x) && x <= 2.0) { if (x <= 0.0) { errno = EDOM; /* lgamma(n) = inf, divide-by-zero for */ - return Py_INFINITY; /* integers n <= 0 */ + return INFINITY; /* integers n <= 0 */ } else { return 0.0; /* lgamma(1) = lgamma(2) = 0.0 */ @@ -632,7 +774,7 @@ m_log(double x) return log(x); errno = EDOM; if (x == 0.0) - return -Py_INFINITY; /* log(0) = -inf */ + return -INFINITY; /* log(0) = -inf */ else return Py_NAN; /* log(-ve) = nan */ } @@ -675,7 +817,7 @@ m_log2(double x) } else if (x == 0.0) { errno = EDOM; - return -Py_INFINITY; /* log2(0) = -inf, divide-by-zero */ + return -INFINITY; /* log2(0) = -inf, divide-by-zero */ } else { errno = EDOM; @@ -691,7 +833,7 @@ m_log10(double x) return log10(x); errno = EDOM; if (x == 0.0) - return -Py_INFINITY; /* log10(0) = -inf */ + return -INFINITY; /* log10(0) = -inf */ else return Py_NAN; /* log10(-ve) = nan */ } @@ -706,140 +848,6 @@ m_log10(double x) } -/*[clinic input] -math.gcd - - *integers as args: array - -Greatest Common Divisor. -[clinic start generated code]*/ - -static PyObject * -math_gcd_impl(PyObject *module, PyObject * const *args, - Py_ssize_t args_length) -/*[clinic end generated code: output=a26c95907374ffb4 input=ded7f0ea3850c05c]*/ -{ - // Fast-path for the common case: gcd(int, int) - if (args_length == 2 && PyLong_CheckExact(args[0]) && PyLong_CheckExact(args[1])) - { - return _PyLong_GCD(args[0], args[1]); - } - - if (args_length == 0) { - return PyLong_FromLong(0); - } - - PyObject *res = PyNumber_Index(args[0]); - if (res == NULL) { - return NULL; - } - if (args_length == 1) { - Py_SETREF(res, PyNumber_Absolute(res)); - return res; - } - - PyObject *one = _PyLong_GetOne(); // borrowed ref - for (Py_ssize_t i = 1; i < args_length; i++) { - PyObject *x = _PyNumber_Index(args[i]); - if (x == NULL) { - Py_DECREF(res); - return NULL; - } - if (res == one) { - /* Fast path: just check arguments. - It is okay to use identity comparison here. */ - Py_DECREF(x); - continue; - } - Py_SETREF(res, _PyLong_GCD(res, x)); - Py_DECREF(x); - if (res == NULL) { - return NULL; - } - } - return res; -} - - -static PyObject * -long_lcm(PyObject *a, PyObject *b) -{ - PyObject *g, *m, *f, *ab; - - if (_PyLong_IsZero((PyLongObject *)a) || _PyLong_IsZero((PyLongObject *)b)) { - return PyLong_FromLong(0); - } - g = _PyLong_GCD(a, b); - if (g == NULL) { - return NULL; - } - f = PyNumber_FloorDivide(a, g); - Py_DECREF(g); - if (f == NULL) { - return NULL; - } - m = PyNumber_Multiply(f, b); - Py_DECREF(f); - if (m == NULL) { - return NULL; - } - ab = PyNumber_Absolute(m); - Py_DECREF(m); - return ab; -} - - -/*[clinic input] -math.lcm - - *integers as args: array - -Least Common Multiple. -[clinic start generated code]*/ - -static PyObject * -math_lcm_impl(PyObject *module, PyObject * const *args, - Py_ssize_t args_length) -/*[clinic end generated code: output=c8a59a5c2e55c816 input=3e4f4b7cdf948a98]*/ -{ - PyObject *res, *x; - Py_ssize_t i; - - if (args_length == 0) { - return PyLong_FromLong(1); - } - res = PyNumber_Index(args[0]); - if (res == NULL) { - return NULL; - } - if (args_length == 1) { - Py_SETREF(res, PyNumber_Absolute(res)); - return res; - } - - PyObject *zero = _PyLong_GetZero(); // borrowed ref - for (i = 1; i < args_length; i++) { - x = PyNumber_Index(args[i]); - if (x == NULL) { - Py_DECREF(res); - return NULL; - } - if (res == zero) { - /* Fast path: just check arguments. - It is okay to use identity comparison here. */ - Py_DECREF(x); - continue; - } - Py_SETREF(res, long_lcm(res, x)); - Py_DECREF(x); - if (res == NULL) { - return NULL; - } - } - return res; -} - - /* Call is_error when errno != 0, and where x is the result libm * returned. is_error will usually set up an exception and return * true (1), but may return false (0) without setting up an exception. @@ -1085,6 +1093,11 @@ FUNC1D(acosh, acosh, 0, "acosh($module, x, /)\n--\n\n" "Return the inverse hyperbolic cosine of x.", "expected argument value not less than 1, got %s") +FUNC1D(acospi, m_acospi, 0, + "acospi($module, x, /)\n--\n\n" + "Return the arc cosine (measured in half-turns) of x.\n\n" + "The result is between 0 and 1.", + "expected a number in range from -1 up to 1, got %s") FUNC1D(asin, asin, 0, "asin($module, x, /)\n--\n\n" "Return the arc sine (measured in radians) of x.\n\n" @@ -1093,6 +1106,11 @@ FUNC1D(asin, asin, 0, FUNC1(asinh, asinh, 0, "asinh($module, x, /)\n--\n\n" "Return the inverse hyperbolic sine of x.") +FUNC1D(asinpi, m_asinpi, 0, + "asinpi($module, x, /)\n--\n\n" + "Return the arc sine (measured in half-turns) of x.\n\n" + "The result is between -1/2 and 1/2.", + "expected a number in range from -1 up to 1, got %s") FUNC1(atan, atan, 0, "atan($module, x, /)\n--\n\n" "Return the arc tangent (measured in radians) of x.\n\n" @@ -1101,10 +1119,19 @@ FUNC2(atan2, atan2, "atan2($module, y, x, /)\n--\n\n" "Return the arc tangent (measured in radians) of y/x.\n\n" "Unlike atan(y/x), the signs of both x and y are considered.") +FUNC2(atan2pi, m_atan2pi, + "atan2pi($module, y, x, /)\n--\n\n" + "Return the arc tangent (measured in half-turns) of y/x.\n\n" + "Unlike atanpi(y/x), the signs of both x and y are considered.") FUNC1D(atanh, atanh, 0, "atanh($module, x, /)\n--\n\n" "Return the inverse hyperbolic tangent of x.", "expected a number between -1 and 1, got %s") +FUNC1D(atanpi, m_atanpi, 0, + "atanpi($module, x, /)\n--\n\n" + "Return the arc tangent (measured in half-turns) of x.\n\n" + "The result is between 0 and 1.", + "expected a number in range from -1 up to 1, got %s") FUNC1(cbrt, cbrt, 0, "cbrt($module, x, /)\n--\n\n" "Return the cube root of x.") @@ -1157,6 +1184,10 @@ FUNC1D(cos, cos, 0, FUNC1(cosh, cosh, 1, "cosh($module, x, /)\n--\n\n" "Return the hyperbolic cosine of x.") +FUNC1D(cospi, m_cospi, 0, + "cospi($module, x, /)\n--\n\n" + "Return the cosine of x (measured in half-turns).", + "expected a finite input, got %s") FUNC1A(erf, erf, "erf($module, x, /)\n--\n\n" "Error function at x.") @@ -1291,6 +1322,10 @@ FUNC1D(sin, sin, 0, FUNC1(sinh, sinh, 1, "sinh($module, x, /)\n--\n\n" "Return the hyperbolic sine of x.") +FUNC1D(sinpi, m_sinpi, 0, + "sinpi($module, x, /)\n--\n\n" + "Return the sine of x (measured in half-turns).", + "expected a finite input, got %s") FUNC1D(sqrt, sqrt, 0, "sqrt($module, x, /)\n--\n\n" "Return the square root of x.", @@ -1302,6 +1337,10 @@ FUNC1D(tan, tan, 0, FUNC1(tanh, tanh, 0, "tanh($module, x, /)\n--\n\n" "Return the hyperbolic tangent of x.") +FUNC1D(tanpi, m_tanpi, 1, + "tanpi($module, x, /)\n--\n\n" + "Return the tangent of x (measured in half-turns).", + "expected a finite input, got %s") /* Precision summation function as msum() by Raymond Hettinger in <https://code.activestate.com/recipes/393090-binary-floating-point-summation-accurate-to-full-p/>, @@ -1531,576 +1570,6 @@ math_fsum(PyObject *module, PyObject *seq) #undef NUM_PARTIALS -static unsigned long -count_set_bits(unsigned long n) -{ - unsigned long count = 0; - while (n != 0) { - ++count; - n &= n - 1; /* clear least significant bit */ - } - return count; -} - -/* Integer square root - -Given a nonnegative integer `n`, we want to compute the largest integer -`a` for which `a * a <= n`, or equivalently the integer part of the exact -square root of `n`. - -We use an adaptive-precision pure-integer version of Newton's iteration. Given -a positive integer `n`, the algorithm produces at each iteration an integer -approximation `a` to the square root of `n >> s` for some even integer `s`, -with `s` decreasing as the iterations progress. On the final iteration, `s` is -zero and we have an approximation to the square root of `n` itself. - -At every step, the approximation `a` is strictly within 1.0 of the true square -root, so we have - - (a - 1)**2 < (n >> s) < (a + 1)**2 - -After the final iteration, a check-and-correct step is needed to determine -whether `a` or `a - 1` gives the desired integer square root of `n`. - -The algorithm is remarkable in its simplicity. There's no need for a -per-iteration check-and-correct step, and termination is straightforward: the -number of iterations is known in advance (it's exactly `floor(log2(log2(n)))` -for `n > 1`). The only tricky part of the correctness proof is in establishing -that the bound `(a - 1)**2 < (n >> s) < (a + 1)**2` is maintained from one -iteration to the next. A sketch of the proof of this is given below. - -In addition to the proof sketch, a formal, computer-verified proof -of correctness (using Lean) of an equivalent recursive algorithm can be found -here: - - https://github.com/mdickinson/snippets/blob/master/proofs/isqrt/src/isqrt.lean - - -Here's Python code equivalent to the C implementation below: - - def isqrt(n): - """ - Return the integer part of the square root of the input. - """ - n = operator.index(n) - - if n < 0: - raise ValueError("isqrt() argument must be nonnegative") - if n == 0: - return 0 - - c = (n.bit_length() - 1) // 2 - a = 1 - d = 0 - for s in reversed(range(c.bit_length())): - # Loop invariant: (a-1)**2 < (n >> 2*(c - d)) < (a+1)**2 - e = d - d = c >> s - a = (a << d - e - 1) + (n >> 2*c - e - d + 1) // a - - return a - (a*a > n) - - -Sketch of proof of correctness ------------------------------- - -The delicate part of the correctness proof is showing that the loop invariant -is preserved from one iteration to the next. That is, just before the line - - a = (a << d - e - 1) + (n >> 2*c - e - d + 1) // a - -is executed in the above code, we know that - - (1) (a - 1)**2 < (n >> 2*(c - e)) < (a + 1)**2. - -(since `e` is always the value of `d` from the previous iteration). We must -prove that after that line is executed, we have - - (a - 1)**2 < (n >> 2*(c - d)) < (a + 1)**2 - -To facilitate the proof, we make some changes of notation. Write `m` for -`n >> 2*(c-d)`, and write `b` for the new value of `a`, so - - b = (a << d - e - 1) + (n >> 2*c - e - d + 1) // a - -or equivalently: - - (2) b = (a << d - e - 1) + (m >> d - e + 1) // a - -Then we can rewrite (1) as: - - (3) (a - 1)**2 < (m >> 2*(d - e)) < (a + 1)**2 - -and we must show that (b - 1)**2 < m < (b + 1)**2. - -From this point on, we switch to mathematical notation, so `/` means exact -division rather than integer division and `^` is used for exponentiation. We -use the `√` symbol for the exact square root. In (3), we can remove the -implicit floor operation to give: - - (4) (a - 1)^2 < m / 4^(d - e) < (a + 1)^2 - -Taking square roots throughout (4), scaling by `2^(d-e)`, and rearranging gives - - (5) 0 <= | 2^(d-e)a - √m | < 2^(d-e) - -Squaring and dividing through by `2^(d-e+1) a` gives - - (6) 0 <= 2^(d-e-1) a + m / (2^(d-e+1) a) - √m < 2^(d-e-1) / a - -We'll show below that `2^(d-e-1) <= a`. Given that, we can replace the -right-hand side of (6) with `1`, and now replacing the central -term `m / (2^(d-e+1) a)` with its floor in (6) gives - - (7) -1 < 2^(d-e-1) a + m // 2^(d-e+1) a - √m < 1 - -Or equivalently, from (2): - - (7) -1 < b - √m < 1 - -and rearranging gives that `(b-1)^2 < m < (b+1)^2`, which is what we needed -to prove. - -We're not quite done: we still have to prove the inequality `2^(d - e - 1) <= -a` that was used to get line (7) above. From the definition of `c`, we have -`4^c <= n`, which implies - - (8) 4^d <= m - -also, since `e == d >> 1`, `d` is at most `2e + 1`, from which it follows -that `2d - 2e - 1 <= d` and hence that - - (9) 4^(2d - 2e - 1) <= m - -Dividing both sides by `4^(d - e)` gives - - (10) 4^(d - e - 1) <= m / 4^(d - e) - -But we know from (4) that `m / 4^(d-e) < (a + 1)^2`, hence - - (11) 4^(d - e - 1) < (a + 1)^2 - -Now taking square roots of both sides and observing that both `2^(d-e-1)` and -`a` are integers gives `2^(d - e - 1) <= a`, which is what we needed. This -completes the proof sketch. - -*/ - -/* - The _approximate_isqrt_tab table provides approximate square roots for - 16-bit integers. For any n in the range 2**14 <= n < 2**16, the value - - a = _approximate_isqrt_tab[(n >> 8) - 64] - - is an approximate square root of n, satisfying (a - 1)**2 < n < (a + 1)**2. - - The table was computed in Python using the expression: - - [min(round(sqrt(256*n + 128)), 255) for n in range(64, 256)] -*/ - -static const uint8_t _approximate_isqrt_tab[192] = { - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, - 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150, - 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159, 160, - 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168, 169, - 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, - 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 186, 186, - 187, 188, 188, 189, 190, 190, 191, 192, 192, 193, 194, 194, - 195, 196, 196, 197, 198, 198, 199, 200, 200, 201, 201, 202, - 203, 203, 204, 205, 205, 206, 206, 207, 208, 208, 209, 210, - 210, 211, 211, 212, 213, 213, 214, 214, 215, 216, 216, 217, - 217, 218, 219, 219, 220, 220, 221, 221, 222, 223, 223, 224, - 224, 225, 225, 226, 227, 227, 228, 228, 229, 229, 230, 230, - 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 237, 237, - 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, - 244, 244, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, - 250, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255, 255, -}; - -/* Approximate square root of a large 64-bit integer. - - Given `n` satisfying `2**62 <= n < 2**64`, return `a` - satisfying `(a - 1)**2 < n < (a + 1)**2`. */ - -static inline uint32_t -_approximate_isqrt(uint64_t n) -{ - uint32_t u = _approximate_isqrt_tab[(n >> 56) - 64]; - u = (u << 7) + (uint32_t)(n >> 41) / u; - return (u << 15) + (uint32_t)((n >> 17) / u); -} - -/*[clinic input] -math.isqrt - - n: object - / - -Return the integer part of the square root of the input. -[clinic start generated code]*/ - -static PyObject * -math_isqrt(PyObject *module, PyObject *n) -/*[clinic end generated code: output=35a6f7f980beab26 input=5b6e7ae4fa6c43d6]*/ -{ - int a_too_large, c_bit_length; - int64_t c, d; - uint64_t m; - uint32_t u; - PyObject *a = NULL, *b; - - n = _PyNumber_Index(n); - if (n == NULL) { - return NULL; - } - - if (_PyLong_IsNegative((PyLongObject *)n)) { - PyErr_SetString( - PyExc_ValueError, - "isqrt() argument must be nonnegative"); - goto error; - } - if (_PyLong_IsZero((PyLongObject *)n)) { - Py_DECREF(n); - return PyLong_FromLong(0); - } - - /* c = (n.bit_length() - 1) // 2 */ - c = _PyLong_NumBits(n); - assert(c > 0); - assert(!PyErr_Occurred()); - c = (c - 1) / 2; - - /* Fast path: if c <= 31 then n < 2**64 and we can compute directly with a - fast, almost branch-free algorithm. */ - if (c <= 31) { - int shift = 31 - (int)c; - m = (uint64_t)PyLong_AsUnsignedLongLong(n); - Py_DECREF(n); - if (m == (uint64_t)(-1) && PyErr_Occurred()) { - return NULL; - } - u = _approximate_isqrt(m << 2*shift) >> shift; - u -= (uint64_t)u * u > m; - return PyLong_FromUnsignedLong(u); - } - - /* Slow path: n >= 2**64. We perform the first five iterations in C integer - arithmetic, then switch to using Python long integers. */ - - /* From n >= 2**64 it follows that c.bit_length() >= 6. */ - c_bit_length = 6; - while ((c >> c_bit_length) > 0) { - ++c_bit_length; - } - - /* Initialise d and a. */ - d = c >> (c_bit_length - 5); - b = _PyLong_Rshift(n, 2*c - 62); - if (b == NULL) { - goto error; - } - m = (uint64_t)PyLong_AsUnsignedLongLong(b); - Py_DECREF(b); - if (m == (uint64_t)(-1) && PyErr_Occurred()) { - goto error; - } - u = _approximate_isqrt(m) >> (31U - d); - a = PyLong_FromUnsignedLong(u); - if (a == NULL) { - goto error; - } - - for (int s = c_bit_length - 6; s >= 0; --s) { - PyObject *q; - int64_t e = d; - - d = c >> s; - - /* q = (n >> 2*c - e - d + 1) // a */ - q = _PyLong_Rshift(n, 2*c - d - e + 1); - if (q == NULL) { - goto error; - } - Py_SETREF(q, PyNumber_FloorDivide(q, a)); - if (q == NULL) { - goto error; - } - - /* a = (a << d - 1 - e) + q */ - Py_SETREF(a, _PyLong_Lshift(a, d - 1 - e)); - if (a == NULL) { - Py_DECREF(q); - goto error; - } - Py_SETREF(a, PyNumber_Add(a, q)); - Py_DECREF(q); - if (a == NULL) { - goto error; - } - } - - /* The correct result is either a or a - 1. Figure out which, and - decrement a if necessary. */ - - /* a_too_large = n < a * a */ - b = PyNumber_Multiply(a, a); - if (b == NULL) { - goto error; - } - a_too_large = PyObject_RichCompareBool(n, b, Py_LT); - Py_DECREF(b); - if (a_too_large == -1) { - goto error; - } - - if (a_too_large) { - Py_SETREF(a, PyNumber_Subtract(a, _PyLong_GetOne())); - } - Py_DECREF(n); - return a; - - error: - Py_XDECREF(a); - Py_DECREF(n); - return NULL; -} - -/* Divide-and-conquer factorial algorithm - * - * Based on the formula and pseudo-code provided at: - * http://www.luschny.de/math/factorial/binarysplitfact.html - * - * Faster algorithms exist, but they're more complicated and depend on - * a fast prime factorization algorithm. - * - * Notes on the algorithm - * ---------------------- - * - * factorial(n) is written in the form 2**k * m, with m odd. k and m are - * computed separately, and then combined using a left shift. - * - * The function factorial_odd_part computes the odd part m (i.e., the greatest - * odd divisor) of factorial(n), using the formula: - * - * factorial_odd_part(n) = - * - * product_{i >= 0} product_{0 < j <= n / 2**i, j odd} j - * - * Example: factorial_odd_part(20) = - * - * (1) * - * (1) * - * (1 * 3 * 5) * - * (1 * 3 * 5 * 7 * 9) * - * (1 * 3 * 5 * 7 * 9 * 11 * 13 * 15 * 17 * 19) - * - * Here i goes from large to small: the first term corresponds to i=4 (any - * larger i gives an empty product), and the last term corresponds to i=0. - * Each term can be computed from the last by multiplying by the extra odd - * numbers required: e.g., to get from the penultimate term to the last one, - * we multiply by (11 * 13 * 15 * 17 * 19). - * - * To see a hint of why this formula works, here are the same numbers as above - * but with the even parts (i.e., the appropriate powers of 2) included. For - * each subterm in the product for i, we multiply that subterm by 2**i: - * - * factorial(20) = - * - * (16) * - * (8) * - * (4 * 12 * 20) * - * (2 * 6 * 10 * 14 * 18) * - * (1 * 3 * 5 * 7 * 9 * 11 * 13 * 15 * 17 * 19) - * - * The factorial_partial_product function computes the product of all odd j in - * range(start, stop) for given start and stop. It's used to compute the - * partial products like (11 * 13 * 15 * 17 * 19) in the example above. It - * operates recursively, repeatedly splitting the range into two roughly equal - * pieces until the subranges are small enough to be computed using only C - * integer arithmetic. - * - * The two-valuation k (i.e., the exponent of the largest power of 2 dividing - * the factorial) is computed independently in the main math_factorial - * function. By standard results, its value is: - * - * two_valuation = n//2 + n//4 + n//8 + .... - * - * It can be shown (e.g., by complete induction on n) that two_valuation is - * equal to n - count_set_bits(n), where count_set_bits(n) gives the number of - * '1'-bits in the binary expansion of n. - */ - -/* factorial_partial_product: Compute product(range(start, stop, 2)) using - * divide and conquer. Assumes start and stop are odd and stop > start. - * max_bits must be >= bit_length(stop - 2). */ - -static PyObject * -factorial_partial_product(unsigned long start, unsigned long stop, - unsigned long max_bits) -{ - unsigned long midpoint, num_operands; - PyObject *left = NULL, *right = NULL, *result = NULL; - - /* If the return value will fit an unsigned long, then we can - * multiply in a tight, fast loop where each multiply is O(1). - * Compute an upper bound on the number of bits required to store - * the answer. - * - * Storing some integer z requires floor(lg(z))+1 bits, which is - * conveniently the value returned by bit_length(z). The - * product x*y will require at most - * bit_length(x) + bit_length(y) bits to store, based - * on the idea that lg product = lg x + lg y. - * - * We know that stop - 2 is the largest number to be multiplied. From - * there, we have: bit_length(answer) <= num_operands * - * bit_length(stop - 2) - */ - - num_operands = (stop - start) / 2; - /* The "num_operands <= 8 * SIZEOF_LONG" check guards against the - * unlikely case of an overflow in num_operands * max_bits. */ - if (num_operands <= 8 * SIZEOF_LONG && - num_operands * max_bits <= 8 * SIZEOF_LONG) { - unsigned long j, total; - for (total = start, j = start + 2; j < stop; j += 2) - total *= j; - return PyLong_FromUnsignedLong(total); - } - - /* find midpoint of range(start, stop), rounded up to next odd number. */ - midpoint = (start + num_operands) | 1; - left = factorial_partial_product(start, midpoint, - _Py_bit_length(midpoint - 2)); - if (left == NULL) - goto error; - right = factorial_partial_product(midpoint, stop, max_bits); - if (right == NULL) - goto error; - result = PyNumber_Multiply(left, right); - - error: - Py_XDECREF(left); - Py_XDECREF(right); - return result; -} - -/* factorial_odd_part: compute the odd part of factorial(n). */ - -static PyObject * -factorial_odd_part(unsigned long n) -{ - long i; - unsigned long v, lower, upper; - PyObject *partial, *tmp, *inner, *outer; - - inner = PyLong_FromLong(1); - if (inner == NULL) - return NULL; - outer = Py_NewRef(inner); - - upper = 3; - for (i = _Py_bit_length(n) - 2; i >= 0; i--) { - v = n >> i; - if (v <= 2) - continue; - lower = upper; - /* (v + 1) | 1 = least odd integer strictly larger than n / 2**i */ - upper = (v + 1) | 1; - /* Here inner is the product of all odd integers j in the range (0, - n/2**(i+1)]. The factorial_partial_product call below gives the - product of all odd integers j in the range (n/2**(i+1), n/2**i]. */ - partial = factorial_partial_product(lower, upper, _Py_bit_length(upper-2)); - /* inner *= partial */ - if (partial == NULL) - goto error; - tmp = PyNumber_Multiply(inner, partial); - Py_DECREF(partial); - if (tmp == NULL) - goto error; - Py_SETREF(inner, tmp); - /* Now inner is the product of all odd integers j in the range (0, - n/2**i], giving the inner product in the formula above. */ - - /* outer *= inner; */ - tmp = PyNumber_Multiply(outer, inner); - if (tmp == NULL) - goto error; - Py_SETREF(outer, tmp); - } - Py_DECREF(inner); - return outer; - - error: - Py_DECREF(outer); - Py_DECREF(inner); - return NULL; -} - - -/* Lookup table for small factorial values */ - -static const unsigned long SmallFactorials[] = { - 1, 1, 2, 6, 24, 120, 720, 5040, 40320, - 362880, 3628800, 39916800, 479001600, -#if SIZEOF_LONG >= 8 - 6227020800, 87178291200, 1307674368000, - 20922789888000, 355687428096000, 6402373705728000, - 121645100408832000, 2432902008176640000 -#endif -}; - -/*[clinic input] -math.factorial - - n as arg: object - / - -Find n!. -[clinic start generated code]*/ - -static PyObject * -math_factorial(PyObject *module, PyObject *arg) -/*[clinic end generated code: output=6686f26fae00e9ca input=366cc321df3d4773]*/ -{ - long x, two_valuation; - int overflow; - PyObject *result, *odd_part; - - x = PyLong_AsLongAndOverflow(arg, &overflow); - if (x == -1 && PyErr_Occurred()) { - return NULL; - } - else if (overflow == 1) { - PyErr_Format(PyExc_OverflowError, - "factorial() argument should not exceed %ld", - LONG_MAX); - return NULL; - } - else if (overflow == -1 || x < 0) { - PyErr_SetString(PyExc_ValueError, - "factorial() not defined for negative values"); - return NULL; - } - - /* use lookup table if x is small */ - if (x < (long)Py_ARRAY_LENGTH(SmallFactorials)) - return PyLong_FromUnsignedLong(SmallFactorials[x]); - - /* else express in the form odd_part * 2**two_valuation, and compute as - odd_part << two_valuation. */ - odd_part = factorial_odd_part(x); - if (odd_part == NULL) - return NULL; - two_valuation = x - count_set_bits(x); - result = _PyLong_Lshift(odd_part, two_valuation); - Py_DECREF(odd_part); - return result; -} - - /*[clinic input] math.trunc @@ -2141,13 +1610,14 @@ math.frexp Return the mantissa and exponent of x, as pair (m, e). -m is a float and e is an int, such that x = m * 2.**e. -If x is 0, m and e are both 0. Else 0.5 <= abs(m) < 1.0. +If x is a finite nonzero number, then m is a float with +0.5 <= abs(m) < 1.0 and an integer e is such that +x == m * 2**e exactly. Else, return (x, 0). [clinic start generated code]*/ static PyObject * math_frexp_impl(PyObject *module, double x) -/*[clinic end generated code: output=03e30d252a15ad4a input=96251c9e208bc6e9]*/ +/*[clinic end generated code: output=03e30d252a15ad4a input=215cf8ea28a0959b]*/ { int i; /* deal with special cases directly, to sidestep platform @@ -2203,7 +1673,7 @@ math_ldexp_impl(PyObject *module, double x, PyObject *i) errno = 0; } else if (exp > INT_MAX) { /* overflow */ - r = copysign(Py_INFINITY, x); + r = copysign(INFINITY, x); errno = ERANGE; } else if (exp < INT_MIN) { /* underflow to +-0 */ @@ -2282,43 +1752,62 @@ math_modf_impl(PyObject *module, double x) in that int is larger than PY_SSIZE_T_MAX. */ static PyObject* -loghelper(PyObject* arg, double (*func)(double)) +loghelper_int(PyObject* arg, double (*func)(double)) { /* If it is int, do it ourselves. */ - if (PyLong_Check(arg)) { - double x, result; - int64_t e; + double x, result; + int64_t e; - /* Negative or zero inputs give a ValueError. */ - if (!_PyLong_IsPositive((PyLongObject *)arg)) { - /* The input can be an arbitrary large integer, so we - don't include it's value in the error message. */ - PyErr_SetString(PyExc_ValueError, - "expected a positive input"); - return NULL; - } + /* Negative or zero inputs give a ValueError. */ + if (!_PyLong_IsPositive((PyLongObject *)arg)) { + PyErr_SetString(PyExc_ValueError, + "expected a positive input"); + return NULL; + } - x = PyLong_AsDouble(arg); - if (x == -1.0 && PyErr_Occurred()) { - if (!PyErr_ExceptionMatches(PyExc_OverflowError)) - return NULL; - /* Here the conversion to double overflowed, but it's possible - to compute the log anyway. Clear the exception and continue. */ - PyErr_Clear(); - x = _PyLong_Frexp((PyLongObject *)arg, &e); - assert(e >= 0); - assert(!PyErr_Occurred()); - /* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */ - result = func(x) + func(2.0) * e; - } - else - /* Successfully converted x to a double. */ - result = func(x); - return PyFloat_FromDouble(result); + x = PyLong_AsDouble(arg); + if (x == -1.0 && PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_OverflowError)) + return NULL; + /* Here the conversion to double overflowed, but it's possible + to compute the log anyway. Clear the exception and continue. */ + PyErr_Clear(); + x = _PyLong_Frexp((PyLongObject *)arg, &e); + assert(!PyErr_Occurred()); + /* Value is ~= x * 2**e, so the log ~= log(x) + log(2) * e. */ + result = fma(func(2.0), (double)e, func(x)); } + else + /* Successfully converted x to a double. */ + result = func(x); + return PyFloat_FromDouble(result); +} +static PyObject* +loghelper(PyObject* arg, double (*func)(double)) +{ + /* If it is int, do it ourselves. */ + if (PyLong_Check(arg)) { + return loghelper_int(arg, func); + } /* Else let libm handle it by itself. */ - return math_1(arg, func, 0, "expected a positive input, got %s"); + PyObject *res = math_1(arg, func, 0, "expected a positive input, got %s"); + if (res == NULL && + PyErr_ExceptionMatches(PyExc_OverflowError) && + PyIndex_Check(arg)) + { + /* Here the conversion to double overflowed, but it's possible + to compute the log anyway. Clear the exception, convert to + integer and continue. */ + PyErr_Clear(); + arg = _PyNumber_Index(arg); + if (arg == NULL) { + return NULL; + } + res = loghelper_int(arg, func); + Py_DECREF(arg); + } + return res; } @@ -3225,6 +2714,7 @@ math_isnan_impl(PyObject *module, double x) /*[clinic input] +@permit_long_summary math.isinf x: double @@ -3235,7 +2725,7 @@ Return True if x is a positive or negative infinity, and False otherwise. static PyObject * math_isinf_impl(PyObject *module, double x) -/*[clinic end generated code: output=9f00cbec4de7b06b input=32630e4212cf961f]*/ +/*[clinic end generated code: output=9f00cbec4de7b06b input=8584152a71a3aea9]*/ { return PyBool_FromLong((long)isinf(x)); } @@ -3514,510 +3004,8 @@ math_prod_impl(PyObject *module, PyObject *iterable, PyObject *start) } -/* least significant 64 bits of the odd part of factorial(n), for n in range(128). - -Python code to generate the values: - - import math - - for n in range(128): - fac = math.factorial(n) - fac_odd_part = fac // (fac & -fac) - reduced_fac_odd_part = fac_odd_part % (2**64) - print(f"{reduced_fac_odd_part:#018x}u") -*/ -static const uint64_t reduced_factorial_odd_part[] = { - 0x0000000000000001u, 0x0000000000000001u, 0x0000000000000001u, 0x0000000000000003u, - 0x0000000000000003u, 0x000000000000000fu, 0x000000000000002du, 0x000000000000013bu, - 0x000000000000013bu, 0x0000000000000b13u, 0x000000000000375fu, 0x0000000000026115u, - 0x000000000007233fu, 0x00000000005cca33u, 0x0000000002898765u, 0x00000000260eeeebu, - 0x00000000260eeeebu, 0x0000000286fddd9bu, 0x00000016beecca73u, 0x000001b02b930689u, - 0x00000870d9df20adu, 0x0000b141df4dae31u, 0x00079dd498567c1bu, 0x00af2e19afc5266du, - 0x020d8a4d0f4f7347u, 0x335281867ec241efu, 0x9b3093d46fdd5923u, 0x5e1f9767cc5866b1u, - 0x92dd23d6966aced7u, 0xa30d0f4f0a196e5bu, 0x8dc3e5a1977d7755u, 0x2ab8ce915831734bu, - 0x2ab8ce915831734bu, 0x81d2a0bc5e5fdcabu, 0x9efcac82445da75bu, 0xbc8b95cf58cde171u, - 0xa0e8444a1f3cecf9u, 0x4191deb683ce3ffdu, 0xddd3878bc84ebfc7u, 0xcb39a64b83ff3751u, - 0xf8203f7993fc1495u, 0xbd2a2a78b35f4bddu, 0x84757be6b6d13921u, 0x3fbbcfc0b524988bu, - 0xbd11ed47c8928df9u, 0x3c26b59e41c2f4c5u, 0x677a5137e883fdb3u, 0xff74e943b03b93ddu, - 0xfe5ebbcb10b2bb97u, 0xb021f1de3235e7e7u, 0x33509eb2e743a58fu, 0x390f9da41279fb7du, - 0xe5cb0154f031c559u, 0x93074695ba4ddb6du, 0x81c471caa636247fu, 0xe1347289b5a1d749u, - 0x286f21c3f76ce2ffu, 0x00be84a2173e8ac7u, 0x1595065ca215b88bu, 0xf95877595b018809u, - 0x9c2efe3c5516f887u, 0x373294604679382bu, 0xaf1ff7a888adcd35u, 0x18ddf279a2c5800bu, - 0x18ddf279a2c5800bu, 0x505a90e2542582cbu, 0x5bacad2cd8d5dc2bu, 0xfe3152bcbff89f41u, - 0xe1467e88bf829351u, 0xb8001adb9e31b4d5u, 0x2803ac06a0cbb91fu, 0x1904b5d698805799u, - 0xe12a648b5c831461u, 0x3516abbd6160cfa9u, 0xac46d25f12fe036du, 0x78bfa1da906b00efu, - 0xf6390338b7f111bdu, 0x0f25f80f538255d9u, 0x4ec8ca55b8db140fu, 0x4ff670740b9b30a1u, - 0x8fd032443a07f325u, 0x80dfe7965c83eeb5u, 0xa3dc1714d1213afdu, 0x205b7bbfcdc62007u, - 0xa78126bbe140a093u, 0x9de1dc61ca7550cfu, 0x84f0046d01b492c5u, 0x2d91810b945de0f3u, - 0xf5408b7f6008aa71u, 0x43707f4863034149u, 0xdac65fb9679279d5u, 0xc48406e7d1114eb7u, - 0xa7dc9ed3c88e1271u, 0xfb25b2efdb9cb30du, 0x1bebda0951c4df63u, 0x5c85e975580ee5bdu, - 0x1591bc60082cb137u, 0x2c38606318ef25d7u, 0x76ca72f7c5c63e27u, 0xf04a75d17baa0915u, - 0x77458175139ae30du, 0x0e6c1330bc1b9421u, 0xdf87d2b5797e8293u, 0xefa5c703e1e68925u, - 0x2b6b1b3278b4f6e1u, 0xceee27b382394249u, 0xd74e3829f5dab91du, 0xfdb17989c26b5f1fu, - 0xc1b7d18781530845u, 0x7b4436b2105a8561u, 0x7ba7c0418372a7d7u, 0x9dbc5c67feb6c639u, - 0x502686d7f6ff6b8fu, 0x6101855406be7a1fu, 0x9956afb5806930e7u, 0xe1f0ee88af40f7c5u, - 0x984b057bda5c1151u, 0x9a49819acc13ea05u, 0x8ef0dead0896ef27u, 0x71f7826efe292b21u, - 0xad80a480e46986efu, 0x01cdc0ebf5e0c6f7u, 0x6e06f839968f68dbu, 0xdd5943ab56e76139u, - 0xcdcf31bf8604c5e7u, 0x7e2b4a847054a1cbu, 0x0ca75697a4d3d0f5u, 0x4703f53ac514a98bu, -}; - -/* inverses of reduced_factorial_odd_part values modulo 2**64. - -Python code to generate the values: - - import math - - for n in range(128): - fac = math.factorial(n) - fac_odd_part = fac // (fac & -fac) - inverted_fac_odd_part = pow(fac_odd_part, -1, 2**64) - print(f"{inverted_fac_odd_part:#018x}u") -*/ -static const uint64_t inverted_factorial_odd_part[] = { - 0x0000000000000001u, 0x0000000000000001u, 0x0000000000000001u, 0xaaaaaaaaaaaaaaabu, - 0xaaaaaaaaaaaaaaabu, 0xeeeeeeeeeeeeeeefu, 0x4fa4fa4fa4fa4fa5u, 0x2ff2ff2ff2ff2ff3u, - 0x2ff2ff2ff2ff2ff3u, 0x938cc70553e3771bu, 0xb71c27cddd93e49fu, 0xb38e3229fcdee63du, - 0xe684bb63544a4cbfu, 0xc2f684917ca340fbu, 0xf747c9cba417526du, 0xbb26eb51d7bd49c3u, - 0xbb26eb51d7bd49c3u, 0xb0a7efb985294093u, 0xbe4b8c69f259eabbu, 0x6854d17ed6dc4fb9u, - 0xe1aa904c915f4325u, 0x3b8206df131cead1u, 0x79c6009fea76fe13u, 0xd8c5d381633cd365u, - 0x4841f12b21144677u, 0x4a91ff68200b0d0fu, 0x8f9513a58c4f9e8bu, 0x2b3e690621a42251u, - 0x4f520f00e03c04e7u, 0x2edf84ee600211d3u, 0xadcaa2764aaacdfdu, 0x161f4f9033f4fe63u, - 0x161f4f9033f4fe63u, 0xbada2932ea4d3e03u, 0xcec189f3efaa30d3u, 0xf7475bb68330bf91u, - 0x37eb7bf7d5b01549u, 0x46b35660a4e91555u, 0xa567c12d81f151f7u, 0x4c724007bb2071b1u, - 0x0f4a0cce58a016bdu, 0xfa21068e66106475u, 0x244ab72b5a318ae1u, 0x366ce67e080d0f23u, - 0xd666fdae5dd2a449u, 0xd740ddd0acc06a0du, 0xb050bbbb28e6f97bu, 0x70b003fe890a5c75u, - 0xd03aabff83037427u, 0x13ec4ca72c783bd7u, 0x90282c06afdbd96fu, 0x4414ddb9db4a95d5u, - 0xa2c68735ae6832e9u, 0xbf72d71455676665u, 0xa8469fab6b759b7fu, 0xc1e55b56e606caf9u, - 0x40455630fc4a1cffu, 0x0120a7b0046d16f7u, 0xa7c3553b08faef23u, 0x9f0bfd1b08d48639u, - 0xa433ffce9a304d37u, 0xa22ad1d53915c683u, 0xcb6cbc723ba5dd1du, 0x547fb1b8ab9d0ba3u, - 0x547fb1b8ab9d0ba3u, 0x8f15a826498852e3u, 0x32e1a03f38880283u, 0x3de4cce63283f0c1u, - 0x5dfe6667e4da95b1u, 0xfda6eeeef479e47du, 0xf14de991cc7882dfu, 0xe68db79247630ca9u, - 0xa7d6db8207ee8fa1u, 0x255e1f0fcf034499u, 0xc9a8990e43dd7e65u, 0x3279b6f289702e0fu, - 0xe7b5905d9b71b195u, 0x03025ba41ff0da69u, 0xb7df3d6d3be55aefu, 0xf89b212ebff2b361u, - 0xfe856d095996f0adu, 0xd6e533e9fdf20f9du, 0xf8c0e84a63da3255u, 0xa677876cd91b4db7u, - 0x07ed4f97780d7d9bu, 0x90a8705f258db62fu, 0xa41bbb2be31b1c0du, 0x6ec28690b038383bu, - 0xdb860c3bb2edd691u, 0x0838286838a980f9u, 0x558417a74b36f77du, 0x71779afc3646ef07u, - 0x743cda377ccb6e91u, 0x7fdf9f3fe89153c5u, 0xdc97d25df49b9a4bu, 0x76321a778eb37d95u, - 0x7cbb5e27da3bd487u, 0x9cff4ade1a009de7u, 0x70eb166d05c15197u, 0xdcf0460b71d5fe3du, - 0x5ac1ee5260b6a3c5u, 0xc922dedfdd78efe1u, 0xe5d381dc3b8eeb9bu, 0xd57e5347bafc6aadu, - 0x86939040983acd21u, 0x395b9d69740a4ff9u, 0x1467299c8e43d135u, 0x5fe440fcad975cdfu, - 0xcaa9a39794a6ca8du, 0xf61dbd640868dea1u, 0xac09d98d74843be7u, 0x2b103b9e1a6b4809u, - 0x2ab92d16960f536fu, 0x6653323d5e3681dfu, 0xefd48c1c0624e2d7u, 0xa496fefe04816f0du, - 0x1754a7b07bbdd7b1u, 0x23353c829a3852cdu, 0xbf831261abd59097u, 0x57a8e656df0618e1u, - 0x16e9206c3100680fu, 0xadad4c6ee921dac7u, 0x635f2b3860265353u, 0xdd6d0059f44b3d09u, - 0xac4dd6b894447dd7u, 0x42ea183eeaa87be3u, 0x15612d1550ee5b5du, 0x226fa19d656cb623u, -}; - -/* exponent of the largest power of 2 dividing factorial(n), for n in range(68) - -Python code to generate the values: - -import math - -for n in range(128): - fac = math.factorial(n) - fac_trailing_zeros = (fac & -fac).bit_length() - 1 - print(fac_trailing_zeros) -*/ - -static const uint8_t factorial_trailing_zeros[] = { - 0, 0, 1, 1, 3, 3, 4, 4, 7, 7, 8, 8, 10, 10, 11, 11, // 0-15 - 15, 15, 16, 16, 18, 18, 19, 19, 22, 22, 23, 23, 25, 25, 26, 26, // 16-31 - 31, 31, 32, 32, 34, 34, 35, 35, 38, 38, 39, 39, 41, 41, 42, 42, // 32-47 - 46, 46, 47, 47, 49, 49, 50, 50, 53, 53, 54, 54, 56, 56, 57, 57, // 48-63 - 63, 63, 64, 64, 66, 66, 67, 67, 70, 70, 71, 71, 73, 73, 74, 74, // 64-79 - 78, 78, 79, 79, 81, 81, 82, 82, 85, 85, 86, 86, 88, 88, 89, 89, // 80-95 - 94, 94, 95, 95, 97, 97, 98, 98, 101, 101, 102, 102, 104, 104, 105, 105, // 96-111 - 109, 109, 110, 110, 112, 112, 113, 113, 116, 116, 117, 117, 119, 119, 120, 120, // 112-127 -}; - -/* Number of permutations and combinations. - * P(n, k) = n! / (n-k)! - * C(n, k) = P(n, k) / k! - */ - -/* Calculate C(n, k) for n in the 63-bit range. */ -static PyObject * -perm_comb_small(unsigned long long n, unsigned long long k, int iscomb) -{ - assert(k != 0); - - /* For small enough n and k the result fits in the 64-bit range and can - * be calculated without allocating intermediate PyLong objects. */ - if (iscomb) { - /* Maps k to the maximal n so that 2*k-1 <= n <= 127 and C(n, k) - * fits into a uint64_t. Exclude k = 1, because the second fast - * path is faster for this case.*/ - static const unsigned char fast_comb_limits1[] = { - 0, 0, 127, 127, 127, 127, 127, 127, // 0-7 - 127, 127, 127, 127, 127, 127, 127, 127, // 8-15 - 116, 105, 97, 91, 86, 82, 78, 76, // 16-23 - 74, 72, 71, 70, 69, 68, 68, 67, // 24-31 - 67, 67, 67, // 32-34 - }; - if (k < Py_ARRAY_LENGTH(fast_comb_limits1) && n <= fast_comb_limits1[k]) { - /* - comb(n, k) fits into a uint64_t. We compute it as - - comb_odd_part << shift - - where 2**shift is the largest power of two dividing comb(n, k) - and comb_odd_part is comb(n, k) >> shift. comb_odd_part can be - calculated efficiently via arithmetic modulo 2**64, using three - lookups and two uint64_t multiplications. - */ - uint64_t comb_odd_part = reduced_factorial_odd_part[n] - * inverted_factorial_odd_part[k] - * inverted_factorial_odd_part[n - k]; - int shift = factorial_trailing_zeros[n] - - factorial_trailing_zeros[k] - - factorial_trailing_zeros[n - k]; - return PyLong_FromUnsignedLongLong(comb_odd_part << shift); - } - - /* Maps k to the maximal n so that 2*k-1 <= n <= 127 and C(n, k)*k - * fits into a long long (which is at least 64 bit). Only contains - * items larger than in fast_comb_limits1. */ - static const unsigned long long fast_comb_limits2[] = { - 0, ULLONG_MAX, 4294967296ULL, 3329022, 102570, 13467, 3612, 1449, // 0-7 - 746, 453, 308, 227, 178, 147, // 8-13 - }; - if (k < Py_ARRAY_LENGTH(fast_comb_limits2) && n <= fast_comb_limits2[k]) { - /* C(n, k) = C(n, k-1) * (n-k+1) / k */ - unsigned long long result = n; - for (unsigned long long i = 1; i < k;) { - result *= --n; - result /= ++i; - } - return PyLong_FromUnsignedLongLong(result); - } - } - else { - /* Maps k to the maximal n so that k <= n and P(n, k) - * fits into a long long (which is at least 64 bit). */ - static const unsigned long long fast_perm_limits[] = { - 0, ULLONG_MAX, 4294967296ULL, 2642246, 65537, 7133, 1627, 568, // 0-7 - 259, 142, 88, 61, 45, 36, 30, 26, // 8-15 - 24, 22, 21, 20, 20, // 16-20 - }; - if (k < Py_ARRAY_LENGTH(fast_perm_limits) && n <= fast_perm_limits[k]) { - if (n <= 127) { - /* P(n, k) fits into a uint64_t. */ - uint64_t perm_odd_part = reduced_factorial_odd_part[n] - * inverted_factorial_odd_part[n - k]; - int shift = factorial_trailing_zeros[n] - - factorial_trailing_zeros[n - k]; - return PyLong_FromUnsignedLongLong(perm_odd_part << shift); - } - - /* P(n, k) = P(n, k-1) * (n-k+1) */ - unsigned long long result = n; - for (unsigned long long i = 1; i < k;) { - result *= --n; - ++i; - } - return PyLong_FromUnsignedLongLong(result); - } - } - - /* For larger n use recursive formulas: - * - * P(n, k) = P(n, j) * P(n-j, k-j) - * C(n, k) = C(n, j) * C(n-j, k-j) // C(k, j) - */ - unsigned long long j = k / 2; - PyObject *a, *b; - a = perm_comb_small(n, j, iscomb); - if (a == NULL) { - return NULL; - } - b = perm_comb_small(n - j, k - j, iscomb); - if (b == NULL) { - goto error; - } - Py_SETREF(a, PyNumber_Multiply(a, b)); - Py_DECREF(b); - if (iscomb && a != NULL) { - b = perm_comb_small(k, j, 1); - if (b == NULL) { - goto error; - } - Py_SETREF(a, PyNumber_FloorDivide(a, b)); - Py_DECREF(b); - } - return a; - -error: - Py_DECREF(a); - return NULL; -} - -/* Calculate P(n, k) or C(n, k) using recursive formulas. - * It is more efficient than sequential multiplication thanks to - * Karatsuba multiplication. - */ -static PyObject * -perm_comb(PyObject *n, unsigned long long k, int iscomb) -{ - if (k == 0) { - return PyLong_FromLong(1); - } - if (k == 1) { - return Py_NewRef(n); - } - - /* P(n, k) = P(n, j) * P(n-j, k-j) */ - /* C(n, k) = C(n, j) * C(n-j, k-j) // C(k, j) */ - unsigned long long j = k / 2; - PyObject *a, *b; - a = perm_comb(n, j, iscomb); - if (a == NULL) { - return NULL; - } - PyObject *t = PyLong_FromUnsignedLongLong(j); - if (t == NULL) { - goto error; - } - n = PyNumber_Subtract(n, t); - Py_DECREF(t); - if (n == NULL) { - goto error; - } - b = perm_comb(n, k - j, iscomb); - Py_DECREF(n); - if (b == NULL) { - goto error; - } - Py_SETREF(a, PyNumber_Multiply(a, b)); - Py_DECREF(b); - if (iscomb && a != NULL) { - b = perm_comb_small(k, j, 1); - if (b == NULL) { - goto error; - } - Py_SETREF(a, PyNumber_FloorDivide(a, b)); - Py_DECREF(b); - } - return a; - -error: - Py_DECREF(a); - return NULL; -} - -/*[clinic input] -math.perm - - n: object - k: object = None - / - -Number of ways to choose k items from n items without repetition and with order. - -Evaluates to n! / (n - k)! when k <= n and evaluates -to zero when k > n. - -If k is not specified or is None, then k defaults to n -and the function returns n!. - -Raises TypeError if either of the arguments are not integers. -Raises ValueError if either of the arguments are negative. -[clinic start generated code]*/ - -static PyObject * -math_perm_impl(PyObject *module, PyObject *n, PyObject *k) -/*[clinic end generated code: output=e021a25469653e23 input=5311c5a00f359b53]*/ -{ - PyObject *result = NULL; - int overflow, cmp; - long long ki, ni; - - if (k == Py_None) { - return math_factorial(module, n); - } - n = PyNumber_Index(n); - if (n == NULL) { - return NULL; - } - k = PyNumber_Index(k); - if (k == NULL) { - Py_DECREF(n); - return NULL; - } - assert(PyLong_CheckExact(n) && PyLong_CheckExact(k)); - - if (_PyLong_IsNegative((PyLongObject *)n)) { - PyErr_SetString(PyExc_ValueError, - "n must be a non-negative integer"); - goto error; - } - if (_PyLong_IsNegative((PyLongObject *)k)) { - PyErr_SetString(PyExc_ValueError, - "k must be a non-negative integer"); - goto error; - } - - cmp = PyObject_RichCompareBool(n, k, Py_LT); - if (cmp != 0) { - if (cmp > 0) { - result = PyLong_FromLong(0); - goto done; - } - goto error; - } - - ki = PyLong_AsLongLongAndOverflow(k, &overflow); - assert(overflow >= 0 && !PyErr_Occurred()); - if (overflow > 0) { - PyErr_Format(PyExc_OverflowError, - "k must not exceed %lld", - LLONG_MAX); - goto error; - } - assert(ki >= 0); - - ni = PyLong_AsLongLongAndOverflow(n, &overflow); - assert(overflow >= 0 && !PyErr_Occurred()); - if (!overflow && ki > 1) { - assert(ni >= 0); - result = perm_comb_small((unsigned long long)ni, - (unsigned long long)ki, 0); - } - else { - result = perm_comb(n, (unsigned long long)ki, 0); - } - -done: - Py_DECREF(n); - Py_DECREF(k); - return result; - -error: - Py_DECREF(n); - Py_DECREF(k); - return NULL; -} - -/*[clinic input] -math.comb - - n: object - k: object - / - -Number of ways to choose k items from n items without repetition and without order. - -Evaluates to n! / (k! * (n - k)!) when k <= n and evaluates -to zero when k > n. - -Also called the binomial coefficient because it is equivalent -to the coefficient of k-th term in polynomial expansion of the -expression (1 + x)**n. - -Raises TypeError if either of the arguments are not integers. -Raises ValueError if either of the arguments are negative. - -[clinic start generated code]*/ - -static PyObject * -math_comb_impl(PyObject *module, PyObject *n, PyObject *k) -/*[clinic end generated code: output=bd2cec8d854f3493 input=9a05315af2518709]*/ -{ - PyObject *result = NULL, *temp; - int overflow, cmp; - long long ki, ni; - - n = PyNumber_Index(n); - if (n == NULL) { - return NULL; - } - k = PyNumber_Index(k); - if (k == NULL) { - Py_DECREF(n); - return NULL; - } - assert(PyLong_CheckExact(n) && PyLong_CheckExact(k)); - - if (_PyLong_IsNegative((PyLongObject *)n)) { - PyErr_SetString(PyExc_ValueError, - "n must be a non-negative integer"); - goto error; - } - if (_PyLong_IsNegative((PyLongObject *)k)) { - PyErr_SetString(PyExc_ValueError, - "k must be a non-negative integer"); - goto error; - } - - ni = PyLong_AsLongLongAndOverflow(n, &overflow); - assert(overflow >= 0 && !PyErr_Occurred()); - if (!overflow) { - assert(ni >= 0); - ki = PyLong_AsLongLongAndOverflow(k, &overflow); - assert(overflow >= 0 && !PyErr_Occurred()); - if (overflow || ki > ni) { - result = PyLong_FromLong(0); - goto done; - } - assert(ki >= 0); - - ki = Py_MIN(ki, ni - ki); - if (ki > 1) { - result = perm_comb_small((unsigned long long)ni, - (unsigned long long)ki, 1); - goto done; - } - /* For k == 1 just return the original n in perm_comb(). */ - } - else { - /* k = min(k, n - k) */ - temp = PyNumber_Subtract(n, k); - if (temp == NULL) { - goto error; - } - assert(PyLong_Check(temp)); - if (_PyLong_IsNegative((PyLongObject *)temp)) { - Py_DECREF(temp); - result = PyLong_FromLong(0); - goto done; - } - cmp = PyObject_RichCompareBool(temp, k, Py_LT); - if (cmp > 0) { - Py_SETREF(k, temp); - } - else { - Py_DECREF(temp); - if (cmp < 0) { - goto error; - } - } - - ki = PyLong_AsLongLongAndOverflow(k, &overflow); - assert(overflow >= 0 && !PyErr_Occurred()); - if (overflow) { - PyErr_Format(PyExc_OverflowError, - "min(n - k, k) must not exceed %lld", - LLONG_MAX); - goto error; - } - assert(ki >= 0); - } - - result = perm_comb(n, (unsigned long long)ki, 1); - -done: - Py_DECREF(n); - Py_DECREF(k); - return result; - -error: - Py_DECREF(n); - Py_DECREF(k); - return NULL; -} - - /*[clinic input] +@permit_long_summary math.nextafter x: double @@ -4030,13 +3018,13 @@ Return the floating-point value the given number of steps after x towards y. If steps is not specified or is None, it defaults to 1. -Raises a TypeError, if x or y is not a double, or if steps is not an integer. -Raises ValueError if steps is negative. +Raises a TypeError, if x or y is not a double, or if steps is not +an integer. Raises ValueError if steps is negative. [clinic start generated code]*/ static PyObject * math_nextafter_impl(PyObject *module, double x, double y, PyObject *steps) -/*[clinic end generated code: output=cc6511f02afc099e input=7f2a5842112af2b4]*/ +/*[clinic end generated code: output=cc6511f02afc099e input=3a9151e6b1e9f346]*/ { #if defined(_AIX) if (x == y) { @@ -4169,7 +3157,7 @@ math_ulp_impl(PyObject *module, double x) if (isinf(x)) { return x; } - double inf = Py_INFINITY; + double inf = INFINITY; double x2 = nextafter(x, inf); if (isinf(x2)) { /* special case: x is the largest positive representable float */ @@ -4193,28 +3181,59 @@ math_exec(PyObject *module) if (PyModule_Add(module, "tau", PyFloat_FromDouble(Py_MATH_TAU)) < 0) { return -1; } - if (PyModule_Add(module, "inf", PyFloat_FromDouble(Py_INFINITY)) < 0) { + if (PyModule_Add(module, "inf", PyFloat_FromDouble(INFINITY)) < 0) { return -1; } if (PyModule_Add(module, "nan", PyFloat_FromDouble(fabs(Py_NAN))) < 0) { return -1; } + + PyObject *intmath = PyImport_ImportModule("_math_integer"); + if (!intmath) { + return -1; + } +#define IMPORT_FROM_INTMATH(NAME) do { \ + if (PyModule_Add(module, #NAME, \ + PyObject_GetAttrString(intmath, #NAME)) < 0) { \ + Py_DECREF(intmath); \ + return -1; \ + } \ + } while(0) + + IMPORT_FROM_INTMATH(comb); + IMPORT_FROM_INTMATH(factorial); + IMPORT_FROM_INTMATH(gcd); + IMPORT_FROM_INTMATH(isqrt); + IMPORT_FROM_INTMATH(lcm); + IMPORT_FROM_INTMATH(perm); + if (_PyImport_SetModuleString("math.integer", intmath) < 0) { + Py_DECREF(intmath); + return -1; + } + if (PyModule_Add(module, "integer", intmath) < 0) { + return -1; + } return 0; } static PyMethodDef math_methods[] = { {"acos", math_acos, METH_O, math_acos_doc}, {"acosh", math_acosh, METH_O, math_acosh_doc}, + {"acospi", math_acospi, METH_O, math_acospi_doc}, {"asin", math_asin, METH_O, math_asin_doc}, {"asinh", math_asinh, METH_O, math_asinh_doc}, + {"asinpi", math_asinpi, METH_O, math_asinpi_doc}, {"atan", math_atan, METH_O, math_atan_doc}, {"atan2", _PyCFunction_CAST(math_atan2), METH_FASTCALL, math_atan2_doc}, {"atanh", math_atanh, METH_O, math_atanh_doc}, + {"atan2pi", _PyCFunction_CAST(math_atan2pi), METH_FASTCALL, math_atan2pi_doc}, + {"atanpi", math_atanpi, METH_O, math_atanpi_doc}, {"cbrt", math_cbrt, METH_O, math_cbrt_doc}, MATH_CEIL_METHODDEF {"copysign", _PyCFunction_CAST(math_copysign), METH_FASTCALL, math_copysign_doc}, {"cos", math_cos, METH_O, math_cos_doc}, {"cosh", math_cosh, METH_O, math_cosh_doc}, + {"cospi", math_cospi, METH_O, math_cospi_doc}, MATH_DEGREES_METHODDEF MATH_DIST_METHODDEF {"erf", math_erf, METH_O, math_erf_doc}, @@ -4223,7 +3242,6 @@ static PyMethodDef math_methods[] = { {"exp2", math_exp2, METH_O, math_exp2_doc}, {"expm1", math_expm1, METH_O, math_expm1_doc}, {"fabs", math_fabs, METH_O, math_fabs_doc}, - MATH_FACTORIAL_METHODDEF MATH_FLOOR_METHODDEF MATH_FMA_METHODDEF MATH_FMAX_METHODDEF @@ -4232,7 +3250,6 @@ static PyMethodDef math_methods[] = { MATH_FREXP_METHODDEF MATH_FSUM_METHODDEF {"gamma", math_gamma, METH_O, math_gamma_doc}, - MATH_GCD_METHODDEF MATH_HYPOT_METHODDEF MATH_ISCLOSE_METHODDEF MATH_ISFINITE_METHODDEF @@ -4240,8 +3257,6 @@ static PyMethodDef math_methods[] = { MATH_ISSUBNORMAL_METHODDEF MATH_ISINF_METHODDEF MATH_ISNAN_METHODDEF - MATH_ISQRT_METHODDEF - MATH_LCM_METHODDEF MATH_LDEXP_METHODDEF {"lgamma", math_lgamma, METH_O, math_lgamma_doc}, {"log", _PyCFunction_CAST(math_log), METH_FASTCALL, math_log_doc}, @@ -4255,20 +3270,21 @@ static PyMethodDef math_methods[] = { MATH_SIGNBIT_METHODDEF {"sin", math_sin, METH_O, math_sin_doc}, {"sinh", math_sinh, METH_O, math_sinh_doc}, + {"sinpi", math_sinpi, METH_O, math_sinpi_doc}, {"sqrt", math_sqrt, METH_O, math_sqrt_doc}, {"tan", math_tan, METH_O, math_tan_doc}, {"tanh", math_tanh, METH_O, math_tanh_doc}, + {"tanpi", math_tanpi, METH_O, math_tanpi_doc}, MATH_SUMPROD_METHODDEF MATH_TRUNC_METHODDEF MATH_PROD_METHODDEF - MATH_PERM_METHODDEF - MATH_COMB_METHODDEF MATH_NEXTAFTER_METHODDEF MATH_ULP_METHODDEF {NULL, NULL} /* sentinel */ }; static PyModuleDef_Slot math_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, math_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/md5module.c b/Modules/md5module.c index 8b6dd4a8195dfb..063be1405dd51f 100644 --- a/Modules/md5module.c +++ b/Modules/md5module.c @@ -22,7 +22,8 @@ #endif #include "Python.h" -#include "pycore_strhex.h" // _Py_strhex() +#include "pycore_object.h" // _PyObject_VisitType() +#include "pycore_strhex.h" // _Py_strhex() #include "hashlib.h" @@ -82,18 +83,14 @@ newMD5object(MD5State * st) } /* Internal methods for a hash object */ -static int -MD5_traverse(PyObject *ptr, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(ptr)); - return 0; -} - static void MD5_dealloc(PyObject *op) { MD5object *ptr = _MD5object_CAST(op); - Hacl_Hash_MD5_free(ptr->hash_state); + if (ptr->hash_state != NULL) { + Hacl_Hash_MD5_free(ptr->hash_state); + ptr->hash_state = NULL; + } PyTypeObject *tp = Py_TYPE(op); PyObject_GC_UnTrack(ptr); PyObject_GC_Del(ptr); @@ -246,7 +243,7 @@ static PyType_Slot md5_type_slots[] = { {Py_tp_dealloc, MD5_dealloc}, {Py_tp_methods, MD5_methods}, {Py_tp_getset, MD5_getseters}, - {Py_tp_traverse, MD5_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {0,0} }; @@ -368,6 +365,7 @@ md5_exec(PyObject *m) } static PyModuleDef_Slot _md5_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, md5_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 0cb4b62d734550..6fb04ba7bd47c6 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -26,6 +26,7 @@ #include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t() #include "pycore_bytesobject.h" // _PyBytes_Find() #include "pycore_fileutils.h" // _Py_stat_struct +#include "pycore_mmap.h" // _PyAnnotateMemoryMap() #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() #include <stddef.h> // offsetof() @@ -91,6 +92,12 @@ my_getpagesize(void) # define MAP_ANONYMOUS MAP_ANON #endif +/*[clinic input] +module mmap +class mmap.mmap "mmap_object *" "" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=82a9f8a529905b9b]*/ + typedef enum { ACCESS_DEFAULT, @@ -119,20 +126,34 @@ typedef struct { #ifdef UNIX int fd; - _Bool trackfd; + int flags; #endif PyObject *weakreflist; access_mode access; + _Bool trackfd; } mmap_object; #define mmap_object_CAST(op) ((mmap_object *)(op)) -static int -mmap_object_traverse(PyObject *op, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(op)); - return 0; +#include "clinic/mmapmodule.c.h" + + +/* Return a Py_ssize_t from the object arg. This conversion logic is similar + to what AC uses for `Py_ssize_t` arguments. + + Returns -1 on error. Use PyErr_Occurred() to disambiguate. +*/ +static Py_ssize_t +_As_Py_ssize_t(PyObject *arg) { + assert(arg != NULL); + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(arg); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + return ival; } static void @@ -171,10 +192,16 @@ mmap_object_dealloc(PyObject *op) Py_DECREF(tp); } +/*[clinic input] +@critical_section +mmap.mmap.close + +[clinic start generated code]*/ + static PyObject * -mmap_close_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_close_impl(mmap_object *self) +/*[clinic end generated code: output=a1ae0c727546f78d input=25020035f047eae1]*/ { - mmap_object *self = mmap_object_CAST(op); if (self->exports > 0) { PyErr_SetString(PyExc_BufferError, "cannot close "\ "exported pointers exist"); @@ -454,7 +481,8 @@ _safe_PyBytes_ReverseFind(Py_ssize_t *out, mmap_object *self, } PyObject * -_safe_PyBytes_FromStringAndSize(char *start, size_t num_bytes) { +_safe_PyBytes_FromStringAndSize(char *start, size_t num_bytes) +{ if (num_bytes == 1) { char dest; if (safe_byte_copy(&dest, start) < 0) { @@ -465,21 +493,28 @@ _safe_PyBytes_FromStringAndSize(char *start, size_t num_bytes) { } } else { - PyObject *result = PyBytes_FromStringAndSize(NULL, num_bytes); - if (result == NULL) { + PyBytesWriter *writer = PyBytesWriter_Create(num_bytes); + if (writer == NULL) { return NULL; } - if (safe_memcpy(PyBytes_AS_STRING(result), start, num_bytes) < 0) { - Py_CLEAR(result); + if (safe_memcpy(PyBytesWriter_GetData(writer), start, num_bytes) < 0) { + PyBytesWriter_Discard(writer); + return NULL; } - return result; + return PyBytesWriter_Finish(writer); } } +/*[clinic input] +@critical_section +mmap.mmap.read_byte + +[clinic start generated code]*/ + static PyObject * -mmap_read_byte_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_read_byte_impl(mmap_object *self) +/*[clinic end generated code: output=d931da1319f3869b input=5b8c6a904bdddda9]*/ { - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); if (self->pos >= self->size) { PyErr_SetString(PyExc_ValueError, "read byte out of range"); @@ -493,12 +528,18 @@ mmap_read_byte_method(PyObject *op, PyObject *Py_UNUSED(ignored)) return PyLong_FromLong((unsigned char) dest); } +/*[clinic input] +@critical_section +mmap.mmap.readline + +[clinic start generated code]*/ + static PyObject * -mmap_read_line_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_readline_impl(mmap_object *self) +/*[clinic end generated code: output=b9d2bf9999283311 input=2c4efd1d06e1cdd1]*/ { Py_ssize_t remaining; char *start, *eol; - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); @@ -523,16 +564,22 @@ mmap_read_line_method(PyObject *op, PyObject *Py_UNUSED(ignored)) return result; } +/*[clinic input] +@critical_section +mmap.mmap.read + + n as num_bytes: object(converter='_Py_convert_optional_to_ssize_t', type='Py_ssize_t', c_default='PY_SSIZE_T_MAX') = None + / + +[clinic start generated code]*/ + static PyObject * -mmap_read_method(PyObject *op, PyObject *args) +mmap_mmap_read_impl(mmap_object *self, Py_ssize_t num_bytes) +/*[clinic end generated code: output=3b4d4f3704ed0969 input=8f97f361d435e357]*/ { - Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining; - mmap_object *self = mmap_object_CAST(op); + Py_ssize_t remaining; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes)) - return NULL; - CHECK_VALID(NULL); /* silently 'adjust' out-of-range requests */ remaining = (self->pos < self->size) ? self->size - self->pos : 0; @@ -548,81 +595,103 @@ mmap_read_method(PyObject *op, PyObject *args) } static PyObject * -mmap_gfind(mmap_object *self, - PyObject *args, - int reverse) +mmap_gfind_lock_held(mmap_object *self, Py_buffer *view, PyObject *start_obj, + PyObject *end_obj, int reverse) { Py_ssize_t start = self->pos; Py_ssize_t end = self->size; - Py_buffer view; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find", - &view, &start, &end)) { - return NULL; - } - else { - if (start < 0) - start += self->size; - if (start < 0) - start = 0; - else if (start > self->size) - start = self->size; - - if (end < 0) - end += self->size; - if (end < 0) - end = 0; - else if (end > self->size) - end = self->size; - - Py_ssize_t index; - PyObject *result; - CHECK_VALID_OR_RELEASE(NULL, view); - if (end < start) { - result = PyLong_FromSsize_t(-1); + if (start_obj != Py_None) { + start = _As_Py_ssize_t(start_obj); + if (start == -1 && PyErr_Occurred()) { + return NULL; } - else if (reverse) { - assert(0 <= start && start <= end && end <= self->size); - if (_safe_PyBytes_ReverseFind(&index, self, - self->data + start, end - start, - view.buf, view.len, start) < 0) - { - result = NULL; - } - else { - result = PyLong_FromSsize_t(index); + + if (end_obj != Py_None) { + end = _As_Py_ssize_t(end_obj); + if (end == -1 && PyErr_Occurred()) { + return NULL; } } + } + + if (start < 0) + start += self->size; + if (start < 0) + start = 0; + + if (end < 0) + end += self->size; + if (end < 0) + end = 0; + else if (end > self->size) + end = self->size; + + Py_ssize_t index; + PyObject *result; + CHECK_VALID(NULL); + if (end < start) { + result = PyLong_FromSsize_t(-1); + } + else if (reverse) { + assert(0 <= start && start <= end && end <= self->size); + if (_safe_PyBytes_ReverseFind(&index, self, + self->data + start, end - start, + view->buf, view->len, start) < 0) + { + result = NULL; + } else { - assert(0 <= start && start <= end && end <= self->size); - if (_safe_PyBytes_Find(&index, self, - self->data + start, end - start, - view.buf, view.len, start) < 0) - { - result = NULL; - } - else { - result = PyLong_FromSsize_t(index); - } + result = PyLong_FromSsize_t(index); + } + } + else { + assert(0 <= start && start <= end && end <= self->size); + if (_safe_PyBytes_Find(&index, self, + self->data + start, end - start, + view->buf, view->len, start) < 0) + { + result = NULL; + } + else { + result = PyLong_FromSsize_t(index); } - PyBuffer_Release(&view); - return result; } + return result; } +/*[clinic input] +@critical_section +mmap.mmap.find + + view: Py_buffer + start: object = None + end: object = None + / + +[clinic start generated code]*/ + static PyObject * -mmap_find_method(PyObject *op, PyObject *args) +mmap_mmap_find_impl(mmap_object *self, Py_buffer *view, PyObject *start, + PyObject *end) +/*[clinic end generated code: output=ef8878a322f00192 input=0135504494b52c2b]*/ { - mmap_object *self = mmap_object_CAST(op); - return mmap_gfind(self, args, 0); + return mmap_gfind_lock_held(self, view, start, end, 0); } +/*[clinic input] +@critical_section +mmap.mmap.rfind = mmap.mmap.find + +[clinic start generated code]*/ + static PyObject * -mmap_rfind_method(PyObject *op, PyObject *args) +mmap_mmap_rfind_impl(mmap_object *self, Py_buffer *view, PyObject *start, + PyObject *end) +/*[clinic end generated code: output=73b918940d67c2b8 input=8aecdd1f70c06c62]*/ { - mmap_object *self = mmap_object_CAST(op); - return mmap_gfind(self, args, 1); + return mmap_gfind_lock_held(self, view, start, end, 1); } static int @@ -634,6 +703,7 @@ is_writable(mmap_object *self) return 0; } +#if defined(MS_WINDOWS) || defined(HAVE_MREMAP) static int is_resizeable(mmap_object *self) { @@ -642,13 +712,11 @@ is_resizeable(mmap_object *self) "mmap can't resize with extant buffers exported."); return 0; } -#ifdef UNIX if (!self->trackfd) { PyErr_SetString(PyExc_ValueError, "mmap can't resize with trackfd=False."); return 0; } -#endif if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT)) return 1; PyErr_Format(PyExc_TypeError, @@ -656,52 +724,58 @@ is_resizeable(mmap_object *self) return 0; } +#endif /* MS_WINDOWS || HAVE_MREMAP */ + + +/*[clinic input] +@critical_section +mmap.mmap.write + bytes as data: Py_buffer + / + +[clinic start generated code]*/ static PyObject * -mmap_write_method(PyObject *op, PyObject *args) +mmap_mmap_write_impl(mmap_object *self, Py_buffer *data) +/*[clinic end generated code: output=9e97063efb6fb27b input=3f16fa79aa89d6f7]*/ { - Py_buffer data; - mmap_object *self = mmap_object_CAST(op); - CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "y*:write", &data)) - return NULL; - if (!is_writable(self)) { - PyBuffer_Release(&data); return NULL; } - if (self->pos > self->size || self->size - self->pos < data.len) { - PyBuffer_Release(&data); + if (self->pos > self->size || self->size - self->pos < data->len) { PyErr_SetString(PyExc_ValueError, "data out of range"); return NULL; } - CHECK_VALID_OR_RELEASE(NULL, data); + CHECK_VALID(NULL); PyObject *result; - if (safe_memcpy(self->data + self->pos, data.buf, data.len) < 0) { + if (safe_memcpy(self->data + self->pos, data->buf, data->len) < 0) { result = NULL; } else { - self->pos += data.len; - result = PyLong_FromSsize_t(data.len); + self->pos += data->len; + result = PyLong_FromSsize_t(data->len); } - PyBuffer_Release(&data); return result; } +/*[clinic input] +@critical_section +mmap.mmap.write_byte + + byte as value: unsigned_char + / + +[clinic start generated code]*/ + static PyObject * -mmap_write_byte_method(PyObject *op, PyObject *args) +mmap_mmap_write_byte_impl(mmap_object *self, unsigned char value) +/*[clinic end generated code: output=aa11adada9b17510 input=32740bfa174f0991]*/ { - char value; - mmap_object *self = mmap_object_CAST(op); - CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "b:write_byte", &value)) - return(NULL); - if (!is_writable(self)) return NULL; @@ -711,17 +785,23 @@ mmap_write_byte_method(PyObject *op, PyObject *args) return NULL; } - if (safe_byte_copy(self->data + self->pos, &value) < 0) { + if (safe_byte_copy(self->data + self->pos, (const char*)&value) < 0) { return NULL; } self->pos++; Py_RETURN_NONE; } +/*[clinic input] +@critical_section +mmap.mmap.size + +[clinic start generated code]*/ + static PyObject * -mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_size_impl(mmap_object *self) +/*[clinic end generated code: output=c177e65e83a648ff input=f69c072efd2e1595]*/ { - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); #ifdef MS_WINDOWS @@ -740,13 +820,11 @@ mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored)) return PyLong_FromLong((long)low); size = (((long long)high)<<32) + low; return PyLong_FromLongLong(size); - } else { - return PyLong_FromSsize_t(self->size); } #endif /* MS_WINDOWS */ #ifdef UNIX - { + if (self->fd != -1) { struct _Py_stat_struct status; if (_Py_fstat(self->fd, &status) == -1) return NULL; @@ -757,6 +835,14 @@ mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored)) #endif } #endif /* UNIX */ + else if (self->trackfd) { + return PyLong_FromSsize_t(self->size); + } + else { + PyErr_SetString(PyExc_ValueError, + "can't get size with trackfd=False"); + return NULL; + } } /* This assumes that you want the entire file mapped, @@ -768,14 +854,22 @@ mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored)) / new size? */ +#if defined(MS_WINDOWS) || defined(HAVE_MREMAP) +/*[clinic input] +@critical_section +mmap.mmap.resize + + newsize as new_size: Py_ssize_t + / + +[clinic start generated code]*/ + static PyObject * -mmap_resize_method(PyObject *op, PyObject *args) +mmap_mmap_resize_impl(mmap_object *self, Py_ssize_t new_size) +/*[clinic end generated code: output=6f262537ce9c2dcc input=b6b5dee52a41b79f]*/ { - Py_ssize_t new_size; - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "n:resize", &new_size) || - !is_resizeable(self)) { + if (!is_resizeable(self)) { return NULL; } if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) { @@ -867,7 +961,6 @@ mmap_resize_method(PyObject *op, PyObject *args) if (error) { return PyErr_SetFromWindowsErr(error); - return NULL; } /* It's possible for a resize to fail, typically because another mapping is still held against the same underlying file. Even if nothing has @@ -882,13 +975,15 @@ mmap_resize_method(PyObject *op, PyObject *args) #endif /* MS_WINDOWS */ #ifdef UNIX -#ifndef HAVE_MREMAP - PyErr_SetString(PyExc_SystemError, - "mmap: resizing not available--no mremap()"); - return NULL; -#else void *newmap; +#ifdef __linux__ + if (self->fd == -1 && !(self->flags & MAP_PRIVATE) && new_size > self->size) { + PyErr_Format(PyExc_ValueError, + "mmap: can't expand a shared anonymous mapping on Linux"); + return NULL; + } +#endif if (self->fd != -1 && ftruncate(self->fd, self->offset + new_size) == -1) { PyErr_SetFromErrno(PyExc_OSError); return NULL; @@ -911,28 +1006,46 @@ mmap_resize_method(PyObject *op, PyObject *args) self->data = newmap; self->size = new_size; Py_RETURN_NONE; -#endif /* HAVE_MREMAP */ #endif /* UNIX */ } } +#endif /* MS_WINDOWS || HAVE_MREMAP */ + +/*[clinic input] +@critical_section +mmap.mmap.tell + +[clinic start generated code]*/ static PyObject * -mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_tell_impl(mmap_object *self) +/*[clinic end generated code: output=6034958630e1b1d1 input=fd163acacf45c3a5]*/ { - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); return PyLong_FromSize_t(self->pos); } +/*[clinic input] +@critical_section +mmap.mmap.flush + + offset: Py_ssize_t = 0 + size: Py_ssize_t = -1 + / + * + flags: int = 0 + +[clinic start generated code]*/ + static PyObject * -mmap_flush_method(PyObject *op, PyObject *args) +mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size, + int flags) +/*[clinic end generated code: output=4225f4174dc75a53 input=42ba5fb716b6c294]*/ { - Py_ssize_t offset = 0; - mmap_object *self = mmap_object_CAST(op); - Py_ssize_t size = self->size; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size)) - return NULL; + if (size == -1) { + size = self->size - offset; + } if (size < 0 || offset < 0 || self->size - offset < size) { PyErr_SetString(PyExc_ValueError, "flush values out of range"); return NULL; @@ -948,8 +1061,10 @@ mmap_flush_method(PyObject *op, PyObject *args) } Py_RETURN_NONE; #elif defined(UNIX) - /* XXX flags for msync? */ - if (-1 == msync(self->data + offset, size, MS_SYNC)) { + if (flags == 0) { + flags = MS_SYNC; + } + if (-1 == msync(self->data + offset, size, flags)) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -960,60 +1075,122 @@ mmap_flush_method(PyObject *op, PyObject *args) #endif } +/*[clinic input] +@critical_section +mmap.mmap.seek + + pos as dist: Py_ssize_t + whence as how: int = 0 + / + +[clinic start generated code]*/ + static PyObject * -mmap_seek_method(PyObject *op, PyObject *args) +mmap_mmap_seek_impl(mmap_object *self, Py_ssize_t dist, int how) +/*[clinic end generated code: output=00310494e8b8c592 input=e2fda5d081c3db22]*/ { - Py_ssize_t dist; - mmap_object *self = mmap_object_CAST(op); - int how=0; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how)) - return NULL; - else { - Py_ssize_t where; - switch (how) { - case 0: /* relative to start */ - where = dist; - break; - case 1: /* relative to current position */ - if (PY_SSIZE_T_MAX - self->pos < dist) - goto onoutofrange; - where = self->pos + dist; - break; - case 2: /* relative to end */ - if (PY_SSIZE_T_MAX - self->size < dist) - goto onoutofrange; - where = self->size + dist; - break; - default: - PyErr_SetString(PyExc_ValueError, "unknown seek type"); - return NULL; - } - if (where > self->size || where < 0) + Py_ssize_t where; + switch (how) { + case 0: /* relative to start */ + where = dist; + break; + case 1: /* relative to current position */ + if (PY_SSIZE_T_MAX - self->pos < dist) + goto onoutofrange; + where = self->pos + dist; + break; + case 2: /* relative to end */ + if (PY_SSIZE_T_MAX - self->size < dist) goto onoutofrange; - self->pos = where; - return PyLong_FromSsize_t(self->pos); + where = self->size + dist; + break; + default: + PyErr_SetString(PyExc_ValueError, "unknown seek type"); + return NULL; } + if (where > self->size || where < 0) + goto onoutofrange; + self->pos = where; + return PyLong_FromSsize_t(self->pos); onoutofrange: PyErr_SetString(PyExc_ValueError, "seek out of range"); return NULL; } +/*[clinic input] +@critical_section +mmap.mmap.set_name + + name: str + / + +[clinic start generated code]*/ + +static PyObject * +mmap_mmap_set_name_impl(mmap_object *self, const char *name) +/*[clinic end generated code: output=1edaf4fd51277760 input=7c0e2a17ca6d1adc]*/ +{ +#if defined(MAP_ANONYMOUS) && defined(__linux__) + const char *prefix = "cpython:mmap:"; + if (strlen(name) + strlen(prefix) > 79) { + PyErr_SetString(PyExc_ValueError, "name is too long"); + return NULL; + } + if (self->flags & MAP_ANONYMOUS) { + char buf[80]; + sprintf(buf, "%s%s", prefix, name); + if (_PyAnnotateMemoryMap(self->data, self->size, buf) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + Py_RETURN_NONE; + } + else { + /* cannot name non-anonymous mappings */ + PyErr_SetString(PyExc_ValueError, + "Cannot set annotation on non-anonymous mappings"); + return NULL; + } +#else + /* naming not supported on this platform */ + PyErr_SetString(PyExc_NotImplementedError, + "Annotation of mmap is not supported on this platform"); + return NULL; +#endif +} + +/*[clinic input] +mmap.mmap.seekable + +[clinic start generated code]*/ + static PyObject * -mmap_seekable_method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap_seekable_impl(mmap_object *self) +/*[clinic end generated code: output=6311dc3ea300fa38 input=5132505f6e259001]*/ { Py_RETURN_TRUE; } +/*[clinic input] +@critical_section +mmap.mmap.move + + dest: Py_ssize_t + src: Py_ssize_t + count as cnt: Py_ssize_t + / + +[clinic start generated code]*/ + static PyObject * -mmap_move_method(PyObject *op, PyObject *args) +mmap_mmap_move_impl(mmap_object *self, Py_ssize_t dest, Py_ssize_t src, + Py_ssize_t cnt) +/*[clinic end generated code: output=391f549a44181793 input=cf8cfe10d9f6b448]*/ { - Py_ssize_t dest, src, cnt; - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) || - !is_writable(self)) { + if (!is_writable(self)) { return NULL; } else { /* bounds check the values */ @@ -1039,30 +1216,53 @@ static PyObject * mmap_closed_get(PyObject *op, void *Py_UNUSED(closure)) { mmap_object *self = mmap_object_CAST(op); + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); #ifdef MS_WINDOWS - return PyBool_FromLong(self->map_handle == NULL ? 1 : 0); + result = PyBool_FromLong(self->map_handle == NULL ? 1 : 0); #elif defined(UNIX) - return PyBool_FromLong(self->data == NULL ? 1 : 0); + result = PyBool_FromLong(self->data == NULL ? 1 : 0); #endif + Py_END_CRITICAL_SECTION(); + return result; } +/*[clinic input] +@critical_section +mmap.mmap.__enter__ + +[clinic start generated code]*/ + static PyObject * -mmap__enter__method(PyObject *op, PyObject *Py_UNUSED(ignored)) +mmap_mmap___enter___impl(mmap_object *self) +/*[clinic end generated code: output=92cfc59f4c4e2d26 input=a446541fbfe0b890]*/ { - mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); return Py_NewRef(self); } +/*[clinic input] +@critical_section +mmap.mmap.__exit__ + + exc_type: object + exc_value: object + traceback: object + / + +[clinic start generated code]*/ + static PyObject * -mmap__exit__method(PyObject *op, PyObject *Py_UNUSED(args)) +mmap_mmap___exit___impl(mmap_object *self, PyObject *exc_type, + PyObject *exc_value, PyObject *traceback) +/*[clinic end generated code: output=bec7e3e319c1f07e input=5f28e91cf752bc64]*/ { - return mmap_close_method(op, NULL); + return mmap_mmap_close_impl(self); } static PyObject * -mmap__repr__method(PyObject *op) +mmap__repr__method_lock_held(PyObject *op) { mmap_object *mobj = mmap_object_CAST(op); @@ -1106,11 +1306,27 @@ mmap__repr__method(PyObject *op) } } +static PyObject * +mmap__repr__method(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap__repr__method_lock_held(op); + Py_END_CRITICAL_SECTION(); + return result; +} + #ifdef MS_WINDOWS +/*[clinic input] +@critical_section +mmap.mmap.__sizeof__ + +[clinic start generated code]*/ + static PyObject * -mmap__sizeof__method(PyObject *op, PyObject *Py_UNUSED(dummy)) +mmap_mmap___sizeof___impl(mmap_object *self) +/*[clinic end generated code: output=1aed30daff807d09 input=8a648868a089553c]*/ { - mmap_object *self = mmap_object_CAST(op); size_t res = _PyObject_SIZE(Py_TYPE(self)); if (self->tagname) { res += (wcslen(self->tagname) + 1) * sizeof(self->tagname[0]); @@ -1120,18 +1336,26 @@ mmap__sizeof__method(PyObject *op, PyObject *Py_UNUSED(dummy)) #endif #if defined(MS_WINDOWS) && defined(Py_DEBUG) +/*[clinic input] +@critical_section +mmap.mmap._protect + + flNewProtect: unsigned_int(bitwise=True) + start: Py_ssize_t + length: Py_ssize_t + / + +[clinic start generated code]*/ + static PyObject * -mmap_protect_method(PyObject *op, PyObject *args) { - DWORD flNewProtect, flOldProtect; - Py_ssize_t start, length; - mmap_object *self = mmap_object_CAST(op); +mmap_mmap__protect_impl(mmap_object *self, unsigned int flNewProtect, + Py_ssize_t start, Py_ssize_t length) +/*[clinic end generated code: output=a87271a34d1ad6cf input=9170498c5e1482da]*/ +{ + DWORD flOldProtect; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "Inn:protect", &flNewProtect, &start, &length)) { - return NULL; - } - if (!VirtualProtect((void *) (self->data + start), length, flNewProtect, &flOldProtect)) { @@ -1144,18 +1368,32 @@ mmap_protect_method(PyObject *op, PyObject *args) { #endif #ifdef HAVE_MADVISE +/*[clinic input] +@critical_section +mmap.mmap.madvise + + option: int + start: Py_ssize_t = 0 + length as length_obj: object = None + / + +[clinic start generated code]*/ + static PyObject * -mmap_madvise_method(PyObject *op, PyObject *args) +mmap_mmap_madvise_impl(mmap_object *self, int option, Py_ssize_t start, + PyObject *length_obj) +/*[clinic end generated code: output=816be656f08c0e3c input=2d37f7a4c87f1053]*/ { - int option; - Py_ssize_t start = 0, length; - mmap_object *self = mmap_object_CAST(op); + Py_ssize_t length; CHECK_VALID(NULL); - length = self->size; - - if (!PyArg_ParseTuple(args, "i|nn:madvise", &option, &start, &length)) { - return NULL; + if (length_obj == Py_None) { + length = self->size; + } else { + length = _As_Py_ssize_t(length_obj); + if (length == -1 && PyErr_Occurred()) { + return NULL; + } } if (start < 0 || start >= self->size) { @@ -1191,32 +1429,27 @@ static struct PyMemberDef mmap_object_members[] = { }; static struct PyMethodDef mmap_object_methods[] = { - {"close", mmap_close_method, METH_NOARGS}, - {"find", mmap_find_method, METH_VARARGS}, - {"rfind", mmap_rfind_method, METH_VARARGS}, - {"flush", mmap_flush_method, METH_VARARGS}, -#ifdef HAVE_MADVISE - {"madvise", mmap_madvise_method, METH_VARARGS}, -#endif - {"move", mmap_move_method, METH_VARARGS}, - {"read", mmap_read_method, METH_VARARGS}, - {"read_byte", mmap_read_byte_method, METH_NOARGS}, - {"readline", mmap_read_line_method, METH_NOARGS}, - {"resize", mmap_resize_method, METH_VARARGS}, - {"seek", mmap_seek_method, METH_VARARGS}, - {"seekable", mmap_seekable_method, METH_NOARGS}, - {"size", mmap_size_method, METH_NOARGS}, - {"tell", mmap_tell_method, METH_NOARGS}, - {"write", mmap_write_method, METH_VARARGS}, - {"write_byte", mmap_write_byte_method, METH_VARARGS}, - {"__enter__", mmap__enter__method, METH_NOARGS}, - {"__exit__", mmap__exit__method, METH_VARARGS}, -#ifdef MS_WINDOWS - {"__sizeof__", mmap__sizeof__method, METH_NOARGS}, -#ifdef Py_DEBUG - {"_protect", mmap_protect_method, METH_VARARGS}, -#endif // Py_DEBUG -#endif // MS_WINDOWS + MMAP_MMAP_CLOSE_METHODDEF + MMAP_MMAP_FIND_METHODDEF + MMAP_MMAP_RFIND_METHODDEF + MMAP_MMAP_FLUSH_METHODDEF + MMAP_MMAP_MADVISE_METHODDEF + MMAP_MMAP_MOVE_METHODDEF + MMAP_MMAP_READ_METHODDEF + MMAP_MMAP_READ_BYTE_METHODDEF + MMAP_MMAP_READLINE_METHODDEF + MMAP_MMAP_RESIZE_METHODDEF + MMAP_MMAP_SEEK_METHODDEF + MMAP_MMAP_SEEKABLE_METHODDEF + MMAP_MMAP_SET_NAME_METHODDEF + MMAP_MMAP_SIZE_METHODDEF + MMAP_MMAP_TELL_METHODDEF + MMAP_MMAP_WRITE_METHODDEF + MMAP_MMAP_WRITE_BYTE_METHODDEF + MMAP_MMAP___ENTER___METHODDEF + MMAP_MMAP___EXIT___METHODDEF + MMAP_MMAP___SIZEOF___METHODDEF + MMAP_MMAP__PROTECT_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -1229,7 +1462,7 @@ static PyGetSetDef mmap_object_getset[] = { /* Functions for treating an mmap'ed file as a buffer */ static int -mmap_buffer_getbuf(PyObject *op, Py_buffer *view, int flags) +mmap_buffer_getbuf_lock_held(PyObject *op, Py_buffer *view, int flags) { mmap_object *self = mmap_object_CAST(op); CHECK_VALID(-1); @@ -1240,23 +1473,45 @@ mmap_buffer_getbuf(PyObject *op, Py_buffer *view, int flags) return 0; } +static int +mmap_buffer_getbuf(PyObject *op, Py_buffer *view, int flags) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_buffer_getbuf_lock_held(op, view, flags); + Py_END_CRITICAL_SECTION(); + return result; +} + static void mmap_buffer_releasebuf(PyObject *op, Py_buffer *Py_UNUSED(view)) { mmap_object *self = mmap_object_CAST(op); + Py_BEGIN_CRITICAL_SECTION(self); self->exports--; + Py_END_CRITICAL_SECTION(); } static Py_ssize_t -mmap_length(PyObject *op) +mmap_length_lock_held(PyObject *op) { mmap_object *self = mmap_object_CAST(op); CHECK_VALID(-1); return self->size; } +static Py_ssize_t +mmap_length(PyObject *op) +{ + Py_ssize_t result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_length_lock_held(op); + Py_END_CRITICAL_SECTION(); + return result; +} + static PyObject * -mmap_item(PyObject *op, Py_ssize_t i) +mmap_item_lock_held(PyObject *op, Py_ssize_t i) { mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); @@ -1273,7 +1528,16 @@ mmap_item(PyObject *op, Py_ssize_t i) } static PyObject * -mmap_subscript(PyObject *op, PyObject *item) +mmap_item(PyObject *op, Py_ssize_t i) { + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_item_lock_held(op, i); + Py_END_CRITICAL_SECTION(); + return result; +} + +static PyObject * +mmap_subscript_lock_held(PyObject *op, PyObject *item) { mmap_object *self = mmap_object_CAST(op); CHECK_VALID(NULL); @@ -1335,8 +1599,18 @@ mmap_subscript(PyObject *op, PyObject *item) } } +static PyObject * +mmap_subscript(PyObject *op, PyObject *item) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_subscript_lock_held(op, item); + Py_END_CRITICAL_SECTION(); + return result; +} + static int -mmap_ass_item(PyObject *op, Py_ssize_t i, PyObject *v) +mmap_ass_item_lock_held(PyObject *op, Py_ssize_t i, PyObject *v) { const char *buf; mmap_object *self = mmap_object_CAST(op); @@ -1367,7 +1641,17 @@ mmap_ass_item(PyObject *op, Py_ssize_t i, PyObject *v) } static int -mmap_ass_subscript(PyObject *op, PyObject *item, PyObject *value) +mmap_ass_item(PyObject *op, Py_ssize_t i, PyObject *v) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_ass_item_lock_held(op, i, v); + Py_END_CRITICAL_SECTION(); + return result; +} + +static int +mmap_ass_subscript_lock_held(PyObject *op, PyObject *item, PyObject *value) { mmap_object *self = mmap_object_CAST(op); CHECK_VALID(-1); @@ -1463,11 +1747,21 @@ mmap_ass_subscript(PyObject *op, PyObject *item, PyObject *value) } } +static int +mmap_ass_subscript(PyObject *op, PyObject *item, PyObject *value) +{ + int result; + Py_BEGIN_CRITICAL_SECTION(op); + result = mmap_ass_subscript_lock_held(op, item, value); + Py_END_CRITICAL_SECTION(); + return result; +} + static PyObject * new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict); PyDoc_STRVAR(mmap_doc, -"Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\ +"Windows: mmap(fileno, length[, tagname[, access[, offset[, trackfd]]]])\n\ \n\ Maps length bytes from the file specified by the file handle fileno,\n\ and returns a mmap object. If length is larger than the current size\n\ @@ -1499,7 +1793,7 @@ static PyType_Slot mmap_object_slots[] = { {Py_tp_members, mmap_object_members}, {Py_tp_getset, mmap_object_getset}, {Py_tp_getattro, PyObject_GenericGetAttr}, - {Py_tp_traverse, mmap_object_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, /* as sequence */ {Py_sq_length, mmap_length}, @@ -1685,6 +1979,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) else { m_obj->fd = -1; } + m_obj->flags = flags; Py_BEGIN_ALLOW_THREADS m_obj->data = mmap(NULL, map_size, prot, flags, fd, offset); @@ -1702,6 +1997,11 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) PyErr_SetFromErrno(PyExc_OSError); return NULL; } +#ifdef MAP_ANONYMOUS + if (m_obj->flags & MAP_ANONYMOUS) { + (void)_PyAnnotateMemoryMap(m_obj->data, map_size, "cpython:mmap"); + } +#endif m_obj->access = (access_mode)access; return (PyObject *)m_obj; } @@ -1727,16 +2027,17 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) PyObject *tagname = Py_None; DWORD dwErr = 0; int fileno; - HANDLE fh = 0; + HANDLE fh = INVALID_HANDLE_VALUE; int access = (access_mode)ACCESS_DEFAULT; + int trackfd = 1; DWORD flProtect, dwDesiredAccess; static char *keywords[] = { "fileno", "length", "tagname", - "access", "offset", NULL }; + "access", "offset", "trackfd", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|OiL", keywords, + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|OiL$p", keywords, &fileno, &map_size, - &tagname, &access, &offset)) { + &tagname, &access, &offset, &trackfd)) { return NULL; } @@ -1803,22 +2104,27 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) m_obj->map_handle = NULL; m_obj->tagname = NULL; m_obj->offset = offset; + m_obj->trackfd = trackfd; - if (fh) { - /* It is necessary to duplicate the handle, so the - Python code can close it on us */ - if (!DuplicateHandle( - GetCurrentProcess(), /* source process handle */ - fh, /* handle to be duplicated */ - GetCurrentProcess(), /* target proc handle */ - (LPHANDLE)&m_obj->file_handle, /* result */ - 0, /* access - ignored due to options value */ - FALSE, /* inherited by child processes? */ - DUPLICATE_SAME_ACCESS)) { /* options */ - dwErr = GetLastError(); - Py_DECREF(m_obj); - PyErr_SetFromWindowsErr(dwErr); - return NULL; + if (fh != INVALID_HANDLE_VALUE) { + if (trackfd) { + /* It is necessary to duplicate the handle, so the + Python code can close it on us */ + if (!DuplicateHandle( + GetCurrentProcess(), /* source process handle */ + fh, /* handle to be duplicated */ + GetCurrentProcess(), /* target proc handle */ + &fh, /* result */ + 0, /* access - ignored due to options value */ + FALSE, /* inherited by child processes? */ + DUPLICATE_SAME_ACCESS)) /* options */ + { + dwErr = GetLastError(); + Py_DECREF(m_obj); + PyErr_SetFromWindowsErr(dwErr); + return NULL; + } + m_obj->file_handle = fh; } if (!map_size) { DWORD low,high; @@ -1826,7 +2132,8 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) /* low might just happen to have the value INVALID_FILE_SIZE; so we need to check the last error also. */ if (low == INVALID_FILE_SIZE && - (dwErr = GetLastError()) != NO_ERROR) { + (dwErr = GetLastError()) != NO_ERROR) + { Py_DECREF(m_obj); return PyErr_SetFromWindowsErr(dwErr); } @@ -1888,7 +2195,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) off_lo = (DWORD)(offset & 0xFFFFFFFF); /* For files, it would be sufficient to pass 0 as size. For anonymous maps, we have to pass the size explicitly. */ - m_obj->map_handle = CreateFileMappingW(m_obj->file_handle, + m_obj->map_handle = CreateFileMappingW(fh, NULL, flProtect, size_hi, @@ -2028,6 +2335,16 @@ mmap_exec(PyObject *module) ADD_INT_MACRO(module, ACCESS_WRITE); ADD_INT_MACRO(module, ACCESS_COPY); +#ifdef MS_INVALIDATE + ADD_INT_MACRO(module, MS_INVALIDATE); +#endif +#ifdef MS_ASYNC + ADD_INT_MACRO(module, MS_ASYNC); +#endif +#ifdef MS_SYNC + ADD_INT_MACRO(module, MS_SYNC); +#endif + #ifdef HAVE_MADVISE // Conventional advice values #ifdef MADV_NORMAL @@ -2113,6 +2430,7 @@ mmap_exec(PyObject *module) } static PyModuleDef_Slot mmap_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, mmap_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/overlapped.c b/Modules/overlapped.c index 29b7b356648a53..255576cc057cdd 100644 --- a/Modules/overlapped.c +++ b/Modules/overlapped.c @@ -12,6 +12,7 @@ #endif #include "Python.h" +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #define WINDOWS_LEAN_AND_MEAN #include <winsock2.h> @@ -559,7 +560,7 @@ _overlapped_BindLocal_impl(PyObject *module, HANDLE Socket, int Family) ret = bind((SOCKET)Socket, (SOCKADDR*)&addr, sizeof(addr)) != SOCKET_ERROR; } else { - PyErr_SetString(PyExc_ValueError, "expected tuple of length 2 or 4"); + PyErr_SetString(PyExc_ValueError, "Only AF_INET and AF_INET6 families are supported"); return NULL; } @@ -884,18 +885,20 @@ _overlapped.Overlapped.getresult Retrieve result of operation. -If wait is true then it blocks until the operation is finished. If wait -is false and the operation is still pending then an error is raised. +If wait is true then it blocks until the operation is finished. If +wait is false and the operation is still pending then an error is +raised. [clinic start generated code]*/ static PyObject * _overlapped_Overlapped_getresult_impl(OverlappedObject *self, BOOL wait) -/*[clinic end generated code: output=8c9bd04d08994f6c input=aa5b03e9897ca074]*/ +/*[clinic end generated code: output=8c9bd04d08994f6c input=852fbd817cbd2b3d]*/ { DWORD transferred = 0; BOOL ret; DWORD err; PyObject *addr; + PyObject *transferred_obj; if (self->type == TYPE_NONE) { PyErr_SetString(PyExc_ValueError, "operation not yet attempted"); @@ -964,18 +967,12 @@ _overlapped_Overlapped_getresult_impl(OverlappedObject *self, BOOL wait) } // The result is a two item tuple: (message, address) - self->read_from.result = PyTuple_New(2); + self->read_from.result = _PyTuple_FromPairSteal( + Py_NewRef(self->read_from.allocated_buffer), addr); if (self->read_from.result == NULL) { - Py_CLEAR(addr); return NULL; } - // first item: message - PyTuple_SET_ITEM(self->read_from.result, 0, - Py_NewRef(self->read_from.allocated_buffer)); - // second item: address - PyTuple_SET_ITEM(self->read_from.result, 1, addr); - return Py_NewRef(self->read_from.result); case TYPE_READ_FROM_INTO: // unparse the address @@ -986,19 +983,19 @@ _overlapped_Overlapped_getresult_impl(OverlappedObject *self, BOOL wait) return NULL; } + transferred_obj = PyLong_FromUnsignedLong((unsigned long)transferred); + if (transferred_obj == NULL) { + Py_DECREF(addr); + return NULL; + } + // The result is a two item tuple: (number of bytes read, address) - self->read_from_into.result = PyTuple_New(2); + self->read_from_into.result = _PyTuple_FromPairSteal( + transferred_obj, addr); if (self->read_from_into.result == NULL) { - Py_CLEAR(addr); return NULL; } - // first item: number of bytes read - PyTuple_SET_ITEM(self->read_from_into.result, 0, - PyLong_FromUnsignedLong((unsigned long)transferred)); - // second item: address - PyTuple_SET_ITEM(self->read_from_into.result, 1, addr); - return Py_NewRef(self->read_from_into.result); default: return PyLong_FromUnsignedLong((unsigned long) transferred); @@ -1806,7 +1803,7 @@ _overlapped_Overlapped_WSASendTo_impl(OverlappedObject *self, HANDLE handle, case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1873,7 +1870,7 @@ _overlapped_Overlapped_WSARecvFrom_impl(OverlappedObject *self, case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -1914,6 +1911,11 @@ _overlapped_Overlapped_WSARecvFromInto_impl(OverlappedObject *self, } #endif + if (bufobj->len < (Py_ssize_t)size) { + PyErr_SetString(PyExc_ValueError, "nbytes is greater than the length of the buffer"); + return NULL; + } + wsabuf.buf = bufobj->buf; wsabuf.len = size; @@ -1940,7 +1942,7 @@ _overlapped_Overlapped_WSARecvFromInto_impl(OverlappedObject *self, case ERROR_IO_PENDING: Py_RETURN_NONE; default: - self->type = TYPE_NOT_STARTED; + Overlapped_clear(self); return SetFromWindowsErr(err); } } @@ -2073,6 +2075,7 @@ overlapped_exec(PyObject *module) } static PyModuleDef_Slot overlapped_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, overlapped_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index b1a80788bd8115..1f1b7fa729c01c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -20,6 +20,7 @@ #include "pycore_fileutils.h" // _Py_closerange() #include "pycore_import.h" // _PyImport_AcquireLock() #include "pycore_initconfig.h" // _PyStatus_EXCEPTION() +#include "pycore_jit_unwind.h" // _Py_jit_debug_mutex #include "pycore_long.h" // _PyLong_IsNegative() #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_object.h" // _PyObject_LookupSpecial() @@ -27,6 +28,7 @@ #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_signal.h" // Py_NSIG #include "pycore_time.h" // _PyLong_FromTime_t() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #include "pycore_typeobject.h" // _PyType_AddMethod() #ifndef MS_WINDOWS @@ -40,6 +42,7 @@ // --- System includes ------------------------------------------------------ +#include <stddef.h> // offsetof() #include <stdio.h> // ctermid() #include <stdlib.h> // system() @@ -408,6 +411,31 @@ extern char *ctermid_r(char *); # define STRUCT_STAT struct stat #endif +#ifdef HAVE_STATX +/* until we can assume glibc 2.28 at runtime, we must weakly link */ +# pragma weak statx +static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME +#ifdef STATX_MNT_ID + | STATX_MNT_ID +#endif +#ifdef STATX_DIOALIGN + | STATX_DIOALIGN +#endif +#ifdef STATX_MNT_ID_UNIQUE + | STATX_MNT_ID_UNIQUE +#endif +#ifdef STATX_SUBVOL + | STATX_SUBVOL +#endif +#ifdef STATX_WRITE_ATOMIC + | STATX_WRITE_ATOMIC +#endif +#ifdef STATX_DIO_READ_ALIGN + | STATX_DIO_READ_ALIGN +#endif + ); +#endif /* HAVE_STATX */ + #if !defined(EX_OK) && defined(EXIT_SUCCESS) # define EX_OK EXIT_SUCCESS @@ -686,7 +714,15 @@ reset_remotedebug_data(PyThreadState *tstate) { tstate->remote_debugger_support.debugger_pending_call = 0; memset(tstate->remote_debugger_support.debugger_script_path, 0, - Py_MAX_SCRIPT_PATH_SIZE); + _Py_MAX_SCRIPT_PATH_SIZE); +} + +static void +reset_asyncio_state(_PyThreadStateImpl *tstate) +{ + llist_init(&tstate->asyncio_tasks_head); + tstate->asyncio_running_loop = NULL; + tstate->asyncio_running_task = NULL; } @@ -723,8 +759,17 @@ PyOS_AfterFork_Child(void) goto fatal_error; } +#if defined(PY_HAVE_JIT_GDB_UNWIND) + // The child can inherit this mutex locked if another thread held it at + // fork(), but the child itself cannot be inside gdb_jit_register_code(). + // Reinitialize it before any executor cleanup can unregister JIT code. + _Py_jit_debug_mutex = (PyMutex){0}; +#endif + reset_remotedebug_data(tstate); + reset_asyncio_state((_PyThreadStateImpl *)tstate); + // Remove the dead thread states. We "start the world" once we are the only // thread state left to undo the stop the world call in `PyOS_BeforeFork`. // That needs to happen before `_PyThreadState_DeleteList`, because that @@ -1159,6 +1204,9 @@ typedef struct { #endif newfunc statresult_new_orig; PyObject *StatResultType; +#ifdef HAVE_STATX + PyObject *StatxResultType; +#endif PyObject *StatVFSResultType; PyObject *TerminalSizeType; PyObject *TimesResultType; @@ -1241,6 +1289,8 @@ get_posix_state(PyObject *module) * Contains a file descriptor if path.accept_fd was true * and the caller provided a signed integer instead of any * sort of string. + * path.is_fd + * True if path was provided as a file descriptor. * * WARNING: if your "path" parameter is optional, and is * unspecified, path_converter will never get called. @@ -1293,6 +1343,7 @@ typedef struct { const wchar_t *wide; const char *narrow; int fd; + bool is_fd; int value_error; Py_ssize_t length; PyObject *object; @@ -1302,7 +1353,7 @@ typedef struct { #define PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, \ make_wide, suppress_value_error, allow_fd) \ {function_name, argument_name, nullable, nonstrict, make_wide, \ - suppress_value_error, allow_fd, NULL, NULL, -1, 0, 0, NULL, NULL} + suppress_value_error, allow_fd, NULL, NULL, -1, false, 0, 0, NULL, NULL} #ifdef MS_WINDOWS #define PATH_T_INITIALIZE_P(function_name, argument_name, nullable, \ nonstrict, suppress_value_error, allow_fd) \ @@ -1436,6 +1487,7 @@ path_converter(PyObject *o, void *p) } path->wide = NULL; path->narrow = NULL; + path->is_fd = true; goto success_exit; } else { @@ -1595,10 +1647,10 @@ dir_fd_and_fd_invalid(const char *function_name, int dir_fd, int fd) } static int -fd_and_follow_symlinks_invalid(const char *function_name, int fd, +fd_and_follow_symlinks_invalid(const char *function_name, int is_fd, int follow_symlinks) { - if ((fd > 0) && (!follow_symlinks)) { + if (is_fd && (!follow_symlinks)) { PyErr_Format(PyExc_ValueError, "%s: cannot use fd and follow_symlinks together", function_name); @@ -1776,7 +1828,7 @@ convertenviron(void) #ifdef MS_WINDOWS k = PyUnicode_FromWideChar(*e, (Py_ssize_t)(p-*e)); #else - k = PyBytes_FromStringAndSize(*e, (int)(p-*e)); + k = PyBytes_FromStringAndSize(*e, (Py_ssize_t)(p-*e)); #endif if (k == NULL) { Py_DECREF(d); @@ -2313,7 +2365,7 @@ PyDoc_STRVAR(stat_result__doc__, "stat_result: Result from stat, fstat, or lstat.\n\n\ This object may be accessed either as a tuple of\n\ (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\ -or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\ +or via the attributes st_mode, st_ino, st_dev, st_nlink, and so on.\n\ \n\ Posix/windows: If your platform supports st_blksize, st_blocks, st_rdev,\n\ or st_flags, they are available as attributes only.\n\ @@ -2432,7 +2484,7 @@ static PyStructSequence_Field stat_result_fields[] = { #endif static PyStructSequence_Desc stat_result_desc = { - "stat_result", /* name */ + "os.stat_result", /* name; see issue gh-63408 */ stat_result__doc__, /* doc */ stat_result_fields, 10 @@ -2462,7 +2514,7 @@ static PyStructSequence_Field statvfs_result_fields[] = { }; static PyStructSequence_Desc statvfs_result_desc = { - "statvfs_result", /* name */ + "os.statvfs_result", /* name; see issue gh-63408 */ statvfs_result__doc__, /* doc */ statvfs_result_fields, 10 @@ -2487,7 +2539,7 @@ static PyStructSequence_Field waitid_result_fields[] = { }; static PyStructSequence_Desc waitid_result_desc = { - "waitid_result", /* name */ + MODNAME ".waitid_result", /* name */ waitid_result__doc__, /* doc */ waitid_result_fields, 5 @@ -2539,6 +2591,9 @@ _posix_clear(PyObject *module) Py_CLEAR(state->SchedParamType); #endif Py_CLEAR(state->StatResultType); +#ifdef HAVE_STATX + Py_CLEAR(state->StatxResultType); +#endif Py_CLEAR(state->StatVFSResultType); Py_CLEAR(state->TerminalSizeType); Py_CLEAR(state->TimesResultType); @@ -2564,6 +2619,9 @@ _posix_traverse(PyObject *module, visitproc visit, void *arg) Py_VISIT(state->SchedParamType); #endif Py_VISIT(state->StatResultType); +#ifdef HAVE_STATX + Py_VISIT(state->StatxResultType); +#endif Py_VISIT(state->StatVFSResultType); Py_VISIT(state->TerminalSizeType); Py_VISIT(state->TimesResultType); @@ -2584,60 +2642,79 @@ _posix_free(void *module) _posix_clear((PyObject *)module); } -static int -fill_time(PyObject *module, PyObject *v, int s_index, int f_index, int ns_index, time_t sec, unsigned long nsec) -{ - assert(!PyErr_Occurred()); - - int res = -1; - PyObject *s_in_ns = NULL; - PyObject *ns_total = NULL; - PyObject *float_s = NULL; - PyObject *s = _PyLong_FromTime_t(sec); - PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec); - if (!(s && ns_fractional)) { - goto exit; +#define SEC_TO_NS (1000000000LL) +static PyObject * +stat_nanosecond_timestamp(_posixstate *state, time_t sec, unsigned long nsec) +{ +#if SIZEOF_TIME_T == 4 + return PyLong_FromLongLong(sec * SEC_TO_NS + nsec); +#else + /* 1677-09-21 00:12:44 to 2262-04-11 23:47:15 UTC inclusive */ + if ((LLONG_MIN/SEC_TO_NS) <= sec && sec <= (LLONG_MAX/SEC_TO_NS - 1)) { + return PyLong_FromLongLong(sec * SEC_TO_NS + nsec); } + else + { + PyObject *ns_total = NULL; + PyObject *s_in_ns = NULL; + PyObject *s = _PyLong_FromTime_t(sec); + PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec); + if (s == NULL || ns_fractional == NULL) { + goto exit; + } - s_in_ns = PyNumber_Multiply(s, get_posix_state(module)->billion); - if (!s_in_ns) { - goto exit; - } + s_in_ns = PyNumber_Multiply(s, state->billion); + if (s_in_ns == NULL) { + goto exit; + } - ns_total = PyNumber_Add(s_in_ns, ns_fractional); - if (!ns_total) - goto exit; + ns_total = PyNumber_Add(s_in_ns, ns_fractional); - float_s = PyFloat_FromDouble(sec + 1e-9*nsec); - if (!float_s) { - goto exit; + exit: + Py_XDECREF(s); + Py_XDECREF(ns_fractional); + Py_XDECREF(s_in_ns); + return ns_total; } +#endif +} + +static int +fill_time(_posixstate *state, PyObject *v, int s_index, int f_index, + int ns_index, time_t sec, unsigned long nsec) +{ + assert(!PyErr_Occurred()); + assert(nsec < SEC_TO_NS); if (s_index >= 0) { + PyObject *s = _PyLong_FromTime_t(sec); + if (s == NULL) { + return -1; + } PyStructSequence_SET_ITEM(v, s_index, s); - s = NULL; } + if (f_index >= 0) { + PyObject *float_s = PyFloat_FromDouble((double)sec + 1e-9 * nsec); + if (float_s == NULL) { + return -1; + } PyStructSequence_SET_ITEM(v, f_index, float_s); - float_s = NULL; } + if (ns_index >= 0) { + PyObject *ns_total = stat_nanosecond_timestamp(state, sec, nsec); + if (ns_total == NULL) { + return -1; + } PyStructSequence_SET_ITEM(v, ns_index, ns_total); - ns_total = NULL; } assert(!PyErr_Occurred()); - res = 0; - -exit: - Py_XDECREF(s); - Py_XDECREF(ns_fractional); - Py_XDECREF(s_in_ns); - Py_XDECREF(ns_total); - Py_XDECREF(float_s); - return res; + return 0; } +#undef SEC_TO_NS #ifdef MS_WINDOWS static PyObject* @@ -2673,7 +2750,8 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) { assert(!PyErr_Occurred()); - PyObject *StatResultType = get_posix_state(module)->StatResultType; + _posixstate *state = get_posix_state(module); + PyObject *StatResultType = state->StatResultType; PyObject *v = PyStructSequence_New((PyTypeObject *)StatResultType); if (v == NULL) { return NULL; @@ -2727,13 +2805,13 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) #else ansec = mnsec = cnsec = 0; #endif - if (fill_time(module, v, 7, 10, 13, st->st_atime, ansec) < 0) { + if (fill_time(state, v, 7, 10, 13, st->st_atime, ansec) < 0) { goto error; } - if (fill_time(module, v, 8, 11, 14, st->st_mtime, mnsec) < 0) { + if (fill_time(state, v, 8, 11, 14, st->st_mtime, mnsec) < 0) { goto error; } - if (fill_time(module, v, 9, 12, 15, st->st_ctime, cnsec) < 0) { + if (fill_time(state, v, 9, 12, 15, st->st_ctime, cnsec) < 0) { goto error; } @@ -2744,7 +2822,7 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) SET_ITEM(ST_BLOCKS_IDX, PyLong_FromLong((long)st->st_blocks)); #endif #ifdef HAVE_STRUCT_STAT_ST_RDEV - SET_ITEM(ST_RDEV_IDX, PyLong_FromLong((long)st->st_rdev)); + SET_ITEM(ST_RDEV_IDX, _PyLong_FromDev(st->st_rdev)); #endif #ifdef HAVE_STRUCT_STAT_ST_GEN SET_ITEM(ST_GEN_IDX, PyLong_FromLong((long)st->st_gen)); @@ -2761,7 +2839,7 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) SET_ITEM(ST_BIRTHTIME_IDX, PyFloat_FromDouble(bsec + bnsec * 1e-9)); } #elif defined(MS_WINDOWS) - if (fill_time(module, v, -1, ST_BIRTHTIME_IDX, ST_BIRTHTIME_NS_IDX, + if (fill_time(state, v, -1, ST_BIRTHTIME_IDX, ST_BIRTHTIME_NS_IDX, st->st_birthtime, st->st_birthtime_nsec) < 0) { goto error; } @@ -2811,12 +2889,13 @@ posix_do_stat(PyObject *module, const char *function_name, path_t *path, if (path_and_dir_fd_invalid("stat", path, dir_fd) || dir_fd_and_fd_invalid("stat", dir_fd, path->fd) || - fd_and_follow_symlinks_invalid("stat", path->fd, follow_symlinks)) + fd_and_follow_symlinks_invalid("stat", path->is_fd, follow_symlinks)) return NULL; Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) + if (path->is_fd) { result = FSTAT(path->fd, &st); + } #ifdef MS_WINDOWS else if (follow_symlinks) result = win32_stat(path->wide, &st); @@ -3043,25 +3122,22 @@ class path_t_converter(CConverter): type = "path_t" impl_by_reference = True parse_by_reference = True + default_type = () + c_init_default = "<placeholder>" # overridden in pre_render() converter = 'path_converter' def converter_init(self, *, allow_fd=False, make_wide=None, nonstrict=False, nullable=False, suppress_value_error=False): - # right now path_t doesn't support default values. - # to support a default value, you'll need to override initialize(). - if self.default not in (unspecified, None): - fail("Can't specify a default to the path_t converter!") - - if self.c_default not in (None, 'Py_None'): - raise RuntimeError("Can't specify a c_default to the path_t converter!") self.nullable = nullable self.nonstrict = nonstrict self.make_wide = make_wide self.suppress_value_error = suppress_value_error self.allow_fd = allow_fd + if nullable: + self.default_type = NoneType def pre_render(self): def strify(value): @@ -3096,6 +3172,8 @@ class path_t_converter(CConverter): class dir_fd_converter(CConverter): type = 'int' + default_type = NoneType + c_init_default = 'DEFAULT_DIR_FD' def converter_init(self, requires=None): if self.default in (unspecified, None): @@ -3105,6 +3183,9 @@ class dir_fd_converter(CConverter): else: self.converter = 'dir_fd_converter' + def c_default_init(self): + self.c_default = 'DEFAULT_DIR_FD' + class uid_t_converter(CConverter): type = "uid_t" converter = '_Py_Uid_Converter' @@ -3122,17 +3203,6 @@ class dev_t_return_converter(unsigned_long_return_converter): conversion_fn = '_PyLong_FromDev' unsigned_cast = '(dev_t)' -class FSConverter_converter(CConverter): - type = 'PyObject *' - converter = 'PyUnicode_FSConverter' - def converter_init(self): - if self.default is not unspecified: - fail("FSConverter_converter does not support default values") - self.c_default = 'NULL' - - def cleanup(self): - return "Py_XDECREF(" + self.name + ");\n" - class pid_t_converter(CConverter): type = 'pid_t' format_unit = '" _Py_PARSE_PID "' @@ -3196,7 +3266,7 @@ class confname_converter(CConverter): """, argname=argname, converter=self.converter, table=self.table) [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=8189d5ae78244626]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=ddbf3ac90a981122]*/ /*[clinic input] @@ -3238,6 +3308,7 @@ os_stat_impl(PyObject *module, path_t *path, int dir_fd, int follow_symlinks) /*[clinic input] +@permit_long_summary os.lstat path : path_t @@ -3254,13 +3325,392 @@ Equivalent to stat(path, follow_symlinks=False). static PyObject * os_lstat_impl(PyObject *module, path_t *path, int dir_fd) -/*[clinic end generated code: output=ef82a5d35ce8ab37 input=0b7474765927b925]*/ +/*[clinic end generated code: output=ef82a5d35ce8ab37 input=024102124f88e743]*/ { int follow_symlinks = 0; return posix_do_stat(module, "lstat", path, dir_fd, follow_symlinks); } +#ifdef HAVE_STATX +typedef struct { + PyObject_HEAD + dev_t rdev, dev; + struct statx stx; +} Py_statx_result; + +#define Py_statx_result_CAST(op) _Py_CAST(Py_statx_result*, (op)) + +#define M(attr, type, offset, doc) \ + {attr, type, offset, Py_READONLY, PyDoc_STR(doc)} +#define MM(attr, type, member, doc) \ + M(#attr, type, offsetof(Py_statx_result, stx.stx_##member), doc) +#define MX(attr, type, member, doc) \ + M(#attr, type, offsetof(Py_statx_result, member), doc) + +static PyMemberDef pystatx_result_members[] = { + MM(stx_mask, Py_T_UINT, mask, "member validity mask"), + MM(stx_blksize, Py_T_UINT, blksize, "blocksize for filesystem I/O"), + MM(stx_attributes, Py_T_ULONGLONG, attributes, "Linux inode attribute bits"), + MM(stx_attributes_mask, Py_T_ULONGLONG, attributes_mask, + "Mask of supported bits in stx_attributes"), + MM(stx_rdev_major, Py_T_UINT, rdev_major, "represented device major number"), + MM(stx_rdev_minor, Py_T_UINT, rdev_minor, "represented device minor number"), + MX(stx_rdev, Py_T_ULONGLONG, rdev, "device type (if inode device)"), + MM(stx_dev_major, Py_T_UINT, dev_major, "containing device major number"), + MM(stx_dev_minor, Py_T_UINT, dev_minor, "containing device minor number"), + MX(stx_dev, Py_T_ULONGLONG, dev, "device"), + {NULL}, +}; + +#undef MX +#undef MM +#undef M + + +#define STATX_GET_UINT(ATTR, MASK) \ + static PyObject* \ + pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \ + { \ + Py_statx_result *self = Py_statx_result_CAST(op); \ + if (!(self->stx.stx_mask & MASK)) { \ + Py_RETURN_NONE; \ + } \ + unsigned long value = self->stx.ATTR; \ + return PyLong_FromUnsignedLong(value); \ + } + +STATX_GET_UINT(stx_uid, STATX_UID) +STATX_GET_UINT(stx_gid, STATX_GID) +STATX_GET_UINT(stx_nlink, STATX_NLINK) +#ifdef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN +STATX_GET_UINT(stx_dio_mem_align, STATX_DIOALIGN) +STATX_GET_UINT(stx_dio_offset_align, STATX_DIOALIGN) +#endif +#ifdef HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN +STATX_GET_UINT(stx_dio_read_offset_align, STATX_DIO_READ_ALIGN) +#endif +#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN +STATX_GET_UINT(stx_atomic_write_unit_min, STATX_WRITE_ATOMIC) +STATX_GET_UINT(stx_atomic_write_unit_max, STATX_WRITE_ATOMIC) +STATX_GET_UINT(stx_atomic_write_segments_max, STATX_WRITE_ATOMIC) +#endif +#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT +STATX_GET_UINT(stx_atomic_write_unit_max_opt, STATX_WRITE_ATOMIC) +#endif + + +static PyObject* +pystatx_result_get_stx_mode(PyObject *op, void *Py_UNUSED(context)) +{ + Py_statx_result *self = Py_statx_result_CAST(op); + if (!(self->stx.stx_mask & (STATX_TYPE | STATX_MODE))) { + Py_RETURN_NONE; + } + return PyLong_FromUnsignedLong(self->stx.stx_mode); +} + + +#define STATX_GET_ULONGLONG(ATTR, MASK) \ + static PyObject* \ + pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \ + { \ + Py_statx_result *self = Py_statx_result_CAST(op); \ + if (!(self->stx.stx_mask & MASK)) { \ + Py_RETURN_NONE; \ + } \ + unsigned long long value = self->stx.ATTR; \ + return PyLong_FromUnsignedLongLong(value); \ + } + +STATX_GET_ULONGLONG(stx_blocks, STATX_BLOCKS) +STATX_GET_ULONGLONG(stx_ino, STATX_INO) +STATX_GET_ULONGLONG(stx_size, STATX_SIZE) +#if defined(STATX_MNT_ID) && defined(HAVE_STRUCT_STATX_STX_MNT_ID) +STATX_GET_ULONGLONG(stx_mnt_id, STATX_MNT_ID) +#endif +#if defined(STATX_SUBVOL) && defined(HAVE_STRUCT_STATX_STX_SUBVOL) +STATX_GET_ULONGLONG(stx_subvol, STATX_SUBVOL) +#endif + + +#define STATX_GET_DOUBLE(ATTR, MASK) \ + static PyObject* \ + pystatx_result_get_##ATTR(PyObject *op, void *Py_UNUSED(context)) \ + { \ + Py_statx_result *self = Py_statx_result_CAST(op); \ + if (!(self->stx.stx_mask & MASK)) { \ + Py_RETURN_NONE; \ + } \ + struct statx_timestamp *ts = &self->stx.ATTR; \ + double sec = ((double)ts->tv_sec + ts->tv_nsec * 1e-9); \ + return PyFloat_FromDouble(sec); \ + } + +STATX_GET_DOUBLE(stx_atime, STATX_ATIME) +STATX_GET_DOUBLE(stx_btime, STATX_BTIME) +STATX_GET_DOUBLE(stx_ctime, STATX_CTIME) +STATX_GET_DOUBLE(stx_mtime, STATX_MTIME) + +#define STATX_GET_NSEC(ATTR, MEMBER, MASK) \ + static PyObject* \ + pystatx_result_get_##ATTR(PyObject *op, void *context) \ + { \ + Py_statx_result *self = Py_statx_result_CAST(op); \ + if (!(self->stx.stx_mask & MASK)) { \ + Py_RETURN_NONE; \ + } \ + struct statx_timestamp *ts = &self->stx.MEMBER; \ + _posixstate *state = PyType_GetModuleState(Py_TYPE(op)); \ + assert(state != NULL); \ + return stat_nanosecond_timestamp(state, ts->tv_sec, ts->tv_nsec); \ + } + +STATX_GET_NSEC(stx_atime_ns, stx_atime, STATX_ATIME) +STATX_GET_NSEC(stx_btime_ns, stx_btime, STATX_BTIME) +STATX_GET_NSEC(stx_ctime_ns, stx_ctime, STATX_CTIME) +STATX_GET_NSEC(stx_mtime_ns, stx_mtime, STATX_MTIME) + +#define G(attr, doc) \ + {#attr, pystatx_result_get_##attr, NULL, PyDoc_STR(doc), NULL} + +static PyGetSetDef pystatx_result_getset[] = { + G(stx_mode, "protection bits"), + G(stx_nlink, "number of hard links"), + G(stx_uid, "user ID of owner"), + G(stx_gid, "group ID of owner"), + G(stx_ino, "inode"), + G(stx_size, "total size, in bytes"), + G(stx_blocks, "number of blocks allocated"), + G(stx_atime, "time of last access"), + G(stx_atime_ns, "time of last access in nanoseconds"), + G(stx_btime, "time of creation"), + G(stx_btime_ns, "time of creation in nanoseconds"), + G(stx_ctime, "time of last change"), + G(stx_ctime_ns, "time of last change in nanoseconds"), + G(stx_mtime, "time of last modification"), + G(stx_mtime_ns, "time of last modification in nanoseconds"), +#if defined(STATX_MNT_ID) && defined(HAVE_STRUCT_STATX_STX_MNT_ID) + G(stx_mnt_id, "mount ID"), +#endif +#ifdef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN + G(stx_dio_mem_align, "direct I/O memory buffer alignment"), + G(stx_dio_offset_align, "direct I/O file offset alignment"), +#endif +#if defined(STATX_SUBVOL) && defined(HAVE_STRUCT_STATX_STX_SUBVOL) + G(stx_subvol, "subvolume ID"), +#endif +#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN + G(stx_atomic_write_unit_min, + "minimum size for direct I/O with torn-write protection"), + G(stx_atomic_write_unit_max, + "maximum size for direct I/O with torn-write protection"), + G(stx_atomic_write_segments_max, + "maximum iovecs for direct I/O with torn-write protection"), +#endif +#ifdef HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN + G(stx_dio_read_offset_align, "direct I/O file offset alignment for reads"), +#endif +#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT + G(stx_atomic_write_unit_max_opt, + "maximum optimized size for direct I/O with torn-write protection"), +#endif + {NULL}, +}; + +#undef G + +static PyObject * +pystatx_result_repr(PyObject *op) +{ + PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); + if (writer == NULL) { + return NULL; + } +#define WRITE_ASCII(s) \ + do { \ + if (PyUnicodeWriter_WriteASCII(writer, s, strlen(s)) < 0) { \ + goto error; \ + } \ + } while (0) + + WRITE_ASCII("os.statx_result("); + + for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_members) - 1; ++i) { + if (i > 0) { + WRITE_ASCII(", "); + } + + PyMemberDef *d = &pystatx_result_members[i]; + WRITE_ASCII(d->name); + WRITE_ASCII("="); + + PyObject *o = PyMember_GetOne((const char *)op, d); + if (o == NULL) { + goto error; + } + if (PyUnicodeWriter_WriteRepr(writer, o) < 0) { + Py_DECREF(o); + goto error; + } + Py_DECREF(o); + } + + for (size_t i = 0; i < Py_ARRAY_LENGTH(pystatx_result_getset) - 1; ++i) { + PyGetSetDef *d = &pystatx_result_getset[i]; + PyObject *o = d->get(op, d->closure); + if (o == NULL) { + goto error; + } + if (o == Py_None) { + continue; + } + + WRITE_ASCII(", "); + WRITE_ASCII(d->name); + WRITE_ASCII("="); + if (PyUnicodeWriter_WriteRepr(writer, o) < 0) { + Py_DECREF(o); + goto error; + } + Py_DECREF(o); + } + + WRITE_ASCII(")"); + return PyUnicodeWriter_Finish(writer); +#undef WRITE_ASCII + +error: + PyUnicodeWriter_Discard(writer); + return NULL; +} + +static int +pystatx_result_traverse(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} + +static void +pystatx_result_dealloc(PyObject *op) +{ + Py_statx_result *self = (Py_statx_result *) op; + PyTypeObject *tp = Py_TYPE(self); + PyObject_GC_UnTrack(self); + tp->tp_free(self); + Py_DECREF(tp); +} + +static PyType_Slot pystatx_result_slots[] = { + {Py_tp_repr, pystatx_result_repr}, + {Py_tp_traverse, pystatx_result_traverse}, + {Py_tp_dealloc, pystatx_result_dealloc}, + {Py_tp_members, pystatx_result_members}, + {Py_tp_getset, pystatx_result_getset}, + {0, NULL}, +}; + +static PyType_Spec pystatx_result_spec = { + .name = "os.statx_result", + .basicsize = sizeof(Py_statx_result), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .slots = pystatx_result_slots, +}; + +/*[clinic input] + +os.statx + + path : path_t(allow_fd=True) + Path to be examined; can be string, bytes, a path-like object or + open-file-descriptor int. + + mask: unsigned_int(bitwise=True) + A bitmask of STATX_* constants defining the requested information. + + * + + flags: int = 0 + A bitmask of AT_NO_AUTOMOUNT and/or AT_STATX_* flags. + + dir_fd : dir_fd = None + If not None, it should be a file descriptor open to a directory, + and path should be a relative string; path will then be relative to + that directory. + + follow_symlinks: bool = True + If False, and the last element of the path is a symbolic link, + statx will examine the symbolic link itself instead of the file + the link points to. + +Perform a statx system call on the given path. + +It's an error to use dir_fd or follow_symlinks when specifying path as + an open file descriptor. + +[clinic start generated code]*/ + +static PyObject * +os_statx_impl(PyObject *module, path_t *path, unsigned int mask, int flags, + int dir_fd, int follow_symlinks) +/*[clinic end generated code: output=e3765979ac6fe15b input=f0116380c5dc4f2f]*/ +{ + if (path_and_dir_fd_invalid("statx", path, dir_fd) || + dir_fd_and_fd_invalid("statx", dir_fd, path->fd) || + fd_and_follow_symlinks_invalid("statx", path->is_fd, follow_symlinks)) { + return NULL; + } + + /* reject flags covered by kwargs, but allow unknown flags that may be + future AT_STATX_* extensions */ + if (flags & (AT_SYMLINK_NOFOLLOW | AT_SYMLINK_FOLLOW)) { + PyErr_Format(PyExc_ValueError, + "use follow_symlinks kwarg instead of AT_SYMLINK_* flag"); + return NULL; + } + if (flags & AT_EMPTY_PATH) { + PyErr_Format(PyExc_ValueError, + "use dir_fd kwarg instead of AT_EMPTY_PATH flag"); + return NULL; + } + + /* Future bits may refer to members beyond the current size of struct + statx, so we need to mask them off to prevent memory corruption. */ + mask &= _Py_STATX_KNOWN; + + _posixstate *state = get_posix_state(module); + PyTypeObject *tp = (PyTypeObject *)state->StatxResultType; + Py_statx_result *v = (Py_statx_result *)tp->tp_alloc(tp, 0); + if (v == NULL) { + return NULL; + } + + int result; + Py_BEGIN_ALLOW_THREADS + if (path->is_fd) { + result = statx(path->fd, "", flags | AT_EMPTY_PATH, mask, &v->stx); + } + else { + result = statx(dir_fd, path->narrow, flags, mask, &v->stx); + } + Py_END_ALLOW_THREADS + + if (result != 0) { + Py_DECREF(v); + return path_error(path); + } + + v->rdev = makedev(v->stx.stx_rdev_major, v->stx.stx_rdev_minor); + v->dev = makedev(v->stx.stx_dev_major, v->stx.stx_dev_minor); + + assert(!PyErr_Occurred()); + return (PyObject *)v; +} +#endif /* HAVE_STATX */ + + /*[clinic input] os.access -> bool @@ -3295,15 +3745,15 @@ dir_fd, effective_ids, and follow_symlinks may not be implemented NotImplementedError. Note that most operations will use the effective uid/gid, therefore this - routine can be used in a suid/sgid environment to test if the invoking user - has the specified access to the path. + routine can be used in a suid/sgid environment to test if the invoking + user has the specified access to the path. [clinic start generated code]*/ static int os_access_impl(PyObject *module, path_t *path, int mode, int dir_fd, int effective_ids, int follow_symlinks) -/*[clinic end generated code: output=cf84158bc90b1a77 input=3ffe4e650ee3bf20]*/ +/*[clinic end generated code: output=cf84158bc90b1a77 input=c33565f7584b99e4]*/ { int return_value; @@ -3475,14 +3925,14 @@ os.chdir Change the current working directory to the specified path. -path may always be specified as a string. -On some platforms, path may also be specified as an open file descriptor. -If this functionality is unavailable, using it raises an exception. +path may always be specified as a string. On some platforms, path may +also be specified as an open file descriptor. If this functionality is +unavailable, using it raises an exception. [clinic start generated code]*/ static PyObject * os_chdir_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=3be6400eee26eaae input=a74ceab5d72adf74]*/ +/*[clinic end generated code: output=3be6400eee26eaae input=64673c342e4369f1]*/ { int result; @@ -3496,7 +3946,7 @@ os_chdir_impl(PyObject *module, path_t *path) result = !win32_wchdir(path->wide); #else #ifdef HAVE_FCHDIR - if (path->fd != -1) + if (path->is_fd) result = fchdir(path->fd); else #endif @@ -3595,15 +4045,16 @@ win32_fchmod(int fd, int mode) os.chmod path: path_t(allow_fd='PATH_HAVE_FCHMOD') - Path to be modified. May always be specified as a str, bytes, or a path-like object. - On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. + Path to be modified. May always be specified as a str, bytes, or + a path-like object. On some platforms, path may also be specified + as an open file descriptor. If this functionality is unavailable, + using it raises an exception. mode: int Operating-system mode bitfield. - Be careful when using number literals for *mode*. The conventional UNIX notation for - numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in - Python. + Be careful when using number literals for *mode*. The conventional + UNIX notation for numeric modes uses an octal base, which needs to + be indicated with a ``0o`` prefix in Python. * @@ -3630,7 +4081,7 @@ dir_fd and follow_symlinks may not be implemented on your platform. static PyObject * os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd, int follow_symlinks) -/*[clinic end generated code: output=5cf6a94915cc7bff input=fcf115d174b9f3d8]*/ +/*[clinic end generated code: output=5cf6a94915cc7bff input=7b6e2eeadd8bf199]*/ { int result; @@ -3652,7 +4103,7 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd, #ifdef MS_WINDOWS result = 0; Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) { + if (path->is_fd) { result = win32_fchmod(path->fd, mode); } else if (follow_symlinks) { @@ -3675,8 +4126,9 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd, #else /* MS_WINDOWS */ Py_BEGIN_ALLOW_THREADS #ifdef HAVE_FCHMOD - if (path->fd != -1) + if (path->is_fd) { result = fchmod(path->fd, mode); + } else #endif /* HAVE_CHMOD */ #ifdef HAVE_LCHMOD @@ -3765,9 +4217,9 @@ os.fchmod The file descriptor of the file to be modified. mode: int Operating-system mode bitfield. - Be careful when using number literals for *mode*. The conventional UNIX notation for - numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in - Python. + Be careful when using number literals for *mode*. The conventional + UNIX notation for numeric modes uses an octal base, which needs to + be indicated with a ``0o`` prefix in Python. Change the access permissions of the file given by file descriptor fd. @@ -3776,7 +4228,7 @@ Equivalent to os.chmod(fd, mode). static PyObject * os_fchmod_impl(PyObject *module, int fd, int mode) -/*[clinic end generated code: output=afd9bc05b4e426b3 input=b5594618bbbc22df]*/ +/*[clinic end generated code: output=afd9bc05b4e426b3 input=d24331f9fdc17f49]*/ { int res; @@ -3810,6 +4262,7 @@ os_fchmod_impl(PyObject *module, int fd, int mode) #if defined(HAVE_LCHMOD) || defined(MS_WINDOWS) /*[clinic input] +@permit_long_summary os.lchmod path: path_t @@ -3817,13 +4270,13 @@ os.lchmod Change the access permissions of a file, without following symbolic links. -If path is a symlink, this affects the link itself rather than the target. -Equivalent to chmod(path, mode, follow_symlinks=False)." +If path is a symlink, this affects the link itself rather than the +target. Equivalent to chmod(path, mode, follow_symlinks=False). [clinic start generated code]*/ static PyObject * os_lchmod_impl(PyObject *module, path_t *path, int mode) -/*[clinic end generated code: output=082344022b51a1d5 input=90c5663c7465d24f]*/ +/*[clinic end generated code: output=082344022b51a1d5 input=13110fb62911b015]*/ { int res; if (PySys_Audit("os.chmod", "Oii", path->object, mode, -1) < 0) { @@ -3861,9 +4314,9 @@ os.chflags Set file flags. -If follow_symlinks is False, and the last element of the path is a symbolic - link, chflags will change flags on the symbolic link itself instead of the - file the link points to. +If follow_symlinks is False, and the last element of the path is +a symbolic link, chflags() will change flags on the symbolic link itself +instead of the file the link points to. follow_symlinks may not be implemented on your platform. If it is unavailable, using it will raise a NotImplementedError. @@ -3872,7 +4325,7 @@ unavailable, using it will raise a NotImplementedError. static PyObject * os_chflags_impl(PyObject *module, path_t *path, unsigned long flags, int follow_symlinks) -/*[clinic end generated code: output=85571c6737661ce9 input=0327e29feb876236]*/ +/*[clinic end generated code: output=85571c6737661ce9 input=31391927707be1de]*/ { int result; @@ -4022,7 +4475,8 @@ os_fdatasync_impl(PyObject *module, int fd) os.chown path : path_t(allow_fd='PATH_HAVE_FCHOWN') - Path to be examined; can be string, bytes, a path-like object, or open-file-descriptor int. + Path to be examined; can be string, bytes, a path-like object, or + open-file-descriptor int. uid: uid_t @@ -4030,7 +4484,7 @@ os.chown * - dir_fd : dir_fd(requires='fchownat') = None + dir_fd: dir_fd(requires='fchownat') = None If not None, it should be a file descriptor open to a directory, and path should be relative; path will then be relative to that directory. @@ -4040,27 +4494,28 @@ os.chown stat will examine the symbolic link itself instead of the file the link points to. -Change the owner and group id of path to the numeric uid and gid.\ +Change the owner and group id of path to the numeric uid and gid. -path may always be specified as a string. -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -If follow_symlinks is False, and the last element of the path is a symbolic - link, chown will modify the symbolic link itself instead of the file the - link points to. +path may always be specified as a string. On some platforms, path may +also be specified as an open file descriptor. If this functionality is +unavailable, using it raises an exception. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative to +that directory. +If follow_symlinks is False, and the last element of the path is +a symbolic link, chown will modify the symbolic link itself instead of +the file the link points to. It is an error to use dir_fd or follow_symlinks when specifying path as - an open file descriptor. -dir_fd and follow_symlinks may not be implemented on your platform. - If they are unavailable, using them will raise a NotImplementedError. +an open file descriptor. +dir_fd and follow_symlinks may not be implemented on your platform. If +they are unavailable, using them will raise a NotImplementedError. [clinic start generated code]*/ static PyObject * os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid, int dir_fd, int follow_symlinks) -/*[clinic end generated code: output=4beadab0db5f70cd input=b08c5ec67996a97d]*/ +/*[clinic end generated code: output=4beadab0db5f70cd input=509c91b7a0e72f52]*/ { int result; @@ -4073,7 +4528,7 @@ os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid, return NULL; #endif if (dir_fd_and_fd_invalid("chown", dir_fd, path->fd) || - fd_and_follow_symlinks_invalid("chown", path->fd, follow_symlinks)) + fd_and_follow_symlinks_invalid("chown", path->is_fd, follow_symlinks)) return NULL; if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid, @@ -4083,7 +4538,7 @@ os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid, Py_BEGIN_ALLOW_THREADS #ifdef HAVE_FCHOWN - if (path->fd != -1) + if (path->is_fd) result = fchown(path->fd, uid, gid); else #endif @@ -4356,20 +4811,21 @@ os.link Create a hard link to a file. If either src_dir_fd or dst_dir_fd is not None, it should be a file - descriptor open to a directory, and the respective path string (src or dst) - should be relative; the path will then be relative to that directory. +descriptor open to a directory, and the respective path string (src or +dst) should be relative; the path will then be relative to that +directory. If follow_symlinks is False, and the last element of src is a symbolic - link, link will create a link to the symbolic link itself instead of the - file the link points to. -src_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on your - platform. If they are unavailable, using them will raise a - NotImplementedError. +link, link will create a link to the symbolic link itself instead of the +file the link points to. +src_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on +your platform. If they are unavailable, using them will raise +a NotImplementedError. [clinic start generated code]*/ static PyObject * os_link_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd, int dst_dir_fd, int follow_symlinks) -/*[clinic end generated code: output=7f00f6007fd5269a input=1d5e602d115fed7b]*/ +/*[clinic end generated code: output=7f00f6007fd5269a input=a28e6866fbd20a01]*/ { #ifdef MS_WINDOWS BOOL result = FALSE; @@ -4560,7 +5016,7 @@ _posix_listdir(path_t *path, PyObject *list) errno = 0; #ifdef HAVE_FDOPENDIR - if (path->fd != -1) { + if (path->is_fd) { if (HAVE_FDOPENDIR_RUNTIME) { /* closedir() closes the FD, so we duplicate it */ fd = _Py_dup(path->fd); @@ -4669,23 +5125,22 @@ os.listdir Return a list containing the names of the files in the directory. -path can be specified as either str, bytes, or a path-like object. If path is bytes, - the filenames returned will also be bytes; in all other circumstances - the filenames returned will be str. +path can be specified as either str, bytes, or a path-like object. If +path is bytes, the filenames returned will also be bytes; in all other +circumstances the filenames returned will be str. If path is None, uses the path='.'. -On some platforms, path may also be specified as an open file descriptor;\ - the file descriptor must refer to a directory. - If this functionality is unavailable, using it raises NotImplementedError. +On some platforms, path may also be specified as an open file +descriptor; the file descriptor must refer to a directory. If this +functionality is unavailable, using it raises NotImplementedError. The list is in arbitrary order. It does not include the special entries '.' and '..' even if they are present in the directory. - [clinic start generated code]*/ static PyObject * os_listdir_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=293045673fcd1a75 input=e3f58030f538295d]*/ +/*[clinic end generated code: output=293045673fcd1a75 input=4eefe7c6a42ec9b2]*/ { if (PySys_Audit("os.listdir", "O", path->object ? path->object : Py_None) < 0) { @@ -5144,6 +5599,7 @@ os__getfinalpathname_impl(PyObject *module, path_t *path) } /*[clinic input] +@permit_long_summary os._findfirstfile path: path_t / @@ -5152,7 +5608,7 @@ A function to get the real file name without accessing the file in Windows. static PyObject * os__findfirstfile_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=106dd3f0779c83dd input=0734dff70f60e1a8]*/ +/*[clinic end generated code: output=106dd3f0779c83dd input=48c319aaa48d05d4]*/ { PyObject *result; HANDLE hFindFile; @@ -5226,7 +5682,7 @@ os__getvolumepathname_impl(PyObject *module, path_t *path) /*[clinic input] os._path_splitroot - path: path_t, + path: path_t / Removes everything after the root on Win32. @@ -5234,7 +5690,7 @@ Removes everything after the root on Win32. static PyObject * os__path_splitroot_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=ab7f1a88b654581c input=42831e41f8458f6d]*/ +/*[clinic end generated code: output=ab7f1a88b654581c input=d356de1edb6050a2]*/ { wchar_t *buffer; wchar_t *end; @@ -5243,7 +5699,7 @@ os__path_splitroot_impl(PyObject *module, path_t *path) buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * (wcslen(path->wide) + 1)); if (!buffer) { - return NULL; + return PyErr_NoMemory(); } wcscpy(buffer, path->wide); for (wchar_t *p = wcschr(buffer, L'/'); p; p = wcschr(p, L'/')) { @@ -5458,7 +5914,7 @@ _testFileExists(path_t *path, BOOL followLinks) } Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) { + if (path->is_fd) { HANDLE hfile = _Py_get_osfhandle_noraise(path->fd); if (hfile != INVALID_HANDLE_VALUE) { if (GetFileType(hfile) != FILE_TYPE_UNKNOWN || !GetLastError()) { @@ -5484,7 +5940,7 @@ _testFileType(path_t *path, int testedType) } Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) { + if (path->is_fd) { HANDLE hfile = _Py_get_osfhandle_noraise(path->fd); if (hfile != INVALID_HANDLE_VALUE) { result = _testFileTypeByHandle(hfile, testedType, TRUE); @@ -5536,7 +5992,7 @@ os__path_lexists_impl(PyObject *module, path_t *path) /*[clinic input] os._path_isdir -> bool - path: path_t(allow_fd=True, suppress_value_error=True), + path: path_t(allow_fd=True, suppress_value_error=True) / Return true if the pathname refers to an existing directory. @@ -5545,7 +6001,7 @@ Return true if the pathname refers to an existing directory. static int os__path_isdir_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=d5786196f9e2fa7a input=0d3fd790564d244b]*/ +/*[clinic end generated code: output=d5786196f9e2fa7a input=b15f9b697a7a759f]*/ { return _testFileType(path, PY_IFDIR); } @@ -5614,7 +6070,7 @@ os__path_isjunction_impl(PyObject *module, path_t *path) /*[clinic input] os._path_splitroot_ex - path: path_t(make_wide=True, nonstrict=True), + path: path_t(make_wide=True, nonstrict=True) / Split a pathname into drive, root and tail. @@ -5624,7 +6080,7 @@ The tail contains anything after the root. static PyObject * os__path_splitroot_ex_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=4b0072b6cdf4b611 input=4ac47b394d68bd21]*/ +/*[clinic end generated code: output=4b0072b6cdf4b611 input=012fbfad14888b2b]*/ { Py_ssize_t drvsize, rootsize; PyObject *drv = NULL, *root = NULL, *tail = NULL, *result = NULL; @@ -5706,22 +6162,21 @@ os.mkdir dir_fd : dir_fd(requires='mkdirat') = None -# "mkdir(path, mode=0o777, *, dir_fd=None)\n\n\ - Create a directory. -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative to +that directory. +dir_fd may not be implemented on your platform. If it is unavailable, +using it will raise a NotImplementedError. -The mode argument is ignored on Windows. Where it is used, the current umask -value is first masked out. +The mode argument is ignored on Windows. Where it is used, the current +umask value is first masked out. [clinic start generated code]*/ static PyObject * os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd) -/*[clinic end generated code: output=a70446903abe821f input=a61722e1576fab03]*/ +/*[clinic end generated code: output=a70446903abe821f input=30270d369599634b]*/ { int result; #ifdef MS_WINDOWS @@ -5986,16 +6441,17 @@ os.rename Rename a file or directory. If either src_dir_fd or dst_dir_fd is not None, it should be a file - descriptor open to a directory, and the respective path string (src or dst) - should be relative; the path will then be relative to that directory. +descriptor open to a directory, and the respective path string (src or +dst) should be relative; the path will then be relative to that +directory. src_dir_fd and dst_dir_fd, may not be implemented on your platform. - If they are unavailable, using them will raise a NotImplementedError. +If they are unavailable, using them will raise a NotImplementedError. [clinic start generated code]*/ static PyObject * os_rename_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd, int dst_dir_fd) -/*[clinic end generated code: output=59e803072cf41230 input=faa61c847912c850]*/ +/*[clinic end generated code: output=59e803072cf41230 input=7d320d687c715fd6]*/ { return internal_rename(src, dst, src_dir_fd, dst_dir_fd, 0); } @@ -6007,16 +6463,17 @@ os.replace = os.rename Rename a file or directory, overwriting the destination. If either src_dir_fd or dst_dir_fd is not None, it should be a file - descriptor open to a directory, and the respective path string (src or dst) - should be relative; the path will then be relative to that directory. +descriptor open to a directory, and the respective path string (src or +dst) should be relative; the path will then be relative to that +directory. src_dir_fd and dst_dir_fd, may not be implemented on your platform. - If they are unavailable, using them will raise a NotImplementedError. +If they are unavailable, using them will raise a NotImplementedError. [clinic start generated code]*/ static PyObject * os_replace_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd, int dst_dir_fd) -/*[clinic end generated code: output=1968c02e7857422b input=c003f0def43378ef]*/ +/*[clinic end generated code: output=1968c02e7857422b input=44ed6b762d5953fc]*/ { return internal_rename(src, dst, src_dir_fd, dst_dir_fd, 1); } @@ -6031,15 +6488,16 @@ os.rmdir Remove a directory. -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative +to that directory. dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. +If it is unavailable, using it will raise a NotImplementedError. [clinic start generated code]*/ static PyObject * os_rmdir_impl(PyObject *module, path_t *path, int dir_fd) -/*[clinic end generated code: output=080eb54f506e8301 input=38c8b375ca34a7e2]*/ +/*[clinic end generated code: output=080eb54f506e8301 input=84325211e33a98e0]*/ { int result; #ifdef HAVE_UNLINKAT @@ -6115,14 +6573,14 @@ os_system_impl(PyObject *module, const wchar_t *command) /*[clinic input] os.system -> long - command: FSConverter + command: unicode_fs_encoded Execute the command in a subshell. [clinic start generated code]*/ static long os_system_impl(PyObject *module, PyObject *command) -/*[clinic end generated code: output=290fc437dd4f33a0 input=86a58554ba6094af]*/ +/*[clinic end generated code: output=290fc437dd4f33a0 input=47c6f24b6dc92881]*/ { long result; const char *bytes = PyBytes_AsString(command); @@ -6210,16 +6668,17 @@ os.unlink Remove a file (same as remove()). -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative to +that directory. dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. +If it is unavailable, using it will raise a NotImplementedError. [clinic start generated code]*/ static PyObject * os_unlink_impl(PyObject *module, path_t *path, int dir_fd) -/*[clinic end generated code: output=621797807b9963b1 input=d7bcde2b1b2a2552]*/ +/*[clinic end generated code: output=621797807b9963b1 input=1a2ef2579207eab1]*/ { int result; #ifdef HAVE_UNLINKAT @@ -6271,15 +6730,16 @@ os.remove = os.unlink Remove a file (same as unlink()). -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative +to that directory. dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. +If it is unavailable, using it will raise a NotImplementedError. [clinic start generated code]*/ static PyObject * os_remove_impl(PyObject *module, path_t *path, int dir_fd) -/*[clinic end generated code: output=a8535b28f0068883 input=e05c5ab55cd30983]*/ +/*[clinic end generated code: output=a8535b28f0068883 input=9f6e66912126bd56]*/ { return os_unlink_impl(module, path, dir_fd); } @@ -6601,38 +7061,37 @@ os.utime dir_fd: dir_fd(requires='futimensat') = None follow_symlinks: bool=True -# "utime(path, times=None, *[, ns], dir_fd=None, follow_symlinks=True)\n\ - Set the access and modified time of path. -path may always be specified as a string. -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. +path may always be specified as a string. On some platforms, path may +also be specified as an open file descriptor. If this functionality is +unavailable, using it raises an exception. If times is not None, it must be a tuple (atime, mtime); - atime and mtime should be expressed as float seconds since the epoch. +atime and mtime should be expressed as float seconds since the epoch. If ns is specified, it must be a tuple (atime_ns, mtime_ns); - atime_ns and mtime_ns should be expressed as integer nanoseconds - since the epoch. +atime_ns and mtime_ns should be expressed as integer nanoseconds +since the epoch. If times is None and ns is unspecified, utime uses the current time. Specifying tuples for both times and ns is an error. -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -If follow_symlinks is False, and the last element of the path is a symbolic - link, utime will modify the symbolic link itself instead of the file the - link points to. -It is an error to use dir_fd or follow_symlinks when specifying path - as an open file descriptor. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative to +that directory. +If follow_symlinks is False, and the last element of the path is +a symbolic link, utime will modify the symbolic link itself instead of +the file the link points to. +It is an error to use dir_fd or follow_symlinks when specifying path as +an open file descriptor. dir_fd and follow_symlinks may not be available on your platform. - If they are unavailable, using them will raise a NotImplementedError. +If they are unavailable, using them will raise a NotImplementedError. [clinic start generated code]*/ static PyObject * os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, int dir_fd, int follow_symlinks) -/*[clinic end generated code: output=cfcac69d027b82cf input=2fbd62a2f228f8f4]*/ +/*[clinic end generated code: output=cfcac69d027b82cf input=5ab470b2bc250788]*/ { #ifdef MS_WINDOWS HANDLE hFile; @@ -6658,7 +7117,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, if (!PyTuple_CheckExact(times) || (PyTuple_Size(times) != 2)) { PyErr_SetString(PyExc_TypeError, "utime: 'times' must be either" - " a tuple of two ints or None"); + " a tuple of two numbers or None"); return NULL; } utime.now = 0; @@ -6699,7 +7158,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, if (path_and_dir_fd_invalid("utime", path, dir_fd) || dir_fd_and_fd_invalid("utime", dir_fd, path->fd) || - fd_and_follow_symlinks_invalid("utime", path->fd, follow_symlinks)) + fd_and_follow_symlinks_invalid("utime", path->is_fd, follow_symlinks)) return NULL; #if !defined(HAVE_UTIMENSAT) @@ -6758,7 +7217,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, #endif #if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS) - if (path->fd != -1) + if (path->is_fd) result = utime_fd(&utime, path->fd); else #endif @@ -6789,6 +7248,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, /*[clinic input] +@permit_long_summary os._exit status: int @@ -6798,7 +7258,7 @@ Exit to the system with specified status, without normal exit processing. static PyObject * os__exit_impl(PyObject *module, int status) -/*[clinic end generated code: output=116e52d9c2260d54 input=5e6d57556b0c4a62]*/ +/*[clinic end generated code: output=116e52d9c2260d54 input=c35d282acfebe8fd]*/ { _exit(status); return NULL; /* Make gcc -Wall happy */ @@ -6853,8 +7313,8 @@ static EXECV_CHAR** parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) { Py_ssize_t i, pos, envc; - PyObject *keys=NULL, *vals=NULL; - PyObject *key2, *val2, *keyval; + PyObject *keys = NULL, *vals = NULL; + PyObject *key = NULL, *val = NULL, *key2 = NULL, *val2 = NULL; EXECV_CHAR **envlist; i = PyMapping_Size(env); @@ -6879,20 +7339,22 @@ parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) } for (pos = 0; pos < i; pos++) { - PyObject *key = PyList_GetItem(keys, pos); // Borrowed ref. + // The 'key' and 'val' must be strong references because of + // possible side-effects by PyUnicode_FS{Converter,Decoder}(). + key = PyList_GetItemRef(keys, pos); if (key == NULL) { goto error; } - PyObject *val = PyList_GetItem(vals, pos); // Borrowed ref. + val = PyList_GetItemRef(vals, pos); if (val == NULL) { goto error; } #if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV) - if (!PyUnicode_FSDecoder(key, &key2)) + if (!PyUnicode_FSDecoder(key, &key2)) { goto error; + } if (!PyUnicode_FSDecoder(val, &val2)) { - Py_DECREF(key2); goto error; } /* Search from index 1 because on Windows starting '=' is allowed for @@ -6901,39 +7363,38 @@ parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) PyUnicode_FindChar(key2, '=', 1, PyUnicode_GET_LENGTH(key2), 1) != -1) { PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); - Py_DECREF(key2); - Py_DECREF(val2); goto error; } - keyval = PyUnicode_FromFormat("%U=%U", key2, val2); + PyObject *keyval = PyUnicode_FromFormat("%U=%U", key2, val2); #else - if (!PyUnicode_FSConverter(key, &key2)) + if (!PyUnicode_FSConverter(key, &key2)) { goto error; + } if (!PyUnicode_FSConverter(val, &val2)) { - Py_DECREF(key2); goto error; } if (PyBytes_GET_SIZE(key2) == 0 || strchr(PyBytes_AS_STRING(key2) + 1, '=') != NULL) { PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); - Py_DECREF(key2); - Py_DECREF(val2); goto error; } - keyval = PyBytes_FromFormat("%s=%s", PyBytes_AS_STRING(key2), - PyBytes_AS_STRING(val2)); + PyObject *keyval = PyBytes_FromFormat("%s=%s", PyBytes_AS_STRING(key2), + PyBytes_AS_STRING(val2)); #endif - Py_DECREF(key2); - Py_DECREF(val2); - if (!keyval) + if (!keyval) { goto error; + } if (!fsconvert_strdup(keyval, &envlist[envc++])) { Py_DECREF(keyval); goto error; } + Py_CLEAR(key); + Py_CLEAR(val); + Py_CLEAR(key2); + Py_CLEAR(val2); Py_DECREF(keyval); } Py_DECREF(vals); @@ -6944,6 +7405,10 @@ parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) return envlist; error: + Py_XDECREF(key); + Py_XDECREF(val); + Py_XDECREF(key2); + Py_XDECREF(val2); Py_XDECREF(keys); Py_XDECREF(vals); free_string_array(envlist, envc); @@ -7122,7 +7587,7 @@ os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env) _Py_BEGIN_SUPPRESS_IPH #ifdef HAVE_FEXECVE - if (path->fd > -1) + if (path->is_fd) fexecve(path->fd, argvlist, envlist); else #endif @@ -7168,6 +7633,7 @@ parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpg PyObject *setsigdef, PyObject *scheduler, posix_spawnattr_t *attrp) { + assert(scheduler == NULL || scheduler == Py_None || PyTuple_Check(scheduler)); long all_flags = 0; errno = posix_spawnattr_init(attrp); @@ -7176,7 +7642,7 @@ parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpg return -1; } - if (setpgroup) { + if (setpgroup && setpgroup != Py_None) { pid_t pgid = PyLong_AsPid(setpgroup); if (pgid == (pid_t)-1 && PyErr_Occurred()) { goto fail; @@ -7249,7 +7715,7 @@ parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpg } #endif - if (scheduler) { + if (scheduler && scheduler != Py_None) { #ifdef POSIX_SPAWN_SETSCHEDULER PyObject *py_schedpolicy; PyObject *schedparam_obj; @@ -7465,7 +7931,7 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a if (argc < 1) { PyErr_Format(PyExc_ValueError, "%s: argv must not be empty", func_name); - return NULL; + goto exit; } if (!PyMapping_Check(env) && env != Py_None) { @@ -7474,6 +7940,12 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a goto exit; } + if (scheduler && !PyTuple_Check(scheduler) && scheduler != Py_None) { + PyErr_Format(PyExc_TypeError, + "%s: scheduler must be a tuple or None", func_name); + goto exit; + } + argvlist = parse_arglist(argv, &argc); if (argvlist == NULL) { goto exit; @@ -7585,17 +8057,18 @@ os.posix_spawn * file_actions: object(c_default='NULL') = () A sequence of file action tuples. - setpgroup: object = NULL + setpgroup: object(c_default='NULL') = None The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. resetids: bool = False If the value is `true` the POSIX_SPAWN_RESETIDS will be activated. setsid: bool = False - If the value is `true` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated. + If the value is `true` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP + will be activated. setsigmask: object(c_default='NULL') = () The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. setsigdef: object(c_default='NULL') = () The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag. - scheduler: object = NULL + scheduler: object(c_default='NULL') = None A tuple with the scheduler policy (optional) and parameters. Execute the program specified by path in a new process. @@ -7607,7 +8080,7 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *setpgroup, int resetids, int setsid, PyObject *setsigmask, PyObject *setsigdef, PyObject *scheduler) -/*[clinic end generated code: output=14a1098c566bc675 input=808aed1090d84e33]*/ +/*[clinic end generated code: output=14a1098c566bc675 input=c7592dcbc96e8114]*/ { return py_posix_spawn(0, module, path, argv, env, file_actions, setpgroup, resetids, setsid, setsigmask, setsigdef, @@ -7631,17 +8104,18 @@ os.posix_spawnp * file_actions: object(c_default='NULL') = () A sequence of file action tuples. - setpgroup: object = NULL + setpgroup: object(c_default='NULL') = None The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. resetids: bool = False If the value is `True` the POSIX_SPAWN_RESETIDS will be activated. setsid: bool = False - If the value is `True` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated. + If the value is `True` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP + will be activated. setsigmask: object(c_default='NULL') = () The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. setsigdef: object(c_default='NULL') = () The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag. - scheduler: object = NULL + scheduler: object(c_default='NULL') = None A tuple with the scheduler policy (optional) and parameters. Execute the program specified by path in a new process. @@ -7653,7 +8127,7 @@ os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *setpgroup, int resetids, int setsid, PyObject *setsigmask, PyObject *setsigdef, PyObject *scheduler) -/*[clinic end generated code: output=7b9aaefe3031238d input=9e89e616116752a1]*/ +/*[clinic end generated code: output=7b9aaefe3031238d input=43ccc1452cae2be3]*/ { return py_posix_spawn(1, module, path, argv, env, file_actions, setpgroup, resetids, setsid, setsigmask, setsigdef, @@ -7993,67 +8467,33 @@ os_register_at_fork_impl(PyObject *module, PyObject *before, // running in the process. Best effort, silent if unable to count threads. // Constraint: Quick. Never overcounts. Never leaves an error set. // -// This should only be called from the parent process after +// This MUST only be called from the parent process after // PyOS_AfterFork_Parent(). -static void -warn_about_fork_with_threads(const char* name) +static int +warn_about_fork_with_threads( + const char* name, // Name of the API to use in the warning message. + const Py_ssize_t num_os_threads // Only trusted when >= 1. +) { // It's not safe to issue the warning while the world is stopped, because // other threads might be holding locks that we need, which would deadlock. assert(!_PyRuntime.stoptheworld.world_stopped); - // TODO: Consider making an `os` module API to return the current number - // of threads in the process. That'd presumably use this platform code but - // raise an error rather than using the inaccurate fallback. - Py_ssize_t num_python_threads = 0; -#if defined(__APPLE__) && defined(HAVE_GETPID) - mach_port_t macos_self = mach_task_self(); - mach_port_t macos_task; - if (task_for_pid(macos_self, getpid(), &macos_task) == KERN_SUCCESS) { - thread_array_t macos_threads; - mach_msg_type_number_t macos_n_threads; - if (task_threads(macos_task, &macos_threads, - &macos_n_threads) == KERN_SUCCESS) { - num_python_threads = macos_n_threads; - } - } -#elif defined(__linux__) - // Linux /proc/self/stat 20th field is the number of threads. - FILE* proc_stat = fopen("/proc/self/stat", "r"); - if (proc_stat) { - size_t n; - // Size chosen arbitrarily. ~60% more bytes than a 20th column index - // observed on the author's workstation. - char stat_line[160]; - n = fread(&stat_line, 1, 159, proc_stat); - stat_line[n] = '\0'; - fclose(proc_stat); - - char *saveptr = NULL; - char *field = strtok_r(stat_line, " ", &saveptr); - unsigned int idx; - for (idx = 19; idx && field; --idx) { - field = strtok_r(NULL, " ", &saveptr); - } - if (idx == 0 && field) { // found the 20th field - num_python_threads = atoi(field); // 0 on error - } - } -#endif + Py_ssize_t num_python_threads = num_os_threads; if (num_python_threads <= 0) { // Fall back to just the number our threading module knows about. // An incomplete view of the world, but better than nothing. PyObject *threading = PyImport_GetModule(&_Py_ID(threading)); if (!threading) { PyErr_Clear(); - return; + return 0; } PyObject *threading_active = PyObject_GetAttr(threading, &_Py_ID(_active)); if (!threading_active) { PyErr_Clear(); Py_DECREF(threading); - return; + return 0; } PyObject *threading_limbo = PyObject_GetAttr(threading, &_Py_ID(_limbo)); @@ -8061,7 +8501,7 @@ warn_about_fork_with_threads(const char* name) PyErr_Clear(); Py_DECREF(threading); Py_DECREF(threading_active); - return; + return 0; } Py_DECREF(threading); // Duplicating what threading.active_count() does but without holding @@ -8077,7 +8517,7 @@ warn_about_fork_with_threads(const char* name) Py_DECREF(threading_limbo); } if (num_python_threads > 1) { - PyErr_WarnFormat( + return PyErr_WarnFormat( PyExc_DeprecationWarning, 1, #ifdef HAVE_GETPID "This process (pid=%d) is multi-threaded, " @@ -8089,8 +8529,53 @@ warn_about_fork_with_threads(const char* name) getpid(), #endif name); - PyErr_Clear(); } + return 0; +} + +// If this returns <= 0, we were unable to successfully use any OS APIs. +// Returns a positive number of threads otherwise. +static Py_ssize_t get_number_of_os_threads(void) +{ + // TODO: Consider making an `os` module API to return the current number + // of threads in the process. That'd presumably use this platform code but + // raise an error rather than using the inaccurate fallback. + Py_ssize_t num_python_threads = 0; +#if defined(__APPLE__) && defined(HAVE_GETPID) + mach_port_t macos_self = mach_task_self(); + mach_port_t macos_task; + if (task_for_pid(macos_self, getpid(), &macos_task) == KERN_SUCCESS) { + thread_array_t macos_threads; + mach_msg_type_number_t macos_n_threads; + if (task_threads(macos_task, &macos_threads, + &macos_n_threads) == KERN_SUCCESS) { + num_python_threads = macos_n_threads; + } + } +#elif defined(__linux__) + // Linux /proc/self/stat 20th field is the number of threads. + FILE* proc_stat = fopen("/proc/self/stat", "r"); + if (proc_stat) { + size_t n; + // Size chosen arbitrarily. ~60% more bytes than a 20th column index + // observed on the author's workstation. + char stat_line[160]; + n = fread(&stat_line, 1, 159, proc_stat); + stat_line[n] = '\0'; + fclose(proc_stat); + + char *saveptr = NULL; + char *field = strtok_r(stat_line, " ", &saveptr); + unsigned int idx; + for (idx = 19; idx && field; --idx) { + field = strtok_r(NULL, " ", &saveptr); + } + if (idx == 0 && field) { // found the 20th field + num_python_threads = atoi(field); // 0 on error + } + } +#endif + return num_python_threads; } #endif // HAVE_FORK1 || HAVE_FORKPTY || HAVE_FORK @@ -8126,10 +8611,14 @@ os_fork1_impl(PyObject *module) /* child: this clobbers and resets the import lock. */ PyOS_AfterFork_Child(); } else { + // Called before AfterFork_Parent in case those hooks start threads. + Py_ssize_t num_os_threads = get_number_of_os_threads(); /* parent: release the import lock. */ PyOS_AfterFork_Parent(); // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. - warn_about_fork_with_threads("fork1"); + if (warn_about_fork_with_threads("fork1", num_os_threads) < 0) { + return NULL; + } } if (pid == -1) { errno = saved_errno; @@ -8175,10 +8664,13 @@ os_fork_impl(PyObject *module) /* child: this clobbers and resets the import lock. */ PyOS_AfterFork_Child(); } else { + // Called before AfterFork_Parent in case those hooks start threads. + Py_ssize_t num_os_threads = get_number_of_os_threads(); /* parent: release the import lock. */ PyOS_AfterFork_Parent(); // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. - warn_about_fork_with_threads("fork"); + if (warn_about_fork_with_threads("fork", num_os_threads) < 0) + return NULL; } if (pid == -1) { errno = saved_errno; @@ -8203,10 +8695,10 @@ static PyObject * os_sched_get_priority_max_impl(PyObject *module, int policy) /*[clinic end generated code: output=9e465c6e43130521 input=2097b7998eca6874]*/ { - int max; - - max = sched_get_priority_max(policy); - if (max < 0) + /* make sure that errno is cleared before the call */ + errno = 0; + int max = sched_get_priority_max(policy); + if (max == -1 && errno) return posix_error(); return PyLong_FromLong(max); } @@ -8224,8 +8716,10 @@ static PyObject * os_sched_get_priority_min_impl(PyObject *module, int policy) /*[clinic end generated code: output=7595c1138cc47a6d input=21bc8fa0d70983bf]*/ { + /* make sure that errno is cleared before the call */ + errno = 0; int min = sched_get_priority_min(policy); - if (min < 0) + if (min == -1 && errno) return posix_error(); return PyLong_FromLong(min); } @@ -8286,7 +8780,7 @@ os_sched_param_impl(PyTypeObject *type, PyObject *sched_priority) static PyObject * os_sched_param_reduce(PyObject *self, PyObject *Py_UNUSED(dummy)) { - return Py_BuildValue("(O(N))", Py_TYPE(self), PyStructSequence_GetItem(self, 0)); + return Py_BuildValue("(O(O))", Py_TYPE(self), PyStructSequence_GetItem(self, 0)); } static PyMethodDef os_sched_param_reduce_method = { @@ -8301,7 +8795,7 @@ static PyStructSequence_Field sched_param_fields[] = { }; static PyStructSequence_Desc sched_param_desc = { - "sched_param", /* name */ + MODNAME ".sched_param", /* name */ os_sched_param__doc__, /* doc */ sched_param_fields, 1 @@ -8432,6 +8926,7 @@ os_sched_setparam_impl(PyObject *module, pid_t pid, PyObject *param_obj) #ifdef HAVE_SCHED_RR_GET_INTERVAL /*[clinic input] +@permit_long_summary os.sched_rr_get_interval -> double pid: pid_t / @@ -8443,7 +8938,7 @@ Value returned is a float. static double os_sched_rr_get_interval_impl(PyObject *module, pid_t pid) -/*[clinic end generated code: output=7e2d935833ab47dc input=2a973da15cca6fae]*/ +/*[clinic end generated code: output=7e2d935833ab47dc input=cab0b83586776b10]*/ { struct timespec interval; if (sched_rr_get_interval(pid, &interval)) { @@ -8583,6 +9078,7 @@ os_sched_setaffinity_impl(PyObject *module, pid_t pid, PyObject *mask) /*[clinic input] +@permit_long_summary os.sched_getaffinity pid: pid_t / @@ -8594,7 +9090,7 @@ The affinity is returned as a set of CPU identifiers. static PyObject * os_sched_getaffinity_impl(PyObject *module, pid_t pid) -/*[clinic end generated code: output=f726f2c193c17a4f input=983ce7cb4a565980]*/ +/*[clinic end generated code: output=f726f2c193c17a4f input=cb79ff13579ef091]*/ { int ncpus = NCPUS_START; size_t setsize; @@ -8667,13 +9163,13 @@ os.posix_openpt -> int Open and return a file descriptor for a master pseudo-terminal device. Performs a posix_openpt() C function call. The oflag argument is used to -set file status flags and file access modes as specified in the manual page -of posix_openpt() of your system. +set file status flags and file access modes as specified in the manual +page of posix_openpt() of your system. [clinic start generated code]*/ static int os_posix_openpt_impl(PyObject *module, int oflag) -/*[clinic end generated code: output=ee0bc2624305fc79 input=0de33d0e29693caa]*/ +/*[clinic end generated code: output=ee0bc2624305fc79 input=3ce4eb297fa64307]*/ { int fd; @@ -8919,13 +9415,13 @@ os_openpty_impl(PyObject *module) if (_Py_set_inheritable(master_fd, 0, NULL) < 0) goto posix_error; -#if !defined(__CYGWIN__) && !defined(__ANDROID__) && !defined(HAVE_DEV_PTC) +#if defined(HAVE_STROPTS_H) && !defined(HAVE_DEV_PTC) ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */ ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */ #ifndef __hpux ioctl(slave_fd, I_PUSH, "ttcompat"); /* push ttcompat */ #endif /* __hpux */ -#endif /* HAVE_CYGWIN */ +#endif /* defined(HAVE_STROPTS_H) && !defined(HAVE_DEV_PTC) */ #endif /* HAVE_OPENPTY */ return Py_BuildValue("(ii)", master_fd, slave_fd); @@ -8948,6 +9444,7 @@ os_openpty_impl(PyObject *module) #if defined(HAVE_LOGIN_TTY) || defined(HAVE_FALLBACK_LOGIN_TTY) /*[clinic input] +@permit_long_summary os.login_tty fd: fildes @@ -8962,7 +9459,7 @@ calling process; close fd. static PyObject * os_login_tty_impl(PyObject *module, int fd) -/*[clinic end generated code: output=495a79911b4cc1bc input=5f298565099903a2]*/ +/*[clinic end generated code: output=495a79911b4cc1bc input=b102a7c36e8baf00]*/ { #ifdef HAVE_LOGIN_TTY if (login_tty(fd) == -1) { @@ -9002,11 +9499,12 @@ Returns a tuple of (pid, master_fd). Like fork(), return pid of 0 to the child process, and pid of child to the parent process. To both, return fd of newly opened pseudo-terminal. +The master_fd is non-inheritable. [clinic start generated code]*/ static PyObject * os_forkpty_impl(PyObject *module) -/*[clinic end generated code: output=60d0a5c7512e4087 input=f1f7f4bae3966010]*/ +/*[clinic end generated code: output=60d0a5c7512e4087 input=24765e0f33275b3b]*/ { int master_fd = -1; pid_t pid; @@ -9030,14 +9528,24 @@ os_forkpty_impl(PyObject *module) /* child: this clobbers and resets the import lock. */ PyOS_AfterFork_Child(); } else { + // Called before AfterFork_Parent in case those hooks start threads. + Py_ssize_t num_os_threads = get_number_of_os_threads(); /* parent: release the import lock. */ PyOS_AfterFork_Parent(); + /* set O_CLOEXEC on master_fd */ + if (_Py_set_inheritable(master_fd, 0, NULL) < 0) { + PyErr_FormatUnraisable("Exception ignored when setting master_fd " + "non-inheritable in forkpty()"); + } + // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. - warn_about_fork_with_threads("forkpty"); + if (warn_about_fork_with_threads("forkpty", num_os_threads) < 0) + return NULL; } if (pid == -1) { return posix_error(); } + return Py_BuildValue("(Ni)", PyLong_FromPid(pid), master_fd); } #endif /* HAVE_FORKPTY */ @@ -9300,38 +9808,38 @@ os_getgroups_impl(PyObject *module) /*[clinic input] os.initgroups - username as oname: FSConverter + username as oname: unicode_fs_encoded gid: int / Initialize the group access list. -Call the system initgroups() to initialize the group access list with all of -the groups of which the specified username is a member, plus the specified -group id. +Call the system initgroups() to initialize the group access list with +all of the groups of which the specified username is a member, plus the +specified group id. [clinic start generated code]*/ static PyObject * os_initgroups_impl(PyObject *module, PyObject *oname, int gid) -/*[clinic end generated code: output=7f074d30a425fd3a input=df3d54331b0af204]*/ +/*[clinic end generated code: output=7f074d30a425fd3a input=35f2d4fb7fcc0bdf]*/ #else /*[clinic input] os.initgroups - username as oname: FSConverter + username as oname: unicode_fs_encoded gid: gid_t / Initialize the group access list. -Call the system initgroups() to initialize the group access list with all of -the groups of which the specified username is a member, plus the specified -group id. +Call the system initgroups() to initialize the group access list with +all of the groups of which the specified username is a member, plus the +specified group id. [clinic start generated code]*/ static PyObject * os_initgroups_impl(PyObject *module, PyObject *oname, gid_t gid) -/*[clinic end generated code: output=59341244521a9e3f input=0cb91bdc59a4c564]*/ +/*[clinic end generated code: output=59341244521a9e3f input=7e4514dff4526a95]*/ #endif { const char *username = PyBytes_AS_STRING(oname); @@ -9528,12 +10036,13 @@ os.getppid Return the parent's process id. If the parent process has already exited, Windows machines will still -return its id; others systems will return the id of the 'init' process (1). +return its id; others systems will return the id of the 'init' proces +(1). [clinic start generated code]*/ static PyObject * os_getppid_impl(PyObject *module) -/*[clinic end generated code: output=43b2a946a8c603b4 input=e637cb87539c030e]*/ +/*[clinic end generated code: output=43b2a946a8c603b4 input=e17c1de18f41316b]*/ { #ifdef MS_WINDOWS return win32_getppid(); @@ -9577,7 +10086,7 @@ os_getlogin_impl(PyObject *module) int err = getlogin_r(name, sizeof(name)); if (err) { int old_errno = errno; - errno = -err; + errno = err; posix_error(); errno = old_errno; } @@ -10087,13 +10596,13 @@ os.waitid Returns the result of waiting for a process or processes. -Returns either waitid_result or None if WNOHANG is specified and there are -no children in a waitable state. +Returns either waitid_result or None if WNOHANG is specified and there +are no children in a waitable state. [clinic start generated code]*/ static PyObject * os_waitid_impl(PyObject *module, idtype_t idtype, id_t id, int options) -/*[clinic end generated code: output=5d2e1c0bde61f4d8 input=d8e7f76e052b7920]*/ +/*[clinic end generated code: output=5d2e1c0bde61f4d8 input=14956bc8d102b5db]*/ { PyObject *result; int res; @@ -10260,13 +10769,13 @@ os.pidfd_open Return a file descriptor referring to the process *pid*. -The descriptor can be used to perform process management without races and -signals. +The descriptor can be used to perform process management without races +and signals. [clinic start generated code]*/ static PyObject * os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags) -/*[clinic end generated code: output=5c7252698947dc41 input=c3fd99ce947ccfef]*/ +/*[clinic end generated code: output=5c7252698947dc41 input=03058b32c389f874]*/ { int fd = syscall(__NR_pidfd_open, pid, flags); if (fd < 0) { @@ -10277,6 +10786,35 @@ os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags) #endif +#if defined(__linux__) && defined(__NR_pidfd_getfd) && \ + !(defined(__ANDROID__) && __ANDROID_API__ < 31) +/*[clinic input] +os.pidfd_getfd + pidfd: int + A process file descriptor. + targetfd: int + The file descriptor to duplicate from the target process. + * + flags: unsigned_int = 0 + Reserved, must be 0. + +Duplicate a file descriptor from the process referred to by *pidfd*. +[clinic start generated code]*/ + +static PyObject * +os_pidfd_getfd_impl(PyObject *module, int pidfd, int targetfd, + unsigned int flags) +/*[clinic end generated code: output=e1a1415a13c7137f input=ef6417fb10deb1cc]*/ +{ + int fd = syscall(__NR_pidfd_getfd, pidfd, targetfd, flags); + if (fd < 0) { + return posix_error(); + } + return PyLong_FromLong(fd); +} +#endif + + #ifdef HAVE_SETNS /*[clinic input] os.setns @@ -10345,8 +10883,9 @@ os.readlink Return a string representing the path to which the symbolic link points. -If dir_fd is not None, it should be a file descriptor open to a directory, -and path should be relative; path will then be relative to that directory. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative to +that directory. dir_fd may not be implemented on your platform. If it is unavailable, using it will raise a NotImplementedError. @@ -10354,7 +10893,7 @@ using it will raise a NotImplementedError. static PyObject * os_readlink_impl(PyObject *module, path_t *path, int dir_fd) -/*[clinic end generated code: output=d21b732a2e814030 input=113c87e0db1ecaf2]*/ +/*[clinic end generated code: output=d21b732a2e814030 input=03d10130870dbca8]*/ { #if defined(HAVE_READLINK) char buffer[MAXPATHLEN+1]; @@ -10549,26 +11088,25 @@ os.symlink * dir_fd: dir_fd(requires='symlinkat')=None -# "symlink(src, dst, target_is_directory=False, *, dir_fd=None)\n\n\ - Create a symbolic link pointing to src named dst. target_is_directory is required on Windows if the target is to be - interpreted as a directory. (On Windows, symlink requires - Windows 6.0 or greater, and raises a NotImplementedError otherwise.) - target_is_directory is ignored on non-Windows platforms. +interpreted as a directory. (On Windows, symlink requires Windows 6.0 +or greater, and raises a NotImplementedError otherwise.) +target_is_directory is ignored on non-Windows platforms. -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative +to that directory. +dir_fd may not be implemented on your platform. If it is unavailable, +using it will raise a NotImplementedError. [clinic start generated code]*/ static PyObject * os_symlink_impl(PyObject *module, path_t *src, path_t *dst, int target_is_directory, int dir_fd) -/*[clinic end generated code: output=08ca9f3f3cf960f6 input=e820ec4472547bc3]*/ +/*[clinic end generated code: output=08ca9f3f3cf960f6 input=71b75467b31c45f7]*/ { #ifdef MS_WINDOWS DWORD result; @@ -10684,7 +11222,7 @@ and elapsed.\n\ See os.times for more information."); static PyStructSequence_Desc times_result_desc = { - "times_result", /* name */ + MODNAME ".times_result", /* name */ times_result__doc__, /* doc */ times_result_fields, 5 @@ -10797,10 +11335,7 @@ build_itimerspec(const struct itimerspec* curr_value) Py_DECREF(value); return NULL; } - PyObject *tuple = PyTuple_Pack(2, value, interval); - Py_DECREF(interval); - Py_DECREF(value); - return tuple; + return _PyTuple_FromPairSteal(value, interval); } static PyObject * @@ -10952,6 +11487,7 @@ os_timerfd_settime_ns_impl(PyObject *module, int fd, int flags, } /*[clinic input] +@permit_long_summary os.timerfd_gettime fd: fildes @@ -10963,7 +11499,7 @@ Return a tuple of a timer file descriptor's (interval, next expiration) in float static PyObject * os_timerfd_gettime_impl(PyObject *module, int fd) -/*[clinic end generated code: output=ec5a94a66cfe6ab4 input=8148e3430870da1c]*/ +/*[clinic end generated code: output=ec5a94a66cfe6ab4 input=05f7d568a4820dc6]*/ { struct itimerspec curr_value; int result; @@ -10978,6 +11514,7 @@ os_timerfd_gettime_impl(PyObject *module, int fd) /*[clinic input] +@permit_long_summary os.timerfd_gettime_ns fd: fildes @@ -10989,7 +11526,7 @@ Return a tuple of a timer file descriptor's (interval, next expiration) in nanos static PyObject * os_timerfd_gettime_ns_impl(PyObject *module, int fd) -/*[clinic end generated code: output=580633a4465f39fe input=a825443e4c6b40ac]*/ +/*[clinic end generated code: output=580633a4465f39fe input=d0de95b9782179c5]*/ { struct itimerspec curr_value; int result; @@ -11128,19 +11665,18 @@ os.open -> int * dir_fd: dir_fd(requires='openat') = None -# "open(path, flags, mode=0o777, *, dir_fd=None)\n\n\ - Open a file for low level IO. Returns a file descriptor (integer). -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative to +that directory. +dir_fd may not be implemented on your platform. If it is unavailable, +using it will raise a NotImplementedError. [clinic start generated code]*/ static int os_open_impl(PyObject *module, path_t *path, int flags, int mode, int dir_fd) -/*[clinic end generated code: output=abc7227888c8bc73 input=ad8623b29acd2934]*/ +/*[clinic end generated code: output=abc7227888c8bc73 input=75f7b4eaf92f2225]*/ { int fd; int async_err = 0; @@ -11429,12 +11965,13 @@ os.lseek -> Py_off_t Set the position of a file descriptor. Return the new position. -The return value is the number of bytes relative to the beginning of the file. +The return value is the number of bytes relative to the beginning of +the file. [clinic start generated code]*/ static Py_off_t os_lseek_impl(PyObject *module, int fd, Py_off_t position, int how) -/*[clinic end generated code: output=971e1efb6b30bd2f input=f096e754c5367504]*/ +/*[clinic end generated code: output=971e1efb6b30bd2f input=32ea0788da7cb44b]*/ { Py_off_t result; @@ -11476,9 +12013,6 @@ static PyObject * os_read_impl(PyObject *module, int fd, Py_ssize_t length) /*[clinic end generated code: output=dafbe9a5cddb987b input=1df2eaa27c0bf1d3]*/ { - Py_ssize_t n; - PyObject *buffer; - if (length < 0) { errno = EINVAL; return posix_error(); @@ -11486,20 +12020,18 @@ os_read_impl(PyObject *module, int fd, Py_ssize_t length) length = Py_MIN(length, _PY_READ_MAX); - buffer = PyBytes_FromStringAndSize((char *)NULL, length); - if (buffer == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(length); + if (writer == NULL) { return NULL; + } - n = _Py_read(fd, PyBytes_AS_STRING(buffer), length); + Py_ssize_t n = _Py_read(fd, PyBytesWriter_GetData(writer), length); if (n == -1) { - Py_DECREF(buffer); + PyBytesWriter_Discard(writer); return NULL; } - if (n != length) - _PyBytes_Resize(&buffer, n); - - return buffer; + return PyBytesWriter_FinishWithSize(writer, n); } /*[clinic input] @@ -11510,20 +12042,20 @@ os.readinto -> Py_ssize_t Read into a buffer object from a file descriptor. -The buffer should be mutable and bytes-like. On success, returns the number of -bytes read. Less bytes may be read than the size of the buffer. The underlying -system call will be retried when interrupted by a signal, unless the signal -handler raises an exception. Other errors will not be retried and an error will -be raised. +The buffer should be mutable and bytes-like. On success, returns the +number of bytes read. Less bytes may be read than the size of the +buffer. The underlying system call will be retried when interrupted by +a signal, unless the signal handler raises an exception. Other errors +will not be retried and an error will be raised. -Returns 0 if *fd* is at end of file or if the provided *buffer* has length 0 -(which can be used to check for errors without reading data). Never returns -negative. +Returns 0 if *fd* is at end of file or if the provided *buffer* has +length 0 (which can be used to check for errors without reading data). +Never returns negative. [clinic start generated code]*/ static Py_ssize_t os_readinto_impl(PyObject *module, int fd, Py_buffer *buffer) -/*[clinic end generated code: output=8091a3513c683a80 input=d40074d0a68de575]*/ +/*[clinic end generated code: output=8091a3513c683a80 input=2a5f8b212cb5730c]*/ { assert(buffer->len >= 0); Py_ssize_t result = _Py_read(fd, buffer->buf, buffer->len); @@ -11656,6 +12188,7 @@ os_readv_impl(PyObject *module, int fd, PyObject *buffers) #ifdef HAVE_PREAD /*[clinic input] +@permit_long_summary os.pread fd: int @@ -11671,24 +12204,24 @@ the beginning of the file. The file offset remains unchanged. static PyObject * os_pread_impl(PyObject *module, int fd, Py_ssize_t length, Py_off_t offset) -/*[clinic end generated code: output=3f875c1eef82e32f input=85cb4a5589627144]*/ +/*[clinic end generated code: output=3f875c1eef82e32f input=5943beb009d3da04]*/ { Py_ssize_t n; int async_err = 0; - PyObject *buffer; if (length < 0) { errno = EINVAL; return posix_error(); } - buffer = PyBytes_FromStringAndSize((char *)NULL, length); - if (buffer == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(length); + if (writer == NULL) { return NULL; + } do { Py_BEGIN_ALLOW_THREADS _Py_BEGIN_SUPPRESS_IPH - n = pread(fd, PyBytes_AS_STRING(buffer), length, offset); + n = pread(fd, PyBytesWriter_GetData(writer), length, offset); _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); @@ -11697,17 +12230,16 @@ os_pread_impl(PyObject *module, int fd, Py_ssize_t length, Py_off_t offset) if (!async_err) { posix_error(); } - Py_DECREF(buffer); + PyBytesWriter_Discard(writer); return NULL; } - if (n != length) - _PyBytes_Resize(&buffer, n); - return buffer; + return PyBytesWriter_FinishWithSize(writer, n); } #endif /* HAVE_PREAD */ #if defined(HAVE_PREADV) || defined (HAVE_PREADV2) /*[clinic input] +@permit_long_summary os.preadv -> Py_ssize_t fd: int @@ -11718,17 +12250,19 @@ os.preadv -> Py_ssize_t Reads from a file descriptor into a number of mutable bytes-like objects. -Combines the functionality of readv() and pread(). As readv(), it will -transfer data into each buffer until it is full and then move on to the next -buffer in the sequence to hold the rest of the data. Its fourth argument, -specifies the file offset at which the input operation is to be performed. It -will return the total number of bytes read (which can be less than the total -capacity of all the objects). +Combines the functionality of readv() and pread(). As readv(), it will +transfer data into each buffer until it is full and then move on to the +next buffer in the sequence to hold the rest of the data. Its fourth +argument, specifies the file offset at which the input operation is to +be performed. It will return the total number of bytes read (which can +be less than the total capacity of all the objects). -The flags argument contains a bitwise OR of zero or more of the following flags: +The flags argument contains a bitwise OR of zero or more of the +following flags: - RWF_HIPRI - RWF_NOWAIT +- RWF_DONTCACHE Using non-zero flags requires Linux 4.6 or newer. [clinic start generated code]*/ @@ -11736,7 +12270,7 @@ Using non-zero flags requires Linux 4.6 or newer. static Py_ssize_t os_preadv_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, int flags) -/*[clinic end generated code: output=26fc9c6e58e7ada5 input=4173919dc1f7ed99]*/ +/*[clinic end generated code: output=26fc9c6e58e7ada5 input=bbc70c63b4f4e877]*/ { Py_ssize_t cnt, n; int async_err = 0; @@ -11855,7 +12389,7 @@ os.sendfile out_fd: int in_fd: int offset: Py_off_t - count: Py_ssize_t + count: Py_ssize_t(allow_negative=False) headers: object(c_default="NULL") = () trailers: object(c_default="NULL") = () flags: int = 0 @@ -11867,7 +12401,7 @@ static PyObject * os_sendfile_impl(PyObject *module, int out_fd, int in_fd, Py_off_t offset, Py_ssize_t count, PyObject *headers, PyObject *trailers, int flags) -/*[clinic end generated code: output=329ea009bdd55afc input=338adb8ff84ae8cd]*/ +/*[clinic end generated code: output=329ea009bdd55afc input=dcb026b94effa922]*/ #else /*[clinic input] os.sendfile @@ -11875,7 +12409,7 @@ os.sendfile out_fd: int in_fd: int offset as offobj: object - count: Py_ssize_t + count: Py_ssize_t(allow_negative=False) Copy count bytes from file descriptor in_fd to file descriptor out_fd. [clinic start generated code]*/ @@ -11883,12 +12417,22 @@ Copy count bytes from file descriptor in_fd to file descriptor out_fd. static PyObject * os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj, Py_ssize_t count) -/*[clinic end generated code: output=ae81216e40f167d8 input=76d64058c74477ba]*/ +/*[clinic end generated code: output=ae81216e40f167d8 input=424df0949059ea5b]*/ #endif { Py_ssize_t ret; int async_err = 0; +#ifdef __APPLE__ + if(sbytes < 0) { + PyErr_SetString(PyExc_ValueError, + "count cannot be negative"); + return NULL; + } +#else + assert(count >= 0); +#endif + #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) #ifndef __APPLE__ off_t sbytes; @@ -12063,6 +12607,7 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj, #if defined(__APPLE__) /*[clinic input] +@permit_long_summary os._fcopyfile in_fd: int @@ -12075,7 +12620,7 @@ Efficiently copy content or metadata of 2 regular file descriptors (macOS). static PyObject * os__fcopyfile_impl(PyObject *module, int in_fd, int out_fd, int flags) -/*[clinic end generated code: output=c9d1a35a992e401b input=1e34638a86948795]*/ +/*[clinic end generated code: output=c9d1a35a992e401b input=80b53ad8863c9101]*/ { int ret; @@ -12264,6 +12809,7 @@ os_pipe2_impl(PyObject *module, int flags) #ifdef HAVE_WRITEV /*[clinic input] +@permit_long_summary os.writev -> Py_ssize_t fd: int buffers: object @@ -12277,7 +12823,7 @@ buffers must be a sequence of bytes-like objects. static Py_ssize_t os_writev_impl(PyObject *module, int fd, PyObject *buffers) -/*[clinic end generated code: output=56565cfac3aac15b input=5b8d17fe4189d2fe]*/ +/*[clinic end generated code: output=56565cfac3aac15b input=5771a0f0c2b326f2]*/ { Py_ssize_t cnt; Py_ssize_t result; @@ -12352,6 +12898,7 @@ os_pwrite_impl(PyObject *module, int fd, Py_buffer *buffer, Py_off_t offset) #if defined(HAVE_PWRITEV) || defined (HAVE_PWRITEV2) /*[clinic input] +@permit_long_summary os.pwritev -> Py_ssize_t fd: int @@ -12362,18 +12909,22 @@ os.pwritev -> Py_ssize_t Writes the contents of bytes-like objects to a file descriptor at a given offset. -Combines the functionality of writev() and pwrite(). All buffers must be a sequence -of bytes-like objects. Buffers are processed in array order. Entire contents of first -buffer is written before proceeding to second, and so on. The operating system may -set a limit (sysconf() value SC_IOV_MAX) on the number of buffers that can be used. -This function writes the contents of each object to the file descriptor and returns -the total number of bytes written. +Combines the functionality of writev() and pwrite(). All buffers must be +a sequence of bytes-like objects. Buffers are processed in array order. +Entire contents of first buffer is written before proceeding to second, +and so on. The operating system may set a limit (sysconf() value +SC_IOV_MAX) on the number of buffers that can be used. +This function writes the contents of each object to the file descriptor +and returns the total number of bytes written. -The flags argument contains a bitwise OR of zero or more of the following flags: +The flags argument contains a bitwise OR of zero or more of the +following flags: - RWF_DSYNC - RWF_SYNC - RWF_APPEND +- RWF_DONTCACHE +- RWF_ATOMIC Using non-zero flags requires Linux 4.7 or newer. [clinic start generated code]*/ @@ -12381,7 +12932,7 @@ Using non-zero flags requires Linux 4.7 or newer. static Py_ssize_t os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, int flags) -/*[clinic end generated code: output=e3dd3e9d11a6a5c7 input=35358c327e1a2a8e]*/ +/*[clinic end generated code: output=e3dd3e9d11a6a5c7 input=b2e352a22f030e9a]*/ { Py_ssize_t cnt; Py_ssize_t result; @@ -12462,7 +13013,7 @@ os.copy_file_range Source file descriptor. dst: int Destination file descriptor. - count: Py_ssize_t + count: Py_ssize_t(allow_negative=False) Number of bytes to copy. offset_src: object = None Starting offset in src. @@ -12478,7 +13029,7 @@ respectively for offset_dst. static PyObject * os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count, PyObject *offset_src, PyObject *offset_dst) -/*[clinic end generated code: output=1a91713a1d99fc7a input=42fdce72681b25a9]*/ +/*[clinic end generated code: output=1a91713a1d99fc7a input=08dacb760869b87c]*/ { off_t offset_src_val, offset_dst_val; off_t *p_offset_src = NULL; @@ -12490,11 +13041,6 @@ os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count, int flags = 0; - if (count < 0) { - PyErr_SetString(PyExc_ValueError, "negative value for 'count' not allowed"); - return NULL; - } - if (offset_src != Py_None) { if (!Py_off_t_converter(offset_src, &offset_src_val)) { return NULL; @@ -12531,7 +13077,7 @@ os.splice Source file descriptor. dst: int Destination file descriptor. - count: Py_ssize_t + count: Py_ssize_t(allow_negative=False) Number of bytes to copy. offset_src: object = None Starting offset in src. @@ -12551,7 +13097,7 @@ static PyObject * os_splice_impl(PyObject *module, int src, int dst, Py_ssize_t count, PyObject *offset_src, PyObject *offset_dst, unsigned int flags) -/*[clinic end generated code: output=d0386f25a8519dc5 input=047527c66c6d2e0a]*/ +/*[clinic end generated code: output=d0386f25a8519dc5 input=034852a7b2e7af35]*/ { off_t offset_src_val, offset_dst_val; off_t *p_offset_src = NULL; @@ -12559,10 +13105,6 @@ os_splice_impl(PyObject *module, int src, int dst, Py_ssize_t count, Py_ssize_t ret; int async_err = 0; - if (count < 0) { - PyErr_SetString(PyExc_ValueError, "negative value for 'count' not allowed"); - return NULL; - } if (offset_src != Py_None) { if (!Py_off_t_converter(offset_src, &offset_src_val)) { @@ -12603,15 +13145,16 @@ os.mkfifo Create a "fifo" (a POSIX named pipe). -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative to +that directory. +dir_fd may not be implemented on your platform. If it is unavailable, +using it will raise a NotImplementedError. [clinic start generated code]*/ static PyObject * os_mkfifo_impl(PyObject *module, path_t *path, int mode, int dir_fd) -/*[clinic end generated code: output=ce41cfad0e68c940 input=73032e98a36e0e19]*/ +/*[clinic end generated code: output=ce41cfad0e68c940 input=d2fb917c01e888d6]*/ { int result; int async_err = 0; @@ -12664,23 +13207,24 @@ os.mknod Create a node in the file system. -Create a node in the file system (file, device special file or named pipe) -at path. mode specifies both the permissions to use and the +Create a node in the file system (file, device special file or named +pipe) at path. mode specifies both the permissions to use and the type of node to be created, being combined (bitwise OR) with one of -S_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO. If S_IFCHR or S_IFBLK is set on mode, -device defines the newly created device special file (probably using -os.makedev()). Otherwise device is ignored. +S_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO. If S_IFCHR or S_IFBLK is set +on mode, device defines the newly created device special file (probably +using os.makedev()). Otherwise device is ignored. -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. +If dir_fd is not None, it should be a file descriptor open to +a directory, and path should be relative; path will then be relative +to that directory. +dir_fd may not be implemented on your platform. If it is unavailable, +using it will raise a NotImplementedError. [clinic start generated code]*/ static PyObject * os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device, int dir_fd) -/*[clinic end generated code: output=92e55d3ca8917461 input=ee44531551a4d83b]*/ +/*[clinic end generated code: output=92e55d3ca8917461 input=7d0099e85c6b4cba]*/ { int result; int async_err = 0; @@ -12849,20 +13393,21 @@ os.truncate Truncate a file, specified by path, to a specific length. -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. +On some platforms, path may also be specified as an open file +descriptor. If this functionality is unavailable, using it raises +an exception. [clinic start generated code]*/ static PyObject * os_truncate_impl(PyObject *module, path_t *path, Py_off_t length) -/*[clinic end generated code: output=43009c8df5c0a12b input=77229cf0b50a9b77]*/ +/*[clinic end generated code: output=43009c8df5c0a12b input=ce33fd7808a511c4]*/ { int result; #ifdef MS_WINDOWS int fd; #endif - if (path->fd != -1) + if (path->is_fd) return os_ftruncate_impl(module, path->fd, length); if (PySys_Audit("os.truncate", "On", path->object, length) < 0) { @@ -12894,20 +13439,11 @@ os_truncate_impl(PyObject *module, path_t *path, Py_off_t length) #endif /* HAVE_TRUNCATE || MS_WINDOWS */ -/* Issue #22396: On 32-bit AIX platform, the prototypes of os.posix_fadvise() - and os.posix_fallocate() in system headers are wrong if _LARGE_FILES is - defined, which is the case in Python on AIX. AIX bug report: - http://www-01.ibm.com/support/docview.wss?uid=isg1IV56170 */ -#if defined(_AIX) && defined(_LARGE_FILES) && !defined(__64BIT__) -# define POSIX_FADVISE_AIX_BUG -#endif - - /* GH-111804: Due to posix_fallocate() not having consistent semantics across OSs, support was dropped in WASI preview2. */ -#if defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG) && \ - !defined(__wasi__) +#if defined(HAVE_POSIX_FALLOCATE) && !defined(__wasi__) /*[clinic input] +@permit_long_summary os.posix_fallocate fd: int @@ -12918,13 +13454,14 @@ os.posix_fallocate Ensure a file has allocated at least a particular number of bytes on disk. Ensure that the file specified by fd encompasses a range of bytes -starting at offset bytes from the beginning and continuing for length bytes. +starting at offset bytes from the beginning and continuing for length +bytes. [clinic start generated code]*/ static PyObject * os_posix_fallocate_impl(PyObject *module, int fd, Py_off_t offset, Py_off_t length) -/*[clinic end generated code: output=73f107139564aa9d input=d7a2ef0ab2ca52fb]*/ +/*[clinic end generated code: output=73f107139564aa9d input=c718971d18b96896]*/ { int result; int async_err = 0; @@ -12944,10 +13481,10 @@ os_posix_fallocate_impl(PyObject *module, int fd, Py_off_t offset, errno = result; return posix_error(); } -#endif /* HAVE_POSIX_FALLOCATE) && !POSIX_FADVISE_AIX_BUG && !defined(__wasi__) */ +#endif /* HAVE_POSIX_FALLOCATE && !defined(__wasi__) */ -#if defined(HAVE_POSIX_FADVISE) && !defined(POSIX_FADVISE_AIX_BUG) +#if defined(HAVE_POSIX_FADVISE) /*[clinic input] os.posix_fadvise @@ -12959,8 +13496,8 @@ os.posix_fadvise Announce an intention to access data in a specific pattern. -Announce an intention to access data in a specific pattern, thus allowing -the kernel to make optimizations. +Announce an intention to access data in a specific pattern, thus +allowing the kernel to make optimizations. The advice applies to the region of the file specified by fd starting at offset and continuing for length bytes. advice is one of POSIX_FADV_NORMAL, POSIX_FADV_SEQUENTIAL, @@ -12971,7 +13508,7 @@ POSIX_FADV_DONTNEED. static PyObject * os_posix_fadvise_impl(PyObject *module, int fd, Py_off_t offset, Py_off_t length, int advice) -/*[clinic end generated code: output=412ef4aa70c98642 input=0fbe554edc2f04b5]*/ +/*[clinic end generated code: output=412ef4aa70c98642 input=961b01a4518ef727]*/ { int result; int async_err = 0; @@ -12991,7 +13528,7 @@ os_posix_fadvise_impl(PyObject *module, int fd, Py_off_t offset, errno = result; return posix_error(); } -#endif /* HAVE_POSIX_FADVISE && !POSIX_FADVISE_AIX_BUG */ +#endif /* HAVE_POSIX_FADVISE */ #ifdef MS_WINDOWS @@ -13082,8 +13619,8 @@ os_putenv_impl(PyObject *module, PyObject *name, PyObject *value) /*[clinic input] os.putenv - name: FSConverter - value: FSConverter + name: unicode_fs_encoded + value: unicode_fs_encoded / Change or add an environment variable. @@ -13091,7 +13628,7 @@ Change or add an environment variable. static PyObject * os_putenv_impl(PyObject *module, PyObject *name, PyObject *value) -/*[clinic end generated code: output=d29a567d6b2327d2 input=a97bc6152f688d31]*/ +/*[clinic end generated code: output=d29a567d6b2327d2 input=84fcd30f873c8c45]*/ { const char *name_string = PyBytes_AS_STRING(name); const char *value_string = PyBytes_AS_STRING(value); @@ -13134,7 +13671,7 @@ os_unsetenv_impl(PyObject *module, PyObject *name) #else /*[clinic input] os.unsetenv - name: FSConverter + name: unicode_fs_encoded / Delete an environment variable. @@ -13142,7 +13679,7 @@ Delete an environment variable. static PyObject * os_unsetenv_impl(PyObject *module, PyObject *name) -/*[clinic end generated code: output=54c4137ab1834f02 input=2bb5288a599c7107]*/ +/*[clinic end generated code: output=54c4137ab1834f02 input=78ff12e505ade80a]*/ { if (PySys_Audit("os.unsetenv", "(O)", name) < 0) { return NULL; @@ -13161,6 +13698,29 @@ os_unsetenv_impl(PyObject *module, PyObject *name) #endif /* !MS_WINDOWS */ +#ifdef HAVE_CLEARENV +/*[clinic input] +os._clearenv +[clinic start generated code]*/ + +static PyObject * +os__clearenv_impl(PyObject *module) +/*[clinic end generated code: output=2d6705d62c014b51 input=47d2fa7f323c43ca]*/ +{ + if (PySys_Audit("os._clearenv", NULL) < 0) { + return NULL; + } + + errno = 0; + int err = clearenv(); + if (err) { + return posix_error(); + } + Py_RETURN_NONE; +} +#endif + + /*[clinic input] os.strerror @@ -13208,6 +13768,7 @@ os_WCOREDUMP_impl(PyObject *module, int status) #ifdef WIFCONTINUED /*[clinic input] +@permit_long_summary os.WIFCONTINUED -> bool status: int @@ -13220,7 +13781,7 @@ job control stop. static int os_WIFCONTINUED_impl(PyObject *module, int status) -/*[clinic end generated code: output=1e35295d844364bd input=e777e7d38eb25bd9]*/ +/*[clinic end generated code: output=1e35295d844364bd input=7b577845a0f8b12f]*/ { WAIT_TYPE wait_status; WAIT_STATUS_INT(wait_status) = status; @@ -13271,6 +13832,7 @@ os_WIFSIGNALED_impl(PyObject *module, int status) #ifdef WIFEXITED /*[clinic input] +@permit_long_summary os.WIFEXITED -> bool status: int @@ -13280,7 +13842,7 @@ Return True if the process returning status exited via the exit() system call. static int os_WIFEXITED_impl(PyObject *module, int status) -/*[clinic end generated code: output=01c09d6ebfeea397 input=d63775a6791586c0]*/ +/*[clinic end generated code: output=01c09d6ebfeea397 input=8c24a82148709b30]*/ { WAIT_TYPE wait_status; WAIT_STATUS_INT(wait_status) = status; @@ -13311,6 +13873,7 @@ os_WEXITSTATUS_impl(PyObject *module, int status) #ifdef WTERMSIG /*[clinic input] +@permit_long_summary os.WTERMSIG -> int status: int @@ -13320,7 +13883,7 @@ Return the signal that terminated the process that provided the status value. static int os_WTERMSIG_impl(PyObject *module, int status) -/*[clinic end generated code: output=172f7dfc8dcfc3ad input=727fd7f84ec3f243]*/ +/*[clinic end generated code: output=172f7dfc8dcfc3ad input=89072f6cbf3f8050]*/ { WAIT_TYPE wait_status; WAIT_STATUS_INT(wait_status) = status; @@ -13331,6 +13894,7 @@ os_WTERMSIG_impl(PyObject *module, int status) #ifdef WSTOPSIG /*[clinic input] +@permit_long_summary os.WSTOPSIG -> int status: int @@ -13340,7 +13904,7 @@ Return the signal that stopped the process that provided the status value. static int os_WSTOPSIG_impl(PyObject *module, int status) -/*[clinic end generated code: output=0ab7586396f5d82b input=46ebf1d1b293c5c1]*/ +/*[clinic end generated code: output=0ab7586396f5d82b input=4698db1a6a320433]*/ { WAIT_TYPE wait_status; WAIT_STATUS_INT(wait_status) = status; @@ -13531,13 +14095,14 @@ os.statvfs Perform a statvfs system call on the given path. path may always be specified as a string. -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. +On some platforms, path may also be specified as an open file +descriptor. If this functionality is unavailable, using it raises +an exception. [clinic start generated code]*/ static PyObject * os_statvfs_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=87106dd1beb8556e input=3f5c35791c669bd9]*/ +/*[clinic end generated code: output=87106dd1beb8556e input=1cfd9a4fd36f7425]*/ { int result; @@ -13548,7 +14113,7 @@ os_statvfs_impl(PyObject *module, path_t *path) struct statfs st; Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) { + if (path->is_fd) { result = fstatfs(path->fd, &st); } else @@ -13566,7 +14131,7 @@ os_statvfs_impl(PyObject *module, path_t *path) Py_BEGIN_ALLOW_THREADS #ifdef HAVE_FSTATVFS - if (path->fd != -1) { + if (path->is_fd) { result = fstatvfs(path->fd, &st); } else @@ -13586,6 +14151,7 @@ os_statvfs_impl(PyObject *module, path_t *path) #ifdef MS_WINDOWS /*[clinic input] +@permit_long_summary os._getdiskusage path: path_t @@ -13595,7 +14161,7 @@ Return disk usage statistics about the given path as a (total, free) tuple. static PyObject * os__getdiskusage_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=3bd3991f5e5c5dfb input=6af8d1b7781cc042]*/ +/*[clinic end generated code: output=3bd3991f5e5c5dfb input=aee7e38bc3e7fb08]*/ { BOOL retval; ULARGE_INTEGER _, total, free; @@ -13816,20 +14382,22 @@ os.pathconf -> long Return the configuration limit name for the file or directory path. If there is no limit, return -1. -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. +On some platforms, path may also be specified as an open file +descriptor. If this functionality is unavailable, using it raises +an exception. [clinic start generated code]*/ static long os_pathconf_impl(PyObject *module, path_t *path, int name) -/*[clinic end generated code: output=5bedee35b293a089 input=6f6072f57b10c787]*/ +/*[clinic end generated code: output=5bedee35b293a089 input=e86f6eacfa006426]*/ { long limit; errno = 0; #ifdef HAVE_FPATHCONF - if (path->fd != -1) + if (path->is_fd) { limit = fpathconf(path->fd, name); + } else #endif limit = pathconf(path->narrow, name); @@ -14626,13 +15194,13 @@ os.abort Abort the interpreter immediately. -This function 'dumps core' or otherwise fails in the hardest way possible -on the hosting operating system. This function never returns. +This function 'dumps core' or otherwise fails in the hardest way +possible on the hosting operating system. This function never returns. [clinic start generated code]*/ static PyObject * os_abort_impl(PyObject *module) -/*[clinic end generated code: output=dcf52586dad2467c input=cf2c7d98bc504047]*/ +/*[clinic end generated code: output=dcf52586dad2467c input=ee8bd0ed690440ab]*/ { abort(); /*NOTREACHED*/ @@ -14841,6 +15409,7 @@ os_setresgid_impl(PyObject *module, gid_t rgid, gid_t egid, gid_t sgid) #ifdef HAVE_GETRESUID /*[clinic input] +@permit_long_summary os.getresuid Return a tuple of the current process's real, effective, and saved user ids. @@ -14848,7 +15417,7 @@ Return a tuple of the current process's real, effective, and saved user ids. static PyObject * os_getresuid_impl(PyObject *module) -/*[clinic end generated code: output=8e0becff5dece5bf input=41ccfa8e1f6517ad]*/ +/*[clinic end generated code: output=8e0becff5dece5bf input=ddf95881f492cb97]*/ { uid_t ruid, euid, suid; if (getresuid(&ruid, &euid, &suid) < 0) @@ -14862,6 +15431,7 @@ os_getresuid_impl(PyObject *module) #ifdef HAVE_GETRESGID /*[clinic input] +@permit_long_summary os.getresgid Return a tuple of the current process's real, effective, and saved group ids. @@ -14869,7 +15439,7 @@ Return a tuple of the current process's real, effective, and saved group ids. static PyObject * os_getresgid_impl(PyObject *module) -/*[clinic end generated code: output=2719c4bfcf27fb9f input=517e68db9ca32df6]*/ +/*[clinic end generated code: output=2719c4bfcf27fb9f input=ad9adadc86fbdb17]*/ { gid_t rgid, egid, sgid; if (getresgid(&rgid, &egid, &sgid) < 0) @@ -14892,30 +15462,27 @@ os.getxattr Return the value of extended attribute attribute on path. -path may be either a string, a path-like object, or an open file descriptor. -If follow_symlinks is False, and the last element of the path is a symbolic - link, getxattr will examine the symbolic link itself instead of the file - the link points to. +path may be either a string, a path-like object, or an open file +descriptor. +If follow_symlinks is False, and the last element of the path is +a symbolic link, getxattr will examine the symbolic link itself +instead of the file the link points to. [clinic start generated code]*/ static PyObject * os_getxattr_impl(PyObject *module, path_t *path, path_t *attribute, int follow_symlinks) -/*[clinic end generated code: output=5f2f44200a43cff2 input=025789491708f7eb]*/ +/*[clinic end generated code: output=5f2f44200a43cff2 input=db1021ed738d9754]*/ { - Py_ssize_t i; - PyObject *buffer = NULL; - - if (fd_and_follow_symlinks_invalid("getxattr", path->fd, follow_symlinks)) + if (fd_and_follow_symlinks_invalid("getxattr", path->is_fd, follow_symlinks)) return NULL; if (PySys_Audit("os.getxattr", "OO", path->object, attribute->object) < 0) { return NULL; } - for (i = 0; ; i++) { - void *ptr; + for (Py_ssize_t i = 0; ; i++) { ssize_t result; static const Py_ssize_t buffer_sizes[] = {128, XATTR_SIZE_MAX, 0}; Py_ssize_t buffer_size = buffer_sizes[i]; @@ -14923,13 +15490,14 @@ os_getxattr_impl(PyObject *module, path_t *path, path_t *attribute, path_error(path); return NULL; } - buffer = PyBytes_FromStringAndSize(NULL, buffer_size); - if (!buffer) + PyBytesWriter *writer = PyBytesWriter_Create(buffer_size); + if (writer == NULL) { return NULL; - ptr = PyBytes_AS_STRING(buffer); + } + void *ptr = PyBytesWriter_GetData(writer); Py_BEGIN_ALLOW_THREADS; - if (path->fd >= 0) + if (path->is_fd) result = fgetxattr(path->fd, attribute->narrow, ptr, buffer_size); else if (follow_symlinks) result = getxattr(path->narrow, attribute->narrow, ptr, buffer_size); @@ -14938,23 +15506,16 @@ os_getxattr_impl(PyObject *module, path_t *path, path_t *attribute, Py_END_ALLOW_THREADS; if (result < 0) { + PyBytesWriter_Discard(writer); if (errno == ERANGE) { - Py_DECREF(buffer); continue; } path_error(path); - Py_DECREF(buffer); return NULL; } - if (result != buffer_size) { - /* Can only shrink. */ - _PyBytes_Resize(&buffer, result); - } - break; + return PyBytesWriter_FinishWithSize(writer, result); } - - return buffer; } @@ -14970,21 +15531,22 @@ os.setxattr Set extended attribute attribute on path to value. -path may be either a string, a path-like object, or an open file descriptor. -If follow_symlinks is False, and the last element of the path is a symbolic - link, setxattr will modify the symbolic link itself instead of the file - the link points to. +path may be either a string, a path-like object, or an open file +descriptor. +If follow_symlinks is False, and the last element of the path is +a symbolic link, setxattr will modify the symbolic link itself instead +of the file the link points to. [clinic start generated code]*/ static PyObject * os_setxattr_impl(PyObject *module, path_t *path, path_t *attribute, Py_buffer *value, int flags, int follow_symlinks) -/*[clinic end generated code: output=98b83f63fdde26bb input=c17c0103009042f0]*/ +/*[clinic end generated code: output=98b83f63fdde26bb input=6c4ee6724e8947a4]*/ { ssize_t result; - if (fd_and_follow_symlinks_invalid("setxattr", path->fd, follow_symlinks)) + if (fd_and_follow_symlinks_invalid("setxattr", path->is_fd, follow_symlinks)) return NULL; if (PySys_Audit("os.setxattr", "OOy#i", path->object, attribute->object, @@ -14993,7 +15555,7 @@ os_setxattr_impl(PyObject *module, path_t *path, path_t *attribute, } Py_BEGIN_ALLOW_THREADS; - if (path->fd > -1) + if (path->is_fd) result = fsetxattr(path->fd, attribute->narrow, value->buf, value->len, flags); else if (follow_symlinks) @@ -15023,21 +15585,22 @@ os.removexattr Remove extended attribute attribute on path. -path may be either a string, a path-like object, or an open file descriptor. -If follow_symlinks is False, and the last element of the path is a symbolic - link, removexattr will modify the symbolic link itself instead of the file - the link points to. +path may be either a string, a path-like object, or an open file +descriptor. +If follow_symlinks is False, and the last element of the path is +a symbolic link, removexattr will modify the symbolic link itself +instead of the file the link points to. [clinic start generated code]*/ static PyObject * os_removexattr_impl(PyObject *module, path_t *path, path_t *attribute, int follow_symlinks) -/*[clinic end generated code: output=521a51817980cda6 input=3d9a7d36fe2f7c4e]*/ +/*[clinic end generated code: output=521a51817980cda6 input=a7ec62a86aa83f01]*/ { ssize_t result; - if (fd_and_follow_symlinks_invalid("removexattr", path->fd, follow_symlinks)) + if (fd_and_follow_symlinks_invalid("removexattr", path->is_fd, follow_symlinks)) return NULL; if (PySys_Audit("os.removexattr", "OO", path->object, attribute->object) < 0) { @@ -15045,7 +15608,7 @@ os_removexattr_impl(PyObject *module, path_t *path, path_t *attribute, } Py_BEGIN_ALLOW_THREADS; - if (path->fd > -1) + if (path->is_fd) result = fremovexattr(path->fd, attribute->narrow); else if (follow_symlinks) result = removexattr(path->narrow, attribute->narrow); @@ -15070,23 +15633,24 @@ os.listxattr Return a list of extended attributes on path. -path may be either None, a string, a path-like object, or an open file descriptor. -if path is None, listxattr will examine the current directory. -If follow_symlinks is False, and the last element of the path is a symbolic - link, listxattr will examine the symbolic link itself instead of the file - the link points to. +path may be either None, a string, a path-like object, or an open file +descriptor. If path is None, listxattr will examine the current +directory. +If follow_symlinks is False, and the last element of the path is +a symbolic link, listxattr will examine the symbolic link itself instead +of the file the link points to. [clinic start generated code]*/ static PyObject * os_listxattr_impl(PyObject *module, path_t *path, int follow_symlinks) -/*[clinic end generated code: output=bebdb4e2ad0ce435 input=9826edf9fdb90869]*/ +/*[clinic end generated code: output=bebdb4e2ad0ce435 input=cb4a6414afaa99bd]*/ { Py_ssize_t i; PyObject *result = NULL; const char *name; char *buffer = NULL; - if (fd_and_follow_symlinks_invalid("listxattr", path->fd, follow_symlinks)) + if (fd_and_follow_symlinks_invalid("listxattr", path->is_fd, follow_symlinks)) goto exit; if (PySys_Audit("os.listxattr", "(O)", @@ -15113,7 +15677,7 @@ os_listxattr_impl(PyObject *module, path_t *path, int follow_symlinks) } Py_BEGIN_ALLOW_THREADS; - if (path->fd > -1) + if (path->is_fd) length = flistxattr(path->fd, buffer, buffer_size); else if (follow_symlinks) length = listxattr(name, buffer, buffer_size); @@ -15166,9 +15730,10 @@ os_listxattr_impl(PyObject *module, path_t *path, int follow_symlinks) /*[clinic input] +@permit_long_summary os.urandom - size: Py_ssize_t + size: Py_ssize_t(allow_negative=False) / Return a bytes object containing random bytes suitable for cryptographic use. @@ -15176,38 +15741,38 @@ Return a bytes object containing random bytes suitable for cryptographic use. static PyObject * os_urandom_impl(PyObject *module, Py_ssize_t size) -/*[clinic end generated code: output=42c5cca9d18068e9 input=4067cdb1b6776c29]*/ +/*[clinic end generated code: output=42c5cca9d18068e9 input=58a0def87dbc2c22]*/ { - PyObject *bytes; - int result; - - if (size < 0) + if (size < 0) { return PyErr_Format(PyExc_ValueError, "negative argument not allowed"); - bytes = PyBytes_FromStringAndSize(NULL, size); - if (bytes == NULL) + } + + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; + } - result = _PyOS_URandom(PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes)); + int result = _PyOS_URandom(PyBytesWriter_GetData(writer), size); if (result == -1) { - Py_DECREF(bytes); + PyBytesWriter_Discard(writer); return NULL; } - return bytes; + return PyBytesWriter_Finish(writer); } #ifdef HAVE_MEMFD_CREATE /*[clinic input] os.memfd_create - name: FSConverter + name: unicode_fs_encoded flags: unsigned_int(bitwise=True, c_default="MFD_CLOEXEC") = MFD_CLOEXEC [clinic start generated code]*/ static PyObject * os_memfd_create_impl(PyObject *module, PyObject *name, unsigned int flags) -/*[clinic end generated code: output=6681ede983bdb9a6 input=a42cfc199bcd56e9]*/ +/*[clinic end generated code: output=6681ede983bdb9a6 input=cd0eb092cfac474b]*/ { int fd; const char *bytes = PyBytes_AS_STRING(name); @@ -15981,7 +16546,7 @@ static PyMemberDef DirEntry_members[] = { {"name", Py_T_OBJECT_EX, offsetof(DirEntry, name), Py_READONLY, "the entry's base filename, relative to scandir() \"path\" argument"}, {"path", Py_T_OBJECT_EX, offsetof(DirEntry, path), Py_READONLY, - "the entry's full path name; equivalent to os.path.join(scandir_path, entry.name)"}, + "the entry's full path name; equivalent to\nos.path.join(scandir_path, entry.name)"}, {NULL} }; @@ -15996,7 +16561,7 @@ static PyMethodDef DirEntry_methods[] = { OS_DIRENTRY_INODE_METHODDEF OS_DIRENTRY___FSPATH___METHODDEF {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, PyDoc_STR("DirEntry is generic over the type of the path (str or bytes)")}, {NULL} }; @@ -16009,11 +16574,14 @@ static PyType_Slot DirEntryType_slots[] = { }; static PyType_Spec DirEntryType_spec = { - MODNAME ".DirEntry", - sizeof(DirEntry), - 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, - DirEntryType_slots + .name = MODNAME ".DirEntry", + .basicsize = sizeof(DirEntry), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = DirEntryType_slots }; @@ -16162,7 +16730,7 @@ DirEntry_from_posix_info(PyObject *module, path_t *path, const char *name, entry->stat = NULL; entry->lstat = NULL; - if (path->fd != -1) { + if (path->is_fd) { entry->dir_fd = path->fd; joined_path = NULL; } @@ -16187,7 +16755,7 @@ DirEntry_from_posix_info(PyObject *module, path_t *path, const char *name, if (!entry->name) goto error; - if (path->fd != -1) { + if (path->is_fd) { entry->path = Py_NewRef(entry->name); } else if (!entry->path) @@ -16311,8 +16879,9 @@ ScandirIterator_closedir(ScandirIterator *iterator) iterator->dirp = NULL; Py_BEGIN_ALLOW_THREADS #ifdef HAVE_FDOPENDIR - if (iterator->path.fd != -1) + if (iterator->path.is_fd) { rewinddir(dirp); + } #endif closedir(dirp); Py_END_ALLOW_THREADS @@ -16451,14 +17020,17 @@ static PyType_Slot ScandirIteratorType_slots[] = { }; static PyType_Spec ScandirIteratorType_spec = { - MODNAME ".ScandirIterator", - sizeof(ScandirIterator), - 0, + .name = MODNAME ".ScandirIterator", + .basicsize = sizeof(ScandirIterator), // bpo-40549: Py_TPFLAGS_BASETYPE should not be used, since // PyType_GetModule(Py_TYPE(self)) doesn't work on a subclass instance. - (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE - | Py_TPFLAGS_DISALLOW_INSTANTIATION), - ScandirIteratorType_slots + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_HAVE_FINALIZE + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = ScandirIteratorType_slots }; /*[clinic input] @@ -16468,16 +17040,16 @@ os.scandir Return an iterator of DirEntry objects for given path. -path can be specified as either str, bytes, or a path-like object. If path -is bytes, the names of yielded DirEntry objects will also be bytes; in -all other circumstances they will be str. +path can be specified as either str, bytes, or a path-like object. If +path is bytes, the names of yielded DirEntry objects will also be bytes; +in all other circumstances they will be str. If path is None, uses the path='.'. [clinic start generated code]*/ static PyObject * os_scandir_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=6eb2668b675ca89e input=6bdd312708fc3bb0]*/ +/*[clinic end generated code: output=6eb2668b675ca89e input=6ab9600993f51577]*/ { ScandirIterator *iterator; #ifdef MS_WINDOWS @@ -16529,7 +17101,7 @@ os_scandir_impl(PyObject *module, path_t *path) #else /* POSIX */ errno = 0; #ifdef HAVE_FDOPENDIR - if (iterator->path.fd != -1) { + if (iterator->path.is_fd) { if (HAVE_FDOPENDIR_RUNTIME) { /* closedir() closes the FD, so we duplicate it */ fd = _Py_dup(iterator->path.fd); @@ -16630,14 +17202,14 @@ os.fspath Return the file system path representation of the object. -If the object is str or bytes, then allow it to pass through as-is. If the -object defines __fspath__(), then return the result of that method. All other -types raise a TypeError. +If the object is str or bytes, then allow it to pass through as-is. If +the object defines __fspath__(), then return the result of that method. +All other types raise a TypeError. [clinic start generated code]*/ static PyObject * os_fspath_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=c3c3b78ecff2914f input=e357165f7b22490f]*/ +/*[clinic end generated code: output=c3c3b78ecff2914f input=d3c54404240d5da0]*/ { return PyOS_FSPath(path); } @@ -16656,25 +17228,20 @@ static PyObject * os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags) /*[clinic end generated code: output=b3a618196a61409c input=59bafac39c594947]*/ { - PyObject *bytes; - Py_ssize_t n; - if (size < 0) { errno = EINVAL; return posix_error(); } - bytes = PyBytes_FromStringAndSize(NULL, size); - if (bytes == NULL) { - PyErr_NoMemory(); + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; } + void *data = PyBytesWriter_GetData(writer); + Py_ssize_t n; while (1) { - n = syscall(SYS_getrandom, - PyBytes_AS_STRING(bytes), - PyBytes_GET_SIZE(bytes), - flags); + n = syscall(SYS_getrandom, data, size, flags); if (n < 0 && errno == EINTR) { if (PyErr_CheckSignals() < 0) { goto error; @@ -16691,14 +17258,12 @@ os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags) goto error; } - if (n != size) { - _PyBytes_Resize(&bytes, n); - } + _Py_MSAN_UNPOISON(data, size); - return bytes; + return PyBytesWriter_FinishWithSize(writer, n); error: - Py_DECREF(bytes); + PyBytesWriter_Discard(writer); return NULL; } #endif /* HAVE_GETRANDOM_SYSCALL */ @@ -16821,13 +17386,13 @@ On Unix: On Windows, return status shifted right by 8 bits. On Unix, if the process is being traced or if waitpid() was called with -WUNTRACED option, the caller must first check if WIFSTOPPED(status) is true. -This function must not be called if WIFSTOPPED(status) is true. +WUNTRACED option, the caller must first check if WIFSTOPPED(status) is +true. This function must not be called if WIFSTOPPED(status) is true. [clinic start generated code]*/ static PyObject * os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj) -/*[clinic end generated code: output=db50b1b0ba3c7153 input=7fe2d7fdaea3db42]*/ +/*[clinic end generated code: output=db50b1b0ba3c7153 input=3b44a23f5090006c]*/ { #ifndef MS_WINDOWS int status = PyLong_AsInt(status_obj); @@ -16917,12 +17482,12 @@ os__supports_virtual_terminal_impl(PyObject *module) /*[clinic input] os._inputhook -Calls PyOS_CallInputHook droppong the GIL first +Calls PyOS_InputHook dropping the GIL first [clinic start generated code]*/ static PyObject * os__inputhook_impl(PyObject *module) -/*[clinic end generated code: output=525aca4ef3c6149f input=fc531701930d064f]*/ +/*[clinic end generated code: output=525aca4ef3c6149f input=b5018fa1ec3aa440]*/ { int result = 0; if (PyOS_InputHook) { @@ -16936,12 +17501,12 @@ os__inputhook_impl(PyObject *module) /*[clinic input] os._is_inputhook_installed -Checks if PyOS_CallInputHook is set +Checks if PyOS_InputHook is set [clinic start generated code]*/ static PyObject * os__is_inputhook_installed_impl(PyObject *module) -/*[clinic end generated code: output=3b3eab4f672c689a input=ff177c9938dd76d8]*/ +/*[clinic end generated code: output=3b3eab4f672c689a input=757820f79f48820c]*/ { return PyBool_FromLong(PyOS_InputHook != NULL); } @@ -16998,6 +17563,7 @@ os__emscripten_log_impl(PyObject *module, const char *arg) static PyMethodDef posix_methods[] = { OS_STAT_METHODDEF + OS_STATX_METHODDEF OS_ACCESS_METHODDEF OS_TTYNAME_METHODDEF OS_CHDIR_METHODDEF @@ -17095,6 +17661,7 @@ static PyMethodDef posix_methods[] = { OS_WAITID_METHODDEF OS_WAITPID_METHODDEF OS_PIDFD_OPEN_METHODDEF + OS_PIDFD_GETFD_METHODDEF OS_GETSID_METHODDEF OS_SETSID_METHODDEF OS_SETPGID_METHODDEF @@ -17133,6 +17700,7 @@ static PyMethodDef posix_methods[] = { OS_POSIX_FADVISE_METHODDEF OS_PUTENV_METHODDEF OS_UNSETENV_METHODDEF + OS__CLEARENV_METHODDEF OS_STRERROR_METHODDEF OS_FCHDIR_METHODDEF OS_FSYNC_METHODDEF @@ -17621,6 +18189,12 @@ all_ins(PyObject *m) #ifdef RWF_NOWAIT if (PyModule_AddIntConstant(m, "RWF_NOWAIT", RWF_NOWAIT)) return -1; #endif +#ifdef RWF_DONTCACHE + if (PyModule_AddIntConstant(m, "RWF_DONTCACHE", RWF_DONTCACHE)) return -1; +#endif +#ifdef RWF_ATOMIC + if (PyModule_AddIntConstant(m, "RWF_ATOMIC", RWF_ATOMIC)) return -1; +#endif #ifdef RWF_APPEND if (PyModule_AddIntConstant(m, "RWF_APPEND", RWF_APPEND)) return -1; #endif @@ -17836,6 +18410,53 @@ all_ins(PyObject *m) #endif #endif /* HAVE_EVENTFD && EFD_CLOEXEC */ +#ifdef NODEV + if (PyModule_Add(m, "NODEV", _PyLong_FromDev(NODEV))) return -1; +#endif + +#ifdef AT_NO_AUTOMOUNT + if (PyModule_AddIntMacro(m, AT_NO_AUTOMOUNT)) return -1; +#endif + +#ifdef HAVE_STATX + if (PyModule_AddIntMacro(m, STATX_TYPE)) return -1; + if (PyModule_AddIntMacro(m, STATX_MODE)) return -1; + if (PyModule_AddIntMacro(m, STATX_NLINK)) return -1; + if (PyModule_AddIntMacro(m, STATX_UID)) return -1; + if (PyModule_AddIntMacro(m, STATX_GID)) return -1; + if (PyModule_AddIntMacro(m, STATX_ATIME)) return -1; + if (PyModule_AddIntMacro(m, STATX_MTIME)) return -1; + if (PyModule_AddIntMacro(m, STATX_CTIME)) return -1; + if (PyModule_AddIntMacro(m, STATX_INO)) return -1; + if (PyModule_AddIntMacro(m, STATX_SIZE)) return -1; + if (PyModule_AddIntMacro(m, STATX_BLOCKS)) return -1; + if (PyModule_AddIntMacro(m, STATX_BASIC_STATS)) return -1; + if (PyModule_AddIntMacro(m, STATX_BTIME)) return -1; +#ifdef STATX_MNT_ID + if (PyModule_AddIntMacro(m, STATX_MNT_ID)) return -1; +#endif +#ifdef STATX_DIOALIGN + if (PyModule_AddIntMacro(m, STATX_DIOALIGN)) return -1; +#endif +#ifdef STATX_MNT_ID_UNIQUE + if (PyModule_AddIntMacro(m, STATX_MNT_ID_UNIQUE)) return -1; +#endif +#ifdef STATX_SUBVOL + if (PyModule_AddIntMacro(m, STATX_SUBVOL)) return -1; +#endif +#ifdef STATX_WRITE_ATOMIC + if (PyModule_AddIntMacro(m, STATX_WRITE_ATOMIC)) return -1; +#endif +#ifdef STATX_DIO_READ_ALIGN + if (PyModule_AddIntMacro(m, STATX_DIO_READ_ALIGN)) return -1; +#endif + /* STATX_ALL intentionally omitted because it is deprecated */ + if (PyModule_AddIntMacro(m, AT_STATX_SYNC_AS_STAT)) return -1; + if (PyModule_AddIntMacro(m, AT_STATX_FORCE_SYNC)) return -1; + if (PyModule_AddIntMacro(m, AT_STATX_DONT_SYNC)) return -1; + /* STATX_ATTR_* constants are in the stat module */ +#endif /* HAVE_STATX */ + #if defined(__APPLE__) if (PyModule_AddIntConstant(m, "_COPYFILE_DATA", COPYFILE_DATA)) return -1; if (PyModule_AddIntConstant(m, "_COPYFILE_STAT", COPYFILE_STAT)) return -1; @@ -18107,6 +18728,24 @@ posixmodule_exec(PyObject *m) } #endif +#ifdef HAVE_STATX + if (statx == NULL) { + PyObject* dct = PyModule_GetDict(m); + if (dct == NULL) { + return -1; + } + if (PyDict_PopString(dct, "statx", NULL) < 0) { + return -1; + } + } + else { + state->StatxResultType = PyType_FromModuleAndSpec(m, &pystatx_result_spec, NULL); + if (PyModule_AddObjectRef(m, "statx_result", state->StatxResultType) < 0) { + return -1; + } + } +#endif + /* Initialize environ dictionary */ if (PyModule_Add(m, "environ", convertenviron()) != 0) { return -1; @@ -18123,14 +18762,12 @@ posixmodule_exec(PyObject *m) } #if defined(HAVE_WAITID) - waitid_result_desc.name = MODNAME ".waitid_result"; state->WaitidResultType = (PyObject *)PyStructSequence_NewType(&waitid_result_desc); if (PyModule_AddObjectRef(m, "waitid_result", state->WaitidResultType) < 0) { return -1; } #endif - stat_result_desc.name = "os.stat_result"; /* see issue #19209 */ stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; stat_result_desc.fields[9].name = PyStructSequence_UnnamedField; @@ -18141,14 +18778,12 @@ posixmodule_exec(PyObject *m) state->statresult_new_orig = ((PyTypeObject *)state->StatResultType)->tp_new; ((PyTypeObject *)state->StatResultType)->tp_new = statresult_new; - statvfs_result_desc.name = "os.statvfs_result"; /* see issue #19209 */ state->StatVFSResultType = (PyObject *)PyStructSequence_NewType(&statvfs_result_desc); if (PyModule_AddObjectRef(m, "statvfs_result", state->StatVFSResultType) < 0) { return -1; } #if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) - sched_param_desc.name = MODNAME ".sched_param"; state->SchedParamType = (PyObject *)PyStructSequence_NewType(&sched_param_desc); if (PyModule_AddObjectRef(m, "sched_param", state->SchedParamType) < 0) { return -1; @@ -18180,7 +18815,6 @@ posixmodule_exec(PyObject *m) return -1; } - times_result_desc.name = MODNAME ".times_result"; state->TimesResultType = (PyObject *)PyStructSequence_NewType(&times_result_desc); if (PyModule_AddObjectRef(m, "times_result", state->TimesResultType) < 0) { return -1; @@ -18245,6 +18879,7 @@ posixmodule_exec(PyObject *m) static PyModuleDef_Slot posixmodile_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, posixmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index ac1a04a54edd60..4a2b33f8700d10 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -302,6 +302,7 @@ pwd_getpwnam_impl(PyObject *module, PyObject *name) #ifdef HAVE_GETPWENT /*[clinic input] +@permit_long_summary pwd.getpwall Return a list of all available password database entries, in arbitrary order. @@ -311,7 +312,7 @@ See help(pwd) for more on password database entries. static PyObject * pwd_getpwall_impl(PyObject *module) -/*[clinic end generated code: output=4853d2f5a0afac8a input=d7ecebfd90219b85]*/ +/*[clinic end generated code: output=4853d2f5a0afac8a input=f8145e0d9a79e32c]*/ { PyObject *d; struct passwd *p; @@ -371,6 +372,7 @@ pwdmodule_exec(PyObject *module) } static PyModuleDef_Slot pwdmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, pwdmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index c449dd848d10c6..53d42ad50e37b9 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -3,12 +3,16 @@ #endif #include "Python.h" +#include "pycore_ceval.h" // _Py_EnterRecursiveCall() +#include "pycore_codecs.h" // _PyCodec_LookupTextEncoding() #include "pycore_import.h" // _PyImport_SetModule() #include "pycore_pyhash.h" // _Py_HashSecret #include "pycore_traceback.h" // _PyTraceback_Add() #include <stdbool.h> #include <stddef.h> // offsetof() + +#include "expat_config.h" #include "expat.h" #include "pyexpat.h" @@ -74,6 +78,15 @@ typedef struct { PyObject_HEAD XML_Parser itself; + /* + * Strong reference to a parent `xmlparseobject` if this parser + * is a child parser. Set to NULL if this parser is a root parser. + * This is needed to keep the parent parser alive as long as it has + * at least one child parser. + * + * See https://github.com/python/cpython/issues/139400 for details. + */ + PyObject *parent; int ordered_attributes; /* Return attributes as a list. */ int specified_attributes; /* Report only specified attributes. */ int in_callback; /* Is a callback active? */ @@ -122,49 +135,90 @@ CALL_XML_HANDLER_SETTER(const struct HandlerInfo *handler_info, setter(xml_parser, xml_handler); } +static int +set_xml_error_attr_code(PyObject *err, enum XML_Error code) +{ + PyObject *v = PyLong_FromLong((long)code); + int ok = v != NULL && PyObject_SetAttr(err, &_Py_ID(code), v) != -1; + Py_XDECREF(v); + return ok; +} + /* Set an integer attribute on the error object; return true on success, * false on an exception. */ static int -set_error_attr(PyObject *err, const char *name, int value) +set_xml_error_attr_location(PyObject *err, const char *name, XML_Size value) { - PyObject *v = PyLong_FromLong(value); + PyObject *v = PyLong_FromSize_t((size_t)value); + int ok = v != NULL && PyObject_SetAttrString(err, name, v) != -1; + Py_XDECREF(v); + return ok; +} - if (v == NULL || PyObject_SetAttrString(err, name, v) == -1) { - Py_XDECREF(v); - return 0; + +static PyObject * +set_xml_error(pyexpat_state *state, + enum XML_Error code, XML_Size lineno, XML_Size column, + const char *errmsg) +{ + PyObject *arg; + if (errmsg == NULL) { + arg = PyUnicode_FromFormat( + "%s: line %zu, column %zu", + XML_ErrorString(code), + (size_t)lineno, (size_t)column + ); } - Py_DECREF(v); - return 1; + else { + arg = PyUnicode_FromStringAndSize(errmsg, strlen(errmsg)); + } + if (arg == NULL) { + return NULL; + } + PyObject *res = PyObject_CallOneArg(state->error, arg); + Py_DECREF(arg); + if ( + res != NULL + && set_xml_error_attr_code(res, code) + && set_xml_error_attr_location(res, "lineno", lineno) + && set_xml_error_attr_location(res, "offset", column) + ) { + PyErr_SetObject(state->error, res); + } + Py_XDECREF(res); + return NULL; } +#define SET_XML_ERROR(STATE, SELF, CODE, ERRMSG) \ + do { \ + XML_Parser parser = SELF->itself; \ + assert(parser != NULL); \ + XML_Size lineno = XML_GetCurrentLineNumber(parser); \ + XML_Size column = XML_GetCurrentColumnNumber(parser); \ + (void)set_xml_error(state, CODE, lineno, column, ERRMSG); \ + } while (0) + /* Build and set an Expat exception, including positioning * information. Always returns NULL. */ static PyObject * set_error(pyexpat_state *state, xmlparseobject *self, enum XML_Error code) { - PyObject *err; - PyObject *buffer; - XML_Parser parser = self->itself; - int lineno = XML_GetErrorLineNumber(parser); - int column = XML_GetErrorColumnNumber(parser); + SET_XML_ERROR(state, self, code, NULL); + return NULL; +} - buffer = PyUnicode_FromFormat("%s: line %i, column %i", - XML_ErrorString(code), lineno, column); - if (buffer == NULL) - return NULL; - err = PyObject_CallOneArg(state->error, buffer); - Py_DECREF(buffer); - if ( err != NULL - && set_error_attr(err, "code", code) - && set_error_attr(err, "offset", column) - && set_error_attr(err, "lineno", lineno)) { - PyErr_SetObject(state->error, err); - } - Py_XDECREF(err); +#if XML_COMBINED_VERSION >= 20400 +static PyObject * +set_invalid_arg(pyexpat_state *state, xmlparseobject *self, const char *errmsg) +{ + SET_XML_ERROR(state, self, XML_ERROR_INVALID_ARGUMENT, errmsg); return NULL; } +#endif + +#undef SET_XML_ERROR static int have_handler(xmlparseobject *self, int type) @@ -340,7 +394,7 @@ my_CharacterDataHandler(void *userData, const XML_Char *data, int len) if (self->buffer == NULL) call_character_handler(self, data, len); else { - if ((self->buffer_used + len) > self->buffer_size) { + if (len > (self->buffer_size - self->buffer_used)) { if (flush_character_buffer(self) < 0) return; /* handler might have changed; drop the rest on the floor @@ -450,6 +504,28 @@ my_StartElementHandler(void *userData, } } +static inline void +invalid_expat_handler_rv(const char *name) +{ + PyObject *exc = PyErr_GetRaisedException(); + assert(exc != NULL); + PyObject *note = PyUnicode_FromFormat("invalid '%s' event handler return value", name); + if (note == NULL) { + goto error; + } + int rc = _PyException_AddNote(exc, note); + Py_DECREF(note); + if (rc < 0) { + goto error; + }; + goto done; + +error: + PyErr_Clear(); +done: + PyErr_SetRaisedException(exc); +} + #define RC_HANDLER(RETURN_TYPE, NAME, PARAMS, \ INIT, PARSE_FORMAT, CONVERSION, \ RETURN_VARIABLE, GETUSERDATA) \ @@ -483,6 +559,9 @@ my_ ## NAME ## Handler PARAMS { \ } \ CONVERSION \ Py_DECREF(rv); \ + if (PyErr_Occurred()) { \ + invalid_expat_handler_rv(#NAME); \ + } \ return RETURN_VARIABLE; \ } @@ -555,6 +634,10 @@ static PyObject * conv_content_model(XML_Content * const model, PyObject *(*conv_string)(void *)) { + if (_Py_EnterRecursiveCall(" in conv_content_model")) { + return NULL; + } + PyObject *result = NULL; PyObject *children = PyTuple_New(model->numchildren); int i; @@ -566,7 +649,7 @@ conv_content_model(XML_Content * const model, conv_string); if (child == NULL) { Py_XDECREF(children); - return NULL; + goto done; } PyTuple_SET_ITEM(children, i, child); } @@ -574,6 +657,8 @@ conv_content_model(XML_Content * const model, model->type, model->quant, conv_string, model->name, children); } +done: + _Py_LeaveRecursiveCall(); return result; } @@ -590,7 +675,7 @@ my_ElementDeclHandler(void *userData, PyObject *modelobj, *nameobj; if (PyErr_Occurred()) - return; + goto finally; if (flush_character_buffer(self) < 0) goto finally; @@ -746,6 +831,7 @@ get_parse_result(pyexpat_state *state, xmlparseobject *self, int rv) #define MAX_CHUNK_SIZE (1 << 20) /*[clinic input] +@permit_long_summary pyexpat.xmlparser.SetReparseDeferralEnabled enabled: bool @@ -757,7 +843,7 @@ Enable/Disable reparse deferral; enabled by default with Expat >=2.6.0. static PyObject * pyexpat_xmlparser_SetReparseDeferralEnabled_impl(xmlparseobject *self, int enabled) -/*[clinic end generated code: output=5ec539e3b63c8c49 input=021eb9e0bafc32c5]*/ +/*[clinic end generated code: output=5ec539e3b63c8c49 input=6d3743500dcee799]*/ { #if XML_COMBINED_VERSION >= 20600 XML_SetReparseDeferralEnabled(self->itself, enabled ? XML_TRUE : XML_FALSE); @@ -767,6 +853,7 @@ pyexpat_xmlparser_SetReparseDeferralEnabled_impl(xmlparseobject *self, } /*[clinic input] +@permit_long_summary pyexpat.xmlparser.GetReparseDeferralEnabled Retrieve reparse deferral enabled status; always returns false with Expat <2.6.0. @@ -774,7 +861,7 @@ Retrieve reparse deferral enabled status; always returns false with Expat <2.6.0 static PyObject * pyexpat_xmlparser_GetReparseDeferralEnabled_impl(xmlparseobject *self) -/*[clinic end generated code: output=4e91312e88a595a8 input=54b5f11d32b20f3e]*/ +/*[clinic end generated code: output=4e91312e88a595a8 input=ae02d7152ab9e2d0]*/ { return PyBool_FromLong(self->reparse_deferral_enabled); } @@ -968,17 +1055,19 @@ pyexpat_xmlparser_GetBase_impl(xmlparseobject *self) } /*[clinic input] +@permit_long_summary pyexpat.xmlparser.GetInputContext Return the untranslated text of the input that caused the current event. -If the event was generated by a large amount of text (such as a start tag -for an element with many attributes), not all of the text may be available. +If the event was generated by a large amount of text (such as +a start tag for an element with many attributes), not all of the +text may be available. [clinic start generated code]*/ static PyObject * pyexpat_xmlparser_GetInputContext_impl(xmlparseobject *self) -/*[clinic end generated code: output=a88026d683fc22cc input=034df8712db68379]*/ +/*[clinic end generated code: output=a88026d683fc22cc input=a672f48f09bb73d2]*/ { if (self->in_callback) { int offset, size; @@ -996,6 +1085,7 @@ pyexpat_xmlparser_GetInputContext_impl(xmlparseobject *self) } /*[clinic input] +@permit_long_summary pyexpat.xmlparser.ExternalEntityParserCreate cls: defining_class @@ -1011,7 +1101,7 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self, PyTypeObject *cls, const char *context, const char *encoding) -/*[clinic end generated code: output=01d4472b49cb3f92 input=ec70c6b9e6e9619a]*/ +/*[clinic end generated code: output=01d4472b49cb3f92 input=550479eaff952cc0]*/ { xmlparseobject *new_parser; pyexpat_state *state = PyType_GetModuleState(cls); @@ -1030,6 +1120,10 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self, new_parser->ns_prefixes = self->ns_prefixes; new_parser->itself = XML_ExternalEntityParserCreate(self->itself, context, encoding); + // The new subparser will make use of the parent XML_Parser inside of Expat. + // So we need to take subparsers into account with the reference counting + // of their parent parser. + new_parser->parent = Py_NewRef(self); new_parser->handlers = 0; new_parser->intern = Py_XNewRef(self->intern); @@ -1074,6 +1168,7 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self, } /*[clinic input] +@permit_long_summary pyexpat.xmlparser.SetParamEntityParsing flag: int @@ -1089,7 +1184,7 @@ was successful. static PyObject * pyexpat_xmlparser_SetParamEntityParsing_impl(xmlparseobject *self, int flag) -/*[clinic end generated code: output=18668ee8e760d64c input=8aea19b4b15e9af1]*/ +/*[clinic end generated code: output=18668ee8e760d64c input=1c43532fcb405879]*/ { flag = XML_SetParamEntityParsing(self->itself, flag); return PyLong_FromLong(flag); @@ -1098,6 +1193,7 @@ pyexpat_xmlparser_SetParamEntityParsing_impl(xmlparseobject *self, int flag) #if XML_COMBINED_VERSION >= 19505 /*[clinic input] +@permit_long_summary pyexpat.xmlparser.UseForeignDTD cls: defining_class @@ -1106,15 +1202,16 @@ pyexpat.xmlparser.UseForeignDTD Allows the application to provide an artificial external subset if one is not specified as part of the document instance. -This readily allows the use of a 'default' document type controlled by the -application, while still getting the advantage of providing document type -information to the parser. 'flag' defaults to True if not provided. +This readily allows the use of a 'default' document type controlled +by the application, while still getting the advantage of providing +document type information to the parser. 'flag' defaults to True if +not provided. [clinic start generated code]*/ static PyObject * pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls, int flag) -/*[clinic end generated code: output=d7d98252bd25a20f input=23440ecb0573fb29]*/ +/*[clinic end generated code: output=d7d98252bd25a20f input=2920baa5bf24714d]*/ { pyexpat_state *state = PyType_GetModuleState(cls); enum XML_Error rc; @@ -1127,6 +1224,195 @@ pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls, } #endif +#if XML_COMBINED_VERSION >= 20400 +static PyObject * +set_activation_threshold(xmlparseobject *self, + PyTypeObject *cls, + unsigned long long threshold, + XML_Bool (*setter)(XML_Parser, unsigned long long)) +{ + assert(self->itself != NULL); + if (setter(self->itself, threshold) == XML_TRUE) { + Py_RETURN_NONE; + } + // The setter fails if self->itself is NULL (which is not possible here) + // or is a non-root parser, which currently only happens for parsers + // created by ExternalEntityParserCreate(). + pyexpat_state *state = PyType_GetModuleState(cls); + return set_invalid_arg(state, self, "parser must be a root parser"); +} + +static PyObject * +set_maximum_amplification(xmlparseobject *self, + PyTypeObject *cls, + float max_factor, + XML_Bool (*setter)(XML_Parser, float)) +{ + assert(self->itself != NULL); + if (setter(self->itself, max_factor) == XML_TRUE) { + Py_RETURN_NONE; + } + // The setter fails if self->itself is NULL (which is not possible here), + // is a non-root parser, which currently only happens for parsers created + // by ExternalEntityParserCreate(), or if 'max_factor' is NaN or < 1.0. + pyexpat_state *state = PyType_GetModuleState(cls); + // Note: Expat has no API to determine whether a parser is a root parser, + // and since the Expat functions for defining the various maximum allowed + // amplifcation factors fail when a bad parser or an out-of-range factor + // is given without specifying which check failed, we check whether the + // factor is out-of-range to improve the error message. See also gh-90949. + const char *message = (isnan(max_factor) || max_factor < 1.0f) + ? "'max_factor' must be at least 1.0" + : "parser must be a root parser"; + return set_invalid_arg(state, self, message); +} +#endif + +#if XML_COMBINED_VERSION >= 20400 +/*[clinic input] +@permit_long_summary +pyexpat.xmlparser.SetBillionLaughsAttackProtectionActivationThreshold + + cls: defining_class + threshold: unsigned_long_long + / + +Sets the number of output bytes needed to activate protection against billion laughs attacks. + +The number of output bytes includes amplification from entity +expansion and reading DTD files. + +Parser objects usually have a protection activation threshold of +8 MiB, but the actual default value depends on the underlying Expat +library. + +Activation thresholds below 4 MiB are known to break support for +DITA 1.3 payload and are hence not recommended. +[clinic start generated code]*/ + +static PyObject * +pyexpat_xmlparser_SetBillionLaughsAttackProtectionActivationThreshold_impl(xmlparseobject *self, + PyTypeObject *cls, + unsigned long long threshold) +/*[clinic end generated code: output=0c082342f1c78114 input=8d84b0e3a873cdba]*/ +{ + return set_activation_threshold( + self, cls, threshold, + XML_SetBillionLaughsAttackProtectionActivationThreshold + ); +} +#endif + +#if XML_COMBINED_VERSION >= 20400 +/*[clinic input] +@permit_long_summary +pyexpat.xmlparser.SetBillionLaughsAttackProtectionMaximumAmplification + + cls: defining_class + max_factor: float + / + +Sets the maximum tolerated amplification factor for protection against billion laughs attacks. + +The amplification factor is calculated as "(direct + indirect) / +direct" while parsing, where "direct" is the number of bytes read +from the primary document in parsing and "indirect" is the number of +bytes added by expanding entities and reading external DTD files, +combined. + +The 'max_factor' value must be a non-NaN floating point value +greater than or equal to 1.0. Amplification factors greater than +30,000 can be observed in the middle of parsing even with benign +files in practice. In particular, the activation threshold should +be carefully chosen to avoid false positives. + +Parser objects usually have a maximum amplification factor of 100, +but the actual default value depends on the underlying Expat +library. +[clinic start generated code]*/ + +static PyObject * +pyexpat_xmlparser_SetBillionLaughsAttackProtectionMaximumAmplification_impl(xmlparseobject *self, + PyTypeObject *cls, + float max_factor) +/*[clinic end generated code: output=c590439eadf463fa input=d0f11971c5b9e98b]*/ +{ + return set_maximum_amplification( + self, cls, max_factor, + XML_SetBillionLaughsAttackProtectionMaximumAmplification + ); +} +#endif + +#if XML_COMBINED_VERSION >= 20702 +/*[clinic input] +@permit_long_summary +pyexpat.xmlparser.SetAllocTrackerActivationThreshold + + cls: defining_class + threshold: unsigned_long_long + / + +Sets the number of allocated bytes of dynamic memory needed to activate protection against disproportionate use of RAM. + +Parser objects usually have an allocation activation threshold of +64 MiB, but the actual default value depends on the underlying Expat +library. +[clinic start generated code]*/ + +static PyObject * +pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl(xmlparseobject *self, + PyTypeObject *cls, + unsigned long long threshold) +/*[clinic end generated code: output=bed7e93207ba08c5 input=4728360b545de87a]*/ +{ + return set_activation_threshold( + self, cls, threshold, + XML_SetAllocTrackerActivationThreshold + ); +} +#endif + +#if XML_COMBINED_VERSION >= 20702 +/*[clinic input] +@permit_long_summary +pyexpat.xmlparser.SetAllocTrackerMaximumAmplification + + cls: defining_class + max_factor: float + / + +Sets the maximum amplification factor between direct input and bytes of dynamic memory allocated. + +The amplification factor is calculated as "allocated / direct" while +parsing, where "direct" is the number of bytes read from the primary +document in parsing and "allocated" is the number of bytes of +dynamic memory allocated in the parser hierarchy. + +The 'max_factor' value must be a non-NaN floating point value +greater than or equal to 1.0. Amplification factors greater than +100.0 can be observed near the start of parsing even with benign +files in practice. In particular, the activation threshold should +be carefully chosen to avoid false positives. + +Parser objects usually have a maximum amplification factor of 100, +but the actual default value depends on the underlying Expat +library. +[clinic start generated code]*/ + +static PyObject * +pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self, + PyTypeObject *cls, + float max_factor) +/*[clinic end generated code: output=6e44bd48c9b112a0 input=dd23ea3ef2069b69]*/ +{ + return set_maximum_amplification( + self, cls, max_factor, + XML_SetAllocTrackerMaximumAmplification + ); +} +#endif + static struct PyMethodDef xmlparse_methods[] = { PYEXPAT_XMLPARSER_PARSE_METHODDEF PYEXPAT_XMLPARSER_PARSEFILE_METHODDEF @@ -1135,9 +1421,11 @@ static struct PyMethodDef xmlparse_methods[] = { PYEXPAT_XMLPARSER_GETINPUTCONTEXT_METHODDEF PYEXPAT_XMLPARSER_EXTERNALENTITYPARSERCREATE_METHODDEF PYEXPAT_XMLPARSER_SETPARAMENTITYPARSING_METHODDEF -#if XML_COMBINED_VERSION >= 19505 PYEXPAT_XMLPARSER_USEFOREIGNDTD_METHODDEF -#endif + PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONACTIVATIONTHRESHOLD_METHODDEF + PYEXPAT_XMLPARSER_SETBILLIONLAUGHSATTACKPROTECTIONMAXIMUMAMPLIFICATION_METHODDEF + PYEXPAT_XMLPARSER_SETALLOCTRACKERACTIVATIONTHRESHOLD_METHODDEF + PYEXPAT_XMLPARSER_SETALLOCTRACKERMAXIMUMAMPLIFICATION_METHODDEF PYEXPAT_XMLPARSER_SETREPARSEDEFERRALENABLED_METHODDEF PYEXPAT_XMLPARSER_GETREPARSEDEFERRALENABLED_METHODDEF {NULL, NULL} /* sentinel */ @@ -1151,6 +1439,60 @@ static struct PyMethodDef xmlparse_methods[] = { Make it as simple as possible. */ +typedef struct { + int map[256]; + char name[0]; +} pyexpat_encoding_info; + +static pyexpat_encoding_info * +pyexpat_encoding_create(const char *name, PyObject *mapping) +{ + if (!PyTuple_Check(mapping) || PyTuple_GET_SIZE(mapping) != 256) { + PyErr_SetString(PyExc_ValueError, + "_expat_decoding_table must be a 256-tuple of integers"); + return NULL; + } + pyexpat_encoding_info *info = (pyexpat_encoding_info *)PyMem_Malloc( + sizeof(pyexpat_encoding_info) + strlen(name) + 1); + if (info == NULL) { + PyErr_NoMemory(); + return NULL; + } + for (int i = 0; i < 256; i++) { + int j = PyLong_AsInt(PyTuple_GET_ITEM(mapping, i)); + if (j == -1 && PyErr_Occurred()) { + PyMem_Free(info); + return NULL; + } + info->map[i] = j; + } + strcpy(info->name, name); + return info; +} + +static int +pyexpat_encoding_convert(void *data, const char *s) +{ + if (PyErr_Occurred()) { + return -1; + } + pyexpat_encoding_info *info = (pyexpat_encoding_info *)data; + int i = (unsigned char)s[0]; + assert(info->map[i] < -1); + PyObject *u = PyUnicode_Decode(s, -info->map[i], info->name, NULL); + if (u == NULL) { + return -1; + } + if (PyUnicode_GET_LENGTH(u) != 1) { + Py_DECREF(u); + return -1; + } + Py_UCS4 ch = PyUnicode_ReadChar(u, 0); + Py_DECREF(u); + return (int)ch; +} + + static const unsigned char template_buffer[256] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, @@ -1183,6 +1525,43 @@ PyUnknownEncodingHandler(void *encodingHandlerData, if (PyErr_Occurred()) return XML_STATUS_ERROR; + PyObject *codec = _PyCodec_LookupTextEncoding(name, NULL); + if (codec == NULL) { + return XML_STATUS_ERROR; + } + if (!PyTuple_CheckExact(codec)) { + PyObject *attr; + if (PyObject_GetOptionalAttrString(codec, "_expat_decoding_table", &attr) < 0) { + Py_DECREF(codec); + return XML_STATUS_ERROR; + } + if (attr != NULL) { + if (attr == Py_False) { + Py_DECREF(attr); + Py_DECREF(codec); + PyErr_Format(PyExc_ValueError, + "encoding '%s' is not supported", + name); + return XML_STATUS_ERROR; + } + pyexpat_encoding_info *data = pyexpat_encoding_create(name, attr); + Py_DECREF(attr); + if (data == NULL) { + Py_DECREF(codec); + return XML_STATUS_ERROR; + } + for (i = 0; i < 256; i++) { + info->map[i] = data->map[i]; + } + info->data = data; + info->convert = pyexpat_encoding_convert; + info->release = PyMem_Free; + Py_DECREF(codec); + return XML_STATUS_OK; + } + } + Py_DECREF(codec); + u = PyUnicode_Decode((const char*) template_buffer, 256, name, "replace"); if (u == NULL) { Py_XDECREF(u); @@ -1191,8 +1570,9 @@ PyUnknownEncodingHandler(void *encodingHandlerData, if (PyUnicode_GET_LENGTH(u) != 256) { Py_DECREF(u); - PyErr_SetString(PyExc_ValueError, - "multi-byte encodings are not supported"); + PyErr_Format(PyExc_ValueError, + "multi-byte encoding '%s' is not supported", + name); return XML_STATUS_ERROR; } @@ -1244,13 +1624,17 @@ newxmlparseobject(pyexpat_state *state, const char *encoding, /* namespace_separator is either NULL or contains one char + \0 */ self->itself = XML_ParserCreate_MM(encoding, &ExpatMemoryHandler, namespace_separator); + self->parent = NULL; if (self->itself == NULL) { PyErr_SetString(PyExc_RuntimeError, "XML_ParserCreate failed"); Py_DECREF(self); return NULL; } -#if XML_COMBINED_VERSION >= 20100 +#if XML_COMBINED_VERSION >= 20800 + /* This feature was added upstream in libexpat 2.8.0. */ + XML_SetHashSalt16Bytes(self->itself, _Py_HashSecret.expat.hashsalt16); +#elif XML_COMBINED_VERSION >= 20100 /* This feature was added upstream in libexpat 2.1.0. */ XML_SetHashSalt(self->itself, (unsigned long)_Py_HashSecret.expat.hashsalt); @@ -1280,6 +1664,7 @@ xmlparse_traverse(PyObject *op, visitproc visit, void *arg) for (size_t i = 0; handler_info[i].name != NULL; i++) { Py_VISIT(self->handlers[i]); } + Py_VISIT(self->parent); Py_VISIT(Py_TYPE(op)); return 0; } @@ -1290,6 +1675,10 @@ xmlparse_clear(PyObject *op) xmlparseobject *self = xmlparseobject_CAST(op); clear_handlers(self, 0); Py_CLEAR(self->intern); + // NOTE: We cannot call Py_CLEAR(self->parent) prior to calling + // XML_ParserFree(self->itself), or a subparser could lose its parent + // XML_Parser while still making use of it internally. + // https://github.com/python/cpython/issues/139400 return 0; } @@ -1303,6 +1692,7 @@ xmlparse_dealloc(PyObject *op) XML_ParserFree(self->itself); } self->itself = NULL; + Py_CLEAR(self->parent); if (self->handlers != NULL) { PyMem_Free(self->handlers); @@ -2138,11 +2528,30 @@ pyexpat_exec(PyObject *mod) #else capi->SetHashSalt = NULL; #endif +#if XML_COMBINED_VERSION >= 20800 + capi->SetHashSalt16Bytes = XML_SetHashSalt16Bytes; +#else + capi->SetHashSalt16Bytes = NULL; +#endif #if XML_COMBINED_VERSION >= 20600 capi->SetReparseDeferralEnabled = XML_SetReparseDeferralEnabled; #else capi->SetReparseDeferralEnabled = NULL; #endif +#if XML_COMBINED_VERSION >= 20702 + capi->SetAllocTrackerActivationThreshold = XML_SetAllocTrackerActivationThreshold; + capi->SetAllocTrackerMaximumAmplification = XML_SetAllocTrackerMaximumAmplification; +#else + capi->SetAllocTrackerActivationThreshold = NULL; + capi->SetAllocTrackerMaximumAmplification = NULL; +#endif +#if XML_COMBINED_VERSION >= 20400 + capi->SetBillionLaughsAttackProtectionActivationThreshold = XML_SetBillionLaughsAttackProtectionActivationThreshold; + capi->SetBillionLaughsAttackProtectionMaximumAmplification = XML_SetBillionLaughsAttackProtectionMaximumAmplification; +#else + capi->SetAllocTrackerActivationThreshold = NULL; + capi->SetAllocTrackerMaximumAmplification = NULL; +#endif /* export using capsule */ PyObject *capi_object = PyCapsule_New(capi, PyExpat_CAPSULE_NAME, @@ -2186,6 +2595,7 @@ pyexpat_free(void *module) } static PyModuleDef_Slot pyexpat_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, pyexpat_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -2213,6 +2623,9 @@ PyInit_pyexpat(void) static void clear_handlers(xmlparseobject *self, int initial) { + if (self->handlers == NULL) { + return; + } for (size_t i = 0; handler_info[i].name != NULL; i++) { if (initial) { self->handlers[i] = NULL; diff --git a/Modules/readline.c b/Modules/readline.c index 0dd99dc66c08e9..c580d2022fccf3 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -255,6 +255,7 @@ readline_read_init_file_impl(PyObject *module, PyObject *filename_obj) if (!PyUnicode_FSConverter(filename_obj, &filename_bytes)) return NULL; if (PySys_Audit("open", "OCi", filename_obj, 'r', 0) < 0) { + Py_DECREF(filename_bytes); return NULL; } errno = rl_read_init_file(PyBytes_AS_STRING(filename_bytes)); @@ -298,6 +299,7 @@ readline_read_history_file_impl(PyObject *module, PyObject *filename_obj) if (!PyUnicode_FSConverter(filename_obj, &filename_bytes)) return NULL; if (PySys_Audit("open", "OCi", filename_obj, 'r', 0) < 0) { + Py_DECREF(filename_bytes); return NULL; } errno = read_history(PyBytes_AS_STRING(filename_bytes)); @@ -343,6 +345,7 @@ readline_write_history_file_impl(PyObject *module, PyObject *filename_obj) return NULL; filename = PyBytes_AS_STRING(filename_bytes); if (PySys_Audit("open", "OCi", filename_obj, 'w', 0) < 0) { + Py_DECREF(filename_bytes); return NULL; } } else { @@ -388,7 +391,7 @@ readline_append_history_file_impl(PyObject *module, int nelements, { if (nelements < 0) { - PyErr_SetString(PyExc_ValueError, "nelements must be positive"); + PyErr_SetString(PyExc_ValueError, "nelements must be non-negative"); return NULL; } @@ -400,6 +403,7 @@ readline_append_history_file_impl(PyObject *module, int nelements, return NULL; filename = PyBytes_AS_STRING(filename_bytes); if (PySys_Audit("open", "OCi", filename_obj, 'a', 0) < 0) { + Py_DECREF(filename_bytes); return NULL; } } else { @@ -428,6 +432,7 @@ readline_append_history_file_impl(PyObject *module, int nelements, /* Set history length */ /*[clinic input] +@permit_long_summary readline.set_history_length length: int @@ -440,7 +445,7 @@ A negative length is used to inhibit history truncation. static PyObject * readline_set_history_length_impl(PyObject *module, int length) -/*[clinic end generated code: output=e161a53e45987dc7 input=b8901bf16488b760]*/ +/*[clinic end generated code: output=e161a53e45987dc7 input=8d02c81b38ef81ec]*/ { FT_ATOMIC_STORE_INT_RELAXED(_history_length, length); Py_RETURN_NONE; @@ -449,6 +454,7 @@ readline_set_history_length_impl(PyObject *module, int length) /* Get history length */ /*[clinic input] +@permit_long_summary readline.get_history_length Return the maximum number of lines that will be written to the history file. @@ -456,7 +462,7 @@ Return the maximum number of lines that will be written to the history file. static PyObject * readline_get_history_length_impl(PyObject *module) -/*[clinic end generated code: output=83a2eeae35b6d2b9 input=5dce2eeba4327817]*/ +/*[clinic end generated code: output=83a2eeae35b6d2b9 input=a65823e732ebfa9d]*/ { int history_length = FT_ATOMIC_LOAD_INT_RELAXED(_history_length); return PyLong_FromLong(history_length); @@ -568,6 +574,26 @@ readline_set_pre_input_hook_impl(PyObject *module, PyObject *function) return set_hook("pre_input_hook", &state->pre_input_hook, function); } + +/* Get pre-input hook */ + +/*[clinic input] +readline.get_pre_input_hook + +Get the current pre-input hook function. +[clinic start generated code]*/ + +static PyObject * +readline_get_pre_input_hook_impl(PyObject *module) +/*[clinic end generated code: output=ad56b77a8e8981ca input=fb1e1b1fbd94e4e5]*/ +{ + readlinestate *state = get_readline_state(module); + if (state->pre_input_hook == NULL) { + Py_RETURN_NONE; + } + return Py_NewRef(state->pre_input_hook); +} + #endif @@ -1020,6 +1046,7 @@ readline_insert_text_impl(PyObject *module, PyObject *string) /* Redisplay the line buffer */ /*[clinic input] +@permit_long_summary @critical_section readline.redisplay @@ -1028,7 +1055,7 @@ Change what's displayed on the screen to reflect contents of the line buffer. static PyObject * readline_redisplay_impl(PyObject *module) -/*[clinic end generated code: output=a8b9725827c3c34b input=5895fd014615ff58]*/ +/*[clinic end generated code: output=a8b9725827c3c34b input=fb6ce76959c6f0ec]*/ { rl_redisplay(); Py_RETURN_NONE; @@ -1069,6 +1096,7 @@ static struct PyMethodDef readline_methods[] = READLINE_SET_STARTUP_HOOK_METHODDEF #ifdef HAVE_RL_PRE_INPUT_HOOK READLINE_SET_PRE_INPUT_HOOK_METHODDEF + READLINE_GET_PRE_INPUT_HOOK_METHODDEF #endif #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER READLINE_CLEAR_HISTORY_METHODDEF @@ -1378,6 +1406,10 @@ setup_readline(readlinestate *mod_state) completer_word_break_characters = strdup(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?"); /* All nonalphanums except '.' */ + + if (!completer_word_break_characters) { + goto error; + } #ifdef WITH_EDITLINE // libedit uses rl_basic_word_break_characters instead of // rl_completer_word_break_characters as complete delimiter @@ -1421,6 +1453,10 @@ setup_readline(readlinestate *mod_state) RESTORE_LOCALE(saved_locale) return 0; + +error: + RESTORE_LOCALE(saved_locale) + return -1; } /* Wrapper around GNU readline that handles signals differently. */ @@ -1600,6 +1636,11 @@ static struct PyModuleDef readlinemodule = { PyMODINIT_FUNC PyInit_readline(void) { + PyABIInfo_VAR(abi_info); + if (PyABIInfo_Check(&abi_info, "readline") < 0) { + return NULL; + } + const char *backend = "readline"; PyObject *m; readlinestate *mod_state; diff --git a/Modules/resource.c b/Modules/resource.c index 3fe18e7c98e3d8..9bf8d2782766cc 100644 --- a/Modules/resource.c +++ b/Modules/resource.c @@ -1,7 +1,5 @@ -// Need limited C API version 3.13 for PySys_Audit() -#include "pyconfig.h" // Py_GIL_DISABLED -#ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030d0000 +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 #endif #include "Python.h" @@ -150,6 +148,42 @@ resource_getrusage_impl(PyObject *module, int who) } #endif +static int +py2rlim(PyObject *obj, rlim_t *out) +{ + obj = PyNumber_Index(obj); + if (obj == NULL) { + return -1; + } + int neg = PyLong_IsNegative(obj); + assert(neg >= 0); + Py_ssize_t bytes = PyLong_AsNativeBytes(obj, out, sizeof(*out), + Py_ASNATIVEBYTES_NATIVE_ENDIAN | + Py_ASNATIVEBYTES_UNSIGNED_BUFFER); + Py_DECREF(obj); + if (bytes < 0) { + return -1; + } + else if (neg && *out == RLIM_INFINITY && bytes <= (Py_ssize_t)sizeof(*out)) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "Use RLIM_INFINITY instead of negative limit value.", 1)) + { + return -1; + } + } + else if (neg) { + PyErr_SetString(PyExc_ValueError, + "Cannot convert negative int"); + return -1; + } + else if (bytes > (Py_ssize_t)sizeof(*out)) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C rlim_t"); + return -1; + } + return 0; +} + static int py2rlimit(PyObject *limits, struct rlimit *rl_out) { @@ -166,26 +200,13 @@ py2rlimit(PyObject *limits, struct rlimit *rl_out) } curobj = PyTuple_GetItem(limits, 0); // borrowed maxobj = PyTuple_GetItem(limits, 1); // borrowed -#if !defined(HAVE_LARGEFILE_SUPPORT) - rl_out->rlim_cur = PyLong_AsLong(curobj); - if (rl_out->rlim_cur == (rlim_t)-1 && PyErr_Occurred()) - goto error; - rl_out->rlim_max = PyLong_AsLong(maxobj); - if (rl_out->rlim_max == (rlim_t)-1 && PyErr_Occurred()) - goto error; -#else - /* The limits are probably bigger than a long */ - rl_out->rlim_cur = PyLong_AsLongLong(curobj); - if (rl_out->rlim_cur == (rlim_t)-1 && PyErr_Occurred()) - goto error; - rl_out->rlim_max = PyLong_AsLongLong(maxobj); - if (rl_out->rlim_max == (rlim_t)-1 && PyErr_Occurred()) + if (py2rlim(curobj, &rl_out->rlim_cur) < 0 || + py2rlim(maxobj, &rl_out->rlim_max) < 0) + { goto error; -#endif + } Py_DECREF(limits); - rl_out->rlim_cur = rl_out->rlim_cur & RLIM_INFINITY; - rl_out->rlim_max = rl_out->rlim_max & RLIM_INFINITY; return 0; error: @@ -193,15 +214,21 @@ py2rlimit(PyObject *limits, struct rlimit *rl_out) return -1; } +static PyObject* +rlim2py(rlim_t value) +{ + return PyLong_FromUnsignedNativeBytes(&value, sizeof(value), -1); +} + static PyObject* rlimit2py(struct rlimit rl) { - if (sizeof(rl.rlim_cur) > sizeof(long)) { - return Py_BuildValue("LL", - (long long) rl.rlim_cur, - (long long) rl.rlim_max); + PyObject *cur = rlim2py(rl.rlim_cur); + if (cur == NULL) { + return NULL; } - return Py_BuildValue("ll", (long) rl.rlim_cur, (long) rl.rlim_max); + PyObject *max = rlim2py(rl.rlim_max); + return Py_BuildValue("NN", cur, max); } /*[clinic input] @@ -495,22 +522,45 @@ resource_exec(PyObject *module) ADD_INT(module, RLIMIT_KQUEUES); #endif - PyObject *v; - if (sizeof(RLIM_INFINITY) > sizeof(long)) { - v = PyLong_FromLongLong((long long) RLIM_INFINITY); - } else - { - v = PyLong_FromLong((long) RLIM_INFINITY); +#ifdef RLIMIT_NTHR + ADD_INT(module, RLIMIT_NTHR); +#endif + +#ifdef RLIMIT_THREADS + ADD_INT(module, RLIMIT_THREADS); +#endif + +#ifdef RLIMIT_UMTXP + ADD_INT(module, RLIMIT_UMTXP); +#endif + +#ifdef RLIMIT_PIPEBUF + ADD_INT(module, RLIMIT_PIPEBUF); +#endif + + if (PyModule_Add(module, "RLIM_INFINITY", rlim2py(RLIM_INFINITY)) < 0) { + return -1; } - if (PyModule_Add(module, "RLIM_INFINITY", v) < 0) { + +#ifdef RLIM_SAVED_CUR + if (PyModule_Add(module, "RLIM_SAVED_CUR", rlim2py(RLIM_SAVED_CUR)) < 0) { return -1; } +#endif + +#ifdef RLIM_SAVED_MAX + if (PyModule_Add(module, "RLIM_SAVED_MAX", rlim2py(RLIM_SAVED_MAX)) < 0) { + return -1; + } +#endif + return 0; #undef ADD_INT } static struct PyModuleDef_Slot resource_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, resource_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index d234d504cb5167..2c56dbc6a541f7 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -15,6 +15,7 @@ #include "Python.h" #include "pycore_fileutils.h" // _Py_set_inheritable() #include "pycore_time.h" // _PyTime_FromSecondsObject() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #include <stdbool.h> #include <stddef.h> // offsetof() @@ -251,7 +252,8 @@ select.select Wait until one or more file descriptors are ready for some kind of I/O. -The first three arguments are iterables of file descriptors to be waited for: +The first three arguments are iterables of file descriptors to be waited +for: rlist -- wait until ready for reading wlist -- wait until ready for writing xlist -- wait for an "exceptional condition" @@ -261,12 +263,12 @@ A file descriptor is either a socket or file object, or a small integer gotten from a fileno() method call on one of those. The optional 4th argument specifies a timeout in seconds; it may be -a floating-point number to specify fractions of seconds. If it is absent +a non-integer to specify fractions of seconds. If it is absent or None, the call will never time out. -The return value is a tuple of three lists corresponding to the first three -arguments; each contains the subset of the corresponding file descriptors -that are ready. +The return value is a tuple of three lists corresponding to the first +three arguments; each contains the subset of the corresponding file +descriptors that are ready. *** IMPORTANT NOTICE *** On Windows, only sockets are supported; on Unix, all file @@ -276,7 +278,7 @@ descriptors can be used. static PyObject * select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist, PyObject *xlist, PyObject *timeout_obj) -/*[clinic end generated code: output=2b3cfa824f7ae4cf input=1199d5e101abca4a]*/ +/*[clinic end generated code: output=2b3cfa824f7ae4cf input=cc93e9bb9ffacbaf]*/ { #ifdef SELECT_USES_HEAP pylist *rfd2obj, *wfd2obj, *efd2obj; @@ -304,8 +306,9 @@ select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist, if (_PyTime_FromSecondsObject(&timeout, timeout_obj, _PyTime_ROUND_TIMEOUT) < 0) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_SetString(PyExc_TypeError, - "timeout must be a float or None"); + PyErr_Format(PyExc_TypeError, + "timeout must be a real number or None, not %T", + timeout_obj); } return NULL; } @@ -425,14 +428,14 @@ select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist, return ret; } -#if defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL) +#if (defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL) /* * poll() support */ typedef struct { PyObject_HEAD - PyObject *dict; + PyObject *dict; // cannot create cycles as it only contains exact ints int ufd_uptodate; int ufd_len; struct pollfd *ufds; @@ -613,38 +616,52 @@ select.poll.poll Polls the set of registered file descriptors. -Returns a list containing any descriptors that have events or errors to -report, as a list of (fd, event) 2-tuples. +Returns a list containing any descriptors that have events or errors +to report, as a list of (fd, event) 2-tuples. [clinic start generated code]*/ static PyObject * select_poll_poll_impl(pollObject *self, PyObject *timeout_obj) -/*[clinic end generated code: output=876e837d193ed7e4 input=54310631457efdec]*/ +/*[clinic end generated code: output=876e837d193ed7e4 input=e0a9c0aa283de8c8]*/ { PyObject *result_list = NULL; int poll_result, i, j; PyObject *value = NULL, *num = NULL; - PyTime_t timeout = -1, ms = -1, deadline = 0; + PyTime_t timeout = -1, deadline = 0; int async_err = 0; if (timeout_obj != Py_None) { if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj, _PyTime_ROUND_TIMEOUT) < 0) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_SetString(PyExc_TypeError, - "timeout must be an integer or None"); + PyErr_Format(PyExc_TypeError, + "timeout must be a real number or None, not %T", + timeout_obj); } return NULL; } + } - ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT); - if (ms < INT_MIN || ms > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, "timeout is too large"); +#ifdef HAVE_PPOLL + struct timespec ts, *ts_p = NULL; + + if (timeout_obj != Py_None) { + if (_PyTime_AsTimespec(timeout, &ts) < 0) { return NULL; } if (timeout >= 0) { - deadline = _PyDeadline_Init(timeout); + ts_p = &ts; + } + } +#else + PyTime_t ms = -1; + + if (timeout_obj != Py_None) { + ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_TIMEOUT); + if (ms < INT_MIN || ms > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "timeout is too large"); + return NULL; } } @@ -658,6 +675,11 @@ select_poll_poll_impl(pollObject *self, PyObject *timeout_obj) ms = -1; #endif } +#endif + + if (timeout >= 0) { + deadline = _PyDeadline_Init(timeout); + } /* Avoid concurrent poll() invocation, issue 8865 */ if (self->poll_running) { @@ -678,7 +700,11 @@ select_poll_poll_impl(pollObject *self, PyObject *timeout_obj) do { Py_BEGIN_ALLOW_THREADS errno = 0; +#ifdef HAVE_PPOLL + poll_result = ppoll(self->ufds, self->ufd_len, ts_p, NULL); +#else poll_result = poll(self->ufds, self->ufd_len, (int)ms); +#endif Py_END_ALLOW_THREADS if (errno != EINTR) @@ -696,8 +722,16 @@ select_poll_poll_impl(pollObject *self, PyObject *timeout_obj) poll_result = 0; break; } +#ifdef HAVE_PPOLL + if (_PyTime_AsTimespec(timeout, &ts) < 0) { + poll_result = -1; + break; + } + assert(ts_p == &ts); +#else ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING); - /* retry poll() with the recomputed timeout */ +#endif + /* retry poll()/ppoll() with the recomputed timeout */ } } while (1); @@ -941,19 +975,19 @@ select_devpoll_unregister_impl(devpollObject *self, int fd) @critical_section select.devpoll.poll timeout as timeout_obj: object = None - The maximum time to wait in milliseconds, or else None (or a negative - value) to wait indefinitely. + The maximum time to wait in milliseconds, or else None (or + a negative value) to wait indefinitely. / Polls the set of registered file descriptors. -Returns a list containing any descriptors that have events or errors to -report, as a list of (fd, event) 2-tuples. +Returns a list containing any descriptors that have events or errors +to report, as a list of (fd, event) 2-tuples. [clinic start generated code]*/ static PyObject * select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj) -/*[clinic end generated code: output=2654e5457cca0b3c input=fe7a3f6dcbc118c5]*/ +/*[clinic end generated code: output=2654e5457cca0b3c input=9e1672658d728539]*/ { struct dvpoll dvp; PyObject *result_list = NULL; @@ -973,8 +1007,9 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj) if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj, _PyTime_ROUND_TIMEOUT) < 0) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_SetString(PyExc_TypeError, - "timeout must be an integer or None"); + PyErr_Format(PyExc_TypeError, + "timeout must be a real number or None, not %T", + timeout_obj); } return NULL; } @@ -1041,9 +1076,7 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj) Py_XDECREF(num2); goto error; } - value = PyTuple_Pack(2, num1, num2); - Py_DECREF(num1); - Py_DECREF(num2); + value = _PyTuple_FromPairSteal(num1, num2); if (value == NULL) goto error; PyList_SET_ITEM(result_list, i, value); @@ -1084,8 +1117,9 @@ static PyObject * select_devpoll_close_impl(devpollObject *self) /*[clinic end generated code: output=26b355bd6429f21b input=408fde21a377ccfb]*/ { - errno = devpoll_internal_close(self); - if (errno < 0) { + int err = devpoll_internal_close(self); + if (err != 0) { + errno = err; PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -1203,13 +1237,13 @@ select.poll Returns a polling object. -This object supports registering and unregistering file descriptors, and then -polling them for I/O events. +This object supports registering and unregistering file descriptors, and +then polling them for I/O events. [clinic start generated code]*/ static PyObject * select_poll_impl(PyObject *module) -/*[clinic end generated code: output=16a665a4e1d228c5 input=3f877909d5696bbf]*/ +/*[clinic end generated code: output=16a665a4e1d228c5 input=0aefd4527e99e0aa]*/ { return (PyObject *)newPollObject(module); } @@ -1221,13 +1255,13 @@ select.devpoll Returns a polling object. -This object supports registering and unregistering file descriptors, and then -polling them for I/O events. +This object supports registering and unregistering file descriptors, and +then polling them for I/O events. [clinic start generated code]*/ static PyObject * select_devpoll_impl(PyObject *module) -/*[clinic end generated code: output=ea9213cc87fd9581 input=53a1af94564f00a3]*/ +/*[clinic end generated code: output=ea9213cc87fd9581 input=4c2ac27d10248526]*/ { return (PyObject *)newDevPollObject(module); } @@ -1410,8 +1444,9 @@ static PyObject * select_epoll_close_impl(pyEpoll_Object *self) /*[clinic end generated code: output=ee2144c446a1a435 input=f626a769192e1dbe]*/ { - errno = pyepoll_internal_close(self); - if (errno < 0) { + int err = pyepoll_internal_close(self); + if (err != 0) { + errno = err; PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -1503,6 +1538,7 @@ pyepoll_internal_ctl(int epfd, int op, int fd, unsigned int events) } /*[clinic input] +@permit_long_summary select.epoll.register fd: fildes @@ -1518,7 +1554,7 @@ The epoll interface supports all file descriptors that support poll. static PyObject * select_epoll_register_impl(pyEpoll_Object *self, int fd, unsigned int eventmask) -/*[clinic end generated code: output=318e5e6386520599 input=a5071b71edfe3578]*/ +/*[clinic end generated code: output=318e5e6386520599 input=9f0c9ebb25a4fc8f]*/ { return pyepoll_internal_ctl(self->epfd, EPOLL_CTL_ADD, fd, eventmask); } @@ -1562,21 +1598,21 @@ select_epoll_unregister_impl(pyEpoll_Object *self, int fd) select.epoll.poll timeout as timeout_obj: object = None - the maximum time to wait in seconds (as float); + the maximum time to wait in seconds (with fractions); a timeout of None or -1 makes poll wait indefinitely maxevents: int = -1 the maximum number of events returned; -1 means no limit Wait for events on the epoll file descriptor. -Returns a list containing any descriptors that have events to report, -as a list of (fd, events) 2-tuples. +Returns a list containing any descriptors that have events to +report, as a list of (fd, events) 2-tuples. [clinic start generated code]*/ static PyObject * select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj, int maxevents) -/*[clinic end generated code: output=e02d121a20246c6c input=33d34a5ea430fd5b]*/ +/*[clinic end generated code: output=e02d121a20246c6c input=911ddc16978a9159]*/ { int nfds, i; PyObject *elist = NULL, *etuple = NULL; @@ -1592,8 +1628,9 @@ select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj, if (_PyTime_FromSecondsObject(&timeout, timeout_obj, _PyTime_ROUND_TIMEOUT) < 0) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_SetString(PyExc_TypeError, - "timeout must be an integer or None"); + PyErr_Format(PyExc_TypeError, + "timeout must be a real number or None, not %T", + timeout_obj); } return NULL; } @@ -2226,8 +2263,9 @@ static PyObject * select_kqueue_close_impl(kqueue_queue_Object *self) /*[clinic end generated code: output=d1c7df0b407a4bc1 input=6d763c858b17b690]*/ { - errno = kqueue_queue_internal_close(self); - if (errno < 0) { + int err = kqueue_queue_internal_close(self); + if (err != 0) { + errno = err; PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -2288,7 +2326,7 @@ select.kqueue.control The maximum number of events that the kernel will return. timeout as otimeout: object = None The maximum time to wait in seconds, or else None to wait forever. - This accepts floats for smaller timeouts, too. + This accepts non-integers for smaller timeouts, too. / Calls the kernel kevent function. @@ -2297,7 +2335,7 @@ Calls the kernel kevent function. static PyObject * select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist, int maxevents, PyObject *otimeout) -/*[clinic end generated code: output=81324ff5130db7ae input=59c4e30811209c47]*/ +/*[clinic end generated code: output=81324ff5130db7ae input=be969d2bc6f84205]*/ { int gotevents = 0; int nchanges = 0; @@ -2328,9 +2366,8 @@ select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist, if (_PyTime_FromSecondsObject(&timeout, otimeout, _PyTime_ROUND_TIMEOUT) < 0) { PyErr_Format(PyExc_TypeError, - "timeout argument must be a number " - "or None, got %.200s", - _PyType_Name(Py_TYPE(otimeout))); + "timeout must be a real number or None, not %T", + otimeout); return NULL; } @@ -2460,7 +2497,7 @@ static PyGetSetDef kqueue_queue_getsetlist[] = { #include "clinic/selectmodule.c.h" -#if defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL) +#if (defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL) static PyMethodDef poll_methods[] = { SELECT_POLL_REGISTER_METHODDEF @@ -2480,7 +2517,11 @@ static PyType_Slot poll_Type_slots[] = { static PyType_Spec poll_Type_spec = { .name = "select.poll", .basicsize = sizeof(pollObject), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + ), .slots = poll_Type_slots, }; @@ -2526,11 +2567,10 @@ static PyType_Slot pyEpoll_Type_slots[] = { }; static PyType_Spec pyEpoll_Type_spec = { - "select.epoll", - sizeof(pyEpoll_Object), - 0, - Py_TPFLAGS_DEFAULT, - pyEpoll_Type_slots + .name = "select.epoll", + .basicsize = sizeof(pyEpoll_Object), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE, + .slots = pyEpoll_Type_slots }; #endif /* HAVE_EPOLL */ @@ -2652,7 +2692,7 @@ _select_exec(PyObject *m) ADD_INT(PIPE_BUF); #endif -#if defined(HAVE_POLL) && !defined(HAVE_BROKEN_POLL) +#if (defined(HAVE_POLL) || defined(HAVE_PPOLL)) && !defined(HAVE_BROKEN_POLL) #ifdef __APPLE__ if (select_have_broken_poll()) { if (PyObject_DelAttrString(m, "poll") == -1) { @@ -2870,6 +2910,7 @@ _select_exec(PyObject *m) } static PyModuleDef_Slot _select_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _select_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/sha1module.c b/Modules/sha1module.c index faa9dcccc5755b..5681780b569b6c 100644 --- a/Modules/sha1module.c +++ b/Modules/sha1module.c @@ -21,6 +21,7 @@ #include "Python.h" #include "hashlib.h" +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_strhex.h" // _Py_strhex() #include "pycore_typeobject.h" // _PyType_GetModuleState() @@ -81,13 +82,6 @@ newSHA1object(SHA1State *st) /* Internal methods for a hash object */ -static int -SHA1_traverse(PyObject *ptr, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(ptr)); - return 0; -} - static void SHA1_dealloc(PyObject *op) { @@ -247,7 +241,7 @@ static PyType_Slot sha1_type_slots[] = { {Py_tp_dealloc, SHA1_dealloc}, {Py_tp_methods, SHA1_methods}, {Py_tp_getset, SHA1_getseters}, - {Py_tp_traverse, SHA1_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {0,0} }; @@ -375,6 +369,7 @@ _sha1_exec(PyObject *module) /* Initialize this module. */ static PyModuleDef_Slot _sha1_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _sha1_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/sha2module.c b/Modules/sha2module.c index 36300ba899fd44..7613ee54954dd6 100644 --- a/Modules/sha2module.c +++ b/Modules/sha2module.c @@ -22,8 +22,9 @@ #include "Python.h" #include "pycore_moduleobject.h" // _PyModule_GetState() -#include "pycore_typeobject.h" // _PyType_GetModuleState() +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_strhex.h" // _Py_strhex() +#include "pycore_typeobject.h" // _PyType_GetModuleState() #include "hashlib.h" @@ -164,14 +165,6 @@ newSHA512object(sha2_state *state) } /* Internal methods for our hash objects. */ - -static int -SHA2_traverse(PyObject *ptr, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(ptr)); - return 0; -} - static void SHA256_dealloc(PyObject *op) { @@ -519,7 +512,7 @@ static PyType_Slot sha256_types_slots[] = { {Py_tp_dealloc, SHA256_dealloc}, {Py_tp_methods, SHA256_methods}, {Py_tp_getset, SHA256_getseters}, - {Py_tp_traverse, SHA2_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {0,0} }; @@ -527,7 +520,7 @@ static PyType_Slot sha512_type_slots[] = { {Py_tp_dealloc, SHA512_dealloc}, {Py_tp_methods, SHA512_methods}, {Py_tp_getset, SHA512_getseters}, - {Py_tp_traverse, SHA2_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {0,0} }; @@ -890,6 +883,7 @@ static int sha2_exec(PyObject *module) } static PyModuleDef_Slot _sha2_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, sha2_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/sha3module.c b/Modules/sha3module.c index 5764556bb680f3..3ddd0323575b70 100644 --- a/Modules/sha3module.c +++ b/Modules/sha3module.c @@ -21,6 +21,7 @@ #endif #include "Python.h" +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_strhex.h" // _Py_strhex() #include "pycore_typeobject.h" // _PyType_GetModuleState() #include "hashlib.h" @@ -226,13 +227,6 @@ SHA3_dealloc(PyObject *self) Py_DECREF(tp); } -static int -SHA3_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - /* External methods for a hash object */ @@ -424,7 +418,7 @@ static PyGetSetDef SHA3_getseters[] = { static PyType_Slot type_slots_obj[] = { \ {Py_tp_clear, SHA3_clear}, \ {Py_tp_dealloc, SHA3_dealloc}, \ - {Py_tp_traverse, SHA3_traverse}, \ + {Py_tp_traverse, _PyObject_VisitType}, \ {Py_tp_doc, (char*)type_doc}, \ {Py_tp_methods, type_methods}, \ {Py_tp_getset, type_getseters}, \ @@ -478,10 +472,7 @@ SHA3_TYPE_SPEC(sha3_512_spec, "sha3_512", sha3_512_slots); static int sha3_shake_check_digest_length(Py_ssize_t length) { - if (length < 0) { - PyErr_SetString(PyExc_ValueError, "negative digest length"); - return -1; - } + assert(length >= 0); if ((size_t)length >= (1 << 29)) { /* * Raise OverflowError to match the semantics of OpenSSL SHAKE @@ -498,14 +489,14 @@ sha3_shake_check_digest_length(Py_ssize_t length) /*[clinic input] _sha3.shake_128.digest - length: Py_ssize_t + length: Py_ssize_t(allow_negative=False) Return the digest value as a bytes object. [clinic start generated code]*/ static PyObject * _sha3_shake_128_digest_impl(SHA3object *self, Py_ssize_t length) -/*[clinic end generated code: output=6c53fb71a6cff0a0 input=be03ade4b31dd54c]*/ +/*[clinic end generated code: output=6c53fb71a6cff0a0 input=1160c9f86ae0f867]*/ { if (sha3_shake_check_digest_length(length) < 0) { return NULL; @@ -519,28 +510,33 @@ _sha3_shake_128_digest_impl(SHA3object *self, Py_ssize_t length) if (length == 0) { return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } - CHECK_HACL_UINT32_T_LENGTH(length); - PyObject *digest = PyBytes_FromStringAndSize(NULL, length); - uint8_t *buffer = (uint8_t *)PyBytes_AS_STRING(digest); + + PyBytesWriter *writer = PyBytesWriter_Create(length); + if (writer == NULL) { + return NULL; + } + uint8_t *buffer = (uint8_t *)PyBytesWriter_GetData(writer); + HASHLIB_ACQUIRE_LOCK(self); (void)Hacl_Hash_SHA3_squeeze(self->hash_state, buffer, (uint32_t)length); HASHLIB_RELEASE_LOCK(self); - return digest; + + return PyBytesWriter_Finish(writer); } /*[clinic input] _sha3.shake_128.hexdigest - length: Py_ssize_t + length: Py_ssize_t(allow_negative=False) Return the digest value as a string of hexadecimal digits. [clinic start generated code]*/ static PyObject * _sha3_shake_128_hexdigest_impl(SHA3object *self, Py_ssize_t length) -/*[clinic end generated code: output=a27412d404f64512 input=0d84d05d7a8ccd37]*/ +/*[clinic end generated code: output=a27412d404f64512 input=ff06c9362949d2c8]*/ { if (sha3_shake_check_digest_length(length) < 0) { return NULL; @@ -550,8 +546,8 @@ _sha3_shake_128_hexdigest_impl(SHA3object *self, Py_ssize_t length) if (length == 0) { return Py_GetConstant(Py_CONSTANT_EMPTY_STR); } - CHECK_HACL_UINT32_T_LENGTH(length); + uint8_t *buffer = PyMem_Malloc(length); if (buffer == NULL) { return PyErr_NoMemory(); @@ -560,6 +556,7 @@ _sha3_shake_128_hexdigest_impl(SHA3object *self, Py_ssize_t length) HASHLIB_ACQUIRE_LOCK(self); (void)Hacl_Hash_SHA3_squeeze(self->hash_state, buffer, (uint32_t)length); HASHLIB_RELEASE_LOCK(self); + PyObject *digest = _Py_strhex((const char *)buffer, length); PyMem_Free(buffer); return digest; @@ -683,6 +680,7 @@ _sha3_exec(PyObject *m) } static PyModuleDef_Slot _sha3_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _sha3_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 54bcd3270ef31a..8456239dee202d 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -14,6 +14,7 @@ #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_signal.h" // _Py_RestoreSignals() #include "pycore_time.h" // _PyTime_FromSecondsObject() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #ifndef MS_WINDOWS # include "posixmodule.h" // _PyLong_FromUid() @@ -193,27 +194,16 @@ double_from_timeval(struct timeval *tv) static PyObject * itimer_retval(struct itimerval *iv) { - PyObject *r, *v; - - r = PyTuple_New(2); - if (r == NULL) - return NULL; - - if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_value)))) { - Py_DECREF(r); + PyObject *value = PyFloat_FromDouble(double_from_timeval(&iv->it_value)); + if (value == NULL) { return NULL; } - - PyTuple_SET_ITEM(r, 0, v); - - if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_interval)))) { - Py_DECREF(r); + PyObject *interval = PyFloat_FromDouble(double_from_timeval(&iv->it_interval)); + if (interval == NULL) { + Py_DECREF(value); return NULL; } - - PyTuple_SET_ITEM(r, 1, v); - - return r; + return _PyTuple_FromPairSteal(value, interval); } #endif @@ -469,16 +459,17 @@ signal.signal Set the action for the given signal. The action can be SIG_DFL, SIG_IGN, or a callable Python object. -The previous action is returned. See getsignal() for possible return values. +The previous action is returned. See getsignal() for possible return +values. *** IMPORTANT NOTICE *** -A signal handler function is called with two arguments: -the first is the signal number, the second is the interrupted stack frame. +A signal handler function is called with two arguments: the first is +the signal number, the second is the interrupted stack frame. [clinic start generated code]*/ static PyObject * signal_signal_impl(PyObject *module, int signalnum, PyObject *handler) -/*[clinic end generated code: output=b44cfda43780f3a1 input=deee84af5fa0432c]*/ +/*[clinic end generated code: output=b44cfda43780f3a1 input=99ce4035ec56ffc1]*/ { _signal_module_state *modstate = get_signal_state(module); PyObject *old_handler; @@ -709,6 +700,7 @@ signal_siginterrupt_impl(PyObject *module, int signalnum, int flag) /*[clinic input] +@permit_long_summary signal.set_wakeup_fd fd as fdobj: object @@ -727,7 +719,7 @@ The fd must be non-blocking. static PyObject * signal_set_wakeup_fd_impl(PyObject *module, PyObject *fdobj, int warn_on_full_buffer) -/*[clinic end generated code: output=2280d72dd2a54c4f input=5b545946a28b8339]*/ +/*[clinic end generated code: output=2280d72dd2a54c4f input=1b914d48079e9274]*/ { struct _Py_stat_struct status; #ifdef MS_WINDOWS @@ -856,8 +848,8 @@ signal.setitimer Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL or ITIMER_PROF). -The timer will fire after value seconds and after that every interval seconds. -The itimer can be cleared by setting seconds to zero. +The timer will fire after value seconds and after that every interval +seconds. The itimer can be cleared by setting seconds to zero. Returns old values as a tuple: (delay, interval). [clinic start generated code]*/ @@ -865,7 +857,7 @@ Returns old values as a tuple: (delay, interval). static PyObject * signal_setitimer_impl(PyObject *module, int which, PyObject *seconds, PyObject *interval) -/*[clinic end generated code: output=65f9dcbddc35527b input=de43daf194e6f66f]*/ +/*[clinic end generated code: output=65f9dcbddc35527b input=bd9f0d2ed8614193]*/ { _signal_module_state *modstate = get_signal_state(module); @@ -1026,13 +1018,13 @@ signal.sigwait Wait for a signal. Suspend execution of the calling thread until the delivery of one of the -signals specified in the signal set sigset. The function accepts the signal -and returns the signal number. +signals specified in the signal set sigset. The function accepts the +signal and returns the signal number. [clinic start generated code]*/ static PyObject * signal_sigwait_impl(PyObject *module, sigset_t sigset) -/*[clinic end generated code: output=f43770699d682f96 input=a6fbd47b1086d119]*/ +/*[clinic end generated code: output=f43770699d682f96 input=91773742dd416a3e]*/ { int err, signum; @@ -1180,7 +1172,13 @@ signal_sigwaitinfo_impl(PyObject *module, sigset_t sigset) err = sigwaitinfo(&sigset, &si); Py_END_ALLOW_THREADS } while (err == -1 - && errno == EINTR && !(async_err = PyErr_CheckSignals())); + && (errno == EINTR +#if defined(__NetBSD__) + /* NetBSD's implementation violates POSIX by setting + * errno to ECANCELED instead of EINTR. */ + || errno == ECANCELED +#endif + ) && !(async_err = PyErr_CheckSignals())); if (err == -1) return (!async_err) ? PyErr_SetFromErrno(PyExc_OSError) : NULL; @@ -1201,13 +1199,13 @@ signal.sigtimedwait Like sigwaitinfo(), but with a timeout. -The timeout is specified in seconds, with floating-point numbers allowed. +The timeout is specified in seconds, rounded up to nanoseconds. [clinic start generated code]*/ static PyObject * signal_sigtimedwait_impl(PyObject *module, sigset_t sigset, PyObject *timeout_obj) -/*[clinic end generated code: output=59c8971e8ae18a64 input=955773219c1596cd]*/ +/*[clinic end generated code: output=59c8971e8ae18a64 input=f89af57d645e48e0]*/ { PyTime_t timeout; if (_PyTime_FromSecondsObject(&timeout, @@ -1623,7 +1621,7 @@ signal_module_exec(PyObject *m) modstate->ignore_handler = state->ignore_handler; // borrowed ref #ifdef PYHAVE_ITIMER_ERROR - modstate->itimer_error = PyErr_NewException("signal.itimer_error", + modstate->itimer_error = PyErr_NewException("signal.ItimerError", PyExc_OSError, NULL); if (modstate->itimer_error == NULL) { return -1; @@ -1700,6 +1698,7 @@ _signal_module_free(void *module) static PyModuleDef_Slot signal_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, signal_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -1772,20 +1771,28 @@ PyErr_CheckSignals(void) Python code to ensure signals are handled. Checking for the GC here allows long running native code to clean cycles created using the C-API even if it doesn't run the evaluation loop */ - if (_Py_eval_breaker_bit_is_set(tstate, _PY_GC_SCHEDULED_BIT)) { + uintptr_t breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + if (breaker & _PY_GC_SCHEDULED_BIT) { _Py_unset_eval_breaker_bit(tstate, _PY_GC_SCHEDULED_BIT); _Py_RunGC(tstate); } + if (breaker & _PY_ASYNC_EXCEPTION_BIT) { + if (_PyEval_RaiseAsyncExc(tstate) < 0) { + return -1; + } + } #if defined(Py_REMOTE_DEBUG) && defined(Py_SUPPORTS_REMOTE_DEBUG) _PyRunRemoteDebugger(tstate); #endif - if (!_Py_ThreadCanHandleSignals(tstate->interp)) { - return 0; + if (_Py_ThreadCanHandleSignals(tstate->interp)) { + if (_PyErr_CheckSignalsTstate(tstate) < 0) { + return -1; + } } - return _PyErr_CheckSignalsTstate(tstate); + return 0; } @@ -1940,7 +1947,7 @@ signal_install_handlers(void) /* Restore signals that the interpreter has called SIG_IGN on to SIG_DFL. * * All of the code in this function must only use async-signal-safe functions, - * listed at `man 7 signal` or + * listed at `man 7 signal-safety` or * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. * * If this function is updated, update also _posix_spawn() of subprocess.py. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index f3ad01854de93b..3e82af3194d053 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -109,7 +109,9 @@ Local naming conventions: #include "pycore_capsule.h" // _PyCapsule_SetTraverse() #include "pycore_fileutils.h" // _Py_set_inheritable() #include "pycore_moduleobject.h" // _PyModule_GetState +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_time.h" // _PyTime_AsMilliseconds() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #include "pycore_pystate.h" // _Py_AssertHoldsTstate() #ifdef _Py_MEMORY_SANITIZER @@ -150,7 +152,7 @@ listen([n]) -- start listening for incoming connections\n\ recv(buflen[, flags]) -- receive data\n\ recv_into(buffer[, nbytes[, flags]]) -- receive data (into a buffer)\n\ recvfrom(buflen[, flags]) -- receive data and sender\'s address\n\ -recvfrom_into(buffer[, nbytes, [, flags])\n\ +recvfrom_into(buffer[, nbytes, [, flags]])\n\ -- receive data and sender\'s address (into a buffer)\n\ sendall(data[, flags]) -- send all data\n\ send(data[, flags]) -- send data, may not send all of it\n\ @@ -652,10 +654,6 @@ class _socket.socket "PySocketSockObject *" "clinic_state()->sock_type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=2db2489bd2219fd8]*/ -#define clinic_state() (find_module_state_by_def(type)) -#include "clinic/socketmodule.c.h" -#undef clinic_state - /* XXX There's a problem here: *static* functions are not supposed to have a Py prefix (or use CapitalizedWords). Later... */ @@ -686,6 +684,18 @@ class _socket.socket "PySocketSockObject *" "clinic_state()->sock_type" #define IS_SELECTABLE(s) (_PyIsSelectable_fd((s)->sock_fd) || (s)->sock_timeout <= 0) #endif +// SCM_RIGHTS, sendmsg(), recvmsg() and sethostname() don't work properly on +// Cygwin: disable these features. +#ifdef __CYGWIN__ +# undef CMSG_LEN +# undef SCM_RIGHTS +# undef HAVE_SETHOSTNAME +#endif + +#define clinic_state() (find_module_state_by_def(type)) +#include "clinic/socketmodule.c.h" +#undef clinic_state + static PyObject* select_error(void) { @@ -2768,6 +2778,12 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret) _Py_FALLTHROUGH; #endif /* AF_RDS */ +#ifdef AF_DIVERT + case AF_DIVERT: + /* FreeBSD divert(4) sockets use sockaddr_in: fall-through */ + _Py_FALLTHROUGH; +#endif /* AF_DIVERT */ + case AF_INET: { *len_ret = sizeof (struct sockaddr_in); @@ -3069,7 +3085,6 @@ sock_accept(PyObject *self, PyObject *Py_UNUSED(ignored)) socklen_t addrlen; PyObject *sock = NULL; PyObject *addr = NULL; - PyObject *res = NULL; struct sock_accept ctx; if (!getsockaddrlen(s, &addrlen)) @@ -3095,7 +3110,7 @@ sock_accept(PyObject *self, PyObject *Py_UNUSED(ignored)) if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) { PyErr_SetFromWindowsErr(0); SOCKETCLOSE(newfd); - goto finally; + goto error; } #endif #else @@ -3106,7 +3121,7 @@ sock_accept(PyObject *self, PyObject *Py_UNUSED(ignored)) { if (_Py_set_inheritable(newfd, 0, NULL) < 0) { SOCKETCLOSE(newfd); - goto finally; + goto error; } } #endif @@ -3114,20 +3129,20 @@ sock_accept(PyObject *self, PyObject *Py_UNUSED(ignored)) sock = PyLong_FromSocket_t(newfd); if (sock == NULL) { SOCKETCLOSE(newfd); - goto finally; + goto error; } addr = makesockaddr(get_sock_fd(s), SAS2SA(&addrbuf), addrlen, s->sock_proto); if (addr == NULL) - goto finally; + goto error; - res = PyTuple_Pack(2, sock, addr); + return _PyTuple_FromPairSteal(sock, addr); -finally: +error: Py_XDECREF(sock); Py_XDECREF(addr); - return res; + return NULL; } PyDoc_STRVAR(accept_doc, @@ -3332,32 +3347,58 @@ sock_setsockopt(PyObject *self, PyObject *args) { PySocketSockObject *s = _PySocketSockObject_CAST(self); + Py_ssize_t arglen; int level; int optname; int res; - Py_buffer optval; + Py_buffer buffer; int flag; unsigned int optlen; - PyObject *none; + PyObject *optval; + + if (!PyArg_ParseTuple(args, "iiO|I:setsockopt", + &level, &optname, &optval, &optlen)) + { + return NULL; + } + + arglen = PyTuple_Size(args); + if (arglen == 3 && optval == Py_None) { + PyErr_Format(PyExc_TypeError, + "setsockopt() requires 4 arguments when the third argument is None"); + return NULL; + } + if (arglen == 4 && optval != Py_None) { + PyErr_Format(PyExc_TypeError, + "setsockopt() only takes 4 arguments when the third argument is None (got %T)", + optval); + return NULL; + } #ifdef AF_VSOCK if (s->sock_family == AF_VSOCK) { + if (!PyIndex_Check(optval)) { + PyErr_Format(PyExc_TypeError, + "setsockopt() argument 3 for AF_VSOCK must be an int (got %T)", + optval); + } uint64_t vflag; // Must be set width of 64 bits /* setsockopt(level, opt, flag) */ - if (PyArg_ParseTuple(args, "iiK:setsockopt", - &level, &optname, &vflag)) { - // level should always be set to AF_VSOCK - res = setsockopt(get_sock_fd(s), level, optname, - (void*)&vflag, sizeof vflag); - goto done; + if (!PyArg_Parse(optval, "K", &vflag)) { + return NULL; } - return NULL; + // level should always be set to AF_VSOCK + res = setsockopt(get_sock_fd(s), level, optname, + (void*)&vflag, sizeof vflag); + goto done; } #endif /* setsockopt(level, opt, flag) */ - if (PyArg_ParseTuple(args, "iii:setsockopt", - &level, &optname, &flag)) { + if (PyIndex_Check(optval)) { + if (!PyArg_Parse(optval, "i", &flag)) { + return NULL; + } #ifdef MS_WINDOWS if (optname == SIO_TCP_SET_ACK_FREQUENCY) { DWORD dummy; @@ -3374,36 +3415,40 @@ sock_setsockopt(PyObject *self, PyObject *args) goto done; } - PyErr_Clear(); - /* setsockopt(level, opt, None, flag) */ - if (PyArg_ParseTuple(args, "iiO!I:setsockopt", - &level, &optname, Py_TYPE(Py_None), &none, &optlen)) { + /* setsockopt(level, opt, None, optlen) */ + if (optval == Py_None) { assert(sizeof(socklen_t) >= sizeof(unsigned int)); res = setsockopt(get_sock_fd(s), level, optname, NULL, (socklen_t)optlen); goto done; } - PyErr_Clear(); /* setsockopt(level, opt, buffer) */ - if (!PyArg_ParseTuple(args, "iiy*:setsockopt", - &level, &optname, &optval)) - return NULL; - + if (PyObject_CheckBuffer(optval)) { + if (!PyArg_Parse(optval, "y*", &buffer)) { + return NULL; + } #ifdef MS_WINDOWS - if (optval.len > INT_MAX) { - PyBuffer_Release(&optval); - PyErr_Format(PyExc_OverflowError, - "socket option is larger than %i bytes", - INT_MAX); - return NULL; - } - res = setsockopt(get_sock_fd(s), level, optname, - optval.buf, (int)optval.len); + if (buffer.len > INT_MAX) { + PyBuffer_Release(&buffer); + PyErr_Format(PyExc_OverflowError, + "socket option is larger than %i bytes", + INT_MAX); + return NULL; + } + res = setsockopt(get_sock_fd(s), level, optname, + buffer.buf, (int)buffer.len); #else - res = setsockopt(get_sock_fd(s), level, optname, optval.buf, optval.len); + res = setsockopt(get_sock_fd(s), level, optname, buffer.buf, buffer.len); #endif - PyBuffer_Release(&optval); + PyBuffer_Release(&buffer); + goto done; + } + + PyErr_Format(PyExc_TypeError, + "socket option should be int, bytes-like object or None (got %T)", + optval); + return NULL; done: if (res < 0) { @@ -3436,7 +3481,6 @@ sock_getsockopt(PyObject *self, PyObject *args) int level; int optname; int res; - PyObject *buf; socklen_t buflen = 0; int flag = 0; socklen_t flagsize; @@ -3481,17 +3525,17 @@ sock_getsockopt(PyObject *self, PyObject *args) "getsockopt buflen out of range"); return NULL; } - buf = PyBytes_FromStringAndSize((char *)NULL, buflen); - if (buf == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(buflen); + if (writer == NULL) { return NULL; + } res = getsockopt(get_sock_fd(s), level, optname, - (void *)PyBytes_AS_STRING(buf), &buflen); + PyBytesWriter_GetData(writer), &buflen); if (res < 0) { - Py_DECREF(buf); + PyBytesWriter_Discard(writer); return s->errorhandler(); } - _PyBytes_Resize(&buf, buflen); - return buf; + return PyBytesWriter_FinishWithSize(writer, buflen); } PyDoc_STRVAR(getsockopt_doc, @@ -3948,7 +3992,6 @@ sock_recv(PyObject *self, PyObject *args) Py_ssize_t recvlen, outlen; int flags = 0; - PyObject *buf; if (!PyArg_ParseTuple(args, "n|i:recv", &recvlen, &flags)) return NULL; @@ -3960,25 +4003,21 @@ sock_recv(PyObject *self, PyObject *args) } /* Allocate a new string. */ - buf = PyBytes_FromStringAndSize((char *) 0, recvlen); - if (buf == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(recvlen); + if (writer == NULL) { return NULL; + } /* Call the guts */ - outlen = sock_recv_guts(s, PyBytes_AS_STRING(buf), recvlen, flags); + outlen = sock_recv_guts(s, PyBytesWriter_GetData(writer), recvlen, flags); if (outlen < 0) { /* An error occurred, release the string and return an error. */ - Py_DECREF(buf); + PyBytesWriter_Discard(writer); return NULL; } - if (outlen != recvlen) { - /* We did not read as many bytes as we anticipated, resize the - string if possible and be successful. */ - _PyBytes_Resize(&buf, outlen); - } - return buf; + return PyBytesWriter_FinishWithSize(writer, outlen); } PyDoc_STRVAR(recv_doc, @@ -4134,9 +4173,7 @@ sock_recvfrom(PyObject *self, PyObject *args) { PySocketSockObject *s = _PySocketSockObject_CAST(self); - PyObject *buf = NULL; PyObject *addr = NULL; - PyObject *ret = NULL; int flags = 0; Py_ssize_t recvlen, outlen; @@ -4149,30 +4186,28 @@ sock_recvfrom(PyObject *self, PyObject *args) return NULL; } - buf = PyBytes_FromStringAndSize((char *) 0, recvlen); - if (buf == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(recvlen); + if (writer == NULL) { return NULL; + } - outlen = sock_recvfrom_guts(s, PyBytes_AS_STRING(buf), + outlen = sock_recvfrom_guts(s, PyBytesWriter_GetData(writer), recvlen, flags, &addr); if (outlen < 0) { - goto finally; + PyBytesWriter_Discard(writer); + goto error; } - if (outlen != recvlen) { - /* We did not read as many bytes as we anticipated, resize the - string if possible and be successful. */ - if (_PyBytes_Resize(&buf, outlen) < 0) - /* Oopsy, not so successful after all. */ - goto finally; + PyObject *buf = PyBytesWriter_FinishWithSize(writer, outlen); + if (buf == NULL) { + goto error; } - ret = PyTuple_Pack(2, buf, addr); + return _PyTuple_FromPairSteal(buf, addr); -finally: - Py_XDECREF(buf); +error: Py_XDECREF(addr); - return ret; + return NULL; } PyDoc_STRVAR(recvfrom_doc, @@ -4403,11 +4438,10 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen, static PyObject * makeval_recvmsg(ssize_t received, void *data) { - PyObject **buf = data; - - if (received < PyBytes_GET_SIZE(*buf)) - _PyBytes_Resize(buf, received); - return Py_XNewRef(*buf); + PyBytesWriter **writer = data; + PyObject *buf = PyBytesWriter_FinishWithSize(*writer, received); + *writer = NULL; + return buf; } /* s.recvmsg(bufsize[, ancbufsize[, flags]]) method */ @@ -4415,13 +4449,8 @@ makeval_recvmsg(ssize_t received, void *data) static PyObject * sock_recvmsg(PyObject *self, PyObject *args) { - PySocketSockObject *s = _PySocketSockObject_CAST(self); - Py_ssize_t bufsize, ancbufsize = 0; int flags = 0; - struct iovec iov; - PyObject *buf = NULL, *retval = NULL; - if (!PyArg_ParseTuple(args, "n|ni:recvmsg", &bufsize, &ancbufsize, &flags)) return NULL; @@ -4429,17 +4458,23 @@ sock_recvmsg(PyObject *self, PyObject *args) PyErr_SetString(PyExc_ValueError, "negative buffer size in recvmsg()"); return NULL; } - if ((buf = PyBytes_FromStringAndSize(NULL, bufsize)) == NULL) + + PyBytesWriter *writer = PyBytesWriter_Create(bufsize); + if (writer == NULL) { return NULL; - iov.iov_base = PyBytes_AS_STRING(buf); + } + struct iovec iov; + iov.iov_base = PyBytesWriter_GetData(writer); iov.iov_len = bufsize; /* Note that we're passing a pointer to *our pointer* to the bytes - object here (&buf); makeval_recvmsg() may incref the object, or - deallocate it and set our pointer to NULL. */ + writer (&writer); makeval_recvmsg() finish it and set our pointer to + NULL. */ + PyObject *retval; + PySocketSockObject *s = _PySocketSockObject_CAST(self); retval = sock_recvmsg_guts(s, &iov, 1, flags, ancbufsize, - &makeval_recvmsg, &buf); - Py_XDECREF(buf); + &makeval_recvmsg, &writer); + PyBytesWriter_Discard(writer); return retval; } @@ -4491,17 +4526,19 @@ sock_recvmsg_into(PyObject *self, PyObject *args) struct iovec *iovs = NULL; Py_ssize_t i, nitems, nbufs = 0; Py_buffer *bufs = NULL; - PyObject *buffers_arg, *fast, *retval = NULL; + PyObject *buffers_arg, *buffers_tuple, *retval = NULL; if (!PyArg_ParseTuple(args, "O|ni:recvmsg_into", &buffers_arg, &ancbufsize, &flags)) return NULL; - if ((fast = PySequence_Fast(buffers_arg, - "recvmsg_into() argument 1 must be an " - "iterable")) == NULL) + buffers_tuple = PySequence_Tuple(buffers_arg); + if (buffers_tuple == NULL) { + PyErr_SetString(PyExc_TypeError, + "recvmsg_into() argument 1 must be an iterable"); return NULL; - nitems = PySequence_Fast_GET_SIZE(fast); + } + nitems = PyTuple_GET_SIZE(buffers_tuple); if (nitems > INT_MAX) { PyErr_SetString(PyExc_OSError, "recvmsg_into() argument 1 is too long"); goto finally; @@ -4515,7 +4552,7 @@ sock_recvmsg_into(PyObject *self, PyObject *args) goto finally; } for (; nbufs < nitems; nbufs++) { - if (!PyArg_Parse(PySequence_Fast_GET_ITEM(fast, nbufs), + if (!PyArg_Parse(PyTuple_GET_ITEM(buffers_tuple, nbufs), "w*;recvmsg_into() argument 1 must be an iterable " "of single-segment read-write buffers", &bufs[nbufs])) @@ -4531,7 +4568,7 @@ sock_recvmsg_into(PyObject *self, PyObject *args) PyBuffer_Release(&bufs[i]); PyMem_Free(bufs); PyMem_Free(iovs); - Py_DECREF(fast); + Py_DECREF(buffers_tuple); return retval; } @@ -4602,12 +4639,13 @@ _socket.socket.send Send a data string to the socket. For the optional flags argument, see the Unix manual. -Return the number of bytes sent; this may be less than len(data) if the network is busy. +Return the number of bytes sent; this may be less than len(data) if +the network is busy. [clinic start generated code]*/ static PyObject * _socket_socket_send_impl(PySocketSockObject *s, Py_buffer *pbuf, int flags) -/*[clinic end generated code: output=3ddf83f17d0c875b input=befe7d7790ccb035]*/ +/*[clinic end generated code: output=3ddf83f17d0c875b input=d2b8af9bf99cfafb]*/ { struct sock_send ctx; @@ -4637,13 +4675,14 @@ Send a data string to the socket. For the optional flags argument, see the Unix manual. This calls send() repeatedly until all data is sent. -If an error occurs, it's impossible to tell how much data has been sent. +If an error occurs, it's impossible to tell how much data has been +sent. [clinic start generated code]*/ static PyObject * _socket_socket_sendall_impl(PySocketSockObject *s, Py_buffer *pbuf, int flags) -/*[clinic end generated code: output=ec92861424d3faa8 input=732b15b9ca64dce6]*/ +/*[clinic end generated code: output=ec92861424d3faa8 input=2600de13b4614893]*/ { char *buf; @@ -4777,6 +4816,7 @@ sock_sendto(PyObject *self, PyObject *args) } if (PySys_Audit("socket.sendto", "OO", s, addro) < 0) { + PyBuffer_Release(&pbuf); return NULL; } @@ -4823,14 +4863,14 @@ sock_sendmsg_iovec(PySocketSockObject *s, PyObject *data_arg, /* Fill in an iovec for each message part, and save the Py_buffer structs to release afterwards. */ - data_fast = PySequence_Fast(data_arg, - "sendmsg() argument 1 must be an " - "iterable"); + data_fast = PySequence_Tuple(data_arg); if (data_fast == NULL) { + PyErr_SetString(PyExc_TypeError, + "sendmsg() argument 1 must be an iterable"); goto finally; } - ndataparts = PySequence_Fast_GET_SIZE(data_fast); + ndataparts = PyTuple_GET_SIZE(data_fast); if (ndataparts > INT_MAX) { PyErr_SetString(PyExc_OSError, "sendmsg() argument 1 is too long"); goto finally; @@ -4852,7 +4892,7 @@ sock_sendmsg_iovec(PySocketSockObject *s, PyObject *data_arg, } } for (; ndatabufs < ndataparts; ndatabufs++) { - if (PyObject_GetBuffer(PySequence_Fast_GET_ITEM(data_fast, ndatabufs), + if (PyObject_GetBuffer(PyTuple_GET_ITEM(data_fast, ndatabufs), &databufs[ndatabufs], PyBUF_SIMPLE) < 0) goto finally; iovs[ndatabufs].iov_base = databufs[ndatabufs].buf; @@ -4892,20 +4932,20 @@ The buffers argument specifies the non-ancillary data as an iterable of bytes-like objects (e.g. bytes objects). The ancdata argument specifies the ancillary data (control messages) as an iterable of zero or more tuples (cmsg_level, cmsg_type, -cmsg_data), where cmsg_level and cmsg_type are integers specifying the -protocol level and protocol-specific type respectively, and cmsg_data -is a bytes-like object holding the associated data. The flags -argument defaults to 0 and has the same meaning as for send(). If -address is supplied and not None, it sets a destination address for -the message. The return value is the number of bytes of non-ancillary -data sent. +cmsg_data), where cmsg_level and cmsg_type are integers specifying +the protocol level and protocol-specific type respectively, and +cmsg_data is a bytes-like object holding the associated data. The +flags argument defaults to 0 and has the same meaning as for send(). +If address is supplied and not None, it sets a destination address +for the message. The return value is the number of bytes of +non-ancillary data sent. [clinic start generated code]*/ static PyObject * _socket_socket_sendmsg_impl(PySocketSockObject *s, PyObject *data_arg, PyObject *cmsg_arg, int flags, PyObject *addr_arg) -/*[clinic end generated code: output=3b4cb1110644ce39 input=479c13d90bd2f88b]*/ +/*[clinic end generated code: output=3b4cb1110644ce39 input=8ae408971a3aa329]*/ { Py_ssize_t i, ndatabufs = 0, ncmsgs, ncmsgbufs = 0; @@ -4952,11 +4992,13 @@ _socket_socket_sendmsg_impl(PySocketSockObject *s, PyObject *data_arg, if (cmsg_arg == NULL) ncmsgs = 0; else { - if ((cmsg_fast = PySequence_Fast(cmsg_arg, - "sendmsg() argument 2 must be an " - "iterable")) == NULL) + cmsg_fast = PySequence_Tuple(cmsg_arg); + if (cmsg_fast == NULL) { + PyErr_SetString(PyExc_TypeError, + "sendmsg() argument 2 must be an iterable"); goto finally; - ncmsgs = PySequence_Fast_GET_SIZE(cmsg_fast); + } + ncmsgs = PyTuple_GET_SIZE(cmsg_fast); } #ifndef CMSG_SPACE @@ -4976,8 +5018,9 @@ _socket_socket_sendmsg_impl(PySocketSockObject *s, PyObject *data_arg, controllen = controllen_last = 0; while (ncmsgbufs < ncmsgs) { size_t bufsize, space; + PyObject *item = PyTuple_GET_ITEM(cmsg_fast, ncmsgbufs); - if (!PyArg_Parse(PySequence_Fast_GET_ITEM(cmsg_fast, ncmsgbufs), + if (!PyArg_Parse(item, "(iiy*):[sendmsg() ancillary data items]", &cmsgs[ncmsgbufs].level, &cmsgs[ncmsgbufs].type, @@ -5506,13 +5549,6 @@ sock_finalize(PyObject *self) PyErr_SetRaisedException(exc); } -static int -sock_traverse(PyObject *s, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(s)); - return 0; -} - static void sock_dealloc(PyObject *s) { @@ -5811,7 +5847,7 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto, static PyType_Slot sock_slots[] = { {Py_tp_dealloc, sock_dealloc}, - {Py_tp_traverse, sock_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_repr, sock_repr}, {Py_tp_doc, (void *)sock_doc}, {Py_tp_methods, sock_methods}, @@ -6234,7 +6270,7 @@ socket_gethostbyaddr(PyObject *self, PyObject *args) gethostbyaddr_r is 8-byte aligned, which at least llvm-gcc does not ensure. The attribute below instructs the compiler to maintain this alignment. */ - char buf[16384] Py_ALIGNED(8); + _Py_ALIGNED_DEF(8, char) buf[16384]; int buf_len = (sizeof buf) - 1; int errnop; #endif @@ -6516,7 +6552,6 @@ socket_socketpair(PyObject *self, PyObject *args) PySocketSockObject *s0 = NULL, *s1 = NULL; SOCKET_T sv[2]; int family, type = SOCK_STREAM, proto = 0; - PyObject *res = NULL; socket_state *state = get_module_state(self); #ifdef SOCK_CLOEXEC int *atomic_flag_works = &sock_cloexec_works; @@ -6561,28 +6596,26 @@ socket_socketpair(PyObject *self, PyObject *args) return set_error(); if (_Py_set_inheritable(sv[0], 0, atomic_flag_works) < 0) - goto finally; + goto error; if (_Py_set_inheritable(sv[1], 0, atomic_flag_works) < 0) - goto finally; + goto error; s0 = new_sockobject(state, sv[0], family, type, proto); if (s0 == NULL) - goto finally; + goto error; s1 = new_sockobject(state, sv[1], family, type, proto); if (s1 == NULL) - goto finally; - res = PyTuple_Pack(2, s0, s1); + goto error; + return _PyTuple_FromPairSteal((PyObject *)s0, (PyObject *)s1); -finally: - if (res == NULL) { - if (s0 == NULL) - SOCKETCLOSE(sv[0]); - if (s1 == NULL) - SOCKETCLOSE(sv[1]); - } +error: + if (s0 == NULL) + SOCKETCLOSE(sv[0]); + if (s1 == NULL) + SOCKETCLOSE(sv[1]); Py_XDECREF(s0); Py_XDECREF(s1); - return res; + return NULL; } PyDoc_STRVAR(socketpair_doc, @@ -6662,6 +6695,7 @@ _socket_htonl_impl(PyObject *module, uint32_t x) /* socket.inet_aton() and socket.inet_ntoa() functions. */ /*[clinic input] +@permit_long_summary _socket.inet_aton ip_addr: str / @@ -6671,7 +6705,7 @@ Convert an IP address in string format (123.45.67.89) to the 32-bit packed binar static PyObject * _socket_inet_aton_impl(PyObject *module, const char *ip_addr) -/*[clinic end generated code: output=f2c2f772eb721b6e input=3a52dec207bf8956]*/ +/*[clinic end generated code: output=f2c2f772eb721b6e input=0bd9e5ee400fafd6]*/ { #ifdef HAVE_INET_ATON struct in_addr buf; @@ -6954,7 +6988,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs) if (PySys_Audit("socket.getaddrinfo", "OOiii", hobj, pobj, family, socktype, protocol) < 0) { - return NULL; + goto err; } memset(&hints, 0, sizeof(hints)); @@ -7164,7 +7198,7 @@ socket_setdefaulttimeout(PyObject *self, PyObject *arg) PyDoc_STRVAR(setdefaulttimeout_doc, "setdefaulttimeout(timeout)\n\ \n\ -Set the default timeout in seconds (float) for new socket objects.\n\ +Set the default timeout in seconds (real number) for new socket objects.\n\ A value of None indicates that new socket objects have no timeout.\n\ When the socket module is first imported, the default is None."); @@ -7259,7 +7293,7 @@ Returns a list of network interface information (index, name) tuples."); /*[clinic input] _socket.if_nametoindex - oname: object(converter="PyUnicode_FSConverter") + oname: unicode_fs_encoded / Returns the interface index corresponding to the interface name if_name. @@ -7267,7 +7301,7 @@ Returns the interface index corresponding to the interface name if_name. static PyObject * _socket_if_nametoindex_impl(PyObject *module, PyObject *oname) -/*[clinic end generated code: output=289a411614f30244 input=01e0f1205307fb77]*/ +/*[clinic end generated code: output=289a411614f30244 input=6125dc20683560cf]*/ { #ifdef MS_WINDOWS NET_IFINDEX index; @@ -7275,11 +7309,10 @@ _socket_if_nametoindex_impl(PyObject *module, PyObject *oname) unsigned long index; #endif + errno = ENODEV; // in case 'if_nametoindex' does not set errno index = if_nametoindex(PyBytes_AS_STRING(oname)); - Py_DECREF(oname); if (index == 0) { - /* if_nametoindex() doesn't set errno */ - PyErr_SetString(PyExc_OSError, "no interface with this name"); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -7288,6 +7321,7 @@ _socket_if_nametoindex_impl(PyObject *module, PyObject *oname) /*[clinic input] +@permit_long_summary _socket.if_indextoname if_index as index: NET_IFINDEX / @@ -7297,8 +7331,9 @@ Returns the interface name corresponding to the interface index if_index. static PyObject * _socket_if_indextoname_impl(PyObject *module, NET_IFINDEX index) -/*[clinic end generated code: output=e48bc324993052e0 input=c93f753d0cf6d7d1]*/ +/*[clinic end generated code: output=e48bc324993052e0 input=2a0026b271cd43ae]*/ { + errno = ENXIO; // in case 'if_indextoname' does not set errno char name[IF_NAMESIZE + 1]; if (if_indextoname(index, name) == NULL) { PyErr_SetFromErrno(PyExc_OSError); @@ -8253,6 +8288,9 @@ socket_exec(PyObject *m) #ifdef SO_BINDTODEVICE ADD_INT_MACRO(m, SO_BINDTODEVICE); #endif +#ifdef SO_PASSRIGHTS + ADD_INT_MACRO(m, SO_PASSRIGHTS); +#endif #ifdef SO_BINDTOIFINDEX ADD_INT_MACRO(m, SO_BINDTOIFINDEX); #endif @@ -8503,6 +8541,43 @@ socket_exec(PyObject *m) ADD_INT_MACRO(m, J1939_FILTER_MAX); #endif +#ifdef HAVE_LINUX_CAN_ISOTP_H + ADD_INT_MACRO(m, SOL_CAN_ISOTP); + + ADD_INT_MACRO(m, CAN_ISOTP_OPTS); + ADD_INT_MACRO(m, CAN_ISOTP_RECV_FC); + + ADD_INT_MACRO(m, CAN_ISOTP_TX_STMIN); + ADD_INT_MACRO(m, CAN_ISOTP_RX_STMIN); + ADD_INT_MACRO(m, CAN_ISOTP_LL_OPTS); + + ADD_INT_MACRO(m, CAN_ISOTP_LISTEN_MODE); + ADD_INT_MACRO(m, CAN_ISOTP_EXTEND_ADDR); + ADD_INT_MACRO(m, CAN_ISOTP_TX_PADDING); + ADD_INT_MACRO(m, CAN_ISOTP_RX_PADDING); + ADD_INT_MACRO(m, CAN_ISOTP_CHK_PAD_LEN); + ADD_INT_MACRO(m, CAN_ISOTP_CHK_PAD_DATA); + ADD_INT_MACRO(m, CAN_ISOTP_HALF_DUPLEX); + ADD_INT_MACRO(m, CAN_ISOTP_FORCE_TXSTMIN); + ADD_INT_MACRO(m, CAN_ISOTP_FORCE_RXSTMIN); + ADD_INT_MACRO(m, CAN_ISOTP_RX_EXT_ADDR); + ADD_INT_MACRO(m, CAN_ISOTP_WAIT_TX_DONE); +#ifdef CAN_ISOTP_SF_BROADCAST + ADD_INT_MACRO(m, CAN_ISOTP_SF_BROADCAST); +#endif + + ADD_INT_MACRO(m, CAN_ISOTP_DEFAULT_FLAGS); + ADD_INT_MACRO(m, CAN_ISOTP_DEFAULT_EXT_ADDRESS); + ADD_INT_MACRO(m, CAN_ISOTP_DEFAULT_PAD_CONTENT); + ADD_INT_MACRO(m, CAN_ISOTP_DEFAULT_FRAME_TXTIME); + ADD_INT_MACRO(m, CAN_ISOTP_DEFAULT_RECV_BS); + ADD_INT_MACRO(m, CAN_ISOTP_DEFAULT_RECV_STMIN); + ADD_INT_MACRO(m, CAN_ISOTP_DEFAULT_RECV_WFTMAX); + + ADD_INT_MACRO(m, CAN_ISOTP_DEFAULT_LL_MTU); + ADD_INT_MACRO(m, CAN_ISOTP_DEFAULT_LL_TX_DL); + ADD_INT_MACRO(m, CAN_ISOTP_DEFAULT_LL_TX_FLAGS); +#endif #ifdef SOL_RDS ADD_INT_MACRO(m, SOL_RDS); #endif @@ -8845,6 +8920,9 @@ socket_exec(PyObject *m) #ifdef IPV6_HOPLIMIT ADD_INT_MACRO(m, IPV6_HOPLIMIT); #endif +#ifdef IPV6_HDRINCL + ADD_INT_MACRO(m, IPV6_HDRINCL); +#endif #ifdef IPV6_HOPOPTS ADD_INT_MACRO(m, IPV6_HOPOPTS); #endif @@ -9217,6 +9295,9 @@ socket_exec(PyObject *m) /* Initialize gethostbyname lock */ #if defined(USE_GETHOSTBYNAME_LOCK) netdb_lock = PyThread_allocate_lock(); + if (netdb_lock == NULL) { + goto error; + } #endif #ifdef MS_WINDOWS @@ -9237,6 +9318,7 @@ socket_exec(PyObject *m) } static struct PyModuleDef_Slot socket_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, socket_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index 200b2b8c7d8310..ac770889ae87f2 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -18,6 +18,10 @@ */ #ifdef AF_BTH # include <ws2bth.h> +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpragma-pack" +# endif # include <pshpack1.h> /* @@ -51,7 +55,10 @@ struct SOCKADDR_BTH_REDEF { }; # include <poppack.h> -#endif +# ifdef __clang__ +# pragma clang diagnostic pop +# endif +#endif /* AF_BTH */ /* Windows 'supports' CMSG_LEN, but does not follow the POSIX standard * interface at all, so there is no point including the code that @@ -158,6 +165,10 @@ typedef int socklen_t; #include <linux/can/bcm.h> #endif +#ifdef HAVE_LINUX_CAN_ISOTP_H +#include <linux/can/isotp.h> +#endif + #ifdef HAVE_LINUX_CAN_J1939_H #include <linux/can/j1939.h> #endif diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c index d0d5223e5acea8..e9e1c4811b8303 100644 --- a/Modules/symtablemodule.c +++ b/Modules/symtablemodule.c @@ -13,17 +13,20 @@ module _symtable _symtable.symtable source: object - filename: object(converter='PyUnicode_FSDecoder') + filename: unicode_fs_decoded startstr: str / + * + module as modname: object = None Return symbol and scope dictionaries used internally by compiler. [clinic start generated code]*/ static PyObject * _symtable_symtable_impl(PyObject *module, PyObject *source, - PyObject *filename, const char *startstr) -/*[clinic end generated code: output=59eb0d5fc7285ac4 input=9dd8a50c0c36a4d7]*/ + PyObject *filename, const char *startstr, + PyObject *modname) +/*[clinic end generated code: output=235ec5a87a9ce178 input=fbf9adaa33c7070d]*/ { struct symtable *st; PyObject *t; @@ -47,12 +50,20 @@ _symtable_symtable_impl(PyObject *module, PyObject *source, else { PyErr_SetString(PyExc_ValueError, "symtable() arg 3 must be 'exec' or 'eval' or 'single'"); - Py_DECREF(filename); Py_XDECREF(source_copy); return NULL; } - st = _Py_SymtableStringObjectFlags(str, filename, start, &cf); - Py_DECREF(filename); + if (modname == Py_None) { + modname = NULL; + } + else if (!PyUnicode_Check(modname)) { + PyErr_Format(PyExc_TypeError, + "symtable() argument 'module' must be str or None, not %T", + modname); + Py_XDECREF(source_copy); + return NULL; + } + st = _Py_SymtableStringObjectFlags(str, filename, start, &cf, modname); Py_XDECREF(source_copy); if (st == NULL) { return NULL; @@ -111,6 +122,7 @@ symtable_init_constants(PyObject *m) } static PyModuleDef_Slot symtable_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, symtable_init_constants}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/syslogmodule.c b/Modules/syslogmodule.c index 5d7fd20c4e0999..2d13f9eda758dd 100644 --- a/Modules/syslogmodule.c +++ b/Modules/syslogmodule.c @@ -451,6 +451,7 @@ syslog_exec(PyObject *module) } static PyModuleDef_Slot syslog_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, syslog_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/termios.c b/Modules/termios.c index efb5fcc17fa5ef..38743e176f0bf6 100644 --- a/Modules/termios.c +++ b/Modules/termios.c @@ -312,6 +312,7 @@ termios_tcsendbreak_impl(PyObject *module, int fd, int duration) } /*[clinic input] +@permit_long_summary termios.tcdrain fd: fildes @@ -322,7 +323,7 @@ Wait until all output written to file descriptor fd has been transmitted. static PyObject * termios_tcdrain_impl(PyObject *module, int fd) -/*[clinic end generated code: output=5fd86944c6255955 input=c99241b140b32447]*/ +/*[clinic end generated code: output=5fd86944c6255955 input=d1557e60b5ec66c5]*/ { termiosmodulestate *state = PyModule_GetState(module); int r; @@ -483,12 +484,13 @@ termios.tcsetwinsize Set the tty winsize for file descriptor fd. The winsize to be set is taken from the winsize argument, which -is a two-item tuple (ws_row, ws_col) like the one returned by tcgetwinsize(). +is a two-item tuple (ws_row, ws_col) like the one returned by +tcgetwinsize(). [clinic start generated code]*/ static PyObject * termios_tcsetwinsize_impl(PyObject *module, int fd, PyObject *winsz) -/*[clinic end generated code: output=2ac3c9bb6eda83e1 input=4a06424465b24aee]*/ +/*[clinic end generated code: output=2ac3c9bb6eda83e1 input=efc9beb16d06382a]*/ { if (!PySequence_Check(winsz) || PySequence_Size(winsz) != 2) { PyErr_SetString(PyExc_TypeError, @@ -499,19 +501,24 @@ termios_tcsetwinsize_impl(PyObject *module, int fd, PyObject *winsz) PyObject *tmp_item; long winsz_0, winsz_1; tmp_item = PySequence_GetItem(winsz, 0); + if (tmp_item == NULL) { + return NULL; + } winsz_0 = PyLong_AsLong(tmp_item); + Py_DECREF(tmp_item); if (winsz_0 == -1 && PyErr_Occurred()) { - Py_XDECREF(tmp_item); return NULL; } - Py_XDECREF(tmp_item); tmp_item = PySequence_GetItem(winsz, 1); + if (tmp_item == NULL) { + return NULL; + } winsz_1 = PyLong_AsLong(tmp_item); + Py_DECREF(tmp_item); if (winsz_1 == -1 && PyErr_Occurred()) { - Py_XDECREF(tmp_item); return NULL; } - Py_XDECREF(tmp_item); + termiosmodulestate *state = PyModule_GetState(module); diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 3271d87ddc27f2..d90bf1f2ef90ed 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -128,7 +128,7 @@ time_time_ns(PyObject *self, PyObject *unused) if (PyTime_Time(&t) < 0) { return NULL; } - return _PyTime_AsLong(t); + return PyLong_FromInt64(t); } PyDoc_STRVAR(time_ns_doc, @@ -261,7 +261,7 @@ time_clock_gettime_ns_impl(PyObject *module, clockid_t clk_id) if (_PyTime_FromTimespec(&t, &ts) < 0) { return NULL; } - return _PyTime_AsLong(t); + return PyLong_FromInt64(t); } #endif /* HAVE_CLOCK_GETTIME */ @@ -310,7 +310,7 @@ time_clock_settime_ns(PyObject *self, PyObject *args) return NULL; } - if (_PyTime_FromLong(&t, obj) < 0) { + if (PyLong_AsInt64(obj, &t) < 0) { return NULL; } if (_PyTime_AsTimespec(t, &ts) == -1) { @@ -820,12 +820,15 @@ time_strftime1(time_char **outbuf, size_t *bufsize, PyErr_NoMemory(); return NULL; } - *outbuf = (time_char *)PyMem_Realloc(*outbuf, - *bufsize*sizeof(time_char)); - if (*outbuf == NULL) { + time_char *tmp = (time_char *)PyMem_Realloc(*outbuf, + *bufsize*sizeof(time_char)); + if (tmp == NULL) { + PyMem_Free(*outbuf); + *outbuf = NULL; PyErr_NoMemory(); return NULL; } + *outbuf = tmp; #if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__) errno = 0; #endif @@ -1170,7 +1173,8 @@ time_tzset(PyObject *self, PyObject *unused) /* Reset timezone, altzone, daylight and tzname */ if (init_timezone(m) < 0) { - return NULL; + Py_DECREF(m); + return NULL; } Py_DECREF(m); if (PyErr_Occurred()) @@ -1216,7 +1220,7 @@ time_monotonic_ns(PyObject *self, PyObject *unused) if (PyTime_Monotonic(&t) < 0) { return NULL; } - return _PyTime_AsLong(t); + return PyLong_FromInt64(t); } PyDoc_STRVAR(monotonic_ns_doc, @@ -1248,7 +1252,7 @@ time_perf_counter_ns(PyObject *self, PyObject *unused) if (PyTime_PerfCounter(&t) < 0) { return NULL; } - return _PyTime_AsLong(t); + return PyLong_FromInt64(t); } PyDoc_STRVAR(perf_counter_ns_doc, @@ -1437,7 +1441,7 @@ time_process_time_ns(PyObject *module, PyObject *unused) if (py_process_time(state, &t, NULL) < 0) { return NULL; } - return _PyTime_AsLong(t); + return PyLong_FromInt64(t); } PyDoc_STRVAR(process_time_ns_doc, @@ -1610,7 +1614,7 @@ time_thread_time_ns(PyObject *self, PyObject *unused) if (_PyTime_GetThreadTimeWithInfo(&t, NULL) < 0) { return NULL; } - return _PyTime_AsLong(t); + return PyLong_FromInt64(t); } PyDoc_STRVAR(thread_time_ns_doc, @@ -2184,6 +2188,7 @@ time_module_free(void *module) static struct PyModuleDef_Slot time_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, time_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c index ef8cf3d0d27459..bcdcc624e66f93 100644 --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -17,11 +17,33 @@ #endif #include "Python.h" +#include "pycore_object.h" // _PyObject_VisitType() #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI +#include "pycore_unicodectype.h" // _PyUnicode_IsXidStart() #include <stdbool.h> #include <stddef.h> // offsetof() +/* helper macro to fixup start/end slice values */ +#define ADJUST_INDICES(start, end, len) \ + do { \ + if (end > len) { \ + end = len; \ + } \ + else if (end < 0) { \ + end += len; \ + if (end < 0) { \ + end = 0; \ + } \ + } \ + if (start < 0) { \ + start += len; \ + if (start < 0) { \ + start = 0; \ + } \ + } \ + } while (0) + /*[clinic input] module unicodedata class unicodedata.UCD 'PreviousDBVersion *' '<not used>' @@ -40,6 +62,11 @@ typedef struct { const unsigned char east_asian_width; /* index into _PyUnicode_EastAsianWidth */ const unsigned char normalization_quick_check; /* see is_normalized() */ + const unsigned char grapheme_cluster_break; /* index into + _PyUnicode_GraphemeBreakNames */ + const unsigned char incb; /* index into + _PyUnicode_IndicConjunctBreakNames */ + const unsigned char ext_pict; /* true if Extended_Pictographic */ } _PyUnicode_DatabaseRecord; typedef struct change_record { @@ -69,6 +96,19 @@ _getrecord_ex(Py_UCS4 code) return &_PyUnicode_Database_Records[index]; } +typedef struct { + PyObject *SegmentType; + PyObject *GraphemeBreakIteratorType; +} unicodedatastate; + +static inline unicodedatastate * +get_unicodedata_state(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (unicodedatastate *)state; +} + /* ------------- Previous-version API ------------------------------------- */ typedef struct previous_version { PyObject_HEAD @@ -230,9 +270,9 @@ unicodedata_UCD_numeric_impl(PyObject *self, int chr, have_old = 1; rc = -1.0; } - else if (old->decimal_changed != 0xFF) { + else if (old->numeric_changed != 0.0) { have_old = 1; - rc = old->decimal_changed; + rc = old->numeric_changed; } } @@ -251,6 +291,7 @@ unicodedata_UCD_numeric_impl(PyObject *self, int chr, } /*[clinic input] +@permit_long_summary unicodedata.UCD.category self: self @@ -262,7 +303,7 @@ Returns the general category assigned to the character chr as string. static PyObject * unicodedata_UCD_category_impl(PyObject *self, int chr) -/*[clinic end generated code: output=8571539ee2e6783a input=27d6f3d85050bc06]*/ +/*[clinic end generated code: output=8571539ee2e6783a input=1d729c67299e8a31]*/ { int index; Py_UCS4 c = (Py_UCS4)chr; @@ -276,6 +317,7 @@ unicodedata_UCD_category_impl(PyObject *self, int chr) } /*[clinic input] +@permit_long_summary unicodedata.UCD.bidirectional self: self @@ -289,7 +331,7 @@ If no such value is defined, an empty string is returned. static PyObject * unicodedata_UCD_bidirectional_impl(PyObject *self, int chr) -/*[clinic end generated code: output=d36310ce2039bb92 input=b3d8f42cebfcf475]*/ +/*[clinic end generated code: output=d36310ce2039bb92 input=838f8a2203bd2990]*/ { int index; Py_UCS4 c = (Py_UCS4)chr; @@ -305,6 +347,7 @@ unicodedata_UCD_bidirectional_impl(PyObject *self, int chr) } /*[clinic input] +@permit_long_summary unicodedata.UCD.combining -> int self: self @@ -318,7 +361,7 @@ Returns 0 if no combining class is defined. static int unicodedata_UCD_combining_impl(PyObject *self, int chr) -/*[clinic end generated code: output=cad056d0cb6a5920 input=9f2d6b2a95d0a22a]*/ +/*[clinic end generated code: output=cad056d0cb6a5920 input=e05edfbb882ebfed]*/ { int index; Py_UCS4 c = (Py_UCS4)chr; @@ -332,6 +375,7 @@ unicodedata_UCD_combining_impl(PyObject *self, int chr) } /*[clinic input] +@permit_long_summary unicodedata.UCD.mirrored -> int self: self @@ -346,7 +390,7 @@ character in bidirectional text, 0 otherwise. static int unicodedata_UCD_mirrored_impl(PyObject *self, int chr) -/*[clinic end generated code: output=2532dbf8121b50e6 input=5dd400d351ae6f3b]*/ +/*[clinic end generated code: output=2532dbf8121b50e6 input=6db28989e49cd9c8]*/ { int index; Py_UCS4 c = (Py_UCS4)chr; @@ -362,6 +406,7 @@ unicodedata_UCD_mirrored_impl(PyObject *self, int chr) } /*[clinic input] +@permit_long_summary unicodedata.UCD.east_asian_width self: self @@ -373,7 +418,7 @@ Returns the east asian width assigned to the character chr as string. static PyObject * unicodedata_UCD_east_asian_width_impl(PyObject *self, int chr) -/*[clinic end generated code: output=484e8537d9ee8197 input=c4854798aab026e0]*/ +/*[clinic end generated code: output=484e8537d9ee8197 input=207c5f68fa475516]*/ { int index; Py_UCS4 c = (Py_UCS4)chr; @@ -388,7 +433,19 @@ unicodedata_UCD_east_asian_width_impl(PyObject *self, int chr) return PyUnicode_FromString(_PyUnicode_EastAsianWidthNames[index]); } +// For Hangul decomposition +#define SBase 0xAC00 +#define LBase 0x1100 +#define VBase 0x1161 +#define TBase 0x11A7 +#define LCount 19 +#define VCount 21 +#define TCount 28 +#define NCount (VCount*TCount) +#define SCount (LCount*NCount) + /*[clinic input] +@permit_long_summary unicodedata.UCD.decomposition self: self @@ -402,7 +459,7 @@ An empty string is returned in case no such mapping is defined. static PyObject * unicodedata_UCD_decomposition_impl(PyObject *self, int chr) -/*[clinic end generated code: output=7d699f3ec7565d27 input=e4c12459ad68507b]*/ +/*[clinic end generated code: output=7d699f3ec7565d27 input=84d628d1abfd01ec]*/ { char decomp[256]; int code, index, count; @@ -418,6 +475,25 @@ unicodedata_UCD_decomposition_impl(PyObject *self, int chr) return Py_GetConstant(Py_CONSTANT_EMPTY_STR); /* unassigned */ } + // Hangul Decomposition. + // See section 3.12.2, "Hangul Syllable Decomposition" + // https://www.unicode.org/versions/latest/core-spec/chapter-3/#G56669 + if (SBase <= code && code < (SBase + SCount)) { + int SIndex = code - SBase; + int L = LBase + SIndex / NCount; + int V = VBase + (SIndex % NCount) / TCount; + int T = TBase + SIndex % TCount; + if (T != TBase) { + PyOS_snprintf(decomp, sizeof(decomp), + "%04X %04X %04X", L, V, T); + } + else { + PyOS_snprintf(decomp, sizeof(decomp), + "%04X %04X", L, V); + } + return PyUnicode_FromString(decomp); + } + if (code < 0 || code >= 0x110000) index = 0; else { @@ -480,15 +556,63 @@ get_decomp_record(PyObject *self, Py_UCS4 code, (*index)++; } -#define SBase 0xAC00 -#define LBase 0x1100 -#define VBase 0x1161 -#define TBase 0x11A7 -#define LCount 19 -#define VCount 21 -#define TCount 28 -#define NCount (VCount*TCount) -#define SCount (LCount*NCount) +/* Small combining runs are usually cheaper with insertion sort. */ +#define CANONICAL_ORDERING_COUNTING_SORT_THRESHOLD 20 + +static void +canonical_ordering_sort_insertion(int kind, void *data, + Py_ssize_t start, Py_ssize_t end) +{ + for (Py_ssize_t i = start + 1; i < end; i++) { + Py_UCS4 code = PyUnicode_READ(kind, data, i); + unsigned char combining = _getrecord_ex(code)->combining; + Py_ssize_t j = i; + + while (j > start) { + Py_UCS4 previous = PyUnicode_READ(kind, data, j - 1); + if (_getrecord_ex(previous)->combining <= combining) { + break; + } + PyUnicode_WRITE(kind, data, j, previous); + j--; + } + if (j != i) { + PyUnicode_WRITE(kind, data, j, code); + } + } +} + +static void +canonical_ordering_sort_counting(int kind, void *data, + Py_ssize_t start, Py_ssize_t end, + Py_UCS4 *sortbuf) +{ + Py_ssize_t counts[256] = {0}; + Py_ssize_t run_length = end - start; + Py_ssize_t total = 0; + + for (Py_ssize_t i = start; i < end; i++) { + Py_UCS4 code = PyUnicode_READ(kind, data, i); + unsigned char combining = _getrecord_ex(code)->combining; + counts[combining]++; + } + + for (size_t i = 0; i < Py_ARRAY_LENGTH(counts); i++) { + Py_ssize_t count = counts[i]; + counts[i] = total; + total += count; + } + + /* Reuse counts[] as the next output slot for each CCC. */ + for (Py_ssize_t i = start; i < end; i++) { + Py_UCS4 code = PyUnicode_READ(kind, data, i); + unsigned char combining = _getrecord_ex(code)->combining; + sortbuf[counts[combining]++] = code; + } + for (Py_ssize_t i = 0; i < run_length; i++) { + PyUnicode_WRITE(kind, data, start + i, sortbuf[i]); + } +} static PyObject* nfd_nfkd(PyObject *self, PyObject *input, int k) @@ -496,13 +620,16 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) PyObject *result; Py_UCS4 *output; Py_ssize_t i, o, osize; - int kind; - const void *data; + int input_kind, result_kind; + const void *input_data; + void *result_data; /* Longest decomposition in Unicode 3.2: U+FDFA */ Py_UCS4 stack[20]; Py_ssize_t space, isize; int index, prefix, count, stackptr; unsigned char prev, cur; + Py_UCS4 *sortbuf = NULL; + Py_ssize_t sortbuflen = 0; stackptr = 0; isize = PyUnicode_GET_LENGTH(input); @@ -522,11 +649,11 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) return NULL; } i = o = 0; - kind = PyUnicode_KIND(input); - data = PyUnicode_DATA(input); + input_kind = PyUnicode_KIND(input); + input_data = PyUnicode_DATA(input); while (i < isize) { - stack[stackptr++] = PyUnicode_READ(kind, data, i++); + stack[stackptr++] = PyUnicode_READ(input_kind, input_data, i++); while(stackptr) { Py_UCS4 code = stack[--stackptr]; /* Hangul Decomposition adds three characters in @@ -543,7 +670,9 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) } output = new_output; } - /* Hangul Decomposition. */ + // Hangul Decomposition. + // See section 3.12.2, "Hangul Syllable Decomposition" + // https://www.unicode.org/versions/latest/core-spec/chapter-3/#G56669 if (SBase <= code && code < (SBase+SCount)) { int SIndex = code - SBase; int L = LBase + SIndex / NCount; @@ -592,49 +721,83 @@ nfd_nfkd(PyObject *self, PyObject *input, int k) if (!result) return NULL; - kind = PyUnicode_KIND(result); - data = PyUnicode_DATA(result); + result_kind = PyUnicode_KIND(result); + result_data = PyUnicode_DATA(result); - /* Sort canonically. */ + /* Sort each consecutive combining-character run canonically. */ i = 0; - prev = _getrecord_ex(PyUnicode_READ(kind, data, i))->combining; - for (i++; i < PyUnicode_GET_LENGTH(result); i++) { - cur = _getrecord_ex(PyUnicode_READ(kind, data, i))->combining; - if (prev == 0 || cur == 0 || prev <= cur) { - prev = cur; + while (i < o) { + Py_ssize_t run_length, run_start; + int needs_sort = 0; + + Py_UCS4 ch = PyUnicode_READ(result_kind, result_data, i); + prev = _getrecord_ex(ch)->combining; + if (prev == 0) { + i++; continue; } - /* Non-canonical order. Need to switch *i with previous. */ - o = i - 1; - while (1) { - Py_UCS4 tmp = PyUnicode_READ(kind, data, o+1); - PyUnicode_WRITE(kind, data, o+1, - PyUnicode_READ(kind, data, o)); - PyUnicode_WRITE(kind, data, o, tmp); - o--; - if (o < 0) - break; - prev = _getrecord_ex(PyUnicode_READ(kind, data, o))->combining; - if (prev == 0 || prev <= cur) + + run_start = i++; + while (i < o) { + Py_UCS4 ch = PyUnicode_READ(result_kind, result_data, i); + cur = _getrecord_ex(ch)->combining; + if (cur == 0) { break; + } + if (prev > cur) { + needs_sort = 1; + } + prev = cur; + i++; } - prev = _getrecord_ex(PyUnicode_READ(kind, data, i))->combining; + if (!needs_sort) { + continue; + } + + run_length = i - run_start; + if (run_length < CANONICAL_ORDERING_COUNTING_SORT_THRESHOLD) { + canonical_ordering_sort_insertion(result_kind, result_data, + run_start, i); + continue; + } + + if (run_length > sortbuflen) { + Py_UCS4 *new_sortbuf = PyMem_Resize(sortbuf, + Py_UCS4, + run_length); + if (new_sortbuf == NULL) { + PyErr_NoMemory(); + PyMem_Free(sortbuf); + Py_DECREF(result); + return NULL; + } + sortbuf = new_sortbuf; + sortbuflen = run_length; + } + + canonical_ordering_sort_counting(result_kind, result_data, + run_start, i, sortbuf); } + PyMem_Free(sortbuf); return result; } static int find_nfc_index(const struct reindex* nfc, Py_UCS4 code) { - unsigned int index; - for (index = 0; nfc[index].start; index++) { - unsigned int start = nfc[index].start; - if (code < start) - return -1; - if (code <= start + nfc[index].count) { - unsigned int delta = code - start; - return nfc[index].index + delta; - } + /* The table is sorted by .start ascending with disjoint [start, start+count] + ranges and ends with a sentinel whose .start exceeds every codepoint, so + a single .start <= code test per entry also stops at the sentinel. Find + the first entry past code, then range-check the candidate (entry i - 1). */ + unsigned int i; + for (i = 0; (Py_UCS4)nfc[i].start <= code; i++) { + } + if (i == 0) { + return -1; + } + unsigned int start = nfc[i - 1].start; + if (code <= start + nfc[i - 1].count) { + return nfc[i - 1].index + (code - start); } return -1; } @@ -847,6 +1010,7 @@ is_normalized_quickcheck(PyObject *self, PyObject *input, bool nfc, bool k, } /*[clinic input] +@permit_long_summary unicodedata.UCD.is_normalized self: self @@ -862,7 +1026,7 @@ Valid values for form are 'NFC', 'NFKC', 'NFD', and 'NFKD'. static PyObject * unicodedata_UCD_is_normalized_impl(PyObject *self, PyObject *form, PyObject *input) -/*[clinic end generated code: output=11e5a3694e723ca5 input=a544f14cea79e508]*/ +/*[clinic end generated code: output=11e5a3694e723ca5 input=de66aa679265300b]*/ { if (PyUnicode_GET_LENGTH(input) == 0) { /* special case empty input strings. */ @@ -1010,21 +1174,18 @@ static const char * const hangul_syllables[][3] = { { 0, 0, "H" } }; -/* These ranges need to match makeunicodedata.py:cjk_ranges. */ static int -is_unified_ideograph(Py_UCS4 code) +find_prefix_id(Py_UCS4 code) { - return - (0x3400 <= code && code <= 0x4DBF) || /* CJK Ideograph Extension A */ - (0x4E00 <= code && code <= 0x9FFF) || /* CJK Ideograph */ - (0x20000 <= code && code <= 0x2A6DF) || /* CJK Ideograph Extension B */ - (0x2A700 <= code && code <= 0x2B739) || /* CJK Ideograph Extension C */ - (0x2B740 <= code && code <= 0x2B81D) || /* CJK Ideograph Extension D */ - (0x2B820 <= code && code <= 0x2CEA1) || /* CJK Ideograph Extension E */ - (0x2CEB0 <= code && code <= 0x2EBE0) || /* CJK Ideograph Extension F */ - (0x2EBF0 <= code && code <= 0x2EE5D) || /* CJK Ideograph Extension I */ - (0x30000 <= code && code <= 0x3134A) || /* CJK Ideograph Extension G */ - (0x31350 <= code && code <= 0x323AF); /* CJK Ideograph Extension H */ + for (int i = 0; i < (int)Py_ARRAY_LENGTH(derived_name_ranges); i++) { + if (code < derived_name_ranges[i].first) { + return -1; + } + if (code <= derived_name_ranges[i].last) { + return derived_name_ranges[i].prefixid; + } + } + return -1; } /* macros used to determine if the given code point is in the PUA range that @@ -1302,7 +1463,9 @@ _getucname(PyObject *self, } } - if (SBase <= code && code < SBase+SCount) { + int prefixid = find_prefix_id(code); + if (prefixid == 0) { + assert(SBase <= code && code < SBase+SCount); /* Hangul syllable. */ int SIndex = code - SBase; int L = SIndex / NCount; @@ -1324,11 +1487,11 @@ _getucname(PyObject *self, return 1; } - if (is_unified_ideograph(code)) { - if (buflen < 28) - /* Worst case: CJK UNIFIED IDEOGRAPH-20000 */ + if (prefixid > 0) { + const char *prefix = derived_name_prefixes[prefixid]; + if (snprintf(buffer, buflen, "%s%04X", prefix, code) >= buflen) { return 0; - sprintf(buffer, "CJK UNIFIED IDEOGRAPH-%X", code); + } return 1; } @@ -1362,7 +1525,7 @@ find_syllable(const char *str, int *len, int *pos, int count, int column) len1 = Py_SAFE_DOWNCAST(strlen(s), size_t, int); if (len1 <= *len) continue; - if (strncmp(str, s, len1) == 0) { + if (PyOS_strnicmp(str, s, len1) == 0) { *len = len1; *pos = i; } @@ -1385,6 +1548,35 @@ _check_alias_and_seq(Py_UCS4* code, int with_named_seq) return 1; } +static Py_UCS4 +parse_hex_code(const char *name, int namelen) +{ + if (namelen < 4 || namelen > 6) { + return (Py_UCS4)-1; + } + if (*name == '0') { + return (Py_UCS4)-1; + } + int v = 0; + while (namelen--) { + v *= 16; + Py_UCS1 c = Py_TOUPPER(*name); + if (c >= '0' && c <= '9') { + v += c - '0'; + } + else if (c >= 'A' && c <= 'F') { + v += c - 'A' + 10; + } + else { + return (Py_UCS4)-1; + } + name++; + } + if (v > 0x10ffff) { + return (Py_UCS4)-1; + } + return v; +} static int _getcode(const char* name, int namelen, Py_UCS4* code) @@ -1393,8 +1585,19 @@ _getcode(const char* name, int namelen, Py_UCS4* code) * Named aliases are not resolved, they are returned as a code point in the * PUA */ - /* Check for hangul syllables. */ - if (strncmp(name, "HANGUL SYLLABLE ", 16) == 0) { + int i = 0; + size_t prefixlen; + for (; i < (int)Py_ARRAY_LENGTH(derived_name_prefixes); i++) { + const char *prefix = derived_name_prefixes[i]; + prefixlen = strlen(derived_name_prefixes[i]); + if (PyOS_strnicmp(name, prefix, prefixlen) == 0) { + break; + } + } + + if (i == 0) { + /* Hangul syllables. */ + assert(PyOS_strnicmp(name, "HANGUL SYLLABLE ", 16) == 0); int len, L = -1, V = -1, T = -1; const char *pos = name + 16; find_syllable(pos, &len, &L, LCount, 0); @@ -1411,27 +1614,11 @@ _getcode(const char* name, int namelen, Py_UCS4* code) return 0; } - /* Check for unified ideographs. */ - if (strncmp(name, "CJK UNIFIED IDEOGRAPH-", 22) == 0) { - /* Four or five hexdigits must follow. */ - unsigned int v; - v = 0; - name += 22; - namelen -= 22; - if (namelen != 4 && namelen != 5) + if (i < (int)Py_ARRAY_LENGTH(derived_name_prefixes)) { + Py_UCS4 v = parse_hex_code(name + prefixlen, namelen - (int)prefixlen); + if (find_prefix_id(v) != i) { return 0; - while (namelen--) { - v *= 16; - if (*name >= '0' && *name <= '9') - v += *name - '0'; - else if (*name >= 'A' && *name <= 'F') - v += *name - 'A' + 10; - else - return 0; - name++; } - if (!is_unified_ideograph(v)) - return 0; *code = v; return 1; } @@ -1456,32 +1643,17 @@ capi_getcode(const char* name, int namelen, Py_UCS4* code, return _check_alias_and_seq(code, with_named_seq); } -static void -unicodedata_destroy_capi(PyObject *capsule) -{ - void *capi = PyCapsule_GetPointer(capsule, PyUnicodeData_CAPSULE_NAME); - PyMem_Free(capi); -} - static PyObject * unicodedata_create_capi(void) { - _PyUnicode_Name_CAPI *capi = PyMem_Malloc(sizeof(_PyUnicode_Name_CAPI)); - if (capi == NULL) { - PyErr_NoMemory(); - return NULL; - } - capi->getname = capi_getucname; - capi->getcode = capi_getcode; - - PyObject *capsule = PyCapsule_New(capi, - PyUnicodeData_CAPSULE_NAME, - unicodedata_destroy_capi); - if (capsule == NULL) { - PyMem_Free(capi); - } - return capsule; -}; + // Statically allocated so that any cached pointers stay valid after unicodedata + // is removed from sys.modules and the capsule is gc'd (gh-149449). + static _PyUnicode_Name_CAPI capi = { + .getname = capi_getucname, + .getcode = capi_getcode, + }; + return PyCapsule_New(&capi, PyUnicodeData_CAPSULE_NAME, NULL); +} /* -------------------------------------------------------------------- */ @@ -1521,6 +1693,40 @@ unicodedata_UCD_name_impl(PyObject *self, int chr, PyObject *default_value) return PyUnicode_FromString(name); } +/*[clinic input] +unicodedata.isxidstart + + chr: int(accept={str}) + / + +Return True if the character has the XID_Start property, else False. + +[clinic start generated code]*/ + +static PyObject * +unicodedata_isxidstart_impl(PyObject *module, int chr) +/*[clinic end generated code: output=7ae0e1a3915aa031 input=3812717f3a6bfc56]*/ +{ + return PyBool_FromLong(_PyUnicode_IsXidStart(chr)); +} + +/*[clinic input] +unicodedata.isxidcontinue + + chr: int(accept={str}) + / + +Return True if the character has the XID_Continue property, else False. + +[clinic start generated code]*/ + +static PyObject * +unicodedata_isxidcontinue_impl(PyObject *module, int chr) +/*[clinic end generated code: output=517caa8b38c73aed input=a971ed6e57cac374]*/ +{ + return PyBool_FromLong(_PyUnicode_IsXidContinue(chr)); +} + /*[clinic input] unicodedata.UCD.lookup @@ -1571,11 +1777,493 @@ unicodedata_UCD_lookup_impl(PyObject *self, const char *name, return PyUnicode_FromOrdinal(code); } + +/* Grapheme Cluster Break algorithm */ + +enum ExtPictState { + ExtPictState_Init, + // \p{Extended_Pictographic} Extend* + ExtPictState_Started, + // ... ZWJ + ExtPictState_ZWJ, + // ... \p{Extended_Pictographic} + ExtPictState_Matched, +}; + +enum InCBState { + InCBState_Init, + // \p{InCB=Consonant} \p{InCB=Extend}* + InCBState_Started, + // ... \p{InCB=Linker} [ \p{InCB=Extend} \p{InCB=Linker} ]* + InCBState_Linker, + // ... \p{InCB=Consonant} + InCBState_Matched, +}; + +typedef struct { + PyObject *str; + Py_ssize_t start; + Py_ssize_t pos; + Py_ssize_t end; + int gcb; + enum ExtPictState ep_state; + enum InCBState incb_state; + bool ri_flag; +} _PyGraphemeBreak; + +static inline enum ExtPictState +update_ext_pict_state(enum ExtPictState state, int gcb, bool ext_pict) +{ + if (ext_pict) { + return (state == ExtPictState_ZWJ) ? ExtPictState_Matched : ExtPictState_Started; + } + if (state == ExtPictState_Started || state == ExtPictState_Matched) { + if (gcb == GCB_Extend) { + return ExtPictState_Started; + } + if (gcb == GCB_ZWJ) { + return ExtPictState_ZWJ; + } + } + return ExtPictState_Init; +} + +static inline enum InCBState +update_incb_state(enum InCBState state, int incb) +{ + if (incb == InCB_Consonant) { + return (state == InCBState_Linker) ? InCBState_Matched : InCBState_Started; + } + if (state != InCBState_Init) { + if (incb == InCB_Extend) { + return (state == InCBState_Linker) ? InCBState_Linker : InCBState_Started; + } + if (incb == InCB_Linker) { + return InCBState_Linker; + } + } + return InCBState_Init; +} + +static inline bool +update_ri_flag(bool flag, int gcb) +{ + if (gcb == GCB_Regional_Indicator) { + return !flag; + } + else { + return false; + } +} + +static inline bool +grapheme_break(int prev_gcb, int curr_gcb, enum ExtPictState ep_state, + bool ri_flag, enum InCBState incb_state) +{ + /* GB3 */ + if (prev_gcb == GCB_CR && curr_gcb == GCB_LF) { + return false; + } + + /* GB4 */ + if (prev_gcb == GCB_CR || + prev_gcb == GCB_LF || + prev_gcb == GCB_Control) + { + return true; + } + + /* GB5 */ + if (curr_gcb == GCB_CR || + curr_gcb == GCB_LF || + curr_gcb == GCB_Control) + { + return true; + } + + /* GB6 */ + if (prev_gcb == GCB_L && + (curr_gcb == GCB_L || + curr_gcb == GCB_V || + curr_gcb == GCB_LV || + curr_gcb == GCB_LVT)) + { + return false; + } + + /* GB7 */ + if ((prev_gcb == GCB_LV || prev_gcb == GCB_V) && + (curr_gcb == GCB_V || curr_gcb == GCB_T)) + { + return false; + } + + /* GB8 */ + if ((prev_gcb == GCB_LVT || prev_gcb == GCB_T) && + curr_gcb == GCB_T) + { + return false; + } + + /* GB9 */ + if (curr_gcb == GCB_Extend || curr_gcb == GCB_ZWJ) { + return false; + } + + /* GB9a */ + if (curr_gcb == GCB_SpacingMark) { + return false; + } + + /* GB9b */ + if (prev_gcb == GCB_Prepend) { + return false; + } + + /* GB9c */ + if (incb_state == InCBState_Matched) { + return false; + } + + /* GB11 */ + if (ep_state == ExtPictState_Matched) { + return false; + } + + /* GB12 and GB13 */ + if (prev_gcb == GCB_Regional_Indicator && curr_gcb == prev_gcb) { + return ri_flag; + } + + /* GB999 */ + return true; +} + +static void +_Py_InitGraphemeBreak(_PyGraphemeBreak *iter, PyObject *str, + Py_ssize_t start, Py_ssize_t end) +{ + iter->str = str; + iter->start = iter->pos = start; + iter->end = end; + iter->gcb = 0; + iter->ep_state = ExtPictState_Init; + iter->ri_flag = false; + iter->incb_state = InCBState_Init; +} + +static Py_ssize_t +_Py_NextGraphemeBreak(_PyGraphemeBreak *iter) +{ + if (iter->start >= iter->end) { + return -1; + } + + int kind = PyUnicode_KIND(iter->str); + void *pstr = PyUnicode_DATA(iter->str); + while (iter->pos < iter->end) { + Py_UCS4 chr = PyUnicode_READ(kind, pstr, iter->pos); + const _PyUnicode_DatabaseRecord *record = _getrecord_ex(chr); + int gcb = record->grapheme_cluster_break; + iter->ep_state = update_ext_pict_state(iter->ep_state, gcb, record->ext_pict); + iter->ri_flag = update_ri_flag(iter->ri_flag, gcb); + iter->incb_state = update_incb_state(iter->incb_state, record->incb); + int prev_gcb = iter->gcb; + iter->gcb = gcb; + if (iter->pos != iter->start && + grapheme_break(prev_gcb, gcb, iter->ep_state, iter->ri_flag, + iter->incb_state)) + { + iter->start = iter->pos; + return iter->pos++; + } + ++iter->pos; + } + iter->start = iter->pos; + return iter->pos; +} + + +/* Text Segment object */ + +typedef struct { + PyObject_HEAD + PyObject *string; + Py_ssize_t start; + Py_ssize_t end; +} SegmentObject; + +static void +Segment_dealloc(PyObject *self) +{ + PyTypeObject *tp = Py_TYPE(self); + PyObject_GC_UnTrack(self); + Py_DECREF(((SegmentObject *)self)->string); + tp->tp_free(self); + Py_DECREF(tp); +} + +static int +Segment_traverse(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(((SegmentObject *)self)->string); + return 0; +} + +static PyObject * +Segment_str(PyObject *self) +{ + SegmentObject *s = (SegmentObject *)self; + return PyUnicode_Substring(s->string, s->start, s->end); +} + +static PyObject * +Segment_repr(PyObject *self) +{ + SegmentObject *s = (SegmentObject *)self; + return PyUnicode_FromFormat("<Segment %zd:%zd>", s->start, s->end); +} + +static PyMemberDef Segment_members[] = { + {"start", Py_T_PYSSIZET, offsetof(SegmentObject, start), Py_READONLY, + PyDoc_STR("grapheme start")}, + {"end", Py_T_PYSSIZET, offsetof(SegmentObject, end), Py_READONLY, + PyDoc_STR("grapheme end")}, + {NULL} /* Sentinel */ +}; + +static PyType_Slot Segment_slots[] = { + {Py_tp_dealloc, Segment_dealloc}, + {Py_tp_traverse, Segment_traverse}, + {Py_tp_str, Segment_str}, + {Py_tp_repr, Segment_repr}, + {Py_tp_members, Segment_members}, + {0, 0}, +}; + +static PyType_Spec Segment_spec = { + .name = "unicodedata.Segment", + .basicsize = sizeof(SegmentObject), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = Segment_slots +}; + + +/* Grapheme Cluster iterator */ + +typedef struct { + PyObject_HEAD + _PyGraphemeBreak iter; +} GraphemeBreakIterator; + +static void +GBI_dealloc(PyObject *self) +{ + PyTypeObject *tp = Py_TYPE(self); + PyObject_GC_UnTrack(self); + Py_DECREF(((GraphemeBreakIterator *)self)->iter.str); + tp->tp_free(self); + Py_DECREF(tp); +} + +static int +GBI_traverse(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(((GraphemeBreakIterator *)self)->iter.str); + return 0; +} + +static PyObject * +GBI_iternext(PyObject *self) +{ + GraphemeBreakIterator *it = (GraphemeBreakIterator *)self; + Py_ssize_t start = it->iter.start; + Py_ssize_t pos = _Py_NextGraphemeBreak(&it->iter); + + if (pos < 0) { + return NULL; + } + PyObject *module = PyType_GetModule(Py_TYPE(it)); + PyObject *SegmentType = get_unicodedata_state(module)->SegmentType; + SegmentObject *s = PyObject_GC_New(SegmentObject, + (PyTypeObject *)SegmentType); + if (!s) { + return NULL; + } + s->string = Py_NewRef(it->iter.str); + s->start = start; + s->end = pos; + PyObject_GC_Track(s); + return (PyObject *)s; +} + + +static PyType_Slot GraphemeBreakIterator_slots[] = { + {Py_tp_dealloc, GBI_dealloc}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, GBI_iternext}, + {Py_tp_traverse, GBI_traverse}, + {0, 0}, +}; + +static PyType_Spec GraphemeBreakIterator_spec = { + .name = "unicodedata.GraphemeBreakIterator", + .basicsize = sizeof(GraphemeBreakIterator), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + ), + .slots = GraphemeBreakIterator_slots +}; + + +/*[clinic input] +unicodedata.iter_graphemes + + unistr: unicode + start: Py_ssize_t = 0 + end: Py_ssize_t(c_default="PY_SSIZE_T_MAX") = sys.maxsize + / + +Returns an iterator to iterate over grapheme clusters. + +It uses extended grapheme cluster rules from TR29. +[clinic start generated code]*/ + +static PyObject * +unicodedata_iter_graphemes_impl(PyObject *module, PyObject *unistr, + Py_ssize_t start, Py_ssize_t end) +/*[clinic end generated code: output=b0b831944265d36f input=a1454d9e8135951f]*/ +{ + PyObject *GraphemeBreakIteratorType = get_unicodedata_state(module)->GraphemeBreakIteratorType; + GraphemeBreakIterator *gbi = PyObject_GC_New(GraphemeBreakIterator, + (PyTypeObject *)GraphemeBreakIteratorType); + if (!gbi) { + return NULL; + } + + Py_ssize_t len = PyUnicode_GET_LENGTH(unistr); + ADJUST_INDICES(start, end, len); + Py_INCREF(unistr); + _Py_InitGraphemeBreak(&gbi->iter, unistr, start, end); + PyObject_GC_Track(gbi); + return (PyObject*)gbi; +} + +/*[clinic input] +unicodedata.block + + chr: int(accept={str}) + / + +Return block assigned to the character chr. +[clinic start generated code]*/ + +static PyObject * +unicodedata_block_impl(PyObject *module, int chr) +/*[clinic end generated code: output=5f8b40c49eaec75a input=0834cf2642d6eaae]*/ +{ + Py_UCS4 c = (Py_UCS4)chr; + int lo = 0, hi = BLOCK_COUNT - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (c < _PyUnicode_Blocks[mid].start) { + hi = mid - 1; + } + else if (c > _PyUnicode_Blocks[mid].end) { + lo = mid + 1; + } + else { + size_t name = _PyUnicode_Blocks[mid].name; + return PyUnicode_FromString(_PyUnicode_BlockNames[name]); + } + } + // Otherwise, return the default value per + // https://www.unicode.org/versions/latest/core-spec/chapter-3/#G64189 + return PyUnicode_FromString("No_Block"); +} + +/*[clinic input] +unicodedata.grapheme_cluster_break + + chr: int(accept={str}) + / + +Returns the Grapheme_Cluster_Break property assigned to the character. +[clinic start generated code]*/ + +static PyObject * +unicodedata_grapheme_cluster_break_impl(PyObject *module, int chr) +/*[clinic end generated code: output=39542e0f63bba36f input=5da75e86435576fd]*/ +{ + Py_UCS4 c = (Py_UCS4)chr; + int index = (int) _getrecord_ex(c)->grapheme_cluster_break; + return PyUnicode_FromString(_PyUnicode_GraphemeBreakNames[index]); +} + +/*[clinic input] +unicodedata.indic_conjunct_break + + chr: int(accept={str}) + / + +Returns the Indic_Conjunct_Break property assigned to the character. +[clinic start generated code]*/ + +static PyObject * +unicodedata_indic_conjunct_break_impl(PyObject *module, int chr) +/*[clinic end generated code: output=673eff2caf797f08 input=5c730f78e469f2e8]*/ +{ + Py_UCS4 c = (Py_UCS4)chr; + int index = (int) _getrecord_ex(c)->incb; + return PyUnicode_FromString(_PyUnicode_IndicConjunctBreakNames[index]); +} + +/*[clinic input] +@permit_long_summary +unicodedata.extended_pictographic + + chr: int(accept={str}) + / + +Returns the Extended_Pictographic property assigned to the character, as boolean. +[clinic start generated code]*/ + +static PyObject * +unicodedata_extended_pictographic_impl(PyObject *module, int chr) +/*[clinic end generated code: output=b6bbb349427370b1 input=250d7bd988997eb3]*/ +{ + Py_UCS4 c = (Py_UCS4)chr; + int index = (int) _getrecord_ex(c)->ext_pict; + return PyBool_FromLong(index); +} + + // List of functions used to define module functions *AND* unicodedata.UCD // methods. For module functions, self is the module. For UCD methods, self // is an UCD instance. The UCD_Check() macro is used to check if self is // an UCD instance. static PyMethodDef unicodedata_functions[] = { + // Module only functions. + UNICODEDATA_BLOCK_METHODDEF + UNICODEDATA_GRAPHEME_CLUSTER_BREAK_METHODDEF + UNICODEDATA_INDIC_CONJUNCT_BREAK_METHODDEF + UNICODEDATA_EXTENDED_PICTOGRAPHIC_METHODDEF + UNICODEDATA_ITER_GRAPHEMES_METHODDEF + UNICODEDATA_ISXIDSTART_METHODDEF + UNICODEDATA_ISXIDCONTINUE_METHODDEF + + // The following definitions are shared between the module + // and the UCD class. +#define DB_methods (unicodedata_functions + 7) + UNICODEDATA_UCD_DECIMAL_METHODDEF UNICODEDATA_UCD_DIGIT_METHODDEF UNICODEDATA_UCD_NUMERIC_METHODDEF @@ -1592,13 +2280,6 @@ static PyMethodDef unicodedata_functions[] = { {NULL, NULL} /* sentinel */ }; -static int -ucd_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static void ucd_dealloc(PyObject *self) { @@ -1610,9 +2291,9 @@ ucd_dealloc(PyObject *self) static PyType_Slot ucd_type_slots[] = { {Py_tp_dealloc, ucd_dealloc}, - {Py_tp_traverse, ucd_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_getattro, PyObject_GenericGetAttr}, - {Py_tp_methods, unicodedata_functions}, + {Py_tp_methods, DB_methods}, {Py_tp_members, DB_members}, {0, 0} }; @@ -1625,6 +2306,7 @@ static PyType_Spec ucd_type_spec = { .slots = ucd_type_slots }; + PyDoc_STRVAR(unicodedata_docstring, "This module provides access to the Unicode Character Database which\n\ defines character properties for all Unicode characters. The data in\n\ @@ -1634,9 +2316,47 @@ this database is based on the UnicodeData.txt file version\n\ The module uses the same names and symbols as defined by the\n\ UnicodeData File Format " UNIDATA_VERSION "."); +static int +unicodedata_traverse(PyObject *module, visitproc visit, void *arg) +{ + unicodedatastate *state = get_unicodedata_state(module); + Py_VISIT(state->SegmentType); + Py_VISIT(state->GraphemeBreakIteratorType); + return 0; +} + +static int +unicodedata_clear(PyObject *module) +{ + unicodedatastate *state = get_unicodedata_state(module); + Py_CLEAR(state->SegmentType); + Py_CLEAR(state->GraphemeBreakIteratorType); + return 0; +} + +static void +unicodedata_free(void *module) +{ + unicodedata_clear((PyObject *)module); +} + static int unicodedata_exec(PyObject *module) { + unicodedatastate *state = get_unicodedata_state(module); + + PyObject *SegmentType = PyType_FromModuleAndSpec(module, &Segment_spec, NULL); + if (SegmentType == NULL) { + return -1; + } + state->SegmentType = SegmentType; + + PyObject *GraphemeBreakIteratorType = PyType_FromModuleAndSpec(module, &GraphemeBreakIterator_spec, NULL); + if (GraphemeBreakIteratorType == NULL) { + return -1; + } + state->GraphemeBreakIteratorType = GraphemeBreakIteratorType; + if (PyModule_AddStringConstant(module, "unidata_version", UNIDATA_VERSION) < 0) { return -1; } @@ -1668,6 +2388,7 @@ unicodedata_exec(PyObject *module) } static PyModuleDef_Slot unicodedata_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, unicodedata_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -1678,9 +2399,12 @@ static struct PyModuleDef unicodedata_module = { PyModuleDef_HEAD_INIT, .m_name = "unicodedata", .m_doc = unicodedata_docstring, - .m_size = 0, + .m_size = sizeof(unicodedatastate), .m_methods = unicodedata_functions, .m_slots = unicodedata_slots, + .m_traverse = unicodedata_traverse, + .m_clear = unicodedata_clear, + .m_free = unicodedata_free, }; PyMODINIT_FUNC diff --git a/Modules/unicodedata_db.h b/Modules/unicodedata_db.h index 610ee6b749cb0c..1c961c5e329341 100644 --- a/Modules/unicodedata_db.h +++ b/Modules/unicodedata_db.h @@ -1,356 +1,403 @@ /* this file was generated by Tools/unicode/makeunicodedata.py 3.3 */ -#define UNIDATA_VERSION "16.0.0" +#define UNIDATA_VERSION "17.0.0" /* a list of unique database records */ const _PyUnicode_DatabaseRecord _PyUnicode_Database_Records[] = { - {0, 0, 0, 0, 0, 0}, - {13, 0, 15, 0, 0, 0}, - {13, 0, 17, 0, 0, 0}, - {13, 0, 16, 0, 0, 0}, - {13, 0, 18, 0, 0, 0}, - {10, 0, 18, 0, 3, 0}, - {26, 0, 19, 0, 3, 0}, - {26, 0, 11, 0, 3, 0}, - {28, 0, 11, 0, 3, 0}, - {22, 0, 19, 1, 3, 0}, - {23, 0, 19, 1, 3, 0}, - {27, 0, 10, 0, 3, 0}, - {26, 0, 13, 0, 3, 0}, - {21, 0, 10, 0, 3, 0}, - {7, 0, 9, 0, 3, 0}, - {27, 0, 19, 1, 3, 0}, - {27, 0, 19, 0, 3, 0}, - {1, 0, 1, 0, 3, 0}, - {29, 0, 19, 0, 3, 0}, - {20, 0, 19, 0, 3, 0}, - {2, 0, 1, 0, 3, 0}, - {10, 0, 13, 0, 0, 136}, - {26, 0, 19, 0, 4, 0}, - {28, 0, 11, 0, 4, 0}, - {30, 0, 19, 0, 3, 0}, - {29, 0, 19, 0, 4, 136}, - {30, 0, 19, 0, 0, 0}, - {19, 0, 1, 0, 4, 136}, - {24, 0, 19, 1, 0, 0}, - {14, 0, 15, 0, 4, 0}, - {30, 0, 19, 0, 4, 0}, - {29, 0, 19, 0, 3, 136}, - {30, 0, 11, 0, 4, 0}, - {27, 0, 11, 0, 4, 0}, - {9, 0, 9, 0, 4, 136}, - {2, 0, 1, 0, 0, 136}, - {25, 0, 19, 1, 0, 0}, - {9, 0, 19, 0, 4, 136}, - {1, 0, 1, 0, 0, 10}, - {1, 0, 1, 0, 4, 0}, - {27, 0, 19, 0, 4, 0}, - {2, 0, 1, 0, 4, 0}, - {2, 0, 1, 0, 4, 10}, - {2, 0, 1, 0, 0, 10}, - {1, 0, 1, 0, 0, 0}, - {1, 0, 1, 0, 4, 136}, - {2, 0, 1, 0, 4, 136}, - {2, 0, 1, 0, 0, 0}, - {19, 0, 1, 0, 0, 0}, - {1, 0, 1, 0, 0, 136}, - {3, 0, 1, 0, 0, 136}, - {18, 0, 1, 0, 0, 136}, - {18, 0, 19, 0, 0, 0}, - {18, 0, 1, 0, 0, 0}, - {29, 0, 19, 0, 0, 0}, - {29, 0, 19, 0, 4, 0}, - {18, 0, 19, 0, 4, 0}, - {18, 0, 1, 0, 4, 0}, - {29, 0, 19, 0, 0, 136}, - {4, 230, 14, 0, 4, 80}, - {4, 230, 14, 0, 4, 0}, - {4, 232, 14, 0, 4, 0}, - {4, 220, 14, 0, 4, 0}, - {4, 216, 14, 0, 4, 80}, - {4, 202, 14, 0, 4, 0}, - {4, 220, 14, 0, 4, 80}, - {4, 202, 14, 0, 4, 80}, - {4, 1, 14, 0, 4, 0}, - {4, 1, 14, 0, 4, 80}, - {4, 230, 14, 0, 4, 170}, - {4, 240, 14, 0, 4, 80}, - {4, 0, 14, 0, 4, 0}, - {4, 233, 14, 0, 4, 0}, - {4, 234, 14, 0, 4, 0}, - {18, 0, 19, 0, 0, 170}, - {26, 0, 19, 0, 0, 170}, - {29, 0, 19, 0, 0, 138}, - {1, 0, 1, 0, 0, 138}, - {27, 0, 19, 0, 0, 0}, - {1, 0, 1, 0, 4, 10}, - {30, 0, 1, 0, 0, 0}, - {4, 230, 14, 0, 0, 0}, - {6, 0, 14, 0, 0, 0}, - {26, 0, 1, 0, 0, 0}, - {21, 0, 19, 0, 0, 0}, - {28, 0, 11, 0, 0, 0}, - {4, 220, 14, 0, 0, 0}, - {4, 222, 14, 0, 0, 0}, - {4, 228, 14, 0, 0, 0}, - {4, 10, 14, 0, 0, 0}, - {4, 11, 14, 0, 0, 0}, - {4, 12, 14, 0, 0, 0}, - {4, 13, 14, 0, 0, 0}, - {4, 14, 14, 0, 0, 0}, - {4, 15, 14, 0, 0, 0}, - {4, 16, 14, 0, 0, 0}, - {4, 17, 14, 0, 0, 0}, - {4, 18, 14, 0, 0, 0}, - {4, 19, 14, 0, 0, 0}, - {4, 20, 14, 0, 0, 0}, - {4, 21, 14, 0, 0, 0}, - {4, 22, 14, 0, 0, 0}, - {21, 0, 4, 0, 0, 0}, - {4, 23, 14, 0, 0, 0}, - {26, 0, 4, 0, 0, 0}, - {4, 24, 14, 0, 0, 0}, - {4, 25, 14, 0, 0, 0}, - {19, 0, 4, 0, 0, 0}, - {14, 0, 12, 0, 0, 0}, - {27, 0, 5, 0, 0, 0}, - {26, 0, 11, 0, 0, 0}, - {28, 0, 5, 0, 0, 0}, - {26, 0, 13, 0, 0, 0}, - {26, 0, 5, 0, 0, 0}, - {4, 30, 14, 0, 0, 0}, - {4, 31, 14, 0, 0, 0}, - {4, 32, 14, 0, 0, 0}, - {14, 0, 5, 0, 0, 0}, - {19, 0, 5, 0, 0, 0}, - {19, 0, 5, 0, 0, 10}, - {18, 0, 5, 0, 0, 0}, - {4, 27, 14, 0, 0, 0}, - {4, 28, 14, 0, 0, 0}, - {4, 29, 14, 0, 0, 0}, - {4, 33, 14, 0, 0, 0}, - {4, 34, 14, 0, 0, 0}, - {4, 230, 14, 0, 0, 80}, - {4, 220, 14, 0, 0, 80}, - {7, 0, 12, 0, 0, 0}, - {26, 0, 12, 0, 0, 0}, - {4, 35, 14, 0, 0, 0}, - {19, 0, 5, 0, 0, 136}, - {7, 0, 9, 0, 0, 0}, - {30, 0, 5, 0, 0, 0}, - {4, 36, 14, 0, 0, 0}, - {4, 0, 14, 0, 0, 0}, - {7, 0, 4, 0, 0, 0}, - {18, 0, 4, 0, 0, 0}, - {26, 0, 19, 0, 0, 0}, - {28, 0, 4, 0, 0, 0}, - {29, 0, 5, 0, 0, 0}, - {5, 0, 1, 0, 0, 0}, - {19, 0, 1, 0, 0, 10}, - {4, 7, 14, 0, 0, 80}, - {4, 9, 14, 0, 0, 0}, - {19, 0, 1, 0, 0, 170}, - {7, 0, 1, 0, 0, 0}, - {4, 7, 14, 0, 0, 0}, - {5, 0, 1, 0, 0, 80}, - {5, 0, 1, 0, 0, 10}, - {9, 0, 1, 0, 0, 0}, - {4, 0, 14, 0, 0, 80}, - {4, 0, 14, 0, 0, 10}, - {4, 84, 14, 0, 0, 0}, - {4, 91, 14, 0, 0, 80}, - {9, 0, 19, 0, 0, 0}, - {4, 0, 1, 0, 0, 0}, - {4, 9, 14, 0, 0, 80}, - {19, 0, 1, 0, 0, 136}, - {4, 103, 14, 0, 0, 0}, - {4, 107, 14, 0, 0, 0}, - {4, 118, 14, 0, 0, 0}, - {4, 122, 14, 0, 0, 0}, - {26, 0, 1, 0, 0, 136}, - {4, 216, 14, 0, 0, 0}, - {22, 0, 19, 1, 0, 0}, - {23, 0, 19, 1, 0, 0}, - {4, 129, 14, 0, 0, 0}, - {4, 130, 14, 0, 0, 0}, - {4, 0, 14, 0, 0, 170}, - {4, 132, 14, 0, 0, 0}, - {4, 0, 14, 0, 0, 136}, - {19, 0, 1, 0, 2, 0}, - {19, 0, 1, 0, 0, 80}, - {10, 0, 18, 0, 0, 0}, - {8, 0, 1, 0, 0, 0}, - {5, 9, 1, 0, 0, 0}, - {14, 0, 15, 0, 0, 0}, - {4, 1, 14, 0, 0, 0}, - {4, 234, 14, 0, 0, 0}, - {4, 214, 14, 0, 0, 0}, - {4, 202, 14, 0, 0, 0}, - {4, 232, 14, 0, 0, 0}, - {4, 218, 14, 0, 0, 0}, - {4, 233, 14, 0, 0, 0}, - {2, 0, 1, 0, 0, 138}, - {2, 0, 1, 0, 0, 170}, - {3, 0, 1, 0, 0, 10}, - {1, 0, 1, 0, 0, 170}, - {29, 0, 19, 0, 0, 170}, - {10, 0, 18, 0, 0, 170}, - {10, 0, 18, 0, 0, 136}, - {14, 0, 1, 0, 0, 0}, - {14, 0, 4, 0, 0, 0}, - {21, 0, 19, 0, 4, 0}, - {21, 0, 19, 0, 0, 136}, - {26, 0, 19, 0, 0, 136}, - {24, 0, 19, 0, 4, 0}, - {25, 0, 19, 0, 4, 0}, - {22, 0, 19, 0, 0, 0}, - {24, 0, 19, 0, 0, 0}, - {26, 0, 19, 0, 4, 136}, - {11, 0, 18, 0, 0, 0}, - {12, 0, 16, 0, 0, 0}, - {14, 0, 2, 0, 0, 0}, - {14, 0, 6, 0, 0, 0}, - {14, 0, 8, 0, 0, 0}, - {14, 0, 3, 0, 0, 0}, - {14, 0, 7, 0, 0, 0}, - {26, 0, 11, 0, 4, 0}, - {26, 0, 11, 0, 4, 136}, - {26, 0, 11, 0, 0, 136}, - {20, 0, 19, 0, 0, 0}, - {27, 0, 13, 0, 0, 0}, - {14, 0, 20, 0, 0, 0}, - {14, 0, 21, 0, 0, 0}, - {14, 0, 22, 0, 0, 0}, - {14, 0, 23, 0, 0, 0}, - {9, 0, 9, 0, 0, 136}, - {27, 0, 10, 0, 0, 136}, - {27, 0, 19, 0, 0, 136}, - {22, 0, 19, 1, 0, 136}, - {23, 0, 19, 1, 0, 136}, - {18, 0, 1, 0, 4, 136}, - {28, 0, 11, 0, 0, 136}, - {28, 0, 11, 0, 1, 0}, - {30, 0, 19, 0, 0, 136}, - {30, 0, 19, 0, 4, 136}, - {1, 0, 1, 0, 4, 170}, - {30, 0, 11, 0, 0, 0}, - {27, 0, 19, 1, 0, 136}, - {9, 0, 19, 0, 0, 136}, - {8, 0, 1, 0, 4, 136}, - {8, 0, 1, 0, 0, 136}, - {27, 0, 19, 0, 0, 10}, - {30, 0, 19, 0, 0, 10}, - {27, 0, 19, 1, 0, 0}, - {27, 0, 19, 1, 4, 0}, - {27, 0, 19, 1, 0, 10}, - {27, 0, 10, 0, 0, 0}, - {27, 0, 11, 0, 0, 0}, - {27, 0, 19, 1, 4, 136}, - {27, 0, 19, 1, 4, 10}, - {30, 0, 19, 0, 2, 0}, - {22, 0, 19, 1, 2, 170}, - {23, 0, 19, 1, 2, 170}, - {30, 0, 1, 0, 4, 136}, - {9, 0, 19, 0, 4, 0}, - {27, 0, 19, 0, 2, 0}, - {27, 0, 19, 1, 0, 170}, - {30, 0, 19, 1, 0, 0}, - {30, 0, 19, 0, 2, 136}, - {10, 0, 18, 0, 5, 136}, - {26, 0, 19, 0, 2, 0}, - {18, 0, 1, 0, 2, 0}, - {8, 0, 1, 0, 2, 0}, - {22, 0, 19, 1, 2, 0}, - {23, 0, 19, 1, 2, 0}, - {21, 0, 19, 0, 2, 0}, - {22, 0, 19, 0, 2, 0}, - {23, 0, 19, 0, 2, 0}, - {4, 218, 14, 0, 2, 0}, - {4, 228, 14, 0, 2, 0}, - {4, 232, 14, 0, 2, 0}, - {4, 222, 14, 0, 2, 0}, - {5, 224, 1, 0, 2, 0}, - {8, 0, 1, 0, 2, 136}, - {19, 0, 1, 0, 2, 10}, - {4, 8, 14, 0, 2, 80}, - {29, 0, 19, 0, 2, 136}, - {18, 0, 1, 0, 2, 10}, - {19, 0, 1, 0, 2, 136}, - {30, 0, 1, 0, 2, 0}, - {9, 0, 1, 0, 2, 136}, - {30, 0, 1, 0, 2, 136}, - {9, 0, 1, 0, 4, 0}, - {9, 0, 19, 0, 2, 136}, - {29, 0, 1, 0, 0, 0}, - {15, 0, 1, 0, 0, 0}, - {16, 0, 1, 0, 4, 0}, - {19, 0, 1, 0, 2, 170}, - {0, 0, 0, 0, 2, 0}, - {19, 0, 4, 0, 0, 170}, - {4, 26, 14, 0, 0, 0}, - {19, 0, 4, 0, 0, 136}, - {23, 0, 19, 0, 0, 0}, - {28, 0, 5, 0, 0, 136}, - {26, 0, 19, 0, 2, 136}, - {22, 0, 19, 0, 2, 136}, - {23, 0, 19, 0, 2, 136}, - {21, 0, 19, 0, 2, 136}, - {20, 0, 19, 0, 2, 136}, - {26, 0, 13, 0, 2, 136}, - {22, 0, 19, 1, 2, 136}, - {23, 0, 19, 1, 2, 136}, - {26, 0, 11, 0, 2, 136}, - {27, 0, 10, 0, 2, 136}, - {21, 0, 10, 0, 2, 136}, - {27, 0, 19, 1, 2, 136}, - {27, 0, 19, 0, 2, 136}, - {28, 0, 11, 0, 2, 136}, - {26, 0, 19, 0, 5, 136}, - {26, 0, 11, 0, 5, 136}, - {28, 0, 11, 0, 5, 136}, - {22, 0, 19, 1, 5, 136}, - {23, 0, 19, 1, 5, 136}, - {27, 0, 10, 0, 5, 136}, - {26, 0, 13, 0, 5, 136}, - {21, 0, 10, 0, 5, 136}, - {7, 0, 9, 0, 5, 136}, - {27, 0, 19, 1, 5, 136}, - {27, 0, 19, 0, 5, 136}, - {1, 0, 1, 0, 5, 136}, - {29, 0, 19, 0, 5, 136}, - {20, 0, 19, 0, 5, 136}, - {2, 0, 1, 0, 5, 136}, - {26, 0, 19, 0, 1, 136}, - {22, 0, 19, 1, 1, 136}, - {23, 0, 19, 1, 1, 136}, - {19, 0, 1, 0, 1, 136}, - {18, 0, 1, 0, 1, 136}, - {30, 0, 19, 0, 5, 136}, - {30, 0, 19, 0, 1, 136}, - {27, 0, 19, 0, 1, 136}, - {14, 0, 19, 0, 0, 0}, - {8, 0, 19, 0, 0, 0}, - {9, 0, 9, 0, 0, 0}, - {9, 0, 4, 0, 0, 0}, - {30, 0, 4, 0, 0, 0}, - {1, 0, 4, 0, 0, 0}, - {2, 0, 4, 0, 0, 0}, - {27, 0, 4, 0, 0, 0}, - {9, 0, 12, 0, 0, 0}, - {9, 0, 5, 0, 0, 0}, - {5, 0, 1, 0, 0, 90}, - {4, 9, 1, 0, 0, 0}, - {4, 0, 14, 0, 0, 90}, - {19, 0, 1, 0, 0, 90}, - {4, 0, 14, 0, 2, 0}, - {5, 6, 1, 0, 2, 0}, - {30, 0, 1, 0, 0, 136}, - {7, 0, 9, 0, 0, 136}, - {30, 0, 1, 0, 0, 170}, - {5, 216, 1, 0, 0, 0}, - {5, 226, 1, 0, 0, 0}, - {9, 0, 1, 0, 2, 0}, - {30, 0, 1, 0, 4, 0}, - {29, 0, 19, 0, 2, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {13, 0, 15, 0, 0, 0, 4, 0, 0}, + {13, 0, 17, 0, 0, 0, 4, 0, 0}, + {13, 0, 16, 0, 0, 0, 3, 0, 0}, + {13, 0, 18, 0, 0, 0, 4, 0, 0}, + {13, 0, 16, 0, 0, 0, 2, 0, 0}, + {13, 0, 16, 0, 0, 0, 4, 0, 0}, + {10, 0, 18, 0, 3, 0, 0, 0, 0}, + {26, 0, 19, 0, 3, 0, 0, 0, 0}, + {26, 0, 11, 0, 3, 0, 0, 0, 0}, + {28, 0, 11, 0, 3, 0, 0, 0, 0}, + {22, 0, 19, 1, 3, 0, 0, 0, 0}, + {23, 0, 19, 1, 3, 0, 0, 0, 0}, + {27, 0, 10, 0, 3, 0, 0, 0, 0}, + {26, 0, 13, 0, 3, 0, 0, 0, 0}, + {21, 0, 10, 0, 3, 0, 0, 0, 0}, + {7, 0, 9, 0, 3, 0, 0, 0, 0}, + {27, 0, 19, 1, 3, 0, 0, 0, 0}, + {27, 0, 19, 0, 3, 0, 0, 0, 0}, + {1, 0, 1, 0, 3, 0, 0, 0, 0}, + {29, 0, 19, 0, 3, 0, 0, 0, 0}, + {20, 0, 19, 0, 3, 0, 0, 0, 0}, + {2, 0, 1, 0, 3, 0, 0, 0, 0}, + {10, 0, 13, 0, 0, 136, 0, 0, 0}, + {26, 0, 19, 0, 4, 0, 0, 0, 0}, + {28, 0, 11, 0, 4, 0, 0, 0, 0}, + {30, 0, 19, 0, 3, 0, 0, 0, 0}, + {29, 0, 19, 0, 4, 136, 0, 0, 0}, + {30, 0, 19, 0, 0, 0, 0, 0, 1}, + {19, 0, 1, 0, 4, 136, 0, 0, 0}, + {24, 0, 19, 1, 0, 0, 0, 0, 0}, + {14, 0, 15, 0, 4, 0, 4, 0, 0}, + {30, 0, 19, 0, 4, 0, 0, 0, 1}, + {29, 0, 19, 0, 3, 136, 0, 0, 0}, + {30, 0, 11, 0, 4, 0, 0, 0, 0}, + {27, 0, 11, 0, 4, 0, 0, 0, 0}, + {9, 0, 9, 0, 4, 136, 0, 0, 0}, + {2, 0, 1, 0, 0, 136, 0, 0, 0}, + {25, 0, 19, 1, 0, 0, 0, 0, 0}, + {9, 0, 19, 0, 4, 136, 0, 0, 0}, + {1, 0, 1, 0, 0, 10, 0, 0, 0}, + {1, 0, 1, 0, 4, 0, 0, 0, 0}, + {27, 0, 19, 0, 4, 0, 0, 0, 0}, + {2, 0, 1, 0, 4, 0, 0, 0, 0}, + {2, 0, 1, 0, 4, 10, 0, 0, 0}, + {2, 0, 1, 0, 0, 10, 0, 0, 0}, + {1, 0, 1, 0, 0, 0, 0, 0, 0}, + {1, 0, 1, 0, 4, 136, 0, 0, 0}, + {2, 0, 1, 0, 4, 136, 0, 0, 0}, + {2, 0, 1, 0, 0, 0, 0, 0, 0}, + {19, 0, 1, 0, 0, 0, 0, 0, 0}, + {1, 0, 1, 0, 0, 136, 0, 0, 0}, + {3, 0, 1, 0, 0, 136, 0, 0, 0}, + {18, 0, 1, 0, 0, 136, 0, 0, 0}, + {18, 0, 19, 0, 0, 0, 0, 0, 0}, + {18, 0, 1, 0, 0, 0, 0, 0, 0}, + {29, 0, 19, 0, 0, 0, 0, 0, 0}, + {29, 0, 19, 0, 4, 0, 0, 0, 0}, + {18, 0, 19, 0, 4, 0, 0, 0, 0}, + {18, 0, 1, 0, 4, 0, 0, 0, 0}, + {29, 0, 19, 0, 0, 136, 0, 0, 0}, + {4, 230, 14, 0, 4, 80, 5, 3, 0}, + {4, 230, 14, 0, 4, 0, 5, 3, 0}, + {4, 232, 14, 0, 4, 0, 5, 3, 0}, + {4, 220, 14, 0, 4, 0, 5, 3, 0}, + {4, 216, 14, 0, 4, 80, 5, 3, 0}, + {4, 202, 14, 0, 4, 0, 5, 3, 0}, + {4, 220, 14, 0, 4, 80, 5, 3, 0}, + {4, 202, 14, 0, 4, 80, 5, 3, 0}, + {4, 1, 14, 0, 4, 0, 5, 3, 0}, + {4, 1, 14, 0, 4, 80, 5, 3, 0}, + {4, 230, 14, 0, 4, 170, 5, 3, 0}, + {4, 240, 14, 0, 4, 80, 5, 3, 0}, + {4, 0, 14, 0, 4, 0, 5, 3, 0}, + {4, 233, 14, 0, 4, 0, 5, 3, 0}, + {4, 234, 14, 0, 4, 0, 5, 3, 0}, + {18, 0, 19, 0, 0, 170, 0, 0, 0}, + {0, 0, 1, 0, 0, 0, 0, 0, 0}, + {26, 0, 19, 0, 0, 170, 0, 0, 0}, + {29, 0, 19, 0, 0, 138, 0, 0, 0}, + {1, 0, 1, 0, 0, 138, 0, 0, 0}, + {27, 0, 19, 0, 0, 0, 0, 0, 0}, + {1, 0, 1, 0, 4, 10, 0, 0, 0}, + {30, 0, 1, 0, 0, 0, 0, 0, 0}, + {4, 230, 14, 0, 0, 0, 5, 3, 0}, + {6, 0, 14, 0, 0, 0, 5, 3, 0}, + {26, 0, 1, 0, 0, 0, 0, 0, 0}, + {21, 0, 19, 0, 0, 0, 0, 0, 0}, + {30, 0, 19, 0, 0, 0, 0, 0, 0}, + {28, 0, 11, 0, 0, 0, 0, 0, 0}, + {0, 0, 4, 0, 0, 0, 0, 0, 0}, + {4, 220, 14, 0, 0, 0, 5, 3, 0}, + {4, 222, 14, 0, 0, 0, 5, 3, 0}, + {4, 228, 14, 0, 0, 0, 5, 3, 0}, + {4, 10, 14, 0, 0, 0, 5, 3, 0}, + {4, 11, 14, 0, 0, 0, 5, 3, 0}, + {4, 12, 14, 0, 0, 0, 5, 3, 0}, + {4, 13, 14, 0, 0, 0, 5, 3, 0}, + {4, 14, 14, 0, 0, 0, 5, 3, 0}, + {4, 15, 14, 0, 0, 0, 5, 3, 0}, + {4, 16, 14, 0, 0, 0, 5, 3, 0}, + {4, 17, 14, 0, 0, 0, 5, 3, 0}, + {4, 18, 14, 0, 0, 0, 5, 3, 0}, + {4, 19, 14, 0, 0, 0, 5, 3, 0}, + {4, 20, 14, 0, 0, 0, 5, 3, 0}, + {4, 21, 14, 0, 0, 0, 5, 3, 0}, + {4, 22, 14, 0, 0, 0, 5, 3, 0}, + {21, 0, 4, 0, 0, 0, 0, 0, 0}, + {4, 23, 14, 0, 0, 0, 5, 3, 0}, + {26, 0, 4, 0, 0, 0, 0, 0, 0}, + {4, 24, 14, 0, 0, 0, 5, 3, 0}, + {4, 25, 14, 0, 0, 0, 5, 3, 0}, + {19, 0, 4, 0, 0, 0, 0, 0, 0}, + {14, 0, 12, 0, 0, 0, 1, 0, 0}, + {27, 0, 5, 0, 0, 0, 0, 0, 0}, + {26, 0, 11, 0, 0, 0, 0, 0, 0}, + {28, 0, 5, 0, 0, 0, 0, 0, 0}, + {26, 0, 13, 0, 0, 0, 0, 0, 0}, + {26, 0, 5, 0, 0, 0, 0, 0, 0}, + {4, 30, 14, 0, 0, 0, 5, 3, 0}, + {4, 31, 14, 0, 0, 0, 5, 3, 0}, + {4, 32, 14, 0, 0, 0, 5, 3, 0}, + {14, 0, 5, 0, 0, 0, 4, 0, 0}, + {19, 0, 5, 0, 0, 0, 0, 0, 0}, + {19, 0, 5, 0, 0, 10, 0, 0, 0}, + {18, 0, 5, 0, 0, 0, 0, 0, 0}, + {4, 27, 14, 0, 0, 0, 5, 3, 0}, + {4, 28, 14, 0, 0, 0, 5, 3, 0}, + {4, 29, 14, 0, 0, 0, 5, 3, 0}, + {4, 33, 14, 0, 0, 0, 5, 3, 0}, + {4, 34, 14, 0, 0, 0, 5, 3, 0}, + {4, 230, 14, 0, 0, 80, 5, 3, 0}, + {4, 220, 14, 0, 0, 80, 5, 3, 0}, + {7, 0, 12, 0, 0, 0, 0, 0, 0}, + {26, 0, 12, 0, 0, 0, 0, 0, 0}, + {4, 35, 14, 0, 0, 0, 5, 3, 0}, + {19, 0, 5, 0, 0, 136, 0, 0, 0}, + {7, 0, 9, 0, 0, 0, 0, 0, 0}, + {30, 0, 5, 0, 0, 0, 0, 0, 0}, + {0, 0, 5, 0, 0, 0, 0, 0, 0}, + {14, 0, 5, 0, 0, 0, 1, 0, 0}, + {4, 36, 14, 0, 0, 0, 5, 3, 0}, + {4, 0, 14, 0, 0, 0, 5, 3, 0}, + {7, 0, 4, 0, 0, 0, 0, 0, 0}, + {18, 0, 4, 0, 0, 0, 0, 0, 0}, + {26, 0, 19, 0, 0, 0, 0, 0, 0}, + {28, 0, 4, 0, 0, 0, 0, 0, 0}, + {29, 0, 5, 0, 0, 0, 0, 0, 0}, + {5, 0, 1, 0, 0, 0, 7, 0, 0}, + {19, 0, 1, 0, 0, 0, 0, 2, 0}, + {19, 0, 1, 0, 0, 10, 0, 2, 0}, + {4, 7, 14, 0, 0, 80, 5, 3, 0}, + {4, 9, 14, 0, 0, 0, 5, 1, 0}, + {19, 0, 1, 0, 0, 170, 0, 2, 0}, + {7, 0, 1, 0, 0, 0, 0, 0, 0}, + {4, 7, 14, 0, 0, 0, 5, 3, 0}, + {5, 0, 1, 0, 0, 80, 5, 3, 0}, + {5, 0, 1, 0, 0, 10, 7, 0, 0}, + {9, 0, 1, 0, 0, 0, 0, 0, 0}, + {19, 0, 1, 0, 0, 170, 0, 0, 0}, + {4, 9, 14, 0, 0, 0, 5, 3, 0}, + {4, 0, 14, 0, 0, 80, 5, 3, 0}, + {19, 0, 1, 0, 0, 10, 0, 0, 0}, + {4, 0, 14, 0, 0, 10, 5, 3, 0}, + {4, 84, 14, 0, 0, 0, 5, 3, 0}, + {4, 91, 14, 0, 0, 80, 5, 3, 0}, + {9, 0, 19, 0, 0, 0, 0, 0, 0}, + {4, 0, 1, 0, 0, 0, 5, 3, 0}, + {5, 0, 1, 0, 0, 10, 5, 3, 0}, + {19, 0, 1, 0, 0, 0, 1, 0, 0}, + {4, 9, 14, 0, 0, 80, 5, 3, 0}, + {19, 0, 1, 0, 0, 136, 7, 0, 0}, + {4, 103, 14, 0, 0, 0, 5, 3, 0}, + {4, 107, 14, 0, 0, 0, 5, 3, 0}, + {4, 118, 14, 0, 0, 0, 5, 3, 0}, + {4, 122, 14, 0, 0, 0, 5, 3, 0}, + {19, 0, 1, 0, 0, 136, 0, 0, 0}, + {26, 0, 1, 0, 0, 136, 0, 0, 0}, + {4, 216, 14, 0, 0, 0, 5, 3, 0}, + {22, 0, 19, 1, 0, 0, 0, 0, 0}, + {23, 0, 19, 1, 0, 0, 0, 0, 0}, + {4, 129, 14, 0, 0, 0, 5, 3, 0}, + {4, 130, 14, 0, 0, 0, 5, 3, 0}, + {4, 0, 14, 0, 0, 170, 5, 3, 0}, + {4, 132, 14, 0, 0, 0, 5, 3, 0}, + {4, 0, 14, 0, 0, 136, 5, 3, 0}, + {5, 0, 1, 0, 0, 0, 0, 0, 0}, + {19, 0, 1, 0, 2, 0, 8, 0, 0}, + {19, 0, 1, 0, 0, 0, 9, 0, 0}, + {19, 0, 1, 0, 0, 80, 9, 0, 0}, + {19, 0, 1, 0, 0, 80, 10, 0, 0}, + {19, 0, 1, 0, 0, 0, 10, 0, 0}, + {10, 0, 18, 0, 0, 0, 0, 0, 0}, + {8, 0, 1, 0, 0, 0, 0, 0, 0}, + {5, 9, 1, 0, 0, 0, 5, 3, 0}, + {14, 0, 15, 0, 0, 0, 4, 0, 0}, + {4, 234, 14, 0, 0, 0, 5, 3, 0}, + {5, 9, 1, 0, 0, 0, 5, 1, 0}, + {4, 1, 14, 0, 0, 0, 5, 3, 0}, + {4, 214, 14, 0, 0, 0, 5, 3, 0}, + {4, 202, 14, 0, 0, 0, 5, 3, 0}, + {4, 232, 14, 0, 0, 0, 5, 3, 0}, + {4, 218, 14, 0, 0, 0, 5, 3, 0}, + {4, 233, 14, 0, 0, 0, 5, 3, 0}, + {2, 0, 1, 0, 0, 138, 0, 0, 0}, + {2, 0, 1, 0, 0, 170, 0, 0, 0}, + {3, 0, 1, 0, 0, 10, 0, 0, 0}, + {1, 0, 1, 0, 0, 170, 0, 0, 0}, + {29, 0, 19, 0, 0, 170, 0, 0, 0}, + {10, 0, 18, 0, 0, 170, 0, 0, 0}, + {10, 0, 18, 0, 0, 136, 0, 0, 0}, + {14, 0, 15, 0, 0, 0, 5, 0, 0}, + {14, 0, 15, 0, 0, 0, 13, 3, 0}, + {14, 0, 1, 0, 0, 0, 4, 0, 0}, + {14, 0, 4, 0, 0, 0, 4, 0, 0}, + {21, 0, 19, 0, 4, 0, 0, 0, 0}, + {21, 0, 19, 0, 0, 136, 0, 0, 0}, + {26, 0, 19, 0, 0, 136, 0, 0, 0}, + {24, 0, 19, 0, 4, 0, 0, 0, 0}, + {25, 0, 19, 0, 4, 0, 0, 0, 0}, + {22, 0, 19, 0, 0, 0, 0, 0, 0}, + {24, 0, 19, 0, 0, 0, 0, 0, 0}, + {26, 0, 19, 0, 4, 136, 0, 0, 0}, + {11, 0, 18, 0, 0, 0, 4, 0, 0}, + {12, 0, 16, 0, 0, 0, 4, 0, 0}, + {14, 0, 2, 0, 0, 0, 4, 0, 0}, + {14, 0, 6, 0, 0, 0, 4, 0, 0}, + {14, 0, 8, 0, 0, 0, 4, 0, 0}, + {14, 0, 3, 0, 0, 0, 4, 0, 0}, + {14, 0, 7, 0, 0, 0, 4, 0, 0}, + {26, 0, 11, 0, 4, 0, 0, 0, 0}, + {26, 0, 11, 0, 4, 136, 0, 0, 0}, + {26, 0, 11, 0, 0, 136, 0, 0, 0}, + {26, 0, 19, 0, 0, 136, 0, 0, 1}, + {20, 0, 19, 0, 0, 0, 0, 0, 0}, + {27, 0, 13, 0, 0, 0, 0, 0, 0}, + {0, 0, 15, 0, 0, 0, 4, 0, 0}, + {14, 0, 20, 0, 0, 0, 4, 0, 0}, + {14, 0, 21, 0, 0, 0, 4, 0, 0}, + {14, 0, 22, 0, 0, 0, 4, 0, 0}, + {14, 0, 23, 0, 0, 0, 4, 0, 0}, + {9, 0, 9, 0, 0, 136, 0, 0, 0}, + {27, 0, 10, 0, 0, 136, 0, 0, 0}, + {27, 0, 19, 0, 0, 136, 0, 0, 0}, + {22, 0, 19, 1, 0, 136, 0, 0, 0}, + {23, 0, 19, 1, 0, 136, 0, 0, 0}, + {18, 0, 1, 0, 4, 136, 0, 0, 0}, + {28, 0, 11, 0, 0, 136, 0, 0, 0}, + {28, 0, 11, 0, 1, 0, 0, 0, 0}, + {0, 0, 11, 0, 0, 0, 0, 0, 0}, + {30, 0, 19, 0, 0, 136, 0, 0, 0}, + {30, 0, 19, 0, 4, 136, 0, 0, 0}, + {30, 0, 19, 0, 4, 136, 0, 0, 1}, + {1, 0, 1, 0, 4, 170, 0, 0, 0}, + {30, 0, 11, 0, 0, 0, 0, 0, 0}, + {2, 0, 1, 0, 0, 136, 0, 0, 1}, + {27, 0, 19, 1, 0, 136, 0, 0, 0}, + {9, 0, 19, 0, 0, 136, 0, 0, 0}, + {8, 0, 1, 0, 4, 136, 0, 0, 0}, + {8, 0, 1, 0, 0, 136, 0, 0, 0}, + {27, 0, 19, 0, 4, 0, 0, 0, 1}, + {27, 0, 19, 0, 0, 10, 0, 0, 0}, + {30, 0, 19, 0, 4, 0, 0, 0, 0}, + {30, 0, 19, 0, 0, 10, 0, 0, 0}, + {27, 0, 19, 1, 0, 0, 0, 0, 0}, + {27, 0, 19, 1, 4, 0, 0, 0, 0}, + {27, 0, 19, 1, 0, 10, 0, 0, 0}, + {27, 0, 10, 0, 0, 0, 0, 0, 0}, + {27, 0, 11, 0, 0, 0, 0, 0, 0}, + {27, 0, 19, 1, 4, 136, 0, 0, 0}, + {27, 0, 19, 1, 4, 10, 0, 0, 0}, + {30, 0, 19, 0, 2, 0, 0, 0, 1}, + {22, 0, 19, 1, 2, 170, 0, 0, 0}, + {23, 0, 19, 1, 2, 170, 0, 0, 0}, + {30, 0, 1, 0, 4, 136, 0, 0, 0}, + {30, 0, 1, 0, 4, 136, 0, 0, 1}, + {9, 0, 19, 0, 4, 0, 0, 0, 0}, + {27, 0, 19, 0, 0, 0, 0, 0, 1}, + {27, 0, 19, 0, 2, 0, 0, 0, 1}, + {30, 0, 19, 0, 2, 0, 0, 0, 0}, + {27, 0, 19, 1, 0, 170, 0, 0, 0}, + {30, 0, 19, 1, 0, 0, 0, 0, 0}, + {30, 0, 19, 0, 2, 136, 0, 0, 0}, + {10, 0, 18, 0, 5, 136, 0, 0, 0}, + {26, 0, 19, 0, 2, 0, 0, 0, 0}, + {18, 0, 1, 0, 2, 0, 0, 0, 0}, + {19, 0, 1, 0, 2, 0, 0, 0, 0}, + {8, 0, 1, 0, 2, 0, 0, 0, 0}, + {22, 0, 19, 1, 2, 0, 0, 0, 0}, + {23, 0, 19, 1, 2, 0, 0, 0, 0}, + {21, 0, 19, 0, 2, 0, 0, 0, 0}, + {22, 0, 19, 0, 2, 0, 0, 0, 0}, + {23, 0, 19, 0, 2, 0, 0, 0, 0}, + {4, 218, 14, 0, 2, 0, 5, 3, 0}, + {4, 228, 14, 0, 2, 0, 5, 3, 0}, + {4, 232, 14, 0, 2, 0, 5, 3, 0}, + {4, 222, 14, 0, 2, 0, 5, 3, 0}, + {5, 224, 1, 0, 2, 0, 5, 3, 0}, + {21, 0, 19, 0, 2, 0, 0, 0, 1}, + {8, 0, 1, 0, 2, 136, 0, 0, 0}, + {26, 0, 19, 0, 2, 0, 0, 0, 1}, + {19, 0, 1, 0, 2, 10, 0, 0, 0}, + {4, 8, 14, 0, 2, 80, 5, 3, 0}, + {29, 0, 19, 0, 2, 136, 0, 0, 0}, + {18, 0, 1, 0, 2, 10, 0, 0, 0}, + {19, 0, 1, 0, 2, 136, 0, 0, 0}, + {30, 0, 1, 0, 2, 0, 0, 0, 0}, + {9, 0, 1, 0, 2, 136, 0, 0, 0}, + {30, 0, 1, 0, 2, 136, 0, 0, 0}, + {9, 0, 1, 0, 4, 0, 0, 0, 0}, + {9, 0, 19, 0, 2, 136, 0, 0, 0}, + {30, 0, 1, 0, 2, 136, 0, 0, 1}, + {29, 0, 1, 0, 0, 0, 0, 0, 0}, + {19, 0, 1, 0, 2, 10, 11, 0, 0}, + {19, 0, 1, 0, 2, 10, 12, 0, 0}, + {15, 0, 1, 0, 0, 0, 0, 0, 0}, + {16, 0, 1, 0, 4, 0, 0, 0, 0}, + {19, 0, 1, 0, 2, 170, 0, 0, 0}, + {0, 0, 1, 0, 2, 0, 0, 0, 0}, + {19, 0, 4, 0, 0, 170, 0, 0, 0}, + {4, 26, 14, 0, 0, 0, 5, 3, 0}, + {19, 0, 4, 0, 0, 136, 0, 0, 0}, + {23, 0, 19, 0, 0, 0, 0, 0, 0}, + {0, 0, 15, 0, 0, 0, 0, 0, 0}, + {28, 0, 5, 0, 0, 136, 0, 0, 0}, + {26, 0, 19, 0, 2, 136, 0, 0, 0}, + {22, 0, 19, 0, 2, 136, 0, 0, 0}, + {23, 0, 19, 0, 2, 136, 0, 0, 0}, + {21, 0, 19, 0, 2, 136, 0, 0, 0}, + {20, 0, 19, 0, 2, 136, 0, 0, 0}, + {26, 0, 13, 0, 2, 136, 0, 0, 0}, + {22, 0, 19, 1, 2, 136, 0, 0, 0}, + {23, 0, 19, 1, 2, 136, 0, 0, 0}, + {26, 0, 11, 0, 2, 136, 0, 0, 0}, + {27, 0, 10, 0, 2, 136, 0, 0, 0}, + {21, 0, 10, 0, 2, 136, 0, 0, 0}, + {27, 0, 19, 1, 2, 136, 0, 0, 0}, + {27, 0, 19, 0, 2, 136, 0, 0, 0}, + {28, 0, 11, 0, 2, 136, 0, 0, 0}, + {26, 0, 19, 0, 5, 136, 0, 0, 0}, + {26, 0, 11, 0, 5, 136, 0, 0, 0}, + {28, 0, 11, 0, 5, 136, 0, 0, 0}, + {22, 0, 19, 1, 5, 136, 0, 0, 0}, + {23, 0, 19, 1, 5, 136, 0, 0, 0}, + {27, 0, 10, 0, 5, 136, 0, 0, 0}, + {26, 0, 13, 0, 5, 136, 0, 0, 0}, + {21, 0, 10, 0, 5, 136, 0, 0, 0}, + {7, 0, 9, 0, 5, 136, 0, 0, 0}, + {27, 0, 19, 1, 5, 136, 0, 0, 0}, + {27, 0, 19, 0, 5, 136, 0, 0, 0}, + {1, 0, 1, 0, 5, 136, 0, 0, 0}, + {29, 0, 19, 0, 5, 136, 0, 0, 0}, + {20, 0, 19, 0, 5, 136, 0, 0, 0}, + {2, 0, 1, 0, 5, 136, 0, 0, 0}, + {26, 0, 19, 0, 1, 136, 0, 0, 0}, + {22, 0, 19, 1, 1, 136, 0, 0, 0}, + {23, 0, 19, 1, 1, 136, 0, 0, 0}, + {19, 0, 1, 0, 1, 136, 0, 0, 0}, + {18, 0, 1, 0, 1, 136, 0, 0, 0}, + {18, 0, 1, 0, 1, 136, 5, 3, 0}, + {30, 0, 19, 0, 5, 136, 0, 0, 0}, + {30, 0, 19, 0, 1, 136, 0, 0, 0}, + {27, 0, 19, 0, 1, 136, 0, 0, 0}, + {14, 0, 19, 0, 0, 0, 4, 0, 0}, + {8, 0, 19, 0, 0, 0, 0, 0, 0}, + {9, 0, 9, 0, 0, 0, 0, 0, 0}, + {9, 0, 4, 0, 0, 0, 0, 0, 0}, + {30, 0, 4, 0, 0, 0, 0, 0, 0}, + {19, 0, 4, 0, 0, 0, 0, 2, 0}, + {1, 0, 4, 0, 0, 0, 0, 0, 0}, + {2, 0, 4, 0, 0, 0, 0, 0, 0}, + {27, 0, 4, 0, 0, 0, 0, 0, 0}, + {9, 0, 12, 0, 0, 0, 0, 0, 0}, + {9, 0, 5, 0, 0, 0, 0, 0, 0}, + {14, 0, 1, 0, 0, 0, 1, 0, 0}, + {5, 0, 1, 0, 0, 90, 5, 3, 0}, + {4, 9, 1, 0, 0, 0, 5, 3, 0}, + {4, 0, 14, 0, 0, 90, 5, 3, 0}, + {19, 0, 1, 0, 0, 90, 9, 0, 0}, + {19, 0, 1, 0, 0, 10, 9, 0, 0}, + {4, 0, 14, 0, 2, 0, 5, 3, 0}, + {5, 6, 1, 0, 2, 0, 5, 3, 0}, + {30, 0, 1, 0, 0, 136, 0, 0, 0}, + {7, 0, 9, 0, 0, 136, 0, 0, 0}, + {30, 0, 1, 0, 0, 170, 0, 0, 0}, + {5, 216, 1, 0, 0, 0, 5, 3, 0}, + {5, 226, 1, 0, 0, 0, 5, 3, 0}, + {9, 0, 1, 0, 2, 0, 0, 0, 0}, + {0, 0, 1, 0, 0, 0, 0, 0, 1}, + {30, 0, 1, 0, 4, 0, 0, 0, 0}, + {30, 0, 1, 0, 4, 0, 0, 0, 1}, + {30, 0, 1, 0, 2, 0, 0, 0, 1}, + {30, 0, 1, 0, 0, 0, 6, 0, 0}, + {29, 0, 19, 0, 2, 0, 5, 3, 0}, + {14, 0, 15, 0, 0, 0, 5, 3, 0}, }; /* Reindexing of NFC first characters. */ @@ -582,7 +629,7 @@ static struct reindex nfc_first[] = { { 93539, 0, 388}, { 93543, 0, 389}, { 93545, 0, 390}, - {0,0,0} + {0x7fffffff, 0, 0} }; static struct reindex nfc_last[] = { @@ -633,7 +680,7 @@ static struct reindex nfc_last[] = { { 90398, 2, 67}, { 90409, 0, 70}, { 93543, 0, 71}, - {0,0,0} + {0x7fffffff, 0, 0} }; /* string literals */ @@ -707,6 +754,751 @@ const char *_PyUnicode_EastAsianWidthNames[] = { "F", NULL }; +#define GCB_Other 0 +#define GCB_Prepend 1 +#define GCB_CR 2 +#define GCB_LF 3 +#define GCB_Control 4 +#define GCB_Extend 5 +#define GCB_Regional_Indicator 6 +#define GCB_SpacingMark 7 +#define GCB_L 8 +#define GCB_V 9 +#define GCB_T 10 +#define GCB_LV 11 +#define GCB_LVT 12 +#define GCB_ZWJ 13 +const char * const _PyUnicode_GraphemeBreakNames[] = { + "Other", + "Prepend", + "CR", + "LF", + "Control", + "Extend", + "Regional_Indicator", + "SpacingMark", + "L", + "V", + "T", + "LV", + "LVT", + "ZWJ", + NULL +}; +#define InCB_None 0 +#define InCB_Linker 1 +#define InCB_Consonant 2 +#define InCB_Extend 3 +const char * const _PyUnicode_IndicConjunctBreakNames[] = { + "None", + "Linker", + "Consonant", + "Extend", + NULL +}; +static const char * const _PyUnicode_BlockNames[] = { + "Basic Latin", + "Latin-1 Supplement", + "Latin Extended-A", + "Latin Extended-B", + "IPA Extensions", + "Spacing Modifier Letters", + "Combining Diacritical Marks", + "Greek and Coptic", + "Cyrillic", + "Cyrillic Supplement", + "Armenian", + "Hebrew", + "Arabic", + "Syriac", + "Arabic Supplement", + "Thaana", + "NKo", + "Samaritan", + "Mandaic", + "Syriac Supplement", + "Arabic Extended-B", + "Arabic Extended-A", + "Devanagari", + "Bengali", + "Gurmukhi", + "Gujarati", + "Oriya", + "Tamil", + "Telugu", + "Kannada", + "Malayalam", + "Sinhala", + "Thai", + "Lao", + "Tibetan", + "Myanmar", + "Georgian", + "Hangul Jamo", + "Ethiopic", + "Ethiopic Supplement", + "Cherokee", + "Unified Canadian Aboriginal Syllabics", + "Ogham", + "Runic", + "Tagalog", + "Hanunoo", + "Buhid", + "Tagbanwa", + "Khmer", + "Mongolian", + "Unified Canadian Aboriginal Syllabics Extended", + "Limbu", + "Tai Le", + "New Tai Lue", + "Khmer Symbols", + "Buginese", + "Tai Tham", + "Combining Diacritical Marks Extended", + "Balinese", + "Sundanese", + "Batak", + "Lepcha", + "Ol Chiki", + "Cyrillic Extended-C", + "Georgian Extended", + "Sundanese Supplement", + "Vedic Extensions", + "Phonetic Extensions", + "Phonetic Extensions Supplement", + "Combining Diacritical Marks Supplement", + "Latin Extended Additional", + "Greek Extended", + "General Punctuation", + "Superscripts and Subscripts", + "Currency Symbols", + "Combining Diacritical Marks for Symbols", + "Letterlike Symbols", + "Number Forms", + "Arrows", + "Mathematical Operators", + "Miscellaneous Technical", + "Control Pictures", + "Optical Character Recognition", + "Enclosed Alphanumerics", + "Box Drawing", + "Block Elements", + "Geometric Shapes", + "Miscellaneous Symbols", + "Dingbats", + "Miscellaneous Mathematical Symbols-A", + "Supplemental Arrows-A", + "Braille Patterns", + "Supplemental Arrows-B", + "Miscellaneous Mathematical Symbols-B", + "Supplemental Mathematical Operators", + "Miscellaneous Symbols and Arrows", + "Glagolitic", + "Latin Extended-C", + "Coptic", + "Georgian Supplement", + "Tifinagh", + "Ethiopic Extended", + "Cyrillic Extended-A", + "Supplemental Punctuation", + "CJK Radicals Supplement", + "Kangxi Radicals", + "Ideographic Description Characters", + "CJK Symbols and Punctuation", + "Hiragana", + "Katakana", + "Bopomofo", + "Hangul Compatibility Jamo", + "Kanbun", + "Bopomofo Extended", + "CJK Strokes", + "Katakana Phonetic Extensions", + "Enclosed CJK Letters and Months", + "CJK Compatibility", + "CJK Unified Ideographs Extension A", + "Yijing Hexagram Symbols", + "CJK Unified Ideographs", + "Yi Syllables", + "Yi Radicals", + "Lisu", + "Vai", + "Cyrillic Extended-B", + "Bamum", + "Modifier Tone Letters", + "Latin Extended-D", + "Syloti Nagri", + "Common Indic Number Forms", + "Phags-pa", + "Saurashtra", + "Devanagari Extended", + "Kayah Li", + "Rejang", + "Hangul Jamo Extended-A", + "Javanese", + "Myanmar Extended-B", + "Cham", + "Myanmar Extended-A", + "Tai Viet", + "Meetei Mayek Extensions", + "Ethiopic Extended-A", + "Latin Extended-E", + "Cherokee Supplement", + "Meetei Mayek", + "Hangul Syllables", + "Hangul Jamo Extended-B", + "High Surrogates", + "High Private Use Surrogates", + "Low Surrogates", + "Private Use Area", + "CJK Compatibility Ideographs", + "Alphabetic Presentation Forms", + "Arabic Presentation Forms-A", + "Variation Selectors", + "Vertical Forms", + "Combining Half Marks", + "CJK Compatibility Forms", + "Small Form Variants", + "Arabic Presentation Forms-B", + "Halfwidth and Fullwidth Forms", + "Specials", + "Linear B Syllabary", + "Linear B Ideograms", + "Aegean Numbers", + "Ancient Greek Numbers", + "Ancient Symbols", + "Phaistos Disc", + "Lycian", + "Carian", + "Coptic Epact Numbers", + "Old Italic", + "Gothic", + "Old Permic", + "Ugaritic", + "Old Persian", + "Deseret", + "Shavian", + "Osmanya", + "Osage", + "Elbasan", + "Caucasian Albanian", + "Vithkuqi", + "Todhri", + "Linear A", + "Latin Extended-F", + "Cypriot Syllabary", + "Imperial Aramaic", + "Palmyrene", + "Nabataean", + "Hatran", + "Phoenician", + "Lydian", + "Sidetic", + "Meroitic Hieroglyphs", + "Meroitic Cursive", + "Kharoshthi", + "Old South Arabian", + "Old North Arabian", + "Manichaean", + "Avestan", + "Inscriptional Parthian", + "Inscriptional Pahlavi", + "Psalter Pahlavi", + "Old Turkic", + "Old Hungarian", + "Hanifi Rohingya", + "Garay", + "Rumi Numeral Symbols", + "Yezidi", + "Arabic Extended-C", + "Old Sogdian", + "Sogdian", + "Old Uyghur", + "Chorasmian", + "Elymaic", + "Brahmi", + "Kaithi", + "Sora Sompeng", + "Chakma", + "Mahajani", + "Sharada", + "Sinhala Archaic Numbers", + "Khojki", + "Multani", + "Khudawadi", + "Grantha", + "Tulu-Tigalari", + "Newa", + "Tirhuta", + "Siddham", + "Modi", + "Mongolian Supplement", + "Takri", + "Myanmar Extended-C", + "Ahom", + "Dogra", + "Warang Citi", + "Dives Akuru", + "Nandinagari", + "Zanabazar Square", + "Soyombo", + "Unified Canadian Aboriginal Syllabics Extended-A", + "Pau Cin Hau", + "Devanagari Extended-A", + "Sharada Supplement", + "Sunuwar", + "Bhaiksuki", + "Marchen", + "Masaram Gondi", + "Gunjala Gondi", + "Tolong Siki", + "Makasar", + "Kawi", + "Lisu Supplement", + "Tamil Supplement", + "Cuneiform", + "Cuneiform Numbers and Punctuation", + "Early Dynastic Cuneiform", + "Cypro-Minoan", + "Egyptian Hieroglyphs", + "Egyptian Hieroglyph Format Controls", + "Egyptian Hieroglyphs Extended-A", + "Anatolian Hieroglyphs", + "Gurung Khema", + "Bamum Supplement", + "Mro", + "Tangsa", + "Bassa Vah", + "Pahawh Hmong", + "Kirat Rai", + "Medefaidrin", + "Beria Erfe", + "Miao", + "Ideographic Symbols and Punctuation", + "Tangut", + "Tangut Components", + "Khitan Small Script", + "Tangut Supplement", + "Tangut Components Supplement", + "Kana Extended-B", + "Kana Supplement", + "Kana Extended-A", + "Small Kana Extension", + "Nushu", + "Duployan", + "Shorthand Format Controls", + "Symbols for Legacy Computing Supplement", + "Miscellaneous Symbols Supplement", + "Znamenny Musical Notation", + "Byzantine Musical Symbols", + "Musical Symbols", + "Ancient Greek Musical Notation", + "Kaktovik Numerals", + "Mayan Numerals", + "Tai Xuan Jing Symbols", + "Counting Rod Numerals", + "Mathematical Alphanumeric Symbols", + "Sutton SignWriting", + "Latin Extended-G", + "Glagolitic Supplement", + "Cyrillic Extended-D", + "Nyiakeng Puachue Hmong", + "Toto", + "Wancho", + "Nag Mundari", + "Ol Onal", + "Tai Yo", + "Ethiopic Extended-B", + "Mende Kikakui", + "Adlam", + "Indic Siyaq Numbers", + "Ottoman Siyaq Numbers", + "Arabic Mathematical Alphabetic Symbols", + "Mahjong Tiles", + "Domino Tiles", + "Playing Cards", + "Enclosed Alphanumeric Supplement", + "Enclosed Ideographic Supplement", + "Miscellaneous Symbols and Pictographs", + "Emoticons", + "Ornamental Dingbats", + "Transport and Map Symbols", + "Alchemical Symbols", + "Geometric Shapes Extended", + "Supplemental Arrows-C", + "Supplemental Symbols and Pictographs", + "Chess Symbols", + "Symbols and Pictographs Extended-A", + "Symbols for Legacy Computing", + "CJK Unified Ideographs Extension B", + "CJK Unified Ideographs Extension C", + "CJK Unified Ideographs Extension D", + "CJK Unified Ideographs Extension E", + "CJK Unified Ideographs Extension F", + "CJK Unified Ideographs Extension I", + "CJK Compatibility Ideographs Supplement", + "CJK Unified Ideographs Extension G", + "CJK Unified Ideographs Extension H", + "CJK Unified Ideographs Extension J", + "Tags", + "Variation Selectors Supplement", + "Supplementary Private Use Area-A", + "Supplementary Private Use Area-B", +}; +typedef struct { + Py_UCS4 start; + Py_UCS4 end; + unsigned short name; +} _PyUnicode_Block; +static const _PyUnicode_Block _PyUnicode_Blocks[] = { + {0x0000, 0x007F, 0}, + {0x0080, 0x00FF, 1}, + {0x0100, 0x017F, 2}, + {0x0180, 0x024F, 3}, + {0x0250, 0x02AF, 4}, + {0x02B0, 0x02FF, 5}, + {0x0300, 0x036F, 6}, + {0x0370, 0x03FF, 7}, + {0x0400, 0x04FF, 8}, + {0x0500, 0x052F, 9}, + {0x0530, 0x058F, 10}, + {0x0590, 0x05FF, 11}, + {0x0600, 0x06FF, 12}, + {0x0700, 0x074F, 13}, + {0x0750, 0x077F, 14}, + {0x0780, 0x07BF, 15}, + {0x07C0, 0x07FF, 16}, + {0x0800, 0x083F, 17}, + {0x0840, 0x085F, 18}, + {0x0860, 0x086F, 19}, + {0x0870, 0x089F, 20}, + {0x08A0, 0x08FF, 21}, + {0x0900, 0x097F, 22}, + {0x0980, 0x09FF, 23}, + {0x0A00, 0x0A7F, 24}, + {0x0A80, 0x0AFF, 25}, + {0x0B00, 0x0B7F, 26}, + {0x0B80, 0x0BFF, 27}, + {0x0C00, 0x0C7F, 28}, + {0x0C80, 0x0CFF, 29}, + {0x0D00, 0x0D7F, 30}, + {0x0D80, 0x0DFF, 31}, + {0x0E00, 0x0E7F, 32}, + {0x0E80, 0x0EFF, 33}, + {0x0F00, 0x0FFF, 34}, + {0x1000, 0x109F, 35}, + {0x10A0, 0x10FF, 36}, + {0x1100, 0x11FF, 37}, + {0x1200, 0x137F, 38}, + {0x1380, 0x139F, 39}, + {0x13A0, 0x13FF, 40}, + {0x1400, 0x167F, 41}, + {0x1680, 0x169F, 42}, + {0x16A0, 0x16FF, 43}, + {0x1700, 0x171F, 44}, + {0x1720, 0x173F, 45}, + {0x1740, 0x175F, 46}, + {0x1760, 0x177F, 47}, + {0x1780, 0x17FF, 48}, + {0x1800, 0x18AF, 49}, + {0x18B0, 0x18FF, 50}, + {0x1900, 0x194F, 51}, + {0x1950, 0x197F, 52}, + {0x1980, 0x19DF, 53}, + {0x19E0, 0x19FF, 54}, + {0x1A00, 0x1A1F, 55}, + {0x1A20, 0x1AAF, 56}, + {0x1AB0, 0x1AFF, 57}, + {0x1B00, 0x1B7F, 58}, + {0x1B80, 0x1BBF, 59}, + {0x1BC0, 0x1BFF, 60}, + {0x1C00, 0x1C4F, 61}, + {0x1C50, 0x1C7F, 62}, + {0x1C80, 0x1C8F, 63}, + {0x1C90, 0x1CBF, 64}, + {0x1CC0, 0x1CCF, 65}, + {0x1CD0, 0x1CFF, 66}, + {0x1D00, 0x1D7F, 67}, + {0x1D80, 0x1DBF, 68}, + {0x1DC0, 0x1DFF, 69}, + {0x1E00, 0x1EFF, 70}, + {0x1F00, 0x1FFF, 71}, + {0x2000, 0x206F, 72}, + {0x2070, 0x209F, 73}, + {0x20A0, 0x20CF, 74}, + {0x20D0, 0x20FF, 75}, + {0x2100, 0x214F, 76}, + {0x2150, 0x218F, 77}, + {0x2190, 0x21FF, 78}, + {0x2200, 0x22FF, 79}, + {0x2300, 0x23FF, 80}, + {0x2400, 0x243F, 81}, + {0x2440, 0x245F, 82}, + {0x2460, 0x24FF, 83}, + {0x2500, 0x257F, 84}, + {0x2580, 0x259F, 85}, + {0x25A0, 0x25FF, 86}, + {0x2600, 0x26FF, 87}, + {0x2700, 0x27BF, 88}, + {0x27C0, 0x27EF, 89}, + {0x27F0, 0x27FF, 90}, + {0x2800, 0x28FF, 91}, + {0x2900, 0x297F, 92}, + {0x2980, 0x29FF, 93}, + {0x2A00, 0x2AFF, 94}, + {0x2B00, 0x2BFF, 95}, + {0x2C00, 0x2C5F, 96}, + {0x2C60, 0x2C7F, 97}, + {0x2C80, 0x2CFF, 98}, + {0x2D00, 0x2D2F, 99}, + {0x2D30, 0x2D7F, 100}, + {0x2D80, 0x2DDF, 101}, + {0x2DE0, 0x2DFF, 102}, + {0x2E00, 0x2E7F, 103}, + {0x2E80, 0x2EFF, 104}, + {0x2F00, 0x2FDF, 105}, + {0x2FF0, 0x2FFF, 106}, + {0x3000, 0x303F, 107}, + {0x3040, 0x309F, 108}, + {0x30A0, 0x30FF, 109}, + {0x3100, 0x312F, 110}, + {0x3130, 0x318F, 111}, + {0x3190, 0x319F, 112}, + {0x31A0, 0x31BF, 113}, + {0x31C0, 0x31EF, 114}, + {0x31F0, 0x31FF, 115}, + {0x3200, 0x32FF, 116}, + {0x3300, 0x33FF, 117}, + {0x3400, 0x4DBF, 118}, + {0x4DC0, 0x4DFF, 119}, + {0x4E00, 0x9FFF, 120}, + {0xA000, 0xA48F, 121}, + {0xA490, 0xA4CF, 122}, + {0xA4D0, 0xA4FF, 123}, + {0xA500, 0xA63F, 124}, + {0xA640, 0xA69F, 125}, + {0xA6A0, 0xA6FF, 126}, + {0xA700, 0xA71F, 127}, + {0xA720, 0xA7FF, 128}, + {0xA800, 0xA82F, 129}, + {0xA830, 0xA83F, 130}, + {0xA840, 0xA87F, 131}, + {0xA880, 0xA8DF, 132}, + {0xA8E0, 0xA8FF, 133}, + {0xA900, 0xA92F, 134}, + {0xA930, 0xA95F, 135}, + {0xA960, 0xA97F, 136}, + {0xA980, 0xA9DF, 137}, + {0xA9E0, 0xA9FF, 138}, + {0xAA00, 0xAA5F, 139}, + {0xAA60, 0xAA7F, 140}, + {0xAA80, 0xAADF, 141}, + {0xAAE0, 0xAAFF, 142}, + {0xAB00, 0xAB2F, 143}, + {0xAB30, 0xAB6F, 144}, + {0xAB70, 0xABBF, 145}, + {0xABC0, 0xABFF, 146}, + {0xAC00, 0xD7AF, 147}, + {0xD7B0, 0xD7FF, 148}, + {0xD800, 0xDB7F, 149}, + {0xDB80, 0xDBFF, 150}, + {0xDC00, 0xDFFF, 151}, + {0xE000, 0xF8FF, 152}, + {0xF900, 0xFAFF, 153}, + {0xFB00, 0xFB4F, 154}, + {0xFB50, 0xFDFF, 155}, + {0xFE00, 0xFE0F, 156}, + {0xFE10, 0xFE1F, 157}, + {0xFE20, 0xFE2F, 158}, + {0xFE30, 0xFE4F, 159}, + {0xFE50, 0xFE6F, 160}, + {0xFE70, 0xFEFF, 161}, + {0xFF00, 0xFFEF, 162}, + {0xFFF0, 0xFFFF, 163}, + {0x10000, 0x1007F, 164}, + {0x10080, 0x100FF, 165}, + {0x10100, 0x1013F, 166}, + {0x10140, 0x1018F, 167}, + {0x10190, 0x101CF, 168}, + {0x101D0, 0x101FF, 169}, + {0x10280, 0x1029F, 170}, + {0x102A0, 0x102DF, 171}, + {0x102E0, 0x102FF, 172}, + {0x10300, 0x1032F, 173}, + {0x10330, 0x1034F, 174}, + {0x10350, 0x1037F, 175}, + {0x10380, 0x1039F, 176}, + {0x103A0, 0x103DF, 177}, + {0x10400, 0x1044F, 178}, + {0x10450, 0x1047F, 179}, + {0x10480, 0x104AF, 180}, + {0x104B0, 0x104FF, 181}, + {0x10500, 0x1052F, 182}, + {0x10530, 0x1056F, 183}, + {0x10570, 0x105BF, 184}, + {0x105C0, 0x105FF, 185}, + {0x10600, 0x1077F, 186}, + {0x10780, 0x107BF, 187}, + {0x10800, 0x1083F, 188}, + {0x10840, 0x1085F, 189}, + {0x10860, 0x1087F, 190}, + {0x10880, 0x108AF, 191}, + {0x108E0, 0x108FF, 192}, + {0x10900, 0x1091F, 193}, + {0x10920, 0x1093F, 194}, + {0x10940, 0x1095F, 195}, + {0x10980, 0x1099F, 196}, + {0x109A0, 0x109FF, 197}, + {0x10A00, 0x10A5F, 198}, + {0x10A60, 0x10A7F, 199}, + {0x10A80, 0x10A9F, 200}, + {0x10AC0, 0x10AFF, 201}, + {0x10B00, 0x10B3F, 202}, + {0x10B40, 0x10B5F, 203}, + {0x10B60, 0x10B7F, 204}, + {0x10B80, 0x10BAF, 205}, + {0x10C00, 0x10C4F, 206}, + {0x10C80, 0x10CFF, 207}, + {0x10D00, 0x10D3F, 208}, + {0x10D40, 0x10D8F, 209}, + {0x10E60, 0x10E7F, 210}, + {0x10E80, 0x10EBF, 211}, + {0x10EC0, 0x10EFF, 212}, + {0x10F00, 0x10F2F, 213}, + {0x10F30, 0x10F6F, 214}, + {0x10F70, 0x10FAF, 215}, + {0x10FB0, 0x10FDF, 216}, + {0x10FE0, 0x10FFF, 217}, + {0x11000, 0x1107F, 218}, + {0x11080, 0x110CF, 219}, + {0x110D0, 0x110FF, 220}, + {0x11100, 0x1114F, 221}, + {0x11150, 0x1117F, 222}, + {0x11180, 0x111DF, 223}, + {0x111E0, 0x111FF, 224}, + {0x11200, 0x1124F, 225}, + {0x11280, 0x112AF, 226}, + {0x112B0, 0x112FF, 227}, + {0x11300, 0x1137F, 228}, + {0x11380, 0x113FF, 229}, + {0x11400, 0x1147F, 230}, + {0x11480, 0x114DF, 231}, + {0x11580, 0x115FF, 232}, + {0x11600, 0x1165F, 233}, + {0x11660, 0x1167F, 234}, + {0x11680, 0x116CF, 235}, + {0x116D0, 0x116FF, 236}, + {0x11700, 0x1174F, 237}, + {0x11800, 0x1184F, 238}, + {0x118A0, 0x118FF, 239}, + {0x11900, 0x1195F, 240}, + {0x119A0, 0x119FF, 241}, + {0x11A00, 0x11A4F, 242}, + {0x11A50, 0x11AAF, 243}, + {0x11AB0, 0x11ABF, 244}, + {0x11AC0, 0x11AFF, 245}, + {0x11B00, 0x11B5F, 246}, + {0x11B60, 0x11B7F, 247}, + {0x11BC0, 0x11BFF, 248}, + {0x11C00, 0x11C6F, 249}, + {0x11C70, 0x11CBF, 250}, + {0x11D00, 0x11D5F, 251}, + {0x11D60, 0x11DAF, 252}, + {0x11DB0, 0x11DEF, 253}, + {0x11EE0, 0x11EFF, 254}, + {0x11F00, 0x11F5F, 255}, + {0x11FB0, 0x11FBF, 256}, + {0x11FC0, 0x11FFF, 257}, + {0x12000, 0x123FF, 258}, + {0x12400, 0x1247F, 259}, + {0x12480, 0x1254F, 260}, + {0x12F90, 0x12FFF, 261}, + {0x13000, 0x1342F, 262}, + {0x13430, 0x1345F, 263}, + {0x13460, 0x143FF, 264}, + {0x14400, 0x1467F, 265}, + {0x16100, 0x1613F, 266}, + {0x16800, 0x16A3F, 267}, + {0x16A40, 0x16A6F, 268}, + {0x16A70, 0x16ACF, 269}, + {0x16AD0, 0x16AFF, 270}, + {0x16B00, 0x16B8F, 271}, + {0x16D40, 0x16D7F, 272}, + {0x16E40, 0x16E9F, 273}, + {0x16EA0, 0x16EDF, 274}, + {0x16F00, 0x16F9F, 275}, + {0x16FE0, 0x16FFF, 276}, + {0x17000, 0x187FF, 277}, + {0x18800, 0x18AFF, 278}, + {0x18B00, 0x18CFF, 279}, + {0x18D00, 0x18D7F, 280}, + {0x18D80, 0x18DFF, 281}, + {0x1AFF0, 0x1AFFF, 282}, + {0x1B000, 0x1B0FF, 283}, + {0x1B100, 0x1B12F, 284}, + {0x1B130, 0x1B16F, 285}, + {0x1B170, 0x1B2FF, 286}, + {0x1BC00, 0x1BC9F, 287}, + {0x1BCA0, 0x1BCAF, 288}, + {0x1CC00, 0x1CEBF, 289}, + {0x1CEC0, 0x1CEFF, 290}, + {0x1CF00, 0x1CFCF, 291}, + {0x1D000, 0x1D0FF, 292}, + {0x1D100, 0x1D1FF, 293}, + {0x1D200, 0x1D24F, 294}, + {0x1D2C0, 0x1D2DF, 295}, + {0x1D2E0, 0x1D2FF, 296}, + {0x1D300, 0x1D35F, 297}, + {0x1D360, 0x1D37F, 298}, + {0x1D400, 0x1D7FF, 299}, + {0x1D800, 0x1DAAF, 300}, + {0x1DF00, 0x1DFFF, 301}, + {0x1E000, 0x1E02F, 302}, + {0x1E030, 0x1E08F, 303}, + {0x1E100, 0x1E14F, 304}, + {0x1E290, 0x1E2BF, 305}, + {0x1E2C0, 0x1E2FF, 306}, + {0x1E4D0, 0x1E4FF, 307}, + {0x1E5D0, 0x1E5FF, 308}, + {0x1E6C0, 0x1E6FF, 309}, + {0x1E7E0, 0x1E7FF, 310}, + {0x1E800, 0x1E8DF, 311}, + {0x1E900, 0x1E95F, 312}, + {0x1EC70, 0x1ECBF, 313}, + {0x1ED00, 0x1ED4F, 314}, + {0x1EE00, 0x1EEFF, 315}, + {0x1F000, 0x1F02F, 316}, + {0x1F030, 0x1F09F, 317}, + {0x1F0A0, 0x1F0FF, 318}, + {0x1F100, 0x1F1FF, 319}, + {0x1F200, 0x1F2FF, 320}, + {0x1F300, 0x1F5FF, 321}, + {0x1F600, 0x1F64F, 322}, + {0x1F650, 0x1F67F, 323}, + {0x1F680, 0x1F6FF, 324}, + {0x1F700, 0x1F77F, 325}, + {0x1F780, 0x1F7FF, 326}, + {0x1F800, 0x1F8FF, 327}, + {0x1F900, 0x1F9FF, 328}, + {0x1FA00, 0x1FA6F, 329}, + {0x1FA70, 0x1FAFF, 330}, + {0x1FB00, 0x1FBFF, 331}, + {0x20000, 0x2A6DF, 332}, + {0x2A700, 0x2B73F, 333}, + {0x2B740, 0x2B81F, 334}, + {0x2B820, 0x2CEAF, 335}, + {0x2CEB0, 0x2EBEF, 336}, + {0x2EBF0, 0x2EE5F, 337}, + {0x2F800, 0x2FA1F, 338}, + {0x30000, 0x3134F, 339}, + {0x31350, 0x323AF, 340}, + {0x323B0, 0x3347F, 341}, + {0xE0000, 0xE007F, 342}, + {0xE0100, 0xE01EF, 343}, + {0xF0000, 0xFFFFF, 344}, + {0x100000, 0x10FFFF, 345}, +}; +#define BLOCK_COUNT 346 + static const char *decomp_prefix[] = { "", "<noBreak>", @@ -752,55 +1544,55 @@ static const unsigned short index1[] = { 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 103, 101, 101, 101, 101, 101, 101, 101, 101, 104, 41, 41, 105, 106, - 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 118, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 121, 121, 122, 123, - 124, 125, 126, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, - 137, 138, 139, 140, 141, 142, 143, 144, 41, 41, 145, 146, 147, 148, 149, - 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, - 164, 165, 166, 167, 168, 169, 170, 171, 172, 137, 173, 174, 175, 176, - 137, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 137, - 189, 190, 191, 41, 41, 41, 41, 41, 41, 41, 192, 193, 41, 194, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 195, 41, 41, 41, 41, 41, 41, 41, 41, 196, 41, 41, 41, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 117, 118, 119, 120, 121, 122, 123, 117, 118, 119, 120, + 121, 122, 123, 117, 118, 119, 120, 121, 122, 123, 117, 118, 119, 120, + 121, 122, 123, 117, 118, 119, 120, 121, 122, 123, 117, 118, 119, 120, + 121, 122, 123, 117, 118, 119, 120, 121, 122, 123, 117, 118, 119, 120, + 121, 122, 123, 117, 118, 119, 120, 121, 122, 123, 117, 118, 119, 120, + 121, 122, 123, 117, 118, 119, 120, 121, 122, 123, 117, 118, 119, 124, + 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, 127, 128, 129, + 130, 131, 132, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 41, 41, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 143, 179, 180, 181, 182, + 143, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 143, + 195, 196, 197, 41, 41, 41, 41, 41, 41, 41, 198, 199, 41, 200, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 201, 41, 41, 41, 41, 41, 41, 41, 41, 202, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 41, 41, 41, 41, 41, 41, 41, 41, 41, 134, 41, 41, 41, 41, 197, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 198, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 41, 41, 41, 41, 199, 200, - 201, 202, 137, 137, 203, 137, 204, 205, 206, 207, 101, 101, 101, 101, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 140, 41, 41, 41, 41, 203, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 204, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 41, 41, 41, 41, 205, 206, + 207, 208, 143, 143, 209, 143, 210, 211, 212, 213, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 208, 101, 101, 101, 101, 101, 101, 101, 101, 101, 209, 210, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 211, 101, 101, - 212, 101, 101, 213, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 214, 215, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 216, 217, - 216, 216, 216, 218, 219, 220, 78, 221, 222, 223, 224, 225, 226, 137, 227, - 228, 229, 230, 231, 232, 233, 234, 78, 78, 78, 78, 235, 236, 137, 137, - 137, 137, 137, 137, 137, 137, 237, 137, 238, 239, 240, 137, 137, 241, - 137, 137, 137, 242, 137, 243, 137, 137, 137, 244, 245, 246, 247, 137, - 137, 137, 137, 137, 248, 249, 250, 137, 251, 252, 137, 137, 253, 254, - 255, 256, 257, 137, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, - 268, 269, 270, 271, 272, 273, 216, 274, 137, 137, 137, 137, 137, 137, - 137, 137, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 214, 215, 216, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 217, 101, 101, + 218, 101, 101, 219, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 220, 221, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 222, 223, + 222, 222, 222, 224, 225, 226, 78, 227, 228, 229, 230, 231, 232, 143, 233, + 234, 235, 236, 237, 238, 239, 240, 78, 78, 78, 78, 241, 242, 143, 143, + 143, 143, 143, 143, 143, 143, 243, 143, 244, 245, 246, 143, 143, 247, + 143, 143, 143, 248, 143, 249, 143, 250, 143, 251, 252, 253, 254, 255, + 255, 255, 255, 255, 256, 257, 258, 255, 259, 260, 255, 255, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 222, 275, + 276, 277, 278, 279, 280, 281, 222, 282, 266, 266, 266, 266, 266, 266, + 266, 283, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, @@ -823,2692 +1615,2913 @@ static const unsigned short index1[] = { 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 275, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 284, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 276, 101, 277, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 285, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 278, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 286, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 279, 101, 101, - 101, 101, 280, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 121, 121, 121, 121, 282, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 283, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 287, 101, 101, + 101, 101, 288, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 127, 127, 127, 127, 290, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 291, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 284, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 292, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 285, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 283, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 286, 137, 287, 288, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 289, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, 120, 289, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 293, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, + 289, 289, 289, 291, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 294, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 294, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 294, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 294, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 294, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 294, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 294, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 294, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 294, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 294, + 295, 296, 297, 298, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, + 296, 296, 296, 296, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 294, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 299, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 299, }; static const unsigned short index2[] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 4, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 3, 3, 3, 2, 5, 6, 6, 7, 8, 7, 6, 6, 9, 10, 6, 11, 12, 13, 12, - 12, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 12, 6, 15, 16, 15, 6, 6, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 9, 6, 10, 18, 19, 18, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 9, 16, 10, 16, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 21, 22, 8, 8, 23, 8, 24, - 22, 25, 26, 27, 28, 16, 29, 30, 31, 32, 33, 34, 34, 25, 35, 22, 22, 25, - 34, 27, 36, 37, 37, 37, 22, 38, 38, 38, 38, 38, 38, 39, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 39, 38, 38, 38, 38, 38, 38, 40, 39, 38, 38, 38, 38, - 38, 39, 41, 42, 42, 43, 43, 43, 43, 41, 43, 42, 42, 42, 43, 42, 42, 43, - 43, 41, 43, 42, 42, 43, 43, 43, 40, 41, 42, 42, 43, 42, 43, 41, 43, 38, - 42, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 44, 41, 38, - 42, 38, 43, 38, 43, 38, 43, 38, 42, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 39, 41, 38, 43, 38, 42, 38, 43, 38, 43, 38, 41, 45, 46, 38, 43, 38, - 43, 41, 38, 43, 38, 43, 38, 43, 45, 46, 39, 41, 38, 42, 38, 43, 38, 42, - 46, 39, 41, 38, 42, 38, 43, 38, 43, 39, 41, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 39, 41, 38, 43, 38, 42, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 38, 43, 38, 43, 38, 43, - 35, 47, 44, 44, 47, 44, 47, 44, 44, 47, 44, 44, 44, 47, 47, 44, 44, 44, - 44, 47, 44, 44, 47, 44, 44, 44, 47, 47, 47, 44, 44, 47, 44, 38, 43, 44, - 47, 44, 47, 44, 44, 47, 44, 47, 47, 44, 47, 44, 38, 43, 44, 44, 44, 47, - 44, 47, 44, 44, 47, 47, 48, 44, 47, 47, 47, 48, 48, 48, 48, 49, 50, 35, - 49, 50, 35, 49, 50, 35, 38, 42, 38, 42, 38, 42, 38, 42, 38, 42, 38, 42, - 38, 42, 38, 42, 47, 38, 43, 38, 43, 38, 43, 44, 47, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 43, 49, 50, 35, 38, 43, 44, 44, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 44, 47, 38, 43, 44, - 47, 44, 47, 44, 47, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 47, 47, 47, 47, 47, 47, 44, 44, 47, 44, 44, 47, 47, 44, 47, 44, 44, - 44, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 47, 41, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 41, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 53, 53, 53, 53, 53, 53, 53, 54, - 54, 55, 54, 52, 56, 52, 56, 56, 56, 52, 56, 52, 52, 57, 53, 54, 54, 54, - 54, 54, 54, 25, 25, 25, 25, 58, 25, 54, 55, 51, 51, 51, 51, 51, 54, 54, - 54, 54, 54, 54, 54, 52, 54, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 59, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, - 59, 59, 60, 60, 59, 60, 59, 60, 59, 59, 61, 62, 62, 62, 62, 61, 63, 62, - 62, 62, 62, 62, 64, 64, 65, 65, 65, 65, 66, 66, 62, 62, 62, 62, 65, 65, - 62, 65, 65, 62, 62, 67, 67, 67, 67, 68, 62, 62, 62, 62, 60, 60, 60, 69, - 69, 59, 69, 69, 70, 60, 62, 62, 62, 60, 60, 60, 62, 62, 71, 60, 60, 60, - 62, 62, 62, 62, 60, 61, 62, 62, 60, 72, 73, 73, 72, 73, 73, 72, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 44, 47, 44, 47, 74, 54, 44, - 47, 0, 0, 51, 47, 47, 47, 75, 44, 0, 0, 0, 0, 58, 76, 38, 75, 38, 38, 38, - 0, 38, 0, 38, 38, 43, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 0, 39, 39, 39, 39, 39, 39, 39, 38, 38, 43, 43, 43, 43, - 43, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 47, 41, 41, 41, 41, 41, 41, 41, 43, 43, 43, 43, 43, 44, 35, 35, 49, 77, - 77, 35, 35, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 35, 35, 35, 47, 49, 35, 78, 44, - 47, 49, 44, 47, 47, 44, 44, 44, 38, 79, 44, 38, 44, 44, 44, 38, 44, 44, - 44, 44, 38, 38, 38, 44, 39, 39, 39, 39, 39, 39, 39, 39, 39, 79, 39, 39, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 4, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6, 6, 6, 2, 7, 8, 8, 9, 10, 9, 8, 8, 11, 12, 8, 13, 14, 15, + 14, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 14, 8, 17, 18, 17, 8, 8, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 11, 8, 12, 20, 21, 20, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 11, 18, 12, 18, 1, 1, 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 24, 10, 10, + 25, 10, 26, 24, 27, 28, 29, 30, 18, 31, 32, 33, 34, 35, 36, 36, 27, 37, + 24, 24, 27, 36, 29, 38, 39, 39, 39, 24, 40, 40, 40, 40, 40, 40, 41, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 40, 42, 41, 40, + 40, 40, 40, 40, 41, 43, 44, 44, 45, 45, 45, 45, 43, 45, 44, 44, 44, 45, + 44, 44, 45, 45, 43, 45, 44, 44, 45, 45, 45, 42, 43, 44, 44, 45, 44, 45, + 43, 45, 40, 44, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 46, 43, 40, 44, 40, 45, 40, 45, 40, 45, 40, 44, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 41, 43, 40, 45, 40, 44, 40, 45, 40, 45, 40, 43, 47, 48, + 40, 45, 40, 45, 43, 40, 45, 40, 45, 40, 45, 47, 48, 41, 43, 40, 44, 40, + 45, 40, 44, 48, 41, 43, 40, 44, 40, 45, 40, 45, 41, 43, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 41, 43, 40, 45, + 40, 44, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 40, 45, 40, + 45, 40, 45, 37, 49, 46, 46, 49, 46, 49, 46, 46, 49, 46, 46, 46, 49, 49, + 46, 46, 46, 46, 49, 46, 46, 49, 46, 46, 46, 49, 49, 49, 46, 46, 49, 46, + 40, 45, 46, 49, 46, 49, 46, 46, 49, 46, 49, 49, 46, 49, 46, 40, 45, 46, + 46, 46, 49, 46, 49, 46, 46, 49, 49, 50, 46, 49, 49, 49, 50, 50, 50, 50, + 51, 52, 37, 51, 52, 37, 51, 52, 37, 40, 44, 40, 44, 40, 44, 40, 44, 40, + 44, 40, 44, 40, 44, 40, 44, 49, 40, 45, 40, 45, 40, 45, 46, 49, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 45, 51, 52, 37, 40, 45, 46, 46, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 46, 49, + 40, 45, 46, 49, 46, 49, 46, 49, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 49, 49, 49, 49, 49, 49, 46, 46, 49, 46, 46, 49, 49, 46, + 49, 46, 46, 46, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 49, 43, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 43, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 55, 55, 55, 55, 55, + 55, 55, 56, 56, 57, 56, 54, 58, 54, 58, 58, 58, 54, 58, 54, 54, 59, 55, + 56, 56, 56, 56, 56, 56, 27, 27, 27, 27, 60, 27, 56, 57, 53, 53, 53, 53, + 53, 56, 56, 56, 56, 56, 56, 56, 54, 56, 55, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 61, 61, 61, 61, 61, 62, 61, 61, + 61, 61, 61, 61, 61, 62, 62, 61, 62, 61, 62, 61, 61, 63, 64, 64, 64, 64, + 63, 65, 64, 64, 64, 64, 64, 66, 66, 67, 67, 67, 67, 68, 68, 64, 64, 64, + 64, 67, 67, 64, 67, 67, 64, 64, 69, 69, 69, 69, 70, 64, 64, 64, 64, 62, + 62, 62, 71, 71, 61, 71, 71, 72, 62, 64, 64, 64, 62, 62, 62, 64, 64, 73, + 62, 62, 62, 64, 64, 64, 64, 62, 63, 64, 64, 62, 74, 75, 75, 74, 75, 75, + 74, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 46, 49, 46, 49, + 76, 56, 46, 49, 77, 77, 53, 49, 49, 49, 78, 46, 77, 77, 77, 77, 60, 79, + 40, 78, 40, 40, 40, 77, 40, 77, 40, 40, 45, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 77, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 45, 45, 45, 45, 45, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 49, 43, 43, 43, 43, 43, 43, 43, 45, 45, 45, 45, + 45, 46, 37, 37, 51, 80, 80, 37, 37, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 37, 37, + 37, 49, 51, 37, 81, 46, 49, 51, 46, 49, 49, 46, 46, 46, 40, 82, 46, 40, + 46, 46, 46, 40, 46, 46, 46, 46, 40, 40, 40, 46, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 82, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 43, 43, 43, 43, 43, 43, 43, 43, 43, 44, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 45, 44, 49, 45, 49, 49, 49, 45, 49, 49, 49, 49, 45, 45, + 45, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 40, 45, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 83, 84, 84, 84, 84, 84, 85, 85, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 40, 45, 46, 49, 46, 49, 46, 49, 46, + 49, 46, 49, 46, 49, 49, 40, 45, 40, 45, 46, 49, 40, 45, 46, 49, 40, 45, + 40, 45, 40, 45, 46, 49, 40, 45, 40, 45, 40, 45, 46, 49, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 46, 49, 40, 45, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 77, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 77, 77, 55, + 86, 86, 86, 86, 86, 86, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 37, 49, 86, 87, 77, 77, 88, 88, 89, + 90, 91, 84, 84, 84, 84, 91, 84, 84, 84, 92, 91, 84, 84, 84, 84, 84, 84, + 91, 91, 91, 91, 91, 91, 84, 84, 91, 84, 84, 92, 93, 84, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 109, 84, 91, 109, 102, 90, 90, 90, 90, 90, 90, 90, 90, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, 90, 90, 90, 112, + 112, 112, 112, 109, 109, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 113, + 113, 113, 113, 113, 113, 81, 81, 114, 115, 115, 116, 117, 118, 88, 88, + 84, 84, 84, 84, 84, 84, 84, 84, 119, 120, 121, 118, 122, 118, 118, 118, + 123, 123, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 125, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 126, 127, 128, 119, 120, 121, 129, 130, 131, 131, 132, 91, 84, 84, + 84, 84, 84, 91, 84, 84, 91, 133, 133, 133, 133, 133, 133, 133, 133, 133, + 133, 115, 134, 134, 118, 123, 123, 135, 123, 123, 123, 123, 136, 136, + 136, 136, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 124, 123, 124, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 124, 118, 123, 84, 84, 84, 84, + 84, 84, 84, 113, 88, 84, 84, 84, 84, 91, 84, 125, 125, 84, 84, 88, 91, + 84, 84, 91, 123, 123, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 123, 123, 123, 138, 138, 123, 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 139, 140, 123, 141, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 84, 91, 84, + 84, 91, 84, 84, 91, 91, 91, 84, 91, 91, 84, 91, 84, 84, 84, 91, 84, 91, + 84, 91, 84, 91, 84, 84, 139, 139, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 123, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 84, 84, 84, 84, 84, 84, + 84, 91, 84, 144, 144, 88, 145, 145, 145, 144, 90, 90, 91, 146, 146, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 84, 84, 84, 84, 144, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 144, 84, 84, 84, 144, 84, 84, 84, 84, 84, 90, 90, + 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, + 109, 90, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 91, 91, 91, + 90, 90, 109, 90, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 139, 139, 139, 139, 139, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 147, 123, 123, 123, 123, 123, 123, 123, 113, 113, 139, 139, 139, + 139, 139, 84, 84, 91, 91, 91, 84, 84, 84, 84, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 125, 84, 84, 84, 84, 84, 91, 91, + 91, 91, 91, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 113, + 91, 84, 84, 91, 84, 84, 91, 84, 84, 84, 91, 91, 91, 126, 127, 128, 84, + 84, 84, 91, 84, 84, 91, 91, 84, 84, 84, 84, 84, 142, 142, 142, 148, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 150, 149, 149, 149, 149, 149, 149, 149, 150, 149, + 149, 150, 149, 149, 149, 149, 149, 142, 148, 151, 50, 148, 148, 148, 142, + 142, 142, 142, 142, 142, 142, 142, 148, 148, 148, 148, 152, 148, 148, 50, + 84, 91, 84, 84, 142, 142, 142, 153, 153, 153, 153, 153, 153, 153, 153, + 50, 50, 142, 142, 86, 86, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 86, 55, 50, 50, 50, 50, 50, 50, 149, 149, 149, 149, 149, 149, 149, + 149, 50, 142, 148, 148, 77, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 50, + 50, 77, 77, 50, 50, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 77, 149, 149, 149, 149, + 149, 149, 149, 77, 149, 77, 77, 77, 149, 149, 149, 149, 77, 77, 155, 50, + 156, 148, 148, 142, 142, 142, 142, 77, 77, 148, 148, 77, 77, 157, 157, + 152, 50, 77, 77, 77, 77, 77, 77, 77, 77, 156, 77, 77, 77, 77, 153, 153, + 77, 153, 50, 50, 142, 142, 77, 77, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 149, 149, 89, 89, 158, 158, 158, 158, 158, 158, 83, 89, + 50, 86, 84, 77, 77, 142, 142, 148, 77, 50, 50, 50, 50, 50, 50, 77, 77, + 77, 77, 50, 50, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, + 77, 50, 159, 77, 50, 159, 77, 50, 50, 77, 77, 155, 77, 148, 148, 148, + 142, 142, 77, 77, 77, 77, 142, 142, 77, 77, 142, 142, 160, 77, 77, 77, + 142, 77, 77, 77, 77, 77, 77, 77, 159, 159, 159, 50, 77, 159, 77, 77, 77, + 77, 77, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 142, + 142, 50, 50, 50, 142, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 142, + 142, 148, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 77, 50, + 50, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 77, 149, 149, 149, 149, 149, 149, 149, 77, + 149, 149, 77, 149, 149, 149, 149, 149, 77, 77, 155, 50, 148, 148, 148, + 142, 142, 142, 142, 142, 77, 142, 142, 148, 77, 148, 148, 152, 77, 77, + 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, + 142, 142, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 86, + 89, 77, 77, 77, 77, 77, 77, 77, 149, 142, 142, 142, 142, 142, 142, 77, + 142, 148, 148, 77, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 50, 50, 77, + 77, 50, 50, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 77, 149, 149, 149, 149, 149, 149, + 149, 77, 149, 149, 77, 149, 149, 149, 149, 149, 77, 77, 155, 50, 156, + 142, 148, 142, 142, 142, 142, 77, 77, 148, 157, 77, 77, 157, 157, 152, + 77, 77, 77, 77, 77, 77, 77, 142, 161, 156, 77, 77, 77, 77, 153, 153, 77, + 149, 50, 50, 142, 142, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 83, 149, 158, 158, 158, 158, 158, 158, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 142, 50, 77, 50, 50, 50, 50, 50, 50, 77, 77, 77, 50, 50, + 50, 77, 50, 50, 162, 50, 77, 77, 77, 50, 50, 77, 50, 77, 50, 50, 77, 77, + 77, 50, 50, 77, 77, 77, 50, 50, 50, 77, 77, 77, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 156, 148, 142, 148, 148, 77, 77, + 77, 148, 148, 148, 77, 157, 157, 157, 160, 77, 77, 50, 77, 77, 77, 77, + 77, 77, 156, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 158, 158, 158, 88, 88, 88, + 88, 88, 88, 89, 88, 77, 77, 77, 77, 77, 142, 148, 148, 148, 142, 50, 50, + 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 77, 50, 50, 50, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 77, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 77, 77, 155, 50, 142, 142, 142, 148, 148, 148, + 148, 77, 142, 142, 163, 77, 142, 142, 142, 152, 77, 77, 77, 77, 77, 77, + 77, 164, 165, 77, 149, 149, 149, 77, 50, 50, 77, 77, 50, 50, 142, 142, + 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, + 77, 77, 77, 86, 166, 166, 166, 166, 166, 166, 166, 83, 50, 142, 148, 148, + 86, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 77, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, + 77, 77, 155, 50, 148, 167, 168, 148, 156, 148, 148, 77, 167, 168, 168, + 77, 168, 168, 142, 160, 77, 77, 77, 77, 77, 77, 77, 156, 156, 77, 77, 77, + 77, 77, 50, 50, 50, 77, 50, 50, 142, 142, 77, 77, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 77, 50, 50, 148, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 142, 142, 148, 148, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 77, 50, 50, 50, 77, 50, 50, 50, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 160, 160, 50, 156, 148, 148, 142, 142, 142, 142, 77, + 148, 148, 148, 77, 157, 157, 157, 152, 169, 83, 77, 77, 77, 77, 50, 50, + 50, 156, 158, 158, 158, 158, 158, 158, 158, 50, 50, 50, 142, 142, 77, 77, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 83, 50, 50, 50, 50, 50, 50, 77, 142, 148, 148, + 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 77, 50, 77, 77, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 170, 77, + 77, 77, 77, 156, 148, 148, 142, 142, 142, 77, 142, 77, 148, 148, 157, + 148, 157, 157, 157, 156, 77, 77, 77, 77, 77, 77, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 77, 77, 148, 148, 86, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 142, 50, 171, 142, 142, 142, 142, 172, 172, 160, 77, 77, 77, 77, 89, 50, + 50, 50, 50, 50, 50, 55, 142, 173, 173, 173, 173, 142, 142, 142, 86, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 86, 86, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 77, 50, + 77, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 77, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 142, 50, 171, 142, 142, 142, 142, 174, + 174, 160, 142, 142, 50, 77, 77, 50, 50, 50, 50, 50, 77, 55, 77, 175, 175, + 175, 175, 142, 142, 142, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 77, 77, 176, 176, 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 50, 83, 83, 83, 86, 86, 86, 86, 86, 86, 86, 86, 177, 86, + 86, 86, 86, 86, 86, 83, 86, 83, 83, 83, 91, 91, 83, 83, 83, 83, 83, 83, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 83, 91, 83, 91, 83, 178, 179, 180, 179, + 180, 148, 148, 50, 50, 50, 159, 50, 50, 50, 50, 77, 50, 50, 50, 50, 159, + 50, 50, 50, 50, 159, 50, 50, 50, 50, 159, 50, 50, 50, 50, 159, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 159, 50, 50, 50, 77, 77, 77, 77, + 181, 182, 183, 184, 183, 183, 185, 183, 185, 182, 182, 182, 182, 142, + 148, 182, 183, 84, 84, 160, 86, 84, 84, 50, 50, 50, 50, 50, 142, 142, + 142, 142, 142, 142, 183, 142, 142, 142, 142, 77, 142, 142, 142, 142, 183, + 142, 142, 142, 142, 183, 142, 142, 142, 142, 183, 142, 142, 142, 142, + 183, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 183, + 142, 142, 142, 77, 83, 83, 83, 83, 83, 83, 83, 83, 91, 83, 83, 83, 83, + 83, 83, 77, 83, 83, 86, 86, 86, 86, 86, 83, 83, 83, 83, 86, 86, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 150, 149, 149, 149, 149, + 186, 186, 142, 161, 142, 142, 148, 142, 142, 142, 142, 142, 155, 186, + 152, 160, 148, 148, 142, 142, 149, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 86, 86, 86, 86, 86, 86, 149, 149, 149, 149, 149, 149, 148, + 148, 142, 142, 149, 149, 149, 149, 142, 142, 142, 149, 186, 186, 186, + 149, 149, 186, 186, 186, 186, 186, 186, 186, 149, 149, 149, 142, 142, + 142, 142, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 142, 186, 148, 142, 142, 186, 186, 186, 186, 186, 186, 91, 149, 186, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 186, 186, 186, 142, 83, + 83, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 77, 46, 77, 77, 77, 77, 77, 46, 77, 77, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 86, 53, 49, 49, 49, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 188, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, + 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 190, 190, 190, 190, 190, 190, 190, 190, 190, + 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, + 190, 190, 190, 190, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, + 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, + 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, + 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, + 191, 191, 191, 191, 191, 191, 191, 191, 191, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 77, + 77, 50, 50, 50, 50, 50, 50, 50, 77, 50, 77, 50, 50, 50, 50, 77, 77, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 77, 50, 50, 50, 50, 77, 77, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 77, 77, 50, 50, 50, + 50, 50, 50, 50, 77, 50, 77, 50, 50, 50, 50, 77, 77, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, + 50, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, + 84, 84, 84, 86, 86, 86, 86, 86, 86, 86, 86, 86, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 77, 77, 77, 77, 77, 77, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 77, 77, 49, 49, + 49, 49, 49, 49, 77, 77, 87, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 83, 86, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 192, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 179, 180, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 86, 86, 86, 193, 193, 193, + 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 142, 142, + 160, 194, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 142, 142, 194, 86, 86, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 142, 142, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 77, 50, 50, 50, 77, 142, 142, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 142, 142, 148, 142, + 142, 142, 142, 142, 142, 142, 148, 148, 148, 148, 148, 148, 148, 148, + 142, 148, 148, 142, 142, 142, 142, 142, 142, 142, 142, 142, 152, 142, 86, + 86, 86, 55, 86, 86, 86, 89, 50, 84, 77, 77, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 77, 77, 77, 77, 77, 77, 166, 166, 166, 166, 166, 166, + 166, 166, 166, 166, 77, 77, 77, 77, 77, 77, 145, 145, 145, 145, 145, 145, + 87, 145, 145, 145, 145, 142, 142, 142, 195, 142, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 55, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, + 50, 50, 50, 50, 50, 142, 142, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 93, 50, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 142, 142, + 142, 148, 148, 148, 148, 142, 142, 148, 148, 148, 77, 77, 77, 77, 148, + 148, 142, 148, 148, 148, 148, 148, 148, 92, 84, 91, 77, 77, 77, 77, 88, + 77, 77, 77, 145, 145, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 50, 50, 50, 50, + 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, + 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 158, 77, 77, + 77, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 84, 91, 148, 148, 142, 77, 77, 86, 86, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 148, 142, 148, 142, 142, 142, + 142, 142, 142, 142, 77, 152, 186, 142, 186, 186, 142, 142, 142, 142, 142, + 142, 142, 142, 148, 148, 148, 148, 148, 148, 142, 142, 84, 84, 84, 84, + 84, 84, 84, 84, 77, 77, 91, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 77, 77, 77, 77, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 77, 77, 77, 77, 77, 77, 86, 86, 86, 86, 86, 86, 86, 55, 86, 86, 86, + 86, 86, 86, 77, 77, 84, 84, 84, 84, 84, 91, 91, 91, 91, 91, 91, 84, 84, + 91, 85, 91, 91, 84, 84, 91, 91, 84, 84, 84, 84, 84, 91, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 91, 77, 77, 84, + 84, 84, 84, 84, 84, 91, 84, 84, 84, 84, 196, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 142, 142, 142, 142, + 148, 50, 162, 50, 162, 50, 162, 149, 150, 50, 162, 50, 50, 50, 162, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 155, 156, 142, 142, 142, 142, 142, 168, 142, 168, + 148, 148, 157, 157, 142, 168, 197, 149, 149, 149, 149, 149, 149, 149, + 149, 77, 86, 86, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 86, + 86, 86, 86, 86, 86, 86, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 84, 91, + 84, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 83, 83, 83, 86, 86, + 86, 142, 142, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 148, 142, 142, 142, 142, 148, 148, 142, 142, + 194, 152, 142, 142, 149, 149, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 50, 149, 149, 149, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 155, 148, 142, 142, 148, 148, + 148, 142, 148, 142, 142, 142, 194, 194, 77, 77, 77, 77, 77, 77, 77, 77, + 86, 86, 86, 86, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 148, 148, 148, 148, 148, 148, 148, 148, 142, 142, 142, + 142, 142, 142, 142, 142, 148, 148, 142, 155, 77, 77, 77, 86, 86, 86, 86, + 86, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, 50, 50, + 50, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 55, 55, 55, 55, 55, 55, 86, 86, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 46, 49, 77, 77, 77, 77, 77, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 77, 77, 46, 46, 46, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, 77, + 77, 77, 77, 77, 77, 84, 84, 84, 86, 198, 91, 91, 91, 91, 91, 84, 84, 91, + 91, 91, 91, 84, 148, 198, 198, 198, 198, 198, 198, 198, 50, 50, 50, 50, + 91, 50, 50, 50, 50, 50, 50, 84, 50, 50, 148, 84, 84, 50, 77, 77, 77, 77, + 77, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 53, 53, 53, 55, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 55, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 55, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 53, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 84, 84, 91, 84, 84, + 84, 84, 84, 84, 84, 91, 84, 84, 196, 199, 91, 200, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 201, 93, 93, 91, + 202, 84, 203, 91, 84, 91, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 45, 45, 45, 45, 37, 204, + 49, 49, 46, 49, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, 40, 45, + 40, 45, 40, 45, 46, 49, 46, 49, 46, 49, 45, 45, 45, 45, 45, 45, 45, 45, + 40, 40, 40, 40, 40, 40, 40, 40, 45, 45, 45, 45, 45, 45, 77, 77, 40, 40, + 40, 40, 40, 40, 77, 77, 45, 45, 45, 45, 45, 45, 45, 45, 40, 40, 40, 40, + 40, 40, 40, 40, 45, 45, 45, 45, 45, 45, 45, 45, 40, 40, 40, 40, 40, 40, + 40, 40, 45, 45, 45, 45, 45, 45, 77, 77, 40, 40, 40, 40, 40, 40, 77, 77, + 45, 45, 45, 45, 45, 45, 45, 45, 77, 40, 77, 40, 77, 40, 77, 40, 45, 45, + 45, 45, 45, 45, 45, 45, 40, 40, 40, 40, 40, 40, 40, 40, 45, 205, 45, 205, + 45, 205, 45, 205, 45, 205, 45, 205, 45, 205, 77, 77, 45, 45, 45, 45, 45, + 45, 45, 45, 206, 206, 206, 206, 206, 206, 206, 206, 45, 45, 45, 45, 45, + 45, 45, 45, 206, 206, 206, 206, 206, 206, 206, 206, 45, 45, 45, 45, 45, + 45, 45, 45, 206, 206, 206, 206, 206, 206, 206, 206, 45, 45, 45, 45, 45, + 77, 45, 45, 40, 40, 40, 207, 206, 60, 205, 60, 60, 79, 45, 45, 45, 77, + 45, 45, 40, 207, 40, 207, 206, 79, 79, 79, 45, 45, 45, 205, 77, 77, 45, + 45, 40, 40, 40, 207, 77, 79, 79, 79, 45, 45, 45, 205, 45, 45, 45, 45, 40, + 40, 40, 207, 40, 79, 208, 208, 77, 77, 45, 45, 45, 77, 45, 45, 40, 207, + 40, 207, 206, 208, 60, 77, 209, 209, 210, 210, 210, 210, 210, 210, 210, + 210, 210, 195, 211, 212, 213, 214, 215, 216, 87, 215, 215, 215, 24, 217, + 218, 219, 220, 221, 218, 219, 220, 221, 24, 24, 24, 145, 222, 222, 222, + 24, 223, 224, 225, 226, 227, 228, 229, 23, 230, 115, 230, 231, 232, 24, + 217, 217, 145, 30, 38, 24, 233, 145, 222, 234, 234, 145, 145, 145, 235, + 179, 180, 217, 217, 233, 145, 145, 145, 145, 145, 145, 145, 145, 81, 145, + 234, 145, 145, 217, 145, 145, 145, 145, 145, 145, 145, 210, 195, 195, + 195, 195, 195, 236, 237, 238, 239, 240, 195, 195, 195, 195, 195, 195, + 241, 53, 77, 77, 36, 241, 241, 241, 241, 241, 242, 242, 243, 244, 245, + 246, 241, 36, 36, 36, 36, 241, 241, 241, 241, 241, 242, 242, 243, 244, + 245, 77, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 77, 77, 77, + 89, 89, 89, 89, 89, 89, 89, 89, 247, 248, 89, 89, 25, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 84, 84, 198, + 198, 84, 84, 84, 84, 198, 198, 198, 84, 84, 85, 85, 85, 85, 84, 85, 85, + 85, 198, 198, 84, 91, 84, 198, 198, 91, 91, 91, 91, 84, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 250, 250, 51, 251, 88, 251, + 250, 51, 88, 251, 37, 51, 51, 51, 37, 37, 51, 51, 51, 48, 88, 51, 251, + 88, 81, 51, 51, 51, 51, 51, 88, 88, 250, 251, 252, 88, 51, 88, 253, 88, + 51, 88, 207, 253, 51, 51, 254, 37, 51, 51, 46, 51, 37, 176, 176, 176, + 176, 255, 88, 250, 37, 37, 51, 51, 256, 81, 81, 81, 81, 51, 37, 37, 37, + 37, 88, 81, 88, 88, 49, 83, 257, 257, 257, 39, 39, 257, 257, 257, 257, + 257, 257, 39, 39, 39, 39, 257, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 258, 258, 258, 259, 259, 259, 259, 258, 258, 258, 258, 258, 258, + 258, 258, 258, 258, 259, 259, 259, 259, 259, 259, 193, 193, 193, 46, 49, + 193, 193, 193, 193, 39, 88, 88, 77, 77, 77, 77, 42, 42, 42, 42, 260, 32, + 32, 32, 32, 32, 261, 261, 88, 88, 88, 88, 81, 88, 88, 81, 88, 88, 81, 88, + 88, 28, 28, 88, 88, 88, 261, 88, 88, 88, 88, 88, 88, 88, 88, 88, 262, + 262, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 263, 261, 261, 88, 88, 42, 88, 42, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 262, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 42, 264, 265, 265, 266, 81, 81, 42, 265, 266, 264, 265, 266, 264, 81, 42, + 81, 265, 267, 268, 81, 265, 264, 81, 81, 81, 265, 264, 264, 265, 42, 265, + 265, 264, 264, 42, 266, 42, 266, 42, 42, 42, 42, 265, 269, 256, 265, 256, + 256, 264, 264, 264, 42, 42, 42, 42, 81, 264, 81, 264, 265, 265, 264, 264, + 264, 266, 264, 264, 266, 264, 264, 266, 265, 266, 264, 264, 265, 81, 81, + 81, 81, 81, 265, 264, 264, 264, 81, 81, 81, 81, 81, 81, 81, 81, 81, 264, + 270, 42, 266, 81, 265, 265, 265, 265, 264, 264, 265, 265, 81, 266, 270, + 270, 266, 266, 264, 264, 266, 266, 264, 264, 266, 266, 264, 264, 264, + 264, 264, 264, 266, 266, 265, 265, 266, 266, 265, 265, 266, 266, 264, + 264, 264, 81, 81, 264, 264, 264, 264, 81, 81, 42, 81, 81, 264, 42, 81, + 81, 81, 81, 81, 81, 81, 81, 264, 264, 81, 42, 264, 264, 264, 264, 264, + 264, 266, 266, 266, 266, 264, 264, 264, 264, 264, 264, 264, 264, 264, 81, + 81, 81, 81, 81, 264, 265, 81, 81, 81, 81, 81, 81, 81, 81, 81, 264, 264, + 264, 264, 264, 81, 81, 264, 264, 81, 81, 81, 81, 264, 264, 264, 264, 264, + 264, 264, 264, 264, 264, 266, 266, 266, 266, 264, 264, 264, 264, 264, + 264, 266, 266, 266, 266, 81, 81, 264, 264, 264, 264, 264, 264, 264, 264, + 264, 264, 264, 264, 264, 264, 264, 264, 88, 88, 88, 88, 88, 88, 88, 88, + 179, 180, 179, 180, 88, 88, 88, 88, 88, 88, 262, 88, 88, 88, 88, 88, 88, + 88, 271, 271, 88, 88, 88, 88, 264, 264, 88, 88, 88, 88, 88, 88, 28, 272, + 273, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 88, 81, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 83, + 88, 88, 88, 88, 88, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 28, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 81, 81, + 81, 81, 81, 81, 88, 88, 88, 88, 88, 88, 88, 271, 271, 271, 271, 28, 28, + 28, 271, 28, 28, 271, 88, 88, 88, 88, 28, 28, 28, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 41, 41, 41, 41, 41, 41, - 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 43, 42, - 47, 43, 47, 47, 47, 43, 47, 47, 47, 47, 43, 43, 43, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 38, 43, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 80, 81, 81, 81, 81, 81, - 82, 82, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 38, 43, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 47, - 38, 43, 38, 43, 44, 47, 38, 43, 44, 47, 38, 43, 38, 43, 38, 43, 44, 47, - 38, 43, 38, 43, 38, 43, 44, 47, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, - 38, 43, 44, 47, 38, 43, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 0, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 53, 83, 83, 83, 83, 83, 83, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 35, 47, 83, 84, 0, 0, 26, 26, 85, 0, 86, 81, 81, 81, 81, 86, 81, - 81, 81, 87, 86, 81, 81, 81, 81, 81, 81, 86, 86, 86, 86, 86, 86, 81, 81, - 86, 81, 81, 87, 88, 81, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 98, 99, - 100, 101, 102, 103, 104, 105, 106, 104, 81, 86, 104, 97, 0, 0, 0, 0, 0, - 0, 0, 0, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 0, - 0, 0, 0, 107, 107, 107, 107, 104, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 108, 108, 108, 108, 108, 108, 78, 78, 109, 110, 110, 111, 112, 113, 26, - 26, 81, 81, 81, 81, 81, 81, 81, 81, 114, 115, 116, 113, 117, 113, 113, - 113, 118, 118, 119, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 120, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 121, 122, 123, 114, 115, 116, 124, 125, 126, 126, 127, 86, 81, - 81, 81, 81, 81, 86, 81, 81, 86, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 110, 129, 129, 113, 118, 118, 130, 118, 118, 118, 118, 131, - 131, 131, 131, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 119, 118, 119, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 119, 113, 118, 81, 81, 81, - 81, 81, 81, 81, 108, 26, 81, 81, 81, 81, 86, 81, 120, 120, 81, 81, 26, - 86, 81, 81, 86, 118, 118, 132, 132, 132, 132, 132, 132, 132, 132, 132, - 132, 118, 118, 118, 133, 133, 118, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 0, 117, 118, 134, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 81, 86, 81, - 81, 86, 81, 81, 86, 86, 86, 81, 86, 86, 81, 86, 81, 81, 81, 86, 81, 86, - 81, 86, 81, 86, 81, 81, 0, 0, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 81, 81, 81, 81, 81, 81, 81, 86, 81, 137, 137, 26, - 138, 138, 138, 137, 0, 0, 86, 139, 139, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 81, 81, 81, 81, 137, 81, 81, 81, 81, 81, 81, 81, 81, 81, 137, - 81, 81, 81, 137, 81, 81, 81, 81, 81, 0, 0, 104, 104, 104, 104, 104, 104, - 104, 104, 104, 104, 104, 104, 104, 104, 104, 0, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 86, 86, 86, 0, 0, 104, 0, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 0, 0, 0, 0, 0, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 140, 118, 118, 118, 118, 118, 118, 0, - 108, 108, 0, 0, 0, 0, 0, 81, 81, 86, 86, 86, 81, 81, 81, 81, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 120, 81, 81, 81, - 81, 81, 86, 86, 86, 86, 86, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 108, 86, 81, 81, 86, 81, 81, 86, 81, 81, 81, 86, 86, 86, 121, - 122, 123, 81, 81, 81, 86, 81, 81, 86, 86, 81, 81, 81, 81, 81, 135, 135, - 135, 141, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 142, 48, 48, 48, 48, 48, 48, 48, 142, 48, 48, 142, 48, 48, - 48, 48, 48, 135, 141, 143, 48, 141, 141, 141, 135, 135, 135, 135, 135, - 135, 135, 135, 141, 141, 141, 141, 144, 141, 141, 48, 81, 86, 81, 81, - 135, 135, 135, 145, 145, 145, 145, 145, 145, 145, 145, 48, 48, 135, 135, - 83, 83, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 83, 53, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 135, 141, 141, 0, 48, - 48, 48, 48, 48, 48, 48, 48, 0, 0, 48, 48, 0, 0, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, - 48, 48, 48, 48, 48, 48, 0, 48, 0, 0, 0, 48, 48, 48, 48, 0, 0, 147, 48, - 148, 141, 141, 135, 135, 135, 135, 0, 0, 141, 141, 0, 0, 149, 149, 144, - 48, 0, 0, 0, 0, 0, 0, 0, 0, 148, 0, 0, 0, 0, 145, 145, 0, 145, 48, 48, - 135, 135, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 48, 48, - 85, 85, 150, 150, 150, 150, 150, 150, 80, 85, 48, 83, 81, 0, 0, 135, 135, - 141, 0, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 48, 48, 0, 0, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 0, 48, 48, 48, 48, 48, 48, 48, 0, 48, 145, 0, 48, 145, 0, 48, 48, 0, 0, - 147, 0, 141, 141, 141, 135, 135, 0, 0, 0, 0, 135, 135, 0, 0, 135, 135, - 144, 0, 0, 0, 135, 0, 0, 0, 0, 0, 0, 0, 145, 145, 145, 48, 0, 145, 0, 0, - 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 135, - 135, 48, 48, 48, 135, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 135, 141, 0, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 0, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, - 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 0, 48, 48, 48, 48, 48, 0, 0, 147, - 48, 141, 141, 141, 135, 135, 135, 135, 135, 0, 135, 135, 141, 0, 141, - 141, 144, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, - 135, 135, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 83, 85, - 0, 0, 0, 0, 0, 0, 0, 48, 135, 135, 135, 135, 135, 135, 0, 135, 141, 141, - 0, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 48, 48, 0, 0, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 0, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 0, 48, 48, 48, 48, 48, 0, 0, - 147, 48, 148, 135, 141, 135, 135, 135, 135, 0, 0, 141, 149, 0, 0, 149, - 149, 144, 0, 0, 0, 0, 0, 0, 0, 135, 151, 148, 0, 0, 0, 0, 145, 145, 0, - 48, 48, 48, 135, 135, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 80, 48, 150, 150, 150, 150, 150, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 135, 48, 0, 48, 48, 48, 48, 48, 48, 0, 0, 0, 48, 48, 48, 0, 48, 48, 142, - 48, 0, 0, 0, 48, 48, 0, 48, 0, 48, 48, 0, 0, 0, 48, 48, 0, 0, 0, 48, 48, - 48, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, - 148, 141, 135, 141, 141, 0, 0, 0, 141, 141, 141, 0, 149, 149, 149, 144, - 0, 0, 48, 0, 0, 0, 0, 0, 0, 148, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 150, 150, 150, 26, - 26, 26, 26, 26, 26, 85, 26, 0, 0, 0, 0, 0, 135, 141, 141, 141, 135, 48, - 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 147, - 48, 135, 135, 135, 141, 141, 141, 141, 0, 135, 135, 152, 0, 135, 135, - 135, 144, 0, 0, 0, 0, 0, 0, 0, 153, 154, 0, 48, 48, 48, 0, 0, 48, 0, 0, - 48, 48, 135, 135, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 0, 0, 0, 0, 0, 0, 0, 83, 155, 155, 155, 155, 155, 155, 155, 80, 48, 135, - 141, 141, 83, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 0, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, - 48, 0, 0, 147, 48, 141, 156, 149, 141, 148, 141, 141, 0, 156, 149, 149, - 0, 149, 149, 135, 144, 0, 0, 0, 0, 0, 0, 0, 148, 148, 0, 0, 0, 0, 0, 0, - 48, 48, 0, 48, 48, 135, 135, 0, 0, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 0, 48, 48, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, - 135, 141, 141, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 0, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 144, 144, 48, 148, 141, 141, 135, 135, 135, 135, 0, 141, - 141, 141, 0, 149, 149, 149, 144, 48, 80, 0, 0, 0, 0, 48, 48, 48, 148, - 150, 150, 150, 150, 150, 150, 150, 48, 48, 48, 135, 135, 0, 0, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 80, 48, 48, 48, 48, 48, 48, 0, 135, 141, 141, 0, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 0, - 0, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 157, 0, 0, 0, 0, 148, 141, 141, - 135, 135, 135, 0, 135, 0, 141, 141, 149, 141, 149, 149, 149, 148, 0, 0, - 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, 0, 141, - 141, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 135, 48, 158, 135, 135, 135, 135, 159, 159, 144, 0, - 0, 0, 0, 85, 48, 48, 48, 48, 48, 48, 53, 135, 160, 160, 160, 160, 135, - 135, 135, 83, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 83, 83, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 0, 48, 0, 48, 48, 48, 48, - 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 135, 48, 158, 135, 135, 135, 135, 161, 161, 144, 135, 135, 48, 0, - 0, 48, 48, 48, 48, 48, 0, 53, 0, 162, 162, 162, 162, 135, 135, 135, 0, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, 0, 158, 158, 48, 48, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 48, 80, 80, 80, 83, 83, 83, 83, 83, 83, 83, 83, - 163, 83, 83, 83, 83, 83, 83, 80, 83, 80, 80, 80, 86, 86, 80, 80, 80, 80, - 80, 80, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 80, 86, 80, 86, 80, 164, 165, 166, - 165, 166, 141, 141, 48, 48, 48, 145, 48, 48, 48, 48, 0, 48, 48, 48, 48, - 145, 48, 48, 48, 48, 145, 48, 48, 48, 48, 145, 48, 48, 48, 48, 145, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 145, 48, 48, 48, 0, 0, 0, 0, - 167, 168, 169, 170, 169, 169, 171, 169, 171, 168, 168, 168, 168, 135, - 141, 168, 169, 81, 81, 144, 83, 81, 81, 48, 48, 48, 48, 48, 135, 135, - 135, 135, 135, 135, 169, 135, 135, 135, 135, 0, 135, 135, 135, 135, 169, - 135, 135, 135, 135, 169, 135, 135, 135, 135, 169, 135, 135, 135, 135, - 169, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 169, - 135, 135, 135, 0, 80, 80, 80, 80, 80, 80, 80, 80, 86, 80, 80, 80, 80, 80, - 80, 0, 80, 80, 83, 83, 83, 83, 83, 80, 80, 80, 80, 83, 83, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 142, 48, 48, 48, 48, 141, 141, 135, 151, - 135, 135, 141, 135, 135, 135, 135, 135, 147, 141, 144, 144, 141, 141, - 135, 135, 48, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 83, 83, - 83, 83, 83, 83, 48, 48, 48, 48, 48, 48, 141, 141, 135, 135, 48, 48, 48, - 48, 135, 135, 135, 48, 141, 141, 141, 48, 48, 141, 141, 141, 141, 141, - 141, 141, 48, 48, 48, 135, 135, 135, 135, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 135, 141, 141, 135, 135, 141, 141, 141, 141, 141, - 141, 86, 48, 141, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 141, - 141, 141, 135, 80, 80, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 0, 44, 0, 0, 0, 0, 0, 44, 0, 0, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 83, 51, 47, 47, 47, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 48, 173, 173, 173, 173, 173, 173, 173, 173, - 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 173, 173, 173, 173, 173, - 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, - 173, 173, 173, 173, 173, 173, 173, 173, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 0, 48, 48, 48, 48, 0, 0, 48, 48, 48, 48, 48, 48, 48, 0, 48, 0, 48, 48, - 48, 48, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, 0, 0, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, 0, 0, 48, - 48, 48, 48, 48, 48, 48, 0, 48, 0, 48, 48, 48, 48, 0, 0, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, - 48, 48, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 81, - 81, 81, 83, 83, 83, 83, 83, 83, 83, 83, 83, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 0, - 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 47, 47, 47, 47, 47, 47, 0, 0, - 84, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 80, 83, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 174, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 165, 166, 0, - 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 83, 83, 83, 175, 175, 175, 48, 48, 48, 48, 48, 48, 48, - 48, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 135, 135, 144, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 135, 135, 176, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 135, 135, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 0, 48, 48, 48, 0, 135, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 135, 135, - 141, 135, 135, 135, 135, 135, 135, 135, 141, 141, 141, 141, 141, 141, - 141, 141, 135, 141, 141, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 144, 135, 83, 83, 83, 53, 83, 83, 83, 85, 48, 81, 0, 0, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, 0, 0, 0, 155, 155, 155, 155, - 155, 155, 155, 155, 155, 155, 0, 0, 0, 0, 0, 0, 138, 138, 138, 138, 138, - 138, 84, 138, 138, 138, 138, 135, 135, 135, 177, 135, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 53, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 48, 48, - 48, 48, 48, 135, 135, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 88, 48, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 135, 135, 135, 141, 141, 141, 141, - 135, 135, 141, 141, 141, 0, 0, 0, 0, 141, 141, 135, 141, 141, 141, 141, - 141, 141, 87, 81, 86, 0, 0, 0, 0, 26, 0, 0, 0, 138, 138, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 0, 0, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 0, 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 150, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 81, 86, 141, 141, 135, 0, 0, 83, 83, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 141, 135, 141, - 135, 135, 135, 135, 135, 135, 135, 0, 144, 141, 135, 141, 141, 135, 135, - 135, 135, 135, 135, 135, 135, 141, 141, 141, 141, 141, 141, 135, 135, 81, - 81, 81, 81, 81, 81, 81, 81, 0, 0, 86, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 0, 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 0, 0, 0, 0, 0, 0, 83, 83, 83, 83, 83, 83, 83, 53, 83, 83, 83, - 83, 83, 83, 0, 0, 81, 81, 81, 81, 81, 86, 86, 86, 86, 86, 86, 81, 81, 86, - 82, 86, 86, 81, 81, 86, 86, 81, 81, 81, 81, 81, 86, 81, 81, 81, 81, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, - 135, 135, 135, 141, 48, 142, 48, 142, 48, 142, 48, 142, 48, 142, 48, 48, - 48, 142, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 147, - 148, 135, 135, 135, 135, 135, 149, 135, 149, 141, 141, 149, 149, 135, - 149, 176, 48, 48, 48, 48, 48, 48, 48, 48, 0, 83, 83, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 83, 83, 83, 83, 83, 83, 83, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 81, 86, 81, 81, 81, 81, 81, 81, 81, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 83, 83, 83, 135, 135, 141, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 141, 135, 135, 135, 135, 141, 141, 135, 135, - 176, 144, 135, 135, 48, 48, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 147, 141, 135, 135, 141, 141, 141, - 135, 141, 135, 135, 135, 176, 176, 0, 0, 0, 0, 0, 0, 0, 0, 83, 83, 83, - 83, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 141, 141, 141, 141, 141, 141, 141, 141, 135, 135, 135, 135, 135, 135, - 135, 135, 141, 141, 135, 147, 0, 0, 0, 83, 83, 83, 83, 83, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, 48, 48, 48, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 53, 53, 53, 53, 53, 53, 83, 83, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 44, 47, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 44, 44, - 44, 83, 83, 83, 83, 83, 83, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 81, 81, 81, - 83, 178, 86, 86, 86, 86, 86, 81, 81, 86, 86, 86, 86, 81, 141, 178, 178, - 178, 178, 178, 178, 178, 48, 48, 48, 48, 86, 48, 48, 48, 48, 48, 48, 81, - 48, 48, 141, 81, 81, 48, 0, 0, 0, 0, 0, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 51, 51, 51, 53, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 53, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 53, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 51, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 81, 81, 86, 81, 81, 81, 81, 81, 81, 81, 86, 81, 81, 179, - 180, 86, 181, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 182, 88, 88, 86, 183, 81, 184, 86, 81, 86, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 43, 43, 43, 43, 35, 185, 47, 47, 44, 47, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, - 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 38, 43, 44, 47, 44, 47, 44, - 47, 43, 43, 43, 43, 43, 43, 43, 43, 38, 38, 38, 38, 38, 38, 38, 38, 43, - 43, 43, 43, 43, 43, 0, 0, 38, 38, 38, 38, 38, 38, 0, 0, 43, 43, 43, 43, - 43, 43, 43, 43, 38, 38, 38, 38, 38, 38, 38, 38, 43, 43, 43, 43, 43, 43, - 43, 43, 38, 38, 38, 38, 38, 38, 38, 38, 43, 43, 43, 43, 43, 43, 0, 0, 38, - 38, 38, 38, 38, 38, 0, 0, 43, 43, 43, 43, 43, 43, 43, 43, 0, 38, 0, 38, - 0, 38, 0, 38, 43, 43, 43, 43, 43, 43, 43, 43, 38, 38, 38, 38, 38, 38, 38, - 38, 43, 186, 43, 186, 43, 186, 43, 186, 43, 186, 43, 186, 43, 186, 0, 0, - 43, 43, 43, 43, 43, 43, 43, 43, 187, 187, 187, 187, 187, 187, 187, 187, - 43, 43, 43, 43, 43, 43, 43, 43, 187, 187, 187, 187, 187, 187, 187, 187, - 43, 43, 43, 43, 43, 43, 43, 43, 187, 187, 187, 187, 187, 187, 187, 187, - 43, 43, 43, 43, 43, 0, 43, 43, 38, 38, 38, 188, 187, 58, 186, 58, 58, 76, - 43, 43, 43, 0, 43, 43, 38, 188, 38, 188, 187, 76, 76, 76, 43, 43, 43, - 186, 0, 0, 43, 43, 38, 38, 38, 188, 0, 76, 76, 76, 43, 43, 43, 186, 43, - 43, 43, 43, 38, 38, 38, 188, 38, 76, 189, 189, 0, 0, 43, 43, 43, 0, 43, - 43, 38, 188, 38, 188, 187, 189, 58, 0, 190, 190, 191, 191, 191, 191, 191, - 191, 191, 191, 191, 177, 177, 177, 192, 193, 194, 195, 84, 194, 194, 194, - 22, 196, 197, 198, 199, 200, 197, 198, 199, 200, 22, 22, 22, 138, 201, - 201, 201, 22, 202, 203, 204, 205, 206, 207, 208, 21, 209, 110, 209, 210, - 211, 22, 196, 196, 138, 28, 36, 22, 196, 138, 201, 212, 212, 138, 138, - 138, 213, 165, 166, 196, 196, 196, 138, 138, 138, 138, 138, 138, 138, - 138, 78, 138, 212, 138, 138, 196, 138, 138, 138, 138, 138, 138, 138, 191, - 177, 177, 177, 177, 177, 0, 214, 215, 216, 217, 177, 177, 177, 177, 177, - 177, 218, 51, 0, 0, 34, 218, 218, 218, 218, 218, 219, 219, 220, 221, 222, - 223, 218, 34, 34, 34, 34, 218, 218, 218, 218, 218, 219, 219, 220, 221, - 222, 0, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 0, 0, 0, 85, - 85, 85, 85, 85, 85, 85, 85, 224, 225, 85, 85, 23, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 81, 178, 178, 81, 81, 81, 81, 178, 178, - 178, 81, 81, 82, 82, 82, 82, 81, 82, 82, 82, 178, 178, 81, 86, 81, 178, - 178, 86, 86, 86, 86, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 226, 226, 49, 227, 26, 227, 226, 49, 26, 227, 35, 49, 49, 49, 35, 35, 49, - 49, 49, 46, 26, 49, 227, 26, 78, 49, 49, 49, 49, 49, 26, 26, 226, 227, - 227, 26, 49, 26, 228, 26, 49, 26, 188, 228, 49, 49, 229, 35, 49, 49, 44, - 49, 35, 158, 158, 158, 158, 35, 26, 226, 35, 35, 49, 49, 230, 78, 78, 78, - 78, 49, 35, 35, 35, 35, 26, 78, 26, 26, 47, 80, 231, 231, 231, 37, 37, - 231, 231, 231, 231, 231, 231, 37, 37, 37, 37, 231, 232, 232, 232, 232, - 232, 232, 232, 232, 232, 232, 232, 232, 233, 233, 233, 233, 232, 232, - 232, 232, 232, 232, 232, 232, 232, 232, 233, 233, 233, 233, 233, 233, - 175, 175, 175, 44, 47, 175, 175, 175, 175, 37, 26, 26, 0, 0, 0, 0, 40, - 40, 40, 40, 40, 30, 30, 30, 30, 30, 234, 234, 26, 26, 26, 26, 78, 26, 26, - 78, 26, 26, 78, 26, 26, 26, 26, 26, 26, 26, 234, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 30, 30, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 235, 234, 234, 26, 26, 40, 26, 40, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 30, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 40, 236, 237, 237, 238, 78, 78, 40, 237, 238, 236, 237, - 238, 236, 78, 40, 78, 237, 239, 240, 78, 237, 236, 78, 78, 78, 237, 236, - 236, 237, 40, 237, 237, 236, 236, 40, 238, 40, 238, 40, 40, 40, 40, 237, - 241, 230, 237, 230, 230, 236, 236, 236, 40, 40, 40, 40, 78, 236, 78, 236, - 237, 237, 236, 236, 236, 238, 236, 236, 238, 236, 236, 238, 237, 238, - 236, 236, 237, 78, 78, 78, 78, 78, 237, 236, 236, 236, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 236, 242, 40, 238, 78, 237, 237, 237, 237, 236, 236, - 237, 237, 78, 238, 242, 242, 238, 238, 236, 236, 238, 238, 236, 236, 238, - 238, 236, 236, 236, 236, 236, 236, 238, 238, 237, 237, 238, 238, 237, - 237, 238, 238, 236, 236, 236, 78, 78, 236, 236, 236, 236, 78, 78, 40, 78, - 78, 236, 40, 78, 78, 78, 78, 78, 78, 78, 78, 236, 236, 78, 40, 236, 236, - 236, 236, 236, 236, 238, 238, 238, 238, 236, 236, 236, 236, 236, 236, - 236, 236, 236, 78, 78, 78, 78, 78, 236, 237, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 236, 236, 236, 236, 236, 78, 78, 236, 236, 78, 78, 78, 78, 236, - 236, 236, 236, 236, 236, 236, 236, 236, 236, 238, 238, 238, 238, 236, - 236, 236, 236, 236, 236, 238, 238, 238, 238, 78, 78, 236, 236, 236, 236, - 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 26, 26, 26, - 26, 26, 26, 26, 26, 165, 166, 165, 166, 26, 26, 26, 26, 26, 26, 30, 26, - 26, 26, 26, 26, 26, 26, 243, 243, 26, 26, 26, 26, 236, 236, 26, 26, 26, - 26, 26, 26, 26, 244, 245, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 26, 78, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 80, 26, 26, 26, 26, 26, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 78, 78, 78, 78, 78, 78, 26, 26, 26, 26, 26, 26, 26, 243, 243, - 243, 243, 26, 26, 26, 243, 26, 26, 243, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, - 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, - 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, - 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, - 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, - 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, - 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 231, 247, - 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, - 247, 247, 247, 247, 247, 247, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 26, 26, 26, 26, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 26, 26, 30, 30, 30, 30, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 30, 30, 26, 30, 30, 30, 30, 30, 30, 30, 26, 26, 26, - 26, 26, 26, 26, 26, 30, 30, 26, 26, 30, 40, 26, 26, 26, 26, 30, 30, 26, - 26, 30, 40, 26, 26, 26, 26, 30, 30, 30, 26, 26, 30, 26, 26, 30, 30, 30, - 30, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 30, - 30, 30, 30, 26, 26, 26, 26, 26, 26, 26, 26, 26, 30, 26, 26, 26, 26, 26, - 26, 26, 26, 78, 78, 78, 78, 78, 248, 248, 78, 26, 26, 26, 26, 26, 30, 30, - 26, 26, 30, 26, 26, 26, 26, 30, 30, 26, 26, 26, 26, 243, 243, 26, 26, 26, - 26, 26, 26, 30, 26, 30, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 243, 243, 243, 243, 243, 243, 243, 243, 26, 26, 26, - 26, 26, 26, 26, 26, 30, 26, 30, 26, 26, 26, 26, 26, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 30, 30, 26, 30, 30, 30, 26, 30, 30, 30, 30, 26, 30, 30, - 26, 40, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, 243, 243, 243, 243, 243, 26, - 26, 26, 243, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 30, 30, 26, 243, 26, - 26, 26, 26, 26, 26, 26, 26, 243, 243, 80, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 243, 243, 30, 26, 26, 26, 26, 243, 243, - 30, 30, 30, 30, 30, 30, 30, 30, 243, 30, 30, 30, 30, 30, 243, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 26, 30, 26, 26, 26, 26, 30, 30, - 243, 30, 30, 30, 30, 30, 30, 30, 243, 243, 30, 243, 30, 30, 30, 30, 243, - 30, 30, 243, 30, 30, 26, 26, 26, 26, 26, 243, 26, 26, 26, 26, 243, 243, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 30, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, 26, 243, 26, 26, 26, 26, - 243, 243, 243, 26, 243, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 165, 166, 165, 166, 165, 166, 165, 166, 165, 166, 165, - 166, 165, 166, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 155, - 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, - 155, 155, 155, 155, 155, 26, 243, 243, 243, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, 236, 78, 78, - 236, 236, 165, 166, 78, 236, 236, 78, 236, 236, 236, 78, 78, 78, 78, 78, - 236, 236, 236, 236, 78, 78, 78, 78, 78, 236, 236, 236, 78, 78, 78, 236, - 236, 236, 236, 9, 10, 9, 10, 9, 10, 9, 10, 165, 166, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 165, 166, 9, 10, 165, 166, 165, 166, 165, 166, 165, 166, 165, 166, 165, - 166, 165, 166, 165, 166, 165, 166, 78, 78, 236, 236, 236, 236, 236, 236, - 78, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, - 78, 78, 78, 78, 78, 78, 78, 78, 236, 78, 78, 78, 78, 78, 78, 78, 236, - 236, 236, 236, 236, 236, 78, 78, 78, 236, 78, 78, 78, 78, 236, 236, 236, - 236, 236, 78, 236, 236, 78, 78, 165, 166, 165, 166, 236, 78, 78, 78, 78, - 236, 78, 236, 236, 236, 78, 78, 236, 236, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 236, 236, 236, 236, 236, 236, 78, 78, 165, 166, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 236, 236, 230, 236, 236, 236, 236, 236, - 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 78, 236, 236, 236, - 236, 78, 78, 236, 78, 236, 78, 78, 236, 78, 236, 236, 236, 236, 78, 78, - 78, 78, 78, 236, 236, 78, 78, 78, 78, 78, 78, 236, 236, 236, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 236, 236, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 236, - 236, 78, 78, 78, 78, 236, 236, 236, 236, 78, 236, 236, 78, 78, 236, 230, - 220, 220, 78, 78, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, - 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, - 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, - 236, 236, 236, 236, 78, 78, 236, 236, 236, 236, 236, 236, 236, 236, 78, - 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, - 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, - 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 78, 78, 78, - 78, 78, 249, 78, 236, 78, 78, 78, 236, 236, 236, 236, 236, 78, 78, 78, - 78, 78, 236, 236, 236, 78, 78, 78, 78, 236, 78, 78, 78, 236, 236, 236, - 236, 236, 78, 236, 78, 78, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, 243, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 26, 26, 78, 78, 78, 78, 78, 78, 26, 26, 26, 243, 26, 26, - 26, 26, 243, 30, 30, 30, 30, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 250, 26, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 44, 47, - 44, 44, 44, 47, 47, 44, 47, 44, 47, 44, 47, 44, 44, 44, 44, 47, 44, 47, - 47, 44, 47, 47, 47, 47, 47, 47, 51, 51, 44, 44, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, - 44, 47, 44, 47, 47, 26, 26, 26, 26, 26, 26, 44, 47, 44, 47, 81, 81, 81, - 44, 47, 0, 0, 0, 0, 0, 138, 138, 138, 138, 155, 138, 138, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 0, 47, 0, - 0, 0, 0, 0, 47, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 51, 83, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 144, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 0, 48, - 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, - 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, - 0, 48, 48, 48, 48, 48, 48, 48, 0, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 138, 138, 28, 36, 28, 36, 138, 138, 138, 28, 36, 138, 28, - 36, 138, 138, 138, 138, 138, 138, 138, 138, 138, 84, 138, 138, 84, 138, - 28, 36, 138, 138, 28, 36, 165, 166, 165, 166, 165, 166, 165, 166, 138, - 138, 138, 138, 138, 52, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, - 84, 84, 138, 138, 138, 138, 84, 138, 199, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 26, 26, 138, 138, 138, 165, 166, 165, - 166, 165, 166, 165, 166, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 0, 243, 243, 243, 243, 251, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 251, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, - 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 252, 253, - 253, 253, 243, 254, 172, 255, 256, 257, 256, 257, 256, 257, 256, 257, - 256, 257, 243, 243, 256, 257, 256, 257, 256, 257, 256, 257, 258, 259, - 260, 260, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 261, 262, - 263, 264, 265, 265, 258, 254, 254, 254, 254, 254, 251, 243, 266, 266, - 266, 254, 172, 253, 243, 26, 0, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 267, 172, 267, 172, 267, 172, 267, 172, 267, 172, 267, - 172, 267, 172, 267, 172, 267, 172, 267, 172, 267, 172, 267, 172, 172, - 267, 172, 267, 172, 267, 172, 172, 172, 172, 172, 172, 267, 267, 172, - 267, 267, 172, 267, 267, 172, 267, 267, 172, 267, 267, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 267, 172, 172, 0, 0, 268, 268, 269, 269, 254, - 270, 271, 258, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 267, 172, 267, 172, 267, 172, 267, 172, 267, 172, 267, 172, 267, 172, - 267, 172, 267, 172, 267, 172, 267, 172, 267, 172, 172, 267, 172, 267, - 172, 267, 172, 172, 172, 172, 172, 172, 267, 267, 172, 267, 267, 172, - 267, 267, 172, 267, 267, 172, 267, 267, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 267, 172, 172, 267, 267, 267, 267, 253, 254, 254, 270, 271, 0, - 0, 0, 0, 0, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 0, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, - 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, - 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, - 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, - 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, - 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, - 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 0, 272, - 272, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 251, 251, 0, 273, 273, - 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, - 275, 275, 275, 275, 251, 276, 276, 276, 276, 276, 276, 276, 276, 276, - 276, 276, 276, 276, 276, 276, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 251, 251, 251, 272, 273, 273, 273, 273, - 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, - 276, 276, 276, 276, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 251, 251, 251, 251, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, + 274, 274, 274, 274, 275, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 251, 251, 251, 251, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 251, 251, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 251, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 254, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 0, 0, 0, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 53, 53, 53, 53, 53, 53, 83, 83, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 53, 138, 138, 138, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, - 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, - 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, - 47, 44, 47, 44, 47, 44, 47, 44, 47, 48, 81, 82, 82, 82, 138, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 138, 52, 44, 47, 44, 47, 44, 47, 44, 47, 44, - 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, - 47, 51, 51, 81, 81, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 81, 81, 83, - 83, 83, 83, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 54, 54, 44, 47, 44, 47, 44, 47, 44, 47, 44, - 47, 44, 47, 44, 47, 47, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, - 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, - 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, - 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 51, 47, 47, - 47, 47, 47, 47, 47, 47, 44, 47, 44, 47, 44, 44, 47, 44, 47, 44, 47, 44, - 47, 44, 47, 52, 277, 277, 44, 47, 44, 47, 48, 44, 47, 44, 47, 47, 47, 44, - 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, - 47, 44, 44, 44, 44, 44, 47, 44, 44, 44, 44, 44, 47, 44, 47, 44, 47, 44, - 47, 44, 47, 44, 47, 44, 47, 44, 47, 44, 44, 44, 44, 47, 44, 47, 44, 44, - 47, 0, 0, 44, 47, 0, 47, 0, 47, 44, 47, 44, 47, 44, 47, 44, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 51, 51, 44, 47, - 48, 51, 51, 47, 48, 48, 48, 48, 48, 48, 48, 135, 48, 48, 48, 144, 48, 48, - 48, 48, 135, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 141, 141, 135, 135, 141, 26, 26, 26, 26, - 144, 0, 0, 0, 150, 150, 150, 150, 150, 150, 80, 80, 85, 229, 0, 0, 0, 0, - 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 138, - 138, 138, 138, 0, 0, 0, 0, 0, 0, 0, 0, 141, 141, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 144, 135, 0, 0, 0, 0, 0, 0, 0, 0, - 83, 83, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, 0, 0, - 0, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 48, 48, 48, 48, 48, 48, 83, 83, 83, 48, 83, 48, 48, 135, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 135, 135, 135, 135, 135, 86, 86, 86, 83, 83, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 141, 176, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 83, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 0, 0, 0, 135, 135, 135, 141, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 147, 141, 141, 135, 135, 135, 135, 141, - 141, 135, 135, 141, 141, 176, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 0, 53, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, - 0, 83, 83, 48, 48, 48, 48, 48, 135, 53, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 48, 48, 48, 48, 48, - 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 135, 135, 135, 135, 135, 135, 141, 141, 135, 135, - 141, 141, 135, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 135, 48, 48, - 48, 48, 48, 48, 48, 48, 135, 141, 0, 0, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 0, 0, 83, 83, 83, 83, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 53, 48, 48, 48, 48, 48, 48, 80, 80, 80, - 48, 141, 135, 141, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 81, 48, 81, 81, 86, 48, 48, 81, 81, 48, 48, 48, 48, 48, 81, 81, 48, - 81, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 48, 48, 53, 83, 83, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 141, 135, 135, 141, 141, 83, 83, 48, 53, 53, 141, 144, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 0, 0, 48, 48, 48, 48, 48, 48, 0, 0, - 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, - 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 0, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 277, - 51, 51, 51, 51, 47, 47, 47, 47, 47, 47, 47, 47, 47, 51, 54, 54, 0, 0, 0, - 0, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 141, 141, 135, 141, 141, 135, 141, 141, - 83, 141, 144, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, - 0, 0, 0, 0, 0, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 279, 279, + 274, 274, 257, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 88, 88, 88, 88, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 262, 262, 262, 262, 262, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 88, 88, 262, 262, 262, + 262, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 262, 262, 88, 262, 262, 262, + 262, 262, 262, 262, 28, 28, 88, 88, 88, 88, 88, 88, 262, 262, 88, 88, 32, + 42, 88, 88, 88, 88, 262, 262, 88, 88, 32, 42, 88, 88, 88, 88, 262, 262, + 262, 88, 88, 262, 88, 88, 262, 262, 262, 262, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 262, 262, 262, 262, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 262, 88, 88, 88, 88, 88, 88, 88, 88, 81, 81, 81, 277, + 277, 278, 278, 81, 28, 28, 28, 28, 28, 262, 262, 88, 88, 262, 88, 88, 88, + 88, 32, 262, 88, 28, 88, 88, 271, 271, 88, 88, 28, 88, 88, 88, 262, 28, + 262, 88, 28, 88, 28, 28, 88, 88, 28, 88, 88, 88, 28, 88, 88, 88, 28, 28, + 279, 279, 279, 279, 279, 279, 279, 279, 28, 28, 28, 88, 88, 88, 88, 88, + 32, 88, 32, 88, 88, 88, 88, 88, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 28, 32, + 262, 88, 32, 262, 32, 28, 262, 32, 262, 262, 88, 262, 262, 88, 42, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 28, 88, 88, 28, 271, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 279, 279, 279, 279, 279, 279, 88, 88, 28, + 271, 28, 28, 28, 28, 88, 28, 88, 28, 28, 88, 262, 262, 28, 271, 88, 88, + 88, 88, 88, 28, 88, 88, 271, 271, 83, 88, 88, 88, 28, 28, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 271, 271, 262, 88, 88, 88, 88, 271, 271, 262, + 262, 32, 262, 262, 262, 262, 262, 271, 32, 262, 32, 262, 32, 271, 262, + 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 88, 262, 88, + 88, 88, 88, 262, 32, 271, 262, 262, 262, 262, 262, 32, 32, 271, 271, 32, + 271, 262, 32, 32, 32, 271, 262, 262, 271, 262, 262, 88, 88, 28, 88, 88, + 271, 88, 88, 28, 28, 271, 271, 28, 28, 88, 28, 88, 88, 28, 88, 28, 88, + 28, 88, 88, 88, 88, 88, 88, 28, 88, 88, 88, 28, 88, 88, 88, 88, 88, 88, + 271, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 28, 28, 88, 88, 88, 88, 88, + 88, 88, 88, 262, 88, 88, 88, 88, 88, 88, 28, 88, 88, 28, 88, 88, 88, 88, + 271, 88, 271, 88, 88, 88, 88, 271, 271, 271, 88, 271, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 28, 28, 88, 88, 88, 179, 180, 179, 180, 179, 180, + 179, 180, 179, 180, 179, 180, 179, 180, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, + 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 88, 271, 271, 271, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 28, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 271, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 271, 264, 81, 81, 264, 264, 179, 180, 81, 264, 264, 81, 264, 264, + 264, 81, 81, 81, 81, 81, 264, 264, 264, 264, 81, 81, 81, 81, 81, 264, + 264, 264, 81, 81, 81, 264, 264, 264, 264, 11, 12, 11, 12, 11, 12, 11, 12, + 179, 180, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 277, 277, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 179, 180, 11, 12, 179, 180, 179, 180, 179, + 180, 179, 180, 179, 180, 179, 180, 179, 180, 179, 180, 179, 180, 81, 81, + 264, 264, 264, 264, 264, 264, 81, 264, 264, 264, 264, 264, 264, 264, 264, + 264, 264, 264, 264, 264, 264, 81, 81, 81, 81, 81, 81, 81, 81, 264, 81, + 81, 81, 81, 81, 81, 81, 264, 264, 264, 264, 264, 264, 81, 81, 81, 264, + 81, 81, 81, 81, 264, 264, 264, 264, 264, 81, 264, 264, 81, 81, 179, 180, + 179, 180, 264, 81, 81, 81, 81, 264, 81, 264, 264, 264, 81, 81, 264, 264, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 264, 264, 264, 264, 264, 264, 81, + 81, 179, 180, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 264, 264, + 256, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, + 264, 264, 264, 81, 264, 264, 264, 264, 81, 81, 264, 81, 264, 81, 81, 264, + 81, 264, 264, 264, 264, 81, 81, 81, 81, 81, 264, 264, 81, 81, 81, 81, 81, + 81, 264, 264, 264, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 264, 264, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 264, 264, 81, 81, 81, 81, 264, 264, 264, 264, 81, + 264, 264, 81, 81, 264, 256, 243, 243, 81, 81, 264, 264, 264, 264, 264, + 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, + 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, + 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 81, 81, 264, 264, 264, + 264, 264, 264, 264, 264, 81, 264, 264, 264, 264, 264, 264, 264, 264, 264, + 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, + 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, 264, + 264, 264, 264, 81, 81, 81, 81, 81, 280, 81, 264, 81, 81, 81, 264, 264, + 264, 264, 264, 81, 81, 81, 81, 81, 264, 264, 264, 81, 81, 81, 81, 264, + 81, 81, 81, 264, 264, 264, 264, 264, 81, 264, 81, 81, 88, 88, 88, 88, 88, + 28, 28, 28, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 271, 271, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 88, 88, 81, 81, 81, 81, 81, 81, + 88, 88, 88, 271, 88, 88, 88, 88, 271, 262, 262, 262, 262, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 77, 77, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 281, 88, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 46, 49, 46, 46, 46, 49, 49, 46, 49, 46, 49, 46, + 49, 46, 46, 46, 46, 49, 46, 49, 49, 46, 49, 49, 49, 49, 49, 49, 53, 53, + 46, 46, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 49, 88, 88, 88, 88, 88, + 88, 46, 49, 46, 49, 84, 84, 84, 46, 49, 77, 77, 77, 77, 77, 145, 145, + 145, 145, 166, 145, 145, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 77, 49, 77, 77, 77, 77, 77, 49, 77, 77, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 77, 77, 77, 77, 77, 77, 77, 53, 86, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 160, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, + 50, 77, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 77, + 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, + 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 77, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 145, 145, 30, 38, 30, 38, 145, + 145, 145, 30, 38, 145, 30, 38, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 87, 145, 145, 87, 145, 30, 38, 145, 145, 30, 38, 179, 180, 179, 180, + 179, 180, 179, 180, 145, 145, 145, 145, 145, 54, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 87, 87, 145, 145, 145, 145, 87, 145, 220, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 88, 88, 145, + 145, 145, 179, 180, 179, 180, 179, 180, 179, 180, 87, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 77, 279, 279, 279, 279, 282, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 282, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 283, 284, 284, 284, 279, 285, 286, 287, 288, 289, 288, + 289, 288, 289, 288, 289, 288, 289, 279, 279, 288, 289, 288, 289, 288, + 289, 288, 289, 290, 291, 292, 292, 279, 287, 287, 287, 287, 287, 287, + 287, 287, 287, 293, 294, 295, 296, 297, 297, 298, 285, 285, 285, 285, + 285, 282, 279, 299, 299, 299, 285, 286, 300, 279, 88, 77, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 301, 286, 301, 286, 301, 286, + 301, 286, 301, 286, 301, 286, 301, 286, 301, 286, 301, 286, 301, 286, + 301, 286, 301, 286, 286, 301, 286, 301, 286, 301, 286, 286, 286, 286, + 286, 286, 301, 301, 286, 301, 301, 286, 301, 301, 286, 301, 301, 286, + 301, 301, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 301, 286, 286, 77, 77, + 302, 302, 303, 303, 285, 304, 305, 290, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 301, 286, 301, 286, 301, 286, 301, 286, 301, + 286, 301, 286, 301, 286, 301, 286, 301, 286, 301, 286, 301, 286, 301, + 286, 286, 301, 286, 301, 286, 301, 286, 286, 286, 286, 286, 286, 301, + 301, 286, 301, 301, 286, 301, 301, 286, 301, 301, 286, 301, 301, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 301, 286, 286, 301, 301, 301, 301, + 284, 285, 285, 304, 305, 77, 77, 77, 77, 77, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 77, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 77, 306, 306, 307, 307, 307, 307, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 279, 279, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 279, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 282, 282, 77, 307, 307, 307, 307, 307, 307, 307, 307, + 307, 307, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 309, 309, 309, 309, 309, 309, 309, 309, 282, 310, + 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 282, 282, 282, 306, 307, 307, 307, 307, 307, 307, 307, 307, 307, 307, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 311, + 308, 311, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 310, 310, 310, + 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 282, 282, 282, 282, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 282, + 282, 282, 282, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 282, 282, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 308, 308, 282, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 172, 172, 280, 172, 280, 172, 172, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 172, 280, 172, 280, 172, 172, 280, 280, 172, - 172, 172, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 281, 281, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 35, 35, 35, 35, 35, 35, 35, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 35, 35, 35, 35, 35, 0, 0, 0, 0, 0, 282, 283, 282, - 284, 284, 284, 284, 284, 284, 284, 284, 284, 219, 282, 282, 282, 282, - 282, 282, 282, 282, 282, 282, 282, 282, 282, 0, 282, 282, 282, 282, 282, - 0, 282, 0, 282, 282, 0, 282, 282, 0, 282, 282, 282, 282, 282, 282, 282, - 282, 282, 284, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 285, 199, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 0, 0, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 286, 26, 26, - 26, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 287, - 287, 287, 287, 287, 287, 287, 288, 289, 287, 0, 0, 0, 0, 0, 0, 81, 81, - 81, 81, 81, 81, 81, 86, 86, 86, 86, 86, 86, 86, 81, 81, 287, 290, 290, - 291, 291, 288, 289, 288, 289, 288, 289, 288, 289, 288, 289, 288, 289, - 288, 289, 288, 289, 253, 253, 288, 289, 287, 287, 287, 287, 291, 291, - 291, 292, 287, 292, 0, 287, 292, 287, 287, 290, 293, 294, 293, 294, 293, - 294, 295, 287, 287, 296, 297, 298, 298, 299, 0, 287, 300, 295, 287, 0, 0, - 0, 0, 131, 131, 131, 118, 131, 0, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 0, 0, 177, 0, 301, 301, 302, 303, 302, 301, 301, 304, 305, 301, 306, - 307, 308, 307, 307, 309, 309, 309, 309, 309, 309, 309, 309, 309, 309, - 307, 301, 310, 311, 310, 301, 301, 312, 312, 312, 312, 312, 312, 312, - 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, - 312, 312, 312, 312, 312, 304, 301, 305, 313, 314, 313, 315, 315, 315, - 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, - 315, 315, 315, 315, 315, 315, 315, 315, 315, 304, 311, 305, 311, 304, - 305, 316, 317, 318, 316, 316, 319, 319, 319, 319, 319, 319, 319, 319, - 319, 319, 320, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - 319, 319, 319, 319, 319, 319, 320, 320, 319, 319, 319, 319, 319, 319, - 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 0, 0, 0, 319, 319, - 319, 319, 319, 319, 0, 0, 319, 319, 319, 319, 319, 319, 0, 0, 319, 319, - 319, 319, 319, 319, 0, 0, 319, 319, 319, 0, 0, 0, 303, 303, 311, 313, - 321, 303, 303, 0, 322, 323, 323, 323, 323, 322, 322, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 324, 324, 324, 26, 30, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 0, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, - 0, 83, 138, 83, 0, 0, 0, 0, 150, 150, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 0, 0, 0, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, - 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, - 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, - 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, 155, - 155, 155, 155, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 155, 155, 26, 80, 80, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 86, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 86, 326, 326, 326, 326, 326, 326, 326, 326, 326, - 326, 326, 326, 326, 326, 326, 326, 326, 326, 326, 326, 326, 326, 326, - 326, 326, 326, 326, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 150, 150, 150, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 175, 48, 48, 48, 48, 48, 48, 48, 48, 175, 0, 0, 0, 0, 0, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 81, 81, - 81, 81, 81, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 0, 83, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 83, 175, 175, - 175, 175, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 0, 0, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 0, 0, 0, - 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 44, 44, 44, 44, 44, 44, 44, 0, 44, - 44, 0, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 0, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 0, 47, 47, 47, 47, 47, 47, 47, 0, - 47, 47, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 142, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 142, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, - 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 53, 51, 51, 51, 51, 51, 0, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 0, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 107, 107, 107, 107, 107, 107, 0, 0, 107, 0, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 0, 107, - 107, 0, 0, 0, 107, 0, 0, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 0, - 104, 327, 327, 327, 327, 327, 327, 327, 327, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 328, 328, 327, 327, 327, 327, 327, 327, 327, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 0, 0, 0, 0, 0, 0, 0, 0, 327, 327, 327, 327, 327, 327, 327, 327, - 327, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 0, 107, 107, 0, 0, 0, 0, 0, 327, 327, 327, 327, - 327, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 327, 327, 327, 327, 327, - 327, 0, 0, 0, 138, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 0, - 0, 0, 0, 327, 327, 107, 107, 327, 327, 327, 327, 327, 327, 327, 327, 327, - 327, 327, 327, 327, 327, 327, 327, 0, 0, 327, 327, 327, 327, 327, 327, - 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, - 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, - 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, 107, 135, - 135, 135, 0, 135, 135, 0, 0, 0, 0, 0, 135, 86, 135, 81, 107, 107, 107, - 107, 0, 107, 107, 107, 0, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 0, 0, 81, 178, 86, 0, 0, 0, 0, 144, 327, - 327, 327, 327, 327, 327, 327, 327, 327, 0, 0, 0, 0, 0, 0, 0, 104, 104, - 104, 104, 104, 104, 104, 104, 104, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 327, 327, - 104, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 327, 327, 327, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, 107, 107, - 107, 107, 107, 328, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 81, 86, 0, 0, 0, 0, 327, 327, 327, 327, 327, 104, - 104, 104, 104, 104, 104, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 0, 138, 138, 138, 138, - 138, 138, 138, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 327, 327, - 327, 327, 327, 327, 327, 327, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 0, 0, 0, - 327, 327, 327, 327, 327, 327, 327, 327, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 0, 0, - 0, 0, 0, 104, 104, 104, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 327, - 327, 327, 327, 327, 327, 327, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, - 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, - 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, - 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 0, 0, 0, - 0, 0, 0, 0, 327, 327, 327, 327, 327, 327, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 81, 81, 81, 81, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 107, 107, 107, 107, 137, 107, 329, 329, 329, - 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 329, 0, 0, 0, 81, 81, 81, 81, 81, 84, 137, 330, 330, - 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, - 330, 330, 330, 330, 330, 330, 0, 0, 0, 0, 0, 0, 0, 0, 331, 331, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 332, 332, 332, 332, 332, 332, 332, - 332, 332, 332, 332, 332, 332, 332, 332, 332, 332, 332, 332, 332, 332, - 332, 332, 332, 332, 332, 332, 332, 332, 332, 332, 0, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 0, 81, 81, 102, 0, 0, - 107, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 118, 118, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 135, 86, 86, 86, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 327, 327, 327, 327, 327, 327, - 327, 327, 327, 327, 107, 0, 0, 0, 0, 0, 0, 0, 0, 118, 118, 118, 118, 118, - 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, - 118, 118, 118, 86, 86, 81, 81, 81, 86, 81, 86, 86, 86, 86, 333, 333, 333, - 333, 113, 113, 113, 113, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 81, 86, 81, 86, 104, 104, 104, - 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 327, 327, 327, 327, 327, 327, 327, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 135, 141, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 144, 83, 83, 83, 83, 83, 83, 83, - 0, 0, 0, 0, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, - 155, 155, 155, 155, 155, 155, 155, 155, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 144, 48, 48, 135, 135, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 144, 135, 135, 141, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 142, 48, 142, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 142, 48, 48, 48, 48, 141, 141, 141, - 135, 135, 135, 135, 141, 141, 144, 143, 83, 83, 192, 83, 83, 83, 83, 135, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, - 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, - 0, 0, 0, 81, 81, 81, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 151, 135, 135, 135, 135, 141, 135, 152, 152, 135, - 135, 135, 144, 144, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 83, 83, 83, 83, 48, 141, 141, 48, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 147, 83, 83, 48, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 135, 135, 141, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 141, 141, 141, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 141, 176, 48, 48, 48, 48, 83, 83, 83, 83, 135, 147, 135, 135, 83, - 141, 135, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 48, 83, 48, - 83, 83, 83, 0, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 141, 141, 141, 135, 135, 135, 141, - 141, 135, 176, 147, 135, 83, 83, 83, 83, 83, 83, 135, 48, 48, 135, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 0, 48, 0, - 48, 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 83, 0, 0, 0, 0, 0, 0, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 135, 141, 141, 141, 135, 135, - 135, 135, 135, 135, 147, 144, 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 0, 0, 0, 0, 0, 0, 135, 135, 141, 141, 0, 48, 48, - 48, 48, 48, 48, 48, 48, 0, 0, 48, 48, 0, 0, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, - 48, 48, 48, 48, 48, 0, 48, 48, 0, 48, 48, 48, 48, 48, 0, 147, 147, 48, - 148, 141, 135, 141, 141, 141, 141, 0, 0, 141, 141, 0, 0, 149, 149, 176, - 0, 0, 48, 0, 0, 0, 0, 0, 0, 148, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 141, - 141, 0, 0, 81, 81, 81, 81, 81, 81, 81, 0, 0, 0, 81, 81, 81, 81, 81, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 142, 48, 142, 48, 48, 48, 48, 0, - 48, 0, 0, 142, 0, 48, 142, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 0, 48, 148, 141, 141, 151, 135, 135, 135, - 135, 135, 0, 148, 0, 0, 334, 0, 334, 334, 148, 141, 0, 141, 141, 144, - 176, 144, 48, 135, 48, 83, 83, 0, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 135, - 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 141, 141, 141, 135, 135, 135, 135, 135, 135, 135, 135, 141, - 141, 144, 135, 135, 141, 147, 48, 48, 48, 48, 83, 83, 83, 83, 83, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 83, 83, 0, 83, 81, 48, 48, - 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 148, - 141, 141, 135, 135, 135, 135, 135, 135, 141, 151, 149, 149, 148, 149, - 135, 135, 141, 144, 147, 48, 48, 83, 48, 0, 0, 0, 0, 0, 0, 0, 0, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 148, 141, - 141, 135, 135, 135, 135, 0, 0, 141, 141, 149, 149, 135, 135, 141, 144, - 147, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 48, 48, 48, 48, 135, 135, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 141, 141, 141, 135, - 135, 135, 135, 135, 135, 135, 135, 141, 141, 135, 141, 144, 135, 83, 83, - 83, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 0, 0, 0, 0, 0, 0, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 135, 141, 135, 141, 141, 135, - 135, 135, 135, 135, 135, 176, 147, 48, 83, 0, 0, 0, 0, 0, 0, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, 0, 0, 0, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, - 135, 141, 135, 141, 141, 135, 135, 135, 135, 141, 135, 135, 135, 135, - 144, 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 150, - 150, 83, 83, 83, 80, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 141, 141, 141, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 141, 144, 147, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 150, 150, 150, 150, 150, 150, 150, 150, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 48, 0, 0, 48, 48, 48, 48, - 48, 48, 48, 48, 0, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 148, 141, 141, 141, - 141, 141, 0, 141, 149, 0, 0, 135, 135, 176, 144, 48, 141, 48, 141, 147, - 83, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 141, 141, - 141, 135, 135, 135, 135, 0, 0, 135, 135, 141, 141, 141, 141, 144, 48, 83, - 48, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 48, 135, 135, 135, 135, 135, 135, 156, 156, 135, 135, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 135, 144, 135, 135, 135, 135, 141, 48, 135, 135, 135, - 135, 83, 83, 83, 83, 83, 83, 83, 83, 144, 0, 0, 0, 0, 0, 0, 0, 0, 48, - 135, 135, 135, 135, 135, 135, 141, 141, 135, 135, 135, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 141, 135, 144, 83, 83, 83, 48, 83, 83, 83, 83, 83, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 83, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 141, 135, - 135, 135, 135, 135, 135, 135, 0, 135, 135, 135, 135, 135, 135, 141, 335, - 48, 83, 83, 83, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 150, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 0, 0, 0, 83, 83, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 0, 141, 135, 135, 135, 135, 135, 135, 135, 141, 135, - 135, 141, 135, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 0, 48, 48, 0, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 135, 135, 135, 135, 135, 135, 0, 0, 0, 135, 0, 135, 135, 0, 135, 135, - 135, 147, 135, 144, 144, 48, 135, 0, 0, 0, 0, 0, 0, 0, 0, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, - 48, 0, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 141, 141, 141, 141, 141, 0, 135, 135, 0, 141, 141, 135, 141, 144, 48, 0, - 0, 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 135, 135, 141, 141, 83, 83, 0, 0, - 0, 0, 0, 0, 0, 135, 135, 48, 141, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 141, 141, 135, 135, 135, 135, 135, 0, 0, 0, 141, 141, 135, 176, - 144, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 26, 26, 26, 26, 26, 26, 26, 26, 85, 85, 85, 85, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, - 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, - 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, - 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, - 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, - 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, - 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, - 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 0, - 83, 83, 83, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 83, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 135, 48, 48, 48, 48, 48, 48, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 151, 151, 151, 336, 336, 336, 336, 336, 336, 336, 336, 151, 141, 141, - 141, 135, 135, 144, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, 0, 83, - 83, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 0, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 0, 0, 178, 178, 178, 178, 178, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 81, 81, 81, 81, 81, 81, - 81, 83, 83, 83, 83, 83, 80, 80, 80, 80, 53, 53, 53, 53, 83, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, - 150, 150, 150, 150, 150, 150, 150, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 53, 53, 53, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 173, 337, 142, 142, 53, 53, 83, 83, 83, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 83, 83, 83, 83, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 0, 0, 0, 0, 135, 48, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 0, 0, 0, 0, 0, 0, 0, 135, 135, 135, - 135, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 254, 253, 254, 338, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 339, 339, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 0, 0, 0, 0, 0, 0, 0, 0, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, - 254, 254, 254, 0, 254, 254, 254, 254, 254, 254, 254, 0, 254, 254, 0, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 172, 172, 172, 0, 0, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 172, 172, 172, 172, 0, 0, 0, 0, 0, 0, 0, 0, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 0, 0, 0, 0, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, - 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 0, 0, 80, 135, 178, 83, 177, 177, 177, 177, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 341, 341, 341, 341, 341, 341, 341, 341, - 341, 341, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 0, 0, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 0, 0, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 342, 342, - 342, 342, 342, 342, 342, 343, 343, 178, 178, 178, 80, 80, 80, 344, 343, - 343, 343, 343, 343, 177, 177, 177, 177, 177, 177, 177, 177, 86, 86, 86, - 86, 86, 86, 86, 86, 80, 80, 81, 81, 81, 81, 81, 86, 86, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 342, 342, 342, 342, 342, 342, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 81, 81, 81, 26, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 150, - 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, - 150, 150, 150, 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, - 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, 150, - 150, 0, 0, 0, 0, 0, 0, 0, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 35, 35, 35, 35, 35, 35, - 35, 0, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 49, 0, 49, 49, 0, 0, 49, 0, 0, 49, 49, 0, 0, 49, 49, 49, 49, 0, 49, 49, - 49, 49, 49, 49, 49, 49, 35, 35, 35, 35, 0, 35, 0, 35, 35, 35, 35, 35, 35, - 35, 0, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 49, 49, 49, 49, 49, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 285, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 77, 77, 77, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 55, 55, 55, + 55, 55, 55, 86, 86, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 55, + 145, 145, 145, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 50, 50, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 50, 84, 85, 85, 85, 145, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 145, 54, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 53, 53, 84, 84, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 84, 84, + 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 54, 54, 54, 54, 54, 54, 54, 54, 54, 56, 56, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 49, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 53, 49, 49, 49, 49, 49, 49, 49, 49, 46, 49, 46, 49, 46, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 54, 312, 312, 46, 49, 46, 49, 50, 46, 49, 46, 49, + 49, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 46, 46, 46, 46, 49, 46, 46, 46, 46, 46, 49, 46, 49, + 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 46, 46, 46, 49, 46, + 49, 46, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, 46, 49, + 46, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 53, 53, 53, 53, 46, 49, 50, 53, 53, 49, 50, 50, 50, 50, 50, + 50, 50, 142, 50, 50, 50, 160, 50, 50, 50, 50, 142, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 148, 148, 142, 142, 148, 88, 88, 88, 88, 160, 77, 77, 77, 158, 158, 158, + 158, 158, 158, 83, 83, 89, 254, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 145, 145, 145, 145, 77, + 77, 77, 77, 77, 77, 77, 77, 148, 148, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 160, 142, 77, 77, 77, 77, 77, 77, 77, 77, + 86, 86, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, + 77, 77, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 50, 50, 50, 50, 50, 50, 86, 86, 86, 50, 86, 50, 50, 142, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 142, 142, 142, 142, 142, 91, 91, 91, 86, 86, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 148, 194, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 86, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + 187, 187, 187, 187, 187, 187, 187, 187, 187, 77, 77, 77, 142, 142, 142, + 148, 50, 50, 50, 50, 50, 149, 149, 149, 50, 50, 50, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 155, 148, 148, 142, 142, 142, 142, 148, 148, 142, + 142, 148, 148, 197, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 77, 55, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, + 86, 86, 149, 149, 149, 149, 149, 142, 55, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 149, + 149, 149, 149, 149, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 142, 142, 142, 142, 142, 142, + 148, 148, 142, 142, 148, 148, 142, 142, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 50, 50, 50, 142, 50, 50, 50, 50, 50, 50, 50, 50, 142, 148, 77, 77, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 86, 86, 86, 86, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 55, 149, 149, 149, 50, 50, 50, 83, 83, 83, 149, 186, 142, 186, + 149, 149, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 84, 50, 84, 84, + 91, 50, 50, 84, 84, 50, 50, 50, 50, 50, 84, 84, 50, 84, 50, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 50, 50, 55, 86, 86, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 148, 142, 142, 148, 148, 86, 86, 50, 55, 55, 148, 152, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 77, 77, 50, + 50, 50, 50, 50, 50, 77, 77, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, + 50, 77, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 312, 53, 53, 53, 53, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 53, 56, 56, 77, 77, 77, 77, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 49, 49, 0, 49, 49, 49, 49, 0, - 0, 49, 49, 49, 49, 49, 49, 49, 49, 0, 49, 49, 49, 49, 49, 49, 49, 0, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 49, 49, 0, 49, 49, 49, 49, 0, 49, 49, 49, 49, - 49, 0, 49, 0, 0, 0, 49, 49, 49, 49, 49, 49, 49, 0, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 49, 49, 49, + 49, 49, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 50, + 50, 50, 50, 50, 50, 50, 50, 148, 148, 142, 148, 148, 142, 148, 148, 86, + 148, 160, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, + 77, 77, 77, 77, 77, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 313, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 313, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, + 188, 188, 77, 77, 77, 77, 191, 191, 191, 191, 191, 191, 191, 191, 191, + 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, + 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, + 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 77, 77, 77, + 77, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, + 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, + 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, + 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, + 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, + 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, + 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, + 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, + 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, + 315, 315, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 286, 286, 317, 286, 317, 286, 286, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 286, 317, 286, 317, 286, + 286, 317, 317, 286, 286, 286, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 318, 318, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 37, 37, 37, 37, 37, 37, + 37, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 37, 37, 37, 37, 37, + 77, 77, 77, 77, 77, 319, 320, 319, 321, 321, 321, 321, 321, 321, 321, + 321, 321, 242, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + 319, 319, 90, 319, 319, 319, 319, 319, 90, 319, 90, 319, 319, 90, 319, + 319, 90, 319, 319, 319, 319, 319, 319, 319, 319, 319, 321, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 147, 147, 147, + 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 322, 220, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 88, 88, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 88, 88, 88, 88, 88, 88, 88, 88, 323, 323, 323, 323, 323, 323, + 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 324, 88, 88, 88, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 325, 325, + 325, 325, 325, 325, 325, 326, 327, 325, 77, 77, 77, 77, 77, 77, 84, 84, + 84, 84, 84, 84, 84, 91, 91, 91, 91, 91, 91, 91, 84, 84, 325, 328, 328, + 329, 329, 326, 327, 326, 327, 326, 327, 326, 327, 326, 327, 326, 327, + 326, 327, 326, 327, 284, 284, 326, 327, 325, 325, 325, 325, 329, 329, + 329, 330, 325, 330, 77, 325, 330, 325, 325, 328, 331, 332, 331, 332, 331, + 332, 333, 325, 325, 334, 335, 336, 336, 337, 77, 325, 338, 333, 325, 77, + 77, 77, 77, 136, 136, 136, 123, 136, 139, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 139, 139, 195, 77, 339, 339, 340, 341, 340, 339, 339, 342, + 343, 339, 344, 345, 346, 345, 345, 347, 347, 347, 347, 347, 347, 347, + 347, 347, 347, 345, 339, 348, 349, 348, 339, 339, 350, 350, 350, 350, + 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, + 350, 350, 350, 350, 350, 350, 350, 350, 342, 339, 343, 351, 352, 351, + 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, + 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 353, 342, 349, + 343, 349, 342, 343, 354, 355, 356, 354, 354, 357, 357, 357, 357, 357, + 357, 357, 357, 357, 357, 358, 357, 357, 357, 357, 357, 357, 357, 357, + 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, + 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, + 357, 357, 357, 357, 357, 357, 357, 357, 357, 359, 359, 357, 357, 357, + 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, + 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 77, + 77, 77, 357, 357, 357, 357, 357, 357, 77, 77, 357, 357, 357, 357, 357, + 357, 77, 77, 357, 357, 357, 357, 357, 357, 77, 77, 357, 357, 357, 77, 77, + 77, 341, 341, 349, 351, 360, 341, 341, 77, 361, 362, 362, 362, 362, 361, + 361, 77, 236, 236, 236, 236, 236, 236, 236, 236, 236, 363, 363, 363, 88, + 262, 323, 323, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 77, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 77, 77, 77, 77, 77, 86, 145, 86, 77, 77, 77, 77, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 77, 77, 77, 83, 83, 83, 83, 83, 83, 83, 83, 83, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 166, 166, 166, 166, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 166, 166, 88, 83, 83, + 77, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 77, 77, 77, 88, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 91, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 91, 365, 365, 365, + 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, + 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 77, 77, 77, 77, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 158, 158, 158, 158, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 193, 50, 50, 50, 50, 50, 50, 50, + 50, 193, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 84, 84, 84, 84, 84, 77, 77, 77, 77, + 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 86, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, + 77, 50, 50, 50, 50, 50, 50, 50, 50, 86, 193, 193, 193, 193, 193, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, + 77, 77, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 77, 77, 77, 77, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, + 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 86, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 77, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 77, 46, 46, 46, 46, 46, 46, 46, 77, 46, 46, 77, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 77, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 77, 49, 49, 49, 49, 49, 49, 49, 77, 49, + 49, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 162, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 162, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 55, 53, + 53, 53, 53, 53, 77, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 77, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 112, 112, 112, 112, 112, 112, 90, 90, 112, 90, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, 112, 112, 90, + 90, 90, 112, 90, 90, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, 109, + 366, 366, 366, 366, 366, 366, 366, 366, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 367, 367, 366, 366, 366, 366, 366, 366, 366, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 90, 90, 90, 90, 90, 90, 90, 90, 366, 366, 366, 366, 366, 366, 366, + 366, 366, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 90, 112, 112, 90, 90, 90, 90, 90, 366, 366, 366, 366, 366, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 366, 366, 366, 366, 366, 366, 90, 90, + 90, 145, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, 90, + 90, 90, 90, 109, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 90, 90, 90, 90, 366, 366, 112, 112, 366, 366, 366, 366, 366, + 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 90, 90, 366, 366, + 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, + 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, + 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, 366, + 366, 366, 368, 142, 142, 142, 90, 142, 142, 90, 90, 90, 90, 90, 142, 91, + 142, 84, 368, 368, 368, 368, 90, 368, 368, 368, 90, 368, 368, 368, 368, + 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, + 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 90, 90, 84, 198, + 91, 90, 90, 90, 90, 152, 366, 366, 366, 366, 366, 366, 366, 366, 366, 90, + 90, 90, 90, 90, 90, 90, 109, 109, 109, 109, 109, 109, 109, 109, 109, 90, + 90, 90, 90, 90, 90, 90, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 366, 366, 109, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 366, 366, 366, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 112, 112, 112, 112, 112, 112, + 112, 112, 367, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 84, 91, 90, 90, 90, 90, 366, 366, 366, 366, 366, 109, 109, + 109, 109, 109, 109, 109, 90, 90, 90, 90, 90, 90, 90, 90, 90, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, 90, 90, 145, 145, + 145, 145, 145, 145, 145, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, 90, + 366, 366, 366, 366, 366, 366, 366, 366, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, 90, + 90, 90, 90, 366, 366, 366, 366, 366, 366, 366, 366, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, + 90, 90, 90, 90, 90, 90, 109, 109, 109, 109, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 366, 366, 366, 366, 366, 366, 366, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 369, 369, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, + 369, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 370, 370, 370, + 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, + 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, + 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, + 370, 370, 370, 370, 370, 370, 90, 90, 90, 90, 90, 90, 90, 366, 366, 366, + 366, 366, 366, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 84, 84, 84, 84, + 139, 139, 139, 139, 139, 139, 139, 139, 133, 133, 133, 133, 133, 133, + 133, 133, 133, 133, 139, 139, 139, 139, 139, 139, 133, 133, 133, 133, + 133, 133, 133, 133, 133, 133, 112, 112, 112, 112, 144, 112, 369, 369, + 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 369, 369, 90, 90, 90, 84, 84, 84, 84, 84, 87, 144, + 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, + 370, 370, 370, 370, 370, 370, 370, 370, 90, 90, 90, 90, 90, 90, 90, 90, + 371, 371, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 372, 372, 372, 372, 372, + 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, + 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 90, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, 84, 84, + 107, 90, 90, 112, 112, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 139, 139, 123, 123, 123, 125, 123, 123, 139, 139, 139, 139, 139, + 139, 139, 139, 145, 88, 88, 88, 88, 88, 88, 88, 88, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 91, 91, 142, 91, 91, 91, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 366, 366, 366, 366, 366, 366, 366, + 366, 366, 366, 112, 90, 90, 90, 90, 90, 90, 90, 90, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 91, 91, 84, 84, 84, 91, 84, 91, 91, 91, 91, 373, 373, + 373, 373, 118, 118, 118, 118, 118, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 84, 91, 84, 91, 109, 109, 109, 109, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 366, 366, 366, 366, 366, 366, 366, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 148, 142, 148, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 160, 86, 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 166, + 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, + 166, 166, 166, 166, 166, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 160, 50, 50, 142, 142, 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 160, + 142, 142, 148, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 162, 50, 162, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 162, 50, 50, 50, 50, 148, 148, 148, 142, + 142, 142, 142, 148, 148, 160, 151, 86, 86, 374, 86, 86, 86, 86, 142, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 374, 77, 77, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 77, 77, 77, 77, 77, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 77, 77, 77, 77, 77, 77, 84, 84, 84, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 161, 142, 142, 142, 142, 148, 142, 163, 163, 142, 142, + 142, 152, 160, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 86, + 86, 86, 86, 149, 148, 148, 149, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 155, 86, 86, + 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 142, 142, 148, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 148, 148, 148, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 148, 194, 50, 169, 169, 50, 86, 86, 86, 86, 142, 155, + 142, 142, 86, 148, 142, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 50, 86, 50, 86, 86, 86, 77, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 148, 148, + 148, 142, 142, 142, 148, 148, 142, 194, 155, 142, 86, 86, 86, 86, 86, 86, + 142, 50, 50, 142, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, + 50, 77, 50, 77, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 86, + 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 142, + 148, 148, 148, 142, 142, 142, 142, 142, 142, 155, 160, 77, 77, 77, 77, + 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, 77, + 77, 142, 142, 148, 148, 77, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 50, + 50, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, + 77, 50, 50, 50, 50, 50, 77, 155, 155, 50, 156, 148, 142, 148, 148, 148, + 148, 77, 77, 148, 148, 77, 77, 157, 157, 194, 77, 77, 50, 77, 77, 77, 77, + 77, 77, 156, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 148, 148, 77, 77, + 84, 84, 84, 84, 84, 84, 84, 77, 77, 77, 84, 84, 84, 84, 84, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 149, 149, 149, 150, 149, 150, 149, 149, + 149, 149, 77, 149, 77, 77, 150, 77, 149, 150, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 77, 50, 156, 148, 148, 161, 142, 142, 142, 142, 142, 77, + 156, 77, 77, 375, 77, 375, 375, 156, 148, 77, 148, 148, 160, 194, 152, + 169, 142, 50, 86, 86, 77, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 142, + 142, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 148, 148, 148, 142, 142, 142, + 142, 142, 142, 142, 142, 148, 148, 160, 142, 142, 148, 155, 50, 50, 50, + 50, 86, 86, 86, 86, 86, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 86, 86, 77, 86, 84, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 156, 148, 148, + 142, 142, 142, 142, 142, 142, 148, 161, 157, 157, 156, 157, 142, 142, + 148, 160, 155, 50, 50, 86, 50, 77, 77, 77, 77, 77, 77, 77, 77, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 156, 148, 148, 142, 142, 142, 142, 77, 77, 148, 148, + 157, 157, 142, 142, 148, 160, 155, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 50, 50, 50, 50, + 142, 142, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 148, 148, 148, 142, 142, + 142, 142, 142, 142, 142, 142, 148, 148, 142, 148, 160, 142, 86, 86, 86, + 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 77, 77, 77, 77, 77, 77, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 142, 148, 142, 148, 148, 142, 142, 142, 142, 142, 142, 194, 155, 50, 86, + 77, 77, 77, 77, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 77, 77, 77, 77, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 142, 148, + 142, 186, 186, 142, 142, 142, 142, 148, 142, 142, 142, 142, 160, 77, 77, + 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 158, 158, 86, + 86, 86, 83, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 148, 148, 148, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 148, 160, 155, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 0, 0, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 158, 158, 158, 158, 158, 158, 158, 158, 158, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 50, 149, 149, 149, 149, 149, 149, 149, 77, 77, + 149, 77, 77, 149, 149, 149, 149, 149, 149, 149, 149, 77, 149, 149, 77, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 156, 148, 148, 148, + 148, 148, 77, 148, 157, 77, 77, 142, 142, 194, 152, 169, 148, 169, 148, + 155, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 148, 148, 148, 142, 142, 142, 142, 77, 77, 142, 142, 148, 148, 148, 148, + 160, 50, 86, 50, 148, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 149, 142, 142, + 142, 142, 142, 142, 167, 167, 142, 142, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 142, 160, 142, 142, 142, 142, 148, 50, 142, + 142, 142, 142, 86, 86, 86, 86, 86, 86, 86, 86, 152, 77, 77, 77, 77, 77, + 77, 77, 77, 149, 142, 142, 142, 142, 142, 142, 148, 148, 142, 142, 142, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 169, 169, + 169, 169, 169, 169, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 148, 142, 152, 86, 86, 86, 50, 86, 86, 86, 86, 86, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, + 77, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 142, 148, 142, 142, 142, 148, 142, 148, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 86, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 148, 142, 142, 142, 142, 142, 142, 142, 77, 142, 142, 142, + 142, 142, 142, 148, 376, 50, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 77, 77, 77, 86, 86, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 77, 77, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 77, 148, 142, + 142, 142, 142, 142, 142, 142, 148, 142, 142, 148, 142, 142, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, + 50, 50, 50, 50, 50, 77, 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 142, 142, 142, 142, 142, 142, + 77, 77, 77, 142, 77, 142, 142, 77, 142, 142, 142, 155, 142, 160, 160, + 169, 142, 77, 77, 77, 77, 77, 77, 77, 77, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 77, + 50, 50, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 148, + 148, 148, 148, 148, 77, 142, 142, 77, 148, 148, 142, 148, 160, 50, 77, + 77, 77, 77, 77, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 55, 50, 50, 77, 77, 77, 77, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 142, 142, 148, 148, 86, 86, 77, 77, 77, 77, 77, 77, + 77, 142, 142, 169, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 77, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 142, 142, 142, + 142, 142, 77, 77, 77, 148, 148, 142, 194, 152, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 142, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 158, 88, 88, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 86, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 193, 193, 193, 193, + 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, + 193, 193, 193, 193, 193, 193, 193, 193, 193, 77, 86, 86, 86, 86, 86, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 86, + 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, 142, 50, 50, 50, 50, 50, 50, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 161, 161, 161, 377, 377, 377, 377, 377, 377, 377, 377, 161, 148, 148, + 148, 142, 142, 160, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, + 77, 86, 86, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 77, 77, 198, 198, 198, 198, 198, 86, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 84, 84, 84, 84, 84, 84, 84, 86, 86, 86, 86, 86, 83, 83, 83, 83, 55, 55, + 55, 55, 86, 83, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 77, 158, 158, 158, 158, 158, 158, 158, + 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 55, 55, 55, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 188, 50, 50, 50, 189, 378, 379, 379, 55, 55, 86, 86, + 86, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 220, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 230, 35, 35, 35, 35, 35, 35, 49, 49, + 49, 49, 49, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 86, 86, 86, 86, + 77, 77, 77, 77, 77, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 77, 77, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 220, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 230, 35, 35, 35, 35, - 35, 35, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 220, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 230, - 35, 35, 35, 35, 35, 35, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 220, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 230, 35, 35, 35, 35, 35, 35, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 220, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 230, 35, 35, 35, 35, 35, 35, 49, 35, 0, 0, - 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, - 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, - 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, 341, - 341, 341, 341, 341, 341, 341, 341, 341, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 80, 80, 80, 80, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 135, 135, 135, 80, 80, 80, 80, 80, 80, 80, 80, 135, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 135, 80, 80, 83, 83, 83, 83, 83, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 135, 135, 135, 135, 0, - 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, - 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 0, 0, 0, 0, 0, 0, 47, 47, 47, 47, 47, 47, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 81, 81, 81, 81, 81, 81, 81, 0, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 0, 0, 81, 81, 81, 81, 81, 81, 81, - 0, 81, 81, 0, 81, 81, 81, 81, 81, 0, 0, 0, 0, 0, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 81, 81, 81, 81, - 81, 81, 81, 53, 53, 53, 53, 53, 53, 53, 0, 0, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 0, 0, 0, 0, 48, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 81, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 81, 81, 81, 81, 146, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 0, 0, 0, 0, 0, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 53, 182, 182, 86, 81, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 81, 86, 48, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 0, 0, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 0, 48, - 48, 48, 48, 0, 48, 48, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 0, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 0, 0, 327, 327, 327, 327, 327, 327, 327, 327, 327, - 86, 86, 86, 86, 86, 86, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 329, 329, 329, 330, 330, 330, 330, 330, 330, 330, - 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, - 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 81, 81, - 81, 81, 81, 81, 147, 137, 0, 0, 0, 0, 136, 136, 136, 136, 136, 136, 136, - 136, 136, 136, 0, 0, 0, 0, 104, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, 333, 333, 333, 333, 333, 333, - 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, - 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, - 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, - 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 133, 333, 333, 333, - 111, 333, 333, 333, 333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, 333, 333, 333, 333, 333, 333, - 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, - 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, - 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 133, 333, 333, 333, - 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 333, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 131, 131, 131, 0, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 0, 131, 131, - 0, 131, 0, 0, 131, 0, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 0, 131, 131, 131, 131, 0, 131, 0, 131, 0, 0, 0, 0, 0, 0, 131, 0, 0, 0, 0, - 131, 0, 131, 0, 131, 0, 131, 131, 131, 0, 131, 131, 0, 131, 0, 0, 131, 0, - 131, 0, 131, 0, 131, 0, 131, 0, 131, 131, 0, 131, 0, 0, 131, 131, 131, - 131, 0, 131, 131, 131, 131, 131, 131, 131, 0, 131, 131, 131, 131, 0, 131, - 131, 131, 131, 0, 131, 0, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 0, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 0, 0, 0, 0, 0, 131, 131, 131, 0, 131, 131, 131, 131, - 131, 0, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, - 131, 131, 131, 131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 26, 26, 26, 26, 243, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 243, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 155, 155, 26, 26, 26, 246, 246, 246, - 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, - 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 340, 26, - 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, - 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, - 246, 246, 246, 246, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, - 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, - 346, 346, 226, 226, 226, 26, 26, 26, 346, 346, 346, 346, 346, 346, 346, - 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, - 346, 346, 346, 346, 346, 346, 346, 346, 346, 272, 346, 246, 272, 272, - 272, 272, 272, 272, 272, 272, 272, 272, 346, 346, 346, 346, 346, 346, - 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 346, 26, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 274, 274, 274, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 274, 274, 274, 0, 0, 0, 0, 274, 274, 274, 274, - 274, 274, 274, 274, 274, 0, 0, 0, 0, 0, 0, 0, 274, 274, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 243, 243, 243, 243, 243, 243, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 26, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 26, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 26, 26, 26, 26, - 243, 243, 243, 243, 243, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 26, 26, 26, 243, 26, 26, 26, 243, 243, 243, 347, 347, 347, - 347, 347, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 26, 243, 26, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 26, 26, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 243, 243, 243, 243, 26, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 243, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, 243, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 26, 26, 26, 26, 26, 26, 243, 26, 26, 26, 243, 243, - 243, 26, 26, 243, 243, 243, 0, 0, 0, 0, 243, 243, 243, 243, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 243, 243, 0, 0, 0, 26, 26, 26, 26, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 0, 0, 0, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 0, 0, 0, 0, - 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, - 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 26, 26, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 26, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 26, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 0, 0, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 0, 0, 0, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 0, 0, 0, 0, 0, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 0, 0, 0, 0, 0, 0, 0, 243, 243, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 0, 0, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 243, 243, 0, 0, 0, 0, 0, 0, 243, 243, 243, 243, - 243, 243, 243, 243, 243, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 341, 341, - 341, 341, 341, 341, 341, 341, 341, 341, 0, 0, 0, 0, 0, 0, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 281, 281, 281, 281, 281, 281, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 281, 281, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 0, 0, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 281, 281, 281, 281, 281, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, - 172, 172, 172, 172, 172, 172, 172, 172, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 177, 177, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, - 177, 177, 177, 177, 177, 177, 177, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 279, 279, 279, 279, 279, 279, 279, - 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, - 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, - 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 49, 49, 49, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, + 142, 50, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 77, + 77, 77, 77, 77, 77, 77, 142, 142, 142, 142, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 285, 285, + 284, 285, 380, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 381, 381, 285, + 285, 287, 287, 287, 77, 77, 77, 77, 77, 77, 77, 77, 77, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 285, 285, + 285, 285, 77, 285, 285, 285, 285, 285, 285, 285, 77, 285, 285, 77, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 286, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 286, 286, + 286, 77, 77, 286, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 286, 286, 286, 286, 77, 77, 77, 77, 77, 77, 77, 77, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 77, 77, 77, 77, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, + 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 77, 77, 77, 77, 77, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 77, 77, 83, 142, 198, 86, 195, 195, 195, + 195, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, + 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 383, 383, + 383, 383, 383, 383, 383, 383, 383, 383, 88, 88, 88, 77, 77, 77, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 77, 77, 77, 77, + 77, 77, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 81, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 77, 77, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 77, 77, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 384, 384, 384, 384, 384, 384, + 384, 385, 385, 198, 198, 198, 83, 83, 83, 386, 385, 385, 385, 385, 385, + 195, 195, 195, 195, 195, 195, 195, 195, 91, 91, 91, 91, 91, 91, 91, 91, + 83, 83, 84, 84, 84, 84, 84, 91, 91, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 384, 384, 384, 384, 384, 384, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 88, 88, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 84, 84, 84, 88, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 158, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 158, 158, 158, 158, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 158, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, - 279, 279, 279, 279, 279, 279, 279, 0, 0, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 387, 387, 387, 387, 387, 387, 387, 387, + 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, + 387, 158, 158, 77, 77, 77, 77, 77, 77, 77, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 37, + 37, 37, 37, 37, 37, 37, 77, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 51, 77, 51, 51, 77, 77, 51, 77, 77, 51, 51, 77, 77, + 51, 51, 51, 51, 77, 51, 51, 51, 51, 51, 51, 51, 51, 37, 37, 37, 37, 77, + 37, 77, 37, 37, 37, 37, 37, 37, 37, 77, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 51, 51, 77, 51, 51, 51, 51, 77, 77, 51, 51, 51, 51, 51, 51, 51, 51, + 77, 51, 51, 51, 51, 51, 51, 51, 77, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 51, + 51, 77, 51, 51, 51, 51, 77, 51, 51, 51, 51, 51, 77, 51, 77, 77, 77, 51, + 51, 51, 51, 51, 51, 51, 77, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 77, 77, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 243, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 256, 37, 37, 37, 37, 37, 37, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 243, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 256, 37, 37, 37, 37, 37, 37, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 243, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 256, 37, 37, 37, 37, 37, + 37, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 243, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 256, 37, + 37, 37, 37, 37, 37, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 243, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 256, 37, 37, 37, 37, 37, 37, 51, 37, 77, 77, 383, 383, 383, 383, + 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, + 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, + 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, + 383, 383, 383, 383, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 83, 83, 83, 83, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 83, + 83, 83, 83, 83, 83, 83, 83, 142, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 142, 83, 83, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 142, 142, 142, 142, 142, 77, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 77, 77, 77, 77, 77, 77, 49, 49, 49, 49, 49, 49, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 84, 84, 84, 84, 84, 84, 84, 77, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 77, 77, + 84, 84, 84, 84, 84, 84, 84, 77, 84, 84, 77, 84, 84, 84, 84, 84, 77, 77, + 77, 77, 77, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 84, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 77, 77, 77, 84, 84, 84, 84, 84, 84, 84, 55, 55, 55, 55, + 55, 55, 55, 77, 77, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, + 77, 77, 77, 50, 83, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 84, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 84, + 84, 84, 84, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 77, 77, 77, + 77, 77, 89, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 55, 201, 201, 91, 84, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 84, 91, 50, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 77, 77, 77, 77, 86, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, + 50, 50, 50, 84, 50, 50, 84, 50, 50, 50, 50, 50, 50, 50, 84, 84, 50, 50, + 50, 50, 50, 84, 77, 77, 77, 77, 77, 77, 77, 77, 50, 55, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 50, 50, 50, 50, 50, 50, 50, 77, 50, 50, 50, 50, 77, 50, 50, 77, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 77, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 90, 90, + 366, 366, 366, 366, 366, 366, 366, 366, 366, 91, 91, 91, 91, 91, 91, 91, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 370, 370, 370, 370, + 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, + 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, + 370, 370, 84, 84, 84, 84, 84, 84, 155, 144, 90, 90, 90, 90, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 90, 90, 90, 90, 109, 109, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 139, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, + 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, + 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, + 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, + 373, 373, 373, 373, 138, 373, 373, 373, 116, 373, 373, 373, 373, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 139, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, + 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, + 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, + 373, 373, 373, 373, 373, 373, 373, 138, 373, 373, 373, 373, 373, 373, + 373, 373, 373, 373, 373, 373, 373, 373, 373, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 136, 136, 136, 136, 139, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 139, 136, + 136, 139, 136, 139, 139, 136, 139, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 139, 136, 136, 136, 136, 139, 136, 139, 136, 139, 139, + 139, 139, 139, 139, 136, 139, 139, 139, 139, 136, 139, 136, 139, 136, + 139, 136, 136, 136, 139, 136, 136, 139, 136, 139, 139, 136, 139, 136, + 139, 136, 139, 136, 139, 136, 139, 136, 136, 139, 136, 139, 139, 136, + 136, 136, 136, 139, 136, 136, 136, 136, 136, 136, 136, 139, 136, 136, + 136, 136, 139, 136, 136, 136, 136, 139, 136, 139, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 139, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 139, 139, 139, 139, + 139, 136, 136, 136, 139, 136, 136, 136, 136, 136, 139, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 81, 81, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 88, 88, 88, 88, + 271, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 388, 388, 388, 388, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 388, 388, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 388, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 271, 388, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 166, + 166, 88, 88, 88, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, + 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, + 274, 274, 274, 274, 274, 382, 88, 274, 274, 274, 274, 274, 274, 274, 274, + 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, + 274, 274, 274, 274, 274, 274, 274, 274, 274, 274, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 250, 250, 250, 88, 88, 88, 390, + 390, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 390, + 390, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 391, 389, 274, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 88, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 308, 311, 311, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 311, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 308, 308, 311, 308, 308, 311, 311, 311, 311, + 311, 311, 311, 311, 311, 308, 388, 388, 388, 388, 308, 308, 308, 308, + 308, 308, 308, 308, 308, 388, 388, 388, 388, 388, 388, 388, 311, 311, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 279, 279, 279, 279, 279, 279, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 28, 88, 88, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 28, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 28, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 88, 88, 28, 28, 88, 28, 28, + 28, 88, 88, 28, 28, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 28, 28, 28, 28, 271, 271, 271, 271, 271, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 88, 88, 28, 271, + 28, 88, 28, 271, 271, 271, 393, 393, 393, 393, 393, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 28, 271, 28, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 28, 88, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 28, 28, + 271, 271, 271, 271, 88, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 88, + 88, 88, 88, 88, 88, 88, 28, 28, 88, 88, 28, 28, 28, 28, 28, 28, 28, 271, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 28, 88, 88, 28, 28, 28, + 28, 88, 88, 28, 88, 88, 88, 88, 271, 271, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 271, 28, 88, 88, 28, 88, 88, 88, 88, 88, 88, 88, 88, + 28, 28, 88, 88, 88, 88, 88, 88, 88, 88, 88, 28, 88, 88, 88, 88, 88, 28, + 28, 28, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 28, 28, 28, 88, + 88, 88, 88, 88, 88, 88, 88, 28, 28, 28, 88, 88, 28, 88, 28, 88, 88, 88, + 88, 28, 88, 88, 88, 88, 88, 88, 28, 88, 88, 88, 28, 88, 88, 88, 88, 88, + 88, 28, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 88, 88, 88, 88, + 88, 28, 271, 28, 28, 28, 271, 271, 271, 88, 88, 271, 271, 271, 271, 388, + 388, 388, 271, 271, 271, 271, 28, 28, 28, 28, 28, 28, 88, 88, 88, 28, 88, + 271, 271, 388, 388, 388, 28, 88, 88, 28, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 388, 388, 388, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 388, 388, 388, 388, 388, 388, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 388, 388, 388, 388, 271, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 388, 388, 388, 388, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 388, 388, 388, 388, 388, 388, 388, 388, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 388, 388, 388, 388, 388, 388, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 388, 388, + 388, 388, 388, 388, 388, 388, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 388, 388, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 388, 388, + 388, 388, 88, 88, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 81, 81, 81, 81, 81, 81, 81, 81, 81, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 88, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 88, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 388, 388, 388, 388, 388, 388, 388, 388, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 388, 388, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 388, 388, 388, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 388, 388, 388, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 271, 388, + 271, 388, 388, 388, 388, 271, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 388, 388, 271, 271, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 388, 388, 388, 388, 271, 271, 271, + 271, 271, 271, 271, 271, 271, 271, 388, 388, 388, 388, 388, 388, 388, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 77, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 88, 77, 77, + 77, 77, 77, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 388, 388, 323, 323, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 318, 318, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 318, 318, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, + 317, 317, 317, 317, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 323, 323, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 318, 318, 318, + 318, 318, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 318, 318, 318, 318, 318, 318, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 323, 323, 236, 195, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 323, 323, }; /* decomposition data */ @@ -3992,85 +5005,85 @@ static const unsigned int decomp_data[] = { 50, 52, 26085, 770, 50, 53, 26085, 770, 50, 54, 26085, 770, 50, 55, 26085, 770, 50, 56, 26085, 770, 50, 57, 26085, 770, 51, 48, 26085, 770, 51, 49, 26085, 778, 103, 97, 108, 259, 1098, 259, 1100, 259, 42863, 259, - 67, 259, 70, 259, 81, 259, 294, 259, 339, 259, 42791, 259, 43831, 259, - 619, 259, 43858, 259, 653, 256, 35912, 256, 26356, 256, 36554, 256, - 36040, 256, 28369, 256, 20018, 256, 21477, 256, 40860, 256, 22865, 256, - 37329, 256, 21895, 256, 22856, 256, 25078, 256, 30313, 256, 32645, 256, - 34367, 256, 34746, 256, 35064, 256, 37007, 256, 27138, 256, 27931, 256, - 28889, 256, 29662, 256, 33853, 256, 37226, 256, 39409, 256, 20098, 256, - 21365, 256, 27396, 256, 29211, 256, 34349, 256, 40478, 256, 23888, 256, - 28651, 256, 34253, 256, 35172, 256, 25289, 256, 33240, 256, 34847, 256, - 24266, 256, 26391, 256, 28010, 256, 29436, 256, 37070, 256, 20358, 256, - 20919, 256, 21214, 256, 25796, 256, 27347, 256, 29200, 256, 30439, 256, - 32769, 256, 34310, 256, 34396, 256, 36335, 256, 38706, 256, 39791, 256, - 40442, 256, 30860, 256, 31103, 256, 32160, 256, 33737, 256, 37636, 256, - 40575, 256, 35542, 256, 22751, 256, 24324, 256, 31840, 256, 32894, 256, - 29282, 256, 30922, 256, 36034, 256, 38647, 256, 22744, 256, 23650, 256, - 27155, 256, 28122, 256, 28431, 256, 32047, 256, 32311, 256, 38475, 256, - 21202, 256, 32907, 256, 20956, 256, 20940, 256, 31260, 256, 32190, 256, - 33777, 256, 38517, 256, 35712, 256, 25295, 256, 35582, 256, 20025, 256, - 23527, 256, 24594, 256, 29575, 256, 30064, 256, 21271, 256, 30971, 256, - 20415, 256, 24489, 256, 19981, 256, 27852, 256, 25976, 256, 32034, 256, - 21443, 256, 22622, 256, 30465, 256, 33865, 256, 35498, 256, 27578, 256, - 36784, 256, 27784, 256, 25342, 256, 33509, 256, 25504, 256, 30053, 256, - 20142, 256, 20841, 256, 20937, 256, 26753, 256, 31975, 256, 33391, 256, - 35538, 256, 37327, 256, 21237, 256, 21570, 256, 22899, 256, 24300, 256, - 26053, 256, 28670, 256, 31018, 256, 38317, 256, 39530, 256, 40599, 256, - 40654, 256, 21147, 256, 26310, 256, 27511, 256, 36706, 256, 24180, 256, - 24976, 256, 25088, 256, 25754, 256, 28451, 256, 29001, 256, 29833, 256, - 31178, 256, 32244, 256, 32879, 256, 36646, 256, 34030, 256, 36899, 256, - 37706, 256, 21015, 256, 21155, 256, 21693, 256, 28872, 256, 35010, 256, - 24265, 256, 24565, 256, 25467, 256, 27566, 256, 31806, 256, 29557, 256, - 20196, 256, 22265, 256, 23994, 256, 24604, 256, 29618, 256, 29801, 256, - 32666, 256, 32838, 256, 37428, 256, 38646, 256, 38728, 256, 38936, 256, - 20363, 256, 31150, 256, 37300, 256, 38584, 256, 24801, 256, 20102, 256, - 20698, 256, 23534, 256, 23615, 256, 26009, 256, 29134, 256, 30274, 256, - 34044, 256, 36988, 256, 40845, 256, 26248, 256, 38446, 256, 21129, 256, - 26491, 256, 26611, 256, 27969, 256, 28316, 256, 29705, 256, 30041, 256, - 30827, 256, 32016, 256, 39006, 256, 20845, 256, 25134, 256, 38520, 256, - 20523, 256, 23833, 256, 28138, 256, 36650, 256, 24459, 256, 24900, 256, - 26647, 256, 38534, 256, 21033, 256, 21519, 256, 23653, 256, 26131, 256, - 26446, 256, 26792, 256, 27877, 256, 29702, 256, 30178, 256, 32633, 256, - 35023, 256, 35041, 256, 37324, 256, 38626, 256, 21311, 256, 28346, 256, - 21533, 256, 29136, 256, 29848, 256, 34298, 256, 38563, 256, 40023, 256, - 40607, 256, 26519, 256, 28107, 256, 33256, 256, 31435, 256, 31520, 256, - 31890, 256, 29376, 256, 28825, 256, 35672, 256, 20160, 256, 33590, 256, - 21050, 256, 20999, 256, 24230, 256, 25299, 256, 31958, 256, 23429, 256, - 27934, 256, 26292, 256, 36667, 256, 34892, 256, 38477, 256, 35211, 256, - 24275, 256, 20800, 256, 21952, 256, 22618, 256, 26228, 256, 20958, 256, - 29482, 256, 30410, 256, 31036, 256, 31070, 256, 31077, 256, 31119, 256, - 38742, 256, 31934, 256, 32701, 256, 34322, 256, 35576, 256, 36920, 256, - 37117, 256, 39151, 256, 39164, 256, 39208, 256, 40372, 256, 37086, 256, - 38583, 256, 20398, 256, 20711, 256, 20813, 256, 21193, 256, 21220, 256, - 21329, 256, 21917, 256, 22022, 256, 22120, 256, 22592, 256, 22696, 256, - 23652, 256, 23662, 256, 24724, 256, 24936, 256, 24974, 256, 25074, 256, - 25935, 256, 26082, 256, 26257, 256, 26757, 256, 28023, 256, 28186, 256, - 28450, 256, 29038, 256, 29227, 256, 29730, 256, 30865, 256, 31038, 256, - 31049, 256, 31048, 256, 31056, 256, 31062, 256, 31069, 256, 31117, 256, - 31118, 256, 31296, 256, 31361, 256, 31680, 256, 32265, 256, 32321, 256, - 32626, 256, 32773, 256, 33261, 256, 33401, 256, 33879, 256, 35088, 256, - 35222, 256, 35585, 256, 35641, 256, 36051, 256, 36104, 256, 36790, 256, - 38627, 256, 38911, 256, 38971, 256, 24693, 256, 148206, 256, 33304, 256, - 20006, 256, 20917, 256, 20840, 256, 20352, 256, 20805, 256, 20864, 256, - 21191, 256, 21242, 256, 21845, 256, 21913, 256, 21986, 256, 22707, 256, - 22852, 256, 22868, 256, 23138, 256, 23336, 256, 24274, 256, 24281, 256, - 24425, 256, 24493, 256, 24792, 256, 24910, 256, 24840, 256, 24928, 256, - 25140, 256, 25540, 256, 25628, 256, 25682, 256, 25942, 256, 26395, 256, - 26454, 256, 27513, 256, 28379, 256, 28363, 256, 28702, 256, 30631, 256, - 29237, 256, 29359, 256, 29809, 256, 29958, 256, 30011, 256, 30237, 256, - 30239, 256, 30427, 256, 30452, 256, 30538, 256, 30528, 256, 30924, 256, - 31409, 256, 31867, 256, 32091, 256, 32574, 256, 33618, 256, 33775, 256, - 34681, 256, 35137, 256, 35206, 256, 35519, 256, 35531, 256, 35565, 256, - 35722, 256, 36664, 256, 36978, 256, 37273, 256, 37494, 256, 38524, 256, - 38875, 256, 38923, 256, 39698, 256, 141386, 256, 141380, 256, 144341, - 256, 15261, 256, 16408, 256, 16441, 256, 152137, 256, 154832, 256, - 163539, 256, 40771, 256, 40846, 514, 102, 102, 514, 102, 105, 514, 102, - 108, 770, 102, 102, 105, 770, 102, 102, 108, 514, 383, 116, 514, 115, - 116, 514, 1396, 1398, 514, 1396, 1381, 514, 1396, 1387, 514, 1406, 1398, - 514, 1396, 1389, 512, 1497, 1460, 512, 1522, 1463, 262, 1506, 262, 1488, - 262, 1491, 262, 1492, 262, 1499, 262, 1500, 262, 1501, 262, 1512, 262, - 1514, 262, 43, 512, 1513, 1473, 512, 1513, 1474, 512, 64329, 1473, 512, - 64329, 1474, 512, 1488, 1463, 512, 1488, 1464, 512, 1488, 1468, 512, + 83, 259, 67, 259, 70, 259, 81, 259, 294, 259, 339, 259, 42791, 259, + 43831, 259, 619, 259, 43858, 259, 653, 256, 35912, 256, 26356, 256, + 36554, 256, 36040, 256, 28369, 256, 20018, 256, 21477, 256, 40860, 256, + 22865, 256, 37329, 256, 21895, 256, 22856, 256, 25078, 256, 30313, 256, + 32645, 256, 34367, 256, 34746, 256, 35064, 256, 37007, 256, 27138, 256, + 27931, 256, 28889, 256, 29662, 256, 33853, 256, 37226, 256, 39409, 256, + 20098, 256, 21365, 256, 27396, 256, 29211, 256, 34349, 256, 40478, 256, + 23888, 256, 28651, 256, 34253, 256, 35172, 256, 25289, 256, 33240, 256, + 34847, 256, 24266, 256, 26391, 256, 28010, 256, 29436, 256, 37070, 256, + 20358, 256, 20919, 256, 21214, 256, 25796, 256, 27347, 256, 29200, 256, + 30439, 256, 32769, 256, 34310, 256, 34396, 256, 36335, 256, 38706, 256, + 39791, 256, 40442, 256, 30860, 256, 31103, 256, 32160, 256, 33737, 256, + 37636, 256, 40575, 256, 35542, 256, 22751, 256, 24324, 256, 31840, 256, + 32894, 256, 29282, 256, 30922, 256, 36034, 256, 38647, 256, 22744, 256, + 23650, 256, 27155, 256, 28122, 256, 28431, 256, 32047, 256, 32311, 256, + 38475, 256, 21202, 256, 32907, 256, 20956, 256, 20940, 256, 31260, 256, + 32190, 256, 33777, 256, 38517, 256, 35712, 256, 25295, 256, 35582, 256, + 20025, 256, 23527, 256, 24594, 256, 29575, 256, 30064, 256, 21271, 256, + 30971, 256, 20415, 256, 24489, 256, 19981, 256, 27852, 256, 25976, 256, + 32034, 256, 21443, 256, 22622, 256, 30465, 256, 33865, 256, 35498, 256, + 27578, 256, 36784, 256, 27784, 256, 25342, 256, 33509, 256, 25504, 256, + 30053, 256, 20142, 256, 20841, 256, 20937, 256, 26753, 256, 31975, 256, + 33391, 256, 35538, 256, 37327, 256, 21237, 256, 21570, 256, 22899, 256, + 24300, 256, 26053, 256, 28670, 256, 31018, 256, 38317, 256, 39530, 256, + 40599, 256, 40654, 256, 21147, 256, 26310, 256, 27511, 256, 36706, 256, + 24180, 256, 24976, 256, 25088, 256, 25754, 256, 28451, 256, 29001, 256, + 29833, 256, 31178, 256, 32244, 256, 32879, 256, 36646, 256, 34030, 256, + 36899, 256, 37706, 256, 21015, 256, 21155, 256, 21693, 256, 28872, 256, + 35010, 256, 24265, 256, 24565, 256, 25467, 256, 27566, 256, 31806, 256, + 29557, 256, 20196, 256, 22265, 256, 23994, 256, 24604, 256, 29618, 256, + 29801, 256, 32666, 256, 32838, 256, 37428, 256, 38646, 256, 38728, 256, + 38936, 256, 20363, 256, 31150, 256, 37300, 256, 38584, 256, 24801, 256, + 20102, 256, 20698, 256, 23534, 256, 23615, 256, 26009, 256, 29134, 256, + 30274, 256, 34044, 256, 36988, 256, 40845, 256, 26248, 256, 38446, 256, + 21129, 256, 26491, 256, 26611, 256, 27969, 256, 28316, 256, 29705, 256, + 30041, 256, 30827, 256, 32016, 256, 39006, 256, 20845, 256, 25134, 256, + 38520, 256, 20523, 256, 23833, 256, 28138, 256, 36650, 256, 24459, 256, + 24900, 256, 26647, 256, 38534, 256, 21033, 256, 21519, 256, 23653, 256, + 26131, 256, 26446, 256, 26792, 256, 27877, 256, 29702, 256, 30178, 256, + 32633, 256, 35023, 256, 35041, 256, 37324, 256, 38626, 256, 21311, 256, + 28346, 256, 21533, 256, 29136, 256, 29848, 256, 34298, 256, 38563, 256, + 40023, 256, 40607, 256, 26519, 256, 28107, 256, 33256, 256, 31435, 256, + 31520, 256, 31890, 256, 29376, 256, 28825, 256, 35672, 256, 20160, 256, + 33590, 256, 21050, 256, 20999, 256, 24230, 256, 25299, 256, 31958, 256, + 23429, 256, 27934, 256, 26292, 256, 36667, 256, 34892, 256, 38477, 256, + 35211, 256, 24275, 256, 20800, 256, 21952, 256, 22618, 256, 26228, 256, + 20958, 256, 29482, 256, 30410, 256, 31036, 256, 31070, 256, 31077, 256, + 31119, 256, 38742, 256, 31934, 256, 32701, 256, 34322, 256, 35576, 256, + 36920, 256, 37117, 256, 39151, 256, 39164, 256, 39208, 256, 40372, 256, + 37086, 256, 38583, 256, 20398, 256, 20711, 256, 20813, 256, 21193, 256, + 21220, 256, 21329, 256, 21917, 256, 22022, 256, 22120, 256, 22592, 256, + 22696, 256, 23652, 256, 23662, 256, 24724, 256, 24936, 256, 24974, 256, + 25074, 256, 25935, 256, 26082, 256, 26257, 256, 26757, 256, 28023, 256, + 28186, 256, 28450, 256, 29038, 256, 29227, 256, 29730, 256, 30865, 256, + 31038, 256, 31049, 256, 31048, 256, 31056, 256, 31062, 256, 31069, 256, + 31117, 256, 31118, 256, 31296, 256, 31361, 256, 31680, 256, 32265, 256, + 32321, 256, 32626, 256, 32773, 256, 33261, 256, 33401, 256, 33879, 256, + 35088, 256, 35222, 256, 35585, 256, 35641, 256, 36051, 256, 36104, 256, + 36790, 256, 38627, 256, 38911, 256, 38971, 256, 24693, 256, 148206, 256, + 33304, 256, 20006, 256, 20917, 256, 20840, 256, 20352, 256, 20805, 256, + 20864, 256, 21191, 256, 21242, 256, 21845, 256, 21913, 256, 21986, 256, + 22707, 256, 22852, 256, 22868, 256, 23138, 256, 23336, 256, 24274, 256, + 24281, 256, 24425, 256, 24493, 256, 24792, 256, 24910, 256, 24840, 256, + 24928, 256, 25140, 256, 25540, 256, 25628, 256, 25682, 256, 25942, 256, + 26395, 256, 26454, 256, 27513, 256, 28379, 256, 28363, 256, 28702, 256, + 30631, 256, 29237, 256, 29359, 256, 29809, 256, 29958, 256, 30011, 256, + 30237, 256, 30239, 256, 30427, 256, 30452, 256, 30538, 256, 30528, 256, + 30924, 256, 31409, 256, 31867, 256, 32091, 256, 32574, 256, 33618, 256, + 33775, 256, 34681, 256, 35137, 256, 35206, 256, 35519, 256, 35531, 256, + 35565, 256, 35722, 256, 36664, 256, 36978, 256, 37273, 256, 37494, 256, + 38524, 256, 38875, 256, 38923, 256, 39698, 256, 141386, 256, 141380, 256, + 144341, 256, 15261, 256, 16408, 256, 16441, 256, 152137, 256, 154832, + 256, 163539, 256, 40771, 256, 40846, 514, 102, 102, 514, 102, 105, 514, + 102, 108, 770, 102, 102, 105, 770, 102, 102, 108, 514, 383, 116, 514, + 115, 116, 514, 1396, 1398, 514, 1396, 1381, 514, 1396, 1387, 514, 1406, + 1398, 514, 1396, 1389, 512, 1497, 1460, 512, 1522, 1463, 262, 1506, 262, + 1488, 262, 1491, 262, 1492, 262, 1499, 262, 1500, 262, 1501, 262, 1512, + 262, 1514, 262, 43, 512, 1513, 1473, 512, 1513, 1474, 512, 64329, 1473, + 512, 64329, 1474, 512, 1488, 1463, 512, 1488, 1464, 512, 1488, 1468, 512, 1489, 1468, 512, 1490, 1468, 512, 1491, 1468, 512, 1492, 1468, 512, 1493, 1468, 512, 1494, 1468, 512, 1496, 1468, 512, 1497, 1468, 512, 1498, 1468, 512, 1499, 1468, 512, 1500, 1468, 512, 1502, 1468, 512, 1504, 1468, 512, @@ -5234,210 +6247,210 @@ static const unsigned short decomp_index2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6603, 6605, - 6607, 0, 0, 0, 6609, 6611, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6613, 6615, 6617, 6619, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 6621, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 6623, 6625, 6627, 6629, 6631, 6633, 6635, 6637, 6637, 6639, - 6641, 6643, 6645, 6647, 6649, 6651, 6653, 6655, 6657, 6659, 6661, 6663, - 6665, 6667, 6669, 6671, 6673, 6675, 6677, 6679, 6681, 6683, 6685, 6687, - 6689, 6691, 6693, 6695, 6697, 6699, 6701, 6703, 6705, 6707, 6709, 6711, - 6713, 6715, 6717, 6719, 6721, 6723, 6725, 6727, 6729, 6731, 6733, 6735, - 6737, 6739, 6741, 6743, 6745, 6747, 6749, 6751, 6753, 6755, 6757, 6759, - 6761, 6763, 6765, 6767, 6769, 6771, 6773, 6775, 6777, 6779, 6781, 6783, - 6785, 6787, 6789, 6791, 6793, 6795, 6797, 6799, 6801, 6803, 6661, 6805, - 6807, 6809, 6811, 6813, 6815, 6817, 6819, 6821, 6823, 6825, 6827, 6829, - 6831, 6833, 6835, 6837, 6839, 6841, 6843, 6845, 6847, 6849, 6851, 6853, - 6855, 6857, 6859, 6861, 6863, 6865, 6867, 6869, 6871, 6873, 6875, 6877, - 6879, 6881, 6883, 6885, 6887, 6889, 6891, 6893, 6895, 6897, 6899, 6901, - 6903, 6905, 6907, 6909, 6911, 6913, 6915, 6917, 6919, 6921, 6923, 6925, - 6927, 6929, 6931, 6933, 6935, 6937, 6939, 6841, 6941, 6943, 6945, 6947, - 6949, 6951, 6953, 6955, 6809, 6957, 6959, 6961, 6963, 6965, 6967, 6969, - 6971, 6973, 6975, 6977, 6979, 6981, 6983, 6985, 6987, 6989, 6991, 6993, - 6995, 6661, 6997, 6999, 7001, 7003, 7005, 7007, 7009, 7011, 7013, 7015, - 7017, 7019, 7021, 7023, 7025, 7027, 7029, 7031, 7033, 7035, 7037, 7039, - 7041, 7043, 7045, 7047, 7049, 6813, 7051, 7053, 7055, 7057, 7059, 7061, - 7063, 7065, 7067, 7069, 7071, 7073, 7075, 7077, 7079, 7081, 7083, 7085, - 7087, 7089, 7091, 7093, 7095, 7097, 7099, 7101, 7103, 7105, 7107, 7109, - 7111, 7113, 7115, 7117, 7119, 7121, 7123, 7125, 7127, 7129, 7131, 7133, - 7135, 7137, 7139, 7141, 7143, 7145, 7147, 7149, 0, 0, 7151, 0, 7153, 0, - 0, 7155, 7157, 7159, 7161, 7163, 7165, 7167, 7169, 7171, 7173, 0, 7175, - 0, 7177, 0, 0, 7179, 7181, 0, 0, 0, 7183, 7185, 7187, 7189, 7191, 7193, - 7195, 7197, 7199, 7201, 7203, 7205, 7207, 7209, 7211, 7213, 7215, 7217, - 7219, 7221, 7223, 7225, 7227, 7229, 7231, 7233, 7235, 7237, 7239, 7241, - 7243, 7245, 7247, 7249, 7251, 7253, 7255, 7257, 7259, 7261, 7263, 7265, - 7267, 7269, 7271, 6919, 7273, 7275, 7277, 7279, 7281, 7283, 7283, 7285, - 7287, 7289, 7291, 7293, 7295, 7297, 7299, 7179, 7301, 7303, 7305, 7307, - 7309, 7311, 0, 0, 7313, 7315, 7317, 7319, 7321, 7323, 7325, 7327, 7207, - 7329, 7331, 7333, 7151, 7335, 7337, 7339, 7341, 7343, 7345, 7347, 7349, - 7351, 7353, 7355, 7357, 7225, 7359, 7227, 7361, 7363, 7365, 7367, 7369, - 7153, 6703, 7371, 7373, 7375, 6843, 7017, 7377, 7379, 7241, 7381, 7243, - 7383, 7385, 7387, 7157, 7389, 7391, 7393, 7395, 7397, 7159, 7399, 7401, - 7403, 7405, 7407, 7409, 7271, 7411, 7413, 6919, 7415, 7279, 7417, 7419, - 7421, 7423, 7425, 7289, 7427, 7177, 7429, 7291, 6805, 7431, 7293, 7433, - 7297, 7435, 7437, 7439, 7441, 7443, 7301, 7169, 7445, 7303, 7447, 7305, - 7449, 6637, 7451, 7453, 7455, 7457, 7459, 7461, 7463, 7465, 7467, 7469, - 7471, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7473, 7476, 7479, 7482, - 7486, 7490, 7493, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7496, 7499, 7502, - 7505, 7508, 0, 0, 0, 0, 0, 7511, 0, 7514, 7517, 7519, 7521, 7523, 7525, - 7527, 7529, 7531, 7533, 7535, 7537, 7540, 7543, 7546, 7549, 7552, 7555, - 7558, 7561, 7564, 7567, 7570, 7573, 0, 7576, 7579, 7582, 7585, 7588, 0, - 7591, 0, 7594, 7597, 0, 7600, 7603, 0, 7606, 7609, 7612, 7615, 7618, - 7621, 7624, 7627, 7630, 7633, 7636, 7638, 7640, 7642, 7644, 7646, 7648, - 7650, 7652, 7654, 7656, 7658, 7660, 7662, 7664, 7666, 7668, 7670, 7672, - 7674, 7676, 7678, 7680, 7682, 7684, 7686, 7688, 7690, 7692, 7694, 7696, - 7698, 7700, 7702, 7704, 7706, 7708, 7710, 7712, 7714, 7716, 7718, 7720, - 7722, 7724, 7726, 7728, 7730, 7732, 7734, 7736, 7738, 7740, 7742, 7744, - 7746, 7748, 7750, 7752, 7754, 7756, 7758, 7760, 7762, 7764, 7766, 7768, - 7770, 7772, 7774, 7776, 7778, 7780, 7782, 7784, 7786, 7788, 7790, 7792, - 7794, 7796, 7798, 7800, 7802, 7804, 7806, 7808, 7810, 7812, 7814, 7816, - 7818, 7820, 7822, 7824, 7826, 7828, 7830, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6603, 6605, 6607, + 6609, 0, 0, 0, 6611, 6613, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6615, 6617, 6619, 6621, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6623, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6625, 6627, 6629, 6631, 6633, 6635, 6637, 6639, 6639, 6641, + 6643, 6645, 6647, 6649, 6651, 6653, 6655, 6657, 6659, 6661, 6663, 6665, + 6667, 6669, 6671, 6673, 6675, 6677, 6679, 6681, 6683, 6685, 6687, 6689, + 6691, 6693, 6695, 6697, 6699, 6701, 6703, 6705, 6707, 6709, 6711, 6713, + 6715, 6717, 6719, 6721, 6723, 6725, 6727, 6729, 6731, 6733, 6735, 6737, + 6739, 6741, 6743, 6745, 6747, 6749, 6751, 6753, 6755, 6757, 6759, 6761, + 6763, 6765, 6767, 6769, 6771, 6773, 6775, 6777, 6779, 6781, 6783, 6785, + 6787, 6789, 6791, 6793, 6795, 6797, 6799, 6801, 6803, 6805, 6663, 6807, + 6809, 6811, 6813, 6815, 6817, 6819, 6821, 6823, 6825, 6827, 6829, 6831, + 6833, 6835, 6837, 6839, 6841, 6843, 6845, 6847, 6849, 6851, 6853, 6855, + 6857, 6859, 6861, 6863, 6865, 6867, 6869, 6871, 6873, 6875, 6877, 6879, + 6881, 6883, 6885, 6887, 6889, 6891, 6893, 6895, 6897, 6899, 6901, 6903, + 6905, 6907, 6909, 6911, 6913, 6915, 6917, 6919, 6921, 6923, 6925, 6927, + 6929, 6931, 6933, 6935, 6937, 6939, 6941, 6843, 6943, 6945, 6947, 6949, + 6951, 6953, 6955, 6957, 6811, 6959, 6961, 6963, 6965, 6967, 6969, 6971, + 6973, 6975, 6977, 6979, 6981, 6983, 6985, 6987, 6989, 6991, 6993, 6995, + 6997, 6663, 6999, 7001, 7003, 7005, 7007, 7009, 7011, 7013, 7015, 7017, + 7019, 7021, 7023, 7025, 7027, 7029, 7031, 7033, 7035, 7037, 7039, 7041, + 7043, 7045, 7047, 7049, 7051, 6815, 7053, 7055, 7057, 7059, 7061, 7063, + 7065, 7067, 7069, 7071, 7073, 7075, 7077, 7079, 7081, 7083, 7085, 7087, + 7089, 7091, 7093, 7095, 7097, 7099, 7101, 7103, 7105, 7107, 7109, 7111, + 7113, 7115, 7117, 7119, 7121, 7123, 7125, 7127, 7129, 7131, 7133, 7135, + 7137, 7139, 7141, 7143, 7145, 7147, 7149, 7151, 0, 0, 7153, 0, 7155, 0, + 0, 7157, 7159, 7161, 7163, 7165, 7167, 7169, 7171, 7173, 7175, 0, 7177, + 0, 7179, 0, 0, 7181, 7183, 0, 0, 0, 7185, 7187, 7189, 7191, 7193, 7195, + 7197, 7199, 7201, 7203, 7205, 7207, 7209, 7211, 7213, 7215, 7217, 7219, + 7221, 7223, 7225, 7227, 7229, 7231, 7233, 7235, 7237, 7239, 7241, 7243, + 7245, 7247, 7249, 7251, 7253, 7255, 7257, 7259, 7261, 7263, 7265, 7267, + 7269, 7271, 7273, 6921, 7275, 7277, 7279, 7281, 7283, 7285, 7285, 7287, + 7289, 7291, 7293, 7295, 7297, 7299, 7301, 7181, 7303, 7305, 7307, 7309, + 7311, 7313, 0, 0, 7315, 7317, 7319, 7321, 7323, 7325, 7327, 7329, 7209, + 7331, 7333, 7335, 7153, 7337, 7339, 7341, 7343, 7345, 7347, 7349, 7351, + 7353, 7355, 7357, 7359, 7227, 7361, 7229, 7363, 7365, 7367, 7369, 7371, + 7155, 6705, 7373, 7375, 7377, 6845, 7019, 7379, 7381, 7243, 7383, 7245, + 7385, 7387, 7389, 7159, 7391, 7393, 7395, 7397, 7399, 7161, 7401, 7403, + 7405, 7407, 7409, 7411, 7273, 7413, 7415, 6921, 7417, 7281, 7419, 7421, + 7423, 7425, 7427, 7291, 7429, 7179, 7431, 7293, 6807, 7433, 7295, 7435, + 7299, 7437, 7439, 7441, 7443, 7445, 7303, 7171, 7447, 7305, 7449, 7307, + 7451, 6639, 7453, 7455, 7457, 7459, 7461, 7463, 7465, 7467, 7469, 7471, + 7473, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7475, 7478, 7481, 7484, + 7488, 7492, 7495, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7498, 7501, 7504, + 7507, 7510, 0, 0, 0, 0, 0, 7513, 0, 7516, 7519, 7521, 7523, 7525, 7527, + 7529, 7531, 7533, 7535, 7537, 7539, 7542, 7545, 7548, 7551, 7554, 7557, + 7560, 7563, 7566, 7569, 7572, 7575, 0, 7578, 7581, 7584, 7587, 7590, 0, + 7593, 0, 7596, 7599, 0, 7602, 7605, 0, 7608, 7611, 7614, 7617, 7620, + 7623, 7626, 7629, 7632, 7635, 7638, 7640, 7642, 7644, 7646, 7648, 7650, + 7652, 7654, 7656, 7658, 7660, 7662, 7664, 7666, 7668, 7670, 7672, 7674, + 7676, 7678, 7680, 7682, 7684, 7686, 7688, 7690, 7692, 7694, 7696, 7698, + 7700, 7702, 7704, 7706, 7708, 7710, 7712, 7714, 7716, 7718, 7720, 7722, + 7724, 7726, 7728, 7730, 7732, 7734, 7736, 7738, 7740, 7742, 7744, 7746, + 7748, 7750, 7752, 7754, 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, + 7772, 7774, 7776, 7778, 7780, 7782, 7784, 7786, 7788, 7790, 7792, 7794, + 7796, 7798, 7800, 7802, 7804, 7806, 7808, 7810, 7812, 7814, 7816, 7818, + 7820, 7822, 7824, 7826, 7828, 7830, 7832, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 7832, 7834, 7836, 7838, 7840, 7842, 7844, 7846, 7848, 7850, 7852, 7854, - 7856, 7858, 7860, 7862, 7864, 7866, 7868, 7870, 7872, 7874, 7876, 7878, - 7881, 7884, 7887, 7890, 7893, 7896, 7899, 7902, 7905, 7908, 7911, 7914, - 7917, 7920, 7923, 7926, 7929, 7932, 7934, 7936, 7938, 7940, 7943, 7946, - 7923, 7949, 7952, 7955, 7958, 7961, 7964, 7967, 7970, 7973, 7976, 7979, - 7982, 7985, 7988, 7991, 7994, 7997, 8000, 8003, 8006, 8009, 8012, 8015, - 8018, 8021, 8024, 8027, 8030, 8033, 8036, 8039, 8042, 8045, 8048, 8051, - 8054, 8057, 8060, 8063, 8066, 8069, 8072, 8075, 8078, 8081, 8084, 8087, - 8090, 8093, 8096, 8099, 8102, 8105, 8108, 8111, 8114, 8117, 8120, 8123, - 8126, 8129, 8132, 8135, 8138, 8141, 8144, 8147, 8150, 8153, 8156, 8159, - 8162, 8165, 8168, 8171, 8174, 8177, 8180, 8183, 8186, 8189, 8192, 8195, - 8198, 8201, 8204, 8207, 8210, 8213, 8216, 8219, 8223, 8227, 8231, 8235, - 8239, 8243, 8246, 8249, 8252, 7926, 8255, 8258, 8261, 8264, 8267, 8270, - 8273, 8276, 8279, 8282, 8285, 8288, 8291, 8294, 8297, 8300, 8303, 8306, - 8309, 8312, 8315, 8318, 8321, 8324, 8327, 8330, 8333, 8336, 8339, 8342, - 8345, 8348, 8351, 8354, 8357, 8360, 8363, 8366, 8369, 8372, 8375, 8378, - 8381, 8384, 8387, 8390, 8393, 8396, 8399, 8402, 8405, 8408, 8411, 8414, - 8417, 8420, 8423, 8426, 8429, 8432, 8435, 8438, 8441, 8444, 8447, 8450, - 8453, 8456, 8459, 8462, 8465, 8468, 8471, 8474, 8477, 8480, 8483, 8486, - 8489, 8492, 8495, 8498, 8501, 8504, 8507, 8510, 8513, 8516, 8519, 8522, - 8525, 8528, 8531, 8534, 8537, 8540, 8543, 8546, 8549, 8552, 8555, 8558, - 8561, 8564, 8567, 8570, 8573, 8576, 8579, 8582, 8585, 8588, 8591, 8594, - 8597, 8600, 8603, 8606, 8609, 8612, 8615, 8618, 8621, 8624, 8627, 8630, - 8633, 8636, 8639, 8642, 8645, 8648, 8651, 8654, 8657, 8660, 8663, 8666, - 8670, 8674, 8678, 8681, 8684, 8687, 8690, 8693, 8696, 8699, 8702, 8705, - 8708, 8711, 8714, 8717, 8720, 8723, 8726, 8729, 8732, 8735, 8738, 8741, - 8744, 8747, 8750, 8753, 8756, 8759, 8762, 8765, 8768, 8771, 8774, 8777, - 8780, 8783, 8786, 8789, 8792, 8795, 8798, 8801, 8804, 8807, 8810, 8813, - 8816, 8819, 8822, 8825, 8828, 8831, 8834, 8837, 8840, 8843, 8846, 8849, - 8852, 8855, 8858, 8861, 8864, 8867, 8870, 8873, 8876, 8879, 8882, 8885, - 8888, 8891, 8894, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 8897, 8901, 8905, 8909, 8913, 8917, 8921, 8925, 8929, 8933, 8937, 8941, - 8945, 8949, 8953, 8957, 8961, 8965, 8969, 8973, 8977, 8981, 8985, 8989, - 8993, 8997, 9001, 9005, 9009, 9013, 9017, 9021, 9025, 9029, 9033, 9037, - 9041, 9045, 9049, 9053, 9057, 9061, 9065, 9069, 9073, 9077, 9081, 9085, - 9089, 9093, 9097, 9101, 9105, 9109, 9113, 9117, 9121, 9125, 9129, 9133, - 9137, 9141, 9145, 9149, 0, 0, 9153, 9157, 9161, 9165, 9169, 9173, 9177, - 9181, 9185, 9189, 9193, 9197, 9201, 9205, 9209, 9213, 9217, 9221, 9225, - 9229, 9233, 9237, 9241, 9245, 9249, 9253, 9257, 9261, 9265, 9269, 9273, - 9277, 9281, 9285, 9289, 9293, 9297, 9301, 9305, 9309, 9313, 9317, 9321, - 9325, 9329, 9333, 9337, 9341, 9345, 9349, 9353, 9357, 9361, 9365, 0, 0, + 7834, 7836, 7838, 7840, 7842, 7844, 7846, 7848, 7850, 7852, 7854, 7856, + 7858, 7860, 7862, 7864, 7866, 7868, 7870, 7872, 7874, 7876, 7878, 7880, + 7883, 7886, 7889, 7892, 7895, 7898, 7901, 7904, 7907, 7910, 7913, 7916, + 7919, 7922, 7925, 7928, 7931, 7934, 7936, 7938, 7940, 7942, 7945, 7948, + 7925, 7951, 7954, 7957, 7960, 7963, 7966, 7969, 7972, 7975, 7978, 7981, + 7984, 7987, 7990, 7993, 7996, 7999, 8002, 8005, 8008, 8011, 8014, 8017, + 8020, 8023, 8026, 8029, 8032, 8035, 8038, 8041, 8044, 8047, 8050, 8053, + 8056, 8059, 8062, 8065, 8068, 8071, 8074, 8077, 8080, 8083, 8086, 8089, + 8092, 8095, 8098, 8101, 8104, 8107, 8110, 8113, 8116, 8119, 8122, 8125, + 8128, 8131, 8134, 8137, 8140, 8143, 8146, 8149, 8152, 8155, 8158, 8161, + 8164, 8167, 8170, 8173, 8176, 8179, 8182, 8185, 8188, 8191, 8194, 8197, + 8200, 8203, 8206, 8209, 8212, 8215, 8218, 8221, 8225, 8229, 8233, 8237, + 8241, 8245, 8248, 8251, 8254, 7928, 8257, 8260, 8263, 8266, 8269, 8272, + 8275, 8278, 8281, 8284, 8287, 8290, 8293, 8296, 8299, 8302, 8305, 8308, + 8311, 8314, 8317, 8320, 8323, 8326, 8329, 8332, 8335, 8338, 8341, 8344, + 8347, 8350, 8353, 8356, 8359, 8362, 8365, 8368, 8371, 8374, 8377, 8380, + 8383, 8386, 8389, 8392, 8395, 8398, 8401, 8404, 8407, 8410, 8413, 8416, + 8419, 8422, 8425, 8428, 8431, 8434, 8437, 8440, 8443, 8446, 8449, 8452, + 8455, 8458, 8461, 8464, 8467, 8470, 8473, 8476, 8479, 8482, 8485, 8488, + 8491, 8494, 8497, 8500, 8503, 8506, 8509, 8512, 8515, 8518, 8521, 8524, + 8527, 8530, 8533, 8536, 8539, 8542, 8545, 8548, 8551, 8554, 8557, 8560, + 8563, 8566, 8569, 8572, 8575, 8578, 8581, 8584, 8587, 8590, 8593, 8596, + 8599, 8602, 8605, 8608, 8611, 8614, 8617, 8620, 8623, 8626, 8629, 8632, + 8635, 8638, 8641, 8644, 8647, 8650, 8653, 8656, 8659, 8662, 8665, 8668, + 8672, 8676, 8680, 8683, 8686, 8689, 8692, 8695, 8698, 8701, 8704, 8707, + 8710, 8713, 8716, 8719, 8722, 8725, 8728, 8731, 8734, 8737, 8740, 8743, + 8746, 8749, 8752, 8755, 8758, 8761, 8764, 8767, 8770, 8773, 8776, 8779, + 8782, 8785, 8788, 8791, 8794, 8797, 8800, 8803, 8806, 8809, 8812, 8815, + 8818, 8821, 8824, 8827, 8830, 8833, 8836, 8839, 8842, 8845, 8848, 8851, + 8854, 8857, 8860, 8863, 8866, 8869, 8872, 8875, 8878, 8881, 8884, 8887, + 8890, 8893, 8896, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8899, 8903, 8907, 8911, 8915, 8919, 8923, 8927, 8931, 8935, 8939, 8943, + 8947, 8951, 8955, 8959, 8963, 8967, 8971, 8975, 8979, 8983, 8987, 8991, + 8995, 8999, 9003, 9007, 9011, 9015, 9019, 9023, 9027, 9031, 9035, 9039, + 9043, 9047, 9051, 9055, 9059, 9063, 9067, 9071, 9075, 9079, 9083, 9087, + 9091, 9095, 9099, 9103, 9107, 9111, 9115, 9119, 9123, 9127, 9131, 9135, + 9139, 9143, 9147, 9151, 0, 0, 9155, 9159, 9163, 9167, 9171, 9175, 9179, + 9183, 9187, 9191, 9195, 9199, 9203, 9207, 9211, 9215, 9219, 9223, 9227, + 9231, 9235, 9239, 9243, 9247, 9251, 9255, 9259, 9263, 9267, 9271, 9275, + 9279, 9283, 9287, 9291, 9295, 9299, 9303, 9307, 9311, 9315, 9319, 9323, + 9327, 9331, 9335, 9339, 9343, 9347, 9351, 9355, 9359, 9363, 9367, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9369, 9373, 9377, 9382, 9387, - 9392, 9397, 9402, 9407, 9412, 9416, 9435, 9444, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9449, 9451, 9453, 9455, 9457, 9459, - 9461, 9463, 9465, 9467, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 9469, 9471, 9473, 9475, 9475, 9477, 9479, 9481, 9483, - 9485, 9487, 9489, 9491, 9493, 9495, 9497, 9499, 9501, 9503, 9505, 9507, - 0, 0, 9509, 9511, 9513, 9513, 9513, 9513, 9515, 9515, 9515, 9517, 9519, - 9521, 0, 9523, 9525, 9527, 9529, 9531, 9533, 9535, 9537, 9539, 9541, - 9543, 9545, 9547, 9549, 9551, 9553, 9555, 9557, 9559, 0, 9561, 9563, - 9565, 9567, 0, 0, 0, 0, 9569, 9572, 9575, 0, 9578, 0, 9581, 9584, 9587, - 9590, 9593, 9596, 9599, 9602, 9605, 9608, 9611, 9613, 9615, 9617, 9619, - 9621, 9623, 9625, 9627, 9629, 9631, 9633, 9635, 9637, 9639, 9641, 9643, - 9645, 9647, 9649, 9651, 9653, 9655, 9657, 9659, 9661, 9663, 9665, 9667, - 9669, 9671, 9673, 9675, 9677, 9679, 9681, 9683, 9685, 9687, 9689, 9691, - 9693, 9695, 9697, 9699, 9701, 9703, 9705, 9707, 9709, 9711, 9713, 9715, - 9717, 9719, 9721, 9723, 9725, 9727, 9729, 9731, 9733, 9735, 9737, 9739, - 9741, 9743, 9745, 9747, 9749, 9751, 9753, 9755, 9757, 9759, 9761, 9763, - 9765, 9767, 9769, 9771, 9773, 9775, 9777, 9779, 9781, 9783, 9785, 9787, - 9789, 9791, 9793, 9795, 9797, 9799, 9801, 9803, 9805, 9807, 9809, 9811, - 9813, 9815, 9817, 9819, 9821, 9823, 9825, 9827, 9829, 9831, 9833, 9835, - 9837, 9839, 9841, 9843, 9845, 9848, 9851, 9854, 9857, 9860, 9863, 9866, - 0, 0, 0, 0, 9869, 9871, 9873, 9875, 9877, 9879, 9881, 9883, 9885, 9887, - 9889, 9891, 9893, 9895, 9897, 9899, 9901, 9903, 9905, 9907, 9909, 9911, - 9913, 9915, 9917, 9919, 9921, 9923, 9925, 9927, 9929, 9931, 9933, 9935, - 9937, 9939, 9941, 9943, 9945, 9947, 9949, 9951, 9953, 9955, 9957, 9959, - 9961, 9963, 9965, 9967, 9969, 9971, 9973, 9975, 9977, 9979, 9981, 9983, - 9985, 9987, 9989, 9991, 9993, 9995, 9997, 9999, 10001, 10003, 10005, - 10007, 10009, 10011, 10013, 10015, 10017, 10019, 10021, 10023, 10025, - 10027, 10029, 10031, 10033, 10035, 10037, 10039, 10041, 10043, 10045, - 10047, 10049, 10051, 10053, 10055, 10057, 10059, 10061, 10063, 10065, - 10067, 10069, 10071, 10073, 10075, 10077, 10079, 10081, 10083, 10085, - 10087, 10089, 10091, 10093, 10095, 10097, 10099, 10101, 10103, 10105, - 10107, 10109, 10111, 10113, 10115, 10117, 10119, 10121, 10123, 10125, - 10127, 10129, 10131, 10133, 10135, 10137, 10139, 10141, 10143, 10145, - 10147, 10149, 10151, 10153, 10155, 10157, 10159, 10161, 10163, 10165, - 10167, 10169, 10171, 10173, 10175, 10177, 10179, 10181, 10183, 10185, - 10187, 10189, 10191, 10193, 10195, 10197, 10199, 10201, 10203, 10205, - 10207, 10209, 10211, 10213, 10215, 10217, 10219, 10221, 10223, 10225, - 10227, 10229, 10231, 10233, 10235, 10237, 10239, 10241, 10243, 10245, - 10247, 0, 0, 0, 10249, 10251, 10253, 10255, 10257, 10259, 0, 0, 10261, - 10263, 10265, 10267, 10269, 10271, 0, 0, 10273, 10275, 10277, 10279, - 10281, 10283, 0, 0, 10285, 10287, 10289, 0, 0, 0, 10291, 10293, 10295, - 10297, 10299, 10301, 10303, 0, 10305, 10307, 10309, 10311, 10313, 10315, - 10317, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9371, 9375, 9379, 9384, 9389, + 9394, 9399, 9404, 9409, 9414, 9418, 9437, 9446, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9451, 9453, 9455, 9457, 9459, 9461, + 9463, 9465, 9467, 9469, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9471, 9473, 9475, 9477, 9477, 9479, 9481, 9483, 9485, + 9487, 9489, 9491, 9493, 9495, 9497, 9499, 9501, 9503, 9505, 9507, 9509, + 0, 0, 9511, 9513, 9515, 9515, 9515, 9515, 9517, 9517, 9517, 9519, 9521, + 9523, 0, 9525, 9527, 9529, 9531, 9533, 9535, 9537, 9539, 9541, 9543, + 9545, 9547, 9549, 9551, 9553, 9555, 9557, 9559, 9561, 0, 9563, 9565, + 9567, 9569, 0, 0, 0, 0, 9571, 9574, 9577, 0, 9580, 0, 9583, 9586, 9589, + 9592, 9595, 9598, 9601, 9604, 9607, 9610, 9613, 9615, 9617, 9619, 9621, + 9623, 9625, 9627, 9629, 9631, 9633, 9635, 9637, 9639, 9641, 9643, 9645, + 9647, 9649, 9651, 9653, 9655, 9657, 9659, 9661, 9663, 9665, 9667, 9669, + 9671, 9673, 9675, 9677, 9679, 9681, 9683, 9685, 9687, 9689, 9691, 9693, + 9695, 9697, 9699, 9701, 9703, 9705, 9707, 9709, 9711, 9713, 9715, 9717, + 9719, 9721, 9723, 9725, 9727, 9729, 9731, 9733, 9735, 9737, 9739, 9741, + 9743, 9745, 9747, 9749, 9751, 9753, 9755, 9757, 9759, 9761, 9763, 9765, + 9767, 9769, 9771, 9773, 9775, 9777, 9779, 9781, 9783, 9785, 9787, 9789, + 9791, 9793, 9795, 9797, 9799, 9801, 9803, 9805, 9807, 9809, 9811, 9813, + 9815, 9817, 9819, 9821, 9823, 9825, 9827, 9829, 9831, 9833, 9835, 9837, + 9839, 9841, 9843, 9845, 9847, 9850, 9853, 9856, 9859, 9862, 9865, 9868, + 0, 0, 0, 0, 9871, 9873, 9875, 9877, 9879, 9881, 9883, 9885, 9887, 9889, + 9891, 9893, 9895, 9897, 9899, 9901, 9903, 9905, 9907, 9909, 9911, 9913, + 9915, 9917, 9919, 9921, 9923, 9925, 9927, 9929, 9931, 9933, 9935, 9937, + 9939, 9941, 9943, 9945, 9947, 9949, 9951, 9953, 9955, 9957, 9959, 9961, + 9963, 9965, 9967, 9969, 9971, 9973, 9975, 9977, 9979, 9981, 9983, 9985, + 9987, 9989, 9991, 9993, 9995, 9997, 9999, 10001, 10003, 10005, 10007, + 10009, 10011, 10013, 10015, 10017, 10019, 10021, 10023, 10025, 10027, + 10029, 10031, 10033, 10035, 10037, 10039, 10041, 10043, 10045, 10047, + 10049, 10051, 10053, 10055, 10057, 10059, 10061, 10063, 10065, 10067, + 10069, 10071, 10073, 10075, 10077, 10079, 10081, 10083, 10085, 10087, + 10089, 10091, 10093, 10095, 10097, 10099, 10101, 10103, 10105, 10107, + 10109, 10111, 10113, 10115, 10117, 10119, 10121, 10123, 10125, 10127, + 10129, 10131, 10133, 10135, 10137, 10139, 10141, 10143, 10145, 10147, + 10149, 10151, 10153, 10155, 10157, 10159, 10161, 10163, 10165, 10167, + 10169, 10171, 10173, 10175, 10177, 10179, 10181, 10183, 10185, 10187, + 10189, 10191, 10193, 10195, 10197, 10199, 10201, 10203, 10205, 10207, + 10209, 10211, 10213, 10215, 10217, 10219, 10221, 10223, 10225, 10227, + 10229, 10231, 10233, 10235, 10237, 10239, 10241, 10243, 10245, 10247, + 10249, 0, 0, 0, 10251, 10253, 10255, 10257, 10259, 10261, 0, 0, 10263, + 10265, 10267, 10269, 10271, 10273, 0, 0, 10275, 10277, 10279, 10281, + 10283, 10285, 0, 0, 10287, 10289, 10291, 0, 0, 0, 10293, 10295, 10297, + 10299, 10301, 10303, 10305, 0, 10307, 10309, 10311, 10313, 10315, 10317, + 10319, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10319, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10321, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 10322, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 10325, 10327, 10329, 10331, 10333, 0, 10335, 10337, - 10339, 10341, 10343, 10345, 10347, 10349, 10351, 10353, 10355, 10357, - 10359, 10361, 10363, 10365, 10367, 10369, 10371, 10373, 10375, 10377, - 10379, 10381, 10383, 10385, 10387, 10389, 10391, 10393, 10395, 10397, - 10399, 10401, 10403, 10405, 10407, 10409, 10411, 10413, 10415, 10417, 0, - 10419, 10421, 10423, 10425, 10427, 10429, 10431, 10433, 10435, 0, 0, 0, + 10324, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 10327, 10329, 10331, 10333, 10335, 0, 10337, 10339, + 10341, 10343, 10345, 10347, 10349, 10351, 10353, 10355, 10357, 10359, + 10361, 10363, 10365, 10367, 10369, 10371, 10373, 10375, 10377, 10379, + 10381, 10383, 10385, 10387, 10389, 10391, 10393, 10395, 10397, 10399, + 10401, 10403, 10405, 10407, 10409, 10411, 10413, 10415, 10417, 10419, 0, + 10421, 10423, 10425, 10427, 10429, 10431, 10433, 10435, 10437, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10437, 0, - 10440, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10443, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10439, 0, + 10442, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10445, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 10446, 10449, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10448, 10451, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10452, 10455, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10454, 10457, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 10458, 0, 10461, 0, 0, 0, 0, 0, 0, 0, 0, 10464, 0, 0, - 10467, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 10460, 0, 10463, 0, 0, 0, 0, 0, 0, 0, 0, 10466, 0, 0, + 10469, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 10470, 0, 10473, 10476, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 10472, 0, 10475, 10478, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 10479, 10482, 0, 10485, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10481, 10484, 0, 10487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10488, 10491, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10490, 10493, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 10494, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 10496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10497, 10500, 10503, 10506, 10509, - 10512, 10515, 10518, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10499, 10502, 10505, 10508, 10511, + 10514, 10517, 10520, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -5445,234 +6458,234 @@ static const unsigned short decomp_index2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 10521, 10524, 10527, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 10523, 10526, 10529, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 10530, 3253, 3192, 3289, 3257, 3259, 10532, 3212, 3218, 10534, 10536, - 3220, 3261, 3224, 10538, 3229, 3231, 3233, 10540, 10542, 10544, 10546, - 10548, 10550, 10552, 3245, 10554, 10556, 10558, 10560, 10562, 10564, - 10566, 10568, 10570, 10572, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10532, 3253, 3192, 3289, 3257, 3259, 10534, 3212, 3218, 10536, 10538, + 3220, 3261, 3224, 10540, 3229, 3231, 3233, 10542, 10544, 10546, 10548, + 10550, 10552, 10554, 3245, 10556, 10558, 10560, 10562, 10564, 10566, + 10568, 10570, 10572, 10574, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10574, 10577, 10580, 10583, 10586, - 10589, 10592, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10576, 10579, 10582, 10585, 10588, + 10591, 10594, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10595, 10598, - 10601, 10604, 10607, 10610, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10597, 10600, + 10603, 10606, 10609, 10612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 10530, 3253, 3192, 3289, 3257, 3259, 10532, 3212, 3218, 10534, 10536, - 3220, 3261, 3224, 10538, 3229, 3231, 3233, 10540, 10542, 10544, 10546, - 10548, 10550, 10552, 3245, 10613, 10615, 10617, 3291, 3255, 10619, 3210, - 3214, 3273, 3293, 10621, 3222, 10623, 10625, 3263, 10627, 10629, 10631, - 10633, 10635, 10637, 10639, 10641, 10643, 10645, 10647, 10530, 3253, - 3192, 3289, 3257, 3259, 10532, 3212, 3218, 10534, 10536, 3220, 3261, - 3224, 10538, 3229, 3231, 3233, 10540, 10542, 10544, 10546, 10548, 10550, - 10552, 3245, 10613, 10615, 10617, 3291, 3255, 10619, 3210, 0, 3273, 3293, - 10621, 3222, 10623, 10625, 3263, 10627, 10629, 10631, 10633, 10635, - 10637, 10639, 10641, 10643, 10645, 10647, 10530, 3253, 3192, 3289, 3257, - 3259, 10532, 3212, 3218, 10534, 10536, 3220, 3261, 3224, 10538, 3229, - 3231, 3233, 10540, 10542, 10544, 10546, 10548, 10550, 10552, 3245, 10613, - 10615, 10617, 3291, 3255, 10619, 3210, 3214, 3273, 3293, 10621, 3222, - 10623, 10625, 3263, 10627, 10629, 10631, 10633, 10635, 10637, 10639, - 10641, 10643, 10645, 10647, 10530, 0, 3192, 3289, 0, 0, 10532, 0, 0, - 10534, 10536, 0, 0, 3224, 10538, 3229, 3231, 0, 10540, 10542, 10544, - 10546, 10548, 10550, 10552, 3245, 10613, 10615, 10617, 3291, 0, 10619, 0, - 3214, 3273, 3293, 10621, 3222, 10623, 10625, 0, 10627, 10629, 10631, - 10633, 10635, 10637, 10639, 10641, 10643, 10645, 10647, 10530, 3253, - 3192, 3289, 3257, 3259, 10532, 3212, 3218, 10534, 10536, 3220, 3261, - 3224, 10538, 3229, 3231, 3233, 10540, 10542, 10544, 10546, 10548, 10550, - 10552, 3245, 10613, 10615, 10617, 3291, 3255, 10619, 3210, 3214, 3273, - 3293, 10621, 3222, 10623, 10625, 3263, 10627, 10629, 10631, 10633, 10635, - 10637, 10639, 10641, 10643, 10645, 10647, 10530, 3253, 0, 3289, 3257, - 3259, 10532, 0, 0, 10534, 10536, 3220, 3261, 3224, 10538, 3229, 3231, 0, - 10540, 10542, 10544, 10546, 10548, 10550, 10552, 0, 10613, 10615, 10617, - 3291, 3255, 10619, 3210, 3214, 3273, 3293, 10621, 3222, 10623, 10625, - 3263, 10627, 10629, 10631, 10633, 10635, 10637, 10639, 10641, 10643, - 10645, 10647, 10530, 3253, 0, 3289, 3257, 3259, 10532, 0, 3218, 10534, - 10536, 3220, 3261, 0, 10538, 0, 0, 0, 10540, 10542, 10544, 10546, 10548, - 10550, 10552, 0, 10613, 10615, 10617, 3291, 3255, 10619, 3210, 3214, - 3273, 3293, 10621, 3222, 10623, 10625, 3263, 10627, 10629, 10631, 10633, - 10635, 10637, 10639, 10641, 10643, 10645, 10647, 10530, 3253, 3192, 3289, - 3257, 3259, 10532, 3212, 3218, 10534, 10536, 3220, 3261, 3224, 10538, - 3229, 3231, 3233, 10540, 10542, 10544, 10546, 10548, 10550, 10552, 3245, - 10613, 10615, 10617, 3291, 3255, 10619, 3210, 3214, 3273, 3293, 10621, - 3222, 10623, 10625, 3263, 10627, 10629, 10631, 10633, 10635, 10637, - 10639, 10641, 10643, 10645, 10647, 10530, 3253, 3192, 3289, 3257, 3259, - 10532, 3212, 3218, 10534, 10536, 3220, 3261, 3224, 10538, 3229, 3231, - 3233, 10540, 10542, 10544, 10546, 10548, 10550, 10552, 3245, 10613, - 10615, 10617, 3291, 3255, 10619, 3210, 3214, 3273, 3293, 10621, 3222, - 10623, 10625, 3263, 10627, 10629, 10631, 10633, 10635, 10637, 10639, - 10641, 10643, 10645, 10647, 10530, 3253, 3192, 3289, 3257, 3259, 10532, - 3212, 3218, 10534, 10536, 3220, 3261, 3224, 10538, 3229, 3231, 3233, - 10540, 10542, 10544, 10546, 10548, 10550, 10552, 3245, 10613, 10615, - 10617, 3291, 3255, 10619, 3210, 3214, 3273, 3293, 10621, 3222, 10623, - 10625, 3263, 10627, 10629, 10631, 10633, 10635, 10637, 10639, 10641, - 10643, 10645, 10647, 10530, 3253, 3192, 3289, 3257, 3259, 10532, 3212, - 3218, 10534, 10536, 3220, 3261, 3224, 10538, 3229, 3231, 3233, 10540, - 10542, 10544, 10546, 10548, 10550, 10552, 3245, 10613, 10615, 10617, - 3291, 3255, 10619, 3210, 3214, 3273, 3293, 10621, 3222, 10623, 10625, - 3263, 10627, 10629, 10631, 10633, 10635, 10637, 10639, 10641, 10643, - 10645, 10647, 10530, 3253, 3192, 3289, 3257, 3259, 10532, 3212, 3218, - 10534, 10536, 3220, 3261, 3224, 10538, 3229, 3231, 3233, 10540, 10542, - 10544, 10546, 10548, 10550, 10552, 3245, 10613, 10615, 10617, 3291, 3255, - 10619, 3210, 3214, 3273, 3293, 10621, 3222, 10623, 10625, 3263, 10627, - 10629, 10631, 10633, 10635, 10637, 10639, 10641, 10643, 10645, 10647, - 10530, 3253, 3192, 3289, 3257, 3259, 10532, 3212, 3218, 10534, 10536, - 3220, 3261, 3224, 10538, 3229, 3231, 3233, 10540, 10542, 10544, 10546, - 10548, 10550, 10552, 3245, 10613, 10615, 10617, 3291, 3255, 10619, 3210, - 3214, 3273, 3293, 10621, 3222, 10623, 10625, 3263, 10627, 10629, 10631, - 10633, 10635, 10637, 10639, 10641, 10643, 10645, 10647, 10649, 10651, 0, - 0, 10653, 10655, 3283, 10657, 10659, 10661, 10663, 10665, 10667, 10669, - 10671, 10673, 10675, 10677, 10679, 3285, 10681, 10683, 10685, 10687, - 10689, 10691, 10693, 10695, 10697, 10699, 10701, 10703, 3281, 10705, - 10707, 10709, 10711, 10713, 10715, 10717, 10719, 10721, 10723, 10725, - 10727, 3279, 10729, 10731, 10733, 10735, 10737, 10739, 10741, 10743, - 10745, 10747, 10749, 10751, 10753, 10755, 10757, 10759, 10653, 10655, - 3283, 10657, 10659, 10661, 10663, 10665, 10667, 10669, 10671, 10673, - 10675, 10677, 10679, 3285, 10681, 10683, 10685, 10687, 10689, 10691, - 10693, 10695, 10697, 10699, 10701, 10703, 3281, 10705, 10707, 10709, - 10711, 10713, 10715, 10717, 10719, 10721, 10723, 10725, 10727, 3279, - 10729, 10731, 10733, 10735, 10737, 10739, 10741, 10743, 10745, 10747, - 10749, 10751, 10753, 10755, 10757, 10759, 10653, 10655, 3283, 10657, - 10659, 10661, 10663, 10665, 10667, 10669, 10671, 10673, 10675, 10677, - 10679, 3285, 10681, 10683, 10685, 10687, 10689, 10691, 10693, 10695, - 10697, 10699, 10701, 10703, 3281, 10705, 10707, 10709, 10711, 10713, - 10715, 10717, 10719, 10721, 10723, 10725, 10727, 3279, 10729, 10731, - 10733, 10735, 10737, 10739, 10741, 10743, 10745, 10747, 10749, 10751, - 10753, 10755, 10757, 10759, 10653, 10655, 3283, 10657, 10659, 10661, - 10663, 10665, 10667, 10669, 10671, 10673, 10675, 10677, 10679, 3285, - 10681, 10683, 10685, 10687, 10689, 10691, 10693, 10695, 10697, 10699, - 10701, 10703, 3281, 10705, 10707, 10709, 10711, 10713, 10715, 10717, - 10719, 10721, 10723, 10725, 10727, 3279, 10729, 10731, 10733, 10735, - 10737, 10739, 10741, 10743, 10745, 10747, 10749, 10751, 10753, 10755, - 10757, 10759, 10653, 10655, 3283, 10657, 10659, 10661, 10663, 10665, - 10667, 10669, 10671, 10673, 10675, 10677, 10679, 3285, 10681, 10683, - 10685, 10687, 10689, 10691, 10693, 10695, 10697, 10699, 10701, 10703, - 3281, 10705, 10707, 10709, 10711, 10713, 10715, 10717, 10719, 10721, - 10723, 10725, 10727, 3279, 10729, 10731, 10733, 10735, 10737, 10739, - 10741, 10743, 10745, 10747, 10749, 10751, 10753, 10755, 10757, 10759, - 10761, 10763, 0, 0, 10554, 10556, 10558, 10560, 10562, 10564, 10566, - 10568, 10570, 10572, 10554, 10556, 10558, 10560, 10562, 10564, 10566, - 10568, 10570, 10572, 10554, 10556, 10558, 10560, 10562, 10564, 10566, - 10568, 10570, 10572, 10554, 10556, 10558, 10560, 10562, 10564, 10566, - 10568, 10570, 10572, 10554, 10556, 10558, 10560, 10562, 10564, 10566, - 10568, 10570, 10572, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 10765, 10767, 10769, 10771, 10773, 10775, 10777, - 10779, 10781, 10783, 10785, 10787, 10789, 10791, 10793, 10795, 10797, - 10799, 10801, 10803, 10805, 10807, 10809, 10811, 10813, 10815, 10817, - 10819, 10821, 10823, 10825, 10827, 10829, 10831, 10833, 10835, 10837, - 10839, 10841, 10843, 10845, 10847, 10849, 10851, 10853, 10855, 10857, - 10859, 10861, 10863, 10865, 10867, 10869, 10871, 10873, 10875, 10877, - 10879, 10881, 10883, 10885, 10887, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 10889, 10891, 10893, 10895, 0, 10897, 10899, 10901, 10903, - 10905, 10907, 10909, 10911, 10913, 10915, 10917, 10919, 10921, 10923, - 10925, 10927, 10929, 10931, 10933, 10935, 10937, 10939, 10941, 10943, - 10945, 10947, 10949, 0, 10891, 10893, 0, 10951, 0, 0, 10901, 0, 10905, - 10907, 10909, 10911, 10913, 10915, 10917, 10919, 10921, 10923, 0, 10927, - 10929, 10931, 10933, 0, 10937, 0, 10941, 0, 0, 0, 0, 0, 0, 10893, 0, 0, - 0, 0, 10901, 0, 10905, 0, 10909, 0, 10913, 10915, 10917, 0, 10921, 10923, - 0, 10927, 0, 0, 10933, 0, 10937, 0, 10941, 0, 10945, 0, 10949, 0, 10891, - 10893, 0, 10951, 0, 0, 10901, 10903, 10905, 10907, 0, 10911, 10913, - 10915, 10917, 10919, 10921, 10923, 0, 10927, 10929, 10931, 10933, 0, - 10937, 10939, 10941, 10943, 0, 10947, 0, 10889, 10891, 10893, 10895, - 10951, 10897, 10899, 10901, 10903, 10905, 0, 10909, 10911, 10913, 10915, - 10917, 10919, 10921, 10923, 10925, 10927, 10929, 10931, 10933, 10935, - 10937, 10939, 10941, 0, 0, 0, 0, 0, 10891, 10893, 10895, 0, 10897, 10899, - 10901, 10903, 10905, 0, 10909, 10911, 10913, 10915, 10917, 10919, 10921, - 10923, 10925, 10927, 10929, 10931, 10933, 10935, 10937, 10939, 10941, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10953, 10956, - 10959, 10962, 10965, 10968, 10971, 10974, 10977, 10980, 10983, 0, 0, 0, - 0, 0, 10986, 10990, 10994, 10998, 11002, 11006, 11010, 11014, 11018, - 11022, 11026, 11030, 11034, 11038, 11042, 11046, 11050, 11054, 11058, - 11062, 11066, 11070, 11074, 11078, 11082, 11086, 11090, 3926, 3956, - 11094, 11097, 0, 11100, 11102, 11104, 11106, 11108, 11110, 11112, 11114, - 11116, 11118, 11120, 11122, 11124, 11126, 11128, 11130, 11132, 11134, - 11136, 11138, 11140, 11142, 11144, 11146, 11148, 11150, 11152, 6348, - 11155, 11158, 11161, 11165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11168, 11171, 11174, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 11177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11180, - 11183, 11186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11188, 11190, 11192, - 11194, 11196, 11198, 11200, 11202, 11204, 11206, 11208, 11210, 11212, - 11214, 11216, 11218, 11220, 11222, 11224, 11226, 11228, 11230, 11232, - 11234, 11236, 11238, 11240, 11242, 11244, 11246, 11248, 11250, 11252, - 11254, 11256, 11258, 11260, 11262, 11264, 11266, 11268, 11270, 11272, - 11274, 0, 0, 0, 0, 11276, 11280, 11284, 11288, 11292, 11296, 11300, - 11304, 11308, 0, 0, 0, 0, 0, 0, 0, 11312, 11314, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 10554, 10556, 10558, 10560, 10562, 10564, 10566, 10568, - 10570, 10572, 0, 0, 0, 0, 0, 0, 11316, 11318, 11320, 11322, 11324, 7195, - 11326, 11328, 11330, 11332, 7197, 11334, 11336, 11338, 7199, 11340, - 11342, 11344, 11346, 11348, 11350, 11352, 11354, 11356, 11358, 11360, - 11362, 7315, 11364, 11366, 11368, 11370, 11372, 11374, 11376, 11378, - 11380, 7325, 7201, 7203, 7327, 11382, 11384, 6817, 11386, 7205, 11388, - 11390, 11392, 11394, 11394, 11394, 11396, 11398, 11400, 11402, 11404, - 11406, 11408, 11410, 11412, 11414, 11416, 11418, 11420, 11422, 11424, - 11426, 11428, 11430, 11430, 7331, 11432, 11434, 11436, 11438, 7209, - 11440, 11442, 11444, 7123, 11446, 11448, 11450, 11452, 11454, 11456, - 11458, 11460, 11462, 11464, 11466, 11468, 11470, 11472, 11474, 11476, - 11478, 11480, 11482, 11484, 11486, 11488, 11490, 11492, 11494, 11496, - 11496, 11498, 11500, 11502, 6809, 11504, 11506, 11508, 11510, 11512, - 11514, 11516, 11518, 7219, 11520, 11522, 11524, 11526, 11528, 11530, - 11532, 11534, 11536, 11538, 11540, 11542, 11544, 11546, 11548, 11550, - 11552, 11554, 11556, 11558, 11560, 6701, 11562, 11564, 11566, 11566, - 11568, 11570, 11570, 11572, 11574, 11576, 11578, 11580, 11582, 11584, - 11586, 11588, 11590, 11592, 11594, 11596, 7221, 11598, 11600, 11602, - 11604, 7355, 11604, 11606, 7225, 11608, 11610, 11612, 11614, 7227, 6647, - 11616, 11618, 11620, 11622, 11624, 11626, 11628, 11630, 11632, 11634, - 11636, 11638, 11640, 11642, 11644, 11646, 11648, 11650, 11652, 11654, - 11656, 11658, 7229, 11660, 11662, 11664, 11666, 11668, 11670, 7233, - 11672, 11674, 11676, 11678, 11680, 11682, 11684, 11686, 6703, 7371, - 11688, 11690, 11692, 11694, 11696, 11698, 11700, 11702, 7235, 11704, - 11706, 11708, 11710, 7457, 11712, 11714, 11716, 11718, 11720, 11722, - 11724, 11726, 11728, 11730, 11732, 11734, 11736, 6843, 11738, 11740, - 11742, 11744, 11746, 11748, 11750, 11752, 11754, 11756, 11758, 7237, - 7017, 11760, 11762, 11764, 11766, 11768, 11770, 11772, 11774, 7379, - 11776, 11778, 11780, 11782, 11784, 11786, 11788, 11790, 7381, 11792, - 11794, 11796, 11798, 11800, 11802, 11804, 11806, 11808, 11810, 11812, - 11814, 7385, 11816, 11818, 11820, 11822, 11824, 11826, 11828, 11830, - 11832, 11834, 11836, 11836, 11838, 11840, 7389, 11842, 11844, 11846, - 11848, 11850, 11852, 11854, 6815, 11856, 11858, 11860, 11862, 11864, - 11866, 11868, 7401, 11870, 11872, 11874, 11876, 11878, 11880, 11880, - 7403, 7461, 11882, 11884, 11886, 11888, 11890, 6739, 7407, 11892, 11894, - 7259, 11896, 11898, 7167, 11900, 11902, 7267, 11904, 11906, 11908, 11910, - 11910, 11912, 11914, 11916, 11918, 11920, 11922, 11924, 11926, 11928, - 11930, 11932, 11934, 11936, 11938, 11940, 11942, 11944, 11946, 11948, - 11950, 11952, 11954, 11956, 11958, 11960, 11962, 11964, 7279, 11966, - 11968, 11970, 11972, 11974, 11976, 11978, 11980, 11982, 11984, 11986, - 11988, 11990, 11992, 11994, 11996, 11568, 11998, 12000, 12002, 12004, - 12006, 12008, 12010, 12012, 12014, 12016, 12018, 12020, 6851, 12022, - 12024, 12026, 12028, 12030, 12032, 7285, 12034, 12036, 12038, 12040, - 12042, 12044, 12046, 12048, 12050, 12052, 12054, 12056, 12058, 12060, - 12062, 12064, 12066, 12068, 12070, 12072, 6729, 12074, 12076, 12078, - 12080, 12082, 12084, 7421, 12086, 12088, 12090, 12092, 12094, 12096, - 12098, 12100, 12102, 12104, 12106, 12108, 12110, 12112, 12114, 12116, - 12118, 12120, 12122, 12124, 7431, 7433, 12126, 12128, 12130, 12132, - 12134, 12136, 12138, 12140, 12142, 12144, 12146, 12148, 12150, 7435, - 12152, 12154, 12156, 12158, 12160, 12162, 12164, 12166, 12168, 12170, - 12172, 12174, 12176, 12178, 12180, 12182, 12184, 12186, 12188, 12190, - 12192, 12194, 12196, 12198, 12200, 12202, 12204, 12206, 12208, 12210, - 7447, 7447, 12212, 12214, 12216, 12218, 12220, 12222, 12224, 12226, - 12228, 12230, 7449, 12232, 12234, 12236, 12238, 12240, 12242, 12244, - 12246, 12248, 12250, 12252, 12254, 12256, 12258, 12260, 12262, 12264, - 12266, 12268, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10532, 3253, 3192, 3289, 3257, 3259, 10534, 3212, 3218, 10536, 10538, + 3220, 3261, 3224, 10540, 3229, 3231, 3233, 10542, 10544, 10546, 10548, + 10550, 10552, 10554, 3245, 10615, 10617, 10619, 3291, 3255, 10621, 3210, + 3214, 3273, 3293, 10623, 3222, 10625, 10627, 3263, 10629, 10631, 10633, + 10635, 10637, 10639, 10641, 10643, 10645, 10647, 10649, 10532, 3253, + 3192, 3289, 3257, 3259, 10534, 3212, 3218, 10536, 10538, 3220, 3261, + 3224, 10540, 3229, 3231, 3233, 10542, 10544, 10546, 10548, 10550, 10552, + 10554, 3245, 10615, 10617, 10619, 3291, 3255, 10621, 3210, 0, 3273, 3293, + 10623, 3222, 10625, 10627, 3263, 10629, 10631, 10633, 10635, 10637, + 10639, 10641, 10643, 10645, 10647, 10649, 10532, 3253, 3192, 3289, 3257, + 3259, 10534, 3212, 3218, 10536, 10538, 3220, 3261, 3224, 10540, 3229, + 3231, 3233, 10542, 10544, 10546, 10548, 10550, 10552, 10554, 3245, 10615, + 10617, 10619, 3291, 3255, 10621, 3210, 3214, 3273, 3293, 10623, 3222, + 10625, 10627, 3263, 10629, 10631, 10633, 10635, 10637, 10639, 10641, + 10643, 10645, 10647, 10649, 10532, 0, 3192, 3289, 0, 0, 10534, 0, 0, + 10536, 10538, 0, 0, 3224, 10540, 3229, 3231, 0, 10542, 10544, 10546, + 10548, 10550, 10552, 10554, 3245, 10615, 10617, 10619, 3291, 0, 10621, 0, + 3214, 3273, 3293, 10623, 3222, 10625, 10627, 0, 10629, 10631, 10633, + 10635, 10637, 10639, 10641, 10643, 10645, 10647, 10649, 10532, 3253, + 3192, 3289, 3257, 3259, 10534, 3212, 3218, 10536, 10538, 3220, 3261, + 3224, 10540, 3229, 3231, 3233, 10542, 10544, 10546, 10548, 10550, 10552, + 10554, 3245, 10615, 10617, 10619, 3291, 3255, 10621, 3210, 3214, 3273, + 3293, 10623, 3222, 10625, 10627, 3263, 10629, 10631, 10633, 10635, 10637, + 10639, 10641, 10643, 10645, 10647, 10649, 10532, 3253, 0, 3289, 3257, + 3259, 10534, 0, 0, 10536, 10538, 3220, 3261, 3224, 10540, 3229, 3231, 0, + 10542, 10544, 10546, 10548, 10550, 10552, 10554, 0, 10615, 10617, 10619, + 3291, 3255, 10621, 3210, 3214, 3273, 3293, 10623, 3222, 10625, 10627, + 3263, 10629, 10631, 10633, 10635, 10637, 10639, 10641, 10643, 10645, + 10647, 10649, 10532, 3253, 0, 3289, 3257, 3259, 10534, 0, 3218, 10536, + 10538, 3220, 3261, 0, 10540, 0, 0, 0, 10542, 10544, 10546, 10548, 10550, + 10552, 10554, 0, 10615, 10617, 10619, 3291, 3255, 10621, 3210, 3214, + 3273, 3293, 10623, 3222, 10625, 10627, 3263, 10629, 10631, 10633, 10635, + 10637, 10639, 10641, 10643, 10645, 10647, 10649, 10532, 3253, 3192, 3289, + 3257, 3259, 10534, 3212, 3218, 10536, 10538, 3220, 3261, 3224, 10540, + 3229, 3231, 3233, 10542, 10544, 10546, 10548, 10550, 10552, 10554, 3245, + 10615, 10617, 10619, 3291, 3255, 10621, 3210, 3214, 3273, 3293, 10623, + 3222, 10625, 10627, 3263, 10629, 10631, 10633, 10635, 10637, 10639, + 10641, 10643, 10645, 10647, 10649, 10532, 3253, 3192, 3289, 3257, 3259, + 10534, 3212, 3218, 10536, 10538, 3220, 3261, 3224, 10540, 3229, 3231, + 3233, 10542, 10544, 10546, 10548, 10550, 10552, 10554, 3245, 10615, + 10617, 10619, 3291, 3255, 10621, 3210, 3214, 3273, 3293, 10623, 3222, + 10625, 10627, 3263, 10629, 10631, 10633, 10635, 10637, 10639, 10641, + 10643, 10645, 10647, 10649, 10532, 3253, 3192, 3289, 3257, 3259, 10534, + 3212, 3218, 10536, 10538, 3220, 3261, 3224, 10540, 3229, 3231, 3233, + 10542, 10544, 10546, 10548, 10550, 10552, 10554, 3245, 10615, 10617, + 10619, 3291, 3255, 10621, 3210, 3214, 3273, 3293, 10623, 3222, 10625, + 10627, 3263, 10629, 10631, 10633, 10635, 10637, 10639, 10641, 10643, + 10645, 10647, 10649, 10532, 3253, 3192, 3289, 3257, 3259, 10534, 3212, + 3218, 10536, 10538, 3220, 3261, 3224, 10540, 3229, 3231, 3233, 10542, + 10544, 10546, 10548, 10550, 10552, 10554, 3245, 10615, 10617, 10619, + 3291, 3255, 10621, 3210, 3214, 3273, 3293, 10623, 3222, 10625, 10627, + 3263, 10629, 10631, 10633, 10635, 10637, 10639, 10641, 10643, 10645, + 10647, 10649, 10532, 3253, 3192, 3289, 3257, 3259, 10534, 3212, 3218, + 10536, 10538, 3220, 3261, 3224, 10540, 3229, 3231, 3233, 10542, 10544, + 10546, 10548, 10550, 10552, 10554, 3245, 10615, 10617, 10619, 3291, 3255, + 10621, 3210, 3214, 3273, 3293, 10623, 3222, 10625, 10627, 3263, 10629, + 10631, 10633, 10635, 10637, 10639, 10641, 10643, 10645, 10647, 10649, + 10532, 3253, 3192, 3289, 3257, 3259, 10534, 3212, 3218, 10536, 10538, + 3220, 3261, 3224, 10540, 3229, 3231, 3233, 10542, 10544, 10546, 10548, + 10550, 10552, 10554, 3245, 10615, 10617, 10619, 3291, 3255, 10621, 3210, + 3214, 3273, 3293, 10623, 3222, 10625, 10627, 3263, 10629, 10631, 10633, + 10635, 10637, 10639, 10641, 10643, 10645, 10647, 10649, 10651, 10653, 0, + 0, 10655, 10657, 3283, 10659, 10661, 10663, 10665, 10667, 10669, 10671, + 10673, 10675, 10677, 10679, 10681, 3285, 10683, 10685, 10687, 10689, + 10691, 10693, 10695, 10697, 10699, 10701, 10703, 10705, 3281, 10707, + 10709, 10711, 10713, 10715, 10717, 10719, 10721, 10723, 10725, 10727, + 10729, 3279, 10731, 10733, 10735, 10737, 10739, 10741, 10743, 10745, + 10747, 10749, 10751, 10753, 10755, 10757, 10759, 10761, 10655, 10657, + 3283, 10659, 10661, 10663, 10665, 10667, 10669, 10671, 10673, 10675, + 10677, 10679, 10681, 3285, 10683, 10685, 10687, 10689, 10691, 10693, + 10695, 10697, 10699, 10701, 10703, 10705, 3281, 10707, 10709, 10711, + 10713, 10715, 10717, 10719, 10721, 10723, 10725, 10727, 10729, 3279, + 10731, 10733, 10735, 10737, 10739, 10741, 10743, 10745, 10747, 10749, + 10751, 10753, 10755, 10757, 10759, 10761, 10655, 10657, 3283, 10659, + 10661, 10663, 10665, 10667, 10669, 10671, 10673, 10675, 10677, 10679, + 10681, 3285, 10683, 10685, 10687, 10689, 10691, 10693, 10695, 10697, + 10699, 10701, 10703, 10705, 3281, 10707, 10709, 10711, 10713, 10715, + 10717, 10719, 10721, 10723, 10725, 10727, 10729, 3279, 10731, 10733, + 10735, 10737, 10739, 10741, 10743, 10745, 10747, 10749, 10751, 10753, + 10755, 10757, 10759, 10761, 10655, 10657, 3283, 10659, 10661, 10663, + 10665, 10667, 10669, 10671, 10673, 10675, 10677, 10679, 10681, 3285, + 10683, 10685, 10687, 10689, 10691, 10693, 10695, 10697, 10699, 10701, + 10703, 10705, 3281, 10707, 10709, 10711, 10713, 10715, 10717, 10719, + 10721, 10723, 10725, 10727, 10729, 3279, 10731, 10733, 10735, 10737, + 10739, 10741, 10743, 10745, 10747, 10749, 10751, 10753, 10755, 10757, + 10759, 10761, 10655, 10657, 3283, 10659, 10661, 10663, 10665, 10667, + 10669, 10671, 10673, 10675, 10677, 10679, 10681, 3285, 10683, 10685, + 10687, 10689, 10691, 10693, 10695, 10697, 10699, 10701, 10703, 10705, + 3281, 10707, 10709, 10711, 10713, 10715, 10717, 10719, 10721, 10723, + 10725, 10727, 10729, 3279, 10731, 10733, 10735, 10737, 10739, 10741, + 10743, 10745, 10747, 10749, 10751, 10753, 10755, 10757, 10759, 10761, + 10763, 10765, 0, 0, 10556, 10558, 10560, 10562, 10564, 10566, 10568, + 10570, 10572, 10574, 10556, 10558, 10560, 10562, 10564, 10566, 10568, + 10570, 10572, 10574, 10556, 10558, 10560, 10562, 10564, 10566, 10568, + 10570, 10572, 10574, 10556, 10558, 10560, 10562, 10564, 10566, 10568, + 10570, 10572, 10574, 10556, 10558, 10560, 10562, 10564, 10566, 10568, + 10570, 10572, 10574, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10767, 10769, 10771, 10773, 10775, 10777, 10779, + 10781, 10783, 10785, 10787, 10789, 10791, 10793, 10795, 10797, 10799, + 10801, 10803, 10805, 10807, 10809, 10811, 10813, 10815, 10817, 10819, + 10821, 10823, 10825, 10827, 10829, 10831, 10833, 10835, 10837, 10839, + 10841, 10843, 10845, 10847, 10849, 10851, 10853, 10855, 10857, 10859, + 10861, 10863, 10865, 10867, 10869, 10871, 10873, 10875, 10877, 10879, + 10881, 10883, 10885, 10887, 10889, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 10891, 10893, 10895, 10897, 0, 10899, 10901, 10903, 10905, + 10907, 10909, 10911, 10913, 10915, 10917, 10919, 10921, 10923, 10925, + 10927, 10929, 10931, 10933, 10935, 10937, 10939, 10941, 10943, 10945, + 10947, 10949, 10951, 0, 10893, 10895, 0, 10953, 0, 0, 10903, 0, 10907, + 10909, 10911, 10913, 10915, 10917, 10919, 10921, 10923, 10925, 0, 10929, + 10931, 10933, 10935, 0, 10939, 0, 10943, 0, 0, 0, 0, 0, 0, 10895, 0, 0, + 0, 0, 10903, 0, 10907, 0, 10911, 0, 10915, 10917, 10919, 0, 10923, 10925, + 0, 10929, 0, 0, 10935, 0, 10939, 0, 10943, 0, 10947, 0, 10951, 0, 10893, + 10895, 0, 10953, 0, 0, 10903, 10905, 10907, 10909, 0, 10913, 10915, + 10917, 10919, 10921, 10923, 10925, 0, 10929, 10931, 10933, 10935, 0, + 10939, 10941, 10943, 10945, 0, 10949, 0, 10891, 10893, 10895, 10897, + 10953, 10899, 10901, 10903, 10905, 10907, 0, 10911, 10913, 10915, 10917, + 10919, 10921, 10923, 10925, 10927, 10929, 10931, 10933, 10935, 10937, + 10939, 10941, 10943, 0, 0, 0, 0, 0, 10893, 10895, 10897, 0, 10899, 10901, + 10903, 10905, 10907, 0, 10911, 10913, 10915, 10917, 10919, 10921, 10923, + 10925, 10927, 10929, 10931, 10933, 10935, 10937, 10939, 10941, 10943, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10955, 10958, + 10961, 10964, 10967, 10970, 10973, 10976, 10979, 10982, 10985, 0, 0, 0, + 0, 0, 10988, 10992, 10996, 11000, 11004, 11008, 11012, 11016, 11020, + 11024, 11028, 11032, 11036, 11040, 11044, 11048, 11052, 11056, 11060, + 11064, 11068, 11072, 11076, 11080, 11084, 11088, 11092, 3926, 3956, + 11096, 11099, 0, 11102, 11104, 11106, 11108, 11110, 11112, 11114, 11116, + 11118, 11120, 11122, 11124, 11126, 11128, 11130, 11132, 11134, 11136, + 11138, 11140, 11142, 11144, 11146, 11148, 11150, 11152, 11154, 6348, + 11157, 11160, 11163, 11167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11170, 11173, 11176, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 11179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11182, + 11185, 11188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11190, 11192, 11194, + 11196, 11198, 11200, 11202, 11204, 11206, 11208, 11210, 11212, 11214, + 11216, 11218, 11220, 11222, 11224, 11226, 11228, 11230, 11232, 11234, + 11236, 11238, 11240, 11242, 11244, 11246, 11248, 11250, 11252, 11254, + 11256, 11258, 11260, 11262, 11264, 11266, 11268, 11270, 11272, 11274, + 11276, 0, 0, 0, 0, 11278, 11282, 11286, 11290, 11294, 11298, 11302, + 11306, 11310, 0, 0, 0, 0, 0, 0, 0, 11314, 11316, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 10556, 10558, 10560, 10562, 10564, 10566, 10568, 10570, + 10572, 10574, 0, 0, 0, 0, 0, 0, 11318, 11320, 11322, 11324, 11326, 7197, + 11328, 11330, 11332, 11334, 7199, 11336, 11338, 11340, 7201, 11342, + 11344, 11346, 11348, 11350, 11352, 11354, 11356, 11358, 11360, 11362, + 11364, 7317, 11366, 11368, 11370, 11372, 11374, 11376, 11378, 11380, + 11382, 7327, 7203, 7205, 7329, 11384, 11386, 6819, 11388, 7207, 11390, + 11392, 11394, 11396, 11396, 11396, 11398, 11400, 11402, 11404, 11406, + 11408, 11410, 11412, 11414, 11416, 11418, 11420, 11422, 11424, 11426, + 11428, 11430, 11432, 11432, 7333, 11434, 11436, 11438, 11440, 7211, + 11442, 11444, 11446, 7125, 11448, 11450, 11452, 11454, 11456, 11458, + 11460, 11462, 11464, 11466, 11468, 11470, 11472, 11474, 11476, 11478, + 11480, 11482, 11484, 11486, 11488, 11490, 11492, 11494, 11496, 11498, + 11498, 11500, 11502, 11504, 6811, 11506, 11508, 11510, 11512, 11514, + 11516, 11518, 11520, 7221, 11522, 11524, 11526, 11528, 11530, 11532, + 11534, 11536, 11538, 11540, 11542, 11544, 11546, 11548, 11550, 11552, + 11554, 11556, 11558, 11560, 11562, 6703, 11564, 11566, 11568, 11568, + 11570, 11572, 11572, 11574, 11576, 11578, 11580, 11582, 11584, 11586, + 11588, 11590, 11592, 11594, 11596, 11598, 7223, 11600, 11602, 11604, + 11606, 7357, 11606, 11608, 7227, 11610, 11612, 11614, 11616, 7229, 6649, + 11618, 11620, 11622, 11624, 11626, 11628, 11630, 11632, 11634, 11636, + 11638, 11640, 11642, 11644, 11646, 11648, 11650, 11652, 11654, 11656, + 11658, 11660, 7231, 11662, 11664, 11666, 11668, 11670, 11672, 7235, + 11674, 11676, 11678, 11680, 11682, 11684, 11686, 11688, 6705, 7373, + 11690, 11692, 11694, 11696, 11698, 11700, 11702, 11704, 7237, 11706, + 11708, 11710, 11712, 7459, 11714, 11716, 11718, 11720, 11722, 11724, + 11726, 11728, 11730, 11732, 11734, 11736, 11738, 6845, 11740, 11742, + 11744, 11746, 11748, 11750, 11752, 11754, 11756, 11758, 11760, 7239, + 7019, 11762, 11764, 11766, 11768, 11770, 11772, 11774, 11776, 7381, + 11778, 11780, 11782, 11784, 11786, 11788, 11790, 11792, 7383, 11794, + 11796, 11798, 11800, 11802, 11804, 11806, 11808, 11810, 11812, 11814, + 11816, 7387, 11818, 11820, 11822, 11824, 11826, 11828, 11830, 11832, + 11834, 11836, 11838, 11838, 11840, 11842, 7391, 11844, 11846, 11848, + 11850, 11852, 11854, 11856, 6817, 11858, 11860, 11862, 11864, 11866, + 11868, 11870, 7403, 11872, 11874, 11876, 11878, 11880, 11882, 11882, + 7405, 7463, 11884, 11886, 11888, 11890, 11892, 6741, 7409, 11894, 11896, + 7261, 11898, 11900, 7169, 11902, 11904, 7269, 11906, 11908, 11910, 11912, + 11912, 11914, 11916, 11918, 11920, 11922, 11924, 11926, 11928, 11930, + 11932, 11934, 11936, 11938, 11940, 11942, 11944, 11946, 11948, 11950, + 11952, 11954, 11956, 11958, 11960, 11962, 11964, 11966, 7281, 11968, + 11970, 11972, 11974, 11976, 11978, 11980, 11982, 11984, 11986, 11988, + 11990, 11992, 11994, 11996, 11998, 11570, 12000, 12002, 12004, 12006, + 12008, 12010, 12012, 12014, 12016, 12018, 12020, 12022, 6853, 12024, + 12026, 12028, 12030, 12032, 12034, 7287, 12036, 12038, 12040, 12042, + 12044, 12046, 12048, 12050, 12052, 12054, 12056, 12058, 12060, 12062, + 12064, 12066, 12068, 12070, 12072, 12074, 6731, 12076, 12078, 12080, + 12082, 12084, 12086, 7423, 12088, 12090, 12092, 12094, 12096, 12098, + 12100, 12102, 12104, 12106, 12108, 12110, 12112, 12114, 12116, 12118, + 12120, 12122, 12124, 12126, 7433, 7435, 12128, 12130, 12132, 12134, + 12136, 12138, 12140, 12142, 12144, 12146, 12148, 12150, 12152, 7437, + 12154, 12156, 12158, 12160, 12162, 12164, 12166, 12168, 12170, 12172, + 12174, 12176, 12178, 12180, 12182, 12184, 12186, 12188, 12190, 12192, + 12194, 12196, 12198, 12200, 12202, 12204, 12206, 12208, 12210, 12212, + 7449, 7449, 12214, 12216, 12218, 12220, 12222, 12224, 12226, 12228, + 12230, 12232, 7451, 12234, 12236, 12238, 12240, 12242, 12244, 12246, + 12248, 12250, 12252, 12254, 12256, 12258, 12260, 12262, 12264, 12266, + 12268, 12270, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6196,7 +7209,6 @@ static const change_record change_records_3_2_0[] = { { 9, 255, 255, 255, 255, 0 }, { 255, 255, 255, 1, 255, 0 }, { 255, 20, 255, 255, 255, 0 }, - { 255, 255, 255, 255, 255, 1000000000000.0 }, { 255, 255, 255, 255, 255, 1e+20 }, { 255, 19, 255, 255, 255, -1 }, { 1, 255, 255, 255, 255, 0 }, @@ -6206,42 +7218,43 @@ static const unsigned char changes_3_2_0_index[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 1, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 1, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 1, 1, 1, 50, 1, 1, 51, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 52, 53, 54, 55, 56, 1, - 57, 1, 1, 1, 58, 1, 1, 1, 1, 1, 1, 59, 1, 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 61, 1, 55, 1, 1, 1, 1, 1, 1, 62, 1, 1, 63, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 64, 1, 1, 1, 1, 1, 65, 1, 66, 1, 67, 1, - 1, 1, 1, 1, 1, 1, 1, 68, 69, 1, 1, 1, 70, 28, 71, 72, 73, 74, 75, 76, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 52, 53, 54, 55, 1, 1, + 56, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 58, 1, 1, 1, 59, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 60, 1, 55, 1, 1, 1, 1, 1, 1, 61, 1, 1, 62, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 63, 1, 1, 1, 1, 1, 64, 1, 65, 1, 66, 1, + 1, 1, 1, 1, 1, 1, 1, 67, 68, 1, 1, 1, 69, 28, 70, 71, 72, 73, 74, 75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 77, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 76, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 78, 79, 80, 1, 81, 82, 83, 84, 85, 86, 87, 88, 89, 28, 90, - 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, - 107, 108, 109, 110, 111, 112, 113, 114, 28, 28, 28, 115, 116, 117, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 118, 28, 28, 28, 28, 119, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 120, 28, 28, 121, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 122, 1, 1, 1, 1, 1, - 1, 28, 28, 123, 124, 1, 125, 126, 127, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 128, 28, 28, - 28, 28, 129, 130, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 131, 28, 132, 133, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 134, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 135, - 28, 136, 137, 1, 138, 139, 140, 141, 1, 142, 143, 28, 28, 144, 1, 1, 1, - 1, 145, 146, 147, 148, 1, 149, 150, 1, 151, 152, 153, 1, 1, 154, 155, - 156, 1, 157, 158, 159, 28, 28, 28, 160, 161, 162, 28, 163, 164, 1, 1, 1, - 1, 165, 67, 1, 1, 1, 1, 1, 1, 1, 166, 167, 168, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 169, 1, 1, 1, 1, 1, 170, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 171, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 77, 78, 79, 1, 80, 81, 82, 83, 84, 85, 86, 87, 88, 28, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 28, 28, 28, 114, 115, 116, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 117, 28, 28, 28, 28, 118, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 119, 28, 28, 120, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 121, 1, 1, 1, 1, 1, + 1, 28, 28, 122, 123, 1, 124, 125, 126, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 127, 128, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 129, 28, 130, 131, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 132, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 133, + 28, 134, 135, 1, 136, 137, 138, 139, 1, 140, 141, 28, 28, 142, 1, 1, 1, + 1, 143, 144, 145, 146, 1, 147, 148, 149, 150, 151, 152, 1, 1, 153, 154, + 155, 1, 156, 157, 158, 28, 28, 28, 159, 160, 161, 28, 162, 163, 1, 1, 1, + 1, 164, 66, 1, 1, 1, 1, 1, 1, 1, 165, 166, 167, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 168, 1, 1, 1, 1, 1, 169, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 170, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 172, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 171, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 173, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 174, 175, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 176, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 177, - 28, 28, 43, 1, 1, 1, 1, 1, 1, 1, 1, 1, 169, 1, 1, 1, 1, 1, 1, 1, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 178, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 179, 1, 1, 1, + 1, 1, 1, 172, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 173, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 174, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 175, + 28, 28, 43, 1, 1, 1, 1, 1, 1, 1, 1, 1, 168, 1, 1, 1, 1, 1, 1, 1, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 176, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 177, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -6357,7 +7370,7 @@ static const unsigned char changes_3_2_0_index[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 180, 1, 1, 1, 1, 1, + 178, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -6388,8 +7401,7 @@ static const unsigned char changes_3_2_0_index[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; static const unsigned char changes_3_2_0_data[] = { @@ -6420,7 +7432,7 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6484,7 +7496,7 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -6525,12 +7537,12 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, - 0, 0, 9, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 9, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 20, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -6670,8 +7682,8 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -6723,7 +7735,7 @@ static const unsigned char changes_3_2_0_data[] = { 41, 42, 43, 44, 1, 1, 0, 0, 0, 4, 38, 8, 6, 7, 39, 40, 41, 42, 43, 44, 1, 1, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, @@ -6832,7 +7844,7 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -6999,23 +8011,13 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, + 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, + 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7023,8 +8025,8 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7033,13 +8035,13 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7047,11 +8049,11 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7066,8 +8068,8 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7080,8 +8082,8 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7089,11 +8091,11 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, + 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7109,13 +8111,13 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7131,12 +8133,11 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7154,12 +8155,12 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -7167,7 +8168,7 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -7176,51 +8177,51 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 9, 9, 0, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, - 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 9, + 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, + 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7228,209 +8229,209 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 0, 0, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 50, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 49, 50, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 21, 21, 21, 21, 21, 21, 0, 0, 0, 1, 1, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21, 21, 21, 0, 0, 0, 1, 1, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, - 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, - 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, + 9, 9, 9, 9, 0, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 9, 0, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 0, 0, 9, 0, - 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 9, 9, 0, 0, 0, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, + 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, - 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 0, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 0, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, + 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 0, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7438,102 +8439,102 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 0, 0, 9, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, + 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, + 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 0, 9, 9, 9, 9, - 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 0, 9, 0, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 0, 9, - 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, - 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 0, 0, 9, 9, 9, - 0, 0, 9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 0, - 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 0, 0, 9, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 0, 0, 9, - 0, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 0, 0, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, + 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, + 0, 9, 9, 0, 0, 9, 9, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 9, + 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 0, 0, + 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 0, 9, 0, 0, 9, 0, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7541,66 +8542,69 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, - 9, 0, 0, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, + 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 0, 9, 9, - 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, + 9, 0, 0, 0, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, + 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7611,46 +8615,43 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7664,17 +8665,16 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -7690,10 +8690,11 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7701,9 +8702,9 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7711,34 +8712,34 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7746,36 +8747,26 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -7784,20 +8775,19 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7807,12 +8797,14 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 0, 9, + 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 0, 0, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, - 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -7827,20 +8819,18 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, + 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -7850,55 +8840,55 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7911,8 +8901,8 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7921,35 +8911,34 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0, + 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 58, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, + 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, + 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7957,21 +8946,22 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, + 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, - 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7984,12 +8974,12 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, - 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -7997,9 +8987,10 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -8008,9 +8999,9 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, + 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -8018,10 +9009,20 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 0, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 0, 9, 9, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -8029,14 +9030,14 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -8047,15 +9048,15 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -8063,50 +9064,50 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 0, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 0, 9, 9, 0, 9, 0, 0, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, - 9, 9, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 9, 0, 9, 0, 9, 0, 9, - 9, 9, 0, 9, 9, 0, 9, 0, 0, 9, 0, 9, 0, 9, 0, 9, 0, 9, 0, 9, 9, 0, 9, 0, - 0, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 0, 9, 9, 9, 9, 0, - 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 0, 9, 9, 9, 9, 9, 0, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 0, 0, 9, 0, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 9, 0, 9, 0, 9, 0, 9, 9, 9, 0, 9, 9, 0, 9, 0, 0, 9, 0, 9, 0, 9, 0, 9, + 0, 9, 0, 9, 9, 0, 9, 0, 0, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, + 9, 9, 0, 9, 9, 9, 9, 0, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 0, 9, + 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -8115,68 +9116,69 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, - 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, - 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 0, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, + 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, + 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, - 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -8190,10 +9192,9 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -8208,7 +9209,7 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -8219,13 +9220,13 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -8238,8 +9239,8 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -8254,23 +9255,10 @@ static const unsigned char changes_3_2_0_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -8283,12 +9271,11 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -8297,14 +9284,15 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -8316,10 +9304,13 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -8329,8 +9320,8 @@ static const unsigned char changes_3_2_0_data[] = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, }; static const change_record* get_change_3_2_0(Py_UCS4 n) diff --git a/Modules/unicodename_db.h b/Modules/unicodename_db.h index 0697e259b39019..5ac5ca462bdfab 100644 --- a/Modules/unicodename_db.h +++ b/Modules/unicodename_db.h @@ -4,3508 +4,3553 @@ /* name->code dictionary */ static const unsigned char packed_name_dawg[] = { - 238, 255, 4, 254, 2, 65, 218, 130, 3, 66, 130, 154, 3, 67, 174, 245, 4, - 68, 206, 135, 1, 69, 186, 157, 1, 70, 190, 54, 71, 210, 164, 1, 72, 182, - 225, 1, 73, 222, 65, 74, 138, 23, 75, 238, 138, 1, 76, 130, 158, 3, 77, - 250, 200, 3, 78, 242, 107, 79, 138, 131, 1, 80, 128, 159, 1, 2, 81, 85, - 226, 7, 82, 194, 180, 1, 83, 214, 193, 4, 84, 134, 225, 2, 85, 242, 104, - 86, 138, 84, 87, 170, 114, 88, 254, 4, 89, 187, 48, 90, 214, 40, 222, 2, + 160, 171, 4, 254, 2, 65, 246, 145, 3, 66, 206, 160, 3, 67, 146, 247, 4, + 68, 250, 135, 1, 69, 178, 153, 1, 70, 174, 55, 71, 214, 169, 1, 72, 138, + 226, 1, 73, 138, 66, 74, 142, 23, 75, 174, 135, 1, 76, 150, 164, 3, 77, + 198, 201, 3, 78, 170, 103, 79, 174, 131, 1, 80, 212, 159, 1, 2, 81, 85, + 226, 7, 82, 158, 180, 1, 83, 210, 197, 4, 84, 146, 236, 2, 85, 246, 104, + 86, 242, 84, 87, 246, 114, 88, 130, 5, 89, 211, 50, 90, 176, 41, 218, 2, 67, 238, 1, 68, 182, 10, 69, 172, 5, 4, 72, 79, 77, 32, 244, 6, 7, 73, - 82, 80, 76, 65, 78, 69, 82, 76, 146, 36, 77, 198, 1, 78, 154, 26, 80, - 138, 20, 82, 226, 136, 2, 83, 210, 2, 84, 70, 85, 214, 1, 86, 144, 254, - 11, 4, 70, 71, 72, 65, 186, 231, 9, 66, 180, 227, 2, 9, 75, 84, 73, 69, - 83, 69, 76, 83, 75, 196, 198, 2, 2, 81, 85, 199, 196, 10, 88, 18, 132, 1, - 2, 67, 79, 46, 75, 20, 5, 85, 84, 69, 32, 65, 164, 153, 17, 6, 84, 73, - 86, 65, 84, 69, 145, 135, 23, 5, 32, 67, 85, 82, 82, 4, 170, 142, 26, 85, - 173, 133, 14, 2, 82, 68, 5, 151, 168, 34, 78, 4, 154, 199, 35, 67, 143, - 248, 3, 78, 188, 1, 232, 1, 4, 76, 65, 77, 32, 242, 7, 77, 212, 179, 13, - 7, 72, 69, 83, 73, 86, 69, 32, 144, 165, 4, 19, 68, 82, 69, 83, 83, 69, - 68, 32, 84, 79, 32, 84, 72, 69, 32, 83, 85, 66, 74, 146, 233, 13, 85, - 237, 185, 1, 6, 73, 32, 83, 72, 65, 75, 176, 1, 218, 1, 67, 44, 4, 83, + 82, 80, 76, 65, 78, 69, 82, 76, 190, 36, 77, 242, 1, 78, 162, 26, 80, + 162, 20, 82, 186, 150, 2, 83, 162, 3, 84, 70, 85, 214, 1, 86, 252, 197, + 1, 8, 75, 84, 73, 69, 83, 69, 76, 83, 244, 194, 10, 4, 70, 71, 72, 65, + 202, 235, 9, 66, 248, 165, 5, 2, 81, 85, 171, 215, 10, 88, 18, 132, 1, 2, + 67, 79, 46, 75, 20, 5, 85, 84, 69, 32, 65, 236, 179, 17, 6, 84, 73, 86, + 65, 84, 69, 189, 153, 23, 5, 32, 67, 85, 82, 82, 4, 182, 172, 26, 85, + 149, 148, 14, 2, 82, 68, 5, 255, 198, 34, 78, 4, 194, 235, 35, 67, 199, + 254, 3, 78, 188, 1, 232, 1, 4, 76, 65, 77, 32, 242, 7, 77, 200, 199, 13, + 7, 72, 69, 83, 73, 86, 69, 32, 136, 172, 4, 19, 68, 82, 69, 83, 83, 69, + 68, 32, 84, 79, 32, 84, 72, 69, 32, 83, 85, 66, 74, 150, 237, 13, 85, + 233, 185, 1, 6, 73, 32, 83, 72, 65, 75, 176, 1, 218, 1, 67, 44, 4, 83, 77, 65, 76, 168, 4, 7, 71, 69, 77, 73, 78, 65, 84, 116, 8, 73, 78, 73, - 84, 73, 65, 76, 32, 38, 78, 198, 213, 2, 72, 200, 197, 23, 4, 65, 76, 73, - 70, 0, 5, 86, 79, 87, 69, 76, 211, 205, 12, 68, 70, 40, 5, 65, 80, 73, + 84, 73, 65, 76, 32, 38, 78, 146, 228, 2, 72, 192, 208, 23, 4, 65, 76, 73, + 70, 0, 5, 86, 79, 87, 69, 76, 155, 222, 12, 68, 70, 40, 5, 65, 80, 73, 84, 65, 215, 4, 79, 68, 45, 9, 76, 32, 76, 69, 84, 84, 69, 82, 32, 68, - 142, 2, 68, 38, 71, 34, 74, 2, 77, 22, 75, 46, 78, 42, 83, 166, 133, 30, - 81, 238, 223, 4, 76, 194, 39, 67, 194, 197, 4, 90, 212, 7, 2, 65, 76, + 142, 2, 68, 38, 71, 34, 74, 2, 77, 22, 75, 46, 78, 42, 83, 182, 159, 30, + 81, 142, 228, 4, 76, 142, 45, 67, 158, 204, 4, 90, 240, 9, 2, 65, 76, 170, 2, 66, 2, 89, 154, 1, 87, 198, 89, 84, 150, 14, 80, 158, 20, 70, 2, - 72, 2, 82, 2, 86, 186, 2, 69, 2, 73, 2, 79, 3, 85, 4, 170, 218, 5, 65, - 155, 128, 35, 72, 4, 130, 198, 40, 66, 215, 22, 65, 2, 231, 217, 39, 73, - 6, 254, 218, 39, 65, 178, 98, 80, 191, 28, 72, 6, 150, 140, 40, 85, 170, - 77, 72, 3, 89, 4, 212, 185, 2, 6, 73, 78, 78, 89, 73, 73, 195, 159, 38, - 72, 4, 40, 4, 69, 32, 67, 79, 219, 241, 39, 73, 2, 233, 209, 39, 13, 78, - 83, 79, 78, 65, 78, 84, 32, 77, 79, 68, 73, 70, 4, 214, 204, 33, 69, 183, - 188, 4, 81, 4, 174, 236, 25, 65, 251, 217, 10, 85, 4, 238, 178, 27, 69, - 189, 146, 12, 12, 73, 83, 83, 73, 79, 78, 32, 84, 73, 67, 75, 69, 116, - 80, 5, 71, 69, 65, 78, 32, 241, 135, 34, 9, 82, 73, 65, 76, 32, 84, 82, + 72, 2, 82, 2, 86, 186, 2, 69, 2, 73, 2, 79, 3, 85, 4, 150, 240, 5, 65, + 163, 151, 35, 72, 4, 246, 242, 40, 66, 215, 22, 65, 2, 219, 134, 40, 73, + 6, 242, 135, 40, 65, 178, 98, 80, 191, 28, 72, 6, 138, 185, 40, 85, 170, + 77, 72, 3, 89, 4, 164, 200, 2, 6, 73, 78, 78, 89, 73, 73, 231, 189, 38, + 72, 4, 40, 4, 69, 32, 67, 79, 207, 158, 40, 73, 2, 197, 252, 39, 13, 78, + 83, 79, 78, 65, 78, 84, 32, 77, 79, 68, 73, 70, 4, 186, 235, 33, 69, 247, + 198, 4, 81, 4, 186, 138, 26, 65, 195, 223, 10, 85, 4, 154, 204, 27, 69, + 245, 177, 12, 12, 73, 83, 83, 73, 79, 78, 32, 84, 73, 67, 75, 69, 116, + 80, 5, 71, 69, 65, 78, 32, 213, 166, 34, 9, 82, 73, 65, 76, 32, 84, 82, 65, 77, 114, 136, 1, 3, 68, 82, 89, 0, 6, 76, 73, 81, 85, 73, 68, 60, 8, - 77, 69, 65, 83, 85, 82, 69, 32, 26, 87, 230, 147, 27, 78, 183, 255, 11, + 77, 69, 65, 83, 85, 82, 69, 32, 26, 87, 198, 173, 27, 78, 183, 144, 12, 67, 2, 153, 2, 11, 32, 77, 69, 65, 83, 85, 82, 69, 32, 70, 73, 4, 246, 1, 83, 31, 84, 14, 100, 6, 69, 73, 71, 72, 84, 32, 241, 1, 14, 79, 82, 68, 32, 83, 69, 80, 65, 82, 65, 84, 79, 82, 32, 10, 54, 70, 66, 83, 30, 84, 65, 5, 66, 65, 83, 69, 32, 4, 38, 73, 89, 5, 79, 85, 82, 84, 72, 2, 85, 3, 82, 83, 84, 2, 49, 4, 69, 67, 79, 78, 2, 21, 3, 72, 73, 82, 2, 11, 68, - 2, 25, 4, 32, 83, 85, 66, 2, 217, 183, 40, 2, 85, 78, 4, 190, 186, 39, - 76, 131, 73, 68, 130, 1, 140, 2, 22, 67, 79, 78, 83, 79, 78, 65, 78, 84, + 2, 25, 4, 32, 83, 85, 66, 2, 205, 228, 40, 2, 85, 78, 4, 174, 229, 39, + 76, 135, 75, 68, 130, 1, 140, 2, 22, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 77, 69, 68, 73, 65, 76, 32, 88, 7, 76, 69, 84, 84, 69, 82, 32, 242, 1, 83, 168, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, - 71, 78, 32, 140, 212, 32, 8, 78, 85, 77, 66, 69, 82, 32, 84, 171, 136, 6, - 68, 6, 26, 76, 243, 206, 40, 82, 4, 204, 134, 40, 7, 73, 71, 65, 84, 73, - 78, 71, 219, 74, 65, 68, 154, 1, 65, 214, 245, 36, 68, 114, 84, 218, 207, - 1, 76, 250, 192, 1, 78, 126, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, + 71, 78, 32, 248, 242, 32, 8, 78, 85, 77, 66, 69, 82, 32, 84, 203, 147, 6, + 68, 6, 26, 76, 231, 251, 40, 82, 4, 192, 179, 40, 7, 73, 71, 65, 84, 73, + 78, 71, 219, 74, 65, 68, 154, 1, 65, 246, 158, 37, 68, 114, 84, 198, 208, + 1, 76, 226, 195, 1, 78, 126, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 77, 2, 82, 3, 83, 9, 45, 9, 76, 84, 69, 82, 78, 65, 84, 69, - 32, 6, 230, 204, 40, 66, 2, 71, 3, 84, 10, 60, 4, 73, 71, 78, 32, 153, - 149, 23, 5, 89, 77, 66, 79, 76, 8, 50, 83, 170, 215, 25, 75, 229, 139, - 10, 2, 82, 85, 4, 180, 207, 37, 4, 77, 65, 76, 76, 247, 173, 2, 69, 22, - 66, 65, 234, 249, 36, 85, 206, 201, 1, 73, 222, 137, 2, 69, 3, 79, 11, - 142, 205, 40, 65, 2, 73, 2, 77, 3, 87, 7, 11, 32, 4, 192, 182, 35, 3, 68, - 69, 80, 145, 197, 4, 5, 65, 82, 82, 73, 86, 150, 2, 216, 1, 20, 67, 72, - 69, 77, 73, 67, 65, 76, 32, 83, 89, 77, 66, 79, 76, 32, 70, 79, 82, 32, - 166, 28, 69, 60, 4, 73, 69, 78, 32, 208, 3, 2, 76, 32, 82, 77, 108, 6, - 84, 69, 82, 78, 65, 84, 141, 142, 36, 3, 65, 82, 77, 232, 1, 158, 2, 65, - 246, 2, 66, 210, 1, 67, 138, 3, 68, 98, 71, 38, 72, 132, 1, 4, 73, 82, - 79, 78, 82, 76, 74, 77, 144, 1, 2, 78, 73, 34, 80, 176, 2, 3, 81, 85, 73, - 74, 82, 146, 2, 83, 210, 5, 84, 166, 1, 86, 200, 1, 2, 87, 65, 170, 238, - 34, 85, 154, 215, 1, 69, 158, 181, 1, 79, 139, 31, 70, 30, 194, 1, 76, - 72, 3, 81, 85, 65, 204, 8, 7, 78, 84, 73, 77, 79, 78, 89, 236, 142, 3, 3, - 77, 65, 76, 128, 174, 21, 3, 82, 83, 69, 248, 229, 13, 5, 85, 82, 73, 80, - 73, 210, 134, 1, 83, 171, 91, 73, 8, 224, 24, 2, 69, 77, 212, 171, 26, 4, - 75, 65, 76, 73, 191, 241, 13, 85, 10, 38, 32, 129, 223, 24, 3, 70, 79, - 82, 8, 216, 7, 4, 86, 73, 84, 65, 145, 150, 23, 4, 82, 69, 71, 73, 16, - 148, 1, 7, 65, 84, 72, 32, 79, 70, 32, 204, 6, 6, 73, 83, 77, 85, 84, 72, - 144, 1, 4, 79, 82, 65, 88, 164, 1, 4, 76, 65, 67, 75, 139, 139, 39, 82, - 4, 138, 234, 16, 77, 165, 203, 22, 5, 86, 65, 80, 79, 85, 28, 82, 65, 92, - 6, 79, 80, 80, 69, 82, 32, 34, 82, 185, 160, 39, 4, 73, 78, 78, 65, 6, - 152, 250, 5, 2, 68, 85, 204, 159, 33, 9, 80, 85, 84, 32, 77, 79, 82, 84, - 85, 239, 28, 76, 4, 230, 13, 65, 255, 147, 39, 79, 16, 72, 8, 79, 67, 85, - 83, 32, 79, 70, 32, 53, 6, 85, 67, 73, 66, 76, 69, 6, 228, 16, 5, 67, 79, - 80, 80, 69, 171, 139, 39, 73, 11, 11, 45, 8, 238, 192, 40, 50, 2, 51, 2, - 52, 3, 53, 8, 44, 2, 73, 83, 129, 198, 37, 3, 65, 89, 45, 6, 144, 2, 4, - 83, 79, 76, 86, 255, 197, 37, 84, 4, 218, 161, 30, 79, 179, 141, 10, 85, - 8, 32, 4, 65, 76, 70, 32, 47, 79, 4, 200, 183, 35, 2, 79, 85, 203, 204, - 3, 68, 4, 212, 250, 2, 4, 82, 83, 69, 32, 191, 139, 37, 85, 6, 56, 3, 32, - 79, 82, 73, 7, 45, 67, 79, 80, 80, 69, 82, 4, 235, 187, 26, 69, 4, 48, 3, - 69, 65, 68, 229, 204, 13, 3, 79, 68, 69, 2, 223, 216, 35, 32, 10, 120, - 16, 69, 82, 67, 85, 82, 89, 32, 83, 85, 66, 76, 73, 77, 65, 84, 69, 196, - 183, 16, 4, 65, 82, 67, 65, 203, 130, 22, 79, 7, 179, 187, 40, 45, 4, - 218, 155, 39, 84, 131, 118, 71, 14, 108, 11, 72, 73, 76, 79, 83, 79, 80, - 72, 69, 82, 83, 34, 79, 98, 85, 173, 134, 30, 6, 82, 69, 67, 73, 80, 73, - 2, 209, 9, 4, 32, 83, 85, 76, 6, 52, 4, 87, 68, 69, 82, 205, 136, 36, 3, - 84, 32, 65, 5, 173, 138, 39, 5, 69, 68, 32, 66, 82, 4, 200, 136, 34, 4, - 84, 82, 69, 70, 241, 159, 6, 3, 82, 73, 70, 4, 220, 176, 35, 5, 78, 84, - 69, 83, 83, 237, 240, 3, 4, 67, 75, 32, 76, 24, 58, 69, 173, 182, 26, 8, - 79, 67, 75, 32, 83, 65, 76, 84, 20, 68, 5, 71, 85, 76, 85, 83, 164, 7, 3, - 65, 76, 71, 195, 129, 26, 84, 15, 32, 4, 32, 79, 70, 32, 71, 45, 6, 188, - 181, 26, 8, 65, 78, 84, 73, 77, 79, 78, 89, 247, 220, 12, 73, 6, 142, - 183, 40, 50, 2, 51, 3, 52, 34, 164, 1, 2, 65, 76, 194, 1, 84, 142, 1, 85, - 192, 191, 17, 2, 80, 73, 164, 178, 12, 2, 73, 76, 170, 176, 9, 79, 141, - 20, 11, 67, 69, 80, 84, 69, 82, 32, 79, 70, 32, 74, 8, 58, 84, 141, 141, - 39, 8, 45, 65, 77, 77, 79, 78, 73, 65, 7, 25, 4, 32, 79, 70, 32, 4, 52, - 8, 67, 79, 80, 80, 69, 82, 32, 65, 231, 5, 65, 2, 229, 128, 30, 7, 78, - 84, 73, 77, 79, 78, 73, 6, 236, 3, 8, 65, 82, 82, 69, 68, 32, 84, 82, - 129, 174, 26, 19, 82, 65, 84, 85, 77, 32, 83, 85, 80, 69, 82, 32, 83, 84, - 82, 65, 84, 85, 77, 12, 44, 6, 66, 76, 73, 77, 65, 84, 155, 1, 76, 10, - 44, 5, 69, 32, 79, 70, 32, 175, 226, 39, 73, 8, 68, 8, 83, 65, 76, 84, - 32, 79, 70, 32, 130, 3, 65, 247, 172, 27, 67, 4, 254, 2, 65, 247, 172, - 27, 67, 2, 219, 184, 39, 70, 12, 68, 3, 65, 82, 84, 32, 2, 73, 78, 34, - 82, 157, 195, 38, 2, 85, 84, 4, 11, 65, 4, 179, 174, 26, 82, 4, 182, 154, - 35, 67, 167, 49, 32, 2, 233, 236, 39, 2, 73, 68, 14, 50, 73, 145, 140, - 38, 6, 69, 82, 68, 73, 71, 82, 12, 64, 5, 78, 69, 71, 65, 82, 237, 172, - 26, 5, 84, 82, 73, 79, 76, 9, 44, 5, 32, 79, 70, 32, 65, 223, 173, 40, - 45, 2, 181, 144, 24, 3, 78, 84, 73, 4, 194, 240, 39, 84, 239, 61, 88, 6, - 38, 77, 194, 154, 39, 70, 255, 118, 82, 2, 223, 133, 39, 66, 22, 104, 7, - 77, 79, 78, 83, 84, 69, 82, 138, 1, 83, 241, 204, 35, 10, 67, 82, 65, 66, - 32, 83, 84, 69, 80, 80, 11, 11, 32, 8, 88, 6, 67, 76, 79, 83, 69, 68, 0, - 4, 79, 80, 69, 78, 237, 170, 36, 4, 83, 84, 69, 80, 2, 205, 208, 37, 3, - 32, 74, 65, 8, 60, 6, 80, 73, 68, 69, 82, 32, 57, 5, 81, 85, 73, 68, 32, - 4, 140, 153, 29, 5, 67, 82, 79, 85, 67, 215, 245, 1, 83, 4, 56, 6, 67, - 76, 79, 83, 69, 68, 1, 4, 79, 80, 69, 78, 2, 217, 207, 27, 5, 32, 84, 69, - 78, 84, 4, 190, 214, 33, 69, 209, 174, 4, 11, 65, 82, 79, 85, 78, 68, 45, - 80, 82, 79, 70, 9, 49, 10, 79, 83, 84, 32, 69, 81, 85, 65, 76, 32, 6, 32, - 2, 84, 79, 251, 151, 32, 79, 5, 227, 232, 6, 32, 4, 232, 219, 10, 3, 73, - 86, 69, 253, 163, 28, 5, 69, 32, 79, 78, 69, 10, 162, 1, 80, 200, 165, - 29, 6, 69, 82, 73, 67, 65, 78, 188, 249, 5, 3, 66, 85, 76, 197, 248, 3, - 16, 65, 76, 71, 65, 77, 65, 84, 73, 79, 78, 32, 79, 82, 32, 67, 79, 4, - 210, 215, 36, 69, 139, 94, 72, 194, 9, 92, 3, 65, 84, 79, 174, 20, 71, - 174, 1, 84, 146, 248, 24, 67, 222, 139, 12, 68, 139, 139, 3, 75, 144, 9, - 104, 17, 76, 73, 65, 78, 32, 72, 73, 69, 82, 79, 71, 76, 89, 80, 72, 32, - 65, 141, 148, 13, 3, 77, 73, 67, 142, 9, 70, 48, 138, 4, 49, 198, 2, 50, - 162, 3, 51, 174, 5, 52, 163, 3, 53, 222, 1, 106, 50, 102, 52, 110, 54, - 102, 57, 182, 7, 51, 210, 219, 11, 49, 206, 134, 23, 48, 242, 1, 53, 2, - 55, 3, 56, 22, 170, 183, 34, 54, 134, 236, 5, 48, 2, 49, 2, 50, 2, 51, 2, - 52, 2, 53, 2, 55, 2, 56, 3, 57, 28, 158, 164, 12, 54, 170, 146, 22, 49, - 2, 53, 134, 236, 5, 48, 2, 50, 2, 51, 2, 52, 2, 55, 2, 56, 3, 57, 26, - 246, 138, 12, 54, 234, 150, 28, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, - 55, 2, 56, 3, 57, 24, 246, 180, 34, 55, 2, 56, 134, 236, 5, 48, 2, 49, 2, - 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 57, 232, 1, 98, 48, 114, 49, 162, 143, - 12, 50, 2, 51, 230, 217, 22, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 42, - 198, 136, 12, 52, 2, 55, 190, 24, 53, 170, 146, 22, 48, 2, 49, 2, 50, - 134, 236, 5, 51, 2, 54, 2, 56, 3, 57, 26, 146, 160, 12, 48, 170, 146, 22, - 53, 134, 236, 5, 49, 2, 50, 2, 51, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, - 222, 1, 102, 48, 110, 49, 102, 57, 210, 1, 56, 206, 253, 11, 50, 2, 54, - 194, 230, 22, 51, 2, 52, 2, 53, 3, 55, 28, 186, 158, 12, 50, 170, 146, - 22, 55, 2, 57, 134, 236, 5, 48, 2, 49, 2, 51, 2, 52, 2, 53, 2, 54, 3, 56, - 24, 246, 175, 34, 53, 2, 54, 134, 236, 5, 48, 2, 49, 2, 50, 2, 51, 2, 52, - 2, 55, 2, 56, 3, 57, 24, 146, 175, 34, 52, 2, 57, 134, 236, 5, 48, 2, 49, - 2, 50, 2, 51, 2, 53, 2, 54, 2, 55, 3, 56, 228, 1, 102, 48, 2, 50, 2, 53, - 102, 51, 110, 54, 102, 56, 162, 1, 57, 130, 230, 11, 55, 186, 250, 22, - 49, 3, 52, 22, 194, 173, 34, 57, 134, 236, 5, 48, 2, 49, 2, 50, 2, 51, 2, - 52, 2, 53, 2, 54, 2, 55, 3, 56, 30, 250, 129, 12, 54, 150, 196, 8, 50, - 214, 210, 19, 48, 2, 49, 2, 51, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 24, - 242, 171, 34, 52, 2, 56, 134, 236, 5, 48, 2, 49, 2, 50, 2, 51, 2, 53, 2, - 54, 2, 55, 3, 57, 26, 98, 51, 174, 170, 34, 49, 2, 54, 134, 236, 5, 48, - 2, 50, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 4, 216, 246, 20, 6, 32, 82, 65, - 32, 79, 82, 215, 159, 19, 65, 20, 160, 235, 39, 3, 51, 32, 69, 210, 42, - 48, 2, 49, 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 202, 1, 102, - 49, 210, 1, 53, 202, 205, 20, 57, 202, 143, 14, 48, 2, 50, 2, 51, 2, 52, - 2, 54, 2, 55, 3, 56, 22, 90, 48, 194, 147, 40, 49, 2, 50, 2, 51, 2, 52, - 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 4, 60, 6, 32, 66, 69, 71, 73, 78, 1, - 5, 65, 32, 69, 78, 68, 2, 213, 167, 16, 8, 32, 76, 79, 71, 79, 71, 82, - 65, 24, 198, 166, 34, 48, 2, 55, 134, 236, 5, 49, 2, 50, 2, 51, 2, 52, 2, - 53, 2, 54, 2, 56, 3, 57, 60, 150, 249, 32, 51, 166, 225, 1, 48, 242, 1, - 49, 3, 50, 14, 96, 2, 76, 69, 198, 162, 2, 85, 136, 162, 32, 2, 83, 84, - 242, 232, 3, 69, 241, 165, 1, 2, 82, 89, 7, 33, 6, 32, 87, 73, 84, 72, - 32, 4, 182, 236, 31, 83, 231, 184, 4, 85, 31, 76, 4, 69, 78, 78, 65, 41, - 11, 73, 67, 76, 79, 67, 75, 87, 73, 83, 69, 32, 5, 241, 230, 36, 5, 32, - 87, 73, 84, 72, 24, 90, 84, 222, 248, 6, 67, 58, 68, 122, 71, 138, 4, 79, - 153, 134, 28, 5, 73, 78, 84, 69, 71, 12, 84, 15, 82, 73, 65, 78, 71, 76, - 69, 45, 72, 69, 65, 68, 69, 68, 32, 155, 253, 6, 79, 10, 112, 3, 76, 69, - 70, 0, 4, 82, 73, 71, 72, 12, 6, 66, 79, 84, 84, 79, 77, 0, 3, 84, 79, - 80, 191, 252, 6, 79, 2, 11, 84, 2, 193, 178, 37, 7, 32, 85, 45, 83, 72, - 65, 80, 158, 1, 128, 1, 20, 76, 32, 70, 85, 78, 67, 84, 73, 79, 78, 65, - 76, 32, 83, 89, 77, 66, 79, 76, 32, 206, 15, 79, 38, 80, 179, 251, 39, - 67, 140, 1, 222, 2, 67, 186, 1, 68, 190, 2, 73, 44, 4, 65, 76, 80, 72, 0, - 4, 79, 77, 69, 71, 32, 4, 74, 79, 84, 32, 36, 4, 76, 69, 70, 84, 68, 2, - 81, 85, 218, 3, 82, 54, 83, 92, 4, 69, 80, 83, 73, 32, 6, 66, 65, 67, 75, - 83, 76, 76, 2, 85, 80, 156, 136, 16, 12, 71, 82, 69, 65, 84, 69, 82, 45, - 84, 72, 65, 78, 0, 5, 84, 73, 76, 68, 69, 203, 145, 20, 90, 14, 64, 6, - 73, 82, 67, 76, 69, 32, 169, 230, 38, 4, 79, 77, 77, 65, 12, 80, 2, 83, - 84, 190, 206, 19, 68, 234, 131, 6, 66, 246, 201, 10, 85, 215, 154, 3, 74, - 4, 250, 226, 38, 73, 207, 107, 65, 22, 76, 2, 69, 76, 120, 3, 79, 87, 78, - 129, 244, 31, 6, 73, 65, 77, 79, 78, 68, 10, 30, 32, 53, 3, 84, 65, 32, - 6, 170, 205, 19, 68, 142, 213, 16, 84, 223, 190, 1, 83, 4, 210, 154, 36, - 85, 143, 198, 1, 83, 10, 22, 32, 167, 9, 87, 8, 52, 5, 84, 65, 67, 75, - 32, 182, 1, 83, 227, 6, 67, 4, 222, 153, 36, 85, 215, 154, 3, 74, 6, 40, - 2, 79, 84, 225, 129, 32, 2, 45, 66, 4, 11, 65, 5, 223, 242, 31, 32, 4, - 146, 203, 19, 68, 223, 205, 16, 85, 4, 28, 2, 32, 83, 187, 7, 87, 2, 169, - 222, 37, 4, 72, 79, 69, 32, 46, 44, 2, 65, 68, 133, 3, 4, 79, 84, 69, 32, - 43, 11, 32, 40, 178, 1, 67, 38, 68, 92, 2, 85, 80, 36, 2, 76, 69, 142, - 186, 6, 81, 176, 216, 13, 3, 78, 79, 84, 22, 69, 178, 184, 5, 66, 194, - 158, 7, 83, 170, 132, 5, 71, 162, 114, 82, 195, 79, 74, 4, 178, 175, 38, - 73, 251, 129, 1, 79, 12, 54, 73, 36, 3, 79, 87, 78, 213, 199, 36, 2, 69, - 76, 4, 130, 221, 31, 86, 251, 230, 6, 65, 4, 222, 245, 3, 32, 255, 234, - 34, 87, 4, 162, 238, 37, 83, 255, 113, 70, 4, 238, 153, 12, 81, 231, 250, - 23, 85, 4, 184, 3, 5, 73, 71, 72, 84, 87, 143, 221, 39, 72, 10, 88, 5, - 69, 77, 73, 67, 79, 34, 76, 34, 84, 253, 151, 12, 7, 81, 85, 73, 83, 72, - 32, 81, 2, 237, 236, 31, 3, 76, 79, 78, 2, 185, 220, 38, 3, 65, 83, 72, - 4, 196, 136, 16, 2, 65, 82, 163, 166, 7, 73, 12, 22, 32, 171, 1, 87, 10, - 78, 67, 36, 5, 84, 65, 67, 75, 32, 253, 171, 39, 6, 83, 72, 79, 69, 32, - 74, 2, 229, 173, 23, 4, 65, 82, 69, 84, 6, 202, 195, 19, 68, 198, 138, - 17, 79, 239, 221, 2, 74, 2, 209, 222, 36, 6, 65, 82, 68, 83, 32, 86, 4, - 178, 227, 33, 83, 171, 200, 5, 76, 12, 92, 2, 82, 79, 177, 159, 26, 15, - 76, 73, 67, 65, 84, 73, 79, 78, 32, 80, 82, 79, 71, 82, 65, 10, 112, 9, - 88, 73, 77, 65, 84, 69, 76, 89, 32, 165, 221, 39, 13, 65, 67, 72, 69, 83, - 32, 84, 72, 69, 32, 76, 73, 77, 8, 76, 6, 69, 81, 85, 65, 76, 32, 245, - 242, 24, 7, 66, 85, 84, 32, 78, 79, 84, 6, 32, 2, 84, 79, 135, 232, 31, - 79, 5, 185, 160, 26, 13, 32, 79, 82, 32, 84, 72, 69, 32, 73, 77, 65, 71, - 69, 240, 22, 148, 1, 4, 65, 66, 73, 67, 156, 246, 1, 7, 77, 69, 78, 73, - 65, 78, 32, 224, 12, 3, 82, 79, 87, 228, 3, 2, 84, 73, 174, 220, 36, 73, - 251, 147, 1, 67, 158, 21, 54, 32, 217, 244, 1, 7, 45, 73, 78, 68, 73, 67, - 32, 130, 21, 178, 3, 67, 238, 1, 68, 246, 2, 69, 186, 1, 70, 212, 1, 2, - 72, 65, 104, 5, 75, 65, 83, 82, 65, 86, 76, 248, 167, 1, 2, 77, 65, 204, - 18, 7, 78, 85, 77, 66, 69, 82, 32, 36, 5, 79, 80, 69, 78, 32, 82, 80, - 238, 1, 82, 156, 2, 8, 66, 65, 83, 69, 76, 73, 78, 69, 52, 6, 73, 78, 86, - 69, 82, 84, 98, 83, 150, 33, 84, 202, 4, 86, 232, 184, 17, 9, 87, 65, 86, - 89, 32, 72, 65, 77, 90, 190, 249, 17, 81, 145, 197, 1, 6, 90, 87, 65, 82, - 65, 75, 16, 44, 2, 79, 77, 81, 5, 85, 82, 76, 89, 32, 4, 216, 158, 19, - 11, 66, 73, 78, 73, 78, 71, 32, 65, 76, 69, 70, 207, 209, 20, 77, 12, 72, - 4, 68, 65, 77, 77, 0, 4, 70, 65, 84, 72, 1, 4, 75, 65, 83, 82, 4, 11, 65, - 5, 183, 167, 38, 84, 24, 158, 1, 65, 108, 5, 79, 85, 66, 76, 69, 188, - 206, 23, 5, 69, 67, 73, 77, 65, 209, 192, 9, 16, 73, 83, 80, 85, 84, 69, - 68, 32, 69, 78, 68, 32, 79, 70, 32, 65, 14, 36, 3, 77, 77, 65, 147, 178, - 33, 84, 13, 22, 32, 199, 5, 84, 6, 154, 226, 1, 73, 54, 77, 135, 233, 36, - 87, 6, 190, 4, 68, 165, 193, 1, 18, 32, 82, 73, 71, 72, 84, 32, 65, 82, - 82, 79, 87, 72, 69, 65, 68, 32, 65, 8, 88, 12, 77, 80, 84, 89, 32, 67, - 69, 78, 84, 82, 69, 32, 57, 6, 78, 68, 32, 79, 70, 32, 4, 144, 156, 37, - 4, 72, 73, 71, 72, 1, 3, 76, 79, 87, 4, 146, 223, 29, 84, 255, 173, 3, - 65, 22, 84, 4, 65, 84, 72, 65, 238, 209, 1, 79, 156, 223, 4, 3, 73, 86, - 69, 195, 233, 30, 85, 17, 22, 32, 139, 2, 84, 10, 52, 5, 87, 73, 84, 72, - 32, 170, 222, 1, 73, 55, 77, 6, 250, 154, 38, 84, 138, 80, 68, 203, 47, - 82, 6, 72, 13, 76, 70, 32, 77, 65, 68, 68, 65, 32, 79, 86, 69, 82, 235, - 16, 77, 2, 165, 205, 17, 2, 32, 77, 13, 18, 32, 43, 84, 6, 254, 71, 87, - 130, 149, 1, 73, 55, 77, 4, 173, 82, 2, 65, 78, 230, 15, 84, 5, 65, 82, - 71, 69, 32, 146, 1, 69, 149, 90, 8, 73, 71, 65, 84, 85, 82, 69, 32, 8, - 64, 10, 82, 79, 85, 78, 68, 32, 68, 79, 84, 32, 179, 245, 10, 67, 6, 148, - 245, 10, 6, 73, 78, 83, 73, 68, 69, 162, 213, 26, 66, 167, 161, 1, 65, - 130, 8, 80, 5, 84, 84, 69, 82, 32, 185, 150, 7, 9, 70, 84, 32, 65, 82, - 82, 79, 87, 72, 254, 7, 210, 2, 65, 240, 10, 2, 66, 69, 150, 4, 68, 186, - 5, 70, 246, 4, 71, 226, 2, 72, 156, 9, 2, 74, 69, 154, 1, 75, 130, 5, 76, - 222, 2, 77, 154, 1, 78, 140, 3, 3, 80, 69, 72, 176, 1, 3, 81, 65, 70, - 214, 1, 82, 230, 3, 83, 162, 9, 84, 174, 8, 85, 148, 2, 2, 86, 69, 28, 3, - 87, 65, 87, 202, 1, 89, 246, 3, 79, 236, 2, 2, 90, 65, 31, 69, 112, 92, - 7, 70, 82, 73, 67, 65, 78, 32, 92, 2, 73, 78, 144, 2, 3, 76, 69, 70, 131, - 225, 39, 69, 8, 52, 3, 81, 65, 70, 218, 251, 32, 70, 139, 193, 3, 78, 5, - 241, 63, 5, 32, 87, 73, 84, 72, 21, 11, 32, 18, 72, 6, 87, 73, 84, 72, - 32, 84, 150, 160, 1, 70, 238, 43, 73, 199, 9, 77, 10, 60, 10, 72, 82, 69, - 69, 32, 68, 79, 84, 83, 32, 147, 54, 87, 6, 160, 250, 14, 17, 80, 79, 73, - 78, 84, 73, 78, 71, 32, 68, 79, 87, 78, 87, 65, 82, 68, 198, 201, 22, 66, - 167, 161, 1, 65, 83, 11, 32, 80, 70, 87, 212, 44, 6, 77, 65, 75, 83, 85, - 82, 182, 113, 70, 255, 52, 73, 70, 48, 4, 73, 84, 72, 32, 161, 44, 3, 65, - 83, 76, 64, 192, 2, 9, 65, 84, 84, 65, 67, 72, 69, 68, 32, 220, 1, 19, - 82, 73, 71, 72, 84, 32, 77, 73, 68, 68, 76, 69, 32, 83, 84, 82, 79, 75, - 69, 188, 1, 6, 72, 65, 77, 90, 65, 32, 44, 8, 87, 65, 86, 89, 32, 72, 65, - 77, 134, 70, 69, 204, 1, 4, 77, 65, 68, 68, 136, 165, 36, 9, 76, 69, 70, - 84, 32, 77, 73, 68, 68, 207, 236, 1, 68, 28, 204, 1, 17, 66, 79, 84, 84, - 79, 77, 32, 82, 73, 71, 72, 84, 32, 75, 65, 83, 82, 0, 14, 84, 79, 80, - 32, 82, 73, 71, 72, 84, 32, 70, 65, 84, 72, 94, 82, 56, 3, 76, 69, 70, - 190, 201, 1, 75, 207, 149, 32, 70, 6, 11, 65, 7, 29, 5, 32, 65, 78, 68, - 32, 4, 240, 170, 19, 3, 76, 69, 70, 251, 175, 19, 68, 8, 52, 3, 73, 71, - 72, 253, 214, 1, 4, 79, 85, 78, 68, 4, 17, 2, 84, 32, 4, 166, 176, 1, 82, - 223, 37, 72, 12, 154, 72, 65, 37, 5, 66, 69, 76, 79, 87, 4, 179, 168, 37, - 90, 50, 22, 72, 227, 63, 69, 41, 22, 32, 203, 63, 69, 28, 68, 5, 87, 73, - 84, 72, 32, 194, 150, 1, 70, 238, 43, 73, 199, 9, 77, 20, 116, 6, 83, 77, - 65, 76, 76, 32, 34, 84, 254, 18, 73, 248, 32, 10, 68, 79, 84, 32, 66, 69, - 76, 79, 87, 32, 191, 20, 72, 6, 242, 38, 77, 231, 184, 23, 86, 8, 88, 10, - 72, 82, 69, 69, 32, 68, 79, 84, 83, 32, 193, 48, 7, 87, 79, 32, 68, 79, - 84, 83, 6, 144, 1, 22, 80, 79, 73, 78, 84, 73, 78, 71, 32, 85, 80, 87, - 65, 82, 68, 83, 32, 66, 69, 76, 79, 87, 161, 54, 8, 72, 79, 82, 73, 90, - 79, 78, 84, 5, 171, 237, 14, 32, 78, 90, 65, 136, 4, 2, 68, 65, 40, 7, - 79, 84, 76, 69, 83, 83, 32, 234, 27, 89, 247, 25, 85, 44, 34, 76, 254, 3, - 72, 235, 45, 68, 27, 11, 32, 24, 56, 5, 87, 73, 84, 72, 32, 222, 145, 1, - 70, 255, 52, 73, 20, 108, 9, 73, 78, 86, 69, 82, 84, 69, 68, 32, 34, 84, - 168, 1, 3, 68, 79, 84, 250, 246, 11, 70, 207, 137, 27, 82, 4, 238, 14, - 83, 163, 196, 39, 86, 8, 132, 1, 10, 72, 82, 69, 69, 32, 68, 79, 84, 83, - 32, 33, 18, 87, 79, 32, 68, 79, 84, 83, 32, 86, 69, 82, 84, 73, 67, 65, - 76, 76, 89, 4, 182, 54, 65, 163, 253, 36, 66, 4, 33, 6, 32, 66, 69, 76, - 79, 87, 5, 157, 154, 15, 11, 32, 65, 78, 68, 32, 83, 77, 65, 76, 76, 32, - 12, 22, 72, 243, 62, 76, 6, 235, 53, 65, 6, 222, 232, 32, 66, 2, 70, 175, - 230, 5, 81, 44, 60, 8, 65, 82, 83, 73, 32, 89, 69, 72, 169, 2, 2, 69, 72, - 23, 11, 32, 20, 68, 5, 87, 73, 84, 72, 32, 218, 140, 1, 70, 238, 43, 73, - 199, 9, 77, 12, 144, 1, 28, 69, 88, 84, 69, 78, 68, 69, 68, 32, 65, 82, - 65, 66, 73, 67, 45, 73, 78, 68, 73, 67, 32, 68, 73, 71, 73, 84, 32, 30, - 84, 207, 39, 73, 6, 250, 8, 70, 255, 49, 84, 4, 150, 145, 7, 72, 179, - 212, 7, 87, 23, 11, 32, 20, 68, 5, 87, 73, 84, 72, 32, 178, 138, 1, 70, - 238, 43, 73, 199, 9, 77, 12, 32, 4, 68, 79, 84, 32, 51, 84, 6, 174, 40, - 66, 229, 190, 15, 4, 77, 79, 86, 69, 6, 64, 10, 72, 82, 69, 69, 32, 68, - 79, 84, 83, 32, 199, 157, 37, 87, 4, 198, 17, 80, 231, 155, 37, 66, 44, - 72, 2, 65, 70, 160, 1, 4, 72, 65, 73, 78, 222, 20, 85, 167, 179, 38, 82, - 19, 11, 32, 16, 68, 5, 87, 73, 84, 72, 32, 218, 135, 1, 70, 238, 43, 73, - 199, 9, 77, 8, 206, 23, 84, 170, 225, 38, 82, 241, 32, 8, 73, 78, 86, 69, - 82, 84, 69, 68, 15, 11, 32, 12, 68, 5, 87, 73, 84, 72, 32, 186, 134, 1, - 70, 238, 43, 73, 199, 9, 77, 4, 174, 37, 84, 131, 228, 26, 68, 82, 78, - 65, 236, 5, 2, 69, 72, 161, 2, 9, 73, 71, 72, 32, 72, 65, 77, 90, 65, 34, - 34, 72, 249, 47, 3, 77, 90, 65, 31, 11, 32, 28, 68, 5, 87, 73, 84, 72, - 32, 210, 132, 1, 70, 238, 43, 73, 199, 9, 77, 20, 132, 2, 29, 69, 88, 84, - 69, 78, 68, 69, 68, 32, 65, 82, 65, 66, 73, 67, 45, 73, 78, 68, 73, 67, - 32, 68, 73, 71, 73, 84, 32, 70, 30, 73, 92, 24, 83, 77, 65, 76, 76, 32, - 65, 82, 65, 66, 73, 67, 32, 76, 69, 84, 84, 69, 82, 32, 84, 65, 72, 32, - 62, 84, 159, 52, 72, 2, 173, 186, 1, 2, 79, 85, 2, 45, 9, 78, 86, 69, 82, - 84, 69, 68, 32, 83, 2, 217, 150, 37, 6, 77, 65, 76, 76, 32, 86, 6, 26, - 65, 203, 165, 37, 66, 4, 222, 30, 78, 155, 168, 38, 66, 8, 88, 10, 72, - 82, 69, 69, 32, 68, 79, 84, 83, 32, 33, 8, 87, 79, 32, 68, 79, 84, 83, - 32, 4, 230, 8, 80, 139, 189, 38, 65, 4, 180, 197, 38, 8, 86, 69, 82, 84, - 73, 67, 65, 76, 27, 65, 41, 11, 32, 38, 144, 1, 4, 71, 79, 65, 76, 88, 5, - 87, 73, 84, 72, 32, 128, 47, 10, 68, 79, 65, 67, 72, 65, 83, 72, 77, 69, - 230, 78, 70, 238, 43, 73, 199, 9, 77, 13, 11, 32, 10, 180, 49, 6, 87, 73, - 84, 72, 32, 72, 250, 76, 70, 238, 43, 73, 199, 9, 77, 8, 134, 26, 73, - 237, 19, 3, 89, 69, 72, 9, 11, 32, 6, 138, 206, 23, 65, 238, 137, 9, 89, - 179, 233, 5, 87, 22, 28, 2, 69, 77, 167, 45, 72, 17, 11, 32, 14, 68, 6, - 87, 73, 84, 72, 32, 84, 138, 124, 70, 238, 43, 73, 199, 9, 77, 6, 166, - 214, 14, 87, 179, 201, 18, 72, 70, 98, 65, 204, 1, 4, 69, 72, 69, 72, - 176, 2, 7, 73, 82, 71, 72, 73, 90, 32, 197, 31, 2, 72, 65, 24, 46, 70, - 177, 162, 1, 5, 83, 72, 77, 73, 82, 23, 11, 32, 20, 64, 5, 87, 73, 84, - 72, 32, 130, 122, 70, 238, 43, 73, 199, 9, 77, 12, 42, 84, 134, 161, 35, - 68, 243, 201, 3, 82, 6, 222, 27, 87, 251, 241, 36, 72, 25, 11, 32, 22, - 64, 5, 87, 73, 84, 72, 32, 230, 120, 70, 238, 43, 73, 199, 9, 77, 14, 38, - 84, 234, 41, 83, 175, 144, 38, 68, 10, 60, 10, 72, 82, 69, 69, 32, 68, - 79, 84, 83, 32, 139, 26, 87, 6, 42, 80, 230, 155, 37, 66, 167, 161, 1, - 65, 2, 161, 140, 37, 14, 79, 73, 78, 84, 73, 78, 71, 32, 85, 80, 87, 65, - 82, 68, 12, 190, 39, 79, 13, 2, 89, 85, 26, 40, 2, 65, 77, 233, 170, 1, - 2, 79, 87, 25, 11, 32, 22, 64, 5, 87, 73, 84, 72, 32, 234, 117, 70, 238, - 43, 73, 199, 9, 77, 14, 88, 2, 68, 79, 36, 6, 83, 77, 65, 76, 76, 32, - 184, 152, 33, 2, 84, 72, 159, 253, 4, 66, 4, 146, 229, 17, 85, 155, 213, - 20, 84, 4, 196, 27, 16, 65, 82, 65, 66, 73, 67, 32, 76, 69, 84, 84, 69, - 82, 32, 84, 65, 199, 155, 39, 86, 18, 36, 3, 69, 69, 77, 231, 244, 38, - 65, 17, 11, 32, 14, 64, 5, 87, 73, 84, 72, 32, 146, 115, 70, 238, 43, 73, - 199, 9, 77, 6, 134, 18, 84, 187, 136, 35, 68, 60, 38, 71, 26, 89, 17, 3, - 79, 79, 78, 21, 22, 79, 235, 109, 32, 10, 239, 26, 69, 31, 11, 32, 28, - 92, 5, 71, 72, 85, 78, 78, 16, 5, 87, 73, 84, 72, 32, 166, 113, 70, 238, - 43, 73, 199, 9, 77, 6, 251, 33, 65, 14, 116, 6, 83, 77, 65, 76, 76, 32, - 38, 84, 176, 24, 8, 73, 78, 86, 69, 82, 84, 69, 68, 210, 218, 26, 68, - 171, 238, 11, 82, 4, 206, 210, 32, 84, 131, 224, 6, 86, 4, 246, 245, 6, - 72, 191, 142, 30, 87, 25, 22, 32, 143, 24, 69, 12, 88, 11, 87, 73, 84, - 72, 32, 83, 77, 65, 76, 76, 32, 242, 110, 70, 238, 43, 73, 199, 9, 77, 4, - 26, 77, 251, 176, 39, 86, 2, 241, 179, 38, 3, 69, 69, 77, 19, 11, 32, 16, - 64, 5, 87, 73, 84, 72, 32, 230, 109, 70, 238, 43, 73, 199, 9, 77, 8, 36, - 4, 68, 79, 84, 32, 183, 12, 84, 6, 44, 5, 66, 69, 76, 79, 87, 199, 178, - 38, 65, 5, 169, 199, 14, 6, 32, 65, 78, 68, 32, 78, 52, 108, 2, 69, 72, - 184, 28, 3, 82, 69, 72, 252, 2, 4, 78, 79, 79, 78, 205, 118, 7, 79, 72, - 73, 78, 71, 89, 65, 35, 11, 32, 32, 52, 5, 87, 73, 84, 72, 32, 174, 107, - 70, 255, 52, 73, 28, 134, 1, 83, 96, 2, 84, 87, 234, 5, 73, 230, 22, 72, - 206, 180, 11, 70, 144, 133, 7, 5, 68, 79, 84, 32, 66, 234, 39, 76, 215, - 220, 19, 82, 10, 44, 5, 77, 65, 76, 76, 32, 171, 252, 38, 84, 8, 198, 6, - 65, 186, 226, 6, 78, 135, 203, 16, 86, 4, 37, 7, 79, 32, 68, 79, 84, 83, - 32, 4, 166, 8, 86, 175, 166, 38, 65, 60, 184, 1, 2, 65, 68, 120, 3, 69, - 69, 78, 136, 6, 4, 72, 69, 69, 78, 204, 148, 1, 4, 85, 80, 69, 82, 180, - 172, 4, 7, 84, 82, 65, 73, 71, 72, 84, 181, 224, 32, 6, 87, 65, 83, 72, - 32, 75, 17, 11, 32, 14, 68, 6, 87, 73, 84, 72, 32, 84, 238, 102, 70, 238, - 43, 73, 199, 9, 77, 6, 186, 138, 33, 72, 223, 240, 3, 87, 27, 11, 32, 24, - 64, 5, 87, 73, 84, 72, 32, 250, 101, 70, 238, 43, 73, 199, 9, 77, 16, - 232, 1, 3, 68, 79, 84, 50, 73, 48, 7, 83, 77, 65, 76, 76, 32, 65, 110, - 84, 150, 202, 11, 70, 221, 152, 5, 31, 69, 88, 84, 69, 78, 68, 69, 68, - 32, 65, 82, 65, 66, 73, 67, 45, 73, 78, 68, 73, 67, 32, 68, 73, 71, 73, - 84, 32, 70, 79, 85, 2, 149, 209, 18, 7, 32, 66, 69, 76, 79, 87, 32, 2, - 173, 186, 17, 7, 78, 86, 69, 82, 84, 69, 68, 2, 85, 19, 82, 65, 66, 73, - 67, 32, 76, 69, 84, 84, 69, 82, 32, 84, 65, 72, 32, 65, 78, 2, 179, 247, - 22, 68, 6, 96, 11, 72, 82, 69, 69, 32, 68, 79, 84, 83, 32, 66, 105, 9, - 87, 79, 32, 68, 79, 84, 83, 32, 86, 4, 25, 4, 69, 76, 79, 87, 5, 11, 32, - 2, 21, 3, 65, 78, 68, 2, 17, 2, 32, 84, 2, 247, 230, 6, 72, 2, 237, 198, - 11, 8, 69, 82, 84, 73, 67, 65, 76, 76, 13, 11, 32, 10, 46, 87, 134, 96, - 70, 238, 43, 73, 199, 9, 77, 2, 249, 226, 26, 5, 73, 84, 72, 32, 68, 120, - 92, 2, 65, 72, 132, 2, 4, 67, 72, 69, 72, 124, 2, 69, 72, 150, 3, 72, 61, - 3, 84, 69, 72, 21, 11, 32, 18, 64, 5, 87, 73, 84, 72, 32, 174, 94, 70, - 238, 43, 73, 199, 9, 77, 10, 26, 84, 139, 225, 26, 68, 8, 26, 87, 199, - 129, 33, 72, 4, 37, 7, 79, 32, 68, 79, 84, 83, 32, 4, 48, 6, 86, 69, 82, - 84, 73, 67, 211, 162, 38, 65, 2, 165, 195, 11, 3, 65, 76, 76, 25, 22, 32, - 163, 5, 69, 12, 64, 5, 87, 73, 84, 72, 32, 158, 92, 70, 238, 43, 73, 199, - 9, 77, 4, 198, 13, 83, 175, 144, 38, 68, 37, 22, 32, 167, 4, 69, 24, 62, - 77, 116, 5, 87, 73, 84, 72, 32, 178, 90, 70, 239, 43, 73, 10, 48, 6, 65, - 82, 66, 85, 84, 65, 175, 144, 1, 69, 9, 11, 32, 6, 226, 90, 70, 254, 52, - 73, 233, 131, 37, 2, 71, 79, 8, 104, 6, 83, 77, 65, 76, 76, 32, 56, 12, - 84, 72, 82, 69, 69, 32, 68, 79, 84, 83, 32, 65, 175, 202, 38, 82, 4, 32, - 2, 84, 69, 199, 155, 39, 86, 2, 191, 158, 38, 72, 2, 161, 233, 27, 4, 66, - 79, 86, 69, 18, 42, 65, 126, 69, 209, 129, 1, 2, 73, 78, 6, 131, 9, 76, - 23, 18, 32, 91, 69, 10, 60, 4, 87, 73, 84, 72, 218, 87, 70, 238, 43, 73, - 199, 9, 77, 2, 129, 9, 2, 32, 83, 10, 131, 11, 72, 15, 158, 1, 32, 169, - 80, 33, 73, 71, 72, 85, 82, 32, 75, 65, 90, 65, 75, 72, 32, 75, 73, 82, - 71, 72, 73, 90, 32, 65, 76, 69, 70, 32, 77, 65, 75, 83, 85, 82, 65, 8, - 96, 16, 87, 73, 84, 72, 32, 72, 65, 77, 90, 65, 32, 65, 66, 79, 86, 69, - 174, 85, 70, 255, 52, 73, 5, 143, 75, 32, 17, 222, 8, 72, 167, 76, 32, - 25, 11, 32, 22, 52, 5, 87, 73, 84, 72, 32, 190, 84, 70, 255, 52, 73, 18, - 80, 4, 68, 79, 84, 32, 162, 2, 69, 182, 1, 72, 170, 170, 14, 84, 143, - 151, 24, 82, 4, 164, 152, 31, 3, 87, 73, 84, 251, 128, 7, 65, 54, 28, 2, - 69, 72, 227, 3, 85, 49, 11, 32, 46, 100, 6, 66, 65, 82, 82, 69, 69, 252, - 2, 5, 87, 73, 84, 72, 32, 170, 79, 70, 238, 43, 73, 199, 9, 77, 17, 11, - 32, 14, 52, 5, 87, 73, 84, 72, 32, 226, 81, 70, 255, 52, 73, 10, 22, 69, - 183, 1, 72, 4, 121, 28, 88, 84, 69, 78, 68, 69, 68, 32, 65, 82, 65, 66, - 73, 67, 45, 73, 78, 68, 73, 67, 32, 68, 73, 71, 73, 84, 32, 84, 4, 184, - 239, 35, 3, 72, 82, 69, 177, 166, 2, 2, 87, 79, 6, 21, 3, 65, 77, 90, 6, - 11, 65, 6, 17, 2, 32, 65, 6, 21, 3, 66, 79, 86, 6, 11, 69, 7, 159, 79, - 32, 22, 64, 10, 72, 65, 77, 90, 65, 32, 65, 66, 79, 86, 18, 83, 39, 84, - 10, 167, 2, 69, 2, 165, 165, 17, 4, 77, 65, 76, 76, 10, 108, 18, 87, 79, - 32, 68, 79, 84, 83, 32, 66, 69, 76, 79, 87, 32, 65, 78, 68, 32, 146, 220, - 36, 65, 183, 5, 72, 6, 70, 72, 132, 204, 6, 7, 83, 77, 65, 76, 76, 32, - 78, 207, 194, 31, 68, 2, 169, 236, 33, 3, 65, 77, 90, 18, 26, 72, 17, 2, - 73, 78, 11, 243, 71, 32, 9, 11, 32, 6, 158, 76, 70, 254, 52, 73, 157, 11, - 13, 87, 73, 84, 72, 32, 73, 78, 86, 69, 82, 84, 69, 68, 220, 7, 230, 5, - 65, 158, 5, 66, 176, 2, 9, 68, 65, 68, 32, 87, 73, 84, 72, 32, 80, 9, 70, - 69, 72, 32, 87, 73, 84, 72, 32, 72, 11, 71, 72, 65, 73, 78, 32, 87, 73, - 84, 72, 32, 154, 1, 72, 134, 3, 74, 190, 2, 75, 252, 1, 9, 76, 65, 77, - 32, 87, 73, 84, 72, 32, 226, 4, 77, 152, 4, 10, 78, 79, 79, 78, 32, 87, - 73, 84, 72, 32, 182, 2, 81, 190, 2, 82, 178, 2, 83, 146, 16, 84, 196, 9, - 7, 87, 65, 83, 65, 76, 76, 65, 40, 9, 89, 69, 72, 32, 87, 73, 84, 72, 32, - 216, 3, 53, 85, 73, 71, 72, 85, 82, 32, 75, 73, 82, 71, 72, 73, 90, 32, - 89, 69, 72, 32, 87, 73, 84, 72, 32, 72, 65, 77, 90, 65, 32, 65, 66, 79, - 86, 69, 32, 87, 73, 84, 72, 32, 65, 76, 69, 70, 32, 77, 65, 75, 83, 85, - 82, 65, 205, 6, 14, 90, 65, 72, 32, 87, 73, 84, 72, 32, 77, 69, 69, 77, - 32, 54, 128, 1, 8, 73, 78, 32, 87, 73, 84, 72, 32, 70, 76, 232, 57, 4, - 75, 66, 65, 82, 129, 197, 36, 8, 90, 90, 65, 32, 87, 65, 32, 74, 28, 152, - 19, 4, 77, 69, 69, 77, 150, 16, 74, 238, 23, 65, 255, 8, 89, 22, 56, 3, - 65, 89, 72, 216, 1, 3, 69, 70, 32, 207, 13, 76, 12, 30, 73, 122, 65, 151, - 56, 69, 8, 52, 9, 32, 65, 83, 45, 83, 65, 76, 65, 65, 47, 77, 4, 80, 4, - 84, 85, 32, 87, 207, 132, 39, 77, 4, 18, 65, 23, 32, 2, 17, 2, 65, 32, 2, - 137, 142, 33, 6, 65, 83, 45, 83, 65, 76, 8, 212, 65, 29, 77, 65, 75, 83, - 85, 82, 65, 32, 87, 73, 84, 72, 32, 83, 85, 80, 69, 82, 83, 67, 82, 73, - 80, 84, 32, 65, 76, 69, 70, 1, 13, 87, 73, 84, 72, 32, 70, 65, 84, 72, - 65, 84, 65, 78, 44, 160, 1, 8, 69, 72, 32, 87, 73, 84, 72, 32, 177, 222, - 25, 25, 73, 83, 77, 73, 76, 76, 65, 72, 32, 65, 82, 45, 82, 65, 72, 77, - 65, 78, 32, 65, 82, 45, 82, 65, 72, 42, 98, 72, 24, 3, 75, 72, 65, 230, - 52, 65, 250, 3, 74, 78, 77, 86, 78, 78, 90, 242, 2, 82, 43, 89, 10, 22, - 65, 171, 56, 69, 6, 131, 59, 72, 36, 222, 6, 72, 158, 7, 75, 218, 38, 65, - 250, 3, 74, 2, 77, 134, 5, 82, 3, 89, 30, 170, 13, 75, 218, 38, 65, 250, + 32, 6, 218, 249, 40, 66, 2, 71, 3, 84, 10, 60, 4, 73, 71, 78, 32, 241, + 178, 23, 5, 89, 77, 66, 79, 76, 8, 50, 83, 182, 245, 25, 75, 249, 145, + 10, 2, 82, 85, 4, 212, 248, 37, 4, 77, 65, 76, 76, 203, 177, 2, 69, 22, + 66, 65, 138, 163, 37, 85, 186, 202, 1, 73, 198, 140, 2, 69, 3, 79, 11, + 130, 250, 40, 65, 2, 73, 2, 77, 3, 87, 7, 11, 32, 4, 232, 218, 35, 3, 68, + 69, 80, 221, 205, 4, 5, 65, 82, 82, 73, 86, 152, 2, 212, 1, 4, 65, 82, + 77, 32, 48, 20, 67, 72, 69, 77, 73, 67, 65, 76, 32, 83, 89, 77, 66, 79, + 76, 32, 70, 79, 82, 32, 166, 28, 69, 60, 4, 73, 69, 78, 32, 208, 3, 2, + 76, 32, 82, 77, 109, 6, 84, 69, 82, 78, 65, 84, 4, 212, 131, 14, 3, 66, + 69, 76, 203, 129, 24, 67, 232, 1, 158, 2, 65, 246, 2, 66, 210, 1, 67, + 138, 3, 68, 98, 71, 38, 72, 132, 1, 4, 73, 82, 79, 78, 82, 76, 74, 77, + 144, 1, 2, 78, 73, 34, 80, 176, 2, 3, 81, 85, 73, 74, 82, 146, 2, 83, + 210, 5, 84, 166, 1, 86, 200, 1, 2, 87, 65, 250, 145, 35, 85, 154, 220, 1, + 69, 194, 212, 1, 70, 219, 56, 79, 30, 194, 1, 76, 72, 3, 81, 85, 65, 204, + 8, 7, 78, 84, 73, 77, 79, 78, 89, 224, 157, 3, 3, 77, 65, 76, 240, 188, + 21, 3, 82, 83, 69, 128, 241, 13, 5, 85, 82, 73, 80, 73, 162, 136, 1, 83, + 183, 93, 73, 8, 224, 24, 2, 69, 77, 240, 196, 26, 4, 75, 65, 76, 73, 235, + 132, 14, 85, 10, 38, 32, 233, 252, 24, 3, 70, 79, 82, 8, 216, 7, 4, 86, + 73, 84, 65, 189, 179, 23, 4, 82, 69, 71, 73, 16, 148, 1, 7, 65, 84, 72, + 32, 79, 70, 32, 204, 6, 6, 73, 83, 77, 85, 84, 72, 144, 1, 4, 79, 82, 65, + 88, 164, 1, 4, 76, 65, 67, 75, 191, 181, 39, 82, 4, 166, 132, 17, 77, + 181, 219, 22, 5, 86, 65, 80, 79, 85, 28, 82, 65, 92, 6, 79, 80, 80, 69, + 82, 32, 34, 82, 253, 202, 39, 4, 73, 78, 78, 65, 6, 216, 143, 6, 2, 68, + 85, 192, 180, 33, 9, 80, 85, 84, 32, 77, 79, 82, 84, 85, 231, 28, 76, 4, + 230, 13, 65, 195, 190, 39, 79, 16, 72, 8, 79, 67, 85, 83, 32, 79, 70, 32, + 53, 6, 85, 67, 73, 66, 76, 69, 6, 228, 16, 5, 67, 79, 80, 80, 69, 223, + 181, 39, 73, 11, 11, 45, 8, 182, 237, 40, 50, 2, 51, 2, 52, 3, 53, 8, 44, + 2, 73, 83, 249, 238, 37, 3, 65, 89, 45, 6, 144, 2, 4, 83, 79, 76, 86, + 247, 238, 37, 84, 4, 222, 188, 30, 79, 247, 158, 10, 85, 8, 32, 4, 65, + 76, 70, 32, 47, 79, 4, 196, 219, 35, 2, 79, 85, 131, 211, 3, 68, 4, 200, + 137, 3, 4, 82, 83, 69, 32, 147, 169, 37, 85, 6, 56, 3, 32, 79, 82, 73, 7, + 45, 67, 79, 80, 80, 69, 82, 4, 135, 213, 26, 69, 4, 48, 3, 69, 65, 68, + 157, 225, 13, 3, 79, 68, 69, 2, 239, 252, 35, 32, 10, 120, 16, 69, 82, + 67, 85, 82, 89, 32, 83, 85, 66, 76, 73, 77, 65, 84, 69, 200, 209, 16, 4, + 65, 82, 67, 65, 167, 146, 22, 79, 7, 251, 231, 40, 45, 4, 158, 198, 39, + 84, 135, 120, 71, 14, 108, 11, 72, 73, 76, 79, 83, 79, 80, 72, 69, 82, + 83, 34, 79, 98, 85, 205, 160, 30, 6, 82, 69, 67, 73, 80, 73, 2, 209, 9, + 4, 32, 83, 85, 76, 6, 52, 4, 87, 68, 69, 82, 185, 172, 36, 3, 84, 32, 65, + 5, 225, 180, 39, 5, 69, 68, 32, 66, 82, 4, 176, 166, 34, 4, 84, 82, 69, + 70, 209, 174, 6, 3, 82, 73, 70, 4, 216, 212, 35, 5, 78, 84, 69, 83, 83, + 181, 247, 3, 4, 67, 75, 32, 76, 24, 58, 69, 201, 207, 26, 8, 79, 67, 75, + 32, 83, 65, 76, 84, 20, 68, 5, 71, 85, 76, 85, 83, 164, 7, 3, 65, 76, 71, + 215, 154, 26, 84, 15, 32, 4, 32, 79, 70, 32, 71, 45, 6, 216, 206, 26, 8, + 65, 78, 84, 73, 77, 79, 78, 89, 143, 238, 12, 73, 6, 214, 227, 40, 50, 2, + 51, 3, 52, 34, 164, 1, 2, 65, 76, 194, 1, 84, 142, 1, 85, 128, 218, 17, + 2, 80, 73, 132, 178, 12, 2, 73, 76, 182, 192, 9, 79, 169, 22, 11, 67, 69, + 80, 84, 69, 82, 32, 79, 70, 32, 74, 8, 58, 84, 193, 183, 39, 8, 45, 65, + 77, 77, 79, 78, 73, 65, 7, 25, 4, 32, 79, 70, 32, 4, 52, 8, 67, 79, 80, + 80, 69, 82, 32, 65, 231, 5, 65, 2, 133, 155, 30, 7, 78, 84, 73, 77, 79, + 78, 73, 6, 236, 3, 8, 65, 82, 82, 69, 68, 32, 84, 82, 157, 199, 26, 19, + 82, 65, 84, 85, 77, 32, 83, 85, 80, 69, 82, 32, 83, 84, 82, 65, 84, 85, + 77, 12, 44, 6, 66, 76, 73, 77, 65, 84, 155, 1, 76, 10, 44, 5, 69, 32, 79, + 70, 32, 247, 142, 40, 73, 8, 68, 8, 83, 65, 76, 84, 32, 79, 70, 32, 130, + 3, 65, 155, 198, 27, 67, 4, 254, 2, 65, 155, 198, 27, 67, 2, 163, 229, + 39, 70, 12, 68, 3, 65, 82, 84, 32, 2, 73, 78, 34, 82, 253, 236, 38, 2, + 85, 84, 4, 11, 65, 4, 207, 199, 26, 82, 4, 178, 190, 35, 67, 187, 49, 32, + 2, 177, 153, 40, 2, 73, 68, 14, 50, 73, 133, 181, 38, 6, 69, 82, 68, 73, + 71, 82, 12, 64, 5, 78, 69, 71, 65, 82, 137, 198, 26, 5, 84, 82, 73, 79, + 76, 9, 44, 5, 32, 79, 70, 32, 65, 167, 218, 40, 45, 2, 141, 174, 24, 3, + 78, 84, 73, 4, 138, 157, 40, 84, 239, 61, 88, 6, 38, 77, 238, 196, 39, + 70, 155, 121, 82, 2, 147, 176, 39, 66, 22, 104, 7, 77, 79, 78, 83, 84, + 69, 82, 138, 1, 83, 129, 241, 35, 10, 67, 82, 65, 66, 32, 83, 84, 69, 80, + 80, 11, 11, 32, 8, 88, 6, 67, 76, 79, 83, 69, 68, 0, 4, 79, 80, 69, 78, + 225, 210, 36, 4, 83, 84, 69, 80, 2, 197, 249, 37, 3, 32, 74, 65, 8, 60, + 6, 80, 73, 68, 69, 82, 32, 57, 5, 81, 85, 73, 68, 32, 4, 248, 178, 29, 5, + 67, 82, 79, 85, 67, 175, 250, 1, 83, 4, 56, 6, 67, 76, 79, 83, 69, 68, 1, + 4, 79, 80, 69, 78, 2, 249, 232, 27, 5, 32, 84, 69, 78, 84, 4, 134, 245, + 33, 69, 253, 184, 4, 11, 65, 82, 79, 85, 78, 68, 45, 80, 82, 79, 70, 9, + 49, 10, 79, 83, 84, 32, 69, 81, 85, 65, 76, 32, 6, 32, 2, 84, 79, 171, + 182, 32, 79, 5, 219, 255, 6, 32, 4, 136, 243, 10, 3, 73, 86, 69, 145, + 183, 28, 5, 69, 32, 79, 78, 69, 12, 162, 1, 80, 180, 191, 29, 6, 69, 82, + 73, 67, 65, 78, 204, 131, 6, 3, 66, 85, 76, 245, 254, 3, 16, 65, 76, 71, + 65, 77, 65, 84, 73, 79, 78, 32, 79, 82, 32, 67, 79, 6, 26, 72, 175, 128, + 37, 69, 4, 204, 242, 24, 3, 73, 84, 82, 255, 155, 15, 79, 194, 9, 92, 3, + 65, 84, 79, 182, 20, 71, 174, 1, 84, 190, 149, 25, 67, 242, 150, 12, 68, + 223, 142, 3, 75, 144, 9, 112, 17, 76, 73, 65, 78, 32, 72, 73, 69, 82, 79, + 71, 76, 89, 80, 72, 32, 65, 197, 201, 39, 5, 77, 73, 67, 65, 76, 142, 9, + 70, 48, 138, 4, 49, 198, 2, 50, 162, 3, 51, 174, 5, 52, 163, 3, 53, 222, + 1, 106, 50, 102, 52, 110, 54, 102, 57, 182, 7, 51, 222, 242, 11, 49, 226, + 146, 23, 48, 130, 2, 53, 2, 55, 3, 56, 22, 210, 189, 36, 54, 242, 145, 4, + 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 28, 170, 187, + 12, 54, 198, 129, 24, 49, 2, 53, 242, 145, 4, 48, 2, 50, 2, 51, 2, 52, 2, + 55, 2, 56, 3, 57, 26, 130, 162, 12, 54, 242, 171, 28, 48, 2, 49, 2, 50, + 2, 51, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 24, 158, 187, 36, 55, 2, 56, + 242, 145, 4, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 57, 232, 1, + 98, 48, 114, 49, 174, 166, 12, 50, 2, 51, 138, 230, 22, 52, 2, 53, 2, 54, + 2, 55, 2, 56, 3, 57, 42, 210, 159, 12, 52, 2, 55, 190, 24, 53, 198, 129, + 24, 48, 2, 49, 2, 50, 242, 145, 4, 51, 2, 54, 2, 56, 3, 57, 26, 158, 183, + 12, 48, 198, 129, 24, 53, 242, 145, 4, 49, 2, 50, 2, 51, 2, 52, 2, 54, 2, + 55, 2, 56, 3, 57, 222, 1, 102, 48, 110, 49, 102, 57, 210, 1, 56, 218, + 148, 12, 50, 2, 54, 230, 242, 22, 51, 2, 52, 2, 53, 3, 55, 28, 198, 181, + 12, 50, 198, 129, 24, 55, 2, 57, 242, 145, 4, 48, 2, 49, 2, 51, 2, 52, 2, + 53, 2, 54, 3, 56, 24, 158, 182, 36, 53, 2, 54, 242, 145, 4, 48, 2, 49, 2, + 50, 2, 51, 2, 52, 2, 55, 2, 56, 3, 57, 24, 186, 181, 36, 52, 2, 57, 242, + 145, 4, 48, 2, 49, 2, 50, 2, 51, 2, 53, 2, 54, 2, 55, 3, 56, 228, 1, 102, + 48, 2, 50, 2, 53, 102, 51, 110, 54, 102, 56, 162, 1, 57, 142, 253, 11, + 55, 222, 134, 23, 49, 3, 52, 22, 234, 179, 36, 57, 242, 145, 4, 48, 2, + 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 30, 134, 153, 12, + 54, 130, 197, 8, 50, 242, 230, 19, 48, 2, 49, 2, 51, 2, 52, 2, 53, 2, 55, + 2, 56, 3, 57, 24, 154, 178, 36, 52, 2, 56, 242, 145, 4, 48, 2, 49, 2, 50, + 2, 51, 2, 53, 2, 54, 2, 55, 3, 57, 26, 98, 51, 214, 176, 36, 49, 2, 54, + 242, 145, 4, 48, 2, 50, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 4, 196, 142, + 21, 6, 32, 82, 65, 32, 79, 82, 255, 179, 19, 65, 20, 180, 151, 40, 3, 51, + 32, 69, 210, 42, 48, 2, 49, 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, + 57, 202, 1, 102, 49, 210, 1, 53, 178, 229, 20, 57, 146, 155, 14, 48, 2, + 50, 2, 51, 2, 52, 2, 54, 2, 55, 3, 56, 22, 90, 48, 214, 191, 40, 49, 2, + 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 4, 60, 6, 32, 66, + 69, 71, 73, 78, 1, 5, 65, 32, 69, 78, 68, 2, 189, 193, 16, 8, 32, 76, 79, + 71, 79, 71, 82, 65, 24, 238, 172, 36, 48, 2, 55, 242, 145, 4, 49, 2, 50, + 2, 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 60, 150, 151, 33, 51, 198, 230, + 1, 48, 130, 2, 49, 3, 50, 14, 96, 2, 76, 69, 182, 176, 2, 85, 180, 183, + 32, 2, 83, 84, 130, 239, 3, 69, 217, 168, 1, 2, 82, 89, 7, 33, 6, 32, 87, + 73, 84, 72, 32, 4, 178, 138, 32, 83, 135, 195, 4, 85, 31, 76, 4, 69, 78, + 78, 65, 41, 11, 73, 67, 76, 79, 67, 75, 87, 73, 83, 69, 32, 5, 177, 143, + 37, 5, 32, 87, 73, 84, 72, 24, 90, 84, 162, 139, 7, 67, 58, 68, 122, 71, + 138, 4, 79, 157, 151, 28, 5, 73, 78, 84, 69, 71, 12, 84, 15, 82, 73, 65, + 78, 71, 76, 69, 45, 72, 69, 65, 68, 69, 68, 32, 223, 143, 7, 79, 10, 112, + 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 12, 6, 66, 79, 84, 84, 79, 77, 0, 3, + 84, 79, 80, 131, 143, 7, 79, 2, 11, 84, 2, 133, 219, 37, 7, 32, 85, 45, + 83, 72, 65, 80, 160, 1, 128, 1, 20, 76, 32, 70, 85, 78, 67, 84, 73, 79, + 78, 65, 76, 32, 83, 89, 77, 66, 79, 76, 32, 206, 15, 79, 38, 80, 199, + 167, 40, 67, 140, 1, 222, 2, 67, 186, 1, 68, 190, 2, 73, 44, 4, 65, 76, + 80, 72, 0, 4, 79, 77, 69, 71, 32, 4, 74, 79, 84, 32, 36, 4, 76, 69, 70, + 84, 68, 2, 81, 85, 218, 3, 82, 54, 83, 92, 4, 69, 80, 83, 73, 32, 6, 66, + 65, 67, 75, 83, 76, 76, 2, 85, 80, 248, 161, 16, 12, 71, 82, 69, 65, 84, + 69, 82, 45, 84, 72, 65, 78, 0, 5, 84, 73, 76, 68, 69, 139, 160, 20, 90, + 14, 64, 6, 73, 82, 67, 76, 69, 32, 185, 144, 39, 4, 79, 77, 77, 65, 12, + 80, 2, 83, 84, 166, 230, 19, 68, 150, 133, 6, 66, 254, 216, 10, 85, 207, + 158, 3, 74, 4, 250, 140, 39, 73, 227, 109, 65, 22, 76, 2, 69, 76, 120, 3, + 79, 87, 78, 253, 145, 32, 6, 73, 65, 77, 79, 78, 68, 10, 30, 32, 53, 3, + 84, 65, 32, 6, 146, 229, 19, 68, 194, 229, 16, 84, 131, 191, 1, 83, 4, + 238, 194, 36, 85, 179, 198, 1, 83, 10, 22, 32, 167, 9, 87, 8, 52, 5, 84, + 65, 67, 75, 32, 182, 1, 83, 227, 6, 67, 4, 250, 193, 36, 85, 207, 158, 3, + 74, 6, 40, 2, 79, 84, 237, 159, 32, 2, 45, 66, 4, 11, 65, 5, 219, 144, + 32, 32, 4, 250, 226, 19, 68, 147, 222, 16, 85, 4, 28, 2, 32, 83, 187, 7, + 87, 2, 233, 134, 38, 4, 72, 79, 69, 32, 46, 44, 2, 65, 68, 133, 3, 4, 79, + 84, 69, 32, 43, 11, 32, 40, 178, 1, 67, 38, 68, 92, 2, 85, 80, 36, 2, 76, + 69, 210, 208, 6, 81, 228, 217, 13, 3, 78, 79, 84, 22, 69, 206, 185, 5, + 66, 174, 163, 7, 83, 226, 142, 5, 71, 250, 115, 82, 199, 81, 74, 4, 170, + 217, 38, 73, 151, 132, 1, 79, 12, 54, 73, 36, 3, 79, 87, 78, 149, 240, + 36, 2, 69, 76, 4, 254, 250, 31, 86, 255, 242, 6, 65, 4, 230, 138, 4, 32, + 135, 128, 35, 87, 4, 218, 150, 38, 83, 215, 115, 70, 4, 230, 172, 12, 81, + 139, 144, 24, 85, 4, 184, 3, 5, 73, 71, 72, 84, 87, 163, 137, 40, 72, 10, + 88, 5, 69, 77, 73, 67, 79, 34, 76, 34, 84, 245, 170, 12, 7, 81, 85, 73, + 83, 72, 32, 81, 2, 233, 138, 32, 3, 76, 79, 78, 2, 201, 134, 39, 3, 65, + 83, 72, 4, 160, 162, 16, 2, 65, 82, 227, 169, 7, 73, 12, 22, 32, 171, 1, + 87, 10, 78, 67, 36, 5, 84, 65, 67, 75, 32, 145, 216, 39, 6, 83, 72, 79, + 69, 32, 74, 2, 129, 203, 23, 4, 65, 82, 69, 84, 6, 178, 219, 19, 68, 158, + 155, 17, 79, 195, 225, 2, 74, 2, 145, 135, 37, 6, 65, 82, 68, 83, 32, 86, + 4, 234, 128, 34, 83, 135, 215, 5, 76, 14, 26, 76, 93, 2, 82, 79, 4, 216, + 184, 26, 14, 73, 67, 65, 84, 73, 79, 78, 32, 80, 82, 79, 71, 82, 65, 139, + 217, 12, 69, 10, 112, 9, 88, 73, 77, 65, 84, 69, 76, 89, 32, 161, 137, + 40, 13, 65, 67, 72, 69, 83, 32, 84, 72, 69, 32, 76, 73, 77, 8, 76, 6, 69, + 81, 85, 65, 76, 32, 137, 144, 25, 7, 66, 85, 84, 32, 78, 79, 84, 6, 32, + 2, 84, 79, 235, 133, 32, 79, 5, 161, 185, 26, 13, 32, 79, 82, 32, 84, 72, + 69, 32, 73, 77, 65, 71, 69, 192, 23, 148, 1, 4, 65, 66, 73, 67, 240, 131, + 2, 7, 77, 69, 78, 73, 65, 78, 32, 224, 12, 3, 82, 79, 87, 228, 3, 2, 84, + 73, 202, 248, 36, 73, 135, 150, 1, 67, 238, 21, 54, 32, 173, 130, 2, 7, + 45, 73, 78, 68, 73, 67, 32, 210, 21, 150, 3, 66, 134, 1, 67, 238, 1, 68, + 138, 3, 69, 186, 1, 70, 212, 1, 2, 72, 65, 104, 5, 75, 65, 83, 82, 65, + 86, 76, 212, 180, 1, 2, 77, 65, 204, 18, 7, 78, 85, 77, 66, 69, 82, 32, + 36, 5, 79, 80, 69, 78, 32, 82, 80, 238, 1, 82, 208, 2, 6, 73, 78, 86, 69, + 82, 84, 98, 83, 146, 33, 84, 202, 4, 86, 228, 194, 17, 9, 87, 65, 86, 89, + 32, 72, 65, 77, 90, 154, 138, 18, 81, 197, 198, 1, 6, 90, 87, 65, 82, 65, + 75, 4, 216, 214, 1, 7, 65, 83, 69, 76, 73, 78, 69, 157, 189, 37, 17, 73, + 66, 76, 73, 67, 65, 76, 32, 69, 78, 68, 32, 79, 70, 32, 86, 69, 16, 44, + 2, 79, 77, 81, 5, 85, 82, 76, 89, 32, 4, 192, 181, 19, 11, 66, 73, 78, + 73, 78, 71, 32, 65, 76, 69, 70, 251, 229, 20, 77, 12, 72, 4, 68, 65, 77, + 77, 0, 4, 70, 65, 84, 72, 1, 4, 75, 65, 83, 82, 4, 11, 65, 5, 175, 208, + 38, 84, 26, 158, 1, 65, 108, 5, 79, 85, 66, 76, 69, 216, 234, 23, 5, 69, + 67, 73, 77, 65, 201, 193, 9, 16, 73, 83, 80, 85, 84, 69, 68, 32, 69, 78, + 68, 32, 79, 70, 32, 65, 14, 36, 3, 77, 77, 65, 155, 207, 33, 84, 13, 22, + 32, 219, 5, 84, 6, 242, 238, 1, 73, 54, 77, 175, 133, 37, 87, 8, 22, 32, + 191, 4, 68, 6, 184, 210, 1, 17, 82, 73, 71, 72, 84, 32, 65, 82, 82, 79, + 87, 72, 69, 65, 68, 32, 65, 191, 30, 86, 8, 88, 12, 77, 80, 84, 89, 32, + 67, 69, 78, 84, 82, 69, 32, 57, 6, 78, 68, 32, 79, 70, 32, 4, 192, 195, + 37, 4, 72, 73, 71, 72, 1, 3, 76, 79, 87, 4, 134, 251, 29, 84, 139, 175, + 3, 65, 22, 84, 4, 65, 84, 72, 65, 166, 222, 1, 79, 148, 232, 4, 3, 73, + 86, 69, 195, 251, 30, 85, 17, 22, 32, 139, 2, 84, 10, 52, 5, 87, 73, 84, + 72, 32, 238, 234, 1, 73, 55, 77, 6, 222, 195, 38, 84, 166, 82, 68, 203, + 47, 82, 6, 72, 13, 76, 70, 32, 77, 65, 68, 68, 65, 32, 79, 86, 69, 82, + 235, 16, 77, 2, 221, 226, 17, 2, 32, 77, 13, 18, 32, 43, 84, 6, 166, 72, + 87, 158, 161, 1, 73, 55, 77, 4, 253, 82, 2, 65, 78, 174, 16, 84, 5, 65, + 82, 71, 69, 32, 146, 1, 69, 133, 91, 8, 73, 71, 65, 84, 85, 82, 69, 32, + 8, 64, 10, 82, 79, 85, 78, 68, 32, 68, 79, 84, 32, 179, 139, 11, 67, 6, + 148, 139, 11, 6, 73, 78, 83, 73, 68, 69, 198, 230, 26, 66, 131, 165, 1, + 65, 136, 8, 80, 5, 84, 84, 69, 82, 32, 137, 170, 7, 9, 70, 84, 32, 65, + 82, 82, 79, 87, 72, 132, 8, 210, 2, 65, 240, 10, 2, 66, 69, 150, 4, 68, + 186, 5, 70, 246, 4, 71, 226, 2, 72, 156, 9, 2, 74, 69, 158, 1, 75, 138, + 5, 76, 226, 2, 77, 154, 1, 78, 160, 3, 3, 80, 69, 72, 176, 1, 3, 81, 65, + 70, 214, 1, 82, 234, 3, 83, 162, 9, 84, 214, 8, 85, 148, 2, 2, 86, 69, + 28, 3, 87, 65, 87, 202, 1, 89, 246, 3, 79, 140, 3, 2, 90, 65, 31, 69, + 112, 92, 7, 70, 82, 73, 67, 65, 78, 32, 92, 2, 73, 78, 144, 2, 3, 76, 69, + 70, 131, 140, 40, 69, 8, 52, 3, 81, 65, 70, 218, 152, 33, 70, 183, 203, + 3, 78, 5, 153, 64, 5, 32, 87, 73, 84, 72, 21, 11, 32, 18, 72, 6, 87, 73, + 84, 72, 32, 84, 242, 172, 1, 70, 202, 43, 73, 211, 9, 77, 10, 60, 10, 72, + 82, 69, 69, 32, 68, 79, 84, 83, 32, 187, 54, 87, 6, 168, 146, 15, 17, 80, + 79, 73, 78, 84, 73, 78, 71, 32, 68, 79, 87, 78, 87, 65, 82, 68, 226, 216, + 22, 66, 131, 165, 1, 65, 83, 11, 32, 80, 70, 87, 228, 44, 6, 77, 65, 75, + 83, 85, 82, 130, 126, 70, 231, 52, 73, 70, 48, 4, 73, 84, 72, 32, 177, + 44, 3, 65, 83, 76, 64, 192, 2, 9, 65, 84, 84, 65, 67, 72, 69, 68, 32, + 220, 1, 19, 82, 73, 71, 72, 84, 32, 77, 73, 68, 68, 76, 69, 32, 83, 84, + 82, 79, 75, 69, 188, 1, 6, 72, 65, 77, 90, 65, 32, 44, 8, 87, 65, 86, 89, + 32, 72, 65, 77, 214, 70, 69, 204, 1, 4, 77, 65, 68, 68, 232, 203, 36, 9, + 76, 69, 70, 84, 32, 77, 73, 68, 68, 159, 240, 1, 68, 28, 204, 1, 17, 66, + 79, 84, 84, 79, 77, 32, 82, 73, 71, 72, 84, 32, 75, 65, 83, 82, 0, 14, + 84, 79, 80, 32, 82, 73, 71, 72, 84, 32, 70, 65, 84, 72, 94, 82, 56, 3, + 76, 69, 70, 130, 214, 1, 75, 195, 165, 32, 70, 6, 11, 65, 7, 29, 5, 32, + 65, 78, 68, 32, 4, 196, 193, 19, 3, 76, 69, 70, 167, 196, 19, 68, 8, 52, + 3, 73, 71, 72, 213, 227, 1, 4, 79, 85, 78, 68, 4, 17, 2, 84, 32, 4, 130, + 189, 1, 82, 219, 37, 72, 12, 234, 72, 65, 37, 5, 66, 69, 76, 79, 87, 4, + 223, 207, 37, 90, 50, 22, 72, 179, 64, 69, 41, 22, 32, 155, 64, 69, 28, + 68, 5, 87, 73, 84, 72, 32, 158, 163, 1, 70, 202, 43, 73, 211, 9, 77, 20, + 116, 6, 83, 77, 65, 76, 76, 32, 34, 84, 254, 18, 73, 160, 33, 10, 68, 79, + 84, 32, 66, 69, 76, 79, 87, 32, 135, 21, 72, 6, 150, 39, 77, 231, 212, + 23, 86, 8, 88, 10, 72, 82, 69, 69, 32, 68, 79, 84, 83, 32, 233, 48, 7, + 87, 79, 32, 68, 79, 84, 83, 6, 144, 1, 22, 80, 79, 73, 78, 84, 73, 78, + 71, 32, 85, 80, 87, 65, 82, 68, 83, 32, 66, 69, 76, 79, 87, 201, 54, 8, + 72, 79, 82, 73, 90, 79, 78, 84, 5, 179, 133, 15, 32, 78, 90, 65, 136, 4, + 2, 68, 65, 40, 7, 79, 84, 76, 69, 83, 83, 32, 250, 27, 89, 147, 26, 85, + 44, 34, 76, 254, 3, 72, 147, 46, 68, 27, 11, 32, 24, 56, 5, 87, 73, 84, + 72, 32, 186, 158, 1, 70, 231, 52, 73, 20, 108, 9, 73, 78, 86, 69, 82, 84, + 69, 68, 32, 34, 84, 168, 1, 3, 68, 79, 84, 222, 136, 12, 70, 235, 162, + 27, 82, 4, 238, 14, 83, 163, 239, 39, 86, 8, 132, 1, 10, 72, 82, 69, 69, + 32, 68, 79, 84, 83, 32, 33, 18, 87, 79, 32, 68, 79, 84, 83, 32, 86, 69, + 82, 84, 73, 67, 65, 76, 76, 89, 4, 226, 54, 65, 155, 164, 37, 66, 4, 33, + 6, 32, 66, 69, 76, 79, 87, 5, 217, 178, 15, 11, 32, 65, 78, 68, 32, 83, + 77, 65, 76, 76, 32, 12, 22, 72, 195, 63, 76, 6, 151, 54, 65, 6, 222, 133, + 33, 66, 2, 70, 175, 244, 5, 81, 44, 60, 8, 65, 82, 83, 73, 32, 89, 69, + 72, 169, 2, 2, 69, 72, 23, 11, 32, 20, 68, 5, 87, 73, 84, 72, 32, 182, + 153, 1, 70, 202, 43, 73, 211, 9, 77, 12, 144, 1, 28, 69, 88, 84, 69, 78, + 68, 69, 68, 32, 65, 82, 65, 66, 73, 67, 45, 73, 78, 68, 73, 67, 32, 68, + 73, 71, 73, 84, 32, 30, 84, 247, 39, 73, 6, 250, 8, 70, 207, 50, 84, 4, + 190, 165, 7, 72, 147, 216, 7, 87, 23, 11, 32, 20, 68, 5, 87, 73, 84, 72, + 32, 142, 151, 1, 70, 202, 43, 73, 211, 9, 77, 12, 32, 4, 68, 79, 84, 32, + 51, 84, 6, 214, 40, 66, 145, 215, 15, 4, 77, 79, 86, 69, 6, 64, 10, 72, + 82, 69, 69, 32, 68, 79, 84, 83, 32, 243, 196, 37, 87, 4, 210, 17, 80, + 255, 194, 37, 66, 44, 72, 2, 65, 70, 160, 1, 4, 72, 65, 73, 78, 238, 20, + 85, 151, 222, 38, 82, 19, 11, 32, 16, 68, 5, 87, 73, 84, 72, 32, 182, + 148, 1, 70, 202, 43, 73, 211, 9, 77, 8, 242, 23, 84, 134, 140, 39, 82, + 241, 32, 8, 73, 78, 86, 69, 82, 84, 69, 68, 15, 11, 32, 12, 68, 5, 87, + 73, 84, 72, 32, 150, 147, 1, 70, 202, 43, 73, 211, 9, 77, 4, 214, 37, 84, + 187, 251, 26, 68, 82, 78, 65, 236, 5, 2, 69, 72, 161, 2, 9, 73, 71, 72, + 32, 72, 65, 77, 90, 65, 34, 34, 72, 201, 48, 3, 77, 90, 65, 31, 11, 32, + 28, 68, 5, 87, 73, 84, 72, 32, 174, 145, 1, 70, 202, 43, 73, 211, 9, 77, + 20, 132, 2, 29, 69, 88, 84, 69, 78, 68, 69, 68, 32, 65, 82, 65, 66, 73, + 67, 45, 73, 78, 68, 73, 67, 32, 68, 73, 71, 73, 84, 32, 70, 30, 73, 92, + 24, 83, 77, 65, 76, 76, 32, 65, 82, 65, 66, 73, 67, 32, 76, 69, 84, 84, + 69, 82, 32, 84, 65, 72, 32, 62, 84, 143, 53, 72, 2, 129, 199, 1, 2, 79, + 85, 2, 45, 9, 78, 86, 69, 82, 84, 69, 68, 32, 83, 2, 133, 190, 37, 6, 77, + 65, 76, 76, 32, 86, 6, 26, 65, 239, 204, 37, 66, 4, 134, 31, 78, 243, + 210, 38, 66, 8, 88, 10, 72, 82, 69, 69, 32, 68, 79, 84, 83, 32, 33, 8, + 87, 79, 32, 68, 79, 84, 83, 32, 4, 242, 8, 80, 255, 231, 38, 65, 4, 180, + 240, 38, 8, 86, 69, 82, 84, 73, 67, 65, 76, 27, 65, 41, 11, 32, 38, 144, + 1, 4, 71, 79, 65, 76, 88, 5, 87, 73, 84, 72, 32, 240, 47, 10, 68, 79, 65, + 67, 72, 65, 83, 72, 77, 69, 210, 90, 70, 202, 43, 73, 211, 9, 77, 13, 11, + 32, 10, 164, 50, 6, 87, 73, 84, 72, 32, 72, 230, 88, 70, 202, 43, 73, + 211, 9, 77, 8, 174, 26, 73, 149, 20, 3, 89, 69, 72, 9, 11, 32, 6, 174, + 234, 23, 65, 202, 138, 9, 89, 179, 247, 5, 87, 22, 28, 2, 69, 77, 247, + 45, 72, 17, 11, 32, 14, 72, 6, 87, 73, 84, 72, 32, 84, 226, 136, 1, 70, + 202, 43, 73, 211, 9, 77, 6, 170, 238, 14, 87, 203, 205, 18, 72, 70, 98, + 65, 208, 1, 4, 69, 72, 69, 72, 180, 2, 7, 73, 82, 71, 72, 73, 90, 32, + 137, 32, 2, 72, 65, 24, 46, 70, 229, 174, 1, 5, 83, 72, 77, 73, 82, 23, + 11, 32, 20, 68, 5, 87, 73, 84, 72, 32, 214, 134, 1, 70, 202, 43, 73, 211, + 9, 77, 12, 42, 84, 218, 194, 35, 68, 151, 211, 3, 82, 6, 254, 27, 87, + 255, 152, 37, 72, 25, 11, 32, 22, 68, 5, 87, 73, 84, 72, 32, 182, 133, 1, + 70, 202, 43, 73, 211, 9, 77, 14, 38, 84, 206, 42, 83, 191, 186, 38, 68, + 10, 60, 10, 72, 82, 69, 69, 32, 68, 79, 84, 83, 32, 167, 26, 87, 6, 42, + 80, 254, 194, 37, 66, 131, 165, 1, 65, 2, 193, 179, 37, 14, 79, 73, 78, + 84, 73, 78, 71, 32, 85, 80, 87, 65, 82, 68, 12, 130, 40, 79, 13, 2, 89, + 85, 26, 40, 2, 65, 77, 161, 183, 1, 2, 79, 87, 25, 11, 32, 22, 68, 5, 87, + 73, 84, 72, 32, 182, 130, 1, 70, 202, 43, 73, 211, 9, 77, 14, 88, 2, 68, + 79, 36, 6, 83, 77, 65, 76, 76, 32, 200, 180, 33, 2, 84, 72, 251, 137, 5, + 66, 4, 146, 251, 17, 85, 139, 234, 20, 84, 4, 224, 27, 16, 65, 82, 65, + 66, 73, 67, 32, 76, 69, 84, 84, 69, 82, 32, 84, 65, 155, 198, 39, 86, 18, + 36, 3, 69, 69, 77, 215, 159, 39, 65, 17, 11, 32, 14, 64, 5, 87, 73, 84, + 72, 32, 222, 127, 70, 202, 43, 73, 211, 9, 77, 6, 158, 18, 84, 239, 169, + 35, 68, 62, 38, 71, 26, 89, 17, 3, 79, 79, 78, 21, 22, 79, 183, 122, 32, + 10, 175, 27, 69, 33, 11, 32, 30, 92, 5, 71, 72, 85, 78, 78, 16, 5, 87, + 73, 84, 72, 32, 242, 125, 70, 202, 43, 73, 211, 9, 77, 6, 187, 34, 65, + 16, 136, 1, 6, 83, 77, 65, 76, 76, 32, 38, 84, 220, 24, 8, 73, 78, 86, + 69, 82, 84, 69, 68, 132, 139, 24, 4, 82, 73, 78, 71, 223, 230, 2, 68, 4, + 170, 239, 32, 84, 131, 238, 6, 86, 4, 250, 137, 7, 72, 195, 161, 30, 87, + 25, 22, 32, 187, 24, 69, 12, 88, 11, 87, 73, 84, 72, 32, 83, 77, 65, 76, + 76, 32, 170, 123, 70, 202, 43, 73, 211, 9, 77, 4, 26, 77, 215, 219, 39, + 86, 2, 205, 222, 38, 3, 69, 69, 77, 19, 11, 32, 16, 64, 5, 87, 73, 84, + 72, 32, 158, 122, 70, 202, 43, 73, 211, 9, 77, 8, 36, 4, 68, 79, 84, 32, + 187, 12, 84, 6, 44, 5, 66, 69, 76, 79, 87, 163, 221, 38, 65, 5, 141, 223, + 14, 6, 32, 65, 78, 68, 32, 78, 52, 112, 2, 69, 72, 224, 28, 3, 82, 69, + 72, 156, 3, 4, 78, 79, 79, 78, 149, 130, 1, 7, 79, 72, 73, 78, 71, 89, + 65, 35, 11, 32, 32, 52, 5, 87, 73, 84, 72, 32, 226, 119, 70, 231, 52, 73, + 28, 134, 1, 83, 96, 2, 84, 87, 234, 5, 73, 174, 23, 72, 194, 197, 11, 70, + 128, 138, 7, 5, 68, 79, 84, 32, 66, 234, 39, 76, 131, 241, 19, 82, 10, + 44, 5, 77, 65, 76, 76, 32, 131, 167, 39, 84, 8, 198, 6, 65, 166, 246, 6, + 78, 151, 211, 16, 86, 4, 37, 7, 79, 32, 68, 79, 84, 83, 32, 4, 166, 8, + 86, 135, 209, 38, 65, 60, 184, 1, 2, 65, 68, 120, 3, 69, 69, 78, 136, 6, + 4, 72, 69, 69, 78, 232, 160, 1, 4, 85, 80, 69, 82, 160, 181, 4, 7, 84, + 82, 65, 73, 71, 72, 84, 133, 246, 32, 6, 87, 65, 83, 72, 32, 75, 17, 11, + 32, 14, 68, 6, 87, 73, 84, 72, 32, 84, 162, 115, 70, 202, 43, 73, 211, 9, + 77, 6, 178, 166, 33, 72, 235, 251, 3, 87, 27, 11, 32, 24, 64, 5, 87, 73, + 84, 72, 32, 174, 114, 70, 202, 43, 73, 211, 9, 77, 16, 232, 1, 3, 68, 79, + 84, 50, 73, 48, 7, 83, 77, 65, 76, 76, 32, 65, 110, 84, 210, 219, 11, 70, + 241, 159, 5, 31, 69, 88, 84, 69, 78, 68, 69, 68, 32, 65, 82, 65, 66, 73, + 67, 45, 73, 78, 68, 73, 67, 32, 68, 73, 71, 73, 84, 32, 70, 79, 85, 2, + 193, 231, 18, 7, 32, 66, 69, 76, 79, 87, 32, 2, 213, 207, 17, 7, 78, 86, + 69, 82, 84, 69, 68, 2, 85, 19, 82, 65, 66, 73, 67, 32, 76, 69, 84, 84, + 69, 82, 32, 84, 65, 72, 32, 65, 78, 2, 147, 147, 23, 68, 6, 96, 11, 72, + 82, 69, 69, 32, 68, 79, 84, 83, 32, 66, 105, 9, 87, 79, 32, 68, 79, 84, + 83, 32, 86, 4, 25, 4, 69, 76, 79, 87, 5, 11, 32, 2, 21, 3, 65, 78, 68, 2, + 17, 2, 32, 84, 2, 247, 250, 6, 72, 2, 169, 216, 11, 8, 69, 82, 84, 73, + 67, 65, 76, 76, 13, 11, 32, 10, 46, 87, 186, 108, 70, 202, 43, 73, 211, + 9, 77, 2, 177, 250, 26, 5, 73, 84, 72, 32, 68, 122, 92, 2, 65, 72, 136, + 2, 4, 67, 72, 69, 72, 124, 2, 69, 72, 150, 3, 72, 97, 3, 84, 69, 72, 21, + 11, 32, 18, 64, 5, 87, 73, 84, 72, 32, 226, 106, 70, 202, 43, 73, 211, 9, + 77, 10, 26, 84, 195, 248, 26, 68, 8, 26, 87, 191, 157, 33, 72, 4, 37, 7, + 79, 32, 68, 79, 84, 83, 32, 4, 48, 6, 86, 69, 82, 84, 73, 67, 171, 205, + 38, 65, 2, 249, 152, 37, 4, 65, 76, 76, 89, 25, 22, 32, 199, 5, 69, 12, + 64, 5, 87, 73, 84, 72, 32, 206, 104, 70, 202, 43, 73, 211, 9, 77, 4, 138, + 14, 83, 191, 186, 38, 68, 37, 22, 32, 203, 4, 69, 24, 62, 77, 116, 5, 87, + 73, 84, 72, 32, 226, 102, 70, 203, 43, 73, 10, 48, 6, 65, 82, 66, 85, 84, + 65, 199, 156, 1, 69, 9, 11, 32, 6, 146, 103, 70, 230, 52, 73, 137, 160, + 37, 2, 71, 79, 8, 104, 6, 83, 77, 65, 76, 76, 32, 56, 12, 84, 72, 82, 69, + 69, 32, 68, 79, 84, 83, 32, 65, 131, 245, 38, 82, 4, 32, 2, 84, 69, 155, + 198, 39, 86, 2, 147, 201, 38, 72, 2, 205, 129, 28, 4, 66, 79, 86, 69, 20, + 42, 65, 16, 3, 73, 78, 32, 147, 1, 69, 6, 167, 9, 76, 4, 198, 207, 32, + 89, 183, 203, 3, 78, 23, 18, 32, 91, 69, 10, 60, 4, 87, 73, 84, 72, 230, + 99, 70, 202, 43, 73, 211, 9, 77, 2, 161, 9, 2, 32, 83, 10, 163, 11, 72, + 15, 158, 1, 32, 181, 92, 33, 73, 71, 72, 85, 82, 32, 75, 65, 90, 65, 75, + 72, 32, 75, 73, 82, 71, 72, 73, 90, 32, 65, 76, 69, 70, 32, 77, 65, 75, + 83, 85, 82, 65, 8, 96, 16, 87, 73, 84, 72, 32, 72, 65, 77, 90, 65, 32, + 65, 66, 79, 86, 69, 186, 97, 70, 231, 52, 73, 5, 155, 87, 32, 17, 254, 8, + 72, 147, 88, 32, 25, 11, 32, 22, 52, 5, 87, 73, 84, 72, 32, 202, 96, 70, + 231, 52, 73, 18, 80, 4, 68, 79, 84, 32, 162, 2, 69, 182, 1, 72, 226, 193, + 14, 84, 135, 170, 24, 82, 4, 204, 180, 31, 3, 87, 73, 84, 131, 143, 7, + 65, 56, 28, 2, 69, 72, 227, 3, 85, 51, 11, 32, 48, 100, 6, 66, 65, 82, + 82, 69, 69, 252, 2, 5, 87, 73, 84, 72, 32, 182, 91, 70, 202, 43, 73, 211, + 9, 77, 17, 11, 32, 14, 52, 5, 87, 73, 84, 72, 32, 238, 93, 70, 231, 52, + 73, 10, 22, 69, 183, 1, 72, 4, 121, 28, 88, 84, 69, 78, 68, 69, 68, 32, + 65, 82, 65, 66, 73, 67, 45, 73, 78, 68, 73, 67, 32, 68, 73, 71, 73, 84, + 32, 84, 4, 148, 150, 36, 3, 72, 82, 69, 133, 170, 2, 2, 87, 79, 6, 21, 3, + 65, 77, 90, 6, 11, 65, 6, 17, 2, 32, 65, 6, 21, 3, 66, 79, 86, 6, 11, 69, + 7, 171, 91, 32, 24, 96, 10, 72, 65, 77, 90, 65, 32, 65, 66, 79, 86, 18, + 83, 38, 84, 165, 137, 37, 4, 70, 79, 85, 82, 10, 167, 2, 69, 2, 133, 186, + 17, 4, 77, 65, 76, 76, 10, 108, 18, 87, 79, 32, 68, 79, 84, 83, 32, 66, + 69, 76, 79, 87, 32, 65, 78, 68, 32, 130, 136, 37, 72, 167, 82, 65, 6, 70, + 72, 168, 223, 6, 7, 83, 77, 65, 76, 76, 32, 78, 187, 217, 31, 68, 2, 237, + 141, 34, 3, 65, 77, 90, 18, 26, 72, 17, 2, 73, 78, 11, 223, 83, 32, 9, + 11, 32, 6, 138, 88, 70, 230, 52, 73, 177, 11, 13, 87, 73, 84, 72, 32, 73, + 78, 86, 69, 82, 84, 69, 68, 158, 8, 166, 5, 65, 178, 7, 66, 176, 2, 2, + 68, 65, 184, 1, 9, 70, 69, 72, 32, 87, 73, 84, 72, 32, 72, 11, 71, 72, + 65, 73, 78, 32, 87, 73, 84, 72, 32, 154, 1, 72, 150, 4, 74, 234, 2, 75, + 216, 2, 9, 76, 65, 77, 32, 87, 73, 84, 72, 32, 226, 4, 77, 154, 4, 78, + 202, 3, 81, 146, 4, 82, 186, 5, 83, 186, 17, 84, 196, 9, 7, 87, 65, 83, + 65, 76, 76, 65, 40, 9, 89, 69, 72, 32, 87, 73, 84, 72, 32, 216, 3, 53, + 85, 73, 71, 72, 85, 82, 32, 75, 73, 82, 71, 72, 73, 90, 32, 89, 69, 72, + 32, 87, 73, 84, 72, 32, 72, 65, 77, 90, 65, 32, 65, 66, 79, 86, 69, 32, + 87, 73, 84, 72, 32, 65, 76, 69, 70, 32, 77, 65, 75, 83, 85, 82, 65, 205, + 6, 14, 90, 65, 72, 32, 87, 73, 84, 72, 32, 77, 69, 69, 77, 32, 66, 192, + 1, 8, 73, 78, 32, 87, 73, 84, 72, 32, 68, 13, 74, 74, 65, 76, 32, 65, 76, + 76, 65, 65, 72, 85, 32, 146, 1, 76, 196, 68, 4, 75, 66, 65, 82, 189, 224, + 36, 8, 90, 90, 65, 32, 87, 65, 32, 74, 28, 236, 23, 4, 77, 69, 69, 77, + 130, 22, 74, 154, 25, 65, 255, 8, 89, 4, 54, 70, 1, 9, 84, 65, 65, 65, + 76, 65, 65, 32, 70, 2, 133, 207, 28, 17, 65, 82, 65, 74, 65, 72, 85, 32, + 65, 83, 72, 45, 83, 72, 65, 82, 69, 30, 56, 3, 65, 89, 72, 156, 2, 3, 69, + 70, 32, 207, 16, 76, 20, 30, 73, 102, 65, 135, 67, 69, 14, 24, 2, 32, 65, + 55, 77, 6, 110, 83, 205, 191, 32, 6, 82, 45, 82, 65, 72, 77, 8, 18, 65, + 23, 32, 4, 17, 2, 65, 32, 4, 17, 2, 65, 83, 4, 33, 6, 45, 83, 65, 76, 65, + 65, 4, 160, 168, 33, 10, 84, 85, 32, 87, 65, 83, 45, 83, 65, 76, 135, + 133, 6, 77, 8, 236, 75, 29, 77, 65, 75, 83, 85, 82, 65, 32, 87, 73, 84, + 72, 32, 83, 85, 80, 69, 82, 83, 67, 82, 73, 80, 84, 32, 65, 76, 69, 70, + 1, 13, 87, 73, 84, 72, 32, 70, 65, 84, 72, 65, 84, 65, 78, 44, 160, 1, 8, + 69, 72, 32, 87, 73, 84, 72, 32, 165, 243, 25, 25, 73, 83, 77, 73, 76, 76, + 65, 72, 32, 65, 82, 45, 82, 65, 72, 77, 65, 78, 32, 65, 82, 45, 82, 65, + 72, 42, 98, 72, 24, 3, 75, 72, 65, 254, 62, 65, 250, 3, 74, 78, 77, 86, + 78, 78, 90, 242, 2, 82, 43, 89, 10, 22, 65, 195, 66, 69, 6, 155, 69, 72, + 38, 100, 7, 68, 32, 87, 73, 84, 72, 32, 185, 28, 13, 65, 77, 65, 84, 32, + 66, 65, 82, 65, 75, 65, 65, 84, 36, 154, 8, 72, 250, 7, 75, 242, 45, 65, + 250, 3, 74, 2, 77, 134, 5, 82, 3, 89, 30, 194, 15, 75, 242, 45, 65, 250, 3, 74, 146, 2, 77, 110, 72, 139, 2, 89, 22, 64, 5, 77, 69, 69, 77, 32, - 250, 50, 65, 250, 3, 74, 135, 5, 89, 10, 40, 5, 87, 73, 84, 72, 32, 243, - 103, 73, 6, 174, 21, 77, 170, 25, 65, 203, 12, 89, 40, 80, 8, 65, 72, 32, - 87, 73, 84, 72, 32, 69, 8, 69, 72, 32, 87, 73, 84, 72, 32, 22, 236, 3, 4, - 77, 69, 69, 77, 226, 45, 65, 138, 6, 74, 247, 2, 89, 18, 164, 1, 5, 77, - 69, 69, 77, 32, 196, 11, 6, 65, 76, 69, 70, 32, 77, 150, 2, 89, 134, 38, - 74, 53, 16, 83, 85, 80, 69, 82, 83, 67, 82, 73, 80, 84, 32, 65, 76, 69, - 70, 8, 40, 5, 87, 73, 84, 72, 32, 223, 100, 73, 4, 254, 43, 74, 3, 77, - 30, 104, 9, 69, 69, 77, 32, 87, 73, 84, 72, 32, 205, 129, 4, 11, 65, 76, - 76, 65, 74, 65, 76, 65, 76, 79, 85, 28, 62, 72, 60, 5, 77, 69, 69, 77, - 32, 186, 45, 65, 255, 8, 89, 8, 17, 2, 65, 72, 8, 11, 32, 8, 178, 29, 87, - 199, 69, 73, 12, 40, 5, 87, 73, 84, 72, 32, 179, 98, 73, 8, 166, 37, 72, - 242, 3, 65, 203, 12, 89, 64, 84, 8, 65, 70, 32, 87, 73, 84, 72, 32, 93, - 9, 72, 65, 72, 32, 87, 73, 84, 72, 32, 46, 168, 46, 2, 65, 76, 218, 1, - 74, 96, 2, 76, 65, 146, 2, 75, 14, 72, 30, 77, 239, 1, 89, 18, 54, 72, - 250, 42, 65, 250, 3, 74, 2, 77, 135, 5, 89, 2, 247, 8, 65, 74, 116, 5, - 65, 76, 69, 70, 32, 210, 1, 72, 124, 5, 74, 69, 69, 77, 32, 78, 75, 28, - 5, 77, 69, 69, 77, 32, 187, 47, 89, 20, 64, 5, 87, 73, 84, 72, 32, 138, - 44, 77, 222, 6, 70, 255, 52, 73, 12, 68, 6, 72, 65, 77, 90, 65, 32, 41, - 7, 77, 65, 68, 68, 65, 32, 65, 8, 38, 65, 249, 45, 4, 66, 69, 76, 79, 4, - 229, 45, 3, 66, 79, 86, 14, 28, 2, 65, 72, 163, 44, 69, 12, 11, 32, 12, - 40, 5, 87, 73, 84, 72, 32, 239, 92, 73, 8, 210, 35, 65, 130, 12, 77, 75, - 89, 14, 40, 5, 87, 73, 84, 72, 32, 159, 92, 73, 10, 130, 47, 74, 2, 77, - 75, 89, 8, 129, 15, 3, 72, 65, 72, 14, 150, 14, 87, 222, 33, 70, 238, 43, - 73, 199, 9, 77, 48, 84, 9, 69, 69, 77, 32, 87, 73, 84, 72, 32, 193, 36, - 7, 79, 72, 65, 77, 77, 65, 68, 46, 116, 5, 65, 76, 69, 70, 32, 66, 72, 0, - 2, 75, 72, 104, 5, 74, 69, 69, 77, 32, 92, 5, 77, 69, 69, 77, 32, 43, 89, - 4, 22, 77, 219, 45, 70, 2, 173, 35, 6, 65, 75, 83, 85, 82, 65, 10, 21, 3, - 65, 72, 32, 10, 40, 5, 87, 73, 84, 72, 32, 219, 88, 73, 6, 250, 31, 74, - 2, 77, 143, 12, 89, 12, 40, 5, 87, 73, 84, 72, 32, 139, 88, 73, 8, 170, - 31, 77, 194, 7, 75, 14, 72, 195, 4, 89, 8, 246, 40, 87, 246, 2, 70, 239, - 43, 73, 2, 11, 69, 2, 143, 33, 72, 60, 134, 1, 72, 28, 5, 74, 69, 69, 77, - 32, 92, 5, 77, 69, 69, 77, 32, 242, 31, 65, 154, 5, 78, 78, 90, 134, 1, - 75, 238, 1, 82, 43, 89, 14, 146, 27, 65, 155, 9, 69, 16, 40, 5, 87, 73, - 84, 72, 32, 199, 85, 73, 12, 186, 24, 72, 242, 3, 65, 130, 12, 77, 75, - 89, 12, 206, 15, 87, 218, 25, 70, 238, 43, 73, 199, 9, 77, 28, 70, 65, - 209, 138, 32, 11, 85, 68, 68, 73, 83, 65, 32, 83, 73, 82, 82, 26, 64, 7, - 70, 32, 87, 73, 84, 72, 32, 193, 8, 4, 76, 65, 32, 85, 24, 64, 5, 77, 69, - 69, 77, 32, 238, 29, 65, 246, 6, 72, 139, 2, 89, 12, 40, 5, 87, 73, 84, - 72, 32, 231, 82, 73, 8, 34, 77, 186, 21, 72, 187, 16, 89, 2, 129, 38, 3, - 69, 69, 77, 18, 30, 65, 129, 26, 2, 69, 72, 16, 128, 1, 14, 68, 73, 32, - 65, 76, 76, 65, 65, 72, 85, 32, 65, 78, 72, 76, 6, 72, 73, 77, 65, 72, - 85, 161, 26, 4, 83, 79, 85, 76, 11, 26, 85, 231, 228, 38, 65, 6, 26, 77, - 255, 170, 37, 78, 5, 199, 228, 38, 65, 4, 26, 32, 1, 2, 77, 32, 2, 205, - 134, 32, 4, 65, 76, 76, 65, 190, 1, 138, 1, 65, 192, 6, 9, 69, 69, 78, - 32, 87, 73, 84, 72, 32, 250, 3, 72, 233, 5, 13, 85, 66, 72, 65, 65, 78, - 65, 72, 85, 32, 87, 65, 32, 46, 48, 7, 68, 32, 87, 73, 84, 72, 32, 251, - 1, 76, 32, 76, 4, 72, 65, 72, 32, 82, 77, 238, 23, 65, 138, 4, 75, 246, - 4, 82, 3, 89, 10, 22, 87, 203, 77, 73, 6, 25, 4, 73, 84, 72, 32, 6, 162, - 16, 72, 187, 16, 89, 8, 21, 3, 69, 69, 77, 8, 11, 32, 8, 208, 31, 6, 87, - 73, 84, 72, 32, 77, 155, 45, 73, 14, 26, 65, 77, 2, 76, 65, 4, 134, 22, - 77, 129, 218, 36, 11, 65, 77, 85, 72, 85, 32, 65, 76, 65, 89, 78, 10, 34, - 32, 133, 1, 3, 76, 76, 65, 4, 22, 85, 167, 84, 73, 2, 217, 8, 23, 83, 69, - 68, 32, 65, 83, 32, 75, 79, 82, 65, 78, 73, 67, 32, 83, 84, 79, 80, 32, - 83, 73, 71, 6, 98, 72, 237, 222, 38, 18, 65, 72, 85, 32, 65, 76, 65, 89, - 72, 73, 32, 87, 65, 45, 65, 65, 76, 73, 4, 228, 63, 12, 79, 85, 32, 65, - 76, 65, 89, 72, 69, 32, 87, 65, 1, 22, 85, 32, 65, 76, 65, 89, 72, 73, - 32, 87, 65, 65, 65, 76, 73, 72, 69, 69, 32, 87, 65, 45, 60, 130, 1, 72, - 100, 5, 74, 69, 69, 77, 32, 84, 5, 75, 72, 65, 72, 32, 92, 5, 77, 69, 69, - 77, 32, 234, 15, 65, 254, 8, 82, 3, 89, 12, 32, 3, 65, 72, 32, 159, 21, - 69, 8, 156, 14, 6, 87, 73, 84, 72, 32, 74, 222, 56, 73, 199, 9, 77, 10, - 52, 5, 87, 73, 84, 72, 32, 134, 70, 73, 199, 9, 77, 4, 234, 12, 65, 139, - 8, 72, 10, 34, 87, 198, 69, 73, 199, 9, 77, 4, 25, 4, 73, 84, 72, 32, 4, - 142, 12, 65, 203, 12, 89, 16, 52, 5, 87, 73, 84, 72, 32, 214, 68, 73, - 199, 9, 77, 10, 202, 7, 72, 174, 4, 74, 199, 11, 77, 82, 96, 10, 65, 68, - 68, 65, 32, 87, 73, 84, 72, 32, 161, 1, 9, 69, 69, 78, 32, 87, 73, 84, - 72, 32, 18, 96, 4, 68, 65, 77, 77, 0, 4, 75, 65, 83, 82, 138, 11, 83, - 245, 41, 6, 70, 65, 84, 72, 65, 32, 6, 11, 65, 6, 28, 2, 84, 65, 211, 52, - 32, 2, 163, 12, 78, 64, 130, 1, 72, 36, 5, 74, 69, 69, 77, 32, 52, 5, 77, - 69, 69, 77, 32, 170, 11, 65, 228, 4, 4, 75, 72, 65, 72, 154, 4, 82, 3, - 89, 18, 194, 15, 69, 229, 3, 2, 65, 72, 10, 158, 18, 87, 246, 2, 70, 238, - 43, 73, 199, 9, 77, 16, 64, 5, 87, 73, 84, 72, 32, 158, 20, 70, 238, 43, - 73, 199, 9, 77, 8, 252, 2, 2, 75, 72, 243, 15, 77, 122, 66, 65, 176, 2, - 8, 69, 72, 32, 87, 73, 84, 72, 32, 179, 4, 72, 28, 88, 11, 66, 65, 65, - 82, 65, 75, 65, 32, 87, 65, 45, 29, 7, 72, 32, 87, 73, 84, 72, 32, 2, - 245, 133, 28, 2, 84, 65, 26, 64, 5, 77, 69, 69, 77, 32, 194, 8, 65, 246, - 6, 72, 139, 2, 89, 14, 52, 5, 87, 73, 84, 72, 32, 174, 61, 73, 199, 9, - 77, 8, 34, 72, 174, 4, 77, 143, 12, 89, 4, 133, 16, 2, 65, 72, 66, 138, - 1, 72, 108, 3, 75, 72, 65, 12, 4, 74, 69, 69, 77, 92, 5, 77, 69, 69, 77, - 32, 238, 4, 65, 154, 5, 78, 78, 90, 242, 2, 82, 43, 89, 14, 32, 3, 65, - 72, 32, 227, 9, 69, 10, 40, 5, 87, 73, 84, 72, 32, 151, 59, 73, 6, 182, - 2, 77, 199, 11, 74, 10, 11, 72, 10, 11, 32, 10, 40, 5, 87, 73, 84, 72, - 32, 183, 58, 73, 6, 154, 1, 65, 62, 77, 143, 12, 89, 18, 64, 5, 87, 73, - 84, 72, 32, 226, 13, 70, 238, 43, 73, 199, 9, 77, 10, 50, 65, 62, 74, - 194, 7, 75, 14, 72, 195, 4, 89, 2, 217, 12, 11, 76, 69, 70, 32, 77, 65, - 75, 83, 85, 82, 65, 2, 225, 7, 3, 69, 69, 77, 28, 56, 2, 65, 76, 117, 8, - 69, 72, 32, 87, 73, 84, 72, 32, 2, 37, 7, 32, 87, 73, 84, 72, 32, 83, 2, - 197, 1, 15, 85, 80, 69, 82, 83, 67, 82, 73, 80, 84, 32, 65, 76, 69, 70, - 26, 108, 3, 74, 69, 69, 126, 65, 198, 4, 77, 86, 78, 78, 90, 242, 2, 82, - 42, 89, 237, 53, 5, 72, 69, 72, 32, 77, 2, 11, 77, 2, 11, 32, 2, 151, 63, - 73, 114, 82, 65, 38, 72, 246, 4, 78, 78, 90, 38, 74, 98, 75, 42, 77, 198, - 1, 82, 43, 89, 4, 217, 2, 5, 76, 69, 70, 32, 77, 76, 22, 65, 139, 3, 69, - 72, 80, 15, 77, 90, 65, 32, 65, 66, 79, 86, 69, 32, 87, 73, 84, 72, 32, - 147, 5, 72, 66, 118, 65, 126, 69, 42, 72, 78, 74, 18, 75, 62, 77, 86, 78, - 22, 79, 16, 2, 87, 65, 18, 89, 26, 90, 242, 2, 82, 67, 85, 12, 22, 76, - 247, 6, 69, 8, 21, 3, 69, 70, 32, 8, 34, 77, 222, 6, 70, 255, 52, 73, 4, - 181, 6, 6, 65, 75, 83, 85, 82, 65, 6, 11, 32, 6, 166, 6, 70, 239, 43, 73, - 8, 22, 69, 191, 3, 65, 4, 11, 72, 4, 11, 32, 4, 230, 4, 73, 167, 54, 77, - 4, 131, 49, 69, 2, 11, 72, 2, 11, 65, 2, 11, 72, 2, 149, 4, 2, 32, 73, 8, - 17, 2, 69, 69, 8, 11, 77, 8, 11, 32, 8, 198, 4, 70, 238, 43, 73, 199, 9, - 77, 2, 93, 2, 79, 79, 4, 231, 3, 69, 4, 215, 3, 87, 8, 186, 3, 69, 15, - 85, 2, 17, 2, 65, 73, 2, 239, 2, 78, 6, 21, 3, 69, 69, 77, 6, 11, 32, 6, - 22, 87, 227, 46, 73, 2, 141, 2, 5, 73, 84, 72, 32, 89, 4, 11, 72, 4, 11, - 65, 4, 143, 46, 72, 14, 21, 3, 69, 69, 77, 14, 11, 32, 14, 64, 5, 87, 73, - 84, 72, 32, 194, 1, 70, 238, 43, 73, 199, 9, 77, 6, 18, 77, 75, 89, 4, - 21, 3, 69, 69, 77, 4, 11, 32, 4, 18, 73, 119, 70, 2, 247, 44, 78, 2, 17, - 2, 69, 72, 2, 77, 2, 32, 70, 4, 11, 69, 4, 11, 72, 4, 11, 32, 4, 22, 70, - 255, 52, 73, 2, 209, 53, 2, 73, 78, 6, 210, 43, 73, 199, 9, 77, 166, 2, - 92, 3, 68, 68, 65, 52, 3, 82, 75, 32, 109, 11, 84, 72, 69, 77, 65, 84, - 73, 67, 65, 76, 32, 4, 152, 133, 37, 5, 32, 87, 65, 65, 74, 239, 62, 72, - 4, 58, 78, 1, 10, 83, 73, 68, 69, 87, 65, 89, 83, 32, 78, 2, 173, 137, - 35, 7, 79, 79, 78, 32, 71, 72, 85, 158, 2, 174, 2, 68, 140, 3, 8, 73, 78, - 73, 84, 73, 65, 76, 32, 222, 1, 76, 132, 2, 9, 79, 80, 69, 82, 65, 84, - 79, 82, 32, 166, 1, 83, 142, 3, 84, 190, 162, 24, 65, 102, 75, 110, 90, - 134, 107, 74, 2, 77, 178, 187, 6, 66, 2, 70, 2, 82, 2, 89, 222, 7, 72, - 190, 59, 71, 242, 253, 2, 78, 166, 165, 2, 81, 135, 3, 87, 62, 26, 79, - 235, 186, 37, 65, 58, 88, 6, 84, 76, 69, 83, 83, 32, 61, 12, 85, 66, 76, - 69, 45, 83, 84, 82, 85, 67, 75, 32, 8, 226, 212, 31, 66, 2, 70, 138, 193, - 3, 78, 167, 165, 2, 81, 50, 178, 12, 83, 210, 20, 75, 170, 140, 24, 84, - 74, 90, 134, 107, 74, 2, 77, 178, 187, 6, 66, 2, 70, 2, 82, 2, 89, 222, - 7, 72, 190, 59, 71, 242, 253, 2, 78, 218, 235, 1, 76, 190, 56, 68, 146, - 1, 81, 222, 1, 65, 171, 1, 87, 40, 182, 1, 84, 166, 9, 83, 186, 160, 24, - 72, 30, 75, 242, 107, 74, 2, 77, 178, 187, 6, 66, 2, 70, 2, 89, 154, 67, - 71, 242, 253, 2, 78, 218, 235, 1, 76, 206, 57, 81, 222, 1, 65, 171, 57, - 68, 4, 150, 209, 31, 72, 207, 230, 6, 69, 56, 48, 6, 79, 79, 80, 69, 68, - 32, 199, 167, 38, 65, 54, 202, 8, 83, 210, 20, 75, 162, 139, 24, 65, 74, - 72, 66, 84, 74, 90, 134, 107, 74, 2, 77, 178, 187, 6, 66, 2, 70, 2, 82, - 2, 89, 154, 67, 71, 242, 253, 2, 78, 218, 235, 1, 76, 190, 56, 68, 146, - 1, 81, 135, 3, 87, 4, 168, 128, 29, 23, 77, 69, 69, 77, 32, 87, 73, 84, - 72, 32, 72, 65, 72, 32, 87, 73, 84, 72, 32, 84, 65, 84, 87, 221, 162, 8, - 9, 72, 65, 72, 32, 87, 73, 84, 72, 32, 52, 84, 9, 84, 82, 69, 84, 67, 72, - 69, 68, 32, 222, 224, 36, 72, 14, 69, 203, 141, 1, 65, 46, 178, 1, 68, - 86, 84, 250, 2, 83, 186, 160, 24, 72, 30, 75, 242, 107, 74, 2, 77, 178, - 187, 6, 66, 2, 70, 2, 89, 222, 7, 90, 190, 59, 71, 242, 253, 2, 78, 166, - 165, 2, 81, 223, 1, 65, 6, 52, 7, 79, 84, 76, 69, 83, 83, 32, 199, 236, - 37, 65, 4, 134, 203, 31, 66, 3, 70, 6, 234, 202, 31, 72, 206, 230, 6, 65, - 3, 69, 38, 42, 65, 190, 163, 24, 72, 167, 141, 14, 69, 32, 44, 5, 73, 76, - 69, 68, 32, 195, 177, 38, 72, 30, 146, 1, 68, 94, 83, 210, 20, 75, 246, - 247, 24, 74, 178, 187, 6, 89, 222, 7, 72, 190, 59, 71, 242, 253, 2, 78, - 218, 235, 1, 76, 206, 57, 81, 223, 1, 65, 6, 52, 7, 79, 84, 76, 69, 83, - 83, 32, 215, 233, 37, 65, 4, 158, 137, 35, 78, 167, 165, 2, 81, 6, 218, - 219, 36, 72, 14, 69, 203, 141, 1, 65, 4, 242, 197, 18, 77, 203, 226, 18, - 83, 6, 228, 235, 24, 3, 75, 65, 83, 12, 4, 68, 65, 77, 77, 1, 4, 70, 65, - 84, 72, 12, 118, 69, 38, 79, 172, 197, 12, 11, 76, 65, 67, 69, 32, 79, - 70, 32, 83, 65, 74, 165, 254, 5, 6, 73, 65, 83, 84, 82, 69, 4, 182, 157, - 32, 82, 235, 218, 5, 80, 4, 252, 181, 12, 8, 69, 84, 73, 67, 32, 86, 69, - 82, 209, 141, 6, 3, 85, 78, 68, 14, 238, 1, 65, 96, 5, 69, 86, 69, 82, - 83, 36, 15, 73, 71, 72, 84, 32, 65, 82, 82, 79, 87, 72, 69, 65, 68, 32, - 225, 158, 23, 28, 79, 85, 78, 68, 69, 68, 32, 72, 73, 71, 72, 32, 83, 84, - 79, 80, 32, 87, 73, 84, 72, 32, 70, 73, 76, 76, 69, 68, 4, 40, 4, 73, 83, - 69, 68, 171, 170, 38, 89, 2, 17, 2, 32, 82, 2, 149, 222, 36, 3, 79, 85, - 78, 2, 181, 214, 21, 4, 69, 68, 32, 68, 6, 26, 65, 167, 139, 36, 66, 4, - 157, 133, 37, 3, 66, 79, 86, 202, 1, 238, 1, 69, 244, 2, 5, 72, 65, 68, - 68, 65, 36, 4, 73, 71, 78, 32, 240, 4, 5, 77, 65, 76, 76, 32, 246, 15, - 85, 240, 2, 6, 89, 77, 66, 79, 76, 32, 209, 208, 36, 18, 84, 65, 82, 84, - 32, 79, 70, 32, 82, 85, 66, 32, 69, 76, 32, 72, 73, 90, 20, 52, 7, 81, - 85, 69, 78, 67, 69, 32, 187, 219, 34, 77, 18, 184, 1, 26, 89, 69, 72, 32, - 87, 73, 84, 72, 32, 72, 65, 77, 90, 65, 32, 65, 66, 79, 86, 69, 32, 87, - 73, 84, 72, 32, 209, 189, 31, 13, 78, 79, 79, 78, 32, 87, 73, 84, 72, 32, - 75, 69, 72, 16, 70, 65, 186, 166, 37, 87, 198, 89, 89, 150, 14, 79, 214, - 22, 69, 3, 85, 6, 36, 3, 76, 69, 70, 191, 164, 38, 69, 5, 159, 13, 32, 7, - 11, 32, 4, 246, 22, 73, 55, 77, 22, 132, 1, 2, 82, 65, 158, 1, 83, 188, - 1, 7, 65, 76, 65, 89, 72, 69, 32, 160, 15, 2, 77, 73, 209, 138, 34, 6, - 84, 65, 75, 72, 65, 76, 4, 132, 1, 13, 72, 77, 65, 84, 85, 76, 76, 65, - 72, 32, 65, 76, 65, 133, 180, 33, 13, 68, 73, 32, 65, 76, 76, 65, 72, 79, - 85, 32, 65, 78, 2, 235, 244, 37, 89, 12, 46, 65, 193, 1, 6, 73, 78, 68, - 72, 73, 32, 8, 136, 1, 18, 76, 76, 65, 76, 76, 65, 72, 79, 85, 32, 65, - 76, 65, 89, 72, 69, 32, 87, 170, 192, 31, 78, 194, 229, 4, 70, 217, 102, - 2, 77, 86, 2, 17, 2, 65, 83, 2, 189, 221, 15, 3, 83, 65, 76, 4, 152, 200, - 20, 12, 80, 79, 83, 84, 80, 79, 83, 73, 84, 73, 79, 78, 185, 136, 14, 2, - 65, 77, 106, 132, 1, 2, 70, 65, 32, 5, 72, 73, 71, 72, 32, 240, 11, 4, - 76, 79, 87, 32, 110, 75, 218, 189, 21, 68, 226, 235, 9, 89, 179, 233, 5, - 87, 4, 190, 3, 82, 147, 160, 36, 84, 74, 236, 2, 17, 68, 79, 84, 76, 69, - 83, 83, 32, 72, 69, 65, 68, 32, 79, 70, 32, 75, 22, 70, 102, 76, 142, 3, - 77, 116, 4, 78, 79, 79, 78, 18, 83, 78, 84, 38, 87, 196, 2, 3, 89, 69, - 72, 224, 175, 14, 18, 85, 80, 82, 73, 71, 72, 84, 32, 82, 69, 67, 84, 65, - 78, 71, 85, 76, 65, 236, 213, 8, 7, 82, 79, 85, 78, 68, 69, 68, 234, 126, - 90, 134, 107, 74, 222, 161, 12, 81, 223, 1, 65, 2, 255, 186, 31, 72, 4, - 24, 2, 65, 82, 31, 79, 2, 11, 83, 2, 175, 2, 73, 2, 253, 180, 26, 6, 79, - 84, 78, 79, 84, 69, 10, 60, 8, 73, 71, 65, 84, 85, 82, 69, 32, 213, 11, - 2, 65, 77, 8, 88, 10, 65, 76, 69, 70, 32, 87, 73, 84, 72, 32, 116, 3, 81, - 65, 70, 1, 3, 83, 65, 68, 4, 84, 8, 76, 65, 77, 32, 87, 73, 84, 72, 209, - 168, 36, 7, 89, 69, 72, 32, 66, 65, 82, 2, 181, 176, 31, 2, 32, 89, 2, - 89, 20, 32, 87, 73, 84, 72, 32, 76, 65, 77, 32, 87, 73, 84, 72, 32, 65, - 76, 69, 70, 32, 2, 165, 145, 22, 3, 77, 65, 75, 6, 26, 69, 191, 249, 15, - 65, 4, 17, 2, 69, 77, 4, 17, 2, 32, 73, 4, 22, 78, 135, 9, 83, 2, 193, 9, - 2, 73, 84, 5, 207, 4, 32, 6, 180, 155, 36, 7, 73, 71, 78, 32, 83, 65, 70, - 218, 38, 69, 203, 141, 1, 65, 4, 178, 196, 23, 72, 235, 207, 14, 65, 20, - 40, 4, 79, 82, 68, 32, 191, 150, 37, 65, 18, 74, 65, 172, 1, 2, 83, 65, - 172, 173, 25, 3, 87, 65, 81, 207, 227, 11, 81, 10, 192, 252, 6, 3, 76, - 45, 74, 244, 185, 18, 3, 82, 45, 82, 144, 226, 6, 7, 84, 72, 45, 84, 72, - 65, 76, 164, 250, 4, 5, 78, 45, 78, 73, 83, 161, 92, 5, 83, 45, 83, 65, - 74, 4, 150, 238, 37, 75, 207, 36, 72, 5, 193, 182, 10, 12, 32, 66, 65, - 82, 82, 69, 69, 32, 87, 73, 84, 72, 20, 68, 5, 78, 79, 79, 78, 32, 70, - 87, 234, 237, 24, 77, 155, 207, 11, 83, 2, 33, 6, 87, 73, 84, 72, 32, 75, - 2, 11, 65, 2, 199, 204, 37, 83, 14, 40, 4, 79, 82, 68, 32, 171, 146, 37, - 65, 12, 106, 73, 174, 242, 15, 77, 148, 175, 9, 3, 84, 65, 83, 200, 218, - 8, 2, 83, 65, 249, 218, 3, 3, 81, 65, 83, 4, 156, 148, 13, 2, 77, 65, - 253, 132, 19, 3, 83, 72, 77, 12, 138, 1, 66, 64, 3, 75, 85, 78, 205, 239, + 170, 60, 65, 250, 3, 74, 135, 5, 89, 10, 40, 5, 87, 73, 84, 72, 32, 255, + 112, 73, 6, 158, 25, 77, 234, 30, 65, 203, 12, 89, 48, 54, 65, 241, 1, 8, + 69, 72, 32, 87, 73, 84, 72, 32, 30, 68, 6, 70, 73, 90, 65, 72, 85, 101, + 7, 72, 32, 87, 73, 84, 72, 32, 8, 22, 77, 247, 28, 32, 4, 30, 32, 1, 3, + 65, 65, 32, 2, 201, 47, 7, 65, 76, 76, 65, 65, 72, 85, 22, 152, 4, 4, 77, + 69, 69, 77, 214, 53, 65, 138, 6, 74, 247, 2, 89, 18, 164, 1, 5, 77, 69, + 69, 77, 32, 204, 12, 6, 65, 76, 69, 70, 32, 77, 150, 2, 89, 158, 45, 74, + 53, 16, 83, 85, 80, 69, 82, 83, 67, 82, 73, 80, 84, 32, 65, 76, 69, 70, + 8, 40, 5, 87, 73, 84, 72, 32, 219, 108, 73, 4, 158, 52, 74, 3, 77, 32, + 68, 4, 65, 76, 76, 65, 81, 9, 69, 69, 77, 32, 87, 73, 84, 72, 32, 4, 168, + 145, 4, 7, 74, 65, 76, 65, 76, 79, 85, 241, 173, 24, 4, 32, 87, 65, 45, + 28, 62, 72, 60, 5, 77, 69, 69, 77, 32, 174, 53, 65, 255, 8, 89, 8, 17, 2, + 65, 72, 8, 11, 32, 8, 150, 37, 87, 179, 69, 73, 12, 40, 5, 87, 73, 84, + 72, 32, 131, 106, 73, 8, 154, 45, 72, 242, 3, 65, 203, 12, 89, 66, 58, + 65, 213, 1, 9, 72, 65, 72, 32, 87, 73, 84, 72, 32, 48, 116, 7, 70, 32, + 87, 73, 84, 72, 32, 221, 99, 17, 82, 82, 65, 77, 65, 32, 65, 76, 76, 65, + 65, 72, 85, 32, 87, 65, 74, 46, 192, 53, 2, 65, 76, 218, 1, 74, 96, 2, + 76, 65, 146, 2, 75, 14, 72, 30, 77, 239, 1, 89, 18, 54, 72, 146, 50, 65, + 250, 3, 74, 2, 77, 135, 5, 89, 2, 247, 8, 65, 74, 116, 5, 65, 76, 69, 70, + 32, 210, 1, 72, 124, 5, 74, 69, 69, 77, 32, 78, 75, 28, 5, 77, 69, 69, + 77, 32, 211, 54, 89, 20, 64, 5, 87, 73, 84, 72, 32, 162, 51, 77, 222, 6, + 70, 231, 52, 73, 12, 68, 6, 72, 65, 77, 90, 65, 32, 41, 7, 77, 65, 68, + 68, 65, 32, 65, 8, 38, 65, 145, 53, 4, 66, 69, 76, 79, 4, 253, 52, 3, 66, + 79, 86, 14, 28, 2, 65, 72, 187, 51, 69, 12, 11, 32, 12, 40, 5, 87, 73, + 84, 72, 32, 227, 99, 73, 8, 234, 42, 65, 130, 12, 77, 75, 89, 14, 40, 5, + 87, 73, 84, 72, 32, 147, 99, 73, 10, 154, 54, 74, 2, 77, 75, 89, 8, 237, + 20, 3, 72, 65, 72, 14, 130, 20, 87, 138, 35, 70, 202, 43, 73, 211, 9, 77, + 48, 84, 9, 69, 69, 77, 32, 87, 73, 84, 72, 32, 217, 43, 7, 79, 72, 65, + 77, 77, 65, 68, 46, 116, 5, 65, 76, 69, 70, 32, 66, 72, 0, 2, 75, 72, + 104, 5, 74, 69, 69, 77, 32, 92, 5, 77, 69, 69, 77, 32, 43, 89, 4, 22, 77, + 243, 52, 70, 2, 197, 42, 6, 65, 75, 83, 85, 82, 65, 10, 21, 3, 65, 72, + 32, 10, 40, 5, 87, 73, 84, 72, 32, 207, 95, 73, 6, 146, 39, 74, 2, 77, + 143, 12, 89, 12, 40, 5, 87, 73, 84, 72, 32, 255, 94, 73, 8, 194, 38, 77, + 194, 7, 75, 14, 72, 195, 4, 89, 8, 142, 48, 87, 246, 2, 70, 203, 43, 73, + 2, 11, 69, 2, 167, 40, 72, 62, 144, 1, 9, 79, 79, 78, 32, 87, 73, 84, 72, + 32, 153, 151, 13, 20, 65, 87, 87, 65, 82, 65, 32, 65, 76, 76, 65, 65, 72, + 85, 32, 77, 65, 82, 81, 65, 60, 134, 1, 72, 28, 5, 74, 69, 69, 77, 32, + 92, 5, 77, 69, 69, 77, 32, 246, 37, 65, 154, 5, 78, 78, 90, 134, 1, 75, + 238, 1, 82, 43, 89, 14, 150, 33, 65, 155, 9, 69, 16, 40, 5, 87, 73, 84, + 72, 32, 167, 91, 73, 12, 190, 30, 72, 242, 3, 65, 130, 12, 77, 75, 89, + 12, 194, 21, 87, 234, 25, 70, 202, 43, 73, 211, 9, 77, 36, 46, 65, 209, + 2, 6, 85, 68, 68, 73, 83, 65, 28, 156, 1, 7, 70, 32, 87, 73, 84, 72, 32, + 212, 12, 4, 76, 65, 32, 85, 157, 147, 32, 18, 68, 68, 65, 83, 65, 32, 65, + 76, 76, 65, 65, 72, 85, 32, 83, 73, 82, 82, 24, 64, 5, 77, 69, 69, 77, + 32, 174, 35, 65, 246, 6, 72, 139, 2, 89, 12, 40, 5, 87, 73, 84, 72, 32, + 131, 88, 73, 8, 34, 77, 250, 26, 72, 187, 16, 89, 2, 193, 43, 3, 69, 69, + 77, 8, 68, 5, 32, 83, 73, 82, 82, 45, 8, 84, 32, 65, 83, 82, 65, 65, 82, + 6, 220, 5, 3, 85, 72, 85, 139, 133, 39, 65, 2, 165, 224, 37, 2, 85, 72, + 44, 30, 65, 177, 30, 2, 69, 72, 42, 92, 11, 68, 73, 32, 65, 76, 76, 65, + 65, 72, 85, 32, 170, 1, 72, 153, 30, 4, 83, 79, 85, 76, 18, 72, 3, 65, + 78, 72, 61, 11, 84, 65, 65, 65, 76, 65, 65, 32, 65, 78, 72, 11, 26, 85, + 147, 135, 39, 65, 6, 186, 3, 77, 247, 199, 37, 78, 9, 142, 3, 85, 227, + 131, 39, 65, 22, 92, 5, 73, 77, 65, 72, 85, 149, 1, 13, 77, 65, 84, 85, + 32, 65, 76, 76, 65, 65, 72, 73, 32, 12, 44, 7, 32, 65, 76, 76, 65, 65, + 72, 19, 77, 5, 215, 20, 32, 8, 30, 32, 1, 3, 65, 65, 32, 4, 33, 6, 65, + 76, 76, 65, 65, 72, 5, 211, 18, 85, 10, 92, 5, 65, 76, 65, 89, 72, 165, + 133, 39, 12, 84, 65, 65, 65, 76, 65, 65, 32, 65, 76, 65, 89, 9, 26, 73, + 227, 131, 39, 65, 4, 11, 77, 5, 211, 131, 39, 65, 194, 1, 134, 1, 65, + 220, 7, 9, 69, 69, 78, 32, 87, 73, 84, 72, 32, 250, 3, 72, 201, 4, 12, + 85, 66, 72, 65, 65, 78, 65, 72, 85, 32, 87, 65, 50, 48, 7, 68, 32, 87, + 73, 84, 72, 32, 251, 1, 76, 32, 76, 4, 72, 65, 72, 32, 82, 77, 154, 25, + 65, 138, 4, 75, 246, 4, 82, 3, 89, 10, 22, 87, 211, 78, 73, 6, 25, 4, 73, + 84, 72, 32, 6, 206, 17, 72, 187, 16, 89, 8, 21, 3, 69, 69, 77, 8, 11, 32, + 8, 252, 32, 6, 87, 73, 84, 72, 32, 77, 247, 44, 73, 18, 26, 65, 77, 2, + 76, 65, 4, 178, 23, 77, 189, 245, 36, 11, 65, 77, 85, 72, 85, 32, 65, 76, + 65, 89, 78, 14, 34, 32, 133, 1, 3, 76, 76, 65, 4, 22, 85, 187, 85, 73, 2, + 245, 9, 23, 83, 69, 68, 32, 65, 83, 32, 75, 79, 82, 65, 78, 73, 67, 32, + 83, 84, 79, 80, 32, 83, 73, 71, 10, 36, 4, 65, 72, 85, 32, 147, 1, 72, 4, + 212, 2, 14, 84, 65, 65, 65, 76, 65, 65, 32, 65, 76, 65, 89, 72, 73, 161, + 252, 38, 14, 65, 76, 65, 89, 72, 73, 32, 87, 65, 45, 65, 65, 76, 73, 6, + 112, 11, 85, 32, 65, 76, 65, 89, 72, 73, 32, 87, 65, 205, 63, 12, 79, 85, + 32, 65, 76, 65, 89, 72, 69, 32, 87, 65, 4, 44, 7, 45, 65, 76, 65, 65, 32, + 65, 3, 65, 2, 33, 6, 65, 76, 73, 72, 69, 69, 2, 245, 62, 4, 32, 87, 65, + 45, 60, 130, 1, 72, 100, 5, 74, 69, 69, 77, 32, 84, 5, 75, 72, 65, 72, + 32, 92, 5, 77, 69, 69, 77, 32, 250, 15, 65, 254, 8, 82, 3, 89, 12, 32, 3, + 65, 72, 32, 175, 21, 69, 8, 172, 14, 6, 87, 73, 84, 72, 32, 74, 186, 56, + 73, 211, 9, 77, 10, 52, 5, 87, 73, 84, 72, 32, 242, 69, 73, 211, 9, 77, + 4, 250, 12, 65, 139, 8, 72, 10, 34, 87, 178, 69, 73, 211, 9, 77, 4, 25, + 4, 73, 84, 72, 32, 4, 158, 12, 65, 203, 12, 89, 16, 52, 5, 87, 73, 84, + 72, 32, 194, 68, 73, 211, 9, 77, 10, 218, 7, 72, 174, 4, 74, 199, 11, 77, + 82, 96, 10, 65, 68, 68, 65, 32, 87, 73, 84, 72, 32, 161, 1, 9, 69, 69, + 78, 32, 87, 73, 84, 72, 32, 18, 96, 4, 68, 65, 77, 77, 0, 4, 75, 65, 83, + 82, 154, 11, 83, 245, 41, 6, 70, 65, 84, 72, 65, 32, 6, 11, 65, 6, 28, 2, + 84, 65, 227, 52, 32, 2, 179, 12, 78, 64, 130, 1, 72, 36, 5, 74, 69, 69, + 77, 32, 52, 5, 77, 69, 69, 77, 32, 186, 11, 65, 228, 4, 4, 75, 72, 65, + 72, 154, 4, 82, 3, 89, 18, 210, 15, 69, 229, 3, 2, 65, 72, 10, 174, 18, + 87, 246, 2, 70, 202, 43, 73, 211, 9, 77, 16, 64, 5, 87, 73, 84, 72, 32, + 174, 20, 70, 202, 43, 73, 211, 9, 77, 8, 140, 3, 2, 75, 72, 243, 15, 77, + 2, 175, 1, 32, 122, 66, 65, 176, 2, 8, 69, 72, 32, 87, 73, 84, 72, 32, + 179, 4, 72, 28, 88, 11, 66, 65, 65, 82, 65, 75, 65, 32, 87, 65, 45, 29, + 7, 72, 32, 87, 73, 84, 72, 32, 2, 181, 145, 28, 2, 84, 65, 26, 64, 5, 77, + 69, 69, 77, 32, 194, 8, 65, 246, 6, 72, 139, 2, 89, 14, 52, 5, 87, 73, + 84, 72, 32, 138, 61, 73, 211, 9, 77, 8, 34, 72, 174, 4, 77, 143, 12, 89, + 4, 133, 16, 2, 65, 72, 66, 138, 1, 72, 108, 3, 75, 72, 65, 12, 4, 74, 69, + 69, 77, 92, 5, 77, 69, 69, 77, 32, 238, 4, 65, 154, 5, 78, 78, 90, 242, + 2, 82, 43, 89, 14, 32, 3, 65, 72, 32, 227, 9, 69, 10, 40, 5, 87, 73, 84, + 72, 32, 243, 58, 73, 6, 182, 2, 77, 199, 11, 74, 10, 11, 72, 10, 11, 32, + 10, 40, 5, 87, 73, 84, 72, 32, 147, 58, 73, 6, 154, 1, 65, 62, 77, 143, + 12, 89, 18, 64, 5, 87, 73, 84, 72, 32, 226, 13, 70, 202, 43, 73, 211, 9, + 77, 10, 50, 65, 62, 74, 194, 7, 75, 14, 72, 195, 4, 89, 2, 217, 12, 11, + 76, 69, 70, 32, 77, 65, 75, 83, 85, 82, 65, 2, 225, 7, 3, 69, 69, 77, 28, + 56, 2, 65, 76, 117, 8, 69, 72, 32, 87, 73, 84, 72, 32, 2, 37, 7, 32, 87, + 73, 84, 72, 32, 83, 2, 197, 1, 15, 85, 80, 69, 82, 83, 67, 82, 73, 80, + 84, 32, 65, 76, 69, 70, 26, 108, 3, 74, 69, 69, 126, 65, 198, 4, 77, 86, + 78, 78, 90, 242, 2, 82, 42, 89, 213, 53, 5, 72, 69, 72, 32, 77, 2, 11, + 77, 2, 11, 32, 2, 255, 62, 73, 114, 82, 65, 38, 72, 246, 4, 78, 78, 90, + 38, 74, 98, 75, 42, 77, 198, 1, 82, 43, 89, 4, 217, 2, 5, 76, 69, 70, 32, + 77, 76, 22, 65, 139, 3, 69, 72, 80, 15, 77, 90, 65, 32, 65, 66, 79, 86, + 69, 32, 87, 73, 84, 72, 32, 147, 5, 72, 66, 118, 65, 126, 69, 42, 72, 78, + 74, 18, 75, 62, 77, 86, 78, 22, 79, 16, 2, 87, 65, 18, 89, 26, 90, 242, + 2, 82, 67, 85, 12, 22, 76, 247, 6, 69, 8, 21, 3, 69, 70, 32, 8, 34, 77, + 222, 6, 70, 231, 52, 73, 4, 181, 6, 6, 65, 75, 83, 85, 82, 65, 6, 11, 32, + 6, 166, 6, 70, 203, 43, 73, 8, 22, 69, 191, 3, 65, 4, 11, 72, 4, 11, 32, + 4, 230, 4, 73, 143, 54, 77, 4, 223, 48, 69, 2, 11, 72, 2, 11, 65, 2, 11, + 72, 2, 149, 4, 2, 32, 73, 8, 17, 2, 69, 69, 8, 11, 77, 8, 11, 32, 8, 198, + 4, 70, 202, 43, 73, 211, 9, 77, 2, 93, 2, 79, 79, 4, 231, 3, 69, 4, 215, + 3, 87, 8, 186, 3, 69, 15, 85, 2, 17, 2, 65, 73, 2, 239, 2, 78, 6, 21, 3, + 69, 69, 77, 6, 11, 32, 6, 22, 87, 191, 46, 73, 2, 141, 2, 5, 73, 84, 72, + 32, 89, 4, 11, 72, 4, 11, 65, 4, 235, 45, 72, 14, 21, 3, 69, 69, 77, 14, + 11, 32, 14, 64, 5, 87, 73, 84, 72, 32, 194, 1, 70, 202, 43, 73, 211, 9, + 77, 6, 18, 77, 75, 89, 4, 21, 3, 69, 69, 77, 4, 11, 32, 4, 18, 73, 119, + 70, 2, 211, 44, 78, 2, 17, 2, 69, 72, 2, 77, 2, 32, 70, 4, 11, 69, 4, 11, + 72, 4, 11, 32, 4, 22, 70, 231, 52, 73, 2, 185, 53, 2, 73, 78, 6, 174, 43, + 73, 211, 9, 77, 166, 2, 92, 3, 68, 68, 65, 52, 3, 82, 75, 32, 109, 11, + 84, 72, 69, 77, 65, 84, 73, 67, 65, 76, 32, 4, 168, 161, 37, 5, 32, 87, + 65, 65, 74, 131, 65, 72, 4, 58, 78, 1, 10, 83, 73, 68, 69, 87, 65, 89, + 83, 32, 78, 2, 253, 163, 35, 7, 79, 79, 78, 32, 71, 72, 85, 158, 2, 174, + 2, 68, 140, 3, 8, 73, 78, 73, 84, 73, 65, 76, 32, 222, 1, 76, 132, 2, 9, + 79, 80, 69, 82, 65, 84, 79, 82, 32, 166, 1, 83, 142, 3, 84, 182, 173, 24, + 65, 102, 75, 110, 90, 234, 106, 74, 2, 77, 250, 192, 6, 66, 2, 70, 2, 82, + 2, 89, 222, 7, 72, 222, 58, 71, 254, 136, 3, 78, 250, 168, 2, 81, 135, 3, + 87, 62, 26, 79, 143, 217, 37, 65, 58, 88, 6, 84, 76, 69, 83, 83, 32, 61, + 12, 85, 66, 76, 69, 45, 83, 84, 82, 85, 67, 75, 32, 8, 134, 229, 31, 66, + 2, 70, 182, 203, 3, 78, 251, 168, 2, 81, 50, 178, 12, 83, 174, 20, 75, + 198, 151, 24, 84, 74, 90, 234, 106, 74, 2, 77, 250, 192, 6, 66, 2, 70, 2, + 82, 2, 89, 222, 7, 72, 222, 58, 71, 254, 136, 3, 78, 154, 237, 1, 76, + 210, 58, 68, 146, 1, 81, 222, 1, 65, 171, 1, 87, 40, 182, 1, 84, 166, 9, + 83, 178, 171, 24, 72, 30, 75, 214, 107, 74, 2, 77, 250, 192, 6, 66, 2, + 70, 2, 89, 186, 66, 71, 254, 136, 3, 78, 154, 237, 1, 76, 226, 59, 81, + 222, 1, 65, 171, 57, 68, 4, 186, 225, 31, 72, 207, 244, 6, 69, 56, 48, 6, + 79, 79, 80, 69, 68, 32, 235, 197, 38, 65, 54, 202, 8, 83, 174, 20, 75, + 190, 150, 24, 65, 74, 72, 66, 84, 74, 90, 234, 106, 74, 2, 77, 250, 192, + 6, 66, 2, 70, 2, 82, 2, 89, 186, 66, 71, 254, 136, 3, 78, 154, 237, 1, + 76, 210, 58, 68, 146, 1, 81, 135, 3, 87, 4, 200, 144, 29, 23, 77, 69, 69, + 77, 32, 87, 73, 84, 72, 32, 72, 65, 72, 32, 87, 73, 84, 72, 32, 84, 65, + 84, 87, 197, 174, 8, 9, 72, 65, 72, 32, 87, 73, 84, 72, 32, 52, 84, 9, + 84, 82, 69, 84, 67, 72, 69, 68, 32, 230, 252, 36, 72, 14, 69, 231, 143, + 1, 65, 46, 178, 1, 68, 86, 84, 250, 2, 83, 178, 171, 24, 72, 30, 75, 214, + 107, 74, 2, 77, 250, 192, 6, 66, 2, 70, 2, 89, 222, 7, 90, 222, 58, 71, + 254, 136, 3, 78, 250, 168, 2, 81, 223, 1, 65, 6, 52, 7, 79, 84, 76, 69, + 83, 83, 32, 235, 138, 38, 65, 4, 170, 219, 31, 66, 3, 70, 6, 142, 219, + 31, 72, 206, 244, 6, 65, 3, 69, 38, 42, 65, 182, 174, 24, 72, 211, 160, + 14, 69, 32, 44, 5, 73, 76, 69, 68, 32, 231, 207, 38, 72, 30, 146, 1, 68, + 94, 83, 174, 20, 75, 246, 130, 25, 74, 250, 192, 6, 89, 222, 7, 72, 222, + 58, 71, 254, 136, 3, 78, 154, 237, 1, 76, 226, 59, 81, 223, 1, 65, 6, 52, + 7, 79, 84, 76, 69, 83, 83, 32, 251, 135, 38, 65, 4, 238, 163, 35, 78, + 251, 168, 2, 81, 6, 226, 247, 36, 72, 14, 69, 231, 143, 1, 65, 4, 250, + 207, 18, 77, 231, 246, 18, 83, 6, 244, 246, 24, 3, 75, 65, 83, 12, 4, 68, + 65, 77, 77, 1, 4, 70, 65, 84, 72, 12, 118, 69, 38, 79, 220, 208, 12, 11, + 76, 65, 67, 69, 32, 79, 70, 32, 83, 65, 74, 253, 252, 5, 6, 73, 65, 83, + 84, 82, 69, 4, 254, 172, 32, 82, 199, 233, 5, 80, 4, 172, 193, 12, 8, 69, + 84, 73, 67, 32, 86, 69, 82, 169, 140, 6, 3, 85, 78, 68, 14, 238, 1, 65, + 96, 5, 69, 86, 69, 82, 83, 36, 15, 73, 71, 72, 84, 32, 65, 82, 82, 79, + 87, 72, 69, 65, 68, 32, 157, 174, 23, 28, 79, 85, 78, 68, 69, 68, 32, 72, + 73, 71, 72, 32, 83, 84, 79, 80, 32, 87, 73, 84, 72, 32, 70, 73, 76, 76, + 69, 68, 4, 40, 4, 73, 83, 69, 68, 207, 200, 38, 89, 2, 17, 2, 32, 82, 2, + 157, 250, 36, 3, 79, 85, 78, 2, 225, 229, 21, 4, 69, 68, 32, 68, 6, 26, + 65, 239, 165, 36, 66, 4, 173, 161, 37, 3, 66, 79, 86, 206, 1, 238, 1, 69, + 244, 2, 5, 72, 65, 68, 68, 65, 36, 4, 73, 71, 78, 32, 240, 4, 5, 77, 65, + 76, 76, 32, 222, 15, 85, 240, 2, 6, 89, 77, 66, 79, 76, 32, 249, 236, 36, + 18, 84, 65, 82, 84, 32, 79, 70, 32, 82, 85, 66, 32, 69, 76, 32, 72, 73, + 90, 20, 52, 7, 81, 85, 69, 78, 67, 69, 32, 139, 246, 34, 77, 18, 184, 1, + 26, 89, 69, 72, 32, 87, 73, 84, 72, 32, 72, 65, 77, 90, 65, 32, 65, 66, + 79, 86, 69, 32, 87, 73, 84, 72, 32, 245, 205, 31, 13, 78, 79, 79, 78, 32, + 87, 73, 84, 72, 32, 75, 69, 72, 16, 70, 65, 222, 196, 37, 87, 198, 89, + 89, 150, 14, 79, 214, 22, 69, 3, 85, 6, 36, 3, 76, 69, 70, 227, 194, 38, + 69, 5, 251, 12, 32, 7, 11, 32, 4, 222, 22, 73, 55, 77, 22, 132, 1, 2, 82, + 65, 158, 1, 83, 188, 1, 7, 65, 76, 65, 89, 72, 69, 32, 136, 15, 2, 77, + 73, 185, 164, 34, 6, 84, 65, 75, 72, 65, 76, 4, 132, 1, 13, 72, 77, 65, + 84, 85, 76, 76, 65, 72, 32, 65, 76, 65, 213, 201, 33, 13, 68, 73, 32, 65, + 76, 76, 65, 72, 79, 85, 32, 65, 78, 2, 143, 147, 38, 89, 12, 46, 65, 193, + 1, 6, 73, 78, 68, 72, 73, 32, 8, 136, 1, 18, 76, 76, 65, 76, 76, 65, 72, + 79, 85, 32, 65, 76, 65, 89, 72, 69, 32, 87, 206, 208, 31, 78, 218, 240, + 4, 70, 249, 115, 2, 77, 86, 2, 17, 2, 65, 83, 2, 217, 233, 15, 3, 83, 65, + 76, 4, 160, 215, 20, 12, 80, 79, 83, 84, 80, 79, 83, 73, 84, 73, 79, 78, + 129, 148, 14, 2, 65, 77, 110, 120, 2, 70, 65, 32, 5, 72, 73, 71, 72, 32, + 134, 11, 89, 88, 4, 76, 79, 87, 32, 118, 75, 158, 205, 21, 68, 139, 228, + 15, 87, 4, 166, 3, 82, 243, 187, 36, 84, 74, 212, 2, 17, 68, 79, 84, 76, + 69, 83, 83, 32, 72, 69, 65, 68, 32, 79, 70, 32, 75, 22, 70, 102, 76, 142, + 3, 77, 118, 83, 78, 84, 38, 87, 198, 2, 89, 142, 1, 78, 128, 187, 14, 18, + 85, 80, 82, 73, 71, 72, 84, 32, 82, 69, 67, 84, 65, 78, 71, 85, 76, 65, + 176, 217, 8, 7, 82, 79, 85, 78, 68, 69, 68, 166, 122, 90, 234, 106, 74, + 166, 181, 12, 81, 223, 1, 65, 2, 199, 203, 31, 72, 4, 24, 2, 65, 82, 31, + 79, 2, 11, 83, 2, 175, 2, 73, 2, 157, 193, 26, 6, 79, 84, 78, 79, 84, 69, + 10, 60, 8, 73, 71, 65, 84, 85, 82, 69, 32, 225, 11, 2, 65, 77, 8, 88, 10, + 65, 76, 69, 70, 32, 87, 73, 84, 72, 32, 116, 3, 81, 65, 70, 1, 3, 83, 65, + 68, 4, 84, 8, 76, 65, 77, 32, 87, 73, 84, 72, 177, 196, 36, 7, 89, 69, + 72, 32, 66, 65, 82, 2, 253, 192, 31, 2, 32, 89, 2, 89, 20, 32, 87, 73, + 84, 72, 32, 76, 65, 77, 32, 87, 73, 84, 72, 32, 65, 76, 69, 70, 32, 2, + 133, 161, 22, 3, 77, 65, 75, 6, 26, 69, 191, 130, 16, 65, 4, 17, 2, 69, + 77, 4, 17, 2, 32, 73, 4, 22, 78, 147, 9, 83, 2, 205, 9, 2, 73, 84, 6, + 164, 183, 36, 7, 73, 71, 78, 32, 83, 65, 70, 166, 39, 69, 231, 143, 1, + 65, 4, 162, 212, 23, 72, 211, 222, 14, 65, 20, 40, 4, 79, 82, 68, 32, + 151, 181, 37, 65, 18, 74, 65, 172, 1, 2, 83, 65, 220, 184, 25, 3, 87, 65, + 81, 247, 246, 11, 81, 10, 244, 133, 7, 3, 76, 45, 74, 240, 187, 18, 3, + 82, 45, 82, 240, 230, 6, 7, 84, 72, 45, 84, 72, 65, 76, 236, 136, 5, 5, + 78, 45, 78, 73, 83, 161, 92, 5, 83, 45, 83, 65, 74, 4, 238, 140, 38, 75, + 207, 36, 72, 4, 17, 2, 69, 72, 5, 233, 187, 10, 12, 32, 66, 65, 82, 82, + 69, 69, 32, 87, 73, 84, 72, 22, 50, 78, 98, 87, 222, 248, 24, 77, 199, + 224, 11, 83, 4, 21, 3, 79, 79, 78, 5, 37, 7, 32, 87, 73, 84, 72, 32, 75, + 2, 11, 65, 2, 131, 235, 37, 83, 14, 40, 4, 79, 82, 68, 32, 231, 176, 37, + 65, 12, 106, 73, 162, 251, 15, 77, 180, 177, 9, 3, 84, 65, 83, 208, 228, + 8, 2, 83, 65, 153, 228, 3, 3, 81, 65, 83, 4, 244, 159, 13, 2, 77, 65, + 157, 137, 19, 3, 83, 72, 77, 12, 138, 1, 66, 64, 3, 75, 85, 78, 193, 250, 24, 22, 80, 69, 82, 83, 67, 82, 73, 80, 84, 32, 65, 76, 69, 70, 32, 77, - 79, 75, 72, 65, 83, 83, 2, 33, 6, 83, 67, 82, 73, 80, 84, 2, 169, 156, - 22, 2, 32, 65, 9, 11, 32, 6, 34, 73, 54, 77, 231, 238, 35, 66, 2, 11, 83, - 2, 213, 204, 31, 5, 79, 76, 65, 84, 69, 2, 11, 69, 2, 11, 68, 2, 11, 73, - 2, 237, 141, 37, 2, 65, 76, 34, 152, 1, 2, 68, 79, 114, 84, 248, 172, 5, - 8, 83, 77, 65, 76, 76, 32, 84, 65, 244, 190, 26, 4, 70, 79, 85, 82, 204, - 123, 4, 87, 65, 83, 76, 239, 209, 4, 82, 6, 88, 16, 85, 66, 76, 69, 32, - 86, 69, 82, 84, 73, 67, 65, 76, 32, 66, 65, 135, 236, 35, 84, 2, 247, - 220, 35, 82, 16, 88, 10, 72, 82, 69, 69, 32, 68, 79, 84, 83, 32, 121, 8, - 87, 79, 32, 68, 79, 84, 83, 32, 8, 168, 235, 31, 17, 80, 79, 73, 78, 84, - 73, 78, 71, 32, 68, 79, 87, 78, 87, 65, 82, 68, 146, 128, 4, 66, 167, - 161, 1, 65, 8, 152, 234, 35, 10, 86, 69, 82, 84, 73, 67, 65, 76, 76, 89, - 42, 66, 167, 161, 1, 65, 30, 202, 1, 65, 152, 2, 4, 79, 78, 69, 32, 168, - 244, 10, 12, 82, 73, 80, 76, 69, 32, 68, 79, 84, 32, 80, 85, 144, 197, 6, - 8, 85, 82, 78, 69, 68, 32, 68, 65, 217, 143, 14, 8, 72, 79, 85, 83, 65, - 78, 68, 83, 12, 68, 5, 84, 87, 69, 69, 76, 153, 235, 35, 6, 73, 76, 32, - 70, 82, 65, 11, 33, 6, 32, 87, 73, 84, 72, 32, 8, 112, 11, 79, 86, 69, - 82, 83, 84, 82, 85, 67, 75, 32, 208, 193, 5, 7, 70, 65, 84, 72, 65, 84, - 65, 155, 231, 4, 84, 4, 26, 72, 143, 134, 37, 87, 2, 169, 228, 29, 2, 65, - 77, 12, 68, 3, 79, 78, 69, 214, 133, 30, 84, 201, 223, 5, 4, 76, 79, 79, - 80, 4, 221, 232, 33, 2, 32, 68, 8, 64, 10, 79, 87, 69, 76, 32, 83, 73, - 71, 78, 32, 183, 137, 24, 69, 6, 72, 10, 73, 78, 86, 69, 82, 84, 69, 68, - 32, 83, 2, 83, 255, 194, 25, 68, 2, 25, 4, 77, 65, 76, 76, 2, 133, 133, - 37, 2, 32, 86, 28, 104, 4, 80, 69, 82, 32, 232, 240, 5, 3, 67, 85, 66, - 156, 137, 5, 5, 70, 79, 85, 82, 84, 227, 149, 25, 68, 4, 158, 142, 25, - 77, 31, 84, 188, 1, 222, 1, 65, 34, 67, 138, 3, 69, 60, 7, 83, 77, 65, - 76, 76, 32, 76, 152, 128, 21, 17, 77, 79, 68, 73, 70, 73, 69, 82, 32, 76, - 69, 84, 84, 69, 82, 32, 76, 162, 248, 8, 72, 252, 182, 2, 3, 68, 82, 65, - 138, 250, 2, 70, 83, 81, 4, 198, 204, 31, 66, 139, 26, 80, 78, 80, 14, - 65, 80, 73, 84, 65, 76, 32, 76, 69, 84, 84, 69, 82, 32, 155, 172, 35, 79, - 76, 250, 1, 84, 36, 2, 89, 73, 150, 3, 67, 38, 82, 34, 69, 42, 71, 34, - 74, 38, 75, 22, 80, 38, 83, 106, 65, 22, 86, 158, 1, 76, 130, 141, 31, - 70, 2, 88, 218, 235, 1, 73, 158, 168, 3, 66, 2, 77, 198, 57, 78, 226, 26, - 90, 210, 96, 72, 190, 28, 68, 171, 1, 79, 4, 254, 167, 36, 73, 151, 212, - 1, 79, 5, 143, 172, 37, 87, 4, 138, 230, 31, 88, 169, 198, 4, 6, 77, 80, - 72, 65, 83, 73, 92, 76, 6, 69, 84, 84, 69, 82, 32, 157, 5, 8, 73, 71, 65, - 84, 85, 82, 69, 32, 80, 242, 1, 67, 38, 82, 34, 69, 42, 71, 34, 74, 38, - 75, 22, 80, 38, 83, 34, 84, 74, 65, 22, 86, 32, 2, 89, 73, 126, 76, 130, - 141, 31, 70, 2, 88, 218, 235, 1, 73, 158, 168, 3, 66, 2, 77, 198, 57, 78, - 226, 26, 90, 210, 96, 72, 190, 28, 68, 171, 1, 79, 8, 34, 72, 186, 248, - 37, 65, 3, 79, 4, 166, 247, 37, 69, 147, 1, 65, 6, 134, 247, 37, 67, 146, - 1, 72, 3, 84, 4, 194, 177, 37, 72, 215, 53, 73, 4, 242, 143, 31, 72, 223, - 231, 6, 65, 4, 211, 212, 33, 69, 4, 194, 244, 3, 73, 199, 129, 34, 69, 4, - 186, 244, 37, 72, 171, 1, 69, 6, 68, 7, 85, 82, 78, 69, 68, 32, 65, 250, - 161, 36, 73, 151, 212, 1, 79, 2, 167, 186, 36, 89, 4, 214, 247, 36, 69, - 163, 126, 79, 7, 146, 168, 21, 32, 231, 253, 15, 87, 12, 84, 5, 69, 67, - 72, 32, 89, 20, 4, 77, 69, 78, 32, 241, 237, 29, 4, 86, 69, 87, 32, 2, - 199, 160, 36, 73, 8, 234, 140, 31, 88, 218, 235, 1, 73, 226, 225, 3, 78, - 255, 119, 69, 14, 108, 10, 32, 80, 79, 73, 78, 84, 73, 78, 71, 32, 169, - 181, 36, 11, 72, 69, 65, 68, 45, 83, 72, 65, 80, 69, 68, 12, 156, 2, 24, - 82, 73, 71, 72, 84, 87, 65, 82, 68, 83, 32, 84, 72, 69, 78, 32, 67, 85, - 82, 86, 73, 78, 71, 32, 48, 16, 85, 80, 87, 65, 82, 68, 83, 32, 84, 72, - 69, 78, 32, 78, 79, 82, 161, 146, 35, 22, 68, 79, 87, 78, 87, 65, 82, 68, - 83, 32, 84, 72, 69, 78, 32, 67, 85, 82, 86, 73, 78, 71, 6, 44, 3, 83, 79, - 85, 198, 209, 26, 68, 35, 85, 2, 149, 147, 29, 4, 84, 72, 32, 87, 4, 168, - 240, 30, 10, 67, 85, 76, 65, 84, 69, 68, 32, 76, 79, 237, 184, 5, 6, 83, - 84, 32, 80, 65, 76, 20, 58, 67, 38, 84, 142, 153, 23, 89, 173, 191, 9, 2, - 83, 69, 4, 206, 168, 8, 69, 231, 180, 11, 73, 12, 48, 4, 69, 82, 73, 83, - 36, 2, 79, 78, 31, 82, 6, 146, 220, 2, 75, 155, 146, 35, 77, 2, 217, 246, - 13, 2, 73, 83, 4, 250, 231, 30, 65, 221, 255, 4, 22, 79, 78, 79, 77, 73, - 67, 65, 76, 32, 83, 89, 77, 66, 79, 76, 32, 70, 79, 82, 32, 85, 82, 4, - 132, 171, 19, 6, 72, 76, 69, 84, 73, 67, 193, 174, 17, 2, 79, 77, 10, 68, - 2, 84, 79, 200, 222, 21, 2, 83, 84, 181, 169, 2, 3, 66, 69, 82, 6, 54, - 77, 129, 237, 36, 7, 32, 82, 73, 67, 75, 83, 72, 4, 174, 135, 24, 79, - 189, 197, 5, 12, 65, 84, 69, 68, 32, 84, 69, 76, 76, 69, 82, 32, 112, 56, - 6, 69, 83, 84, 65, 78, 32, 165, 223, 4, 2, 79, 67, 110, 52, 7, 76, 69, - 84, 84, 69, 82, 32, 159, 183, 31, 65, 108, 234, 1, 65, 58, 71, 42, 72, - 34, 78, 50, 88, 42, 83, 42, 89, 34, 84, 178, 146, 34, 85, 202, 141, 1, - 79, 134, 60, 73, 190, 137, 1, 66, 2, 68, 2, 90, 130, 64, 69, 206, 41, 67, - 2, 70, 2, 74, 2, 75, 2, 76, 2, 77, 2, 80, 2, 82, 3, 86, 17, 230, 155, 35, - 65, 242, 139, 2, 69, 162, 64, 78, 3, 79, 6, 234, 208, 37, 71, 2, 72, 215, - 22, 69, 4, 194, 208, 37, 77, 215, 22, 69, 12, 46, 71, 246, 207, 37, 78, - 2, 89, 215, 22, 69, 6, 242, 207, 37, 86, 2, 89, 215, 22, 69, 8, 38, 72, - 238, 184, 37, 83, 143, 45, 69, 4, 162, 207, 37, 89, 215, 22, 69, 6, 130, - 207, 37, 72, 2, 84, 215, 22, 69, 192, 41, 178, 1, 65, 242, 170, 1, 69, - 252, 15, 9, 72, 65, 73, 75, 83, 85, 75, 73, 32, 202, 6, 73, 130, 3, 76, - 182, 45, 79, 138, 70, 82, 242, 18, 85, 138, 8, 89, 130, 144, 35, 80, 147, - 1, 83, 190, 14, 132, 1, 2, 66, 89, 94, 67, 206, 2, 68, 146, 1, 71, 106, - 76, 192, 29, 4, 77, 85, 77, 32, 238, 114, 78, 178, 1, 82, 98, 83, 223, 6, - 84, 11, 11, 32, 8, 190, 234, 12, 67, 230, 241, 5, 66, 216, 249, 13, 3, - 65, 78, 71, 215, 249, 3, 83, 16, 62, 75, 160, 251, 8, 5, 84, 82, 73, 65, - 78, 211, 150, 28, 79, 12, 42, 32, 46, 83, 153, 222, 10, 2, 45, 84, 4, - 234, 252, 9, 87, 217, 218, 20, 2, 79, 70, 6, 132, 1, 26, 76, 65, 78, 84, - 69, 68, 32, 83, 79, 85, 84, 72, 32, 65, 82, 82, 79, 87, 32, 87, 73, 84, - 72, 32, 72, 79, 235, 162, 37, 80, 4, 130, 189, 29, 82, 221, 182, 5, 2, - 79, 75, 4, 144, 241, 34, 27, 77, 73, 78, 84, 79, 78, 32, 82, 65, 67, 81, - 85, 69, 84, 32, 65, 78, 68, 32, 83, 72, 85, 84, 84, 76, 69, 67, 199, 176, - 2, 71, 6, 192, 226, 30, 6, 85, 69, 84, 84, 69, 32, 190, 242, 5, 69, 229, - 6, 8, 71, 65, 71, 69, 32, 67, 76, 65, 156, 2, 44, 6, 73, 78, 69, 83, 69, - 32, 183, 25, 76, 254, 1, 132, 3, 6, 67, 65, 82, 73, 75, 32, 84, 15, 73, - 78, 86, 69, 82, 84, 69, 68, 32, 67, 65, 82, 73, 75, 32, 68, 7, 76, 69, - 84, 84, 69, 82, 32, 224, 8, 15, 77, 85, 83, 73, 67, 65, 76, 32, 83, 89, - 77, 66, 79, 76, 32, 196, 6, 2, 80, 65, 184, 1, 5, 83, 73, 71, 78, 32, - 176, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 224, 164, 14, 5, - 65, 68, 69, 71, 32, 254, 141, 19, 87, 239, 162, 2, 68, 6, 32, 2, 80, 65, - 219, 129, 3, 83, 4, 86, 82, 145, 249, 30, 5, 77, 85, 78, 71, 75, 4, 36, - 3, 80, 65, 82, 131, 129, 3, 83, 2, 165, 133, 36, 2, 69, 82, 110, 166, 2, - 65, 112, 2, 66, 65, 32, 2, 67, 65, 32, 2, 68, 65, 110, 69, 32, 2, 71, 65, - 30, 73, 2, 79, 2, 85, 36, 2, 74, 65, 30, 75, 68, 2, 76, 65, 18, 78, 72, - 2, 80, 65, 36, 2, 82, 65, 16, 2, 83, 65, 54, 84, 128, 1, 2, 86, 69, 0, 3, - 90, 65, 76, 250, 205, 37, 72, 2, 77, 2, 87, 3, 89, 10, 226, 2, 75, 184, - 3, 5, 83, 89, 85, 82, 65, 172, 162, 15, 8, 82, 67, 72, 65, 73, 67, 32, - 74, 167, 249, 3, 73, 5, 165, 244, 18, 3, 32, 75, 69, 5, 129, 248, 30, 3, - 32, 76, 65, 9, 17, 2, 32, 77, 6, 44, 5, 85, 82, 68, 65, 32, 231, 192, 33, - 65, 4, 130, 176, 14, 77, 25, 3, 65, 76, 80, 4, 254, 3, 70, 231, 139, 37, - 75, 5, 137, 227, 34, 2, 32, 71, 4, 11, 75, 4, 161, 15, 2, 65, 82, 5, 193, - 196, 35, 2, 32, 74, 8, 34, 65, 225, 2, 3, 72, 79, 84, 7, 222, 2, 70, 211, - 171, 14, 32, 7, 131, 13, 32, 8, 34, 65, 134, 208, 37, 71, 3, 89, 5, 173, - 252, 26, 4, 32, 82, 65, 77, 5, 213, 200, 36, 4, 32, 75, 65, 80, 7, 167, - 12, 32, 7, 21, 3, 32, 83, 65, 4, 146, 207, 37, 71, 3, 80, 10, 30, 65, 97, - 3, 90, 73, 82, 9, 11, 32, 6, 160, 172, 14, 6, 77, 85, 82, 68, 65, 32, - 208, 7, 3, 76, 65, 84, 255, 171, 18, 84, 2, 193, 168, 14, 2, 32, 83, 56, - 168, 1, 10, 67, 79, 77, 66, 73, 78, 73, 78, 71, 32, 246, 1, 68, 172, 1, - 10, 76, 69, 70, 84, 45, 72, 65, 78, 68, 32, 117, 11, 82, 73, 71, 72, 84, - 45, 72, 65, 78, 68, 32, 18, 132, 1, 4, 75, 69, 77, 80, 78, 74, 240, 131, - 20, 2, 66, 69, 180, 182, 7, 3, 69, 78, 68, 148, 171, 3, 3, 84, 69, 71, - 159, 164, 4, 71, 8, 32, 2, 76, 73, 1, 2, 85, 76, 5, 37, 7, 32, 87, 73, - 84, 72, 32, 74, 2, 189, 162, 14, 3, 69, 71, 79, 20, 50, 65, 90, 69, 242, - 250, 36, 73, 2, 79, 3, 85, 10, 40, 2, 78, 71, 158, 251, 36, 69, 3, 73, 7, - 11, 32, 4, 218, 4, 83, 155, 244, 10, 71, 4, 238, 250, 36, 85, 167, 80, - 78, 10, 76, 6, 79, 80, 69, 78, 32, 80, 113, 9, 67, 76, 79, 83, 69, 68, - 32, 80, 76, 6, 254, 249, 36, 65, 2, 73, 3, 85, 8, 72, 8, 67, 76, 79, 83, - 69, 68, 32, 84, 29, 6, 79, 80, 69, 78, 32, 68, 4, 238, 198, 37, 65, 3, - 85, 4, 150, 201, 37, 65, 3, 85, 12, 30, 77, 69, 3, 78, 84, 73, 6, 44, 3, - 65, 68, 65, 241, 133, 35, 2, 69, 78, 5, 69, 2, 32, 76, 7, 11, 32, 4, 38, - 76, 245, 146, 35, 3, 66, 65, 87, 2, 225, 239, 36, 3, 65, 78, 84, 12, 106, - 83, 20, 4, 85, 76, 85, 32, 170, 224, 30, 67, 240, 6, 3, 66, 73, 83, 217, - 249, 1, 4, 82, 69, 82, 69, 2, 179, 232, 24, 85, 4, 254, 194, 20, 67, 237, - 241, 16, 3, 82, 73, 67, 30, 120, 3, 76, 65, 32, 32, 3, 82, 65, 32, 12, 4, - 83, 85, 75, 85, 34, 84, 104, 5, 80, 69, 80, 69, 84, 53, 3, 85, 76, 85, 4, - 165, 1, 4, 76, 69, 78, 71, 4, 115, 82, 5, 237, 203, 36, 3, 32, 73, 76, - 10, 36, 5, 65, 76, 73, 78, 71, 99, 69, 9, 11, 32, 6, 18, 82, 55, 84, 4, - 17, 2, 69, 80, 4, 11, 65, 5, 17, 2, 32, 84, 2, 11, 69, 2, 219, 222, 30, - 68, 5, 181, 230, 30, 3, 32, 83, 65, 30, 86, 79, 228, 147, 23, 5, 32, 79, - 70, 32, 89, 177, 155, 13, 6, 69, 84, 32, 83, 72, 79, 26, 32, 2, 79, 78, - 21, 2, 84, 32, 5, 195, 174, 35, 45, 22, 44, 2, 66, 79, 246, 1, 83, 179, - 192, 37, 88, 18, 38, 88, 205, 1, 4, 76, 68, 32, 83, 17, 33, 6, 32, 87, - 73, 84, 72, 32, 14, 82, 66, 86, 83, 168, 211, 12, 4, 76, 73, 71, 72, 246, - 180, 10, 67, 151, 184, 14, 88, 6, 52, 4, 79, 76, 68, 32, 149, 240, 36, 3, - 65, 76, 76, 4, 26, 83, 159, 136, 23, 67, 2, 165, 211, 12, 4, 67, 82, 73, - 80, 164, 10, 120, 2, 67, 79, 180, 1, 7, 76, 69, 84, 84, 69, 82, 32, 128, - 92, 3, 78, 74, 65, 138, 207, 28, 83, 238, 192, 5, 70, 83, 81, 8, 26, 77, - 135, 239, 36, 76, 6, 72, 12, 66, 73, 78, 73, 78, 71, 32, 77, 65, 82, 75, - 32, 239, 187, 37, 77, 4, 196, 215, 21, 6, 84, 85, 75, 87, 69, 78, 201, - 173, 3, 4, 75, 79, 81, 78, 148, 10, 170, 1, 70, 58, 75, 170, 1, 76, 66, - 77, 102, 78, 234, 1, 80, 214, 103, 82, 102, 83, 134, 1, 84, 54, 89, 178, - 219, 2, 87, 202, 219, 33, 69, 214, 22, 65, 2, 73, 2, 79, 3, 85, 8, 234, - 79, 65, 234, 213, 36, 69, 254, 5, 79, 219, 16, 85, 22, 78, 69, 46, 79, - 190, 203, 35, 89, 130, 237, 1, 80, 186, 2, 65, 2, 73, 3, 85, 6, 250, 174, - 36, 85, 166, 140, 1, 78, 3, 84, 7, 234, 200, 35, 86, 165, 225, 1, 2, 71, - 72, 10, 202, 102, 69, 202, 131, 26, 79, 182, 208, 10, 65, 2, 73, 3, 85, - 19, 62, 69, 174, 101, 66, 158, 212, 36, 65, 2, 73, 2, 79, 3, 85, 4, 142, - 202, 35, 69, 187, 239, 1, 78, 30, 102, 71, 50, 74, 50, 84, 158, 101, 85, - 130, 216, 34, 83, 246, 7, 68, 246, 222, 1, 89, 218, 19, 65, 3, 73, 6, - 186, 209, 32, 75, 202, 228, 4, 71, 187, 2, 65, 6, 242, 205, 26, 85, 198, - 211, 10, 69, 171, 4, 65, 4, 242, 146, 37, 85, 151, 14, 69, 128, 9, 76, 5, - 72, 65, 83, 69, 45, 218, 100, 69, 138, 2, 85, 138, 208, 36, 65, 3, 73, - 244, 8, 116, 2, 65, 32, 168, 18, 2, 66, 32, 136, 13, 2, 67, 32, 160, 20, - 2, 68, 32, 172, 18, 2, 69, 32, 137, 25, 2, 70, 32, 176, 1, 158, 1, 71, - 106, 75, 130, 1, 76, 102, 77, 198, 3, 78, 210, 4, 80, 146, 2, 83, 190, 2, - 84, 154, 1, 85, 184, 198, 30, 2, 70, 73, 150, 238, 4, 86, 215, 222, 1, - 82, 6, 60, 5, 72, 69, 85, 65, 69, 181, 46, 5, 66, 73, 69, 69, 32, 4, 176, - 27, 2, 71, 72, 191, 174, 26, 82, 12, 38, 65, 34, 69, 150, 67, 80, 3, 85, - 4, 254, 176, 37, 70, 187, 2, 81, 4, 240, 214, 33, 5, 85, 75, 69, 85, 84, - 167, 220, 3, 84, 10, 78, 85, 230, 65, 79, 236, 159, 26, 2, 65, 80, 241, - 214, 9, 4, 69, 84, 32, 75, 5, 159, 159, 27, 65, 36, 142, 1, 65, 176, 1, - 2, 66, 65, 38, 79, 76, 6, 86, 69, 85, 65, 69, 78, 244, 70, 8, 69, 85, 78, - 74, 79, 77, 78, 68, 201, 175, 34, 2, 71, 66, 20, 62, 69, 172, 49, 2, 78, - 83, 149, 239, 31, 4, 80, 32, 80, 73, 16, 58, 77, 234, 170, 12, 75, 226, - 168, 7, 78, 211, 200, 17, 83, 11, 186, 24, 66, 14, 71, 194, 44, 86, 239, - 220, 27, 75, 4, 146, 211, 19, 78, 171, 220, 17, 81, 6, 36, 2, 79, 77, - 237, 1, 2, 78, 32, 4, 178, 252, 12, 80, 239, 184, 23, 69, 2, 207, 243, - 35, 71, 48, 122, 65, 32, 2, 68, 65, 54, 71, 98, 75, 32, 2, 83, 72, 38, - 84, 102, 89, 74, 90, 158, 239, 35, 74, 158, 107, 69, 251, 70, 73, 4, 250, - 8, 65, 195, 164, 37, 81, 4, 22, 65, 159, 22, 32, 2, 153, 44, 3, 78, 71, - 71, 8, 52, 3, 75, 85, 69, 222, 188, 32, 65, 239, 155, 3, 71, 4, 250, 7, - 32, 165, 155, 12, 2, 78, 90, 4, 246, 25, 65, 135, 203, 19, 73, 4, 174, - 188, 35, 73, 187, 239, 1, 65, 8, 40, 2, 65, 80, 157, 157, 28, 2, 79, 81, - 7, 11, 32, 4, 188, 184, 35, 2, 77, 70, 1, 2, 78, 84, 6, 26, 73, 155, 142, - 37, 69, 5, 193, 13, 7, 84, 32, 77, 79, 78, 71, 75, 4, 214, 5, 65, 229, - 159, 12, 4, 85, 78, 32, 77, 22, 58, 65, 80, 3, 79, 78, 32, 178, 140, 37, - 69, 163, 28, 85, 10, 42, 65, 154, 18, 32, 250, 17, 77, 15, 83, 4, 230, - 190, 26, 82, 155, 234, 10, 77, 8, 56, 4, 77, 70, 79, 78, 1, 6, 80, 65, - 32, 78, 74, 73, 4, 37, 7, 32, 80, 73, 80, 65, 69, 77, 4, 206, 16, 71, - 243, 148, 37, 66, 22, 70, 72, 194, 1, 79, 192, 66, 2, 69, 85, 170, 190, - 36, 85, 167, 34, 73, 10, 74, 73, 70, 85, 241, 252, 35, 10, 79, 81, 32, - 78, 83, 72, 85, 84, 32, 89, 4, 170, 188, 26, 82, 177, 211, 6, 8, 78, 68, - 65, 32, 80, 65, 32, 78, 4, 188, 11, 4, 69, 78, 83, 72, 195, 154, 37, 77, - 6, 208, 234, 35, 2, 78, 74, 254, 186, 1, 81, 3, 84, 10, 44, 2, 69, 85, - 44, 3, 73, 84, 65, 31, 85, 4, 130, 209, 35, 65, 1, 4, 84, 69, 85, 87, 2, - 11, 32, 2, 223, 30, 77, 4, 186, 26, 32, 251, 246, 26, 65, 4, 228, 3, 5, - 32, 89, 85, 81, 32, 217, 230, 27, 3, 78, 75, 78, 112, 164, 1, 7, 71, 72, - 69, 85, 71, 72, 69, 34, 75, 126, 76, 122, 77, 154, 3, 78, 234, 2, 80, 90, - 83, 178, 1, 84, 106, 89, 190, 22, 87, 204, 46, 2, 70, 69, 131, 201, 11, - 86, 4, 198, 56, 85, 215, 233, 36, 78, 12, 40, 2, 69, 85, 50, 73, 203, - 144, 37, 65, 6, 230, 54, 89, 174, 184, 12, 80, 147, 160, 24, 65, 4, 242, - 142, 37, 69, 175, 18, 81, 8, 46, 65, 240, 69, 2, 79, 77, 183, 190, 36, - 69, 4, 50, 65, 129, 61, 7, 77, 32, 78, 83, 72, 85, 84, 2, 131, 182, 26, - 78, 26, 70, 65, 74, 66, 140, 1, 2, 69, 85, 58, 70, 145, 23, 3, 79, 78, - 84, 7, 21, 3, 32, 78, 74, 4, 178, 212, 18, 85, 229, 142, 17, 3, 69, 85, - 65, 10, 90, 65, 154, 46, 85, 184, 146, 30, 2, 69, 85, 133, 139, 6, 7, 73, - 84, 32, 77, 66, 65, 65, 4, 150, 12, 65, 205, 192, 19, 4, 32, 77, 65, 69, - 4, 252, 141, 32, 5, 84, 32, 78, 71, 71, 239, 143, 5, 81, 4, 48, 4, 79, - 78, 32, 84, 193, 204, 26, 2, 73, 89, 2, 163, 65, 69, 24, 110, 71, 166, 1, - 83, 50, 89, 152, 23, 8, 84, 73, 69, 69, 32, 83, 72, 69, 201, 144, 35, 5, - 68, 85, 32, 78, 74, 12, 70, 71, 164, 162, 36, 8, 75, 73, 78, 68, 73, 32, - 77, 86, 191, 104, 79, 8, 60, 3, 85, 79, 81, 204, 211, 19, 2, 69, 85, 179, - 140, 16, 65, 5, 193, 180, 28, 2, 32, 76, 4, 26, 72, 227, 202, 36, 69, 2, - 167, 229, 36, 85, 4, 248, 45, 2, 65, 69, 211, 17, 73, 8, 200, 195, 10, 2, - 69, 69, 218, 169, 12, 65, 164, 182, 8, 3, 85, 78, 71, 151, 218, 5, 73, - 12, 88, 2, 65, 75, 16, 2, 72, 69, 252, 185, 19, 2, 69, 84, 146, 239, 15, - 73, 231, 216, 1, 85, 2, 211, 25, 69, 4, 128, 200, 26, 4, 84, 32, 78, 74, - 241, 128, 5, 4, 85, 65, 69, 81, 6, 32, 2, 85, 32, 227, 220, 35, 65, 4, - 36, 4, 77, 65, 69, 77, 139, 7, 78, 2, 11, 71, 2, 135, 7, 66, 4, 44, 4, - 65, 70, 85, 32, 229, 4, 2, 69, 85, 2, 221, 165, 32, 6, 76, 69, 69, 82, - 65, 69, 196, 1, 170, 1, 71, 82, 75, 206, 2, 76, 50, 77, 250, 3, 78, 238, - 6, 80, 78, 83, 118, 84, 176, 1, 3, 86, 69, 85, 46, 87, 62, 89, 202, 157, - 30, 66, 182, 218, 2, 70, 163, 230, 3, 82, 6, 40, 2, 72, 65, 185, 184, 19, - 2, 66, 65, 4, 198, 170, 26, 82, 155, 234, 10, 80, 22, 66, 69, 182, 1, 85, - 132, 194, 26, 3, 80, 65, 82, 139, 206, 10, 65, 14, 40, 2, 78, 32, 54, 85, - 155, 147, 37, 84, 4, 180, 136, 33, 4, 70, 65, 84, 73, 235, 140, 3, 76, 8, - 42, 83, 174, 194, 26, 75, 195, 208, 10, 77, 4, 232, 14, 3, 72, 69, 85, - 239, 53, 69, 4, 48, 6, 79, 80, 32, 78, 75, 65, 147, 146, 37, 84, 2, 11, - 65, 2, 235, 167, 26, 82, 8, 202, 47, 65, 242, 145, 26, 73, 183, 208, 10, - 85, 38, 82, 65, 130, 1, 66, 214, 165, 26, 85, 216, 25, 4, 71, 66, 65, 83, - 139, 225, 8, 73, 8, 18, 32, 79, 69, 4, 42, 78, 145, 231, 17, 4, 75, 69, - 85, 65, 2, 11, 83, 2, 231, 160, 35, 73, 4, 226, 226, 36, 77, 211, 25, 83, - 24, 34, 65, 114, 69, 82, 73, 35, 85, 6, 32, 2, 65, 32, 255, 178, 19, 78, - 4, 200, 206, 31, 8, 67, 65, 66, 66, 65, 71, 69, 45, 133, 238, 4, 2, 80, - 73, 8, 50, 85, 142, 164, 26, 82, 173, 218, 5, 2, 69, 75, 4, 162, 142, 37, - 77, 3, 88, 7, 226, 7, 82, 167, 134, 37, 84, 4, 186, 251, 36, 65, 175, 18, - 69, 72, 130, 1, 65, 54, 68, 110, 71, 222, 1, 74, 102, 83, 130, 1, 84, - 102, 90, 237, 7, 12, 89, 73, 82, 32, 77, 75, 80, 65, 82, 65, 81, 32, 4, - 128, 188, 26, 4, 78, 83, 65, 78, 195, 208, 10, 81, 12, 60, 2, 69, 85, - 174, 41, 65, 134, 155, 19, 79, 159, 162, 17, 73, 4, 136, 156, 35, 2, 65, - 69, 199, 239, 1, 84, 20, 50, 71, 98, 75, 222, 185, 26, 65, 223, 191, 10, - 79, 12, 26, 85, 247, 186, 36, 69, 11, 180, 39, 3, 65, 69, 78, 190, 147, - 36, 79, 202, 26, 69, 155, 53, 77, 4, 36, 3, 85, 69, 32, 183, 185, 26, 65, - 2, 241, 179, 19, 3, 77, 65, 69, 10, 34, 65, 34, 69, 183, 214, 12, 85, 4, - 202, 248, 36, 69, 219, 16, 77, 4, 202, 153, 35, 69, 151, 99, 85, 12, 78, - 85, 214, 183, 26, 72, 204, 233, 5, 2, 69, 85, 158, 214, 4, 79, 219, 16, - 65, 4, 224, 244, 36, 4, 79, 84, 32, 78, 179, 19, 78, 8, 54, 69, 244, 234, - 36, 4, 85, 32, 77, 66, 131, 26, 65, 4, 168, 175, 19, 2, 85, 78, 131, 216, - 17, 78, 4, 238, 221, 35, 69, 147, 169, 1, 65, 6, 26, 73, 227, 182, 36, - 69, 4, 26, 82, 167, 134, 37, 78, 2, 175, 178, 35, 73, 10, 30, 69, 50, 72, - 255, 6, 85, 4, 26, 84, 179, 249, 35, 85, 2, 231, 181, 36, 70, 4, 238, - 210, 12, 85, 159, 226, 13, 73, 10, 40, 2, 65, 65, 34, 69, 41, 2, 73, 84, - 2, 11, 83, 2, 203, 154, 26, 72, 4, 228, 25, 2, 85, 84, 235, 234, 36, 84, - 4, 38, 85, 249, 248, 32, 3, 65, 32, 89, 2, 143, 241, 26, 65, 4, 236, 245, - 27, 2, 65, 69, 255, 141, 9, 88, 4, 40, 4, 65, 78, 71, 75, 139, 131, 37, - 85, 2, 143, 19, 85, 10, 42, 85, 158, 208, 12, 69, 135, 176, 24, 65, 6, - 210, 18, 87, 212, 3, 4, 32, 77, 85, 79, 179, 236, 36, 77, 234, 1, 134, 1, - 70, 68, 2, 71, 72, 34, 75, 254, 1, 76, 142, 1, 77, 130, 3, 78, 130, 5, - 80, 122, 82, 90, 83, 190, 1, 84, 158, 1, 87, 39, 89, 4, 36, 3, 69, 85, - 70, 179, 254, 36, 65, 2, 11, 69, 2, 151, 2, 85, 4, 202, 1, 69, 203, 252, - 36, 65, 22, 46, 69, 146, 1, 85, 42, 87, 143, 143, 35, 89, 10, 26, 85, - 227, 255, 36, 84, 8, 72, 3, 65, 69, 84, 20, 5, 79, 84, 32, 77, 66, 130, - 255, 36, 77, 3, 80, 2, 235, 249, 11, 77, 2, 231, 148, 26, 85, 9, 146, - 238, 36, 79, 218, 16, 78, 3, 81, 2, 171, 201, 36, 65, 14, 58, 69, 194, - 173, 26, 79, 254, 224, 8, 73, 227, 222, 1, 85, 8, 42, 85, 146, 142, 35, - 69, 187, 239, 1, 84, 4, 214, 234, 26, 65, 243, 146, 10, 77, 41, 94, 65, - 58, 66, 66, 69, 38, 70, 80, 2, 71, 66, 214, 254, 31, 79, 242, 130, 4, 86, - 151, 121, 85, 4, 164, 211, 17, 2, 76, 69, 133, 244, 18, 3, 69, 78, 74, 6, - 32, 2, 65, 65, 247, 220, 36, 85, 5, 189, 231, 28, 2, 32, 83, 6, 234, 176, - 18, 85, 171, 219, 16, 69, 10, 44, 2, 69, 85, 246, 255, 34, 79, 207, 11, - 73, 4, 162, 228, 36, 65, 215, 22, 84, 6, 158, 139, 35, 73, 168, 70, 2, - 79, 70, 227, 40, 69, 72, 78, 68, 46, 71, 226, 1, 74, 98, 83, 110, 84, 34, - 89, 222, 245, 36, 73, 3, 85, 8, 194, 39, 69, 178, 130, 36, 79, 139, 63, - 65, 24, 18, 71, 99, 75, 12, 54, 65, 202, 42, 69, 162, 231, 31, 87, 147, - 214, 4, 85, 6, 152, 38, 2, 65, 77, 195, 210, 36, 80, 12, 68, 2, 69, 85, - 182, 136, 35, 73, 2, 89, 218, 159, 1, 85, 215, 79, 65, 4, 154, 197, 12, - 65, 251, 158, 24, 82, 12, 60, 2, 69, 85, 230, 15, 73, 214, 180, 12, 85, - 199, 178, 24, 65, 4, 218, 228, 36, 65, 175, 18, 84, 10, 46, 72, 32, 3, - 73, 69, 69, 195, 229, 36, 85, 4, 138, 218, 36, 85, 219, 5, 69, 4, 150, - 246, 36, 80, 3, 84, 6, 130, 14, 69, 147, 195, 36, 85, 8, 174, 217, 36, - 69, 218, 5, 85, 254, 5, 65, 219, 16, 73, 12, 42, 69, 46, 85, 194, 244, - 36, 65, 3, 73, 4, 220, 138, 26, 2, 85, 84, 155, 234, 10, 69, 4, 158, 216, - 36, 85, 175, 28, 81, 8, 48, 3, 69, 78, 32, 162, 224, 36, 73, 175, 1, 65, - 4, 238, 213, 26, 79, 215, 251, 9, 77, 26, 58, 72, 90, 85, 186, 16, 65, - 174, 6, 69, 179, 191, 36, 79, 12, 54, 69, 174, 162, 26, 79, 222, 188, 10, - 73, 219, 19, 85, 6, 214, 7, 85, 139, 235, 36, 69, 6, 234, 219, 36, 65, - 214, 22, 69, 3, 85, 18, 62, 69, 74, 85, 222, 160, 26, 79, 226, 185, 10, - 65, 215, 22, 73, 8, 26, 85, 135, 130, 35, 69, 6, 210, 157, 35, 65, 234, - 211, 1, 78, 3, 84, 5, 227, 212, 36, 79, 4, 134, 138, 32, 85, 235, 230, 4, - 65, 10, 40, 2, 65, 69, 18, 85, 191, 160, 36, 69, 2, 251, 3, 77, 6, 22, - 87, 227, 13, 79, 2, 207, 159, 26, 79, 186, 2, 178, 1, 70, 154, 1, 71, - 202, 1, 75, 170, 1, 76, 158, 1, 77, 134, 2, 78, 190, 7, 80, 166, 2, 82, - 78, 83, 166, 1, 84, 246, 1, 86, 66, 87, 34, 89, 238, 216, 36, 65, 2, 73, - 3, 79, 18, 54, 85, 234, 129, 23, 65, 198, 213, 13, 69, 255, 5, 79, 10, - 26, 32, 135, 142, 31, 69, 6, 132, 229, 23, 4, 82, 69, 77, 69, 146, 129, - 11, 67, 207, 135, 2, 73, 16, 24, 2, 66, 69, 39, 72, 4, 222, 224, 35, 85, - 167, 140, 1, 84, 12, 34, 65, 34, 69, 199, 219, 36, 79, 2, 11, 65, 2, 151, - 130, 26, 77, 8, 26, 85, 131, 236, 36, 84, 6, 170, 213, 36, 65, 214, 22, - 78, 3, 88, 18, 50, 69, 62, 80, 18, 85, 218, 234, 36, 73, 3, 79, 6, 26, - 85, 139, 235, 36, 84, 4, 178, 212, 36, 65, 215, 22, 88, 2, 211, 28, 69, - 6, 170, 206, 36, 69, 162, 28, 79, 15, 84, 18, 50, 65, 40, 2, 69, 85, 22, - 79, 195, 233, 36, 85, 6, 162, 217, 36, 65, 218, 16, 80, 3, 81, 2, 167, - 215, 36, 65, 8, 222, 158, 18, 79, 226, 202, 18, 77, 3, 81, 32, 110, 65, - 44, 2, 66, 69, 34, 70, 20, 2, 71, 66, 34, 73, 142, 253, 25, 85, 186, 154, - 10, 69, 2, 79, 139, 60, 86, 11, 250, 242, 17, 69, 170, 245, 18, 80, 3, - 81, 4, 158, 215, 36, 85, 219, 16, 69, 2, 155, 181, 12, 69, 4, 226, 151, - 36, 69, 227, 79, 65, 5, 207, 208, 36, 69, 80, 114, 68, 170, 1, 71, 138, - 3, 74, 112, 2, 83, 72, 58, 84, 32, 3, 89, 73, 32, 54, 90, 210, 159, 36, - 65, 191, 47, 75, 12, 34, 65, 98, 73, 187, 149, 36, 85, 6, 32, 2, 65, 32, - 215, 229, 36, 80, 4, 240, 164, 18, 3, 77, 89, 32, 137, 177, 13, 3, 83, - 79, 70, 4, 226, 148, 26, 65, 183, 208, 10, 81, 36, 78, 71, 50, 85, 122, - 75, 118, 79, 172, 221, 11, 3, 69, 85, 82, 223, 132, 25, 65, 12, 18, 69, - 51, 85, 6, 26, 85, 191, 244, 34, 69, 4, 255, 144, 24, 65, 6, 64, 6, 65, - 69, 83, 72, 65, 69, 134, 249, 25, 82, 155, 234, 10, 80, 2, 11, 32, 2, - 183, 189, 23, 78, 12, 34, 65, 20, 2, 69, 85, 35, 85, 5, 243, 206, 36, 65, - 4, 150, 208, 36, 65, 175, 18, 88, 4, 162, 226, 36, 77, 3, 80, 4, 134, - 226, 36, 80, 3, 81, 8, 18, 65, 31, 69, 2, 201, 230, 31, 2, 69, 77, 6, 26, - 69, 255, 212, 35, 85, 5, 245, 190, 36, 4, 32, 69, 80, 79, 6, 26, 85, 171, - 241, 34, 73, 4, 210, 224, 36, 79, 15, 69, 4, 234, 207, 36, 85, 207, 16, - 65, 4, 140, 159, 26, 4, 67, 76, 69, 65, 151, 244, 1, 66, 4, 186, 143, 26, - 65, 3, 85, 34, 42, 65, 90, 69, 58, 73, 50, 79, 23, 85, 8, 32, 2, 32, 80, - 223, 233, 17, 65, 4, 196, 148, 28, 2, 69, 79, 241, 192, 7, 2, 76, 85, 6, - 26, 85, 223, 205, 36, 69, 4, 178, 222, 36, 84, 3, 88, 7, 11, 69, 4, 214, - 141, 26, 69, 183, 208, 10, 84, 5, 135, 142, 36, 79, 11, 82, 65, 130, 221, - 36, 69, 3, 77, 8, 46, 65, 238, 14, 69, 149, 246, 18, 2, 73, 77, 4, 254, - 220, 36, 69, 3, 81, 18, 62, 69, 30, 72, 150, 245, 31, 85, 158, 214, 4, - 79, 163, 14, 65, 4, 162, 220, 36, 69, 3, 84, 8, 42, 69, 198, 239, 22, 79, - 231, 155, 3, 73, 2, 137, 169, 12, 2, 85, 65, 28, 34, 65, 94, 69, 50, 79, - 39, 85, 10, 56, 2, 69, 78, 202, 238, 22, 65, 154, 236, 13, 77, 3, 81, 2, - 205, 209, 11, 3, 32, 78, 84, 6, 26, 85, 167, 218, 36, 78, 5, 211, 167, - 12, 65, 6, 138, 223, 34, 79, 135, 251, 1, 81, 6, 170, 7, 77, 239, 187, - 36, 65, 6, 26, 69, 219, 200, 36, 79, 4, 150, 239, 25, 85, 155, 234, 10, - 69, 6, 246, 10, 69, 207, 187, 32, 85, 26, 68, 2, 69, 85, 46, 73, 32, 3, - 79, 81, 32, 54, 85, 155, 215, 36, 65, 8, 174, 133, 24, 65, 246, 210, 12, - 77, 3, 88, 4, 162, 193, 36, 69, 215, 22, 84, 4, 180, 195, 12, 4, 83, 87, - 73, 77, 227, 197, 9, 67, 8, 238, 134, 26, 69, 206, 252, 8, 65, 234, 211, - 1, 78, 3, 81, 108, 162, 1, 75, 82, 76, 46, 77, 98, 78, 190, 1, 80, 66, - 82, 50, 83, 110, 84, 38, 89, 146, 222, 2, 87, 204, 191, 9, 2, 86, 85, - 254, 155, 24, 69, 242, 5, 70, 231, 16, 85, 14, 226, 138, 18, 69, 170, - 219, 16, 89, 130, 237, 1, 80, 186, 2, 65, 2, 79, 3, 85, 6, 190, 132, 26, - 79, 182, 208, 10, 65, 3, 73, 13, 42, 66, 34, 69, 254, 211, 36, 65, 3, 79, - 4, 186, 132, 36, 69, 171, 77, 65, 2, 195, 228, 34, 69, 22, 94, 71, 38, - 74, 38, 85, 130, 216, 34, 83, 246, 7, 68, 150, 3, 84, 226, 219, 1, 89, - 219, 19, 73, 4, 134, 236, 31, 75, 203, 228, 4, 71, 4, 202, 232, 25, 85, - 239, 215, 10, 65, 5, 235, 187, 36, 65, 6, 26, 69, 251, 231, 25, 85, 4, - 234, 197, 35, 85, 167, 140, 1, 69, 12, 186, 2, 69, 150, 134, 9, 73, 159, - 201, 27, 85, 14, 66, 72, 230, 2, 69, 162, 248, 18, 65, 142, 177, 17, 85, - 235, 36, 73, 6, 158, 189, 36, 73, 218, 19, 79, 3, 85, 6, 182, 141, 21, - 65, 239, 166, 15, 69, 4, 246, 255, 25, 79, 183, 208, 10, 65, 4, 178, 185, - 36, 65, 215, 22, 69, 14, 54, 69, 150, 134, 9, 73, 202, 178, 27, 65, 215, - 22, 85, 6, 138, 195, 35, 85, 166, 140, 1, 69, 3, 78, 16, 62, 72, 50, 69, - 162, 248, 18, 65, 142, 177, 17, 85, 235, 36, 73, 8, 46, 69, 190, 186, 36, - 73, 218, 19, 79, 3, 85, 2, 239, 193, 35, 85, 10, 158, 131, 18, 69, 202, - 135, 3, 65, 155, 195, 15, 73, 6, 150, 253, 25, 79, 2, 85, 183, 208, 10, - 65, 14, 42, 75, 242, 144, 35, 65, 147, 157, 1, 74, 11, 49, 10, 78, 79, - 84, 69, 32, 87, 73, 84, 72, 32, 8, 184, 207, 9, 2, 80, 79, 198, 1, 89, - 156, 177, 12, 2, 69, 85, 163, 177, 6, 68, 6, 38, 32, 173, 244, 16, 3, 66, - 69, 82, 4, 134, 202, 29, 67, 213, 240, 5, 5, 79, 70, 32, 83, 79, 78, 72, - 3, 75, 69, 84, 60, 7, 83, 65, 32, 86, 65, 72, 32, 179, 194, 34, 69, 5, - 233, 156, 16, 10, 66, 65, 76, 76, 32, 65, 78, 68, 32, 72, 72, 104, 10, + 79, 75, 72, 65, 83, 83, 2, 33, 6, 83, 67, 82, 73, 80, 84, 2, 137, 172, + 22, 2, 32, 65, 9, 11, 32, 6, 34, 73, 54, 77, 199, 137, 36, 66, 2, 11, 83, + 2, 133, 221, 31, 5, 79, 76, 65, 84, 69, 2, 11, 69, 2, 11, 68, 2, 11, 73, + 2, 169, 172, 37, 2, 65, 76, 34, 156, 1, 2, 68, 79, 126, 84, 188, 179, 5, + 8, 83, 77, 65, 76, 76, 32, 84, 65, 252, 199, 26, 4, 70, 79, 85, 82, 224, + 129, 1, 4, 87, 65, 83, 76, 187, 218, 4, 82, 6, 48, 6, 85, 66, 76, 69, 32, + 86, 139, 135, 36, 84, 2, 49, 10, 69, 82, 84, 73, 67, 65, 76, 32, 66, 65, + 2, 207, 247, 35, 82, 16, 88, 10, 72, 82, 69, 69, 32, 68, 79, 84, 83, 32, + 121, 8, 87, 79, 32, 68, 79, 84, 83, 32, 8, 244, 250, 31, 17, 80, 79, 73, + 78, 84, 73, 78, 71, 32, 68, 79, 87, 78, 87, 65, 82, 68, 150, 139, 4, 66, + 131, 165, 1, 65, 8, 204, 177, 10, 9, 86, 69, 82, 84, 73, 67, 65, 76, 76, + 198, 211, 25, 66, 131, 165, 1, 65, 30, 202, 1, 65, 152, 2, 4, 79, 78, 69, + 32, 240, 249, 10, 12, 82, 73, 80, 76, 69, 32, 68, 79, 84, 32, 80, 85, + 196, 201, 6, 8, 85, 82, 78, 69, 68, 32, 68, 65, 249, 149, 14, 8, 72, 79, + 85, 83, 65, 78, 68, 83, 12, 68, 5, 84, 87, 69, 69, 76, 229, 133, 36, 6, + 73, 76, 32, 70, 82, 65, 11, 33, 6, 32, 87, 73, 84, 72, 32, 8, 112, 11, + 79, 86, 69, 82, 83, 84, 82, 85, 67, 75, 32, 140, 201, 5, 7, 70, 65, 84, + 72, 65, 84, 65, 235, 228, 4, 84, 4, 26, 72, 183, 164, 37, 87, 2, 185, + 244, 29, 2, 65, 77, 12, 68, 3, 79, 78, 69, 246, 149, 30, 84, 245, 233, 5, + 4, 76, 79, 79, 80, 4, 225, 253, 33, 2, 32, 68, 8, 64, 10, 79, 87, 69, 76, + 32, 83, 73, 71, 78, 32, 179, 148, 24, 69, 6, 72, 10, 73, 78, 86, 69, 82, + 84, 69, 68, 32, 83, 2, 83, 135, 206, 25, 68, 2, 25, 4, 77, 65, 76, 76, 2, + 173, 163, 37, 2, 32, 86, 28, 104, 4, 80, 69, 82, 32, 236, 249, 5, 3, 67, + 85, 66, 152, 134, 5, 5, 70, 79, 85, 82, 84, 163, 171, 25, 68, 4, 158, + 153, 25, 77, 31, 84, 188, 1, 222, 1, 65, 34, 67, 138, 3, 69, 60, 7, 83, + 77, 65, 76, 76, 32, 76, 200, 143, 21, 17, 77, 79, 68, 73, 70, 73, 69, 82, + 32, 76, 69, 84, 84, 69, 82, 32, 76, 146, 249, 8, 72, 140, 188, 2, 3, 68, + 82, 65, 178, 255, 2, 70, 83, 81, 4, 142, 220, 31, 66, 143, 26, 80, 78, + 80, 14, 65, 80, 73, 84, 65, 76, 32, 76, 69, 84, 84, 69, 82, 32, 243, 198, + 35, 79, 76, 250, 1, 84, 36, 2, 89, 73, 150, 3, 67, 38, 82, 34, 69, 42, + 71, 34, 74, 38, 75, 22, 80, 38, 83, 106, 65, 22, 86, 158, 1, 76, 170, + 157, 31, 70, 2, 88, 134, 241, 1, 73, 214, 174, 3, 66, 2, 77, 222, 57, 78, + 230, 28, 90, 210, 96, 72, 190, 28, 68, 171, 1, 79, 4, 138, 196, 36, 73, + 179, 214, 1, 79, 5, 183, 202, 37, 87, 4, 214, 245, 31, 88, 233, 210, 4, + 6, 77, 80, 72, 65, 83, 73, 92, 76, 6, 69, 84, 84, 69, 82, 32, 157, 5, 8, + 73, 71, 65, 84, 85, 82, 69, 32, 80, 242, 1, 67, 38, 82, 34, 69, 42, 71, + 34, 74, 38, 75, 22, 80, 38, 83, 34, 84, 74, 65, 22, 86, 32, 2, 89, 73, + 126, 76, 170, 157, 31, 70, 2, 88, 134, 241, 1, 73, 214, 174, 3, 66, 2, + 77, 222, 57, 78, 230, 28, 90, 210, 96, 72, 190, 28, 68, 171, 1, 79, 8, + 34, 72, 226, 150, 38, 65, 3, 79, 4, 206, 149, 38, 69, 147, 1, 65, 6, 174, + 149, 38, 67, 146, 1, 72, 3, 84, 4, 234, 207, 37, 72, 215, 53, 73, 4, 154, + 160, 31, 72, 223, 245, 6, 65, 4, 147, 234, 33, 69, 4, 214, 251, 3, 73, + 219, 152, 34, 69, 4, 226, 146, 38, 72, 171, 1, 69, 6, 68, 7, 85, 82, 78, + 69, 68, 32, 65, 134, 190, 36, 73, 179, 214, 1, 79, 2, 187, 214, 36, 89, + 4, 254, 149, 37, 69, 163, 126, 79, 7, 194, 183, 21, 32, 223, 140, 16, 87, + 12, 84, 5, 69, 67, 72, 32, 89, 20, 4, 77, 69, 78, 32, 145, 254, 29, 4, + 86, 69, 87, 32, 2, 211, 188, 36, 73, 8, 146, 157, 31, 88, 134, 241, 1, + 73, 178, 232, 3, 78, 131, 122, 69, 14, 108, 10, 32, 80, 79, 73, 78, 84, + 73, 78, 71, 32, 189, 209, 36, 11, 72, 69, 65, 68, 45, 83, 72, 65, 80, 69, + 68, 12, 156, 2, 24, 82, 73, 71, 72, 84, 87, 65, 82, 68, 83, 32, 84, 72, + 69, 78, 32, 67, 85, 82, 86, 73, 78, 71, 32, 48, 16, 85, 80, 87, 65, 82, + 68, 83, 32, 84, 72, 69, 78, 32, 78, 79, 82, 249, 172, 35, 22, 68, 79, 87, + 78, 87, 65, 82, 68, 83, 32, 84, 72, 69, 78, 32, 67, 85, 82, 86, 73, 78, + 71, 6, 44, 3, 83, 79, 85, 146, 221, 26, 68, 35, 85, 2, 205, 156, 34, 4, + 84, 72, 32, 87, 4, 228, 200, 31, 11, 67, 85, 76, 65, 84, 69, 68, 32, 76, + 79, 82, 189, 252, 4, 6, 83, 84, 32, 80, 65, 76, 24, 58, 67, 38, 84, 202, + 168, 23, 89, 201, 197, 9, 2, 83, 69, 4, 242, 177, 8, 69, 167, 186, 11, + 73, 16, 48, 4, 69, 82, 73, 83, 36, 2, 79, 78, 31, 82, 6, 174, 227, 2, 75, + 163, 169, 35, 77, 2, 185, 130, 14, 2, 73, 83, 8, 104, 20, 79, 78, 79, 77, + 73, 67, 65, 76, 32, 83, 89, 77, 66, 79, 76, 32, 70, 79, 82, 32, 159, 130, + 14, 65, 4, 216, 210, 25, 13, 65, 83, 84, 69, 82, 79, 73, 68, 32, 80, 82, + 79, 83, 205, 175, 10, 2, 85, 82, 4, 164, 185, 19, 6, 72, 76, 69, 84, 73, + 67, 217, 187, 17, 2, 79, 77, 10, 68, 2, 84, 79, 160, 237, 21, 2, 83, 84, + 133, 165, 2, 3, 66, 69, 82, 6, 54, 77, 213, 138, 37, 7, 32, 82, 73, 67, + 75, 83, 72, 4, 214, 145, 24, 79, 209, 202, 5, 12, 65, 84, 69, 68, 32, 84, + 69, 76, 76, 69, 82, 32, 112, 56, 6, 69, 83, 84, 65, 78, 32, 169, 227, 4, + 2, 79, 67, 110, 52, 7, 76, 69, 84, 84, 69, 82, 32, 147, 198, 31, 65, 108, + 234, 1, 65, 58, 71, 42, 72, 34, 78, 50, 88, 42, 83, 42, 89, 34, 84, 178, + 172, 34, 85, 206, 141, 1, 79, 238, 60, 73, 166, 140, 1, 66, 2, 68, 2, 90, + 130, 64, 69, 206, 41, 67, 2, 70, 2, 74, 2, 75, 2, 76, 2, 77, 2, 80, 2, + 82, 3, 86, 17, 234, 181, 35, 65, 194, 143, 2, 69, 162, 64, 78, 3, 79, 6, + 190, 238, 37, 71, 2, 72, 215, 22, 69, 4, 150, 238, 37, 77, 215, 22, 69, + 12, 46, 71, 202, 237, 37, 78, 2, 89, 215, 22, 69, 6, 198, 237, 37, 86, 2, + 89, 215, 22, 69, 8, 38, 72, 194, 214, 37, 83, 143, 45, 69, 4, 246, 236, + 37, 89, 215, 22, 69, 6, 214, 236, 37, 72, 2, 84, 215, 22, 69, 178, 42, + 178, 1, 65, 194, 171, 1, 69, 244, 21, 9, 72, 65, 73, 75, 83, 85, 75, 73, + 32, 202, 6, 73, 130, 3, 76, 182, 45, 79, 138, 70, 82, 246, 18, 85, 138, + 8, 89, 138, 167, 35, 80, 147, 1, 83, 198, 14, 132, 1, 2, 66, 89, 94, 67, + 206, 2, 68, 146, 1, 71, 106, 76, 192, 29, 4, 77, 85, 77, 32, 190, 115, + 78, 178, 1, 82, 98, 83, 223, 6, 84, 11, 11, 32, 8, 174, 245, 12, 67, 146, + 245, 5, 66, 196, 128, 14, 3, 65, 78, 71, 135, 128, 4, 83, 16, 62, 75, + 244, 131, 9, 5, 84, 82, 73, 65, 78, 211, 171, 28, 79, 12, 42, 32, 46, 83, + 221, 227, 10, 2, 45, 84, 4, 162, 129, 10, 87, 229, 229, 20, 2, 79, 70, 6, + 132, 1, 26, 76, 65, 78, 84, 69, 68, 32, 83, 79, 85, 84, 72, 32, 65, 82, + 82, 79, 87, 32, 87, 73, 84, 72, 32, 72, 79, 191, 192, 37, 80, 4, 190, + 204, 29, 82, 165, 193, 5, 2, 79, 75, 4, 148, 139, 35, 27, 77, 73, 78, 84, + 79, 78, 32, 82, 65, 67, 81, 85, 69, 84, 32, 65, 78, 68, 32, 83, 72, 85, + 84, 84, 76, 69, 67, 151, 180, 2, 71, 6, 148, 242, 30, 6, 85, 69, 84, 84, + 69, 32, 162, 254, 5, 69, 129, 9, 8, 71, 65, 71, 69, 32, 67, 76, 65, 156, + 2, 44, 6, 73, 78, 69, 83, 69, 32, 183, 25, 76, 254, 1, 132, 3, 6, 67, 65, + 82, 73, 75, 32, 84, 15, 73, 78, 86, 69, 82, 84, 69, 68, 32, 67, 65, 82, + 73, 75, 32, 68, 7, 76, 69, 84, 84, 69, 82, 32, 224, 8, 15, 77, 85, 83, + 73, 67, 65, 76, 32, 83, 89, 77, 66, 79, 76, 32, 196, 6, 2, 80, 65, 184, + 1, 5, 83, 73, 71, 78, 32, 176, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, + 78, 32, 168, 176, 14, 5, 65, 68, 69, 71, 32, 234, 150, 19, 87, 167, 169, + 2, 68, 6, 32, 2, 80, 65, 167, 136, 3, 83, 4, 86, 82, 229, 136, 31, 5, 77, + 85, 78, 71, 75, 4, 36, 3, 80, 65, 82, 207, 135, 3, 83, 2, 221, 160, 36, + 2, 69, 82, 110, 166, 2, 65, 112, 2, 66, 65, 32, 2, 67, 65, 32, 2, 68, 65, + 110, 69, 32, 2, 71, 65, 30, 73, 2, 79, 2, 85, 36, 2, 74, 65, 30, 75, 68, + 2, 76, 65, 18, 78, 72, 2, 80, 65, 36, 2, 82, 65, 16, 2, 83, 65, 54, 84, + 128, 1, 2, 86, 69, 0, 3, 90, 65, 76, 206, 235, 37, 72, 2, 77, 2, 87, 3, + 89, 10, 226, 2, 75, 184, 3, 5, 83, 89, 85, 82, 65, 184, 170, 15, 8, 82, + 67, 72, 65, 73, 67, 32, 74, 187, 255, 3, 73, 5, 197, 130, 19, 3, 32, 75, + 69, 5, 213, 135, 31, 3, 32, 76, 65, 9, 17, 2, 32, 77, 6, 44, 5, 85, 82, + 68, 65, 32, 155, 213, 33, 65, 4, 202, 187, 14, 77, 25, 3, 65, 76, 80, 4, + 254, 3, 70, 187, 169, 37, 75, 5, 141, 253, 34, 2, 32, 71, 4, 11, 75, 4, + 161, 15, 2, 65, 82, 5, 173, 223, 35, 2, 32, 74, 8, 34, 65, 225, 2, 3, 72, + 79, 84, 7, 222, 2, 70, 155, 183, 14, 32, 7, 131, 13, 32, 8, 34, 65, 218, + 237, 37, 71, 3, 89, 5, 157, 135, 27, 4, 32, 82, 65, 77, 5, 141, 228, 36, + 4, 32, 75, 65, 80, 7, 167, 12, 32, 7, 21, 3, 32, 83, 65, 4, 230, 236, 37, + 71, 3, 80, 10, 30, 65, 97, 3, 90, 73, 82, 9, 11, 32, 6, 232, 183, 14, 6, + 77, 85, 82, 68, 65, 32, 212, 7, 3, 76, 65, 84, 179, 181, 18, 84, 2, 137, + 180, 14, 2, 32, 83, 56, 168, 1, 10, 67, 79, 77, 66, 73, 78, 73, 78, 71, + 32, 246, 1, 68, 172, 1, 10, 76, 69, 70, 84, 45, 72, 65, 78, 68, 32, 117, + 11, 82, 73, 71, 72, 84, 45, 72, 65, 78, 68, 32, 18, 132, 1, 4, 75, 69, + 77, 80, 78, 74, 168, 146, 20, 2, 66, 69, 220, 182, 7, 3, 69, 78, 68, 136, + 172, 3, 3, 84, 69, 71, 207, 174, 4, 71, 8, 32, 2, 76, 73, 1, 2, 85, 76, + 5, 37, 7, 32, 87, 73, 84, 72, 32, 74, 2, 133, 174, 14, 3, 69, 71, 79, 20, + 50, 65, 90, 69, 198, 152, 37, 73, 2, 79, 3, 85, 10, 40, 2, 78, 71, 242, + 152, 37, 69, 3, 73, 7, 11, 32, 4, 218, 4, 83, 247, 254, 10, 71, 4, 194, + 152, 37, 85, 167, 80, 78, 10, 76, 6, 79, 80, 69, 78, 32, 80, 113, 9, 67, + 76, 79, 83, 69, 68, 32, 80, 76, 6, 210, 151, 37, 65, 2, 73, 3, 85, 8, 72, + 8, 67, 76, 79, 83, 69, 68, 32, 84, 29, 6, 79, 80, 69, 78, 32, 68, 4, 194, + 228, 37, 65, 3, 85, 4, 234, 230, 37, 65, 3, 85, 12, 30, 77, 69, 3, 78, + 84, 73, 6, 44, 3, 65, 68, 65, 245, 159, 35, 2, 69, 78, 5, 69, 2, 32, 76, + 7, 11, 32, 4, 38, 76, 133, 173, 35, 3, 66, 65, 87, 2, 181, 141, 37, 3, + 65, 78, 84, 12, 106, 83, 20, 4, 85, 76, 85, 32, 254, 239, 30, 67, 240, 6, + 3, 66, 73, 83, 133, 255, 1, 4, 82, 69, 82, 69, 2, 223, 242, 24, 85, 4, + 210, 209, 20, 67, 237, 128, 17, 3, 82, 73, 67, 30, 120, 3, 76, 65, 32, + 32, 3, 82, 65, 32, 12, 4, 83, 85, 75, 85, 34, 84, 104, 5, 80, 69, 80, 69, + 84, 53, 3, 85, 76, 85, 4, 165, 1, 4, 76, 69, 78, 71, 4, 115, 82, 5, 193, + 233, 36, 3, 32, 73, 76, 10, 36, 5, 65, 76, 73, 78, 71, 99, 69, 9, 11, 32, + 6, 18, 82, 55, 84, 4, 17, 2, 69, 80, 4, 11, 65, 5, 17, 2, 32, 84, 2, 11, + 69, 2, 175, 238, 30, 68, 5, 137, 246, 30, 3, 32, 83, 65, 30, 86, 79, 132, + 158, 23, 5, 32, 79, 70, 32, 89, 217, 172, 13, 6, 69, 84, 32, 83, 72, 79, + 26, 32, 2, 79, 78, 21, 2, 84, 32, 5, 187, 200, 35, 45, 22, 44, 2, 66, 79, + 246, 1, 83, 135, 222, 37, 88, 18, 38, 88, 205, 1, 4, 76, 68, 32, 83, 17, + 33, 6, 32, 87, 73, 84, 72, 32, 14, 82, 66, 86, 83, 132, 222, 12, 4, 76, + 73, 71, 72, 238, 180, 10, 67, 151, 203, 14, 88, 6, 52, 4, 79, 76, 68, 32, + 233, 141, 37, 3, 65, 76, 76, 4, 26, 83, 243, 146, 23, 67, 2, 129, 222, + 12, 4, 67, 82, 73, 80, 172, 10, 120, 2, 67, 79, 180, 1, 7, 76, 69, 84, + 84, 69, 82, 32, 208, 92, 3, 78, 74, 65, 246, 221, 28, 83, 182, 203, 5, + 70, 83, 81, 8, 26, 77, 219, 140, 37, 76, 6, 72, 12, 66, 73, 78, 73, 78, + 71, 32, 77, 65, 82, 75, 32, 195, 217, 37, 77, 4, 184, 230, 21, 6, 84, 85, + 75, 87, 69, 78, 141, 169, 3, 4, 75, 79, 81, 78, 156, 10, 170, 1, 70, 58, + 75, 170, 1, 76, 66, 77, 102, 78, 234, 1, 80, 166, 104, 82, 102, 83, 134, + 1, 84, 54, 89, 162, 225, 2, 87, 222, 242, 33, 69, 214, 22, 65, 2, 73, 2, + 79, 3, 85, 8, 170, 80, 65, 254, 242, 36, 69, 254, 5, 79, 219, 16, 85, 22, + 78, 69, 46, 79, 170, 230, 35, 89, 234, 239, 1, 80, 186, 2, 65, 2, 73, 3, + 85, 6, 178, 202, 36, 85, 194, 142, 1, 78, 3, 84, 7, 214, 227, 35, 86, + 141, 228, 1, 2, 71, 72, 10, 154, 103, 69, 234, 141, 26, 79, 154, 227, 10, + 65, 2, 73, 3, 85, 19, 62, 69, 254, 101, 66, 162, 241, 36, 65, 2, 73, 2, + 79, 3, 85, 4, 250, 228, 35, 69, 163, 242, 1, 78, 30, 102, 71, 50, 74, 50, + 84, 238, 101, 85, 158, 242, 34, 83, 246, 7, 68, 222, 225, 1, 89, 218, 19, + 65, 3, 73, 6, 186, 230, 32, 75, 158, 237, 4, 71, 187, 2, 65, 6, 234, 216, + 26, 85, 162, 230, 10, 69, 171, 4, 65, 4, 198, 176, 37, 85, 151, 14, 69, + 136, 9, 76, 5, 72, 65, 83, 69, 45, 170, 101, 69, 138, 2, 85, 142, 237, + 36, 65, 3, 73, 252, 8, 116, 2, 65, 32, 168, 18, 2, 66, 32, 180, 13, 2, + 67, 32, 180, 20, 2, 68, 32, 172, 18, 2, 69, 32, 153, 25, 2, 70, 32, 176, + 1, 158, 1, 71, 106, 75, 130, 1, 76, 102, 77, 198, 3, 78, 210, 4, 80, 146, + 2, 83, 190, 2, 84, 154, 1, 85, 140, 214, 30, 2, 70, 73, 174, 249, 4, 86, + 191, 225, 1, 82, 6, 60, 5, 72, 69, 85, 65, 69, 153, 47, 5, 66, 73, 69, + 69, 32, 4, 220, 27, 2, 71, 72, 139, 185, 26, 82, 12, 38, 65, 34, 69, 214, + 67, 80, 3, 85, 4, 210, 206, 37, 70, 187, 2, 81, 4, 240, 240, 33, 5, 85, + 75, 69, 85, 84, 251, 223, 3, 84, 10, 78, 85, 166, 66, 79, 156, 170, 26, + 2, 65, 80, 213, 233, 9, 4, 69, 84, 32, 75, 5, 255, 173, 27, 65, 36, 142, + 1, 65, 176, 1, 2, 66, 65, 38, 79, 76, 6, 86, 69, 85, 65, 69, 78, 180, 71, + 8, 69, 85, 78, 74, 79, 77, 78, 68, 153, 201, 34, 2, 71, 66, 20, 62, 69, + 236, 49, 2, 78, 83, 221, 131, 32, 4, 80, 32, 80, 73, 16, 58, 77, 218, + 181, 12, 75, 190, 172, 7, 78, 219, 215, 17, 83, 11, 230, 24, 66, 14, 71, + 214, 44, 86, 255, 235, 27, 75, 4, 222, 225, 19, 78, 179, 235, 17, 81, 6, + 36, 2, 79, 77, 237, 1, 2, 78, 32, 4, 190, 135, 13, 80, 183, 203, 23, 69, + 2, 143, 143, 36, 71, 48, 122, 65, 32, 2, 68, 65, 54, 71, 98, 75, 32, 2, + 83, 72, 38, 84, 102, 89, 74, 90, 222, 138, 36, 74, 178, 109, 69, 251, 70, + 73, 4, 250, 8, 65, 151, 194, 37, 81, 4, 22, 65, 203, 22, 32, 2, 217, 44, + 3, 78, 71, 71, 8, 52, 3, 75, 85, 69, 222, 209, 32, 65, 167, 162, 3, 71, + 4, 250, 7, 32, 149, 166, 12, 2, 78, 90, 4, 166, 26, 65, 143, 217, 19, 73, + 4, 154, 215, 35, 73, 163, 242, 1, 65, 8, 40, 2, 65, 80, 237, 172, 28, 2, + 79, 81, 7, 11, 32, 4, 168, 211, 35, 2, 77, 70, 1, 2, 78, 84, 6, 26, 73, + 239, 171, 37, 69, 5, 193, 13, 7, 84, 32, 77, 79, 78, 71, 75, 4, 214, 5, + 65, 213, 170, 12, 4, 85, 78, 32, 77, 22, 58, 65, 80, 3, 79, 78, 32, 134, + 170, 37, 69, 163, 28, 85, 10, 42, 65, 198, 18, 32, 142, 18, 77, 15, 83, + 4, 222, 201, 26, 82, 247, 252, 10, 77, 8, 56, 4, 77, 70, 79, 78, 1, 6, + 80, 65, 32, 78, 74, 73, 4, 37, 7, 32, 80, 73, 80, 65, 69, 77, 4, 250, 16, + 71, 155, 178, 37, 66, 22, 70, 72, 194, 1, 79, 144, 67, 2, 69, 85, 174, + 219, 36, 85, 167, 34, 73, 10, 74, 73, 70, 85, 177, 152, 36, 10, 79, 81, + 32, 78, 83, 72, 85, 84, 32, 89, 4, 162, 199, 26, 82, 237, 220, 6, 8, 78, + 68, 65, 32, 80, 65, 32, 78, 4, 204, 11, 4, 69, 78, 83, 72, 135, 184, 37, + 77, 6, 144, 134, 36, 2, 78, 74, 146, 189, 1, 81, 3, 84, 10, 44, 2, 69, + 85, 44, 3, 73, 84, 65, 31, 85, 4, 186, 236, 35, 65, 1, 4, 84, 69, 85, 87, + 2, 11, 32, 2, 195, 31, 77, 4, 234, 26, 32, 171, 133, 27, 65, 4, 228, 3, + 5, 32, 89, 85, 81, 32, 169, 246, 27, 3, 78, 75, 78, 116, 164, 1, 7, 71, + 72, 69, 85, 71, 72, 69, 34, 75, 126, 76, 122, 77, 154, 3, 78, 250, 2, 80, + 118, 83, 178, 1, 84, 106, 89, 210, 22, 87, 220, 46, 2, 70, 69, 163, 211, + 11, 86, 4, 134, 57, 85, 235, 134, 37, 78, 12, 40, 2, 69, 85, 50, 73, 159, + 174, 37, 65, 6, 166, 55, 89, 250, 194, 12, 80, 219, 178, 24, 65, 4, 198, + 172, 37, 69, 175, 18, 81, 8, 46, 65, 192, 70, 2, 79, 77, 187, 219, 36, + 69, 4, 50, 65, 209, 61, 7, 77, 32, 78, 83, 72, 85, 84, 2, 251, 192, 26, + 78, 26, 70, 65, 74, 66, 140, 1, 2, 69, 85, 58, 70, 193, 23, 3, 79, 78, + 84, 7, 21, 3, 32, 78, 74, 4, 210, 226, 18, 85, 133, 156, 17, 3, 69, 85, + 65, 10, 90, 65, 218, 46, 85, 204, 161, 30, 2, 69, 85, 133, 153, 6, 7, 73, + 84, 32, 77, 66, 65, 65, 4, 198, 12, 65, 213, 206, 19, 4, 32, 77, 65, 69, + 4, 132, 163, 32, 5, 84, 32, 78, 71, 71, 187, 152, 5, 81, 4, 48, 4, 79, + 78, 32, 84, 177, 215, 26, 2, 73, 89, 2, 243, 65, 69, 26, 110, 71, 182, 1, + 83, 50, 89, 200, 23, 8, 84, 73, 69, 69, 32, 83, 72, 69, 245, 170, 35, 5, + 68, 85, 32, 78, 74, 14, 70, 71, 248, 191, 36, 8, 75, 73, 78, 68, 73, 32, + 77, 86, 191, 104, 79, 10, 76, 3, 85, 79, 81, 244, 225, 19, 2, 69, 85, + 186, 153, 16, 65, 187, 172, 1, 79, 5, 129, 196, 28, 2, 32, 76, 4, 26, 72, + 167, 232, 36, 69, 2, 235, 130, 37, 85, 4, 168, 46, 2, 65, 69, 227, 17, + 73, 10, 76, 3, 85, 78, 71, 200, 205, 10, 2, 69, 69, 154, 169, 12, 65, + 243, 163, 14, 73, 4, 246, 177, 31, 71, 247, 199, 4, 65, 12, 88, 2, 65, + 75, 16, 2, 72, 69, 156, 200, 19, 2, 69, 84, 178, 251, 15, 73, 207, 219, + 1, 85, 2, 231, 25, 69, 4, 196, 210, 26, 4, 84, 32, 78, 74, 221, 138, 5, + 4, 85, 65, 69, 81, 6, 32, 2, 85, 32, 247, 247, 35, 65, 4, 36, 4, 77, 65, + 69, 77, 143, 7, 78, 2, 11, 71, 2, 139, 7, 66, 4, 44, 4, 65, 70, 85, 32, + 233, 4, 2, 69, 85, 2, 177, 186, 32, 6, 76, 69, 69, 82, 65, 69, 198, 1, + 174, 1, 71, 82, 75, 206, 2, 76, 50, 77, 250, 3, 78, 238, 6, 80, 78, 83, + 134, 1, 84, 176, 1, 3, 86, 69, 85, 46, 87, 62, 89, 222, 172, 30, 66, 226, + 227, 2, 70, 247, 234, 3, 82, 6, 40, 2, 72, 65, 213, 198, 19, 2, 66, 65, + 4, 142, 181, 26, 82, 247, 252, 10, 80, 22, 66, 69, 182, 1, 85, 196, 204, + 26, 3, 80, 65, 82, 239, 224, 10, 65, 14, 40, 2, 78, 32, 54, 85, 191, 176, + 37, 84, 4, 132, 161, 33, 4, 70, 65, 84, 73, 191, 145, 3, 76, 8, 42, 83, + 238, 204, 26, 75, 167, 227, 10, 77, 4, 248, 14, 3, 72, 69, 85, 255, 53, + 69, 4, 48, 6, 79, 80, 32, 78, 75, 65, 183, 175, 37, 84, 2, 11, 65, 2, + 179, 178, 26, 82, 8, 234, 47, 65, 146, 156, 26, 73, 155, 227, 10, 85, 38, + 82, 65, 130, 1, 66, 158, 176, 26, 85, 208, 25, 4, 71, 66, 65, 83, 135, + 241, 8, 73, 8, 18, 32, 79, 69, 4, 42, 78, 141, 240, 17, 4, 75, 69, 85, + 65, 2, 11, 83, 2, 163, 187, 35, 73, 4, 134, 128, 37, 77, 211, 25, 83, 24, + 34, 65, 114, 69, 82, 73, 35, 85, 6, 32, 2, 65, 32, 155, 193, 19, 78, 4, + 244, 226, 31, 8, 67, 65, 66, 66, 65, 71, 69, 45, 253, 246, 4, 2, 80, 73, + 8, 50, 85, 214, 174, 26, 82, 189, 228, 5, 2, 69, 75, 4, 198, 171, 37, 77, + 3, 88, 7, 226, 7, 82, 203, 163, 37, 84, 4, 222, 152, 37, 65, 175, 18, 69, + 72, 130, 1, 65, 54, 68, 110, 71, 222, 1, 74, 102, 83, 130, 1, 84, 102, + 90, 253, 7, 12, 89, 73, 82, 32, 77, 75, 80, 65, 82, 65, 81, 32, 4, 192, + 198, 26, 4, 78, 83, 65, 78, 167, 227, 10, 81, 12, 60, 2, 69, 85, 206, 41, + 65, 238, 168, 19, 79, 187, 177, 17, 73, 4, 196, 182, 35, 2, 65, 69, 175, + 242, 1, 84, 20, 50, 71, 98, 75, 158, 196, 26, 65, 195, 210, 10, 79, 12, + 26, 85, 155, 216, 36, 69, 11, 212, 39, 3, 65, 69, 78, 194, 176, 36, 79, + 202, 26, 69, 155, 53, 77, 4, 36, 3, 85, 69, 32, 247, 195, 26, 65, 2, 249, + 193, 19, 3, 77, 65, 69, 10, 34, 65, 34, 69, 147, 225, 12, 85, 4, 238, + 149, 37, 69, 219, 16, 77, 4, 134, 180, 35, 69, 227, 99, 85, 12, 78, 85, + 150, 194, 26, 72, 220, 243, 5, 2, 69, 85, 242, 222, 4, 79, 219, 16, 65, + 4, 132, 146, 37, 4, 79, 84, 32, 78, 179, 19, 78, 8, 54, 69, 152, 136, 37, + 4, 85, 32, 77, 66, 131, 26, 65, 4, 176, 189, 19, 2, 85, 78, 159, 231, 17, + 78, 4, 254, 248, 35, 69, 167, 171, 1, 65, 6, 26, 73, 135, 212, 36, 69, 4, + 26, 82, 203, 163, 37, 78, 2, 183, 205, 35, 73, 12, 30, 69, 30, 72, 163, + 7, 85, 4, 78, 84, 135, 148, 36, 85, 6, 48, 2, 69, 84, 174, 221, 12, 85, + 131, 226, 13, 73, 2, 215, 210, 36, 70, 10, 40, 2, 65, 65, 34, 69, 41, 2, + 73, 84, 2, 11, 83, 2, 131, 165, 26, 72, 4, 228, 25, 2, 85, 84, 255, 135, + 37, 84, 4, 38, 85, 185, 145, 33, 3, 65, 32, 89, 2, 175, 255, 26, 65, 4, + 252, 132, 28, 2, 65, 69, 131, 156, 9, 88, 4, 40, 4, 65, 78, 71, 75, 159, + 160, 37, 85, 2, 143, 19, 85, 10, 42, 85, 234, 218, 12, 69, 207, 194, 24, + 65, 6, 210, 18, 87, 212, 3, 4, 32, 77, 85, 79, 199, 137, 37, 77, 234, 1, + 134, 1, 70, 68, 2, 71, 72, 34, 75, 254, 1, 76, 142, 1, 77, 130, 3, 78, + 130, 5, 80, 122, 82, 90, 83, 190, 1, 84, 158, 1, 87, 39, 89, 4, 36, 3, + 69, 85, 70, 199, 155, 37, 65, 2, 11, 69, 2, 151, 2, 85, 4, 202, 1, 69, + 223, 153, 37, 65, 22, 46, 69, 146, 1, 85, 42, 87, 187, 169, 35, 89, 10, + 26, 85, 247, 156, 37, 84, 8, 72, 3, 65, 69, 84, 20, 5, 79, 84, 32, 77, + 66, 150, 156, 37, 77, 3, 80, 2, 155, 132, 12, 77, 2, 159, 159, 26, 85, 9, + 166, 139, 37, 79, 218, 16, 78, 3, 81, 2, 191, 230, 36, 65, 14, 58, 69, + 242, 183, 26, 79, 250, 240, 8, 73, 203, 225, 1, 85, 8, 42, 85, 190, 168, + 35, 69, 163, 242, 1, 84, 4, 246, 248, 26, 65, 231, 161, 10, 77, 41, 94, + 65, 58, 66, 66, 69, 38, 70, 80, 2, 71, 66, 150, 147, 32, 79, 198, 139, 4, + 86, 151, 121, 85, 4, 144, 220, 17, 2, 76, 69, 173, 136, 19, 3, 69, 78, + 74, 6, 32, 2, 65, 65, 139, 250, 36, 85, 5, 185, 246, 28, 2, 32, 83, 6, + 202, 190, 18, 85, 247, 231, 16, 69, 10, 44, 2, 69, 85, 162, 154, 35, 79, + 207, 11, 73, 4, 182, 129, 37, 65, 215, 22, 84, 6, 202, 165, 35, 73, 252, + 70, 2, 79, 70, 247, 42, 69, 72, 78, 68, 46, 71, 226, 1, 74, 98, 83, 110, + 84, 34, 89, 242, 146, 37, 73, 3, 85, 8, 210, 39, 69, 182, 159, 36, 79, + 139, 63, 65, 24, 18, 71, 99, 75, 12, 54, 65, 218, 42, 69, 210, 251, 31, + 87, 231, 222, 4, 85, 6, 168, 38, 2, 65, 77, 199, 239, 36, 80, 12, 68, 2, + 69, 85, 226, 162, 35, 73, 2, 89, 194, 162, 1, 85, 215, 79, 65, 4, 230, + 207, 12, 65, 195, 177, 24, 82, 12, 60, 2, 69, 85, 230, 15, 73, 162, 191, + 12, 85, 143, 197, 24, 65, 4, 238, 129, 37, 65, 175, 18, 84, 10, 46, 72, + 32, 3, 73, 69, 69, 215, 130, 37, 85, 4, 158, 247, 36, 85, 219, 5, 69, 4, + 170, 147, 37, 80, 3, 84, 6, 130, 14, 69, 167, 224, 36, 85, 8, 194, 246, + 36, 69, 218, 5, 85, 254, 5, 65, 219, 16, 73, 12, 42, 69, 46, 85, 214, + 145, 37, 65, 3, 73, 4, 148, 149, 26, 2, 85, 84, 247, 252, 10, 69, 4, 178, + 245, 36, 85, 175, 28, 81, 8, 48, 3, 69, 78, 32, 182, 253, 36, 73, 175, 1, + 65, 4, 190, 225, 26, 79, 155, 141, 10, 77, 26, 58, 72, 90, 85, 202, 16, + 65, 174, 6, 69, 183, 220, 36, 79, 12, 54, 69, 222, 172, 26, 79, 194, 207, + 10, 73, 219, 19, 85, 6, 214, 7, 85, 159, 136, 37, 69, 6, 254, 248, 36, + 65, 214, 22, 69, 3, 85, 18, 62, 69, 74, 85, 142, 171, 26, 79, 198, 204, + 10, 65, 215, 22, 73, 8, 26, 85, 179, 156, 35, 69, 6, 202, 184, 35, 65, + 134, 214, 1, 78, 3, 84, 5, 247, 241, 36, 79, 4, 198, 158, 32, 85, 191, + 239, 4, 65, 10, 40, 2, 65, 69, 18, 85, 211, 189, 36, 69, 2, 251, 3, 77, + 6, 22, 87, 243, 13, 79, 2, 255, 169, 26, 79, 188, 2, 178, 1, 70, 154, 1, + 71, 202, 1, 75, 170, 1, 76, 158, 1, 77, 134, 2, 78, 206, 7, 80, 166, 2, + 82, 78, 83, 166, 1, 84, 246, 1, 86, 66, 87, 34, 89, 242, 245, 36, 65, 2, + 73, 3, 79, 18, 54, 85, 210, 139, 23, 65, 242, 232, 13, 69, 255, 5, 79, + 10, 26, 32, 163, 162, 31, 69, 6, 208, 238, 23, 4, 82, 69, 77, 69, 242, + 145, 11, 67, 183, 138, 2, 73, 16, 24, 2, 66, 69, 39, 72, 4, 214, 251, 35, + 85, 195, 142, 1, 84, 12, 34, 65, 34, 69, 219, 248, 36, 79, 2, 11, 65, 2, + 207, 140, 26, 77, 8, 26, 85, 151, 137, 37, 84, 6, 190, 242, 36, 65, 214, + 22, 78, 3, 88, 18, 50, 69, 62, 80, 18, 85, 238, 135, 37, 73, 3, 79, 6, + 26, 85, 159, 136, 37, 84, 4, 198, 241, 36, 65, 215, 22, 88, 2, 227, 28, + 69, 6, 190, 235, 36, 69, 162, 28, 79, 15, 84, 18, 50, 65, 40, 2, 69, 85, + 22, 79, 215, 134, 37, 85, 6, 182, 246, 36, 65, 218, 16, 80, 3, 81, 2, + 187, 244, 36, 65, 8, 190, 172, 18, 79, 150, 218, 18, 77, 3, 81, 32, 110, + 65, 44, 2, 66, 69, 34, 70, 20, 2, 71, 66, 34, 73, 198, 135, 26, 85, 150, + 173, 10, 69, 2, 79, 139, 60, 86, 11, 218, 128, 18, 69, 222, 132, 19, 80, + 3, 81, 4, 178, 244, 36, 85, 219, 16, 69, 2, 231, 191, 12, 69, 4, 246, + 180, 36, 69, 227, 79, 65, 5, 227, 237, 36, 69, 82, 114, 68, 170, 1, 71, + 154, 3, 74, 112, 2, 83, 72, 58, 84, 32, 3, 89, 73, 32, 54, 90, 214, 188, + 36, 65, 191, 47, 75, 12, 34, 65, 98, 73, 207, 178, 36, 85, 6, 32, 2, 65, + 32, 235, 130, 37, 80, 4, 208, 178, 18, 3, 77, 89, 32, 241, 183, 13, 3, + 83, 79, 70, 4, 146, 159, 26, 65, 155, 227, 10, 81, 38, 78, 71, 66, 85, + 122, 75, 118, 79, 204, 231, 11, 3, 69, 85, 82, 195, 151, 25, 65, 14, 34, + 69, 50, 85, 219, 128, 37, 79, 6, 26, 85, 219, 142, 35, 69, 4, 219, 154, + 24, 65, 6, 64, 6, 65, 69, 83, 72, 65, 69, 174, 131, 26, 82, 247, 252, 10, + 80, 2, 11, 32, 2, 243, 198, 23, 78, 12, 34, 65, 20, 2, 69, 85, 35, 85, 5, + 247, 235, 36, 65, 4, 154, 237, 36, 65, 175, 18, 88, 4, 166, 255, 36, 77, + 3, 80, 4, 138, 255, 36, 80, 3, 81, 8, 18, 65, 31, 69, 2, 249, 250, 31, 2, + 69, 77, 6, 26, 69, 231, 239, 35, 85, 5, 249, 219, 36, 4, 32, 69, 80, 79, + 6, 26, 85, 199, 139, 35, 73, 4, 214, 253, 36, 79, 15, 69, 4, 238, 236, + 36, 85, 207, 16, 65, 4, 232, 169, 26, 4, 67, 76, 69, 65, 167, 248, 1, 66, + 4, 218, 153, 26, 65, 3, 85, 34, 42, 65, 90, 69, 58, 73, 50, 79, 23, 85, + 8, 32, 2, 32, 80, 175, 247, 17, 65, 4, 176, 163, 28, 2, 69, 79, 237, 204, + 7, 2, 76, 85, 6, 26, 85, 227, 234, 36, 69, 4, 182, 251, 36, 84, 3, 88, 7, + 11, 69, 4, 246, 151, 26, 69, 155, 227, 10, 84, 5, 139, 171, 36, 79, 11, + 82, 65, 134, 250, 36, 69, 3, 77, 8, 46, 65, 238, 14, 69, 253, 131, 19, 2, + 73, 77, 4, 130, 250, 36, 69, 3, 81, 18, 62, 69, 30, 72, 198, 137, 32, 85, + 242, 222, 4, 79, 163, 14, 65, 4, 166, 249, 36, 69, 3, 84, 8, 42, 69, 158, + 249, 22, 79, 175, 156, 3, 73, 2, 197, 179, 12, 2, 85, 65, 28, 34, 65, 94, + 69, 50, 79, 39, 85, 10, 56, 2, 69, 78, 162, 248, 22, 65, 198, 255, 13, + 77, 3, 81, 2, 237, 219, 11, 3, 32, 78, 84, 6, 26, 85, 171, 247, 36, 78, + 5, 143, 178, 12, 65, 6, 166, 249, 34, 79, 239, 253, 1, 81, 6, 170, 7, 77, + 243, 216, 36, 65, 6, 26, 69, 223, 229, 36, 79, 4, 190, 249, 25, 85, 247, + 252, 10, 69, 6, 246, 10, 69, 179, 207, 32, 85, 26, 68, 2, 69, 85, 46, 73, + 32, 3, 79, 81, 32, 54, 85, 159, 244, 36, 65, 8, 138, 143, 24, 65, 158, + 230, 12, 77, 3, 88, 4, 166, 222, 36, 69, 215, 22, 84, 4, 244, 205, 12, 4, + 83, 87, 73, 77, 191, 201, 9, 67, 8, 142, 145, 26, 69, 150, 141, 9, 65, + 134, 214, 1, 78, 3, 81, 108, 162, 1, 75, 82, 76, 46, 77, 98, 78, 190, 1, + 80, 66, 82, 50, 83, 110, 84, 38, 89, 130, 228, 2, 87, 152, 196, 9, 2, 86, + 85, 198, 174, 24, 69, 242, 5, 70, 231, 16, 85, 14, 178, 152, 18, 69, 246, + 231, 16, 89, 234, 239, 1, 80, 186, 2, 65, 2, 79, 3, 85, 6, 222, 142, 26, + 79, 154, 227, 10, 65, 3, 73, 13, 42, 66, 34, 69, 130, 241, 36, 65, 3, 79, + 4, 190, 161, 36, 69, 171, 77, 65, 2, 223, 254, 34, 69, 22, 94, 71, 38, + 74, 38, 85, 158, 242, 34, 83, 246, 7, 68, 150, 3, 84, 202, 222, 1, 89, + 219, 19, 73, 4, 182, 128, 32, 75, 159, 237, 4, 71, 4, 242, 242, 25, 85, + 203, 234, 10, 65, 5, 239, 216, 36, 65, 6, 26, 69, 163, 242, 25, 85, 4, + 210, 224, 35, 85, 195, 142, 1, 69, 12, 186, 2, 69, 254, 137, 9, 73, 187, + 226, 27, 85, 14, 66, 72, 230, 2, 69, 138, 134, 19, 65, 170, 192, 17, 85, + 235, 36, 73, 6, 162, 218, 36, 73, 218, 19, 79, 3, 85, 6, 214, 155, 21, + 65, 211, 181, 15, 69, 4, 150, 138, 26, 79, 155, 227, 10, 65, 4, 182, 214, + 36, 65, 215, 22, 69, 14, 54, 69, 254, 137, 9, 73, 230, 203, 27, 65, 215, + 22, 85, 6, 242, 221, 35, 85, 194, 142, 1, 69, 3, 78, 16, 62, 72, 50, 69, + 138, 134, 19, 65, 170, 192, 17, 85, 235, 36, 73, 8, 46, 69, 194, 215, 36, + 73, 218, 19, 79, 3, 85, 2, 215, 220, 35, 85, 10, 238, 144, 18, 69, 154, + 136, 3, 65, 255, 209, 15, 73, 6, 182, 135, 26, 79, 2, 85, 155, 227, 10, + 65, 14, 42, 75, 226, 171, 35, 65, 167, 159, 1, 74, 11, 49, 10, 78, 79, + 84, 69, 32, 87, 73, 84, 72, 32, 8, 172, 212, 9, 2, 80, 79, 198, 1, 89, + 192, 186, 12, 2, 69, 85, 247, 177, 6, 68, 6, 38, 32, 149, 253, 16, 3, 66, + 69, 82, 4, 250, 216, 29, 67, 201, 252, 5, 5, 79, 70, 32, 83, 79, 78, 72, + 3, 75, 69, 84, 60, 7, 83, 65, 32, 86, 65, 72, 32, 207, 220, 34, 69, 5, + 193, 165, 16, 10, 66, 65, 76, 76, 32, 65, 78, 68, 32, 72, 72, 104, 10, 67, 79, 77, 66, 73, 78, 73, 78, 71, 32, 164, 1, 7, 76, 69, 84, 84, 69, - 82, 32, 151, 246, 33, 70, 10, 52, 4, 72, 73, 71, 72, 44, 3, 76, 79, 87, - 39, 77, 4, 236, 219, 21, 2, 45, 76, 159, 252, 10, 32, 4, 32, 2, 45, 77, - 191, 215, 32, 32, 2, 173, 215, 32, 2, 73, 68, 60, 238, 1, 68, 38, 69, 38, - 71, 34, 75, 38, 85, 20, 2, 87, 65, 22, 89, 212, 228, 29, 2, 72, 87, 230, - 191, 1, 77, 190, 218, 2, 79, 250, 160, 2, 86, 246, 5, 74, 2, 84, 2, 90, - 162, 8, 67, 2, 83, 158, 20, 66, 2, 70, 2, 80, 186, 2, 65, 3, 73, 4, 138, - 131, 34, 72, 255, 194, 2, 79, 7, 222, 201, 31, 78, 135, 252, 4, 69, 4, - 214, 160, 36, 66, 219, 35, 65, 4, 158, 229, 29, 80, 131, 224, 6, 65, 5, - 147, 160, 36, 87, 5, 227, 159, 36, 68, 4, 254, 196, 35, 69, 131, 105, 73, - 121, 48, 3, 65, 75, 32, 230, 10, 72, 143, 174, 22, 84, 112, 196, 1, 15, + 82, 32, 203, 143, 34, 70, 10, 52, 4, 72, 73, 71, 72, 44, 3, 76, 79, 87, + 39, 77, 4, 136, 234, 21, 2, 45, 76, 179, 134, 11, 32, 4, 32, 2, 45, 77, + 239, 239, 32, 32, 2, 221, 239, 32, 2, 73, 68, 60, 238, 1, 68, 38, 69, 38, + 71, 34, 75, 38, 85, 20, 2, 87, 65, 22, 89, 216, 243, 29, 2, 72, 87, 154, + 197, 1, 77, 186, 223, 2, 79, 202, 164, 2, 86, 246, 5, 74, 2, 84, 2, 90, + 162, 8, 67, 2, 83, 158, 20, 66, 2, 70, 2, 80, 186, 2, 65, 3, 73, 4, 190, + 156, 34, 72, 207, 198, 2, 79, 7, 142, 222, 31, 78, 219, 132, 5, 69, 4, + 218, 189, 36, 66, 219, 35, 65, 4, 162, 244, 29, 80, 131, 238, 6, 65, 5, + 151, 189, 36, 87, 5, 231, 188, 36, 68, 4, 130, 226, 35, 69, 131, 105, 73, + 121, 48, 3, 65, 75, 32, 230, 10, 72, 231, 183, 22, 84, 112, 196, 1, 15, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 28, 7, 76, 69, 84, 84, 69, 82, 32, 248, 4, 3, 80, 65, 78, 50, 83, 141, 2, 11, 86, - 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 4, 226, 193, 36, 78, 87, 72, 76, - 194, 1, 77, 114, 78, 68, 2, 80, 65, 38, 83, 196, 222, 18, 4, 75, 65, 82, - 79, 134, 222, 17, 66, 2, 67, 2, 68, 2, 71, 2, 72, 2, 74, 2, 76, 2, 82, 2, - 87, 2, 89, 186, 2, 65, 2, 73, 3, 85, 10, 26, 65, 135, 190, 36, 66, 9, 45, - 9, 78, 68, 65, 73, 76, 73, 78, 71, 32, 6, 210, 189, 36, 72, 2, 78, 3, 83, - 10, 152, 2, 2, 79, 82, 150, 187, 36, 68, 2, 71, 2, 89, 187, 2, 65, 5, - 189, 244, 20, 4, 75, 80, 65, 75, 24, 80, 10, 73, 77, 65, 76, 85, 78, 71, - 85, 78, 32, 96, 2, 79, 85, 207, 189, 36, 65, 20, 242, 187, 36, 71, 2, 72, - 2, 76, 2, 77, 2, 80, 2, 82, 2, 83, 2, 87, 2, 89, 187, 2, 65, 2, 237, 222, - 18, 5, 84, 72, 69, 82, 78, 4, 166, 2, 71, 253, 222, 18, 4, 79, 78, 71, - 79, 10, 96, 12, 89, 77, 66, 79, 76, 32, 66, 73, 78, 68, 85, 32, 249, 179, - 10, 6, 73, 71, 78, 32, 84, 79, 8, 78, 80, 136, 159, 11, 3, 74, 85, 68, - 149, 153, 25, 6, 78, 65, 32, 77, 69, 84, 4, 64, 3, 65, 78, 71, 241, 156, - 23, 7, 73, 78, 65, 82, 66, 79, 82, 2, 151, 228, 25, 79, 18, 122, 85, 252, - 146, 26, 6, 80, 65, 75, 80, 65, 75, 232, 250, 2, 5, 75, 65, 82, 79, 32, - 234, 235, 6, 69, 162, 64, 73, 3, 79, 5, 241, 187, 30, 15, 32, 70, 79, 82, - 32, 83, 73, 77, 65, 76, 85, 78, 71, 85, 78, 5, 195, 219, 23, 84, 254, 1, - 134, 1, 65, 230, 2, 69, 50, 76, 130, 1, 78, 190, 10, 84, 224, 228, 32, 2, - 67, 65, 248, 183, 2, 5, 86, 69, 82, 65, 71, 215, 140, 1, 68, 20, 136, 1, - 4, 77, 69, 68, 32, 170, 1, 82, 136, 169, 3, 10, 67, 72, 32, 87, 73, 84, - 72, 32, 85, 77, 178, 158, 7, 84, 166, 176, 25, 86, 19, 78, 8, 86, 65, 0, - 2, 68, 69, 52, 4, 69, 73, 71, 72, 1, 7, 83, 73, 88, 84, 69, 69, 78, 2, - 197, 164, 20, 8, 83, 67, 69, 78, 68, 73, 78, 71, 2, 189, 164, 20, 2, 84, - 72, 4, 220, 181, 32, 3, 68, 69, 68, 215, 194, 3, 32, 4, 172, 197, 8, 3, - 82, 32, 77, 195, 203, 26, 84, 13, 11, 76, 11, 38, 32, 149, 156, 25, 3, - 72, 79, 80, 6, 238, 185, 12, 80, 152, 160, 16, 6, 87, 73, 84, 72, 32, 67, - 159, 199, 6, 83, 208, 1, 84, 5, 71, 65, 76, 73, 32, 158, 9, 84, 37, 9, - 90, 69, 78, 69, 32, 82, 73, 78, 71, 200, 1, 210, 1, 65, 40, 9, 67, 85, - 82, 82, 69, 78, 67, 89, 32, 148, 2, 7, 76, 69, 84, 84, 69, 82, 32, 204, - 3, 6, 82, 85, 80, 69, 69, 32, 34, 83, 250, 213, 22, 73, 134, 4, 86, 242, - 223, 11, 68, 249, 107, 3, 71, 65, 78, 6, 154, 154, 32, 66, 50, 78, 187, - 57, 85, 12, 120, 10, 78, 85, 77, 69, 82, 65, 84, 79, 82, 32, 209, 170, - 23, 14, 68, 69, 78, 79, 77, 73, 78, 65, 84, 79, 82, 32, 83, 73, 10, 52, - 3, 79, 78, 69, 218, 156, 31, 70, 163, 163, 3, 84, 5, 157, 177, 29, 19, - 32, 76, 69, 83, 83, 32, 84, 72, 65, 78, 32, 84, 72, 69, 32, 68, 69, 78, - 79, 108, 226, 1, 75, 90, 82, 146, 184, 21, 86, 138, 138, 8, 89, 134, 144, - 3, 65, 38, 68, 114, 84, 230, 5, 85, 206, 201, 1, 73, 162, 193, 1, 78, 46, - 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 80, 138, 69, 72, 2, 76, 2, 77, 186, - 2, 69, 3, 79, 8, 26, 72, 251, 172, 36, 65, 6, 26, 65, 219, 254, 13, 73, - 5, 253, 205, 18, 3, 78, 68, 65, 10, 34, 65, 226, 169, 36, 72, 3, 82, 7, - 33, 6, 32, 87, 73, 84, 72, 32, 4, 180, 148, 25, 5, 76, 79, 87, 69, 82, 1, - 6, 77, 73, 68, 68, 76, 69, 4, 194, 164, 35, 83, 191, 69, 77, 20, 116, 19, - 69, 81, 85, 69, 78, 67, 69, 32, 70, 79, 82, 32, 76, 69, 84, 84, 69, 82, - 32, 182, 255, 25, 65, 219, 149, 6, 73, 6, 158, 216, 22, 82, 155, 207, 13, - 89, 4, 226, 146, 26, 32, 199, 138, 9, 79, 5, 133, 179, 33, 3, 32, 87, 73, - 4, 182, 213, 34, 87, 219, 64, 32, 194, 1, 184, 2, 7, 76, 69, 84, 84, 69, - 82, 32, 244, 1, 7, 78, 85, 77, 66, 69, 82, 32, 72, 5, 83, 73, 71, 78, 32, - 48, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 142, 245, 25, 68, - 170, 243, 3, 87, 152, 186, 2, 10, 71, 65, 80, 32, 70, 73, 76, 76, 69, 82, - 205, 182, 3, 12, 72, 85, 78, 68, 82, 69, 68, 83, 32, 85, 78, 73, 92, 210, - 1, 86, 218, 202, 32, 65, 38, 68, 114, 84, 230, 5, 85, 206, 201, 1, 73, - 162, 193, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, - 69, 72, 2, 76, 2, 77, 2, 82, 2, 89, 186, 2, 69, 3, 79, 8, 234, 1, 79, - 143, 163, 36, 65, 36, 142, 126, 69, 38, 70, 66, 78, 26, 83, 138, 173, 21, - 84, 155, 223, 12, 79, 10, 206, 143, 32, 67, 210, 61, 65, 231, 147, 3, 86, - 24, 80, 2, 86, 79, 194, 207, 32, 65, 38, 85, 206, 201, 1, 73, 222, 137, - 2, 69, 3, 79, 6, 33, 6, 67, 65, 76, 73, 67, 32, 6, 158, 208, 32, 82, 203, - 210, 3, 76, 26, 148, 1, 4, 67, 89, 67, 76, 36, 2, 71, 32, 28, 2, 76, 76, - 46, 82, 66, 84, 204, 166, 19, 4, 79, 72, 65, 90, 176, 253, 11, 2, 75, 73, - 155, 172, 4, 83, 4, 174, 183, 32, 73, 255, 233, 3, 69, 4, 178, 139, 34, - 82, 67, 83, 4, 216, 215, 9, 2, 69, 68, 199, 235, 23, 73, 4, 140, 184, 32, - 7, 84, 72, 68, 65, 89, 32, 67, 179, 232, 3, 68, 4, 188, 211, 13, 2, 67, - 79, 137, 203, 22, 4, 73, 78, 71, 32, 192, 7, 42, 65, 174, 33, 79, 165, - 11, 2, 85, 69, 242, 2, 32, 2, 67, 75, 247, 236, 17, 78, 240, 2, 22, 32, - 223, 31, 45, 224, 2, 210, 1, 67, 254, 4, 68, 174, 2, 70, 102, 72, 82, 76, - 186, 4, 77, 250, 2, 78, 38, 80, 46, 82, 150, 4, 83, 154, 4, 84, 82, 85, - 248, 2, 3, 86, 69, 82, 214, 128, 12, 79, 156, 205, 20, 3, 66, 79, 87, - 251, 247, 1, 81, 98, 196, 1, 5, 73, 82, 67, 76, 69, 200, 1, 6, 85, 82, - 86, 69, 68, 32, 224, 251, 25, 12, 82, 79, 83, 83, 32, 79, 78, 32, 83, 72, - 73, 69, 180, 158, 3, 5, 69, 78, 84, 82, 69, 230, 161, 5, 72, 211, 13, 76, - 11, 11, 32, 8, 72, 5, 87, 73, 84, 72, 32, 217, 235, 16, 7, 70, 79, 82, - 32, 82, 69, 67, 6, 140, 69, 8, 87, 72, 73, 84, 69, 32, 68, 79, 198, 242, - 18, 68, 161, 146, 2, 8, 84, 87, 79, 32, 87, 72, 73, 84, 16, 84, 4, 68, - 79, 87, 78, 0, 2, 85, 80, 56, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, 4, - 229, 252, 31, 9, 87, 65, 82, 68, 83, 32, 65, 78, 68, 4, 53, 11, 84, 87, - 65, 82, 68, 83, 32, 65, 78, 68, 32, 4, 158, 143, 21, 85, 231, 154, 12, - 68, 30, 64, 6, 73, 65, 77, 79, 78, 68, 152, 1, 3, 79, 87, 78, 43, 82, 13, - 11, 32, 10, 154, 19, 67, 244, 250, 12, 10, 77, 73, 78, 85, 83, 32, 87, - 72, 73, 84, 152, 166, 6, 6, 87, 73, 84, 72, 32, 68, 206, 161, 15, 79, - 135, 13, 83, 12, 214, 19, 32, 62, 45, 247, 167, 31, 87, 6, 212, 182, 34, - 2, 79, 80, 231, 20, 65, 8, 18, 76, 39, 79, 4, 206, 234, 27, 79, 155, 170, - 8, 65, 4, 252, 217, 2, 2, 85, 82, 151, 155, 31, 76, 12, 38, 69, 218, 206, - 34, 65, 251, 2, 79, 6, 160, 208, 34, 2, 65, 82, 247, 11, 88, 40, 64, 5, - 65, 82, 71, 69, 32, 140, 2, 3, 69, 70, 84, 203, 1, 79, 12, 48, 6, 67, 73, - 82, 67, 76, 69, 203, 242, 34, 83, 11, 37, 7, 32, 77, 73, 78, 85, 83, 32, - 8, 54, 76, 36, 4, 82, 73, 71, 72, 13, 3, 85, 80, 80, 4, 32, 2, 69, 70, - 13, 2, 79, 87, 2, 31, 84, 2, 17, 2, 69, 82, 2, 173, 147, 33, 8, 32, 81, - 85, 65, 82, 84, 69, 82, 22, 96, 10, 45, 80, 79, 73, 78, 84, 73, 78, 71, - 32, 64, 6, 87, 65, 82, 68, 83, 32, 235, 207, 34, 32, 12, 146, 7, 68, 190, - 8, 73, 190, 195, 34, 80, 206, 24, 83, 51, 84, 4, 242, 164, 33, 69, 255, - 139, 1, 66, 6, 158, 15, 87, 175, 203, 34, 90, 30, 76, 6, 69, 68, 73, 85, - 77, 32, 245, 222, 21, 7, 79, 79, 78, 32, 76, 73, 76, 28, 66, 68, 42, 76, - 36, 4, 82, 73, 71, 72, 12, 2, 85, 80, 111, 83, 6, 84, 3, 79, 87, 78, 163, - 209, 34, 73, 6, 32, 2, 69, 70, 195, 216, 34, 79, 4, 11, 84, 4, 81, 18, - 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 84, 82, 73, 65, 78, 71, 76, 69, - 5, 145, 9, 2, 32, 67, 8, 198, 13, 77, 247, 222, 34, 81, 4, 238, 145, 17, - 69, 155, 191, 17, 73, 8, 238, 135, 25, 85, 158, 201, 9, 65, 87, 69, 34, - 52, 4, 73, 71, 72, 84, 254, 252, 33, 79, 155, 85, 69, 30, 94, 32, 84, 10, - 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 245, 1, 6, 87, 65, 82, 68, 83, - 32, 6, 60, 9, 84, 82, 73, 65, 78, 71, 76, 69, 32, 171, 227, 34, 80, 2, - 231, 204, 6, 67, 16, 90, 68, 88, 8, 84, 82, 73, 65, 78, 71, 76, 69, 230, - 7, 73, 194, 200, 34, 80, 203, 19, 83, 4, 65, 14, 79, 85, 66, 76, 69, 32, - 84, 82, 73, 65, 78, 71, 76, 69, 5, 207, 189, 12, 32, 5, 205, 229, 20, 11, - 32, 87, 73, 84, 72, 32, 68, 79, 85, 66, 76, 8, 230, 158, 19, 65, 162, - 254, 13, 69, 255, 139, 1, 66, 40, 158, 2, 77, 148, 1, 5, 81, 85, 65, 82, - 69, 180, 253, 25, 3, 85, 78, 32, 172, 223, 1, 5, 75, 85, 76, 76, 32, 160, - 178, 3, 3, 78, 79, 87, 168, 223, 1, 5, 65, 70, 69, 84, 89, 192, 131, 1, - 13, 76, 73, 71, 72, 84, 76, 89, 32, 83, 77, 65, 76, 76, 254, 91, 67, 50, - 72, 222, 1, 80, 175, 19, 84, 12, 40, 4, 65, 76, 76, 32, 203, 208, 34, 73, - 10, 214, 200, 34, 68, 150, 7, 76, 70, 83, 213, 15, 13, 85, 80, 45, 80, - 79, 73, 78, 84, 73, 78, 71, 32, 67, 9, 11, 32, 6, 54, 67, 208, 178, 33, - 3, 70, 79, 82, 163, 158, 1, 66, 2, 181, 248, 30, 3, 69, 78, 84, 14, 192, - 4, 3, 73, 78, 89, 242, 174, 23, 82, 154, 165, 11, 79, 54, 69, 135, 2, 87, - 18, 38, 80, 229, 213, 32, 3, 78, 73, 86, 16, 46, 32, 62, 45, 170, 1, 80, - 207, 166, 31, 87, 2, 233, 219, 34, 10, 80, 79, 73, 78, 84, 73, 78, 71, - 32, 66, 8, 45, 9, 80, 79, 73, 78, 84, 73, 78, 71, 32, 8, 66, 73, 156, - 216, 34, 5, 68, 79, 85, 66, 76, 238, 3, 83, 51, 84, 2, 213, 218, 33, 8, - 83, 79, 83, 67, 69, 76, 69, 83, 4, 21, 3, 69, 82, 32, 4, 210, 182, 24, - 76, 207, 163, 9, 82, 10, 56, 6, 84, 73, 67, 65, 76, 32, 41, 4, 89, 32, - 83, 77, 4, 152, 198, 34, 2, 82, 69, 239, 22, 69, 6, 21, 3, 65, 76, 76, 6, - 11, 32, 6, 186, 194, 34, 68, 150, 7, 76, 247, 20, 83, 16, 84, 15, 76, 69, - 84, 84, 69, 82, 32, 67, 65, 80, 73, 84, 65, 76, 32, 227, 146, 11, 70, 10, - 154, 254, 35, 67, 2, 72, 2, 73, 2, 82, 3, 90, 200, 4, 52, 3, 67, 75, 32, - 166, 151, 2, 83, 183, 201, 10, 87, 196, 4, 80, 7, 79, 67, 84, 65, 78, 84, - 45, 149, 7, 8, 83, 69, 88, 84, 65, 78, 84, 45, 204, 3, 58, 49, 130, 3, - 50, 114, 53, 50, 54, 18, 51, 215, 1, 52, 234, 1, 74, 50, 246, 1, 51, 174, - 91, 52, 46, 53, 38, 54, 30, 55, 187, 157, 35, 56, 116, 62, 51, 226, 92, - 52, 46, 53, 38, 54, 30, 55, 187, 157, 35, 56, 55, 54, 52, 214, 92, 53, - 38, 54, 30, 55, 187, 157, 35, 56, 22, 50, 53, 166, 2, 54, 190, 90, 55, - 187, 157, 35, 56, 11, 42, 54, 210, 220, 28, 55, 159, 157, 7, 56, 4, 234, - 249, 35, 55, 3, 56, 56, 170, 1, 53, 50, 54, 210, 89, 52, 110, 55, 187, - 157, 35, 56, 118, 62, 52, 254, 89, 51, 98, 53, 38, 54, 30, 55, 187, 157, - 35, 56, 24, 46, 53, 50, 54, 190, 90, 55, 187, 157, 35, 56, 13, 206, 1, - 54, 186, 217, 28, 55, 159, 157, 7, 56, 7, 187, 90, 55, 61, 54, 52, 118, - 53, 230, 88, 54, 30, 55, 187, 157, 35, 56, 31, 46, 53, 170, 89, 54, 30, - 55, 187, 157, 35, 56, 15, 38, 54, 158, 89, 55, 187, 157, 35, 56, 7, 210, - 246, 35, 55, 3, 56, 14, 226, 88, 54, 30, 55, 187, 157, 35, 56, 31, 46, - 54, 234, 87, 53, 66, 55, 187, 157, 35, 56, 6, 166, 88, 55, 187, 157, 35, - 56, 120, 70, 49, 238, 1, 50, 190, 188, 25, 51, 38, 52, 30, 53, 147, 182, - 10, 54, 61, 58, 50, 126, 51, 150, 189, 25, 52, 30, 53, 147, 182, 10, 54, - 31, 50, 51, 222, 189, 25, 52, 30, 53, 147, 182, 10, 54, 15, 42, 52, 206, - 189, 25, 53, 147, 182, 10, 54, 7, 218, 243, 35, 53, 3, 54, 15, 146, 189, - 25, 52, 166, 153, 3, 53, 139, 157, 7, 54, 31, 50, 52, 138, 188, 25, 51, - 66, 53, 147, 182, 10, 54, 7, 199, 188, 25, 53, 6, 190, 153, 22, 32, 237, - 195, 8, 4, 66, 69, 82, 82, 232, 4, 200, 1, 3, 76, 68, 32, 78, 79, 104, 7, - 80, 79, 77, 79, 70, 79, 32, 188, 6, 2, 84, 84, 216, 6, 5, 85, 81, 85, 69, - 84, 54, 87, 198, 1, 88, 206, 215, 5, 77, 178, 168, 3, 89, 170, 167, 26, - 65, 139, 34, 78, 14, 254, 190, 8, 83, 142, 204, 7, 69, 190, 207, 17, 70, - 234, 2, 87, 215, 10, 71, 10, 26, 75, 215, 144, 23, 77, 9, 40, 4, 77, 65, - 82, 75, 175, 239, 35, 83, 5, 133, 171, 23, 3, 32, 84, 65, 150, 1, 96, 13, - 70, 73, 78, 65, 76, 32, 76, 69, 84, 84, 69, 82, 32, 53, 7, 76, 69, 84, - 84, 69, 82, 32, 10, 162, 238, 35, 71, 2, 72, 2, 75, 2, 80, 3, 84, 140, 1, - 234, 1, 65, 54, 85, 22, 69, 82, 71, 46, 73, 70, 78, 38, 79, 98, 90, 170, - 154, 7, 75, 178, 174, 24, 67, 2, 76, 2, 83, 246, 76, 66, 206, 201, 1, 74, - 222, 137, 2, 68, 2, 70, 2, 72, 2, 77, 2, 80, 2, 81, 2, 82, 2, 84, 2, 86, - 3, 88, 21, 50, 73, 2, 85, 74, 78, 134, 235, 35, 72, 3, 77, 5, 235, 155, - 35, 78, 17, 50, 78, 134, 235, 35, 69, 2, 72, 2, 73, 3, 82, 7, 130, 235, - 35, 71, 3, 78, 11, 230, 234, 35, 72, 2, 78, 2, 85, 3, 87, 15, 180, 239, - 33, 2, 78, 78, 134, 251, 1, 72, 2, 77, 2, 82, 3, 85, 9, 222, 238, 33, 71, - 155, 251, 1, 78, 17, 66, 78, 142, 232, 34, 32, 134, 129, 1, 69, 2, 77, 2, - 79, 3, 85, 4, 142, 233, 35, 71, 3, 78, 9, 242, 232, 35, 72, 2, 73, 3, 89, - 66, 104, 3, 79, 77, 32, 133, 179, 21, 17, 76, 69, 32, 87, 73, 84, 72, 32, - 80, 79, 80, 80, 73, 78, 71, 32, 67, 64, 152, 2, 5, 72, 65, 76, 70, 32, - 232, 1, 5, 76, 69, 70, 84, 32, 88, 6, 82, 73, 71, 72, 84, 32, 88, 14, 83, - 81, 85, 65, 82, 69, 32, 66, 82, 65, 67, 75, 69, 84, 206, 213, 15, 65, - 226, 134, 16, 67, 210, 3, 80, 140, 3, 13, 74, 85, 83, 84, 73, 70, 73, 69, - 68, 32, 85, 80, 80, 135, 5, 84, 32, 148, 1, 16, 70, 79, 82, 87, 65, 82, - 68, 45, 70, 65, 67, 73, 78, 71, 32, 82, 130, 225, 31, 76, 22, 82, 252, 2, - 2, 83, 84, 174, 5, 66, 231, 243, 1, 73, 10, 252, 153, 29, 11, 85, 78, 78, - 69, 82, 32, 70, 82, 65, 77, 69, 223, 200, 2, 79, 8, 192, 230, 31, 13, 74, - 85, 83, 84, 73, 70, 73, 69, 68, 32, 85, 80, 80, 126, 67, 51, 72, 8, 230, - 230, 31, 67, 50, 72, 33, 13, 74, 85, 83, 84, 73, 70, 73, 69, 68, 32, 85, - 80, 80, 5, 225, 214, 23, 9, 32, 79, 86, 69, 82, 32, 84, 79, 80, 5, 149, - 211, 34, 8, 32, 79, 70, 32, 70, 76, 79, 87, 14, 58, 76, 116, 3, 84, 73, - 69, 141, 134, 33, 3, 32, 65, 78, 6, 26, 32, 247, 143, 35, 73, 4, 148, - 183, 9, 7, 79, 70, 32, 72, 89, 71, 73, 245, 130, 23, 6, 87, 73, 84, 72, - 32, 83, 7, 207, 195, 32, 32, 218, 2, 88, 10, 32, 68, 82, 65, 87, 73, 78, - 71, 83, 32, 193, 226, 34, 6, 73, 78, 71, 32, 71, 76, 216, 2, 176, 1, 2, - 68, 79, 228, 6, 6, 72, 69, 65, 86, 89, 32, 254, 2, 76, 204, 25, 6, 82, - 73, 71, 72, 84, 32, 144, 4, 3, 85, 80, 32, 245, 3, 9, 86, 69, 82, 84, 73, - 67, 65, 76, 32, 66, 52, 5, 85, 66, 76, 69, 32, 165, 3, 3, 87, 78, 32, 30, - 58, 68, 216, 2, 2, 85, 80, 234, 5, 86, 211, 163, 29, 72, 14, 64, 8, 73, - 65, 71, 79, 78, 65, 76, 32, 149, 2, 3, 79, 87, 78, 8, 104, 6, 85, 80, 80, - 69, 82, 32, 205, 14, 15, 76, 79, 87, 69, 82, 32, 76, 69, 70, 84, 32, 84, - 79, 32, 77, 6, 76, 8, 76, 69, 70, 84, 32, 84, 79, 32, 237, 24, 6, 82, 73, - 71, 72, 84, 32, 4, 204, 23, 14, 77, 73, 68, 68, 76, 69, 32, 67, 69, 78, - 84, 82, 69, 32, 159, 173, 23, 76, 6, 199, 26, 32, 36, 128, 1, 10, 72, 69, - 65, 86, 89, 32, 65, 78, 68, 32, 132, 1, 10, 76, 73, 71, 72, 84, 32, 65, - 78, 68, 32, 210, 38, 68, 131, 4, 83, 12, 76, 3, 76, 69, 70, 0, 4, 82, 73, - 71, 72, 228, 35, 2, 85, 80, 151, 5, 72, 4, 17, 2, 84, 32, 4, 230, 32, 85, - 239, 140, 35, 76, 12, 76, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 156, 36, - 2, 85, 80, 235, 4, 72, 4, 17, 2, 84, 32, 4, 198, 32, 85, 135, 9, 72, 46, - 136, 1, 4, 76, 69, 70, 84, 68, 2, 85, 80, 130, 1, 86, 172, 20, 2, 68, 79, - 170, 2, 81, 100, 2, 84, 82, 154, 140, 29, 72, 151, 134, 6, 82, 5, 45, 9, - 32, 65, 78, 68, 32, 76, 73, 71, 72, 2, 195, 132, 34, 84, 11, 29, 5, 32, - 65, 78, 68, 32, 8, 42, 76, 134, 164, 29, 72, 151, 134, 6, 82, 4, 224, - 174, 24, 4, 73, 71, 72, 84, 167, 251, 10, 69, 8, 209, 20, 7, 69, 82, 84, - 73, 67, 65, 76, 156, 1, 56, 4, 69, 70, 84, 32, 169, 2, 5, 73, 71, 72, 84, - 32, 16, 160, 27, 19, 68, 79, 87, 78, 32, 72, 69, 65, 86, 89, 32, 65, 78, - 68, 32, 82, 73, 71, 72, 24, 14, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, - 82, 73, 71, 72, 100, 14, 76, 73, 71, 72, 84, 32, 65, 78, 68, 32, 82, 73, - 71, 72, 97, 17, 85, 80, 32, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 82, - 73, 71, 72, 140, 1, 248, 1, 4, 65, 82, 67, 32, 30, 68, 248, 15, 10, 72, - 79, 82, 73, 90, 79, 78, 84, 65, 76, 112, 4, 76, 69, 70, 84, 62, 81, 34, - 84, 172, 1, 2, 85, 80, 120, 8, 86, 69, 82, 84, 73, 67, 65, 76, 192, 197, - 13, 7, 66, 79, 84, 84, 79, 77, 32, 143, 203, 21, 82, 8, 230, 202, 17, 68, - 67, 85, 80, 52, 8, 73, 65, 71, 79, 78, 65, 76, 32, 199, 14, 79, 68, 168, - 1, 14, 76, 79, 87, 69, 82, 32, 76, 69, 70, 84, 32, 84, 79, 32, 96, 7, 77, - 73, 68, 68, 76, 69, 32, 136, 3, 6, 85, 80, 80, 69, 82, 32, 162, 136, 34, - 67, 183, 4, 68, 4, 38, 77, 33, 5, 85, 80, 80, 69, 82, 2, 29, 5, 73, 68, - 68, 76, 69, 2, 129, 12, 2, 32, 67, 16, 88, 8, 76, 69, 70, 84, 32, 84, 79, - 32, 213, 1, 9, 82, 73, 71, 72, 84, 32, 84, 79, 32, 10, 156, 1, 6, 76, 79, - 87, 69, 82, 32, 33, 28, 85, 80, 80, 69, 82, 32, 67, 69, 78, 84, 82, 69, - 32, 84, 79, 32, 77, 73, 68, 68, 76, 69, 32, 82, 73, 71, 72, 84, 6, 246, - 3, 67, 255, 155, 35, 82, 5, 135, 191, 20, 32, 6, 232, 4, 15, 85, 80, 80, - 69, 82, 32, 67, 69, 78, 84, 82, 69, 32, 84, 79, 235, 3, 76, 44, 144, 1, - 10, 67, 69, 78, 84, 82, 69, 32, 84, 79, 32, 248, 3, 8, 76, 69, 70, 84, - 32, 84, 79, 32, 193, 2, 9, 82, 73, 71, 72, 84, 32, 84, 79, 32, 20, 52, 7, - 77, 73, 68, 68, 76, 69, 32, 171, 176, 23, 76, 16, 56, 4, 76, 69, 70, 84, - 165, 1, 5, 82, 73, 71, 72, 84, 9, 11, 32, 6, 84, 10, 84, 79, 32, 76, 79, - 87, 69, 82, 32, 67, 233, 186, 20, 5, 65, 78, 68, 32, 77, 4, 29, 5, 69, - 78, 84, 82, 69, 5, 133, 196, 32, 3, 32, 84, 79, 9, 11, 32, 6, 88, 3, 65, - 78, 68, 65, 15, 84, 79, 32, 76, 79, 87, 69, 82, 32, 67, 69, 78, 84, 82, - 69, 2, 241, 185, 20, 11, 32, 77, 73, 68, 68, 76, 69, 32, 76, 69, 70, 5, - 189, 211, 10, 9, 32, 84, 79, 32, 77, 73, 68, 68, 76, 14, 68, 6, 76, 79, - 87, 69, 82, 32, 97, 7, 77, 73, 68, 68, 76, 69, 32, 6, 48, 6, 67, 69, 78, - 84, 82, 69, 227, 152, 35, 82, 5, 11, 32, 2, 233, 4, 4, 84, 79, 32, 85, 8, - 76, 10, 67, 69, 78, 84, 82, 69, 32, 84, 79, 32, 33, 5, 82, 73, 71, 72, - 84, 4, 250, 3, 85, 139, 201, 13, 76, 5, 11, 32, 2, 193, 204, 13, 2, 84, - 79, 10, 46, 76, 69, 7, 77, 73, 68, 68, 76, 69, 32, 4, 29, 5, 79, 87, 69, - 82, 32, 4, 198, 191, 32, 67, 235, 214, 2, 76, 6, 34, 67, 37, 4, 76, 69, - 70, 84, 2, 45, 6, 69, 78, 84, 82, 69, 32, 5, 11, 32, 2, 133, 170, 23, 2, - 84, 79, 12, 36, 2, 87, 78, 253, 2, 2, 85, 66, 9, 11, 32, 6, 25, 4, 65, - 78, 68, 32, 6, 210, 142, 29, 72, 250, 133, 6, 76, 31, 82, 9, 11, 32, 6, - 40, 4, 65, 78, 68, 32, 227, 141, 35, 87, 4, 26, 85, 179, 168, 23, 76, 2, - 193, 168, 23, 2, 80, 80, 5, 149, 237, 33, 10, 32, 65, 78, 68, 32, 72, 69, - 65, 86, 89, 4, 109, 5, 85, 65, 68, 82, 85, 6, 66, 82, 225, 199, 13, 10, - 79, 80, 32, 65, 78, 68, 32, 85, 80, 80, 4, 11, 73, 4, 11, 80, 4, 41, 8, - 76, 69, 32, 68, 65, 83, 72, 32, 4, 190, 178, 16, 86, 151, 217, 12, 72, - 11, 29, 5, 32, 65, 78, 68, 32, 8, 34, 72, 230, 144, 35, 76, 31, 82, 4, - 200, 149, 24, 4, 69, 65, 86, 89, 175, 245, 4, 79, 17, 29, 5, 32, 65, 78, - 68, 32, 14, 162, 143, 28, 66, 42, 84, 206, 122, 72, 250, 133, 6, 76, 31, - 82, 16, 148, 2, 18, 68, 79, 87, 78, 32, 72, 69, 65, 86, 89, 32, 65, 78, - 68, 32, 76, 69, 70, 24, 13, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 76, - 69, 70, 100, 13, 76, 73, 71, 72, 84, 32, 65, 78, 68, 32, 76, 69, 70, 97, - 16, 85, 80, 32, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 76, 69, 70, 2, - 101, 3, 84, 32, 85, 6, 17, 2, 84, 32, 6, 58, 85, 178, 3, 68, 245, 4, 6, - 86, 69, 82, 84, 73, 67, 2, 183, 158, 33, 80, 6, 17, 2, 84, 32, 6, 58, 85, - 134, 4, 68, 205, 4, 6, 86, 69, 82, 84, 73, 67, 2, 239, 8, 80, 2, 185, 2, - 3, 84, 32, 68, 36, 128, 1, 10, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, - 188, 1, 10, 76, 73, 71, 72, 84, 32, 65, 78, 68, 32, 186, 2, 68, 131, 4, - 83, 12, 80, 4, 68, 79, 87, 78, 24, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, - 255, 4, 72, 2, 145, 5, 2, 32, 72, 4, 17, 2, 84, 32, 4, 26, 68, 191, 137, - 35, 76, 2, 133, 155, 33, 3, 79, 87, 78, 12, 80, 4, 68, 79, 87, 78, 24, 3, - 76, 69, 70, 0, 4, 82, 73, 71, 72, 211, 4, 72, 2, 229, 4, 2, 32, 72, 4, - 17, 2, 84, 32, 4, 22, 68, 131, 5, 72, 2, 233, 4, 3, 79, 87, 78, 24, 130, - 1, 68, 188, 1, 10, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 144, 1, 10, - 76, 73, 71, 72, 84, 32, 65, 78, 68, 32, 183, 1, 83, 6, 49, 10, 79, 85, - 66, 76, 69, 32, 65, 78, 68, 32, 6, 92, 3, 76, 69, 70, 0, 4, 82, 73, 71, - 72, 13, 10, 72, 79, 82, 73, 90, 79, 78, 84, 65, 76, 2, 11, 84, 2, 233, - 231, 26, 2, 32, 83, 6, 54, 72, 68, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, - 2, 37, 7, 79, 82, 73, 90, 79, 78, 84, 2, 145, 150, 33, 2, 65, 76, 2, 247, - 149, 33, 84, 6, 54, 72, 60, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, 2, 37, - 7, 79, 82, 73, 90, 79, 78, 84, 2, 29, 2, 65, 76, 2, 11, 84, 2, 17, 2, 32, - 72, 2, 137, 156, 35, 3, 69, 65, 86, 6, 49, 10, 73, 78, 71, 76, 69, 32, - 65, 78, 68, 32, 6, 96, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 221, 171, 26, - 9, 72, 79, 82, 73, 90, 79, 78, 84, 65, 2, 231, 171, 26, 84, 134, 6, 46, - 65, 178, 14, 69, 150, 1, 73, 175, 1, 79, 232, 5, 36, 4, 72, 77, 73, 32, - 247, 9, 73, 230, 1, 192, 1, 7, 76, 69, 84, 84, 69, 82, 32, 196, 2, 7, 78, - 85, 77, 66, 69, 82, 32, 144, 2, 12, 80, 85, 78, 67, 84, 85, 65, 84, 73, - 79, 78, 32, 84, 5, 83, 73, 71, 78, 32, 122, 86, 239, 244, 24, 68, 108, - 210, 1, 79, 238, 205, 31, 65, 38, 68, 114, 84, 46, 86, 186, 5, 85, 206, - 201, 1, 73, 42, 76, 250, 192, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, - 2, 75, 2, 80, 138, 69, 72, 2, 77, 2, 82, 2, 89, 187, 2, 69, 15, 45, 9, - 76, 68, 32, 84, 65, 77, 73, 76, 32, 12, 158, 255, 28, 76, 206, 149, 2, - 83, 134, 92, 78, 175, 242, 2, 82, 42, 82, 69, 38, 70, 66, 78, 26, 83, - 138, 173, 21, 84, 238, 228, 5, 79, 203, 213, 7, 74, 4, 145, 135, 31, 4, - 73, 71, 72, 84, 8, 26, 79, 227, 147, 21, 73, 4, 142, 185, 33, 82, 159, - 180, 1, 85, 4, 65, 3, 73, 78, 69, 8, 40, 4, 69, 86, 69, 78, 1, 2, 73, 88, - 5, 215, 147, 35, 84, 10, 46, 76, 202, 208, 12, 68, 177, 16, 2, 67, 82, 4, - 198, 190, 19, 79, 235, 204, 14, 73, 12, 246, 143, 31, 67, 152, 62, 9, 79, - 76, 68, 32, 84, 65, 77, 73, 76, 230, 157, 1, 74, 158, 2, 86, 122, 85, - 187, 240, 1, 65, 34, 64, 10, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 167, - 234, 32, 73, 32, 138, 1, 79, 208, 174, 29, 11, 66, 72, 65, 84, 84, 73, - 80, 82, 79, 76, 85, 214, 159, 2, 65, 38, 85, 22, 86, 186, 201, 1, 73, - 223, 137, 2, 69, 7, 253, 142, 31, 10, 76, 68, 32, 84, 65, 77, 73, 76, 32, - 83, 130, 4, 72, 12, 76, 76, 69, 32, 80, 65, 84, 84, 69, 82, 78, 32, 231, - 160, 35, 78, 128, 4, 44, 5, 68, 79, 84, 83, 45, 171, 244, 6, 66, 254, 3, - 74, 49, 74, 50, 66, 51, 54, 52, 46, 53, 38, 54, 30, 55, 187, 157, 35, 56, - 129, 2, 66, 50, 66, 51, 54, 52, 46, 53, 38, 54, 30, 55, 187, 157, 35, 56, - 129, 1, 58, 51, 54, 52, 46, 53, 38, 54, 30, 55, 187, 157, 35, 56, 65, 50, - 52, 46, 53, 38, 54, 30, 55, 187, 157, 35, 56, 33, 42, 53, 38, 54, 30, 55, - 187, 157, 35, 56, 17, 34, 54, 30, 55, 187, 157, 35, 56, 9, 26, 55, 187, - 157, 35, 56, 5, 183, 157, 35, 56, 8, 26, 65, 183, 134, 35, 86, 6, 156, - 185, 20, 11, 75, 32, 80, 69, 82, 77, 73, 84, 84, 69, 68, 196, 171, 8, 6, - 83, 84, 45, 70, 69, 69, 167, 184, 6, 68, 10, 42, 68, 92, 2, 69, 70, 243, - 151, 35, 67, 4, 204, 161, 32, 6, 71, 69, 32, 65, 84, 32, 193, 70, 9, 69, - 32, 87, 73, 84, 72, 32, 86, 69, 4, 190, 245, 31, 67, 203, 165, 3, 83, 12, - 84, 4, 75, 69, 78, 32, 176, 192, 8, 2, 67, 67, 160, 210, 25, 2, 87, 78, - 231, 118, 79, 6, 248, 173, 27, 17, 67, 73, 82, 67, 76, 69, 32, 87, 73, - 84, 72, 32, 78, 79, 82, 84, 72, 218, 202, 6, 66, 147, 26, 72, 134, 1, - 208, 1, 4, 66, 66, 76, 69, 46, 71, 156, 4, 4, 72, 73, 68, 32, 36, 2, 76, - 76, 122, 83, 56, 4, 84, 84, 69, 82, 176, 249, 10, 10, 73, 76, 68, 73, 78, - 71, 32, 67, 79, 78, 184, 213, 16, 2, 82, 82, 247, 239, 6, 67, 4, 204, - 145, 28, 2, 32, 84, 243, 133, 7, 83, 63, 33, 6, 73, 78, 69, 83, 69, 32, - 60, 144, 1, 7, 76, 69, 84, 84, 69, 82, 32, 172, 2, 11, 86, 79, 87, 69, - 76, 32, 83, 73, 71, 78, 32, 190, 176, 16, 69, 225, 241, 13, 4, 80, 65, - 76, 76, 46, 154, 1, 77, 34, 78, 234, 145, 35, 66, 2, 67, 2, 68, 2, 71, 2, - 72, 2, 74, 2, 75, 2, 76, 2, 80, 2, 82, 2, 83, 2, 84, 2, 86, 2, 89, 187, - 2, 65, 4, 134, 146, 35, 80, 187, 2, 65, 12, 46, 71, 34, 89, 154, 145, 35, - 82, 187, 2, 65, 4, 182, 145, 35, 75, 187, 2, 65, 4, 150, 145, 35, 67, - 187, 2, 65, 10, 218, 252, 34, 65, 214, 22, 69, 2, 73, 2, 79, 3, 85, 40, - 142, 152, 10, 76, 135, 241, 18, 86, 10, 56, 2, 69, 84, 20, 4, 72, 79, 82, - 78, 231, 162, 9, 83, 5, 179, 210, 31, 32, 5, 241, 184, 27, 5, 32, 87, 73, - 84, 72, 9, 26, 84, 143, 192, 32, 32, 4, 234, 184, 27, 83, 15, 32, 5, 243, - 237, 34, 70, 240, 3, 140, 1, 23, 90, 65, 78, 84, 73, 78, 69, 32, 77, 85, - 83, 73, 67, 65, 76, 32, 83, 89, 77, 66, 79, 76, 32, 245, 179, 20, 5, 84, - 69, 32, 79, 82, 238, 3, 154, 2, 65, 128, 7, 2, 67, 72, 170, 1, 68, 158, - 5, 69, 206, 2, 70, 166, 8, 71, 202, 3, 73, 162, 2, 75, 142, 5, 76, 190, - 2, 77, 250, 4, 79, 114, 80, 174, 4, 82, 42, 83, 194, 5, 84, 196, 5, 2, - 86, 65, 250, 1, 89, 178, 160, 32, 78, 245, 142, 2, 9, 88, 73, 82, 79, 78, - 32, 75, 76, 65, 62, 60, 5, 71, 79, 71, 73, 32, 222, 1, 78, 118, 80, 199, - 2, 82, 16, 70, 65, 0, 2, 71, 79, 64, 2, 77, 69, 37, 5, 80, 79, 76, 73, - 32, 4, 17, 2, 82, 71, 4, 160, 227, 15, 2, 79, 84, 159, 169, 19, 73, 4, - 214, 151, 9, 84, 231, 224, 25, 83, 4, 26, 65, 1, 2, 71, 79, 2, 131, 203, - 17, 82, 6, 72, 6, 84, 73, 75, 69, 78, 79, 205, 233, 34, 6, 65, 84, 82, - 73, 67, 72, 4, 168, 32, 2, 75, 89, 147, 232, 34, 77, 22, 52, 5, 69, 83, - 79, 32, 69, 38, 79, 155, 246, 34, 76, 4, 204, 1, 2, 88, 79, 147, 55, 75, - 16, 80, 6, 83, 84, 82, 79, 70, 79, 188, 40, 3, 68, 69, 82, 181, 128, 1, - 2, 84, 72, 10, 24, 2, 73, 32, 79, 83, 4, 56, 9, 83, 89, 78, 68, 69, 83, - 77, 79, 83, 195, 24, 84, 2, 143, 44, 32, 7, 11, 32, 4, 214, 60, 68, 255, - 145, 22, 78, 18, 48, 2, 71, 79, 33, 6, 75, 84, 73, 75, 79, 32, 4, 170, - 21, 83, 171, 242, 34, 78, 14, 182, 190, 27, 86, 138, 170, 7, 90, 162, 8, - 75, 254, 2, 68, 2, 78, 162, 17, 71, 3, 80, 14, 80, 4, 82, 79, 65, 32, - 168, 37, 4, 79, 82, 69, 86, 221, 2, 4, 65, 77, 73, 76, 6, 228, 214, 19, - 2, 83, 80, 220, 191, 3, 3, 90, 89, 71, 185, 188, 10, 3, 75, 76, 73, 42, - 50, 73, 172, 219, 8, 2, 65, 83, 227, 138, 26, 89, 38, 122, 65, 200, 1, 5, - 69, 83, 73, 83, 32, 70, 71, 208, 1, 3, 80, 76, 73, 173, 183, 27, 8, 70, - 84, 79, 71, 71, 79, 83, 32, 10, 48, 6, 83, 84, 79, 76, 73, 32, 179, 203, - 33, 82, 8, 80, 6, 65, 80, 76, 73, 32, 77, 174, 55, 68, 141, 220, 22, 5, - 84, 72, 69, 83, 69, 4, 40, 2, 69, 71, 133, 165, 28, 2, 73, 75, 2, 195, - 135, 30, 65, 12, 38, 84, 246, 50, 65, 42, 68, 63, 77, 6, 194, 10, 69, - 239, 41, 82, 10, 72, 5, 79, 82, 71, 79, 78, 217, 128, 35, 7, 82, 65, 77, - 77, 65, 32, 71, 9, 69, 15, 32, 80, 65, 82, 69, 83, 84, 73, 71, 77, 69, - 78, 79, 78, 32, 6, 222, 38, 68, 137, 10, 8, 65, 82, 73, 83, 84, 69, 82, - 65, 5, 175, 47, 32, 16, 166, 1, 78, 116, 6, 84, 69, 82, 79, 78, 32, 188, - 44, 4, 88, 79, 32, 69, 156, 238, 32, 4, 80, 69, 71, 69, 152, 48, 6, 75, - 83, 84, 82, 69, 80, 237, 13, 3, 76, 65, 70, 4, 224, 22, 3, 68, 79, 70, - 145, 159, 27, 18, 65, 82, 88, 73, 83, 32, 75, 65, 73, 32, 70, 84, 72, 79, - 82, 65, 32, 86, 4, 208, 11, 5, 65, 82, 71, 79, 83, 151, 20, 80, 44, 180, - 1, 9, 65, 78, 69, 82, 79, 83, 73, 83, 32, 52, 6, 84, 72, 79, 82, 65, 32, - 165, 6, 22, 72, 84, 79, 82, 65, 32, 83, 75, 76, 73, 82, 79, 78, 32, 67, - 72, 82, 79, 77, 65, 32, 86, 6, 246, 4, 68, 14, 77, 25, 5, 84, 69, 84, 82, - 65, 36, 220, 2, 8, 65, 82, 67, 72, 65, 73, 79, 78, 80, 10, 68, 73, 65, - 84, 79, 78, 73, 75, 73, 32, 96, 11, 73, 32, 89, 70, 69, 83, 73, 83, 32, - 84, 69, 32, 15, 77, 65, 76, 65, 75, 79, 78, 32, 67, 72, 82, 79, 77, 65, - 32, 74, 78, 40, 8, 83, 75, 76, 73, 82, 79, 78, 32, 141, 209, 18, 18, 69, - 78, 65, 82, 77, 79, 78, 73, 79, 83, 32, 65, 78, 84, 73, 70, 79, 78, 5, - 57, 12, 32, 68, 69, 89, 84, 69, 82, 79, 85, 32, 73, 67, 2, 199, 175, 27, - 72, 14, 62, 78, 254, 216, 34, 90, 162, 8, 75, 254, 2, 68, 163, 17, 80, 6, - 242, 39, 73, 251, 147, 33, 65, 2, 237, 42, 4, 84, 65, 82, 84, 4, 18, 68, - 15, 77, 2, 35, 73, 2, 21, 3, 79, 78, 79, 2, 151, 19, 70, 4, 166, 19, 65, - 213, 219, 32, 2, 69, 78, 6, 84, 7, 67, 72, 82, 79, 77, 65, 32, 193, 149, - 17, 8, 68, 73, 65, 84, 79, 78, 79, 78, 4, 42, 86, 169, 153, 17, 4, 83, - 89, 78, 65, 2, 251, 209, 32, 65, 22, 80, 6, 69, 78, 73, 75, 73, 32, 44, - 2, 79, 82, 185, 26, 5, 82, 79, 78, 84, 72, 4, 192, 242, 30, 2, 68, 73, 1, - 2, 89, 70, 16, 68, 2, 71, 79, 225, 1, 10, 84, 72, 77, 73, 75, 79, 78, 32, - 78, 32, 12, 28, 2, 78, 32, 155, 1, 83, 10, 96, 14, 80, 65, 82, 69, 83, - 84, 73, 71, 77, 69, 78, 79, 78, 32, 242, 33, 65, 113, 3, 78, 69, 79, 4, - 214, 24, 68, 249, 202, 32, 5, 65, 82, 73, 83, 84, 2, 153, 191, 33, 5, 89, - 78, 84, 72, 69, 4, 142, 28, 65, 1, 2, 68, 73, 16, 56, 2, 77, 73, 114, 83, - 225, 240, 33, 4, 67, 72, 65, 68, 8, 34, 70, 169, 28, 3, 68, 73, 65, 6, - 40, 4, 84, 72, 79, 82, 195, 230, 33, 79, 4, 242, 160, 34, 79, 227, 79, - 65, 6, 44, 6, 65, 75, 73, 65, 32, 84, 231, 13, 79, 2, 153, 201, 21, 12, - 69, 76, 79, 85, 83, 32, 73, 67, 72, 73, 77, 65, 54, 108, 2, 65, 84, 96, - 6, 69, 78, 84, 73, 77, 65, 140, 1, 5, 76, 65, 83, 77, 65, 18, 79, 98, 82, - 175, 1, 89, 6, 40, 3, 65, 86, 65, 201, 3, 2, 72, 73, 4, 140, 29, 5, 32, - 84, 82, 79, 77, 135, 176, 34, 83, 18, 24, 2, 84, 65, 15, 32, 11, 11, 32, - 8, 36, 4, 78, 69, 79, 32, 183, 28, 65, 6, 252, 140, 28, 2, 77, 69, 254, - 212, 2, 75, 151, 226, 3, 65, 7, 243, 28, 32, 8, 64, 6, 78, 84, 69, 86, - 77, 65, 202, 212, 8, 82, 151, 246, 25, 85, 5, 181, 166, 14, 2, 32, 65, - 14, 44, 4, 65, 84, 73, 77, 105, 3, 69, 77, 65, 12, 18, 65, 35, 79, 8, - 182, 23, 32, 195, 209, 34, 84, 4, 134, 12, 75, 181, 184, 31, 5, 89, 80, - 79, 82, 82, 2, 227, 140, 27, 83, 2, 159, 201, 34, 76, 14, 22, 69, 147, - 22, 89, 12, 44, 5, 73, 77, 77, 65, 32, 191, 251, 29, 77, 10, 72, 2, 69, - 78, 0, 5, 73, 77, 73, 83, 69, 54, 84, 69, 3, 68, 89, 79, 2, 161, 160, 27, - 8, 79, 83, 32, 67, 72, 82, 79, 78, 4, 44, 5, 69, 83, 83, 65, 82, 1, 2, - 82, 73, 2, 17, 2, 79, 78, 2, 25, 4, 32, 67, 72, 82, 2, 247, 221, 33, 79, - 28, 84, 8, 65, 82, 84, 89, 82, 73, 65, 32, 229, 231, 30, 7, 73, 75, 82, - 79, 78, 32, 73, 26, 72, 5, 65, 76, 76, 73, 32, 38, 68, 38, 80, 134, 1, - 86, 30, 84, 87, 76, 4, 34, 68, 177, 2, 3, 80, 82, 79, 2, 237, 2, 5, 69, - 89, 84, 69, 82, 8, 60, 7, 76, 65, 71, 73, 79, 83, 32, 45, 4, 82, 79, 84, - 79, 4, 200, 1, 5, 84, 69, 84, 65, 82, 111, 73, 4, 22, 86, 227, 1, 83, 2, - 209, 1, 3, 65, 82, 89, 8, 56, 8, 69, 84, 65, 82, 84, 79, 83, 32, 61, 2, - 82, 73, 4, 22, 76, 135, 1, 73, 2, 21, 3, 69, 71, 69, 2, 63, 84, 4, 18, - 70, 35, 84, 2, 209, 197, 21, 3, 79, 78, 73, 2, 11, 79, 2, 11, 83, 2, 17, - 2, 32, 73, 2, 155, 239, 22, 67, 18, 92, 4, 76, 73, 71, 79, 180, 1, 5, 89, - 82, 65, 78, 73, 210, 14, 88, 165, 130, 34, 2, 77, 65, 4, 211, 1, 78, 42, - 60, 2, 65, 82, 100, 2, 73, 65, 90, 69, 169, 1, 2, 83, 73, 12, 40, 2, 65, - 75, 181, 170, 17, 2, 73, 67, 10, 52, 3, 65, 76, 69, 45, 6, 76, 73, 84, - 73, 75, 73, 4, 11, 83, 4, 17, 2, 77, 65, 4, 23, 32, 7, 11, 32, 4, 198, - 15, 65, 155, 151, 22, 78, 12, 72, 3, 84, 65, 83, 136, 3, 6, 76, 65, 83, - 84, 79, 78, 191, 216, 8, 82, 6, 26, 84, 247, 220, 34, 77, 4, 32, 2, 79, - 75, 139, 223, 34, 73, 2, 209, 189, 34, 2, 79, 85, 14, 36, 5, 70, 73, 83, - 84, 79, 67, 76, 10, 46, 80, 214, 1, 78, 170, 8, 76, 187, 1, 83, 2, 135, - 11, 65, 4, 162, 142, 34, 79, 227, 79, 73, 4, 178, 5, 69, 137, 183, 34, 2, - 65, 80, 42, 120, 5, 69, 73, 83, 77, 65, 32, 8, 73, 77, 65, 78, 83, 73, - 83, 32, 182, 1, 84, 154, 1, 89, 245, 240, 1, 3, 65, 88, 73, 5, 11, 32, 2, - 223, 162, 22, 78, 16, 36, 2, 65, 82, 1, 3, 84, 72, 69, 8, 25, 4, 83, 69, - 79, 83, 9, 11, 32, 6, 18, 84, 39, 68, 4, 34, 82, 13, 4, 69, 84, 82, 65, - 2, 11, 73, 2, 141, 146, 27, 3, 83, 73, 77, 8, 68, 5, 65, 86, 82, 79, 83, - 52, 4, 82, 65, 71, 71, 179, 199, 16, 73, 5, 29, 5, 32, 65, 80, 79, 68, 2, - 255, 219, 8, 69, 2, 141, 241, 1, 2, 73, 83, 12, 34, 78, 149, 1, 3, 82, - 77, 65, 8, 36, 5, 65, 71, 77, 65, 32, 91, 69, 6, 154, 8, 65, 154, 151, - 22, 78, 217, 240, 4, 10, 77, 69, 84, 65, 32, 83, 84, 65, 86, 82, 2, 159, - 183, 34, 86, 5, 11, 84, 2, 243, 245, 16, 73, 40, 42, 69, 70, 72, 218, 1, - 82, 227, 2, 73, 6, 136, 12, 3, 84, 82, 65, 242, 161, 8, 76, 201, 154, 24, - 2, 83, 83, 12, 26, 69, 167, 178, 34, 73, 10, 64, 2, 77, 65, 145, 194, 33, - 8, 83, 32, 75, 65, 73, 32, 65, 80, 9, 56, 2, 32, 65, 33, 8, 84, 73, 83, - 77, 79, 83, 32, 69, 2, 189, 213, 33, 3, 80, 76, 79, 4, 218, 182, 34, 83, - 3, 88, 20, 38, 73, 73, 5, 79, 77, 73, 75, 79, 6, 48, 2, 71, 79, 206, 217, - 29, 80, 143, 251, 4, 65, 2, 183, 156, 33, 82, 14, 42, 76, 32, 2, 78, 32, - 62, 80, 95, 83, 2, 11, 89, 2, 227, 178, 34, 71, 6, 26, 65, 139, 154, 22, - 78, 4, 250, 2, 82, 147, 174, 34, 76, 4, 46, 65, 129, 160, 33, 5, 83, 73, - 70, 73, 83, 2, 237, 177, 34, 6, 82, 65, 75, 65, 76, 69, 2, 11, 89, 2, - 145, 192, 16, 2, 78, 65, 10, 26, 82, 219, 176, 34, 84, 8, 21, 3, 69, 73, - 65, 8, 26, 32, 113, 2, 73, 32, 6, 38, 69, 242, 5, 68, 255, 145, 22, 78, - 2, 11, 75, 2, 29, 5, 70, 79, 78, 73, 84, 2, 165, 129, 34, 2, 73, 75, 2, - 11, 65, 2, 11, 82, 2, 153, 128, 34, 3, 67, 72, 65, 22, 28, 2, 70, 69, - 231, 3, 80, 14, 34, 78, 49, 4, 83, 73, 83, 32, 4, 11, 32, 4, 202, 196, - 30, 75, 151, 226, 3, 65, 10, 42, 65, 42, 68, 62, 77, 89, 2, 84, 82, 2, - 133, 2, 6, 80, 76, 73, 32, 68, 89, 2, 233, 1, 11, 73, 71, 82, 65, 77, 77, - 79, 83, 32, 69, 88, 2, 173, 1, 18, 79, 78, 79, 71, 82, 65, 77, 77, 79, - 83, 32, 84, 69, 83, 83, 69, 82, 65, 4, 11, 73, 4, 60, 11, 71, 82, 65, 77, - 77, 79, 83, 32, 79, 75, 84, 59, 84, 2, 11, 79, 2, 181, 174, 2, 6, 32, 68, - 79, 68, 69, 75, 2, 153, 252, 33, 4, 73, 77, 79, 82, 8, 26, 79, 139, 209, - 29, 83, 6, 56, 6, 75, 82, 73, 83, 73, 83, 181, 221, 29, 2, 82, 82, 5, 17, - 2, 32, 68, 2, 11, 73, 2, 183, 208, 29, 80, 172, 82, 182, 1, 65, 134, 72, - 69, 230, 1, 72, 154, 31, 73, 208, 41, 3, 74, 75, 32, 138, 26, 76, 186, - 18, 79, 194, 113, 82, 234, 7, 85, 150, 128, 2, 89, 242, 164, 19, 71, 226, - 216, 10, 83, 203, 18, 67, 198, 13, 110, 68, 66, 76, 50, 77, 90, 78, 174, - 51, 80, 62, 82, 198, 7, 84, 138, 1, 85, 242, 164, 18, 67, 187, 203, 6, - 83, 4, 34, 85, 137, 139, 27, 2, 65, 32, 2, 221, 194, 32, 2, 67, 69, 4, - 152, 207, 9, 3, 76, 32, 77, 247, 200, 19, 69, 6, 36, 3, 69, 82, 65, 231, - 246, 33, 80, 5, 165, 177, 27, 7, 32, 87, 73, 84, 72, 32, 70, 193, 11, - 148, 1, 16, 65, 68, 73, 65, 78, 32, 83, 89, 76, 76, 65, 66, 73, 67, 83, - 32, 148, 49, 2, 67, 69, 94, 68, 216, 227, 23, 3, 78, 69, 68, 131, 154, - 10, 79, 172, 11, 226, 1, 65, 190, 1, 66, 162, 2, 67, 242, 8, 69, 50, 70, - 198, 4, 72, 38, 73, 30, 75, 134, 1, 76, 82, 77, 174, 1, 78, 202, 4, 81, - 178, 1, 79, 194, 1, 80, 70, 82, 142, 1, 83, 194, 4, 84, 246, 3, 87, 254, - 5, 89, 247, 204, 17, 71, 21, 90, 65, 30, 73, 40, 10, 84, 72, 65, 80, 65, - 83, 67, 65, 78, 32, 150, 195, 34, 78, 3, 89, 7, 214, 195, 34, 73, 3, 89, - 5, 169, 221, 23, 5, 86, 73, 76, 73, 75, 4, 146, 195, 34, 77, 3, 83, 42, - 156, 1, 9, 76, 65, 67, 75, 70, 79, 79, 84, 32, 172, 151, 19, 9, 73, 66, - 76, 69, 45, 67, 82, 69, 69, 221, 202, 7, 10, 69, 65, 86, 69, 82, 32, 68, - 69, 78, 69, 36, 82, 87, 230, 228, 11, 75, 2, 78, 162, 220, 22, 65, 2, 69, - 2, 73, 2, 79, 3, 83, 11, 130, 193, 34, 65, 2, 69, 2, 73, 3, 79, 141, 3, - 82, 65, 186, 42, 87, 142, 169, 8, 72, 222, 166, 23, 79, 134, 60, 73, 223, - 137, 2, 69, 241, 2, 48, 6, 82, 82, 73, 69, 82, 32, 239, 181, 32, 65, 234, - 2, 170, 1, 68, 122, 71, 94, 72, 46, 73, 46, 74, 82, 75, 30, 78, 66, 83, - 66, 80, 2, 90, 58, 84, 38, 76, 132, 1, 2, 67, 72, 2, 77, 2, 82, 2, 87, 2, - 89, 207, 161, 34, 69, 32, 46, 69, 202, 5, 76, 2, 90, 163, 184, 34, 73, 6, - 26, 78, 207, 189, 34, 69, 4, 146, 134, 17, 69, 237, 210, 6, 2, 84, 65, - 30, 254, 4, 72, 182, 166, 21, 87, 234, 165, 7, 65, 230, 171, 5, 69, 162, - 64, 73, 2, 79, 3, 85, 19, 162, 4, 87, 206, 161, 34, 69, 215, 22, 73, 5, - 225, 135, 14, 6, 78, 73, 84, 73, 65, 76, 26, 202, 3, 74, 130, 248, 33, - 69, 234, 61, 87, 186, 2, 65, 2, 73, 2, 79, 3, 85, 26, 154, 1, 75, 227, 1, - 72, 14, 222, 250, 33, 69, 162, 64, 65, 2, 71, 2, 73, 2, 79, 3, 85, 26, - 62, 72, 226, 249, 33, 69, 162, 64, 65, 2, 73, 2, 79, 3, 85, 15, 222, 249, - 33, 69, 162, 64, 65, 2, 73, 2, 79, 3, 85, 72, 34, 76, 70, 84, 66, 72, 3, - 83, 24, 130, 1, 72, 130, 248, 33, 69, 162, 64, 65, 2, 73, 2, 79, 3, 85, - 24, 62, 83, 130, 248, 33, 69, 162, 64, 65, 2, 73, 2, 79, 3, 85, 12, 254, - 247, 33, 69, 162, 64, 65, 2, 73, 2, 79, 3, 85, 7, 236, 29, 4, 65, 83, 84, - 69, 251, 153, 34, 78, 51, 74, 65, 22, 73, 130, 229, 31, 85, 250, 11, 79, - 150, 83, 87, 207, 242, 1, 69, 7, 143, 173, 32, 65, 33, 40, 4, 78, 65, 76, - 32, 175, 182, 34, 73, 28, 160, 1, 2, 68, 79, 134, 1, 82, 78, 83, 166, - 133, 14, 71, 186, 2, 65, 128, 177, 3, 6, 66, 79, 84, 84, 79, 77, 0, 3, - 84, 79, 80, 166, 244, 12, 80, 247, 244, 2, 77, 6, 44, 5, 85, 66, 76, 69, - 32, 131, 160, 24, 87, 4, 252, 198, 6, 12, 83, 72, 79, 82, 84, 32, 86, 69, - 82, 84, 73, 67, 255, 193, 7, 65, 6, 38, 73, 213, 231, 32, 3, 65, 73, 83, - 4, 230, 184, 17, 71, 163, 250, 16, 78, 4, 152, 146, 25, 4, 77, 65, 76, - 76, 213, 252, 6, 4, 72, 79, 82, 84, 4, 150, 176, 26, 89, 223, 130, 8, 75, - 7, 206, 178, 34, 73, 3, 78, 39, 66, 87, 234, 27, 65, 170, 208, 31, 79, - 134, 60, 73, 223, 137, 2, 69, 19, 138, 143, 12, 65, 134, 221, 19, 79, - 134, 60, 73, 223, 137, 2, 69, 49, 142, 7, 72, 154, 20, 65, 66, 87, 234, - 207, 31, 79, 134, 60, 73, 223, 137, 2, 69, 43, 70, 69, 38, 79, 238, 25, - 65, 66, 87, 238, 139, 32, 73, 223, 137, 2, 72, 7, 197, 208, 26, 4, 68, - 73, 65, 76, 7, 11, 79, 5, 249, 234, 32, 8, 83, 69, 45, 67, 82, 69, 69, - 32, 149, 1, 164, 1, 8, 45, 67, 82, 69, 69, 32, 84, 72, 38, 65, 250, 2, - 71, 76, 2, 78, 71, 48, 4, 85, 78, 65, 86, 142, 20, 79, 30, 87, 238, 139, - 32, 73, 222, 137, 2, 69, 3, 72, 6, 170, 164, 32, 73, 223, 137, 2, 69, 63, - 104, 6, 83, 75, 65, 80, 73, 32, 188, 1, 7, 84, 84, 73, 76, 73, 75, 32, - 226, 161, 32, 65, 223, 137, 2, 89, 30, 70, 83, 86, 87, 210, 17, 67, 2, - 75, 2, 77, 2, 78, 2, 84, 3, 89, 14, 168, 192, 28, 2, 75, 87, 254, 122, - 67, 2, 80, 2, 84, 190, 254, 2, 87, 175, 116, 45, 4, 230, 140, 34, 79, - 191, 28, 65, 24, 30, 72, 1, 3, 83, 72, 82, 12, 150, 191, 28, 65, 166, - 166, 3, 79, 135, 60, 73, 19, 38, 65, 230, 228, 31, 79, 135, 60, 73, 9, - 230, 160, 32, 65, 223, 137, 2, 73, 15, 154, 190, 28, 65, 166, 166, 3, 79, - 135, 60, 73, 18, 224, 9, 3, 73, 75, 32, 185, 186, 23, 2, 85, 84, 33, 68, - 7, 74, 73, 66, 87, 65, 89, 32, 246, 168, 34, 78, 2, 79, 3, 89, 24, 74, - 78, 142, 136, 30, 83, 158, 160, 4, 67, 2, 75, 2, 77, 2, 80, 3, 84, 11, - 11, 87, 8, 186, 226, 31, 79, 135, 60, 73, 39, 206, 4, 87, 166, 13, 65, - 38, 79, 138, 140, 32, 73, 223, 137, 2, 69, 39, 104, 7, 45, 67, 82, 69, - 69, 32, 82, 146, 14, 87, 182, 2, 65, 170, 208, 31, 79, 134, 60, 73, 223, - 137, 2, 69, 4, 246, 143, 34, 87, 215, 22, 69, 125, 94, 65, 218, 1, 72, - 138, 1, 79, 238, 2, 87, 226, 195, 11, 80, 198, 210, 20, 73, 223, 137, 2, - 69, 43, 26, 89, 215, 155, 32, 65, 37, 25, 4, 73, 83, 73, 32, 34, 70, 72, - 54, 74, 218, 4, 83, 234, 139, 34, 89, 202, 18, 84, 147, 1, 77, 10, 234, - 222, 31, 79, 226, 197, 2, 65, 2, 69, 3, 73, 6, 238, 208, 30, 85, 171, - 211, 3, 73, 37, 70, 87, 202, 13, 79, 130, 243, 11, 65, 138, 153, 20, 73, - 223, 137, 2, 69, 16, 198, 13, 79, 226, 169, 28, 65, 170, 226, 3, 73, 223, - 137, 2, 69, 15, 80, 12, 85, 84, 72, 45, 83, 76, 65, 86, 69, 89, 32, 75, - 154, 162, 34, 79, 3, 89, 8, 134, 161, 34, 65, 2, 69, 2, 73, 3, 79, 117, - 110, 72, 190, 1, 76, 78, 84, 238, 8, 65, 66, 87, 170, 185, 11, 89, 194, - 150, 20, 79, 134, 60, 73, 223, 137, 2, 69, 39, 108, 7, 45, 67, 82, 69, - 69, 32, 84, 226, 4, 87, 170, 175, 28, 65, 166, 166, 3, 79, 134, 60, 73, - 223, 137, 2, 69, 16, 11, 72, 17, 250, 179, 28, 65, 166, 166, 3, 79, 134, - 60, 73, 223, 137, 2, 69, 12, 11, 72, 12, 210, 217, 31, 79, 142, 175, 2, - 87, 214, 22, 65, 2, 69, 3, 73, 24, 50, 72, 194, 158, 34, 65, 2, 69, 2, - 73, 3, 79, 17, 186, 178, 28, 65, 166, 166, 3, 79, 142, 175, 2, 87, 214, - 22, 69, 3, 73, 224, 1, 54, 69, 218, 3, 79, 130, 247, 11, 65, 139, 153, - 20, 73, 185, 1, 17, 2, 83, 84, 182, 1, 44, 6, 45, 67, 82, 69, 69, 32, - 251, 2, 69, 180, 1, 110, 76, 66, 77, 2, 80, 2, 89, 16, 2, 78, 87, 38, 82, - 62, 83, 26, 67, 2, 75, 18, 84, 26, 70, 199, 4, 87, 27, 178, 6, 87, 198, - 169, 28, 65, 166, 166, 3, 79, 227, 197, 2, 69, 17, 243, 5, 87, 6, 166, - 175, 28, 65, 135, 236, 5, 69, 13, 186, 168, 32, 87, 206, 242, 1, 65, 2, - 69, 2, 73, 3, 79, 28, 22, 72, 239, 4, 87, 14, 235, 4, 87, 16, 22, 72, - 199, 4, 87, 2, 191, 167, 32, 87, 2, 171, 183, 23, 82, 31, 11, 79, 29, 41, - 8, 68, 83, 45, 67, 82, 69, 69, 32, 26, 56, 2, 84, 72, 225, 150, 32, 6, - 70, 73, 78, 65, 76, 32, 25, 50, 87, 190, 152, 34, 65, 2, 69, 2, 73, 3, - 79, 14, 182, 172, 28, 65, 166, 166, 3, 79, 134, 60, 73, 139, 243, 1, 69, - 61, 92, 6, 45, 67, 82, 69, 69, 32, 150, 1, 65, 38, 79, 30, 87, 238, 139, - 32, 73, 223, 137, 2, 69, 24, 110, 80, 134, 207, 31, 67, 2, 75, 2, 76, 2, - 77, 2, 78, 2, 83, 2, 84, 2, 89, 182, 168, 2, 79, 247, 30, 87, 4, 222, - 163, 32, 87, 219, 211, 1, 79, 9, 170, 140, 32, 65, 223, 137, 2, 89, 7, - 226, 149, 34, 79, 3, 89, 14, 194, 169, 28, 65, 166, 166, 3, 79, 134, 60, - 73, 223, 137, 2, 69, 10, 26, 76, 239, 148, 34, 82, 9, 26, 32, 155, 181, - 9, 76, 4, 182, 150, 22, 67, 151, 149, 10, 84, 4, 214, 253, 33, 76, 215, - 22, 89, 4, 216, 171, 15, 3, 73, 84, 85, 253, 132, 8, 3, 82, 73, 67, 124, - 140, 1, 2, 68, 32, 102, 69, 72, 11, 73, 65, 78, 32, 76, 69, 84, 84, 69, - 82, 32, 210, 3, 79, 62, 80, 90, 82, 141, 212, 27, 4, 32, 83, 76, 73, 6, - 52, 5, 73, 78, 68, 69, 88, 169, 133, 33, 2, 70, 73, 5, 165, 131, 33, 6, - 32, 68, 73, 86, 73, 68, 6, 26, 84, 227, 190, 21, 32, 5, 181, 156, 10, 6, - 32, 73, 78, 83, 69, 82, 98, 196, 1, 2, 67, 45, 38, 76, 22, 77, 50, 78, - 38, 83, 46, 84, 22, 85, 186, 213, 3, 65, 2, 68, 2, 69, 2, 71, 2, 75, 2, - 80, 130, 230, 26, 82, 238, 200, 1, 73, 222, 137, 2, 66, 2, 79, 2, 81, 3, - 88, 4, 174, 242, 26, 49, 219, 167, 3, 51, 7, 223, 214, 3, 68, 11, 11, 66, - 9, 134, 143, 34, 50, 2, 51, 3, 52, 9, 226, 142, 34, 68, 2, 71, 3, 78, 13, - 246, 213, 3, 72, 2, 84, 203, 184, 30, 83, 7, 203, 213, 3, 84, 13, 11, 85, - 11, 11, 85, 9, 230, 141, 34, 50, 2, 51, 3, 85, 4, 160, 132, 33, 6, 85, - 83, 69, 76, 32, 72, 163, 137, 1, 78, 4, 224, 189, 27, 6, 32, 83, 84, 82, - 69, 65, 249, 208, 5, 7, 69, 78, 84, 82, 89, 32, 83, 4, 142, 200, 27, 73, - 247, 167, 6, 79, 9, 29, 5, 32, 70, 65, 67, 69, 7, 33, 6, 32, 87, 73, 84, - 72, 32, 4, 208, 235, 6, 2, 84, 69, 129, 251, 24, 6, 87, 82, 89, 32, 83, - 77, 108, 88, 16, 67, 65, 83, 73, 65, 78, 32, 65, 76, 66, 65, 78, 73, 65, - 78, 32, 235, 137, 30, 84, 106, 60, 7, 76, 69, 84, 84, 69, 82, 32, 161, - 250, 27, 2, 67, 73, 104, 174, 2, 65, 34, 67, 146, 1, 68, 78, 69, 34, 71, - 46, 73, 46, 74, 34, 75, 34, 76, 34, 80, 34, 83, 74, 84, 62, 89, 46, 90, - 128, 241, 16, 2, 81, 65, 214, 192, 6, 77, 148, 173, 6, 3, 86, 69, 89, - 178, 232, 2, 70, 128, 19, 3, 78, 79, 87, 150, 20, 82, 242, 21, 88, 154, - 46, 79, 202, 26, 66, 237, 24, 3, 72, 69, 89, 4, 218, 206, 33, 79, 179, - 28, 76, 16, 34, 65, 34, 72, 49, 2, 89, 65, 4, 146, 183, 33, 89, 227, 79, - 82, 8, 242, 166, 28, 65, 154, 206, 5, 79, 203, 17, 73, 4, 162, 134, 34, - 87, 3, 89, 8, 42, 90, 222, 187, 32, 89, 215, 173, 1, 65, 4, 214, 249, 32, - 89, 191, 122, 65, 4, 218, 181, 33, 89, 227, 79, 66, 4, 136, 217, 32, 2, - 72, 69, 187, 155, 1, 73, 6, 138, 201, 32, 82, 134, 108, 87, 135, 77, 78, - 4, 146, 248, 32, 72, 199, 14, 65, 4, 130, 134, 33, 73, 199, 69, 65, 4, - 162, 180, 33, 65, 171, 51, 89, 4, 142, 1, 73, 247, 178, 33, 69, 8, 34, - 72, 137, 128, 34, 2, 69, 89, 6, 182, 151, 20, 65, 163, 218, 13, 79, 6, - 38, 73, 210, 246, 32, 89, 171, 78, 65, 2, 247, 201, 33, 87, 4, 156, 188, - 33, 2, 65, 89, 1, 2, 79, 87, 6, 130, 147, 16, 72, 219, 185, 4, 65, 16, - 72, 2, 68, 73, 32, 2, 78, 84, 212, 156, 32, 3, 76, 84, 73, 187, 80, 82, - 4, 174, 250, 32, 32, 223, 100, 76, 8, 32, 2, 82, 69, 239, 249, 32, 32, 6, - 44, 5, 76, 73, 78, 69, 32, 147, 226, 4, 32, 4, 178, 214, 21, 76, 155, - 194, 10, 79, 252, 5, 102, 65, 170, 17, 69, 134, 8, 73, 150, 1, 79, 244, - 191, 13, 6, 82, 73, 83, 84, 77, 65, 139, 130, 10, 85, 198, 2, 66, 73, 32, - 4, 75, 77, 65, 32, 148, 6, 2, 77, 32, 131, 8, 82, 4, 250, 192, 33, 78, - 223, 61, 82, 142, 1, 156, 1, 7, 76, 69, 84, 84, 69, 82, 32, 142, 3, 83, - 98, 86, 214, 202, 23, 68, 194, 221, 7, 81, 200, 94, 5, 77, 65, 65, 89, - 89, 136, 177, 1, 2, 65, 85, 3, 79, 76, 202, 1, 68, 54, 78, 54, 84, 54, - 89, 166, 250, 28, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 76, 2, 80, 190, 141, - 3, 72, 2, 77, 2, 82, 2, 83, 2, 86, 2, 87, 150, 240, 1, 65, 186, 2, 69, 2, - 73, 3, 85, 8, 190, 251, 28, 68, 190, 141, 3, 72, 151, 240, 1, 65, 8, 198, - 136, 32, 71, 2, 78, 2, 89, 151, 240, 1, 65, 8, 214, 250, 28, 84, 190, - 141, 3, 72, 151, 240, 1, 65, 4, 222, 135, 32, 89, 151, 240, 1, 65, 8, 40, - 4, 73, 71, 78, 32, 155, 249, 21, 69, 6, 154, 229, 29, 67, 154, 222, 1, - 86, 179, 241, 1, 65, 26, 64, 10, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, - 143, 192, 31, 73, 24, 194, 158, 30, 65, 250, 6, 85, 206, 201, 1, 69, 2, - 73, 3, 79, 166, 1, 236, 1, 15, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, - 83, 73, 71, 78, 32, 112, 7, 76, 69, 84, 84, 69, 82, 32, 152, 4, 12, 80, - 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 60, 11, 86, 79, 87, 69, 76, - 32, 83, 73, 71, 78, 32, 207, 255, 31, 68, 14, 72, 6, 70, 73, 78, 65, 76, - 32, 174, 243, 33, 76, 2, 82, 2, 87, 3, 89, 6, 142, 245, 33, 78, 86, 72, - 3, 77, 104, 132, 2, 6, 70, 73, 78, 65, 76, 32, 110, 78, 50, 77, 78, 80, - 226, 215, 11, 66, 138, 135, 6, 68, 206, 134, 12, 83, 194, 130, 2, 65, - 156, 194, 1, 2, 67, 72, 2, 71, 2, 74, 2, 75, 2, 84, 138, 69, 72, 2, 76, - 2, 82, 2, 86, 2, 89, 186, 2, 69, 2, 73, 2, 79, 3, 85, 22, 158, 248, 31, - 78, 190, 189, 1, 83, 206, 60, 67, 146, 1, 71, 2, 75, 2, 76, 2, 80, 2, 82, - 2, 84, 3, 89, 14, 46, 71, 34, 72, 170, 219, 33, 85, 215, 22, 65, 4, 198, - 219, 33, 85, 215, 22, 65, 6, 166, 219, 33, 85, 158, 20, 74, 187, 2, 65, - 6, 150, 239, 33, 72, 2, 80, 187, 2, 65, 8, 246, 156, 11, 83, 186, 247, - 18, 68, 45, 4, 84, 82, 73, 80, 20, 158, 157, 30, 65, 242, 201, 1, 73, - 190, 201, 1, 79, 2, 85, 203, 44, 69, 14, 72, 7, 65, 67, 84, 69, 82, 32, - 84, 49, 7, 84, 32, 87, 73, 84, 72, 32, 8, 240, 242, 9, 3, 65, 66, 85, - 147, 230, 23, 73, 6, 128, 1, 13, 85, 80, 87, 65, 82, 68, 83, 32, 84, 82, - 69, 78, 68, 217, 160, 25, 12, 68, 79, 87, 78, 87, 65, 82, 68, 83, 32, 84, - 82, 5, 225, 242, 6, 6, 32, 65, 78, 68, 32, 89, 234, 2, 80, 2, 67, 75, 82, - 69, 90, 82, 156, 247, 4, 2, 83, 84, 229, 151, 15, 2, 81, 85, 6, 56, 8, - 69, 82, 32, 66, 79, 65, 82, 68, 175, 171, 33, 32, 5, 211, 244, 30, 32, 4, - 140, 177, 27, 4, 83, 69, 32, 87, 229, 144, 5, 9, 82, 73, 78, 71, 32, 77, - 69, 71, 65, 220, 2, 40, 5, 79, 75, 69, 69, 32, 143, 5, 82, 216, 2, 46, + 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 4, 230, 222, 36, 78, 87, 72, 76, + 194, 1, 77, 114, 78, 68, 2, 80, 65, 38, 83, 192, 236, 18, 4, 75, 65, 82, + 79, 142, 237, 17, 66, 2, 67, 2, 68, 2, 71, 2, 72, 2, 74, 2, 76, 2, 82, 2, + 87, 2, 89, 186, 2, 65, 2, 73, 3, 85, 10, 26, 65, 139, 219, 36, 66, 9, 45, + 9, 78, 68, 65, 73, 76, 73, 78, 71, 32, 6, 214, 218, 36, 72, 2, 78, 3, 83, + 10, 152, 2, 2, 79, 82, 154, 216, 36, 68, 2, 71, 2, 89, 187, 2, 65, 5, + 221, 130, 21, 4, 75, 80, 65, 75, 24, 80, 10, 73, 77, 65, 76, 85, 78, 71, + 85, 78, 32, 96, 2, 79, 85, 211, 218, 36, 65, 20, 246, 216, 36, 71, 2, 72, + 2, 76, 2, 77, 2, 80, 2, 82, 2, 83, 2, 87, 2, 89, 187, 2, 65, 2, 233, 236, + 18, 5, 84, 72, 69, 82, 78, 4, 166, 2, 71, 249, 236, 18, 4, 79, 78, 71, + 79, 10, 96, 12, 89, 77, 66, 79, 76, 32, 66, 73, 78, 68, 85, 32, 137, 190, + 10, 6, 73, 71, 78, 32, 84, 79, 8, 78, 80, 168, 169, 11, 3, 74, 85, 68, + 249, 171, 25, 6, 78, 65, 32, 77, 69, 84, 4, 64, 3, 65, 78, 71, 173, 166, + 23, 7, 73, 78, 65, 82, 66, 79, 82, 2, 183, 238, 25, 79, 18, 122, 85, 188, + 158, 26, 6, 80, 65, 75, 80, 65, 75, 152, 254, 2, 5, 75, 65, 82, 79, 32, + 254, 249, 6, 69, 162, 64, 73, 3, 79, 5, 173, 202, 30, 15, 32, 70, 79, 82, + 32, 83, 73, 77, 65, 76, 85, 78, 71, 85, 78, 5, 159, 229, 23, 84, 228, 2, + 182, 1, 65, 230, 2, 69, 50, 76, 146, 1, 78, 188, 10, 9, 82, 73, 65, 32, + 69, 82, 70, 69, 32, 186, 5, 84, 152, 248, 32, 2, 67, 65, 176, 185, 2, 5, + 86, 69, 82, 65, 71, 243, 142, 1, 68, 20, 136, 1, 4, 77, 69, 68, 32, 170, + 1, 82, 140, 172, 3, 10, 67, 72, 32, 87, 73, 84, 72, 32, 85, 77, 142, 165, + 7, 84, 154, 195, 25, 86, 19, 78, 8, 86, 65, 0, 2, 68, 69, 52, 4, 69, 73, + 71, 72, 1, 7, 83, 73, 88, 84, 69, 69, 78, 2, 169, 178, 20, 8, 83, 67, 69, + 78, 68, 73, 78, 71, 2, 161, 178, 20, 2, 84, 72, 4, 220, 205, 32, 3, 68, + 69, 68, 171, 199, 3, 32, 4, 228, 200, 8, 3, 82, 32, 77, 203, 226, 26, 84, + 15, 11, 76, 13, 54, 32, 252, 165, 25, 3, 72, 79, 80, 231, 236, 9, 79, 6, + 238, 195, 12, 80, 212, 164, 16, 6, 87, 73, 84, 72, 32, 67, 139, 211, 6, + 83, 208, 1, 84, 5, 71, 65, 76, 73, 32, 158, 9, 84, 37, 9, 90, 69, 78, 69, + 32, 82, 73, 78, 71, 200, 1, 210, 1, 65, 40, 9, 67, 85, 82, 82, 69, 78, + 67, 89, 32, 148, 2, 7, 76, 69, 84, 84, 69, 82, 32, 204, 3, 6, 82, 85, 80, + 69, 69, 32, 34, 83, 170, 223, 22, 73, 134, 4, 86, 158, 240, 11, 68, 225, + 110, 3, 71, 65, 78, 6, 190, 173, 32, 66, 50, 78, 135, 63, 85, 12, 120, + 10, 78, 85, 77, 69, 82, 65, 84, 79, 82, 32, 205, 179, 23, 14, 68, 69, 78, + 79, 77, 73, 78, 65, 84, 79, 82, 32, 83, 73, 10, 52, 3, 79, 78, 69, 210, + 176, 31, 70, 135, 169, 3, 84, 5, 225, 191, 29, 19, 32, 76, 69, 83, 83, + 32, 84, 72, 65, 78, 32, 84, 72, 69, 32, 68, 69, 78, 79, 108, 226, 1, 75, + 90, 82, 238, 197, 21, 86, 242, 138, 8, 89, 178, 154, 3, 65, 38, 68, 114, + 84, 230, 5, 85, 186, 202, 1, 73, 138, 196, 1, 78, 46, 83, 82, 66, 2, 67, + 2, 71, 2, 74, 2, 80, 138, 69, 72, 2, 76, 2, 77, 186, 2, 69, 3, 79, 8, 26, + 72, 191, 201, 36, 65, 6, 26, 65, 215, 133, 14, 73, 5, 185, 219, 18, 3, + 78, 68, 65, 10, 34, 65, 166, 198, 36, 72, 3, 82, 7, 33, 6, 32, 87, 73, + 84, 72, 32, 4, 156, 158, 25, 5, 76, 79, 87, 69, 82, 1, 6, 77, 73, 68, 68, + 76, 69, 4, 134, 193, 35, 83, 191, 69, 77, 20, 116, 19, 69, 81, 85, 69, + 78, 67, 69, 32, 70, 79, 82, 32, 76, 69, 84, 84, 69, 82, 32, 210, 137, 26, + 65, 227, 158, 6, 73, 6, 206, 225, 22, 82, 175, 226, 13, 89, 4, 186, 157, + 26, 32, 151, 154, 9, 79, 5, 249, 203, 33, 3, 32, 87, 73, 100, 56, 6, 67, + 65, 80, 73, 84, 65, 1, 4, 83, 77, 65, 76, 50, 45, 9, 76, 32, 76, 69, 84, + 84, 69, 82, 32, 50, 182, 2, 65, 50, 68, 42, 69, 82, 71, 38, 78, 38, 83, + 154, 131, 17, 77, 136, 235, 1, 6, 72, 73, 82, 68, 69, 65, 0, 2, 75, 79, + 224, 194, 13, 6, 84, 65, 84, 65, 83, 79, 196, 209, 2, 5, 66, 65, 83, 73, + 71, 244, 50, 3, 87, 65, 83, 164, 108, 3, 70, 73, 84, 0, 3, 76, 65, 75, + 170, 11, 79, 2, 80, 2, 85, 219, 19, 73, 4, 26, 82, 179, 194, 36, 89, 2, + 139, 141, 23, 75, 4, 212, 206, 31, 3, 65, 82, 66, 3, 74, 6, 40, 4, 82, + 73, 71, 79, 203, 193, 36, 72, 5, 245, 172, 20, 4, 32, 84, 65, 77, 4, 210, + 226, 29, 79, 155, 220, 6, 78, 4, 166, 188, 31, 73, 187, 246, 3, 71, 4, + 246, 158, 26, 72, 155, 182, 3, 69, 4, 166, 234, 34, 87, 219, 64, 32, 194, + 1, 184, 2, 7, 76, 69, 84, 84, 69, 82, 32, 244, 1, 7, 78, 85, 77, 66, 69, + 82, 32, 72, 5, 83, 73, 71, 78, 32, 48, 11, 86, 79, 87, 69, 76, 32, 83, + 73, 71, 78, 32, 242, 249, 25, 68, 198, 247, 3, 87, 208, 195, 2, 10, 71, + 65, 80, 32, 70, 73, 76, 76, 69, 82, 161, 187, 3, 12, 72, 85, 78, 68, 82, + 69, 68, 83, 32, 85, 78, 73, 92, 210, 1, 86, 146, 222, 32, 65, 38, 68, + 114, 84, 230, 5, 85, 186, 202, 1, 73, 138, 196, 1, 78, 46, 83, 82, 66, 2, + 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, 2, 82, 2, 89, + 186, 2, 69, 3, 79, 8, 234, 1, 79, 155, 186, 36, 65, 36, 142, 126, 69, 38, + 70, 66, 78, 26, 83, 234, 176, 21, 84, 195, 240, 12, 79, 10, 186, 157, 32, + 67, 158, 67, 65, 187, 151, 3, 86, 24, 80, 2, 86, 79, 250, 226, 32, 65, + 38, 85, 186, 202, 1, 73, 198, 140, 2, 69, 3, 79, 6, 33, 6, 67, 65, 76, + 73, 67, 32, 6, 214, 227, 32, 82, 159, 214, 3, 76, 26, 148, 1, 4, 67, 89, + 67, 76, 36, 2, 71, 32, 28, 2, 76, 76, 46, 82, 66, 84, 224, 174, 19, 4, + 79, 72, 65, 90, 212, 131, 12, 2, 75, 73, 239, 180, 4, 83, 4, 194, 202, + 32, 73, 247, 237, 3, 69, 4, 226, 158, 34, 82, 67, 83, 4, 240, 219, 9, 2, + 69, 68, 235, 250, 23, 73, 4, 160, 203, 32, 7, 84, 72, 68, 65, 89, 32, 67, + 171, 236, 3, 68, 4, 192, 216, 13, 2, 67, 79, 145, 221, 22, 4, 73, 78, 71, + 32, 196, 7, 42, 65, 174, 33, 79, 165, 11, 2, 85, 69, 246, 2, 32, 2, 67, + 75, 207, 244, 17, 78, 244, 2, 22, 32, 223, 31, 45, 228, 2, 210, 1, 67, + 254, 4, 68, 174, 2, 70, 102, 72, 82, 76, 186, 4, 77, 250, 2, 78, 38, 80, + 46, 82, 150, 4, 83, 154, 4, 84, 82, 85, 248, 2, 3, 86, 69, 82, 158, 133, + 12, 79, 140, 220, 20, 3, 66, 79, 87, 187, 249, 1, 81, 102, 196, 1, 5, 73, + 82, 67, 76, 69, 200, 1, 6, 85, 82, 86, 69, 68, 32, 168, 129, 26, 12, 82, + 79, 83, 83, 32, 79, 78, 32, 83, 72, 73, 69, 248, 161, 3, 5, 69, 78, 84, + 82, 69, 254, 172, 5, 72, 159, 14, 76, 11, 11, 32, 8, 72, 5, 87, 73, 84, + 72, 32, 189, 238, 16, 7, 70, 79, 82, 32, 82, 69, 67, 6, 140, 69, 8, 87, + 72, 73, 84, 69, 32, 68, 79, 218, 250, 18, 68, 177, 146, 2, 8, 84, 87, 79, + 32, 87, 72, 73, 84, 16, 84, 4, 68, 79, 87, 78, 0, 2, 85, 80, 56, 3, 76, + 69, 70, 1, 4, 82, 73, 71, 72, 4, 205, 138, 32, 9, 87, 65, 82, 68, 83, 32, + 65, 78, 68, 4, 53, 11, 84, 87, 65, 82, 68, 83, 32, 65, 78, 68, 32, 4, + 194, 151, 21, 85, 255, 165, 12, 68, 30, 64, 6, 73, 65, 77, 79, 78, 68, + 152, 1, 3, 79, 87, 78, 43, 82, 13, 11, 32, 10, 154, 19, 67, 248, 255, 12, + 10, 77, 73, 78, 85, 83, 32, 87, 72, 73, 84, 168, 169, 6, 6, 87, 73, 84, + 72, 32, 68, 178, 174, 15, 79, 135, 13, 83, 12, 214, 19, 32, 62, 45, 203, + 182, 31, 87, 6, 248, 202, 34, 2, 79, 80, 179, 21, 65, 8, 18, 76, 39, 79, + 4, 194, 243, 27, 79, 179, 184, 8, 65, 4, 184, 219, 2, 2, 85, 82, 147, + 173, 31, 76, 12, 38, 69, 210, 227, 34, 65, 251, 2, 79, 6, 152, 229, 34, + 2, 65, 82, 247, 11, 88, 40, 64, 5, 65, 82, 71, 69, 32, 140, 2, 3, 69, 70, + 84, 203, 1, 79, 12, 48, 6, 67, 73, 82, 67, 76, 69, 211, 135, 35, 83, 11, + 37, 7, 32, 77, 73, 78, 85, 83, 32, 8, 54, 76, 36, 4, 82, 73, 71, 72, 13, + 3, 85, 80, 80, 4, 32, 2, 69, 70, 13, 2, 79, 87, 2, 31, 84, 2, 17, 2, 69, + 82, 2, 229, 166, 33, 8, 32, 81, 85, 65, 82, 84, 69, 82, 22, 96, 10, 45, + 80, 79, 73, 78, 84, 73, 78, 71, 32, 64, 6, 87, 65, 82, 68, 83, 32, 227, + 228, 34, 32, 12, 146, 7, 68, 190, 8, 73, 182, 216, 34, 80, 206, 24, 83, + 51, 84, 4, 174, 184, 33, 69, 231, 140, 1, 66, 6, 158, 15, 87, 167, 224, + 34, 90, 30, 76, 6, 69, 68, 73, 85, 77, 32, 205, 226, 21, 7, 79, 79, 78, + 32, 76, 73, 76, 28, 66, 68, 42, 76, 36, 4, 82, 73, 71, 72, 12, 2, 85, 80, + 111, 83, 6, 84, 3, 79, 87, 78, 155, 230, 34, 73, 6, 32, 2, 69, 70, 187, + 237, 34, 79, 4, 11, 84, 4, 81, 18, 45, 80, 79, 73, 78, 84, 73, 78, 71, + 32, 84, 82, 73, 65, 78, 71, 76, 69, 5, 145, 9, 2, 32, 67, 8, 198, 13, 77, + 255, 243, 34, 81, 4, 194, 153, 17, 69, 191, 204, 17, 73, 8, 158, 140, 25, + 85, 230, 217, 9, 65, 87, 69, 34, 52, 4, 73, 71, 72, 84, 162, 145, 34, 79, + 239, 85, 69, 30, 94, 32, 84, 10, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, + 245, 1, 6, 87, 65, 82, 68, 83, 32, 6, 60, 9, 84, 82, 73, 65, 78, 71, 76, + 69, 32, 163, 248, 34, 80, 2, 243, 206, 6, 67, 16, 90, 68, 88, 8, 84, 82, + 73, 65, 78, 71, 76, 69, 230, 7, 73, 186, 221, 34, 80, 203, 19, 83, 4, 65, + 14, 79, 85, 66, 76, 69, 32, 84, 82, 73, 65, 78, 71, 76, 69, 5, 175, 194, + 12, 32, 5, 241, 237, 20, 11, 32, 87, 73, 84, 72, 32, 68, 79, 85, 66, 76, + 8, 250, 166, 19, 65, 202, 137, 14, 69, 231, 140, 1, 66, 40, 158, 2, 77, + 148, 1, 5, 81, 85, 65, 82, 69, 180, 133, 26, 3, 85, 78, 32, 160, 224, 1, + 5, 75, 85, 76, 76, 32, 228, 183, 3, 3, 78, 79, 87, 168, 228, 1, 5, 65, + 70, 69, 84, 89, 184, 131, 1, 13, 76, 73, 71, 72, 84, 76, 89, 32, 83, 77, + 65, 76, 76, 198, 93, 67, 50, 72, 222, 1, 80, 191, 19, 84, 12, 40, 4, 65, + 76, 76, 32, 195, 229, 34, 73, 10, 206, 221, 34, 68, 150, 7, 76, 70, 83, + 213, 15, 13, 85, 80, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 67, 9, 11, + 32, 6, 54, 67, 140, 198, 33, 3, 70, 79, 82, 223, 159, 1, 66, 2, 245, 134, + 31, 3, 69, 78, 84, 14, 192, 4, 3, 73, 78, 89, 214, 178, 23, 82, 174, 182, + 11, 79, 54, 69, 135, 2, 87, 18, 38, 80, 157, 233, 32, 3, 78, 73, 86, 16, + 46, 32, 62, 45, 170, 1, 80, 163, 181, 31, 87, 2, 225, 240, 34, 10, 80, + 79, 73, 78, 84, 73, 78, 71, 32, 66, 8, 45, 9, 80, 79, 73, 78, 84, 73, 78, + 71, 32, 8, 66, 73, 148, 237, 34, 5, 68, 79, 85, 66, 76, 238, 3, 83, 51, + 84, 2, 141, 238, 33, 8, 83, 79, 83, 67, 69, 76, 69, 83, 4, 21, 3, 69, 82, + 32, 4, 182, 187, 24, 76, 163, 178, 9, 82, 10, 56, 6, 84, 73, 67, 65, 76, + 32, 41, 4, 89, 32, 83, 77, 4, 144, 219, 34, 2, 82, 69, 235, 22, 69, 6, + 21, 3, 65, 76, 76, 6, 11, 32, 6, 178, 215, 34, 68, 150, 7, 76, 135, 21, + 83, 16, 84, 15, 76, 69, 84, 84, 69, 82, 32, 67, 65, 80, 73, 84, 65, 76, + 32, 247, 150, 11, 70, 10, 166, 149, 36, 67, 2, 72, 2, 73, 2, 82, 3, 90, + 200, 4, 52, 3, 67, 75, 32, 186, 151, 2, 83, 167, 206, 10, 87, 196, 4, 80, + 7, 79, 67, 84, 65, 78, 84, 45, 149, 7, 8, 83, 69, 88, 84, 65, 78, 84, 45, + 204, 3, 58, 49, 130, 3, 50, 114, 53, 50, 54, 18, 51, 215, 1, 52, 234, 1, + 74, 50, 246, 1, 51, 174, 91, 52, 46, 53, 38, 54, 30, 55, 199, 180, 35, + 56, 116, 62, 51, 226, 92, 52, 46, 53, 38, 54, 30, 55, 199, 180, 35, 56, + 55, 54, 52, 214, 92, 53, 38, 54, 30, 55, 199, 180, 35, 56, 22, 50, 53, + 166, 2, 54, 190, 90, 55, 199, 180, 35, 56, 11, 42, 54, 202, 229, 28, 55, + 179, 171, 7, 56, 4, 246, 144, 36, 55, 3, 56, 56, 170, 1, 53, 50, 54, 210, + 89, 52, 110, 55, 199, 180, 35, 56, 118, 62, 52, 254, 89, 51, 98, 53, 38, + 54, 30, 55, 199, 180, 35, 56, 24, 46, 53, 50, 54, 190, 90, 55, 199, 180, + 35, 56, 13, 206, 1, 54, 178, 226, 28, 55, 179, 171, 7, 56, 7, 187, 90, + 55, 61, 54, 52, 118, 53, 230, 88, 54, 30, 55, 199, 180, 35, 56, 31, 46, + 53, 170, 89, 54, 30, 55, 199, 180, 35, 56, 15, 38, 54, 158, 89, 55, 199, + 180, 35, 56, 7, 222, 141, 36, 55, 3, 56, 14, 226, 88, 54, 30, 55, 199, + 180, 35, 56, 31, 46, 54, 234, 87, 53, 66, 55, 199, 180, 35, 56, 6, 166, + 88, 55, 199, 180, 35, 56, 120, 70, 49, 238, 1, 50, 162, 193, 25, 51, 38, + 52, 30, 53, 187, 200, 10, 54, 61, 58, 50, 126, 51, 250, 193, 25, 52, 30, + 53, 187, 200, 10, 54, 31, 50, 51, 194, 194, 25, 52, 30, 53, 187, 200, 10, + 54, 15, 42, 52, 178, 194, 25, 53, 187, 200, 10, 54, 7, 230, 138, 36, 53, + 3, 54, 15, 246, 193, 25, 52, 186, 157, 3, 53, 159, 171, 7, 54, 31, 50, + 52, 238, 192, 25, 51, 66, 53, 187, 200, 10, 54, 7, 171, 193, 25, 53, 6, + 150, 157, 22, 32, 213, 206, 8, 4, 66, 69, 82, 82, 232, 4, 200, 1, 3, 76, + 68, 32, 78, 79, 104, 7, 80, 79, 77, 79, 70, 79, 32, 188, 6, 2, 84, 84, + 216, 6, 5, 85, 81, 85, 69, 84, 54, 87, 198, 1, 88, 182, 217, 5, 77, 222, + 170, 3, 89, 162, 186, 26, 65, 139, 34, 78, 14, 138, 189, 8, 83, 242, 208, + 7, 69, 254, 223, 17, 70, 234, 2, 87, 203, 11, 71, 10, 26, 75, 187, 148, + 23, 77, 9, 40, 4, 77, 65, 82, 75, 187, 134, 36, 83, 5, 241, 174, 23, 3, + 32, 84, 65, 150, 1, 96, 13, 70, 73, 78, 65, 76, 32, 76, 69, 84, 84, 69, + 82, 32, 53, 7, 76, 69, 84, 84, 69, 82, 32, 10, 174, 133, 36, 71, 2, 72, + 2, 75, 2, 80, 3, 84, 140, 1, 234, 1, 65, 54, 85, 22, 69, 82, 71, 46, 73, + 70, 78, 38, 79, 98, 90, 190, 156, 7, 75, 230, 210, 24, 67, 2, 76, 2, 83, + 230, 57, 66, 186, 202, 1, 74, 198, 140, 2, 68, 2, 70, 2, 72, 2, 77, 2, + 80, 2, 81, 2, 82, 2, 84, 2, 86, 3, 88, 21, 50, 73, 2, 85, 74, 78, 146, + 130, 36, 72, 3, 77, 5, 247, 178, 35, 78, 17, 50, 78, 146, 130, 36, 69, 2, + 72, 2, 73, 3, 82, 7, 142, 130, 36, 71, 3, 78, 11, 242, 129, 36, 72, 2, + 78, 2, 85, 3, 87, 15, 216, 131, 34, 2, 78, 78, 238, 253, 1, 72, 2, 77, 2, + 82, 3, 85, 9, 130, 131, 34, 71, 131, 254, 1, 78, 17, 66, 78, 154, 255, + 34, 32, 134, 129, 1, 69, 2, 77, 2, 79, 3, 85, 4, 154, 128, 36, 71, 3, 78, + 9, 254, 255, 35, 72, 2, 73, 3, 89, 66, 104, 3, 79, 77, 32, 145, 183, 21, + 17, 76, 69, 32, 87, 73, 84, 72, 32, 80, 79, 80, 80, 73, 78, 71, 32, 67, + 64, 152, 2, 5, 72, 65, 76, 70, 32, 232, 1, 5, 76, 69, 70, 84, 32, 88, 6, + 82, 73, 71, 72, 84, 32, 88, 14, 83, 81, 85, 65, 82, 69, 32, 66, 82, 65, + 67, 75, 69, 84, 210, 216, 15, 65, 150, 150, 16, 67, 210, 3, 80, 140, 3, + 13, 74, 85, 83, 84, 73, 70, 73, 69, 68, 32, 85, 80, 80, 135, 5, 84, 32, + 148, 1, 16, 70, 79, 82, 87, 65, 82, 68, 45, 70, 65, 67, 73, 78, 71, 32, + 82, 186, 243, 31, 76, 22, 82, 252, 2, 2, 83, 84, 174, 5, 66, 211, 245, 1, + 73, 10, 248, 162, 29, 11, 85, 78, 78, 69, 82, 32, 70, 82, 65, 77, 69, + 155, 210, 2, 79, 8, 248, 248, 31, 13, 74, 85, 83, 84, 73, 70, 73, 69, 68, + 32, 85, 80, 80, 126, 67, 51, 72, 8, 158, 249, 31, 67, 50, 72, 33, 13, 74, + 85, 83, 84, 73, 70, 73, 69, 68, 32, 85, 80, 80, 5, 181, 219, 23, 9, 32, + 79, 86, 69, 82, 32, 84, 79, 80, 5, 133, 232, 34, 8, 32, 79, 70, 32, 70, + 76, 79, 87, 14, 58, 76, 116, 3, 84, 73, 69, 201, 153, 33, 3, 32, 65, 78, + 6, 26, 32, 131, 167, 35, 73, 4, 172, 187, 9, 7, 79, 70, 32, 72, 89, 71, + 73, 149, 146, 23, 6, 87, 73, 84, 72, 32, 83, 7, 135, 215, 32, 32, 218, 2, + 88, 10, 32, 68, 82, 65, 87, 73, 78, 71, 83, 32, 205, 249, 34, 6, 73, 78, + 71, 32, 71, 76, 216, 2, 176, 1, 2, 68, 79, 228, 6, 6, 72, 69, 65, 86, 89, + 32, 254, 2, 76, 204, 25, 6, 82, 73, 71, 72, 84, 32, 144, 4, 3, 85, 80, + 32, 245, 3, 9, 86, 69, 82, 84, 73, 67, 65, 76, 32, 66, 52, 5, 85, 66, 76, + 69, 32, 165, 3, 3, 87, 78, 32, 30, 58, 68, 216, 2, 2, 85, 80, 234, 5, 86, + 255, 171, 29, 72, 14, 64, 8, 73, 65, 71, 79, 78, 65, 76, 32, 149, 2, 3, + 79, 87, 78, 8, 104, 6, 85, 80, 80, 69, 82, 32, 205, 14, 15, 76, 79, 87, + 69, 82, 32, 76, 69, 70, 84, 32, 84, 79, 32, 77, 6, 76, 8, 76, 69, 70, 84, + 32, 84, 79, 32, 237, 24, 6, 82, 73, 71, 72, 84, 32, 4, 204, 23, 14, 77, + 73, 68, 68, 76, 69, 32, 67, 69, 78, 84, 82, 69, 32, 243, 177, 23, 76, 6, + 199, 26, 32, 36, 128, 1, 10, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 132, + 1, 10, 76, 73, 71, 72, 84, 32, 65, 78, 68, 32, 210, 38, 68, 131, 4, 83, + 12, 76, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 228, 35, 2, 85, 80, 151, 5, + 72, 4, 17, 2, 84, 32, 4, 230, 32, 85, 251, 163, 35, 76, 12, 76, 3, 76, + 69, 70, 0, 4, 82, 73, 71, 72, 156, 36, 2, 85, 80, 235, 4, 72, 4, 17, 2, + 84, 32, 4, 198, 32, 85, 135, 9, 72, 46, 136, 1, 4, 76, 69, 70, 84, 68, 2, + 85, 80, 130, 1, 86, 172, 20, 2, 68, 79, 170, 2, 81, 100, 2, 84, 82, 198, + 148, 29, 72, 247, 148, 6, 82, 5, 45, 9, 32, 65, 78, 68, 32, 76, 73, 71, + 72, 2, 179, 153, 34, 84, 11, 29, 5, 32, 65, 78, 68, 32, 8, 42, 76, 178, + 172, 29, 72, 247, 148, 6, 82, 4, 144, 179, 24, 4, 73, 71, 72, 84, 131, + 142, 11, 69, 8, 209, 20, 7, 69, 82, 84, 73, 67, 65, 76, 156, 1, 56, 4, + 69, 70, 84, 32, 169, 2, 5, 73, 71, 72, 84, 32, 16, 160, 27, 19, 68, 79, + 87, 78, 32, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 82, 73, 71, 72, 24, + 14, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 82, 73, 71, 72, 100, 14, 76, + 73, 71, 72, 84, 32, 65, 78, 68, 32, 82, 73, 71, 72, 97, 17, 85, 80, 32, + 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 82, 73, 71, 72, 140, 1, 248, 1, + 4, 65, 82, 67, 32, 30, 68, 248, 15, 10, 72, 79, 82, 73, 90, 79, 78, 84, + 65, 76, 112, 4, 76, 69, 70, 84, 62, 81, 34, 84, 172, 1, 2, 85, 80, 120, + 8, 86, 69, 82, 84, 73, 67, 65, 76, 156, 199, 13, 7, 66, 79, 84, 84, 79, + 77, 32, 191, 224, 21, 82, 8, 166, 210, 17, 68, 67, 85, 80, 52, 8, 73, 65, + 71, 79, 78, 65, 76, 32, 199, 14, 79, 68, 168, 1, 14, 76, 79, 87, 69, 82, + 32, 76, 69, 70, 84, 32, 84, 79, 32, 96, 7, 77, 73, 68, 68, 76, 69, 32, + 136, 3, 6, 85, 80, 80, 69, 82, 32, 154, 157, 34, 67, 183, 4, 68, 4, 38, + 77, 33, 5, 85, 80, 80, 69, 82, 2, 29, 5, 73, 68, 68, 76, 69, 2, 129, 12, + 2, 32, 67, 16, 88, 8, 76, 69, 70, 84, 32, 84, 79, 32, 213, 1, 9, 82, 73, + 71, 72, 84, 32, 84, 79, 32, 10, 156, 1, 6, 76, 79, 87, 69, 82, 32, 33, + 28, 85, 80, 80, 69, 82, 32, 67, 69, 78, 84, 82, 69, 32, 84, 79, 32, 77, + 73, 68, 68, 76, 69, 32, 82, 73, 71, 72, 84, 6, 246, 3, 67, 139, 179, 35, + 82, 5, 171, 199, 20, 32, 6, 232, 4, 15, 85, 80, 80, 69, 82, 32, 67, 69, + 78, 84, 82, 69, 32, 84, 79, 235, 3, 76, 44, 144, 1, 10, 67, 69, 78, 84, + 82, 69, 32, 84, 79, 32, 248, 3, 8, 76, 69, 70, 84, 32, 84, 79, 32, 193, + 2, 9, 82, 73, 71, 72, 84, 32, 84, 79, 32, 20, 52, 7, 77, 73, 68, 68, 76, + 69, 32, 255, 180, 23, 76, 16, 56, 4, 76, 69, 70, 84, 165, 1, 5, 82, 73, + 71, 72, 84, 9, 11, 32, 6, 84, 10, 84, 79, 32, 76, 79, 87, 69, 82, 32, 67, + 141, 195, 20, 5, 65, 78, 68, 32, 77, 4, 29, 5, 69, 78, 84, 82, 69, 5, + 189, 215, 32, 3, 32, 84, 79, 9, 11, 32, 6, 88, 3, 65, 78, 68, 65, 15, 84, + 79, 32, 76, 79, 87, 69, 82, 32, 67, 69, 78, 84, 82, 69, 2, 149, 194, 20, + 11, 32, 77, 73, 68, 68, 76, 69, 32, 76, 69, 70, 5, 209, 215, 10, 9, 32, + 84, 79, 32, 77, 73, 68, 68, 76, 14, 68, 6, 76, 79, 87, 69, 82, 32, 97, 7, + 77, 73, 68, 68, 76, 69, 32, 6, 48, 6, 67, 69, 78, 84, 82, 69, 239, 175, + 35, 82, 5, 11, 32, 2, 233, 4, 4, 84, 79, 32, 85, 8, 76, 10, 67, 69, 78, + 84, 82, 69, 32, 84, 79, 32, 33, 5, 82, 73, 71, 72, 84, 4, 250, 3, 85, + 231, 202, 13, 76, 5, 11, 32, 2, 157, 206, 13, 2, 84, 79, 10, 46, 76, 69, + 7, 77, 73, 68, 68, 76, 69, 32, 4, 29, 5, 79, 87, 69, 82, 32, 4, 254, 210, + 32, 67, 191, 218, 2, 76, 6, 34, 67, 37, 4, 76, 69, 70, 84, 2, 45, 6, 69, + 78, 84, 82, 69, 32, 5, 11, 32, 2, 217, 174, 23, 2, 84, 79, 12, 36, 2, 87, + 78, 253, 2, 2, 85, 66, 9, 11, 32, 6, 25, 4, 65, 78, 68, 32, 6, 254, 150, + 29, 72, 218, 148, 6, 76, 31, 82, 9, 11, 32, 6, 40, 4, 65, 78, 68, 32, + 239, 164, 35, 87, 4, 26, 85, 135, 173, 23, 76, 2, 149, 173, 23, 2, 80, + 80, 5, 133, 130, 34, 10, 32, 65, 78, 68, 32, 72, 69, 65, 86, 89, 4, 109, + 5, 85, 65, 68, 82, 85, 6, 66, 82, 189, 201, 13, 10, 79, 80, 32, 65, 78, + 68, 32, 85, 80, 80, 4, 11, 73, 4, 11, 80, 4, 41, 8, 76, 69, 32, 68, 65, + 83, 72, 32, 4, 166, 181, 16, 86, 219, 222, 12, 72, 11, 29, 5, 32, 65, 78, + 68, 32, 8, 34, 72, 242, 167, 35, 76, 31, 82, 4, 248, 153, 24, 4, 69, 65, + 86, 89, 171, 249, 4, 79, 17, 29, 5, 32, 65, 78, 68, 32, 14, 154, 152, 28, + 66, 42, 84, 130, 122, 72, 218, 148, 6, 76, 31, 82, 16, 148, 2, 18, 68, + 79, 87, 78, 32, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 76, 69, 70, 24, + 13, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 76, 69, 70, 100, 13, 76, 73, + 71, 72, 84, 32, 65, 78, 68, 32, 76, 69, 70, 97, 16, 85, 80, 32, 72, 69, + 65, 86, 89, 32, 65, 78, 68, 32, 76, 69, 70, 2, 101, 3, 84, 32, 85, 6, 17, + 2, 84, 32, 6, 58, 85, 178, 3, 68, 245, 4, 6, 86, 69, 82, 84, 73, 67, 2, + 231, 177, 33, 80, 6, 17, 2, 84, 32, 6, 58, 85, 134, 4, 68, 205, 4, 6, 86, + 69, 82, 84, 73, 67, 2, 239, 8, 80, 2, 185, 2, 3, 84, 32, 68, 36, 128, 1, + 10, 72, 69, 65, 86, 89, 32, 65, 78, 68, 32, 188, 1, 10, 76, 73, 71, 72, + 84, 32, 65, 78, 68, 32, 186, 2, 68, 131, 4, 83, 12, 80, 4, 68, 79, 87, + 78, 24, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 255, 4, 72, 2, 145, 5, 2, + 32, 72, 4, 17, 2, 84, 32, 4, 26, 68, 203, 160, 35, 76, 2, 181, 174, 33, + 3, 79, 87, 78, 12, 80, 4, 68, 79, 87, 78, 24, 3, 76, 69, 70, 0, 4, 82, + 73, 71, 72, 211, 4, 72, 2, 229, 4, 2, 32, 72, 4, 17, 2, 84, 32, 4, 22, + 68, 131, 5, 72, 2, 233, 4, 3, 79, 87, 78, 24, 130, 1, 68, 188, 1, 10, 72, + 69, 65, 86, 89, 32, 65, 78, 68, 32, 144, 1, 10, 76, 73, 71, 72, 84, 32, + 65, 78, 68, 32, 183, 1, 83, 6, 49, 10, 79, 85, 66, 76, 69, 32, 65, 78, + 68, 32, 6, 92, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 13, 10, 72, 79, 82, + 73, 90, 79, 78, 84, 65, 76, 2, 11, 84, 2, 221, 240, 26, 2, 32, 83, 6, 54, + 72, 68, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, 2, 37, 7, 79, 82, 73, 90, + 79, 78, 84, 2, 193, 169, 33, 2, 65, 76, 2, 167, 169, 33, 84, 6, 54, 72, + 60, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, 2, 37, 7, 79, 82, 73, 90, 79, + 78, 84, 2, 29, 2, 65, 76, 2, 11, 84, 2, 17, 2, 32, 72, 2, 149, 179, 35, + 3, 69, 65, 86, 6, 49, 10, 73, 78, 71, 76, 69, 32, 65, 78, 68, 32, 6, 96, + 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 229, 180, 26, 9, 72, 79, 82, 73, 90, + 79, 78, 84, 65, 2, 239, 180, 26, 84, 134, 6, 46, 65, 178, 14, 69, 150, 1, + 73, 179, 1, 79, 232, 5, 36, 4, 72, 77, 73, 32, 247, 9, 73, 230, 1, 192, + 1, 7, 76, 69, 84, 84, 69, 82, 32, 196, 2, 7, 78, 85, 77, 66, 69, 82, 32, + 144, 2, 12, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 84, 5, 83, + 73, 71, 78, 32, 122, 86, 211, 249, 24, 68, 108, 210, 1, 79, 166, 225, 31, + 65, 38, 68, 114, 84, 46, 86, 186, 5, 85, 186, 202, 1, 73, 42, 76, 226, + 195, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, + 72, 2, 77, 2, 82, 2, 89, 187, 2, 69, 15, 45, 9, 76, 68, 32, 84, 65, 77, + 73, 76, 32, 12, 202, 135, 29, 76, 142, 155, 2, 83, 210, 97, 78, 131, 246, + 2, 82, 42, 82, 69, 38, 70, 66, 78, 26, 83, 234, 176, 21, 84, 130, 234, 5, + 79, 227, 227, 7, 74, 4, 181, 172, 31, 4, 73, 71, 72, 84, 8, 26, 79, 195, + 151, 21, 73, 4, 178, 205, 33, 82, 135, 183, 1, 85, 4, 65, 3, 73, 78, 69, + 8, 40, 4, 69, 86, 69, 78, 1, 2, 73, 88, 5, 227, 170, 35, 84, 10, 46, 76, + 206, 213, 12, 68, 177, 16, 2, 67, 82, 4, 242, 198, 19, 79, 199, 217, 14, + 73, 12, 226, 157, 31, 67, 228, 67, 9, 79, 76, 68, 32, 84, 65, 77, 73, 76, + 246, 157, 1, 74, 158, 2, 86, 122, 85, 255, 243, 1, 65, 34, 64, 10, 79, + 87, 69, 76, 32, 83, 73, 71, 78, 32, 239, 253, 32, 73, 32, 138, 1, 79, + 152, 183, 29, 11, 66, 72, 65, 84, 84, 73, 80, 82, 79, 76, 85, 198, 170, + 2, 65, 38, 85, 22, 86, 166, 202, 1, 73, 199, 140, 2, 69, 7, 233, 156, 31, + 10, 76, 68, 32, 84, 65, 77, 73, 76, 32, 83, 130, 4, 72, 12, 76, 76, 69, + 32, 80, 65, 84, 84, 69, 82, 78, 32, 243, 183, 35, 78, 128, 4, 44, 5, 68, + 79, 84, 83, 45, 175, 246, 6, 66, 254, 3, 74, 49, 74, 50, 66, 51, 54, 52, + 46, 53, 38, 54, 30, 55, 199, 180, 35, 56, 129, 2, 66, 50, 66, 51, 54, 52, + 46, 53, 38, 54, 30, 55, 199, 180, 35, 56, 129, 1, 58, 51, 54, 52, 46, 53, + 38, 54, 30, 55, 199, 180, 35, 56, 65, 50, 52, 46, 53, 38, 54, 30, 55, + 199, 180, 35, 56, 33, 42, 53, 38, 54, 30, 55, 199, 180, 35, 56, 17, 34, + 54, 30, 55, 199, 180, 35, 56, 9, 26, 55, 199, 180, 35, 56, 5, 195, 180, + 35, 56, 8, 26, 65, 195, 157, 35, 86, 6, 196, 193, 20, 11, 75, 32, 80, 69, + 82, 77, 73, 84, 84, 69, 68, 152, 172, 8, 6, 83, 84, 45, 70, 69, 69, 183, + 198, 6, 68, 10, 42, 68, 96, 2, 69, 70, 251, 174, 35, 67, 4, 136, 181, 32, + 6, 71, 69, 32, 65, 84, 32, 149, 158, 1, 9, 69, 32, 87, 73, 84, 72, 32, + 86, 69, 4, 242, 136, 32, 67, 159, 169, 3, 83, 12, 84, 4, 75, 69, 78, 32, + 192, 196, 8, 2, 67, 67, 152, 229, 25, 2, 87, 78, 231, 118, 79, 6, 248, + 182, 27, 17, 67, 73, 82, 67, 76, 69, 32, 87, 73, 84, 72, 32, 78, 79, 82, + 84, 72, 222, 214, 6, 66, 151, 28, 72, 134, 1, 208, 1, 4, 66, 66, 76, 69, + 46, 71, 156, 4, 4, 72, 73, 68, 32, 36, 2, 76, 76, 122, 83, 56, 4, 84, 84, + 69, 82, 240, 253, 10, 10, 73, 76, 68, 73, 78, 71, 32, 67, 79, 78, 248, + 217, 16, 2, 82, 82, 255, 253, 6, 67, 4, 196, 154, 28, 2, 32, 84, 131, + 148, 7, 83, 63, 33, 6, 73, 78, 69, 83, 69, 32, 60, 144, 1, 7, 76, 69, 84, + 84, 69, 82, 32, 172, 2, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, + 146, 184, 16, 69, 193, 248, 13, 4, 80, 65, 76, 76, 46, 154, 1, 77, 34, + 78, 242, 168, 35, 66, 2, 67, 2, 68, 2, 71, 2, 72, 2, 74, 2, 75, 2, 76, 2, + 80, 2, 82, 2, 83, 2, 84, 2, 86, 2, 89, 187, 2, 65, 4, 142, 169, 35, 80, + 187, 2, 65, 12, 46, 71, 34, 89, 162, 168, 35, 82, 187, 2, 65, 4, 190, + 168, 35, 75, 187, 2, 65, 4, 158, 168, 35, 67, 187, 2, 65, 10, 226, 147, + 35, 65, 214, 22, 69, 2, 73, 2, 79, 3, 85, 40, 178, 156, 10, 76, 143, 245, + 18, 86, 10, 56, 2, 69, 84, 20, 4, 72, 79, 82, 78, 251, 166, 9, 83, 5, + 231, 229, 31, 32, 5, 241, 193, 27, 5, 32, 87, 73, 84, 72, 9, 26, 84, 199, + 211, 32, 32, 4, 234, 193, 27, 83, 15, 32, 5, 251, 132, 35, 70, 240, 3, + 140, 1, 23, 90, 65, 78, 84, 73, 78, 69, 32, 77, 85, 83, 73, 67, 65, 76, + 32, 83, 89, 77, 66, 79, 76, 32, 149, 188, 20, 5, 84, 69, 32, 79, 82, 238, + 3, 154, 2, 65, 128, 7, 2, 67, 72, 170, 1, 68, 158, 5, 69, 206, 2, 70, + 166, 8, 71, 202, 3, 73, 162, 2, 75, 142, 5, 76, 190, 2, 77, 250, 4, 79, + 114, 80, 174, 4, 82, 42, 83, 194, 5, 84, 196, 5, 2, 86, 65, 250, 1, 89, + 230, 179, 32, 78, 201, 146, 2, 9, 88, 73, 82, 79, 78, 32, 75, 76, 65, 62, + 60, 5, 71, 79, 71, 73, 32, 222, 1, 78, 118, 80, 199, 2, 82, 16, 70, 65, + 0, 2, 71, 79, 64, 2, 77, 69, 37, 5, 80, 79, 76, 73, 32, 4, 17, 2, 82, 71, + 4, 128, 230, 15, 2, 79, 84, 199, 189, 19, 73, 4, 234, 155, 9, 84, 219, + 243, 25, 83, 4, 26, 65, 1, 2, 71, 79, 2, 239, 210, 17, 82, 6, 72, 6, 84, + 73, 75, 69, 78, 79, 213, 128, 35, 6, 65, 84, 82, 73, 67, 72, 4, 168, 32, + 2, 75, 89, 155, 255, 34, 77, 22, 52, 5, 69, 83, 79, 32, 69, 38, 79, 163, + 141, 35, 76, 4, 204, 1, 2, 88, 79, 147, 55, 75, 16, 80, 6, 83, 84, 82, + 79, 70, 79, 188, 40, 3, 68, 69, 82, 237, 129, 1, 2, 84, 72, 10, 24, 2, + 73, 32, 79, 83, 4, 56, 9, 83, 89, 78, 68, 69, 83, 77, 79, 83, 195, 24, + 84, 2, 143, 44, 32, 7, 11, 32, 4, 214, 60, 68, 235, 149, 22, 78, 18, 48, + 2, 71, 79, 33, 6, 75, 84, 73, 75, 79, 32, 4, 170, 21, 83, 179, 137, 35, + 78, 14, 182, 199, 27, 86, 146, 184, 7, 90, 162, 8, 75, 254, 2, 68, 2, 78, + 162, 17, 71, 3, 80, 14, 80, 4, 82, 79, 65, 32, 168, 37, 4, 79, 82, 69, + 86, 221, 2, 4, 65, 77, 73, 76, 6, 132, 223, 19, 2, 83, 80, 156, 188, 3, + 3, 90, 89, 71, 205, 204, 10, 3, 75, 76, 73, 42, 50, 73, 192, 223, 8, 2, + 65, 83, 215, 157, 26, 89, 38, 122, 65, 200, 1, 5, 69, 83, 73, 83, 32, 70, + 71, 208, 1, 3, 80, 76, 73, 173, 192, 27, 8, 70, 84, 79, 71, 71, 79, 83, + 32, 10, 48, 6, 83, 84, 79, 76, 73, 32, 167, 224, 33, 82, 8, 80, 6, 65, + 80, 76, 73, 32, 77, 174, 55, 68, 237, 224, 22, 5, 84, 72, 69, 83, 69, 4, + 40, 2, 69, 71, 141, 174, 28, 2, 73, 75, 2, 247, 149, 30, 65, 12, 38, 84, + 246, 50, 65, 42, 68, 63, 77, 6, 194, 10, 69, 239, 41, 82, 10, 72, 5, 79, + 82, 71, 79, 78, 225, 151, 35, 7, 82, 65, 77, 77, 65, 32, 71, 9, 69, 15, + 32, 80, 65, 82, 69, 83, 84, 73, 71, 77, 69, 78, 79, 78, 32, 6, 222, 38, + 68, 137, 10, 8, 65, 82, 73, 83, 84, 69, 82, 65, 5, 175, 47, 32, 16, 166, + 1, 78, 116, 6, 84, 69, 82, 79, 78, 32, 188, 44, 4, 88, 79, 32, 69, 188, + 130, 33, 4, 80, 69, 71, 69, 236, 48, 6, 75, 83, 84, 82, 69, 80, 237, 13, + 3, 76, 65, 70, 4, 224, 22, 3, 68, 79, 70, 145, 168, 27, 18, 65, 82, 88, + 73, 83, 32, 75, 65, 73, 32, 70, 84, 72, 79, 82, 65, 32, 86, 4, 208, 11, + 5, 65, 82, 71, 79, 83, 151, 20, 80, 44, 180, 1, 9, 65, 78, 69, 82, 79, + 83, 73, 83, 32, 52, 6, 84, 72, 79, 82, 65, 32, 165, 6, 22, 72, 84, 79, + 82, 65, 32, 83, 75, 76, 73, 82, 79, 78, 32, 67, 72, 82, 79, 77, 65, 32, + 86, 6, 246, 4, 68, 14, 77, 25, 5, 84, 69, 84, 82, 65, 36, 220, 2, 8, 65, + 82, 67, 72, 65, 73, 79, 78, 80, 10, 68, 73, 65, 84, 79, 78, 73, 75, 73, + 32, 96, 11, 73, 32, 89, 70, 69, 83, 73, 83, 32, 84, 69, 32, 15, 77, 65, + 76, 65, 75, 79, 78, 32, 67, 72, 82, 79, 77, 65, 32, 74, 78, 40, 8, 83, + 75, 76, 73, 82, 79, 78, 32, 157, 217, 18, 18, 69, 78, 65, 82, 77, 79, 78, + 73, 79, 83, 32, 65, 78, 84, 73, 70, 79, 78, 5, 57, 12, 32, 68, 69, 89, + 84, 69, 82, 79, 85, 32, 73, 67, 2, 199, 184, 27, 72, 14, 62, 78, 134, + 240, 34, 90, 162, 8, 75, 254, 2, 68, 163, 17, 80, 6, 242, 39, 73, 239, + 168, 33, 65, 2, 237, 42, 4, 84, 65, 82, 84, 4, 18, 68, 15, 77, 2, 35, 73, + 2, 21, 3, 79, 78, 79, 2, 151, 19, 70, 4, 166, 19, 65, 245, 239, 32, 2, + 69, 78, 6, 84, 7, 67, 72, 82, 79, 77, 65, 32, 193, 157, 17, 8, 68, 73, + 65, 84, 79, 78, 79, 78, 4, 42, 86, 169, 161, 17, 4, 83, 89, 78, 65, 2, + 175, 229, 32, 65, 22, 80, 6, 69, 78, 73, 75, 73, 32, 44, 2, 79, 82, 185, + 26, 5, 82, 79, 78, 84, 72, 4, 244, 132, 31, 2, 68, 73, 1, 2, 89, 70, 16, + 68, 2, 71, 79, 225, 1, 10, 84, 72, 77, 73, 75, 79, 78, 32, 78, 32, 12, + 28, 2, 78, 32, 155, 1, 83, 10, 96, 14, 80, 65, 82, 69, 83, 84, 73, 71, + 77, 69, 78, 79, 78, 32, 242, 33, 65, 113, 3, 78, 69, 79, 4, 214, 24, 68, + 153, 223, 32, 5, 65, 82, 73, 83, 84, 2, 141, 212, 33, 5, 89, 78, 84, 72, + 69, 4, 142, 28, 65, 1, 2, 68, 73, 16, 56, 2, 77, 73, 114, 83, 233, 135, + 34, 4, 67, 72, 65, 68, 8, 34, 70, 169, 28, 3, 68, 73, 65, 6, 40, 4, 84, + 72, 79, 82, 175, 251, 33, 79, 4, 250, 183, 34, 79, 227, 79, 65, 6, 44, 6, + 65, 75, 73, 65, 32, 84, 231, 13, 79, 2, 217, 204, 21, 12, 69, 76, 79, 85, + 83, 32, 73, 67, 72, 73, 77, 65, 54, 108, 2, 65, 84, 96, 6, 69, 78, 84, + 73, 77, 65, 140, 1, 5, 76, 65, 83, 77, 65, 18, 79, 98, 82, 175, 1, 89, 6, + 40, 3, 65, 86, 65, 201, 3, 2, 72, 73, 4, 140, 29, 5, 32, 84, 82, 79, 77, + 143, 199, 34, 83, 18, 24, 2, 84, 65, 15, 32, 11, 11, 32, 8, 36, 4, 78, + 69, 79, 32, 183, 28, 65, 6, 132, 150, 28, 2, 77, 69, 170, 222, 2, 75, + 235, 230, 3, 65, 7, 243, 28, 32, 8, 64, 6, 78, 84, 69, 86, 77, 65, 222, + 216, 8, 82, 139, 137, 26, 85, 5, 145, 169, 14, 2, 32, 65, 14, 44, 4, 65, + 84, 73, 77, 105, 3, 69, 77, 65, 12, 18, 65, 35, 79, 8, 182, 23, 32, 203, + 232, 34, 84, 4, 134, 12, 75, 233, 203, 31, 5, 89, 80, 79, 82, 82, 2, 227, + 149, 27, 83, 2, 167, 224, 34, 76, 14, 22, 69, 147, 22, 89, 12, 44, 5, 73, + 77, 77, 65, 32, 243, 137, 30, 77, 10, 72, 2, 69, 78, 0, 5, 73, 77, 73, + 83, 69, 54, 84, 69, 3, 68, 89, 79, 2, 161, 169, 27, 8, 79, 83, 32, 67, + 72, 82, 79, 78, 4, 44, 5, 69, 83, 83, 65, 82, 1, 2, 82, 73, 2, 17, 2, 79, + 78, 2, 25, 4, 32, 67, 72, 82, 2, 227, 242, 33, 79, 28, 84, 8, 65, 82, 84, + 89, 82, 73, 65, 32, 153, 250, 30, 7, 73, 75, 82, 79, 78, 32, 73, 26, 72, + 5, 65, 76, 76, 73, 32, 38, 68, 38, 80, 134, 1, 86, 30, 84, 87, 76, 4, 34, + 68, 177, 2, 3, 80, 82, 79, 2, 237, 2, 5, 69, 89, 84, 69, 82, 8, 60, 7, + 76, 65, 71, 73, 79, 83, 32, 45, 4, 82, 79, 84, 79, 4, 200, 1, 5, 84, 69, + 84, 65, 82, 111, 73, 4, 22, 86, 227, 1, 83, 2, 209, 1, 3, 65, 82, 89, 8, + 56, 8, 69, 84, 65, 82, 84, 79, 83, 32, 61, 2, 82, 73, 4, 22, 76, 135, 1, + 73, 2, 21, 3, 69, 71, 69, 2, 63, 84, 4, 18, 70, 35, 84, 2, 145, 201, 21, + 3, 79, 78, 73, 2, 11, 79, 2, 11, 83, 2, 17, 2, 32, 73, 2, 251, 243, 22, + 67, 18, 92, 4, 76, 73, 71, 79, 180, 1, 5, 89, 82, 65, 78, 73, 210, 14, + 88, 173, 153, 34, 2, 77, 65, 4, 211, 1, 78, 42, 60, 2, 65, 82, 100, 2, + 73, 65, 90, 69, 169, 1, 2, 83, 73, 12, 40, 2, 65, 75, 161, 178, 17, 2, + 73, 67, 10, 52, 3, 65, 76, 69, 45, 6, 76, 73, 84, 73, 75, 73, 4, 11, 83, + 4, 17, 2, 77, 65, 4, 23, 32, 7, 11, 32, 4, 198, 15, 65, 135, 155, 22, 78, + 12, 72, 3, 84, 65, 83, 136, 3, 6, 76, 65, 83, 84, 79, 78, 211, 220, 8, + 82, 6, 26, 84, 255, 243, 34, 77, 4, 32, 2, 79, 75, 147, 246, 34, 73, 2, + 217, 212, 34, 2, 79, 85, 14, 36, 5, 70, 73, 83, 84, 79, 67, 76, 10, 46, + 80, 214, 1, 78, 170, 8, 76, 187, 1, 83, 2, 135, 11, 65, 4, 170, 165, 34, + 79, 227, 79, 73, 4, 178, 5, 69, 145, 206, 34, 2, 65, 80, 42, 120, 5, 69, + 73, 83, 77, 65, 32, 8, 73, 77, 65, 78, 83, 73, 83, 32, 182, 1, 84, 154, + 1, 89, 229, 239, 1, 3, 65, 88, 73, 5, 11, 32, 2, 203, 166, 22, 78, 16, + 36, 2, 65, 82, 1, 3, 84, 72, 69, 8, 25, 4, 83, 69, 79, 83, 9, 11, 32, 6, + 18, 84, 39, 68, 4, 34, 82, 13, 4, 69, 84, 82, 65, 2, 11, 73, 2, 141, 155, + 27, 3, 83, 73, 77, 8, 68, 5, 65, 86, 82, 79, 83, 52, 4, 82, 65, 71, 71, + 251, 206, 16, 73, 5, 29, 5, 32, 65, 80, 79, 68, 2, 147, 224, 8, 69, 2, + 253, 239, 1, 2, 73, 83, 12, 34, 78, 149, 1, 3, 82, 77, 65, 8, 36, 5, 65, + 71, 77, 65, 32, 91, 69, 6, 154, 8, 65, 134, 155, 22, 78, 237, 245, 4, 10, + 77, 69, 84, 65, 32, 83, 84, 65, 86, 82, 2, 167, 206, 34, 86, 5, 11, 84, + 2, 243, 253, 16, 73, 40, 42, 69, 70, 72, 218, 1, 82, 227, 2, 73, 6, 136, + 12, 3, 84, 82, 65, 134, 166, 8, 76, 213, 170, 24, 2, 83, 83, 12, 26, 69, + 175, 201, 34, 73, 10, 64, 2, 77, 65, 141, 215, 33, 8, 83, 32, 75, 65, 73, + 32, 65, 80, 9, 56, 2, 32, 65, 33, 8, 84, 73, 83, 77, 79, 83, 32, 69, 2, + 197, 236, 33, 3, 80, 76, 79, 4, 226, 205, 34, 83, 3, 88, 20, 38, 73, 73, + 5, 79, 77, 73, 75, 79, 6, 48, 2, 71, 79, 130, 232, 29, 80, 227, 131, 5, + 65, 2, 171, 177, 33, 82, 14, 42, 76, 32, 2, 78, 32, 62, 80, 95, 83, 2, + 11, 89, 2, 235, 201, 34, 71, 6, 26, 65, 247, 157, 22, 78, 4, 250, 2, 82, + 155, 197, 34, 76, 4, 46, 65, 245, 180, 33, 5, 83, 73, 70, 73, 83, 2, 245, + 200, 34, 6, 82, 65, 75, 65, 76, 69, 2, 11, 89, 2, 217, 199, 16, 2, 78, + 65, 10, 26, 82, 227, 199, 34, 84, 8, 21, 3, 69, 73, 65, 8, 26, 32, 113, + 2, 73, 32, 6, 38, 69, 242, 5, 68, 235, 149, 22, 78, 2, 11, 75, 2, 29, 5, + 70, 79, 78, 73, 84, 2, 173, 152, 34, 2, 73, 75, 2, 11, 65, 2, 11, 82, 2, + 161, 151, 34, 3, 67, 72, 65, 22, 28, 2, 70, 69, 231, 3, 80, 14, 34, 78, + 49, 4, 83, 73, 83, 32, 4, 11, 32, 4, 254, 214, 30, 75, 235, 230, 3, 65, + 10, 42, 65, 42, 68, 62, 77, 89, 2, 84, 82, 2, 133, 2, 6, 80, 76, 73, 32, + 68, 89, 2, 233, 1, 11, 73, 71, 82, 65, 77, 77, 79, 83, 32, 69, 88, 2, + 173, 1, 18, 79, 78, 79, 71, 82, 65, 77, 77, 79, 83, 32, 84, 69, 83, 83, + 69, 82, 65, 4, 11, 73, 4, 60, 11, 71, 82, 65, 77, 77, 79, 83, 32, 79, 75, + 84, 59, 84, 2, 11, 79, 2, 153, 176, 2, 6, 32, 68, 79, 68, 69, 75, 2, 161, + 147, 34, 4, 73, 77, 79, 82, 8, 26, 79, 191, 223, 29, 83, 6, 56, 6, 75, + 82, 73, 83, 73, 83, 233, 235, 29, 2, 82, 82, 5, 17, 2, 32, 68, 2, 11, 73, + 2, 235, 222, 29, 80, 252, 66, 182, 1, 65, 250, 71, 69, 230, 1, 72, 222, + 32, 73, 208, 41, 3, 74, 75, 32, 138, 22, 76, 186, 18, 79, 238, 117, 82, + 234, 7, 85, 150, 128, 2, 89, 186, 167, 19, 71, 190, 235, 10, 83, 203, 18, + 67, 198, 13, 110, 68, 58, 76, 50, 77, 90, 78, 174, 51, 80, 62, 82, 198, + 7, 84, 138, 1, 85, 162, 173, 18, 67, 151, 204, 6, 83, 4, 34, 85, 129, + 148, 27, 2, 65, 32, 2, 223, 220, 7, 67, 4, 196, 211, 9, 3, 76, 32, 77, + 207, 210, 19, 69, 6, 36, 3, 69, 82, 65, 247, 141, 34, 80, 5, 161, 186, + 27, 7, 32, 87, 73, 84, 72, 32, 70, 193, 11, 148, 1, 16, 65, 68, 73, 65, + 78, 32, 83, 89, 76, 76, 65, 66, 73, 67, 83, 32, 148, 49, 2, 67, 69, 94, + 68, 192, 232, 23, 3, 78, 69, 68, 171, 172, 10, 79, 172, 11, 226, 1, 65, + 190, 1, 66, 162, 2, 67, 242, 8, 69, 50, 70, 198, 4, 72, 38, 73, 30, 75, + 134, 1, 76, 82, 77, 174, 1, 78, 202, 4, 81, 178, 1, 79, 194, 1, 80, 70, + 82, 142, 1, 83, 194, 4, 84, 246, 3, 87, 254, 5, 89, 143, 213, 17, 71, 21, + 90, 65, 30, 73, 40, 10, 84, 72, 65, 80, 65, 83, 67, 65, 78, 32, 166, 218, + 34, 78, 3, 89, 7, 230, 218, 34, 73, 3, 89, 5, 221, 225, 23, 5, 86, 73, + 76, 73, 75, 4, 162, 218, 34, 77, 3, 83, 42, 156, 1, 9, 76, 65, 67, 75, + 70, 79, 79, 84, 32, 212, 159, 19, 9, 73, 66, 76, 69, 45, 67, 82, 69, 69, + 189, 203, 7, 10, 69, 65, 86, 69, 82, 32, 68, 69, 78, 69, 36, 82, 87, 238, + 233, 11, 75, 2, 78, 170, 238, 22, 65, 2, 69, 2, 73, 2, 79, 3, 83, 11, + 146, 216, 34, 65, 2, 69, 2, 73, 3, 79, 141, 3, 82, 65, 186, 42, 87, 170, + 173, 8, 72, 130, 182, 23, 79, 238, 60, 73, 199, 140, 2, 69, 241, 2, 48, + 6, 82, 82, 73, 69, 82, 32, 151, 202, 32, 65, 234, 2, 170, 1, 68, 122, 71, + 94, 72, 46, 73, 46, 74, 82, 75, 30, 78, 66, 83, 66, 80, 2, 90, 58, 84, + 38, 76, 132, 1, 2, 67, 72, 2, 77, 2, 82, 2, 87, 2, 89, 223, 184, 34, 69, + 32, 46, 69, 202, 5, 76, 2, 90, 179, 207, 34, 73, 6, 26, 78, 223, 212, 34, + 69, 4, 134, 142, 17, 69, 173, 207, 6, 2, 84, 65, 30, 254, 4, 72, 254, + 169, 21, 87, 198, 147, 9, 65, 210, 209, 3, 69, 162, 64, 73, 2, 79, 3, 85, + 19, 162, 4, 87, 222, 184, 34, 69, 215, 22, 73, 5, 197, 138, 14, 6, 78, + 73, 84, 73, 65, 76, 26, 202, 3, 74, 146, 143, 34, 69, 234, 61, 87, 186, + 2, 65, 2, 73, 2, 79, 3, 85, 26, 154, 1, 75, 227, 1, 72, 14, 238, 145, 34, + 69, 162, 64, 65, 2, 71, 2, 73, 2, 79, 3, 85, 26, 62, 72, 242, 144, 34, + 69, 162, 64, 65, 2, 73, 2, 79, 3, 85, 15, 238, 144, 34, 69, 162, 64, 65, + 2, 73, 2, 79, 3, 85, 72, 34, 76, 70, 84, 66, 72, 3, 83, 24, 130, 1, 72, + 146, 143, 34, 69, 162, 64, 65, 2, 73, 2, 79, 3, 85, 24, 62, 83, 146, 143, + 34, 69, 162, 64, 65, 2, 73, 2, 79, 3, 85, 12, 142, 143, 34, 69, 162, 64, + 65, 2, 73, 2, 79, 3, 85, 7, 236, 29, 4, 65, 83, 84, 69, 139, 177, 34, 78, + 51, 74, 65, 22, 73, 194, 248, 31, 85, 250, 11, 79, 254, 83, 87, 183, 245, + 1, 69, 7, 183, 193, 32, 65, 33, 40, 4, 78, 65, 76, 32, 191, 205, 34, 73, + 28, 160, 1, 2, 68, 79, 134, 1, 82, 78, 83, 138, 136, 14, 71, 186, 2, 65, + 180, 182, 3, 6, 66, 79, 84, 84, 79, 77, 0, 3, 84, 79, 80, 202, 254, 12, + 80, 175, 247, 2, 77, 6, 44, 5, 85, 66, 76, 69, 32, 159, 168, 24, 87, 4, + 240, 196, 6, 12, 83, 72, 79, 82, 84, 32, 86, 69, 82, 84, 73, 67, 239, + 198, 7, 65, 6, 38, 73, 201, 252, 32, 3, 65, 73, 83, 4, 254, 192, 17, 71, + 155, 137, 17, 78, 4, 164, 155, 25, 4, 77, 65, 76, 76, 133, 135, 7, 4, 72, + 79, 82, 84, 4, 158, 185, 26, 89, 231, 144, 8, 75, 7, 222, 201, 34, 73, 3, + 78, 39, 66, 87, 234, 27, 65, 234, 227, 31, 79, 238, 60, 73, 199, 140, 2, + 69, 19, 210, 144, 12, 65, 254, 238, 19, 79, 238, 60, 73, 199, 140, 2, 69, + 49, 142, 7, 72, 154, 20, 65, 66, 87, 170, 227, 31, 79, 238, 60, 73, 199, + 140, 2, 69, 43, 70, 69, 38, 79, 238, 25, 65, 66, 87, 150, 160, 32, 73, + 199, 140, 2, 72, 7, 205, 217, 26, 4, 68, 73, 65, 76, 7, 11, 79, 5, 245, + 255, 32, 8, 83, 69, 45, 67, 82, 69, 69, 32, 149, 1, 164, 1, 8, 45, 67, + 82, 69, 69, 32, 84, 72, 38, 65, 250, 2, 71, 76, 2, 78, 71, 48, 4, 85, 78, + 65, 86, 142, 20, 79, 30, 87, 150, 160, 32, 73, 198, 140, 2, 69, 3, 72, 6, + 210, 184, 32, 73, 199, 140, 2, 69, 63, 104, 6, 83, 75, 65, 80, 73, 32, + 188, 1, 7, 84, 84, 73, 76, 73, 75, 32, 138, 182, 32, 65, 199, 140, 2, 89, + 30, 70, 83, 86, 87, 210, 17, 67, 2, 75, 2, 77, 2, 78, 2, 84, 3, 89, 14, + 226, 201, 29, 67, 2, 80, 2, 84, 236, 103, 2, 75, 87, 190, 156, 2, 87, + 151, 119, 45, 4, 246, 163, 34, 79, 191, 28, 65, 24, 30, 72, 1, 3, 83, 72, + 82, 12, 186, 176, 30, 65, 194, 200, 1, 79, 239, 60, 73, 19, 38, 65, 166, + 248, 31, 79, 239, 60, 73, 9, 142, 181, 32, 65, 199, 140, 2, 73, 15, 190, + 175, 30, 65, 194, 200, 1, 79, 239, 60, 73, 18, 224, 9, 3, 73, 75, 32, + 237, 190, 23, 2, 85, 84, 33, 68, 7, 74, 73, 66, 87, 65, 89, 32, 134, 192, + 34, 78, 2, 79, 3, 89, 24, 74, 78, 218, 174, 30, 83, 226, 144, 4, 67, 2, + 75, 2, 77, 2, 80, 3, 84, 11, 11, 87, 8, 250, 245, 31, 79, 239, 60, 73, + 39, 206, 4, 87, 166, 13, 65, 38, 79, 178, 160, 32, 73, 199, 140, 2, 69, + 39, 104, 7, 45, 67, 82, 69, 69, 32, 82, 146, 14, 87, 182, 2, 65, 234, + 227, 31, 79, 238, 60, 73, 199, 140, 2, 69, 4, 134, 167, 34, 87, 215, 22, + 69, 125, 94, 65, 218, 1, 72, 138, 1, 79, 238, 2, 87, 234, 200, 11, 80, + 230, 225, 20, 73, 199, 140, 2, 69, 43, 26, 89, 255, 175, 32, 65, 37, 25, + 4, 73, 83, 73, 32, 34, 70, 72, 54, 74, 218, 4, 83, 250, 162, 34, 89, 202, + 18, 84, 147, 1, 77, 10, 170, 242, 31, 79, 178, 201, 2, 65, 2, 69, 3, 73, + 6, 170, 228, 30, 85, 255, 214, 3, 73, 37, 70, 87, 202, 13, 79, 202, 244, + 11, 65, 234, 171, 20, 73, 199, 140, 2, 69, 16, 198, 13, 79, 134, 155, 30, + 65, 174, 133, 2, 73, 199, 140, 2, 69, 15, 80, 12, 85, 84, 72, 45, 83, 76, + 65, 86, 69, 89, 32, 75, 170, 185, 34, 79, 3, 89, 8, 150, 184, 34, 65, 2, + 69, 2, 73, 3, 79, 117, 110, 72, 190, 1, 76, 78, 84, 238, 8, 65, 66, 87, + 178, 190, 11, 89, 250, 164, 20, 79, 238, 60, 73, 199, 140, 2, 69, 39, + 108, 7, 45, 67, 82, 69, 69, 32, 84, 226, 4, 87, 206, 160, 30, 65, 194, + 200, 1, 79, 238, 60, 73, 199, 140, 2, 69, 16, 11, 72, 17, 158, 165, 30, + 65, 194, 200, 1, 79, 238, 60, 73, 199, 140, 2, 69, 12, 11, 72, 12, 146, + 237, 31, 79, 222, 178, 2, 87, 214, 22, 65, 2, 69, 3, 73, 24, 50, 72, 210, + 181, 34, 65, 2, 69, 2, 73, 3, 79, 17, 222, 163, 30, 65, 194, 200, 1, 79, + 222, 178, 2, 87, 214, 22, 69, 3, 73, 224, 1, 54, 69, 218, 3, 79, 202, + 248, 11, 65, 235, 171, 20, 73, 185, 1, 17, 2, 83, 84, 182, 1, 44, 6, 45, + 67, 82, 69, 69, 32, 251, 2, 69, 180, 1, 110, 76, 66, 77, 2, 80, 2, 89, + 16, 2, 78, 87, 38, 82, 62, 83, 26, 67, 2, 75, 18, 84, 26, 70, 199, 4, 87, + 27, 178, 6, 87, 234, 154, 30, 65, 194, 200, 1, 79, 179, 201, 2, 69, 17, + 243, 5, 87, 6, 202, 160, 30, 65, 243, 145, 4, 69, 13, 226, 188, 32, 87, + 182, 245, 1, 65, 2, 69, 2, 73, 3, 79, 28, 22, 72, 239, 4, 87, 14, 235, 4, + 87, 16, 22, 72, 199, 4, 87, 2, 231, 187, 32, 87, 2, 223, 187, 23, 82, 31, + 11, 79, 29, 41, 8, 68, 83, 45, 67, 82, 69, 69, 32, 26, 56, 2, 84, 72, + 137, 171, 32, 6, 70, 73, 78, 65, 76, 32, 25, 50, 87, 206, 175, 34, 65, 2, + 69, 2, 73, 3, 79, 14, 218, 157, 30, 65, 194, 200, 1, 79, 238, 60, 73, + 243, 245, 1, 69, 61, 92, 6, 45, 67, 82, 69, 69, 32, 150, 1, 65, 38, 79, + 30, 87, 150, 160, 32, 73, 199, 140, 2, 69, 24, 110, 80, 198, 226, 31, 67, + 2, 75, 2, 76, 2, 77, 2, 78, 2, 83, 2, 84, 2, 89, 134, 172, 2, 79, 247, + 30, 87, 4, 134, 184, 32, 87, 195, 214, 1, 79, 9, 210, 160, 32, 65, 199, + 140, 2, 89, 7, 242, 172, 34, 79, 3, 89, 14, 230, 154, 30, 65, 194, 200, + 1, 79, 238, 60, 73, 199, 140, 2, 69, 10, 26, 76, 255, 171, 34, 82, 9, 26, + 32, 179, 185, 9, 76, 4, 142, 155, 22, 67, 231, 164, 10, 84, 4, 230, 148, + 34, 76, 215, 22, 89, 4, 180, 179, 15, 3, 73, 84, 85, 213, 129, 8, 3, 82, + 73, 67, 124, 140, 1, 2, 68, 32, 102, 69, 72, 11, 73, 65, 78, 32, 76, 69, + 84, 84, 69, 82, 32, 210, 3, 79, 62, 80, 90, 82, 141, 221, 27, 4, 32, 83, + 76, 73, 6, 52, 5, 73, 78, 68, 69, 88, 157, 154, 33, 2, 70, 73, 5, 153, + 152, 33, 6, 32, 68, 73, 86, 73, 68, 6, 26, 84, 203, 194, 21, 32, 5, 141, + 161, 10, 6, 32, 73, 78, 83, 69, 82, 98, 196, 1, 2, 67, 45, 38, 76, 22, + 77, 50, 78, 38, 83, 46, 84, 22, 85, 166, 215, 3, 65, 2, 68, 2, 69, 2, 71, + 2, 75, 2, 80, 210, 247, 26, 82, 218, 201, 1, 73, 198, 140, 2, 66, 2, 79, + 2, 81, 3, 88, 4, 170, 251, 26, 49, 155, 177, 3, 51, 7, 203, 216, 3, 68, + 11, 11, 66, 9, 150, 166, 34, 50, 2, 51, 3, 52, 9, 242, 165, 34, 68, 2, + 71, 3, 78, 13, 226, 215, 3, 72, 2, 84, 239, 205, 30, 83, 7, 183, 215, 3, + 84, 13, 11, 85, 11, 11, 85, 9, 246, 164, 34, 50, 2, 51, 3, 85, 4, 148, + 153, 33, 6, 85, 83, 69, 76, 32, 72, 191, 139, 1, 78, 4, 224, 198, 27, 6, + 32, 83, 84, 82, 69, 65, 137, 223, 5, 7, 69, 78, 84, 82, 89, 32, 83, 4, + 142, 209, 27, 73, 135, 182, 6, 79, 9, 29, 5, 32, 70, 65, 67, 69, 7, 33, + 6, 32, 87, 73, 84, 72, 32, 4, 224, 233, 6, 2, 84, 69, 173, 144, 25, 6, + 87, 82, 89, 32, 83, 77, 108, 88, 16, 67, 65, 83, 73, 65, 78, 32, 65, 76, + 66, 65, 78, 73, 65, 78, 32, 167, 156, 30, 84, 106, 60, 7, 76, 69, 84, 84, + 69, 82, 32, 213, 130, 28, 2, 67, 73, 104, 170, 2, 65, 34, 67, 146, 1, 68, + 78, 69, 34, 71, 46, 73, 46, 74, 34, 75, 34, 76, 34, 80, 34, 83, 74, 84, + 62, 89, 46, 90, 148, 249, 16, 2, 81, 65, 242, 188, 6, 77, 252, 132, 9, 3, + 86, 69, 89, 154, 33, 70, 128, 19, 3, 78, 79, 87, 226, 32, 82, 186, 11, + 88, 154, 46, 79, 202, 26, 66, 237, 24, 3, 72, 69, 89, 4, 238, 229, 33, + 79, 179, 28, 76, 16, 34, 65, 34, 72, 49, 2, 89, 65, 4, 166, 206, 33, 89, + 227, 79, 82, 8, 142, 181, 28, 65, 146, 215, 5, 79, 203, 17, 73, 4, 182, + 157, 34, 87, 3, 89, 8, 42, 90, 214, 208, 32, 89, 243, 175, 1, 65, 4, 206, + 142, 33, 89, 219, 124, 65, 4, 238, 204, 33, 89, 227, 79, 66, 4, 136, 238, + 32, 2, 72, 69, 207, 157, 1, 73, 6, 138, 222, 32, 82, 154, 110, 87, 135, + 77, 78, 4, 138, 141, 33, 72, 227, 16, 65, 4, 150, 157, 33, 73, 199, 69, + 65, 4, 182, 203, 33, 65, 171, 51, 89, 4, 142, 1, 73, 139, 202, 33, 69, 8, + 34, 72, 157, 151, 34, 2, 69, 89, 6, 158, 155, 20, 65, 207, 237, 13, 79, + 6, 38, 73, 202, 139, 33, 89, 199, 80, 65, 2, 139, 225, 33, 87, 4, 176, + 211, 33, 2, 65, 89, 1, 2, 79, 87, 6, 242, 154, 16, 72, 183, 181, 4, 65, + 16, 72, 2, 68, 73, 32, 2, 78, 84, 128, 177, 32, 3, 76, 84, 73, 151, 81, + 82, 4, 194, 145, 33, 32, 223, 100, 76, 8, 32, 2, 82, 69, 131, 145, 33, + 32, 6, 44, 5, 76, 73, 78, 69, 32, 167, 228, 4, 32, 4, 170, 218, 21, 76, + 207, 210, 10, 79, 130, 6, 102, 65, 170, 17, 69, 162, 8, 73, 190, 2, 79, + 188, 193, 13, 6, 82, 73, 83, 84, 77, 65, 207, 132, 10, 85, 198, 2, 66, + 73, 32, 4, 75, 77, 65, 32, 148, 6, 2, 77, 32, 131, 8, 82, 4, 142, 216, + 33, 78, 223, 61, 82, 142, 1, 156, 1, 7, 76, 69, 84, 84, 69, 82, 32, 142, + 3, 83, 98, 86, 194, 207, 23, 68, 154, 236, 7, 81, 176, 95, 5, 77, 65, 65, + 89, 89, 240, 179, 1, 2, 65, 85, 3, 79, 76, 202, 1, 68, 54, 78, 54, 84, + 54, 89, 230, 136, 29, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 76, 2, 80, 170, + 147, 3, 72, 2, 77, 2, 82, 2, 83, 2, 86, 2, 87, 254, 242, 1, 65, 186, 2, + 69, 2, 73, 3, 85, 8, 254, 137, 29, 68, 170, 147, 3, 72, 255, 242, 1, 65, + 8, 242, 156, 32, 71, 2, 78, 2, 89, 255, 242, 1, 65, 8, 150, 137, 29, 84, + 170, 147, 3, 72, 255, 242, 1, 65, 4, 138, 156, 32, 89, 255, 242, 1, 65, + 8, 40, 4, 73, 71, 78, 32, 247, 253, 21, 69, 6, 142, 243, 29, 67, 246, + 227, 1, 86, 247, 244, 1, 65, 26, 64, 10, 79, 87, 69, 76, 32, 83, 73, 71, + 78, 32, 223, 211, 31, 73, 24, 130, 178, 30, 65, 250, 6, 85, 186, 202, 1, + 69, 2, 73, 3, 79, 166, 1, 236, 1, 15, 67, 79, 78, 83, 79, 78, 65, 78, 84, + 32, 83, 73, 71, 78, 32, 112, 7, 76, 69, 84, 84, 69, 82, 32, 152, 4, 12, + 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 60, 11, 86, 79, 87, 69, + 76, 32, 83, 73, 71, 78, 32, 251, 147, 32, 68, 14, 72, 6, 70, 73, 78, 65, + 76, 32, 194, 138, 34, 76, 2, 82, 2, 87, 3, 89, 6, 162, 140, 34, 78, 86, + 72, 3, 77, 104, 132, 2, 6, 70, 73, 78, 65, 76, 32, 110, 78, 50, 77, 78, + 80, 174, 217, 11, 66, 226, 141, 6, 68, 210, 140, 12, 83, 198, 136, 2, 65, + 132, 197, 1, 2, 67, 72, 2, 71, 2, 74, 2, 75, 2, 84, 138, 69, 72, 2, 76, + 2, 82, 2, 86, 2, 89, 186, 2, 69, 2, 73, 2, 79, 3, 85, 22, 202, 140, 32, + 78, 166, 192, 1, 83, 206, 60, 67, 146, 1, 71, 2, 75, 2, 76, 2, 80, 2, 82, + 2, 84, 3, 89, 14, 46, 71, 34, 72, 190, 242, 33, 85, 215, 22, 65, 4, 218, + 242, 33, 85, 215, 22, 65, 6, 186, 242, 33, 85, 158, 20, 74, 187, 2, 65, + 6, 170, 134, 34, 72, 2, 80, 187, 2, 65, 8, 130, 162, 11, 83, 238, 133, + 19, 68, 45, 4, 84, 82, 73, 80, 20, 222, 176, 30, 65, 222, 202, 1, 73, + 166, 204, 1, 79, 2, 85, 203, 44, 69, 14, 72, 7, 65, 67, 84, 69, 82, 32, + 84, 49, 7, 84, 32, 87, 73, 84, 72, 32, 8, 192, 247, 9, 3, 65, 66, 85, + 215, 248, 23, 73, 6, 128, 1, 13, 85, 80, 87, 65, 82, 68, 83, 32, 84, 82, + 69, 78, 68, 213, 169, 25, 12, 68, 79, 87, 78, 87, 65, 82, 68, 83, 32, 84, + 82, 5, 229, 241, 6, 6, 32, 65, 78, 68, 32, 89, 236, 2, 80, 2, 67, 75, 82, + 69, 90, 82, 176, 249, 4, 2, 83, 84, 185, 153, 15, 2, 81, 85, 6, 56, 8, + 69, 82, 32, 66, 79, 65, 82, 68, 195, 194, 33, 32, 5, 151, 136, 31, 32, 4, + 148, 186, 27, 4, 83, 69, 32, 87, 221, 156, 5, 9, 82, 73, 78, 71, 32, 77, + 69, 71, 65, 222, 2, 40, 5, 79, 75, 69, 69, 32, 143, 5, 82, 216, 2, 46, 76, 1, 7, 83, 77, 65, 76, 76, 32, 76, 172, 1, 33, 6, 69, 84, 84, 69, 82, 32, 172, 1, 170, 1, 68, 74, 72, 74, 78, 70, 83, 62, 84, 54, 71, 2, 76, 2, - 77, 0, 2, 81, 85, 2, 87, 2, 89, 194, 228, 33, 75, 186, 2, 65, 2, 69, 2, - 73, 2, 79, 2, 85, 3, 86, 14, 130, 231, 33, 76, 186, 2, 65, 2, 69, 2, 73, - 2, 79, 2, 85, 3, 86, 14, 186, 230, 33, 78, 186, 2, 65, 2, 69, 2, 73, 2, - 79, 2, 85, 3, 86, 14, 142, 200, 29, 65, 158, 160, 4, 69, 2, 73, 2, 79, 2, - 85, 3, 86, 15, 230, 231, 33, 65, 2, 69, 2, 73, 2, 79, 2, 85, 3, 86, 30, - 50, 76, 2, 83, 250, 230, 33, 65, 2, 69, 3, 73, 12, 246, 230, 33, 65, 2, - 69, 2, 73, 2, 79, 2, 85, 3, 86, 4, 48, 6, 89, 32, 66, 76, 79, 83, 147, - 210, 32, 73, 2, 163, 213, 33, 83, 12, 96, 2, 76, 68, 254, 235, 7, 32, - 204, 246, 2, 2, 80, 77, 224, 174, 21, 2, 67, 75, 255, 131, 1, 82, 5, 237, - 216, 28, 7, 82, 69, 78, 32, 67, 82, 79, 60, 92, 8, 82, 65, 83, 77, 73, - 65, 78, 32, 174, 254, 4, 80, 189, 255, 11, 5, 67, 79, 76, 65, 84, 56, 52, - 7, 76, 69, 84, 84, 69, 82, 32, 247, 194, 21, 78, 42, 224, 1, 6, 67, 85, - 82, 76, 69, 68, 30, 83, 206, 191, 21, 68, 34, 76, 158, 206, 1, 82, 178, - 215, 2, 65, 50, 71, 90, 90, 98, 89, 158, 208, 1, 72, 234, 5, 75, 198, 75, - 66, 178, 216, 4, 78, 134, 2, 84, 2, 87, 218, 103, 80, 171, 4, 77, 2, 181, - 227, 32, 2, 32, 87, 6, 180, 191, 21, 6, 77, 65, 76, 76, 32, 65, 252, 239, - 5, 2, 65, 77, 195, 178, 5, 72, 232, 4, 66, 78, 20, 2, 82, 67, 201, 40, 7, - 84, 89, 83, 67, 65, 80, 69, 2, 155, 191, 33, 69, 226, 4, 28, 2, 76, 69, - 207, 39, 85, 220, 4, 30, 32, 185, 6, 2, 68, 32, 24, 244, 1, 5, 87, 73, - 84, 72, 32, 185, 186, 18, 49, 68, 73, 86, 73, 68, 69, 68, 32, 66, 89, 32, - 72, 79, 82, 73, 90, 79, 78, 84, 65, 76, 32, 66, 65, 82, 32, 65, 78, 68, - 32, 84, 79, 80, 32, 72, 65, 76, 70, 32, 68, 73, 86, 73, 68, 69, 68, 32, - 66, 89, 22, 146, 2, 76, 46, 83, 108, 22, 84, 87, 79, 32, 72, 79, 82, 73, - 90, 79, 78, 84, 65, 76, 32, 83, 84, 82, 79, 75, 69, 83, 44, 6, 85, 80, - 80, 69, 82, 32, 44, 17, 65, 76, 76, 32, 66, 85, 84, 32, 85, 80, 80, 69, - 82, 32, 76, 69, 70, 182, 181, 26, 86, 178, 136, 4, 82, 231, 249, 1, 72, - 4, 198, 191, 30, 69, 49, 4, 79, 87, 69, 82, 4, 104, 11, 77, 65, 76, 76, - 32, 67, 73, 82, 67, 76, 69, 221, 5, 10, 85, 80, 69, 82, 73, 77, 80, 79, - 83, 69, 2, 153, 217, 30, 6, 32, 84, 79, 32, 84, 72, 4, 40, 4, 82, 73, 71, - 72, 159, 190, 30, 72, 2, 177, 190, 30, 10, 84, 32, 81, 85, 65, 68, 82, - 65, 78, 84, 196, 4, 230, 2, 65, 186, 1, 66, 58, 67, 230, 1, 68, 246, 1, - 72, 230, 2, 73, 194, 10, 75, 190, 2, 76, 38, 77, 136, 1, 7, 78, 85, 77, - 66, 69, 82, 32, 192, 4, 17, 79, 80, 69, 78, 32, 67, 69, 78, 84, 82, 69, - 32, 69, 73, 71, 72, 84, 54, 80, 114, 82, 50, 84, 78, 87, 232, 248, 9, 4, - 90, 69, 82, 79, 234, 183, 16, 69, 246, 177, 4, 86, 158, 68, 71, 190, 113, - 83, 223, 160, 1, 88, 6, 100, 12, 78, 84, 73, 67, 76, 79, 67, 75, 87, 73, - 83, 69, 213, 149, 30, 7, 83, 84, 69, 82, 73, 83, 75, 4, 200, 238, 8, 11, - 45, 82, 79, 84, 65, 84, 69, 68, 32, 68, 73, 179, 204, 23, 32, 4, 32, 2, - 79, 76, 167, 245, 31, 85, 2, 131, 143, 14, 68, 16, 60, 4, 82, 79, 83, 83, - 210, 2, 32, 174, 209, 33, 67, 3, 68, 10, 26, 32, 155, 193, 1, 73, 8, 64, - 6, 70, 79, 82, 77, 69, 69, 229, 227, 31, 4, 80, 79, 77, 77, 7, 33, 6, 32, - 87, 73, 84, 72, 32, 4, 214, 175, 31, 70, 207, 82, 84, 30, 34, 73, 70, 79, - 211, 233, 31, 65, 24, 216, 187, 4, 8, 86, 73, 83, 73, 79, 78, 32, 83, - 211, 165, 27, 71, 4, 64, 10, 76, 76, 65, 82, 32, 83, 73, 71, 78, 32, 191, - 145, 30, 84, 2, 145, 156, 19, 13, 87, 73, 84, 72, 32, 79, 86, 69, 82, 76, - 65, 73, 68, 64, 192, 1, 6, 65, 78, 71, 85, 76, 32, 204, 191, 12, 20, 79, - 82, 73, 90, 79, 78, 84, 65, 76, 32, 66, 65, 82, 32, 87, 73, 84, 72, 32, - 78, 136, 210, 9, 4, 85, 77, 65, 78, 145, 1, 4, 69, 65, 86, 89, 58, 110, - 67, 184, 188, 20, 5, 73, 69, 85, 78, 71, 42, 72, 30, 75, 66, 77, 34, 78, - 34, 80, 62, 82, 30, 83, 27, 84, 8, 150, 188, 20, 72, 157, 3, 4, 73, 69, - 85, 67, 116, 220, 1, 9, 68, 69, 79, 71, 82, 65, 80, 72, 32, 196, 8, 27, - 84, 65, 76, 73, 67, 32, 76, 65, 84, 73, 78, 32, 67, 65, 80, 73, 84, 65, - 76, 32, 76, 69, 84, 84, 69, 82, 32, 157, 138, 27, 9, 78, 70, 79, 82, 77, - 65, 84, 73, 79, 110, 174, 1, 65, 110, 67, 90, 69, 86, 70, 62, 72, 38, 75, - 70, 76, 50, 77, 86, 78, 62, 81, 30, 82, 74, 83, 162, 173, 6, 80, 242, - 143, 14, 84, 50, 87, 170, 214, 1, 73, 255, 151, 10, 79, 8, 158, 189, 20, - 76, 184, 133, 1, 3, 67, 67, 69, 204, 239, 6, 3, 84, 84, 69, 249, 17, 5, - 68, 86, 65, 78, 84, 8, 26, 79, 163, 200, 30, 69, 6, 240, 200, 10, 2, 82, - 82, 230, 243, 9, 78, 203, 251, 12, 80, 8, 234, 188, 20, 78, 222, 163, 9, - 65, 188, 165, 3, 5, 88, 67, 69, 76, 76, 231, 24, 73, 10, 246, 188, 20, - 73, 208, 184, 5, 2, 69, 77, 191, 218, 6, 79, 4, 210, 163, 29, 73, 247, - 167, 3, 65, 4, 212, 155, 7, 8, 73, 78, 68, 69, 82, 71, 65, 82, 231, 189, - 22, 79, 6, 134, 188, 20, 65, 142, 141, 12, 79, 195, 83, 69, 8, 38, 69, - 182, 162, 32, 65, 191, 84, 79, 4, 160, 173, 32, 3, 68, 73, 67, 251, 15, - 84, 6, 26, 73, 171, 174, 32, 65, 4, 218, 155, 33, 71, 231, 19, 78, 2, - 153, 179, 28, 2, 85, 69, 8, 26, 69, 251, 154, 33, 73, 6, 142, 187, 20, - 83, 221, 253, 7, 2, 76, 73, 22, 90, 69, 38, 85, 144, 148, 14, 2, 67, 72, - 214, 166, 6, 79, 22, 80, 34, 84, 215, 252, 11, 73, 4, 246, 152, 10, 67, - 215, 215, 21, 86, 6, 226, 187, 20, 80, 166, 255, 1, 73, 139, 137, 11, 78, - 4, 218, 195, 33, 67, 3, 82, 98, 116, 8, 65, 84, 65, 75, 65, 78, 65, 32, - 133, 1, 16, 79, 82, 69, 65, 78, 32, 67, 72, 65, 82, 65, 67, 84, 69, 82, - 32, 94, 170, 229, 10, 72, 2, 75, 2, 77, 2, 78, 2, 82, 2, 83, 2, 84, 126, - 87, 46, 89, 246, 219, 22, 65, 2, 69, 2, 73, 2, 79, 3, 85, 4, 228, 138, - 16, 3, 74, 85, 69, 221, 151, 17, 4, 67, 72, 65, 77, 106, 158, 173, 27, - 65, 255, 129, 4, 69, 4, 100, 19, 85, 76, 84, 73, 80, 76, 73, 67, 65, 84, - 73, 79, 78, 32, 83, 73, 71, 78, 32, 243, 185, 31, 73, 2, 157, 84, 4, 87, - 73, 84, 72, 98, 50, 69, 46, 70, 98, 83, 94, 84, 191, 183, 20, 78, 6, 180, - 1, 3, 73, 71, 72, 199, 169, 25, 76, 30, 28, 3, 73, 70, 84, 35, 79, 6, - 214, 1, 89, 155, 233, 31, 69, 24, 142, 2, 82, 167, 183, 20, 85, 8, 40, 4, - 69, 86, 69, 78, 1, 2, 73, 88, 4, 11, 84, 4, 104, 2, 89, 32, 143, 233, 31, - 69, 52, 56, 2, 69, 78, 32, 4, 72, 73, 82, 84, 29, 2, 87, 69, 5, 11, 32, - 2, 247, 198, 25, 79, 24, 74, 89, 175, 232, 31, 69, 24, 26, 78, 215, 191, - 32, 76, 22, 17, 2, 84, 89, 23, 11, 32, 20, 72, 2, 79, 78, 154, 203, 31, - 70, 30, 83, 42, 84, 170, 86, 78, 235, 110, 69, 4, 210, 197, 25, 32, 235, - 245, 7, 69, 2, 129, 165, 31, 8, 32, 80, 79, 73, 78, 84, 69, 68, 8, 132, - 199, 17, 3, 79, 83, 84, 154, 246, 6, 65, 192, 204, 2, 8, 69, 82, 80, 69, - 78, 68, 73, 67, 139, 171, 4, 76, 4, 128, 250, 29, 3, 73, 78, 71, 171, - 170, 1, 69, 6, 52, 7, 82, 73, 65, 78, 71, 76, 69, 131, 175, 26, 73, 5, - 147, 147, 22, 32, 6, 44, 5, 72, 73, 84, 69, 32, 203, 184, 33, 90, 4, 142, - 217, 31, 66, 139, 25, 83, 6, 218, 160, 27, 77, 196, 178, 4, 6, 76, 65, - 84, 73, 79, 78, 225, 161, 1, 3, 83, 32, 84, 5, 225, 242, 31, 6, 32, 65, - 84, 32, 68, 85, 158, 18, 192, 1, 24, 67, 79, 77, 80, 65, 84, 73, 66, 73, - 76, 73, 84, 89, 32, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 144, 3, 8, - 82, 65, 68, 73, 67, 65, 76, 32, 245, 17, 7, 83, 84, 82, 79, 75, 69, 32, - 236, 15, 24, 2, 50, 70, 75, 70, 188, 8, 34, 65, 162, 246, 10, 56, 3, 57, - 60, 202, 1, 49, 135, 236, 18, 48, 176, 7, 26, 65, 227, 245, 10, 57, 176, - 3, 134, 1, 54, 134, 236, 18, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, - 55, 2, 56, 2, 57, 2, 65, 2, 66, 2, 67, 139, 146, 9, 68, 28, 198, 179, 33, - 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 2, 57, 2, 65, - 2, 66, 2, 67, 3, 68, 230, 1, 210, 1, 66, 126, 67, 226, 4, 68, 62, 69, 50, - 70, 34, 71, 50, 72, 86, 74, 154, 1, 76, 62, 77, 130, 1, 80, 46, 82, 78, - 83, 182, 3, 84, 82, 87, 168, 218, 10, 2, 78, 69, 144, 17, 4, 75, 78, 73, - 70, 251, 151, 12, 79, 14, 74, 79, 180, 250, 10, 4, 82, 85, 83, 72, 246, - 233, 4, 65, 251, 192, 13, 76, 6, 186, 161, 10, 76, 154, 248, 22, 78, 215, - 22, 88, 56, 104, 12, 45, 83, 73, 77, 80, 76, 73, 70, 73, 69, 68, 32, 162, - 3, 73, 50, 76, 158, 173, 32, 79, 191, 78, 72, 44, 114, 69, 38, 70, 50, - 71, 38, 76, 34, 83, 94, 84, 154, 199, 14, 87, 42, 68, 234, 226, 16, 66, - 222, 120, 72, 135, 2, 67, 4, 174, 218, 31, 86, 191, 183, 1, 65, 6, 210, - 197, 31, 73, 218, 214, 1, 76, 235, 16, 82, 4, 186, 143, 23, 79, 147, 215, - 8, 65, 4, 238, 171, 32, 69, 187, 48, 79, 10, 150, 219, 6, 73, 134, 186, - 17, 65, 198, 194, 1, 72, 248, 178, 7, 3, 80, 69, 69, 215, 11, 69, 6, 214, - 10, 85, 152, 176, 10, 2, 65, 78, 203, 158, 4, 79, 4, 140, 210, 20, 3, 86, - 73, 76, 151, 200, 12, 84, 4, 170, 214, 17, 73, 131, 193, 14, 79, 6, 216, - 148, 24, 2, 73, 86, 186, 241, 2, 69, 179, 164, 6, 79, 10, 196, 6, 2, 65, - 84, 194, 141, 33, 87, 3, 89, 4, 242, 137, 32, 73, 131, 80, 79, 8, 244, 5, - 4, 82, 65, 83, 83, 143, 186, 29, 72, 10, 48, 2, 69, 65, 146, 159, 28, 79, - 163, 252, 3, 65, 6, 186, 8, 82, 239, 160, 33, 68, 10, 72, 12, 45, 83, 73, - 77, 80, 76, 73, 70, 73, 69, 68, 32, 179, 152, 31, 65, 8, 42, 84, 174, - 195, 14, 68, 247, 244, 16, 69, 4, 194, 6, 85, 227, 206, 14, 79, 12, 248, - 241, 10, 3, 79, 78, 71, 197, 245, 15, 3, 65, 77, 69, 14, 18, 69, 35, 79, - 4, 242, 138, 33, 65, 159, 27, 83, 10, 136, 241, 10, 3, 85, 78, 68, 166, - 162, 13, 84, 162, 205, 7, 82, 211, 118, 79, 6, 188, 240, 10, 2, 65, 87, - 175, 182, 18, 69, 8, 34, 65, 169, 213, 28, 2, 69, 80, 6, 130, 214, 32, - 73, 226, 79, 77, 3, 80, 38, 122, 69, 90, 73, 186, 1, 78, 196, 1, 4, 80, - 73, 82, 73, 248, 234, 10, 4, 77, 65, 76, 76, 214, 149, 12, 72, 195, 208, - 9, 85, 8, 40, 4, 67, 79, 78, 68, 227, 154, 32, 65, 6, 11, 32, 6, 166, - 180, 31, 84, 183, 86, 79, 12, 60, 9, 77, 80, 76, 73, 70, 73, 69, 68, 32, - 147, 160, 33, 76, 10, 34, 72, 50, 87, 135, 182, 10, 89, 4, 140, 160, 10, - 3, 65, 76, 70, 223, 248, 17, 79, 4, 234, 208, 6, 65, 175, 129, 22, 72, 6, - 192, 1, 2, 79, 85, 151, 241, 32, 65, 8, 58, 85, 146, 217, 24, 65, 218, - 204, 1, 72, 147, 174, 3, 73, 2, 239, 134, 24, 82, 12, 26, 65, 49, 2, 69, - 83, 8, 164, 235, 10, 2, 76, 75, 1, 3, 84, 69, 82, 4, 247, 234, 10, 84, - 76, 110, 72, 182, 1, 80, 38, 83, 142, 163, 31, 84, 176, 250, 1, 2, 66, - 88, 2, 87, 2, 88, 86, 68, 2, 78, 3, 81, 31, 42, 80, 22, 88, 30, 90, 143, - 159, 33, 71, 5, 231, 158, 33, 87, 4, 210, 158, 33, 87, 87, 71, 19, 50, - 90, 194, 163, 31, 87, 154, 251, 1, 71, 3, 84, 9, 190, 163, 31, 90, 155, - 251, 1, 80, 9, 178, 158, 33, 68, 2, 71, 3, 90, 23, 50, 87, 30, 90, 194, - 157, 33, 71, 2, 80, 3, 84, 7, 218, 157, 33, 71, 3, 90, 9, 234, 156, 33, - 87, 86, 80, 3, 90, 124, 62, 65, 194, 1, 73, 114, 79, 181, 241, 20, 4, 69, - 65, 82, 32, 8, 132, 1, 2, 80, 80, 208, 231, 18, 6, 83, 83, 73, 67, 65, - 76, 169, 137, 13, 14, 77, 83, 72, 69, 76, 76, 32, 77, 79, 66, 73, 76, 69, - 32, 4, 196, 189, 19, 5, 73, 78, 71, 32, 72, 239, 227, 1, 69, 6, 48, 6, - 78, 75, 73, 78, 71, 32, 223, 131, 32, 80, 4, 190, 145, 25, 71, 181, 183, - 3, 6, 66, 69, 69, 82, 32, 77, 108, 72, 2, 67, 75, 140, 10, 2, 83, 69, - 236, 3, 2, 85, 68, 131, 153, 30, 87, 70, 64, 6, 32, 70, 65, 67, 69, 32, - 237, 2, 5, 87, 73, 83, 69, 32, 48, 58, 69, 46, 70, 36, 2, 78, 73, 2, 79, - 18, 83, 51, 84, 8, 120, 2, 76, 69, 125, 4, 73, 71, 72, 84, 8, 178, 1, 73, - 25, 3, 79, 85, 82, 4, 155, 1, 78, 8, 26, 69, 125, 2, 73, 88, 4, 57, 2, - 86, 69, 16, 38, 69, 14, 87, 41, 3, 72, 82, 69, 4, 63, 78, 8, 24, 2, 69, - 76, 27, 79, 4, 11, 86, 4, 11, 69, 4, 180, 252, 28, 2, 32, 79, 137, 173, - 2, 3, 45, 84, 72, 22, 90, 67, 58, 68, 122, 71, 48, 5, 82, 73, 71, 72, 84, - 226, 2, 84, 122, 79, 143, 137, 31, 73, 4, 196, 1, 3, 76, 79, 83, 141, 99, - 4, 79, 78, 84, 79, 2, 141, 3, 26, 79, 87, 78, 87, 65, 82, 68, 83, 32, 65, + 77, 0, 2, 81, 85, 2, 87, 2, 89, 214, 251, 33, 75, 186, 2, 65, 2, 69, 2, + 73, 2, 79, 2, 85, 3, 86, 14, 150, 254, 33, 76, 186, 2, 65, 2, 69, 2, 73, + 2, 79, 2, 85, 3, 86, 14, 206, 253, 33, 78, 186, 2, 65, 2, 69, 2, 73, 2, + 79, 2, 85, 3, 86, 14, 222, 238, 29, 65, 226, 144, 4, 69, 2, 73, 2, 79, 2, + 85, 3, 86, 15, 250, 254, 33, 65, 2, 69, 2, 73, 2, 79, 2, 85, 3, 86, 30, + 50, 76, 2, 83, 142, 254, 33, 65, 2, 69, 3, 73, 12, 138, 254, 33, 65, 2, + 69, 2, 73, 2, 79, 2, 85, 3, 86, 6, 32, 2, 89, 32, 171, 231, 32, 73, 4, + 40, 4, 66, 76, 79, 83, 215, 231, 32, 83, 2, 155, 236, 33, 83, 16, 152, 1, + 2, 76, 68, 48, 11, 78, 69, 83, 69, 32, 83, 77, 65, 76, 76, 32, 154, 239, + 7, 32, 184, 247, 2, 2, 80, 77, 204, 190, 21, 2, 67, 75, 155, 134, 1, 82, + 5, 225, 230, 28, 7, 82, 69, 78, 32, 67, 82, 79, 4, 172, 208, 19, 10, 83, + 73, 77, 80, 76, 73, 70, 73, 69, 68, 1, 11, 84, 82, 65, 68, 73, 84, 73, + 79, 78, 65, 76, 60, 92, 8, 82, 65, 83, 77, 73, 65, 78, 32, 254, 254, 4, + 80, 197, 133, 12, 5, 67, 79, 76, 65, 84, 56, 52, 7, 76, 69, 84, 84, 69, + 82, 32, 251, 197, 21, 78, 42, 224, 1, 6, 67, 85, 82, 76, 69, 68, 30, 83, + 210, 194, 21, 68, 34, 76, 134, 206, 1, 82, 142, 220, 2, 65, 50, 71, 90, + 90, 98, 89, 198, 207, 1, 72, 234, 5, 75, 174, 81, 66, 170, 225, 4, 78, + 134, 2, 84, 2, 87, 218, 103, 80, 171, 4, 77, 2, 133, 249, 32, 2, 32, 87, + 6, 184, 194, 21, 6, 77, 65, 76, 76, 32, 65, 232, 243, 5, 2, 65, 77, 163, + 193, 5, 72, 232, 4, 66, 78, 20, 2, 82, 67, 201, 40, 7, 84, 89, 83, 67, + 65, 80, 69, 2, 235, 212, 33, 69, 226, 4, 28, 2, 76, 69, 207, 39, 85, 220, + 4, 30, 32, 185, 6, 2, 68, 32, 24, 244, 1, 5, 87, 73, 84, 72, 32, 161, + 193, 18, 49, 68, 73, 86, 73, 68, 69, 68, 32, 66, 89, 32, 72, 79, 82, 73, + 90, 79, 78, 84, 65, 76, 32, 66, 65, 82, 32, 65, 78, 68, 32, 84, 79, 80, + 32, 72, 65, 76, 70, 32, 68, 73, 86, 73, 68, 69, 68, 32, 66, 89, 22, 146, + 2, 76, 46, 83, 108, 22, 84, 87, 79, 32, 72, 79, 82, 73, 90, 79, 78, 84, + 65, 76, 32, 83, 84, 82, 79, 75, 69, 83, 44, 6, 85, 80, 80, 69, 82, 32, + 44, 17, 65, 76, 76, 32, 66, 85, 84, 32, 85, 80, 80, 69, 82, 32, 76, 69, + 70, 242, 188, 26, 86, 242, 146, 4, 82, 183, 251, 1, 72, 4, 194, 209, 30, + 69, 49, 4, 79, 87, 69, 82, 4, 104, 11, 77, 65, 76, 76, 32, 67, 73, 82, + 67, 76, 69, 221, 5, 10, 85, 80, 69, 82, 73, 77, 80, 79, 83, 69, 2, 149, + 235, 30, 6, 32, 84, 79, 32, 84, 72, 4, 40, 4, 82, 73, 71, 72, 155, 208, + 30, 72, 2, 173, 208, 30, 10, 84, 32, 81, 85, 65, 68, 82, 65, 78, 84, 196, + 4, 230, 2, 65, 186, 1, 66, 58, 67, 230, 1, 68, 246, 1, 72, 230, 2, 73, + 194, 10, 75, 190, 2, 76, 38, 77, 136, 1, 7, 78, 85, 77, 66, 69, 82, 32, + 192, 4, 17, 79, 80, 69, 78, 32, 67, 69, 78, 84, 82, 69, 32, 69, 73, 71, + 72, 84, 54, 80, 114, 82, 50, 84, 78, 87, 140, 252, 9, 4, 90, 69, 82, 79, + 134, 188, 16, 69, 182, 188, 4, 86, 146, 68, 71, 150, 115, 83, 227, 162, + 1, 88, 6, 100, 12, 78, 84, 73, 67, 76, 79, 67, 75, 87, 73, 83, 69, 209, + 167, 30, 7, 83, 84, 69, 82, 73, 83, 75, 4, 160, 241, 8, 11, 45, 82, 79, + 84, 65, 84, 69, 68, 32, 68, 73, 167, 221, 23, 32, 4, 32, 2, 79, 76, 143, + 136, 32, 85, 2, 223, 209, 14, 68, 16, 60, 4, 82, 79, 83, 83, 210, 2, 32, + 254, 230, 33, 67, 3, 68, 10, 26, 32, 199, 193, 1, 73, 8, 64, 6, 70, 79, + 82, 77, 69, 69, 205, 246, 31, 4, 80, 79, 77, 77, 7, 33, 6, 32, 87, 73, + 84, 72, 32, 4, 210, 193, 31, 70, 135, 84, 84, 30, 34, 73, 70, 79, 187, + 252, 31, 65, 24, 168, 188, 4, 8, 86, 73, 83, 73, 79, 78, 32, 83, 235, + 183, 27, 71, 4, 64, 10, 76, 76, 65, 82, 32, 83, 73, 71, 78, 32, 187, 163, + 30, 84, 2, 225, 158, 19, 13, 87, 73, 84, 72, 32, 79, 86, 69, 82, 76, 65, + 73, 68, 64, 192, 1, 6, 65, 78, 71, 85, 76, 32, 252, 191, 12, 20, 79, 82, + 73, 90, 79, 78, 84, 65, 76, 32, 66, 65, 82, 32, 87, 73, 84, 72, 32, 78, + 128, 213, 9, 4, 85, 77, 65, 78, 145, 1, 4, 69, 65, 86, 89, 58, 110, 67, + 192, 190, 20, 5, 73, 69, 85, 78, 71, 42, 72, 30, 75, 66, 77, 34, 78, 34, + 80, 62, 82, 30, 83, 27, 84, 8, 158, 190, 20, 72, 157, 3, 4, 73, 69, 85, + 67, 116, 220, 1, 9, 68, 69, 79, 71, 82, 65, 80, 72, 32, 196, 8, 27, 84, + 65, 76, 73, 67, 32, 76, 65, 84, 73, 78, 32, 67, 65, 80, 73, 84, 65, 76, + 32, 76, 69, 84, 84, 69, 82, 32, 221, 145, 27, 9, 78, 70, 79, 82, 77, 65, + 84, 73, 79, 110, 174, 1, 65, 110, 67, 90, 69, 86, 70, 62, 72, 38, 75, 70, + 76, 50, 77, 86, 78, 62, 81, 30, 82, 74, 83, 146, 170, 6, 80, 138, 149, + 14, 84, 50, 87, 202, 215, 1, 73, 163, 168, 10, 79, 8, 166, 191, 20, 76, + 200, 134, 1, 3, 67, 67, 69, 184, 249, 6, 3, 84, 84, 69, 249, 17, 5, 68, + 86, 65, 78, 84, 8, 26, 79, 159, 218, 30, 69, 6, 184, 204, 10, 2, 82, 82, + 166, 242, 9, 78, 147, 143, 13, 80, 8, 242, 190, 20, 78, 174, 179, 9, 65, + 180, 169, 3, 5, 88, 67, 69, 76, 76, 231, 24, 73, 10, 254, 190, 20, 73, + 144, 190, 5, 2, 69, 77, 199, 232, 6, 79, 4, 198, 176, 29, 73, 211, 176, + 3, 65, 4, 176, 158, 7, 8, 73, 78, 68, 69, 82, 71, 65, 82, 143, 204, 22, + 79, 6, 142, 190, 20, 65, 214, 160, 12, 79, 195, 83, 69, 8, 38, 69, 242, + 181, 32, 65, 211, 86, 79, 4, 236, 192, 32, 3, 68, 73, 67, 227, 15, 84, 6, + 26, 73, 247, 193, 32, 65, 4, 170, 177, 33, 71, 231, 19, 78, 2, 157, 192, + 28, 2, 85, 69, 8, 26, 69, 203, 176, 33, 73, 6, 150, 189, 20, 83, 217, + 136, 8, 2, 76, 73, 22, 90, 69, 38, 85, 184, 149, 14, 2, 67, 72, 182, 167, + 6, 79, 22, 80, 34, 84, 131, 142, 12, 73, 4, 186, 156, 10, 67, 199, 231, + 21, 86, 6, 234, 189, 20, 80, 146, 128, 2, 73, 231, 155, 11, 78, 4, 170, + 217, 33, 67, 3, 82, 98, 116, 8, 65, 84, 65, 75, 65, 78, 65, 32, 133, 1, + 16, 79, 82, 69, 65, 78, 32, 67, 72, 65, 82, 65, 67, 84, 69, 82, 32, 94, + 242, 232, 10, 72, 2, 75, 2, 77, 2, 78, 2, 82, 2, 83, 2, 84, 126, 87, 46, + 89, 254, 237, 22, 65, 2, 69, 2, 73, 2, 79, 3, 85, 4, 152, 145, 16, 3, 74, + 85, 69, 249, 166, 17, 4, 67, 72, 65, 77, 106, 146, 180, 27, 65, 255, 140, + 4, 69, 4, 100, 19, 85, 76, 84, 73, 80, 76, 73, 67, 65, 84, 73, 79, 78, + 32, 83, 73, 71, 78, 32, 219, 204, 31, 73, 2, 209, 81, 4, 87, 73, 84, 72, + 98, 50, 69, 46, 70, 98, 83, 94, 84, 199, 185, 20, 78, 6, 180, 1, 3, 73, + 71, 72, 255, 176, 25, 76, 30, 28, 3, 73, 70, 84, 35, 79, 6, 214, 1, 89, + 207, 252, 31, 69, 24, 142, 2, 82, 175, 185, 20, 85, 8, 40, 4, 69, 86, 69, + 78, 1, 2, 73, 88, 4, 11, 84, 4, 104, 2, 89, 32, 195, 252, 31, 69, 52, 56, + 2, 69, 78, 32, 4, 72, 73, 82, 84, 29, 2, 87, 69, 5, 11, 32, 2, 191, 206, + 25, 79, 24, 74, 89, 227, 251, 31, 69, 24, 26, 78, 167, 213, 32, 76, 22, + 17, 2, 84, 89, 23, 11, 32, 20, 72, 2, 79, 78, 130, 222, 31, 70, 30, 83, + 42, 84, 142, 87, 78, 239, 112, 69, 4, 154, 205, 25, 32, 243, 131, 8, 69, + 2, 245, 182, 31, 8, 32, 80, 79, 73, 78, 84, 69, 68, 8, 248, 205, 17, 3, + 79, 83, 84, 242, 246, 6, 65, 228, 203, 2, 8, 69, 82, 80, 69, 78, 68, 73, + 67, 131, 183, 4, 76, 4, 252, 139, 30, 3, 73, 78, 71, 163, 170, 1, 69, 6, + 52, 7, 82, 73, 65, 78, 71, 76, 69, 195, 182, 26, 73, 5, 135, 150, 22, 32, + 6, 44, 5, 72, 73, 84, 69, 32, 155, 206, 33, 90, 4, 246, 235, 31, 66, 215, + 25, 83, 6, 206, 167, 27, 77, 184, 190, 4, 6, 76, 65, 84, 73, 79, 78, 201, + 164, 1, 3, 83, 32, 84, 5, 157, 134, 32, 6, 32, 65, 84, 32, 68, 85, 178, + 2, 80, 8, 82, 65, 68, 73, 67, 65, 76, 32, 245, 17, 7, 83, 84, 82, 79, 75, + 69, 32, 230, 1, 210, 1, 66, 126, 67, 226, 4, 68, 62, 69, 50, 70, 34, 71, + 50, 72, 86, 74, 154, 1, 76, 62, 77, 130, 1, 80, 46, 82, 78, 83, 182, 3, + 84, 82, 87, 240, 225, 10, 2, 78, 69, 208, 13, 4, 75, 78, 73, 70, 255, + 155, 12, 79, 14, 74, 79, 188, 254, 10, 4, 82, 85, 83, 72, 162, 240, 4, + 65, 195, 203, 13, 76, 6, 130, 169, 10, 76, 162, 138, 23, 78, 215, 22, 88, + 56, 104, 12, 45, 83, 73, 77, 80, 76, 73, 70, 73, 69, 68, 32, 162, 3, 73, + 50, 76, 238, 198, 32, 79, 191, 78, 72, 44, 114, 69, 38, 70, 50, 71, 38, + 76, 34, 83, 94, 84, 182, 209, 14, 87, 42, 68, 182, 239, 16, 66, 170, 121, + 72, 163, 4, 67, 4, 226, 241, 31, 86, 219, 185, 1, 65, 6, 186, 220, 31, + 73, 194, 217, 1, 76, 235, 16, 82, 4, 198, 151, 23, 79, 187, 230, 8, 65, + 4, 190, 197, 32, 69, 187, 48, 79, 10, 238, 225, 6, 73, 250, 190, 17, 65, + 194, 194, 1, 72, 128, 193, 7, 3, 80, 69, 69, 215, 11, 69, 6, 214, 10, 85, + 224, 183, 10, 2, 65, 78, 159, 161, 4, 79, 4, 180, 216, 20, 3, 86, 73, 76, + 191, 219, 12, 84, 4, 150, 225, 17, 73, 219, 205, 14, 79, 6, 164, 160, 24, + 2, 73, 86, 222, 240, 2, 69, 147, 179, 6, 79, 10, 196, 6, 2, 65, 84, 146, + 167, 33, 87, 3, 89, 4, 190, 161, 32, 73, 135, 82, 79, 8, 244, 5, 4, 82, + 65, 83, 83, 231, 207, 29, 72, 10, 48, 2, 69, 65, 150, 176, 28, 79, 211, + 130, 4, 65, 6, 186, 8, 82, 191, 186, 33, 68, 10, 72, 12, 45, 83, 73, 77, + 80, 76, 73, 70, 73, 69, 68, 32, 155, 175, 31, 65, 8, 42, 84, 202, 205, + 14, 68, 195, 129, 17, 69, 4, 194, 6, 85, 255, 216, 14, 79, 12, 128, 246, + 10, 3, 79, 78, 71, 129, 253, 15, 3, 65, 77, 69, 14, 18, 69, 35, 79, 4, + 194, 164, 33, 65, 159, 27, 83, 10, 144, 245, 10, 3, 85, 78, 68, 234, 169, + 13, 84, 138, 217, 7, 82, 239, 120, 79, 6, 196, 244, 10, 2, 65, 87, 163, + 199, 18, 69, 8, 34, 65, 249, 181, 32, 2, 69, 80, 6, 210, 239, 32, 73, + 226, 79, 77, 3, 80, 38, 122, 69, 90, 73, 186, 1, 78, 196, 1, 4, 80, 73, + 82, 73, 128, 239, 10, 4, 77, 65, 76, 76, 218, 153, 12, 72, 135, 226, 9, + 85, 8, 40, 4, 67, 79, 78, 68, 151, 178, 32, 65, 6, 11, 32, 6, 142, 203, + 31, 84, 155, 87, 79, 12, 60, 9, 77, 80, 76, 73, 70, 73, 69, 68, 32, 227, + 185, 33, 76, 10, 34, 72, 50, 87, 207, 189, 10, 89, 4, 212, 167, 10, 3, + 65, 76, 70, 155, 130, 18, 79, 4, 194, 215, 6, 65, 167, 219, 25, 72, 6, + 192, 1, 2, 79, 85, 231, 138, 33, 65, 8, 58, 85, 202, 228, 24, 65, 242, + 204, 1, 72, 191, 184, 3, 73, 2, 187, 146, 24, 82, 12, 26, 65, 49, 2, 69, + 83, 8, 172, 239, 10, 2, 76, 75, 1, 3, 84, 69, 82, 4, 255, 238, 10, 84, + 76, 110, 72, 182, 1, 80, 38, 83, 246, 185, 31, 84, 152, 253, 1, 2, 66, + 88, 2, 87, 2, 88, 86, 68, 2, 78, 3, 81, 31, 42, 80, 22, 88, 30, 90, 223, + 184, 33, 71, 5, 183, 184, 33, 87, 4, 162, 184, 33, 87, 87, 71, 19, 50, + 90, 170, 186, 31, 87, 130, 254, 1, 71, 3, 84, 9, 166, 186, 31, 90, 131, + 254, 1, 80, 9, 130, 184, 33, 68, 2, 71, 3, 90, 23, 50, 87, 30, 90, 146, + 183, 33, 71, 2, 80, 3, 84, 7, 170, 183, 33, 71, 3, 90, 9, 186, 182, 33, + 87, 86, 80, 3, 90, 124, 62, 65, 194, 1, 73, 114, 79, 233, 247, 20, 4, 69, + 65, 82, 32, 8, 132, 1, 2, 80, 80, 128, 217, 3, 6, 83, 83, 73, 67, 65, 76, + 181, 175, 28, 14, 77, 83, 72, 69, 76, 76, 32, 77, 79, 66, 73, 76, 69, 32, + 4, 232, 195, 19, 5, 73, 78, 71, 32, 72, 227, 228, 1, 69, 6, 48, 6, 78, + 75, 73, 78, 71, 32, 171, 155, 32, 80, 4, 246, 156, 25, 71, 149, 189, 3, + 6, 66, 69, 69, 82, 32, 77, 108, 72, 2, 67, 75, 140, 10, 2, 83, 69, 236, + 3, 2, 85, 68, 131, 175, 30, 87, 70, 64, 6, 32, 70, 65, 67, 69, 32, 237, + 2, 5, 87, 73, 83, 69, 32, 48, 58, 69, 46, 70, 36, 2, 78, 73, 2, 79, 18, + 83, 51, 84, 8, 120, 2, 76, 69, 125, 4, 73, 71, 72, 84, 8, 178, 1, 73, 25, + 3, 79, 85, 82, 4, 155, 1, 78, 8, 26, 69, 125, 2, 73, 88, 4, 57, 2, 86, + 69, 16, 38, 69, 14, 87, 41, 3, 72, 82, 69, 4, 63, 78, 8, 24, 2, 69, 76, + 27, 79, 4, 11, 86, 4, 11, 69, 4, 228, 140, 29, 2, 32, 79, 193, 179, 2, 3, + 45, 84, 72, 22, 90, 67, 58, 68, 122, 71, 48, 5, 82, 73, 71, 72, 84, 226, + 2, 84, 122, 79, 247, 159, 31, 73, 4, 196, 1, 3, 76, 79, 83, 181, 103, 4, + 79, 78, 84, 79, 2, 141, 3, 26, 79, 87, 78, 87, 65, 82, 68, 83, 32, 65, 78, 68, 32, 85, 80, 87, 65, 82, 68, 83, 32, 79, 80, 69, 78, 32, 2, 21, 3, 65, 80, 80, 2, 133, 4, 2, 69, 68, 6, 228, 1, 14, 32, 65, 78, 68, 32, 76, 69, 70, 84, 32, 83, 69, 77, 73, 45, 38, 87, 65, 82, 68, 83, 32, 65, 78, 68, 32, 76, 69, 70, 84, 87, 65, 82, 68, 83, 32, 79, 80, 69, 78, 32, 67, - 73, 82, 67, 76, 69, 32, 65, 82, 82, 79, 87, 83, 2, 185, 182, 30, 6, 67, - 73, 82, 67, 76, 69, 5, 161, 83, 15, 32, 87, 73, 84, 72, 32, 67, 73, 82, + 73, 82, 67, 76, 69, 32, 65, 82, 82, 79, 87, 83, 2, 185, 204, 30, 6, 67, + 73, 82, 67, 76, 69, 5, 253, 85, 15, 32, 87, 73, 84, 72, 32, 67, 73, 82, 67, 76, 69, 68, 32, 79, 4, 82, 79, 37, 16, 82, 73, 65, 78, 71, 76, 69, 45, 72, 69, 65, 68, 69, 68, 32, 79, 2, 69, 6, 80, 32, 83, 69, 77, 73, 2, - 21, 3, 80, 69, 78, 2, 11, 32, 2, 217, 180, 30, 4, 67, 73, 82, 67, 26, 32, - 2, 68, 32, 191, 153, 32, 32, 24, 216, 1, 2, 83, 85, 66, 85, 178, 175, 19, - 77, 180, 3, 9, 76, 79, 67, 75, 32, 87, 73, 84, 72, 222, 134, 12, 66, 249, - 149, 1, 23, 73, 78, 84, 69, 82, 83, 69, 67, 84, 73, 79, 78, 32, 87, 73, - 84, 72, 32, 83, 69, 82, 73, 70, 8, 30, 66, 1, 3, 80, 69, 82, 4, 181, 252, - 24, 3, 83, 69, 84, 6, 82, 77, 33, 16, 78, 73, 79, 78, 32, 87, 73, 84, 72, - 32, 83, 69, 82, 73, 70, 83, 2, 145, 223, 24, 3, 66, 82, 69, 5, 237, 252, - 31, 9, 32, 65, 78, 68, 32, 83, 77, 65, 83, 11, 33, 6, 32, 87, 73, 84, 72, - 32, 8, 72, 4, 84, 79, 82, 78, 214, 167, 5, 76, 206, 220, 19, 83, 135, - 218, 6, 82, 2, 203, 217, 25, 65, 226, 9, 158, 1, 67, 82, 76, 98, 77, 162, - 82, 78, 220, 5, 2, 79, 75, 58, 80, 150, 19, 82, 138, 1, 85, 170, 253, 27, - 87, 196, 158, 3, 2, 70, 70, 246, 47, 73, 183, 51, 65, 6, 26, 75, 203, - 148, 4, 79, 4, 142, 233, 8, 82, 189, 136, 23, 4, 84, 65, 73, 76, 8, 44, - 2, 79, 78, 165, 134, 25, 3, 76, 73, 83, 7, 11, 32, 4, 138, 128, 26, 69, - 195, 129, 6, 83, 158, 6, 72, 7, 66, 73, 78, 73, 78, 71, 32, 166, 80, 77, - 94, 80, 151, 154, 32, 69, 142, 6, 214, 2, 65, 174, 2, 66, 134, 1, 67, - 246, 12, 68, 174, 9, 69, 146, 2, 70, 54, 71, 140, 9, 2, 72, 79, 82, 73, - 216, 1, 2, 75, 65, 162, 1, 76, 230, 13, 77, 250, 1, 78, 78, 79, 138, 2, - 80, 174, 1, 82, 244, 4, 5, 90, 73, 71, 90, 65, 166, 2, 83, 242, 3, 84, - 238, 2, 85, 148, 1, 9, 86, 69, 82, 84, 73, 67, 65, 76, 32, 36, 2, 87, 73, - 183, 1, 88, 24, 148, 1, 4, 67, 85, 84, 69, 86, 78, 252, 131, 25, 6, 83, - 84, 69, 82, 73, 83, 197, 224, 5, 14, 76, 77, 79, 83, 84, 32, 69, 81, 85, - 65, 76, 32, 84, 79, 10, 22, 45, 167, 33, 32, 4, 200, 204, 12, 6, 71, 82, - 65, 86, 69, 45, 199, 205, 3, 77, 6, 244, 2, 4, 84, 73, 67, 76, 189, 179, - 3, 4, 78, 85, 73, 84, 12, 42, 82, 201, 212, 30, 4, 73, 78, 68, 85, 10, - 48, 3, 69, 86, 69, 149, 227, 30, 3, 73, 68, 71, 7, 166, 164, 12, 45, 251, - 175, 18, 32, 142, 1, 142, 1, 65, 34, 76, 98, 79, 116, 8, 89, 82, 73, 76, - 76, 73, 67, 32, 184, 28, 11, 73, 82, 67, 85, 77, 70, 76, 69, 88, 32, 65, - 195, 150, 12, 69, 6, 162, 18, 82, 215, 217, 28, 78, 4, 41, 8, 79, 67, 75, - 87, 73, 83, 69, 32, 4, 204, 171, 12, 4, 82, 73, 78, 71, 231, 218, 16, 65, - 10, 76, 4, 77, 77, 65, 32, 189, 16, 10, 78, 74, 79, 73, 78, 73, 78, 71, - 32, 77, 6, 210, 147, 13, 65, 239, 204, 17, 66, 116, 252, 1, 8, 72, 85, + 21, 3, 80, 69, 78, 2, 11, 32, 2, 217, 202, 30, 4, 67, 73, 82, 67, 26, 32, + 2, 68, 32, 143, 179, 32, 32, 24, 216, 1, 2, 83, 85, 66, 85, 214, 181, 19, + 77, 180, 3, 9, 76, 79, 67, 75, 32, 87, 73, 84, 72, 238, 151, 12, 66, 149, + 152, 1, 23, 73, 78, 84, 69, 82, 83, 69, 67, 84, 73, 79, 78, 32, 87, 73, + 84, 72, 32, 83, 69, 82, 73, 70, 8, 30, 66, 1, 3, 80, 69, 82, 4, 237, 135, + 25, 3, 83, 69, 84, 6, 82, 77, 33, 16, 78, 73, 79, 78, 32, 87, 73, 84, 72, + 32, 83, 69, 82, 73, 70, 83, 2, 201, 234, 24, 3, 66, 82, 69, 5, 161, 148, + 32, 9, 32, 65, 78, 68, 32, 83, 77, 65, 83, 11, 33, 6, 32, 87, 73, 84, 72, + 32, 8, 72, 4, 84, 79, 82, 78, 138, 168, 5, 76, 226, 231, 19, 83, 251, + 229, 6, 82, 2, 135, 229, 25, 65, 152, 10, 158, 1, 67, 82, 76, 98, 77, + 202, 86, 78, 220, 5, 2, 79, 75, 58, 80, 154, 19, 82, 138, 1, 85, 170, + 137, 28, 87, 232, 167, 3, 2, 70, 70, 246, 47, 73, 183, 51, 65, 6, 26, 75, + 155, 153, 4, 79, 4, 150, 240, 8, 82, 129, 153, 23, 4, 84, 65, 73, 76, 8, + 44, 2, 79, 78, 237, 145, 25, 3, 76, 73, 83, 7, 11, 32, 4, 202, 139, 26, + 69, 211, 143, 6, 83, 212, 6, 72, 7, 66, 73, 78, 73, 78, 71, 32, 206, 84, + 77, 94, 80, 191, 175, 32, 69, 196, 6, 210, 2, 65, 186, 2, 66, 118, 67, + 246, 12, 68, 226, 10, 69, 142, 2, 70, 70, 71, 156, 9, 2, 72, 79, 82, 73, + 248, 1, 2, 75, 65, 162, 1, 76, 158, 14, 77, 178, 2, 78, 78, 79, 138, 2, + 80, 182, 1, 82, 248, 4, 5, 90, 73, 71, 90, 65, 138, 2, 83, 162, 4, 84, + 238, 2, 85, 168, 1, 8, 86, 69, 82, 84, 73, 67, 65, 76, 204, 1, 2, 87, 73, + 171, 1, 88, 26, 148, 1, 4, 67, 85, 84, 69, 98, 78, 188, 143, 25, 6, 83, + 84, 69, 82, 73, 83, 241, 234, 5, 14, 76, 77, 79, 83, 84, 32, 69, 81, 85, + 65, 76, 32, 84, 79, 12, 22, 45, 231, 34, 32, 6, 186, 57, 86, 184, 152, + 12, 6, 71, 82, 65, 86, 69, 45, 251, 210, 3, 77, 6, 228, 2, 4, 84, 73, 67, + 76, 237, 183, 3, 4, 78, 85, 73, 84, 12, 42, 82, 189, 234, 30, 4, 73, 78, + 68, 85, 10, 32, 3, 69, 86, 69, 243, 37, 73, 7, 210, 169, 12, 45, 211, + 192, 18, 32, 142, 1, 142, 1, 65, 34, 76, 98, 79, 116, 8, 89, 82, 73, 76, + 76, 73, 67, 32, 252, 29, 11, 73, 82, 67, 85, 77, 70, 76, 69, 88, 32, 65, + 171, 154, 12, 69, 6, 222, 19, 82, 211, 232, 28, 78, 4, 41, 8, 79, 67, 75, + 87, 73, 83, 69, 32, 4, 248, 176, 12, 4, 82, 73, 78, 71, 191, 234, 16, 65, + 10, 76, 4, 77, 77, 65, 32, 249, 17, 10, 78, 74, 79, 73, 78, 73, 78, 71, + 32, 77, 6, 142, 153, 13, 65, 175, 221, 17, 66, 116, 252, 1, 8, 72, 85, 78, 68, 82, 69, 68, 32, 32, 7, 76, 69, 84, 84, 69, 82, 32, 166, 5, 80, - 116, 5, 68, 65, 83, 73, 65, 38, 84, 106, 77, 178, 243, 2, 75, 176, 138, - 13, 15, 83, 77, 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, 32, 66, 89, 229, - 193, 16, 2, 86, 90, 4, 194, 7, 77, 195, 158, 3, 84, 84, 238, 1, 66, 38, - 68, 50, 69, 82, 73, 106, 79, 22, 83, 66, 85, 30, 89, 186, 137, 3, 76, - 140, 5, 5, 77, 79, 78, 79, 71, 150, 18, 72, 238, 226, 12, 84, 190, 243, - 15, 90, 150, 83, 67, 2, 71, 182, 8, 70, 134, 14, 80, 2, 86, 158, 20, 75, - 187, 2, 65, 4, 250, 161, 6, 73, 163, 216, 26, 69, 4, 248, 233, 25, 3, 74, - 69, 82, 255, 143, 7, 69, 14, 58, 83, 142, 249, 32, 70, 2, 76, 2, 77, 2, - 78, 3, 82, 5, 147, 178, 31, 45, 11, 56, 8, 79, 84, 73, 70, 73, 69, 68, - 32, 191, 248, 32, 69, 6, 142, 160, 6, 66, 174, 216, 26, 65, 3, 69, 5, - 159, 193, 25, 77, 6, 26, 72, 171, 133, 16, 79, 4, 158, 253, 30, 67, 195, - 250, 1, 65, 5, 173, 153, 3, 2, 75, 82, 8, 254, 189, 28, 69, 246, 156, 4, - 65, 174, 28, 73, 3, 85, 8, 66, 65, 48, 4, 83, 73, 76, 73, 189, 195, 29, - 4, 79, 75, 82, 89, 4, 142, 246, 2, 89, 133, 228, 12, 3, 76, 65, 84, 2, - 145, 13, 5, 32, 80, 78, 69, 85, 10, 80, 2, 69, 78, 0, 7, 72, 79, 85, 83, - 65, 78, 68, 177, 8, 4, 73, 84, 76, 79, 2, 17, 2, 32, 77, 2, 129, 235, 21, - 5, 73, 76, 76, 73, 79, 110, 62, 69, 244, 1, 8, 73, 65, 69, 82, 69, 83, - 73, 83, 39, 79, 38, 68, 9, 86, 65, 78, 65, 71, 65, 82, 73, 32, 145, 138, - 32, 2, 76, 69, 36, 92, 7, 76, 69, 84, 84, 69, 82, 32, 216, 131, 26, 6, - 83, 73, 71, 78, 32, 65, 227, 253, 4, 68, 14, 254, 222, 32, 86, 162, 17, - 75, 2, 78, 2, 80, 2, 82, 186, 2, 65, 3, 85, 7, 146, 229, 27, 45, 195, - 223, 2, 32, 66, 58, 84, 128, 1, 4, 85, 66, 76, 69, 241, 4, 2, 87, 78, 14, - 38, 32, 173, 139, 16, 3, 84, 69, 68, 10, 64, 5, 65, 66, 79, 86, 69, 197, - 179, 6, 5, 66, 69, 76, 79, 87, 7, 191, 197, 32, 32, 48, 22, 32, 191, 4, - 68, 46, 230, 1, 66, 0, 10, 73, 78, 86, 69, 82, 84, 69, 68, 32, 66, 26, + 116, 5, 68, 65, 83, 73, 65, 38, 84, 106, 77, 230, 247, 2, 75, 220, 144, + 13, 15, 83, 77, 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, 32, 66, 89, 221, + 208, 16, 2, 86, 90, 4, 194, 7, 77, 247, 162, 3, 84, 84, 238, 1, 66, 38, + 68, 50, 69, 82, 73, 106, 79, 22, 83, 66, 85, 30, 89, 238, 141, 3, 76, + 140, 5, 5, 77, 79, 78, 79, 71, 150, 18, 72, 154, 233, 12, 84, 182, 130, + 16, 90, 150, 83, 67, 2, 71, 182, 8, 70, 134, 14, 80, 2, 86, 158, 20, 75, + 187, 2, 65, 4, 218, 168, 6, 73, 155, 235, 26, 69, 4, 188, 245, 25, 3, 74, + 69, 82, 147, 158, 7, 69, 14, 58, 83, 230, 146, 33, 70, 2, 76, 2, 77, 2, + 78, 3, 82, 5, 207, 201, 31, 45, 11, 56, 8, 79, 84, 73, 70, 73, 69, 68, + 32, 151, 146, 33, 69, 6, 238, 166, 6, 66, 166, 235, 26, 65, 3, 69, 5, + 227, 204, 25, 77, 6, 26, 72, 139, 144, 16, 79, 4, 142, 148, 31, 67, 171, + 253, 1, 65, 5, 225, 157, 3, 2, 75, 82, 8, 250, 206, 28, 69, 210, 165, 4, + 65, 174, 28, 73, 3, 85, 8, 66, 65, 48, 4, 83, 73, 76, 73, 193, 217, 29, + 4, 79, 75, 82, 89, 4, 194, 250, 2, 89, 169, 234, 12, 3, 76, 65, 84, 2, + 209, 14, 5, 32, 80, 78, 69, 85, 10, 80, 2, 69, 78, 0, 7, 72, 79, 85, 83, + 65, 78, 68, 237, 9, 4, 73, 84, 76, 79, 2, 17, 2, 32, 77, 2, 253, 241, 21, + 5, 73, 76, 76, 73, 79, 124, 66, 69, 244, 1, 8, 73, 65, 69, 82, 69, 83, + 73, 83, 131, 1, 79, 38, 68, 9, 86, 65, 78, 65, 71, 65, 82, 73, 32, 229, + 163, 32, 2, 76, 69, 36, 92, 7, 76, 69, 84, 84, 69, 82, 32, 172, 143, 26, + 6, 83, 73, 71, 78, 32, 65, 251, 136, 5, 68, 14, 210, 248, 32, 86, 162, + 17, 75, 2, 78, 2, 80, 2, 82, 186, 2, 65, 3, 85, 9, 26, 32, 131, 246, 27, + 45, 4, 214, 233, 30, 66, 189, 142, 1, 16, 87, 73, 84, 72, 32, 82, 65, 73, + 83, 69, 68, 32, 76, 69, 70, 84, 78, 58, 84, 164, 1, 4, 85, 66, 76, 69, + 169, 5, 2, 87, 78, 16, 74, 32, 180, 182, 10, 5, 45, 65, 78, 68, 45, 213, + 222, 5, 3, 84, 69, 68, 10, 64, 5, 65, 66, 79, 86, 69, 165, 185, 6, 5, 66, + 69, 76, 79, 87, 7, 147, 222, 32, 32, 56, 22, 32, 247, 4, 68, 54, 222, 1, + 65, 34, 66, 0, 10, 73, 78, 86, 69, 82, 84, 69, 68, 32, 66, 26, 67, 34, 77, 54, 79, 34, 80, 68, 2, 82, 73, 48, 5, 84, 73, 76, 68, 69, 80, 9, 86, - 69, 82, 84, 73, 67, 65, 76, 32, 198, 134, 16, 65, 42, 71, 150, 187, 4, - 76, 203, 172, 11, 67, 4, 229, 16, 2, 82, 69, 4, 21, 3, 65, 67, 82, 4, - 229, 245, 16, 2, 79, 78, 4, 230, 35, 80, 163, 226, 30, 86, 6, 238, 36, - 76, 253, 224, 7, 9, 65, 82, 69, 78, 84, 72, 69, 83, 69, 4, 156, 51, 4, - 71, 72, 84, 87, 207, 248, 9, 78, 7, 11, 32, 4, 44, 3, 76, 69, 70, 1, 4, - 82, 73, 71, 72, 2, 199, 203, 30, 84, 6, 198, 151, 12, 83, 151, 177, 14, - 76, 2, 165, 129, 16, 2, 32, 67, 4, 128, 41, 2, 32, 84, 243, 162, 31, 87, - 16, 76, 9, 78, 67, 76, 79, 83, 73, 78, 71, 32, 205, 152, 26, 4, 81, 85, - 65, 76, 14, 132, 1, 6, 67, 73, 82, 67, 76, 69, 22, 83, 164, 160, 6, 3, - 75, 69, 89, 228, 203, 19, 7, 85, 80, 87, 65, 82, 68, 32, 191, 192, 5, 68, - 5, 175, 180, 18, 32, 4, 214, 216, 25, 67, 131, 240, 5, 81, 4, 32, 2, 69, - 82, 159, 142, 5, 79, 2, 139, 74, 77, 126, 88, 17, 76, 65, 71, 79, 76, 73, - 84, 73, 67, 32, 76, 69, 84, 84, 69, 82, 32, 143, 3, 82, 76, 238, 1, 68, - 30, 73, 46, 83, 50, 84, 130, 137, 6, 65, 22, 66, 94, 67, 134, 1, 70, 38, - 71, 238, 2, 77, 32, 2, 76, 74, 34, 78, 88, 2, 80, 79, 30, 82, 194, 2, 86, - 22, 89, 90, 90, 226, 132, 19, 72, 174, 128, 2, 85, 170, 168, 5, 79, 235, - 5, 75, 4, 186, 139, 6, 74, 31, 79, 11, 198, 140, 6, 78, 54, 79, 227, 171, - 26, 90, 8, 166, 141, 6, 77, 130, 4, 76, 211, 200, 22, 72, 4, 218, 145, 6, - 86, 219, 191, 26, 83, 50, 38, 65, 173, 3, 4, 69, 69, 75, 32, 36, 84, 5, - 78, 84, 72, 65, 32, 196, 1, 2, 86, 69, 133, 188, 28, 5, 80, 72, 69, 77, - 69, 24, 68, 6, 68, 73, 71, 73, 84, 32, 65, 7, 76, 69, 84, 84, 69, 82, 32, - 14, 250, 229, 16, 83, 130, 141, 14, 70, 70, 84, 62, 90, 251, 85, 79, 10, - 134, 207, 32, 86, 162, 17, 75, 2, 78, 2, 80, 187, 2, 65, 10, 18, 32, 67, - 45, 6, 26, 65, 231, 166, 16, 84, 4, 209, 144, 4, 4, 67, 67, 69, 78, 4, - 212, 132, 12, 6, 65, 67, 85, 84, 69, 45, 215, 243, 3, 77, 14, 148, 1, 8, - 77, 85, 83, 73, 67, 65, 76, 32, 242, 151, 4, 75, 242, 195, 2, 80, 174, 4, - 89, 225, 238, 3, 11, 68, 73, 65, 76, 89, 84, 73, 75, 65, 32, 84, 6, 26, - 84, 247, 186, 15, 80, 4, 206, 187, 15, 69, 35, 82, 6, 166, 246, 12, 79, - 152, 236, 18, 8, 77, 79, 84, 72, 69, 84, 73, 67, 167, 45, 82, 14, 26, 78, - 151, 177, 30, 83, 12, 52, 7, 86, 69, 82, 84, 69, 68, 32, 251, 181, 28, - 70, 10, 64, 2, 66, 82, 45, 10, 68, 79, 85, 66, 76, 69, 32, 65, 82, 67, 6, - 22, 69, 235, 37, 73, 4, 219, 221, 11, 86, 4, 247, 190, 30, 72, 8, 128, 1, - 16, 84, 65, 75, 65, 78, 65, 45, 72, 73, 82, 65, 71, 65, 78, 65, 32, 229, - 233, 17, 9, 86, 89, 75, 65, 32, 65, 66, 79, 86, 4, 222, 129, 10, 83, 35, - 86, 158, 1, 80, 5, 65, 84, 73, 78, 32, 156, 8, 3, 69, 70, 84, 184, 3, 2, - 73, 71, 83, 79, 106, 156, 1, 21, 76, 69, 84, 84, 69, 82, 32, 83, 77, 65, - 76, 76, 32, 67, 65, 80, 73, 84, 65, 76, 32, 53, 13, 83, 77, 65, 76, 76, - 32, 76, 69, 84, 84, 69, 82, 32, 10, 130, 218, 32, 71, 2, 76, 2, 77, 2, - 78, 3, 82, 96, 226, 1, 65, 70, 67, 34, 69, 30, 70, 78, 73, 86, 76, 106, - 79, 2, 85, 134, 1, 82, 50, 84, 226, 171, 12, 83, 182, 146, 2, 66, 138, - 157, 2, 87, 150, 248, 15, 68, 2, 71, 2, 72, 2, 75, 2, 77, 2, 78, 2, 80, - 2, 86, 2, 88, 3, 90, 13, 242, 243, 2, 32, 198, 139, 26, 76, 182, 216, 3, - 69, 2, 79, 3, 86, 5, 241, 139, 12, 3, 32, 67, 69, 7, 246, 213, 32, 83, 3, - 84, 5, 249, 179, 27, 14, 76, 65, 84, 84, 69, 78, 69, 68, 32, 79, 80, 69, - 78, 32, 11, 37, 7, 78, 83, 85, 76, 65, 82, 32, 8, 246, 213, 32, 68, 2, - 71, 2, 82, 3, 84, 7, 164, 134, 16, 14, 32, 87, 73, 84, 72, 32, 68, 79, - 85, 66, 76, 69, 32, 77, 149, 218, 12, 3, 79, 78, 71, 7, 33, 6, 32, 87, - 73, 84, 72, 32, 4, 214, 155, 12, 68, 225, 222, 12, 15, 76, 73, 71, 72, - 84, 32, 67, 69, 78, 84, 82, 65, 76, 73, 90, 7, 11, 32, 4, 150, 250, 11, - 82, 175, 187, 18, 66, 5, 129, 26, 6, 85, 82, 78, 69, 68, 32, 32, 46, 32, - 213, 2, 6, 87, 65, 82, 68, 83, 32, 28, 154, 1, 65, 112, 12, 80, 65, 82, - 69, 78, 84, 72, 69, 83, 73, 83, 32, 226, 12, 72, 182, 1, 84, 129, 164, - 30, 11, 82, 73, 71, 72, 84, 32, 65, 82, 82, 79, 87, 12, 48, 4, 82, 82, - 79, 87, 185, 178, 30, 2, 78, 71, 8, 26, 72, 195, 178, 30, 32, 4, 177, - 178, 30, 3, 69, 65, 68, 4, 164, 223, 7, 4, 65, 66, 79, 86, 225, 190, 24, - 5, 66, 69, 76, 79, 87, 4, 230, 13, 72, 153, 238, 11, 5, 65, 82, 82, 79, - 87, 10, 52, 6, 65, 84, 85, 82, 69, 32, 169, 17, 2, 72, 84, 8, 238, 1, 76, - 23, 82, 10, 36, 3, 78, 71, 32, 139, 181, 31, 87, 8, 172, 7, 7, 68, 79, - 85, 66, 76, 69, 32, 246, 7, 83, 71, 86, 20, 52, 5, 65, 67, 82, 79, 78, - 221, 251, 25, 2, 73, 78, 19, 18, 32, 127, 45, 10, 34, 76, 22, 82, 135, - 175, 30, 66, 4, 41, 2, 69, 70, 4, 21, 3, 73, 71, 72, 4, 229, 212, 16, 6, - 84, 32, 72, 65, 76, 70, 6, 186, 158, 12, 71, 186, 2, 65, 135, 169, 3, 66, - 4, 152, 9, 9, 85, 77, 66, 69, 82, 32, 83, 73, 71, 181, 213, 11, 2, 79, - 84, 18, 136, 1, 17, 76, 68, 32, 80, 69, 82, 77, 73, 67, 32, 76, 69, 84, - 84, 69, 82, 32, 82, 80, 140, 156, 12, 4, 71, 79, 78, 69, 151, 198, 18, - 86, 10, 242, 43, 90, 202, 148, 18, 78, 222, 155, 9, 68, 182, 171, 2, 83, - 159, 243, 1, 65, 2, 169, 203, 24, 6, 69, 78, 32, 77, 65, 82, 12, 18, 65, - 107, 76, 8, 236, 7, 9, 82, 69, 78, 84, 72, 69, 83, 69, 83, 225, 194, 24, - 9, 76, 65, 84, 65, 76, 73, 90, 69, 68, 4, 149, 170, 30, 7, 85, 83, 32, - 83, 73, 71, 78, 38, 18, 69, 127, 73, 6, 72, 5, 86, 69, 82, 83, 69, 217, - 200, 24, 7, 84, 82, 79, 70, 76, 69, 88, 4, 22, 32, 227, 12, 68, 2, 141, - 8, 2, 83, 79, 32, 40, 3, 71, 72, 84, 157, 5, 2, 78, 71, 26, 50, 32, 149, - 4, 7, 87, 65, 82, 68, 83, 32, 72, 24, 104, 5, 65, 82, 82, 79, 87, 218, 1, - 72, 124, 12, 80, 65, 82, 69, 78, 84, 72, 69, 83, 73, 83, 32, 59, 84, 12, - 44, 5, 72, 69, 65, 68, 32, 235, 166, 30, 32, 8, 26, 65, 235, 166, 30, 66, - 6, 36, 3, 78, 68, 32, 243, 199, 31, 66, 4, 40, 4, 68, 79, 87, 78, 1, 2, - 85, 80, 2, 133, 223, 8, 9, 32, 65, 82, 82, 79, 87, 72, 69, 65, 6, 11, 65, - 6, 48, 6, 76, 70, 32, 82, 73, 78, 21, 2, 82, 80, 4, 243, 164, 30, 71, 2, - 17, 2, 79, 79, 2, 255, 197, 31, 78, 4, 250, 218, 9, 65, 241, 150, 21, 5, - 66, 69, 76, 79, 87, 2, 233, 195, 24, 2, 65, 67, 2, 141, 144, 21, 16, 65, - 82, 80, 79, 79, 78, 32, 87, 73, 84, 72, 32, 66, 65, 82, 66, 6, 11, 32, 6, - 202, 237, 11, 79, 226, 181, 18, 66, 167, 161, 1, 65, 18, 188, 1, 5, 72, - 79, 82, 84, 32, 160, 1, 7, 81, 85, 65, 82, 69, 32, 66, 56, 5, 84, 82, 79, - 78, 71, 168, 193, 3, 2, 85, 83, 168, 254, 20, 2, 78, 65, 221, 208, 5, 6, - 69, 65, 71, 85, 76, 76, 6, 18, 83, 71, 86, 4, 26, 79, 243, 234, 11, 84, - 2, 145, 235, 11, 5, 76, 73, 68, 85, 83, 2, 49, 10, 69, 82, 84, 73, 67, - 65, 76, 32, 76, 73, 2, 171, 234, 11, 78, 4, 196, 214, 7, 5, 82, 65, 67, - 75, 69, 223, 201, 22, 69, 2, 181, 191, 24, 17, 32, 67, 69, 78, 84, 82, - 65, 76, 73, 90, 65, 84, 73, 79, 78, 32, 83, 20, 98, 72, 32, 4, 73, 76, - 68, 69, 136, 1, 6, 82, 73, 80, 76, 69, 32, 69, 5, 85, 82, 78, 69, 68, 2, - 185, 212, 7, 3, 82, 69, 69, 11, 11, 32, 8, 76, 3, 76, 69, 70, 0, 4, 82, - 73, 71, 72, 202, 231, 11, 79, 227, 181, 18, 66, 2, 241, 141, 30, 6, 84, - 32, 72, 65, 76, 70, 6, 210, 213, 15, 65, 176, 212, 15, 5, 85, 78, 68, 69, - 82, 211, 64, 68, 2, 161, 176, 15, 2, 32, 67, 10, 34, 80, 134, 189, 31, - 82, 3, 83, 6, 42, 87, 225, 186, 24, 4, 32, 84, 65, 67, 2, 45, 9, 65, 82, - 68, 83, 32, 65, 82, 82, 79, 2, 231, 139, 30, 87, 6, 246, 149, 26, 76, - 199, 191, 2, 84, 6, 52, 3, 68, 69, 32, 153, 229, 11, 4, 71, 71, 76, 89, - 4, 92, 12, 73, 78, 86, 69, 82, 84, 69, 68, 32, 66, 82, 73, 169, 148, 29, - 5, 66, 82, 73, 68, 71, 2, 197, 185, 24, 2, 68, 71, 6, 198, 236, 11, 45, - 207, 172, 18, 32, 6, 52, 7, 69, 82, 67, 73, 65, 76, 32, 231, 182, 32, 65, - 4, 134, 136, 29, 77, 179, 146, 3, 65, 8, 138, 159, 8, 82, 140, 149, 16, - 3, 79, 83, 73, 190, 239, 2, 76, 159, 252, 3, 65, 38, 214, 1, 70, 84, 10, - 83, 84, 82, 85, 67, 84, 73, 79, 78, 32, 46, 84, 252, 235, 2, 8, 86, 69, - 78, 73, 69, 78, 67, 69, 180, 180, 18, 6, 73, 67, 65, 76, 32, 84, 246, - 173, 9, 74, 241, 99, 7, 71, 82, 85, 69, 78, 84, 32, 6, 228, 147, 27, 4, - 69, 84, 84, 73, 158, 237, 1, 85, 189, 162, 2, 4, 79, 85, 78, 68, 4, 164, - 206, 20, 2, 87, 79, 183, 222, 10, 83, 20, 80, 5, 65, 73, 78, 83, 32, 198, - 1, 79, 28, 4, 82, 79, 76, 32, 143, 146, 3, 73, 12, 48, 3, 65, 83, 32, 93, - 5, 87, 73, 84, 72, 32, 6, 222, 152, 24, 77, 181, 8, 15, 78, 79, 82, 77, - 65, 76, 32, 83, 85, 66, 71, 82, 79, 85, 80, 6, 186, 195, 4, 76, 174, 214, - 19, 86, 171, 233, 4, 79, 2, 217, 169, 30, 2, 85, 82, 4, 188, 236, 19, 3, - 75, 78, 79, 165, 129, 4, 8, 83, 69, 81, 85, 69, 78, 67, 69, 6, 26, 73, - 143, 158, 2, 69, 4, 162, 175, 32, 78, 87, 69, 206, 2, 36, 4, 84, 73, 67, - 32, 171, 18, 89, 202, 2, 186, 1, 67, 204, 1, 6, 69, 80, 65, 67, 84, 32, - 86, 70, 36, 11, 79, 76, 68, 32, 78, 85, 66, 73, 65, 78, 32, 110, 83, 181, - 244, 28, 13, 77, 79, 82, 80, 72, 79, 76, 79, 71, 73, 67, 65, 76, 126, 76, - 9, 79, 77, 66, 73, 78, 73, 78, 71, 32, 161, 3, 5, 65, 80, 73, 84, 65, 6, - 68, 9, 83, 80, 73, 82, 73, 84, 85, 83, 32, 213, 175, 31, 2, 78, 73, 4, - 152, 149, 6, 2, 76, 69, 173, 131, 17, 2, 65, 83, 56, 134, 173, 21, 78, - 230, 193, 2, 68, 145, 238, 6, 8, 84, 72, 79, 85, 83, 65, 78, 68, 4, 146, - 173, 18, 82, 231, 172, 11, 85, 8, 58, 68, 0, 3, 73, 78, 68, 142, 15, 86, - 131, 202, 29, 70, 2, 217, 217, 29, 7, 73, 82, 69, 67, 84, 32, 81, 134, 1, - 56, 3, 77, 65, 76, 193, 11, 6, 89, 77, 66, 79, 76, 32, 120, 45, 9, 76, - 32, 76, 69, 84, 84, 69, 82, 32, 120, 138, 2, 65, 48, 6, 66, 79, 72, 65, - 73, 82, 32, 2, 67, 82, 170, 1, 68, 160, 1, 2, 71, 65, 34, 72, 38, 75, 46, - 70, 34, 76, 66, 79, 134, 3, 83, 90, 84, 46, 90, 230, 211, 5, 86, 214, 54, - 80, 190, 210, 3, 73, 238, 162, 22, 82, 198, 3, 69, 218, 7, 77, 2, 78, - 163, 17, 85, 4, 44, 5, 75, 72, 77, 73, 77, 223, 193, 19, 76, 2, 177, 1, - 4, 73, 67, 32, 75, 10, 92, 12, 89, 80, 84, 79, 71, 82, 65, 77, 77, 73, - 67, 32, 53, 7, 79, 83, 83, 69, 68, 32, 83, 8, 50, 83, 226, 4, 71, 210, - 133, 32, 69, 219, 7, 78, 2, 207, 220, 24, 72, 12, 80, 9, 73, 65, 76, 69, - 67, 84, 45, 80, 32, 244, 255, 31, 2, 65, 76, 175, 17, 69, 8, 182, 156, 8, - 72, 250, 150, 8, 65, 244, 183, 12, 2, 75, 65, 255, 165, 3, 78, 4, 190, 3, - 78, 223, 255, 31, 77, 4, 190, 198, 25, 79, 215, 150, 5, 65, 8, 42, 72, - 234, 233, 28, 65, 255, 165, 3, 83, 4, 226, 143, 32, 69, 219, 19, 73, 4, - 176, 191, 19, 7, 45, 83, 72, 65, 80, 69, 68, 155, 241, 7, 65, 35, 36, 3, - 76, 68, 32, 207, 253, 31, 79, 30, 76, 7, 67, 79, 80, 84, 73, 67, 32, 189, - 1, 7, 78, 85, 66, 73, 65, 78, 32, 22, 98, 71, 42, 72, 184, 1, 2, 83, 72, - 154, 222, 14, 68, 142, 248, 9, 79, 222, 224, 5, 69, 207, 104, 65, 2, 17, - 2, 65, 78, 2, 183, 251, 15, 71, 8, 138, 195, 25, 79, 238, 125, 65, 139, - 204, 5, 69, 8, 50, 78, 228, 177, 16, 2, 83, 72, 155, 158, 5, 87, 4, 154, - 140, 32, 71, 3, 89, 10, 54, 72, 138, 151, 6, 65, 178, 227, 25, 79, 219, - 3, 73, 4, 142, 254, 31, 73, 187, 13, 69, 4, 240, 241, 31, 3, 72, 69, 84, - 167, 8, 65, 2, 135, 250, 31, 65, 14, 62, 75, 30, 77, 2, 80, 22, 83, 241, - 234, 27, 3, 84, 65, 85, 4, 26, 72, 143, 138, 32, 65, 2, 131, 235, 27, 73, - 4, 188, 175, 16, 6, 72, 73, 77, 65, 32, 83, 193, 254, 3, 3, 84, 65, 85, - 4, 188, 200, 24, 3, 76, 69, 70, 245, 145, 2, 4, 82, 73, 71, 72, 6, 96, 6, - 78, 73, 83, 72, 32, 86, 204, 200, 25, 8, 82, 69, 83, 80, 79, 78, 68, 83, - 231, 201, 5, 65, 2, 213, 229, 28, 4, 69, 82, 83, 69, 44, 104, 2, 78, 84, - 156, 132, 3, 6, 67, 72, 32, 65, 78, 68, 253, 142, 28, 8, 80, 76, 69, 32, - 87, 73, 84, 72, 40, 56, 2, 69, 82, 37, 8, 73, 78, 71, 32, 82, 79, 68, 32, - 4, 190, 181, 27, 66, 195, 131, 1, 83, 36, 48, 4, 84, 69, 78, 83, 1, 4, - 85, 78, 73, 84, 18, 133, 220, 23, 2, 32, 68, 51, 82, 69, 76, 5, 73, 67, - 75, 69, 84, 34, 79, 198, 5, 85, 50, 89, 143, 214, 30, 65, 4, 196, 168, 8, - 3, 68, 73, 84, 181, 201, 20, 7, 83, 67, 69, 78, 84, 32, 77, 5, 169, 247, - 26, 3, 32, 66, 65, 28, 84, 2, 83, 83, 152, 242, 29, 3, 67, 79, 68, 152, - 155, 1, 3, 73, 83, 83, 255, 57, 87, 22, 46, 32, 184, 2, 3, 69, 68, 32, - 223, 1, 73, 14, 44, 3, 79, 70, 32, 82, 80, 179, 212, 31, 77, 4, 224, 172, - 28, 6, 74, 69, 82, 85, 83, 65, 145, 208, 2, 5, 76, 79, 82, 82, 65, 8, 76, - 10, 65, 84, 84, 89, 32, 87, 73, 84, 72, 32, 41, 5, 79, 77, 77, 69, 69, 4, - 218, 165, 10, 82, 25, 3, 76, 69, 70, 5, 189, 161, 3, 11, 32, 87, 73, 84, - 72, 32, 72, 65, 76, 70, 45, 6, 204, 132, 25, 37, 78, 69, 71, 65, 84, 73, - 86, 69, 32, 83, 81, 85, 65, 82, 69, 68, 32, 76, 65, 84, 73, 78, 32, 67, - 65, 80, 73, 84, 65, 76, 32, 76, 69, 84, 84, 69, 82, 192, 221, 2, 2, 70, - 76, 189, 210, 2, 3, 83, 87, 79, 2, 145, 235, 23, 5, 78, 71, 32, 76, 65, - 4, 248, 199, 17, 3, 90, 69, 73, 255, 167, 14, 84, 6, 130, 220, 19, 73, - 189, 149, 7, 4, 83, 84, 65, 76, 206, 19, 154, 1, 66, 20, 8, 78, 69, 73, - 70, 79, 82, 77, 32, 154, 250, 1, 80, 114, 82, 180, 3, 2, 83, 84, 242, - 219, 15, 67, 161, 229, 9, 6, 84, 32, 79, 70, 32, 77, 2, 211, 175, 5, 69, - 170, 19, 176, 1, 13, 78, 85, 77, 69, 82, 73, 67, 32, 83, 73, 71, 78, 32, - 184, 20, 17, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 83, 73, 71, - 78, 32, 233, 1, 5, 83, 73, 71, 78, 32, 222, 1, 78, 69, 166, 2, 70, 152, - 3, 2, 78, 73, 154, 2, 79, 130, 3, 83, 151, 4, 84, 24, 68, 5, 73, 71, 72, - 84, 32, 153, 1, 7, 76, 65, 77, 73, 84, 69, 32, 16, 142, 14, 83, 230, 113, - 85, 238, 50, 71, 136, 56, 17, 86, 65, 82, 73, 65, 78, 84, 32, 70, 79, 82, - 77, 32, 85, 83, 83, 85, 214, 193, 26, 68, 219, 248, 1, 65, 8, 232, 140, - 21, 5, 79, 78, 69, 32, 84, 246, 144, 9, 70, 175, 14, 84, 58, 48, 4, 73, - 86, 69, 32, 125, 4, 79, 85, 82, 32, 26, 70, 83, 206, 1, 66, 250, 12, 65, - 82, 71, 138, 110, 85, 203, 172, 27, 68, 6, 182, 15, 72, 173, 144, 19, 5, - 73, 88, 84, 72, 83, 32, 150, 1, 66, 44, 18, 86, 65, 82, 73, 65, 78, 84, - 32, 70, 79, 82, 77, 32, 76, 73, 77, 77, 85, 206, 12, 65, 82, 71, 26, 83, - 242, 109, 85, 203, 172, 27, 68, 6, 204, 123, 3, 65, 78, 50, 163, 212, 26, - 85, 9, 214, 202, 12, 32, 143, 190, 19, 52, 24, 44, 4, 71, 73, 68, 65, 33, - 3, 78, 69, 32, 4, 238, 159, 30, 69, 207, 104, 77, 20, 156, 1, 19, 86, 65, - 82, 73, 65, 78, 84, 32, 70, 79, 82, 77, 32, 73, 76, 73, 77, 77, 85, 174, - 7, 83, 230, 113, 85, 238, 50, 71, 222, 249, 26, 68, 219, 248, 1, 65, 9, - 150, 132, 32, 32, 186, 2, 51, 3, 52, 28, 92, 16, 76, 68, 32, 65, 83, 83, - 89, 82, 73, 65, 78, 32, 79, 78, 69, 32, 37, 3, 78, 69, 32, 4, 154, 194, - 18, 83, 175, 192, 11, 81, 24, 166, 1, 69, 52, 8, 81, 85, 65, 82, 84, 69, - 82, 32, 206, 7, 66, 54, 71, 84, 5, 84, 72, 73, 82, 68, 200, 194, 24, 2, - 83, 72, 217, 191, 6, 6, 72, 65, 76, 70, 32, 71, 4, 158, 8, 83, 205, 164, - 1, 5, 73, 71, 72, 84, 72, 4, 166, 155, 30, 65, 207, 111, 71, 38, 144, 1, - 5, 69, 86, 69, 78, 32, 188, 1, 20, 72, 65, 82, 50, 32, 84, 73, 77, 69, - 83, 32, 71, 65, 76, 32, 80, 76, 85, 83, 32, 37, 3, 73, 88, 32, 18, 148, - 1, 17, 86, 65, 82, 73, 65, 78, 84, 32, 70, 79, 82, 77, 32, 73, 77, 73, - 78, 218, 1, 83, 230, 113, 85, 238, 50, 71, 222, 249, 26, 68, 219, 248, 1, - 65, 6, 238, 194, 12, 32, 143, 190, 19, 51, 4, 222, 159, 28, 68, 167, 225, - 2, 77, 16, 142, 1, 83, 142, 3, 65, 218, 110, 85, 238, 50, 71, 144, 229, - 26, 16, 86, 65, 82, 73, 65, 78, 84, 32, 70, 79, 82, 77, 32, 65, 83, 72, - 207, 20, 68, 2, 227, 61, 72, 50, 52, 5, 72, 82, 69, 69, 32, 241, 1, 3, - 87, 79, 32, 28, 142, 1, 66, 40, 4, 83, 72, 65, 82, 24, 16, 86, 65, 82, - 73, 65, 78, 84, 32, 70, 79, 82, 77, 32, 69, 83, 72, 118, 65, 82, 71, 211, - 154, 28, 68, 6, 136, 112, 3, 85, 82, 85, 199, 10, 65, 8, 226, 111, 50, 3, - 85, 4, 130, 224, 24, 49, 203, 3, 50, 22, 82, 65, 30, 66, 32, 2, 69, 83, - 22, 71, 26, 83, 61, 6, 84, 72, 73, 82, 68, 83, 4, 225, 184, 1, 2, 83, 72, - 4, 254, 120, 65, 223, 201, 26, 85, 2, 247, 226, 24, 72, 4, 53, 3, 69, 83, - 72, 4, 11, 72, 4, 17, 2, 65, 82, 4, 142, 251, 31, 50, 3, 85, 4, 11, 32, - 4, 196, 186, 27, 12, 86, 65, 82, 73, 65, 78, 84, 32, 70, 79, 82, 77, 171, - 95, 68, 10, 160, 1, 9, 68, 73, 65, 71, 79, 78, 65, 76, 32, 204, 203, 25, - 6, 86, 69, 82, 84, 73, 67, 133, 247, 2, 14, 79, 76, 68, 32, 65, 83, 83, - 89, 82, 73, 65, 78, 32, 87, 6, 192, 173, 28, 2, 84, 82, 160, 251, 2, 4, - 81, 85, 65, 68, 15, 67, 194, 17, 202, 1, 65, 186, 15, 66, 170, 5, 68, - 190, 15, 69, 202, 10, 71, 182, 30, 72, 238, 3, 73, 238, 4, 75, 154, 20, - 76, 134, 37, 77, 134, 6, 78, 226, 17, 80, 210, 3, 82, 42, 83, 238, 19, - 84, 186, 8, 85, 207, 20, 90, 141, 1, 160, 1, 7, 32, 84, 73, 77, 69, 83, - 32, 142, 1, 66, 250, 3, 68, 38, 75, 90, 76, 212, 1, 3, 77, 65, 82, 78, - 78, 134, 2, 82, 42, 83, 154, 235, 30, 80, 215, 127, 50, 16, 144, 173, 1, - 5, 76, 65, 71, 65, 82, 226, 23, 71, 246, 239, 12, 73, 154, 182, 13, 77, - 182, 161, 2, 83, 218, 162, 1, 66, 246, 67, 72, 187, 2, 65, 45, 22, 32, - 219, 2, 50, 28, 48, 6, 84, 73, 77, 69, 83, 32, 139, 203, 1, 71, 26, 180, - 1, 2, 71, 65, 38, 73, 36, 2, 83, 72, 128, 113, 8, 85, 32, 80, 76, 85, 83, - 32, 85, 188, 56, 4, 68, 85, 78, 51, 166, 2, 65, 204, 2, 3, 78, 85, 78, - 242, 27, 76, 143, 165, 30, 72, 4, 194, 210, 1, 78, 231, 159, 30, 76, 4, - 210, 169, 1, 71, 223, 200, 29, 77, 4, 178, 216, 24, 85, 143, 177, 5, 69, - 15, 37, 7, 32, 84, 73, 77, 69, 83, 32, 12, 206, 133, 1, 77, 130, 59, 71, - 194, 9, 83, 234, 10, 84, 140, 179, 28, 2, 66, 65, 147, 233, 1, 65, 5, - 249, 51, 5, 32, 84, 73, 77, 69, 7, 37, 7, 32, 84, 73, 77, 69, 83, 32, 4, - 252, 27, 5, 83, 72, 73, 84, 65, 215, 80, 69, 23, 68, 7, 32, 84, 73, 77, - 69, 83, 32, 218, 202, 25, 69, 187, 212, 5, 65, 16, 102, 75, 156, 173, 1, - 2, 68, 73, 246, 223, 26, 71, 218, 248, 1, 85, 238, 94, 65, 170, 92, 83, - 215, 42, 72, 4, 130, 158, 1, 65, 135, 208, 30, 73, 7, 37, 7, 32, 84, 73, - 77, 69, 83, 32, 4, 210, 253, 3, 75, 223, 194, 27, 83, 13, 26, 32, 243, - 191, 31, 83, 8, 128, 1, 10, 80, 76, 85, 83, 32, 78, 65, 71, 65, 32, 128, - 185, 8, 7, 84, 72, 82, 69, 69, 32, 84, 177, 252, 5, 4, 79, 86, 69, 82, 4, - 144, 140, 1, 16, 79, 80, 80, 79, 83, 73, 78, 71, 32, 65, 78, 32, 80, 76, - 85, 83, 251, 175, 23, 83, 6, 200, 49, 2, 65, 68, 239, 151, 18, 75, 18, - 26, 72, 131, 164, 12, 65, 17, 42, 32, 202, 200, 18, 71, 223, 161, 13, 50, - 10, 68, 9, 79, 86, 69, 82, 32, 65, 83, 72, 32, 174, 97, 90, 135, 112, 75, - 6, 176, 1, 8, 79, 86, 69, 82, 32, 65, 83, 72, 157, 143, 1, 29, 84, 85, - 71, 50, 32, 79, 86, 69, 82, 32, 84, 85, 71, 50, 32, 84, 85, 71, 50, 32, - 79, 86, 69, 82, 32, 84, 85, 71, 50, 5, 149, 145, 1, 27, 32, 67, 82, 79, - 83, 83, 73, 78, 71, 32, 65, 83, 72, 32, 79, 86, 69, 82, 32, 65, 83, 72, - 32, 79, 86, 69, 82, 52, 30, 65, 158, 2, 73, 95, 85, 27, 66, 68, 44, 4, - 72, 65, 82, 50, 90, 76, 66, 82, 251, 238, 27, 71, 5, 145, 208, 1, 6, 32, - 84, 73, 77, 69, 83, 9, 37, 7, 32, 84, 73, 77, 69, 83, 32, 6, 238, 164, 1, - 65, 170, 173, 30, 78, 163, 17, 90, 7, 252, 219, 30, 7, 32, 79, 86, 69, - 82, 32, 66, 239, 136, 1, 65, 5, 211, 203, 24, 65, 9, 37, 7, 32, 84, 73, - 77, 69, 83, 32, 6, 134, 156, 1, 73, 134, 167, 29, 71, 187, 161, 1, 65, - 19, 50, 32, 168, 1, 3, 76, 85, 71, 239, 169, 1, 82, 8, 88, 8, 79, 86, 69, - 82, 32, 66, 85, 32, 129, 183, 27, 8, 67, 82, 79, 83, 83, 73, 78, 71, 6, - 164, 198, 12, 7, 84, 73, 77, 69, 83, 32, 78, 246, 224, 17, 65, 135, 108, - 85, 5, 189, 242, 3, 8, 32, 79, 86, 69, 82, 32, 66, 85, 180, 1, 34, 65, - 222, 5, 73, 195, 2, 85, 67, 50, 71, 150, 5, 82, 146, 49, 32, 131, 171, - 31, 77, 55, 26, 32, 139, 225, 31, 51, 50, 76, 13, 75, 73, 83, 73, 77, 53, - 32, 84, 73, 77, 69, 83, 32, 227, 198, 1, 84, 48, 146, 1, 65, 30, 66, 38, - 71, 112, 2, 73, 82, 42, 76, 94, 85, 182, 131, 1, 80, 162, 61, 84, 218, - 230, 27, 75, 242, 158, 2, 78, 254, 2, 83, 163, 17, 72, 4, 110, 32, 255, - 188, 30, 77, 4, 246, 245, 29, 65, 147, 233, 1, 73, 10, 34, 65, 58, 73, - 235, 151, 31, 85, 5, 11, 32, 2, 133, 246, 29, 6, 80, 76, 85, 83, 32, 77, - 5, 219, 196, 24, 82, 5, 229, 255, 13, 5, 32, 80, 76, 85, 83, 8, 26, 85, - 179, 221, 31, 65, 7, 160, 151, 1, 7, 32, 80, 76, 85, 83, 32, 77, 143, - 198, 30, 77, 6, 52, 7, 50, 32, 80, 76, 85, 83, 32, 171, 219, 31, 83, 4, - 128, 27, 2, 71, 73, 139, 217, 29, 77, 7, 191, 193, 12, 65, 25, 54, 77, - 150, 1, 78, 76, 2, 83, 72, 231, 217, 31, 66, 13, 44, 7, 32, 84, 73, 77, - 69, 83, 32, 59, 50, 6, 220, 69, 2, 85, 32, 230, 212, 13, 73, 199, 147, - 17, 83, 5, 221, 152, 13, 6, 32, 84, 73, 77, 69, 83, 5, 181, 239, 18, 14, - 32, 75, 65, 83, 75, 65, 76, 32, 85, 32, 71, 85, 78, 85, 5, 245, 157, 1, - 5, 32, 80, 76, 85, 83, 91, 70, 32, 66, 66, 94, 71, 234, 4, 78, 198, 185, - 24, 82, 175, 153, 7, 72, 6, 154, 176, 1, 71, 170, 3, 83, 153, 197, 12, 4, - 79, 86, 69, 82, 9, 52, 7, 32, 84, 73, 77, 69, 83, 32, 255, 215, 31, 50, - 4, 250, 145, 1, 69, 247, 152, 30, 83, 61, 52, 7, 32, 84, 73, 77, 69, 83, - 32, 131, 145, 31, 85, 56, 122, 65, 58, 68, 30, 71, 74, 75, 90, 76, 110, - 77, 50, 83, 130, 80, 69, 218, 58, 73, 190, 198, 16, 72, 170, 238, 13, 78, - 3, 80, 6, 32, 2, 83, 72, 235, 168, 31, 78, 5, 215, 206, 14, 32, 4, 138, - 134, 31, 73, 3, 85, 8, 26, 73, 183, 213, 31, 65, 7, 140, 141, 1, 2, 82, - 50, 151, 199, 30, 83, 8, 26, 85, 211, 185, 1, 65, 6, 40, 4, 83, 72, 85, - 50, 195, 212, 31, 82, 5, 175, 26, 32, 8, 26, 65, 45, 2, 85, 72, 6, 202, - 26, 77, 205, 158, 24, 3, 75, 45, 48, 2, 225, 57, 5, 32, 80, 76, 85, 83, - 6, 162, 235, 29, 65, 198, 170, 1, 69, 223, 61, 73, 4, 238, 138, 1, 73, - 211, 177, 30, 72, 11, 26, 51, 215, 210, 31, 52, 7, 143, 9, 32, 117, 130, - 1, 32, 90, 50, 210, 1, 78, 202, 1, 82, 60, 3, 83, 72, 50, 52, 3, 90, 69, - 78, 170, 225, 18, 71, 230, 235, 11, 68, 215, 127, 76, 4, 168, 25, 11, 79, - 86, 69, 82, 32, 69, 32, 78, 85, 78, 32, 253, 94, 4, 84, 73, 77, 69, 19, - 37, 7, 32, 84, 73, 77, 69, 83, 32, 16, 134, 1, 83, 144, 168, 1, 10, 65, - 32, 80, 76, 85, 83, 32, 72, 65, 32, 242, 133, 29, 71, 206, 16, 80, 154, - 24, 75, 254, 100, 77, 219, 19, 85, 4, 130, 198, 30, 65, 227, 114, 72, 15, - 11, 32, 12, 96, 4, 67, 82, 79, 83, 0, 4, 79, 80, 80, 79, 36, 6, 84, 73, - 77, 69, 83, 32, 155, 158, 24, 83, 2, 237, 146, 14, 4, 83, 73, 78, 71, 6, - 204, 138, 1, 4, 71, 65, 78, 50, 227, 172, 30, 77, 6, 36, 3, 73, 78, 50, - 203, 253, 30, 69, 5, 131, 193, 30, 32, 5, 229, 16, 9, 32, 67, 82, 79, 83, - 83, 73, 78, 71, 63, 11, 32, 60, 96, 14, 83, 72, 69, 83, 72, 73, 71, 32, - 84, 73, 77, 69, 83, 32, 97, 6, 84, 73, 77, 69, 83, 32, 16, 178, 131, 1, - 73, 194, 224, 2, 77, 222, 255, 25, 65, 244, 107, 2, 76, 65, 198, 87, 83, - 147, 17, 72, 44, 134, 1, 65, 68, 5, 68, 85, 78, 51, 32, 26, 75, 58, 76, - 62, 83, 34, 85, 222, 127, 73, 204, 31, 2, 72, 65, 150, 253, 28, 71, 179, - 101, 66, 9, 244, 86, 9, 32, 80, 76, 85, 83, 32, 76, 65, 76, 147, 243, 30, - 78, 4, 217, 32, 2, 71, 85, 6, 168, 153, 24, 5, 65, 83, 75, 65, 76, 251, - 185, 3, 85, 8, 34, 65, 210, 200, 31, 73, 3, 85, 5, 201, 85, 2, 76, 32, 4, - 226, 177, 31, 72, 215, 22, 85, 4, 150, 200, 31, 50, 3, 68, 160, 2, 42, - 65, 166, 19, 69, 122, 73, 135, 5, 85, 191, 1, 114, 50, 144, 16, 2, 66, - 65, 94, 68, 22, 76, 38, 78, 206, 140, 1, 32, 154, 6, 82, 146, 145, 28, - 83, 191, 145, 2, 77, 153, 1, 11, 32, 150, 1, 68, 6, 84, 73, 77, 69, 83, - 32, 153, 128, 1, 5, 79, 86, 69, 82, 32, 148, 1, 202, 1, 65, 162, 2, 66, - 154, 1, 68, 178, 1, 69, 58, 71, 178, 1, 72, 230, 1, 73, 70, 75, 194, 1, - 76, 62, 77, 50, 78, 130, 1, 83, 142, 1, 85, 222, 154, 1, 84, 240, 130, - 23, 3, 90, 73, 90, 247, 150, 7, 80, 18, 132, 1, 6, 32, 80, 76, 85, 83, - 32, 50, 78, 52, 2, 83, 72, 225, 160, 18, 14, 66, 50, 32, 84, 69, 78, 85, - 32, 80, 76, 85, 83, 32, 84, 6, 202, 58, 68, 134, 200, 13, 73, 155, 190, - 17, 72, 5, 185, 61, 9, 32, 80, 76, 85, 83, 32, 75, 65, 75, 7, 11, 50, 5, - 133, 90, 6, 32, 80, 76, 85, 83, 32, 10, 26, 65, 77, 2, 85, 82, 6, 42, 72, - 44, 2, 82, 32, 151, 193, 31, 68, 2, 11, 65, 2, 135, 168, 24, 82, 5, 11, - 32, 2, 185, 246, 30, 4, 80, 76, 85, 83, 14, 34, 73, 54, 85, 155, 192, 31, - 65, 7, 17, 2, 77, 32, 4, 146, 22, 84, 207, 129, 1, 71, 6, 56, 8, 71, 32, - 84, 73, 77, 69, 83, 32, 223, 191, 31, 66, 4, 158, 119, 73, 151, 45, 75, - 10, 38, 78, 254, 2, 76, 183, 232, 29, 82, 5, 243, 28, 32, 18, 18, 65, 99, - 73, 11, 26, 82, 247, 158, 1, 78, 7, 33, 6, 32, 80, 76, 85, 83, 32, 4, - 222, 167, 31, 78, 255, 2, 68, 9, 174, 122, 52, 225, 228, 12, 7, 82, 50, - 32, 80, 76, 85, 83, 12, 62, 65, 154, 124, 85, 205, 226, 12, 6, 73, 32, - 80, 76, 85, 83, 8, 40, 6, 32, 80, 76, 85, 83, 32, 83, 76, 4, 48, 6, 76, - 85, 32, 80, 76, 85, 187, 188, 31, 65, 2, 11, 83, 2, 215, 97, 32, 5, 249, - 221, 13, 5, 32, 80, 76, 85, 83, 4, 176, 100, 10, 83, 72, 32, 80, 76, 85, - 83, 32, 72, 85, 147, 15, 71, 12, 34, 65, 36, 2, 73, 68, 27, 85, 4, 250, - 204, 24, 83, 147, 238, 6, 75, 5, 225, 77, 2, 32, 80, 4, 60, 5, 83, 72, - 85, 50, 32, 197, 127, 5, 51, 32, 80, 76, 85, 2, 205, 158, 1, 3, 80, 76, - 85, 8, 26, 65, 199, 184, 31, 85, 7, 11, 77, 5, 227, 159, 1, 32, 6, 250, - 77, 69, 154, 131, 29, 85, 163, 232, 1, 73, 10, 26, 69, 73, 2, 85, 78, 7, - 33, 6, 32, 80, 76, 85, 83, 32, 4, 150, 159, 24, 69, 215, 133, 7, 71, 5, - 11, 32, 2, 187, 101, 79, 14, 42, 72, 150, 216, 23, 65, 239, 206, 7, 85, - 8, 18, 69, 51, 73, 5, 237, 190, 30, 7, 32, 80, 76, 85, 83, 32, 84, 4, - 146, 183, 31, 68, 3, 77, 7, 11, 68, 5, 209, 214, 13, 5, 32, 80, 76, 85, - 83, 7, 11, 32, 4, 142, 218, 29, 82, 129, 175, 1, 11, 67, 82, 79, 83, 83, - 73, 78, 71, 32, 71, 65, 5, 191, 131, 1, 32, 7, 134, 131, 1, 32, 247, 161, - 30, 65, 11, 11, 50, 9, 11, 32, 6, 80, 8, 67, 82, 79, 83, 83, 73, 78, 71, - 0, 4, 79, 86, 69, 82, 211, 182, 26, 84, 2, 197, 49, 3, 32, 71, 65, 8, 44, - 5, 83, 72, 84, 73, 78, 207, 154, 24, 50, 7, 37, 7, 32, 84, 73, 77, 69, - 83, 32, 4, 250, 186, 30, 75, 215, 120, 85, 49, 70, 32, 94, 52, 114, 82, - 190, 1, 83, 146, 173, 30, 68, 211, 130, 1, 71, 6, 188, 253, 8, 6, 84, 73, - 77, 69, 83, 32, 205, 244, 4, 8, 67, 82, 79, 83, 83, 73, 78, 71, 7, 11, - 32, 4, 64, 8, 67, 82, 79, 83, 83, 73, 78, 71, 1, 4, 79, 86, 69, 82, 2, - 233, 150, 24, 3, 32, 71, 73, 16, 26, 51, 147, 136, 1, 50, 13, 37, 7, 32, - 84, 73, 77, 69, 83, 32, 10, 70, 65, 0, 2, 76, 85, 206, 127, 71, 246, 239, - 12, 73, 155, 190, 17, 80, 2, 189, 239, 13, 7, 32, 80, 76, 85, 83, 32, 73, - 14, 26, 72, 147, 166, 30, 65, 13, 11, 32, 10, 22, 84, 247, 20, 67, 8, 44, - 5, 73, 77, 69, 83, 32, 255, 137, 31, 69, 6, 192, 20, 6, 71, 73, 83, 72, - 32, 67, 146, 126, 84, 243, 213, 29, 66, 43, 110, 50, 174, 1, 68, 218, 1, - 77, 54, 82, 144, 236, 13, 9, 32, 67, 82, 79, 83, 83, 73, 78, 71, 247, - 189, 17, 76, 15, 11, 32, 12, 48, 6, 84, 73, 77, 69, 83, 32, 167, 132, 1, - 71, 10, 252, 24, 3, 75, 65, 75, 194, 75, 73, 152, 20, 10, 83, 65, 76, 32, - 80, 76, 85, 83, 32, 84, 247, 179, 29, 78, 11, 11, 32, 8, 128, 1, 10, 80, - 76, 85, 83, 32, 71, 73, 83, 72, 32, 16, 6, 84, 73, 77, 69, 83, 32, 177, - 66, 8, 79, 86, 69, 82, 32, 71, 85, 68, 2, 135, 121, 84, 4, 172, 145, 1, - 5, 65, 32, 80, 76, 85, 239, 160, 29, 75, 5, 17, 2, 32, 84, 2, 241, 42, 4, - 73, 77, 69, 83, 9, 26, 85, 235, 169, 31, 55, 4, 214, 168, 31, 83, 147, 1, - 78, 50, 30, 65, 82, 73, 215, 1, 85, 11, 26, 32, 143, 169, 31, 76, 6, 32, - 2, 84, 69, 147, 128, 1, 71, 4, 235, 127, 78, 23, 37, 7, 32, 84, 73, 77, - 69, 83, 32, 20, 104, 3, 65, 83, 72, 206, 198, 27, 68, 202, 224, 2, 78, - 94, 75, 170, 57, 66, 2, 71, 162, 25, 83, 143, 45, 85, 7, 224, 55, 8, 32, - 79, 86, 69, 82, 32, 72, 73, 227, 239, 30, 50, 19, 48, 2, 66, 50, 166, - 141, 24, 76, 159, 152, 7, 83, 13, 37, 7, 32, 84, 73, 77, 69, 83, 32, 10, - 254, 138, 1, 75, 178, 186, 26, 76, 186, 215, 2, 72, 226, 57, 65, 195, 9, - 85, 49, 104, 3, 68, 73, 77, 94, 71, 214, 1, 76, 62, 77, 206, 159, 31, 32, - 170, 1, 83, 146, 1, 66, 2, 78, 3, 82, 7, 53, 11, 32, 79, 86, 69, 82, 32, - 73, 68, 73, 77, 32, 4, 202, 244, 23, 83, 155, 183, 6, 66, 13, 11, 73, 11, - 11, 32, 8, 162, 123, 71, 220, 230, 11, 31, 79, 86, 69, 82, 32, 73, 71, - 73, 32, 83, 72, 73, 82, 32, 79, 86, 69, 82, 32, 83, 72, 73, 82, 32, 85, - 68, 32, 79, 86, 69, 82, 142, 134, 17, 68, 155, 168, 1, 82, 7, 26, 32, - 167, 162, 31, 50, 2, 169, 72, 4, 84, 73, 77, 69, 13, 26, 32, 139, 210, - 30, 73, 8, 76, 4, 67, 82, 79, 83, 0, 4, 79, 80, 80, 79, 162, 111, 84, - 143, 130, 23, 83, 2, 197, 158, 30, 5, 83, 73, 78, 71, 32, 224, 1, 78, 65, - 146, 15, 73, 242, 1, 85, 158, 73, 69, 229, 168, 23, 4, 87, 85, 51, 49, - 177, 1, 164, 1, 7, 32, 84, 73, 77, 69, 83, 32, 210, 9, 50, 66, 68, 102, - 75, 50, 76, 98, 77, 28, 4, 83, 75, 65, 76, 248, 192, 15, 6, 80, 32, 69, - 76, 65, 77, 219, 209, 15, 66, 134, 1, 170, 1, 65, 94, 66, 82, 69, 30, 71, - 182, 2, 73, 34, 75, 34, 76, 38, 77, 170, 1, 83, 118, 84, 34, 85, 214, 8, - 72, 246, 51, 78, 222, 215, 16, 80, 154, 221, 13, 82, 147, 17, 90, 13, 46, - 68, 250, 239, 30, 78, 165, 44, 2, 83, 72, 5, 229, 50, 7, 32, 80, 76, 85, - 83, 32, 75, 10, 34, 65, 194, 156, 31, 73, 3, 85, 6, 186, 179, 29, 76, - 134, 233, 1, 68, 3, 82, 4, 138, 25, 82, 151, 61, 83, 26, 30, 65, 98, 73, - 147, 1, 85, 11, 38, 82, 206, 123, 78, 231, 159, 30, 76, 5, 249, 21, 10, - 32, 80, 76, 85, 83, 32, 83, 72, 65, 51, 11, 32, 2, 83, 72, 171, 129, 24, + 69, 82, 84, 73, 67, 65, 76, 32, 146, 144, 16, 71, 243, 182, 4, 76, 6, + 226, 17, 82, 131, 129, 16, 67, 4, 145, 17, 2, 82, 69, 4, 154, 223, 31, + 65, 135, 42, 73, 4, 21, 3, 65, 67, 82, 4, 165, 255, 16, 2, 79, 78, 4, + 138, 37, 80, 179, 246, 30, 86, 6, 146, 38, 76, 253, 228, 7, 9, 65, 82, + 69, 78, 84, 72, 69, 83, 69, 6, 240, 52, 4, 71, 72, 84, 87, 155, 253, 9, + 78, 7, 11, 32, 4, 44, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, 2, 143, 224, + 30, 84, 6, 182, 155, 12, 83, 227, 182, 14, 76, 2, 201, 138, 16, 2, 32, + 67, 6, 138, 51, 32, 131, 175, 31, 87, 18, 72, 9, 78, 67, 76, 79, 83, 73, + 78, 71, 32, 177, 35, 4, 81, 85, 65, 76, 14, 132, 1, 6, 67, 73, 82, 67, + 76, 69, 22, 83, 216, 165, 6, 3, 75, 69, 89, 216, 208, 19, 7, 85, 80, 87, + 65, 82, 68, 32, 171, 204, 5, 68, 5, 215, 185, 18, 32, 4, 234, 226, 25, + 67, 147, 252, 5, 81, 6, 48, 2, 69, 82, 154, 141, 5, 79, 175, 131, 12, 76, + 2, 255, 76, 77, 128, 1, 88, 17, 76, 65, 71, 79, 76, 73, 84, 73, 67, 32, + 76, 69, 84, 84, 69, 82, 32, 143, 3, 82, 76, 238, 1, 68, 30, 73, 46, 83, + 50, 84, 158, 142, 6, 65, 22, 66, 94, 67, 134, 1, 70, 38, 71, 238, 2, 77, + 32, 2, 76, 74, 34, 78, 88, 2, 80, 79, 30, 82, 194, 2, 86, 22, 89, 90, 90, + 210, 137, 19, 72, 190, 133, 2, 85, 162, 177, 5, 79, 235, 5, 75, 4, 214, + 144, 6, 74, 31, 79, 11, 226, 145, 6, 78, 54, 79, 219, 190, 26, 90, 8, + 194, 146, 6, 77, 130, 4, 76, 223, 210, 22, 72, 4, 246, 150, 6, 86, 211, + 210, 26, 83, 52, 38, 65, 185, 3, 4, 69, 69, 75, 32, 38, 84, 5, 78, 84, + 72, 65, 32, 196, 1, 2, 86, 69, 189, 203, 28, 5, 80, 72, 69, 77, 69, 24, + 68, 6, 68, 73, 71, 73, 84, 32, 65, 7, 76, 69, 84, 84, 69, 82, 32, 14, + 170, 239, 16, 83, 254, 152, 14, 70, 70, 84, 62, 90, 223, 86, 79, 10, 154, + 231, 32, 86, 162, 17, 75, 2, 78, 2, 80, 187, 2, 65, 12, 18, 32, 67, 45, + 6, 26, 65, 131, 176, 16, 84, 4, 237, 147, 4, 4, 67, 67, 69, 78, 6, 150, + 22, 86, 168, 242, 11, 6, 65, 67, 85, 84, 69, 45, 139, 249, 3, 77, 14, + 148, 1, 8, 77, 85, 83, 73, 67, 65, 76, 32, 130, 155, 4, 75, 246, 197, 2, + 80, 174, 4, 89, 141, 236, 3, 11, 68, 73, 65, 76, 89, 84, 73, 75, 65, 32, + 84, 6, 26, 84, 255, 195, 15, 80, 4, 214, 196, 15, 69, 35, 82, 6, 146, + 250, 12, 79, 180, 128, 19, 8, 77, 79, 84, 72, 69, 84, 73, 67, 167, 45, + 82, 16, 26, 78, 203, 197, 30, 83, 14, 52, 7, 86, 69, 82, 84, 69, 68, 32, + 167, 197, 28, 70, 12, 60, 2, 66, 82, 69, 9, 68, 79, 85, 66, 76, 69, 32, + 65, 82, 8, 18, 69, 23, 73, 4, 203, 224, 11, 86, 4, 157, 211, 30, 2, 68, + 71, 4, 11, 67, 4, 131, 211, 30, 72, 8, 128, 1, 16, 84, 65, 75, 65, 78, + 65, 45, 72, 73, 82, 65, 71, 65, 78, 65, 32, 229, 242, 17, 9, 86, 89, 75, + 65, 32, 65, 66, 79, 86, 4, 190, 135, 10, 83, 35, 86, 162, 1, 80, 5, 65, + 84, 73, 78, 32, 164, 8, 3, 69, 70, 84, 232, 3, 2, 73, 71, 83, 79, 106, + 156, 1, 21, 76, 69, 84, 84, 69, 82, 32, 83, 77, 65, 76, 76, 32, 67, 65, + 80, 73, 84, 65, 76, 32, 53, 13, 83, 77, 65, 76, 76, 32, 76, 69, 84, 84, + 69, 82, 32, 10, 234, 241, 32, 71, 2, 76, 2, 77, 2, 78, 3, 82, 96, 226, 1, + 65, 70, 67, 34, 69, 30, 70, 78, 73, 86, 76, 106, 79, 2, 85, 134, 1, 82, + 50, 84, 158, 175, 12, 83, 162, 151, 2, 66, 238, 157, 2, 87, 242, 134, 16, + 68, 2, 71, 2, 72, 2, 75, 2, 77, 2, 78, 2, 80, 2, 86, 2, 88, 3, 90, 13, + 182, 246, 2, 32, 150, 157, 26, 76, 138, 220, 3, 69, 2, 79, 3, 86, 5, 173, + 143, 12, 3, 32, 67, 69, 7, 222, 237, 32, 83, 3, 84, 5, 149, 195, 27, 14, + 76, 65, 84, 84, 69, 78, 69, 68, 32, 79, 80, 69, 78, 32, 11, 37, 7, 78, + 83, 85, 76, 65, 82, 32, 8, 222, 237, 32, 68, 2, 71, 2, 82, 3, 84, 7, 148, + 143, 16, 14, 32, 87, 73, 84, 72, 32, 68, 79, 85, 66, 76, 69, 32, 77, 185, + 228, 12, 3, 79, 78, 71, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 146, 159, + 12, 68, 133, 229, 12, 15, 76, 73, 71, 72, 84, 32, 67, 69, 78, 84, 82, 65, + 76, 73, 90, 7, 11, 32, 4, 210, 253, 11, 82, 255, 203, 18, 66, 5, 253, + 185, 30, 7, 85, 82, 78, 69, 68, 32, 87, 36, 46, 32, 133, 3, 6, 87, 65, + 82, 68, 83, 32, 32, 122, 65, 192, 1, 12, 80, 65, 82, 69, 78, 84, 72, 69, + 83, 73, 83, 32, 166, 13, 72, 154, 10, 84, 57, 5, 82, 73, 71, 72, 84, 14, + 52, 5, 78, 71, 76, 69, 32, 77, 4, 82, 82, 79, 87, 6, 140, 253, 3, 6, 67, + 69, 78, 84, 82, 69, 138, 202, 26, 66, 131, 165, 1, 65, 8, 26, 72, 151, + 198, 30, 32, 4, 133, 198, 30, 3, 69, 65, 68, 4, 220, 227, 7, 4, 65, 66, + 79, 86, 217, 209, 24, 5, 66, 69, 76, 79, 87, 4, 142, 14, 72, 245, 240, + 11, 5, 65, 82, 82, 79, 87, 10, 52, 6, 65, 84, 85, 82, 69, 32, 129, 18, 2, + 72, 84, 8, 234, 1, 76, 23, 82, 10, 36, 3, 78, 71, 32, 183, 202, 31, 87, + 8, 236, 7, 7, 68, 79, 85, 66, 76, 69, 32, 242, 7, 83, 71, 86, 26, 48, 5, + 65, 67, 82, 79, 78, 205, 5, 2, 73, 78, 23, 18, 32, 127, 45, 10, 34, 76, + 22, 82, 223, 194, 30, 66, 4, 41, 2, 69, 70, 4, 21, 3, 73, 71, 72, 4, 189, + 221, 16, 6, 84, 32, 72, 65, 76, 70, 10, 54, 86, 250, 20, 65, 150, 140, + 12, 71, 235, 176, 3, 66, 2, 129, 200, 31, 8, 69, 82, 84, 73, 67, 65, 76, + 45, 4, 164, 9, 9, 85, 77, 66, 69, 82, 32, 83, 73, 71, 129, 215, 11, 2, + 79, 84, 18, 136, 1, 17, 76, 68, 32, 80, 69, 82, 77, 73, 67, 32, 76, 69, + 84, 84, 69, 82, 32, 82, 80, 216, 158, 12, 4, 71, 79, 78, 69, 219, 215, + 18, 86, 10, 198, 45, 90, 194, 150, 18, 78, 182, 166, 9, 68, 186, 176, 2, + 83, 239, 246, 1, 65, 2, 153, 212, 24, 6, 69, 78, 32, 77, 65, 82, 12, 18, + 65, 107, 76, 8, 220, 7, 9, 82, 69, 78, 84, 72, 69, 83, 69, 83, 225, 203, + 24, 9, 76, 65, 84, 65, 76, 73, 90, 69, 68, 4, 11, 85, 4, 165, 189, 30, 6, + 83, 32, 83, 73, 71, 78, 40, 18, 69, 127, 73, 6, 72, 5, 86, 69, 82, 83, + 69, 193, 209, 24, 7, 84, 82, 79, 70, 76, 69, 88, 4, 22, 32, 251, 12, 68, + 2, 137, 8, 2, 83, 79, 34, 40, 3, 71, 72, 84, 133, 5, 2, 78, 71, 28, 50, + 32, 253, 3, 7, 87, 65, 82, 68, 83, 32, 72, 26, 108, 5, 65, 82, 82, 79, + 87, 218, 1, 72, 124, 12, 80, 65, 82, 69, 78, 84, 72, 69, 83, 73, 83, 32, + 159, 9, 84, 12, 44, 5, 72, 69, 65, 68, 32, 251, 185, 30, 32, 8, 26, 65, + 251, 185, 30, 66, 6, 36, 3, 78, 68, 32, 223, 222, 31, 66, 4, 40, 4, 68, + 79, 87, 78, 1, 2, 85, 80, 2, 197, 227, 8, 9, 32, 65, 82, 82, 79, 87, 72, + 69, 65, 6, 11, 65, 6, 48, 6, 76, 70, 32, 82, 73, 78, 21, 2, 82, 80, 4, + 131, 184, 30, 71, 2, 17, 2, 79, 79, 2, 235, 220, 31, 78, 4, 222, 223, 9, + 65, 221, 166, 21, 5, 66, 69, 76, 79, 87, 2, 237, 148, 21, 16, 65, 82, 80, + 79, 79, 78, 32, 87, 73, 84, 72, 32, 66, 65, 82, 66, 6, 11, 32, 6, 166, + 240, 11, 79, 178, 198, 18, 66, 131, 165, 1, 65, 24, 166, 1, 72, 204, 1, + 6, 81, 85, 65, 82, 69, 32, 84, 5, 84, 82, 79, 78, 71, 128, 195, 3, 2, 85, + 83, 160, 133, 21, 2, 78, 65, 149, 234, 5, 6, 69, 65, 71, 85, 76, 76, 8, + 40, 4, 79, 82, 84, 32, 187, 186, 16, 65, 6, 18, 83, 71, 86, 4, 26, 79, + 187, 237, 11, 84, 2, 217, 237, 11, 5, 76, 73, 68, 85, 83, 2, 49, 10, 69, + 82, 84, 73, 67, 65, 76, 32, 76, 73, 2, 243, 236, 11, 78, 6, 26, 66, 151, + 216, 31, 65, 4, 164, 218, 7, 5, 82, 65, 67, 75, 69, 251, 216, 22, 69, 2, + 133, 200, 24, 17, 32, 67, 69, 78, 84, 82, 65, 76, 73, 90, 65, 84, 73, 79, + 78, 32, 83, 20, 98, 72, 32, 4, 73, 76, 68, 69, 136, 1, 6, 82, 73, 80, 76, + 69, 32, 69, 5, 85, 82, 78, 69, 68, 2, 153, 216, 7, 3, 82, 69, 69, 11, 11, + 32, 8, 76, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 246, 233, 11, 79, 179, + 198, 18, 66, 2, 245, 160, 30, 6, 84, 32, 72, 65, 76, 70, 6, 178, 221, 15, + 65, 140, 225, 15, 5, 85, 78, 68, 69, 82, 239, 66, 68, 2, 249, 183, 15, 2, + 32, 67, 12, 34, 80, 222, 211, 31, 82, 3, 83, 8, 18, 32, 43, 87, 4, 11, + 84, 4, 185, 195, 24, 2, 65, 67, 4, 25, 4, 65, 82, 68, 83, 4, 241, 173, + 30, 6, 32, 65, 82, 82, 79, 87, 16, 42, 32, 37, 6, 45, 76, 73, 78, 69, 45, + 6, 174, 157, 26, 76, 175, 202, 2, 84, 10, 54, 65, 48, 5, 71, 82, 65, 86, + 69, 139, 214, 15, 77, 4, 25, 4, 67, 85, 84, 69, 5, 147, 221, 11, 45, 5, + 143, 131, 12, 45, 6, 52, 3, 68, 69, 32, 137, 230, 11, 4, 71, 71, 76, 89, + 4, 184, 193, 24, 14, 73, 78, 86, 69, 82, 84, 69, 68, 32, 66, 82, 73, 68, + 71, 149, 229, 4, 5, 66, 82, 73, 68, 71, 6, 194, 237, 11, 45, 159, 189, + 18, 32, 6, 52, 7, 69, 82, 67, 73, 65, 76, 32, 143, 204, 32, 65, 4, 218, + 153, 29, 77, 135, 150, 3, 65, 8, 234, 161, 8, 82, 204, 153, 16, 3, 79, + 83, 73, 250, 244, 2, 76, 231, 130, 4, 65, 38, 214, 1, 70, 84, 10, 83, 84, + 82, 85, 67, 84, 73, 79, 78, 32, 46, 84, 252, 235, 2, 8, 86, 69, 78, 73, + 69, 78, 67, 69, 128, 183, 18, 6, 73, 67, 65, 76, 32, 84, 234, 189, 9, 74, + 217, 102, 7, 71, 82, 85, 69, 78, 84, 32, 6, 192, 160, 27, 4, 69, 84, 84, + 73, 150, 242, 1, 85, 245, 163, 2, 4, 79, 85, 78, 68, 4, 164, 209, 20, 2, + 87, 79, 223, 240, 10, 83, 20, 80, 5, 65, 73, 78, 83, 32, 198, 1, 79, 28, + 4, 82, 79, 76, 32, 183, 146, 3, 73, 12, 48, 3, 65, 83, 32, 93, 5, 87, 73, + 84, 72, 32, 6, 238, 159, 24, 77, 181, 8, 15, 78, 79, 82, 77, 65, 76, 32, + 83, 85, 66, 71, 82, 79, 85, 80, 6, 198, 191, 4, 76, 178, 225, 19, 86, + 239, 243, 4, 79, 2, 153, 188, 30, 2, 85, 82, 4, 196, 238, 19, 3, 75, 78, + 79, 173, 134, 4, 8, 83, 69, 81, 85, 69, 78, 67, 69, 6, 26, 73, 147, 158, + 2, 69, 4, 202, 196, 32, 78, 87, 69, 206, 2, 36, 4, 84, 73, 67, 32, 175, + 18, 89, 202, 2, 186, 1, 67, 204, 1, 6, 69, 80, 65, 67, 84, 32, 86, 70, + 36, 11, 79, 76, 68, 32, 78, 85, 66, 73, 65, 78, 32, 110, 83, 137, 134, + 29, 13, 77, 79, 82, 80, 72, 79, 76, 79, 71, 73, 67, 65, 76, 126, 76, 9, + 79, 77, 66, 73, 78, 73, 78, 71, 32, 161, 3, 5, 65, 80, 73, 84, 65, 6, 68, + 9, 83, 80, 73, 82, 73, 84, 85, 83, 32, 253, 196, 31, 2, 78, 73, 4, 204, + 151, 6, 2, 76, 69, 157, 136, 17, 2, 65, 83, 56, 210, 175, 21, 78, 170, + 198, 2, 68, 141, 250, 6, 8, 84, 72, 79, 85, 83, 65, 78, 68, 4, 142, 175, + 18, 82, 195, 188, 11, 85, 8, 58, 68, 0, 3, 73, 78, 68, 146, 15, 86, 215, + 219, 29, 70, 2, 177, 235, 29, 7, 73, 82, 69, 67, 84, 32, 81, 134, 1, 56, + 3, 77, 65, 76, 197, 11, 6, 89, 77, 66, 79, 76, 32, 120, 45, 9, 76, 32, + 76, 69, 84, 84, 69, 82, 32, 120, 138, 2, 65, 48, 6, 66, 79, 72, 65, 73, + 82, 32, 2, 67, 82, 170, 1, 68, 160, 1, 2, 71, 65, 34, 72, 38, 75, 46, 70, + 34, 76, 66, 79, 138, 3, 83, 90, 84, 46, 90, 150, 214, 5, 86, 214, 54, 80, + 170, 211, 3, 73, 246, 180, 22, 82, 198, 3, 69, 218, 7, 77, 2, 78, 163, + 17, 85, 4, 44, 5, 75, 72, 77, 73, 77, 223, 195, 19, 76, 2, 177, 1, 4, 73, + 67, 32, 75, 10, 92, 12, 89, 80, 84, 79, 71, 82, 65, 77, 77, 73, 67, 32, + 53, 7, 79, 83, 83, 69, 68, 32, 83, 8, 50, 83, 226, 4, 71, 250, 154, 32, + 69, 219, 7, 78, 2, 239, 227, 24, 72, 12, 80, 9, 73, 65, 76, 69, 67, 84, + 45, 80, 32, 156, 149, 32, 2, 65, 76, 175, 17, 69, 8, 154, 159, 8, 72, + 226, 154, 8, 65, 252, 194, 12, 2, 75, 65, 211, 169, 3, 78, 4, 190, 3, 78, + 135, 149, 32, 77, 4, 230, 205, 25, 79, 187, 162, 5, 65, 8, 42, 72, 190, + 251, 28, 65, 211, 169, 3, 83, 4, 138, 165, 32, 69, 219, 19, 73, 4, 176, + 193, 19, 7, 45, 83, 72, 65, 80, 69, 68, 239, 251, 7, 65, 35, 36, 3, 76, + 68, 32, 247, 146, 32, 79, 30, 76, 7, 67, 79, 80, 84, 73, 67, 32, 193, 1, + 7, 78, 85, 66, 73, 65, 78, 32, 22, 98, 71, 42, 72, 188, 1, 2, 83, 72, + 162, 228, 14, 68, 162, 249, 9, 79, 254, 235, 5, 69, 183, 107, 65, 2, 17, + 2, 65, 78, 2, 231, 129, 16, 71, 8, 178, 202, 25, 79, 246, 130, 1, 65, + 131, 213, 5, 69, 8, 50, 78, 172, 184, 16, 2, 83, 72, 147, 154, 5, 87, 4, + 190, 161, 32, 71, 3, 89, 10, 54, 72, 186, 153, 6, 65, 166, 246, 25, 79, + 219, 3, 73, 4, 178, 147, 32, 73, 187, 13, 69, 4, 148, 135, 32, 3, 72, 69, + 84, 167, 8, 65, 2, 171, 143, 32, 65, 14, 62, 75, 30, 77, 2, 80, 22, 83, + 185, 247, 27, 3, 84, 65, 85, 4, 26, 72, 179, 159, 32, 65, 2, 203, 247, + 27, 73, 4, 132, 182, 16, 6, 72, 73, 77, 65, 32, 83, 245, 250, 3, 3, 84, + 65, 85, 4, 216, 207, 24, 3, 76, 69, 70, 133, 151, 2, 4, 82, 73, 71, 72, + 6, 96, 6, 78, 73, 83, 72, 32, 86, 240, 207, 25, 8, 82, 69, 83, 80, 79, + 78, 68, 83, 203, 213, 5, 65, 2, 165, 247, 28, 4, 69, 82, 83, 69, 44, 104, + 2, 78, 84, 192, 132, 3, 6, 67, 72, 32, 65, 78, 68, 253, 163, 28, 8, 80, + 76, 69, 32, 87, 73, 84, 72, 40, 56, 2, 69, 82, 37, 8, 73, 78, 71, 32, 82, + 79, 68, 32, 4, 170, 194, 27, 66, 167, 136, 1, 83, 36, 48, 4, 84, 69, 78, + 83, 1, 4, 85, 78, 73, 84, 18, 145, 227, 23, 2, 32, 68, 51, 82, 69, 76, 5, + 73, 67, 75, 69, 84, 34, 79, 198, 5, 85, 50, 89, 159, 233, 30, 65, 4, 188, + 171, 8, 3, 68, 73, 84, 141, 216, 20, 7, 83, 67, 69, 78, 84, 32, 77, 5, + 129, 132, 27, 3, 32, 66, 65, 28, 84, 2, 83, 83, 232, 131, 30, 3, 67, 79, + 68, 208, 156, 1, 3, 73, 83, 83, 155, 60, 87, 22, 46, 32, 184, 2, 3, 69, + 68, 32, 223, 1, 73, 14, 44, 3, 79, 70, 32, 82, 80, 215, 233, 31, 77, 4, + 140, 190, 28, 6, 74, 69, 82, 85, 83, 65, 133, 210, 2, 5, 76, 79, 82, 82, + 65, 8, 76, 10, 65, 84, 84, 89, 32, 87, 73, 84, 72, 32, 41, 5, 79, 77, 77, + 69, 69, 4, 206, 165, 10, 82, 25, 3, 76, 69, 70, 5, 225, 161, 3, 11, 32, + 87, 73, 84, 72, 32, 72, 65, 76, 70, 45, 6, 220, 139, 25, 37, 78, 69, 71, + 65, 84, 73, 86, 69, 32, 83, 81, 85, 65, 82, 69, 68, 32, 76, 65, 84, 73, + 78, 32, 67, 65, 80, 73, 84, 65, 76, 32, 76, 69, 84, 84, 69, 82, 248, 226, + 2, 2, 70, 76, 177, 216, 2, 3, 83, 87, 79, 2, 157, 242, 23, 5, 78, 71, 32, + 76, 65, 4, 176, 206, 17, 3, 90, 69, 73, 235, 182, 14, 84, 6, 138, 222, + 19, 73, 141, 160, 7, 4, 83, 84, 65, 76, 206, 19, 154, 1, 66, 20, 8, 78, + 69, 73, 70, 79, 82, 77, 32, 154, 250, 1, 80, 114, 82, 180, 3, 2, 83, 84, + 150, 222, 15, 67, 161, 191, 13, 6, 84, 32, 79, 70, 32, 77, 2, 255, 177, + 5, 69, 170, 19, 176, 1, 13, 78, 85, 77, 69, 82, 73, 67, 32, 83, 73, 71, + 78, 32, 184, 20, 17, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 83, + 73, 71, 78, 32, 233, 1, 5, 83, 73, 71, 78, 32, 222, 1, 78, 69, 166, 2, + 70, 152, 3, 2, 78, 73, 154, 2, 79, 130, 3, 83, 151, 4, 84, 24, 68, 5, 73, + 71, 72, 84, 32, 153, 1, 7, 76, 65, 77, 73, 84, 69, 32, 16, 142, 14, 83, + 230, 113, 85, 238, 50, 71, 136, 56, 17, 86, 65, 82, 73, 65, 78, 84, 32, + 70, 79, 82, 77, 32, 85, 83, 83, 85, 166, 211, 26, 68, 199, 249, 1, 65, 8, + 176, 143, 21, 5, 79, 78, 69, 32, 84, 234, 160, 9, 70, 175, 14, 84, 58, + 48, 4, 73, 86, 69, 32, 125, 4, 79, 85, 82, 32, 26, 70, 83, 206, 1, 66, + 250, 12, 65, 82, 71, 138, 110, 85, 155, 190, 27, 68, 6, 182, 15, 72, 169, + 146, 19, 5, 73, 88, 84, 72, 83, 32, 150, 1, 66, 44, 18, 86, 65, 82, 73, + 65, 78, 84, 32, 70, 79, 82, 77, 32, 76, 73, 77, 77, 85, 206, 12, 65, 82, + 71, 26, 83, 242, 109, 85, 155, 190, 27, 68, 6, 204, 123, 3, 65, 78, 50, + 235, 224, 26, 85, 9, 206, 203, 12, 32, 187, 210, 19, 52, 24, 44, 4, 71, + 73, 68, 65, 33, 3, 78, 69, 32, 4, 170, 178, 30, 69, 183, 107, 77, 20, + 156, 1, 19, 86, 65, 82, 73, 65, 78, 84, 32, 70, 79, 82, 77, 32, 73, 76, + 73, 77, 77, 85, 174, 7, 83, 230, 113, 85, 238, 50, 71, 174, 139, 27, 68, + 199, 249, 1, 65, 9, 186, 153, 32, 32, 186, 2, 51, 3, 52, 28, 92, 16, 76, + 68, 32, 65, 83, 83, 89, 82, 73, 65, 78, 32, 79, 78, 69, 32, 37, 3, 78, + 69, 32, 4, 170, 196, 18, 83, 219, 208, 11, 81, 24, 166, 1, 69, 52, 8, 81, + 85, 65, 82, 84, 69, 82, 32, 206, 7, 66, 54, 71, 84, 5, 84, 72, 73, 82, + 68, 216, 201, 24, 2, 83, 72, 237, 205, 6, 6, 72, 65, 76, 70, 32, 71, 4, + 158, 8, 83, 205, 164, 1, 5, 73, 71, 72, 84, 72, 4, 226, 173, 30, 65, 183, + 114, 71, 38, 144, 1, 5, 69, 86, 69, 78, 32, 188, 1, 20, 72, 65, 82, 50, + 32, 84, 73, 77, 69, 83, 32, 71, 65, 76, 32, 80, 76, 85, 83, 32, 37, 3, + 73, 88, 32, 18, 148, 1, 17, 86, 65, 82, 73, 65, 78, 84, 32, 70, 79, 82, + 77, 32, 73, 77, 73, 78, 218, 1, 83, 230, 113, 85, 238, 50, 71, 174, 139, + 27, 68, 199, 249, 1, 65, 6, 230, 195, 12, 32, 187, 210, 19, 51, 4, 174, + 177, 28, 68, 251, 228, 2, 77, 16, 142, 1, 83, 142, 3, 65, 218, 110, 85, + 238, 50, 71, 224, 245, 26, 16, 86, 65, 82, 73, 65, 78, 84, 32, 70, 79, + 82, 77, 32, 65, 83, 72, 207, 21, 68, 2, 227, 61, 72, 50, 52, 5, 72, 82, + 69, 69, 32, 241, 1, 3, 87, 79, 32, 28, 142, 1, 66, 40, 4, 83, 72, 65, 82, + 24, 16, 86, 65, 82, 73, 65, 78, 84, 32, 70, 79, 82, 77, 32, 69, 83, 72, + 118, 65, 82, 71, 163, 172, 28, 68, 6, 136, 112, 3, 85, 82, 85, 199, 10, + 65, 8, 226, 111, 50, 3, 85, 4, 146, 231, 24, 49, 203, 3, 50, 22, 82, 65, + 30, 66, 32, 2, 69, 83, 22, 71, 26, 83, 61, 6, 84, 72, 73, 82, 68, 83, 4, + 225, 184, 1, 2, 83, 72, 4, 254, 120, 65, 167, 214, 26, 85, 2, 135, 234, + 24, 72, 4, 53, 3, 69, 83, 72, 4, 11, 72, 4, 17, 2, 65, 82, 4, 178, 144, + 32, 50, 3, 85, 4, 11, 32, 4, 140, 199, 27, 12, 86, 65, 82, 73, 65, 78, + 84, 32, 70, 79, 82, 77, 179, 100, 68, 10, 160, 1, 9, 68, 73, 65, 71, 79, + 78, 65, 76, 32, 144, 210, 25, 6, 86, 69, 82, 84, 73, 67, 145, 130, 3, 14, + 79, 76, 68, 32, 65, 83, 83, 89, 82, 73, 65, 78, 32, 87, 6, 144, 191, 28, + 2, 84, 82, 244, 254, 2, 4, 81, 85, 65, 68, 15, 67, 194, 17, 202, 1, 65, + 186, 15, 66, 170, 5, 68, 190, 15, 69, 202, 10, 71, 182, 30, 72, 238, 3, + 73, 238, 4, 75, 154, 20, 76, 134, 37, 77, 134, 6, 78, 226, 17, 80, 210, + 3, 82, 42, 83, 238, 19, 84, 186, 8, 85, 207, 20, 90, 141, 1, 160, 1, 7, + 32, 84, 73, 77, 69, 83, 32, 142, 1, 66, 250, 3, 68, 38, 75, 90, 76, 212, + 1, 3, 77, 65, 82, 78, 78, 134, 2, 82, 42, 83, 190, 128, 31, 80, 215, 127, + 50, 16, 144, 173, 1, 5, 76, 65, 71, 65, 82, 226, 23, 71, 254, 245, 12, + 73, 226, 192, 13, 77, 162, 163, 2, 83, 194, 165, 1, 66, 246, 67, 72, 187, + 2, 65, 45, 22, 32, 219, 2, 50, 28, 48, 6, 84, 73, 77, 69, 83, 32, 139, + 203, 1, 71, 26, 180, 1, 2, 71, 65, 38, 73, 36, 2, 83, 72, 128, 113, 8, + 85, 32, 80, 76, 85, 83, 32, 85, 188, 56, 4, 68, 85, 78, 51, 166, 2, 65, + 204, 2, 3, 78, 85, 78, 242, 27, 76, 179, 186, 30, 72, 4, 194, 210, 1, 78, + 139, 181, 30, 76, 4, 210, 169, 1, 71, 131, 222, 29, 77, 4, 194, 223, 24, + 85, 187, 188, 5, 69, 15, 37, 7, 32, 84, 73, 77, 69, 83, 32, 12, 206, 133, + 1, 77, 130, 59, 71, 194, 9, 83, 234, 10, 84, 200, 197, 28, 2, 66, 65, + 251, 235, 1, 65, 5, 249, 51, 5, 32, 84, 73, 77, 69, 7, 37, 7, 32, 84, 73, + 77, 69, 83, 32, 4, 252, 27, 5, 83, 72, 73, 84, 65, 215, 80, 69, 23, 68, + 7, 32, 84, 73, 77, 69, 83, 32, 158, 209, 25, 69, 155, 227, 5, 65, 16, + 102, 75, 156, 173, 1, 2, 68, 73, 198, 241, 26, 71, 198, 249, 1, 85, 186, + 95, 65, 198, 94, 83, 215, 42, 72, 4, 130, 158, 1, 65, 171, 229, 30, 73, + 7, 37, 7, 32, 84, 73, 77, 69, 83, 32, 4, 218, 249, 3, 75, 251, 219, 27, + 83, 13, 26, 32, 151, 213, 31, 83, 8, 128, 1, 10, 80, 76, 85, 83, 32, 78, + 65, 71, 65, 32, 248, 187, 8, 7, 84, 72, 82, 69, 69, 32, 84, 193, 255, 5, + 4, 79, 86, 69, 82, 4, 144, 140, 1, 16, 79, 80, 80, 79, 83, 73, 78, 71, + 32, 65, 78, 32, 80, 76, 85, 83, 139, 183, 23, 83, 6, 200, 49, 2, 65, 68, + 203, 153, 18, 75, 18, 26, 72, 139, 165, 12, 65, 17, 42, 32, 166, 202, 18, + 71, 167, 181, 13, 50, 10, 68, 9, 79, 86, 69, 82, 32, 65, 83, 72, 32, 174, + 97, 90, 135, 112, 75, 6, 176, 1, 8, 79, 86, 69, 82, 32, 65, 83, 72, 157, + 143, 1, 29, 84, 85, 71, 50, 32, 79, 86, 69, 82, 32, 84, 85, 71, 50, 32, + 84, 85, 71, 50, 32, 79, 86, 69, 82, 32, 84, 85, 71, 50, 5, 149, 145, 1, + 27, 32, 67, 82, 79, 83, 83, 73, 78, 71, 32, 65, 83, 72, 32, 79, 86, 69, + 82, 32, 65, 83, 72, 32, 79, 86, 69, 82, 52, 30, 65, 158, 2, 73, 95, 85, + 27, 66, 68, 44, 4, 72, 65, 82, 50, 90, 76, 66, 82, 203, 255, 27, 71, 5, + 145, 208, 1, 6, 32, 84, 73, 77, 69, 83, 9, 37, 7, 32, 84, 73, 77, 69, 83, + 32, 6, 238, 164, 1, 65, 206, 194, 30, 78, 163, 17, 90, 7, 132, 239, 30, + 7, 32, 79, 86, 69, 82, 32, 66, 139, 139, 1, 65, 5, 227, 210, 24, 65, 9, + 37, 7, 32, 84, 73, 77, 69, 83, 32, 6, 134, 156, 1, 73, 166, 186, 29, 71, + 191, 163, 1, 65, 19, 50, 32, 168, 1, 3, 76, 85, 71, 239, 169, 1, 82, 8, + 88, 8, 79, 86, 69, 82, 32, 66, 85, 32, 201, 195, 27, 8, 67, 82, 79, 83, + 83, 73, 78, 71, 6, 160, 199, 12, 7, 84, 73, 77, 69, 83, 32, 78, 138, 243, + 17, 65, 155, 110, 85, 5, 197, 238, 3, 8, 32, 79, 86, 69, 82, 32, 66, 85, + 180, 1, 34, 65, 222, 5, 73, 195, 2, 85, 67, 50, 71, 150, 5, 82, 146, 49, + 32, 167, 192, 31, 77, 55, 26, 32, 175, 246, 31, 51, 50, 76, 13, 75, 73, + 83, 73, 77, 53, 32, 84, 73, 77, 69, 83, 32, 227, 198, 1, 84, 48, 146, 1, + 65, 30, 66, 38, 71, 112, 2, 73, 82, 42, 76, 94, 85, 182, 131, 1, 80, 162, + 61, 84, 186, 248, 27, 75, 182, 162, 2, 78, 254, 2, 83, 163, 17, 72, 4, + 110, 32, 159, 208, 30, 77, 4, 178, 136, 30, 65, 251, 235, 1, 73, 10, 34, + 65, 58, 73, 143, 173, 31, 85, 5, 11, 32, 2, 193, 136, 30, 6, 80, 76, 85, + 83, 32, 77, 5, 235, 203, 24, 82, 5, 129, 134, 14, 5, 32, 80, 76, 85, 83, + 8, 26, 85, 215, 242, 31, 65, 7, 160, 151, 1, 7, 32, 80, 76, 85, 83, 32, + 77, 179, 219, 30, 77, 6, 52, 7, 50, 32, 80, 76, 85, 83, 32, 207, 240, 31, + 83, 4, 128, 27, 2, 71, 73, 199, 235, 29, 77, 7, 187, 194, 12, 65, 25, 54, + 77, 150, 1, 78, 76, 2, 83, 72, 139, 239, 31, 66, 13, 44, 7, 32, 84, 73, + 77, 69, 83, 32, 59, 50, 6, 220, 69, 2, 85, 32, 238, 218, 13, 73, 227, + 162, 17, 83, 5, 205, 158, 13, 6, 32, 84, 73, 77, 69, 83, 5, 177, 241, 18, + 14, 32, 75, 65, 83, 75, 65, 76, 32, 85, 32, 71, 85, 78, 85, 5, 245, 157, + 1, 5, 32, 80, 76, 85, 83, 91, 70, 32, 66, 66, 94, 71, 234, 4, 78, 214, + 192, 24, 82, 195, 167, 7, 72, 6, 154, 176, 1, 71, 170, 3, 83, 181, 203, + 12, 4, 79, 86, 69, 82, 9, 52, 7, 32, 84, 73, 77, 69, 83, 32, 163, 237, + 31, 50, 4, 250, 145, 1, 69, 155, 174, 30, 83, 61, 52, 7, 32, 84, 73, 77, + 69, 83, 32, 167, 166, 31, 85, 56, 122, 65, 58, 68, 30, 71, 74, 75, 90, + 76, 110, 77, 50, 83, 130, 80, 69, 218, 58, 73, 182, 200, 16, 72, 214, + 129, 14, 78, 3, 80, 6, 32, 2, 83, 72, 143, 190, 31, 78, 5, 251, 212, 14, + 32, 4, 174, 155, 31, 73, 3, 85, 8, 26, 73, 219, 234, 31, 65, 7, 140, 141, + 1, 2, 82, 50, 187, 220, 30, 83, 8, 26, 85, 211, 185, 1, 65, 6, 40, 4, 83, + 72, 85, 50, 231, 233, 31, 82, 5, 175, 26, 32, 8, 26, 65, 45, 2, 85, 72, + 6, 202, 26, 77, 221, 165, 24, 3, 75, 45, 48, 2, 225, 57, 5, 32, 80, 76, + 85, 83, 6, 222, 253, 29, 65, 174, 173, 1, 69, 223, 61, 73, 4, 238, 138, + 1, 73, 247, 198, 30, 72, 11, 26, 51, 251, 231, 31, 52, 7, 143, 9, 32, + 117, 130, 1, 32, 90, 50, 210, 1, 78, 202, 1, 82, 60, 3, 83, 72, 50, 52, + 3, 90, 69, 78, 166, 227, 18, 71, 142, 255, 11, 68, 215, 127, 76, 4, 168, + 25, 11, 79, 86, 69, 82, 32, 69, 32, 78, 85, 78, 32, 253, 94, 4, 84, 73, + 77, 69, 19, 37, 7, 32, 84, 73, 77, 69, 83, 32, 16, 134, 1, 83, 144, 168, + 1, 10, 65, 32, 80, 76, 85, 83, 32, 72, 65, 32, 146, 153, 29, 71, 182, 16, + 80, 182, 26, 75, 254, 100, 77, 219, 19, 85, 4, 138, 217, 30, 65, 255, + 116, 72, 15, 11, 32, 12, 96, 4, 67, 82, 79, 83, 0, 4, 79, 80, 80, 79, 36, + 6, 84, 73, 77, 69, 83, 32, 171, 165, 24, 83, 2, 245, 152, 14, 4, 83, 73, + 78, 71, 6, 204, 138, 1, 4, 71, 65, 78, 50, 135, 194, 30, 77, 6, 36, 3, + 73, 78, 50, 239, 146, 31, 69, 5, 139, 212, 30, 32, 5, 229, 16, 9, 32, 67, + 82, 79, 83, 83, 73, 78, 71, 63, 11, 32, 60, 96, 14, 83, 72, 69, 83, 72, + 73, 71, 32, 84, 73, 77, 69, 83, 32, 97, 6, 84, 73, 77, 69, 83, 32, 16, + 178, 131, 1, 73, 202, 220, 2, 77, 146, 150, 26, 65, 220, 110, 2, 76, 65, + 198, 87, 83, 147, 17, 72, 44, 134, 1, 65, 68, 5, 68, 85, 78, 51, 32, 26, + 75, 58, 76, 62, 83, 34, 85, 222, 127, 73, 204, 31, 2, 72, 65, 166, 144, + 29, 71, 199, 103, 66, 9, 244, 86, 9, 32, 80, 76, 85, 83, 32, 76, 65, 76, + 183, 136, 31, 78, 4, 217, 32, 2, 71, 85, 6, 184, 160, 24, 5, 65, 83, 75, + 65, 76, 187, 195, 3, 85, 8, 34, 65, 246, 221, 31, 73, 3, 85, 5, 201, 85, + 2, 76, 32, 4, 134, 199, 31, 72, 215, 22, 85, 4, 186, 221, 31, 50, 3, 68, + 160, 2, 42, 65, 166, 19, 69, 122, 73, 135, 5, 85, 191, 1, 114, 50, 144, + 16, 2, 66, 65, 94, 68, 22, 76, 38, 78, 206, 140, 1, 32, 154, 6, 82, 218, + 162, 28, 83, 155, 149, 2, 77, 153, 1, 11, 32, 150, 1, 68, 6, 84, 73, 77, + 69, 83, 32, 153, 128, 1, 5, 79, 86, 69, 82, 32, 148, 1, 202, 1, 65, 162, + 2, 66, 154, 1, 68, 178, 1, 69, 58, 71, 178, 1, 72, 230, 1, 73, 70, 75, + 194, 1, 76, 62, 77, 50, 78, 130, 1, 83, 142, 1, 85, 222, 154, 1, 84, 128, + 138, 23, 3, 90, 73, 90, 139, 165, 7, 80, 18, 132, 1, 6, 32, 80, 76, 85, + 83, 32, 50, 78, 52, 2, 83, 72, 189, 162, 18, 14, 66, 50, 32, 84, 69, 78, + 85, 32, 80, 76, 85, 83, 32, 84, 6, 202, 58, 68, 142, 206, 13, 73, 183, + 205, 17, 72, 5, 185, 61, 9, 32, 80, 76, 85, 83, 32, 75, 65, 75, 7, 11, + 50, 5, 133, 90, 6, 32, 80, 76, 85, 83, 32, 10, 26, 65, 77, 2, 85, 82, 6, + 42, 72, 44, 2, 82, 32, 187, 214, 31, 68, 2, 11, 65, 2, 151, 175, 24, 82, + 5, 11, 32, 2, 221, 139, 31, 4, 80, 76, 85, 83, 14, 34, 73, 54, 85, 191, + 213, 31, 65, 7, 17, 2, 77, 32, 4, 146, 22, 84, 207, 129, 1, 71, 6, 56, 8, + 71, 32, 84, 73, 77, 69, 83, 32, 131, 213, 31, 66, 4, 158, 119, 73, 151, + 45, 75, 10, 38, 78, 254, 2, 76, 191, 251, 29, 82, 5, 243, 28, 32, 18, 18, + 65, 99, 73, 11, 26, 82, 247, 158, 1, 78, 7, 33, 6, 32, 80, 76, 85, 83, + 32, 4, 130, 189, 31, 78, 255, 2, 68, 9, 174, 122, 52, 253, 234, 12, 7, + 82, 50, 32, 80, 76, 85, 83, 12, 62, 65, 154, 124, 85, 233, 232, 12, 6, + 73, 32, 80, 76, 85, 83, 8, 40, 6, 32, 80, 76, 85, 83, 32, 83, 76, 4, 48, + 6, 76, 85, 32, 80, 76, 85, 223, 209, 31, 65, 2, 11, 83, 2, 215, 97, 32, + 5, 149, 228, 13, 5, 32, 80, 76, 85, 83, 4, 176, 100, 10, 83, 72, 32, 80, + 76, 85, 83, 32, 72, 85, 147, 15, 71, 12, 34, 65, 36, 2, 73, 68, 27, 85, + 4, 158, 212, 24, 83, 147, 252, 6, 75, 5, 225, 77, 2, 32, 80, 4, 60, 5, + 83, 72, 85, 50, 32, 197, 127, 5, 51, 32, 80, 76, 85, 2, 205, 158, 1, 3, + 80, 76, 85, 8, 26, 65, 235, 205, 31, 85, 7, 11, 77, 5, 227, 159, 1, 32, + 6, 250, 77, 69, 214, 149, 29, 85, 139, 235, 1, 73, 10, 26, 69, 73, 2, 85, + 78, 7, 33, 6, 32, 80, 76, 85, 83, 32, 4, 166, 166, 24, 69, 235, 147, 7, + 71, 5, 11, 32, 2, 187, 101, 79, 14, 42, 72, 178, 223, 23, 65, 247, 220, + 7, 85, 8, 18, 69, 51, 73, 5, 145, 212, 30, 7, 32, 80, 76, 85, 83, 32, 84, + 4, 182, 204, 31, 68, 3, 77, 7, 11, 68, 5, 237, 220, 13, 5, 32, 80, 76, + 85, 83, 7, 11, 32, 4, 130, 237, 29, 82, 177, 177, 1, 11, 67, 82, 79, 83, + 83, 73, 78, 71, 32, 71, 65, 5, 191, 131, 1, 32, 7, 134, 131, 1, 32, 155, + 183, 30, 65, 11, 11, 50, 9, 11, 32, 6, 80, 8, 67, 82, 79, 83, 83, 73, 78, + 71, 0, 4, 79, 86, 69, 82, 163, 195, 26, 84, 2, 197, 49, 3, 32, 71, 65, 8, + 44, 5, 83, 72, 84, 73, 78, 223, 161, 24, 50, 7, 37, 7, 32, 84, 73, 77, + 69, 83, 32, 4, 158, 208, 30, 75, 215, 120, 85, 49, 70, 32, 94, 52, 114, + 82, 190, 1, 83, 182, 194, 30, 68, 211, 130, 1, 71, 6, 152, 253, 8, 6, 84, + 73, 77, 69, 83, 32, 249, 250, 4, 8, 67, 82, 79, 83, 83, 73, 78, 71, 7, + 11, 32, 4, 64, 8, 67, 82, 79, 83, 83, 73, 78, 71, 1, 4, 79, 86, 69, 82, + 2, 249, 157, 24, 3, 32, 71, 73, 16, 26, 51, 147, 136, 1, 50, 13, 37, 7, + 32, 84, 73, 77, 69, 83, 32, 10, 70, 65, 0, 2, 76, 85, 206, 127, 71, 254, + 245, 12, 73, 183, 205, 17, 80, 2, 197, 245, 13, 7, 32, 80, 76, 85, 83, + 32, 73, 14, 26, 72, 155, 185, 30, 65, 13, 11, 32, 10, 22, 84, 247, 20, + 67, 8, 44, 5, 73, 77, 69, 83, 32, 163, 159, 31, 69, 6, 192, 20, 6, 71, + 73, 83, 72, 32, 67, 146, 126, 84, 151, 235, 29, 66, 43, 110, 50, 174, 1, + 68, 218, 1, 77, 54, 82, 152, 242, 13, 9, 32, 67, 82, 79, 83, 83, 73, 78, + 71, 147, 205, 17, 76, 15, 11, 32, 12, 48, 6, 84, 73, 77, 69, 83, 32, 167, + 132, 1, 71, 10, 252, 24, 3, 75, 65, 75, 194, 75, 73, 152, 20, 10, 83, 65, + 76, 32, 80, 76, 85, 83, 32, 84, 155, 201, 29, 78, 11, 11, 32, 8, 128, 1, + 10, 80, 76, 85, 83, 32, 71, 73, 83, 72, 32, 16, 6, 84, 73, 77, 69, 83, + 32, 177, 66, 8, 79, 86, 69, 82, 32, 71, 85, 68, 2, 135, 121, 84, 4, 172, + 145, 1, 5, 65, 32, 80, 76, 85, 147, 182, 29, 75, 5, 17, 2, 32, 84, 2, + 241, 42, 4, 73, 77, 69, 83, 9, 26, 85, 143, 191, 31, 55, 4, 250, 189, 31, + 83, 147, 1, 78, 50, 30, 65, 82, 73, 215, 1, 85, 11, 26, 32, 179, 190, 31, + 76, 6, 32, 2, 84, 69, 147, 128, 1, 71, 4, 235, 127, 78, 23, 37, 7, 32, + 84, 73, 77, 69, 83, 32, 20, 104, 3, 65, 83, 72, 158, 216, 27, 68, 158, + 228, 2, 78, 94, 75, 170, 57, 66, 2, 71, 162, 25, 83, 143, 45, 85, 7, 224, + 55, 8, 32, 79, 86, 69, 82, 32, 72, 73, 135, 133, 31, 50, 19, 48, 2, 66, + 50, 182, 148, 24, 76, 179, 166, 7, 83, 13, 37, 7, 32, 84, 73, 77, 69, 83, + 32, 10, 254, 138, 1, 75, 130, 204, 26, 76, 242, 216, 2, 72, 254, 59, 65, + 195, 9, 85, 49, 104, 3, 68, 73, 77, 94, 71, 214, 1, 76, 62, 77, 242, 180, + 31, 32, 170, 1, 83, 146, 1, 66, 2, 78, 3, 82, 7, 53, 11, 32, 79, 86, 69, + 82, 32, 73, 68, 73, 77, 32, 4, 218, 251, 23, 83, 175, 197, 6, 66, 13, 11, + 73, 11, 11, 32, 8, 162, 123, 71, 204, 236, 11, 31, 79, 86, 69, 82, 32, + 73, 71, 73, 32, 83, 72, 73, 82, 32, 79, 86, 69, 82, 32, 83, 72, 73, 82, + 32, 85, 68, 32, 79, 86, 69, 82, 174, 147, 17, 68, 175, 170, 1, 82, 7, 26, + 32, 203, 183, 31, 50, 2, 169, 72, 4, 84, 73, 77, 69, 13, 26, 32, 175, + 231, 30, 73, 8, 76, 4, 67, 82, 79, 83, 0, 4, 79, 80, 80, 79, 162, 111, + 84, 159, 137, 23, 83, 2, 233, 179, 30, 5, 83, 73, 78, 71, 32, 224, 1, 78, + 65, 146, 15, 73, 242, 1, 85, 158, 73, 69, 245, 175, 23, 4, 87, 85, 51, + 49, 177, 1, 164, 1, 7, 32, 84, 73, 77, 69, 83, 32, 210, 9, 50, 66, 68, + 102, 75, 50, 76, 98, 77, 28, 4, 83, 75, 65, 76, 184, 199, 15, 6, 80, 32, + 69, 76, 65, 77, 191, 224, 15, 66, 134, 1, 170, 1, 65, 94, 66, 82, 69, 30, + 71, 182, 2, 73, 34, 75, 34, 76, 38, 77, 170, 1, 83, 118, 84, 34, 85, 214, + 8, 72, 246, 51, 78, 214, 217, 16, 80, 198, 240, 13, 82, 147, 17, 90, 13, + 46, 68, 158, 133, 31, 78, 165, 44, 2, 83, 72, 5, 229, 50, 7, 32, 80, 76, + 85, 83, 32, 75, 10, 34, 65, 230, 177, 31, 73, 3, 85, 6, 246, 197, 29, 76, + 238, 235, 1, 68, 3, 82, 4, 138, 25, 82, 151, 61, 83, 26, 30, 65, 98, 73, + 147, 1, 85, 11, 38, 82, 206, 123, 78, 139, 181, 30, 76, 5, 249, 21, 10, + 32, 80, 76, 85, 83, 32, 83, 72, 65, 51, 11, 32, 2, 83, 72, 187, 136, 24, 82, 7, 11, 32, 4, 26, 67, 255, 130, 1, 80, 2, 37, 7, 82, 79, 83, 83, 73, - 78, 71, 2, 137, 185, 27, 2, 32, 71, 7, 182, 164, 27, 82, 179, 245, 3, 68, - 4, 234, 133, 31, 71, 219, 19, 77, 8, 250, 8, 73, 195, 164, 17, 65, 6, - 218, 163, 12, 85, 171, 245, 18, 73, 12, 18, 69, 83, 73, 9, 33, 6, 32, 80, - 76, 85, 83, 32, 6, 190, 243, 30, 68, 150, 14, 84, 255, 2, 71, 5, 45, 9, - 32, 80, 76, 85, 83, 32, 78, 85, 78, 2, 159, 244, 26, 85, 18, 62, 72, 178, - 196, 27, 65, 244, 217, 2, 2, 85, 72, 131, 120, 73, 10, 202, 154, 30, 85, - 142, 54, 73, 162, 70, 65, 3, 69, 4, 230, 221, 30, 65, 223, 56, 85, 17, - 110, 32, 238, 92, 82, 180, 142, 26, 9, 77, 85, 77, 32, 84, 73, 77, 69, - 83, 134, 169, 4, 83, 146, 1, 50, 3, 68, 2, 235, 137, 21, 85, 5, 197, 248, - 11, 11, 32, 67, 82, 79, 83, 83, 73, 78, 71, 32, 75, 10, 42, 53, 182, 148, - 31, 50, 2, 51, 3, 52, 5, 129, 249, 23, 9, 32, 79, 86, 69, 82, 32, 75, 65, - 68, 5, 189, 75, 8, 32, 84, 73, 77, 69, 83, 32, 73, 9, 26, 32, 219, 130, - 31, 65, 4, 166, 105, 84, 249, 187, 23, 9, 67, 82, 79, 83, 83, 73, 78, 71, - 32, 4, 234, 146, 31, 50, 3, 52, 7, 11, 32, 4, 70, 76, 1, 13, 79, 86, 69, + 78, 71, 2, 217, 202, 27, 2, 32, 71, 7, 134, 181, 27, 82, 135, 250, 3, 68, + 4, 142, 155, 31, 71, 219, 19, 77, 8, 250, 8, 73, 187, 166, 17, 65, 6, + 202, 169, 12, 85, 223, 132, 19, 73, 12, 18, 69, 83, 73, 9, 33, 6, 32, 80, + 76, 85, 83, 32, 6, 226, 136, 31, 68, 150, 14, 84, 255, 2, 71, 5, 45, 9, + 32, 80, 76, 85, 83, 32, 78, 85, 78, 2, 199, 206, 29, 85, 18, 62, 72, 130, + 214, 27, 65, 200, 221, 2, 2, 85, 72, 131, 120, 73, 10, 238, 175, 30, 85, + 142, 54, 73, 162, 70, 65, 3, 69, 4, 138, 243, 30, 65, 223, 56, 85, 17, + 110, 32, 238, 92, 82, 252, 154, 26, 9, 77, 85, 77, 32, 84, 73, 77, 69, + 83, 226, 177, 4, 83, 146, 1, 50, 3, 68, 2, 131, 144, 21, 85, 5, 193, 249, + 11, 11, 32, 67, 82, 79, 83, 83, 73, 78, 71, 32, 75, 10, 42, 53, 218, 169, + 31, 50, 2, 51, 3, 52, 5, 145, 128, 24, 9, 32, 79, 86, 69, 82, 32, 75, 65, + 68, 5, 189, 75, 8, 32, 84, 73, 77, 69, 83, 32, 73, 9, 26, 32, 255, 151, + 31, 65, 4, 166, 105, 84, 157, 195, 23, 9, 67, 82, 79, 83, 83, 73, 78, 71, + 32, 4, 142, 168, 31, 50, 3, 52, 7, 11, 32, 4, 70, 76, 1, 13, 79, 86, 69, 82, 32, 75, 65, 83, 75, 65, 76, 32, 76, 2, 173, 116, 24, 65, 71, 65, 66, 32, 84, 73, 77, 69, 83, 32, 85, 32, 79, 86, 69, 82, 32, 76, 65, 71, 65, - 66, 32, 21, 68, 7, 32, 84, 73, 77, 69, 83, 32, 50, 83, 150, 144, 31, 68, - 3, 78, 6, 26, 85, 255, 201, 30, 66, 5, 167, 144, 31, 68, 8, 52, 3, 73, - 77, 53, 170, 134, 30, 65, 183, 137, 1, 72, 5, 169, 244, 23, 11, 32, 79, + 66, 32, 21, 68, 7, 32, 84, 73, 77, 69, 83, 32, 50, 83, 186, 165, 31, 68, + 3, 78, 6, 26, 85, 163, 223, 30, 66, 5, 203, 165, 31, 68, 8, 52, 3, 73, + 77, 53, 178, 153, 30, 65, 211, 139, 1, 72, 5, 185, 251, 23, 11, 32, 79, 86, 69, 82, 32, 75, 73, 83, 73, 77, 25, 200, 1, 29, 32, 79, 86, 69, 82, 32, 72, 73, 32, 84, 73, 77, 69, 83, 32, 65, 83, 72, 50, 32, 75, 85, 32, - 79, 86, 69, 82, 32, 72, 18, 52, 54, 82, 186, 98, 83, 230, 1, 76, 242, - 168, 30, 51, 2, 55, 3, 78, 2, 155, 71, 73, 5, 221, 142, 30, 8, 32, 86, + 79, 86, 69, 82, 32, 72, 18, 52, 54, 82, 186, 98, 83, 230, 1, 76, 150, + 190, 30, 51, 2, 55, 3, 78, 2, 155, 71, 73, 5, 129, 164, 30, 8, 32, 86, 65, 82, 73, 65, 78, 84, 5, 213, 115, 9, 32, 79, 80, 80, 79, 83, 73, 78, 71, 240, 2, 30, 65, 174, 26, 73, 59, 85, 141, 2, 68, 2, 71, 65, 252, 12, 2, 75, 45, 222, 11, 76, 46, 77, 135, 55, 72, 118, 22, 66, 131, 11, 82, - 109, 11, 32, 106, 48, 6, 84, 73, 77, 69, 83, 32, 167, 219, 23, 83, 104, + 109, 11, 32, 106, 48, 6, 84, 73, 77, 69, 83, 32, 183, 226, 23, 83, 104, 190, 1, 65, 186, 1, 66, 34, 71, 70, 72, 66, 73, 94, 75, 118, 76, 46, 77, - 46, 83, 138, 2, 84, 126, 85, 188, 165, 4, 8, 90, 85, 32, 79, 86, 69, 82, - 32, 214, 186, 25, 68, 218, 81, 69, 143, 57, 78, 15, 80, 6, 32, 80, 76, - 85, 83, 32, 76, 4, 83, 72, 32, 90, 178, 136, 31, 76, 3, 78, 6, 38, 68, - 158, 231, 29, 71, 251, 23, 76, 2, 201, 62, 5, 65, 32, 80, 76, 85, 2, 149, - 112, 2, 73, 68, 4, 246, 193, 30, 65, 163, 70, 73, 10, 48, 2, 85, 68, 254, - 180, 27, 65, 203, 210, 3, 73, 5, 191, 105, 32, 6, 164, 238, 17, 7, 73, - 32, 84, 73, 77, 69, 83, 227, 156, 12, 65, 8, 22, 77, 175, 62, 71, 7, 33, - 6, 32, 80, 76, 85, 83, 32, 4, 206, 225, 30, 76, 179, 34, 72, 10, 26, 85, - 251, 138, 29, 73, 6, 26, 76, 227, 133, 31, 51, 5, 41, 8, 32, 80, 76, 85, - 83, 32, 72, 73, 2, 219, 65, 32, 8, 198, 100, 65, 190, 184, 28, 73, 247, - 107, 85, 6, 26, 69, 191, 156, 29, 85, 5, 175, 25, 32, 12, 26, 72, 219, - 243, 30, 85, 10, 100, 14, 73, 84, 65, 32, 80, 76, 85, 83, 32, 71, 73, 83, + 46, 83, 138, 2, 84, 126, 85, 232, 167, 4, 8, 90, 85, 32, 79, 86, 69, 82, + 32, 202, 203, 25, 68, 222, 83, 69, 143, 57, 78, 15, 80, 6, 32, 80, 76, + 85, 83, 32, 76, 4, 83, 72, 32, 90, 214, 157, 31, 76, 3, 78, 6, 38, 68, + 190, 250, 29, 71, 227, 23, 76, 2, 201, 62, 5, 65, 32, 80, 76, 85, 2, 149, + 112, 2, 73, 68, 4, 154, 215, 30, 65, 163, 70, 73, 10, 48, 2, 85, 68, 206, + 198, 27, 65, 159, 214, 3, 73, 5, 191, 105, 32, 6, 128, 240, 17, 7, 73, + 32, 84, 73, 77, 69, 83, 171, 176, 12, 65, 8, 22, 77, 175, 62, 71, 7, 33, + 6, 32, 80, 76, 85, 83, 32, 4, 242, 246, 30, 76, 179, 34, 72, 10, 26, 85, + 183, 157, 29, 73, 6, 26, 76, 135, 155, 31, 51, 5, 41, 8, 32, 80, 76, 85, + 83, 32, 72, 73, 2, 219, 65, 32, 8, 198, 100, 65, 250, 202, 28, 73, 223, + 110, 85, 6, 26, 69, 251, 174, 29, 85, 5, 175, 25, 32, 12, 26, 72, 255, + 136, 31, 85, 10, 100, 14, 73, 84, 65, 32, 80, 76, 85, 83, 32, 71, 73, 83, 72, 32, 96, 2, 85, 50, 217, 3, 2, 69, 32, 4, 48, 6, 80, 76, 85, 83, 32, - 69, 163, 133, 26, 84, 2, 11, 82, 2, 11, 73, 2, 207, 233, 23, 78, 5, 189, + 69, 243, 145, 26, 84, 2, 11, 82, 2, 11, 73, 2, 223, 240, 23, 78, 5, 189, 73, 5, 32, 80, 76, 85, 83, 6, 86, 65, 189, 30, 16, 69, 32, 80, 76, 85, - 83, 32, 65, 32, 80, 76, 85, 83, 32, 83, 85, 4, 154, 231, 23, 75, 211, - 154, 7, 71, 13, 72, 6, 32, 80, 76, 85, 83, 32, 190, 41, 50, 178, 214, 30, - 83, 147, 1, 68, 4, 26, 85, 227, 128, 31, 65, 2, 255, 40, 32, 11, 11, 32, + 83, 32, 65, 32, 80, 76, 85, 83, 32, 83, 85, 4, 170, 238, 23, 75, 231, + 168, 7, 71, 13, 72, 6, 32, 80, 76, 85, 83, 32, 190, 41, 50, 214, 235, 30, + 83, 147, 1, 68, 4, 26, 85, 135, 150, 31, 65, 2, 255, 40, 32, 11, 11, 32, 8, 68, 4, 71, 85, 78, 85, 97, 9, 84, 73, 77, 69, 83, 32, 83, 72, 69, 5, 73, 16, 32, 79, 86, 69, 82, 32, 76, 65, 71, 65, 82, 32, 71, 85, 78, 85, - 2, 151, 210, 30, 32, 5, 11, 32, 2, 157, 196, 14, 4, 80, 76, 85, 83, 136, + 2, 187, 231, 30, 32, 5, 11, 32, 2, 201, 202, 14, 4, 80, 76, 85, 83, 136, 1, 82, 48, 146, 2, 49, 30, 50, 114, 51, 82, 52, 222, 2, 54, 154, 4, 55, 243, 24, 53, 22, 158, 1, 50, 30, 56, 180, 52, 15, 55, 57, 32, 79, 86, 69, - 82, 32, 76, 65, 75, 45, 48, 55, 57, 246, 245, 10, 53, 134, 185, 12, 54, - 2, 57, 94, 51, 203, 162, 3, 48, 4, 242, 252, 30, 49, 3, 53, 4, 136, 227, - 23, 12, 49, 32, 79, 86, 69, 82, 32, 76, 65, 75, 45, 48, 207, 153, 7, 48, - 4, 214, 226, 23, 52, 95, 51, 16, 46, 50, 38, 54, 222, 219, 23, 49, 159, - 2, 51, 6, 182, 251, 30, 48, 2, 53, 3, 56, 4, 146, 251, 30, 53, 3, 54, 12, - 42, 52, 254, 223, 11, 56, 131, 130, 12, 57, 6, 202, 250, 30, 51, 2, 55, - 3, 56, 30, 62, 52, 214, 1, 53, 30, 57, 170, 223, 23, 55, 203, 162, 3, 56, - 14, 26, 57, 207, 249, 30, 49, 13, 37, 7, 32, 84, 73, 77, 69, 83, 32, 10, - 116, 9, 80, 65, 80, 32, 80, 76, 85, 83, 32, 240, 151, 13, 7, 85, 50, 32, - 80, 76, 85, 83, 238, 31, 73, 235, 155, 17, 71, 4, 210, 13, 80, 51, 76, 4, - 146, 248, 30, 48, 3, 55, 8, 246, 247, 30, 48, 2, 50, 2, 51, 3, 53, 46, - 60, 2, 49, 55, 240, 1, 2, 52, 56, 130, 216, 23, 48, 23, 51, 23, 37, 7, + 82, 32, 76, 65, 75, 45, 48, 55, 57, 254, 246, 10, 53, 142, 191, 12, 54, + 2, 57, 94, 51, 139, 172, 3, 48, 4, 150, 146, 31, 49, 3, 53, 4, 152, 234, + 23, 12, 49, 32, 79, 86, 69, 82, 32, 76, 65, 75, 45, 48, 227, 167, 7, 48, + 4, 230, 233, 23, 52, 95, 51, 16, 46, 50, 38, 54, 238, 226, 23, 49, 159, + 2, 51, 6, 218, 144, 31, 48, 2, 53, 3, 56, 4, 182, 144, 31, 53, 3, 54, 12, + 42, 52, 250, 224, 11, 56, 151, 136, 12, 57, 6, 238, 143, 31, 51, 2, 55, + 3, 56, 30, 62, 52, 214, 1, 53, 30, 57, 186, 230, 23, 55, 139, 172, 3, 56, + 14, 26, 57, 243, 142, 31, 49, 13, 37, 7, 32, 84, 73, 77, 69, 83, 32, 10, + 116, 9, 80, 65, 80, 32, 80, 76, 85, 83, 32, 140, 158, 13, 7, 85, 50, 32, + 80, 76, 85, 83, 218, 31, 73, 135, 171, 17, 71, 4, 210, 13, 80, 51, 76, 4, + 182, 141, 31, 48, 3, 55, 8, 154, 141, 31, 48, 2, 50, 2, 51, 3, 53, 46, + 60, 2, 49, 55, 240, 1, 2, 52, 56, 146, 223, 23, 48, 23, 51, 23, 37, 7, 32, 84, 73, 77, 69, 83, 32, 20, 122, 84, 34, 85, 162, 11, 75, 132, 34, 9, - 68, 85, 78, 51, 32, 71, 85, 78, 85, 134, 224, 28, 65, 246, 161, 1, 66, - 247, 67, 76, 4, 138, 189, 30, 65, 223, 56, 69, 6, 178, 199, 14, 82, 150, - 174, 16, 50, 3, 68, 21, 37, 7, 32, 84, 73, 77, 69, 83, 32, 18, 154, 1, - 85, 220, 8, 4, 80, 65, 80, 32, 146, 45, 73, 196, 219, 12, 10, 83, 72, 69, - 83, 72, 32, 80, 76, 85, 83, 210, 132, 5, 68, 130, 202, 12, 78, 163, 17, - 71, 4, 194, 197, 14, 82, 151, 174, 16, 68, 4, 226, 216, 23, 50, 143, 165, + 68, 85, 78, 51, 32, 71, 85, 78, 85, 194, 242, 28, 65, 222, 164, 1, 66, + 247, 67, 76, 4, 174, 210, 30, 65, 223, 56, 69, 6, 222, 205, 14, 82, 142, + 189, 16, 50, 3, 68, 21, 37, 7, 32, 84, 73, 77, 69, 83, 32, 18, 154, 1, + 85, 220, 8, 4, 80, 65, 80, 32, 146, 45, 73, 224, 225, 12, 10, 83, 72, 69, + 83, 72, 32, 80, 76, 85, 83, 178, 128, 5, 68, 170, 221, 12, 78, 163, 17, + 71, 4, 238, 203, 14, 82, 143, 189, 16, 68, 4, 242, 223, 23, 50, 207, 174, 3, 52, 5, 11, 32, 2, 145, 6, 4, 84, 73, 77, 69, 7, 49, 10, 32, 84, 73, - 77, 69, 83, 32, 75, 85, 82, 5, 225, 206, 11, 5, 32, 80, 76, 85, 83, 9, - 204, 206, 11, 2, 77, 77, 170, 162, 19, 83, 147, 1, 76, 93, 90, 50, 212, - 7, 3, 71, 65, 76, 142, 1, 77, 130, 62, 32, 150, 170, 30, 51, 2, 72, 3, + 77, 69, 83, 32, 75, 85, 82, 5, 221, 207, 11, 5, 32, 80, 76, 85, 83, 9, + 200, 207, 11, 2, 77, 77, 210, 182, 19, 83, 147, 1, 76, 93, 90, 50, 212, + 7, 3, 71, 65, 76, 142, 1, 77, 130, 62, 32, 186, 191, 30, 51, 2, 72, 3, 76, 69, 11, 32, 66, 88, 4, 67, 82, 79, 83, 0, 4, 79, 80, 80, 79, 44, 4, - 71, 85, 78, 85, 38, 83, 35, 84, 2, 209, 204, 11, 6, 83, 73, 78, 71, 32, - 76, 2, 193, 24, 5, 32, 84, 73, 77, 69, 6, 250, 68, 72, 227, 250, 22, 81, - 54, 44, 5, 73, 77, 69, 83, 32, 251, 201, 30, 69, 52, 188, 1, 4, 69, 83, + 71, 85, 78, 85, 38, 83, 35, 84, 2, 205, 205, 11, 6, 83, 73, 78, 71, 32, + 76, 2, 193, 24, 5, 32, 84, 73, 77, 69, 6, 250, 68, 72, 243, 129, 23, 81, + 54, 44, 5, 73, 77, 69, 83, 32, 159, 223, 30, 69, 52, 188, 1, 4, 69, 83, 72, 50, 94, 72, 42, 75, 68, 2, 76, 65, 34, 77, 60, 3, 80, 65, 80, 118, - 83, 90, 84, 250, 56, 71, 158, 251, 7, 78, 178, 212, 18, 68, 198, 215, 2, - 65, 138, 67, 66, 215, 53, 73, 7, 11, 32, 4, 26, 80, 147, 239, 25, 84, 2, - 17, 2, 76, 85, 2, 173, 227, 29, 3, 83, 32, 76, 4, 184, 66, 2, 73, 32, - 227, 160, 29, 65, 8, 32, 2, 65, 68, 135, 236, 30, 73, 6, 226, 19, 51, - 163, 216, 30, 50, 4, 174, 20, 32, 219, 181, 17, 71, 2, 11, 69, 2, 11, 32, - 2, 241, 175, 13, 4, 80, 76, 85, 83, 5, 11, 32, 2, 33, 6, 80, 76, 85, 83, - 32, 80, 2, 45, 9, 65, 80, 32, 80, 76, 85, 83, 32, 76, 2, 159, 244, 26, - 85, 6, 26, 73, 147, 197, 30, 72, 4, 194, 18, 32, 181, 171, 26, 7, 75, 50, + 83, 90, 84, 250, 56, 71, 250, 250, 7, 78, 166, 230, 18, 68, 254, 216, 2, + 65, 166, 69, 66, 215, 53, 73, 7, 11, 32, 4, 26, 80, 227, 251, 25, 84, 2, + 17, 2, 76, 85, 2, 181, 246, 29, 3, 83, 32, 76, 4, 184, 66, 2, 73, 32, + 235, 179, 29, 65, 8, 32, 2, 65, 68, 171, 129, 31, 73, 6, 226, 19, 51, + 199, 237, 30, 50, 4, 174, 20, 32, 183, 183, 17, 71, 2, 11, 69, 2, 11, 32, + 2, 249, 181, 13, 4, 80, 76, 85, 83, 5, 11, 32, 2, 33, 6, 80, 76, 85, 83, + 32, 80, 2, 45, 9, 65, 80, 32, 80, 76, 85, 83, 32, 76, 2, 239, 132, 27, + 85, 6, 26, 73, 183, 218, 30, 72, 4, 194, 18, 32, 253, 183, 26, 7, 75, 50, 32, 80, 76, 85, 83, 4, 162, 53, 85, 139, 24, 65, 9, 11, 32, 6, 22, 79, 207, 67, 83, 4, 56, 7, 80, 80, 79, 83, 73, 78, 71, 1, 3, 86, 69, 82, 2, - 21, 3, 32, 76, 85, 2, 231, 222, 29, 71, 7, 45, 9, 32, 79, 86, 69, 82, 32, + 21, 3, 32, 76, 85, 2, 239, 241, 29, 71, 7, 45, 9, 32, 79, 86, 69, 82, 32, 76, 85, 77, 5, 187, 59, 32, 70, 34, 65, 70, 69, 22, 73, 71, 85, 17, 170, - 45, 32, 188, 1, 2, 83, 72, 202, 184, 30, 50, 2, 72, 3, 82, 7, 207, 198, - 26, 83, 7, 164, 241, 26, 8, 32, 80, 76, 85, 83, 32, 90, 65, 179, 245, 3, - 78, 43, 104, 2, 83, 72, 186, 60, 71, 144, 133, 11, 5, 32, 79, 86, 69, 82, - 40, 2, 82, 71, 225, 197, 6, 2, 78, 83, 31, 22, 32, 183, 2, 51, 16, 136, + 45, 32, 188, 1, 2, 83, 72, 238, 205, 30, 50, 2, 72, 3, 82, 7, 175, 235, + 26, 83, 7, 244, 129, 27, 8, 32, 80, 76, 85, 83, 32, 90, 65, 135, 250, 3, + 78, 43, 104, 2, 83, 72, 186, 60, 71, 140, 134, 11, 5, 32, 79, 86, 69, 82, + 40, 2, 82, 71, 225, 198, 6, 2, 78, 83, 31, 22, 32, 183, 2, 51, 16, 136, 1, 9, 79, 86, 69, 82, 32, 77, 85, 83, 72, 124, 6, 84, 73, 77, 69, 83, 32, - 209, 216, 26, 10, 67, 82, 79, 83, 83, 73, 78, 71, 32, 77, 9, 37, 7, 32, - 84, 73, 77, 69, 83, 32, 6, 42, 65, 238, 173, 28, 75, 143, 179, 2, 71, 2, - 241, 188, 11, 5, 32, 80, 76, 85, 83, 6, 178, 234, 29, 75, 158, 118, 90, + 161, 233, 26, 10, 67, 82, 79, 83, 83, 73, 78, 71, 32, 77, 9, 37, 7, 32, + 84, 73, 77, 69, 83, 32, 6, 42, 65, 206, 191, 28, 75, 211, 182, 2, 71, 2, + 237, 189, 11, 5, 32, 80, 76, 85, 83, 6, 214, 255, 29, 75, 158, 118, 90, 187, 2, 65, 13, 11, 32, 10, 44, 6, 84, 73, 77, 69, 83, 32, 203, 57, 71, - 8, 38, 65, 162, 206, 30, 68, 163, 17, 90, 5, 205, 129, 13, 5, 32, 80, 76, + 8, 38, 65, 198, 227, 30, 68, 163, 17, 90, 5, 233, 135, 13, 5, 32, 80, 76, 85, 83, 158, 1, 42, 65, 134, 2, 69, 94, 73, 199, 7, 85, 23, 52, 2, 71, - 65, 166, 1, 77, 198, 223, 30, 50, 3, 52, 11, 26, 32, 207, 224, 30, 82, 6, + 65, 166, 1, 77, 234, 244, 30, 50, 3, 52, 11, 26, 32, 243, 245, 30, 82, 6, 100, 8, 79, 80, 80, 79, 83, 73, 78, 71, 146, 59, 73, 197, 14, 9, 84, 73, - 77, 69, 83, 32, 83, 72, 85, 2, 157, 157, 30, 3, 32, 78, 65, 7, 204, 21, - 2, 32, 78, 247, 201, 30, 50, 9, 11, 32, 6, 44, 6, 84, 73, 77, 69, 83, 32, - 179, 57, 83, 4, 190, 152, 30, 85, 163, 70, 65, 73, 90, 77, 78, 78, 168, - 182, 20, 6, 32, 84, 73, 77, 69, 83, 238, 189, 8, 83, 135, 233, 1, 50, 7, + 77, 69, 83, 32, 83, 72, 85, 2, 193, 178, 30, 3, 32, 78, 65, 7, 204, 21, + 2, 32, 78, 155, 223, 30, 50, 9, 11, 32, 6, 44, 6, 84, 73, 77, 69, 83, 32, + 179, 57, 83, 4, 226, 173, 30, 85, 163, 70, 65, 73, 90, 77, 78, 78, 136, + 186, 20, 6, 32, 84, 73, 77, 69, 83, 202, 204, 8, 83, 239, 235, 1, 50, 7, 45, 9, 32, 84, 73, 77, 69, 83, 32, 71, 65, 4, 158, 3, 82, 179, 58, 78, - 59, 36, 3, 68, 65, 50, 243, 220, 30, 57, 55, 37, 7, 32, 84, 73, 77, 69, + 59, 36, 3, 68, 65, 50, 151, 242, 30, 57, 55, 37, 7, 32, 84, 73, 77, 69, 83, 32, 52, 162, 1, 65, 34, 71, 50, 75, 16, 5, 76, 65, 75, 45, 48, 22, 77, 86, 78, 34, 80, 76, 3, 83, 72, 69, 94, 85, 240, 15, 3, 68, 73, 77, - 230, 190, 29, 66, 235, 117, 72, 6, 246, 2, 83, 175, 216, 30, 78, 8, 26, - 73, 183, 216, 29, 85, 5, 215, 217, 30, 83, 2, 211, 20, 69, 2, 243, 193, - 23, 53, 4, 26, 69, 247, 241, 28, 65, 2, 25, 4, 32, 80, 76, 85, 2, 177, - 41, 3, 83, 32, 71, 4, 250, 137, 30, 85, 227, 79, 69, 2, 33, 6, 65, 80, - 32, 80, 76, 85, 2, 11, 83, 2, 157, 200, 29, 2, 32, 80, 9, 37, 7, 32, 80, - 76, 85, 83, 32, 65, 6, 26, 83, 175, 142, 29, 32, 4, 11, 72, 5, 107, 32, - 11, 50, 32, 34, 50, 174, 169, 14, 82, 135, 173, 16, 83, 2, 173, 142, 14, - 3, 80, 76, 85, 2, 11, 32, 2, 21, 3, 80, 76, 85, 2, 11, 83, 2, 227, 238, - 28, 32, 57, 24, 2, 49, 49, 99, 78, 9, 11, 32, 6, 200, 25, 9, 79, 86, 69, - 82, 32, 78, 85, 49, 49, 150, 191, 25, 84, 207, 161, 3, 82, 47, 30, 32, + 238, 209, 29, 66, 135, 120, 72, 6, 246, 2, 83, 211, 237, 30, 78, 8, 26, + 73, 219, 237, 29, 85, 5, 251, 238, 30, 83, 2, 211, 20, 69, 2, 131, 201, + 23, 53, 4, 26, 69, 179, 132, 29, 65, 2, 25, 4, 32, 80, 76, 85, 2, 177, + 41, 3, 83, 32, 71, 4, 158, 159, 30, 85, 227, 79, 69, 2, 33, 6, 65, 80, + 32, 80, 76, 85, 2, 11, 83, 2, 165, 219, 29, 2, 32, 80, 9, 37, 7, 32, 80, + 76, 85, 83, 32, 65, 6, 26, 83, 183, 161, 29, 32, 4, 11, 72, 5, 107, 32, + 11, 50, 32, 34, 50, 218, 175, 14, 82, 255, 187, 16, 83, 2, 217, 148, 14, + 3, 80, 76, 85, 2, 11, 32, 2, 21, 3, 80, 76, 85, 2, 11, 83, 2, 159, 129, + 29, 32, 57, 24, 2, 49, 49, 99, 78, 9, 11, 32, 6, 200, 25, 9, 79, 86, 69, + 82, 32, 78, 85, 49, 49, 230, 203, 25, 84, 243, 167, 3, 82, 47, 30, 32, 169, 3, 2, 85, 90, 18, 144, 1, 12, 67, 82, 79, 83, 83, 73, 78, 71, 32, 78, 85, 78, 72, 12, 76, 65, 71, 65, 82, 32, 84, 73, 77, 69, 83, 32, 174, - 1, 79, 231, 212, 25, 84, 5, 209, 40, 14, 32, 76, 65, 71, 65, 82, 32, 79, - 86, 69, 82, 32, 76, 65, 10, 56, 3, 83, 65, 76, 158, 235, 28, 77, 14, 85, - 235, 70, 71, 5, 133, 202, 29, 23, 32, 79, 86, 69, 82, 32, 78, 85, 78, 32, - 76, 65, 71, 65, 82, 32, 84, 73, 77, 69, 83, 32, 83, 2, 201, 185, 17, 3, + 1, 79, 183, 225, 25, 84, 5, 209, 40, 14, 32, 76, 65, 71, 65, 82, 32, 79, + 86, 69, 82, 32, 76, 65, 10, 56, 3, 83, 65, 76, 218, 253, 28, 77, 14, 85, + 207, 71, 71, 5, 141, 221, 29, 23, 32, 79, 86, 69, 82, 32, 78, 85, 78, 32, + 76, 65, 71, 65, 82, 32, 84, 73, 77, 69, 83, 32, 83, 2, 165, 187, 17, 3, 86, 69, 82, 27, 11, 32, 24, 120, 10, 65, 66, 50, 32, 84, 73, 77, 69, 83, 32, 205, 37, 15, 75, 73, 83, 73, 77, 53, 32, 84, 73, 77, 69, 83, 32, 66, - 73, 20, 168, 1, 2, 75, 65, 202, 7, 73, 212, 39, 2, 65, 83, 130, 177, 2, - 68, 232, 206, 8, 3, 83, 73, 76, 214, 135, 12, 85, 210, 237, 5, 71, 138, - 149, 1, 78, 254, 2, 66, 163, 17, 76, 2, 251, 217, 26, 68, 46, 42, 65, 36, - 4, 69, 83, 72, 50, 23, 73, 9, 194, 207, 30, 68, 2, 78, 3, 80, 5, 191, - 187, 28, 32, 35, 22, 32, 151, 1, 82, 20, 80, 6, 84, 73, 77, 69, 83, 32, - 201, 131, 18, 8, 67, 82, 79, 83, 83, 73, 78, 71, 18, 214, 21, 85, 150, - 48, 65, 2, 73, 254, 199, 29, 66, 187, 64, 69, 12, 32, 2, 73, 71, 191, - 205, 30, 50, 11, 11, 32, 8, 96, 6, 84, 73, 77, 69, 83, 32, 197, 153, 26, - 12, 79, 80, 80, 79, 83, 73, 78, 71, 32, 80, 73, 82, 6, 138, 195, 29, 75, - 162, 67, 85, 235, 67, 90, 8, 234, 67, 65, 182, 136, 30, 73, 3, 85, 202, + 73, 20, 168, 1, 2, 75, 65, 202, 7, 73, 212, 39, 2, 65, 83, 138, 173, 2, + 68, 220, 211, 8, 3, 83, 73, 76, 234, 141, 12, 85, 210, 249, 5, 71, 158, + 151, 1, 78, 254, 2, 66, 163, 17, 76, 2, 203, 234, 26, 68, 46, 42, 65, 36, + 4, 69, 83, 72, 50, 23, 73, 9, 230, 228, 30, 68, 2, 78, 3, 80, 5, 135, + 205, 28, 32, 35, 22, 32, 151, 1, 82, 20, 80, 6, 84, 73, 77, 69, 83, 32, + 193, 133, 18, 8, 67, 82, 79, 83, 83, 73, 78, 71, 18, 214, 21, 85, 150, + 48, 65, 2, 73, 162, 221, 29, 66, 187, 64, 69, 12, 32, 2, 73, 71, 227, + 226, 30, 50, 11, 11, 32, 8, 96, 6, 84, 73, 77, 69, 83, 32, 141, 166, 26, + 12, 79, 80, 80, 79, 83, 73, 78, 71, 32, 80, 73, 82, 6, 146, 214, 29, 75, + 190, 69, 85, 235, 67, 90, 8, 234, 67, 65, 218, 157, 30, 73, 3, 85, 202, 1, 46, 65, 250, 5, 72, 150, 11, 73, 163, 1, 85, 63, 46, 71, 190, 4, 76, - 122, 78, 227, 197, 30, 82, 53, 11, 32, 50, 92, 4, 71, 85, 78, 85, 54, 78, + 122, 78, 135, 219, 30, 82, 53, 11, 32, 50, 92, 4, 71, 85, 78, 85, 54, 78, 32, 6, 84, 73, 77, 69, 83, 32, 241, 21, 4, 79, 86, 69, 82, 5, 29, 5, 32, - 84, 73, 77, 69, 2, 159, 230, 17, 83, 2, 149, 164, 25, 3, 85, 84, 73, 42, + 84, 73, 77, 69, 2, 155, 232, 17, 83, 2, 237, 176, 25, 3, 85, 84, 73, 42, 150, 1, 73, 42, 75, 34, 83, 64, 2, 84, 65, 38, 85, 220, 62, 2, 68, 85, - 166, 223, 28, 76, 226, 40, 78, 210, 48, 69, 138, 60, 77, 162, 17, 72, - 187, 2, 65, 2, 11, 71, 2, 11, 73, 2, 191, 31, 32, 4, 182, 143, 30, 85, - 199, 53, 65, 6, 26, 72, 167, 190, 29, 65, 4, 162, 192, 13, 69, 151, 193, - 16, 73, 4, 226, 172, 23, 75, 211, 154, 7, 66, 10, 254, 197, 30, 83, 146, + 182, 242, 28, 76, 246, 42, 78, 210, 48, 69, 138, 60, 77, 162, 17, 72, + 187, 2, 65, 2, 11, 71, 2, 11, 73, 2, 191, 31, 32, 4, 218, 164, 30, 85, + 199, 53, 65, 6, 26, 72, 175, 209, 29, 65, 4, 198, 198, 13, 69, 151, 208, + 16, 73, 4, 242, 179, 23, 75, 231, 168, 7, 66, 10, 162, 219, 30, 83, 146, 1, 50, 2, 66, 2, 77, 3, 82, 5, 33, 6, 32, 76, 65, 71, 65, 66, 2, 37, 7, - 32, 84, 73, 77, 69, 83, 32, 2, 11, 65, 2, 11, 83, 2, 199, 172, 23, 72, 2, - 135, 169, 11, 71, 106, 46, 65, 250, 1, 69, 242, 2, 73, 239, 3, 85, 29, - 50, 51, 182, 1, 54, 174, 166, 23, 66, 223, 3, 82, 19, 37, 7, 32, 84, 73, - 77, 69, 83, 32, 16, 90, 85, 146, 25, 83, 222, 201, 26, 71, 166, 232, 2, - 84, 170, 50, 66, 218, 47, 78, 215, 22, 65, 5, 11, 32, 2, 181, 131, 26, 4, - 80, 76, 85, 83, 5, 175, 45, 32, 27, 62, 32, 140, 2, 2, 83, 72, 150, 203, - 26, 71, 199, 245, 3, 78, 14, 84, 8, 79, 86, 69, 82, 32, 83, 72, 69, 88, - 5, 80, 76, 85, 83, 32, 207, 156, 30, 72, 7, 11, 32, 4, 190, 15, 71, 141, + 32, 84, 73, 77, 69, 83, 32, 2, 11, 65, 2, 11, 83, 2, 215, 179, 23, 72, 2, + 131, 170, 11, 71, 106, 46, 65, 250, 1, 69, 242, 2, 73, 239, 3, 85, 29, + 50, 51, 182, 1, 54, 190, 173, 23, 66, 223, 3, 82, 19, 37, 7, 32, 84, 73, + 77, 69, 83, 32, 16, 90, 85, 146, 25, 83, 174, 219, 26, 71, 250, 235, 2, + 84, 170, 50, 66, 218, 47, 78, 215, 22, 65, 5, 11, 32, 2, 253, 143, 26, 4, + 80, 76, 85, 83, 5, 175, 45, 32, 27, 62, 32, 140, 2, 2, 83, 72, 230, 219, + 26, 71, 155, 250, 3, 78, 14, 84, 8, 79, 86, 69, 82, 32, 83, 72, 69, 88, + 5, 80, 76, 85, 83, 32, 243, 177, 30, 72, 7, 11, 32, 4, 190, 15, 71, 141, 6, 12, 84, 65, 66, 32, 79, 86, 69, 82, 32, 84, 65, 66, 6, 48, 2, 72, 85, - 20, 2, 78, 65, 183, 159, 29, 83, 2, 211, 167, 23, 66, 2, 191, 167, 23, - 77, 7, 218, 133, 29, 76, 255, 186, 1, 50, 40, 62, 68, 74, 77, 218, 1, 82, - 206, 138, 26, 78, 211, 176, 4, 84, 7, 37, 7, 32, 84, 73, 77, 69, 83, 32, - 4, 242, 174, 30, 73, 219, 16, 65, 25, 37, 7, 32, 84, 73, 77, 69, 83, 32, - 22, 114, 66, 38, 73, 130, 19, 75, 134, 187, 2, 77, 178, 211, 2, 76, 130, - 251, 23, 71, 250, 23, 83, 238, 9, 68, 215, 127, 65, 4, 154, 206, 2, 85, - 195, 230, 26, 65, 4, 249, 20, 2, 71, 73, 7, 11, 32, 4, 60, 9, 79, 86, 69, - 82, 32, 83, 72, 73, 82, 151, 191, 25, 84, 2, 177, 196, 29, 11, 32, 66, + 20, 2, 78, 65, 215, 178, 29, 83, 2, 227, 174, 23, 66, 2, 207, 174, 23, + 77, 7, 234, 152, 29, 76, 147, 189, 1, 50, 40, 62, 68, 74, 77, 218, 1, 82, + 150, 151, 26, 78, 175, 185, 4, 84, 7, 37, 7, 32, 84, 73, 77, 69, 83, 32, + 4, 150, 196, 30, 73, 219, 16, 65, 25, 37, 7, 32, 84, 73, 77, 69, 83, 32, + 22, 114, 66, 38, 73, 130, 19, 75, 142, 183, 2, 77, 234, 217, 2, 76, 226, + 139, 24, 71, 226, 23, 83, 138, 12, 68, 215, 127, 65, 4, 162, 202, 2, 85, + 195, 253, 26, 65, 4, 249, 20, 2, 71, 73, 7, 11, 32, 4, 60, 9, 79, 86, 69, + 82, 32, 83, 72, 73, 82, 231, 203, 25, 84, 2, 213, 217, 29, 11, 32, 66, 85, 82, 32, 79, 86, 69, 82, 32, 66, 13, 88, 14, 32, 79, 86, 69, 82, 32, - 73, 78, 86, 69, 82, 84, 69, 68, 34, 50, 251, 194, 29, 66, 2, 11, 32, 2, - 235, 205, 25, 83, 7, 33, 6, 32, 80, 76, 85, 83, 32, 4, 88, 7, 69, 50, 32, - 84, 73, 77, 69, 177, 152, 11, 9, 68, 85, 71, 32, 84, 73, 77, 69, 83, 2, - 131, 132, 13, 83, 17, 50, 32, 30, 71, 234, 152, 11, 76, 215, 135, 12, 75, + 73, 78, 86, 69, 82, 84, 69, 68, 34, 50, 159, 216, 29, 66, 2, 11, 32, 2, + 187, 218, 25, 83, 7, 33, 6, 32, 80, 76, 85, 83, 32, 4, 88, 7, 69, 50, 32, + 84, 73, 77, 69, 173, 153, 11, 9, 68, 85, 71, 32, 84, 73, 77, 69, 83, 2, + 139, 138, 13, 83, 17, 50, 32, 30, 71, 230, 153, 11, 76, 235, 141, 12, 75, 4, 138, 8, 84, 163, 9, 71, 7, 11, 52, 5, 49, 10, 32, 79, 86, 69, 82, 32, - 83, 73, 71, 52, 2, 199, 14, 32, 19, 78, 68, 22, 77, 22, 82, 156, 217, 12, - 5, 32, 79, 86, 69, 82, 179, 230, 16, 72, 5, 199, 184, 30, 50, 5, 147, - 208, 28, 65, 5, 159, 184, 30, 57, 72, 46, 65, 150, 4, 73, 250, 1, 85, - 227, 8, 69, 37, 66, 32, 94, 66, 166, 1, 71, 148, 1, 2, 75, 52, 135, 180, - 30, 82, 8, 60, 6, 84, 73, 77, 69, 83, 32, 130, 14, 71, 135, 149, 28, 65, - 4, 254, 162, 30, 72, 3, 77, 7, 11, 32, 4, 180, 203, 17, 29, 79, 86, 69, + 83, 73, 71, 52, 2, 199, 14, 32, 19, 78, 68, 22, 77, 22, 82, 184, 223, 12, + 5, 32, 79, 86, 69, 82, 187, 245, 16, 72, 5, 235, 205, 30, 50, 5, 207, + 226, 28, 65, 5, 195, 205, 30, 57, 72, 46, 65, 150, 4, 73, 250, 1, 85, + 227, 8, 69, 37, 66, 32, 94, 66, 166, 1, 71, 148, 1, 2, 75, 52, 171, 201, + 30, 82, 8, 60, 6, 84, 73, 77, 69, 83, 32, 130, 14, 71, 207, 166, 28, 65, + 4, 162, 184, 30, 72, 3, 77, 7, 11, 32, 4, 176, 205, 17, 29, 79, 86, 69, 82, 32, 84, 65, 66, 32, 78, 73, 32, 79, 86, 69, 82, 32, 78, 73, 32, 68, - 73, 83, 72, 32, 79, 86, 69, 82, 143, 187, 5, 83, 15, 37, 7, 32, 84, 73, - 77, 69, 83, 32, 12, 74, 84, 252, 254, 7, 2, 83, 72, 206, 137, 21, 71, - 190, 101, 85, 203, 50, 66, 2, 11, 85, 2, 235, 154, 23, 71, 5, 29, 5, 32, - 80, 76, 85, 83, 2, 221, 202, 28, 2, 32, 83, 17, 46, 82, 150, 29, 32, 134, - 150, 30, 50, 3, 76, 9, 11, 32, 6, 48, 8, 79, 86, 69, 82, 32, 84, 73, 82, + 73, 83, 72, 32, 79, 86, 69, 82, 163, 192, 5, 83, 15, 37, 7, 32, 84, 73, + 77, 69, 83, 32, 12, 74, 84, 216, 254, 7, 2, 83, 72, 130, 157, 21, 71, + 210, 103, 85, 203, 50, 66, 2, 11, 85, 2, 251, 161, 23, 71, 5, 29, 5, 32, + 80, 76, 85, 83, 2, 153, 221, 28, 2, 32, 83, 17, 46, 82, 150, 29, 32, 170, + 171, 30, 50, 3, 76, 9, 11, 32, 6, 48, 8, 79, 86, 69, 82, 32, 84, 73, 82, 99, 84, 5, 11, 32, 2, 11, 71, 2, 21, 3, 65, 68, 32, 2, 241, 5, 8, 79, 86, 69, 82, 32, 71, 65, 68, 2, 217, 21, 6, 73, 77, 69, 83, 32, 84, 17, 50, - 77, 114, 82, 130, 151, 23, 71, 175, 153, 7, 75, 7, 37, 7, 32, 84, 73, 77, - 69, 83, 32, 4, 46, 71, 205, 197, 17, 5, 84, 72, 82, 69, 69, 2, 221, 16, - 2, 65, 78, 5, 141, 139, 11, 17, 32, 79, 86, 69, 82, 32, 84, 85, 82, 32, + 77, 114, 82, 146, 158, 23, 71, 195, 167, 7, 75, 7, 37, 7, 32, 84, 73, 77, + 69, 83, 32, 4, 46, 71, 201, 199, 17, 5, 84, 72, 82, 69, 69, 2, 221, 16, + 2, 65, 78, 5, 137, 140, 11, 17, 32, 79, 86, 69, 82, 32, 84, 85, 82, 32, 90, 65, 32, 79, 86, 69, 82, 179, 1, 138, 1, 32, 246, 2, 68, 226, 2, 78, - 46, 77, 158, 2, 82, 128, 9, 2, 83, 72, 174, 1, 90, 224, 185, 12, 2, 84, - 85, 158, 226, 17, 50, 3, 66, 12, 64, 7, 79, 86, 69, 82, 32, 85, 32, 158, - 2, 85, 139, 128, 29, 71, 6, 200, 1, 10, 80, 65, 32, 79, 86, 69, 82, 32, - 80, 65, 216, 159, 9, 19, 85, 32, 82, 69, 86, 69, 82, 83, 69, 68, 32, 79, - 86, 69, 82, 32, 85, 32, 82, 137, 148, 20, 10, 83, 85, 82, 32, 79, 86, 69, + 46, 77, 158, 2, 82, 128, 9, 2, 83, 72, 174, 1, 90, 252, 191, 12, 2, 84, + 85, 166, 241, 17, 50, 3, 66, 12, 64, 7, 79, 86, 69, 82, 32, 85, 32, 158, + 2, 85, 155, 147, 29, 71, 6, 200, 1, 10, 80, 65, 32, 79, 86, 69, 82, 32, + 80, 65, 220, 159, 9, 19, 85, 32, 82, 69, 86, 69, 82, 83, 69, 68, 32, 79, + 86, 69, 82, 32, 85, 32, 82, 169, 169, 20, 10, 83, 85, 82, 32, 79, 86, 69, 82, 32, 83, 2, 11, 32, 2, 45, 9, 71, 65, 82, 32, 79, 86, 69, 82, 32, 2, - 191, 138, 29, 71, 5, 251, 134, 30, 32, 21, 26, 32, 227, 170, 30, 85, 16, + 223, 157, 29, 71, 5, 159, 156, 30, 32, 21, 26, 32, 135, 192, 30, 85, 16, 70, 75, 44, 2, 83, 72, 100, 6, 84, 73, 77, 69, 83, 32, 135, 1, 71, 2, 11, - 85, 2, 11, 83, 2, 155, 135, 11, 72, 4, 29, 5, 69, 83, 72, 73, 71, 5, 11, - 32, 2, 11, 84, 2, 217, 227, 29, 6, 73, 77, 69, 83, 32, 66, 8, 92, 14, 85, - 32, 80, 76, 85, 83, 32, 85, 32, 80, 76, 85, 83, 32, 210, 226, 29, 66, - 215, 50, 77, 4, 11, 85, 5, 11, 32, 2, 11, 71, 2, 227, 131, 30, 85, 21, - 72, 7, 32, 84, 73, 77, 69, 83, 32, 136, 1, 2, 85, 77, 159, 167, 29, 66, - 10, 50, 76, 16, 2, 77, 69, 50, 83, 135, 167, 30, 85, 2, 231, 6, 65, 5, - 11, 32, 2, 201, 241, 25, 4, 80, 76, 85, 83, 2, 131, 134, 11, 72, 7, 37, - 7, 32, 84, 73, 77, 69, 83, 32, 4, 158, 11, 75, 243, 152, 30, 80, 93, 54, - 32, 102, 50, 194, 2, 73, 22, 85, 187, 162, 30, 52, 4, 62, 83, 221, 172, - 29, 9, 67, 82, 79, 83, 83, 73, 78, 71, 32, 2, 165, 242, 25, 4, 72, 69, - 83, 72, 23, 11, 32, 20, 42, 73, 37, 6, 84, 73, 77, 69, 83, 32, 2, 165, - 130, 24, 4, 78, 86, 69, 82, 18, 46, 65, 82, 85, 130, 163, 29, 78, 251, - 125, 72, 6, 48, 6, 32, 80, 76, 85, 83, 32, 207, 163, 30, 76, 4, 146, 161, - 30, 72, 3, 78, 8, 26, 50, 151, 163, 30, 68, 7, 33, 6, 32, 80, 76, 85, 83, - 32, 4, 206, 186, 28, 65, 203, 212, 1, 66, 5, 203, 162, 30, 51, 59, 56, 7, + 85, 2, 11, 83, 2, 151, 136, 11, 72, 4, 29, 5, 69, 83, 72, 73, 71, 5, 11, + 32, 2, 11, 84, 2, 253, 248, 29, 6, 73, 77, 69, 83, 32, 66, 8, 92, 14, 85, + 32, 80, 76, 85, 83, 32, 85, 32, 80, 76, 85, 83, 32, 246, 247, 29, 66, + 215, 50, 77, 4, 11, 85, 5, 11, 32, 2, 11, 71, 2, 135, 153, 30, 85, 21, + 72, 7, 32, 84, 73, 77, 69, 83, 32, 136, 1, 2, 85, 77, 195, 188, 29, 66, + 10, 50, 76, 16, 2, 77, 69, 50, 83, 171, 188, 30, 85, 2, 231, 6, 65, 5, + 11, 32, 2, 145, 254, 25, 4, 80, 76, 85, 83, 2, 255, 134, 11, 72, 7, 37, + 7, 32, 84, 73, 77, 69, 83, 32, 4, 158, 11, 75, 151, 174, 30, 80, 93, 54, + 32, 102, 50, 194, 2, 73, 22, 85, 223, 183, 30, 52, 4, 62, 83, 129, 194, + 29, 9, 67, 82, 79, 83, 83, 73, 78, 71, 32, 2, 237, 254, 25, 4, 72, 69, + 83, 72, 23, 11, 32, 20, 42, 73, 37, 6, 84, 73, 77, 69, 83, 32, 2, 233, + 136, 24, 4, 78, 86, 69, 82, 18, 46, 65, 82, 85, 166, 184, 29, 78, 251, + 125, 72, 6, 48, 6, 32, 80, 76, 85, 83, 32, 243, 184, 30, 76, 4, 182, 182, + 30, 72, 3, 78, 8, 26, 50, 187, 184, 30, 68, 7, 33, 6, 32, 80, 76, 85, 83, + 32, 4, 138, 205, 28, 65, 179, 215, 1, 66, 5, 239, 183, 30, 51, 59, 56, 7, 32, 84, 73, 77, 69, 83, 32, 165, 4, 2, 68, 65, 52, 134, 1, 65, 46, 68, - 38, 71, 82, 73, 46, 76, 78, 83, 46, 85, 250, 252, 28, 66, 230, 33, 77, + 38, 71, 82, 73, 46, 76, 78, 83, 46, 85, 154, 144, 29, 66, 234, 35, 77, 238, 90, 84, 146, 17, 75, 162, 17, 72, 3, 80, 5, 11, 83, 2, 11, 72, 2, - 255, 254, 16, 71, 4, 190, 254, 10, 65, 171, 210, 18, 85, 10, 26, 65, 139, - 160, 30, 85, 9, 34, 78, 230, 159, 30, 76, 3, 82, 2, 211, 9, 50, 6, 250, - 139, 30, 71, 202, 18, 83, 147, 1, 77, 6, 46, 85, 221, 129, 23, 5, 65, 75, - 45, 54, 54, 4, 246, 158, 30, 51, 3, 77, 4, 136, 132, 23, 2, 73, 71, 255, - 131, 7, 72, 6, 42, 32, 242, 239, 13, 82, 151, 174, 16, 68, 2, 165, 242, - 28, 6, 80, 76, 85, 83, 32, 71, 5, 11, 32, 2, 153, 212, 13, 4, 84, 73, 77, - 69, 17, 84, 7, 32, 84, 73, 77, 69, 83, 32, 172, 144, 29, 2, 85, 77, 166, - 140, 1, 50, 3, 88, 8, 50, 84, 212, 201, 26, 2, 75, 85, 203, 210, 3, 65, - 2, 11, 65, 2, 191, 129, 23, 75, 6, 26, 51, 227, 155, 30, 85, 5, 29, 5, - 32, 84, 73, 77, 69, 2, 21, 3, 83, 32, 75, 2, 11, 65, 2, 139, 173, 23, 83, - 42, 50, 65, 190, 1, 73, 146, 1, 85, 223, 254, 22, 69, 13, 50, 32, 254, - 141, 29, 77, 166, 140, 1, 55, 3, 71, 4, 56, 8, 83, 81, 85, 65, 82, 69, - 68, 32, 243, 155, 25, 84, 2, 11, 84, 2, 21, 3, 73, 77, 69, 2, 11, 83, 2, - 221, 160, 29, 2, 32, 75, 15, 86, 66, 220, 229, 22, 6, 32, 79, 86, 69, 82, - 32, 186, 25, 90, 174, 153, 7, 51, 3, 71, 5, 17, 2, 32, 75, 2, 17, 2, 65, + 219, 128, 17, 71, 4, 186, 255, 10, 65, 211, 230, 18, 85, 10, 26, 65, 175, + 181, 30, 85, 9, 34, 78, 138, 181, 30, 76, 3, 82, 2, 211, 9, 50, 6, 158, + 161, 30, 71, 202, 18, 83, 147, 1, 77, 6, 46, 85, 237, 136, 23, 5, 65, 75, + 45, 54, 54, 4, 154, 180, 30, 51, 3, 77, 4, 152, 139, 23, 2, 73, 71, 147, + 146, 7, 72, 6, 42, 32, 158, 246, 13, 82, 143, 189, 16, 68, 2, 181, 133, + 29, 6, 80, 76, 85, 83, 32, 71, 5, 11, 32, 2, 197, 218, 13, 4, 84, 73, 77, + 69, 17, 84, 7, 32, 84, 73, 77, 69, 83, 32, 180, 163, 29, 2, 85, 77, 194, + 142, 1, 50, 3, 88, 8, 50, 84, 164, 219, 26, 2, 75, 85, 159, 214, 3, 65, + 2, 11, 65, 2, 207, 136, 23, 75, 6, 26, 51, 135, 177, 30, 85, 5, 29, 5, + 32, 84, 73, 77, 69, 2, 21, 3, 83, 32, 75, 2, 11, 65, 2, 175, 180, 23, 83, + 42, 50, 65, 190, 1, 73, 146, 1, 85, 239, 133, 23, 69, 13, 50, 32, 134, + 161, 29, 77, 194, 142, 1, 55, 3, 71, 4, 56, 8, 83, 81, 85, 65, 82, 69, + 68, 32, 195, 168, 25, 84, 2, 11, 84, 2, 21, 3, 73, 77, 69, 2, 11, 83, 2, + 129, 182, 29, 2, 32, 75, 15, 86, 66, 236, 236, 22, 6, 32, 79, 86, 69, 82, + 32, 186, 25, 90, 194, 167, 7, 51, 3, 71, 5, 17, 2, 32, 75, 2, 17, 2, 65, 66, 2, 135, 2, 65, 15, 84, 10, 32, 79, 86, 69, 82, 32, 90, 85, 32, 80, - 42, 53, 182, 158, 29, 66, 215, 120, 77, 2, 245, 245, 28, 5, 76, 85, 83, + 42, 53, 218, 179, 29, 66, 215, 120, 77, 2, 149, 137, 29, 5, 76, 85, 83, 32, 83, 7, 37, 7, 32, 84, 73, 77, 69, 83, 32, 4, 44, 5, 84, 72, 82, 69, - 69, 179, 150, 30, 65, 2, 29, 5, 32, 68, 73, 83, 72, 2, 11, 32, 2, 143, - 152, 25, 84, 8, 42, 32, 154, 228, 22, 73, 255, 200, 3, 67, 4, 198, 159, - 22, 79, 209, 247, 6, 8, 87, 73, 84, 72, 32, 83, 84, 82, 18, 134, 1, 76, - 162, 1, 82, 249, 206, 29, 23, 86, 69, 68, 32, 83, 84, 69, 77, 32, 80, 65, - 82, 65, 71, 82, 65, 80, 72, 32, 83, 73, 71, 78, 10, 48, 2, 89, 32, 241, - 162, 3, 4, 73, 78, 71, 32, 8, 68, 2, 76, 79, 193, 243, 27, 9, 66, 82, 65, - 67, 75, 69, 84, 32, 69, 6, 214, 209, 26, 71, 155, 193, 3, 79, 6, 52, 5, - 69, 78, 67, 89, 32, 53, 4, 89, 32, 65, 78, 4, 212, 247, 24, 4, 69, 88, - 67, 72, 227, 147, 4, 83, 2, 217, 235, 19, 3, 68, 32, 82, 4, 218, 131, 3, - 79, 135, 247, 25, 65, 240, 8, 128, 1, 2, 80, 82, 140, 9, 7, 82, 73, 76, - 76, 73, 67, 32, 132, 223, 25, 7, 76, 73, 78, 68, 82, 73, 67, 205, 253, 2, + 69, 215, 171, 30, 65, 2, 29, 5, 32, 68, 73, 83, 72, 2, 11, 32, 2, 223, + 164, 25, 84, 8, 42, 32, 170, 235, 22, 73, 155, 211, 3, 67, 4, 226, 166, + 22, 79, 217, 133, 7, 8, 87, 73, 84, 72, 32, 83, 84, 82, 18, 134, 1, 76, + 162, 1, 82, 157, 228, 29, 23, 86, 69, 68, 32, 83, 84, 69, 77, 32, 80, 65, + 82, 65, 71, 82, 65, 80, 72, 32, 83, 73, 71, 78, 10, 48, 2, 89, 32, 133, + 160, 3, 4, 73, 78, 71, 32, 8, 68, 2, 76, 79, 145, 133, 28, 9, 66, 82, 65, + 67, 75, 69, 84, 32, 69, 6, 166, 227, 26, 71, 239, 196, 3, 79, 6, 52, 5, + 69, 78, 67, 89, 32, 53, 4, 89, 32, 65, 78, 4, 172, 132, 25, 4, 69, 88, + 67, 72, 175, 156, 4, 83, 2, 185, 239, 19, 3, 68, 32, 82, 4, 182, 128, 3, + 79, 203, 141, 26, 65, 240, 8, 128, 1, 2, 80, 82, 140, 9, 7, 82, 73, 76, + 76, 73, 67, 32, 204, 235, 25, 7, 76, 73, 78, 68, 82, 73, 67, 149, 132, 3, 2, 67, 76, 180, 2, 140, 1, 13, 73, 79, 84, 32, 83, 89, 76, 76, 65, 66, 76, 69, 32, 169, 1, 16, 79, 45, 77, 73, 78, 79, 65, 78, 32, 83, 73, 71, - 78, 32, 67, 77, 110, 238, 177, 7, 75, 2, 76, 2, 77, 2, 78, 2, 80, 2, 82, - 2, 83, 2, 84, 126, 87, 138, 42, 74, 2, 90, 130, 239, 6, 88, 154, 195, 15, - 65, 2, 69, 2, 73, 2, 79, 3, 85, 198, 1, 46, 48, 146, 5, 49, 153, 135, 26, + 78, 32, 67, 77, 110, 138, 181, 7, 75, 2, 76, 2, 77, 2, 78, 2, 80, 2, 82, + 2, 83, 2, 84, 126, 87, 202, 38, 74, 2, 90, 230, 245, 6, 88, 254, 209, 15, + 65, 2, 69, 2, 73, 2, 79, 3, 85, 198, 1, 46, 48, 146, 5, 49, 233, 151, 26, 2, 51, 48, 170, 1, 102, 48, 78, 49, 74, 50, 78, 51, 78, 52, 62, 53, 86, - 55, 190, 186, 10, 57, 190, 3, 54, 215, 149, 14, 56, 16, 194, 140, 30, 49, - 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 16, 194, 3, 50, 182, - 136, 30, 48, 2, 49, 2, 51, 2, 53, 2, 55, 3, 57, 16, 174, 139, 30, 49, 2, - 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 16, 226, 138, 30, 48, 2, - 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 12, 150, 138, 30, 48, 2, - 49, 2, 52, 2, 54, 2, 55, 3, 57, 18, 218, 137, 30, 48, 2, 49, 2, 50, 2, - 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 20, 82, 53, 182, 136, 30, 48, 2, - 49, 2, 50, 2, 51, 2, 52, 2, 54, 2, 56, 3, 57, 5, 179, 136, 30, 66, 24, - 18, 48, 87, 49, 18, 138, 136, 30, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, - 2, 55, 2, 56, 3, 57, 6, 182, 135, 30, 48, 2, 50, 3, 52, 184, 6, 140, 1, + 55, 198, 187, 10, 57, 190, 3, 54, 143, 161, 14, 56, 16, 230, 161, 30, 49, + 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 16, 194, 3, 50, 218, + 157, 30, 48, 2, 49, 2, 51, 2, 53, 2, 55, 3, 57, 16, 210, 160, 30, 49, 2, + 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 16, 134, 160, 30, 48, 2, + 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 12, 186, 159, 30, 48, 2, + 49, 2, 52, 2, 54, 2, 55, 3, 57, 18, 254, 158, 30, 48, 2, 49, 2, 50, 2, + 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 20, 82, 53, 218, 157, 30, 48, 2, + 49, 2, 50, 2, 51, 2, 52, 2, 54, 2, 56, 3, 57, 5, 215, 157, 30, 66, 24, + 18, 48, 87, 49, 18, 174, 157, 30, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, + 2, 55, 2, 56, 3, 57, 6, 218, 156, 30, 48, 2, 50, 3, 52, 184, 6, 140, 1, 9, 67, 65, 80, 73, 84, 65, 76, 32, 76, 202, 4, 75, 32, 7, 76, 69, 84, 84, 69, 82, 32, 132, 1, 3, 80, 65, 89, 30, 83, 211, 41, 84, 242, 2, 44, 6, 69, 84, 84, 69, 82, 32, 175, 43, 73, 236, 2, 150, 2, 76, 46, 78, 34, 80, 34, 82, 58, 84, 62, 85, 190, 5, 65, 214, 1, 66, 242, 1, 67, 150, 1, 68, 254, 1, 69, 206, 2, 71, 130, 1, 72, 134, 1, 73, 230, 3, 75, 226, 3, 77, - 218, 1, 79, 226, 2, 83, 218, 7, 89, 166, 1, 90, 162, 183, 29, 70, 134, - 14, 74, 2, 86, 2, 87, 159, 20, 81, 6, 222, 25, 73, 254, 210, 29, 74, 159, - 20, 72, 4, 134, 27, 69, 171, 209, 29, 74, 8, 134, 34, 69, 135, 205, 29, - 83, 12, 220, 9, 3, 79, 85, 78, 226, 18, 69, 207, 227, 29, 72, 20, 174, - 33, 69, 106, 83, 234, 178, 29, 67, 186, 22, 74, 3, 87, 13, 198, 34, 32, - 115, 75, 2, 205, 248, 29, 3, 65, 86, 89, 6, 248, 10, 5, 77, 85, 76, 84, - 73, 200, 129, 13, 2, 80, 65, 241, 231, 11, 14, 83, 77, 65, 76, 76, 32, - 67, 65, 80, 73, 84, 65, 76, 32, 2, 177, 214, 29, 2, 69, 82, 186, 3, 136, + 218, 1, 79, 226, 2, 83, 218, 7, 89, 166, 1, 90, 198, 204, 29, 70, 134, + 14, 74, 2, 86, 2, 87, 159, 20, 81, 6, 222, 25, 73, 162, 232, 29, 74, 159, + 20, 72, 4, 134, 27, 69, 207, 230, 29, 74, 8, 134, 34, 69, 171, 226, 29, + 83, 12, 220, 9, 3, 79, 85, 78, 226, 18, 69, 243, 248, 29, 72, 20, 174, + 33, 69, 106, 83, 142, 200, 29, 67, 186, 22, 74, 3, 87, 13, 198, 34, 32, + 115, 75, 2, 241, 141, 30, 3, 65, 86, 89, 6, 248, 10, 5, 77, 85, 76, 84, + 73, 244, 135, 13, 2, 80, 65, 157, 238, 11, 14, 83, 77, 65, 76, 76, 32, + 67, 65, 80, 73, 84, 65, 76, 32, 2, 213, 235, 29, 2, 69, 82, 186, 3, 136, 1, 6, 77, 65, 76, 76, 32, 76, 201, 37, 22, 85, 66, 83, 67, 82, 73, 80, 84, 32, 83, 77, 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, 32, 134, 3, 44, 6, 69, 84, 84, 69, 82, 32, 151, 36, 73, 128, 3, 154, 2, 65, 214, 1, 66, 242, 1, 67, 150, 1, 68, 254, 1, 69, 206, 2, 71, 130, 1, 72, 134, 1, 73, 230, 3, 75, 222, 2, 76, 134, 1, 77, 114, 78, 106, 79, 110, 80, 50, 82, - 198, 1, 83, 218, 2, 84, 218, 2, 85, 246, 1, 87, 54, 89, 166, 1, 90, 162, - 183, 29, 70, 134, 14, 74, 2, 86, 159, 20, 81, 17, 108, 6, 32, 87, 73, 84, - 72, 32, 36, 9, 66, 75, 72, 65, 83, 73, 65, 78, 32, 249, 152, 12, 4, 76, - 69, 85, 84, 4, 186, 194, 9, 68, 147, 182, 3, 66, 8, 172, 133, 13, 3, 67, - 72, 69, 158, 213, 6, 68, 255, 157, 10, 72, 18, 110, 65, 78, 73, 32, 3, - 82, 79, 65, 232, 160, 3, 6, 76, 69, 78, 68, 69, 68, 246, 224, 9, 89, 139, - 247, 16, 69, 6, 200, 21, 6, 82, 82, 69, 68, 32, 79, 253, 129, 12, 5, 83, - 72, 75, 73, 82, 4, 230, 2, 78, 163, 158, 3, 71, 2, 171, 179, 10, 68, 14, - 76, 2, 72, 69, 246, 9, 76, 220, 146, 13, 4, 82, 79, 83, 83, 167, 174, 16, - 67, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 150, 29, 68, 255, 169, 29, 86, - 26, 96, 2, 74, 69, 20, 6, 79, 85, 66, 76, 69, 32, 66, 90, 250, 200, 29, - 67, 186, 22, 87, 215, 22, 69, 5, 219, 230, 22, 82, 4, 36, 3, 77, 79, 78, - 159, 246, 29, 79, 2, 153, 13, 2, 79, 67, 12, 46, 69, 182, 245, 28, 90, - 206, 105, 72, 3, 87, 5, 223, 214, 29, 76, 41, 86, 76, 98, 78, 110, 82, - 166, 15, 32, 170, 238, 12, 83, 178, 193, 14, 77, 163, 180, 2, 70, 11, 33, - 6, 32, 87, 73, 84, 72, 32, 8, 166, 20, 77, 130, 235, 12, 68, 190, 193, - 14, 84, 143, 96, 72, 13, 33, 6, 32, 87, 73, 84, 72, 32, 10, 198, 19, 77, - 130, 235, 12, 68, 202, 52, 76, 246, 140, 14, 84, 143, 96, 72, 5, 133, - 195, 28, 5, 32, 87, 73, 84, 72, 14, 32, 2, 72, 69, 255, 219, 29, 74, 13, - 33, 6, 32, 87, 73, 84, 72, 32, 10, 142, 18, 77, 146, 15, 85, 242, 219, - 12, 68, 155, 31, 83, 12, 26, 65, 135, 219, 29, 87, 11, 48, 6, 32, 87, 73, - 84, 72, 32, 207, 136, 28, 82, 6, 134, 252, 12, 68, 202, 161, 15, 72, 219, - 163, 1, 83, 35, 84, 6, 32, 87, 73, 84, 72, 32, 50, 69, 74, 79, 201, 1, 6, - 90, 72, 73, 84, 83, 65, 6, 174, 183, 9, 68, 214, 10, 71, 243, 196, 3, 77, - 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 174, 193, 9, 71, 191, 171, 3, 66, - 17, 11, 84, 14, 48, 6, 73, 70, 73, 69, 68, 32, 227, 238, 29, 65, 12, 80, - 2, 67, 76, 38, 76, 190, 149, 3, 66, 198, 196, 25, 89, 234, 147, 1, 65, 3, - 69, 2, 33, 6, 79, 83, 69, 68, 32, 76, 2, 151, 4, 73, 5, 197, 136, 13, 14, + 198, 1, 83, 218, 2, 84, 218, 2, 85, 246, 1, 87, 54, 89, 166, 1, 90, 198, + 204, 29, 70, 134, 14, 74, 2, 86, 159, 20, 81, 17, 108, 6, 32, 87, 73, 84, + 72, 32, 36, 9, 66, 75, 72, 65, 83, 73, 65, 78, 32, 149, 159, 12, 4, 76, + 69, 85, 84, 4, 178, 195, 9, 68, 191, 187, 3, 66, 8, 216, 139, 13, 3, 67, + 72, 69, 210, 210, 6, 68, 195, 175, 10, 72, 18, 110, 65, 78, 73, 32, 3, + 82, 79, 65, 148, 163, 3, 6, 76, 69, 78, 68, 69, 68, 246, 228, 9, 89, 131, + 134, 17, 69, 6, 200, 21, 6, 82, 82, 69, 68, 32, 79, 153, 136, 12, 5, 83, + 72, 75, 73, 82, 4, 230, 2, 78, 207, 160, 3, 71, 2, 163, 180, 10, 68, 14, + 76, 2, 72, 69, 246, 9, 76, 136, 153, 13, 4, 82, 79, 83, 83, 159, 189, 16, + 67, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 150, 29, 68, 163, 191, 29, 86, + 26, 96, 2, 74, 69, 20, 6, 79, 85, 66, 76, 69, 32, 66, 90, 158, 222, 29, + 67, 186, 22, 87, 215, 22, 69, 5, 235, 237, 22, 82, 4, 36, 3, 77, 79, 78, + 195, 139, 30, 79, 2, 153, 13, 2, 79, 67, 12, 46, 69, 218, 138, 29, 90, + 206, 105, 72, 3, 87, 5, 131, 236, 29, 76, 41, 86, 76, 98, 78, 110, 82, + 166, 15, 32, 214, 244, 12, 83, 230, 204, 14, 77, 231, 183, 2, 70, 11, 33, + 6, 32, 87, 73, 84, 72, 32, 8, 166, 20, 77, 174, 241, 12, 68, 242, 204, + 14, 84, 183, 97, 72, 13, 33, 6, 32, 87, 73, 84, 72, 32, 10, 198, 19, 77, + 174, 241, 12, 68, 202, 52, 76, 170, 152, 14, 84, 183, 97, 72, 5, 149, + 214, 28, 5, 32, 87, 73, 84, 72, 14, 32, 2, 72, 69, 163, 241, 29, 74, 13, + 33, 6, 32, 87, 73, 84, 72, 32, 10, 142, 18, 77, 146, 15, 85, 158, 226, + 12, 68, 155, 31, 83, 12, 26, 65, 171, 240, 29, 87, 11, 48, 6, 32, 87, 73, + 84, 72, 32, 139, 155, 28, 82, 6, 178, 130, 13, 68, 166, 174, 15, 72, 247, + 165, 1, 83, 35, 84, 6, 32, 87, 73, 84, 72, 32, 50, 69, 74, 79, 201, 1, 6, + 90, 72, 73, 84, 83, 65, 6, 166, 184, 9, 68, 214, 10, 71, 167, 202, 3, 77, + 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 166, 194, 9, 71, 235, 176, 3, 66, + 17, 11, 84, 14, 48, 6, 73, 70, 73, 69, 68, 32, 135, 132, 30, 65, 12, 80, + 2, 67, 76, 38, 76, 234, 151, 3, 66, 246, 225, 25, 89, 178, 137, 1, 65, 3, + 69, 2, 33, 6, 79, 83, 69, 68, 32, 76, 2, 151, 4, 73, 5, 241, 142, 13, 14, 32, 87, 73, 84, 72, 32, 68, 79, 85, 66, 76, 69, 32, 71, 34, 102, 65, 98, - 79, 184, 190, 29, 11, 72, 65, 75, 65, 83, 83, 73, 65, 78, 32, 67, 186, - 22, 74, 255, 2, 83, 11, 33, 6, 32, 87, 73, 84, 72, 32, 8, 226, 246, 12, - 68, 202, 161, 15, 72, 142, 163, 1, 86, 79, 83, 18, 36, 3, 77, 73, 32, - 203, 177, 26, 80, 16, 58, 68, 246, 160, 12, 76, 2, 78, 2, 83, 2, 84, 3, - 90, 6, 242, 160, 12, 90, 154, 179, 17, 74, 215, 22, 69, 8, 94, 73, 228, - 200, 10, 10, 79, 78, 71, 45, 76, 69, 71, 71, 69, 68, 154, 138, 19, 74, - 159, 20, 72, 2, 209, 145, 3, 4, 84, 84, 76, 69, 4, 21, 3, 79, 78, 79, 4, - 18, 67, 39, 71, 2, 145, 132, 19, 4, 85, 76, 65, 82, 2, 245, 10, 4, 82, - 65, 80, 72, 6, 62, 69, 144, 131, 19, 5, 65, 82, 82, 79, 87, 155, 206, 10, - 74, 2, 165, 245, 12, 5, 85, 84, 82, 65, 76, 11, 52, 4, 77, 69, 71, 65, - 166, 3, 32, 251, 227, 29, 84, 5, 173, 190, 29, 8, 32, 87, 73, 84, 72, 32, - 84, 73, 10, 138, 6, 69, 138, 236, 12, 65, 255, 224, 16, 83, 14, 50, 69, - 100, 4, 79, 85, 78, 68, 235, 226, 29, 72, 8, 37, 7, 86, 69, 82, 83, 69, - 68, 32, 8, 166, 197, 19, 68, 170, 151, 9, 84, 166, 100, 89, 151, 14, 90, - 4, 242, 159, 10, 32, 137, 244, 1, 2, 69, 68, 34, 108, 4, 67, 72, 87, 65, - 34, 72, 132, 1, 4, 79, 70, 84, 32, 214, 238, 12, 84, 213, 1, 5, 69, 77, - 73, 83, 79, 5, 11, 32, 2, 187, 238, 5, 87, 16, 92, 4, 79, 82, 84, 32, - 180, 237, 12, 2, 72, 65, 146, 251, 14, 67, 238, 227, 1, 87, 215, 22, 65, - 6, 226, 174, 27, 73, 163, 180, 2, 85, 8, 38, 69, 210, 219, 28, 83, 151, - 112, 68, 4, 182, 226, 29, 76, 3, 77, 28, 140, 1, 4, 65, 76, 76, 32, 50, - 69, 106, 83, 224, 135, 12, 11, 72, 82, 69, 69, 45, 76, 69, 71, 71, 69, - 68, 138, 171, 17, 67, 186, 22, 74, 3, 87, 6, 214, 231, 12, 72, 206, 229, - 15, 89, 151, 125, 84, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 26, 77, 131, - 235, 12, 68, 2, 189, 244, 21, 5, 73, 68, 68, 76, 69, 8, 158, 201, 29, 72, + 79, 232, 206, 17, 10, 72, 65, 75, 65, 83, 83, 73, 65, 78, 32, 174, 155, + 12, 74, 255, 2, 83, 11, 33, 6, 32, 87, 73, 84, 72, 32, 8, 142, 253, 12, + 68, 166, 174, 15, 72, 170, 165, 1, 86, 79, 83, 18, 36, 3, 77, 73, 32, + 155, 195, 26, 80, 16, 58, 68, 254, 166, 12, 76, 2, 78, 2, 83, 2, 84, 3, + 90, 6, 250, 166, 12, 90, 182, 194, 17, 74, 215, 22, 69, 8, 94, 73, 224, + 201, 10, 10, 79, 78, 71, 45, 76, 69, 71, 71, 69, 68, 194, 158, 19, 74, + 159, 20, 72, 2, 253, 147, 3, 4, 84, 84, 76, 69, 4, 21, 3, 79, 78, 79, 4, + 18, 67, 39, 71, 2, 217, 134, 19, 4, 85, 76, 65, 82, 2, 245, 10, 4, 82, + 65, 80, 72, 6, 62, 69, 216, 133, 19, 5, 65, 82, 82, 79, 87, 247, 224, 10, + 74, 2, 209, 251, 12, 5, 85, 84, 82, 65, 76, 11, 52, 4, 77, 69, 71, 65, + 166, 3, 32, 159, 249, 29, 84, 5, 209, 211, 29, 8, 32, 87, 73, 84, 72, 32, + 84, 73, 10, 138, 6, 69, 182, 242, 12, 65, 247, 239, 16, 83, 14, 50, 69, + 100, 4, 79, 85, 78, 68, 143, 248, 29, 72, 8, 37, 7, 86, 69, 82, 83, 69, + 68, 32, 8, 134, 201, 19, 68, 214, 166, 9, 84, 190, 102, 89, 151, 14, 90, + 4, 234, 160, 10, 32, 153, 249, 1, 2, 69, 68, 34, 108, 4, 67, 72, 87, 65, + 34, 72, 132, 1, 4, 79, 70, 84, 32, 130, 245, 12, 84, 213, 1, 5, 69, 77, + 73, 83, 79, 5, 11, 32, 2, 167, 241, 5, 87, 16, 92, 4, 79, 82, 84, 32, + 224, 243, 12, 2, 72, 65, 162, 135, 15, 67, 214, 230, 1, 87, 215, 22, 65, + 6, 194, 192, 27, 73, 231, 183, 2, 85, 8, 38, 69, 246, 240, 28, 83, 151, + 112, 68, 4, 218, 247, 29, 76, 3, 77, 28, 140, 1, 4, 65, 76, 76, 32, 50, + 69, 106, 83, 232, 141, 12, 11, 72, 82, 69, 69, 45, 76, 69, 71, 71, 69, + 68, 166, 186, 17, 67, 186, 22, 74, 3, 87, 6, 130, 238, 12, 72, 254, 254, + 15, 89, 223, 114, 84, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 26, 77, 175, + 241, 12, 68, 2, 217, 251, 21, 5, 73, 68, 68, 76, 69, 8, 194, 222, 29, 72, 2, 83, 2, 87, 215, 22, 69, 15, 58, 32, 114, 75, 53, 8, 78, 66, 76, 69, - 78, 68, 69, 68, 6, 29, 5, 87, 73, 84, 72, 32, 6, 26, 68, 171, 245, 12, - 77, 4, 212, 167, 9, 5, 79, 85, 66, 76, 69, 187, 8, 73, 5, 11, 82, 2, 213, - 4, 6, 65, 73, 78, 73, 65, 78, 2, 207, 232, 24, 32, 4, 212, 201, 28, 4, - 73, 68, 69, 32, 251, 147, 1, 69, 18, 62, 65, 28, 3, 69, 82, 85, 194, 220, - 29, 73, 2, 78, 3, 85, 7, 218, 220, 29, 69, 3, 84, 7, 33, 6, 32, 87, 73, - 84, 72, 32, 4, 178, 163, 9, 68, 255, 197, 3, 66, 18, 18, 69, 71, 72, 9, - 156, 1, 7, 32, 87, 73, 84, 72, 32, 68, 233, 215, 29, 2, 77, 76, 10, 26, - 69, 179, 196, 29, 87, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 26, 68, 239, - 215, 12, 66, 4, 134, 172, 9, 73, 175, 185, 3, 69, 6, 37, 7, 71, 65, 84, - 85, 82, 69, 32, 6, 66, 65, 188, 198, 15, 2, 84, 69, 237, 229, 13, 4, 69, - 78, 32, 71, 2, 131, 190, 29, 32, 52, 198, 1, 66, 38, 68, 38, 69, 36, 3, - 71, 72, 69, 38, 72, 130, 137, 22, 89, 138, 211, 5, 83, 182, 113, 84, 238, - 8, 90, 130, 64, 73, 150, 19, 67, 186, 22, 80, 2, 86, 158, 20, 75, 186, 2, - 65, 2, 79, 3, 85, 4, 206, 224, 12, 89, 139, 247, 16, 69, 6, 146, 215, 28, - 90, 163, 128, 1, 69, 6, 142, 215, 29, 70, 2, 76, 3, 83, 5, 201, 5, 5, 32, - 87, 73, 84, 72, 4, 11, 65, 5, 227, 237, 27, 82, 2, 153, 248, 15, 4, 72, - 79, 85, 83, 212, 15, 178, 1, 65, 138, 4, 67, 54, 69, 146, 35, 73, 138, - 23, 79, 154, 45, 82, 174, 3, 85, 144, 232, 12, 13, 78, 65, 32, 68, 79, - 85, 66, 76, 69, 32, 72, 69, 76, 134, 183, 15, 86, 207, 47, 76, 30, 116, - 4, 71, 71, 69, 82, 146, 1, 78, 32, 4, 82, 75, 32, 83, 36, 2, 83, 72, 184, - 253, 21, 2, 76, 69, 139, 145, 1, 84, 9, 11, 32, 6, 44, 5, 87, 73, 84, 72, - 32, 219, 210, 6, 75, 4, 44, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, 2, 237, - 187, 28, 4, 84, 32, 71, 85, 4, 214, 148, 29, 67, 251, 30, 71, 4, 230, - 200, 21, 85, 211, 146, 5, 72, 10, 30, 32, 105, 3, 69, 68, 32, 4, 60, 9, - 87, 73, 84, 72, 32, 76, 69, 70, 84, 147, 190, 28, 83, 2, 17, 2, 32, 85, - 2, 143, 141, 23, 80, 6, 238, 233, 4, 84, 142, 189, 12, 76, 155, 194, 10, - 79, 10, 194, 208, 29, 49, 2, 50, 2, 51, 2, 52, 3, 83, 180, 4, 226, 1, 67, - 248, 1, 5, 71, 82, 69, 69, 32, 98, 76, 82, 78, 228, 3, 8, 80, 65, 82, 84, - 77, 69, 78, 84, 34, 83, 174, 6, 86, 192, 140, 15, 11, 82, 69, 76, 73, 67, - 84, 32, 72, 79, 85, 83, 168, 180, 10, 2, 65, 70, 143, 199, 3, 69, 8, 50, - 73, 177, 250, 5, 6, 82, 69, 65, 83, 69, 32, 6, 56, 4, 77, 65, 76, 32, - 205, 168, 9, 4, 68, 85, 79, 85, 4, 88, 9, 83, 69, 80, 65, 82, 65, 84, 79, - 82, 149, 248, 21, 7, 69, 88, 80, 79, 78, 69, 78, 2, 21, 3, 32, 75, 69, 2, - 155, 185, 28, 89, 6, 140, 194, 22, 4, 67, 69, 76, 83, 186, 131, 6, 83, - 177, 106, 8, 70, 65, 72, 82, 69, 78, 72, 69, 9, 248, 250, 16, 5, 73, 86, - 69, 82, 89, 248, 252, 5, 2, 84, 65, 231, 140, 5, 69, 34, 104, 20, 84, 73, + 78, 68, 69, 68, 6, 29, 5, 87, 73, 84, 72, 32, 6, 26, 68, 215, 251, 12, + 77, 4, 204, 168, 9, 5, 79, 85, 66, 76, 69, 187, 8, 73, 5, 11, 82, 2, 213, + 4, 6, 65, 73, 78, 73, 65, 78, 2, 159, 245, 24, 32, 4, 236, 220, 28, 4, + 73, 68, 69, 32, 135, 150, 1, 69, 18, 62, 65, 28, 3, 69, 82, 85, 230, 241, + 29, 73, 2, 78, 3, 85, 7, 254, 241, 29, 69, 3, 84, 7, 33, 6, 32, 87, 73, + 84, 72, 32, 4, 170, 164, 9, 68, 179, 203, 3, 66, 18, 18, 69, 71, 72, 9, + 156, 1, 7, 32, 87, 73, 84, 72, 32, 68, 141, 237, 29, 2, 77, 76, 10, 26, + 69, 215, 217, 29, 87, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 26, 68, 147, + 222, 12, 66, 4, 254, 172, 9, 73, 227, 190, 3, 69, 6, 37, 7, 71, 65, 84, + 85, 82, 69, 32, 6, 66, 65, 180, 200, 15, 2, 84, 69, 153, 249, 13, 4, 69, + 78, 32, 71, 2, 167, 211, 29, 32, 52, 198, 1, 66, 38, 68, 38, 69, 36, 3, + 71, 72, 69, 38, 72, 158, 144, 22, 89, 170, 222, 5, 83, 134, 114, 84, 134, + 11, 90, 130, 64, 73, 150, 19, 67, 186, 22, 80, 2, 86, 158, 20, 75, 186, + 2, 65, 2, 79, 3, 85, 4, 250, 230, 12, 89, 131, 134, 17, 69, 6, 182, 236, + 28, 90, 163, 128, 1, 69, 6, 178, 236, 29, 70, 2, 76, 3, 83, 5, 201, 5, 5, + 32, 87, 73, 84, 72, 4, 11, 65, 5, 159, 128, 28, 82, 2, 145, 250, 15, 4, + 72, 79, 85, 83, 214, 15, 178, 1, 65, 138, 4, 67, 54, 69, 154, 35, 73, + 166, 23, 79, 154, 45, 82, 174, 3, 85, 172, 238, 12, 13, 78, 65, 32, 68, + 79, 85, 66, 76, 69, 32, 72, 69, 76, 234, 197, 15, 86, 207, 47, 76, 30, + 116, 4, 71, 71, 69, 82, 146, 1, 78, 32, 4, 82, 75, 32, 83, 36, 2, 83, 72, + 212, 132, 22, 2, 76, 69, 131, 145, 1, 84, 9, 11, 32, 6, 44, 5, 87, 73, + 84, 72, 32, 247, 213, 6, 75, 4, 44, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, + 2, 141, 207, 28, 4, 84, 32, 71, 85, 4, 250, 169, 29, 67, 251, 30, 71, 4, + 242, 207, 21, 85, 155, 157, 5, 72, 10, 30, 32, 105, 3, 69, 68, 32, 4, 60, + 9, 87, 73, 84, 72, 32, 76, 69, 70, 84, 155, 209, 28, 83, 2, 17, 2, 32, + 85, 2, 163, 148, 23, 80, 6, 154, 236, 4, 84, 234, 188, 12, 76, 207, 210, + 10, 79, 10, 230, 229, 29, 49, 2, 50, 2, 51, 2, 52, 3, 83, 180, 4, 222, 1, + 67, 248, 1, 5, 71, 82, 69, 69, 32, 98, 76, 82, 78, 228, 3, 8, 80, 65, 82, + 84, 77, 69, 78, 84, 32, 12, 82, 69, 76, 73, 67, 84, 32, 72, 79, 85, 83, + 69, 42, 83, 174, 6, 86, 148, 209, 25, 2, 65, 70, 227, 203, 3, 69, 8, 50, + 73, 173, 253, 5, 6, 82, 69, 65, 83, 69, 32, 6, 56, 4, 77, 65, 76, 32, + 237, 169, 9, 4, 68, 85, 79, 85, 4, 88, 9, 83, 69, 80, 65, 82, 65, 84, 79, + 82, 181, 255, 21, 7, 69, 88, 80, 79, 78, 69, 78, 2, 21, 3, 32, 75, 69, 2, + 167, 204, 28, 89, 6, 164, 201, 22, 4, 67, 69, 76, 83, 202, 145, 6, 83, + 177, 106, 8, 70, 65, 72, 82, 69, 78, 72, 69, 9, 248, 252, 16, 5, 73, 86, + 69, 82, 89, 160, 130, 6, 2, 84, 65, 203, 152, 5, 69, 34, 104, 20, 84, 73, 83, 84, 82, 89, 32, 83, 89, 77, 66, 79, 76, 32, 76, 73, 71, 72, 84, 32, - 135, 237, 21, 83, 30, 88, 4, 68, 79, 87, 78, 0, 2, 85, 80, 153, 1, 9, 86, + 167, 244, 21, 83, 30, 88, 4, 68, 79, 87, 78, 0, 2, 85, 80, 153, 1, 9, 86, 69, 82, 84, 73, 67, 65, 76, 32, 8, 69, 15, 32, 65, 78, 68, 32, 72, 79, - 82, 73, 90, 79, 78, 84, 65, 76, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 150, - 221, 27, 87, 186, 25, 67, 159, 45, 84, 14, 52, 4, 65, 78, 68, 32, 45, 5, - 87, 73, 84, 72, 32, 10, 142, 156, 22, 66, 42, 84, 255, 191, 5, 87, 4, - 190, 245, 27, 67, 159, 45, 84, 2, 193, 226, 24, 3, 32, 83, 84, 170, 1, - 64, 2, 67, 69, 48, 2, 69, 82, 129, 5, 5, 75, 84, 79, 80, 32, 2, 153, 182, - 27, 7, 78, 68, 73, 78, 71, 32, 78, 164, 1, 32, 3, 69, 84, 32, 183, 4, 84, - 160, 1, 56, 6, 67, 65, 80, 73, 84, 65, 1, 4, 83, 77, 65, 76, 80, 45, 9, - 76, 32, 76, 69, 84, 84, 69, 82, 32, 80, 218, 1, 69, 96, 4, 76, 79, 78, - 71, 0, 5, 83, 72, 79, 82, 84, 74, 79, 30, 84, 2, 90, 158, 231, 11, 67, - 250, 234, 15, 66, 2, 68, 2, 74, 2, 80, 2, 86, 2, 89, 182, 99, 71, 2, 75, - 158, 103, 87, 162, 19, 65, 203, 17, 72, 20, 242, 162, 25, 83, 134, 165, - 2, 78, 138, 250, 1, 84, 146, 1, 70, 2, 76, 2, 77, 2, 82, 3, 87, 12, 11, - 32, 12, 134, 162, 25, 65, 190, 218, 1, 79, 226, 197, 2, 69, 3, 73, 4, - 230, 193, 29, 73, 3, 87, 4, 146, 210, 27, 72, 231, 216, 1, 69, 5, 161, - 179, 28, 4, 32, 73, 83, 76, 4, 158, 198, 26, 67, 193, 224, 1, 4, 87, 73, - 78, 68, 202, 2, 100, 8, 65, 78, 65, 71, 65, 82, 73, 32, 161, 18, 12, 73, - 67, 69, 32, 67, 79, 78, 84, 82, 79, 76, 32, 192, 2, 222, 1, 65, 38, 67, - 22, 71, 36, 4, 72, 69, 65, 68, 96, 7, 76, 69, 84, 84, 69, 82, 32, 198, 5, - 83, 148, 7, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 154, 129, 19, - 68, 212, 129, 6, 4, 74, 65, 73, 78, 171, 156, 4, 79, 4, 210, 216, 12, 67, - 151, 206, 12, 66, 2, 159, 146, 6, 65, 4, 194, 216, 12, 82, 135, 238, 1, - 65, 6, 44, 5, 32, 77, 65, 82, 75, 251, 140, 29, 83, 5, 181, 143, 19, 7, - 32, 87, 73, 84, 72, 32, 72, 158, 1, 158, 2, 65, 54, 67, 62, 68, 50, 71, - 62, 72, 52, 2, 77, 65, 46, 83, 130, 10, 79, 246, 147, 7, 66, 206, 252, 4, - 75, 150, 202, 3, 82, 238, 232, 6, 89, 154, 119, 85, 174, 30, 78, 254, - 198, 1, 74, 150, 52, 76, 70, 84, 46, 86, 134, 207, 1, 73, 158, 194, 1, - 80, 2, 90, 138, 69, 70, 2, 81, 187, 2, 69, 13, 158, 186, 29, 65, 2, 73, - 2, 85, 2, 87, 3, 89, 10, 26, 65, 155, 183, 29, 72, 9, 185, 2, 4, 78, 68, - 82, 65, 12, 178, 223, 25, 68, 198, 215, 3, 72, 187, 2, 65, 10, 130, 238, - 12, 76, 190, 131, 16, 72, 138, 69, 71, 187, 2, 65, 4, 232, 210, 16, 4, - 69, 65, 86, 89, 219, 229, 12, 65, 5, 233, 154, 7, 6, 82, 87, 65, 82, 73, - 32, 12, 38, 72, 134, 181, 29, 83, 187, 2, 65, 8, 36, 3, 79, 82, 84, 151, - 183, 29, 65, 6, 199, 136, 12, 32, 68, 168, 1, 19, 69, 81, 85, 69, 78, 67, - 69, 32, 70, 79, 82, 32, 76, 69, 84, 84, 69, 82, 32, 104, 4, 73, 71, 78, - 32, 133, 254, 26, 10, 84, 82, 69, 83, 83, 32, 83, 73, 71, 78, 16, 174, - 217, 3, 71, 2, 75, 228, 225, 23, 3, 68, 68, 68, 2, 82, 230, 247, 1, 89, - 38, 70, 2, 81, 3, 90, 48, 178, 3, 66, 0, 10, 69, 88, 84, 69, 78, 68, 69, - 68, 32, 66, 36, 11, 67, 65, 78, 68, 82, 65, 66, 73, 78, 68, 85, 64, 8, - 87, 69, 83, 84, 69, 82, 78, 32, 32, 10, 82, 69, 86, 69, 82, 83, 69, 68, - 32, 78, 226, 200, 6, 83, 190, 190, 12, 73, 142, 149, 6, 77, 46, 78, 242, - 60, 65, 72, 18, 68, 79, 85, 66, 76, 69, 32, 67, 65, 78, 68, 82, 65, 66, - 73, 78, 68, 85, 62, 80, 216, 196, 2, 12, 72, 73, 71, 72, 32, 83, 80, 65, - 67, 73, 78, 71, 139, 78, 86, 4, 241, 222, 12, 4, 72, 65, 76, 69, 11, 11, - 32, 8, 134, 194, 22, 65, 238, 152, 3, 86, 183, 230, 1, 84, 4, 30, 78, 21, - 3, 70, 73, 86, 2, 17, 2, 73, 78, 2, 129, 221, 21, 8, 69, 45, 76, 73, 75, - 69, 32, 66, 50, 150, 1, 65, 52, 7, 67, 65, 78, 68, 82, 65, 32, 62, 79, - 186, 134, 19, 80, 162, 180, 4, 85, 158, 224, 1, 83, 222, 63, 86, 186, - 201, 1, 73, 223, 137, 2, 69, 10, 210, 174, 29, 65, 2, 73, 2, 85, 2, 87, - 3, 89, 6, 172, 135, 19, 4, 76, 79, 78, 71, 242, 166, 10, 69, 3, 79, 7, - 142, 151, 29, 79, 215, 22, 69, 10, 194, 154, 24, 70, 136, 6, 2, 83, 84, - 154, 157, 3, 84, 183, 86, 79, 230, 2, 182, 2, 65, 150, 2, 69, 74, 71, + 82, 73, 90, 79, 78, 84, 65, 76, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 214, + 239, 27, 87, 134, 26, 67, 167, 45, 84, 14, 52, 4, 65, 78, 68, 32, 45, 5, + 87, 73, 84, 72, 32, 10, 162, 163, 22, 66, 42, 84, 171, 203, 5, 87, 4, + 202, 136, 28, 67, 167, 45, 84, 2, 177, 239, 24, 3, 32, 83, 84, 2, 237, + 149, 23, 5, 32, 66, 85, 73, 76, 170, 1, 64, 2, 67, 69, 48, 2, 69, 82, + 129, 5, 5, 75, 84, 79, 80, 32, 2, 177, 200, 27, 7, 78, 68, 73, 78, 71, + 32, 78, 164, 1, 32, 3, 69, 84, 32, 183, 4, 84, 160, 1, 56, 6, 67, 65, 80, + 73, 84, 65, 1, 4, 83, 77, 65, 76, 80, 45, 9, 76, 32, 76, 69, 84, 84, 69, + 82, 32, 80, 218, 1, 69, 96, 4, 76, 79, 78, 71, 0, 5, 83, 72, 79, 82, 84, + 74, 79, 30, 84, 2, 90, 158, 237, 11, 67, 146, 247, 15, 66, 2, 68, 2, 74, + 2, 80, 2, 86, 2, 89, 130, 100, 71, 2, 75, 186, 105, 87, 162, 19, 65, 203, + 17, 72, 20, 174, 199, 25, 83, 226, 146, 2, 78, 242, 252, 1, 84, 146, 1, + 70, 2, 76, 2, 77, 2, 82, 3, 87, 12, 11, 32, 12, 194, 198, 25, 65, 178, + 199, 1, 79, 178, 201, 2, 69, 3, 73, 4, 230, 214, 29, 73, 3, 87, 4, 170, + 228, 27, 72, 207, 219, 1, 69, 5, 133, 198, 28, 4, 32, 73, 83, 76, 4, 206, + 215, 26, 67, 141, 226, 1, 4, 87, 73, 78, 68, 202, 2, 100, 8, 65, 78, 65, + 71, 65, 82, 73, 32, 133, 18, 12, 73, 67, 69, 32, 67, 79, 78, 84, 82, 79, + 76, 32, 192, 2, 222, 1, 65, 38, 67, 22, 71, 36, 4, 72, 69, 65, 68, 96, 7, + 76, 69, 84, 84, 69, 82, 32, 202, 5, 83, 148, 7, 11, 86, 79, 87, 69, 76, + 32, 83, 73, 71, 78, 32, 238, 131, 19, 68, 160, 139, 6, 4, 74, 65, 73, 78, + 135, 165, 4, 79, 4, 218, 222, 12, 67, 239, 211, 12, 66, 2, 147, 149, 6, + 65, 4, 202, 222, 12, 82, 151, 238, 1, 65, 6, 44, 5, 32, 77, 65, 82, 75, + 251, 161, 29, 83, 5, 141, 146, 19, 7, 32, 87, 73, 84, 72, 32, 72, 158, 1, + 162, 2, 65, 54, 67, 62, 68, 50, 71, 62, 72, 52, 2, 77, 65, 46, 83, 170, + 157, 7, 66, 150, 131, 5, 75, 130, 198, 3, 82, 150, 173, 3, 79, 238, 192, + 3, 89, 214, 118, 85, 250, 35, 78, 138, 199, 1, 74, 174, 57, 76, 70, 84, + 46, 86, 242, 207, 1, 73, 134, 197, 1, 80, 2, 90, 138, 69, 70, 2, 81, 187, + 2, 69, 13, 154, 207, 29, 65, 2, 73, 2, 85, 2, 87, 3, 89, 10, 26, 65, 151, + 204, 29, 72, 9, 185, 2, 4, 78, 68, 82, 65, 12, 218, 240, 25, 68, 154, + 219, 3, 72, 187, 2, 65, 10, 134, 244, 12, 76, 182, 146, 16, 72, 138, 69, + 71, 187, 2, 65, 4, 188, 212, 16, 4, 69, 65, 86, 89, 131, 249, 12, 65, 5, + 157, 154, 7, 6, 82, 87, 65, 82, 73, 32, 12, 38, 72, 130, 202, 29, 83, + 187, 2, 65, 8, 36, 3, 79, 82, 84, 147, 204, 29, 65, 6, 167, 142, 12, 32, + 68, 168, 1, 19, 69, 81, 85, 69, 78, 67, 69, 32, 70, 79, 82, 32, 76, 69, + 84, 84, 69, 82, 32, 104, 4, 73, 71, 78, 32, 189, 143, 27, 10, 84, 82, 69, + 83, 83, 32, 83, 73, 71, 78, 16, 158, 219, 3, 71, 2, 75, 136, 242, 23, 3, + 68, 68, 68, 2, 82, 206, 250, 1, 89, 38, 70, 2, 81, 3, 90, 48, 178, 3, 66, + 0, 10, 69, 88, 84, 69, 78, 68, 69, 68, 32, 66, 36, 11, 67, 65, 78, 68, + 82, 65, 66, 73, 78, 68, 85, 64, 8, 87, 69, 83, 84, 69, 82, 78, 32, 32, + 10, 82, 69, 86, 69, 82, 83, 69, 68, 32, 78, 214, 203, 6, 83, 158, 190, + 12, 73, 150, 158, 6, 77, 46, 78, 190, 66, 65, 72, 18, 68, 79, 85, 66, 76, + 69, 32, 67, 65, 78, 68, 82, 65, 66, 73, 78, 68, 85, 62, 80, 144, 198, 2, + 12, 72, 73, 71, 72, 32, 83, 80, 65, 67, 73, 78, 71, 167, 80, 86, 4, 245, + 228, 12, 4, 72, 65, 76, 69, 11, 11, 32, 8, 130, 201, 22, 65, 154, 163, 3, + 86, 163, 231, 1, 84, 4, 30, 78, 21, 3, 70, 73, 86, 2, 17, 2, 73, 78, 2, + 245, 227, 21, 8, 69, 45, 76, 73, 75, 69, 32, 66, 50, 150, 1, 65, 52, 7, + 67, 65, 78, 68, 82, 65, 32, 142, 138, 19, 79, 34, 80, 162, 183, 4, 85, + 194, 229, 1, 83, 170, 69, 86, 166, 202, 1, 73, 199, 140, 2, 69, 10, 206, + 195, 29, 65, 2, 73, 2, 85, 2, 87, 3, 89, 6, 228, 138, 19, 4, 76, 79, 78, + 71, 182, 184, 10, 69, 3, 79, 10, 146, 167, 24, 70, 136, 6, 2, 83, 84, + 254, 162, 3, 84, 155, 87, 79, 232, 2, 182, 2, 65, 150, 2, 69, 74, 71, 224, 4, 6, 78, 71, 66, 65, 84, 32, 248, 1, 5, 82, 69, 67, 84, 32, 98, 83, - 178, 2, 86, 200, 7, 2, 89, 65, 32, 4, 90, 90, 89, 32, 224, 172, 5, 2, 84, - 84, 184, 156, 15, 10, 70, 70, 69, 82, 69, 78, 67, 69, 32, 66, 173, 205, - 7, 12, 77, 69, 78, 83, 73, 79, 78, 32, 79, 82, 73, 71, 20, 42, 77, 242, - 251, 8, 69, 183, 134, 13, 71, 16, 40, 4, 79, 78, 68, 32, 207, 152, 3, 69, - 14, 128, 1, 5, 87, 73, 84, 72, 32, 186, 164, 18, 84, 164, 224, 2, 12, 83, - 72, 65, 80, 69, 32, 87, 73, 84, 72, 32, 65, 183, 228, 4, 79, 8, 190, 128, - 22, 66, 182, 2, 84, 238, 137, 4, 76, 27, 82, 14, 176, 131, 5, 5, 32, 70, - 65, 67, 69, 133, 161, 17, 4, 83, 69, 76, 32, 78, 64, 3, 73, 84, 32, 149, + 206, 2, 86, 200, 7, 2, 89, 65, 32, 4, 90, 90, 89, 32, 180, 175, 5, 2, 84, + 84, 204, 160, 15, 10, 70, 70, 69, 82, 69, 78, 67, 69, 32, 66, 197, 219, + 7, 12, 77, 69, 78, 83, 73, 79, 78, 32, 79, 82, 73, 71, 20, 42, 77, 226, + 252, 8, 69, 207, 140, 13, 71, 16, 40, 4, 79, 78, 68, 32, 247, 154, 3, 69, + 14, 128, 1, 5, 87, 73, 84, 72, 32, 250, 166, 18, 84, 232, 228, 2, 12, 83, + 72, 65, 80, 69, 32, 87, 73, 84, 72, 32, 65, 251, 238, 4, 79, 8, 198, 135, + 22, 66, 182, 2, 84, 174, 148, 4, 76, 27, 82, 14, 132, 134, 5, 5, 32, 70, + 65, 67, 69, 189, 165, 17, 4, 83, 69, 76, 32, 78, 64, 3, 73, 84, 32, 149, 2, 8, 82, 65, 77, 32, 70, 79, 82, 32, 60, 98, 70, 44, 2, 78, 73, 2, 79, 14, 83, 46, 84, 44, 3, 90, 69, 82, 13, 5, 69, 73, 71, 72, 84, 12, 128, 1, 2, 73, 86, 25, 3, 79, 85, 82, 6, 87, 78, 12, 96, 4, 69, 86, 69, 78, 1, 2, - 73, 88, 12, 28, 3, 72, 82, 69, 15, 87, 6, 23, 69, 6, 11, 79, 7, 143, 247, + 73, 88, 12, 28, 3, 72, 82, 69, 15, 87, 6, 23, 69, 6, 11, 79, 7, 143, 249, 16, 32, 18, 88, 5, 69, 65, 82, 84, 72, 64, 5, 71, 82, 69, 65, 84, 0, 4, - 76, 69, 83, 83, 39, 72, 7, 25, 4, 76, 89, 32, 72, 4, 194, 174, 24, 85, - 147, 141, 1, 69, 4, 169, 132, 13, 4, 69, 82, 32, 89, 4, 188, 148, 5, 7, + 76, 69, 83, 83, 39, 72, 7, 25, 4, 76, 89, 32, 72, 4, 138, 187, 24, 85, + 239, 145, 1, 69, 4, 193, 138, 13, 4, 69, 82, 32, 89, 4, 148, 151, 5, 7, 69, 65, 86, 69, 78, 76, 89, 1, 4, 85, 77, 65, 78, 64, 120, 17, 78, 69, 71, 65, 84, 73, 86, 69, 32, 67, 73, 82, 67, 76, 69, 68, 32, 41, 9, 67, - 73, 82, 67, 76, 69, 68, 32, 83, 42, 38, 83, 154, 32, 78, 151, 196, 20, - 68, 22, 49, 10, 65, 78, 83, 45, 83, 69, 82, 73, 70, 32, 22, 226, 31, 78, - 175, 144, 27, 68, 4, 148, 233, 22, 15, 67, 85, 82, 82, 69, 78, 84, 32, - 83, 89, 77, 66, 79, 76, 32, 195, 155, 6, 72, 10, 70, 65, 144, 1, 5, 67, - 79, 78, 84, 73, 197, 235, 25, 3, 71, 85, 73, 6, 76, 9, 80, 80, 79, 73, - 78, 84, 69, 68, 32, 133, 132, 27, 4, 66, 76, 69, 68, 4, 252, 159, 17, 7, - 66, 85, 84, 32, 82, 69, 76, 147, 194, 11, 70, 2, 53, 11, 78, 85, 79, 85, - 83, 32, 85, 78, 68, 69, 82, 2, 137, 130, 17, 3, 76, 73, 78, 156, 1, 80, - 9, 69, 83, 32, 65, 75, 85, 82, 85, 32, 238, 5, 73, 157, 251, 16, 2, 79, - 82, 144, 1, 142, 2, 68, 36, 7, 76, 69, 84, 84, 69, 82, 32, 236, 1, 5, 83, - 73, 71, 78, 32, 58, 86, 140, 162, 9, 6, 77, 69, 68, 73, 65, 76, 182, 255, - 4, 71, 222, 232, 4, 69, 128, 203, 4, 12, 80, 82, 69, 70, 73, 88, 69, 68, - 32, 78, 65, 83, 217, 248, 4, 7, 73, 78, 73, 84, 73, 65, 76, 22, 174, 190, - 25, 79, 211, 235, 1, 73, 84, 142, 195, 10, 84, 190, 237, 11, 89, 162, - 126, 65, 138, 146, 2, 68, 214, 6, 85, 206, 201, 1, 73, 42, 76, 250, 192, - 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 75, 2, 80, 138, 69, 72, 2, 74, 2, - 77, 2, 82, 2, 86, 2, 90, 186, 2, 69, 3, 79, 8, 174, 217, 24, 72, 150, 43, - 67, 98, 78, 235, 206, 3, 65, 18, 64, 10, 79, 87, 69, 76, 32, 83, 73, 71, - 78, 32, 179, 223, 26, 73, 16, 134, 150, 15, 65, 218, 174, 10, 85, 206, - 201, 1, 73, 222, 137, 2, 69, 3, 79, 10, 68, 5, 83, 73, 79, 78, 32, 212, - 241, 1, 2, 78, 71, 171, 145, 26, 68, 6, 26, 83, 227, 227, 5, 84, 4, 170, - 174, 27, 76, 211, 97, 73, 2, 165, 132, 19, 3, 32, 76, 65, 4, 134, 131, - 28, 83, 139, 86, 70, 212, 5, 188, 2, 6, 67, 85, 77, 69, 78, 84, 124, 7, - 69, 83, 32, 78, 79, 84, 32, 194, 3, 71, 202, 3, 76, 36, 10, 77, 73, 78, - 79, 32, 84, 73, 76, 69, 32, 226, 1, 78, 38, 84, 246, 2, 85, 204, 17, 2, - 87, 78, 164, 189, 16, 8, 32, 78, 79, 84, 32, 76, 73, 84, 156, 250, 11, 8, - 86, 69, 32, 79, 70, 32, 80, 69, 174, 4, 79, 235, 25, 68, 9, 33, 6, 32, - 87, 73, 84, 72, 32, 6, 40, 4, 84, 69, 88, 84, 139, 142, 2, 80, 5, 133, - 142, 2, 6, 32, 65, 78, 68, 32, 80, 22, 164, 1, 11, 67, 79, 78, 84, 65, - 73, 78, 32, 65, 83, 32, 92, 6, 68, 73, 86, 73, 68, 69, 112, 2, 80, 82, - 48, 7, 83, 85, 67, 67, 69, 69, 68, 253, 213, 22, 2, 70, 79, 6, 248, 1, - 15, 78, 79, 82, 77, 65, 76, 32, 83, 85, 66, 71, 82, 79, 85, 80, 231, 245, - 20, 77, 5, 217, 249, 21, 23, 32, 87, 73, 84, 72, 32, 82, 69, 86, 69, 82, - 83, 69, 68, 32, 78, 69, 71, 65, 84, 73, 79, 78, 6, 44, 5, 69, 67, 69, 68, - 69, 195, 146, 28, 79, 5, 233, 158, 9, 2, 32, 79, 125, 36, 3, 82, 65, 32, - 191, 209, 28, 32, 120, 120, 7, 76, 69, 84, 84, 69, 82, 32, 208, 1, 11, - 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 170, 161, 23, 65, 187, 2, 83, - 88, 178, 179, 25, 65, 38, 68, 82, 82, 34, 84, 230, 5, 85, 206, 201, 1, - 73, 162, 193, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, - 138, 69, 72, 2, 76, 2, 77, 2, 86, 2, 89, 186, 2, 69, 3, 79, 22, 194, 140, - 19, 86, 246, 171, 6, 65, 38, 85, 206, 201, 1, 73, 222, 137, 2, 69, 3, 79, - 4, 202, 141, 21, 80, 183, 188, 2, 76, 200, 1, 72, 8, 72, 79, 82, 73, 90, - 79, 78, 84, 1, 6, 86, 69, 82, 84, 73, 67, 100, 17, 2, 65, 76, 100, 32, 2, - 45, 48, 155, 251, 23, 32, 98, 58, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, - 3, 54, 14, 205, 166, 20, 2, 45, 48, 4, 174, 239, 26, 75, 255, 146, 1, 71, - 26, 34, 32, 57, 4, 84, 69, 68, 32, 8, 170, 130, 25, 80, 34, 77, 194, 70, - 79, 239, 194, 2, 65, 18, 214, 1, 67, 34, 83, 220, 145, 16, 3, 76, 73, 78, - 148, 133, 1, 4, 79, 66, 69, 76, 244, 9, 9, 84, 82, 65, 78, 83, 80, 79, - 83, 73, 238, 220, 6, 70, 141, 41, 14, 82, 73, 71, 72, 84, 45, 80, 79, 73, - 78, 84, 73, 78, 71, 4, 226, 180, 27, 73, 231, 16, 82, 4, 134, 243, 26, - 79, 223, 114, 81, 162, 1, 40, 3, 66, 76, 69, 137, 17, 2, 71, 72, 160, 1, - 42, 32, 190, 10, 45, 241, 5, 2, 68, 32, 106, 226, 2, 67, 174, 1, 68, 38, - 72, 32, 4, 73, 78, 84, 69, 38, 76, 144, 1, 7, 78, 69, 83, 84, 69, 68, 32, - 74, 80, 66, 83, 130, 2, 85, 36, 9, 86, 69, 82, 84, 73, 67, 65, 76, 32, - 144, 161, 6, 6, 79, 66, 76, 73, 81, 85, 196, 189, 2, 9, 82, 73, 71, 72, - 84, 32, 65, 82, 67, 246, 183, 3, 65, 242, 215, 9, 69, 182, 188, 4, 81, - 177, 105, 6, 87, 65, 86, 89, 32, 79, 24, 100, 7, 73, 82, 67, 76, 69, 68, - 32, 180, 207, 7, 4, 85, 82, 76, 89, 241, 196, 1, 4, 79, 76, 79, 78, 20, - 26, 78, 151, 196, 20, 68, 2, 157, 213, 2, 5, 85, 77, 66, 69, 82, 4, 190, - 236, 18, 79, 135, 199, 6, 65, 4, 198, 190, 20, 73, 223, 63, 89, 4, 154, - 130, 26, 82, 251, 244, 1, 71, 12, 54, 79, 129, 156, 17, 7, 69, 70, 84, - 32, 65, 82, 67, 10, 26, 87, 199, 190, 25, 71, 6, 26, 45, 139, 230, 27, - 32, 4, 174, 189, 20, 82, 211, 1, 57, 6, 224, 137, 17, 9, 76, 69, 83, 83, - 45, 84, 72, 65, 78, 179, 227, 9, 71, 8, 26, 82, 207, 248, 26, 76, 6, 242, - 157, 1, 69, 207, 235, 15, 73, 18, 80, 6, 81, 85, 65, 82, 69, 32, 30, 84, - 50, 85, 149, 215, 13, 4, 79, 76, 73, 68, 4, 230, 205, 25, 73, 55, 85, 4, - 204, 191, 12, 3, 65, 67, 75, 135, 201, 4, 82, 8, 58, 83, 146, 156, 1, 67, - 222, 136, 21, 80, 151, 236, 4, 66, 2, 169, 147, 28, 4, 80, 69, 78, 83, 4, - 214, 231, 18, 80, 139, 196, 9, 78, 10, 36, 3, 66, 65, 82, 163, 226, 27, - 76, 9, 11, 32, 6, 52, 7, 68, 79, 85, 66, 76, 69, 32, 151, 213, 26, 76, 4, - 146, 213, 26, 76, 51, 82, 48, 112, 5, 76, 73, 78, 69, 32, 220, 1, 7, 83, - 84, 82, 85, 67, 75, 32, 141, 226, 8, 7, 69, 78, 68, 69, 68, 32, 77, 12, - 48, 8, 83, 76, 65, 78, 84, 69, 68, 32, 75, 69, 8, 70, 69, 56, 7, 71, 82, - 69, 65, 84, 69, 82, 1, 4, 76, 69, 83, 83, 4, 185, 179, 20, 9, 81, 85, 65, - 76, 32, 84, 79, 32, 79, 2, 221, 165, 14, 5, 45, 84, 72, 65, 78, 34, 160, - 1, 8, 67, 65, 80, 73, 84, 65, 76, 32, 92, 7, 73, 84, 65, 76, 73, 67, 32, - 124, 6, 83, 77, 65, 76, 76, 32, 245, 208, 13, 8, 78, 45, 65, 82, 89, 32, - 83, 85, 18, 198, 163, 12, 71, 226, 201, 14, 80, 222, 137, 2, 67, 2, 72, - 2, 78, 2, 81, 2, 82, 3, 90, 10, 76, 6, 83, 77, 65, 76, 76, 32, 201, 230, - 21, 7, 67, 65, 80, 73, 84, 65, 76, 8, 214, 245, 28, 68, 2, 69, 2, 73, 3, - 74, 4, 238, 161, 12, 71, 231, 191, 16, 80, 6, 218, 161, 10, 70, 26, 77, - 135, 192, 17, 83, 2, 139, 251, 27, 78, 166, 1, 50, 32, 158, 1, 45, 189, - 1, 4, 87, 65, 82, 68, 10, 60, 4, 84, 65, 67, 75, 226, 206, 25, 70, 30, - 82, 191, 79, 65, 5, 29, 5, 32, 87, 73, 84, 72, 2, 11, 32, 2, 11, 67, 2, - 129, 245, 20, 4, 73, 82, 67, 76, 28, 60, 9, 80, 79, 73, 78, 84, 73, 78, - 71, 32, 211, 207, 25, 70, 24, 62, 83, 246, 209, 25, 65, 86, 69, 62, 70, - 46, 82, 207, 1, 84, 4, 142, 180, 17, 84, 205, 160, 8, 6, 77, 65, 76, 76, - 32, 82, 128, 1, 56, 8, 32, 70, 65, 67, 73, 78, 71, 32, 89, 2, 83, 32, 8, - 54, 72, 1, 9, 78, 79, 84, 67, 72, 69, 68, 32, 72, 4, 181, 204, 27, 3, 79, - 79, 75, 120, 178, 1, 65, 200, 2, 6, 66, 76, 65, 67, 75, 32, 38, 84, 172, - 237, 8, 2, 87, 72, 150, 154, 8, 90, 218, 250, 8, 68, 50, 70, 82, 72, 150, - 4, 67, 46, 81, 42, 82, 22, 83, 247, 7, 80, 34, 40, 4, 82, 82, 79, 87, - 179, 253, 25, 78, 33, 11, 32, 30, 144, 1, 5, 87, 73, 84, 72, 32, 160, - 228, 13, 14, 76, 69, 70, 84, 87, 65, 82, 68, 83, 32, 79, 70, 32, 85, 166, - 153, 12, 65, 178, 10, 70, 179, 5, 84, 22, 184, 183, 5, 6, 67, 79, 82, 78, - 69, 82, 182, 200, 20, 68, 58, 76, 42, 77, 38, 78, 58, 83, 66, 69, 250, - 12, 84, 139, 58, 72, 6, 178, 130, 21, 65, 175, 255, 4, 67, 36, 36, 2, 82, - 73, 161, 2, 2, 87, 79, 32, 44, 5, 65, 78, 71, 76, 69, 143, 144, 26, 80, - 30, 56, 8, 45, 72, 69, 65, 68, 69, 68, 32, 203, 149, 26, 32, 28, 68, 5, - 65, 82, 82, 79, 87, 230, 133, 17, 90, 166, 136, 9, 68, 39, 80, 23, 11, - 32, 20, 184, 137, 26, 15, 76, 69, 70, 84, 87, 65, 82, 68, 83, 32, 79, 70, - 32, 85, 80, 98, 84, 23, 87, 4, 134, 143, 26, 32, 105, 15, 45, 72, 69, 65, - 68, 69, 68, 32, 65, 82, 82, 79, 87, 32, 87, 22, 138, 1, 65, 106, 79, 152, - 1, 12, 85, 77, 32, 87, 73, 84, 72, 32, 68, 82, 85, 77, 160, 242, 16, 6, - 73, 86, 69, 32, 83, 76, 231, 220, 10, 69, 8, 238, 202, 2, 67, 200, 147, - 6, 10, 70, 84, 73, 78, 71, 32, 80, 79, 73, 78, 133, 235, 15, 3, 71, 79, - 78, 8, 56, 6, 77, 69, 68, 65, 82, 89, 34, 80, 159, 177, 27, 79, 2, 217, - 234, 20, 3, 32, 67, 65, 4, 204, 181, 18, 6, 32, 79, 70, 32, 66, 76, 171, - 251, 9, 76, 2, 189, 217, 12, 3, 83, 84, 73, 162, 2, 76, 7, 80, 76, 79, - 89, 65, 78, 32, 244, 132, 20, 2, 77, 80, 215, 220, 8, 67, 158, 2, 212, 2, - 6, 65, 70, 70, 73, 88, 32, 160, 7, 7, 76, 69, 84, 84, 69, 82, 32, 148, - 194, 1, 16, 84, 72, 73, 67, 75, 32, 76, 69, 84, 84, 69, 82, 32, 83, 69, - 76, 180, 174, 3, 4, 68, 79, 85, 66, 220, 230, 16, 19, 80, 85, 78, 67, 84, - 85, 65, 84, 73, 79, 78, 32, 67, 72, 73, 78, 79, 79, 75, 149, 194, 5, 11, - 83, 73, 71, 78, 32, 79, 32, 87, 73, 84, 72, 64, 140, 1, 9, 65, 84, 84, - 65, 67, 72, 69, 68, 32, 184, 1, 5, 72, 73, 71, 72, 32, 106, 76, 40, 4, - 82, 73, 71, 72, 173, 2, 4, 77, 73, 68, 32, 14, 112, 2, 84, 65, 232, 4, - 13, 76, 69, 70, 84, 45, 84, 79, 45, 82, 73, 71, 72, 84, 26, 83, 166, 239, - 20, 69, 3, 73, 6, 44, 5, 78, 71, 69, 78, 84, 159, 214, 27, 73, 5, 255, - 243, 20, 32, 20, 174, 2, 76, 50, 84, 38, 86, 166, 174, 8, 71, 186, 2, 65, - 170, 192, 18, 87, 186, 25, 67, 199, 129, 1, 68, 24, 36, 2, 69, 70, 29, 3, - 79, 87, 32, 2, 213, 2, 3, 84, 32, 72, 22, 94, 65, 38, 76, 50, 84, 38, 86, - 166, 174, 8, 71, 226, 194, 18, 87, 186, 25, 67, 199, 129, 1, 68, 4, 186, - 229, 21, 67, 199, 221, 5, 82, 4, 132, 128, 8, 3, 79, 78, 71, 219, 195, - 19, 73, 2, 229, 165, 8, 4, 73, 71, 72, 84, 4, 37, 7, 69, 82, 84, 73, 67, - 65, 76, 5, 131, 1, 32, 4, 42, 72, 41, 6, 86, 69, 82, 84, 73, 67, 2, 37, - 7, 79, 82, 73, 90, 79, 78, 84, 2, 17, 2, 65, 76, 2, 11, 32, 2, 11, 83, 2, - 161, 209, 27, 2, 69, 67, 214, 1, 222, 1, 65, 22, 68, 34, 69, 30, 70, 22, - 71, 22, 74, 162, 1, 75, 66, 76, 50, 77, 62, 78, 134, 1, 79, 50, 80, 86, - 82, 74, 83, 210, 2, 84, 66, 85, 42, 86, 38, 87, 70, 88, 162, 210, 27, 72, - 142, 60, 73, 206, 41, 89, 215, 22, 66, 5, 151, 180, 28, 79, 7, 142, 155, - 28, 32, 223, 61, 72, 7, 202, 216, 28, 69, 3, 85, 5, 207, 136, 28, 32, 5, - 255, 226, 24, 32, 19, 11, 32, 16, 76, 8, 87, 73, 84, 72, 32, 68, 79, 84, - 230, 5, 77, 2, 78, 195, 173, 27, 83, 5, 165, 218, 27, 12, 83, 32, 73, 78, - 83, 73, 68, 69, 32, 65, 78, 68, 9, 26, 32, 207, 214, 28, 75, 4, 186, 225, - 24, 82, 147, 245, 3, 77, 9, 248, 204, 22, 3, 79, 78, 71, 175, 137, 6, 72, - 11, 11, 32, 8, 162, 4, 78, 218, 173, 27, 87, 243, 163, 1, 83, 19, 38, 32, - 49, 5, 65, 83, 65, 76, 32, 8, 202, 3, 77, 218, 173, 27, 87, 243, 163, 1, - 83, 8, 226, 212, 28, 65, 2, 73, 2, 79, 3, 85, 11, 166, 211, 28, 79, 146, - 1, 65, 2, 85, 3, 87, 9, 52, 7, 69, 82, 78, 73, 78, 32, 65, 243, 131, 28, - 32, 4, 206, 211, 28, 77, 3, 78, 11, 248, 201, 22, 6, 79, 77, 65, 78, 73, - 65, 222, 203, 5, 32, 223, 61, 72, 49, 58, 32, 164, 1, 5, 76, 79, 65, 78, - 32, 191, 178, 6, 72, 26, 102, 74, 22, 75, 2, 80, 2, 84, 20, 7, 87, 73, - 84, 72, 32, 68, 79, 162, 209, 28, 77, 2, 78, 3, 83, 5, 235, 147, 28, 32, - 5, 215, 152, 28, 32, 4, 139, 217, 12, 84, 18, 74, 69, 230, 195, 5, 79, - 254, 188, 22, 65, 210, 78, 68, 146, 1, 74, 3, 85, 6, 190, 208, 28, 69, 2, - 72, 3, 78, 9, 26, 32, 131, 208, 28, 72, 4, 238, 218, 24, 82, 147, 245, 3, - 83, 9, 250, 255, 27, 32, 226, 79, 72, 3, 73, 5, 245, 148, 7, 4, 79, 67, - 65, 76, 17, 66, 79, 242, 149, 28, 32, 134, 37, 69, 218, 19, 65, 2, 72, 3, - 73, 5, 203, 206, 28, 87, 188, 91, 234, 2, 65, 188, 3, 10, 68, 73, 84, 79, - 82, 73, 65, 76, 32, 67, 22, 71, 224, 79, 4, 73, 71, 72, 84, 146, 2, 76, - 194, 9, 77, 214, 5, 78, 246, 3, 79, 36, 2, 81, 85, 182, 8, 82, 222, 1, - 83, 118, 84, 242, 31, 85, 198, 1, 88, 128, 5, 2, 89, 69, 200, 80, 4, 45, - 77, 65, 73, 148, 140, 19, 3, 74, 69, 67, 228, 147, 2, 8, 86, 69, 82, 71, - 82, 69, 69, 78, 175, 190, 5, 80, 22, 38, 82, 202, 230, 26, 83, 179, 64, - 71, 19, 30, 32, 137, 1, 2, 84, 72, 6, 88, 3, 79, 70, 32, 233, 229, 4, 13, - 87, 73, 84, 72, 32, 72, 69, 65, 82, 73, 78, 71, 32, 4, 140, 190, 11, 2, - 77, 65, 135, 230, 6, 82, 11, 17, 2, 32, 71, 8, 44, 5, 76, 79, 66, 69, 32, - 203, 148, 25, 82, 6, 70, 65, 181, 235, 21, 11, 69, 85, 82, 79, 80, 69, - 45, 65, 70, 82, 73, 4, 176, 137, 10, 4, 77, 69, 82, 73, 133, 154, 2, 11, - 83, 73, 65, 45, 65, 85, 83, 84, 82, 65, 76, 2, 183, 176, 2, 79, 228, 79, - 92, 17, 89, 80, 84, 73, 65, 78, 32, 72, 73, 69, 82, 79, 71, 76, 89, 80, - 72, 151, 199, 28, 71, 226, 79, 30, 32, 197, 74, 2, 45, 49, 172, 17, 146, - 3, 65, 178, 4, 66, 52, 2, 67, 48, 224, 1, 2, 68, 48, 170, 4, 69, 178, 3, - 70, 164, 4, 2, 71, 48, 210, 3, 72, 214, 1, 73, 238, 2, 76, 122, 77, 222, - 8, 78, 210, 5, 79, 140, 5, 2, 80, 48, 120, 2, 82, 48, 232, 1, 2, 83, 48, - 190, 3, 84, 220, 2, 2, 85, 48, 250, 2, 86, 134, 5, 87, 236, 2, 3, 88, 48, - 48, 88, 3, 89, 48, 48, 84, 2, 90, 48, 252, 214, 3, 3, 75, 48, 48, 189, - 146, 15, 3, 81, 48, 48, 228, 1, 30, 48, 133, 3, 2, 65, 48, 160, 1, 86, - 48, 98, 49, 102, 52, 166, 55, 51, 162, 241, 20, 55, 150, 227, 1, 50, 2, - 53, 3, 54, 24, 170, 68, 54, 170, 146, 22, 53, 134, 236, 5, 49, 2, 50, 2, - 51, 2, 52, 2, 55, 2, 56, 3, 57, 24, 242, 213, 22, 52, 2, 55, 134, 236, 5, - 48, 2, 49, 2, 50, 2, 51, 2, 53, 2, 54, 2, 56, 3, 57, 28, 142, 213, 22, - 48, 2, 50, 2, 51, 2, 53, 134, 236, 5, 49, 2, 52, 2, 54, 2, 55, 2, 56, 3, - 57, 68, 46, 48, 246, 54, 51, 210, 211, 22, 49, 3, 50, 22, 210, 65, 55, - 174, 254, 27, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 26, - 148, 9, 4, 69, 71, 73, 78, 185, 25, 2, 48, 48, 56, 34, 48, 90, 49, 175, - 244, 8, 50, 24, 230, 39, 50, 234, 150, 28, 49, 2, 51, 2, 52, 2, 53, 2, - 54, 2, 55, 2, 56, 3, 57, 22, 242, 209, 22, 48, 134, 236, 5, 49, 2, 50, 2, - 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 184, 1, 70, 48, 94, 51, - 102, 52, 102, 53, 106, 54, 194, 29, 50, 195, 230, 22, 49, 20, 194, 208, - 22, 56, 134, 236, 5, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 57, - 24, 230, 207, 22, 49, 2, 52, 134, 236, 5, 48, 2, 50, 2, 51, 2, 53, 2, 54, - 2, 55, 2, 56, 3, 57, 24, 130, 207, 22, 54, 2, 56, 134, 236, 5, 48, 2, 49, - 2, 50, 2, 51, 2, 52, 2, 53, 2, 55, 3, 57, 42, 214, 60, 48, 202, 145, 22, - 50, 2, 52, 134, 236, 5, 49, 2, 51, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 32, - 194, 60, 55, 250, 252, 27, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 3, 54, - 94, 30, 48, 189, 2, 2, 78, 68, 88, 38, 48, 94, 50, 102, 51, 215, 7, 49, - 22, 158, 204, 22, 56, 2, 57, 134, 236, 5, 49, 2, 50, 2, 51, 2, 52, 2, 53, - 2, 54, 3, 55, 24, 194, 203, 22, 48, 2, 56, 134, 236, 5, 49, 2, 50, 2, 51, - 2, 52, 2, 53, 2, 54, 2, 55, 3, 57, 18, 222, 202, 22, 52, 134, 236, 5, 48, - 2, 49, 2, 50, 2, 51, 2, 54, 2, 55, 3, 56, 6, 11, 32, 6, 136, 180, 5, 6, - 87, 65, 76, 76, 69, 68, 210, 19, 69, 203, 211, 20, 83, 132, 1, 42, 48, - 133, 9, 5, 85, 76, 76, 32, 66, 130, 1, 54, 48, 94, 49, 102, 51, 102, 52, - 102, 53, 215, 1, 50, 20, 202, 200, 22, 49, 134, 236, 5, 50, 2, 51, 2, 52, - 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 22, 238, 199, 22, 51, 134, 236, 5, 48, - 2, 49, 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 26, 138, 199, 22, - 49, 2, 55, 2, 56, 134, 236, 5, 48, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, - 57, 26, 166, 198, 22, 53, 2, 54, 2, 55, 134, 236, 5, 48, 2, 49, 2, 50, 2, - 51, 2, 52, 2, 56, 3, 57, 14, 222, 26, 49, 234, 150, 28, 48, 2, 50, 3, 51, - 128, 1, 62, 48, 98, 49, 102, 51, 102, 52, 210, 27, 50, 147, 201, 8, 53, - 24, 166, 50, 55, 170, 146, 22, 54, 134, 236, 5, 49, 2, 50, 2, 51, 2, 52, - 2, 53, 2, 56, 3, 57, 22, 238, 195, 22, 49, 134, 236, 5, 48, 2, 50, 2, 51, - 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 24, 138, 195, 22, 54, 2, 55, - 134, 236, 5, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 56, 3, 57, 24, - 166, 194, 22, 51, 2, 53, 134, 236, 5, 48, 2, 49, 2, 50, 2, 52, 2, 54, 2, - 55, 2, 56, 3, 57, 24, 80, 2, 48, 48, 84, 4, 65, 76, 70, 32, 161, 40, 7, - 79, 82, 73, 90, 79, 78, 84, 18, 238, 192, 22, 54, 134, 236, 5, 49, 2, 50, - 2, 51, 2, 52, 2, 53, 2, 55, 3, 56, 4, 22, 66, 255, 42, 76, 2, 223, 238, - 16, 76, 52, 58, 48, 181, 1, 9, 78, 83, 69, 82, 84, 32, 65, 84, 32, 38, - 18, 48, 95, 49, 22, 158, 191, 22, 53, 2, 57, 134, 236, 5, 49, 2, 50, 2, - 51, 2, 52, 2, 54, 2, 55, 3, 56, 16, 194, 190, 22, 48, 2, 49, 134, 236, 5, - 50, 2, 51, 2, 52, 3, 53, 14, 68, 6, 66, 79, 84, 84, 79, 77, 0, 3, 84, 79, - 80, 215, 212, 19, 77, 7, 11, 32, 4, 182, 155, 27, 69, 221, 6, 2, 83, 84, - 22, 32, 2, 48, 48, 203, 221, 15, 79, 20, 222, 188, 22, 50, 2, 54, 134, - 236, 5, 49, 2, 51, 2, 52, 2, 53, 2, 55, 3, 56, 164, 1, 118, 48, 128, 4, - 15, 79, 68, 73, 70, 73, 69, 82, 32, 68, 65, 77, 65, 71, 69, 68, 205, 228, - 24, 5, 73, 82, 82, 79, 82, 132, 1, 42, 48, 98, 49, 106, 50, 102, 51, 107, - 52, 24, 182, 40, 49, 170, 146, 22, 51, 134, 236, 5, 50, 2, 52, 2, 53, 2, - 54, 2, 55, 2, 56, 3, 57, 44, 138, 41, 50, 246, 144, 22, 48, 2, 53, 2, 54, - 2, 55, 134, 236, 5, 49, 2, 51, 2, 52, 2, 56, 3, 57, 26, 150, 185, 22, 50, - 2, 52, 2, 56, 134, 236, 5, 48, 2, 49, 2, 51, 2, 53, 2, 54, 2, 55, 3, 57, - 26, 138, 38, 51, 170, 146, 22, 49, 134, 236, 5, 48, 2, 50, 2, 52, 2, 53, - 2, 54, 2, 55, 2, 56, 3, 57, 12, 202, 183, 22, 48, 134, 236, 5, 49, 2, 50, - 2, 51, 3, 52, 31, 25, 4, 32, 65, 84, 32, 28, 96, 6, 66, 79, 84, 84, 79, - 77, 120, 5, 83, 84, 65, 82, 84, 68, 3, 84, 79, 80, 227, 146, 27, 69, 11, - 11, 32, 8, 56, 5, 83, 84, 65, 82, 84, 186, 1, 65, 159, 146, 27, 69, 5, - 129, 2, 8, 32, 65, 78, 68, 32, 84, 79, 80, 7, 29, 5, 32, 65, 78, 68, 32, - 4, 206, 181, 24, 66, 155, 242, 2, 84, 11, 11, 32, 8, 54, 65, 20, 5, 83, - 84, 65, 82, 84, 139, 146, 27, 69, 2, 73, 2, 78, 68, 5, 53, 11, 32, 65, - 78, 68, 32, 66, 79, 84, 84, 79, 77, 2, 251, 209, 19, 32, 194, 1, 50, 48, - 128, 2, 2, 76, 48, 229, 1, 2, 85, 48, 98, 58, 49, 98, 51, 194, 14, 50, - 150, 6, 52, 227, 209, 22, 48, 24, 146, 32, 56, 174, 254, 27, 48, 2, 49, - 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 57, 28, 218, 177, 22, 51, 2, - 52, 2, 53, 2, 55, 134, 236, 5, 48, 2, 49, 2, 50, 2, 54, 2, 56, 3, 57, 44, - 34, 48, 94, 49, 175, 131, 21, 50, 20, 210, 176, 22, 53, 134, 236, 5, 49, - 2, 50, 2, 51, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 22, 246, 175, 22, 55, - 134, 236, 5, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, - 52, 34, 49, 102, 50, 231, 226, 22, 48, 26, 238, 174, 22, 48, 2, 49, 2, - 56, 134, 236, 5, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 57, 8, 138, - 174, 22, 50, 134, 236, 5, 48, 3, 49, 152, 1, 50, 48, 145, 247, 18, 6, 86, - 69, 82, 76, 65, 89, 150, 1, 66, 48, 154, 1, 49, 138, 1, 50, 102, 51, 106, - 53, 191, 223, 22, 52, 34, 90, 54, 134, 172, 22, 49, 2, 53, 134, 236, 5, - 50, 2, 51, 2, 52, 2, 55, 2, 56, 3, 57, 15, 134, 152, 28, 65, 2, 66, 2, - 67, 2, 68, 2, 69, 3, 70, 28, 98, 48, 230, 170, 22, 57, 134, 236, 5, 49, - 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 9, 230, 150, 28, 65, 2, - 66, 3, 67, 28, 190, 170, 22, 48, 2, 52, 2, 53, 2, 57, 134, 236, 5, 49, 2, - 50, 2, 51, 2, 54, 2, 55, 3, 56, 32, 134, 23, 54, 214, 146, 22, 48, 2, 51, - 134, 236, 5, 49, 2, 50, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 8, 202, 22, - 48, 175, 254, 27, 49, 26, 26, 48, 143, 194, 8, 49, 22, 182, 168, 22, 49, - 2, 51, 134, 236, 5, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 68, 34, - 48, 98, 49, 163, 221, 22, 50, 24, 142, 21, 51, 170, 146, 22, 50, 134, - 236, 5, 49, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 24, 214, 166, 22, - 48, 2, 54, 134, 236, 5, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 55, 2, 56, 3, - 57, 108, 50, 48, 94, 49, 106, 50, 98, 51, 171, 172, 19, 52, 22, 190, 165, - 22, 50, 2, 54, 134, 236, 5, 49, 2, 51, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, - 26, 186, 18, 52, 170, 146, 22, 55, 134, 236, 5, 48, 2, 49, 2, 50, 2, 51, - 2, 53, 2, 54, 2, 56, 3, 57, 24, 210, 17, 54, 174, 254, 27, 48, 2, 49, 2, - 50, 2, 51, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 22, 154, 163, 22, 53, 134, - 236, 5, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 90, - 34, 48, 249, 12, 3, 65, 76, 76, 88, 42, 48, 94, 49, 102, 51, 243, 214, - 22, 50, 26, 230, 161, 22, 51, 2, 55, 2, 56, 2, 57, 134, 236, 5, 49, 2, - 50, 2, 52, 2, 53, 3, 54, 24, 138, 161, 22, 49, 2, 54, 134, 236, 5, 48, 2, - 50, 2, 51, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 18, 166, 160, 22, 50, 2, - 51, 134, 236, 5, 48, 2, 49, 2, 52, 2, 53, 3, 54, 94, 50, 48, 90, 50, 102, - 51, 102, 52, 211, 211, 22, 49, 22, 254, 12, 54, 174, 254, 27, 49, 2, 50, - 2, 51, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 24, 206, 158, 22, 51, 2, 57, - 134, 236, 5, 48, 2, 49, 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 22, - 234, 157, 22, 50, 134, 236, 5, 48, 2, 49, 2, 51, 2, 52, 2, 53, 2, 54, 2, - 55, 2, 56, 3, 57, 6, 138, 137, 28, 48, 2, 49, 3, 50, 158, 1, 42, 48, 185, - 4, 5, 69, 82, 84, 73, 67, 156, 1, 62, 48, 98, 49, 98, 50, 210, 1, 51, - 225, 152, 22, 2, 52, 48, 42, 198, 9, 55, 98, 49, 202, 145, 22, 50, 134, - 236, 5, 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 32, 186, 8, 49, 46, 50, - 174, 254, 27, 48, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 50, - 98, 48, 206, 153, 22, 51, 2, 56, 2, 57, 134, 236, 5, 49, 2, 50, 2, 52, 2, - 53, 2, 54, 3, 55, 27, 206, 133, 28, 65, 2, 66, 2, 67, 2, 68, 2, 69, 2, - 70, 2, 71, 2, 72, 2, 73, 2, 74, 2, 75, 3, 76, 28, 222, 152, 22, 48, 2, - 49, 2, 51, 2, 55, 134, 236, 5, 50, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 2, - 221, 221, 23, 2, 65, 76, 66, 34, 48, 161, 2, 3, 73, 68, 69, 64, 26, 48, - 94, 49, 103, 50, 22, 158, 151, 22, 51, 2, 57, 134, 236, 5, 49, 2, 50, 2, - 52, 2, 53, 2, 54, 2, 55, 3, 56, 28, 194, 150, 22, 48, 2, 52, 2, 55, 2, - 56, 134, 236, 5, 49, 2, 50, 2, 51, 2, 53, 2, 54, 3, 57, 14, 222, 149, 22, - 52, 134, 236, 5, 48, 2, 49, 2, 50, 2, 51, 3, 53, 2, 17, 2, 32, 76, 2, - 239, 181, 15, 79, 24, 202, 2, 52, 170, 146, 22, 54, 2, 56, 134, 236, 5, - 49, 2, 50, 2, 51, 2, 53, 3, 55, 18, 154, 148, 22, 49, 134, 236, 5, 50, 2, - 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 82, 22, 48, 167, 1, 49, 34, 90, - 50, 46, 51, 170, 146, 22, 52, 2, 53, 134, 236, 5, 49, 2, 54, 2, 55, 2, - 56, 3, 57, 11, 214, 254, 27, 65, 2, 66, 2, 67, 3, 68, 7, 170, 254, 27, - 65, 3, 66, 48, 66, 53, 86, 54, 250, 252, 27, 48, 2, 49, 2, 50, 2, 51, 3, - 52, 21, 202, 253, 27, 65, 2, 66, 2, 67, 2, 68, 2, 69, 2, 70, 2, 71, 2, - 72, 3, 73, 19, 246, 252, 27, 65, 2, 66, 2, 67, 2, 68, 2, 69, 2, 70, 2, - 71, 3, 72, 182, 62, 22, 51, 211, 1, 52, 192, 46, 106, 52, 174, 188, 5, - 53, 2, 54, 2, 55, 2, 56, 2, 57, 2, 65, 2, 66, 2, 67, 2, 68, 2, 69, 3, 70, - 192, 2, 214, 179, 13, 54, 2, 55, 2, 56, 2, 57, 2, 65, 2, 66, 2, 67, 2, - 68, 2, 69, 3, 70, 246, 15, 42, 51, 158, 187, 5, 48, 2, 49, 3, 50, 246, 3, - 142, 1, 70, 186, 177, 13, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, - 2, 55, 2, 56, 2, 57, 2, 65, 2, 66, 2, 67, 2, 68, 3, 69, 22, 250, 248, 27, - 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 2, 57, 3, 65, - 18, 26, 32, 239, 226, 15, 72, 16, 70, 80, 124, 5, 82, 65, 89, 83, 32, - 190, 146, 3, 84, 231, 207, 22, 83, 8, 186, 146, 3, 79, 241, 185, 16, 22, - 69, 84, 65, 76, 76, 69, 68, 32, 79, 85, 84, 76, 73, 78, 69, 68, 32, 66, - 76, 65, 67, 75, 4, 228, 234, 18, 2, 73, 78, 1, 3, 79, 85, 84, 160, 1, - 132, 1, 13, 66, 65, 83, 65, 78, 32, 76, 69, 84, 84, 69, 82, 32, 166, 3, - 69, 176, 4, 7, 89, 77, 65, 73, 67, 32, 76, 171, 237, 27, 70, 80, 230, 1, - 71, 78, 76, 34, 78, 50, 82, 158, 232, 25, 69, 190, 137, 1, 67, 2, 68, 2, - 75, 2, 83, 2, 84, 2, 90, 206, 105, 66, 2, 70, 2, 72, 2, 74, 2, 77, 2, 80, - 2, 81, 2, 86, 2, 88, 214, 22, 65, 2, 73, 2, 79, 2, 85, 3, 89, 8, 38, 72, - 154, 220, 27, 74, 215, 22, 69, 4, 242, 160, 25, 65, 251, 209, 2, 69, 4, - 242, 219, 27, 76, 215, 22, 69, 8, 210, 219, 27, 68, 2, 74, 214, 22, 65, - 3, 69, 4, 162, 219, 27, 82, 215, 22, 69, 32, 96, 5, 67, 84, 82, 73, 67, - 160, 1, 7, 77, 69, 78, 84, 32, 79, 70, 238, 229, 26, 80, 235, 80, 86, 10, - 26, 32, 179, 130, 24, 65, 8, 98, 80, 144, 205, 17, 2, 84, 79, 128, 231, - 8, 9, 76, 73, 71, 72, 84, 32, 66, 85, 76, 171, 33, 65, 2, 11, 76, 2, 147, - 239, 27, 85, 19, 11, 32, 16, 72, 5, 87, 73, 84, 72, 32, 217, 207, 16, 7, - 79, 80, 69, 78, 73, 78, 71, 12, 130, 1, 76, 32, 12, 84, 87, 79, 32, 72, - 79, 82, 73, 90, 79, 78, 84, 142, 214, 19, 86, 194, 172, 4, 85, 234, 60, - 79, 219, 173, 2, 68, 2, 173, 201, 25, 3, 79, 78, 71, 2, 217, 213, 14, 7, - 65, 76, 32, 83, 84, 82, 79, 46, 254, 168, 4, 69, 129, 202, 15, 15, 73, - 71, 65, 84, 85, 82, 69, 32, 90, 65, 89, 73, 78, 45, 89, 53, 48, 4, 79, - 74, 73, 32, 218, 2, 80, 163, 3, 32, 18, 164, 1, 10, 67, 79, 77, 80, 79, - 78, 69, 78, 84, 32, 109, 26, 77, 79, 68, 73, 70, 73, 69, 82, 32, 70, 73, - 84, 90, 80, 65, 84, 82, 73, 67, 75, 32, 84, 89, 80, 69, 45, 8, 128, 255, - 14, 2, 82, 69, 12, 5, 67, 85, 82, 76, 89, 0, 5, 87, 72, 73, 84, 69, 213, - 205, 2, 2, 66, 65, 10, 208, 208, 20, 2, 49, 45, 174, 153, 7, 51, 2, 52, - 2, 53, 3, 54, 26, 44, 3, 84, 89, 32, 177, 244, 3, 2, 72, 65, 24, 82, 78, - 60, 3, 80, 65, 71, 20, 3, 83, 69, 84, 181, 164, 27, 4, 68, 79, 67, 85, 8, - 36, 3, 79, 84, 69, 151, 254, 23, 69, 7, 199, 152, 13, 32, 4, 139, 225, - 25, 69, 11, 33, 6, 32, 87, 73, 84, 72, 32, 8, 130, 130, 14, 82, 24, 3, - 76, 69, 70, 212, 166, 1, 2, 83, 77, 179, 144, 9, 79, 38, 86, 32, 64, 2, - 68, 32, 214, 1, 81, 20, 6, 86, 69, 76, 79, 80, 69, 227, 220, 15, 84, 6, - 42, 81, 198, 253, 25, 68, 131, 170, 1, 83, 2, 195, 159, 27, 85, 20, 32, - 3, 79, 70, 32, 131, 1, 87, 18, 88, 3, 80, 82, 79, 242, 221, 20, 71, 56, - 2, 83, 69, 162, 71, 77, 30, 84, 195, 165, 5, 76, 4, 178, 222, 20, 84, - 135, 133, 6, 79, 2, 173, 239, 19, 7, 73, 84, 72, 32, 76, 69, 70, 5, 231, - 165, 21, 85, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 42, 76, 209, 233, 23, - 4, 68, 79, 87, 78, 2, 209, 203, 22, 4, 73, 71, 72, 84, 6, 254, 226, 27, - 76, 2, 77, 3, 84, 44, 28, 2, 65, 76, 231, 6, 73, 38, 30, 32, 133, 2, 2, - 83, 32, 12, 56, 3, 84, 79, 32, 177, 141, 13, 5, 65, 78, 68, 32, 80, 10, - 68, 3, 79, 82, 32, 221, 143, 27, 8, 66, 89, 32, 68, 69, 70, 73, 78, 8, - 64, 3, 80, 82, 69, 28, 3, 83, 85, 67, 162, 206, 25, 71, 39, 76, 2, 193, - 156, 15, 2, 67, 69, 2, 233, 128, 26, 3, 67, 69, 69, 26, 72, 4, 83, 73, - 71, 78, 224, 203, 25, 4, 87, 73, 84, 72, 235, 195, 1, 67, 23, 11, 32, 20, - 42, 65, 201, 1, 5, 87, 73, 84, 72, 32, 12, 112, 5, 66, 79, 86, 69, 32, - 65, 19, 78, 68, 32, 83, 76, 65, 78, 84, 69, 68, 32, 80, 65, 82, 65, 76, - 76, 69, 76, 8, 154, 132, 21, 80, 210, 5, 84, 182, 184, 2, 76, 139, 252, - 2, 82, 5, 243, 239, 6, 32, 8, 160, 1, 4, 66, 85, 77, 80, 20, 7, 73, 78, - 70, 73, 78, 73, 84, 20, 18, 84, 87, 79, 32, 68, 79, 84, 83, 32, 65, 66, - 79, 86, 69, 32, 65, 78, 68, 215, 156, 15, 68, 2, 163, 223, 26, 89, 2, - 207, 174, 25, 89, 2, 17, 2, 32, 84, 2, 243, 173, 25, 87, 6, 80, 7, 86, - 65, 76, 69, 78, 84, 32, 237, 135, 21, 7, 65, 78, 71, 85, 76, 65, 82, 4, - 48, 6, 87, 73, 84, 72, 32, 70, 223, 187, 27, 84, 2, 11, 79, 2, 201, 242, - 2, 2, 85, 82, 20, 152, 1, 11, 82, 79, 82, 45, 66, 65, 82, 82, 69, 68, 32, - 152, 163, 5, 7, 73, 83, 32, 70, 79, 82, 77, 193, 195, 7, 9, 65, 83, 69, - 32, 84, 79, 32, 84, 72, 12, 160, 220, 5, 4, 87, 72, 73, 84, 13, 5, 66, - 76, 65, 67, 75, 10, 58, 67, 20, 6, 84, 73, 77, 65, 84, 69, 255, 215, 27, - 65, 5, 211, 144, 26, 65, 4, 210, 196, 26, 68, 171, 147, 1, 83, 154, 8, - 60, 7, 72, 73, 79, 80, 73, 67, 32, 150, 215, 27, 66, 3, 88, 150, 8, 204, - 1, 2, 67, 79, 232, 1, 7, 78, 85, 77, 66, 69, 82, 32, 114, 80, 54, 83, - 156, 24, 11, 84, 79, 78, 65, 76, 32, 77, 65, 82, 75, 32, 226, 252, 18, - 68, 214, 235, 5, 70, 82, 81, 221, 146, 2, 4, 87, 79, 82, 68, 10, 26, 77, - 187, 133, 27, 76, 8, 52, 7, 66, 73, 78, 73, 78, 71, 32, 183, 210, 27, 77, - 6, 60, 11, 71, 69, 77, 73, 78, 65, 84, 73, 79, 78, 32, 51, 86, 4, 44, 5, - 65, 78, 68, 32, 86, 211, 146, 27, 77, 2, 201, 246, 23, 4, 79, 87, 69, 76, - 22, 66, 84, 226, 255, 21, 72, 222, 228, 3, 69, 30, 70, 42, 78, 39, 83, 8, - 194, 199, 16, 69, 170, 158, 9, 72, 27, 87, 4, 166, 113, 65, 153, 145, 26, - 5, 82, 69, 70, 65, 67, 198, 7, 50, 69, 37, 8, 89, 76, 76, 65, 66, 76, 69, - 32, 4, 202, 134, 24, 77, 139, 226, 2, 67, 194, 7, 210, 1, 66, 90, 67, - 246, 1, 68, 186, 1, 70, 90, 71, 214, 2, 72, 162, 1, 75, 102, 77, 90, 78, - 90, 80, 138, 2, 81, 174, 1, 82, 86, 83, 210, 1, 84, 122, 74, 2, 76, 138, - 1, 87, 2, 89, 66, 88, 134, 1, 90, 95, 86, 38, 194, 12, 87, 230, 8, 66, - 214, 206, 21, 65, 2, 79, 230, 171, 5, 69, 162, 64, 73, 3, 85, 78, 94, 67, - 254, 15, 72, 202, 210, 21, 65, 2, 79, 230, 171, 5, 69, 234, 61, 87, 186, - 2, 73, 3, 85, 42, 70, 72, 254, 225, 21, 65, 230, 171, 5, 69, 162, 64, 73, - 2, 79, 3, 85, 28, 166, 19, 72, 214, 206, 21, 65, 230, 171, 5, 69, 162, - 64, 73, 2, 79, 3, 85, 60, 94, 68, 214, 14, 90, 254, 209, 21, 65, 2, 79, - 230, 171, 5, 69, 234, 61, 87, 186, 2, 73, 3, 85, 30, 210, 14, 72, 254, - 209, 21, 65, 2, 79, 230, 171, 5, 69, 234, 61, 87, 186, 2, 73, 3, 85, 24, - 190, 8, 87, 186, 215, 21, 65, 230, 171, 5, 69, 234, 61, 89, 186, 2, 73, - 2, 79, 3, 85, 118, 142, 1, 85, 226, 7, 71, 232, 3, 7, 76, 79, 84, 84, 65, - 76, 32, 158, 2, 87, 218, 1, 89, 214, 206, 21, 65, 2, 79, 230, 171, 5, 69, - 163, 64, 73, 39, 29, 5, 82, 65, 71, 69, 32, 36, 74, 66, 2, 70, 2, 77, 2, - 80, 46, 71, 2, 75, 2, 81, 191, 130, 12, 72, 4, 11, 87, 4, 198, 178, 27, - 69, 215, 22, 73, 6, 11, 87, 6, 206, 136, 27, 69, 163, 64, 73, 52, 70, 72, - 134, 220, 21, 65, 2, 79, 230, 171, 5, 69, 162, 64, 73, 3, 85, 36, 202, 4, - 87, 230, 8, 89, 214, 206, 21, 65, 230, 171, 5, 69, 162, 64, 73, 2, 79, 3, - 85, 64, 250, 4, 88, 134, 6, 87, 218, 1, 89, 214, 206, 21, 65, 2, 79, 230, - 171, 5, 69, 162, 64, 73, 3, 85, 26, 142, 3, 87, 186, 215, 21, 65, 2, 79, - 230, 171, 5, 69, 234, 61, 89, 186, 2, 73, 3, 85, 36, 166, 7, 89, 202, - 210, 21, 65, 2, 79, 230, 171, 5, 69, 234, 61, 87, 186, 2, 73, 3, 85, 56, - 82, 72, 142, 1, 87, 186, 215, 21, 65, 2, 79, 230, 171, 5, 69, 162, 64, - 73, 3, 85, 32, 74, 65, 250, 215, 21, 79, 230, 171, 5, 69, 234, 61, 87, - 186, 2, 73, 3, 85, 19, 160, 9, 8, 82, 89, 78, 71, 69, 65, 76, 32, 219, - 186, 27, 65, 8, 154, 131, 27, 69, 162, 64, 65, 3, 73, 64, 94, 72, 134, 6, - 87, 218, 1, 89, 214, 206, 21, 65, 2, 79, 230, 171, 5, 69, 162, 64, 73, 3, - 85, 24, 130, 6, 87, 174, 208, 21, 65, 230, 171, 5, 69, 162, 64, 73, 2, - 79, 3, 85, 20, 226, 213, 21, 65, 2, 79, 230, 171, 5, 69, 234, 61, 87, 2, - 89, 186, 2, 73, 3, 85, 74, 102, 69, 226, 1, 72, 170, 3, 90, 78, 83, 214, - 206, 21, 65, 2, 79, 206, 233, 5, 87, 186, 2, 73, 3, 85, 13, 56, 8, 66, - 65, 84, 66, 69, 73, 84, 32, 243, 191, 27, 69, 8, 230, 206, 22, 66, 2, 70, - 2, 77, 3, 80, 80, 118, 72, 76, 2, 84, 72, 62, 90, 162, 2, 83, 162, 207, - 21, 65, 2, 79, 230, 171, 5, 69, 234, 61, 87, 186, 2, 73, 3, 85, 18, 198, - 210, 21, 65, 2, 79, 230, 171, 5, 69, 234, 61, 87, 186, 2, 73, 3, 85, 12, - 222, 253, 26, 69, 234, 61, 65, 186, 2, 73, 2, 79, 3, 85, 16, 190, 209, - 21, 65, 2, 79, 230, 171, 5, 69, 162, 64, 73, 3, 85, 40, 82, 87, 218, 1, - 89, 214, 206, 21, 65, 2, 79, 230, 171, 5, 69, 162, 64, 73, 3, 85, 10, - 170, 208, 21, 65, 230, 171, 5, 69, 163, 64, 73, 48, 90, 72, 78, 90, 214, - 206, 21, 65, 2, 79, 230, 171, 5, 69, 234, 61, 87, 186, 2, 73, 3, 85, 16, - 158, 207, 21, 65, 230, 171, 5, 69, 234, 61, 87, 186, 2, 73, 2, 79, 3, 85, - 14, 210, 206, 21, 65, 230, 171, 5, 69, 162, 64, 73, 2, 79, 3, 85, 20, - 130, 1, 68, 74, 72, 30, 75, 42, 82, 0, 7, 83, 72, 79, 82, 84, 32, 82, - 204, 140, 4, 3, 67, 72, 73, 165, 246, 22, 3, 89, 73, 90, 6, 48, 4, 69, - 82, 69, 84, 249, 164, 26, 2, 73, 70, 5, 17, 2, 45, 72, 2, 177, 131, 27, - 2, 73, 68, 4, 196, 164, 26, 2, 69, 78, 219, 12, 85, 2, 245, 154, 4, 3, - 73, 75, 82, 10, 68, 2, 82, 79, 197, 173, 26, 9, 76, 69, 82, 32, 67, 79, - 78, 83, 84, 8, 92, 5, 80, 69, 65, 78, 32, 204, 131, 24, 8, 45, 67, 85, - 82, 82, 69, 78, 67, 131, 172, 2, 32, 4, 214, 135, 4, 67, 19, 80, 50, 30, - 67, 102, 80, 187, 1, 84, 6, 60, 9, 76, 65, 77, 65, 84, 73, 79, 78, 32, - 247, 158, 26, 69, 4, 174, 228, 24, 81, 235, 143, 2, 77, 10, 96, 7, 76, - 79, 83, 73, 79, 78, 32, 157, 247, 26, 11, 82, 69, 83, 83, 73, 79, 78, 76, - 69, 83, 83, 8, 232, 209, 11, 4, 70, 82, 65, 77, 229, 146, 15, 8, 65, 84, - 32, 72, 79, 82, 73, 90, 34, 98, 82, 153, 179, 21, 18, 69, 78, 68, 69, 68, - 32, 65, 82, 65, 66, 73, 67, 45, 73, 78, 68, 73, 67, 14, 140, 1, 12, 69, - 77, 69, 76, 89, 32, 72, 69, 65, 86, 89, 32, 141, 222, 25, 16, 65, 84, 69, - 82, 82, 69, 83, 84, 82, 73, 65, 76, 32, 65, 76, 73, 12, 50, 83, 202, 155, - 25, 70, 234, 2, 87, 215, 10, 71, 4, 218, 156, 25, 65, 43, 73, 7, 250, - 167, 19, 71, 171, 137, 8, 83, 142, 4, 142, 1, 65, 130, 19, 69, 190, 1, - 73, 230, 7, 76, 242, 5, 79, 238, 6, 82, 234, 4, 85, 228, 183, 20, 2, 86, - 83, 202, 192, 4, 83, 223, 137, 2, 70, 92, 122, 67, 178, 15, 76, 176, 2, - 2, 88, 32, 238, 141, 3, 77, 172, 247, 5, 2, 82, 83, 188, 250, 1, 2, 84, - 72, 187, 230, 9, 73, 70, 72, 2, 69, 32, 248, 172, 16, 4, 83, 73, 77, 73, - 169, 202, 4, 2, 84, 79, 66, 226, 1, 83, 160, 1, 4, 87, 73, 84, 72, 224, - 250, 12, 2, 80, 65, 228, 136, 2, 2, 77, 65, 140, 145, 11, 13, 84, 72, 82, - 79, 87, 73, 78, 71, 32, 65, 32, 75, 73, 201, 1, 14, 72, 79, 76, 68, 73, - 78, 71, 32, 66, 65, 67, 75, 32, 84, 4, 232, 251, 16, 18, 65, 86, 79, 85, - 82, 73, 78, 71, 32, 68, 69, 76, 73, 67, 73, 79, 85, 83, 241, 128, 5, 13, - 67, 82, 69, 65, 77, 73, 78, 71, 32, 73, 78, 32, 70, 54, 38, 32, 201, 137, - 24, 3, 79, 85, 84, 52, 196, 4, 2, 67, 79, 44, 5, 72, 69, 65, 68, 45, 38, - 77, 98, 79, 92, 7, 78, 79, 32, 71, 79, 79, 68, 238, 1, 80, 164, 1, 16, - 83, 84, 85, 67, 75, 45, 79, 85, 84, 32, 84, 79, 78, 71, 85, 69, 110, 84, - 192, 177, 1, 9, 66, 65, 71, 83, 32, 85, 78, 68, 69, 224, 158, 17, 22, 70, - 73, 78, 71, 69, 82, 32, 67, 79, 86, 69, 82, 73, 78, 71, 32, 67, 76, 79, - 83, 69, 68, 244, 67, 3, 82, 79, 76, 252, 231, 1, 13, 76, 79, 79, 75, 32, - 79, 70, 32, 84, 82, 73, 85, 77, 232, 130, 3, 8, 68, 73, 65, 71, 79, 78, - 65, 76, 1, 20, 85, 78, 69, 86, 69, 78, 32, 69, 89, 69, 83, 32, 65, 78, - 68, 32, 87, 65, 86, 89, 4, 252, 4, 3, 87, 66, 79, 227, 148, 19, 76, 2, - 173, 159, 22, 4, 66, 65, 78, 68, 4, 60, 6, 69, 68, 73, 67, 65, 76, 189, - 211, 25, 3, 79, 78, 79, 2, 177, 224, 25, 3, 32, 77, 65, 12, 90, 75, 36, - 4, 80, 69, 78, 32, 205, 161, 20, 10, 78, 69, 32, 69, 89, 69, 66, 82, 79, - 87, 2, 165, 142, 22, 4, 32, 71, 69, 83, 8, 116, 5, 77, 79, 85, 84, 72, - 217, 129, 24, 18, 69, 89, 69, 83, 32, 65, 78, 68, 32, 72, 65, 78, 68, 32, - 79, 86, 69, 82, 7, 11, 32, 4, 136, 252, 9, 3, 86, 79, 77, 141, 154, 9, 5, - 65, 78, 68, 32, 67, 6, 132, 1, 18, 65, 82, 84, 89, 32, 72, 79, 82, 78, - 32, 65, 78, 68, 32, 80, 65, 82, 84, 100, 2, 69, 69, 197, 148, 19, 4, 76, - 69, 65, 68, 2, 161, 184, 22, 2, 89, 32, 7, 29, 5, 32, 65, 78, 68, 32, 4, - 36, 3, 87, 73, 78, 231, 148, 19, 84, 2, 157, 177, 1, 4, 75, 73, 78, 71, - 4, 50, 69, 225, 187, 22, 6, 72, 69, 82, 77, 79, 77, 2, 157, 142, 27, 8, - 65, 82, 83, 32, 79, 70, 32, 74, 10, 34, 76, 185, 146, 22, 2, 65, 70, 8, - 84, 13, 73, 78, 71, 32, 68, 73, 65, 71, 79, 78, 65, 76, 32, 189, 212, 23, - 2, 69, 78, 6, 128, 1, 9, 67, 82, 79, 83, 83, 73, 78, 71, 32, 185, 167, - 19, 16, 73, 78, 32, 87, 72, 73, 84, 69, 32, 67, 73, 82, 67, 76, 69, 32, - 4, 232, 133, 16, 3, 82, 73, 83, 143, 169, 3, 78, 4, 222, 244, 14, 73, - 255, 137, 4, 77, 14, 50, 65, 50, 77, 44, 2, 82, 82, 147, 217, 18, 78, 4, - 212, 161, 8, 3, 82, 70, 85, 147, 231, 9, 84, 4, 204, 233, 8, 2, 73, 78, - 143, 177, 7, 65, 4, 184, 238, 7, 2, 73, 83, 163, 173, 19, 89, 56, 156, 1, - 5, 71, 85, 82, 69, 32, 38, 76, 178, 1, 78, 98, 82, 182, 2, 83, 236, 13, - 4, 86, 69, 32, 68, 213, 249, 4, 10, 69, 76, 68, 32, 72, 79, 67, 75, 69, - 89, 4, 226, 177, 25, 68, 131, 170, 1, 83, 10, 32, 2, 69, 32, 65, 2, 77, - 32, 6, 166, 187, 13, 70, 136, 154, 2, 3, 67, 65, 66, 191, 150, 8, 83, 4, - 52, 4, 80, 82, 79, 74, 197, 142, 20, 3, 70, 82, 65, 2, 171, 203, 15, 69, - 4, 72, 4, 71, 69, 82, 80, 205, 144, 25, 8, 73, 84, 69, 32, 80, 65, 82, - 84, 2, 167, 255, 15, 82, 22, 34, 69, 189, 1, 3, 83, 84, 32, 13, 56, 2, - 32, 69, 68, 4, 87, 79, 82, 75, 171, 170, 15, 67, 4, 146, 179, 13, 78, - 153, 208, 4, 8, 88, 84, 73, 78, 71, 85, 73, 83, 4, 216, 172, 23, 6, 32, - 83, 80, 65, 82, 75, 223, 233, 3, 83, 10, 190, 170, 5, 81, 216, 176, 10, - 8, 83, 84, 82, 79, 78, 71, 32, 73, 239, 215, 6, 80, 10, 38, 72, 129, 200, - 23, 3, 84, 69, 68, 9, 168, 248, 3, 13, 73, 78, 71, 32, 80, 79, 76, 69, - 32, 65, 78, 68, 32, 246, 202, 5, 69, 237, 202, 16, 19, 32, 67, 65, 75, - 69, 32, 87, 73, 84, 72, 32, 83, 87, 73, 82, 76, 32, 68, 69, 42, 50, 65, - 170, 1, 69, 98, 79, 194, 1, 85, 39, 89, 12, 114, 84, 148, 157, 4, 5, 80, - 80, 73, 78, 71, 136, 158, 3, 6, 71, 32, 73, 78, 32, 72, 161, 202, 17, 3, - 77, 73, 78, 6, 198, 208, 8, 32, 250, 197, 11, 66, 139, 229, 5, 78, 4, - 160, 221, 23, 8, 88, 69, 68, 32, 66, 73, 67, 69, 153, 145, 1, 7, 85, 82, - 45, 68, 69, 45, 76, 10, 46, 82, 28, 3, 87, 69, 82, 167, 203, 25, 80, 2, - 161, 137, 26, 2, 65, 76, 7, 17, 2, 32, 80, 4, 58, 85, 169, 178, 24, 8, - 76, 65, 89, 73, 78, 71, 32, 67, 2, 189, 166, 26, 4, 78, 67, 84, 85, 4, - 162, 152, 3, 83, 219, 224, 23, 84, 13, 25, 4, 73, 78, 71, 32, 10, 64, 6, - 83, 65, 85, 67, 69, 82, 166, 130, 10, 68, 223, 130, 10, 69, 7, 29, 5, 32, - 87, 73, 84, 72, 4, 34, 32, 1, 4, 79, 85, 84, 32, 2, 21, 3, 66, 69, 65, 2, - 147, 208, 26, 77, 54, 102, 71, 20, 2, 76, 68, 68, 2, 79, 84, 22, 82, 238, - 1, 85, 148, 255, 22, 2, 78, 68, 235, 205, 3, 88, 5, 171, 251, 26, 71, 4, - 164, 192, 9, 8, 73, 78, 71, 32, 72, 65, 78, 68, 223, 147, 17, 69, 5, 139, - 148, 14, 80, 16, 102, 75, 158, 206, 20, 77, 186, 181, 4, 32, 214, 115, - 67, 201, 120, 9, 84, 85, 78, 69, 32, 67, 79, 79, 75, 8, 80, 10, 32, 65, - 78, 68, 32, 75, 78, 73, 70, 69, 162, 150, 15, 69, 183, 163, 11, 73, 5, - 249, 207, 15, 7, 32, 87, 73, 84, 72, 32, 80, 22, 26, 82, 255, 161, 23, - 78, 20, 38, 32, 218, 2, 84, 183, 221, 18, 45, 16, 110, 67, 174, 1, 68, - 134, 165, 2, 66, 248, 147, 10, 7, 76, 69, 65, 70, 32, 67, 76, 182, 111, - 84, 155, 200, 11, 80, 4, 128, 170, 13, 3, 76, 85, 66, 209, 86, 32, 79, - 82, 78, 69, 82, 32, 65, 82, 82, 79, 87, 83, 32, 67, 73, 82, 67, 76, 73, - 78, 71, 32, 65, 78, 84, 73, 67, 76, 79, 67, 75, 87, 4, 21, 3, 79, 84, 32, - 4, 138, 197, 23, 80, 239, 128, 3, 77, 2, 183, 38, 72, 28, 78, 65, 226, 1, - 69, 98, 79, 209, 241, 16, 8, 73, 69, 68, 32, 83, 72, 82, 73, 10, 72, 6, - 67, 84, 73, 79, 78, 32, 69, 8, 77, 69, 32, 87, 73, 84, 72, 32, 4, 146, - 239, 19, 83, 177, 6, 9, 78, 85, 77, 69, 82, 65, 84, 79, 82, 6, 50, 80, - 194, 165, 2, 65, 217, 161, 21, 2, 84, 73, 2, 221, 238, 21, 2, 73, 67, 6, - 48, 6, 78, 67, 72, 32, 70, 82, 235, 251, 18, 69, 4, 146, 240, 25, 73, - 249, 12, 3, 65, 78, 67, 10, 52, 3, 78, 84, 45, 116, 2, 87, 78, 151, 197, - 26, 71, 4, 70, 84, 173, 138, 2, 11, 70, 65, 67, 73, 78, 71, 32, 66, 65, - 66, 89, 2, 197, 178, 12, 6, 73, 76, 84, 69, 68, 32, 5, 149, 135, 8, 6, - 73, 78, 71, 32, 70, 65, 226, 1, 80, 2, 76, 76, 134, 6, 78, 156, 233, 16, - 5, 69, 76, 32, 80, 85, 191, 251, 9, 83, 216, 1, 42, 32, 73, 6, 87, 73, - 68, 84, 72, 32, 10, 246, 255, 11, 77, 252, 169, 3, 2, 79, 85, 210, 232, - 8, 66, 151, 29, 83, 206, 1, 242, 1, 67, 42, 76, 78, 78, 30, 80, 66, 82, - 142, 1, 83, 38, 89, 158, 146, 10, 77, 198, 208, 10, 65, 158, 2, 68, 58, - 69, 98, 71, 118, 72, 202, 4, 81, 190, 143, 2, 87, 218, 28, 84, 250, 145, - 1, 70, 148, 176, 1, 6, 66, 82, 79, 75, 69, 78, 211, 7, 86, 10, 214, 230, - 20, 73, 62, 79, 159, 75, 69, 116, 42, 69, 138, 234, 20, 65, 243, 171, 4, - 79, 10, 162, 1, 70, 251, 235, 20, 83, 4, 246, 186, 21, 79, 35, 85, 6, 42, - 79, 250, 236, 20, 69, 203, 225, 2, 76, 2, 159, 148, 25, 85, 10, 36, 3, - 73, 71, 72, 131, 231, 24, 69, 8, 17, 2, 84, 32, 8, 228, 162, 20, 5, 87, - 72, 73, 84, 69, 202, 211, 2, 67, 210, 3, 80, 239, 7, 83, 4, 166, 176, 23, - 69, 147, 184, 1, 79, 2, 159, 130, 25, 69, 6, 136, 192, 11, 8, 67, 84, 73, - 79, 78, 32, 65, 80, 176, 247, 8, 5, 69, 82, 65, 76, 32, 143, 183, 1, 78, - 226, 20, 114, 65, 250, 6, 69, 198, 17, 73, 162, 1, 76, 134, 15, 79, 194, - 6, 82, 178, 92, 85, 246, 131, 22, 72, 139, 234, 3, 83, 142, 1, 42, 82, - 237, 221, 26, 4, 77, 69, 32, 68, 140, 1, 36, 3, 65, 89, 32, 235, 208, 25, - 76, 138, 1, 166, 1, 67, 210, 1, 83, 192, 2, 6, 86, 79, 87, 69, 76, 32, - 132, 166, 8, 6, 82, 69, 68, 85, 80, 76, 202, 202, 10, 72, 230, 168, 1, - 80, 170, 171, 3, 77, 191, 189, 1, 68, 52, 42, 79, 205, 1, 5, 65, 80, 73, - 84, 65, 8, 88, 10, 77, 66, 73, 78, 73, 78, 71, 32, 68, 79, 37, 8, 78, 83, - 79, 78, 65, 78, 84, 32, 4, 246, 135, 12, 85, 195, 241, 13, 84, 4, 190, - 136, 12, 78, 203, 145, 11, 71, 46, 36, 3, 77, 65, 76, 215, 250, 21, 85, - 44, 45, 9, 76, 32, 76, 69, 84, 84, 69, 82, 32, 44, 200, 1, 4, 79, 76, 68, - 32, 174, 137, 20, 78, 238, 231, 6, 66, 2, 67, 2, 68, 2, 70, 2, 71, 2, 72, - 2, 74, 2, 75, 2, 76, 2, 77, 2, 80, 2, 82, 2, 83, 2, 84, 2, 87, 2, 88, 2, - 89, 187, 2, 65, 4, 150, 241, 26, 75, 3, 78, 12, 44, 5, 83, 73, 71, 78, - 32, 139, 177, 26, 76, 10, 226, 178, 26, 69, 162, 64, 65, 2, 73, 3, 79, - 242, 2, 112, 2, 65, 82, 110, 77, 50, 79, 204, 189, 23, 9, 82, 77, 65, 78, - 32, 80, 69, 78, 78, 218, 173, 2, 84, 239, 105, 78, 7, 29, 5, 32, 87, 73, - 84, 72, 4, 224, 147, 14, 5, 79, 85, 84, 32, 72, 149, 160, 9, 5, 32, 72, - 65, 78, 68, 4, 26, 32, 211, 244, 21, 73, 2, 251, 255, 22, 83, 226, 2, 64, - 6, 77, 69, 84, 82, 73, 67, 73, 6, 82, 71, 73, 65, 78, 32, 6, 236, 250, - 19, 4, 65, 76, 76, 89, 213, 222, 1, 5, 32, 80, 82, 79, 80, 220, 2, 228, - 1, 6, 67, 65, 80, 73, 84, 65, 0, 4, 83, 77, 65, 76, 172, 3, 7, 76, 69, - 84, 84, 69, 82, 32, 180, 2, 24, 77, 84, 65, 86, 82, 85, 76, 73, 32, 67, - 65, 80, 73, 84, 65, 76, 32, 76, 69, 84, 84, 69, 82, 32, 165, 6, 2, 80, - 65, 80, 45, 9, 76, 32, 76, 69, 84, 84, 69, 82, 32, 80, 142, 2, 65, 34, - 72, 166, 5, 67, 118, 71, 130, 1, 74, 34, 75, 82, 80, 34, 83, 94, 90, 220, - 180, 5, 2, 84, 65, 250, 142, 8, 76, 246, 179, 2, 82, 154, 160, 9, 66, 2, - 77, 2, 88, 202, 40, 78, 2, 81, 230, 33, 86, 234, 47, 68, 14, 69, 2, 73, - 2, 79, 2, 85, 2, 89, 143, 57, 87, 4, 162, 155, 26, 69, 227, 79, 78, 10, - 46, 65, 226, 211, 26, 73, 2, 79, 215, 22, 69, 4, 178, 234, 26, 69, 3, 82, - 94, 254, 1, 85, 178, 2, 65, 42, 67, 74, 69, 46, 71, 34, 72, 98, 74, 34, - 75, 34, 76, 50, 80, 34, 83, 34, 84, 62, 90, 202, 247, 15, 82, 154, 160, - 9, 66, 2, 77, 2, 88, 202, 40, 78, 2, 81, 230, 33, 86, 234, 47, 68, 14, - 73, 2, 79, 2, 89, 142, 57, 87, 255, 2, 70, 4, 204, 248, 21, 4, 45, 66, - 82, 74, 203, 239, 4, 78, 92, 250, 1, 65, 42, 67, 74, 69, 46, 71, 34, 72, - 98, 74, 34, 75, 34, 76, 50, 80, 34, 83, 34, 84, 62, 90, 202, 247, 15, 82, - 154, 160, 9, 66, 2, 77, 2, 88, 202, 40, 78, 2, 81, 230, 33, 86, 234, 47, - 68, 14, 73, 2, 79, 2, 85, 2, 89, 142, 57, 87, 255, 2, 70, 6, 134, 150, - 26, 69, 2, 73, 227, 79, 78, 8, 38, 72, 230, 219, 25, 73, 215, 57, 65, 4, - 182, 149, 26, 73, 135, 23, 65, 4, 224, 136, 9, 2, 76, 73, 151, 220, 17, - 78, 4, 202, 154, 25, 72, 163, 122, 65, 12, 46, 65, 170, 205, 26, 73, 2, - 79, 215, 22, 69, 6, 26, 82, 227, 227, 26, 69, 5, 223, 220, 25, 68, 4, - 202, 153, 25, 72, 207, 64, 73, 4, 242, 193, 25, 72, 219, 81, 65, 4, 11, - 65, 4, 146, 210, 15, 66, 239, 144, 11, 83, 4, 162, 193, 25, 72, 223, 104, - 65, 4, 230, 226, 25, 72, 247, 47, 65, 6, 196, 183, 3, 6, 85, 82, 78, 69, - 68, 32, 211, 253, 1, 65, 4, 166, 192, 25, 72, 219, 81, 69, 2, 177, 166, - 20, 7, 82, 65, 71, 82, 65, 80, 72, 10, 48, 2, 77, 69, 20, 4, 78, 71, 69, - 82, 31, 82, 2, 179, 205, 25, 76, 2, 141, 184, 18, 2, 32, 82, 6, 38, 76, - 173, 235, 13, 3, 65, 70, 70, 5, 219, 204, 25, 83, 202, 1, 66, 65, 214, - 13, 79, 169, 144, 26, 7, 69, 73, 67, 72, 32, 83, 84, 194, 1, 84, 8, 71, - 79, 76, 73, 84, 73, 67, 32, 229, 12, 8, 83, 83, 32, 79, 70, 32, 77, 73, - 192, 1, 56, 6, 67, 65, 80, 73, 84, 65, 1, 4, 83, 77, 65, 76, 96, 45, 9, - 76, 32, 76, 69, 84, 84, 69, 82, 32, 96, 206, 1, 65, 22, 66, 42, 67, 94, - 68, 94, 70, 38, 71, 46, 73, 138, 2, 76, 58, 77, 66, 78, 34, 79, 30, 80, - 58, 82, 30, 83, 186, 1, 84, 110, 86, 22, 89, 90, 90, 226, 132, 19, 72, - 174, 128, 2, 85, 147, 174, 5, 75, 2, 163, 183, 26, 90, 4, 214, 3, 73, - 217, 198, 26, 2, 85, 75, 4, 48, 8, 65, 85, 68, 65, 84, 69, 32, 67, 15, - 72, 2, 11, 72, 2, 217, 250, 19, 2, 82, 73, 6, 42, 74, 30, 79, 221, 183, - 26, 2, 90, 69, 2, 145, 250, 19, 2, 69, 82, 2, 147, 235, 24, 66, 4, 186, - 138, 21, 82, 147, 171, 5, 73, 2, 21, 3, 76, 65, 71, 2, 207, 222, 21, 79, - 13, 38, 78, 54, 79, 141, 1, 2, 90, 72, 2, 145, 172, 26, 8, 73, 84, 73, - 65, 76, 32, 73, 90, 4, 33, 6, 84, 65, 84, 69, 68, 32, 4, 26, 66, 25, 2, - 83, 77, 2, 11, 73, 2, 35, 71, 2, 21, 3, 65, 76, 76, 2, 253, 209, 24, 2, - 32, 89, 4, 142, 213, 26, 73, 211, 2, 69, 4, 52, 9, 65, 84, 73, 78, 65, - 84, 69, 32, 77, 35, 74, 2, 173, 133, 11, 3, 89, 83, 76, 2, 249, 140, 9, - 3, 85, 68, 73, 2, 11, 65, 2, 187, 135, 21, 83, 4, 190, 177, 26, 78, 3, - 84, 4, 26, 79, 243, 213, 26, 69, 2, 133, 191, 22, 2, 75, 79, 2, 177, 235, - 21, 2, 73, 84, 14, 106, 72, 58, 76, 172, 240, 13, 6, 80, 73, 68, 69, 82, - 89, 201, 175, 10, 8, 77, 65, 76, 76, 32, 89, 85, 83, 6, 32, 2, 84, 65, - 171, 212, 26, 65, 5, 159, 172, 25, 80, 2, 239, 246, 19, 79, 6, 78, 86, - 144, 147, 22, 9, 82, 79, 75, 85, 84, 65, 83, 84, 73, 203, 172, 4, 83, 2, - 129, 162, 19, 2, 82, 73, 2, 135, 195, 24, 69, 12, 50, 69, 214, 244, 18, - 65, 250, 221, 7, 79, 3, 85, 6, 250, 130, 21, 83, 155, 143, 5, 82, 4, 156, - 145, 9, 3, 69, 77, 76, 145, 250, 15, 4, 72, 73, 86, 69, 2, 207, 206, 26, - 76, 6, 228, 238, 11, 13, 66, 69, 32, 87, 73, 84, 72, 32, 77, 69, 82, 73, - 68, 250, 198, 4, 87, 255, 135, 9, 86, 68, 162, 1, 65, 44, 12, 84, 72, 73, - 67, 32, 76, 69, 84, 84, 69, 82, 32, 230, 161, 18, 82, 206, 102, 71, 172, - 131, 5, 3, 78, 71, 71, 182, 186, 1, 79, 161, 75, 2, 76, 70, 4, 144, 183, - 15, 2, 76, 32, 183, 152, 11, 84, 54, 206, 2, 65, 50, 72, 46, 73, 46, 78, - 46, 80, 2, 81, 40, 2, 82, 65, 22, 84, 200, 137, 9, 2, 87, 73, 230, 141, - 12, 85, 188, 69, 3, 70, 65, 73, 204, 7, 4, 66, 65, 73, 82, 128, 22, 2, - 79, 84, 150, 30, 68, 158, 123, 77, 164, 131, 1, 3, 83, 65, 85, 162, 16, - 69, 184, 29, 3, 76, 65, 71, 146, 200, 1, 74, 196, 16, 2, 71, 73, 141, 12, - 2, 75, 85, 4, 204, 198, 24, 3, 73, 72, 86, 187, 131, 2, 72, 4, 214, 224, - 13, 87, 145, 226, 11, 2, 65, 71, 4, 230, 138, 9, 85, 217, 229, 14, 2, 71, - 71, 6, 154, 206, 15, 73, 253, 252, 8, 2, 65, 85, 2, 213, 134, 26, 5, 65, - 73, 82, 84, 72, 2, 235, 165, 26, 73, 4, 192, 169, 23, 2, 72, 73, 233, 69, - 2, 69, 73, 234, 9, 46, 65, 198, 5, 69, 206, 82, 73, 151, 3, 79, 152, 1, - 96, 7, 68, 85, 65, 84, 73, 79, 78, 32, 5, 78, 84, 72, 65, 32, 242, 179, - 20, 86, 139, 129, 5, 80, 2, 11, 32, 2, 151, 184, 25, 67, 146, 1, 120, 7, - 76, 69, 84, 84, 69, 82, 32, 212, 2, 5, 83, 73, 71, 78, 32, 226, 231, 22, - 65, 248, 8, 2, 86, 79, 239, 195, 3, 79, 100, 214, 1, 86, 250, 235, 22, - 65, 38, 68, 114, 84, 230, 5, 85, 206, 201, 1, 73, 42, 76, 250, 192, 1, - 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 206, 40, 79, 162, - 8, 69, 158, 20, 72, 2, 77, 2, 82, 3, 89, 14, 60, 5, 69, 68, 73, 67, 32, - 210, 242, 22, 79, 139, 211, 3, 65, 4, 244, 143, 24, 6, 68, 79, 85, 66, - 76, 69, 235, 240, 1, 65, 16, 66, 67, 250, 176, 22, 78, 242, 60, 65, 182, - 1, 80, 179, 146, 3, 86, 4, 226, 238, 7, 79, 195, 193, 14, 65, 192, 8, 76, - 10, 65, 84, 69, 82, 45, 84, 72, 65, 78, 32, 206, 7, 69, 219, 180, 25, 89, - 56, 134, 1, 65, 150, 3, 66, 62, 79, 216, 2, 11, 69, 81, 85, 65, 76, 32, - 84, 79, 32, 79, 82, 222, 205, 6, 67, 138, 4, 87, 231, 227, 18, 83, 16, - 44, 5, 66, 79, 86, 69, 32, 187, 210, 6, 78, 12, 150, 1, 83, 180, 1, 19, - 68, 79, 85, 66, 76, 69, 45, 76, 73, 78, 69, 32, 69, 81, 85, 65, 76, 32, - 65, 224, 204, 6, 4, 76, 69, 83, 83, 135, 211, 18, 82, 6, 148, 1, 7, 73, - 77, 73, 76, 65, 82, 32, 225, 207, 6, 23, 76, 65, 78, 84, 69, 68, 32, 69, - 81, 85, 65, 76, 32, 65, 66, 79, 86, 69, 32, 76, 69, 83, 83, 4, 26, 65, - 183, 207, 6, 79, 2, 65, 3, 66, 79, 86, 6, 40, 4, 69, 83, 73, 68, 159, - 208, 6, 85, 2, 231, 2, 69, 20, 40, 2, 82, 32, 245, 1, 3, 86, 69, 82, 16, - 120, 16, 83, 76, 65, 78, 84, 69, 68, 32, 69, 81, 85, 65, 76, 32, 84, 79, - 230, 209, 6, 65, 138, 247, 12, 69, 131, 227, 4, 76, 9, 49, 10, 32, 87, - 73, 84, 72, 32, 68, 79, 84, 32, 6, 44, 5, 65, 66, 79, 86, 69, 163, 153, - 18, 73, 5, 195, 138, 26, 32, 4, 52, 7, 76, 65, 80, 80, 73, 78, 71, 227, - 232, 19, 32, 2, 185, 170, 24, 2, 32, 76, 134, 8, 36, 2, 75, 32, 185, 73, - 2, 78, 32, 254, 7, 130, 3, 65, 216, 15, 8, 67, 65, 80, 73, 84, 65, 76, - 32, 182, 11, 68, 134, 1, 70, 68, 2, 73, 78, 222, 3, 75, 138, 1, 76, 174, - 3, 78, 66, 77, 84, 3, 88, 69, 83, 22, 79, 202, 1, 80, 90, 82, 182, 1, 83, - 130, 22, 84, 200, 2, 13, 85, 80, 83, 73, 76, 79, 78, 32, 87, 73, 84, 72, - 32, 150, 1, 86, 142, 2, 89, 178, 225, 7, 66, 248, 175, 3, 4, 71, 82, 65, - 77, 204, 23, 2, 90, 69, 147, 249, 11, 81, 112, 92, 10, 67, 82, 79, 80, - 72, 79, 78, 73, 67, 32, 172, 14, 6, 78, 79, 32, 84, 69, 76, 23, 82, 106, - 188, 2, 6, 65, 84, 84, 73, 67, 32, 222, 5, 67, 92, 3, 78, 65, 88, 32, 12, - 68, 69, 76, 80, 72, 73, 67, 32, 70, 73, 86, 69, 0, 14, 83, 84, 82, 65, - 84, 73, 65, 78, 32, 70, 73, 70, 84, 89, 40, 11, 69, 80, 73, 68, 65, 85, - 82, 69, 65, 78, 32, 112, 3, 72, 69, 82, 164, 1, 9, 77, 69, 83, 83, 69, - 78, 73, 65, 78, 35, 84, 48, 72, 2, 70, 73, 180, 2, 4, 79, 78, 69, 32, - 205, 1, 4, 84, 69, 78, 32, 26, 36, 3, 70, 84, 89, 105, 2, 86, 69, 11, 11, - 32, 8, 22, 84, 171, 4, 83, 6, 48, 7, 72, 79, 85, 83, 65, 78, 68, 215, 3, - 65, 5, 231, 3, 32, 17, 11, 32, 14, 56, 7, 72, 85, 78, 68, 82, 69, 68, 18, - 84, 143, 3, 83, 7, 131, 2, 32, 6, 48, 7, 72, 79, 85, 83, 65, 78, 68, 187, - 2, 65, 5, 213, 1, 2, 32, 84, 14, 98, 72, 48, 7, 84, 72, 79, 85, 83, 65, - 78, 174, 174, 24, 81, 241, 225, 1, 5, 68, 82, 65, 67, 72, 6, 44, 5, 85, - 78, 68, 82, 69, 143, 174, 24, 65, 4, 17, 2, 68, 32, 4, 22, 84, 131, 1, - 83, 2, 95, 65, 8, 30, 84, 86, 83, 175, 1, 77, 4, 50, 65, 21, 8, 72, 79, - 85, 83, 65, 78, 68, 32, 2, 187, 174, 16, 76, 2, 11, 83, 2, 145, 175, 24, - 2, 84, 65, 4, 88, 5, 65, 82, 89, 83, 84, 145, 1, 12, 89, 82, 69, 78, 65, - 73, 67, 32, 84, 87, 79, 32, 2, 101, 5, 73, 65, 78, 32, 70, 2, 17, 2, 32, - 77, 2, 199, 144, 13, 78, 6, 30, 70, 29, 3, 84, 87, 79, 2, 177, 178, 15, - 2, 73, 86, 5, 11, 32, 2, 233, 144, 10, 5, 68, 82, 65, 67, 72, 8, 112, 8, - 77, 73, 79, 78, 73, 65, 78, 32, 189, 135, 25, 14, 65, 69, 85, 77, 32, 79, - 78, 69, 32, 80, 76, 69, 84, 72, 6, 238, 185, 12, 70, 134, 159, 12, 84, - 191, 58, 79, 2, 11, 32, 2, 183, 216, 24, 84, 32, 92, 8, 72, 69, 83, 80, - 73, 65, 78, 32, 129, 1, 10, 82, 79, 69, 90, 69, 78, 73, 65, 78, 32, 20, - 40, 2, 70, 73, 38, 84, 135, 151, 18, 79, 6, 162, 215, 20, 86, 231, 230, - 3, 70, 8, 202, 174, 15, 72, 178, 172, 10, 69, 239, 48, 87, 12, 36, 2, 70, - 73, 209, 24, 2, 84, 69, 8, 142, 176, 18, 86, 205, 250, 6, 3, 70, 84, 89, - 2, 159, 132, 10, 69, 4, 240, 215, 22, 2, 79, 85, 161, 181, 1, 3, 84, 65, - 66, 154, 2, 66, 76, 174, 45, 82, 66, 68, 220, 226, 7, 2, 75, 65, 135, 6, - 84, 144, 2, 44, 6, 69, 84, 84, 69, 82, 32, 239, 45, 85, 142, 2, 198, 2, - 65, 190, 1, 69, 28, 4, 73, 79, 84, 65, 128, 1, 2, 79, 77, 156, 3, 3, 82, - 72, 79, 46, 83, 48, 7, 85, 80, 83, 73, 76, 79, 78, 146, 33, 80, 170, 2, - 84, 202, 183, 5, 68, 144, 176, 2, 2, 75, 65, 166, 192, 1, 71, 190, 132, - 11, 67, 194, 149, 2, 66, 2, 72, 2, 90, 166, 1, 76, 230, 231, 2, 89, 210, - 43, 77, 2, 78, 147, 17, 88, 48, 68, 4, 76, 80, 72, 65, 213, 28, 8, 82, - 67, 72, 65, 73, 67, 32, 83, 47, 33, 6, 32, 87, 73, 84, 72, 32, 44, 242, - 2, 68, 30, 80, 226, 29, 86, 226, 5, 79, 246, 236, 3, 84, 239, 167, 5, 77, - 62, 186, 1, 84, 131, 27, 80, 31, 33, 6, 32, 87, 73, 84, 72, 32, 28, 186, - 5, 68, 136, 25, 2, 80, 83, 158, 1, 86, 226, 5, 79, 246, 236, 3, 84, 239, - 167, 5, 77, 62, 28, 2, 69, 71, 235, 34, 73, 42, 11, 65, 43, 33, 6, 32, - 87, 73, 84, 72, 32, 40, 54, 68, 30, 80, 194, 35, 79, 22, 86, 227, 236, 3, - 84, 16, 65, 4, 65, 83, 73, 65, 18, 36, 4, 83, 73, 76, 73, 211, 16, 82, - 17, 29, 5, 32, 65, 78, 68, 32, 14, 44, 2, 79, 88, 0, 3, 86, 65, 82, 23, - 80, 4, 81, 2, 73, 65, 6, 60, 10, 69, 82, 73, 83, 80, 79, 77, 69, 78, 73, - 175, 15, 82, 5, 169, 15, 7, 32, 65, 78, 68, 32, 80, 82, 5, 161, 35, 7, - 32, 87, 73, 84, 72, 32, 68, 6, 222, 140, 8, 73, 226, 194, 17, 65, 239, - 48, 72, 23, 33, 6, 32, 87, 73, 84, 72, 32, 20, 66, 68, 166, 26, 86, 226, - 5, 79, 246, 236, 3, 84, 239, 167, 5, 77, 10, 130, 24, 65, 181, 176, 22, - 5, 73, 65, 76, 89, 84, 18, 76, 9, 73, 65, 76, 89, 84, 73, 75, 65, 32, 32, - 3, 82, 65, 67, 227, 22, 65, 8, 174, 24, 65, 199, 243, 3, 84, 2, 243, 186, - 11, 72, 4, 40, 3, 73, 86, 69, 1, 3, 79, 85, 82, 2, 205, 37, 2, 32, 79, - 76, 144, 1, 27, 83, 84, 82, 85, 77, 69, 78, 84, 65, 76, 32, 78, 79, 84, - 65, 84, 73, 79, 78, 32, 83, 89, 77, 66, 79, 76, 45, 161, 154, 22, 2, 68, - 73, 74, 70, 49, 70, 50, 62, 51, 62, 52, 170, 37, 53, 206, 243, 25, 55, 3, - 56, 17, 174, 154, 26, 49, 2, 50, 2, 51, 2, 52, 2, 55, 2, 56, 3, 57, 15, - 234, 153, 26, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 57, 12, 174, 153, 26, - 48, 2, 50, 2, 54, 2, 55, 2, 56, 3, 57, 17, 242, 152, 26, 48, 2, 50, 2, - 51, 2, 53, 2, 55, 2, 56, 3, 57, 8, 54, 65, 38, 79, 169, 32, 6, 89, 65, - 84, 72, 79, 83, 4, 134, 255, 7, 80, 203, 133, 17, 73, 2, 11, 82, 2, 11, - 79, 2, 135, 244, 23, 78, 32, 128, 1, 6, 69, 84, 84, 69, 82, 32, 168, 2, - 6, 79, 87, 69, 82, 32, 78, 32, 6, 85, 78, 65, 84, 69, 32, 145, 194, 22, - 2, 73, 84, 24, 94, 83, 140, 13, 9, 65, 82, 67, 72, 65, 73, 67, 32, 75, 2, - 75, 182, 11, 68, 207, 172, 25, 89, 16, 88, 13, 77, 65, 76, 76, 32, 67, - 65, 80, 73, 84, 65, 76, 32, 210, 12, 65, 247, 243, 7, 84, 12, 74, 80, - 226, 192, 9, 71, 194, 156, 9, 79, 218, 206, 2, 82, 139, 176, 1, 76, 4, - 194, 128, 26, 83, 219, 19, 73, 2, 221, 134, 10, 3, 85, 77, 69, 4, 222, - 25, 83, 215, 224, 7, 69, 4, 80, 4, 69, 84, 82, 69, 241, 192, 23, 10, 85, - 83, 73, 67, 65, 76, 32, 76, 69, 73, 2, 163, 212, 2, 84, 12, 88, 3, 78, - 69, 32, 198, 236, 9, 88, 148, 155, 5, 3, 85, 78, 75, 253, 198, 5, 2, 66, + 73, 82, 67, 76, 69, 68, 32, 83, 42, 38, 83, 182, 32, 78, 255, 202, 20, + 68, 22, 49, 10, 65, 78, 83, 45, 83, 69, 82, 73, 70, 32, 22, 254, 31, 78, + 199, 162, 27, 68, 4, 184, 161, 27, 15, 67, 85, 82, 82, 69, 78, 84, 32, + 83, 89, 77, 66, 79, 76, 32, 187, 248, 1, 72, 12, 98, 65, 144, 1, 5, 67, + 79, 78, 84, 73, 244, 227, 15, 3, 84, 79, 82, 253, 152, 10, 3, 71, 85, 73, + 6, 76, 9, 80, 80, 79, 73, 78, 84, 69, 68, 32, 169, 149, 27, 4, 66, 76, + 69, 68, 4, 196, 162, 17, 7, 66, 85, 84, 32, 82, 69, 76, 203, 212, 11, 70, + 2, 53, 11, 78, 85, 79, 85, 83, 32, 85, 78, 68, 69, 82, 2, 209, 132, 17, + 3, 76, 73, 78, 156, 1, 80, 9, 69, 83, 32, 65, 75, 85, 82, 85, 32, 238, 5, + 73, 229, 253, 16, 2, 79, 82, 144, 1, 142, 2, 68, 36, 7, 76, 69, 84, 84, + 69, 82, 32, 236, 1, 5, 83, 73, 71, 78, 32, 58, 86, 240, 162, 9, 6, 77, + 69, 68, 73, 65, 76, 234, 132, 5, 71, 186, 232, 4, 69, 148, 209, 4, 12, + 80, 82, 69, 70, 73, 88, 69, 68, 32, 78, 65, 83, 209, 129, 5, 7, 73, 78, + 73, 84, 73, 65, 76, 22, 218, 207, 25, 79, 191, 236, 1, 73, 84, 218, 200, + 10, 84, 242, 238, 11, 89, 182, 230, 2, 65, 162, 52, 68, 214, 6, 85, 186, + 202, 1, 73, 42, 76, 226, 195, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 75, + 2, 80, 138, 69, 72, 2, 74, 2, 77, 2, 82, 2, 86, 2, 90, 186, 2, 69, 3, 79, + 8, 210, 229, 24, 72, 210, 42, 67, 98, 78, 139, 216, 3, 65, 18, 64, 10, + 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 239, 240, 26, 73, 16, 218, 151, + 15, 65, 178, 190, 10, 85, 186, 202, 1, 73, 198, 140, 2, 69, 3, 79, 10, + 68, 5, 83, 73, 79, 78, 32, 212, 237, 1, 2, 78, 71, 159, 168, 26, 68, 6, + 26, 83, 183, 230, 5, 84, 4, 194, 192, 27, 76, 187, 100, 73, 2, 177, 138, + 19, 3, 32, 76, 65, 4, 234, 149, 28, 83, 167, 88, 70, 212, 5, 188, 2, 6, + 67, 85, 77, 69, 78, 84, 124, 7, 69, 83, 32, 78, 79, 84, 32, 194, 3, 71, + 202, 3, 76, 36, 10, 77, 73, 78, 79, 32, 84, 73, 76, 69, 32, 226, 1, 78, + 38, 84, 246, 2, 85, 204, 17, 2, 87, 78, 136, 191, 16, 8, 32, 78, 79, 84, + 32, 76, 73, 84, 184, 141, 12, 8, 86, 69, 32, 79, 70, 32, 80, 69, 174, 4, + 79, 235, 25, 68, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 40, 4, 84, 69, 88, + 84, 251, 138, 2, 80, 5, 245, 138, 2, 6, 32, 65, 78, 68, 32, 80, 22, 164, + 1, 11, 67, 79, 78, 84, 65, 73, 78, 32, 65, 83, 32, 92, 6, 68, 73, 86, 73, + 68, 69, 112, 2, 80, 82, 48, 7, 83, 85, 67, 67, 69, 69, 68, 237, 220, 22, + 2, 70, 79, 6, 248, 1, 15, 78, 79, 82, 77, 65, 76, 32, 83, 85, 66, 71, 82, + 79, 85, 80, 207, 252, 20, 77, 5, 197, 128, 22, 23, 32, 87, 73, 84, 72, + 32, 82, 69, 86, 69, 82, 83, 69, 68, 32, 78, 69, 71, 65, 84, 73, 79, 78, + 6, 44, 5, 69, 67, 69, 68, 69, 195, 167, 28, 79, 5, 205, 159, 9, 2, 32, + 79, 125, 36, 3, 82, 65, 32, 191, 230, 28, 32, 120, 120, 7, 76, 69, 84, + 84, 69, 82, 32, 208, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, + 178, 173, 23, 65, 187, 2, 83, 88, 222, 196, 25, 65, 38, 68, 82, 82, 34, + 84, 230, 5, 85, 186, 202, 1, 73, 138, 196, 1, 78, 46, 83, 82, 66, 2, 67, + 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, 2, 86, 2, 89, 186, + 2, 69, 3, 79, 22, 182, 146, 19, 86, 174, 183, 6, 65, 38, 85, 186, 202, 1, + 73, 198, 140, 2, 69, 3, 79, 4, 194, 148, 21, 80, 199, 193, 2, 76, 200, 1, + 72, 8, 72, 79, 82, 73, 90, 79, 78, 84, 1, 6, 86, 69, 82, 84, 73, 67, 100, + 17, 2, 65, 76, 100, 32, 2, 45, 48, 207, 135, 24, 32, 98, 58, 48, 2, 49, + 2, 50, 2, 51, 2, 52, 2, 53, 3, 54, 14, 201, 173, 20, 2, 45, 48, 4, 210, + 128, 27, 75, 219, 150, 1, 71, 26, 34, 32, 57, 4, 84, 69, 68, 32, 8, 214, + 146, 25, 80, 34, 77, 194, 71, 79, 195, 198, 2, 65, 18, 214, 1, 67, 34, + 83, 180, 147, 16, 3, 76, 73, 78, 148, 134, 1, 4, 79, 66, 69, 76, 244, 9, + 9, 84, 82, 65, 78, 83, 80, 79, 83, 73, 202, 230, 6, 70, 161, 41, 14, 82, + 73, 71, 72, 84, 45, 80, 79, 73, 78, 84, 73, 78, 71, 4, 198, 199, 27, 73, + 239, 16, 82, 4, 170, 132, 27, 79, 183, 116, 81, 162, 1, 40, 3, 66, 76, + 69, 137, 17, 2, 71, 72, 160, 1, 42, 32, 190, 10, 45, 241, 5, 2, 68, 32, + 106, 226, 2, 67, 174, 1, 68, 38, 72, 32, 4, 73, 78, 84, 69, 38, 76, 144, + 1, 7, 78, 69, 83, 84, 69, 68, 32, 74, 80, 66, 83, 130, 2, 85, 36, 9, 86, + 69, 82, 84, 73, 67, 65, 76, 32, 136, 164, 6, 6, 79, 66, 76, 73, 81, 85, + 196, 187, 2, 9, 82, 73, 71, 72, 84, 32, 65, 82, 67, 134, 189, 3, 65, 218, + 216, 9, 69, 246, 198, 4, 81, 153, 106, 6, 87, 65, 86, 89, 32, 79, 24, + 100, 7, 73, 82, 67, 76, 69, 68, 32, 140, 207, 7, 4, 85, 82, 76, 89, 253, + 197, 1, 4, 79, 76, 79, 78, 20, 26, 78, 255, 202, 20, 68, 2, 169, 215, 2, + 5, 85, 77, 66, 69, 82, 4, 202, 242, 18, 79, 167, 210, 6, 65, 4, 174, 197, + 20, 73, 239, 63, 89, 4, 198, 147, 26, 82, 179, 246, 1, 71, 12, 54, 79, + 217, 158, 17, 7, 69, 70, 84, 32, 65, 82, 67, 10, 26, 87, 243, 207, 25, + 71, 6, 26, 45, 135, 249, 27, 32, 4, 150, 196, 20, 82, 211, 1, 57, 6, 184, + 140, 17, 9, 76, 69, 83, 83, 45, 84, 72, 65, 78, 255, 241, 9, 71, 8, 26, + 82, 231, 138, 27, 76, 6, 214, 153, 1, 69, 195, 242, 15, 73, 18, 80, 6, + 81, 85, 65, 82, 69, 32, 30, 84, 50, 85, 173, 221, 13, 4, 79, 76, 73, 68, + 4, 146, 223, 25, 73, 55, 85, 4, 212, 197, 12, 3, 65, 67, 75, 215, 197, 4, + 82, 8, 58, 83, 246, 151, 1, 67, 250, 147, 21, 80, 175, 247, 4, 66, 2, + 169, 168, 28, 4, 80, 69, 78, 83, 4, 226, 237, 18, 80, 255, 210, 9, 78, + 10, 36, 3, 66, 65, 82, 159, 245, 27, 76, 9, 11, 32, 6, 52, 7, 68, 79, 85, + 66, 76, 69, 32, 195, 230, 26, 76, 4, 190, 230, 26, 76, 51, 82, 48, 112, + 5, 76, 73, 78, 69, 32, 220, 1, 7, 83, 84, 82, 85, 67, 75, 32, 133, 227, + 8, 7, 69, 78, 68, 69, 68, 32, 77, 12, 48, 8, 83, 76, 65, 78, 84, 69, 68, + 32, 75, 69, 8, 70, 69, 56, 7, 71, 82, 69, 65, 84, 69, 82, 1, 4, 76, 69, + 83, 83, 4, 161, 186, 20, 9, 81, 85, 65, 76, 32, 84, 79, 32, 79, 2, 245, + 171, 14, 5, 45, 84, 72, 65, 78, 34, 160, 1, 8, 67, 65, 80, 73, 84, 65, + 76, 32, 92, 7, 73, 84, 65, 76, 73, 67, 32, 124, 6, 83, 77, 65, 76, 76, + 32, 141, 215, 13, 8, 78, 45, 65, 82, 89, 32, 83, 85, 18, 206, 169, 12, + 71, 242, 213, 14, 80, 198, 140, 2, 67, 2, 72, 2, 78, 2, 81, 2, 82, 3, 90, + 10, 76, 6, 83, 77, 65, 76, 76, 32, 181, 237, 21, 7, 67, 65, 80, 73, 84, + 65, 76, 8, 214, 138, 29, 68, 2, 69, 2, 73, 3, 74, 4, 246, 167, 12, 71, + 223, 206, 16, 80, 6, 166, 167, 10, 70, 26, 77, 159, 205, 17, 83, 2, 139, + 144, 28, 78, 166, 1, 50, 32, 158, 1, 45, 189, 1, 4, 87, 65, 82, 68, 10, + 60, 4, 84, 65, 67, 75, 142, 224, 25, 70, 30, 82, 195, 79, 65, 5, 29, 5, + 32, 87, 73, 84, 72, 2, 11, 32, 2, 11, 67, 2, 249, 251, 20, 4, 73, 82, 67, + 76, 28, 60, 9, 80, 79, 73, 78, 84, 73, 78, 71, 32, 255, 224, 25, 70, 24, + 62, 83, 162, 227, 25, 65, 86, 69, 62, 70, 46, 82, 207, 1, 84, 4, 230, + 182, 17, 84, 161, 175, 8, 6, 77, 65, 76, 76, 32, 82, 128, 1, 56, 8, 32, + 70, 65, 67, 73, 78, 71, 32, 89, 2, 83, 32, 8, 54, 72, 1, 9, 78, 79, 84, + 67, 72, 69, 68, 32, 72, 4, 161, 223, 27, 3, 79, 79, 75, 120, 178, 1, 65, + 200, 2, 6, 66, 76, 65, 67, 75, 32, 38, 84, 144, 238, 8, 2, 87, 72, 138, + 156, 8, 90, 178, 137, 9, 68, 50, 70, 82, 72, 150, 4, 67, 46, 81, 42, 82, + 22, 83, 247, 7, 80, 34, 40, 4, 82, 82, 79, 87, 227, 142, 26, 78, 33, 11, + 32, 30, 144, 1, 5, 87, 73, 84, 72, 32, 184, 234, 13, 14, 76, 69, 70, 84, + 87, 65, 82, 68, 83, 32, 79, 70, 32, 85, 190, 164, 12, 65, 178, 10, 70, + 179, 5, 84, 22, 140, 186, 5, 6, 67, 79, 82, 78, 69, 82, 146, 215, 20, 68, + 58, 76, 42, 77, 38, 78, 58, 83, 66, 69, 250, 12, 84, 135, 58, 72, 6, 170, + 137, 21, 65, 231, 137, 5, 67, 36, 36, 2, 82, 73, 161, 2, 2, 87, 79, 32, + 44, 5, 65, 78, 71, 76, 69, 191, 161, 26, 80, 30, 56, 8, 45, 72, 69, 65, + 68, 69, 68, 32, 251, 166, 26, 32, 28, 68, 5, 65, 82, 82, 79, 87, 190, + 136, 17, 90, 254, 150, 9, 68, 39, 80, 23, 11, 32, 20, 232, 154, 26, 15, + 76, 69, 70, 84, 87, 65, 82, 68, 83, 32, 79, 70, 32, 85, 80, 98, 84, 23, + 87, 4, 182, 160, 26, 32, 105, 15, 45, 72, 69, 65, 68, 69, 68, 32, 65, 82, + 82, 79, 87, 32, 87, 22, 138, 1, 65, 106, 79, 152, 1, 12, 85, 77, 32, 87, + 73, 84, 72, 32, 68, 82, 85, 77, 248, 244, 16, 6, 73, 86, 69, 32, 83, 76, + 139, 237, 10, 69, 8, 250, 204, 2, 67, 180, 146, 6, 10, 70, 84, 73, 78, + 71, 32, 80, 79, 73, 78, 233, 245, 15, 3, 71, 79, 78, 8, 56, 6, 77, 69, + 68, 65, 82, 89, 34, 80, 139, 196, 27, 79, 2, 209, 241, 20, 3, 32, 67, 65, + 4, 164, 184, 18, 6, 32, 79, 70, 32, 66, 76, 211, 141, 10, 76, 2, 213, + 223, 12, 3, 83, 84, 73, 162, 2, 76, 7, 80, 76, 79, 89, 65, 78, 32, 240, + 139, 20, 2, 77, 80, 219, 234, 8, 67, 158, 2, 212, 2, 6, 65, 70, 70, 73, + 88, 32, 160, 7, 7, 76, 69, 84, 84, 69, 82, 32, 180, 190, 1, 16, 84, 72, + 73, 67, 75, 32, 76, 69, 84, 84, 69, 82, 32, 83, 69, 76, 232, 180, 3, 4, + 68, 79, 85, 66, 248, 234, 16, 19, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, + 78, 32, 67, 72, 73, 78, 79, 79, 75, 145, 206, 5, 11, 83, 73, 71, 78, 32, + 79, 32, 87, 73, 84, 72, 64, 140, 1, 9, 65, 84, 84, 65, 67, 72, 69, 68, + 32, 184, 1, 5, 72, 73, 71, 72, 32, 106, 76, 40, 4, 82, 73, 71, 72, 173, + 2, 4, 77, 73, 68, 32, 14, 112, 2, 84, 65, 232, 4, 13, 76, 69, 70, 84, 45, + 84, 79, 45, 82, 73, 71, 72, 84, 26, 83, 158, 246, 20, 69, 3, 73, 6, 44, + 5, 78, 71, 69, 78, 84, 131, 233, 27, 73, 5, 247, 250, 20, 32, 20, 174, 2, + 76, 50, 84, 38, 86, 250, 174, 8, 71, 186, 2, 65, 238, 209, 18, 87, 134, + 26, 67, 227, 131, 1, 68, 24, 36, 2, 69, 70, 29, 3, 79, 87, 32, 2, 213, 2, + 3, 84, 32, 72, 22, 94, 65, 38, 76, 50, 84, 38, 86, 250, 174, 8, 71, 166, + 212, 18, 87, 134, 26, 67, 227, 131, 1, 68, 4, 186, 236, 21, 67, 195, 233, + 5, 82, 4, 216, 128, 8, 3, 79, 78, 71, 131, 214, 19, 73, 2, 185, 166, 8, + 4, 73, 71, 72, 84, 4, 37, 7, 69, 82, 84, 73, 67, 65, 76, 5, 131, 1, 32, + 4, 42, 72, 41, 6, 86, 69, 82, 84, 73, 67, 2, 37, 7, 79, 82, 73, 90, 79, + 78, 84, 2, 17, 2, 65, 76, 2, 11, 32, 2, 11, 83, 2, 133, 228, 27, 2, 69, + 67, 214, 1, 222, 1, 65, 22, 68, 34, 69, 30, 70, 22, 71, 22, 74, 170, 1, + 75, 66, 76, 50, 77, 62, 78, 134, 1, 79, 50, 80, 86, 82, 74, 83, 210, 2, + 84, 66, 85, 42, 86, 38, 87, 70, 88, 154, 231, 27, 72, 142, 60, 73, 206, + 41, 89, 215, 22, 66, 5, 151, 201, 28, 79, 7, 142, 176, 28, 32, 223, 61, + 72, 7, 202, 237, 28, 69, 3, 85, 5, 207, 157, 28, 32, 5, 171, 243, 24, 32, + 19, 11, 32, 16, 76, 8, 87, 73, 84, 72, 32, 68, 79, 84, 238, 5, 77, 2, 78, + 167, 192, 27, 83, 5, 53, 11, 83, 32, 73, 78, 83, 73, 68, 69, 32, 65, 78, + 2, 239, 238, 27, 68, 9, 26, 32, 199, 235, 28, 75, 4, 222, 241, 24, 82, + 231, 249, 3, 77, 9, 148, 211, 22, 3, 79, 78, 71, 139, 152, 6, 72, 11, 11, + 32, 8, 162, 4, 78, 190, 192, 27, 87, 135, 166, 1, 83, 19, 38, 32, 49, 5, + 65, 83, 65, 76, 32, 8, 202, 3, 77, 190, 192, 27, 87, 135, 166, 1, 83, 8, + 218, 233, 28, 65, 2, 73, 2, 79, 3, 85, 11, 158, 232, 28, 79, 146, 1, 65, + 2, 85, 3, 87, 9, 52, 7, 69, 82, 78, 73, 78, 32, 65, 235, 152, 28, 32, 4, + 198, 232, 28, 77, 3, 78, 11, 148, 208, 22, 6, 79, 77, 65, 78, 73, 65, + 186, 218, 5, 32, 223, 61, 72, 49, 58, 32, 164, 1, 5, 76, 79, 65, 78, 32, + 239, 177, 6, 72, 26, 102, 74, 22, 75, 2, 80, 2, 84, 20, 7, 87, 73, 84, + 72, 32, 68, 79, 154, 230, 28, 77, 2, 78, 3, 83, 5, 227, 168, 28, 32, 5, + 207, 173, 28, 32, 4, 167, 223, 12, 84, 18, 74, 69, 214, 198, 5, 79, 134, + 207, 22, 65, 210, 78, 68, 146, 1, 74, 3, 85, 6, 182, 229, 28, 69, 2, 72, + 3, 78, 9, 26, 32, 251, 228, 28, 72, 4, 146, 235, 24, 82, 231, 249, 3, 83, + 9, 242, 148, 28, 32, 226, 79, 72, 3, 73, 5, 137, 149, 7, 4, 79, 67, 65, + 76, 17, 66, 79, 234, 170, 28, 32, 134, 37, 69, 218, 19, 65, 2, 72, 3, 73, + 5, 195, 227, 28, 87, 140, 29, 234, 2, 65, 188, 3, 10, 68, 73, 84, 79, 82, + 73, 65, 76, 32, 67, 22, 71, 188, 75, 4, 73, 71, 72, 84, 146, 2, 76, 194, + 9, 77, 214, 5, 78, 246, 3, 79, 36, 2, 81, 85, 182, 8, 82, 222, 1, 83, + 118, 84, 242, 31, 85, 226, 1, 88, 128, 5, 2, 89, 69, 208, 86, 4, 45, 77, + 65, 73, 132, 145, 19, 3, 74, 69, 67, 244, 152, 2, 8, 86, 69, 82, 71, 82, + 69, 69, 78, 167, 199, 5, 80, 22, 38, 82, 218, 248, 26, 83, 135, 65, 71, + 19, 30, 32, 137, 1, 2, 84, 72, 6, 88, 3, 79, 70, 32, 181, 232, 4, 13, 87, + 73, 84, 72, 32, 72, 69, 65, 82, 73, 78, 71, 32, 4, 132, 196, 11, 2, 77, + 65, 195, 227, 6, 82, 11, 17, 2, 32, 71, 8, 44, 5, 76, 79, 66, 69, 32, + 239, 165, 25, 82, 6, 70, 65, 173, 242, 21, 11, 69, 85, 82, 79, 80, 69, + 45, 65, 70, 82, 73, 4, 244, 142, 10, 4, 77, 69, 82, 73, 193, 154, 2, 11, + 83, 73, 65, 45, 65, 85, 83, 84, 82, 65, 76, 2, 187, 178, 2, 79, 176, 17, + 112, 18, 89, 80, 84, 73, 65, 78, 32, 72, 73, 69, 82, 79, 71, 76, 89, 80, + 72, 32, 202, 212, 2, 69, 179, 135, 26, 71, 172, 17, 146, 3, 65, 178, 4, + 66, 52, 2, 67, 48, 224, 1, 2, 68, 48, 170, 4, 69, 178, 3, 70, 164, 4, 2, + 71, 48, 210, 3, 72, 214, 1, 73, 238, 2, 76, 122, 77, 222, 8, 78, 210, 5, + 79, 140, 5, 2, 80, 48, 120, 2, 82, 48, 232, 1, 2, 83, 48, 190, 3, 84, + 220, 2, 2, 85, 48, 250, 2, 86, 134, 5, 87, 236, 2, 3, 88, 48, 48, 88, 3, + 89, 48, 48, 84, 2, 90, 48, 188, 217, 3, 3, 75, 48, 48, 129, 151, 15, 3, + 81, 48, 48, 228, 1, 30, 48, 133, 3, 2, 65, 48, 160, 1, 86, 48, 98, 49, + 102, 52, 166, 55, 51, 150, 248, 20, 55, 198, 232, 1, 50, 2, 53, 3, 54, + 24, 170, 68, 54, 198, 129, 24, 53, 242, 145, 4, 49, 2, 50, 2, 51, 2, 52, + 2, 55, 2, 56, 3, 57, 24, 142, 197, 24, 52, 2, 55, 242, 145, 4, 48, 2, 49, + 2, 50, 2, 51, 2, 53, 2, 54, 2, 56, 3, 57, 28, 170, 196, 24, 48, 2, 50, 2, + 51, 2, 53, 242, 145, 4, 49, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 68, 46, + 48, 246, 54, 51, 246, 223, 22, 49, 3, 50, 22, 210, 65, 55, 182, 147, 28, + 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 26, 148, 9, 4, 69, + 71, 73, 78, 185, 25, 2, 48, 48, 56, 34, 48, 90, 49, 155, 245, 8, 50, 24, + 230, 39, 50, 242, 171, 28, 49, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, + 3, 57, 22, 142, 193, 24, 48, 242, 145, 4, 49, 2, 50, 2, 51, 2, 52, 2, 53, + 2, 54, 2, 55, 2, 56, 3, 57, 184, 1, 70, 48, 94, 51, 102, 52, 102, 53, + 106, 54, 194, 29, 50, 231, 242, 22, 49, 20, 222, 191, 24, 56, 242, 145, + 4, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 57, 24, 130, 191, 24, + 49, 2, 52, 242, 145, 4, 48, 2, 50, 2, 51, 2, 53, 2, 54, 2, 55, 2, 56, 3, + 57, 24, 158, 190, 24, 54, 2, 56, 242, 145, 4, 48, 2, 49, 2, 50, 2, 51, 2, + 52, 2, 53, 2, 55, 3, 57, 42, 214, 60, 48, 230, 128, 24, 50, 2, 52, 242, + 145, 4, 49, 2, 51, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 32, 194, 60, 55, + 130, 146, 28, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 3, 54, 94, 30, 48, + 189, 2, 2, 78, 68, 88, 38, 48, 94, 50, 102, 51, 215, 7, 49, 22, 186, 187, + 24, 56, 2, 57, 242, 145, 4, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 55, + 24, 222, 186, 24, 48, 2, 56, 242, 145, 4, 49, 2, 50, 2, 51, 2, 52, 2, 53, + 2, 54, 2, 55, 3, 57, 18, 250, 185, 24, 52, 242, 145, 4, 48, 2, 49, 2, 50, + 2, 51, 2, 54, 2, 55, 3, 56, 6, 11, 32, 6, 136, 183, 5, 6, 87, 65, 76, 76, + 69, 68, 210, 19, 69, 247, 225, 20, 83, 132, 1, 42, 48, 133, 9, 5, 85, 76, + 76, 32, 66, 130, 1, 54, 48, 94, 49, 102, 51, 102, 52, 102, 53, 215, 1, + 50, 20, 230, 183, 24, 49, 242, 145, 4, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, + 55, 2, 56, 3, 57, 22, 138, 183, 24, 51, 242, 145, 4, 48, 2, 49, 2, 50, 2, + 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 26, 166, 182, 24, 49, 2, 55, 2, + 56, 242, 145, 4, 48, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 57, 26, 194, + 181, 24, 53, 2, 54, 2, 55, 242, 145, 4, 48, 2, 49, 2, 50, 2, 51, 2, 52, + 2, 56, 3, 57, 14, 222, 26, 49, 242, 171, 28, 48, 2, 50, 3, 51, 128, 1, + 62, 48, 98, 49, 102, 51, 102, 52, 210, 27, 50, 255, 201, 8, 53, 24, 166, + 50, 55, 198, 129, 24, 54, 242, 145, 4, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, + 56, 3, 57, 22, 138, 179, 24, 49, 242, 145, 4, 48, 2, 50, 2, 51, 2, 52, 2, + 53, 2, 54, 2, 55, 2, 56, 3, 57, 24, 166, 178, 24, 54, 2, 55, 242, 145, 4, + 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 56, 3, 57, 24, 194, 177, 24, + 51, 2, 53, 242, 145, 4, 48, 2, 49, 2, 50, 2, 52, 2, 54, 2, 55, 2, 56, 3, + 57, 24, 80, 2, 48, 48, 84, 4, 65, 76, 70, 32, 161, 40, 7, 79, 82, 73, 90, + 79, 78, 84, 18, 138, 176, 24, 54, 242, 145, 4, 49, 2, 50, 2, 51, 2, 52, + 2, 53, 2, 55, 3, 56, 4, 22, 66, 255, 42, 76, 2, 191, 241, 16, 76, 52, 58, + 48, 181, 1, 9, 78, 83, 69, 82, 84, 32, 65, 84, 32, 38, 18, 48, 95, 49, + 22, 186, 174, 24, 53, 2, 57, 242, 145, 4, 49, 2, 50, 2, 51, 2, 52, 2, 54, + 2, 55, 3, 56, 16, 222, 173, 24, 48, 2, 49, 242, 145, 4, 50, 2, 51, 2, 52, + 3, 53, 14, 68, 6, 66, 79, 84, 84, 79, 77, 0, 3, 84, 79, 80, 199, 219, 19, + 77, 7, 11, 32, 4, 162, 174, 27, 69, 249, 8, 2, 83, 84, 22, 32, 2, 48, 48, + 167, 223, 15, 79, 20, 250, 171, 24, 50, 2, 54, 242, 145, 4, 49, 2, 51, 2, + 52, 2, 53, 2, 55, 3, 56, 164, 1, 118, 48, 128, 4, 15, 79, 68, 73, 70, 73, + 69, 82, 32, 68, 65, 77, 65, 71, 69, 68, 129, 246, 24, 5, 73, 82, 82, 79, + 82, 132, 1, 42, 48, 98, 49, 106, 50, 102, 51, 107, 52, 24, 182, 40, 49, + 198, 129, 24, 51, 242, 145, 4, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, + 57, 44, 138, 41, 50, 146, 128, 24, 48, 2, 53, 2, 54, 2, 55, 242, 145, 4, + 49, 2, 51, 2, 52, 2, 56, 3, 57, 26, 178, 168, 24, 50, 2, 52, 2, 56, 242, + 145, 4, 48, 2, 49, 2, 51, 2, 53, 2, 54, 2, 55, 3, 57, 26, 138, 38, 51, + 198, 129, 24, 49, 242, 145, 4, 48, 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, + 56, 3, 57, 12, 230, 166, 24, 48, 242, 145, 4, 49, 2, 50, 2, 51, 3, 52, + 31, 25, 4, 32, 65, 84, 32, 28, 96, 6, 66, 79, 84, 84, 79, 77, 120, 5, 83, + 84, 65, 82, 84, 68, 3, 84, 79, 80, 207, 165, 27, 69, 11, 11, 32, 8, 56, + 5, 83, 84, 65, 82, 84, 186, 1, 65, 139, 165, 27, 69, 5, 129, 2, 8, 32, + 65, 78, 68, 32, 84, 79, 80, 7, 29, 5, 32, 65, 78, 68, 32, 4, 222, 198, + 24, 66, 147, 246, 2, 84, 11, 11, 32, 8, 54, 65, 20, 5, 83, 84, 65, 82, + 84, 247, 164, 27, 69, 2, 73, 2, 78, 68, 5, 53, 11, 32, 65, 78, 68, 32, + 66, 79, 84, 84, 79, 77, 2, 235, 216, 19, 32, 194, 1, 50, 48, 128, 2, 2, + 76, 48, 229, 1, 2, 85, 48, 98, 58, 49, 98, 51, 194, 14, 50, 150, 6, 52, + 247, 221, 22, 48, 24, 146, 32, 56, 182, 147, 28, 48, 2, 49, 2, 50, 2, 51, + 2, 52, 2, 53, 2, 54, 2, 55, 3, 57, 28, 246, 160, 24, 51, 2, 52, 2, 53, 2, + 55, 242, 145, 4, 48, 2, 49, 2, 50, 2, 54, 2, 56, 3, 57, 44, 34, 48, 94, + 49, 163, 138, 21, 50, 20, 238, 159, 24, 53, 242, 145, 4, 49, 2, 50, 2, + 51, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 22, 146, 159, 24, 55, 242, 145, 4, + 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 52, 34, 49, + 102, 50, 251, 238, 22, 48, 26, 138, 158, 24, 48, 2, 49, 2, 56, 242, 145, + 4, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 57, 8, 166, 157, 24, 50, + 242, 145, 4, 48, 3, 49, 152, 1, 50, 48, 149, 254, 18, 6, 86, 69, 82, 76, + 65, 89, 150, 1, 66, 48, 154, 1, 49, 138, 1, 50, 102, 51, 106, 53, 227, + 235, 22, 52, 34, 90, 54, 162, 155, 24, 49, 2, 53, 242, 145, 4, 50, 2, 51, + 2, 52, 2, 55, 2, 56, 3, 57, 15, 142, 173, 28, 65, 2, 66, 2, 67, 2, 68, 2, + 69, 3, 70, 28, 98, 48, 130, 154, 24, 57, 242, 145, 4, 49, 2, 50, 2, 51, + 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 9, 238, 171, 28, 65, 2, 66, 3, 67, 28, + 218, 153, 24, 48, 2, 52, 2, 53, 2, 57, 242, 145, 4, 49, 2, 50, 2, 51, 2, + 54, 2, 55, 3, 56, 32, 134, 23, 54, 242, 129, 24, 48, 2, 51, 242, 145, 4, + 49, 2, 50, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 8, 202, 22, 48, 183, 147, + 28, 49, 26, 26, 48, 251, 194, 8, 49, 22, 210, 151, 24, 49, 2, 51, 242, + 145, 4, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 68, 34, 48, 98, 49, + 199, 233, 22, 50, 24, 142, 21, 51, 198, 129, 24, 50, 242, 145, 4, 49, 2, + 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 24, 242, 149, 24, 48, 2, 54, 242, + 145, 4, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 108, 50, 48, + 94, 49, 106, 50, 98, 51, 175, 179, 19, 52, 22, 218, 148, 24, 50, 2, 54, + 242, 145, 4, 49, 2, 51, 2, 52, 2, 53, 2, 55, 2, 56, 3, 57, 26, 186, 18, + 52, 198, 129, 24, 55, 242, 145, 4, 48, 2, 49, 2, 50, 2, 51, 2, 53, 2, 54, + 2, 56, 3, 57, 24, 210, 17, 54, 182, 147, 28, 48, 2, 49, 2, 50, 2, 51, 2, + 52, 2, 53, 2, 55, 2, 56, 3, 57, 22, 182, 146, 24, 53, 242, 145, 4, 48, 2, + 49, 2, 50, 2, 51, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 90, 34, 48, 249, 12, + 3, 65, 76, 76, 88, 42, 48, 94, 49, 102, 51, 151, 227, 22, 50, 26, 130, + 145, 24, 51, 2, 55, 2, 56, 2, 57, 242, 145, 4, 49, 2, 50, 2, 52, 2, 53, + 3, 54, 24, 166, 144, 24, 49, 2, 54, 242, 145, 4, 48, 2, 50, 2, 51, 2, 52, + 2, 53, 2, 55, 2, 56, 3, 57, 18, 194, 143, 24, 50, 2, 51, 242, 145, 4, 48, + 2, 49, 2, 52, 2, 53, 3, 54, 94, 50, 48, 90, 50, 102, 51, 102, 52, 247, + 223, 22, 49, 22, 254, 12, 54, 182, 147, 28, 49, 2, 50, 2, 51, 2, 52, 2, + 53, 2, 55, 2, 56, 3, 57, 24, 234, 141, 24, 51, 2, 57, 242, 145, 4, 48, 2, + 49, 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 22, 134, 141, 24, 50, 242, + 145, 4, 48, 2, 49, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 6, + 146, 158, 28, 48, 2, 49, 3, 50, 158, 1, 42, 48, 185, 4, 5, 69, 82, 84, + 73, 67, 156, 1, 62, 48, 98, 49, 98, 50, 210, 1, 51, 253, 135, 24, 2, 52, + 48, 42, 198, 9, 55, 98, 49, 230, 128, 24, 50, 242, 145, 4, 51, 2, 52, 2, + 53, 2, 54, 2, 56, 3, 57, 32, 186, 8, 49, 46, 50, 182, 147, 28, 48, 2, 51, + 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 50, 98, 48, 234, 136, 24, 51, + 2, 56, 2, 57, 242, 145, 4, 49, 2, 50, 2, 52, 2, 53, 2, 54, 3, 55, 27, + 214, 154, 28, 65, 2, 66, 2, 67, 2, 68, 2, 69, 2, 70, 2, 71, 2, 72, 2, 73, + 2, 74, 2, 75, 3, 76, 28, 250, 135, 24, 48, 2, 49, 2, 51, 2, 55, 242, 145, + 4, 50, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 2, 137, 234, 23, 2, 65, 76, 66, + 34, 48, 161, 2, 3, 73, 68, 69, 64, 26, 48, 94, 49, 103, 50, 22, 186, 134, + 24, 51, 2, 57, 242, 145, 4, 49, 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, + 28, 222, 133, 24, 48, 2, 52, 2, 55, 2, 56, 242, 145, 4, 49, 2, 50, 2, 51, + 2, 53, 2, 54, 3, 57, 14, 250, 132, 24, 52, 242, 145, 4, 48, 2, 49, 2, 50, + 2, 51, 3, 53, 2, 17, 2, 32, 76, 2, 203, 183, 15, 79, 24, 202, 2, 52, 198, + 129, 24, 54, 2, 56, 242, 145, 4, 49, 2, 50, 2, 51, 2, 53, 3, 55, 18, 182, + 131, 24, 49, 242, 145, 4, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, + 82, 22, 48, 167, 1, 49, 34, 90, 50, 46, 51, 198, 129, 24, 52, 2, 53, 242, + 145, 4, 49, 2, 54, 2, 55, 2, 56, 3, 57, 11, 222, 147, 28, 65, 2, 66, 2, + 67, 3, 68, 7, 178, 147, 28, 65, 3, 66, 48, 66, 53, 86, 54, 130, 146, 28, + 48, 2, 49, 2, 50, 2, 51, 3, 52, 21, 210, 146, 28, 65, 2, 66, 2, 67, 2, + 68, 2, 69, 2, 70, 2, 71, 2, 72, 3, 73, 19, 254, 145, 28, 65, 2, 66, 2, + 67, 2, 68, 2, 69, 2, 70, 2, 71, 3, 72, 18, 26, 32, 211, 233, 15, 72, 16, + 70, 80, 124, 5, 82, 65, 89, 83, 32, 226, 152, 3, 84, 131, 223, 22, 83, 8, + 222, 152, 3, 79, 209, 190, 16, 22, 69, 84, 65, 76, 76, 69, 68, 32, 79, + 85, 84, 76, 73, 78, 69, 68, 32, 66, 76, 65, 67, 75, 4, 252, 245, 18, 2, + 73, 78, 1, 3, 79, 85, 84, 160, 1, 132, 1, 13, 66, 65, 83, 65, 78, 32, 76, + 69, 84, 84, 69, 82, 32, 166, 3, 69, 176, 4, 7, 89, 77, 65, 73, 67, 32, + 76, 199, 134, 28, 70, 80, 230, 1, 71, 78, 76, 34, 78, 50, 82, 210, 254, + 25, 69, 166, 140, 1, 67, 2, 68, 2, 75, 2, 83, 2, 84, 2, 90, 206, 105, 66, + 2, 70, 2, 72, 2, 74, 2, 77, 2, 80, 2, 81, 2, 86, 2, 88, 214, 22, 65, 2, + 73, 2, 79, 2, 85, 3, 89, 8, 38, 72, 182, 245, 27, 74, 215, 22, 69, 4, + 190, 182, 25, 65, 203, 213, 2, 69, 4, 142, 245, 27, 76, 215, 22, 69, 8, + 238, 244, 27, 68, 2, 74, 214, 22, 65, 3, 69, 4, 190, 244, 27, 82, 215, + 22, 69, 32, 96, 5, 67, 84, 82, 73, 67, 160, 1, 7, 77, 69, 78, 84, 32, 79, + 70, 238, 252, 26, 80, 135, 83, 86, 10, 26, 32, 131, 151, 24, 65, 8, 98, + 80, 232, 212, 17, 2, 84, 79, 176, 246, 8, 9, 76, 73, 71, 72, 84, 32, 66, + 85, 76, 187, 33, 65, 2, 11, 76, 2, 175, 136, 28, 85, 19, 11, 32, 16, 72, + 5, 87, 73, 84, 72, 32, 153, 214, 16, 7, 79, 80, 69, 78, 73, 78, 71, 12, + 130, 1, 76, 32, 12, 84, 87, 79, 32, 72, 79, 82, 73, 90, 79, 78, 84, 146, + 225, 19, 86, 226, 182, 4, 85, 142, 61, 79, 175, 177, 2, 68, 2, 245, 222, + 25, 3, 79, 78, 71, 2, 173, 219, 14, 7, 65, 76, 32, 83, 84, 82, 79, 46, + 238, 175, 4, 69, 165, 206, 15, 15, 73, 71, 65, 84, 85, 82, 69, 32, 90, + 65, 89, 73, 78, 45, 89, 53, 48, 4, 79, 74, 73, 32, 218, 2, 80, 163, 3, + 32, 18, 164, 1, 10, 67, 79, 77, 80, 79, 78, 69, 78, 84, 32, 109, 26, 77, + 79, 68, 73, 70, 73, 69, 82, 32, 70, 73, 84, 90, 80, 65, 84, 82, 73, 67, + 75, 32, 84, 89, 80, 69, 45, 8, 244, 132, 15, 2, 82, 69, 12, 5, 67, 85, + 82, 76, 89, 0, 5, 87, 72, 73, 84, 69, 185, 207, 2, 2, 66, 65, 10, 216, + 219, 20, 2, 49, 45, 194, 167, 7, 51, 2, 52, 2, 53, 3, 54, 26, 44, 3, 84, + 89, 32, 149, 251, 3, 2, 72, 65, 24, 82, 78, 60, 3, 80, 65, 71, 20, 3, 83, + 69, 84, 209, 189, 27, 4, 68, 79, 67, 85, 8, 36, 3, 79, 84, 69, 187, 147, + 24, 69, 7, 251, 162, 13, 32, 4, 191, 247, 25, 69, 11, 33, 6, 32, 87, 73, + 84, 72, 32, 8, 242, 135, 14, 82, 24, 3, 76, 69, 70, 224, 166, 1, 2, 83, + 77, 255, 159, 9, 79, 38, 86, 32, 64, 2, 68, 32, 214, 1, 81, 20, 6, 86, + 69, 76, 79, 80, 69, 199, 227, 15, 84, 6, 42, 81, 250, 147, 26, 68, 235, + 172, 1, 83, 2, 223, 184, 27, 85, 20, 32, 3, 79, 70, 32, 131, 1, 87, 18, + 88, 3, 80, 82, 79, 254, 232, 20, 71, 56, 2, 83, 69, 166, 71, 77, 30, 84, + 203, 177, 5, 76, 4, 190, 233, 20, 84, 151, 147, 6, 79, 2, 193, 250, 19, + 7, 73, 84, 72, 32, 76, 69, 70, 5, 247, 176, 21, 85, 7, 33, 6, 32, 87, 73, + 84, 72, 32, 4, 42, 76, 153, 254, 23, 4, 68, 79, 87, 78, 2, 161, 220, 22, + 4, 73, 71, 72, 84, 6, 154, 252, 27, 76, 2, 77, 3, 84, 46, 28, 2, 65, 76, + 231, 6, 73, 40, 30, 32, 133, 2, 2, 83, 32, 12, 56, 3, 84, 79, 32, 229, + 151, 13, 5, 65, 78, 68, 32, 80, 10, 68, 3, 79, 82, 32, 249, 168, 27, 8, + 66, 89, 32, 68, 69, 70, 73, 78, 8, 64, 3, 80, 82, 69, 28, 3, 83, 85, 67, + 226, 227, 25, 71, 39, 76, 2, 189, 162, 15, 2, 67, 69, 2, 157, 151, 26, 3, + 67, 69, 69, 28, 72, 4, 83, 73, 71, 78, 160, 225, 25, 4, 87, 73, 84, 72, + 199, 199, 1, 67, 25, 11, 32, 22, 42, 65, 201, 1, 5, 87, 73, 84, 72, 32, + 12, 112, 5, 66, 79, 86, 69, 32, 65, 19, 78, 68, 32, 83, 76, 65, 78, 84, + 69, 68, 32, 80, 65, 82, 65, 76, 76, 69, 76, 8, 182, 143, 21, 80, 210, 5, + 84, 146, 189, 2, 76, 171, 131, 3, 82, 5, 239, 243, 6, 32, 10, 160, 1, 4, + 66, 85, 77, 80, 20, 7, 73, 78, 70, 73, 78, 73, 84, 20, 18, 84, 87, 79, + 32, 68, 79, 84, 83, 32, 65, 66, 79, 86, 69, 32, 65, 78, 68, 211, 162, 15, + 68, 2, 191, 248, 26, 89, 4, 155, 211, 25, 89, 2, 17, 2, 32, 84, 2, 187, + 195, 25, 87, 6, 80, 7, 86, 65, 76, 69, 78, 84, 32, 137, 147, 21, 7, 65, + 78, 71, 85, 76, 65, 82, 4, 48, 6, 87, 73, 84, 72, 32, 70, 251, 212, 27, + 84, 2, 11, 79, 2, 237, 248, 2, 2, 85, 82, 20, 152, 1, 11, 82, 79, 82, 45, + 66, 65, 82, 82, 69, 68, 32, 236, 166, 5, 7, 73, 83, 32, 70, 79, 82, 77, + 161, 202, 7, 9, 65, 83, 69, 32, 84, 79, 32, 84, 72, 12, 140, 224, 5, 4, + 87, 72, 73, 84, 13, 5, 66, 76, 65, 67, 75, 10, 58, 67, 20, 6, 84, 73, 77, + 65, 84, 69, 155, 241, 27, 65, 5, 211, 167, 26, 65, 4, 210, 219, 26, 68, + 199, 149, 1, 83, 154, 8, 60, 7, 72, 73, 79, 80, 73, 67, 32, 178, 240, 27, + 66, 3, 88, 150, 8, 204, 1, 2, 67, 79, 232, 1, 7, 78, 85, 77, 66, 69, 82, + 32, 114, 80, 54, 83, 156, 24, 11, 84, 79, 78, 65, 76, 32, 77, 65, 82, 75, + 32, 230, 135, 19, 68, 158, 246, 5, 70, 82, 81, 173, 150, 2, 4, 87, 79, + 82, 68, 10, 26, 77, 215, 158, 27, 76, 8, 52, 7, 66, 73, 78, 73, 78, 71, + 32, 211, 235, 27, 77, 6, 60, 11, 71, 69, 77, 73, 78, 65, 84, 73, 79, 78, + 32, 51, 86, 4, 44, 5, 65, 78, 68, 32, 86, 239, 171, 27, 77, 2, 145, 140, + 24, 4, 79, 87, 69, 76, 22, 66, 84, 134, 144, 22, 72, 238, 234, 3, 69, 30, + 70, 42, 78, 39, 83, 8, 130, 206, 16, 69, 158, 174, 9, 72, 27, 87, 4, 202, + 119, 65, 145, 164, 26, 5, 82, 69, 70, 65, 67, 198, 7, 50, 69, 37, 8, 89, + 76, 76, 65, 66, 76, 69, 32, 4, 146, 156, 24, 77, 223, 229, 2, 67, 194, 7, + 210, 1, 66, 90, 67, 246, 1, 68, 186, 1, 70, 90, 71, 214, 2, 72, 162, 1, + 75, 102, 77, 90, 78, 90, 80, 138, 2, 81, 174, 1, 82, 86, 83, 210, 1, 84, + 122, 74, 2, 76, 138, 1, 87, 2, 89, 66, 88, 134, 1, 90, 95, 86, 38, 194, + 12, 87, 230, 8, 66, 134, 194, 23, 65, 2, 79, 210, 209, 3, 69, 162, 64, + 73, 3, 85, 78, 94, 67, 254, 15, 72, 250, 197, 23, 65, 2, 79, 210, 209, 3, + 69, 234, 61, 87, 186, 2, 73, 3, 85, 42, 70, 72, 174, 213, 23, 65, 210, + 209, 3, 69, 162, 64, 73, 2, 79, 3, 85, 28, 166, 19, 72, 134, 194, 23, 65, + 210, 209, 3, 69, 162, 64, 73, 2, 79, 3, 85, 60, 94, 68, 214, 14, 90, 174, + 197, 23, 65, 2, 79, 210, 209, 3, 69, 234, 61, 87, 186, 2, 73, 3, 85, 30, + 210, 14, 72, 174, 197, 23, 65, 2, 79, 210, 209, 3, 69, 234, 61, 87, 186, + 2, 73, 3, 85, 24, 190, 8, 87, 234, 202, 23, 65, 210, 209, 3, 69, 234, 61, + 89, 186, 2, 73, 2, 79, 3, 85, 118, 142, 1, 85, 226, 7, 71, 232, 3, 7, 76, + 79, 84, 84, 65, 76, 32, 158, 2, 87, 218, 1, 89, 134, 194, 23, 65, 2, 79, + 210, 209, 3, 69, 163, 64, 73, 39, 29, 5, 82, 65, 71, 69, 32, 36, 74, 66, + 2, 70, 2, 77, 2, 80, 46, 71, 2, 75, 2, 81, 247, 140, 12, 72, 4, 11, 87, + 4, 226, 203, 27, 69, 215, 22, 73, 6, 11, 87, 6, 234, 161, 27, 69, 163, + 64, 73, 52, 70, 72, 182, 207, 23, 65, 2, 79, 210, 209, 3, 69, 162, 64, + 73, 3, 85, 36, 202, 4, 87, 230, 8, 89, 134, 194, 23, 65, 210, 209, 3, 69, + 162, 64, 73, 2, 79, 3, 85, 64, 250, 4, 88, 134, 6, 87, 218, 1, 89, 134, + 194, 23, 65, 2, 79, 210, 209, 3, 69, 162, 64, 73, 3, 85, 26, 142, 3, 87, + 234, 202, 23, 65, 2, 79, 210, 209, 3, 69, 234, 61, 89, 186, 2, 73, 3, 85, + 36, 166, 7, 89, 250, 197, 23, 65, 2, 79, 210, 209, 3, 69, 234, 61, 87, + 186, 2, 73, 3, 85, 56, 82, 72, 142, 1, 87, 234, 202, 23, 65, 2, 79, 210, + 209, 3, 69, 162, 64, 73, 3, 85, 32, 74, 65, 170, 203, 23, 79, 210, 209, + 3, 69, 234, 61, 87, 186, 2, 73, 3, 85, 19, 160, 9, 8, 82, 89, 78, 71, 69, + 65, 76, 32, 247, 211, 27, 65, 8, 182, 156, 27, 69, 162, 64, 65, 3, 73, + 64, 94, 72, 134, 6, 87, 218, 1, 89, 134, 194, 23, 65, 2, 79, 210, 209, 3, + 69, 162, 64, 73, 3, 85, 24, 130, 6, 87, 222, 195, 23, 65, 210, 209, 3, + 69, 162, 64, 73, 2, 79, 3, 85, 20, 146, 201, 23, 65, 2, 79, 210, 209, 3, + 69, 234, 61, 87, 2, 89, 186, 2, 73, 3, 85, 74, 102, 69, 226, 1, 72, 170, + 3, 90, 78, 83, 134, 194, 23, 65, 2, 79, 186, 143, 4, 87, 186, 2, 73, 3, + 85, 13, 56, 8, 66, 65, 84, 66, 69, 73, 84, 32, 143, 217, 27, 69, 8, 174, + 223, 22, 66, 2, 70, 2, 77, 3, 80, 80, 118, 72, 76, 2, 84, 72, 62, 90, + 162, 2, 83, 210, 194, 23, 65, 2, 79, 210, 209, 3, 69, 234, 61, 87, 186, + 2, 73, 3, 85, 18, 246, 197, 23, 65, 2, 79, 210, 209, 3, 69, 234, 61, 87, + 186, 2, 73, 3, 85, 12, 250, 150, 27, 69, 234, 61, 65, 186, 2, 73, 2, 79, + 3, 85, 16, 238, 196, 23, 65, 2, 79, 210, 209, 3, 69, 162, 64, 73, 3, 85, + 40, 82, 87, 218, 1, 89, 134, 194, 23, 65, 2, 79, 210, 209, 3, 69, 162, + 64, 73, 3, 85, 10, 218, 195, 23, 65, 210, 209, 3, 69, 163, 64, 73, 48, + 90, 72, 78, 90, 134, 194, 23, 65, 2, 79, 210, 209, 3, 69, 234, 61, 87, + 186, 2, 73, 3, 85, 16, 206, 194, 23, 65, 210, 209, 3, 69, 234, 61, 87, + 186, 2, 73, 2, 79, 3, 85, 14, 130, 194, 23, 65, 210, 209, 3, 69, 162, 64, + 73, 2, 79, 3, 85, 20, 130, 1, 68, 74, 72, 30, 75, 42, 82, 0, 7, 83, 72, + 79, 82, 84, 32, 82, 220, 147, 4, 3, 67, 72, 73, 177, 136, 23, 3, 89, 73, + 90, 6, 48, 4, 69, 82, 69, 84, 205, 200, 26, 2, 73, 70, 5, 17, 2, 45, 72, + 2, 205, 156, 27, 2, 73, 68, 4, 152, 200, 26, 2, 69, 78, 163, 2, 85, 2, + 137, 162, 4, 3, 73, 75, 82, 12, 96, 2, 82, 79, 244, 155, 11, 3, 78, 79, + 77, 181, 168, 15, 9, 76, 69, 82, 32, 67, 79, 78, 83, 84, 8, 92, 5, 80, + 69, 65, 78, 32, 248, 152, 24, 8, 45, 67, 85, 82, 82, 69, 78, 67, 215, + 175, 2, 32, 4, 202, 142, 4, 67, 19, 80, 50, 30, 67, 102, 80, 187, 1, 84, + 6, 60, 9, 76, 65, 77, 65, 84, 73, 79, 78, 32, 243, 181, 26, 69, 4, 222, + 249, 24, 81, 187, 147, 2, 77, 10, 96, 7, 76, 79, 83, 73, 79, 78, 32, 157, + 144, 27, 11, 82, 69, 83, 83, 73, 79, 78, 76, 69, 83, 83, 8, 136, 220, 11, + 4, 70, 82, 65, 77, 197, 161, 15, 8, 65, 84, 32, 72, 79, 82, 73, 90, 34, + 98, 82, 209, 189, 21, 18, 69, 78, 68, 69, 68, 32, 65, 82, 65, 66, 73, 67, + 45, 73, 78, 68, 73, 67, 14, 140, 1, 12, 69, 77, 69, 76, 89, 32, 72, 69, + 65, 86, 89, 32, 241, 244, 25, 16, 65, 84, 69, 82, 82, 69, 83, 84, 82, 73, + 65, 76, 32, 65, 76, 73, 12, 50, 83, 238, 176, 25, 70, 234, 2, 87, 203, + 11, 71, 4, 254, 177, 25, 65, 43, 73, 7, 226, 178, 19, 71, 195, 151, 8, + 83, 152, 4, 142, 1, 65, 130, 19, 69, 190, 1, 73, 134, 8, 76, 138, 6, 79, + 142, 7, 82, 130, 5, 85, 228, 193, 20, 2, 86, 83, 242, 203, 4, 83, 199, + 140, 2, 70, 92, 122, 67, 178, 15, 76, 176, 2, 2, 88, 32, 170, 148, 3, 77, + 176, 250, 5, 2, 82, 83, 140, 251, 1, 2, 84, 72, 203, 230, 9, 73, 70, 72, + 2, 69, 32, 156, 179, 16, 4, 83, 73, 77, 73, 165, 206, 4, 2, 84, 79, 66, + 226, 1, 83, 160, 1, 4, 87, 73, 84, 72, 172, 128, 13, 2, 80, 65, 252, 136, + 2, 2, 77, 65, 164, 162, 11, 13, 84, 72, 82, 79, 87, 73, 78, 71, 32, 65, + 32, 75, 73, 201, 1, 14, 72, 79, 76, 68, 73, 78, 71, 32, 66, 65, 67, 75, + 32, 84, 4, 192, 130, 17, 18, 65, 86, 79, 85, 82, 73, 78, 71, 32, 68, 69, + 76, 73, 67, 73, 79, 85, 83, 141, 138, 5, 13, 67, 82, 69, 65, 77, 73, 78, + 71, 32, 73, 78, 32, 70, 54, 38, 32, 245, 158, 24, 3, 79, 85, 84, 52, 196, + 4, 2, 67, 79, 44, 5, 72, 69, 65, 68, 45, 38, 77, 98, 79, 92, 7, 78, 79, + 32, 71, 79, 79, 68, 238, 1, 80, 164, 1, 16, 83, 84, 85, 67, 75, 45, 79, + 85, 84, 32, 84, 79, 78, 71, 85, 69, 110, 84, 204, 183, 1, 9, 66, 65, 71, + 83, 32, 85, 78, 68, 69, 188, 163, 17, 22, 70, 73, 78, 71, 69, 82, 32, 67, + 79, 86, 69, 82, 73, 78, 71, 32, 67, 76, 79, 83, 69, 68, 244, 67, 3, 82, + 79, 76, 180, 231, 1, 13, 76, 79, 79, 75, 32, 79, 70, 32, 84, 82, 73, 85, + 77, 244, 141, 3, 8, 68, 73, 65, 71, 79, 78, 65, 76, 1, 20, 85, 78, 69, + 86, 69, 78, 32, 69, 89, 69, 83, 32, 65, 78, 68, 32, 87, 65, 86, 89, 4, + 252, 4, 3, 87, 66, 79, 203, 159, 19, 76, 2, 225, 175, 22, 4, 66, 65, 78, + 68, 4, 60, 6, 69, 68, 73, 67, 65, 76, 161, 234, 25, 3, 79, 78, 79, 2, + 157, 247, 25, 3, 32, 77, 65, 12, 90, 75, 36, 4, 80, 69, 78, 32, 189, 172, + 20, 10, 78, 69, 32, 69, 89, 69, 66, 82, 79, 87, 2, 217, 158, 22, 4, 32, + 71, 69, 83, 8, 116, 5, 77, 79, 85, 84, 72, 133, 151, 24, 18, 69, 89, 69, + 83, 32, 65, 78, 68, 32, 72, 65, 78, 68, 32, 79, 86, 69, 82, 7, 11, 32, 4, + 236, 133, 10, 3, 86, 79, 77, 145, 155, 9, 5, 65, 78, 68, 32, 67, 6, 132, + 1, 18, 65, 82, 84, 89, 32, 72, 79, 82, 78, 32, 65, 78, 68, 32, 80, 65, + 82, 84, 100, 2, 69, 69, 173, 159, 19, 4, 76, 69, 65, 68, 2, 205, 200, 22, + 2, 89, 32, 7, 29, 5, 32, 65, 78, 68, 32, 4, 36, 3, 87, 73, 78, 207, 159, + 19, 84, 2, 169, 183, 1, 4, 75, 73, 78, 71, 4, 50, 69, 169, 204, 22, 6, + 72, 69, 82, 77, 79, 77, 2, 157, 167, 27, 8, 65, 82, 83, 32, 79, 70, 32, + 74, 10, 34, 76, 237, 162, 22, 2, 65, 70, 8, 84, 13, 73, 78, 71, 32, 68, + 73, 65, 71, 79, 78, 65, 76, 32, 233, 233, 23, 2, 69, 78, 6, 128, 1, 9, + 67, 82, 79, 83, 83, 73, 78, 71, 32, 177, 178, 19, 16, 73, 78, 32, 87, 72, + 73, 84, 69, 32, 67, 73, 82, 67, 76, 69, 32, 4, 140, 140, 16, 3, 82, 73, + 83, 227, 173, 3, 78, 4, 194, 250, 14, 73, 131, 143, 4, 77, 14, 50, 65, + 50, 77, 44, 2, 82, 82, 251, 227, 18, 78, 4, 156, 171, 8, 3, 82, 70, 85, + 199, 232, 9, 84, 4, 152, 243, 8, 2, 73, 78, 231, 173, 7, 65, 4, 144, 243, + 7, 2, 73, 83, 203, 193, 19, 89, 58, 138, 1, 71, 90, 76, 178, 1, 78, 98, + 82, 182, 2, 83, 164, 14, 4, 86, 69, 32, 68, 141, 252, 4, 10, 69, 76, 68, + 32, 72, 79, 67, 75, 69, 89, 6, 48, 4, 85, 82, 69, 32, 221, 132, 26, 2, + 72, 84, 4, 218, 199, 25, 68, 235, 172, 1, 83, 10, 32, 2, 69, 32, 65, 2, + 77, 32, 6, 218, 192, 13, 70, 140, 155, 2, 3, 67, 65, 66, 147, 165, 8, 83, + 4, 52, 4, 80, 82, 79, 74, 149, 153, 20, 3, 70, 82, 65, 2, 227, 209, 15, + 69, 4, 72, 4, 71, 69, 82, 80, 197, 166, 25, 8, 73, 84, 69, 32, 80, 65, + 82, 84, 2, 171, 133, 16, 82, 22, 34, 69, 189, 1, 3, 83, 84, 32, 13, 56, + 2, 32, 69, 68, 4, 87, 79, 82, 75, 227, 176, 15, 67, 4, 198, 184, 13, 78, + 193, 213, 4, 8, 88, 84, 73, 78, 71, 85, 73, 83, 4, 192, 193, 23, 6, 32, + 83, 80, 65, 82, 75, 215, 237, 3, 83, 10, 238, 173, 5, 81, 224, 179, 10, + 8, 83, 84, 82, 79, 78, 71, 32, 73, 223, 225, 6, 80, 10, 38, 72, 141, 221, + 23, 3, 84, 69, 68, 9, 128, 255, 3, 13, 73, 78, 71, 32, 80, 79, 76, 69, + 32, 65, 78, 68, 32, 226, 205, 5, 69, 137, 218, 16, 19, 32, 67, 65, 75, + 69, 32, 87, 73, 84, 72, 32, 83, 87, 73, 82, 76, 32, 68, 69, 46, 50, 65, + 170, 1, 69, 98, 79, 194, 1, 85, 39, 89, 12, 114, 84, 236, 163, 4, 5, 80, + 80, 73, 78, 71, 244, 155, 3, 6, 71, 32, 73, 78, 32, 72, 213, 219, 17, 3, + 77, 73, 78, 6, 242, 217, 8, 32, 174, 199, 11, 66, 135, 241, 5, 78, 4, + 172, 242, 23, 8, 88, 69, 68, 32, 66, 73, 67, 69, 153, 145, 1, 7, 85, 82, + 45, 68, 69, 45, 76, 12, 52, 2, 82, 65, 20, 3, 87, 69, 82, 243, 225, 25, + 80, 5, 251, 161, 26, 76, 7, 17, 2, 32, 80, 4, 58, 85, 185, 199, 24, 8, + 76, 65, 89, 73, 78, 71, 32, 67, 2, 157, 191, 26, 4, 78, 67, 84, 85, 4, + 190, 158, 3, 83, 159, 243, 23, 84, 15, 25, 4, 73, 78, 71, 32, 12, 64, 6, + 83, 65, 85, 67, 69, 82, 134, 140, 10, 68, 207, 131, 10, 69, 9, 11, 32, 6, + 40, 4, 87, 73, 84, 72, 203, 145, 26, 83, 4, 34, 32, 1, 4, 79, 85, 84, 32, + 2, 21, 3, 66, 69, 65, 2, 219, 232, 26, 77, 56, 102, 71, 20, 2, 76, 68, + 68, 2, 79, 84, 22, 82, 142, 2, 85, 232, 146, 23, 2, 78, 68, 191, 210, 3, + 88, 5, 243, 147, 27, 71, 4, 208, 201, 9, 8, 73, 78, 71, 32, 72, 65, 78, + 68, 251, 162, 17, 69, 5, 171, 153, 14, 80, 18, 78, 75, 132, 1, 3, 84, 85, + 78, 238, 215, 20, 77, 222, 192, 4, 32, 179, 116, 67, 8, 80, 10, 32, 65, + 78, 68, 32, 75, 78, 73, 70, 69, 218, 156, 15, 69, 223, 181, 11, 73, 5, + 177, 214, 15, 7, 32, 87, 73, 84, 72, 32, 80, 4, 200, 135, 27, 6, 69, 32, + 67, 79, 79, 75, 179, 27, 65, 22, 26, 82, 175, 182, 23, 78, 20, 38, 32, + 218, 2, 84, 199, 231, 18, 45, 16, 110, 67, 174, 1, 68, 182, 170, 2, 66, + 136, 152, 10, 7, 76, 69, 65, 70, 32, 67, 76, 242, 106, 84, 235, 215, 11, + 80, 4, 252, 174, 13, 3, 76, 85, 66, 181, 86, 32, 79, 82, 78, 69, 82, 32, + 65, 82, 82, 79, 87, 83, 32, 67, 73, 82, 67, 76, 73, 78, 71, 32, 65, 78, + 84, 73, 67, 76, 79, 67, 75, 87, 4, 21, 3, 79, 84, 32, 4, 222, 217, 23, + 80, 195, 132, 3, 77, 2, 231, 43, 72, 30, 78, 65, 250, 1, 69, 98, 79, 237, + 250, 16, 8, 73, 69, 68, 32, 83, 72, 82, 73, 12, 96, 6, 67, 84, 73, 79, + 78, 32, 68, 8, 77, 69, 32, 87, 73, 84, 72, 32, 245, 238, 10, 2, 71, 73, + 4, 142, 249, 19, 83, 177, 6, 9, 78, 85, 77, 69, 82, 65, 84, 79, 82, 6, + 50, 80, 218, 170, 2, 65, 253, 176, 21, 2, 84, 73, 2, 161, 254, 21, 2, 73, + 67, 6, 48, 6, 78, 67, 72, 32, 70, 82, 243, 133, 19, 69, 4, 150, 134, 26, + 73, 133, 15, 3, 65, 78, 67, 10, 52, 3, 78, 84, 45, 116, 2, 87, 78, 167, + 221, 26, 71, 4, 70, 84, 217, 143, 2, 11, 70, 65, 67, 73, 78, 71, 32, 66, + 65, 66, 89, 2, 237, 187, 12, 6, 73, 76, 84, 69, 68, 32, 5, 237, 143, 8, + 6, 73, 78, 71, 32, 70, 65, 226, 1, 80, 2, 76, 76, 134, 6, 78, 184, 242, + 16, 5, 69, 76, 32, 80, 85, 179, 138, 10, 83, 216, 1, 42, 32, 73, 6, 87, + 73, 68, 84, 72, 32, 10, 158, 137, 12, 77, 188, 166, 3, 2, 79, 85, 170, + 247, 8, 66, 151, 29, 83, 206, 1, 242, 1, 67, 42, 76, 78, 78, 30, 80, 66, + 82, 142, 1, 83, 38, 89, 182, 155, 10, 77, 226, 208, 10, 65, 158, 2, 68, + 58, 69, 98, 71, 118, 72, 202, 4, 81, 198, 153, 2, 87, 182, 29, 84, 162, + 146, 1, 70, 224, 177, 1, 6, 66, 82, 79, 75, 69, 78, 211, 7, 86, 10, 138, + 240, 20, 73, 62, 79, 131, 81, 69, 116, 42, 69, 190, 243, 20, 65, 231, + 183, 4, 79, 10, 162, 1, 70, 175, 245, 20, 83, 4, 142, 202, 21, 79, 35, + 85, 6, 42, 79, 174, 246, 20, 69, 211, 236, 2, 76, 2, 199, 169, 25, 85, + 10, 36, 3, 73, 71, 72, 183, 251, 24, 69, 8, 17, 2, 84, 32, 8, 244, 172, + 20, 5, 87, 72, 73, 84, 69, 246, 220, 2, 67, 210, 3, 80, 239, 7, 83, 4, + 226, 196, 23, 69, 139, 184, 1, 79, 2, 199, 151, 25, 69, 6, 180, 201, 11, + 8, 67, 84, 73, 79, 78, 32, 65, 80, 132, 248, 8, 5, 69, 82, 65, 76, 32, + 211, 188, 1, 78, 130, 21, 114, 65, 250, 6, 69, 222, 22, 73, 162, 1, 76, + 134, 15, 79, 198, 6, 82, 178, 92, 85, 242, 146, 22, 72, 131, 238, 3, 83, + 142, 1, 42, 82, 253, 245, 26, 4, 77, 69, 32, 68, 140, 1, 36, 3, 65, 89, + 32, 231, 230, 25, 76, 138, 1, 166, 1, 67, 210, 1, 83, 192, 2, 6, 86, 79, + 87, 69, 76, 32, 224, 174, 8, 6, 82, 69, 68, 85, 80, 76, 246, 203, 10, 72, + 238, 168, 1, 80, 214, 181, 3, 77, 171, 190, 1, 68, 52, 42, 79, 205, 1, 5, + 65, 80, 73, 84, 65, 8, 88, 10, 77, 66, 73, 78, 73, 78, 71, 32, 68, 79, + 37, 8, 78, 83, 79, 78, 65, 78, 84, 32, 4, 158, 145, 12, 85, 171, 128, 14, + 84, 4, 230, 145, 12, 78, 223, 156, 11, 71, 46, 36, 3, 77, 65, 76, 147, + 138, 22, 85, 44, 45, 9, 76, 32, 76, 69, 84, 84, 69, 82, 32, 44, 200, 1, + 4, 79, 76, 68, 32, 190, 147, 20, 78, 238, 245, 6, 66, 2, 67, 2, 68, 2, + 70, 2, 71, 2, 72, 2, 74, 2, 75, 2, 76, 2, 77, 2, 80, 2, 82, 2, 83, 2, 84, + 2, 87, 2, 88, 2, 89, 187, 2, 65, 4, 166, 137, 27, 75, 3, 78, 12, 44, 5, + 83, 73, 71, 78, 32, 155, 201, 26, 76, 10, 242, 202, 26, 69, 162, 64, 65, + 2, 73, 3, 79, 146, 3, 112, 2, 65, 82, 110, 77, 50, 79, 136, 210, 23, 9, + 82, 77, 65, 78, 32, 80, 69, 78, 78, 174, 177, 2, 84, 239, 105, 78, 7, 29, + 5, 32, 87, 73, 84, 72, 4, 200, 152, 14, 5, 79, 85, 84, 32, 72, 233, 175, + 9, 5, 32, 72, 65, 78, 68, 4, 26, 32, 143, 132, 22, 73, 2, 183, 147, 23, + 83, 130, 3, 46, 77, 245, 5, 6, 82, 71, 73, 65, 78, 32, 38, 92, 13, 65, + 78, 84, 73, 67, 32, 70, 73, 71, 85, 82, 69, 32, 205, 4, 5, 69, 84, 82, + 73, 67, 32, 158, 1, 65, 82, 67, 172, 1, 9, 70, 79, 82, 84, 85, 78, 65, + 32, 77, 44, 3, 76, 65, 69, 0, 4, 84, 82, 73, 83, 34, 80, 80, 3, 82, 85, + 66, 219, 206, 10, 86, 6, 216, 1, 6, 67, 81, 85, 73, 83, 73, 12, 4, 77, + 73, 83, 83, 211, 158, 23, 76, 8, 42, 65, 97, 6, 79, 78, 74, 85, 78, 67, + 6, 56, 3, 80, 85, 84, 0, 3, 85, 68, 65, 131, 180, 18, 82, 2, 165, 90, 5, + 32, 68, 82, 65, 67, 2, 11, 84, 2, 191, 229, 26, 73, 4, 206, 167, 2, 73, + 233, 163, 24, 2, 65, 74, 2, 197, 207, 10, 3, 84, 73, 84, 6, 44, 2, 85, + 69, 153, 248, 22, 3, 79, 80, 85, 4, 138, 225, 26, 76, 155, 34, 82, 2, + 159, 250, 24, 69, 6, 228, 255, 19, 4, 65, 76, 76, 89, 137, 228, 1, 5, 32, + 80, 82, 79, 80, 220, 2, 228, 1, 6, 67, 65, 80, 73, 84, 65, 0, 4, 83, 77, + 65, 76, 172, 3, 7, 76, 69, 84, 84, 69, 82, 32, 180, 2, 24, 77, 84, 65, + 86, 82, 85, 76, 73, 32, 67, 65, 80, 73, 84, 65, 76, 32, 76, 69, 84, 84, + 69, 82, 32, 165, 6, 2, 80, 65, 80, 45, 9, 76, 32, 76, 69, 84, 84, 69, 82, + 32, 80, 142, 2, 65, 34, 72, 166, 5, 67, 118, 71, 130, 1, 74, 34, 75, 82, + 80, 34, 83, 94, 90, 176, 178, 5, 2, 84, 65, 214, 144, 8, 76, 226, 180, 2, + 82, 218, 176, 9, 66, 2, 77, 2, 88, 226, 40, 78, 2, 81, 234, 35, 86, 234, + 47, 68, 14, 69, 2, 73, 2, 79, 2, 85, 2, 89, 143, 57, 87, 4, 154, 174, 26, + 69, 227, 79, 78, 10, 46, 65, 218, 230, 26, 73, 2, 79, 215, 22, 69, 4, + 170, 253, 26, 69, 3, 82, 94, 254, 1, 85, 178, 2, 65, 42, 67, 74, 69, 46, + 71, 34, 72, 98, 74, 34, 75, 34, 76, 50, 80, 34, 83, 34, 84, 62, 90, 230, + 247, 15, 82, 218, 176, 9, 66, 2, 77, 2, 88, 226, 40, 78, 2, 81, 234, 35, + 86, 234, 47, 68, 14, 73, 2, 79, 2, 89, 142, 57, 87, 255, 2, 70, 4, 240, + 130, 22, 4, 45, 66, 82, 74, 159, 248, 4, 78, 92, 250, 1, 65, 42, 67, 74, + 69, 46, 71, 34, 72, 98, 74, 34, 75, 34, 76, 50, 80, 34, 83, 34, 84, 62, + 90, 230, 247, 15, 82, 218, 176, 9, 66, 2, 77, 2, 88, 226, 40, 78, 2, 81, + 234, 35, 86, 234, 47, 68, 14, 73, 2, 79, 2, 85, 2, 89, 142, 57, 87, 255, + 2, 70, 6, 254, 168, 26, 69, 2, 73, 227, 79, 78, 8, 38, 72, 194, 236, 25, + 73, 243, 59, 65, 4, 174, 168, 26, 73, 135, 23, 65, 4, 208, 140, 9, 2, 76, + 73, 159, 235, 17, 78, 4, 166, 171, 25, 72, 191, 124, 65, 12, 46, 65, 162, + 224, 26, 73, 2, 79, 215, 22, 69, 6, 26, 82, 219, 246, 26, 69, 5, 215, + 239, 25, 68, 4, 166, 170, 25, 72, 207, 64, 73, 4, 230, 210, 25, 72, 223, + 83, 65, 4, 11, 65, 4, 174, 210, 15, 66, 203, 163, 11, 83, 4, 150, 210, + 25, 72, 227, 106, 65, 4, 222, 245, 25, 72, 247, 47, 65, 6, 176, 184, 3, + 6, 85, 82, 78, 69, 68, 32, 187, 250, 1, 65, 4, 154, 209, 25, 72, 223, 83, + 69, 2, 157, 171, 20, 7, 82, 65, 71, 82, 65, 80, 72, 10, 48, 2, 77, 69, + 20, 4, 78, 71, 69, 82, 31, 82, 2, 143, 222, 25, 76, 2, 237, 188, 18, 2, + 32, 82, 6, 38, 76, 253, 234, 13, 3, 65, 70, 70, 5, 183, 221, 25, 83, 202, + 1, 66, 65, 214, 13, 79, 161, 163, 26, 7, 69, 73, 67, 72, 32, 83, 84, 194, + 1, 84, 8, 71, 79, 76, 73, 84, 73, 67, 32, 229, 12, 8, 83, 83, 32, 79, 70, + 32, 77, 73, 192, 1, 56, 6, 67, 65, 80, 73, 84, 65, 1, 4, 83, 77, 65, 76, + 96, 45, 9, 76, 32, 76, 69, 84, 84, 69, 82, 32, 96, 206, 1, 65, 22, 66, + 42, 67, 94, 68, 94, 70, 38, 71, 46, 73, 138, 2, 76, 58, 77, 66, 78, 34, + 79, 30, 80, 58, 82, 30, 83, 186, 1, 84, 110, 86, 22, 89, 90, 90, 210, + 137, 19, 72, 190, 133, 2, 85, 139, 183, 5, 75, 2, 155, 202, 26, 90, 4, + 214, 3, 73, 209, 217, 26, 2, 85, 75, 4, 48, 8, 65, 85, 68, 65, 84, 69, + 32, 67, 15, 72, 2, 11, 72, 2, 209, 255, 19, 2, 82, 73, 6, 42, 74, 30, 79, + 213, 202, 26, 2, 90, 69, 2, 137, 255, 19, 2, 69, 82, 2, 163, 251, 24, 66, + 4, 186, 148, 21, 82, 139, 180, 5, 73, 2, 21, 3, 76, 65, 71, 2, 243, 232, + 21, 79, 13, 38, 78, 54, 79, 141, 1, 2, 90, 72, 2, 137, 191, 26, 8, 73, + 84, 73, 65, 76, 32, 73, 90, 4, 33, 6, 84, 65, 84, 69, 68, 32, 4, 26, 66, + 25, 2, 83, 77, 2, 11, 73, 2, 35, 71, 2, 21, 3, 65, 76, 76, 2, 141, 226, + 24, 2, 32, 89, 4, 134, 232, 26, 73, 211, 2, 69, 4, 52, 9, 65, 84, 73, 78, + 65, 84, 69, 32, 77, 35, 74, 2, 193, 137, 11, 3, 89, 83, 76, 2, 213, 144, + 9, 3, 85, 68, 73, 2, 11, 65, 2, 187, 145, 21, 83, 4, 182, 196, 26, 78, 3, + 84, 4, 26, 79, 235, 232, 26, 69, 2, 221, 200, 22, 2, 75, 79, 2, 213, 245, + 21, 2, 73, 84, 14, 106, 72, 58, 76, 252, 239, 13, 6, 80, 73, 68, 69, 82, + 89, 173, 191, 10, 8, 77, 65, 76, 76, 32, 89, 85, 83, 6, 32, 2, 84, 65, + 163, 231, 26, 65, 5, 131, 189, 25, 80, 2, 231, 251, 19, 79, 6, 78, 86, + 172, 157, 22, 9, 82, 79, 75, 85, 84, 65, 83, 84, 73, 167, 181, 4, 83, 2, + 229, 166, 19, 2, 82, 73, 2, 151, 211, 24, 69, 12, 50, 69, 198, 249, 18, + 65, 130, 236, 7, 79, 3, 85, 6, 250, 140, 21, 83, 147, 152, 5, 82, 4, 248, + 148, 9, 3, 69, 77, 76, 145, 135, 16, 4, 72, 73, 86, 69, 2, 199, 225, 26, + 76, 6, 248, 242, 11, 13, 66, 69, 32, 87, 73, 84, 72, 32, 77, 69, 82, 73, + 68, 154, 196, 4, 87, 183, 151, 9, 86, 68, 162, 1, 65, 44, 12, 84, 72, 73, + 67, 32, 76, 69, 84, 84, 69, 82, 32, 198, 166, 18, 82, 222, 102, 71, 228, + 141, 5, 3, 78, 71, 71, 238, 187, 1, 79, 185, 77, 2, 76, 70, 4, 172, 183, + 15, 2, 76, 32, 147, 171, 11, 84, 54, 210, 2, 65, 50, 72, 46, 73, 46, 78, + 46, 80, 2, 81, 40, 2, 82, 65, 22, 84, 160, 141, 9, 2, 87, 73, 246, 147, + 12, 85, 244, 69, 3, 70, 65, 73, 204, 7, 4, 66, 65, 73, 82, 248, 21, 2, + 79, 84, 150, 30, 68, 166, 128, 1, 77, 198, 147, 1, 69, 164, 30, 3, 76, + 65, 71, 148, 41, 3, 83, 65, 85, 230, 161, 1, 74, 196, 16, 2, 71, 73, 141, + 12, 2, 75, 85, 4, 216, 214, 24, 3, 73, 72, 86, 163, 134, 2, 72, 4, 162, + 224, 13, 87, 157, 243, 11, 2, 65, 71, 4, 190, 142, 9, 85, 165, 241, 14, + 2, 71, 71, 6, 178, 206, 15, 73, 241, 140, 9, 2, 65, 85, 2, 201, 153, 26, + 5, 65, 73, 82, 84, 72, 2, 223, 184, 26, 73, 4, 224, 184, 23, 2, 72, 73, + 237, 69, 2, 69, 73, 234, 9, 46, 65, 198, 5, 69, 206, 82, 73, 151, 3, 79, + 152, 1, 96, 7, 68, 85, 65, 84, 73, 79, 78, 32, 5, 78, 84, 72, 65, 32, + 138, 184, 20, 86, 219, 141, 5, 80, 2, 11, 32, 2, 239, 200, 25, 67, 146, + 1, 120, 7, 76, 69, 84, 84, 69, 82, 32, 212, 2, 5, 83, 73, 71, 78, 32, + 130, 247, 22, 65, 248, 8, 2, 86, 79, 195, 199, 3, 79, 100, 214, 1, 86, + 154, 251, 22, 65, 38, 68, 114, 84, 230, 5, 85, 186, 202, 1, 73, 42, 76, + 226, 195, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 206, + 40, 79, 162, 8, 69, 158, 20, 72, 2, 77, 2, 82, 3, 89, 14, 60, 5, 69, 68, + 73, 67, 32, 242, 129, 23, 79, 223, 214, 3, 65, 4, 164, 159, 24, 6, 68, + 79, 85, 66, 76, 69, 175, 244, 1, 65, 16, 66, 67, 206, 186, 22, 78, 190, + 66, 65, 182, 1, 80, 135, 150, 3, 86, 4, 162, 242, 7, 79, 215, 199, 14, + 65, 192, 8, 76, 10, 65, 84, 69, 82, 45, 84, 72, 65, 78, 32, 206, 7, 69, + 207, 199, 25, 89, 56, 134, 1, 65, 150, 3, 66, 62, 79, 216, 2, 11, 69, 81, + 85, 65, 76, 32, 84, 79, 32, 79, 82, 182, 204, 6, 67, 138, 4, 87, 131, + 248, 18, 83, 16, 44, 5, 66, 79, 86, 69, 32, 147, 209, 6, 78, 12, 150, 1, + 83, 180, 1, 19, 68, 79, 85, 66, 76, 69, 45, 76, 73, 78, 69, 32, 69, 81, + 85, 65, 76, 32, 65, 184, 203, 6, 4, 76, 69, 83, 83, 159, 229, 18, 82, 6, + 148, 1, 7, 73, 77, 73, 76, 65, 82, 32, 185, 206, 6, 23, 76, 65, 78, 84, + 69, 68, 32, 69, 81, 85, 65, 76, 32, 65, 66, 79, 86, 69, 32, 76, 69, 83, + 83, 4, 26, 65, 143, 206, 6, 79, 2, 65, 3, 66, 79, 86, 6, 40, 4, 69, 83, + 73, 68, 247, 206, 6, 85, 2, 231, 2, 69, 20, 40, 2, 82, 32, 245, 1, 3, 86, + 69, 82, 16, 120, 16, 83, 76, 65, 78, 84, 69, 68, 32, 69, 81, 85, 65, 76, + 32, 84, 79, 190, 208, 6, 65, 166, 253, 12, 69, 167, 237, 4, 76, 9, 49, + 10, 32, 87, 73, 84, 72, 32, 68, 79, 84, 32, 6, 44, 5, 65, 66, 79, 86, 69, + 255, 157, 18, 73, 5, 183, 157, 26, 32, 4, 52, 7, 76, 65, 80, 80, 73, 78, + 71, 215, 237, 19, 32, 2, 209, 185, 24, 2, 32, 76, 134, 8, 36, 2, 75, 32, + 185, 73, 2, 78, 32, 254, 7, 130, 3, 65, 216, 15, 8, 67, 65, 80, 73, 84, + 65, 76, 32, 182, 11, 68, 134, 1, 70, 68, 2, 73, 78, 222, 3, 75, 138, 1, + 76, 174, 3, 78, 66, 77, 84, 3, 88, 69, 83, 22, 79, 202, 1, 80, 90, 82, + 182, 1, 83, 130, 22, 84, 200, 2, 13, 85, 80, 83, 73, 76, 79, 78, 32, 87, + 73, 84, 72, 32, 150, 1, 86, 142, 2, 89, 230, 228, 7, 66, 212, 176, 3, 4, + 71, 82, 65, 77, 196, 23, 2, 90, 69, 175, 132, 12, 81, 112, 92, 10, 67, + 82, 79, 80, 72, 79, 78, 73, 67, 32, 172, 14, 6, 78, 79, 32, 84, 69, 76, + 23, 82, 106, 188, 2, 6, 65, 84, 84, 73, 67, 32, 222, 5, 67, 92, 3, 78, + 65, 88, 32, 12, 68, 69, 76, 80, 72, 73, 67, 32, 70, 73, 86, 69, 0, 14, + 83, 84, 82, 65, 84, 73, 65, 78, 32, 70, 73, 70, 84, 89, 40, 11, 69, 80, + 73, 68, 65, 85, 82, 69, 65, 78, 32, 112, 3, 72, 69, 82, 164, 1, 9, 77, + 69, 83, 83, 69, 78, 73, 65, 78, 35, 84, 48, 72, 2, 70, 73, 180, 2, 4, 79, + 78, 69, 32, 205, 1, 4, 84, 69, 78, 32, 26, 36, 3, 70, 84, 89, 105, 2, 86, + 69, 11, 11, 32, 8, 22, 84, 171, 4, 83, 6, 48, 7, 72, 79, 85, 83, 65, 78, + 68, 215, 3, 65, 5, 231, 3, 32, 17, 11, 32, 14, 56, 7, 72, 85, 78, 68, 82, + 69, 68, 18, 84, 143, 3, 83, 7, 131, 2, 32, 6, 48, 7, 72, 79, 85, 83, 65, + 78, 68, 187, 2, 65, 5, 213, 1, 2, 32, 84, 14, 98, 72, 48, 7, 84, 72, 79, + 85, 83, 65, 78, 186, 190, 24, 81, 217, 228, 1, 5, 68, 82, 65, 67, 72, 6, + 44, 5, 85, 78, 68, 82, 69, 155, 190, 24, 65, 4, 17, 2, 68, 32, 4, 22, 84, + 131, 1, 83, 2, 95, 65, 8, 30, 84, 86, 83, 175, 1, 77, 4, 50, 65, 21, 8, + 72, 79, 85, 83, 65, 78, 68, 32, 2, 163, 178, 16, 76, 2, 11, 83, 2, 157, + 191, 24, 2, 84, 65, 4, 88, 5, 65, 82, 89, 83, 84, 145, 1, 12, 89, 82, 69, + 78, 65, 73, 67, 32, 84, 87, 79, 32, 2, 101, 5, 73, 65, 78, 32, 70, 2, 17, + 2, 32, 77, 2, 243, 143, 13, 78, 6, 30, 70, 29, 3, 84, 87, 79, 2, 201, + 178, 15, 2, 73, 86, 5, 11, 32, 2, 237, 148, 10, 5, 68, 82, 65, 67, 72, 8, + 112, 8, 77, 73, 79, 78, 73, 65, 78, 32, 157, 152, 25, 14, 65, 69, 85, 77, + 32, 79, 78, 69, 32, 80, 76, 69, 84, 72, 6, 182, 185, 12, 70, 150, 176, + 12, 84, 215, 58, 79, 2, 11, 32, 2, 143, 233, 24, 84, 32, 92, 8, 72, 69, + 83, 80, 73, 65, 78, 32, 129, 1, 10, 82, 79, 69, 90, 69, 78, 73, 65, 78, + 32, 20, 40, 2, 70, 73, 38, 84, 227, 155, 18, 79, 6, 158, 225, 20, 86, + 247, 236, 3, 70, 8, 226, 174, 15, 72, 142, 191, 10, 69, 239, 48, 87, 12, + 36, 2, 70, 73, 209, 24, 2, 84, 69, 8, 250, 180, 18, 86, 213, 136, 7, 3, + 70, 84, 89, 2, 155, 136, 10, 69, 4, 144, 231, 22, 2, 79, 85, 153, 181, 1, + 3, 84, 65, 66, 154, 2, 66, 76, 174, 45, 82, 66, 68, 144, 230, 7, 2, 75, + 65, 135, 6, 84, 144, 2, 44, 6, 69, 84, 84, 69, 82, 32, 239, 45, 85, 142, + 2, 198, 2, 65, 190, 1, 69, 28, 4, 73, 79, 84, 65, 128, 1, 2, 79, 77, 156, + 3, 3, 82, 72, 79, 46, 83, 48, 7, 85, 80, 83, 73, 76, 79, 78, 146, 33, 80, + 170, 2, 84, 146, 182, 5, 68, 252, 180, 2, 2, 75, 65, 238, 192, 1, 71, + 190, 138, 11, 67, 230, 154, 2, 66, 2, 72, 2, 90, 166, 1, 76, 186, 235, 2, + 89, 210, 43, 77, 2, 78, 147, 17, 88, 48, 68, 4, 76, 80, 72, 65, 213, 28, + 8, 82, 67, 72, 65, 73, 67, 32, 83, 47, 33, 6, 32, 87, 73, 84, 72, 32, 44, + 242, 2, 68, 30, 80, 226, 29, 86, 226, 5, 79, 162, 234, 3, 84, 191, 174, + 5, 77, 62, 186, 1, 84, 131, 27, 80, 31, 33, 6, 32, 87, 73, 84, 72, 32, + 28, 186, 5, 68, 136, 25, 2, 80, 83, 158, 1, 86, 226, 5, 79, 162, 234, 3, + 84, 191, 174, 5, 77, 62, 28, 2, 69, 71, 235, 34, 73, 42, 11, 65, 43, 33, + 6, 32, 87, 73, 84, 72, 32, 40, 54, 68, 30, 80, 194, 35, 79, 22, 86, 143, + 234, 3, 84, 16, 65, 4, 65, 83, 73, 65, 18, 36, 4, 83, 73, 76, 73, 211, + 16, 82, 17, 29, 5, 32, 65, 78, 68, 32, 14, 44, 2, 79, 88, 0, 3, 86, 65, + 82, 23, 80, 4, 81, 2, 73, 65, 6, 60, 10, 69, 82, 73, 83, 80, 79, 77, 69, + 78, 73, 175, 15, 82, 5, 169, 15, 7, 32, 65, 78, 68, 32, 80, 82, 5, 161, + 35, 7, 32, 87, 73, 84, 72, 32, 68, 6, 146, 144, 8, 73, 162, 210, 17, 65, + 239, 48, 72, 23, 33, 6, 32, 87, 73, 84, 72, 32, 20, 66, 68, 166, 26, 86, + 226, 5, 79, 162, 234, 3, 84, 191, 174, 5, 77, 10, 130, 24, 65, 213, 191, + 22, 5, 73, 65, 76, 89, 84, 18, 76, 9, 73, 65, 76, 89, 84, 73, 75, 65, 32, + 32, 3, 82, 65, 67, 227, 22, 65, 8, 174, 24, 65, 243, 240, 3, 84, 2, 131, + 191, 11, 72, 4, 40, 3, 73, 86, 69, 1, 3, 79, 85, 82, 2, 205, 37, 2, 32, + 79, 76, 144, 1, 27, 83, 84, 82, 85, 77, 69, 78, 84, 65, 76, 32, 78, 79, + 84, 65, 84, 73, 79, 78, 32, 83, 89, 77, 66, 79, 76, 45, 193, 168, 22, 2, + 68, 73, 74, 70, 49, 70, 50, 62, 51, 62, 52, 170, 37, 53, 194, 134, 26, + 55, 3, 56, 17, 162, 173, 26, 49, 2, 50, 2, 51, 2, 52, 2, 55, 2, 56, 3, + 57, 15, 222, 172, 26, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 57, 12, 162, + 172, 26, 48, 2, 50, 2, 54, 2, 55, 2, 56, 3, 57, 17, 230, 171, 26, 48, 2, + 50, 2, 51, 2, 53, 2, 55, 2, 56, 3, 57, 8, 54, 65, 38, 79, 169, 32, 6, 89, + 65, 84, 72, 79, 83, 4, 186, 130, 8, 80, 239, 146, 17, 73, 2, 11, 82, 2, + 11, 79, 2, 167, 131, 24, 78, 32, 128, 1, 6, 69, 84, 84, 69, 82, 32, 168, + 2, 6, 79, 87, 69, 82, 32, 78, 32, 6, 85, 78, 65, 84, 69, 32, 177, 209, + 22, 2, 73, 84, 24, 94, 83, 140, 13, 9, 65, 82, 67, 72, 65, 73, 67, 32, + 75, 2, 75, 182, 11, 68, 195, 191, 25, 89, 16, 88, 13, 77, 65, 76, 76, 32, + 67, 65, 80, 73, 84, 65, 76, 32, 210, 12, 65, 171, 247, 7, 84, 12, 74, 80, + 222, 196, 9, 71, 166, 157, 9, 79, 154, 212, 2, 82, 139, 181, 1, 76, 4, + 182, 147, 26, 83, 219, 19, 73, 2, 213, 138, 10, 3, 85, 77, 69, 4, 222, + 25, 83, 139, 228, 7, 69, 4, 80, 4, 69, 84, 82, 69, 149, 208, 23, 10, 85, + 83, 73, 67, 65, 76, 32, 76, 69, 73, 2, 235, 212, 2, 84, 12, 88, 3, 78, + 69, 32, 194, 240, 9, 88, 176, 151, 5, 3, 85, 78, 75, 225, 208, 5, 2, 66, 79, 6, 64, 8, 72, 65, 76, 70, 32, 83, 73, 71, 21, 4, 81, 85, 65, 82, 4, - 139, 146, 25, 78, 2, 255, 206, 20, 84, 16, 62, 82, 206, 11, 83, 114, 69, - 154, 236, 7, 72, 211, 132, 17, 73, 2, 217, 29, 2, 79, 83, 6, 100, 3, 72, - 79, 32, 165, 246, 7, 16, 69, 86, 69, 82, 83, 69, 68, 32, 76, 85, 78, 65, - 84, 69, 32, 69, 4, 180, 243, 13, 10, 87, 73, 84, 72, 32, 83, 84, 82, 79, - 75, 135, 137, 11, 83, 226, 2, 220, 1, 5, 77, 65, 76, 76, 32, 192, 19, 22, + 255, 164, 25, 78, 2, 251, 216, 20, 84, 16, 62, 82, 206, 11, 83, 114, 69, + 206, 239, 7, 72, 247, 145, 17, 73, 2, 217, 29, 2, 79, 83, 6, 100, 3, 72, + 79, 32, 217, 249, 7, 16, 69, 86, 69, 82, 83, 69, 68, 32, 76, 85, 78, 65, + 84, 69, 32, 69, 4, 240, 243, 13, 10, 87, 73, 84, 72, 32, 83, 84, 82, 79, + 75, 163, 153, 11, 83, 226, 2, 220, 1, 5, 77, 65, 76, 76, 32, 192, 19, 22, 85, 66, 83, 67, 82, 73, 80, 84, 32, 83, 77, 65, 76, 76, 32, 76, 69, 84, - 84, 69, 82, 32, 72, 10, 89, 77, 66, 79, 76, 32, 84, 65, 85, 32, 189, 144, + 84, 69, 82, 32, 72, 10, 89, 77, 66, 79, 76, 32, 84, 65, 85, 32, 201, 160, 24, 6, 73, 78, 85, 83, 79, 73, 212, 2, 56, 7, 76, 69, 84, 84, 69, 82, 32, 202, 17, 82, 67, 68, 206, 2, 178, 2, 65, 162, 2, 68, 38, 69, 52, 4, 73, 79, 84, 65, 0, 7, 85, 80, 83, 73, 76, 79, 78, 254, 3, 75, 28, 2, 79, 77, - 182, 5, 80, 112, 3, 82, 72, 79, 94, 83, 94, 84, 244, 230, 7, 2, 70, 73, - 138, 193, 1, 71, 190, 132, 11, 67, 194, 149, 2, 66, 2, 72, 2, 90, 166, 1, - 76, 182, 147, 3, 77, 2, 78, 147, 17, 88, 58, 64, 4, 76, 80, 72, 65, 149, + 182, 5, 80, 112, 3, 82, 72, 79, 94, 83, 94, 84, 168, 234, 7, 2, 70, 73, + 210, 193, 1, 71, 190, 138, 11, 67, 230, 154, 2, 66, 2, 72, 2, 90, 166, 1, + 76, 138, 151, 3, 77, 2, 78, 147, 17, 88, 58, 64, 4, 76, 80, 72, 65, 149, 1, 7, 82, 67, 72, 65, 73, 67, 32, 55, 33, 6, 32, 87, 73, 84, 72, 32, 52, - 82, 86, 226, 6, 68, 30, 80, 114, 79, 142, 1, 89, 226, 238, 3, 84, 239, - 167, 5, 77, 6, 154, 5, 82, 155, 3, 65, 4, 18, 75, 23, 83, 2, 215, 244, 7, - 79, 2, 11, 65, 2, 203, 189, 13, 77, 4, 138, 223, 7, 73, 235, 239, 14, 69, + 82, 86, 226, 6, 68, 30, 80, 114, 79, 142, 1, 89, 142, 236, 3, 84, 191, + 174, 5, 77, 6, 154, 5, 82, 155, 3, 65, 4, 18, 75, 23, 83, 2, 139, 248, 7, + 79, 2, 11, 65, 2, 147, 189, 13, 77, 4, 202, 226, 7, 73, 203, 251, 14, 69, 70, 22, 80, 215, 4, 84, 20, 249, 7, 3, 83, 73, 76, 41, 33, 6, 32, 87, 73, - 84, 72, 32, 38, 78, 68, 166, 1, 80, 178, 1, 86, 226, 5, 79, 246, 236, 3, - 84, 239, 167, 5, 77, 18, 50, 65, 29, 8, 73, 65, 76, 89, 84, 73, 75, 65, + 84, 72, 32, 38, 78, 68, 166, 1, 80, 178, 1, 86, 226, 5, 79, 162, 234, 3, + 84, 191, 174, 5, 77, 18, 50, 65, 29, 8, 73, 65, 76, 89, 84, 73, 75, 65, 8, 153, 1, 3, 83, 73, 65, 11, 29, 5, 32, 65, 78, 68, 32, 8, 170, 1, 80, - 154, 6, 79, 22, 86, 227, 236, 3, 84, 10, 18, 83, 115, 69, 8, 21, 3, 73, + 154, 6, 79, 22, 86, 143, 234, 3, 84, 10, 18, 83, 115, 69, 8, 21, 3, 73, 76, 73, 9, 17, 2, 32, 65, 6, 21, 3, 78, 68, 32, 6, 30, 80, 154, 6, 79, 23, 86, 2, 11, 69, 2, 11, 82, 2, 181, 17, 4, 73, 83, 80, 79, 4, 22, 82, - 147, 15, 65, 2, 133, 226, 25, 2, 65, 67, 4, 206, 239, 7, 65, 3, 79, 70, + 147, 15, 65, 2, 249, 244, 25, 2, 65, 67, 4, 130, 243, 7, 65, 3, 79, 70, 28, 2, 69, 71, 151, 3, 73, 50, 11, 65, 51, 33, 6, 32, 87, 73, 84, 72, 32, - 48, 58, 68, 30, 80, 114, 79, 62, 86, 82, 89, 227, 238, 3, 84, 16, 61, 4, + 48, 58, 68, 30, 80, 114, 79, 62, 86, 82, 89, 143, 236, 3, 84, 16, 61, 4, 65, 83, 73, 65, 20, 32, 4, 83, 73, 76, 73, 91, 69, 17, 29, 5, 32, 65, 78, 68, 32, 14, 42, 79, 12, 2, 80, 69, 50, 86, 83, 89, 4, 83, 88, 4, 89, 9, 82, 73, 83, 80, 79, 77, 69, 78, 73, 4, 11, 65, 4, 11, 82, 4, 17, 2, 73, 65, 5, 33, 6, 32, 65, 78, 68, 32, 89, 2, 243, 12, 80, 20, 17, 2, 67, 82, 20, 17, 2, 79, 78, 21, 33, 6, 32, 87, 73, 84, 72, 32, 18, 88, 5, 68, 65, - 83, 73, 65, 0, 5, 80, 83, 73, 76, 73, 54, 79, 22, 86, 227, 236, 3, 84, 7, - 29, 5, 32, 65, 78, 68, 32, 4, 18, 79, 23, 86, 2, 207, 216, 9, 88, 2, 179, - 9, 65, 8, 88, 11, 65, 77, 80, 72, 89, 76, 73, 65, 78, 32, 68, 174, 233, - 25, 72, 2, 83, 219, 19, 73, 2, 139, 212, 7, 73, 7, 33, 6, 32, 87, 73, 84, - 72, 32, 4, 34, 68, 145, 129, 21, 2, 80, 83, 2, 243, 197, 8, 65, 10, 54, - 65, 186, 231, 7, 84, 230, 1, 73, 207, 243, 17, 72, 4, 142, 177, 13, 77, - 207, 202, 12, 78, 4, 246, 193, 22, 72, 219, 148, 3, 65, 4, 41, 8, 69, 86, + 83, 73, 65, 0, 5, 80, 83, 73, 76, 73, 54, 79, 22, 86, 143, 234, 3, 84, 7, + 29, 5, 32, 65, 78, 68, 32, 4, 18, 79, 23, 86, 2, 203, 220, 9, 88, 2, 179, + 9, 65, 8, 88, 11, 65, 77, 80, 72, 89, 76, 73, 65, 78, 32, 68, 162, 252, + 25, 72, 2, 83, 219, 19, 73, 2, 203, 215, 7, 73, 7, 33, 6, 32, 87, 73, 84, + 72, 32, 4, 34, 68, 177, 139, 21, 2, 80, 83, 2, 203, 201, 8, 65, 10, 54, + 65, 238, 234, 7, 84, 230, 1, 73, 143, 131, 18, 72, 4, 214, 176, 13, 77, + 251, 221, 12, 78, 4, 150, 209, 22, 72, 175, 152, 3, 65, 4, 41, 8, 69, 86, 69, 82, 83, 69, 68, 32, 4, 18, 68, 43, 76, 2, 37, 7, 79, 84, 84, 69, 68, - 32, 76, 2, 11, 85, 2, 33, 6, 78, 65, 84, 69, 32, 83, 2, 169, 232, 7, 3, - 73, 71, 77, 10, 158, 166, 9, 71, 190, 132, 11, 67, 2, 80, 222, 102, 82, - 231, 174, 1, 66, 2, 239, 144, 21, 82, 16, 106, 72, 104, 7, 82, 89, 66, - 76, 73, 79, 78, 44, 3, 87, 79, 32, 254, 229, 3, 79, 233, 196, 16, 2, 65, - 76, 6, 40, 4, 82, 69, 69, 32, 143, 230, 7, 69, 4, 146, 1, 79, 157, 200, - 22, 7, 81, 85, 65, 82, 84, 69, 82, 2, 21, 3, 32, 66, 65, 2, 231, 218, 23, - 83, 4, 42, 79, 221, 152, 12, 4, 84, 72, 73, 82, 2, 237, 224, 19, 2, 66, + 32, 76, 2, 11, 85, 2, 33, 6, 78, 65, 84, 69, 32, 83, 2, 221, 235, 7, 3, + 73, 71, 77, 10, 154, 170, 9, 71, 190, 138, 11, 67, 2, 80, 130, 103, 82, + 231, 179, 1, 66, 2, 143, 155, 21, 82, 16, 106, 72, 104, 7, 82, 89, 66, + 76, 73, 79, 78, 44, 3, 87, 79, 32, 170, 227, 3, 79, 185, 209, 16, 2, 65, + 76, 6, 40, 4, 82, 69, 69, 32, 195, 233, 7, 69, 4, 146, 1, 79, 189, 215, + 22, 7, 81, 85, 65, 82, 84, 69, 82, 2, 21, 3, 32, 66, 65, 2, 255, 233, 23, + 83, 4, 42, 79, 165, 152, 12, 4, 84, 72, 73, 82, 2, 133, 229, 19, 2, 66, 79, 6, 80, 5, 65, 67, 85, 84, 69, 0, 9, 68, 73, 65, 69, 82, 69, 83, 73, - 83, 39, 72, 2, 33, 6, 32, 65, 78, 68, 32, 72, 2, 197, 195, 7, 2, 79, 79, + 83, 39, 72, 2, 33, 6, 32, 65, 78, 68, 32, 72, 2, 133, 199, 7, 2, 79, 79, 60, 102, 65, 21, 21, 79, 67, 65, 76, 32, 78, 79, 84, 65, 84, 73, 79, 78, - 32, 83, 89, 77, 66, 79, 76, 45, 2, 135, 207, 9, 82, 58, 90, 50, 2, 53, - 154, 179, 23, 49, 182, 192, 2, 51, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 13, - 202, 243, 25, 48, 2, 49, 2, 50, 2, 51, 3, 52, 4, 26, 80, 207, 177, 20, - 69, 2, 11, 79, 2, 33, 6, 71, 69, 71, 82, 65, 77, 2, 197, 246, 20, 2, 77, - 69, 8, 170, 237, 13, 65, 178, 177, 10, 66, 174, 76, 72, 253, 64, 3, 83, - 65, 76, 12, 60, 6, 78, 78, 73, 78, 71, 32, 137, 233, 24, 3, 77, 65, 67, - 10, 100, 4, 70, 65, 67, 69, 149, 228, 17, 15, 67, 65, 84, 32, 70, 65, 67, + 32, 83, 89, 77, 66, 79, 76, 45, 2, 131, 211, 9, 82, 58, 90, 50, 2, 53, + 190, 194, 23, 49, 134, 196, 2, 51, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 13, + 190, 134, 26, 48, 2, 49, 2, 50, 2, 51, 3, 52, 4, 26, 80, 203, 187, 20, + 69, 2, 11, 79, 2, 33, 6, 71, 69, 71, 82, 65, 77, 2, 229, 128, 21, 2, 77, + 69, 8, 230, 237, 13, 65, 206, 193, 10, 66, 202, 78, 72, 253, 64, 3, 83, + 65, 76, 12, 60, 6, 78, 78, 73, 78, 71, 32, 253, 251, 24, 3, 77, 65, 67, + 10, 100, 4, 70, 65, 67, 69, 241, 232, 17, 15, 67, 65, 84, 32, 70, 65, 67, 69, 32, 87, 73, 84, 72, 32, 83, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 108, 23, 79, 78, 69, 32, 76, 65, 82, 71, 69, 32, 65, 78, 68, 32, 79, 78, 69, - 32, 83, 77, 65, 76, 76, 35, 83, 2, 11, 32, 2, 191, 157, 8, 69, 4, 32, 2, - 84, 65, 203, 226, 17, 77, 2, 187, 243, 22, 82, 6, 28, 3, 85, 80, 32, 39, - 87, 4, 214, 192, 22, 83, 179, 236, 2, 77, 2, 227, 144, 18, 73, 220, 4, - 136, 1, 2, 65, 82, 70, 73, 52, 7, 74, 65, 82, 65, 84, 73, 32, 208, 6, 12, + 32, 83, 77, 65, 76, 76, 35, 83, 2, 11, 32, 2, 151, 161, 8, 69, 4, 32, 2, + 84, 65, 167, 231, 17, 77, 2, 223, 130, 23, 82, 6, 28, 3, 85, 80, 32, 39, + 87, 4, 246, 207, 22, 83, 135, 240, 2, 77, 2, 207, 149, 18, 73, 220, 4, + 136, 1, 2, 65, 82, 70, 73, 52, 7, 74, 65, 82, 65, 84, 73, 32, 184, 6, 12, 78, 74, 65, 76, 65, 32, 71, 79, 78, 68, 73, 32, 143, 3, 82, 4, 34, 65, - 245, 246, 20, 2, 68, 83, 2, 11, 78, 2, 187, 229, 24, 73, 4, 238, 202, 24, - 84, 217, 160, 1, 4, 68, 69, 32, 68, 182, 1, 168, 1, 7, 76, 69, 84, 84, + 149, 129, 21, 2, 68, 83, 2, 11, 78, 2, 175, 248, 24, 73, 4, 222, 219, 24, + 84, 221, 162, 1, 4, 68, 69, 32, 68, 182, 1, 168, 1, 7, 76, 69, 84, 84, 69, 82, 32, 220, 1, 5, 83, 73, 71, 78, 32, 160, 2, 6, 86, 79, 87, 69, 76, - 32, 238, 251, 19, 65, 154, 24, 82, 166, 225, 3, 68, 203, 224, 1, 79, 98, - 162, 144, 22, 65, 38, 68, 114, 84, 46, 86, 186, 5, 85, 206, 201, 1, 73, - 42, 76, 246, 14, 90, 134, 178, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, + 32, 234, 133, 20, 65, 154, 24, 82, 182, 231, 3, 68, 179, 227, 1, 79, 98, + 194, 159, 22, 65, 38, 68, 114, 84, 46, 86, 186, 5, 85, 186, 202, 1, 73, + 42, 76, 246, 14, 90, 238, 180, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 77, 2, 82, 2, 89, 186, 2, 69, 3, 79, - 24, 98, 67, 28, 3, 77, 65, 68, 22, 84, 138, 202, 3, 83, 238, 137, 18, 78, - 242, 60, 65, 231, 147, 3, 86, 4, 118, 73, 219, 210, 21, 65, 2, 231, 135, + 24, 98, 67, 28, 3, 77, 65, 68, 22, 84, 182, 199, 3, 83, 150, 150, 18, 78, + 190, 66, 65, 187, 151, 3, 86, 4, 118, 73, 175, 220, 21, 65, 2, 219, 140, 19, 68, 4, 68, 5, 87, 79, 45, 67, 73, 29, 8, 72, 82, 69, 69, 45, 68, 79, - 84, 2, 25, 4, 82, 67, 76, 69, 2, 253, 195, 20, 5, 32, 78, 85, 75, 84, 34, - 36, 5, 83, 73, 71, 78, 32, 87, 67, 30, 82, 67, 130, 146, 22, 65, 38, 85, - 22, 86, 186, 201, 1, 73, 222, 137, 2, 69, 3, 79, 4, 245, 210, 21, 5, 65, - 78, 68, 82, 65, 126, 108, 7, 76, 69, 84, 84, 69, 82, 32, 216, 1, 5, 83, - 73, 71, 78, 32, 38, 86, 154, 241, 23, 68, 203, 224, 1, 79, 80, 130, 243, - 21, 78, 146, 23, 65, 38, 68, 114, 84, 230, 5, 85, 206, 201, 1, 73, 42, - 76, 246, 193, 1, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 206, 40, 79, 162, - 8, 69, 158, 20, 72, 2, 77, 2, 82, 2, 83, 2, 86, 3, 89, 4, 178, 172, 23, - 86, 179, 241, 1, 65, 20, 190, 7, 79, 163, 162, 23, 73, 160, 2, 84, 6, 77, - 85, 75, 72, 73, 32, 189, 7, 10, 85, 78, 71, 32, 75, 72, 69, 77, 65, 32, - 172, 1, 194, 1, 65, 44, 7, 76, 69, 84, 84, 69, 82, 32, 238, 1, 83, 228, - 2, 2, 86, 79, 172, 144, 13, 3, 84, 73, 80, 130, 237, 5, 73, 216, 170, 2, - 5, 69, 75, 32, 79, 78, 214, 193, 2, 68, 227, 172, 1, 85, 4, 218, 200, 21, - 66, 213, 225, 1, 2, 68, 68, 96, 162, 194, 8, 71, 2, 75, 134, 195, 13, 65, - 38, 68, 82, 82, 34, 84, 230, 5, 85, 206, 201, 1, 73, 42, 76, 250, 192, 1, - 78, 126, 66, 2, 67, 2, 74, 2, 80, 2, 83, 206, 40, 79, 162, 8, 69, 158, - 20, 70, 2, 72, 2, 77, 2, 86, 2, 89, 3, 90, 26, 108, 19, 69, 81, 85, 69, - 78, 67, 69, 32, 70, 79, 82, 32, 76, 69, 84, 84, 69, 82, 32, 93, 4, 73, - 71, 78, 32, 12, 70, 71, 2, 75, 230, 225, 23, 83, 170, 216, 1, 76, 226, - 31, 70, 3, 90, 2, 227, 225, 23, 72, 14, 128, 1, 6, 65, 68, 65, 75, 32, - 66, 2, 66, 176, 140, 15, 2, 85, 68, 254, 186, 6, 78, 180, 171, 2, 3, 89, - 65, 75, 163, 165, 1, 86, 2, 255, 147, 8, 73, 18, 45, 9, 87, 69, 76, 32, - 83, 73, 71, 78, 32, 18, 250, 134, 22, 65, 38, 85, 206, 201, 1, 73, 234, - 234, 1, 79, 163, 8, 69, 116, 216, 1, 22, 67, 79, 78, 83, 79, 78, 65, 78, - 84, 32, 83, 73, 71, 78, 32, 77, 69, 68, 73, 65, 76, 32, 44, 7, 76, 69, - 84, 84, 69, 82, 32, 168, 1, 5, 83, 73, 71, 78, 32, 56, 6, 86, 79, 87, 69, - 76, 32, 251, 228, 23, 68, 8, 234, 213, 25, 72, 2, 82, 2, 86, 3, 89, 60, - 198, 230, 21, 78, 182, 23, 68, 114, 84, 206, 145, 3, 66, 2, 67, 2, 71, 2, - 74, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, 2, 82, 2, 83, 2, 86, 2, 89, - 187, 2, 65, 4, 214, 145, 25, 65, 233, 35, 6, 84, 72, 79, 76, 72, 79, 24, - 130, 148, 20, 83, 155, 128, 5, 76, 162, 23, 110, 65, 174, 95, 69, 246, - 103, 73, 146, 9, 79, 158, 12, 84, 30, 85, 130, 1, 89, 249, 236, 12, 4, - 82, 89, 86, 78, 138, 11, 236, 1, 2, 73, 82, 60, 8, 76, 70, 87, 73, 68, - 84, 72, 32, 242, 10, 77, 210, 1, 78, 236, 76, 21, 80, 80, 89, 32, 80, 69, - 82, 83, 79, 78, 32, 82, 65, 73, 83, 73, 78, 71, 32, 79, 78, 22, 82, 38, - 84, 252, 242, 17, 2, 85, 77, 251, 241, 5, 68, 6, 26, 32, 163, 217, 24, - 67, 4, 242, 162, 24, 80, 211, 113, 83, 244, 1, 140, 2, 7, 72, 65, 78, 71, - 85, 76, 32, 216, 4, 8, 75, 65, 84, 65, 75, 65, 78, 65, 204, 3, 3, 76, 69, - 70, 0, 4, 82, 73, 71, 72, 140, 190, 6, 11, 70, 79, 82, 77, 83, 32, 76, - 73, 71, 72, 84, 142, 129, 4, 85, 174, 218, 2, 73, 250, 184, 4, 66, 242, - 227, 4, 87, 211, 35, 68, 104, 52, 7, 76, 69, 84, 84, 69, 82, 32, 167, - 217, 10, 70, 102, 206, 1, 75, 28, 5, 78, 73, 69, 85, 78, 42, 80, 24, 5, - 82, 73, 69, 85, 76, 86, 83, 98, 89, 202, 61, 67, 54, 69, 30, 73, 242, 4, - 77, 138, 1, 84, 206, 3, 87, 198, 1, 72, 230, 194, 24, 65, 2, 79, 163, 64, - 85, 6, 222, 65, 72, 155, 3, 73, 7, 11, 45, 4, 134, 72, 67, 131, 3, 72, 6, - 146, 69, 72, 35, 73, 17, 11, 45, 14, 206, 49, 84, 226, 14, 80, 130, 4, - 77, 194, 3, 75, 218, 2, 72, 99, 83, 12, 40, 4, 83, 65, 78, 71, 151, 221, - 13, 73, 10, 210, 70, 67, 42, 75, 74, 80, 34, 84, 211, 2, 83, 14, 194, - 134, 23, 69, 194, 133, 2, 65, 162, 64, 73, 2, 79, 3, 85, 118, 70, 32, - 193, 240, 2, 11, 45, 72, 73, 82, 65, 71, 65, 78, 65, 32, 80, 116, 76, 7, - 76, 69, 84, 84, 69, 82, 32, 150, 240, 2, 83, 34, 86, 215, 200, 21, 77, - 110, 146, 1, 83, 138, 234, 2, 78, 150, 2, 72, 2, 75, 2, 77, 2, 82, 2, 84, - 170, 1, 89, 222, 41, 87, 154, 178, 22, 65, 2, 69, 2, 73, 2, 79, 3, 85, - 28, 76, 5, 77, 65, 76, 76, 32, 234, 200, 25, 65, 2, 69, 2, 73, 2, 79, 3, - 85, 18, 242, 236, 2, 89, 142, 183, 22, 84, 234, 36, 65, 2, 69, 2, 73, 2, - 79, 3, 85, 4, 11, 84, 4, 212, 157, 13, 2, 32, 67, 239, 138, 11, 87, 14, - 56, 3, 77, 69, 82, 106, 83, 233, 248, 21, 3, 66, 85, 82, 9, 29, 5, 32, - 65, 78, 68, 32, 6, 168, 170, 12, 3, 87, 82, 69, 144, 228, 3, 2, 83, 73, - 207, 136, 8, 80, 4, 152, 137, 25, 3, 84, 69, 82, 163, 61, 65, 194, 8, + 84, 2, 25, 4, 82, 67, 76, 69, 2, 165, 206, 20, 5, 32, 78, 85, 75, 84, 34, + 44, 5, 83, 73, 71, 78, 32, 215, 191, 15, 67, 30, 210, 191, 15, 67, 154, + 226, 6, 65, 38, 85, 22, 86, 166, 202, 1, 73, 198, 140, 2, 69, 3, 79, 126, + 108, 7, 76, 69, 84, 84, 69, 82, 32, 216, 1, 5, 83, 73, 71, 78, 32, 38, + 86, 190, 129, 24, 68, 179, 227, 1, 79, 80, 186, 129, 22, 78, 146, 24, 65, + 38, 68, 114, 84, 230, 5, 85, 186, 202, 1, 73, 42, 76, 222, 196, 1, 66, 2, + 67, 2, 71, 2, 74, 2, 75, 2, 80, 206, 40, 79, 162, 8, 69, 158, 20, 72, 2, + 77, 2, 82, 2, 83, 2, 86, 3, 89, 4, 250, 187, 23, 86, 247, 244, 1, 65, 20, + 190, 7, 79, 235, 177, 23, 73, 160, 2, 84, 6, 77, 85, 75, 72, 73, 32, 189, + 7, 10, 85, 78, 71, 32, 75, 72, 69, 77, 65, 32, 172, 1, 194, 1, 65, 44, 7, + 76, 69, 84, 84, 69, 82, 32, 238, 1, 83, 228, 2, 2, 86, 79, 140, 144, 13, + 3, 84, 73, 80, 174, 242, 5, 73, 252, 175, 2, 5, 69, 75, 32, 79, 78, 202, + 199, 2, 68, 203, 175, 1, 85, 4, 198, 210, 21, 66, 177, 231, 1, 2, 68, 68, + 96, 174, 198, 8, 71, 2, 75, 178, 206, 13, 65, 38, 68, 82, 82, 34, 84, + 230, 5, 85, 186, 202, 1, 73, 42, 76, 226, 195, 1, 78, 126, 66, 2, 67, 2, + 74, 2, 80, 2, 83, 206, 40, 79, 162, 8, 69, 158, 20, 70, 2, 72, 2, 77, 2, + 86, 2, 89, 3, 90, 26, 108, 19, 69, 81, 85, 69, 78, 67, 69, 32, 70, 79, + 82, 32, 76, 69, 84, 84, 69, 82, 32, 93, 4, 73, 71, 78, 32, 12, 70, 71, 2, + 75, 138, 242, 23, 83, 146, 219, 1, 76, 226, 31, 70, 3, 90, 2, 135, 242, + 23, 72, 14, 128, 1, 6, 65, 68, 65, 75, 32, 66, 2, 66, 220, 140, 15, 2, + 85, 68, 190, 196, 6, 78, 236, 177, 2, 3, 89, 65, 75, 139, 168, 1, 86, 2, + 239, 151, 8, 73, 18, 45, 9, 87, 69, 76, 32, 83, 73, 71, 78, 32, 18, 178, + 150, 22, 65, 38, 85, 186, 202, 1, 73, 210, 237, 1, 79, 163, 8, 69, 116, + 216, 1, 22, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, + 77, 69, 68, 73, 65, 76, 32, 44, 7, 76, 69, 84, 84, 69, 82, 32, 168, 1, 5, + 83, 73, 71, 78, 32, 56, 6, 86, 79, 87, 69, 76, 32, 159, 245, 23, 68, 8, + 246, 232, 25, 72, 2, 82, 2, 86, 3, 89, 60, 254, 244, 21, 78, 182, 24, 68, + 114, 84, 162, 149, 3, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, + 2, 76, 2, 77, 2, 82, 2, 83, 2, 86, 2, 89, 187, 2, 65, 4, 226, 164, 25, + 65, 233, 35, 6, 84, 72, 79, 76, 72, 79, 24, 150, 158, 20, 83, 147, 137, + 5, 76, 168, 23, 110, 65, 214, 95, 69, 150, 104, 73, 146, 9, 79, 158, 12, + 84, 30, 85, 130, 1, 89, 225, 236, 12, 4, 82, 89, 86, 78, 140, 11, 236, 1, + 2, 73, 82, 100, 8, 76, 70, 87, 73, 68, 84, 72, 32, 242, 10, 77, 210, 1, + 78, 236, 76, 21, 80, 80, 89, 32, 80, 69, 82, 83, 79, 78, 32, 82, 65, 73, + 83, 73, 78, 71, 32, 79, 78, 22, 82, 38, 84, 208, 247, 17, 2, 85, 77, 255, + 253, 5, 68, 8, 66, 32, 140, 199, 20, 6, 89, 32, 67, 82, 69, 65, 251, 164, + 4, 67, 4, 194, 179, 24, 80, 231, 115, 83, 244, 1, 140, 2, 7, 72, 65, 78, + 71, 85, 76, 32, 216, 4, 8, 75, 65, 84, 65, 75, 65, 78, 65, 204, 3, 3, 76, + 69, 70, 0, 4, 82, 73, 71, 72, 204, 188, 6, 11, 70, 79, 82, 77, 83, 32, + 76, 73, 71, 72, 84, 202, 134, 4, 85, 250, 213, 2, 73, 142, 190, 4, 66, + 166, 238, 4, 87, 215, 35, 68, 104, 52, 7, 76, 69, 84, 84, 69, 82, 32, + 163, 221, 10, 70, 102, 206, 1, 75, 28, 5, 78, 73, 69, 85, 78, 42, 80, 24, + 5, 82, 73, 69, 85, 76, 86, 83, 98, 89, 202, 61, 67, 54, 69, 30, 73, 242, + 4, 77, 138, 1, 84, 206, 3, 87, 198, 1, 72, 202, 213, 24, 65, 2, 79, 163, + 64, 85, 6, 222, 65, 72, 155, 3, 73, 7, 11, 45, 4, 134, 72, 67, 131, 3, + 72, 6, 146, 69, 72, 35, 73, 17, 11, 45, 14, 206, 49, 84, 226, 14, 80, + 130, 4, 77, 194, 3, 75, 218, 2, 72, 99, 83, 12, 40, 4, 83, 65, 78, 71, + 211, 221, 13, 73, 10, 210, 70, 67, 42, 75, 74, 80, 34, 84, 211, 2, 83, + 14, 214, 149, 23, 69, 146, 137, 2, 65, 162, 64, 73, 2, 79, 3, 85, 118, + 70, 32, 157, 241, 2, 11, 45, 72, 73, 82, 65, 71, 65, 78, 65, 32, 80, 116, + 76, 7, 76, 69, 84, 84, 69, 82, 32, 242, 240, 2, 83, 34, 86, 195, 216, 21, + 77, 110, 146, 1, 83, 230, 234, 2, 78, 150, 2, 72, 2, 75, 2, 77, 2, 82, 2, + 84, 170, 1, 89, 158, 38, 87, 226, 199, 22, 65, 2, 69, 2, 73, 2, 79, 3, + 85, 28, 76, 5, 77, 65, 76, 76, 32, 206, 219, 25, 65, 2, 69, 2, 73, 2, 79, + 3, 85, 18, 206, 237, 2, 89, 150, 201, 22, 84, 234, 36, 65, 2, 69, 2, 73, + 2, 79, 3, 85, 4, 11, 84, 4, 156, 157, 13, 2, 32, 67, 135, 156, 11, 87, + 14, 56, 3, 77, 69, 82, 106, 83, 249, 135, 22, 3, 66, 85, 82, 9, 29, 5, + 32, 65, 78, 68, 32, 6, 196, 169, 12, 3, 87, 82, 69, 212, 233, 3, 2, 83, + 73, 191, 148, 8, 80, 4, 252, 155, 25, 3, 84, 69, 82, 163, 61, 65, 194, 8, 118, 68, 202, 2, 71, 128, 66, 13, 73, 70, 73, 32, 82, 79, 72, 73, 78, 71, 89, 65, 32, 165, 5, 5, 85, 78, 79, 79, 32, 10, 100, 12, 32, 87, 73, 84, - 72, 32, 73, 78, 68, 69, 88, 32, 188, 1, 2, 66, 65, 193, 218, 21, 2, 83, + 72, 32, 73, 78, 68, 69, 88, 32, 188, 1, 2, 66, 65, 173, 233, 21, 2, 83, 72, 4, 156, 1, 18, 65, 78, 68, 32, 77, 73, 68, 68, 76, 69, 32, 70, 73, 78, 71, 69, 82, 83, 1, 16, 70, 73, 78, 71, 69, 82, 32, 65, 78, 68, 32, - 84, 72, 85, 77, 66, 2, 233, 134, 16, 2, 32, 67, 4, 186, 185, 24, 76, 183, - 137, 1, 71, 170, 7, 84, 3, 85, 76, 32, 217, 64, 13, 90, 72, 79, 85, 32, + 84, 72, 85, 77, 66, 2, 201, 139, 16, 2, 32, 67, 4, 130, 202, 24, 76, 211, + 139, 1, 71, 170, 7, 84, 3, 85, 76, 32, 217, 64, 13, 90, 72, 79, 85, 32, 78, 85, 77, 69, 82, 65, 76, 32, 146, 7, 164, 1, 9, 67, 72, 79, 83, 69, 79, 78, 71, 32, 244, 15, 4, 68, 79, 85, 66, 0, 4, 83, 73, 78, 71, 46, 74, - 180, 31, 7, 76, 69, 84, 84, 69, 82, 32, 147, 154, 10, 70, 250, 1, 246, 1, + 180, 31, 7, 76, 69, 84, 84, 69, 82, 32, 143, 158, 10, 70, 250, 1, 246, 1, 67, 172, 2, 5, 73, 69, 85, 78, 71, 146, 1, 75, 132, 1, 5, 77, 73, 69, 85, 77, 56, 5, 78, 73, 69, 85, 78, 74, 80, 172, 2, 5, 82, 73, 69, 85, 76, 210, 1, 83, 166, 3, 84, 124, 2, 89, 69, 200, 40, 5, 72, 73, 69, 85, 72, - 199, 145, 10, 70, 30, 76, 2, 72, 73, 84, 7, 69, 79, 78, 71, 67, 72, 73, + 195, 149, 10, 70, 30, 76, 2, 72, 73, 84, 7, 69, 79, 78, 71, 67, 72, 73, 121, 4, 73, 69, 85, 67, 16, 40, 4, 69, 85, 67, 72, 41, 2, 84, 85, 7, 11, 45, 4, 202, 31, 75, 243, 26, 72, 10, 21, 3, 69, 85, 77, 10, 22, 83, 155, - 46, 67, 6, 40, 4, 83, 65, 78, 71, 135, 205, 13, 73, 4, 194, 54, 67, 227, + 46, 67, 6, 40, 4, 83, 65, 78, 71, 195, 205, 13, 73, 4, 194, 54, 67, 227, 3, 83, 5, 215, 30, 45, 27, 11, 45, 24, 90, 80, 234, 30, 82, 242, 13, 67, 194, 5, 77, 138, 1, 84, 186, 2, 75, 218, 2, 72, 99, 83, 6, 214, 50, 72, 214, 3, 73, 207, 2, 65, 16, 80, 7, 65, 80, 89, 69, 79, 85, 78, 228, 20, @@ -3518,16 +3563,16 @@ static const unsigned char packed_name_dawg[] = { 79, 83, 251, 3, 83, 13, 11, 45, 10, 242, 46, 84, 146, 2, 67, 42, 75, 75, 80, 29, 11, 45, 26, 78, 75, 42, 83, 190, 44, 77, 154, 3, 67, 82, 78, 34, 80, 34, 84, 243, 1, 72, 6, 170, 22, 65, 130, 19, 72, 135, 7, 73, 8, 40, - 4, 83, 65, 78, 71, 235, 197, 13, 73, 6, 206, 47, 75, 74, 80, 35, 84, 58, + 4, 83, 65, 78, 71, 167, 198, 13, 73, 6, 206, 47, 75, 74, 80, 35, 84, 58, 48, 3, 73, 79, 83, 217, 1, 4, 83, 65, 78, 71, 33, 11, 45, 30, 130, 1, 80, 44, 2, 83, 83, 210, 22, 82, 210, 1, 75, 162, 12, 67, 194, 5, 77, 138, 1, - 84, 226, 2, 78, 178, 2, 72, 191, 157, 18, 73, 6, 184, 23, 4, 73, 69, 85, + 84, 226, 2, 78, 178, 2, 72, 163, 162, 18, 73, 6, 184, 23, 4, 73, 69, 85, 80, 179, 19, 72, 2, 233, 48, 3, 65, 78, 71, 26, 236, 17, 5, 67, 73, 69, 85, 67, 172, 3, 4, 83, 73, 79, 83, 154, 1, 82, 186, 20, 84, 54, 89, 134, - 2, 75, 42, 78, 34, 80, 146, 2, 72, 191, 157, 18, 73, 16, 40, 5, 73, 75, + 2, 75, 42, 78, 34, 80, 146, 2, 72, 163, 162, 18, 73, 16, 40, 5, 73, 75, 69, 85, 84, 195, 41, 72, 15, 11, 45, 12, 226, 20, 82, 178, 19, 77, 154, - 3, 67, 42, 75, 74, 80, 243, 2, 83, 4, 150, 19, 83, 139, 22, 79, 2, 169, - 245, 8, 6, 76, 69, 32, 68, 79, 84, 216, 3, 92, 9, 79, 78, 71, 83, 69, 79, + 3, 67, 42, 75, 74, 80, 243, 2, 83, 4, 150, 19, 83, 139, 22, 79, 2, 149, + 249, 8, 6, 76, 69, 32, 68, 79, 84, 216, 3, 92, 9, 79, 78, 71, 83, 69, 79, 78, 71, 32, 165, 21, 9, 85, 78, 71, 83, 69, 79, 78, 71, 32, 154, 2, 226, 1, 67, 80, 5, 72, 73, 69, 85, 72, 60, 5, 73, 69, 85, 78, 71, 46, 75, 220, 1, 5, 77, 73, 69, 85, 77, 188, 1, 5, 78, 73, 69, 85, 78, 94, 80, 240, 2, @@ -3539,7 +3584,7 @@ static const unsigned char packed_name_dawg[] = { 77, 19, 11, 45, 16, 166, 13, 75, 170, 1, 82, 42, 83, 224, 13, 2, 67, 72, 146, 9, 78, 34, 80, 147, 2, 72, 27, 11, 45, 24, 74, 80, 30, 83, 134, 13, 82, 242, 13, 67, 130, 9, 75, 42, 78, 179, 2, 72, 6, 174, 33, 73, 131, 6, - 65, 6, 40, 4, 83, 65, 78, 71, 227, 185, 13, 73, 4, 238, 35, 78, 147, 3, + 65, 6, 40, 4, 83, 65, 78, 71, 159, 186, 13, 73, 4, 238, 35, 78, 147, 3, 83, 21, 11, 45, 18, 174, 12, 82, 242, 13, 67, 202, 6, 84, 186, 2, 75, 218, 2, 72, 62, 80, 39, 83, 36, 88, 6, 65, 78, 83, 73, 79, 83, 48, 6, 72, 73, 69, 85, 80, 72, 53, 4, 73, 69, 85, 80, 7, 11, 45, 4, 236, 7, 2, 75, @@ -3552,5300 +3597,5329 @@ static const unsigned char packed_name_dawg[] = { 254, 32, 72, 99, 83, 9, 11, 45, 6, 130, 30, 75, 218, 2, 72, 99, 83, 14, 48, 4, 73, 69, 85, 80, 174, 26, 72, 163, 6, 65, 11, 11, 45, 8, 42, 80, 222, 29, 84, 242, 1, 72, 99, 83, 2, 243, 25, 72, 6, 40, 4, 83, 65, 78, - 71, 211, 178, 13, 73, 4, 182, 28, 75, 187, 3, 83, 6, 100, 5, 73, 75, 69, + 71, 143, 179, 13, 73, 4, 182, 28, 75, 187, 3, 83, 6, 100, 5, 73, 75, 69, 85, 84, 151, 25, 72, 6, 56, 9, 79, 82, 73, 78, 72, 73, 69, 85, 72, 191, 3, 83, 5, 255, 29, 45, 52, 48, 3, 73, 79, 83, 161, 1, 4, 83, 65, 78, 71, 25, 11, 45, 22, 82, 75, 162, 3, 82, 242, 13, 67, 198, 2, 80, 254, 2, 77, 138, 1, 84, 147, 5, 72, 4, 22, 65, 135, 26, 73, 2, 237, 18, 6, 80, 89, 69, 79, 85, 78, 28, 160, 1, 5, 82, 73, 69, 85, 76, 36, 6, 84, 73, 75, 69, 85, 84, 16, 3, 89, 69, 83, 138, 19, 83, 178, 1, 77, 154, 3, 67, 42, 75, - 42, 78, 34, 80, 207, 159, 18, 73, 5, 17, 2, 45, 75, 2, 159, 17, 72, 5, - 255, 16, 45, 2, 139, 184, 18, 73, 20, 40, 5, 73, 75, 69, 85, 84, 155, 21, + 42, 78, 34, 80, 179, 164, 18, 73, 5, 17, 2, 45, 75, 2, 159, 17, 72, 5, + 255, 16, 45, 2, 239, 188, 18, 73, 20, 40, 5, 73, 75, 69, 85, 84, 155, 21, 72, 19, 11, 45, 16, 58, 82, 42, 83, 42, 84, 162, 13, 67, 130, 9, 75, 75, - 80, 2, 17, 2, 73, 69, 2, 131, 147, 24, 85, 4, 21, 3, 73, 79, 83, 5, 223, + 80, 2, 17, 2, 73, 69, 2, 203, 163, 24, 85, 4, 21, 3, 73, 79, 83, 5, 223, 1, 45, 2, 255, 19, 72, 18, 44, 6, 83, 73, 69, 85, 78, 71, 243, 19, 79, 17, 11, 45, 14, 50, 75, 30, 83, 198, 17, 77, 154, 6, 72, 63, 80, 4, 166, - 14, 72, 135, 7, 73, 4, 26, 83, 131, 171, 13, 73, 2, 21, 3, 65, 78, 71, 2, + 14, 72, 135, 7, 73, 4, 26, 83, 191, 171, 13, 73, 2, 21, 3, 65, 78, 71, 2, 207, 20, 75, 190, 1, 122, 65, 118, 69, 134, 1, 73, 92, 7, 83, 83, 65, 78, - 71, 65, 82, 106, 79, 138, 1, 85, 102, 89, 174, 15, 87, 235, 141, 10, 70, - 23, 48, 4, 82, 65, 69, 65, 98, 45, 139, 152, 25, 69, 13, 11, 45, 10, 250, - 210, 22, 69, 226, 197, 2, 65, 2, 73, 3, 85, 25, 18, 79, 55, 85, 9, 11, - 45, 6, 158, 243, 24, 69, 234, 36, 79, 3, 85, 15, 11, 45, 12, 170, 9, 69, - 170, 142, 25, 65, 2, 79, 3, 85, 31, 11, 45, 28, 66, 65, 34, 89, 166, 1, - 79, 170, 240, 24, 69, 234, 36, 73, 3, 85, 5, 11, 82, 2, 215, 144, 18, 65, - 14, 50, 65, 162, 208, 22, 69, 226, 197, 2, 79, 3, 85, 7, 138, 247, 24, - 45, 247, 30, 69, 23, 26, 45, 199, 149, 25, 69, 18, 50, 79, 22, 89, 158, - 207, 22, 69, 227, 197, 2, 85, 5, 183, 129, 25, 45, 8, 154, 207, 22, 69, - 195, 133, 2, 65, 17, 11, 45, 14, 158, 19, 89, 186, 164, 5, 73, 152, 254, - 12, 3, 69, 79, 45, 190, 158, 6, 65, 163, 64, 85, 62, 42, 65, 70, 69, 66, - 73, 22, 79, 107, 85, 11, 26, 45, 175, 147, 25, 69, 6, 182, 244, 24, 89, - 246, 30, 79, 3, 85, 11, 11, 79, 9, 11, 45, 6, 178, 144, 25, 89, 186, 2, - 79, 3, 85, 5, 219, 237, 24, 45, 19, 11, 45, 16, 58, 89, 202, 209, 24, 65, - 174, 33, 69, 246, 30, 73, 3, 79, 6, 198, 209, 24, 65, 175, 33, 69, 21, - 11, 45, 18, 142, 16, 89, 206, 187, 22, 69, 194, 133, 2, 65, 162, 64, 73, + 71, 65, 82, 106, 79, 138, 1, 85, 102, 89, 174, 15, 87, 231, 145, 10, 70, + 23, 48, 4, 82, 65, 69, 65, 98, 45, 239, 170, 25, 69, 13, 11, 45, 10, 142, + 226, 22, 69, 178, 201, 2, 65, 2, 73, 3, 85, 25, 18, 79, 55, 85, 9, 11, + 45, 6, 130, 134, 25, 69, 234, 36, 79, 3, 85, 15, 11, 45, 12, 170, 9, 69, + 142, 161, 25, 65, 2, 79, 3, 85, 31, 11, 45, 28, 66, 65, 34, 89, 166, 1, + 79, 142, 131, 25, 69, 234, 36, 73, 3, 85, 5, 11, 82, 2, 171, 149, 18, 65, + 14, 50, 65, 182, 223, 22, 69, 178, 201, 2, 79, 3, 85, 7, 238, 137, 25, + 45, 247, 30, 69, 23, 26, 45, 171, 168, 25, 69, 18, 50, 79, 22, 89, 178, + 222, 22, 69, 179, 201, 2, 85, 5, 155, 148, 25, 45, 8, 174, 222, 22, 69, + 147, 137, 2, 65, 17, 11, 45, 14, 158, 19, 89, 130, 163, 5, 73, 180, 132, + 13, 3, 69, 79, 45, 190, 172, 6, 65, 163, 64, 85, 62, 42, 65, 70, 69, 66, + 73, 22, 79, 107, 85, 11, 26, 45, 147, 166, 25, 69, 6, 154, 135, 25, 89, + 246, 30, 79, 3, 85, 11, 11, 79, 9, 11, 45, 6, 150, 163, 25, 89, 186, 2, + 79, 3, 85, 5, 191, 128, 25, 45, 19, 11, 45, 16, 58, 89, 174, 228, 24, 65, + 174, 33, 69, 246, 30, 73, 3, 79, 6, 170, 228, 24, 65, 175, 33, 69, 21, + 11, 45, 18, 142, 16, 89, 226, 202, 22, 69, 146, 137, 2, 65, 162, 64, 73, 2, 79, 3, 85, 186, 1, 226, 1, 65, 46, 67, 54, 69, 30, 73, 22, 75, 188, 1, 5, 77, 73, 69, 85, 77, 64, 5, 78, 73, 69, 85, 78, 70, 80, 168, 1, 5, 82, - 73, 69, 85, 76, 254, 1, 84, 90, 83, 246, 2, 87, 50, 89, 150, 1, 72, 230, - 194, 24, 79, 163, 64, 85, 9, 156, 13, 3, 82, 65, 69, 235, 129, 25, 69, 4, - 22, 72, 207, 8, 73, 2, 141, 236, 24, 2, 73, 69, 7, 166, 142, 25, 79, 3, - 85, 5, 207, 168, 18, 69, 14, 56, 7, 65, 80, 89, 69, 79, 85, 78, 106, 72, + 73, 69, 85, 76, 254, 1, 84, 90, 83, 246, 2, 87, 50, 89, 150, 1, 72, 202, + 213, 24, 79, 163, 64, 85, 9, 156, 13, 3, 82, 65, 69, 207, 148, 25, 69, 4, + 22, 72, 207, 8, 73, 2, 241, 254, 24, 2, 73, 69, 7, 138, 161, 25, 79, 3, + 85, 5, 179, 173, 18, 69, 14, 56, 7, 65, 80, 89, 69, 79, 85, 78, 106, 72, 155, 3, 73, 8, 30, 80, 30, 83, 231, 3, 77, 4, 190, 4, 72, 215, 3, 73, 2, - 25, 4, 83, 65, 78, 71, 2, 207, 7, 80, 2, 213, 60, 2, 73, 69, 9, 11, 45, + 25, 4, 83, 65, 78, 71, 2, 207, 7, 80, 2, 241, 60, 2, 73, 69, 9, 11, 45, 6, 22, 80, 247, 9, 83, 4, 142, 7, 73, 207, 2, 65, 13, 11, 45, 10, 234, 5, 67, 146, 1, 84, 242, 1, 72, 62, 80, 39, 83, 20, 48, 4, 73, 69, 85, 80, 170, 2, 72, 163, 6, 65, 17, 11, 45, 14, 42, 83, 186, 2, 84, 146, 2, 67, 43, 75, 6, 21, 3, 73, 79, 83, 7, 11, 45, 4, 202, 4, 75, 107, 84, 27, 11, 45, 24, 68, 2, 75, 73, 34, 77, 34, 80, 106, 84, 54, 89, 222, 4, 72, 99, - 83, 4, 149, 1, 4, 89, 69, 79, 75, 2, 11, 73, 2, 255, 223, 23, 69, 8, 30, - 72, 34, 73, 131, 6, 65, 2, 193, 228, 18, 3, 73, 69, 85, 4, 21, 3, 69, 85, - 80, 5, 243, 5, 45, 4, 22, 72, 151, 3, 73, 2, 225, 230, 21, 2, 73, 69, 2, + 83, 4, 149, 1, 4, 89, 69, 79, 75, 2, 11, 73, 2, 207, 240, 23, 69, 8, 30, + 72, 34, 73, 131, 6, 65, 2, 197, 232, 18, 3, 73, 69, 85, 4, 21, 3, 69, 85, + 80, 5, 243, 5, 45, 4, 22, 72, 151, 3, 73, 2, 241, 245, 21, 2, 73, 69, 2, 17, 2, 69, 79, 2, 167, 4, 82, 28, 44, 3, 73, 79, 83, 57, 4, 83, 65, 78, 71, 13, 11, 45, 10, 122, 67, 42, 75, 42, 78, 34, 80, 35, 84, 16, 78, 67, - 42, 75, 42, 78, 34, 80, 34, 84, 242, 1, 72, 98, 83, 223, 156, 18, 73, 2, - 11, 73, 2, 249, 221, 23, 2, 69, 85, 2, 11, 73, 2, 237, 219, 24, 2, 89, - 69, 2, 11, 73, 2, 247, 132, 24, 69, 2, 11, 73, 2, 147, 143, 24, 69, 2, - 11, 73, 2, 11, 75, 2, 139, 139, 24, 69, 10, 230, 190, 22, 69, 194, 133, - 2, 65, 163, 64, 73, 34, 58, 69, 206, 1, 79, 62, 85, 182, 193, 24, 65, + 42, 75, 42, 78, 34, 80, 34, 84, 242, 1, 72, 98, 83, 195, 161, 18, 73, 2, + 11, 73, 2, 201, 238, 23, 2, 69, 85, 2, 11, 73, 2, 209, 238, 24, 2, 89, + 69, 2, 11, 73, 2, 219, 151, 24, 69, 2, 11, 73, 2, 247, 161, 24, 69, 2, + 11, 73, 2, 11, 75, 2, 239, 157, 24, 69, 10, 250, 205, 22, 69, 146, 137, + 2, 65, 163, 64, 73, 34, 58, 69, 206, 1, 79, 62, 85, 154, 212, 24, 65, 163, 64, 73, 13, 42, 79, 73, 6, 83, 73, 69, 85, 78, 71, 5, 11, 82, 2, 17, - 2, 73, 78, 2, 11, 72, 2, 161, 235, 9, 2, 73, 69, 7, 11, 45, 4, 18, 80, - 39, 83, 2, 11, 65, 2, 11, 78, 2, 11, 83, 2, 223, 146, 13, 73, 9, 11, 45, - 6, 26, 89, 235, 129, 25, 73, 4, 199, 193, 24, 65, 9, 11, 45, 6, 26, 89, - 175, 129, 25, 73, 4, 203, 187, 22, 69, 24, 210, 136, 11, 84, 162, 136, - 12, 70, 30, 83, 210, 86, 78, 14, 79, 223, 110, 69, 100, 156, 1, 7, 76, + 2, 73, 78, 2, 11, 72, 2, 157, 239, 9, 2, 73, 69, 7, 11, 45, 4, 18, 80, + 39, 83, 2, 11, 65, 2, 11, 78, 2, 11, 83, 2, 155, 147, 13, 73, 9, 11, 45, + 6, 26, 89, 207, 148, 25, 73, 4, 171, 212, 24, 65, 9, 11, 45, 6, 26, 89, + 147, 148, 25, 73, 4, 223, 202, 22, 69, 24, 138, 136, 11, 84, 230, 152, + 12, 70, 30, 83, 182, 87, 78, 14, 79, 227, 112, 69, 100, 156, 1, 7, 76, 69, 84, 84, 69, 82, 32, 196, 2, 5, 77, 65, 82, 75, 32, 72, 5, 83, 73, 71, - 78, 32, 132, 159, 2, 6, 86, 79, 87, 69, 76, 32, 255, 235, 20, 68, 58, - 202, 1, 68, 34, 75, 254, 241, 20, 84, 154, 50, 82, 130, 223, 1, 78, 238, - 178, 1, 83, 138, 69, 66, 2, 67, 2, 70, 2, 71, 2, 72, 2, 74, 2, 76, 2, 77, - 2, 80, 2, 86, 2, 87, 2, 89, 2, 90, 187, 2, 65, 4, 166, 251, 24, 68, 187, - 2, 65, 8, 56, 5, 73, 78, 78, 65, 32, 206, 250, 24, 72, 187, 2, 65, 4, - 202, 250, 24, 87, 3, 89, 4, 248, 197, 21, 6, 78, 65, 32, 75, 72, 79, 153, - 183, 2, 3, 83, 65, 75, 8, 52, 2, 84, 65, 217, 238, 22, 5, 72, 65, 82, 66, - 65, 6, 42, 72, 158, 145, 20, 83, 235, 231, 4, 78, 2, 163, 217, 24, 65, - 42, 62, 76, 144, 239, 18, 6, 83, 73, 71, 78, 32, 80, 247, 1, 86, 36, 33, - 6, 69, 84, 84, 69, 82, 32, 36, 146, 137, 21, 78, 250, 238, 3, 66, 2, 68, + 78, 32, 224, 159, 2, 6, 86, 79, 87, 69, 76, 32, 159, 251, 20, 68, 58, + 202, 1, 68, 34, 75, 246, 251, 20, 84, 178, 55, 82, 238, 223, 1, 78, 214, + 181, 1, 83, 138, 69, 66, 2, 67, 2, 70, 2, 71, 2, 72, 2, 74, 2, 76, 2, 77, + 2, 80, 2, 86, 2, 87, 2, 89, 2, 90, 187, 2, 65, 4, 138, 142, 25, 68, 187, + 2, 65, 8, 56, 5, 73, 78, 78, 65, 32, 178, 141, 25, 72, 187, 2, 65, 4, + 174, 141, 25, 87, 3, 89, 4, 136, 213, 21, 6, 78, 65, 32, 75, 72, 79, 237, + 186, 2, 3, 83, 65, 75, 8, 52, 2, 84, 65, 213, 254, 22, 5, 72, 65, 82, 66, + 65, 6, 42, 72, 174, 155, 20, 83, 191, 240, 4, 78, 2, 135, 236, 24, 65, + 42, 62, 76, 152, 243, 18, 6, 83, 73, 71, 78, 32, 80, 247, 1, 86, 36, 33, + 6, 69, 84, 84, 69, 82, 32, 36, 162, 151, 21, 78, 206, 243, 3, 66, 2, 68, 2, 71, 2, 72, 2, 75, 2, 76, 2, 77, 2, 80, 2, 82, 2, 83, 2, 84, 2, 87, 2, - 89, 186, 2, 65, 2, 73, 3, 85, 2, 139, 235, 23, 69, 4, 130, 180, 23, 68, - 143, 197, 1, 80, 54, 52, 5, 67, 72, 73, 78, 71, 41, 4, 82, 65, 78, 32, 2, - 17, 2, 32, 67, 2, 163, 200, 23, 72, 52, 52, 7, 76, 69, 84, 84, 69, 82, - 32, 175, 179, 6, 78, 42, 218, 1, 65, 234, 173, 11, 90, 162, 47, 84, 150, - 119, 76, 50, 81, 60, 6, 68, 65, 76, 69, 84, 72, 146, 165, 4, 71, 122, 83, - 66, 89, 158, 208, 1, 72, 234, 5, 75, 198, 75, 66, 178, 216, 4, 78, 134, - 2, 87, 218, 103, 80, 171, 4, 77, 4, 130, 250, 16, 76, 151, 172, 7, 89, - 172, 9, 230, 1, 65, 240, 32, 5, 66, 82, 69, 87, 32, 210, 32, 76, 200, 1, - 16, 78, 84, 65, 73, 71, 65, 78, 65, 32, 76, 69, 84, 84, 69, 82, 32, 174, - 12, 82, 120, 11, 88, 65, 71, 82, 65, 77, 32, 70, 79, 82, 32, 221, 162, - 24, 4, 68, 71, 69, 72, 214, 1, 42, 68, 98, 82, 201, 1, 3, 86, 89, 32, 6, - 44, 5, 83, 84, 79, 78, 69, 187, 200, 23, 80, 5, 217, 225, 2, 7, 32, 71, - 82, 65, 86, 69, 89, 12, 32, 2, 84, 32, 139, 151, 17, 45, 10, 60, 5, 87, - 73, 84, 72, 32, 246, 163, 12, 68, 155, 143, 9, 72, 6, 76, 9, 84, 73, 80, - 32, 79, 78, 32, 84, 72, 174, 242, 12, 82, 191, 228, 10, 65, 2, 223, 190, - 24, 69, 196, 1, 134, 2, 65, 202, 1, 66, 230, 2, 67, 154, 3, 68, 162, 1, - 69, 186, 3, 70, 94, 72, 62, 76, 222, 1, 77, 110, 79, 162, 1, 82, 142, 2, - 83, 228, 1, 3, 78, 79, 82, 198, 1, 84, 128, 2, 2, 85, 80, 174, 1, 87, - 138, 140, 14, 73, 254, 238, 3, 80, 210, 131, 4, 86, 251, 77, 71, 12, 108, - 17, 82, 82, 79, 87, 32, 83, 72, 65, 70, 84, 32, 87, 73, 68, 84, 72, 32, - 182, 158, 18, 77, 155, 188, 4, 83, 8, 36, 3, 79, 78, 69, 223, 141, 23, - 84, 7, 11, 32, 4, 174, 238, 13, 84, 135, 252, 8, 72, 14, 48, 4, 65, 76, - 76, 79, 21, 4, 76, 65, 67, 75, 2, 187, 167, 5, 84, 12, 30, 32, 153, 1, 2, - 45, 70, 6, 52, 7, 67, 85, 82, 86, 69, 68, 32, 247, 228, 23, 72, 4, 40, 4, - 68, 79, 87, 78, 1, 2, 85, 80, 2, 213, 203, 23, 8, 87, 65, 82, 68, 83, 32, - 65, 78, 6, 45, 9, 69, 65, 84, 72, 69, 82, 69, 68, 32, 6, 234, 211, 13, - 83, 218, 168, 3, 78, 211, 206, 6, 82, 14, 116, 2, 72, 69, 52, 5, 73, 82, - 67, 76, 69, 149, 220, 17, 14, 79, 78, 67, 65, 86, 69, 45, 80, 79, 73, 78, - 84, 69, 68, 4, 216, 203, 20, 4, 86, 82, 79, 78, 239, 218, 2, 67, 9, 64, - 6, 32, 87, 73, 84, 72, 32, 133, 212, 22, 4, 68, 32, 83, 65, 4, 52, 7, 83, - 84, 82, 79, 75, 69, 32, 199, 254, 4, 67, 2, 29, 5, 65, 78, 68, 32, 84, 2, - 11, 87, 2, 11, 79, 2, 21, 3, 32, 68, 79, 2, 11, 84, 2, 207, 234, 23, 83, - 14, 52, 7, 65, 83, 72, 69, 68, 32, 84, 18, 73, 31, 79, 2, 175, 16, 82, 2, - 137, 144, 19, 2, 86, 73, 10, 192, 16, 3, 87, 78, 87, 198, 139, 14, 85, - 187, 180, 4, 76, 16, 120, 5, 73, 71, 72, 84, 32, 156, 2, 16, 88, 67, 76, - 65, 77, 65, 84, 73, 79, 78, 32, 77, 65, 82, 75, 32, 147, 205, 18, 81, 10, - 40, 2, 80, 79, 126, 84, 231, 207, 22, 83, 6, 33, 6, 73, 78, 84, 69, 68, - 32, 6, 202, 186, 16, 80, 196, 147, 6, 11, 82, 69, 67, 84, 73, 76, 73, 78, - 69, 65, 82, 23, 66, 2, 141, 208, 22, 24, 69, 65, 82, 68, 82, 79, 80, 45, - 83, 80, 79, 75, 69, 68, 32, 80, 82, 79, 80, 69, 76, 76, 69, 82, 4, 246, - 207, 23, 83, 199, 79, 79, 6, 44, 5, 79, 85, 82, 32, 66, 139, 254, 4, 73, - 2, 209, 131, 11, 6, 65, 76, 76, 79, 79, 78, 4, 242, 186, 17, 79, 169, - 221, 5, 6, 69, 65, 82, 84, 32, 69, 20, 74, 65, 44, 3, 69, 70, 84, 28, 2, - 79, 87, 149, 249, 4, 3, 73, 71, 65, 4, 172, 207, 21, 2, 82, 71, 147, 208, - 1, 84, 8, 254, 3, 45, 195, 6, 87, 6, 26, 32, 223, 143, 10, 69, 4, 154, - 150, 14, 68, 25, 4, 83, 73, 78, 71, 4, 56, 8, 85, 76, 84, 73, 80, 76, 73, - 67, 235, 176, 21, 73, 2, 25, 4, 65, 84, 73, 79, 2, 207, 153, 5, 78, 6, - 132, 140, 6, 9, 80, 69, 78, 32, 67, 69, 78, 84, 82, 144, 175, 10, 13, 86, - 65, 76, 32, 87, 73, 84, 72, 32, 79, 86, 65, 76, 153, 141, 6, 5, 85, 84, - 76, 73, 78, 12, 76, 4, 73, 71, 72, 84, 245, 188, 23, 9, 79, 85, 78, 68, - 45, 84, 73, 80, 80, 10, 62, 45, 109, 11, 87, 65, 82, 68, 83, 32, 65, 82, - 82, 79, 87, 4, 69, 15, 80, 79, 73, 78, 84, 73, 78, 71, 32, 65, 78, 71, - 76, 69, 32, 4, 222, 229, 6, 66, 147, 173, 7, 81, 7, 139, 6, 32, 26, 106, - 65, 78, 73, 44, 2, 79, 85, 160, 154, 14, 7, 67, 82, 73, 80, 84, 32, 76, - 157, 135, 1, 3, 80, 65, 82, 4, 236, 145, 14, 10, 78, 83, 45, 83, 69, 82, - 73, 70, 32, 73, 171, 180, 8, 76, 8, 216, 143, 14, 2, 78, 71, 179, 179, 8, - 88, 10, 21, 3, 84, 72, 32, 10, 60, 5, 69, 65, 83, 84, 32, 29, 6, 87, 69, - 83, 84, 32, 80, 6, 26, 80, 203, 190, 23, 65, 4, 41, 8, 79, 73, 78, 84, - 73, 78, 71, 32, 4, 242, 237, 16, 86, 139, 191, 6, 66, 12, 88, 9, 69, 65, - 82, 68, 82, 79, 80, 45, 83, 130, 1, 82, 241, 225, 6, 4, 87, 69, 76, 86, - 6, 64, 6, 80, 79, 75, 69, 68, 32, 241, 182, 23, 4, 72, 65, 78, 75, 4, - 164, 195, 22, 8, 80, 73, 78, 87, 72, 69, 69, 76, 27, 65, 2, 193, 3, 5, - 73, 65, 78, 71, 76, 6, 26, 87, 203, 133, 10, 80, 4, 53, 11, 65, 82, 68, - 83, 32, 65, 82, 82, 79, 87, 32, 4, 29, 5, 87, 73, 84, 72, 32, 4, 192, - 251, 19, 5, 76, 65, 82, 71, 69, 203, 238, 1, 69, 12, 76, 5, 72, 73, 84, - 69, 32, 164, 1, 2, 73, 68, 237, 236, 22, 3, 69, 68, 71, 8, 64, 6, 83, 81, - 85, 65, 82, 69, 166, 214, 17, 68, 215, 171, 5, 67, 5, 173, 161, 23, 19, - 32, 67, 79, 78, 84, 65, 73, 78, 73, 78, 71, 32, 66, 76, 65, 67, 75, 32, - 86, 2, 157, 232, 20, 2, 69, 45, 140, 2, 112, 7, 65, 67, 67, 69, 78, 84, - 32, 138, 8, 76, 164, 14, 5, 77, 65, 82, 75, 32, 114, 80, 217, 157, 21, 2, - 89, 79, 60, 128, 2, 9, 65, 84, 78, 65, 72, 32, 72, 65, 70, 22, 68, 36, 3, - 71, 69, 82, 74, 77, 124, 2, 80, 65, 28, 4, 69, 84, 78, 65, 20, 2, 81, 65, - 58, 83, 58, 84, 156, 1, 2, 89, 69, 78, 90, 196, 165, 8, 3, 82, 69, 86, - 142, 129, 15, 79, 225, 146, 1, 3, 73, 76, 85, 2, 243, 157, 18, 85, 4, - 230, 128, 19, 69, 179, 140, 5, 65, 6, 32, 3, 69, 83, 72, 179, 28, 83, 5, - 129, 221, 6, 4, 32, 77, 85, 81, 8, 84, 5, 69, 82, 75, 72, 65, 164, 238, - 17, 2, 85, 78, 249, 45, 5, 65, 72, 65, 80, 65, 5, 209, 219, 19, 4, 32, - 75, 69, 70, 4, 26, 83, 251, 143, 24, 90, 2, 151, 169, 24, 72, 4, 128, - 137, 24, 6, 82, 78, 69, 89, 32, 80, 191, 35, 68, 4, 182, 24, 69, 193, - 213, 22, 6, 72, 65, 76, 83, 72, 69, 8, 38, 69, 249, 209, 22, 3, 73, 80, - 69, 6, 48, 6, 76, 73, 83, 72, 65, 32, 207, 224, 11, 86, 4, 240, 155, 22, - 3, 81, 69, 84, 249, 141, 2, 4, 71, 69, 68, 79, 4, 128, 205, 8, 10, 82, - 65, 72, 32, 66, 69, 78, 32, 89, 79, 255, 221, 2, 84, 8, 34, 65, 237, 145, - 24, 2, 73, 78, 6, 40, 4, 81, 69, 70, 32, 183, 255, 5, 82, 4, 154, 135, - 11, 81, 157, 176, 12, 3, 71, 65, 68, 150, 1, 76, 6, 69, 84, 84, 69, 82, - 32, 201, 11, 8, 73, 71, 65, 84, 85, 82, 69, 32, 140, 1, 134, 3, 65, 204, - 1, 3, 66, 69, 84, 0, 3, 75, 65, 70, 0, 2, 80, 69, 68, 6, 70, 73, 78, 65, - 76, 32, 92, 2, 81, 79, 16, 2, 72, 69, 52, 2, 78, 85, 0, 4, 90, 65, 89, - 73, 18, 83, 48, 3, 82, 69, 83, 170, 1, 84, 52, 4, 68, 65, 76, 69, 12, 5, - 71, 73, 77, 69, 76, 0, 5, 76, 65, 77, 69, 68, 0, 3, 77, 69, 77, 48, 3, - 86, 65, 86, 80, 5, 87, 73, 68, 69, 32, 153, 1, 3, 89, 79, 68, 14, 26, 76, - 171, 198, 23, 89, 12, 64, 2, 69, 70, 73, 10, 84, 69, 82, 78, 65, 84, 73, - 86, 69, 32, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 130, 13, 77, 130, 1, 80, - 35, 81, 4, 242, 201, 16, 65, 251, 160, 1, 80, 7, 33, 6, 32, 87, 73, 84, - 72, 32, 4, 142, 15, 82, 199, 225, 13, 68, 14, 88, 2, 75, 65, 236, 2, 2, - 80, 69, 220, 153, 1, 2, 84, 83, 182, 166, 22, 78, 135, 110, 77, 4, 235, - 2, 70, 7, 244, 10, 5, 32, 87, 73, 84, 72, 167, 184, 24, 84, 4, 167, 2, - 78, 16, 44, 4, 65, 77, 69, 75, 17, 3, 72, 73, 78, 4, 231, 1, 72, 13, 33, - 6, 32, 87, 73, 84, 72, 32, 10, 40, 6, 68, 65, 71, 69, 83, 72, 39, 83, 7, - 33, 6, 32, 65, 78, 68, 32, 83, 4, 254, 254, 5, 72, 203, 192, 2, 73, 12, - 50, 69, 12, 2, 65, 86, 1, 4, 83, 65, 68, 73, 4, 11, 84, 5, 233, 236, 13, - 7, 32, 87, 73, 84, 72, 32, 68, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 148, - 253, 1, 2, 72, 79, 131, 239, 11, 68, 16, 174, 2, 76, 202, 203, 8, 65, - 210, 196, 2, 84, 186, 217, 2, 82, 160, 244, 8, 2, 68, 65, 242, 93, 75, - 222, 106, 72, 169, 4, 7, 70, 73, 78, 65, 76, 32, 77, 7, 33, 6, 32, 87, - 73, 84, 72, 32, 4, 176, 7, 2, 72, 73, 255, 226, 13, 68, 10, 72, 6, 65, - 76, 69, 70, 32, 76, 29, 8, 89, 73, 68, 68, 73, 83, 72, 32, 2, 193, 177, - 19, 2, 65, 77, 8, 120, 7, 68, 79, 85, 66, 76, 69, 32, 232, 4, 9, 89, 79, - 68, 32, 89, 79, 68, 32, 80, 185, 128, 21, 5, 86, 65, 86, 32, 89, 4, 254, - 142, 11, 86, 163, 246, 9, 89, 6, 80, 3, 76, 79, 87, 0, 3, 85, 80, 80, - 237, 232, 22, 6, 77, 65, 83, 79, 82, 65, 2, 233, 169, 23, 2, 69, 82, 50, - 84, 5, 79, 73, 78, 84, 32, 229, 5, 11, 85, 78, 67, 84, 85, 65, 84, 73, - 79, 78, 32, 38, 228, 1, 9, 68, 65, 71, 69, 83, 72, 32, 79, 82, 46, 72, - 106, 80, 166, 1, 81, 86, 82, 22, 83, 176, 209, 9, 2, 84, 83, 132, 142, - 11, 17, 74, 85, 68, 69, 79, 45, 83, 80, 65, 78, 73, 83, 72, 32, 86, 65, - 82, 233, 143, 3, 3, 77, 69, 84, 2, 17, 2, 32, 77, 2, 201, 1, 2, 65, 80, - 12, 60, 5, 65, 84, 65, 70, 32, 106, 73, 33, 4, 79, 76, 65, 77, 6, 38, 80, - 34, 81, 145, 2, 2, 83, 69, 2, 11, 65, 2, 131, 215, 17, 84, 2, 193, 162, - 23, 3, 65, 77, 65, 2, 11, 82, 2, 143, 230, 13, 73, 5, 181, 137, 11, 12, - 32, 72, 65, 83, 69, 82, 32, 70, 79, 82, 32, 86, 6, 52, 5, 65, 77, 65, 84, - 83, 189, 191, 11, 2, 85, 66, 5, 165, 242, 10, 2, 32, 81, 2, 167, 225, 6, - 65, 8, 34, 69, 22, 72, 243, 178, 8, 73, 2, 239, 161, 23, 71, 4, 238, 178, - 8, 73, 199, 209, 7, 69, 12, 152, 1, 3, 71, 69, 82, 60, 3, 80, 65, 83, 20, - 7, 83, 79, 70, 32, 80, 65, 83, 212, 249, 21, 7, 78, 85, 78, 32, 72, 65, - 70, 185, 183, 1, 3, 77, 65, 81, 4, 26, 83, 211, 202, 22, 69, 2, 157, 176, - 23, 3, 72, 65, 89, 2, 155, 226, 13, 69, 2, 135, 226, 13, 85, 8, 114, 77, - 160, 149, 12, 15, 76, 83, 67, 72, 82, 69, 73, 66, 69, 82, 32, 80, 65, 85, - 83, 185, 223, 5, 3, 73, 67, 79, 4, 176, 222, 5, 12, 69, 84, 32, 87, 73, - 84, 72, 32, 87, 72, 73, 84, 231, 191, 17, 32, 188, 4, 164, 1, 2, 65, 45, - 50, 72, 70, 75, 230, 1, 77, 114, 78, 146, 2, 82, 66, 83, 154, 1, 84, 226, - 1, 87, 62, 89, 110, 69, 134, 241, 8, 85, 190, 170, 5, 79, 143, 191, 3, - 73, 8, 190, 144, 24, 87, 246, 30, 49, 2, 50, 3, 51, 72, 166, 6, 79, 146, - 191, 8, 65, 146, 224, 5, 85, 158, 115, 69, 3, 73, 74, 72, 2, 65, 45, 104, - 2, 79, 45, 178, 4, 73, 226, 3, 69, 223, 142, 15, 85, 24, 210, 234, 11, - 49, 206, 172, 12, 75, 214, 22, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, - 56, 3, 57, 8, 178, 153, 24, 75, 218, 19, 49, 2, 50, 3, 51, 54, 68, 2, 69, - 45, 154, 7, 79, 222, 142, 15, 65, 2, 73, 243, 203, 2, 85, 6, 218, 169, - 24, 77, 186, 2, 49, 3, 50, 68, 116, 2, 69, 45, 72, 2, 73, 45, 246, 2, 65, - 194, 243, 8, 79, 190, 170, 5, 85, 169, 136, 6, 6, 45, 77, 85, 45, 77, 79, - 14, 254, 139, 24, 75, 246, 30, 49, 2, 50, 2, 51, 2, 52, 2, 53, 3, 54, 16, - 214, 147, 24, 84, 214, 22, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 55, - 54, 222, 4, 79, 2, 85, 222, 142, 15, 73, 242, 203, 2, 65, 3, 69, 68, 62, - 65, 2, 85, 226, 3, 73, 134, 241, 8, 69, 219, 157, 6, 79, 16, 11, 45, 16, - 206, 168, 24, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 64, - 74, 69, 20, 2, 79, 45, 72, 2, 85, 45, 190, 144, 15, 73, 243, 203, 2, 65, - 18, 139, 240, 18, 45, 14, 234, 164, 24, 82, 186, 2, 49, 2, 50, 2, 51, 2, - 52, 2, 53, 3, 54, 10, 230, 135, 24, 84, 246, 30, 49, 2, 50, 2, 51, 3, 52, - 42, 170, 242, 8, 65, 2, 73, 218, 157, 6, 79, 243, 203, 2, 69, 32, 40, 2, - 65, 45, 66, 79, 207, 218, 17, 85, 12, 198, 134, 24, 89, 246, 30, 49, 2, - 50, 2, 51, 2, 52, 3, 53, 12, 11, 45, 12, 238, 164, 24, 49, 2, 50, 2, 51, - 2, 52, 2, 53, 3, 54, 4, 140, 167, 8, 21, 77, 73, 84, 73, 65, 78, 32, 67, - 79, 78, 74, 85, 71, 65, 84, 69, 32, 77, 65, 84, 82, 167, 253, 15, 66, - 128, 1, 210, 2, 65, 110, 66, 144, 1, 2, 67, 79, 94, 68, 198, 2, 70, 66, - 71, 40, 4, 72, 79, 76, 68, 164, 1, 2, 73, 78, 116, 2, 77, 79, 58, 79, - 122, 80, 100, 2, 82, 69, 62, 83, 238, 1, 84, 210, 5, 87, 212, 155, 15, 4, - 76, 73, 77, 73, 220, 180, 5, 11, 89, 79, 85, 84, 72, 70, 85, 76, 32, 70, - 79, 181, 173, 3, 9, 69, 78, 84, 72, 85, 83, 73, 65, 83, 6, 76, 3, 80, 80, - 82, 124, 4, 70, 84, 69, 82, 189, 151, 19, 4, 66, 85, 78, 68, 2, 233, 253, - 23, 2, 79, 65, 6, 92, 5, 69, 70, 79, 82, 69, 128, 215, 3, 6, 73, 84, 73, - 78, 71, 32, 1, 4, 82, 69, 65, 75, 2, 197, 206, 23, 7, 32, 67, 79, 77, 80, - 76, 69, 6, 26, 78, 183, 142, 19, 77, 4, 228, 187, 20, 4, 84, 69, 77, 80, - 245, 211, 2, 3, 70, 76, 73, 14, 110, 69, 78, 73, 206, 146, 19, 85, 129, - 241, 2, 15, 65, 82, 75, 69, 78, 73, 78, 71, 32, 79, 70, 32, 84, 72, 69, - 6, 222, 142, 19, 67, 192, 6, 2, 76, 73, 201, 196, 4, 5, 86, 69, 76, 79, - 80, 4, 252, 132, 19, 21, 70, 70, 73, 67, 85, 76, 84, 89, 32, 65, 84, 32, - 84, 72, 69, 32, 66, 69, 71, 73, 78, 177, 248, 2, 4, 83, 80, 69, 82, 4, - 220, 137, 19, 2, 79, 76, 217, 188, 4, 5, 69, 76, 76, 79, 87, 12, 36, 5, - 65, 84, 72, 69, 82, 35, 82, 2, 225, 134, 15, 3, 73, 78, 71, 10, 40, 4, - 69, 65, 84, 32, 155, 221, 23, 65, 8, 22, 80, 211, 5, 84, 6, 22, 79, 147, - 5, 82, 4, 172, 2, 2, 83, 83, 203, 217, 23, 87, 8, 50, 78, 206, 138, 19, - 67, 217, 5, 3, 70, 76, 85, 4, 160, 144, 19, 2, 79, 67, 205, 231, 1, 5, - 69, 82, 32, 84, 82, 4, 204, 171, 22, 3, 68, 69, 83, 189, 61, 3, 85, 84, - 72, 6, 26, 66, 37, 2, 80, 80, 2, 149, 199, 23, 4, 83, 84, 82, 85, 4, 26, - 82, 239, 197, 23, 79, 2, 145, 248, 21, 2, 69, 83, 6, 196, 139, 15, 9, 85, - 83, 72, 73, 78, 71, 32, 85, 80, 204, 137, 2, 3, 82, 79, 71, 255, 196, 6, - 69, 6, 26, 84, 207, 250, 18, 86, 4, 254, 139, 19, 85, 223, 57, 82, 8, - 132, 1, 5, 77, 65, 76, 76, 32, 236, 156, 21, 6, 84, 65, 78, 68, 83, 84, - 233, 240, 1, 11, 80, 76, 73, 84, 84, 73, 78, 71, 32, 65, 80, 4, 24, 2, - 80, 82, 43, 84, 2, 225, 140, 19, 5, 69, 80, 79, 78, 68, 2, 11, 65, 2, - 155, 195, 23, 77, 30, 36, 3, 72, 69, 32, 175, 219, 17, 82, 28, 186, 2, - 65, 130, 1, 67, 132, 1, 3, 70, 65, 77, 20, 9, 82, 69, 67, 69, 80, 84, 73, - 86, 69, 30, 87, 172, 22, 12, 77, 65, 82, 82, 89, 73, 78, 71, 32, 77, 65, - 73, 168, 147, 5, 6, 71, 69, 78, 84, 76, 69, 248, 221, 10, 13, 75, 69, 69, - 80, 73, 78, 71, 32, 83, 84, 73, 76, 76, 201, 233, 3, 7, 74, 79, 89, 79, - 85, 83, 32, 6, 58, 82, 133, 181, 11, 8, 66, 89, 83, 77, 65, 76, 32, 87, - 4, 248, 168, 20, 8, 79, 85, 83, 73, 78, 71, 32, 84, 171, 214, 3, 77, 6, - 128, 131, 1, 2, 65, 85, 212, 164, 19, 9, 82, 69, 65, 84, 73, 86, 69, 32, - 72, 133, 212, 1, 9, 76, 73, 78, 71, 73, 78, 71, 32, 70, 2, 243, 235, 23, - 73, 2, 157, 166, 20, 2, 32, 69, 4, 178, 135, 22, 69, 213, 201, 1, 5, 65, - 78, 68, 69, 82, 4, 202, 231, 6, 65, 141, 166, 6, 14, 79, 82, 75, 32, 79, - 78, 32, 84, 72, 69, 32, 68, 69, 67, 222, 1, 236, 1, 2, 71, 72, 180, 2, 7, - 82, 65, 71, 65, 78, 65, 32, 212, 4, 7, 83, 84, 79, 82, 73, 67, 32, 168, - 186, 15, 7, 78, 68, 85, 32, 84, 69, 77, 240, 33, 4, 75, 73, 78, 71, 162, - 250, 1, 66, 177, 168, 4, 8, 80, 80, 79, 80, 79, 84, 65, 77, 12, 18, 32, - 119, 45, 6, 204, 135, 5, 2, 66, 82, 220, 231, 16, 6, 86, 79, 76, 84, 65, - 71, 229, 48, 9, 79, 67, 84, 69, 84, 32, 80, 82, 69, 6, 92, 11, 83, 80, - 69, 69, 68, 32, 84, 82, 65, 73, 78, 157, 200, 5, 6, 72, 69, 69, 76, 69, - 68, 5, 129, 186, 11, 14, 32, 87, 73, 84, 72, 32, 66, 85, 76, 76, 69, 84, - 32, 78, 200, 1, 112, 9, 68, 73, 71, 82, 65, 80, 72, 32, 89, 20, 7, 76, - 69, 84, 84, 69, 82, 32, 222, 172, 1, 86, 139, 185, 20, 73, 2, 243, 170, - 17, 79, 194, 1, 194, 1, 65, 74, 83, 206, 163, 1, 66, 162, 3, 78, 150, 2, - 68, 2, 71, 2, 72, 2, 75, 2, 77, 2, 80, 2, 82, 2, 84, 2, 90, 126, 87, 46, - 89, 142, 183, 22, 86, 234, 36, 69, 2, 73, 2, 79, 3, 85, 7, 37, 7, 82, 67, - 72, 65, 73, 67, 32, 4, 210, 225, 23, 87, 151, 14, 89, 42, 76, 5, 77, 65, - 76, 76, 32, 206, 133, 24, 65, 2, 69, 2, 73, 2, 79, 3, 85, 32, 170, 169, - 1, 87, 46, 89, 170, 173, 5, 75, 230, 137, 17, 84, 234, 36, 65, 2, 69, 2, - 73, 2, 79, 3, 85, 2, 139, 179, 8, 83, 108, 166, 1, 76, 52, 3, 78, 69, 89, - 46, 82, 146, 7, 84, 138, 1, 85, 154, 188, 16, 83, 246, 213, 2, 67, 156, - 192, 3, 6, 77, 79, 84, 72, 69, 84, 242, 164, 1, 79, 155, 3, 80, 6, 176, - 140, 16, 4, 76, 79, 87, 32, 247, 246, 7, 69, 4, 186, 147, 22, 66, 129, - 159, 1, 2, 32, 80, 66, 60, 8, 73, 90, 79, 78, 84, 65, 76, 32, 153, 6, 2, - 83, 69, 60, 218, 1, 66, 110, 76, 152, 1, 17, 79, 78, 69, 32, 69, 73, 71, - 72, 84, 72, 32, 66, 76, 79, 67, 75, 45, 88, 10, 83, 67, 65, 78, 32, 76, - 73, 78, 69, 45, 46, 84, 162, 217, 21, 67, 42, 69, 238, 6, 77, 150, 1, 82, - 247, 2, 90, 6, 44, 5, 76, 65, 67, 75, 32, 163, 199, 23, 65, 4, 38, 79, - 169, 199, 22, 3, 72, 69, 88, 2, 155, 199, 22, 67, 10, 40, 4, 73, 78, 69, - 32, 135, 223, 21, 65, 8, 44, 5, 87, 73, 84, 72, 32, 171, 223, 21, 69, 6, - 26, 84, 219, 224, 21, 70, 4, 250, 224, 21, 72, 171, 90, 73, 14, 136, 225, - 16, 3, 49, 51, 53, 158, 157, 7, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 55, 8, - 206, 253, 23, 49, 2, 51, 2, 55, 3, 57, 10, 32, 2, 65, 66, 223, 227, 21, - 82, 8, 26, 85, 223, 226, 21, 32, 6, 33, 6, 76, 65, 84, 73, 79, 78, 7, 11, - 32, 4, 188, 197, 9, 8, 87, 73, 84, 72, 32, 74, 85, 83, 223, 129, 14, 83, - 7, 11, 32, 4, 180, 191, 7, 2, 82, 65, 167, 255, 15, 70, 10, 26, 32, 235, - 241, 22, 69, 8, 86, 80, 240, 243, 18, 5, 66, 69, 86, 69, 82, 160, 80, 3, - 83, 80, 82, 215, 181, 4, 68, 2, 167, 230, 14, 69, 12, 48, 6, 82, 71, 76, - 65, 83, 83, 77, 2, 83, 69, 5, 249, 170, 20, 14, 32, 87, 73, 84, 72, 32, - 70, 76, 79, 87, 73, 78, 71, 32, 9, 11, 32, 6, 88, 8, 87, 73, 84, 72, 32, - 71, 65, 82, 217, 241, 21, 8, 66, 85, 73, 76, 68, 73, 78, 71, 2, 223, 164, - 22, 68, 7, 178, 248, 23, 74, 3, 83, 8, 106, 83, 248, 227, 22, 11, 78, 68, - 82, 69, 68, 32, 80, 79, 73, 78, 84, 148, 11, 2, 71, 71, 163, 136, 1, 84, - 2, 211, 230, 22, 72, 18, 90, 80, 224, 1, 5, 83, 84, 69, 82, 69, 236, 238, - 16, 2, 71, 73, 229, 130, 5, 2, 65, 67, 12, 60, 3, 72, 69, 78, 245, 158, - 4, 6, 79, 68, 73, 65, 83, 84, 11, 34, 32, 82, 65, 231, 238, 19, 45, 4, - 26, 87, 135, 150, 22, 66, 2, 21, 3, 73, 84, 72, 2, 185, 188, 3, 2, 32, - 68, 2, 177, 220, 12, 6, 84, 73, 79, 78, 32, 80, 2, 165, 225, 22, 2, 83, - 73, 206, 5, 160, 1, 3, 67, 69, 32, 152, 1, 2, 68, 69, 222, 24, 77, 222, - 2, 78, 200, 37, 7, 90, 65, 75, 65, 89, 65, 32, 129, 228, 19, 9, 32, 76, - 79, 86, 69, 32, 89, 79, 85, 8, 114, 67, 232, 161, 11, 18, 72, 79, 67, 75, - 69, 89, 32, 83, 84, 73, 67, 75, 32, 65, 78, 68, 32, 80, 159, 190, 1, 83, - 4, 230, 239, 15, 82, 163, 226, 2, 85, 246, 1, 68, 3, 78, 84, 73, 201, 1, - 9, 79, 71, 82, 65, 80, 72, 73, 67, 32, 8, 64, 4, 67, 65, 76, 32, 105, 8, - 70, 73, 67, 65, 84, 73, 79, 78, 6, 32, 2, 84, 79, 215, 239, 22, 87, 5, - 237, 242, 14, 12, 32, 65, 78, 68, 32, 83, 76, 65, 78, 84, 69, 68, 2, 141, - 217, 22, 2, 32, 67, 238, 1, 208, 2, 11, 65, 78, 78, 79, 84, 65, 84, 73, - 79, 78, 32, 242, 3, 67, 72, 2, 68, 69, 248, 5, 5, 78, 85, 77, 66, 69, 22, - 84, 128, 237, 5, 8, 72, 65, 76, 70, 32, 70, 73, 76, 148, 186, 1, 5, 69, - 78, 84, 69, 82, 0, 3, 82, 73, 83, 24, 5, 76, 69, 86, 69, 76, 140, 138, 9, - 5, 86, 65, 82, 73, 65, 182, 223, 4, 70, 158, 47, 73, 159, 228, 1, 83, 32, - 232, 1, 5, 66, 79, 84, 84, 79, 22, 70, 82, 77, 62, 84, 140, 1, 4, 76, 73, - 78, 75, 160, 143, 1, 4, 83, 69, 67, 79, 218, 158, 6, 79, 144, 134, 6, 6, - 82, 69, 86, 69, 82, 83, 240, 203, 9, 5, 72, 69, 65, 86, 69, 169, 39, 3, - 69, 65, 82, 2, 227, 169, 23, 77, 6, 48, 3, 79, 85, 82, 153, 160, 23, 3, - 73, 82, 83, 4, 142, 169, 23, 84, 27, 32, 4, 36, 3, 73, 68, 68, 155, 129, - 23, 65, 2, 167, 181, 13, 76, 8, 34, 72, 46, 87, 203, 130, 8, 79, 4, 194, - 142, 9, 82, 133, 143, 14, 2, 73, 82, 2, 243, 167, 23, 79, 4, 36, 3, 76, - 79, 83, 135, 151, 21, 79, 2, 181, 167, 23, 3, 73, 78, 71, 36, 104, 20, - 83, 67, 82, 73, 80, 84, 73, 79, 78, 32, 67, 72, 65, 82, 65, 67, 84, 69, - 82, 32, 159, 172, 7, 80, 34, 144, 2, 9, 65, 66, 79, 86, 69, 32, 84, 79, - 32, 84, 8, 76, 69, 70, 84, 32, 84, 79, 32, 76, 5, 79, 86, 69, 82, 76, 20, - 2, 83, 85, 182, 241, 14, 82, 252, 189, 5, 8, 70, 85, 76, 76, 32, 83, 85, - 82, 145, 228, 2, 15, 72, 79, 82, 73, 90, 79, 78, 84, 65, 76, 32, 82, 69, - 70, 76, 4, 60, 9, 77, 73, 68, 68, 76, 69, 32, 65, 78, 143, 199, 21, 66, - 2, 215, 183, 21, 68, 4, 128, 148, 22, 10, 77, 73, 68, 68, 76, 69, 32, 65, - 78, 68, 183, 166, 1, 82, 2, 247, 220, 16, 65, 18, 72, 12, 82, 82, 79, 85, - 78, 68, 32, 70, 82, 79, 77, 32, 147, 178, 17, 66, 16, 82, 76, 220, 203, - 11, 3, 85, 80, 80, 158, 249, 9, 66, 166, 161, 1, 65, 159, 82, 82, 6, 206, - 203, 11, 79, 219, 236, 11, 69, 2, 235, 213, 8, 82, 148, 1, 92, 10, 65, - 76, 76, 89, 32, 77, 65, 82, 75, 32, 41, 9, 69, 76, 69, 71, 82, 65, 80, - 72, 32, 10, 198, 241, 21, 70, 70, 84, 183, 86, 79, 138, 1, 140, 1, 11, - 83, 89, 77, 66, 79, 76, 32, 70, 79, 82, 32, 141, 231, 12, 17, 76, 73, 78, - 69, 32, 70, 69, 69, 68, 32, 83, 69, 80, 65, 82, 65, 84, 136, 1, 182, 1, - 65, 58, 68, 200, 2, 5, 72, 79, 85, 82, 32, 214, 1, 74, 28, 4, 70, 69, 66, - 82, 64, 2, 77, 65, 148, 192, 15, 3, 78, 79, 86, 0, 4, 83, 69, 80, 84, 25, - 4, 79, 67, 84, 79, 4, 244, 244, 19, 3, 85, 71, 85, 169, 182, 1, 2, 80, - 82, 64, 44, 3, 65, 89, 32, 221, 196, 15, 2, 69, 67, 62, 66, 84, 162, 209, - 5, 69, 66, 70, 70, 78, 26, 83, 227, 241, 16, 79, 34, 34, 72, 90, 87, 239, - 140, 23, 69, 8, 36, 3, 73, 82, 84, 199, 237, 21, 82, 6, 26, 89, 251, 136, - 22, 69, 5, 155, 178, 22, 45, 24, 26, 69, 179, 220, 23, 79, 22, 36, 3, 78, - 84, 89, 183, 223, 22, 76, 21, 247, 158, 15, 45, 50, 78, 84, 254, 206, 5, - 69, 66, 70, 70, 78, 26, 83, 234, 155, 16, 90, 251, 85, 79, 20, 42, 87, - 218, 208, 5, 72, 195, 186, 17, 69, 14, 26, 69, 223, 218, 23, 79, 12, 36, - 3, 78, 84, 89, 227, 221, 22, 76, 11, 143, 154, 17, 45, 6, 24, 2, 65, 78, - 35, 85, 2, 11, 85, 2, 243, 162, 17, 65, 4, 142, 195, 23, 78, 143, 5, 76, - 4, 154, 183, 23, 82, 171, 34, 89, 68, 40, 6, 65, 71, 69, 32, 79, 70, 83, - 80, 5, 221, 131, 9, 15, 32, 79, 82, 32, 65, 80, 80, 82, 79, 88, 73, 77, - 65, 84, 69, 65, 65, 14, 69, 82, 73, 65, 76, 32, 65, 82, 65, 77, 65, 73, - 67, 32, 62, 72, 7, 78, 85, 77, 66, 69, 82, 32, 230, 18, 76, 221, 195, 19, - 2, 83, 69, 16, 26, 84, 167, 195, 15, 79, 10, 162, 183, 11, 87, 138, 148, - 1, 69, 143, 156, 9, 72, 136, 3, 202, 1, 67, 234, 1, 68, 214, 6, 70, 132, - 2, 5, 72, 73, 66, 73, 84, 156, 1, 15, 80, 85, 84, 32, 83, 89, 77, 66, 79, - 76, 32, 70, 79, 82, 32, 206, 1, 83, 236, 5, 2, 84, 69, 206, 8, 86, 251, - 250, 9, 66, 10, 32, 2, 79, 77, 69, 2, 82, 69, 4, 240, 202, 16, 3, 73, 78, - 71, 157, 225, 2, 5, 80, 76, 69, 84, 69, 6, 36, 3, 65, 83, 69, 195, 144, - 23, 77, 4, 34, 32, 185, 181, 10, 2, 83, 32, 2, 173, 183, 11, 8, 70, 79, - 78, 84, 32, 83, 73, 90, 145, 1, 24, 2, 69, 88, 103, 73, 5, 237, 185, 22, - 20, 32, 80, 79, 73, 78, 84, 73, 78, 71, 32, 65, 84, 32, 84, 72, 69, 32, - 86, 73, 69, 138, 1, 72, 8, 67, 32, 83, 73, 89, 65, 81, 32, 249, 254, 17, - 4, 65, 78, 32, 82, 136, 1, 196, 1, 11, 65, 76, 84, 69, 82, 78, 65, 84, - 69, 32, 76, 2, 76, 28, 9, 70, 82, 65, 67, 84, 73, 79, 78, 32, 100, 7, 78, - 85, 77, 66, 69, 82, 32, 190, 243, 8, 82, 149, 125, 6, 80, 76, 65, 67, 69, - 72, 2, 157, 142, 23, 2, 65, 75, 6, 68, 4, 79, 78, 69, 32, 137, 206, 21, - 7, 84, 72, 82, 69, 69, 32, 81, 4, 222, 203, 21, 72, 47, 81, 122, 212, 1, - 10, 65, 76, 84, 69, 82, 78, 65, 84, 69, 32, 72, 5, 75, 65, 82, 79, 82, 0, - 4, 76, 65, 75, 72, 198, 138, 10, 69, 66, 70, 94, 78, 26, 83, 78, 84, 240, - 130, 5, 8, 80, 82, 69, 70, 73, 88, 69, 68, 199, 41, 79, 6, 26, 84, 211, - 179, 22, 79, 4, 244, 160, 6, 2, 69, 78, 147, 141, 17, 87, 5, 239, 252, - 22, 65, 16, 72, 5, 73, 78, 73, 84, 89, 85, 9, 79, 82, 77, 65, 84, 73, 79, - 78, 32, 5, 45, 9, 32, 78, 69, 71, 65, 84, 69, 68, 32, 2, 157, 168, 8, 4, - 87, 73, 84, 72, 12, 42, 83, 137, 203, 19, 4, 68, 69, 83, 75, 10, 192, - 149, 7, 5, 69, 80, 65, 82, 65, 139, 252, 9, 79, 4, 11, 32, 4, 152, 249, - 22, 15, 65, 82, 65, 66, 73, 67, 32, 70, 79, 82, 77, 32, 83, 72, 65, 1, - 14, 83, 89, 77, 77, 69, 84, 82, 73, 67, 32, 83, 87, 65, 80, 10, 80, 6, - 76, 65, 84, 73, 78, 32, 198, 240, 14, 83, 165, 201, 7, 4, 78, 85, 77, 66, - 6, 64, 6, 67, 65, 80, 73, 84, 65, 0, 4, 83, 77, 65, 76, 27, 76, 2, 21, 3, - 76, 32, 76, 2, 253, 198, 21, 2, 69, 84, 116, 84, 13, 67, 82, 73, 80, 84, - 73, 79, 78, 65, 76, 32, 80, 65, 209, 196, 15, 2, 69, 82, 114, 72, 6, 72, - 76, 65, 86, 73, 32, 225, 1, 7, 82, 84, 72, 73, 65, 78, 32, 54, 48, 7, 76, - 69, 84, 84, 69, 82, 32, 191, 3, 78, 38, 134, 173, 10, 84, 226, 118, 65, - 22, 68, 34, 76, 22, 77, 50, 87, 186, 165, 4, 71, 90, 90, 34, 83, 66, 89, - 158, 208, 1, 72, 234, 5, 75, 198, 75, 66, 178, 216, 4, 78, 223, 105, 80, - 60, 22, 76, 251, 1, 78, 44, 11, 69, 44, 29, 5, 84, 84, 69, 82, 32, 44, - 150, 171, 10, 84, 246, 118, 68, 34, 76, 50, 81, 238, 205, 1, 82, 178, - 215, 2, 65, 50, 71, 90, 90, 34, 83, 66, 89, 158, 208, 1, 72, 234, 5, 75, - 198, 75, 66, 178, 216, 4, 78, 134, 2, 87, 218, 103, 80, 171, 4, 77, 16, - 33, 6, 85, 77, 66, 69, 82, 32, 16, 146, 162, 11, 84, 174, 140, 4, 79, - 219, 128, 3, 70, 50, 36, 4, 71, 82, 65, 76, 179, 3, 82, 23, 11, 32, 20, - 58, 65, 140, 1, 5, 87, 73, 84, 72, 32, 175, 160, 21, 69, 4, 96, 6, 86, - 69, 82, 65, 71, 69, 145, 168, 16, 12, 82, 79, 85, 78, 68, 32, 65, 32, 80, - 79, 73, 78, 2, 181, 169, 16, 5, 32, 87, 73, 84, 72, 14, 160, 1, 3, 84, - 73, 77, 20, 2, 85, 78, 248, 235, 6, 16, 76, 69, 70, 84, 87, 65, 82, 68, - 83, 32, 65, 82, 82, 79, 87, 32, 210, 162, 13, 73, 198, 1, 79, 247, 64, - 68, 2, 139, 144, 20, 69, 4, 150, 144, 20, 68, 175, 222, 2, 73, 28, 94, - 76, 232, 1, 7, 83, 69, 67, 84, 73, 79, 78, 146, 7, 82, 228, 249, 11, 2, - 67, 65, 43, 73, 8, 164, 1, 17, 73, 78, 69, 65, 82, 32, 65, 78, 78, 79, - 84, 65, 84, 73, 79, 78, 32, 229, 232, 4, 17, 79, 67, 75, 69, 68, 32, 70, - 69, 77, 65, 76, 69, 32, 65, 78, 68, 32, 6, 140, 164, 8, 3, 65, 78, 67, - 170, 153, 8, 84, 243, 208, 3, 83, 15, 11, 32, 12, 168, 1, 6, 65, 66, 79, - 86, 69, 32, 64, 5, 87, 73, 84, 72, 32, 209, 137, 20, 22, 66, 69, 83, 73, - 68, 69, 32, 65, 78, 68, 32, 74, 79, 73, 78, 69, 68, 32, 87, 73, 84, 72, - 4, 192, 138, 20, 9, 66, 65, 82, 32, 65, 66, 79, 86, 69, 23, 85, 6, 202, - 171, 4, 76, 222, 223, 15, 79, 239, 221, 2, 68, 40, 56, 2, 69, 82, 189, 5, - 7, 73, 83, 73, 66, 76, 69, 32, 34, 48, 3, 83, 69, 32, 225, 2, 4, 84, 69, - 68, 32, 16, 174, 1, 66, 80, 5, 67, 72, 69, 67, 75, 68, 24, 68, 79, 87, - 78, 87, 65, 82, 68, 83, 32, 65, 82, 82, 79, 87, 32, 87, 73, 84, 72, 32, - 84, 73, 80, 250, 163, 20, 87, 215, 26, 77, 6, 44, 5, 76, 65, 67, 75, 32, - 159, 215, 21, 85, 4, 226, 249, 21, 68, 211, 27, 83, 4, 136, 190, 20, 8, - 69, 82, 32, 66, 79, 65, 82, 68, 231, 182, 2, 32, 2, 233, 215, 20, 2, 32, - 76, 18, 144, 1, 6, 73, 78, 84, 69, 82, 82, 22, 76, 142, 242, 11, 80, 206, - 180, 4, 69, 220, 193, 1, 2, 79, 72, 168, 153, 2, 4, 85, 78, 68, 69, 179, - 97, 81, 2, 139, 211, 4, 79, 6, 60, 9, 79, 87, 32, 75, 65, 86, 89, 75, 65, - 163, 228, 6, 65, 5, 237, 144, 18, 11, 32, 87, 73, 84, 72, 32, 75, 65, 86, - 89, 75, 6, 38, 84, 194, 172, 19, 80, 223, 88, 83, 2, 235, 168, 16, 73, - 218, 1, 94, 65, 134, 21, 69, 62, 79, 42, 85, 165, 187, 11, 10, 73, 71, - 83, 65, 87, 32, 80, 85, 90, 90, 202, 1, 120, 5, 67, 75, 45, 79, 45, 32, - 7, 80, 65, 78, 69, 83, 69, 32, 184, 2, 7, 86, 65, 78, 69, 83, 69, 32, - 171, 174, 23, 82, 2, 209, 166, 18, 3, 76, 65, 78, 16, 246, 1, 67, 18, 80, - 136, 157, 1, 10, 73, 78, 68, 85, 83, 84, 82, 73, 65, 76, 216, 223, 3, 3, - 66, 65, 78, 186, 173, 3, 68, 252, 128, 11, 15, 83, 89, 77, 66, 79, 76, - 32, 70, 79, 82, 32, 66, 69, 71, 73, 200, 246, 1, 3, 71, 79, 66, 197, 108, - 2, 79, 71, 2, 203, 32, 65, 2, 197, 136, 13, 7, 79, 83, 84, 32, 79, 70, - 70, 182, 1, 172, 2, 15, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, - 71, 78, 32, 76, 2, 76, 69, 40, 4, 82, 73, 71, 72, 224, 6, 2, 80, 65, 176, - 3, 14, 84, 85, 82, 78, 69, 68, 32, 80, 65, 68, 65, 32, 80, 73, 88, 5, 83, - 73, 71, 78, 32, 140, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, - 239, 173, 21, 68, 6, 52, 2, 75, 69, 152, 189, 16, 2, 80, 69, 175, 5, 67, - 2, 163, 246, 22, 82, 96, 38, 70, 65, 5, 84, 84, 69, 82, 32, 2, 41, 8, 84, - 32, 82, 69, 82, 69, 78, 71, 2, 211, 224, 21, 71, 94, 230, 1, 68, 26, 73, - 48, 2, 75, 65, 66, 78, 130, 1, 66, 2, 67, 2, 71, 16, 2, 80, 65, 56, 2, - 82, 65, 32, 2, 83, 65, 42, 84, 74, 74, 158, 155, 21, 65, 166, 135, 2, 72, - 2, 76, 2, 77, 2, 87, 2, 89, 186, 2, 69, 2, 79, 3, 85, 8, 222, 3, 68, 15, - 65, 7, 232, 198, 5, 3, 32, 75, 65, 215, 225, 17, 73, 7, 11, 32, 4, 22, - 83, 215, 2, 77, 2, 165, 242, 20, 2, 65, 83, 14, 36, 2, 71, 65, 90, 89, - 167, 1, 65, 7, 33, 6, 32, 76, 69, 76, 69, 84, 5, 29, 5, 32, 82, 65, 83, - 87, 2, 227, 223, 5, 65, 4, 163, 1, 65, 7, 11, 32, 4, 154, 1, 77, 245, - 161, 23, 3, 67, 69, 82, 5, 201, 192, 16, 3, 32, 65, 71, 7, 17, 2, 32, 77, - 4, 70, 85, 71, 65, 8, 18, 65, 55, 84, 5, 17, 2, 32, 77, 2, 11, 85, 2, - 135, 128, 23, 82, 4, 11, 65, 5, 11, 32, 2, 11, 77, 2, 11, 65, 2, 11, 72, - 2, 141, 174, 17, 2, 65, 80, 28, 40, 3, 68, 65, 32, 165, 3, 2, 78, 71, 24, - 174, 1, 65, 90, 76, 86, 80, 140, 144, 7, 10, 84, 73, 82, 84, 65, 32, 84, - 85, 77, 69, 130, 253, 11, 87, 164, 192, 2, 7, 73, 83, 69, 78, 45, 73, 83, - 141, 209, 1, 3, 77, 65, 68, 6, 44, 3, 68, 69, 71, 169, 145, 22, 2, 78, - 68, 5, 11, 32, 2, 157, 220, 22, 2, 65, 68, 6, 38, 85, 129, 159, 23, 3, - 73, 78, 71, 4, 160, 183, 18, 2, 78, 71, 207, 241, 3, 72, 4, 38, 73, 253, - 175, 18, 3, 65, 78, 71, 2, 145, 185, 16, 3, 83, 69, 76, 4, 220, 141, 13, - 5, 82, 65, 78, 71, 75, 135, 195, 9, 75, 10, 104, 5, 67, 69, 67, 65, 75, - 144, 197, 5, 5, 80, 65, 78, 89, 65, 176, 2, 3, 87, 73, 71, 227, 240, 10, - 76, 5, 209, 158, 18, 3, 32, 84, 69, 18, 116, 4, 83, 85, 75, 85, 42, 84, - 64, 4, 87, 85, 76, 85, 238, 182, 16, 80, 213, 218, 1, 7, 68, 73, 82, 71, - 65, 32, 77, 5, 193, 164, 22, 5, 32, 77, 69, 78, 68, 6, 26, 65, 171, 184, - 16, 79, 4, 146, 184, 16, 82, 187, 148, 6, 76, 5, 25, 4, 32, 77, 69, 76, - 2, 247, 153, 23, 73, 4, 36, 3, 76, 76, 89, 255, 216, 19, 65, 2, 219, 187, - 19, 70, 4, 180, 236, 21, 2, 89, 83, 171, 96, 73, 6, 168, 188, 14, 2, 71, - 71, 182, 253, 4, 80, 199, 195, 3, 78, 186, 25, 74, 65, 178, 78, 69, 218, - 1, 72, 150, 50, 73, 198, 6, 78, 50, 79, 111, 82, 200, 10, 200, 1, 5, 73, - 84, 72, 73, 32, 234, 4, 78, 188, 45, 6, 84, 65, 75, 65, 78, 65, 208, 13, - 3, 87, 73, 32, 220, 8, 7, 89, 65, 72, 32, 76, 73, 32, 204, 195, 4, 6, 75, - 84, 79, 86, 73, 75, 251, 223, 17, 65, 136, 1, 204, 1, 7, 76, 69, 84, 84, - 69, 82, 32, 178, 2, 83, 138, 116, 68, 208, 237, 3, 2, 86, 79, 160, 137, - 3, 11, 78, 85, 77, 66, 69, 82, 32, 83, 73, 71, 78, 230, 191, 9, 65, 229, - 211, 1, 6, 69, 78, 85, 77, 69, 82, 90, 214, 1, 68, 242, 187, 19, 65, 150, - 1, 84, 230, 5, 85, 206, 201, 1, 73, 162, 193, 1, 78, 46, 83, 82, 66, 2, - 67, 2, 71, 2, 74, 2, 75, 2, 80, 2, 82, 138, 69, 72, 2, 76, 2, 77, 2, 86, - 2, 89, 186, 2, 69, 3, 79, 10, 38, 68, 178, 147, 23, 72, 187, 2, 65, 6, - 166, 155, 21, 68, 138, 248, 1, 72, 187, 2, 65, 12, 40, 4, 73, 71, 78, 32, - 203, 148, 11, 69, 10, 202, 128, 19, 67, 98, 78, 234, 206, 3, 65, 239, 1, - 86, 230, 4, 42, 71, 241, 39, 5, 78, 65, 68, 65, 32, 174, 3, 76, 11, 88, - 73, 32, 82, 65, 68, 73, 67, 65, 76, 32, 169, 204, 20, 2, 65, 82, 172, 3, - 130, 2, 65, 94, 66, 230, 2, 67, 150, 2, 68, 158, 3, 69, 198, 1, 70, 138, - 2, 71, 146, 1, 72, 210, 2, 73, 76, 2, 74, 65, 34, 75, 30, 76, 230, 1, 77, - 254, 1, 78, 62, 79, 102, 80, 110, 82, 166, 1, 83, 230, 6, 84, 226, 2, 86, - 50, 87, 210, 2, 89, 199, 227, 21, 85, 10, 56, 2, 82, 82, 186, 228, 21, - 71, 182, 102, 78, 207, 47, 88, 4, 234, 146, 22, 79, 207, 1, 73, 34, 58, - 65, 38, 73, 46, 76, 54, 79, 102, 82, 203, 196, 21, 69, 4, 186, 196, 5, - 77, 163, 254, 13, 68, 6, 146, 216, 21, 84, 218, 113, 82, 163, 70, 71, 6, - 134, 217, 19, 79, 234, 134, 2, 65, 159, 153, 1, 85, 10, 62, 76, 194, 242, - 22, 65, 218, 5, 78, 142, 5, 68, 203, 17, 87, 2, 209, 218, 3, 4, 84, 32, - 79, 70, 6, 42, 73, 198, 241, 9, 65, 187, 180, 11, 85, 2, 191, 243, 13, - 83, 28, 58, 65, 70, 76, 74, 79, 214, 174, 10, 72, 247, 240, 10, 73, 6, - 38, 85, 250, 240, 22, 82, 219, 5, 86, 2, 129, 232, 21, 2, 76, 68, 8, 42, - 65, 206, 183, 7, 73, 131, 193, 14, 79, 4, 218, 140, 23, 78, 3, 87, 10, - 250, 233, 21, 82, 128, 2, 2, 77, 80, 218, 98, 86, 134, 5, 76, 235, 56, - 87, 34, 38, 69, 38, 73, 98, 79, 195, 1, 82, 4, 186, 137, 21, 65, 183, - 201, 1, 69, 8, 38, 83, 198, 244, 13, 86, 175, 2, 80, 4, 132, 170, 19, 5, - 84, 73, 78, 71, 85, 251, 224, 3, 72, 16, 94, 84, 76, 3, 85, 66, 76, 222, - 246, 12, 87, 136, 194, 9, 2, 32, 78, 222, 23, 79, 223, 56, 71, 7, 25, 4, - 84, 69, 68, 32, 4, 184, 180, 7, 3, 67, 76, 73, 235, 145, 15, 84, 2, 207, - 195, 3, 69, 6, 230, 208, 21, 65, 202, 167, 1, 85, 219, 16, 89, 20, 106, - 65, 38, 78, 32, 3, 86, 69, 78, 152, 252, 8, 6, 77, 66, 82, 79, 73, 68, - 206, 224, 13, 73, 243, 19, 89, 6, 234, 221, 16, 82, 131, 170, 6, 84, 4, - 178, 25, 67, 171, 176, 22, 84, 5, 175, 182, 22, 73, 28, 74, 65, 50, 73, - 62, 76, 38, 82, 170, 20, 69, 218, 160, 22, 79, 223, 23, 85, 6, 246, 242, - 13, 84, 254, 252, 8, 67, 131, 22, 78, 8, 142, 232, 12, 69, 210, 243, 9, - 71, 230, 19, 82, 199, 21, 83, 4, 226, 190, 21, 85, 251, 198, 1, 89, 4, - 168, 215, 21, 2, 65, 71, 187, 173, 1, 79, 14, 58, 79, 52, 2, 82, 65, 150, - 154, 19, 72, 147, 163, 2, 65, 7, 174, 190, 22, 76, 241, 34, 5, 32, 83, - 76, 79, 87, 4, 186, 180, 22, 73, 135, 18, 83, 22, 58, 65, 142, 1, 69, 60, - 5, 73, 68, 73, 78, 71, 19, 79, 8, 38, 76, 250, 188, 22, 78, 199, 13, 73, - 4, 34, 70, 137, 236, 21, 2, 66, 69, 2, 41, 8, 32, 84, 82, 69, 69, 32, 84, - 82, 2, 187, 161, 19, 85, 6, 26, 65, 143, 130, 23, 77, 4, 234, 229, 22, - 82, 175, 28, 68, 2, 195, 19, 32, 6, 26, 82, 183, 254, 22, 79, 4, 246, - 234, 22, 83, 215, 22, 78, 6, 26, 78, 191, 234, 22, 67, 4, 26, 83, 231, - 255, 22, 67, 2, 131, 242, 21, 69, 4, 138, 234, 22, 68, 215, 22, 82, 2, - 205, 172, 5, 2, 78, 73, 22, 46, 65, 34, 69, 78, 73, 41, 3, 79, 78, 71, 4, - 158, 233, 22, 77, 191, 19, 67, 8, 38, 65, 210, 193, 22, 71, 199, 58, 69, - 4, 218, 235, 13, 84, 211, 147, 9, 70, 6, 178, 232, 22, 70, 2, 78, 215, - 22, 68, 5, 161, 196, 11, 3, 32, 83, 84, 22, 42, 69, 34, 73, 46, 79, 235, - 173, 22, 65, 4, 166, 174, 22, 76, 195, 51, 65, 4, 188, 235, 11, 2, 78, - 73, 151, 179, 9, 76, 12, 34, 82, 34, 85, 167, 173, 22, 79, 4, 234, 219, - 21, 84, 183, 80, 78, 6, 26, 78, 219, 251, 22, 84, 4, 146, 208, 21, 84, - 215, 172, 1, 68, 6, 26, 79, 255, 223, 22, 69, 4, 210, 229, 22, 83, 215, - 22, 84, 10, 40, 2, 78, 69, 22, 80, 171, 181, 22, 76, 5, 235, 150, 6, 83, - 4, 198, 171, 10, 80, 195, 189, 2, 69, 10, 54, 82, 202, 224, 21, 76, 166, - 1, 79, 175, 152, 1, 73, 4, 156, 199, 12, 2, 73, 86, 225, 254, 6, 2, 79, - 70, 18, 62, 65, 42, 73, 242, 166, 10, 79, 130, 131, 12, 85, 195, 9, 69, - 6, 150, 170, 22, 73, 226, 79, 80, 3, 84, 6, 240, 230, 12, 3, 71, 72, 84, - 242, 212, 9, 86, 155, 39, 67, 74, 170, 1, 65, 86, 67, 54, 69, 62, 72, - 178, 1, 73, 46, 76, 62, 80, 106, 84, 186, 227, 17, 87, 166, 155, 1, 78, - 234, 63, 79, 150, 173, 1, 77, 254, 102, 81, 254, 32, 75, 247, 47, 85, 6, - 228, 193, 3, 9, 67, 82, 73, 70, 73, 67, 73, 65, 76, 214, 153, 19, 76, - 175, 28, 89, 4, 248, 238, 10, 2, 82, 73, 161, 215, 5, 2, 72, 79, 8, 158, - 186, 21, 67, 142, 51, 65, 146, 8, 76, 167, 129, 1, 69, 10, 18, 69, 39, - 79, 4, 218, 236, 21, 76, 171, 137, 1, 69, 6, 40, 4, 82, 84, 32, 84, 151, - 217, 22, 79, 4, 44, 5, 65, 73, 76, 69, 68, 175, 249, 15, 72, 2, 145, 243, - 20, 2, 32, 66, 4, 228, 229, 17, 2, 67, 75, 247, 139, 5, 76, 6, 26, 65, - 179, 183, 22, 73, 4, 214, 221, 22, 86, 199, 21, 83, 10, 50, 69, 34, 73, - 242, 254, 18, 82, 223, 164, 3, 79, 4, 250, 186, 22, 65, 183, 22, 69, 2, - 255, 214, 22, 82, 12, 34, 69, 34, 79, 235, 228, 21, 65, 4, 166, 226, 22, - 65, 219, 16, 80, 6, 26, 80, 243, 219, 22, 78, 5, 191, 161, 22, 80, 30, - 82, 65, 98, 73, 34, 79, 38, 82, 52, 2, 85, 82, 32, 2, 87, 79, 135, 160, - 22, 69, 6, 38, 78, 142, 205, 21, 66, 247, 26, 76, 2, 33, 6, 78, 69, 68, - 32, 76, 69, 2, 179, 221, 13, 65, 4, 142, 179, 22, 71, 155, 39, 76, 4, - 198, 229, 18, 78, 135, 137, 2, 79, 6, 250, 228, 16, 73, 206, 219, 4, 65, - 159, 153, 1, 69, 4, 130, 166, 21, 66, 219, 37, 84, 5, 239, 176, 19, 32, - 4, 162, 212, 12, 65, 217, 148, 5, 3, 73, 76, 76, 26, 58, 65, 110, 69, 42, - 72, 32, 2, 73, 78, 30, 79, 39, 82, 6, 32, 2, 76, 75, 215, 176, 22, 84, 5, - 11, 32, 2, 11, 69, 2, 17, 2, 78, 67, 2, 153, 226, 17, 2, 76, 79, 4, 136, - 158, 22, 2, 65, 80, 195, 51, 83, 4, 214, 166, 21, 73, 147, 51, 69, 4, - 174, 237, 22, 68, 3, 69, 4, 146, 163, 21, 77, 235, 198, 1, 82, 4, 246, - 155, 22, 79, 239, 80, 65, 2, 201, 206, 20, 2, 69, 76, 184, 1, 92, 2, 76, - 69, 148, 2, 5, 83, 73, 71, 78, 32, 150, 190, 17, 65, 250, 8, 86, 147, - 177, 3, 68, 110, 44, 5, 84, 84, 69, 82, 32, 187, 169, 22, 78, 108, 234, - 196, 17, 78, 150, 204, 1, 65, 38, 68, 46, 76, 38, 82, 34, 84, 46, 86, - 186, 5, 85, 202, 141, 1, 79, 134, 60, 73, 206, 193, 1, 83, 82, 66, 2, 67, - 2, 71, 2, 74, 2, 75, 2, 80, 162, 7, 69, 234, 61, 70, 2, 72, 2, 77, 3, 89, - 22, 94, 67, 138, 1, 83, 246, 211, 18, 78, 242, 60, 65, 174, 158, 1, 74, - 150, 3, 85, 167, 242, 1, 86, 4, 100, 19, 79, 77, 66, 73, 78, 73, 78, 71, - 32, 65, 78, 85, 83, 86, 65, 82, 65, 32, 65, 195, 211, 18, 65, 2, 225, - 230, 19, 3, 66, 79, 86, 4, 224, 190, 12, 6, 80, 65, 67, 73, 78, 71, 163, - 134, 5, 73, 162, 2, 62, 32, 173, 11, 10, 45, 72, 73, 82, 65, 71, 65, 78, - 65, 32, 154, 2, 140, 1, 7, 76, 69, 84, 84, 69, 82, 32, 242, 9, 86, 148, - 237, 18, 10, 68, 73, 71, 82, 65, 80, 72, 32, 75, 79, 246, 203, 1, 73, - 135, 145, 1, 77, 146, 2, 186, 1, 65, 178, 1, 66, 106, 77, 186, 2, 78, 54, - 83, 226, 1, 68, 2, 71, 2, 72, 2, 75, 2, 80, 2, 82, 2, 84, 2, 86, 2, 90, - 126, 87, 46, 89, 246, 219, 22, 69, 2, 73, 2, 79, 3, 85, 19, 60, 4, 73, - 78, 85, 32, 45, 7, 82, 67, 72, 65, 73, 67, 32, 8, 130, 7, 84, 234, 197, - 22, 67, 215, 22, 80, 8, 38, 89, 134, 190, 22, 87, 235, 36, 69, 4, 234, - 226, 22, 69, 3, 73, 20, 50, 73, 158, 226, 22, 65, 2, 69, 2, 79, 3, 85, - 13, 253, 4, 9, 68, 65, 75, 85, 79, 78, 32, 78, 71, 36, 50, 73, 182, 225, - 22, 65, 2, 69, 2, 79, 3, 85, 29, 29, 5, 78, 78, 65, 78, 32, 26, 96, 15, - 78, 65, 83, 65, 76, 73, 90, 69, 68, 32, 84, 79, 78, 69, 45, 69, 5, 84, - 79, 78, 69, 45, 14, 174, 224, 22, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 55, - 3, 56, 12, 234, 223, 22, 50, 2, 51, 2, 52, 2, 53, 2, 55, 3, 56, 13, 174, - 223, 22, 65, 2, 69, 2, 73, 2, 79, 3, 85, 76, 76, 5, 77, 65, 76, 76, 32, - 174, 222, 22, 65, 2, 69, 2, 73, 2, 79, 3, 85, 66, 142, 1, 72, 2, 82, 54, - 75, 46, 84, 30, 87, 46, 89, 206, 136, 19, 78, 242, 146, 3, 83, 210, 27, - 77, 234, 36, 65, 2, 69, 2, 73, 2, 79, 3, 85, 10, 154, 221, 22, 65, 2, 69, - 2, 73, 2, 79, 3, 85, 8, 230, 220, 22, 65, 2, 69, 2, 79, 3, 85, 4, 186, - 220, 22, 79, 3, 85, 8, 158, 220, 22, 65, 2, 69, 2, 73, 3, 79, 6, 242, - 219, 22, 65, 2, 79, 3, 85, 2, 241, 184, 20, 5, 79, 73, 67, 69, 68, 8, 52, - 5, 68, 79, 85, 66, 76, 22, 80, 38, 83, 35, 86, 2, 139, 250, 7, 69, 2, 89, - 6, 82, 79, 76, 79, 78, 71, 2, 29, 5, 69, 77, 73, 45, 86, 2, 21, 3, 79, - 73, 67, 2, 33, 6, 69, 68, 32, 83, 79, 85, 2, 191, 141, 22, 78, 174, 1, - 216, 1, 7, 76, 69, 84, 84, 69, 82, 32, 128, 2, 12, 80, 85, 78, 67, 84, - 85, 65, 84, 73, 79, 78, 32, 148, 3, 5, 83, 73, 71, 78, 32, 88, 11, 86, - 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 170, 163, 12, 68, 195, 209, 6, - 67, 94, 226, 1, 65, 166, 160, 4, 74, 230, 219, 14, 68, 114, 84, 230, 5, - 85, 22, 86, 186, 201, 1, 73, 162, 193, 1, 78, 46, 83, 82, 66, 2, 67, 2, - 71, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, 2, 82, 2, 87, 2, 89, 186, 2, - 69, 3, 79, 7, 130, 214, 22, 65, 3, 73, 22, 122, 67, 90, 68, 58, 70, 54, - 83, 20, 12, 65, 76, 84, 69, 82, 78, 65, 84, 69, 32, 83, 69, 233, 193, 21, - 4, 84, 82, 73, 80, 4, 56, 8, 76, 79, 83, 73, 78, 71, 32, 83, 195, 130, - 21, 73, 2, 225, 202, 21, 2, 80, 73, 4, 11, 79, 4, 216, 194, 21, 2, 85, - 66, 175, 145, 1, 84, 4, 196, 129, 21, 5, 73, 76, 76, 69, 68, 139, 57, 76, - 6, 18, 69, 23, 80, 2, 195, 237, 10, 67, 4, 236, 144, 4, 2, 65, 67, 199, - 184, 17, 73, 12, 242, 219, 7, 75, 162, 226, 10, 67, 98, 78, 162, 59, 82, - 154, 162, 1, 86, 179, 241, 1, 65, 20, 66, 65, 214, 158, 4, 86, 190, 223, - 14, 69, 2, 85, 207, 201, 1, 73, 6, 148, 222, 16, 8, 76, 84, 69, 82, 78, - 65, 84, 69, 162, 243, 5, 65, 3, 73, 96, 148, 1, 7, 76, 69, 84, 84, 69, - 82, 32, 200, 1, 5, 83, 73, 71, 78, 32, 44, 5, 84, 79, 78, 69, 32, 92, 6, - 86, 79, 87, 69, 76, 32, 231, 219, 20, 68, 56, 138, 189, 18, 79, 134, 7, - 72, 154, 145, 2, 78, 238, 178, 1, 75, 2, 80, 2, 83, 2, 84, 138, 69, 66, - 2, 67, 2, 68, 2, 71, 2, 76, 2, 77, 2, 82, 2, 86, 2, 87, 2, 89, 2, 90, - 186, 2, 65, 3, 73, 4, 182, 236, 4, 67, 249, 222, 17, 2, 83, 72, 6, 36, 5, - 67, 65, 76, 89, 65, 23, 80, 5, 17, 2, 32, 80, 2, 173, 223, 17, 3, 76, 79, - 80, 10, 226, 140, 22, 69, 2, 85, 163, 64, 79, 36, 24, 2, 76, 86, 23, 89, - 2, 159, 211, 20, 73, 35, 68, 5, 66, 79, 65, 82, 68, 36, 4, 67, 65, 80, - 32, 187, 244, 2, 72, 5, 245, 134, 19, 4, 32, 65, 78, 68, 26, 254, 136, - 17, 78, 238, 174, 3, 65, 182, 34, 68, 187, 29, 84, 188, 13, 202, 1, 65, - 212, 9, 18, 73, 84, 65, 78, 32, 83, 77, 65, 76, 76, 32, 83, 67, 82, 73, - 80, 84, 32, 204, 3, 4, 77, 69, 82, 32, 204, 25, 5, 79, 74, 75, 73, 32, - 141, 6, 8, 85, 68, 65, 87, 65, 68, 73, 32, 138, 1, 56, 8, 82, 79, 83, 72, - 84, 72, 73, 32, 243, 163, 22, 78, 136, 1, 220, 1, 4, 68, 73, 71, 73, 20, - 7, 76, 69, 84, 84, 69, 82, 32, 144, 2, 7, 78, 85, 77, 66, 69, 82, 32, 36, - 12, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 136, 2, 5, 83, 73, - 71, 78, 32, 198, 1, 86, 183, 194, 8, 70, 8, 223, 134, 16, 84, 74, 182, 1, - 75, 42, 84, 234, 133, 7, 78, 186, 229, 11, 68, 238, 145, 3, 83, 82, 66, - 2, 67, 2, 71, 2, 80, 2, 86, 138, 69, 72, 2, 74, 2, 76, 2, 77, 2, 82, 2, - 89, 2, 90, 187, 2, 65, 6, 138, 195, 22, 72, 2, 75, 187, 2, 65, 12, 142, - 236, 18, 84, 214, 214, 3, 72, 187, 2, 65, 8, 162, 203, 14, 84, 227, 165, - 2, 79, 18, 70, 67, 74, 68, 66, 76, 36, 5, 77, 65, 78, 71, 65, 159, 240, - 20, 83, 4, 26, 82, 247, 241, 20, 73, 2, 145, 162, 21, 6, 69, 83, 67, 69, - 78, 84, 6, 26, 79, 251, 230, 18, 65, 4, 194, 230, 18, 85, 219, 220, 3, - 84, 4, 226, 220, 6, 79, 227, 190, 7, 73, 2, 215, 135, 21, 76, 12, 72, 2, - 66, 65, 22, 67, 20, 2, 68, 79, 166, 139, 20, 86, 179, 241, 1, 65, 2, 239, - 196, 21, 82, 2, 203, 147, 6, 65, 4, 56, 8, 85, 66, 76, 69, 32, 82, 73, - 78, 207, 147, 20, 84, 2, 203, 147, 20, 71, 14, 44, 5, 79, 87, 69, 76, 32, - 247, 135, 20, 73, 12, 44, 5, 83, 73, 71, 78, 32, 163, 254, 21, 76, 10, - 182, 141, 4, 86, 230, 178, 18, 69, 2, 73, 2, 79, 3, 85, 176, 7, 72, 12, - 67, 72, 65, 82, 65, 67, 84, 69, 82, 45, 49, 56, 179, 200, 7, 70, 174, 7, - 22, 66, 147, 1, 67, 128, 4, 170, 247, 7, 48, 2, 49, 2, 50, 2, 51, 2, 52, - 2, 53, 2, 54, 2, 55, 2, 56, 2, 57, 2, 65, 2, 66, 2, 67, 2, 68, 2, 69, 3, - 70, 174, 3, 142, 1, 68, 142, 245, 7, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, - 53, 2, 54, 2, 55, 2, 56, 2, 57, 2, 65, 2, 66, 2, 67, 163, 198, 13, 70, - 12, 206, 188, 22, 48, 2, 49, 2, 50, 2, 51, 2, 52, 3, 53, 246, 2, 202, 1, - 67, 240, 2, 18, 73, 78, 68, 69, 80, 69, 78, 68, 69, 78, 84, 32, 86, 79, - 87, 69, 76, 32, 220, 2, 7, 76, 69, 84, 84, 69, 82, 32, 254, 2, 83, 208, - 12, 6, 86, 79, 87, 69, 76, 32, 143, 180, 20, 68, 70, 176, 1, 20, 79, 78, - 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 67, 79, 69, 78, 71, 32, - 173, 172, 17, 17, 85, 82, 82, 69, 78, 67, 89, 32, 83, 89, 77, 66, 79, 76, - 32, 82, 73, 68, 138, 1, 78, 154, 4, 67, 2, 75, 90, 80, 74, 84, 54, 68, 2, - 76, 138, 234, 21, 83, 158, 41, 77, 2, 82, 2, 86, 2, 89, 190, 28, 66, 3, - 72, 8, 142, 153, 22, 71, 2, 89, 246, 30, 65, 3, 79, 42, 82, 81, 196, 1, - 11, 83, 73, 71, 78, 32, 67, 79, 69, 78, 71, 32, 50, 76, 3, 82, 26, 82, - 65, 44, 6, 79, 79, 32, 84, 89, 80, 34, 85, 134, 172, 20, 73, 223, 137, 2, - 69, 8, 170, 182, 22, 65, 2, 73, 2, 81, 3, 85, 4, 11, 69, 4, 207, 142, 10, - 32, 9, 198, 185, 7, 85, 155, 252, 14, 75, 8, 18, 81, 31, 82, 4, 166, 181, - 22, 69, 3, 85, 4, 215, 149, 18, 89, 70, 138, 1, 67, 2, 75, 42, 78, 50, - 80, 30, 83, 46, 84, 54, 68, 2, 76, 166, 147, 22, 77, 2, 82, 2, 86, 2, 89, - 190, 28, 66, 2, 72, 3, 81, 8, 210, 1, 72, 154, 178, 22, 65, 3, 79, 8, - 206, 148, 22, 71, 2, 78, 2, 89, 247, 30, 79, 6, 122, 72, 155, 178, 22, - 79, 6, 130, 148, 22, 83, 190, 28, 72, 187, 2, 65, 12, 50, 72, 0, 2, 84, - 72, 154, 178, 22, 65, 3, 79, 4, 150, 178, 22, 65, 3, 79, 130, 1, 60, 4, - 73, 71, 78, 32, 221, 6, 6, 89, 77, 66, 79, 76, 32, 46, 202, 2, 65, 104, - 10, 83, 65, 77, 89, 79, 75, 32, 83, 65, 78, 22, 66, 118, 67, 86, 75, 74, - 82, 54, 84, 144, 223, 4, 3, 76, 69, 75, 168, 11, 6, 80, 72, 78, 65, 69, - 75, 248, 226, 10, 10, 89, 85, 85, 75, 65, 76, 69, 65, 80, 73, 200, 238, - 1, 3, 78, 73, 75, 180, 165, 3, 9, 77, 85, 85, 83, 73, 75, 65, 84, 79, - 133, 15, 4, 86, 73, 82, 73, 6, 100, 9, 86, 65, 75, 82, 65, 72, 65, 83, - 65, 176, 128, 18, 4, 84, 84, 72, 65, 209, 136, 4, 2, 72, 83, 2, 167, 171, - 22, 78, 8, 38, 65, 137, 164, 21, 3, 69, 89, 89, 6, 174, 6, 84, 216, 1, 2, - 78, 84, 193, 219, 20, 6, 82, 73, 89, 79, 79, 83, 4, 152, 149, 7, 12, 65, - 77, 78, 85, 67, 32, 80, 73, 73, 32, 75, 85, 211, 212, 12, 79, 6, 140, - 214, 11, 2, 65, 75, 154, 140, 9, 72, 177, 80, 4, 79, 79, 77, 85, 4, 210, - 213, 11, 79, 249, 224, 5, 4, 69, 65, 72, 77, 4, 192, 151, 21, 8, 79, 65, - 78, 68, 65, 75, 72, 73, 253, 2, 4, 82, 73, 73, 83, 84, 128, 1, 3, 68, 65, - 80, 92, 10, 76, 69, 75, 32, 65, 84, 84, 65, 75, 32, 182, 1, 80, 72, 5, - 84, 85, 84, 69, 89, 78, 66, 47, 77, 24, 22, 45, 223, 3, 32, 20, 30, 80, - 238, 2, 66, 47, 77, 8, 138, 3, 73, 37, 3, 82, 65, 77, 20, 50, 80, 98, 66, - 130, 143, 11, 77, 255, 200, 10, 83, 12, 36, 3, 82, 65, 77, 203, 148, 22, - 73, 11, 11, 45, 8, 42, 66, 130, 143, 11, 77, 239, 213, 8, 80, 4, 250, - 215, 21, 85, 151, 60, 69, 26, 44, 2, 65, 84, 44, 3, 82, 65, 77, 91, 73, - 2, 21, 3, 72, 65, 77, 2, 223, 178, 16, 65, 20, 18, 45, 119, 32, 16, 34, - 66, 32, 2, 80, 73, 15, 77, 8, 30, 69, 37, 3, 85, 79, 78, 4, 35, 73, 4, - 21, 3, 85, 79, 89, 4, 11, 32, 4, 34, 82, 169, 240, 21, 2, 75, 79, 2, 195, - 253, 20, 79, 42, 76, 10, 73, 78, 72, 69, 82, 69, 78, 84, 32, 65, 29, 5, - 83, 73, 71, 78, 32, 4, 218, 164, 22, 65, 3, 81, 38, 102, 65, 54, 73, 30, - 79, 38, 89, 184, 253, 2, 5, 67, 79, 69, 78, 71, 250, 165, 7, 85, 183, - 255, 11, 69, 10, 174, 174, 3, 65, 170, 245, 18, 69, 2, 73, 3, 85, 7, 162, - 163, 22, 69, 3, 73, 6, 134, 163, 22, 69, 2, 77, 3, 79, 7, 226, 162, 22, - 65, 3, 89, 130, 1, 146, 1, 68, 88, 7, 76, 69, 84, 84, 69, 82, 32, 170, 2, - 83, 160, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 230, 225, 15, - 87, 211, 80, 65, 6, 48, 6, 79, 85, 66, 76, 69, 32, 219, 196, 18, 65, 4, - 170, 160, 10, 83, 163, 164, 8, 68, 90, 234, 1, 83, 254, 4, 66, 42, 71, - 186, 134, 6, 68, 214, 134, 12, 74, 158, 50, 65, 150, 1, 84, 218, 207, 1, - 76, 250, 192, 1, 78, 126, 67, 2, 75, 2, 80, 138, 69, 72, 2, 77, 2, 81, 2, - 82, 2, 86, 2, 89, 186, 2, 69, 2, 73, 2, 79, 3, 85, 4, 26, 72, 211, 158, - 22, 65, 2, 189, 222, 21, 3, 79, 82, 84, 12, 40, 4, 73, 71, 78, 32, 195, - 157, 10, 69, 10, 54, 83, 238, 137, 18, 78, 206, 61, 86, 159, 145, 3, 65, - 4, 26, 72, 187, 162, 17, 85, 2, 11, 65, 2, 159, 248, 21, 68, 18, 170, - 234, 3, 86, 154, 223, 14, 65, 242, 201, 1, 73, 222, 137, 2, 69, 2, 79, 3, - 85, 138, 1, 100, 7, 76, 69, 84, 84, 69, 82, 32, 176, 2, 5, 83, 73, 71, - 78, 32, 202, 177, 16, 86, 187, 246, 3, 68, 94, 222, 1, 66, 42, 71, 186, - 134, 6, 68, 174, 133, 12, 74, 198, 51, 65, 118, 82, 34, 84, 230, 5, 85, - 206, 201, 1, 73, 162, 193, 1, 78, 126, 67, 2, 75, 2, 80, 2, 83, 138, 69, - 72, 2, 76, 2, 77, 2, 86, 2, 89, 186, 2, 69, 3, 79, 6, 182, 151, 22, 66, - 2, 72, 187, 2, 65, 6, 142, 151, 22, 71, 2, 72, 187, 2, 65, 6, 190, 133, - 18, 78, 206, 61, 86, 159, 145, 3, 65, 136, 1, 140, 1, 8, 82, 65, 84, 32, - 82, 65, 73, 32, 216, 3, 2, 83, 83, 208, 223, 20, 4, 87, 73, 70, 82, 182, - 45, 80, 240, 93, 2, 77, 79, 191, 18, 84, 116, 140, 1, 7, 76, 69, 84, 84, - 69, 82, 32, 172, 1, 5, 83, 73, 71, 78, 32, 92, 11, 86, 79, 87, 69, 76, - 32, 83, 73, 71, 78, 32, 243, 229, 11, 68, 64, 206, 188, 18, 68, 114, 84, - 226, 222, 1, 78, 238, 178, 1, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 2, - 83, 138, 69, 72, 2, 76, 2, 77, 2, 82, 2, 86, 2, 89, 187, 2, 65, 12, 208, - 202, 9, 3, 84, 79, 78, 0, 2, 89, 85, 242, 251, 1, 83, 230, 137, 10, 65, - 239, 1, 86, 16, 246, 192, 18, 65, 174, 147, 3, 85, 162, 64, 69, 2, 73, 3, - 79, 13, 40, 4, 73, 78, 71, 32, 163, 210, 21, 32, 8, 96, 4, 70, 65, 67, - 69, 181, 135, 14, 14, 67, 65, 84, 32, 70, 65, 67, 69, 32, 87, 73, 84, 72, - 32, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 162, 134, 14, 83, 215, 144, 5, - 67, 4, 168, 146, 18, 3, 69, 69, 76, 215, 227, 3, 79, 4, 40, 4, 82, 69, - 65, 78, 187, 239, 21, 65, 2, 33, 6, 32, 83, 84, 65, 78, 68, 2, 129, 228, - 18, 2, 65, 82, 2, 11, 79, 2, 191, 161, 10, 78, 174, 43, 154, 1, 65, 150, - 232, 1, 69, 182, 62, 73, 238, 83, 79, 222, 28, 85, 94, 89, 226, 224, 7, - 82, 244, 179, 9, 5, 32, 66, 32, 66, 65, 166, 160, 1, 76, 239, 66, 70, - 252, 22, 182, 1, 66, 44, 6, 67, 82, 79, 83, 83, 69, 46, 68, 56, 2, 79, - 32, 222, 11, 82, 236, 21, 4, 83, 84, 32, 81, 76, 4, 84, 73, 78, 32, 237, - 129, 20, 8, 78, 71, 85, 65, 71, 69, 32, 84, 4, 176, 182, 14, 2, 32, 67, - 175, 206, 6, 69, 2, 153, 237, 16, 6, 32, 83, 84, 73, 67, 75, 4, 220, 242, - 12, 5, 89, 32, 66, 69, 69, 243, 220, 8, 68, 174, 1, 200, 2, 3, 72, 79, - 32, 28, 7, 76, 69, 84, 84, 69, 82, 32, 214, 5, 83, 148, 1, 9, 84, 79, 78, - 69, 32, 77, 65, 73, 32, 84, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, - 32, 188, 164, 4, 2, 75, 79, 224, 247, 12, 2, 89, 65, 230, 194, 2, 69, - 190, 50, 68, 236, 135, 1, 7, 67, 65, 78, 67, 69, 76, 76, 221, 68, 6, 78, - 73, 71, 71, 65, 72, 4, 190, 235, 21, 77, 3, 78, 94, 176, 1, 3, 70, 79, - 32, 78, 75, 96, 2, 76, 79, 50, 80, 154, 1, 83, 86, 84, 30, 72, 234, 149, - 16, 78, 166, 208, 5, 66, 2, 67, 2, 68, 2, 77, 2, 82, 2, 87, 2, 89, 247, - 30, 79, 8, 42, 70, 254, 162, 15, 83, 195, 170, 5, 84, 4, 214, 184, 21, - 79, 155, 62, 65, 10, 26, 72, 255, 135, 22, 79, 8, 32, 3, 77, 85, 32, 231, - 2, 79, 4, 146, 175, 21, 78, 211, 57, 71, 7, 17, 2, 32, 76, 4, 170, 182, - 21, 73, 67, 79, 30, 52, 4, 65, 76, 73, 32, 210, 1, 72, 131, 133, 22, 79, - 24, 154, 194, 6, 68, 34, 84, 206, 6, 78, 138, 195, 13, 66, 2, 67, 2, 71, - 2, 74, 171, 216, 1, 76, 8, 52, 9, 65, 78, 83, 75, 82, 73, 84, 32, 83, 71, - 79, 4, 254, 130, 22, 72, 3, 83, 6, 26, 72, 131, 133, 22, 79, 4, 11, 79, - 4, 11, 32, 4, 170, 159, 15, 83, 195, 170, 5, 84, 6, 112, 14, 69, 77, 73, - 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 169, 173, 18, 8, 73, 71, 78, - 32, 80, 65, 76, 73, 4, 138, 171, 21, 78, 211, 57, 76, 8, 50, 84, 220, - 145, 17, 2, 67, 65, 139, 238, 4, 69, 4, 134, 228, 21, 72, 247, 30, 73, - 32, 106, 65, 44, 5, 77, 65, 73, 32, 75, 146, 226, 17, 89, 142, 76, 85, - 206, 201, 1, 69, 2, 73, 223, 137, 2, 79, 11, 238, 129, 22, 65, 2, 73, 2, - 77, 3, 89, 4, 226, 177, 21, 65, 3, 79, 166, 1, 32, 2, 71, 69, 131, 250, - 20, 73, 164, 1, 26, 32, 199, 239, 13, 82, 160, 1, 254, 1, 66, 44, 4, 71, - 82, 69, 69, 22, 79, 250, 1, 84, 182, 231, 11, 68, 36, 2, 85, 80, 176, - 192, 3, 12, 76, 69, 70, 84, 32, 84, 82, 73, 65, 78, 71, 76, 164, 193, 4, - 5, 80, 85, 82, 80, 76, 12, 3, 82, 69, 68, 0, 6, 89, 69, 76, 76, 79, 87, - 243, 64, 67, 10, 40, 3, 82, 79, 87, 201, 1, 2, 76, 85, 4, 195, 235, 19, - 78, 10, 48, 3, 78, 69, 32, 129, 1, 4, 82, 65, 78, 71, 4, 160, 155, 8, 5, - 68, 79, 84, 32, 79, 181, 233, 2, 18, 82, 73, 78, 71, 32, 79, 86, 69, 82, - 32, 84, 87, 79, 32, 82, 73, 78, 71, 6, 11, 69, 6, 11, 32, 6, 210, 170, - 20, 67, 154, 21, 68, 139, 28, 83, 116, 156, 1, 3, 87, 79, 32, 108, 10, - 89, 80, 69, 32, 80, 73, 69, 67, 69, 32, 157, 186, 18, 17, 82, 73, 80, 76, - 69, 32, 86, 69, 82, 84, 73, 67, 65, 76, 32, 66, 65, 4, 150, 225, 17, 68, - 193, 87, 19, 82, 73, 78, 71, 83, 32, 79, 86, 69, 82, 32, 79, 78, 69, 32, - 82, 73, 78, 71, 110, 182, 1, 67, 136, 2, 9, 68, 73, 65, 71, 79, 78, 65, - 76, 32, 218, 1, 76, 186, 2, 82, 158, 1, 83, 204, 3, 6, 85, 80, 80, 69, - 82, 32, 145, 2, 9, 86, 69, 82, 84, 69, 88, 32, 79, 70, 14, 80, 9, 69, 78, - 84, 82, 69, 32, 79, 70, 32, 73, 7, 82, 79, 83, 83, 66, 65, 82, 8, 252, 8, - 6, 90, 32, 87, 73, 84, 72, 142, 239, 21, 75, 2, 88, 3, 89, 7, 33, 6, 32, - 87, 73, 84, 72, 32, 4, 40, 3, 76, 79, 87, 1, 3, 85, 80, 80, 2, 181, 197, - 10, 2, 69, 82, 12, 48, 6, 85, 80, 80, 69, 82, 32, 227, 222, 9, 76, 8, 56, - 4, 76, 69, 70, 84, 157, 223, 9, 4, 82, 73, 71, 72, 5, 11, 32, 2, 21, 3, - 65, 78, 68, 2, 17, 2, 32, 76, 2, 17, 2, 79, 87, 2, 213, 194, 21, 2, 69, - 82, 26, 48, 5, 79, 87, 69, 82, 32, 129, 3, 2, 69, 70, 24, 84, 5, 76, 69, - 70, 84, 32, 56, 6, 82, 73, 71, 72, 84, 32, 158, 6, 72, 179, 1, 84, 8, 22, - 65, 207, 7, 67, 4, 194, 1, 78, 159, 202, 20, 82, 10, 22, 65, 151, 7, 67, - 6, 192, 1, 13, 78, 68, 32, 85, 80, 80, 69, 82, 32, 82, 73, 71, 72, 193, - 189, 19, 2, 82, 67, 4, 44, 4, 65, 73, 83, 69, 77, 3, 73, 71, 72, 2, 53, - 11, 68, 32, 85, 80, 80, 69, 82, 32, 76, 69, 70, 2, 187, 245, 18, 84, 2, - 141, 243, 20, 3, 84, 32, 65, 34, 48, 5, 72, 79, 82, 84, 32, 77, 3, 84, - 69, 77, 4, 40, 3, 76, 79, 87, 1, 3, 85, 80, 80, 2, 217, 4, 4, 69, 82, 32, - 84, 31, 44, 6, 32, 87, 73, 84, 72, 32, 171, 1, 45, 8, 44, 5, 76, 69, 70, - 84, 32, 30, 82, 59, 67, 4, 82, 67, 167, 214, 10, 74, 2, 21, 3, 73, 71, - 72, 2, 11, 84, 2, 17, 2, 32, 67, 2, 177, 205, 20, 4, 82, 79, 83, 83, 20, - 48, 2, 49, 50, 22, 50, 26, 52, 247, 181, 11, 51, 5, 207, 211, 14, 51, 9, - 11, 51, 7, 11, 52, 5, 243, 237, 21, 53, 18, 62, 72, 96, 3, 76, 69, 70, 0, - 4, 82, 73, 71, 72, 83, 84, 4, 65, 14, 65, 76, 70, 32, 86, 69, 82, 84, 69, - 88, 32, 79, 70, 32, 4, 218, 236, 21, 77, 3, 87, 6, 17, 2, 84, 32, 6, 26, - 67, 131, 240, 18, 65, 4, 186, 92, 82, 211, 147, 17, 79, 2, 201, 243, 8, - 3, 69, 82, 77, 2, 211, 219, 14, 32, 6, 53, 11, 85, 65, 82, 84, 69, 82, - 32, 77, 79, 79, 78, 7, 151, 234, 6, 32, 150, 20, 150, 1, 67, 148, 46, 18, - 69, 80, 73, 71, 82, 65, 80, 72, 73, 67, 32, 76, 69, 84, 84, 69, 82, 32, - 252, 1, 7, 76, 69, 84, 84, 69, 82, 32, 183, 13, 83, 200, 7, 56, 8, 65, - 80, 73, 84, 65, 76, 32, 76, 139, 168, 20, 82, 198, 7, 76, 6, 69, 84, 84, - 69, 82, 32, 221, 44, 8, 73, 71, 65, 84, 85, 82, 69, 32, 194, 7, 154, 2, - 65, 158, 3, 66, 154, 1, 67, 254, 1, 68, 250, 1, 69, 174, 2, 70, 82, 71, - 194, 1, 72, 146, 1, 73, 154, 2, 74, 118, 75, 126, 76, 234, 2, 77, 114, - 78, 134, 2, 79, 198, 2, 80, 102, 81, 62, 82, 190, 2, 83, 130, 3, 84, 142, - 3, 85, 234, 1, 86, 146, 1, 87, 118, 88, 50, 89, 167, 1, 90, 93, 172, 1, - 6, 32, 87, 73, 84, 72, 32, 166, 1, 69, 230, 65, 78, 216, 146, 14, 6, 70, - 82, 73, 67, 65, 78, 222, 182, 3, 76, 130, 182, 2, 86, 182, 162, 1, 65, 2, - 79, 2, 85, 3, 89, 66, 230, 64, 66, 34, 68, 108, 3, 82, 73, 78, 242, 33, - 77, 250, 21, 67, 162, 49, 72, 158, 1, 79, 198, 10, 71, 186, 2, 65, 202, - 168, 3, 73, 186, 159, 13, 84, 227, 179, 3, 83, 7, 33, 6, 32, 87, 73, 84, - 72, 32, 4, 202, 183, 1, 65, 187, 194, 3, 77, 21, 60, 6, 32, 87, 73, 84, - 72, 32, 174, 68, 82, 207, 249, 20, 69, 14, 138, 67, 84, 142, 60, 70, 218, - 56, 76, 226, 143, 16, 68, 162, 199, 2, 72, 219, 163, 1, 83, 33, 108, 6, - 32, 87, 73, 84, 72, 32, 188, 70, 7, 76, 79, 83, 69, 68, 32, 73, 54, 85, - 174, 202, 20, 79, 139, 60, 72, 20, 94, 67, 194, 180, 1, 65, 230, 227, 3, - 80, 166, 244, 14, 72, 158, 50, 66, 238, 32, 68, 211, 80, 83, 8, 182, 68, - 69, 202, 112, 73, 255, 133, 19, 65, 31, 44, 6, 32, 87, 73, 84, 72, 32, - 179, 1, 90, 24, 78, 83, 194, 15, 67, 250, 47, 84, 230, 116, 76, 226, 143, - 16, 68, 163, 199, 2, 72, 8, 92, 13, 77, 65, 76, 76, 32, 76, 69, 84, 84, - 69, 82, 32, 90, 178, 137, 1, 72, 243, 164, 20, 84, 5, 225, 72, 2, 32, 87, - 91, 104, 6, 32, 87, 73, 84, 72, 32, 152, 1, 2, 90, 72, 182, 76, 71, 166, - 239, 16, 84, 142, 159, 4, 83, 63, 78, 70, 174, 74, 67, 158, 2, 68, 194, - 16, 84, 158, 23, 77, 250, 1, 86, 250, 44, 72, 158, 1, 79, 198, 10, 71, - 186, 2, 65, 202, 168, 3, 73, 62, 66, 223, 210, 16, 83, 7, 11, 32, 4, 186, - 70, 87, 211, 8, 82, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 210, 135, 20, - 72, 138, 83, 68, 211, 80, 83, 35, 76, 6, 32, 87, 73, 84, 72, 32, 238, 81, - 76, 202, 182, 18, 65, 195, 207, 2, 72, 20, 202, 84, 67, 134, 90, 65, 134, - 169, 3, 66, 182, 25, 77, 198, 140, 4, 79, 142, 137, 11, 72, 138, 83, 68, - 211, 80, 83, 29, 76, 6, 32, 87, 73, 84, 72, 32, 182, 126, 65, 246, 238, - 7, 87, 207, 154, 12, 69, 20, 234, 82, 66, 34, 67, 46, 68, 226, 177, 19, - 72, 219, 163, 1, 83, 59, 80, 6, 32, 87, 73, 84, 72, 32, 180, 88, 2, 78, - 83, 218, 218, 20, 79, 207, 36, 83, 40, 138, 1, 68, 210, 83, 67, 242, 1, - 77, 142, 1, 84, 142, 70, 72, 158, 1, 79, 198, 10, 71, 186, 2, 65, 202, - 168, 3, 73, 62, 66, 223, 210, 16, 83, 10, 22, 79, 239, 83, 73, 6, 150, - 121, 85, 231, 190, 18, 84, 11, 33, 6, 32, 87, 73, 84, 72, 32, 8, 42, 67, - 234, 241, 17, 84, 227, 179, 3, 83, 4, 166, 170, 1, 73, 207, 217, 3, 82, - 25, 33, 6, 32, 87, 73, 84, 72, 32, 22, 230, 89, 67, 34, 68, 50, 83, 234, - 78, 65, 138, 1, 76, 246, 205, 7, 79, 143, 137, 11, 72, 41, 60, 6, 32, 87, - 73, 84, 72, 32, 238, 94, 65, 251, 244, 20, 74, 32, 138, 1, 66, 36, 2, 68, - 79, 52, 7, 77, 73, 68, 68, 76, 69, 32, 38, 83, 174, 2, 67, 230, 91, 72, - 242, 71, 65, 138, 1, 76, 251, 198, 16, 84, 4, 242, 186, 12, 69, 139, 223, - 8, 65, 6, 22, 85, 151, 92, 84, 2, 129, 236, 4, 2, 66, 76, 4, 162, 238, - 17, 84, 167, 147, 3, 68, 4, 214, 2, 77, 151, 159, 21, 84, 19, 44, 6, 32, - 87, 73, 84, 72, 32, 255, 94, 73, 10, 174, 165, 1, 65, 234, 144, 16, 68, - 154, 55, 84, 139, 144, 2, 72, 33, 48, 6, 32, 87, 73, 84, 72, 32, 155, - 208, 21, 74, 28, 102, 67, 44, 2, 83, 77, 226, 96, 76, 146, 64, 71, 186, - 2, 65, 102, 68, 154, 206, 7, 79, 135, 249, 8, 84, 6, 250, 131, 1, 69, 22, - 73, 131, 166, 19, 65, 2, 189, 226, 10, 10, 65, 76, 76, 32, 76, 69, 84, - 84, 69, 82, 101, 108, 6, 32, 87, 73, 84, 72, 32, 226, 103, 76, 226, 152, - 4, 80, 170, 150, 9, 77, 242, 182, 7, 73, 2, 79, 3, 85, 84, 144, 1, 2, 76, - 79, 34, 77, 146, 97, 67, 70, 68, 154, 2, 79, 38, 83, 66, 84, 106, 86, - 234, 43, 72, 242, 12, 71, 186, 2, 65, 202, 168, 3, 73, 63, 66, 4, 206, - 99, 78, 235, 232, 20, 79, 8, 202, 99, 65, 195, 153, 4, 73, 17, 33, 6, 32, - 87, 73, 84, 72, 32, 14, 178, 104, 70, 34, 83, 178, 55, 65, 138, 216, 18, - 72, 139, 83, 68, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 134, 105, 68, 39, - 83, 43, 94, 32, 156, 1, 8, 69, 86, 69, 82, 83, 69, 68, 32, 160, 111, 3, - 85, 77, 32, 243, 141, 4, 65, 28, 40, 5, 87, 73, 84, 72, 32, 159, 112, 82, - 26, 194, 78, 67, 230, 28, 68, 170, 2, 84, 174, 48, 65, 138, 1, 76, 194, - 167, 3, 73, 182, 166, 4, 79, 231, 172, 12, 83, 8, 162, 110, 72, 230, 150, - 4, 79, 202, 183, 13, 67, 155, 140, 3, 69, 49, 136, 1, 6, 32, 87, 73, 84, - 72, 32, 136, 1, 5, 77, 65, 76, 76, 32, 184, 115, 2, 65, 76, 234, 1, 72, - 152, 2, 2, 73, 71, 243, 132, 4, 67, 32, 86, 67, 210, 111, 65, 118, 68, - 138, 1, 83, 174, 1, 86, 238, 246, 7, 79, 143, 137, 11, 72, 10, 170, 112, - 65, 230, 10, 69, 82, 79, 203, 31, 73, 4, 152, 146, 19, 11, 81, 32, 87, - 73, 84, 72, 32, 72, 79, 79, 75, 233, 243, 1, 7, 67, 65, 80, 73, 84, 65, - 76, 59, 136, 1, 6, 32, 87, 73, 84, 72, 32, 168, 1, 6, 85, 82, 78, 69, 68, - 32, 132, 123, 2, 72, 79, 176, 1, 2, 79, 78, 74, 82, 251, 197, 20, 90, 22, - 82, 67, 50, 68, 198, 152, 1, 76, 182, 232, 3, 82, 206, 238, 14, 72, 219, - 163, 1, 83, 8, 146, 120, 69, 22, 73, 62, 79, 199, 165, 19, 65, 6, 134, - 142, 1, 73, 171, 154, 16, 79, 18, 178, 40, 73, 146, 133, 3, 65, 178, 149, - 18, 72, 2, 75, 2, 76, 2, 77, 2, 84, 3, 86, 79, 26, 32, 203, 129, 5, 80, - 74, 44, 5, 87, 73, 84, 72, 32, 155, 160, 20, 66, 72, 238, 131, 1, 67, 74, - 68, 150, 2, 72, 170, 1, 77, 134, 1, 79, 142, 1, 84, 186, 9, 71, 186, 2, - 65, 202, 168, 3, 73, 62, 66, 242, 209, 14, 82, 239, 128, 2, 83, 23, 88, - 6, 32, 87, 73, 84, 72, 32, 250, 138, 1, 73, 66, 79, 170, 166, 19, 69, - 251, 141, 1, 89, 8, 170, 138, 1, 68, 210, 209, 16, 84, 139, 144, 2, 72, - 19, 48, 6, 32, 87, 73, 84, 72, 32, 251, 145, 7, 89, 14, 134, 144, 1, 67, - 18, 68, 70, 71, 186, 2, 65, 139, 216, 18, 72, 7, 145, 140, 1, 7, 32, 87, - 73, 84, 72, 32, 68, 29, 48, 6, 32, 87, 73, 84, 72, 32, 135, 153, 17, 79, - 24, 226, 142, 1, 67, 18, 68, 70, 71, 22, 72, 42, 76, 254, 1, 65, 186, - 194, 3, 77, 202, 133, 13, 84, 227, 179, 3, 83, 25, 33, 6, 32, 87, 73, 84, - 72, 32, 22, 186, 57, 67, 162, 87, 65, 102, 68, 38, 76, 34, 83, 190, 226, - 3, 80, 167, 244, 14, 72, 4, 170, 207, 10, 73, 231, 213, 10, 79, 12, 128, - 1, 5, 65, 82, 67, 72, 65, 30, 73, 64, 9, 82, 69, 86, 69, 82, 83, 69, 68, - 32, 245, 204, 10, 7, 83, 73, 68, 69, 87, 65, 89, 2, 177, 235, 13, 2, 73, - 67, 4, 164, 107, 5, 78, 86, 69, 82, 84, 221, 205, 14, 3, 32, 76, 79, 4, - 222, 185, 21, 70, 3, 80, 138, 1, 198, 3, 65, 36, 2, 66, 73, 152, 1, 21, - 73, 78, 86, 69, 82, 84, 69, 68, 32, 71, 76, 79, 84, 84, 65, 76, 32, 83, - 84, 79, 80, 72, 2, 82, 69, 222, 1, 83, 128, 8, 3, 84, 87, 79, 178, 20, - 87, 216, 163, 4, 2, 68, 69, 148, 5, 2, 76, 65, 206, 11, 71, 128, 176, 1, - 21, 80, 72, 65, 82, 89, 78, 71, 69, 65, 76, 32, 86, 79, 73, 67, 69, 68, - 32, 70, 82, 73, 164, 131, 14, 20, 86, 79, 73, 67, 69, 68, 32, 76, 65, 82, - 89, 78, 71, 69, 65, 76, 32, 83, 80, 73, 179, 117, 89, 4, 174, 181, 4, 76, - 235, 176, 16, 73, 6, 76, 7, 76, 65, 66, 73, 65, 76, 32, 29, 8, 68, 69, - 78, 84, 65, 76, 32, 80, 4, 26, 80, 175, 200, 4, 67, 2, 161, 129, 16, 6, - 69, 82, 67, 85, 83, 83, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 170, 237, 4, - 67, 243, 150, 16, 83, 8, 104, 7, 86, 69, 82, 83, 69, 68, 32, 185, 237, 4, - 13, 84, 82, 79, 70, 76, 69, 88, 32, 67, 76, 73, 67, 75, 4, 80, 3, 69, 83, - 72, 157, 8, 12, 71, 76, 79, 84, 84, 65, 76, 32, 83, 84, 79, 80, 2, 217, - 132, 1, 2, 32, 76, 96, 172, 1, 13, 77, 65, 76, 76, 32, 67, 65, 80, 73, - 84, 65, 76, 32, 248, 111, 10, 84, 82, 69, 84, 67, 72, 69, 68, 32, 67, - 221, 175, 19, 10, 73, 78, 79, 76, 79, 71, 73, 67, 65, 76, 90, 230, 1, 69, - 30, 73, 22, 76, 74, 79, 42, 82, 122, 84, 210, 176, 4, 66, 214, 41, 71, - 166, 146, 16, 65, 162, 64, 67, 2, 68, 2, 70, 2, 72, 2, 74, 2, 75, 2, 77, - 2, 78, 2, 80, 2, 81, 2, 83, 2, 85, 2, 86, 2, 87, 2, 89, 3, 90, 7, 238, - 173, 21, 84, 3, 90, 5, 227, 213, 4, 78, 7, 33, 6, 32, 87, 73, 84, 72, 32, - 4, 162, 219, 8, 66, 143, 163, 12, 83, 9, 242, 94, 80, 150, 207, 20, 69, - 3, 85, 11, 88, 8, 69, 86, 69, 82, 83, 69, 68, 32, 244, 127, 5, 32, 87, - 73, 84, 72, 187, 156, 20, 85, 4, 130, 173, 21, 78, 3, 82, 13, 33, 6, 85, - 82, 78, 69, 68, 32, 10, 194, 172, 21, 69, 2, 71, 2, 75, 2, 77, 3, 82, - 184, 11, 136, 1, 5, 77, 65, 76, 76, 32, 153, 130, 1, 22, 85, 66, 83, 67, - 82, 73, 80, 84, 32, 83, 77, 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, 32, - 148, 11, 76, 15, 67, 65, 80, 73, 84, 65, 76, 32, 76, 69, 84, 84, 69, 82, - 32, 43, 76, 4, 18, 73, 3, 85, 2, 211, 220, 4, 32, 144, 11, 76, 6, 69, 84, - 84, 69, 82, 32, 173, 127, 8, 73, 71, 65, 84, 85, 82, 69, 32, 254, 10, - 182, 2, 65, 190, 5, 66, 198, 3, 67, 186, 4, 68, 182, 4, 69, 174, 9, 70, - 214, 1, 71, 162, 2, 72, 230, 2, 73, 166, 7, 74, 182, 1, 75, 178, 2, 76, - 206, 6, 77, 178, 2, 78, 218, 3, 79, 242, 8, 80, 230, 1, 81, 174, 1, 82, - 142, 8, 83, 250, 10, 84, 246, 13, 85, 218, 9, 86, 130, 3, 87, 110, 88, - 226, 2, 89, 171, 3, 90, 101, 118, 32, 198, 3, 69, 82, 78, 152, 220, 4, 4, - 76, 80, 72, 65, 158, 163, 15, 86, 182, 162, 1, 65, 2, 79, 2, 85, 3, 89, - 72, 88, 5, 87, 73, 84, 72, 32, 225, 222, 5, 11, 82, 69, 86, 69, 82, 83, - 69, 68, 45, 83, 67, 70, 150, 1, 66, 34, 68, 54, 82, 170, 34, 77, 250, 21, - 67, 162, 49, 72, 158, 1, 79, 198, 10, 71, 186, 2, 65, 202, 168, 3, 73, - 186, 159, 13, 84, 227, 179, 3, 83, 12, 173, 105, 4, 82, 69, 86, 69, 12, - 22, 79, 151, 57, 73, 8, 214, 57, 84, 223, 12, 85, 10, 26, 73, 135, 225, - 4, 69, 8, 26, 78, 139, 168, 4, 71, 6, 17, 2, 71, 32, 6, 236, 58, 4, 65, - 66, 79, 86, 235, 201, 18, 66, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 254, - 115, 71, 186, 2, 65, 187, 194, 3, 77, 2, 165, 191, 10, 7, 71, 76, 73, 67, - 65, 78, 65, 41, 140, 1, 6, 32, 87, 73, 84, 72, 32, 130, 1, 65, 108, 11, - 76, 65, 67, 75, 76, 69, 84, 84, 69, 82, 32, 38, 82, 134, 195, 4, 79, 203, - 182, 16, 69, 18, 110, 84, 142, 60, 70, 218, 56, 76, 178, 219, 3, 77, 174, - 7, 80, 134, 173, 12, 68, 162, 199, 2, 72, 219, 163, 1, 83, 2, 255, 7, 79, - 8, 64, 5, 82, 82, 69, 68, 32, 173, 80, 6, 83, 69, 76, 73, 78, 69, 6, 182, - 32, 65, 174, 254, 20, 69, 3, 79, 6, 202, 189, 4, 79, 243, 224, 16, 69, 2, - 165, 179, 10, 4, 79, 75, 69, 78, 47, 108, 6, 32, 87, 73, 84, 72, 32, 196, - 1, 2, 72, 73, 92, 6, 76, 79, 83, 69, 68, 32, 90, 85, 175, 202, 20, 79, - 24, 102, 67, 194, 112, 65, 230, 227, 3, 80, 218, 5, 82, 206, 238, 14, 72, - 158, 50, 66, 238, 32, 68, 211, 80, 83, 10, 54, 69, 202, 112, 73, 230, - 245, 6, 85, 155, 144, 12, 65, 4, 245, 51, 5, 68, 73, 76, 76, 65, 7, 49, - 10, 32, 87, 73, 84, 72, 32, 76, 79, 87, 32, 4, 174, 106, 82, 45, 4, 76, - 69, 70, 84, 8, 34, 73, 18, 79, 247, 193, 4, 82, 2, 175, 87, 78, 4, 218, - 214, 4, 80, 231, 140, 9, 77, 4, 37, 7, 65, 84, 82, 73, 76, 76, 79, 5, - 253, 130, 13, 5, 32, 87, 73, 84, 72, 73, 96, 6, 32, 87, 73, 84, 72, 32, - 182, 1, 69, 34, 79, 178, 1, 90, 138, 207, 4, 66, 247, 181, 16, 85, 32, - 98, 83, 34, 84, 238, 32, 67, 238, 44, 77, 170, 31, 76, 214, 211, 3, 72, - 138, 15, 80, 135, 173, 12, 68, 4, 146, 67, 72, 243, 164, 20, 84, 4, 26, - 79, 231, 227, 18, 65, 2, 243, 245, 19, 80, 8, 130, 78, 90, 215, 164, 20, - 76, 16, 60, 6, 84, 76, 69, 83, 83, 32, 49, 5, 85, 66, 76, 69, 32, 8, 26, - 74, 171, 150, 21, 73, 7, 255, 191, 4, 32, 8, 42, 87, 230, 195, 4, 82, - 199, 238, 5, 84, 2, 239, 232, 6, 89, 11, 11, 32, 8, 26, 87, 239, 191, 4, - 68, 2, 181, 89, 5, 73, 84, 72, 32, 67, 119, 112, 6, 32, 87, 73, 84, 72, - 32, 214, 4, 71, 72, 2, 78, 71, 68, 2, 83, 72, 164, 1, 2, 90, 72, 247, - 236, 16, 84, 76, 182, 1, 67, 158, 2, 68, 110, 78, 214, 15, 84, 158, 23, - 77, 250, 1, 86, 194, 3, 70, 186, 41, 72, 158, 1, 79, 198, 10, 71, 186, 2, - 65, 202, 168, 3, 73, 62, 66, 186, 64, 82, 167, 146, 16, 83, 24, 92, 6, - 69, 68, 73, 76, 76, 65, 32, 9, 73, 82, 67, 85, 77, 70, 76, 69, 88, 191, - 236, 19, 65, 5, 137, 143, 4, 3, 32, 65, 78, 19, 11, 32, 16, 40, 4, 65, - 78, 68, 32, 151, 243, 18, 66, 14, 174, 85, 67, 130, 2, 72, 226, 11, 71, - 186, 2, 65, 186, 194, 3, 77, 250, 169, 4, 68, 211, 219, 8, 84, 12, 22, - 79, 239, 97, 73, 10, 28, 2, 84, 32, 239, 50, 85, 8, 204, 87, 5, 65, 66, - 79, 86, 69, 171, 154, 18, 66, 2, 167, 142, 14, 79, 4, 221, 255, 6, 13, - 89, 80, 84, 79, 76, 79, 71, 73, 67, 65, 76, 32, 65, 7, 33, 6, 32, 87, 73, - 84, 72, 32, 4, 246, 188, 4, 67, 231, 9, 80, 13, 33, 6, 32, 87, 73, 84, - 72, 32, 10, 88, 10, 68, 79, 85, 66, 76, 69, 32, 66, 65, 82, 190, 197, 4, - 80, 142, 1, 67, 207, 4, 82, 5, 177, 198, 4, 4, 32, 65, 78, 68, 15, 11, - 32, 12, 38, 82, 37, 5, 87, 73, 84, 72, 32, 2, 193, 138, 14, 4, 69, 86, - 69, 82, 10, 54, 67, 138, 196, 4, 80, 218, 5, 82, 195, 142, 14, 84, 4, - 198, 214, 7, 85, 155, 144, 12, 65, 17, 84, 6, 32, 87, 73, 84, 72, 32, 73, - 11, 69, 78, 71, 32, 68, 73, 71, 82, 65, 80, 72, 10, 222, 187, 4, 77, 174, - 7, 80, 166, 244, 14, 72, 138, 83, 68, 211, 80, 83, 5, 181, 146, 18, 8, - 32, 87, 73, 84, 72, 32, 84, 82, 37, 72, 6, 32, 87, 73, 84, 72, 32, 126, - 76, 202, 182, 18, 65, 195, 207, 2, 72, 22, 218, 3, 67, 134, 90, 65, 134, - 169, 3, 66, 182, 25, 77, 174, 33, 80, 154, 235, 3, 79, 142, 137, 11, 72, - 138, 83, 68, 211, 80, 83, 8, 33, 6, 79, 84, 84, 65, 76, 32, 8, 242, 182, - 18, 83, 170, 209, 2, 65, 2, 73, 3, 85, 39, 140, 1, 6, 32, 87, 73, 84, 72, - 32, 162, 44, 65, 232, 25, 12, 79, 79, 75, 69, 68, 32, 83, 67, 72, 87, 65, - 32, 250, 237, 3, 69, 219, 210, 16, 86, 24, 86, 66, 34, 67, 46, 68, 226, - 90, 76, 222, 226, 3, 80, 166, 244, 14, 72, 219, 163, 1, 83, 2, 233, 135, - 13, 3, 82, 69, 86, 6, 170, 58, 69, 154, 32, 73, 255, 133, 19, 65, 8, 246, - 86, 73, 174, 185, 3, 69, 171, 218, 12, 79, 75, 80, 6, 32, 87, 73, 84, 72, - 32, 222, 4, 78, 188, 1, 2, 79, 84, 155, 254, 20, 83, 48, 178, 1, 67, 34, - 68, 210, 1, 77, 64, 6, 79, 71, 79, 78, 69, 75, 78, 84, 142, 70, 72, 226, - 11, 71, 186, 2, 65, 202, 168, 3, 73, 62, 66, 136, 64, 6, 83, 84, 82, 79, - 75, 69, 51, 82, 4, 222, 87, 73, 255, 133, 19, 65, 14, 18, 73, 47, 79, 4, - 217, 26, 7, 65, 69, 82, 69, 83, 73, 83, 10, 28, 2, 84, 32, 227, 36, 85, - 8, 64, 10, 65, 66, 79, 86, 69, 32, 65, 78, 68, 32, 171, 227, 18, 66, 6, - 162, 83, 71, 186, 2, 65, 131, 200, 16, 84, 4, 29, 5, 65, 67, 82, 79, 78, - 5, 229, 35, 4, 32, 65, 78, 68, 7, 157, 72, 15, 32, 65, 78, 68, 32, 68, - 79, 84, 32, 65, 66, 79, 86, 69, 32, 4, 21, 3, 73, 76, 68, 4, 211, 135, 5, - 69, 16, 46, 83, 93, 7, 86, 69, 82, 84, 69, 68, 32, 12, 29, 5, 85, 76, 65, - 82, 32, 12, 130, 255, 20, 68, 2, 70, 2, 71, 2, 82, 2, 83, 3, 84, 4, 26, - 65, 219, 231, 20, 79, 2, 247, 165, 17, 76, 6, 166, 157, 4, 65, 129, 186, - 6, 5, 73, 70, 73, 69, 68, 13, 33, 6, 32, 87, 73, 84, 72, 32, 10, 94, 67, - 236, 173, 4, 13, 68, 79, 84, 32, 65, 66, 79, 86, 69, 32, 65, 78, 68, 247, - 158, 16, 83, 6, 190, 81, 73, 206, 217, 3, 82, 179, 172, 15, 65, 29, 48, - 6, 32, 87, 73, 84, 72, 32, 195, 249, 20, 82, 24, 98, 67, 34, 68, 50, 83, - 234, 78, 65, 138, 1, 76, 222, 226, 3, 80, 154, 235, 3, 79, 143, 137, 11, - 72, 4, 222, 47, 69, 151, 166, 19, 65, 6, 226, 69, 73, 130, 192, 3, 69, - 243, 181, 4, 79, 4, 29, 5, 84, 82, 79, 75, 69, 5, 173, 24, 6, 32, 65, 78, - 68, 32, 68, 79, 136, 1, 6, 32, 87, 73, 84, 72, 32, 250, 3, 65, 38, 69, - 48, 5, 79, 78, 71, 32, 83, 218, 173, 4, 83, 2, 90, 246, 181, 16, 85, 219, - 16, 74, 50, 178, 1, 66, 86, 67, 56, 2, 68, 79, 100, 3, 77, 73, 68, 130, - 2, 72, 242, 71, 65, 138, 1, 76, 226, 218, 3, 73, 162, 1, 82, 222, 2, 70, - 130, 4, 80, 158, 228, 12, 84, 227, 179, 3, 83, 6, 36, 3, 69, 76, 84, 187, - 190, 20, 65, 5, 153, 175, 4, 6, 32, 65, 78, 68, 32, 80, 8, 178, 43, 69, - 22, 73, 234, 149, 7, 85, 155, 144, 12, 65, 8, 38, 84, 25, 5, 85, 66, 76, - 69, 32, 4, 153, 24, 2, 32, 66, 4, 202, 166, 4, 77, 239, 173, 15, 66, 8, - 36, 4, 68, 76, 69, 32, 219, 43, 45, 6, 198, 145, 17, 84, 218, 146, 3, 82, - 79, 68, 4, 133, 148, 4, 4, 77, 66, 68, 65, 6, 246, 174, 4, 90, 205, 250, - 12, 3, 78, 73, 83, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 18, 68, 35, 72, - 4, 218, 62, 73, 211, 180, 19, 79, 2, 157, 166, 4, 2, 73, 71, 27, 56, 6, - 32, 87, 73, 84, 72, 32, 102, 73, 187, 225, 20, 85, 16, 150, 71, 65, 130, - 218, 3, 67, 186, 2, 77, 174, 7, 80, 134, 173, 12, 68, 154, 55, 84, 139, - 144, 2, 72, 6, 25, 4, 68, 68, 76, 69, 6, 76, 7, 45, 87, 69, 76, 83, 72, - 32, 149, 252, 16, 6, 32, 83, 67, 79, 84, 83, 4, 238, 231, 19, 76, 183, - 137, 1, 86, 49, 58, 32, 216, 2, 2, 71, 32, 150, 221, 20, 85, 219, 16, 74, - 40, 88, 5, 87, 73, 84, 72, 32, 153, 136, 6, 11, 80, 82, 69, 67, 69, 68, - 69, 68, 32, 66, 89, 38, 122, 67, 74, 76, 170, 36, 77, 234, 27, 71, 186, - 2, 65, 102, 68, 130, 227, 3, 80, 218, 5, 82, 194, 229, 3, 79, 135, 249, - 8, 84, 10, 182, 35, 69, 22, 73, 210, 249, 3, 82, 154, 156, 3, 85, 155, - 144, 12, 65, 6, 144, 65, 3, 79, 78, 71, 202, 2, 73, 131, 234, 3, 69, 2, - 25, 4, 87, 73, 84, 72, 2, 153, 202, 17, 5, 32, 84, 73, 76, 68, 115, 116, - 6, 32, 87, 73, 84, 72, 32, 186, 6, 76, 52, 4, 80, 69, 78, 32, 214, 174, - 13, 77, 242, 182, 7, 73, 2, 79, 3, 85, 86, 154, 1, 67, 70, 68, 152, 1, 2, - 76, 79, 86, 77, 46, 79, 38, 83, 66, 84, 106, 86, 234, 43, 72, 242, 12, - 71, 186, 2, 65, 202, 168, 3, 73, 62, 66, 187, 64, 82, 14, 184, 48, 9, 73, - 82, 67, 85, 77, 70, 76, 69, 88, 187, 149, 19, 65, 14, 18, 73, 47, 79, 4, - 233, 12, 7, 65, 69, 82, 69, 83, 73, 83, 10, 22, 84, 183, 46, 85, 6, 11, - 32, 6, 152, 12, 5, 65, 66, 79, 86, 69, 195, 191, 18, 66, 6, 66, 78, 132, - 197, 12, 6, 87, 32, 82, 73, 78, 71, 231, 163, 8, 79, 2, 171, 20, 71, 6, - 11, 65, 6, 189, 2, 4, 67, 82, 79, 78, 4, 229, 10, 5, 71, 79, 78, 69, 75, - 4, 25, 4, 84, 82, 79, 75, 4, 11, 69, 5, 225, 48, 2, 32, 65, 8, 25, 4, 73, - 76, 68, 69, 9, 29, 5, 32, 65, 78, 68, 32, 6, 174, 46, 68, 142, 13, 65, - 187, 194, 3, 77, 6, 81, 18, 69, 82, 84, 73, 67, 65, 76, 32, 76, 73, 78, - 69, 32, 66, 69, 76, 79, 87, 7, 233, 42, 4, 32, 65, 78, 68, 2, 137, 129, - 10, 8, 68, 32, 80, 79, 76, 73, 83, 72, 16, 26, 79, 219, 159, 4, 69, 13, - 48, 6, 32, 87, 73, 84, 72, 32, 247, 228, 20, 69, 8, 222, 54, 71, 186, 2, - 65, 190, 233, 3, 82, 167, 146, 16, 83, 23, 48, 6, 32, 87, 73, 84, 72, 32, - 175, 208, 20, 72, 18, 86, 70, 34, 83, 178, 55, 65, 186, 220, 3, 77, 174, - 7, 80, 166, 244, 14, 72, 139, 83, 68, 2, 137, 185, 14, 3, 76, 79, 85, 6, - 218, 27, 84, 241, 164, 12, 6, 81, 85, 73, 82, 82, 69, 13, 48, 6, 32, 87, - 73, 84, 72, 32, 215, 155, 4, 80, 8, 42, 68, 16, 4, 72, 79, 79, 75, 23, - 83, 2, 227, 44, 73, 5, 247, 173, 18, 32, 2, 197, 26, 6, 84, 82, 79, 75, - 69, 32, 77, 90, 32, 228, 4, 8, 69, 86, 69, 82, 83, 69, 68, 32, 148, 2, 2, - 85, 77, 255, 141, 4, 65, 46, 36, 4, 87, 73, 84, 72, 235, 6, 82, 44, 26, - 32, 215, 174, 15, 79, 42, 166, 1, 67, 50, 68, 200, 1, 8, 70, 73, 83, 72, - 72, 79, 79, 75, 66, 76, 34, 84, 142, 18, 77, 162, 30, 65, 202, 168, 3, - 73, 158, 59, 80, 154, 235, 3, 79, 231, 172, 12, 83, 6, 170, 19, 69, 230, - 249, 3, 82, 179, 172, 15, 65, 8, 11, 79, 8, 24, 2, 84, 32, 111, 85, 6, - 26, 66, 131, 225, 19, 65, 4, 25, 4, 69, 76, 79, 87, 5, 29, 5, 32, 65, 78, - 68, 32, 2, 139, 244, 3, 77, 2, 21, 3, 66, 76, 69, 2, 11, 32, 2, 227, 46, - 71, 7, 29, 5, 32, 65, 78, 68, 32, 4, 162, 141, 4, 77, 175, 7, 80, 4, 222, - 49, 73, 207, 230, 3, 79, 4, 178, 168, 18, 65, 163, 26, 73, 22, 162, 1, - 72, 40, 6, 79, 80, 69, 78, 32, 69, 164, 142, 4, 8, 82, 32, 87, 73, 84, - 72, 32, 70, 168, 2, 3, 83, 67, 82, 226, 206, 14, 69, 234, 86, 67, 139, - 164, 1, 75, 2, 11, 65, 2, 225, 244, 9, 2, 76, 70, 7, 33, 6, 32, 87, 73, - 84, 72, 32, 4, 214, 151, 4, 82, 207, 238, 14, 72, 5, 11, 32, 2, 11, 82, - 2, 149, 253, 16, 3, 79, 84, 85, 85, 180, 1, 6, 32, 87, 73, 84, 72, 32, - 218, 4, 65, 66, 67, 218, 1, 72, 34, 73, 156, 2, 13, 81, 85, 65, 84, 32, - 82, 69, 86, 69, 82, 83, 69, 68, 193, 229, 9, 6, 84, 73, 82, 82, 85, 80, - 40, 110, 65, 34, 67, 86, 68, 138, 1, 83, 174, 1, 86, 210, 9, 77, 134, - 130, 4, 80, 154, 235, 3, 79, 143, 137, 11, 72, 4, 205, 1, 4, 67, 85, 84, - 69, 12, 58, 65, 230, 10, 69, 82, 79, 202, 31, 73, 231, 245, 6, 85, 4, - 113, 3, 82, 79, 78, 8, 32, 3, 79, 84, 32, 207, 32, 73, 6, 26, 66, 215, - 216, 19, 65, 4, 25, 4, 69, 76, 79, 87, 5, 11, 32, 2, 181, 212, 19, 3, 65, - 78, 68, 4, 22, 72, 203, 42, 87, 2, 21, 3, 79, 82, 84, 2, 17, 2, 32, 83, - 2, 11, 84, 2, 21, 3, 82, 79, 75, 2, 11, 69, 2, 17, 2, 32, 79, 2, 233, - 199, 19, 4, 86, 69, 82, 76, 2, 37, 7, 69, 82, 84, 73, 67, 65, 76, 2, 205, - 40, 2, 32, 76, 4, 46, 76, 245, 190, 19, 5, 75, 72, 65, 32, 89, 2, 247, - 12, 84, 18, 48, 3, 72, 87, 65, 97, 5, 82, 73, 80, 84, 32, 11, 33, 6, 32, - 87, 73, 84, 72, 32, 8, 222, 35, 71, 186, 2, 65, 190, 233, 3, 82, 207, - 238, 14, 72, 8, 26, 82, 155, 255, 3, 71, 5, 253, 175, 11, 5, 32, 87, 73, - 84, 72, 2, 225, 219, 16, 3, 65, 82, 80, 14, 48, 7, 68, 69, 87, 65, 89, - 83, 32, 199, 1, 71, 12, 110, 79, 56, 4, 84, 85, 82, 78, 176, 195, 10, 11, - 68, 73, 65, 69, 82, 69, 83, 73, 90, 69, 68, 203, 139, 10, 85, 7, 26, 80, - 211, 129, 4, 32, 2, 165, 234, 9, 2, 69, 78, 2, 237, 255, 12, 2, 69, 68, - 2, 201, 217, 16, 4, 77, 79, 73, 68, 2, 247, 250, 9, 32, 147, 1, 188, 1, - 6, 32, 87, 73, 84, 72, 32, 192, 3, 2, 69, 83, 70, 72, 130, 2, 79, 102, - 82, 54, 85, 250, 253, 3, 67, 202, 1, 83, 132, 247, 10, 9, 65, 73, 76, 76, - 69, 83, 83, 32, 80, 131, 207, 5, 90, 34, 110, 67, 182, 1, 68, 66, 77, - 170, 31, 76, 222, 226, 3, 80, 168, 5, 4, 72, 79, 79, 75, 50, 82, 167, - 146, 16, 83, 10, 58, 69, 22, 73, 62, 79, 174, 149, 7, 85, 155, 144, 12, - 65, 2, 251, 157, 12, 68, 2, 37, 7, 82, 67, 85, 77, 70, 76, 69, 2, 179, - 157, 18, 88, 2, 17, 2, 77, 77, 2, 139, 157, 18, 65, 8, 32, 2, 73, 65, - 179, 175, 16, 79, 4, 154, 21, 71, 215, 6, 69, 4, 17, 2, 73, 68, 4, 26, - 45, 203, 250, 3, 68, 2, 225, 136, 4, 6, 72, 69, 73, 71, 72, 84, 6, 45, 9, - 72, 32, 68, 73, 71, 82, 65, 80, 72, 7, 191, 128, 4, 32, 8, 64, 12, 32, - 87, 73, 84, 72, 32, 83, 84, 82, 73, 75, 69, 43, 79, 2, 205, 163, 16, 5, - 84, 72, 82, 79, 85, 6, 17, 2, 82, 78, 7, 41, 8, 32, 87, 73, 84, 72, 32, - 83, 84, 4, 25, 4, 82, 79, 75, 69, 5, 11, 32, 2, 209, 209, 3, 6, 84, 72, - 82, 79, 85, 71, 8, 26, 78, 151, 128, 4, 80, 6, 17, 2, 69, 32, 6, 134, - 201, 4, 83, 230, 197, 9, 84, 255, 131, 1, 70, 2, 17, 2, 69, 83, 2, 11, - 73, 2, 135, 163, 20, 76, 76, 44, 5, 82, 78, 69, 68, 32, 151, 197, 20, 77, - 74, 162, 1, 68, 22, 72, 66, 73, 54, 79, 142, 1, 82, 110, 84, 22, 86, 146, - 252, 3, 65, 38, 77, 198, 2, 89, 142, 174, 16, 85, 218, 19, 69, 2, 71, 2, - 75, 2, 76, 3, 87, 2, 223, 138, 17, 69, 7, 217, 236, 3, 11, 32, 87, 73, - 84, 72, 32, 70, 73, 83, 72, 72, 5, 11, 78, 2, 169, 212, 9, 5, 83, 85, 76, - 65, 82, 12, 66, 69, 180, 225, 3, 7, 32, 79, 80, 69, 78, 45, 79, 159, 29, - 80, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 202, 157, 18, 72, 187, 244, 1, - 83, 15, 33, 6, 32, 87, 73, 84, 72, 32, 12, 146, 242, 3, 77, 174, 7, 80, - 130, 5, 76, 154, 143, 14, 84, 143, 96, 72, 5, 211, 249, 3, 32, 7, 11, 32, - 4, 161, 5, 4, 87, 73, 84, 72, 97, 90, 32, 228, 222, 3, 6, 80, 83, 73, 76, - 79, 78, 242, 224, 16, 69, 2, 73, 2, 77, 3, 79, 82, 48, 3, 66, 65, 82, 49, - 5, 87, 73, 84, 72, 32, 5, 245, 17, 8, 32, 87, 73, 84, 72, 32, 83, 72, 78, - 142, 1, 67, 74, 68, 150, 2, 72, 170, 1, 77, 134, 1, 79, 106, 82, 38, 84, - 186, 9, 71, 82, 83, 234, 1, 65, 202, 168, 3, 73, 62, 66, 135, 66, 76, 6, - 200, 197, 4, 9, 73, 82, 67, 85, 77, 70, 76, 69, 88, 247, 210, 14, 65, 18, - 52, 8, 73, 65, 69, 82, 69, 83, 73, 83, 131, 1, 79, 13, 11, 32, 10, 40, 4, - 65, 78, 68, 32, 155, 158, 18, 66, 8, 50, 67, 226, 13, 71, 186, 2, 65, - 187, 194, 3, 77, 2, 211, 150, 19, 65, 6, 26, 85, 131, 142, 18, 84, 4, 21, - 3, 66, 76, 69, 4, 11, 32, 4, 138, 13, 71, 187, 2, 65, 14, 11, 79, 14, 28, - 2, 82, 78, 191, 81, 79, 13, 29, 5, 32, 65, 78, 68, 32, 10, 66, 72, 226, - 11, 71, 186, 2, 65, 178, 236, 7, 68, 211, 219, 8, 84, 2, 213, 80, 2, 79, - 79, 10, 29, 5, 65, 67, 82, 79, 78, 11, 29, 5, 32, 65, 78, 68, 32, 8, 50, - 68, 214, 10, 71, 186, 2, 65, 131, 200, 16, 84, 2, 171, 10, 73, 6, 29, 5, - 71, 79, 78, 69, 75, 7, 11, 32, 4, 25, 4, 65, 78, 68, 32, 4, 178, 12, 65, - 131, 200, 16, 84, 4, 218, 245, 3, 69, 187, 145, 14, 73, 6, 25, 4, 73, 76, - 68, 69, 7, 11, 32, 4, 26, 65, 251, 152, 18, 66, 2, 17, 2, 78, 68, 2, 11, - 32, 2, 139, 11, 65, 29, 84, 6, 32, 87, 73, 84, 72, 32, 162, 1, 73, 66, - 79, 170, 166, 19, 69, 251, 141, 1, 89, 14, 82, 68, 182, 237, 3, 80, 142, - 1, 67, 146, 7, 82, 130, 220, 12, 84, 139, 144, 2, 72, 4, 26, 73, 243, - 245, 7, 79, 2, 17, 2, 65, 71, 2, 209, 132, 20, 2, 79, 78, 2, 41, 8, 83, - 73, 71, 79, 84, 72, 73, 67, 2, 135, 145, 16, 32, 6, 33, 6, 76, 65, 80, - 85, 75, 32, 6, 166, 157, 20, 65, 2, 79, 3, 85, 19, 33, 6, 32, 87, 73, 84, - 72, 32, 16, 202, 4, 67, 18, 68, 70, 71, 186, 2, 65, 246, 250, 17, 82, - 151, 93, 72, 17, 33, 6, 32, 87, 73, 84, 72, 32, 14, 42, 68, 32, 2, 76, - 79, 135, 234, 3, 80, 4, 222, 3, 73, 255, 173, 19, 79, 8, 60, 11, 78, 71, - 32, 76, 69, 70, 84, 32, 76, 69, 71, 79, 87, 7, 11, 32, 4, 60, 7, 65, 78, - 68, 32, 76, 79, 87, 65, 4, 87, 73, 84, 72, 2, 17, 2, 32, 82, 2, 21, 3, - 73, 71, 72, 2, 167, 143, 11, 84, 2, 193, 174, 19, 4, 32, 83, 69, 82, 33, - 48, 6, 32, 87, 73, 84, 72, 32, 147, 139, 16, 79, 28, 110, 67, 18, 68, 70, - 71, 22, 72, 42, 76, 22, 83, 234, 1, 65, 186, 194, 3, 77, 202, 133, 13, - 84, 247, 178, 1, 82, 2, 203, 3, 73, 6, 26, 73, 215, 147, 16, 79, 2, 17, - 2, 65, 69, 2, 151, 172, 16, 82, 2, 223, 194, 18, 82, 4, 17, 2, 79, 79, 4, - 171, 131, 5, 75, 2, 195, 180, 19, 79, 4, 26, 72, 187, 253, 19, 84, 2, 21, - 3, 79, 82, 84, 2, 233, 246, 7, 6, 32, 82, 73, 71, 72, 84, 31, 33, 6, 32, - 87, 73, 84, 72, 32, 28, 98, 65, 22, 67, 82, 68, 38, 76, 34, 83, 146, 219, - 3, 77, 174, 7, 80, 218, 5, 82, 207, 238, 14, 72, 2, 231, 179, 13, 67, 6, - 42, 73, 230, 245, 6, 85, 155, 144, 12, 65, 2, 241, 192, 11, 4, 82, 67, - 85, 77, 6, 230, 181, 3, 69, 171, 218, 12, 79, 2, 11, 73, 2, 179, 172, 12, - 78, 4, 26, 87, 171, 250, 19, 84, 2, 179, 133, 17, 65, 18, 90, 70, 242, - 188, 9, 73, 172, 9, 6, 76, 79, 78, 71, 32, 83, 226, 198, 10, 83, 219, 5, - 79, 10, 34, 70, 134, 169, 20, 73, 3, 76, 7, 130, 169, 20, 73, 3, 76, 36, - 150, 1, 83, 210, 167, 20, 65, 2, 69, 2, 72, 2, 73, 2, 74, 2, 75, 2, 76, - 2, 77, 2, 78, 2, 79, 2, 80, 2, 82, 2, 84, 2, 85, 2, 86, 3, 88, 5, 143, - 225, 4, 67, 224, 5, 204, 1, 2, 65, 70, 144, 1, 2, 70, 84, 210, 38, 79, - 20, 5, 80, 67, 72, 65, 32, 164, 8, 8, 83, 83, 45, 84, 72, 65, 78, 32, - 254, 167, 16, 68, 228, 23, 6, 86, 69, 76, 32, 83, 76, 134, 230, 2, 77, - 239, 79, 71, 6, 120, 3, 76, 69, 83, 204, 191, 1, 14, 32, 70, 76, 85, 84, - 84, 69, 82, 73, 78, 71, 32, 73, 78, 209, 212, 11, 3, 89, 32, 71, 2, 135, - 228, 14, 83, 134, 4, 58, 32, 250, 20, 45, 217, 2, 6, 87, 65, 82, 68, 83, - 32, 246, 1, 166, 2, 65, 234, 4, 66, 202, 1, 67, 96, 2, 68, 79, 112, 2, - 72, 65, 166, 3, 76, 46, 77, 38, 79, 102, 82, 146, 3, 83, 82, 84, 222, 1, - 87, 242, 176, 8, 70, 236, 5, 14, 74, 85, 83, 84, 73, 70, 73, 69, 68, 32, - 82, 73, 71, 72, 86, 78, 202, 1, 80, 153, 13, 10, 86, 69, 82, 84, 73, 67, - 65, 76, 32, 66, 28, 22, 78, 143, 4, 82, 22, 28, 2, 68, 32, 191, 3, 71, - 16, 96, 6, 76, 79, 87, 69, 82, 32, 32, 6, 82, 73, 71, 72, 84, 32, 89, 6, - 85, 80, 80, 69, 82, 32, 4, 202, 1, 65, 235, 175, 17, 79, 6, 50, 84, 209, - 250, 17, 6, 68, 79, 85, 66, 76, 69, 4, 250, 136, 17, 82, 147, 231, 1, 65, - 6, 82, 65, 53, 16, 79, 78, 69, 32, 69, 73, 71, 72, 84, 72, 32, 66, 76, - 79, 67, 75, 2, 237, 134, 17, 8, 78, 68, 32, 82, 73, 71, 72, 84, 5, 165, - 253, 18, 17, 32, 67, 79, 78, 84, 65, 73, 78, 73, 78, 71, 32, 66, 76, 65, - 67, 75, 6, 154, 182, 8, 69, 185, 1, 4, 76, 69, 32, 66, 6, 26, 67, 247, - 185, 8, 82, 2, 209, 185, 8, 5, 32, 76, 69, 83, 83, 12, 40, 4, 65, 82, 66, - 32, 143, 186, 8, 76, 8, 40, 4, 68, 79, 87, 78, 1, 2, 85, 80, 4, 57, 12, - 32, 82, 73, 71, 72, 84, 32, 66, 65, 82, 66, 32, 4, 240, 244, 16, 4, 68, - 79, 87, 78, 1, 2, 85, 80, 14, 210, 185, 8, 85, 166, 26, 79, 206, 231, 2, - 69, 233, 168, 2, 8, 76, 79, 83, 69, 68, 32, 69, 78, 10, 44, 5, 85, 66, - 76, 69, 32, 255, 185, 8, 84, 8, 178, 187, 8, 87, 190, 30, 65, 138, 128, - 3, 81, 131, 190, 4, 80, 38, 36, 3, 76, 70, 32, 247, 191, 8, 78, 36, 132, - 2, 6, 67, 73, 82, 67, 76, 69, 246, 186, 8, 66, 90, 70, 82, 72, 50, 82, - 58, 84, 70, 87, 246, 13, 76, 22, 85, 220, 185, 8, 30, 73, 78, 86, 69, 82, - 83, 69, 32, 77, 69, 68, 73, 85, 77, 32, 83, 72, 65, 68, 69, 32, 65, 78, - 68, 32, 82, 73, 71, 72, 84, 251, 26, 77, 11, 33, 6, 32, 87, 73, 84, 72, - 32, 8, 42, 84, 206, 243, 17, 70, 235, 210, 1, 68, 4, 242, 197, 5, 72, - 179, 128, 13, 87, 4, 152, 158, 1, 2, 85, 71, 199, 159, 7, 79, 2, 161, - 133, 19, 4, 85, 76, 84, 73, 8, 36, 3, 78, 69, 32, 199, 190, 8, 85, 6, - 190, 162, 17, 81, 146, 4, 69, 45, 5, 84, 72, 73, 82, 68, 30, 44, 5, 73, - 71, 72, 84, 32, 155, 191, 8, 65, 28, 152, 1, 5, 65, 82, 82, 79, 87, 132, - 1, 12, 68, 79, 85, 66, 76, 69, 32, 65, 82, 82, 79, 87, 30, 87, 222, 236, - 8, 79, 154, 128, 8, 66, 54, 83, 207, 68, 84, 11, 11, 32, 8, 72, 5, 87, - 73, 84, 72, 32, 165, 192, 18, 7, 84, 72, 82, 79, 85, 71, 72, 6, 182, 174, - 16, 68, 242, 179, 3, 86, 79, 83, 7, 201, 228, 8, 2, 32, 87, 4, 210, 247, - 8, 65, 215, 176, 8, 72, 34, 174, 189, 8, 45, 70, 69, 78, 73, 168, 1, 3, - 80, 69, 69, 26, 81, 227, 2, 85, 26, 106, 82, 134, 194, 8, 72, 174, 212, - 7, 79, 208, 133, 1, 8, 87, 79, 32, 84, 72, 73, 82, 68, 219, 196, 1, 65, - 6, 40, 4, 73, 65, 78, 71, 255, 196, 8, 65, 4, 236, 236, 4, 8, 76, 69, 32, - 66, 69, 83, 73, 68, 207, 175, 12, 85, 16, 162, 199, 8, 72, 202, 1, 73, - 169, 184, 10, 2, 82, 73, 62, 128, 1, 9, 80, 79, 73, 78, 84, 73, 78, 71, - 32, 138, 1, 83, 206, 199, 8, 70, 226, 2, 72, 153, 7, 7, 84, 79, 45, 82, - 73, 71, 72, 30, 86, 65, 214, 204, 8, 67, 94, 68, 58, 77, 58, 82, 182, 1, - 83, 74, 84, 255, 157, 8, 69, 6, 146, 205, 8, 78, 202, 160, 8, 84, 159, 2, - 73, 4, 44, 5, 73, 68, 69, 32, 65, 131, 208, 8, 72, 2, 253, 142, 1, 2, 82, - 67, 210, 1, 172, 1, 5, 65, 82, 82, 79, 87, 174, 5, 66, 70, 72, 134, 4, - 84, 178, 2, 87, 226, 207, 8, 68, 210, 1, 70, 222, 7, 76, 26, 79, 34, 80, - 50, 82, 70, 83, 230, 191, 8, 67, 47, 81, 73, 26, 32, 143, 215, 17, 45, - 68, 102, 65, 138, 1, 84, 132, 2, 5, 87, 73, 84, 72, 32, 198, 209, 8, 70, - 181, 149, 10, 4, 79, 86, 69, 82, 12, 44, 5, 66, 79, 86, 69, 32, 179, 211, - 8, 78, 10, 64, 4, 83, 72, 79, 82, 170, 210, 8, 82, 142, 226, 4, 65, 55, - 84, 2, 143, 233, 18, 84, 12, 56, 7, 72, 82, 79, 85, 71, 72, 32, 53, 3, - 79, 32, 66, 6, 142, 178, 13, 83, 202, 196, 4, 76, 247, 145, 2, 88, 6, 32, - 2, 65, 82, 187, 212, 8, 76, 5, 169, 158, 12, 23, 32, 79, 86, 69, 82, 32, - 82, 73, 71, 72, 84, 87, 65, 82, 68, 83, 32, 65, 82, 82, 79, 87, 32, 36, - 118, 76, 154, 212, 8, 68, 182, 1, 80, 30, 83, 38, 84, 178, 195, 8, 77, - 38, 78, 122, 69, 226, 151, 1, 72, 143, 163, 1, 86, 4, 158, 153, 17, 65, - 219, 243, 1, 79, 8, 190, 214, 8, 65, 224, 10, 5, 79, 84, 84, 79, 77, 207, - 185, 8, 76, 30, 26, 65, 255, 159, 17, 69, 26, 48, 6, 82, 80, 79, 79, 78, - 32, 183, 190, 19, 78, 24, 100, 10, 87, 73, 84, 72, 32, 66, 65, 82, 66, - 32, 209, 216, 8, 9, 79, 86, 69, 82, 32, 82, 73, 71, 72, 22, 40, 4, 68, - 79, 87, 78, 117, 2, 85, 80, 10, 26, 32, 219, 165, 17, 87, 8, 222, 217, 8, - 66, 188, 2, 7, 65, 66, 79, 86, 69, 32, 82, 222, 193, 8, 70, 179, 5, 84, - 12, 26, 32, 231, 164, 17, 87, 10, 60, 6, 65, 66, 79, 86, 69, 32, 198, - 156, 17, 70, 179, 5, 84, 6, 42, 76, 201, 217, 8, 4, 82, 73, 71, 72, 4, - 238, 215, 8, 69, 199, 218, 4, 79, 54, 44, 2, 82, 73, 190, 221, 8, 79, - 159, 5, 87, 34, 44, 5, 65, 78, 71, 76, 69, 243, 225, 8, 80, 30, 56, 8, - 45, 72, 69, 65, 68, 69, 68, 32, 227, 170, 17, 32, 28, 52, 5, 65, 82, 82, - 79, 87, 178, 163, 17, 68, 39, 80, 25, 11, 32, 22, 180, 222, 8, 9, 79, 86, - 69, 82, 32, 82, 73, 71, 72, 22, 87, 251, 192, 8, 84, 6, 26, 72, 131, 228, - 8, 65, 4, 45, 9, 73, 84, 69, 32, 65, 82, 82, 79, 87, 5, 149, 168, 17, 2, - 32, 87, 5, 143, 231, 18, 80, 148, 1, 252, 1, 15, 67, 79, 78, 83, 79, 78, - 65, 78, 84, 32, 83, 73, 71, 78, 32, 124, 7, 76, 69, 84, 84, 69, 82, 32, - 236, 1, 12, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 198, 1, 83, - 172, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 251, 132, 18, 68, - 18, 66, 75, 22, 78, 154, 251, 19, 76, 2, 77, 2, 80, 2, 82, 3, 84, 5, 179, - 170, 19, 65, 5, 233, 201, 12, 4, 89, 73, 78, 45, 78, 194, 1, 75, 2, 80, - 190, 148, 7, 68, 194, 219, 10, 66, 2, 70, 2, 71, 2, 72, 2, 77, 138, 15, - 78, 194, 178, 1, 84, 46, 67, 2, 83, 138, 69, 74, 2, 76, 2, 82, 2, 86, 2, - 87, 2, 89, 187, 2, 65, 6, 246, 246, 19, 72, 2, 76, 187, 2, 65, 10, 82, - 84, 40, 14, 78, 89, 69, 84, 32, 84, 72, 89, 79, 79, 77, 32, 84, 65, 43, - 67, 6, 38, 65, 21, 5, 83, 72, 79, 79, 75, 2, 179, 248, 6, 45, 5, 17, 2, - 32, 67, 2, 217, 134, 15, 3, 69, 82, 45, 8, 92, 4, 73, 71, 78, 32, 37, 15, - 85, 66, 74, 79, 73, 78, 69, 68, 32, 76, 69, 84, 84, 69, 82, 4, 130, 227, - 15, 78, 227, 201, 2, 82, 4, 11, 32, 4, 250, 243, 19, 82, 3, 89, 14, 238, - 162, 16, 85, 202, 141, 1, 79, 170, 195, 2, 65, 186, 2, 69, 3, 73, 52, - 138, 1, 65, 128, 4, 11, 69, 81, 85, 65, 76, 32, 84, 79, 32, 79, 82, 200, - 1, 2, 66, 85, 42, 67, 186, 1, 79, 210, 2, 87, 231, 227, 18, 83, 16, 40, - 5, 66, 79, 86, 69, 32, 171, 4, 78, 12, 152, 1, 7, 71, 82, 69, 65, 84, 69, - 82, 110, 83, 176, 1, 19, 68, 79, 85, 66, 76, 69, 45, 76, 73, 78, 69, 32, - 69, 81, 85, 65, 76, 32, 65, 227, 212, 15, 76, 2, 181, 5, 23, 45, 84, 72, - 65, 78, 32, 65, 66, 79, 86, 69, 32, 68, 79, 85, 66, 76, 69, 45, 76, 73, - 78, 69, 6, 152, 1, 7, 73, 77, 73, 76, 65, 82, 32, 93, 26, 76, 65, 78, 84, - 69, 68, 32, 69, 81, 85, 65, 76, 32, 65, 66, 79, 86, 69, 32, 71, 82, 69, - 65, 84, 69, 82, 4, 18, 65, 59, 79, 2, 25, 4, 66, 79, 86, 69, 2, 169, 222, - 17, 2, 32, 71, 2, 227, 2, 82, 2, 145, 2, 6, 45, 84, 72, 65, 78, 32, 4, - 17, 2, 68, 32, 4, 220, 3, 5, 78, 79, 84, 32, 65, 245, 151, 13, 11, 83, - 73, 78, 71, 76, 69, 45, 76, 73, 78, 69, 4, 241, 249, 12, 5, 84, 32, 78, - 79, 84, 4, 65, 14, 76, 79, 83, 69, 68, 32, 66, 89, 32, 67, 85, 82, 86, - 69, 5, 11, 32, 2, 61, 13, 65, 66, 79, 86, 69, 32, 83, 76, 65, 78, 84, 69, - 68, 2, 17, 2, 32, 69, 2, 251, 228, 14, 81, 18, 40, 2, 82, 32, 141, 220, - 11, 2, 86, 69, 16, 114, 65, 48, 16, 83, 76, 65, 78, 84, 69, 68, 32, 69, - 81, 85, 65, 76, 32, 84, 79, 218, 246, 12, 69, 223, 226, 4, 71, 2, 173, - 184, 9, 7, 80, 80, 82, 79, 88, 73, 77, 9, 49, 10, 32, 87, 73, 84, 72, 32, - 68, 79, 84, 32, 6, 26, 65, 163, 199, 11, 73, 4, 25, 4, 66, 79, 86, 69, 5, - 231, 153, 18, 32, 6, 25, 4, 73, 84, 72, 32, 6, 66, 67, 40, 8, 81, 85, 69, - 83, 84, 73, 79, 78, 143, 153, 19, 68, 2, 249, 197, 11, 5, 73, 82, 67, 76, - 69, 2, 17, 2, 32, 77, 2, 17, 2, 65, 82, 2, 151, 236, 18, 75, 136, 11, - 190, 1, 71, 186, 5, 77, 166, 7, 78, 168, 65, 2, 80, 83, 20, 3, 83, 85, - 32, 238, 199, 15, 82, 136, 35, 11, 86, 82, 69, 32, 84, 79, 85, 82, 78, - 79, 73, 210, 59, 79, 242, 219, 1, 90, 187, 82, 66, 44, 26, 65, 57, 2, 72, - 84, 2, 245, 167, 9, 9, 84, 85, 82, 69, 32, 79, 80, 69, 78, 42, 38, 32, - 137, 4, 4, 78, 73, 78, 71, 36, 146, 1, 69, 38, 70, 126, 82, 40, 3, 76, - 69, 70, 86, 83, 42, 84, 188, 198, 7, 3, 66, 76, 85, 138, 156, 9, 87, 250, - 41, 86, 250, 77, 71, 135, 69, 67, 2, 129, 209, 17, 4, 73, 71, 72, 84, 8, - 94, 73, 205, 157, 18, 17, 79, 85, 82, 32, 80, 79, 73, 78, 84, 69, 68, 32, - 66, 76, 65, 67, 75, 4, 149, 205, 17, 2, 86, 69, 4, 36, 3, 73, 71, 72, - 155, 176, 17, 65, 2, 165, 237, 1, 16, 84, 32, 84, 79, 82, 84, 79, 73, 83, - 69, 32, 83, 72, 69, 76, 76, 6, 170, 236, 16, 72, 138, 98, 65, 43, 73, 4, - 200, 153, 16, 2, 87, 69, 21, 3, 72, 82, 69, 7, 29, 5, 32, 77, 79, 79, 68, - 5, 155, 149, 8, 32, 138, 1, 80, 3, 66, 85, 32, 137, 157, 8, 11, 73, 84, - 69, 68, 32, 76, 73, 65, 66, 73, 76, 136, 1, 128, 1, 7, 76, 69, 84, 84, - 69, 82, 32, 222, 1, 83, 200, 2, 5, 86, 79, 87, 69, 76, 194, 206, 12, 69, - 182, 188, 4, 81, 247, 95, 68, 60, 170, 1, 71, 242, 139, 6, 84, 170, 221, - 8, 89, 186, 132, 1, 78, 162, 169, 3, 83, 82, 66, 2, 67, 2, 68, 2, 74, 2, - 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, 2, 82, 3, 87, 6, 254, 148, 18, 89, - 202, 199, 1, 72, 187, 2, 65, 32, 108, 4, 73, 71, 78, 32, 128, 1, 12, 77, - 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, 32, 181, 250, 6, 2, 85, 66, 8, - 72, 3, 75, 69, 77, 0, 3, 77, 85, 75, 32, 2, 83, 65, 207, 149, 17, 76, 2, - 129, 154, 17, 3, 80, 72, 82, 2, 155, 201, 19, 45, 18, 174, 235, 15, 78, - 186, 172, 3, 65, 194, 66, 75, 2, 76, 2, 77, 2, 80, 2, 82, 3, 84, 20, 84, - 6, 32, 83, 73, 71, 78, 32, 177, 85, 10, 45, 67, 65, 82, 82, 73, 69, 82, - 32, 76, 18, 170, 211, 5, 65, 166, 194, 11, 79, 194, 133, 2, 69, 162, 64, - 73, 3, 85, 226, 8, 22, 69, 199, 64, 75, 222, 8, 34, 32, 177, 3, 3, 65, - 82, 32, 14, 120, 12, 73, 78, 84, 69, 71, 82, 65, 84, 73, 79, 78, 32, 186, - 156, 13, 70, 150, 143, 3, 83, 245, 147, 1, 4, 84, 65, 66, 85, 6, 108, 5, - 87, 73, 84, 72, 32, 157, 1, 17, 78, 79, 84, 32, 73, 78, 67, 76, 85, 68, - 73, 78, 71, 32, 84, 72, 69, 4, 76, 7, 82, 69, 67, 84, 65, 78, 71, 1, 8, - 83, 69, 77, 73, 67, 73, 82, 67, 2, 73, 16, 85, 76, 65, 82, 32, 80, 65, - 84, 72, 32, 65, 82, 79, 85, 78, 68, 2, 17, 2, 32, 80, 2, 223, 178, 18, - 79, 208, 8, 60, 8, 65, 32, 83, 73, 71, 78, 32, 65, 221, 24, 2, 66, 32, - 170, 5, 122, 49, 86, 51, 202, 2, 52, 214, 1, 53, 158, 4, 54, 142, 3, 55, - 148, 4, 2, 56, 48, 78, 66, 213, 136, 18, 3, 48, 50, 56, 6, 128, 188, 12, - 5, 48, 48, 45, 49, 48, 200, 221, 5, 2, 50, 48, 233, 19, 2, 51, 49, 150, - 1, 78, 48, 90, 49, 130, 1, 55, 246, 156, 14, 50, 2, 51, 2, 52, 2, 53, 3, - 54, 22, 178, 1, 57, 214, 210, 19, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, - 2, 55, 3, 56, 24, 90, 51, 214, 210, 19, 48, 2, 49, 2, 50, 2, 52, 2, 53, - 2, 54, 2, 55, 2, 56, 3, 57, 6, 210, 210, 19, 65, 2, 66, 3, 67, 4, 174, - 210, 19, 48, 3, 49, 38, 18, 48, 91, 49, 20, 162, 1, 48, 2, 49, 2, 50, 2, - 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 18, 74, 48, 2, 49, 2, 50, - 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 2, 205, 178, 6, 2, 45, 86, 160, - 1, 102, 48, 78, 49, 62, 51, 86, 52, 70, 53, 86, 54, 206, 11, 50, 150, - 166, 2, 57, 198, 229, 11, 55, 3, 56, 16, 210, 207, 19, 49, 2, 50, 2, 51, - 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 12, 134, 207, 19, 48, 2, 49, 2, 50, 2, - 51, 2, 53, 3, 54, 18, 202, 206, 19, 48, 2, 49, 2, 50, 2, 52, 2, 53, 2, - 54, 2, 55, 2, 56, 3, 57, 14, 246, 205, 19, 48, 2, 49, 2, 50, 2, 53, 2, - 55, 2, 56, 3, 57, 18, 178, 205, 19, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, - 53, 2, 54, 2, 55, 3, 57, 12, 222, 204, 19, 51, 2, 52, 2, 53, 2, 54, 2, - 56, 3, 57, 104, 70, 48, 78, 50, 86, 51, 38, 52, 78, 54, 162, 146, 14, 53, - 243, 1, 49, 16, 218, 203, 19, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 54, 2, - 56, 3, 57, 18, 142, 203, 19, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 54, 2, - 55, 2, 56, 3, 57, 6, 186, 202, 19, 52, 2, 55, 3, 56, 16, 150, 202, 19, - 48, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 10, 202, 201, 19, - 48, 2, 49, 2, 50, 2, 51, 3, 52, 44, 86, 48, 134, 2, 49, 196, 252, 1, 2, - 51, 50, 177, 184, 17, 6, 50, 54, 32, 69, 89, 89, 26, 110, 57, 142, 219, - 8, 55, 182, 6, 50, 62, 54, 230, 62, 52, 226, 151, 3, 51, 110, 56, 134, - 206, 2, 49, 147, 117, 53, 10, 26, 45, 131, 190, 18, 32, 8, 96, 2, 50, 32, - 200, 169, 12, 3, 54, 32, 76, 184, 2, 3, 52, 32, 76, 201, 164, 3, 3, 51, - 32, 76, 2, 163, 173, 12, 76, 14, 110, 49, 22, 51, 32, 3, 52, 32, 65, 0, - 2, 53, 32, 238, 154, 4, 50, 238, 199, 4, 48, 241, 235, 6, 2, 55, 32, 2, - 167, 185, 18, 32, 2, 11, 32, 2, 175, 142, 12, 79, 2, 179, 137, 18, 66, - 16, 130, 197, 19, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 55, - 162, 1, 22, 48, 155, 5, 49, 140, 1, 82, 49, 54, 50, 118, 51, 62, 52, 78, - 53, 86, 54, 62, 55, 70, 56, 151, 136, 14, 48, 10, 194, 195, 19, 48, 2, - 49, 2, 51, 2, 54, 3, 55, 28, 86, 49, 2, 50, 146, 77, 51, 170, 245, 18, - 48, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 7, 182, 194, 19, 70, 3, 77, 12, - 154, 194, 19, 48, 2, 49, 2, 52, 2, 55, 2, 56, 3, 57, 16, 222, 193, 19, - 48, 2, 49, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 18, 146, 193, 19, - 48, 2, 49, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 12, 190, 192, - 19, 48, 2, 49, 2, 53, 2, 54, 2, 55, 3, 57, 14, 130, 192, 19, 48, 2, 51, - 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 12, 190, 191, 19, 48, 2, 49, 2, 50, 2, - 53, 2, 54, 3, 55, 22, 82, 50, 36, 2, 51, 49, 30, 56, 214, 160, 12, 49, - 206, 2, 54, 146, 1, 55, 3, 57, 6, 174, 190, 19, 48, 2, 50, 3, 51, 4, 138, - 190, 19, 65, 3, 66, 4, 238, 189, 19, 48, 3, 56, 166, 3, 116, 9, 73, 68, - 69, 79, 71, 82, 65, 77, 32, 216, 17, 10, 77, 79, 78, 79, 71, 82, 65, 77, - 32, 66, 249, 1, 2, 83, 89, 234, 1, 54, 66, 249, 15, 8, 86, 69, 83, 83, - 69, 76, 32, 66, 176, 1, 22, 49, 255, 10, 50, 126, 86, 48, 210, 3, 50, - 162, 1, 51, 86, 52, 122, 53, 114, 54, 146, 1, 55, 118, 56, 71, 57, 28, - 114, 53, 98, 54, 58, 55, 78, 56, 62, 57, 192, 133, 7, 3, 50, 32, 87, 138, - 88, 48, 225, 156, 11, 4, 52, 32, 68, 69, 6, 168, 132, 7, 2, 32, 69, 196, - 149, 11, 3, 70, 32, 77, 129, 80, 7, 77, 32, 83, 84, 65, 76, 76, 4, 152, - 240, 1, 3, 70, 32, 69, 177, 142, 16, 2, 77, 32, 4, 36, 3, 70, 32, 83, 1, - 2, 77, 32, 2, 145, 225, 11, 4, 72, 69, 45, 71, 4, 188, 152, 9, 3, 77, 32, - 66, 237, 133, 9, 3, 70, 32, 83, 4, 208, 176, 17, 4, 77, 32, 66, 85, 157, - 109, 3, 70, 32, 67, 10, 196, 145, 9, 4, 51, 32, 83, 80, 240, 10, 5, 49, - 32, 66, 65, 82, 220, 231, 4, 4, 50, 32, 79, 76, 20, 6, 53, 32, 67, 89, - 80, 69, 245, 98, 4, 48, 32, 87, 72, 6, 54, 49, 172, 130, 17, 3, 48, 32, - 79, 215, 179, 2, 50, 2, 197, 156, 18, 2, 32, 87, 10, 224, 5, 3, 53, 32, - 87, 204, 143, 9, 6, 48, 32, 66, 82, 79, 78, 172, 2, 4, 49, 32, 71, 79, - 138, 158, 10, 50, 3, 54, 16, 82, 57, 214, 232, 2, 49, 198, 203, 16, 48, - 2, 50, 2, 51, 2, 52, 2, 55, 3, 56, 2, 169, 97, 3, 32, 67, 76, 20, 248, - 160, 14, 5, 51, 32, 65, 82, 77, 204, 207, 4, 5, 50, 32, 71, 65, 82, 182, - 67, 48, 2, 49, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 18, 134, 242, - 13, 54, 200, 190, 3, 4, 51, 32, 77, 79, 158, 130, 2, 48, 2, 49, 2, 50, 2, - 52, 2, 55, 2, 56, 3, 57, 14, 246, 177, 19, 48, 2, 49, 2, 50, 2, 51, 2, - 52, 2, 53, 3, 57, 4, 164, 164, 2, 3, 49, 32, 72, 143, 141, 17, 48, 50, - 42, 50, 110, 51, 130, 1, 52, 227, 1, 53, 4, 84, 8, 48, 32, 70, 79, 79, - 84, 83, 84, 169, 210, 6, 7, 53, 32, 66, 65, 84, 72, 84, 2, 143, 157, 18, - 79, 12, 104, 4, 51, 32, 83, 87, 156, 255, 13, 4, 48, 32, 83, 80, 146, - 149, 4, 49, 214, 154, 1, 50, 2, 52, 3, 54, 2, 147, 152, 18, 79, 16, 168, - 1, 9, 48, 32, 87, 72, 69, 69, 76, 69, 68, 2, 49, 34, 51, 168, 158, 17, - 12, 50, 32, 67, 72, 65, 82, 73, 79, 84, 32, 70, 82, 250, 142, 2, 53, 2, - 54, 2, 56, 3, 57, 2, 185, 163, 18, 3, 32, 67, 72, 2, 247, 190, 6, 32, 18, - 236, 165, 18, 3, 52, 32, 68, 158, 135, 1, 49, 2, 50, 2, 51, 2, 53, 2, 54, - 2, 55, 2, 56, 3, 57, 58, 50, 50, 192, 144, 12, 2, 49, 53, 1, 2, 51, 48, - 54, 50, 50, 238, 146, 12, 53, 150, 227, 1, 48, 3, 49, 12, 186, 171, 19, + 89, 186, 2, 65, 2, 73, 3, 85, 2, 211, 251, 23, 69, 4, 210, 196, 23, 68, + 163, 199, 1, 80, 54, 52, 5, 67, 72, 73, 78, 71, 41, 4, 82, 65, 78, 32, 2, + 17, 2, 32, 67, 2, 243, 216, 23, 72, 52, 52, 7, 76, 69, 84, 84, 69, 82, + 32, 223, 182, 6, 78, 42, 218, 1, 65, 186, 173, 11, 90, 238, 46, 84, 146, + 120, 76, 50, 81, 60, 6, 68, 65, 76, 69, 84, 72, 214, 169, 4, 71, 122, 83, + 66, 89, 198, 207, 1, 72, 234, 5, 75, 174, 81, 66, 170, 225, 4, 78, 134, + 2, 87, 218, 103, 80, 171, 4, 77, 4, 222, 254, 16, 76, 159, 186, 7, 89, + 174, 9, 210, 1, 65, 242, 32, 66, 130, 33, 76, 200, 1, 16, 78, 84, 65, 73, + 71, 65, 78, 65, 32, 76, 69, 84, 84, 69, 82, 32, 174, 12, 82, 120, 11, 88, + 65, 71, 82, 65, 77, 32, 70, 79, 82, 32, 165, 181, 24, 4, 68, 71, 69, 72, + 214, 1, 42, 68, 98, 82, 201, 1, 3, 86, 89, 32, 6, 44, 5, 83, 84, 79, 78, + 69, 159, 217, 23, 80, 5, 137, 223, 2, 7, 32, 71, 82, 65, 86, 69, 89, 12, + 32, 2, 84, 32, 251, 155, 17, 45, 10, 60, 5, 87, 73, 84, 72, 32, 198, 163, + 12, 68, 239, 158, 9, 72, 6, 76, 9, 84, 73, 80, 32, 79, 78, 32, 84, 72, + 238, 242, 12, 82, 243, 244, 10, 65, 2, 215, 209, 24, 69, 196, 1, 134, 2, + 65, 202, 1, 66, 230, 2, 67, 154, 3, 68, 162, 1, 69, 186, 3, 70, 94, 72, + 62, 76, 222, 1, 77, 110, 79, 162, 1, 82, 142, 2, 83, 228, 1, 3, 78, 79, + 82, 198, 1, 84, 128, 2, 2, 85, 80, 174, 1, 87, 162, 140, 14, 73, 222, + 243, 3, 80, 130, 142, 4, 86, 227, 78, 71, 12, 108, 17, 82, 82, 79, 87, + 32, 83, 72, 65, 70, 84, 32, 87, 73, 68, 84, 72, 32, 158, 163, 18, 77, + 207, 198, 4, 83, 8, 36, 3, 79, 78, 69, 239, 157, 23, 84, 7, 11, 32, 4, + 202, 238, 13, 84, 251, 139, 9, 72, 14, 48, 4, 65, 76, 76, 79, 21, 4, 76, + 65, 67, 75, 2, 191, 231, 5, 84, 12, 30, 32, 153, 1, 2, 45, 70, 6, 52, 7, + 67, 85, 82, 86, 69, 68, 32, 239, 247, 23, 72, 4, 40, 4, 68, 79, 87, 78, + 1, 2, 85, 80, 2, 201, 220, 23, 8, 87, 65, 82, 68, 83, 32, 65, 78, 6, 45, + 9, 69, 65, 84, 72, 69, 82, 69, 68, 32, 6, 134, 212, 13, 83, 174, 173, 3, + 78, 215, 218, 6, 82, 14, 116, 2, 72, 69, 52, 5, 73, 82, 67, 76, 69, 253, + 224, 17, 14, 79, 78, 67, 65, 86, 69, 45, 80, 79, 73, 78, 84, 69, 68, 4, + 172, 213, 20, 4, 86, 82, 79, 78, 255, 225, 2, 67, 9, 64, 6, 32, 87, 73, + 84, 72, 32, 161, 227, 22, 4, 68, 32, 83, 65, 4, 52, 7, 83, 84, 82, 79, + 75, 69, 32, 163, 253, 4, 67, 2, 29, 5, 65, 78, 68, 32, 84, 2, 11, 87, 2, + 11, 79, 2, 21, 3, 32, 68, 79, 2, 11, 84, 2, 199, 253, 23, 83, 14, 52, 7, + 65, 83, 72, 69, 68, 32, 84, 18, 73, 31, 79, 2, 175, 16, 82, 2, 137, 154, + 19, 2, 86, 73, 10, 192, 16, 3, 87, 78, 87, 222, 139, 14, 85, 191, 184, 4, + 76, 16, 120, 5, 73, 71, 72, 84, 32, 156, 2, 16, 88, 67, 76, 65, 77, 65, + 84, 73, 79, 78, 32, 77, 65, 82, 75, 32, 175, 209, 18, 81, 10, 40, 2, 80, + 79, 126, 84, 131, 223, 22, 83, 6, 33, 6, 73, 78, 84, 69, 68, 32, 6, 170, + 191, 16, 80, 128, 158, 6, 11, 82, 69, 67, 84, 73, 76, 73, 78, 69, 65, 82, + 23, 66, 2, 169, 223, 22, 24, 69, 65, 82, 68, 82, 79, 80, 45, 83, 80, 79, + 75, 69, 68, 32, 80, 82, 79, 80, 69, 76, 76, 69, 82, 4, 210, 224, 23, 83, + 227, 81, 79, 6, 44, 5, 79, 85, 82, 32, 66, 231, 252, 4, 73, 2, 157, 131, + 11, 6, 65, 76, 76, 79, 79, 78, 4, 214, 191, 17, 79, 161, 233, 5, 6, 69, + 65, 82, 84, 32, 69, 20, 74, 65, 44, 3, 69, 70, 84, 28, 2, 79, 87, 241, + 247, 4, 3, 73, 71, 65, 4, 208, 222, 21, 2, 82, 71, 211, 209, 1, 84, 8, + 254, 3, 45, 195, 6, 87, 6, 26, 32, 239, 147, 10, 69, 4, 178, 150, 14, 68, + 25, 4, 83, 73, 78, 71, 4, 56, 8, 85, 76, 84, 73, 80, 76, 73, 67, 143, + 192, 21, 73, 2, 25, 4, 65, 84, 73, 79, 2, 211, 217, 5, 78, 6, 200, 143, + 6, 9, 80, 69, 78, 32, 67, 69, 78, 84, 82, 172, 176, 10, 13, 86, 65, 76, + 32, 87, 73, 84, 72, 32, 79, 86, 65, 76, 213, 151, 6, 5, 85, 84, 76, 73, + 78, 12, 76, 4, 73, 71, 72, 84, 233, 205, 23, 9, 79, 85, 78, 68, 45, 84, + 73, 80, 80, 10, 62, 45, 109, 11, 87, 65, 82, 68, 83, 32, 65, 82, 82, 79, + 87, 4, 69, 15, 80, 79, 73, 78, 84, 73, 78, 71, 32, 65, 78, 71, 76, 69, + 32, 4, 138, 233, 6, 66, 255, 169, 7, 81, 7, 139, 6, 32, 26, 106, 65, 78, + 73, 44, 2, 79, 85, 240, 154, 14, 7, 67, 82, 73, 80, 84, 32, 76, 193, 139, + 1, 3, 80, 65, 82, 4, 132, 146, 14, 10, 78, 83, 45, 83, 69, 82, 73, 70, + 32, 73, 175, 195, 8, 76, 8, 240, 143, 14, 2, 78, 71, 183, 194, 8, 88, 10, + 21, 3, 84, 72, 32, 10, 60, 5, 69, 65, 83, 84, 32, 29, 6, 87, 69, 83, 84, + 32, 80, 6, 26, 80, 191, 207, 23, 65, 4, 41, 8, 79, 73, 78, 84, 73, 78, + 71, 32, 4, 226, 242, 16, 86, 255, 202, 6, 66, 12, 88, 9, 69, 65, 82, 68, + 82, 79, 80, 45, 83, 130, 1, 82, 197, 229, 6, 4, 87, 69, 76, 86, 6, 64, 6, + 80, 79, 75, 69, 68, 32, 229, 199, 23, 4, 72, 65, 78, 75, 4, 192, 210, 22, + 8, 80, 73, 78, 87, 72, 69, 69, 76, 27, 65, 2, 193, 3, 5, 73, 65, 78, 71, + 76, 6, 26, 87, 219, 137, 10, 80, 4, 53, 11, 65, 82, 68, 83, 32, 65, 82, + 82, 79, 87, 32, 4, 29, 5, 87, 73, 84, 72, 32, 4, 128, 134, 20, 5, 76, 65, + 82, 71, 69, 179, 243, 1, 69, 12, 76, 5, 72, 73, 84, 69, 32, 164, 1, 2, + 73, 68, 253, 252, 22, 3, 69, 68, 71, 8, 64, 6, 83, 81, 85, 65, 82, 69, + 158, 219, 17, 68, 187, 183, 5, 67, 5, 145, 178, 23, 19, 32, 67, 79, 78, + 84, 65, 73, 78, 73, 78, 71, 32, 66, 76, 65, 67, 75, 32, 86, 2, 157, 247, + 20, 2, 69, 45, 142, 2, 40, 4, 82, 69, 87, 32, 195, 229, 24, 69, 140, 2, + 112, 7, 65, 67, 67, 69, 78, 84, 32, 142, 8, 76, 164, 14, 5, 77, 65, 82, + 75, 32, 114, 80, 201, 172, 21, 2, 89, 79, 60, 128, 2, 9, 65, 84, 78, 65, + 72, 32, 72, 65, 70, 22, 68, 36, 3, 71, 69, 82, 74, 77, 124, 2, 80, 65, + 28, 4, 69, 84, 78, 65, 20, 2, 81, 65, 58, 83, 58, 84, 156, 1, 2, 89, 69, + 78, 90, 148, 169, 8, 3, 82, 69, 86, 242, 141, 15, 79, 245, 148, 1, 3, 73, + 76, 85, 2, 219, 161, 18, 85, 4, 182, 138, 19, 69, 171, 149, 5, 65, 6, 32, + 3, 69, 83, 72, 179, 28, 83, 5, 165, 224, 6, 4, 32, 77, 85, 81, 8, 84, 5, + 69, 82, 75, 72, 65, 236, 242, 17, 2, 85, 78, 153, 45, 5, 65, 72, 65, 80, + 65, 5, 197, 229, 19, 4, 32, 75, 69, 70, 4, 26, 83, 195, 162, 24, 90, 2, + 223, 187, 24, 72, 4, 200, 155, 24, 6, 82, 78, 69, 89, 32, 80, 191, 35, + 68, 4, 182, 24, 69, 161, 229, 22, 6, 72, 65, 76, 83, 72, 69, 8, 38, 69, + 217, 225, 22, 3, 73, 80, 69, 6, 48, 6, 76, 73, 83, 72, 65, 32, 239, 223, + 11, 86, 4, 228, 170, 22, 3, 81, 69, 84, 205, 145, 2, 4, 71, 69, 68, 79, + 4, 228, 208, 8, 10, 82, 65, 72, 32, 66, 69, 78, 32, 89, 79, 155, 217, 2, + 84, 8, 18, 65, 95, 73, 6, 40, 4, 81, 69, 70, 32, 219, 130, 6, 82, 4, 222, + 134, 11, 81, 149, 193, 12, 3, 71, 65, 68, 2, 231, 163, 24, 78, 150, 1, + 76, 6, 69, 84, 84, 69, 82, 32, 201, 11, 8, 73, 71, 65, 84, 85, 82, 69, + 32, 140, 1, 134, 3, 65, 204, 1, 3, 66, 69, 84, 0, 3, 75, 65, 70, 0, 2, + 80, 69, 68, 6, 70, 73, 78, 65, 76, 32, 92, 2, 81, 79, 16, 2, 72, 69, 52, + 2, 78, 85, 0, 4, 90, 65, 89, 73, 18, 83, 48, 3, 82, 69, 83, 170, 1, 84, + 52, 4, 68, 65, 76, 69, 12, 5, 71, 73, 77, 69, 76, 0, 5, 76, 65, 77, 69, + 68, 0, 3, 77, 69, 77, 48, 3, 86, 65, 86, 80, 5, 87, 73, 68, 69, 32, 153, + 1, 3, 89, 79, 68, 14, 26, 76, 239, 216, 23, 89, 12, 64, 2, 69, 70, 73, + 10, 84, 69, 82, 78, 65, 84, 73, 86, 69, 32, 9, 33, 6, 32, 87, 73, 84, 72, + 32, 6, 130, 13, 77, 130, 1, 80, 35, 81, 4, 174, 206, 16, 65, 131, 161, 1, + 80, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 138, 15, 82, 171, 225, 13, 68, + 14, 88, 2, 75, 65, 236, 2, 2, 80, 69, 148, 154, 1, 2, 84, 83, 194, 184, + 22, 78, 135, 110, 77, 4, 235, 2, 70, 7, 244, 10, 5, 32, 87, 73, 84, 72, + 235, 202, 24, 84, 4, 167, 2, 78, 16, 44, 4, 65, 77, 69, 75, 17, 3, 72, + 73, 78, 4, 231, 1, 72, 13, 33, 6, 32, 87, 73, 84, 72, 32, 10, 40, 6, 68, + 65, 71, 69, 83, 72, 39, 83, 7, 33, 6, 32, 65, 78, 68, 32, 83, 4, 142, + 130, 6, 72, 155, 193, 2, 73, 12, 50, 69, 12, 2, 65, 86, 1, 4, 83, 65, 68, + 73, 4, 11, 84, 5, 201, 236, 13, 7, 32, 87, 73, 84, 72, 32, 68, 7, 33, 6, + 32, 87, 73, 84, 72, 32, 4, 208, 253, 1, 2, 72, 79, 167, 238, 11, 68, 16, + 174, 2, 76, 178, 207, 8, 65, 230, 191, 2, 84, 158, 218, 2, 82, 156, 132, + 9, 2, 68, 65, 218, 96, 75, 222, 106, 72, 169, 4, 7, 70, 73, 78, 65, 76, + 32, 77, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 172, 7, 2, 72, 73, 227, 226, + 13, 68, 10, 72, 6, 65, 76, 69, 70, 32, 76, 29, 8, 89, 73, 68, 68, 73, 83, + 72, 32, 2, 185, 187, 19, 2, 65, 77, 8, 120, 7, 68, 79, 85, 66, 76, 69, + 32, 232, 4, 9, 89, 79, 68, 32, 89, 79, 68, 32, 80, 169, 143, 21, 5, 86, + 65, 86, 32, 89, 4, 250, 141, 11, 86, 151, 134, 10, 89, 6, 80, 3, 76, 79, + 87, 0, 3, 85, 80, 80, 149, 249, 22, 6, 77, 65, 83, 79, 82, 65, 2, 145, + 186, 23, 2, 69, 82, 50, 84, 5, 79, 73, 78, 84, 32, 225, 5, 11, 85, 78, + 67, 84, 85, 65, 84, 73, 79, 78, 32, 38, 228, 1, 9, 68, 65, 71, 69, 83, + 72, 32, 79, 82, 46, 72, 106, 80, 162, 1, 81, 86, 82, 22, 83, 148, 213, 9, + 2, 84, 83, 148, 153, 11, 17, 74, 85, 68, 69, 79, 45, 83, 80, 65, 78, 73, + 83, 72, 32, 86, 65, 82, 189, 147, 3, 3, 77, 69, 84, 2, 17, 2, 32, 77, 2, + 197, 1, 2, 65, 80, 12, 60, 5, 65, 84, 65, 70, 32, 102, 73, 33, 4, 79, 76, + 65, 77, 6, 38, 80, 34, 81, 141, 2, 2, 83, 69, 2, 11, 65, 2, 199, 219, 17, + 84, 2, 233, 192, 23, 2, 65, 77, 2, 11, 82, 2, 243, 229, 13, 73, 5, 181, + 136, 11, 12, 32, 72, 65, 83, 69, 82, 32, 70, 79, 82, 32, 86, 6, 52, 5, + 65, 77, 65, 84, 83, 221, 190, 11, 2, 85, 66, 5, 217, 241, 10, 2, 32, 81, + 2, 211, 228, 6, 65, 8, 34, 69, 22, 72, 215, 182, 8, 73, 2, 155, 178, 23, + 71, 4, 210, 182, 8, 73, 147, 210, 7, 69, 12, 152, 1, 3, 71, 69, 82, 60, + 3, 80, 65, 83, 20, 7, 83, 79, 70, 32, 80, 65, 83, 216, 136, 22, 7, 78, + 85, 78, 32, 72, 65, 70, 253, 186, 1, 3, 77, 65, 81, 4, 26, 83, 179, 218, + 22, 69, 2, 229, 194, 23, 3, 72, 65, 89, 2, 255, 225, 13, 69, 2, 235, 225, + 13, 85, 8, 114, 77, 176, 149, 12, 15, 76, 83, 67, 72, 82, 69, 73, 66, 69, + 82, 32, 80, 65, 85, 83, 229, 227, 5, 3, 73, 67, 79, 4, 196, 225, 5, 12, + 69, 84, 32, 87, 73, 84, 72, 32, 87, 72, 73, 84, 255, 204, 17, 32, 188, 4, + 164, 1, 2, 65, 45, 50, 72, 70, 75, 230, 1, 77, 114, 78, 146, 2, 82, 66, + 83, 154, 1, 84, 226, 1, 87, 62, 89, 110, 69, 234, 244, 8, 85, 150, 170, + 5, 79, 139, 192, 3, 73, 8, 134, 163, 24, 87, 246, 30, 49, 2, 50, 3, 51, + 72, 166, 6, 79, 250, 194, 8, 65, 230, 223, 5, 85, 166, 116, 69, 3, 73, + 74, 72, 2, 65, 45, 104, 2, 79, 45, 178, 4, 73, 226, 3, 69, 163, 147, 15, + 85, 24, 250, 233, 11, 49, 238, 191, 12, 75, 214, 22, 50, 2, 51, 2, 52, 2, + 53, 2, 54, 2, 55, 2, 56, 3, 57, 8, 250, 171, 24, 75, 218, 19, 49, 2, 50, + 3, 51, 54, 68, 2, 69, 45, 154, 7, 79, 162, 147, 15, 65, 2, 73, 231, 203, + 2, 85, 6, 162, 188, 24, 77, 186, 2, 49, 3, 50, 68, 116, 2, 69, 45, 72, 2, + 73, 45, 246, 2, 65, 166, 247, 8, 79, 150, 170, 5, 85, 225, 146, 6, 6, 45, + 77, 85, 45, 77, 79, 14, 198, 158, 24, 75, 246, 30, 49, 2, 50, 2, 51, 2, + 52, 2, 53, 3, 54, 16, 158, 166, 24, 84, 214, 22, 49, 2, 50, 2, 51, 2, 52, + 2, 53, 2, 54, 3, 55, 54, 222, 4, 79, 2, 85, 162, 147, 15, 73, 230, 203, + 2, 65, 3, 69, 68, 62, 65, 2, 85, 226, 3, 73, 234, 244, 8, 69, 187, 158, + 6, 79, 16, 11, 45, 16, 150, 187, 24, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, + 54, 2, 55, 3, 56, 64, 74, 69, 20, 2, 79, 45, 72, 2, 85, 45, 130, 149, 15, + 73, 231, 203, 2, 65, 18, 223, 249, 18, 45, 14, 178, 183, 24, 82, 186, 2, + 49, 2, 50, 2, 51, 2, 52, 2, 53, 3, 54, 10, 174, 154, 24, 84, 246, 30, 49, + 2, 50, 2, 51, 3, 52, 42, 142, 246, 8, 65, 2, 73, 186, 158, 6, 79, 231, + 203, 2, 69, 32, 40, 2, 65, 45, 66, 79, 135, 223, 17, 85, 12, 142, 153, + 24, 89, 246, 30, 49, 2, 50, 2, 51, 2, 52, 3, 53, 12, 11, 45, 12, 182, + 183, 24, 49, 2, 50, 2, 51, 2, 52, 2, 53, 3, 54, 4, 240, 170, 8, 21, 77, + 73, 84, 73, 65, 78, 32, 67, 79, 78, 74, 85, 71, 65, 84, 69, 32, 77, 65, + 84, 82, 139, 140, 16, 66, 128, 1, 210, 2, 65, 110, 66, 144, 1, 2, 67, 79, + 94, 68, 198, 2, 70, 66, 71, 40, 4, 72, 79, 76, 68, 164, 1, 2, 73, 78, + 116, 2, 77, 79, 58, 79, 122, 80, 100, 2, 82, 69, 66, 83, 238, 1, 84, 210, + 5, 87, 148, 160, 15, 4, 76, 73, 77, 73, 140, 191, 5, 11, 89, 79, 85, 84, + 72, 70, 85, 76, 32, 70, 79, 137, 177, 3, 9, 69, 78, 84, 72, 85, 83, 73, + 65, 83, 6, 76, 3, 80, 80, 82, 124, 4, 70, 84, 69, 82, 185, 161, 19, 4, + 66, 85, 78, 68, 2, 177, 144, 24, 2, 79, 65, 6, 92, 5, 69, 70, 79, 82, 69, + 156, 213, 3, 6, 73, 84, 73, 78, 71, 32, 1, 4, 82, 69, 65, 75, 2, 141, + 225, 23, 7, 32, 67, 79, 77, 80, 76, 69, 6, 26, 78, 179, 152, 19, 77, 4, + 180, 202, 20, 4, 84, 69, 77, 80, 209, 213, 2, 3, 70, 76, 73, 14, 110, 69, + 78, 73, 202, 156, 19, 85, 241, 245, 2, 15, 65, 82, 75, 69, 78, 73, 78, + 71, 32, 79, 70, 32, 84, 72, 69, 6, 218, 152, 19, 67, 192, 6, 2, 76, 73, + 149, 205, 4, 5, 86, 69, 76, 79, 80, 4, 248, 142, 19, 21, 70, 70, 73, 67, + 85, 76, 84, 89, 32, 65, 84, 32, 84, 72, 69, 32, 66, 69, 71, 73, 78, 169, + 253, 2, 4, 83, 80, 69, 82, 4, 216, 147, 19, 2, 79, 76, 165, 197, 4, 5, + 69, 76, 76, 79, 87, 12, 36, 5, 65, 84, 72, 69, 82, 35, 82, 2, 165, 139, + 15, 3, 73, 78, 71, 10, 40, 4, 69, 65, 84, 32, 227, 239, 23, 65, 8, 22, + 80, 215, 5, 84, 6, 22, 79, 151, 5, 82, 4, 172, 2, 2, 83, 83, 147, 236, + 23, 87, 8, 50, 78, 202, 148, 19, 67, 217, 5, 3, 70, 76, 85, 4, 156, 154, + 19, 2, 79, 67, 197, 236, 1, 5, 69, 82, 32, 84, 82, 4, 172, 187, 22, 3, + 68, 69, 83, 145, 62, 3, 85, 84, 72, 6, 26, 66, 37, 2, 80, 80, 2, 221, + 217, 23, 4, 83, 84, 82, 85, 4, 26, 82, 183, 216, 23, 79, 2, 133, 135, 22, + 2, 69, 83, 6, 136, 144, 15, 9, 85, 83, 72, 73, 78, 71, 32, 85, 80, 192, + 137, 2, 3, 82, 79, 71, 143, 211, 6, 69, 6, 26, 84, 203, 132, 19, 86, 4, + 250, 149, 19, 85, 171, 137, 4, 82, 8, 132, 1, 5, 77, 65, 76, 76, 32, 224, + 171, 21, 6, 84, 65, 78, 68, 83, 84, 185, 244, 1, 11, 80, 76, 73, 84, 84, + 73, 78, 71, 32, 65, 80, 4, 24, 2, 80, 82, 43, 84, 2, 217, 150, 19, 5, 69, + 80, 79, 78, 68, 2, 11, 65, 2, 223, 213, 23, 77, 30, 36, 3, 72, 69, 32, + 227, 223, 17, 82, 28, 186, 2, 65, 130, 1, 67, 132, 1, 3, 70, 65, 77, 20, + 9, 82, 69, 67, 69, 80, 84, 73, 86, 69, 30, 87, 172, 22, 12, 77, 65, 82, + 82, 89, 73, 78, 71, 32, 77, 65, 73, 184, 150, 5, 6, 71, 69, 78, 84, 76, + 69, 164, 223, 10, 13, 75, 69, 69, 80, 73, 78, 71, 32, 83, 84, 73, 76, 76, + 173, 238, 3, 7, 74, 79, 89, 79, 85, 83, 32, 6, 58, 82, 161, 180, 11, 8, + 66, 89, 83, 77, 65, 76, 32, 87, 4, 196, 183, 20, 8, 79, 85, 83, 73, 78, + 71, 32, 84, 163, 218, 3, 77, 6, 188, 131, 1, 2, 65, 85, 228, 178, 19, 9, + 82, 69, 65, 84, 73, 86, 69, 32, 72, 161, 212, 1, 9, 76, 73, 78, 71, 73, + 78, 71, 32, 70, 2, 183, 254, 23, 73, 2, 233, 180, 20, 2, 32, 69, 4, 142, + 151, 22, 69, 189, 204, 1, 5, 65, 78, 68, 69, 82, 4, 242, 234, 6, 65, 205, + 162, 6, 14, 79, 82, 75, 32, 79, 78, 32, 84, 72, 69, 32, 68, 69, 67, 222, + 1, 236, 1, 2, 71, 72, 180, 2, 7, 82, 65, 71, 65, 78, 65, 32, 212, 4, 7, + 83, 84, 79, 82, 73, 67, 32, 212, 190, 15, 7, 78, 68, 85, 32, 84, 69, 77, + 240, 33, 4, 75, 73, 78, 71, 218, 249, 1, 66, 169, 180, 4, 8, 80, 80, 79, + 80, 79, 84, 65, 77, 12, 18, 32, 119, 45, 6, 216, 138, 5, 2, 66, 82, 184, + 243, 16, 6, 86, 79, 76, 84, 65, 71, 217, 49, 9, 79, 67, 84, 69, 84, 32, + 80, 82, 69, 6, 92, 11, 83, 80, 69, 69, 68, 32, 84, 82, 65, 73, 78, 173, + 203, 5, 6, 72, 69, 69, 76, 69, 68, 5, 157, 185, 11, 14, 32, 87, 73, 84, + 72, 32, 66, 85, 76, 76, 69, 84, 32, 78, 200, 1, 112, 9, 68, 73, 71, 82, + 65, 80, 72, 32, 89, 20, 7, 76, 69, 84, 84, 69, 82, 32, 154, 173, 1, 86, + 191, 199, 20, 73, 2, 183, 175, 17, 79, 194, 1, 194, 1, 65, 74, 83, 138, + 164, 1, 66, 162, 3, 78, 150, 2, 68, 2, 71, 2, 72, 2, 75, 2, 77, 2, 80, 2, + 82, 2, 84, 2, 90, 126, 87, 46, 89, 150, 201, 22, 86, 234, 36, 69, 2, 73, + 2, 79, 3, 85, 7, 37, 7, 82, 67, 72, 65, 73, 67, 32, 4, 150, 244, 23, 87, + 151, 14, 89, 42, 76, 5, 77, 65, 76, 76, 32, 146, 152, 24, 65, 2, 69, 2, + 73, 2, 79, 3, 85, 32, 230, 169, 1, 87, 46, 89, 150, 176, 5, 75, 130, 153, + 17, 84, 234, 36, 65, 2, 69, 2, 73, 2, 79, 3, 85, 2, 235, 182, 8, 83, 108, + 166, 1, 76, 52, 3, 78, 69, 89, 46, 82, 146, 7, 84, 138, 1, 85, 206, 192, + 16, 83, 178, 219, 2, 67, 220, 198, 3, 6, 77, 79, 84, 72, 69, 84, 134, + 167, 1, 79, 155, 3, 80, 6, 236, 144, 16, 4, 76, 79, 87, 32, 255, 132, 8, + 69, 4, 150, 163, 22, 66, 233, 161, 1, 2, 32, 80, 66, 60, 8, 73, 90, 79, + 78, 84, 65, 76, 32, 153, 6, 2, 83, 69, 60, 218, 1, 66, 110, 76, 152, 1, + 17, 79, 78, 69, 32, 69, 73, 71, 72, 84, 72, 32, 66, 76, 79, 67, 75, 45, + 88, 10, 83, 67, 65, 78, 32, 76, 73, 78, 69, 45, 46, 84, 146, 232, 21, 67, + 42, 69, 230, 6, 77, 150, 1, 82, 247, 2, 90, 6, 44, 5, 76, 65, 67, 75, 32, + 231, 217, 23, 65, 4, 38, 79, 217, 215, 22, 3, 72, 69, 88, 2, 203, 215, + 22, 67, 10, 40, 4, 73, 78, 69, 32, 247, 237, 21, 65, 8, 44, 5, 87, 73, + 84, 72, 32, 155, 238, 21, 69, 6, 26, 84, 195, 239, 21, 70, 4, 226, 239, + 21, 72, 243, 91, 73, 14, 184, 229, 16, 3, 49, 51, 53, 178, 171, 7, 50, 2, + 51, 2, 52, 2, 53, 2, 54, 3, 55, 8, 146, 144, 24, 49, 2, 51, 2, 55, 3, 57, + 10, 32, 2, 65, 66, 199, 242, 21, 82, 8, 26, 85, 199, 241, 21, 32, 6, 33, + 6, 76, 65, 84, 73, 79, 78, 7, 11, 32, 4, 128, 197, 9, 8, 87, 73, 84, 72, + 32, 74, 85, 83, 223, 148, 14, 83, 7, 11, 32, 4, 128, 195, 7, 2, 82, 65, + 159, 142, 16, 70, 10, 26, 32, 147, 130, 23, 69, 8, 86, 80, 232, 253, 18, + 5, 66, 69, 86, 69, 82, 144, 80, 3, 83, 80, 82, 179, 190, 4, 68, 2, 231, + 234, 14, 69, 12, 48, 6, 82, 71, 76, 65, 83, 83, 77, 2, 83, 69, 5, 233, + 185, 20, 14, 32, 87, 73, 84, 72, 32, 70, 76, 79, 87, 73, 78, 71, 32, 9, + 11, 32, 6, 88, 8, 87, 73, 84, 72, 32, 71, 65, 82, 181, 129, 22, 8, 66, + 85, 73, 76, 68, 73, 78, 71, 2, 135, 181, 22, 68, 7, 246, 138, 24, 74, 3, + 83, 8, 106, 83, 160, 244, 22, 11, 78, 68, 82, 69, 68, 32, 80, 79, 73, 78, + 84, 176, 13, 2, 71, 71, 163, 136, 1, 84, 2, 251, 246, 22, 72, 20, 80, 2, + 71, 73, 22, 80, 224, 1, 5, 83, 84, 69, 82, 69, 161, 129, 22, 2, 65, 67, + 4, 255, 243, 21, 69, 12, 60, 3, 72, 69, 78, 145, 157, 4, 6, 79, 68, 73, + 65, 83, 84, 11, 34, 32, 82, 65, 203, 252, 19, 45, 4, 26, 87, 215, 165, + 22, 66, 2, 21, 3, 73, 84, 72, 2, 197, 186, 3, 2, 32, 68, 2, 141, 220, 12, + 6, 84, 73, 79, 78, 32, 80, 2, 193, 241, 22, 2, 83, 73, 210, 5, 172, 1, 3, + 67, 69, 32, 152, 1, 2, 68, 69, 222, 24, 77, 222, 2, 78, 230, 35, 82, 132, + 2, 7, 90, 65, 75, 65, 89, 65, 32, 185, 242, 19, 9, 32, 76, 79, 86, 69, + 32, 89, 79, 85, 8, 114, 67, 236, 160, 11, 18, 72, 79, 67, 75, 69, 89, 32, + 83, 84, 73, 67, 75, 32, 65, 78, 68, 32, 80, 235, 190, 1, 83, 4, 138, 244, + 15, 82, 223, 231, 2, 85, 246, 1, 68, 3, 78, 84, 73, 201, 1, 9, 79, 71, + 82, 65, 80, 72, 73, 67, 32, 8, 64, 4, 67, 65, 76, 32, 105, 8, 70, 73, 67, + 65, 84, 73, 79, 78, 6, 32, 2, 84, 79, 131, 130, 23, 87, 5, 149, 247, 14, + 12, 32, 65, 78, 68, 32, 83, 76, 65, 78, 84, 69, 68, 2, 181, 233, 22, 2, + 32, 67, 238, 1, 208, 2, 11, 65, 78, 78, 79, 84, 65, 84, 73, 79, 78, 32, + 242, 3, 67, 72, 2, 68, 69, 248, 5, 5, 78, 85, 77, 66, 69, 22, 84, 224, + 239, 5, 8, 72, 65, 76, 70, 32, 70, 73, 76, 232, 186, 1, 5, 69, 78, 84, + 69, 82, 0, 3, 82, 73, 83, 24, 5, 76, 69, 86, 69, 76, 240, 138, 9, 5, 86, + 65, 82, 73, 65, 250, 233, 4, 70, 154, 47, 73, 243, 231, 1, 83, 32, 232, + 1, 5, 66, 79, 84, 84, 79, 22, 70, 82, 77, 62, 84, 140, 1, 4, 76, 73, 78, + 75, 196, 143, 1, 4, 83, 69, 67, 79, 234, 161, 6, 79, 224, 130, 6, 6, 82, + 69, 86, 69, 82, 83, 152, 222, 9, 5, 72, 69, 65, 86, 69, 169, 39, 3, 69, + 65, 82, 2, 143, 188, 23, 77, 6, 48, 3, 79, 85, 82, 197, 178, 23, 3, 73, + 82, 83, 4, 186, 187, 23, 84, 27, 32, 4, 36, 3, 73, 68, 68, 199, 147, 23, + 65, 2, 171, 181, 13, 76, 8, 34, 72, 46, 87, 151, 134, 8, 79, 4, 134, 146, + 9, 82, 237, 157, 14, 2, 73, 82, 2, 159, 186, 23, 79, 4, 36, 3, 76, 79, + 83, 227, 165, 21, 79, 2, 225, 185, 23, 3, 73, 78, 71, 36, 104, 20, 83, + 67, 82, 73, 80, 84, 73, 79, 78, 32, 67, 72, 65, 82, 65, 67, 84, 69, 82, + 32, 211, 175, 7, 80, 34, 144, 2, 9, 65, 66, 79, 86, 69, 32, 84, 79, 32, + 84, 8, 76, 69, 70, 84, 32, 84, 79, 32, 76, 5, 79, 86, 69, 82, 76, 20, 2, + 83, 85, 222, 245, 14, 82, 172, 200, 5, 8, 70, 85, 76, 76, 32, 83, 85, 82, + 229, 231, 2, 15, 72, 79, 82, 73, 90, 79, 78, 84, 65, 76, 32, 82, 69, 70, + 76, 4, 60, 9, 77, 73, 68, 68, 76, 69, 32, 65, 78, 223, 213, 21, 66, 2, + 175, 198, 21, 68, 4, 144, 164, 22, 10, 77, 73, 68, 68, 76, 69, 32, 65, + 78, 68, 211, 168, 1, 82, 2, 147, 225, 16, 65, 18, 72, 12, 82, 82, 79, 85, + 78, 68, 32, 70, 82, 79, 77, 32, 223, 181, 17, 66, 16, 82, 76, 208, 203, + 11, 3, 85, 80, 80, 250, 135, 10, 66, 130, 165, 1, 65, 159, 82, 82, 6, + 194, 203, 11, 79, 147, 255, 11, 69, 2, 175, 217, 8, 82, 148, 1, 92, 10, + 65, 76, 76, 89, 32, 77, 65, 82, 75, 32, 41, 9, 69, 76, 69, 71, 82, 65, + 80, 72, 32, 10, 138, 129, 22, 70, 70, 84, 155, 87, 79, 138, 1, 140, 1, + 11, 83, 89, 77, 66, 79, 76, 32, 70, 79, 82, 32, 221, 230, 12, 17, 76, 73, + 78, 69, 32, 70, 69, 69, 68, 32, 83, 69, 80, 65, 82, 65, 84, 136, 1, 182, + 1, 65, 58, 68, 200, 2, 5, 72, 79, 85, 82, 32, 214, 1, 74, 28, 4, 70, 69, + 66, 82, 64, 2, 77, 65, 168, 196, 15, 3, 78, 79, 86, 0, 4, 83, 69, 80, 84, + 25, 4, 79, 67, 84, 79, 4, 168, 131, 20, 3, 85, 71, 85, 165, 142, 2, 2, + 80, 82, 64, 44, 3, 65, 89, 32, 241, 200, 15, 2, 69, 67, 62, 66, 84, 142, + 212, 5, 69, 66, 70, 70, 78, 26, 83, 159, 255, 16, 79, 34, 34, 72, 90, 87, + 155, 159, 23, 69, 8, 36, 3, 73, 82, 84, 139, 253, 21, 82, 6, 26, 89, 139, + 153, 22, 69, 5, 179, 194, 22, 45, 24, 26, 69, 223, 238, 23, 79, 22, 36, + 3, 78, 84, 89, 227, 241, 22, 76, 21, 139, 163, 15, 45, 50, 78, 84, 234, + 209, 5, 69, 66, 70, 70, 78, 26, 83, 194, 168, 16, 90, 223, 86, 79, 20, + 42, 87, 198, 211, 5, 72, 131, 202, 17, 69, 14, 26, 69, 139, 237, 23, 79, + 12, 36, 3, 78, 84, 89, 143, 240, 22, 76, 11, 175, 158, 17, 45, 6, 24, 2, + 65, 78, 35, 85, 2, 11, 85, 2, 191, 166, 17, 65, 4, 186, 213, 23, 78, 143, + 5, 76, 4, 198, 201, 23, 82, 171, 34, 89, 68, 40, 6, 65, 71, 69, 32, 79, + 70, 83, 80, 5, 161, 135, 9, 15, 32, 79, 82, 32, 65, 80, 80, 82, 79, 88, + 73, 77, 65, 84, 69, 65, 65, 14, 69, 82, 73, 65, 76, 32, 65, 82, 65, 77, + 65, 73, 67, 32, 62, 72, 7, 78, 85, 77, 66, 69, 82, 32, 230, 18, 76, 181, + 209, 19, 2, 83, 69, 16, 26, 84, 187, 199, 15, 79, 10, 130, 183, 11, 87, + 250, 147, 1, 69, 131, 172, 9, 72, 136, 3, 202, 1, 67, 234, 1, 68, 214, 6, + 70, 132, 2, 5, 72, 73, 66, 73, 84, 156, 1, 15, 80, 85, 84, 32, 83, 89, + 77, 66, 79, 76, 32, 70, 79, 82, 32, 206, 1, 83, 236, 5, 2, 84, 69, 206, + 8, 86, 147, 250, 9, 66, 10, 32, 2, 79, 77, 69, 2, 82, 69, 4, 140, 207, + 16, 3, 73, 78, 71, 209, 230, 2, 5, 80, 76, 69, 84, 69, 6, 36, 3, 65, 83, + 69, 239, 162, 23, 77, 4, 34, 32, 157, 180, 10, 2, 83, 32, 2, 161, 183, + 11, 8, 70, 79, 78, 84, 32, 83, 73, 90, 145, 1, 24, 2, 69, 88, 103, 73, 5, + 149, 202, 22, 20, 32, 80, 79, 73, 78, 84, 73, 78, 71, 32, 65, 84, 32, 84, + 72, 69, 32, 86, 73, 69, 138, 1, 72, 8, 67, 32, 83, 73, 89, 65, 81, 32, + 173, 136, 18, 4, 65, 78, 32, 82, 136, 1, 196, 1, 11, 65, 76, 84, 69, 82, + 78, 65, 84, 69, 32, 76, 2, 76, 28, 9, 70, 82, 65, 67, 84, 73, 79, 78, 32, + 100, 7, 78, 85, 77, 66, 69, 82, 32, 130, 247, 8, 82, 209, 120, 6, 80, 76, + 65, 67, 69, 72, 2, 201, 160, 23, 2, 65, 75, 6, 68, 4, 79, 78, 69, 32, + 205, 221, 21, 7, 84, 72, 82, 69, 69, 32, 81, 4, 162, 219, 21, 72, 47, 81, + 122, 212, 1, 10, 65, 76, 84, 69, 82, 78, 65, 84, 69, 32, 72, 5, 75, 65, + 82, 79, 82, 0, 4, 76, 65, 75, 72, 222, 137, 10, 69, 66, 70, 94, 78, 26, + 83, 78, 84, 236, 135, 5, 8, 80, 82, 69, 70, 73, 88, 69, 68, 199, 41, 79, + 6, 26, 84, 251, 195, 22, 79, 4, 132, 164, 6, 2, 69, 78, 175, 156, 17, 87, + 5, 155, 143, 23, 65, 16, 72, 5, 73, 78, 73, 84, 89, 85, 9, 79, 82, 77, + 65, 84, 73, 79, 78, 32, 5, 45, 9, 32, 78, 69, 71, 65, 84, 69, 68, 32, 2, + 225, 171, 8, 4, 87, 73, 84, 72, 12, 42, 83, 225, 216, 19, 4, 68, 69, 83, + 75, 10, 244, 152, 7, 5, 69, 80, 65, 82, 65, 243, 252, 9, 79, 4, 11, 32, + 4, 196, 139, 23, 15, 65, 82, 65, 66, 73, 67, 32, 70, 79, 82, 77, 32, 83, + 72, 65, 1, 14, 83, 89, 77, 77, 69, 84, 82, 73, 67, 32, 83, 87, 65, 80, + 10, 80, 6, 76, 65, 84, 73, 78, 32, 218, 244, 14, 83, 161, 213, 7, 4, 78, + 85, 77, 66, 6, 64, 6, 67, 65, 80, 73, 84, 65, 0, 4, 83, 77, 65, 76, 27, + 76, 2, 21, 3, 76, 32, 76, 2, 193, 214, 21, 2, 69, 84, 116, 84, 13, 67, + 82, 73, 80, 84, 73, 79, 78, 65, 76, 32, 80, 65, 245, 200, 15, 2, 69, 82, + 114, 72, 6, 72, 76, 65, 86, 73, 32, 225, 1, 7, 82, 84, 72, 73, 65, 78, + 32, 54, 48, 7, 76, 69, 84, 84, 69, 82, 32, 191, 3, 78, 38, 234, 171, 10, + 84, 222, 119, 65, 22, 68, 34, 76, 22, 77, 50, 87, 254, 169, 4, 71, 90, + 90, 34, 83, 66, 89, 198, 207, 1, 72, 234, 5, 75, 174, 81, 66, 170, 225, + 4, 78, 223, 105, 80, 60, 22, 76, 251, 1, 78, 44, 11, 69, 44, 29, 5, 84, + 84, 69, 82, 32, 44, 250, 169, 10, 84, 242, 119, 68, 34, 76, 50, 81, 214, + 205, 1, 82, 142, 220, 2, 65, 50, 71, 90, 90, 34, 83, 66, 89, 198, 207, 1, + 72, 234, 5, 75, 174, 81, 66, 170, 225, 4, 78, 134, 2, 87, 218, 103, 80, + 171, 4, 77, 16, 33, 6, 85, 77, 66, 69, 82, 32, 16, 242, 161, 11, 84, 226, + 144, 4, 79, 167, 134, 3, 70, 50, 36, 4, 71, 82, 65, 76, 179, 3, 82, 23, + 11, 32, 20, 58, 65, 140, 1, 5, 87, 73, 84, 72, 32, 135, 175, 21, 69, 4, + 96, 6, 86, 69, 82, 65, 71, 69, 169, 172, 16, 12, 82, 79, 85, 78, 68, 32, + 65, 32, 80, 79, 73, 78, 2, 205, 173, 16, 5, 32, 87, 73, 84, 72, 14, 160, + 1, 3, 84, 73, 77, 20, 2, 85, 78, 172, 239, 6, 16, 76, 69, 70, 84, 87, 65, + 82, 68, 83, 32, 65, 82, 82, 79, 87, 32, 246, 173, 13, 73, 198, 1, 79, + 251, 64, 68, 2, 227, 158, 20, 69, 4, 238, 158, 20, 68, 131, 226, 2, 73, + 28, 94, 76, 232, 1, 7, 83, 69, 67, 84, 73, 79, 78, 146, 7, 82, 232, 249, + 11, 2, 67, 65, 43, 73, 8, 164, 1, 17, 73, 78, 69, 65, 82, 32, 65, 78, 78, + 79, 84, 65, 84, 73, 79, 78, 32, 221, 235, 4, 17, 79, 67, 75, 69, 68, 32, + 70, 69, 77, 65, 76, 69, 32, 65, 78, 68, 32, 6, 208, 167, 8, 3, 65, 78, + 67, 146, 154, 8, 84, 159, 219, 3, 83, 15, 11, 32, 12, 168, 1, 6, 65, 66, + 79, 86, 69, 32, 64, 5, 87, 73, 84, 72, 32, 169, 152, 20, 22, 66, 69, 83, + 73, 68, 69, 32, 65, 78, 68, 32, 74, 79, 73, 78, 69, 68, 32, 87, 73, 84, + 72, 4, 152, 153, 20, 9, 66, 65, 82, 32, 65, 66, 79, 86, 69, 23, 85, 6, + 206, 169, 4, 76, 178, 240, 15, 79, 195, 225, 2, 68, 40, 56, 2, 69, 82, + 189, 5, 7, 73, 83, 73, 66, 76, 69, 32, 34, 48, 3, 83, 69, 32, 225, 2, 4, + 84, 69, 68, 32, 16, 174, 1, 66, 80, 5, 67, 72, 69, 67, 75, 68, 24, 68, + 79, 87, 78, 87, 65, 82, 68, 83, 32, 65, 82, 82, 79, 87, 32, 87, 73, 84, + 72, 32, 84, 73, 80, 210, 178, 20, 87, 219, 26, 77, 6, 44, 5, 76, 65, 67, + 75, 32, 227, 230, 21, 85, 4, 250, 137, 22, 68, 227, 27, 83, 4, 228, 204, + 20, 8, 69, 82, 32, 66, 79, 65, 82, 68, 183, 186, 2, 32, 2, 197, 230, 20, + 2, 32, 76, 18, 144, 1, 6, 73, 78, 84, 69, 82, 82, 22, 76, 146, 242, 11, + 80, 230, 184, 4, 69, 244, 198, 1, 2, 79, 72, 204, 158, 2, 4, 85, 78, 68, + 69, 183, 97, 81, 2, 131, 214, 4, 79, 6, 60, 9, 79, 87, 32, 75, 65, 86, + 89, 75, 65, 215, 231, 6, 65, 5, 205, 154, 18, 11, 32, 87, 73, 84, 72, 32, + 75, 65, 86, 89, 75, 6, 38, 84, 154, 186, 19, 80, 223, 89, 83, 2, 135, + 173, 16, 73, 4, 222, 169, 22, 69, 215, 93, 73, 218, 1, 94, 65, 138, 21, + 69, 62, 79, 42, 85, 133, 187, 11, 10, 73, 71, 83, 65, 87, 32, 80, 85, 90, + 90, 202, 1, 120, 5, 67, 75, 45, 79, 45, 32, 7, 80, 65, 78, 69, 83, 69, + 32, 184, 2, 7, 86, 65, 78, 69, 83, 69, 32, 183, 192, 23, 82, 2, 145, 176, + 18, 3, 76, 65, 78, 16, 246, 1, 67, 18, 80, 204, 153, 1, 10, 73, 78, 68, + 85, 83, 84, 82, 73, 65, 76, 236, 229, 3, 3, 66, 65, 78, 134, 174, 3, 68, + 144, 139, 11, 15, 83, 89, 77, 66, 79, 76, 32, 70, 79, 82, 32, 66, 69, 71, + 73, 180, 248, 1, 3, 71, 79, 66, 169, 109, 2, 79, 71, 2, 207, 32, 65, 2, + 141, 137, 13, 7, 79, 83, 84, 32, 79, 70, 70, 182, 1, 172, 2, 15, 67, 79, + 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 76, 2, 76, 69, 40, 4, + 82, 73, 71, 72, 224, 6, 2, 80, 65, 176, 3, 14, 84, 85, 82, 78, 69, 68, + 32, 80, 65, 68, 65, 32, 80, 73, 88, 5, 83, 73, 71, 78, 32, 144, 1, 11, + 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 143, 189, 21, 68, 6, 52, 2, + 75, 69, 164, 193, 16, 2, 80, 69, 175, 5, 67, 2, 175, 136, 23, 82, 96, 38, + 70, 65, 5, 84, 84, 69, 82, 32, 2, 41, 8, 84, 32, 82, 69, 82, 69, 78, 71, + 2, 195, 240, 21, 71, 94, 230, 1, 68, 26, 73, 48, 2, 75, 65, 66, 78, 130, + 1, 66, 2, 67, 2, 71, 16, 2, 80, 65, 56, 2, 82, 65, 32, 2, 83, 65, 42, 84, + 74, 74, 194, 170, 21, 65, 142, 138, 2, 72, 2, 76, 2, 77, 2, 87, 2, 89, + 186, 2, 69, 2, 79, 3, 85, 8, 222, 3, 68, 15, 65, 7, 236, 201, 5, 3, 32, + 75, 65, 223, 240, 17, 73, 7, 11, 32, 4, 22, 83, 215, 2, 77, 2, 237, 128, + 21, 2, 65, 83, 14, 36, 2, 71, 65, 90, 89, 167, 1, 65, 7, 33, 6, 32, 76, + 69, 76, 69, 84, 5, 29, 5, 32, 82, 65, 83, 87, 2, 211, 226, 5, 65, 4, 163, + 1, 65, 7, 11, 32, 4, 154, 1, 77, 129, 180, 23, 3, 67, 69, 82, 5, 213, + 196, 16, 3, 32, 65, 71, 7, 17, 2, 32, 77, 4, 70, 85, 71, 65, 8, 18, 65, + 55, 84, 5, 17, 2, 32, 77, 2, 11, 85, 2, 147, 146, 23, 82, 4, 11, 65, 5, + 11, 32, 2, 11, 77, 2, 11, 65, 2, 11, 72, 2, 213, 177, 17, 2, 65, 80, 28, + 40, 3, 68, 65, 32, 165, 3, 2, 78, 71, 24, 174, 1, 65, 90, 76, 86, 80, + 168, 147, 7, 10, 84, 73, 82, 84, 65, 32, 84, 85, 77, 69, 210, 130, 12, + 87, 168, 199, 2, 7, 73, 83, 69, 78, 45, 73, 83, 169, 211, 1, 3, 77, 65, + 68, 6, 44, 3, 68, 69, 71, 153, 161, 22, 2, 78, 68, 5, 11, 32, 2, 169, + 238, 22, 2, 65, 68, 6, 38, 85, 141, 177, 23, 3, 73, 78, 71, 4, 216, 192, + 18, 2, 78, 71, 163, 250, 3, 72, 4, 38, 73, 181, 185, 18, 3, 65, 78, 71, + 2, 157, 189, 16, 3, 83, 69, 76, 4, 244, 144, 13, 5, 82, 65, 78, 71, 75, + 251, 209, 9, 75, 10, 108, 5, 67, 69, 67, 65, 75, 172, 202, 5, 3, 87, 73, + 71, 254, 241, 10, 76, 249, 228, 2, 5, 80, 65, 78, 89, 65, 5, 133, 168, + 18, 3, 32, 84, 69, 18, 116, 4, 83, 85, 75, 85, 42, 84, 64, 4, 87, 85, 76, + 85, 246, 186, 16, 80, 137, 224, 1, 7, 68, 73, 82, 71, 65, 32, 77, 5, 201, + 182, 22, 5, 32, 77, 69, 78, 68, 6, 26, 65, 179, 188, 16, 79, 4, 154, 188, + 16, 82, 187, 162, 6, 76, 5, 25, 4, 32, 77, 69, 76, 2, 255, 171, 23, 73, + 4, 36, 3, 76, 76, 89, 179, 231, 19, 65, 2, 143, 202, 19, 70, 4, 168, 252, + 21, 2, 89, 83, 191, 98, 73, 6, 172, 192, 14, 2, 71, 71, 194, 135, 5, 80, + 191, 199, 3, 78, 142, 18, 74, 65, 178, 78, 69, 218, 1, 72, 214, 46, 73, + 198, 6, 78, 50, 79, 111, 82, 202, 10, 200, 1, 5, 73, 84, 72, 73, 32, 234, + 4, 78, 188, 45, 6, 84, 65, 75, 65, 78, 65, 208, 13, 3, 87, 73, 32, 220, + 8, 7, 89, 65, 72, 32, 76, 73, 32, 148, 198, 4, 6, 75, 84, 79, 86, 73, 75, + 187, 239, 17, 65, 136, 1, 204, 1, 7, 76, 69, 84, 84, 69, 82, 32, 178, 2, + 83, 202, 112, 68, 228, 243, 3, 2, 86, 79, 236, 137, 3, 11, 78, 85, 77, + 66, 69, 82, 32, 83, 73, 71, 78, 214, 197, 9, 65, 189, 211, 1, 6, 69, 78, + 85, 77, 69, 82, 90, 214, 1, 68, 166, 202, 19, 65, 150, 1, 84, 230, 5, 85, + 186, 202, 1, 73, 138, 196, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, + 75, 2, 80, 2, 82, 138, 69, 72, 2, 76, 2, 77, 2, 86, 2, 89, 186, 2, 69, 3, + 79, 10, 38, 68, 186, 165, 23, 72, 187, 2, 65, 6, 198, 170, 21, 68, 242, + 250, 1, 72, 187, 2, 65, 12, 40, 4, 73, 71, 78, 32, 155, 148, 11, 69, 10, + 178, 137, 19, 67, 98, 78, 138, 216, 3, 65, 239, 1, 86, 232, 4, 42, 71, + 241, 39, 5, 78, 65, 68, 65, 32, 174, 3, 76, 11, 88, 73, 32, 82, 65, 68, + 73, 67, 65, 76, 32, 225, 218, 20, 2, 65, 82, 172, 3, 130, 2, 65, 94, 66, + 230, 2, 67, 150, 2, 68, 158, 3, 69, 198, 1, 70, 138, 2, 71, 146, 1, 72, + 210, 2, 73, 76, 2, 74, 65, 34, 75, 30, 76, 230, 1, 77, 254, 1, 78, 62, + 79, 102, 80, 110, 82, 166, 1, 83, 230, 6, 84, 226, 2, 86, 50, 87, 210, 2, + 89, 183, 243, 21, 85, 10, 56, 2, 82, 82, 174, 244, 21, 71, 202, 104, 78, + 207, 47, 88, 4, 242, 164, 22, 79, 207, 1, 73, 34, 58, 65, 38, 73, 46, 76, + 54, 79, 102, 82, 183, 212, 21, 69, 4, 166, 199, 5, 77, 235, 137, 14, 68, + 6, 134, 232, 21, 84, 238, 115, 82, 163, 70, 71, 6, 186, 231, 19, 79, 170, + 136, 2, 65, 179, 155, 1, 85, 10, 62, 76, 202, 132, 23, 65, 218, 5, 78, + 142, 5, 68, 203, 17, 87, 2, 177, 216, 3, 4, 84, 32, 79, 70, 6, 42, 73, + 134, 240, 9, 65, 155, 197, 11, 85, 2, 195, 247, 13, 83, 28, 58, 65, 70, + 76, 74, 79, 182, 173, 10, 72, 183, 129, 11, 73, 6, 38, 85, 130, 131, 23, + 82, 219, 5, 86, 2, 245, 247, 21, 2, 76, 68, 8, 42, 65, 242, 186, 7, 73, + 219, 205, 14, 79, 4, 226, 158, 23, 78, 3, 87, 10, 234, 249, 21, 82, 148, + 2, 2, 77, 80, 222, 100, 86, 134, 5, 76, 235, 56, 87, 34, 38, 69, 38, 73, + 98, 79, 195, 1, 82, 4, 218, 152, 21, 65, 159, 204, 1, 69, 8, 38, 83, 202, + 248, 13, 86, 175, 2, 80, 4, 184, 184, 19, 5, 84, 73, 78, 71, 85, 207, + 228, 3, 72, 16, 94, 84, 76, 3, 85, 66, 76, 242, 249, 12, 87, 252, 208, 9, + 2, 32, 78, 222, 23, 79, 223, 56, 71, 7, 25, 4, 84, 69, 68, 32, 4, 220, + 183, 7, 3, 67, 76, 73, 207, 160, 15, 84, 2, 227, 130, 4, 69, 6, 218, 224, + 21, 65, 222, 169, 1, 85, 219, 16, 89, 20, 106, 65, 38, 78, 32, 3, 86, 69, + 78, 244, 250, 8, 6, 77, 66, 82, 79, 73, 68, 250, 243, 13, 73, 243, 19, + 89, 6, 146, 225, 16, 82, 227, 184, 6, 84, 4, 178, 25, 67, 179, 194, 22, + 84, 5, 183, 200, 22, 73, 28, 74, 65, 50, 73, 62, 76, 38, 82, 170, 20, 69, + 226, 178, 22, 79, 223, 23, 85, 6, 250, 246, 13, 84, 130, 139, 9, 67, 131, + 22, 78, 8, 210, 232, 12, 69, 150, 133, 10, 71, 230, 19, 82, 199, 21, 83, + 4, 206, 206, 21, 85, 151, 201, 1, 89, 4, 156, 231, 21, 2, 65, 71, 207, + 175, 1, 79, 14, 58, 79, 52, 2, 82, 65, 166, 168, 19, 72, 239, 164, 2, 65, + 7, 182, 208, 22, 76, 241, 34, 5, 32, 83, 76, 79, 87, 4, 194, 198, 22, 73, + 135, 18, 83, 22, 58, 65, 142, 1, 69, 60, 5, 73, 68, 73, 78, 71, 19, 79, + 8, 38, 76, 130, 207, 22, 78, 199, 13, 73, 4, 34, 70, 141, 252, 21, 2, 66, + 69, 2, 41, 8, 32, 84, 82, 69, 69, 32, 84, 82, 2, 239, 175, 19, 85, 6, 26, + 65, 151, 148, 23, 77, 4, 242, 247, 22, 82, 175, 28, 68, 2, 195, 19, 32, + 6, 26, 82, 191, 144, 23, 79, 4, 254, 252, 22, 83, 215, 22, 78, 6, 26, 78, + 199, 252, 22, 67, 4, 26, 83, 239, 145, 23, 67, 2, 239, 129, 22, 69, 4, + 146, 252, 22, 68, 215, 22, 82, 2, 185, 175, 5, 2, 78, 73, 22, 46, 65, 34, + 69, 78, 73, 41, 3, 79, 78, 71, 4, 166, 251, 22, 77, 191, 19, 67, 8, 38, + 65, 218, 211, 22, 71, 199, 58, 69, 4, 222, 239, 13, 84, 215, 161, 9, 70, + 6, 186, 250, 22, 70, 2, 78, 215, 22, 68, 5, 129, 196, 11, 3, 32, 83, 84, + 22, 42, 69, 34, 73, 46, 79, 243, 191, 22, 65, 4, 174, 192, 22, 76, 195, + 51, 65, 4, 232, 234, 11, 2, 78, 73, 139, 195, 9, 76, 12, 34, 82, 34, 85, + 175, 191, 22, 79, 4, 238, 235, 21, 84, 187, 82, 78, 6, 26, 78, 227, 141, + 23, 84, 4, 134, 224, 21, 84, 235, 174, 1, 68, 6, 26, 79, 135, 242, 22, + 69, 4, 218, 247, 22, 83, 215, 22, 84, 10, 40, 2, 78, 69, 22, 80, 179, + 199, 22, 76, 5, 251, 153, 6, 83, 4, 166, 170, 10, 80, 247, 193, 2, 69, + 10, 54, 82, 206, 240, 21, 76, 166, 1, 79, 179, 154, 1, 73, 4, 252, 198, + 12, 2, 73, 86, 181, 141, 7, 2, 79, 70, 18, 62, 65, 42, 73, 210, 165, 10, + 79, 170, 150, 12, 85, 195, 9, 69, 6, 158, 188, 22, 73, 226, 79, 80, 3, + 84, 6, 132, 234, 12, 3, 71, 72, 84, 230, 227, 9, 86, 155, 39, 67, 74, + 170, 1, 65, 86, 67, 54, 69, 62, 72, 178, 1, 73, 46, 76, 62, 80, 106, 84, + 246, 236, 17, 87, 158, 159, 1, 78, 234, 64, 79, 130, 174, 1, 77, 226, + 103, 81, 130, 35, 75, 247, 47, 85, 6, 196, 191, 3, 9, 67, 82, 73, 70, 73, + 67, 73, 65, 76, 254, 173, 19, 76, 175, 28, 89, 4, 200, 238, 10, 2, 82, + 73, 249, 218, 5, 2, 72, 79, 8, 146, 202, 21, 67, 134, 51, 65, 174, 10, + 76, 167, 129, 1, 69, 10, 18, 69, 39, 79, 4, 198, 252, 21, 76, 199, 139, + 1, 69, 6, 40, 4, 82, 84, 32, 84, 159, 235, 22, 79, 4, 44, 5, 65, 73, 76, + 69, 68, 183, 253, 15, 72, 2, 177, 130, 21, 2, 32, 66, 4, 160, 239, 17, 2, + 67, 75, 195, 148, 5, 76, 6, 26, 65, 187, 201, 22, 73, 4, 222, 239, 22, + 86, 199, 21, 83, 10, 50, 69, 34, 73, 166, 140, 19, 82, 179, 169, 3, 79, + 4, 130, 205, 22, 65, 183, 22, 69, 2, 135, 233, 22, 82, 12, 34, 69, 34, + 79, 215, 244, 21, 65, 4, 174, 244, 22, 65, 219, 16, 80, 6, 26, 80, 251, + 237, 22, 78, 5, 199, 179, 22, 80, 30, 82, 65, 98, 73, 34, 79, 38, 82, 52, + 2, 85, 82, 32, 2, 87, 79, 143, 178, 22, 69, 6, 38, 78, 130, 221, 21, 66, + 239, 26, 76, 2, 33, 6, 78, 69, 68, 32, 76, 69, 2, 183, 225, 13, 65, 4, + 150, 197, 22, 71, 155, 39, 76, 4, 250, 242, 18, 78, 243, 138, 2, 79, 6, + 166, 232, 16, 73, 150, 232, 4, 65, 179, 155, 1, 69, 4, 238, 181, 21, 66, + 227, 37, 84, 5, 163, 191, 19, 32, 4, 230, 212, 12, 65, 209, 157, 5, 3, + 73, 76, 76, 26, 58, 65, 110, 69, 42, 72, 32, 2, 73, 78, 30, 79, 39, 82, + 6, 32, 2, 76, 75, 223, 194, 22, 84, 5, 11, 32, 2, 11, 69, 2, 17, 2, 78, + 67, 2, 213, 235, 17, 2, 76, 79, 4, 144, 176, 22, 2, 65, 80, 195, 51, 83, + 4, 194, 182, 21, 73, 231, 63, 69, 4, 182, 255, 22, 68, 3, 69, 4, 254, + 178, 21, 77, 135, 201, 1, 82, 4, 254, 173, 22, 79, 239, 80, 65, 2, 245, + 220, 20, 2, 69, 76, 186, 1, 92, 2, 76, 69, 148, 2, 5, 83, 73, 71, 78, 32, + 146, 199, 17, 65, 186, 9, 86, 247, 182, 3, 68, 110, 44, 5, 84, 84, 69, + 82, 32, 195, 187, 22, 78, 108, 166, 206, 17, 78, 142, 209, 1, 65, 38, 68, + 46, 76, 38, 82, 34, 84, 46, 86, 186, 5, 85, 206, 141, 1, 79, 238, 60, 73, + 182, 196, 1, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 162, 7, 69, + 234, 61, 70, 2, 72, 2, 77, 3, 89, 22, 94, 67, 138, 1, 83, 222, 220, 18, + 78, 190, 66, 65, 190, 158, 1, 74, 150, 3, 85, 235, 245, 1, 86, 4, 100, + 19, 79, 77, 66, 73, 78, 73, 78, 71, 32, 65, 78, 85, 83, 86, 65, 82, 65, + 32, 65, 171, 220, 18, 65, 2, 149, 245, 19, 3, 66, 79, 86, 4, 192, 190, + 12, 6, 80, 65, 67, 73, 78, 71, 255, 143, 5, 73, 162, 2, 62, 32, 173, 11, + 10, 45, 72, 73, 82, 65, 71, 65, 78, 65, 32, 154, 2, 140, 1, 7, 76, 69, + 84, 84, 69, 82, 32, 242, 9, 86, 208, 250, 18, 10, 68, 73, 71, 82, 65, 80, + 72, 32, 75, 79, 238, 204, 1, 73, 191, 146, 1, 77, 146, 2, 186, 1, 65, + 178, 1, 66, 106, 77, 186, 2, 78, 54, 83, 226, 1, 68, 2, 71, 2, 72, 2, 75, + 2, 80, 2, 82, 2, 84, 2, 86, 2, 90, 126, 87, 46, 89, 254, 237, 22, 69, 2, + 73, 2, 79, 3, 85, 19, 60, 4, 73, 78, 85, 32, 45, 7, 82, 67, 72, 65, 73, + 67, 32, 8, 130, 7, 84, 242, 215, 22, 67, 215, 22, 80, 8, 38, 89, 142, + 208, 22, 87, 235, 36, 69, 4, 242, 244, 22, 69, 3, 73, 20, 50, 73, 166, + 244, 22, 65, 2, 69, 2, 79, 3, 85, 13, 253, 4, 9, 68, 65, 75, 85, 79, 78, + 32, 78, 71, 36, 50, 73, 190, 243, 22, 65, 2, 69, 2, 79, 3, 85, 29, 29, 5, + 78, 78, 65, 78, 32, 26, 96, 15, 78, 65, 83, 65, 76, 73, 90, 69, 68, 32, + 84, 79, 78, 69, 45, 69, 5, 84, 79, 78, 69, 45, 14, 182, 242, 22, 49, 2, + 50, 2, 51, 2, 52, 2, 53, 2, 55, 3, 56, 12, 242, 241, 22, 50, 2, 51, 2, + 52, 2, 53, 2, 55, 3, 56, 13, 182, 241, 22, 65, 2, 69, 2, 73, 2, 79, 3, + 85, 76, 76, 5, 77, 65, 76, 76, 32, 182, 240, 22, 65, 2, 69, 2, 73, 2, 79, + 3, 85, 66, 142, 1, 72, 2, 82, 54, 75, 46, 84, 30, 87, 46, 89, 130, 151, + 19, 78, 198, 150, 3, 83, 210, 27, 77, 234, 36, 65, 2, 69, 2, 73, 2, 79, + 3, 85, 10, 162, 239, 22, 65, 2, 69, 2, 73, 2, 79, 3, 85, 8, 238, 238, 22, + 65, 2, 69, 2, 79, 3, 85, 4, 194, 238, 22, 79, 3, 85, 8, 166, 238, 22, 65, + 2, 69, 2, 73, 3, 79, 6, 250, 237, 22, 65, 2, 79, 3, 85, 2, 165, 199, 20, + 5, 79, 73, 67, 69, 68, 8, 52, 5, 68, 79, 85, 66, 76, 22, 80, 38, 83, 35, + 86, 2, 175, 253, 7, 69, 2, 89, 6, 82, 79, 76, 79, 78, 71, 2, 29, 5, 69, + 77, 73, 45, 86, 2, 21, 3, 79, 73, 67, 2, 33, 6, 69, 68, 32, 83, 79, 85, + 2, 199, 159, 22, 78, 174, 1, 216, 1, 7, 76, 69, 84, 84, 69, 82, 32, 128, + 2, 12, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 148, 3, 5, 83, 73, + 71, 78, 32, 88, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 138, 163, + 12, 68, 151, 224, 6, 67, 94, 226, 1, 65, 250, 162, 4, 74, 198, 231, 14, + 68, 114, 84, 230, 5, 85, 22, 86, 166, 202, 1, 73, 138, 196, 1, 78, 46, + 83, 82, 66, 2, 67, 2, 71, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, 2, 82, + 2, 87, 2, 89, 186, 2, 69, 3, 79, 7, 138, 232, 22, 65, 3, 73, 22, 122, 67, + 90, 68, 58, 70, 54, 83, 20, 12, 65, 76, 84, 69, 82, 78, 65, 84, 69, 32, + 83, 69, 213, 209, 21, 4, 84, 82, 73, 80, 4, 56, 8, 76, 79, 83, 73, 78, + 71, 32, 83, 175, 146, 21, 73, 2, 205, 218, 21, 2, 80, 73, 4, 11, 79, 4, + 196, 210, 21, 2, 85, 66, 203, 147, 1, 84, 4, 176, 145, 21, 5, 73, 76, 76, + 69, 68, 163, 57, 76, 6, 18, 69, 23, 80, 2, 163, 237, 10, 67, 4, 192, 147, + 4, 2, 65, 67, 223, 197, 17, 73, 12, 146, 223, 7, 75, 234, 231, 10, 67, + 98, 78, 238, 64, 82, 170, 162, 1, 86, 247, 244, 1, 65, 20, 66, 65, 170, + 161, 4, 86, 158, 235, 14, 69, 2, 85, 187, 202, 1, 73, 6, 216, 225, 16, 8, + 76, 84, 69, 82, 78, 65, 84, 69, 230, 129, 6, 65, 3, 73, 96, 148, 1, 7, + 76, 69, 84, 84, 69, 82, 32, 200, 1, 5, 83, 73, 71, 78, 32, 44, 5, 84, 79, + 78, 69, 32, 92, 6, 86, 79, 87, 69, 76, 32, 135, 235, 20, 68, 56, 242, + 197, 18, 79, 186, 7, 72, 158, 151, 2, 78, 214, 181, 1, 75, 2, 80, 2, 83, + 2, 84, 138, 69, 66, 2, 67, 2, 68, 2, 71, 2, 76, 2, 77, 2, 82, 2, 86, 2, + 87, 2, 89, 2, 90, 186, 2, 65, 3, 73, 4, 182, 239, 4, 67, 129, 238, 17, 2, + 83, 72, 6, 36, 5, 67, 65, 76, 89, 65, 23, 80, 5, 17, 2, 32, 80, 2, 225, + 232, 17, 3, 76, 79, 80, 10, 234, 158, 22, 69, 2, 85, 163, 64, 79, 36, 24, + 2, 76, 86, 23, 89, 2, 191, 226, 20, 73, 35, 68, 5, 66, 79, 65, 82, 68, + 36, 4, 67, 65, 80, 32, 167, 242, 2, 72, 5, 169, 149, 19, 4, 32, 65, 78, + 68, 26, 142, 146, 17, 78, 138, 180, 3, 65, 170, 35, 68, 135, 30, 84, 142, + 6, 202, 1, 65, 224, 9, 4, 77, 69, 82, 32, 204, 25, 5, 79, 74, 75, 73, 32, + 140, 6, 8, 85, 68, 65, 87, 65, 68, 73, 32, 169, 172, 7, 17, 73, 84, 65, + 78, 32, 83, 77, 65, 76, 76, 32, 83, 67, 82, 73, 80, 84, 138, 1, 56, 8, + 82, 79, 83, 72, 84, 72, 73, 32, 251, 181, 22, 78, 136, 1, 220, 1, 4, 68, + 73, 71, 73, 20, 7, 76, 69, 84, 84, 69, 82, 32, 144, 2, 7, 78, 85, 77, 66, + 69, 82, 32, 36, 12, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 136, + 2, 5, 83, 73, 71, 78, 32, 210, 1, 86, 135, 193, 8, 70, 8, 219, 138, 16, + 84, 74, 182, 1, 75, 42, 84, 142, 137, 7, 78, 202, 240, 11, 68, 194, 149, + 3, 83, 82, 66, 2, 67, 2, 71, 2, 80, 2, 86, 138, 69, 72, 2, 74, 2, 76, 2, + 77, 2, 82, 2, 89, 2, 90, 187, 2, 65, 6, 146, 213, 22, 72, 2, 75, 187, 2, + 65, 12, 194, 250, 18, 84, 170, 218, 3, 72, 187, 2, 65, 8, 162, 207, 14, + 84, 243, 170, 2, 79, 18, 70, 67, 74, 68, 66, 76, 36, 5, 77, 65, 78, 71, + 65, 139, 128, 21, 83, 4, 26, 82, 227, 129, 21, 73, 2, 149, 178, 21, 6, + 69, 83, 67, 69, 78, 84, 6, 26, 79, 175, 245, 18, 65, 4, 246, 244, 18, 85, + 175, 224, 3, 84, 4, 138, 224, 6, 79, 171, 191, 7, 73, 2, 203, 151, 21, + 76, 12, 72, 2, 66, 65, 22, 67, 20, 2, 68, 79, 234, 153, 20, 86, 247, 244, + 1, 65, 2, 247, 214, 21, 82, 2, 219, 150, 6, 65, 4, 44, 5, 85, 66, 76, 69, + 32, 143, 162, 20, 84, 2, 21, 3, 82, 73, 78, 2, 243, 161, 20, 71, 14, 44, + 5, 79, 87, 69, 76, 32, 175, 150, 20, 73, 12, 44, 5, 83, 73, 71, 78, 32, + 159, 144, 22, 76, 10, 254, 143, 4, 86, 154, 194, 18, 69, 2, 73, 2, 79, 3, + 85, 246, 2, 202, 1, 67, 240, 2, 18, 73, 78, 68, 69, 80, 69, 78, 68, 69, + 78, 84, 32, 86, 79, 87, 69, 76, 32, 220, 2, 7, 76, 69, 84, 84, 69, 82, + 32, 254, 2, 83, 208, 12, 6, 86, 79, 87, 69, 76, 32, 239, 198, 20, 68, 70, + 176, 1, 20, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 67, + 79, 69, 78, 71, 32, 169, 185, 17, 17, 85, 82, 82, 69, 78, 67, 89, 32, 83, + 89, 77, 66, 79, 76, 32, 82, 73, 68, 138, 1, 78, 154, 4, 67, 2, 75, 90, + 80, 74, 84, 54, 68, 2, 76, 210, 255, 21, 83, 158, 41, 77, 2, 82, 2, 86, + 2, 89, 190, 28, 66, 3, 72, 8, 214, 174, 22, 71, 2, 89, 246, 30, 65, 3, + 79, 42, 82, 81, 196, 1, 11, 83, 73, 71, 78, 32, 67, 79, 69, 78, 71, 32, + 50, 76, 3, 82, 26, 82, 65, 44, 6, 79, 79, 32, 84, 89, 80, 34, 85, 230, + 190, 20, 73, 199, 140, 2, 69, 8, 242, 203, 22, 65, 2, 73, 2, 81, 3, 85, + 4, 11, 69, 4, 251, 144, 10, 32, 9, 166, 192, 7, 85, 131, 139, 15, 75, 8, + 18, 81, 31, 82, 4, 238, 202, 22, 69, 3, 85, 4, 183, 185, 18, 89, 70, 138, + 1, 67, 2, 75, 42, 78, 50, 80, 30, 83, 46, 84, 54, 68, 2, 76, 238, 168, + 22, 77, 2, 82, 2, 86, 2, 89, 190, 28, 66, 2, 72, 3, 81, 8, 210, 1, 72, + 226, 199, 22, 65, 3, 79, 8, 150, 170, 22, 71, 2, 78, 2, 89, 247, 30, 79, + 6, 122, 72, 227, 199, 22, 79, 6, 202, 169, 22, 83, 190, 28, 72, 187, 2, + 65, 12, 50, 72, 0, 2, 84, 72, 226, 199, 22, 65, 3, 79, 4, 222, 199, 22, + 65, 3, 79, 130, 1, 60, 4, 73, 71, 78, 32, 221, 6, 6, 89, 77, 66, 79, 76, + 32, 46, 202, 2, 65, 104, 10, 83, 65, 77, 89, 79, 75, 32, 83, 65, 78, 22, + 66, 118, 67, 86, 75, 74, 82, 54, 84, 188, 229, 4, 3, 76, 69, 75, 168, 11, + 6, 80, 72, 78, 65, 69, 75, 148, 228, 10, 10, 89, 85, 85, 75, 65, 76, 69, + 65, 80, 73, 244, 243, 1, 3, 78, 73, 75, 236, 171, 3, 9, 77, 85, 85, 83, + 73, 75, 65, 84, 79, 141, 15, 4, 86, 73, 82, 73, 6, 100, 9, 86, 65, 75, + 82, 65, 72, 65, 83, 65, 156, 141, 18, 4, 84, 84, 72, 65, 173, 145, 4, 2, + 72, 83, 2, 239, 192, 22, 78, 8, 38, 65, 181, 183, 21, 3, 69, 89, 89, 6, + 174, 6, 84, 216, 1, 2, 78, 84, 237, 238, 20, 6, 82, 73, 89, 79, 79, 83, + 4, 248, 155, 7, 12, 65, 77, 78, 85, 67, 32, 80, 73, 73, 32, 75, 85, 235, + 223, 12, 79, 6, 240, 216, 11, 2, 65, 75, 226, 156, 9, 72, 205, 82, 4, 79, + 79, 77, 85, 4, 182, 216, 11, 79, 137, 235, 5, 4, 69, 65, 72, 77, 4, 232, + 173, 21, 4, 82, 73, 73, 83, 217, 9, 8, 79, 65, 78, 68, 65, 75, 72, 73, + 84, 128, 1, 3, 68, 65, 80, 92, 10, 76, 69, 75, 32, 65, 84, 84, 65, 75, + 32, 182, 1, 80, 72, 5, 84, 85, 84, 69, 89, 78, 66, 47, 77, 24, 22, 45, + 223, 3, 32, 20, 30, 80, 238, 2, 66, 47, 77, 8, 138, 3, 73, 37, 3, 82, 65, + 77, 20, 50, 80, 98, 66, 238, 145, 11, 77, 219, 219, 10, 83, 12, 36, 3, + 82, 65, 77, 147, 170, 22, 73, 11, 11, 45, 8, 42, 66, 238, 145, 11, 77, + 251, 228, 8, 80, 4, 194, 237, 21, 85, 151, 60, 69, 26, 44, 2, 65, 84, 44, + 3, 82, 65, 77, 91, 73, 2, 21, 3, 72, 65, 77, 2, 227, 185, 16, 65, 20, 18, + 45, 119, 32, 16, 34, 66, 32, 2, 80, 73, 15, 77, 8, 30, 69, 37, 3, 85, 79, + 78, 4, 35, 73, 4, 21, 3, 85, 79, 89, 4, 11, 32, 4, 34, 82, 241, 133, 22, + 2, 75, 79, 2, 247, 144, 21, 79, 42, 76, 10, 73, 78, 72, 69, 82, 69, 78, + 84, 32, 65, 29, 5, 83, 73, 71, 78, 32, 4, 162, 186, 22, 65, 3, 81, 38, + 102, 65, 54, 73, 30, 79, 38, 89, 216, 254, 2, 5, 67, 79, 69, 78, 71, 234, + 167, 7, 85, 239, 145, 12, 69, 10, 194, 180, 3, 65, 222, 132, 19, 69, 2, + 73, 3, 85, 7, 234, 184, 22, 69, 3, 73, 6, 206, 184, 22, 69, 2, 77, 3, 79, + 7, 170, 184, 22, 65, 3, 89, 130, 1, 146, 1, 68, 88, 7, 76, 69, 84, 84, + 69, 82, 32, 170, 2, 83, 160, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, + 78, 32, 162, 233, 15, 87, 231, 85, 65, 6, 48, 6, 79, 85, 66, 76, 69, 32, + 207, 214, 18, 65, 4, 186, 163, 10, 83, 135, 179, 8, 68, 90, 234, 1, 83, + 254, 4, 66, 42, 71, 146, 141, 6, 68, 218, 140, 12, 74, 182, 55, 65, 150, + 1, 84, 198, 208, 1, 76, 226, 195, 1, 78, 126, 67, 2, 75, 2, 80, 138, 69, + 72, 2, 77, 2, 81, 2, 82, 2, 86, 2, 89, 186, 2, 69, 2, 73, 2, 79, 3, 85, + 4, 26, 72, 155, 180, 22, 65, 2, 133, 244, 21, 3, 79, 82, 84, 12, 40, 4, + 73, 71, 78, 32, 211, 160, 10, 69, 10, 54, 83, 150, 150, 18, 78, 154, 67, + 86, 243, 148, 3, 65, 4, 26, 72, 175, 175, 17, 85, 2, 11, 65, 2, 231, 141, + 22, 68, 18, 190, 240, 3, 86, 250, 234, 14, 65, 222, 202, 1, 73, 198, 140, + 2, 69, 2, 79, 3, 85, 138, 1, 100, 7, 76, 69, 84, 84, 69, 82, 32, 176, 2, + 5, 83, 73, 71, 78, 32, 154, 190, 16, 86, 203, 252, 3, 68, 94, 222, 1, 66, + 42, 71, 146, 141, 6, 68, 178, 139, 12, 74, 222, 56, 65, 118, 82, 34, 84, + 230, 5, 85, 186, 202, 1, 73, 138, 196, 1, 78, 126, 67, 2, 75, 2, 80, 2, + 83, 138, 69, 72, 2, 76, 2, 77, 2, 86, 2, 89, 186, 2, 69, 3, 79, 6, 254, + 172, 22, 66, 2, 72, 187, 2, 65, 6, 214, 172, 22, 71, 2, 72, 187, 2, 65, + 6, 230, 145, 18, 78, 154, 67, 86, 243, 148, 3, 65, 136, 1, 140, 1, 8, 82, + 65, 84, 32, 82, 65, 73, 32, 216, 3, 2, 83, 83, 132, 243, 20, 4, 87, 73, + 70, 82, 202, 47, 80, 240, 93, 2, 77, 79, 191, 18, 84, 116, 140, 1, 7, 76, + 69, 84, 84, 69, 82, 32, 172, 1, 5, 83, 73, 71, 78, 32, 92, 11, 86, 79, + 87, 69, 76, 32, 83, 73, 71, 78, 32, 147, 233, 11, 68, 64, 194, 206, 18, + 68, 114, 84, 206, 223, 1, 78, 214, 181, 1, 66, 2, 67, 2, 71, 2, 74, 2, + 75, 2, 80, 2, 83, 138, 69, 72, 2, 76, 2, 77, 2, 82, 2, 86, 2, 89, 187, 2, + 65, 12, 236, 204, 9, 3, 84, 79, 78, 0, 2, 89, 85, 190, 252, 1, 83, 198, + 156, 10, 65, 239, 1, 86, 16, 234, 210, 18, 65, 130, 151, 3, 85, 162, 64, + 69, 2, 73, 3, 79, 13, 40, 4, 73, 78, 71, 32, 235, 231, 21, 32, 8, 96, 4, + 70, 65, 67, 69, 229, 142, 14, 14, 67, 65, 84, 32, 70, 65, 67, 69, 32, 87, + 73, 84, 72, 32, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 210, 141, 14, 83, + 159, 155, 5, 67, 4, 156, 163, 18, 3, 69, 69, 76, 171, 232, 3, 79, 4, 40, + 4, 82, 69, 65, 78, 131, 133, 22, 65, 2, 33, 6, 32, 83, 84, 65, 78, 68, 2, + 245, 245, 18, 2, 65, 82, 2, 11, 79, 2, 223, 164, 10, 78, 200, 43, 154, 1, + 65, 178, 233, 1, 69, 198, 62, 73, 226, 83, 79, 206, 33, 85, 94, 89, 190, + 221, 7, 82, 232, 195, 9, 5, 32, 66, 32, 66, 65, 142, 163, 1, 76, 239, 66, + 70, 134, 23, 142, 1, 66, 44, 6, 67, 82, 79, 83, 83, 69, 46, 68, 58, 78, + 64, 2, 79, 32, 222, 11, 82, 236, 21, 4, 83, 84, 32, 81, 77, 4, 84, 73, + 78, 32, 4, 152, 190, 14, 2, 32, 67, 155, 218, 6, 69, 2, 189, 250, 16, 6, + 32, 83, 84, 73, 67, 75, 4, 200, 250, 12, 5, 89, 32, 66, 69, 69, 247, 234, + 8, 68, 4, 230, 197, 9, 68, 161, 241, 10, 7, 71, 85, 65, 71, 69, 32, 84, + 174, 1, 200, 2, 3, 72, 79, 32, 28, 7, 76, 69, 84, 84, 69, 82, 32, 214, 5, + 83, 148, 1, 9, 84, 79, 78, 69, 32, 77, 65, 73, 32, 84, 11, 86, 79, 87, + 69, 76, 32, 83, 73, 71, 78, 32, 228, 170, 4, 2, 75, 79, 148, 254, 12, 2, + 89, 65, 230, 199, 2, 69, 170, 51, 68, 212, 138, 1, 7, 67, 65, 78, 67, 69, + 76, 76, 221, 68, 6, 78, 73, 71, 71, 65, 72, 4, 238, 128, 22, 77, 3, 78, + 94, 176, 1, 3, 70, 79, 32, 78, 75, 96, 2, 76, 79, 50, 80, 154, 1, 83, 86, + 84, 30, 72, 214, 156, 16, 78, 234, 222, 5, 66, 2, 67, 2, 68, 2, 77, 2, + 82, 2, 87, 2, 89, 247, 30, 79, 8, 42, 70, 174, 170, 15, 83, 175, 182, 5, + 84, 4, 134, 206, 21, 79, 155, 62, 65, 10, 26, 72, 175, 157, 22, 79, 8, + 32, 3, 77, 85, 32, 231, 2, 79, 4, 194, 196, 21, 78, 211, 57, 71, 7, 17, + 2, 32, 76, 4, 218, 203, 21, 73, 67, 79, 30, 52, 4, 65, 76, 73, 32, 210, + 1, 72, 179, 154, 22, 79, 24, 230, 200, 6, 68, 34, 84, 206, 6, 78, 134, + 207, 13, 66, 2, 67, 2, 71, 2, 74, 147, 219, 1, 76, 8, 52, 9, 65, 78, 83, + 75, 82, 73, 84, 32, 83, 71, 79, 4, 174, 152, 22, 72, 3, 83, 6, 26, 72, + 179, 154, 22, 79, 4, 11, 79, 4, 11, 32, 4, 218, 166, 15, 83, 175, 182, 5, + 84, 6, 112, 14, 69, 77, 73, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, + 133, 191, 18, 8, 73, 71, 78, 32, 80, 65, 76, 73, 4, 186, 192, 21, 78, + 211, 57, 76, 8, 50, 84, 184, 158, 17, 2, 67, 65, 223, 246, 4, 69, 4, 182, + 249, 21, 72, 247, 30, 73, 32, 106, 65, 44, 5, 77, 65, 73, 32, 75, 218, + 133, 18, 89, 162, 58, 85, 186, 202, 1, 69, 2, 73, 199, 140, 2, 79, 11, + 158, 151, 22, 65, 2, 73, 2, 77, 3, 89, 4, 146, 199, 21, 65, 3, 79, 166, + 1, 32, 2, 71, 69, 179, 143, 21, 73, 164, 1, 26, 32, 223, 246, 13, 82, + 160, 1, 254, 1, 66, 44, 4, 71, 82, 69, 69, 22, 79, 250, 1, 84, 242, 237, + 11, 68, 36, 2, 85, 80, 164, 193, 3, 12, 76, 69, 70, 84, 32, 84, 82, 73, + 65, 78, 71, 76, 200, 203, 4, 5, 80, 85, 82, 80, 76, 12, 3, 82, 69, 68, 0, + 6, 89, 69, 76, 76, 79, 87, 179, 66, 67, 10, 40, 3, 82, 79, 87, 201, 1, 2, + 76, 85, 4, 151, 253, 19, 78, 10, 48, 3, 78, 69, 32, 129, 1, 4, 82, 65, + 78, 71, 4, 164, 157, 8, 5, 68, 79, 84, 32, 79, 133, 234, 2, 18, 82, 73, + 78, 71, 32, 79, 86, 69, 82, 32, 84, 87, 79, 32, 82, 73, 78, 71, 6, 11, + 69, 6, 11, 32, 6, 230, 189, 20, 67, 162, 21, 68, 155, 28, 83, 116, 156, + 1, 3, 87, 79, 32, 108, 10, 89, 80, 69, 32, 80, 73, 69, 67, 69, 32, 249, + 203, 18, 17, 82, 73, 80, 76, 69, 32, 86, 69, 82, 84, 73, 67, 65, 76, 32, + 66, 65, 4, 166, 237, 17, 68, 141, 93, 19, 82, 73, 78, 71, 83, 32, 79, 86, + 69, 82, 32, 79, 78, 69, 32, 82, 73, 78, 71, 110, 182, 1, 67, 136, 2, 9, + 68, 73, 65, 71, 79, 78, 65, 76, 32, 218, 1, 76, 186, 2, 82, 158, 1, 83, + 204, 3, 6, 85, 80, 80, 69, 82, 32, 145, 2, 9, 86, 69, 82, 84, 69, 88, 32, + 79, 70, 14, 80, 9, 69, 78, 84, 82, 69, 32, 79, 70, 32, 73, 7, 82, 79, 83, + 83, 66, 65, 82, 8, 252, 8, 6, 90, 32, 87, 73, 84, 72, 190, 132, 22, 75, + 2, 88, 3, 89, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 40, 3, 76, 79, 87, 1, + 3, 85, 80, 80, 2, 189, 200, 10, 2, 69, 82, 12, 48, 6, 85, 80, 80, 69, 82, + 32, 219, 225, 9, 76, 8, 56, 4, 76, 69, 70, 84, 149, 226, 9, 4, 82, 73, + 71, 72, 5, 11, 32, 2, 21, 3, 65, 78, 68, 2, 17, 2, 32, 76, 2, 17, 2, 79, + 87, 2, 133, 216, 21, 2, 69, 82, 26, 48, 5, 79, 87, 69, 82, 32, 129, 3, 2, + 69, 70, 24, 84, 5, 76, 69, 70, 84, 32, 56, 6, 82, 73, 71, 72, 84, 32, + 158, 6, 72, 179, 1, 84, 8, 22, 65, 207, 7, 67, 4, 194, 1, 78, 187, 221, + 20, 82, 10, 22, 65, 151, 7, 67, 6, 192, 1, 13, 78, 68, 32, 85, 80, 80, + 69, 82, 32, 82, 73, 71, 72, 173, 207, 19, 2, 82, 67, 4, 44, 4, 65, 73, + 83, 69, 77, 3, 73, 71, 72, 2, 53, 11, 68, 32, 85, 80, 80, 69, 82, 32, 76, + 69, 70, 2, 155, 135, 19, 84, 2, 189, 136, 21, 3, 84, 32, 65, 34, 48, 5, + 72, 79, 82, 84, 32, 77, 3, 84, 69, 77, 4, 40, 3, 76, 79, 87, 1, 3, 85, + 80, 80, 2, 217, 4, 4, 69, 82, 32, 84, 31, 44, 6, 32, 87, 73, 84, 72, 32, + 171, 1, 45, 8, 44, 5, 76, 69, 70, 84, 32, 30, 82, 59, 67, 4, 82, 67, 251, + 216, 10, 74, 2, 21, 3, 73, 71, 72, 2, 11, 84, 2, 17, 2, 32, 67, 2, 221, + 224, 20, 4, 82, 79, 83, 83, 20, 48, 2, 49, 50, 22, 50, 26, 52, 255, 184, + 11, 51, 5, 235, 218, 14, 51, 9, 11, 51, 7, 11, 52, 5, 163, 131, 22, 53, + 18, 62, 72, 96, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 83, 84, 4, 65, 14, + 65, 76, 70, 32, 86, 69, 82, 84, 69, 88, 32, 79, 70, 32, 4, 138, 130, 22, + 77, 3, 87, 6, 17, 2, 84, 32, 6, 26, 67, 227, 129, 19, 65, 4, 202, 92, 82, + 159, 164, 17, 79, 2, 209, 245, 8, 3, 69, 82, 77, 2, 239, 226, 14, 32, 6, + 53, 11, 85, 65, 82, 84, 69, 82, 32, 77, 79, 79, 78, 7, 223, 240, 6, 32, + 158, 20, 150, 1, 67, 224, 46, 18, 69, 80, 73, 71, 82, 65, 80, 72, 73, 67, + 32, 76, 69, 84, 84, 69, 82, 32, 252, 1, 7, 76, 69, 84, 84, 69, 82, 32, + 247, 12, 83, 206, 7, 56, 8, 65, 80, 73, 84, 65, 76, 32, 76, 167, 187, 20, + 82, 204, 7, 76, 6, 69, 84, 84, 69, 82, 32, 169, 45, 8, 73, 71, 65, 84, + 85, 82, 69, 32, 200, 7, 154, 2, 65, 158, 3, 66, 154, 1, 67, 254, 1, 68, + 186, 2, 69, 174, 2, 70, 82, 71, 194, 1, 72, 146, 1, 73, 154, 2, 74, 118, + 75, 126, 76, 234, 2, 77, 114, 78, 134, 2, 79, 198, 2, 80, 114, 81, 62, + 82, 190, 2, 83, 130, 3, 84, 142, 3, 85, 234, 1, 86, 146, 1, 87, 118, 88, + 50, 89, 167, 1, 90, 93, 172, 1, 6, 32, 87, 73, 84, 72, 32, 166, 1, 69, + 246, 65, 78, 228, 153, 14, 6, 70, 82, 73, 67, 65, 78, 158, 193, 3, 76, + 210, 183, 2, 86, 186, 164, 1, 65, 2, 79, 2, 85, 3, 89, 66, 246, 64, 66, + 34, 68, 108, 3, 82, 73, 78, 242, 33, 77, 250, 21, 67, 150, 50, 72, 158, + 1, 79, 198, 10, 71, 186, 2, 65, 246, 173, 3, 73, 194, 170, 13, 84, 219, + 183, 3, 83, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 206, 184, 1, 65, 239, + 199, 3, 77, 21, 60, 6, 32, 87, 73, 84, 72, 32, 190, 68, 82, 239, 142, 21, + 69, 14, 154, 67, 84, 138, 60, 70, 210, 57, 76, 234, 154, 16, 68, 170, + 206, 2, 72, 247, 165, 1, 83, 33, 108, 6, 32, 87, 73, 84, 72, 32, 204, 70, + 7, 76, 79, 83, 69, 68, 32, 73, 54, 85, 206, 223, 20, 79, 139, 60, 72, 20, + 94, 67, 198, 181, 1, 65, 154, 233, 3, 80, 130, 129, 15, 72, 182, 50, 66, + 242, 34, 68, 211, 80, 83, 8, 198, 68, 69, 190, 113, 73, 151, 152, 19, 65, + 35, 76, 6, 32, 87, 73, 84, 72, 32, 178, 1, 90, 25, 6, 79, 85, 66, 76, 69, + 32, 24, 78, 83, 226, 15, 67, 202, 47, 84, 218, 117, 76, 234, 154, 16, 68, + 171, 206, 2, 72, 8, 92, 13, 77, 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, + 32, 90, 150, 138, 1, 72, 159, 185, 20, 84, 5, 209, 72, 2, 32, 87, 4, 254, + 71, 87, 239, 180, 10, 84, 91, 104, 6, 32, 87, 73, 84, 72, 32, 152, 1, 2, + 90, 72, 134, 76, 71, 130, 148, 17, 84, 210, 143, 4, 83, 63, 78, 70, 254, + 73, 67, 158, 2, 68, 194, 16, 84, 158, 23, 77, 250, 1, 86, 238, 45, 72, + 158, 1, 79, 198, 10, 71, 186, 2, 65, 246, 173, 3, 73, 62, 66, 223, 225, + 16, 83, 7, 11, 32, 4, 138, 70, 87, 211, 8, 82, 9, 33, 6, 32, 87, 73, 84, + 72, 32, 6, 166, 154, 20, 72, 166, 85, 68, 211, 80, 83, 35, 76, 6, 32, 87, + 73, 84, 72, 32, 190, 81, 76, 154, 200, 18, 65, 147, 211, 2, 72, 20, 154, + 84, 67, 250, 90, 65, 178, 174, 3, 66, 190, 25, 77, 150, 136, 4, 79, 154, + 154, 11, 72, 166, 85, 68, 211, 80, 83, 29, 76, 6, 32, 87, 73, 84, 72, 32, + 250, 126, 65, 250, 239, 7, 87, 247, 173, 12, 69, 20, 186, 82, 66, 34, 67, + 46, 68, 230, 196, 19, 72, 247, 165, 1, 83, 59, 80, 6, 32, 87, 73, 84, 72, + 32, 132, 88, 2, 78, 83, 250, 239, 20, 79, 207, 36, 83, 40, 138, 1, 68, + 162, 83, 67, 242, 1, 77, 142, 1, 84, 130, 71, 72, 158, 1, 79, 198, 10, + 71, 186, 2, 65, 246, 173, 3, 73, 62, 66, 223, 225, 16, 83, 10, 22, 79, + 191, 83, 73, 6, 218, 121, 85, 183, 207, 18, 84, 11, 33, 6, 32, 87, 73, + 84, 72, 32, 8, 42, 67, 226, 130, 18, 84, 219, 183, 3, 83, 4, 234, 170, 1, + 73, 131, 223, 3, 82, 25, 33, 6, 32, 87, 73, 84, 72, 32, 22, 182, 89, 67, + 34, 68, 50, 83, 222, 79, 65, 138, 1, 76, 250, 206, 7, 79, 155, 154, 11, + 72, 41, 60, 6, 32, 87, 73, 84, 72, 32, 190, 94, 65, 155, 138, 21, 74, 32, + 138, 1, 66, 36, 2, 68, 79, 52, 7, 77, 73, 68, 68, 76, 69, 32, 38, 83, + 174, 2, 67, 182, 91, 72, 230, 72, 65, 138, 1, 76, 175, 215, 16, 84, 4, + 222, 193, 12, 69, 143, 237, 8, 65, 6, 22, 85, 231, 91, 84, 2, 249, 241, + 4, 2, 66, 76, 4, 154, 255, 17, 84, 159, 151, 3, 68, 4, 214, 2, 77, 135, + 180, 21, 84, 19, 44, 6, 32, 87, 73, 84, 72, 32, 207, 94, 73, 10, 242, + 165, 1, 65, 242, 155, 16, 68, 198, 60, 84, 231, 145, 2, 72, 33, 48, 6, + 32, 87, 73, 84, 72, 32, 139, 229, 21, 74, 28, 102, 67, 44, 2, 83, 77, + 178, 96, 76, 134, 65, 71, 186, 2, 65, 102, 68, 158, 207, 7, 79, 183, 136, + 9, 84, 6, 190, 132, 1, 69, 22, 73, 155, 184, 19, 65, 2, 209, 228, 10, 10, + 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, 101, 108, 6, 32, 87, 73, 84, 72, + 32, 178, 103, 76, 138, 159, 4, 80, 142, 151, 9, 77, 134, 197, 7, 73, 2, + 79, 3, 85, 84, 144, 1, 2, 76, 79, 34, 77, 226, 96, 67, 70, 68, 154, 2, + 79, 38, 83, 66, 84, 106, 86, 222, 44, 72, 242, 12, 71, 186, 2, 65, 246, + 173, 3, 73, 63, 66, 4, 158, 99, 78, 139, 254, 20, 79, 8, 154, 99, 65, + 235, 159, 4, 73, 19, 44, 6, 32, 87, 73, 84, 72, 32, 155, 23, 72, 14, 242, + 103, 70, 34, 83, 170, 56, 65, 154, 234, 18, 72, 167, 85, 68, 7, 33, 6, + 32, 87, 73, 84, 72, 32, 4, 190, 105, 68, 39, 83, 43, 94, 32, 156, 1, 8, + 69, 86, 69, 82, 83, 69, 68, 32, 216, 111, 3, 85, 77, 32, 167, 147, 4, 65, + 28, 40, 5, 87, 73, 84, 72, 32, 215, 112, 82, 26, 134, 78, 67, 218, 29, + 68, 170, 2, 84, 174, 48, 65, 138, 1, 76, 238, 172, 3, 73, 142, 162, 4, + 79, 143, 192, 12, 83, 8, 218, 110, 72, 154, 156, 4, 79, 238, 194, 13, 67, + 239, 143, 3, 69, 49, 136, 1, 6, 32, 87, 73, 84, 72, 32, 136, 1, 5, 77, + 65, 76, 76, 32, 240, 115, 2, 65, 76, 234, 1, 72, 152, 2, 2, 73, 71, 167, + 138, 4, 67, 32, 86, 67, 138, 112, 65, 118, 68, 138, 1, 83, 174, 1, 86, + 242, 247, 7, 79, 155, 154, 11, 72, 10, 226, 112, 65, 230, 10, 69, 82, 79, + 203, 31, 73, 4, 184, 163, 19, 11, 81, 32, 87, 73, 84, 72, 32, 72, 79, 79, + 75, 173, 247, 1, 7, 67, 65, 80, 73, 84, 65, 76, 59, 136, 1, 6, 32, 87, + 73, 84, 72, 32, 168, 1, 6, 85, 82, 78, 69, 68, 32, 188, 123, 2, 72, 79, + 176, 1, 2, 79, 78, 74, 82, 167, 218, 20, 90, 22, 82, 67, 50, 68, 254, + 152, 1, 76, 234, 237, 3, 82, 170, 251, 14, 72, 247, 165, 1, 83, 8, 202, + 120, 69, 22, 73, 62, 79, 223, 183, 19, 65, 6, 190, 142, 1, 73, 179, 165, + 16, 79, 18, 246, 39, 73, 242, 138, 3, 65, 242, 164, 18, 72, 2, 75, 2, 76, + 2, 77, 2, 84, 3, 86, 79, 26, 32, 183, 135, 5, 80, 74, 44, 5, 87, 73, 84, + 72, 32, 251, 178, 20, 66, 72, 166, 132, 1, 67, 74, 68, 150, 2, 72, 170, + 1, 77, 134, 1, 79, 142, 1, 84, 186, 9, 71, 186, 2, 65, 246, 173, 3, 73, + 62, 66, 158, 221, 14, 82, 195, 132, 2, 83, 23, 88, 6, 32, 87, 73, 84, 72, + 32, 178, 139, 1, 73, 66, 79, 186, 184, 19, 69, 151, 144, 1, 89, 8, 226, + 138, 1, 68, 134, 226, 16, 84, 231, 145, 2, 72, 19, 48, 6, 32, 87, 73, 84, + 72, 32, 167, 147, 7, 89, 14, 190, 144, 1, 67, 18, 68, 70, 71, 186, 2, 65, + 155, 234, 18, 72, 7, 201, 140, 1, 7, 32, 87, 73, 84, 72, 32, 68, 29, 48, + 6, 32, 87, 73, 84, 72, 32, 143, 165, 17, 79, 24, 154, 143, 1, 67, 18, 68, + 70, 71, 22, 72, 42, 76, 254, 1, 65, 238, 199, 3, 77, 202, 144, 13, 84, + 219, 183, 3, 83, 25, 33, 6, 32, 87, 73, 84, 72, 32, 22, 254, 56, 67, 150, + 88, 65, 102, 68, 38, 76, 34, 83, 242, 231, 3, 80, 131, 129, 15, 72, 4, + 178, 209, 10, 73, 195, 232, 10, 79, 12, 128, 1, 5, 65, 82, 67, 72, 65, + 30, 73, 64, 9, 82, 69, 86, 69, 82, 83, 69, 68, 32, 253, 206, 10, 7, 83, + 73, 68, 69, 87, 65, 89, 2, 141, 242, 13, 2, 73, 67, 4, 220, 107, 5, 78, + 86, 69, 82, 84, 201, 211, 14, 3, 32, 76, 79, 4, 194, 206, 21, 70, 3, 80, + 138, 1, 242, 2, 65, 36, 2, 66, 73, 152, 1, 21, 73, 78, 86, 69, 82, 84, + 69, 68, 32, 71, 76, 79, 84, 84, 65, 76, 32, 83, 84, 79, 80, 72, 2, 80, + 72, 16, 2, 82, 69, 222, 1, 83, 132, 8, 3, 84, 87, 79, 182, 20, 87, 128, + 170, 4, 2, 68, 69, 148, 5, 2, 76, 65, 206, 11, 71, 136, 192, 15, 20, 86, + 79, 73, 67, 69, 68, 32, 76, 65, 82, 89, 78, 71, 69, 65, 76, 32, 83, 80, + 73, 199, 119, 89, 4, 230, 187, 4, 76, 235, 191, 16, 73, 6, 76, 7, 76, 65, + 66, 73, 65, 76, 32, 29, 8, 68, 69, 78, 84, 65, 76, 32, 80, 4, 26, 80, + 239, 206, 4, 67, 2, 205, 141, 16, 6, 69, 82, 67, 85, 83, 83, 7, 33, 6, + 32, 87, 73, 84, 72, 32, 4, 234, 243, 4, 67, 235, 165, 16, 83, 2, 207, 81, + 65, 8, 104, 7, 86, 69, 82, 83, 69, 68, 32, 233, 243, 4, 13, 84, 82, 79, + 70, 76, 69, 88, 32, 67, 76, 73, 67, 75, 4, 80, 3, 69, 83, 72, 161, 8, 12, + 71, 76, 79, 84, 84, 65, 76, 32, 83, 84, 79, 80, 2, 213, 133, 1, 2, 32, + 76, 96, 172, 1, 13, 77, 65, 76, 76, 32, 67, 65, 80, 73, 84, 65, 76, 32, + 244, 112, 10, 84, 82, 69, 84, 67, 72, 69, 68, 32, 67, 237, 193, 19, 10, + 73, 78, 79, 76, 79, 71, 73, 67, 65, 76, 90, 230, 1, 69, 30, 73, 22, 76, + 74, 79, 42, 82, 126, 84, 254, 182, 4, 66, 214, 41, 71, 158, 161, 16, 65, + 162, 64, 67, 2, 68, 2, 70, 2, 72, 2, 74, 2, 75, 2, 77, 2, 78, 2, 80, 2, + 81, 2, 83, 2, 85, 2, 86, 2, 87, 2, 89, 3, 90, 7, 150, 195, 21, 84, 3, 90, + 5, 147, 220, 4, 78, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 162, 221, 8, 66, + 183, 182, 12, 83, 9, 238, 95, 80, 194, 227, 20, 69, 3, 85, 11, 92, 8, 69, + 86, 69, 82, 83, 69, 68, 32, 236, 128, 1, 5, 32, 87, 73, 84, 72, 231, 176, + 20, 85, 4, 166, 194, 21, 78, 3, 82, 13, 33, 6, 85, 82, 78, 69, 68, 32, + 10, 230, 193, 21, 69, 2, 71, 2, 75, 2, 77, 3, 82, 186, 11, 136, 1, 5, 77, + 65, 76, 76, 32, 145, 131, 1, 22, 85, 66, 83, 67, 82, 73, 80, 84, 32, 83, + 77, 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, 32, 150, 11, 76, 15, 67, 65, + 80, 73, 84, 65, 76, 32, 76, 69, 84, 84, 69, 82, 32, 43, 76, 4, 18, 73, 3, + 85, 2, 255, 226, 4, 32, 146, 11, 80, 6, 69, 84, 84, 69, 82, 32, 161, 128, + 1, 8, 73, 71, 65, 84, 85, 82, 69, 32, 128, 11, 182, 2, 65, 190, 5, 66, + 198, 3, 67, 186, 4, 68, 182, 4, 69, 174, 9, 70, 214, 1, 71, 162, 2, 72, + 230, 2, 73, 166, 7, 74, 182, 1, 75, 178, 2, 76, 206, 6, 77, 178, 2, 78, + 218, 3, 79, 242, 8, 80, 218, 2, 81, 174, 1, 82, 142, 8, 83, 250, 10, 84, + 246, 13, 85, 218, 9, 86, 130, 3, 87, 110, 88, 226, 2, 89, 171, 3, 90, + 101, 118, 32, 198, 3, 69, 82, 78, 192, 226, 4, 4, 76, 80, 72, 65, 146, + 176, 15, 86, 186, 164, 1, 65, 2, 79, 2, 85, 3, 89, 72, 88, 5, 87, 73, 84, + 72, 32, 157, 229, 5, 11, 82, 69, 86, 69, 82, 83, 69, 68, 45, 83, 67, 70, + 150, 1, 66, 34, 68, 54, 82, 170, 34, 77, 250, 21, 67, 150, 50, 72, 158, + 1, 79, 198, 10, 71, 186, 2, 65, 246, 173, 3, 73, 194, 170, 13, 84, 219, + 183, 3, 83, 12, 161, 106, 4, 82, 69, 86, 69, 12, 22, 79, 151, 57, 73, 8, + 214, 57, 84, 211, 13, 85, 10, 26, 73, 175, 231, 4, 69, 8, 26, 78, 179, + 174, 4, 71, 6, 17, 2, 71, 32, 6, 236, 58, 4, 65, 66, 79, 86, 175, 219, + 18, 66, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 242, 116, 71, 186, 2, 65, + 239, 199, 3, 77, 2, 233, 193, 10, 7, 71, 76, 73, 67, 65, 78, 65, 41, 140, + 1, 6, 32, 87, 73, 84, 72, 32, 130, 1, 65, 108, 11, 76, 65, 67, 75, 76, + 69, 84, 84, 69, 82, 32, 38, 82, 174, 201, 4, 79, 195, 197, 16, 69, 18, + 110, 84, 138, 60, 70, 210, 57, 76, 230, 224, 3, 77, 174, 7, 80, 218, 178, + 12, 68, 170, 206, 2, 72, 247, 165, 1, 83, 2, 255, 7, 79, 8, 64, 5, 82, + 82, 69, 68, 32, 161, 81, 6, 83, 69, 76, 73, 78, 69, 6, 182, 32, 65, 206, + 147, 21, 69, 3, 79, 6, 242, 195, 4, 79, 235, 239, 16, 69, 2, 233, 181, + 10, 4, 79, 75, 69, 78, 47, 108, 6, 32, 87, 73, 84, 72, 32, 196, 1, 2, 72, + 73, 92, 6, 76, 79, 83, 69, 68, 32, 90, 85, 207, 223, 20, 79, 24, 102, 67, + 182, 113, 65, 154, 233, 3, 80, 218, 5, 82, 170, 251, 14, 72, 182, 50, 66, + 242, 34, 68, 211, 80, 83, 10, 54, 69, 190, 113, 73, 202, 246, 6, 85, 207, + 161, 12, 65, 4, 245, 51, 5, 68, 73, 76, 76, 65, 7, 49, 10, 32, 87, 73, + 84, 72, 32, 76, 79, 87, 32, 4, 162, 107, 82, 45, 4, 76, 69, 70, 84, 8, + 34, 73, 18, 79, 159, 200, 4, 82, 2, 163, 88, 78, 4, 130, 221, 4, 80, 203, + 141, 9, 77, 4, 37, 7, 65, 84, 82, 73, 76, 76, 79, 5, 133, 138, 13, 5, 32, + 87, 73, 84, 72, 73, 96, 6, 32, 87, 73, 84, 72, 32, 182, 1, 69, 34, 79, + 178, 1, 90, 178, 213, 4, 66, 239, 196, 16, 85, 32, 98, 83, 34, 84, 238, + 32, 67, 226, 45, 77, 170, 31, 76, 138, 217, 3, 72, 138, 15, 80, 219, 178, + 12, 68, 4, 134, 68, 72, 159, 185, 20, 84, 4, 26, 79, 139, 205, 19, 65, 2, + 143, 137, 20, 80, 8, 246, 78, 90, 131, 185, 20, 76, 16, 60, 6, 84, 76, + 69, 83, 83, 32, 49, 5, 85, 66, 76, 69, 32, 8, 26, 74, 203, 171, 21, 73, + 7, 167, 198, 4, 32, 8, 42, 87, 142, 202, 4, 82, 227, 234, 5, 84, 2, 215, + 234, 6, 89, 11, 11, 32, 8, 26, 87, 151, 198, 4, 68, 2, 169, 90, 5, 73, + 84, 72, 32, 67, 119, 112, 6, 32, 87, 73, 84, 72, 32, 214, 4, 71, 72, 2, + 78, 71, 68, 2, 83, 72, 164, 1, 2, 90, 72, 211, 145, 17, 84, 76, 182, 1, + 67, 158, 2, 68, 110, 78, 214, 15, 84, 158, 23, 77, 250, 1, 86, 190, 3, + 70, 178, 42, 72, 158, 1, 79, 198, 10, 71, 186, 2, 65, 246, 173, 3, 73, + 62, 66, 194, 64, 82, 159, 161, 16, 83, 24, 92, 6, 69, 68, 73, 76, 76, 65, + 32, 9, 73, 82, 67, 85, 77, 70, 76, 69, 88, 203, 255, 19, 65, 5, 169, 149, + 4, 3, 32, 65, 78, 19, 11, 32, 16, 40, 4, 65, 78, 68, 32, 219, 132, 19, + 66, 14, 162, 86, 67, 130, 2, 72, 226, 11, 71, 186, 2, 65, 238, 199, 3, + 77, 210, 165, 4, 68, 251, 234, 8, 84, 12, 22, 79, 227, 98, 73, 10, 28, 2, + 84, 32, 227, 51, 85, 8, 192, 88, 5, 65, 66, 79, 86, 69, 251, 170, 18, 66, + 2, 183, 149, 14, 79, 4, 209, 129, 7, 13, 89, 80, 84, 79, 76, 79, 71, 73, + 67, 65, 76, 32, 65, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 158, 195, 4, 67, + 231, 9, 80, 13, 33, 6, 32, 87, 73, 84, 72, 32, 10, 88, 10, 68, 79, 85, + 66, 76, 69, 32, 66, 65, 82, 230, 203, 4, 80, 142, 1, 67, 207, 4, 82, 5, + 217, 204, 4, 4, 32, 65, 78, 68, 15, 11, 32, 12, 38, 82, 37, 5, 87, 73, + 84, 72, 32, 2, 209, 145, 14, 4, 69, 86, 69, 82, 10, 54, 67, 178, 202, 4, + 80, 218, 5, 82, 247, 153, 14, 84, 4, 158, 216, 7, 85, 207, 161, 12, 65, + 17, 84, 6, 32, 87, 73, 84, 72, 32, 73, 11, 69, 78, 71, 32, 68, 73, 71, + 82, 65, 80, 72, 10, 134, 194, 4, 77, 174, 7, 80, 130, 129, 15, 72, 166, + 85, 68, 211, 80, 83, 5, 133, 164, 18, 8, 32, 87, 73, 84, 72, 32, 84, 82, + 37, 72, 6, 32, 87, 73, 84, 72, 32, 126, 76, 154, 200, 18, 65, 147, 211, + 2, 72, 22, 218, 3, 67, 250, 90, 65, 178, 174, 3, 66, 190, 25, 77, 174, + 33, 80, 234, 230, 3, 79, 154, 154, 11, 72, 166, 85, 68, 211, 80, 83, 8, + 33, 6, 79, 84, 84, 65, 76, 32, 8, 194, 200, 18, 83, 250, 212, 2, 65, 2, + 73, 3, 85, 39, 140, 1, 6, 32, 87, 73, 84, 72, 32, 150, 45, 65, 232, 25, + 12, 79, 79, 75, 69, 68, 32, 83, 67, 72, 87, 65, 32, 174, 243, 3, 69, 211, + 225, 16, 86, 24, 86, 66, 34, 67, 46, 68, 214, 91, 76, 146, 232, 3, 80, + 130, 129, 15, 72, 247, 165, 1, 83, 2, 129, 143, 13, 3, 82, 69, 86, 6, + 158, 59, 69, 154, 32, 73, 151, 152, 19, 65, 8, 234, 87, 73, 226, 190, 3, + 69, 255, 223, 12, 79, 75, 80, 6, 32, 87, 73, 84, 72, 32, 222, 4, 78, 188, + 1, 2, 79, 84, 187, 147, 21, 83, 48, 178, 1, 67, 34, 68, 210, 1, 77, 64, + 6, 79, 71, 79, 78, 69, 75, 78, 84, 130, 71, 72, 226, 11, 71, 186, 2, 65, + 246, 173, 3, 73, 62, 66, 144, 64, 6, 83, 84, 82, 79, 75, 69, 51, 82, 4, + 210, 88, 73, 151, 152, 19, 65, 14, 18, 73, 47, 79, 4, 217, 26, 7, 65, 69, + 82, 69, 83, 73, 83, 10, 28, 2, 84, 32, 215, 37, 85, 8, 64, 10, 65, 66, + 79, 86, 69, 32, 65, 78, 68, 32, 239, 244, 18, 66, 6, 150, 84, 71, 186, 2, + 65, 183, 216, 16, 84, 4, 29, 5, 65, 67, 82, 79, 78, 5, 217, 36, 4, 32, + 65, 78, 68, 7, 145, 73, 15, 32, 65, 78, 68, 32, 68, 79, 84, 32, 65, 66, + 79, 86, 69, 32, 4, 21, 3, 73, 76, 68, 4, 151, 142, 5, 69, 16, 46, 83, 93, + 7, 86, 69, 82, 84, 69, 68, 32, 12, 29, 5, 85, 76, 65, 82, 32, 12, 162, + 148, 21, 68, 2, 70, 2, 71, 2, 82, 2, 83, 3, 84, 4, 26, 65, 251, 252, 20, + 79, 2, 195, 183, 17, 76, 6, 206, 163, 4, 65, 181, 183, 6, 5, 73, 70, 73, + 69, 68, 13, 33, 6, 32, 87, 73, 84, 72, 32, 10, 94, 67, 148, 180, 4, 13, + 68, 79, 84, 32, 65, 66, 79, 86, 69, 32, 65, 78, 68, 239, 173, 16, 83, 6, + 178, 82, 73, 130, 223, 3, 82, 151, 185, 15, 65, 29, 48, 6, 32, 87, 73, + 84, 72, 32, 227, 142, 21, 82, 24, 98, 67, 34, 68, 50, 83, 222, 79, 65, + 138, 1, 76, 146, 232, 3, 80, 234, 230, 3, 79, 155, 154, 11, 72, 4, 210, + 48, 69, 175, 184, 19, 65, 6, 214, 70, 73, 182, 197, 3, 69, 203, 177, 4, + 79, 4, 29, 5, 84, 82, 79, 75, 69, 5, 161, 25, 6, 32, 65, 78, 68, 32, 68, + 79, 136, 1, 6, 32, 87, 73, 84, 72, 32, 250, 3, 65, 38, 69, 48, 5, 79, 78, + 71, 32, 83, 130, 180, 4, 83, 2, 90, 238, 196, 16, 85, 219, 16, 74, 50, + 178, 1, 66, 86, 67, 56, 2, 68, 79, 100, 3, 77, 73, 68, 130, 2, 72, 230, + 72, 65, 138, 1, 76, 150, 224, 3, 73, 162, 1, 82, 222, 2, 70, 130, 4, 80, + 158, 239, 12, 84, 219, 183, 3, 83, 6, 36, 3, 69, 76, 84, 219, 211, 20, + 65, 5, 193, 181, 4, 6, 32, 65, 78, 68, 32, 80, 8, 166, 44, 69, 22, 73, + 206, 150, 7, 85, 207, 161, 12, 65, 8, 38, 84, 25, 5, 85, 66, 76, 69, 32, + 4, 141, 25, 2, 32, 66, 4, 242, 172, 4, 77, 227, 186, 15, 66, 8, 36, 4, + 68, 76, 69, 32, 207, 44, 45, 6, 238, 162, 17, 84, 210, 150, 3, 82, 79, + 68, 4, 173, 154, 4, 4, 77, 66, 68, 65, 6, 158, 181, 4, 90, 241, 133, 13, + 3, 78, 73, 83, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 18, 68, 35, 72, 4, + 206, 63, 73, 255, 200, 19, 79, 2, 197, 172, 4, 2, 73, 71, 27, 56, 6, 32, + 87, 73, 84, 72, 32, 102, 73, 219, 246, 20, 85, 16, 138, 72, 65, 182, 223, + 3, 67, 186, 2, 77, 174, 7, 80, 218, 178, 12, 68, 198, 60, 84, 231, 145, + 2, 72, 6, 25, 4, 68, 68, 76, 69, 6, 76, 7, 45, 87, 69, 76, 83, 72, 32, + 225, 140, 17, 6, 32, 83, 67, 79, 84, 83, 4, 242, 250, 19, 76, 211, 139, + 1, 86, 49, 58, 32, 216, 2, 2, 71, 32, 182, 242, 20, 85, 219, 16, 74, 40, + 88, 5, 87, 73, 84, 72, 32, 209, 142, 6, 11, 80, 82, 69, 67, 69, 68, 69, + 68, 32, 66, 89, 38, 122, 67, 74, 76, 158, 37, 77, 234, 27, 71, 186, 2, + 65, 102, 68, 182, 232, 3, 80, 218, 5, 82, 146, 225, 3, 79, 183, 136, 9, + 84, 10, 170, 36, 69, 22, 73, 134, 255, 3, 82, 202, 151, 3, 85, 207, 161, + 12, 65, 6, 132, 66, 3, 79, 78, 71, 202, 2, 73, 183, 239, 3, 69, 2, 25, 4, + 87, 73, 84, 72, 2, 229, 219, 17, 5, 32, 84, 73, 76, 68, 115, 116, 6, 32, + 87, 73, 84, 72, 32, 186, 6, 76, 52, 4, 80, 69, 78, 32, 226, 181, 13, 77, + 134, 197, 7, 73, 2, 79, 3, 85, 86, 154, 1, 67, 70, 68, 152, 1, 2, 76, 79, + 86, 77, 46, 79, 38, 83, 66, 84, 106, 86, 222, 44, 72, 242, 12, 71, 186, + 2, 65, 246, 173, 3, 73, 62, 66, 195, 64, 82, 14, 172, 49, 9, 73, 82, 67, + 85, 77, 70, 76, 69, 88, 211, 167, 19, 65, 14, 18, 73, 47, 79, 4, 221, 13, + 7, 65, 69, 82, 69, 83, 73, 83, 10, 22, 84, 171, 47, 85, 6, 11, 32, 6, + 140, 13, 5, 65, 66, 79, 86, 69, 147, 208, 18, 66, 6, 66, 78, 140, 204, + 12, 6, 87, 32, 82, 73, 78, 71, 255, 177, 8, 79, 2, 159, 21, 71, 6, 11, + 65, 6, 189, 2, 4, 67, 82, 79, 78, 4, 217, 11, 5, 71, 79, 78, 69, 75, 4, + 25, 4, 84, 82, 79, 75, 4, 11, 69, 5, 213, 49, 2, 32, 65, 8, 25, 4, 73, + 76, 68, 69, 9, 29, 5, 32, 65, 78, 68, 32, 6, 162, 47, 68, 142, 13, 65, + 239, 199, 3, 77, 6, 81, 18, 69, 82, 84, 73, 67, 65, 76, 32, 76, 73, 78, + 69, 32, 66, 69, 76, 79, 87, 7, 221, 43, 4, 32, 65, 78, 68, 2, 205, 131, + 10, 8, 68, 32, 80, 79, 76, 73, 83, 72, 16, 26, 79, 131, 166, 4, 69, 13, + 48, 6, 32, 87, 73, 84, 72, 32, 151, 250, 20, 69, 8, 210, 55, 71, 186, 2, + 65, 242, 238, 3, 82, 159, 161, 16, 83, 25, 44, 6, 32, 87, 73, 84, 72, 32, + 179, 1, 72, 18, 86, 70, 34, 83, 170, 56, 65, 238, 225, 3, 77, 174, 7, 80, + 130, 129, 15, 72, 167, 85, 68, 2, 205, 191, 14, 3, 76, 79, 85, 6, 210, + 28, 84, 133, 171, 12, 6, 81, 85, 73, 82, 82, 69, 4, 26, 65, 223, 247, 20, + 73, 2, 193, 183, 5, 18, 82, 89, 78, 71, 69, 65, 76, 32, 86, 79, 73, 67, + 69, 68, 32, 70, 82, 73, 13, 48, 6, 32, 87, 73, 84, 72, 32, 139, 161, 4, + 80, 8, 42, 68, 16, 4, 72, 79, 79, 75, 23, 83, 2, 227, 44, 73, 5, 223, + 190, 18, 32, 2, 197, 26, 6, 84, 82, 79, 75, 69, 32, 77, 90, 32, 228, 4, + 8, 69, 86, 69, 82, 83, 69, 68, 32, 148, 2, 2, 85, 77, 179, 147, 4, 65, + 46, 36, 4, 87, 73, 84, 72, 235, 6, 82, 44, 26, 32, 247, 185, 15, 79, 42, + 166, 1, 67, 50, 68, 200, 1, 8, 70, 73, 83, 72, 72, 79, 79, 75, 66, 76, + 34, 84, 142, 18, 77, 162, 30, 65, 246, 173, 3, 73, 166, 59, 80, 234, 230, + 3, 79, 143, 192, 12, 83, 6, 170, 19, 69, 154, 255, 3, 82, 151, 185, 15, + 65, 8, 11, 79, 8, 24, 2, 84, 32, 111, 85, 6, 26, 66, 175, 245, 19, 65, 4, + 25, 4, 69, 76, 79, 87, 5, 29, 5, 32, 65, 78, 68, 32, 2, 191, 249, 3, 77, + 2, 21, 3, 66, 76, 69, 2, 11, 32, 2, 227, 46, 71, 7, 29, 5, 32, 65, 78, + 68, 32, 4, 214, 146, 4, 77, 175, 7, 80, 4, 222, 49, 73, 131, 236, 3, 79, + 4, 162, 211, 18, 73, 195, 61, 65, 22, 162, 1, 72, 40, 6, 79, 80, 69, 78, + 32, 69, 216, 147, 4, 8, 82, 32, 87, 73, 84, 72, 32, 70, 168, 2, 3, 83, + 67, 82, 242, 218, 14, 69, 190, 87, 67, 159, 166, 1, 75, 2, 11, 65, 2, + 177, 246, 9, 2, 76, 70, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 138, 157, 4, + 82, 171, 251, 14, 72, 5, 11, 32, 2, 11, 82, 2, 237, 141, 17, 3, 79, 84, + 85, 85, 180, 1, 6, 32, 87, 73, 84, 72, 32, 218, 4, 65, 66, 67, 218, 1, + 72, 34, 73, 156, 2, 13, 81, 85, 65, 84, 32, 82, 69, 86, 69, 82, 83, 69, + 68, 145, 231, 9, 6, 84, 73, 82, 82, 85, 80, 40, 110, 65, 34, 67, 86, 68, + 138, 1, 83, 174, 1, 86, 210, 9, 77, 186, 135, 4, 80, 234, 230, 3, 79, + 155, 154, 11, 72, 4, 205, 1, 4, 67, 85, 84, 69, 12, 58, 65, 230, 10, 69, + 82, 79, 202, 31, 73, 203, 246, 6, 85, 4, 113, 3, 82, 79, 78, 8, 32, 3, + 79, 84, 32, 207, 32, 73, 6, 26, 66, 131, 237, 19, 65, 4, 25, 4, 69, 76, + 79, 87, 5, 11, 32, 2, 225, 232, 19, 3, 65, 78, 68, 4, 22, 72, 203, 42, + 87, 2, 21, 3, 79, 82, 84, 2, 17, 2, 32, 83, 2, 11, 84, 2, 21, 3, 82, 79, + 75, 2, 11, 69, 2, 17, 2, 32, 79, 2, 249, 217, 19, 4, 86, 69, 82, 76, 2, + 37, 7, 69, 82, 84, 73, 67, 65, 76, 2, 205, 40, 2, 32, 76, 4, 46, 76, 217, + 221, 19, 5, 75, 72, 65, 32, 89, 2, 247, 12, 84, 18, 48, 3, 72, 87, 65, + 97, 5, 82, 73, 80, 84, 32, 11, 33, 6, 32, 87, 73, 84, 72, 32, 8, 222, 35, + 71, 186, 2, 65, 242, 238, 3, 82, 171, 251, 14, 72, 8, 26, 82, 207, 132, + 4, 71, 5, 165, 182, 11, 5, 32, 87, 73, 84, 72, 2, 185, 235, 16, 3, 65, + 82, 80, 14, 48, 7, 68, 69, 87, 65, 89, 83, 32, 199, 1, 71, 12, 110, 79, + 56, 4, 84, 85, 82, 78, 208, 200, 10, 11, 68, 73, 65, 69, 82, 69, 83, 73, + 90, 69, 68, 215, 154, 10, 85, 7, 26, 80, 135, 135, 4, 32, 2, 245, 235, 9, + 2, 69, 78, 2, 145, 134, 13, 2, 69, 68, 2, 161, 233, 16, 4, 77, 79, 73, + 68, 2, 191, 252, 9, 32, 147, 1, 188, 1, 6, 32, 87, 73, 84, 72, 32, 192, + 3, 2, 69, 83, 70, 72, 130, 2, 79, 102, 82, 54, 85, 174, 131, 4, 67, 202, + 1, 83, 132, 253, 10, 9, 65, 73, 76, 76, 69, 83, 83, 32, 80, 251, 215, 5, + 90, 34, 110, 67, 182, 1, 68, 66, 77, 170, 31, 76, 146, 232, 3, 80, 168, + 5, 4, 72, 79, 79, 75, 50, 82, 159, 161, 16, 83, 10, 58, 69, 22, 73, 62, + 79, 146, 150, 7, 85, 207, 161, 12, 65, 2, 143, 164, 12, 68, 2, 37, 7, 82, + 67, 85, 77, 70, 76, 69, 2, 139, 174, 18, 88, 2, 17, 2, 77, 77, 2, 227, + 173, 18, 65, 8, 32, 2, 73, 65, 187, 186, 16, 79, 4, 154, 21, 71, 215, 6, + 69, 4, 17, 2, 73, 68, 4, 26, 45, 255, 255, 3, 68, 2, 149, 142, 4, 6, 72, + 69, 73, 71, 72, 84, 6, 45, 9, 72, 32, 68, 73, 71, 82, 65, 80, 72, 7, 243, + 133, 4, 32, 8, 64, 12, 32, 87, 73, 84, 72, 32, 83, 84, 82, 73, 75, 69, + 43, 79, 2, 157, 175, 16, 5, 84, 72, 82, 79, 85, 6, 17, 2, 82, 78, 7, 41, + 8, 32, 87, 73, 84, 72, 32, 83, 84, 4, 25, 4, 82, 79, 75, 69, 5, 11, 32, + 2, 133, 215, 3, 6, 84, 72, 82, 79, 85, 71, 8, 26, 78, 203, 133, 4, 80, 6, + 17, 2, 69, 32, 6, 206, 206, 4, 83, 186, 207, 10, 70, 155, 168, 3, 84, 2, + 17, 2, 69, 83, 2, 11, 73, 2, 179, 183, 20, 76, 76, 44, 5, 82, 78, 69, 68, + 32, 195, 217, 20, 77, 74, 162, 1, 68, 22, 72, 66, 73, 54, 79, 142, 1, 82, + 110, 84, 22, 86, 198, 129, 4, 65, 38, 77, 198, 2, 89, 134, 189, 16, 85, + 218, 19, 69, 2, 71, 2, 75, 2, 76, 3, 87, 2, 183, 155, 17, 69, 7, 141, + 242, 3, 11, 32, 87, 73, 84, 72, 32, 70, 73, 83, 72, 72, 5, 11, 78, 2, + 249, 213, 9, 5, 83, 85, 76, 65, 82, 12, 66, 69, 232, 230, 3, 7, 32, 79, + 80, 69, 78, 45, 79, 159, 29, 80, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, + 162, 174, 18, 72, 143, 248, 1, 83, 15, 33, 6, 32, 87, 73, 84, 72, 32, 12, + 198, 247, 3, 77, 174, 7, 80, 130, 5, 76, 206, 154, 14, 84, 183, 97, 72, + 5, 135, 255, 3, 32, 7, 11, 32, 4, 161, 5, 4, 87, 73, 84, 72, 97, 90, 32, + 152, 228, 3, 6, 80, 83, 73, 76, 79, 78, 234, 239, 16, 69, 2, 73, 2, 77, + 3, 79, 82, 48, 3, 66, 65, 82, 49, 5, 87, 73, 84, 72, 32, 5, 245, 17, 8, + 32, 87, 73, 84, 72, 32, 83, 72, 78, 142, 1, 67, 74, 68, 150, 2, 72, 170, + 1, 77, 134, 1, 79, 106, 82, 38, 84, 186, 9, 71, 82, 83, 234, 1, 65, 246, + 173, 3, 73, 62, 66, 143, 66, 76, 6, 152, 203, 4, 9, 73, 82, 67, 85, 77, + 70, 76, 69, 88, 191, 223, 14, 65, 18, 52, 8, 73, 65, 69, 82, 69, 83, 73, + 83, 131, 1, 79, 13, 11, 32, 10, 40, 4, 65, 78, 68, 32, 235, 174, 18, 66, + 8, 50, 67, 226, 13, 71, 186, 2, 65, 239, 199, 3, 77, 2, 235, 168, 19, 65, + 6, 26, 85, 219, 158, 18, 84, 4, 21, 3, 66, 76, 69, 4, 11, 32, 4, 138, 13, + 71, 187, 2, 65, 14, 11, 79, 14, 28, 2, 82, 78, 207, 81, 79, 13, 29, 5, + 32, 65, 78, 68, 32, 10, 66, 72, 226, 11, 71, 186, 2, 65, 190, 237, 7, 68, + 251, 234, 8, 84, 2, 229, 80, 2, 79, 79, 10, 29, 5, 65, 67, 82, 79, 78, + 11, 29, 5, 32, 65, 78, 68, 32, 8, 50, 68, 214, 10, 71, 186, 2, 65, 183, + 216, 16, 84, 2, 171, 10, 73, 6, 29, 5, 71, 79, 78, 69, 75, 7, 11, 32, 4, + 25, 4, 65, 78, 68, 32, 4, 178, 12, 65, 183, 216, 16, 84, 4, 142, 251, 3, + 69, 223, 156, 14, 73, 6, 25, 4, 73, 76, 68, 69, 7, 11, 32, 4, 26, 65, + 203, 169, 18, 66, 2, 17, 2, 78, 68, 2, 11, 32, 2, 139, 11, 65, 29, 84, 6, + 32, 87, 73, 84, 72, 32, 162, 1, 73, 66, 79, 186, 184, 19, 69, 151, 144, + 1, 89, 14, 82, 68, 234, 242, 3, 80, 142, 1, 67, 146, 7, 82, 130, 231, 12, + 84, 231, 145, 2, 72, 4, 26, 73, 255, 246, 7, 79, 2, 17, 2, 65, 71, 2, + 253, 152, 20, 2, 79, 78, 2, 41, 8, 83, 73, 71, 79, 84, 72, 73, 67, 2, + 183, 234, 18, 32, 6, 33, 6, 76, 65, 80, 85, 75, 32, 6, 210, 177, 20, 65, + 2, 79, 3, 85, 19, 33, 6, 32, 87, 73, 84, 72, 32, 16, 202, 4, 67, 18, 68, + 70, 71, 186, 2, 65, 206, 139, 18, 82, 207, 94, 72, 17, 33, 6, 32, 87, 73, + 84, 72, 32, 14, 42, 68, 32, 2, 76, 79, 187, 239, 3, 80, 4, 222, 3, 73, + 171, 194, 19, 79, 8, 60, 11, 78, 71, 32, 76, 69, 70, 84, 32, 76, 69, 71, + 79, 87, 7, 11, 32, 4, 60, 7, 65, 78, 68, 32, 76, 79, 87, 65, 4, 87, 73, + 84, 72, 2, 17, 2, 32, 82, 2, 21, 3, 73, 71, 72, 2, 207, 149, 11, 84, 2, + 237, 194, 19, 4, 32, 83, 69, 82, 33, 48, 6, 32, 87, 73, 84, 72, 32, 227, + 150, 16, 79, 28, 110, 67, 18, 68, 70, 71, 22, 72, 42, 76, 22, 83, 234, 1, + 65, 238, 199, 3, 77, 202, 144, 13, 84, 155, 179, 1, 82, 2, 203, 3, 73, 6, + 26, 73, 223, 158, 16, 79, 2, 17, 2, 65, 69, 2, 239, 187, 16, 82, 2, 163, + 212, 18, 82, 4, 17, 2, 79, 79, 4, 239, 136, 5, 75, 2, 239, 200, 19, 79, + 4, 26, 72, 231, 145, 20, 84, 2, 21, 3, 79, 82, 84, 2, 249, 247, 7, 6, 32, + 82, 73, 71, 72, 84, 31, 33, 6, 32, 87, 73, 84, 72, 32, 28, 98, 65, 22, + 67, 82, 68, 38, 76, 34, 83, 198, 224, 3, 77, 174, 7, 80, 218, 5, 82, 171, + 251, 14, 72, 2, 147, 186, 13, 67, 6, 42, 73, 202, 246, 6, 85, 207, 161, + 12, 65, 2, 153, 199, 11, 4, 82, 67, 85, 77, 6, 154, 187, 3, 69, 255, 223, + 12, 79, 2, 11, 73, 2, 215, 178, 12, 78, 4, 26, 87, 215, 142, 20, 84, 2, + 139, 150, 17, 65, 18, 90, 70, 194, 190, 9, 73, 172, 9, 6, 76, 79, 78, 71, + 32, 83, 190, 217, 10, 83, 219, 5, 79, 10, 34, 70, 178, 189, 20, 73, 3, + 76, 7, 174, 189, 20, 73, 3, 76, 36, 150, 1, 83, 254, 187, 20, 65, 2, 69, + 2, 72, 2, 73, 2, 74, 2, 75, 2, 76, 2, 77, 2, 78, 2, 79, 2, 80, 2, 82, 2, + 84, 2, 85, 2, 86, 3, 88, 5, 215, 230, 4, 67, 226, 5, 240, 1, 2, 65, 70, + 144, 1, 2, 70, 84, 190, 38, 79, 20, 5, 80, 67, 72, 65, 32, 164, 8, 8, 83, + 83, 45, 84, 72, 65, 78, 32, 236, 245, 12, 5, 85, 75, 79, 84, 72, 218, + 194, 3, 68, 228, 23, 6, 86, 69, 76, 32, 83, 76, 218, 233, 2, 77, 239, 79, + 71, 6, 120, 3, 76, 69, 83, 160, 196, 1, 14, 32, 70, 76, 85, 84, 84, 69, + 82, 73, 78, 71, 32, 73, 78, 241, 213, 11, 3, 89, 32, 71, 2, 151, 239, 14, + 83, 134, 4, 58, 32, 250, 20, 45, 217, 2, 6, 87, 65, 82, 68, 83, 32, 246, + 1, 166, 2, 65, 234, 4, 66, 202, 1, 67, 96, 2, 68, 79, 112, 2, 72, 65, + 166, 3, 76, 46, 77, 38, 79, 102, 82, 146, 3, 83, 82, 84, 222, 1, 87, 210, + 178, 8, 70, 236, 5, 14, 74, 85, 83, 84, 73, 70, 73, 69, 68, 32, 82, 73, + 71, 72, 86, 78, 202, 1, 80, 153, 13, 10, 86, 69, 82, 84, 73, 67, 65, 76, + 32, 66, 28, 22, 78, 143, 4, 82, 22, 28, 2, 68, 32, 191, 3, 71, 16, 96, 6, + 76, 79, 87, 69, 82, 32, 32, 6, 82, 73, 71, 72, 84, 32, 89, 6, 85, 80, 80, + 69, 82, 32, 4, 202, 1, 65, 163, 192, 17, 79, 6, 50, 84, 133, 139, 18, 6, + 68, 79, 85, 66, 76, 69, 4, 174, 153, 17, 82, 211, 232, 1, 65, 6, 82, 65, + 53, 16, 79, 78, 69, 32, 69, 73, 71, 72, 84, 72, 32, 66, 76, 79, 67, 75, + 2, 161, 151, 17, 8, 78, 68, 32, 82, 73, 71, 72, 84, 5, 169, 143, 19, 17, + 32, 67, 79, 78, 84, 65, 73, 78, 73, 78, 71, 32, 66, 76, 65, 67, 75, 6, + 250, 183, 8, 69, 185, 1, 4, 76, 69, 32, 66, 6, 26, 67, 215, 187, 8, 82, + 2, 177, 187, 8, 5, 32, 76, 69, 83, 83, 12, 40, 4, 65, 82, 66, 32, 239, + 187, 8, 76, 8, 40, 4, 68, 79, 87, 78, 1, 2, 85, 80, 4, 57, 12, 32, 82, + 73, 71, 72, 84, 32, 66, 65, 82, 66, 32, 4, 164, 133, 17, 4, 68, 79, 87, + 78, 1, 2, 85, 80, 14, 178, 187, 8, 85, 166, 26, 79, 242, 235, 2, 69, 141, + 168, 2, 8, 76, 79, 83, 69, 68, 32, 69, 78, 10, 44, 5, 85, 66, 76, 69, 32, + 223, 187, 8, 84, 8, 146, 189, 8, 87, 190, 30, 65, 154, 132, 3, 81, 199, + 199, 4, 80, 38, 36, 3, 76, 70, 32, 215, 193, 8, 78, 36, 132, 2, 6, 67, + 73, 82, 67, 76, 69, 214, 188, 8, 66, 90, 70, 82, 72, 50, 82, 58, 84, 70, + 87, 246, 13, 76, 22, 85, 176, 200, 8, 30, 73, 78, 86, 69, 82, 83, 69, 32, + 77, 69, 68, 73, 85, 77, 32, 83, 72, 65, 68, 69, 32, 65, 78, 68, 32, 82, + 73, 71, 72, 84, 255, 26, 77, 11, 33, 6, 32, 87, 73, 84, 72, 32, 8, 42, + 84, 130, 132, 18, 70, 191, 214, 1, 68, 4, 146, 203, 5, 72, 255, 140, 13, + 87, 4, 232, 162, 1, 2, 85, 71, 215, 156, 7, 79, 2, 141, 151, 19, 4, 85, + 76, 84, 73, 8, 36, 3, 78, 69, 32, 167, 192, 8, 85, 6, 246, 178, 17, 81, + 146, 4, 69, 45, 5, 84, 72, 73, 82, 68, 30, 44, 5, 73, 71, 72, 84, 32, + 251, 192, 8, 65, 28, 152, 1, 5, 65, 82, 82, 79, 87, 132, 1, 12, 68, 79, + 85, 66, 76, 69, 32, 65, 82, 82, 79, 87, 30, 87, 138, 238, 8, 79, 162, + 143, 8, 66, 54, 83, 211, 68, 84, 11, 11, 32, 8, 72, 5, 87, 73, 84, 72, + 32, 145, 210, 18, 7, 84, 72, 82, 79, 85, 71, 72, 6, 198, 190, 16, 68, + 234, 183, 3, 86, 79, 83, 7, 169, 230, 8, 2, 32, 87, 4, 254, 248, 8, 65, + 227, 191, 8, 72, 34, 142, 191, 8, 45, 70, 69, 78, 73, 168, 1, 3, 80, 69, + 69, 26, 81, 227, 2, 85, 26, 106, 82, 230, 195, 8, 72, 130, 226, 7, 79, + 212, 134, 1, 8, 87, 79, 32, 84, 72, 73, 82, 68, 151, 198, 1, 65, 6, 40, + 4, 73, 65, 78, 71, 223, 198, 8, 65, 4, 140, 242, 4, 8, 76, 69, 32, 66, + 69, 83, 73, 68, 231, 186, 12, 85, 16, 130, 201, 8, 72, 202, 1, 73, 181, + 200, 10, 2, 82, 73, 62, 128, 1, 9, 80, 79, 73, 78, 84, 73, 78, 71, 32, + 138, 1, 83, 174, 201, 8, 70, 226, 2, 72, 153, 7, 7, 84, 79, 45, 82, 73, + 71, 72, 30, 86, 65, 182, 206, 8, 67, 94, 68, 58, 77, 58, 82, 182, 1, 83, + 74, 84, 211, 172, 8, 69, 6, 242, 206, 8, 78, 158, 175, 8, 84, 159, 2, 73, + 4, 44, 5, 73, 68, 69, 32, 65, 227, 209, 8, 72, 2, 205, 147, 1, 2, 82, 67, + 210, 1, 172, 1, 5, 65, 82, 82, 79, 87, 174, 5, 66, 70, 72, 242, 3, 84, + 178, 2, 87, 214, 209, 8, 68, 210, 1, 70, 170, 7, 76, 26, 79, 34, 80, 50, + 82, 70, 83, 242, 206, 8, 67, 47, 81, 73, 26, 32, 211, 231, 17, 45, 68, + 102, 65, 138, 1, 84, 132, 2, 5, 87, 73, 84, 72, 32, 166, 211, 8, 70, 217, + 165, 10, 4, 79, 86, 69, 82, 12, 44, 5, 66, 79, 86, 69, 32, 147, 213, 8, + 78, 10, 64, 4, 83, 72, 79, 82, 138, 212, 8, 82, 182, 230, 4, 65, 55, 84, + 2, 147, 251, 18, 84, 12, 56, 7, 72, 82, 79, 85, 71, 72, 32, 53, 3, 79, + 32, 66, 6, 150, 184, 13, 83, 238, 206, 4, 76, 211, 149, 2, 88, 6, 32, 2, + 65, 82, 155, 214, 8, 76, 5, 169, 164, 12, 23, 32, 79, 86, 69, 82, 32, 82, + 73, 71, 72, 84, 87, 65, 82, 68, 83, 32, 65, 82, 82, 79, 87, 32, 36, 118, + 76, 250, 213, 8, 68, 182, 1, 80, 30, 83, 38, 84, 138, 210, 8, 77, 38, 78, + 122, 69, 150, 153, 1, 72, 171, 165, 1, 86, 4, 214, 169, 17, 65, 171, 247, + 1, 79, 8, 158, 216, 8, 65, 172, 10, 5, 79, 84, 84, 79, 77, 219, 200, 8, + 76, 30, 26, 65, 183, 176, 17, 69, 26, 48, 6, 82, 80, 79, 79, 78, 32, 191, + 210, 19, 78, 24, 80, 10, 87, 73, 84, 72, 32, 66, 65, 82, 66, 32, 173, + 241, 9, 4, 79, 86, 69, 82, 22, 40, 4, 68, 79, 87, 78, 117, 2, 85, 80, 10, + 26, 32, 167, 182, 17, 87, 8, 158, 219, 8, 66, 188, 2, 7, 65, 66, 79, 86, + 69, 32, 82, 234, 208, 8, 70, 179, 5, 84, 12, 26, 32, 179, 181, 17, 87, + 10, 60, 6, 65, 66, 79, 86, 69, 32, 146, 173, 17, 70, 179, 5, 84, 6, 42, + 76, 137, 219, 8, 4, 82, 73, 71, 72, 4, 174, 217, 8, 69, 147, 223, 4, 79, + 54, 44, 2, 82, 73, 254, 222, 8, 79, 159, 5, 87, 34, 44, 5, 65, 78, 71, + 76, 69, 179, 227, 8, 80, 30, 56, 8, 45, 72, 69, 65, 68, 69, 68, 32, 175, + 187, 17, 32, 28, 52, 5, 65, 82, 82, 79, 87, 254, 179, 17, 68, 39, 80, 25, + 11, 32, 22, 244, 223, 8, 9, 79, 86, 69, 82, 32, 82, 73, 71, 72, 22, 87, + 135, 208, 8, 84, 6, 26, 72, 195, 229, 8, 65, 4, 45, 9, 73, 84, 69, 32, + 65, 82, 82, 79, 87, 5, 225, 184, 17, 2, 32, 87, 5, 167, 249, 18, 80, 148, + 1, 252, 1, 15, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, + 32, 124, 7, 76, 69, 84, 84, 69, 82, 32, 236, 1, 12, 80, 85, 78, 67, 84, + 85, 65, 84, 73, 79, 78, 32, 198, 1, 83, 172, 1, 11, 86, 79, 87, 69, 76, + 32, 83, 73, 71, 78, 32, 175, 150, 18, 68, 18, 66, 75, 22, 78, 182, 143, + 20, 76, 2, 77, 2, 80, 2, 82, 3, 84, 5, 207, 190, 19, 65, 5, 241, 207, 12, + 4, 89, 73, 78, 45, 78, 194, 1, 75, 2, 80, 178, 149, 7, 68, 130, 236, 10, + 66, 2, 70, 2, 71, 2, 72, 2, 77, 138, 15, 78, 170, 181, 1, 84, 46, 67, 2, + 83, 138, 69, 74, 2, 76, 2, 82, 2, 86, 2, 87, 2, 89, 187, 2, 65, 6, 146, + 139, 20, 72, 2, 76, 187, 2, 65, 10, 82, 84, 40, 14, 78, 89, 69, 84, 32, + 84, 72, 89, 79, 79, 77, 32, 84, 65, 43, 67, 6, 38, 65, 21, 5, 83, 72, 79, + 79, 75, 2, 171, 249, 6, 45, 5, 17, 2, 32, 67, 2, 161, 146, 15, 3, 69, 82, + 45, 8, 92, 4, 73, 71, 78, 32, 37, 15, 85, 66, 74, 79, 73, 78, 69, 68, 32, + 76, 69, 84, 84, 69, 82, 4, 254, 237, 15, 78, 231, 208, 2, 82, 4, 11, 32, + 4, 150, 136, 20, 82, 3, 89, 14, 182, 179, 16, 85, 206, 141, 1, 79, 250, + 198, 2, 65, 186, 2, 69, 3, 73, 52, 138, 1, 65, 128, 4, 11, 69, 81, 85, + 65, 76, 32, 84, 79, 32, 79, 82, 200, 1, 2, 66, 85, 42, 67, 186, 1, 79, + 210, 2, 87, 131, 248, 18, 83, 16, 40, 5, 66, 79, 86, 69, 32, 171, 4, 78, + 12, 152, 1, 7, 71, 82, 69, 65, 84, 69, 82, 110, 83, 176, 1, 19, 68, 79, + 85, 66, 76, 69, 45, 76, 73, 78, 69, 32, 69, 81, 85, 65, 76, 32, 65, 219, + 223, 15, 76, 2, 181, 5, 23, 45, 84, 72, 65, 78, 32, 65, 66, 79, 86, 69, + 32, 68, 79, 85, 66, 76, 69, 45, 76, 73, 78, 69, 6, 152, 1, 7, 73, 77, 73, + 76, 65, 82, 32, 93, 26, 76, 65, 78, 84, 69, 68, 32, 69, 81, 85, 65, 76, + 32, 65, 66, 79, 86, 69, 32, 71, 82, 69, 65, 84, 69, 82, 4, 18, 65, 59, + 79, 2, 25, 4, 66, 79, 86, 69, 2, 233, 238, 17, 2, 32, 71, 2, 227, 2, 82, + 2, 145, 2, 6, 45, 84, 72, 65, 78, 32, 4, 17, 2, 68, 32, 4, 220, 3, 5, 78, + 79, 84, 32, 65, 145, 158, 13, 11, 83, 73, 78, 71, 76, 69, 45, 76, 73, 78, + 69, 4, 141, 128, 13, 5, 84, 32, 78, 79, 84, 4, 65, 14, 76, 79, 83, 69, + 68, 32, 66, 89, 32, 67, 85, 82, 86, 69, 5, 11, 32, 2, 61, 13, 65, 66, 79, + 86, 69, 32, 83, 76, 65, 78, 84, 69, 68, 2, 17, 2, 32, 69, 2, 203, 240, + 14, 81, 18, 40, 2, 82, 32, 145, 226, 11, 2, 86, 69, 16, 114, 65, 48, 16, + 83, 76, 65, 78, 84, 69, 68, 32, 69, 81, 85, 65, 76, 32, 84, 79, 246, 252, + 12, 69, 131, 237, 4, 71, 2, 161, 186, 9, 7, 80, 80, 82, 79, 88, 73, 77, + 9, 49, 10, 32, 87, 73, 84, 72, 32, 68, 79, 84, 32, 6, 26, 65, 167, 205, + 11, 73, 4, 25, 4, 66, 79, 86, 69, 5, 231, 171, 18, 32, 6, 25, 4, 73, 84, + 72, 32, 6, 66, 67, 40, 8, 81, 85, 69, 83, 84, 73, 79, 78, 171, 173, 19, + 68, 2, 253, 203, 11, 5, 73, 82, 67, 76, 69, 2, 17, 2, 32, 77, 2, 17, 2, + 65, 82, 2, 179, 128, 19, 75, 136, 11, 190, 1, 71, 186, 5, 77, 166, 7, 78, + 156, 65, 2, 80, 83, 20, 3, 83, 85, 32, 194, 216, 15, 82, 136, 35, 11, 86, + 82, 69, 32, 84, 79, 85, 82, 78, 79, 73, 214, 59, 79, 190, 221, 1, 90, + 191, 84, 66, 44, 26, 65, 57, 2, 72, 84, 2, 233, 169, 9, 9, 84, 85, 82, + 69, 32, 79, 80, 69, 78, 42, 38, 32, 137, 4, 4, 78, 73, 78, 71, 36, 146, + 1, 69, 38, 70, 126, 82, 40, 3, 76, 69, 70, 86, 83, 42, 84, 160, 200, 7, + 3, 66, 76, 85, 238, 170, 9, 87, 254, 41, 86, 226, 78, 71, 219, 69, 67, 2, + 193, 225, 17, 4, 73, 71, 72, 84, 8, 94, 73, 205, 175, 18, 17, 79, 85, 82, + 32, 80, 79, 73, 78, 84, 69, 68, 32, 66, 76, 65, 67, 75, 4, 213, 221, 17, + 2, 86, 69, 4, 36, 3, 73, 71, 72, 187, 152, 18, 65, 2, 245, 241, 1, 16, + 84, 32, 84, 79, 82, 84, 79, 73, 83, 69, 32, 83, 72, 69, 76, 76, 6, 246, + 252, 16, 72, 254, 97, 65, 43, 73, 4, 144, 170, 16, 2, 87, 69, 21, 3, 72, + 82, 69, 7, 29, 5, 32, 77, 79, 79, 68, 5, 143, 151, 8, 32, 138, 1, 80, 3, + 66, 85, 32, 253, 158, 8, 11, 73, 84, 69, 68, 32, 76, 73, 65, 66, 73, 76, + 136, 1, 128, 1, 7, 76, 69, 84, 84, 69, 82, 32, 222, 1, 83, 200, 2, 5, 86, + 79, 87, 69, 76, 206, 212, 12, 69, 246, 198, 4, 81, 223, 96, 68, 60, 170, + 1, 71, 250, 140, 6, 84, 234, 231, 8, 89, 186, 136, 1, 78, 246, 173, 3, + 83, 82, 66, 2, 67, 2, 68, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, + 2, 82, 3, 87, 6, 254, 166, 18, 89, 230, 201, 1, 72, 187, 2, 65, 32, 108, + 4, 73, 71, 78, 32, 128, 1, 12, 77, 65, 76, 76, 32, 76, 69, 84, 84, 69, + 82, 32, 169, 251, 6, 2, 85, 66, 8, 72, 3, 75, 69, 77, 0, 3, 77, 85, 75, + 32, 2, 83, 65, 155, 166, 17, 76, 2, 205, 170, 17, 3, 80, 72, 82, 2, 183, + 221, 19, 45, 18, 246, 250, 15, 78, 142, 177, 3, 65, 194, 66, 75, 2, 76, + 2, 77, 2, 80, 2, 82, 3, 84, 20, 84, 6, 32, 83, 73, 71, 78, 32, 149, 90, + 10, 45, 67, 65, 82, 82, 73, 69, 82, 32, 76, 18, 154, 212, 5, 65, 130, + 210, 11, 79, 146, 137, 2, 69, 162, 64, 73, 3, 85, 226, 8, 22, 69, 187, + 64, 75, 222, 8, 34, 32, 177, 3, 3, 65, 82, 32, 14, 120, 12, 73, 78, 84, + 69, 71, 82, 65, 84, 73, 79, 78, 32, 202, 162, 13, 70, 206, 153, 3, 83, + 237, 147, 1, 4, 84, 65, 66, 85, 6, 108, 5, 87, 73, 84, 72, 32, 157, 1, + 17, 78, 79, 84, 32, 73, 78, 67, 76, 85, 68, 73, 78, 71, 32, 84, 72, 69, + 4, 76, 7, 82, 69, 67, 84, 65, 78, 71, 1, 8, 83, 69, 77, 73, 67, 73, 82, + 67, 2, 73, 16, 85, 76, 65, 82, 32, 80, 65, 84, 72, 32, 65, 82, 79, 85, + 78, 68, 2, 17, 2, 32, 80, 2, 231, 196, 18, 79, 208, 8, 60, 8, 65, 32, 83, + 73, 71, 78, 32, 65, 205, 24, 2, 66, 32, 170, 5, 122, 49, 86, 51, 202, 2, + 52, 214, 1, 53, 158, 4, 54, 142, 3, 55, 132, 4, 2, 56, 48, 78, 66, 237, + 154, 18, 3, 48, 50, 56, 6, 136, 194, 12, 5, 48, 48, 45, 49, 48, 200, 233, + 5, 2, 50, 48, 233, 19, 2, 51, 49, 150, 1, 78, 48, 90, 49, 130, 1, 55, + 174, 168, 14, 50, 2, 51, 2, 52, 2, 53, 3, 54, 22, 178, 1, 57, 242, 230, + 19, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 24, 90, 51, 242, + 230, 19, 48, 2, 49, 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 6, + 238, 230, 19, 65, 2, 66, 3, 67, 4, 202, 230, 19, 48, 3, 49, 38, 18, 48, + 91, 49, 20, 162, 1, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, + 2, 56, 3, 57, 18, 74, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, + 55, 3, 56, 2, 161, 179, 6, 2, 45, 86, 160, 1, 102, 48, 78, 49, 62, 51, + 86, 52, 70, 53, 86, 54, 190, 11, 50, 194, 171, 2, 57, 226, 235, 11, 55, + 3, 56, 16, 238, 227, 19, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, + 57, 12, 162, 227, 19, 48, 2, 49, 2, 50, 2, 51, 2, 53, 3, 54, 18, 230, + 226, 19, 48, 2, 49, 2, 50, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 14, + 146, 226, 19, 48, 2, 49, 2, 50, 2, 53, 2, 55, 2, 56, 3, 57, 18, 206, 225, + 19, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 57, 12, 250, + 224, 19, 51, 2, 52, 2, 53, 2, 54, 2, 56, 3, 57, 104, 70, 48, 78, 50, 86, + 51, 38, 52, 78, 54, 202, 157, 14, 53, 131, 2, 49, 16, 246, 223, 19, 48, + 2, 49, 2, 50, 2, 51, 2, 52, 2, 54, 2, 56, 3, 57, 18, 170, 223, 19, 48, 2, + 49, 2, 50, 2, 51, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 6, 214, 222, 19, 52, + 2, 55, 3, 56, 16, 178, 222, 19, 48, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, + 56, 3, 57, 10, 230, 221, 19, 48, 2, 49, 2, 50, 2, 51, 3, 52, 44, 86, 48, + 134, 2, 49, 196, 129, 2, 2, 51, 50, 205, 199, 17, 6, 50, 54, 32, 69, 89, + 89, 26, 110, 57, 206, 220, 8, 55, 182, 6, 50, 62, 54, 254, 63, 52, 146, + 155, 3, 51, 114, 56, 186, 211, 2, 49, 155, 122, 53, 10, 26, 45, 131, 208, + 18, 32, 8, 96, 2, 50, 32, 208, 175, 12, 3, 54, 32, 76, 184, 2, 3, 52, 32, + 76, 137, 174, 3, 3, 51, 32, 76, 2, 171, 179, 12, 76, 14, 114, 51, 32, 3, + 52, 32, 65, 0, 2, 53, 32, 134, 65, 49, 174, 223, 3, 50, 250, 195, 4, 48, + 249, 249, 6, 2, 55, 32, 2, 11, 32, 2, 199, 148, 12, 79, 2, 203, 155, 18, + 66, 16, 174, 217, 19, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, + 55, 162, 1, 22, 48, 155, 5, 49, 140, 1, 82, 49, 54, 50, 118, 51, 62, 52, + 78, 53, 86, 54, 62, 55, 70, 56, 207, 147, 14, 48, 10, 238, 215, 19, 48, + 2, 49, 2, 51, 2, 54, 3, 55, 28, 86, 49, 2, 50, 138, 82, 51, 222, 132, 19, + 48, 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 7, 226, 214, 19, 70, 3, 77, 12, + 198, 214, 19, 48, 2, 49, 2, 52, 2, 55, 2, 56, 3, 57, 16, 138, 214, 19, + 48, 2, 49, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 18, 190, 213, 19, + 48, 2, 49, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 12, 234, 212, + 19, 48, 2, 49, 2, 53, 2, 54, 2, 55, 3, 57, 14, 174, 212, 19, 48, 2, 51, + 2, 52, 2, 54, 2, 55, 2, 56, 3, 57, 12, 234, 211, 19, 48, 2, 49, 2, 50, 2, + 53, 2, 54, 3, 55, 22, 82, 50, 36, 2, 51, 49, 30, 56, 238, 166, 12, 49, + 206, 2, 54, 146, 1, 55, 3, 57, 6, 218, 210, 19, 48, 2, 50, 3, 51, 4, 182, + 210, 19, 65, 3, 66, 4, 154, 210, 19, 48, 3, 56, 166, 3, 116, 9, 73, 68, + 69, 79, 71, 82, 65, 77, 32, 220, 17, 10, 77, 79, 78, 79, 71, 82, 65, 77, + 32, 66, 249, 1, 2, 83, 89, 234, 1, 54, 66, 253, 15, 8, 86, 69, 83, 83, + 69, 76, 32, 66, 176, 1, 22, 49, 131, 11, 50, 126, 86, 48, 210, 3, 50, + 166, 1, 51, 86, 52, 122, 53, 114, 54, 146, 1, 55, 118, 56, 71, 57, 28, + 114, 53, 98, 54, 58, 55, 78, 56, 62, 57, 208, 134, 7, 3, 50, 32, 87, 254, + 88, 48, 137, 175, 11, 4, 52, 32, 68, 69, 6, 184, 133, 7, 2, 32, 69, 220, + 166, 11, 3, 70, 32, 77, 133, 82, 7, 77, 32, 83, 84, 65, 76, 76, 4, 168, + 245, 1, 3, 70, 32, 69, 185, 155, 16, 2, 77, 32, 4, 36, 3, 70, 32, 83, 1, + 2, 77, 32, 2, 181, 231, 11, 4, 72, 69, 45, 71, 4, 164, 155, 9, 3, 77, 32, + 66, 173, 149, 9, 3, 70, 32, 83, 4, 148, 194, 17, 4, 77, 32, 66, 85, 129, + 110, 3, 70, 32, 67, 10, 172, 148, 9, 4, 51, 32, 83, 80, 240, 10, 5, 49, + 32, 66, 65, 82, 148, 240, 4, 4, 50, 32, 79, 76, 20, 6, 53, 32, 67, 89, + 80, 69, 129, 179, 4, 4, 48, 32, 87, 72, 6, 54, 49, 216, 234, 17, 3, 48, + 32, 79, 211, 223, 1, 50, 2, 233, 174, 18, 2, 32, 87, 10, 224, 5, 3, 53, + 32, 87, 176, 146, 9, 6, 48, 32, 66, 82, 79, 78, 172, 2, 4, 49, 32, 71, + 79, 206, 175, 10, 50, 3, 54, 16, 82, 57, 134, 238, 2, 49, 190, 218, 16, + 48, 2, 50, 2, 51, 2, 52, 2, 55, 3, 56, 2, 157, 102, 3, 32, 67, 76, 20, + 212, 172, 14, 5, 51, 32, 65, 82, 77, 152, 216, 4, 5, 50, 32, 71, 65, 82, + 182, 67, 48, 2, 49, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 18, 182, + 253, 13, 54, 216, 196, 3, 4, 51, 32, 77, 79, 134, 133, 2, 48, 2, 49, 2, + 50, 2, 52, 2, 55, 2, 56, 3, 57, 14, 158, 198, 19, 48, 2, 49, 2, 50, 2, + 51, 2, 52, 2, 53, 3, 57, 4, 204, 169, 2, 3, 49, 32, 72, 143, 156, 17, 48, + 50, 42, 50, 110, 51, 130, 1, 52, 227, 1, 53, 4, 84, 8, 48, 32, 70, 79, + 79, 84, 83, 84, 169, 211, 6, 7, 53, 32, 66, 65, 84, 72, 84, 2, 155, 175, + 18, 79, 12, 104, 4, 51, 32, 83, 87, 184, 138, 14, 4, 48, 32, 83, 80, 154, + 156, 4, 49, 218, 156, 1, 50, 2, 52, 3, 54, 2, 183, 170, 18, 79, 16, 168, + 1, 9, 48, 32, 87, 72, 69, 69, 76, 69, 68, 2, 49, 34, 51, 232, 175, 17, + 12, 50, 32, 67, 72, 65, 82, 73, 79, 84, 32, 70, 82, 226, 145, 2, 53, 2, + 54, 2, 56, 3, 57, 2, 197, 181, 18, 3, 32, 67, 72, 2, 247, 191, 6, 32, 18, + 148, 186, 18, 3, 52, 32, 68, 158, 135, 1, 49, 2, 50, 2, 51, 2, 53, 2, 54, + 2, 55, 2, 56, 3, 57, 58, 50, 50, 212, 150, 12, 2, 49, 53, 1, 2, 51, 48, + 54, 50, 50, 130, 153, 12, 53, 198, 232, 1, 48, 3, 49, 12, 226, 191, 19, 49, 2, 50, 2, 54, 2, 55, 2, 56, 3, 57, 12, 46, 49, 153, 12, 6, 50, 52, 55, 32, 68, 73, 10, 50, 50, 70, 51, 181, 11, 5, 53, 54, 32, 84, 85, 4, - 208, 174, 3, 3, 55, 32, 75, 189, 220, 15, 5, 56, 32, 75, 65, 78, 4, 56, - 3, 53, 32, 77, 201, 239, 15, 5, 51, 32, 65, 82, 69, 2, 207, 203, 12, 69, + 148, 180, 3, 3, 55, 32, 75, 161, 235, 15, 5, 56, 32, 75, 65, 78, 4, 56, + 3, 53, 32, 77, 157, 128, 16, 5, 51, 32, 65, 82, 69, 2, 247, 209, 12, 69, 176, 1, 84, 9, 76, 76, 65, 66, 76, 69, 32, 66, 48, 229, 12, 7, 77, 66, 79, 76, 32, 66, 48, 148, 1, 114, 48, 142, 1, 49, 162, 1, 50, 230, 1, 51, 174, 1, 52, 162, 1, 53, 154, 1, 54, 186, 1, 55, 198, 1, 56, 87, 57, 18, - 118, 54, 150, 198, 1, 55, 150, 8, 52, 190, 14, 57, 194, 246, 10, 53, 250, - 146, 2, 56, 198, 10, 49, 254, 2, 50, 207, 8, 51, 2, 183, 234, 17, 32, 16, - 122, 54, 18, 55, 190, 207, 1, 49, 134, 2, 50, 166, 26, 52, 248, 186, 1, - 2, 53, 32, 250, 242, 5, 48, 221, 243, 8, 2, 51, 32, 2, 183, 90, 32, 2, - 143, 133, 11, 32, 18, 166, 1, 51, 22, 54, 20, 3, 57, 32, 80, 224, 6, 2, - 53, 32, 220, 174, 3, 2, 48, 32, 196, 205, 14, 2, 55, 32, 140, 7, 2, 52, - 32, 158, 89, 56, 185, 44, 3, 49, 32, 81, 2, 215, 164, 14, 32, 2, 171, - 234, 14, 32, 2, 139, 138, 12, 85, 16, 134, 1, 48, 20, 2, 51, 32, 154, - 195, 1, 55, 254, 34, 54, 158, 241, 4, 57, 234, 163, 2, 56, 250, 168, 4, - 49, 133, 223, 5, 3, 50, 32, 81, 2, 151, 166, 14, 32, 2, 135, 1, 82, 16, - 116, 2, 51, 32, 22, 53, 142, 191, 1, 48, 222, 1, 49, 134, 6, 50, 218, 10, - 52, 178, 5, 54, 241, 216, 12, 3, 56, 32, 78, 2, 247, 170, 15, 65, 2, 223, - 144, 17, 32, 18, 130, 1, 51, 190, 191, 1, 49, 150, 1, 56, 42, 57, 102, - 55, 214, 5, 48, 194, 141, 2, 52, 212, 161, 15, 2, 50, 32, 157, 4, 2, 53, - 32, 2, 135, 194, 12, 32, 16, 132, 1, 2, 50, 32, 20, 2, 56, 32, 204, 1, 3, - 54, 32, 84, 178, 186, 1, 55, 226, 3, 57, 146, 2, 53, 206, 247, 6, 49, - 151, 154, 10, 48, 2, 191, 215, 17, 80, 2, 245, 132, 12, 2, 82, 79, 18, - 172, 1, 3, 54, 32, 82, 210, 186, 1, 55, 158, 8, 48, 170, 16, 53, 12, 3, - 49, 32, 68, 132, 169, 7, 2, 52, 32, 250, 249, 3, 50, 232, 143, 6, 3, 56, - 32, 81, 241, 2, 2, 51, 32, 2, 171, 131, 12, 65, 8, 206, 186, 1, 49, 188, - 24, 3, 55, 32, 84, 192, 249, 6, 2, 53, 32, 183, 146, 6, 48, 4, 154, 228, - 12, 49, 21, 3, 48, 32, 68, 28, 90, 52, 30, 54, 30, 56, 174, 253, 11, 53, - 158, 2, 49, 30, 51, 166, 1, 50, 235, 163, 3, 55, 4, 234, 154, 19, 55, 3, - 57, 4, 206, 154, 19, 51, 3, 52, 8, 178, 154, 19, 50, 2, 51, 2, 54, 3, 57, - 4, 136, 202, 10, 9, 69, 68, 32, 80, 65, 80, 69, 82, 67, 227, 188, 7, 32, - 5, 195, 233, 17, 84, 98, 96, 7, 76, 69, 84, 84, 69, 82, 32, 169, 234, 6, - 11, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 94, 238, 1, 84, 230, 240, - 2, 68, 178, 159, 10, 85, 150, 149, 2, 78, 138, 30, 69, 234, 139, 3, 67, + 118, 54, 182, 203, 1, 55, 130, 8, 52, 190, 14, 57, 222, 247, 10, 53, 158, + 152, 2, 56, 198, 10, 49, 254, 2, 50, 207, 8, 51, 2, 203, 252, 17, 32, 16, + 122, 54, 18, 55, 202, 212, 1, 49, 134, 2, 50, 166, 26, 52, 176, 187, 1, + 2, 53, 32, 210, 242, 5, 48, 229, 128, 9, 2, 51, 32, 2, 171, 95, 32, 2, + 159, 139, 11, 32, 18, 166, 1, 51, 22, 54, 20, 3, 57, 32, 80, 224, 6, 2, + 53, 32, 168, 180, 3, 2, 48, 32, 156, 218, 14, 2, 55, 32, 140, 7, 2, 52, + 32, 162, 91, 56, 185, 44, 3, 49, 32, 81, 2, 171, 176, 14, 32, 2, 247, + 245, 14, 32, 2, 159, 144, 12, 85, 16, 134, 1, 48, 20, 2, 51, 32, 186, + 200, 1, 55, 234, 34, 54, 142, 237, 4, 57, 210, 165, 2, 56, 246, 171, 4, + 49, 205, 237, 5, 3, 50, 32, 81, 2, 235, 177, 14, 32, 2, 135, 1, 82, 16, + 116, 2, 51, 32, 22, 53, 174, 196, 1, 48, 222, 1, 49, 242, 5, 50, 218, 10, + 52, 178, 5, 54, 185, 223, 12, 3, 56, 32, 78, 2, 203, 186, 15, 65, 2, 159, + 162, 17, 32, 18, 130, 1, 51, 222, 196, 1, 49, 150, 1, 56, 42, 57, 102, + 55, 194, 5, 48, 250, 141, 2, 52, 184, 176, 15, 2, 50, 32, 157, 4, 2, 53, + 32, 2, 175, 200, 12, 32, 16, 132, 1, 2, 50, 32, 20, 2, 56, 32, 204, 1, 3, + 54, 32, 84, 210, 191, 1, 55, 226, 3, 57, 146, 2, 53, 250, 243, 6, 49, + 243, 172, 10, 48, 2, 203, 233, 17, 80, 2, 137, 139, 12, 2, 82, 79, 18, + 172, 1, 3, 54, 32, 82, 242, 191, 1, 55, 138, 8, 48, 170, 16, 53, 12, 3, + 49, 32, 68, 220, 166, 7, 2, 52, 32, 222, 252, 3, 50, 200, 158, 6, 3, 56, + 32, 81, 241, 2, 2, 51, 32, 2, 191, 137, 12, 65, 8, 238, 191, 1, 49, 168, + 24, 3, 55, 32, 84, 248, 245, 6, 2, 53, 32, 191, 156, 6, 48, 4, 202, 155, + 17, 49, 21, 3, 48, 32, 68, 28, 90, 52, 30, 54, 30, 56, 194, 131, 12, 53, + 158, 2, 49, 30, 51, 166, 1, 50, 171, 173, 3, 55, 4, 146, 175, 19, 55, 3, + 57, 4, 246, 174, 19, 51, 3, 52, 8, 218, 174, 19, 50, 2, 51, 2, 54, 3, 57, + 4, 152, 208, 10, 9, 69, 68, 32, 80, 65, 80, 69, 82, 67, 223, 200, 7, 32, + 5, 215, 251, 17, 84, 98, 96, 7, 76, 69, 84, 84, 69, 82, 32, 181, 235, 6, + 11, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 94, 238, 1, 84, 150, 246, + 2, 68, 234, 159, 10, 85, 130, 159, 2, 78, 138, 31, 69, 190, 143, 3, 67, 2, 71, 2, 72, 2, 75, 2, 80, 2, 83, 2, 89, 2, 90, 162, 7, 65, 2, 79, 234, 61, 66, 2, 70, 2, 74, 2, 76, 2, 77, 2, 87, 2, 88, 187, 2, 73, 20, 64, 4, - 79, 78, 69, 32, 214, 206, 18, 83, 138, 69, 72, 187, 2, 65, 12, 48, 4, 77, - 89, 65, 32, 129, 192, 1, 2, 78, 65, 10, 130, 183, 12, 74, 234, 191, 6, - 66, 158, 11, 84, 254, 16, 67, 39, 78, 196, 2, 232, 1, 2, 67, 75, 132, 1, - 3, 71, 73, 67, 176, 6, 3, 78, 71, 32, 222, 4, 84, 116, 3, 86, 69, 32, 70, - 87, 172, 12, 5, 90, 69, 78, 71, 69, 134, 231, 7, 66, 148, 138, 10, 8, 85, + 79, 78, 69, 32, 254, 226, 18, 83, 138, 69, 72, 187, 2, 65, 12, 48, 4, 77, + 89, 65, 32, 141, 197, 1, 2, 78, 65, 10, 170, 189, 12, 74, 234, 205, 6, + 66, 158, 11, 84, 254, 16, 67, 39, 78, 210, 2, 232, 1, 2, 67, 75, 132, 1, + 3, 71, 73, 67, 176, 6, 3, 78, 71, 32, 206, 9, 84, 116, 3, 86, 69, 32, 70, + 87, 172, 12, 5, 90, 69, 78, 71, 69, 226, 227, 7, 66, 240, 156, 10, 8, 85, 68, 76, 89, 32, 67, 82, 89, 141, 15, 4, 76, 76, 73, 80, 9, 96, 10, 73, - 78, 71, 45, 83, 72, 73, 70, 84, 32, 153, 20, 9, 32, 87, 73, 84, 72, 32, - 73, 78, 75, 4, 162, 163, 17, 90, 251, 85, 79, 40, 56, 6, 32, 71, 65, 84, + 78, 71, 45, 83, 72, 73, 70, 84, 32, 137, 25, 9, 32, 87, 73, 84, 72, 32, + 73, 78, 75, 4, 226, 180, 17, 90, 223, 86, 79, 40, 56, 6, 32, 71, 65, 84, 69, 32, 137, 2, 3, 65, 76, 32, 12, 104, 6, 66, 85, 70, 70, 69, 82, 84, 9, - 73, 78, 86, 69, 82, 84, 69, 68, 32, 182, 130, 18, 65, 159, 85, 79, 5, + 73, 78, 86, 69, 82, 84, 69, 68, 32, 194, 148, 18, 65, 187, 87, 79, 5, 133, 1, 17, 32, 87, 73, 84, 72, 32, 73, 78, 86, 69, 82, 84, 69, 68, 32, - 73, 78, 4, 48, 3, 79, 85, 84, 129, 154, 6, 3, 73, 78, 80, 2, 167, 150, + 73, 78, 4, 48, 3, 79, 85, 84, 129, 155, 6, 3, 73, 78, 80, 2, 207, 170, 18, 80, 28, 36, 3, 65, 78, 68, 85, 2, 79, 82, 15, 33, 6, 32, 87, 73, 84, - 72, 32, 12, 226, 1, 68, 94, 72, 58, 77, 151, 161, 15, 85, 15, 11, 32, 12, + 72, 32, 12, 226, 1, 68, 94, 72, 58, 77, 199, 177, 15, 85, 15, 11, 32, 12, 88, 13, 79, 86, 69, 82, 76, 65, 80, 80, 73, 78, 71, 32, 76, 49, 5, 87, - 73, 84, 72, 32, 2, 249, 255, 17, 7, 79, 71, 73, 67, 65, 76, 32, 10, 26, - 68, 94, 72, 59, 77, 6, 11, 79, 6, 44, 5, 85, 66, 76, 69, 32, 247, 143, - 18, 84, 4, 234, 161, 15, 85, 235, 60, 79, 2, 177, 207, 15, 9, 79, 82, 73, - 90, 79, 78, 84, 65, 76, 2, 225, 218, 7, 5, 73, 68, 68, 76, 69, 34, 66, - 68, 228, 1, 4, 76, 69, 70, 84, 109, 5, 82, 73, 71, 72, 84, 6, 164, 1, 30, - 65, 83, 72, 32, 70, 82, 79, 77, 32, 76, 69, 70, 84, 32, 77, 69, 77, 66, - 69, 82, 32, 79, 70, 32, 68, 79, 85, 66, 76, 69, 166, 144, 16, 73, 219, - 208, 1, 82, 2, 17, 2, 32, 86, 2, 181, 128, 18, 5, 69, 82, 84, 73, 67, 16, - 18, 32, 119, 87, 6, 48, 6, 82, 73, 71, 72, 84, 32, 139, 238, 15, 84, 4, - 246, 227, 15, 68, 215, 138, 2, 65, 12, 26, 87, 139, 244, 8, 32, 10, 29, - 5, 65, 82, 68, 83, 32, 10, 82, 65, 0, 8, 68, 79, 85, 66, 76, 69, 32, 65, - 221, 227, 7, 4, 83, 81, 85, 73, 4, 25, 4, 82, 82, 79, 87, 5, 137, 162, - 16, 2, 32, 70, 6, 92, 5, 73, 79, 78, 32, 66, 144, 237, 17, 9, 32, 79, 70, - 32, 70, 79, 82, 84, 85, 211, 91, 85, 2, 211, 235, 9, 79, 4, 38, 76, 157, - 249, 13, 3, 72, 79, 84, 2, 133, 206, 17, 2, 69, 84, 222, 1, 34, 32, 229, - 1, 3, 69, 82, 32, 14, 138, 1, 66, 196, 195, 10, 11, 68, 79, 85, 66, 76, - 69, 32, 80, 82, 73, 77, 154, 173, 6, 65, 204, 111, 6, 75, 65, 86, 89, 75, - 65, 211, 10, 76, 4, 38, 82, 249, 248, 4, 3, 65, 84, 84, 2, 209, 240, 17, - 7, 73, 71, 72, 84, 78, 69, 83, 208, 1, 158, 1, 72, 140, 2, 5, 76, 69, 70, - 84, 32, 236, 2, 6, 82, 73, 71, 72, 84, 32, 234, 230, 15, 66, 74, 67, 74, - 70, 234, 10, 77, 150, 2, 79, 166, 18, 83, 67, 84, 20, 84, 4, 65, 76, 70, - 32, 241, 184, 7, 11, 79, 82, 73, 90, 79, 78, 84, 65, 76, 32, 82, 18, 212, - 164, 7, 9, 65, 78, 68, 32, 85, 80, 80, 69, 82, 32, 7, 73, 78, 86, 69, 82, - 83, 69, 194, 201, 8, 72, 230, 1, 86, 170, 26, 77, 130, 3, 76, 22, 82, - 202, 5, 66, 195, 156, 1, 67, 72, 186, 1, 66, 60, 8, 70, 79, 85, 78, 84, - 65, 73, 78, 22, 80, 56, 12, 83, 69, 77, 73, 67, 73, 82, 67, 85, 76, 65, - 82, 154, 1, 81, 246, 2, 84, 248, 191, 11, 3, 67, 82, 65, 139, 177, 4, 79, - 24, 56, 8, 65, 76, 76, 80, 79, 73, 78, 84, 255, 246, 15, 76, 2, 179, 240, - 9, 32, 4, 212, 243, 14, 5, 65, 73, 78, 84, 66, 163, 140, 1, 69, 2, 221, - 193, 7, 5, 32, 65, 78, 84, 73, 74, 110, 81, 166, 2, 83, 82, 84, 238, 176, - 7, 82, 202, 184, 8, 66, 238, 3, 67, 226, 3, 79, 222, 7, 68, 207, 2, 80, - 30, 17, 2, 85, 65, 30, 48, 6, 68, 82, 65, 78, 84, 32, 187, 130, 16, 82, - 28, 74, 70, 60, 2, 78, 69, 50, 83, 226, 253, 15, 67, 226, 1, 77, 143, 1, - 84, 4, 26, 65, 255, 180, 17, 82, 2, 185, 183, 7, 3, 67, 69, 32, 2, 25, 4, - 85, 84, 82, 65, 2, 191, 189, 18, 76, 4, 254, 218, 10, 77, 143, 165, 5, - 84, 6, 148, 190, 7, 11, 69, 77, 73, 67, 73, 82, 67, 85, 76, 65, 82, 231, - 194, 8, 72, 6, 226, 129, 16, 82, 155, 1, 87, 5, 141, 213, 17, 25, 32, 68, - 73, 86, 73, 68, 69, 68, 32, 66, 89, 32, 72, 79, 82, 73, 90, 79, 78, 84, - 65, 76, 32, 82, 85, 6, 18, 71, 23, 78, 2, 215, 241, 13, 71, 4, 188, 213, - 17, 5, 65, 82, 32, 69, 67, 255, 100, 71, 114, 104, 12, 67, 73, 65, 78, - 32, 76, 69, 84, 84, 69, 82, 32, 228, 1, 5, 68, 73, 65, 78, 32, 131, 237, - 17, 73, 58, 206, 1, 77, 194, 137, 5, 75, 206, 203, 9, 66, 50, 84, 234, - 164, 2, 65, 2, 69, 2, 78, 134, 251, 1, 68, 2, 71, 2, 72, 2, 73, 2, 74, 2, - 76, 2, 80, 2, 81, 2, 82, 2, 83, 2, 85, 2, 87, 2, 88, 3, 90, 5, 167, 245, - 18, 77, 54, 88, 7, 76, 69, 84, 84, 69, 82, 32, 189, 213, 14, 9, 84, 82, - 73, 65, 78, 71, 85, 76, 65, 52, 214, 148, 13, 84, 174, 192, 1, 76, 198, - 152, 2, 83, 238, 11, 65, 2, 69, 2, 78, 134, 251, 1, 66, 2, 67, 2, 68, 2, - 70, 2, 71, 2, 73, 2, 75, 2, 77, 2, 79, 2, 81, 2, 82, 2, 85, 2, 86, 3, 89, - 248, 53, 162, 1, 65, 206, 103, 69, 246, 102, 73, 154, 25, 79, 148, 112, - 3, 82, 79, 32, 234, 3, 85, 192, 70, 7, 89, 65, 78, 77, 65, 82, 32, 218, - 145, 15, 86, 198, 61, 77, 27, 87, 204, 23, 186, 1, 71, 62, 72, 254, 10, - 75, 234, 3, 76, 252, 13, 2, 77, 77, 22, 78, 154, 17, 80, 118, 82, 174, 7, - 83, 190, 7, 84, 196, 36, 3, 89, 65, 78, 140, 129, 1, 3, 88, 73, 77, 247, - 230, 15, 67, 6, 128, 226, 17, 4, 73, 67, 32, 87, 238, 88, 78, 155, 53, - 69, 166, 1, 84, 6, 65, 74, 65, 78, 73, 32, 129, 3, 10, 74, 79, 78, 71, - 32, 84, 73, 76, 69, 32, 78, 38, 76, 162, 2, 83, 207, 129, 13, 65, 72, 88, - 6, 69, 84, 84, 69, 82, 32, 161, 144, 12, 10, 73, 71, 65, 84, 85, 82, 69, - 32, 83, 72, 70, 182, 174, 3, 78, 186, 229, 11, 68, 82, 82, 34, 84, 206, - 145, 3, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, - 2, 83, 2, 86, 186, 2, 65, 2, 69, 2, 73, 2, 79, 3, 85, 4, 226, 235, 6, 69, - 229, 236, 7, 5, 73, 71, 78, 32, 78, 88, 236, 1, 2, 66, 65, 38, 69, 46, - 70, 46, 78, 42, 79, 46, 80, 22, 83, 118, 84, 194, 1, 87, 112, 5, 71, 82, - 69, 69, 78, 0, 3, 82, 69, 68, 208, 135, 6, 3, 65, 85, 84, 214, 21, 74, - 197, 158, 11, 11, 67, 72, 82, 89, 83, 65, 78, 84, 72, 69, 77, 4, 138, - 158, 1, 77, 223, 200, 17, 67, 8, 228, 2, 4, 73, 71, 72, 84, 195, 1, 65, - 12, 172, 2, 2, 73, 86, 13, 3, 79, 85, 82, 8, 192, 1, 2, 79, 82, 65, 2, - 73, 78, 8, 218, 1, 78, 189, 223, 11, 3, 82, 67, 72, 2, 159, 191, 17, 76, - 18, 88, 2, 79, 85, 76, 4, 69, 86, 69, 78, 0, 2, 73, 88, 198, 151, 12, 85, - 187, 194, 1, 80, 2, 157, 2, 2, 84, 72, 12, 36, 3, 72, 82, 69, 13, 2, 87, - 79, 6, 11, 69, 6, 25, 4, 32, 79, 70, 32, 6, 46, 67, 229, 246, 6, 5, 66, - 65, 77, 66, 79, 4, 224, 139, 6, 2, 73, 82, 217, 217, 10, 5, 72, 65, 82, - 65, 67, 6, 50, 69, 60, 4, 72, 73, 84, 69, 247, 172, 17, 73, 2, 17, 2, 83, - 84, 2, 17, 2, 32, 87, 2, 147, 215, 17, 73, 2, 17, 2, 32, 68, 2, 183, 172, - 17, 82, 52, 52, 5, 65, 83, 65, 82, 32, 233, 251, 14, 2, 69, 77, 50, 162, - 1, 69, 40, 7, 76, 69, 84, 84, 69, 82, 32, 152, 1, 5, 80, 65, 83, 83, 73, - 32, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 173, 216, 18, 3, 65, - 78, 71, 2, 161, 228, 15, 5, 78, 68, 32, 79, 70, 36, 158, 232, 16, 78, - 246, 247, 1, 66, 2, 67, 2, 68, 2, 71, 2, 74, 2, 75, 2, 76, 2, 77, 2, 80, - 2, 82, 2, 83, 2, 84, 2, 86, 2, 89, 187, 2, 65, 2, 11, 77, 2, 211, 136, - 18, 66, 8, 146, 225, 18, 69, 2, 73, 2, 79, 3, 85, 246, 1, 80, 7, 65, 89, - 65, 76, 65, 77, 32, 176, 11, 2, 69, 32, 225, 1, 3, 84, 69, 83, 236, 1, - 198, 1, 68, 44, 9, 70, 82, 65, 67, 84, 73, 79, 78, 32, 240, 1, 7, 76, 69, - 84, 84, 69, 82, 32, 164, 5, 7, 78, 85, 77, 66, 69, 82, 32, 36, 5, 83, 73, - 71, 78, 32, 178, 180, 13, 86, 247, 196, 1, 65, 22, 204, 169, 8, 2, 65, - 84, 227, 195, 8, 73, 26, 56, 4, 79, 78, 69, 32, 121, 6, 84, 72, 82, 69, - 69, 32, 18, 82, 84, 146, 135, 5, 83, 230, 249, 7, 70, 62, 79, 130, 216, - 3, 69, 46, 72, 47, 81, 4, 254, 131, 13, 87, 239, 214, 3, 69, 8, 170, 135, - 5, 83, 170, 253, 7, 69, 110, 84, 163, 214, 3, 81, 132, 1, 226, 1, 65, 82, - 67, 158, 1, 68, 78, 84, 82, 86, 218, 130, 13, 78, 146, 251, 1, 76, 38, - 82, 134, 6, 85, 202, 141, 1, 79, 134, 60, 73, 206, 193, 1, 83, 82, 66, 2, - 71, 2, 74, 2, 75, 2, 80, 162, 7, 69, 234, 61, 72, 2, 77, 3, 89, 11, 192, - 151, 16, 7, 82, 67, 72, 65, 73, 67, 32, 254, 194, 2, 65, 2, 73, 3, 85, - 22, 26, 72, 215, 217, 18, 65, 20, 44, 5, 73, 76, 76, 85, 32, 167, 217, - 18, 65, 18, 130, 249, 12, 76, 158, 229, 3, 78, 170, 194, 1, 82, 222, 56, - 75, 2, 77, 3, 89, 10, 220, 220, 10, 4, 79, 84, 32, 82, 182, 180, 7, 68, - 138, 69, 72, 187, 2, 65, 10, 38, 84, 170, 213, 18, 72, 187, 2, 65, 6, - 166, 213, 18, 72, 2, 84, 187, 2, 65, 12, 166, 227, 3, 69, 138, 161, 11, - 79, 139, 211, 3, 65, 6, 154, 131, 13, 79, 131, 128, 4, 84, 18, 50, 67, - 114, 86, 206, 254, 14, 65, 167, 146, 3, 80, 6, 54, 79, 120, 5, 73, 82, - 67, 85, 76, 203, 192, 14, 65, 2, 221, 178, 13, 9, 77, 66, 73, 78, 73, 78, - 71, 32, 65, 6, 60, 9, 69, 82, 84, 73, 67, 65, 76, 32, 66, 255, 145, 18, - 73, 2, 209, 254, 14, 2, 65, 82, 8, 80, 12, 87, 73, 84, 72, 32, 83, 84, - 82, 79, 75, 69, 32, 70, 65, 227, 204, 17, 83, 4, 64, 10, 65, 78, 68, 32, - 77, 65, 76, 69, 32, 65, 227, 204, 17, 83, 2, 25, 4, 78, 68, 32, 70, 2, - 11, 69, 2, 11, 77, 2, 231, 209, 7, 65, 2, 211, 145, 17, 69, 2, 219, 208, - 16, 79, 185, 1, 210, 1, 32, 220, 2, 5, 68, 65, 73, 67, 32, 224, 3, 8, 73, - 67, 72, 65, 69, 65, 78, 32, 222, 8, 83, 216, 165, 2, 3, 85, 65, 76, 190, - 217, 10, 65, 152, 168, 1, 8, 84, 69, 76, 80, 73, 69, 67, 69, 203, 251, 3, - 71, 12, 132, 1, 3, 73, 78, 32, 128, 1, 5, 87, 73, 84, 72, 32, 204, 146, - 2, 3, 68, 65, 78, 253, 252, 12, 8, 65, 78, 68, 32, 87, 79, 77, 65, 4, - 144, 135, 10, 19, 66, 85, 83, 73, 78, 69, 83, 83, 32, 83, 85, 73, 84, 32, - 76, 69, 86, 73, 84, 185, 151, 1, 4, 84, 85, 88, 69, 4, 224, 231, 13, 8, - 71, 85, 65, 32, 80, 73, 32, 77, 149, 157, 3, 4, 84, 85, 82, 66, 58, 112, - 4, 65, 70, 70, 82, 28, 7, 76, 69, 84, 84, 69, 82, 32, 244, 173, 14, 3, - 86, 79, 67, 146, 67, 71, 251, 25, 80, 2, 249, 227, 17, 2, 73, 67, 50, 82, - 65, 176, 1, 2, 68, 85, 2, 85, 28, 3, 72, 65, 76, 22, 73, 183, 132, 18, - 75, 38, 154, 1, 75, 198, 235, 12, 84, 146, 192, 1, 83, 242, 207, 3, 73, - 226, 79, 66, 2, 68, 2, 71, 2, 72, 2, 76, 2, 77, 2, 78, 2, 80, 2, 81, 2, - 82, 3, 90, 5, 235, 200, 18, 83, 2, 153, 131, 8, 2, 83, 72, 2, 187, 200, - 18, 81, 4, 222, 202, 18, 78, 3, 84, 102, 216, 1, 7, 76, 69, 84, 84, 69, - 82, 32, 194, 4, 78, 80, 12, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, - 32, 220, 1, 4, 83, 73, 71, 78, 129, 195, 10, 16, 65, 66, 66, 82, 69, 86, - 73, 65, 84, 73, 79, 78, 32, 77, 65, 82, 72, 218, 1, 65, 46, 66, 38, 68, - 30, 71, 30, 74, 2, 90, 30, 75, 30, 81, 38, 83, 50, 84, 54, 88, 234, 162, - 6, 76, 158, 206, 1, 82, 154, 217, 2, 89, 158, 208, 1, 72, 222, 169, 5, - 78, 134, 2, 87, 218, 103, 70, 2, 80, 171, 4, 77, 6, 150, 203, 10, 76, - 122, 65, 171, 251, 6, 89, 4, 254, 237, 12, 72, 211, 214, 3, 69, 4, 202, - 164, 6, 65, 23, 72, 4, 190, 202, 10, 72, 15, 73, 4, 250, 202, 10, 72, 15, - 65, 4, 194, 161, 12, 72, 15, 65, 4, 158, 164, 6, 72, 151, 253, 5, 79, 8, - 194, 199, 10, 83, 154, 3, 65, 251, 250, 6, 72, 6, 154, 163, 6, 72, 206, - 159, 10, 69, 243, 131, 1, 65, 4, 170, 160, 12, 65, 3, 79, 10, 33, 6, 85, - 77, 66, 69, 82, 32, 10, 130, 202, 10, 79, 58, 84, 135, 198, 2, 70, 14, - 80, 2, 68, 79, 116, 3, 76, 73, 78, 134, 167, 5, 70, 186, 202, 11, 84, - 167, 10, 83, 6, 54, 84, 13, 9, 85, 66, 76, 69, 32, 68, 79, 84, 32, 5, 11, - 32, 2, 25, 4, 87, 73, 84, 72, 2, 199, 192, 2, 73, 2, 155, 203, 3, 69, 2, - 159, 150, 17, 32, 2, 11, 32, 2, 249, 170, 18, 2, 83, 72, 4, 92, 17, 32, - 83, 89, 77, 66, 79, 76, 32, 70, 79, 82, 32, 76, 73, 71, 72, 84, 147, 214, - 10, 76, 2, 135, 252, 14, 72, 142, 1, 142, 1, 65, 20, 5, 67, 72, 69, 78, - 32, 144, 163, 6, 4, 82, 73, 65, 71, 193, 157, 11, 13, 84, 73, 65, 76, 32, - 65, 82, 84, 83, 32, 85, 78, 73, 2, 171, 161, 5, 67, 136, 1, 152, 1, 7, - 76, 69, 84, 84, 69, 82, 32, 194, 1, 83, 236, 2, 11, 86, 79, 87, 69, 76, - 32, 83, 73, 71, 78, 32, 170, 237, 17, 72, 225, 5, 4, 77, 65, 82, 75, 60, - 254, 3, 84, 146, 148, 2, 68, 202, 171, 14, 78, 238, 178, 1, 67, 2, 75, 2, - 80, 2, 83, 2, 90, 138, 69, 45, 2, 66, 2, 71, 2, 72, 2, 74, 2, 76, 2, 77, - 2, 82, 2, 87, 2, 89, 187, 2, 65, 62, 96, 4, 73, 71, 78, 32, 37, 16, 85, - 66, 74, 79, 73, 78, 69, 68, 32, 76, 69, 84, 84, 69, 82, 32, 4, 158, 167, - 14, 67, 203, 207, 3, 65, 58, 182, 1, 84, 146, 148, 2, 68, 202, 171, 14, - 78, 238, 178, 1, 67, 2, 75, 2, 80, 2, 83, 2, 90, 138, 69, 66, 2, 71, 2, - 72, 2, 74, 2, 76, 2, 77, 2, 82, 2, 87, 2, 89, 187, 2, 65, 8, 194, 242, - 17, 83, 138, 69, 72, 187, 2, 65, 10, 158, 183, 18, 65, 186, 2, 69, 2, 73, - 2, 79, 3, 85, 156, 1, 120, 11, 65, 82, 65, 77, 32, 71, 79, 78, 68, 73, - 32, 232, 5, 3, 67, 85, 76, 64, 5, 75, 32, 87, 79, 82, 183, 240, 17, 85, - 150, 1, 100, 7, 76, 69, 84, 84, 69, 82, 32, 178, 2, 82, 56, 5, 83, 73, - 71, 78, 32, 82, 86, 223, 194, 16, 68, 94, 210, 1, 74, 42, 84, 154, 219, - 14, 65, 38, 68, 214, 6, 85, 206, 201, 1, 73, 42, 76, 214, 192, 1, 75, 38, - 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 80, 138, 69, 72, 2, 77, 2, 82, 2, - 86, 2, 89, 186, 2, 69, 3, 79, 6, 130, 179, 18, 78, 38, 72, 187, 2, 65, - 10, 246, 237, 17, 84, 138, 69, 72, 2, 82, 187, 2, 65, 4, 32, 2, 65, 45, - 175, 220, 14, 69, 2, 147, 240, 17, 75, 10, 178, 176, 1, 67, 198, 196, 12, - 72, 246, 43, 78, 186, 221, 1, 86, 179, 241, 1, 65, 22, 26, 79, 139, 251, - 15, 73, 20, 45, 9, 87, 69, 76, 32, 83, 73, 71, 78, 32, 20, 74, 86, 154, - 223, 14, 65, 38, 85, 206, 201, 1, 73, 222, 137, 2, 69, 3, 79, 2, 233, - 201, 7, 6, 79, 67, 65, 76, 73, 67, 2, 145, 249, 17, 11, 73, 78, 69, 32, - 79, 82, 68, 73, 78, 65, 76, 2, 207, 158, 17, 75, 226, 15, 76, 10, 72, 69, - 77, 65, 84, 73, 67, 65, 76, 32, 237, 207, 14, 3, 69, 32, 68, 224, 15, - 252, 1, 5, 66, 79, 76, 68, 32, 168, 6, 14, 68, 79, 85, 66, 76, 69, 45, - 83, 84, 82, 85, 67, 75, 32, 238, 1, 70, 164, 2, 7, 73, 84, 65, 76, 73, - 67, 32, 180, 3, 10, 77, 79, 78, 79, 83, 80, 65, 67, 69, 32, 40, 2, 82, - 73, 36, 3, 76, 69, 70, 167, 1, 83, 160, 5, 176, 1, 8, 67, 65, 80, 73, 84, - 65, 76, 32, 130, 2, 83, 210, 14, 73, 222, 3, 69, 38, 75, 38, 78, 30, 80, - 98, 82, 242, 5, 84, 56, 7, 70, 82, 65, 75, 84, 85, 82, 159, 160, 16, 68, - 104, 190, 4, 68, 174, 15, 84, 186, 4, 65, 22, 66, 2, 90, 42, 69, 98, 71, - 22, 73, 22, 75, 34, 76, 22, 79, 50, 80, 42, 82, 22, 83, 70, 85, 142, 197, - 1, 67, 214, 249, 12, 77, 2, 78, 206, 201, 1, 88, 222, 137, 2, 70, 2, 72, - 2, 74, 2, 81, 2, 86, 2, 87, 3, 89, 208, 1, 60, 5, 77, 65, 76, 76, 32, - 213, 25, 5, 67, 82, 73, 80, 84, 104, 250, 1, 68, 230, 19, 65, 22, 66, 2, - 90, 42, 69, 38, 70, 62, 71, 22, 73, 22, 75, 34, 76, 22, 79, 50, 80, 42, - 82, 22, 83, 34, 84, 38, 85, 142, 197, 1, 67, 214, 249, 12, 77, 2, 78, - 206, 201, 1, 88, 222, 137, 2, 72, 2, 74, 2, 81, 2, 86, 2, 87, 3, 89, 7, - 26, 73, 235, 239, 14, 69, 2, 187, 213, 1, 71, 110, 68, 8, 67, 65, 80, 73, - 84, 65, 76, 32, 162, 23, 83, 223, 159, 16, 68, 38, 154, 168, 18, 65, 2, - 66, 2, 68, 2, 69, 2, 70, 2, 71, 2, 73, 2, 74, 2, 75, 2, 76, 2, 77, 2, 79, - 2, 83, 2, 84, 2, 85, 2, 86, 2, 87, 2, 88, 3, 89, 96, 52, 7, 82, 65, 75, - 84, 85, 82, 32, 219, 142, 7, 65, 94, 52, 8, 67, 65, 80, 73, 84, 65, 76, - 32, 143, 21, 83, 42, 134, 166, 18, 65, 2, 66, 2, 68, 2, 69, 2, 70, 2, 71, - 2, 74, 2, 75, 2, 76, 2, 77, 2, 78, 2, 79, 2, 80, 2, 81, 2, 83, 2, 84, 2, - 85, 2, 86, 2, 87, 2, 88, 3, 89, 222, 1, 100, 6, 83, 77, 65, 76, 76, 32, - 222, 7, 67, 230, 2, 69, 38, 75, 38, 78, 30, 80, 98, 82, 243, 5, 84, 104, - 242, 1, 68, 198, 12, 65, 22, 66, 2, 90, 42, 69, 38, 70, 62, 71, 22, 73, - 22, 75, 34, 76, 22, 79, 50, 80, 42, 82, 22, 83, 34, 84, 38, 85, 142, 197, - 1, 67, 214, 249, 12, 77, 2, 78, 206, 201, 1, 88, 222, 137, 2, 74, 2, 81, - 2, 86, 2, 87, 3, 89, 9, 52, 7, 79, 84, 76, 69, 83, 83, 32, 175, 232, 14, - 69, 4, 186, 161, 18, 73, 3, 74, 124, 130, 16, 67, 34, 83, 223, 159, 16, - 68, 12, 32, 2, 71, 72, 131, 137, 7, 83, 10, 17, 2, 84, 32, 10, 112, 6, - 87, 72, 73, 84, 69, 32, 170, 244, 5, 68, 234, 106, 65, 241, 230, 4, 9, - 70, 76, 65, 84, 84, 69, 78, 69, 68, 4, 130, 165, 14, 83, 39, 84, 130, 6, - 84, 10, 65, 78, 83, 45, 83, 69, 82, 73, 70, 32, 133, 14, 6, 67, 82, 73, - 80, 84, 32, 176, 5, 96, 5, 66, 79, 76, 68, 32, 176, 12, 6, 73, 84, 65, - 76, 73, 67, 34, 67, 34, 83, 223, 159, 16, 68, 204, 3, 98, 73, 122, 67, - 230, 2, 69, 38, 75, 38, 78, 30, 80, 98, 82, 30, 83, 214, 5, 84, 215, 160, - 16, 68, 220, 1, 33, 6, 84, 65, 76, 73, 67, 32, 220, 1, 74, 67, 230, 2, - 69, 38, 75, 38, 78, 30, 80, 98, 82, 30, 83, 215, 5, 84, 102, 37, 7, 65, - 80, 73, 84, 65, 76, 32, 102, 250, 1, 84, 186, 4, 65, 22, 66, 2, 90, 22, - 68, 22, 69, 98, 71, 22, 73, 22, 75, 34, 76, 22, 79, 50, 80, 42, 82, 22, - 83, 70, 85, 142, 197, 1, 67, 214, 249, 12, 77, 2, 78, 206, 201, 1, 88, - 222, 137, 2, 70, 2, 72, 2, 74, 2, 81, 2, 86, 2, 87, 3, 89, 9, 40, 4, 72, - 69, 84, 65, 215, 244, 17, 65, 5, 159, 134, 17, 32, 2, 213, 161, 16, 4, - 80, 83, 73, 76, 2, 17, 2, 65, 80, 2, 159, 7, 80, 2, 197, 246, 17, 2, 65, - 66, 6, 74, 72, 172, 149, 5, 8, 65, 82, 84, 73, 65, 76, 32, 68, 167, 239, - 11, 73, 2, 207, 132, 17, 73, 2, 185, 132, 17, 2, 72, 79, 102, 29, 5, 77, - 65, 76, 76, 32, 102, 246, 1, 65, 22, 66, 2, 90, 22, 68, 22, 69, 38, 70, - 62, 71, 22, 73, 22, 75, 34, 76, 22, 79, 50, 80, 42, 82, 22, 83, 34, 84, - 38, 85, 142, 197, 1, 67, 214, 249, 12, 77, 2, 78, 206, 201, 1, 88, 222, - 137, 2, 72, 2, 74, 2, 81, 2, 86, 2, 87, 3, 89, 5, 251, 188, 14, 76, 5, - 207, 240, 17, 69, 5, 247, 219, 14, 69, 7, 186, 212, 1, 80, 131, 190, 16, - 84, 5, 11, 73, 2, 29, 5, 78, 65, 76, 32, 83, 2, 227, 1, 73, 5, 155, 194, - 15, 65, 5, 179, 239, 17, 79, 5, 11, 65, 2, 139, 218, 14, 80, 5, 187, 219, - 14, 65, 7, 11, 77, 4, 246, 170, 1, 73, 243, 165, 16, 69, 9, 174, 255, 17, - 72, 2, 83, 219, 19, 73, 5, 235, 243, 17, 72, 5, 11, 73, 2, 175, 241, 17, - 71, 7, 234, 216, 14, 72, 219, 148, 3, 65, 5, 207, 209, 1, 80, 2, 11, 72, - 2, 11, 69, 2, 11, 84, 2, 167, 254, 16, 65, 104, 11, 32, 104, 18, 67, 35, - 83, 52, 53, 5, 65, 80, 73, 84, 65, 52, 21, 3, 77, 65, 76, 52, 147, 254, - 11, 76, 82, 76, 8, 67, 65, 80, 73, 84, 65, 76, 32, 157, 1, 6, 83, 77, 65, - 76, 76, 32, 36, 254, 143, 18, 65, 2, 67, 2, 68, 2, 71, 2, 74, 2, 75, 2, - 78, 2, 79, 2, 80, 2, 81, 2, 83, 2, 84, 2, 85, 2, 86, 2, 87, 2, 88, 2, 89, - 3, 90, 46, 226, 142, 18, 65, 2, 66, 2, 67, 2, 68, 2, 70, 2, 72, 2, 73, 2, - 74, 2, 75, 2, 76, 2, 77, 2, 78, 2, 80, 2, 81, 2, 82, 2, 83, 2, 84, 2, 85, - 2, 86, 2, 87, 2, 88, 2, 89, 3, 90, 40, 45, 9, 32, 78, 85, 77, 69, 82, 65, - 76, 32, 40, 70, 69, 66, 70, 70, 78, 26, 83, 66, 84, 170, 155, 16, 90, - 251, 85, 79, 6, 40, 4, 73, 71, 72, 84, 215, 247, 9, 76, 5, 147, 184, 16, - 69, 8, 30, 73, 105, 3, 79, 85, 82, 4, 206, 134, 5, 70, 167, 238, 12, 86, - 4, 65, 3, 73, 78, 69, 8, 40, 4, 69, 86, 69, 78, 1, 2, 73, 88, 5, 235, - 182, 16, 84, 10, 42, 72, 162, 247, 9, 87, 163, 195, 7, 69, 4, 154, 133, - 5, 73, 207, 149, 11, 82, 252, 8, 234, 1, 65, 192, 4, 9, 67, 72, 65, 78, - 73, 67, 65, 76, 32, 34, 68, 164, 15, 11, 69, 84, 69, 73, 32, 77, 65, 89, - 69, 75, 32, 178, 11, 76, 34, 78, 154, 50, 82, 176, 15, 8, 83, 83, 65, 71, - 69, 32, 87, 65, 20, 2, 84, 82, 235, 135, 17, 77, 26, 72, 6, 83, 85, 82, - 69, 68, 32, 253, 220, 16, 6, 84, 32, 79, 78, 32, 66, 24, 96, 5, 65, 78, - 71, 76, 69, 148, 142, 10, 9, 82, 73, 71, 72, 84, 32, 65, 78, 71, 131, - 231, 7, 66, 21, 11, 32, 18, 212, 1, 39, 87, 73, 84, 72, 32, 79, 80, 69, - 78, 32, 65, 82, 77, 32, 69, 78, 68, 73, 78, 71, 32, 73, 78, 32, 65, 82, - 82, 79, 87, 32, 80, 79, 73, 78, 84, 73, 78, 71, 32, 181, 210, 17, 7, 79, - 80, 69, 78, 73, 78, 71, 16, 62, 68, 24, 3, 76, 69, 70, 0, 4, 82, 73, 71, - 72, 43, 85, 4, 73, 3, 79, 87, 78, 4, 137, 144, 8, 5, 84, 32, 65, 78, 68, - 4, 11, 80, 4, 225, 227, 11, 3, 32, 65, 78, 4, 142, 133, 17, 65, 215, 56, - 76, 250, 1, 56, 9, 69, 70, 65, 73, 68, 82, 73, 78, 32, 159, 7, 73, 190, - 1, 174, 1, 67, 52, 6, 68, 73, 71, 73, 84, 32, 152, 1, 7, 78, 85, 77, 66, - 69, 82, 32, 114, 83, 164, 146, 7, 12, 69, 88, 67, 76, 65, 77, 65, 84, 73, - 79, 78, 32, 247, 154, 8, 70, 70, 128, 3, 5, 65, 80, 73, 84, 65, 191, 172, - 15, 79, 26, 82, 84, 48, 2, 79, 78, 222, 143, 16, 70, 30, 83, 102, 90, - 238, 85, 78, 235, 110, 69, 8, 44, 3, 72, 82, 69, 217, 128, 17, 2, 87, 79, - 4, 215, 128, 17, 69, 20, 50, 84, 230, 248, 4, 69, 46, 70, 42, 78, 31, 83, - 6, 166, 250, 4, 72, 172, 242, 4, 2, 87, 69, 135, 195, 7, 69, 70, 68, 3, - 77, 65, 76, 157, 206, 9, 8, 89, 77, 66, 79, 76, 32, 65, 73, 68, 45, 9, - 76, 32, 76, 69, 84, 84, 69, 82, 32, 68, 242, 1, 65, 38, 78, 130, 227, 3, - 72, 2, 75, 198, 197, 10, 89, 138, 147, 3, 79, 162, 64, 66, 2, 67, 2, 68, - 2, 69, 2, 70, 2, 71, 2, 73, 2, 74, 2, 76, 2, 77, 2, 80, 2, 81, 2, 82, 2, - 83, 2, 84, 2, 85, 2, 86, 2, 87, 2, 88, 3, 90, 7, 146, 243, 3, 84, 255, - 136, 14, 73, 7, 234, 251, 17, 71, 3, 89, 60, 48, 5, 69, 86, 65, 76, 32, - 45, 3, 85, 77, 32, 6, 210, 237, 10, 69, 198, 187, 4, 67, 115, 81, 54, - 210, 1, 66, 50, 70, 164, 1, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 248, 1, - 11, 77, 65, 84, 72, 69, 77, 65, 84, 73, 67, 65, 22, 83, 84, 4, 84, 72, - 82, 69, 182, 158, 15, 86, 130, 63, 69, 166, 4, 87, 215, 10, 71, 4, 180, - 156, 6, 3, 79, 76, 68, 255, 225, 7, 76, 10, 84, 9, 76, 65, 84, 84, 69, - 78, 69, 68, 32, 184, 3, 3, 79, 85, 82, 243, 222, 15, 73, 4, 44, 3, 76, - 69, 70, 1, 4, 82, 73, 71, 72, 2, 213, 1, 3, 84, 32, 80, 6, 11, 84, 6, 78, - 32, 41, 15, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 65, 78, 71, 76, 69, - 4, 36, 5, 67, 85, 82, 76, 89, 59, 80, 2, 17, 2, 32, 66, 2, 137, 183, 7, - 4, 82, 65, 67, 75, 2, 169, 178, 17, 10, 65, 82, 69, 78, 84, 72, 69, 83, - 73, 83, 2, 139, 183, 17, 76, 10, 212, 152, 6, 4, 77, 65, 76, 76, 250, - 229, 8, 72, 212, 95, 2, 73, 88, 183, 2, 65, 4, 11, 69, 4, 45, 9, 32, 80, - 79, 73, 78, 84, 69, 68, 32, 4, 162, 202, 9, 80, 219, 147, 6, 66, 158, 1, - 146, 1, 65, 104, 6, 67, 72, 69, 73, 75, 72, 34, 76, 144, 6, 8, 83, 89, - 76, 76, 65, 66, 76, 69, 0, 4, 87, 79, 82, 68, 50, 86, 139, 250, 15, 68, - 6, 80, 8, 72, 65, 78, 71, 32, 75, 72, 85, 164, 6, 3, 80, 85, 78, 147, - 213, 13, 78, 2, 159, 183, 16, 68, 4, 166, 162, 17, 65, 139, 60, 69, 94, + 73, 84, 72, 32, 2, 133, 146, 18, 7, 79, 71, 73, 67, 65, 76, 32, 10, 26, + 68, 94, 72, 59, 77, 6, 11, 79, 6, 44, 5, 85, 66, 76, 69, 32, 159, 164, + 18, 84, 4, 154, 178, 15, 85, 143, 61, 79, 2, 133, 224, 15, 9, 79, 82, 73, + 90, 79, 78, 84, 65, 76, 2, 225, 220, 7, 5, 73, 68, 68, 76, 69, 48, 70, + 68, 228, 1, 4, 76, 69, 70, 84, 241, 2, 5, 82, 73, 71, 72, 84, 6, 164, 1, + 30, 65, 83, 72, 32, 70, 82, 79, 77, 32, 76, 69, 70, 84, 32, 77, 69, 77, + 66, 69, 82, 32, 79, 70, 32, 68, 79, 85, 66, 76, 69, 250, 160, 16, 73, + 151, 210, 1, 82, 2, 17, 2, 32, 86, 2, 189, 146, 18, 5, 69, 82, 84, 73, + 67, 20, 46, 32, 193, 1, 6, 87, 65, 82, 68, 83, 32, 8, 48, 6, 82, 73, 71, + 72, 84, 32, 191, 254, 15, 84, 6, 44, 5, 65, 82, 82, 79, 87, 255, 243, 15, + 68, 5, 245, 243, 13, 18, 32, 87, 73, 84, 72, 32, 68, 69, 80, 69, 78, 68, + 69, 78, 84, 32, 76, 79, 12, 214, 3, 68, 42, 65, 146, 1, 83, 153, 240, 8, + 19, 72, 65, 82, 80, 79, 79, 78, 32, 65, 66, 79, 86, 69, 32, 83, 72, 79, + 82, 84, 22, 48, 6, 87, 65, 82, 68, 83, 32, 159, 247, 8, 32, 20, 88, 5, + 65, 82, 82, 79, 87, 202, 1, 68, 96, 8, 72, 65, 82, 80, 79, 79, 78, 32, + 91, 83, 11, 11, 32, 8, 164, 1, 7, 84, 72, 82, 79, 85, 71, 72, 132, 143, + 3, 10, 87, 73, 84, 72, 32, 68, 79, 85, 66, 76, 248, 227, 5, 9, 79, 86, + 69, 82, 32, 76, 79, 78, 71, 203, 188, 7, 70, 2, 255, 137, 18, 32, 4, 37, + 7, 79, 85, 66, 76, 69, 32, 65, 4, 25, 4, 82, 82, 79, 87, 5, 233, 174, 16, + 2, 32, 70, 4, 160, 242, 8, 4, 79, 86, 69, 82, 33, 11, 65, 66, 79, 86, 69, + 32, 83, 72, 79, 82, 84, 2, 157, 224, 7, 3, 81, 85, 73, 6, 92, 5, 73, 79, + 78, 32, 66, 196, 250, 17, 9, 32, 79, 70, 32, 70, 79, 82, 84, 85, 215, 93, + 85, 2, 135, 237, 9, 79, 4, 38, 76, 137, 128, 14, 3, 72, 79, 84, 2, 169, + 219, 17, 2, 69, 84, 222, 1, 34, 32, 229, 1, 3, 69, 82, 32, 14, 138, 1, + 66, 228, 196, 10, 11, 68, 79, 85, 66, 76, 69, 32, 80, 82, 73, 77, 214, + 183, 6, 65, 148, 113, 6, 75, 65, 86, 89, 75, 65, 227, 10, 76, 4, 38, 82, + 133, 245, 4, 3, 65, 84, 84, 2, 237, 253, 17, 7, 73, 71, 72, 84, 78, 69, + 83, 208, 1, 158, 1, 72, 140, 2, 5, 76, 69, 70, 84, 32, 236, 2, 6, 82, 73, + 71, 72, 84, 32, 206, 242, 15, 66, 74, 67, 74, 70, 234, 10, 77, 150, 2, + 79, 170, 18, 83, 67, 84, 20, 84, 4, 65, 76, 70, 32, 129, 182, 7, 11, 79, + 82, 73, 90, 79, 78, 84, 65, 76, 32, 82, 18, 228, 161, 7, 9, 65, 78, 68, + 32, 85, 80, 80, 69, 82, 32, 7, 73, 78, 86, 69, 82, 83, 69, 150, 216, 8, + 72, 230, 1, 86, 174, 26, 77, 130, 3, 76, 22, 82, 202, 5, 66, 247, 157, 1, + 67, 72, 186, 1, 66, 60, 8, 70, 79, 85, 78, 84, 65, 73, 78, 22, 80, 56, + 12, 83, 69, 77, 73, 67, 73, 82, 67, 85, 76, 65, 82, 154, 1, 81, 246, 2, + 84, 156, 193, 11, 3, 67, 82, 65, 203, 187, 4, 79, 24, 56, 8, 65, 76, 76, + 80, 79, 73, 78, 84, 227, 130, 16, 76, 2, 231, 241, 9, 32, 4, 184, 254, + 14, 5, 65, 73, 78, 84, 66, 163, 141, 1, 69, 2, 237, 190, 7, 5, 32, 65, + 78, 84, 73, 74, 110, 81, 166, 2, 83, 82, 84, 254, 173, 7, 82, 158, 199, + 8, 66, 238, 3, 67, 226, 3, 79, 222, 7, 68, 207, 2, 80, 30, 17, 2, 85, 65, + 30, 48, 6, 68, 82, 65, 78, 84, 32, 163, 142, 16, 82, 28, 74, 70, 60, 2, + 78, 69, 50, 83, 202, 137, 16, 67, 226, 1, 77, 143, 1, 84, 4, 26, 65, 155, + 194, 17, 82, 2, 201, 180, 7, 3, 67, 69, 32, 2, 25, 4, 85, 84, 82, 65, 2, + 247, 204, 18, 76, 4, 158, 220, 10, 77, 215, 175, 5, 84, 6, 164, 187, 7, + 11, 69, 77, 73, 67, 73, 82, 67, 85, 76, 65, 82, 191, 209, 8, 72, 6, 202, + 141, 16, 82, 155, 1, 87, 5, 177, 226, 17, 25, 32, 68, 73, 86, 73, 68, 69, + 68, 32, 66, 89, 32, 72, 79, 82, 73, 90, 79, 78, 84, 65, 76, 32, 82, 85, + 6, 18, 71, 23, 78, 2, 195, 248, 13, 71, 4, 220, 226, 17, 5, 65, 82, 32, + 69, 67, 151, 103, 71, 114, 104, 12, 67, 73, 65, 78, 32, 76, 69, 84, 84, + 69, 82, 32, 232, 1, 5, 68, 73, 65, 78, 32, 183, 252, 17, 73, 58, 210, 1, + 77, 202, 133, 5, 75, 154, 214, 9, 84, 158, 24, 66, 246, 146, 2, 65, 2, + 69, 2, 78, 238, 253, 1, 68, 2, 71, 2, 72, 2, 73, 2, 74, 2, 76, 2, 80, 2, + 81, 2, 82, 2, 83, 2, 85, 2, 87, 2, 88, 3, 90, 5, 219, 132, 19, 77, 54, + 88, 7, 76, 69, 84, 84, 69, 82, 32, 205, 219, 14, 9, 84, 82, 73, 65, 78, + 71, 85, 76, 65, 52, 146, 155, 13, 84, 190, 215, 1, 76, 198, 135, 2, 83, + 238, 11, 65, 2, 69, 2, 78, 238, 253, 1, 66, 2, 67, 2, 68, 2, 70, 2, 71, + 2, 73, 2, 75, 2, 77, 2, 79, 2, 81, 2, 82, 2, 85, 2, 86, 3, 89, 128, 54, + 162, 1, 65, 194, 103, 69, 182, 103, 73, 154, 25, 79, 164, 112, 3, 82, 79, + 32, 234, 3, 85, 204, 70, 7, 89, 65, 78, 77, 65, 82, 32, 190, 160, 15, 86, + 198, 61, 77, 27, 87, 204, 23, 186, 1, 71, 62, 72, 254, 10, 75, 234, 3, + 76, 252, 13, 2, 77, 77, 22, 78, 154, 17, 80, 118, 82, 174, 7, 83, 190, 7, + 84, 184, 36, 3, 89, 65, 78, 204, 129, 1, 3, 88, 73, 77, 227, 243, 15, 67, + 6, 152, 239, 17, 4, 73, 67, 32, 87, 138, 91, 78, 155, 53, 69, 166, 1, 84, + 6, 65, 74, 65, 78, 73, 32, 129, 3, 10, 74, 79, 78, 71, 32, 84, 73, 76, + 69, 32, 78, 38, 76, 162, 2, 83, 139, 136, 13, 65, 72, 88, 6, 69, 84, 84, + 69, 82, 32, 213, 145, 12, 10, 73, 71, 65, 84, 85, 82, 69, 32, 83, 72, 70, + 134, 175, 3, 78, 202, 240, 11, 68, 82, 82, 34, 84, 162, 149, 3, 66, 2, + 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, 2, 83, 2, 86, + 186, 2, 65, 2, 69, 2, 73, 2, 79, 3, 85, 4, 222, 232, 6, 69, 253, 245, 7, + 5, 73, 71, 78, 32, 78, 88, 236, 1, 2, 66, 65, 38, 69, 46, 70, 46, 78, 42, + 79, 46, 80, 22, 83, 118, 84, 194, 1, 87, 112, 5, 71, 82, 69, 69, 78, 0, + 3, 82, 69, 68, 220, 131, 6, 3, 65, 85, 84, 222, 21, 74, 209, 175, 11, 11, + 67, 72, 82, 89, 83, 65, 78, 84, 72, 69, 77, 4, 162, 158, 1, 77, 251, 215, + 17, 67, 8, 228, 2, 4, 73, 71, 72, 84, 195, 1, 65, 12, 172, 2, 2, 73, 86, + 13, 3, 79, 85, 82, 8, 192, 1, 2, 79, 82, 65, 2, 73, 78, 8, 218, 1, 78, + 225, 224, 11, 3, 82, 67, 72, 2, 191, 204, 17, 76, 18, 88, 2, 79, 85, 76, + 4, 69, 86, 69, 78, 0, 2, 73, 88, 234, 152, 12, 85, 255, 199, 1, 80, 2, + 157, 2, 2, 84, 72, 12, 36, 3, 72, 82, 69, 13, 2, 87, 79, 6, 11, 69, 6, + 25, 4, 32, 79, 70, 32, 6, 46, 67, 241, 243, 6, 5, 66, 65, 77, 66, 79, 4, + 236, 135, 6, 2, 73, 82, 153, 234, 10, 5, 72, 65, 82, 65, 67, 6, 50, 69, + 60, 4, 72, 73, 84, 69, 151, 186, 17, 73, 2, 17, 2, 83, 84, 2, 17, 2, 32, + 87, 2, 171, 228, 17, 73, 2, 17, 2, 32, 68, 2, 215, 185, 17, 82, 52, 52, + 5, 65, 83, 65, 82, 32, 165, 135, 15, 2, 69, 77, 50, 162, 1, 69, 40, 7, + 76, 69, 84, 84, 69, 82, 32, 152, 1, 5, 80, 65, 83, 83, 73, 32, 11, 86, + 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 225, 231, 18, 3, 65, 78, 71, 2, + 129, 240, 15, 5, 78, 68, 32, 79, 70, 36, 234, 244, 16, 78, 222, 250, 1, + 66, 2, 67, 2, 68, 2, 71, 2, 74, 2, 75, 2, 76, 2, 77, 2, 80, 2, 82, 2, 83, + 2, 84, 2, 86, 2, 89, 187, 2, 65, 2, 11, 77, 2, 135, 152, 18, 66, 8, 198, + 240, 18, 69, 2, 73, 2, 79, 3, 85, 246, 1, 80, 7, 65, 89, 65, 76, 65, 77, + 32, 176, 11, 2, 69, 32, 225, 1, 3, 84, 69, 83, 236, 1, 198, 1, 68, 44, 9, + 70, 82, 65, 67, 84, 73, 79, 78, 32, 240, 1, 7, 76, 69, 84, 84, 69, 82, + 32, 164, 5, 7, 78, 85, 77, 66, 69, 82, 32, 36, 5, 83, 73, 71, 78, 32, + 154, 187, 13, 86, 239, 201, 1, 65, 22, 216, 166, 8, 2, 65, 84, 163, 211, + 8, 73, 26, 56, 4, 79, 78, 69, 32, 121, 6, 84, 72, 82, 69, 69, 32, 18, 82, + 84, 178, 131, 5, 83, 130, 132, 8, 70, 62, 79, 146, 222, 3, 69, 46, 72, + 47, 81, 4, 186, 138, 13, 87, 255, 220, 3, 69, 8, 202, 131, 5, 83, 198, + 135, 8, 69, 110, 84, 179, 220, 3, 81, 132, 1, 226, 1, 65, 82, 67, 158, 1, + 68, 78, 84, 82, 86, 150, 137, 13, 78, 182, 128, 2, 76, 38, 82, 134, 6, + 85, 206, 141, 1, 79, 238, 60, 73, 182, 196, 1, 83, 82, 66, 2, 71, 2, 74, + 2, 75, 2, 80, 162, 7, 69, 234, 61, 72, 2, 77, 3, 89, 11, 164, 163, 16, 7, + 82, 67, 72, 65, 73, 67, 32, 206, 198, 2, 65, 2, 73, 3, 85, 22, 26, 72, + 139, 233, 18, 65, 20, 44, 5, 73, 76, 76, 85, 32, 219, 232, 18, 65, 18, + 190, 255, 12, 76, 174, 235, 3, 78, 146, 197, 1, 82, 222, 56, 75, 2, 77, + 3, 89, 10, 136, 222, 10, 4, 79, 84, 32, 82, 190, 194, 7, 68, 138, 69, 72, + 187, 2, 65, 10, 38, 84, 222, 228, 18, 72, 187, 2, 65, 6, 218, 228, 18, + 72, 2, 84, 187, 2, 65, 12, 242, 227, 3, 69, 158, 172, 11, 79, 223, 214, + 3, 65, 6, 214, 137, 13, 79, 223, 134, 4, 84, 18, 50, 67, 114, 86, 174, + 138, 15, 65, 251, 149, 3, 80, 6, 54, 79, 120, 5, 73, 82, 67, 85, 76, 223, + 198, 14, 65, 2, 197, 185, 13, 9, 77, 66, 73, 78, 73, 78, 71, 32, 65, 6, + 60, 9, 69, 82, 84, 73, 67, 65, 76, 32, 66, 179, 161, 18, 73, 2, 177, 138, + 15, 2, 65, 82, 8, 80, 12, 87, 73, 84, 72, 32, 83, 84, 82, 79, 75, 69, 32, + 70, 65, 151, 220, 17, 83, 4, 64, 10, 65, 78, 68, 32, 77, 65, 76, 69, 32, + 65, 151, 220, 17, 83, 2, 25, 4, 78, 68, 32, 70, 2, 11, 69, 2, 11, 77, 2, + 191, 206, 7, 65, 2, 243, 158, 17, 69, 2, 167, 221, 16, 79, 185, 1, 210, + 1, 32, 220, 2, 5, 68, 65, 73, 67, 32, 224, 3, 8, 73, 67, 72, 65, 69, 65, + 78, 32, 222, 8, 83, 156, 166, 2, 3, 85, 65, 76, 182, 223, 10, 65, 240, + 167, 1, 8, 84, 69, 76, 80, 73, 69, 67, 69, 235, 132, 4, 71, 12, 132, 1, + 3, 73, 78, 32, 128, 1, 5, 87, 73, 84, 72, 32, 136, 147, 2, 3, 68, 65, 78, + 161, 136, 13, 8, 65, 78, 68, 32, 87, 79, 77, 65, 4, 172, 136, 10, 19, 66, + 85, 83, 73, 78, 69, 83, 83, 32, 83, 85, 73, 84, 32, 76, 69, 86, 73, 84, + 189, 151, 1, 4, 84, 85, 88, 69, 4, 192, 238, 13, 8, 71, 85, 65, 32, 80, + 73, 32, 77, 205, 163, 3, 4, 84, 85, 82, 66, 58, 112, 4, 65, 70, 70, 82, + 28, 7, 76, 69, 84, 84, 69, 82, 32, 132, 180, 14, 3, 86, 79, 67, 226, 72, + 71, 251, 25, 80, 2, 173, 243, 17, 2, 73, 67, 50, 82, 65, 176, 1, 2, 68, + 85, 2, 85, 28, 3, 72, 65, 76, 22, 73, 235, 147, 18, 75, 38, 154, 1, 75, + 130, 242, 12, 84, 250, 191, 1, 83, 130, 217, 3, 73, 226, 79, 66, 2, 68, + 2, 71, 2, 72, 2, 76, 2, 77, 2, 78, 2, 80, 2, 81, 2, 82, 3, 90, 5, 159, + 216, 18, 83, 2, 237, 255, 7, 2, 83, 72, 2, 239, 215, 18, 81, 4, 146, 218, + 18, 78, 3, 84, 102, 216, 1, 7, 76, 69, 84, 84, 69, 82, 32, 194, 4, 78, + 80, 12, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 220, 1, 4, 83, + 73, 71, 78, 173, 196, 10, 16, 65, 66, 66, 82, 69, 86, 73, 65, 84, 73, 79, + 78, 32, 77, 65, 82, 72, 218, 1, 65, 46, 66, 38, 68, 30, 71, 30, 74, 2, + 90, 30, 75, 30, 81, 38, 83, 50, 84, 54, 88, 210, 159, 6, 76, 134, 206, 1, + 82, 246, 221, 2, 89, 198, 207, 1, 72, 190, 184, 5, 78, 134, 2, 87, 218, + 103, 70, 2, 80, 171, 4, 77, 6, 194, 204, 10, 76, 122, 65, 179, 137, 7, + 89, 4, 186, 244, 12, 72, 227, 220, 3, 69, 4, 178, 161, 6, 65, 23, 72, 4, + 234, 203, 10, 72, 15, 73, 4, 166, 204, 10, 72, 15, 65, 4, 150, 162, 12, + 72, 15, 65, 4, 134, 161, 6, 72, 131, 129, 6, 79, 8, 238, 200, 10, 83, + 154, 3, 65, 131, 137, 7, 72, 6, 130, 160, 6, 72, 178, 175, 10, 69, 219, + 134, 1, 65, 4, 254, 160, 12, 65, 3, 79, 10, 33, 6, 85, 77, 66, 69, 82, + 32, 10, 174, 203, 10, 79, 58, 84, 131, 203, 2, 70, 14, 80, 2, 68, 79, + 116, 3, 76, 73, 78, 242, 162, 5, 70, 230, 219, 11, 84, 167, 10, 83, 6, + 54, 84, 13, 9, 85, 66, 76, 69, 32, 68, 79, 84, 32, 5, 11, 32, 2, 25, 4, + 87, 73, 84, 72, 2, 151, 193, 2, 73, 2, 231, 203, 3, 69, 2, 191, 163, 17, + 32, 2, 11, 32, 2, 173, 186, 18, 2, 83, 72, 4, 92, 17, 32, 83, 89, 77, 66, + 79, 76, 32, 70, 79, 82, 32, 76, 73, 71, 72, 84, 191, 215, 10, 76, 2, 231, + 135, 15, 72, 142, 1, 142, 1, 65, 20, 5, 67, 72, 69, 78, 32, 140, 160, 6, + 4, 82, 73, 65, 71, 249, 175, 11, 13, 84, 73, 65, 76, 32, 65, 82, 84, 83, + 32, 85, 78, 73, 2, 151, 157, 5, 67, 136, 1, 152, 1, 7, 76, 69, 84, 84, + 69, 82, 32, 194, 1, 83, 236, 2, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, + 78, 32, 222, 252, 17, 72, 225, 5, 4, 77, 65, 82, 75, 60, 254, 3, 84, 206, + 148, 2, 68, 218, 183, 14, 78, 214, 181, 1, 67, 2, 75, 2, 80, 2, 83, 2, + 90, 138, 69, 45, 2, 66, 2, 71, 2, 72, 2, 74, 2, 76, 2, 77, 2, 82, 2, 87, + 2, 89, 187, 2, 65, 62, 96, 4, 73, 71, 78, 32, 37, 16, 85, 66, 74, 79, 73, + 78, 69, 68, 32, 76, 69, 84, 84, 69, 82, 32, 4, 178, 173, 14, 67, 235, + 216, 3, 65, 58, 182, 1, 84, 206, 148, 2, 68, 218, 183, 14, 78, 214, 181, + 1, 67, 2, 75, 2, 80, 2, 83, 2, 90, 138, 69, 66, 2, 71, 2, 72, 2, 74, 2, + 76, 2, 77, 2, 82, 2, 87, 2, 89, 187, 2, 65, 8, 246, 129, 18, 83, 138, 69, + 72, 187, 2, 65, 10, 210, 198, 18, 65, 186, 2, 69, 2, 73, 2, 79, 3, 85, + 156, 1, 120, 11, 65, 82, 65, 77, 32, 71, 79, 78, 68, 73, 32, 232, 5, 3, + 67, 85, 76, 64, 5, 75, 32, 87, 79, 82, 235, 255, 17, 85, 150, 1, 100, 7, + 76, 69, 84, 84, 69, 82, 32, 178, 2, 82, 56, 5, 83, 73, 71, 78, 32, 82, + 86, 171, 207, 16, 68, 94, 210, 1, 74, 42, 84, 250, 230, 14, 65, 38, 68, + 214, 6, 85, 186, 202, 1, 73, 42, 76, 190, 195, 1, 75, 38, 78, 46, 83, 82, + 66, 2, 67, 2, 71, 2, 80, 138, 69, 72, 2, 77, 2, 82, 2, 86, 2, 89, 186, 2, + 69, 3, 79, 6, 182, 194, 18, 78, 38, 72, 187, 2, 65, 10, 170, 253, 17, 84, + 138, 69, 72, 2, 82, 187, 2, 65, 4, 32, 2, 65, 45, 143, 232, 14, 69, 2, + 199, 255, 17, 75, 10, 230, 176, 1, 67, 234, 202, 12, 72, 178, 43, 78, + 150, 227, 1, 86, 247, 244, 1, 65, 22, 26, 79, 251, 134, 16, 73, 20, 45, + 9, 87, 69, 76, 32, 83, 73, 71, 78, 32, 20, 74, 86, 250, 234, 14, 65, 38, + 85, 186, 202, 1, 73, 198, 140, 2, 69, 3, 79, 2, 193, 198, 7, 6, 79, 67, + 65, 76, 73, 67, 2, 197, 136, 18, 11, 73, 78, 69, 32, 79, 82, 68, 73, 78, + 65, 76, 2, 231, 171, 17, 75, 226, 15, 76, 10, 72, 69, 77, 65, 84, 73, 67, + 65, 76, 32, 205, 219, 14, 3, 69, 32, 68, 224, 15, 252, 1, 5, 66, 79, 76, + 68, 32, 168, 6, 14, 68, 79, 85, 66, 76, 69, 45, 83, 84, 82, 85, 67, 75, + 32, 238, 1, 70, 164, 2, 7, 73, 84, 65, 76, 73, 67, 32, 180, 3, 10, 77, + 79, 78, 79, 83, 80, 65, 67, 69, 32, 40, 2, 82, 73, 36, 3, 76, 69, 70, + 167, 1, 83, 160, 5, 176, 1, 8, 67, 65, 80, 73, 84, 65, 76, 32, 130, 2, + 83, 210, 14, 73, 210, 3, 69, 38, 75, 38, 78, 30, 80, 98, 82, 242, 5, 84, + 56, 7, 70, 82, 65, 75, 84, 85, 82, 247, 172, 16, 68, 104, 190, 4, 68, + 174, 15, 84, 174, 4, 65, 22, 66, 2, 90, 42, 69, 98, 71, 22, 73, 22, 75, + 34, 76, 22, 79, 50, 80, 42, 82, 22, 83, 70, 85, 214, 197, 1, 67, 250, + 132, 13, 77, 2, 78, 186, 202, 1, 88, 198, 140, 2, 70, 2, 72, 2, 74, 2, + 81, 2, 86, 2, 87, 3, 89, 208, 1, 60, 5, 77, 65, 76, 76, 32, 201, 25, 5, + 67, 82, 73, 80, 84, 104, 250, 1, 68, 218, 19, 65, 22, 66, 2, 90, 42, 69, + 38, 70, 62, 71, 22, 73, 22, 75, 34, 76, 22, 79, 50, 80, 42, 82, 22, 83, + 34, 84, 38, 85, 214, 197, 1, 67, 250, 132, 13, 77, 2, 78, 186, 202, 1, + 88, 198, 140, 2, 72, 2, 74, 2, 81, 2, 86, 2, 87, 3, 89, 7, 26, 73, 203, + 251, 14, 69, 2, 247, 213, 1, 71, 110, 68, 8, 67, 65, 80, 73, 84, 65, 76, + 32, 150, 23, 83, 183, 172, 16, 68, 38, 206, 183, 18, 65, 2, 66, 2, 68, 2, + 69, 2, 70, 2, 71, 2, 73, 2, 74, 2, 75, 2, 76, 2, 77, 2, 79, 2, 83, 2, 84, + 2, 85, 2, 86, 2, 87, 2, 88, 3, 89, 96, 52, 7, 82, 65, 75, 84, 85, 82, 32, + 179, 139, 7, 65, 94, 52, 8, 67, 65, 80, 73, 84, 65, 76, 32, 131, 21, 83, + 42, 186, 181, 18, 65, 2, 66, 2, 68, 2, 69, 2, 70, 2, 71, 2, 74, 2, 75, 2, + 76, 2, 77, 2, 78, 2, 79, 2, 80, 2, 81, 2, 83, 2, 84, 2, 85, 2, 86, 2, 87, + 2, 88, 3, 89, 222, 1, 100, 6, 83, 77, 65, 76, 76, 32, 222, 7, 67, 218, 2, + 69, 38, 75, 38, 78, 30, 80, 98, 82, 243, 5, 84, 104, 242, 1, 68, 186, 12, + 65, 22, 66, 2, 90, 42, 69, 38, 70, 62, 71, 22, 73, 22, 75, 34, 76, 22, + 79, 50, 80, 42, 82, 22, 83, 34, 84, 38, 85, 214, 197, 1, 67, 250, 132, + 13, 77, 2, 78, 186, 202, 1, 88, 198, 140, 2, 74, 2, 81, 2, 86, 2, 87, 3, + 89, 9, 52, 7, 79, 84, 76, 69, 83, 83, 32, 143, 244, 14, 69, 4, 238, 176, + 18, 73, 3, 74, 124, 246, 15, 67, 34, 83, 183, 172, 16, 68, 12, 32, 2, 71, + 72, 219, 133, 7, 83, 10, 17, 2, 84, 32, 10, 112, 6, 87, 72, 73, 84, 69, + 32, 194, 240, 5, 68, 222, 107, 65, 153, 235, 4, 9, 70, 76, 65, 84, 84, + 69, 78, 69, 68, 4, 226, 175, 14, 83, 39, 84, 130, 6, 84, 10, 65, 78, 83, + 45, 83, 69, 82, 73, 70, 32, 249, 13, 6, 67, 82, 73, 80, 84, 32, 176, 5, + 96, 5, 66, 79, 76, 68, 32, 164, 12, 6, 73, 84, 65, 76, 73, 67, 34, 67, + 34, 83, 183, 172, 16, 68, 204, 3, 98, 73, 122, 67, 218, 2, 69, 38, 75, + 38, 78, 30, 80, 98, 82, 30, 83, 214, 5, 84, 175, 173, 16, 68, 220, 1, 33, + 6, 84, 65, 76, 73, 67, 32, 220, 1, 74, 67, 218, 2, 69, 38, 75, 38, 78, + 30, 80, 98, 82, 30, 83, 215, 5, 84, 102, 37, 7, 65, 80, 73, 84, 65, 76, + 32, 102, 250, 1, 84, 174, 4, 65, 22, 66, 2, 90, 22, 68, 22, 69, 98, 71, + 22, 73, 22, 75, 34, 76, 22, 79, 50, 80, 42, 82, 22, 83, 70, 85, 214, 197, + 1, 67, 250, 132, 13, 77, 2, 78, 186, 202, 1, 88, 198, 140, 2, 70, 2, 72, + 2, 74, 2, 81, 2, 86, 2, 87, 3, 89, 9, 156, 155, 11, 4, 72, 69, 84, 65, + 151, 233, 6, 65, 2, 173, 174, 16, 4, 80, 83, 73, 76, 2, 17, 2, 65, 80, 2, + 159, 7, 80, 2, 133, 134, 18, 2, 65, 66, 6, 74, 72, 200, 145, 5, 8, 65, + 82, 84, 73, 65, 76, 32, 68, 175, 128, 12, 73, 2, 243, 145, 17, 73, 2, + 221, 145, 17, 2, 72, 79, 102, 29, 5, 77, 65, 76, 76, 32, 102, 246, 1, 65, + 22, 66, 2, 90, 22, 68, 22, 69, 38, 70, 62, 71, 22, 73, 22, 75, 34, 76, + 22, 79, 50, 80, 42, 82, 22, 83, 34, 84, 38, 85, 214, 197, 1, 67, 250, + 132, 13, 77, 2, 78, 186, 202, 1, 88, 198, 140, 2, 72, 2, 74, 2, 81, 2, + 86, 2, 87, 3, 89, 5, 231, 200, 14, 76, 5, 143, 128, 18, 69, 5, 227, 231, + 14, 69, 7, 130, 213, 1, 80, 251, 204, 16, 84, 5, 11, 73, 2, 29, 5, 78, + 65, 76, 32, 83, 2, 227, 1, 73, 5, 139, 206, 15, 65, 5, 243, 254, 17, 79, + 5, 11, 65, 2, 247, 229, 14, 80, 5, 167, 231, 14, 65, 7, 11, 77, 4, 190, + 171, 1, 73, 235, 180, 16, 69, 9, 238, 142, 18, 72, 2, 83, 219, 19, 73, 5, + 171, 131, 18, 72, 5, 11, 73, 2, 239, 128, 18, 71, 7, 214, 228, 14, 72, + 175, 152, 3, 65, 5, 151, 210, 1, 80, 2, 11, 72, 2, 11, 69, 2, 11, 84, 2, + 203, 139, 17, 65, 104, 11, 32, 104, 18, 67, 35, 83, 52, 53, 5, 65, 80, + 73, 84, 65, 52, 21, 3, 77, 65, 76, 52, 247, 254, 11, 76, 82, 76, 8, 67, + 65, 80, 73, 84, 65, 76, 32, 157, 1, 6, 83, 77, 65, 76, 76, 32, 36, 190, + 159, 18, 65, 2, 67, 2, 68, 2, 71, 2, 74, 2, 75, 2, 78, 2, 79, 2, 80, 2, + 81, 2, 83, 2, 84, 2, 85, 2, 86, 2, 87, 2, 88, 2, 89, 3, 90, 46, 162, 158, + 18, 65, 2, 66, 2, 67, 2, 68, 2, 70, 2, 72, 2, 73, 2, 74, 2, 75, 2, 76, 2, + 77, 2, 78, 2, 80, 2, 81, 2, 82, 2, 83, 2, 84, 2, 85, 2, 86, 2, 87, 2, 88, + 2, 89, 3, 90, 40, 45, 9, 32, 78, 85, 77, 69, 82, 65, 76, 32, 40, 70, 69, + 66, 70, 70, 78, 26, 83, 66, 84, 130, 168, 16, 90, 223, 86, 79, 6, 40, 4, + 73, 71, 72, 84, 255, 248, 9, 76, 5, 183, 197, 16, 69, 8, 30, 73, 105, 3, + 79, 85, 82, 4, 198, 130, 5, 70, 239, 129, 13, 86, 4, 65, 3, 73, 78, 69, + 8, 40, 4, 69, 86, 69, 78, 1, 2, 73, 88, 5, 143, 196, 16, 84, 10, 42, 72, + 202, 248, 9, 87, 187, 209, 7, 69, 4, 146, 129, 5, 73, 175, 166, 11, 82, + 130, 9, 226, 1, 65, 188, 4, 9, 67, 72, 65, 78, 73, 67, 65, 76, 32, 34, + 68, 204, 15, 11, 69, 84, 69, 73, 32, 77, 65, 89, 69, 75, 32, 178, 11, 76, + 62, 78, 134, 50, 82, 176, 15, 8, 83, 83, 65, 71, 69, 32, 87, 65, 22, 84, + 135, 151, 17, 77, 26, 68, 6, 83, 85, 82, 69, 68, 32, 189, 177, 14, 5, 84, + 32, 79, 78, 32, 24, 96, 5, 65, 78, 71, 76, 69, 216, 143, 10, 9, 82, 73, + 71, 72, 84, 32, 65, 78, 71, 139, 245, 7, 66, 21, 11, 32, 18, 212, 1, 39, + 87, 73, 84, 72, 32, 79, 80, 69, 78, 32, 65, 82, 77, 32, 69, 78, 68, 73, + 78, 71, 32, 73, 78, 32, 65, 82, 82, 79, 87, 32, 80, 79, 73, 78, 84, 73, + 78, 71, 32, 129, 226, 17, 7, 79, 80, 69, 78, 73, 78, 71, 16, 62, 68, 24, + 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 43, 85, 4, 73, 3, 79, 87, 78, 4, + 209, 145, 8, 5, 84, 32, 65, 78, 68, 4, 11, 80, 4, 205, 228, 11, 3, 32, + 65, 78, 4, 218, 148, 17, 65, 215, 56, 76, 252, 1, 56, 9, 69, 70, 65, 73, + 68, 82, 73, 78, 32, 159, 7, 73, 190, 1, 174, 1, 67, 52, 6, 68, 73, 71, + 73, 84, 32, 152, 1, 7, 78, 85, 77, 66, 69, 82, 32, 114, 83, 148, 143, 7, + 12, 69, 88, 67, 76, 65, 77, 65, 84, 73, 79, 78, 32, 131, 170, 8, 70, 70, + 128, 3, 5, 65, 80, 73, 84, 65, 187, 184, 15, 79, 26, 82, 84, 48, 2, 79, + 78, 194, 156, 16, 70, 30, 83, 102, 90, 210, 86, 78, 239, 112, 69, 8, 44, + 3, 72, 82, 69, 165, 144, 17, 2, 87, 79, 4, 163, 144, 17, 69, 20, 50, 84, + 234, 244, 4, 69, 46, 70, 42, 78, 31, 83, 6, 170, 246, 4, 72, 220, 247, 4, + 2, 87, 69, 159, 209, 7, 69, 70, 68, 3, 77, 65, 76, 209, 207, 9, 8, 89, + 77, 66, 79, 76, 32, 65, 73, 68, 45, 9, 76, 32, 76, 69, 84, 84, 69, 82, + 32, 68, 242, 1, 65, 38, 78, 142, 223, 3, 72, 2, 75, 178, 213, 10, 89, + 222, 150, 3, 79, 162, 64, 66, 2, 67, 2, 68, 2, 69, 2, 70, 2, 71, 2, 73, + 2, 74, 2, 76, 2, 77, 2, 80, 2, 81, 2, 82, 2, 83, 2, 84, 2, 85, 2, 86, 2, + 87, 2, 88, 3, 90, 7, 178, 239, 3, 84, 171, 156, 14, 73, 7, 182, 139, 18, + 71, 3, 89, 62, 48, 5, 69, 86, 65, 76, 32, 45, 3, 85, 77, 32, 6, 142, 239, + 10, 69, 134, 198, 4, 67, 115, 81, 56, 210, 1, 66, 50, 70, 164, 1, 3, 76, + 69, 70, 0, 4, 82, 73, 71, 72, 248, 1, 11, 77, 65, 84, 72, 69, 77, 65, 84, + 73, 67, 65, 22, 83, 124, 4, 84, 72, 82, 69, 138, 170, 15, 86, 246, 62, + 69, 166, 4, 87, 203, 11, 71, 4, 216, 153, 6, 3, 79, 76, 68, 211, 239, 7, + 76, 10, 84, 9, 76, 65, 84, 84, 69, 78, 69, 68, 32, 224, 3, 3, 79, 85, 82, + 187, 234, 15, 73, 4, 44, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, 2, 213, 1, + 3, 84, 32, 80, 6, 11, 84, 6, 78, 32, 41, 15, 45, 80, 79, 73, 78, 84, 73, + 78, 71, 32, 65, 78, 71, 76, 69, 4, 36, 5, 67, 85, 82, 76, 89, 59, 80, 2, + 17, 2, 32, 66, 2, 173, 180, 7, 4, 82, 65, 67, 75, 2, 245, 193, 17, 10, + 65, 82, 69, 78, 84, 72, 69, 83, 73, 83, 2, 215, 198, 17, 76, 12, 202, + 138, 15, 72, 200, 95, 2, 73, 88, 182, 2, 65, 245, 115, 15, 77, 65, 76, + 76, 32, 87, 72, 73, 84, 69, 32, 67, 73, 82, 67, 4, 11, 69, 4, 45, 9, 32, + 80, 79, 73, 78, 84, 69, 68, 32, 4, 174, 203, 9, 80, 151, 158, 6, 66, 158, + 1, 146, 1, 65, 104, 6, 67, 72, 69, 73, 75, 72, 34, 76, 144, 6, 8, 83, 89, + 76, 76, 65, 66, 76, 69, 0, 4, 87, 79, 82, 68, 50, 86, 199, 134, 16, 68, + 6, 80, 8, 72, 65, 78, 71, 32, 75, 72, 85, 164, 6, 3, 80, 85, 78, 151, + 219, 13, 78, 2, 175, 196, 16, 68, 4, 202, 177, 17, 65, 139, 60, 69, 94, 52, 6, 69, 84, 84, 69, 82, 32, 185, 5, 2, 85, 77, 92, 250, 1, 66, 36, 2, 67, 72, 38, 68, 50, 71, 38, 74, 34, 75, 42, 78, 62, 80, 30, 83, 42, 84, - 54, 73, 0, 3, 76, 65, 73, 0, 3, 77, 73, 84, 138, 247, 12, 72, 166, 10, - 82, 2, 87, 232, 181, 2, 2, 65, 84, 182, 220, 1, 89, 246, 8, 85, 226, 79, - 69, 3, 79, 4, 182, 180, 16, 72, 255, 186, 1, 65, 4, 218, 229, 16, 73, - 183, 137, 1, 65, 8, 238, 165, 10, 72, 202, 191, 6, 73, 247, 65, 68, 4, - 190, 165, 10, 72, 231, 197, 7, 79, 4, 154, 179, 16, 72, 203, 49, 73, 6, - 216, 1, 2, 79, 75, 163, 163, 10, 72, 12, 178, 1, 65, 0, 3, 71, 79, 85, - 230, 233, 17, 78, 3, 89, 6, 118, 65, 163, 177, 16, 72, 6, 158, 220, 17, - 65, 162, 14, 72, 3, 83, 10, 48, 2, 73, 76, 162, 163, 10, 72, 191, 129, 7, - 84, 5, 181, 177, 1, 4, 32, 76, 79, 78, 2, 209, 232, 17, 3, 32, 73, 89, 2, - 173, 130, 17, 7, 32, 82, 69, 80, 69, 84, 73, 30, 64, 10, 79, 87, 69, 76, - 32, 83, 73, 71, 78, 32, 143, 178, 15, 73, 28, 130, 1, 65, 44, 4, 67, 72, - 69, 73, 2, 79, 0, 3, 83, 79, 85, 0, 2, 89, 69, 22, 73, 38, 85, 194, 131, - 11, 78, 151, 175, 4, 86, 8, 242, 216, 16, 78, 182, 80, 65, 187, 64, 85, - 2, 199, 216, 16, 78, 4, 178, 216, 16, 78, 239, 144, 1, 73, 4, 142, 216, - 16, 78, 239, 144, 1, 85, 4, 182, 224, 16, 84, 195, 56, 79, 178, 3, 160, - 1, 11, 68, 69, 32, 75, 73, 75, 65, 75, 85, 73, 32, 140, 211, 16, 20, 79, - 82, 65, 72, 32, 87, 73, 84, 72, 32, 78, 73, 78, 69, 32, 66, 82, 65, 78, - 67, 95, 83, 174, 3, 148, 1, 17, 67, 79, 77, 66, 73, 78, 73, 78, 71, 32, - 78, 85, 77, 66, 69, 82, 32, 164, 1, 10, 83, 89, 76, 76, 65, 66, 76, 69, - 32, 77, 155, 167, 9, 68, 14, 62, 84, 56, 7, 72, 85, 78, 68, 82, 69, 68, - 135, 186, 4, 77, 8, 26, 69, 207, 186, 4, 72, 6, 26, 78, 167, 161, 14, 69, - 4, 160, 186, 4, 2, 32, 84, 219, 170, 13, 83, 142, 3, 22, 48, 163, 21, 49, - 198, 1, 118, 48, 250, 1, 49, 210, 1, 50, 138, 2, 51, 254, 1, 52, 150, 2, - 53, 158, 2, 54, 242, 1, 55, 134, 2, 56, 187, 2, 57, 18, 142, 1, 49, 34, - 50, 22, 51, 22, 52, 254, 150, 2, 53, 154, 240, 3, 56, 156, 218, 10, 3, - 57, 32, 77, 92, 3, 55, 32, 77, 237, 90, 3, 54, 32, 87, 2, 11, 32, 2, 195, - 206, 17, 75, 2, 159, 217, 17, 32, 2, 215, 148, 12, 32, 2, 11, 32, 2, 251, - 205, 17, 87, 20, 130, 1, 49, 22, 54, 18, 56, 22, 57, 248, 28, 2, 48, 32, - 198, 183, 7, 53, 170, 203, 5, 52, 198, 10, 55, 222, 9, 50, 243, 235, 3, - 51, 2, 147, 179, 17, 32, 2, 167, 25, 32, 2, 163, 204, 13, 32, 2, 227, - 245, 12, 32, 20, 106, 49, 22, 50, 22, 51, 22, 52, 22, 53, 22, 54, 22, 55, - 22, 57, 130, 224, 11, 48, 145, 231, 1, 2, 56, 32, 2, 211, 169, 10, 32, 2, - 247, 128, 10, 32, 2, 143, 186, 17, 32, 2, 247, 142, 12, 32, 2, 167, 227, - 12, 32, 2, 135, 188, 17, 32, 2, 179, 221, 12, 32, 2, 239, 28, 32, 20, - 174, 1, 48, 16, 2, 49, 32, 20, 2, 52, 32, 20, 2, 56, 32, 246, 194, 4, 57, - 182, 51, 50, 22, 53, 136, 230, 7, 2, 54, 32, 232, 255, 3, 3, 55, 32, 78, - 237, 90, 3, 51, 32, 89, 2, 179, 37, 32, 2, 207, 200, 17, 89, 2, 187, 200, - 17, 70, 2, 255, 145, 16, 78, 20, 184, 1, 2, 48, 32, 20, 2, 51, 32, 30, - 53, 22, 56, 160, 8, 3, 52, 32, 75, 128, 22, 3, 54, 32, 72, 222, 2, 55, - 138, 213, 4, 49, 208, 179, 4, 3, 57, 32, 87, 241, 232, 5, 3, 50, 32, 72, - 2, 247, 234, 15, 72, 2, 213, 151, 17, 2, 78, 71, 2, 191, 162, 11, 32, 2, - 235, 186, 17, 32, 20, 178, 1, 48, 18, 53, 20, 2, 54, 32, 20, 2, 56, 32, - 22, 57, 136, 213, 9, 2, 50, 32, 236, 4, 2, 51, 32, 170, 215, 1, 49, 148, - 223, 3, 3, 52, 32, 76, 137, 143, 1, 3, 55, 32, 78, 2, 159, 4, 32, 2, 155, - 145, 16, 32, 2, 215, 170, 17, 71, 2, 247, 144, 13, 78, 2, 237, 131, 16, - 2, 32, 77, 20, 196, 1, 2, 50, 32, 22, 54, 238, 25, 51, 168, 2, 3, 55, 32, - 78, 140, 216, 1, 2, 52, 32, 234, 150, 3, 56, 248, 144, 5, 3, 48, 32, 78, - 172, 89, 3, 49, 32, 87, 158, 13, 57, 225, 227, 4, 3, 53, 32, 75, 2, 203, - 168, 17, 77, 2, 247, 151, 10, 32, 20, 196, 1, 3, 52, 32, 75, 20, 2, 53, - 32, 22, 57, 232, 7, 3, 49, 32, 71, 234, 1, 51, 236, 6, 3, 48, 32, 71, - 146, 5, 50, 236, 224, 10, 2, 55, 32, 252, 149, 4, 3, 54, 32, 75, 241, 87, - 3, 56, 32, 70, 2, 155, 175, 17, 80, 2, 155, 189, 17, 70, 2, 187, 243, 10, - 32, 20, 228, 1, 2, 48, 32, 20, 2, 49, 32, 20, 2, 52, 32, 22, 53, 172, 14, - 6, 54, 32, 76, 79, 78, 71, 224, 11, 2, 57, 32, 168, 173, 3, 3, 51, 32, - 72, 168, 154, 9, 4, 50, 32, 78, 71, 152, 239, 3, 3, 55, 32, 72, 189, 97, - 3, 56, 32, 70, 2, 139, 187, 17, 89, 2, 147, 226, 15, 80, 2, 255, 225, 15, - 76, 2, 207, 212, 16, 32, 20, 230, 1, 53, 216, 14, 4, 48, 32, 78, 71, 244, - 7, 3, 51, 32, 71, 192, 152, 12, 2, 55, 32, 138, 118, 57, 192, 112, 3, 50, - 32, 75, 156, 202, 1, 3, 49, 32, 84, 208, 27, 4, 56, 32, 78, 89, 252, 131, - 1, 3, 52, 32, 77, 225, 34, 2, 54, 32, 2, 207, 159, 17, 32, 200, 1, 118, - 48, 186, 2, 49, 210, 2, 50, 166, 2, 51, 222, 1, 52, 186, 2, 53, 214, 2, - 54, 166, 2, 55, 190, 2, 56, 223, 2, 57, 20, 222, 1, 49, 30, 52, 28, 7, - 53, 32, 76, 79, 78, 71, 32, 12, 2, 51, 32, 184, 11, 6, 54, 32, 76, 79, - 78, 71, 138, 241, 4, 50, 198, 168, 2, 48, 212, 134, 5, 3, 55, 32, 71, - 228, 176, 3, 3, 57, 32, 89, 253, 39, 3, 56, 32, 75, 2, 181, 130, 16, 2, - 32, 70, 2, 241, 132, 15, 2, 32, 84, 2, 11, 77, 2, 203, 132, 15, 66, 20, - 208, 1, 6, 48, 32, 76, 79, 78, 71, 22, 51, 34, 54, 22, 56, 32, 2, 57, 32, - 248, 17, 4, 53, 32, 78, 71, 204, 208, 2, 2, 55, 32, 184, 151, 2, 3, 50, - 32, 75, 128, 136, 10, 3, 52, 32, 87, 229, 132, 2, 2, 49, 32, 2, 227, 165, - 16, 32, 2, 11, 32, 2, 151, 179, 17, 74, 2, 203, 192, 16, 32, 2, 11, 32, - 2, 227, 178, 17, 87, 2, 151, 185, 15, 78, 20, 226, 1, 50, 32, 2, 51, 32, - 128, 4, 3, 52, 32, 71, 254, 9, 48, 148, 240, 9, 4, 56, 32, 72, 79, 156, - 220, 2, 5, 55, 32, 78, 71, 71, 224, 88, 2, 53, 32, 220, 55, 2, 57, 32, - 236, 236, 1, 3, 54, 32, 87, 245, 150, 1, 2, 49, 32, 2, 11, 32, 2, 171, - 155, 13, 77, 2, 11, 78, 2, 167, 179, 17, 68, 20, 200, 3, 2, 56, 32, 252, - 4, 3, 52, 32, 78, 216, 243, 4, 3, 50, 32, 75, 210, 210, 2, 49, 2, 53, - 236, 174, 2, 2, 55, 32, 232, 129, 5, 3, 51, 32, 70, 0, 3, 54, 32, 83, - 236, 53, 2, 48, 32, 233, 148, 1, 3, 57, 32, 87, 20, 236, 1, 8, 50, 32, + 54, 73, 0, 3, 76, 65, 73, 0, 3, 77, 73, 84, 218, 253, 12, 72, 166, 10, + 82, 2, 87, 248, 186, 2, 2, 65, 84, 250, 223, 1, 89, 246, 8, 85, 226, 79, + 69, 3, 79, 4, 198, 193, 16, 72, 147, 189, 1, 65, 4, 226, 242, 16, 73, + 211, 139, 1, 65, 8, 138, 167, 10, 72, 182, 203, 6, 73, 147, 68, 68, 4, + 218, 166, 10, 72, 239, 211, 7, 79, 4, 170, 192, 16, 72, 195, 49, 73, 6, + 216, 1, 2, 79, 75, 191, 164, 10, 72, 12, 178, 1, 65, 0, 3, 71, 79, 85, + 138, 249, 17, 78, 3, 89, 6, 118, 65, 179, 190, 16, 72, 6, 194, 235, 17, + 65, 162, 14, 72, 3, 83, 10, 48, 2, 73, 76, 190, 164, 10, 72, 199, 143, 7, + 84, 5, 225, 177, 1, 4, 32, 76, 79, 78, 2, 245, 247, 17, 3, 32, 73, 89, 2, + 209, 145, 17, 7, 32, 82, 69, 80, 69, 84, 73, 30, 64, 10, 79, 87, 69, 76, + 32, 83, 73, 71, 78, 32, 239, 189, 15, 73, 28, 130, 1, 65, 44, 4, 67, 72, + 69, 73, 2, 79, 0, 3, 83, 79, 85, 0, 2, 89, 69, 22, 73, 38, 85, 230, 132, + 11, 78, 211, 185, 4, 86, 8, 250, 229, 16, 78, 210, 82, 65, 187, 64, 85, + 2, 207, 229, 16, 78, 4, 186, 229, 16, 78, 139, 147, 1, 73, 4, 150, 229, + 16, 78, 139, 147, 1, 85, 6, 200, 220, 16, 4, 80, 79, 77, 69, 146, 19, 84, + 195, 56, 79, 178, 3, 160, 1, 11, 68, 69, 32, 75, 73, 75, 65, 75, 85, 73, + 32, 136, 224, 16, 20, 79, 82, 65, 72, 32, 87, 73, 84, 72, 32, 78, 73, 78, + 69, 32, 66, 82, 65, 78, 67, 79, 83, 174, 3, 148, 1, 17, 67, 79, 77, 66, + 73, 78, 73, 78, 71, 32, 78, 85, 77, 66, 69, 82, 32, 164, 1, 10, 83, 89, + 76, 76, 65, 66, 76, 69, 32, 77, 139, 168, 9, 68, 14, 62, 84, 56, 7, 72, + 85, 78, 68, 82, 69, 68, 199, 181, 4, 77, 8, 26, 69, 143, 182, 4, 72, 6, + 26, 78, 219, 172, 14, 69, 4, 224, 181, 4, 2, 32, 84, 163, 190, 13, 83, + 142, 3, 22, 48, 143, 21, 49, 198, 1, 118, 48, 250, 1, 49, 210, 1, 50, + 138, 2, 51, 254, 1, 52, 130, 2, 53, 158, 2, 54, 242, 1, 55, 134, 2, 56, + 187, 2, 57, 18, 142, 1, 49, 34, 50, 22, 51, 22, 52, 162, 151, 2, 53, 214, + 236, 3, 56, 196, 236, 10, 3, 57, 32, 77, 92, 3, 55, 32, 77, 237, 90, 3, + 54, 32, 87, 2, 11, 32, 2, 203, 221, 17, 75, 2, 167, 232, 17, 32, 2, 231, + 154, 12, 32, 2, 11, 32, 2, 131, 221, 17, 87, 20, 130, 1, 49, 22, 54, 18, + 56, 22, 57, 228, 28, 2, 48, 32, 214, 183, 7, 53, 218, 209, 5, 52, 198, + 10, 55, 222, 9, 50, 207, 244, 3, 51, 2, 155, 194, 17, 32, 2, 147, 25, 32, + 2, 139, 210, 13, 32, 2, 151, 252, 12, 32, 20, 106, 49, 22, 50, 22, 51, + 22, 52, 22, 53, 22, 54, 22, 55, 22, 57, 194, 224, 11, 48, 185, 236, 1, 2, + 56, 32, 2, 199, 170, 10, 32, 2, 247, 129, 10, 32, 2, 151, 201, 17, 32, 2, + 135, 149, 12, 32, 2, 219, 233, 12, 32, 2, 143, 203, 17, 32, 2, 231, 227, + 12, 32, 2, 219, 28, 32, 20, 174, 1, 48, 16, 2, 49, 32, 20, 2, 52, 32, 20, + 2, 56, 32, 182, 190, 4, 57, 214, 51, 50, 22, 53, 220, 240, 7, 2, 54, 32, + 188, 136, 4, 3, 55, 32, 78, 237, 90, 3, 51, 32, 89, 2, 159, 37, 32, 2, + 215, 215, 17, 89, 2, 195, 215, 17, 70, 2, 235, 158, 16, 78, 20, 192, 1, + 2, 48, 32, 22, 53, 22, 56, 160, 8, 3, 52, 32, 75, 128, 22, 3, 54, 32, 72, + 222, 2, 55, 254, 208, 4, 49, 224, 184, 4, 3, 57, 32, 87, 156, 174, 4, 2, + 51, 32, 157, 197, 1, 3, 50, 32, 72, 2, 143, 247, 15, 72, 2, 227, 212, 15, + 32, 2, 135, 202, 17, 32, 20, 178, 1, 48, 18, 53, 20, 2, 54, 32, 20, 2, + 56, 32, 22, 57, 156, 214, 9, 2, 50, 32, 236, 4, 2, 51, 32, 210, 214, 1, + 49, 164, 234, 3, 3, 52, 32, 76, 197, 144, 1, 3, 55, 32, 78, 2, 159, 4, + 32, 2, 155, 158, 16, 32, 2, 243, 185, 17, 71, 2, 183, 151, 13, 78, 2, + 237, 144, 16, 2, 32, 77, 20, 196, 1, 2, 50, 32, 22, 54, 238, 25, 51, 168, + 2, 3, 55, 32, 78, 200, 216, 1, 2, 52, 32, 158, 146, 3, 56, 144, 150, 5, + 3, 48, 32, 78, 192, 89, 3, 49, 32, 87, 158, 13, 57, 249, 238, 4, 3, 53, + 32, 75, 2, 231, 183, 17, 77, 2, 131, 153, 10, 32, 20, 196, 1, 3, 52, 32, + 75, 20, 2, 53, 32, 22, 57, 232, 7, 3, 49, 32, 71, 234, 1, 51, 236, 6, 3, + 48, 32, 71, 146, 5, 50, 136, 226, 10, 2, 55, 32, 172, 160, 4, 3, 54, 32, + 75, 217, 88, 3, 56, 32, 70, 2, 183, 190, 17, 80, 2, 183, 204, 17, 70, 2, + 215, 244, 10, 32, 20, 228, 1, 2, 48, 32, 20, 2, 49, 32, 20, 2, 52, 32, + 22, 53, 172, 14, 6, 54, 32, 76, 79, 78, 71, 224, 11, 2, 57, 32, 152, 169, + 3, 3, 51, 32, 72, 128, 165, 9, 4, 50, 32, 78, 71, 236, 247, 3, 3, 55, 32, + 72, 189, 97, 3, 56, 32, 70, 2, 167, 202, 17, 89, 2, 199, 238, 15, 80, 2, + 179, 238, 15, 76, 2, 235, 227, 16, 32, 20, 230, 1, 53, 216, 14, 4, 48, + 32, 78, 71, 244, 7, 3, 51, 32, 71, 144, 159, 12, 2, 55, 32, 250, 117, 57, + 200, 117, 3, 50, 32, 75, 136, 203, 1, 3, 49, 32, 84, 156, 28, 4, 56, 32, + 78, 89, 152, 134, 1, 3, 52, 32, 77, 225, 34, 2, 54, 32, 2, 235, 174, 17, + 32, 200, 1, 118, 48, 186, 2, 49, 210, 2, 50, 166, 2, 51, 222, 1, 52, 186, + 2, 53, 214, 2, 54, 166, 2, 55, 190, 2, 56, 223, 2, 57, 20, 222, 1, 49, + 30, 52, 28, 7, 53, 32, 76, 79, 78, 71, 32, 12, 2, 51, 32, 184, 11, 6, 54, + 32, 76, 79, 78, 71, 254, 236, 4, 50, 170, 170, 2, 48, 204, 143, 5, 3, 55, + 32, 71, 200, 182, 3, 3, 57, 32, 89, 201, 40, 3, 56, 32, 75, 2, 181, 143, + 16, 2, 32, 70, 2, 189, 144, 15, 2, 32, 84, 2, 11, 77, 2, 151, 144, 15, + 66, 20, 208, 1, 6, 48, 32, 76, 79, 78, 71, 22, 51, 34, 54, 22, 56, 32, 2, + 57, 32, 248, 17, 4, 53, 32, 78, 71, 128, 209, 2, 2, 55, 32, 248, 146, 2, + 3, 50, 32, 75, 216, 151, 10, 3, 52, 32, 87, 181, 136, 2, 2, 49, 32, 2, + 235, 178, 16, 32, 2, 11, 32, 2, 179, 194, 17, 74, 2, 207, 205, 16, 32, 2, + 11, 32, 2, 255, 193, 17, 87, 2, 203, 197, 15, 78, 20, 226, 1, 50, 32, 2, + 51, 32, 128, 4, 3, 52, 32, 71, 254, 9, 48, 168, 241, 9, 4, 56, 32, 72, + 79, 208, 225, 2, 5, 55, 32, 78, 71, 71, 148, 88, 2, 53, 32, 168, 61, 2, + 57, 32, 216, 237, 1, 3, 54, 32, 87, 221, 153, 1, 2, 49, 32, 2, 11, 32, 2, + 235, 161, 13, 77, 2, 11, 78, 2, 195, 194, 17, 68, 20, 200, 3, 2, 56, 32, + 252, 4, 3, 52, 32, 78, 200, 239, 4, 3, 50, 32, 75, 250, 215, 2, 49, 2, + 53, 232, 174, 2, 2, 55, 32, 160, 140, 5, 3, 51, 32, 70, 0, 3, 54, 32, 83, + 224, 53, 2, 48, 32, 197, 152, 1, 3, 57, 32, 87, 20, 236, 1, 8, 50, 32, 76, 79, 78, 71, 32, 77, 20, 3, 53, 32, 77, 22, 54, 224, 2, 3, 57, 32, 78, - 174, 219, 6, 55, 220, 155, 3, 3, 51, 32, 87, 236, 224, 2, 2, 48, 32, 176, - 60, 3, 56, 32, 71, 204, 228, 1, 3, 49, 32, 89, 1, 3, 52, 32, 86, 2, 171, - 164, 17, 66, 2, 179, 175, 17, 66, 2, 213, 141, 16, 3, 32, 78, 71, 20, + 238, 215, 6, 55, 176, 160, 3, 3, 51, 32, 87, 160, 230, 2, 2, 48, 32, 168, + 60, 3, 56, 32, 71, 216, 233, 1, 3, 49, 32, 89, 1, 3, 52, 32, 86, 2, 199, + 179, 17, 66, 2, 207, 190, 17, 66, 2, 221, 154, 16, 3, 32, 78, 71, 20, 192, 1, 2, 50, 32, 34, 52, 26, 53, 34, 54, 36, 2, 55, 32, 188, 7, 2, 48, - 32, 244, 250, 9, 3, 56, 32, 75, 128, 193, 2, 2, 49, 32, 156, 138, 3, 5, - 57, 32, 78, 71, 71, 253, 207, 1, 2, 51, 32, 2, 11, 78, 2, 207, 190, 17, - 74, 2, 165, 5, 2, 32, 77, 2, 11, 32, 2, 247, 172, 17, 71, 2, 145, 249, - 14, 4, 32, 78, 71, 71, 2, 223, 208, 15, 74, 20, 220, 1, 2, 48, 32, 20, 6, - 49, 32, 76, 79, 78, 71, 30, 56, 156, 2, 2, 55, 32, 220, 192, 9, 3, 52, - 32, 78, 236, 49, 4, 54, 32, 71, 85, 232, 129, 5, 2, 53, 32, 240, 87, 3, - 50, 32, 83, 0, 2, 51, 32, 165, 101, 2, 57, 32, 2, 251, 246, 14, 74, 2, - 241, 157, 12, 2, 32, 77, 2, 171, 206, 12, 32, 24, 198, 1, 50, 2, 52, 36, - 6, 53, 32, 76, 79, 78, 71, 28, 3, 55, 32, 78, 34, 56, 184, 236, 11, 2, - 54, 32, 160, 98, 3, 57, 32, 75, 168, 253, 2, 3, 51, 32, 86, 136, 111, 4, - 48, 32, 78, 89, 219, 53, 49, 4, 181, 246, 14, 4, 32, 77, 66, 79, 2, 253, - 156, 17, 2, 32, 74, 2, 11, 71, 2, 203, 241, 15, 85, 2, 207, 231, 15, 32, + 32, 128, 252, 9, 3, 56, 32, 75, 188, 198, 2, 2, 49, 32, 136, 144, 3, 5, + 57, 32, 78, 71, 71, 229, 210, 1, 2, 51, 32, 2, 11, 78, 2, 235, 205, 17, + 74, 2, 165, 5, 2, 32, 77, 2, 11, 32, 2, 147, 188, 17, 71, 2, 221, 132, + 15, 4, 32, 78, 71, 71, 2, 147, 221, 15, 74, 20, 220, 1, 2, 48, 32, 20, 6, + 49, 32, 76, 79, 78, 71, 30, 56, 156, 2, 2, 55, 32, 240, 193, 9, 3, 52, + 32, 78, 236, 49, 4, 54, 32, 71, 85, 160, 140, 5, 2, 53, 32, 216, 88, 3, + 50, 32, 83, 0, 2, 51, 32, 241, 101, 2, 57, 32, 2, 199, 130, 15, 74, 2, + 193, 164, 12, 2, 32, 77, 2, 243, 212, 12, 32, 24, 198, 1, 50, 2, 52, 36, + 6, 53, 32, 76, 79, 78, 71, 28, 3, 55, 32, 78, 34, 56, 220, 242, 11, 2, + 54, 32, 196, 98, 3, 57, 32, 75, 148, 131, 3, 3, 51, 32, 86, 240, 113, 4, + 48, 32, 78, 89, 219, 53, 49, 4, 129, 130, 15, 4, 32, 77, 66, 79, 2, 153, + 172, 17, 2, 32, 74, 2, 11, 71, 2, 203, 254, 15, 85, 2, 207, 244, 15, 32, 20, 212, 1, 2, 48, 32, 22, 49, 22, 50, 20, 6, 51, 32, 76, 79, 78, 71, 34, - 56, 216, 136, 9, 2, 53, 32, 252, 128, 1, 3, 52, 32, 78, 168, 193, 2, 2, - 54, 32, 236, 251, 2, 4, 55, 32, 77, 66, 161, 30, 4, 57, 32, 77, 85, 2, - 163, 242, 14, 68, 2, 139, 136, 10, 32, 2, 239, 216, 10, 32, 2, 165, 172, - 15, 3, 32, 78, 71, 2, 17, 2, 32, 77, 2, 163, 201, 15, 66, 16, 142, 1, 48, + 56, 220, 137, 9, 2, 53, 32, 128, 129, 1, 3, 52, 32, 78, 232, 198, 2, 2, + 54, 32, 216, 129, 3, 4, 55, 32, 77, 66, 237, 30, 4, 57, 32, 77, 85, 2, + 239, 253, 14, 68, 2, 147, 137, 10, 32, 2, 139, 218, 10, 32, 2, 217, 184, + 15, 3, 32, 78, 71, 2, 17, 2, 32, 77, 2, 215, 213, 15, 66, 16, 142, 1, 48, 32, 3, 49, 32, 78, 20, 3, 50, 32, 78, 20, 2, 51, 32, 20, 3, 52, 32, 87, - 22, 53, 20, 2, 54, 32, 213, 201, 12, 3, 55, 32, 70, 2, 11, 32, 2, 243, - 199, 15, 71, 2, 223, 199, 15, 68, 2, 155, 146, 17, 74, 2, 131, 231, 16, - 72, 2, 131, 163, 17, 85, 2, 199, 236, 15, 32, 2, 255, 144, 1, 83, 248, 1, - 72, 6, 79, 73, 84, 73, 67, 32, 204, 254, 10, 2, 67, 85, 179, 183, 2, 80, + 22, 53, 20, 2, 54, 32, 157, 208, 12, 3, 55, 32, 70, 2, 11, 32, 2, 167, + 212, 15, 71, 2, 147, 212, 15, 68, 2, 183, 161, 17, 74, 2, 159, 246, 16, + 72, 2, 159, 178, 17, 85, 2, 199, 249, 15, 32, 2, 163, 145, 1, 83, 248, 1, + 72, 6, 79, 73, 84, 73, 67, 32, 136, 255, 10, 2, 67, 85, 191, 193, 2, 80, 244, 1, 104, 8, 67, 85, 82, 83, 73, 86, 69, 32, 221, 10, 13, 72, 73, 69, 82, 79, 71, 76, 89, 80, 72, 73, 67, 32, 180, 1, 96, 9, 70, 82, 65, 67, 84, 73, 79, 78, 32, 250, 2, 76, 133, 3, 7, 78, 85, 77, 66, 69, 82, 32, 24, 78, 69, 50, 70, 44, 4, 79, 78, 69, 32, 46, 83, 50, 84, 61, 3, 78, 73, 78, 4, 160, 1, 2, 76, 69, 93, 4, 73, 71, 72, 84, 4, 192, 1, 2, 73, 86, - 13, 3, 79, 85, 82, 4, 140, 175, 15, 4, 84, 87, 69, 76, 19, 72, 4, 26, 69, + 13, 3, 79, 85, 82, 4, 192, 187, 15, 4, 84, 87, 69, 76, 19, 72, 4, 26, 69, 93, 2, 73, 88, 2, 65, 2, 86, 69, 6, 46, 69, 12, 3, 72, 82, 69, 13, 2, 87, - 79, 2, 23, 78, 2, 11, 69, 2, 237, 176, 15, 5, 32, 84, 87, 69, 76, 52, 76, + 79, 2, 23, 78, 2, 11, 69, 2, 161, 189, 15, 5, 32, 84, 87, 69, 76, 52, 76, 6, 69, 84, 84, 69, 82, 32, 137, 2, 8, 79, 71, 79, 71, 82, 65, 77, 32, 48, - 182, 1, 65, 46, 84, 182, 235, 1, 78, 2, 83, 218, 200, 13, 72, 130, 179, + 182, 1, 65, 46, 84, 238, 235, 1, 78, 2, 83, 214, 212, 13, 72, 234, 181, 1, 75, 138, 69, 66, 2, 68, 2, 76, 2, 77, 2, 80, 2, 81, 2, 82, 2, 87, 2, - 89, 186, 2, 69, 2, 73, 3, 79, 5, 253, 176, 11, 6, 82, 67, 72, 65, 73, 67, - 6, 202, 174, 17, 65, 2, 69, 3, 79, 4, 138, 209, 4, 73, 241, 192, 12, 2, + 89, 186, 2, 69, 2, 73, 3, 79, 5, 209, 177, 11, 6, 82, 67, 72, 65, 73, 67, + 6, 230, 189, 17, 65, 2, 69, 3, 79, 4, 254, 204, 4, 73, 153, 212, 12, 2, 82, 77, 104, 92, 5, 69, 73, 71, 72, 84, 30, 70, 92, 4, 78, 73, 78, 69, 54, 83, 78, 84, 73, 2, 79, 78, 11, 150, 1, 89, 223, 1, 32, 24, 18, 73, - 35, 79, 12, 142, 2, 86, 207, 235, 3, 70, 12, 148, 2, 2, 85, 82, 167, 235, - 3, 82, 11, 28, 2, 84, 89, 223, 1, 32, 2, 203, 160, 6, 32, 24, 40, 4, 69, - 86, 69, 78, 1, 2, 73, 88, 13, 154, 1, 32, 167, 235, 3, 84, 28, 34, 72, - 50, 87, 131, 159, 6, 69, 12, 32, 2, 82, 69, 155, 235, 3, 73, 8, 39, 69, - 12, 26, 79, 155, 235, 3, 69, 9, 11, 32, 6, 166, 157, 6, 72, 235, 185, 5, - 84, 64, 96, 7, 76, 69, 84, 84, 69, 82, 32, 141, 167, 3, 11, 83, 89, 77, + 35, 79, 12, 142, 2, 86, 215, 231, 3, 70, 12, 148, 2, 2, 85, 82, 175, 231, + 3, 82, 11, 28, 2, 84, 89, 223, 1, 32, 2, 139, 157, 6, 32, 24, 40, 4, 69, + 86, 69, 78, 1, 2, 73, 88, 13, 154, 1, 32, 175, 231, 3, 84, 28, 34, 72, + 50, 87, 195, 155, 6, 69, 12, 32, 2, 82, 69, 163, 231, 3, 73, 8, 39, 69, + 12, 26, 79, 163, 231, 3, 69, 9, 11, 32, 6, 230, 153, 6, 72, 207, 195, 5, + 84, 64, 96, 7, 76, 69, 84, 84, 69, 82, 32, 253, 162, 3, 11, 83, 89, 77, 66, 79, 76, 32, 86, 73, 68, 74, 60, 174, 1, 66, 2, 82, 22, 78, 30, 83, - 38, 84, 222, 172, 15, 72, 130, 179, 1, 75, 138, 69, 68, 2, 76, 2, 77, 2, - 80, 2, 81, 2, 87, 2, 89, 186, 2, 65, 2, 69, 2, 73, 3, 79, 4, 219, 165, 3, - 65, 8, 198, 165, 3, 65, 3, 69, 6, 170, 165, 3, 65, 151, 130, 14, 69, 10, - 134, 165, 3, 65, 2, 69, 151, 130, 14, 79, 2, 215, 160, 12, 73, 20, 44, 5, - 73, 67, 65, 76, 32, 175, 166, 17, 79, 18, 116, 10, 76, 79, 78, 71, 32, - 79, 86, 69, 82, 32, 74, 80, 26, 84, 168, 1, 7, 83, 72, 79, 82, 84, 32, - 79, 215, 32, 66, 4, 156, 246, 2, 2, 83, 72, 201, 237, 10, 7, 84, 87, 79, - 32, 83, 72, 79, 2, 109, 3, 69, 78, 84, 8, 66, 69, 34, 82, 41, 10, 87, 79, - 32, 83, 72, 79, 82, 84, 83, 32, 2, 17, 2, 84, 82, 2, 23, 65, 2, 11, 73, - 2, 245, 139, 16, 2, 83, 69, 4, 26, 79, 163, 254, 7, 74, 2, 129, 190, 10, - 4, 86, 69, 82, 32, 230, 2, 104, 3, 65, 79, 32, 136, 18, 2, 67, 82, 142, - 1, 68, 142, 1, 76, 130, 1, 78, 245, 2, 4, 82, 82, 79, 82, 170, 2, 156, 1, - 7, 76, 69, 84, 84, 69, 82, 32, 180, 10, 5, 83, 73, 71, 78, 32, 212, 1, 5, - 84, 79, 78, 69, 32, 69, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, - 178, 1, 214, 1, 65, 110, 66, 34, 68, 106, 71, 34, 76, 50, 78, 106, 82, - 170, 1, 83, 54, 84, 218, 1, 86, 32, 3, 89, 73, 32, 118, 90, 246, 165, 13, - 81, 254, 231, 1, 80, 246, 193, 1, 72, 2, 77, 138, 69, 70, 2, 75, 2, 87, - 3, 88, 10, 52, 7, 82, 67, 72, 65, 73, 67, 32, 227, 158, 17, 72, 8, 210, - 254, 8, 90, 222, 174, 4, 78, 251, 238, 3, 77, 4, 210, 138, 17, 82, 219, - 19, 65, 16, 50, 90, 154, 4, 76, 138, 151, 17, 68, 187, 2, 65, 8, 254, - 137, 17, 89, 162, 17, 72, 2, 90, 187, 2, 65, 6, 226, 213, 16, 72, 195, - 71, 65, 8, 222, 178, 10, 72, 238, 231, 6, 89, 187, 2, 65, 18, 54, 65, - 222, 212, 16, 71, 2, 78, 2, 89, 139, 69, 72, 5, 11, 83, 2, 149, 218, 13, - 4, 65, 76, 73, 90, 16, 60, 9, 69, 70, 79, 82, 77, 69, 68, 32, 84, 219, - 132, 17, 84, 14, 40, 4, 79, 78, 69, 45, 195, 160, 15, 83, 12, 254, 154, - 17, 49, 2, 50, 2, 52, 2, 53, 2, 54, 3, 56, 8, 234, 134, 17, 89, 162, 17, - 72, 2, 83, 187, 2, 65, 32, 78, 76, 20, 4, 79, 78, 69, 45, 70, 83, 178, - 150, 17, 84, 186, 2, 65, 3, 69, 4, 155, 175, 10, 72, 14, 170, 153, 17, - 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 8, 174, 150, 17, 72, 2, 83, - 186, 2, 65, 3, 69, 4, 254, 149, 17, 70, 187, 2, 65, 16, 70, 84, 144, 157, - 15, 2, 68, 90, 190, 62, 78, 206, 185, 1, 75, 3, 80, 8, 142, 208, 16, 83, - 138, 69, 84, 187, 2, 65, 16, 50, 90, 178, 207, 16, 83, 138, 69, 72, 187, - 2, 65, 8, 202, 172, 10, 83, 238, 231, 6, 89, 187, 2, 65, 8, 144, 1, 9, - 82, 69, 70, 79, 82, 77, 69, 68, 32, 34, 65, 189, 243, 15, 18, 67, 79, 78, - 83, 79, 78, 65, 78, 84, 32, 77, 79, 68, 73, 70, 73, 69, 82, 4, 30, 65, - 213, 88, 2, 86, 79, 2, 133, 139, 12, 3, 83, 80, 73, 8, 218, 246, 14, 66, - 140, 77, 3, 84, 79, 80, 154, 84, 65, 159, 82, 82, 104, 146, 1, 65, 78, - 69, 62, 73, 78, 79, 74, 85, 74, 89, 160, 145, 7, 9, 82, 79, 85, 78, 68, - 69, 68, 32, 69, 178, 185, 7, 87, 202, 74, 78, 251, 124, 86, 19, 250, 151, - 15, 78, 250, 186, 1, 69, 146, 63, 72, 146, 1, 65, 2, 73, 3, 85, 15, 254, - 191, 13, 82, 178, 215, 1, 78, 154, 251, 1, 65, 3, 73, 23, 230, 150, 15, - 65, 54, 79, 158, 250, 1, 78, 86, 69, 2, 71, 2, 73, 3, 85, 13, 42, 69, - 150, 145, 17, 71, 2, 79, 3, 85, 4, 146, 145, 17, 82, 3, 89, 19, 210, 149, - 15, 65, 206, 231, 1, 69, 134, 19, 78, 2, 79, 86, 73, 3, 85, 7, 214, 252, - 16, 85, 219, 19, 73, 12, 18, 32, 63, 79, 4, 196, 252, 15, 4, 79, 78, 32, - 85, 13, 4, 68, 65, 83, 72, 8, 206, 226, 11, 83, 162, 130, 4, 80, 222, 35, - 32, 163, 112, 66, 12, 64, 4, 68, 76, 69, 32, 189, 223, 4, 6, 76, 73, 78, - 69, 32, 72, 10, 184, 192, 5, 3, 84, 72, 73, 238, 217, 8, 76, 22, 82, 207, - 163, 2, 68, 8, 76, 6, 73, 84, 65, 82, 89, 32, 232, 188, 10, 3, 75, 89, - 32, 195, 201, 5, 76, 4, 26, 72, 199, 170, 12, 77, 2, 227, 177, 4, 69, 24, - 42, 73, 76, 2, 85, 83, 131, 140, 17, 89, 6, 34, 68, 22, 77, 155, 134, 15, - 66, 2, 219, 156, 6, 73, 2, 223, 235, 6, 73, 16, 46, 32, 205, 177, 10, 5, - 45, 79, 82, 45, 80, 14, 40, 4, 83, 73, 71, 78, 203, 167, 13, 84, 13, 11, - 32, 10, 44, 5, 87, 73, 84, 72, 32, 135, 173, 6, 73, 8, 66, 67, 250, 202, - 4, 68, 234, 163, 8, 82, 21, 4, 70, 65, 76, 76, 2, 177, 231, 11, 3, 79, - 77, 77, 5, 171, 130, 15, 32, 184, 9, 184, 1, 10, 66, 73, 76, 69, 32, 80, - 72, 79, 78, 69, 166, 1, 68, 190, 76, 78, 170, 27, 79, 172, 1, 3, 83, 81, - 85, 38, 84, 250, 1, 85, 242, 176, 11, 89, 209, 219, 2, 5, 86, 73, 69, 32, - 67, 7, 11, 32, 4, 108, 21, 87, 73, 84, 72, 32, 82, 73, 71, 72, 84, 87, - 65, 82, 68, 83, 32, 65, 82, 82, 79, 87, 167, 178, 1, 79, 2, 11, 32, 2, - 225, 212, 16, 2, 65, 84, 154, 6, 58, 69, 74, 73, 149, 75, 7, 85, 76, 79, - 32, 84, 87, 79, 4, 208, 182, 16, 10, 82, 78, 32, 80, 69, 78, 84, 65, 84, - 72, 159, 18, 76, 148, 6, 42, 32, 221, 1, 5, 70, 73, 69, 82, 32, 158, 1, - 88, 5, 83, 73, 71, 78, 32, 230, 179, 3, 86, 190, 162, 3, 68, 186, 1, 76, - 195, 194, 4, 65, 10, 42, 65, 166, 212, 8, 72, 155, 237, 7, 86, 4, 44, 5, - 82, 68, 72, 65, 67, 175, 191, 16, 78, 2, 213, 191, 16, 3, 65, 78, 68, - 246, 4, 92, 12, 66, 82, 69, 86, 69, 32, 87, 73, 84, 72, 32, 73, 89, 7, - 76, 69, 84, 84, 69, 82, 32, 2, 33, 6, 78, 86, 69, 82, 84, 69, 2, 21, 3, - 68, 32, 66, 2, 129, 134, 16, 2, 82, 69, 244, 4, 198, 1, 65, 82, 66, 66, - 67, 190, 13, 68, 202, 1, 69, 182, 2, 71, 82, 72, 46, 76, 230, 4, 77, 216, - 3, 7, 79, 80, 69, 78, 32, 83, 72, 22, 80, 38, 82, 182, 4, 83, 206, 33, - 84, 114, 85, 118, 86, 63, 89, 6, 38, 76, 158, 27, 67, 251, 204, 10, 80, - 2, 245, 19, 6, 86, 69, 79, 76, 65, 82, 6, 184, 19, 5, 73, 76, 65, 66, 73, - 193, 45, 4, 69, 71, 73, 78, 160, 1, 240, 1, 7, 65, 80, 73, 84, 65, 76, - 32, 192, 2, 7, 69, 78, 84, 82, 69, 68, 32, 100, 13, 72, 73, 78, 69, 83, - 69, 32, 84, 79, 78, 69, 32, 89, 108, 8, 89, 82, 73, 76, 76, 73, 67, 32, - 242, 225, 10, 73, 244, 2, 4, 82, 79, 83, 83, 255, 196, 5, 79, 56, 206, 1, - 66, 42, 82, 130, 27, 72, 202, 141, 13, 79, 138, 147, 3, 65, 162, 64, 67, - 2, 68, 2, 69, 2, 70, 2, 71, 2, 73, 2, 74, 2, 75, 2, 76, 2, 77, 2, 78, 2, - 80, 2, 81, 2, 84, 2, 85, 2, 86, 3, 87, 5, 133, 150, 6, 5, 65, 82, 82, 69, - 68, 7, 41, 8, 69, 86, 69, 82, 83, 69, 68, 32, 4, 194, 251, 16, 69, 3, 78, - 4, 30, 76, 21, 3, 82, 73, 71, 2, 29, 2, 69, 70, 2, 11, 72, 2, 11, 84, 2, - 181, 26, 2, 32, 72, 16, 36, 3, 65, 78, 71, 1, 2, 73, 78, 8, 11, 32, 8, - 182, 134, 12, 83, 214, 162, 4, 80, 158, 44, 81, 3, 82, 78, 34, 72, 30, - 83, 187, 169, 16, 69, 2, 217, 144, 15, 2, 65, 82, 74, 40, 5, 77, 65, 76, - 76, 32, 183, 6, 79, 72, 186, 1, 66, 138, 1, 68, 38, 69, 150, 1, 80, 58, - 83, 94, 84, 34, 89, 158, 243, 15, 90, 130, 64, 73, 150, 19, 67, 2, 71, - 186, 22, 74, 2, 86, 158, 20, 72, 2, 75, 186, 2, 65, 2, 79, 3, 85, 6, 38, - 89, 198, 27, 65, 199, 219, 16, 69, 2, 221, 202, 1, 19, 69, 76, 79, 82, - 85, 83, 83, 73, 65, 78, 45, 85, 75, 82, 65, 73, 78, 73, 65, 4, 242, 213, - 6, 90, 183, 160, 10, 69, 15, 50, 83, 210, 245, 16, 70, 2, 76, 2, 77, 3, - 82, 5, 25, 4, 32, 87, 73, 84, 2, 21, 3, 72, 32, 68, 2, 11, 69, 2, 177, - 141, 13, 3, 83, 67, 69, 4, 26, 65, 215, 244, 16, 69, 2, 137, 200, 16, 2, - 76, 79, 8, 42, 84, 206, 173, 1, 67, 139, 196, 15, 72, 4, 153, 19, 8, 82, - 65, 73, 71, 72, 84, 32, 85, 4, 134, 221, 16, 83, 215, 22, 69, 6, 36, 3, - 69, 82, 85, 151, 243, 16, 85, 5, 37, 7, 32, 87, 73, 84, 72, 32, 66, 2, - 21, 3, 65, 67, 75, 2, 229, 180, 16, 2, 32, 89, 2, 227, 175, 11, 70, 16, - 18, 69, 27, 79, 2, 169, 5, 2, 78, 84, 14, 64, 2, 84, 32, 52, 5, 85, 66, - 76, 69, 32, 129, 52, 2, 87, 78, 6, 242, 218, 9, 83, 142, 192, 4, 86, 171, - 180, 1, 72, 4, 194, 137, 2, 65, 219, 247, 2, 80, 24, 40, 5, 88, 84, 82, - 65, 45, 131, 49, 78, 20, 52, 5, 72, 73, 71, 72, 32, 81, 4, 76, 79, 87, - 32, 10, 156, 1, 9, 69, 88, 84, 82, 65, 45, 76, 79, 87, 154, 7, 68, 70, - 76, 79, 84, 10, 76, 10, 69, 88, 84, 82, 65, 45, 72, 73, 71, 72, 154, 7, - 68, 70, 76, 79, 84, 2, 145, 8, 8, 32, 67, 79, 78, 84, 79, 85, 82, 8, 162, - 9, 82, 226, 3, 76, 237, 191, 15, 9, 69, 79, 82, 71, 73, 65, 78, 32, 78, - 10, 248, 5, 4, 73, 71, 72, 32, 255, 40, 65, 44, 46, 65, 84, 2, 79, 87, - 201, 11, 2, 69, 70, 2, 21, 3, 84, 69, 82, 2, 17, 2, 65, 76, 2, 17, 2, 32, - 67, 2, 191, 188, 15, 76, 36, 86, 32, 249, 139, 12, 15, 69, 82, 32, 82, - 73, 71, 72, 84, 32, 67, 79, 82, 78, 69, 82, 34, 158, 1, 67, 20, 2, 68, - 79, 40, 4, 76, 69, 70, 84, 82, 77, 12, 2, 82, 73, 50, 84, 178, 3, 65, 42, - 71, 170, 2, 73, 184, 141, 14, 2, 85, 80, 179, 187, 1, 86, 2, 199, 210, - 10, 73, 6, 238, 2, 84, 253, 145, 14, 2, 87, 78, 6, 28, 2, 32, 65, 247, 2, - 45, 4, 25, 4, 82, 82, 79, 87, 5, 199, 148, 14, 72, 2, 111, 65, 4, 228, - 147, 14, 3, 71, 72, 84, 223, 212, 2, 78, 4, 194, 2, 79, 243, 204, 14, 73, - 18, 18, 65, 23, 73, 2, 147, 195, 15, 67, 16, 26, 68, 195, 185, 13, 78, - 14, 38, 32, 217, 1, 4, 68, 76, 69, 32, 8, 26, 68, 70, 76, 79, 84, 4, 17, - 2, 79, 84, 4, 25, 4, 84, 69, 68, 32, 4, 18, 76, 79, 84, 2, 25, 4, 69, 70, - 84, 45, 2, 25, 4, 83, 84, 69, 77, 2, 17, 2, 32, 84, 2, 11, 79, 2, 11, 78, - 2, 199, 196, 15, 69, 6, 40, 6, 68, 79, 85, 66, 76, 69, 75, 71, 4, 11, 32, - 4, 18, 65, 43, 71, 2, 11, 67, 2, 169, 208, 10, 2, 85, 84, 2, 11, 82, 2, - 247, 207, 10, 65, 2, 207, 225, 14, 69, 4, 150, 182, 13, 76, 207, 150, 2, - 82, 26, 104, 6, 65, 73, 83, 69, 68, 32, 150, 1, 69, 216, 1, 3, 73, 71, - 72, 133, 245, 8, 5, 72, 79, 84, 73, 67, 10, 70, 68, 30, 73, 162, 213, 9, - 69, 244, 242, 5, 2, 85, 80, 211, 74, 67, 2, 157, 146, 15, 2, 79, 87, 2, - 137, 213, 9, 7, 78, 86, 69, 82, 84, 69, 68, 8, 104, 7, 86, 69, 82, 83, - 69, 68, 32, 137, 28, 14, 84, 82, 79, 70, 76, 69, 88, 32, 67, 76, 73, 67, - 75, 32, 6, 26, 71, 175, 143, 14, 67, 4, 11, 76, 4, 49, 10, 79, 84, 84, - 65, 76, 32, 83, 84, 79, 80, 5, 171, 19, 32, 6, 17, 2, 84, 32, 6, 38, 72, - 166, 197, 13, 84, 235, 69, 65, 2, 133, 191, 7, 3, 65, 76, 70, 156, 2, - 138, 1, 72, 48, 5, 77, 65, 76, 76, 32, 148, 31, 8, 84, 82, 69, 83, 83, - 32, 65, 78, 53, 11, 85, 80, 69, 82, 83, 67, 82, 73, 80, 84, 32, 4, 136, - 153, 8, 3, 79, 82, 84, 203, 194, 6, 69, 144, 2, 154, 2, 65, 50, 66, 130, - 1, 67, 238, 2, 68, 238, 2, 90, 66, 69, 46, 70, 30, 71, 110, 72, 102, 77, - 34, 73, 34, 74, 98, 76, 176, 3, 7, 78, 32, 87, 73, 84, 72, 32, 30, 79, - 94, 80, 22, 82, 146, 2, 83, 190, 1, 84, 246, 7, 85, 142, 1, 86, 158, 192, - 16, 75, 2, 81, 2, 87, 2, 88, 3, 89, 9, 238, 131, 13, 76, 214, 136, 3, 73, - 227, 79, 69, 11, 46, 65, 50, 79, 222, 8, 32, 239, 173, 16, 69, 2, 17, 2, - 82, 82, 2, 193, 246, 5, 2, 69, 68, 2, 229, 20, 4, 84, 84, 79, 77, 41, - 100, 7, 65, 80, 73, 84, 65, 76, 32, 180, 1, 6, 76, 79, 83, 69, 68, 32, - 190, 17, 32, 199, 179, 16, 72, 30, 114, 73, 214, 6, 71, 226, 16, 76, 146, - 171, 16, 79, 158, 20, 65, 186, 2, 66, 2, 72, 2, 78, 2, 82, 2, 85, 3, 89, - 7, 22, 78, 191, 11, 32, 2, 249, 239, 5, 5, 86, 69, 82, 84, 69, 4, 26, 82, - 195, 161, 9, 79, 2, 217, 20, 9, 69, 86, 69, 82, 83, 69, 68, 32, 79, 23, - 104, 6, 32, 87, 73, 84, 72, 32, 86, 69, 32, 9, 79, 84, 76, 69, 83, 83, - 32, 74, 32, 105, 3, 90, 32, 68, 6, 26, 72, 163, 163, 14, 84, 4, 21, 3, - 79, 79, 75, 5, 225, 234, 13, 3, 32, 65, 78, 4, 238, 15, 90, 143, 162, 16, - 76, 4, 33, 6, 87, 73, 84, 72, 32, 83, 4, 29, 5, 84, 82, 79, 75, 69, 5, - 133, 234, 8, 4, 32, 65, 78, 68, 6, 33, 6, 73, 71, 82, 65, 80, 72, 7, 33, - 6, 32, 87, 73, 84, 72, 32, 4, 138, 14, 67, 207, 4, 82, 11, 202, 211, 16, - 83, 2, 84, 2, 90, 63, 78, 5, 225, 13, 3, 69, 78, 71, 11, 56, 5, 82, 69, - 69, 75, 32, 162, 1, 32, 195, 128, 14, 65, 4, 26, 71, 191, 132, 11, 80, 2, - 195, 129, 14, 65, 11, 40, 6, 32, 87, 73, 84, 72, 32, 39, 69, 4, 166, 255, - 14, 72, 219, 163, 1, 83, 4, 17, 2, 78, 71, 5, 11, 32, 2, 195, 230, 8, 87, - 4, 222, 4, 32, 251, 168, 16, 79, 5, 37, 7, 32, 87, 73, 84, 72, 32, 67, 2, - 11, 82, 2, 225, 157, 14, 6, 79, 83, 83, 69, 68, 45, 25, 116, 6, 32, 87, - 73, 84, 72, 32, 226, 9, 83, 2, 90, 112, 2, 69, 90, 253, 174, 16, 8, 73, - 71, 65, 84, 85, 82, 69, 32, 12, 54, 73, 82, 77, 82, 82, 222, 6, 80, 243, - 244, 3, 66, 2, 49, 10, 78, 86, 69, 82, 84, 69, 68, 32, 76, 65, 2, 173, - 218, 12, 2, 90, 89, 2, 11, 73, 2, 17, 2, 68, 68, 2, 17, 2, 76, 69, 2, - 145, 235, 12, 2, 32, 84, 4, 61, 13, 69, 84, 82, 79, 70, 76, 69, 88, 32, - 72, 79, 79, 75, 5, 205, 12, 4, 32, 65, 78, 68, 4, 210, 11, 82, 207, 1, - 76, 9, 18, 32, 47, 80, 2, 21, 3, 87, 73, 84, 2, 151, 157, 16, 72, 4, 193, - 186, 12, 2, 69, 78, 5, 163, 185, 16, 72, 15, 80, 6, 32, 87, 73, 84, 72, - 32, 62, 65, 41, 8, 69, 86, 69, 82, 83, 69, 68, 32, 4, 26, 70, 155, 152, - 14, 84, 2, 165, 224, 8, 3, 73, 83, 72, 2, 17, 2, 77, 83, 2, 131, 232, 5, - 32, 6, 38, 71, 170, 7, 79, 227, 195, 16, 69, 2, 11, 76, 2, 173, 249, 13, - 4, 79, 84, 84, 65, 13, 72, 6, 32, 87, 73, 84, 72, 32, 34, 67, 61, 6, 73, - 68, 69, 87, 65, 89, 4, 158, 3, 67, 155, 243, 14, 72, 4, 26, 82, 203, 216, - 11, 72, 2, 233, 218, 5, 3, 73, 80, 84, 2, 231, 189, 6, 83, 51, 106, 32, - 102, 67, 116, 2, 69, 83, 44, 2, 79, 80, 42, 83, 96, 6, 85, 82, 78, 69, - 68, 32, 231, 139, 13, 72, 4, 29, 5, 87, 73, 84, 72, 32, 4, 22, 80, 219, - 5, 82, 2, 153, 220, 8, 6, 65, 76, 65, 84, 65, 76, 2, 45, 9, 32, 68, 73, - 71, 82, 65, 80, 72, 32, 2, 25, 4, 87, 73, 84, 72, 2, 17, 2, 32, 67, 2, - 179, 145, 3, 85, 2, 11, 72, 2, 217, 150, 10, 3, 32, 68, 73, 2, 189, 225, - 5, 5, 32, 72, 65, 76, 70, 4, 37, 7, 32, 68, 73, 71, 82, 65, 80, 4, 11, - 72, 5, 11, 32, 2, 141, 3, 4, 87, 73, 84, 72, 32, 86, 65, 38, 77, 74, 79, - 42, 82, 214, 1, 89, 230, 193, 16, 72, 2, 73, 2, 86, 3, 87, 7, 150, 236, - 12, 76, 183, 216, 3, 69, 5, 41, 8, 32, 87, 73, 84, 72, 32, 76, 79, 2, - 213, 141, 4, 2, 78, 71, 2, 11, 80, 2, 225, 156, 6, 2, 69, 78, 9, 33, 6, - 32, 87, 73, 84, 72, 32, 6, 26, 76, 167, 239, 14, 72, 4, 37, 7, 79, 78, - 71, 32, 76, 69, 71, 5, 25, 4, 32, 65, 78, 68, 2, 17, 2, 32, 82, 2, 11, - 69, 2, 181, 214, 8, 7, 84, 82, 79, 70, 76, 69, 88, 5, 29, 5, 32, 87, 73, - 84, 72, 2, 185, 238, 3, 2, 32, 66, 9, 18, 32, 95, 80, 4, 40, 4, 87, 73, - 84, 72, 179, 159, 15, 66, 2, 17, 2, 32, 76, 2, 11, 69, 2, 131, 1, 70, 2, - 189, 240, 15, 2, 83, 73, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 26, 82, - 139, 236, 14, 72, 2, 21, 3, 73, 71, 72, 2, 231, 211, 8, 84, 4, 11, 68, 4, - 11, 32, 4, 130, 210, 1, 72, 35, 76, 4, 24, 2, 72, 65, 31, 84, 2, 25, 4, - 76, 70, 32, 84, 2, 43, 82, 4, 30, 82, 53, 3, 85, 82, 78, 2, 249, 237, 15, - 8, 73, 65, 78, 71, 85, 76, 65, 82, 2, 253, 166, 8, 2, 69, 68, 8, 70, 80, - 212, 154, 10, 7, 78, 65, 83, 80, 73, 82, 65, 175, 162, 6, 83, 4, 11, 32, - 4, 242, 161, 13, 84, 235, 69, 65, 4, 26, 79, 199, 162, 15, 69, 2, 11, 73, - 2, 167, 235, 15, 67, 4, 36, 3, 65, 78, 71, 1, 2, 73, 78, 2, 25, 4, 32, - 68, 69, 80, 2, 21, 3, 65, 82, 84, 2, 21, 3, 73, 78, 71, 2, 17, 2, 32, 84, - 2, 11, 79, 2, 143, 134, 6, 78, 2, 11, 32, 2, 211, 145, 15, 83, 234, 2, - 92, 2, 69, 89, 88, 7, 71, 79, 76, 73, 65, 78, 32, 206, 24, 79, 177, 130, - 12, 3, 75, 69, 89, 6, 26, 32, 135, 252, 15, 45, 4, 152, 131, 12, 6, 87, - 73, 84, 72, 32, 87, 179, 205, 2, 66, 214, 2, 194, 2, 68, 46, 70, 148, 1, - 14, 73, 78, 86, 69, 82, 84, 69, 68, 32, 66, 73, 82, 71, 65, 16, 7, 76, - 69, 84, 84, 69, 82, 32, 138, 16, 83, 132, 1, 7, 82, 79, 84, 65, 84, 69, - 68, 22, 66, 98, 84, 160, 2, 4, 86, 79, 87, 69, 154, 241, 3, 67, 164, 1, - 6, 77, 65, 78, 67, 72, 85, 216, 190, 7, 4, 78, 73, 82, 85, 239, 203, 2, - 69, 22, 232, 20, 3, 79, 85, 66, 207, 176, 14, 73, 12, 112, 19, 82, 69, - 69, 32, 86, 65, 82, 73, 65, 84, 73, 79, 78, 32, 83, 69, 76, 69, 67, 214, - 227, 13, 85, 199, 46, 79, 8, 249, 244, 9, 3, 84, 79, 82, 5, 227, 19, 32, - 136, 2, 130, 2, 65, 244, 4, 2, 67, 72, 88, 2, 77, 65, 174, 2, 83, 246, 1, - 84, 234, 3, 90, 214, 178, 14, 72, 142, 171, 1, 75, 2, 76, 162, 7, 69, 2, - 79, 2, 85, 234, 61, 66, 2, 68, 2, 70, 2, 71, 2, 74, 2, 78, 2, 80, 2, 81, - 2, 82, 2, 87, 2, 89, 187, 2, 73, 59, 56, 8, 76, 73, 32, 71, 65, 76, 73, - 32, 231, 177, 16, 78, 54, 174, 1, 65, 52, 6, 86, 73, 83, 65, 82, 71, 22, - 68, 76, 5, 72, 65, 76, 70, 32, 34, 73, 50, 85, 34, 78, 30, 84, 66, 66, - 234, 230, 15, 80, 2, 90, 254, 68, 83, 14, 67, 3, 75, 7, 48, 6, 78, 85, - 83, 86, 65, 82, 215, 176, 16, 72, 2, 195, 160, 9, 65, 8, 26, 65, 239, - 173, 16, 68, 7, 246, 247, 8, 77, 233, 245, 6, 3, 71, 65, 76, 4, 186, 173, - 16, 89, 187, 2, 85, 5, 45, 9, 78, 86, 69, 82, 84, 69, 68, 32, 85, 2, 181, - 236, 15, 3, 66, 65, 68, 4, 202, 172, 16, 71, 3, 78, 8, 60, 6, 72, 82, 69, - 69, 32, 66, 234, 230, 15, 84, 195, 71, 65, 2, 17, 2, 65, 76, 2, 143, 137, - 16, 85, 6, 26, 65, 231, 173, 16, 73, 5, 29, 5, 32, 87, 73, 84, 72, 2, - 237, 220, 14, 2, 32, 84, 41, 29, 5, 78, 67, 72, 85, 32, 38, 104, 9, 65, - 76, 73, 32, 71, 65, 76, 73, 32, 222, 177, 14, 90, 138, 248, 1, 70, 2, 75, - 2, 82, 187, 2, 73, 28, 122, 68, 254, 192, 9, 67, 226, 222, 2, 84, 134, - 145, 2, 66, 2, 71, 2, 74, 2, 76, 130, 179, 1, 90, 254, 4, 78, 131, 64, - 83, 4, 222, 176, 14, 68, 139, 248, 1, 72, 48, 52, 4, 73, 66, 69, 32, 142, - 168, 16, 72, 187, 2, 65, 44, 242, 221, 5, 71, 2, 72, 158, 173, 6, 73, - 154, 19, 84, 222, 145, 2, 67, 2, 83, 246, 7, 82, 214, 161, 1, 65, 186, 9, - 90, 162, 7, 85, 234, 61, 68, 2, 70, 2, 74, 2, 75, 2, 80, 187, 2, 69, 60, - 52, 4, 79, 68, 79, 32, 154, 166, 16, 83, 187, 2, 65, 56, 250, 1, 65, 98, - 68, 34, 74, 34, 78, 236, 164, 1, 8, 76, 79, 78, 71, 32, 86, 79, 87, 250, - 179, 4, 71, 182, 192, 6, 84, 222, 145, 2, 67, 246, 7, 72, 174, 178, 1, - 79, 2, 85, 234, 61, 66, 2, 75, 2, 77, 2, 80, 2, 81, 2, 87, 2, 89, 186, 2, - 69, 3, 73, 6, 56, 8, 76, 73, 32, 71, 65, 76, 73, 32, 199, 165, 16, 78, 4, - 214, 171, 14, 90, 139, 248, 1, 84, 4, 186, 163, 16, 90, 187, 2, 65, 4, - 154, 163, 16, 73, 187, 2, 65, 2, 251, 162, 16, 73, 6, 198, 145, 16, 72, - 162, 17, 82, 187, 2, 65, 8, 128, 1, 4, 87, 73, 82, 76, 189, 190, 4, 21, - 73, 66, 69, 32, 83, 89, 76, 76, 65, 66, 76, 69, 32, 66, 79, 85, 78, 68, - 65, 82, 89, 6, 17, 2, 32, 66, 6, 25, 4, 73, 82, 71, 65, 7, 33, 6, 32, 87, - 73, 84, 72, 32, 4, 150, 2, 68, 187, 221, 15, 79, 6, 152, 1, 3, 82, 73, - 80, 56, 18, 85, 82, 78, 69, 68, 32, 83, 87, 73, 82, 76, 32, 66, 73, 82, - 71, 65, 32, 197, 192, 1, 8, 79, 68, 79, 32, 83, 79, 70, 84, 2, 225, 221, - 15, 9, 76, 69, 32, 66, 73, 82, 71, 65, 32, 2, 33, 6, 87, 73, 84, 72, 32, - 68, 2, 161, 221, 15, 5, 79, 85, 66, 76, 69, 2, 207, 229, 9, 76, 10, 96, - 9, 71, 82, 65, 77, 32, 70, 79, 82, 32, 208, 131, 4, 5, 83, 84, 65, 66, - 76, 183, 232, 9, 82, 6, 26, 89, 219, 182, 12, 69, 4, 206, 206, 15, 65, - 155, 1, 73, 10, 48, 2, 78, 32, 230, 209, 4, 68, 191, 182, 11, 83, 6, 88, - 12, 86, 73, 69, 87, 73, 78, 71, 32, 67, 69, 82, 69, 234, 181, 12, 67, 85, - 2, 76, 65, 2, 189, 254, 15, 2, 77, 79, 4, 190, 175, 12, 73, 191, 238, 3, - 69, 10, 26, 72, 69, 2, 79, 82, 2, 45, 9, 69, 82, 32, 67, 72, 82, 73, 83, - 84, 2, 255, 254, 2, 77, 8, 50, 32, 52, 4, 73, 90, 69, 68, 143, 144, 15, - 87, 4, 226, 196, 8, 66, 233, 159, 6, 4, 83, 67, 79, 79, 2, 189, 176, 3, - 7, 32, 87, 72, 69, 69, 76, 67, 18, 52, 2, 78, 84, 152, 1, 2, 83, 69, 131, - 153, 16, 84, 10, 48, 3, 65, 73, 78, 141, 132, 12, 3, 32, 70, 85, 9, 11, - 32, 6, 142, 202, 9, 82, 24, 5, 67, 65, 66, 76, 69, 217, 235, 1, 6, 66, - 73, 67, 89, 67, 76, 7, 11, 32, 4, 148, 137, 15, 2, 84, 82, 231, 83, 70, - 86, 52, 7, 76, 69, 84, 84, 69, 82, 32, 211, 234, 5, 68, 62, 198, 1, 75, - 62, 77, 34, 78, 34, 79, 30, 80, 34, 84, 234, 105, 68, 180, 128, 8, 2, 72, - 65, 2, 82, 134, 192, 1, 69, 134, 29, 83, 154, 89, 76, 246, 7, 67, 130, - 207, 4, 89, 190, 28, 66, 2, 87, 187, 2, 65, 6, 192, 173, 5, 2, 69, 65, - 170, 255, 5, 72, 243, 234, 4, 79, 4, 242, 132, 16, 65, 215, 1, 73, 4, - 158, 199, 15, 73, 139, 60, 71, 7, 222, 150, 16, 76, 3, 79, 4, 234, 130, - 16, 72, 219, 19, 65, 6, 178, 144, 9, 72, 218, 130, 7, 69, 155, 3, 65, - 206, 4, 44, 2, 76, 84, 234, 6, 83, 151, 252, 13, 67, 102, 36, 4, 65, 78, - 73, 32, 219, 2, 73, 76, 52, 7, 76, 69, 84, 84, 69, 82, 32, 147, 148, 4, - 83, 74, 206, 1, 68, 222, 83, 78, 166, 236, 1, 82, 214, 198, 9, 74, 178, - 51, 84, 206, 145, 3, 66, 2, 67, 2, 71, 2, 75, 2, 80, 138, 69, 72, 2, 76, + 38, 84, 146, 185, 15, 72, 234, 181, 1, 75, 138, 69, 68, 2, 76, 2, 77, 2, + 80, 2, 81, 2, 87, 2, 89, 186, 2, 65, 2, 69, 2, 73, 3, 79, 4, 203, 161, 3, + 65, 8, 182, 161, 3, 65, 3, 69, 6, 154, 161, 3, 65, 195, 149, 14, 69, 10, + 246, 160, 3, 65, 2, 69, 195, 149, 14, 79, 2, 167, 167, 12, 73, 22, 26, + 82, 131, 248, 16, 73, 20, 44, 5, 73, 67, 65, 76, 32, 175, 181, 17, 79, + 18, 116, 10, 76, 79, 78, 71, 32, 79, 86, 69, 82, 32, 74, 80, 26, 84, 168, + 1, 7, 83, 72, 79, 82, 84, 32, 79, 215, 32, 66, 4, 232, 241, 2, 2, 83, 72, + 169, 253, 10, 7, 84, 87, 79, 32, 83, 72, 79, 2, 109, 3, 69, 78, 84, 8, + 66, 69, 34, 82, 41, 10, 87, 79, 32, 83, 72, 79, 82, 84, 83, 32, 2, 17, 2, + 84, 82, 2, 23, 65, 2, 11, 73, 2, 241, 152, 16, 2, 83, 69, 4, 26, 79, 159, + 255, 7, 74, 2, 129, 191, 10, 4, 86, 69, 82, 32, 230, 2, 104, 3, 65, 79, + 32, 136, 18, 2, 67, 82, 142, 1, 68, 142, 1, 76, 130, 1, 78, 245, 2, 4, + 82, 82, 79, 82, 170, 2, 156, 1, 7, 76, 69, 84, 84, 69, 82, 32, 180, 10, + 5, 83, 73, 71, 78, 32, 212, 1, 5, 84, 79, 78, 69, 32, 69, 11, 86, 79, 87, + 69, 76, 32, 83, 73, 71, 78, 32, 178, 1, 214, 1, 65, 110, 66, 34, 68, 106, + 71, 34, 76, 50, 78, 106, 82, 170, 1, 83, 54, 84, 218, 1, 86, 32, 3, 89, + 73, 32, 118, 90, 162, 176, 13, 81, 234, 233, 1, 80, 222, 196, 1, 72, 2, + 77, 138, 69, 70, 2, 75, 2, 87, 3, 88, 10, 52, 7, 82, 67, 72, 65, 73, 67, + 32, 227, 173, 17, 72, 8, 186, 255, 8, 90, 162, 184, 4, 78, 207, 243, 3, + 77, 4, 210, 153, 17, 82, 219, 19, 65, 16, 50, 90, 154, 4, 76, 138, 166, + 17, 68, 187, 2, 65, 8, 254, 152, 17, 89, 162, 17, 72, 2, 90, 187, 2, 65, + 6, 226, 228, 16, 72, 195, 71, 65, 8, 222, 179, 10, 72, 238, 245, 6, 89, + 187, 2, 65, 18, 54, 65, 222, 227, 16, 71, 2, 78, 2, 89, 139, 69, 72, 5, + 11, 83, 2, 193, 229, 13, 4, 65, 76, 73, 90, 16, 60, 9, 69, 70, 79, 82, + 77, 69, 68, 32, 84, 219, 147, 17, 84, 14, 40, 4, 79, 78, 69, 45, 219, + 172, 15, 83, 12, 254, 169, 17, 49, 2, 50, 2, 52, 2, 53, 2, 54, 3, 56, 8, + 234, 149, 17, 89, 162, 17, 72, 2, 83, 187, 2, 65, 32, 78, 76, 20, 4, 79, + 78, 69, 45, 70, 83, 178, 165, 17, 84, 186, 2, 65, 3, 69, 4, 155, 176, 10, + 72, 14, 170, 168, 17, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 8, + 174, 165, 17, 72, 2, 83, 186, 2, 65, 3, 69, 4, 254, 164, 17, 70, 187, 2, + 65, 16, 70, 84, 168, 169, 15, 2, 68, 90, 146, 63, 78, 226, 187, 1, 75, 3, + 80, 8, 142, 223, 16, 83, 138, 69, 84, 187, 2, 65, 16, 50, 90, 178, 222, + 16, 83, 138, 69, 72, 187, 2, 65, 8, 202, 173, 10, 83, 238, 245, 6, 89, + 187, 2, 65, 8, 144, 1, 9, 82, 69, 70, 79, 82, 77, 69, 68, 32, 34, 65, + 185, 128, 16, 18, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 77, 79, 68, 73, + 70, 73, 69, 82, 4, 30, 65, 221, 88, 2, 86, 79, 2, 185, 145, 12, 3, 83, + 80, 73, 8, 254, 129, 15, 66, 204, 78, 3, 84, 79, 80, 182, 86, 65, 159, + 82, 82, 104, 146, 1, 65, 78, 69, 62, 73, 78, 79, 74, 85, 74, 89, 148, + 145, 7, 9, 82, 79, 85, 78, 68, 69, 68, 32, 69, 238, 196, 7, 87, 178, 75, + 78, 227, 127, 86, 19, 146, 164, 15, 78, 226, 189, 1, 69, 146, 63, 72, + 146, 1, 65, 2, 73, 3, 85, 15, 170, 203, 13, 82, 158, 216, 1, 78, 130, + 254, 1, 65, 3, 73, 23, 254, 162, 15, 65, 54, 79, 134, 253, 1, 78, 86, 69, + 2, 71, 2, 73, 3, 85, 13, 42, 69, 150, 160, 17, 71, 2, 79, 3, 85, 4, 146, + 160, 17, 82, 3, 89, 19, 234, 161, 15, 65, 182, 234, 1, 69, 134, 19, 78, + 2, 79, 86, 73, 3, 85, 7, 214, 139, 17, 85, 219, 19, 73, 12, 18, 32, 63, + 79, 4, 168, 137, 16, 4, 79, 78, 32, 85, 13, 4, 68, 65, 83, 72, 8, 194, + 232, 11, 83, 154, 137, 4, 80, 242, 37, 32, 163, 112, 66, 12, 64, 4, 68, + 76, 69, 32, 161, 219, 4, 6, 76, 73, 78, 69, 32, 72, 10, 144, 189, 5, 3, + 84, 72, 73, 198, 232, 8, 76, 22, 82, 159, 167, 2, 68, 8, 76, 6, 73, 84, + 65, 82, 89, 32, 216, 189, 10, 3, 75, 89, 32, 211, 215, 5, 76, 4, 26, 72, + 143, 177, 12, 77, 2, 187, 173, 4, 69, 24, 42, 73, 76, 2, 85, 83, 131, + 155, 17, 89, 6, 34, 68, 22, 77, 179, 146, 15, 66, 2, 255, 152, 6, 73, 2, + 155, 233, 6, 73, 16, 46, 32, 205, 178, 10, 5, 45, 79, 82, 45, 80, 14, 40, + 4, 83, 73, 71, 78, 211, 178, 13, 84, 13, 11, 32, 10, 44, 5, 87, 73, 84, + 72, 32, 171, 169, 6, 73, 8, 66, 67, 218, 198, 4, 68, 230, 173, 8, 82, 21, + 4, 70, 65, 76, 76, 2, 229, 237, 11, 3, 79, 77, 77, 5, 195, 142, 15, 32, + 186, 9, 184, 1, 10, 66, 73, 76, 69, 32, 80, 72, 79, 78, 69, 166, 1, 68, + 198, 76, 78, 178, 27, 79, 172, 1, 3, 83, 81, 85, 38, 84, 250, 1, 85, 142, + 183, 11, 89, 189, 225, 2, 5, 86, 73, 69, 32, 67, 7, 11, 32, 4, 108, 21, + 87, 73, 84, 72, 32, 82, 73, 71, 72, 84, 87, 65, 82, 68, 83, 32, 65, 82, + 82, 79, 87, 195, 178, 1, 79, 2, 11, 32, 2, 225, 227, 16, 2, 65, 84, 156, + 6, 58, 69, 74, 73, 157, 75, 7, 85, 76, 79, 32, 84, 87, 79, 4, 208, 197, + 16, 10, 82, 78, 32, 80, 69, 78, 84, 65, 84, 72, 159, 18, 76, 150, 6, 42, + 32, 221, 1, 5, 70, 73, 69, 82, 32, 158, 1, 88, 5, 83, 73, 71, 78, 32, + 210, 175, 3, 86, 170, 163, 3, 68, 186, 1, 76, 243, 203, 4, 65, 10, 42, + 65, 142, 213, 8, 72, 179, 251, 7, 86, 4, 44, 5, 82, 68, 72, 65, 67, 175, + 206, 16, 78, 2, 213, 206, 16, 3, 65, 78, 68, 248, 4, 92, 12, 66, 82, 69, + 86, 69, 32, 87, 73, 84, 72, 32, 73, 89, 7, 76, 69, 84, 84, 69, 82, 32, 2, + 33, 6, 78, 86, 69, 82, 84, 69, 2, 21, 3, 68, 32, 66, 2, 129, 149, 16, 2, + 82, 69, 246, 4, 198, 1, 65, 82, 66, 66, 67, 198, 13, 68, 202, 1, 69, 182, + 2, 71, 82, 72, 46, 76, 230, 4, 77, 216, 3, 7, 79, 80, 69, 78, 32, 83, 72, + 22, 80, 38, 82, 182, 4, 83, 206, 33, 84, 114, 85, 118, 86, 63, 89, 6, 38, + 76, 166, 27, 67, 151, 205, 10, 80, 2, 253, 19, 6, 86, 69, 79, 76, 65, 82, + 6, 192, 19, 5, 73, 76, 65, 66, 73, 193, 45, 4, 69, 71, 73, 78, 162, 1, + 240, 1, 7, 65, 80, 73, 84, 65, 76, 32, 200, 2, 7, 69, 78, 84, 82, 69, 68, + 32, 100, 13, 72, 73, 78, 69, 83, 69, 32, 84, 79, 78, 69, 32, 89, 108, 8, + 89, 82, 73, 76, 76, 73, 67, 32, 142, 226, 10, 73, 244, 2, 4, 82, 79, 83, + 83, 219, 211, 5, 79, 58, 214, 1, 66, 42, 82, 130, 27, 72, 238, 152, 13, + 79, 222, 150, 3, 65, 162, 64, 67, 2, 68, 2, 69, 2, 70, 2, 71, 2, 73, 2, + 74, 2, 75, 2, 76, 2, 77, 2, 78, 2, 80, 2, 81, 2, 83, 2, 84, 2, 85, 2, 86, + 3, 87, 5, 161, 146, 6, 5, 65, 82, 82, 69, 68, 7, 41, 8, 69, 86, 69, 82, + 83, 69, 68, 32, 4, 186, 138, 17, 69, 3, 78, 4, 30, 76, 21, 3, 82, 73, 71, + 2, 29, 2, 69, 70, 2, 11, 72, 2, 11, 84, 2, 181, 26, 2, 32, 72, 16, 36, 3, + 65, 78, 71, 1, 2, 73, 78, 8, 11, 32, 8, 218, 140, 12, 83, 170, 171, 4, + 80, 158, 44, 81, 3, 82, 78, 34, 72, 30, 83, 179, 184, 16, 69, 2, 233, + 156, 15, 2, 65, 82, 74, 40, 5, 77, 65, 76, 76, 32, 183, 6, 79, 72, 186, + 1, 66, 138, 1, 68, 38, 69, 150, 1, 80, 58, 83, 94, 84, 34, 89, 150, 130, + 16, 90, 130, 64, 73, 150, 19, 67, 2, 71, 186, 22, 74, 2, 86, 158, 20, 72, + 2, 75, 186, 2, 65, 2, 79, 3, 85, 6, 38, 89, 198, 27, 65, 191, 234, 16, + 69, 2, 237, 202, 1, 19, 69, 76, 79, 82, 85, 83, 83, 73, 65, 78, 45, 85, + 75, 82, 65, 73, 78, 73, 65, 4, 166, 211, 6, 90, 251, 177, 10, 69, 15, 50, + 83, 202, 132, 17, 70, 2, 76, 2, 77, 3, 82, 5, 25, 4, 32, 87, 73, 84, 2, + 21, 3, 72, 32, 68, 2, 11, 69, 2, 177, 152, 13, 3, 83, 67, 69, 4, 26, 65, + 207, 131, 17, 69, 2, 129, 215, 16, 2, 76, 79, 8, 42, 84, 226, 173, 1, 67, + 239, 210, 15, 72, 4, 153, 19, 8, 82, 65, 73, 71, 72, 84, 32, 85, 4, 254, + 235, 16, 83, 215, 22, 69, 6, 36, 3, 69, 82, 85, 143, 130, 17, 85, 5, 37, + 7, 32, 87, 73, 84, 72, 32, 66, 2, 21, 3, 65, 67, 75, 2, 221, 195, 16, 2, + 32, 89, 2, 227, 181, 11, 70, 16, 18, 69, 27, 79, 2, 169, 5, 2, 78, 84, + 14, 64, 2, 84, 32, 52, 5, 85, 66, 76, 69, 32, 129, 52, 2, 87, 78, 6, 214, + 219, 9, 83, 210, 202, 4, 86, 247, 181, 1, 72, 4, 210, 137, 2, 65, 155, + 244, 2, 80, 24, 40, 5, 88, 84, 82, 65, 45, 131, 49, 78, 20, 52, 5, 72, + 73, 71, 72, 32, 81, 4, 76, 79, 87, 32, 10, 156, 1, 9, 69, 88, 84, 82, 65, + 45, 76, 79, 87, 154, 7, 68, 70, 76, 79, 84, 10, 76, 10, 69, 88, 84, 82, + 65, 45, 72, 73, 71, 72, 154, 7, 68, 70, 76, 79, 84, 2, 145, 8, 8, 32, 67, + 79, 78, 84, 79, 85, 82, 8, 162, 9, 82, 226, 3, 76, 225, 204, 15, 9, 69, + 79, 82, 71, 73, 65, 78, 32, 78, 10, 248, 5, 4, 73, 71, 72, 32, 255, 40, + 65, 44, 46, 65, 84, 2, 79, 87, 201, 11, 2, 69, 70, 2, 21, 3, 84, 69, 82, + 2, 17, 2, 65, 76, 2, 17, 2, 32, 67, 2, 163, 201, 15, 76, 36, 86, 32, 185, + 146, 12, 15, 69, 82, 32, 82, 73, 71, 72, 84, 32, 67, 79, 82, 78, 69, 82, + 34, 158, 1, 67, 20, 2, 68, 79, 40, 4, 76, 69, 70, 84, 82, 77, 12, 2, 82, + 73, 50, 84, 178, 3, 65, 42, 71, 170, 2, 73, 224, 152, 14, 2, 85, 80, 255, + 188, 1, 86, 2, 227, 210, 10, 73, 6, 238, 2, 84, 165, 157, 14, 2, 87, 78, + 6, 28, 2, 32, 65, 247, 2, 45, 4, 25, 4, 82, 82, 79, 87, 5, 239, 159, 14, + 72, 2, 111, 65, 4, 140, 159, 14, 3, 71, 72, 84, 175, 216, 2, 78, 4, 194, + 2, 79, 143, 216, 14, 73, 18, 18, 65, 23, 73, 2, 247, 207, 15, 67, 16, 26, + 68, 231, 196, 13, 78, 14, 38, 32, 217, 1, 4, 68, 76, 69, 32, 8, 26, 68, + 70, 76, 79, 84, 4, 17, 2, 79, 84, 4, 25, 4, 84, 69, 68, 32, 4, 18, 76, + 79, 84, 2, 25, 4, 69, 70, 84, 45, 2, 25, 4, 83, 84, 69, 77, 2, 17, 2, 32, + 84, 2, 11, 79, 2, 11, 78, 2, 187, 209, 15, 69, 6, 40, 6, 68, 79, 85, 66, + 76, 69, 75, 71, 4, 11, 32, 4, 18, 65, 43, 71, 2, 11, 67, 2, 197, 208, 10, + 2, 85, 84, 2, 11, 82, 2, 147, 208, 10, 65, 2, 223, 237, 14, 69, 4, 186, + 193, 13, 76, 159, 152, 2, 82, 26, 104, 6, 65, 73, 83, 69, 68, 32, 150, 1, + 69, 216, 1, 3, 73, 71, 72, 245, 245, 8, 5, 72, 79, 84, 73, 67, 10, 70, + 68, 30, 73, 138, 214, 9, 69, 128, 255, 5, 2, 85, 80, 215, 76, 67, 2, 249, + 158, 15, 2, 79, 87, 2, 241, 213, 9, 7, 78, 86, 69, 82, 84, 69, 68, 8, + 104, 7, 86, 69, 82, 83, 69, 68, 32, 137, 28, 14, 84, 82, 79, 70, 76, 69, + 88, 32, 67, 76, 73, 67, 75, 32, 6, 26, 71, 215, 154, 14, 67, 4, 11, 76, + 4, 49, 10, 79, 84, 84, 65, 76, 32, 83, 84, 79, 80, 5, 171, 19, 32, 6, 17, + 2, 84, 32, 6, 38, 72, 202, 208, 13, 84, 239, 69, 65, 2, 249, 191, 7, 3, + 65, 76, 70, 156, 2, 138, 1, 72, 48, 5, 77, 65, 76, 76, 32, 148, 31, 8, + 84, 82, 69, 83, 83, 32, 65, 78, 53, 11, 85, 80, 69, 82, 83, 67, 82, 73, + 80, 84, 32, 4, 232, 153, 8, 3, 79, 82, 84, 251, 205, 6, 69, 144, 2, 154, + 2, 65, 50, 66, 130, 1, 67, 238, 2, 68, 238, 2, 90, 66, 69, 46, 70, 30, + 71, 110, 72, 102, 77, 34, 73, 34, 74, 98, 76, 176, 3, 7, 78, 32, 87, 73, + 84, 72, 32, 30, 79, 94, 80, 22, 82, 146, 2, 83, 190, 1, 84, 246, 7, 85, + 142, 1, 86, 150, 207, 16, 75, 2, 81, 2, 87, 2, 88, 3, 89, 9, 146, 143, + 13, 76, 170, 140, 3, 73, 227, 79, 69, 11, 46, 65, 50, 79, 222, 8, 32, + 231, 188, 16, 69, 2, 17, 2, 82, 82, 2, 221, 242, 5, 2, 69, 68, 2, 229, + 20, 4, 84, 84, 79, 77, 41, 100, 7, 65, 80, 73, 84, 65, 76, 32, 180, 1, 6, + 76, 79, 83, 69, 68, 32, 190, 17, 32, 191, 194, 16, 72, 30, 114, 73, 214, + 6, 71, 226, 16, 76, 138, 186, 16, 79, 158, 20, 65, 186, 2, 66, 2, 72, 2, + 78, 2, 82, 2, 85, 3, 89, 7, 22, 78, 191, 11, 32, 2, 149, 236, 5, 5, 86, + 69, 82, 84, 69, 4, 26, 82, 167, 162, 9, 79, 2, 217, 20, 9, 69, 86, 69, + 82, 83, 69, 68, 32, 79, 23, 104, 6, 32, 87, 73, 84, 72, 32, 86, 69, 32, + 9, 79, 84, 76, 69, 83, 83, 32, 74, 32, 105, 3, 90, 32, 68, 6, 26, 72, + 215, 174, 14, 84, 4, 21, 3, 79, 79, 75, 5, 137, 246, 13, 3, 32, 65, 78, + 4, 238, 15, 90, 135, 177, 16, 76, 4, 33, 6, 87, 73, 84, 72, 32, 83, 4, + 29, 5, 84, 82, 79, 75, 69, 5, 245, 234, 8, 4, 32, 65, 78, 68, 6, 33, 6, + 73, 71, 82, 65, 80, 72, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 138, 14, 67, + 207, 4, 82, 11, 194, 226, 16, 83, 2, 84, 2, 90, 63, 78, 5, 225, 13, 3, + 69, 78, 71, 11, 56, 5, 82, 69, 69, 75, 32, 162, 1, 32, 235, 139, 14, 65, + 4, 26, 71, 191, 138, 11, 80, 2, 235, 140, 14, 65, 11, 40, 6, 32, 87, 73, + 84, 72, 32, 39, 69, 4, 130, 140, 15, 72, 247, 165, 1, 83, 4, 17, 2, 78, + 71, 5, 11, 32, 2, 179, 231, 8, 87, 4, 222, 4, 32, 243, 183, 16, 79, 5, + 37, 7, 32, 87, 73, 84, 72, 32, 67, 2, 11, 82, 2, 149, 169, 14, 6, 79, 83, + 83, 69, 68, 45, 25, 116, 6, 32, 87, 73, 84, 72, 32, 226, 9, 83, 2, 90, + 112, 2, 69, 90, 245, 189, 16, 8, 73, 71, 65, 84, 85, 82, 69, 32, 12, 54, + 73, 82, 77, 82, 82, 222, 6, 80, 195, 240, 3, 66, 2, 49, 10, 78, 86, 69, + 82, 84, 69, 68, 32, 76, 65, 2, 209, 228, 12, 2, 90, 89, 2, 11, 73, 2, 17, + 2, 68, 68, 2, 17, 2, 76, 69, 2, 145, 246, 12, 2, 32, 84, 4, 61, 13, 69, + 84, 82, 79, 70, 76, 69, 88, 32, 72, 79, 79, 75, 5, 205, 12, 4, 32, 65, + 78, 68, 4, 210, 11, 82, 207, 1, 76, 9, 18, 32, 47, 80, 2, 21, 3, 87, 73, + 84, 2, 143, 172, 16, 72, 4, 153, 192, 12, 2, 69, 78, 5, 155, 200, 16, 72, + 15, 80, 6, 32, 87, 73, 84, 72, 32, 62, 65, 41, 8, 69, 86, 69, 82, 83, 69, + 68, 32, 4, 26, 70, 207, 163, 14, 84, 2, 149, 225, 8, 3, 73, 83, 72, 2, + 17, 2, 77, 83, 2, 159, 228, 5, 32, 6, 38, 71, 170, 7, 79, 219, 210, 16, + 69, 2, 11, 76, 2, 213, 132, 14, 4, 79, 84, 84, 65, 13, 72, 6, 32, 87, 73, + 84, 72, 32, 34, 67, 61, 6, 73, 68, 69, 87, 65, 89, 4, 158, 3, 67, 247, + 255, 14, 72, 4, 26, 82, 239, 222, 11, 72, 2, 133, 215, 5, 3, 73, 80, 84, + 2, 211, 189, 6, 83, 51, 106, 32, 102, 67, 116, 2, 69, 83, 44, 2, 79, 80, + 42, 83, 96, 6, 85, 82, 78, 69, 68, 32, 139, 151, 13, 72, 4, 29, 5, 87, + 73, 84, 72, 32, 4, 22, 80, 219, 5, 82, 2, 137, 221, 8, 6, 65, 76, 65, 84, + 65, 76, 2, 45, 9, 32, 68, 73, 71, 82, 65, 80, 72, 32, 2, 25, 4, 87, 73, + 84, 72, 2, 17, 2, 32, 67, 2, 227, 140, 3, 85, 2, 11, 72, 2, 241, 150, 10, + 3, 32, 68, 73, 2, 217, 221, 5, 5, 32, 72, 65, 76, 70, 4, 37, 7, 32, 68, + 73, 71, 82, 65, 80, 4, 11, 72, 5, 11, 32, 2, 141, 3, 4, 87, 73, 84, 72, + 32, 86, 65, 38, 77, 74, 79, 42, 82, 214, 1, 89, 222, 208, 16, 72, 2, 73, + 2, 86, 3, 87, 7, 186, 247, 12, 76, 139, 220, 3, 69, 5, 41, 8, 32, 87, 73, + 84, 72, 32, 76, 79, 2, 177, 137, 4, 2, 78, 71, 2, 11, 80, 2, 149, 154, 6, + 2, 69, 78, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 26, 76, 131, 252, 14, 72, + 4, 37, 7, 79, 78, 71, 32, 76, 69, 71, 5, 25, 4, 32, 65, 78, 68, 2, 17, 2, + 32, 82, 2, 11, 69, 2, 165, 215, 8, 7, 84, 82, 79, 70, 76, 69, 88, 5, 29, + 5, 32, 87, 73, 84, 72, 2, 137, 234, 3, 2, 32, 66, 9, 18, 32, 95, 80, 4, + 40, 4, 87, 73, 84, 72, 167, 172, 15, 66, 2, 17, 2, 32, 76, 2, 11, 69, 2, + 131, 1, 70, 2, 181, 255, 15, 2, 83, 73, 7, 33, 6, 32, 87, 73, 84, 72, 32, + 4, 26, 82, 231, 248, 14, 72, 2, 21, 3, 73, 71, 72, 2, 215, 212, 8, 84, 4, + 11, 68, 4, 11, 32, 4, 146, 210, 1, 72, 35, 76, 4, 24, 2, 72, 65, 31, 84, + 2, 25, 4, 76, 70, 32, 84, 2, 43, 82, 4, 30, 82, 53, 3, 85, 82, 78, 2, + 241, 252, 15, 8, 73, 65, 78, 71, 85, 76, 65, 82, 2, 221, 167, 8, 2, 69, + 68, 8, 70, 80, 236, 154, 10, 7, 78, 65, 83, 80, 73, 82, 65, 143, 177, 6, + 83, 4, 11, 32, 4, 150, 173, 13, 84, 239, 69, 65, 4, 26, 79, 187, 175, 15, + 69, 2, 11, 73, 2, 159, 250, 15, 67, 4, 36, 3, 65, 78, 71, 1, 2, 73, 78, + 2, 25, 4, 32, 68, 69, 80, 2, 21, 3, 65, 82, 84, 2, 21, 3, 73, 78, 71, 2, + 17, 2, 32, 84, 2, 11, 79, 2, 223, 130, 6, 78, 2, 11, 32, 2, 183, 158, 15, + 83, 234, 2, 92, 2, 69, 89, 88, 7, 71, 79, 76, 73, 65, 78, 32, 206, 24, + 79, 133, 136, 12, 3, 75, 69, 89, 6, 26, 32, 255, 138, 16, 45, 4, 180, + 137, 12, 6, 87, 73, 84, 72, 32, 87, 167, 211, 2, 66, 214, 2, 194, 2, 68, + 46, 70, 148, 1, 14, 73, 78, 86, 69, 82, 84, 69, 68, 32, 66, 73, 82, 71, + 65, 16, 7, 76, 69, 84, 84, 69, 82, 32, 138, 16, 83, 132, 1, 7, 82, 79, + 84, 65, 84, 69, 68, 22, 66, 98, 84, 160, 2, 4, 86, 79, 87, 69, 246, 236, + 3, 67, 164, 1, 6, 77, 65, 78, 67, 72, 85, 160, 201, 7, 4, 78, 73, 82, 85, + 239, 208, 2, 69, 22, 232, 20, 3, 79, 85, 66, 223, 188, 14, 73, 12, 112, + 19, 82, 69, 69, 32, 86, 65, 82, 73, 65, 84, 73, 79, 78, 32, 83, 69, 76, + 69, 67, 254, 238, 13, 85, 195, 46, 79, 8, 229, 245, 9, 3, 84, 79, 82, 5, + 227, 19, 32, 136, 2, 130, 2, 65, 244, 4, 2, 67, 72, 88, 2, 77, 65, 174, + 2, 83, 246, 1, 84, 234, 3, 90, 230, 190, 14, 72, 246, 173, 1, 75, 2, 76, + 162, 7, 69, 2, 79, 2, 85, 234, 61, 66, 2, 68, 2, 70, 2, 71, 2, 74, 2, 78, + 2, 80, 2, 81, 2, 82, 2, 87, 2, 89, 187, 2, 73, 59, 56, 8, 76, 73, 32, 71, + 65, 76, 73, 32, 223, 192, 16, 78, 54, 174, 1, 65, 52, 6, 86, 73, 83, 65, + 82, 71, 22, 68, 76, 5, 72, 65, 76, 70, 32, 34, 73, 50, 85, 34, 78, 30, + 84, 66, 66, 226, 245, 15, 80, 2, 90, 254, 68, 83, 14, 67, 3, 75, 7, 48, + 6, 78, 85, 83, 86, 65, 82, 207, 191, 16, 72, 2, 167, 161, 9, 65, 8, 26, + 65, 231, 188, 16, 68, 7, 218, 248, 8, 77, 253, 131, 7, 3, 71, 65, 76, 4, + 178, 188, 16, 89, 187, 2, 85, 5, 45, 9, 78, 86, 69, 82, 84, 69, 68, 32, + 85, 2, 173, 251, 15, 3, 66, 65, 68, 4, 194, 187, 16, 71, 3, 78, 8, 60, 6, + 72, 82, 69, 69, 32, 66, 226, 245, 15, 84, 195, 71, 65, 2, 17, 2, 65, 76, + 2, 135, 152, 16, 85, 6, 26, 65, 223, 188, 16, 73, 5, 29, 5, 32, 87, 73, + 84, 72, 2, 201, 233, 14, 2, 32, 84, 41, 29, 5, 78, 67, 72, 85, 32, 38, + 104, 9, 65, 76, 73, 32, 71, 65, 76, 73, 32, 238, 189, 14, 90, 242, 250, + 1, 70, 2, 75, 2, 82, 187, 2, 73, 28, 122, 68, 246, 193, 9, 67, 246, 227, + 2, 84, 138, 151, 2, 66, 2, 71, 2, 74, 2, 76, 234, 181, 1, 90, 254, 4, 78, + 131, 64, 83, 4, 238, 188, 14, 68, 243, 250, 1, 72, 48, 52, 4, 73, 66, 69, + 32, 134, 183, 16, 72, 187, 2, 65, 44, 138, 218, 5, 71, 2, 72, 170, 202, + 6, 84, 238, 3, 73, 246, 147, 2, 67, 2, 83, 246, 7, 82, 190, 164, 1, 65, + 186, 9, 90, 162, 7, 85, 234, 61, 68, 2, 70, 2, 74, 2, 75, 2, 80, 187, 2, + 69, 60, 52, 4, 79, 68, 79, 32, 146, 181, 16, 83, 187, 2, 65, 56, 250, 1, + 65, 98, 68, 34, 74, 34, 78, 252, 164, 1, 8, 76, 79, 78, 71, 32, 86, 79, + 87, 130, 176, 4, 71, 170, 202, 6, 84, 226, 151, 2, 67, 246, 7, 72, 150, + 181, 1, 79, 2, 85, 234, 61, 66, 2, 75, 2, 77, 2, 80, 2, 81, 2, 87, 2, 89, + 186, 2, 69, 3, 73, 6, 56, 8, 76, 73, 32, 71, 65, 76, 73, 32, 191, 180, + 16, 78, 4, 230, 183, 14, 90, 243, 250, 1, 84, 4, 178, 178, 16, 90, 187, + 2, 65, 4, 146, 178, 16, 73, 187, 2, 65, 2, 243, 177, 16, 73, 6, 190, 160, + 16, 72, 162, 17, 82, 187, 2, 65, 8, 128, 1, 4, 87, 73, 82, 76, 141, 187, + 4, 21, 73, 66, 69, 32, 83, 89, 76, 76, 65, 66, 76, 69, 32, 66, 79, 85, + 78, 68, 65, 82, 89, 6, 17, 2, 32, 66, 6, 25, 4, 73, 82, 71, 65, 7, 33, 6, + 32, 87, 73, 84, 72, 32, 4, 150, 2, 68, 179, 236, 15, 79, 6, 152, 1, 3, + 82, 73, 80, 56, 18, 85, 82, 78, 69, 68, 32, 83, 87, 73, 82, 76, 32, 66, + 73, 82, 71, 65, 32, 217, 192, 1, 8, 79, 68, 79, 32, 83, 79, 70, 84, 2, + 217, 236, 15, 9, 76, 69, 32, 66, 73, 82, 71, 65, 32, 2, 33, 6, 87, 73, + 84, 72, 32, 68, 2, 153, 236, 15, 5, 79, 85, 66, 76, 69, 2, 187, 230, 9, + 76, 10, 84, 9, 71, 82, 65, 77, 32, 70, 79, 82, 32, 60, 4, 83, 84, 65, 66, + 139, 247, 13, 82, 6, 26, 89, 231, 193, 12, 69, 4, 210, 221, 15, 65, 155, + 1, 73, 2, 223, 255, 3, 76, 10, 48, 2, 78, 32, 174, 206, 4, 68, 231, 200, + 11, 83, 6, 88, 12, 86, 73, 69, 87, 73, 78, 71, 32, 67, 69, 82, 69, 226, + 192, 12, 67, 85, 2, 76, 65, 2, 173, 141, 16, 2, 77, 79, 4, 226, 185, 12, + 73, 139, 243, 3, 69, 10, 26, 72, 69, 2, 79, 82, 2, 45, 9, 69, 82, 32, 67, + 72, 82, 73, 83, 84, 2, 167, 250, 2, 77, 8, 50, 32, 52, 4, 73, 90, 69, 68, + 227, 156, 15, 87, 4, 202, 197, 8, 66, 221, 171, 6, 4, 83, 67, 79, 79, 2, + 133, 172, 3, 7, 32, 87, 72, 69, 69, 76, 67, 18, 52, 2, 78, 84, 152, 1, 2, + 83, 69, 243, 167, 16, 84, 10, 48, 3, 65, 73, 78, 221, 137, 12, 3, 32, 70, + 85, 9, 11, 32, 6, 238, 202, 9, 82, 24, 5, 67, 65, 66, 76, 69, 177, 241, + 1, 6, 66, 73, 67, 89, 67, 76, 7, 11, 32, 4, 232, 149, 15, 2, 84, 82, 131, + 86, 70, 86, 52, 7, 76, 69, 84, 84, 69, 82, 32, 155, 231, 5, 68, 62, 198, + 1, 75, 62, 77, 34, 78, 34, 79, 30, 80, 34, 84, 242, 105, 68, 136, 129, 8, + 2, 72, 65, 2, 82, 166, 226, 1, 83, 190, 89, 76, 246, 7, 67, 218, 100, 69, + 254, 242, 3, 89, 190, 28, 66, 2, 87, 187, 2, 65, 6, 212, 169, 5, 2, 69, + 65, 178, 137, 6, 72, 199, 243, 4, 79, 4, 226, 147, 16, 65, 215, 1, 73, 4, + 142, 214, 15, 73, 139, 60, 71, 7, 206, 165, 16, 76, 3, 79, 4, 218, 145, + 16, 72, 219, 19, 65, 6, 146, 145, 9, 72, 234, 144, 7, 69, 155, 3, 65, + 206, 4, 44, 2, 76, 84, 234, 6, 83, 171, 135, 14, 67, 102, 36, 4, 65, 78, + 73, 32, 219, 2, 73, 76, 52, 7, 76, 69, 84, 84, 69, 82, 32, 203, 144, 4, + 83, 74, 206, 1, 68, 234, 83, 78, 246, 231, 1, 82, 254, 208, 9, 74, 202, + 56, 84, 162, 149, 3, 66, 2, 67, 2, 71, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, 2, 83, 2, 86, 2, 89, 186, 2, 65, 2, 69, 2, 73, 3, 85, 10, 38, 68, - 178, 144, 16, 72, 187, 2, 65, 6, 174, 144, 16, 68, 2, 72, 187, 2, 65, 26, - 56, 2, 80, 76, 236, 2, 3, 83, 69, 84, 175, 254, 14, 77, 18, 50, 69, 89, + 162, 159, 16, 72, 187, 2, 65, 6, 158, 159, 16, 68, 2, 72, 187, 2, 65, 26, + 56, 2, 80, 76, 236, 2, 3, 83, 69, 84, 131, 139, 15, 77, 18, 50, 69, 89, 8, 73, 67, 65, 84, 73, 79, 78, 32, 2, 41, 8, 32, 77, 85, 83, 73, 67, 65, - 76, 2, 21, 3, 32, 78, 79, 2, 147, 253, 14, 84, 16, 40, 4, 83, 73, 71, 78, - 207, 144, 16, 88, 15, 11, 32, 12, 48, 3, 73, 78, 32, 81, 5, 87, 73, 84, - 72, 32, 8, 226, 206, 3, 76, 22, 82, 192, 175, 9, 5, 68, 79, 85, 66, 76, - 247, 236, 1, 84, 4, 174, 164, 12, 85, 195, 234, 2, 68, 7, 11, 32, 4, 232, - 83, 5, 77, 85, 76, 84, 73, 239, 139, 12, 85, 228, 3, 44, 5, 72, 82, 79, - 79, 77, 21, 2, 73, 67, 5, 175, 226, 14, 32, 224, 3, 30, 32, 101, 3, 65, - 76, 32, 6, 76, 4, 78, 65, 84, 85, 200, 43, 2, 70, 76, 161, 137, 11, 4, - 83, 72, 65, 82, 2, 235, 201, 10, 82, 218, 3, 64, 8, 75, 69, 89, 66, 79, - 65, 82, 68, 66, 83, 135, 247, 3, 78, 5, 41, 8, 32, 87, 73, 84, 72, 32, - 74, 65, 2, 143, 239, 13, 67, 212, 3, 48, 6, 89, 77, 66, 79, 76, 32, 243, - 166, 11, 67, 210, 3, 230, 2, 66, 238, 1, 67, 226, 9, 68, 242, 2, 69, 162, - 1, 70, 166, 2, 71, 112, 9, 65, 82, 80, 69, 71, 71, 73, 65, 84, 168, 1, 2, - 72, 65, 126, 75, 198, 3, 76, 138, 1, 77, 190, 2, 78, 162, 1, 79, 218, 2, - 80, 200, 2, 2, 81, 85, 146, 2, 82, 198, 2, 83, 230, 5, 84, 182, 10, 86, - 42, 88, 42, 87, 200, 140, 9, 9, 73, 78, 86, 69, 82, 84, 69, 68, 32, 255, - 195, 6, 90, 20, 36, 5, 69, 71, 73, 78, 32, 59, 82, 8, 130, 15, 80, 30, - 83, 218, 246, 7, 66, 143, 231, 7, 84, 12, 24, 2, 65, 67, 35, 69, 4, 198, - 210, 15, 75, 155, 53, 69, 8, 26, 86, 203, 197, 15, 65, 6, 32, 2, 73, 83, - 131, 135, 16, 69, 5, 171, 26, 32, 84, 120, 2, 65, 69, 30, 76, 94, 79, - 194, 7, 82, 234, 11, 32, 232, 32, 7, 73, 82, 67, 76, 69, 32, 88, 189, - 222, 5, 2, 85, 84, 2, 177, 193, 15, 2, 83, 85, 8, 42, 73, 245, 23, 5, 85, - 83, 84, 69, 82, 4, 26, 77, 223, 225, 13, 86, 2, 247, 214, 9, 65, 64, 26, - 77, 171, 130, 16, 68, 62, 64, 7, 66, 73, 78, 73, 78, 71, 32, 237, 145, 6, - 3, 77, 79, 78, 60, 202, 1, 65, 80, 7, 77, 65, 82, 67, 65, 84, 79, 56, 2, - 68, 79, 56, 2, 85, 80, 28, 2, 70, 76, 40, 5, 72, 65, 82, 77, 79, 22, 83, - 142, 2, 84, 226, 176, 7, 66, 224, 193, 3, 2, 76, 79, 195, 138, 5, 82, 6, - 76, 5, 67, 67, 69, 78, 84, 37, 10, 85, 71, 77, 69, 78, 84, 65, 84, 73, - 79, 5, 205, 2, 5, 45, 83, 84, 65, 67, 2, 199, 240, 14, 78, 6, 52, 2, 87, - 78, 168, 3, 2, 85, 66, 191, 225, 15, 73, 2, 233, 230, 14, 2, 32, 66, 12, - 248, 76, 2, 65, 71, 239, 179, 15, 73, 2, 195, 216, 14, 78, 12, 132, 1, 9, - 78, 65, 80, 32, 80, 73, 90, 90, 73, 22, 84, 224, 205, 4, 11, 80, 82, 69, - 67, 72, 71, 69, 83, 65, 78, 71, 219, 129, 6, 77, 2, 139, 244, 11, 67, 6, - 44, 5, 65, 67, 67, 65, 84, 151, 238, 15, 69, 4, 40, 4, 73, 83, 83, 73, - 195, 254, 15, 79, 2, 203, 223, 15, 77, 10, 34, 82, 193, 143, 12, 2, 69, - 78, 8, 28, 2, 73, 80, 191, 6, 69, 2, 209, 242, 11, 6, 76, 69, 32, 84, 79, - 78, 4, 22, 79, 191, 2, 69, 2, 255, 240, 14, 73, 24, 98, 65, 142, 1, 69, - 84, 6, 79, 85, 66, 76, 69, 32, 225, 158, 10, 7, 82, 85, 77, 32, 67, 76, - 69, 10, 96, 2, 32, 67, 20, 2, 77, 80, 204, 29, 4, 83, 72, 69, 68, 217, - 180, 15, 5, 76, 32, 83, 69, 71, 2, 223, 189, 8, 65, 5, 231, 243, 13, 32, - 4, 52, 3, 67, 82, 69, 145, 228, 8, 4, 71, 82, 69, 69, 2, 165, 25, 3, 83, - 67, 69, 6, 246, 21, 83, 130, 7, 66, 243, 134, 5, 70, 14, 32, 3, 78, 68, - 32, 155, 16, 73, 10, 74, 80, 30, 83, 208, 13, 3, 79, 70, 32, 138, 233, 7, - 66, 143, 231, 7, 84, 2, 229, 211, 12, 2, 72, 82, 2, 191, 128, 15, 76, 34, - 104, 6, 69, 82, 77, 65, 84, 65, 22, 73, 122, 79, 102, 32, 152, 36, 3, 85, - 83, 65, 205, 229, 3, 2, 76, 65, 5, 219, 202, 13, 32, 10, 22, 78, 143, 34, - 86, 8, 56, 9, 71, 69, 82, 69, 68, 32, 84, 82, 69, 243, 20, 65, 6, 237, - 237, 5, 4, 77, 79, 76, 79, 6, 216, 25, 3, 85, 82, 45, 183, 150, 14, 82, - 18, 54, 32, 56, 7, 76, 73, 83, 83, 65, 78, 68, 23, 82, 6, 25, 4, 67, 76, - 69, 70, 7, 161, 13, 3, 32, 79, 84, 4, 215, 129, 6, 79, 8, 84, 9, 65, 67, - 69, 32, 78, 79, 84, 69, 32, 37, 8, 69, 71, 79, 82, 73, 65, 78, 32, 4, - 164, 222, 8, 2, 78, 79, 27, 83, 4, 250, 2, 67, 3, 70, 8, 44, 3, 76, 70, - 32, 209, 8, 3, 85, 80, 84, 6, 52, 3, 80, 69, 68, 226, 222, 3, 78, 207, - 183, 3, 82, 2, 139, 198, 5, 65, 24, 48, 6, 73, 69, 86, 65, 78, 32, 247, - 205, 14, 79, 22, 178, 1, 67, 46, 69, 68, 7, 81, 85, 65, 82, 84, 69, 82, - 62, 70, 164, 219, 3, 4, 72, 65, 76, 70, 0, 5, 87, 72, 79, 76, 69, 157, - 224, 1, 9, 82, 69, 67, 73, 84, 65, 84, 73, 86, 2, 11, 32, 2, 11, 67, 2, - 163, 163, 5, 76, 6, 64, 5, 73, 71, 72, 84, 72, 201, 186, 14, 5, 78, 68, - 32, 79, 70, 4, 217, 252, 5, 10, 32, 78, 79, 84, 69, 32, 83, 84, 69, 77, - 4, 222, 14, 76, 197, 204, 3, 4, 73, 78, 65, 76, 8, 44, 4, 79, 78, 71, 65, - 221, 13, 2, 69, 70, 7, 11, 32, 4, 28, 3, 73, 77, 80, 3, 80, 2, 197, 2, 7, - 69, 82, 70, 69, 67, 84, 65, 18, 104, 2, 65, 88, 20, 2, 69, 90, 20, 5, 73, - 78, 73, 77, 65, 48, 3, 79, 79, 78, 25, 4, 85, 76, 84, 73, 2, 255, 204, - 15, 73, 2, 135, 207, 15, 90, 7, 11, 32, 4, 170, 144, 7, 82, 167, 194, 5, - 66, 4, 193, 17, 2, 32, 78, 4, 60, 11, 80, 76, 69, 32, 77, 69, 65, 83, 85, - 82, 69, 15, 32, 2, 11, 32, 2, 163, 143, 7, 82, 10, 120, 4, 69, 66, 69, - 78, 216, 26, 3, 85, 76, 76, 192, 220, 5, 3, 65, 84, 85, 141, 217, 6, 7, - 79, 84, 69, 72, 69, 65, 68, 2, 213, 211, 14, 4, 83, 84, 73, 77, 32, 88, - 2, 78, 69, 120, 14, 82, 78, 65, 77, 69, 78, 84, 32, 83, 84, 82, 79, 75, - 69, 107, 84, 6, 92, 18, 32, 72, 85, 78, 68, 82, 69, 68, 32, 84, 87, 69, - 78, 84, 89, 45, 69, 73, 163, 20, 45, 4, 185, 13, 2, 71, 72, 22, 11, 45, - 22, 158, 166, 3, 49, 162, 195, 12, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, - 2, 56, 3, 57, 4, 173, 4, 3, 84, 65, 86, 18, 96, 9, 65, 82, 69, 78, 84, - 72, 69, 83, 73, 0, 2, 76, 85, 18, 69, 142, 1, 79, 223, 222, 13, 73, 2, - 247, 22, 83, 6, 68, 4, 68, 65, 76, 32, 49, 9, 83, 32, 83, 85, 66, 80, 85, - 78, 67, 4, 26, 85, 199, 165, 15, 77, 2, 171, 165, 15, 80, 2, 167, 195, - 13, 84, 6, 48, 2, 68, 65, 245, 5, 5, 82, 82, 69, 67, 84, 2, 151, 224, 13, - 84, 12, 76, 6, 65, 82, 84, 69, 82, 32, 125, 9, 73, 78, 68, 73, 67, 69, - 83, 73, 77, 8, 60, 5, 84, 79, 78, 69, 32, 246, 207, 3, 78, 207, 183, 3, - 82, 4, 26, 83, 243, 141, 5, 70, 2, 197, 228, 15, 3, 72, 65, 82, 4, 17, 2, - 65, 32, 4, 142, 171, 12, 65, 205, 182, 3, 3, 66, 65, 83, 14, 22, 69, 175, - 1, 73, 10, 72, 4, 80, 69, 65, 84, 81, 10, 86, 69, 82, 83, 69, 32, 70, 73, - 78, 65, 8, 56, 8, 69, 68, 32, 70, 73, 71, 85, 82, 163, 146, 14, 32, 6, - 147, 217, 5, 69, 2, 211, 4, 76, 4, 48, 2, 71, 72, 57, 6, 78, 70, 79, 82, - 90, 65, 2, 33, 6, 84, 32, 82, 69, 80, 69, 2, 255, 158, 10, 65, 2, 147, - 176, 8, 78, 48, 136, 1, 6, 67, 65, 78, 68, 73, 67, 62, 69, 166, 1, 72, - 54, 73, 252, 1, 6, 81, 85, 65, 82, 69, 32, 212, 153, 8, 2, 85, 66, 243, - 100, 79, 4, 17, 2, 85, 83, 5, 141, 218, 13, 5, 32, 70, 76, 69, 88, 14, - 32, 2, 77, 73, 179, 182, 15, 71, 12, 64, 6, 66, 82, 69, 86, 73, 83, 1, 6, - 77, 73, 78, 73, 77, 65, 6, 11, 32, 6, 138, 13, 87, 182, 244, 6, 82, 167, - 194, 5, 66, 6, 84, 3, 79, 82, 84, 213, 233, 5, 3, 65, 82, 80, 14, 32, 4, - 78, 71, 76, 69, 43, 88, 2, 17, 2, 32, 66, 2, 135, 246, 13, 65, 12, 18, - 45, 79, 84, 4, 242, 7, 76, 173, 190, 14, 11, 83, 84, 82, 73, 78, 71, 32, - 70, 82, 69, 84, 8, 52, 3, 69, 69, 78, 1, 6, 89, 45, 70, 79, 85, 82, 4, - 193, 12, 2, 84, 72, 6, 26, 78, 247, 219, 15, 66, 4, 229, 9, 7, 79, 84, - 69, 72, 69, 65, 68, 60, 132, 1, 6, 69, 77, 80, 85, 83, 32, 154, 4, 72, - 88, 2, 87, 79, 84, 7, 79, 82, 67, 85, 76, 85, 83, 46, 82, 141, 3, 3, 85, - 82, 78, 16, 232, 1, 27, 73, 77, 80, 69, 82, 70, 69, 67, 84, 85, 77, 32, - 67, 85, 77, 32, 80, 82, 79, 76, 65, 84, 73, 79, 78, 69, 32, 129, 1, 25, - 80, 69, 82, 70, 69, 67, 84, 85, 77, 32, 67, 85, 77, 32, 80, 82, 79, 76, - 65, 84, 73, 79, 78, 69, 32, 10, 60, 10, 73, 77, 80, 69, 82, 70, 69, 67, - 84, 65, 131, 1, 80, 9, 213, 206, 5, 11, 32, 68, 73, 77, 73, 78, 85, 84, - 73, 79, 78, 6, 60, 3, 73, 77, 80, 41, 8, 80, 69, 82, 70, 69, 67, 84, 65, - 2, 197, 178, 15, 5, 69, 82, 70, 69, 67, 5, 169, 189, 8, 12, 32, 68, 73, - 77, 73, 78, 85, 84, 73, 79, 78, 45, 6, 72, 2, 82, 69, 249, 5, 11, 73, 82, - 84, 89, 45, 83, 69, 67, 79, 78, 68, 2, 11, 69, 2, 11, 45, 2, 11, 76, 2, - 37, 7, 73, 78, 69, 32, 83, 84, 65, 2, 235, 211, 14, 70, 5, 225, 206, 11, - 6, 32, 82, 69, 83, 85, 80, 27, 33, 6, 73, 65, 78, 71, 76, 69, 24, 128, 1, - 10, 32, 78, 79, 84, 69, 72, 69, 65, 68, 32, 61, 17, 45, 82, 79, 85, 78, - 68, 32, 78, 79, 84, 69, 72, 69, 65, 68, 32, 68, 20, 58, 68, 24, 3, 85, - 80, 32, 38, 82, 25, 3, 76, 69, 70, 4, 93, 3, 79, 87, 78, 8, 34, 82, 78, - 87, 219, 182, 12, 66, 4, 21, 3, 73, 71, 72, 4, 11, 84, 4, 11, 32, 4, 26, - 87, 219, 182, 12, 66, 2, 11, 72, 2, 223, 138, 14, 73, 7, 11, 32, 4, 246, - 186, 8, 83, 183, 150, 7, 85, 4, 36, 3, 79, 73, 68, 159, 142, 15, 73, 2, - 237, 251, 12, 5, 32, 78, 79, 84, 69, 6, 92, 4, 72, 79, 76, 69, 221, 199, - 8, 13, 73, 84, 72, 32, 70, 73, 78, 71, 69, 82, 78, 65, 73, 4, 11, 32, 4, - 218, 186, 3, 78, 207, 183, 3, 82, 232, 3, 224, 2, 15, 67, 79, 78, 83, 79, - 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 254, 1, 76, 212, 14, 16, 77, 79, - 68, 73, 70, 73, 69, 82, 32, 76, 69, 84, 84, 69, 82, 32, 122, 83, 80, 16, - 69, 65, 83, 84, 69, 82, 78, 32, 80, 87, 79, 32, 75, 65, 82, 69, 222, 9, - 84, 204, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 160, 175, 9, - 3, 80, 65, 79, 207, 142, 4, 68, 16, 66, 77, 165, 1, 11, 83, 72, 65, 78, - 32, 77, 69, 68, 73, 65, 76, 14, 80, 6, 69, 68, 73, 65, 76, 32, 45, 10, - 79, 78, 32, 77, 69, 68, 73, 65, 76, 32, 8, 250, 200, 15, 72, 2, 82, 2, - 87, 3, 89, 6, 206, 200, 15, 76, 2, 77, 3, 78, 2, 219, 217, 10, 32, 238, - 1, 104, 6, 69, 84, 84, 69, 82, 32, 185, 13, 15, 79, 71, 79, 71, 82, 65, - 77, 32, 75, 72, 65, 77, 84, 73, 32, 232, 1, 238, 1, 65, 50, 69, 146, 1, - 71, 50, 75, 254, 1, 77, 134, 1, 78, 58, 82, 86, 83, 130, 3, 84, 198, 1, - 87, 178, 227, 11, 68, 214, 6, 85, 22, 86, 186, 201, 1, 73, 42, 76, 246, - 193, 1, 66, 2, 67, 2, 74, 2, 80, 138, 69, 72, 2, 89, 187, 2, 79, 7, 144, - 253, 14, 4, 73, 84, 79, 78, 219, 74, 85, 9, 77, 17, 65, 83, 84, 69, 82, - 78, 32, 80, 87, 79, 32, 75, 65, 82, 69, 78, 32, 6, 42, 71, 186, 213, 10, - 89, 135, 181, 3, 78, 2, 183, 213, 10, 72, 6, 154, 200, 9, 82, 218, 251, - 5, 72, 187, 2, 65, 44, 32, 2, 72, 65, 219, 197, 15, 65, 43, 25, 4, 77, - 84, 73, 32, 40, 134, 1, 68, 34, 84, 242, 217, 8, 78, 230, 162, 6, 67, 2, - 72, 2, 74, 170, 37, 76, 226, 31, 70, 2, 71, 2, 82, 2, 83, 2, 88, 3, 90, - 6, 242, 252, 14, 68, 139, 69, 72, 4, 211, 252, 14, 84, 12, 36, 3, 79, 78, - 32, 219, 195, 15, 65, 10, 60, 2, 66, 66, 218, 200, 13, 74, 254, 183, 1, - 78, 199, 66, 69, 4, 150, 195, 15, 65, 3, 69, 10, 214, 216, 8, 78, 238, - 231, 6, 71, 2, 89, 187, 2, 65, 4, 252, 220, 2, 12, 85, 77, 65, 73, 32, - 80, 65, 76, 65, 85, 78, 71, 199, 229, 12, 65, 50, 90, 72, 192, 221, 2, 9, - 71, 65, 87, 32, 75, 65, 82, 69, 78, 158, 225, 12, 83, 187, 2, 65, 44, 66, - 65, 197, 1, 11, 87, 69, 32, 80, 65, 76, 65, 85, 78, 71, 32, 41, 17, 2, - 78, 32, 38, 134, 1, 78, 246, 196, 13, 74, 2, 80, 2, 84, 130, 179, 1, 66, - 2, 67, 2, 71, 2, 75, 138, 69, 68, 2, 70, 2, 72, 2, 90, 187, 2, 65, 6, - 250, 188, 15, 78, 2, 89, 187, 2, 65, 4, 202, 196, 13, 67, 3, 83, 36, 38, - 65, 138, 247, 14, 84, 139, 69, 72, 31, 41, 8, 73, 32, 76, 65, 73, 78, 71, - 32, 28, 82, 78, 206, 227, 11, 68, 190, 146, 3, 66, 2, 71, 2, 74, 170, 37, - 76, 227, 31, 70, 4, 142, 187, 15, 78, 3, 89, 6, 92, 17, 69, 83, 84, 69, - 82, 78, 32, 80, 87, 79, 32, 75, 65, 82, 69, 78, 32, 207, 188, 15, 65, 4, - 194, 203, 10, 80, 203, 246, 2, 84, 6, 162, 176, 14, 79, 166, 60, 81, 139, - 63, 72, 4, 56, 6, 75, 72, 65, 77, 84, 73, 1, 4, 83, 72, 65, 78, 2, 29, 5, - 32, 82, 69, 68, 85, 2, 193, 132, 1, 2, 80, 76, 90, 76, 2, 72, 65, 20, 4, - 73, 71, 78, 32, 233, 6, 6, 89, 77, 66, 79, 76, 32, 20, 199, 186, 9, 78, - 52, 202, 3, 65, 32, 12, 75, 72, 65, 77, 84, 73, 32, 84, 79, 78, 69, 45, - 30, 83, 132, 2, 15, 84, 65, 73, 32, 76, 65, 73, 78, 71, 32, 84, 79, 78, - 69, 45, 28, 22, 87, 69, 83, 84, 69, 82, 78, 32, 80, 87, 79, 32, 75, 65, - 82, 69, 78, 32, 84, 79, 78, 69, 206, 244, 2, 68, 252, 163, 5, 19, 82, 85, - 77, 65, 73, 32, 80, 65, 76, 65, 85, 78, 71, 32, 84, 79, 78, 69, 45, 204, - 170, 3, 9, 80, 65, 79, 32, 75, 65, 82, 69, 78, 148, 114, 6, 76, 73, 84, - 84, 76, 69, 199, 187, 2, 86, 4, 234, 162, 14, 83, 255, 78, 78, 4, 178, - 182, 15, 49, 3, 51, 18, 40, 4, 72, 65, 78, 32, 147, 229, 14, 69, 16, 84, - 8, 67, 79, 85, 78, 67, 73, 76, 32, 84, 5, 84, 79, 78, 69, 45, 151, 182, - 14, 83, 6, 172, 196, 11, 8, 69, 77, 80, 72, 65, 84, 73, 67, 229, 239, 3, - 4, 84, 79, 78, 69, 8, 190, 180, 15, 50, 2, 51, 2, 53, 3, 54, 4, 146, 180, - 15, 50, 3, 53, 10, 11, 45, 10, 234, 179, 15, 49, 2, 50, 2, 51, 2, 52, 3, - 53, 18, 130, 1, 65, 132, 1, 2, 76, 79, 28, 5, 83, 72, 65, 78, 32, 192, - 173, 8, 4, 71, 69, 78, 73, 169, 97, 6, 67, 79, 77, 80, 76, 69, 8, 84, 5, - 73, 84, 79, 78, 32, 141, 174, 6, 10, 70, 79, 82, 69, 77, 69, 78, 84, 73, - 79, 6, 98, 69, 186, 249, 8, 84, 151, 158, 5, 79, 2, 217, 173, 8, 2, 67, - 65, 4, 26, 69, 207, 151, 14, 79, 2, 201, 12, 4, 88, 67, 76, 65, 24, 140, - 1, 20, 79, 78, 69, 32, 77, 65, 82, 75, 32, 83, 71, 65, 87, 32, 75, 65, - 82, 69, 78, 32, 221, 175, 9, 8, 65, 73, 32, 76, 65, 73, 78, 71, 4, 38, - 72, 129, 206, 8, 3, 75, 69, 32, 2, 139, 208, 8, 65, 56, 150, 2, 65, 76, - 9, 71, 69, 66, 65, 32, 75, 65, 82, 69, 20, 6, 75, 65, 89, 65, 72, 32, 40, - 4, 77, 79, 78, 32, 34, 83, 142, 1, 69, 40, 18, 87, 69, 83, 84, 69, 82, - 78, 32, 80, 87, 79, 32, 75, 65, 82, 69, 78, 32, 228, 182, 9, 2, 84, 65, - 142, 160, 2, 85, 22, 86, 187, 201, 1, 73, 8, 26, 73, 219, 172, 15, 65, 7, - 25, 4, 84, 79, 78, 32, 4, 223, 162, 13, 65, 2, 151, 236, 14, 78, 6, 190, - 149, 15, 69, 2, 79, 215, 22, 85, 4, 146, 152, 15, 73, 219, 19, 79, 10, - 80, 4, 72, 65, 78, 32, 157, 204, 8, 10, 71, 65, 87, 32, 75, 65, 82, 69, - 78, 32, 8, 54, 69, 20, 5, 70, 73, 78, 65, 76, 247, 167, 15, 65, 5, 199, - 173, 14, 32, 2, 227, 152, 15, 32, 4, 174, 133, 15, 69, 151, 14, 85, 230, - 17, 152, 2, 5, 45, 65, 82, 89, 32, 214, 4, 65, 222, 16, 66, 30, 69, 146, - 32, 73, 136, 1, 3, 75, 79, 32, 170, 10, 79, 162, 24, 85, 224, 8, 22, 89, - 73, 65, 75, 69, 78, 71, 32, 80, 85, 65, 67, 72, 85, 69, 32, 72, 77, 79, - 78, 71, 32, 216, 135, 2, 2, 80, 78, 220, 189, 12, 2, 78, 66, 27, 76, 32, - 130, 1, 67, 114, 84, 46, 83, 160, 1, 5, 85, 78, 73, 79, 78, 108, 4, 87, - 72, 73, 84, 246, 225, 11, 76, 210, 17, 73, 223, 160, 2, 80, 8, 52, 7, 73, - 82, 67, 76, 69, 68, 32, 135, 151, 14, 79, 6, 40, 2, 80, 76, 14, 84, 219, - 141, 8, 68, 2, 35, 85, 2, 21, 3, 73, 77, 69, 2, 219, 229, 11, 83, 6, 40, - 6, 81, 85, 65, 82, 69, 32, 87, 85, 4, 60, 9, 73, 78, 84, 69, 82, 83, 69, - 67, 84, 1, 2, 85, 78, 2, 215, 191, 11, 73, 2, 11, 77, 2, 179, 226, 11, - 77, 7, 69, 15, 32, 79, 80, 69, 82, 65, 84, 79, 82, 32, 87, 73, 84, 72, - 32, 4, 150, 157, 11, 80, 131, 182, 3, 68, 2, 11, 69, 2, 233, 204, 12, 2, - 32, 86, 188, 2, 170, 2, 66, 252, 4, 10, 71, 32, 77, 85, 78, 68, 65, 82, - 73, 32, 142, 4, 73, 52, 2, 78, 68, 208, 4, 7, 84, 73, 79, 78, 65, 76, 32, - 228, 213, 1, 2, 85, 83, 140, 137, 8, 5, 77, 69, 32, 66, 65, 184, 212, 3, - 7, 90, 65, 82, 32, 65, 77, 85, 188, 160, 1, 8, 82, 82, 79, 87, 32, 78, - 79, 45, 231, 62, 75, 82, 52, 7, 65, 84, 65, 69, 65, 78, 32, 231, 157, 15, - 76, 80, 160, 1, 7, 76, 69, 84, 84, 69, 82, 32, 236, 2, 7, 78, 85, 77, 66, - 69, 82, 32, 133, 243, 9, 16, 67, 82, 85, 67, 73, 70, 79, 82, 77, 32, 78, - 85, 77, 66, 69, 82, 62, 236, 1, 6, 70, 73, 78, 65, 76, 32, 154, 132, 2, - 84, 246, 118, 68, 34, 76, 50, 81, 238, 205, 1, 82, 178, 215, 2, 65, 50, - 71, 90, 90, 34, 83, 66, 89, 158, 208, 1, 72, 234, 5, 75, 198, 75, 66, - 178, 216, 4, 78, 134, 2, 87, 218, 103, 80, 171, 4, 77, 18, 246, 250, 2, - 65, 54, 76, 250, 163, 4, 83, 190, 3, 89, 134, 214, 1, 75, 198, 75, 66, - 178, 216, 4, 78, 222, 105, 72, 171, 4, 77, 16, 134, 252, 2, 84, 134, 166, - 4, 79, 223, 137, 6, 70, 84, 84, 7, 76, 69, 84, 84, 69, 82, 32, 164, 2, 5, - 83, 73, 71, 78, 32, 191, 167, 13, 68, 54, 42, 65, 50, 69, 66, 73, 50, 79, - 47, 85, 11, 138, 137, 15, 78, 202, 17, 66, 2, 72, 3, 74, 15, 158, 159, - 13, 78, 210, 113, 76, 138, 109, 84, 174, 28, 71, 3, 77, 11, 194, 211, 14, - 68, 162, 70, 72, 2, 83, 3, 84, 11, 222, 152, 15, 78, 86, 76, 2, 80, 3, - 89, 11, 134, 153, 15, 67, 2, 68, 2, 75, 3, 82, 10, 100, 2, 77, 85, 20, 3, - 83, 85, 84, 182, 118, 73, 212, 234, 10, 2, 79, 74, 241, 253, 2, 3, 84, - 79, 89, 2, 139, 223, 14, 72, 2, 207, 150, 15, 85, 4, 208, 182, 11, 5, 76, - 32, 80, 79, 76, 187, 15, 82, 133, 1, 41, 8, 73, 78, 65, 71, 65, 82, 73, - 32, 130, 1, 140, 1, 7, 76, 69, 84, 84, 69, 82, 32, 248, 1, 5, 83, 73, 71, - 78, 32, 52, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 239, 229, 4, - 72, 94, 210, 1, 86, 226, 185, 11, 65, 38, 68, 82, 82, 34, 84, 230, 5, 85, - 206, 201, 1, 73, 42, 76, 250, 192, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, - 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 77, 2, 89, 186, 2, 69, 3, 79, 6, - 202, 148, 5, 79, 183, 255, 9, 65, 10, 254, 240, 9, 83, 238, 203, 1, 65, - 231, 147, 3, 86, 24, 250, 235, 4, 80, 238, 39, 86, 246, 171, 6, 65, 38, - 85, 206, 201, 1, 73, 222, 137, 2, 69, 3, 79, 4, 158, 48, 68, 255, 160, - 14, 80, 4, 154, 146, 15, 83, 15, 72, 254, 4, 216, 1, 3, 71, 65, 84, 192, - 7, 6, 73, 84, 72, 69, 82, 32, 194, 3, 83, 136, 1, 2, 85, 84, 242, 1, 87, - 148, 16, 3, 88, 84, 32, 144, 186, 8, 4, 80, 84, 85, 78, 216, 133, 3, 2, - 67, 75, 238, 161, 2, 82, 207, 144, 1, 76, 162, 1, 152, 1, 4, 73, 86, 69, - 32, 161, 233, 12, 27, 69, 68, 32, 68, 79, 85, 66, 76, 69, 32, 86, 69, 82, - 84, 73, 67, 65, 76, 32, 66, 65, 82, 32, 68, 79, 85, 66, 160, 1, 152, 1, - 8, 67, 73, 82, 67, 76, 69, 68, 32, 224, 1, 9, 68, 73, 65, 71, 79, 78, 65, - 76, 32, 184, 1, 8, 83, 81, 85, 65, 82, 69, 68, 32, 231, 206, 8, 65, 78, - 112, 5, 68, 73, 71, 73, 84, 28, 7, 78, 85, 77, 66, 69, 82, 32, 180, 3, 2, - 76, 65, 202, 228, 13, 84, 135, 4, 83, 2, 209, 157, 13, 2, 32, 90, 20, 50, - 84, 214, 133, 2, 69, 46, 70, 42, 78, 31, 83, 6, 150, 135, 2, 72, 47, 87, - 6, 38, 77, 206, 202, 13, 67, 183, 4, 68, 2, 49, 10, 73, 68, 68, 76, 69, - 32, 82, 73, 71, 72, 2, 17, 2, 84, 32, 2, 41, 8, 84, 79, 32, 76, 79, 87, - 69, 82, 2, 153, 137, 12, 2, 32, 67, 74, 142, 1, 76, 70, 85, 230, 154, 12, - 68, 138, 29, 81, 216, 129, 1, 2, 67, 82, 218, 19, 65, 234, 19, 73, 2, 87, - 134, 8, 82, 194, 157, 1, 80, 3, 83, 54, 26, 65, 183, 237, 10, 69, 52, - 129, 246, 8, 5, 84, 73, 78, 32, 67, 2, 171, 233, 13, 80, 18, 158, 1, 65, - 216, 1, 17, 71, 82, 69, 65, 84, 69, 82, 45, 84, 72, 65, 78, 32, 78, 79, - 82, 32, 37, 14, 76, 69, 83, 83, 45, 84, 72, 65, 78, 32, 78, 79, 82, 32, - 6, 92, 3, 32, 83, 85, 85, 16, 80, 80, 82, 79, 88, 73, 77, 65, 84, 69, 76, - 89, 32, 78, 79, 82, 4, 30, 66, 1, 3, 80, 69, 82, 2, 217, 245, 6, 8, 83, - 69, 84, 32, 79, 70, 32, 78, 2, 233, 48, 5, 32, 65, 67, 84, 85, 6, 254, - 144, 8, 69, 131, 227, 4, 76, 6, 218, 144, 8, 69, 223, 226, 4, 71, 6, 26, - 84, 151, 255, 12, 83, 4, 76, 5, 73, 78, 71, 32, 68, 185, 210, 10, 8, 32, - 87, 73, 84, 72, 32, 69, 71, 2, 181, 252, 7, 2, 79, 76, 64, 40, 4, 82, 65, - 76, 32, 159, 203, 14, 69, 62, 48, 6, 67, 72, 69, 83, 83, 32, 191, 198, - 14, 70, 60, 74, 75, 234, 165, 13, 66, 38, 69, 130, 5, 80, 22, 81, 38, 82, - 131, 2, 84, 20, 44, 5, 78, 73, 71, 72, 84, 251, 166, 13, 73, 15, 167, - 167, 13, 32, 246, 2, 70, 32, 184, 8, 2, 65, 32, 252, 6, 3, 76, 73, 78, - 195, 223, 3, 83, 174, 1, 90, 77, 64, 4, 83, 72, 69, 81, 20, 8, 84, 65, - 73, 32, 76, 85, 69, 32, 255, 230, 13, 76, 4, 25, 4, 79, 79, 78, 32, 4, - 138, 143, 8, 87, 211, 222, 5, 83, 2, 163, 189, 9, 69, 166, 1, 160, 1, 7, - 76, 69, 84, 84, 69, 82, 32, 244, 2, 8, 83, 73, 71, 78, 32, 76, 65, 69, - 22, 84, 76, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 155, 138, 13, + 76, 2, 21, 3, 32, 78, 79, 2, 247, 137, 15, 84, 16, 40, 4, 83, 73, 71, 78, + 191, 159, 16, 88, 15, 11, 32, 12, 48, 3, 73, 78, 32, 81, 5, 87, 73, 84, + 72, 32, 8, 178, 202, 3, 76, 22, 82, 140, 191, 9, 5, 68, 79, 85, 66, 76, + 183, 238, 1, 84, 4, 166, 175, 12, 85, 187, 238, 2, 68, 7, 11, 32, 4, 244, + 83, 5, 77, 85, 76, 84, 73, 255, 150, 12, 85, 228, 3, 44, 5, 72, 82, 79, + 79, 77, 21, 2, 73, 67, 5, 139, 239, 14, 32, 224, 3, 30, 32, 109, 3, 65, + 76, 32, 6, 64, 4, 78, 65, 84, 85, 20, 3, 83, 72, 65, 209, 43, 2, 70, 76, + 2, 239, 207, 10, 82, 2, 151, 187, 11, 82, 218, 3, 64, 8, 75, 69, 89, 66, + 79, 65, 82, 68, 66, 83, 183, 243, 3, 78, 5, 41, 8, 32, 87, 73, 84, 72, + 32, 74, 65, 2, 155, 250, 13, 67, 212, 3, 48, 6, 89, 77, 66, 79, 76, 32, + 163, 173, 11, 67, 210, 3, 230, 2, 66, 238, 1, 67, 230, 9, 68, 250, 2, 69, + 162, 1, 70, 166, 2, 71, 112, 9, 65, 82, 80, 69, 71, 71, 73, 65, 84, 168, + 1, 2, 72, 65, 126, 75, 198, 3, 76, 138, 1, 77, 186, 2, 78, 162, 1, 79, + 218, 2, 80, 200, 2, 2, 81, 85, 146, 2, 82, 198, 2, 83, 226, 5, 84, 182, + 10, 86, 42, 88, 42, 87, 156, 141, 9, 9, 73, 78, 86, 69, 82, 84, 69, 68, + 32, 143, 210, 6, 90, 20, 36, 5, 69, 71, 73, 78, 32, 59, 82, 8, 142, 15, + 80, 30, 83, 174, 247, 7, 66, 151, 245, 7, 84, 12, 24, 2, 65, 67, 35, 69, + 4, 174, 225, 15, 75, 155, 53, 69, 8, 26, 86, 179, 212, 15, 65, 6, 32, 2, + 73, 83, 235, 149, 16, 69, 5, 179, 26, 32, 84, 120, 2, 65, 69, 34, 76, 94, + 79, 194, 7, 82, 242, 11, 32, 224, 32, 7, 73, 82, 67, 76, 69, 32, 88, 157, + 223, 5, 2, 85, 84, 2, 11, 83, 2, 143, 208, 15, 85, 8, 42, 73, 249, 23, 5, + 85, 83, 84, 69, 82, 4, 26, 77, 239, 236, 13, 86, 2, 251, 214, 9, 65, 64, + 26, 77, 143, 145, 16, 68, 62, 64, 7, 66, 73, 78, 73, 78, 71, 32, 205, + 146, 6, 3, 77, 79, 78, 60, 202, 1, 65, 80, 7, 77, 65, 82, 67, 65, 84, 79, + 56, 2, 68, 79, 56, 2, 85, 80, 28, 2, 70, 76, 40, 5, 72, 65, 82, 77, 79, + 22, 83, 142, 2, 84, 174, 177, 7, 66, 172, 199, 3, 2, 76, 79, 143, 147, 5, + 82, 6, 76, 5, 67, 67, 69, 78, 84, 37, 10, 85, 71, 77, 69, 78, 84, 65, 84, + 73, 79, 5, 205, 2, 5, 45, 83, 84, 65, 67, 2, 143, 253, 14, 78, 6, 52, 2, + 87, 78, 168, 3, 2, 85, 66, 163, 240, 15, 73, 2, 201, 243, 14, 2, 32, 66, + 12, 248, 76, 2, 65, 71, 211, 194, 15, 73, 2, 147, 229, 14, 78, 12, 132, + 1, 9, 78, 65, 80, 32, 80, 73, 90, 90, 73, 22, 84, 156, 202, 4, 11, 80, + 82, 69, 67, 72, 71, 69, 83, 65, 78, 71, 247, 138, 6, 77, 2, 155, 254, 11, + 67, 6, 44, 5, 65, 67, 67, 65, 84, 251, 252, 15, 69, 4, 40, 4, 73, 83, 83, + 73, 167, 141, 16, 79, 2, 175, 238, 15, 77, 10, 34, 82, 217, 153, 12, 2, + 69, 78, 8, 28, 2, 73, 80, 199, 6, 69, 2, 225, 252, 11, 6, 76, 69, 32, 84, + 79, 78, 4, 22, 79, 179, 2, 69, 2, 199, 253, 14, 73, 24, 98, 65, 142, 1, + 69, 92, 6, 79, 85, 66, 76, 69, 32, 197, 164, 10, 7, 82, 85, 77, 32, 67, + 76, 69, 10, 96, 2, 32, 67, 20, 2, 77, 80, 204, 29, 4, 83, 72, 69, 68, + 189, 195, 15, 5, 76, 32, 83, 69, 71, 2, 179, 190, 8, 65, 5, 227, 255, 13, + 32, 4, 40, 3, 67, 82, 69, 29, 3, 71, 82, 69, 2, 181, 25, 3, 83, 67, 69, + 2, 207, 228, 8, 69, 6, 242, 21, 83, 254, 6, 66, 243, 130, 5, 70, 14, 32, + 3, 78, 68, 32, 151, 16, 73, 10, 74, 80, 30, 83, 204, 13, 3, 79, 70, 32, + 226, 233, 7, 66, 151, 245, 7, 84, 2, 237, 222, 12, 2, 72, 82, 2, 155, + 143, 15, 76, 34, 104, 6, 69, 82, 77, 65, 84, 65, 22, 73, 122, 79, 102, + 32, 144, 36, 3, 85, 83, 65, 137, 226, 3, 2, 76, 65, 5, 227, 213, 13, 32, + 10, 22, 78, 135, 34, 86, 8, 56, 9, 71, 69, 82, 69, 68, 32, 84, 82, 69, + 239, 20, 65, 6, 189, 237, 5, 4, 77, 79, 76, 79, 6, 208, 25, 3, 85, 82, + 45, 255, 162, 14, 82, 18, 54, 32, 56, 7, 76, 73, 83, 83, 65, 78, 68, 23, + 82, 6, 25, 4, 67, 76, 69, 70, 7, 157, 13, 3, 32, 79, 84, 4, 175, 130, 6, + 79, 8, 84, 9, 65, 67, 69, 32, 78, 79, 84, 69, 32, 37, 8, 69, 71, 79, 82, + 73, 65, 78, 32, 4, 236, 222, 8, 2, 78, 79, 27, 83, 4, 250, 2, 67, 3, 70, + 8, 44, 3, 76, 70, 32, 205, 8, 3, 85, 80, 84, 6, 52, 3, 80, 69, 68, 134, + 219, 3, 78, 151, 181, 8, 82, 2, 191, 194, 5, 65, 24, 48, 6, 73, 69, 86, + 65, 78, 32, 191, 218, 14, 79, 22, 178, 1, 67, 46, 69, 68, 7, 81, 85, 65, + 82, 84, 69, 82, 62, 70, 200, 215, 3, 4, 72, 65, 76, 70, 0, 5, 87, 72, 79, + 76, 69, 173, 224, 1, 9, 82, 69, 67, 73, 84, 65, 84, 73, 86, 2, 11, 32, 2, + 11, 67, 2, 159, 159, 5, 76, 6, 64, 5, 73, 71, 72, 84, 72, 145, 199, 14, + 5, 78, 68, 32, 79, 70, 4, 177, 253, 5, 10, 32, 78, 79, 84, 69, 32, 83, + 84, 69, 77, 4, 218, 14, 76, 237, 200, 3, 4, 73, 78, 65, 76, 8, 44, 4, 79, + 78, 71, 65, 217, 13, 2, 69, 70, 7, 11, 32, 4, 28, 3, 73, 77, 80, 3, 80, + 2, 193, 2, 7, 69, 82, 70, 69, 67, 84, 65, 18, 104, 2, 65, 88, 20, 2, 69, + 90, 20, 5, 73, 78, 73, 77, 65, 44, 3, 79, 79, 78, 25, 4, 85, 76, 84, 73, + 2, 219, 219, 15, 73, 2, 227, 221, 15, 90, 7, 11, 32, 4, 150, 138, 12, 82, + 195, 83, 66, 4, 189, 17, 2, 32, 78, 4, 60, 11, 80, 76, 69, 32, 77, 69, + 65, 83, 85, 82, 69, 15, 32, 2, 11, 32, 2, 147, 137, 12, 82, 10, 120, 4, + 69, 66, 69, 78, 212, 26, 3, 85, 76, 76, 160, 221, 5, 3, 65, 84, 85, 189, + 227, 6, 7, 79, 84, 69, 72, 69, 65, 68, 2, 177, 224, 14, 4, 83, 84, 73, + 77, 32, 88, 2, 78, 69, 120, 14, 82, 78, 65, 77, 69, 78, 84, 32, 83, 84, + 82, 79, 75, 69, 107, 84, 6, 92, 18, 32, 72, 85, 78, 68, 82, 69, 68, 32, + 84, 87, 69, 78, 84, 89, 45, 69, 73, 159, 20, 45, 4, 181, 13, 2, 71, 72, + 22, 11, 45, 22, 222, 161, 3, 49, 194, 214, 12, 50, 2, 51, 2, 52, 2, 53, + 2, 54, 2, 55, 2, 56, 3, 57, 4, 173, 4, 3, 84, 65, 86, 18, 96, 9, 65, 82, + 69, 78, 84, 72, 69, 83, 73, 0, 2, 76, 85, 18, 69, 142, 1, 79, 215, 234, + 13, 73, 2, 243, 22, 83, 6, 68, 4, 68, 65, 76, 32, 49, 9, 83, 32, 83, 85, + 66, 80, 85, 78, 67, 4, 26, 85, 167, 180, 15, 77, 2, 139, 180, 15, 80, 2, + 179, 206, 13, 84, 6, 48, 2, 68, 65, 245, 5, 5, 82, 82, 69, 67, 84, 2, + 143, 236, 13, 84, 12, 76, 6, 65, 82, 84, 69, 82, 32, 125, 9, 73, 78, 68, + 73, 67, 69, 83, 73, 77, 8, 60, 5, 84, 79, 78, 69, 32, 158, 204, 3, 78, + 151, 181, 8, 82, 4, 26, 83, 239, 137, 5, 70, 2, 165, 243, 15, 3, 72, 65, + 82, 4, 17, 2, 65, 32, 4, 154, 182, 12, 65, 161, 186, 3, 3, 66, 65, 83, + 14, 22, 69, 175, 1, 73, 10, 72, 4, 80, 69, 65, 84, 81, 10, 86, 69, 82, + 83, 69, 32, 70, 73, 78, 65, 8, 56, 8, 69, 68, 32, 70, 73, 71, 85, 82, + 231, 158, 14, 32, 6, 231, 216, 5, 69, 2, 207, 4, 76, 4, 48, 2, 71, 72, + 57, 6, 78, 70, 79, 82, 90, 65, 2, 33, 6, 84, 32, 82, 69, 80, 69, 2, 231, + 164, 10, 65, 2, 223, 176, 8, 78, 48, 136, 1, 6, 67, 65, 78, 68, 73, 67, + 62, 69, 162, 1, 72, 54, 73, 252, 1, 6, 81, 85, 65, 82, 69, 32, 176, 154, + 8, 2, 85, 66, 251, 100, 79, 4, 17, 2, 85, 83, 5, 133, 230, 13, 5, 32, 70, + 76, 69, 88, 14, 32, 2, 77, 73, 147, 197, 15, 71, 12, 64, 6, 66, 82, 69, + 86, 73, 83, 1, 6, 77, 73, 78, 73, 77, 65, 6, 11, 32, 6, 134, 13, 87, 170, + 238, 11, 82, 195, 83, 66, 6, 84, 3, 79, 82, 84, 181, 234, 5, 3, 65, 82, + 80, 14, 32, 4, 78, 71, 76, 69, 43, 88, 2, 17, 2, 32, 66, 2, 131, 130, 14, + 65, 12, 18, 45, 79, 84, 4, 242, 7, 76, 141, 203, 14, 11, 83, 84, 82, 73, + 78, 71, 32, 70, 82, 69, 84, 8, 52, 3, 69, 69, 78, 1, 6, 89, 45, 70, 79, + 85, 82, 4, 193, 12, 2, 84, 72, 6, 26, 78, 219, 234, 15, 66, 4, 229, 9, 7, + 79, 84, 69, 72, 69, 65, 68, 60, 132, 1, 6, 69, 77, 80, 85, 83, 32, 154, + 4, 72, 88, 2, 87, 79, 84, 7, 79, 82, 67, 85, 76, 85, 83, 46, 82, 141, 3, + 3, 85, 82, 78, 16, 232, 1, 27, 73, 77, 80, 69, 82, 70, 69, 67, 84, 85, + 77, 32, 67, 85, 77, 32, 80, 82, 79, 76, 65, 84, 73, 79, 78, 69, 32, 129, + 1, 25, 80, 69, 82, 70, 69, 67, 84, 85, 77, 32, 67, 85, 77, 32, 80, 82, + 79, 76, 65, 84, 73, 79, 78, 69, 32, 10, 60, 10, 73, 77, 80, 69, 82, 70, + 69, 67, 84, 65, 131, 1, 80, 9, 173, 206, 5, 11, 32, 68, 73, 77, 73, 78, + 85, 84, 73, 79, 78, 6, 60, 3, 73, 77, 80, 41, 8, 80, 69, 82, 70, 69, 67, + 84, 65, 2, 169, 193, 15, 5, 69, 82, 70, 69, 67, 5, 249, 189, 8, 12, 32, + 68, 73, 77, 73, 78, 85, 84, 73, 79, 78, 45, 6, 72, 2, 82, 69, 249, 5, 11, + 73, 82, 84, 89, 45, 83, 69, 67, 79, 78, 68, 2, 11, 69, 2, 11, 45, 2, 11, + 76, 2, 37, 7, 73, 78, 69, 32, 83, 84, 65, 2, 207, 226, 14, 70, 5, 241, + 216, 11, 6, 32, 82, 69, 83, 85, 80, 27, 33, 6, 73, 65, 78, 71, 76, 69, + 24, 128, 1, 10, 32, 78, 79, 84, 69, 72, 69, 65, 68, 32, 61, 17, 45, 82, + 79, 85, 78, 68, 32, 78, 79, 84, 69, 72, 69, 65, 68, 32, 68, 20, 58, 68, + 24, 3, 85, 80, 32, 38, 82, 25, 3, 76, 69, 70, 4, 93, 3, 79, 87, 78, 8, + 34, 82, 78, 87, 235, 193, 12, 66, 4, 21, 3, 73, 71, 72, 4, 11, 84, 4, 11, + 32, 4, 26, 87, 235, 193, 12, 66, 2, 11, 72, 2, 167, 151, 14, 73, 7, 11, + 32, 4, 198, 187, 8, 83, 203, 164, 7, 85, 4, 36, 3, 79, 73, 68, 131, 157, + 15, 73, 2, 129, 135, 13, 5, 32, 78, 79, 84, 69, 6, 92, 4, 72, 79, 76, 69, + 177, 200, 8, 13, 73, 84, 72, 32, 70, 73, 78, 71, 69, 82, 78, 65, 73, 4, + 11, 32, 4, 134, 183, 3, 78, 151, 181, 8, 82, 232, 3, 224, 2, 15, 67, 79, + 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 254, 1, 76, 212, 14, + 16, 77, 79, 68, 73, 70, 73, 69, 82, 32, 76, 69, 84, 84, 69, 82, 32, 122, + 83, 80, 16, 69, 65, 83, 84, 69, 82, 78, 32, 80, 87, 79, 32, 75, 65, 82, + 69, 218, 9, 84, 204, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, + 192, 175, 9, 3, 80, 65, 79, 175, 154, 4, 68, 16, 66, 77, 165, 1, 11, 83, + 72, 65, 78, 32, 77, 69, 68, 73, 65, 76, 14, 80, 6, 69, 68, 73, 65, 76, + 32, 45, 10, 79, 78, 32, 77, 69, 68, 73, 65, 76, 32, 8, 222, 215, 15, 72, + 2, 82, 2, 87, 3, 89, 6, 178, 215, 15, 76, 2, 77, 3, 78, 2, 235, 223, 10, + 32, 238, 1, 104, 6, 69, 84, 84, 69, 82, 32, 185, 13, 15, 79, 71, 79, 71, + 82, 65, 77, 32, 75, 72, 65, 77, 84, 73, 32, 232, 1, 238, 1, 65, 50, 69, + 146, 1, 71, 50, 75, 254, 1, 77, 134, 1, 78, 58, 82, 86, 83, 130, 3, 84, + 198, 1, 87, 194, 238, 11, 68, 214, 6, 85, 22, 86, 166, 202, 1, 73, 42, + 76, 222, 196, 1, 66, 2, 67, 2, 74, 2, 80, 138, 69, 72, 2, 89, 187, 2, 79, + 7, 244, 139, 15, 4, 73, 84, 79, 78, 219, 74, 85, 9, 77, 17, 65, 83, 84, + 69, 82, 78, 32, 80, 87, 79, 32, 75, 65, 82, 69, 78, 32, 6, 42, 71, 202, + 219, 10, 89, 199, 187, 3, 78, 2, 199, 219, 10, 72, 6, 182, 200, 9, 82, + 162, 138, 6, 72, 187, 2, 65, 44, 32, 2, 72, 65, 191, 212, 15, 65, 43, 25, + 4, 77, 84, 73, 32, 40, 134, 1, 68, 34, 84, 214, 218, 8, 78, 230, 176, 6, + 67, 2, 72, 2, 74, 170, 37, 76, 226, 31, 70, 2, 71, 2, 82, 2, 83, 2, 88, + 3, 90, 6, 214, 139, 15, 68, 139, 69, 72, 4, 183, 139, 15, 84, 12, 36, 3, + 79, 78, 32, 191, 210, 15, 65, 10, 60, 2, 66, 66, 214, 212, 13, 74, 230, + 186, 1, 78, 199, 66, 69, 4, 250, 209, 15, 65, 3, 69, 10, 186, 217, 8, 78, + 238, 245, 6, 71, 2, 89, 187, 2, 65, 4, 184, 216, 2, 12, 85, 77, 65, 73, + 32, 80, 65, 76, 65, 85, 78, 71, 239, 248, 12, 65, 50, 90, 72, 252, 216, + 2, 9, 71, 65, 87, 32, 75, 65, 82, 69, 78, 198, 244, 12, 83, 187, 2, 65, + 44, 66, 65, 197, 1, 11, 87, 69, 32, 80, 65, 76, 65, 85, 78, 71, 32, 41, + 17, 2, 78, 32, 38, 134, 1, 78, 242, 208, 13, 74, 2, 80, 2, 84, 234, 181, + 1, 66, 2, 67, 2, 71, 2, 75, 138, 69, 68, 2, 70, 2, 72, 2, 90, 187, 2, 65, + 6, 222, 203, 15, 78, 2, 89, 187, 2, 65, 4, 198, 208, 13, 67, 3, 83, 36, + 38, 65, 238, 133, 15, 84, 139, 69, 72, 31, 41, 8, 73, 32, 76, 65, 73, 78, + 71, 32, 28, 82, 78, 222, 238, 11, 68, 146, 150, 3, 66, 2, 71, 2, 74, 170, + 37, 76, 227, 31, 70, 4, 242, 201, 15, 78, 3, 89, 6, 92, 17, 69, 83, 84, + 69, 82, 78, 32, 80, 87, 79, 32, 75, 65, 82, 69, 78, 32, 179, 203, 15, 65, + 4, 210, 209, 10, 80, 183, 252, 2, 84, 6, 234, 188, 14, 79, 194, 62, 81, + 139, 63, 72, 4, 56, 6, 75, 72, 65, 77, 84, 73, 1, 4, 83, 72, 65, 78, 2, + 29, 5, 32, 82, 69, 68, 85, 2, 165, 128, 1, 2, 80, 76, 90, 76, 2, 72, 65, + 20, 4, 73, 71, 78, 32, 233, 6, 6, 89, 77, 66, 79, 76, 32, 20, 227, 186, + 9, 78, 52, 202, 3, 65, 32, 12, 75, 72, 65, 77, 84, 73, 32, 84, 79, 78, + 69, 45, 30, 83, 132, 2, 15, 84, 65, 73, 32, 76, 65, 73, 78, 71, 32, 84, + 79, 78, 69, 45, 28, 22, 87, 69, 83, 84, 69, 82, 78, 32, 80, 87, 79, 32, + 75, 65, 82, 69, 78, 32, 84, 79, 78, 69, 146, 240, 2, 68, 136, 169, 5, 19, + 82, 85, 77, 65, 73, 32, 80, 65, 76, 65, 85, 78, 71, 32, 84, 79, 78, 69, + 45, 140, 180, 3, 9, 80, 65, 79, 32, 75, 65, 82, 69, 78, 148, 115, 6, 76, + 73, 84, 84, 76, 69, 155, 191, 2, 86, 4, 134, 188, 14, 83, 199, 68, 78, 4, + 150, 197, 15, 49, 3, 51, 18, 40, 4, 72, 65, 78, 32, 247, 243, 14, 69, 16, + 84, 8, 67, 79, 85, 78, 67, 73, 76, 32, 84, 5, 84, 79, 78, 69, 45, 251, + 196, 14, 83, 6, 188, 206, 11, 8, 69, 77, 80, 72, 65, 84, 73, 67, 185, + 244, 3, 4, 84, 79, 78, 69, 8, 162, 195, 15, 50, 2, 51, 2, 53, 3, 54, 4, + 246, 194, 15, 50, 3, 53, 10, 11, 45, 10, 206, 194, 15, 49, 2, 50, 2, 51, + 2, 52, 3, 53, 18, 130, 1, 65, 128, 1, 2, 76, 79, 28, 5, 83, 72, 65, 78, + 32, 152, 174, 8, 4, 71, 69, 78, 73, 217, 96, 6, 67, 79, 77, 80, 76, 69, + 8, 84, 5, 73, 84, 79, 78, 32, 237, 174, 6, 10, 70, 79, 82, 69, 77, 69, + 78, 84, 73, 79, 6, 94, 69, 170, 171, 13, 84, 139, 121, 79, 2, 177, 174, + 8, 2, 67, 65, 4, 26, 69, 179, 164, 14, 79, 2, 201, 12, 4, 88, 67, 76, 65, + 24, 140, 1, 20, 79, 78, 69, 32, 77, 65, 82, 75, 32, 83, 71, 65, 87, 32, + 75, 65, 82, 69, 78, 32, 253, 175, 9, 8, 65, 73, 32, 76, 65, 73, 78, 71, + 4, 38, 72, 233, 206, 8, 3, 75, 69, 32, 2, 243, 208, 8, 65, 56, 150, 2, + 65, 76, 9, 71, 69, 66, 65, 32, 75, 65, 82, 69, 20, 6, 75, 65, 89, 65, 72, + 32, 40, 4, 77, 79, 78, 32, 34, 83, 142, 1, 69, 40, 18, 87, 69, 83, 84, + 69, 82, 78, 32, 80, 87, 79, 32, 75, 65, 82, 69, 78, 32, 136, 183, 9, 2, + 84, 65, 254, 170, 2, 85, 22, 86, 167, 202, 1, 73, 8, 26, 73, 195, 187, + 15, 65, 7, 25, 4, 84, 79, 78, 32, 4, 223, 174, 13, 65, 2, 255, 250, 14, + 78, 6, 166, 164, 15, 69, 2, 79, 215, 22, 85, 4, 250, 166, 15, 73, 219, + 19, 79, 10, 80, 4, 72, 65, 78, 32, 133, 205, 8, 10, 71, 65, 87, 32, 75, + 65, 82, 69, 78, 32, 8, 54, 69, 20, 5, 70, 73, 78, 65, 76, 223, 182, 15, + 65, 5, 175, 188, 14, 32, 2, 203, 167, 15, 32, 4, 150, 148, 15, 69, 151, + 14, 85, 208, 11, 152, 2, 5, 45, 65, 82, 89, 32, 214, 4, 65, 222, 16, 66, + 30, 69, 146, 32, 73, 136, 1, 3, 75, 79, 32, 170, 10, 79, 162, 24, 85, + 152, 4, 22, 89, 73, 65, 75, 69, 78, 71, 32, 80, 85, 65, 67, 72, 85, 69, + 32, 72, 77, 79, 78, 71, 32, 232, 135, 2, 2, 80, 78, 252, 208, 12, 2, 78, + 66, 27, 76, 32, 130, 1, 67, 114, 84, 46, 83, 160, 1, 5, 85, 78, 73, 79, + 78, 108, 4, 87, 72, 73, 84, 138, 237, 11, 76, 210, 17, 73, 151, 162, 2, + 80, 8, 52, 7, 73, 82, 67, 76, 69, 68, 32, 211, 163, 14, 79, 6, 40, 2, 80, + 76, 14, 84, 175, 142, 8, 68, 2, 35, 85, 2, 21, 3, 73, 77, 69, 2, 239, + 240, 11, 83, 6, 40, 6, 81, 85, 65, 82, 69, 32, 87, 85, 4, 60, 9, 73, 78, + 84, 69, 82, 83, 69, 67, 84, 1, 2, 85, 78, 2, 199, 202, 11, 73, 2, 11, 77, + 2, 199, 237, 11, 77, 7, 69, 15, 32, 79, 80, 69, 82, 65, 84, 79, 82, 32, + 87, 73, 84, 72, 32, 4, 170, 167, 11, 80, 215, 186, 3, 68, 2, 11, 69, 2, + 129, 216, 12, 2, 32, 86, 188, 2, 170, 2, 66, 252, 4, 10, 71, 32, 77, 85, + 78, 68, 65, 82, 73, 32, 142, 4, 73, 52, 2, 78, 68, 208, 4, 7, 84, 73, 79, + 78, 65, 76, 32, 184, 209, 1, 2, 85, 83, 148, 147, 8, 5, 77, 69, 32, 66, + 65, 220, 218, 3, 7, 90, 65, 82, 32, 65, 77, 85, 164, 163, 1, 8, 82, 82, + 79, 87, 32, 78, 79, 45, 231, 62, 75, 82, 52, 7, 65, 84, 65, 69, 65, 78, + 32, 207, 172, 15, 76, 80, 160, 1, 7, 76, 69, 84, 84, 69, 82, 32, 236, 2, + 7, 78, 85, 77, 66, 69, 82, 32, 161, 249, 9, 16, 67, 82, 85, 67, 73, 70, + 79, 82, 77, 32, 78, 85, 77, 66, 69, 82, 62, 236, 1, 6, 70, 73, 78, 65, + 76, 32, 186, 255, 1, 84, 242, 119, 68, 34, 76, 50, 81, 214, 205, 1, 82, + 142, 220, 2, 65, 50, 71, 90, 90, 34, 83, 66, 89, 198, 207, 1, 72, 234, 5, + 75, 174, 81, 66, 170, 225, 4, 78, 134, 2, 87, 218, 103, 80, 171, 4, 77, + 18, 146, 247, 2, 65, 54, 76, 190, 168, 4, 83, 190, 3, 89, 174, 213, 1, + 75, 174, 81, 66, 170, 225, 4, 78, 222, 105, 72, 171, 4, 77, 16, 162, 248, + 2, 84, 202, 170, 4, 79, 255, 148, 6, 70, 84, 84, 7, 76, 69, 84, 84, 69, + 82, 32, 164, 2, 5, 83, 73, 71, 78, 32, 191, 179, 13, 68, 54, 42, 65, 50, + 69, 66, 73, 50, 79, 47, 85, 11, 242, 151, 15, 78, 202, 17, 66, 2, 72, 3, + 74, 15, 158, 171, 13, 78, 158, 114, 76, 166, 111, 84, 174, 28, 71, 3, 77, + 11, 170, 226, 14, 68, 162, 70, 72, 2, 83, 3, 84, 11, 198, 167, 15, 78, + 86, 76, 2, 80, 3, 89, 11, 238, 167, 15, 67, 2, 68, 2, 75, 3, 82, 10, 100, + 2, 77, 85, 20, 3, 83, 85, 84, 230, 113, 73, 184, 250, 10, 2, 79, 74, 197, + 129, 3, 3, 84, 79, 89, 2, 243, 237, 14, 72, 2, 183, 165, 15, 85, 4, 228, + 193, 11, 5, 76, 32, 80, 79, 76, 187, 15, 82, 133, 1, 41, 8, 73, 78, 65, + 71, 65, 82, 73, 32, 130, 1, 140, 1, 7, 76, 69, 84, 84, 69, 82, 32, 248, + 1, 5, 83, 73, 71, 78, 32, 52, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, + 32, 175, 226, 4, 72, 94, 210, 1, 86, 246, 196, 11, 65, 38, 68, 82, 82, + 34, 84, 230, 5, 85, 186, 202, 1, 73, 42, 76, 226, 195, 1, 78, 46, 83, 82, + 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 77, 2, 89, 186, 2, + 69, 3, 79, 6, 166, 148, 5, 79, 195, 142, 10, 65, 10, 154, 247, 9, 83, + 230, 208, 1, 65, 187, 151, 3, 86, 24, 158, 233, 4, 80, 166, 42, 86, 174, + 183, 6, 65, 38, 85, 186, 202, 1, 73, 198, 140, 2, 69, 3, 79, 4, 162, 48, + 68, 227, 175, 14, 80, 4, 130, 161, 15, 83, 15, 72, 254, 4, 216, 1, 3, 71, + 65, 84, 192, 7, 6, 73, 84, 72, 69, 82, 32, 194, 3, 83, 136, 1, 2, 85, 84, + 242, 1, 87, 148, 16, 3, 88, 84, 32, 232, 186, 8, 4, 80, 84, 85, 78, 148, + 144, 3, 2, 67, 75, 166, 163, 2, 82, 235, 146, 1, 76, 162, 1, 152, 1, 4, + 73, 86, 69, 32, 181, 244, 12, 27, 69, 68, 32, 68, 79, 85, 66, 76, 69, 32, + 86, 69, 82, 84, 73, 67, 65, 76, 32, 66, 65, 82, 32, 68, 79, 85, 66, 160, + 1, 152, 1, 8, 67, 73, 82, 67, 76, 69, 68, 32, 224, 1, 9, 68, 73, 65, 71, + 79, 78, 65, 76, 32, 184, 1, 8, 83, 81, 85, 65, 82, 69, 68, 32, 195, 207, + 8, 65, 78, 112, 5, 68, 73, 71, 73, 84, 28, 7, 78, 85, 77, 66, 69, 82, 32, + 180, 3, 2, 76, 65, 158, 241, 13, 84, 151, 4, 83, 2, 209, 169, 13, 2, 32, + 90, 20, 50, 84, 246, 128, 2, 69, 46, 70, 42, 78, 31, 83, 6, 182, 130, 2, + 72, 47, 87, 6, 38, 77, 162, 215, 13, 67, 183, 4, 68, 2, 49, 10, 73, 68, + 68, 76, 69, 32, 82, 73, 71, 72, 2, 17, 2, 84, 32, 2, 41, 8, 84, 79, 32, + 76, 79, 87, 69, 82, 2, 173, 148, 12, 2, 32, 67, 74, 142, 1, 76, 70, 85, + 254, 165, 12, 68, 138, 29, 81, 140, 131, 1, 2, 67, 82, 226, 19, 65, 234, + 19, 73, 2, 87, 150, 8, 82, 198, 159, 1, 80, 3, 83, 54, 26, 65, 251, 242, + 10, 69, 52, 141, 246, 8, 5, 84, 73, 78, 32, 67, 2, 143, 246, 13, 80, 18, + 158, 1, 65, 216, 1, 17, 71, 82, 69, 65, 84, 69, 82, 45, 84, 72, 65, 78, + 32, 78, 79, 82, 32, 37, 14, 76, 69, 83, 83, 45, 84, 72, 65, 78, 32, 78, + 79, 82, 32, 6, 92, 3, 32, 83, 85, 85, 16, 80, 80, 82, 79, 88, 73, 77, 65, + 84, 69, 76, 89, 32, 78, 79, 82, 4, 30, 66, 1, 3, 80, 69, 82, 2, 169, 246, + 6, 8, 83, 69, 84, 32, 79, 70, 32, 78, 2, 233, 48, 5, 32, 65, 67, 84, 85, + 6, 230, 145, 8, 69, 167, 237, 4, 76, 6, 194, 145, 8, 69, 131, 237, 4, 71, + 6, 26, 84, 151, 139, 13, 83, 4, 76, 5, 73, 78, 71, 32, 68, 197, 216, 10, + 8, 32, 87, 73, 84, 72, 32, 69, 71, 2, 141, 253, 7, 2, 79, 76, 64, 40, 4, + 82, 65, 76, 32, 135, 218, 14, 69, 62, 48, 6, 67, 72, 69, 83, 83, 32, 167, + 213, 14, 70, 60, 74, 75, 162, 178, 13, 66, 38, 69, 150, 5, 80, 22, 81, + 38, 82, 131, 2, 84, 20, 44, 5, 78, 73, 71, 72, 84, 199, 179, 13, 73, 15, + 243, 179, 13, 32, 246, 2, 70, 32, 184, 8, 2, 65, 32, 252, 6, 3, 76, 73, + 78, 207, 219, 3, 83, 174, 1, 90, 77, 64, 4, 83, 72, 69, 81, 20, 8, 84, + 65, 73, 32, 76, 85, 69, 32, 227, 243, 13, 76, 4, 25, 4, 79, 79, 78, 32, + 4, 242, 143, 8, 87, 183, 234, 5, 83, 2, 147, 195, 9, 69, 166, 1, 160, 1, + 7, 76, 69, 84, 84, 69, 82, 32, 244, 2, 8, 83, 73, 71, 78, 32, 76, 65, 69, + 22, 84, 76, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 155, 150, 13, 68, 102, 76, 6, 70, 73, 78, 65, 76, 32, 68, 4, 72, 73, 71, 72, 1, 3, 76, - 79, 87, 14, 162, 131, 13, 78, 154, 251, 1, 66, 2, 68, 2, 75, 2, 77, 3, - 86, 44, 11, 32, 44, 146, 1, 75, 2, 88, 34, 83, 138, 139, 11, 78, 162, - 169, 3, 84, 82, 80, 138, 69, 66, 2, 68, 2, 70, 2, 72, 2, 76, 2, 77, 2, - 81, 2, 86, 3, 89, 4, 158, 250, 14, 86, 187, 2, 65, 4, 254, 249, 14, 85, - 187, 2, 65, 5, 151, 252, 14, 86, 6, 252, 154, 7, 3, 79, 78, 69, 233, 80, + 79, 87, 14, 162, 143, 13, 78, 130, 254, 1, 66, 2, 68, 2, 75, 2, 77, 3, + 86, 44, 11, 32, 44, 146, 1, 75, 2, 88, 34, 83, 158, 149, 11, 78, 246, + 173, 3, 84, 82, 80, 138, 69, 66, 2, 68, 2, 70, 2, 72, 2, 76, 2, 77, 2, + 81, 2, 86, 3, 89, 4, 134, 137, 15, 86, 187, 2, 65, 4, 230, 136, 15, 85, + 187, 2, 65, 5, 255, 138, 15, 86, 6, 220, 155, 7, 3, 79, 78, 69, 221, 80, 8, 72, 65, 77, 32, 68, 73, 71, 73, 34, 110, 65, 46, 73, 30, 79, 38, 85, - 216, 175, 12, 11, 86, 79, 87, 69, 76, 32, 83, 72, 79, 82, 84, 135, 202, - 2, 69, 8, 146, 219, 10, 65, 182, 159, 4, 69, 3, 89, 4, 154, 250, 14, 73, - 3, 89, 9, 202, 218, 10, 65, 183, 159, 4, 89, 11, 166, 218, 10, 69, 182, - 159, 4, 85, 3, 89, 194, 1, 182, 1, 68, 106, 71, 72, 7, 76, 69, 84, 84, - 69, 82, 32, 214, 2, 83, 194, 23, 80, 162, 139, 1, 86, 182, 230, 7, 65, - 144, 234, 1, 5, 73, 78, 83, 69, 82, 202, 174, 1, 67, 175, 193, 2, 79, 26, - 64, 6, 79, 85, 66, 76, 69, 32, 142, 155, 11, 65, 147, 235, 1, 73, 4, 254, - 154, 11, 68, 175, 138, 1, 67, 2, 11, 65, 2, 11, 80, 2, 17, 2, 32, 70, 2, - 129, 141, 11, 2, 73, 76, 108, 218, 1, 78, 62, 86, 142, 154, 11, 65, 38, - 68, 114, 84, 230, 5, 85, 206, 201, 1, 73, 206, 193, 1, 83, 82, 66, 2, 67, + 240, 186, 12, 11, 86, 79, 87, 69, 76, 32, 83, 72, 79, 82, 84, 215, 205, + 2, 69, 8, 146, 248, 10, 65, 158, 145, 4, 69, 3, 89, 4, 130, 137, 15, 73, + 3, 89, 9, 202, 247, 10, 65, 159, 145, 4, 89, 11, 166, 247, 10, 69, 158, + 145, 4, 85, 3, 89, 194, 1, 182, 1, 68, 106, 71, 72, 7, 76, 69, 84, 84, + 69, 82, 32, 214, 2, 83, 194, 23, 80, 246, 134, 1, 86, 210, 240, 7, 65, + 180, 238, 1, 5, 73, 78, 83, 69, 82, 206, 175, 1, 67, 255, 196, 2, 79, 26, + 64, 6, 79, 85, 66, 76, 69, 32, 162, 166, 11, 65, 255, 235, 1, 73, 4, 146, + 166, 11, 68, 179, 138, 1, 67, 2, 11, 65, 2, 11, 80, 2, 17, 2, 32, 70, 2, + 241, 151, 11, 2, 73, 76, 108, 218, 1, 78, 62, 86, 162, 165, 11, 65, 38, + 68, 114, 84, 230, 5, 85, 186, 202, 1, 73, 182, 196, 1, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 76, 2, 77, 2, 80, 2, 82, 138, 69, 72, 2, 87, 2, - 89, 186, 2, 69, 3, 79, 14, 166, 173, 14, 71, 2, 89, 138, 69, 72, 2, 78, - 187, 2, 65, 10, 26, 69, 139, 161, 11, 79, 2, 173, 190, 12, 3, 68, 73, 67, - 22, 26, 73, 247, 200, 4, 65, 20, 44, 3, 71, 78, 32, 165, 164, 9, 2, 68, - 68, 18, 226, 222, 10, 67, 98, 78, 242, 60, 65, 174, 158, 1, 74, 228, 2, - 5, 70, 73, 78, 65, 76, 50, 85, 167, 242, 1, 86, 4, 167, 199, 14, 69, 4, - 202, 235, 9, 80, 159, 237, 3, 76, 6, 70, 78, 185, 220, 13, 11, 71, 72, - 84, 32, 87, 73, 84, 72, 32, 83, 84, 4, 200, 241, 7, 7, 69, 32, 80, 79, - 73, 78, 84, 179, 253, 6, 74, 124, 152, 1, 3, 67, 79, 77, 130, 3, 68, 78, - 76, 156, 4, 4, 72, 73, 71, 72, 72, 7, 83, 89, 77, 66, 79, 76, 32, 226, - 217, 7, 69, 205, 188, 1, 3, 84, 65, 77, 20, 52, 7, 66, 73, 78, 73, 78, - 71, 32, 235, 236, 14, 77, 18, 88, 3, 68, 79, 85, 32, 5, 76, 79, 78, 71, - 32, 78, 78, 33, 6, 83, 72, 79, 82, 84, 32, 2, 225, 237, 13, 3, 66, 76, - 69, 8, 142, 1, 72, 34, 76, 230, 251, 10, 82, 21, 7, 68, 69, 83, 67, 69, - 78, 68, 2, 11, 65, 2, 171, 206, 10, 83, 6, 34, 72, 34, 76, 231, 251, 10, - 82, 2, 173, 252, 10, 3, 73, 71, 72, 2, 141, 252, 10, 2, 79, 87, 24, 152, - 1, 4, 65, 78, 84, 65, 144, 207, 12, 4, 79, 82, 79, 77, 155, 43, 73, 70, + 89, 186, 2, 69, 3, 79, 14, 142, 188, 14, 71, 2, 89, 138, 69, 72, 2, 78, + 187, 2, 65, 10, 26, 69, 159, 172, 11, 79, 2, 209, 201, 12, 3, 68, 73, 67, + 22, 26, 73, 183, 197, 4, 65, 20, 44, 3, 71, 78, 32, 149, 170, 9, 2, 68, + 68, 18, 170, 228, 10, 67, 98, 78, 190, 66, 65, 190, 158, 1, 74, 228, 2, + 5, 70, 73, 78, 65, 76, 50, 85, 235, 245, 1, 86, 4, 143, 214, 14, 69, 4, + 230, 241, 9, 80, 231, 243, 3, 76, 6, 70, 78, 157, 233, 13, 11, 71, 72, + 84, 32, 87, 73, 84, 72, 32, 83, 84, 4, 176, 242, 7, 7, 69, 32, 80, 79, + 73, 78, 84, 179, 139, 7, 74, 124, 152, 1, 3, 67, 79, 77, 130, 3, 68, 78, + 76, 156, 4, 4, 72, 73, 71, 72, 72, 7, 83, 89, 77, 66, 79, 76, 32, 186, + 218, 7, 69, 229, 193, 1, 3, 84, 65, 77, 20, 52, 7, 66, 73, 78, 73, 78, + 71, 32, 211, 251, 14, 77, 18, 88, 3, 68, 79, 85, 32, 5, 76, 79, 78, 71, + 32, 78, 78, 33, 6, 83, 72, 79, 82, 84, 32, 2, 201, 252, 13, 3, 66, 76, + 69, 8, 142, 1, 72, 34, 76, 250, 133, 11, 82, 21, 7, 68, 69, 83, 67, 69, + 78, 68, 2, 11, 65, 2, 239, 211, 10, 83, 6, 34, 72, 34, 76, 251, 133, 11, + 82, 2, 193, 134, 11, 3, 73, 71, 72, 2, 161, 134, 11, 2, 79, 87, 24, 152, + 1, 4, 65, 78, 84, 65, 156, 218, 12, 4, 79, 82, 79, 77, 143, 44, 73, 70, 76, 4, 65, 74, 65, 78, 32, 6, 69, 84, 84, 69, 82, 32, 173, 3, 2, 79, 87, - 2, 185, 161, 13, 3, 89, 65, 76, 66, 228, 1, 2, 68, 65, 42, 74, 90, 78, - 138, 143, 11, 82, 206, 147, 1, 79, 162, 75, 67, 162, 186, 1, 69, 250, 18, + 2, 133, 174, 13, 3, 89, 65, 76, 66, 228, 1, 2, 68, 65, 42, 74, 90, 78, + 158, 154, 11, 82, 210, 147, 1, 79, 138, 76, 67, 138, 189, 1, 69, 250, 18, 71, 242, 42, 66, 2, 70, 2, 72, 2, 75, 2, 76, 2, 77, 2, 80, 2, 83, 2, 84, - 2, 87, 2, 89, 186, 2, 65, 2, 73, 3, 85, 5, 197, 178, 11, 5, 71, 66, 65, - 83, 73, 8, 40, 4, 79, 78, 65, 32, 227, 232, 14, 65, 6, 158, 238, 12, 67, - 138, 248, 1, 74, 3, 82, 11, 26, 65, 1, 2, 89, 65, 5, 245, 135, 8, 5, 32, - 87, 79, 76, 79, 2, 29, 5, 32, 84, 79, 78, 69, 2, 17, 2, 32, 65, 2, 135, + 2, 87, 2, 89, 186, 2, 65, 2, 73, 3, 85, 5, 217, 189, 11, 5, 71, 66, 65, + 83, 73, 8, 40, 4, 79, 78, 65, 32, 203, 247, 14, 65, 6, 158, 250, 12, 67, + 242, 250, 1, 74, 3, 82, 11, 26, 65, 1, 2, 89, 65, 5, 221, 136, 8, 5, 32, + 87, 79, 76, 79, 2, 29, 5, 32, 84, 79, 78, 69, 2, 17, 2, 32, 65, 2, 147, 207, 8, 80, 4, 68, 7, 71, 66, 65, 75, 85, 82, 85, 1, 6, 79, 79, 32, 68, - 69, 78, 2, 247, 146, 13, 78, 184, 1, 90, 32, 156, 3, 2, 77, 73, 118, 78, - 150, 1, 82, 238, 7, 84, 250, 153, 14, 45, 147, 40, 83, 18, 202, 1, 66, - 96, 5, 69, 78, 84, 82, 89, 22, 80, 204, 186, 2, 15, 79, 78, 69, 32, 85, - 78, 68, 69, 82, 32, 69, 73, 71, 72, 84, 160, 129, 4, 9, 77, 79, 66, 73, + 69, 78, 2, 195, 159, 13, 78, 186, 1, 94, 32, 156, 3, 2, 77, 73, 118, 78, + 150, 1, 82, 234, 7, 84, 134, 209, 6, 83, 223, 215, 7, 45, 18, 202, 1, 66, + 96, 5, 69, 78, 84, 82, 89, 22, 80, 148, 182, 2, 15, 79, 78, 69, 32, 85, + 78, 68, 69, 82, 32, 69, 73, 71, 72, 84, 164, 134, 4, 9, 77, 79, 66, 73, 76, 69, 32, 80, 72, 189, 30, 3, 83, 77, 79, 4, 52, 4, 82, 69, 65, 75, - 165, 137, 2, 3, 73, 67, 89, 2, 17, 2, 32, 72, 2, 155, 195, 13, 69, 5, - 203, 220, 13, 32, 4, 60, 6, 69, 68, 69, 83, 84, 82, 221, 201, 9, 3, 73, - 82, 65, 2, 145, 159, 11, 2, 73, 65, 4, 36, 5, 78, 65, 76, 32, 68, 59, 83, - 2, 173, 206, 13, 9, 73, 71, 73, 84, 32, 83, 72, 65, 80, 2, 175, 144, 11, - 77, 6, 38, 45, 249, 217, 9, 3, 70, 79, 82, 4, 76, 8, 66, 82, 69, 65, 75, - 73, 78, 71, 161, 169, 2, 5, 80, 79, 84, 65, 66, 2, 249, 221, 6, 2, 32, - 72, 97, 56, 2, 84, 72, 146, 11, 77, 209, 229, 10, 3, 68, 73, 67, 88, 42, - 32, 201, 205, 6, 4, 69, 65, 83, 84, 86, 96, 5, 69, 65, 83, 84, 32, 148, - 2, 6, 73, 78, 68, 73, 67, 32, 221, 1, 5, 87, 69, 83, 84, 32, 32, 82, 65, - 238, 242, 6, 80, 130, 1, 84, 138, 197, 4, 66, 38, 68, 18, 83, 247, 58, - 87, 14, 40, 4, 82, 82, 79, 87, 215, 238, 6, 78, 13, 11, 32, 10, 96, 9, - 67, 82, 79, 83, 83, 73, 78, 71, 32, 248, 2, 2, 65, 78, 162, 238, 6, 87, - 203, 134, 5, 70, 4, 234, 197, 3, 83, 231, 170, 3, 78, 20, 54, 80, 60, 3, - 81, 85, 65, 66, 82, 171, 132, 1, 70, 2, 37, 7, 76, 65, 67, 69, 72, 79, - 76, 2, 243, 179, 4, 68, 4, 220, 179, 4, 2, 82, 84, 209, 230, 9, 5, 78, - 84, 73, 84, 89, 2, 17, 2, 85, 80, 2, 171, 166, 4, 69, 34, 82, 65, 254, - 238, 6, 80, 130, 1, 84, 138, 197, 4, 66, 38, 68, 18, 83, 247, 58, 87, 16, - 34, 78, 33, 4, 82, 82, 79, 87, 2, 241, 194, 3, 3, 68, 32, 83, 15, 11, 32, - 12, 84, 3, 84, 79, 32, 142, 234, 6, 67, 40, 3, 65, 78, 68, 234, 2, 87, - 203, 134, 5, 70, 4, 150, 235, 6, 67, 169, 204, 6, 4, 76, 79, 78, 71, 56, - 54, 32, 236, 5, 5, 67, 72, 69, 68, 32, 207, 2, 69, 38, 162, 1, 65, 132, - 2, 4, 78, 79, 82, 77, 78, 80, 46, 83, 170, 1, 84, 234, 221, 7, 69, 196, - 33, 7, 73, 68, 69, 78, 84, 73, 67, 154, 193, 4, 71, 38, 76, 191, 78, 67, - 10, 88, 3, 32, 83, 85, 52, 7, 78, 32, 69, 76, 69, 77, 69, 28, 2, 83, 89, - 251, 128, 8, 76, 4, 30, 66, 1, 3, 80, 69, 82, 2, 29, 2, 83, 69, 2, 11, - 78, 2, 183, 125, 84, 2, 37, 7, 77, 80, 84, 79, 84, 73, 67, 2, 17, 2, 65, - 76, 2, 185, 129, 8, 2, 76, 89, 4, 253, 195, 6, 14, 65, 76, 32, 83, 85, - 66, 71, 82, 79, 85, 80, 32, 79, 70, 2, 137, 129, 8, 6, 65, 82, 65, 76, - 76, 69, 6, 48, 6, 81, 85, 65, 82, 69, 32, 247, 204, 13, 73, 4, 68, 5, 73, - 77, 65, 71, 69, 1, 8, 79, 82, 73, 71, 73, 78, 65, 76, 2, 21, 3, 32, 79, - 70, 2, 255, 193, 6, 32, 4, 210, 199, 10, 82, 215, 241, 1, 73, 8, 58, 76, - 40, 4, 82, 73, 71, 72, 133, 1, 3, 85, 80, 80, 4, 36, 2, 69, 70, 133, 1, - 2, 79, 87, 2, 89, 20, 84, 32, 83, 69, 77, 73, 67, 73, 82, 67, 76, 69, 32, - 87, 73, 84, 72, 32, 84, 72, 2, 17, 2, 82, 69, 2, 167, 128, 13, 69, 2, 11, - 69, 2, 41, 8, 82, 32, 82, 73, 71, 72, 84, 45, 2, 197, 147, 3, 6, 83, 72, - 65, 68, 79, 87, 11, 34, 32, 53, 4, 66, 79, 79, 75, 4, 17, 2, 80, 65, 4, - 222, 184, 14, 71, 215, 22, 68, 5, 81, 18, 32, 87, 73, 84, 72, 32, 68, 69, - 67, 79, 82, 65, 84, 73, 86, 69, 32, 67, 2, 171, 141, 4, 79, 186, 6, 102, - 77, 176, 3, 4, 83, 72, 85, 32, 172, 178, 5, 8, 84, 32, 65, 78, 68, 32, - 66, 79, 187, 155, 8, 76, 26, 36, 4, 66, 69, 82, 32, 247, 2, 69, 24, 58, - 69, 50, 70, 42, 83, 66, 84, 57, 4, 78, 73, 78, 69, 4, 204, 1, 3, 73, 71, - 72, 21, 3, 76, 69, 86, 4, 144, 1, 2, 79, 85, 13, 2, 73, 70, 6, 34, 73, - 85, 4, 69, 86, 69, 78, 4, 82, 88, 175, 251, 13, 71, 8, 40, 2, 72, 73, 46, - 69, 21, 2, 87, 69, 2, 11, 82, 2, 17, 2, 84, 69, 2, 11, 69, 2, 171, 199, - 7, 78, 4, 148, 199, 7, 3, 76, 86, 69, 1, 3, 78, 84, 89, 2, 215, 242, 6, - 82, 154, 6, 72, 12, 67, 72, 65, 82, 65, 67, 84, 69, 82, 45, 49, 66, 251, - 166, 12, 73, 152, 6, 18, 49, 87, 50, 160, 2, 222, 1, 55, 2, 56, 2, 57, 2, - 65, 2, 66, 2, 67, 2, 68, 2, 69, 3, 70, 248, 3, 138, 1, 48, 2, 49, 2, 50, - 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 2, 57, 2, 65, 2, 66, 2, 67, 2, - 68, 2, 69, 143, 1, 70, 32, 194, 199, 14, 48, 2, 49, 2, 50, 2, 51, 2, 52, - 2, 53, 2, 54, 2, 55, 2, 56, 2, 57, 2, 65, 2, 66, 2, 67, 2, 68, 2, 69, 3, - 70, 24, 182, 198, 14, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, - 55, 2, 56, 2, 57, 2, 65, 3, 66, 142, 1, 118, 76, 226, 3, 83, 164, 2, 5, - 84, 79, 78, 69, 45, 148, 225, 7, 8, 67, 73, 82, 67, 76, 69, 68, 32, 155, - 236, 4, 68, 92, 88, 6, 69, 84, 84, 69, 82, 32, 197, 163, 1, 10, 79, 71, - 79, 71, 82, 65, 77, 32, 78, 89, 90, 130, 2, 78, 90, 84, 246, 214, 7, 88, - 162, 126, 65, 232, 203, 1, 2, 72, 65, 242, 70, 82, 206, 147, 1, 79, 174, - 60, 68, 2, 77, 2, 80, 150, 201, 1, 69, 234, 61, 67, 2, 70, 2, 71, 2, 75, - 2, 76, 2, 81, 2, 83, 2, 86, 2, 89, 2, 90, 186, 2, 73, 2, 85, 3, 87, 22, - 86, 84, 230, 183, 12, 80, 254, 134, 2, 67, 2, 75, 2, 81, 2, 82, 2, 89, - 187, 2, 65, 6, 222, 190, 14, 83, 2, 88, 187, 2, 65, 14, 64, 4, 73, 71, - 78, 32, 189, 1, 7, 89, 76, 76, 65, 66, 76, 69, 12, 56, 4, 70, 79, 82, 32, - 209, 193, 13, 4, 88, 87, 32, 88, 10, 156, 9, 2, 76, 79, 194, 164, 3, 84, - 232, 93, 8, 73, 78, 86, 69, 82, 84, 69, 66, 160, 141, 4, 3, 65, 78, 73, - 183, 167, 2, 80, 2, 209, 244, 11, 4, 32, 76, 69, 78, 14, 202, 190, 14, - 66, 2, 68, 2, 71, 2, 74, 2, 77, 2, 83, 3, 86, 250, 14, 178, 2, 66, 226, - 1, 67, 224, 5, 4, 70, 70, 73, 67, 54, 71, 248, 6, 4, 73, 76, 32, 68, 22, - 76, 186, 69, 78, 150, 5, 80, 234, 7, 82, 218, 10, 83, 188, 8, 2, 84, 84, - 154, 8, 85, 248, 1, 3, 86, 69, 82, 198, 170, 2, 89, 202, 198, 5, 72, 238, - 254, 1, 75, 154, 249, 1, 68, 194, 64, 77, 246, 9, 87, 183, 137, 1, 88, - 10, 132, 1, 6, 76, 73, 81, 85, 69, 32, 180, 158, 2, 9, 83, 69, 82, 86, - 69, 82, 32, 69, 89, 189, 29, 8, 74, 69, 67, 84, 32, 82, 69, 80, 6, 176, - 198, 4, 13, 65, 78, 71, 76, 69, 32, 79, 80, 69, 78, 73, 78, 71, 175, 241, - 1, 72, 28, 56, 2, 82, 32, 234, 4, 84, 229, 193, 5, 3, 67, 85, 76, 22, - 156, 1, 11, 65, 77, 79, 85, 78, 84, 32, 79, 70, 32, 67, 22, 66, 198, 1, - 67, 118, 68, 106, 70, 0, 10, 73, 78, 86, 69, 82, 84, 69, 68, 32, 70, 143, - 225, 12, 72, 2, 207, 179, 5, 72, 6, 136, 1, 15, 82, 65, 78, 67, 72, 32, - 66, 65, 78, 75, 32, 73, 68, 69, 78, 160, 254, 4, 5, 69, 76, 84, 32, 66, - 157, 134, 6, 3, 79, 87, 32, 2, 21, 3, 84, 73, 70, 2, 11, 73, 2, 215, 244, - 10, 67, 4, 92, 17, 85, 83, 84, 79, 77, 69, 82, 32, 65, 67, 67, 79, 85, - 78, 84, 32, 78, 155, 202, 1, 72, 2, 183, 156, 6, 85, 4, 44, 5, 79, 85, - 66, 76, 69, 251, 204, 12, 65, 2, 11, 32, 2, 11, 66, 2, 177, 158, 7, 3, - 65, 67, 75, 2, 191, 243, 13, 79, 4, 152, 241, 8, 4, 65, 71, 79, 78, 181, - 189, 3, 2, 79, 80, 2, 11, 69, 2, 221, 251, 7, 5, 32, 66, 85, 73, 76, 60, - 48, 4, 72, 65, 77, 32, 133, 176, 14, 2, 79, 78, 58, 122, 70, 0, 10, 82, - 69, 86, 69, 82, 83, 69, 68, 32, 70, 36, 7, 76, 69, 84, 84, 69, 82, 32, - 137, 253, 3, 3, 83, 80, 65, 2, 149, 138, 4, 4, 69, 65, 84, 72, 52, 196, - 1, 2, 65, 73, 22, 66, 2, 80, 34, 67, 36, 2, 69, 65, 64, 2, 70, 69, 22, - 71, 22, 73, 58, 76, 2, 82, 22, 78, 46, 79, 34, 83, 50, 85, 206, 184, 1, - 77, 170, 9, 68, 153, 210, 11, 3, 84, 73, 78, 2, 231, 159, 14, 76, 2, 11, - 69, 2, 143, 174, 12, 73, 4, 142, 147, 8, 69, 191, 149, 4, 79, 6, 138, 1, - 66, 2, 68, 169, 141, 4, 6, 77, 72, 65, 78, 67, 72, 2, 143, 165, 9, 65, 2, - 131, 168, 13, 79, 4, 32, 2, 79, 68, 139, 175, 13, 70, 2, 239, 132, 8, 72, - 2, 147, 139, 12, 85, 4, 236, 154, 13, 3, 71, 69, 65, 219, 67, 73, 4, 166, - 222, 13, 78, 227, 79, 82, 4, 146, 250, 11, 65, 233, 177, 1, 3, 84, 82, - 65, 6, 60, 5, 73, 76, 76, 69, 65, 234, 170, 12, 65, 147, 130, 2, 82, 2, - 151, 221, 13, 78, 2, 211, 131, 13, 82, 182, 8, 38, 32, 142, 11, 68, 199, - 164, 13, 73, 184, 1, 64, 6, 67, 72, 73, 75, 73, 32, 205, 6, 5, 79, 78, - 65, 76, 32, 96, 132, 1, 7, 76, 69, 84, 84, 69, 82, 32, 148, 3, 2, 77, 85, - 62, 71, 74, 80, 204, 215, 3, 2, 82, 69, 210, 221, 8, 68, 235, 170, 1, 65, - 60, 50, 65, 98, 69, 54, 73, 50, 76, 62, 79, 51, 85, 16, 50, 65, 154, 169, - 14, 78, 86, 71, 2, 76, 3, 84, 8, 234, 169, 14, 74, 2, 75, 2, 77, 3, 87, - 8, 158, 227, 13, 68, 198, 13, 82, 222, 56, 78, 3, 80, 8, 194, 151, 14, - 78, 202, 17, 72, 2, 82, 3, 83, 12, 214, 188, 8, 65, 134, 236, 5, 69, 2, - 73, 2, 79, 3, 85, 8, 242, 139, 14, 84, 174, 28, 66, 2, 72, 3, 86, 8, 142, - 216, 13, 78, 226, 79, 67, 2, 68, 3, 89, 4, 56, 2, 45, 71, 129, 190, 12, - 6, 32, 84, 84, 85, 68, 68, 2, 253, 189, 12, 13, 65, 65, 72, 76, 65, 65, - 32, 84, 84, 85, 68, 68, 65, 6, 84, 11, 85, 78, 67, 84, 85, 65, 84, 73, - 79, 78, 32, 129, 219, 6, 4, 72, 65, 65, 82, 4, 48, 8, 68, 79, 85, 66, 76, - 69, 32, 77, 3, 77, 2, 129, 223, 13, 3, 85, 67, 65, 88, 100, 7, 76, 69, - 84, 84, 69, 82, 32, 192, 2, 5, 83, 73, 71, 78, 32, 142, 183, 8, 65, 191, - 249, 3, 68, 60, 42, 65, 54, 69, 58, 73, 54, 79, 59, 85, 13, 250, 163, 14, - 66, 2, 68, 2, 72, 2, 76, 3, 87, 13, 230, 211, 13, 78, 226, 79, 67, 2, 71, - 2, 72, 3, 83, 13, 174, 195, 8, 84, 226, 223, 5, 68, 2, 78, 3, 80, 13, - 254, 233, 13, 82, 138, 56, 78, 86, 77, 2, 79, 3, 89, 13, 130, 220, 13, - 68, 218, 52, 78, 202, 17, 74, 2, 75, 3, 82, 6, 58, 73, 236, 228, 12, 4, - 72, 79, 68, 68, 219, 151, 1, 77, 2, 243, 181, 1, 75, 252, 6, 34, 32, 153, - 57, 3, 69, 82, 32, 246, 6, 240, 2, 8, 67, 72, 73, 78, 69, 83, 69, 32, 44, - 10, 72, 85, 78, 71, 65, 82, 73, 65, 78, 32, 244, 6, 7, 73, 84, 65, 76, - 73, 67, 32, 212, 4, 14, 78, 79, 82, 84, 72, 32, 65, 82, 65, 66, 73, 65, - 78, 32, 196, 4, 3, 80, 69, 82, 216, 13, 2, 83, 79, 144, 13, 14, 84, 85, - 82, 75, 73, 67, 32, 76, 69, 84, 84, 69, 82, 32, 132, 7, 7, 85, 89, 71, - 72, 85, 82, 32, 219, 209, 11, 75, 4, 174, 251, 11, 73, 177, 95, 3, 72, - 79, 79, 216, 1, 96, 6, 67, 65, 80, 73, 84, 65, 0, 4, 83, 77, 65, 76, 221, - 5, 7, 78, 85, 77, 66, 69, 82, 32, 102, 45, 9, 76, 32, 76, 69, 84, 84, 69, - 82, 32, 102, 226, 1, 65, 54, 69, 164, 2, 10, 78, 73, 75, 79, 76, 83, 66, - 85, 82, 71, 0, 9, 82, 85, 68, 73, 77, 69, 78, 84, 65, 42, 79, 34, 85, - 192, 240, 3, 5, 67, 76, 79, 83, 69, 150, 157, 8, 73, 241, 203, 1, 6, 83, - 72, 79, 82, 84, 32, 11, 234, 222, 12, 77, 198, 117, 78, 162, 70, 65, 3, - 75, 63, 174, 1, 77, 22, 78, 78, 83, 134, 248, 9, 67, 86, 71, 2, 76, 2, - 84, 198, 152, 2, 90, 242, 134, 2, 66, 2, 68, 2, 69, 2, 70, 2, 72, 2, 74, - 2, 75, 2, 80, 2, 82, 3, 86, 5, 235, 152, 14, 80, 11, 34, 84, 182, 152, - 14, 67, 3, 89, 5, 189, 148, 2, 5, 45, 83, 72, 65, 80, 5, 139, 152, 14, - 90, 4, 11, 32, 4, 150, 129, 14, 79, 3, 85, 7, 250, 128, 14, 69, 215, 22, - 79, 9, 150, 148, 14, 78, 154, 3, 83, 3, 85, 12, 210, 4, 70, 222, 254, 5, - 79, 243, 191, 6, 84, 78, 80, 7, 76, 69, 84, 84, 69, 82, 32, 169, 3, 8, - 78, 85, 77, 69, 82, 65, 76, 32, 70, 190, 1, 69, 90, 75, 50, 83, 36, 3, - 78, 79, 82, 242, 191, 10, 85, 206, 201, 1, 73, 190, 137, 1, 80, 2, 84, - 150, 83, 67, 186, 22, 66, 2, 68, 2, 72, 2, 86, 2, 89, 2, 90, 214, 22, 65, - 3, 79, 23, 186, 244, 9, 83, 154, 153, 2, 82, 150, 201, 1, 75, 222, 61, - 70, 2, 76, 2, 77, 3, 78, 8, 150, 253, 13, 72, 214, 22, 65, 2, 69, 3, 85, - 4, 32, 2, 79, 85, 199, 252, 13, 72, 2, 29, 5, 84, 72, 69, 82, 78, 2, 233, - 137, 13, 2, 32, 84, 8, 38, 70, 206, 190, 12, 84, 191, 58, 79, 4, 11, 73, - 4, 174, 165, 12, 70, 167, 214, 1, 86, 64, 76, 7, 76, 69, 84, 84, 69, 82, - 32, 209, 3, 7, 78, 85, 77, 66, 69, 82, 32, 58, 210, 1, 65, 38, 71, 38, - 72, 30, 75, 38, 84, 74, 90, 134, 107, 77, 208, 153, 3, 2, 69, 83, 226, - 161, 3, 66, 2, 70, 2, 82, 2, 89, 138, 193, 3, 78, 218, 235, 1, 76, 190, - 56, 68, 146, 1, 81, 134, 3, 87, 131, 56, 83, 4, 186, 193, 3, 76, 199, - 254, 9, 73, 4, 230, 226, 12, 72, 171, 154, 1, 69, 4, 134, 142, 14, 65, 3, - 69, 4, 250, 174, 7, 72, 223, 222, 5, 65, 8, 34, 72, 166, 141, 14, 65, 3, - 69, 4, 254, 132, 13, 65, 167, 136, 1, 69, 4, 11, 65, 4, 162, 190, 13, 73, - 227, 79, 72, 6, 154, 148, 6, 84, 159, 224, 6, 79, 180, 1, 64, 11, 77, 73, - 67, 32, 76, 69, 84, 84, 69, 82, 32, 183, 5, 83, 76, 228, 1, 2, 67, 72, - 22, 68, 66, 69, 22, 73, 30, 77, 2, 78, 30, 80, 22, 83, 70, 84, 50, 86, - 38, 89, 94, 90, 218, 190, 6, 76, 2, 82, 162, 222, 2, 71, 146, 165, 2, 79, - 142, 205, 1, 66, 246, 40, 65, 254, 31, 75, 174, 45, 72, 187, 2, 85, 2, - 139, 212, 7, 69, 6, 26, 90, 139, 247, 13, 79, 4, 174, 156, 9, 72, 231, - 201, 4, 73, 5, 187, 138, 14, 70, 7, 166, 138, 14, 65, 3, 69, 2, 169, 243, - 13, 2, 69, 78, 2, 223, 192, 6, 69, 6, 26, 72, 235, 245, 13, 73, 4, 140, - 155, 9, 3, 67, 72, 79, 3, 79, 4, 26, 83, 167, 245, 13, 65, 2, 147, 228, - 13, 73, 4, 182, 154, 9, 79, 215, 181, 4, 69, 14, 60, 2, 69, 82, 166, 168, - 8, 65, 154, 206, 5, 82, 203, 17, 85, 7, 130, 136, 14, 73, 3, 85, 4, 182, - 153, 9, 72, 231, 201, 4, 65, 104, 88, 4, 73, 65, 78, 32, 241, 5, 13, 79, - 78, 65, 76, 32, 67, 79, 77, 80, 85, 84, 69, 82, 100, 80, 7, 78, 85, 77, - 66, 69, 82, 32, 80, 5, 83, 73, 71, 78, 32, 163, 207, 10, 87, 10, 42, 84, - 182, 178, 8, 72, 139, 186, 4, 79, 6, 142, 230, 1, 87, 251, 207, 11, 69, - 88, 210, 1, 65, 86, 66, 62, 68, 98, 74, 2, 86, 30, 84, 42, 88, 210, 111, - 71, 2, 75, 2, 78, 2, 82, 162, 190, 9, 77, 190, 139, 3, 83, 218, 69, 67, - 2, 70, 2, 72, 2, 76, 2, 80, 2, 89, 2, 90, 186, 2, 73, 3, 85, 9, 45, 9, - 85, 82, 65, 77, 65, 90, 68, 65, 65, 7, 146, 234, 6, 45, 247, 150, 7, 72, - 6, 38, 65, 253, 161, 10, 3, 85, 85, 77, 5, 187, 128, 14, 71, 10, 34, 65, - 190, 130, 14, 73, 3, 85, 7, 37, 7, 72, 89, 65, 65, 85, 83, 72, 5, 231, - 232, 6, 45, 4, 254, 129, 14, 65, 3, 73, 6, 170, 255, 13, 72, 186, 2, 65, - 3, 85, 4, 176, 204, 11, 8, 83, 72, 65, 65, 89, 65, 84, 72, 139, 181, 2, - 65, 5, 141, 142, 5, 31, 32, 87, 73, 84, 72, 32, 77, 79, 78, 73, 84, 79, - 82, 32, 73, 78, 32, 80, 79, 82, 84, 82, 65, 73, 84, 32, 79, 82, 73, 69, - 78, 144, 1, 92, 6, 71, 68, 73, 65, 78, 32, 141, 7, 12, 85, 84, 72, 32, - 65, 82, 65, 66, 73, 65, 78, 32, 80, 58, 70, 74, 76, 145, 5, 7, 78, 85, - 77, 66, 69, 82, 32, 2, 11, 82, 2, 241, 221, 11, 10, 65, 67, 84, 73, 79, - 78, 32, 79, 78, 69, 60, 76, 6, 69, 84, 84, 69, 82, 32, 149, 4, 8, 73, 71, - 65, 84, 85, 82, 69, 32, 58, 234, 1, 65, 96, 6, 70, 73, 78, 65, 76, 32, - 200, 1, 5, 82, 69, 83, 72, 45, 194, 215, 1, 76, 254, 165, 4, 71, 90, 90, - 34, 83, 66, 89, 158, 208, 1, 72, 234, 5, 75, 198, 75, 66, 178, 216, 4, - 78, 134, 2, 84, 2, 87, 218, 103, 80, 171, 4, 77, 6, 26, 76, 215, 251, 12, - 89, 4, 156, 128, 6, 8, 84, 69, 82, 78, 65, 84, 69, 32, 243, 214, 1, 69, - 18, 116, 3, 78, 85, 78, 0, 5, 83, 65, 68, 72, 69, 0, 3, 84, 65, 87, 222, - 215, 1, 65, 178, 201, 6, 66, 143, 194, 5, 72, 5, 41, 8, 32, 87, 73, 84, - 72, 32, 86, 69, 2, 241, 214, 5, 4, 82, 84, 73, 67, 2, 157, 215, 1, 6, 65, - 89, 73, 78, 45, 68, 18, 42, 84, 198, 254, 5, 79, 223, 137, 6, 70, 10, 42, - 72, 194, 216, 1, 87, 251, 207, 11, 69, 4, 222, 136, 12, 82, 159, 2, 73, - 64, 60, 7, 76, 69, 84, 84, 69, 82, 32, 245, 3, 3, 78, 85, 77, 58, 202, 1, - 65, 38, 68, 74, 71, 34, 75, 34, 83, 78, 84, 230, 43, 90, 182, 166, 1, 76, - 50, 81, 238, 205, 1, 82, 154, 217, 2, 89, 158, 208, 1, 72, 174, 81, 66, - 178, 216, 4, 78, 134, 2, 87, 218, 103, 70, 171, 4, 77, 4, 198, 167, 3, - 76, 199, 254, 9, 89, 6, 32, 2, 72, 65, 183, 211, 1, 65, 4, 194, 156, 8, - 76, 215, 171, 5, 68, 4, 238, 44, 72, 191, 204, 5, 73, 4, 198, 208, 7, 65, - 187, 75, 72, 8, 26, 65, 211, 244, 12, 72, 6, 142, 194, 7, 77, 138, 133, - 6, 68, 143, 45, 84, 8, 130, 91, 72, 226, 150, 11, 69, 243, 131, 1, 65, 6, - 56, 4, 66, 69, 82, 32, 229, 185, 13, 4, 69, 82, 73, 67, 4, 26, 70, 195, - 217, 12, 79, 2, 199, 132, 12, 73, 146, 1, 80, 7, 79, 82, 75, 72, 79, 78, - 32, 197, 3, 8, 89, 69, 78, 73, 83, 69, 73, 32, 84, 54, 65, 202, 1, 69, - 122, 73, 30, 79, 195, 134, 12, 66, 45, 106, 69, 194, 208, 9, 83, 158, - 160, 4, 66, 2, 68, 2, 71, 2, 76, 2, 78, 2, 81, 2, 82, 2, 84, 3, 89, 20, - 218, 240, 13, 66, 2, 68, 2, 71, 2, 75, 2, 76, 2, 78, 2, 82, 2, 83, 2, 84, - 3, 89, 20, 74, 78, 138, 211, 13, 76, 158, 27, 83, 146, 1, 67, 2, 77, 2, - 80, 3, 90, 8, 178, 239, 13, 67, 2, 71, 2, 84, 3, 89, 7, 134, 239, 13, 67, - 3, 81, 13, 130, 3, 69, 234, 235, 13, 80, 2, 81, 3, 84, 62, 38, 65, 170, - 1, 69, 86, 73, 23, 79, 39, 98, 69, 162, 236, 13, 83, 62, 78, 86, 66, 2, - 68, 2, 71, 2, 76, 2, 81, 2, 82, 2, 84, 3, 89, 17, 150, 242, 11, 78, 154, - 251, 1, 66, 2, 71, 2, 75, 2, 84, 3, 89, 15, 46, 78, 174, 235, 13, 83, - 146, 1, 67, 3, 90, 6, 186, 236, 13, 67, 2, 84, 3, 89, 5, 151, 236, 13, - 81, 6, 26, 69, 235, 235, 13, 81, 5, 231, 235, 13, 75, 52, 148, 1, 10, 67, - 79, 77, 66, 73, 78, 73, 78, 71, 32, 36, 7, 76, 69, 84, 84, 69, 82, 32, - 233, 1, 12, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 8, 186, 236, - 5, 84, 143, 227, 3, 68, 36, 134, 200, 1, 65, 210, 206, 1, 82, 130, 216, - 2, 76, 58, 90, 34, 83, 66, 89, 128, 211, 1, 6, 70, 73, 78, 65, 76, 32, 0, - 6, 71, 73, 77, 69, 76, 45, 134, 3, 75, 198, 75, 66, 178, 216, 4, 78, 134, - 2, 84, 2, 87, 218, 103, 80, 171, 4, 77, 8, 56, 4, 84, 87, 79, 32, 214, - 196, 11, 70, 235, 129, 1, 66, 4, 246, 210, 12, 66, 71, 68, 6, 130, 181, - 1, 87, 240, 154, 3, 3, 65, 68, 85, 231, 205, 7, 77, 22, 212, 1, 34, 32, - 87, 73, 84, 72, 32, 69, 88, 67, 76, 65, 77, 65, 84, 73, 79, 78, 32, 77, - 65, 82, 75, 32, 87, 73, 84, 72, 32, 76, 69, 70, 84, 32, 82, 44, 7, 67, - 79, 77, 73, 78, 71, 32, 194, 1, 69, 235, 147, 13, 73, 2, 21, 3, 73, 71, - 72, 2, 243, 235, 9, 84, 10, 148, 1, 6, 65, 85, 84, 79, 77, 79, 20, 7, 70, - 73, 82, 69, 32, 69, 78, 192, 224, 2, 2, 84, 65, 148, 232, 8, 6, 80, 79, - 76, 73, 67, 69, 155, 21, 66, 2, 135, 191, 11, 66, 2, 175, 202, 12, 71, 8, - 70, 32, 205, 174, 12, 11, 45, 80, 73, 69, 67, 69, 32, 83, 87, 73, 77, 6, - 40, 4, 68, 79, 84, 32, 215, 157, 10, 66, 4, 26, 79, 175, 159, 10, 76, 2, - 177, 233, 2, 11, 86, 69, 82, 32, 84, 87, 79, 32, 68, 79, 84, 46, 82, 69, - 188, 6, 2, 84, 73, 248, 212, 11, 5, 72, 73, 85, 67, 72, 171, 180, 1, 80, - 36, 70, 78, 201, 5, 12, 82, 65, 84, 73, 78, 71, 32, 83, 89, 83, 84, 69, - 34, 22, 32, 139, 4, 45, 28, 108, 2, 66, 79, 32, 7, 67, 69, 78, 84, 82, - 69, 32, 114, 70, 70, 72, 42, 77, 226, 133, 7, 83, 215, 233, 3, 76, 4, - 198, 220, 13, 79, 155, 3, 88, 8, 50, 84, 222, 200, 11, 66, 222, 2, 65, - 191, 82, 67, 2, 37, 7, 69, 65, 82, 68, 82, 79, 80, 2, 191, 202, 11, 45, - 4, 44, 5, 73, 76, 69, 32, 70, 239, 130, 2, 79, 2, 235, 130, 2, 79, 2, 17, - 2, 65, 78, 2, 191, 175, 10, 68, 4, 57, 12, 65, 73, 76, 66, 79, 88, 32, - 87, 73, 84, 72, 32, 4, 44, 3, 76, 79, 87, 21, 4, 82, 65, 73, 83, 2, 17, - 2, 69, 82, 2, 189, 243, 11, 2, 69, 68, 6, 108, 15, 67, 73, 82, 67, 85, - 73, 84, 45, 79, 85, 84, 80, 85, 84, 32, 181, 187, 12, 6, 79, 85, 84, 76, - 73, 78, 4, 18, 72, 3, 76, 2, 173, 191, 1, 4, 45, 84, 89, 80, 2, 153, 205, - 12, 6, 77, 32, 67, 79, 77, 77, 6, 64, 2, 79, 78, 237, 177, 1, 8, 67, 65, - 76, 32, 68, 73, 83, 67, 2, 167, 192, 11, 32, 206, 1, 112, 3, 65, 78, 71, - 66, 73, 212, 8, 5, 78, 65, 84, 69, 32, 32, 3, 84, 72, 79, 202, 171, 5, - 32, 175, 158, 6, 67, 6, 28, 2, 69, 32, 135, 22, 85, 4, 174, 133, 12, 66, - 175, 76, 72, 188, 1, 48, 5, 71, 73, 78, 65, 76, 21, 3, 89, 65, 32, 2, - 203, 133, 1, 32, 186, 1, 106, 65, 30, 70, 250, 1, 73, 32, 7, 76, 69, 84, - 84, 69, 82, 32, 142, 2, 83, 218, 1, 86, 243, 223, 11, 68, 4, 246, 249, 9, - 73, 3, 85, 12, 41, 8, 82, 65, 67, 84, 73, 79, 78, 32, 12, 56, 4, 79, 78, - 69, 32, 81, 6, 84, 72, 82, 69, 69, 32, 8, 42, 83, 162, 210, 11, 69, 46, - 72, 47, 81, 2, 173, 211, 11, 4, 73, 88, 84, 69, 4, 26, 83, 183, 212, 11, - 81, 2, 245, 253, 7, 4, 73, 88, 84, 69, 2, 217, 179, 12, 3, 83, 83, 72, - 104, 226, 1, 82, 238, 232, 6, 89, 134, 144, 3, 65, 38, 68, 114, 84, 46, - 86, 186, 5, 85, 206, 201, 1, 73, 42, 76, 250, 192, 1, 78, 46, 83, 82, 66, - 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 77, 2, 87, 186, 2, 69, - 3, 79, 6, 214, 208, 13, 72, 2, 82, 187, 2, 65, 18, 112, 20, 69, 81, 85, - 69, 78, 67, 69, 32, 70, 79, 82, 32, 76, 69, 84, 84, 69, 82, 32, 82, 29, - 4, 73, 71, 78, 32, 4, 186, 207, 13, 72, 3, 82, 14, 150, 189, 9, 67, 98, - 78, 242, 60, 65, 142, 239, 1, 79, 219, 164, 1, 86, 26, 49, 10, 79, 87, - 69, 76, 32, 83, 73, 71, 78, 32, 26, 142, 253, 9, 65, 38, 85, 22, 86, 186, - 201, 1, 73, 222, 137, 2, 69, 3, 79, 4, 174, 215, 6, 76, 243, 30, 82, 4, - 224, 240, 3, 2, 68, 79, 155, 183, 2, 71, 226, 1, 76, 4, 65, 71, 69, 32, - 140, 4, 6, 77, 65, 78, 89, 65, 32, 231, 202, 13, 67, 144, 1, 56, 6, 67, - 65, 80, 73, 84, 65, 1, 4, 83, 77, 65, 76, 72, 45, 9, 76, 32, 76, 69, 84, - 84, 69, 82, 32, 72, 194, 1, 65, 38, 69, 90, 75, 42, 79, 22, 84, 226, 224, - 6, 72, 230, 239, 4, 67, 2, 68, 2, 71, 130, 179, 1, 83, 2, 90, 130, 3, 66, - 138, 66, 76, 2, 77, 2, 78, 2, 80, 2, 87, 186, 2, 73, 3, 85, 9, 182, 209, - 11, 73, 135, 251, 1, 72, 15, 26, 72, 159, 252, 12, 73, 10, 222, 191, 9, - 84, 222, 145, 2, 67, 138, 248, 1, 75, 3, 80, 6, 134, 201, 13, 72, 2, 89, - 187, 2, 65, 5, 183, 251, 12, 73, 6, 194, 131, 13, 83, 195, 71, 65, 80, - 52, 7, 76, 69, 84, 84, 69, 82, 32, 143, 217, 11, 68, 60, 246, 1, 65, 38, - 67, 22, 68, 38, 75, 34, 83, 30, 77, 242, 240, 2, 81, 206, 144, 8, 79, - 224, 123, 2, 76, 65, 208, 73, 2, 78, 85, 134, 2, 87, 142, 62, 69, 234, - 61, 66, 2, 70, 2, 71, 2, 72, 2, 74, 2, 82, 2, 84, 2, 88, 2, 89, 186, 2, - 73, 3, 85, 7, 142, 250, 2, 76, 167, 206, 10, 65, 2, 163, 202, 12, 65, 4, - 150, 187, 8, 69, 175, 138, 5, 72, 4, 166, 198, 12, 65, 251, 126, 72, 4, - 26, 72, 159, 199, 13, 65, 2, 199, 199, 12, 73, 124, 68, 11, 79, 77, 65, - 78, 32, 83, 73, 89, 65, 81, 32, 231, 141, 13, 69, 122, 172, 1, 17, 65, - 76, 84, 69, 82, 78, 65, 84, 69, 32, 78, 85, 77, 66, 69, 82, 32, 200, 1, - 13, 70, 82, 65, 67, 84, 73, 79, 78, 32, 79, 78, 69, 32, 48, 3, 77, 65, - 82, 47, 78, 26, 54, 70, 50, 83, 46, 84, 198, 170, 12, 78, 235, 110, 69, - 6, 236, 202, 5, 3, 79, 85, 82, 151, 253, 6, 73, 6, 188, 202, 5, 2, 73, - 88, 251, 137, 6, 69, 10, 170, 184, 2, 69, 12, 2, 87, 79, 131, 156, 9, 72, - 4, 26, 83, 131, 192, 11, 72, 2, 239, 192, 11, 73, 2, 11, 82, 2, 11, 65, - 2, 255, 248, 11, 84, 90, 33, 6, 85, 77, 66, 69, 82, 32, 90, 58, 69, 66, - 70, 94, 78, 26, 83, 78, 84, 183, 172, 5, 79, 10, 25, 4, 73, 71, 72, 84, - 11, 170, 182, 2, 89, 255, 183, 5, 32, 20, 18, 73, 35, 79, 10, 166, 2, 70, - 199, 171, 5, 86, 10, 134, 2, 82, 209, 171, 5, 2, 85, 82, 10, 65, 3, 73, - 78, 69, 20, 40, 4, 69, 86, 69, 78, 1, 2, 73, 88, 11, 166, 1, 84, 191, - 235, 7, 32, 24, 34, 72, 50, 87, 235, 179, 2, 69, 10, 34, 73, 249, 171, 5, - 2, 82, 69, 4, 51, 82, 10, 26, 69, 223, 171, 5, 79, 4, 11, 78, 4, 11, 84, - 4, 191, 179, 2, 89, 84, 34, 84, 161, 162, 11, 2, 78, 67, 82, 42, 66, 37, - 6, 76, 73, 78, 69, 68, 32, 2, 141, 178, 12, 4, 79, 88, 32, 84, 80, 92, 7, - 76, 65, 84, 73, 78, 32, 67, 222, 189, 6, 87, 146, 233, 4, 66, 246, 13, - 71, 159, 23, 68, 54, 130, 170, 7, 65, 143, 210, 4, 82, 12, 18, 72, 35, - 76, 2, 137, 172, 12, 3, 69, 65, 84, 10, 32, 2, 65, 80, 247, 162, 12, 73, - 9, 29, 5, 80, 73, 78, 71, 32, 6, 40, 6, 87, 72, 73, 84, 69, 32, 51, 66, - 4, 44, 5, 65, 78, 68, 32, 66, 223, 250, 9, 83, 2, 197, 250, 9, 4, 76, 65, - 67, 75, 146, 13, 210, 1, 65, 174, 65, 68, 30, 69, 134, 13, 72, 138, 25, - 73, 206, 4, 76, 148, 15, 2, 78, 80, 54, 79, 234, 8, 82, 208, 15, 15, 83, - 65, 76, 84, 69, 82, 32, 80, 65, 72, 76, 65, 86, 73, 32, 214, 5, 85, 183, - 159, 12, 77, 154, 6, 134, 2, 68, 38, 71, 212, 1, 11, 72, 65, 87, 72, 32, - 72, 77, 79, 78, 71, 32, 134, 23, 76, 130, 6, 78, 58, 82, 160, 22, 2, 83, - 83, 132, 2, 10, 85, 32, 67, 73, 78, 32, 72, 65, 85, 32, 180, 7, 3, 87, - 32, 80, 188, 241, 7, 2, 67, 75, 157, 133, 5, 4, 80, 69, 82, 67, 5, 237, - 184, 1, 4, 68, 73, 78, 71, 14, 26, 69, 219, 145, 13, 79, 13, 34, 32, 186, - 182, 13, 82, 3, 83, 6, 72, 6, 87, 73, 84, 72, 32, 67, 241, 153, 4, 6, 70, - 65, 67, 73, 78, 71, 4, 50, 85, 193, 226, 7, 6, 73, 82, 67, 76, 69, 68, 2, - 131, 172, 12, 82, 254, 1, 190, 1, 67, 160, 6, 9, 77, 65, 82, 75, 32, 67, - 73, 77, 32, 160, 1, 7, 78, 85, 77, 66, 69, 82, 32, 244, 1, 5, 83, 73, 71, - 78, 32, 144, 10, 7, 86, 79, 87, 69, 76, 32, 75, 255, 174, 11, 68, 78, 92, - 9, 76, 65, 78, 32, 83, 73, 71, 78, 32, 137, 3, 9, 79, 78, 83, 79, 78, 65, - 78, 84, 32, 38, 132, 1, 2, 72, 65, 38, 75, 46, 76, 34, 84, 82, 86, 30, - 89, 148, 9, 2, 88, 89, 172, 5, 2, 80, 72, 174, 1, 70, 165, 2, 2, 77, 85, - 4, 190, 197, 2, 87, 187, 236, 10, 77, 6, 246, 15, 72, 234, 130, 13, 79, - 159, 14, 87, 4, 210, 12, 65, 251, 230, 12, 73, 8, 22, 83, 247, 9, 72, 6, - 180, 196, 2, 3, 72, 69, 69, 214, 176, 9, 65, 3, 87, 4, 254, 195, 2, 65, - 3, 87, 4, 226, 195, 2, 65, 211, 166, 10, 69, 40, 122, 67, 38, 72, 46, 78, - 60, 2, 80, 76, 2, 81, 250, 221, 2, 76, 2, 77, 2, 82, 2, 86, 2, 88, 2, 89, - 147, 171, 10, 65, 4, 130, 223, 2, 72, 147, 171, 10, 65, 6, 222, 222, 2, - 76, 2, 78, 147, 171, 10, 65, 12, 58, 67, 22, 84, 230, 221, 2, 75, 2, 76, - 147, 171, 10, 65, 2, 247, 221, 2, 72, 4, 226, 221, 2, 72, 3, 83, 14, 42, - 75, 50, 83, 38, 84, 223, 155, 13, 72, 4, 26, 72, 159, 239, 12, 69, 2, - 251, 156, 6, 65, 4, 230, 241, 11, 85, 255, 186, 1, 79, 4, 218, 240, 11, - 85, 215, 18, 65, 14, 52, 7, 72, 85, 78, 68, 82, 69, 68, 38, 84, 79, 77, - 4, 108, 2, 32, 77, 251, 170, 13, 83, 8, 24, 2, 69, 78, 51, 82, 6, 26, 32, - 143, 171, 13, 83, 4, 18, 66, 35, 84, 2, 205, 208, 4, 3, 73, 76, 76, 2, - 11, 72, 2, 225, 235, 9, 3, 79, 85, 83, 72, 188, 1, 4, 67, 73, 77, 32, - 246, 2, 72, 32, 3, 73, 66, 32, 22, 77, 86, 78, 50, 84, 124, 4, 86, 79, - 83, 32, 198, 1, 88, 208, 1, 6, 90, 87, 74, 32, 84, 72, 238, 176, 1, 76, - 203, 223, 4, 65, 16, 174, 1, 67, 84, 5, 78, 82, 69, 83, 32, 22, 84, 240, - 234, 11, 7, 80, 85, 66, 32, 68, 65, 87, 133, 187, 1, 16, 72, 65, 73, 83, - 32, 76, 85, 83, 32, 78, 84, 79, 71, 32, 78, 84, 4, 48, 7, 85, 65, 77, 32, - 84, 83, 72, 255, 3, 72, 2, 11, 79, 2, 195, 186, 2, 79, 2, 163, 183, 1, - 84, 6, 52, 3, 88, 87, 86, 173, 135, 10, 4, 83, 79, 86, 32, 5, 157, 150, - 6, 4, 32, 67, 72, 87, 4, 158, 72, 78, 131, 202, 12, 76, 2, 219, 234, 11, - 89, 6, 40, 4, 69, 69, 74, 32, 191, 231, 12, 85, 4, 128, 3, 2, 84, 83, 57, - 2, 83, 85, 4, 26, 84, 207, 241, 8, 81, 2, 155, 184, 2, 85, 6, 196, 1, 7, - 88, 72, 69, 69, 74, 32, 67, 236, 167, 8, 12, 72, 73, 82, 68, 45, 83, 84, - 65, 71, 69, 32, 72, 167, 214, 4, 65, 14, 54, 70, 22, 83, 30, 84, 134, 69, - 76, 239, 240, 7, 78, 2, 223, 144, 13, 69, 2, 249, 146, 6, 2, 69, 69, 6, - 42, 72, 29, 6, 83, 72, 65, 66, 32, 67, 4, 82, 73, 135, 145, 13, 79, 2, - 239, 203, 5, 69, 14, 34, 73, 22, 89, 207, 155, 11, 65, 2, 247, 229, 11, - 65, 10, 40, 4, 69, 69, 77, 32, 171, 130, 13, 79, 8, 84, 3, 78, 84, 88, - 200, 144, 6, 2, 84, 79, 218, 167, 2, 82, 181, 172, 3, 2, 70, 65, 2, 199, - 144, 6, 73, 2, 247, 179, 2, 65, 56, 50, 65, 66, 69, 38, 73, 2, 85, 38, - 79, 39, 87, 20, 170, 1, 65, 2, 73, 2, 85, 2, 87, 190, 158, 13, 66, 3, 86, - 8, 106, 69, 190, 158, 13, 66, 3, 86, 8, 70, 65, 190, 158, 13, 66, 3, 86, - 8, 34, 79, 190, 158, 13, 66, 3, 86, 4, 186, 158, 13, 66, 3, 86, 76, 18, - 76, 23, 77, 2, 175, 224, 12, 65, 74, 74, 32, 104, 6, 89, 82, 69, 78, 69, - 32, 201, 136, 4, 4, 83, 32, 85, 80, 8, 80, 3, 66, 82, 65, 150, 173, 11, - 84, 172, 97, 4, 68, 79, 87, 78, 1, 2, 85, 80, 2, 175, 250, 12, 78, 64, + 225, 132, 2, 3, 73, 67, 89, 2, 17, 2, 32, 72, 2, 251, 207, 13, 69, 5, + 175, 235, 13, 32, 4, 60, 6, 69, 68, 69, 83, 84, 82, 245, 207, 9, 3, 73, + 82, 65, 2, 161, 170, 11, 2, 73, 65, 4, 36, 5, 78, 65, 76, 32, 68, 59, 83, + 2, 133, 219, 13, 9, 73, 71, 73, 84, 32, 83, 72, 65, 80, 2, 191, 155, 11, + 77, 6, 38, 45, 145, 224, 9, 3, 70, 79, 82, 4, 76, 8, 66, 82, 69, 65, 75, + 73, 78, 71, 233, 164, 2, 5, 80, 79, 84, 65, 66, 2, 213, 222, 6, 2, 32, + 72, 97, 56, 2, 84, 72, 142, 11, 77, 237, 239, 10, 3, 68, 73, 67, 88, 42, + 32, 149, 206, 6, 4, 69, 65, 83, 84, 86, 96, 5, 69, 65, 83, 84, 32, 148, + 2, 6, 73, 78, 68, 73, 67, 32, 217, 1, 5, 87, 69, 83, 84, 32, 32, 82, 65, + 202, 243, 6, 80, 130, 1, 84, 190, 207, 4, 66, 38, 68, 18, 83, 251, 58, + 87, 14, 40, 4, 82, 82, 79, 87, 179, 239, 6, 78, 13, 11, 32, 10, 96, 9, + 67, 82, 79, 83, 83, 73, 78, 71, 32, 244, 2, 2, 65, 78, 130, 239, 6, 87, + 131, 145, 5, 70, 4, 242, 193, 3, 83, 187, 175, 3, 78, 20, 50, 80, 60, 3, + 81, 85, 65, 66, 82, 255, 127, 70, 2, 37, 7, 76, 65, 67, 69, 72, 79, 76, + 2, 179, 176, 4, 68, 4, 156, 176, 4, 2, 82, 84, 249, 248, 9, 5, 78, 84, + 73, 84, 89, 2, 17, 2, 85, 80, 2, 235, 162, 4, 69, 34, 82, 65, 222, 239, + 6, 80, 130, 1, 84, 190, 207, 4, 66, 38, 68, 18, 83, 251, 58, 87, 16, 34, + 78, 33, 4, 82, 82, 79, 87, 2, 253, 190, 3, 3, 68, 32, 83, 15, 11, 32, 12, + 84, 3, 84, 79, 32, 238, 234, 6, 67, 40, 3, 65, 78, 68, 234, 2, 87, 131, + 145, 5, 70, 4, 246, 235, 6, 67, 173, 216, 6, 4, 76, 79, 78, 71, 56, 54, + 32, 236, 5, 5, 67, 72, 69, 68, 32, 207, 2, 69, 38, 162, 1, 65, 132, 2, 4, + 78, 79, 82, 77, 78, 80, 46, 83, 170, 1, 84, 210, 222, 7, 69, 196, 33, 7, + 73, 68, 69, 78, 84, 73, 67, 190, 203, 4, 71, 38, 76, 135, 80, 67, 10, 88, + 3, 32, 83, 85, 52, 7, 78, 32, 69, 76, 69, 77, 69, 28, 2, 83, 89, 227, + 129, 8, 76, 4, 30, 66, 1, 3, 80, 69, 82, 2, 29, 2, 83, 69, 2, 11, 78, 2, + 139, 121, 84, 2, 37, 7, 77, 80, 84, 79, 84, 73, 67, 2, 17, 2, 65, 76, 2, + 161, 130, 8, 2, 76, 89, 4, 205, 196, 6, 14, 65, 76, 32, 83, 85, 66, 71, + 82, 79, 85, 80, 32, 79, 70, 2, 241, 129, 8, 6, 65, 82, 65, 76, 76, 69, 6, + 48, 6, 81, 85, 65, 82, 69, 32, 223, 219, 13, 73, 4, 68, 5, 73, 77, 65, + 71, 69, 1, 8, 79, 82, 73, 71, 73, 78, 65, 76, 2, 21, 3, 32, 79, 70, 2, + 207, 194, 6, 32, 4, 230, 209, 10, 82, 207, 242, 1, 73, 8, 58, 76, 40, 4, + 82, 73, 71, 72, 133, 1, 3, 85, 80, 80, 4, 36, 2, 69, 70, 133, 1, 2, 79, + 87, 2, 89, 20, 84, 32, 83, 69, 77, 73, 67, 73, 82, 67, 76, 69, 32, 87, + 73, 84, 72, 32, 84, 72, 2, 17, 2, 82, 69, 2, 243, 140, 13, 69, 2, 11, 69, + 2, 41, 8, 82, 32, 82, 73, 71, 72, 84, 45, 2, 133, 144, 3, 6, 83, 72, 65, + 68, 79, 87, 11, 34, 32, 53, 4, 66, 79, 79, 75, 4, 17, 2, 80, 65, 4, 198, + 199, 14, 71, 215, 22, 68, 5, 81, 18, 32, 87, 73, 84, 72, 32, 68, 69, 67, + 79, 82, 65, 84, 73, 86, 69, 32, 67, 2, 235, 137, 4, 79, 34, 102, 77, 196, + 182, 5, 8, 84, 32, 65, 78, 68, 32, 66, 79, 184, 255, 6, 3, 83, 72, 85, + 135, 170, 1, 76, 26, 36, 4, 66, 69, 82, 32, 247, 2, 69, 24, 58, 69, 50, + 70, 42, 83, 66, 84, 57, 4, 78, 73, 78, 69, 4, 204, 1, 3, 73, 71, 72, 21, + 3, 76, 69, 86, 4, 144, 1, 2, 79, 85, 13, 2, 73, 70, 6, 34, 73, 85, 4, 69, + 86, 69, 78, 4, 82, 88, 155, 138, 14, 71, 8, 40, 2, 72, 73, 46, 69, 21, 2, + 87, 69, 2, 11, 82, 2, 17, 2, 84, 69, 2, 11, 69, 2, 135, 200, 7, 78, 4, + 240, 199, 7, 3, 76, 86, 69, 1, 3, 78, 84, 89, 2, 187, 243, 6, 82, 142, 1, + 118, 76, 226, 3, 83, 164, 2, 5, 84, 79, 78, 69, 45, 196, 230, 7, 8, 67, + 73, 82, 67, 76, 69, 68, 32, 179, 247, 4, 68, 92, 88, 6, 69, 84, 84, 69, + 82, 32, 173, 163, 1, 10, 79, 71, 79, 71, 82, 65, 77, 32, 78, 89, 90, 130, + 2, 78, 90, 84, 166, 220, 7, 88, 182, 230, 2, 65, 144, 1, 2, 72, 65, 226, + 51, 82, 210, 147, 1, 79, 150, 61, 68, 2, 77, 2, 80, 254, 203, 1, 69, 234, + 61, 67, 2, 70, 2, 71, 2, 75, 2, 76, 2, 81, 2, 83, 2, 86, 2, 89, 2, 90, + 186, 2, 73, 2, 85, 3, 87, 22, 86, 84, 174, 200, 12, 80, 230, 137, 2, 67, + 2, 75, 2, 81, 2, 82, 2, 89, 187, 2, 65, 6, 142, 210, 14, 83, 2, 88, 187, + 2, 65, 14, 64, 4, 73, 71, 78, 32, 189, 1, 7, 89, 76, 76, 65, 66, 76, 69, + 12, 56, 4, 70, 79, 82, 32, 129, 213, 13, 4, 88, 87, 32, 88, 10, 204, 9, + 2, 76, 79, 230, 164, 3, 84, 156, 94, 8, 73, 78, 86, 69, 82, 84, 69, 66, + 232, 144, 4, 3, 65, 78, 73, 195, 177, 2, 80, 2, 177, 132, 12, 4, 32, 76, + 69, 78, 14, 250, 209, 14, 66, 2, 68, 2, 71, 2, 74, 2, 77, 2, 83, 3, 86, + 254, 14, 226, 2, 66, 226, 1, 67, 226, 5, 71, 244, 6, 4, 73, 76, 32, 68, + 22, 76, 198, 69, 78, 150, 5, 80, 234, 7, 82, 242, 10, 83, 188, 8, 2, 84, + 84, 154, 8, 85, 248, 1, 3, 86, 69, 82, 254, 170, 2, 89, 148, 151, 4, 14, + 70, 70, 73, 67, 69, 32, 66, 85, 73, 76, 68, 73, 78, 71, 154, 185, 1, 72, + 146, 132, 2, 75, 210, 250, 1, 68, 194, 64, 77, 246, 9, 87, 211, 139, 1, + 88, 10, 132, 1, 6, 76, 73, 81, 85, 69, 32, 252, 158, 2, 9, 83, 69, 82, + 86, 69, 82, 32, 69, 89, 189, 29, 8, 74, 69, 67, 84, 32, 82, 69, 80, 6, + 172, 203, 4, 13, 65, 78, 71, 76, 69, 32, 79, 80, 69, 78, 73, 78, 71, 171, + 241, 1, 72, 28, 56, 2, 82, 32, 234, 4, 84, 225, 198, 5, 3, 67, 85, 76, + 22, 156, 1, 11, 65, 77, 79, 85, 78, 84, 32, 79, 70, 32, 67, 22, 66, 198, + 1, 67, 118, 68, 106, 70, 0, 10, 73, 78, 86, 69, 82, 84, 69, 68, 32, 70, + 243, 241, 12, 72, 2, 203, 184, 5, 72, 6, 136, 1, 15, 82, 65, 78, 67, 72, + 32, 66, 65, 78, 75, 32, 73, 68, 69, 78, 156, 131, 5, 5, 69, 76, 84, 32, + 66, 205, 144, 6, 3, 79, 87, 32, 2, 21, 3, 84, 73, 70, 2, 11, 73, 2, 131, + 132, 11, 67, 4, 92, 17, 85, 83, 84, 79, 77, 69, 82, 32, 65, 67, 67, 79, + 85, 78, 84, 32, 78, 243, 201, 1, 72, 2, 159, 161, 6, 85, 4, 44, 5, 79, + 85, 66, 76, 69, 147, 221, 12, 65, 2, 11, 32, 2, 11, 66, 2, 157, 163, 7, + 3, 65, 67, 75, 2, 191, 134, 14, 79, 4, 160, 251, 8, 4, 65, 71, 79, 78, + 197, 195, 3, 2, 79, 80, 60, 48, 4, 72, 65, 77, 32, 185, 195, 14, 2, 79, + 78, 58, 122, 70, 0, 10, 82, 69, 86, 69, 82, 83, 69, 68, 32, 70, 36, 7, + 76, 69, 84, 84, 69, 82, 32, 149, 254, 3, 3, 83, 80, 65, 2, 161, 139, 4, + 4, 69, 65, 84, 72, 52, 196, 1, 2, 65, 73, 22, 66, 2, 80, 34, 67, 36, 2, + 69, 65, 64, 2, 70, 69, 22, 71, 22, 73, 58, 76, 2, 82, 22, 78, 46, 79, 34, + 83, 46, 85, 222, 184, 1, 77, 170, 9, 68, 189, 227, 11, 3, 84, 73, 78, 2, + 155, 179, 14, 76, 2, 11, 69, 2, 219, 190, 12, 73, 4, 226, 151, 8, 69, + 183, 161, 4, 79, 6, 138, 1, 66, 2, 68, 153, 143, 4, 6, 77, 72, 65, 78, + 67, 72, 2, 247, 175, 9, 65, 2, 183, 187, 13, 79, 4, 32, 2, 79, 68, 191, + 194, 13, 70, 2, 195, 137, 8, 72, 2, 243, 154, 12, 85, 4, 132, 172, 13, 3, + 71, 69, 65, 247, 69, 73, 4, 218, 241, 13, 78, 227, 79, 82, 4, 202, 225, + 12, 65, 229, 93, 3, 84, 82, 65, 6, 60, 5, 73, 76, 76, 69, 65, 186, 187, + 12, 65, 251, 132, 2, 82, 2, 207, 240, 13, 78, 2, 247, 148, 13, 82, 182, + 8, 38, 32, 142, 11, 68, 255, 183, 13, 73, 184, 1, 64, 6, 67, 72, 73, 75, + 73, 32, 205, 6, 5, 79, 78, 65, 76, 32, 96, 132, 1, 7, 76, 69, 84, 84, 69, + 82, 32, 148, 3, 2, 77, 85, 62, 71, 74, 80, 164, 216, 3, 2, 82, 69, 202, + 237, 8, 68, 211, 173, 1, 65, 60, 50, 65, 98, 69, 54, 73, 50, 76, 62, 79, + 51, 85, 16, 50, 65, 210, 188, 14, 78, 86, 71, 2, 76, 3, 84, 8, 162, 189, + 14, 74, 2, 75, 2, 77, 3, 87, 8, 214, 246, 13, 68, 198, 13, 82, 222, 56, + 78, 3, 80, 8, 250, 170, 14, 78, 202, 17, 72, 2, 82, 3, 83, 12, 162, 170, + 10, 65, 242, 145, 4, 69, 2, 73, 2, 79, 3, 85, 8, 170, 159, 14, 84, 174, + 28, 66, 2, 72, 3, 86, 8, 198, 235, 13, 78, 226, 79, 67, 2, 68, 3, 89, 4, + 56, 2, 45, 71, 209, 206, 12, 6, 32, 84, 84, 85, 68, 68, 2, 205, 206, 12, + 13, 65, 65, 72, 76, 65, 65, 32, 84, 84, 85, 68, 68, 65, 6, 84, 11, 85, + 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 177, 224, 6, 4, 72, 65, 65, 82, + 4, 48, 8, 68, 79, 85, 66, 76, 69, 32, 77, 3, 77, 2, 185, 242, 13, 3, 85, + 67, 65, 88, 100, 7, 76, 69, 84, 84, 69, 82, 32, 192, 2, 5, 83, 73, 71, + 78, 32, 206, 193, 8, 65, 207, 255, 3, 68, 60, 42, 65, 54, 69, 58, 73, 54, + 79, 59, 85, 13, 178, 183, 14, 66, 2, 68, 2, 72, 2, 76, 3, 87, 13, 158, + 231, 13, 78, 226, 79, 67, 2, 71, 2, 72, 3, 83, 13, 238, 205, 8, 84, 218, + 232, 5, 68, 2, 78, 3, 80, 13, 182, 253, 13, 82, 138, 56, 78, 86, 77, 2, + 79, 3, 89, 13, 186, 239, 13, 68, 218, 52, 78, 202, 17, 74, 2, 75, 3, 82, + 6, 58, 73, 144, 246, 12, 4, 72, 79, 68, 68, 239, 153, 1, 77, 2, 131, 182, + 1, 75, 252, 6, 34, 32, 165, 57, 3, 69, 82, 32, 246, 6, 240, 2, 8, 67, 72, + 73, 78, 69, 83, 69, 32, 44, 10, 72, 85, 78, 71, 65, 82, 73, 65, 78, 32, + 128, 7, 7, 73, 84, 65, 76, 73, 67, 32, 212, 4, 14, 78, 79, 82, 84, 72, + 32, 65, 82, 65, 66, 73, 65, 78, 32, 196, 4, 3, 80, 69, 82, 216, 13, 2, + 83, 79, 144, 13, 14, 84, 85, 82, 75, 73, 67, 32, 76, 69, 84, 84, 69, 82, + 32, 132, 7, 7, 85, 89, 71, 72, 85, 82, 32, 171, 225, 11, 75, 4, 146, 139, + 12, 73, 241, 96, 3, 72, 79, 79, 216, 1, 96, 6, 67, 65, 80, 73, 84, 65, 0, + 4, 83, 77, 65, 76, 233, 5, 7, 78, 85, 77, 66, 69, 82, 32, 102, 45, 9, 76, + 32, 76, 69, 84, 84, 69, 82, 32, 102, 214, 1, 65, 54, 69, 168, 2, 10, 78, + 73, 75, 79, 76, 83, 66, 85, 82, 71, 0, 9, 82, 85, 68, 73, 77, 69, 78, 84, + 65, 42, 79, 32, 5, 83, 72, 79, 82, 84, 22, 85, 168, 242, 3, 5, 67, 76, + 79, 83, 69, 243, 171, 8, 73, 11, 154, 240, 12, 77, 218, 119, 78, 162, 70, + 65, 3, 75, 63, 178, 1, 77, 22, 78, 78, 83, 182, 130, 10, 67, 254, 23, 71, + 2, 76, 2, 84, 198, 135, 2, 90, 218, 137, 2, 66, 2, 68, 2, 69, 2, 70, 2, + 72, 2, 74, 2, 75, 2, 80, 2, 82, 3, 86, 5, 171, 172, 14, 80, 11, 34, 84, + 246, 171, 14, 67, 3, 89, 5, 197, 149, 2, 5, 45, 83, 72, 65, 80, 5, 203, + 171, 14, 90, 4, 11, 32, 4, 214, 148, 14, 79, 3, 85, 7, 186, 148, 14, 69, + 215, 22, 79, 2, 131, 237, 13, 32, 9, 194, 167, 14, 78, 154, 3, 83, 3, 85, + 12, 210, 4, 70, 242, 131, 6, 79, 239, 203, 6, 84, 78, 80, 7, 76, 69, 84, + 84, 69, 82, 32, 169, 3, 8, 78, 85, 77, 69, 82, 65, 76, 32, 70, 190, 1, + 69, 90, 75, 50, 83, 36, 3, 78, 79, 82, 202, 207, 10, 85, 186, 202, 1, 73, + 166, 140, 1, 80, 2, 84, 150, 83, 67, 186, 22, 66, 2, 68, 2, 72, 2, 86, 2, + 89, 2, 90, 214, 22, 65, 3, 79, 23, 214, 254, 9, 83, 194, 159, 2, 82, 254, + 203, 1, 75, 222, 61, 70, 2, 76, 2, 77, 3, 78, 8, 194, 144, 14, 72, 214, + 22, 65, 2, 69, 3, 85, 4, 32, 2, 79, 85, 243, 143, 14, 72, 2, 29, 5, 84, + 72, 69, 82, 78, 2, 253, 154, 13, 2, 32, 84, 8, 38, 70, 222, 207, 12, 84, + 215, 58, 79, 4, 11, 73, 4, 242, 181, 12, 70, 143, 217, 1, 86, 64, 76, 7, + 76, 69, 84, 84, 69, 82, 32, 209, 3, 7, 78, 85, 77, 66, 69, 82, 32, 58, + 210, 1, 65, 38, 71, 38, 72, 30, 75, 38, 84, 74, 90, 234, 106, 77, 140, + 158, 3, 2, 69, 83, 238, 162, 3, 66, 2, 70, 2, 82, 2, 89, 182, 203, 3, 78, + 154, 237, 1, 76, 210, 58, 68, 146, 1, 81, 134, 3, 87, 131, 56, 83, 4, + 134, 194, 3, 76, 167, 145, 10, 73, 4, 254, 243, 12, 72, 191, 156, 1, 69, + 4, 178, 161, 14, 65, 3, 69, 4, 166, 180, 7, 72, 223, 236, 5, 65, 8, 34, + 72, 210, 160, 14, 65, 3, 69, 4, 142, 150, 13, 65, 195, 138, 1, 69, 4, 11, + 65, 4, 206, 209, 13, 73, 227, 79, 72, 6, 190, 153, 6, 84, 163, 236, 6, + 79, 180, 1, 64, 11, 77, 73, 67, 32, 76, 69, 84, 84, 69, 82, 32, 183, 5, + 83, 76, 228, 1, 2, 67, 72, 22, 68, 66, 69, 22, 73, 30, 77, 2, 78, 30, 80, + 22, 83, 70, 84, 50, 86, 38, 89, 94, 90, 254, 195, 6, 76, 2, 82, 214, 227, + 2, 71, 150, 170, 2, 79, 222, 208, 1, 66, 246, 40, 65, 254, 31, 75, 174, + 45, 72, 187, 2, 85, 2, 215, 216, 7, 69, 6, 26, 90, 183, 138, 14, 79, 4, + 134, 167, 9, 72, 187, 210, 4, 73, 5, 231, 157, 14, 70, 7, 210, 157, 14, + 65, 3, 69, 2, 213, 134, 14, 2, 69, 78, 2, 131, 198, 6, 69, 6, 26, 72, + 151, 137, 14, 73, 4, 228, 165, 9, 3, 67, 72, 79, 3, 79, 4, 26, 83, 211, + 136, 14, 65, 2, 191, 247, 13, 73, 4, 142, 165, 9, 79, 171, 190, 4, 69, + 14, 60, 2, 69, 82, 218, 178, 8, 65, 146, 215, 5, 82, 203, 17, 85, 7, 174, + 155, 14, 73, 3, 85, 4, 142, 164, 9, 72, 187, 210, 4, 65, 104, 88, 4, 73, + 65, 78, 32, 241, 5, 13, 79, 78, 65, 76, 32, 67, 79, 77, 80, 85, 84, 69, + 82, 100, 80, 7, 78, 85, 77, 66, 69, 82, 32, 80, 5, 83, 73, 71, 78, 32, + 251, 222, 10, 87, 10, 42, 84, 234, 188, 8, 72, 255, 192, 4, 79, 6, 238, + 230, 1, 87, 199, 226, 11, 69, 88, 210, 1, 65, 86, 66, 62, 68, 98, 74, 2, + 86, 30, 84, 42, 88, 182, 111, 71, 2, 75, 2, 78, 2, 82, 150, 206, 9, 77, + 146, 143, 3, 83, 218, 69, 67, 2, 70, 2, 72, 2, 76, 2, 80, 2, 89, 2, 90, + 186, 2, 73, 3, 85, 9, 45, 9, 85, 82, 65, 77, 65, 90, 68, 65, 65, 7, 170, + 239, 6, 45, 139, 165, 7, 72, 6, 38, 65, 213, 177, 10, 3, 85, 85, 77, 5, + 231, 147, 14, 71, 10, 34, 65, 234, 149, 14, 73, 3, 85, 7, 37, 7, 72, 89, + 65, 65, 85, 83, 72, 5, 255, 237, 6, 45, 4, 170, 149, 14, 65, 3, 73, 6, + 214, 146, 14, 72, 186, 2, 65, 3, 85, 4, 152, 220, 11, 8, 83, 72, 65, 65, + 89, 65, 84, 72, 207, 184, 2, 65, 5, 181, 147, 5, 31, 32, 87, 73, 84, 72, + 32, 77, 79, 78, 73, 84, 79, 82, 32, 73, 78, 32, 80, 79, 82, 84, 82, 65, + 73, 84, 32, 79, 82, 73, 69, 78, 144, 1, 92, 6, 71, 68, 73, 65, 78, 32, + 141, 7, 12, 85, 84, 72, 32, 65, 82, 65, 66, 73, 65, 78, 32, 80, 58, 70, + 74, 76, 145, 5, 7, 78, 85, 77, 66, 69, 82, 32, 2, 11, 82, 2, 201, 237, + 11, 10, 65, 67, 84, 73, 79, 78, 32, 79, 78, 69, 60, 76, 6, 69, 84, 84, + 69, 82, 32, 149, 4, 8, 73, 71, 65, 84, 85, 82, 69, 32, 58, 234, 1, 65, + 96, 6, 70, 73, 78, 65, 76, 32, 200, 1, 5, 82, 69, 83, 72, 45, 162, 216, + 1, 76, 194, 170, 4, 71, 90, 90, 34, 83, 66, 89, 198, 207, 1, 72, 234, 5, + 75, 174, 81, 66, 170, 225, 4, 78, 134, 2, 84, 2, 87, 218, 103, 80, 171, + 4, 77, 6, 26, 76, 131, 143, 13, 89, 4, 192, 133, 6, 8, 84, 69, 82, 78, + 65, 84, 69, 32, 155, 214, 1, 69, 18, 116, 3, 78, 85, 78, 0, 5, 83, 65, + 68, 72, 69, 0, 3, 84, 65, 87, 190, 216, 1, 65, 134, 211, 6, 66, 135, 203, + 5, 72, 5, 41, 8, 32, 87, 73, 84, 72, 32, 86, 69, 2, 133, 220, 5, 4, 82, + 84, 73, 67, 2, 253, 215, 1, 6, 65, 89, 73, 78, 45, 68, 18, 42, 84, 234, + 131, 6, 79, 255, 148, 6, 70, 10, 42, 72, 162, 217, 1, 87, 199, 226, 11, + 69, 4, 162, 153, 12, 82, 159, 2, 73, 64, 60, 7, 76, 69, 84, 84, 69, 82, + 32, 245, 3, 3, 78, 85, 77, 58, 202, 1, 65, 38, 68, 74, 71, 34, 75, 34, + 83, 78, 84, 254, 43, 90, 254, 166, 1, 76, 50, 81, 214, 205, 1, 82, 246, + 221, 2, 89, 198, 207, 1, 72, 150, 87, 66, 170, 225, 4, 78, 134, 2, 87, + 218, 103, 70, 171, 4, 77, 4, 146, 168, 3, 76, 167, 145, 10, 89, 6, 32, 2, + 72, 65, 151, 212, 1, 65, 4, 246, 166, 8, 76, 207, 180, 5, 68, 4, 134, 45, + 72, 203, 209, 5, 73, 4, 146, 213, 7, 65, 163, 81, 72, 8, 26, 65, 255, + 135, 13, 72, 6, 218, 198, 7, 77, 234, 147, 6, 68, 143, 45, 84, 8, 230, + 90, 72, 194, 167, 11, 69, 219, 134, 1, 65, 6, 56, 4, 66, 69, 82, 32, 145, + 205, 13, 4, 69, 82, 73, 67, 4, 26, 70, 235, 234, 12, 79, 2, 139, 149, 12, + 73, 146, 1, 80, 7, 79, 82, 75, 72, 79, 78, 32, 197, 3, 8, 89, 69, 78, 73, + 83, 69, 73, 32, 84, 54, 65, 202, 1, 69, 122, 73, 30, 79, 135, 151, 12, + 66, 45, 106, 69, 170, 243, 9, 83, 226, 144, 4, 66, 2, 68, 2, 71, 2, 76, + 2, 78, 2, 81, 2, 82, 2, 84, 3, 89, 20, 134, 132, 14, 66, 2, 68, 2, 71, 2, + 75, 2, 76, 2, 78, 2, 82, 2, 83, 2, 84, 3, 89, 20, 74, 78, 182, 230, 13, + 76, 158, 27, 83, 146, 1, 67, 2, 77, 2, 80, 3, 90, 8, 222, 130, 14, 67, 2, + 71, 2, 84, 3, 89, 7, 178, 130, 14, 67, 3, 81, 13, 130, 3, 69, 150, 255, + 13, 80, 2, 81, 3, 84, 62, 38, 65, 170, 1, 69, 86, 73, 23, 79, 39, 98, 69, + 206, 255, 13, 83, 62, 78, 86, 66, 2, 68, 2, 71, 2, 76, 2, 81, 2, 82, 2, + 84, 3, 89, 17, 218, 130, 12, 78, 130, 254, 1, 66, 2, 71, 2, 75, 2, 84, 3, + 89, 15, 46, 78, 218, 254, 13, 83, 146, 1, 67, 3, 90, 6, 230, 255, 13, 67, + 2, 84, 3, 89, 5, 195, 255, 13, 81, 6, 26, 69, 151, 255, 13, 81, 5, 147, + 255, 13, 75, 52, 148, 1, 10, 67, 79, 77, 66, 73, 78, 73, 78, 71, 32, 36, + 7, 76, 69, 84, 84, 69, 82, 32, 233, 1, 12, 80, 85, 78, 67, 84, 85, 65, + 84, 73, 79, 78, 32, 8, 222, 241, 5, 84, 243, 231, 3, 68, 36, 230, 200, 1, + 65, 186, 206, 1, 82, 222, 220, 2, 76, 58, 90, 34, 83, 66, 89, 168, 210, + 1, 6, 70, 73, 78, 65, 76, 32, 0, 6, 71, 73, 77, 69, 76, 45, 134, 3, 75, + 174, 81, 66, 170, 225, 4, 78, 134, 2, 84, 2, 87, 218, 103, 80, 171, 4, + 77, 8, 56, 4, 84, 87, 79, 32, 174, 212, 11, 70, 187, 131, 1, 66, 4, 158, + 228, 12, 66, 75, 68, 6, 146, 181, 1, 87, 136, 160, 3, 3, 65, 68, 85, 207, + 217, 7, 77, 22, 212, 1, 34, 32, 87, 73, 84, 72, 32, 69, 88, 67, 76, 65, + 77, 65, 84, 73, 79, 78, 32, 77, 65, 82, 75, 32, 87, 73, 84, 72, 32, 76, + 69, 70, 84, 32, 82, 44, 7, 67, 79, 77, 73, 78, 71, 32, 194, 1, 69, 151, + 167, 13, 73, 2, 21, 3, 73, 71, 72, 2, 203, 250, 9, 84, 10, 148, 1, 6, 65, + 85, 84, 79, 77, 79, 20, 7, 70, 73, 82, 69, 32, 69, 78, 144, 225, 2, 2, + 84, 65, 148, 247, 8, 6, 80, 79, 76, 73, 67, 69, 143, 22, 66, 2, 223, 206, + 11, 66, 2, 215, 219, 12, 71, 8, 70, 32, 229, 191, 12, 11, 45, 80, 73, 69, + 67, 69, 32, 83, 87, 73, 77, 6, 40, 4, 68, 79, 84, 32, 175, 173, 10, 66, + 4, 26, 79, 135, 175, 10, 76, 2, 129, 234, 2, 11, 86, 69, 82, 32, 84, 87, + 79, 32, 68, 79, 84, 46, 82, 69, 188, 6, 2, 84, 73, 188, 229, 11, 5, 72, + 73, 85, 67, 72, 147, 183, 1, 80, 36, 70, 78, 201, 5, 12, 82, 65, 84, 73, + 78, 71, 32, 83, 89, 83, 84, 69, 34, 22, 32, 139, 4, 45, 28, 108, 2, 66, + 79, 32, 7, 67, 69, 78, 84, 82, 69, 32, 114, 70, 70, 72, 42, 77, 142, 139, + 7, 83, 135, 244, 3, 76, 4, 242, 239, 13, 79, 155, 3, 88, 8, 50, 84, 174, + 216, 11, 66, 222, 2, 65, 135, 84, 67, 2, 37, 7, 69, 65, 82, 68, 82, 79, + 80, 2, 143, 218, 11, 45, 4, 44, 5, 73, 76, 69, 32, 70, 243, 131, 2, 79, + 2, 239, 131, 2, 79, 2, 17, 2, 65, 78, 2, 151, 191, 10, 68, 4, 57, 12, 65, + 73, 76, 66, 79, 88, 32, 87, 73, 84, 72, 32, 4, 44, 3, 76, 79, 87, 21, 4, + 82, 65, 73, 83, 2, 17, 2, 69, 82, 2, 129, 132, 12, 2, 69, 68, 6, 108, 15, + 67, 73, 82, 67, 85, 73, 84, 45, 79, 85, 84, 80, 85, 84, 32, 221, 204, 12, + 6, 79, 85, 84, 76, 73, 78, 4, 18, 72, 3, 76, 2, 161, 192, 1, 4, 45, 84, + 89, 80, 2, 169, 222, 12, 6, 77, 32, 67, 79, 77, 77, 6, 64, 2, 79, 78, + 253, 177, 1, 8, 67, 65, 76, 32, 68, 73, 83, 67, 2, 247, 207, 11, 32, 208, + 1, 104, 3, 65, 78, 71, 66, 67, 34, 73, 212, 8, 5, 78, 65, 84, 69, 32, 32, + 3, 84, 72, 79, 199, 176, 5, 32, 6, 28, 2, 69, 32, 167, 22, 85, 4, 198, + 150, 12, 66, 203, 78, 72, 4, 186, 174, 13, 85, 223, 61, 65, 188, 1, 48, + 5, 71, 73, 78, 65, 76, 21, 3, 89, 65, 32, 2, 183, 133, 1, 32, 186, 1, + 106, 65, 30, 70, 250, 1, 73, 32, 7, 76, 69, 84, 84, 69, 82, 32, 142, 2, + 83, 218, 1, 86, 159, 240, 11, 68, 4, 182, 137, 10, 73, 3, 85, 12, 41, 8, + 82, 65, 67, 84, 73, 79, 78, 32, 12, 56, 4, 79, 78, 69, 32, 81, 6, 84, 72, + 82, 69, 69, 32, 8, 42, 83, 206, 226, 11, 69, 46, 72, 47, 81, 2, 217, 227, + 11, 4, 73, 88, 84, 69, 4, 26, 83, 227, 228, 11, 81, 2, 145, 136, 8, 4, + 73, 88, 84, 69, 2, 233, 196, 12, 3, 83, 83, 72, 104, 226, 1, 82, 130, + 238, 6, 89, 178, 154, 3, 65, 38, 68, 114, 84, 46, 86, 186, 5, 85, 186, + 202, 1, 73, 42, 76, 226, 195, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, + 2, 75, 2, 80, 138, 69, 72, 2, 77, 2, 87, 186, 2, 69, 3, 79, 6, 234, 227, + 13, 72, 2, 82, 187, 2, 65, 18, 112, 20, 69, 81, 85, 69, 78, 67, 69, 32, + 70, 79, 82, 32, 76, 69, 84, 84, 69, 82, 32, 82, 29, 4, 73, 71, 78, 32, 4, + 206, 226, 13, 72, 3, 82, 14, 138, 199, 9, 67, 98, 78, 190, 66, 65, 250, + 239, 1, 79, 195, 167, 1, 86, 26, 49, 10, 79, 87, 69, 76, 32, 83, 73, 71, + 78, 32, 26, 206, 140, 10, 65, 38, 85, 22, 86, 166, 202, 1, 73, 198, 140, + 2, 69, 3, 79, 4, 194, 220, 6, 76, 243, 30, 82, 4, 240, 245, 3, 2, 68, 79, + 139, 183, 2, 71, 226, 1, 76, 4, 65, 71, 69, 32, 140, 4, 6, 77, 65, 78, + 89, 65, 32, 251, 221, 13, 67, 144, 1, 56, 6, 67, 65, 80, 73, 84, 65, 1, + 4, 83, 77, 65, 76, 72, 45, 9, 76, 32, 76, 69, 84, 84, 69, 82, 32, 72, + 194, 1, 65, 38, 69, 90, 75, 42, 79, 22, 84, 246, 229, 6, 72, 254, 250, 4, + 67, 2, 68, 2, 71, 234, 181, 1, 83, 2, 90, 130, 3, 66, 138, 66, 76, 2, 77, + 2, 78, 2, 80, 2, 87, 186, 2, 73, 3, 85, 9, 226, 225, 11, 73, 239, 253, 1, + 72, 15, 26, 72, 179, 143, 13, 73, 10, 134, 202, 9, 84, 226, 151, 2, 67, + 242, 250, 1, 75, 3, 80, 6, 154, 220, 13, 72, 2, 89, 187, 2, 65, 5, 203, + 142, 13, 73, 6, 214, 150, 13, 83, 195, 71, 65, 80, 52, 7, 76, 69, 84, 84, + 69, 82, 32, 187, 233, 11, 68, 60, 246, 1, 65, 38, 67, 22, 68, 38, 75, 34, + 83, 30, 77, 162, 241, 2, 81, 226, 159, 8, 79, 148, 125, 2, 76, 65, 236, + 75, 2, 78, 85, 134, 2, 87, 142, 62, 69, 234, 61, 66, 2, 70, 2, 71, 2, 72, + 2, 74, 2, 82, 2, 84, 2, 88, 2, 89, 186, 2, 73, 3, 85, 7, 194, 250, 2, 76, + 135, 225, 10, 65, 2, 183, 221, 12, 65, 4, 222, 197, 8, 69, 251, 146, 5, + 72, 4, 186, 217, 12, 65, 251, 126, 72, 4, 26, 72, 179, 218, 13, 65, 2, + 219, 218, 12, 73, 124, 68, 11, 79, 77, 65, 78, 32, 83, 73, 89, 65, 81, + 32, 251, 160, 13, 69, 122, 172, 1, 17, 65, 76, 84, 69, 82, 78, 65, 84, + 69, 32, 78, 85, 77, 66, 69, 82, 32, 200, 1, 13, 70, 82, 65, 67, 84, 73, + 79, 78, 32, 79, 78, 69, 32, 48, 3, 77, 65, 82, 47, 78, 26, 54, 70, 50, + 83, 46, 84, 214, 187, 12, 78, 239, 112, 69, 6, 248, 207, 5, 3, 79, 85, + 82, 159, 139, 7, 73, 6, 200, 207, 5, 2, 73, 88, 155, 149, 6, 69, 10, 226, + 184, 2, 69, 12, 2, 87, 79, 247, 171, 9, 72, 4, 26, 83, 175, 208, 11, 72, + 2, 155, 209, 11, 73, 2, 11, 82, 2, 11, 65, 2, 247, 137, 12, 84, 90, 33, + 6, 85, 77, 66, 69, 82, 32, 90, 58, 69, 66, 70, 94, 78, 26, 83, 78, 84, + 179, 177, 5, 79, 10, 25, 4, 73, 71, 72, 84, 11, 226, 182, 2, 89, 227, + 193, 5, 32, 20, 18, 73, 35, 79, 10, 166, 2, 70, 195, 176, 5, 86, 10, 134, + 2, 82, 205, 176, 5, 2, 85, 82, 10, 65, 3, 73, 78, 69, 20, 40, 4, 69, 86, + 69, 78, 1, 2, 73, 88, 11, 166, 1, 84, 219, 245, 7, 32, 24, 34, 72, 50, + 87, 163, 180, 2, 69, 10, 34, 73, 245, 176, 5, 2, 82, 69, 4, 51, 82, 10, + 26, 69, 219, 176, 5, 79, 4, 11, 78, 4, 11, 84, 4, 247, 179, 2, 89, 84, + 34, 84, 217, 177, 11, 2, 78, 67, 82, 42, 66, 37, 6, 76, 73, 78, 69, 68, + 32, 2, 133, 195, 12, 4, 79, 88, 32, 84, 80, 92, 7, 76, 65, 84, 73, 78, + 32, 67, 242, 194, 6, 87, 182, 243, 4, 66, 234, 14, 71, 159, 23, 68, 54, + 186, 174, 7, 65, 215, 222, 4, 82, 12, 18, 72, 43, 76, 2, 17, 2, 69, 65, + 2, 239, 188, 12, 84, 10, 32, 2, 65, 80, 255, 179, 12, 73, 9, 29, 5, 80, + 73, 78, 71, 32, 6, 40, 6, 87, 72, 73, 84, 69, 32, 51, 66, 4, 44, 5, 65, + 78, 68, 32, 66, 151, 138, 10, 83, 2, 253, 137, 10, 4, 76, 65, 67, 75, + 152, 13, 150, 1, 65, 206, 65, 68, 30, 69, 134, 13, 72, 138, 25, 73, 202, + 4, 76, 160, 15, 2, 78, 80, 54, 79, 238, 8, 82, 210, 15, 83, 186, 6, 85, + 239, 177, 12, 77, 158, 6, 134, 2, 68, 38, 71, 212, 1, 11, 72, 65, 87, 72, + 32, 72, 77, 79, 78, 71, 32, 134, 23, 76, 130, 6, 78, 58, 82, 196, 22, 2, + 83, 83, 132, 2, 10, 85, 32, 67, 73, 78, 32, 72, 65, 85, 32, 176, 7, 3, + 87, 32, 80, 152, 252, 7, 2, 67, 75, 233, 141, 5, 4, 80, 69, 82, 67, 5, + 253, 185, 1, 4, 68, 73, 78, 71, 14, 26, 69, 163, 165, 13, 79, 13, 34, 32, + 130, 202, 13, 82, 3, 83, 6, 72, 6, 87, 73, 84, 72, 32, 67, 181, 159, 4, + 6, 70, 65, 67, 73, 78, 71, 4, 50, 85, 145, 237, 7, 6, 73, 82, 67, 76, 69, + 68, 2, 175, 189, 12, 82, 254, 1, 190, 1, 67, 160, 6, 9, 77, 65, 82, 75, + 32, 67, 73, 77, 32, 160, 1, 7, 78, 85, 77, 66, 69, 82, 32, 244, 1, 5, 83, + 73, 71, 78, 32, 144, 10, 7, 86, 79, 87, 69, 76, 32, 75, 223, 191, 11, 68, + 78, 92, 9, 76, 65, 78, 32, 83, 73, 71, 78, 32, 137, 3, 9, 79, 78, 83, 79, + 78, 65, 78, 84, 32, 38, 132, 1, 2, 72, 65, 38, 75, 46, 76, 34, 84, 82, + 86, 30, 89, 148, 9, 2, 88, 89, 172, 5, 2, 80, 72, 174, 1, 70, 165, 2, 2, + 77, 85, 4, 170, 198, 2, 87, 151, 255, 10, 77, 6, 246, 15, 72, 178, 150, + 13, 79, 159, 14, 87, 4, 210, 12, 65, 195, 250, 12, 73, 8, 22, 83, 247, 9, + 72, 6, 160, 197, 2, 3, 72, 69, 69, 158, 193, 9, 65, 3, 87, 4, 234, 196, + 2, 65, 3, 87, 4, 206, 196, 2, 65, 175, 185, 10, 69, 40, 122, 67, 38, 72, + 46, 78, 60, 2, 80, 76, 2, 81, 222, 222, 2, 76, 2, 77, 2, 82, 2, 86, 2, + 88, 2, 89, 247, 189, 10, 65, 4, 230, 223, 2, 72, 247, 189, 10, 65, 6, + 194, 223, 2, 76, 2, 78, 247, 189, 10, 65, 12, 58, 67, 22, 84, 202, 222, + 2, 75, 2, 76, 247, 189, 10, 65, 2, 219, 222, 2, 72, 4, 198, 222, 2, 72, + 3, 83, 14, 42, 75, 50, 83, 38, 84, 167, 175, 13, 72, 4, 26, 72, 231, 130, + 13, 69, 2, 175, 162, 6, 65, 4, 154, 131, 12, 85, 147, 189, 1, 79, 4, 142, + 130, 12, 85, 215, 18, 65, 14, 52, 7, 72, 85, 78, 68, 82, 69, 68, 38, 84, + 79, 77, 4, 108, 2, 32, 77, 195, 190, 13, 83, 8, 24, 2, 69, 78, 51, 82, 6, + 26, 32, 215, 190, 13, 83, 4, 18, 66, 35, 84, 2, 253, 213, 4, 3, 73, 76, + 76, 2, 11, 72, 2, 213, 251, 9, 3, 79, 85, 83, 72, 188, 1, 4, 67, 73, 77, + 32, 246, 2, 72, 32, 3, 73, 66, 32, 22, 77, 86, 78, 50, 84, 124, 4, 86, + 79, 83, 32, 198, 1, 88, 208, 1, 6, 90, 87, 74, 32, 84, 72, 142, 178, 1, + 76, 223, 227, 4, 65, 16, 174, 1, 67, 84, 5, 78, 82, 69, 83, 32, 22, 84, + 164, 252, 11, 7, 80, 85, 66, 32, 68, 65, 87, 153, 189, 1, 16, 72, 65, 73, + 83, 32, 76, 85, 83, 32, 78, 84, 79, 71, 32, 78, 84, 4, 48, 7, 85, 65, 77, + 32, 84, 83, 72, 255, 3, 72, 2, 11, 79, 2, 175, 187, 2, 79, 2, 195, 184, + 1, 84, 6, 52, 3, 88, 87, 86, 161, 151, 10, 4, 83, 79, 86, 32, 5, 209, + 155, 6, 4, 32, 67, 72, 87, 4, 190, 72, 78, 171, 221, 12, 76, 2, 143, 252, + 11, 89, 6, 40, 4, 69, 69, 74, 32, 135, 251, 12, 85, 4, 128, 3, 2, 84, 83, + 57, 2, 83, 85, 4, 26, 84, 187, 252, 8, 81, 2, 135, 185, 2, 85, 6, 196, 1, + 7, 88, 72, 69, 69, 74, 32, 67, 224, 178, 8, 12, 72, 73, 82, 68, 45, 83, + 84, 65, 71, 69, 32, 72, 251, 222, 4, 65, 14, 54, 70, 22, 83, 30, 84, 166, + 69, 76, 195, 251, 7, 78, 2, 167, 164, 13, 69, 2, 173, 152, 6, 2, 69, 69, + 6, 42, 72, 29, 6, 83, 72, 65, 66, 32, 67, 4, 82, 73, 207, 164, 13, 79, 2, + 175, 209, 5, 69, 14, 34, 73, 22, 89, 175, 172, 11, 65, 2, 171, 247, 11, + 65, 10, 40, 4, 69, 69, 77, 32, 243, 149, 13, 79, 8, 84, 3, 78, 84, 88, + 252, 149, 6, 2, 84, 79, 154, 173, 2, 82, 245, 178, 3, 2, 70, 65, 2, 251, + 149, 6, 73, 2, 227, 180, 2, 65, 56, 50, 65, 66, 69, 38, 73, 2, 85, 38, + 79, 39, 87, 20, 170, 1, 65, 2, 73, 2, 85, 2, 87, 134, 178, 13, 66, 3, 86, + 8, 106, 69, 134, 178, 13, 66, 3, 86, 8, 70, 65, 134, 178, 13, 66, 3, 86, + 8, 34, 79, 134, 178, 13, 66, 3, 86, 4, 130, 178, 13, 66, 3, 86, 76, 18, + 76, 23, 77, 2, 247, 243, 12, 65, 74, 74, 32, 104, 6, 89, 82, 69, 78, 69, + 32, 141, 142, 4, 4, 83, 32, 85, 80, 8, 80, 3, 66, 82, 65, 246, 189, 11, + 84, 248, 97, 4, 68, 79, 87, 78, 1, 2, 85, 80, 2, 247, 141, 13, 78, 64, 80, 2, 76, 69, 40, 4, 82, 73, 71, 72, 253, 2, 7, 78, 85, 77, 66, 69, 82, 32, 48, 38, 70, 89, 5, 84, 84, 69, 82, 32, 2, 57, 12, 84, 45, 80, 79, 73, - 78, 84, 73, 78, 71, 32, 70, 2, 177, 226, 5, 2, 76, 69, 46, 224, 1, 5, 70, - 73, 78, 65, 76, 30, 84, 246, 118, 68, 34, 76, 50, 81, 238, 205, 1, 82, - 178, 215, 2, 65, 50, 71, 90, 90, 34, 83, 66, 89, 158, 208, 1, 72, 234, 5, - 75, 198, 75, 66, 178, 216, 4, 78, 134, 2, 87, 218, 103, 80, 171, 4, 77, - 2, 217, 152, 12, 2, 32, 78, 4, 222, 150, 11, 69, 243, 131, 1, 65, 14, - 198, 120, 84, 226, 175, 10, 70, 251, 86, 79, 4, 32, 2, 67, 65, 203, 216, - 12, 68, 2, 131, 132, 12, 75, 184, 2, 94, 65, 220, 1, 11, 69, 78, 84, 72, - 69, 83, 73, 90, 69, 68, 32, 242, 16, 84, 131, 180, 12, 82, 14, 80, 5, 71, - 82, 65, 80, 72, 52, 5, 76, 76, 69, 76, 32, 137, 158, 6, 2, 67, 72, 6, - 198, 232, 9, 32, 194, 222, 1, 85, 207, 145, 1, 79, 6, 44, 5, 87, 73, 84, - 72, 32, 219, 246, 12, 84, 4, 150, 193, 6, 84, 131, 176, 4, 72, 150, 2, + 78, 84, 73, 78, 71, 32, 70, 2, 229, 231, 5, 2, 76, 69, 46, 224, 1, 5, 70, + 73, 78, 65, 76, 30, 84, 242, 119, 68, 34, 76, 50, 81, 214, 205, 1, 82, + 142, 220, 2, 65, 50, 71, 90, 90, 34, 83, 66, 89, 198, 207, 1, 72, 234, 5, + 75, 174, 81, 66, 170, 225, 4, 78, 134, 2, 87, 218, 103, 80, 171, 4, 77, + 2, 161, 172, 12, 2, 32, 78, 4, 190, 167, 11, 69, 219, 134, 1, 65, 14, + 194, 121, 84, 198, 191, 10, 70, 223, 87, 79, 4, 32, 2, 67, 65, 147, 236, + 12, 68, 2, 191, 149, 12, 75, 188, 2, 94, 65, 220, 1, 11, 69, 78, 84, 72, + 69, 83, 73, 90, 69, 68, 32, 242, 16, 84, 203, 199, 12, 82, 14, 80, 5, 71, + 82, 65, 80, 72, 52, 5, 76, 76, 69, 76, 32, 209, 163, 6, 2, 67, 72, 6, + 186, 248, 9, 32, 250, 223, 1, 85, 235, 147, 1, 79, 6, 44, 5, 87, 73, 84, + 72, 32, 163, 138, 13, 84, 4, 222, 198, 6, 84, 175, 186, 4, 72, 150, 2, 252, 1, 7, 72, 65, 78, 71, 85, 76, 32, 188, 4, 10, 73, 68, 69, 79, 71, 82, 65, 80, 72, 32, 188, 7, 18, 75, 79, 82, 69, 65, 78, 32, 67, 72, 65, - 82, 65, 67, 84, 69, 82, 32, 79, 44, 7, 78, 85, 77, 66, 69, 82, 32, 202, - 201, 4, 68, 213, 169, 2, 2, 76, 65, 58, 102, 67, 110, 72, 30, 75, 66, 77, + 82, 65, 67, 84, 69, 82, 32, 79, 44, 7, 78, 85, 77, 66, 69, 82, 32, 250, + 206, 4, 68, 145, 169, 2, 2, 76, 65, 58, 102, 67, 110, 72, 30, 75, 66, 77, 34, 78, 34, 80, 62, 82, 30, 83, 26, 84, 73, 5, 73, 69, 85, 78, 71, 10, 34, 72, 33, 4, 73, 69, 85, 67, 4, 141, 3, 4, 73, 69, 85, 67, 7, 11, 32, - 4, 234, 145, 13, 65, 3, 85, 4, 197, 2, 3, 73, 69, 85, 8, 168, 2, 5, 72, + 4, 178, 165, 13, 65, 3, 85, 4, 197, 2, 3, 73, 69, 85, 8, 168, 2, 5, 72, 73, 69, 85, 75, 13, 5, 73, 89, 69, 79, 75, 4, 245, 1, 4, 73, 69, 85, 77, 4, 213, 1, 4, 73, 69, 85, 78, 8, 168, 1, 5, 72, 73, 69, 85, 80, 13, 4, 73, 69, 85, 80, 4, 121, 4, 73, 69, 85, 76, 4, 93, 3, 73, 79, 83, 8, 56, - 5, 72, 73, 69, 85, 84, 13, 5, 73, 75, 69, 85, 84, 4, 11, 72, 5, 195, 140, + 5, 72, 73, 69, 85, 84, 13, 5, 73, 75, 69, 85, 84, 4, 11, 72, 5, 139, 160, 13, 32, 72, 148, 1, 2, 65, 76, 30, 67, 74, 69, 82, 70, 112, 2, 76, 65, - 22, 77, 38, 78, 32, 2, 82, 69, 78, 83, 138, 2, 84, 50, 87, 158, 156, 11, - 72, 139, 82, 79, 2, 241, 133, 8, 2, 76, 73, 4, 32, 2, 79, 78, 211, 133, - 11, 65, 2, 185, 240, 7, 4, 71, 82, 65, 84, 6, 42, 78, 222, 163, 9, 65, - 163, 190, 3, 73, 2, 169, 4, 5, 84, 69, 82, 80, 82, 10, 58, 73, 160, 130, - 12, 5, 69, 83, 84, 73, 86, 239, 16, 79, 6, 208, 2, 3, 78, 65, 78, 186, - 242, 12, 82, 3, 86, 2, 195, 210, 12, 66, 4, 234, 182, 10, 69, 195, 132, - 2, 79, 4, 198, 241, 11, 73, 195, 1, 65, 8, 38, 83, 174, 250, 11, 80, 219, - 109, 65, 4, 134, 209, 6, 79, 167, 185, 6, 84, 18, 58, 69, 34, 79, 22, 80, - 34, 84, 50, 85, 167, 252, 11, 73, 4, 226, 181, 11, 86, 199, 82, 76, 2, - 243, 240, 7, 67, 2, 11, 69, 2, 179, 176, 4, 67, 4, 26, 85, 239, 216, 11, - 79, 2, 147, 247, 12, 68, 4, 26, 80, 175, 136, 13, 78, 2, 21, 3, 69, 82, - 86, 2, 135, 255, 11, 73, 6, 186, 152, 11, 72, 230, 159, 1, 69, 239, 48, - 87, 4, 246, 208, 9, 79, 227, 254, 1, 65, 4, 182, 153, 8, 32, 137, 158, 4, + 22, 77, 38, 78, 32, 2, 82, 69, 78, 83, 138, 2, 84, 50, 87, 254, 172, 11, + 72, 239, 82, 79, 2, 237, 144, 8, 2, 76, 73, 4, 32, 2, 79, 78, 179, 150, + 11, 65, 2, 181, 251, 7, 4, 71, 82, 65, 84, 6, 42, 78, 174, 179, 9, 65, + 155, 194, 3, 73, 2, 169, 4, 5, 84, 69, 82, 80, 82, 10, 58, 73, 204, 147, + 12, 5, 69, 83, 84, 73, 86, 139, 19, 79, 6, 208, 2, 3, 78, 65, 78, 130, + 134, 13, 82, 3, 86, 2, 139, 230, 12, 66, 4, 226, 198, 10, 69, 147, 136, + 2, 79, 4, 138, 131, 12, 73, 195, 1, 65, 8, 38, 83, 218, 139, 12, 80, 247, + 111, 65, 4, 190, 214, 6, 79, 183, 199, 6, 84, 18, 58, 69, 34, 79, 22, 80, + 34, 84, 50, 85, 211, 141, 12, 73, 4, 142, 199, 11, 86, 227, 84, 76, 2, + 239, 251, 7, 67, 2, 11, 69, 2, 227, 181, 4, 67, 4, 26, 85, 163, 234, 11, + 79, 2, 219, 138, 13, 68, 4, 26, 80, 247, 155, 13, 78, 2, 21, 3, 69, 82, + 86, 2, 183, 144, 12, 73, 6, 154, 169, 11, 72, 206, 162, 1, 69, 239, 48, + 87, 4, 234, 224, 9, 79, 163, 128, 2, 65, 4, 170, 164, 8, 32, 221, 166, 4, 2, 74, 69, 22, 42, 69, 46, 70, 42, 78, 30, 83, 51, 84, 4, 216, 1, 3, 73, - 71, 72, 211, 240, 4, 76, 4, 160, 1, 2, 79, 85, 13, 2, 73, 70, 2, 133, 1, + 71, 72, 131, 246, 4, 76, 4, 160, 1, 2, 79, 85, 13, 2, 73, 70, 2, 133, 1, 3, 73, 78, 69, 4, 34, 73, 73, 4, 69, 86, 69, 78, 2, 71, 88, 8, 34, 72, - 46, 87, 135, 181, 12, 69, 2, 11, 73, 2, 11, 82, 2, 131, 177, 11, 84, 4, - 11, 69, 4, 222, 151, 11, 78, 167, 112, 76, 18, 128, 1, 3, 73, 65, 76, - 216, 1, 3, 89, 32, 80, 168, 169, 8, 6, 78, 69, 82, 83, 72, 73, 169, 211, - 3, 6, 32, 65, 76, 84, 69, 82, 12, 62, 32, 193, 123, 10, 76, 89, 45, 82, - 69, 67, 89, 67, 76, 69, 10, 38, 68, 41, 5, 76, 73, 78, 69, 32, 2, 217, - 169, 4, 5, 73, 70, 70, 69, 82, 8, 222, 200, 3, 68, 226, 45, 70, 20, 4, - 66, 65, 67, 75, 199, 139, 9, 85, 2, 191, 237, 3, 79, 10, 98, 69, 52, 9, - 73, 86, 69, 45, 80, 85, 76, 76, 45, 89, 9, 80, 79, 82, 84, 32, 67, 79, - 78, 84, 4, 224, 226, 9, 4, 78, 71, 69, 82, 219, 138, 2, 68, 4, 40, 4, 68, - 79, 87, 78, 1, 2, 85, 80, 2, 185, 171, 5, 6, 45, 79, 85, 84, 80, 85, 2, - 231, 236, 11, 82, 114, 192, 1, 12, 71, 76, 79, 84, 84, 65, 76, 32, 83, - 84, 79, 80, 62, 76, 160, 3, 3, 82, 73, 83, 36, 14, 77, 73, 68, 45, 76, - 69, 86, 69, 76, 32, 84, 79, 78, 69, 57, 7, 83, 65, 78, 68, 72, 73, 32, 7, - 11, 32, 4, 206, 5, 70, 201, 238, 11, 4, 86, 65, 82, 73, 82, 72, 6, 69, - 84, 84, 69, 82, 32, 213, 2, 7, 79, 87, 45, 70, 65, 76, 76, 74, 206, 1, - 70, 166, 143, 7, 73, 2, 85, 214, 250, 1, 78, 242, 169, 3, 67, 2, 75, 2, - 80, 2, 84, 138, 69, 66, 2, 68, 2, 71, 2, 72, 2, 76, 2, 77, 2, 82, 2, 83, - 2, 86, 2, 90, 186, 2, 65, 2, 69, 3, 79, 20, 44, 5, 73, 78, 65, 76, 32, - 251, 250, 12, 65, 18, 222, 255, 10, 78, 154, 251, 1, 75, 2, 76, 2, 77, 2, - 80, 2, 84, 2, 87, 3, 89, 8, 157, 1, 5, 73, 78, 71, 32, 84, 7, 11, 32, 4, - 192, 1, 5, 76, 79, 78, 71, 32, 15, 70, 12, 66, 84, 73, 12, 71, 76, 79, - 84, 84, 65, 76, 32, 83, 84, 79, 80, 8, 21, 3, 79, 78, 69, 9, 11, 32, 6, - 32, 4, 76, 79, 78, 71, 27, 70, 5, 11, 32, 2, 11, 70, 2, 223, 195, 3, 73, - 2, 155, 176, 4, 82, 4, 250, 247, 12, 70, 3, 73, 80, 130, 1, 65, 122, 78, - 162, 1, 82, 162, 9, 83, 44, 3, 84, 82, 73, 130, 17, 68, 245, 136, 12, 9, - 79, 80, 76, 69, 32, 72, 85, 71, 71, 12, 50, 67, 50, 78, 190, 234, 6, 32, - 191, 139, 6, 82, 6, 182, 198, 11, 79, 202, 28, 69, 171, 147, 1, 72, 2, - 203, 225, 11, 85, 10, 118, 71, 20, 3, 83, 73, 86, 222, 178, 1, 84, 132, - 184, 4, 10, 32, 79, 86, 69, 82, 32, 83, 84, 65, 77, 167, 214, 4, 67, 2, - 151, 245, 11, 85, 2, 183, 183, 12, 69, 48, 186, 1, 32, 116, 10, 80, 69, - 78, 68, 73, 67, 85, 76, 65, 82, 42, 83, 210, 165, 7, 67, 252, 9, 10, 77, - 65, 78, 69, 78, 84, 32, 80, 65, 80, 201, 128, 2, 8, 70, 79, 82, 77, 73, - 78, 71, 32, 6, 34, 77, 30, 84, 227, 235, 11, 83, 2, 181, 241, 1, 2, 73, - 76, 2, 213, 137, 11, 8, 69, 78, 32, 84, 72, 79, 85, 83, 5, 129, 253, 8, - 5, 32, 87, 73, 84, 72, 32, 60, 2, 79, 78, 178, 75, 80, 221, 157, 11, 4, - 69, 86, 69, 82, 28, 38, 32, 149, 246, 9, 3, 65, 76, 32, 26, 216, 2, 10, - 68, 79, 73, 78, 71, 32, 67, 65, 82, 84, 32, 3, 73, 78, 32, 92, 5, 87, 73, - 84, 72, 32, 220, 213, 7, 4, 70, 82, 79, 87, 204, 13, 27, 82, 65, 73, 83, - 73, 78, 71, 32, 66, 79, 84, 72, 32, 72, 65, 78, 68, 83, 32, 73, 78, 32, - 67, 69, 76, 69, 66, 128, 185, 4, 5, 67, 76, 73, 77, 66, 213, 45, 11, 66, - 79, 87, 73, 78, 71, 32, 68, 69, 69, 80, 2, 11, 87, 2, 251, 183, 3, 72, 4, - 164, 156, 12, 6, 76, 79, 84, 85, 83, 32, 253, 64, 9, 83, 84, 69, 65, 77, - 89, 32, 82, 79, 12, 154, 1, 66, 240, 145, 2, 3, 80, 79, 85, 220, 160, 1, - 2, 67, 82, 196, 250, 5, 6, 70, 79, 76, 68, 69, 68, 221, 189, 2, 8, 72, - 69, 65, 68, 83, 67, 65, 82, 4, 36, 3, 76, 79, 78, 171, 228, 10, 65, 2, - 11, 68, 2, 11, 32, 2, 11, 72, 2, 11, 65, 2, 219, 178, 12, 73, 4, 224, - 153, 9, 2, 69, 84, 195, 202, 2, 79, 2, 253, 137, 9, 2, 32, 68, 140, 2, - 66, 65, 184, 19, 9, 73, 76, 73, 80, 80, 73, 78, 69, 32, 47, 79, 204, 1, - 108, 6, 71, 83, 45, 80, 65, 32, 189, 7, 16, 73, 83, 84, 79, 83, 32, 68, - 73, 83, 67, 32, 83, 73, 71, 78, 32, 112, 100, 7, 76, 69, 84, 84, 69, 82, - 32, 248, 4, 5, 77, 65, 82, 75, 32, 30, 83, 33, 4, 68, 79, 85, 66, 96, - 138, 2, 65, 138, 1, 67, 50, 68, 42, 83, 64, 5, 86, 79, 73, 67, 69, 222, - 242, 8, 71, 246, 168, 3, 78, 82, 84, 46, 75, 2, 80, 2, 90, 162, 7, 69, - 234, 61, 66, 2, 70, 2, 72, 2, 74, 2, 76, 2, 77, 2, 81, 2, 82, 2, 87, 2, - 88, 2, 89, 186, 2, 73, 2, 79, 3, 85, 7, 80, 8, 76, 84, 69, 82, 78, 65, - 84, 69, 21, 8, 83, 80, 73, 82, 65, 84, 69, 68, 2, 251, 226, 12, 32, 2, - 11, 32, 2, 255, 226, 12, 70, 6, 26, 65, 211, 226, 12, 72, 5, 223, 208, 8, - 78, 6, 186, 226, 12, 68, 2, 90, 187, 2, 65, 6, 168, 164, 8, 4, 77, 65, - 76, 76, 234, 189, 4, 72, 187, 2, 65, 4, 34, 68, 21, 4, 76, 69, 83, 83, 2, - 167, 233, 10, 32, 2, 215, 171, 9, 32, 4, 206, 156, 12, 68, 59, 83, 10, - 28, 3, 73, 78, 71, 31, 85, 2, 189, 150, 12, 2, 76, 69, 8, 58, 66, 173, - 151, 12, 8, 80, 69, 82, 70, 73, 88, 69, 68, 6, 133, 179, 8, 13, 74, 79, - 73, 78, 69, 68, 32, 76, 69, 84, 84, 69, 82, 92, 238, 1, 66, 146, 1, 67, - 172, 2, 2, 68, 79, 38, 71, 66, 72, 64, 2, 76, 73, 32, 2, 77, 65, 70, 80, - 162, 1, 82, 38, 83, 150, 1, 84, 70, 87, 160, 223, 5, 2, 70, 76, 252, 232, - 1, 2, 79, 88, 196, 234, 3, 2, 69, 65, 250, 9, 65, 135, 1, 86, 10, 52, 2, - 69, 69, 22, 79, 133, 41, 4, 85, 76, 76, 83, 5, 247, 171, 7, 72, 4, 32, 2, - 79, 77, 135, 223, 12, 87, 2, 11, 69, 2, 163, 134, 12, 82, 16, 34, 65, 86, - 72, 22, 76, 23, 79, 6, 234, 218, 5, 80, 148, 230, 3, 8, 82, 80, 69, 78, - 84, 82, 89, 32, 195, 157, 3, 84, 2, 227, 191, 2, 73, 2, 243, 161, 11, 85, - 6, 26, 76, 33, 2, 77, 66, 2, 11, 85, 2, 187, 141, 12, 77, 5, 37, 7, 73, - 78, 73, 78, 71, 32, 79, 2, 209, 239, 9, 5, 66, 76, 73, 81, 85, 4, 154, - 179, 11, 76, 203, 146, 1, 86, 4, 42, 82, 201, 252, 10, 4, 65, 85, 78, 84, - 2, 239, 163, 11, 65, 6, 42, 69, 146, 209, 7, 79, 143, 250, 2, 73, 2, 131, - 166, 12, 76, 4, 202, 201, 12, 76, 203, 17, 68, 4, 34, 78, 161, 236, 9, 2, - 84, 84, 2, 11, 65, 2, 255, 156, 9, 67, 8, 52, 2, 69, 68, 50, 76, 153, - 166, 7, 3, 65, 80, 89, 2, 25, 4, 69, 83, 84, 82, 2, 219, 143, 11, 73, 4, - 176, 182, 4, 2, 85, 77, 177, 226, 2, 3, 65, 78, 69, 4, 166, 202, 10, 79, - 147, 254, 1, 65, 12, 108, 2, 72, 73, 222, 217, 11, 65, 158, 45, 76, 128, - 19, 3, 84, 82, 65, 177, 39, 7, 77, 65, 76, 76, 32, 65, 88, 4, 242, 185, - 2, 69, 139, 158, 10, 80, 6, 192, 180, 4, 5, 65, 84, 84, 79, 79, 202, 222, - 7, 73, 207, 36, 85, 4, 190, 225, 7, 79, 209, 231, 3, 5, 65, 86, 89, 32, - 66, 4, 174, 244, 1, 83, 25, 4, 68, 79, 85, 66, 60, 56, 8, 69, 78, 73, 67, - 73, 65, 78, 32, 251, 207, 10, 76, 58, 92, 7, 76, 69, 84, 84, 69, 82, 32, - 160, 3, 7, 78, 85, 77, 66, 69, 82, 32, 203, 150, 6, 87, 44, 234, 1, 65, - 34, 68, 22, 72, 22, 81, 22, 83, 58, 84, 158, 130, 2, 87, 138, 229, 5, 90, - 154, 180, 1, 89, 184, 206, 1, 2, 82, 79, 236, 94, 3, 71, 65, 77, 134, 8, - 75, 130, 1, 78, 144, 58, 3, 76, 65, 77, 138, 17, 66, 198, 30, 80, 171, 4, - 77, 4, 130, 210, 11, 76, 199, 49, 73, 2, 163, 187, 3, 69, 4, 147, 243, 6, - 69, 2, 187, 209, 11, 79, 6, 190, 194, 10, 65, 186, 144, 1, 72, 189, 124, - 2, 69, 77, 4, 170, 173, 12, 65, 191, 8, 69, 12, 238, 49, 84, 135, 166, 4, - 79, 40, 104, 2, 67, 75, 66, 71, 62, 76, 90, 78, 178, 1, 83, 32, 7, 84, - 67, 72, 70, 79, 82, 75, 199, 205, 12, 69, 5, 17, 2, 85, 80, 2, 21, 3, 32, - 84, 82, 2, 203, 160, 11, 85, 7, 11, 32, 4, 26, 78, 251, 146, 12, 70, 2, - 243, 198, 11, 79, 6, 52, 4, 69, 32, 79, 70, 246, 91, 67, 195, 243, 11, - 76, 2, 11, 32, 2, 255, 135, 10, 80, 14, 68, 2, 67, 72, 46, 69, 178, 164, - 4, 87, 182, 162, 7, 75, 243, 98, 65, 4, 160, 155, 3, 2, 69, 68, 255, 164, - 8, 73, 4, 28, 2, 32, 68, 255, 72, 65, 2, 221, 129, 5, 2, 69, 67, 4, 234, - 185, 11, 67, 139, 1, 84, 5, 153, 252, 9, 10, 32, 87, 73, 84, 72, 32, 84, - 69, 69, 32, 218, 1, 38, 65, 218, 9, 85, 135, 195, 12, 68, 176, 1, 78, 67, - 128, 1, 12, 78, 67, 75, 32, 67, 79, 78, 83, 84, 65, 78, 84, 83, 89, 6, - 44, 5, 69, 32, 79, 70, 32, 239, 180, 11, 65, 4, 56, 6, 73, 78, 84, 69, - 82, 69, 129, 246, 11, 2, 87, 79, 2, 199, 136, 7, 83, 5, 45, 9, 32, 79, - 86, 69, 82, 32, 84, 87, 79, 2, 11, 32, 2, 243, 182, 12, 80, 166, 1, 96, - 9, 73, 78, 71, 32, 67, 65, 82, 68, 32, 141, 166, 4, 9, 71, 82, 79, 85, - 78, 68, 32, 83, 76, 164, 1, 182, 1, 66, 44, 3, 82, 69, 68, 0, 5, 87, 72, - 73, 84, 69, 42, 70, 74, 75, 38, 69, 34, 83, 36, 3, 81, 85, 69, 14, 84, - 92, 2, 65, 67, 0, 3, 78, 73, 78, 13, 4, 74, 65, 67, 75, 4, 40, 4, 76, 65, - 67, 75, 251, 151, 11, 65, 2, 17, 2, 32, 74, 2, 159, 237, 1, 79, 18, 30, - 79, 249, 1, 2, 73, 86, 10, 128, 2, 2, 85, 82, 235, 187, 11, 79, 16, 34, - 78, 185, 1, 3, 73, 78, 71, 8, 181, 1, 4, 73, 71, 72, 84, 16, 32, 2, 69, - 86, 117, 2, 73, 88, 8, 91, 69, 66, 78, 69, 12, 3, 72, 82, 69, 12, 2, 87, - 79, 161, 1, 5, 82, 85, 77, 80, 45, 8, 23, 78, 8, 11, 69, 8, 25, 4, 32, - 79, 70, 32, 8, 88, 3, 67, 76, 85, 20, 3, 83, 80, 65, 174, 130, 9, 72, - 137, 3, 5, 68, 73, 65, 77, 79, 2, 199, 134, 12, 66, 2, 151, 176, 11, 68, - 42, 90, 50, 238, 130, 10, 49, 182, 192, 2, 51, 2, 52, 2, 53, 2, 54, 2, - 55, 2, 56, 3, 57, 7, 158, 195, 12, 48, 3, 49, 41, 46, 83, 160, 4, 2, 84, - 79, 227, 240, 8, 78, 26, 52, 5, 32, 83, 73, 71, 78, 193, 147, 9, 2, 45, - 77, 25, 11, 32, 22, 64, 3, 73, 78, 32, 124, 5, 87, 73, 84, 72, 32, 207, - 250, 3, 65, 6, 34, 76, 22, 82, 183, 156, 11, 84, 2, 41, 2, 69, 70, 2, 21, - 3, 73, 71, 72, 2, 229, 238, 10, 6, 84, 32, 72, 65, 76, 70, 14, 162, 1, - 68, 34, 83, 200, 160, 10, 4, 84, 73, 76, 68, 208, 121, 5, 66, 76, 65, 67, - 75, 181, 36, 16, 67, 73, 82, 67, 85, 77, 70, 76, 69, 88, 32, 65, 67, 67, - 69, 78, 2, 11, 79, 2, 219, 145, 10, 84, 4, 54, 77, 161, 176, 5, 7, 85, - 66, 83, 67, 82, 73, 80, 2, 249, 154, 9, 3, 65, 76, 76, 11, 33, 6, 32, 70, - 79, 82, 77, 32, 8, 234, 205, 10, 70, 71, 84, 2, 253, 132, 12, 8, 32, 84, - 82, 65, 78, 83, 73, 83, 58, 232, 1, 5, 76, 73, 67, 69, 32, 122, 80, 136, - 1, 11, 82, 84, 65, 66, 76, 69, 32, 83, 84, 69, 82, 22, 83, 158, 1, 84, - 146, 1, 85, 196, 1, 4, 87, 69, 82, 32, 194, 132, 7, 79, 145, 248, 4, 11, - 67, 75, 69, 84, 32, 67, 65, 76, 67, 85, 76, 6, 52, 3, 67, 65, 82, 209, - 248, 3, 4, 79, 70, 70, 73, 5, 149, 162, 10, 11, 83, 32, 82, 69, 86, 79, - 76, 86, 73, 78, 71, 6, 76, 13, 32, 68, 73, 82, 69, 67, 84, 73, 79, 78, - 65, 76, 32, 219, 214, 1, 67, 4, 166, 127, 73, 205, 180, 6, 6, 70, 79, 82, - 77, 65, 84, 2, 223, 154, 12, 69, 12, 40, 2, 69, 73, 22, 84, 235, 135, 5, - 73, 2, 167, 233, 11, 68, 8, 36, 3, 65, 76, 32, 171, 172, 11, 66, 6, 162, - 213, 1, 72, 157, 197, 6, 4, 77, 65, 82, 75, 8, 66, 65, 250, 134, 2, 32, - 141, 167, 9, 6, 84, 69, 68, 32, 80, 76, 4, 26, 66, 211, 152, 12, 84, 2, - 29, 5, 76, 69, 32, 87, 65, 2, 143, 48, 84, 12, 108, 4, 76, 84, 82, 89, - 28, 7, 82, 73, 78, 71, 32, 76, 73, 28, 2, 84, 73, 150, 205, 10, 78, 203, - 231, 1, 67, 2, 185, 240, 11, 2, 32, 76, 2, 193, 174, 5, 2, 81, 85, 4, - 225, 206, 10, 2, 78, 71, 8, 24, 2, 79, 78, 47, 83, 4, 136, 162, 11, 4, - 45, 79, 70, 70, 15, 32, 4, 212, 136, 9, 3, 76, 69, 69, 175, 153, 2, 89, - 140, 1, 74, 69, 162, 10, 73, 234, 2, 79, 157, 206, 9, 6, 65, 89, 69, 82, - 32, 66, 102, 132, 1, 6, 71, 78, 65, 78, 84, 32, 66, 83, 224, 186, 5, 4, - 67, 69, 68, 69, 204, 202, 1, 5, 86, 73, 79, 85, 83, 177, 32, 2, 84, 90, - 6, 42, 87, 130, 179, 8, 80, 215, 181, 2, 77, 2, 255, 188, 7, 79, 70, 176, - 1, 27, 69, 78, 84, 65, 84, 73, 79, 78, 32, 70, 79, 82, 77, 32, 70, 79, - 82, 32, 86, 69, 82, 84, 73, 67, 65, 76, 32, 221, 200, 8, 10, 67, 82, 73, - 80, 84, 73, 79, 78, 32, 84, 68, 198, 1, 67, 22, 69, 46, 72, 50, 73, 94, - 76, 188, 1, 6, 82, 73, 71, 72, 84, 32, 192, 2, 6, 87, 65, 86, 89, 32, 76, - 174, 151, 4, 83, 184, 207, 4, 9, 84, 87, 79, 32, 68, 79, 84, 32, 76, 135, - 114, 81, 4, 167, 185, 2, 79, 6, 222, 153, 6, 88, 174, 216, 2, 77, 3, 78, - 2, 229, 138, 9, 7, 79, 82, 73, 90, 79, 78, 84, 4, 49, 10, 68, 69, 79, 71, - 82, 65, 80, 72, 73, 67, 4, 11, 32, 4, 142, 220, 9, 67, 35, 70, 22, 40, 4, - 69, 70, 84, 32, 219, 197, 10, 79, 20, 112, 6, 87, 72, 73, 84, 69, 32, - 142, 1, 66, 42, 68, 198, 99, 67, 166, 7, 65, 138, 190, 7, 80, 238, 7, 83, - 39, 84, 4, 162, 2, 67, 139, 99, 76, 22, 110, 66, 42, 68, 36, 6, 87, 72, - 73, 84, 69, 32, 162, 99, 67, 166, 7, 65, 138, 190, 7, 80, 238, 7, 83, 39, - 84, 2, 157, 100, 6, 76, 65, 67, 75, 32, 76, 2, 209, 106, 5, 79, 85, 66, - 76, 69, 6, 74, 67, 17, 14, 76, 69, 78, 84, 73, 67, 85, 76, 65, 82, 32, - 66, 82, 65, 2, 239, 98, 79, 4, 218, 215, 11, 67, 177, 29, 2, 75, 67, 2, - 135, 194, 10, 79, 22, 46, 78, 156, 1, 2, 86, 65, 203, 145, 12, 77, 10, - 34, 84, 225, 200, 6, 2, 67, 69, 6, 26, 32, 53, 2, 69, 82, 2, 21, 3, 83, - 67, 82, 2, 153, 177, 10, 2, 69, 69, 5, 17, 2, 32, 73, 2, 195, 216, 11, - 67, 10, 60, 5, 67, 89, 32, 77, 69, 29, 6, 84, 69, 32, 85, 83, 69, 2, 133, - 161, 7, 2, 83, 83, 8, 26, 32, 159, 166, 8, 45, 4, 226, 239, 5, 84, 151, - 158, 5, 79, 14, 130, 1, 74, 30, 80, 236, 33, 5, 72, 73, 66, 73, 84, 200, - 230, 8, 6, 66, 73, 78, 71, 32, 67, 169, 225, 1, 5, 83, 69, 82, 80, 73, 2, - 165, 162, 5, 2, 69, 67, 6, 64, 6, 79, 82, 84, 73, 79, 78, 205, 139, 11, - 4, 69, 82, 84, 89, 5, 247, 209, 5, 65, 58, 172, 1, 15, 70, 79, 85, 82, - 32, 68, 79, 84, 83, 32, 87, 73, 84, 72, 32, 32, 7, 76, 69, 84, 84, 69, - 82, 32, 230, 2, 78, 134, 32, 83, 1, 8, 84, 85, 82, 78, 69, 68, 32, 83, 4, - 190, 226, 10, 67, 227, 112, 68, 36, 166, 1, 65, 22, 68, 34, 76, 22, 77, - 50, 87, 186, 165, 4, 71, 90, 90, 34, 83, 66, 89, 158, 208, 1, 72, 234, 5, - 75, 198, 75, 66, 178, 216, 4, 78, 134, 2, 84, 219, 103, 80, 2, 155, 166, - 4, 76, 2, 11, 65, 2, 143, 201, 6, 76, 2, 183, 166, 4, 65, 2, 25, 4, 69, - 77, 45, 81, 2, 147, 253, 5, 79, 2, 37, 7, 65, 87, 45, 65, 89, 73, 78, 2, - 173, 205, 1, 2, 45, 82, 14, 33, 6, 85, 77, 66, 69, 82, 32, 14, 42, 84, - 134, 166, 4, 79, 131, 231, 2, 70, 8, 42, 87, 150, 176, 10, 72, 231, 159, - 1, 69, 4, 210, 178, 10, 69, 135, 237, 1, 79, 18, 252, 1, 4, 78, 67, 84, + 46, 87, 207, 200, 12, 69, 2, 11, 73, 2, 11, 82, 2, 175, 194, 11, 84, 4, + 11, 69, 4, 190, 168, 11, 78, 143, 115, 76, 22, 164, 1, 3, 73, 65, 76, + 216, 1, 3, 89, 32, 80, 224, 206, 6, 5, 72, 69, 78, 79, 80, 180, 229, 1, + 6, 78, 69, 82, 83, 72, 73, 225, 219, 3, 6, 32, 65, 76, 84, 69, 82, 12, + 62, 32, 173, 124, 10, 76, 89, 45, 82, 69, 67, 89, 67, 76, 69, 10, 38, 68, + 41, 5, 76, 73, 78, 69, 32, 2, 229, 174, 4, 5, 73, 70, 70, 69, 82, 8, 254, + 205, 3, 68, 226, 45, 70, 20, 4, 66, 65, 67, 75, 203, 153, 9, 85, 2, 223, + 242, 3, 79, 10, 98, 69, 52, 9, 73, 86, 69, 45, 80, 85, 76, 76, 45, 89, 9, + 80, 79, 82, 84, 32, 67, 79, 78, 84, 4, 176, 242, 9, 4, 78, 71, 69, 82, + 147, 140, 2, 68, 4, 40, 4, 68, 79, 87, 78, 1, 2, 85, 80, 2, 213, 176, 5, + 6, 45, 79, 85, 84, 80, 85, 2, 239, 253, 11, 82, 114, 192, 1, 12, 71, 76, + 79, 84, 84, 65, 76, 32, 83, 84, 79, 80, 62, 76, 156, 3, 3, 82, 73, 83, + 36, 14, 77, 73, 68, 45, 76, 69, 86, 69, 76, 32, 84, 79, 78, 69, 57, 7, + 83, 65, 78, 68, 72, 73, 32, 7, 11, 32, 4, 202, 5, 70, 213, 255, 11, 4, + 86, 65, 82, 73, 82, 72, 6, 69, 84, 84, 69, 82, 32, 209, 2, 7, 79, 87, 45, + 70, 65, 76, 76, 74, 202, 1, 70, 226, 252, 8, 73, 2, 85, 238, 27, 78, 198, + 174, 3, 67, 2, 75, 2, 80, 2, 84, 138, 69, 66, 2, 68, 2, 71, 2, 72, 2, 76, + 2, 77, 2, 82, 2, 83, 2, 86, 2, 90, 186, 2, 65, 2, 69, 3, 79, 20, 44, 5, + 73, 78, 65, 76, 32, 163, 142, 13, 65, 18, 158, 144, 11, 78, 130, 254, 1, + 75, 2, 76, 2, 77, 2, 80, 2, 84, 2, 87, 3, 89, 8, 157, 1, 5, 73, 78, 71, + 32, 84, 7, 11, 32, 4, 192, 1, 5, 76, 79, 78, 71, 32, 15, 70, 12, 66, 84, + 73, 12, 71, 76, 79, 84, 84, 65, 76, 32, 83, 84, 79, 80, 8, 21, 3, 79, 78, + 69, 9, 11, 32, 6, 32, 4, 76, 79, 78, 71, 27, 70, 5, 11, 32, 2, 11, 70, 2, + 131, 201, 3, 73, 2, 171, 181, 4, 82, 4, 162, 139, 13, 70, 3, 73, 80, 130, + 1, 65, 122, 78, 162, 1, 82, 162, 9, 83, 44, 3, 84, 82, 73, 130, 17, 68, + 157, 156, 12, 9, 79, 80, 76, 69, 32, 72, 85, 71, 71, 12, 50, 67, 50, 78, + 138, 239, 6, 32, 155, 154, 6, 82, 6, 202, 215, 11, 79, 194, 28, 69, 199, + 149, 1, 72, 2, 227, 128, 12, 85, 10, 118, 71, 20, 3, 83, 73, 86, 222, + 179, 1, 84, 156, 188, 4, 10, 32, 79, 86, 69, 82, 32, 83, 84, 65, 77, 187, + 184, 5, 67, 2, 191, 136, 12, 85, 2, 223, 202, 12, 69, 48, 186, 1, 32, + 116, 10, 80, 69, 78, 68, 73, 67, 85, 76, 65, 82, 42, 83, 130, 176, 7, 67, + 252, 9, 10, 77, 65, 78, 69, 78, 84, 32, 80, 65, 80, 237, 133, 2, 8, 70, + 79, 82, 77, 73, 78, 71, 32, 6, 34, 77, 30, 84, 139, 255, 11, 83, 2, 129, + 242, 1, 2, 73, 76, 2, 149, 154, 11, 8, 69, 78, 32, 84, 72, 79, 85, 83, 5, + 213, 139, 9, 5, 32, 87, 73, 84, 72, 32, 60, 2, 79, 78, 154, 75, 80, 157, + 177, 11, 4, 69, 86, 69, 82, 28, 38, 32, 237, 133, 10, 3, 65, 76, 32, 26, + 216, 2, 10, 68, 79, 73, 78, 71, 32, 67, 65, 82, 84, 32, 3, 73, 78, 32, + 92, 5, 87, 73, 84, 72, 32, 184, 224, 7, 4, 70, 82, 79, 87, 204, 13, 27, + 82, 65, 73, 83, 73, 78, 71, 32, 66, 79, 84, 72, 32, 72, 65, 78, 68, 83, + 32, 73, 78, 32, 67, 69, 76, 69, 66, 204, 193, 4, 5, 67, 76, 73, 77, 66, + 213, 45, 11, 66, 79, 87, 73, 78, 71, 32, 68, 69, 69, 80, 2, 11, 87, 2, + 159, 189, 3, 72, 4, 204, 175, 12, 6, 76, 79, 84, 85, 83, 32, 253, 64, 9, + 83, 84, 69, 65, 77, 89, 32, 82, 79, 12, 154, 1, 66, 180, 146, 2, 3, 80, + 79, 85, 188, 165, 1, 2, 67, 82, 244, 132, 6, 6, 70, 79, 76, 68, 69, 68, + 177, 193, 2, 8, 72, 69, 65, 68, 83, 67, 65, 82, 4, 36, 3, 76, 79, 78, + 235, 244, 10, 65, 2, 11, 68, 2, 11, 32, 2, 11, 72, 2, 11, 65, 2, 131, + 198, 12, 73, 4, 180, 169, 9, 2, 69, 84, 151, 206, 2, 79, 2, 209, 153, 9, + 2, 32, 68, 140, 2, 66, 65, 184, 19, 9, 73, 76, 73, 80, 80, 73, 78, 69, + 32, 47, 79, 204, 1, 108, 6, 71, 83, 45, 80, 65, 32, 189, 7, 16, 73, 83, + 84, 79, 83, 32, 68, 73, 83, 67, 32, 83, 73, 71, 78, 32, 112, 100, 7, 76, + 69, 84, 84, 69, 82, 32, 248, 4, 5, 77, 65, 82, 75, 32, 30, 83, 33, 4, 68, + 79, 85, 66, 96, 138, 2, 65, 138, 1, 67, 50, 68, 42, 83, 64, 5, 86, 79, + 73, 67, 69, 178, 129, 9, 71, 202, 173, 3, 78, 82, 84, 46, 75, 2, 80, 2, + 90, 162, 7, 69, 234, 61, 66, 2, 70, 2, 72, 2, 74, 2, 76, 2, 77, 2, 81, 2, + 82, 2, 87, 2, 88, 2, 89, 186, 2, 73, 2, 79, 3, 85, 7, 80, 8, 76, 84, 69, + 82, 78, 65, 84, 69, 21, 8, 83, 80, 73, 82, 65, 84, 69, 68, 2, 163, 246, + 12, 32, 2, 11, 32, 2, 167, 246, 12, 70, 6, 26, 65, 251, 245, 12, 72, 5, + 231, 218, 8, 78, 6, 226, 245, 12, 68, 2, 90, 187, 2, 65, 6, 244, 174, 8, + 4, 77, 65, 76, 76, 198, 198, 4, 72, 187, 2, 65, 4, 34, 68, 21, 4, 76, 69, + 83, 83, 2, 231, 249, 10, 32, 2, 171, 187, 9, 32, 4, 246, 175, 12, 68, 59, + 83, 10, 28, 3, 73, 78, 71, 31, 85, 2, 229, 169, 12, 2, 76, 69, 8, 58, 66, + 213, 170, 12, 8, 80, 69, 82, 70, 73, 88, 69, 68, 6, 209, 189, 8, 13, 74, + 79, 73, 78, 69, 68, 32, 76, 69, 84, 84, 69, 82, 92, 238, 1, 66, 146, 1, + 67, 172, 2, 2, 68, 79, 38, 71, 66, 72, 64, 2, 76, 73, 32, 2, 77, 65, 70, + 80, 162, 1, 82, 38, 83, 150, 1, 84, 70, 87, 200, 228, 5, 2, 70, 76, 176, + 238, 1, 2, 79, 88, 252, 240, 3, 2, 69, 65, 138, 10, 65, 135, 1, 86, 10, + 52, 2, 69, 69, 22, 79, 145, 41, 4, 85, 76, 76, 83, 5, 147, 182, 7, 72, 4, + 32, 2, 79, 77, 175, 242, 12, 87, 2, 11, 69, 2, 203, 153, 12, 82, 16, 34, + 65, 86, 72, 22, 76, 23, 79, 6, 130, 224, 5, 80, 208, 240, 3, 8, 82, 80, + 69, 78, 84, 82, 89, 32, 151, 161, 3, 84, 2, 199, 193, 2, 73, 2, 135, 179, + 11, 85, 6, 26, 76, 33, 2, 77, 66, 2, 11, 85, 2, 227, 160, 12, 77, 5, 37, + 7, 73, 78, 73, 78, 71, 32, 79, 2, 169, 255, 9, 5, 66, 76, 73, 81, 85, 4, + 174, 196, 11, 76, 223, 148, 1, 86, 4, 42, 82, 137, 141, 11, 4, 65, 85, + 78, 84, 2, 131, 181, 11, 65, 6, 42, 69, 238, 219, 7, 79, 243, 255, 2, 73, + 2, 171, 185, 12, 76, 4, 242, 220, 12, 76, 203, 17, 68, 4, 34, 78, 249, + 251, 9, 2, 84, 84, 2, 11, 65, 2, 211, 172, 9, 67, 8, 52, 2, 69, 68, 50, + 76, 181, 176, 7, 3, 65, 80, 89, 2, 25, 4, 69, 83, 84, 82, 2, 231, 160, + 11, 73, 4, 192, 187, 4, 2, 85, 77, 209, 231, 2, 3, 65, 78, 69, 4, 230, + 218, 10, 79, 251, 128, 2, 65, 12, 108, 2, 72, 73, 134, 237, 11, 65, 158, + 45, 76, 128, 19, 3, 84, 82, 65, 177, 39, 7, 77, 65, 76, 76, 32, 65, 88, + 4, 214, 187, 2, 69, 207, 175, 10, 80, 6, 208, 185, 4, 5, 65, 84, 84, 79, + 79, 226, 236, 7, 73, 207, 36, 85, 4, 146, 236, 7, 79, 137, 238, 3, 5, 65, + 86, 89, 32, 66, 4, 250, 244, 1, 83, 25, 4, 68, 79, 85, 66, 60, 56, 8, 69, + 78, 73, 67, 73, 65, 78, 32, 187, 224, 10, 76, 58, 92, 7, 76, 69, 84, 84, + 69, 82, 32, 160, 3, 7, 78, 85, 77, 66, 69, 82, 32, 231, 155, 6, 87, 44, + 234, 1, 65, 34, 68, 22, 72, 22, 81, 22, 83, 58, 84, 226, 130, 2, 87, 154, + 239, 5, 90, 154, 185, 1, 89, 164, 207, 1, 2, 82, 79, 184, 95, 3, 71, 65, + 77, 162, 10, 75, 130, 1, 78, 144, 58, 3, 76, 65, 77, 138, 17, 66, 198, + 30, 80, 171, 4, 77, 4, 170, 229, 11, 76, 199, 49, 73, 2, 199, 192, 3, 69, + 4, 195, 253, 6, 69, 2, 227, 228, 11, 79, 6, 254, 210, 10, 65, 162, 147, + 1, 72, 189, 124, 2, 69, 77, 4, 210, 192, 12, 65, 191, 8, 69, 12, 202, 50, + 84, 203, 170, 4, 79, 40, 104, 2, 67, 75, 66, 71, 62, 76, 90, 78, 178, 1, + 83, 28, 7, 84, 67, 72, 70, 79, 82, 75, 243, 224, 12, 69, 5, 17, 2, 85, + 80, 2, 21, 3, 32, 84, 82, 2, 223, 177, 11, 85, 7, 11, 32, 4, 26, 78, 163, + 166, 12, 70, 2, 131, 216, 11, 79, 6, 52, 4, 69, 32, 79, 70, 246, 92, 67, + 235, 133, 12, 76, 2, 11, 32, 2, 215, 151, 10, 80, 14, 68, 2, 67, 72, 46, + 69, 194, 169, 4, 87, 206, 176, 7, 75, 243, 98, 65, 4, 196, 160, 3, 2, 69, + 68, 231, 176, 8, 73, 4, 28, 2, 32, 68, 239, 73, 65, 2, 253, 134, 5, 2, + 69, 67, 4, 134, 203, 11, 67, 123, 84, 5, 245, 139, 10, 10, 32, 87, 73, + 84, 72, 32, 84, 69, 69, 32, 218, 1, 38, 65, 230, 9, 85, 167, 214, 12, 68, + 176, 1, 78, 67, 128, 1, 12, 78, 67, 75, 32, 67, 79, 78, 83, 84, 65, 78, + 84, 83, 89, 6, 44, 5, 69, 32, 79, 70, 32, 151, 198, 11, 65, 4, 56, 6, 73, + 78, 84, 69, 82, 69, 173, 137, 12, 2, 87, 79, 2, 251, 146, 7, 83, 5, 45, + 9, 32, 79, 86, 69, 82, 32, 84, 87, 79, 2, 11, 32, 2, 159, 202, 12, 80, + 166, 1, 80, 7, 71, 82, 79, 85, 78, 68, 32, 29, 9, 73, 78, 71, 32, 67, 65, + 82, 68, 32, 2, 173, 171, 4, 2, 83, 76, 164, 1, 182, 1, 66, 44, 3, 82, 69, + 68, 0, 5, 87, 72, 73, 84, 69, 42, 70, 74, 75, 38, 69, 34, 83, 36, 3, 81, + 85, 69, 14, 84, 92, 2, 65, 67, 0, 3, 78, 73, 78, 13, 4, 74, 65, 67, 75, + 4, 40, 4, 76, 65, 67, 75, 135, 169, 11, 65, 2, 17, 2, 32, 74, 2, 219, + 237, 1, 79, 18, 30, 79, 249, 1, 2, 73, 86, 10, 128, 2, 2, 85, 82, 239, + 204, 11, 79, 16, 34, 78, 185, 1, 3, 73, 78, 71, 8, 181, 1, 4, 73, 71, 72, + 84, 16, 32, 2, 69, 86, 117, 2, 73, 88, 8, 91, 69, 66, 78, 69, 12, 3, 72, + 82, 69, 12, 2, 87, 79, 161, 1, 5, 82, 85, 77, 80, 45, 8, 23, 78, 8, 11, + 69, 8, 25, 4, 32, 79, 70, 32, 8, 88, 3, 67, 76, 85, 20, 3, 83, 80, 65, + 250, 145, 9, 72, 137, 3, 5, 68, 73, 65, 77, 79, 2, 231, 153, 12, 66, 2, + 171, 193, 11, 68, 42, 90, 50, 190, 146, 10, 49, 134, 196, 2, 51, 2, 52, + 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 7, 190, 214, 12, 48, 3, 49, 41, 46, + 83, 160, 4, 2, 84, 79, 175, 128, 9, 78, 26, 52, 5, 32, 83, 73, 71, 78, + 141, 163, 9, 2, 45, 77, 25, 11, 32, 22, 64, 3, 73, 78, 32, 124, 5, 87, + 73, 84, 72, 32, 215, 255, 3, 65, 6, 34, 76, 22, 82, 195, 173, 11, 84, 2, + 41, 2, 69, 70, 2, 21, 3, 73, 71, 72, 2, 233, 255, 10, 6, 84, 32, 72, 65, + 76, 70, 14, 162, 1, 68, 34, 83, 140, 176, 10, 4, 84, 73, 76, 68, 152, + 123, 5, 66, 76, 65, 67, 75, 201, 38, 16, 67, 73, 82, 67, 85, 77, 70, 76, + 69, 88, 32, 65, 67, 67, 69, 78, 2, 11, 79, 2, 167, 161, 10, 84, 4, 54, + 77, 173, 181, 5, 7, 85, 66, 83, 67, 82, 73, 80, 2, 197, 170, 9, 3, 65, + 76, 76, 11, 33, 6, 32, 70, 79, 82, 77, 32, 8, 162, 222, 10, 70, 71, 84, + 2, 157, 152, 12, 8, 32, 84, 82, 65, 78, 83, 73, 83, 58, 232, 1, 5, 76, + 73, 67, 69, 32, 122, 80, 140, 1, 11, 82, 84, 65, 66, 76, 69, 32, 83, 84, + 69, 82, 22, 83, 158, 1, 84, 146, 1, 85, 196, 1, 4, 87, 69, 82, 32, 210, + 142, 7, 79, 157, 129, 5, 11, 67, 75, 69, 84, 32, 67, 65, 76, 67, 85, 76, + 6, 52, 3, 67, 65, 82, 217, 253, 3, 4, 79, 70, 70, 73, 5, 217, 177, 10, + 11, 83, 32, 82, 69, 86, 79, 76, 86, 73, 78, 71, 6, 76, 13, 32, 68, 73, + 82, 69, 67, 84, 73, 79, 78, 65, 76, 32, 159, 215, 1, 67, 4, 158, 128, 1, + 73, 169, 190, 6, 6, 70, 79, 82, 77, 65, 84, 2, 251, 173, 12, 69, 12, 40, + 2, 69, 73, 22, 84, 243, 140, 5, 73, 2, 195, 252, 11, 68, 8, 36, 3, 65, + 76, 32, 171, 189, 11, 66, 6, 226, 213, 1, 72, 213, 206, 6, 4, 77, 65, 82, + 75, 8, 66, 65, 238, 135, 2, 32, 153, 183, 9, 6, 84, 69, 68, 32, 80, 76, + 4, 26, 66, 239, 171, 12, 84, 2, 29, 5, 76, 69, 32, 87, 65, 2, 243, 48, + 84, 12, 108, 4, 76, 84, 82, 89, 28, 7, 82, 73, 78, 71, 32, 76, 73, 28, 2, + 84, 73, 202, 221, 10, 78, 179, 234, 1, 67, 2, 213, 131, 12, 2, 32, 76, 2, + 205, 179, 5, 2, 81, 85, 4, 149, 223, 10, 2, 78, 71, 8, 24, 2, 79, 78, 47, + 83, 4, 136, 179, 11, 4, 45, 79, 70, 70, 15, 32, 4, 156, 152, 9, 3, 76, + 69, 69, 231, 154, 2, 89, 140, 1, 74, 69, 162, 10, 73, 230, 2, 79, 237, + 221, 9, 6, 65, 89, 69, 82, 32, 66, 102, 132, 1, 6, 71, 78, 65, 78, 84, + 32, 66, 83, 252, 191, 5, 4, 67, 69, 68, 69, 192, 207, 1, 5, 86, 73, 79, + 85, 83, 241, 32, 2, 84, 90, 6, 42, 87, 202, 193, 8, 80, 143, 184, 2, 77, + 2, 199, 199, 7, 79, 70, 176, 1, 27, 69, 78, 84, 65, 84, 73, 79, 78, 32, + 70, 79, 82, 77, 32, 70, 79, 82, 32, 86, 69, 82, 84, 73, 67, 65, 76, 32, + 129, 216, 8, 10, 67, 82, 73, 80, 84, 73, 79, 78, 32, 84, 68, 198, 1, 67, + 22, 69, 46, 72, 50, 73, 94, 76, 188, 1, 6, 82, 73, 71, 72, 84, 32, 192, + 2, 6, 87, 65, 86, 89, 32, 76, 178, 156, 4, 83, 252, 217, 4, 9, 84, 87, + 79, 32, 68, 79, 84, 32, 76, 139, 114, 81, 4, 191, 190, 2, 79, 6, 158, + 158, 6, 88, 182, 227, 2, 77, 3, 78, 2, 173, 154, 9, 7, 79, 82, 73, 90, + 79, 78, 84, 4, 49, 10, 68, 69, 79, 71, 82, 65, 80, 72, 73, 67, 4, 11, 32, + 4, 218, 235, 9, 67, 35, 70, 22, 40, 4, 69, 70, 84, 32, 143, 214, 10, 79, + 20, 112, 6, 87, 72, 73, 84, 69, 32, 142, 1, 66, 42, 68, 186, 100, 67, + 166, 7, 65, 222, 203, 7, 80, 238, 7, 83, 39, 84, 4, 162, 2, 67, 255, 99, + 76, 22, 110, 66, 42, 68, 36, 6, 87, 72, 73, 84, 69, 32, 150, 100, 67, + 166, 7, 65, 222, 203, 7, 80, 238, 7, 83, 39, 84, 2, 145, 101, 6, 76, 65, + 67, 75, 32, 76, 2, 197, 107, 5, 79, 85, 66, 76, 69, 6, 74, 67, 17, 14, + 76, 69, 78, 84, 73, 67, 85, 76, 65, 82, 32, 66, 82, 65, 2, 227, 99, 79, + 4, 246, 234, 11, 67, 177, 29, 2, 75, 67, 2, 187, 210, 10, 79, 22, 46, 78, + 156, 1, 2, 86, 65, 231, 164, 12, 77, 10, 34, 84, 133, 211, 6, 2, 67, 69, + 6, 26, 32, 53, 2, 69, 82, 2, 21, 3, 83, 67, 82, 2, 205, 193, 10, 2, 69, + 69, 5, 17, 2, 32, 73, 2, 223, 235, 11, 67, 10, 60, 5, 67, 89, 32, 77, 69, + 29, 6, 84, 69, 32, 85, 83, 69, 2, 213, 171, 7, 2, 83, 83, 8, 26, 32, 231, + 180, 8, 45, 4, 134, 166, 10, 84, 139, 121, 79, 14, 98, 74, 30, 80, 90, + 83, 156, 34, 5, 72, 73, 66, 73, 84, 173, 245, 8, 6, 66, 73, 78, 71, 32, + 67, 2, 213, 167, 5, 2, 69, 67, 6, 64, 6, 79, 82, 84, 73, 79, 78, 137, + 157, 11, 4, 69, 82, 84, 89, 5, 183, 215, 5, 65, 2, 173, 250, 10, 4, 69, + 82, 80, 73, 60, 76, 14, 65, 76, 84, 69, 82, 32, 80, 65, 72, 76, 65, 86, + 73, 32, 215, 5, 89, 58, 172, 1, 15, 70, 79, 85, 82, 32, 68, 79, 84, 83, + 32, 87, 73, 84, 72, 32, 32, 7, 76, 69, 84, 84, 69, 82, 32, 230, 2, 78, + 154, 32, 83, 1, 8, 84, 85, 82, 78, 69, 68, 32, 83, 4, 246, 242, 10, 67, + 247, 114, 68, 36, 166, 1, 65, 22, 68, 34, 76, 22, 77, 50, 87, 254, 169, + 4, 71, 90, 90, 34, 83, 66, 89, 198, 207, 1, 72, 234, 5, 75, 174, 81, 66, + 170, 225, 4, 78, 134, 2, 84, 219, 103, 80, 2, 223, 170, 4, 76, 2, 11, 65, + 2, 227, 210, 6, 76, 2, 251, 170, 4, 65, 2, 25, 4, 69, 77, 45, 81, 2, 255, + 128, 6, 79, 2, 37, 7, 65, 87, 45, 65, 89, 73, 78, 2, 149, 205, 1, 2, 45, + 82, 14, 33, 6, 85, 77, 66, 69, 82, 32, 14, 42, 84, 202, 170, 4, 79, 191, + 236, 2, 70, 8, 42, 87, 250, 191, 10, 72, 207, 162, 1, 69, 4, 182, 194, + 10, 69, 239, 239, 1, 79, 2, 243, 132, 12, 67, 18, 252, 1, 4, 78, 67, 84, 85, 94, 82, 56, 19, 84, 32, 76, 73, 84, 84, 69, 82, 32, 73, 78, 32, 73, - 84, 83, 32, 80, 76, 65, 214, 151, 1, 83, 140, 42, 20, 66, 76, 73, 67, 32, - 65, 68, 68, 82, 69, 83, 83, 32, 76, 79, 85, 68, 83, 80, 69, 194, 218, 10, - 49, 3, 50, 4, 136, 206, 10, 9, 83, 32, 69, 76, 69, 86, 65, 84, 85, 229, - 144, 1, 5, 65, 84, 73, 79, 78, 4, 32, 2, 80, 76, 227, 133, 12, 83, 2, - 247, 148, 11, 69, 2, 11, 67, 2, 235, 136, 11, 69, 40, 98, 65, 180, 6, 6, - 69, 83, 84, 73, 79, 78, 162, 133, 6, 79, 165, 131, 5, 5, 73, 78, 67, 85, + 84, 83, 32, 80, 76, 65, 178, 151, 1, 83, 132, 42, 20, 66, 76, 73, 67, 32, + 65, 68, 68, 82, 69, 83, 83, 32, 76, 79, 85, 68, 83, 80, 69, 166, 237, 10, + 49, 3, 50, 4, 164, 222, 10, 9, 83, 32, 69, 76, 69, 86, 65, 84, 85, 129, + 147, 1, 5, 65, 84, 73, 79, 78, 4, 32, 2, 80, 76, 155, 152, 12, 83, 2, + 175, 167, 11, 69, 2, 11, 67, 2, 135, 153, 11, 69, 40, 98, 65, 180, 6, 6, + 69, 83, 84, 73, 79, 78, 254, 136, 6, 79, 229, 143, 5, 5, 73, 78, 67, 85, 78, 30, 104, 2, 68, 82, 240, 4, 9, 84, 69, 82, 78, 73, 79, 78, 32, 73, - 48, 4, 82, 84, 69, 82, 219, 243, 10, 79, 24, 56, 4, 65, 78, 84, 32, 157, + 48, 4, 82, 84, 69, 82, 143, 132, 11, 79, 24, 56, 4, 65, 78, 84, 32, 157, 4, 5, 85, 80, 76, 69, 32, 20, 44, 6, 85, 80, 80, 69, 82, 32, 131, 2, 76, 16, 56, 4, 76, 69, 70, 84, 249, 1, 5, 82, 73, 71, 72, 84, 11, 29, 5, 32, 65, 78, 68, 32, 8, 108, 6, 76, 79, 87, 69, 82, 32, 53, 17, 85, 80, 80, 69, 82, 32, 82, 73, 71, 72, 84, 32, 65, 78, 68, 32, 76, 4, 192, 1, 5, 76, - 69, 70, 84, 32, 231, 235, 11, 82, 4, 11, 79, 4, 11, 87, 4, 157, 236, 11, + 69, 70, 84, 32, 159, 254, 11, 82, 4, 11, 79, 4, 11, 87, 4, 213, 254, 11, 2, 69, 82, 7, 65, 14, 32, 65, 78, 68, 32, 76, 79, 87, 69, 82, 32, 76, 69, 70, 4, 11, 84, 5, 11, 32, 2, 21, 3, 65, 78, 68, 2, 17, 2, 32, 76, 2, 17, - 2, 79, 87, 2, 237, 196, 10, 2, 69, 82, 4, 22, 73, 223, 37, 80, 2, 205, - 213, 8, 7, 78, 84, 69, 71, 82, 65, 76, 2, 17, 2, 32, 78, 2, 131, 206, 10, - 79, 6, 34, 32, 233, 192, 5, 2, 69, 68, 4, 250, 134, 5, 69, 159, 204, 6, - 77, 220, 9, 114, 65, 142, 8, 69, 204, 27, 6, 72, 73, 78, 79, 67, 69, 34, - 73, 174, 88, 76, 46, 79, 198, 19, 85, 151, 131, 11, 83, 62, 110, 67, 104, - 2, 68, 73, 130, 1, 73, 162, 5, 84, 152, 237, 7, 4, 66, 66, 73, 84, 178, - 229, 3, 90, 235, 56, 77, 6, 40, 4, 73, 78, 71, 32, 187, 235, 8, 67, 4, - 164, 192, 10, 7, 77, 79, 84, 79, 82, 67, 89, 175, 48, 67, 8, 66, 79, 205, - 165, 8, 10, 67, 65, 76, 32, 83, 89, 77, 66, 79, 76, 7, 232, 174, 6, 4, - 65, 67, 84, 73, 129, 175, 4, 2, 32, 66, 36, 60, 5, 76, 87, 65, 89, 32, - 46, 78, 21, 4, 83, 69, 68, 32, 4, 140, 245, 8, 2, 84, 82, 203, 249, 1, - 67, 5, 191, 245, 10, 66, 28, 152, 1, 3, 68, 79, 84, 34, 73, 48, 4, 72, - 65, 78, 68, 182, 1, 77, 38, 83, 166, 167, 7, 70, 230, 146, 2, 67, 237, - 195, 1, 7, 66, 65, 67, 75, 32, 79, 70, 5, 29, 5, 84, 69, 68, 32, 73, 2, - 193, 40, 8, 78, 84, 69, 82, 80, 79, 76, 65, 7, 33, 6, 32, 87, 73, 84, 72, - 32, 4, 198, 27, 70, 209, 190, 2, 28, 80, 65, 82, 84, 32, 66, 69, 84, 87, + 2, 79, 87, 2, 137, 213, 10, 2, 69, 82, 4, 22, 73, 239, 37, 80, 2, 177, + 228, 8, 7, 78, 84, 69, 71, 82, 65, 76, 2, 17, 2, 32, 78, 2, 159, 222, 10, + 79, 6, 34, 32, 161, 197, 5, 2, 69, 68, 4, 162, 139, 5, 69, 175, 218, 6, + 77, 220, 9, 114, 65, 142, 8, 69, 220, 27, 6, 72, 73, 78, 79, 67, 69, 34, + 73, 250, 87, 76, 46, 79, 198, 19, 85, 243, 149, 11, 83, 62, 110, 67, 104, + 2, 68, 73, 130, 1, 73, 162, 5, 84, 172, 246, 7, 4, 66, 66, 73, 84, 214, + 238, 3, 90, 235, 56, 77, 6, 40, 4, 73, 78, 71, 32, 159, 250, 8, 67, 4, + 192, 208, 10, 7, 77, 79, 84, 79, 82, 67, 89, 199, 48, 67, 8, 66, 79, 141, + 180, 8, 10, 67, 65, 76, 32, 83, 89, 77, 66, 79, 76, 7, 168, 184, 6, 4, + 65, 67, 84, 73, 229, 181, 4, 2, 32, 66, 36, 60, 5, 76, 87, 65, 89, 32, + 46, 78, 21, 4, 83, 69, 68, 32, 4, 240, 131, 9, 2, 84, 82, 155, 251, 1, + 67, 5, 243, 133, 11, 66, 28, 152, 1, 3, 68, 79, 84, 34, 73, 48, 4, 72, + 65, 78, 68, 182, 1, 77, 38, 83, 166, 177, 7, 70, 206, 151, 2, 67, 161, + 197, 1, 7, 66, 65, 67, 75, 32, 79, 70, 5, 29, 5, 84, 69, 68, 32, 73, 2, + 209, 40, 8, 78, 84, 69, 82, 80, 79, 76, 65, 7, 33, 6, 32, 87, 73, 84, 72, + 32, 4, 214, 27, 70, 245, 194, 2, 28, 80, 65, 82, 84, 32, 66, 69, 84, 87, 69, 69, 78, 32, 77, 73, 68, 68, 76, 69, 32, 65, 78, 68, 32, 82, 73, 78, - 71, 6, 174, 133, 11, 67, 2, 68, 3, 82, 4, 60, 9, 77, 65, 76, 76, 32, 76, - 69, 70, 84, 255, 234, 10, 81, 2, 129, 145, 8, 2, 32, 83, 5, 187, 236, 11, + 71, 6, 230, 151, 11, 67, 2, 68, 3, 82, 4, 60, 9, 77, 65, 76, 76, 32, 76, + 69, 70, 84, 179, 251, 10, 81, 2, 229, 158, 8, 2, 32, 83, 5, 243, 254, 11, 73, 250, 1, 226, 1, 67, 132, 4, 2, 68, 32, 64, 2, 71, 73, 144, 1, 5, 74, 65, 78, 71, 32, 202, 4, 76, 32, 8, 77, 73, 78, 68, 69, 82, 32, 82, 34, - 80, 106, 83, 136, 1, 5, 84, 85, 82, 78, 32, 42, 86, 193, 199, 1, 5, 70, - 69, 82, 69, 78, 26, 114, 69, 60, 3, 89, 67, 76, 230, 204, 5, 79, 149, - 145, 4, 13, 82, 69, 65, 84, 73, 79, 78, 65, 76, 32, 86, 69, 72, 4, 38, - 73, 181, 182, 10, 3, 80, 84, 65, 2, 235, 235, 11, 80, 18, 78, 69, 53, 15, + 80, 106, 83, 136, 1, 5, 84, 85, 82, 78, 32, 42, 86, 209, 199, 1, 5, 70, + 69, 82, 69, 78, 26, 114, 69, 60, 3, 89, 67, 76, 146, 209, 5, 79, 205, + 155, 4, 13, 82, 69, 65, 84, 73, 79, 78, 65, 76, 32, 86, 69, 72, 4, 38, + 73, 209, 198, 10, 3, 80, 84, 65, 2, 163, 254, 11, 80, 18, 78, 69, 53, 15, 73, 78, 71, 32, 83, 89, 77, 66, 79, 76, 32, 70, 79, 82, 32, 2, 29, 5, 68, - 32, 80, 65, 80, 2, 131, 164, 10, 69, 16, 100, 5, 84, 89, 80, 69, 45, 133, - 254, 4, 14, 71, 69, 78, 69, 82, 73, 67, 32, 77, 65, 84, 69, 82, 73, 14, - 58, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 55, 2, 173, 206, 5, 6, 32, - 80, 76, 65, 83, 84, 4, 42, 65, 165, 251, 4, 4, 71, 73, 70, 84, 2, 199, - 186, 3, 80, 54, 120, 4, 83, 84, 69, 82, 249, 240, 5, 20, 79, 78, 65, 76, - 32, 73, 78, 68, 73, 67, 65, 84, 79, 82, 32, 83, 89, 77, 66, 79, 2, 155, - 155, 10, 69, 74, 128, 1, 15, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, + 32, 80, 65, 80, 2, 211, 179, 10, 69, 16, 100, 5, 84, 89, 80, 69, 45, 173, + 130, 5, 14, 71, 69, 78, 69, 82, 73, 67, 32, 77, 65, 84, 69, 82, 73, 14, + 58, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 55, 2, 133, 210, 5, 6, 32, + 80, 76, 65, 83, 84, 4, 42, 65, 205, 255, 4, 4, 71, 73, 70, 84, 2, 231, + 190, 3, 80, 54, 120, 4, 83, 84, 69, 82, 213, 244, 5, 20, 79, 78, 65, 76, + 32, 73, 78, 68, 73, 67, 65, 84, 79, 82, 32, 83, 89, 77, 66, 79, 2, 235, + 170, 10, 69, 74, 128, 1, 15, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 44, 7, 76, 69, 84, 84, 69, 82, 32, 226, 1, 83, 35, 86, 8, - 194, 135, 10, 78, 154, 251, 1, 72, 3, 82, 46, 162, 1, 78, 238, 243, 7, - 77, 234, 138, 4, 66, 2, 67, 2, 68, 2, 71, 2, 72, 2, 74, 2, 75, 2, 76, 2, - 80, 2, 82, 2, 83, 2, 84, 2, 87, 2, 89, 187, 2, 65, 12, 206, 244, 7, 89, - 142, 27, 71, 250, 238, 3, 68, 187, 2, 65, 2, 11, 69, 2, 155, 151, 11, 67, - 18, 64, 10, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 151, 199, 9, 73, 16, - 54, 69, 254, 190, 11, 65, 186, 64, 73, 2, 79, 3, 85, 7, 178, 255, 11, 65, - 3, 85, 2, 189, 238, 10, 3, 73, 69, 86, 2, 137, 175, 11, 3, 73, 66, 66, 2, - 41, 8, 76, 65, 67, 69, 77, 69, 78, 84, 2, 17, 2, 32, 67, 2, 157, 198, 10, - 5, 72, 65, 82, 65, 67, 8, 32, 2, 84, 82, 251, 244, 6, 80, 6, 180, 252, 7, - 16, 73, 67, 84, 69, 68, 32, 76, 69, 70, 84, 32, 69, 78, 84, 82, 89, 179, - 240, 3, 79, 6, 214, 233, 10, 83, 166, 104, 76, 31, 82, 70, 64, 4, 69, 82, - 83, 69, 165, 242, 3, 6, 79, 76, 86, 73, 78, 71, 68, 30, 32, 169, 3, 2, + 146, 151, 10, 78, 130, 254, 1, 72, 3, 82, 46, 162, 1, 78, 186, 253, 7, + 77, 214, 147, 4, 66, 2, 67, 2, 68, 2, 71, 2, 72, 2, 74, 2, 75, 2, 76, 2, + 80, 2, 82, 2, 83, 2, 84, 2, 87, 2, 89, 187, 2, 65, 12, 154, 254, 7, 89, + 166, 31, 71, 206, 243, 3, 68, 187, 2, 65, 2, 11, 69, 2, 211, 169, 11, 67, + 18, 64, 10, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 139, 214, 9, 73, 16, + 54, 69, 182, 209, 11, 65, 186, 64, 73, 2, 79, 3, 85, 7, 234, 145, 12, 65, + 3, 85, 2, 217, 254, 10, 3, 73, 69, 86, 2, 193, 193, 11, 3, 73, 66, 66, 2, + 41, 8, 76, 65, 67, 69, 77, 69, 78, 84, 2, 17, 2, 32, 67, 2, 193, 214, 10, + 5, 72, 65, 82, 65, 67, 8, 32, 2, 84, 82, 231, 254, 6, 80, 6, 152, 138, 8, + 16, 73, 67, 84, 69, 68, 32, 76, 69, 70, 84, 32, 69, 78, 84, 82, 89, 135, + 245, 3, 79, 6, 242, 249, 10, 83, 194, 106, 76, 31, 82, 70, 64, 4, 69, 82, + 83, 69, 197, 246, 3, 6, 79, 76, 86, 73, 78, 71, 68, 30, 32, 169, 3, 2, 68, 32, 20, 184, 1, 6, 67, 72, 69, 67, 75, 69, 28, 2, 76, 73, 108, 7, 83, - 79, 76, 73, 68, 85, 83, 212, 220, 7, 16, 84, 73, 76, 68, 69, 32, 79, 80, - 69, 82, 65, 84, 79, 82, 32, 65, 179, 246, 2, 73, 2, 145, 227, 10, 2, 82, - 32, 4, 248, 207, 3, 18, 71, 72, 84, 32, 70, 79, 85, 82, 32, 80, 79, 73, - 78, 84, 69, 68, 32, 80, 251, 236, 1, 78, 9, 11, 32, 6, 184, 162, 5, 9, - 80, 82, 69, 67, 69, 68, 73, 78, 71, 250, 150, 3, 79, 251, 154, 1, 87, 48, - 232, 2, 5, 65, 78, 71, 76, 69, 20, 7, 68, 79, 85, 66, 76, 69, 32, 118, - 78, 20, 5, 70, 79, 82, 75, 69, 70, 80, 82, 82, 214, 1, 83, 106, 84, 144, - 229, 6, 30, 72, 65, 78, 68, 32, 87, 73, 84, 72, 32, 77, 73, 68, 68, 76, - 69, 32, 70, 73, 78, 71, 69, 82, 32, 69, 88, 84, 69, 78, 68, 202, 185, 2, - 67, 114, 81, 156, 65, 3, 86, 73, 67, 177, 36, 6, 69, 77, 80, 84, 89, 32, - 5, 231, 227, 3, 32, 6, 40, 3, 80, 82, 73, 41, 3, 83, 84, 82, 4, 17, 2, - 77, 69, 5, 179, 180, 3, 32, 2, 29, 5, 79, 75, 69, 32, 78, 2, 235, 177, 6, - 79, 2, 49, 10, 68, 32, 80, 65, 82, 65, 71, 82, 65, 80, 2, 179, 4, 72, 4, - 36, 3, 73, 76, 67, 203, 219, 10, 82, 2, 11, 82, 2, 177, 236, 10, 2, 79, - 87, 6, 156, 1, 17, 65, 73, 83, 69, 68, 32, 72, 65, 78, 68, 32, 87, 73, - 84, 72, 32, 70, 200, 107, 8, 79, 84, 65, 84, 69, 68, 32, 70, 217, 166, 6, - 4, 73, 71, 72, 84, 2, 133, 113, 9, 73, 78, 71, 69, 82, 83, 32, 83, 80, 4, - 208, 134, 1, 17, 65, 78, 83, 45, 83, 69, 82, 73, 70, 32, 67, 65, 80, 73, - 84, 65, 76, 183, 159, 7, 69, 10, 88, 4, 73, 76, 68, 69, 28, 7, 82, 73, - 80, 76, 69, 32, 80, 241, 150, 7, 3, 72, 85, 77, 5, 213, 231, 4, 2, 32, - 69, 2, 235, 215, 10, 82, 2, 11, 82, 2, 231, 177, 11, 79, 147, 4, 228, 1, - 4, 66, 66, 79, 78, 152, 1, 3, 67, 69, 32, 60, 3, 71, 72, 84, 240, 81, 2, - 78, 71, 252, 1, 23, 83, 73, 78, 71, 32, 68, 73, 65, 71, 79, 78, 65, 76, - 32, 67, 82, 79, 83, 83, 73, 78, 71, 32, 226, 212, 5, 65, 255, 158, 4, 70, - 19, 37, 7, 32, 65, 82, 82, 79, 87, 32, 16, 88, 3, 76, 69, 70, 0, 4, 82, - 73, 71, 72, 158, 193, 4, 85, 141, 128, 7, 3, 68, 79, 87, 4, 171, 248, 1, - 84, 4, 26, 67, 199, 228, 9, 66, 2, 217, 145, 1, 3, 82, 65, 67, 224, 3, - 110, 32, 190, 36, 45, 152, 12, 11, 72, 65, 78, 68, 32, 73, 78, 84, 69, - 82, 73, 29, 6, 87, 65, 82, 68, 83, 32, 202, 1, 166, 2, 65, 132, 6, 2, 66, - 76, 62, 67, 184, 1, 2, 68, 79, 242, 1, 70, 60, 2, 72, 65, 176, 5, 13, 74, - 85, 83, 84, 73, 70, 73, 69, 68, 32, 76, 69, 70, 20, 2, 76, 79, 66, 78, - 82, 79, 122, 80, 148, 1, 2, 82, 65, 58, 83, 154, 6, 84, 160, 5, 9, 86, - 69, 82, 84, 73, 67, 65, 76, 32, 147, 1, 87, 28, 22, 78, 163, 4, 82, 22, - 24, 2, 68, 32, 39, 71, 4, 170, 50, 76, 21, 3, 85, 80, 80, 18, 26, 69, 17, - 2, 76, 69, 2, 203, 26, 82, 17, 11, 32, 14, 154, 1, 66, 44, 8, 68, 79, 84, - 84, 69, 68, 32, 83, 2, 83, 112, 5, 87, 73, 84, 72, 32, 153, 196, 10, 12, - 86, 65, 82, 73, 65, 78, 84, 32, 87, 73, 84, 72, 4, 153, 194, 10, 6, 82, - 65, 67, 75, 69, 84, 2, 37, 7, 85, 66, 83, 84, 73, 84, 85, 2, 25, 4, 84, - 73, 79, 78, 2, 21, 3, 32, 77, 65, 2, 231, 138, 1, 82, 4, 68, 11, 68, 79, - 87, 78, 87, 65, 82, 68, 83, 32, 90, 179, 232, 8, 65, 2, 233, 201, 10, 5, - 73, 71, 90, 65, 71, 6, 18, 67, 79, 82, 2, 41, 8, 32, 71, 82, 69, 65, 84, - 69, 82, 2, 249, 24, 4, 45, 84, 72, 65, 4, 41, 8, 79, 87, 32, 87, 73, 84, - 72, 32, 4, 188, 220, 7, 7, 67, 73, 82, 67, 76, 69, 68, 151, 180, 2, 83, - 4, 25, 4, 65, 67, 75, 32, 4, 130, 27, 76, 139, 205, 7, 84, 12, 38, 85, - 166, 26, 79, 207, 231, 2, 69, 8, 53, 11, 82, 76, 89, 32, 66, 82, 65, 67, - 75, 69, 84, 9, 11, 32, 6, 44, 4, 77, 73, 68, 68, 250, 10, 76, 27, 85, 2, - 201, 170, 10, 2, 76, 69, 12, 38, 84, 41, 5, 85, 66, 76, 69, 32, 2, 137, - 17, 6, 84, 69, 68, 32, 83, 85, 10, 50, 65, 94, 87, 198, 158, 3, 81, 131, - 190, 4, 80, 4, 162, 31, 78, 157, 157, 3, 15, 82, 82, 79, 87, 32, 87, 73, - 84, 72, 32, 82, 79, 85, 78, 68, 2, 139, 24, 73, 6, 26, 73, 247, 254, 2, - 76, 4, 154, 201, 8, 86, 175, 97, 83, 28, 32, 3, 76, 70, 32, 187, 4, 78, - 26, 128, 1, 8, 65, 78, 68, 32, 76, 69, 70, 84, 62, 66, 90, 70, 82, 72, - 50, 82, 58, 84, 70, 87, 246, 13, 76, 22, 85, 215, 212, 8, 77, 2, 29, 5, - 32, 72, 65, 76, 70, 2, 245, 202, 8, 2, 32, 87, 6, 11, 76, 6, 40, 4, 65, - 67, 75, 32, 163, 172, 10, 79, 4, 146, 138, 10, 67, 207, 11, 83, 4, 58, - 79, 221, 152, 3, 8, 76, 89, 73, 78, 71, 32, 83, 65, 2, 175, 187, 9, 76, - 2, 253, 201, 8, 7, 79, 82, 73, 90, 79, 78, 84, 2, 33, 6, 85, 78, 78, 73, - 78, 71, 2, 247, 228, 6, 32, 2, 209, 169, 5, 12, 82, 73, 80, 76, 69, 32, - 68, 65, 83, 72, 32, 72, 2, 189, 151, 10, 4, 72, 73, 84, 69, 2, 173, 152, - 1, 16, 68, 32, 84, 69, 76, 69, 80, 72, 79, 78, 69, 32, 82, 69, 67, 69, 4, - 231, 217, 7, 84, 2, 233, 133, 11, 11, 87, 32, 80, 65, 82, 65, 80, 72, 82, - 65, 83, 2, 177, 4, 16, 79, 82, 77, 65, 76, 32, 70, 65, 67, 84, 79, 82, - 32, 83, 69, 77, 8, 74, 85, 210, 206, 8, 78, 169, 188, 1, 8, 80, 69, 78, - 32, 83, 81, 85, 65, 2, 181, 215, 10, 6, 84, 69, 82, 32, 74, 79, 8, 49, - 10, 65, 82, 69, 78, 84, 72, 69, 83, 73, 83, 9, 11, 32, 6, 34, 76, 26, 85, - 171, 182, 9, 69, 2, 157, 37, 2, 79, 87, 2, 133, 37, 2, 80, 80, 2, 217, - 10, 10, 73, 83, 69, 68, 32, 79, 77, 73, 83, 83, 40, 62, 45, 70, 69, 78, - 73, 68, 2, 80, 69, 126, 81, 227, 2, 85, 2, 253, 241, 7, 12, 83, 72, 65, - 80, 69, 68, 32, 66, 65, 71, 32, 68, 4, 26, 77, 231, 221, 8, 86, 2, 205, - 196, 10, 7, 73, 68, 73, 82, 69, 67, 84, 4, 194, 146, 3, 78, 145, 238, 7, - 8, 68, 69, 87, 65, 89, 83, 32, 85, 8, 32, 4, 65, 75, 69, 82, 67, 69, 7, - 33, 6, 32, 87, 73, 84, 72, 32, 4, 214, 248, 3, 79, 55, 84, 2, 137, 5, 2, - 67, 72, 20, 57, 12, 85, 65, 82, 69, 32, 66, 82, 65, 67, 75, 69, 84, 21, - 11, 32, 18, 84, 3, 76, 79, 87, 0, 3, 85, 80, 80, 28, 5, 87, 73, 84, 72, - 32, 143, 177, 9, 69, 2, 213, 226, 3, 2, 69, 82, 12, 96, 8, 84, 73, 67, - 75, 32, 73, 78, 32, 230, 6, 81, 246, 221, 7, 85, 222, 125, 68, 183, 189, - 2, 83, 4, 212, 225, 3, 6, 66, 79, 84, 84, 79, 77, 1, 3, 84, 79, 80, 2, - 165, 4, 6, 66, 83, 84, 73, 84, 85, 26, 54, 72, 130, 3, 82, 174, 209, 7, - 79, 171, 202, 2, 65, 14, 62, 73, 116, 5, 79, 85, 71, 72, 84, 45, 4, 82, - 69, 69, 32, 4, 21, 3, 82, 68, 32, 4, 68, 4, 73, 78, 68, 85, 185, 207, 1, - 7, 87, 72, 73, 84, 69, 32, 82, 2, 175, 148, 11, 67, 2, 11, 32, 2, 197, - 132, 3, 3, 66, 85, 66, 8, 60, 9, 81, 85, 65, 82, 84, 69, 82, 83, 32, 191, - 215, 8, 69, 6, 34, 76, 22, 85, 179, 221, 8, 66, 2, 37, 2, 79, 87, 2, 17, - 2, 80, 80, 2, 139, 216, 8, 69, 8, 34, 65, 89, 4, 73, 65, 78, 71, 2, 33, - 6, 78, 83, 80, 79, 83, 73, 2, 11, 84, 2, 17, 2, 73, 79, 2, 235, 247, 10, - 78, 6, 32, 2, 76, 69, 199, 214, 8, 85, 5, 41, 8, 32, 65, 66, 79, 86, 69, - 32, 76, 2, 225, 163, 9, 2, 69, 70, 6, 18, 66, 95, 82, 4, 68, 9, 65, 82, - 32, 87, 73, 84, 72, 32, 81, 177, 175, 10, 2, 79, 88, 2, 251, 208, 8, 85, - 2, 241, 173, 9, 3, 85, 76, 69, 14, 22, 72, 203, 1, 73, 12, 25, 4, 73, 84, - 69, 32, 12, 54, 67, 54, 76, 250, 196, 7, 80, 238, 7, 83, 39, 84, 4, 26, - 79, 207, 193, 7, 85, 2, 65, 3, 82, 78, 69, 2, 41, 8, 69, 78, 84, 73, 67, - 85, 76, 65, 2, 143, 244, 10, 82, 2, 133, 190, 6, 6, 71, 71, 76, 89, 32, - 70, 62, 118, 70, 226, 2, 72, 128, 1, 9, 80, 79, 73, 78, 84, 73, 78, 71, - 32, 214, 4, 83, 197, 1, 6, 84, 79, 45, 76, 69, 70, 18, 33, 6, 65, 67, 73, - 78, 71, 32, 18, 116, 14, 65, 82, 77, 69, 78, 73, 65, 78, 32, 69, 84, 69, - 82, 78, 20, 4, 66, 65, 83, 83, 16, 3, 70, 73, 83, 87, 83, 2, 171, 145, 8, - 73, 2, 227, 43, 73, 6, 26, 72, 239, 195, 11, 84, 5, 11, 32, 2, 149, 162, - 8, 6, 87, 73, 84, 72, 32, 79, 8, 236, 203, 3, 10, 86, 65, 83, 84, 73, 32, - 83, 73, 71, 78, 143, 213, 4, 78, 2, 81, 18, 65, 78, 68, 69, 68, 32, 73, - 78, 84, 69, 82, 76, 65, 67, 69, 68, 32, 80, 2, 21, 3, 69, 78, 84, 2, 231, - 134, 10, 65, 30, 90, 65, 30, 67, 94, 68, 58, 77, 58, 82, 182, 1, 83, 74, - 84, 254, 157, 8, 69, 179, 124, 71, 4, 90, 78, 203, 160, 8, 84, 2, 29, 5, - 85, 82, 86, 69, 68, 2, 17, 2, 32, 65, 2, 11, 78, 2, 177, 237, 10, 2, 71, - 76, 4, 248, 254, 2, 5, 79, 85, 66, 76, 69, 131, 177, 6, 73, 2, 129, 168, - 10, 9, 65, 71, 78, 73, 70, 89, 73, 78, 71, 10, 38, 79, 206, 163, 9, 65, - 247, 28, 73, 6, 92, 5, 67, 75, 69, 84, 32, 137, 163, 9, 12, 76, 76, 69, - 82, 32, 67, 79, 65, 83, 84, 69, 82, 4, 228, 43, 3, 66, 79, 79, 179, 189, - 10, 83, 2, 11, 84, 2, 21, 3, 73, 67, 75, 2, 221, 177, 6, 4, 32, 70, 73, - 71, 2, 155, 220, 7, 65, 4, 46, 72, 85, 7, 73, 68, 69, 32, 65, 82, 67, 2, - 17, 2, 65, 68, 2, 17, 2, 69, 68, 2, 173, 156, 10, 6, 32, 87, 72, 73, 84, - 69, 2, 11, 32, 2, 241, 224, 8, 8, 67, 76, 79, 67, 75, 87, 73, 83, 8, 17, - 2, 84, 32, 8, 86, 73, 40, 4, 79, 86, 69, 82, 152, 130, 5, 5, 69, 77, 66, - 69, 68, 251, 246, 5, 77, 2, 17, 2, 83, 79, 2, 131, 135, 1, 76, 2, 247, - 150, 3, 82, 2, 129, 171, 10, 2, 79, 82, 214, 1, 160, 1, 5, 65, 82, 82, - 79, 87, 130, 9, 66, 86, 68, 210, 1, 70, 122, 72, 230, 6, 76, 26, 79, 34, - 80, 50, 82, 70, 83, 94, 84, 206, 8, 87, 190, 182, 8, 67, 47, 81, 75, 26, - 32, 223, 132, 9, 45, 70, 94, 65, 170, 2, 70, 110, 84, 184, 1, 5, 87, 73, - 84, 72, 32, 181, 157, 1, 4, 79, 86, 69, 82, 12, 40, 5, 66, 79, 86, 69, - 32, 143, 1, 78, 10, 70, 82, 140, 161, 1, 5, 83, 72, 79, 82, 84, 130, 193, - 3, 65, 55, 84, 4, 37, 7, 69, 86, 69, 82, 83, 69, 32, 4, 226, 225, 4, 65, - 55, 84, 2, 61, 13, 68, 32, 85, 80, 80, 69, 82, 32, 65, 78, 68, 32, 76, 2, - 17, 2, 79, 87, 2, 169, 198, 8, 2, 69, 82, 6, 25, 4, 82, 79, 77, 32, 6, - 36, 3, 66, 65, 82, 227, 198, 8, 68, 5, 189, 1, 6, 32, 84, 79, 32, 66, 76, - 10, 56, 7, 72, 82, 79, 85, 71, 72, 32, 65, 3, 79, 32, 66, 6, 184, 220, 4, - 3, 83, 85, 80, 198, 197, 4, 71, 155, 146, 2, 88, 4, 26, 76, 227, 250, 10, - 65, 2, 141, 231, 9, 3, 65, 67, 75, 40, 140, 1, 6, 67, 79, 82, 78, 69, 82, - 26, 68, 98, 76, 86, 80, 30, 83, 38, 84, 178, 195, 8, 77, 38, 78, 122, 69, - 226, 151, 1, 72, 143, 163, 1, 86, 2, 133, 19, 2, 32, 68, 4, 11, 79, 4, - 40, 4, 84, 84, 69, 68, 155, 205, 7, 85, 2, 17, 2, 32, 83, 2, 251, 158, - 11, 84, 6, 26, 79, 143, 196, 8, 65, 4, 26, 87, 211, 176, 11, 79, 2, 253, - 196, 3, 2, 69, 82, 2, 237, 130, 9, 2, 76, 85, 6, 186, 196, 8, 77, 251, - 187, 2, 84, 10, 206, 16, 73, 171, 3, 65, 8, 58, 65, 128, 12, 5, 79, 84, - 84, 79, 77, 175, 184, 8, 76, 2, 145, 2, 2, 67, 75, 14, 48, 6, 79, 85, 66, - 76, 69, 32, 207, 210, 8, 65, 12, 40, 5, 65, 82, 82, 79, 87, 135, 19, 68, - 11, 26, 32, 171, 250, 8, 45, 6, 26, 87, 207, 200, 8, 70, 4, 25, 4, 73, - 84, 72, 32, 4, 146, 253, 10, 86, 79, 83, 4, 40, 4, 82, 79, 78, 84, 235, - 195, 8, 73, 2, 233, 194, 8, 14, 45, 84, 73, 76, 84, 69, 68, 32, 83, 72, - 65, 68, 79, 87, 30, 26, 65, 163, 199, 8, 69, 26, 48, 6, 82, 80, 79, 79, - 78, 32, 219, 229, 10, 78, 24, 88, 8, 79, 86, 69, 82, 32, 76, 69, 70, 45, - 10, 87, 73, 84, 72, 32, 66, 65, 82, 66, 32, 2, 157, 132, 8, 6, 84, 87, - 65, 82, 68, 83, 22, 44, 4, 68, 79, 87, 78, 173, 1, 2, 85, 80, 10, 26, 32, - 219, 204, 8, 87, 8, 76, 8, 65, 66, 79, 86, 69, 32, 76, 69, 18, 66, 154, - 196, 8, 70, 179, 5, 84, 2, 227, 2, 70, 2, 177, 218, 4, 7, 69, 76, 79, 87, - 32, 76, 79, 12, 26, 32, 175, 203, 8, 87, 10, 60, 6, 65, 66, 79, 86, 69, - 32, 142, 195, 8, 70, 179, 5, 84, 6, 22, 76, 155, 1, 82, 4, 32, 2, 69, 70, - 239, 216, 4, 79, 2, 253, 139, 2, 24, 84, 87, 65, 82, 68, 83, 32, 72, 65, - 82, 80, 79, 79, 78, 32, 87, 73, 84, 72, 32, 66, 65, 82, 66, 2, 21, 3, 73, - 71, 72, 2, 105, 24, 84, 87, 65, 82, 68, 83, 32, 72, 65, 82, 80, 79, 79, - 78, 32, 87, 73, 84, 72, 32, 66, 65, 82, 66, 2, 11, 32, 2, 179, 236, 1, - 68, 2, 141, 1, 2, 69, 70, 2, 189, 197, 8, 3, 80, 69, 78, 4, 190, 201, 8, - 65, 181, 205, 1, 3, 85, 83, 72, 4, 36, 3, 73, 71, 72, 131, 210, 10, 79, - 2, 11, 84, 2, 171, 1, 45, 6, 32, 2, 81, 85, 203, 192, 8, 65, 4, 26, 73, - 227, 192, 8, 65, 2, 217, 200, 8, 2, 71, 71, 54, 38, 79, 60, 2, 82, 73, - 227, 4, 87, 2, 11, 80, 2, 11, 32, 2, 241, 184, 8, 4, 83, 72, 65, 68, 34, - 40, 5, 65, 78, 71, 76, 69, 255, 3, 80, 30, 56, 8, 45, 72, 69, 65, 68, 69, - 68, 32, 239, 204, 8, 32, 28, 52, 5, 65, 82, 82, 79, 87, 190, 197, 8, 68, - 39, 80, 25, 11, 32, 22, 64, 8, 79, 86, 69, 82, 32, 76, 69, 70, 22, 87, - 251, 192, 8, 84, 2, 171, 192, 8, 84, 18, 25, 4, 73, 84, 72, 32, 18, 128, - 1, 7, 68, 79, 85, 66, 76, 69, 32, 36, 7, 76, 79, 78, 71, 32, 84, 73, 218, - 192, 8, 66, 158, 1, 77, 34, 78, 34, 86, 35, 72, 4, 158, 251, 8, 72, 239, - 243, 1, 86, 4, 11, 80, 4, 11, 32, 4, 18, 68, 35, 85, 2, 169, 193, 8, 3, - 79, 87, 78, 2, 139, 193, 8, 80, 4, 21, 3, 76, 69, 32, 4, 138, 3, 68, 243, - 128, 10, 65, 18, 11, 79, 18, 56, 8, 45, 72, 69, 65, 68, 69, 68, 32, 163, - 195, 8, 32, 16, 76, 6, 65, 82, 82, 79, 87, 32, 213, 1, 8, 84, 82, 73, 80, - 76, 69, 32, 68, 14, 44, 5, 87, 73, 84, 72, 32, 167, 183, 8, 70, 12, 42, - 84, 238, 183, 7, 68, 243, 179, 3, 86, 8, 26, 65, 231, 194, 8, 82, 6, 17, - 2, 73, 76, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 150, 183, 7, 68, 243, - 179, 3, 86, 2, 137, 132, 1, 2, 65, 83, 8, 58, 65, 21, 10, 72, 73, 84, 69, - 32, 65, 82, 82, 79, 87, 2, 195, 191, 8, 86, 7, 11, 32, 4, 148, 189, 2, 4, - 70, 82, 79, 77, 147, 135, 6, 87, 19, 66, 32, 136, 1, 6, 69, 68, 32, 80, - 76, 65, 21, 3, 73, 78, 71, 12, 82, 66, 22, 80, 248, 196, 4, 2, 73, 78, - 26, 69, 238, 147, 3, 79, 239, 194, 2, 65, 2, 147, 135, 11, 85, 2, 11, 79, - 2, 171, 213, 10, 73, 2, 155, 227, 10, 78, 2, 253, 194, 3, 2, 32, 66, 4, - 24, 2, 70, 65, 75, 83, 2, 17, 2, 76, 76, 2, 21, 3, 73, 78, 71, 2, 217, - 226, 1, 2, 32, 68, 2, 233, 168, 3, 2, 79, 85, 8, 130, 151, 11, 69, 2, 73, - 2, 77, 3, 79, 126, 136, 2, 2, 67, 75, 20, 2, 76, 76, 188, 2, 4, 77, 65, - 78, 32, 194, 8, 79, 80, 2, 83, 69, 20, 6, 84, 65, 84, 69, 68, 32, 152, 3, - 3, 85, 78, 68, 214, 173, 3, 87, 200, 204, 3, 15, 65, 83, 84, 69, 68, 32, - 83, 87, 69, 69, 84, 32, 80, 79, 84, 205, 164, 2, 2, 66, 79, 5, 159, 248, - 10, 69, 10, 130, 1, 69, 64, 4, 32, 79, 70, 32, 101, 21, 73, 78, 71, 32, - 79, 78, 32, 84, 72, 69, 32, 70, 76, 79, 79, 82, 32, 76, 65, 85, 71, 6, - 60, 9, 68, 45, 85, 80, 32, 78, 69, 87, 83, 33, 2, 82, 32, 2, 11, 80, 2, - 215, 254, 1, 65, 4, 28, 3, 67, 79, 65, 23, 83, 2, 187, 218, 9, 83, 2, - 211, 94, 75, 2, 139, 193, 10, 72, 72, 140, 1, 6, 67, 69, 78, 84, 85, 82, - 22, 68, 100, 3, 81, 85, 73, 28, 8, 78, 85, 77, 69, 82, 65, 76, 32, 182, - 4, 83, 114, 85, 255, 219, 7, 65, 2, 187, 205, 5, 73, 6, 98, 69, 232, 5, - 5, 85, 80, 79, 78, 68, 61, 12, 73, 77, 73, 68, 73, 65, 32, 83, 69, 88, - 84, 85, 2, 229, 5, 3, 78, 65, 82, 48, 142, 1, 70, 136, 1, 3, 79, 78, 69, - 134, 1, 83, 66, 84, 232, 14, 10, 82, 69, 86, 69, 82, 83, 69, 68, 32, 79, - 210, 232, 2, 69, 143, 251, 6, 78, 14, 26, 73, 219, 149, 10, 79, 12, 36, - 3, 70, 84, 89, 187, 250, 2, 86, 7, 11, 32, 4, 222, 186, 5, 84, 185, 212, - 4, 5, 69, 65, 82, 76, 89, 11, 11, 32, 8, 50, 72, 41, 8, 84, 72, 79, 85, - 83, 65, 78, 68, 4, 185, 1, 6, 85, 78, 68, 82, 69, 68, 5, 197, 253, 3, 2, - 32, 67, 6, 32, 2, 73, 88, 171, 156, 9, 69, 5, 149, 141, 10, 2, 32, 76, - 10, 42, 69, 210, 248, 2, 87, 191, 163, 6, 72, 4, 11, 78, 5, 11, 32, 2, - 159, 184, 5, 84, 10, 46, 69, 181, 185, 7, 5, 73, 76, 73, 81, 85, 8, 60, - 2, 77, 85, 40, 5, 83, 84, 69, 82, 84, 21, 2, 88, 84, 2, 17, 2, 78, 67, 2, - 223, 184, 7, 73, 2, 199, 219, 7, 73, 4, 18, 65, 23, 85, 2, 171, 219, 7, - 78, 2, 143, 184, 7, 76, 4, 48, 6, 84, 32, 86, 69, 71, 69, 147, 209, 9, - 83, 2, 201, 192, 2, 2, 84, 65, 5, 243, 193, 9, 84, 10, 166, 2, 70, 32, - 11, 72, 69, 65, 86, 89, 32, 66, 76, 65, 67, 75, 52, 24, 76, 73, 71, 72, - 84, 32, 70, 79, 85, 82, 32, 80, 79, 73, 78, 84, 69, 68, 32, 66, 76, 65, - 67, 75, 0, 18, 87, 72, 73, 84, 69, 32, 70, 79, 85, 82, 32, 80, 79, 73, - 78, 84, 69, 68, 169, 53, 8, 67, 65, 80, 73, 84, 65, 76, 32, 2, 29, 5, 76, - 79, 82, 65, 76, 2, 213, 166, 9, 8, 32, 72, 69, 65, 82, 84, 32, 66, 2, - 149, 191, 9, 2, 32, 67, 16, 74, 32, 89, 14, 69, 68, 32, 83, 89, 77, 66, - 79, 76, 32, 70, 79, 82, 32, 4, 24, 2, 80, 85, 43, 84, 2, 11, 83, 2, 245, - 132, 10, 2, 72, 80, 2, 203, 217, 3, 65, 12, 68, 2, 83, 72, 230, 152, 6, - 67, 138, 198, 4, 70, 2, 76, 147, 17, 88, 4, 40, 4, 85, 65, 78, 71, 195, - 222, 10, 79, 2, 207, 239, 10, 88, 136, 2, 226, 1, 66, 20, 3, 71, 66, 89, - 40, 5, 76, 69, 45, 68, 69, 40, 3, 77, 73, 32, 154, 5, 78, 156, 26, 26, - 83, 83, 73, 65, 78, 32, 65, 83, 84, 82, 79, 76, 79, 71, 73, 67, 65, 76, - 32, 83, 89, 77, 66, 79, 76, 32, 231, 142, 5, 80, 2, 255, 228, 8, 76, 2, - 189, 249, 8, 5, 32, 70, 79, 79, 84, 2, 11, 76, 2, 149, 245, 5, 2, 65, 89, - 62, 68, 9, 70, 82, 65, 67, 84, 73, 79, 78, 32, 102, 78, 231, 193, 2, 68, - 8, 40, 4, 79, 78, 69, 32, 199, 159, 9, 84, 6, 34, 84, 134, 252, 8, 72, - 47, 81, 2, 179, 253, 8, 72, 36, 33, 6, 85, 77, 66, 69, 82, 32, 36, 76, 5, - 69, 73, 71, 72, 84, 38, 70, 92, 2, 78, 73, 22, 79, 18, 83, 83, 84, 4, - 202, 132, 3, 32, 223, 249, 7, 89, 8, 18, 73, 35, 79, 4, 130, 2, 86, 247, - 142, 9, 70, 4, 136, 2, 2, 85, 82, 207, 142, 9, 82, 4, 77, 2, 78, 69, 2, - 167, 1, 78, 8, 40, 4, 69, 86, 69, 78, 1, 2, 73, 88, 4, 250, 130, 3, 32, - 151, 232, 7, 84, 10, 34, 72, 50, 87, 131, 172, 10, 69, 4, 32, 2, 82, 69, - 211, 142, 9, 73, 2, 39, 69, 4, 26, 79, 195, 142, 9, 69, 2, 231, 129, 3, - 32, 182, 1, 32, 3, 73, 67, 32, 147, 25, 78, 178, 1, 220, 1, 6, 66, 69, - 76, 71, 84, 72, 20, 4, 67, 82, 79, 83, 20, 7, 76, 69, 84, 84, 69, 82, 32, - 210, 22, 83, 24, 6, 77, 85, 76, 84, 73, 80, 208, 181, 7, 5, 65, 82, 76, - 65, 85, 181, 201, 1, 7, 84, 86, 73, 77, 65, 68, 85, 2, 147, 150, 9, 79, - 2, 227, 182, 7, 83, 166, 1, 202, 4, 65, 110, 67, 98, 68, 126, 69, 82, 70, - 222, 1, 71, 104, 2, 72, 65, 50, 73, 220, 1, 5, 74, 69, 82, 65, 78, 34, - 75, 58, 76, 234, 1, 79, 128, 1, 13, 82, 65, 73, 68, 79, 32, 82, 65, 68, - 32, 82, 69, 73, 34, 83, 176, 2, 16, 66, 69, 82, 75, 65, 78, 65, 78, 32, - 66, 69, 79, 82, 67, 32, 66, 144, 1, 12, 78, 65, 85, 68, 73, 90, 32, 78, - 89, 68, 32, 78, 110, 84, 194, 1, 87, 176, 87, 7, 85, 82, 85, 90, 32, 85, - 82, 192, 188, 2, 10, 77, 65, 78, 78, 65, 90, 32, 77, 65, 78, 180, 63, 13, - 80, 69, 82, 84, 72, 79, 32, 80, 69, 79, 82, 84, 72, 142, 189, 3, 89, 202, - 210, 3, 81, 2, 86, 2, 88, 3, 90, 8, 222, 4, 69, 174, 175, 6, 67, 0, 4, - 78, 83, 85, 90, 253, 179, 3, 9, 76, 71, 73, 90, 32, 69, 79, 76, 72, 11, - 46, 69, 30, 65, 145, 138, 7, 3, 87, 69, 79, 4, 26, 65, 247, 242, 10, 78, - 2, 247, 202, 9, 76, 11, 84, 6, 79, 84, 84, 69, 68, 45, 249, 226, 3, 9, - 65, 71, 65, 90, 32, 68, 65, 69, 71, 6, 134, 242, 10, 76, 2, 78, 3, 80, - 11, 240, 74, 7, 72, 87, 65, 90, 32, 69, 72, 150, 238, 9, 65, 206, 55, 84, - 63, 78, 12, 120, 13, 82, 65, 78, 75, 83, 32, 67, 65, 83, 75, 69, 84, 32, - 137, 165, 7, 11, 69, 72, 85, 32, 70, 69, 79, 72, 32, 70, 69, 10, 46, 65, - 142, 178, 10, 73, 2, 79, 207, 60, 69, 4, 26, 69, 207, 239, 10, 67, 2, - 207, 199, 9, 83, 9, 26, 69, 195, 182, 10, 65, 4, 52, 7, 66, 79, 32, 71, - 89, 70, 85, 231, 238, 10, 82, 2, 143, 238, 10, 32, 4, 236, 8, 2, 69, 71, - 13, 4, 71, 76, 65, 90, 12, 156, 1, 2, 78, 71, 20, 9, 83, 65, 90, 32, 73, - 83, 32, 73, 83, 20, 5, 87, 65, 90, 32, 69, 252, 179, 10, 10, 67, 69, 76, - 65, 78, 68, 73, 67, 45, 89, 3, 79, 5, 199, 201, 6, 87, 2, 219, 172, 10, - 83, 2, 199, 235, 10, 79, 2, 11, 32, 2, 183, 236, 10, 74, 7, 21, 3, 65, - 85, 78, 4, 242, 232, 10, 32, 155, 3, 65, 12, 120, 15, 65, 85, 75, 65, 90, - 32, 76, 65, 71, 85, 32, 76, 79, 71, 82, 21, 11, 79, 78, 71, 45, 66, 82, - 65, 78, 67, 72, 45, 2, 187, 225, 9, 32, 10, 64, 3, 65, 82, 32, 158, 4, - 72, 62, 77, 66, 79, 167, 172, 10, 89, 2, 195, 211, 10, 65, 15, 150, 5, - 83, 0, 12, 84, 72, 65, 76, 65, 78, 32, 69, 84, 72, 69, 76, 224, 228, 10, - 4, 80, 69, 78, 45, 14, 69, 2, 78, 3, 79, 2, 11, 68, 2, 155, 176, 10, 32, - 26, 150, 1, 72, 244, 2, 18, 73, 71, 69, 76, 32, 76, 79, 78, 71, 45, 66, - 82, 65, 78, 67, 72, 45, 83, 200, 239, 6, 5, 79, 87, 73, 76, 79, 147, 171, - 2, 84, 21, 45, 9, 79, 82, 84, 45, 84, 87, 73, 71, 45, 18, 102, 66, 58, - 72, 62, 77, 30, 78, 38, 79, 42, 83, 186, 1, 84, 128, 163, 6, 2, 65, 82, - 199, 135, 4, 89, 2, 33, 6, 74, 65, 82, 75, 65, 78, 2, 171, 170, 9, 32, 2, - 25, 4, 65, 71, 65, 76, 2, 11, 76, 2, 195, 228, 10, 32, 2, 169, 150, 3, 2, - 65, 68, 2, 193, 149, 10, 4, 65, 85, 68, 32, 2, 17, 2, 83, 83, 2, 247, - 197, 10, 32, 2, 11, 79, 2, 187, 239, 6, 76, 4, 116, 15, 72, 85, 82, 73, - 83, 65, 90, 32, 84, 72, 85, 82, 83, 32, 84, 33, 10, 73, 87, 65, 90, 32, - 84, 73, 82, 32, 84, 2, 11, 72, 2, 155, 217, 5, 79, 2, 17, 2, 89, 82, 2, - 223, 198, 10, 32, 5, 41, 8, 85, 78, 74, 79, 32, 87, 89, 78, 2, 11, 78, 2, - 159, 228, 9, 32, 2, 21, 3, 73, 78, 71, 2, 229, 159, 7, 2, 76, 69, 4, 200, - 249, 8, 16, 73, 78, 71, 32, 83, 72, 73, 82, 84, 32, 87, 73, 84, 72, 32, - 83, 211, 175, 1, 69, 12, 120, 3, 66, 73, 78, 2, 78, 28, 2, 81, 85, 0, 3, - 86, 73, 71, 166, 145, 7, 83, 229, 169, 1, 6, 84, 82, 69, 68, 69, 67, 2, - 161, 187, 8, 2, 79, 86, 2, 249, 186, 8, 2, 73, 78, 250, 39, 244, 1, 2, - 32, 73, 22, 65, 238, 25, 67, 138, 5, 69, 166, 11, 72, 190, 33, 73, 198, - 232, 1, 75, 154, 2, 76, 142, 9, 77, 166, 21, 78, 174, 2, 79, 158, 39, 80, - 172, 12, 2, 81, 85, 146, 70, 83, 38, 84, 250, 16, 85, 166, 43, 87, 186, - 1, 89, 251, 166, 5, 71, 2, 239, 184, 9, 78, 196, 2, 140, 2, 5, 70, 69, - 84, 89, 32, 36, 4, 71, 73, 84, 84, 30, 76, 112, 8, 77, 65, 82, 73, 84, - 65, 78, 32, 142, 14, 78, 190, 3, 84, 100, 2, 85, 82, 244, 239, 2, 2, 73, - 76, 234, 91, 88, 144, 134, 6, 15, 75, 69, 32, 66, 79, 84, 84, 76, 69, 32, - 65, 78, 68, 32, 67, 159, 98, 82, 4, 138, 254, 1, 86, 223, 221, 7, 80, 2, - 245, 208, 3, 2, 65, 82, 6, 18, 84, 75, 85, 4, 36, 3, 32, 83, 72, 139, - 186, 9, 73, 2, 11, 65, 2, 199, 156, 10, 75, 2, 255, 209, 9, 84, 122, 184, - 1, 7, 76, 69, 84, 84, 69, 82, 32, 198, 3, 77, 248, 2, 12, 80, 85, 78, 67, - 84, 85, 65, 84, 73, 79, 78, 32, 132, 4, 11, 86, 79, 87, 69, 76, 32, 83, - 73, 71, 78, 32, 203, 155, 4, 65, 44, 202, 1, 66, 32, 2, 68, 65, 22, 73, - 38, 75, 22, 76, 34, 83, 46, 84, 182, 2, 65, 196, 221, 5, 2, 71, 65, 222, - 148, 1, 82, 146, 141, 2, 90, 154, 81, 77, 172, 1, 2, 81, 85, 118, 78, - 226, 6, 89, 251, 101, 70, 4, 214, 186, 10, 73, 247, 25, 65, 2, 251, 194, - 9, 76, 6, 206, 214, 10, 78, 2, 84, 3, 89, 2, 251, 212, 9, 65, 2, 11, 65, - 2, 163, 194, 9, 66, 4, 152, 7, 3, 73, 78, 71, 223, 132, 9, 72, 6, 154, - 212, 9, 65, 134, 101, 73, 229, 10, 5, 83, 65, 65, 68, 73, 18, 96, 4, 65, - 82, 75, 32, 165, 1, 15, 79, 68, 73, 70, 73, 69, 82, 32, 76, 69, 84, 84, - 69, 82, 32, 12, 82, 68, 40, 2, 73, 78, 90, 69, 242, 2, 78, 197, 176, 8, - 5, 79, 67, 67, 76, 85, 2, 17, 2, 65, 71, 2, 159, 235, 8, 69, 5, 17, 2, - 45, 65, 2, 231, 209, 9, 76, 6, 46, 69, 180, 6, 2, 83, 72, 163, 204, 10, - 73, 2, 129, 217, 9, 11, 80, 69, 78, 84, 72, 69, 84, 73, 67, 32, 89, 28, - 130, 1, 65, 154, 1, 66, 22, 78, 30, 83, 130, 1, 90, 174, 162, 3, 84, 136, - 135, 7, 9, 77, 69, 76, 79, 68, 73, 67, 32, 81, 3, 81, 10, 76, 3, 70, 83, - 65, 34, 78, 28, 2, 84, 77, 145, 171, 10, 4, 82, 75, 65, 65, 2, 11, 65, 2, - 179, 208, 10, 81, 4, 26, 78, 187, 196, 5, 71, 2, 11, 65, 2, 143, 171, 10, - 65, 2, 201, 138, 3, 2, 69, 81, 4, 64, 4, 72, 73, 89, 89, 41, 8, 79, 70, - 32, 77, 65, 83, 72, 70, 2, 17, 2, 65, 65, 2, 163, 220, 8, 76, 2, 243, - 186, 9, 65, 4, 34, 65, 217, 219, 8, 2, 73, 81, 2, 255, 204, 9, 69, 30, - 92, 5, 76, 79, 78, 71, 32, 54, 79, 66, 83, 186, 224, 4, 65, 134, 236, 5, - 69, 2, 73, 3, 85, 10, 170, 225, 4, 65, 134, 236, 5, 69, 2, 73, 3, 85, 7, - 41, 8, 86, 69, 82, 76, 79, 78, 71, 32, 4, 203, 224, 4, 65, 4, 26, 72, - 171, 209, 5, 85, 2, 253, 139, 6, 3, 79, 82, 84, 10, 68, 8, 83, 45, 83, - 69, 82, 73, 70, 32, 145, 169, 10, 3, 68, 87, 73, 8, 44, 6, 72, 69, 65, - 86, 89, 32, 139, 2, 73, 6, 48, 3, 68, 79, 85, 81, 5, 76, 79, 87, 32, 68, - 4, 11, 66, 4, 21, 3, 76, 69, 32, 4, 84, 6, 84, 85, 82, 78, 69, 68, 23, - 67, 2, 21, 3, 79, 85, 66, 2, 17, 2, 76, 69, 2, 17, 2, 32, 67, 2, 33, 6, - 79, 77, 77, 65, 32, 81, 2, 205, 255, 8, 3, 85, 79, 84, 2, 157, 133, 10, - 10, 78, 84, 69, 82, 82, 79, 66, 65, 78, 71, 6, 48, 6, 69, 76, 76, 73, 84, - 69, 251, 189, 5, 85, 5, 25, 4, 32, 65, 78, 84, 2, 135, 145, 7, 69, 166, - 1, 52, 7, 65, 83, 72, 84, 82, 65, 32, 235, 187, 4, 79, 164, 1, 180, 1, 7, - 76, 69, 84, 84, 69, 82, 32, 212, 1, 5, 83, 73, 71, 78, 32, 194, 21, 68, - 156, 246, 2, 16, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, - 32, 72, 139, 150, 2, 86, 100, 198, 235, 6, 65, 38, 68, 114, 84, 46, 86, - 186, 5, 85, 202, 141, 1, 79, 134, 60, 73, 42, 76, 250, 192, 1, 78, 46, - 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 162, 7, 69, 234, 61, 72, - 2, 77, 2, 82, 3, 89, 8, 210, 175, 6, 67, 202, 207, 3, 65, 239, 1, 86, 52, - 66, 65, 32, 4, 72, 79, 79, 76, 46, 79, 74, 82, 143, 194, 10, 73, 4, 166, - 175, 9, 76, 215, 18, 82, 5, 153, 182, 5, 6, 32, 83, 65, 84, 67, 72, 6, - 36, 3, 82, 80, 73, 183, 138, 9, 79, 4, 202, 242, 9, 79, 135, 18, 85, 36, - 66, 69, 72, 4, 73, 80, 84, 32, 198, 246, 1, 85, 255, 194, 6, 79, 4, 36, - 3, 87, 68, 82, 195, 241, 9, 69, 2, 11, 73, 2, 167, 131, 10, 86, 28, 80, - 8, 67, 65, 80, 73, 84, 65, 76, 32, 86, 76, 81, 6, 83, 77, 65, 76, 76, 32, - 18, 170, 192, 10, 66, 2, 69, 2, 70, 2, 72, 2, 73, 2, 76, 2, 77, 2, 80, 3, - 82, 2, 37, 7, 73, 71, 65, 84, 85, 82, 69, 2, 11, 32, 2, 193, 251, 9, 2, - 69, 84, 8, 134, 191, 10, 69, 2, 71, 2, 76, 3, 79, 220, 1, 130, 2, 65, 30, - 67, 74, 69, 36, 5, 71, 77, 69, 78, 84, 28, 2, 77, 73, 172, 1, 8, 80, 65, - 82, 65, 84, 69, 68, 32, 138, 4, 82, 158, 1, 83, 68, 2, 84, 32, 140, 131, - 5, 8, 87, 73, 78, 71, 32, 78, 69, 69, 146, 140, 3, 88, 254, 104, 68, 213, - 160, 1, 2, 76, 70, 4, 210, 188, 10, 76, 3, 84, 6, 34, 84, 133, 217, 5, 2, - 79, 78, 4, 222, 187, 6, 73, 219, 199, 3, 79, 4, 130, 220, 1, 68, 195, - 132, 1, 45, 23, 221, 187, 4, 2, 69, 68, 6, 176, 80, 28, 68, 73, 82, 69, - 67, 84, 32, 80, 82, 79, 68, 85, 67, 84, 32, 87, 73, 84, 72, 32, 66, 79, - 84, 84, 79, 77, 32, 67, 252, 197, 7, 3, 83, 69, 88, 255, 212, 1, 67, 158, - 1, 48, 6, 66, 76, 79, 67, 75, 32, 191, 166, 9, 83, 156, 1, 88, 9, 81, 85, - 65, 68, 82, 65, 78, 84, 45, 129, 1, 8, 83, 69, 88, 84, 65, 78, 84, 45, - 30, 42, 49, 38, 50, 30, 51, 131, 184, 10, 52, 17, 34, 50, 30, 51, 131, - 184, 10, 52, 9, 26, 51, 131, 184, 10, 52, 5, 255, 183, 10, 52, 126, 58, - 49, 54, 50, 46, 51, 38, 52, 30, 53, 147, 182, 10, 54, 65, 50, 50, 46, 51, - 38, 52, 30, 53, 147, 182, 10, 54, 33, 42, 51, 38, 52, 30, 53, 147, 182, - 10, 54, 17, 34, 52, 30, 53, 147, 182, 10, 54, 9, 26, 53, 147, 182, 10, - 54, 5, 143, 182, 10, 54, 4, 120, 2, 86, 73, 241, 170, 2, 22, 73, 79, 85, - 83, 32, 70, 65, 67, 69, 32, 87, 73, 84, 72, 32, 83, 89, 77, 66, 79, 76, - 83, 2, 11, 67, 2, 175, 243, 9, 69, 4, 52, 7, 81, 85, 73, 81, 85, 65, 68, - 219, 253, 8, 65, 2, 91, 82, 4, 64, 10, 84, 82, 65, 78, 83, 77, 73, 84, - 32, 83, 195, 173, 6, 77, 2, 11, 84, 2, 211, 236, 8, 65, 242, 2, 102, 65, - 246, 19, 73, 102, 79, 134, 11, 69, 70, 82, 136, 131, 9, 5, 85, 70, 70, - 76, 69, 167, 143, 1, 89, 176, 2, 164, 1, 12, 68, 79, 87, 69, 68, 32, 87, - 72, 73, 84, 69, 32, 56, 9, 76, 76, 79, 87, 32, 80, 65, 78, 32, 62, 82, - 210, 9, 86, 172, 184, 7, 2, 77, 82, 147, 230, 1, 75, 6, 162, 223, 8, 67, - 206, 11, 83, 237, 4, 3, 76, 65, 84, 2, 17, 2, 79, 70, 2, 17, 2, 32, 70, - 2, 231, 249, 6, 79, 194, 1, 40, 4, 65, 68, 65, 32, 255, 175, 10, 75, 192, - 1, 162, 1, 68, 46, 69, 110, 72, 34, 76, 246, 1, 83, 212, 2, 6, 86, 79, - 87, 69, 76, 32, 250, 189, 4, 65, 228, 211, 1, 7, 67, 79, 78, 84, 73, 78, - 85, 163, 134, 4, 79, 24, 238, 209, 6, 79, 66, 65, 147, 235, 1, 73, 4, 84, - 15, 88, 84, 82, 65, 32, 83, 72, 79, 82, 84, 32, 86, 79, 87, 69, 215, 242, - 8, 75, 2, 139, 236, 9, 76, 2, 177, 253, 9, 3, 69, 65, 68, 96, 33, 6, 69, - 84, 84, 69, 82, 32, 96, 214, 210, 6, 65, 38, 68, 114, 84, 46, 86, 186, 5, - 85, 206, 201, 1, 73, 42, 76, 250, 192, 1, 78, 46, 83, 82, 66, 2, 67, 2, - 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 77, 2, 82, 2, 89, 186, 2, 69, 3, - 79, 30, 70, 65, 38, 69, 56, 4, 73, 71, 78, 32, 233, 164, 9, 3, 85, 84, - 82, 2, 153, 233, 9, 4, 78, 68, 72, 73, 6, 180, 201, 2, 5, 67, 84, 73, 79, - 78, 191, 179, 4, 80, 20, 106, 73, 190, 134, 5, 83, 158, 142, 1, 67, 98, - 78, 242, 60, 65, 174, 158, 1, 74, 150, 3, 85, 167, 242, 1, 86, 2, 37, 7, - 78, 86, 69, 82, 84, 69, 68, 2, 173, 148, 6, 2, 32, 67, 30, 60, 6, 77, 79, - 68, 73, 70, 73, 21, 5, 83, 73, 71, 78, 32, 2, 151, 137, 6, 69, 28, 82, - 80, 226, 211, 6, 65, 38, 85, 22, 86, 186, 201, 1, 73, 222, 137, 2, 69, 3, - 79, 2, 57, 12, 82, 73, 83, 72, 84, 72, 65, 77, 65, 84, 82, 65, 2, 155, - 144, 10, 32, 98, 72, 3, 69, 68, 32, 21, 11, 73, 65, 78, 32, 76, 69, 84, - 84, 69, 82, 32, 2, 147, 233, 9, 73, 96, 158, 2, 65, 120, 3, 67, 72, 85, - 22, 69, 70, 72, 46, 73, 46, 76, 22, 77, 38, 79, 94, 80, 18, 82, 22, 83, - 38, 84, 64, 2, 87, 79, 36, 2, 89, 69, 156, 235, 4, 2, 74, 85, 234, 222, - 2, 68, 202, 13, 90, 242, 87, 70, 182, 6, 71, 150, 45, 66, 246, 11, 75, - 218, 21, 86, 246, 25, 78, 167, 128, 1, 85, 16, 82, 82, 174, 234, 9, 73, - 234, 25, 68, 162, 8, 71, 2, 87, 198, 21, 83, 147, 1, 72, 4, 130, 151, 9, - 82, 135, 140, 1, 69, 2, 187, 128, 10, 82, 8, 38, 65, 206, 233, 9, 82, - 139, 56, 71, 4, 166, 162, 10, 82, 3, 84, 4, 200, 167, 8, 2, 65, 45, 203, - 169, 1, 85, 6, 254, 209, 9, 65, 142, 57, 67, 215, 22, 70, 2, 243, 153, 8, - 79, 4, 154, 149, 5, 69, 155, 244, 3, 73, 12, 70, 79, 130, 151, 9, 73, - 138, 109, 85, 150, 25, 65, 154, 3, 78, 3, 82, 2, 223, 137, 10, 90, 2, - 175, 13, 69, 2, 215, 254, 8, 79, 4, 194, 255, 8, 85, 187, 160, 1, 79, 6, - 26, 72, 147, 131, 10, 79, 4, 242, 250, 5, 73, 131, 147, 4, 69, 4, 226, - 149, 9, 79, 183, 137, 1, 69, 4, 242, 158, 10, 65, 3, 87, 10, 78, 69, 134, - 229, 3, 70, 252, 120, 6, 78, 84, 79, 32, 83, 72, 139, 192, 5, 80, 2, 231, - 215, 9, 76, 46, 252, 1, 2, 79, 84, 32, 6, 80, 80, 73, 78, 71, 32, 72, 2, - 82, 84, 176, 8, 7, 85, 76, 68, 69, 82, 69, 68, 220, 239, 1, 24, 67, 75, - 69, 68, 32, 70, 65, 67, 69, 32, 87, 73, 84, 72, 32, 69, 88, 80, 76, 79, - 68, 73, 78, 71, 158, 150, 3, 86, 251, 206, 4, 87, 2, 221, 133, 8, 3, 73, - 78, 71, 4, 36, 3, 84, 82, 79, 155, 233, 5, 66, 2, 11, 76, 2, 163, 129, 8, - 76, 32, 102, 32, 248, 5, 12, 72, 65, 78, 68, 32, 70, 79, 82, 77, 65, 84, - 32, 254, 171, 6, 67, 179, 232, 3, 83, 20, 154, 2, 66, 92, 11, 83, 76, 65, - 78, 84, 69, 68, 32, 78, 79, 82, 196, 1, 22, 82, 73, 71, 72, 84, 87, 65, - 82, 68, 83, 32, 65, 82, 82, 79, 87, 32, 65, 66, 79, 86, 69, 28, 7, 85, - 80, 32, 84, 65, 67, 75, 128, 1, 4, 76, 69, 70, 84, 149, 159, 2, 9, 68, - 79, 87, 78, 32, 84, 65, 67, 75, 4, 88, 14, 65, 67, 75, 83, 76, 65, 78, - 84, 69, 68, 32, 83, 79, 85, 33, 4, 69, 78, 84, 32, 2, 11, 84, 2, 203, - 252, 8, 72, 2, 245, 36, 37, 65, 82, 82, 79, 87, 32, 80, 79, 73, 78, 84, - 73, 78, 71, 32, 68, 79, 87, 78, 87, 65, 82, 68, 83, 32, 84, 72, 69, 78, - 32, 78, 79, 82, 84, 72, 32, 69, 2, 229, 249, 5, 2, 32, 76, 7, 11, 32, 4, - 76, 13, 65, 66, 79, 86, 69, 32, 83, 72, 79, 82, 84, 32, 68, 159, 131, 2, - 87, 2, 11, 79, 2, 11, 87, 2, 11, 78, 2, 11, 32, 2, 191, 249, 6, 84, 8, - 120, 10, 67, 79, 78, 84, 73, 78, 85, 73, 78, 71, 0, 6, 76, 69, 84, 84, - 69, 82, 40, 4, 68, 79, 87, 78, 1, 2, 85, 80, 2, 205, 178, 3, 5, 32, 79, - 86, 69, 82, 2, 21, 3, 32, 83, 84, 2, 227, 146, 10, 69, 2, 25, 4, 32, 79, - 80, 69, 2, 247, 133, 9, 78, 4, 26, 73, 191, 145, 10, 85, 2, 131, 146, 10, - 77, 159, 14, 174, 1, 68, 168, 19, 2, 71, 78, 204, 181, 1, 6, 77, 73, 76, - 65, 82, 32, 158, 2, 78, 202, 24, 88, 241, 226, 6, 15, 76, 72, 79, 85, 69, - 84, 84, 69, 32, 79, 70, 32, 74, 65, 80, 200, 1, 64, 5, 68, 72, 65, 77, - 32, 133, 17, 6, 69, 87, 65, 89, 83, 32, 184, 1, 202, 1, 69, 68, 7, 76, - 69, 84, 84, 69, 82, 32, 176, 4, 15, 82, 69, 80, 69, 84, 73, 84, 73, 79, - 78, 32, 77, 65, 82, 75, 50, 83, 164, 8, 11, 86, 79, 87, 69, 76, 32, 83, - 73, 71, 78, 32, 243, 163, 6, 68, 2, 37, 7, 78, 68, 32, 79, 70, 32, 84, 2, - 189, 195, 9, 2, 69, 88, 102, 214, 1, 65, 98, 84, 186, 177, 6, 68, 158, 1, - 86, 186, 5, 85, 206, 201, 1, 73, 162, 193, 1, 78, 46, 83, 82, 66, 2, 67, - 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, 2, 82, 2, 89, 186, - 2, 69, 3, 79, 11, 72, 8, 76, 84, 69, 82, 78, 65, 84, 69, 202, 139, 10, - 65, 2, 73, 3, 85, 2, 223, 230, 9, 32, 14, 134, 1, 72, 204, 225, 5, 19, - 87, 79, 45, 67, 73, 82, 67, 76, 69, 32, 65, 76, 84, 69, 82, 78, 65, 84, - 69, 162, 225, 3, 84, 195, 71, 65, 4, 152, 202, 9, 20, 82, 69, 69, 45, 67, - 73, 82, 67, 76, 69, 32, 65, 76, 84, 69, 82, 78, 65, 84, 69, 147, 64, 65, - 6, 11, 45, 6, 174, 137, 10, 49, 2, 50, 3, 51, 44, 38, 69, 181, 7, 4, 73, - 71, 78, 32, 32, 96, 11, 67, 84, 73, 79, 78, 32, 77, 65, 82, 75, 32, 177, - 6, 8, 80, 65, 82, 65, 84, 79, 82, 32, 28, 80, 11, 68, 79, 85, 66, 76, 69, - 32, 82, 73, 78, 71, 57, 5, 87, 73, 84, 72, 32, 5, 11, 32, 2, 129, 219, 8, - 6, 87, 73, 84, 72, 32, 82, 24, 212, 1, 12, 67, 73, 82, 67, 76, 69, 83, - 32, 65, 78, 68, 32, 116, 5, 81, 85, 65, 68, 82, 0, 4, 83, 69, 80, 84, 12, - 16, 82, 65, 89, 83, 32, 65, 78, 68, 32, 68, 79, 84, 84, 69, 68, 32, 46, - 68, 45, 3, 84, 82, 73, 6, 60, 4, 70, 79, 85, 82, 0, 3, 84, 87, 79, 195, - 216, 8, 82, 2, 169, 196, 6, 8, 32, 69, 78, 67, 76, 79, 83, 85, 2, 83, 85, - 6, 42, 68, 28, 3, 84, 82, 73, 215, 1, 67, 2, 197, 1, 3, 79, 85, 66, 2, - 171, 1, 80, 6, 52, 9, 68, 69, 78, 84, 32, 65, 78, 68, 32, 103, 80, 4, - 116, 6, 68, 79, 84, 84, 69, 68, 49, 14, 85, 45, 83, 72, 65, 80, 69, 68, - 32, 79, 82, 78, 65, 77, 2, 17, 2, 76, 69, 2, 17, 2, 32, 67, 2, 25, 4, 82, - 69, 83, 67, 2, 251, 185, 1, 69, 4, 150, 224, 8, 66, 131, 81, 68, 12, 210, - 222, 4, 83, 158, 142, 1, 67, 98, 78, 234, 206, 3, 65, 239, 1, 86, 26, 74, - 65, 94, 86, 154, 172, 6, 85, 206, 201, 1, 73, 222, 137, 2, 69, 3, 79, 10, - 240, 172, 6, 10, 76, 84, 69, 82, 78, 65, 84, 69, 32, 85, 170, 211, 3, 65, - 2, 73, 3, 85, 4, 11, 79, 4, 33, 6, 67, 65, 76, 73, 67, 32, 4, 199, 172, - 6, 82, 16, 56, 5, 66, 76, 65, 67, 75, 1, 5, 87, 72, 73, 84, 69, 8, 11, - 32, 8, 70, 82, 24, 3, 76, 69, 70, 12, 4, 68, 79, 87, 78, 1, 2, 85, 80, 2, - 21, 3, 73, 71, 72, 2, 11, 84, 2, 225, 92, 6, 32, 80, 79, 73, 78, 84, 194, - 10, 96, 8, 87, 82, 73, 84, 73, 78, 71, 32, 133, 239, 1, 10, 32, 79, 70, - 32, 84, 72, 69, 32, 72, 79, 192, 10, 136, 3, 4, 65, 73, 82, 32, 192, 1, - 2, 66, 82, 102, 67, 138, 1, 68, 218, 4, 69, 242, 6, 70, 244, 3, 4, 87, - 65, 76, 76, 138, 1, 72, 234, 77, 76, 212, 6, 2, 77, 79, 222, 42, 78, 218, - 1, 82, 202, 7, 83, 162, 5, 84, 180, 10, 5, 71, 82, 65, 83, 80, 184, 5, - 30, 85, 80, 80, 69, 82, 32, 66, 79, 68, 89, 32, 84, 73, 76, 84, 73, 78, - 71, 32, 70, 82, 79, 77, 32, 72, 73, 80, 32, 74, 79, 195, 197, 4, 80, 8, - 48, 4, 66, 76, 79, 87, 29, 4, 83, 85, 67, 75, 4, 58, 32, 155, 221, 4, 73, - 4, 30, 32, 61, 3, 73, 78, 71, 2, 129, 158, 1, 10, 83, 77, 65, 76, 76, 32, - 82, 79, 84, 65, 2, 167, 248, 8, 32, 10, 52, 5, 69, 65, 84, 72, 32, 249, - 169, 1, 2, 85, 83, 4, 144, 164, 2, 2, 69, 88, 1, 2, 73, 78, 10, 40, 6, - 72, 69, 69, 75, 83, 32, 63, 79, 6, 154, 107, 83, 146, 38, 78, 225, 217, - 3, 4, 80, 85, 70, 70, 4, 174, 166, 9, 76, 223, 46, 77, 28, 108, 15, 82, - 69, 65, 77, 89, 32, 69, 89, 69, 66, 82, 79, 87, 83, 32, 165, 1, 7, 89, - 78, 65, 77, 73, 67, 32, 8, 64, 4, 68, 79, 87, 78, 0, 2, 85, 80, 29, 4, - 78, 69, 85, 84, 2, 153, 143, 1, 2, 32, 78, 4, 21, 3, 82, 65, 76, 4, 11, - 32, 4, 194, 58, 68, 187, 185, 9, 85, 20, 180, 1, 11, 69, 86, 69, 82, 89, - 32, 79, 84, 72, 69, 82, 30, 70, 22, 83, 128, 122, 9, 65, 82, 82, 79, 87, - 72, 69, 65, 68, 254, 38, 82, 188, 200, 3, 2, 84, 69, 21, 4, 71, 82, 65, - 68, 2, 181, 218, 8, 2, 32, 84, 2, 151, 136, 6, 65, 6, 68, 11, 73, 77, 85, - 76, 84, 65, 78, 69, 79, 85, 83, 151, 215, 8, 76, 5, 223, 151, 1, 32, 54, - 64, 2, 89, 69, 244, 221, 4, 4, 88, 67, 73, 84, 155, 132, 4, 65, 50, 166, - 1, 32, 56, 15, 66, 82, 79, 87, 83, 32, 83, 84, 82, 65, 73, 71, 72, 84, - 32, 44, 5, 71, 65, 90, 69, 45, 140, 2, 7, 76, 65, 83, 72, 69, 83, 32, 65, - 2, 83, 32, 6, 240, 150, 1, 5, 66, 76, 73, 78, 75, 175, 247, 4, 87, 6, - 186, 53, 68, 154, 84, 78, 163, 229, 8, 85, 18, 100, 11, 70, 76, 79, 79, - 82, 80, 76, 65, 78, 69, 32, 25, 10, 87, 65, 76, 76, 80, 76, 65, 78, 69, - 32, 8, 82, 83, 207, 45, 67, 10, 18, 67, 43, 83, 4, 254, 45, 85, 213, 95, - 3, 73, 82, 67, 6, 37, 7, 84, 82, 65, 73, 71, 72, 84, 7, 11, 32, 4, 222, - 163, 1, 65, 55, 68, 6, 130, 51, 68, 252, 167, 4, 4, 70, 76, 85, 84, 191, - 145, 5, 85, 14, 112, 5, 72, 65, 76, 70, 32, 26, 67, 28, 4, 87, 73, 68, - 69, 230, 92, 79, 177, 130, 4, 6, 83, 81, 85, 69, 69, 90, 4, 22, 67, 131, - 93, 79, 2, 225, 232, 2, 2, 76, 79, 4, 162, 67, 32, 217, 61, 4, 78, 73, - 78, 71, 38, 204, 1, 28, 65, 67, 69, 32, 68, 73, 82, 69, 67, 84, 73, 79, - 78, 32, 80, 79, 83, 73, 84, 73, 79, 78, 32, 78, 79, 83, 69, 32, 138, 1, - 73, 82, 76, 176, 1, 8, 79, 82, 69, 72, 69, 65, 68, 32, 135, 148, 7, 85, - 6, 88, 10, 85, 80, 32, 79, 82, 32, 68, 79, 87, 78, 13, 8, 70, 79, 82, 87, - 65, 82, 68, 32, 5, 11, 32, 2, 225, 225, 4, 3, 84, 73, 76, 12, 240, 225, - 3, 11, 76, 76, 32, 77, 79, 68, 73, 70, 73, 69, 82, 151, 184, 2, 78, 12, - 44, 4, 73, 67, 75, 32, 29, 3, 79, 79, 82, 10, 146, 141, 1, 76, 35, 83, 2, - 221, 233, 8, 20, 80, 76, 65, 78, 69, 32, 83, 72, 79, 85, 76, 68, 69, 82, - 32, 72, 73, 80, 32, 77, 6, 166, 89, 87, 222, 38, 67, 47, 78, 156, 4, 34, - 65, 133, 75, 3, 69, 65, 68, 140, 4, 36, 3, 78, 68, 45, 139, 172, 9, 73, - 138, 4, 92, 5, 65, 78, 71, 76, 69, 138, 5, 67, 150, 10, 70, 186, 42, 72, - 241, 12, 4, 79, 86, 65, 76, 37, 11, 32, 34, 188, 1, 5, 73, 78, 68, 69, - 88, 176, 1, 7, 76, 73, 84, 84, 76, 69, 32, 136, 1, 5, 82, 73, 78, 71, 32, - 173, 66, 18, 77, 73, 68, 68, 76, 69, 32, 82, 73, 78, 71, 32, 76, 73, 84, - 84, 76, 69, 17, 11, 32, 14, 128, 1, 7, 77, 73, 68, 68, 76, 69, 32, 228, - 24, 11, 82, 73, 78, 71, 32, 76, 73, 84, 84, 76, 69, 241, 42, 5, 84, 72, - 85, 77, 66, 4, 174, 70, 76, 243, 201, 8, 82, 8, 44, 5, 73, 78, 68, 69, - 88, 203, 224, 9, 85, 7, 145, 24, 18, 32, 84, 72, 85, 77, 66, 32, 73, 78, - 68, 69, 88, 32, 84, 72, 85, 77, 66, 4, 108, 22, 68, 79, 87, 78, 32, 77, - 73, 68, 68, 76, 69, 32, 84, 72, 85, 77, 66, 32, 73, 78, 68, 69, 155, 68, - 76, 2, 223, 157, 8, 88, 88, 64, 5, 73, 82, 67, 76, 69, 184, 3, 3, 76, 65, - 87, 183, 2, 85, 35, 11, 32, 32, 116, 5, 73, 78, 68, 69, 88, 200, 1, 7, - 76, 73, 84, 84, 76, 69, 32, 36, 7, 77, 73, 68, 68, 76, 69, 32, 163, 64, - 82, 21, 11, 32, 18, 72, 6, 77, 73, 68, 68, 76, 69, 198, 26, 72, 242, 38, - 82, 255, 215, 8, 66, 13, 11, 32, 10, 64, 5, 67, 82, 79, 83, 83, 198, 64, - 84, 82, 76, 243, 201, 8, 82, 4, 134, 65, 32, 227, 212, 8, 69, 4, 210, - 181, 8, 73, 139, 166, 1, 85, 6, 252, 47, 10, 82, 73, 78, 71, 32, 76, 73, - 84, 84, 76, 187, 171, 9, 85, 17, 11, 32, 14, 144, 2, 28, 77, 73, 68, 68, - 76, 69, 32, 82, 73, 78, 71, 32, 76, 73, 84, 84, 76, 69, 32, 67, 79, 78, - 74, 79, 73, 78, 69, 68, 176, 49, 2, 70, 79, 198, 11, 78, 162, 1, 84, 217, - 118, 23, 73, 78, 68, 69, 88, 32, 84, 72, 85, 77, 66, 32, 67, 85, 82, 86, - 69, 32, 84, 72, 85, 77, 66, 5, 139, 181, 1, 32, 38, 46, 80, 149, 2, 6, - 82, 76, 73, 67, 85, 69, 31, 11, 32, 28, 188, 1, 5, 73, 78, 68, 69, 88, - 56, 19, 70, 73, 86, 69, 32, 70, 73, 78, 71, 69, 82, 83, 32, 83, 80, 82, - 69, 65, 68, 196, 50, 7, 77, 73, 68, 68, 76, 69, 32, 18, 79, 218, 7, 78, - 163, 1, 84, 9, 11, 32, 6, 40, 5, 84, 72, 85, 77, 66, 243, 58, 82, 5, 215, - 46, 32, 9, 11, 32, 6, 72, 5, 73, 78, 68, 69, 88, 0, 6, 77, 73, 68, 68, - 76, 69, 179, 71, 79, 2, 189, 133, 9, 13, 32, 82, 73, 78, 71, 32, 76, 73, - 84, 84, 76, 69, 32, 172, 2, 44, 3, 73, 83, 84, 177, 33, 3, 76, 65, 84, - 247, 1, 11, 32, 244, 1, 160, 2, 5, 73, 78, 68, 69, 88, 192, 16, 7, 76, - 73, 84, 84, 76, 69, 32, 196, 2, 7, 77, 73, 68, 68, 76, 69, 32, 200, 4, 5, - 82, 73, 78, 71, 32, 132, 2, 5, 84, 72, 85, 77, 66, 138, 2, 72, 205, 9, - 22, 70, 79, 85, 82, 32, 70, 73, 78, 71, 69, 82, 83, 32, 67, 79, 78, 74, - 79, 73, 78, 69, 68, 137, 1, 11, 32, 134, 1, 232, 1, 4, 66, 69, 78, 84, - 36, 6, 72, 73, 78, 71, 69, 68, 76, 6, 77, 73, 68, 68, 76, 69, 168, 7, 2, - 67, 85, 64, 6, 84, 72, 85, 77, 66, 32, 160, 5, 16, 85, 80, 32, 77, 73, - 68, 68, 76, 69, 32, 72, 73, 78, 71, 69, 68, 151, 4, 82, 5, 217, 45, 5, - 32, 79, 86, 69, 82, 9, 11, 32, 6, 252, 20, 8, 77, 73, 68, 68, 76, 69, 32, - 85, 167, 160, 8, 76, 71, 11, 32, 68, 236, 1, 4, 66, 69, 78, 84, 42, 67, - 244, 1, 10, 85, 80, 32, 83, 80, 82, 69, 65, 68, 32, 100, 6, 72, 73, 78, - 71, 69, 68, 46, 82, 80, 5, 84, 72, 85, 77, 66, 188, 28, 14, 83, 84, 82, - 65, 73, 71, 72, 84, 32, 84, 72, 85, 77, 66, 227, 17, 76, 5, 213, 92, 6, - 32, 84, 72, 85, 77, 66, 28, 68, 8, 79, 78, 74, 79, 73, 78, 69, 68, 233, - 1, 4, 82, 79, 83, 83, 23, 11, 32, 20, 144, 1, 6, 67, 85, 80, 80, 69, 68, - 28, 6, 84, 72, 85, 77, 66, 32, 68, 5, 72, 73, 78, 71, 69, 166, 22, 73, - 165, 7, 6, 77, 73, 68, 68, 76, 69, 5, 11, 32, 2, 203, 27, 84, 8, 160, 1, - 4, 83, 73, 68, 69, 219, 61, 70, 6, 22, 69, 159, 47, 32, 4, 223, 15, 68, - 5, 241, 30, 7, 32, 83, 80, 82, 69, 65, 68, 8, 32, 3, 73, 78, 71, 247, 19, - 65, 7, 11, 32, 4, 138, 36, 67, 255, 225, 8, 66, 19, 11, 32, 16, 64, 6, - 65, 78, 71, 76, 69, 68, 22, 67, 106, 72, 159, 132, 9, 66, 5, 247, 211, 5, - 32, 6, 74, 85, 230, 7, 73, 241, 25, 10, 79, 78, 74, 79, 73, 78, 69, 68, - 32, 72, 2, 145, 188, 4, 2, 80, 80, 4, 194, 33, 73, 217, 26, 2, 79, 79, - 40, 164, 1, 7, 65, 78, 71, 76, 69, 68, 32, 46, 67, 220, 1, 14, 70, 79, - 82, 87, 65, 82, 68, 32, 73, 78, 68, 69, 88, 32, 32, 4, 72, 79, 79, 75, - 53, 4, 83, 73, 68, 69, 4, 128, 1, 2, 73, 78, 1, 3, 79, 85, 84, 12, 36, 5, - 73, 82, 67, 76, 69, 15, 85, 5, 47, 68, 8, 32, 4, 80, 80, 69, 68, 39, 82, - 2, 225, 40, 5, 32, 77, 73, 68, 68, 6, 56, 9, 86, 69, 32, 84, 72, 85, 77, - 66, 32, 143, 37, 76, 4, 182, 160, 1, 73, 131, 188, 4, 85, 4, 202, 84, 83, - 255, 171, 8, 66, 7, 157, 8, 9, 69, 68, 32, 77, 73, 68, 68, 76, 69, 15, - 11, 32, 12, 92, 6, 73, 78, 68, 69, 88, 32, 156, 13, 5, 84, 72, 85, 77, - 66, 193, 8, 4, 66, 79, 84, 72, 4, 26, 72, 239, 254, 8, 66, 2, 231, 140, - 8, 73, 7, 37, 7, 32, 84, 72, 85, 77, 66, 32, 4, 178, 28, 67, 227, 129, 1, - 83, 22, 88, 4, 68, 79, 87, 78, 186, 1, 84, 158, 6, 82, 130, 21, 73, 226, - 224, 8, 66, 159, 67, 85, 9, 11, 32, 6, 80, 9, 79, 84, 72, 69, 82, 83, 32, - 67, 73, 25, 7, 82, 73, 80, 80, 76, 69, 32, 2, 225, 51, 2, 82, 67, 4, 22, - 67, 171, 80, 83, 2, 11, 85, 2, 241, 179, 4, 2, 82, 86, 4, 196, 35, 6, 79, - 85, 67, 72, 69, 83, 39, 72, 30, 134, 1, 82, 28, 6, 84, 72, 85, 77, 66, - 32, 138, 3, 85, 134, 1, 68, 210, 30, 76, 205, 244, 7, 9, 66, 69, 78, 84, - 32, 79, 86, 69, 82, 4, 238, 4, 65, 231, 29, 73, 16, 80, 7, 65, 78, 71, - 76, 69, 68, 32, 126, 67, 124, 4, 72, 79, 79, 75, 147, 32, 76, 6, 60, 10, - 79, 85, 84, 32, 73, 78, 68, 69, 88, 32, 215, 1, 73, 4, 26, 67, 151, 188, - 9, 85, 2, 133, 186, 2, 3, 82, 79, 83, 6, 76, 12, 73, 82, 67, 76, 69, 68, - 32, 73, 78, 68, 69, 88, 45, 3, 85, 80, 80, 4, 11, 32, 4, 150, 21, 72, - 131, 166, 9, 85, 2, 25, 4, 69, 68, 32, 73, 2, 233, 30, 4, 78, 68, 69, 88, - 4, 11, 80, 5, 175, 15, 32, 18, 102, 68, 20, 6, 77, 73, 68, 68, 76, 69, - 42, 82, 198, 29, 84, 82, 76, 226, 244, 7, 73, 139, 166, 1, 85, 2, 175, - 229, 7, 79, 7, 11, 32, 4, 206, 3, 82, 179, 16, 67, 2, 11, 65, 2, 37, 7, - 73, 83, 69, 68, 32, 75, 78, 2, 11, 85, 2, 11, 67, 2, 147, 148, 8, 75, 35, - 11, 32, 32, 148, 1, 8, 66, 69, 84, 87, 69, 69, 78, 32, 102, 72, 20, 5, - 79, 86, 69, 82, 32, 120, 4, 83, 73, 68, 69, 100, 6, 85, 78, 68, 69, 82, - 32, 207, 40, 70, 8, 80, 12, 73, 78, 68, 69, 88, 32, 77, 73, 68, 68, 76, - 69, 246, 20, 77, 155, 6, 82, 5, 135, 70, 32, 2, 187, 169, 4, 69, 4, 52, - 6, 70, 79, 85, 82, 32, 82, 161, 2, 2, 84, 87, 2, 11, 65, 2, 233, 80, 9, - 73, 83, 69, 68, 32, 75, 78, 85, 67, 6, 11, 32, 6, 38, 68, 190, 15, 67, - 255, 225, 8, 66, 2, 25, 4, 73, 65, 71, 79, 2, 131, 171, 8, 78, 10, 54, - 73, 34, 84, 48, 4, 70, 79, 85, 82, 131, 23, 76, 2, 161, 7, 4, 78, 68, 69, - 88, 4, 34, 87, 13, 4, 72, 82, 69, 69, 2, 11, 79, 2, 197, 164, 8, 5, 32, - 70, 73, 78, 71, 55, 11, 32, 52, 194, 1, 70, 172, 3, 4, 72, 69, 69, 76, - 192, 1, 6, 83, 80, 76, 73, 84, 32, 236, 1, 6, 84, 72, 85, 77, 66, 32, - 165, 244, 4, 16, 66, 69, 84, 87, 69, 69, 78, 32, 80, 65, 76, 77, 32, 70, - 65, 67, 24, 140, 1, 18, 73, 86, 69, 32, 70, 73, 78, 71, 69, 82, 83, 32, - 83, 80, 82, 69, 65, 68, 165, 1, 11, 79, 85, 82, 32, 70, 73, 78, 71, 69, - 82, 83, 15, 11, 32, 12, 68, 6, 72, 73, 78, 71, 69, 68, 42, 84, 186, 2, - 70, 199, 233, 8, 66, 7, 11, 32, 4, 190, 4, 84, 155, 15, 78, 2, 189, 35, - 6, 72, 85, 77, 66, 32, 70, 11, 11, 32, 8, 72, 9, 67, 79, 78, 74, 79, 73, - 78, 69, 68, 154, 8, 72, 231, 226, 8, 66, 5, 221, 145, 9, 3, 32, 83, 80, - 11, 11, 32, 8, 96, 19, 70, 73, 86, 69, 32, 70, 73, 78, 71, 69, 82, 83, - 32, 83, 80, 82, 69, 65, 68, 151, 2, 84, 7, 11, 32, 4, 26, 70, 199, 233, - 8, 66, 2, 21, 3, 79, 85, 82, 2, 167, 1, 32, 10, 72, 6, 67, 69, 78, 84, - 82, 69, 96, 5, 73, 78, 68, 69, 88, 167, 16, 76, 7, 49, 10, 32, 84, 72, - 85, 77, 66, 32, 83, 73, 68, 4, 11, 69, 5, 11, 32, 2, 131, 232, 8, 66, 2, - 11, 32, 2, 11, 84, 2, 173, 135, 1, 5, 72, 85, 77, 66, 32, 6, 242, 30, 70, - 162, 104, 83, 159, 224, 7, 66, 82, 48, 4, 73, 78, 71, 69, 181, 9, 3, 79, - 79, 75, 63, 11, 32, 60, 206, 1, 70, 172, 1, 5, 73, 78, 68, 69, 88, 196, - 2, 6, 76, 73, 84, 84, 76, 69, 80, 6, 77, 73, 68, 68, 76, 69, 30, 79, 64, - 4, 82, 73, 78, 71, 124, 6, 84, 72, 85, 77, 66, 32, 158, 6, 78, 139, 142, - 4, 83, 4, 92, 19, 73, 86, 69, 32, 70, 73, 78, 71, 69, 82, 83, 32, 83, 80, - 82, 69, 65, 68, 32, 19, 79, 2, 191, 25, 79, 2, 249, 1, 11, 85, 82, 32, - 70, 73, 78, 71, 69, 82, 83, 32, 23, 11, 32, 20, 86, 72, 40, 7, 77, 73, - 68, 68, 76, 69, 32, 104, 5, 84, 72, 85, 77, 66, 219, 9, 82, 2, 11, 73, 2, - 177, 154, 4, 2, 78, 71, 6, 36, 4, 82, 73, 78, 71, 203, 10, 76, 5, 11, 32, - 2, 11, 67, 2, 21, 3, 79, 78, 74, 2, 175, 33, 79, 11, 11, 32, 8, 34, 83, - 210, 22, 79, 203, 39, 76, 4, 190, 148, 7, 73, 195, 8, 77, 9, 11, 32, 6, - 22, 73, 199, 8, 84, 4, 25, 4, 78, 68, 69, 88, 5, 155, 8, 32, 5, 11, 32, - 2, 171, 8, 82, 8, 21, 3, 80, 69, 78, 9, 11, 32, 6, 178, 7, 78, 163, 1, - 84, 5, 97, 22, 32, 68, 79, 87, 78, 32, 73, 78, 68, 69, 88, 32, 84, 72, - 85, 77, 66, 32, 72, 79, 79, 75, 2, 177, 77, 2, 32, 77, 6, 68, 9, 66, 69, - 84, 87, 69, 69, 78, 32, 77, 53, 4, 83, 73, 68, 69, 2, 29, 5, 73, 68, 68, - 76, 69, 2, 163, 148, 4, 32, 5, 33, 6, 32, 84, 79, 85, 67, 72, 2, 185, - 250, 7, 3, 73, 78, 71, 21, 11, 32, 18, 172, 1, 4, 67, 85, 82, 76, 28, 18, - 73, 78, 68, 69, 88, 32, 82, 73, 78, 71, 32, 76, 73, 84, 84, 76, 69, 32, - 48, 7, 77, 73, 68, 68, 76, 69, 32, 225, 2, 4, 82, 73, 78, 71, 2, 253, - 147, 5, 2, 73, 67, 6, 142, 183, 5, 85, 142, 238, 2, 79, 243, 41, 73, 8, - 104, 21, 82, 73, 78, 71, 32, 76, 73, 84, 84, 76, 69, 32, 67, 79, 78, 74, - 79, 73, 78, 69, 68, 143, 2, 84, 7, 223, 228, 2, 32, 17, 11, 32, 14, 112, - 14, 70, 73, 86, 69, 32, 70, 73, 78, 71, 69, 82, 83, 32, 83, 22, 76, 66, - 78, 70, 82, 94, 84, 183, 244, 7, 73, 2, 215, 160, 2, 80, 2, 21, 3, 73, - 84, 84, 2, 17, 2, 76, 69, 2, 135, 166, 8, 32, 2, 11, 79, 2, 11, 32, 2, - 11, 84, 2, 11, 72, 2, 247, 251, 5, 85, 2, 11, 73, 2, 21, 3, 78, 71, 32, - 2, 11, 76, 2, 11, 73, 2, 11, 84, 2, 179, 246, 7, 84, 4, 29, 5, 72, 85, - 77, 66, 32, 4, 194, 14, 70, 163, 104, 83, 17, 11, 32, 14, 56, 8, 77, 79, - 86, 69, 77, 69, 78, 84, 247, 150, 8, 82, 12, 26, 45, 163, 199, 7, 32, 10, + 79, 76, 73, 68, 85, 83, 232, 229, 7, 16, 84, 73, 76, 68, 69, 32, 79, 80, + 69, 82, 65, 84, 79, 82, 32, 65, 195, 253, 2, 73, 2, 197, 243, 10, 2, 82, + 32, 4, 152, 212, 3, 18, 71, 72, 84, 32, 70, 79, 85, 82, 32, 80, 79, 73, + 78, 84, 69, 68, 32, 80, 135, 237, 1, 78, 9, 11, 32, 6, 240, 166, 5, 9, + 80, 82, 69, 67, 69, 68, 73, 78, 71, 166, 161, 3, 79, 251, 154, 1, 87, 48, + 248, 2, 5, 65, 78, 71, 76, 69, 20, 7, 68, 79, 85, 66, 76, 69, 32, 118, + 78, 20, 5, 70, 79, 82, 75, 69, 70, 80, 82, 82, 214, 1, 83, 106, 84, 236, + 238, 6, 30, 72, 65, 78, 68, 32, 87, 73, 84, 72, 32, 77, 73, 68, 68, 76, + 69, 32, 70, 73, 78, 71, 69, 82, 32, 69, 88, 84, 69, 78, 68, 198, 190, 2, + 67, 114, 81, 180, 102, 6, 69, 77, 80, 84, 89, 32, 253, 93, 7, 86, 73, 67, + 84, 79, 82, 89, 5, 247, 231, 3, 32, 6, 40, 3, 80, 82, 73, 41, 3, 83, 84, + 82, 4, 17, 2, 77, 69, 5, 195, 184, 3, 32, 2, 29, 5, 79, 75, 69, 32, 78, + 2, 155, 187, 6, 79, 2, 49, 10, 68, 32, 80, 65, 82, 65, 71, 82, 65, 80, 2, + 179, 4, 72, 4, 36, 3, 73, 76, 67, 239, 235, 10, 82, 2, 11, 82, 2, 217, + 254, 10, 2, 79, 87, 6, 156, 1, 17, 65, 73, 83, 69, 68, 32, 72, 65, 78, + 68, 32, 87, 73, 84, 72, 32, 70, 148, 107, 8, 79, 84, 65, 84, 69, 68, 32, + 70, 253, 176, 6, 4, 73, 71, 72, 84, 2, 209, 112, 9, 73, 78, 71, 69, 82, + 83, 32, 83, 80, 4, 156, 134, 1, 17, 65, 78, 83, 45, 83, 69, 82, 73, 70, + 32, 67, 65, 80, 73, 84, 65, 76, 191, 174, 7, 69, 10, 88, 4, 73, 76, 68, + 69, 28, 7, 82, 73, 80, 76, 69, 32, 80, 225, 160, 7, 3, 72, 85, 77, 5, + 237, 235, 4, 2, 32, 69, 2, 143, 232, 10, 82, 2, 11, 82, 2, 143, 196, 11, + 79, 147, 4, 228, 1, 4, 66, 66, 79, 78, 152, 1, 3, 67, 69, 32, 60, 3, 71, + 72, 84, 188, 81, 2, 78, 71, 252, 1, 23, 83, 73, 78, 71, 32, 68, 73, 65, + 71, 79, 78, 65, 76, 32, 67, 82, 79, 83, 83, 73, 78, 71, 32, 198, 222, 5, + 65, 227, 165, 4, 70, 19, 37, 7, 32, 65, 82, 82, 79, 87, 32, 16, 88, 3, + 76, 69, 70, 0, 4, 82, 73, 71, 72, 178, 197, 4, 85, 161, 142, 7, 3, 68, + 79, 87, 4, 207, 252, 1, 84, 4, 26, 67, 135, 244, 9, 66, 2, 157, 145, 1, + 3, 82, 65, 67, 224, 3, 110, 32, 190, 36, 45, 152, 12, 11, 72, 65, 78, 68, + 32, 73, 78, 84, 69, 82, 73, 29, 6, 87, 65, 82, 68, 83, 32, 202, 1, 166, + 2, 65, 132, 6, 2, 66, 76, 62, 67, 184, 1, 2, 68, 79, 242, 1, 70, 60, 2, + 72, 65, 176, 5, 13, 74, 85, 83, 84, 73, 70, 73, 69, 68, 32, 76, 69, 70, + 20, 2, 76, 79, 66, 78, 82, 79, 122, 80, 148, 1, 2, 82, 65, 58, 83, 154, + 6, 84, 160, 5, 9, 86, 69, 82, 84, 73, 67, 65, 76, 32, 147, 1, 87, 28, 22, + 78, 163, 4, 82, 22, 24, 2, 68, 32, 39, 71, 4, 170, 50, 76, 21, 3, 85, 80, + 80, 18, 26, 69, 17, 2, 76, 69, 2, 203, 26, 82, 17, 11, 32, 14, 154, 1, + 66, 44, 8, 68, 79, 84, 84, 69, 68, 32, 83, 2, 83, 112, 5, 87, 73, 84, 72, + 32, 189, 212, 10, 12, 86, 65, 82, 73, 65, 78, 84, 32, 87, 73, 84, 72, 4, + 173, 210, 10, 6, 82, 65, 67, 75, 69, 84, 2, 37, 7, 85, 66, 83, 84, 73, + 84, 85, 2, 25, 4, 84, 73, 79, 78, 2, 21, 3, 32, 77, 65, 2, 171, 138, 1, + 82, 4, 68, 11, 68, 79, 87, 78, 87, 65, 82, 68, 83, 32, 90, 139, 247, 8, + 65, 2, 141, 218, 10, 5, 73, 71, 90, 65, 71, 6, 18, 67, 79, 82, 2, 41, 8, + 32, 71, 82, 69, 65, 84, 69, 82, 2, 249, 24, 4, 45, 84, 72, 65, 4, 41, 8, + 79, 87, 32, 87, 73, 84, 72, 32, 4, 144, 234, 7, 7, 67, 73, 82, 67, 76, + 69, 68, 207, 182, 2, 83, 4, 25, 4, 65, 67, 75, 32, 4, 130, 27, 76, 223, + 218, 7, 84, 12, 38, 85, 166, 26, 79, 243, 235, 2, 69, 8, 53, 11, 82, 76, + 89, 32, 66, 82, 65, 67, 75, 69, 84, 9, 11, 32, 6, 44, 4, 77, 73, 68, 68, + 250, 10, 76, 27, 85, 2, 221, 186, 10, 2, 76, 69, 12, 38, 84, 41, 5, 85, + 66, 76, 69, 32, 2, 137, 17, 6, 84, 69, 68, 32, 83, 85, 10, 50, 65, 94, + 87, 214, 162, 3, 81, 199, 199, 4, 80, 4, 162, 31, 78, 173, 161, 3, 15, + 82, 82, 79, 87, 32, 87, 73, 84, 72, 32, 82, 79, 85, 78, 68, 2, 139, 24, + 73, 6, 26, 73, 155, 131, 3, 76, 4, 238, 215, 8, 86, 191, 97, 83, 28, 32, + 3, 76, 70, 32, 187, 4, 78, 26, 128, 1, 8, 65, 78, 68, 32, 76, 69, 70, 84, + 62, 66, 90, 70, 82, 72, 50, 82, 58, 84, 70, 87, 246, 13, 76, 22, 85, 175, + 227, 8, 77, 2, 29, 5, 32, 72, 65, 76, 70, 2, 201, 217, 8, 2, 32, 87, 6, + 11, 76, 6, 40, 4, 65, 67, 75, 32, 183, 188, 10, 79, 4, 158, 154, 10, 67, + 207, 11, 83, 4, 58, 79, 237, 156, 3, 8, 76, 89, 73, 78, 71, 32, 83, 65, + 2, 131, 202, 9, 76, 2, 209, 216, 8, 7, 79, 82, 73, 90, 79, 78, 84, 2, 33, + 6, 85, 78, 78, 73, 78, 71, 2, 203, 238, 6, 32, 2, 153, 173, 5, 12, 82, + 73, 80, 76, 69, 32, 68, 65, 83, 72, 32, 72, 2, 209, 167, 10, 4, 72, 73, + 84, 69, 2, 173, 152, 1, 16, 68, 32, 84, 69, 76, 69, 80, 72, 79, 78, 69, + 32, 82, 69, 67, 69, 4, 187, 231, 7, 84, 2, 145, 152, 11, 11, 87, 32, 80, + 65, 82, 65, 80, 72, 82, 65, 83, 2, 177, 4, 16, 79, 82, 77, 65, 76, 32, + 70, 65, 67, 84, 79, 82, 32, 83, 69, 77, 8, 74, 85, 166, 221, 8, 78, 225, + 189, 1, 8, 80, 69, 78, 32, 83, 81, 85, 65, 2, 221, 233, 10, 6, 84, 69, + 82, 32, 74, 79, 8, 49, 10, 65, 82, 69, 78, 84, 72, 69, 83, 73, 83, 9, 11, + 32, 6, 34, 76, 26, 85, 255, 196, 9, 69, 2, 157, 37, 2, 79, 87, 2, 133, + 37, 2, 80, 80, 2, 217, 10, 10, 73, 83, 69, 68, 32, 79, 77, 73, 83, 83, + 40, 62, 45, 70, 69, 78, 73, 68, 2, 80, 69, 126, 81, 227, 2, 85, 2, 173, + 128, 8, 12, 83, 72, 65, 80, 69, 68, 32, 66, 65, 71, 32, 68, 4, 26, 77, + 191, 236, 8, 86, 2, 217, 212, 10, 7, 73, 68, 73, 82, 69, 67, 84, 4, 210, + 150, 3, 78, 169, 252, 7, 8, 68, 69, 87, 65, 89, 83, 32, 85, 8, 32, 4, 65, + 75, 69, 82, 67, 69, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 246, 252, 3, 79, + 55, 84, 2, 137, 5, 2, 67, 72, 20, 57, 12, 85, 65, 82, 69, 32, 66, 82, 65, + 67, 75, 69, 84, 21, 11, 32, 18, 84, 3, 76, 79, 87, 0, 3, 85, 80, 80, 28, + 5, 87, 73, 84, 72, 32, 227, 191, 9, 69, 2, 245, 230, 3, 2, 69, 82, 12, + 96, 8, 84, 73, 67, 75, 32, 73, 78, 32, 230, 6, 81, 166, 236, 7, 85, 134, + 126, 68, 135, 193, 2, 83, 4, 244, 229, 3, 6, 66, 79, 84, 84, 79, 77, 1, + 3, 84, 79, 80, 2, 165, 4, 6, 66, 83, 84, 73, 84, 85, 26, 54, 72, 130, 3, + 82, 130, 223, 7, 79, 235, 204, 2, 65, 14, 62, 73, 116, 5, 79, 85, 71, 72, + 84, 45, 4, 82, 69, 69, 32, 4, 21, 3, 82, 68, 32, 4, 68, 4, 73, 78, 68, + 85, 221, 211, 1, 7, 87, 72, 73, 84, 69, 32, 82, 2, 215, 166, 11, 67, 2, + 11, 32, 2, 213, 136, 3, 3, 66, 85, 66, 8, 60, 9, 81, 85, 65, 82, 84, 69, + 82, 83, 32, 151, 230, 8, 69, 6, 34, 76, 22, 85, 139, 236, 8, 66, 2, 37, + 2, 79, 87, 2, 17, 2, 80, 80, 2, 227, 230, 8, 69, 8, 34, 65, 89, 4, 73, + 65, 78, 71, 2, 33, 6, 78, 83, 80, 79, 83, 73, 2, 11, 84, 2, 17, 2, 73, + 79, 2, 147, 138, 11, 78, 6, 32, 2, 76, 69, 159, 229, 8, 85, 5, 41, 8, 32, + 65, 66, 79, 86, 69, 32, 76, 2, 181, 178, 9, 2, 69, 70, 6, 18, 66, 95, 82, + 4, 68, 9, 65, 82, 32, 87, 73, 84, 72, 32, 81, 213, 191, 10, 2, 79, 88, 2, + 211, 223, 8, 85, 2, 189, 188, 9, 3, 85, 76, 69, 14, 22, 72, 203, 1, 73, + 12, 25, 4, 73, 84, 69, 32, 12, 54, 67, 54, 76, 206, 210, 7, 80, 238, 7, + 83, 39, 84, 4, 26, 79, 163, 207, 7, 85, 2, 65, 3, 82, 78, 69, 2, 41, 8, + 69, 78, 84, 73, 67, 85, 76, 65, 2, 183, 134, 11, 82, 2, 225, 199, 6, 6, + 71, 71, 76, 89, 32, 70, 62, 118, 70, 226, 2, 72, 128, 1, 9, 80, 79, 73, + 78, 84, 73, 78, 71, 32, 214, 4, 83, 197, 1, 6, 84, 79, 45, 76, 69, 70, + 18, 33, 6, 65, 67, 73, 78, 71, 32, 18, 116, 14, 65, 82, 77, 69, 78, 73, + 65, 78, 32, 69, 84, 69, 82, 78, 20, 4, 66, 65, 83, 83, 16, 3, 70, 73, 83, + 87, 83, 2, 255, 159, 8, 73, 2, 175, 43, 73, 6, 26, 72, 151, 214, 11, 84, + 5, 11, 32, 2, 233, 176, 8, 6, 87, 73, 84, 72, 32, 79, 8, 140, 208, 3, 10, + 86, 65, 83, 84, 73, 32, 83, 73, 71, 78, 195, 223, 4, 78, 2, 81, 18, 65, + 78, 68, 69, 68, 32, 73, 78, 84, 69, 82, 76, 65, 67, 69, 68, 32, 80, 2, + 21, 3, 69, 78, 84, 2, 251, 150, 10, 65, 30, 90, 65, 30, 67, 94, 68, 58, + 77, 58, 82, 182, 1, 83, 74, 84, 210, 172, 8, 69, 179, 124, 71, 4, 90, 78, + 159, 175, 8, 84, 2, 29, 5, 85, 82, 86, 69, 68, 2, 17, 2, 32, 65, 2, 11, + 78, 2, 217, 255, 10, 2, 71, 76, 4, 136, 131, 3, 5, 79, 85, 66, 76, 69, + 179, 188, 6, 73, 2, 165, 184, 10, 9, 65, 71, 78, 73, 70, 89, 73, 78, 71, + 10, 38, 79, 154, 178, 9, 65, 235, 29, 73, 6, 92, 5, 67, 75, 69, 84, 32, + 213, 177, 9, 12, 76, 76, 69, 82, 32, 67, 79, 65, 83, 84, 69, 82, 4, 176, + 43, 3, 66, 79, 79, 143, 208, 10, 83, 2, 11, 84, 2, 21, 3, 73, 67, 75, 2, + 185, 187, 6, 4, 32, 70, 73, 71, 2, 239, 234, 7, 65, 4, 46, 72, 85, 7, 73, + 68, 69, 32, 65, 82, 67, 2, 17, 2, 65, 68, 2, 17, 2, 69, 68, 2, 209, 172, + 10, 6, 32, 87, 72, 73, 84, 69, 2, 11, 32, 2, 201, 239, 8, 8, 67, 76, 79, + 67, 75, 87, 73, 83, 8, 17, 2, 84, 32, 8, 86, 73, 40, 4, 79, 86, 69, 82, + 176, 134, 5, 5, 69, 77, 66, 69, 68, 139, 133, 6, 77, 2, 17, 2, 83, 79, 2, + 131, 135, 1, 76, 2, 135, 155, 3, 82, 2, 141, 187, 10, 2, 79, 82, 214, 1, + 160, 1, 5, 65, 82, 82, 79, 87, 130, 9, 66, 86, 68, 210, 1, 70, 122, 72, + 178, 6, 76, 26, 79, 34, 80, 50, 82, 70, 83, 94, 84, 206, 8, 87, 202, 197, + 8, 67, 47, 81, 75, 26, 32, 195, 147, 9, 45, 70, 94, 65, 170, 2, 70, 110, + 84, 184, 1, 5, 87, 73, 84, 72, 32, 129, 160, 1, 4, 79, 86, 69, 82, 12, + 40, 5, 66, 79, 86, 69, 32, 143, 1, 78, 10, 70, 82, 216, 163, 1, 5, 83, + 72, 79, 82, 84, 222, 194, 3, 65, 55, 84, 4, 37, 7, 69, 86, 69, 82, 83, + 69, 32, 4, 138, 230, 4, 65, 55, 84, 2, 61, 13, 68, 32, 85, 80, 80, 69, + 82, 32, 65, 78, 68, 32, 76, 2, 17, 2, 79, 87, 2, 129, 213, 8, 2, 69, 82, + 6, 25, 4, 82, 79, 77, 32, 6, 36, 3, 66, 65, 82, 187, 213, 8, 68, 5, 189, + 1, 6, 32, 84, 79, 32, 66, 76, 10, 56, 7, 72, 82, 79, 85, 71, 72, 32, 65, + 3, 79, 32, 66, 6, 224, 224, 4, 3, 83, 85, 80, 234, 207, 4, 71, 247, 149, + 2, 88, 4, 26, 76, 139, 141, 11, 65, 2, 153, 247, 9, 3, 65, 67, 75, 40, + 140, 1, 6, 67, 79, 82, 78, 69, 82, 26, 68, 98, 76, 86, 80, 30, 83, 38, + 84, 138, 210, 8, 77, 38, 78, 122, 69, 150, 153, 1, 72, 171, 165, 1, 86, + 2, 209, 18, 2, 32, 68, 4, 11, 79, 4, 40, 4, 84, 84, 69, 68, 203, 219, 7, + 85, 2, 17, 2, 32, 83, 2, 163, 177, 11, 84, 6, 26, 79, 231, 210, 8, 65, 4, + 26, 87, 251, 194, 11, 79, 2, 157, 201, 3, 2, 69, 82, 2, 193, 145, 9, 2, + 76, 85, 6, 146, 211, 8, 77, 203, 191, 2, 84, 10, 154, 16, 73, 171, 3, 65, + 8, 58, 65, 204, 11, 5, 79, 84, 84, 79, 77, 187, 199, 8, 76, 2, 145, 2, 2, + 67, 75, 14, 48, 6, 79, 85, 66, 76, 69, 32, 167, 225, 8, 65, 12, 40, 5, + 65, 82, 82, 79, 87, 211, 18, 68, 11, 26, 32, 143, 137, 9, 45, 6, 26, 87, + 167, 215, 8, 70, 4, 25, 4, 73, 84, 72, 32, 4, 186, 143, 11, 86, 79, 83, + 4, 40, 4, 82, 79, 78, 84, 195, 210, 8, 73, 2, 193, 209, 8, 14, 45, 84, + 73, 76, 84, 69, 68, 32, 83, 72, 65, 68, 79, 87, 30, 26, 65, 251, 213, 8, + 69, 26, 48, 6, 82, 80, 79, 79, 78, 32, 131, 248, 10, 78, 24, 80, 10, 87, + 73, 84, 72, 32, 66, 65, 82, 66, 32, 197, 152, 1, 4, 79, 86, 69, 82, 22, + 44, 4, 68, 79, 87, 78, 173, 1, 2, 85, 80, 10, 26, 32, 231, 219, 8, 87, 8, + 76, 8, 65, 66, 79, 86, 69, 32, 76, 69, 18, 66, 166, 211, 8, 70, 179, 5, + 84, 2, 227, 2, 70, 2, 253, 222, 4, 7, 69, 76, 79, 87, 32, 76, 79, 12, 26, + 32, 187, 218, 8, 87, 10, 60, 6, 65, 66, 79, 86, 69, 32, 154, 210, 8, 70, + 179, 5, 84, 6, 22, 76, 155, 1, 82, 4, 32, 2, 69, 70, 187, 221, 4, 79, 2, + 213, 144, 2, 24, 84, 87, 65, 82, 68, 83, 32, 72, 65, 82, 80, 79, 79, 78, + 32, 87, 73, 84, 72, 32, 66, 65, 82, 66, 2, 21, 3, 73, 71, 72, 2, 105, 24, + 84, 87, 65, 82, 68, 83, 32, 72, 65, 82, 80, 79, 79, 78, 32, 87, 73, 84, + 72, 32, 66, 65, 82, 66, 2, 11, 32, 2, 139, 241, 1, 68, 2, 141, 1, 2, 69, + 70, 2, 201, 212, 8, 3, 80, 69, 78, 4, 202, 216, 8, 65, 233, 206, 1, 3, + 85, 83, 72, 4, 36, 3, 73, 71, 72, 223, 228, 10, 79, 2, 11, 84, 2, 171, 1, + 45, 6, 32, 2, 81, 85, 215, 207, 8, 65, 4, 26, 73, 239, 207, 8, 65, 2, + 229, 215, 8, 2, 71, 71, 54, 38, 79, 60, 2, 82, 73, 227, 4, 87, 2, 11, 80, + 2, 11, 32, 2, 253, 199, 8, 4, 83, 72, 65, 68, 34, 40, 5, 65, 78, 71, 76, + 69, 255, 3, 80, 30, 56, 8, 45, 72, 69, 65, 68, 69, 68, 32, 251, 219, 8, + 32, 28, 52, 5, 65, 82, 82, 79, 87, 202, 212, 8, 68, 39, 80, 25, 11, 32, + 22, 64, 8, 79, 86, 69, 82, 32, 76, 69, 70, 22, 87, 135, 208, 8, 84, 2, + 183, 207, 8, 84, 18, 25, 4, 73, 84, 72, 32, 18, 128, 1, 7, 68, 79, 85, + 66, 76, 69, 32, 36, 7, 76, 79, 78, 71, 32, 84, 73, 230, 207, 8, 66, 158, + 1, 77, 34, 78, 34, 86, 35, 72, 4, 166, 138, 9, 72, 195, 247, 1, 86, 4, + 11, 80, 4, 11, 32, 4, 18, 68, 35, 85, 2, 181, 208, 8, 3, 79, 87, 78, 2, + 151, 208, 8, 80, 4, 21, 3, 76, 69, 32, 4, 138, 3, 68, 203, 145, 10, 65, + 18, 11, 79, 18, 56, 8, 45, 72, 69, 65, 68, 69, 68, 32, 175, 210, 8, 32, + 16, 76, 6, 65, 82, 82, 79, 87, 32, 213, 1, 8, 84, 82, 73, 80, 76, 69, 32, + 68, 14, 44, 5, 87, 73, 84, 72, 32, 179, 198, 8, 70, 12, 42, 84, 210, 198, + 7, 68, 235, 183, 3, 86, 8, 26, 65, 243, 209, 8, 82, 6, 17, 2, 73, 76, 7, + 33, 6, 32, 87, 73, 84, 72, 32, 4, 250, 197, 7, 68, 235, 183, 3, 86, 2, + 249, 132, 1, 2, 65, 83, 8, 58, 65, 21, 10, 72, 73, 84, 69, 32, 65, 82, + 82, 79, 87, 2, 207, 206, 8, 86, 7, 11, 32, 4, 216, 193, 2, 4, 70, 82, 79, + 77, 219, 145, 6, 87, 19, 66, 32, 136, 1, 6, 69, 68, 32, 80, 76, 65, 21, + 3, 73, 78, 71, 12, 82, 66, 22, 80, 212, 201, 4, 2, 73, 78, 26, 69, 154, + 158, 3, 79, 195, 198, 2, 65, 2, 239, 153, 11, 85, 2, 11, 79, 2, 135, 232, + 10, 73, 2, 247, 245, 10, 78, 2, 209, 199, 3, 2, 32, 66, 4, 24, 2, 70, 65, + 75, 83, 2, 17, 2, 76, 76, 2, 21, 3, 73, 78, 71, 2, 177, 231, 1, 2, 32, + 68, 2, 189, 173, 3, 2, 79, 85, 8, 222, 169, 11, 69, 2, 73, 2, 77, 3, 79, + 126, 136, 2, 2, 67, 75, 20, 2, 76, 76, 188, 2, 4, 77, 65, 78, 32, 194, 8, + 79, 80, 2, 83, 69, 20, 6, 84, 65, 84, 69, 68, 32, 152, 3, 3, 85, 78, 68, + 170, 178, 3, 87, 252, 213, 3, 15, 65, 83, 84, 69, 68, 32, 83, 87, 69, 69, + 84, 32, 80, 79, 84, 185, 166, 2, 2, 66, 79, 5, 251, 138, 11, 69, 10, 130, + 1, 69, 64, 4, 32, 79, 70, 32, 101, 21, 73, 78, 71, 32, 79, 78, 32, 84, + 72, 69, 32, 70, 76, 79, 79, 82, 32, 76, 65, 85, 71, 6, 60, 9, 68, 45, 85, + 80, 32, 78, 69, 87, 83, 33, 2, 82, 32, 2, 11, 80, 2, 175, 131, 2, 65, 4, + 28, 3, 67, 79, 65, 23, 83, 2, 131, 235, 9, 83, 2, 135, 95, 75, 2, 231, + 211, 10, 72, 72, 140, 1, 6, 67, 69, 78, 84, 85, 82, 22, 68, 100, 3, 81, + 85, 73, 28, 8, 78, 85, 77, 69, 82, 65, 76, 32, 182, 4, 83, 114, 85, 135, + 235, 7, 65, 2, 159, 215, 5, 73, 6, 98, 69, 232, 5, 5, 85, 80, 79, 78, 68, + 61, 12, 73, 77, 73, 68, 73, 65, 32, 83, 69, 88, 84, 85, 2, 229, 5, 3, 78, + 65, 82, 48, 142, 1, 70, 136, 1, 3, 79, 78, 69, 134, 1, 83, 66, 84, 232, + 14, 10, 82, 69, 86, 69, 82, 83, 69, 68, 32, 79, 150, 237, 2, 69, 163, + 135, 7, 78, 14, 26, 73, 183, 168, 10, 79, 12, 36, 3, 70, 84, 89, 255, + 254, 2, 86, 7, 11, 32, 4, 194, 196, 5, 84, 177, 221, 4, 5, 69, 65, 82, + 76, 89, 11, 11, 32, 8, 50, 72, 41, 8, 84, 72, 79, 85, 83, 65, 78, 68, 4, + 185, 1, 6, 85, 78, 68, 82, 69, 68, 5, 141, 130, 4, 2, 32, 67, 6, 32, 2, + 73, 88, 159, 172, 9, 69, 5, 241, 159, 10, 2, 32, 76, 10, 42, 69, 150, + 253, 2, 87, 239, 174, 6, 72, 4, 11, 78, 5, 11, 32, 2, 131, 194, 5, 84, + 10, 46, 69, 189, 200, 7, 5, 73, 76, 73, 81, 85, 8, 60, 2, 77, 85, 40, 5, + 83, 84, 69, 82, 84, 21, 2, 88, 84, 2, 17, 2, 78, 67, 2, 231, 199, 7, 73, + 2, 207, 234, 7, 73, 4, 18, 65, 23, 85, 2, 179, 234, 7, 78, 2, 151, 199, + 7, 76, 4, 48, 6, 84, 32, 86, 69, 71, 69, 219, 225, 9, 83, 2, 141, 197, 2, + 2, 84, 65, 5, 179, 210, 9, 84, 10, 166, 2, 70, 32, 11, 72, 69, 65, 86, + 89, 32, 66, 76, 65, 67, 75, 52, 24, 76, 73, 71, 72, 84, 32, 70, 79, 85, + 82, 32, 80, 79, 73, 78, 84, 69, 68, 32, 66, 76, 65, 67, 75, 0, 18, 87, + 72, 73, 84, 69, 32, 70, 79, 85, 82, 32, 80, 79, 73, 78, 84, 69, 68, 161, + 53, 8, 67, 65, 80, 73, 84, 65, 76, 32, 2, 29, 5, 76, 79, 82, 65, 76, 2, + 201, 182, 9, 8, 32, 72, 69, 65, 82, 84, 32, 66, 2, 213, 207, 9, 2, 32, + 67, 16, 74, 32, 89, 14, 69, 68, 32, 83, 89, 77, 66, 79, 76, 32, 70, 79, + 82, 32, 4, 24, 2, 80, 85, 43, 84, 2, 11, 83, 2, 209, 151, 10, 2, 72, 80, + 2, 147, 222, 3, 65, 12, 68, 2, 83, 72, 238, 162, 6, 67, 222, 206, 4, 70, + 2, 76, 147, 17, 88, 4, 40, 4, 85, 65, 78, 71, 159, 241, 10, 79, 2, 171, + 130, 11, 88, 136, 2, 226, 1, 66, 20, 3, 71, 66, 89, 40, 5, 76, 69, 45, + 68, 69, 40, 3, 77, 73, 32, 154, 5, 78, 156, 26, 26, 83, 83, 73, 65, 78, + 32, 65, 83, 84, 82, 79, 76, 79, 71, 73, 67, 65, 76, 32, 83, 89, 77, 66, + 79, 76, 32, 203, 152, 5, 80, 2, 255, 243, 8, 76, 2, 177, 137, 9, 5, 32, + 70, 79, 79, 84, 2, 11, 76, 2, 165, 255, 5, 2, 65, 89, 62, 68, 9, 70, 82, + 65, 67, 84, 73, 79, 78, 32, 102, 78, 171, 198, 2, 68, 8, 40, 4, 79, 78, + 69, 32, 187, 175, 9, 84, 6, 34, 84, 250, 139, 9, 72, 47, 81, 2, 167, 141, + 9, 72, 36, 33, 6, 85, 77, 66, 69, 82, 32, 36, 76, 5, 69, 73, 71, 72, 84, + 38, 70, 92, 2, 78, 73, 22, 79, 18, 83, 83, 84, 4, 158, 137, 3, 32, 231, + 135, 8, 89, 8, 18, 73, 35, 79, 4, 130, 2, 86, 235, 158, 9, 70, 4, 136, 2, + 2, 85, 82, 195, 158, 9, 82, 4, 77, 2, 78, 69, 2, 167, 1, 78, 8, 40, 4, + 69, 86, 69, 78, 1, 2, 73, 88, 4, 206, 135, 3, 32, 159, 246, 7, 84, 10, + 34, 72, 50, 87, 223, 190, 10, 69, 4, 32, 2, 82, 69, 199, 158, 9, 73, 2, + 39, 69, 4, 26, 79, 183, 158, 9, 69, 2, 187, 134, 3, 32, 182, 1, 32, 3, + 73, 67, 32, 147, 25, 78, 178, 1, 220, 1, 6, 66, 69, 76, 71, 84, 72, 20, + 4, 67, 82, 79, 83, 20, 7, 76, 69, 84, 84, 69, 82, 32, 210, 22, 83, 24, 6, + 77, 85, 76, 84, 73, 80, 216, 196, 7, 5, 65, 82, 76, 65, 85, 161, 202, 1, + 7, 84, 86, 73, 77, 65, 68, 85, 2, 135, 166, 9, 79, 2, 235, 197, 7, 83, + 166, 1, 202, 4, 65, 110, 67, 98, 68, 126, 69, 82, 70, 222, 1, 71, 104, 2, + 72, 65, 50, 73, 220, 1, 5, 74, 69, 82, 65, 78, 34, 75, 58, 76, 234, 1, + 79, 128, 1, 13, 82, 65, 73, 68, 79, 32, 82, 65, 68, 32, 82, 69, 73, 34, + 83, 176, 2, 16, 66, 69, 82, 75, 65, 78, 65, 78, 32, 66, 69, 79, 82, 67, + 32, 66, 144, 1, 12, 78, 65, 85, 68, 73, 90, 32, 78, 89, 68, 32, 78, 110, + 84, 194, 1, 87, 128, 91, 7, 85, 82, 85, 90, 32, 85, 82, 196, 189, 2, 10, + 77, 65, 78, 78, 65, 90, 32, 77, 65, 78, 168, 63, 13, 80, 69, 82, 84, 72, + 79, 32, 80, 69, 79, 82, 84, 72, 206, 199, 3, 89, 158, 214, 3, 81, 2, 86, + 2, 88, 3, 90, 8, 222, 4, 69, 174, 185, 6, 67, 0, 4, 78, 83, 85, 90, 189, + 186, 3, 9, 76, 71, 73, 90, 32, 69, 79, 76, 72, 11, 46, 69, 30, 65, 245, + 152, 7, 3, 87, 69, 79, 4, 26, 65, 211, 133, 11, 78, 2, 191, 219, 9, 76, + 11, 84, 6, 79, 84, 84, 69, 68, 45, 193, 231, 3, 9, 65, 71, 65, 90, 32, + 68, 65, 69, 71, 6, 226, 132, 11, 76, 2, 78, 3, 80, 11, 136, 76, 7, 72, + 87, 65, 90, 32, 69, 72, 218, 255, 9, 65, 206, 55, 84, 63, 78, 12, 120, + 13, 82, 65, 78, 75, 83, 32, 67, 65, 83, 75, 69, 84, 32, 145, 180, 7, 11, + 69, 72, 85, 32, 70, 69, 79, 72, 32, 70, 69, 10, 46, 65, 234, 196, 10, 73, + 2, 79, 207, 60, 69, 4, 26, 69, 171, 130, 11, 67, 2, 151, 216, 9, 83, 9, + 26, 69, 159, 201, 10, 65, 4, 52, 7, 66, 79, 32, 71, 89, 70, 85, 195, 129, + 11, 82, 2, 235, 128, 11, 32, 4, 236, 8, 2, 69, 71, 13, 4, 71, 76, 65, 90, + 12, 156, 1, 2, 78, 71, 20, 9, 83, 65, 90, 32, 73, 83, 32, 73, 83, 20, 5, + 87, 65, 90, 32, 69, 216, 198, 10, 10, 67, 69, 76, 65, 78, 68, 73, 67, 45, + 89, 3, 79, 5, 199, 211, 6, 87, 2, 183, 191, 10, 83, 2, 163, 254, 10, 79, + 2, 11, 32, 2, 147, 255, 10, 74, 7, 21, 3, 65, 85, 78, 4, 206, 251, 10, + 32, 155, 3, 65, 12, 120, 15, 65, 85, 75, 65, 90, 32, 76, 65, 71, 85, 32, + 76, 79, 71, 82, 21, 11, 79, 78, 71, 45, 66, 82, 65, 78, 67, 72, 45, 2, + 251, 241, 9, 32, 10, 64, 3, 65, 82, 32, 158, 4, 72, 62, 77, 66, 79, 131, + 191, 10, 89, 2, 159, 230, 10, 65, 15, 150, 5, 83, 0, 12, 84, 72, 65, 76, + 65, 78, 32, 69, 84, 72, 69, 76, 188, 247, 10, 4, 80, 69, 78, 45, 14, 69, + 2, 78, 3, 79, 2, 11, 68, 2, 247, 194, 10, 32, 26, 150, 1, 72, 244, 2, 18, + 73, 71, 69, 76, 32, 76, 79, 78, 71, 45, 66, 82, 65, 78, 67, 72, 45, 83, + 208, 253, 6, 5, 79, 87, 73, 76, 79, 203, 173, 2, 84, 21, 45, 9, 79, 82, + 84, 45, 84, 87, 73, 71, 45, 18, 102, 66, 58, 72, 62, 77, 30, 78, 38, 79, + 42, 83, 186, 1, 84, 128, 173, 6, 2, 65, 82, 163, 144, 4, 89, 2, 33, 6, + 74, 65, 82, 75, 65, 78, 2, 243, 186, 9, 32, 2, 25, 4, 65, 71, 65, 76, 2, + 11, 76, 2, 159, 247, 10, 32, 2, 253, 154, 3, 2, 65, 68, 2, 157, 168, 10, + 4, 65, 85, 68, 32, 2, 17, 2, 83, 83, 2, 211, 216, 10, 32, 2, 11, 79, 2, + 195, 253, 6, 76, 4, 116, 15, 72, 85, 82, 73, 83, 65, 90, 32, 84, 72, 85, + 82, 83, 32, 84, 33, 10, 73, 87, 65, 90, 32, 84, 73, 82, 32, 84, 2, 11, + 72, 2, 171, 227, 5, 79, 2, 17, 2, 89, 82, 2, 187, 217, 10, 32, 5, 41, 8, + 85, 78, 74, 79, 32, 87, 89, 78, 2, 11, 78, 2, 251, 246, 9, 32, 2, 21, 3, + 73, 78, 71, 2, 237, 174, 7, 2, 76, 69, 4, 188, 137, 9, 16, 73, 78, 71, + 32, 83, 72, 73, 82, 84, 32, 87, 73, 84, 72, 32, 83, 187, 178, 1, 69, 12, + 120, 3, 66, 73, 78, 2, 78, 28, 2, 81, 85, 0, 3, 86, 73, 71, 174, 160, 7, + 83, 229, 169, 1, 6, 84, 82, 69, 68, 69, 67, 2, 169, 202, 8, 2, 79, 86, 2, + 129, 202, 8, 2, 73, 78, 200, 40, 244, 1, 2, 32, 73, 22, 65, 162, 26, 67, + 138, 5, 69, 166, 11, 72, 242, 36, 73, 162, 233, 1, 75, 154, 2, 76, 142, + 9, 77, 166, 21, 78, 190, 2, 79, 158, 39, 80, 172, 12, 2, 81, 85, 138, 70, + 83, 38, 84, 138, 17, 85, 150, 43, 87, 186, 1, 89, 247, 178, 5, 71, 2, + 183, 201, 9, 78, 198, 2, 132, 2, 5, 70, 69, 84, 89, 32, 36, 4, 71, 73, + 84, 84, 30, 76, 112, 8, 77, 65, 82, 73, 84, 65, 78, 32, 146, 14, 78, 190, + 3, 84, 102, 85, 204, 244, 2, 2, 73, 76, 242, 91, 88, 144, 148, 6, 15, 75, + 69, 32, 66, 79, 84, 84, 76, 69, 32, 65, 78, 68, 32, 67, 159, 98, 82, 4, + 254, 251, 6, 86, 207, 242, 2, 80, 2, 201, 213, 3, 2, 65, 82, 6, 18, 84, + 75, 85, 4, 36, 3, 32, 83, 72, 235, 202, 9, 73, 2, 11, 65, 2, 171, 175, + 10, 75, 2, 227, 228, 9, 84, 122, 184, 1, 7, 76, 69, 84, 84, 69, 82, 32, + 198, 3, 77, 248, 2, 12, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, + 136, 4, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 203, 159, 4, 65, + 44, 202, 1, 66, 32, 2, 68, 65, 22, 73, 38, 75, 22, 76, 34, 83, 46, 84, + 182, 2, 65, 212, 231, 5, 2, 71, 65, 222, 153, 1, 82, 202, 142, 2, 90, + 182, 83, 77, 172, 1, 2, 81, 85, 118, 78, 226, 6, 89, 251, 101, 70, 4, + 186, 205, 10, 73, 247, 25, 65, 2, 151, 224, 9, 76, 6, 178, 233, 10, 78, + 2, 84, 3, 89, 2, 223, 231, 9, 65, 2, 11, 65, 2, 191, 223, 9, 66, 4, 156, + 7, 3, 73, 78, 71, 163, 149, 9, 72, 6, 254, 230, 9, 65, 134, 101, 73, 229, + 10, 5, 83, 65, 65, 68, 73, 18, 96, 4, 65, 82, 75, 32, 165, 1, 15, 79, 68, + 73, 70, 73, 69, 82, 32, 76, 69, 84, 84, 69, 82, 32, 12, 82, 68, 40, 2, + 73, 78, 90, 69, 242, 2, 78, 213, 191, 8, 5, 79, 67, 67, 76, 85, 2, 17, 2, + 65, 71, 2, 155, 251, 8, 69, 5, 17, 2, 45, 65, 2, 203, 228, 9, 76, 6, 46, + 69, 184, 6, 2, 83, 72, 131, 223, 10, 73, 2, 229, 235, 9, 11, 80, 69, 78, + 84, 72, 69, 84, 73, 67, 32, 89, 28, 130, 1, 65, 154, 1, 66, 22, 78, 30, + 83, 134, 1, 90, 250, 166, 3, 84, 156, 149, 7, 9, 77, 69, 76, 79, 68, 73, + 67, 32, 81, 3, 81, 10, 76, 3, 70, 83, 65, 34, 78, 28, 2, 84, 77, 245, + 189, 10, 4, 82, 75, 65, 65, 2, 11, 65, 2, 151, 227, 10, 81, 4, 26, 78, + 211, 206, 5, 71, 2, 11, 65, 2, 243, 189, 10, 65, 2, 165, 143, 3, 2, 69, + 81, 4, 64, 4, 72, 73, 89, 89, 45, 8, 79, 70, 32, 77, 65, 83, 72, 70, 2, + 11, 65, 2, 11, 65, 2, 155, 236, 8, 76, 2, 139, 216, 9, 65, 4, 34, 65, + 209, 235, 8, 2, 73, 81, 2, 223, 223, 9, 69, 30, 92, 5, 76, 79, 78, 71, + 32, 54, 79, 66, 83, 174, 205, 6, 65, 242, 145, 4, 69, 2, 73, 3, 85, 10, + 158, 206, 6, 65, 242, 145, 4, 69, 2, 73, 3, 85, 7, 41, 8, 86, 69, 82, 76, + 79, 78, 71, 32, 4, 191, 205, 6, 65, 4, 26, 72, 183, 219, 5, 85, 2, 129, + 150, 6, 3, 79, 82, 84, 10, 68, 8, 83, 45, 83, 69, 82, 73, 70, 32, 241, + 187, 10, 3, 68, 87, 73, 8, 44, 6, 72, 69, 65, 86, 89, 32, 139, 2, 73, 6, + 48, 3, 68, 79, 85, 81, 5, 76, 79, 87, 32, 68, 4, 11, 66, 4, 21, 3, 76, + 69, 32, 4, 84, 6, 84, 85, 82, 78, 69, 68, 23, 67, 2, 21, 3, 79, 85, 66, + 2, 17, 2, 76, 69, 2, 17, 2, 32, 67, 2, 33, 6, 79, 77, 77, 65, 32, 81, 2, + 145, 144, 9, 3, 85, 79, 84, 2, 253, 151, 10, 10, 78, 84, 69, 82, 82, 79, + 66, 65, 78, 71, 6, 48, 6, 69, 76, 76, 73, 84, 69, 143, 200, 5, 85, 5, 25, + 4, 32, 65, 78, 84, 2, 147, 160, 7, 69, 168, 1, 50, 82, 225, 141, 5, 6, + 68, 73, 32, 82, 73, 89, 166, 1, 52, 7, 65, 83, 72, 84, 82, 65, 32, 183, + 191, 4, 79, 164, 1, 180, 1, 7, 76, 69, 84, 84, 69, 82, 32, 212, 1, 5, 83, + 73, 71, 78, 32, 194, 21, 68, 176, 250, 2, 16, 67, 79, 78, 83, 79, 78, 65, + 78, 84, 32, 83, 73, 71, 78, 32, 72, 211, 155, 2, 86, 100, 154, 250, 6, + 65, 38, 68, 114, 84, 46, 86, 186, 5, 85, 206, 141, 1, 79, 238, 60, 73, + 42, 76, 226, 195, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, + 80, 162, 7, 69, 234, 61, 72, 2, 77, 2, 82, 3, 89, 8, 218, 184, 6, 67, + 234, 216, 3, 65, 239, 1, 86, 52, 66, 65, 32, 4, 72, 79, 79, 76, 46, 79, + 74, 82, 183, 212, 10, 73, 4, 194, 191, 9, 76, 227, 20, 82, 5, 245, 191, + 5, 6, 32, 83, 65, 84, 67, 72, 6, 36, 3, 82, 80, 73, 203, 154, 9, 79, 4, + 242, 132, 10, 79, 135, 18, 85, 36, 66, 69, 72, 4, 73, 80, 84, 32, 214, + 250, 1, 85, 175, 206, 6, 79, 4, 36, 3, 87, 68, 82, 235, 131, 10, 69, 2, + 11, 73, 2, 207, 149, 10, 86, 28, 80, 8, 67, 65, 80, 73, 84, 65, 76, 32, + 86, 76, 81, 6, 83, 77, 65, 76, 76, 32, 18, 210, 210, 10, 66, 2, 69, 2, + 70, 2, 72, 2, 73, 2, 76, 2, 77, 2, 80, 3, 82, 2, 37, 7, 73, 71, 65, 84, + 85, 82, 69, 2, 11, 32, 2, 233, 141, 10, 2, 69, 84, 8, 174, 209, 10, 69, + 2, 71, 2, 76, 3, 79, 220, 1, 130, 2, 65, 30, 67, 74, 69, 36, 5, 71, 77, + 69, 78, 84, 28, 2, 77, 73, 172, 1, 8, 80, 65, 82, 65, 84, 69, 68, 32, + 138, 4, 82, 158, 1, 83, 68, 2, 84, 32, 168, 140, 5, 8, 87, 73, 78, 71, + 32, 78, 69, 69, 202, 145, 3, 88, 190, 106, 68, 233, 162, 1, 2, 76, 70, 4, + 250, 206, 10, 76, 3, 84, 6, 34, 84, 245, 226, 5, 2, 79, 78, 4, 178, 201, + 6, 73, 175, 204, 3, 79, 4, 166, 224, 1, 68, 191, 132, 1, 45, 23, 189, + 191, 4, 2, 69, 68, 6, 212, 84, 28, 68, 73, 82, 69, 67, 84, 32, 80, 82, + 79, 68, 85, 67, 84, 32, 87, 73, 84, 72, 32, 66, 79, 84, 84, 79, 77, 32, + 67, 172, 208, 7, 3, 83, 69, 88, 211, 216, 1, 67, 158, 1, 48, 6, 66, 76, + 79, 67, 75, 32, 203, 182, 9, 83, 156, 1, 88, 9, 81, 85, 65, 68, 82, 65, + 78, 84, 45, 129, 1, 8, 83, 69, 88, 84, 65, 78, 84, 45, 30, 42, 49, 38, + 50, 30, 51, 171, 202, 10, 52, 17, 34, 50, 30, 51, 171, 202, 10, 52, 9, + 26, 51, 171, 202, 10, 52, 5, 167, 202, 10, 52, 126, 58, 49, 54, 50, 46, + 51, 38, 52, 30, 53, 187, 200, 10, 54, 65, 50, 50, 46, 51, 38, 52, 30, 53, + 187, 200, 10, 54, 33, 42, 51, 38, 52, 30, 53, 187, 200, 10, 54, 17, 34, + 52, 30, 53, 187, 200, 10, 54, 9, 26, 53, 187, 200, 10, 54, 5, 183, 200, + 10, 54, 4, 120, 2, 86, 73, 129, 175, 2, 22, 73, 79, 85, 83, 32, 70, 65, + 67, 69, 32, 87, 73, 84, 72, 32, 83, 89, 77, 66, 79, 76, 83, 2, 11, 67, 2, + 215, 133, 10, 69, 4, 52, 7, 81, 85, 73, 81, 85, 65, 68, 239, 141, 9, 65, + 2, 91, 82, 4, 64, 10, 84, 82, 65, 78, 83, 77, 73, 84, 32, 83, 151, 187, + 6, 77, 2, 11, 84, 2, 223, 252, 8, 65, 134, 3, 102, 65, 218, 20, 73, 102, + 79, 214, 13, 69, 70, 82, 224, 143, 9, 5, 85, 70, 70, 76, 69, 195, 145, 1, + 89, 192, 2, 164, 1, 12, 68, 79, 87, 69, 68, 32, 87, 72, 73, 84, 69, 32, + 56, 9, 76, 76, 79, 87, 32, 80, 65, 78, 32, 62, 82, 182, 10, 86, 160, 198, + 7, 2, 77, 82, 227, 233, 1, 75, 6, 174, 239, 8, 67, 206, 11, 83, 245, 4, + 3, 76, 65, 84, 2, 17, 2, 79, 70, 2, 17, 2, 32, 70, 2, 187, 136, 7, 79, + 210, 1, 40, 4, 65, 68, 65, 32, 167, 194, 10, 75, 208, 1, 162, 1, 68, 46, + 69, 110, 72, 34, 76, 246, 1, 83, 212, 2, 6, 86, 79, 87, 69, 76, 32, 170, + 199, 4, 65, 188, 211, 1, 7, 67, 79, 78, 84, 73, 78, 85, 195, 143, 4, 79, + 24, 194, 224, 6, 79, 66, 65, 255, 235, 1, 73, 4, 84, 15, 88, 84, 82, 65, + 32, 83, 72, 79, 82, 84, 32, 86, 79, 87, 69, 235, 130, 9, 75, 2, 179, 254, + 9, 76, 2, 217, 143, 10, 3, 69, 65, 68, 96, 33, 6, 69, 84, 84, 69, 82, 32, + 96, 170, 225, 6, 65, 38, 68, 114, 84, 46, 86, 186, 5, 85, 186, 202, 1, + 73, 42, 76, 226, 195, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, + 2, 80, 138, 69, 72, 2, 77, 2, 82, 2, 89, 186, 2, 69, 3, 79, 30, 70, 65, + 38, 69, 56, 4, 73, 71, 78, 32, 145, 183, 9, 3, 85, 84, 82, 2, 193, 251, + 9, 4, 78, 68, 72, 73, 6, 212, 205, 2, 5, 67, 84, 73, 79, 78, 243, 189, 4, + 80, 20, 106, 73, 154, 144, 5, 83, 202, 141, 1, 67, 98, 78, 190, 66, 65, + 190, 158, 1, 74, 150, 3, 85, 235, 245, 1, 86, 2, 37, 7, 78, 86, 69, 82, + 84, 69, 68, 2, 181, 157, 6, 2, 32, 67, 46, 60, 6, 77, 79, 68, 73, 70, 73, + 21, 5, 83, 73, 71, 78, 32, 2, 155, 146, 6, 69, 44, 110, 67, 42, 79, 34, + 80, 162, 183, 4, 85, 194, 229, 1, 83, 242, 68, 65, 58, 86, 166, 202, 1, + 73, 199, 140, 2, 69, 4, 193, 157, 6, 5, 65, 78, 68, 82, 65, 7, 186, 162, + 10, 79, 215, 22, 69, 2, 57, 12, 82, 73, 83, 72, 84, 72, 65, 77, 65, 84, + 82, 65, 2, 223, 161, 10, 32, 98, 72, 3, 69, 68, 32, 21, 11, 73, 65, 78, + 32, 76, 69, 84, 84, 69, 82, 32, 2, 215, 250, 9, 73, 96, 158, 2, 65, 120, + 3, 67, 72, 85, 22, 69, 70, 72, 46, 73, 46, 76, 22, 77, 38, 79, 94, 80, + 18, 82, 22, 83, 38, 84, 64, 2, 87, 79, 36, 2, 89, 69, 212, 243, 4, 2, 74, + 85, 166, 228, 2, 68, 202, 13, 90, 218, 88, 70, 182, 6, 71, 234, 45, 66, + 246, 11, 75, 234, 21, 86, 250, 27, 78, 167, 128, 1, 85, 16, 82, 82, 242, + 251, 9, 73, 234, 25, 68, 162, 8, 71, 2, 87, 198, 21, 83, 147, 1, 72, 4, + 170, 166, 9, 82, 163, 142, 1, 69, 2, 255, 145, 10, 82, 8, 38, 65, 146, + 251, 9, 82, 139, 56, 71, 4, 234, 179, 10, 82, 3, 84, 4, 164, 182, 8, 2, + 65, 45, 179, 172, 1, 85, 6, 194, 227, 9, 65, 142, 57, 67, 215, 22, 70, 2, + 207, 168, 8, 79, 4, 146, 158, 5, 69, 227, 250, 3, 73, 12, 70, 79, 170, + 166, 9, 73, 166, 111, 85, 150, 25, 65, 154, 3, 78, 3, 82, 2, 163, 155, + 10, 90, 2, 255, 15, 69, 2, 151, 142, 9, 79, 4, 130, 143, 9, 85, 191, 162, + 1, 79, 6, 26, 72, 215, 148, 10, 79, 4, 218, 131, 6, 73, 223, 155, 4, 69, + 4, 138, 165, 9, 79, 211, 139, 1, 69, 4, 182, 176, 10, 65, 3, 87, 10, 78, + 69, 186, 232, 3, 70, 148, 126, 6, 78, 84, 79, 32, 83, 72, 131, 201, 5, + 80, 2, 171, 233, 9, 76, 50, 252, 1, 2, 79, 84, 32, 6, 80, 80, 73, 78, 71, + 32, 72, 2, 82, 84, 128, 11, 7, 85, 76, 68, 69, 82, 69, 68, 184, 240, 1, + 24, 67, 75, 69, 68, 32, 70, 65, 67, 69, 32, 87, 73, 84, 72, 32, 69, 88, + 80, 76, 79, 68, 73, 78, 71, 234, 155, 3, 86, 199, 215, 4, 87, 2, 197, + 147, 8, 3, 73, 78, 71, 4, 36, 3, 84, 82, 79, 131, 242, 5, 66, 2, 11, 76, + 2, 139, 143, 8, 76, 36, 102, 32, 200, 8, 12, 72, 65, 78, 68, 32, 70, 79, + 82, 77, 65, 84, 32, 250, 182, 6, 67, 171, 236, 3, 83, 24, 242, 1, 66, 92, + 11, 83, 76, 65, 78, 84, 69, 68, 32, 78, 79, 82, 196, 1, 4, 76, 69, 70, + 84, 156, 1, 11, 82, 73, 71, 72, 84, 87, 65, 82, 68, 83, 32, 248, 1, 7, + 85, 80, 32, 84, 65, 67, 75, 129, 161, 2, 9, 68, 79, 87, 78, 32, 84, 65, + 67, 75, 4, 88, 14, 65, 67, 75, 83, 76, 65, 78, 84, 69, 68, 32, 83, 79, + 85, 33, 4, 69, 78, 84, 32, 2, 11, 84, 2, 179, 140, 9, 72, 2, 221, 40, 37, + 65, 82, 82, 79, 87, 32, 80, 79, 73, 78, 84, 73, 78, 71, 32, 68, 79, 87, + 78, 87, 65, 82, 68, 83, 32, 84, 72, 69, 78, 32, 78, 79, 82, 84, 72, 32, + 69, 4, 116, 24, 87, 65, 82, 68, 83, 32, 72, 65, 82, 80, 79, 79, 78, 32, + 65, 66, 79, 86, 69, 32, 76, 79, 78, 71, 171, 3, 32, 2, 237, 1, 5, 32, 82, + 73, 71, 72, 4, 112, 11, 65, 82, 82, 79, 87, 32, 65, 66, 79, 86, 69, 29, + 13, 72, 65, 82, 80, 79, 79, 78, 32, 65, 66, 79, 86, 69, 2, 157, 128, 6, + 2, 32, 76, 2, 29, 5, 32, 76, 79, 78, 71, 2, 25, 4, 32, 76, 69, 70, 2, + 153, 250, 6, 6, 84, 87, 65, 82, 68, 83, 7, 11, 32, 4, 76, 13, 65, 66, 79, + 86, 69, 32, 83, 72, 79, 82, 84, 32, 68, 251, 131, 2, 87, 2, 11, 79, 2, + 11, 87, 2, 11, 78, 2, 11, 32, 2, 223, 132, 7, 84, 8, 120, 10, 67, 79, 78, + 84, 73, 78, 85, 73, 78, 71, 0, 6, 76, 69, 84, 84, 69, 82, 40, 4, 68, 79, + 87, 78, 1, 2, 85, 80, 2, 193, 179, 3, 5, 32, 79, 86, 69, 82, 2, 21, 3, + 32, 83, 84, 2, 215, 161, 10, 69, 2, 25, 4, 32, 79, 80, 69, 2, 207, 146, + 9, 78, 4, 26, 73, 179, 160, 10, 85, 2, 247, 160, 10, 77, 211, 14, 174, 1, + 68, 152, 20, 2, 71, 78, 184, 181, 1, 6, 77, 73, 76, 65, 82, 32, 158, 2, + 78, 202, 24, 88, 237, 238, 6, 15, 76, 72, 79, 85, 69, 84, 84, 69, 32, 79, + 70, 32, 74, 65, 80, 252, 1, 40, 5, 68, 72, 65, 77, 32, 135, 17, 69, 184, + 1, 202, 1, 69, 68, 7, 76, 69, 84, 84, 69, 82, 32, 176, 4, 15, 82, 69, 80, + 69, 84, 73, 84, 73, 79, 78, 32, 77, 65, 82, 75, 50, 83, 164, 8, 11, 86, + 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 171, 175, 6, 68, 2, 37, 7, 78, + 68, 32, 79, 70, 32, 84, 2, 201, 210, 9, 2, 69, 88, 102, 214, 1, 65, 98, + 84, 242, 188, 6, 68, 158, 1, 86, 186, 5, 85, 186, 202, 1, 73, 138, 196, + 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, + 76, 2, 77, 2, 82, 2, 89, 186, 2, 69, 3, 79, 11, 72, 8, 76, 84, 69, 82, + 78, 65, 84, 69, 214, 154, 10, 65, 2, 73, 3, 85, 2, 235, 245, 9, 32, 14, + 134, 1, 72, 252, 231, 5, 19, 87, 79, 45, 67, 73, 82, 67, 76, 69, 32, 65, + 76, 84, 69, 82, 78, 65, 84, 69, 254, 233, 3, 84, 195, 71, 65, 4, 164, + 217, 9, 20, 82, 69, 69, 45, 67, 73, 82, 67, 76, 69, 32, 65, 76, 84, 69, + 82, 78, 65, 84, 69, 147, 64, 65, 6, 11, 45, 6, 186, 152, 10, 49, 2, 50, + 3, 51, 44, 38, 69, 181, 7, 4, 73, 71, 78, 32, 32, 96, 11, 67, 84, 73, 79, + 78, 32, 77, 65, 82, 75, 32, 177, 6, 8, 80, 65, 82, 65, 84, 79, 82, 32, + 28, 80, 11, 68, 79, 85, 66, 76, 69, 32, 82, 73, 78, 71, 57, 5, 87, 73, + 84, 72, 32, 5, 11, 32, 2, 249, 231, 8, 6, 87, 73, 84, 72, 32, 82, 24, + 212, 1, 12, 67, 73, 82, 67, 76, 69, 83, 32, 65, 78, 68, 32, 116, 5, 81, + 85, 65, 68, 82, 0, 4, 83, 69, 80, 84, 12, 16, 82, 65, 89, 83, 32, 65, 78, + 68, 32, 68, 79, 84, 84, 69, 68, 32, 46, 68, 45, 3, 84, 82, 73, 6, 60, 4, + 70, 79, 85, 82, 0, 3, 84, 87, 79, 187, 229, 8, 82, 2, 225, 207, 6, 8, 32, + 69, 78, 67, 76, 79, 83, 85, 2, 83, 85, 6, 42, 68, 28, 3, 84, 82, 73, 215, + 1, 67, 2, 197, 1, 3, 79, 85, 66, 2, 171, 1, 80, 6, 52, 9, 68, 69, 78, 84, + 32, 65, 78, 68, 32, 103, 80, 4, 116, 6, 68, 79, 84, 84, 69, 68, 49, 14, + 85, 45, 83, 72, 65, 80, 69, 68, 32, 79, 82, 78, 65, 77, 2, 17, 2, 76, 69, + 2, 17, 2, 32, 67, 2, 25, 4, 82, 69, 83, 67, 2, 239, 186, 1, 69, 4, 158, + 237, 8, 66, 135, 83, 68, 12, 146, 229, 4, 83, 202, 141, 1, 67, 98, 78, + 138, 216, 3, 65, 239, 1, 86, 26, 74, 65, 94, 86, 210, 183, 6, 85, 186, + 202, 1, 73, 198, 140, 2, 69, 3, 79, 10, 168, 184, 6, 10, 76, 84, 69, 82, + 78, 65, 84, 69, 32, 85, 254, 214, 3, 65, 2, 73, 3, 85, 4, 11, 79, 4, 33, + 6, 67, 65, 76, 73, 67, 32, 4, 255, 183, 6, 82, 68, 84, 12, 84, 73, 67, + 32, 76, 69, 84, 84, 69, 82, 32, 78, 49, 5, 87, 65, 89, 83, 32, 52, 178, + 156, 1, 50, 222, 176, 3, 48, 131, 2, 49, 16, 56, 5, 66, 76, 65, 67, 75, + 1, 5, 87, 72, 73, 84, 69, 8, 11, 32, 8, 70, 82, 24, 3, 76, 69, 70, 12, 4, + 68, 79, 87, 78, 1, 2, 85, 80, 2, 21, 3, 73, 71, 72, 2, 11, 84, 2, 225, + 92, 6, 32, 80, 79, 73, 78, 84, 194, 10, 96, 8, 87, 82, 73, 84, 73, 78, + 71, 32, 241, 238, 1, 10, 32, 79, 70, 32, 84, 72, 69, 32, 72, 79, 192, 10, + 136, 3, 4, 65, 73, 82, 32, 192, 1, 2, 66, 82, 102, 67, 138, 1, 68, 218, + 4, 69, 242, 6, 70, 244, 3, 4, 87, 65, 76, 76, 138, 1, 72, 234, 77, 76, + 212, 6, 2, 77, 79, 222, 42, 78, 218, 1, 82, 182, 7, 83, 162, 5, 84, 180, + 10, 5, 71, 82, 65, 83, 80, 184, 5, 30, 85, 80, 80, 69, 82, 32, 66, 79, + 68, 89, 32, 84, 73, 76, 84, 73, 78, 71, 32, 70, 82, 79, 77, 32, 72, 73, + 80, 32, 74, 79, 135, 207, 4, 80, 8, 48, 4, 66, 76, 79, 87, 29, 4, 83, 85, + 67, 75, 4, 58, 32, 211, 226, 4, 73, 4, 30, 32, 61, 3, 73, 78, 71, 2, 237, + 157, 1, 10, 83, 77, 65, 76, 76, 32, 82, 79, 84, 65, 2, 171, 134, 9, 32, + 10, 52, 5, 69, 65, 84, 72, 32, 229, 169, 1, 2, 85, 83, 4, 140, 164, 2, 2, + 69, 88, 1, 2, 73, 78, 10, 40, 6, 72, 69, 69, 75, 83, 32, 63, 79, 6, 154, + 107, 83, 146, 38, 78, 153, 223, 3, 4, 80, 85, 70, 70, 4, 178, 180, 9, 76, + 223, 46, 77, 28, 108, 15, 82, 69, 65, 77, 89, 32, 69, 89, 69, 66, 82, 79, + 87, 83, 32, 165, 1, 7, 89, 78, 65, 77, 73, 67, 32, 8, 64, 4, 68, 79, 87, + 78, 0, 2, 85, 80, 29, 4, 78, 69, 85, 84, 2, 153, 143, 1, 2, 32, 78, 4, + 21, 3, 82, 65, 76, 4, 11, 32, 4, 194, 58, 68, 191, 199, 9, 85, 20, 180, + 1, 11, 69, 86, 69, 82, 89, 32, 79, 84, 72, 69, 82, 30, 70, 22, 83, 128, + 122, 9, 65, 82, 82, 79, 87, 72, 69, 65, 68, 234, 38, 82, 136, 206, 3, 2, + 84, 69, 21, 4, 71, 82, 65, 68, 2, 181, 230, 8, 2, 32, 84, 2, 163, 146, 6, + 65, 6, 68, 11, 73, 77, 85, 76, 84, 65, 78, 69, 79, 85, 83, 151, 227, 8, + 76, 5, 203, 151, 1, 32, 54, 64, 2, 89, 69, 172, 227, 4, 4, 88, 67, 73, + 84, 203, 138, 4, 65, 50, 166, 1, 32, 56, 15, 66, 82, 79, 87, 83, 32, 83, + 84, 82, 65, 73, 71, 72, 84, 32, 44, 5, 71, 65, 90, 69, 45, 140, 2, 7, 76, + 65, 83, 72, 69, 83, 32, 65, 2, 83, 32, 6, 220, 150, 1, 5, 66, 76, 73, 78, + 75, 243, 129, 5, 87, 6, 186, 53, 68, 154, 84, 78, 167, 243, 8, 85, 18, 100, 11, 70, 76, 79, 79, 82, 80, 76, 65, 78, 69, 32, 25, 10, 87, 65, 76, - 76, 80, 76, 65, 78, 69, 32, 4, 62, 67, 223, 40, 83, 6, 38, 67, 28, 2, 84, - 73, 195, 40, 83, 2, 169, 155, 8, 2, 85, 82, 2, 183, 251, 8, 76, 38, 50, - 73, 225, 3, 7, 79, 67, 65, 84, 73, 79, 78, 22, 32, 3, 77, 66, 32, 171, 1, - 80, 16, 56, 4, 67, 79, 77, 66, 29, 6, 76, 69, 78, 71, 84, 72, 2, 193, - 212, 5, 2, 73, 78, 14, 11, 45, 14, 146, 150, 9, 49, 2, 50, 2, 51, 2, 52, - 2, 53, 2, 54, 3, 55, 6, 58, 32, 153, 1, 9, 83, 32, 80, 82, 69, 83, 83, - 69, 68, 4, 116, 12, 76, 79, 87, 69, 82, 32, 79, 86, 69, 82, 32, 85, 133, - 251, 7, 11, 85, 80, 80, 69, 82, 32, 79, 86, 69, 82, 32, 2, 11, 80, 2, - 163, 214, 8, 80, 2, 29, 5, 32, 84, 79, 71, 69, 2, 11, 84, 2, 227, 213, 8, - 72, 16, 22, 32, 203, 1, 45, 12, 148, 1, 2, 72, 69, 236, 177, 2, 3, 84, - 79, 82, 144, 222, 4, 3, 68, 69, 80, 0, 3, 87, 73, 68, 229, 109, 10, 76, - 73, 77, 66, 83, 32, 68, 73, 71, 73, 4, 196, 13, 4, 65, 68, 32, 78, 131, - 218, 8, 73, 4, 52, 5, 70, 76, 79, 79, 82, 1, 4, 87, 65, 76, 76, 2, 221, - 210, 8, 5, 80, 76, 65, 78, 69, 156, 3, 64, 4, 85, 84, 72, 32, 161, 5, 7, - 86, 69, 77, 69, 78, 84, 45, 54, 186, 1, 67, 88, 5, 70, 82, 79, 87, 78, 0, - 5, 83, 77, 73, 76, 69, 56, 4, 75, 73, 83, 83, 36, 5, 79, 80, 69, 78, 32, - 192, 1, 5, 84, 69, 78, 83, 69, 165, 29, 5, 87, 82, 73, 78, 75, 8, 48, 6, - 76, 79, 83, 69, 68, 32, 163, 223, 7, 79, 6, 222, 2, 70, 142, 38, 67, 47, - 78, 7, 11, 32, 4, 22, 79, 203, 1, 87, 2, 147, 186, 7, 80, 7, 11, 32, 4, - 166, 1, 87, 83, 70, 18, 100, 4, 79, 86, 65, 76, 0, 9, 82, 69, 67, 84, 65, - 78, 71, 76, 69, 42, 87, 82, 70, 235, 185, 7, 67, 7, 11, 32, 4, 26, 87, - 151, 184, 7, 89, 2, 25, 4, 82, 73, 78, 75, 2, 203, 128, 4, 76, 7, 11, 32, - 4, 18, 70, 43, 83, 2, 17, 2, 79, 82, 2, 215, 244, 7, 87, 2, 17, 2, 85, - 67, 2, 219, 255, 3, 75, 230, 2, 192, 1, 9, 68, 73, 65, 71, 79, 78, 65, - 76, 32, 148, 1, 11, 70, 76, 79, 79, 82, 80, 76, 65, 78, 69, 32, 184, 14, - 6, 72, 73, 78, 71, 69, 32, 189, 2, 10, 87, 65, 76, 76, 80, 76, 65, 78, - 69, 32, 32, 56, 8, 66, 69, 84, 87, 69, 69, 78, 32, 22, 65, 31, 84, 16, - 18, 65, 31, 84, 8, 229, 28, 3, 87, 65, 89, 8, 201, 28, 6, 79, 87, 65, 82, - 68, 83, 150, 1, 208, 2, 24, 65, 82, 77, 32, 67, 73, 82, 67, 76, 69, 32, - 72, 73, 84, 84, 73, 78, 71, 32, 87, 65, 76, 76, 32, 38, 66, 34, 67, 224, - 1, 8, 70, 73, 78, 71, 69, 82, 32, 67, 52, 5, 72, 85, 77, 80, 32, 184, 3, - 5, 76, 79, 79, 80, 32, 198, 1, 83, 108, 7, 84, 82, 73, 80, 76, 69, 32, - 110, 87, 206, 12, 68, 198, 2, 80, 154, 6, 90, 227, 232, 7, 74, 12, 178, - 7, 76, 154, 9, 77, 39, 83, 8, 150, 17, 79, 175, 230, 7, 69, 28, 86, 72, - 20, 5, 85, 82, 86, 69, 32, 196, 29, 5, 79, 82, 78, 69, 82, 227, 165, 7, - 82, 2, 211, 212, 7, 69, 18, 80, 4, 67, 79, 77, 66, 158, 8, 72, 230, 15, - 76, 202, 172, 2, 77, 183, 177, 1, 83, 2, 11, 73, 2, 131, 248, 3, 78, 6, - 128, 9, 6, 73, 82, 67, 76, 69, 83, 239, 20, 79, 18, 56, 8, 72, 73, 84, - 84, 73, 78, 71, 32, 239, 244, 3, 83, 16, 72, 8, 67, 69, 73, 76, 73, 78, - 71, 32, 101, 6, 70, 76, 79, 79, 82, 32, 8, 56, 5, 76, 65, 82, 71, 69, 1, - 5, 83, 77, 65, 76, 76, 4, 11, 32, 4, 226, 54, 84, 135, 2, 68, 8, 88, 4, - 83, 77, 65, 76, 12, 5, 76, 65, 82, 71, 69, 17, 7, 84, 82, 73, 80, 76, 69, - 32, 2, 11, 76, 2, 255, 18, 32, 4, 56, 5, 76, 65, 82, 71, 69, 1, 5, 83, - 77, 65, 76, 76, 2, 145, 53, 2, 32, 84, 18, 56, 8, 72, 73, 84, 84, 73, 78, - 71, 32, 183, 241, 3, 83, 16, 64, 7, 67, 69, 73, 76, 73, 78, 71, 1, 5, 70, - 76, 79, 79, 82, 8, 11, 32, 8, 22, 76, 191, 9, 83, 4, 249, 22, 4, 65, 82, - 71, 69, 12, 44, 6, 72, 65, 75, 73, 78, 71, 243, 16, 73, 2, 21, 3, 32, 80, - 65, 2, 221, 240, 3, 4, 82, 65, 76, 76, 8, 76, 12, 65, 76, 84, 69, 82, 78, - 65, 84, 73, 78, 71, 32, 138, 18, 87, 63, 83, 4, 134, 18, 87, 147, 21, 77, - 18, 80, 4, 65, 86, 69, 32, 169, 1, 11, 82, 73, 83, 84, 32, 67, 73, 82, - 67, 76, 69, 14, 30, 72, 102, 83, 171, 20, 76, 8, 37, 7, 73, 84, 84, 73, - 78, 71, 32, 8, 252, 2, 4, 67, 69, 73, 76, 25, 5, 70, 76, 79, 79, 82, 4, - 170, 146, 5, 78, 231, 224, 1, 77, 4, 209, 5, 10, 32, 72, 73, 84, 84, 73, - 78, 71, 32, 87, 14, 112, 3, 85, 80, 32, 184, 1, 6, 68, 79, 87, 78, 32, - 83, 149, 225, 5, 10, 83, 73, 68, 69, 32, 84, 79, 32, 83, 73, 10, 40, 5, - 68, 79, 87, 78, 32, 143, 1, 83, 8, 68, 8, 65, 76, 84, 69, 82, 78, 65, 84, - 230, 17, 76, 215, 216, 3, 83, 4, 21, 3, 73, 78, 71, 4, 11, 32, 4, 190, - 17, 76, 215, 216, 3, 83, 2, 207, 30, 69, 162, 1, 148, 2, 11, 65, 82, 77, - 32, 67, 73, 82, 67, 76, 69, 32, 98, 66, 54, 67, 174, 4, 68, 100, 8, 70, - 73, 78, 71, 69, 82, 32, 67, 64, 5, 72, 85, 77, 80, 32, 60, 5, 76, 79, 79, - 80, 32, 102, 80, 34, 83, 216, 1, 7, 84, 82, 73, 80, 76, 69, 32, 218, 1, - 87, 202, 2, 90, 227, 232, 7, 74, 8, 18, 77, 39, 83, 4, 225, 13, 5, 69, - 68, 73, 85, 77, 4, 11, 77, 4, 177, 13, 3, 65, 76, 76, 12, 34, 79, 185, - 13, 3, 69, 78, 68, 6, 183, 13, 88, 46, 100, 6, 79, 82, 78, 69, 82, 32, - 88, 4, 85, 82, 86, 69, 232, 11, 4, 72, 69, 67, 75, 227, 165, 7, 82, 8, - 54, 82, 194, 12, 76, 162, 167, 2, 77, 183, 177, 1, 83, 2, 11, 79, 2, 239, - 176, 5, 84, 30, 50, 32, 137, 2, 7, 68, 32, 67, 82, 79, 83, 83, 26, 66, - 72, 68, 2, 84, 72, 133, 5, 7, 81, 85, 65, 82, 84, 69, 82, 12, 196, 5, 10, - 65, 76, 70, 45, 67, 73, 82, 67, 76, 69, 147, 14, 73, 6, 96, 2, 69, 78, - 29, 18, 82, 69, 69, 45, 81, 85, 65, 82, 84, 69, 82, 32, 67, 73, 82, 67, - 76, 69, 2, 11, 32, 2, 131, 1, 83, 4, 11, 32, 4, 246, 176, 2, 77, 183, - 177, 1, 83, 8, 33, 6, 79, 85, 66, 76, 69, 32, 8, 30, 83, 150, 4, 65, 75, - 87, 2, 209, 196, 8, 3, 84, 82, 65, 6, 32, 3, 73, 82, 67, 151, 9, 79, 4, - 173, 7, 3, 76, 69, 83, 10, 142, 8, 76, 166, 8, 72, 254, 158, 2, 77, 183, - 177, 1, 83, 12, 68, 5, 83, 77, 65, 76, 76, 142, 7, 76, 166, 8, 72, 255, - 158, 2, 77, 5, 11, 32, 2, 227, 36, 68, 6, 181, 6, 4, 69, 65, 75, 83, 12, - 22, 73, 231, 36, 72, 10, 29, 5, 78, 71, 76, 69, 32, 10, 52, 8, 83, 84, - 82, 65, 73, 71, 72, 84, 207, 1, 87, 8, 11, 32, 8, 42, 76, 202, 172, 2, - 77, 183, 177, 1, 83, 4, 25, 4, 65, 82, 71, 69, 5, 147, 207, 8, 83, 8, 26, - 65, 74, 87, 63, 83, 4, 49, 10, 76, 84, 69, 82, 78, 65, 84, 73, 78, 71, 5, - 17, 2, 32, 87, 2, 29, 5, 82, 73, 83, 84, 32, 2, 165, 196, 7, 2, 70, 76, - 2, 37, 7, 84, 82, 65, 73, 71, 72, 84, 2, 159, 20, 32, 26, 104, 4, 65, 86, - 69, 32, 185, 1, 17, 82, 73, 83, 84, 32, 67, 73, 82, 67, 76, 69, 32, 70, - 82, 79, 78, 84, 22, 108, 6, 67, 85, 82, 86, 69, 32, 140, 1, 13, 68, 73, - 65, 71, 79, 78, 65, 76, 32, 80, 65, 84, 72, 223, 8, 72, 12, 48, 4, 68, - 79, 85, 66, 1, 4, 84, 82, 73, 80, 6, 85, 2, 76, 69, 4, 11, 32, 4, 210, - 30, 68, 43, 83, 6, 29, 5, 73, 71, 90, 65, 71, 6, 11, 32, 6, 42, 76, 162, - 167, 2, 77, 183, 177, 1, 83, 2, 175, 166, 7, 65, 10, 40, 4, 79, 83, 69, - 32, 135, 182, 7, 69, 8, 26, 67, 46, 78, 35, 87, 2, 11, 79, 2, 217, 214, - 7, 3, 78, 84, 65, 2, 221, 219, 7, 3, 69, 85, 84, 4, 44, 3, 82, 73, 78, - 241, 157, 1, 2, 73, 71, 2, 151, 167, 5, 75, 72, 56, 7, 79, 84, 65, 84, - 73, 79, 78, 225, 22, 2, 85, 66, 66, 60, 10, 32, 77, 79, 68, 73, 70, 73, - 69, 82, 45, 155, 1, 45, 30, 82, 49, 250, 226, 8, 50, 2, 51, 2, 52, 2, 53, - 2, 54, 2, 55, 2, 56, 3, 57, 14, 246, 226, 8, 48, 2, 49, 2, 50, 2, 51, 2, - 52, 2, 53, 3, 54, 36, 104, 11, 70, 76, 79, 79, 82, 80, 76, 65, 78, 69, - 32, 133, 2, 10, 87, 65, 76, 76, 80, 76, 65, 78, 69, 32, 18, 100, 4, 68, - 79, 85, 66, 0, 4, 83, 73, 78, 71, 21, 11, 65, 76, 84, 69, 82, 78, 65, 84, - 73, 78, 71, 6, 17, 2, 76, 69, 7, 45, 9, 32, 72, 73, 84, 84, 73, 78, 71, - 32, 4, 32, 2, 67, 69, 33, 2, 70, 76, 2, 11, 73, 2, 231, 142, 8, 76, 2, - 239, 166, 8, 79, 18, 88, 8, 65, 76, 84, 69, 82, 78, 65, 84, 44, 4, 68, - 79, 85, 66, 1, 4, 83, 73, 78, 71, 6, 72, 4, 73, 78, 71, 32, 159, 222, 8, - 69, 6, 17, 2, 76, 69, 7, 11, 32, 4, 11, 72, 4, 11, 73, 4, 33, 6, 84, 84, - 73, 78, 71, 32, 4, 44, 2, 67, 72, 21, 5, 70, 82, 79, 78, 84, 2, 179, 243, - 4, 69, 2, 205, 213, 6, 2, 32, 87, 30, 172, 1, 8, 72, 79, 85, 76, 68, 69, - 82, 32, 196, 1, 7, 81, 85, 69, 69, 90, 69, 32, 240, 1, 7, 85, 82, 70, 65, - 67, 69, 32, 240, 10, 5, 84, 82, 73, 75, 69, 243, 129, 5, 69, 6, 100, 4, - 72, 73, 80, 32, 129, 246, 3, 15, 84, 73, 76, 84, 73, 78, 71, 32, 70, 82, - 79, 77, 32, 87, 65, 4, 48, 4, 80, 79, 83, 73, 253, 192, 7, 2, 83, 80, 2, - 11, 84, 2, 205, 150, 5, 2, 73, 79, 12, 48, 6, 70, 76, 73, 67, 75, 32, 18, - 76, 35, 83, 2, 211, 16, 65, 4, 129, 1, 4, 65, 82, 71, 69, 6, 34, 69, 65, - 4, 77, 65, 76, 76, 2, 17, 2, 81, 85, 2, 21, 3, 69, 78, 84, 2, 151, 207, - 7, 73, 4, 11, 32, 4, 214, 11, 77, 187, 4, 83, 4, 22, 83, 135, 11, 66, 2, - 249, 207, 1, 4, 89, 77, 66, 79, 82, 58, 69, 218, 2, 79, 137, 8, 6, 82, - 65, 86, 69, 76, 45, 20, 76, 3, 69, 84, 72, 185, 1, 11, 78, 83, 69, 32, - 67, 72, 69, 69, 75, 83, 32, 15, 11, 32, 12, 56, 3, 79, 78, 32, 86, 77, - 177, 5, 4, 66, 73, 84, 69, 8, 56, 4, 76, 73, 80, 83, 1, 6, 84, 79, 78, - 71, 85, 69, 5, 11, 32, 2, 11, 77, 2, 201, 194, 3, 2, 79, 86, 6, 50, 77, - 160, 176, 4, 2, 72, 73, 167, 138, 3, 76, 2, 213, 163, 3, 2, 73, 68, 28, - 76, 5, 78, 71, 85, 69, 32, 196, 4, 4, 82, 83, 79, 45, 129, 2, 2, 85, 67, - 16, 192, 2, 7, 67, 69, 78, 84, 82, 69, 32, 52, 14, 73, 78, 83, 73, 68, - 69, 32, 77, 79, 85, 84, 72, 32, 82, 36, 4, 84, 73, 80, 32, 88, 7, 76, 73, - 67, 75, 73, 78, 71, 208, 174, 7, 14, 83, 84, 73, 67, 75, 73, 78, 71, 32, - 79, 85, 84, 32, 70, 149, 158, 1, 17, 77, 79, 86, 69, 83, 32, 65, 71, 65, - 73, 78, 83, 84, 32, 67, 72, 69, 4, 214, 1, 73, 181, 180, 3, 5, 83, 84, - 73, 67, 75, 2, 181, 197, 3, 4, 69, 76, 65, 88, 4, 84, 7, 66, 69, 84, 87, - 69, 69, 78, 41, 10, 84, 79, 85, 67, 72, 73, 78, 71, 32, 73, 2, 11, 32, 2, - 173, 155, 5, 2, 76, 73, 2, 173, 174, 5, 5, 78, 83, 73, 68, 69, 6, 120, - 10, 87, 65, 76, 76, 80, 76, 65, 78, 69, 32, 253, 199, 3, 14, 70, 76, 79, - 79, 82, 80, 76, 65, 78, 69, 32, 84, 87, 73, 4, 108, 8, 67, 85, 82, 86, - 69, 68, 32, 66, 161, 204, 1, 13, 83, 84, 82, 65, 73, 71, 72, 84, 32, 83, - 84, 82, 69, 2, 211, 191, 7, 69, 6, 11, 72, 6, 11, 32, 6, 30, 66, 34, 77, - 187, 4, 83, 2, 137, 249, 6, 3, 69, 84, 87, 2, 149, 2, 3, 85, 76, 84, 34, - 100, 11, 70, 76, 79, 79, 82, 80, 76, 65, 78, 69, 32, 29, 10, 87, 65, 76, - 76, 80, 76, 65, 78, 69, 32, 14, 178, 1, 82, 151, 2, 83, 20, 72, 11, 65, - 82, 77, 32, 83, 80, 73, 82, 65, 76, 32, 78, 82, 151, 2, 83, 6, 30, 84, - 134, 2, 68, 43, 83, 2, 11, 82, 2, 11, 73, 2, 147, 166, 7, 80, 12, 41, 8, - 79, 84, 65, 84, 73, 79, 78, 45, 12, 52, 5, 70, 76, 79, 79, 82, 1, 4, 87, - 65, 76, 76, 6, 33, 6, 80, 76, 65, 78, 69, 32, 6, 26, 65, 54, 68, 43, 83, - 2, 29, 5, 76, 84, 69, 82, 78, 2, 203, 194, 3, 65, 2, 17, 2, 79, 85, 2, - 147, 164, 7, 66, 2, 231, 163, 7, 73, 2, 11, 72, 2, 203, 192, 3, 65, 2, - 11, 73, 2, 187, 179, 7, 78, 10, 100, 6, 65, 66, 79, 86, 69, 32, 162, 1, - 79, 213, 148, 2, 10, 77, 73, 78, 85, 83, 32, 83, 73, 77, 73, 4, 60, 7, - 71, 82, 69, 65, 84, 69, 82, 1, 4, 76, 69, 83, 83, 2, 37, 7, 45, 84, 72, - 65, 78, 32, 65, 2, 25, 4, 66, 79, 86, 69, 2, 161, 241, 1, 2, 32, 69, 4, - 147, 179, 6, 82, 250, 1, 68, 3, 71, 76, 69, 212, 4, 5, 72, 65, 76, 65, - 32, 143, 161, 5, 69, 20, 50, 32, 185, 195, 8, 6, 45, 83, 72, 73, 70, 84, - 16, 138, 1, 67, 0, 9, 71, 82, 65, 80, 72, 73, 67, 32, 67, 116, 2, 72, 73, - 74, 76, 36, 4, 82, 73, 71, 72, 141, 244, 4, 4, 83, 72, 73, 70, 2, 41, 8, - 72, 65, 82, 65, 67, 84, 69, 82, 2, 37, 7, 32, 73, 78, 84, 82, 79, 68, 2, - 11, 85, 2, 223, 132, 8, 67, 2, 25, 4, 71, 72, 45, 82, 2, 185, 1, 7, 69, - 86, 69, 82, 83, 69, 68, 4, 32, 2, 69, 70, 109, 2, 79, 87, 2, 49, 10, 84, - 45, 80, 79, 73, 78, 84, 73, 78, 71, 2, 21, 3, 32, 65, 78, 2, 17, 2, 71, - 76, 2, 31, 69, 2, 17, 2, 45, 57, 2, 11, 32, 2, 11, 81, 2, 233, 176, 2, 2, - 85, 79, 228, 1, 168, 2, 8, 65, 82, 67, 72, 65, 73, 67, 32, 224, 1, 15, - 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 114, 76, 188, - 8, 5, 83, 73, 71, 78, 32, 144, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, - 78, 32, 177, 227, 2, 17, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, - 75, 85, 78, 68, 68, 40, 46, 68, 109, 7, 78, 85, 77, 66, 69, 82, 32, 18, - 25, 4, 73, 71, 73, 84, 18, 11, 32, 18, 234, 204, 6, 70, 30, 83, 42, 84, - 170, 86, 78, 14, 79, 223, 110, 69, 22, 226, 232, 2, 79, 146, 229, 3, 69, - 30, 70, 42, 78, 38, 83, 39, 84, 6, 18, 82, 63, 89, 4, 56, 6, 65, 75, 65, - 65, 82, 65, 209, 184, 8, 2, 69, 80, 2, 205, 184, 8, 3, 65, 78, 83, 138, - 1, 60, 6, 69, 84, 84, 69, 82, 32, 233, 186, 2, 3, 73, 84, 72, 118, 246, - 1, 65, 106, 69, 34, 73, 62, 85, 34, 77, 220, 1, 4, 68, 65, 78, 84, 58, - 79, 28, 8, 83, 65, 78, 89, 65, 75, 65, 32, 50, 70, 2, 72, 2, 82, 2, 86, - 2, 89, 32, 8, 84, 65, 65, 76, 85, 74, 65, 32, 29, 9, 75, 65, 78, 84, 65, - 74, 65, 32, 78, 34, 102, 69, 204, 1, 2, 76, 80, 144, 2, 5, 77, 66, 65, - 32, 66, 14, 65, 2, 73, 2, 85, 151, 253, 4, 89, 4, 230, 3, 69, 151, 253, - 4, 89, 12, 46, 76, 2, 82, 154, 3, 73, 151, 253, 4, 89, 4, 11, 85, 4, 138, - 3, 85, 151, 253, 4, 89, 28, 42, 65, 177, 1, 5, 85, 85, 82, 68, 72, 22, - 32, 2, 72, 65, 179, 255, 4, 89, 20, 41, 8, 65, 80, 82, 65, 65, 78, 65, - 32, 20, 70, 84, 138, 1, 68, 22, 66, 2, 67, 2, 71, 2, 74, 2, 75, 3, 80, 4, - 154, 1, 84, 15, 65, 6, 25, 4, 65, 74, 65, 32, 6, 102, 76, 2, 78, 3, 83, - 4, 86, 79, 151, 253, 4, 89, 8, 26, 68, 22, 71, 3, 74, 4, 18, 68, 15, 65, - 2, 11, 65, 2, 147, 253, 4, 89, 6, 26, 78, 21, 2, 83, 65, 2, 89, 2, 65, - 65, 4, 68, 11, 78, 89, 79, 79, 71, 65, 32, 78, 65, 65, 75, 139, 252, 4, - 89, 2, 149, 176, 8, 4, 83, 73, 75, 89, 8, 66, 65, 218, 157, 4, 67, 217, - 145, 4, 6, 86, 73, 83, 65, 82, 71, 4, 192, 116, 5, 76, 45, 76, 65, 75, - 237, 186, 7, 6, 78, 85, 83, 86, 65, 82, 34, 56, 5, 68, 73, 71, 65, 32, - 62, 71, 82, 75, 155, 2, 65, 12, 58, 71, 48, 4, 75, 79, 77, 66, 118, 65, - 26, 73, 19, 80, 4, 11, 65, 4, 236, 2, 3, 69, 84, 84, 67, 89, 2, 11, 85, - 2, 223, 173, 8, 86, 16, 52, 5, 69, 84, 84, 73, 32, 85, 4, 79, 77, 66, 85, - 6, 26, 65, 26, 73, 19, 80, 2, 213, 1, 2, 69, 68, 2, 203, 1, 83, 2, 175, - 1, 65, 10, 40, 2, 86, 65, 137, 128, 8, 2, 32, 68, 9, 29, 5, 32, 72, 65, - 65, 32, 6, 62, 65, 0, 6, 68, 73, 71, 65, 32, 65, 85, 3, 71, 65, 89, 2, - 17, 2, 69, 76, 2, 11, 65, 2, 17, 2, 45, 80, 2, 11, 73, 2, 147, 139, 8, - 76, 2, 189, 253, 5, 5, 65, 78, 85, 75, 73, 12, 84, 2, 32, 80, 206, 2, 45, - 205, 149, 6, 10, 84, 69, 69, 78, 32, 80, 79, 73, 78, 84, 8, 140, 1, 23, - 69, 84, 65, 76, 76, 69, 68, 32, 66, 76, 65, 67, 75, 32, 65, 78, 68, 32, - 87, 72, 73, 84, 69, 49, 7, 79, 73, 78, 84, 69, 68, 32, 2, 25, 4, 32, 70, - 76, 79, 2, 223, 227, 6, 82, 6, 78, 80, 218, 147, 6, 66, 217, 132, 1, 9, - 83, 84, 65, 82, 32, 87, 73, 84, 72, 2, 21, 3, 73, 78, 87, 2, 217, 147, 6, - 4, 72, 69, 69, 76, 2, 139, 208, 3, 80, 12, 46, 73, 102, 85, 153, 145, 7, - 3, 65, 84, 69, 4, 56, 8, 32, 65, 78, 68, 32, 83, 75, 73, 243, 239, 7, 69, - 2, 17, 2, 32, 66, 2, 255, 215, 7, 79, 6, 32, 2, 76, 76, 235, 164, 8, 78, - 5, 11, 32, 2, 49, 10, 65, 78, 68, 32, 67, 82, 79, 83, 83, 66, 2, 11, 79, - 2, 187, 147, 7, 78, 42, 46, 65, 198, 4, 69, 210, 1, 73, 167, 1, 79, 14, - 64, 5, 78, 84, 69, 68, 32, 193, 146, 6, 5, 86, 79, 78, 73, 67, 12, 148, - 1, 12, 69, 81, 85, 65, 76, 32, 84, 79, 32, 79, 82, 32, 229, 1, 19, 78, - 79, 82, 84, 72, 32, 65, 82, 82, 79, 87, 32, 87, 73, 84, 72, 32, 72, 79, - 8, 60, 7, 71, 82, 69, 65, 84, 69, 82, 1, 4, 76, 69, 83, 83, 4, 29, 5, 45, - 84, 72, 65, 78, 5, 11, 32, 2, 25, 4, 87, 73, 84, 72, 2, 25, 4, 32, 68, - 79, 84, 2, 17, 2, 32, 73, 2, 11, 78, 2, 11, 83, 2, 183, 147, 6, 73, 4, - 24, 2, 79, 75, 43, 82, 2, 17, 2, 69, 68, 2, 131, 206, 5, 32, 2, 29, 5, - 73, 90, 79, 78, 84, 2, 11, 65, 2, 199, 238, 5, 76, 12, 80, 2, 69, 80, - 144, 144, 8, 9, 85, 84, 72, 32, 79, 82, 32, 83, 80, 203, 17, 68, 8, 40, - 4, 73, 78, 71, 32, 143, 228, 7, 89, 6, 176, 223, 4, 8, 65, 67, 67, 79, - 77, 77, 79, 68, 234, 174, 2, 83, 139, 86, 70, 6, 76, 9, 67, 69, 32, 79, - 70, 32, 80, 73, 90, 21, 6, 71, 72, 84, 76, 89, 32, 2, 211, 157, 8, 90, 4, - 40, 2, 83, 77, 173, 217, 6, 2, 70, 82, 2, 215, 234, 6, 73, 10, 18, 80, - 75, 84, 6, 152, 222, 4, 9, 73, 78, 71, 32, 76, 65, 82, 71, 69, 139, 193, - 3, 69, 4, 26, 32, 195, 158, 8, 72, 2, 11, 77, 2, 249, 132, 7, 3, 65, 67, - 72, 150, 1, 34, 65, 158, 13, 73, 207, 7, 79, 116, 32, 2, 76, 76, 155, - 142, 7, 83, 114, 30, 32, 141, 12, 2, 69, 82, 110, 194, 2, 65, 48, 3, 66, - 76, 85, 0, 5, 79, 82, 65, 78, 71, 20, 2, 67, 79, 158, 1, 68, 22, 69, 140, - 2, 11, 73, 68, 69, 79, 71, 82, 65, 80, 72, 73, 67, 28, 2, 76, 69, 34, 82, - 218, 3, 83, 22, 84, 36, 2, 85, 80, 56, 4, 86, 69, 69, 32, 136, 253, 1, 3, - 71, 82, 69, 34, 72, 146, 4, 80, 234, 77, 78, 222, 240, 2, 70, 83, 81, 6, - 194, 203, 4, 77, 214, 49, 73, 135, 138, 1, 83, 2, 135, 206, 6, 69, 12, - 68, 7, 78, 84, 65, 73, 78, 83, 32, 202, 130, 2, 77, 167, 199, 5, 76, 6, - 36, 4, 65, 83, 32, 77, 175, 1, 87, 2, 11, 69, 2, 11, 77, 2, 171, 219, 7, - 66, 2, 219, 130, 2, 79, 12, 84, 9, 76, 69, 77, 69, 78, 84, 32, 79, 70, - 162, 130, 2, 81, 42, 88, 175, 216, 2, 77, 7, 17, 2, 32, 87, 4, 25, 4, 73, - 84, 72, 32, 4, 26, 86, 171, 233, 4, 79, 2, 129, 243, 5, 21, 69, 82, 84, - 73, 67, 65, 76, 32, 66, 65, 82, 32, 65, 84, 32, 69, 78, 68, 32, 79, 70, - 2, 221, 196, 5, 2, 32, 67, 8, 130, 1, 70, 247, 132, 2, 83, 40, 96, 3, 73, - 71, 72, 64, 13, 79, 77, 65, 78, 32, 78, 85, 77, 69, 82, 65, 76, 32, 191, - 255, 5, 69, 6, 17, 2, 84, 32, 6, 170, 143, 4, 67, 210, 3, 80, 147, 8, 84, - 32, 62, 69, 50, 70, 62, 79, 46, 84, 166, 163, 6, 83, 211, 86, 78, 4, 26, - 76, 231, 233, 7, 73, 2, 183, 164, 6, 69, 8, 26, 73, 167, 155, 7, 79, 6, - 130, 26, 86, 255, 140, 6, 70, 6, 11, 78, 6, 11, 69, 7, 247, 191, 2, 32, - 8, 42, 87, 190, 163, 6, 72, 231, 159, 1, 69, 4, 26, 69, 231, 146, 8, 79, - 2, 143, 150, 7, 76, 2, 147, 199, 4, 69, 4, 182, 248, 3, 87, 211, 128, 2, - 73, 2, 185, 243, 4, 9, 45, 80, 79, 73, 78, 84, 73, 78, 71, 2, 11, 87, 2, - 21, 3, 73, 84, 72, 2, 11, 32, 2, 163, 166, 4, 85, 4, 29, 5, 32, 84, 72, - 65, 78, 5, 11, 32, 2, 11, 79, 2, 135, 189, 1, 82, 32, 34, 76, 141, 136, - 7, 2, 82, 75, 30, 40, 4, 73, 78, 71, 32, 131, 144, 8, 69, 28, 112, 14, - 67, 65, 84, 32, 70, 65, 67, 69, 32, 87, 73, 84, 72, 32, 41, 10, 70, 65, - 67, 69, 32, 87, 73, 84, 72, 32, 4, 160, 1, 2, 72, 69, 163, 236, 4, 79, - 24, 86, 72, 108, 10, 79, 80, 69, 78, 32, 77, 79, 85, 84, 72, 246, 1, 83, - 203, 219, 2, 84, 6, 34, 69, 54, 79, 231, 234, 7, 65, 2, 165, 146, 5, 8, - 65, 82, 84, 45, 83, 72, 65, 80, 2, 219, 201, 4, 82, 9, 29, 5, 32, 65, 78, - 68, 32, 6, 26, 67, 58, 83, 71, 84, 2, 17, 2, 79, 76, 2, 145, 188, 3, 4, - 68, 32, 83, 87, 2, 11, 77, 2, 11, 73, 2, 11, 76, 2, 217, 144, 5, 3, 73, - 78, 71, 2, 37, 7, 73, 71, 72, 84, 76, 89, 45, 2, 231, 143, 5, 67, 8, 64, - 11, 77, 73, 76, 73, 78, 71, 32, 69, 89, 69, 83, 175, 1, 85, 7, 29, 5, 32, - 65, 78, 68, 32, 4, 52, 4, 72, 65, 78, 68, 57, 5, 84, 72, 82, 69, 69, 2, - 221, 232, 4, 9, 32, 67, 79, 86, 69, 82, 73, 78, 71, 2, 241, 199, 4, 2, - 32, 72, 2, 17, 2, 78, 71, 2, 173, 245, 6, 4, 76, 65, 83, 83, 2, 171, 220, - 4, 75, 16, 42, 65, 32, 2, 69, 69, 21, 2, 79, 87, 4, 142, 255, 6, 73, 227, - 114, 75, 2, 131, 128, 7, 90, 10, 100, 7, 32, 67, 65, 80, 80, 69, 68, 28, - 3, 77, 65, 78, 136, 218, 2, 3, 66, 79, 65, 167, 143, 1, 70, 2, 137, 159, - 4, 2, 32, 77, 5, 49, 10, 32, 87, 73, 84, 72, 79, 85, 84, 32, 83, 2, 179, - 236, 6, 78, 155, 3, 162, 2, 67, 48, 2, 70, 84, 228, 1, 6, 71, 68, 73, 65, - 78, 32, 244, 10, 3, 76, 73, 68, 140, 2, 12, 79, 78, 32, 87, 73, 84, 72, - 32, 82, 73, 71, 72, 20, 11, 82, 65, 32, 83, 79, 77, 80, 69, 78, 71, 32, - 234, 2, 85, 216, 8, 6, 89, 79, 77, 66, 79, 32, 182, 226, 6, 77, 130, 135, - 1, 72, 3, 83, 4, 232, 227, 2, 3, 67, 69, 82, 215, 226, 4, 75, 10, 70, 32, - 124, 9, 87, 65, 82, 69, 45, 70, 85, 78, 67, 223, 250, 5, 66, 6, 58, 72, - 44, 6, 73, 67, 69, 32, 67, 82, 239, 188, 6, 83, 2, 11, 89, 2, 11, 80, 2, - 231, 174, 6, 72, 2, 191, 199, 6, 69, 2, 11, 84, 2, 203, 138, 6, 73, 84, - 248, 1, 10, 67, 79, 77, 66, 73, 78, 73, 78, 71, 32, 168, 2, 13, 73, 78, - 68, 69, 80, 69, 78, 68, 69, 78, 84, 32, 83, 20, 7, 76, 69, 84, 84, 69, - 82, 32, 188, 3, 7, 78, 85, 77, 66, 69, 82, 32, 113, 12, 80, 85, 78, 67, - 84, 85, 65, 84, 73, 79, 78, 32, 22, 140, 1, 3, 72, 79, 79, 20, 4, 76, 79, - 78, 71, 50, 83, 58, 84, 142, 227, 3, 68, 176, 237, 1, 4, 82, 69, 83, 72, - 129, 15, 4, 67, 85, 82, 86, 4, 195, 224, 5, 75, 2, 25, 4, 32, 72, 79, 79, - 2, 135, 209, 5, 75, 2, 21, 3, 84, 82, 79, 2, 11, 75, 2, 207, 208, 5, 69, - 4, 193, 223, 1, 2, 87, 79, 2, 143, 254, 6, 72, 42, 190, 1, 65, 50, 71, - 34, 76, 56, 5, 82, 69, 83, 72, 45, 2, 90, 34, 83, 66, 89, 158, 208, 1, - 72, 234, 5, 75, 198, 75, 66, 2, 70, 178, 216, 4, 78, 134, 2, 84, 2, 87, - 218, 103, 80, 171, 4, 77, 4, 26, 76, 163, 252, 6, 89, 2, 219, 215, 1, 69, - 2, 11, 73, 2, 239, 238, 2, 77, 4, 26, 65, 135, 147, 6, 69, 2, 193, 209, - 1, 2, 77, 69, 2, 11, 65, 2, 167, 251, 6, 89, 6, 26, 65, 251, 250, 6, 72, - 4, 182, 200, 1, 77, 139, 133, 6, 68, 2, 199, 208, 1, 79, 8, 18, 79, 59, - 84, 4, 11, 78, 4, 11, 69, 5, 11, 32, 2, 167, 166, 2, 72, 4, 182, 140, 6, - 87, 179, 157, 1, 69, 10, 66, 67, 0, 6, 72, 65, 76, 70, 32, 67, 53, 4, 84, - 87, 79, 32, 2, 21, 3, 73, 82, 67, 2, 173, 236, 4, 2, 76, 69, 6, 100, 13, - 86, 69, 82, 84, 73, 67, 65, 76, 32, 66, 65, 82, 83, 13, 8, 67, 73, 82, - 67, 76, 69, 83, 32, 5, 11, 32, 2, 249, 166, 6, 4, 87, 73, 84, 72, 8, 30, - 32, 189, 1, 2, 85, 83, 4, 93, 21, 81, 85, 73, 76, 84, 32, 83, 81, 85, 65, - 82, 69, 32, 79, 82, 78, 65, 77, 69, 78, 84, 5, 11, 32, 2, 11, 73, 2, 17, - 2, 78, 32, 2, 11, 66, 2, 233, 212, 6, 4, 76, 65, 67, 75, 5, 145, 199, 4, - 7, 32, 87, 73, 84, 72, 32, 79, 2, 179, 251, 3, 84, 70, 52, 7, 76, 69, 84, - 84, 69, 82, 32, 159, 131, 6, 68, 50, 198, 1, 69, 32, 2, 77, 65, 30, 78, - 186, 146, 1, 66, 2, 67, 2, 68, 2, 71, 2, 72, 2, 74, 2, 75, 2, 76, 2, 80, - 2, 82, 2, 83, 2, 84, 2, 86, 2, 89, 242, 222, 6, 65, 2, 73, 2, 79, 3, 85, - 4, 226, 241, 7, 69, 147, 1, 72, 4, 210, 242, 7, 69, 3, 72, 6, 182, 146, - 1, 71, 2, 89, 243, 222, 6, 65, 58, 104, 3, 84, 72, 32, 169, 160, 6, 17, + 76, 80, 76, 65, 78, 69, 32, 8, 82, 83, 207, 45, 67, 10, 18, 67, 43, 83, + 4, 254, 45, 85, 213, 95, 3, 73, 82, 67, 6, 37, 7, 84, 82, 65, 73, 71, 72, + 84, 7, 11, 32, 4, 202, 163, 1, 65, 55, 68, 6, 130, 51, 68, 180, 173, 4, + 4, 70, 76, 85, 84, 139, 154, 5, 85, 14, 112, 5, 72, 65, 76, 70, 32, 26, + 67, 28, 4, 87, 73, 68, 69, 230, 92, 79, 233, 135, 4, 6, 83, 81, 85, 69, + 69, 90, 4, 22, 67, 131, 93, 79, 2, 213, 232, 2, 2, 76, 79, 4, 162, 67, + 32, 217, 61, 4, 78, 73, 78, 71, 38, 204, 1, 28, 65, 67, 69, 32, 68, 73, + 82, 69, 67, 84, 73, 79, 78, 32, 80, 79, 83, 73, 84, 73, 79, 78, 32, 78, + 79, 83, 69, 32, 138, 1, 73, 82, 76, 176, 1, 8, 79, 82, 69, 72, 69, 65, + 68, 32, 187, 158, 7, 85, 6, 88, 10, 85, 80, 32, 79, 82, 32, 68, 79, 87, + 78, 13, 8, 70, 79, 82, 87, 65, 82, 68, 32, 5, 11, 32, 2, 153, 231, 4, 3, + 84, 73, 76, 12, 180, 225, 3, 11, 76, 76, 32, 77, 79, 68, 73, 70, 73, 69, + 82, 131, 195, 2, 78, 12, 44, 4, 73, 67, 75, 32, 29, 3, 79, 79, 82, 10, + 254, 140, 1, 76, 35, 83, 2, 225, 247, 8, 20, 80, 76, 65, 78, 69, 32, 83, + 72, 79, 85, 76, 68, 69, 82, 32, 72, 73, 80, 32, 77, 6, 166, 89, 87, 222, + 38, 67, 47, 78, 156, 4, 34, 65, 133, 75, 3, 69, 65, 68, 140, 4, 36, 3, + 78, 68, 45, 143, 186, 9, 73, 138, 4, 92, 5, 65, 78, 71, 76, 69, 138, 5, + 67, 150, 10, 70, 186, 42, 72, 241, 12, 4, 79, 86, 65, 76, 37, 11, 32, 34, + 188, 1, 5, 73, 78, 68, 69, 88, 176, 1, 7, 76, 73, 84, 84, 76, 69, 32, + 136, 1, 5, 82, 73, 78, 71, 32, 173, 66, 18, 77, 73, 68, 68, 76, 69, 32, + 82, 73, 78, 71, 32, 76, 73, 84, 84, 76, 69, 17, 11, 32, 14, 128, 1, 7, + 77, 73, 68, 68, 76, 69, 32, 228, 24, 11, 82, 73, 78, 71, 32, 76, 73, 84, + 84, 76, 69, 241, 42, 5, 84, 72, 85, 77, 66, 4, 174, 70, 76, 247, 215, 8, + 82, 8, 44, 5, 73, 78, 68, 69, 88, 207, 238, 9, 85, 7, 145, 24, 18, 32, + 84, 72, 85, 77, 66, 32, 73, 78, 68, 69, 88, 32, 84, 72, 85, 77, 66, 4, + 108, 22, 68, 79, 87, 78, 32, 77, 73, 68, 68, 76, 69, 32, 84, 72, 85, 77, + 66, 32, 73, 78, 68, 69, 155, 68, 76, 2, 207, 169, 8, 88, 88, 64, 5, 73, + 82, 67, 76, 69, 184, 3, 3, 76, 65, 87, 183, 2, 85, 35, 11, 32, 32, 116, + 5, 73, 78, 68, 69, 88, 200, 1, 7, 76, 73, 84, 84, 76, 69, 32, 36, 7, 77, + 73, 68, 68, 76, 69, 32, 163, 64, 82, 21, 11, 32, 18, 72, 6, 77, 73, 68, + 68, 76, 69, 198, 26, 72, 242, 38, 82, 131, 230, 8, 66, 13, 11, 32, 10, + 64, 5, 67, 82, 79, 83, 83, 198, 64, 84, 82, 76, 247, 215, 8, 82, 4, 134, + 65, 32, 231, 226, 8, 69, 4, 194, 193, 8, 73, 159, 168, 1, 85, 6, 252, 47, + 10, 82, 73, 78, 71, 32, 76, 73, 84, 84, 76, 191, 185, 9, 85, 17, 11, 32, + 14, 144, 2, 28, 77, 73, 68, 68, 76, 69, 32, 82, 73, 78, 71, 32, 76, 73, + 84, 84, 76, 69, 32, 67, 79, 78, 74, 79, 73, 78, 69, 68, 176, 49, 2, 70, + 79, 198, 11, 78, 162, 1, 84, 197, 118, 23, 73, 78, 68, 69, 88, 32, 84, + 72, 85, 77, 66, 32, 67, 85, 82, 86, 69, 32, 84, 72, 85, 77, 66, 5, 247, + 180, 1, 32, 38, 46, 80, 149, 2, 6, 82, 76, 73, 67, 85, 69, 31, 11, 32, + 28, 188, 1, 5, 73, 78, 68, 69, 88, 56, 19, 70, 73, 86, 69, 32, 70, 73, + 78, 71, 69, 82, 83, 32, 83, 80, 82, 69, 65, 68, 196, 50, 7, 77, 73, 68, + 68, 76, 69, 32, 18, 79, 218, 7, 78, 163, 1, 84, 9, 11, 32, 6, 40, 5, 84, + 72, 85, 77, 66, 243, 58, 82, 5, 215, 46, 32, 9, 11, 32, 6, 72, 5, 73, 78, + 68, 69, 88, 0, 6, 77, 73, 68, 68, 76, 69, 179, 71, 79, 2, 193, 147, 9, + 13, 32, 82, 73, 78, 71, 32, 76, 73, 84, 84, 76, 69, 32, 172, 2, 44, 3, + 73, 83, 84, 177, 33, 3, 76, 65, 84, 247, 1, 11, 32, 244, 1, 160, 2, 5, + 73, 78, 68, 69, 88, 192, 16, 7, 76, 73, 84, 84, 76, 69, 32, 196, 2, 7, + 77, 73, 68, 68, 76, 69, 32, 200, 4, 5, 82, 73, 78, 71, 32, 132, 2, 5, 84, + 72, 85, 77, 66, 138, 2, 72, 205, 9, 22, 70, 79, 85, 82, 32, 70, 73, 78, + 71, 69, 82, 83, 32, 67, 79, 78, 74, 79, 73, 78, 69, 68, 137, 1, 11, 32, + 134, 1, 232, 1, 4, 66, 69, 78, 84, 36, 6, 72, 73, 78, 71, 69, 68, 76, 6, + 77, 73, 68, 68, 76, 69, 168, 7, 2, 67, 85, 64, 6, 84, 72, 85, 77, 66, 32, + 160, 5, 16, 85, 80, 32, 77, 73, 68, 68, 76, 69, 32, 72, 73, 78, 71, 69, + 68, 151, 4, 82, 5, 217, 45, 5, 32, 79, 86, 69, 82, 9, 11, 32, 6, 252, 20, + 8, 77, 73, 68, 68, 76, 69, 32, 85, 167, 172, 8, 76, 71, 11, 32, 68, 236, + 1, 4, 66, 69, 78, 84, 42, 67, 244, 1, 10, 85, 80, 32, 83, 80, 82, 69, 65, + 68, 32, 100, 6, 72, 73, 78, 71, 69, 68, 46, 82, 80, 5, 84, 72, 85, 77, + 66, 188, 28, 14, 83, 84, 82, 65, 73, 71, 72, 84, 32, 84, 72, 85, 77, 66, + 227, 17, 76, 5, 213, 92, 6, 32, 84, 72, 85, 77, 66, 28, 68, 8, 79, 78, + 74, 79, 73, 78, 69, 68, 233, 1, 4, 82, 79, 83, 83, 23, 11, 32, 20, 144, + 1, 6, 67, 85, 80, 80, 69, 68, 28, 6, 84, 72, 85, 77, 66, 32, 68, 5, 72, + 73, 78, 71, 69, 166, 22, 73, 165, 7, 6, 77, 73, 68, 68, 76, 69, 5, 11, + 32, 2, 203, 27, 84, 8, 160, 1, 4, 83, 73, 68, 69, 219, 61, 70, 6, 22, 69, + 159, 47, 32, 4, 223, 15, 68, 5, 241, 30, 7, 32, 83, 80, 82, 69, 65, 68, + 8, 32, 3, 73, 78, 71, 247, 19, 65, 7, 11, 32, 4, 138, 36, 67, 131, 240, + 8, 66, 19, 11, 32, 16, 64, 6, 65, 78, 71, 76, 69, 68, 22, 67, 106, 72, + 163, 146, 9, 66, 5, 167, 221, 5, 32, 6, 74, 85, 230, 7, 73, 241, 25, 10, + 79, 78, 74, 79, 73, 78, 69, 68, 32, 72, 2, 201, 193, 4, 2, 80, 80, 4, + 194, 33, 73, 217, 26, 2, 79, 79, 40, 164, 1, 7, 65, 78, 71, 76, 69, 68, + 32, 46, 67, 220, 1, 14, 70, 79, 82, 87, 65, 82, 68, 32, 73, 78, 68, 69, + 88, 32, 32, 4, 72, 79, 79, 75, 53, 4, 83, 73, 68, 69, 4, 128, 1, 2, 73, + 78, 1, 3, 79, 85, 84, 12, 36, 5, 73, 82, 67, 76, 69, 15, 85, 5, 47, 68, + 8, 32, 4, 80, 80, 69, 68, 39, 82, 2, 225, 40, 5, 32, 77, 73, 68, 68, 6, + 56, 9, 86, 69, 32, 84, 72, 85, 77, 66, 32, 143, 37, 76, 4, 162, 160, 1, + 73, 163, 198, 4, 85, 4, 202, 84, 83, 131, 186, 8, 66, 7, 157, 8, 9, 69, + 68, 32, 77, 73, 68, 68, 76, 69, 15, 11, 32, 12, 92, 6, 73, 78, 68, 69, + 88, 32, 156, 13, 5, 84, 72, 85, 77, 66, 193, 8, 4, 66, 79, 84, 72, 4, 26, + 72, 243, 140, 9, 66, 2, 215, 152, 8, 73, 7, 37, 7, 32, 84, 72, 85, 77, + 66, 32, 4, 178, 28, 67, 207, 129, 1, 83, 22, 88, 4, 68, 79, 87, 78, 186, + 1, 84, 158, 6, 82, 130, 21, 73, 230, 238, 8, 66, 159, 67, 85, 9, 11, 32, + 6, 80, 9, 79, 84, 72, 69, 82, 83, 32, 67, 73, 25, 7, 82, 73, 80, 80, 76, + 69, 32, 2, 225, 51, 2, 82, 67, 4, 22, 67, 171, 80, 83, 2, 11, 85, 2, 169, + 185, 4, 2, 82, 86, 4, 196, 35, 6, 79, 85, 67, 72, 69, 83, 39, 72, 30, + 134, 1, 82, 28, 6, 84, 72, 85, 77, 66, 32, 138, 3, 85, 134, 1, 68, 210, + 30, 76, 189, 128, 8, 9, 66, 69, 78, 84, 32, 79, 86, 69, 82, 4, 238, 4, + 65, 231, 29, 73, 16, 80, 7, 65, 78, 71, 76, 69, 68, 32, 126, 67, 124, 4, + 72, 79, 79, 75, 147, 32, 76, 6, 60, 10, 79, 85, 84, 32, 73, 78, 68, 69, + 88, 32, 215, 1, 73, 4, 26, 67, 155, 202, 9, 85, 2, 249, 185, 2, 3, 82, + 79, 83, 6, 76, 12, 73, 82, 67, 76, 69, 68, 32, 73, 78, 68, 69, 88, 45, 3, + 85, 80, 80, 4, 11, 32, 4, 150, 21, 72, 135, 180, 9, 85, 2, 25, 4, 69, 68, + 32, 73, 2, 233, 30, 4, 78, 68, 69, 88, 4, 11, 80, 5, 175, 15, 32, 18, + 102, 68, 20, 6, 77, 73, 68, 68, 76, 69, 42, 82, 198, 29, 84, 82, 76, 210, + 128, 8, 73, 159, 168, 1, 85, 2, 151, 241, 7, 79, 7, 11, 32, 4, 206, 3, + 82, 179, 16, 67, 2, 11, 65, 2, 37, 7, 73, 83, 69, 68, 32, 75, 78, 2, 11, + 85, 2, 11, 67, 2, 131, 160, 8, 75, 35, 11, 32, 32, 148, 1, 8, 66, 69, 84, + 87, 69, 69, 78, 32, 102, 72, 20, 5, 79, 86, 69, 82, 32, 120, 4, 83, 73, + 68, 69, 100, 6, 85, 78, 68, 69, 82, 32, 207, 40, 70, 8, 80, 12, 73, 78, + 68, 69, 88, 32, 77, 73, 68, 68, 76, 69, 246, 20, 77, 155, 6, 82, 5, 135, + 70, 32, 2, 243, 174, 4, 69, 4, 52, 6, 70, 79, 85, 82, 32, 82, 161, 2, 2, + 84, 87, 2, 11, 65, 2, 233, 80, 9, 73, 83, 69, 68, 32, 75, 78, 85, 67, 6, + 11, 32, 6, 38, 68, 190, 15, 67, 131, 240, 8, 66, 2, 25, 4, 73, 65, 71, + 79, 2, 235, 182, 8, 78, 10, 54, 73, 34, 84, 48, 4, 70, 79, 85, 82, 131, + 23, 76, 2, 161, 7, 4, 78, 68, 69, 88, 4, 34, 87, 13, 4, 72, 82, 69, 69, + 2, 11, 79, 2, 173, 176, 8, 5, 32, 70, 73, 78, 71, 55, 11, 32, 52, 194, 1, + 70, 172, 3, 4, 72, 69, 69, 76, 192, 1, 6, 83, 80, 76, 73, 84, 32, 236, 1, + 6, 84, 72, 85, 77, 66, 32, 205, 249, 4, 16, 66, 69, 84, 87, 69, 69, 78, + 32, 80, 65, 76, 77, 32, 70, 65, 67, 24, 140, 1, 18, 73, 86, 69, 32, 70, + 73, 78, 71, 69, 82, 83, 32, 83, 80, 82, 69, 65, 68, 165, 1, 11, 79, 85, + 82, 32, 70, 73, 78, 71, 69, 82, 83, 15, 11, 32, 12, 68, 6, 72, 73, 78, + 71, 69, 68, 42, 84, 186, 2, 70, 203, 247, 8, 66, 7, 11, 32, 4, 190, 4, + 84, 155, 15, 78, 2, 189, 35, 6, 72, 85, 77, 66, 32, 70, 11, 11, 32, 8, + 72, 9, 67, 79, 78, 74, 79, 73, 78, 69, 68, 154, 8, 72, 235, 240, 8, 66, + 5, 225, 159, 9, 3, 32, 83, 80, 11, 11, 32, 8, 96, 19, 70, 73, 86, 69, 32, + 70, 73, 78, 71, 69, 82, 83, 32, 83, 80, 82, 69, 65, 68, 151, 2, 84, 7, + 11, 32, 4, 26, 70, 203, 247, 8, 66, 2, 21, 3, 79, 85, 82, 2, 167, 1, 32, + 10, 72, 6, 67, 69, 78, 84, 82, 69, 96, 5, 73, 78, 68, 69, 88, 167, 16, + 76, 7, 49, 10, 32, 84, 72, 85, 77, 66, 32, 83, 73, 68, 4, 11, 69, 5, 11, + 32, 2, 135, 246, 8, 66, 2, 11, 32, 2, 11, 84, 2, 153, 135, 1, 5, 72, 85, + 77, 66, 32, 6, 242, 30, 70, 142, 104, 83, 183, 238, 7, 66, 82, 48, 4, 73, + 78, 71, 69, 181, 9, 3, 79, 79, 75, 63, 11, 32, 60, 206, 1, 70, 172, 1, 5, + 73, 78, 68, 69, 88, 196, 2, 6, 76, 73, 84, 84, 76, 69, 80, 6, 77, 73, 68, + 68, 76, 69, 30, 79, 64, 4, 82, 73, 78, 71, 124, 6, 84, 72, 85, 77, 66, + 32, 158, 6, 78, 195, 147, 4, 83, 4, 92, 19, 73, 86, 69, 32, 70, 73, 78, + 71, 69, 82, 83, 32, 83, 80, 82, 69, 65, 68, 32, 19, 79, 2, 191, 25, 79, + 2, 249, 1, 11, 85, 82, 32, 70, 73, 78, 71, 69, 82, 83, 32, 23, 11, 32, + 20, 86, 72, 40, 7, 77, 73, 68, 68, 76, 69, 32, 104, 5, 84, 72, 85, 77, + 66, 219, 9, 82, 2, 11, 73, 2, 233, 159, 4, 2, 78, 71, 6, 36, 4, 82, 73, + 78, 71, 203, 10, 76, 5, 11, 32, 2, 11, 67, 2, 21, 3, 79, 78, 74, 2, 175, + 33, 79, 11, 11, 32, 8, 34, 83, 210, 22, 79, 203, 39, 76, 4, 218, 159, 7, + 73, 195, 8, 77, 9, 11, 32, 6, 22, 73, 199, 8, 84, 4, 25, 4, 78, 68, 69, + 88, 5, 155, 8, 32, 5, 11, 32, 2, 171, 8, 82, 8, 21, 3, 80, 69, 78, 9, 11, + 32, 6, 178, 7, 78, 163, 1, 84, 5, 97, 22, 32, 68, 79, 87, 78, 32, 73, 78, + 68, 69, 88, 32, 84, 72, 85, 77, 66, 32, 72, 79, 79, 75, 2, 157, 77, 2, + 32, 77, 6, 68, 9, 66, 69, 84, 87, 69, 69, 78, 32, 77, 53, 4, 83, 73, 68, + 69, 2, 29, 5, 73, 68, 68, 76, 69, 2, 219, 153, 4, 32, 5, 33, 6, 32, 84, + 79, 85, 67, 72, 2, 169, 134, 8, 3, 73, 78, 71, 21, 11, 32, 18, 172, 1, 4, + 67, 85, 82, 76, 28, 18, 73, 78, 68, 69, 88, 32, 82, 73, 78, 71, 32, 76, + 73, 84, 84, 76, 69, 32, 48, 7, 77, 73, 68, 68, 76, 69, 32, 225, 2, 4, 82, + 73, 78, 71, 2, 173, 157, 5, 2, 73, 67, 6, 154, 193, 5, 85, 134, 242, 2, + 79, 243, 41, 73, 8, 104, 21, 82, 73, 78, 71, 32, 76, 73, 84, 84, 76, 69, + 32, 67, 79, 78, 74, 79, 73, 78, 69, 68, 143, 2, 84, 7, 211, 228, 2, 32, + 17, 11, 32, 14, 112, 14, 70, 73, 86, 69, 32, 70, 73, 78, 71, 69, 82, 83, + 32, 83, 22, 76, 66, 78, 70, 82, 94, 84, 167, 128, 8, 73, 2, 219, 160, 2, + 80, 2, 21, 3, 73, 84, 84, 2, 17, 2, 76, 69, 2, 139, 180, 8, 32, 2, 11, + 79, 2, 11, 32, 2, 11, 84, 2, 11, 72, 2, 167, 134, 6, 85, 2, 11, 73, 2, + 21, 3, 78, 71, 32, 2, 11, 76, 2, 11, 73, 2, 11, 84, 2, 163, 130, 8, 84, + 4, 29, 5, 72, 85, 77, 66, 32, 4, 194, 14, 70, 143, 104, 83, 17, 11, 32, + 14, 56, 8, 77, 79, 86, 69, 77, 69, 78, 84, 251, 164, 8, 82, 12, 26, 45, + 139, 211, 7, 32, 10, 100, 11, 70, 76, 79, 79, 82, 80, 76, 65, 78, 69, 32, + 25, 10, 87, 65, 76, 76, 80, 76, 65, 78, 69, 32, 4, 62, 67, 223, 40, 83, + 6, 38, 67, 28, 2, 84, 73, 195, 40, 83, 2, 173, 169, 8, 2, 85, 82, 2, 187, + 137, 9, 76, 38, 50, 73, 225, 3, 7, 79, 67, 65, 84, 73, 79, 78, 22, 32, 3, + 77, 66, 32, 171, 1, 80, 16, 56, 4, 67, 79, 77, 66, 29, 6, 76, 69, 78, 71, + 84, 72, 2, 241, 222, 5, 2, 73, 78, 14, 11, 45, 14, 150, 164, 9, 49, 2, + 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 55, 6, 58, 32, 153, 1, 9, 83, 32, 80, + 82, 69, 83, 83, 69, 68, 4, 116, 12, 76, 79, 87, 69, 82, 32, 79, 86, 69, + 82, 32, 85, 133, 135, 8, 11, 85, 80, 80, 69, 82, 32, 79, 86, 69, 82, 32, + 2, 11, 80, 2, 167, 228, 8, 80, 2, 29, 5, 32, 84, 79, 71, 69, 2, 11, 84, + 2, 231, 227, 8, 72, 16, 22, 32, 203, 1, 45, 12, 148, 1, 2, 72, 69, 240, + 177, 2, 3, 84, 79, 82, 168, 233, 4, 3, 68, 69, 80, 0, 3, 87, 73, 68, 189, + 124, 10, 76, 73, 77, 66, 83, 32, 68, 73, 71, 73, 4, 196, 13, 4, 65, 68, + 32, 78, 135, 232, 8, 73, 4, 52, 5, 70, 76, 79, 79, 82, 1, 4, 87, 65, 76, + 76, 2, 225, 224, 8, 5, 80, 76, 65, 78, 69, 156, 3, 64, 4, 85, 84, 72, 32, + 161, 5, 7, 86, 69, 77, 69, 78, 84, 45, 54, 186, 1, 67, 88, 5, 70, 82, 79, + 87, 78, 0, 5, 83, 77, 73, 76, 69, 56, 4, 75, 73, 83, 83, 36, 5, 79, 80, + 69, 78, 32, 192, 1, 5, 84, 69, 78, 83, 69, 165, 29, 5, 87, 82, 73, 78, + 75, 8, 48, 6, 76, 79, 83, 69, 68, 32, 147, 235, 7, 79, 6, 222, 2, 70, + 142, 38, 67, 47, 78, 7, 11, 32, 4, 22, 79, 203, 1, 87, 2, 251, 197, 7, + 80, 7, 11, 32, 4, 166, 1, 87, 83, 70, 18, 100, 4, 79, 86, 65, 76, 0, 9, + 82, 69, 67, 84, 65, 78, 71, 76, 69, 42, 87, 82, 70, 211, 197, 7, 67, 7, + 11, 32, 4, 26, 87, 255, 195, 7, 89, 2, 25, 4, 82, 73, 78, 75, 2, 131, + 134, 4, 76, 7, 11, 32, 4, 18, 70, 43, 83, 2, 17, 2, 79, 82, 2, 215, 128, + 8, 87, 2, 17, 2, 85, 67, 2, 147, 133, 4, 75, 230, 2, 192, 1, 9, 68, 73, + 65, 71, 79, 78, 65, 76, 32, 148, 1, 11, 70, 76, 79, 79, 82, 80, 76, 65, + 78, 69, 32, 184, 14, 6, 72, 73, 78, 71, 69, 32, 189, 2, 10, 87, 65, 76, + 76, 80, 76, 65, 78, 69, 32, 32, 56, 8, 66, 69, 84, 87, 69, 69, 78, 32, + 22, 65, 31, 84, 16, 18, 65, 31, 84, 8, 229, 28, 3, 87, 65, 89, 8, 201, + 28, 6, 79, 87, 65, 82, 68, 83, 150, 1, 208, 2, 24, 65, 82, 77, 32, 67, + 73, 82, 67, 76, 69, 32, 72, 73, 84, 84, 73, 78, 71, 32, 87, 65, 76, 76, + 32, 38, 66, 34, 67, 224, 1, 8, 70, 73, 78, 71, 69, 82, 32, 67, 52, 5, 72, + 85, 77, 80, 32, 184, 3, 5, 76, 79, 79, 80, 32, 198, 1, 83, 108, 7, 84, + 82, 73, 80, 76, 69, 32, 110, 87, 206, 12, 68, 198, 2, 80, 154, 6, 90, + 231, 246, 7, 74, 12, 178, 7, 76, 154, 9, 77, 39, 83, 8, 150, 17, 79, 151, + 242, 7, 69, 28, 86, 72, 20, 5, 85, 82, 86, 69, 32, 196, 29, 5, 79, 82, + 78, 69, 82, 211, 177, 7, 82, 2, 195, 224, 7, 69, 18, 80, 4, 67, 79, 77, + 66, 158, 8, 72, 230, 15, 76, 194, 172, 2, 77, 247, 182, 1, 83, 2, 11, 73, + 2, 187, 253, 3, 78, 6, 128, 9, 6, 73, 82, 67, 76, 69, 83, 239, 20, 79, + 18, 56, 8, 72, 73, 84, 84, 73, 78, 71, 32, 167, 250, 3, 83, 16, 72, 8, + 67, 69, 73, 76, 73, 78, 71, 32, 101, 6, 70, 76, 79, 79, 82, 32, 8, 56, 5, + 76, 65, 82, 71, 69, 1, 5, 83, 77, 65, 76, 76, 4, 11, 32, 4, 206, 54, 84, + 135, 2, 68, 8, 88, 4, 83, 77, 65, 76, 12, 5, 76, 65, 82, 71, 69, 17, 7, + 84, 82, 73, 80, 76, 69, 32, 2, 11, 76, 2, 255, 18, 32, 4, 56, 5, 76, 65, + 82, 71, 69, 1, 5, 83, 77, 65, 76, 76, 2, 253, 52, 2, 32, 84, 18, 56, 8, + 72, 73, 84, 84, 73, 78, 71, 32, 239, 246, 3, 83, 16, 64, 7, 67, 69, 73, + 76, 73, 78, 71, 1, 5, 70, 76, 79, 79, 82, 8, 11, 32, 8, 22, 76, 191, 9, + 83, 4, 249, 22, 4, 65, 82, 71, 69, 12, 44, 6, 72, 65, 75, 73, 78, 71, + 243, 16, 73, 2, 21, 3, 32, 80, 65, 2, 149, 246, 3, 4, 82, 65, 76, 76, 8, + 76, 12, 65, 76, 84, 69, 82, 78, 65, 84, 73, 78, 71, 32, 138, 18, 87, 63, + 83, 4, 134, 18, 87, 255, 20, 77, 18, 80, 4, 65, 86, 69, 32, 169, 1, 11, + 82, 73, 83, 84, 32, 67, 73, 82, 67, 76, 69, 14, 30, 72, 102, 83, 171, 20, + 76, 8, 37, 7, 73, 84, 84, 73, 78, 71, 32, 8, 252, 2, 4, 67, 69, 73, 76, + 25, 5, 70, 76, 79, 79, 82, 4, 182, 156, 5, 78, 247, 225, 1, 77, 4, 209, + 5, 10, 32, 72, 73, 84, 84, 73, 78, 71, 32, 87, 14, 112, 3, 85, 80, 32, + 184, 1, 6, 68, 79, 87, 78, 32, 83, 197, 235, 5, 10, 83, 73, 68, 69, 32, + 84, 79, 32, 83, 73, 10, 40, 5, 68, 79, 87, 78, 32, 143, 1, 83, 8, 68, 8, + 65, 76, 84, 69, 82, 78, 65, 84, 230, 17, 76, 143, 222, 3, 83, 4, 21, 3, + 73, 78, 71, 4, 11, 32, 4, 190, 17, 76, 143, 222, 3, 83, 2, 187, 30, 69, + 162, 1, 148, 2, 11, 65, 82, 77, 32, 67, 73, 82, 67, 76, 69, 32, 98, 66, + 54, 67, 174, 4, 68, 100, 8, 70, 73, 78, 71, 69, 82, 32, 67, 64, 5, 72, + 85, 77, 80, 32, 60, 5, 76, 79, 79, 80, 32, 102, 80, 34, 83, 216, 1, 7, + 84, 82, 73, 80, 76, 69, 32, 218, 1, 87, 202, 2, 90, 231, 246, 7, 74, 8, + 18, 77, 39, 83, 4, 225, 13, 5, 69, 68, 73, 85, 77, 4, 11, 77, 4, 177, 13, + 3, 65, 76, 76, 12, 34, 79, 185, 13, 3, 69, 78, 68, 6, 183, 13, 88, 46, + 100, 6, 79, 82, 78, 69, 82, 32, 88, 4, 85, 82, 86, 69, 232, 11, 4, 72, + 69, 67, 75, 211, 177, 7, 82, 8, 54, 82, 194, 12, 76, 154, 167, 2, 77, + 247, 182, 1, 83, 2, 11, 79, 2, 159, 187, 5, 84, 30, 50, 32, 137, 2, 7, + 68, 32, 67, 82, 79, 83, 83, 26, 66, 72, 68, 2, 84, 72, 133, 5, 7, 81, 85, + 65, 82, 84, 69, 82, 12, 196, 5, 10, 65, 76, 70, 45, 67, 73, 82, 67, 76, + 69, 147, 14, 73, 6, 96, 2, 69, 78, 29, 18, 82, 69, 69, 45, 81, 85, 65, + 82, 84, 69, 82, 32, 67, 73, 82, 67, 76, 69, 2, 11, 32, 2, 131, 1, 83, 4, + 11, 32, 4, 238, 176, 2, 77, 247, 182, 1, 83, 8, 33, 6, 79, 85, 66, 76, + 69, 32, 8, 30, 83, 150, 4, 65, 75, 87, 2, 213, 210, 8, 3, 84, 82, 65, 6, + 32, 3, 73, 82, 67, 151, 9, 79, 4, 173, 7, 3, 76, 69, 83, 10, 142, 8, 76, + 166, 8, 72, 246, 158, 2, 77, 247, 182, 1, 83, 12, 68, 5, 83, 77, 65, 76, + 76, 142, 7, 76, 166, 8, 72, 247, 158, 2, 77, 5, 11, 32, 2, 207, 36, 68, + 6, 181, 6, 4, 69, 65, 75, 83, 12, 22, 73, 211, 36, 72, 10, 29, 5, 78, 71, + 76, 69, 32, 10, 52, 8, 83, 84, 82, 65, 73, 71, 72, 84, 207, 1, 87, 8, 11, + 32, 8, 42, 76, 194, 172, 2, 77, 247, 182, 1, 83, 4, 25, 4, 65, 82, 71, + 69, 5, 151, 221, 8, 83, 8, 26, 65, 74, 87, 63, 83, 4, 49, 10, 76, 84, 69, + 82, 78, 65, 84, 73, 78, 71, 5, 17, 2, 32, 87, 2, 29, 5, 82, 73, 83, 84, + 32, 2, 149, 208, 7, 2, 70, 76, 2, 37, 7, 84, 82, 65, 73, 71, 72, 84, 2, + 139, 20, 32, 26, 104, 4, 65, 86, 69, 32, 185, 1, 17, 82, 73, 83, 84, 32, + 67, 73, 82, 67, 76, 69, 32, 70, 82, 79, 78, 84, 22, 108, 6, 67, 85, 82, + 86, 69, 32, 140, 1, 13, 68, 73, 65, 71, 79, 78, 65, 76, 32, 80, 65, 84, + 72, 223, 8, 72, 12, 48, 4, 68, 79, 85, 66, 1, 4, 84, 82, 73, 80, 6, 85, + 2, 76, 69, 4, 11, 32, 4, 190, 30, 68, 43, 83, 6, 29, 5, 73, 71, 90, 65, + 71, 6, 11, 32, 6, 42, 76, 154, 167, 2, 77, 247, 182, 1, 83, 2, 159, 178, + 7, 65, 10, 40, 4, 79, 83, 69, 32, 247, 193, 7, 69, 8, 26, 67, 46, 78, 35, + 87, 2, 11, 79, 2, 193, 226, 7, 3, 78, 84, 65, 2, 197, 231, 7, 3, 69, 85, + 84, 4, 44, 3, 82, 73, 78, 237, 157, 1, 2, 73, 71, 2, 199, 177, 5, 75, 72, + 56, 7, 79, 84, 65, 84, 73, 79, 78, 205, 22, 2, 85, 66, 66, 60, 10, 32, + 77, 79, 68, 73, 70, 73, 69, 82, 45, 155, 1, 45, 30, 82, 49, 254, 240, 8, + 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 14, 250, 240, 8, 48, + 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 3, 54, 36, 104, 11, 70, 76, 79, 79, + 82, 80, 76, 65, 78, 69, 32, 133, 2, 10, 87, 65, 76, 76, 80, 76, 65, 78, + 69, 32, 18, 100, 4, 68, 79, 85, 66, 0, 4, 83, 73, 78, 71, 21, 11, 65, 76, + 84, 69, 82, 78, 65, 84, 73, 78, 71, 6, 17, 2, 76, 69, 7, 45, 9, 32, 72, + 73, 84, 84, 73, 78, 71, 32, 4, 32, 2, 67, 69, 33, 2, 70, 76, 2, 11, 73, + 2, 235, 156, 8, 76, 2, 243, 180, 8, 79, 18, 88, 8, 65, 76, 84, 69, 82, + 78, 65, 84, 44, 4, 68, 79, 85, 66, 1, 4, 83, 73, 78, 71, 6, 72, 4, 73, + 78, 71, 32, 163, 236, 8, 69, 6, 17, 2, 76, 69, 7, 11, 32, 4, 11, 72, 4, + 11, 73, 4, 33, 6, 84, 84, 73, 78, 71, 32, 4, 44, 5, 70, 82, 79, 78, 84, + 139, 249, 4, 67, 2, 253, 224, 6, 2, 32, 87, 30, 172, 1, 8, 72, 79, 85, + 76, 68, 69, 82, 32, 196, 1, 7, 81, 85, 69, 69, 90, 69, 32, 240, 1, 7, 85, + 82, 70, 65, 67, 69, 32, 240, 10, 5, 84, 82, 73, 75, 69, 183, 140, 5, 69, + 6, 100, 4, 72, 73, 80, 32, 225, 251, 3, 15, 84, 73, 76, 84, 73, 78, 71, + 32, 70, 82, 79, 77, 32, 87, 65, 4, 48, 4, 80, 79, 83, 73, 145, 205, 7, 2, + 83, 80, 2, 11, 84, 2, 145, 161, 5, 2, 73, 79, 12, 48, 6, 70, 76, 73, 67, + 75, 32, 18, 76, 35, 83, 2, 211, 16, 65, 4, 129, 1, 4, 65, 82, 71, 69, 6, + 34, 69, 65, 4, 77, 65, 76, 76, 2, 17, 2, 81, 85, 2, 21, 3, 69, 78, 84, 2, + 147, 219, 7, 73, 4, 11, 32, 4, 214, 11, 77, 187, 4, 83, 4, 22, 83, 135, + 11, 66, 2, 129, 208, 1, 4, 89, 77, 66, 79, 82, 58, 69, 218, 2, 79, 137, + 8, 6, 82, 65, 86, 69, 76, 45, 20, 76, 3, 69, 84, 72, 185, 1, 11, 78, 83, + 69, 32, 67, 72, 69, 69, 75, 83, 32, 15, 11, 32, 12, 56, 3, 79, 78, 32, + 86, 77, 177, 5, 4, 66, 73, 84, 69, 8, 56, 4, 76, 73, 80, 83, 1, 6, 84, + 79, 78, 71, 85, 69, 5, 11, 32, 2, 11, 77, 2, 149, 200, 3, 2, 79, 86, 6, + 50, 77, 220, 181, 4, 2, 72, 73, 255, 144, 3, 76, 2, 225, 168, 3, 2, 73, + 68, 28, 76, 5, 78, 71, 85, 69, 32, 196, 4, 4, 82, 83, 79, 45, 129, 2, 2, + 85, 67, 16, 192, 2, 7, 67, 69, 78, 84, 82, 69, 32, 52, 14, 73, 78, 83, + 73, 68, 69, 32, 77, 79, 85, 84, 72, 32, 82, 36, 4, 84, 73, 80, 32, 88, 7, + 76, 73, 67, 75, 73, 78, 71, 228, 186, 7, 14, 83, 84, 73, 67, 75, 73, 78, + 71, 32, 79, 85, 84, 32, 70, 153, 160, 1, 17, 77, 79, 86, 69, 83, 32, 65, + 71, 65, 73, 78, 83, 84, 32, 67, 72, 69, 4, 214, 1, 73, 129, 186, 3, 5, + 83, 84, 73, 67, 75, 2, 129, 203, 3, 4, 69, 76, 65, 88, 4, 84, 7, 66, 69, + 84, 87, 69, 69, 78, 41, 10, 84, 79, 85, 67, 72, 73, 78, 71, 32, 73, 2, + 11, 32, 2, 241, 165, 5, 2, 76, 73, 2, 241, 184, 5, 5, 78, 83, 73, 68, 69, + 6, 120, 10, 87, 65, 76, 76, 80, 76, 65, 78, 69, 32, 201, 205, 3, 14, 70, + 76, 79, 79, 82, 80, 76, 65, 78, 69, 32, 84, 87, 73, 4, 108, 8, 67, 85, + 82, 86, 69, 68, 32, 66, 169, 204, 1, 13, 83, 84, 82, 65, 73, 71, 72, 84, + 32, 83, 84, 82, 69, 2, 207, 203, 7, 69, 6, 11, 72, 6, 11, 32, 6, 30, 66, + 34, 77, 187, 4, 83, 2, 133, 133, 7, 3, 69, 84, 87, 2, 149, 2, 3, 85, 76, + 84, 34, 100, 11, 70, 76, 79, 79, 82, 80, 76, 65, 78, 69, 32, 29, 10, 87, + 65, 76, 76, 80, 76, 65, 78, 69, 32, 14, 178, 1, 82, 151, 2, 83, 20, 72, + 11, 65, 82, 77, 32, 83, 80, 73, 82, 65, 76, 32, 78, 82, 151, 2, 83, 6, + 30, 84, 134, 2, 68, 43, 83, 2, 11, 82, 2, 11, 73, 2, 151, 178, 7, 80, 12, + 41, 8, 79, 84, 65, 84, 73, 79, 78, 45, 12, 52, 5, 70, 76, 79, 79, 82, 1, + 4, 87, 65, 76, 76, 6, 33, 6, 80, 76, 65, 78, 69, 32, 6, 26, 65, 54, 68, + 43, 83, 2, 29, 5, 76, 84, 69, 82, 78, 2, 151, 200, 3, 65, 2, 17, 2, 79, + 85, 2, 151, 176, 7, 66, 2, 235, 175, 7, 73, 2, 11, 72, 2, 151, 198, 3, + 65, 2, 11, 73, 2, 195, 205, 7, 78, 10, 100, 6, 65, 66, 79, 86, 69, 32, + 162, 1, 79, 141, 148, 2, 10, 77, 73, 78, 85, 83, 32, 83, 73, 77, 73, 4, + 60, 7, 71, 82, 69, 65, 84, 69, 82, 1, 4, 76, 69, 83, 83, 2, 37, 7, 45, + 84, 72, 65, 78, 32, 65, 2, 25, 4, 66, 79, 86, 69, 2, 185, 241, 1, 2, 32, + 69, 4, 207, 189, 6, 82, 250, 1, 68, 3, 71, 76, 69, 212, 4, 5, 72, 65, 76, + 65, 32, 211, 171, 5, 69, 20, 50, 32, 209, 209, 8, 6, 45, 83, 72, 73, 70, + 84, 16, 138, 1, 67, 0, 9, 71, 82, 65, 80, 72, 73, 67, 32, 67, 116, 2, 72, + 73, 74, 76, 36, 4, 82, 73, 71, 72, 209, 254, 4, 4, 83, 72, 73, 70, 2, 41, + 8, 72, 65, 82, 65, 67, 84, 69, 82, 2, 37, 7, 32, 73, 78, 84, 82, 79, 68, + 2, 11, 85, 2, 247, 146, 8, 67, 2, 25, 4, 71, 72, 45, 82, 2, 185, 1, 7, + 69, 86, 69, 82, 83, 69, 68, 4, 32, 2, 69, 70, 109, 2, 79, 87, 2, 49, 10, + 84, 45, 80, 79, 73, 78, 84, 73, 78, 71, 2, 21, 3, 32, 65, 78, 2, 17, 2, + 71, 76, 2, 31, 69, 2, 17, 2, 45, 57, 2, 11, 32, 2, 11, 81, 2, 165, 176, + 2, 2, 85, 79, 228, 1, 168, 2, 8, 65, 82, 67, 72, 65, 73, 67, 32, 224, 1, + 15, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 114, 76, + 188, 8, 5, 83, 73, 71, 78, 32, 144, 1, 11, 86, 79, 87, 69, 76, 32, 83, + 73, 71, 78, 32, 209, 232, 2, 17, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, + 78, 32, 75, 85, 78, 68, 68, 40, 46, 68, 109, 7, 78, 85, 77, 66, 69, 82, + 32, 18, 25, 4, 73, 71, 73, 84, 18, 11, 32, 18, 154, 216, 6, 70, 30, 83, + 42, 84, 142, 87, 78, 14, 79, 227, 112, 69, 22, 130, 238, 2, 79, 162, 235, + 3, 69, 30, 70, 42, 78, 38, 83, 39, 84, 6, 18, 82, 63, 89, 4, 56, 6, 65, + 75, 65, 65, 82, 65, 233, 198, 8, 2, 69, 80, 2, 229, 198, 8, 3, 65, 78, + 83, 138, 1, 60, 6, 69, 84, 84, 69, 82, 32, 185, 186, 2, 3, 73, 84, 72, + 118, 246, 1, 65, 106, 69, 34, 73, 62, 85, 34, 77, 220, 1, 4, 68, 65, 78, + 84, 58, 79, 28, 8, 83, 65, 78, 89, 65, 75, 65, 32, 50, 70, 2, 72, 2, 82, + 2, 86, 2, 89, 32, 8, 84, 65, 65, 76, 85, 74, 65, 32, 29, 9, 75, 65, 78, + 84, 65, 74, 65, 32, 78, 34, 102, 69, 204, 1, 2, 76, 80, 144, 2, 5, 77, + 66, 65, 32, 66, 14, 65, 2, 73, 2, 85, 219, 135, 5, 89, 4, 230, 3, 69, + 219, 135, 5, 89, 12, 46, 76, 2, 82, 154, 3, 73, 219, 135, 5, 89, 4, 11, + 85, 4, 138, 3, 85, 219, 135, 5, 89, 28, 42, 65, 177, 1, 5, 85, 85, 82, + 68, 72, 22, 32, 2, 72, 65, 247, 137, 5, 89, 20, 41, 8, 65, 80, 82, 65, + 65, 78, 65, 32, 20, 70, 84, 138, 1, 68, 22, 66, 2, 67, 2, 71, 2, 74, 2, + 75, 3, 80, 4, 154, 1, 84, 15, 65, 6, 25, 4, 65, 74, 65, 32, 6, 102, 76, + 2, 78, 3, 83, 4, 86, 79, 219, 135, 5, 89, 8, 26, 68, 22, 71, 3, 74, 4, + 18, 68, 15, 65, 2, 11, 65, 2, 215, 135, 5, 89, 6, 26, 78, 21, 2, 83, 65, + 2, 89, 2, 65, 65, 4, 68, 11, 78, 89, 79, 79, 71, 65, 32, 78, 65, 65, 75, + 207, 134, 5, 89, 2, 173, 190, 8, 4, 83, 73, 75, 89, 8, 66, 65, 210, 162, + 4, 67, 249, 154, 4, 6, 86, 73, 83, 65, 82, 71, 4, 200, 116, 5, 76, 45, + 76, 65, 75, 253, 200, 7, 6, 78, 85, 83, 86, 65, 82, 34, 56, 5, 68, 73, + 71, 65, 32, 62, 71, 82, 75, 155, 2, 65, 12, 58, 71, 48, 4, 75, 79, 77, + 66, 118, 65, 26, 73, 19, 80, 4, 11, 65, 4, 236, 2, 3, 69, 84, 84, 67, 89, + 2, 11, 85, 2, 247, 187, 8, 86, 16, 52, 5, 69, 84, 84, 73, 32, 85, 4, 79, + 77, 66, 85, 6, 26, 65, 26, 73, 19, 80, 2, 213, 1, 2, 69, 68, 2, 203, 1, + 83, 2, 175, 1, 65, 10, 40, 2, 86, 65, 161, 142, 8, 2, 32, 68, 9, 29, 5, + 32, 72, 65, 65, 32, 6, 62, 65, 0, 6, 68, 73, 71, 65, 32, 65, 85, 3, 71, + 65, 89, 2, 17, 2, 69, 76, 2, 11, 65, 2, 17, 2, 45, 80, 2, 11, 73, 2, 171, + 153, 8, 76, 2, 129, 136, 6, 5, 65, 78, 85, 75, 73, 12, 84, 2, 32, 80, + 206, 2, 45, 137, 160, 6, 10, 84, 69, 69, 78, 32, 80, 79, 73, 78, 84, 8, + 140, 1, 23, 69, 84, 65, 76, 76, 69, 68, 32, 66, 76, 65, 67, 75, 32, 65, + 78, 68, 32, 87, 72, 73, 84, 69, 49, 7, 79, 73, 78, 84, 69, 68, 32, 2, 25, + 4, 32, 70, 76, 79, 2, 219, 239, 6, 82, 6, 78, 80, 150, 158, 6, 66, 153, + 134, 1, 9, 83, 84, 65, 82, 32, 87, 73, 84, 72, 2, 21, 3, 73, 78, 87, 2, + 149, 158, 6, 4, 72, 69, 69, 76, 2, 235, 213, 3, 80, 12, 46, 73, 102, 85, + 173, 157, 7, 3, 65, 84, 69, 4, 56, 8, 32, 65, 78, 68, 32, 83, 75, 73, + 139, 254, 7, 69, 2, 17, 2, 32, 66, 2, 151, 230, 7, 79, 6, 32, 2, 76, 76, + 131, 179, 8, 78, 5, 11, 32, 2, 49, 10, 65, 78, 68, 32, 67, 82, 79, 83, + 83, 66, 2, 11, 79, 2, 199, 159, 7, 78, 42, 46, 65, 198, 4, 69, 210, 1, + 73, 167, 1, 79, 14, 64, 5, 78, 84, 69, 68, 32, 253, 156, 6, 5, 86, 79, + 78, 73, 67, 12, 148, 1, 12, 69, 81, 85, 65, 76, 32, 84, 79, 32, 79, 82, + 32, 229, 1, 19, 78, 79, 82, 84, 72, 32, 65, 82, 82, 79, 87, 32, 87, 73, + 84, 72, 32, 72, 79, 8, 60, 7, 71, 82, 69, 65, 84, 69, 82, 1, 4, 76, 69, + 83, 83, 4, 29, 5, 45, 84, 72, 65, 78, 5, 11, 32, 2, 25, 4, 87, 73, 84, + 72, 2, 25, 4, 32, 68, 79, 84, 2, 17, 2, 32, 73, 2, 11, 78, 2, 11, 83, 2, + 231, 158, 6, 73, 4, 24, 2, 79, 75, 43, 82, 2, 17, 2, 69, 68, 2, 203, 216, + 5, 32, 2, 29, 5, 73, 90, 79, 78, 84, 2, 11, 65, 2, 155, 249, 5, 76, 12, + 80, 2, 69, 80, 168, 158, 8, 9, 85, 84, 72, 32, 79, 82, 32, 83, 80, 203, + 17, 68, 8, 40, 4, 73, 78, 71, 32, 167, 242, 7, 89, 6, 244, 233, 4, 8, 65, + 67, 67, 79, 77, 77, 79, 68, 162, 176, 2, 83, 167, 88, 70, 6, 76, 9, 67, + 69, 32, 79, 70, 32, 80, 73, 90, 21, 6, 71, 72, 84, 76, 89, 32, 2, 235, + 171, 8, 90, 4, 40, 2, 83, 77, 169, 229, 6, 2, 70, 82, 2, 219, 246, 6, 73, + 10, 18, 80, 75, 84, 6, 220, 232, 4, 9, 73, 78, 71, 32, 76, 65, 82, 71, + 69, 223, 196, 3, 69, 4, 26, 32, 219, 172, 8, 72, 2, 11, 77, 2, 141, 145, + 7, 3, 65, 67, 72, 150, 1, 34, 65, 158, 13, 73, 207, 7, 79, 116, 32, 2, + 76, 76, 151, 154, 7, 83, 114, 30, 32, 141, 12, 2, 69, 82, 110, 194, 2, + 65, 48, 3, 66, 76, 85, 0, 5, 79, 82, 65, 78, 71, 20, 2, 67, 79, 158, 1, + 68, 22, 69, 140, 2, 11, 73, 68, 69, 79, 71, 82, 65, 80, 72, 73, 67, 28, + 2, 76, 69, 34, 82, 218, 3, 83, 22, 84, 36, 2, 85, 80, 56, 4, 86, 69, 69, + 32, 196, 252, 1, 3, 71, 82, 69, 34, 72, 146, 4, 80, 206, 83, 78, 134, + 246, 2, 70, 83, 81, 6, 134, 214, 4, 77, 214, 49, 73, 255, 137, 1, 83, 2, + 131, 218, 6, 69, 12, 68, 7, 78, 84, 65, 73, 78, 83, 32, 134, 130, 2, 77, + 131, 214, 5, 76, 6, 36, 4, 65, 83, 32, 77, 175, 1, 87, 2, 11, 69, 2, 11, + 77, 2, 195, 233, 7, 66, 2, 151, 130, 2, 79, 12, 84, 9, 76, 69, 77, 69, + 78, 84, 32, 79, 70, 222, 129, 2, 81, 42, 88, 183, 227, 2, 77, 7, 17, 2, + 32, 87, 4, 25, 4, 73, 84, 72, 32, 4, 26, 86, 239, 243, 4, 79, 2, 197, + 253, 5, 21, 69, 82, 84, 73, 67, 65, 76, 32, 66, 65, 82, 32, 65, 84, 32, + 69, 78, 68, 32, 79, 70, 2, 165, 207, 5, 2, 32, 67, 8, 130, 1, 70, 179, + 132, 2, 83, 40, 96, 3, 73, 71, 72, 64, 13, 79, 77, 65, 78, 32, 78, 85, + 77, 69, 82, 65, 76, 32, 251, 137, 6, 69, 6, 17, 2, 84, 32, 6, 238, 152, + 4, 67, 210, 3, 80, 147, 8, 84, 32, 62, 69, 50, 70, 62, 79, 46, 84, 214, + 174, 6, 83, 183, 87, 78, 4, 26, 76, 255, 247, 7, 73, 2, 231, 175, 6, 69, + 8, 26, 73, 191, 169, 7, 79, 6, 146, 26, 86, 159, 152, 6, 70, 6, 11, 78, + 6, 11, 69, 7, 151, 197, 2, 32, 8, 42, 87, 238, 174, 6, 72, 207, 162, 1, + 69, 4, 26, 69, 255, 160, 8, 79, 2, 167, 164, 7, 76, 2, 215, 209, 4, 69, + 4, 174, 253, 3, 87, 151, 134, 2, 73, 2, 253, 253, 4, 9, 45, 80, 79, 73, + 78, 84, 73, 78, 71, 2, 11, 87, 2, 21, 3, 73, 84, 72, 2, 11, 32, 2, 195, + 176, 4, 85, 4, 29, 5, 32, 84, 72, 65, 78, 5, 11, 32, 2, 11, 79, 2, 159, + 189, 1, 82, 32, 34, 76, 165, 150, 7, 2, 82, 75, 30, 40, 4, 73, 78, 71, + 32, 155, 158, 8, 69, 28, 112, 14, 67, 65, 84, 32, 70, 65, 67, 69, 32, 87, + 73, 84, 72, 32, 41, 10, 70, 65, 67, 69, 32, 87, 73, 84, 72, 32, 4, 160, + 1, 2, 72, 69, 231, 246, 4, 79, 24, 86, 72, 108, 10, 79, 80, 69, 78, 32, + 77, 79, 85, 84, 72, 246, 1, 83, 215, 224, 2, 84, 6, 34, 69, 54, 79, 255, + 248, 7, 65, 2, 237, 156, 5, 8, 65, 82, 84, 45, 83, 72, 65, 80, 2, 159, + 212, 4, 82, 9, 29, 5, 32, 65, 78, 68, 32, 6, 26, 67, 58, 83, 71, 84, 2, + 17, 2, 79, 76, 2, 169, 145, 7, 4, 68, 32, 83, 87, 2, 11, 77, 2, 11, 73, + 2, 11, 76, 2, 161, 155, 5, 3, 73, 78, 71, 2, 37, 7, 73, 71, 72, 84, 76, + 89, 45, 2, 175, 154, 5, 67, 8, 64, 11, 77, 73, 76, 73, 78, 71, 32, 69, + 89, 69, 83, 175, 1, 85, 7, 29, 5, 32, 65, 78, 68, 32, 4, 52, 4, 72, 65, + 78, 68, 57, 5, 84, 72, 82, 69, 69, 2, 161, 243, 4, 9, 32, 67, 79, 86, 69, + 82, 73, 78, 71, 2, 181, 210, 4, 2, 32, 72, 2, 17, 2, 78, 71, 2, 185, 129, + 7, 4, 76, 65, 83, 83, 2, 239, 230, 4, 75, 18, 42, 65, 48, 2, 69, 69, 21, + 2, 79, 87, 6, 26, 75, 243, 138, 7, 73, 4, 195, 136, 1, 69, 2, 139, 142, + 7, 90, 10, 100, 7, 32, 67, 65, 80, 80, 69, 68, 28, 3, 77, 65, 78, 132, + 223, 2, 3, 66, 79, 65, 143, 143, 1, 70, 2, 153, 169, 4, 2, 32, 77, 5, 49, + 10, 32, 87, 73, 84, 72, 79, 85, 84, 32, 83, 2, 183, 248, 6, 78, 155, 3, + 162, 2, 67, 48, 2, 70, 84, 228, 1, 6, 71, 68, 73, 65, 78, 32, 244, 10, 3, + 76, 73, 68, 140, 2, 12, 79, 78, 32, 87, 73, 84, 72, 32, 82, 73, 71, 72, + 20, 11, 82, 65, 32, 83, 79, 77, 80, 69, 78, 71, 32, 234, 2, 85, 216, 8, + 6, 89, 79, 77, 66, 79, 32, 190, 240, 6, 77, 130, 135, 1, 72, 3, 83, 4, + 164, 233, 2, 3, 67, 69, 82, 163, 235, 4, 75, 10, 70, 32, 124, 9, 87, 65, + 82, 69, 45, 70, 85, 78, 67, 255, 133, 6, 66, 6, 58, 72, 44, 6, 73, 67, + 69, 32, 67, 82, 227, 200, 6, 83, 2, 11, 89, 2, 11, 80, 2, 211, 186, 6, + 72, 2, 179, 211, 6, 69, 2, 11, 84, 2, 235, 149, 6, 73, 84, 248, 1, 10, + 67, 79, 77, 66, 73, 78, 73, 78, 71, 32, 168, 2, 13, 73, 78, 68, 69, 80, + 69, 78, 68, 69, 78, 84, 32, 83, 20, 7, 76, 69, 84, 84, 69, 82, 32, 188, + 3, 7, 78, 85, 77, 66, 69, 82, 32, 113, 12, 80, 85, 78, 67, 84, 85, 65, + 84, 73, 79, 78, 32, 22, 140, 1, 3, 72, 79, 79, 20, 4, 76, 79, 78, 71, 50, + 83, 58, 84, 242, 231, 3, 68, 128, 243, 1, 4, 82, 69, 83, 72, 249, 14, 4, + 67, 85, 82, 86, 4, 239, 234, 5, 75, 2, 25, 4, 32, 72, 79, 79, 2, 187, + 219, 5, 75, 2, 21, 3, 84, 82, 79, 2, 11, 75, 2, 131, 219, 5, 69, 4, 233, + 222, 1, 2, 87, 79, 2, 151, 140, 7, 72, 42, 190, 1, 65, 50, 71, 34, 76, + 56, 5, 82, 69, 83, 72, 45, 2, 90, 34, 83, 66, 89, 198, 207, 1, 72, 234, + 5, 75, 174, 81, 66, 2, 70, 170, 225, 4, 78, 134, 2, 84, 2, 87, 218, 103, + 80, 171, 4, 77, 4, 26, 76, 171, 138, 7, 89, 2, 131, 215, 1, 69, 2, 11, + 73, 2, 171, 244, 2, 77, 4, 26, 65, 167, 158, 6, 69, 2, 233, 208, 1, 2, + 77, 69, 2, 11, 65, 2, 175, 137, 7, 89, 6, 26, 65, 131, 137, 7, 72, 4, + 222, 199, 1, 77, 235, 147, 6, 68, 2, 239, 207, 1, 79, 8, 18, 79, 59, 84, + 4, 11, 78, 4, 11, 69, 5, 11, 32, 2, 183, 171, 2, 72, 4, 214, 151, 6, 87, + 155, 160, 1, 69, 10, 66, 67, 0, 6, 72, 65, 76, 70, 32, 67, 53, 4, 84, 87, + 79, 32, 2, 21, 3, 73, 82, 67, 2, 225, 246, 4, 2, 76, 69, 6, 100, 13, 86, + 69, 82, 84, 73, 67, 65, 76, 32, 66, 65, 82, 83, 13, 8, 67, 73, 82, 67, + 76, 69, 83, 32, 5, 11, 32, 2, 229, 178, 6, 4, 87, 73, 84, 72, 8, 30, 32, + 189, 1, 2, 85, 83, 4, 93, 21, 81, 85, 73, 76, 84, 32, 83, 81, 85, 65, 82, + 69, 32, 79, 82, 78, 65, 77, 69, 78, 84, 5, 11, 32, 2, 11, 73, 2, 17, 2, + 78, 32, 2, 11, 66, 2, 237, 224, 6, 4, 76, 65, 67, 75, 5, 197, 209, 4, 7, + 32, 87, 73, 84, 72, 32, 79, 2, 231, 132, 4, 84, 70, 52, 7, 76, 69, 84, + 84, 69, 82, 32, 191, 142, 6, 68, 50, 198, 1, 69, 32, 2, 77, 65, 30, 78, + 194, 146, 1, 66, 2, 67, 2, 68, 2, 71, 2, 72, 2, 74, 2, 75, 2, 76, 2, 80, + 2, 82, 2, 83, 2, 84, 2, 86, 2, 89, 242, 236, 6, 65, 2, 73, 2, 79, 3, 85, + 4, 234, 255, 7, 69, 147, 1, 72, 4, 218, 128, 8, 69, 3, 72, 6, 190, 146, + 1, 71, 2, 89, 243, 236, 6, 65, 58, 104, 3, 84, 72, 32, 149, 172, 6, 17, 78, 68, 32, 82, 69, 67, 79, 82, 68, 73, 78, 71, 32, 67, 79, 80, 89, 56, 60, 5, 69, 65, 83, 84, 32, 253, 2, 5, 87, 69, 83, 84, 32, 30, 96, 5, 65, - 82, 82, 79, 87, 194, 4, 80, 130, 1, 84, 138, 197, 4, 66, 38, 68, 18, 83, - 247, 58, 87, 13, 11, 32, 10, 68, 2, 65, 78, 38, 67, 120, 2, 84, 79, 154, - 2, 87, 203, 134, 5, 70, 2, 253, 2, 5, 68, 32, 83, 79, 85, 2, 37, 7, 82, - 79, 83, 83, 73, 78, 71, 2, 17, 2, 32, 78, 2, 17, 2, 79, 82, 2, 173, 133, - 5, 5, 84, 72, 32, 69, 65, 2, 17, 2, 32, 67, 2, 243, 241, 3, 79, 26, 96, - 5, 65, 82, 82, 79, 87, 198, 1, 80, 130, 1, 84, 138, 197, 4, 66, 38, 68, - 18, 83, 247, 58, 87, 9, 11, 32, 6, 52, 5, 65, 78, 68, 32, 78, 74, 87, - 203, 134, 5, 70, 2, 17, 2, 79, 82, 2, 21, 3, 84, 72, 32, 2, 137, 131, 5, - 2, 87, 69, 2, 21, 3, 73, 84, 72, 2, 11, 32, 2, 251, 151, 6, 72, 6, 41, 8, - 79, 73, 78, 84, 73, 78, 71, 32, 6, 42, 86, 242, 160, 4, 76, 155, 158, 2, - 66, 2, 17, 2, 73, 78, 2, 199, 160, 4, 69, 4, 73, 16, 82, 73, 65, 78, 71, + 82, 82, 79, 87, 194, 4, 80, 130, 1, 84, 190, 207, 4, 66, 38, 68, 18, 83, + 251, 58, 87, 13, 11, 32, 10, 68, 2, 65, 78, 38, 67, 120, 2, 84, 79, 154, + 2, 87, 131, 145, 5, 70, 2, 253, 2, 5, 68, 32, 83, 79, 85, 2, 37, 7, 82, + 79, 83, 83, 73, 78, 71, 2, 17, 2, 32, 78, 2, 17, 2, 79, 82, 2, 229, 143, + 5, 5, 84, 72, 32, 69, 65, 2, 17, 2, 32, 67, 2, 167, 251, 3, 79, 26, 96, + 5, 65, 82, 82, 79, 87, 198, 1, 80, 130, 1, 84, 190, 207, 4, 66, 38, 68, + 18, 83, 251, 58, 87, 9, 11, 32, 6, 52, 5, 65, 78, 68, 32, 78, 74, 87, + 131, 145, 5, 70, 2, 17, 2, 79, 82, 2, 21, 3, 84, 72, 32, 2, 193, 141, 5, + 2, 87, 69, 2, 21, 3, 73, 84, 72, 2, 11, 32, 2, 231, 163, 6, 72, 6, 41, 8, + 79, 73, 78, 84, 73, 78, 71, 32, 6, 42, 86, 166, 171, 4, 76, 219, 159, 2, + 66, 2, 17, 2, 73, 78, 2, 251, 170, 4, 69, 4, 73, 16, 82, 73, 65, 78, 71, 76, 69, 45, 72, 69, 65, 68, 69, 68, 32, 65, 4, 25, 4, 82, 82, 79, 87, 5, - 11, 32, 2, 203, 137, 5, 84, 166, 1, 220, 3, 23, 67, 76, 85, 83, 84, 69, + 11, 32, 2, 131, 148, 5, 84, 166, 1, 220, 3, 23, 67, 76, 85, 83, 84, 69, 82, 45, 73, 78, 73, 84, 73, 65, 76, 32, 76, 69, 84, 84, 69, 82, 32, 40, 21, 70, 73, 78, 65, 76, 32, 67, 79, 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 112, 27, 72, 69, 65, 68, 32, 77, 65, 82, 75, 32, 87, 73, 84, 72, 32, 77, 79, 79, 78, 32, 65, 78, 68, 32, 83, 85, 78, 104, 7, 76, 69, 84, 84, 69, 82, 32, 180, 1, 5, 77, 65, 82, 75, 32, 54, 83, 100, 8, - 84, 69, 82, 77, 73, 78, 65, 76, 40, 6, 86, 79, 87, 69, 76, 32, 207, 132, - 4, 71, 8, 142, 158, 7, 83, 138, 69, 76, 3, 82, 24, 138, 197, 3, 83, 134, - 165, 2, 78, 226, 248, 1, 45, 186, 2, 66, 2, 68, 2, 71, 2, 75, 2, 76, 2, + 84, 69, 82, 77, 73, 78, 65, 76, 40, 6, 86, 79, 87, 69, 76, 32, 131, 143, + 4, 71, 8, 150, 172, 7, 83, 138, 69, 76, 3, 82, 24, 206, 226, 3, 83, 226, + 146, 2, 78, 202, 251, 1, 45, 186, 2, 66, 2, 68, 2, 71, 2, 75, 2, 76, 2, 77, 3, 82, 7, 29, 5, 32, 65, 78, 68, 32, 4, 50, 70, 1, 8, 84, 82, 73, 80, - 76, 69, 32, 70, 2, 235, 212, 5, 76, 82, 230, 188, 2, 68, 138, 222, 4, 75, + 76, 69, 32, 70, 2, 139, 224, 5, 76, 82, 162, 194, 2, 68, 214, 230, 4, 75, 38, 78, 46, 83, 38, 84, 46, 66, 2, 67, 2, 71, 2, 74, 2, 80, 2, 90, 138, - 69, 45, 2, 72, 2, 76, 2, 77, 2, 82, 2, 86, 2, 89, 187, 2, 65, 8, 222, - 140, 4, 80, 206, 142, 3, 68, 58, 83, 63, 84, 10, 40, 4, 73, 71, 78, 32, - 139, 159, 7, 85, 8, 246, 168, 5, 74, 158, 2, 86, 122, 85, 187, 240, 1, - 65, 4, 237, 223, 3, 5, 32, 77, 65, 82, 75, 22, 44, 5, 83, 73, 71, 78, 32, - 179, 158, 7, 76, 20, 88, 7, 86, 79, 67, 65, 76, 73, 67, 154, 159, 7, 65, - 26, 79, 2, 85, 162, 64, 69, 3, 73, 4, 11, 32, 4, 194, 223, 7, 76, 3, 82, + 69, 45, 2, 72, 2, 76, 2, 77, 2, 82, 2, 86, 2, 89, 187, 2, 65, 8, 146, + 151, 4, 80, 162, 146, 3, 68, 58, 83, 63, 84, 10, 40, 4, 73, 71, 78, 32, + 147, 173, 7, 85, 8, 186, 179, 5, 74, 158, 2, 86, 122, 85, 255, 243, 1, + 65, 4, 161, 233, 3, 5, 32, 77, 65, 82, 75, 22, 44, 5, 83, 73, 71, 78, 32, + 187, 172, 7, 76, 20, 88, 7, 86, 79, 67, 65, 76, 73, 67, 162, 173, 7, 65, + 26, 79, 2, 85, 162, 64, 69, 3, 73, 4, 11, 32, 4, 202, 237, 7, 76, 3, 82, 71, 122, 65, 206, 1, 69, 168, 5, 13, 72, 69, 82, 73, 67, 65, 76, 32, 65, 78, 71, 76, 69, 82, 73, 220, 1, 2, 76, 65, 91, 79, 17, 48, 4, 71, 72, 69, - 84, 22, 82, 147, 199, 7, 67, 2, 159, 202, 7, 84, 10, 24, 2, 75, 76, 59, - 83, 6, 26, 73, 191, 214, 5, 69, 2, 237, 213, 6, 2, 78, 71, 4, 17, 2, 69, - 32, 4, 214, 53, 72, 135, 3, 86, 22, 104, 2, 65, 75, 230, 3, 69, 68, 4, - 83, 77, 73, 76, 141, 214, 3, 9, 67, 75, 76, 69, 32, 70, 73, 76, 76, 12, - 70, 45, 64, 2, 69, 82, 153, 2, 8, 73, 78, 71, 32, 72, 69, 65, 68, 2, 161, - 193, 5, 11, 78, 79, 45, 69, 86, 73, 76, 32, 77, 79, 78, 9, 33, 6, 32, 87, + 84, 22, 82, 155, 213, 7, 67, 2, 167, 216, 7, 84, 10, 24, 2, 75, 76, 59, + 83, 6, 26, 73, 223, 225, 5, 69, 2, 245, 227, 6, 2, 78, 71, 4, 17, 2, 69, + 32, 4, 202, 53, 72, 135, 3, 86, 22, 104, 2, 65, 75, 230, 3, 69, 68, 4, + 83, 77, 73, 76, 193, 223, 3, 9, 67, 75, 76, 69, 32, 70, 73, 76, 76, 12, + 70, 45, 64, 2, 69, 82, 153, 2, 8, 73, 78, 71, 32, 72, 69, 65, 68, 2, 205, + 203, 5, 11, 78, 79, 45, 69, 86, 73, 76, 32, 77, 79, 78, 9, 33, 6, 32, 87, 73, 84, 72, 32, 6, 26, 67, 78, 79, 55, 84, 2, 33, 6, 65, 78, 67, 69, 76, - 76, 2, 241, 169, 7, 5, 65, 84, 73, 79, 78, 2, 209, 186, 4, 8, 78, 69, 32, - 83, 79, 85, 78, 68, 2, 25, 4, 72, 82, 69, 69, 2, 149, 197, 6, 10, 32, 83, - 79, 85, 78, 68, 32, 87, 65, 86, 2, 11, 32, 2, 185, 145, 6, 9, 73, 78, 32, - 83, 73, 76, 72, 79, 85, 4, 34, 68, 245, 245, 2, 2, 67, 72, 2, 11, 66, 2, - 247, 195, 6, 79, 2, 203, 208, 6, 79, 7, 45, 9, 32, 79, 80, 69, 78, 73, - 78, 71, 32, 4, 154, 172, 7, 76, 227, 42, 85, 10, 44, 3, 68, 69, 82, 41, - 4, 82, 65, 76, 32, 5, 17, 2, 32, 87, 2, 191, 154, 6, 69, 6, 80, 8, 67, - 65, 76, 69, 78, 68, 65, 82, 0, 4, 78, 79, 84, 69, 29, 2, 83, 72, 2, 141, - 143, 7, 2, 32, 80, 2, 223, 205, 5, 69, 4, 64, 10, 83, 72, 73, 78, 71, 32, - 83, 87, 69, 65, 211, 156, 6, 84, 2, 159, 193, 6, 84, 10, 90, 79, 60, 7, - 85, 84, 73, 78, 71, 32, 87, 192, 240, 2, 3, 82, 84, 83, 191, 173, 3, 78, - 4, 240, 87, 7, 76, 32, 79, 70, 32, 84, 72, 231, 251, 6, 78, 2, 11, 72, 2, - 231, 174, 6, 65, 222, 5, 26, 65, 191, 140, 7, 73, 220, 5, 28, 2, 82, 69, - 163, 69, 84, 218, 5, 30, 32, 245, 46, 2, 68, 32, 234, 3, 250, 1, 65, 104, + 76, 2, 249, 183, 7, 5, 65, 84, 73, 79, 78, 2, 133, 197, 4, 8, 78, 69, 32, + 83, 79, 85, 78, 68, 2, 25, 4, 72, 82, 69, 69, 2, 145, 209, 6, 10, 32, 83, + 79, 85, 78, 68, 32, 87, 65, 86, 2, 11, 32, 2, 165, 157, 6, 9, 73, 78, 32, + 83, 73, 76, 72, 79, 85, 4, 34, 68, 197, 251, 2, 2, 67, 72, 2, 11, 66, 2, + 183, 220, 6, 79, 2, 211, 222, 6, 79, 7, 45, 9, 32, 79, 80, 69, 78, 73, + 78, 71, 32, 4, 162, 186, 7, 76, 227, 42, 85, 10, 44, 3, 68, 69, 82, 41, + 4, 82, 65, 76, 32, 5, 17, 2, 32, 87, 2, 179, 166, 6, 69, 6, 80, 8, 67, + 65, 76, 69, 78, 68, 65, 82, 0, 4, 78, 79, 84, 69, 29, 2, 83, 72, 2, 149, + 157, 7, 2, 32, 80, 2, 255, 216, 5, 69, 4, 64, 10, 83, 72, 73, 78, 71, 32, + 83, 87, 69, 65, 199, 168, 6, 84, 2, 139, 205, 6, 84, 10, 90, 79, 60, 7, + 85, 84, 73, 78, 71, 32, 87, 144, 246, 2, 3, 82, 84, 83, 227, 179, 3, 78, + 4, 248, 87, 7, 76, 32, 79, 70, 32, 84, 72, 231, 137, 7, 78, 2, 11, 72, 2, + 219, 186, 6, 65, 222, 5, 26, 65, 199, 154, 7, 73, 220, 5, 28, 2, 82, 69, + 155, 69, 84, 218, 5, 30, 32, 233, 46, 2, 68, 32, 234, 3, 250, 1, 65, 104, 2, 86, 32, 94, 66, 158, 1, 67, 170, 1, 68, 102, 69, 194, 2, 70, 146, 2, - 71, 222, 1, 72, 214, 2, 73, 94, 75, 190, 4, 76, 102, 77, 210, 5, 78, 94, + 71, 222, 1, 72, 214, 2, 73, 94, 75, 182, 4, 76, 102, 77, 206, 5, 78, 94, 79, 178, 1, 80, 222, 3, 82, 154, 3, 83, 206, 2, 84, 74, 87, 230, 6, 89, - 251, 211, 6, 85, 16, 102, 32, 58, 80, 132, 73, 2, 78, 80, 144, 153, 2, 3, - 82, 85, 72, 194, 51, 65, 162, 185, 4, 77, 3, 85, 2, 21, 3, 79, 86, 69, 2, - 11, 82, 2, 175, 190, 7, 32, 4, 234, 184, 2, 69, 231, 138, 1, 65, 14, 98, - 65, 36, 4, 85, 83, 83, 89, 170, 148, 3, 73, 216, 74, 2, 79, 82, 138, 53, - 69, 195, 185, 3, 81, 4, 32, 2, 65, 82, 203, 205, 7, 82, 2, 167, 148, 3, - 69, 22, 98, 65, 30, 79, 158, 20, 77, 196, 183, 7, 7, 32, 79, 86, 69, 82, - 32, 75, 74, 85, 14, 67, 3, 68, 4, 206, 204, 7, 76, 3, 80, 5, 17, 2, 82, - 80, 2, 155, 194, 2, 79, 20, 82, 65, 162, 19, 77, 250, 205, 2, 69, 130, - 49, 79, 162, 185, 4, 66, 2, 74, 3, 76, 5, 135, 22, 65, 18, 66, 69, 22, - 82, 164, 25, 5, 83, 85, 75, 85, 85, 175, 177, 7, 86, 2, 151, 216, 5, 75, - 12, 52, 7, 65, 32, 78, 65, 77, 69, 32, 155, 202, 7, 71, 10, 132, 1, 4, - 72, 69, 73, 83, 20, 5, 84, 65, 73, 83, 89, 152, 22, 3, 77, 69, 73, 220, - 193, 2, 3, 82, 69, 73, 1, 4, 83, 89, 79, 85, 2, 183, 181, 7, 69, 2, 147, - 164, 7, 79, 12, 26, 79, 207, 200, 7, 77, 10, 60, 9, 85, 82, 32, 67, 79, - 82, 78, 69, 82, 227, 171, 7, 79, 8, 26, 32, 243, 199, 7, 83, 6, 128, 1, - 11, 66, 76, 65, 67, 75, 32, 84, 82, 73, 65, 78, 216, 62, 6, 68, 73, 65, - 71, 79, 78, 229, 199, 3, 5, 83, 65, 76, 84, 73, 2, 167, 137, 4, 71, 24, - 102, 65, 46, 73, 172, 12, 5, 85, 82, 65, 77, 85, 238, 149, 3, 72, 246, - 160, 4, 80, 186, 2, 66, 3, 89, 6, 130, 246, 6, 82, 222, 46, 78, 147, 33, - 76, 6, 42, 82, 158, 130, 5, 78, 199, 192, 2, 71, 2, 201, 210, 5, 2, 85, - 68, 30, 130, 1, 65, 22, 69, 54, 79, 62, 85, 242, 214, 1, 80, 156, 227, 5, - 10, 73, 82, 65, 71, 65, 78, 65, 32, 72, 79, 234, 8, 71, 3, 90, 5, 167, - 244, 1, 73, 4, 168, 11, 3, 75, 85, 84, 233, 232, 1, 2, 82, 85, 6, 26, 79, - 151, 195, 7, 78, 4, 170, 158, 7, 82, 235, 36, 78, 6, 54, 73, 144, 17, 4, - 65, 82, 65, 68, 175, 231, 5, 82, 2, 255, 211, 3, 73, 14, 54, 78, 212, 12, - 4, 77, 65, 71, 69, 159, 181, 7, 85, 7, 154, 210, 2, 73, 255, 219, 4, 84, - 58, 230, 1, 65, 48, 2, 89, 85, 20, 3, 73, 82, 79, 78, 77, 90, 79, 60, 2, - 85, 82, 144, 8, 2, 69, 69, 134, 146, 3, 72, 238, 153, 3, 67, 232, 120, 3, - 32, 79, 72, 162, 14, 80, 186, 2, 66, 2, 71, 2, 75, 2, 76, 2, 84, 2, 86, - 3, 87, 9, 22, 82, 131, 98, 73, 4, 22, 79, 163, 21, 65, 2, 183, 252, 4, - 82, 9, 252, 16, 3, 77, 69, 69, 140, 4, 2, 87, 65, 129, 171, 2, 3, 71, 85, - 82, 9, 11, 32, 6, 22, 67, 199, 14, 83, 4, 22, 65, 179, 6, 85, 2, 233, - 233, 4, 2, 80, 73, 4, 18, 79, 23, 82, 2, 243, 158, 7, 80, 2, 207, 129, 6, - 85, 4, 164, 206, 5, 4, 85, 90, 69, 73, 215, 68, 79, 12, 62, 79, 244, 13, - 2, 69, 70, 222, 174, 7, 77, 2, 78, 3, 88, 4, 150, 135, 6, 90, 187, 181, - 1, 71, 82, 162, 1, 32, 70, 65, 122, 66, 22, 69, 54, 73, 110, 77, 68, 2, - 85, 32, 78, 86, 2, 87, 222, 147, 3, 72, 250, 153, 3, 79, 254, 134, 1, 80, - 186, 2, 71, 2, 76, 3, 83, 10, 34, 79, 242, 2, 67, 139, 8, 83, 6, 198, 10, - 86, 199, 159, 7, 72, 13, 54, 73, 44, 2, 78, 83, 206, 84, 82, 215, 234, 4, - 72, 4, 240, 202, 5, 2, 75, 85, 179, 202, 1, 82, 2, 243, 233, 6, 89, 5, - 223, 171, 2, 32, 6, 28, 2, 71, 65, 251, 10, 69, 5, 171, 233, 6, 84, 8, - 42, 75, 20, 2, 82, 73, 199, 184, 7, 76, 2, 183, 147, 6, 85, 5, 11, 66, 2, - 11, 65, 2, 139, 255, 2, 65, 7, 11, 32, 4, 22, 67, 139, 8, 83, 2, 11, 85, - 2, 155, 172, 2, 66, 16, 210, 183, 7, 65, 2, 70, 2, 71, 2, 76, 2, 77, 2, - 83, 2, 86, 3, 87, 5, 11, 32, 2, 11, 77, 2, 171, 244, 6, 69, 16, 70, 65, - 130, 12, 79, 150, 170, 7, 70, 2, 77, 2, 83, 2, 86, 3, 87, 5, 159, 151, 7, - 78, 12, 78, 78, 20, 7, 82, 73, 71, 73, 78, 65, 76, 150, 182, 2, 79, 139, - 255, 4, 86, 2, 199, 144, 7, 83, 6, 21, 3, 32, 79, 70, 7, 25, 4, 32, 79, - 82, 32, 4, 242, 96, 78, 51, 69, 46, 118, 65, 82, 69, 94, 73, 74, 79, 142, - 161, 7, 80, 218, 16, 67, 2, 70, 2, 72, 2, 77, 2, 82, 2, 83, 2, 86, 3, 87, - 9, 38, 65, 229, 254, 3, 3, 32, 65, 77, 4, 236, 1, 2, 83, 69, 219, 140, 7, - 84, 8, 34, 69, 22, 78, 223, 147, 7, 83, 2, 139, 159, 7, 90, 4, 206, 227, - 1, 73, 155, 170, 5, 83, 6, 34, 75, 233, 3, 3, 65, 83, 85, 4, 230, 248, 2, - 85, 163, 185, 4, 79, 6, 34, 73, 22, 78, 21, 2, 83, 73, 2, 131, 195, 3, - 78, 2, 183, 146, 7, 68, 2, 241, 247, 6, 4, 84, 73, 79, 78, 22, 60, 2, 65, - 68, 114, 69, 62, 73, 134, 1, 85, 207, 221, 6, 79, 7, 21, 3, 32, 79, 86, - 4, 25, 4, 69, 82, 32, 83, 5, 17, 2, 32, 83, 2, 11, 81, 2, 133, 164, 2, 2, - 85, 65, 4, 36, 3, 78, 84, 79, 183, 138, 7, 77, 2, 179, 219, 5, 71, 6, 40, - 2, 71, 72, 62, 84, 235, 171, 7, 82, 2, 233, 238, 3, 10, 84, 32, 79, 80, - 69, 78, 32, 66, 79, 88, 2, 253, 244, 2, 2, 84, 79, 4, 228, 1, 2, 85, 66, - 163, 233, 4, 80, 22, 138, 1, 65, 72, 3, 69, 78, 84, 28, 11, 80, 73, 82, - 65, 76, 32, 70, 82, 79, 77, 32, 152, 188, 2, 3, 73, 82, 73, 214, 239, 4, - 82, 3, 86, 4, 48, 2, 73, 75, 149, 173, 2, 4, 78, 84, 73, 73, 2, 251, 242, - 2, 85, 4, 134, 172, 7, 73, 3, 79, 8, 18, 66, 43, 84, 4, 201, 128, 7, 5, - 79, 84, 84, 79, 77, 4, 11, 79, 4, 151, 128, 7, 80, 6, 42, 65, 186, 135, - 3, 72, 207, 211, 3, 79, 2, 201, 245, 6, 2, 82, 71, 34, 50, 65, 20, 4, 73, - 84, 72, 32, 131, 170, 7, 66, 2, 215, 187, 3, 84, 30, 214, 1, 66, 40, 3, + 143, 226, 6, 85, 16, 102, 32, 58, 80, 252, 72, 2, 78, 80, 204, 158, 2, 3, + 82, 85, 72, 186, 51, 65, 254, 193, 4, 77, 3, 85, 2, 21, 3, 79, 86, 69, 2, + 11, 82, 2, 183, 204, 7, 32, 4, 166, 190, 2, 69, 223, 142, 1, 65, 14, 98, + 65, 36, 4, 85, 83, 83, 89, 214, 153, 3, 73, 232, 78, 2, 79, 82, 130, 54, + 69, 151, 189, 3, 81, 4, 32, 2, 65, 82, 211, 219, 7, 82, 2, 211, 153, 3, + 69, 22, 98, 65, 30, 79, 146, 20, 77, 216, 197, 7, 7, 32, 79, 86, 69, 82, + 32, 75, 74, 85, 14, 67, 3, 68, 4, 214, 218, 7, 76, 3, 80, 5, 17, 2, 82, + 80, 2, 215, 199, 2, 79, 20, 82, 65, 150, 19, 77, 186, 211, 2, 69, 250, + 48, 79, 254, 193, 4, 66, 2, 74, 3, 76, 5, 251, 21, 65, 18, 66, 69, 22, + 82, 152, 25, 5, 83, 85, 75, 85, 85, 195, 191, 7, 86, 2, 183, 227, 5, 75, + 12, 52, 7, 65, 32, 78, 65, 77, 69, 32, 163, 216, 7, 71, 10, 132, 1, 4, + 72, 69, 73, 83, 20, 5, 84, 65, 73, 83, 89, 140, 22, 3, 77, 69, 73, 156, + 199, 2, 3, 82, 69, 73, 1, 4, 83, 89, 79, 85, 2, 191, 195, 7, 69, 2, 155, + 178, 7, 79, 12, 26, 79, 215, 214, 7, 77, 10, 60, 9, 85, 82, 32, 67, 79, + 82, 78, 69, 82, 235, 185, 7, 79, 8, 26, 32, 251, 213, 7, 83, 6, 128, 1, + 11, 66, 76, 65, 67, 75, 32, 84, 82, 73, 65, 78, 208, 62, 6, 68, 73, 65, + 71, 79, 78, 161, 210, 3, 5, 83, 65, 76, 84, 73, 2, 219, 147, 4, 71, 24, + 102, 65, 46, 73, 160, 12, 5, 85, 82, 65, 77, 85, 134, 233, 5, 72, 242, + 219, 1, 80, 186, 2, 66, 3, 89, 6, 138, 132, 7, 82, 222, 46, 78, 147, 33, + 76, 6, 42, 82, 214, 140, 5, 78, 151, 196, 2, 71, 2, 233, 221, 5, 2, 85, + 68, 30, 130, 1, 65, 22, 69, 54, 79, 62, 85, 142, 191, 3, 80, 136, 137, 4, + 10, 73, 82, 65, 71, 65, 78, 65, 32, 72, 79, 234, 8, 71, 3, 90, 5, 183, + 249, 1, 73, 4, 156, 11, 3, 75, 85, 84, 133, 238, 1, 2, 82, 85, 6, 26, 79, + 159, 209, 7, 78, 4, 178, 172, 7, 82, 235, 36, 78, 6, 54, 73, 132, 17, 4, + 65, 82, 65, 68, 167, 243, 5, 82, 2, 187, 221, 3, 73, 14, 54, 78, 200, 12, + 4, 77, 65, 71, 69, 179, 195, 7, 85, 7, 206, 215, 2, 73, 211, 228, 4, 84, + 58, 234, 1, 65, 56, 3, 73, 82, 79, 78, 77, 90, 79, 60, 2, 85, 82, 140, 8, + 2, 69, 69, 208, 143, 2, 2, 89, 85, 206, 213, 3, 72, 206, 82, 67, 132, + 123, 3, 32, 79, 72, 162, 14, 80, 186, 2, 66, 2, 71, 2, 75, 2, 76, 2, 84, + 2, 86, 3, 87, 9, 22, 82, 135, 98, 73, 4, 166, 21, 65, 183, 132, 2, 79, 9, + 248, 16, 3, 77, 69, 69, 140, 4, 2, 87, 65, 193, 176, 2, 3, 71, 85, 82, 9, + 11, 32, 6, 22, 67, 195, 14, 83, 4, 22, 65, 175, 6, 85, 2, 169, 244, 4, 2, + 80, 73, 4, 18, 79, 23, 82, 2, 131, 173, 7, 80, 2, 203, 141, 6, 85, 4, + 204, 217, 5, 4, 85, 90, 69, 73, 171, 69, 79, 12, 62, 79, 240, 13, 2, 69, + 70, 242, 188, 7, 77, 2, 78, 3, 88, 4, 146, 147, 6, 90, 207, 183, 1, 71, + 82, 158, 1, 32, 70, 65, 122, 66, 22, 69, 54, 73, 110, 77, 68, 2, 85, 32, + 78, 86, 2, 87, 246, 230, 5, 72, 218, 82, 79, 154, 137, 1, 80, 186, 2, 71, + 2, 76, 3, 83, 10, 34, 79, 242, 2, 67, 139, 8, 83, 6, 198, 10, 86, 219, + 173, 7, 72, 13, 54, 73, 44, 2, 78, 83, 226, 84, 82, 239, 245, 4, 72, 4, + 156, 214, 5, 2, 75, 85, 155, 205, 1, 82, 2, 135, 248, 6, 89, 5, 167, 177, + 2, 32, 6, 28, 2, 71, 65, 251, 10, 69, 5, 191, 247, 6, 84, 8, 42, 75, 20, + 2, 82, 73, 219, 198, 7, 76, 2, 183, 159, 6, 85, 5, 11, 66, 2, 11, 65, 2, + 195, 132, 3, 65, 7, 11, 32, 4, 22, 67, 139, 8, 83, 2, 11, 85, 2, 227, + 177, 2, 66, 16, 230, 197, 7, 65, 2, 70, 2, 71, 2, 76, 2, 77, 2, 83, 2, + 86, 3, 87, 5, 11, 32, 2, 11, 77, 2, 191, 130, 7, 69, 16, 70, 65, 130, 12, + 79, 170, 184, 7, 70, 2, 77, 2, 83, 2, 86, 3, 87, 5, 179, 165, 7, 78, 12, + 78, 78, 20, 7, 82, 73, 71, 73, 78, 65, 76, 214, 187, 2, 79, 223, 135, 5, + 86, 2, 219, 158, 7, 83, 6, 21, 3, 32, 79, 70, 7, 25, 4, 32, 79, 82, 32, + 4, 134, 97, 78, 51, 69, 46, 118, 65, 82, 69, 94, 73, 74, 79, 162, 175, 7, + 80, 218, 16, 67, 2, 70, 2, 72, 2, 77, 2, 82, 2, 83, 2, 86, 3, 87, 9, 38, + 65, 165, 137, 4, 3, 32, 65, 77, 4, 236, 1, 2, 83, 69, 239, 154, 7, 84, 8, + 34, 69, 22, 78, 243, 161, 7, 83, 2, 159, 173, 7, 90, 4, 234, 232, 1, 73, + 147, 179, 5, 83, 6, 34, 75, 233, 3, 3, 65, 83, 85, 4, 158, 254, 2, 85, + 255, 193, 4, 79, 6, 34, 73, 22, 78, 21, 2, 83, 73, 2, 203, 204, 3, 78, 2, + 203, 160, 7, 68, 2, 133, 134, 7, 4, 84, 73, 79, 78, 22, 60, 2, 65, 68, + 114, 69, 62, 73, 134, 1, 85, 227, 235, 6, 79, 7, 21, 3, 32, 79, 86, 4, + 25, 4, 69, 82, 32, 83, 5, 17, 2, 32, 83, 2, 11, 81, 2, 205, 169, 2, 2, + 85, 65, 4, 36, 3, 78, 84, 79, 203, 152, 7, 77, 2, 171, 231, 5, 71, 6, 40, + 2, 71, 72, 62, 84, 255, 185, 7, 82, 2, 169, 249, 3, 10, 84, 32, 79, 80, + 69, 78, 32, 66, 79, 88, 2, 181, 250, 2, 2, 84, 79, 4, 228, 1, 2, 85, 66, + 231, 243, 4, 80, 22, 138, 1, 65, 72, 3, 69, 78, 84, 28, 11, 80, 73, 82, + 65, 76, 32, 70, 82, 79, 77, 32, 216, 193, 2, 3, 73, 82, 73, 170, 248, 4, + 82, 3, 86, 4, 48, 2, 73, 75, 213, 178, 2, 4, 78, 84, 73, 73, 2, 179, 248, + 2, 85, 4, 154, 186, 7, 73, 3, 79, 8, 18, 66, 43, 84, 4, 221, 142, 7, 5, + 79, 84, 84, 79, 77, 4, 11, 79, 4, 171, 142, 7, 80, 6, 42, 65, 210, 218, + 5, 72, 203, 142, 1, 79, 2, 221, 131, 7, 2, 82, 71, 34, 50, 65, 20, 4, 73, + 84, 72, 32, 151, 184, 7, 66, 2, 159, 197, 3, 84, 30, 214, 1, 66, 40, 3, 68, 73, 65, 0, 5, 79, 82, 84, 72, 79, 94, 72, 54, 76, 126, 84, 28, 6, 85, - 80, 80, 69, 82, 32, 186, 1, 86, 178, 136, 4, 82, 173, 130, 2, 13, 67, 79, - 78, 84, 79, 85, 82, 69, 68, 32, 79, 85, 84, 2, 217, 140, 4, 5, 79, 84, - 84, 79, 77, 2, 11, 71, 2, 213, 175, 4, 15, 79, 78, 65, 76, 32, 67, 82, + 80, 80, 69, 82, 32, 186, 1, 86, 242, 146, 4, 82, 253, 131, 2, 13, 67, 79, + 78, 84, 79, 85, 82, 69, 68, 32, 79, 85, 84, 2, 153, 151, 4, 5, 79, 84, + 84, 79, 77, 2, 11, 71, 2, 153, 186, 4, 15, 79, 78, 65, 76, 32, 67, 82, 79, 83, 83, 72, 65, 84, 67, 72, 2, 11, 79, 2, 149, 3, 6, 82, 73, 90, 79, - 78, 84, 6, 44, 5, 79, 87, 69, 82, 32, 199, 138, 4, 69, 4, 44, 3, 76, 69, - 70, 1, 4, 82, 73, 71, 72, 2, 189, 1, 3, 84, 32, 68, 2, 165, 138, 4, 2, + 78, 84, 6, 44, 5, 79, 87, 69, 82, 32, 135, 149, 4, 69, 4, 44, 3, 76, 69, + 70, 1, 4, 82, 73, 71, 72, 2, 189, 1, 3, 84, 32, 68, 2, 229, 148, 4, 2, 79, 80, 8, 60, 5, 76, 69, 70, 84, 32, 37, 6, 82, 73, 71, 72, 84, 32, 4, - 70, 68, 185, 153, 4, 2, 84, 79, 4, 34, 68, 245, 171, 4, 2, 84, 79, 2, - 129, 137, 4, 7, 73, 65, 71, 79, 78, 65, 76, 2, 29, 5, 69, 82, 84, 73, 67, - 2, 225, 171, 4, 2, 65, 76, 6, 32, 2, 65, 65, 195, 217, 5, 85, 4, 214, - 254, 6, 82, 247, 5, 68, 240, 1, 226, 1, 67, 174, 9, 68, 34, 70, 88, 3, + 70, 68, 249, 163, 4, 2, 84, 79, 4, 34, 68, 185, 182, 4, 2, 84, 79, 2, + 193, 147, 4, 7, 73, 65, 71, 79, 78, 65, 76, 2, 29, 5, 69, 82, 84, 73, 67, + 2, 165, 182, 4, 2, 65, 76, 6, 32, 2, 65, 65, 187, 229, 5, 85, 4, 234, + 140, 7, 82, 247, 5, 68, 240, 1, 226, 1, 67, 174, 9, 68, 34, 70, 88, 3, 82, 73, 83, 142, 1, 72, 66, 75, 106, 76, 166, 1, 77, 38, 78, 34, 79, 94, - 80, 34, 83, 190, 2, 84, 156, 1, 5, 69, 73, 71, 72, 84, 22, 85, 90, 86, - 230, 249, 4, 65, 218, 107, 87, 223, 97, 73, 90, 128, 1, 21, 74, 75, 32, + 80, 34, 83, 190, 2, 84, 160, 1, 5, 69, 73, 71, 72, 84, 22, 85, 90, 86, + 154, 132, 5, 65, 162, 109, 87, 243, 99, 73, 90, 128, 1, 21, 74, 75, 32, 85, 78, 73, 70, 73, 69, 68, 32, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, - 198, 141, 6, 79, 243, 146, 1, 76, 86, 68, 2, 52, 69, 82, 53, 206, 2, 54, - 150, 2, 55, 166, 1, 56, 95, 57, 10, 50, 48, 234, 4, 65, 214, 242, 5, 56, - 223, 97, 50, 4, 182, 159, 7, 48, 3, 57, 30, 150, 1, 50, 42, 51, 38, 52, - 48, 2, 53, 66, 0, 2, 68, 69, 22, 57, 164, 4, 2, 56, 70, 222, 162, 3, 66, - 248, 205, 2, 2, 70, 56, 221, 97, 2, 49, 56, 6, 214, 4, 55, 142, 211, 6, - 49, 3, 52, 4, 210, 167, 3, 70, 143, 206, 2, 67, 4, 26, 48, 219, 167, 3, - 51, 2, 155, 157, 7, 56, 2, 135, 157, 7, 54, 4, 174, 167, 3, 50, 143, 243, + 190, 153, 6, 79, 143, 149, 1, 76, 86, 68, 2, 52, 69, 82, 53, 206, 2, 54, + 150, 2, 55, 166, 1, 56, 95, 57, 10, 50, 48, 234, 4, 65, 214, 254, 5, 56, + 243, 99, 50, 4, 202, 173, 7, 48, 3, 57, 30, 150, 1, 50, 42, 51, 38, 52, + 48, 2, 53, 66, 0, 2, 68, 69, 22, 57, 164, 4, 2, 56, 70, 158, 172, 3, 66, + 184, 208, 2, 2, 70, 56, 241, 99, 2, 49, 56, 6, 214, 4, 55, 162, 225, 6, + 49, 3, 52, 4, 146, 177, 3, 70, 207, 208, 2, 67, 4, 26, 48, 155, 177, 3, + 51, 2, 175, 171, 7, 56, 2, 155, 171, 7, 54, 4, 238, 176, 3, 50, 227, 247, 3, 49, 24, 86, 50, 46, 51, 50, 53, 34, 54, 16, 2, 55, 48, 28, 2, 70, 49, - 129, 2, 2, 69, 56, 6, 70, 57, 170, 165, 3, 53, 167, 186, 2, 52, 4, 26, - 53, 131, 166, 3, 48, 2, 175, 155, 7, 53, 4, 202, 2, 66, 143, 163, 3, 57, - 2, 171, 2, 50, 4, 234, 154, 7, 56, 3, 57, 2, 207, 154, 7, 52, 12, 74, 53, - 38, 57, 12, 2, 49, 50, 20, 2, 68, 52, 245, 150, 7, 2, 65, 55, 4, 230, - 163, 3, 51, 231, 244, 2, 49, 2, 11, 56, 2, 191, 153, 7, 49, 2, 171, 153, - 7, 50, 6, 50, 57, 20, 2, 68, 55, 141, 163, 3, 2, 67, 65, 2, 219, 162, 3, - 69, 2, 207, 152, 7, 48, 4, 152, 210, 6, 2, 49, 52, 233, 67, 2, 48, 52, 2, - 11, 79, 2, 143, 216, 3, 84, 10, 84, 3, 65, 76, 76, 104, 4, 79, 85, 82, - 32, 240, 5, 3, 73, 86, 69, 135, 161, 5, 82, 2, 57, 12, 73, 78, 71, 32, - 68, 73, 65, 71, 79, 78, 65, 76, 2, 11, 32, 2, 11, 83, 2, 147, 174, 5, 76, - 4, 230, 211, 3, 68, 199, 194, 3, 75, 8, 212, 213, 3, 2, 73, 45, 214, 135, - 3, 68, 222, 56, 67, 3, 86, 8, 56, 8, 65, 84, 65, 75, 65, 78, 65, 32, 199, - 131, 7, 69, 6, 226, 225, 6, 75, 214, 28, 68, 159, 20, 83, 60, 36, 5, 65, + 129, 2, 2, 69, 56, 6, 70, 57, 234, 174, 3, 53, 231, 188, 2, 52, 4, 26, + 53, 195, 175, 3, 48, 2, 195, 169, 7, 53, 4, 202, 2, 66, 207, 172, 3, 57, + 2, 171, 2, 50, 4, 254, 168, 7, 56, 3, 57, 2, 227, 168, 7, 52, 12, 74, 53, + 38, 57, 12, 2, 49, 50, 20, 2, 68, 52, 137, 165, 7, 2, 65, 55, 4, 166, + 173, 3, 51, 187, 249, 2, 49, 2, 11, 56, 2, 211, 167, 7, 49, 2, 191, 167, + 7, 50, 6, 50, 57, 20, 2, 68, 55, 205, 172, 3, 2, 67, 65, 2, 155, 172, 3, + 69, 2, 227, 166, 7, 48, 4, 172, 224, 6, 2, 49, 52, 233, 67, 2, 48, 52, 2, + 11, 79, 2, 207, 226, 3, 84, 10, 84, 3, 65, 76, 76, 104, 4, 79, 85, 82, + 32, 240, 5, 3, 73, 86, 69, 179, 172, 5, 82, 2, 57, 12, 73, 78, 71, 32, + 68, 73, 65, 71, 79, 78, 65, 76, 2, 11, 32, 2, 11, 83, 2, 191, 185, 5, 76, + 4, 166, 222, 3, 68, 155, 198, 3, 75, 8, 148, 224, 3, 2, 73, 45, 170, 139, + 3, 68, 222, 56, 67, 3, 86, 8, 56, 8, 65, 84, 65, 75, 65, 78, 65, 32, 219, + 145, 7, 69, 6, 246, 239, 6, 75, 214, 28, 68, 159, 20, 83, 60, 36, 5, 65, 84, 73, 78, 32, 79, 79, 54, 164, 5, 12, 83, 77, 65, 76, 76, 32, 76, 69, - 84, 84, 69, 82, 239, 123, 67, 6, 198, 210, 3, 71, 201, 170, 2, 3, 83, 83, - 76, 4, 178, 141, 5, 73, 139, 134, 2, 86, 4, 246, 148, 6, 69, 163, 126, + 84, 84, 69, 82, 167, 123, 67, 6, 134, 221, 3, 71, 153, 172, 2, 3, 83, 83, + 76, 4, 222, 152, 5, 73, 243, 136, 2, 86, 4, 138, 163, 6, 69, 163, 126, 71, 4, 140, 3, 15, 78, 69, 32, 72, 85, 78, 68, 82, 69, 68, 32, 84, 87, - 69, 78, 235, 143, 7, 75, 4, 158, 2, 80, 131, 138, 5, 76, 20, 110, 69, - 146, 1, 72, 20, 2, 73, 88, 134, 251, 4, 65, 186, 66, 77, 222, 49, 81, - 246, 98, 79, 222, 61, 68, 3, 83, 4, 56, 7, 67, 79, 78, 68, 32, 83, 67, - 21, 3, 86, 69, 78, 2, 219, 188, 5, 82, 2, 29, 5, 32, 80, 79, 73, 78, 2, - 11, 84, 2, 203, 229, 5, 32, 2, 251, 143, 7, 86, 2, 17, 2, 84, 89, 2, 199, - 143, 7, 32, 8, 44, 4, 72, 82, 69, 69, 22, 87, 243, 4, 73, 2, 239, 200, 6, - 32, 4, 68, 13, 69, 78, 84, 89, 45, 84, 87, 79, 32, 80, 79, 73, 78, 19, - 79, 2, 223, 86, 84, 2, 139, 139, 7, 32, 4, 48, 6, 80, 32, 87, 73, 84, 72, - 191, 199, 6, 72, 2, 17, 2, 32, 69, 2, 247, 119, 88, 4, 150, 199, 6, 79, - 163, 70, 83, 2, 137, 237, 5, 6, 32, 66, 76, 65, 67, 75, 6, 234, 140, 7, - 50, 2, 51, 3, 65, 93, 134, 1, 65, 178, 6, 69, 236, 1, 10, 73, 67, 75, 32, - 70, 73, 71, 85, 82, 69, 182, 1, 79, 90, 82, 174, 4, 85, 238, 252, 6, 83, - 3, 88, 38, 92, 6, 70, 70, 32, 79, 70, 32, 106, 77, 82, 82, 206, 3, 84, - 210, 133, 3, 78, 243, 214, 2, 68, 4, 60, 8, 65, 69, 83, 67, 85, 76, 65, - 80, 21, 3, 72, 69, 82, 2, 163, 132, 5, 73, 2, 147, 246, 5, 77, 2, 21, 3, - 80, 69, 68, 2, 17, 2, 32, 69, 2, 229, 220, 1, 4, 78, 86, 69, 76, 24, 42, - 32, 205, 1, 5, 84, 32, 79, 70, 32, 12, 82, 69, 54, 79, 180, 235, 1, 8, - 65, 78, 68, 32, 67, 82, 69, 83, 139, 128, 2, 87, 2, 17, 2, 81, 85, 2, 11, - 65, 2, 171, 202, 6, 76, 4, 44, 5, 70, 32, 68, 65, 86, 251, 199, 3, 80, 2, - 163, 193, 6, 73, 12, 66, 71, 30, 83, 40, 4, 80, 82, 79, 84, 242, 77, 72, - 215, 101, 84, 2, 89, 4, 85, 65, 82, 68, 4, 26, 69, 191, 249, 1, 84, 2, - 11, 76, 2, 21, 3, 69, 67, 84, 2, 29, 5, 69, 68, 32, 65, 82, 2, 183, 131, - 7, 69, 4, 188, 152, 5, 10, 85, 69, 32, 79, 70, 32, 76, 73, 66, 69, 179, - 157, 1, 73, 8, 92, 2, 65, 77, 112, 9, 78, 79, 71, 82, 65, 80, 72, 73, 67, - 209, 214, 1, 4, 84, 72, 79, 83, 4, 54, 32, 197, 250, 5, 7, 73, 78, 71, - 32, 66, 79, 87, 2, 33, 6, 76, 79, 67, 79, 77, 79, 2, 143, 208, 1, 84, 2, - 217, 177, 4, 2, 32, 70, 11, 11, 32, 8, 60, 5, 87, 73, 84, 72, 32, 233, - 163, 2, 4, 76, 69, 65, 78, 4, 32, 4, 65, 82, 77, 83, 51, 68, 2, 25, 4, - 32, 82, 65, 73, 2, 203, 246, 1, 83, 2, 167, 235, 5, 82, 4, 44, 4, 67, 75, - 32, 67, 21, 3, 80, 87, 65, 2, 163, 250, 5, 72, 2, 131, 223, 6, 84, 20, - 66, 65, 108, 10, 69, 83, 83, 32, 79, 85, 84, 76, 73, 78, 79, 73, 6, 44, - 4, 73, 71, 72, 84, 45, 3, 87, 66, 69, 4, 184, 150, 3, 2, 32, 82, 143, - 211, 2, 78, 2, 243, 72, 82, 2, 17, 2, 69, 68, 2, 17, 2, 32, 87, 2, 169, - 233, 4, 4, 72, 73, 84, 69, 12, 76, 4, 78, 71, 32, 84, 52, 4, 80, 69, 68, - 32, 241, 8, 4, 67, 84, 76, 89, 2, 17, 2, 69, 82, 2, 181, 197, 6, 3, 77, - 73, 78, 8, 70, 68, 24, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, 13, 2, 85, - 80, 2, 33, 3, 79, 87, 78, 2, 11, 84, 2, 11, 45, 2, 169, 216, 5, 8, 80, - 79, 73, 78, 84, 73, 78, 71, 6, 100, 8, 68, 73, 79, 32, 77, 73, 67, 82, - 20, 9, 70, 70, 69, 68, 32, 70, 76, 65, 84, 187, 249, 6, 80, 2, 167, 209, - 5, 79, 2, 11, 66, 2, 243, 166, 4, 82, 162, 3, 150, 1, 66, 212, 2, 5, 67, - 67, 69, 69, 68, 192, 3, 8, 77, 77, 65, 84, 73, 79, 78, 32, 70, 78, 204, - 22, 3, 80, 69, 82, 204, 11, 2, 82, 70, 47, 83, 63, 11, 83, 60, 76, 6, 67, - 82, 73, 80, 84, 32, 136, 1, 3, 69, 84, 32, 93, 3, 84, 73, 84, 30, 118, - 76, 186, 29, 69, 166, 1, 80, 22, 82, 218, 211, 2, 77, 246, 149, 2, 70, - 30, 83, 42, 84, 62, 90, 238, 85, 78, 15, 79, 2, 207, 30, 69, 28, 56, 6, - 65, 66, 79, 86, 69, 32, 246, 33, 79, 159, 3, 87, 6, 142, 32, 83, 143, - 184, 5, 82, 2, 251, 176, 5, 85, 22, 11, 83, 23, 11, 32, 20, 128, 1, 6, - 65, 66, 79, 86, 69, 32, 156, 1, 7, 66, 85, 84, 32, 78, 79, 84, 32, 2, 79, - 82, 141, 146, 3, 5, 85, 78, 68, 69, 82, 12, 100, 4, 78, 79, 84, 32, 28, - 12, 83, 73, 78, 71, 76, 69, 45, 76, 73, 78, 69, 32, 218, 32, 65, 39, 69, - 4, 242, 32, 65, 167, 1, 69, 4, 250, 32, 69, 83, 78, 2, 93, 5, 32, 69, 81, - 85, 73, 4, 17, 2, 32, 69, 4, 17, 2, 81, 85, 4, 22, 73, 167, 33, 65, 2, - 173, 33, 6, 86, 65, 76, 69, 78, 84, 6, 242, 136, 3, 66, 136, 228, 1, 4, - 87, 73, 84, 72, 147, 142, 1, 84, 249, 1, 194, 1, 32, 72, 7, 68, 65, 78, - 69, 83, 69, 32, 204, 12, 4, 82, 73, 83, 69, 72, 5, 85, 87, 65, 82, 32, - 132, 174, 2, 14, 83, 69, 84, 32, 79, 86, 69, 82, 32, 66, 85, 73, 76, 68, - 159, 157, 3, 70, 4, 50, 87, 225, 197, 5, 6, 66, 69, 72, 73, 78, 68, 2, - 171, 180, 6, 73, 146, 1, 198, 2, 65, 20, 17, 67, 79, 78, 83, 79, 78, 65, - 78, 84, 32, 83, 73, 71, 78, 32, 80, 65, 172, 1, 7, 76, 69, 84, 84, 69, - 82, 32, 188, 3, 18, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 66, - 73, 78, 68, 85, 32, 228, 1, 5, 83, 73, 71, 78, 32, 204, 1, 13, 86, 79, - 87, 69, 76, 32, 83, 73, 71, 78, 32, 80, 65, 183, 245, 4, 68, 2, 171, 152, - 3, 86, 10, 68, 2, 77, 73, 40, 2, 78, 89, 33, 7, 83, 65, 78, 71, 65, 78, - 32, 2, 17, 2, 78, 71, 2, 207, 228, 5, 75, 4, 142, 5, 65, 211, 155, 1, 73, - 4, 162, 235, 6, 77, 3, 87, 76, 246, 1, 65, 58, 70, 78, 76, 2, 82, 34, 83, - 254, 150, 3, 69, 254, 216, 1, 78, 238, 178, 1, 66, 2, 75, 138, 69, 67, 2, - 68, 2, 71, 2, 72, 2, 74, 2, 77, 2, 80, 2, 81, 2, 84, 2, 86, 2, 87, 2, 88, - 2, 89, 2, 90, 186, 2, 73, 2, 79, 3, 85, 7, 180, 171, 6, 6, 82, 67, 72, - 65, 73, 67, 147, 64, 69, 6, 44, 5, 73, 78, 65, 76, 32, 227, 234, 6, 65, - 4, 222, 234, 6, 75, 3, 77, 4, 218, 197, 6, 69, 235, 36, 65, 4, 234, 231, - 6, 89, 187, 2, 65, 16, 90, 66, 2, 68, 2, 75, 12, 3, 76, 69, 85, 38, 67, - 34, 80, 253, 229, 6, 3, 83, 85, 82, 2, 11, 65, 2, 253, 103, 5, 32, 83, - 65, 84, 65, 2, 11, 65, 2, 171, 164, 6, 75, 4, 172, 3, 3, 65, 78, 71, 189, - 162, 6, 3, 85, 82, 78, 10, 32, 2, 80, 65, 235, 145, 3, 86, 8, 28, 3, 77, - 65, 65, 23, 78, 2, 203, 230, 6, 69, 6, 18, 71, 71, 89, 4, 38, 76, 225, - 160, 6, 3, 87, 73, 83, 2, 209, 197, 5, 2, 65, 89, 2, 201, 227, 6, 2, 69, - 67, 12, 18, 77, 23, 78, 2, 187, 134, 3, 69, 10, 96, 3, 69, 85, 76, 34, - 79, 22, 89, 188, 228, 1, 3, 71, 72, 85, 201, 189, 2, 4, 65, 69, 76, 65, - 2, 11, 69, 2, 195, 148, 6, 85, 2, 231, 161, 4, 76, 2, 247, 151, 1, 85, 5, - 165, 161, 3, 13, 32, 79, 86, 69, 82, 32, 77, 79, 85, 78, 84, 65, 73, 88, - 88, 7, 76, 69, 84, 84, 69, 82, 32, 184, 6, 6, 83, 73, 71, 78, 32, 80, - 135, 236, 4, 68, 66, 142, 2, 65, 66, 67, 66, 68, 44, 3, 72, 65, 77, 22, - 74, 34, 75, 42, 78, 38, 79, 2, 85, 34, 80, 38, 82, 20, 4, 83, 72, 89, 69, - 34, 84, 112, 3, 86, 65, 82, 230, 169, 4, 71, 148, 57, 4, 76, 79, 65, 67, - 136, 89, 2, 73, 77, 210, 13, 89, 150, 27, 66, 214, 89, 69, 203, 28, 77, - 6, 42, 80, 238, 215, 5, 65, 255, 134, 1, 86, 2, 251, 248, 1, 80, 4, 40, - 2, 72, 69, 253, 204, 6, 2, 65, 82, 2, 243, 207, 5, 76, 4, 22, 69, 155, - 95, 79, 2, 219, 204, 6, 86, 2, 171, 193, 6, 83, 2, 11, 89, 2, 239, 222, - 6, 65, 6, 194, 172, 6, 76, 146, 48, 73, 99, 72, 4, 138, 190, 5, 71, 171, - 160, 1, 65, 2, 11, 84, 2, 147, 144, 1, 84, 4, 198, 189, 5, 72, 175, 161, - 1, 73, 2, 243, 185, 6, 69, 4, 242, 199, 6, 76, 215, 22, 82, 8, 42, 69, - 22, 72, 209, 187, 6, 2, 65, 83, 2, 171, 142, 1, 78, 4, 26, 65, 167, 185, - 5, 69, 2, 243, 201, 6, 82, 2, 255, 218, 6, 67, 2, 175, 190, 6, 86, 72, - 54, 83, 182, 237, 4, 72, 209, 66, 4, 86, 73, 76, 76, 68, 56, 6, 67, 82, - 73, 80, 84, 32, 229, 2, 3, 69, 84, 32, 34, 114, 69, 34, 76, 134, 1, 80, - 22, 82, 218, 211, 2, 77, 246, 149, 2, 70, 30, 83, 42, 84, 62, 90, 238, - 85, 78, 15, 79, 4, 174, 69, 81, 183, 235, 5, 73, 6, 88, 18, 65, 84, 73, - 78, 32, 83, 77, 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, 32, 31, 69, 4, - 170, 218, 6, 73, 3, 78, 2, 55, 70, 2, 191, 171, 3, 76, 2, 21, 3, 73, 71, - 72, 2, 11, 84, 2, 139, 215, 2, 32, 34, 148, 1, 6, 65, 66, 79, 86, 69, 32, - 96, 7, 66, 69, 83, 73, 68, 69, 32, 162, 1, 79, 158, 3, 87, 221, 189, 4, - 9, 80, 82, 69, 67, 69, 68, 73, 78, 71, 6, 26, 83, 135, 188, 2, 76, 4, 11, - 85, 4, 26, 80, 151, 236, 4, 66, 2, 145, 236, 4, 2, 69, 82, 4, 108, 23, - 65, 78, 68, 32, 74, 79, 73, 78, 69, 68, 32, 66, 89, 32, 68, 65, 83, 72, - 32, 87, 73, 84, 72, 23, 83, 2, 17, 2, 32, 83, 2, 241, 234, 4, 2, 85, 66, - 16, 11, 70, 17, 11, 32, 14, 120, 6, 65, 66, 79, 86, 69, 32, 132, 1, 4, - 87, 73, 84, 72, 249, 210, 5, 11, 79, 82, 32, 69, 81, 85, 65, 76, 32, 84, - 79, 8, 34, 65, 38, 69, 18, 84, 67, 78, 2, 11, 76, 2, 113, 3, 77, 79, 83, - 2, 203, 62, 81, 2, 21, 3, 73, 76, 68, 2, 171, 148, 3, 69, 2, 17, 2, 32, - 78, 2, 11, 79, 2, 11, 84, 2, 11, 32, 2, 11, 69, 2, 17, 2, 81, 85, 2, 11, - 65, 2, 11, 76, 2, 219, 228, 2, 32, 6, 25, 4, 73, 84, 72, 32, 6, 96, 2, - 80, 76, 24, 14, 77, 85, 76, 84, 73, 80, 76, 73, 67, 65, 84, 73, 79, 78, - 183, 129, 6, 68, 2, 11, 85, 2, 11, 83, 2, 165, 164, 4, 5, 32, 83, 73, 71, - 78, 4, 144, 202, 4, 2, 65, 67, 215, 206, 1, 69, 4, 60, 9, 80, 69, 78, 83, - 73, 79, 78, 32, 82, 131, 189, 6, 72, 2, 21, 3, 65, 73, 76, 2, 187, 196, - 5, 87, 8, 26, 65, 98, 73, 35, 85, 4, 44, 5, 83, 72, 32, 65, 77, 227, 207, - 6, 78, 2, 253, 139, 6, 7, 80, 69, 82, 83, 65, 78, 68, 2, 11, 77, 2, 183, - 145, 6, 77, 2, 233, 145, 3, 2, 78, 71, 236, 2, 92, 11, 76, 79, 84, 73, - 32, 78, 65, 71, 82, 73, 32, 226, 5, 77, 194, 17, 78, 97, 2, 82, 73, 90, - 180, 1, 7, 76, 69, 84, 84, 69, 82, 32, 168, 2, 11, 80, 79, 69, 84, 82, - 89, 32, 77, 65, 82, 75, 56, 5, 83, 73, 71, 78, 32, 145, 1, 11, 86, 79, - 87, 69, 76, 32, 83, 73, 71, 78, 32, 64, 174, 1, 68, 46, 82, 34, 84, 166, - 91, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 198, 208, 5, 72, 2, 76, 2, 77, - 2, 78, 2, 83, 246, 30, 65, 2, 69, 2, 73, 2, 79, 3, 85, 8, 238, 91, 68, - 198, 208, 5, 72, 247, 30, 79, 4, 134, 172, 6, 82, 247, 30, 79, 8, 162, - 91, 84, 198, 208, 5, 72, 247, 30, 79, 8, 11, 45, 8, 162, 202, 6, 49, 2, - 50, 2, 51, 3, 52, 8, 46, 65, 70, 72, 165, 132, 6, 3, 68, 86, 73, 4, 64, - 10, 76, 84, 69, 82, 78, 65, 84, 69, 32, 72, 155, 132, 6, 78, 2, 193, 137, - 2, 2, 65, 83, 10, 242, 169, 6, 79, 246, 30, 65, 2, 69, 2, 73, 3, 85, 88, - 60, 8, 66, 79, 76, 32, 70, 79, 82, 32, 225, 16, 2, 77, 69, 86, 238, 2, - 66, 48, 2, 67, 65, 118, 68, 234, 3, 69, 130, 2, 70, 56, 8, 72, 79, 82, - 73, 90, 79, 78, 84, 0, 6, 86, 69, 82, 84, 73, 67, 44, 3, 76, 73, 78, 52, - 9, 77, 65, 82, 75, 83, 32, 67, 72, 65, 22, 78, 90, 65, 68, 3, 82, 69, 67, - 32, 5, 71, 82, 79, 85, 80, 0, 4, 85, 78, 73, 84, 22, 83, 205, 3, 15, 84, - 89, 80, 69, 32, 65, 32, 69, 76, 69, 67, 84, 82, 79, 78, 4, 190, 189, 4, - 69, 137, 201, 1, 3, 65, 67, 75, 4, 44, 3, 82, 82, 73, 189, 183, 1, 2, 78, - 67, 2, 33, 6, 65, 71, 69, 32, 82, 69, 2, 11, 84, 2, 215, 185, 1, 85, 20, - 24, 2, 65, 84, 55, 69, 2, 173, 5, 9, 65, 32, 76, 73, 78, 75, 32, 69, 83, - 18, 84, 4, 76, 69, 84, 69, 133, 2, 12, 86, 73, 67, 69, 32, 67, 79, 78, - 84, 82, 79, 76, 11, 11, 32, 8, 160, 1, 11, 82, 69, 67, 84, 65, 78, 71, - 85, 76, 65, 82, 0, 6, 83, 81, 85, 65, 82, 69, 154, 9, 70, 129, 185, 5, - 11, 77, 69, 68, 73, 85, 77, 32, 83, 72, 65, 68, 2, 61, 13, 32, 67, 72, - 69, 67, 75, 69, 82, 32, 66, 79, 65, 82, 2, 227, 193, 5, 68, 8, 11, 32, 8, - 162, 173, 1, 70, 162, 163, 3, 84, 183, 86, 79, 12, 22, 78, 207, 1, 83, - 10, 48, 5, 68, 32, 79, 70, 32, 137, 1, 2, 81, 85, 8, 18, 77, 31, 84, 2, - 245, 149, 5, 2, 69, 68, 6, 64, 11, 82, 65, 78, 83, 77, 73, 83, 83, 73, - 79, 78, 223, 107, 69, 5, 235, 207, 3, 32, 2, 155, 7, 73, 2, 209, 246, 4, - 2, 67, 65, 4, 36, 2, 73, 76, 73, 3, 79, 82, 77, 2, 191, 2, 69, 2, 229, - 160, 1, 6, 65, 76, 32, 84, 65, 66, 2, 11, 69, 2, 17, 2, 32, 70, 2, 163, - 177, 1, 69, 2, 227, 132, 5, 80, 6, 26, 69, 239, 180, 4, 85, 4, 56, 8, 71, - 65, 84, 73, 86, 69, 32, 65, 175, 162, 5, 87, 2, 21, 3, 67, 75, 78, 2, 21, - 3, 79, 87, 76, 2, 231, 135, 1, 69, 2, 11, 79, 2, 17, 2, 82, 68, 2, 163, - 141, 3, 32, 18, 180, 1, 7, 65, 77, 65, 82, 73, 84, 65, 60, 3, 72, 73, 70, - 52, 8, 84, 65, 82, 84, 32, 79, 70, 32, 64, 8, 85, 66, 83, 84, 73, 84, 85, - 84, 200, 1, 3, 89, 78, 67, 195, 249, 5, 80, 2, 25, 4, 78, 32, 83, 79, 2, - 11, 85, 2, 155, 252, 5, 82, 4, 17, 2, 84, 32, 4, 158, 191, 5, 79, 243, - 41, 73, 4, 22, 72, 215, 101, 84, 2, 17, 2, 69, 65, 2, 159, 231, 5, 68, 4, - 11, 69, 5, 11, 32, 2, 11, 70, 2, 21, 3, 79, 82, 77, 2, 17, 2, 32, 84, 2, - 203, 152, 6, 87, 2, 205, 249, 5, 2, 73, 67, 2, 11, 84, 2, 187, 165, 6, - 82, 7, 38, 67, 181, 171, 2, 3, 65, 71, 79, 2, 181, 133, 1, 9, 72, 82, 79, - 78, 79, 85, 83, 32, 73, 180, 1, 36, 3, 65, 67, 32, 199, 128, 5, 78, 178, - 1, 150, 3, 65, 52, 4, 66, 65, 82, 82, 32, 2, 67, 79, 80, 13, 68, 79, 84, - 84, 69, 68, 32, 90, 76, 65, 77, 65, 32, 118, 69, 90, 72, 204, 2, 7, 76, - 69, 84, 84, 69, 82, 32, 236, 9, 9, 79, 66, 76, 73, 81, 85, 69, 32, 76, - 28, 4, 80, 84, 72, 65, 0, 4, 90, 81, 65, 80, 110, 82, 88, 2, 83, 85, 174, - 2, 84, 140, 247, 4, 3, 77, 85, 83, 232, 22, 7, 70, 69, 77, 73, 78, 73, - 78, 253, 136, 1, 5, 81, 85, 83, 72, 83, 2, 11, 66, 2, 133, 201, 5, 5, 66, - 82, 69, 86, 73, 2, 11, 69, 2, 247, 176, 6, 75, 6, 38, 78, 165, 17, 4, 76, - 79, 78, 32, 2, 17, 2, 84, 82, 2, 219, 224, 5, 65, 4, 28, 3, 65, 78, 71, - 35, 72, 2, 11, 85, 2, 191, 143, 5, 76, 2, 11, 79, 2, 169, 220, 3, 5, 82, - 73, 90, 79, 78, 6, 60, 10, 78, 68, 32, 79, 70, 32, 80, 65, 82, 65, 151, - 14, 83, 2, 201, 11, 2, 71, 82, 14, 104, 8, 65, 82, 75, 76, 69, 65, 78, - 32, 132, 1, 4, 66, 65, 83, 65, 65, 7, 79, 82, 73, 90, 79, 78, 84, 6, 60, - 5, 65, 83, 84, 69, 82, 40, 4, 77, 69, 84, 79, 3, 79, 2, 17, 2, 73, 83, 2, - 155, 168, 4, 67, 2, 201, 167, 2, 2, 66, 69, 6, 164, 11, 8, 45, 69, 83, - 65, 83, 65, 32, 68, 151, 132, 4, 32, 2, 137, 221, 5, 2, 65, 76, 92, 154, - 2, 68, 102, 72, 32, 3, 76, 65, 77, 34, 77, 204, 1, 2, 80, 69, 142, 1, 82, - 78, 83, 144, 1, 8, 70, 73, 78, 65, 76, 32, 83, 69, 106, 65, 14, 75, 2, - 81, 34, 84, 40, 5, 71, 65, 77, 65, 76, 40, 4, 89, 85, 68, 72, 214, 74, - 66, 142, 172, 4, 90, 166, 44, 78, 134, 2, 87, 175, 126, 69, 4, 76, 14, - 79, 84, 76, 69, 83, 83, 32, 68, 65, 76, 65, 84, 72, 32, 139, 3, 65, 2, - 167, 201, 2, 82, 4, 11, 69, 5, 239, 168, 6, 84, 2, 11, 65, 2, 207, 168, - 6, 68, 24, 60, 9, 65, 76, 65, 89, 65, 76, 65, 77, 32, 183, 152, 6, 73, - 22, 78, 76, 22, 78, 234, 173, 4, 66, 246, 213, 1, 84, 138, 34, 83, 14, - 74, 3, 82, 4, 135, 159, 4, 76, 8, 246, 83, 78, 250, 209, 5, 71, 3, 89, 9, - 33, 6, 82, 83, 73, 65, 78, 32, 6, 50, 66, 16, 3, 68, 72, 65, 17, 3, 71, - 72, 65, 2, 199, 78, 72, 2, 147, 2, 76, 2, 199, 157, 5, 77, 4, 52, 7, 69, - 86, 69, 82, 83, 69, 68, 159, 190, 4, 73, 2, 255, 222, 4, 32, 14, 142, 1, - 69, 40, 7, 79, 71, 68, 73, 65, 78, 32, 64, 12, 85, 80, 69, 82, 83, 67, - 82, 73, 80, 84, 32, 65, 222, 164, 5, 72, 201, 82, 2, 65, 68, 2, 17, 2, - 77, 75, 2, 243, 162, 4, 65, 6, 42, 90, 32, 2, 75, 72, 211, 141, 6, 70, 2, - 239, 247, 4, 72, 2, 11, 76, 2, 11, 65, 2, 135, 163, 6, 80, 6, 36, 3, 69, - 84, 72, 191, 165, 5, 65, 5, 173, 85, 6, 32, 71, 65, 82, 83, 72, 5, 167, - 246, 5, 32, 4, 217, 132, 4, 2, 73, 78, 6, 21, 3, 72, 65, 32, 6, 42, 68, - 178, 132, 4, 66, 167, 161, 1, 65, 2, 17, 2, 79, 84, 2, 223, 150, 1, 84, - 8, 58, 66, 162, 203, 2, 87, 249, 166, 1, 4, 85, 75, 75, 65, 4, 157, 240, - 3, 2, 65, 83, 14, 88, 8, 66, 76, 73, 78, 69, 65, 82, 32, 113, 10, 80, 82, - 65, 76, 73, 78, 69, 65, 82, 32, 8, 44, 5, 67, 79, 76, 79, 78, 211, 206, - 3, 70, 7, 11, 32, 4, 29, 5, 83, 75, 69, 87, 69, 4, 251, 244, 5, 68, 6, - 44, 5, 67, 79, 76, 79, 78, 227, 205, 3, 70, 5, 253, 236, 5, 7, 32, 83, - 75, 69, 87, 69, 68, 8, 62, 72, 25, 11, 87, 79, 32, 86, 69, 82, 84, 73, - 67, 65, 76, 4, 21, 3, 82, 69, 69, 4, 25, 4, 32, 68, 79, 84, 4, 231, 255, - 3, 83, 190, 43, 102, 45, 58, 65, 206, 105, 69, 154, 45, 72, 182, 46, 73, - 230, 69, 79, 170, 33, 82, 214, 16, 85, 147, 22, 87, 4, 32, 2, 83, 72, - 255, 246, 4, 82, 2, 235, 149, 5, 73, 244, 26, 182, 1, 66, 82, 71, 196, - 17, 2, 73, 32, 170, 29, 75, 144, 5, 9, 76, 76, 89, 32, 77, 65, 82, 75, - 32, 34, 77, 178, 37, 78, 132, 13, 3, 80, 69, 32, 94, 85, 190, 148, 5, 67, - 159, 11, 88, 5, 221, 122, 16, 76, 69, 32, 84, 69, 78, 78, 73, 83, 32, 80, - 65, 68, 68, 76, 69, 144, 2, 78, 32, 240, 11, 5, 65, 76, 79, 71, 32, 241, - 2, 6, 66, 65, 78, 87, 65, 32, 190, 1, 162, 1, 65, 102, 67, 186, 1, 68, - 58, 69, 98, 71, 118, 72, 46, 76, 230, 3, 80, 58, 81, 62, 82, 106, 83, - 142, 76, 78, 230, 222, 1, 84, 250, 145, 1, 70, 231, 183, 1, 86, 6, 42, - 80, 150, 201, 2, 77, 219, 187, 1, 83, 2, 17, 2, 79, 83, 2, 145, 235, 5, - 4, 84, 82, 79, 80, 8, 18, 73, 63, 79, 2, 25, 4, 82, 67, 85, 77, 2, 213, - 2, 4, 70, 76, 69, 88, 6, 26, 77, 167, 199, 5, 76, 4, 11, 77, 4, 152, 131, - 5, 7, 69, 82, 67, 73, 65, 76, 32, 235, 147, 1, 65, 22, 26, 79, 151, 165, - 4, 73, 2, 11, 76, 2, 235, 84, 76, 4, 18, 81, 43, 88, 2, 17, 2, 85, 65, 2, - 171, 231, 2, 76, 2, 153, 172, 5, 4, 67, 76, 65, 77, 4, 11, 82, 4, 18, 65, - 55, 69, 2, 11, 86, 2, 11, 69, 2, 177, 120, 3, 32, 65, 67, 2, 145, 4, 4, - 65, 84, 69, 82, 2, 145, 142, 2, 6, 89, 80, 72, 69, 78, 45, 114, 38, 65, - 254, 2, 69, 247, 168, 4, 79, 104, 25, 4, 84, 73, 78, 32, 104, 34, 67, 41, - 4, 83, 77, 65, 76, 52, 11, 65, 52, 25, 4, 80, 73, 84, 65, 52, 41, 8, 76, - 32, 76, 69, 84, 84, 69, 82, 52, 11, 32, 52, 194, 146, 6, 65, 2, 66, 2, - 67, 2, 68, 2, 69, 2, 70, 2, 71, 2, 72, 2, 73, 2, 74, 2, 75, 2, 76, 2, 77, - 2, 78, 2, 79, 2, 80, 2, 81, 2, 82, 2, 83, 2, 84, 2, 85, 2, 86, 2, 87, 2, - 88, 2, 89, 3, 90, 8, 22, 83, 199, 1, 70, 2, 11, 83, 2, 197, 63, 3, 45, - 84, 72, 4, 26, 69, 203, 225, 2, 76, 2, 11, 82, 2, 227, 67, 67, 4, 11, 85, - 4, 26, 79, 203, 190, 3, 69, 2, 147, 166, 5, 84, 8, 36, 3, 73, 71, 72, - 199, 249, 3, 69, 6, 17, 2, 84, 32, 6, 242, 136, 2, 67, 210, 3, 80, 239, - 7, 83, 6, 142, 195, 2, 69, 146, 184, 1, 79, 167, 214, 1, 80, 46, 80, 7, - 76, 69, 84, 84, 69, 82, 32, 208, 1, 5, 83, 73, 71, 78, 32, 147, 2, 86, - 38, 162, 1, 65, 242, 154, 2, 78, 250, 238, 3, 66, 2, 68, 2, 71, 2, 72, 2, - 75, 2, 76, 2, 77, 2, 80, 2, 82, 2, 83, 2, 84, 2, 87, 2, 89, 186, 2, 73, - 3, 85, 5, 197, 193, 5, 6, 82, 67, 72, 65, 73, 67, 4, 26, 80, 199, 181, 2, - 86, 2, 25, 4, 65, 77, 85, 68, 2, 227, 212, 2, 80, 36, 48, 7, 76, 69, 84, - 84, 69, 82, 32, 147, 1, 86, 32, 194, 153, 2, 78, 250, 238, 3, 66, 2, 68, - 2, 71, 2, 75, 2, 76, 2, 77, 2, 80, 2, 83, 2, 84, 2, 87, 2, 89, 186, 2, - 65, 2, 73, 3, 85, 4, 41, 8, 79, 87, 69, 76, 32, 83, 73, 71, 4, 11, 78, 4, - 243, 200, 5, 32, 212, 3, 112, 10, 76, 69, 32, 76, 69, 84, 84, 69, 82, 32, - 248, 2, 5, 84, 72, 65, 77, 32, 189, 18, 5, 86, 73, 69, 84, 32, 70, 186, - 1, 65, 34, 69, 30, 84, 250, 148, 2, 78, 210, 171, 1, 79, 162, 254, 1, 75, - 2, 80, 162, 7, 85, 234, 61, 70, 2, 72, 2, 76, 2, 77, 2, 81, 2, 83, 2, 86, - 2, 88, 2, 89, 187, 2, 73, 7, 142, 240, 5, 85, 215, 22, 73, 7, 194, 134, - 6, 69, 3, 72, 18, 60, 3, 79, 78, 69, 170, 190, 5, 83, 138, 69, 72, 187, - 2, 65, 10, 11, 45, 10, 218, 133, 6, 50, 2, 51, 2, 52, 2, 53, 3, 54, 254, - 1, 196, 1, 2, 67, 79, 240, 3, 4, 72, 79, 82, 65, 0, 4, 84, 72, 65, 77, - 28, 7, 76, 69, 84, 84, 69, 82, 32, 220, 4, 5, 83, 73, 71, 78, 32, 213, 5, - 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 20, 164, 1, 13, 78, 83, - 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 161, 241, 4, 21, 77, 66, 73, - 78, 73, 78, 71, 32, 67, 82, 89, 80, 84, 79, 71, 82, 65, 77, 77, 73, 67, - 18, 148, 1, 6, 70, 73, 78, 65, 76, 32, 22, 76, 56, 16, 72, 73, 71, 72, - 32, 82, 65, 84, 72, 65, 32, 79, 82, 32, 76, 79, 22, 77, 134, 254, 5, 66, - 3, 83, 2, 215, 190, 5, 78, 4, 54, 79, 213, 149, 1, 7, 65, 32, 84, 65, 78, - 71, 32, 2, 187, 214, 1, 87, 6, 48, 6, 69, 68, 73, 65, 76, 32, 139, 128, - 6, 65, 4, 206, 253, 5, 76, 3, 82, 20, 201, 142, 4, 2, 32, 68, 106, 188, - 1, 2, 71, 82, 44, 5, 72, 73, 71, 72, 32, 94, 76, 222, 1, 82, 134, 168, 2, - 85, 206, 201, 1, 73, 178, 15, 78, 186, 219, 1, 79, 162, 8, 69, 158, 20, - 66, 2, 68, 2, 77, 2, 87, 187, 2, 65, 2, 21, 3, 69, 65, 84, 2, 179, 251, - 5, 32, 32, 242, 1, 75, 42, 82, 186, 179, 5, 83, 82, 67, 2, 80, 2, 84, - 138, 69, 70, 2, 72, 3, 89, 36, 60, 3, 79, 87, 32, 170, 188, 5, 65, 206, - 41, 85, 159, 20, 76, 28, 86, 75, 42, 82, 138, 180, 5, 67, 2, 80, 2, 84, - 138, 69, 70, 2, 72, 2, 83, 3, 89, 6, 182, 249, 5, 72, 2, 88, 187, 2, 65, - 2, 133, 129, 4, 2, 65, 84, 8, 26, 65, 191, 228, 5, 85, 7, 214, 248, 5, - 78, 3, 84, 50, 178, 1, 72, 34, 75, 176, 1, 4, 77, 65, 73, 32, 82, 82, - 136, 1, 2, 83, 65, 92, 5, 87, 73, 65, 78, 71, 188, 126, 3, 68, 79, 75, - 224, 117, 3, 84, 79, 78, 205, 168, 3, 2, 67, 65, 4, 198, 168, 5, 65, 179, - 63, 79, 14, 52, 4, 72, 85, 69, 78, 134, 3, 65, 195, 219, 4, 69, 8, 80, 6, - 32, 84, 79, 78, 69, 45, 149, 174, 4, 8, 45, 76, 85, 69, 32, 75, 65, 82, - 6, 146, 248, 5, 51, 2, 52, 3, 53, 8, 56, 4, 75, 65, 78, 71, 134, 135, 1, - 89, 183, 181, 3, 83, 5, 183, 140, 1, 32, 4, 92, 3, 65, 32, 72, 21, 16, - 69, 86, 69, 82, 83, 69, 68, 32, 82, 79, 84, 65, 84, 69, 68, 32, 2, 195, - 187, 4, 65, 2, 155, 198, 3, 82, 8, 48, 3, 84, 75, 65, 230, 171, 4, 87, - 203, 121, 75, 4, 17, 2, 65, 78, 5, 199, 131, 4, 75, 5, 245, 191, 3, 2, - 87, 65, 38, 90, 65, 36, 4, 77, 65, 73, 32, 22, 79, 46, 84, 86, 85, 178, - 233, 3, 73, 223, 137, 2, 69, 9, 194, 244, 5, 65, 2, 69, 3, 73, 2, 183, - 224, 4, 83, 11, 210, 213, 3, 65, 186, 158, 2, 79, 3, 89, 4, 42, 65, 197, - 136, 1, 4, 72, 65, 77, 32, 2, 17, 2, 76, 76, 2, 211, 128, 4, 32, 9, 234, - 178, 5, 85, 163, 64, 69, 144, 1, 184, 1, 7, 76, 69, 84, 84, 69, 82, 32, - 188, 2, 5, 77, 65, 73, 32, 75, 32, 7, 83, 89, 77, 66, 79, 76, 32, 116, 9, + 69, 78, 255, 157, 7, 75, 4, 158, 2, 80, 175, 149, 5, 76, 20, 110, 69, + 146, 1, 72, 20, 2, 73, 88, 190, 133, 5, 65, 250, 67, 77, 246, 49, 81, + 250, 100, 79, 222, 61, 68, 3, 83, 4, 56, 7, 67, 79, 78, 68, 32, 83, 67, + 21, 3, 86, 69, 78, 2, 211, 200, 5, 82, 2, 29, 5, 32, 80, 79, 73, 78, 2, + 11, 84, 2, 203, 241, 5, 32, 2, 143, 158, 7, 86, 2, 17, 2, 84, 89, 2, 219, + 157, 7, 32, 8, 44, 4, 72, 82, 69, 69, 22, 87, 247, 4, 73, 2, 131, 215, 6, + 32, 4, 68, 13, 69, 78, 84, 89, 45, 84, 87, 79, 32, 80, 79, 73, 78, 23, + 79, 2, 251, 135, 5, 84, 2, 155, 153, 7, 32, 4, 48, 6, 80, 32, 87, 73, 84, + 72, 207, 213, 6, 72, 2, 17, 2, 32, 69, 2, 171, 119, 88, 4, 166, 213, 6, + 79, 163, 70, 83, 2, 149, 249, 5, 6, 32, 66, 76, 65, 67, 75, 6, 250, 154, + 7, 50, 2, 51, 3, 65, 95, 134, 1, 65, 178, 6, 69, 236, 1, 10, 73, 67, 75, + 32, 70, 73, 71, 85, 82, 69, 182, 1, 79, 90, 82, 190, 4, 85, 238, 138, 7, + 83, 3, 88, 38, 92, 6, 70, 70, 32, 79, 70, 32, 106, 77, 82, 82, 206, 3, + 84, 142, 143, 3, 78, 179, 217, 2, 68, 4, 60, 8, 65, 69, 83, 67, 85, 76, + 65, 80, 21, 3, 72, 69, 82, 2, 203, 143, 5, 73, 2, 151, 130, 6, 77, 2, 21, + 3, 80, 69, 68, 2, 17, 2, 32, 69, 2, 233, 225, 1, 4, 78, 86, 69, 76, 24, + 42, 32, 205, 1, 5, 84, 32, 79, 70, 32, 12, 82, 69, 54, 79, 248, 240, 1, + 8, 65, 78, 68, 32, 67, 82, 69, 83, 131, 133, 2, 87, 2, 17, 2, 81, 85, 2, + 11, 65, 2, 187, 216, 6, 76, 4, 44, 5, 70, 32, 68, 65, 86, 183, 210, 3, + 80, 2, 179, 207, 6, 73, 12, 66, 71, 30, 83, 40, 4, 80, 82, 79, 84, 242, + 77, 72, 239, 106, 84, 2, 89, 4, 85, 65, 82, 68, 4, 26, 69, 131, 255, 1, + 84, 2, 11, 76, 2, 21, 3, 69, 67, 84, 2, 29, 5, 69, 68, 32, 65, 82, 2, + 199, 145, 7, 69, 4, 228, 163, 5, 10, 85, 69, 32, 79, 70, 32, 76, 73, 66, + 69, 155, 160, 1, 73, 8, 92, 2, 65, 77, 112, 9, 78, 79, 71, 82, 65, 80, + 72, 73, 67, 213, 219, 1, 4, 84, 72, 79, 83, 4, 54, 32, 185, 134, 6, 7, + 73, 78, 71, 32, 66, 79, 87, 2, 33, 6, 76, 79, 67, 79, 77, 79, 2, 147, + 213, 1, 84, 2, 153, 188, 4, 2, 32, 70, 11, 11, 32, 8, 60, 5, 87, 73, 84, + 72, 32, 193, 169, 2, 4, 76, 69, 65, 78, 4, 32, 4, 65, 82, 77, 83, 51, 68, + 2, 25, 4, 32, 82, 65, 73, 2, 143, 252, 1, 83, 2, 179, 247, 5, 82, 4, 44, + 4, 67, 75, 32, 67, 21, 3, 80, 87, 65, 2, 179, 136, 6, 72, 2, 147, 237, 6, + 84, 22, 66, 65, 124, 10, 69, 83, 83, 32, 79, 85, 84, 76, 73, 78, 79, 73, + 8, 56, 4, 73, 71, 72, 84, 45, 6, 87, 66, 69, 82, 82, 89, 4, 196, 160, 3, + 2, 32, 82, 131, 213, 2, 78, 5, 195, 248, 5, 32, 2, 17, 2, 69, 68, 2, 17, + 2, 32, 87, 2, 205, 243, 4, 4, 72, 73, 84, 69, 12, 76, 4, 78, 71, 32, 84, + 52, 4, 80, 69, 68, 32, 241, 8, 4, 67, 84, 76, 89, 2, 17, 2, 69, 82, 2, + 181, 211, 6, 3, 77, 73, 78, 8, 70, 68, 24, 3, 76, 69, 70, 0, 4, 82, 73, + 71, 72, 13, 2, 85, 80, 2, 33, 3, 79, 87, 78, 2, 11, 84, 2, 11, 45, 2, + 149, 228, 5, 8, 80, 79, 73, 78, 84, 73, 78, 71, 6, 100, 8, 68, 73, 79, + 32, 77, 73, 67, 82, 20, 9, 70, 70, 69, 68, 32, 70, 76, 65, 84, 187, 135, + 7, 80, 2, 147, 221, 5, 79, 2, 11, 66, 2, 163, 177, 4, 82, 162, 3, 150, 1, + 66, 212, 2, 5, 67, 67, 69, 69, 68, 192, 3, 8, 77, 77, 65, 84, 73, 79, 78, + 32, 70, 78, 204, 22, 3, 80, 69, 82, 188, 11, 2, 82, 70, 47, 83, 63, 11, + 83, 60, 76, 6, 67, 82, 73, 80, 84, 32, 136, 1, 3, 69, 84, 32, 93, 3, 84, + 73, 84, 30, 118, 76, 186, 29, 69, 166, 1, 80, 22, 82, 134, 221, 2, 77, + 226, 151, 2, 70, 30, 83, 42, 84, 62, 90, 210, 86, 78, 15, 79, 2, 207, 30, + 69, 28, 56, 6, 65, 66, 79, 86, 69, 32, 246, 33, 79, 159, 3, 87, 6, 142, + 32, 83, 139, 196, 5, 82, 2, 223, 188, 5, 85, 22, 11, 83, 23, 11, 32, 20, + 128, 1, 6, 65, 66, 79, 86, 69, 32, 156, 1, 7, 66, 85, 84, 32, 78, 79, 84, + 32, 2, 79, 82, 149, 156, 3, 5, 85, 78, 68, 69, 82, 12, 100, 4, 78, 79, + 84, 32, 28, 12, 83, 73, 78, 71, 76, 69, 45, 76, 73, 78, 69, 32, 218, 32, + 65, 39, 69, 4, 242, 32, 65, 167, 1, 69, 4, 250, 32, 69, 83, 78, 2, 93, 5, + 32, 69, 81, 85, 73, 4, 17, 2, 32, 69, 4, 17, 2, 81, 85, 4, 22, 73, 167, + 33, 65, 2, 173, 33, 6, 86, 65, 76, 69, 78, 84, 6, 250, 146, 3, 66, 152, + 229, 1, 4, 87, 73, 84, 72, 251, 144, 1, 84, 249, 1, 194, 1, 32, 72, 7, + 68, 65, 78, 69, 83, 69, 32, 204, 12, 4, 82, 73, 83, 69, 72, 5, 85, 87, + 65, 82, 32, 168, 179, 2, 14, 83, 69, 84, 32, 79, 86, 69, 82, 32, 66, 85, + 73, 76, 68, 247, 163, 3, 70, 4, 50, 87, 205, 209, 5, 6, 66, 69, 72, 73, + 78, 68, 2, 171, 194, 6, 73, 146, 1, 198, 2, 65, 20, 17, 67, 79, 78, 83, + 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 80, 65, 172, 1, 7, 76, 69, + 84, 84, 69, 82, 32, 188, 3, 18, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, + 78, 32, 66, 73, 78, 68, 85, 32, 228, 1, 5, 83, 73, 71, 78, 32, 204, 1, + 13, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 80, 65, 207, 128, 5, 68, + 2, 215, 162, 3, 86, 10, 68, 2, 77, 73, 40, 2, 78, 89, 33, 7, 83, 65, 78, + 71, 65, 78, 32, 2, 17, 2, 78, 71, 2, 179, 240, 5, 75, 4, 142, 5, 65, 219, + 160, 1, 73, 4, 162, 249, 6, 77, 3, 87, 76, 246, 1, 65, 58, 70, 78, 76, 2, + 82, 34, 83, 170, 161, 3, 69, 234, 217, 1, 78, 214, 181, 1, 66, 2, 75, + 138, 69, 67, 2, 68, 2, 71, 2, 72, 2, 74, 2, 77, 2, 80, 2, 81, 2, 84, 2, + 86, 2, 87, 2, 88, 2, 89, 2, 90, 186, 2, 73, 2, 79, 3, 85, 7, 180, 185, 6, + 6, 82, 67, 72, 65, 73, 67, 147, 64, 69, 6, 44, 5, 73, 78, 65, 76, 32, + 227, 248, 6, 65, 4, 222, 248, 6, 75, 3, 77, 4, 218, 211, 6, 69, 235, 36, + 65, 4, 234, 245, 6, 89, 187, 2, 65, 16, 90, 66, 2, 68, 2, 75, 12, 3, 76, + 69, 85, 38, 67, 34, 80, 253, 243, 6, 3, 83, 85, 82, 2, 11, 65, 2, 189, + 103, 5, 32, 83, 65, 84, 65, 2, 11, 65, 2, 171, 178, 6, 75, 4, 172, 3, 3, + 65, 78, 71, 189, 176, 6, 3, 85, 82, 78, 10, 32, 2, 80, 65, 151, 156, 3, + 86, 8, 28, 3, 77, 65, 65, 23, 78, 2, 203, 244, 6, 69, 6, 18, 71, 71, 89, + 4, 38, 76, 225, 174, 6, 3, 87, 73, 83, 2, 205, 209, 5, 2, 65, 89, 2, 201, + 241, 6, 2, 69, 67, 12, 18, 77, 23, 78, 2, 231, 144, 3, 69, 10, 96, 3, 69, + 85, 76, 34, 79, 22, 89, 232, 233, 1, 3, 71, 72, 85, 205, 194, 2, 4, 65, + 69, 76, 65, 2, 11, 69, 2, 195, 162, 6, 85, 2, 151, 172, 4, 76, 2, 255, + 156, 1, 85, 5, 209, 171, 3, 13, 32, 79, 86, 69, 82, 32, 77, 79, 85, 78, + 84, 65, 73, 88, 88, 7, 76, 69, 84, 84, 69, 82, 32, 184, 6, 6, 83, 73, 71, + 78, 32, 80, 159, 247, 4, 68, 66, 142, 2, 65, 66, 67, 66, 68, 44, 3, 72, + 65, 77, 22, 74, 34, 75, 42, 78, 38, 79, 2, 85, 34, 80, 38, 82, 20, 4, 83, + 72, 89, 69, 34, 84, 112, 3, 86, 65, 82, 144, 238, 4, 4, 76, 79, 65, 67, + 218, 29, 71, 148, 60, 2, 73, 77, 142, 26, 89, 222, 16, 66, 214, 89, 69, + 203, 28, 77, 6, 42, 80, 210, 227, 5, 65, 155, 137, 1, 86, 2, 167, 254, 1, + 80, 4, 40, 2, 72, 69, 253, 218, 6, 2, 65, 82, 2, 215, 219, 5, 76, 4, 22, + 69, 219, 94, 79, 2, 219, 218, 6, 86, 2, 171, 207, 6, 83, 2, 11, 89, 2, + 239, 236, 6, 65, 6, 194, 186, 6, 76, 146, 48, 73, 99, 72, 4, 134, 202, 5, + 71, 175, 162, 1, 65, 2, 11, 84, 2, 155, 149, 1, 84, 4, 194, 201, 5, 72, + 179, 163, 1, 73, 2, 243, 199, 6, 69, 4, 242, 213, 6, 76, 215, 22, 82, 8, + 42, 69, 22, 72, 209, 201, 6, 2, 65, 83, 2, 179, 147, 1, 78, 4, 26, 65, + 147, 197, 5, 69, 2, 243, 215, 6, 82, 2, 255, 232, 6, 67, 2, 175, 204, 6, + 86, 72, 54, 83, 206, 248, 4, 72, 165, 67, 4, 86, 73, 76, 76, 68, 56, 6, + 67, 82, 73, 80, 84, 32, 229, 2, 3, 69, 84, 32, 34, 114, 69, 34, 76, 134, + 1, 80, 22, 82, 134, 221, 2, 77, 226, 151, 2, 70, 30, 83, 42, 84, 62, 90, + 210, 86, 78, 15, 79, 4, 210, 68, 81, 147, 250, 5, 73, 6, 88, 18, 65, 84, + 73, 78, 32, 83, 77, 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, 32, 31, 69, + 4, 170, 232, 6, 73, 3, 78, 2, 55, 70, 2, 235, 181, 3, 76, 2, 21, 3, 73, + 71, 72, 2, 11, 84, 2, 183, 224, 2, 32, 34, 148, 1, 6, 65, 66, 79, 86, 69, + 32, 96, 7, 66, 69, 83, 73, 68, 69, 32, 162, 1, 79, 158, 3, 87, 129, 200, + 4, 9, 80, 82, 69, 67, 69, 68, 73, 78, 71, 6, 26, 83, 227, 192, 2, 76, 4, + 11, 85, 4, 26, 80, 175, 247, 4, 66, 2, 169, 247, 4, 2, 69, 82, 4, 108, + 23, 65, 78, 68, 32, 74, 79, 73, 78, 69, 68, 32, 66, 89, 32, 68, 65, 83, + 72, 32, 87, 73, 84, 72, 23, 83, 2, 17, 2, 32, 83, 2, 137, 246, 4, 2, 85, + 66, 16, 11, 70, 17, 11, 32, 14, 120, 6, 65, 66, 79, 86, 69, 32, 132, 1, + 4, 87, 73, 84, 72, 249, 224, 5, 11, 79, 82, 32, 69, 81, 85, 65, 76, 32, + 84, 79, 8, 34, 65, 38, 69, 18, 84, 67, 78, 2, 11, 76, 2, 113, 3, 77, 79, + 83, 2, 239, 61, 81, 2, 21, 3, 73, 76, 68, 2, 215, 158, 3, 69, 2, 17, 2, + 32, 78, 2, 11, 79, 2, 11, 84, 2, 11, 32, 2, 11, 69, 2, 17, 2, 81, 85, 2, + 11, 65, 2, 11, 76, 2, 143, 238, 2, 32, 6, 25, 4, 73, 84, 72, 32, 6, 104, + 14, 77, 85, 76, 84, 73, 80, 76, 73, 67, 65, 84, 73, 79, 78, 0, 4, 80, 76, + 85, 83, 199, 143, 6, 68, 2, 225, 174, 4, 5, 32, 83, 73, 71, 78, 4, 184, + 213, 4, 2, 65, 67, 191, 209, 1, 69, 4, 60, 9, 80, 69, 78, 83, 73, 79, 78, + 32, 82, 147, 203, 6, 72, 2, 21, 3, 65, 73, 76, 2, 175, 208, 5, 87, 8, 26, + 65, 98, 73, 35, 85, 4, 44, 5, 83, 72, 32, 65, 77, 243, 221, 6, 78, 2, + 141, 154, 6, 7, 80, 69, 82, 83, 65, 78, 68, 2, 11, 77, 2, 199, 159, 6, + 77, 2, 165, 156, 3, 2, 78, 71, 236, 2, 92, 11, 76, 79, 84, 73, 32, 78, + 65, 71, 82, 73, 32, 226, 5, 77, 242, 16, 78, 97, 2, 82, 73, 90, 180, 1, + 7, 76, 69, 84, 84, 69, 82, 32, 168, 2, 11, 80, 79, 69, 84, 82, 89, 32, + 77, 65, 82, 75, 56, 5, 83, 73, 71, 78, 32, 145, 1, 11, 86, 79, 87, 69, + 76, 32, 83, 73, 71, 78, 32, 64, 174, 1, 68, 46, 82, 34, 84, 242, 90, 66, + 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 223, 5, 72, 2, 76, 2, 77, 2, 78, + 2, 83, 246, 30, 65, 2, 69, 2, 73, 2, 79, 3, 85, 8, 186, 91, 68, 138, 223, + 5, 72, 247, 30, 79, 4, 150, 186, 6, 82, 247, 30, 79, 8, 238, 90, 84, 138, + 223, 5, 72, 247, 30, 79, 8, 11, 45, 8, 178, 216, 6, 49, 2, 50, 2, 51, 3, + 52, 8, 46, 65, 70, 72, 181, 146, 6, 3, 68, 86, 73, 4, 64, 10, 76, 84, 69, + 82, 78, 65, 84, 69, 32, 72, 171, 146, 6, 78, 2, 245, 142, 2, 2, 65, 83, + 10, 130, 184, 6, 79, 246, 30, 65, 2, 69, 2, 73, 3, 85, 88, 60, 8, 66, 79, + 76, 32, 70, 79, 82, 32, 145, 16, 2, 77, 69, 86, 238, 2, 66, 48, 2, 67, + 65, 118, 68, 238, 3, 69, 130, 2, 70, 56, 8, 72, 79, 82, 73, 90, 79, 78, + 84, 0, 6, 86, 69, 82, 84, 73, 67, 44, 3, 76, 73, 78, 52, 9, 77, 65, 82, + 75, 83, 32, 67, 72, 65, 22, 78, 90, 65, 68, 3, 82, 69, 67, 32, 5, 71, 82, + 79, 85, 80, 0, 4, 85, 78, 73, 84, 22, 83, 249, 2, 15, 84, 89, 80, 69, 32, + 65, 32, 69, 76, 69, 67, 84, 82, 79, 78, 4, 230, 200, 4, 69, 241, 203, 1, + 3, 65, 67, 75, 4, 44, 3, 82, 82, 73, 129, 189, 1, 2, 78, 67, 2, 33, 6, + 65, 71, 69, 32, 82, 69, 2, 11, 84, 2, 155, 191, 1, 85, 20, 24, 2, 65, 84, + 55, 69, 2, 177, 5, 9, 65, 32, 76, 73, 78, 75, 32, 69, 83, 18, 84, 4, 76, + 69, 84, 69, 137, 2, 12, 86, 73, 67, 69, 32, 67, 79, 78, 84, 82, 79, 76, + 11, 11, 32, 8, 164, 1, 11, 82, 69, 67, 84, 65, 78, 71, 85, 76, 65, 82, 0, + 6, 83, 81, 85, 65, 82, 69, 174, 186, 4, 70, 249, 149, 1, 11, 77, 69, 68, + 73, 85, 77, 32, 83, 72, 65, 68, 2, 61, 13, 32, 67, 72, 69, 67, 75, 69, + 82, 32, 66, 79, 65, 82, 2, 239, 207, 5, 68, 8, 11, 32, 8, 226, 178, 1, + 70, 134, 169, 3, 84, 155, 87, 79, 12, 22, 78, 207, 1, 83, 10, 48, 5, 68, + 32, 79, 70, 32, 137, 1, 2, 81, 85, 8, 18, 77, 31, 84, 2, 237, 161, 5, 2, + 69, 68, 6, 64, 11, 82, 65, 78, 83, 77, 73, 83, 83, 73, 79, 78, 243, 112, + 69, 5, 167, 218, 3, 32, 2, 199, 6, 73, 2, 193, 130, 5, 2, 67, 65, 4, 36, + 2, 73, 76, 73, 3, 79, 82, 77, 2, 191, 2, 69, 2, 165, 166, 1, 6, 65, 76, + 32, 84, 65, 66, 2, 11, 69, 2, 17, 2, 32, 70, 2, 227, 182, 1, 69, 2, 219, + 144, 5, 80, 6, 26, 69, 147, 192, 4, 85, 4, 56, 8, 71, 65, 84, 73, 86, 69, + 32, 65, 183, 174, 5, 87, 2, 21, 3, 67, 75, 78, 2, 21, 3, 79, 87, 76, 2, + 231, 140, 1, 69, 2, 11, 79, 2, 17, 2, 82, 68, 2, 219, 151, 3, 32, 18, + 176, 1, 7, 65, 77, 65, 82, 73, 84, 65, 60, 3, 72, 73, 70, 52, 8, 84, 65, + 82, 84, 32, 79, 70, 32, 64, 8, 85, 66, 83, 84, 73, 84, 85, 84, 120, 3, + 89, 78, 67, 163, 136, 6, 80, 2, 25, 4, 78, 32, 83, 79, 2, 11, 85, 2, 171, + 138, 6, 82, 4, 17, 2, 84, 32, 4, 174, 205, 5, 79, 243, 41, 73, 4, 22, 72, + 239, 106, 84, 2, 17, 2, 69, 65, 2, 175, 245, 5, 68, 4, 163, 177, 4, 69, + 2, 173, 136, 6, 2, 73, 67, 2, 11, 84, 2, 155, 180, 6, 82, 7, 38, 67, 193, + 181, 2, 3, 65, 71, 79, 2, 137, 139, 1, 9, 72, 82, 79, 78, 79, 85, 83, 32, + 73, 180, 1, 36, 3, 65, 67, 32, 147, 141, 5, 78, 178, 1, 150, 3, 65, 52, + 4, 66, 65, 82, 82, 32, 2, 67, 79, 80, 13, 68, 79, 84, 84, 69, 68, 32, 90, + 76, 65, 77, 65, 32, 118, 69, 90, 72, 204, 2, 7, 76, 69, 84, 84, 69, 82, + 32, 236, 9, 9, 79, 66, 76, 73, 81, 85, 69, 32, 76, 28, 4, 80, 84, 72, 65, + 0, 4, 90, 81, 65, 80, 110, 82, 88, 2, 83, 85, 174, 2, 84, 216, 131, 5, 3, + 77, 85, 83, 224, 22, 7, 70, 69, 77, 73, 78, 73, 78, 153, 139, 1, 5, 81, + 85, 83, 72, 83, 2, 11, 66, 2, 229, 215, 5, 5, 66, 82, 69, 86, 73, 2, 11, + 69, 2, 215, 191, 6, 75, 6, 38, 78, 165, 17, 4, 76, 79, 78, 32, 2, 17, 2, + 84, 82, 2, 187, 239, 5, 65, 4, 28, 3, 65, 78, 71, 35, 72, 2, 11, 85, 2, + 155, 156, 5, 76, 2, 11, 79, 2, 185, 231, 3, 5, 82, 73, 90, 79, 78, 6, 60, + 10, 78, 68, 32, 79, 70, 32, 80, 65, 82, 65, 151, 14, 83, 2, 201, 11, 2, + 71, 82, 14, 104, 8, 65, 82, 75, 76, 69, 65, 78, 32, 132, 1, 4, 66, 65, + 83, 65, 65, 7, 79, 82, 73, 90, 79, 78, 84, 6, 60, 5, 65, 83, 84, 69, 82, + 40, 4, 77, 69, 84, 79, 3, 79, 2, 17, 2, 73, 83, 2, 147, 180, 4, 67, 2, + 213, 177, 2, 2, 66, 69, 6, 164, 11, 8, 45, 69, 83, 65, 83, 65, 32, 68, + 155, 143, 4, 32, 2, 233, 235, 5, 2, 65, 76, 92, 154, 2, 68, 102, 72, 32, + 3, 76, 65, 77, 34, 77, 204, 1, 2, 80, 69, 142, 1, 82, 78, 83, 144, 1, 8, + 70, 73, 78, 65, 76, 32, 83, 69, 106, 65, 14, 75, 2, 81, 34, 84, 40, 5, + 71, 65, 77, 65, 76, 40, 4, 89, 85, 68, 72, 190, 80, 66, 242, 178, 4, 90, + 186, 46, 78, 134, 2, 87, 175, 126, 69, 4, 76, 14, 79, 84, 76, 69, 83, 83, + 32, 68, 65, 76, 65, 84, 72, 32, 139, 3, 65, 2, 179, 212, 2, 82, 4, 11, + 69, 5, 207, 183, 6, 84, 2, 11, 65, 2, 175, 183, 6, 68, 24, 60, 9, 65, 76, + 65, 89, 65, 76, 65, 77, 32, 151, 167, 6, 73, 22, 78, 76, 22, 78, 226, + 185, 4, 66, 222, 216, 1, 84, 138, 34, 83, 14, 74, 3, 82, 4, 255, 170, 4, + 76, 8, 222, 89, 78, 242, 218, 5, 71, 3, 89, 9, 33, 6, 82, 83, 73, 65, 78, + 32, 6, 50, 66, 16, 3, 68, 72, 65, 17, 3, 71, 72, 65, 2, 175, 84, 72, 2, + 147, 2, 76, 2, 139, 170, 5, 77, 4, 52, 7, 69, 86, 69, 82, 83, 69, 68, + 151, 202, 4, 73, 2, 195, 235, 4, 32, 14, 142, 1, 69, 40, 7, 79, 71, 68, + 73, 65, 78, 32, 64, 12, 85, 80, 69, 82, 83, 67, 82, 73, 80, 84, 32, 65, + 190, 179, 5, 72, 201, 82, 2, 65, 68, 2, 17, 2, 77, 75, 2, 235, 174, 4, + 65, 6, 42, 90, 32, 2, 75, 72, 179, 156, 6, 70, 2, 187, 132, 5, 72, 2, 11, + 76, 2, 11, 65, 2, 231, 177, 6, 80, 6, 36, 3, 69, 84, 72, 159, 180, 5, 65, + 5, 149, 91, 6, 32, 71, 65, 82, 83, 72, 5, 135, 133, 6, 32, 4, 221, 143, + 4, 2, 73, 78, 6, 21, 3, 72, 65, 32, 6, 42, 68, 182, 143, 4, 66, 131, 165, + 1, 65, 2, 17, 2, 79, 84, 2, 243, 156, 1, 84, 8, 58, 66, 174, 214, 2, 87, + 249, 166, 1, 4, 85, 75, 75, 65, 4, 169, 251, 3, 2, 65, 83, 14, 88, 8, 66, + 76, 73, 78, 69, 65, 82, 32, 113, 10, 80, 82, 65, 76, 73, 78, 69, 65, 82, + 32, 8, 44, 5, 67, 79, 76, 79, 78, 227, 217, 3, 70, 7, 11, 32, 4, 29, 5, + 83, 75, 69, 87, 69, 4, 219, 131, 6, 68, 6, 44, 5, 67, 79, 76, 79, 78, + 243, 216, 3, 70, 5, 221, 251, 5, 7, 32, 83, 75, 69, 87, 69, 68, 8, 62, + 72, 25, 11, 87, 79, 32, 86, 69, 82, 84, 73, 67, 65, 76, 4, 21, 3, 82, 69, + 69, 4, 25, 4, 32, 68, 79, 84, 4, 235, 138, 4, 83, 136, 47, 102, 45, 58, + 65, 162, 111, 69, 218, 45, 72, 202, 46, 73, 254, 68, 79, 246, 37, 82, + 214, 17, 85, 147, 22, 87, 4, 32, 2, 83, 72, 203, 131, 5, 82, 2, 203, 164, + 5, 73, 200, 29, 182, 1, 66, 86, 71, 196, 17, 2, 73, 32, 142, 35, 75, 144, + 5, 9, 76, 76, 89, 32, 77, 65, 82, 75, 32, 34, 77, 178, 37, 78, 240, 12, + 3, 80, 69, 32, 94, 85, 202, 157, 5, 67, 159, 11, 88, 5, 241, 128, 1, 16, + 76, 69, 32, 84, 69, 78, 78, 73, 83, 32, 80, 65, 68, 68, 76, 69, 144, 2, + 78, 32, 240, 11, 5, 65, 76, 79, 71, 32, 241, 2, 6, 66, 65, 78, 87, 65, + 32, 190, 1, 162, 1, 65, 102, 67, 186, 1, 68, 58, 69, 98, 71, 118, 72, 46, + 76, 230, 3, 80, 58, 81, 62, 82, 106, 83, 242, 81, 78, 230, 227, 1, 84, + 162, 146, 1, 70, 179, 185, 1, 86, 6, 42, 80, 158, 212, 2, 77, 211, 187, + 1, 83, 2, 17, 2, 79, 83, 2, 237, 249, 5, 4, 84, 82, 79, 80, 8, 18, 73, + 63, 79, 2, 25, 4, 82, 67, 85, 77, 2, 213, 2, 4, 70, 76, 69, 88, 6, 26, + 77, 131, 214, 5, 76, 4, 11, 77, 4, 172, 156, 5, 7, 69, 82, 67, 73, 65, + 76, 32, 179, 137, 1, 65, 22, 26, 79, 139, 177, 4, 73, 2, 11, 76, 2, 207, + 90, 76, 4, 18, 81, 43, 88, 2, 17, 2, 85, 65, 2, 179, 242, 2, 76, 2, 245, + 186, 5, 4, 67, 76, 65, 77, 4, 11, 82, 4, 18, 65, 55, 69, 2, 11, 86, 2, + 11, 69, 2, 193, 126, 3, 32, 65, 67, 2, 145, 4, 4, 65, 84, 69, 82, 2, 153, + 152, 2, 6, 89, 80, 72, 69, 78, 45, 114, 38, 65, 254, 2, 69, 235, 180, 4, + 79, 104, 25, 4, 84, 73, 78, 32, 104, 34, 67, 41, 4, 83, 77, 65, 76, 52, + 11, 65, 52, 25, 4, 80, 73, 84, 65, 52, 41, 8, 76, 32, 76, 69, 84, 84, 69, + 82, 52, 11, 32, 52, 158, 161, 6, 65, 2, 66, 2, 67, 2, 68, 2, 69, 2, 70, + 2, 71, 2, 72, 2, 73, 2, 74, 2, 75, 2, 76, 2, 77, 2, 78, 2, 79, 2, 80, 2, + 81, 2, 82, 2, 83, 2, 84, 2, 85, 2, 86, 2, 87, 2, 88, 2, 89, 3, 90, 8, 22, + 83, 199, 1, 70, 2, 11, 83, 2, 169, 69, 3, 45, 84, 72, 4, 26, 69, 211, + 236, 2, 76, 2, 11, 82, 2, 199, 73, 67, 4, 11, 85, 4, 26, 79, 215, 201, 3, + 69, 2, 239, 180, 5, 84, 8, 36, 3, 73, 71, 72, 199, 132, 4, 69, 6, 17, 2, + 84, 32, 6, 250, 146, 2, 67, 210, 3, 80, 239, 7, 83, 6, 150, 206, 2, 69, + 138, 184, 1, 79, 131, 218, 1, 80, 46, 80, 7, 76, 69, 84, 84, 69, 82, 32, + 208, 1, 5, 83, 73, 71, 78, 32, 147, 2, 86, 38, 162, 1, 65, 250, 164, 2, + 78, 206, 243, 3, 66, 2, 68, 2, 71, 2, 72, 2, 75, 2, 76, 2, 77, 2, 80, 2, + 82, 2, 83, 2, 84, 2, 87, 2, 89, 186, 2, 73, 3, 85, 5, 161, 208, 5, 6, 82, + 67, 72, 65, 73, 67, 4, 26, 80, 207, 192, 2, 86, 2, 25, 4, 65, 77, 85, 68, + 2, 235, 223, 2, 80, 36, 48, 7, 76, 69, 84, 84, 69, 82, 32, 147, 1, 86, + 32, 202, 163, 2, 78, 206, 243, 3, 66, 2, 68, 2, 71, 2, 75, 2, 76, 2, 77, + 2, 80, 2, 83, 2, 84, 2, 87, 2, 89, 186, 2, 65, 2, 73, 3, 85, 4, 41, 8, + 79, 87, 69, 76, 32, 83, 73, 71, 4, 11, 78, 4, 207, 215, 5, 32, 194, 4, + 140, 1, 10, 76, 69, 32, 76, 69, 84, 84, 69, 82, 32, 248, 2, 5, 84, 72, + 65, 77, 32, 184, 18, 5, 86, 73, 69, 84, 32, 225, 6, 3, 89, 79, 32, 70, + 186, 1, 65, 34, 69, 30, 84, 230, 158, 2, 78, 214, 172, 1, 79, 242, 129, + 2, 75, 2, 80, 162, 7, 85, 234, 61, 70, 2, 72, 2, 76, 2, 77, 2, 81, 2, 83, + 2, 86, 2, 88, 2, 89, 187, 2, 73, 7, 206, 254, 5, 85, 215, 22, 73, 7, 130, + 149, 6, 69, 3, 72, 18, 60, 3, 79, 78, 69, 234, 204, 5, 83, 138, 69, 72, + 187, 2, 65, 10, 11, 45, 10, 154, 148, 6, 50, 2, 51, 2, 52, 2, 53, 3, 54, + 254, 1, 196, 1, 2, 67, 79, 232, 3, 4, 72, 79, 82, 65, 0, 4, 84, 72, 65, + 77, 28, 7, 76, 69, 84, 84, 69, 82, 32, 220, 4, 5, 83, 73, 71, 78, 32, + 217, 5, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 20, 164, 1, 13, + 78, 83, 79, 78, 65, 78, 84, 32, 83, 73, 71, 78, 32, 197, 253, 4, 21, 77, + 66, 73, 78, 73, 78, 71, 32, 67, 82, 89, 80, 84, 79, 71, 82, 65, 77, 77, + 73, 67, 18, 148, 1, 6, 70, 73, 78, 65, 76, 32, 22, 76, 48, 16, 72, 73, + 71, 72, 32, 82, 65, 84, 72, 65, 32, 79, 82, 32, 76, 79, 22, 77, 206, 140, + 6, 66, 3, 83, 2, 151, 205, 5, 78, 4, 46, 79, 157, 26, 6, 65, 32, 84, 65, + 78, 71, 2, 167, 220, 1, 87, 6, 48, 6, 69, 68, 73, 65, 76, 32, 211, 142, + 6, 65, 4, 150, 140, 6, 76, 3, 82, 20, 169, 154, 4, 2, 32, 68, 106, 188, + 1, 2, 71, 82, 44, 5, 72, 73, 71, 72, 32, 94, 76, 222, 1, 82, 250, 178, 2, + 85, 186, 202, 1, 73, 178, 15, 78, 162, 222, 1, 79, 162, 8, 69, 158, 20, + 66, 2, 68, 2, 77, 2, 87, 187, 2, 65, 2, 21, 3, 69, 65, 84, 2, 251, 137, + 6, 32, 32, 242, 1, 75, 42, 82, 130, 194, 5, 83, 82, 67, 2, 80, 2, 84, + 138, 69, 70, 2, 72, 3, 89, 36, 60, 3, 79, 87, 32, 242, 202, 5, 65, 206, + 41, 85, 159, 20, 76, 28, 86, 75, 42, 82, 210, 194, 5, 67, 2, 80, 2, 84, + 138, 69, 70, 2, 72, 2, 83, 3, 89, 6, 254, 135, 6, 72, 2, 88, 187, 2, 65, + 2, 229, 140, 4, 2, 65, 84, 8, 26, 65, 135, 243, 5, 85, 7, 158, 135, 6, + 78, 3, 84, 50, 182, 1, 72, 34, 75, 176, 1, 4, 77, 65, 73, 32, 82, 82, + 136, 1, 2, 83, 65, 92, 5, 87, 73, 65, 78, 71, 172, 132, 1, 3, 68, 79, 75, + 224, 121, 3, 84, 79, 78, 161, 173, 3, 2, 67, 65, 4, 138, 183, 5, 65, 179, + 63, 79, 14, 52, 4, 72, 85, 69, 78, 134, 3, 65, 131, 232, 4, 69, 8, 80, 6, + 32, 84, 79, 78, 69, 45, 189, 186, 4, 8, 45, 76, 85, 69, 32, 75, 65, 82, + 6, 214, 134, 6, 51, 2, 52, 3, 53, 8, 56, 4, 75, 65, 78, 71, 246, 140, 1, + 89, 247, 187, 3, 83, 5, 167, 146, 1, 32, 4, 92, 3, 65, 32, 72, 21, 16, + 69, 86, 69, 82, 83, 69, 68, 32, 82, 79, 84, 65, 84, 69, 68, 32, 2, 243, + 199, 4, 65, 2, 139, 209, 3, 82, 8, 48, 3, 84, 75, 65, 142, 184, 4, 87, + 231, 123, 75, 4, 17, 2, 65, 78, 5, 163, 143, 4, 75, 5, 245, 202, 3, 2, + 87, 65, 38, 90, 65, 36, 4, 77, 65, 73, 32, 22, 79, 46, 84, 86, 85, 142, + 245, 3, 73, 199, 140, 2, 69, 9, 134, 131, 6, 65, 2, 69, 3, 73, 2, 179, + 249, 4, 83, 11, 186, 224, 3, 65, 150, 162, 2, 79, 3, 89, 4, 42, 65, 181, + 142, 1, 4, 72, 65, 77, 32, 2, 17, 2, 76, 76, 2, 175, 140, 4, 32, 9, 174, + 193, 5, 85, 163, 64, 69, 144, 1, 184, 1, 7, 76, 69, 84, 84, 69, 82, 32, + 188, 2, 5, 77, 65, 73, 32, 75, 32, 7, 83, 89, 77, 66, 79, 76, 32, 124, 9, 84, 79, 78, 69, 32, 77, 65, 73, 32, 81, 6, 86, 79, 87, 69, 76, 32, 96, 44, 4, 72, 73, 71, 72, 1, 3, 76, 79, 87, 48, 11, 32, 48, 154, 1, 75, 30, - 67, 2, 80, 2, 84, 34, 78, 166, 208, 5, 66, 2, 68, 2, 70, 2, 71, 2, 72, 2, - 76, 2, 77, 2, 82, 2, 83, 2, 86, 2, 89, 247, 30, 79, 6, 26, 72, 187, 239, - 5, 79, 4, 194, 208, 5, 72, 247, 30, 79, 6, 162, 208, 5, 71, 2, 89, 247, - 30, 79, 4, 246, 157, 5, 65, 195, 52, 72, 10, 68, 2, 75, 79, 106, 78, 240, - 126, 4, 72, 79, 32, 72, 183, 179, 3, 83, 4, 212, 127, 3, 73, 32, 75, 179, - 238, 4, 78, 8, 58, 78, 254, 132, 1, 84, 230, 164, 2, 83, 171, 192, 2, 69, - 2, 143, 170, 3, 85, 26, 50, 65, 58, 85, 30, 73, 134, 236, 5, 69, 3, 79, - 10, 130, 214, 5, 85, 214, 22, 65, 2, 77, 2, 78, 3, 89, 9, 26, 69, 135, - 236, 5, 65, 5, 131, 236, 5, 65, 138, 1, 52, 3, 82, 73, 32, 237, 222, 4, - 4, 69, 79, 85, 84, 136, 1, 82, 65, 20, 7, 76, 69, 84, 84, 69, 82, 32, - 166, 2, 83, 78, 86, 187, 246, 3, 68, 2, 195, 211, 1, 66, 88, 210, 1, 65, - 250, 142, 2, 68, 82, 82, 34, 84, 230, 5, 85, 206, 201, 1, 73, 162, 193, - 1, 78, 126, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 2, 83, 138, 69, 72, 2, - 76, 2, 77, 2, 86, 2, 89, 186, 2, 69, 3, 79, 11, 172, 185, 3, 7, 82, 67, - 72, 65, 73, 67, 32, 198, 175, 2, 65, 2, 73, 3, 85, 8, 25, 4, 73, 71, 78, - 32, 8, 166, 212, 1, 78, 234, 206, 3, 65, 239, 1, 86, 18, 49, 10, 79, 87, - 69, 76, 32, 83, 73, 71, 78, 32, 18, 214, 147, 2, 65, 38, 85, 206, 201, 1, - 73, 222, 137, 2, 69, 3, 79, 4, 154, 51, 70, 155, 154, 4, 79, 188, 6, 36, - 3, 73, 76, 32, 239, 193, 4, 65, 186, 6, 154, 3, 65, 106, 67, 194, 2, 68, - 72, 3, 87, 69, 84, 48, 9, 70, 82, 65, 67, 84, 73, 79, 78, 32, 232, 7, 9, - 73, 78, 32, 80, 79, 83, 83, 69, 83, 22, 76, 200, 2, 7, 78, 85, 77, 66, - 69, 82, 32, 152, 1, 18, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, - 69, 78, 68, 32, 79, 70, 54, 82, 42, 83, 166, 14, 84, 228, 1, 7, 86, 79, - 87, 69, 76, 32, 83, 100, 2, 89, 69, 176, 186, 4, 5, 77, 79, 78, 84, 72, - 171, 118, 79, 6, 80, 5, 83, 32, 65, 66, 79, 242, 132, 2, 85, 217, 244, 1, - 5, 78, 68, 32, 79, 68, 2, 243, 197, 3, 86, 52, 80, 9, 79, 78, 83, 79, 78, - 65, 78, 84, 32, 156, 21, 3, 85, 82, 82, 171, 9, 82, 48, 130, 1, 75, 22, - 76, 22, 78, 46, 84, 218, 191, 1, 83, 194, 77, 82, 202, 210, 3, 67, 2, 72, - 2, 74, 2, 77, 2, 80, 2, 86, 3, 89, 5, 215, 162, 5, 83, 7, 243, 227, 4, - 76, 11, 134, 229, 3, 78, 134, 251, 1, 71, 3, 89, 5, 223, 223, 5, 84, 26, - 68, 2, 82, 89, 164, 28, 2, 69, 66, 238, 143, 2, 65, 227, 193, 1, 73, 2, - 137, 200, 1, 7, 32, 67, 85, 76, 84, 73, 86, 42, 168, 1, 4, 79, 78, 69, - 32, 220, 4, 6, 84, 72, 82, 69, 69, 32, 177, 215, 5, 22, 68, 79, 87, 78, - 83, 67, 65, 76, 73, 78, 71, 32, 70, 65, 67, 84, 79, 82, 32, 75, 73, 73, - 30, 112, 5, 69, 73, 71, 72, 84, 34, 70, 40, 3, 72, 65, 76, 22, 79, 88, 4, - 83, 73, 88, 84, 74, 84, 187, 215, 3, 81, 4, 210, 3, 73, 227, 216, 5, 72, - 4, 156, 3, 2, 79, 82, 187, 213, 3, 73, 4, 215, 218, 1, 70, 2, 225, 2, 18, - 78, 69, 45, 72, 85, 78, 68, 82, 69, 68, 45, 65, 78, 68, 45, 83, 73, 88, - 6, 232, 217, 1, 5, 69, 69, 78, 84, 72, 177, 24, 5, 89, 45, 70, 79, 85, 8, - 38, 72, 138, 1, 87, 239, 214, 3, 69, 4, 132, 1, 18, 82, 69, 69, 45, 72, - 85, 78, 68, 82, 69, 68, 45, 65, 78, 68, 45, 84, 87, 201, 156, 4, 8, 73, - 82, 84, 89, 45, 83, 69, 67, 2, 17, 2, 69, 78, 2, 17, 2, 84, 73, 2, 207, - 214, 3, 69, 10, 58, 69, 28, 4, 83, 73, 88, 84, 82, 84, 163, 214, 3, 81, - 2, 129, 1, 3, 73, 71, 72, 4, 50, 69, 149, 215, 3, 6, 89, 45, 70, 79, 85, - 82, 2, 145, 215, 3, 2, 69, 78, 2, 21, 3, 87, 69, 78, 2, 221, 214, 3, 3, - 84, 73, 69, 2, 171, 214, 1, 83, 72, 48, 6, 69, 84, 84, 69, 82, 32, 195, - 237, 3, 65, 70, 194, 1, 78, 158, 201, 1, 84, 166, 49, 65, 82, 76, 38, 82, - 134, 6, 85, 202, 141, 1, 79, 134, 60, 73, 206, 193, 1, 83, 242, 7, 69, - 234, 61, 67, 2, 72, 2, 74, 2, 75, 2, 77, 2, 80, 2, 86, 3, 89, 10, 46, 78, - 250, 209, 5, 71, 2, 89, 187, 2, 65, 4, 246, 209, 5, 78, 187, 2, 65, 8, - 38, 79, 130, 128, 4, 84, 131, 77, 83, 4, 11, 78, 4, 17, 2, 69, 32, 4, 18, - 72, 31, 84, 2, 217, 71, 3, 85, 78, 68, 2, 149, 132, 2, 3, 72, 79, 85, 2, - 17, 2, 32, 84, 2, 11, 69, 2, 171, 182, 5, 88, 2, 17, 2, 85, 80, 2, 131, - 182, 3, 69, 194, 4, 152, 1, 5, 65, 76, 84, 32, 80, 20, 4, 73, 71, 78, 32, - 206, 4, 80, 28, 9, 84, 65, 82, 84, 73, 78, 71, 32, 70, 41, 8, 89, 76, 76, - 65, 66, 76, 69, 32, 2, 219, 215, 3, 65, 40, 90, 65, 48, 3, 67, 69, 86, - 34, 75, 80, 2, 77, 85, 102, 85, 14, 80, 118, 86, 167, 64, 78, 4, 216, 2, - 4, 65, 90, 72, 65, 199, 136, 5, 78, 2, 11, 73, 2, 231, 170, 5, 84, 6, 38, - 85, 173, 170, 5, 3, 65, 65, 67, 4, 18, 90, 87, 82, 2, 167, 187, 5, 72, 6, - 60, 4, 75, 75, 85, 82, 16, 2, 84, 72, 21, 3, 85, 86, 85, 2, 167, 82, 85, - 2, 251, 149, 3, 65, 2, 75, 90, 8, 26, 65, 131, 254, 4, 79, 6, 34, 84, - 182, 146, 4, 65, 15, 78, 2, 11, 72, 2, 17, 2, 65, 75, 2, 179, 168, 5, 75, - 10, 38, 65, 214, 81, 69, 143, 184, 4, 73, 4, 180, 102, 3, 82, 65, 65, - 221, 243, 2, 6, 75, 65, 73, 89, 65, 82, 2, 11, 69, 2, 187, 9, 78, 2, 17, - 2, 82, 79, 2, 235, 196, 4, 77, 148, 4, 130, 1, 75, 166, 1, 76, 166, 1, - 78, 186, 1, 82, 86, 83, 178, 1, 84, 214, 2, 67, 2, 72, 2, 74, 2, 77, 2, - 80, 2, 86, 3, 89, 46, 84, 2, 83, 83, 174, 246, 1, 65, 38, 85, 202, 141, - 1, 79, 134, 60, 73, 191, 201, 1, 69, 24, 214, 239, 1, 65, 250, 6, 85, - 202, 141, 1, 79, 134, 60, 73, 191, 201, 1, 69, 66, 78, 76, 146, 245, 1, - 65, 38, 85, 202, 141, 1, 79, 134, 60, 73, 191, 201, 1, 69, 44, 226, 6, - 76, 174, 238, 1, 65, 38, 85, 202, 141, 1, 79, 134, 60, 73, 191, 201, 1, - 69, 110, 98, 78, 174, 5, 71, 2, 89, 174, 238, 1, 65, 38, 85, 202, 141, 1, - 79, 134, 60, 73, 191, 201, 1, 69, 44, 170, 5, 78, 174, 238, 1, 65, 38, - 85, 202, 141, 1, 79, 134, 60, 73, 191, 201, 1, 69, 44, 214, 4, 82, 174, - 238, 1, 65, 38, 85, 202, 141, 1, 79, 134, 60, 73, 191, 201, 1, 69, 68, - 90, 72, 170, 3, 83, 174, 238, 1, 65, 38, 85, 202, 141, 1, 79, 134, 60, - 73, 191, 201, 1, 69, 24, 210, 241, 1, 65, 38, 85, 202, 141, 1, 79, 230, - 2, 82, 162, 57, 73, 191, 201, 1, 69, 44, 210, 2, 84, 174, 238, 1, 65, 38, - 85, 202, 141, 1, 79, 134, 60, 73, 191, 201, 1, 69, 6, 68, 2, 79, 84, 33, - 11, 82, 65, 68, 73, 84, 73, 79, 78, 65, 76, 32, 2, 11, 65, 2, 163, 188, - 4, 76, 4, 24, 2, 67, 82, 55, 78, 2, 17, 2, 69, 68, 2, 11, 73, 2, 211, - 187, 4, 84, 2, 11, 85, 2, 17, 2, 77, 66, 2, 123, 69, 22, 25, 4, 73, 71, - 78, 32, 22, 170, 238, 1, 65, 38, 85, 202, 141, 1, 79, 134, 60, 73, 191, - 201, 1, 69, 2, 11, 65, 2, 163, 186, 4, 82, 184, 13, 36, 5, 65, 66, 65, - 84, 65, 35, 71, 2, 11, 32, 2, 147, 209, 3, 84, 182, 13, 54, 69, 20, 3, - 83, 65, 32, 217, 6, 3, 85, 84, 32, 2, 207, 166, 4, 82, 178, 1, 52, 7, 76, - 69, 84, 84, 69, 82, 32, 155, 206, 3, 68, 158, 1, 210, 1, 65, 58, 70, 54, - 72, 38, 76, 58, 77, 54, 78, 50, 83, 126, 85, 114, 69, 2, 73, 2, 79, 2, - 86, 186, 241, 4, 84, 82, 67, 2, 68, 2, 71, 2, 75, 2, 80, 138, 69, 66, 2, - 82, 2, 87, 2, 88, 2, 89, 3, 90, 16, 150, 4, 87, 202, 185, 5, 67, 2, 81, - 2, 88, 3, 90, 4, 172, 236, 4, 5, 73, 78, 65, 76, 32, 251, 80, 65, 6, 174, - 177, 1, 84, 199, 139, 4, 65, 4, 204, 150, 4, 5, 79, 78, 71, 32, 85, 131, - 166, 1, 65, 10, 150, 188, 5, 65, 2, 67, 2, 81, 2, 88, 3, 90, 8, 170, 185, - 5, 71, 2, 72, 2, 89, 187, 2, 65, 8, 26, 72, 155, 187, 5, 65, 6, 40, 4, - 79, 82, 84, 32, 239, 186, 5, 65, 4, 188, 151, 1, 2, 85, 69, 137, 151, 3, - 2, 65, 87, 32, 58, 73, 54, 69, 202, 185, 5, 67, 2, 81, 2, 88, 3, 90, 16, - 50, 85, 202, 185, 5, 67, 2, 81, 2, 88, 3, 90, 8, 198, 185, 5, 67, 2, 81, - 2, 88, 3, 90, 130, 12, 64, 10, 67, 79, 77, 80, 79, 78, 69, 78, 84, 45, - 143, 150, 3, 73, 128, 12, 70, 48, 178, 1, 49, 2, 50, 2, 51, 2, 52, 2, 53, - 2, 54, 95, 55, 198, 1, 86, 48, 242, 1, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, - 54, 2, 55, 2, 56, 3, 57, 18, 170, 183, 5, 49, 2, 50, 2, 51, 2, 52, 2, 53, - 2, 54, 2, 55, 2, 56, 3, 57, 200, 1, 150, 1, 48, 2, 49, 2, 50, 2, 51, 2, - 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 138, 1, 58, 48, 2, 49, 2, 50, 2, - 51, 2, 52, 2, 53, 95, 54, 20, 186, 181, 5, 48, 2, 49, 2, 50, 2, 51, 2, - 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 18, 222, 180, 5, 48, 2, 49, 2, 50, - 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 3, 56, 4, 48, 6, 67, 65, 82, 84, 82, - 73, 21, 2, 68, 82, 2, 183, 254, 3, 68, 2, 239, 182, 4, 73, 2, 179, 173, - 3, 82, 142, 3, 154, 1, 65, 152, 2, 5, 68, 68, 89, 32, 66, 22, 76, 166, - 14, 78, 144, 1, 5, 83, 84, 32, 84, 85, 21, 12, 84, 82, 65, 71, 82, 65, - 77, 32, 70, 79, 82, 32, 10, 68, 9, 67, 85, 80, 32, 87, 73, 84, 72, 79, - 58, 82, 199, 224, 4, 80, 2, 33, 6, 85, 84, 32, 72, 65, 78, 2, 231, 140, - 4, 68, 6, 72, 9, 45, 79, 70, 70, 32, 67, 65, 76, 69, 29, 5, 68, 82, 79, - 80, 45, 2, 245, 142, 4, 2, 78, 68, 4, 250, 155, 3, 83, 245, 115, 4, 66, - 65, 82, 66, 2, 171, 142, 4, 69, 216, 1, 38, 69, 213, 2, 4, 85, 71, 85, - 32, 16, 60, 6, 80, 72, 79, 78, 69, 32, 246, 1, 83, 151, 179, 2, 86, 12, - 132, 1, 3, 82, 69, 67, 228, 150, 1, 3, 76, 79, 67, 146, 144, 3, 83, 189, - 116, 13, 79, 78, 32, 84, 79, 80, 32, 79, 70, 32, 77, 79, 68, 6, 36, 5, - 69, 73, 86, 69, 82, 51, 79, 5, 29, 5, 32, 87, 73, 84, 72, 2, 179, 38, 32, - 2, 255, 140, 3, 82, 2, 11, 67, 2, 167, 229, 3, 79, 200, 1, 162, 1, 65, - 20, 15, 70, 82, 65, 67, 84, 73, 79, 78, 32, 68, 73, 71, 73, 84, 32, 164, - 2, 2, 76, 69, 252, 3, 5, 83, 73, 71, 78, 32, 198, 2, 86, 147, 177, 3, 68, - 2, 247, 205, 1, 73, 14, 74, 84, 40, 2, 79, 78, 81, 10, 90, 69, 82, 79, - 32, 70, 79, 82, 32, 79, 8, 36, 3, 72, 82, 69, 13, 2, 87, 79, 4, 11, 69, - 4, 29, 5, 32, 70, 79, 82, 32, 4, 34, 79, 21, 4, 69, 86, 69, 78, 2, 17, 2, - 68, 68, 2, 49, 10, 32, 80, 79, 87, 69, 82, 83, 32, 79, 70, 2, 133, 22, 2, - 32, 70, 114, 44, 5, 84, 84, 69, 82, 32, 219, 230, 4, 78, 112, 214, 1, 68, - 54, 78, 106, 82, 38, 84, 138, 203, 1, 65, 82, 76, 114, 86, 186, 5, 85, - 202, 141, 1, 79, 134, 60, 73, 206, 193, 1, 83, 82, 66, 2, 67, 2, 71, 2, - 74, 2, 75, 2, 80, 162, 7, 69, 234, 61, 72, 2, 77, 3, 89, 10, 166, 223, 4, - 68, 138, 69, 72, 2, 90, 187, 2, 65, 10, 42, 65, 210, 163, 5, 71, 2, 78, - 3, 89, 5, 41, 8, 75, 65, 65, 82, 65, 32, 80, 79, 2, 131, 37, 76, 6, 158, - 204, 1, 82, 175, 217, 3, 65, 10, 230, 221, 4, 84, 138, 69, 72, 2, 83, - 187, 2, 65, 20, 86, 67, 194, 1, 83, 212, 35, 3, 84, 85, 85, 170, 107, 78, - 242, 60, 65, 231, 147, 3, 86, 6, 60, 9, 79, 77, 66, 73, 78, 73, 78, 71, - 32, 171, 143, 1, 65, 4, 70, 65, 145, 166, 4, 11, 67, 65, 78, 68, 82, 65, - 66, 73, 78, 68, 85, 2, 33, 6, 78, 85, 83, 86, 65, 82, 2, 235, 165, 4, 65, - 2, 11, 73, 2, 209, 231, 3, 3, 68, 68, 72, 30, 49, 10, 79, 87, 69, 76, 32, - 83, 73, 71, 78, 32, 30, 174, 206, 1, 65, 38, 85, 22, 86, 182, 141, 1, 79, - 134, 60, 73, 191, 201, 1, 69, 6, 80, 10, 78, 73, 83, 32, 82, 65, 67, 81, - 85, 69, 174, 132, 3, 71, 175, 156, 2, 84, 2, 11, 84, 2, 25, 4, 32, 65, - 78, 68, 2, 215, 152, 3, 32, 2, 199, 137, 5, 66, 162, 1, 210, 2, 65, 134, - 1, 66, 174, 1, 67, 222, 2, 68, 202, 3, 69, 154, 2, 70, 182, 1, 71, 198, - 1, 72, 64, 8, 89, 79, 85, 84, 72, 70, 85, 76, 52, 2, 73, 78, 46, 75, 98, - 76, 146, 1, 77, 134, 1, 79, 62, 80, 142, 1, 82, 210, 1, 83, 224, 1, 14, - 86, 65, 83, 84, 78, 69, 83, 83, 32, 79, 82, 32, 87, 65, 12, 2, 87, 65, - 154, 94, 85, 203, 150, 4, 74, 8, 88, 4, 67, 67, 85, 77, 22, 83, 228, 20, - 2, 68, 86, 229, 197, 1, 5, 71, 71, 82, 65, 86, 2, 239, 185, 1, 85, 2, - 151, 217, 4, 67, 6, 92, 7, 79, 76, 68, 32, 82, 69, 83, 32, 5, 82, 65, 78, - 67, 72, 177, 146, 4, 3, 65, 82, 82, 2, 249, 202, 4, 3, 79, 76, 85, 2, 11, - 73, 2, 213, 166, 1, 3, 78, 71, 32, 22, 54, 72, 20, 3, 76, 79, 83, 66, 79, - 175, 152, 2, 69, 2, 155, 229, 3, 65, 6, 26, 69, 227, 249, 3, 85, 4, 230, - 248, 1, 68, 215, 138, 2, 78, 12, 28, 3, 77, 80, 76, 35, 78, 4, 246, 17, - 73, 143, 183, 4, 69, 8, 32, 4, 83, 84, 65, 78, 23, 84, 2, 199, 135, 5, - 67, 6, 46, 69, 20, 3, 82, 65, 82, 203, 137, 4, 65, 2, 251, 199, 4, 78, 2, - 183, 170, 3, 73, 20, 80, 4, 65, 82, 75, 69, 22, 69, 210, 1, 73, 74, 85, - 249, 248, 4, 3, 79, 85, 66, 2, 203, 198, 4, 78, 6, 132, 1, 19, 70, 69, - 67, 84, 73, 86, 69, 78, 69, 83, 83, 32, 79, 82, 32, 68, 73, 83, 84, 34, - 80, 137, 7, 6, 67, 73, 83, 73, 86, 69, 2, 11, 79, 2, 215, 197, 4, 82, 2, - 11, 65, 2, 11, 82, 2, 143, 10, 84, 8, 68, 6, 70, 70, 73, 67, 85, 76, 34, - 77, 161, 12, 4, 86, 69, 82, 71, 2, 11, 84, 2, 159, 129, 4, 73, 4, 140, 1, - 2, 73, 78, 243, 194, 4, 77, 14, 100, 5, 77, 66, 69, 76, 76, 34, 78, 124, - 4, 88, 72, 65, 85, 188, 106, 3, 84, 69, 82, 147, 159, 3, 65, 2, 189, 208, - 4, 3, 73, 83, 72, 6, 80, 4, 68, 69, 65, 86, 20, 4, 76, 65, 82, 71, 129, - 146, 3, 4, 67, 79, 85, 78, 2, 171, 154, 4, 79, 2, 183, 207, 4, 69, 2, - 135, 194, 4, 83, 12, 70, 79, 76, 3, 85, 76, 76, 196, 5, 3, 65, 73, 76, - 159, 225, 4, 76, 4, 18, 76, 35, 83, 2, 225, 192, 4, 3, 76, 79, 87, 2, - 205, 4, 2, 84, 69, 4, 166, 191, 3, 32, 175, 59, 78, 10, 96, 8, 65, 84, - 72, 69, 82, 73, 78, 71, 22, 79, 64, 3, 82, 69, 65, 65, 5, 85, 65, 82, 68, - 69, 5, 215, 144, 4, 32, 2, 41, 8, 73, 78, 71, 32, 84, 79, 32, 77, 2, 211, - 218, 4, 69, 2, 75, 84, 4, 48, 2, 65, 82, 33, 6, 79, 76, 68, 73, 78, 71, - 2, 11, 68, 2, 175, 248, 3, 78, 2, 11, 32, 2, 235, 243, 1, 66, 4, 26, 67, - 211, 208, 4, 78, 2, 143, 5, 82, 4, 60, 7, 69, 69, 80, 73, 78, 71, 32, - 221, 184, 4, 2, 73, 78, 2, 11, 83, 2, 147, 134, 3, 77, 6, 18, 65, 107, - 69, 4, 60, 3, 66, 79, 85, 21, 8, 87, 32, 79, 82, 32, 77, 79, 68, 2, 243, - 187, 4, 82, 2, 175, 131, 4, 69, 2, 139, 188, 4, 71, 6, 26, 65, 30, 69, - 47, 73, 2, 153, 187, 4, 2, 83, 83, 2, 11, 65, 2, 11, 83, 2, 179, 235, 3, - 85, 2, 11, 82, 2, 171, 197, 4, 69, 4, 164, 203, 3, 7, 78, 32, 84, 72, 69, - 32, 86, 179, 110, 80, 8, 54, 65, 64, 4, 69, 78, 69, 84, 245, 97, 2, 85, - 82, 4, 22, 84, 243, 2, 67, 2, 17, 2, 84, 69, 2, 183, 186, 4, 82, 2, 139, - 200, 1, 82, 12, 30, 69, 157, 1, 2, 73, 84, 10, 34, 76, 22, 83, 243, 230, - 4, 65, 2, 227, 227, 1, 69, 6, 18, 73, 51, 80, 4, 30, 68, 137, 1, 2, 83, - 84, 2, 147, 1, 69, 2, 11, 79, 2, 187, 255, 3, 78, 2, 243, 254, 3, 85, 10, - 34, 69, 64, 2, 73, 78, 23, 84, 2, 11, 86, 2, 17, 2, 69, 82, 2, 11, 65, 2, - 211, 202, 4, 78, 2, 183, 182, 4, 75, 6, 42, 79, 237, 132, 3, 4, 82, 69, - 78, 71, 4, 26, 80, 143, 240, 4, 86, 2, 11, 80, 2, 179, 209, 3, 65, 2, 39, - 83, 4, 26, 73, 243, 227, 4, 84, 2, 147, 181, 4, 84, 224, 2, 86, 65, 180, - 31, 2, 69, 82, 246, 1, 73, 206, 1, 79, 100, 3, 82, 69, 69, 187, 8, 85, - 146, 2, 44, 4, 65, 78, 65, 32, 145, 10, 2, 73, 32, 100, 122, 65, 38, 69, - 76, 7, 76, 69, 84, 84, 69, 82, 32, 154, 7, 79, 76, 3, 73, 66, 73, 0, 3, - 85, 66, 85, 45, 2, 83, 85, 4, 184, 8, 3, 65, 66, 65, 3, 66, 6, 58, 66, 0, - 3, 69, 66, 69, 245, 7, 4, 89, 66, 69, 89, 2, 243, 7, 69, 78, 202, 1, 65, - 42, 68, 82, 70, 2, 81, 14, 71, 50, 72, 38, 75, 62, 76, 32, 3, 77, 69, 69, - 20, 2, 67, 72, 2, 74, 2, 80, 18, 78, 42, 83, 94, 84, 102, 86, 2, 87, 42, - 90, 138, 138, 3, 66, 2, 82, 3, 89, 4, 252, 1, 2, 76, 73, 203, 218, 4, 73, - 6, 30, 65, 29, 3, 72, 65, 65, 4, 218, 2, 65, 255, 1, 86, 2, 239, 219, 4, - 76, 2, 123, 65, 6, 110, 65, 86, 78, 129, 218, 4, 3, 72, 65, 73, 4, 186, - 141, 3, 72, 151, 240, 1, 65, 4, 26, 65, 255, 140, 3, 72, 2, 11, 65, 2, - 211, 218, 4, 70, 4, 18, 65, 35, 72, 2, 11, 65, 2, 159, 218, 4, 77, 2, - 211, 1, 65, 4, 236, 217, 4, 2, 79, 79, 191, 34, 65, 8, 32, 2, 65, 65, 18, - 72, 23, 69, 2, 163, 16, 68, 4, 18, 69, 87, 65, 2, 255, 216, 4, 69, 10, - 62, 65, 16, 3, 72, 65, 65, 198, 138, 3, 84, 207, 242, 1, 79, 2, 131, 1, - 86, 5, 167, 216, 4, 76, 2, 17, 2, 65, 65, 2, 255, 215, 4, 86, 6, 26, 65, - 187, 252, 4, 79, 4, 26, 86, 159, 252, 4, 65, 2, 21, 3, 73, 89, 65, 2, - 171, 232, 4, 78, 6, 48, 3, 65, 66, 79, 14, 66, 1, 3, 79, 66, 79, 2, 23, - 65, 2, 11, 79, 2, 11, 70, 2, 11, 73, 2, 179, 231, 4, 76, 2, 199, 250, 3, - 75, 174, 1, 26, 67, 167, 137, 3, 68, 154, 1, 132, 1, 9, 72, 65, 82, 65, - 67, 84, 69, 82, 32, 129, 207, 4, 17, 85, 82, 82, 69, 78, 67, 89, 32, 83, - 89, 77, 66, 79, 76, 32, 66, 65, 152, 1, 176, 2, 6, 66, 79, 32, 66, 65, - 73, 16, 6, 67, 72, 79, 32, 67, 72, 56, 3, 68, 79, 32, 44, 2, 70, 79, 76, - 3, 72, 79, 32, 66, 75, 190, 1, 76, 138, 1, 77, 252, 1, 7, 65, 78, 71, 75, - 72, 65, 78, 46, 78, 158, 1, 80, 234, 1, 82, 70, 83, 238, 2, 84, 224, 2, - 3, 87, 79, 32, 34, 89, 141, 142, 4, 2, 79, 32, 2, 143, 12, 77, 8, 212, - 251, 2, 2, 65, 78, 162, 170, 1, 73, 167, 58, 79, 4, 176, 209, 4, 3, 67, - 72, 65, 227, 33, 68, 6, 32, 2, 32, 70, 21, 2, 78, 71, 4, 227, 250, 2, 65, - 2, 211, 171, 3, 77, 4, 40, 4, 78, 79, 75, 72, 235, 243, 4, 72, 2, 251, - 241, 4, 85, 14, 40, 2, 72, 79, 229, 9, 3, 79, 32, 75, 12, 26, 32, 235, - 250, 3, 77, 10, 36, 2, 75, 72, 57, 3, 82, 65, 75, 8, 158, 9, 87, 138, - 215, 3, 85, 138, 68, 79, 139, 60, 65, 2, 131, 155, 4, 72, 8, 76, 2, 79, - 32, 224, 11, 8, 65, 75, 75, 72, 65, 78, 71, 89, 151, 231, 4, 85, 4, 32, - 2, 67, 72, 207, 161, 4, 76, 2, 183, 208, 4, 85, 16, 28, 2, 65, 73, 219, - 52, 79, 14, 42, 32, 176, 1, 3, 84, 65, 73, 19, 89, 10, 76, 4, 67, 72, 65, - 84, 44, 5, 72, 65, 78, 45, 65, 22, 84, 203, 237, 4, 69, 2, 11, 84, 2, 11, - 65, 2, 207, 238, 4, 87, 2, 139, 221, 3, 75, 4, 234, 209, 4, 72, 159, 11, - 82, 2, 203, 2, 75, 2, 205, 198, 4, 2, 65, 77, 8, 60, 3, 71, 79, 32, 32, - 3, 73, 75, 72, 29, 3, 79, 32, 78, 2, 11, 78, 2, 223, 202, 4, 71, 2, 249, - 210, 4, 2, 65, 72, 4, 182, 159, 4, 69, 227, 79, 85, 12, 68, 6, 65, 73, - 89, 65, 78, 78, 22, 72, 133, 204, 4, 3, 79, 32, 80, 2, 215, 218, 4, 79, - 8, 36, 3, 73, 78, 84, 21, 2, 79, 32, 2, 139, 201, 4, 72, 6, 44, 2, 80, - 72, 161, 5, 4, 83, 65, 77, 80, 4, 182, 156, 4, 85, 155, 1, 65, 4, 32, 2, - 79, 32, 239, 236, 4, 85, 2, 11, 82, 2, 167, 234, 4, 85, 36, 44, 4, 65, - 82, 65, 32, 225, 1, 2, 79, 32, 28, 62, 65, 130, 1, 85, 130, 225, 2, 73, - 222, 137, 2, 69, 3, 79, 13, 64, 6, 73, 32, 77, 65, 73, 77, 154, 235, 4, - 65, 2, 69, 3, 77, 4, 26, 65, 255, 160, 3, 85, 2, 11, 76, 2, 151, 215, 4, - 65, 9, 186, 170, 4, 69, 163, 64, 85, 8, 24, 2, 82, 85, 23, 83, 2, 199, - 214, 4, 83, 6, 242, 199, 4, 65, 226, 31, 85, 187, 2, 79, 18, 30, 72, 133, - 2, 2, 79, 32, 14, 48, 6, 65, 78, 84, 72, 65, 75, 21, 2, 79, 32, 2, 163, - 213, 3, 72, 12, 80, 8, 78, 65, 78, 71, 77, 79, 78, 84, 20, 4, 80, 72, 85, - 84, 13, 2, 84, 72, 2, 175, 201, 4, 72, 2, 123, 72, 8, 34, 65, 234, 150, - 4, 79, 3, 85, 4, 222, 157, 3, 72, 131, 202, 1, 78, 4, 38, 84, 209, 177, - 2, 3, 80, 65, 84, 2, 159, 200, 4, 65, 2, 11, 87, 2, 139, 147, 3, 65, 6, - 30, 65, 45, 3, 79, 32, 89, 2, 21, 3, 77, 65, 75, 2, 167, 156, 3, 75, 4, - 154, 149, 4, 73, 227, 77, 65, 10, 30, 69, 145, 1, 2, 77, 79, 6, 18, 32, - 107, 70, 4, 84, 11, 68, 79, 69, 83, 32, 78, 79, 84, 32, 69, 88, 189, 208, - 3, 4, 69, 88, 73, 83, 2, 235, 122, 73, 2, 159, 196, 3, 79, 4, 46, 77, - 141, 188, 3, 5, 68, 89, 78, 65, 77, 2, 155, 172, 3, 69, 10, 18, 78, 95, - 82, 8, 26, 32, 179, 219, 3, 75, 6, 26, 83, 251, 218, 2, 71, 4, 206, 206, - 2, 65, 223, 215, 1, 80, 2, 21, 3, 68, 32, 80, 2, 25, 4, 76, 65, 67, 69, - 2, 17, 2, 32, 77, 2, 131, 207, 3, 69, 4, 56, 4, 85, 71, 72, 84, 161, 206, - 3, 4, 78, 71, 32, 83, 2, 161, 187, 1, 5, 32, 66, 65, 76, 76, 46, 22, 32, - 207, 3, 45, 30, 154, 1, 68, 90, 76, 118, 82, 198, 153, 1, 66, 86, 67, - 194, 6, 83, 134, 167, 1, 80, 137, 22, 15, 78, 69, 84, 87, 79, 82, 75, 69, - 68, 32, 67, 79, 77, 80, 85, 4, 64, 10, 73, 77, 69, 78, 83, 73, 79, 78, - 65, 76, 163, 157, 1, 79, 2, 139, 187, 3, 32, 6, 80, 12, 73, 78, 69, 83, - 32, 67, 79, 78, 86, 69, 82, 71, 149, 150, 1, 2, 69, 70, 4, 225, 179, 4, - 3, 73, 78, 71, 10, 40, 4, 65, 89, 83, 32, 183, 149, 1, 73, 8, 146, 192, - 2, 66, 166, 161, 1, 65, 130, 82, 76, 31, 82, 16, 44, 2, 68, 32, 254, 3, - 80, 147, 156, 1, 69, 12, 196, 2, 12, 84, 79, 80, 45, 76, 73, 71, 72, 84, - 69, 68, 32, 80, 17, 76, 69, 70, 84, 45, 76, 73, 71, 72, 84, 69, 68, 32, - 68, 79, 87, 78, 0, 16, 82, 73, 71, 72, 84, 45, 76, 73, 71, 72, 84, 69, - 68, 32, 85, 80, 245, 132, 2, 25, 66, 79, 84, 84, 79, 77, 45, 76, 73, 71, - 72, 84, 69, 68, 32, 82, 73, 71, 72, 84, 87, 65, 82, 68, 83, 6, 76, 4, 76, - 69, 70, 84, 69, 11, 82, 73, 71, 72, 84, 87, 65, 82, 68, 83, 32, 2, 11, - 87, 2, 25, 4, 65, 82, 68, 83, 2, 197, 238, 1, 2, 32, 69, 4, 170, 238, 1, - 69, 159, 22, 65, 2, 249, 154, 4, 5, 69, 82, 45, 69, 77, 8, 34, 77, 85, 4, - 78, 68, 69, 82, 4, 21, 3, 66, 83, 32, 4, 38, 85, 149, 223, 2, 3, 68, 79, - 87, 2, 179, 209, 3, 80, 4, 180, 171, 3, 10, 32, 67, 76, 79, 85, 68, 32, - 65, 78, 68, 161, 46, 2, 83, 84, 230, 5, 204, 1, 6, 66, 69, 84, 65, 78, - 32, 184, 45, 6, 69, 32, 79, 86, 69, 82, 72, 7, 70, 73, 78, 65, 71, 72, - 32, 150, 9, 71, 152, 1, 3, 76, 68, 69, 252, 2, 2, 77, 69, 56, 2, 78, 89, - 78, 82, 139, 199, 3, 67, 160, 3, 228, 2, 18, 65, 83, 84, 82, 79, 76, 79, - 71, 73, 67, 65, 76, 32, 83, 73, 71, 78, 32, 172, 1, 18, 67, 65, 78, 84, - 73, 76, 76, 65, 84, 73, 79, 78, 32, 83, 73, 71, 78, 32, 208, 1, 6, 68, - 73, 71, 73, 84, 32, 100, 9, 75, 85, 32, 82, 85, 32, 75, 72, 65, 26, 76, - 176, 3, 5, 77, 65, 82, 75, 32, 222, 18, 83, 181, 15, 11, 86, 79, 87, 69, - 76, 32, 83, 73, 71, 78, 32, 6, 46, 83, 205, 40, 6, 45, 75, 72, 89, 85, - 68, 4, 104, 8, 68, 79, 78, 71, 32, 84, 83, 72, 205, 21, 13, 71, 82, 65, - 32, 71, 67, 65, 78, 32, 45, 67, 72, 65, 2, 235, 31, 85, 8, 144, 1, 5, 72, - 69, 65, 86, 89, 0, 5, 76, 73, 71, 72, 84, 40, 7, 83, 66, 85, 66, 32, 45, - 67, 193, 171, 4, 8, 67, 65, 78, 71, 32, 84, 69, 45, 2, 17, 2, 32, 66, 2, - 211, 188, 3, 69, 2, 231, 198, 3, 72, 40, 156, 223, 2, 4, 72, 65, 76, 70, - 82, 70, 30, 83, 42, 84, 62, 90, 238, 85, 78, 14, 79, 223, 110, 69, 5, - 221, 10, 2, 32, 66, 92, 96, 6, 69, 84, 84, 69, 82, 32, 153, 2, 13, 79, - 71, 79, 84, 89, 80, 69, 32, 83, 73, 71, 78, 32, 88, 230, 1, 75, 162, 115, - 82, 212, 142, 3, 10, 70, 73, 88, 69, 68, 45, 70, 79, 82, 77, 202, 1, 68, - 86, 78, 46, 83, 38, 84, 46, 66, 2, 67, 2, 71, 2, 80, 2, 90, 138, 69, 45, - 2, 72, 2, 74, 2, 76, 2, 77, 2, 87, 2, 89, 187, 2, 65, 8, 134, 202, 4, 83, - 14, 72, 2, 75, 187, 2, 65, 4, 224, 25, 3, 76, 72, 65, 13, 4, 67, 72, 65, - 68, 80, 254, 2, 66, 176, 1, 8, 77, 78, 89, 65, 77, 32, 89, 73, 114, 67, - 172, 2, 13, 89, 73, 71, 32, 77, 71, 79, 32, 84, 83, 72, 69, 71, 190, 1, - 71, 212, 2, 9, 65, 78, 71, 32, 75, 72, 65, 78, 71, 58, 72, 48, 2, 73, 78, - 246, 1, 78, 222, 1, 82, 118, 83, 46, 84, 36, 4, 76, 69, 65, 68, 212, 100, - 2, 80, 65, 181, 191, 1, 17, 68, 69, 76, 73, 77, 73, 84, 69, 82, 32, 84, - 83, 72, 69, 71, 32, 66, 10, 52, 9, 75, 65, 45, 32, 83, 72, 79, 71, 32, - 27, 83, 4, 142, 1, 71, 67, 89, 6, 34, 75, 201, 21, 3, 68, 85, 83, 4, 56, - 6, 65, 45, 32, 83, 72, 79, 89, 4, 85, 82, 32, 89, 2, 21, 3, 71, 32, 71, - 2, 41, 8, 73, 32, 77, 71, 79, 32, 82, 71, 2, 243, 252, 2, 89, 2, 221, 2, - 2, 73, 71, 12, 84, 5, 65, 82, 69, 84, 32, 240, 1, 2, 72, 69, 29, 7, 76, - 79, 83, 73, 78, 71, 32, 6, 112, 12, 45, 68, 90, 85, 68, 32, 82, 84, 65, - 71, 83, 32, 97, 12, 89, 73, 71, 32, 77, 71, 79, 32, 80, 72, 85, 82, 4, - 42, 66, 37, 6, 77, 69, 32, 76, 79, 78, 2, 33, 6, 90, 72, 73, 32, 77, 73, - 2, 203, 22, 71, 2, 225, 3, 3, 32, 83, 72, 2, 149, 183, 2, 2, 32, 77, 4, - 68, 13, 66, 82, 68, 65, 32, 82, 78, 89, 73, 78, 71, 32, 89, 3, 89, 2, - 213, 5, 11, 73, 71, 32, 77, 71, 79, 32, 83, 71, 65, 66, 12, 68, 4, 84, - 69, 82, 32, 141, 2, 8, 85, 71, 32, 82, 84, 65, 71, 83, 8, 56, 8, 89, 73, - 71, 32, 77, 71, 79, 32, 227, 251, 3, 84, 6, 68, 4, 45, 85, 77, 32, 117, - 9, 84, 82, 85, 78, 67, 65, 84, 69, 68, 4, 88, 7, 82, 78, 65, 77, 32, 66, - 67, 245, 2, 10, 71, 84, 69, 82, 32, 84, 83, 72, 69, 71, 2, 241, 2, 2, 65, - 68, 2, 231, 189, 4, 32, 4, 21, 3, 32, 71, 89, 4, 146, 240, 3, 79, 135, - 18, 65, 2, 17, 2, 65, 76, 2, 241, 154, 4, 2, 65, 78, 6, 92, 6, 73, 84, - 73, 65, 76, 32, 181, 248, 3, 11, 84, 69, 82, 83, 89, 76, 76, 65, 66, 73, - 67, 4, 68, 13, 66, 82, 68, 65, 32, 82, 78, 89, 73, 78, 71, 32, 89, 3, 89, - 2, 53, 11, 73, 71, 32, 77, 71, 79, 32, 77, 68, 85, 78, 2, 179, 156, 4, - 32, 10, 72, 10, 71, 65, 83, 32, 66, 90, 85, 78, 71, 32, 77, 4, 89, 73, - 83, 32, 4, 56, 3, 83, 71, 79, 145, 154, 4, 5, 78, 89, 73, 32, 90, 2, 251, - 9, 82, 6, 44, 5, 84, 83, 72, 69, 71, 179, 245, 3, 83, 5, 163, 245, 3, 32, - 4, 128, 245, 3, 8, 71, 89, 65, 32, 71, 82, 65, 77, 1, 14, 73, 78, 32, 67, - 72, 69, 78, 32, 83, 80, 85, 78, 71, 83, 4, 140, 244, 3, 4, 66, 82, 85, - 76, 39, 72, 6, 32, 4, 82, 65, 73, 76, 55, 83, 2, 225, 7, 9, 73, 78, 71, - 32, 77, 67, 72, 65, 78, 4, 56, 5, 65, 32, 45, 80, 72, 209, 242, 3, 3, 72, - 69, 71, 2, 183, 148, 4, 82, 156, 1, 84, 4, 73, 71, 78, 32, 228, 6, 9, 85, - 66, 74, 79, 73, 78, 69, 68, 32, 143, 4, 89, 42, 176, 1, 4, 71, 82, 85, - 32, 96, 2, 76, 67, 30, 77, 36, 11, 78, 89, 73, 32, 90, 76, 65, 32, 78, - 65, 65, 22, 82, 252, 2, 2, 89, 65, 130, 4, 73, 165, 4, 5, 83, 78, 65, 32, - 76, 4, 40, 3, 67, 65, 78, 1, 3, 77, 69, 68, 2, 25, 4, 32, 82, 71, 89, 2, - 169, 4, 2, 73, 78, 4, 238, 3, 73, 179, 4, 69, 4, 136, 4, 2, 65, 82, 231, - 3, 67, 2, 215, 144, 4, 32, 20, 116, 4, 68, 69, 76, 32, 240, 1, 10, 74, - 69, 83, 32, 83, 85, 32, 78, 71, 65, 181, 236, 3, 6, 78, 65, 77, 32, 66, - 67, 16, 52, 5, 68, 75, 65, 82, 32, 53, 4, 78, 65, 71, 32, 8, 94, 71, 181, - 202, 2, 6, 82, 68, 69, 76, 32, 78, 8, 42, 71, 69, 6, 82, 68, 69, 76, 32, - 68, 6, 46, 67, 212, 143, 2, 2, 78, 89, 167, 122, 83, 2, 179, 178, 4, 73, - 2, 187, 145, 3, 75, 2, 199, 195, 2, 32, 4, 18, 78, 71, 82, 2, 11, 71, 2, - 21, 3, 32, 82, 84, 2, 11, 65, 2, 171, 244, 3, 71, 2, 17, 2, 32, 84, 2, - 219, 157, 3, 83, 94, 68, 7, 76, 69, 84, 84, 69, 82, 32, 145, 2, 5, 83, - 73, 71, 78, 32, 88, 220, 1, 10, 70, 73, 88, 69, 68, 45, 70, 79, 82, 77, - 150, 230, 3, 68, 50, 75, 38, 78, 46, 83, 38, 84, 46, 66, 2, 67, 2, 71, 2, - 80, 2, 90, 138, 69, 45, 2, 72, 2, 74, 2, 76, 2, 77, 2, 82, 2, 87, 2, 89, - 187, 2, 65, 6, 11, 32, 6, 222, 172, 4, 82, 2, 87, 3, 89, 6, 38, 73, 50, - 77, 33, 3, 76, 67, 69, 2, 45, 9, 78, 86, 69, 82, 84, 69, 68, 32, 77, 2, - 11, 67, 2, 45, 2, 72, 85, 2, 25, 4, 32, 84, 83, 65, 2, 11, 32, 2, 211, - 227, 2, 67, 20, 60, 6, 76, 76, 65, 66, 76, 69, 21, 5, 77, 66, 79, 76, 32, - 2, 155, 156, 4, 32, 18, 104, 4, 68, 82, 73, 76, 32, 6, 78, 79, 82, 32, - 66, 85, 130, 1, 80, 93, 7, 82, 68, 79, 32, 82, 74, 69, 2, 11, 32, 2, 139, - 135, 4, 66, 9, 11, 32, 6, 72, 4, 66, 90, 72, 73, 0, 4, 71, 83, 85, 77, 1, - 4, 78, 89, 73, 83, 2, 177, 247, 1, 5, 32, 45, 75, 72, 89, 4, 52, 6, 65, - 68, 77, 65, 32, 71, 21, 3, 72, 85, 82, 2, 167, 224, 2, 68, 2, 191, 112, - 32, 5, 237, 238, 2, 6, 32, 82, 71, 89, 65, 32, 30, 116, 8, 82, 69, 86, - 69, 82, 83, 69, 68, 186, 85, 85, 22, 86, 182, 141, 1, 79, 134, 60, 73, - 190, 201, 1, 69, 235, 61, 65, 4, 129, 159, 2, 2, 32, 73, 2, 25, 4, 32, - 73, 78, 70, 2, 11, 73, 2, 11, 78, 2, 151, 187, 2, 73, 118, 216, 1, 9, 67, - 79, 78, 83, 79, 78, 65, 78, 84, 20, 7, 76, 69, 84, 84, 69, 82, 32, 236, - 6, 20, 77, 79, 68, 73, 70, 73, 69, 82, 32, 76, 69, 84, 84, 69, 82, 32, - 76, 65, 66, 73, 37, 8, 83, 69, 80, 65, 82, 65, 84, 79, 2, 131, 232, 3, - 32, 112, 106, 65, 108, 17, 66, 69, 82, 66, 69, 82, 32, 65, 67, 65, 68, - 69, 77, 89, 32, 89, 65, 30, 84, 223, 1, 89, 4, 84, 6, 89, 69, 82, 32, 89, - 65, 173, 163, 4, 9, 72, 65, 71, 71, 65, 82, 32, 89, 65, 2, 183, 163, 4, - 71, 4, 178, 164, 4, 72, 3, 74, 18, 92, 11, 65, 87, 69, 76, 76, 69, 77, - 69, 84, 32, 89, 33, 8, 85, 65, 82, 69, 71, 32, 89, 65, 2, 11, 65, 2, 171, - 163, 4, 90, 16, 62, 71, 190, 2, 75, 142, 159, 4, 90, 62, 78, 86, 72, 3, - 81, 4, 214, 162, 4, 72, 3, 78, 86, 54, 65, 210, 2, 69, 182, 159, 4, 73, - 2, 79, 3, 85, 77, 190, 1, 68, 30, 71, 2, 75, 14, 66, 2, 72, 22, 83, 30, - 84, 30, 90, 138, 77, 82, 186, 209, 3, 67, 146, 1, 65, 2, 70, 2, 74, 2, - 76, 2, 77, 2, 78, 2, 80, 2, 81, 2, 86, 2, 87, 3, 89, 9, 38, 68, 159, 160, - 4, 72, 7, 11, 72, 5, 155, 160, 4, 72, 7, 134, 160, 4, 72, 3, 83, 7, 234, - 159, 4, 72, 3, 84, 7, 206, 159, 4, 72, 3, 90, 5, 179, 159, 4, 89, 2, 233, - 181, 3, 4, 65, 76, 73, 90, 2, 183, 221, 3, 82, 6, 76, 2, 69, 82, 21, 13, - 72, 84, 32, 84, 82, 73, 70, 79, 76, 73, 65, 84, 69, 5, 131, 225, 3, 32, - 2, 33, 6, 32, 83, 78, 79, 87, 70, 2, 175, 53, 76, 19, 11, 32, 16, 72, 8, - 79, 80, 69, 82, 65, 84, 79, 82, 233, 1, 5, 87, 73, 84, 72, 32, 11, 11, - 32, 8, 38, 65, 101, 5, 87, 73, 84, 72, 32, 4, 25, 4, 66, 79, 86, 69, 4, - 11, 32, 4, 26, 76, 139, 252, 2, 82, 2, 11, 69, 2, 147, 252, 2, 70, 4, 26, - 82, 251, 154, 3, 68, 2, 17, 2, 73, 83, 2, 241, 202, 2, 3, 73, 78, 71, 6, - 26, 68, 143, 234, 1, 82, 4, 11, 79, 4, 175, 252, 1, 84, 6, 22, 82, 239, - 125, 83, 2, 11, 32, 2, 243, 171, 1, 67, 5, 21, 3, 32, 84, 87, 2, 21, 3, - 79, 32, 68, 2, 237, 86, 3, 79, 84, 83, 170, 1, 100, 5, 72, 85, 84, 65, - 32, 184, 6, 11, 79, 78, 73, 65, 78, 32, 83, 73, 71, 78, 32, 227, 129, 3, - 69, 164, 1, 178, 1, 65, 88, 7, 76, 69, 84, 84, 69, 82, 32, 204, 1, 2, 83, - 73, 200, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 162, 162, 2, - 68, 204, 152, 1, 2, 71, 86, 255, 71, 79, 4, 18, 66, 51, 78, 2, 29, 5, 66, - 82, 69, 86, 73, 2, 171, 22, 65, 2, 143, 131, 4, 74, 94, 178, 60, 65, 38, - 68, 114, 84, 46, 86, 186, 5, 85, 206, 201, 1, 73, 162, 193, 1, 78, 46, - 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, 77, - 2, 82, 2, 89, 186, 2, 69, 3, 79, 12, 21, 3, 71, 78, 32, 12, 46, 67, 98, - 78, 242, 60, 65, 231, 147, 3, 86, 2, 11, 65, 2, 11, 78, 2, 25, 4, 68, 82, - 65, 66, 2, 11, 73, 2, 11, 78, 2, 139, 239, 3, 68, 2, 11, 85, 2, 135, 239, - 3, 75, 30, 78, 83, 166, 63, 65, 38, 85, 22, 86, 186, 201, 1, 73, 222, - 137, 2, 69, 3, 79, 4, 25, 4, 72, 79, 82, 84, 4, 11, 32, 4, 198, 146, 4, - 69, 3, 79, 4, 144, 221, 3, 8, 67, 65, 80, 73, 84, 65, 76, 32, 239, 24, - 69, 146, 2, 140, 2, 12, 68, 72, 82, 73, 32, 76, 69, 84, 84, 69, 82, 32, - 182, 4, 77, 18, 78, 34, 79, 80, 2, 80, 32, 168, 17, 23, 82, 84, 79, 73, - 83, 69, 32, 83, 72, 69, 76, 76, 32, 66, 82, 65, 67, 75, 69, 84, 69, 68, - 32, 130, 4, 84, 222, 149, 2, 73, 149, 70, 5, 75, 89, 79, 32, 84, 104, - 242, 1, 71, 42, 74, 30, 77, 34, 78, 70, 72, 34, 80, 34, 83, 210, 50, 82, - 206, 147, 1, 79, 134, 60, 69, 42, 76, 198, 2, 65, 178, 191, 1, 67, 2, 68, - 2, 75, 2, 84, 2, 88, 2, 90, 138, 69, 66, 2, 70, 2, 81, 2, 86, 186, 2, 73, - 2, 85, 3, 89, 6, 170, 139, 4, 72, 2, 74, 187, 2, 65, 4, 186, 141, 4, 65, - 3, 89, 4, 230, 138, 4, 66, 187, 2, 65, 14, 66, 71, 190, 194, 2, 74, 194, - 130, 1, 88, 138, 69, 68, 187, 2, 65, 4, 130, 138, 4, 74, 187, 2, 65, 4, - 226, 137, 4, 83, 187, 2, 65, 10, 54, 72, 198, 193, 2, 75, 202, 199, 1, - 84, 187, 2, 65, 4, 138, 137, 4, 84, 187, 2, 65, 2, 231, 28, 65, 2, 11, - 71, 2, 179, 244, 3, 85, 6, 32, 2, 84, 72, 151, 254, 2, 76, 5, 11, 66, 2, - 11, 82, 2, 151, 162, 2, 85, 72, 252, 1, 4, 65, 82, 67, 32, 162, 2, 67, - 44, 2, 72, 65, 166, 3, 80, 140, 3, 13, 74, 85, 83, 84, 73, 70, 73, 69, - 68, 32, 76, 79, 87, 84, 5, 76, 69, 70, 84, 32, 228, 1, 6, 82, 73, 71, 72, - 84, 32, 170, 2, 83, 38, 84, 81, 7, 87, 73, 84, 72, 32, 85, 80, 6, 180, 1, - 19, 65, 78, 84, 73, 67, 76, 79, 67, 75, 87, 73, 83, 69, 32, 65, 82, 82, - 79, 87, 73, 21, 67, 76, 79, 67, 75, 87, 73, 83, 69, 32, 65, 82, 82, 79, - 87, 32, 87, 73, 84, 72, 32, 5, 29, 5, 32, 87, 73, 84, 72, 2, 17, 2, 32, - 80, 2, 187, 128, 2, 76, 2, 11, 77, 2, 143, 128, 2, 73, 2, 11, 85, 2, 133, - 179, 3, 3, 82, 76, 89, 34, 36, 3, 76, 70, 32, 179, 133, 4, 84, 32, 70, - 70, 186, 1, 76, 22, 82, 178, 2, 83, 250, 5, 66, 231, 243, 1, 73, 8, 136, - 1, 15, 79, 82, 87, 65, 82, 68, 45, 70, 65, 67, 73, 78, 71, 32, 82, 157, - 2, 13, 76, 65, 73, 76, 73, 78, 71, 32, 82, 79, 66, 79, 84, 4, 22, 85, - 243, 1, 79, 2, 203, 197, 3, 78, 8, 41, 2, 69, 70, 8, 21, 3, 73, 71, 72, - 8, 11, 84, 8, 54, 32, 69, 9, 45, 70, 65, 67, 73, 78, 71, 32, 82, 2, 11, - 80, 2, 33, 6, 65, 82, 69, 78, 84, 72, 2, 207, 222, 1, 69, 6, 38, 79, 21, - 5, 85, 78, 78, 69, 82, 2, 155, 177, 3, 66, 4, 29, 5, 32, 70, 82, 65, 77, - 4, 11, 69, 4, 11, 45, 4, 134, 129, 4, 49, 3, 50, 4, 18, 69, 59, 84, 2, - 11, 67, 2, 11, 84, 2, 11, 73, 2, 147, 135, 2, 79, 2, 17, 2, 65, 78, 2, - 11, 68, 2, 21, 3, 73, 78, 71, 2, 17, 2, 32, 80, 2, 11, 69, 2, 11, 82, 2, - 207, 175, 3, 83, 4, 17, 2, 69, 82, 4, 33, 6, 32, 72, 65, 76, 70, 32, 4, - 250, 3, 66, 139, 105, 87, 10, 180, 1, 13, 74, 85, 83, 84, 73, 70, 73, 69, - 68, 32, 76, 79, 87, 126, 67, 50, 72, 225, 214, 2, 21, 66, 76, 65, 67, 75, - 32, 76, 69, 70, 84, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 83, 2, 237, - 1, 7, 69, 82, 32, 82, 73, 71, 72, 8, 78, 67, 50, 72, 33, 13, 74, 85, 83, - 84, 73, 70, 73, 69, 68, 32, 76, 79, 87, 4, 26, 79, 243, 130, 3, 82, 2, - 139, 190, 3, 82, 2, 253, 168, 3, 3, 65, 76, 70, 2, 33, 6, 69, 82, 32, 76, - 69, 70, 2, 53, 11, 84, 32, 81, 85, 65, 82, 84, 69, 82, 32, 66, 2, 11, 76, - 2, 209, 168, 2, 3, 65, 67, 75, 2, 201, 167, 3, 4, 81, 85, 65, 82, 2, 11, - 79, 2, 165, 167, 3, 12, 82, 84, 79, 73, 83, 69, 32, 83, 72, 69, 76, 76, - 2, 29, 5, 87, 65, 82, 68, 83, 2, 17, 2, 32, 65, 2, 153, 252, 2, 4, 82, - 82, 79, 87, 20, 188, 1, 22, 67, 74, 75, 32, 85, 78, 73, 70, 73, 69, 68, - 32, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 161, 2, 19, 76, 65, 84, 73, - 78, 32, 67, 65, 80, 73, 84, 65, 76, 32, 76, 69, 84, 84, 69, 18, 40, 2, - 52, 69, 34, 53, 54, 54, 87, 55, 4, 202, 1, 48, 203, 205, 2, 56, 4, 30, - 50, 141, 1, 2, 66, 56, 2, 175, 176, 3, 68, 6, 48, 2, 50, 53, 22, 53, 249, - 205, 2, 2, 55, 50, 2, 135, 246, 3, 51, 2, 67, 53, 4, 32, 2, 48, 66, 21, - 2, 54, 68, 2, 195, 245, 3, 57, 2, 175, 245, 3, 55, 2, 11, 82, 2, 179, - 183, 3, 32, 64, 48, 6, 65, 76, 32, 82, 85, 78, 21, 2, 79, 32, 2, 247, - 250, 2, 79, 62, 72, 7, 76, 69, 84, 84, 69, 82, 32, 217, 2, 6, 83, 73, 71, - 78, 32, 82, 60, 206, 1, 66, 106, 78, 138, 30, 73, 202, 141, 1, 69, 162, - 75, 67, 162, 186, 1, 65, 234, 61, 68, 2, 71, 2, 72, 2, 74, 2, 75, 2, 76, - 2, 77, 2, 80, 2, 82, 2, 83, 2, 84, 2, 87, 2, 89, 186, 2, 79, 3, 85, 12, - 52, 7, 82, 69, 65, 84, 72, 89, 32, 227, 241, 3, 65, 10, 182, 30, 73, 202, - 141, 1, 69, 143, 175, 2, 65, 4, 246, 238, 3, 71, 187, 2, 65, 2, 17, 2, - 73, 83, 2, 21, 3, 73, 78, 71, 2, 11, 32, 2, 147, 198, 2, 84, 98, 46, 65, - 254, 1, 73, 246, 11, 79, 195, 1, 85, 16, 66, 67, 36, 2, 68, 69, 34, 77, - 20, 2, 78, 83, 155, 159, 3, 73, 4, 246, 231, 1, 75, 243, 206, 1, 84, 2, - 141, 16, 4, 32, 77, 65, 82, 5, 131, 212, 1, 32, 4, 44, 2, 80, 76, 33, 5, - 86, 69, 82, 83, 65, 2, 11, 85, 2, 199, 207, 3, 84, 2, 171, 62, 76, 66, - 156, 1, 3, 65, 78, 71, 176, 3, 8, 68, 69, 78, 84, 32, 69, 77, 66, 20, 9, - 71, 82, 65, 77, 32, 70, 79, 82, 32, 140, 2, 4, 80, 76, 69, 32, 167, 151, - 3, 67, 16, 48, 2, 76, 69, 245, 1, 5, 85, 76, 65, 82, 32, 10, 44, 6, 32, - 87, 73, 84, 72, 32, 163, 1, 45, 8, 80, 9, 83, 69, 82, 73, 70, 83, 32, 65, - 84, 54, 85, 138, 168, 2, 82, 187, 66, 68, 2, 17, 2, 32, 66, 2, 197, 218, - 3, 3, 79, 84, 84, 2, 241, 60, 2, 78, 68, 2, 205, 202, 2, 4, 72, 69, 65, - 68, 6, 68, 9, 70, 76, 65, 71, 32, 79, 78, 32, 80, 34, 82, 179, 138, 2, - 66, 2, 11, 79, 2, 207, 205, 3, 83, 2, 11, 85, 2, 239, 171, 3, 76, 2, 155, - 215, 3, 76, 16, 66, 69, 34, 72, 34, 76, 22, 77, 46, 84, 42, 87, 255, 210, - 1, 70, 2, 11, 65, 2, 211, 230, 1, 82, 2, 11, 69, 2, 211, 248, 1, 65, 2, - 215, 184, 3, 65, 2, 21, 3, 79, 85, 78, 2, 175, 187, 2, 84, 2, 17, 2, 72, - 85, 2, 215, 199, 1, 78, 4, 206, 175, 2, 65, 131, 42, 73, 30, 176, 2, 3, - 67, 79, 76, 24, 20, 72, 79, 82, 73, 90, 79, 78, 84, 65, 76, 32, 66, 65, - 82, 32, 87, 73, 84, 72, 32, 98, 80, 34, 84, 20, 13, 86, 69, 82, 84, 73, - 67, 65, 76, 32, 66, 65, 82, 32, 44, 9, 83, 79, 76, 73, 68, 85, 83, 32, - 66, 188, 21, 2, 68, 65, 168, 187, 1, 6, 78, 69, 83, 84, 69, 68, 171, 11, - 73, 2, 129, 37, 2, 79, 78, 4, 34, 68, 33, 4, 84, 82, 73, 80, 2, 17, 2, - 79, 85, 2, 11, 66, 2, 185, 179, 3, 2, 76, 69, 4, 254, 221, 1, 76, 135, - 110, 82, 2, 167, 202, 1, 73, 8, 42, 66, 74, 68, 134, 189, 1, 82, 115, 87, - 2, 29, 5, 73, 78, 65, 82, 89, 2, 21, 3, 32, 82, 69, 2, 235, 32, 76, 2, - 25, 4, 69, 76, 73, 77, 2, 191, 170, 2, 73, 10, 24, 2, 76, 76, 35, 80, 5, - 137, 220, 1, 3, 69, 89, 66, 6, 44, 5, 73, 67, 65, 76, 32, 243, 207, 3, - 72, 4, 18, 68, 47, 70, 2, 11, 82, 2, 11, 73, 2, 243, 221, 3, 78, 2, 215, - 248, 1, 73, 6, 18, 69, 79, 77, 5, 129, 188, 3, 14, 32, 76, 73, 71, 72, - 84, 32, 77, 79, 79, 78, 32, 65, 82, 2, 235, 170, 3, 80, 216, 1, 76, 3, - 71, 82, 73, 22, 76, 214, 12, 82, 221, 186, 2, 5, 77, 66, 76, 69, 82, 2, - 155, 216, 2, 75, 162, 1, 68, 11, 85, 45, 84, 73, 71, 65, 76, 65, 82, 73, - 32, 179, 222, 3, 73, 160, 1, 122, 65, 42, 67, 30, 68, 106, 71, 32, 7, 76, - 69, 84, 84, 69, 82, 32, 178, 3, 82, 32, 5, 83, 73, 71, 78, 32, 131, 3, - 86, 2, 11, 85, 2, 177, 155, 3, 2, 32, 76, 2, 245, 158, 3, 2, 79, 78, 4, - 18, 79, 67, 65, 2, 11, 85, 2, 11, 66, 2, 25, 4, 76, 69, 32, 68, 2, 11, - 65, 2, 159, 183, 3, 78, 2, 129, 215, 2, 3, 69, 77, 73, 100, 206, 1, 65, - 38, 68, 46, 76, 38, 82, 34, 84, 46, 86, 186, 5, 85, 206, 201, 1, 73, 162, - 193, 1, 78, 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 206, 40, - 79, 162, 8, 69, 158, 20, 72, 2, 77, 3, 89, 9, 158, 218, 3, 65, 2, 73, 3, - 85, 8, 186, 146, 3, 68, 138, 69, 72, 187, 2, 65, 6, 154, 208, 1, 76, 183, - 137, 2, 65, 4, 242, 214, 3, 82, 187, 2, 65, 8, 202, 145, 3, 84, 138, 69, - 72, 187, 2, 65, 10, 214, 5, 79, 139, 211, 3, 65, 2, 11, 69, 2, 243, 221, - 1, 80, 18, 174, 1, 65, 72, 6, 76, 79, 79, 80, 69, 68, 40, 2, 79, 77, 0, - 5, 83, 72, 82, 73, 73, 48, 13, 67, 65, 78, 68, 82, 65, 32, 65, 78, 85, - 78, 65, 83, 22, 80, 179, 146, 3, 86, 4, 26, 86, 235, 145, 3, 78, 2, 21, - 3, 65, 71, 82, 2, 251, 219, 1, 65, 2, 17, 2, 32, 86, 2, 187, 157, 1, 73, - 2, 17, 2, 32, 80, 2, 25, 4, 85, 83, 72, 80, 2, 231, 204, 3, 73, 2, 237, - 176, 3, 2, 76, 85, 30, 56, 10, 69, 68, 73, 67, 32, 84, 79, 78, 69, 32, - 35, 79, 4, 226, 164, 1, 65, 235, 4, 83, 26, 45, 9, 87, 69, 76, 32, 83, - 73, 71, 78, 32, 26, 70, 65, 38, 85, 22, 86, 186, 201, 1, 73, 234, 234, 1, - 79, 163, 8, 69, 6, 202, 211, 3, 65, 2, 73, 3, 85, 5, 167, 211, 3, 85, 8, - 11, 79, 8, 33, 6, 67, 65, 76, 73, 67, 32, 8, 26, 82, 159, 214, 2, 76, 5, - 199, 210, 3, 82, 50, 50, 75, 84, 4, 78, 69, 68, 32, 135, 173, 2, 84, 4, - 48, 6, 73, 83, 72, 32, 76, 73, 135, 192, 3, 69, 2, 11, 82, 2, 191, 202, - 2, 65, 44, 238, 1, 65, 80, 6, 66, 76, 65, 67, 75, 32, 40, 7, 87, 72, 73, - 84, 69, 32, 83, 22, 67, 50, 68, 96, 2, 78, 79, 32, 2, 79, 75, 30, 83, - 153, 168, 3, 21, 71, 82, 69, 69, 75, 32, 83, 77, 65, 76, 76, 32, 76, 69, - 84, 84, 69, 82, 32, 73, 79, 4, 26, 77, 239, 170, 2, 78, 2, 17, 2, 80, 69, - 2, 11, 82, 2, 251, 192, 2, 83, 4, 18, 80, 23, 83, 2, 147, 150, 2, 69, 2, - 131, 152, 2, 72, 4, 144, 3, 5, 65, 80, 73, 84, 65, 167, 121, 79, 6, 30, - 65, 33, 3, 73, 71, 73, 2, 11, 71, 2, 231, 143, 3, 71, 4, 221, 221, 1, 3, - 84, 32, 84, 6, 162, 2, 82, 255, 195, 2, 84, 2, 141, 228, 1, 2, 32, 72, - 14, 128, 1, 18, 65, 78, 83, 45, 83, 69, 82, 73, 70, 32, 67, 65, 80, 73, - 84, 65, 76, 32, 38, 69, 32, 3, 77, 65, 76, 33, 2, 79, 85, 6, 222, 203, 3, - 71, 2, 76, 3, 89, 2, 11, 77, 2, 159, 251, 2, 73, 2, 11, 76, 2, 235, 201, - 2, 32, 4, 21, 3, 84, 72, 32, 4, 32, 2, 69, 65, 1, 2, 87, 69, 2, 53, 11, - 83, 84, 32, 80, 79, 73, 78, 84, 73, 78, 71, 2, 17, 2, 32, 76, 2, 195, - 200, 2, 69, 38, 58, 69, 52, 8, 73, 83, 84, 69, 68, 32, 82, 73, 63, 79, 2, - 17, 2, 76, 86, 2, 153, 178, 1, 3, 69, 32, 80, 2, 17, 2, 71, 72, 2, 137, - 109, 6, 84, 87, 65, 82, 68, 83, 34, 30, 32, 229, 10, 2, 45, 69, 32, 134, - 3, 66, 86, 67, 116, 3, 68, 79, 84, 226, 1, 72, 44, 14, 73, 78, 84, 69, - 82, 83, 69, 67, 84, 73, 78, 71, 32, 76, 92, 6, 74, 79, 73, 78, 69, 68, - 64, 8, 76, 79, 71, 73, 67, 65, 76, 32, 98, 77, 0, 3, 87, 79, 77, 116, 13, - 82, 73, 78, 71, 83, 32, 65, 76, 73, 71, 78, 69, 68, 48, 24, 65, 83, 84, - 69, 82, 73, 83, 75, 83, 32, 65, 76, 73, 71, 78, 69, 68, 32, 86, 69, 82, - 84, 73, 67, 35, 83, 2, 29, 5, 85, 84, 84, 79, 78, 2, 17, 2, 32, 77, 2, - 11, 79, 2, 191, 187, 2, 85, 2, 93, 21, 79, 78, 83, 69, 67, 85, 84, 73, - 86, 69, 32, 69, 81, 85, 65, 76, 83, 32, 83, 73, 71, 2, 251, 133, 3, 78, - 6, 18, 32, 55, 83, 4, 22, 76, 131, 1, 80, 2, 145, 163, 1, 2, 69, 65, 2, - 53, 11, 32, 79, 86, 69, 82, 32, 79, 78, 69, 32, 68, 2, 11, 79, 2, 11, 84, - 2, 17, 2, 32, 80, 2, 29, 5, 85, 78, 67, 84, 85, 2, 167, 241, 2, 65, 2, - 11, 69, 2, 11, 65, 2, 159, 173, 2, 82, 4, 17, 2, 79, 71, 4, 25, 4, 73, - 67, 65, 76, 4, 11, 32, 4, 130, 179, 2, 65, 159, 85, 79, 2, 17, 2, 32, 83, - 2, 21, 3, 81, 85, 65, 2, 183, 172, 2, 82, 4, 30, 79, 13, 3, 65, 78, 68, - 2, 11, 82, 2, 11, 32, 2, 11, 79, 2, 11, 80, 2, 143, 18, 69, 2, 11, 69, 2, - 45, 9, 78, 32, 72, 79, 76, 68, 73, 78, 71, 2, 11, 32, 2, 11, 72, 2, 11, - 65, 2, 143, 223, 1, 78, 2, 45, 9, 32, 72, 79, 82, 73, 90, 79, 78, 84, 2, - 11, 65, 2, 219, 154, 3, 76, 2, 49, 10, 80, 69, 69, 67, 72, 32, 66, 85, - 66, 66, 2, 203, 169, 2, 76, 2, 11, 77, 2, 245, 212, 1, 2, 32, 68, 248, 3, - 140, 1, 8, 71, 65, 82, 73, 84, 73, 67, 32, 152, 6, 7, 77, 66, 82, 69, 76, - 76, 65, 166, 1, 78, 158, 8, 80, 138, 166, 1, 82, 151, 134, 2, 83, 62, 48, - 7, 76, 69, 84, 84, 69, 82, 32, 159, 5, 87, 60, 206, 1, 65, 28, 2, 81, 79, - 22, 66, 22, 68, 50, 71, 44, 2, 72, 79, 22, 75, 34, 76, 32, 2, 82, 65, 22, - 83, 74, 84, 74, 89, 22, 90, 150, 182, 2, 78, 202, 91, 80, 246, 5, 87, - 202, 12, 77, 174, 18, 73, 3, 85, 4, 26, 76, 247, 233, 2, 73, 2, 155, 183, - 3, 80, 2, 243, 148, 3, 69, 4, 26, 69, 211, 175, 2, 72, 2, 195, 148, 3, - 76, 4, 166, 140, 2, 72, 189, 138, 1, 2, 65, 77, 5, 151, 182, 3, 84, 4, - 150, 183, 2, 65, 239, 126, 72, 2, 11, 65, 2, 139, 147, 3, 77, 2, 187, - 189, 1, 83, 8, 38, 65, 238, 183, 2, 72, 239, 90, 83, 4, 234, 160, 3, 68, - 239, 13, 77, 6, 38, 72, 206, 154, 3, 69, 175, 28, 79, 2, 11, 65, 2, 231, - 250, 1, 78, 2, 183, 240, 2, 79, 4, 246, 145, 3, 69, 207, 36, 85, 2, 21, - 3, 79, 82, 68, 2, 25, 4, 32, 68, 73, 86, 2, 235, 149, 1, 73, 7, 11, 32, - 4, 84, 4, 79, 78, 32, 71, 45, 13, 87, 73, 84, 72, 32, 82, 65, 73, 78, 32, - 68, 82, 79, 2, 11, 82, 2, 11, 79, 2, 231, 166, 2, 85, 2, 239, 246, 2, 80, - 32, 160, 1, 3, 65, 77, 85, 20, 7, 67, 69, 82, 84, 65, 73, 78, 34, 68, 62, - 73, 245, 5, 18, 77, 65, 82, 82, 73, 69, 68, 32, 80, 65, 82, 84, 78, 69, - 82, 83, 72, 73, 2, 187, 162, 2, 83, 2, 11, 84, 2, 243, 171, 2, 89, 4, 26, - 69, 159, 159, 2, 79, 2, 11, 82, 2, 135, 151, 3, 84, 22, 60, 2, 79, 78, - 234, 3, 84, 106, 86, 173, 58, 3, 67, 79, 82, 15, 11, 32, 12, 160, 1, 6, - 65, 66, 79, 86, 69, 32, 108, 22, 66, 69, 83, 73, 68, 69, 32, 65, 78, 68, - 32, 74, 79, 73, 78, 69, 68, 32, 87, 73, 84, 72, 41, 5, 87, 73, 84, 72, - 32, 4, 52, 9, 66, 65, 82, 32, 65, 66, 79, 86, 69, 23, 73, 2, 17, 2, 32, - 73, 2, 161, 49, 4, 78, 84, 69, 82, 2, 17, 2, 32, 85, 2, 239, 222, 2, 78, - 6, 66, 77, 58, 79, 189, 245, 2, 8, 76, 79, 71, 73, 67, 65, 76, 32, 2, 11, - 73, 2, 11, 78, 2, 11, 85, 2, 183, 167, 2, 83, 2, 11, 86, 2, 209, 140, 2, - 2, 69, 82, 4, 18, 32, 67, 69, 2, 11, 83, 2, 17, 2, 69, 80, 2, 11, 65, 2, - 187, 244, 2, 82, 2, 255, 153, 2, 68, 2, 57, 12, 69, 82, 83, 65, 76, 32, - 82, 69, 67, 89, 67, 76, 2, 17, 2, 73, 78, 2, 155, 153, 2, 71, 2, 135, - 153, 2, 80, 144, 3, 130, 1, 32, 246, 7, 45, 180, 8, 4, 80, 69, 82, 32, - 132, 40, 8, 83, 73, 68, 69, 45, 68, 79, 87, 21, 6, 87, 65, 82, 68, 83, - 32, 38, 140, 1, 5, 65, 82, 82, 79, 87, 224, 1, 5, 66, 65, 82, 66, 32, - 228, 1, 5, 68, 79, 87, 78, 32, 210, 1, 70, 30, 82, 93, 4, 84, 65, 67, 75, - 8, 64, 4, 72, 69, 65, 68, 137, 51, 7, 32, 84, 72, 82, 79, 85, 71, 7, 11, - 32, 4, 112, 22, 66, 69, 84, 87, 69, 69, 78, 32, 84, 87, 79, 32, 72, 79, - 82, 73, 90, 79, 78, 84, 65, 76, 171, 155, 2, 73, 2, 193, 147, 2, 2, 32, - 66, 8, 44, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, 4, 57, 12, 84, 32, 68, - 79, 87, 78, 32, 66, 65, 82, 66, 32, 4, 44, 3, 76, 69, 70, 1, 4, 82, 73, - 71, 72, 2, 11, 84, 2, 25, 4, 32, 72, 65, 82, 2, 11, 80, 2, 231, 214, 2, - 79, 14, 76, 5, 65, 82, 82, 79, 87, 62, 66, 38, 68, 18, 83, 246, 58, 87, - 219, 9, 84, 5, 37, 7, 32, 87, 73, 84, 72, 32, 66, 2, 187, 156, 2, 65, 2, - 221, 138, 2, 4, 76, 65, 67, 75, 2, 191, 59, 79, 2, 179, 65, 65, 2, 11, - 73, 2, 223, 112, 83, 2, 53, 11, 73, 71, 72, 84, 32, 68, 73, 65, 71, 79, - 78, 2, 189, 128, 1, 4, 65, 76, 32, 69, 5, 29, 5, 32, 87, 73, 84, 72, 2, - 33, 6, 32, 67, 73, 82, 67, 76, 2, 175, 166, 2, 69, 32, 58, 70, 225, 1, 9, - 80, 79, 73, 78, 84, 73, 78, 71, 32, 4, 41, 8, 65, 67, 73, 78, 71, 32, 83, - 78, 4, 65, 14, 65, 75, 69, 32, 72, 69, 65, 68, 32, 87, 73, 84, 72, 32, 4, - 42, 79, 25, 6, 67, 76, 79, 83, 69, 68, 2, 21, 3, 80, 69, 78, 2, 21, 3, - 32, 77, 79, 2, 135, 159, 1, 85, 28, 130, 1, 65, 86, 69, 62, 70, 20, 8, - 77, 73, 76, 73, 84, 65, 82, 89, 26, 82, 88, 6, 83, 77, 65, 76, 76, 32, - 118, 84, 255, 121, 71, 4, 22, 84, 159, 2, 73, 2, 37, 7, 79, 77, 73, 67, - 32, 66, 79, 2, 219, 227, 1, 77, 2, 29, 5, 78, 69, 82, 71, 89, 2, 213, - 179, 1, 2, 32, 87, 2, 143, 158, 3, 82, 2, 129, 1, 2, 32, 65, 8, 64, 5, - 79, 67, 75, 69, 84, 118, 69, 222, 129, 1, 65, 247, 28, 73, 2, 239, 200, - 2, 32, 4, 18, 65, 67, 82, 2, 11, 73, 2, 11, 82, 2, 17, 2, 80, 76, 2, 255, - 131, 2, 65, 2, 11, 69, 2, 151, 248, 1, 68, 4, 37, 7, 82, 73, 65, 78, 71, - 76, 69, 4, 11, 32, 4, 11, 87, 4, 25, 4, 73, 84, 72, 32, 4, 18, 76, 27, - 82, 2, 11, 69, 2, 35, 70, 2, 21, 3, 73, 71, 72, 2, 11, 84, 2, 17, 2, 32, - 72, 2, 21, 3, 65, 76, 70, 2, 17, 2, 32, 66, 2, 11, 76, 2, 143, 235, 1, - 65, 200, 1, 192, 1, 4, 65, 78, 68, 32, 210, 2, 66, 74, 67, 74, 70, 36, 5, - 72, 65, 76, 70, 32, 200, 4, 5, 76, 69, 70, 84, 32, 254, 5, 77, 150, 2, - 79, 60, 6, 82, 73, 71, 72, 84, 32, 234, 17, 83, 67, 84, 8, 34, 76, 53, 4, - 82, 73, 71, 72, 6, 48, 2, 69, 70, 173, 1, 5, 79, 87, 69, 82, 32, 2, 53, - 11, 84, 32, 65, 78, 68, 32, 76, 79, 87, 69, 82, 2, 201, 35, 25, 32, 84, - 82, 73, 65, 78, 71, 85, 76, 65, 82, 32, 84, 72, 82, 69, 69, 32, 81, 85, - 65, 82, 84, 69, 82, 4, 28, 2, 84, 82, 231, 39, 79, 2, 225, 4, 7, 73, 65, - 78, 71, 85, 76, 65, 2, 17, 2, 76, 65, 2, 17, 2, 68, 69, 2, 185, 223, 1, - 3, 32, 83, 67, 10, 33, 6, 69, 78, 84, 82, 69, 32, 10, 174, 12, 76, 22, - 82, 251, 21, 79, 2, 17, 2, 73, 86, 2, 179, 31, 69, 20, 140, 1, 5, 66, 76, - 79, 67, 75, 110, 72, 32, 8, 73, 78, 86, 69, 82, 83, 69, 32, 198, 1, 86, - 170, 26, 77, 130, 3, 76, 22, 82, 139, 162, 1, 67, 5, 225, 28, 23, 32, 65, - 78, 68, 32, 76, 79, 87, 69, 82, 32, 72, 65, 76, 70, 32, 73, 78, 86, 69, - 82, 83, 69, 2, 225, 18, 4, 69, 65, 86, 89, 4, 100, 21, 77, 69, 68, 73, - 85, 77, 32, 83, 72, 65, 68, 69, 32, 65, 78, 68, 32, 76, 79, 87, 69, 51, - 87, 2, 11, 82, 2, 185, 35, 5, 32, 72, 65, 76, 70, 2, 21, 3, 72, 73, 84, - 2, 191, 191, 1, 69, 2, 29, 5, 69, 82, 84, 73, 67, 2, 197, 115, 14, 65, - 76, 32, 76, 73, 78, 69, 32, 87, 73, 84, 72, 32, 84, 62, 50, 66, 238, 3, - 67, 54, 79, 74, 84, 179, 13, 81, 22, 65, 14, 76, 79, 67, 75, 32, 68, 73, - 65, 71, 79, 78, 65, 76, 32, 22, 144, 1, 6, 76, 79, 87, 69, 82, 32, 233, - 8, 24, 85, 80, 80, 69, 82, 32, 77, 73, 68, 68, 76, 69, 32, 76, 69, 70, - 84, 32, 84, 79, 32, 85, 80, 80, 18, 176, 1, 10, 67, 69, 78, 84, 82, 69, - 32, 84, 79, 32, 36, 8, 76, 69, 70, 84, 32, 84, 79, 32, 229, 10, 18, 77, - 73, 68, 68, 76, 69, 32, 76, 69, 70, 84, 32, 84, 79, 32, 85, 80, 80, 6, - 70, 76, 253, 8, 3, 85, 80, 80, 6, 34, 76, 213, 9, 3, 85, 80, 80, 2, 149, - 10, 2, 79, 87, 2, 29, 5, 79, 82, 78, 69, 82, 2, 155, 232, 1, 32, 4, 182, - 12, 78, 53, 12, 82, 32, 76, 79, 87, 69, 82, 32, 82, 73, 71, 72, 8, 34, - 79, 166, 19, 82, 155, 1, 87, 2, 237, 18, 11, 32, 76, 79, 87, 69, 82, 32, - 82, 73, 71, 72, 12, 33, 6, 73, 68, 68, 76, 69, 32, 12, 52, 7, 67, 69, 78, - 84, 82, 69, 32, 74, 76, 23, 82, 4, 44, 3, 76, 69, 70, 1, 4, 82, 73, 71, - 72, 2, 117, 3, 84, 32, 79, 4, 41, 2, 69, 70, 4, 21, 3, 73, 71, 72, 4, 17, - 2, 84, 32, 4, 30, 79, 253, 17, 2, 84, 87, 2, 139, 9, 78, 4, 11, 78, 4, - 17, 2, 69, 32, 4, 150, 21, 81, 147, 4, 69, 68, 84, 2, 66, 76, 186, 6, 68, - 114, 79, 222, 1, 80, 38, 81, 224, 4, 2, 83, 72, 51, 84, 22, 61, 13, 79, - 67, 75, 32, 68, 73, 65, 71, 79, 78, 65, 76, 32, 22, 140, 1, 24, 76, 79, - 87, 69, 82, 32, 77, 73, 68, 68, 76, 69, 32, 76, 69, 70, 84, 32, 84, 79, - 32, 76, 79, 87, 57, 6, 85, 80, 80, 69, 82, 32, 4, 21, 3, 69, 82, 32, 4, - 246, 3, 67, 135, 215, 2, 82, 18, 176, 1, 10, 67, 69, 78, 84, 82, 69, 32, - 84, 79, 32, 92, 8, 76, 69, 70, 84, 32, 84, 79, 32, 141, 1, 18, 77, 73, - 68, 68, 76, 69, 32, 76, 69, 70, 84, 32, 84, 79, 32, 76, 79, 87, 6, 32, 3, - 76, 79, 87, 139, 1, 85, 4, 21, 3, 69, 82, 32, 4, 142, 2, 77, 223, 214, 2, - 82, 6, 28, 3, 76, 79, 87, 51, 85, 4, 21, 3, 69, 82, 32, 4, 142, 1, 67, - 43, 77, 2, 17, 2, 80, 80, 2, 17, 2, 69, 82, 2, 117, 2, 32, 77, 6, 21, 3, - 69, 82, 32, 6, 34, 67, 42, 77, 223, 214, 2, 82, 2, 11, 69, 2, 141, 225, - 1, 2, 78, 84, 2, 25, 4, 73, 68, 68, 76, 2, 139, 176, 1, 69, 2, 57, 12, - 82, 79, 80, 45, 83, 72, 65, 68, 79, 87, 69, 68, 2, 17, 2, 32, 87, 2, 213, - 190, 1, 3, 72, 73, 84, 4, 62, 78, 53, 11, 82, 32, 76, 79, 87, 69, 82, 32, - 76, 69, 70, 2, 225, 16, 9, 69, 32, 83, 73, 88, 84, 69, 69, 78, 2, 69, 15, - 84, 32, 67, 85, 82, 76, 89, 32, 66, 82, 65, 67, 75, 69, 84, 2, 11, 32, 2, - 219, 173, 2, 83, 2, 11, 69, 2, 205, 74, 2, 78, 67, 26, 17, 2, 85, 65, 26, - 44, 6, 68, 82, 65, 78, 84, 32, 255, 3, 82, 24, 90, 67, 112, 10, 70, 65, - 67, 69, 32, 87, 73, 84, 72, 32, 114, 77, 76, 2, 83, 84, 67, 84, 14, 68, - 7, 73, 82, 67, 85, 76, 65, 82, 145, 167, 1, 4, 72, 69, 83, 83, 2, 17, 2, - 32, 65, 2, 147, 212, 1, 82, 4, 34, 67, 45, 4, 79, 80, 69, 78, 2, 21, 3, - 76, 79, 83, 2, 17, 2, 69, 68, 2, 177, 231, 1, 3, 32, 69, 89, 2, 25, 4, - 73, 67, 82, 79, 2, 11, 67, 2, 233, 194, 1, 4, 79, 77, 80, 85, 2, 41, 8, - 65, 78, 68, 73, 78, 71, 32, 75, 2, 195, 207, 2, 78, 2, 21, 3, 69, 76, 69, - 2, 11, 86, 2, 187, 90, 73, 2, 181, 167, 1, 3, 84, 69, 82, 4, 153, 102, 8, - 65, 68, 79, 87, 69, 68, 32, 87, 8, 30, 79, 106, 82, 155, 1, 87, 2, 49, - 10, 32, 76, 79, 87, 69, 82, 32, 76, 69, 70, 2, 11, 84, 2, 11, 32, 2, 11, - 70, 2, 195, 112, 73, 4, 25, 4, 73, 65, 78, 71, 4, 40, 4, 85, 76, 65, 82, - 219, 224, 2, 76, 2, 17, 2, 32, 77, 2, 41, 8, 69, 68, 73, 85, 77, 32, 83, - 72, 2, 223, 102, 65, 2, 21, 3, 69, 76, 70, 2, 11, 84, 2, 163, 164, 1, 72, - 2, 17, 2, 69, 86, 2, 17, 2, 69, 78, 2, 145, 1, 2, 32, 69, 10, 64, 5, 72, - 82, 69, 69, 32, 193, 1, 6, 82, 73, 65, 78, 71, 85, 8, 54, 69, 49, 9, 81, - 85, 65, 82, 84, 69, 82, 83, 32, 2, 29, 5, 73, 71, 72, 84, 72, 2, 243, 5, - 83, 6, 30, 76, 22, 82, 203, 5, 66, 2, 41, 2, 69, 70, 2, 21, 3, 73, 71, - 72, 2, 43, 84, 2, 17, 2, 76, 65, 2, 11, 82, 2, 17, 2, 32, 79, 2, 25, 4, - 78, 69, 32, 81, 2, 185, 4, 6, 85, 65, 82, 84, 69, 82, 2, 199, 181, 2, 78, - 128, 1, 154, 1, 65, 184, 6, 2, 66, 76, 150, 1, 68, 50, 70, 82, 72, 150, - 4, 67, 46, 81, 42, 82, 22, 83, 102, 84, 146, 7, 80, 173, 3, 6, 87, 72, - 73, 84, 69, 32, 32, 34, 78, 33, 4, 82, 82, 79, 87, 2, 11, 67, 2, 195, - 172, 2, 79, 31, 11, 32, 28, 134, 1, 65, 192, 1, 14, 76, 69, 70, 84, 87, - 65, 82, 68, 83, 32, 79, 70, 32, 68, 32, 5, 87, 73, 84, 72, 32, 210, 8, - 70, 179, 5, 84, 2, 41, 8, 78, 68, 32, 82, 73, 71, 72, 84, 2, 17, 2, 32, - 79, 2, 25, 4, 78, 69, 32, 69, 2, 21, 3, 73, 71, 72, 2, 17, 2, 84, 72, 2, - 11, 32, 2, 11, 66, 2, 11, 76, 2, 191, 190, 1, 79, 2, 197, 206, 1, 3, 79, - 87, 78, 20, 74, 68, 58, 76, 42, 77, 38, 78, 58, 83, 66, 69, 250, 12, 84, - 139, 58, 72, 2, 21, 3, 79, 85, 66, 2, 11, 76, 2, 247, 188, 2, 69, 2, 11, - 65, 2, 253, 22, 3, 82, 71, 69, 2, 225, 22, 5, 69, 68, 73, 85, 77, 2, 25, - 4, 79, 84, 67, 72, 2, 11, 69, 2, 139, 56, 68, 4, 11, 77, 4, 25, 4, 65, - 76, 76, 32, 4, 22, 69, 223, 21, 84, 2, 129, 22, 10, 81, 85, 73, 76, 65, - 84, 69, 82, 65, 76, 4, 25, 4, 65, 67, 75, 32, 4, 26, 67, 139, 208, 1, 65, - 2, 25, 4, 73, 82, 67, 76, 2, 25, 4, 69, 68, 32, 87, 2, 11, 72, 2, 237, - 14, 2, 73, 84, 4, 22, 79, 195, 13, 65, 2, 177, 14, 2, 85, 66, 2, 11, 73, - 2, 37, 7, 78, 71, 69, 82, 45, 80, 79, 2, 173, 206, 1, 2, 83, 84, 20, 88, - 17, 65, 82, 80, 79, 79, 78, 32, 87, 73, 84, 72, 32, 66, 65, 82, 66, 32, - 131, 3, 69, 16, 56, 4, 76, 69, 70, 84, 245, 1, 5, 82, 73, 71, 72, 84, 10, - 22, 32, 239, 9, 87, 8, 60, 7, 66, 69, 83, 73, 68, 69, 32, 206, 1, 70, - 179, 5, 84, 4, 40, 4, 68, 79, 87, 78, 1, 2, 85, 80, 2, 197, 149, 1, 23, - 87, 65, 82, 68, 83, 32, 72, 65, 82, 80, 79, 79, 78, 32, 87, 73, 84, 72, - 32, 66, 65, 82, 66, 6, 22, 32, 251, 7, 87, 4, 22, 70, 179, 5, 84, 2, 217, - 195, 1, 3, 82, 79, 77, 4, 25, 4, 65, 86, 89, 32, 4, 26, 67, 155, 202, 1, - 65, 2, 189, 10, 7, 79, 77, 80, 82, 69, 83, 83, 2, 137, 9, 6, 85, 65, 68, - 82, 85, 80, 2, 171, 145, 2, 79, 4, 30, 65, 53, 3, 81, 85, 65, 2, 245, - 200, 1, 8, 78, 83, 45, 83, 69, 82, 73, 70, 2, 131, 9, 82, 36, 36, 2, 82, - 73, 229, 7, 2, 87, 79, 30, 40, 5, 65, 78, 71, 76, 69, 155, 7, 80, 28, 52, - 8, 45, 72, 69, 65, 68, 69, 68, 32, 219, 12, 32, 26, 48, 5, 65, 82, 82, - 79, 87, 174, 5, 68, 39, 80, 23, 11, 32, 20, 92, 17, 76, 69, 70, 84, 87, - 65, 82, 68, 83, 32, 79, 70, 32, 68, 79, 87, 78, 98, 84, 23, 87, 2, 37, 7, - 87, 65, 82, 68, 83, 32, 84, 2, 37, 7, 82, 73, 65, 78, 71, 76, 69, 2, 219, - 5, 45, 2, 171, 190, 1, 79, 16, 25, 4, 73, 84, 72, 32, 16, 114, 66, 28, 6, - 76, 79, 78, 71, 32, 84, 130, 1, 77, 34, 78, 34, 86, 34, 72, 149, 56, 6, - 68, 79, 85, 66, 76, 69, 2, 149, 2, 3, 79, 76, 68, 4, 17, 2, 73, 80, 4, - 11, 32, 4, 34, 76, 21, 4, 82, 73, 71, 72, 2, 17, 2, 69, 70, 2, 11, 84, 2, - 11, 87, 2, 243, 125, 65, 2, 121, 5, 69, 68, 73, 85, 77, 2, 89, 5, 65, 82, - 82, 79, 87, 2, 29, 5, 69, 82, 89, 32, 72, 2, 25, 4, 69, 65, 86, 89, 2, - 221, 177, 2, 4, 32, 83, 72, 65, 2, 11, 65, 2, 249, 1, 2, 83, 72, 2, 11, - 65, 2, 25, 4, 73, 82, 69, 68, 2, 29, 5, 32, 65, 82, 82, 79, 2, 211, 157, - 2, 87, 2, 11, 76, 2, 187, 192, 1, 69, 6, 74, 32, 61, 14, 45, 72, 69, 65, - 68, 69, 68, 32, 65, 82, 82, 79, 87, 32, 2, 25, 4, 72, 69, 65, 68, 2, 11, - 69, 2, 179, 191, 1, 68, 4, 42, 87, 253, 134, 1, 4, 70, 82, 79, 77, 2, 33, - 6, 73, 84, 72, 32, 84, 82, 2, 61, 13, 73, 65, 78, 71, 76, 69, 32, 65, 82, - 82, 79, 87, 72, 2, 149, 121, 2, 69, 65, 18, 88, 5, 65, 82, 82, 79, 87, - 149, 3, 12, 68, 79, 85, 66, 76, 69, 32, 65, 82, 82, 79, 87, 15, 11, 32, - 12, 108, 11, 79, 78, 32, 80, 69, 68, 69, 83, 84, 65, 76, 106, 87, 217, - 179, 1, 8, 70, 82, 79, 77, 32, 66, 65, 82, 7, 33, 6, 32, 87, 73, 84, 72, - 32, 4, 26, 86, 171, 180, 1, 72, 2, 205, 180, 1, 5, 69, 82, 84, 73, 67, 2, - 29, 5, 73, 84, 72, 73, 78, 2, 17, 2, 32, 84, 2, 37, 7, 82, 73, 65, 78, - 71, 76, 69, 2, 11, 32, 2, 11, 65, 2, 25, 4, 82, 82, 79, 87, 2, 11, 72, 2, - 195, 142, 2, 69, 5, 45, 9, 32, 79, 78, 32, 80, 69, 68, 69, 83, 2, 235, - 202, 1, 84, 240, 15, 86, 65, 254, 16, 83, 174, 3, 69, 234, 45, 73, 186, - 8, 79, 134, 2, 84, 21, 2, 85, 76, 218, 8, 116, 2, 73, 32, 128, 16, 17, - 82, 73, 65, 84, 73, 79, 78, 32, 83, 69, 76, 69, 67, 84, 79, 82, 45, 245, - 45, 2, 77, 80, 216, 4, 54, 67, 34, 70, 82, 81, 40, 2, 83, 89, 207, 95, - 68, 2, 11, 79, 2, 231, 176, 2, 77, 2, 11, 85, 2, 11, 76, 2, 11, 76, 2, - 11, 32, 2, 11, 83, 2, 147, 216, 1, 84, 2, 17, 2, 85, 69, 2, 215, 231, 1, - 83, 190, 4, 68, 7, 76, 76, 65, 66, 76, 69, 32, 197, 11, 5, 77, 66, 79, - 76, 32, 164, 4, 214, 1, 68, 70, 66, 2, 83, 2, 84, 2, 90, 70, 71, 122, 72, - 82, 75, 134, 1, 76, 130, 1, 77, 86, 78, 134, 3, 67, 2, 70, 2, 74, 2, 80, - 2, 82, 2, 86, 2, 89, 78, 87, 50, 69, 34, 79, 250, 73, 65, 2, 73, 3, 85, - 42, 66, 72, 162, 8, 79, 194, 133, 2, 69, 162, 64, 65, 2, 73, 3, 85, 28, - 230, 7, 72, 58, 79, 194, 133, 2, 69, 162, 64, 65, 2, 73, 3, 85, 34, 62, - 66, 202, 6, 69, 86, 79, 226, 197, 2, 65, 2, 73, 3, 85, 18, 106, 79, 222, - 5, 69, 182, 198, 2, 65, 2, 73, 3, 85, 24, 50, 79, 222, 5, 69, 178, 75, - 65, 2, 73, 3, 85, 7, 142, 204, 2, 78, 3, 79, 34, 70, 80, 206, 5, 79, 222, - 74, 65, 230, 186, 1, 69, 162, 64, 73, 3, 85, 18, 246, 4, 69, 86, 79, 222, - 74, 65, 134, 251, 1, 73, 3, 85, 16, 54, 69, 218, 4, 79, 226, 197, 2, 65, - 2, 73, 3, 85, 7, 26, 78, 159, 202, 2, 69, 2, 21, 3, 71, 84, 72, 2, 139, - 140, 2, 69, 42, 214, 3, 66, 0, 2, 71, 66, 58, 79, 194, 133, 2, 69, 162, - 64, 65, 2, 73, 3, 85, 90, 90, 68, 174, 1, 71, 126, 74, 2, 89, 58, 79, - 194, 133, 2, 69, 162, 64, 65, 2, 73, 3, 85, 24, 54, 79, 234, 135, 2, 69, - 162, 64, 65, 2, 73, 3, 85, 15, 36, 3, 76, 69, 32, 227, 199, 2, 79, 10, - 54, 83, 182, 168, 2, 68, 190, 28, 70, 2, 75, 3, 77, 2, 179, 168, 2, 79, - 25, 42, 71, 138, 247, 1, 65, 2, 69, 3, 79, 16, 50, 69, 86, 79, 226, 197, - 2, 65, 2, 73, 3, 85, 7, 178, 198, 2, 69, 3, 78, 14, 54, 79, 194, 133, 2, - 69, 162, 64, 65, 2, 73, 3, 85, 5, 223, 197, 2, 79, 28, 46, 69, 34, 79, - 250, 73, 65, 2, 73, 3, 85, 9, 150, 74, 69, 135, 251, 1, 78, 9, 246, 73, - 79, 135, 251, 1, 78, 26, 66, 68, 62, 70, 30, 74, 22, 75, 50, 78, 22, 84, - 151, 234, 1, 66, 6, 26, 79, 135, 243, 1, 65, 4, 130, 243, 1, 79, 135, 50, - 45, 4, 74, 69, 219, 192, 2, 65, 2, 199, 242, 1, 79, 4, 26, 69, 155, 242, - 1, 85, 2, 151, 242, 1, 69, 2, 163, 175, 2, 73, 6, 238, 241, 1, 73, 2, 79, - 195, 78, 65, 128, 4, 74, 49, 94, 50, 98, 51, 2, 52, 2, 53, 2, 54, 2, 55, - 2, 56, 3, 57, 223, 1, 182, 1, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, - 54, 2, 55, 2, 56, 3, 57, 137, 1, 90, 48, 2, 49, 2, 50, 2, 51, 2, 52, 94, - 53, 218, 191, 2, 54, 2, 55, 2, 56, 3, 57, 23, 178, 192, 2, 48, 2, 49, 2, - 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 17, 214, 191, 2, 48, - 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 3, 54, 194, 1, 116, 4, 68, 73, 67, 32, - 206, 19, 82, 168, 155, 1, 13, 67, 84, 79, 82, 32, 79, 82, 32, 67, 82, 79, - 83, 83, 219, 106, 83, 86, 60, 5, 83, 73, 71, 78, 32, 153, 10, 5, 84, 79, - 78, 69, 32, 48, 218, 2, 65, 216, 1, 17, 68, 79, 85, 66, 76, 69, 32, 65, - 78, 85, 83, 86, 65, 82, 65, 32, 65, 98, 74, 52, 6, 78, 73, 72, 83, 72, - 86, 22, 82, 240, 1, 8, 72, 69, 88, 73, 70, 79, 82, 77, 22, 76, 52, 4, 84, - 73, 82, 89, 22, 85, 60, 8, 86, 73, 83, 65, 82, 71, 65, 32, 237, 6, 17, - 89, 65, 74, 85, 82, 86, 69, 68, 73, 67, 32, 77, 73, 68, 76, 73, 78, 14, - 76, 8, 78, 85, 83, 86, 65, 82, 65, 32, 212, 1, 3, 84, 73, 75, 151, 2, 82, - 10, 134, 1, 65, 24, 4, 66, 65, 72, 73, 24, 9, 85, 66, 72, 65, 89, 65, 84, - 79, 32, 201, 4, 10, 86, 65, 77, 65, 71, 79, 77, 85, 75, 72, 2, 21, 3, 78, - 84, 65, 2, 21, 3, 82, 71, 79, 2, 11, 77, 2, 163, 9, 85, 2, 235, 245, 1, - 82, 2, 33, 6, 73, 72, 86, 65, 77, 85, 2, 151, 3, 76, 2, 203, 181, 2, 65, - 8, 144, 1, 15, 69, 86, 69, 82, 83, 69, 68, 32, 86, 73, 83, 65, 82, 71, - 65, 36, 9, 79, 84, 65, 84, 69, 68, 32, 65, 82, 57, 5, 84, 72, 65, 78, 71, - 4, 11, 32, 4, 218, 6, 65, 23, 85, 2, 25, 4, 68, 72, 65, 86, 2, 201, 243, - 1, 2, 73, 83, 2, 17, 2, 32, 76, 2, 21, 3, 79, 78, 71, 2, 229, 240, 1, 2, - 32, 65, 2, 171, 178, 2, 65, 2, 37, 7, 80, 65, 68, 72, 77, 65, 78, 2, 171, - 178, 2, 73, 10, 40, 3, 65, 78, 85, 2, 85, 179, 9, 83, 4, 25, 4, 68, 65, - 84, 84, 4, 11, 65, 5, 25, 4, 32, 87, 73, 84, 2, 11, 72, 2, 11, 32, 2, 11, - 84, 2, 11, 65, 2, 159, 170, 1, 73, 38, 128, 2, 5, 67, 65, 78, 68, 82, 16, - 2, 68, 79, 96, 2, 75, 65, 136, 1, 4, 80, 82, 69, 78, 16, 2, 82, 73, 106, - 84, 164, 1, 11, 89, 65, 74, 85, 82, 86, 69, 68, 73, 67, 32, 180, 1, 12, - 65, 84, 72, 65, 82, 86, 65, 86, 69, 68, 73, 67, 169, 231, 1, 2, 83, 72, - 4, 131, 19, 65, 6, 40, 5, 85, 66, 76, 69, 32, 191, 3, 84, 4, 22, 82, 211, - 5, 83, 2, 11, 73, 2, 251, 1, 78, 4, 56, 3, 82, 83, 72, 17, 7, 84, 72, 65, - 75, 65, 32, 65, 2, 139, 116, 65, 2, 17, 2, 78, 85, 2, 17, 2, 68, 65, 2, - 139, 139, 2, 84, 2, 131, 53, 75, 4, 82, 78, 237, 2, 15, 71, 86, 69, 68, - 73, 67, 32, 75, 65, 83, 72, 77, 73, 82, 73, 2, 219, 177, 1, 71, 6, 42, - 72, 24, 4, 82, 73, 80, 76, 19, 87, 2, 49, 3, 82, 69, 69, 2, 219, 2, 69, - 2, 11, 79, 2, 25, 4, 32, 68, 79, 84, 2, 11, 83, 2, 11, 32, 2, 167, 15, - 66, 8, 176, 1, 10, 65, 71, 71, 82, 65, 86, 65, 84, 69, 68, 22, 73, 105, - 27, 75, 65, 84, 72, 65, 75, 65, 32, 73, 78, 68, 69, 80, 69, 78, 68, 69, - 78, 84, 32, 83, 86, 65, 82, 73, 84, 65, 2, 17, 2, 32, 73, 2, 49, 10, 78, - 68, 69, 80, 69, 78, 68, 69, 78, 84, 2, 17, 2, 32, 83, 2, 189, 134, 2, 3, - 86, 65, 82, 5, 241, 10, 7, 32, 83, 67, 72, 82, 79, 69, 104, 62, 83, 16, - 6, 84, 73, 67, 65, 76, 32, 181, 17, 2, 89, 32, 2, 167, 88, 73, 76, 200, - 2, 4, 66, 65, 82, 32, 174, 3, 67, 42, 69, 62, 70, 38, 71, 32, 11, 73, 68, - 69, 79, 71, 82, 65, 80, 72, 73, 67, 48, 12, 75, 65, 78, 65, 32, 82, 69, - 80, 69, 65, 84, 32, 254, 1, 76, 194, 3, 77, 88, 17, 79, 78, 69, 32, 69, - 73, 71, 72, 84, 72, 32, 66, 76, 79, 67, 75, 45, 62, 82, 158, 1, 84, 218, - 1, 90, 173, 72, 3, 83, 73, 88, 8, 108, 6, 66, 69, 83, 73, 68, 69, 64, 8, - 68, 79, 85, 66, 76, 69, 32, 76, 20, 4, 84, 82, 73, 80, 143, 1, 87, 2, 17, - 2, 32, 82, 2, 21, 3, 73, 71, 72, 2, 159, 129, 1, 84, 2, 69, 2, 69, 70, 2, - 25, 4, 76, 69, 32, 82, 2, 21, 3, 73, 71, 72, 2, 11, 84, 2, 29, 5, 32, 84, - 85, 82, 78, 2, 11, 83, 2, 11, 84, 2, 203, 128, 1, 73, 2, 21, 3, 73, 84, - 72, 2, 17, 2, 32, 72, 2, 137, 244, 1, 7, 79, 82, 73, 90, 79, 78, 84, 2, - 141, 235, 1, 5, 65, 80, 65, 67, 73, 2, 25, 4, 76, 76, 73, 80, 2, 11, 83, - 2, 215, 229, 1, 73, 2, 11, 79, 2, 213, 82, 2, 85, 82, 2, 221, 155, 1, 3, - 79, 45, 75, 2, 17, 2, 32, 73, 2, 133, 185, 1, 2, 84, 69, 10, 120, 4, 77, - 65, 82, 75, 45, 22, 87, 73, 84, 72, 32, 86, 79, 73, 67, 69, 68, 32, 83, - 79, 85, 78, 68, 32, 77, 65, 82, 75, 7, 11, 32, 4, 50, 85, 21, 3, 76, 79, - 87, 5, 17, 2, 32, 85, 2, 17, 2, 80, 80, 2, 17, 2, 69, 82, 2, 153, 29, 2, - 32, 72, 16, 30, 65, 33, 3, 73, 78, 69, 2, 11, 68, 2, 151, 226, 1, 68, 15, - 11, 32, 12, 38, 69, 49, 5, 87, 73, 84, 72, 32, 2, 25, 4, 88, 84, 69, 78, - 2, 219, 206, 1, 83, 10, 56, 4, 67, 73, 82, 67, 98, 70, 26, 84, 151, 140, - 1, 77, 4, 11, 76, 4, 11, 69, 4, 11, 32, 4, 26, 66, 167, 161, 1, 65, 2, - 11, 69, 2, 223, 131, 1, 76, 2, 57, 3, 79, 85, 82, 2, 11, 72, 2, 21, 3, - 82, 69, 69, 2, 45, 9, 32, 84, 73, 67, 75, 32, 77, 65, 82, 2, 167, 223, 1, - 75, 2, 65, 14, 65, 76, 69, 32, 87, 73, 84, 72, 32, 83, 84, 82, 79, 75, 2, - 171, 149, 1, 69, 12, 150, 156, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 3, 55, - 4, 42, 65, 57, 6, 69, 83, 73, 83, 84, 79, 2, 25, 4, 67, 73, 78, 71, 2, - 11, 32, 2, 207, 121, 67, 2, 21, 3, 82, 32, 83, 2, 11, 69, 2, 159, 215, 1, - 71, 10, 32, 2, 65, 66, 106, 73, 19, 82, 6, 18, 32, 35, 85, 2, 11, 75, 2, - 179, 136, 2, 69, 4, 33, 6, 76, 65, 84, 73, 79, 78, 5, 251, 45, 32, 2, - 167, 9, 76, 2, 29, 5, 65, 70, 70, 73, 67, 2, 177, 238, 1, 2, 32, 76, 2, - 233, 106, 4, 73, 71, 90, 65, 26, 80, 6, 72, 69, 65, 86, 89, 32, 176, 5, - 3, 77, 85, 67, 241, 9, 3, 66, 79, 76, 20, 62, 69, 190, 1, 70, 38, 82, 82, - 83, 246, 1, 87, 215, 10, 71, 4, 25, 4, 73, 71, 72, 84, 4, 11, 32, 4, 22, - 80, 223, 2, 83, 2, 25, 4, 79, 73, 78, 84, 2, 17, 2, 69, 68, 2, 17, 2, 32, - 66, 2, 25, 4, 76, 65, 67, 75, 2, 11, 32, 2, 215, 79, 83, 2, 11, 73, 2, - 185, 1, 2, 86, 69, 2, 11, 69, 2, 29, 5, 86, 69, 82, 83, 69, 2, 17, 2, 32, - 83, 2, 231, 1, 79, 6, 30, 65, 42, 73, 147, 1, 79, 2, 11, 76, 2, 11, 84, - 2, 147, 116, 73, 2, 11, 88, 2, 11, 32, 2, 11, 83, 2, 21, 3, 80, 79, 75, - 2, 17, 2, 69, 68, 2, 11, 32, 2, 11, 65, 2, 11, 83, 2, 205, 78, 3, 84, 69, - 82, 2, 173, 13, 3, 76, 73, 68, 4, 21, 3, 72, 73, 84, 4, 11, 69, 4, 11, - 32, 4, 226, 64, 67, 163, 49, 83, 4, 11, 72, 4, 11, 32, 4, 18, 71, 39, 76, - 2, 69, 6, 82, 69, 65, 84, 69, 82, 2, 11, 69, 2, 11, 83, 2, 11, 83, 2, 17, - 2, 45, 84, 2, 187, 71, 72, 160, 1, 140, 1, 9, 66, 82, 65, 84, 73, 79, 78, - 32, 77, 34, 67, 36, 3, 68, 69, 79, 126, 69, 222, 1, 79, 22, 82, 21, 7, - 84, 72, 75, 85, 81, 73, 32, 2, 11, 79, 2, 183, 249, 1, 68, 2, 221, 129, - 1, 4, 84, 79, 82, 89, 6, 30, 32, 77, 3, 67, 65, 83, 4, 18, 67, 43, 71, 2, - 17, 2, 65, 77, 2, 203, 202, 1, 69, 2, 139, 119, 65, 2, 219, 71, 83, 6, + 67, 2, 80, 2, 84, 34, 78, 234, 222, 5, 66, 2, 68, 2, 70, 2, 71, 2, 72, 2, + 76, 2, 77, 2, 82, 2, 83, 2, 86, 2, 89, 247, 30, 79, 6, 26, 72, 255, 253, + 5, 79, 4, 134, 223, 5, 72, 247, 30, 79, 6, 230, 222, 5, 71, 2, 89, 247, + 30, 79, 4, 186, 172, 5, 65, 195, 52, 72, 10, 72, 2, 75, 79, 110, 78, 216, + 132, 1, 4, 72, 79, 32, 72, 247, 185, 3, 83, 4, 192, 133, 1, 3, 73, 32, + 75, 135, 247, 4, 78, 8, 58, 78, 230, 138, 1, 84, 234, 169, 2, 83, 251, + 195, 2, 69, 2, 251, 180, 3, 85, 26, 62, 65, 206, 4, 85, 206, 228, 1, 73, + 242, 145, 4, 69, 3, 79, 10, 178, 228, 5, 85, 214, 22, 65, 2, 77, 2, 78, + 3, 89, 110, 72, 7, 76, 69, 84, 84, 69, 82, 32, 238, 3, 83, 157, 1, 3, 88, + 65, 77, 96, 206, 1, 65, 80, 2, 76, 79, 12, 4, 72, 73, 71, 72, 126, 78, + 34, 85, 198, 133, 1, 80, 2, 84, 138, 95, 73, 194, 200, 1, 79, 190, 170, + 2, 66, 2, 67, 2, 68, 2, 71, 2, 77, 2, 81, 2, 86, 2, 89, 247, 30, 69, 16, + 222, 225, 5, 85, 214, 22, 65, 2, 69, 2, 75, 2, 77, 2, 78, 2, 80, 3, 84, + 21, 11, 87, 18, 11, 32, 18, 70, 75, 194, 158, 5, 78, 210, 57, 70, 2, 72, + 2, 80, 2, 84, 3, 88, 6, 142, 216, 5, 72, 2, 86, 247, 30, 79, 4, 230, 215, + 5, 71, 247, 30, 79, 9, 202, 228, 1, 69, 243, 145, 4, 65, 12, 76, 4, 73, + 71, 78, 32, 245, 156, 5, 9, 89, 77, 66, 79, 76, 32, 77, 85, 69, 10, 38, + 65, 206, 222, 5, 85, 255, 5, 79, 6, 202, 244, 5, 78, 86, 85, 3, 89, 2, + 171, 129, 1, 32, 138, 1, 52, 3, 82, 73, 32, 201, 229, 4, 4, 69, 79, 85, + 84, 136, 1, 82, 65, 20, 7, 76, 69, 84, 84, 69, 82, 32, 166, 2, 83, 78, + 86, 203, 252, 3, 68, 2, 155, 211, 1, 66, 88, 210, 1, 65, 158, 148, 2, 68, + 82, 82, 34, 84, 230, 5, 85, 186, 202, 1, 73, 138, 196, 1, 78, 126, 66, 2, + 67, 2, 71, 2, 74, 2, 75, 2, 80, 2, 83, 138, 69, 72, 2, 76, 2, 77, 2, 86, + 2, 89, 186, 2, 69, 3, 79, 11, 208, 190, 3, 7, 82, 67, 72, 65, 73, 67, 32, + 154, 179, 2, 65, 2, 73, 3, 85, 8, 25, 4, 73, 71, 78, 32, 8, 254, 211, 1, + 78, 138, 216, 3, 65, 239, 1, 86, 18, 49, 10, 79, 87, 69, 76, 32, 83, 73, + 71, 78, 32, 18, 250, 152, 2, 65, 38, 85, 186, 202, 1, 73, 198, 140, 2, + 69, 3, 79, 4, 134, 51, 70, 163, 161, 4, 79, 188, 6, 36, 3, 73, 76, 32, + 211, 200, 4, 65, 186, 6, 154, 3, 65, 106, 67, 194, 2, 68, 72, 3, 87, 69, + 84, 48, 9, 70, 82, 65, 67, 84, 73, 79, 78, 32, 232, 7, 9, 73, 78, 32, 80, + 79, 83, 83, 69, 83, 22, 76, 200, 2, 7, 78, 85, 77, 66, 69, 82, 32, 152, + 1, 18, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, 32, 69, 78, 68, 32, + 79, 70, 54, 82, 42, 83, 166, 14, 84, 228, 1, 7, 86, 79, 87, 69, 76, 32, + 83, 100, 2, 89, 69, 168, 195, 4, 5, 77, 79, 78, 84, 72, 171, 118, 79, 6, + 80, 5, 83, 32, 65, 66, 79, 150, 138, 2, 85, 197, 245, 1, 5, 78, 68, 32, + 79, 68, 2, 143, 203, 3, 86, 52, 80, 9, 79, 78, 83, 79, 78, 65, 78, 84, + 32, 156, 21, 3, 85, 82, 82, 171, 9, 82, 48, 130, 1, 75, 22, 76, 22, 78, + 46, 84, 194, 191, 1, 83, 254, 82, 82, 158, 214, 3, 67, 2, 72, 2, 74, 2, + 77, 2, 80, 2, 86, 3, 89, 5, 207, 171, 5, 83, 7, 235, 236, 4, 76, 11, 150, + 235, 3, 78, 238, 253, 1, 71, 3, 89, 5, 215, 232, 5, 84, 26, 68, 2, 82, + 89, 164, 28, 2, 69, 66, 146, 149, 2, 65, 207, 194, 1, 73, 2, 225, 199, 1, + 7, 32, 67, 85, 76, 84, 73, 86, 42, 168, 1, 4, 79, 78, 69, 32, 220, 4, 6, + 84, 72, 82, 69, 69, 32, 169, 224, 5, 22, 68, 79, 87, 78, 83, 67, 65, 76, + 73, 78, 71, 32, 70, 65, 67, 84, 79, 82, 32, 75, 73, 73, 30, 112, 5, 69, + 73, 71, 72, 84, 34, 70, 40, 3, 72, 65, 76, 22, 79, 88, 4, 83, 73, 88, 84, + 74, 84, 203, 221, 3, 81, 4, 210, 3, 73, 219, 225, 5, 72, 4, 156, 3, 2, + 79, 82, 203, 219, 3, 73, 4, 251, 222, 1, 70, 2, 225, 2, 18, 78, 69, 45, + 72, 85, 78, 68, 82, 69, 68, 45, 65, 78, 68, 45, 83, 73, 88, 6, 140, 222, + 1, 5, 69, 69, 78, 84, 72, 141, 25, 5, 89, 45, 70, 79, 85, 8, 38, 72, 138, + 1, 87, 255, 220, 3, 69, 4, 132, 1, 18, 82, 69, 69, 45, 72, 85, 78, 68, + 82, 69, 68, 45, 65, 78, 68, 45, 84, 87, 173, 163, 4, 8, 73, 82, 84, 89, + 45, 83, 69, 67, 2, 17, 2, 69, 78, 2, 17, 2, 84, 73, 2, 223, 220, 3, 69, + 10, 58, 69, 28, 4, 83, 73, 88, 84, 82, 84, 179, 220, 3, 81, 2, 129, 1, 3, + 73, 71, 72, 4, 50, 69, 165, 221, 3, 6, 89, 45, 70, 79, 85, 82, 2, 161, + 221, 3, 2, 69, 78, 2, 21, 3, 87, 69, 78, 2, 237, 220, 3, 3, 84, 73, 69, + 2, 207, 218, 1, 83, 72, 48, 6, 69, 84, 84, 69, 82, 32, 211, 243, 3, 65, + 70, 194, 1, 78, 170, 201, 1, 84, 190, 54, 65, 82, 76, 38, 82, 134, 6, 85, + 206, 141, 1, 79, 238, 60, 73, 182, 196, 1, 83, 242, 7, 69, 234, 61, 67, + 2, 72, 2, 74, 2, 75, 2, 77, 2, 80, 2, 86, 3, 89, 10, 46, 78, 242, 218, 5, + 71, 2, 89, 187, 2, 65, 4, 238, 218, 5, 78, 187, 2, 65, 8, 38, 79, 222, + 134, 4, 84, 159, 79, 83, 4, 11, 78, 4, 17, 2, 69, 32, 4, 18, 72, 31, 84, + 2, 133, 72, 3, 85, 78, 68, 2, 185, 137, 2, 3, 72, 79, 85, 2, 17, 2, 32, + 84, 2, 11, 69, 2, 163, 191, 5, 88, 2, 17, 2, 85, 80, 2, 159, 187, 3, 69, + 194, 4, 152, 1, 5, 65, 76, 84, 32, 80, 20, 4, 73, 71, 78, 32, 206, 4, 80, + 28, 9, 84, 65, 82, 84, 73, 78, 71, 32, 70, 41, 8, 89, 76, 76, 65, 66, 76, + 69, 32, 2, 235, 221, 3, 65, 40, 90, 65, 48, 3, 67, 69, 86, 34, 75, 80, 2, + 77, 85, 102, 85, 14, 80, 118, 86, 211, 64, 78, 4, 216, 2, 4, 65, 90, 72, + 65, 191, 145, 5, 78, 2, 11, 73, 2, 223, 179, 5, 84, 6, 38, 85, 165, 179, + 5, 3, 65, 65, 67, 4, 18, 90, 87, 82, 2, 159, 196, 5, 72, 6, 60, 4, 75, + 75, 85, 82, 16, 2, 84, 72, 21, 3, 85, 86, 85, 2, 203, 82, 85, 2, 175, + 155, 3, 65, 2, 75, 90, 8, 26, 65, 251, 134, 5, 79, 6, 34, 84, 154, 153, + 4, 65, 15, 78, 2, 11, 72, 2, 17, 2, 65, 75, 2, 171, 177, 5, 75, 10, 38, + 65, 250, 81, 69, 227, 192, 4, 73, 4, 216, 102, 3, 82, 65, 65, 201, 249, + 2, 6, 75, 65, 73, 89, 65, 82, 2, 11, 69, 2, 187, 9, 78, 2, 17, 2, 82, 79, + 2, 227, 205, 4, 77, 148, 4, 130, 1, 75, 166, 1, 76, 166, 1, 78, 186, 1, + 82, 86, 83, 178, 1, 84, 214, 2, 67, 2, 72, 2, 74, 2, 77, 2, 80, 2, 86, 3, + 89, 46, 84, 2, 83, 83, 210, 251, 1, 65, 38, 85, 206, 141, 1, 79, 238, 60, + 73, 167, 204, 1, 69, 24, 250, 244, 1, 65, 250, 6, 85, 206, 141, 1, 79, + 238, 60, 73, 167, 204, 1, 69, 66, 78, 76, 182, 250, 1, 65, 38, 85, 206, + 141, 1, 79, 238, 60, 73, 167, 204, 1, 69, 44, 226, 6, 76, 210, 243, 1, + 65, 38, 85, 206, 141, 1, 79, 238, 60, 73, 167, 204, 1, 69, 110, 98, 78, + 174, 5, 71, 2, 89, 210, 243, 1, 65, 38, 85, 206, 141, 1, 79, 238, 60, 73, + 167, 204, 1, 69, 44, 170, 5, 78, 210, 243, 1, 65, 38, 85, 206, 141, 1, + 79, 238, 60, 73, 167, 204, 1, 69, 44, 214, 4, 82, 210, 243, 1, 65, 38, + 85, 206, 141, 1, 79, 238, 60, 73, 167, 204, 1, 69, 68, 90, 72, 170, 3, + 83, 210, 243, 1, 65, 38, 85, 206, 141, 1, 79, 238, 60, 73, 167, 204, 1, + 69, 24, 246, 246, 1, 65, 38, 85, 206, 141, 1, 79, 230, 2, 82, 138, 58, + 73, 167, 204, 1, 69, 44, 210, 2, 84, 210, 243, 1, 65, 38, 85, 206, 141, + 1, 79, 238, 60, 73, 167, 204, 1, 69, 6, 68, 2, 79, 84, 33, 11, 82, 65, + 68, 73, 84, 73, 79, 78, 65, 76, 32, 2, 11, 65, 2, 155, 197, 4, 76, 4, 24, + 2, 67, 82, 55, 78, 2, 17, 2, 69, 68, 2, 11, 73, 2, 203, 196, 4, 84, 2, + 11, 85, 2, 17, 2, 77, 66, 2, 123, 69, 22, 25, 4, 73, 71, 78, 32, 22, 206, + 243, 1, 65, 38, 85, 206, 141, 1, 79, 238, 60, 73, 167, 204, 1, 69, 2, 11, + 65, 2, 155, 195, 4, 82, 158, 15, 36, 5, 65, 66, 65, 84, 65, 35, 71, 2, + 11, 32, 2, 163, 215, 3, 84, 156, 15, 54, 69, 20, 3, 83, 65, 32, 213, 6, + 3, 85, 84, 32, 2, 195, 173, 4, 82, 178, 1, 52, 7, 76, 69, 84, 84, 69, 82, + 32, 171, 212, 3, 68, 158, 1, 210, 1, 65, 58, 70, 54, 72, 38, 76, 58, 77, + 54, 78, 50, 83, 122, 85, 114, 69, 2, 73, 2, 79, 2, 86, 182, 250, 4, 84, + 82, 67, 2, 68, 2, 71, 2, 75, 2, 80, 138, 69, 66, 2, 82, 2, 87, 2, 88, 2, + 89, 3, 90, 16, 146, 4, 87, 198, 194, 5, 67, 2, 81, 2, 88, 3, 90, 4, 164, + 245, 4, 5, 73, 78, 65, 76, 32, 251, 80, 65, 6, 186, 177, 1, 84, 179, 148, + 4, 65, 4, 176, 157, 4, 5, 79, 78, 71, 32, 85, 151, 168, 1, 65, 10, 142, + 197, 5, 65, 2, 67, 2, 81, 2, 88, 3, 90, 8, 162, 194, 5, 71, 2, 72, 2, 89, + 187, 2, 65, 8, 26, 72, 147, 196, 5, 65, 6, 40, 4, 79, 82, 84, 32, 231, + 195, 5, 65, 4, 184, 229, 3, 2, 85, 69, 233, 79, 2, 65, 87, 32, 58, 73, + 54, 69, 198, 194, 5, 67, 2, 81, 2, 88, 3, 90, 16, 50, 85, 198, 194, 5, + 67, 2, 81, 2, 88, 3, 90, 8, 194, 194, 5, 67, 2, 81, 2, 88, 3, 90, 232, + 13, 64, 10, 67, 79, 77, 80, 79, 78, 69, 78, 84, 45, 183, 155, 3, 73, 230, + 13, 78, 48, 178, 1, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 95, 56, + 198, 1, 86, 48, 130, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, + 56, 3, 57, 18, 158, 192, 5, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, + 2, 56, 3, 57, 200, 1, 166, 1, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, + 54, 2, 55, 2, 56, 3, 57, 168, 1, 74, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, + 53, 2, 54, 2, 55, 95, 56, 20, 158, 190, 5, 48, 2, 49, 2, 50, 2, 51, 2, + 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 8, 194, 189, 5, 48, 2, 49, 2, 50, + 3, 51, 4, 48, 6, 67, 65, 82, 84, 82, 73, 21, 2, 68, 82, 2, 175, 133, 4, + 68, 2, 251, 191, 4, 73, 2, 215, 179, 3, 82, 144, 3, 154, 1, 65, 152, 2, + 5, 68, 68, 89, 32, 66, 22, 76, 230, 14, 78, 144, 1, 5, 83, 84, 32, 84, + 85, 21, 12, 84, 82, 65, 71, 82, 65, 77, 32, 70, 79, 82, 32, 10, 68, 9, + 67, 85, 80, 32, 87, 73, 84, 72, 79, 58, 82, 211, 233, 4, 80, 2, 33, 6, + 85, 84, 32, 72, 65, 78, 2, 223, 147, 4, 68, 6, 72, 9, 45, 79, 70, 70, 32, + 67, 65, 76, 69, 29, 5, 68, 82, 79, 80, 45, 2, 253, 149, 4, 2, 78, 68, 4, + 170, 161, 3, 83, 205, 117, 4, 66, 65, 82, 66, 2, 179, 149, 4, 69, 218, 1, + 38, 69, 213, 2, 4, 85, 71, 85, 32, 16, 60, 6, 80, 72, 79, 78, 69, 32, + 246, 1, 83, 211, 184, 2, 86, 12, 132, 1, 3, 82, 69, 67, 208, 150, 1, 3, + 76, 79, 67, 178, 153, 3, 83, 189, 116, 13, 79, 78, 32, 84, 79, 80, 32, + 79, 70, 32, 77, 79, 68, 6, 36, 5, 69, 73, 86, 69, 82, 51, 79, 5, 29, 5, + 32, 87, 73, 84, 72, 2, 243, 38, 32, 2, 183, 146, 3, 82, 2, 11, 67, 2, + 151, 236, 3, 79, 202, 1, 162, 1, 65, 84, 15, 70, 82, 65, 67, 84, 73, 79, + 78, 32, 68, 73, 71, 73, 84, 32, 164, 2, 2, 76, 69, 252, 3, 5, 83, 73, 71, + 78, 32, 198, 2, 86, 247, 182, 3, 68, 4, 60, 9, 82, 67, 72, 65, 73, 67, + 32, 83, 72, 243, 210, 1, 73, 2, 167, 237, 2, 82, 14, 74, 84, 40, 2, 79, + 78, 81, 10, 90, 69, 82, 79, 32, 70, 79, 82, 32, 79, 8, 36, 3, 72, 82, 69, + 13, 2, 87, 79, 4, 11, 69, 4, 29, 5, 32, 70, 79, 82, 32, 4, 34, 79, 21, 4, + 69, 86, 69, 78, 2, 17, 2, 68, 68, 2, 49, 10, 32, 80, 79, 87, 69, 82, 83, + 32, 79, 70, 2, 133, 22, 2, 32, 70, 114, 44, 5, 84, 84, 69, 82, 32, 167, + 239, 4, 78, 112, 214, 1, 68, 54, 78, 106, 82, 38, 84, 130, 208, 1, 65, + 82, 76, 114, 86, 186, 5, 85, 206, 141, 1, 79, 238, 60, 73, 182, 196, 1, + 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 162, 7, 69, 234, 61, 72, + 2, 77, 3, 89, 10, 242, 231, 4, 68, 138, 69, 72, 2, 90, 187, 2, 65, 10, + 42, 65, 158, 172, 5, 71, 2, 78, 3, 89, 5, 41, 8, 75, 65, 65, 82, 65, 32, + 80, 79, 2, 251, 36, 76, 6, 150, 209, 1, 82, 131, 221, 3, 65, 10, 178, + 230, 4, 84, 138, 69, 72, 2, 83, 187, 2, 65, 20, 86, 67, 194, 1, 83, 204, + 35, 3, 84, 85, 85, 222, 106, 78, 190, 66, 65, 187, 151, 3, 86, 6, 60, 9, + 79, 77, 66, 73, 78, 73, 78, 71, 32, 215, 142, 1, 65, 4, 70, 65, 221, 174, + 4, 11, 67, 65, 78, 68, 82, 65, 66, 73, 78, 68, 85, 2, 33, 6, 78, 85, 83, + 86, 65, 82, 2, 183, 174, 4, 65, 2, 11, 73, 2, 137, 238, 3, 3, 68, 68, 72, + 30, 49, 10, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 30, 166, 211, 1, 65, + 38, 85, 22, 86, 186, 141, 1, 79, 238, 60, 73, 167, 204, 1, 69, 6, 80, 10, + 78, 73, 83, 32, 82, 65, 67, 81, 85, 69, 158, 137, 3, 71, 139, 160, 2, 84, + 2, 11, 84, 2, 25, 4, 32, 65, 78, 68, 2, 187, 158, 3, 32, 2, 147, 146, 5, + 66, 162, 1, 210, 2, 65, 134, 1, 66, 174, 1, 67, 222, 2, 68, 202, 3, 69, + 154, 2, 70, 182, 1, 71, 198, 1, 72, 64, 8, 89, 79, 85, 84, 72, 70, 85, + 76, 52, 2, 73, 78, 46, 75, 98, 76, 146, 1, 77, 134, 1, 79, 62, 80, 142, + 1, 82, 210, 1, 83, 224, 1, 14, 86, 65, 83, 84, 78, 69, 83, 83, 32, 79, + 82, 32, 87, 65, 12, 2, 87, 65, 138, 94, 85, 167, 159, 4, 74, 8, 88, 4, + 67, 67, 85, 77, 22, 83, 228, 20, 2, 68, 86, 221, 202, 1, 5, 71, 71, 82, + 65, 86, 2, 195, 190, 1, 85, 2, 227, 225, 4, 67, 6, 92, 7, 79, 76, 68, 32, + 82, 69, 83, 32, 5, 82, 65, 78, 67, 72, 229, 152, 4, 3, 65, 82, 82, 2, + 197, 211, 4, 3, 79, 76, 85, 2, 11, 73, 2, 205, 170, 1, 3, 78, 71, 32, 22, + 54, 72, 20, 3, 76, 79, 83, 66, 79, 167, 157, 2, 69, 2, 211, 235, 3, 65, + 6, 26, 69, 171, 128, 4, 85, 4, 222, 253, 1, 68, 167, 140, 2, 78, 12, 28, + 3, 77, 80, 76, 35, 78, 4, 246, 17, 73, 219, 191, 4, 69, 8, 32, 4, 83, 84, + 65, 78, 23, 84, 2, 147, 144, 5, 67, 6, 46, 69, 20, 3, 82, 65, 82, 251, + 143, 4, 65, 2, 199, 208, 4, 78, 2, 155, 176, 3, 73, 20, 80, 4, 65, 82, + 75, 69, 22, 69, 210, 1, 73, 74, 85, 197, 129, 5, 3, 79, 85, 66, 2, 151, + 207, 4, 78, 6, 132, 1, 19, 70, 69, 67, 84, 73, 86, 69, 78, 69, 83, 83, + 32, 79, 82, 32, 68, 73, 83, 84, 34, 80, 137, 7, 6, 67, 73, 83, 73, 86, + 69, 2, 11, 79, 2, 163, 206, 4, 82, 2, 11, 65, 2, 11, 82, 2, 143, 10, 84, + 8, 68, 6, 70, 70, 73, 67, 85, 76, 34, 77, 161, 12, 4, 86, 69, 82, 71, 2, + 11, 84, 2, 223, 135, 4, 73, 4, 140, 1, 2, 73, 78, 191, 203, 4, 77, 14, + 100, 5, 77, 66, 69, 76, 76, 34, 78, 124, 4, 88, 72, 65, 85, 172, 106, 3, + 84, 69, 82, 215, 165, 3, 65, 2, 137, 217, 4, 3, 73, 83, 72, 6, 80, 4, 68, + 69, 65, 86, 20, 4, 76, 65, 82, 71, 229, 151, 3, 4, 67, 79, 85, 78, 2, + 247, 162, 4, 79, 2, 131, 216, 4, 69, 2, 211, 202, 4, 83, 12, 70, 79, 76, + 3, 85, 76, 76, 196, 5, 3, 65, 73, 76, 235, 233, 4, 76, 4, 18, 76, 35, 83, + 2, 173, 201, 4, 3, 76, 79, 87, 2, 205, 4, 2, 84, 69, 4, 214, 197, 3, 32, + 199, 59, 78, 10, 96, 8, 65, 84, 72, 69, 82, 73, 78, 71, 22, 79, 64, 3, + 82, 69, 65, 65, 5, 85, 65, 82, 68, 69, 5, 163, 153, 4, 32, 2, 41, 8, 73, + 78, 71, 32, 84, 79, 32, 77, 2, 159, 227, 4, 69, 2, 75, 84, 4, 48, 2, 65, + 82, 33, 6, 79, 76, 68, 73, 78, 71, 2, 11, 68, 2, 247, 254, 3, 78, 2, 11, + 32, 2, 227, 248, 1, 66, 4, 26, 67, 159, 217, 4, 78, 2, 143, 5, 82, 4, 60, + 7, 69, 69, 80, 73, 78, 71, 32, 169, 193, 4, 2, 73, 78, 2, 11, 83, 2, 247, + 139, 3, 77, 6, 18, 65, 107, 69, 4, 60, 3, 66, 79, 85, 21, 8, 87, 32, 79, + 82, 32, 77, 79, 68, 2, 191, 196, 4, 82, 2, 223, 137, 4, 69, 2, 215, 196, + 4, 71, 6, 26, 65, 30, 69, 47, 73, 2, 229, 195, 4, 2, 83, 83, 2, 11, 65, + 2, 11, 83, 2, 251, 241, 3, 85, 2, 11, 82, 2, 247, 205, 4, 69, 4, 220, + 209, 3, 7, 78, 32, 84, 72, 69, 32, 86, 199, 112, 80, 8, 54, 65, 64, 4, + 69, 78, 69, 84, 229, 97, 2, 85, 82, 4, 22, 84, 243, 2, 67, 2, 17, 2, 84, + 69, 2, 131, 195, 4, 82, 2, 131, 205, 1, 82, 12, 30, 69, 157, 1, 2, 73, + 84, 10, 34, 76, 22, 83, 191, 239, 4, 65, 2, 219, 232, 1, 69, 6, 18, 73, + 51, 80, 4, 30, 68, 137, 1, 2, 83, 84, 2, 147, 1, 69, 2, 11, 79, 2, 239, + 133, 4, 78, 2, 163, 133, 4, 85, 10, 34, 69, 64, 2, 73, 78, 23, 84, 2, 11, + 86, 2, 17, 2, 69, 82, 2, 11, 65, 2, 159, 211, 4, 78, 2, 131, 191, 4, 75, + 6, 42, 79, 209, 138, 3, 4, 82, 69, 78, 71, 4, 26, 80, 219, 248, 4, 86, 2, + 11, 80, 2, 235, 215, 3, 65, 2, 39, 83, 4, 26, 73, 191, 236, 4, 84, 2, + 223, 189, 4, 84, 226, 2, 78, 65, 182, 31, 69, 146, 2, 73, 206, 1, 79, + 100, 3, 82, 69, 69, 187, 8, 85, 146, 2, 44, 4, 65, 78, 65, 32, 145, 10, + 2, 73, 32, 100, 122, 65, 38, 69, 76, 7, 76, 69, 84, 84, 69, 82, 32, 154, + 7, 79, 76, 3, 73, 66, 73, 0, 3, 85, 66, 85, 45, 2, 83, 85, 4, 184, 8, 3, + 65, 66, 65, 3, 66, 6, 58, 66, 0, 3, 69, 66, 69, 245, 7, 4, 89, 66, 69, + 89, 2, 243, 7, 69, 78, 202, 1, 65, 42, 68, 82, 70, 2, 81, 14, 71, 50, 72, + 38, 75, 62, 76, 32, 3, 77, 69, 69, 20, 2, 67, 72, 2, 74, 2, 80, 18, 78, + 42, 83, 94, 84, 102, 86, 2, 87, 42, 90, 246, 143, 3, 66, 2, 82, 3, 89, 4, + 252, 1, 2, 76, 73, 159, 227, 4, 73, 6, 30, 65, 29, 3, 72, 65, 65, 4, 218, + 2, 65, 255, 1, 86, 2, 195, 228, 4, 76, 2, 123, 65, 6, 110, 65, 86, 78, + 213, 226, 4, 3, 72, 65, 73, 4, 166, 147, 3, 72, 255, 242, 1, 65, 4, 26, + 65, 235, 146, 3, 72, 2, 11, 65, 2, 167, 227, 4, 70, 4, 18, 65, 35, 72, 2, + 11, 65, 2, 243, 226, 4, 77, 2, 211, 1, 65, 4, 192, 226, 4, 2, 79, 79, + 191, 34, 65, 8, 32, 2, 65, 65, 18, 72, 23, 69, 2, 163, 16, 68, 4, 18, 69, + 87, 65, 2, 211, 225, 4, 69, 10, 62, 65, 16, 3, 72, 65, 65, 178, 144, 3, + 84, 183, 245, 1, 79, 2, 131, 1, 86, 5, 251, 224, 4, 76, 2, 17, 2, 65, 65, + 2, 211, 224, 4, 86, 6, 26, 65, 143, 133, 5, 79, 4, 26, 86, 243, 132, 5, + 65, 2, 21, 3, 73, 89, 65, 2, 255, 240, 4, 78, 6, 48, 3, 65, 66, 79, 14, + 66, 1, 3, 79, 66, 79, 2, 23, 65, 2, 11, 79, 2, 11, 70, 2, 11, 73, 2, 135, + 240, 4, 76, 2, 155, 131, 4, 75, 174, 1, 26, 67, 147, 143, 3, 68, 154, 1, + 132, 1, 9, 72, 65, 82, 65, 67, 84, 69, 82, 32, 213, 215, 4, 17, 85, 82, + 82, 69, 78, 67, 89, 32, 83, 89, 77, 66, 79, 76, 32, 66, 65, 152, 1, 176, + 2, 6, 66, 79, 32, 66, 65, 73, 16, 6, 67, 72, 79, 32, 67, 72, 56, 3, 68, + 79, 32, 44, 2, 70, 79, 76, 3, 72, 79, 32, 66, 75, 190, 1, 76, 138, 1, 77, + 252, 1, 7, 65, 78, 71, 75, 72, 65, 78, 46, 78, 158, 1, 80, 234, 1, 82, + 70, 83, 238, 2, 84, 224, 2, 3, 87, 79, 32, 34, 89, 225, 150, 4, 2, 79, + 32, 2, 143, 12, 77, 8, 192, 129, 3, 2, 65, 78, 138, 173, 1, 73, 167, 58, + 79, 4, 132, 218, 4, 3, 67, 72, 65, 227, 33, 68, 6, 32, 2, 32, 70, 21, 2, + 78, 71, 4, 207, 128, 3, 65, 2, 139, 178, 3, 77, 4, 40, 4, 78, 79, 75, 72, + 191, 252, 4, 72, 2, 207, 250, 4, 85, 14, 40, 2, 72, 79, 229, 9, 3, 79, + 32, 75, 12, 26, 32, 191, 131, 4, 77, 10, 36, 2, 75, 72, 57, 3, 82, 65, + 75, 8, 158, 9, 87, 150, 234, 3, 85, 210, 57, 79, 139, 60, 65, 2, 215, + 163, 4, 72, 8, 76, 2, 79, 32, 224, 11, 8, 65, 75, 75, 72, 65, 78, 71, 89, + 235, 239, 4, 85, 4, 32, 2, 67, 72, 163, 170, 4, 76, 2, 139, 217, 4, 85, + 16, 28, 2, 65, 73, 211, 52, 79, 14, 42, 32, 176, 1, 3, 84, 65, 73, 19, + 89, 10, 76, 4, 67, 72, 65, 84, 44, 5, 72, 65, 78, 45, 65, 22, 84, 159, + 246, 4, 69, 2, 11, 84, 2, 11, 65, 2, 163, 247, 4, 87, 2, 151, 240, 3, 75, + 4, 190, 218, 4, 72, 159, 11, 82, 2, 203, 2, 75, 2, 161, 207, 4, 2, 65, + 77, 8, 60, 3, 71, 79, 32, 32, 3, 73, 75, 72, 29, 3, 79, 32, 78, 2, 11, + 78, 2, 179, 211, 4, 71, 2, 205, 219, 4, 2, 65, 72, 4, 138, 168, 4, 69, + 227, 79, 85, 12, 68, 6, 65, 73, 89, 65, 78, 78, 22, 72, 217, 212, 4, 3, + 79, 32, 80, 2, 171, 227, 4, 79, 8, 36, 3, 73, 78, 84, 21, 2, 79, 32, 2, + 223, 209, 4, 72, 6, 44, 2, 80, 72, 161, 5, 4, 83, 65, 77, 80, 4, 138, + 165, 4, 85, 155, 1, 65, 4, 32, 2, 79, 32, 195, 245, 4, 85, 2, 11, 82, 2, + 251, 242, 4, 85, 36, 44, 4, 65, 82, 65, 32, 225, 1, 2, 79, 32, 28, 62, + 65, 130, 1, 85, 238, 230, 2, 73, 198, 140, 2, 69, 3, 79, 13, 64, 6, 73, + 32, 77, 65, 73, 77, 238, 243, 4, 65, 2, 69, 3, 77, 4, 26, 65, 183, 167, + 3, 85, 2, 11, 76, 2, 235, 223, 4, 65, 9, 142, 179, 4, 69, 163, 64, 85, 8, + 24, 2, 82, 85, 23, 83, 2, 155, 223, 4, 83, 6, 198, 208, 4, 65, 226, 31, + 85, 187, 2, 79, 18, 30, 72, 133, 2, 2, 79, 32, 14, 48, 6, 65, 78, 84, 72, + 65, 75, 21, 2, 79, 32, 2, 175, 232, 3, 72, 12, 80, 8, 78, 65, 78, 71, 77, + 79, 78, 84, 20, 4, 80, 72, 85, 84, 13, 2, 84, 72, 2, 131, 210, 4, 72, 2, + 123, 72, 8, 34, 65, 190, 159, 4, 79, 3, 85, 4, 150, 164, 3, 72, 159, 204, + 1, 78, 4, 38, 84, 225, 182, 2, 3, 80, 65, 84, 2, 243, 208, 4, 65, 2, 11, + 87, 2, 195, 153, 3, 65, 6, 30, 65, 45, 3, 79, 32, 89, 2, 21, 3, 77, 65, + 75, 2, 223, 162, 3, 75, 4, 238, 157, 4, 73, 227, 77, 65, 12, 26, 82, 167, + 199, 2, 84, 10, 30, 69, 145, 1, 2, 77, 79, 6, 18, 32, 107, 70, 4, 84, 11, + 68, 79, 69, 83, 32, 78, 79, 84, 32, 69, 88, 229, 228, 3, 4, 69, 88, 73, + 83, 2, 171, 127, 73, 2, 211, 202, 3, 79, 4, 46, 77, 177, 194, 3, 5, 68, + 89, 78, 65, 77, 2, 191, 178, 3, 69, 10, 18, 78, 95, 82, 8, 26, 32, 235, + 227, 3, 75, 6, 26, 83, 203, 224, 2, 71, 4, 170, 211, 2, 65, 187, 219, 1, + 80, 2, 21, 3, 68, 32, 80, 2, 25, 4, 76, 65, 67, 69, 2, 17, 2, 32, 77, 2, + 159, 213, 3, 69, 4, 56, 4, 85, 71, 72, 84, 189, 212, 3, 4, 78, 71, 32, + 83, 2, 133, 192, 1, 5, 32, 66, 65, 76, 76, 46, 22, 32, 207, 3, 45, 30, + 154, 1, 68, 90, 76, 118, 82, 170, 158, 1, 66, 86, 67, 194, 6, 83, 254, + 166, 1, 80, 253, 22, 15, 78, 69, 84, 87, 79, 82, 75, 69, 68, 32, 67, 79, + 77, 80, 85, 4, 64, 10, 73, 77, 69, 78, 83, 73, 79, 78, 65, 76, 135, 162, + 1, 79, 2, 175, 193, 3, 32, 6, 80, 12, 73, 78, 69, 83, 32, 67, 79, 78, 86, + 69, 82, 71, 249, 154, 1, 2, 69, 70, 4, 153, 188, 4, 3, 73, 78, 71, 10, + 40, 4, 65, 89, 83, 32, 155, 154, 1, 73, 8, 238, 196, 2, 66, 130, 165, 1, + 65, 130, 82, 76, 31, 82, 16, 44, 2, 68, 32, 254, 3, 80, 247, 160, 1, 69, + 12, 196, 2, 12, 84, 79, 80, 45, 76, 73, 71, 72, 84, 69, 68, 32, 80, 17, + 76, 69, 70, 84, 45, 76, 73, 71, 72, 84, 69, 68, 32, 68, 79, 87, 78, 0, + 16, 82, 73, 71, 72, 84, 45, 76, 73, 71, 72, 84, 69, 68, 32, 85, 80, 221, + 137, 2, 25, 66, 79, 84, 84, 79, 77, 45, 76, 73, 71, 72, 84, 69, 68, 32, + 82, 73, 71, 72, 84, 87, 65, 82, 68, 83, 6, 76, 4, 76, 69, 70, 84, 69, 11, + 82, 73, 71, 72, 84, 87, 65, 82, 68, 83, 32, 2, 11, 87, 2, 25, 4, 65, 82, + 68, 83, 2, 173, 243, 1, 2, 32, 69, 4, 146, 243, 1, 69, 159, 22, 65, 2, + 177, 163, 4, 5, 69, 82, 45, 69, 77, 8, 34, 77, 85, 4, 78, 68, 69, 82, 4, + 21, 3, 66, 83, 32, 4, 38, 85, 229, 228, 2, 3, 68, 79, 87, 2, 235, 217, 3, + 80, 4, 216, 177, 3, 10, 32, 67, 76, 79, 85, 68, 32, 65, 78, 68, 181, 48, + 2, 83, 84, 230, 5, 204, 1, 6, 66, 69, 84, 65, 78, 32, 148, 45, 6, 69, 32, + 79, 86, 69, 82, 72, 7, 70, 73, 78, 65, 71, 72, 32, 206, 8, 71, 152, 1, 3, + 76, 68, 69, 252, 2, 2, 77, 69, 60, 2, 78, 89, 78, 82, 171, 208, 3, 67, + 160, 3, 228, 2, 18, 65, 83, 84, 82, 79, 76, 79, 71, 73, 67, 65, 76, 32, + 83, 73, 71, 78, 32, 172, 1, 18, 67, 65, 78, 84, 73, 76, 76, 65, 84, 73, + 79, 78, 32, 83, 73, 71, 78, 32, 172, 1, 6, 68, 73, 71, 73, 84, 32, 100, + 9, 75, 85, 32, 82, 85, 32, 75, 72, 65, 26, 76, 176, 3, 5, 77, 65, 82, 75, + 32, 222, 18, 83, 181, 15, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, + 6, 46, 83, 169, 40, 6, 45, 75, 72, 89, 85, 68, 4, 104, 8, 68, 79, 78, 71, + 32, 84, 83, 72, 169, 21, 13, 71, 82, 65, 32, 71, 67, 65, 78, 32, 45, 67, + 72, 65, 2, 199, 31, 85, 8, 148, 1, 7, 83, 66, 85, 66, 32, 45, 67, 180, + 207, 3, 5, 72, 69, 65, 86, 89, 0, 5, 76, 73, 71, 72, 84, 233, 100, 8, 67, + 65, 78, 71, 32, 84, 69, 45, 2, 167, 205, 3, 72, 40, 144, 229, 2, 4, 72, + 65, 76, 70, 82, 70, 30, 83, 42, 84, 62, 90, 210, 86, 78, 14, 79, 227, + 112, 69, 5, 221, 10, 2, 32, 66, 92, 96, 6, 69, 84, 84, 69, 82, 32, 153, + 2, 13, 79, 71, 79, 84, 89, 80, 69, 32, 83, 73, 71, 78, 32, 88, 230, 1, + 75, 170, 120, 82, 168, 146, 3, 10, 70, 73, 88, 69, 68, 45, 70, 79, 82, + 77, 202, 1, 68, 86, 78, 46, 83, 38, 84, 46, 66, 2, 67, 2, 71, 2, 80, 2, + 90, 138, 69, 45, 2, 72, 2, 74, 2, 76, 2, 77, 2, 87, 2, 89, 187, 2, 65, 8, + 226, 210, 4, 83, 14, 72, 2, 75, 187, 2, 65, 4, 224, 25, 3, 76, 72, 65, + 13, 4, 67, 72, 65, 68, 80, 254, 2, 66, 176, 1, 8, 77, 78, 89, 65, 77, 32, + 89, 73, 114, 67, 172, 2, 13, 89, 73, 71, 32, 77, 71, 79, 32, 84, 83, 72, + 69, 71, 190, 1, 71, 212, 2, 9, 65, 78, 71, 32, 75, 72, 65, 78, 71, 58, + 72, 48, 2, 73, 78, 246, 1, 78, 222, 1, 82, 118, 83, 46, 84, 36, 4, 76, + 69, 65, 68, 220, 105, 2, 80, 65, 173, 191, 1, 17, 68, 69, 76, 73, 77, 73, + 84, 69, 82, 32, 84, 83, 72, 69, 71, 32, 66, 10, 52, 9, 75, 65, 45, 32, + 83, 72, 79, 71, 32, 27, 83, 4, 142, 1, 71, 67, 89, 6, 34, 75, 201, 21, 3, + 68, 85, 83, 4, 56, 6, 65, 45, 32, 83, 72, 79, 89, 4, 85, 82, 32, 89, 2, + 21, 3, 71, 32, 71, 2, 41, 8, 73, 32, 77, 71, 79, 32, 82, 71, 2, 179, 131, + 3, 89, 2, 221, 2, 2, 73, 71, 12, 84, 5, 65, 82, 69, 84, 32, 240, 1, 2, + 72, 69, 29, 7, 76, 79, 83, 73, 78, 71, 32, 6, 112, 12, 45, 68, 90, 85, + 68, 32, 82, 84, 65, 71, 83, 32, 97, 12, 89, 73, 71, 32, 77, 71, 79, 32, + 80, 72, 85, 82, 4, 42, 66, 37, 6, 77, 69, 32, 76, 79, 78, 2, 33, 6, 90, + 72, 73, 32, 77, 73, 2, 203, 22, 71, 2, 225, 3, 3, 32, 83, 72, 2, 137, + 189, 2, 2, 32, 77, 4, 68, 13, 66, 82, 68, 65, 32, 82, 78, 89, 73, 78, 71, + 32, 89, 3, 89, 2, 213, 5, 11, 73, 71, 32, 77, 71, 79, 32, 83, 71, 65, 66, + 12, 68, 4, 84, 69, 82, 32, 141, 2, 8, 85, 71, 32, 82, 84, 65, 71, 83, 8, + 56, 8, 89, 73, 71, 32, 77, 71, 79, 32, 191, 132, 4, 84, 6, 68, 4, 45, 85, + 77, 32, 117, 9, 84, 82, 85, 78, 67, 65, 84, 69, 68, 4, 88, 7, 82, 78, 65, + 77, 32, 66, 67, 245, 2, 10, 71, 84, 69, 82, 32, 84, 83, 72, 69, 71, 2, + 241, 2, 2, 65, 68, 2, 195, 198, 4, 32, 4, 21, 3, 32, 71, 89, 4, 238, 248, + 3, 79, 135, 18, 65, 2, 17, 2, 65, 76, 2, 205, 163, 4, 2, 65, 78, 6, 92, + 6, 73, 84, 73, 65, 76, 32, 145, 129, 4, 11, 84, 69, 82, 83, 89, 76, 76, + 65, 66, 73, 67, 4, 68, 13, 66, 82, 68, 65, 32, 82, 78, 89, 73, 78, 71, + 32, 89, 3, 89, 2, 53, 11, 73, 71, 32, 77, 71, 79, 32, 77, 68, 85, 78, 2, + 143, 165, 4, 32, 10, 72, 10, 71, 65, 83, 32, 66, 90, 85, 78, 71, 32, 77, + 4, 89, 73, 83, 32, 4, 56, 3, 83, 71, 79, 237, 162, 4, 5, 78, 89, 73, 32, + 90, 2, 251, 9, 82, 6, 44, 5, 84, 83, 72, 69, 71, 143, 254, 3, 83, 5, 255, + 253, 3, 32, 4, 220, 253, 3, 8, 71, 89, 65, 32, 71, 82, 65, 77, 1, 14, 73, + 78, 32, 67, 72, 69, 78, 32, 83, 80, 85, 78, 71, 83, 4, 232, 252, 3, 4, + 66, 82, 85, 76, 39, 72, 6, 32, 4, 82, 65, 73, 76, 55, 83, 2, 225, 7, 9, + 73, 78, 71, 32, 77, 67, 72, 65, 78, 4, 56, 5, 65, 32, 45, 80, 72, 173, + 251, 3, 3, 72, 69, 71, 2, 147, 157, 4, 82, 156, 1, 84, 4, 73, 71, 78, 32, + 228, 6, 9, 85, 66, 74, 79, 73, 78, 69, 68, 32, 143, 4, 89, 42, 176, 1, 4, + 71, 82, 85, 32, 96, 2, 76, 67, 30, 77, 36, 11, 78, 89, 73, 32, 90, 76, + 65, 32, 78, 65, 65, 22, 82, 252, 2, 2, 89, 65, 130, 4, 73, 165, 4, 5, 83, + 78, 65, 32, 76, 4, 40, 3, 67, 65, 78, 1, 3, 77, 69, 68, 2, 25, 4, 32, 82, + 71, 89, 2, 169, 4, 2, 73, 78, 4, 238, 3, 73, 179, 4, 69, 4, 136, 4, 2, + 65, 82, 231, 3, 67, 2, 179, 153, 4, 32, 20, 116, 4, 68, 69, 76, 32, 240, + 1, 10, 74, 69, 83, 32, 83, 85, 32, 78, 71, 65, 145, 245, 3, 6, 78, 65, + 77, 32, 66, 67, 16, 52, 5, 68, 75, 65, 82, 32, 53, 4, 78, 65, 71, 32, 8, + 94, 71, 169, 208, 2, 6, 82, 68, 69, 76, 32, 78, 8, 42, 71, 69, 6, 82, 68, + 69, 76, 32, 68, 6, 46, 67, 220, 148, 2, 2, 78, 89, 231, 123, 83, 2, 143, + 187, 4, 73, 2, 147, 152, 3, 75, 2, 187, 201, 2, 32, 4, 18, 78, 71, 82, 2, + 11, 71, 2, 21, 3, 32, 82, 84, 2, 11, 65, 2, 135, 253, 3, 71, 2, 17, 2, + 32, 84, 2, 171, 164, 3, 83, 94, 68, 7, 76, 69, 84, 84, 69, 82, 32, 145, + 2, 5, 83, 73, 71, 78, 32, 88, 220, 1, 10, 70, 73, 88, 69, 68, 45, 70, 79, + 82, 77, 242, 238, 3, 68, 50, 75, 38, 78, 46, 83, 38, 84, 46, 66, 2, 67, + 2, 71, 2, 80, 2, 90, 138, 69, 45, 2, 72, 2, 74, 2, 76, 2, 77, 2, 82, 2, + 87, 2, 89, 187, 2, 65, 6, 11, 32, 6, 186, 181, 4, 82, 2, 87, 3, 89, 6, + 38, 73, 50, 77, 33, 3, 76, 67, 69, 2, 45, 9, 78, 86, 69, 82, 84, 69, 68, + 32, 77, 2, 11, 67, 2, 45, 2, 72, 85, 2, 25, 4, 32, 84, 83, 65, 2, 11, 32, + 2, 147, 234, 2, 67, 20, 60, 6, 76, 76, 65, 66, 76, 69, 21, 5, 77, 66, 79, + 76, 32, 2, 247, 164, 4, 32, 18, 104, 4, 68, 82, 73, 76, 32, 6, 78, 79, + 82, 32, 66, 85, 130, 1, 80, 93, 7, 82, 68, 79, 32, 82, 74, 69, 2, 11, 32, + 2, 231, 143, 4, 66, 9, 11, 32, 6, 72, 4, 66, 90, 72, 73, 0, 4, 71, 83, + 85, 77, 1, 4, 78, 89, 73, 83, 2, 145, 212, 2, 5, 32, 45, 75, 72, 89, 4, + 52, 6, 65, 68, 77, 65, 32, 71, 21, 3, 72, 85, 82, 2, 231, 230, 2, 68, 2, + 199, 117, 32, 5, 181, 245, 2, 6, 32, 82, 71, 89, 65, 32, 30, 116, 8, 82, + 69, 86, 69, 82, 83, 69, 68, 194, 90, 85, 22, 86, 186, 141, 1, 79, 238, + 60, 73, 166, 204, 1, 69, 235, 61, 65, 4, 245, 164, 2, 2, 32, 73, 2, 25, + 4, 32, 73, 78, 70, 2, 11, 73, 2, 11, 78, 2, 139, 193, 2, 73, 118, 216, 1, + 9, 67, 79, 78, 83, 79, 78, 65, 78, 84, 20, 7, 76, 69, 84, 84, 69, 82, 32, + 164, 6, 20, 77, 79, 68, 73, 70, 73, 69, 82, 32, 76, 69, 84, 84, 69, 82, + 32, 76, 65, 66, 73, 37, 8, 83, 69, 80, 65, 82, 65, 84, 79, 2, 223, 240, + 3, 32, 112, 106, 65, 108, 17, 66, 69, 82, 66, 69, 82, 32, 65, 67, 65, 68, + 69, 77, 89, 32, 89, 65, 30, 84, 211, 1, 89, 4, 84, 6, 89, 69, 82, 32, 89, + 65, 137, 172, 4, 9, 72, 65, 71, 71, 65, 82, 32, 89, 65, 2, 147, 172, 4, + 71, 4, 142, 173, 4, 72, 3, 74, 18, 92, 11, 65, 87, 69, 76, 76, 69, 77, + 69, 84, 32, 89, 21, 8, 85, 65, 82, 69, 71, 32, 89, 65, 2, 235, 205, 2, + 65, 16, 62, 71, 226, 26, 75, 210, 143, 4, 90, 62, 78, 86, 72, 3, 81, 4, + 190, 171, 4, 72, 3, 78, 86, 54, 65, 210, 25, 69, 158, 145, 4, 73, 2, 79, + 3, 85, 77, 194, 1, 71, 2, 75, 18, 83, 30, 84, 30, 90, 134, 23, 68, 126, + 66, 2, 72, 198, 58, 82, 142, 213, 3, 67, 146, 1, 65, 2, 70, 2, 74, 2, 76, + 2, 77, 2, 78, 2, 80, 2, 81, 2, 86, 2, 87, 3, 89, 7, 199, 24, 72, 7, 150, + 169, 4, 72, 3, 83, 7, 250, 168, 4, 72, 3, 84, 7, 222, 168, 4, 72, 3, 90, + 2, 141, 191, 3, 4, 65, 76, 73, 90, 2, 219, 230, 3, 82, 6, 76, 2, 69, 82, + 21, 13, 72, 84, 32, 84, 82, 73, 70, 79, 76, 73, 65, 84, 69, 5, 167, 234, + 3, 32, 2, 33, 6, 32, 83, 78, 79, 87, 70, 2, 219, 58, 76, 19, 11, 32, 16, + 72, 8, 79, 80, 69, 82, 65, 84, 79, 82, 233, 1, 5, 87, 73, 84, 72, 32, 11, + 11, 32, 8, 38, 65, 101, 5, 87, 73, 84, 72, 32, 4, 25, 4, 66, 79, 86, 69, + 4, 11, 32, 4, 26, 76, 171, 131, 3, 82, 2, 11, 69, 2, 179, 131, 3, 70, 4, + 26, 82, 159, 164, 3, 68, 2, 17, 2, 73, 83, 2, 249, 209, 2, 3, 73, 78, 71, + 6, 26, 68, 223, 239, 1, 82, 4, 11, 79, 4, 247, 129, 2, 84, 6, 26, 82, + 187, 131, 1, 83, 2, 11, 32, 2, 195, 177, 1, 67, 5, 21, 3, 32, 84, 87, 2, + 21, 3, 79, 32, 68, 2, 185, 92, 3, 79, 84, 83, 170, 1, 100, 5, 72, 85, 84, + 65, 32, 184, 6, 11, 79, 78, 73, 65, 78, 32, 83, 73, 71, 78, 32, 231, 136, + 3, 69, 164, 1, 178, 1, 65, 88, 7, 76, 69, 84, 84, 69, 82, 32, 204, 1, 2, + 83, 73, 200, 1, 11, 86, 79, 87, 69, 76, 32, 83, 73, 71, 78, 32, 218, 168, + 2, 68, 180, 155, 1, 2, 71, 86, 255, 71, 79, 4, 18, 66, 51, 78, 2, 29, 5, + 66, 82, 69, 86, 73, 2, 247, 26, 65, 2, 175, 140, 4, 74, 94, 254, 65, 65, + 38, 68, 114, 84, 46, 86, 186, 5, 85, 186, 202, 1, 73, 138, 196, 1, 78, + 46, 83, 82, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, 80, 138, 69, 72, 2, 76, 2, + 77, 2, 82, 2, 89, 186, 2, 69, 3, 79, 12, 21, 3, 71, 78, 32, 12, 46, 67, + 98, 78, 190, 66, 65, 187, 151, 3, 86, 2, 11, 65, 2, 11, 78, 2, 25, 4, 68, + 82, 65, 66, 2, 11, 73, 2, 11, 78, 2, 171, 248, 3, 68, 2, 11, 85, 2, 167, + 248, 3, 75, 30, 78, 83, 242, 68, 65, 38, 85, 22, 86, 166, 202, 1, 73, + 198, 140, 2, 69, 3, 79, 4, 25, 4, 72, 79, 82, 84, 4, 11, 32, 4, 230, 155, + 4, 69, 3, 79, 4, 176, 230, 3, 8, 67, 65, 80, 73, 84, 65, 76, 32, 239, 24, + 69, 254, 2, 192, 2, 12, 68, 72, 82, 73, 32, 76, 69, 84, 84, 69, 82, 32, + 180, 4, 10, 76, 79, 78, 71, 32, 83, 73, 75, 73, 32, 154, 4, 77, 18, 78, + 34, 79, 80, 2, 80, 32, 168, 17, 23, 82, 84, 79, 73, 83, 69, 32, 83, 72, + 69, 76, 76, 32, 66, 82, 65, 67, 75, 69, 84, 69, 68, 32, 130, 4, 84, 202, + 151, 2, 73, 249, 70, 5, 75, 89, 79, 32, 84, 104, 242, 1, 71, 42, 74, 30, + 77, 34, 78, 70, 72, 34, 80, 34, 83, 234, 55, 82, 210, 147, 1, 79, 238, + 60, 69, 42, 76, 198, 2, 65, 154, 194, 1, 67, 2, 68, 2, 75, 2, 84, 2, 88, + 2, 90, 138, 69, 66, 2, 70, 2, 81, 2, 86, 186, 2, 73, 2, 85, 3, 89, 6, + 150, 148, 4, 72, 2, 74, 187, 2, 65, 4, 166, 150, 4, 65, 3, 89, 4, 210, + 147, 4, 66, 187, 2, 65, 14, 66, 71, 142, 201, 2, 74, 222, 132, 1, 88, + 138, 69, 68, 187, 2, 65, 4, 238, 146, 4, 74, 187, 2, 65, 4, 206, 146, 4, + 83, 187, 2, 65, 10, 54, 72, 150, 200, 2, 75, 230, 201, 1, 84, 187, 2, 65, + 4, 246, 145, 4, 84, 187, 2, 65, 108, 92, 7, 76, 69, 84, 84, 69, 82, 32, + 228, 2, 5, 83, 73, 71, 78, 32, 58, 85, 143, 156, 2, 68, 82, 190, 1, 65, + 22, 68, 30, 78, 58, 82, 14, 84, 30, 66, 2, 67, 2, 71, 2, 74, 2, 75, 2, + 80, 226, 144, 4, 69, 2, 72, 2, 73, 2, 76, 2, 77, 2, 79, 2, 83, 2, 85, 2, + 86, 2, 88, 3, 89, 5, 239, 145, 4, 65, 9, 122, 68, 227, 144, 4, 72, 11, + 34, 78, 158, 145, 4, 71, 3, 89, 5, 155, 145, 4, 89, 7, 39, 82, 9, 26, 84, + 227, 144, 4, 72, 5, 223, 144, 4, 72, 4, 176, 238, 3, 2, 83, 69, 177, 25, + 4, 72, 69, 67, 65, 2, 205, 205, 3, 2, 78, 71, 2, 239, 28, 65, 2, 11, 71, + 2, 135, 249, 3, 85, 6, 32, 2, 84, 72, 207, 128, 3, 76, 5, 11, 66, 2, 11, + 82, 2, 131, 164, 2, 85, 72, 252, 1, 4, 65, 82, 67, 32, 162, 2, 67, 44, 2, + 72, 65, 166, 3, 80, 140, 3, 13, 74, 85, 83, 84, 73, 70, 73, 69, 68, 32, + 76, 79, 87, 84, 5, 76, 69, 70, 84, 32, 228, 1, 6, 82, 73, 71, 72, 84, 32, + 170, 2, 83, 38, 84, 81, 7, 87, 73, 84, 72, 32, 85, 80, 6, 180, 1, 19, 65, + 78, 84, 73, 67, 76, 79, 67, 75, 87, 73, 83, 69, 32, 65, 82, 82, 79, 87, + 73, 21, 67, 76, 79, 67, 75, 87, 73, 83, 69, 32, 65, 82, 82, 79, 87, 32, + 87, 73, 84, 72, 32, 5, 29, 5, 32, 87, 73, 84, 72, 2, 17, 2, 32, 80, 2, + 167, 130, 2, 76, 2, 11, 77, 2, 251, 129, 2, 73, 2, 11, 85, 2, 217, 183, + 3, 3, 82, 76, 89, 34, 36, 3, 76, 70, 32, 135, 138, 4, 84, 32, 70, 70, + 186, 1, 76, 22, 82, 178, 2, 83, 250, 5, 66, 211, 245, 1, 73, 8, 136, 1, + 15, 79, 82, 87, 65, 82, 68, 45, 70, 65, 67, 73, 78, 71, 32, 82, 157, 2, + 13, 76, 65, 73, 76, 73, 78, 71, 32, 82, 79, 66, 79, 84, 4, 22, 85, 243, + 1, 79, 2, 159, 202, 3, 78, 8, 41, 2, 69, 70, 8, 21, 3, 73, 71, 72, 8, 11, + 84, 8, 54, 32, 69, 9, 45, 70, 65, 67, 73, 78, 71, 32, 82, 2, 11, 80, 2, + 33, 6, 65, 82, 69, 78, 84, 72, 2, 207, 223, 1, 69, 6, 38, 79, 21, 5, 85, + 78, 78, 69, 82, 2, 239, 181, 3, 66, 4, 29, 5, 32, 70, 82, 65, 77, 4, 11, + 69, 4, 11, 45, 4, 218, 133, 4, 49, 3, 50, 4, 18, 69, 59, 84, 2, 11, 67, + 2, 11, 84, 2, 11, 73, 2, 255, 136, 2, 79, 2, 17, 2, 65, 78, 2, 11, 68, 2, + 21, 3, 73, 78, 71, 2, 17, 2, 32, 80, 2, 11, 69, 2, 11, 82, 2, 163, 180, + 3, 83, 4, 17, 2, 69, 82, 4, 33, 6, 32, 72, 65, 76, 70, 32, 4, 250, 3, 66, + 139, 106, 87, 10, 180, 1, 13, 74, 85, 83, 84, 73, 70, 73, 69, 68, 32, 76, + 79, 87, 126, 67, 50, 72, 161, 217, 2, 21, 66, 76, 65, 67, 75, 32, 76, 69, + 70, 84, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 83, 2, 237, 1, 7, 69, 82, + 32, 82, 73, 71, 72, 8, 78, 67, 50, 72, 33, 13, 74, 85, 83, 84, 73, 70, + 73, 69, 68, 32, 76, 79, 87, 4, 26, 79, 199, 135, 3, 82, 2, 223, 194, 3, + 82, 2, 209, 173, 3, 3, 65, 76, 70, 2, 33, 6, 69, 82, 32, 76, 69, 70, 2, + 53, 11, 84, 32, 81, 85, 65, 82, 84, 69, 82, 32, 66, 2, 11, 76, 2, 137, + 171, 2, 3, 65, 67, 75, 2, 157, 172, 3, 4, 81, 85, 65, 82, 2, 11, 79, 2, + 249, 171, 3, 12, 82, 84, 79, 73, 83, 69, 32, 83, 72, 69, 76, 76, 2, 29, + 5, 87, 65, 82, 68, 83, 2, 17, 2, 32, 65, 2, 237, 128, 3, 4, 82, 82, 79, + 87, 20, 188, 1, 22, 67, 74, 75, 32, 85, 78, 73, 70, 73, 69, 68, 32, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 161, 2, 19, 76, 65, 84, 73, 78, 32, + 67, 65, 80, 73, 84, 65, 76, 32, 76, 69, 84, 84, 69, 18, 40, 2, 52, 69, + 34, 53, 54, 54, 87, 55, 4, 202, 1, 48, 139, 208, 2, 56, 4, 30, 50, 141, + 1, 2, 66, 56, 2, 131, 181, 3, 68, 6, 48, 2, 50, 53, 22, 53, 185, 208, 2, + 2, 55, 50, 2, 219, 250, 3, 51, 2, 67, 53, 4, 32, 2, 48, 66, 21, 2, 54, + 68, 2, 151, 250, 3, 57, 2, 131, 250, 3, 55, 2, 11, 82, 2, 135, 188, 3, + 32, 64, 48, 6, 65, 76, 32, 82, 85, 78, 21, 2, 79, 32, 2, 203, 255, 2, 79, + 62, 72, 7, 76, 69, 84, 84, 69, 82, 32, 217, 2, 6, 83, 73, 71, 78, 32, 82, + 60, 206, 1, 66, 106, 78, 138, 31, 73, 206, 141, 1, 69, 138, 76, 67, 138, + 189, 1, 65, 234, 61, 68, 2, 71, 2, 72, 2, 74, 2, 75, 2, 76, 2, 77, 2, 80, + 2, 82, 2, 83, 2, 84, 2, 87, 2, 89, 186, 2, 79, 3, 85, 12, 52, 7, 82, 69, + 65, 84, 72, 89, 32, 183, 246, 3, 65, 10, 182, 31, 73, 206, 141, 1, 69, + 223, 178, 2, 65, 4, 202, 243, 3, 71, 187, 2, 65, 2, 17, 2, 73, 83, 2, 21, + 3, 73, 78, 71, 2, 11, 32, 2, 211, 200, 2, 84, 104, 54, 65, 254, 1, 69, + 86, 73, 246, 11, 79, 231, 1, 85, 16, 66, 67, 36, 2, 68, 69, 34, 77, 20, + 2, 78, 83, 231, 163, 3, 73, 4, 218, 233, 1, 75, 219, 209, 1, 84, 2, 133, + 17, 4, 32, 77, 65, 82, 5, 243, 212, 1, 32, 4, 44, 2, 80, 76, 33, 5, 86, + 69, 82, 83, 65, 2, 11, 85, 2, 147, 212, 3, 84, 2, 163, 63, 76, 4, 52, 7, + 65, 83, 85, 82, 69, 32, 67, 235, 220, 2, 69, 2, 11, 72, 2, 171, 4, 69, + 66, 156, 1, 3, 65, 78, 71, 176, 3, 8, 68, 69, 78, 84, 32, 69, 77, 66, 20, + 9, 71, 82, 65, 77, 32, 70, 79, 82, 32, 140, 2, 4, 80, 76, 69, 32, 159, + 155, 3, 67, 16, 48, 2, 76, 69, 245, 1, 5, 85, 76, 65, 82, 32, 10, 44, 6, + 32, 87, 73, 84, 72, 32, 163, 1, 45, 8, 80, 9, 83, 69, 82, 73, 70, 83, 32, + 65, 84, 54, 85, 238, 169, 2, 82, 207, 68, 68, 2, 17, 2, 32, 66, 2, 189, + 222, 3, 3, 79, 84, 84, 2, 149, 61, 2, 78, 68, 2, 193, 204, 2, 4, 72, 69, + 65, 68, 6, 68, 9, 70, 76, 65, 71, 32, 79, 78, 32, 80, 34, 82, 195, 139, + 2, 66, 2, 11, 79, 2, 199, 209, 3, 83, 2, 11, 85, 2, 231, 175, 3, 76, 2, + 147, 219, 3, 76, 16, 66, 69, 34, 72, 34, 76, 22, 77, 46, 84, 42, 87, 155, + 211, 1, 70, 2, 11, 65, 2, 227, 231, 1, 82, 2, 11, 69, 2, 227, 249, 1, 65, + 2, 207, 188, 3, 65, 2, 21, 3, 79, 85, 78, 2, 147, 189, 2, 84, 2, 17, 2, + 72, 85, 2, 251, 199, 1, 78, 4, 178, 177, 2, 65, 251, 41, 73, 30, 176, 2, + 3, 67, 79, 76, 24, 20, 72, 79, 82, 73, 90, 79, 78, 84, 65, 76, 32, 66, + 65, 82, 32, 87, 73, 84, 72, 32, 98, 80, 34, 84, 20, 13, 86, 69, 82, 84, + 73, 67, 65, 76, 32, 66, 65, 82, 32, 44, 9, 83, 79, 76, 73, 68, 85, 83, + 32, 66, 224, 21, 2, 68, 65, 160, 187, 1, 6, 78, 69, 83, 84, 69, 68, 159, + 12, 73, 2, 165, 37, 2, 79, 78, 4, 34, 68, 33, 4, 84, 82, 73, 80, 2, 17, + 2, 79, 85, 2, 11, 66, 2, 177, 183, 3, 2, 76, 69, 4, 142, 223, 1, 76, 235, + 110, 82, 2, 195, 202, 1, 73, 8, 42, 66, 74, 68, 170, 189, 1, 82, 115, 87, + 2, 29, 5, 73, 78, 65, 82, 89, 2, 21, 3, 32, 82, 69, 2, 143, 33, 76, 2, + 25, 4, 69, 76, 73, 77, 2, 163, 172, 2, 73, 12, 32, 2, 76, 76, 42, 77, 23, + 80, 5, 17, 2, 69, 89, 2, 255, 220, 1, 66, 2, 247, 184, 2, 66, 6, 44, 5, + 73, 67, 65, 76, 32, 199, 211, 3, 72, 4, 18, 68, 47, 70, 2, 11, 82, 2, 11, + 73, 2, 199, 225, 3, 78, 2, 195, 249, 1, 73, 6, 18, 69, 79, 77, 5, 213, + 191, 3, 14, 32, 76, 73, 71, 72, 84, 32, 77, 79, 79, 78, 32, 65, 82, 2, + 191, 174, 3, 80, 216, 1, 76, 3, 71, 82, 73, 22, 76, 214, 12, 82, 173, + 188, 2, 5, 77, 66, 76, 69, 82, 2, 239, 219, 2, 75, 162, 1, 68, 11, 85, + 45, 84, 73, 71, 65, 76, 65, 82, 73, 32, 135, 226, 3, 73, 160, 1, 122, 65, + 42, 67, 30, 68, 106, 71, 32, 7, 76, 69, 84, 84, 69, 82, 32, 178, 3, 82, + 32, 5, 83, 73, 71, 78, 32, 131, 3, 86, 2, 11, 85, 2, 133, 159, 3, 2, 32, + 76, 2, 201, 162, 3, 2, 79, 78, 4, 18, 79, 67, 65, 2, 11, 85, 2, 11, 66, + 2, 25, 4, 76, 69, 32, 68, 2, 11, 65, 2, 243, 186, 3, 78, 2, 213, 218, 2, + 3, 69, 77, 73, 100, 206, 1, 65, 38, 68, 46, 76, 38, 82, 34, 84, 46, 86, + 186, 5, 85, 186, 202, 1, 73, 138, 196, 1, 78, 46, 83, 82, 66, 2, 67, 2, + 71, 2, 74, 2, 75, 2, 80, 206, 40, 79, 162, 8, 69, 158, 20, 72, 2, 77, 3, + 89, 9, 242, 221, 3, 65, 2, 73, 3, 85, 8, 142, 150, 3, 68, 138, 69, 72, + 187, 2, 65, 6, 134, 209, 1, 76, 159, 140, 2, 65, 4, 198, 218, 3, 82, 187, + 2, 65, 8, 158, 149, 3, 84, 138, 69, 72, 187, 2, 65, 10, 214, 5, 79, 223, + 214, 3, 65, 2, 11, 69, 2, 223, 222, 1, 80, 18, 174, 1, 65, 72, 6, 76, 79, + 79, 80, 69, 68, 40, 2, 79, 77, 0, 5, 83, 72, 82, 73, 73, 48, 13, 67, 65, + 78, 68, 82, 65, 32, 65, 78, 85, 78, 65, 83, 22, 80, 135, 150, 3, 86, 4, + 26, 86, 191, 149, 3, 78, 2, 21, 3, 65, 71, 82, 2, 231, 220, 1, 65, 2, 17, + 2, 32, 86, 2, 203, 157, 1, 73, 2, 17, 2, 32, 80, 2, 25, 4, 85, 83, 72, + 80, 2, 187, 208, 3, 73, 2, 193, 180, 3, 2, 76, 85, 30, 56, 10, 69, 68, + 73, 67, 32, 84, 79, 78, 69, 32, 35, 79, 4, 226, 164, 1, 65, 235, 4, 83, + 26, 45, 9, 87, 69, 76, 32, 83, 73, 71, 78, 32, 26, 70, 65, 38, 85, 22, + 86, 166, 202, 1, 73, 210, 237, 1, 79, 163, 8, 69, 6, 158, 215, 3, 65, 2, + 73, 3, 85, 5, 251, 214, 3, 85, 8, 11, 79, 8, 33, 6, 67, 65, 76, 73, 67, + 32, 8, 26, 82, 243, 217, 2, 76, 5, 155, 214, 3, 82, 50, 50, 75, 84, 4, + 78, 69, 68, 32, 199, 174, 2, 84, 4, 48, 6, 73, 83, 72, 32, 76, 73, 219, + 195, 3, 69, 2, 11, 82, 2, 147, 206, 2, 65, 44, 238, 1, 65, 80, 6, 66, 76, + 65, 67, 75, 32, 40, 7, 87, 72, 73, 84, 69, 32, 83, 22, 67, 50, 68, 96, 2, + 78, 79, 32, 2, 79, 75, 30, 83, 237, 171, 3, 21, 71, 82, 69, 69, 75, 32, + 83, 77, 65, 76, 76, 32, 76, 69, 84, 84, 69, 82, 32, 73, 79, 4, 26, 77, + 175, 172, 2, 78, 2, 17, 2, 80, 69, 2, 11, 82, 2, 179, 194, 2, 83, 4, 18, + 80, 23, 83, 2, 211, 151, 2, 69, 2, 195, 153, 2, 72, 4, 144, 3, 5, 65, 80, + 73, 84, 65, 171, 121, 79, 6, 30, 65, 33, 3, 73, 71, 73, 2, 11, 71, 2, + 187, 147, 3, 71, 4, 201, 222, 1, 3, 84, 32, 84, 6, 162, 2, 82, 211, 199, + 2, 84, 2, 249, 228, 1, 2, 32, 72, 14, 128, 1, 18, 65, 78, 83, 45, 83, 69, + 82, 73, 70, 32, 67, 65, 80, 73, 84, 65, 76, 32, 38, 69, 32, 3, 77, 65, + 76, 33, 2, 79, 85, 6, 178, 207, 3, 71, 2, 76, 3, 89, 2, 11, 77, 2, 243, + 254, 2, 73, 2, 11, 76, 2, 191, 205, 2, 32, 4, 21, 3, 84, 72, 32, 4, 32, + 2, 69, 65, 1, 2, 87, 69, 2, 53, 11, 83, 84, 32, 80, 79, 73, 78, 84, 73, + 78, 71, 2, 17, 2, 32, 76, 2, 151, 204, 2, 69, 38, 58, 69, 52, 8, 73, 83, + 84, 69, 68, 32, 82, 73, 63, 79, 2, 17, 2, 76, 86, 2, 145, 178, 1, 3, 69, + 32, 80, 2, 17, 2, 71, 72, 2, 141, 109, 6, 84, 87, 65, 82, 68, 83, 34, 30, + 32, 229, 10, 2, 45, 69, 32, 134, 3, 66, 86, 67, 116, 3, 68, 79, 84, 226, + 1, 72, 44, 14, 73, 78, 84, 69, 82, 83, 69, 67, 84, 73, 78, 71, 32, 76, + 92, 6, 74, 79, 73, 78, 69, 68, 64, 8, 76, 79, 71, 73, 67, 65, 76, 32, 98, + 77, 0, 3, 87, 79, 77, 116, 13, 82, 73, 78, 71, 83, 32, 65, 76, 73, 71, + 78, 69, 68, 48, 24, 65, 83, 84, 69, 82, 73, 83, 75, 83, 32, 65, 76, 73, + 71, 78, 69, 68, 32, 86, 69, 82, 84, 73, 67, 35, 83, 2, 29, 5, 85, 84, 84, + 79, 78, 2, 17, 2, 32, 77, 2, 11, 79, 2, 251, 188, 2, 85, 2, 93, 21, 79, + 78, 83, 69, 67, 85, 84, 73, 86, 69, 32, 69, 81, 85, 65, 76, 83, 32, 83, + 73, 71, 2, 207, 137, 3, 78, 6, 18, 32, 55, 83, 4, 22, 76, 131, 1, 80, 2, + 145, 163, 1, 2, 69, 65, 2, 53, 11, 32, 79, 86, 69, 82, 32, 79, 78, 69, + 32, 68, 2, 11, 79, 2, 11, 84, 2, 17, 2, 32, 80, 2, 29, 5, 85, 78, 67, 84, + 85, 2, 251, 244, 2, 65, 2, 11, 69, 2, 11, 65, 2, 227, 188, 2, 82, 4, 17, + 2, 79, 71, 4, 25, 4, 73, 67, 65, 76, 4, 11, 32, 4, 186, 180, 2, 65, 187, + 87, 79, 2, 17, 2, 32, 83, 2, 21, 3, 81, 85, 65, 2, 255, 173, 2, 82, 4, + 30, 79, 13, 3, 65, 78, 68, 2, 11, 82, 2, 11, 32, 2, 11, 79, 2, 11, 80, 2, + 143, 18, 69, 2, 11, 69, 2, 45, 9, 78, 32, 72, 79, 76, 68, 73, 78, 71, 2, + 11, 32, 2, 11, 72, 2, 11, 65, 2, 251, 223, 1, 78, 2, 45, 9, 32, 72, 79, + 82, 73, 90, 79, 78, 84, 2, 11, 65, 2, 175, 158, 3, 76, 2, 49, 10, 80, 69, + 69, 67, 72, 32, 66, 85, 66, 66, 2, 147, 171, 2, 76, 2, 11, 77, 2, 225, + 213, 1, 2, 32, 68, 248, 3, 140, 1, 8, 71, 65, 82, 73, 84, 73, 67, 32, + 152, 6, 7, 77, 66, 82, 69, 76, 76, 65, 166, 1, 78, 158, 8, 80, 246, 166, + 1, 82, 255, 136, 2, 83, 62, 48, 7, 76, 69, 84, 84, 69, 82, 32, 159, 5, + 87, 60, 206, 1, 65, 28, 2, 81, 79, 22, 66, 22, 68, 50, 71, 44, 2, 72, 79, + 22, 75, 34, 76, 32, 2, 82, 65, 22, 83, 74, 84, 74, 89, 22, 90, 234, 185, + 2, 78, 202, 91, 80, 246, 5, 87, 202, 12, 77, 174, 18, 73, 3, 85, 4, 26, + 76, 203, 237, 2, 73, 2, 239, 186, 3, 80, 2, 199, 152, 3, 69, 4, 26, 69, + 139, 177, 2, 72, 2, 151, 152, 3, 76, 4, 230, 141, 2, 72, 209, 140, 1, 2, + 65, 77, 5, 235, 185, 3, 84, 4, 234, 186, 2, 65, 239, 126, 72, 2, 11, 65, + 2, 223, 150, 3, 77, 2, 167, 190, 1, 83, 8, 38, 65, 194, 187, 2, 72, 239, + 90, 83, 4, 190, 164, 3, 68, 239, 13, 77, 6, 38, 72, 162, 158, 3, 69, 175, + 28, 79, 2, 11, 65, 2, 167, 252, 1, 78, 2, 139, 244, 2, 79, 4, 202, 149, + 3, 69, 207, 36, 85, 2, 21, 3, 79, 82, 68, 2, 25, 4, 32, 68, 73, 86, 2, + 235, 149, 1, 73, 7, 11, 32, 4, 84, 4, 79, 78, 32, 71, 45, 13, 87, 73, 84, + 72, 32, 82, 65, 73, 78, 32, 68, 82, 79, 2, 11, 82, 2, 11, 79, 2, 159, + 168, 2, 85, 2, 195, 250, 2, 80, 32, 160, 1, 3, 65, 77, 85, 20, 7, 67, 69, + 82, 84, 65, 73, 78, 34, 68, 62, 73, 245, 5, 18, 77, 65, 82, 82, 73, 69, + 68, 32, 80, 65, 82, 84, 78, 69, 82, 83, 72, 73, 2, 243, 163, 2, 83, 2, + 11, 84, 2, 199, 175, 2, 89, 4, 26, 69, 215, 160, 2, 79, 2, 11, 82, 2, + 219, 154, 3, 84, 22, 60, 2, 79, 78, 234, 3, 84, 106, 86, 177, 58, 3, 67, + 79, 82, 15, 11, 32, 12, 160, 1, 6, 65, 66, 79, 86, 69, 32, 108, 22, 66, + 69, 83, 73, 68, 69, 32, 65, 78, 68, 32, 74, 79, 73, 78, 69, 68, 32, 87, + 73, 84, 72, 41, 5, 87, 73, 84, 72, 32, 4, 52, 9, 66, 65, 82, 32, 65, 66, + 79, 86, 69, 23, 73, 2, 17, 2, 32, 73, 2, 161, 49, 4, 78, 84, 69, 82, 2, + 17, 2, 32, 85, 2, 195, 226, 2, 78, 6, 66, 77, 58, 79, 145, 249, 2, 8, 76, + 79, 71, 73, 67, 65, 76, 32, 2, 11, 73, 2, 11, 78, 2, 11, 85, 2, 139, 171, + 2, 83, 2, 11, 86, 2, 161, 142, 2, 2, 69, 82, 4, 18, 32, 67, 69, 2, 11, + 83, 2, 17, 2, 69, 80, 2, 11, 65, 2, 143, 248, 2, 82, 2, 183, 155, 2, 68, + 2, 57, 12, 69, 82, 83, 65, 76, 32, 82, 69, 67, 89, 67, 76, 2, 17, 2, 73, + 78, 2, 211, 154, 2, 71, 2, 191, 154, 2, 80, 144, 3, 130, 1, 32, 246, 7, + 45, 180, 8, 4, 80, 69, 82, 32, 136, 40, 8, 83, 73, 68, 69, 45, 68, 79, + 87, 21, 6, 87, 65, 82, 68, 83, 32, 38, 140, 1, 5, 65, 82, 82, 79, 87, + 224, 1, 5, 66, 65, 82, 66, 32, 228, 1, 5, 68, 79, 87, 78, 32, 210, 1, 70, + 30, 82, 93, 4, 84, 65, 67, 75, 8, 64, 4, 72, 69, 65, 68, 141, 51, 7, 32, + 84, 72, 82, 79, 85, 71, 7, 11, 32, 4, 112, 22, 66, 69, 84, 87, 69, 69, + 78, 32, 84, 87, 79, 32, 72, 79, 82, 73, 90, 79, 78, 84, 65, 76, 227, 156, + 2, 73, 2, 145, 149, 2, 2, 32, 66, 8, 44, 3, 76, 69, 70, 1, 4, 82, 73, 71, + 72, 4, 57, 12, 84, 32, 68, 79, 87, 78, 32, 66, 65, 82, 66, 32, 4, 44, 3, + 76, 69, 70, 1, 4, 82, 73, 71, 72, 2, 11, 84, 2, 25, 4, 32, 72, 65, 82, 2, + 11, 80, 2, 187, 218, 2, 79, 14, 76, 5, 65, 82, 82, 79, 87, 62, 66, 38, + 68, 18, 83, 250, 58, 87, 219, 9, 84, 5, 37, 7, 32, 87, 73, 84, 72, 32, + 66, 2, 247, 157, 2, 65, 2, 173, 140, 2, 4, 76, 65, 67, 75, 2, 195, 59, + 79, 2, 183, 65, 65, 2, 11, 73, 2, 239, 112, 83, 2, 53, 11, 73, 71, 72, + 84, 32, 68, 73, 65, 71, 79, 78, 2, 189, 128, 1, 4, 65, 76, 32, 69, 5, 29, + 5, 32, 87, 73, 84, 72, 2, 33, 6, 32, 67, 73, 82, 67, 76, 2, 131, 170, 2, + 69, 32, 58, 70, 225, 1, 9, 80, 79, 73, 78, 84, 73, 78, 71, 32, 4, 41, 8, + 65, 67, 73, 78, 71, 32, 83, 78, 4, 65, 14, 65, 75, 69, 32, 72, 69, 65, + 68, 32, 87, 73, 84, 72, 32, 4, 42, 79, 25, 6, 67, 76, 79, 83, 69, 68, 2, + 21, 3, 80, 69, 78, 2, 21, 3, 32, 77, 79, 2, 243, 159, 1, 85, 28, 130, 1, + 65, 86, 69, 62, 70, 20, 8, 77, 73, 76, 73, 84, 65, 82, 89, 26, 82, 88, 6, + 83, 77, 65, 76, 76, 32, 118, 84, 255, 121, 71, 4, 22, 84, 159, 2, 73, 2, + 37, 7, 79, 77, 73, 67, 32, 66, 79, 2, 155, 229, 1, 77, 2, 29, 5, 78, 69, + 82, 71, 89, 2, 193, 180, 1, 2, 32, 87, 2, 227, 161, 3, 82, 2, 129, 1, 2, + 32, 65, 8, 64, 5, 79, 67, 75, 69, 84, 118, 69, 214, 129, 1, 65, 235, 29, + 73, 2, 195, 204, 2, 32, 4, 18, 65, 67, 82, 2, 11, 73, 2, 11, 82, 2, 17, + 2, 80, 76, 2, 207, 133, 2, 65, 2, 11, 69, 2, 215, 249, 1, 68, 4, 37, 7, + 82, 73, 65, 78, 71, 76, 69, 4, 11, 32, 4, 11, 87, 4, 25, 4, 73, 84, 72, + 32, 4, 18, 76, 27, 82, 2, 11, 69, 2, 35, 70, 2, 21, 3, 73, 71, 72, 2, 11, + 84, 2, 17, 2, 32, 72, 2, 21, 3, 65, 76, 70, 2, 17, 2, 32, 66, 2, 11, 76, + 2, 207, 236, 1, 65, 200, 1, 192, 1, 4, 65, 78, 68, 32, 210, 2, 66, 74, + 67, 74, 70, 36, 5, 72, 65, 76, 70, 32, 200, 4, 5, 76, 69, 70, 84, 32, + 254, 5, 77, 150, 2, 79, 60, 6, 82, 73, 71, 72, 84, 32, 238, 17, 83, 67, + 84, 8, 34, 76, 53, 4, 82, 73, 71, 72, 6, 48, 2, 69, 70, 173, 1, 5, 79, + 87, 69, 82, 32, 2, 53, 11, 84, 32, 65, 78, 68, 32, 76, 79, 87, 69, 82, 2, + 205, 35, 25, 32, 84, 82, 73, 65, 78, 71, 85, 76, 65, 82, 32, 84, 72, 82, + 69, 69, 32, 81, 85, 65, 82, 84, 69, 82, 4, 28, 2, 84, 82, 235, 39, 79, 2, + 225, 4, 7, 73, 65, 78, 71, 85, 76, 65, 2, 17, 2, 76, 65, 2, 17, 2, 68, + 69, 2, 249, 224, 1, 3, 32, 83, 67, 10, 33, 6, 69, 78, 84, 82, 69, 32, 10, + 174, 12, 76, 22, 82, 255, 21, 79, 2, 17, 2, 73, 86, 2, 183, 31, 69, 20, + 140, 1, 5, 66, 76, 79, 67, 75, 110, 72, 32, 8, 73, 78, 86, 69, 82, 83, + 69, 32, 198, 1, 86, 174, 26, 77, 130, 3, 76, 22, 82, 191, 163, 1, 67, 5, + 229, 28, 23, 32, 65, 78, 68, 32, 76, 79, 87, 69, 82, 32, 72, 65, 76, 70, + 32, 73, 78, 86, 69, 82, 83, 69, 2, 225, 18, 4, 69, 65, 86, 89, 4, 100, + 21, 77, 69, 68, 73, 85, 77, 32, 83, 72, 65, 68, 69, 32, 65, 78, 68, 32, + 76, 79, 87, 69, 51, 87, 2, 11, 82, 2, 189, 35, 5, 32, 72, 65, 76, 70, 2, + 21, 3, 72, 73, 84, 2, 247, 192, 1, 69, 2, 29, 5, 69, 82, 84, 73, 67, 2, + 189, 115, 14, 65, 76, 32, 76, 73, 78, 69, 32, 87, 73, 84, 72, 32, 84, 62, + 50, 66, 238, 3, 67, 54, 79, 74, 84, 183, 13, 81, 22, 65, 14, 76, 79, 67, + 75, 32, 68, 73, 65, 71, 79, 78, 65, 76, 32, 22, 144, 1, 6, 76, 79, 87, + 69, 82, 32, 233, 8, 24, 85, 80, 80, 69, 82, 32, 77, 73, 68, 68, 76, 69, + 32, 76, 69, 70, 84, 32, 84, 79, 32, 85, 80, 80, 18, 176, 1, 10, 67, 69, + 78, 84, 82, 69, 32, 84, 79, 32, 36, 8, 76, 69, 70, 84, 32, 84, 79, 32, + 229, 10, 18, 77, 73, 68, 68, 76, 69, 32, 76, 69, 70, 84, 32, 84, 79, 32, + 85, 80, 80, 6, 70, 76, 253, 8, 3, 85, 80, 80, 6, 34, 76, 213, 9, 3, 85, + 80, 80, 2, 149, 10, 2, 79, 87, 2, 29, 5, 79, 82, 78, 69, 82, 2, 219, 233, + 1, 32, 4, 182, 12, 78, 53, 12, 82, 32, 76, 79, 87, 69, 82, 32, 82, 73, + 71, 72, 8, 34, 79, 170, 19, 82, 155, 1, 87, 2, 241, 18, 11, 32, 76, 79, + 87, 69, 82, 32, 82, 73, 71, 72, 12, 33, 6, 73, 68, 68, 76, 69, 32, 12, + 52, 7, 67, 69, 78, 84, 82, 69, 32, 74, 76, 23, 82, 4, 44, 3, 76, 69, 70, + 1, 4, 82, 73, 71, 72, 2, 117, 3, 84, 32, 79, 4, 41, 2, 69, 70, 4, 21, 3, + 73, 71, 72, 4, 17, 2, 84, 32, 4, 30, 79, 129, 18, 2, 84, 87, 2, 139, 9, + 78, 4, 11, 78, 4, 17, 2, 69, 32, 4, 154, 21, 81, 147, 4, 69, 68, 84, 2, + 66, 76, 186, 6, 68, 114, 79, 222, 1, 80, 42, 81, 224, 4, 2, 83, 72, 51, + 84, 22, 61, 13, 79, 67, 75, 32, 68, 73, 65, 71, 79, 78, 65, 76, 32, 22, + 140, 1, 24, 76, 79, 87, 69, 82, 32, 77, 73, 68, 68, 76, 69, 32, 76, 69, + 70, 84, 32, 84, 79, 32, 76, 79, 87, 57, 6, 85, 80, 80, 69, 82, 32, 4, 21, + 3, 69, 82, 32, 4, 246, 3, 67, 219, 218, 2, 82, 18, 176, 1, 10, 67, 69, + 78, 84, 82, 69, 32, 84, 79, 32, 92, 8, 76, 69, 70, 84, 32, 84, 79, 32, + 141, 1, 18, 77, 73, 68, 68, 76, 69, 32, 76, 69, 70, 84, 32, 84, 79, 32, + 76, 79, 87, 6, 32, 3, 76, 79, 87, 139, 1, 85, 4, 21, 3, 69, 82, 32, 4, + 142, 2, 77, 179, 218, 2, 82, 6, 28, 3, 76, 79, 87, 51, 85, 4, 21, 3, 69, + 82, 32, 4, 142, 1, 67, 43, 77, 2, 17, 2, 80, 80, 2, 17, 2, 69, 82, 2, + 117, 2, 32, 77, 6, 21, 3, 69, 82, 32, 6, 34, 67, 42, 77, 179, 218, 2, 82, + 2, 11, 69, 2, 221, 226, 1, 2, 78, 84, 2, 25, 4, 73, 68, 68, 76, 2, 195, + 177, 1, 69, 2, 57, 12, 82, 79, 80, 45, 83, 72, 65, 68, 79, 87, 69, 68, 2, + 17, 2, 32, 87, 2, 149, 192, 1, 3, 72, 73, 84, 4, 62, 78, 53, 11, 82, 32, + 76, 79, 87, 69, 82, 32, 76, 69, 70, 2, 229, 16, 9, 69, 32, 83, 73, 88, + 84, 69, 69, 78, 2, 69, 15, 84, 32, 67, 85, 82, 76, 89, 32, 66, 82, 65, + 67, 75, 69, 84, 2, 11, 32, 2, 175, 177, 2, 83, 2, 11, 69, 2, 165, 162, 1, + 2, 78, 67, 26, 17, 2, 85, 65, 26, 44, 6, 68, 82, 65, 78, 84, 32, 255, 3, + 82, 24, 90, 67, 112, 10, 70, 65, 67, 69, 32, 87, 73, 84, 72, 32, 114, 77, + 76, 2, 83, 84, 67, 84, 14, 68, 7, 73, 82, 67, 85, 76, 65, 82, 197, 168, + 1, 4, 72, 69, 83, 83, 2, 17, 2, 32, 65, 2, 207, 213, 1, 82, 4, 34, 67, + 45, 4, 79, 80, 69, 78, 2, 21, 3, 76, 79, 83, 2, 17, 2, 69, 68, 2, 245, + 232, 1, 3, 32, 69, 89, 2, 25, 4, 73, 67, 82, 79, 2, 11, 67, 2, 165, 196, + 1, 4, 79, 77, 80, 85, 2, 41, 8, 65, 78, 68, 73, 78, 71, 32, 75, 2, 147, + 211, 2, 78, 2, 21, 3, 69, 76, 69, 2, 11, 86, 2, 183, 90, 73, 2, 233, 168, + 1, 3, 84, 69, 82, 4, 141, 102, 8, 65, 68, 79, 87, 69, 68, 32, 87, 8, 30, + 79, 106, 82, 155, 1, 87, 2, 49, 10, 32, 76, 79, 87, 69, 82, 32, 76, 69, + 70, 2, 11, 84, 2, 11, 32, 2, 11, 70, 2, 171, 113, 73, 4, 25, 4, 73, 65, + 78, 71, 4, 40, 4, 85, 76, 65, 82, 171, 228, 2, 76, 2, 17, 2, 32, 77, 2, + 41, 8, 69, 68, 73, 85, 77, 32, 83, 72, 2, 199, 103, 65, 2, 21, 3, 69, 76, + 70, 2, 11, 84, 2, 215, 165, 1, 72, 2, 17, 2, 69, 86, 2, 17, 2, 69, 78, 2, + 145, 1, 2, 32, 69, 10, 64, 5, 72, 82, 69, 69, 32, 193, 1, 6, 82, 73, 65, + 78, 71, 85, 8, 54, 69, 49, 9, 81, 85, 65, 82, 84, 69, 82, 83, 32, 2, 29, + 5, 73, 71, 72, 84, 72, 2, 243, 5, 83, 6, 30, 76, 22, 82, 203, 5, 66, 2, + 41, 2, 69, 70, 2, 21, 3, 73, 71, 72, 2, 43, 84, 2, 17, 2, 76, 65, 2, 11, + 82, 2, 17, 2, 32, 79, 2, 25, 4, 78, 69, 32, 81, 2, 185, 4, 6, 85, 65, 82, + 84, 69, 82, 2, 151, 185, 2, 78, 128, 1, 154, 1, 65, 184, 6, 2, 66, 76, + 150, 1, 68, 50, 70, 82, 72, 150, 4, 67, 46, 81, 42, 82, 22, 83, 102, 84, + 146, 7, 80, 173, 3, 6, 87, 72, 73, 84, 69, 32, 32, 34, 78, 33, 4, 82, 82, + 79, 87, 2, 11, 67, 2, 147, 176, 2, 79, 31, 11, 32, 28, 134, 1, 65, 192, + 1, 14, 76, 69, 70, 84, 87, 65, 82, 68, 83, 32, 79, 70, 32, 68, 32, 5, 87, + 73, 84, 72, 32, 210, 8, 70, 179, 5, 84, 2, 41, 8, 78, 68, 32, 82, 73, 71, + 72, 84, 2, 17, 2, 32, 79, 2, 25, 4, 78, 69, 32, 69, 2, 21, 3, 73, 71, 72, + 2, 17, 2, 84, 72, 2, 11, 32, 2, 11, 66, 2, 11, 76, 2, 251, 191, 1, 79, 2, + 145, 208, 1, 3, 79, 87, 78, 20, 74, 68, 58, 76, 42, 77, 38, 78, 58, 83, + 66, 69, 250, 12, 84, 135, 58, 72, 2, 21, 3, 79, 85, 66, 2, 11, 76, 2, + 199, 192, 2, 69, 2, 11, 65, 2, 253, 22, 3, 82, 71, 69, 2, 225, 22, 5, 69, + 68, 73, 85, 77, 2, 25, 4, 79, 84, 67, 72, 2, 11, 69, 2, 151, 56, 68, 4, + 11, 77, 4, 25, 4, 65, 76, 76, 32, 4, 22, 69, 223, 21, 84, 2, 129, 22, 10, + 81, 85, 73, 76, 65, 84, 69, 82, 65, 76, 4, 25, 4, 65, 67, 75, 32, 4, 26, + 67, 215, 209, 1, 65, 2, 25, 4, 73, 82, 67, 76, 2, 25, 4, 69, 68, 32, 87, + 2, 11, 72, 2, 237, 14, 2, 73, 84, 4, 22, 79, 195, 13, 65, 2, 177, 14, 2, + 85, 66, 2, 11, 73, 2, 37, 7, 78, 71, 69, 82, 45, 80, 79, 2, 249, 207, 1, + 2, 83, 84, 20, 88, 17, 65, 82, 80, 79, 79, 78, 32, 87, 73, 84, 72, 32, + 66, 65, 82, 66, 32, 131, 3, 69, 16, 56, 4, 76, 69, 70, 84, 245, 1, 5, 82, + 73, 71, 72, 84, 10, 22, 32, 239, 9, 87, 8, 60, 7, 66, 69, 83, 73, 68, 69, + 32, 206, 1, 70, 179, 5, 84, 4, 40, 4, 68, 79, 87, 78, 1, 2, 85, 80, 2, + 249, 150, 1, 23, 87, 65, 82, 68, 83, 32, 72, 65, 82, 80, 79, 79, 78, 32, + 87, 73, 84, 72, 32, 66, 65, 82, 66, 6, 22, 32, 251, 7, 87, 4, 22, 70, + 179, 5, 84, 2, 165, 197, 1, 3, 82, 79, 77, 4, 25, 4, 65, 86, 89, 32, 4, + 26, 67, 231, 203, 1, 65, 2, 189, 10, 7, 79, 77, 80, 82, 69, 83, 83, 2, + 137, 9, 6, 85, 65, 68, 82, 85, 80, 2, 251, 148, 2, 79, 4, 30, 65, 53, 3, + 81, 85, 65, 2, 193, 202, 1, 8, 78, 83, 45, 83, 69, 82, 73, 70, 2, 131, 9, + 82, 36, 36, 2, 82, 73, 229, 7, 2, 87, 79, 30, 40, 5, 65, 78, 71, 76, 69, + 155, 7, 80, 28, 52, 8, 45, 72, 69, 65, 68, 69, 68, 32, 219, 12, 32, 26, + 48, 5, 65, 82, 82, 79, 87, 174, 5, 68, 39, 80, 23, 11, 32, 20, 92, 17, + 76, 69, 70, 84, 87, 65, 82, 68, 83, 32, 79, 70, 32, 68, 79, 87, 78, 98, + 84, 23, 87, 2, 37, 7, 87, 65, 82, 68, 83, 32, 84, 2, 37, 7, 82, 73, 65, + 78, 71, 76, 69, 2, 219, 5, 45, 2, 247, 191, 1, 79, 16, 25, 4, 73, 84, 72, + 32, 16, 114, 66, 28, 6, 76, 79, 78, 71, 32, 84, 130, 1, 77, 34, 78, 34, + 86, 34, 72, 145, 56, 6, 68, 79, 85, 66, 76, 69, 2, 149, 2, 3, 79, 76, 68, + 4, 17, 2, 73, 80, 4, 11, 32, 4, 34, 76, 21, 4, 82, 73, 71, 72, 2, 17, 2, + 69, 70, 2, 11, 84, 2, 11, 87, 2, 219, 126, 65, 2, 121, 5, 69, 68, 73, 85, + 77, 2, 89, 5, 65, 82, 82, 79, 87, 2, 29, 5, 69, 82, 89, 32, 72, 2, 25, 4, + 69, 65, 86, 89, 2, 173, 181, 2, 4, 32, 83, 72, 65, 2, 11, 65, 2, 249, 1, + 2, 83, 72, 2, 11, 65, 2, 25, 4, 73, 82, 69, 68, 2, 29, 5, 32, 65, 82, 82, + 79, 2, 163, 161, 2, 87, 2, 11, 76, 2, 135, 194, 1, 69, 6, 74, 32, 61, 14, + 45, 72, 69, 65, 68, 69, 68, 32, 65, 82, 82, 79, 87, 32, 2, 25, 4, 72, 69, + 65, 68, 2, 11, 69, 2, 255, 192, 1, 68, 4, 42, 87, 177, 136, 1, 4, 70, 82, + 79, 77, 2, 33, 6, 73, 84, 72, 32, 84, 82, 2, 61, 13, 73, 65, 78, 71, 76, + 69, 32, 65, 82, 82, 79, 87, 72, 2, 253, 121, 2, 69, 65, 18, 88, 5, 65, + 82, 82, 79, 87, 149, 3, 12, 68, 79, 85, 66, 76, 69, 32, 65, 82, 82, 79, + 87, 15, 11, 32, 12, 108, 11, 79, 78, 32, 80, 69, 68, 69, 83, 84, 65, 76, + 106, 87, 165, 181, 1, 8, 70, 82, 79, 77, 32, 66, 65, 82, 7, 33, 6, 32, + 87, 73, 84, 72, 32, 4, 26, 86, 247, 181, 1, 72, 2, 153, 182, 1, 5, 69, + 82, 84, 73, 67, 2, 29, 5, 73, 84, 72, 73, 78, 2, 17, 2, 32, 84, 2, 37, 7, + 82, 73, 65, 78, 71, 76, 69, 2, 11, 32, 2, 11, 65, 2, 25, 4, 82, 82, 79, + 87, 2, 11, 72, 2, 147, 146, 2, 69, 5, 45, 9, 32, 79, 78, 32, 80, 69, 68, + 69, 83, 2, 159, 204, 1, 84, 244, 15, 86, 65, 254, 16, 83, 174, 3, 69, + 194, 46, 73, 202, 8, 79, 134, 2, 84, 21, 2, 85, 76, 218, 8, 116, 2, 73, + 32, 128, 16, 17, 82, 73, 65, 84, 73, 79, 78, 32, 83, 69, 76, 69, 67, 84, + 79, 82, 45, 233, 45, 2, 77, 80, 216, 4, 54, 67, 34, 70, 82, 81, 40, 2, + 83, 89, 183, 96, 68, 2, 11, 79, 2, 183, 180, 2, 77, 2, 11, 85, 2, 11, 76, + 2, 11, 76, 2, 11, 32, 2, 11, 83, 2, 227, 219, 1, 84, 2, 17, 2, 85, 69, 2, + 167, 235, 1, 83, 190, 4, 68, 7, 76, 76, 65, 66, 76, 69, 32, 197, 11, 5, + 77, 66, 79, 76, 32, 164, 4, 214, 1, 68, 70, 66, 2, 83, 2, 84, 2, 90, 70, + 71, 122, 72, 82, 75, 134, 1, 76, 130, 1, 77, 86, 78, 134, 3, 67, 2, 70, + 2, 74, 2, 80, 2, 82, 2, 86, 2, 89, 78, 87, 50, 69, 34, 79, 226, 74, 65, + 2, 73, 3, 85, 42, 66, 72, 162, 8, 79, 146, 137, 2, 69, 162, 64, 65, 2, + 73, 3, 85, 28, 230, 7, 72, 58, 79, 146, 137, 2, 69, 162, 64, 65, 2, 73, + 3, 85, 34, 62, 66, 202, 6, 69, 86, 79, 178, 201, 2, 65, 2, 73, 3, 85, 18, + 106, 79, 222, 5, 69, 134, 202, 2, 65, 2, 73, 3, 85, 24, 50, 79, 222, 5, + 69, 154, 76, 65, 2, 73, 3, 85, 7, 222, 207, 2, 78, 3, 79, 34, 70, 80, + 206, 5, 79, 198, 75, 65, 206, 189, 1, 69, 162, 64, 73, 3, 85, 18, 246, 4, + 69, 86, 79, 198, 75, 65, 238, 253, 1, 73, 3, 85, 16, 54, 69, 218, 4, 79, + 178, 201, 2, 65, 2, 73, 3, 85, 7, 26, 78, 239, 205, 2, 69, 2, 21, 3, 71, + 84, 72, 2, 219, 143, 2, 69, 42, 214, 3, 66, 0, 2, 71, 66, 58, 79, 146, + 137, 2, 69, 162, 64, 65, 2, 73, 3, 85, 90, 90, 68, 174, 1, 71, 126, 74, + 2, 89, 58, 79, 146, 137, 2, 69, 162, 64, 65, 2, 73, 3, 85, 24, 54, 79, + 186, 139, 2, 69, 162, 64, 65, 2, 73, 3, 85, 15, 36, 3, 76, 69, 32, 179, + 203, 2, 79, 10, 54, 83, 134, 172, 2, 68, 190, 28, 70, 2, 75, 3, 77, 2, + 131, 172, 2, 79, 25, 42, 71, 218, 250, 1, 65, 2, 69, 3, 79, 16, 50, 69, + 86, 79, 178, 201, 2, 65, 2, 73, 3, 85, 7, 130, 202, 2, 69, 3, 78, 14, 54, + 79, 146, 137, 2, 69, 162, 64, 65, 2, 73, 3, 85, 5, 175, 201, 2, 79, 28, + 46, 69, 34, 79, 226, 74, 65, 2, 73, 3, 85, 9, 254, 74, 69, 239, 253, 1, + 78, 9, 222, 74, 79, 239, 253, 1, 78, 26, 66, 68, 62, 70, 30, 74, 22, 75, + 50, 78, 22, 84, 231, 237, 1, 66, 6, 26, 79, 215, 246, 1, 65, 4, 210, 246, + 1, 79, 135, 50, 45, 4, 74, 69, 171, 196, 2, 65, 2, 151, 246, 1, 79, 4, + 26, 69, 235, 245, 1, 85, 2, 231, 245, 1, 69, 2, 243, 178, 2, 73, 6, 190, + 245, 1, 73, 2, 79, 195, 78, 65, 128, 4, 74, 49, 94, 50, 98, 51, 2, 52, 2, + 53, 2, 54, 2, 55, 2, 56, 3, 57, 223, 1, 182, 1, 48, 2, 49, 2, 50, 2, 51, + 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 137, 1, 90, 48, 2, 49, 2, 50, + 2, 51, 2, 52, 94, 53, 170, 195, 2, 54, 2, 55, 2, 56, 3, 57, 23, 130, 196, + 2, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 2, 54, 2, 55, 2, 56, 3, 57, 17, + 166, 195, 2, 48, 2, 49, 2, 50, 2, 51, 2, 52, 2, 53, 3, 54, 196, 1, 128, + 1, 4, 68, 73, 67, 32, 190, 19, 82, 152, 25, 2, 83, 84, 201, 131, 1, 13, + 67, 84, 79, 82, 32, 79, 82, 32, 67, 82, 79, 83, 83, 86, 60, 5, 83, 73, + 71, 78, 32, 137, 10, 5, 84, 79, 78, 69, 32, 48, 218, 2, 65, 216, 1, 17, + 68, 79, 85, 66, 76, 69, 32, 65, 78, 85, 83, 86, 65, 82, 65, 32, 65, 98, + 74, 52, 6, 78, 73, 72, 83, 72, 86, 22, 82, 240, 1, 8, 72, 69, 88, 73, 70, + 79, 82, 77, 22, 76, 52, 4, 84, 73, 82, 89, 22, 85, 60, 8, 86, 73, 83, 65, + 82, 71, 65, 32, 221, 6, 17, 89, 65, 74, 85, 82, 86, 69, 68, 73, 67, 32, + 77, 73, 68, 76, 73, 78, 14, 76, 8, 78, 85, 83, 86, 65, 82, 65, 32, 212, + 1, 3, 84, 73, 75, 151, 2, 82, 10, 134, 1, 65, 24, 4, 66, 65, 72, 73, 24, + 9, 85, 66, 72, 65, 89, 65, 84, 79, 32, 201, 4, 10, 86, 65, 77, 65, 71, + 79, 77, 85, 75, 72, 2, 21, 3, 78, 84, 65, 2, 21, 3, 82, 71, 79, 2, 11, + 77, 2, 147, 9, 85, 2, 175, 249, 1, 82, 2, 33, 6, 73, 72, 86, 65, 77, 85, + 2, 151, 3, 76, 2, 143, 185, 2, 65, 8, 144, 1, 15, 69, 86, 69, 82, 83, 69, + 68, 32, 86, 73, 83, 65, 82, 71, 65, 36, 9, 79, 84, 65, 84, 69, 68, 32, + 65, 82, 57, 5, 84, 72, 65, 78, 71, 4, 11, 32, 4, 202, 6, 65, 23, 85, 2, + 25, 4, 68, 72, 65, 86, 2, 141, 247, 1, 2, 73, 83, 2, 17, 2, 32, 76, 2, + 21, 3, 79, 78, 71, 2, 169, 244, 1, 2, 32, 65, 2, 239, 181, 2, 65, 2, 37, + 7, 80, 65, 68, 72, 77, 65, 78, 2, 239, 181, 2, 73, 10, 40, 3, 65, 78, 85, + 2, 85, 163, 9, 83, 4, 25, 4, 68, 65, 84, 84, 4, 11, 65, 5, 25, 4, 32, 87, + 73, 84, 2, 11, 72, 2, 11, 32, 2, 11, 84, 2, 211, 87, 65, 38, 128, 2, 5, + 67, 65, 78, 68, 82, 16, 2, 68, 79, 96, 2, 75, 65, 136, 1, 4, 80, 82, 69, + 78, 16, 2, 82, 73, 106, 84, 164, 1, 11, 89, 65, 74, 85, 82, 86, 69, 68, + 73, 67, 32, 180, 1, 12, 65, 84, 72, 65, 82, 86, 65, 86, 69, 68, 73, 67, + 253, 234, 1, 2, 83, 72, 4, 251, 18, 65, 6, 40, 5, 85, 66, 76, 69, 32, + 191, 3, 84, 4, 22, 82, 211, 5, 83, 2, 11, 73, 2, 251, 1, 78, 4, 56, 3, + 82, 83, 72, 17, 7, 84, 72, 65, 75, 65, 32, 65, 2, 203, 117, 65, 2, 17, 2, + 78, 85, 2, 17, 2, 68, 65, 2, 223, 142, 2, 84, 2, 239, 53, 75, 4, 82, 78, + 237, 2, 15, 71, 86, 69, 68, 73, 67, 32, 75, 65, 83, 72, 77, 73, 82, 73, + 2, 175, 181, 1, 71, 6, 42, 72, 24, 4, 82, 73, 80, 76, 19, 87, 2, 49, 3, + 82, 69, 69, 2, 219, 2, 69, 2, 11, 79, 2, 25, 4, 32, 68, 79, 84, 2, 11, + 83, 2, 11, 32, 2, 159, 15, 66, 8, 176, 1, 10, 65, 71, 71, 82, 65, 86, 65, + 84, 69, 68, 22, 73, 105, 27, 75, 65, 84, 72, 65, 75, 65, 32, 73, 78, 68, + 69, 80, 69, 78, 68, 69, 78, 84, 32, 83, 86, 65, 82, 73, 84, 65, 2, 17, 2, + 32, 73, 2, 49, 10, 78, 68, 69, 80, 69, 78, 68, 69, 78, 84, 2, 17, 2, 32, + 83, 2, 145, 138, 2, 3, 86, 65, 82, 5, 241, 10, 7, 32, 83, 67, 72, 82, 79, + 69, 104, 62, 83, 16, 6, 84, 73, 67, 65, 76, 32, 173, 17, 2, 89, 32, 2, + 223, 89, 73, 76, 200, 2, 4, 66, 65, 82, 32, 174, 3, 67, 42, 69, 62, 70, + 38, 71, 32, 11, 73, 68, 69, 79, 71, 82, 65, 80, 72, 73, 67, 48, 12, 75, + 65, 78, 65, 32, 82, 69, 80, 69, 65, 84, 32, 254, 1, 76, 186, 3, 77, 88, + 17, 79, 78, 69, 32, 69, 73, 71, 72, 84, 72, 32, 66, 76, 79, 67, 75, 45, + 62, 82, 158, 1, 84, 218, 1, 90, 237, 73, 3, 83, 73, 88, 8, 108, 6, 66, + 69, 83, 73, 68, 69, 64, 8, 68, 79, 85, 66, 76, 69, 32, 76, 20, 4, 84, 82, + 73, 80, 143, 1, 87, 2, 17, 2, 32, 82, 2, 21, 3, 73, 71, 72, 2, 223, 130, + 1, 84, 2, 69, 2, 69, 70, 2, 25, 4, 76, 69, 32, 82, 2, 21, 3, 73, 71, 72, + 2, 11, 84, 2, 29, 5, 32, 84, 85, 82, 78, 2, 11, 83, 2, 11, 84, 2, 139, + 130, 1, 73, 2, 21, 3, 73, 84, 72, 2, 17, 2, 32, 72, 2, 221, 247, 1, 7, + 79, 82, 73, 90, 79, 78, 84, 2, 225, 238, 1, 5, 65, 80, 65, 67, 73, 2, 25, + 4, 76, 76, 73, 80, 2, 11, 83, 2, 171, 233, 1, 73, 2, 11, 79, 2, 141, 84, + 2, 85, 82, 2, 177, 159, 1, 3, 79, 45, 75, 2, 17, 2, 32, 73, 2, 217, 188, + 1, 2, 84, 69, 10, 120, 4, 77, 65, 82, 75, 45, 22, 87, 73, 84, 72, 32, 86, + 79, 73, 67, 69, 68, 32, 83, 79, 85, 78, 68, 32, 77, 65, 82, 75, 7, 11, + 32, 4, 50, 85, 21, 3, 76, 79, 87, 5, 17, 2, 32, 85, 2, 17, 2, 80, 80, 2, + 17, 2, 69, 82, 2, 133, 30, 2, 32, 72, 16, 30, 65, 33, 3, 73, 78, 69, 2, + 11, 68, 2, 235, 229, 1, 68, 15, 11, 32, 12, 38, 69, 49, 5, 87, 73, 84, + 72, 32, 2, 25, 4, 88, 84, 69, 78, 2, 175, 210, 1, 83, 10, 60, 5, 67, 73, + 82, 67, 76, 86, 70, 26, 84, 215, 141, 1, 77, 4, 11, 69, 4, 11, 32, 4, 26, + 66, 131, 165, 1, 65, 2, 11, 69, 2, 183, 133, 1, 76, 2, 57, 3, 79, 85, 82, + 2, 11, 72, 2, 21, 3, 82, 69, 69, 2, 45, 9, 32, 84, 73, 67, 75, 32, 77, + 65, 82, 2, 131, 227, 1, 75, 2, 65, 14, 65, 76, 69, 32, 87, 73, 84, 72, + 32, 83, 84, 82, 79, 75, 2, 135, 153, 1, 69, 12, 242, 159, 2, 50, 2, 51, + 2, 52, 2, 53, 2, 54, 3, 55, 4, 42, 65, 57, 6, 69, 83, 73, 83, 84, 79, 2, + 25, 4, 67, 73, 78, 71, 2, 11, 32, 2, 167, 123, 67, 2, 21, 3, 82, 32, 83, + 2, 11, 69, 2, 251, 218, 1, 71, 10, 32, 2, 65, 66, 106, 73, 19, 82, 6, 18, + 32, 35, 85, 2, 11, 75, 2, 143, 140, 2, 69, 4, 33, 6, 76, 65, 84, 73, 79, + 78, 5, 239, 46, 32, 2, 155, 10, 76, 2, 29, 5, 65, 70, 70, 73, 67, 2, 141, + 242, 1, 2, 32, 76, 2, 177, 108, 4, 73, 71, 90, 65, 26, 80, 6, 72, 69, 65, + 86, 89, 32, 176, 5, 3, 77, 85, 67, 229, 10, 3, 66, 79, 76, 20, 62, 69, + 190, 1, 70, 38, 82, 82, 83, 246, 1, 87, 203, 11, 71, 4, 25, 4, 73, 71, + 72, 84, 4, 11, 32, 4, 22, 80, 223, 2, 83, 2, 25, 4, 79, 73, 78, 84, 2, + 17, 2, 69, 68, 2, 17, 2, 32, 66, 2, 25, 4, 76, 65, 67, 75, 2, 11, 32, 2, + 151, 81, 83, 2, 11, 73, 2, 185, 1, 2, 86, 69, 2, 11, 69, 2, 29, 5, 86, + 69, 82, 83, 69, 2, 17, 2, 32, 83, 2, 231, 1, 79, 6, 30, 65, 42, 73, 147, + 1, 79, 2, 11, 76, 2, 11, 84, 2, 235, 117, 73, 2, 11, 88, 2, 11, 32, 2, + 11, 83, 2, 21, 3, 80, 79, 75, 2, 17, 2, 69, 68, 2, 11, 32, 2, 11, 65, 2, + 11, 83, 2, 149, 80, 3, 84, 69, 82, 2, 161, 14, 3, 76, 73, 68, 4, 21, 3, + 72, 73, 84, 4, 11, 69, 4, 11, 32, 4, 162, 66, 67, 187, 49, 83, 4, 11, 72, + 4, 11, 32, 4, 18, 71, 39, 76, 2, 69, 6, 82, 69, 65, 84, 69, 82, 2, 11, + 69, 2, 11, 83, 2, 11, 83, 2, 17, 2, 45, 84, 2, 251, 72, 72, 4, 11, 65, 5, + 11, 32, 2, 11, 70, 2, 21, 3, 79, 82, 77, 2, 17, 2, 32, 84, 2, 195, 245, + 1, 87, 162, 1, 156, 1, 9, 66, 82, 65, 84, 73, 79, 78, 32, 77, 32, 4, 67, + 84, 79, 82, 36, 3, 68, 69, 79, 126, 69, 222, 1, 79, 22, 82, 21, 7, 84, + 72, 75, 85, 81, 73, 32, 2, 11, 79, 2, 159, 252, 1, 68, 4, 170, 130, 1, + 89, 255, 141, 1, 73, 6, 30, 32, 77, 3, 67, 65, 83, 4, 18, 67, 43, 71, 2, + 17, 2, 65, 77, 2, 179, 205, 1, 69, 2, 239, 119, 65, 2, 167, 72, 83, 6, 168, 1, 31, 84, 78, 65, 77, 69, 83, 69, 32, 65, 76, 84, 69, 82, 78, 65, - 84, 69, 32, 82, 69, 65, 68, 73, 78, 71, 32, 77, 65, 82, 75, 32, 189, 108, - 5, 87, 68, 65, 84, 65, 4, 26, 78, 219, 138, 2, 67, 2, 139, 129, 1, 72, 2, - 167, 141, 1, 76, 2, 243, 237, 1, 71, 140, 1, 56, 6, 67, 65, 80, 73, 84, + 84, 69, 32, 82, 69, 65, 68, 73, 78, 71, 32, 77, 65, 82, 75, 32, 161, 109, + 5, 87, 68, 65, 84, 65, 4, 26, 78, 195, 141, 2, 67, 2, 215, 129, 1, 72, 2, + 143, 144, 1, 76, 2, 219, 240, 1, 71, 140, 1, 56, 6, 67, 65, 80, 73, 84, 65, 1, 4, 83, 77, 65, 76, 70, 45, 9, 76, 32, 76, 69, 84, 84, 69, 82, 32, - 70, 230, 1, 66, 34, 69, 22, 73, 22, 76, 34, 78, 246, 136, 1, 67, 2, 68, + 70, 230, 1, 66, 34, 69, 22, 73, 22, 76, 34, 78, 222, 139, 1, 67, 2, 68, 2, 83, 2, 84, 226, 56, 72, 238, 48, 70, 2, 74, 2, 77, 2, 80, 2, 82, 2, 86, 2, 88, 2, 90, 158, 20, 71, 2, 75, 2, 81, 186, 2, 65, 2, 79, 2, 85, 3, - 89, 4, 166, 243, 1, 66, 215, 22, 69, 5, 219, 137, 2, 73, 5, 243, 242, 1, - 74, 4, 250, 134, 2, 76, 187, 2, 65, 4, 190, 242, 1, 74, 215, 22, 69, 8, - 28, 3, 73, 68, 69, 59, 76, 2, 21, 3, 68, 32, 71, 2, 141, 71, 4, 82, 69, - 69, 75, 6, 46, 67, 20, 3, 76, 69, 89, 41, 2, 85, 77, 2, 219, 222, 1, 65, - 2, 11, 66, 2, 11, 65, 2, 139, 126, 76, 2, 11, 69, 2, 17, 2, 32, 73, 2, - 193, 125, 4, 78, 84, 69, 71, 5, 239, 134, 2, 83, 40, 70, 67, 45, 13, 71, + 89, 4, 142, 246, 1, 66, 215, 22, 69, 5, 195, 140, 2, 73, 5, 219, 245, 1, + 74, 4, 226, 137, 2, 76, 187, 2, 65, 4, 166, 245, 1, 74, 215, 22, 69, 8, + 28, 3, 73, 68, 69, 59, 76, 2, 21, 3, 68, 32, 71, 2, 225, 71, 4, 82, 69, + 69, 75, 6, 46, 67, 20, 3, 76, 69, 89, 41, 2, 85, 77, 2, 195, 225, 1, 65, + 2, 11, 66, 2, 11, 65, 2, 215, 126, 76, 2, 11, 69, 2, 17, 2, 32, 73, 2, + 141, 126, 4, 78, 84, 69, 71, 5, 215, 137, 2, 83, 40, 70, 67, 45, 13, 71, 65, 82, 32, 70, 82, 65, 67, 84, 73, 79, 78, 32, 2, 11, 65, 2, 11, 78, 2, - 159, 200, 1, 85, 38, 106, 70, 96, 4, 79, 78, 69, 32, 178, 2, 84, 80, 7, + 135, 203, 1, 85, 38, 106, 70, 96, 4, 79, 78, 69, 32, 178, 2, 84, 80, 7, 83, 69, 86, 69, 78, 32, 69, 153, 33, 3, 90, 69, 82, 6, 56, 4, 73, 86, 69, 32, 253, 3, 5, 79, 85, 82, 32, 70, 4, 162, 3, 69, 113, 3, 83, 73, 88, 18, 66, 69, 28, 2, 70, 73, 18, 72, 34, 78, 14, 81, 30, 83, 55, 84, 2, 197, 1, - 3, 73, 71, 72, 2, 171, 1, 70, 2, 11, 65, 2, 251, 129, 1, 76, 2, 111, 73, - 2, 133, 75, 3, 85, 65, 82, 4, 24, 2, 69, 86, 15, 73, 2, 43, 69, 2, 43, - 88, 4, 18, 69, 35, 72, 2, 11, 78, 2, 255, 128, 2, 84, 2, 143, 107, 73, + 3, 73, 71, 72, 2, 171, 1, 70, 2, 11, 65, 2, 227, 132, 1, 76, 2, 111, 73, + 2, 217, 75, 3, 85, 65, 82, 4, 24, 2, 69, 86, 15, 73, 2, 43, 69, 2, 43, + 88, 4, 18, 69, 35, 72, 2, 11, 78, 2, 231, 131, 2, 84, 2, 243, 107, 73, 10, 48, 5, 72, 82, 69, 69, 32, 93, 3, 87, 79, 32, 6, 26, 69, 26, 81, 67, - 70, 2, 109, 3, 73, 71, 72, 2, 21, 3, 85, 65, 82, 2, 139, 114, 84, 4, 22, - 70, 219, 32, 84, 2, 11, 73, 2, 11, 70, 2, 205, 194, 1, 2, 84, 72, 164, 6, - 86, 65, 250, 23, 69, 158, 3, 72, 250, 68, 73, 234, 8, 79, 134, 6, 82, - 191, 144, 1, 74, 204, 2, 114, 70, 18, 78, 220, 5, 2, 88, 73, 158, 1, 82, - 210, 10, 84, 194, 1, 86, 221, 152, 1, 6, 83, 84, 69, 66, 65, 83, 2, 147, + 70, 2, 109, 3, 73, 71, 72, 2, 21, 3, 85, 65, 82, 2, 215, 114, 84, 4, 22, + 70, 219, 32, 84, 2, 11, 73, 2, 11, 70, 2, 181, 197, 1, 2, 84, 72, 168, 6, + 86, 65, 250, 23, 69, 158, 3, 72, 222, 69, 73, 234, 8, 79, 238, 5, 82, + 219, 146, 1, 74, 204, 2, 114, 70, 18, 78, 220, 5, 2, 88, 73, 158, 1, 82, + 210, 10, 84, 194, 1, 86, 197, 155, 1, 6, 83, 84, 69, 66, 65, 83, 2, 231, 90, 70, 122, 36, 4, 67, 72, 79, 32, 183, 5, 73, 118, 100, 7, 76, 69, 84, 84, 69, 82, 32, 252, 3, 3, 78, 71, 85, 16, 5, 84, 79, 78, 69, 32, 243, 7, 68, 88, 210, 1, 65, 38, 79, 34, 69, 22, 73, 22, 76, 50, 78, 42, 84, 50, - 85, 22, 89, 130, 178, 1, 75, 2, 80, 2, 83, 138, 69, 66, 2, 67, 2, 68, 2, + 85, 22, 89, 234, 180, 1, 75, 2, 80, 2, 83, 138, 69, 66, 2, 67, 2, 68, 2, 70, 2, 71, 2, 72, 2, 74, 2, 77, 2, 82, 2, 86, 2, 87, 3, 90, 13, 34, 65, - 210, 250, 1, 78, 87, 85, 7, 11, 78, 5, 151, 251, 1, 71, 5, 131, 251, 1, - 78, 5, 155, 250, 1, 78, 4, 26, 76, 195, 250, 1, 65, 2, 135, 248, 1, 72, - 6, 242, 247, 1, 71, 2, 89, 187, 2, 65, 8, 202, 247, 1, 72, 2, 82, 2, 83, - 187, 2, 65, 5, 243, 169, 1, 69, 4, 174, 248, 1, 73, 147, 1, 65, 2, 159, - 114, 78, 8, 40, 3, 75, 79, 73, 1, 3, 84, 85, 80, 5, 139, 229, 1, 78, 4, + 186, 253, 1, 78, 87, 85, 7, 11, 78, 5, 255, 253, 1, 71, 5, 235, 253, 1, + 78, 5, 131, 253, 1, 78, 4, 26, 76, 171, 253, 1, 65, 2, 239, 250, 1, 72, + 6, 218, 250, 1, 71, 2, 89, 187, 2, 65, 8, 178, 250, 1, 72, 2, 82, 2, 83, + 187, 2, 65, 5, 219, 172, 1, 69, 4, 150, 251, 1, 73, 147, 1, 65, 2, 135, + 117, 78, 8, 40, 3, 75, 79, 73, 1, 3, 84, 85, 80, 5, 243, 231, 1, 78, 4, 21, 3, 78, 71, 32, 4, 76, 8, 67, 82, 69, 83, 67, 69, 78, 84, 1, 7, 71, - 73, 66, 66, 79, 85, 83, 2, 21, 3, 32, 77, 79, 2, 11, 79, 2, 155, 100, 78, - 170, 1, 72, 9, 65, 78, 71, 32, 67, 73, 84, 73, 32, 229, 111, 4, 78, 73, + 73, 66, 66, 79, 85, 83, 2, 21, 3, 32, 77, 79, 2, 11, 79, 2, 231, 100, 78, + 170, 1, 72, 9, 65, 78, 71, 32, 67, 73, 84, 73, 32, 205, 114, 4, 78, 73, 78, 71, 168, 1, 128, 1, 6, 67, 65, 80, 73, 84, 65, 0, 4, 83, 77, 65, 76, - 190, 4, 68, 156, 2, 7, 78, 85, 77, 66, 69, 82, 32, 175, 222, 1, 79, 64, + 190, 4, 68, 156, 2, 7, 78, 85, 77, 66, 69, 82, 32, 151, 225, 1, 79, 64, 45, 9, 76, 32, 76, 69, 84, 84, 69, 82, 32, 64, 174, 1, 65, 38, 69, 42, - 72, 74, 78, 50, 79, 22, 83, 50, 85, 30, 89, 226, 42, 84, 180, 110, 2, 86, + 72, 74, 78, 50, 79, 22, 83, 50, 85, 30, 89, 174, 43, 84, 208, 112, 2, 86, 73, 222, 51, 66, 2, 80, 246, 5, 75, 158, 11, 73, 2, 87, 162, 17, 68, 3, - 71, 9, 166, 243, 1, 78, 86, 77, 3, 84, 7, 11, 78, 4, 202, 243, 1, 78, 3, - 89, 8, 38, 79, 198, 154, 1, 73, 235, 31, 65, 4, 170, 186, 1, 82, 235, 25, - 76, 4, 26, 71, 215, 161, 1, 85, 2, 147, 240, 1, 65, 5, 151, 172, 1, 68, - 4, 26, 83, 179, 222, 1, 73, 2, 159, 205, 1, 85, 4, 242, 241, 1, 67, 3, - 85, 8, 34, 85, 182, 241, 1, 65, 3, 79, 5, 179, 241, 1, 74, 20, 11, 73, + 71, 9, 142, 246, 1, 78, 86, 77, 3, 84, 7, 11, 78, 4, 178, 246, 1, 78, 3, + 89, 8, 38, 79, 174, 157, 1, 73, 235, 31, 65, 4, 146, 189, 1, 82, 235, 25, + 76, 4, 26, 71, 191, 164, 1, 85, 2, 251, 242, 1, 65, 5, 255, 174, 1, 68, + 4, 26, 83, 155, 225, 1, 73, 2, 135, 208, 1, 85, 4, 218, 244, 1, 67, 3, + 85, 8, 34, 85, 158, 244, 1, 65, 3, 79, 5, 155, 244, 1, 74, 20, 11, 73, 20, 11, 71, 20, 17, 2, 73, 84, 20, 11, 32, 20, 66, 70, 30, 83, 42, 84, - 62, 90, 238, 85, 78, 14, 79, 223, 110, 69, 4, 206, 115, 73, 131, 4, 79, - 4, 22, 69, 207, 99, 73, 2, 135, 28, 86, 4, 26, 72, 211, 208, 1, 87, 2, - 11, 82, 2, 227, 216, 1, 69, 2, 11, 69, 2, 163, 208, 1, 82, 18, 42, 69, + 62, 90, 210, 86, 78, 14, 79, 227, 112, 69, 4, 182, 118, 73, 131, 4, 79, + 4, 22, 69, 155, 100, 73, 2, 211, 28, 86, 4, 26, 72, 187, 211, 1, 87, 2, + 11, 82, 2, 203, 219, 1, 69, 2, 11, 69, 2, 139, 211, 1, 82, 18, 42, 69, 30, 70, 42, 78, 38, 83, 39, 84, 2, 221, 1, 3, 73, 71, 72, 4, 22, 73, 139, 1, 79, 2, 171, 1, 70, 2, 17, 2, 73, 78, 2, 135, 1, 69, 4, 92, 2, 69, 86, - 25, 2, 73, 88, 6, 34, 72, 26, 87, 179, 157, 1, 69, 2, 11, 73, 2, 35, 82, - 2, 11, 69, 2, 11, 78, 2, 175, 219, 1, 84, 12, 32, 2, 69, 82, 179, 235, 1, - 67, 10, 34, 32, 165, 156, 1, 2, 77, 69, 8, 80, 3, 67, 76, 79, 22, 87, - 228, 200, 1, 5, 66, 85, 70, 70, 65, 1, 2, 80, 79, 2, 175, 182, 1, 83, 2, - 223, 110, 65, 20, 60, 2, 69, 32, 124, 4, 73, 78, 71, 32, 161, 1, 2, 89, - 32, 6, 182, 2, 68, 161, 189, 1, 23, 65, 82, 82, 79, 87, 32, 80, 79, 73, + 25, 2, 73, 88, 6, 34, 72, 26, 87, 155, 160, 1, 69, 2, 11, 73, 2, 35, 82, + 2, 11, 69, 2, 11, 78, 2, 151, 222, 1, 84, 12, 32, 2, 69, 82, 155, 238, 1, + 67, 10, 34, 32, 141, 159, 1, 2, 77, 69, 8, 80, 3, 67, 76, 79, 22, 87, + 204, 203, 1, 5, 66, 85, 70, 70, 65, 1, 2, 80, 79, 2, 151, 185, 1, 83, 2, + 199, 113, 65, 20, 60, 2, 69, 32, 124, 4, 73, 78, 71, 32, 161, 1, 2, 89, + 32, 6, 182, 2, 68, 137, 192, 1, 23, 65, 82, 82, 79, 87, 32, 80, 79, 73, 78, 84, 73, 78, 71, 32, 68, 73, 82, 69, 67, 84, 76, 89, 6, 64, 5, 66, 76, 65, 67, 75, 0, 5, 87, 72, 73, 84, 69, 55, 72, 2, 17, 2, 32, 70, 2, 11, - 76, 2, 175, 232, 1, 65, 2, 11, 65, 2, 11, 78, 2, 215, 97, 68, 8, 26, 68, - 34, 76, 43, 79, 2, 11, 65, 2, 143, 231, 1, 83, 4, 22, 79, 203, 78, 73, 2, - 163, 78, 87, 2, 11, 86, 2, 11, 69, 2, 135, 78, 82, 14, 48, 3, 65, 82, 89, - 70, 68, 70, 73, 171, 1, 83, 4, 11, 32, 4, 32, 2, 67, 65, 211, 169, 1, 70, - 2, 183, 169, 1, 84, 4, 26, 71, 167, 149, 1, 68, 2, 129, 70, 6, 69, 45, + 76, 2, 151, 235, 1, 65, 2, 11, 65, 2, 11, 78, 2, 191, 100, 68, 8, 26, 68, + 34, 76, 43, 79, 2, 11, 65, 2, 247, 233, 1, 83, 4, 22, 79, 175, 79, 73, 2, + 135, 79, 87, 2, 11, 86, 2, 11, 69, 2, 235, 78, 82, 14, 48, 3, 65, 82, 89, + 70, 68, 70, 73, 171, 1, 83, 4, 11, 32, 4, 32, 2, 67, 65, 187, 172, 1, 70, + 2, 159, 172, 1, 84, 4, 26, 71, 143, 152, 1, 68, 2, 229, 70, 6, 69, 45, 84, 65, 73, 76, 4, 116, 17, 69, 82, 83, 84, 82, 65, 83, 83, 32, 69, 76, - 76, 73, 80, 84, 73, 67, 145, 45, 7, 71, 72, 84, 32, 76, 73, 70, 2, 17, 2, - 32, 70, 2, 149, 148, 1, 2, 85, 78, 2, 37, 7, 84, 32, 83, 89, 82, 73, 65, - 2, 131, 35, 67, 188, 2, 52, 3, 69, 69, 76, 100, 3, 73, 84, 69, 219, 62, - 65, 7, 60, 7, 32, 79, 70, 32, 68, 72, 65, 21, 4, 67, 72, 65, 73, 2, 147, - 194, 1, 82, 2, 231, 79, 82, 180, 2, 54, 32, 161, 66, 8, 45, 70, 69, 65, - 84, 72, 69, 82, 178, 2, 148, 2, 18, 65, 82, 82, 79, 87, 32, 83, 72, 65, - 70, 84, 32, 87, 73, 68, 84, 72, 32, 114, 66, 46, 67, 250, 15, 68, 166, 5, - 69, 50, 70, 178, 3, 72, 246, 3, 76, 210, 4, 77, 222, 1, 78, 34, 80, 146, + 76, 73, 80, 84, 73, 67, 229, 45, 7, 71, 72, 84, 32, 76, 73, 70, 2, 17, 2, + 32, 70, 2, 253, 150, 1, 2, 85, 78, 2, 37, 7, 84, 32, 83, 89, 82, 73, 65, + 2, 215, 35, 67, 192, 2, 52, 3, 69, 69, 76, 100, 3, 73, 84, 69, 175, 63, + 65, 7, 60, 7, 32, 79, 70, 32, 68, 72, 65, 21, 4, 67, 72, 65, 73, 2, 251, + 196, 1, 82, 2, 179, 80, 82, 184, 2, 54, 32, 133, 67, 8, 45, 70, 69, 65, + 84, 72, 69, 82, 182, 2, 148, 2, 18, 65, 82, 82, 79, 87, 32, 83, 72, 65, + 70, 84, 32, 87, 73, 68, 84, 72, 32, 114, 66, 46, 67, 198, 16, 68, 166, 5, + 69, 50, 70, 186, 3, 72, 246, 3, 76, 210, 4, 77, 222, 1, 78, 34, 80, 146, 1, 81, 78, 82, 142, 2, 83, 158, 12, 84, 228, 3, 2, 85, 80, 149, 4, 3, 86, - 69, 82, 4, 22, 84, 231, 70, 79, 2, 11, 87, 2, 21, 3, 79, 32, 84, 2, 17, - 2, 72, 73, 2, 11, 82, 2, 239, 161, 1, 68, 2, 11, 85, 2, 11, 76, 2, 135, - 170, 1, 76, 94, 154, 1, 72, 176, 10, 5, 73, 82, 67, 76, 69, 162, 3, 76, + 69, 82, 4, 22, 84, 203, 71, 79, 2, 11, 87, 2, 21, 3, 79, 32, 84, 2, 17, + 2, 72, 73, 2, 11, 82, 2, 215, 164, 1, 68, 2, 11, 85, 2, 11, 76, 2, 239, + 172, 1, 76, 98, 154, 1, 72, 252, 10, 5, 73, 82, 67, 76, 69, 162, 3, 76, 24, 20, 79, 78, 67, 65, 86, 69, 45, 83, 73, 68, 69, 68, 32, 68, 73, 65, - 77, 79, 78, 68, 79, 82, 66, 25, 4, 69, 83, 83, 32, 66, 66, 66, 38, 69, - 118, 75, 142, 4, 80, 22, 81, 38, 82, 131, 2, 84, 6, 241, 5, 5, 73, 83, - 72, 79, 80, 4, 45, 9, 81, 85, 73, 72, 79, 80, 80, 69, 82, 5, 17, 2, 32, - 82, 2, 129, 6, 8, 79, 84, 65, 84, 69, 68, 32, 78, 26, 38, 73, 25, 5, 78, - 73, 71, 72, 84, 6, 177, 4, 2, 78, 71, 21, 22, 32, 151, 3, 45, 12, 41, 8, - 82, 79, 84, 65, 84, 69, 68, 32, 12, 104, 2, 70, 79, 0, 15, 79, 78, 69, + 77, 79, 78, 68, 79, 82, 70, 25, 4, 69, 83, 83, 32, 70, 104, 3, 65, 76, + 70, 18, 66, 38, 69, 116, 3, 70, 69, 82, 22, 75, 142, 4, 80, 22, 81, 38, + 82, 131, 2, 84, 2, 255, 83, 73, 6, 133, 6, 5, 73, 83, 72, 79, 80, 4, 45, + 9, 81, 85, 73, 72, 79, 80, 80, 69, 82, 5, 17, 2, 32, 82, 2, 149, 6, 8, + 79, 84, 65, 84, 69, 68, 32, 78, 2, 167, 222, 1, 90, 26, 38, 73, 25, 5, + 78, 73, 71, 72, 84, 6, 177, 4, 2, 78, 71, 21, 22, 32, 151, 3, 45, 12, 41, + 8, 82, 79, 84, 65, 84, 69, 68, 32, 12, 104, 2, 70, 79, 0, 15, 79, 78, 69, 32, 72, 85, 78, 68, 82, 69, 68, 32, 84, 72, 73, 18, 84, 215, 3, 78, 2, 207, 1, 82, 6, 148, 1, 11, 87, 79, 32, 72, 85, 78, 68, 82, 69, 68, 32, 133, 3, 20, 72, 82, 69, 69, 32, 72, 85, 78, 68, 82, 69, 68, 32, 70, 73, @@ -8854,374 +8928,377 @@ static const unsigned char packed_name_dawg[] = { 87, 6, 21, 3, 85, 69, 69, 6, 35, 78, 6, 21, 3, 79, 79, 75, 7, 45, 9, 32, 82, 79, 84, 65, 84, 69, 68, 32, 4, 70, 78, 25, 13, 84, 87, 79, 32, 72, 85, 78, 68, 82, 69, 68, 32, 83, 2, 49, 3, 73, 78, 69, 2, 25, 4, 69, 86, - 69, 78, 2, 17, 2, 84, 89, 2, 225, 65, 6, 32, 68, 69, 71, 82, 69, 12, 29, + 69, 78, 2, 17, 2, 84, 89, 2, 241, 65, 6, 32, 68, 69, 71, 82, 69, 12, 29, 5, 85, 82, 78, 69, 68, 12, 11, 32, 12, 42, 66, 30, 75, 34, 80, 34, 81, - 47, 82, 2, 197, 91, 3, 73, 83, 72, 4, 198, 131, 1, 73, 171, 38, 78, 2, - 11, 65, 2, 179, 132, 1, 87, 2, 11, 85, 2, 11, 69, 2, 135, 132, 1, 69, 2, - 243, 169, 1, 79, 19, 11, 32, 16, 100, 16, 67, 79, 78, 84, 65, 73, 78, 73, + 47, 82, 2, 225, 93, 3, 73, 83, 72, 4, 226, 133, 1, 73, 171, 38, 78, 2, + 11, 65, 2, 207, 134, 1, 87, 2, 11, 85, 2, 11, 69, 2, 163, 134, 1, 69, 2, + 143, 172, 1, 79, 19, 11, 32, 16, 100, 16, 67, 79, 78, 84, 65, 73, 78, 73, 78, 71, 32, 66, 76, 65, 67, 75, 121, 5, 87, 73, 84, 72, 32, 2, 17, 2, 32, 83, 2, 11, 77, 2, 21, 3, 65, 76, 76, 2, 11, 32, 2, 11, 67, 2, 11, 73, 2, - 11, 82, 2, 187, 45, 67, 14, 56, 2, 68, 79, 70, 84, 228, 31, 2, 76, 79, - 231, 1, 85, 4, 18, 84, 35, 87, 2, 11, 32, 2, 167, 166, 1, 82, 2, 143, 54, - 78, 2, 11, 87, 2, 11, 79, 2, 11, 32, 2, 247, 59, 68, 2, 209, 27, 2, 85, - 66, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 228, 30, 2, 76, 69, 49, 2, 82, - 73, 2, 17, 2, 79, 83, 2, 227, 141, 1, 83, 28, 76, 6, 73, 65, 77, 79, 78, + 11, 82, 2, 195, 45, 67, 14, 56, 2, 68, 79, 70, 84, 236, 31, 2, 76, 79, + 231, 1, 85, 4, 18, 84, 35, 87, 2, 11, 32, 2, 195, 168, 1, 82, 2, 167, 54, + 78, 2, 11, 87, 2, 11, 79, 2, 11, 32, 2, 147, 60, 68, 2, 217, 27, 2, 85, + 66, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 236, 30, 2, 76, 69, 49, 2, 82, + 73, 2, 17, 2, 79, 83, 2, 255, 143, 1, 83, 28, 76, 6, 73, 65, 77, 79, 78, 68, 216, 2, 3, 79, 87, 78, 177, 1, 2, 82, 65, 15, 11, 32, 12, 160, 1, 17, 67, 79, 78, 84, 65, 73, 78, 73, 78, 71, 32, 66, 76, 65, 67, 75, 32, 128, - 1, 9, 87, 73, 84, 72, 32, 67, 69, 78, 84, 226, 23, 83, 197, 19, 2, 73, + 1, 9, 87, 73, 84, 72, 32, 67, 69, 78, 84, 234, 23, 83, 213, 19, 2, 73, 78, 6, 74, 83, 0, 6, 86, 69, 82, 89, 32, 83, 29, 6, 77, 69, 68, 73, 85, - 77, 2, 25, 4, 77, 65, 76, 76, 2, 197, 15, 2, 32, 68, 2, 11, 82, 2, 11, + 77, 2, 25, 4, 77, 65, 76, 76, 2, 205, 15, 2, 32, 68, 2, 11, 82, 2, 11, 69, 2, 231, 58, 68, 10, 96, 10, 32, 80, 79, 73, 78, 84, 73, 78, 71, 32, - 53, 10, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 6, 170, 36, 66, 24, 5, - 76, 69, 70, 84, 32, 51, 73, 4, 194, 37, 83, 51, 84, 4, 33, 6, 85, 71, 72, - 84, 83, 32, 4, 22, 77, 255, 120, 75, 2, 159, 122, 65, 2, 29, 5, 88, 67, - 76, 65, 77, 2, 167, 15, 65, 14, 74, 76, 144, 2, 11, 79, 85, 82, 32, 80, + 53, 10, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 6, 178, 36, 66, 24, 5, + 76, 69, 70, 84, 32, 51, 73, 4, 202, 37, 83, 51, 84, 4, 33, 6, 85, 71, 72, + 84, 83, 32, 4, 22, 77, 155, 123, 75, 2, 187, 124, 65, 2, 29, 5, 88, 67, + 76, 65, 77, 2, 175, 15, 65, 14, 74, 76, 144, 2, 11, 79, 85, 82, 32, 80, 79, 73, 78, 84, 69, 68, 71, 82, 8, 28, 2, 65, 71, 175, 1, 79, 5, 149, 1, 34, 32, 87, 73, 84, 72, 32, 72, 79, 82, 73, 90, 79, 78, 84, 65, 76, 32, 77, 73, 68, 68, 76, 69, 32, 66, 76, 65, 67, 75, 32, 83, 84, 82, 73, 2, - 231, 176, 1, 80, 4, 26, 82, 163, 137, 1, 87, 2, 17, 2, 69, 84, 2, 163, - 176, 1, 84, 4, 11, 32, 4, 18, 67, 23, 83, 2, 171, 198, 1, 85, 2, 247, 36, - 84, 2, 235, 61, 79, 16, 34, 65, 150, 1, 69, 231, 1, 79, 2, 25, 4, 82, 68, - 32, 83, 2, 45, 9, 72, 69, 76, 76, 32, 70, 76, 79, 80, 2, 17, 2, 80, 89, - 2, 17, 2, 32, 68, 2, 11, 73, 2, 211, 193, 1, 83, 10, 22, 65, 151, 12, 88, - 8, 30, 82, 29, 3, 86, 89, 32, 4, 11, 84, 5, 211, 15, 32, 4, 74, 67, 53, - 14, 83, 65, 76, 84, 73, 82, 69, 32, 87, 73, 84, 72, 32, 82, 2, 17, 2, 72, - 69, 2, 11, 67, 2, 211, 129, 1, 75, 2, 167, 19, 79, 4, 60, 8, 82, 73, 90, - 79, 78, 84, 65, 76, 233, 42, 2, 85, 82, 2, 205, 31, 2, 32, 69, 22, 42, - 65, 116, 3, 69, 70, 84, 175, 1, 79, 4, 24, 2, 82, 71, 19, 84, 2, 227, 32, - 69, 2, 11, 73, 2, 11, 78, 2, 17, 2, 32, 67, 2, 11, 82, 2, 187, 42, 79, - 12, 58, 32, 77, 10, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 6, 44, 6, 76, - 65, 78, 69, 32, 77, 247, 24, 80, 2, 11, 69, 2, 227, 10, 82, 6, 150, 2, - 80, 206, 24, 83, 51, 84, 6, 160, 1, 4, 87, 69, 82, 32, 221, 8, 30, 90, - 69, 78, 71, 69, 32, 67, 79, 78, 84, 65, 73, 78, 73, 78, 71, 32, 66, 76, - 65, 67, 75, 32, 83, 77, 65, 76, 76, 32, 76, 4, 44, 3, 76, 69, 70, 1, 4, - 82, 73, 71, 72, 2, 11, 84, 2, 17, 2, 32, 80, 2, 203, 5, 79, 12, 68, 6, - 69, 68, 73, 85, 77, 32, 117, 7, 79, 79, 78, 32, 83, 69, 76, 10, 30, 68, - 54, 83, 227, 6, 76, 2, 11, 73, 2, 11, 65, 2, 11, 77, 2, 199, 46, 79, 6, - 246, 26, 84, 50, 77, 59, 81, 2, 11, 69, 2, 203, 185, 1, 78, 2, 11, 73, 2, - 227, 187, 1, 66, 6, 18, 65, 87, 69, 2, 37, 7, 82, 65, 76, 76, 69, 76, 79, - 2, 11, 71, 2, 11, 82, 2, 163, 170, 1, 65, 4, 11, 78, 4, 174, 2, 84, 211, - 46, 78, 2, 21, 3, 85, 69, 83, 2, 197, 118, 9, 84, 73, 79, 78, 32, 77, 65, - 82, 75, 14, 34, 69, 25, 4, 73, 71, 72, 84, 2, 129, 21, 2, 67, 84, 12, 60, - 10, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 211, 17, 32, 8, 30, 80, 202, - 19, 83, 51, 84, 4, 18, 69, 55, 79, 2, 11, 78, 2, 11, 84, 2, 11, 65, 2, - 179, 104, 71, 2, 11, 73, 2, 11, 78, 2, 139, 122, 84, 56, 118, 67, 32, 3, - 69, 83, 65, 18, 72, 58, 77, 166, 1, 80, 68, 5, 81, 85, 65, 82, 69, 180, - 6, 2, 85, 78, 183, 12, 84, 2, 153, 40, 4, 73, 83, 83, 79, 2, 175, 37, 77, - 2, 21, 3, 79, 71, 73, 2, 169, 121, 4, 32, 80, 73, 69, 8, 32, 4, 65, 76, - 76, 32, 115, 73, 6, 18, 76, 71, 83, 2, 11, 79, 2, 11, 90, 2, 11, 69, 2, - 11, 78, 2, 203, 158, 1, 71, 4, 210, 19, 84, 107, 81, 2, 211, 44, 76, 2, - 21, 3, 65, 68, 69, 2, 11, 32, 2, 11, 83, 2, 251, 151, 1, 85, 29, 11, 32, - 26, 114, 66, 36, 17, 67, 79, 78, 84, 65, 73, 78, 73, 78, 71, 32, 66, 76, - 65, 67, 75, 32, 85, 5, 87, 73, 84, 72, 32, 2, 17, 2, 85, 84, 2, 159, 99, - 84, 6, 54, 86, 178, 17, 83, 37, 6, 77, 69, 68, 73, 85, 77, 2, 153, 17, 3, - 69, 82, 89, 18, 150, 1, 76, 50, 82, 214, 1, 85, 144, 1, 17, 86, 69, 82, - 84, 73, 67, 65, 76, 32, 66, 73, 83, 69, 67, 84, 73, 78, 233, 19, 6, 67, - 69, 78, 84, 82, 69, 6, 18, 69, 15, 79, 2, 67, 70, 4, 247, 1, 87, 4, 18, - 73, 115, 79, 2, 17, 2, 71, 72, 2, 33, 6, 84, 87, 65, 82, 68, 83, 2, 11, - 32, 2, 11, 84, 2, 11, 73, 2, 215, 172, 1, 67, 2, 29, 5, 85, 78, 68, 69, - 68, 2, 21, 3, 32, 67, 79, 2, 193, 32, 2, 82, 78, 4, 17, 2, 80, 80, 4, 21, - 3, 69, 82, 32, 4, 44, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, 2, 33, 6, 84, - 32, 81, 85, 65, 68, 2, 175, 36, 82, 2, 171, 20, 71, 11, 11, 32, 8, 84, - 12, 66, 69, 72, 73, 78, 68, 32, 67, 76, 79, 85, 68, 69, 5, 87, 73, 84, - 72, 32, 5, 29, 5, 32, 87, 73, 84, 72, 2, 17, 2, 32, 82, 2, 255, 44, 65, - 4, 38, 82, 29, 5, 83, 77, 65, 76, 76, 2, 11, 65, 2, 179, 110, 89, 2, 11, - 32, 2, 21, 3, 67, 76, 79, 2, 187, 101, 85, 10, 38, 79, 54, 69, 62, 82, - 203, 1, 87, 2, 49, 10, 85, 67, 72, 84, 79, 78, 69, 32, 84, 69, 2, 17, 2, - 76, 69, 2, 11, 80, 2, 11, 72, 2, 151, 17, 79, 4, 148, 1, 4, 65, 80, 69, - 90, 33, 28, 73, 65, 78, 71, 76, 69, 32, 67, 79, 78, 84, 65, 73, 78, 73, - 78, 71, 32, 83, 77, 65, 76, 76, 32, 87, 72, 73, 84, 2, 11, 73, 2, 183, - 152, 1, 85, 2, 135, 4, 69, 2, 11, 79, 2, 85, 19, 45, 87, 65, 89, 32, 76, - 69, 70, 84, 32, 87, 65, 89, 32, 84, 82, 65, 70, 70, 2, 11, 73, 2, 251, - 167, 1, 67, 12, 62, 32, 185, 1, 10, 45, 80, 79, 73, 78, 84, 73, 78, 71, - 32, 4, 11, 80, 4, 41, 8, 79, 73, 78, 84, 73, 78, 71, 32, 4, 18, 66, 75, - 73, 2, 21, 3, 65, 67, 75, 2, 25, 4, 72, 65, 78, 68, 2, 17, 2, 32, 73, 2, - 17, 2, 78, 68, 2, 219, 25, 69, 8, 54, 67, 42, 83, 125, 7, 84, 82, 73, 65, - 78, 71, 76, 2, 21, 3, 72, 69, 86, 2, 179, 85, 82, 2, 25, 4, 77, 65, 76, - 76, 2, 17, 2, 32, 84, 2, 17, 2, 82, 73, 2, 11, 65, 2, 11, 78, 2, 11, 71, - 2, 211, 141, 1, 76, 4, 11, 69, 5, 11, 32, 2, 11, 87, 2, 217, 18, 3, 73, - 84, 72, 10, 44, 6, 84, 73, 67, 65, 76, 32, 255, 1, 89, 8, 62, 69, 48, 9, - 82, 69, 67, 84, 65, 78, 71, 76, 69, 127, 66, 2, 11, 76, 2, 17, 2, 76, 73, - 2, 183, 25, 80, 5, 37, 7, 32, 87, 73, 84, 72, 32, 72, 2, 37, 7, 79, 82, - 73, 90, 79, 78, 84, 2, 17, 2, 65, 76, 2, 11, 32, 2, 11, 66, 2, 219, 104, - 65, 2, 17, 2, 32, 83, 2, 11, 77, 2, 21, 3, 65, 76, 76, 2, 17, 2, 32, 83, - 2, 11, 81, 2, 11, 85, 2, 11, 65, 2, 227, 137, 1, 82, 2, 11, 69, 2, 11, - 68, 2, 17, 2, 32, 82, 2, 21, 3, 73, 71, 72, 2, 11, 84, 2, 11, 87, 2, 241, - 4, 4, 65, 82, 68, 83, 100, 140, 1, 10, 68, 69, 45, 72, 69, 65, 68, 69, - 68, 32, 132, 4, 4, 71, 71, 76, 89, 124, 6, 76, 84, 69, 68, 32, 70, 42, - 78, 189, 1, 2, 82, 69, 80, 128, 1, 3, 76, 69, 70, 0, 4, 82, 73, 71, 72, - 12, 4, 68, 79, 87, 78, 0, 2, 85, 80, 32, 3, 78, 79, 82, 1, 3, 83, 79, 85, - 10, 11, 84, 10, 109, 5, 87, 65, 82, 68, 83, 20, 21, 3, 84, 72, 32, 20, - 32, 2, 69, 65, 1, 2, 87, 69, 10, 17, 2, 83, 84, 10, 11, 32, 10, 110, 72, - 0, 6, 86, 69, 82, 89, 32, 72, 28, 5, 76, 73, 71, 72, 84, 0, 6, 77, 69, - 68, 73, 85, 77, 23, 66, 2, 25, 4, 69, 65, 86, 89, 2, 17, 2, 32, 66, 2, - 21, 3, 65, 82, 66, 2, 11, 32, 2, 11, 65, 2, 11, 82, 2, 11, 82, 2, 131, - 28, 79, 2, 17, 2, 32, 86, 2, 11, 69, 2, 33, 6, 82, 84, 73, 67, 65, 76, 2, - 11, 32, 2, 11, 76, 2, 11, 73, 2, 215, 130, 1, 78, 2, 11, 76, 2, 11, 79, - 2, 147, 91, 87, 12, 46, 68, 106, 69, 186, 15, 75, 163, 136, 1, 71, 6, 22, - 32, 139, 26, 79, 4, 44, 2, 67, 72, 217, 15, 4, 66, 76, 79, 87, 2, 11, 73, - 2, 151, 129, 1, 77, 2, 11, 32, 2, 121, 3, 71, 76, 65, 4, 36, 5, 68, 32, - 75, 69, 89, 51, 76, 2, 17, 2, 66, 79, 2, 11, 65, 2, 203, 80, 82, 2, 11, - 69, 2, 243, 88, 83, 30, 66, 77, 158, 3, 82, 226, 11, 78, 226, 64, 79, - 129, 9, 2, 76, 70, 14, 36, 2, 65, 78, 161, 2, 2, 69, 78, 13, 72, 12, 32, - 87, 73, 84, 72, 32, 66, 85, 78, 78, 89, 32, 29, 2, 83, 32, 2, 11, 69, 2, - 167, 6, 65, 8, 48, 2, 66, 79, 28, 2, 67, 76, 54, 72, 19, 83, 2, 11, 79, - 2, 207, 86, 84, 2, 11, 79, 2, 11, 84, 2, 11, 72, 2, 155, 86, 69, 2, 187, - 119, 65, 2, 17, 2, 65, 78, 2, 131, 10, 68, 2, 11, 83, 2, 11, 32, 2, 11, - 83, 2, 11, 89, 2, 17, 2, 77, 66, 2, 187, 9, 79, 10, 72, 2, 68, 32, 156, - 1, 3, 76, 68, 32, 32, 2, 82, 73, 219, 144, 1, 77, 4, 56, 9, 83, 69, 80, - 65, 82, 65, 84, 79, 82, 203, 83, 74, 2, 17, 2, 32, 77, 2, 21, 3, 73, 68, - 68, 2, 11, 76, 2, 11, 69, 2, 11, 32, 2, 207, 64, 68, 2, 11, 77, 2, 223, - 144, 1, 65, 2, 11, 69, 2, 171, 83, 68, 10, 56, 7, 65, 80, 80, 69, 68, 32, - 80, 30, 69, 163, 1, 73, 2, 213, 76, 3, 82, 69, 83, 6, 48, 2, 65, 84, 80, - 3, 83, 84, 76, 187, 108, 78, 2, 11, 72, 2, 11, 32, 2, 11, 80, 2, 25, 4, - 82, 79, 68, 85, 2, 195, 114, 67, 2, 11, 69, 2, 247, 80, 82, 2, 11, 84, 2, - 11, 73, 2, 17, 2, 78, 71, 2, 17, 2, 32, 72, 2, 11, 65, 2, 215, 71, 78, - 34, 76, 2, 32, 73, 138, 1, 45, 28, 7, 73, 65, 78, 71, 81, 73, 32, 155, - 83, 79, 2, 53, 11, 78, 32, 65, 32, 82, 69, 67, 84, 65, 78, 71, 2, 11, 76, - 2, 11, 69, 2, 11, 32, 2, 11, 66, 2, 11, 79, 2, 163, 140, 1, 88, 2, 11, - 82, 2, 187, 122, 65, 28, 48, 5, 66, 76, 65, 67, 75, 1, 3, 82, 69, 68, 14, - 11, 32, 14, 130, 1, 67, 72, 4, 69, 76, 69, 80, 28, 4, 71, 69, 78, 69, 46, - 72, 36, 4, 83, 79, 76, 68, 169, 9, 6, 77, 65, 78, 68, 65, 82, 4, 24, 2, - 65, 78, 19, 72, 2, 167, 58, 78, 2, 201, 57, 3, 65, 82, 73, 2, 11, 72, 2, - 191, 70, 65, 2, 11, 82, 2, 11, 65, 2, 179, 137, 1, 76, 2, 17, 2, 79, 82, - 2, 183, 114, 83, 2, 143, 75, 73, 242, 19, 50, 65, 58, 69, 226, 11, 73, - 217, 35, 2, 79, 45, 2, 17, 2, 87, 78, 2, 11, 73, 2, 241, 74, 2, 78, 71, - 98, 60, 4, 76, 76, 79, 87, 62, 78, 53, 5, 90, 73, 68, 73, 32, 2, 17, 2, - 32, 72, 2, 11, 69, 2, 11, 65, 2, 227, 106, 82, 2, 11, 32, 2, 11, 83, 2, - 11, 73, 2, 251, 54, 71, 94, 112, 10, 67, 79, 77, 66, 73, 78, 73, 78, 71, - 32, 76, 5, 72, 89, 80, 72, 69, 17, 7, 76, 69, 84, 84, 69, 82, 32, 4, 44, - 3, 77, 65, 68, 13, 4, 72, 65, 77, 90, 2, 11, 68, 2, 215, 67, 65, 2, 215, - 27, 78, 88, 250, 1, 67, 50, 77, 18, 68, 42, 69, 58, 72, 30, 75, 22, 71, - 2, 81, 32, 3, 76, 65, 77, 98, 78, 18, 80, 30, 83, 66, 84, 28, 2, 86, 65, - 126, 87, 14, 79, 18, 88, 52, 3, 89, 79, 84, 154, 1, 90, 134, 53, 82, 238, - 48, 66, 254, 5, 85, 162, 14, 70, 3, 74, 6, 22, 72, 147, 114, 73, 4, 22, - 72, 251, 113, 73, 2, 247, 113, 73, 4, 11, 65, 4, 178, 130, 1, 68, 3, 76, - 8, 42, 76, 142, 50, 89, 226, 79, 84, 3, 87, 2, 71, 73, 4, 150, 112, 65, - 147, 15, 72, 4, 18, 72, 15, 65, 2, 11, 65, 2, 163, 129, 1, 70, 5, 11, 32, - 2, 11, 87, 2, 21, 3, 73, 84, 72, 2, 17, 2, 32, 68, 2, 11, 79, 2, 187, 3, - 84, 2, 207, 48, 85, 4, 202, 105, 72, 215, 22, 69, 8, 46, 72, 246, 47, 73, - 194, 9, 65, 163, 70, 69, 2, 243, 47, 73, 4, 238, 104, 72, 215, 22, 65, 5, - 37, 7, 32, 65, 76, 84, 69, 82, 78, 2, 17, 2, 65, 84, 2, 11, 69, 2, 11, - 32, 2, 11, 70, 2, 11, 79, 2, 227, 109, 82, 2, 11, 65, 2, 159, 126, 87, 4, - 22, 72, 251, 125, 65, 2, 11, 69, 2, 139, 46, 89, 5, 37, 7, 32, 87, 73, - 84, 72, 32, 67, 2, 45, 9, 73, 82, 67, 85, 77, 70, 76, 69, 88, 2, 11, 32, - 2, 11, 65, 2, 11, 66, 2, 11, 79, 2, 255, 101, 86, 6, 22, 65, 175, 124, - 69, 5, 171, 124, 76, 140, 19, 34, 32, 161, 35, 3, 78, 32, 89, 138, 19, - 88, 8, 82, 65, 68, 73, 67, 65, 76, 32, 181, 7, 9, 83, 89, 76, 76, 65, 66, - 76, 69, 32, 110, 170, 1, 66, 42, 67, 86, 68, 42, 71, 74, 72, 66, 74, 66, - 75, 30, 76, 30, 77, 26, 78, 74, 80, 26, 83, 74, 84, 30, 86, 30, 89, 30, - 90, 250, 35, 81, 198, 49, 87, 235, 30, 79, 4, 22, 66, 247, 64, 85, 2, - 163, 93, 85, 12, 42, 85, 18, 89, 178, 98, 72, 203, 22, 73, 2, 135, 121, - 79, 7, 130, 121, 80, 3, 84, 4, 22, 68, 215, 120, 85, 2, 247, 63, 85, 10, - 42, 71, 238, 91, 79, 162, 28, 69, 15, 65, 4, 162, 89, 85, 235, 30, 79, 8, - 22, 88, 243, 88, 77, 6, 238, 88, 85, 202, 2, 73, 163, 28, 79, 8, 22, 74, - 167, 119, 79, 6, 246, 90, 85, 218, 5, 73, 215, 22, 89, 4, 206, 90, 73, - 175, 28, 69, 6, 190, 54, 73, 199, 7, 89, 4, 182, 118, 79, 15, 73, 8, 30, - 89, 26, 90, 199, 90, 66, 4, 254, 117, 73, 3, 79, 2, 231, 117, 85, 4, 182, - 89, 85, 3, 89, 10, 22, 72, 223, 97, 83, 8, 214, 60, 85, 178, 28, 65, 162, - 28, 79, 15, 89, 4, 214, 88, 65, 175, 28, 85, 4, 138, 60, 85, 211, 56, 69, - 4, 158, 88, 73, 175, 28, 79, 10, 54, 85, 224, 62, 2, 90, 73, 238, 24, 79, - 175, 28, 65, 4, 246, 115, 80, 3, 82, 156, 18, 134, 2, 66, 134, 1, 67, - 162, 1, 68, 110, 70, 50, 71, 150, 1, 72, 138, 3, 73, 134, 1, 74, 98, 75, - 54, 76, 62, 77, 134, 1, 78, 234, 4, 80, 54, 81, 2, 89, 46, 82, 162, 1, - 83, 134, 1, 84, 102, 86, 82, 87, 58, 88, 50, 90, 140, 2, 2, 85, 79, 66, - 65, 2, 79, 107, 69, 132, 1, 66, 66, 238, 21, 85, 150, 1, 69, 26, 73, 42, - 65, 2, 79, 67, 89, 64, 234, 21, 85, 150, 1, 69, 26, 73, 42, 65, 2, 79, 3, - 89, 122, 66, 72, 238, 20, 85, 150, 1, 69, 26, 73, 42, 65, 2, 79, 67, 89, - 54, 46, 85, 146, 22, 65, 2, 69, 2, 79, 67, 89, 19, 142, 22, 79, 106, 82, - 230, 88, 80, 3, 88, 106, 58, 68, 138, 15, 85, 134, 5, 73, 94, 69, 66, 65, - 3, 79, 54, 210, 19, 85, 58, 73, 94, 69, 66, 65, 3, 79, 42, 182, 20, 79, - 66, 65, 2, 73, 2, 89, 67, 85, 116, 58, 71, 214, 15, 85, 146, 4, 73, 42, - 65, 2, 69, 3, 79, 56, 50, 73, 162, 15, 85, 186, 4, 65, 2, 69, 3, 79, 13, - 150, 19, 69, 142, 90, 84, 3, 88, 246, 1, 78, 73, 30, 76, 58, 77, 54, 78, - 110, 88, 50, 85, 254, 15, 69, 66, 65, 3, 79, 6, 198, 19, 69, 231, 88, 84, - 64, 238, 16, 85, 58, 73, 94, 69, 2, 79, 66, 65, 67, 89, 58, 182, 16, 85, - 58, 73, 158, 1, 65, 2, 79, 35, 89, 42, 46, 79, 34, 85, 202, 16, 69, 26, - 73, 43, 65, 6, 242, 106, 80, 2, 84, 3, 88, 6, 238, 17, 79, 231, 88, 84, - 46, 46, 85, 254, 15, 69, 26, 73, 42, 65, 3, 79, 8, 187, 16, 79, 19, 42, - 84, 130, 16, 69, 206, 89, 80, 3, 88, 5, 11, 69, 2, 11, 82, 2, 11, 65, 2, - 11, 84, 2, 11, 73, 2, 11, 79, 2, 191, 39, 78, 106, 50, 74, 190, 10, 85, - 146, 4, 73, 42, 79, 67, 89, 50, 158, 13, 85, 174, 1, 73, 42, 79, 3, 89, - 56, 242, 12, 85, 58, 73, 158, 1, 65, 2, 69, 3, 79, 70, 218, 9, 85, 250, - 3, 69, 26, 73, 42, 65, 2, 79, 67, 89, 106, 70, 71, 218, 8, 85, 158, 3, - 73, 158, 1, 65, 2, 79, 2, 89, 107, 69, 44, 186, 11, 85, 150, 1, 69, 66, - 65, 2, 79, 105, 2, 73, 69, 248, 2, 102, 66, 54, 68, 94, 71, 90, 74, 46, - 82, 50, 89, 78, 90, 134, 7, 85, 58, 73, 94, 65, 2, 69, 67, 79, 54, 202, - 10, 73, 158, 1, 65, 2, 79, 66, 85, 3, 89, 46, 46, 73, 198, 10, 69, 66, - 65, 2, 79, 67, 85, 13, 234, 11, 69, 230, 88, 80, 2, 84, 3, 88, 34, 60, 2, - 85, 79, 218, 9, 69, 0, 2, 73, 69, 66, 65, 3, 79, 7, 226, 99, 84, 3, 88, - 50, 230, 1, 85, 242, 7, 73, 42, 79, 67, 89, 46, 146, 9, 79, 66, 65, 2, - 69, 66, 85, 3, 89, 38, 30, 85, 222, 8, 73, 43, 79, 15, 194, 8, 79, 142, - 90, 80, 2, 84, 3, 88, 56, 62, 85, 206, 4, 79, 178, 2, 73, 158, 1, 65, 66, - 89, 43, 69, 15, 254, 8, 79, 2, 82, 230, 88, 80, 3, 88, 60, 150, 6, 85, - 58, 73, 158, 1, 65, 2, 79, 67, 89, 56, 254, 2, 85, 146, 4, 73, 42, 79, - 67, 89, 100, 58, 82, 254, 4, 85, 150, 1, 69, 66, 65, 2, 79, 67, 89, 48, - 46, 85, 162, 6, 69, 2, 79, 66, 89, 43, 65, 17, 134, 7, 79, 2, 82, 230, - 88, 80, 2, 84, 3, 88, 176, 1, 70, 83, 158, 3, 72, 50, 85, 58, 73, 94, 69, - 66, 65, 2, 79, 67, 89, 56, 130, 4, 73, 94, 69, 66, 65, 2, 79, 2, 85, 67, - 89, 56, 46, 85, 158, 3, 73, 94, 69, 66, 65, 3, 79, 21, 182, 4, 79, 106, - 82, 230, 88, 80, 2, 84, 3, 88, 60, 54, 69, 166, 3, 73, 42, 65, 2, 79, 66, - 85, 3, 89, 4, 150, 93, 80, 3, 88, 28, 38, 85, 206, 2, 69, 2, 79, 67, 65, - 9, 203, 2, 79, 40, 210, 2, 73, 42, 79, 66, 89, 41, 2, 85, 79, 178, 1, 66, - 72, 50, 85, 58, 73, 42, 90, 54, 69, 66, 65, 2, 79, 67, 89, 54, 46, 85, - 214, 1, 65, 2, 69, 2, 79, 67, 89, 19, 146, 1, 79, 170, 1, 82, 230, 88, - 80, 2, 84, 3, 88, 15, 90, 69, 142, 90, 80, 2, 84, 3, 88, 58, 50, 69, 2, - 79, 26, 73, 42, 65, 34, 85, 35, 89, 7, 138, 90, 80, 3, 88, 17, 38, 69, - 206, 89, 80, 2, 84, 3, 88, 9, 202, 89, 80, 2, 84, 3, 88, 11, 70, 82, 230, - 88, 80, 3, 88, 13, 38, 82, 230, 88, 80, 2, 84, 3, 88, 5, 227, 88, 88, 2, - 219, 7, 65, 2, 207, 57, 89, 180, 4, 252, 1, 10, 32, 78, 79, 84, 65, 84, - 73, 79, 78, 32, 224, 6, 16, 65, 78, 65, 66, 65, 90, 65, 82, 32, 83, 81, - 85, 65, 82, 69, 32, 210, 15, 69, 180, 2, 6, 73, 80, 80, 69, 82, 45, 96, - 8, 78, 65, 77, 69, 78, 78, 89, 32, 188, 33, 3, 79, 77, 66, 235, 26, 87, - 26, 144, 1, 9, 66, 65, 71, 32, 77, 69, 77, 66, 69, 42, 82, 104, 6, 68, - 79, 77, 65, 73, 78, 60, 3, 76, 69, 70, 154, 1, 83, 145, 2, 3, 84, 89, 80, - 2, 11, 82, 2, 11, 83, 2, 215, 83, 72, 8, 100, 4, 65, 78, 71, 69, 60, 3, - 73, 71, 72, 221, 1, 11, 69, 76, 65, 84, 73, 79, 78, 65, 76, 32, 67, 2, - 173, 3, 11, 32, 65, 78, 84, 73, 82, 69, 83, 84, 82, 73, 4, 17, 2, 84, 32, - 4, 60, 4, 73, 77, 65, 71, 13, 7, 66, 73, 78, 68, 73, 78, 71, 2, 11, 69, - 2, 25, 4, 32, 66, 82, 65, 2, 11, 67, 2, 175, 29, 75, 8, 44, 6, 67, 72, - 69, 77, 65, 32, 211, 1, 80, 6, 18, 67, 71, 80, 2, 17, 2, 79, 77, 2, 11, - 80, 2, 11, 79, 2, 11, 83, 2, 107, 73, 4, 30, 73, 41, 3, 82, 79, 74, 2, - 11, 80, 2, 11, 73, 2, 163, 80, 78, 2, 11, 69, 2, 11, 67, 2, 11, 84, 2, - 87, 73, 2, 139, 52, 79, 2, 11, 69, 2, 11, 32, 2, 11, 67, 2, 11, 79, 2, - 11, 76, 2, 11, 79, 2, 223, 79, 78, 144, 1, 240, 1, 2, 67, 76, 68, 7, 73, - 78, 73, 84, 73, 65, 76, 204, 2, 14, 70, 73, 78, 65, 76, 32, 67, 79, 78, - 83, 79, 78, 65, 78, 16, 7, 76, 69, 84, 84, 69, 82, 32, 148, 3, 5, 77, 65, - 82, 75, 32, 206, 1, 83, 181, 3, 6, 86, 79, 87, 69, 76, 32, 14, 64, 5, 79, - 83, 73, 78, 71, 137, 1, 6, 85, 83, 84, 69, 82, 45, 4, 11, 32, 4, 64, 12, - 68, 79, 85, 66, 76, 69, 45, 76, 73, 78, 69, 68, 23, 72, 2, 17, 2, 32, 72, - 2, 17, 2, 69, 65, 2, 215, 10, 68, 10, 96, 13, 70, 73, 78, 65, 76, 32, 76, - 69, 84, 84, 69, 82, 32, 41, 7, 73, 78, 73, 84, 73, 65, 76, 8, 238, 72, - 76, 2, 82, 2, 86, 3, 89, 2, 37, 7, 32, 76, 69, 84, 84, 69, 82, 2, 151, 6, - 32, 2, 131, 9, 84, 82, 166, 1, 68, 50, 75, 38, 78, 46, 83, 38, 84, 46, - 66, 2, 67, 2, 71, 2, 80, 2, 90, 138, 69, 45, 2, 72, 2, 74, 2, 76, 2, 77, - 2, 82, 2, 86, 2, 89, 187, 2, 65, 12, 206, 1, 68, 2, 90, 138, 69, 72, 187, - 2, 65, 6, 154, 70, 83, 14, 72, 187, 2, 65, 8, 130, 70, 71, 2, 78, 2, 89, - 187, 2, 65, 6, 214, 69, 72, 2, 83, 187, 2, 65, 12, 42, 83, 2, 84, 138, - 69, 72, 187, 2, 65, 4, 134, 69, 72, 187, 2, 65, 8, 50, 68, 58, 83, 40, 4, - 76, 79, 78, 71, 23, 84, 2, 29, 5, 79, 85, 66, 76, 69, 2, 11, 32, 2, 11, - 83, 2, 11, 72, 2, 11, 65, 2, 159, 70, 68, 2, 17, 2, 32, 84, 2, 17, 2, 83, - 72, 2, 147, 69, 69, 14, 36, 4, 73, 71, 78, 32, 255, 2, 85, 12, 54, 65, - 72, 6, 67, 65, 78, 68, 82, 65, 167, 1, 86, 2, 11, 78, 2, 11, 85, 2, 17, - 2, 83, 86, 2, 11, 65, 2, 135, 66, 82, 6, 36, 5, 66, 73, 78, 68, 85, 15, - 32, 5, 11, 32, 2, 25, 4, 87, 73, 84, 72, 2, 17, 2, 32, 79, 2, 21, 3, 82, - 78, 65, 2, 11, 77, 2, 11, 69, 2, 239, 38, 78, 4, 11, 73, 4, 18, 82, 19, - 83, 2, 219, 33, 65, 2, 11, 65, 2, 11, 82, 2, 139, 64, 71, 2, 151, 4, 66, - 20, 38, 76, 109, 5, 83, 73, 71, 78, 32, 2, 17, 2, 69, 78, 2, 11, 71, 2, - 11, 84, 2, 11, 72, 2, 11, 32, 2, 11, 77, 2, 11, 65, 2, 135, 62, 82, 18, - 86, 65, 26, 79, 2, 85, 16, 8, 82, 69, 86, 69, 82, 83, 69, 68, 146, 64, - 69, 3, 73, 4, 182, 64, 73, 3, 85, 5, 159, 64, 69, 2, 183, 44, 32, 12, 72, - 2, 66, 82, 16, 9, 82, 79, 32, 87, 73, 68, 84, 72, 32, 203, 1, 85, 2, 147, - 2, 65, 8, 32, 2, 78, 79, 86, 83, 31, 74, 4, 26, 45, 73, 2, 78, 45, 2, 29, - 5, 66, 82, 69, 65, 75, 2, 11, 32, 2, 11, 83, 2, 163, 1, 80, 2, 11, 74, 2, - 11, 79, 2, 11, 73, 2, 11, 78, 2, 143, 5, 69, 2, 219, 61, 83, 2, 21, 3, - 77, 79, 85, 2, 17, 2, 84, 72, 2, 11, 32, 2, 11, 70, 2, 11, 65, 2, 167, - 38, 67, 242, 2, 168, 1, 10, 67, 79, 77, 66, 73, 78, 73, 78, 71, 32, 224, - 18, 6, 78, 69, 85, 77, 69, 32, 181, 38, 17, 80, 82, 73, 90, 78, 65, 75, - 32, 77, 79, 68, 73, 70, 73, 69, 82, 32, 128, 1, 144, 2, 17, 76, 79, 87, - 69, 82, 32, 84, 79, 78, 65, 76, 32, 82, 65, 78, 71, 69, 88, 5, 77, 65, - 82, 75, 32, 148, 3, 18, 65, 84, 84, 65, 67, 72, 73, 78, 71, 32, 86, 69, - 82, 84, 73, 67, 65, 76, 233, 11, 17, 84, 79, 78, 65, 76, 32, 82, 65, 78, - 71, 69, 32, 77, 65, 82, 75, 32, 2, 33, 6, 32, 73, 78, 68, 73, 67, 2, 11, - 65, 2, 11, 84, 2, 11, 79, 2, 219, 56, 82, 118, 182, 2, 67, 142, 1, 68, - 104, 8, 71, 79, 82, 65, 90, 68, 79, 32, 34, 78, 106, 75, 114, 79, 72, 2, - 80, 79, 152, 2, 8, 77, 65, 76, 79, 32, 80, 79, 86, 100, 2, 82, 65, 50, - 83, 186, 1, 84, 108, 7, 86, 89, 83, 79, 75, 79, 32, 234, 1, 90, 224, 10, - 2, 76, 79, 224, 22, 4, 85, 68, 65, 82, 129, 6, 4, 66, 79, 82, 90, 6, 60, - 6, 72, 65, 83, 72, 75, 65, 29, 5, 85, 82, 86, 69, 68, 5, 197, 47, 3, 32, - 80, 79, 2, 17, 2, 32, 79, 2, 11, 77, 2, 235, 24, 69, 4, 240, 10, 13, 69, - 77, 69, 83, 84, 86, 69, 78, 78, 89, 32, 90, 65, 217, 14, 6, 86, 79, 69, - 84, 79, 67, 10, 30, 78, 89, 3, 86, 89, 83, 8, 29, 5, 73, 90, 75, 79, 32, - 8, 164, 8, 8, 83, 32, 75, 82, 89, 90, 72, 69, 35, 79, 2, 167, 20, 79, 8, - 56, 4, 82, 89, 90, 72, 150, 6, 65, 233, 39, 2, 85, 80, 5, 21, 3, 32, 79, - 78, 2, 11, 32, 2, 203, 7, 76, 6, 220, 5, 3, 84, 83, 69, 200, 13, 5, 66, - 76, 65, 67, 72, 131, 31, 78, 18, 22, 68, 131, 2, 86, 6, 60, 7, 67, 72, - 65, 83, 72, 73, 69, 201, 22, 3, 86, 69, 82, 5, 17, 2, 32, 87, 2, 21, 3, - 73, 84, 72, 2, 17, 2, 32, 86, 2, 29, 5, 69, 82, 84, 73, 67, 2, 17, 2, 65, - 76, 2, 11, 32, 2, 11, 83, 2, 11, 84, 2, 11, 82, 2, 11, 79, 2, 131, 25, - 75, 12, 29, 5, 89, 83, 72, 69, 32, 12, 22, 83, 251, 3, 79, 8, 154, 3, 32, - 229, 2, 4, 84, 82, 65, 78, 4, 28, 2, 90, 83, 183, 5, 86, 2, 219, 37, 69, - 10, 136, 1, 2, 75, 79, 16, 16, 84, 82, 65, 78, 78, 79, 32, 77, 65, 76, - 79, 32, 80, 79, 86, 89, 236, 1, 5, 82, 69, 68, 78, 69, 231, 26, 79, 2, - 239, 42, 66, 2, 11, 83, 2, 183, 22, 72, 10, 50, 79, 16, 5, 83, 65, 84, - 65, 32, 139, 36, 73, 2, 187, 28, 67, 6, 158, 1, 79, 201, 24, 3, 83, 32, - 75, 10, 24, 2, 83, 32, 95, 79, 6, 11, 75, 6, 44, 6, 72, 79, 75, 72, 76, - 79, 247, 24, 82, 4, 11, 77, 4, 17, 2, 32, 79, 4, 11, 78, 4, 11, 32, 4, - 18, 76, 31, 82, 2, 11, 69, 2, 179, 14, 70, 2, 11, 73, 2, 11, 71, 2, 139, - 14, 72, 6, 18, 65, 31, 69, 2, 249, 25, 3, 68, 69, 82, 4, 22, 86, 239, 10, - 76, 2, 199, 38, 79, 6, 60, 5, 77, 82, 65, 67, 72, 18, 83, 1, 4, 84, 82, - 69, 83, 2, 155, 10, 78, 2, 17, 2, 86, 69, 2, 155, 6, 84, 232, 1, 128, 2, - 2, 67, 72, 38, 68, 218, 1, 70, 28, 10, 71, 79, 76, 85, 66, 67, 72, 73, - 75, 32, 158, 1, 75, 160, 2, 6, 77, 69, 67, 72, 73, 75, 184, 1, 2, 78, 69, - 18, 79, 138, 2, 80, 146, 2, 82, 114, 83, 180, 20, 9, 86, 82, 65, 75, 72, - 73, 89, 65, 32, 151, 2, 90, 4, 246, 11, 69, 209, 10, 2, 65, 83, 10, 74, - 69, 98, 85, 16, 9, 86, 65, 32, 86, 32, 67, 72, 69, 76, 187, 27, 79, 4, - 76, 2, 82, 66, 213, 2, 12, 77, 69, 83, 84, 86, 69, 78, 78, 89, 32, 75, - 76, 2, 195, 34, 73, 2, 203, 34, 68, 2, 11, 78, 2, 231, 36, 85, 2, 11, 73, - 2, 147, 34, 84, 10, 78, 84, 38, 83, 240, 3, 5, 77, 82, 65, 67, 72, 145, - 14, 4, 66, 79, 82, 90, 4, 32, 3, 82, 69, 83, 251, 1, 73, 2, 21, 3, 86, - 69, 84, 2, 231, 17, 76, 14, 76, 4, 72, 65, 77, 73, 18, 76, 40, 3, 79, 66, - 89, 16, 2, 82, 89, 87, 85, 2, 219, 3, 76, 2, 11, 89, 2, 11, 85, 2, 151, - 33, 67, 2, 223, 31, 76, 6, 28, 2, 85, 75, 219, 32, 90, 5, 21, 3, 32, 84, - 73, 2, 11, 75, 2, 251, 15, 72, 2, 11, 70, 2, 11, 73, 2, 11, 83, 2, 215, - 30, 77, 11, 11, 32, 8, 44, 7, 75, 76, 89, 85, 67, 72, 69, 83, 80, 6, 64, - 9, 78, 69, 80, 79, 83, 84, 79, 89, 65, 14, 80, 163, 14, 86, 2, 39, 78, 2, - 25, 4, 79, 86, 79, 68, 2, 143, 14, 78, 2, 223, 22, 77, 14, 40, 2, 66, 76, - 41, 4, 83, 79, 75, 65, 2, 11, 65, 2, 11, 75, 2, 243, 30, 79, 13, 11, 32, - 10, 30, 75, 142, 26, 84, 39, 83, 6, 104, 11, 76, 89, 85, 67, 72, 69, 86, - 65, 89, 65, 32, 185, 25, 10, 82, 89, 85, 75, 79, 86, 65, 89, 65, 32, 4, - 202, 16, 78, 251, 8, 83, 14, 58, 65, 88, 8, 69, 82, 69, 86, 79, 68, 75, - 65, 27, 79, 6, 44, 3, 82, 65, 75, 222, 19, 76, 211, 5, 85, 2, 11, 76, 2, - 11, 73, 2, 171, 28, 84, 5, 153, 15, 2, 32, 78, 4, 68, 5, 68, 67, 72, 65, - 83, 245, 9, 7, 76, 75, 85, 76, 73, 90, 77, 2, 11, 72, 2, 219, 4, 73, 4, - 64, 11, 69, 86, 69, 82, 83, 69, 68, 32, 67, 72, 69, 139, 26, 79, 2, 25, - 4, 76, 89, 85, 83, 2, 215, 17, 84, 126, 96, 9, 75, 65, 77, 69, 89, 84, - 83, 65, 32, 148, 2, 8, 76, 79, 90, 72, 73, 84, 73, 69, 119, 84, 22, 128, - 1, 13, 68, 86, 79, 69, 67, 72, 69, 76, 78, 65, 89, 65, 32, 44, 7, 75, 76, - 89, 85, 67, 72, 69, 74, 84, 218, 18, 77, 119, 83, 8, 218, 10, 75, 110, - 78, 178, 8, 80, 75, 83, 6, 40, 5, 86, 65, 89, 65, 32, 243, 10, 78, 4, - 178, 15, 84, 183, 4, 83, 4, 162, 15, 73, 147, 4, 82, 9, 11, 32, 6, 48, 2, - 83, 32, 25, 6, 90, 65, 75, 82, 89, 84, 4, 166, 4, 75, 95, 90, 2, 11, 79, - 2, 211, 22, 69, 96, 92, 4, 65, 84, 89, 65, 172, 4, 6, 79, 80, 73, 84, 83, - 65, 189, 1, 5, 82, 69, 76, 65, 32, 23, 11, 32, 20, 76, 2, 83, 32, 236, 2, - 9, 90, 65, 75, 82, 89, 84, 65, 89, 65, 159, 5, 78, 14, 160, 1, 14, 68, - 86, 85, 77, 89, 65, 32, 90, 65, 80, 89, 65, 84, 89, 28, 7, 75, 82, 89, - 90, 72, 69, 77, 24, 2, 82, 79, 17, 8, 90, 65, 80, 89, 65, 84, 79, 89, 2, - 11, 77, 2, 215, 19, 73, 5, 189, 1, 2, 32, 73, 2, 203, 2, 71, 7, 21, 3, - 32, 73, 32, 4, 54, 75, 37, 9, 80, 79, 68, 67, 72, 65, 83, 72, 73, 2, 11, - 82, 2, 21, 3, 89, 90, 72, 2, 211, 1, 69, 5, 17, 2, 32, 83, 2, 17, 2, 32, - 90, 2, 29, 5, 65, 80, 89, 65, 84, 2, 11, 79, 2, 199, 17, 89, 7, 11, 32, - 4, 68, 6, 83, 32, 79, 67, 72, 75, 29, 7, 87, 73, 84, 72, 32, 83, 79, 2, - 11, 79, 2, 215, 16, 77, 2, 45, 9, 82, 79, 67, 72, 89, 65, 32, 78, 79, 2, - 11, 90, 2, 163, 7, 72, 68, 140, 1, 9, 68, 86, 79, 69, 67, 72, 69, 76, 78, - 162, 1, 75, 78, 78, 174, 1, 71, 152, 3, 8, 77, 82, 65, 67, 72, 78, 79, - 84, 42, 80, 75, 84, 10, 18, 79, 71, 65, 6, 64, 7, 80, 79, 86, 79, 68, 78, - 65, 189, 5, 4, 75, 82, 89, 90, 4, 17, 2, 89, 65, 5, 17, 2, 32, 75, 2, - 145, 5, 4, 76, 89, 85, 67, 26, 48, 6, 76, 89, 85, 67, 72, 69, 77, 2, 82, - 89, 4, 22, 78, 203, 6, 80, 2, 157, 8, 9, 69, 80, 79, 83, 84, 79, 89, 65, - 78, 22, 48, 7, 85, 75, 79, 86, 65, 89, 65, 195, 3, 90, 21, 11, 32, 18, - 54, 71, 236, 2, 5, 84, 82, 89, 65, 83, 179, 2, 80, 14, 21, 3, 82, 79, 77, - 14, 32, 4, 78, 65, 89, 65, 47, 79, 5, 237, 1, 7, 32, 87, 73, 84, 72, 32, - 83, 10, 92, 10, 75, 82, 89, 90, 72, 69, 86, 65, 89, 65, 17, 9, 80, 79, - 86, 79, 68, 78, 65, 89, 65, 5, 175, 2, 32, 7, 33, 6, 32, 87, 73, 84, 72, - 32, 4, 24, 2, 68, 79, 23, 83, 2, 41, 2, 85, 66, 2, 21, 3, 73, 78, 71, 2, - 133, 6, 6, 76, 69, 32, 90, 65, 80, 2, 175, 6, 75, 2, 237, 5, 3, 72, 69, - 86, 2, 11, 73, 2, 11, 75, 2, 187, 5, 72, 6, 22, 79, 187, 3, 82, 4, 28, 2, - 76, 85, 187, 1, 86, 2, 163, 1, 80, 8, 80, 5, 82, 89, 65, 83, 79, 153, 2, - 10, 73, 75, 72, 65, 89, 65, 32, 80, 85, 84, 6, 62, 80, 44, 4, 83, 84, 82, - 69, 173, 1, 4, 71, 76, 65, 83, 2, 17, 2, 79, 86, 2, 193, 1, 2, 79, 68, 2, - 171, 1, 76, 16, 88, 12, 75, 76, 89, 85, 67, 72, 69, 86, 65, 89, 65, 32, - 38, 77, 46, 80, 38, 84, 39, 83, 8, 34, 77, 46, 80, 38, 84, 39, 83, 2, 25, - 4, 82, 65, 67, 72, 2, 247, 1, 78, 2, 11, 82, 2, 205, 1, 2, 79, 83, 2, 11, - 82, 2, 11, 69, 2, 11, 83, 2, 157, 1, 4, 86, 69, 84, 76, 6, 30, 65, 121, - 3, 77, 69, 89, 4, 32, 4, 78, 79, 90, 72, 31, 80, 2, 11, 69, 2, 151, 3, - 75, 2, 17, 2, 89, 65, 2, 11, 84, 2, 11, 65, 2, 35, 89, 2, 11, 84, 2, 11, - 83, 2, 183, 2, 65, 10, 108, 11, 68, 73, 82, 69, 67, 84, 73, 79, 78, 32, - 70, 28, 3, 75, 82, 89, 28, 5, 76, 69, 86, 69, 76, 35, 82, 2, 11, 76, 2, - 159, 1, 73, 2, 11, 90, 2, 143, 1, 72, 4, 11, 45, 4, 114, 50, 3, 51, 2, - 11, 79, 2, 83, 71, 8, 26, 78, 34, 83, 15, 74, 4, 18, 66, 27, 74, 2, 11, - 83, 2, 11, 80, 3, 0, + 131, 179, 1, 80, 4, 26, 82, 191, 139, 1, 87, 2, 17, 2, 69, 84, 2, 191, + 178, 1, 84, 4, 11, 32, 4, 18, 67, 23, 83, 2, 199, 200, 1, 85, 2, 143, 37, + 84, 2, 141, 64, 2, 79, 87, 16, 34, 65, 150, 1, 69, 231, 1, 79, 2, 25, 4, + 82, 68, 32, 83, 2, 45, 9, 72, 69, 76, 76, 32, 70, 76, 79, 80, 2, 17, 2, + 80, 89, 2, 17, 2, 32, 68, 2, 11, 73, 2, 231, 195, 1, 83, 10, 22, 65, 151, + 12, 88, 8, 30, 82, 29, 3, 86, 89, 32, 4, 11, 84, 5, 211, 15, 32, 4, 74, + 67, 53, 14, 83, 65, 76, 84, 73, 82, 69, 32, 87, 73, 84, 72, 32, 82, 2, + 17, 2, 72, 69, 2, 11, 67, 2, 231, 131, 1, 75, 2, 167, 19, 79, 4, 60, 8, + 82, 73, 90, 79, 78, 84, 65, 76, 249, 42, 2, 85, 82, 2, 201, 31, 2, 32, + 69, 22, 42, 65, 116, 3, 69, 70, 84, 175, 1, 79, 4, 24, 2, 82, 71, 19, 84, + 2, 243, 32, 69, 2, 11, 73, 2, 11, 78, 2, 17, 2, 32, 67, 2, 11, 82, 2, + 203, 42, 79, 12, 58, 32, 77, 10, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, + 6, 44, 6, 76, 65, 78, 69, 32, 77, 247, 24, 80, 2, 11, 69, 2, 227, 10, 82, + 6, 150, 2, 80, 206, 24, 83, 51, 84, 6, 160, 1, 4, 87, 69, 82, 32, 221, 8, + 30, 90, 69, 78, 71, 69, 32, 67, 79, 78, 84, 65, 73, 78, 73, 78, 71, 32, + 66, 76, 65, 67, 75, 32, 83, 77, 65, 76, 76, 32, 76, 4, 44, 3, 76, 69, 70, + 1, 4, 82, 73, 71, 72, 2, 11, 84, 2, 17, 2, 32, 80, 2, 203, 5, 79, 12, 68, + 6, 69, 68, 73, 85, 77, 32, 117, 7, 79, 79, 78, 32, 83, 69, 76, 10, 30, + 68, 54, 83, 227, 6, 76, 2, 11, 73, 2, 11, 65, 2, 11, 77, 2, 191, 46, 79, + 6, 134, 27, 84, 50, 77, 59, 81, 2, 11, 69, 2, 223, 187, 1, 78, 2, 11, 73, + 2, 247, 189, 1, 66, 6, 18, 65, 87, 69, 2, 37, 7, 82, 65, 76, 76, 69, 76, + 79, 2, 11, 71, 2, 11, 82, 2, 183, 172, 1, 65, 4, 11, 78, 4, 174, 2, 84, + 203, 46, 78, 2, 21, 3, 85, 69, 83, 2, 217, 120, 9, 84, 73, 79, 78, 32, + 77, 65, 82, 75, 14, 34, 69, 25, 4, 73, 71, 72, 84, 2, 129, 21, 2, 67, 84, + 12, 60, 10, 45, 80, 79, 73, 78, 84, 73, 78, 71, 32, 211, 17, 32, 8, 30, + 80, 202, 19, 83, 51, 84, 4, 18, 69, 55, 79, 2, 11, 78, 2, 11, 84, 2, 11, + 65, 2, 199, 106, 71, 2, 11, 73, 2, 11, 78, 2, 159, 124, 84, 56, 118, 67, + 32, 3, 69, 83, 65, 18, 72, 58, 77, 166, 1, 80, 68, 5, 81, 85, 65, 82, 69, + 180, 6, 2, 85, 78, 199, 12, 84, 2, 145, 40, 4, 73, 83, 83, 79, 2, 167, + 37, 77, 2, 21, 3, 79, 71, 73, 2, 189, 123, 4, 32, 80, 73, 69, 8, 32, 4, + 65, 76, 76, 32, 115, 73, 6, 18, 76, 71, 83, 2, 11, 79, 2, 11, 90, 2, 11, + 69, 2, 11, 78, 2, 223, 160, 1, 71, 4, 226, 19, 84, 107, 81, 2, 231, 46, + 76, 2, 21, 3, 65, 68, 69, 2, 11, 32, 2, 11, 83, 2, 143, 154, 1, 85, 29, + 11, 32, 26, 114, 66, 36, 17, 67, 79, 78, 84, 65, 73, 78, 73, 78, 71, 32, + 66, 76, 65, 67, 75, 32, 85, 5, 87, 73, 84, 72, 32, 2, 17, 2, 85, 84, 2, + 179, 101, 84, 6, 54, 86, 194, 17, 83, 37, 6, 77, 69, 68, 73, 85, 77, 2, + 169, 17, 3, 69, 82, 89, 18, 150, 1, 76, 50, 82, 214, 1, 85, 144, 1, 17, + 86, 69, 82, 84, 73, 67, 65, 76, 32, 66, 73, 83, 69, 67, 84, 73, 78, 249, + 19, 6, 67, 69, 78, 84, 82, 69, 6, 18, 69, 15, 79, 2, 67, 70, 4, 247, 1, + 87, 4, 18, 73, 115, 79, 2, 17, 2, 71, 72, 2, 33, 6, 84, 87, 65, 82, 68, + 83, 2, 11, 32, 2, 11, 84, 2, 11, 73, 2, 235, 174, 1, 67, 2, 29, 5, 85, + 78, 68, 69, 68, 2, 21, 3, 32, 67, 79, 2, 185, 32, 2, 82, 78, 4, 17, 2, + 80, 80, 4, 21, 3, 69, 82, 32, 4, 44, 3, 76, 69, 70, 1, 4, 82, 73, 71, 72, + 2, 33, 6, 84, 32, 81, 85, 65, 68, 2, 167, 36, 82, 2, 187, 20, 71, 11, 11, + 32, 8, 84, 12, 66, 69, 72, 73, 78, 68, 32, 67, 76, 79, 85, 68, 69, 5, 87, + 73, 84, 72, 32, 5, 29, 5, 32, 87, 73, 84, 72, 2, 17, 2, 32, 82, 2, 147, + 47, 65, 4, 38, 82, 29, 5, 83, 77, 65, 76, 76, 2, 11, 65, 2, 199, 112, 89, + 2, 11, 32, 2, 21, 3, 67, 76, 79, 2, 207, 103, 85, 10, 38, 79, 54, 69, 62, + 82, 203, 1, 87, 2, 49, 10, 85, 67, 72, 84, 79, 78, 69, 32, 84, 69, 2, 17, + 2, 76, 69, 2, 11, 80, 2, 11, 72, 2, 167, 17, 79, 4, 148, 1, 4, 65, 80, + 69, 90, 33, 28, 73, 65, 78, 71, 76, 69, 32, 67, 79, 78, 84, 65, 73, 78, + 73, 78, 71, 32, 83, 77, 65, 76, 76, 32, 87, 72, 73, 84, 2, 11, 73, 2, + 203, 154, 1, 85, 2, 135, 4, 69, 2, 11, 79, 2, 85, 19, 45, 87, 65, 89, 32, + 76, 69, 70, 84, 32, 87, 65, 89, 32, 84, 82, 65, 70, 70, 2, 11, 73, 2, + 143, 170, 1, 67, 12, 62, 32, 185, 1, 10, 45, 80, 79, 73, 78, 84, 73, 78, + 71, 32, 4, 11, 80, 4, 41, 8, 79, 73, 78, 84, 73, 78, 71, 32, 4, 18, 66, + 75, 73, 2, 21, 3, 65, 67, 75, 2, 25, 4, 72, 65, 78, 68, 2, 17, 2, 32, 73, + 2, 17, 2, 78, 68, 2, 211, 25, 69, 8, 54, 67, 42, 83, 125, 7, 84, 82, 73, + 65, 78, 71, 76, 2, 21, 3, 72, 69, 86, 2, 199, 87, 82, 2, 25, 4, 77, 65, + 76, 76, 2, 17, 2, 32, 84, 2, 17, 2, 82, 73, 2, 11, 65, 2, 11, 78, 2, 11, + 71, 2, 231, 143, 1, 76, 4, 11, 69, 5, 11, 32, 2, 11, 87, 2, 209, 18, 3, + 73, 84, 72, 10, 44, 6, 84, 73, 67, 65, 76, 32, 143, 2, 89, 8, 58, 69, 48, + 7, 82, 69, 67, 84, 65, 78, 71, 147, 1, 66, 2, 11, 76, 2, 17, 2, 76, 73, + 2, 183, 25, 80, 4, 17, 2, 76, 69, 5, 37, 7, 32, 87, 73, 84, 72, 32, 72, + 2, 37, 7, 79, 82, 73, 90, 79, 78, 84, 2, 17, 2, 65, 76, 2, 11, 32, 2, 11, + 66, 2, 223, 106, 65, 2, 17, 2, 32, 83, 2, 11, 77, 2, 21, 3, 65, 76, 76, + 2, 17, 2, 32, 83, 2, 11, 81, 2, 11, 85, 2, 11, 65, 2, 231, 139, 1, 82, 2, + 11, 69, 2, 11, 68, 2, 17, 2, 32, 82, 2, 21, 3, 73, 71, 72, 2, 11, 84, 2, + 11, 87, 2, 241, 4, 4, 65, 82, 68, 83, 100, 140, 1, 10, 68, 69, 45, 72, + 69, 65, 68, 69, 68, 32, 132, 4, 4, 71, 71, 76, 89, 124, 6, 76, 84, 69, + 68, 32, 70, 42, 78, 189, 1, 2, 82, 69, 80, 128, 1, 3, 76, 69, 70, 0, 4, + 82, 73, 71, 72, 12, 4, 68, 79, 87, 78, 0, 2, 85, 80, 32, 3, 78, 79, 82, + 1, 3, 83, 79, 85, 10, 11, 84, 10, 109, 5, 87, 65, 82, 68, 83, 20, 21, 3, + 84, 72, 32, 20, 32, 2, 69, 65, 1, 2, 87, 69, 10, 17, 2, 83, 84, 10, 11, + 32, 10, 110, 72, 0, 6, 86, 69, 82, 89, 32, 72, 28, 5, 76, 73, 71, 72, 84, + 0, 6, 77, 69, 68, 73, 85, 77, 23, 66, 2, 25, 4, 69, 65, 86, 89, 2, 17, 2, + 32, 66, 2, 21, 3, 65, 82, 66, 2, 11, 32, 2, 11, 65, 2, 11, 82, 2, 11, 82, + 2, 135, 30, 79, 2, 17, 2, 32, 86, 2, 11, 69, 2, 33, 6, 82, 84, 73, 67, + 65, 76, 2, 11, 32, 2, 11, 76, 2, 11, 73, 2, 219, 132, 1, 78, 2, 11, 76, + 2, 11, 79, 2, 151, 93, 87, 12, 46, 68, 106, 69, 190, 17, 75, 163, 136, 1, + 71, 6, 22, 32, 143, 28, 79, 4, 44, 2, 67, 72, 221, 17, 4, 66, 76, 79, 87, + 2, 11, 73, 2, 155, 131, 1, 77, 2, 11, 32, 2, 121, 3, 71, 76, 65, 4, 36, + 5, 68, 32, 75, 69, 89, 51, 76, 2, 17, 2, 66, 79, 2, 11, 65, 2, 207, 82, + 82, 2, 11, 69, 2, 247, 90, 83, 30, 66, 77, 134, 3, 82, 254, 13, 78, 226, + 64, 79, 129, 9, 2, 76, 70, 14, 36, 2, 65, 78, 137, 2, 2, 69, 78, 13, 72, + 12, 32, 87, 73, 84, 72, 32, 66, 85, 78, 78, 89, 32, 29, 2, 83, 32, 2, 11, + 69, 2, 143, 6, 65, 8, 52, 2, 66, 79, 16, 2, 67, 76, 54, 83, 199, 12, 72, + 2, 251, 13, 79, 2, 11, 79, 2, 11, 84, 2, 11, 72, 2, 167, 88, 69, 2, 17, + 2, 65, 78, 2, 131, 10, 68, 2, 11, 83, 2, 11, 32, 2, 11, 83, 2, 11, 89, 2, + 17, 2, 77, 66, 2, 187, 9, 79, 10, 72, 2, 68, 32, 156, 1, 3, 76, 68, 32, + 32, 2, 82, 73, 247, 146, 1, 77, 4, 56, 9, 83, 69, 80, 65, 82, 65, 84, 79, + 82, 231, 85, 74, 2, 17, 2, 32, 77, 2, 21, 3, 73, 68, 68, 2, 11, 76, 2, + 11, 69, 2, 11, 32, 2, 235, 66, 68, 2, 11, 77, 2, 251, 146, 1, 65, 2, 11, + 69, 2, 199, 85, 68, 10, 56, 7, 65, 80, 80, 69, 68, 32, 80, 30, 69, 163, + 1, 73, 2, 241, 78, 3, 82, 69, 83, 6, 48, 2, 65, 84, 80, 3, 83, 84, 76, + 215, 110, 78, 2, 11, 72, 2, 11, 32, 2, 11, 80, 2, 25, 4, 82, 79, 68, 85, + 2, 223, 116, 67, 2, 11, 69, 2, 147, 83, 82, 2, 11, 84, 2, 11, 73, 2, 17, + 2, 78, 71, 2, 17, 2, 32, 72, 2, 11, 65, 2, 243, 73, 78, 34, 76, 2, 32, + 73, 138, 1, 45, 28, 7, 73, 65, 78, 71, 81, 73, 32, 183, 85, 79, 2, 53, + 11, 78, 32, 65, 32, 82, 69, 67, 84, 65, 78, 71, 2, 11, 76, 2, 11, 69, 2, + 11, 32, 2, 11, 66, 2, 11, 79, 2, 191, 142, 1, 88, 2, 11, 82, 2, 215, 124, + 65, 28, 48, 5, 66, 76, 65, 67, 75, 1, 3, 82, 69, 68, 14, 11, 32, 14, 130, + 1, 67, 72, 4, 69, 76, 69, 80, 28, 4, 71, 69, 78, 69, 46, 72, 40, 4, 83, + 79, 76, 68, 193, 11, 6, 77, 65, 78, 68, 65, 82, 4, 24, 2, 65, 78, 19, 72, + 2, 195, 60, 78, 2, 229, 59, 3, 65, 82, 73, 2, 11, 72, 2, 219, 72, 65, 2, + 11, 82, 2, 11, 65, 2, 207, 139, 1, 76, 2, 11, 79, 2, 11, 82, 2, 207, 116, + 83, 2, 167, 77, 73, 248, 19, 54, 65, 206, 2, 69, 226, 11, 73, 217, 35, 2, + 79, 45, 8, 84, 16, 78, 71, 81, 73, 78, 32, 83, 73, 71, 78, 32, 83, 76, + 79, 87, 32, 199, 1, 87, 6, 28, 3, 79, 78, 69, 51, 84, 2, 17, 2, 32, 66, + 2, 11, 69, 2, 131, 109, 65, 4, 60, 9, 72, 82, 69, 69, 32, 72, 65, 76, 70, + 1, 2, 87, 79, 2, 21, 3, 32, 66, 69, 2, 11, 65, 2, 223, 74, 84, 2, 11, 78, + 2, 11, 73, 2, 241, 74, 2, 78, 71, 98, 60, 4, 76, 76, 79, 87, 62, 78, 53, + 5, 90, 73, 68, 73, 32, 2, 17, 2, 32, 72, 2, 11, 69, 2, 11, 65, 2, 227, + 106, 82, 2, 11, 32, 2, 11, 83, 2, 11, 73, 2, 251, 54, 71, 94, 112, 10, + 67, 79, 77, 66, 73, 78, 73, 78, 71, 32, 76, 5, 72, 89, 80, 72, 69, 17, 7, + 76, 69, 84, 84, 69, 82, 32, 4, 44, 3, 77, 65, 68, 13, 4, 72, 65, 77, 90, + 2, 11, 68, 2, 215, 67, 65, 2, 215, 27, 78, 88, 250, 1, 67, 50, 77, 18, + 68, 42, 69, 58, 72, 30, 75, 22, 71, 2, 81, 32, 3, 76, 65, 77, 98, 78, 18, + 80, 30, 83, 66, 84, 28, 2, 86, 65, 126, 87, 14, 79, 18, 88, 52, 3, 89, + 79, 84, 154, 1, 90, 134, 53, 82, 238, 48, 66, 254, 5, 85, 162, 14, 70, 3, + 74, 6, 22, 72, 147, 114, 73, 4, 22, 72, 251, 113, 73, 2, 247, 113, 73, 4, + 11, 65, 4, 178, 130, 1, 68, 3, 76, 8, 42, 76, 142, 50, 89, 226, 79, 84, + 3, 87, 2, 71, 73, 4, 150, 112, 65, 147, 15, 72, 4, 18, 72, 15, 65, 2, 11, + 65, 2, 163, 129, 1, 70, 5, 11, 32, 2, 11, 87, 2, 21, 3, 73, 84, 72, 2, + 17, 2, 32, 68, 2, 11, 79, 2, 187, 3, 84, 2, 207, 48, 85, 4, 202, 105, 72, + 215, 22, 69, 8, 46, 72, 246, 47, 73, 194, 9, 65, 163, 70, 69, 2, 243, 47, + 73, 4, 238, 104, 72, 215, 22, 65, 5, 37, 7, 32, 65, 76, 84, 69, 82, 78, + 2, 17, 2, 65, 84, 2, 11, 69, 2, 11, 32, 2, 11, 70, 2, 11, 79, 2, 227, + 109, 82, 2, 11, 65, 2, 159, 126, 87, 4, 22, 72, 251, 125, 65, 2, 11, 69, + 2, 139, 46, 89, 5, 41, 8, 32, 87, 73, 84, 72, 32, 67, 73, 2, 41, 8, 82, + 67, 85, 77, 70, 76, 69, 88, 2, 11, 32, 2, 11, 65, 2, 11, 66, 2, 11, 79, + 2, 255, 101, 86, 6, 22, 65, 175, 124, 69, 5, 171, 124, 76, 140, 19, 34, + 32, 161, 35, 3, 78, 32, 89, 138, 19, 88, 8, 82, 65, 68, 73, 67, 65, 76, + 32, 181, 7, 9, 83, 89, 76, 76, 65, 66, 76, 69, 32, 110, 170, 1, 66, 42, + 67, 86, 68, 42, 71, 74, 72, 66, 74, 66, 75, 30, 76, 30, 77, 26, 78, 74, + 80, 26, 83, 74, 84, 30, 86, 30, 89, 30, 90, 250, 35, 81, 198, 49, 87, + 235, 30, 79, 4, 22, 66, 247, 64, 85, 2, 163, 93, 85, 12, 42, 85, 18, 89, + 178, 98, 72, 203, 22, 73, 2, 135, 121, 79, 7, 130, 121, 80, 3, 84, 4, 22, + 68, 215, 120, 85, 2, 247, 63, 85, 10, 42, 71, 238, 91, 79, 162, 28, 69, + 15, 65, 4, 162, 89, 85, 235, 30, 79, 8, 22, 88, 243, 88, 77, 6, 238, 88, + 85, 202, 2, 73, 163, 28, 79, 8, 22, 74, 167, 119, 79, 6, 246, 90, 85, + 218, 5, 73, 215, 22, 89, 4, 206, 90, 73, 175, 28, 69, 6, 190, 54, 73, + 199, 7, 89, 4, 182, 118, 79, 15, 73, 8, 30, 89, 26, 90, 199, 90, 66, 4, + 254, 117, 73, 3, 79, 2, 231, 117, 85, 4, 182, 89, 85, 3, 89, 10, 22, 72, + 223, 97, 83, 8, 214, 60, 85, 178, 28, 65, 162, 28, 79, 15, 89, 4, 214, + 88, 65, 175, 28, 85, 4, 138, 60, 85, 211, 56, 69, 4, 158, 88, 73, 175, + 28, 79, 10, 54, 85, 224, 62, 2, 90, 73, 238, 24, 79, 175, 28, 65, 4, 246, + 115, 80, 3, 82, 156, 18, 134, 2, 66, 134, 1, 67, 162, 1, 68, 110, 70, 50, + 71, 150, 1, 72, 138, 3, 73, 134, 1, 74, 98, 75, 54, 76, 62, 77, 134, 1, + 78, 234, 4, 80, 54, 81, 2, 89, 46, 82, 162, 1, 83, 134, 1, 84, 102, 86, + 82, 87, 58, 88, 50, 90, 140, 2, 2, 85, 79, 66, 65, 2, 79, 107, 69, 132, + 1, 66, 66, 238, 21, 85, 150, 1, 69, 26, 73, 42, 65, 2, 79, 67, 89, 64, + 234, 21, 85, 150, 1, 69, 26, 73, 42, 65, 2, 79, 3, 89, 122, 66, 72, 238, + 20, 85, 150, 1, 69, 26, 73, 42, 65, 2, 79, 67, 89, 54, 46, 85, 146, 22, + 65, 2, 69, 2, 79, 67, 89, 19, 142, 22, 79, 106, 82, 230, 88, 80, 3, 88, + 106, 58, 68, 138, 15, 85, 134, 5, 73, 94, 69, 66, 65, 3, 79, 54, 210, 19, + 85, 58, 73, 94, 69, 66, 65, 3, 79, 42, 182, 20, 79, 66, 65, 2, 73, 2, 89, + 67, 85, 116, 58, 71, 214, 15, 85, 146, 4, 73, 42, 65, 2, 69, 3, 79, 56, + 50, 73, 162, 15, 85, 186, 4, 65, 2, 69, 3, 79, 13, 150, 19, 69, 142, 90, + 84, 3, 88, 246, 1, 78, 73, 30, 76, 58, 77, 54, 78, 110, 88, 50, 85, 254, + 15, 69, 66, 65, 3, 79, 6, 198, 19, 69, 231, 88, 84, 64, 238, 16, 85, 58, + 73, 94, 69, 2, 79, 66, 65, 67, 89, 58, 182, 16, 85, 58, 73, 158, 1, 65, + 2, 79, 35, 89, 42, 46, 79, 34, 85, 202, 16, 69, 26, 73, 43, 65, 6, 242, + 106, 80, 2, 84, 3, 88, 6, 238, 17, 79, 231, 88, 84, 46, 46, 85, 254, 15, + 69, 26, 73, 42, 65, 3, 79, 8, 187, 16, 79, 19, 42, 84, 130, 16, 69, 206, + 89, 80, 3, 88, 5, 11, 69, 2, 11, 82, 2, 11, 65, 2, 11, 84, 2, 11, 73, 2, + 11, 79, 2, 191, 39, 78, 106, 50, 74, 190, 10, 85, 146, 4, 73, 42, 79, 67, + 89, 50, 158, 13, 85, 174, 1, 73, 42, 79, 3, 89, 56, 242, 12, 85, 58, 73, + 158, 1, 65, 2, 69, 3, 79, 70, 218, 9, 85, 250, 3, 69, 26, 73, 42, 65, 2, + 79, 67, 89, 106, 70, 71, 218, 8, 85, 158, 3, 73, 158, 1, 65, 2, 79, 2, + 89, 107, 69, 44, 186, 11, 85, 150, 1, 69, 66, 65, 2, 79, 105, 2, 73, 69, + 248, 2, 102, 66, 54, 68, 94, 71, 90, 74, 46, 82, 50, 89, 78, 90, 134, 7, + 85, 58, 73, 94, 65, 2, 69, 67, 79, 54, 202, 10, 73, 158, 1, 65, 2, 79, + 66, 85, 3, 89, 46, 46, 73, 198, 10, 69, 66, 65, 2, 79, 67, 85, 13, 234, + 11, 69, 230, 88, 80, 2, 84, 3, 88, 34, 60, 2, 85, 79, 218, 9, 69, 0, 2, + 73, 69, 66, 65, 3, 79, 7, 226, 99, 84, 3, 88, 50, 230, 1, 85, 242, 7, 73, + 42, 79, 67, 89, 46, 146, 9, 79, 66, 65, 2, 69, 66, 85, 3, 89, 38, 30, 85, + 222, 8, 73, 43, 79, 15, 194, 8, 79, 142, 90, 80, 2, 84, 3, 88, 56, 62, + 85, 206, 4, 79, 178, 2, 73, 158, 1, 65, 66, 89, 43, 69, 15, 254, 8, 79, + 2, 82, 230, 88, 80, 3, 88, 60, 150, 6, 85, 58, 73, 158, 1, 65, 2, 79, 67, + 89, 56, 254, 2, 85, 146, 4, 73, 42, 79, 67, 89, 100, 58, 82, 254, 4, 85, + 150, 1, 69, 66, 65, 2, 79, 67, 89, 48, 46, 85, 162, 6, 69, 2, 79, 66, 89, + 43, 65, 17, 134, 7, 79, 2, 82, 230, 88, 80, 2, 84, 3, 88, 176, 1, 70, 83, + 158, 3, 72, 50, 85, 58, 73, 94, 69, 66, 65, 2, 79, 67, 89, 56, 130, 4, + 73, 94, 69, 66, 65, 2, 79, 2, 85, 67, 89, 56, 46, 85, 158, 3, 73, 94, 69, + 66, 65, 3, 79, 21, 182, 4, 79, 106, 82, 230, 88, 80, 2, 84, 3, 88, 60, + 54, 69, 166, 3, 73, 42, 65, 2, 79, 66, 85, 3, 89, 4, 150, 93, 80, 3, 88, + 28, 38, 85, 206, 2, 69, 2, 79, 67, 65, 9, 203, 2, 79, 40, 210, 2, 73, 42, + 79, 66, 89, 41, 2, 85, 79, 178, 1, 66, 72, 50, 85, 58, 73, 42, 90, 54, + 69, 66, 65, 2, 79, 67, 89, 54, 46, 85, 214, 1, 65, 2, 69, 2, 79, 67, 89, + 19, 146, 1, 79, 170, 1, 82, 230, 88, 80, 2, 84, 3, 88, 15, 90, 69, 142, + 90, 80, 2, 84, 3, 88, 58, 50, 69, 2, 79, 26, 73, 42, 65, 34, 85, 35, 89, + 7, 138, 90, 80, 3, 88, 17, 38, 69, 206, 89, 80, 2, 84, 3, 88, 9, 202, 89, + 80, 2, 84, 3, 88, 11, 70, 82, 230, 88, 80, 3, 88, 13, 38, 82, 230, 88, + 80, 2, 84, 3, 88, 5, 227, 88, 88, 2, 219, 7, 65, 2, 207, 57, 89, 180, 4, + 252, 1, 10, 32, 78, 79, 84, 65, 84, 73, 79, 78, 32, 224, 6, 16, 65, 78, + 65, 66, 65, 90, 65, 82, 32, 83, 81, 85, 65, 82, 69, 32, 210, 15, 69, 180, + 2, 6, 73, 80, 80, 69, 82, 45, 96, 8, 78, 65, 77, 69, 78, 78, 89, 32, 188, + 33, 3, 79, 77, 66, 235, 26, 87, 26, 144, 1, 9, 66, 65, 71, 32, 77, 69, + 77, 66, 69, 42, 82, 104, 6, 68, 79, 77, 65, 73, 78, 60, 3, 76, 69, 70, + 154, 1, 83, 145, 2, 3, 84, 89, 80, 2, 11, 82, 2, 11, 83, 2, 215, 83, 72, + 8, 100, 4, 65, 78, 71, 69, 60, 3, 73, 71, 72, 221, 1, 11, 69, 76, 65, 84, + 73, 79, 78, 65, 76, 32, 67, 2, 173, 3, 11, 32, 65, 78, 84, 73, 82, 69, + 83, 84, 82, 73, 4, 17, 2, 84, 32, 4, 60, 4, 73, 77, 65, 71, 13, 7, 66, + 73, 78, 68, 73, 78, 71, 2, 11, 69, 2, 25, 4, 32, 66, 82, 65, 2, 11, 67, + 2, 175, 29, 75, 8, 44, 6, 67, 72, 69, 77, 65, 32, 211, 1, 80, 6, 18, 67, + 71, 80, 2, 17, 2, 79, 77, 2, 11, 80, 2, 11, 79, 2, 11, 83, 2, 107, 73, 4, + 30, 73, 41, 3, 82, 79, 74, 2, 11, 80, 2, 11, 73, 2, 163, 80, 78, 2, 11, + 69, 2, 11, 67, 2, 11, 84, 2, 87, 73, 2, 139, 52, 79, 2, 11, 69, 2, 11, + 32, 2, 11, 67, 2, 11, 79, 2, 11, 76, 2, 11, 79, 2, 223, 79, 78, 144, 1, + 240, 1, 2, 67, 76, 68, 7, 73, 78, 73, 84, 73, 65, 76, 204, 2, 14, 70, 73, + 78, 65, 76, 32, 67, 79, 78, 83, 79, 78, 65, 78, 16, 7, 76, 69, 84, 84, + 69, 82, 32, 148, 3, 5, 77, 65, 82, 75, 32, 206, 1, 83, 181, 3, 6, 86, 79, + 87, 69, 76, 32, 14, 64, 5, 79, 83, 73, 78, 71, 137, 1, 6, 85, 83, 84, 69, + 82, 45, 4, 11, 32, 4, 64, 12, 68, 79, 85, 66, 76, 69, 45, 76, 73, 78, 69, + 68, 23, 72, 2, 17, 2, 32, 72, 2, 17, 2, 69, 65, 2, 215, 10, 68, 10, 96, + 13, 70, 73, 78, 65, 76, 32, 76, 69, 84, 84, 69, 82, 32, 41, 7, 73, 78, + 73, 84, 73, 65, 76, 8, 238, 72, 76, 2, 82, 2, 86, 3, 89, 2, 37, 7, 32, + 76, 69, 84, 84, 69, 82, 2, 151, 6, 32, 2, 131, 9, 84, 82, 166, 1, 68, 50, + 75, 38, 78, 46, 83, 38, 84, 46, 66, 2, 67, 2, 71, 2, 80, 2, 90, 138, 69, + 45, 2, 72, 2, 74, 2, 76, 2, 77, 2, 82, 2, 86, 2, 89, 187, 2, 65, 12, 206, + 1, 68, 2, 90, 138, 69, 72, 187, 2, 65, 6, 154, 70, 83, 14, 72, 187, 2, + 65, 8, 130, 70, 71, 2, 78, 2, 89, 187, 2, 65, 6, 214, 69, 72, 2, 83, 187, + 2, 65, 12, 42, 83, 2, 84, 138, 69, 72, 187, 2, 65, 4, 134, 69, 72, 187, + 2, 65, 8, 50, 68, 58, 83, 40, 4, 76, 79, 78, 71, 23, 84, 2, 29, 5, 79, + 85, 66, 76, 69, 2, 11, 32, 2, 11, 83, 2, 11, 72, 2, 11, 65, 2, 159, 70, + 68, 2, 17, 2, 32, 84, 2, 17, 2, 83, 72, 2, 147, 69, 69, 14, 36, 4, 73, + 71, 78, 32, 255, 2, 85, 12, 54, 65, 72, 6, 67, 65, 78, 68, 82, 65, 167, + 1, 86, 2, 11, 78, 2, 11, 85, 2, 17, 2, 83, 86, 2, 11, 65, 2, 135, 66, 82, + 6, 36, 5, 66, 73, 78, 68, 85, 15, 32, 5, 11, 32, 2, 25, 4, 87, 73, 84, + 72, 2, 17, 2, 32, 79, 2, 21, 3, 82, 78, 65, 2, 11, 77, 2, 11, 69, 2, 239, + 38, 78, 4, 11, 73, 4, 18, 82, 19, 83, 2, 219, 33, 65, 2, 11, 65, 2, 11, + 82, 2, 139, 64, 71, 2, 151, 4, 66, 20, 38, 76, 109, 5, 83, 73, 71, 78, + 32, 2, 17, 2, 69, 78, 2, 11, 71, 2, 11, 84, 2, 11, 72, 2, 11, 32, 2, 11, + 77, 2, 11, 65, 2, 135, 62, 82, 18, 86, 65, 26, 79, 2, 85, 16, 8, 82, 69, + 86, 69, 82, 83, 69, 68, 146, 64, 69, 3, 73, 4, 182, 64, 73, 3, 85, 5, + 159, 64, 69, 2, 183, 44, 32, 12, 72, 2, 66, 82, 16, 9, 82, 79, 32, 87, + 73, 68, 84, 72, 32, 203, 1, 85, 2, 147, 2, 65, 8, 32, 2, 78, 79, 86, 83, + 31, 74, 4, 26, 45, 73, 2, 78, 45, 2, 29, 5, 66, 82, 69, 65, 75, 2, 11, + 32, 2, 11, 83, 2, 163, 1, 80, 2, 11, 74, 2, 11, 79, 2, 11, 73, 2, 11, 78, + 2, 143, 5, 69, 2, 219, 61, 83, 2, 21, 3, 77, 79, 85, 2, 17, 2, 84, 72, 2, + 11, 32, 2, 11, 70, 2, 11, 65, 2, 167, 38, 67, 242, 2, 168, 1, 10, 67, 79, + 77, 66, 73, 78, 73, 78, 71, 32, 224, 18, 6, 78, 69, 85, 77, 69, 32, 181, + 38, 17, 80, 82, 73, 90, 78, 65, 75, 32, 77, 79, 68, 73, 70, 73, 69, 82, + 32, 128, 1, 144, 2, 17, 76, 79, 87, 69, 82, 32, 84, 79, 78, 65, 76, 32, + 82, 65, 78, 71, 69, 88, 5, 77, 65, 82, 75, 32, 148, 3, 18, 65, 84, 84, + 65, 67, 72, 73, 78, 71, 32, 86, 69, 82, 84, 73, 67, 65, 76, 233, 11, 17, + 84, 79, 78, 65, 76, 32, 82, 65, 78, 71, 69, 32, 77, 65, 82, 75, 32, 2, + 33, 6, 32, 73, 78, 68, 73, 67, 2, 11, 65, 2, 11, 84, 2, 11, 79, 2, 219, + 56, 82, 118, 182, 2, 67, 142, 1, 68, 104, 8, 71, 79, 82, 65, 90, 68, 79, + 32, 34, 78, 106, 75, 114, 79, 72, 2, 80, 79, 152, 2, 8, 77, 65, 76, 79, + 32, 80, 79, 86, 100, 2, 82, 65, 50, 83, 186, 1, 84, 108, 7, 86, 89, 83, + 79, 75, 79, 32, 234, 1, 90, 224, 10, 2, 76, 79, 224, 22, 4, 85, 68, 65, + 82, 129, 6, 4, 66, 79, 82, 90, 6, 60, 6, 72, 65, 83, 72, 75, 65, 29, 5, + 85, 82, 86, 69, 68, 5, 197, 47, 3, 32, 80, 79, 2, 17, 2, 32, 79, 2, 11, + 77, 2, 235, 24, 69, 4, 240, 10, 13, 69, 77, 69, 83, 84, 86, 69, 78, 78, + 89, 32, 90, 65, 217, 14, 6, 86, 79, 69, 84, 79, 67, 10, 30, 78, 89, 3, + 86, 89, 83, 8, 29, 5, 73, 90, 75, 79, 32, 8, 164, 8, 8, 83, 32, 75, 82, + 89, 90, 72, 69, 35, 79, 2, 167, 20, 79, 8, 56, 4, 82, 89, 90, 72, 150, 6, + 65, 233, 39, 2, 85, 80, 5, 21, 3, 32, 79, 78, 2, 11, 32, 2, 203, 7, 76, + 6, 220, 5, 3, 84, 83, 69, 200, 13, 5, 66, 76, 65, 67, 72, 131, 31, 78, + 18, 22, 68, 131, 2, 86, 6, 60, 7, 67, 72, 65, 83, 72, 73, 69, 201, 22, 3, + 86, 69, 82, 5, 17, 2, 32, 87, 2, 21, 3, 73, 84, 72, 2, 17, 2, 32, 86, 2, + 29, 5, 69, 82, 84, 73, 67, 2, 17, 2, 65, 76, 2, 11, 32, 2, 11, 83, 2, 11, + 84, 2, 11, 82, 2, 11, 79, 2, 131, 25, 75, 12, 29, 5, 89, 83, 72, 69, 32, + 12, 22, 83, 251, 3, 79, 8, 154, 3, 32, 229, 2, 4, 84, 82, 65, 78, 4, 28, + 2, 90, 83, 183, 5, 86, 2, 219, 37, 69, 10, 136, 1, 2, 75, 79, 16, 16, 84, + 82, 65, 78, 78, 79, 32, 77, 65, 76, 79, 32, 80, 79, 86, 89, 236, 1, 5, + 82, 69, 68, 78, 69, 231, 26, 79, 2, 239, 42, 66, 2, 11, 83, 2, 183, 22, + 72, 10, 50, 79, 16, 5, 83, 65, 84, 65, 32, 139, 36, 73, 2, 187, 28, 67, + 6, 158, 1, 79, 201, 24, 3, 83, 32, 75, 10, 24, 2, 83, 32, 95, 79, 6, 11, + 75, 6, 44, 6, 72, 79, 75, 72, 76, 79, 247, 24, 82, 4, 11, 77, 4, 17, 2, + 32, 79, 4, 11, 78, 4, 11, 32, 4, 18, 76, 31, 82, 2, 11, 69, 2, 179, 14, + 70, 2, 11, 73, 2, 11, 71, 2, 139, 14, 72, 6, 18, 65, 31, 69, 2, 249, 25, + 3, 68, 69, 82, 4, 22, 86, 239, 10, 76, 2, 199, 38, 79, 6, 60, 5, 77, 82, + 65, 67, 72, 18, 83, 1, 4, 84, 82, 69, 83, 2, 155, 10, 78, 2, 17, 2, 86, + 69, 2, 155, 6, 84, 232, 1, 128, 2, 2, 67, 72, 38, 68, 218, 1, 70, 28, 10, + 71, 79, 76, 85, 66, 67, 72, 73, 75, 32, 158, 1, 75, 160, 2, 6, 77, 69, + 67, 72, 73, 75, 184, 1, 2, 78, 69, 18, 79, 138, 2, 80, 146, 2, 82, 114, + 83, 180, 20, 9, 86, 82, 65, 75, 72, 73, 89, 65, 32, 151, 2, 90, 4, 246, + 11, 69, 209, 10, 2, 65, 83, 10, 74, 69, 98, 85, 16, 9, 86, 65, 32, 86, + 32, 67, 72, 69, 76, 187, 27, 79, 4, 76, 2, 82, 66, 213, 2, 12, 77, 69, + 83, 84, 86, 69, 78, 78, 89, 32, 75, 76, 2, 195, 34, 73, 2, 203, 34, 68, + 2, 11, 78, 2, 231, 36, 85, 2, 11, 73, 2, 147, 34, 84, 10, 78, 84, 38, 83, + 240, 3, 5, 77, 82, 65, 67, 72, 145, 14, 4, 66, 79, 82, 90, 4, 32, 3, 82, + 69, 83, 251, 1, 73, 2, 21, 3, 86, 69, 84, 2, 231, 17, 76, 14, 76, 4, 72, + 65, 77, 73, 18, 76, 40, 3, 79, 66, 89, 16, 2, 82, 89, 87, 85, 2, 219, 3, + 76, 2, 11, 89, 2, 11, 85, 2, 151, 33, 67, 2, 223, 31, 76, 6, 28, 2, 85, + 75, 219, 32, 90, 5, 21, 3, 32, 84, 73, 2, 11, 75, 2, 251, 15, 72, 2, 11, + 70, 2, 11, 73, 2, 11, 83, 2, 215, 30, 77, 11, 11, 32, 8, 44, 7, 75, 76, + 89, 85, 67, 72, 69, 83, 80, 6, 64, 9, 78, 69, 80, 79, 83, 84, 79, 89, 65, + 14, 80, 163, 14, 86, 2, 39, 78, 2, 25, 4, 79, 86, 79, 68, 2, 143, 14, 78, + 2, 223, 22, 77, 14, 40, 2, 66, 76, 41, 4, 83, 79, 75, 65, 2, 11, 65, 2, + 11, 75, 2, 243, 30, 79, 13, 11, 32, 10, 30, 75, 142, 26, 84, 39, 83, 6, + 104, 11, 76, 89, 85, 67, 72, 69, 86, 65, 89, 65, 32, 185, 25, 10, 82, 89, + 85, 75, 79, 86, 65, 89, 65, 32, 4, 202, 16, 78, 251, 8, 83, 14, 58, 65, + 88, 8, 69, 82, 69, 86, 79, 68, 75, 65, 27, 79, 6, 44, 3, 82, 65, 75, 222, + 19, 76, 211, 5, 85, 2, 11, 76, 2, 11, 73, 2, 171, 28, 84, 5, 153, 15, 2, + 32, 78, 4, 68, 5, 68, 67, 72, 65, 83, 245, 9, 7, 76, 75, 85, 76, 73, 90, + 77, 2, 11, 72, 2, 219, 4, 73, 4, 64, 11, 69, 86, 69, 82, 83, 69, 68, 32, + 67, 72, 69, 139, 26, 79, 2, 25, 4, 76, 89, 85, 83, 2, 215, 17, 84, 126, + 96, 9, 75, 65, 77, 69, 89, 84, 83, 65, 32, 148, 2, 8, 76, 79, 90, 72, 73, + 84, 73, 69, 119, 84, 22, 128, 1, 13, 68, 86, 79, 69, 67, 72, 69, 76, 78, + 65, 89, 65, 32, 44, 7, 75, 76, 89, 85, 67, 72, 69, 74, 84, 218, 18, 77, + 119, 83, 8, 218, 10, 75, 110, 78, 178, 8, 80, 75, 83, 6, 40, 5, 86, 65, + 89, 65, 32, 243, 10, 78, 4, 178, 15, 84, 183, 4, 83, 4, 162, 15, 73, 147, + 4, 82, 9, 11, 32, 6, 48, 2, 83, 32, 25, 6, 90, 65, 75, 82, 89, 84, 4, + 166, 4, 75, 95, 90, 2, 11, 79, 2, 211, 22, 69, 96, 92, 4, 65, 84, 89, 65, + 172, 4, 6, 79, 80, 73, 84, 83, 65, 189, 1, 5, 82, 69, 76, 65, 32, 23, 11, + 32, 20, 76, 2, 83, 32, 236, 2, 9, 90, 65, 75, 82, 89, 84, 65, 89, 65, + 159, 5, 78, 14, 160, 1, 14, 68, 86, 85, 77, 89, 65, 32, 90, 65, 80, 89, + 65, 84, 89, 28, 7, 75, 82, 89, 90, 72, 69, 77, 24, 2, 82, 79, 17, 8, 90, + 65, 80, 89, 65, 84, 79, 89, 2, 11, 77, 2, 215, 19, 73, 5, 189, 1, 2, 32, + 73, 2, 203, 2, 71, 7, 21, 3, 32, 73, 32, 4, 54, 75, 37, 9, 80, 79, 68, + 67, 72, 65, 83, 72, 73, 2, 11, 82, 2, 21, 3, 89, 90, 72, 2, 211, 1, 69, + 5, 17, 2, 32, 83, 2, 17, 2, 32, 90, 2, 29, 5, 65, 80, 89, 65, 84, 2, 11, + 79, 2, 199, 17, 89, 7, 11, 32, 4, 68, 6, 83, 32, 79, 67, 72, 75, 29, 7, + 87, 73, 84, 72, 32, 83, 79, 2, 11, 79, 2, 215, 16, 77, 2, 45, 9, 82, 79, + 67, 72, 89, 65, 32, 78, 79, 2, 11, 90, 2, 163, 7, 72, 68, 140, 1, 9, 68, + 86, 79, 69, 67, 72, 69, 76, 78, 162, 1, 75, 78, 78, 174, 1, 71, 152, 3, + 8, 77, 82, 65, 67, 72, 78, 79, 84, 42, 80, 75, 84, 10, 18, 79, 71, 65, 6, + 64, 7, 80, 79, 86, 79, 68, 78, 65, 189, 5, 4, 75, 82, 89, 90, 4, 17, 2, + 89, 65, 5, 17, 2, 32, 75, 2, 145, 5, 4, 76, 89, 85, 67, 26, 48, 6, 76, + 89, 85, 67, 72, 69, 77, 2, 82, 89, 4, 22, 78, 203, 6, 80, 2, 157, 8, 9, + 69, 80, 79, 83, 84, 79, 89, 65, 78, 22, 48, 7, 85, 75, 79, 86, 65, 89, + 65, 195, 3, 90, 21, 11, 32, 18, 54, 71, 236, 2, 5, 84, 82, 89, 65, 83, + 179, 2, 80, 14, 21, 3, 82, 79, 77, 14, 32, 4, 78, 65, 89, 65, 47, 79, 5, + 237, 1, 7, 32, 87, 73, 84, 72, 32, 83, 10, 92, 10, 75, 82, 89, 90, 72, + 69, 86, 65, 89, 65, 17, 9, 80, 79, 86, 79, 68, 78, 65, 89, 65, 5, 175, 2, + 32, 7, 33, 6, 32, 87, 73, 84, 72, 32, 4, 24, 2, 68, 79, 23, 83, 2, 41, 2, + 85, 66, 2, 21, 3, 73, 78, 71, 2, 133, 6, 6, 76, 69, 32, 90, 65, 80, 2, + 175, 6, 75, 2, 237, 5, 3, 72, 69, 86, 2, 11, 73, 2, 11, 75, 2, 187, 5, + 72, 6, 22, 79, 187, 3, 82, 4, 28, 2, 76, 85, 187, 1, 86, 2, 163, 1, 80, + 8, 80, 5, 82, 89, 65, 83, 79, 153, 2, 10, 73, 75, 72, 65, 89, 65, 32, 80, + 85, 84, 6, 62, 80, 44, 4, 83, 84, 82, 69, 173, 1, 4, 71, 76, 65, 83, 2, + 17, 2, 79, 86, 2, 193, 1, 2, 79, 68, 2, 171, 1, 76, 16, 88, 12, 75, 76, + 89, 85, 67, 72, 69, 86, 65, 89, 65, 32, 38, 77, 46, 80, 38, 84, 39, 83, + 8, 34, 77, 46, 80, 38, 84, 39, 83, 2, 25, 4, 82, 65, 67, 72, 2, 247, 1, + 78, 2, 11, 82, 2, 205, 1, 2, 79, 83, 2, 11, 82, 2, 11, 69, 2, 11, 83, 2, + 157, 1, 4, 86, 69, 84, 76, 6, 30, 65, 121, 3, 77, 69, 89, 4, 32, 4, 78, + 79, 90, 72, 31, 80, 2, 11, 69, 2, 151, 3, 75, 2, 17, 2, 89, 65, 2, 11, + 84, 2, 11, 65, 2, 35, 89, 2, 11, 84, 2, 11, 83, 2, 183, 2, 65, 10, 108, + 11, 68, 73, 82, 69, 67, 84, 73, 79, 78, 32, 70, 28, 3, 75, 82, 89, 28, 5, + 76, 69, 86, 69, 76, 35, 82, 2, 11, 76, 2, 159, 1, 73, 2, 11, 90, 2, 143, + 1, 72, 4, 11, 45, 4, 114, 50, 3, 51, 2, 11, 79, 2, 83, 71, 8, 26, 78, 34, + 83, 15, 74, 4, 18, 66, 27, 74, 2, 11, 83, 2, 11, 80, 3, 0, }; static const unsigned int dawg_pos_to_codepoint[] = { @@ -9248,1040 +9325,944 @@ static const unsigned int dawg_pos_to_codepoint[] = { 71425, 71424, 71431, 71430, 71441, 71433, 71437, 71439, 71484, 71485, 71467, 71486, 71487, 71456, 71457, 71465, 71466, 71463, 71460, 71461, 71458, 71459, 71462, 71464, 71483, 71482, 71477, 71476, 71479, 71478, - 71475, 71474, 71472, 71481, 71473, 71480, 9992, 128747, 128748, 128874, - 128822, 128823, 128837, 128776, 128777, 128774, 128775, 128773, 128811, - 128859, 128826, 128829, 128855, 128769, 128875, 128876, 128830, 128834, - 128835, 128836, 128783, 128857, 128848, 128846, 128844, 128805, 128800, - 128803, 128804, 128798, 128869, 128870, 128871, 128872, 128873, 128787, - 128865, 128866, 128864, 128880, 128794, 128841, 128883, 128882, 128854, - 128878, 128796, 128797, 128801, 128810, 128851, 128784, 128785, 128786, - 128824, 128881, 128789, 128879, 128782, 128843, 128858, 128856, 128868, - 128867, 128863, 128768, 128833, 128818, 128816, 128817, 128799, 128819, - 128820, 128821, 128827, 128828, 128877, 128792, 128793, 128788, 128806, - 128813, 128825, 128850, 128860, 128861, 128814, 128807, 128812, 128802, - 128862, 128781, 128839, 128795, 128852, 128847, 128831, 128832, 128840, - 128809, 128849, 128845, 128778, 128815, 128779, 128780, 128790, 128791, - 128808, 128772, 128842, 128853, 128771, 128838, 128770, 9879, 8501, - 983054, 128126, 117837, 117836, 117844, 117845, 117842, 117843, 117839, - 117838, 117841, 117840, 8780, 9006, 983203, 8776, 10863, 8778, 9095, - 9941, 9200, 38, 127994, 127944, 128657, 10815, 82970, 82971, 82964, - 82965, 82966, 82967, 82968, 82969, 82972, 82973, 82974, 82994, 82995, - 82996, 82987, 82988, 82992, 82993, 82986, 82989, 82990, 82991, 82997, - 82998, 82999, 83016, 83017, 83018, 83019, 83010, 83011, 83012, 83013, - 83014, 83015, 83020, 83021, 83022, 83050, 83051, 83052, 83053, 83043, - 83044, 83045, 83046, 83047, 83048, 83049, 83054, 82984, 82985, 82975, - 82976, 82977, 82978, 82979, 82980, 82981, 82982, 82983, 82953, 82954, - 82955, 82956, 82957, 82958, 82959, 82960, 82961, 82962, 82963, 82944, - 82945, 82946, 82947, 82948, 82949, 82950, 82951, 82952, 83000, 83001, - 83002, 83003, 83004, 83005, 83006, 83007, 83008, 83009, 83023, 83024, - 83025, 83026, 83027, 83028, 83029, 83030, 83031, 83032, 83033, 83034, - 83035, 83036, 83037, 83038, 83039, 83040, 83041, 83042, 83062, 83063, - 83064, 83065, 83070, 83071, 83072, 83073, 83066, 83067, 83068, 83055, - 83056, 83057, 83058, 83059, 83060, 83061, 83069, 83074, 83075, 83076, - 83077, 83078, 83083, 83084, 83079, 83080, 83081, 83082, 83085, 83086, - 83087, 83088, 83094, 83095, 83089, 83090, 83091, 83092, 83093, 83096, - 83097, 83098, 83099, 83105, 83106, 83100, 83101, 83102, 83103, 83104, - 83107, 83108, 83109, 83110, 83111, 83112, 83113, 83114, 83115, 83116, - 83117, 83118, 83119, 83120, 83121, 83122, 83123, 83124, 83125, 83126, - 83127, 83128, 83129, 83130, 83131, 83132, 83133, 83134, 83135, 83136, - 83137, 83138, 83139, 83140, 83141, 83142, 83143, 83144, 83145, 83146, - 83147, 83148, 83149, 83150, 83151, 83152, 83153, 83154, 83155, 83156, - 83157, 83158, 83159, 83160, 83161, 83162, 83163, 83164, 83165, 83166, - 83167, 83168, 83169, 83170, 83173, 83174, 83175, 83180, 83181, 83183, - 83184, 83171, 83172, 83176, 83177, 83178, 83179, 83182, 83190, 83191, - 83192, 83193, 83185, 83186, 83187, 83188, 83189, 83194, 83195, 83196, - 83274, 83275, 83280, 83281, 83270, 83271, 83272, 83273, 83276, 83277, - 83278, 83279, 83268, 83269, 83259, 83260, 83261, 83262, 83263, 83264, - 83265, 83266, 83267, 83204, 83205, 83197, 83198, 83199, 83200, 83201, - 83202, 83203, 83206, 83207, 83245, 83246, 83238, 83239, 83240, 83241, - 83242, 83243, 83244, 83247, 83248, 83208, 83209, 83210, 83211, 83212, - 83213, 83214, 83215, 83216, 83217, 83218, 83219, 83220, 83221, 83222, - 83223, 83224, 83225, 83226, 83227, 83228, 83229, 83230, 83231, 83232, - 83233, 83234, 83235, 83236, 83237, 83249, 83250, 83251, 83252, 83253, - 83254, 83255, 83256, 83257, 83258, 83291, 83292, 83282, 83283, 83284, - 83285, 83286, 83287, 83288, 83289, 83290, 83312, 83313, 83303, 83304, - 83305, 83306, 83307, 83308, 83309, 83310, 83311, 83348, 83349, 83339, - 83340, 83341, 83342, 83343, 83344, 83345, 83346, 83347, 83322, 83323, - 83324, 83325, 83316, 83317, 83318, 83314, 83315, 83319, 83320, 83321, - 83326, 83327, 83328, 83354, 83355, 83359, 83360, 83350, 83351, 83352, - 83353, 83356, 83357, 83358, 83361, 83377, 83378, 83374, 83375, 83381, - 83382, 83373, 83376, 83379, 83380, 83383, 83384, 83385, 83389, 83386, - 83387, 83388, 83390, 83391, 83392, 83393, 83394, 83395, 83363, 83364, - 83362, 83365, 83366, 83367, 83368, 83369, 83370, 83371, 83372, 83293, - 83294, 83295, 83296, 83297, 83298, 83299, 83300, 83301, 83302, 83329, - 83330, 83331, 83332, 83333, 83334, 83335, 83336, 83337, 83338, 83406, - 83407, 83408, 83409, 83410, 83411, 83412, 83413, 83414, 83415, 83416, - 83447, 83448, 83455, 83456, 83449, 83450, 83451, 83452, 83453, 83454, - 83457, 83458, 83489, 83490, 83491, 83492, 83493, 83494, 83495, 83496, - 83396, 83397, 83398, 83399, 83400, 83401, 83402, 83403, 83404, 83405, - 83417, 83418, 83419, 83420, 83421, 83422, 83423, 83424, 83425, 83426, - 83427, 83428, 83429, 83430, 83431, 83432, 83433, 83434, 83435, 83436, - 83437, 83438, 83439, 83440, 83441, 83442, 83443, 83444, 83445, 83446, - 83459, 83460, 83461, 83462, 83463, 83464, 83465, 83466, 83467, 83468, - 83469, 83470, 83471, 83472, 83473, 83474, 83475, 83476, 83477, 83478, - 83479, 83480, 83481, 83482, 83483, 83484, 83485, 83486, 83487, 83488, - 83526, 83497, 83498, 83499, 83500, 83501, 83502, 83503, 83504, 83505, - 83506, 83507, 83508, 83509, 83510, 83511, 83512, 83513, 83514, 83515, - 83516, 83517, 83518, 83519, 83520, 83521, 83522, 83523, 83524, 83525, - 129728, 8736, 10654, 10660, 128551, 8491, 128162, 128544, 128028, 117768, - 128246, 11150, 11148, 11149, 11151, 11119, 8630, 10560, 8755, 128260, - 10226, 8634, 10769, 9875, 10193, 9765, 9021, 9055, 9061, 9033, 9052, - 9022, 9066, 9058, 9067, 9042, 9049, 9035, 9034, 9038, 9062, 9073, 9046, - 9050, 9075, 9080, 9014, 9082, 9078, 9077, 9081, 9060, 9051, 9063, 9029, - 9109, 9020, 9056, 9017, 9018, 9036, 9047, 9044, 9037, 9043, 9040, 9027, - 9031, 9072, 9071, 9016, 9026, 9025, 9028, 9032, 9019, 9054, 9048, 9030, - 9076, 9070, 9023, 9059, 9069, 9015, 9079, 9024, 9074, 9057, 9041, 9045, - 9053, 9039, 9065, 9064, 9068, 39, 11236, 8773, 8786, 10864, 8774, 8784, - 983195, 983196, 69372, 1548, 2277, 2280, 2276, 2279, 2278, 2281, 1615, - 65144, 65145, 2302, 1612, 65138, 1549, 2206, 2299, 2300, 1643, 2274, - 1771, 1770, 1565, 1757, 1614, 1630, 2293, 2292, 65142, 65143, 1611, - 65136, 1538, 1645, 1748, 2207, 1621, 1620, 1616, 2294, 65146, 65147, - 1613, 65140, 2258, 2255, 2254, 2257, 2236, 2244, 2235, 2237, 1593, 1886, - 2227, 1696, 1887, 1885, 65226, 65227, 65225, 65228, 1575, 2165, 2176, - 2173, 2161, 2174, 2171, 2167, 2177, 2169, 2166, 2168, 2178, 2164, 2160, - 2162, 2175, 2172, 1571, 65156, 65155, 1573, 65160, 65159, 1651, 1650, - 1908, 1907, 1570, 65154, 65153, 2163, 2170, 1649, 64337, 64336, 1609, - 65264, 65263, 65166, 65165, 1749, 1576, 2230, 1878, 2208, 1874, 1875, - 1872, 1876, 1877, 1873, 2209, 65168, 65169, 65167, 65170, 1664, 64347, - 64348, 64346, 64349, 1659, 64339, 64340, 64338, 64341, 1583, 1882, 1774, - 1679, 2222, 69314, 1881, 1674, 1675, 1680, 1673, 65194, 65193, 1676, - 64389, 64388, 1590, 1787, 65214, 65215, 65213, 65216, 1677, 64387, 64386, - 1672, 64393, 64392, 1646, 1697, 1647, 1668, 64371, 64372, 64370, 64373, - 1678, 64391, 64390, 1740, 1911, 1910, 1909, 1599, 1598, 1597, 64509, - 64510, 64508, 64511, 1601, 1699, 2212, 1698, 1889, 1701, 1888, 65234, - 65235, 65233, 65236, 1711, 1716, 1714, 1712, 2224, 64403, 64404, 64402, - 64405, 1594, 2243, 1788, 65230, 65231, 65229, 65232, 1715, 64407, 64408, - 64406, 64409, 2248, 1581, 1916, 2186, 1903, 1906, 1902, 1880, 1669, 1666, - 1879, 1665, 65186, 65187, 65185, 65188, 1569, 65152, 1607, 1729, 1730, - 64423, 64424, 64422, 64425, 1791, 1728, 64421, 64420, 1726, 64427, 64428, - 64426, 64429, 65258, 65259, 65257, 65260, 1652, 1653, 1656, 1654, 1580, - 2210, 2246, 2245, 65182, 65183, 65181, 65184, 1688, 64395, 64394, 1603, - 69316, 1919, 1710, 2228, 1708, 1707, 65242, 65243, 65241, 65244, 1568, - 1705, 1892, 1596, 1891, 2189, 1595, 2242, 1890, 64399, 64400, 64398, - 64401, 1733, 64481, 64480, 1737, 64483, 64482, 1582, 65190, 65191, 65189, - 65192, 1604, 2214, 1718, 2247, 1717, 1720, 1719, 1898, 65246, 65247, - 65245, 65248, 2221, 1605, 2215, 1894, 1893, 65250, 65251, 65249, 65252, - 1564, 1709, 1713, 64411, 64412, 64410, 64413, 64468, 64469, 64467, 64470, - 1667, 64375, 64376, 64374, 64377, 1606, 1722, 64415, 64414, 1896, 1897, - 1725, 1895, 2185, 1721, 1724, 65254, 65255, 65253, 65256, 1662, 2231, - 2238, 64343, 64344, 64342, 64345, 1702, 64367, 64368, 64366, 64369, 1602, - 2213, 2229, 1703, 1704, 65238, 65239, 65237, 65240, 1585, 1905, 2233, - 1682, 1685, 1883, 1899, 1687, 1775, 1900, 1689, 1684, 1686, 2218, 1683, - 65198, 65197, 1681, 64397, 64396, 1723, 64417, 64418, 64416, 64419, 2220, - 1589, 2223, 1694, 1693, 65210, 65211, 65209, 65212, 1587, 1690, 1918, - 1904, 1691, 1692, 1901, 1884, 1917, 65202, 65203, 65201, 65204, 1588, - 1786, 65206, 65207, 65205, 65208, 1648, 2225, 1706, 1591, 69315, 2211, - 2188, 1695, 2187, 65218, 65219, 65217, 65220, 1670, 2241, 1727, 64379, - 64380, 64378, 64381, 1671, 64383, 64384, 64382, 64385, 1578, 1577, 65172, - 65171, 1731, 65176, 2232, 2239, 1661, 1660, 65174, 65175, 65173, 1663, - 64355, 64356, 64354, 64357, 1584, 65196, 65195, 1579, 65178, 65179, - 65177, 65180, 2182, 1657, 2240, 64359, 64360, 64358, 64361, 1658, 64351, - 64352, 64350, 64353, 1735, 1655, 64477, 64472, 64471, 64488, 64489, 1739, - 1700, 64363, 64364, 64362, 64365, 64479, 64478, 1608, 2219, 1743, 1913, - 1912, 1572, 65158, 65157, 1738, 1732, 65262, 65261, 1610, 1746, 1915, - 1914, 1747, 64433, 64432, 64431, 64430, 1574, 65162, 65163, 65161, 65164, - 1742, 2216, 2234, 2217, 1741, 1745, 65266, 65267, 65265, 65268, 1736, - 64476, 64475, 1734, 64474, 64473, 1592, 65222, 65223, 65221, 65224, 1586, - 65200, 65199, 2226, 1744, 64485, 64486, 64484, 64487, 2297, 2295, 64888, - 64887, 64886, 64950, 64699, 64554, 64964, 64885, 64698, 64553, 64787, - 64759, 64788, 64760, 64842, 64839, 64841, 64840, 64845, 65015, 64656, - 64605, 64828, 64829, 65010, 65011, 65023, 64962, 64669, 64518, 64672, - 64738, 64926, 64670, 64519, 64622, 64521, 64668, 64517, 64620, 64671, - 64520, 64737, 64621, 64619, 64618, 64623, 64522, 65021, 64878, 64939, - 64693, 64547, 64880, 64879, 64694, 64548, 64803, 64775, 64692, 64546, - 64695, 64549, 64812, 64784, 64804, 64776, 64893, 64892, 64704, 64559, - 64636, 64561, 64702, 64557, 64961, 64705, 64560, 64703, 64558, 64637, - 64562, 64889, 64891, 64890, 64701, 64556, 64789, 64761, 64700, 64555, - 64790, 64762, 64859, 64858, 64682, 64536, 64795, 64767, 64959, 64681, + 71475, 71474, 71472, 71481, 71473, 71480, 9992, 128747, 128748, 130042, + 9200, 128874, 128822, 128823, 128837, 128776, 128777, 128774, 128775, + 128773, 128811, 128859, 128826, 128829, 128855, 128769, 128875, 128876, + 128830, 128834, 128835, 128836, 128783, 128857, 128848, 128846, 128844, + 128805, 128800, 128803, 128804, 128798, 128869, 128870, 128871, 128872, + 128873, 128787, 128865, 128866, 128864, 128880, 128794, 128841, 128883, + 128882, 128854, 128878, 128796, 128797, 128801, 128810, 128851, 128784, + 128785, 128786, 128824, 128881, 128789, 128879, 128782, 128843, 128858, + 128856, 128868, 128867, 128863, 128768, 128833, 128818, 128816, 128817, + 128799, 128819, 128820, 128821, 128827, 128828, 128877, 128792, 128793, + 128788, 128806, 128813, 128825, 128850, 128860, 128861, 128814, 128807, + 128812, 128802, 128862, 128781, 128839, 128795, 128852, 128847, 128831, + 128832, 128840, 128809, 128849, 128845, 128778, 128815, 128779, 128780, + 128790, 128791, 128808, 128772, 128842, 128853, 128771, 128770, 128838, + 9879, 8501, 983054, 128126, 117837, 117836, 117844, 117845, 117842, + 117843, 117839, 117838, 117841, 117840, 8780, 9006, 983203, 8776, 10863, + 8778, 9095, 9941, 118479, 127994, 38, 127944, 128657, 10815, 82970, + 82971, 82964, 82965, 82966, 82967, 82968, 82969, 82972, 82973, 82974, + 82994, 82995, 82996, 82987, 82988, 82992, 82993, 82986, 82989, 82990, + 82991, 82997, 82998, 82999, 83016, 83017, 83018, 83019, 83010, 83011, + 83012, 83013, 83014, 83015, 83020, 83021, 83022, 83050, 83051, 83052, + 83053, 83043, 83044, 83045, 83046, 83047, 83048, 83049, 83054, 82984, + 82985, 82975, 82976, 82977, 82978, 82979, 82980, 82981, 82982, 82983, + 82953, 82954, 82955, 82956, 82957, 82958, 82959, 82960, 82961, 82962, + 82963, 82944, 82945, 82946, 82947, 82948, 82949, 82950, 82951, 82952, + 83000, 83001, 83002, 83003, 83004, 83005, 83006, 83007, 83008, 83009, + 83023, 83024, 83025, 83026, 83027, 83028, 83029, 83030, 83031, 83032, + 83033, 83034, 83035, 83036, 83037, 83038, 83039, 83040, 83041, 83042, + 83062, 83063, 83064, 83065, 83070, 83071, 83072, 83073, 83066, 83067, + 83068, 83055, 83056, 83057, 83058, 83059, 83060, 83061, 83069, 83074, + 83075, 83076, 83077, 83078, 83083, 83084, 83079, 83080, 83081, 83082, + 83085, 83086, 83087, 83088, 83094, 83095, 83089, 83090, 83091, 83092, + 83093, 83096, 83097, 83098, 83099, 83105, 83106, 83100, 83101, 83102, + 83103, 83104, 83107, 83108, 83109, 83110, 83111, 83112, 83113, 83114, + 83115, 83116, 83117, 83118, 83119, 83120, 83121, 83122, 83123, 83124, + 83125, 83126, 83127, 83128, 83129, 83130, 83131, 83132, 83133, 83134, + 83135, 83136, 83137, 83138, 83139, 83140, 83141, 83142, 83143, 83144, + 83145, 83146, 83147, 83148, 83149, 83150, 83151, 83152, 83153, 83154, + 83155, 83156, 83157, 83158, 83159, 83160, 83161, 83162, 83163, 83164, + 83165, 83166, 83167, 83168, 83169, 83170, 83173, 83174, 83175, 83180, + 83181, 83183, 83184, 83171, 83172, 83176, 83177, 83178, 83179, 83182, + 83190, 83191, 83192, 83193, 83185, 83186, 83187, 83188, 83189, 83194, + 83195, 83196, 83274, 83275, 83280, 83281, 83270, 83271, 83272, 83273, + 83276, 83277, 83278, 83279, 83268, 83269, 83259, 83260, 83261, 83262, + 83263, 83264, 83265, 83266, 83267, 83204, 83205, 83197, 83198, 83199, + 83200, 83201, 83202, 83203, 83206, 83207, 83245, 83246, 83238, 83239, + 83240, 83241, 83242, 83243, 83244, 83247, 83248, 83208, 83209, 83210, + 83211, 83212, 83213, 83214, 83215, 83216, 83217, 83218, 83219, 83220, + 83221, 83222, 83223, 83224, 83225, 83226, 83227, 83228, 83229, 83230, + 83231, 83232, 83233, 83234, 83235, 83236, 83237, 83249, 83250, 83251, + 83252, 83253, 83254, 83255, 83256, 83257, 83258, 83291, 83292, 83282, + 83283, 83284, 83285, 83286, 83287, 83288, 83289, 83290, 83312, 83313, + 83303, 83304, 83305, 83306, 83307, 83308, 83309, 83310, 83311, 83348, + 83349, 83339, 83340, 83341, 83342, 83343, 83344, 83345, 83346, 83347, + 83322, 83323, 83324, 83325, 83316, 83317, 83318, 83314, 83315, 83319, + 83320, 83321, 83326, 83327, 83328, 83354, 83355, 83359, 83360, 83350, + 83351, 83352, 83353, 83356, 83357, 83358, 83361, 83377, 83378, 83374, + 83375, 83381, 83382, 83373, 83376, 83379, 83380, 83383, 83384, 83385, + 83389, 83386, 83387, 83388, 83390, 83391, 83392, 83393, 83394, 83395, + 83363, 83364, 83362, 83365, 83366, 83367, 83368, 83369, 83370, 83371, + 83372, 83293, 83294, 83295, 83296, 83297, 83298, 83299, 83300, 83301, + 83302, 83329, 83330, 83331, 83332, 83333, 83334, 83335, 83336, 83337, + 83338, 83406, 83407, 83408, 83409, 83410, 83411, 83412, 83413, 83414, + 83415, 83416, 83447, 83448, 83455, 83456, 83449, 83450, 83451, 83452, + 83453, 83454, 83457, 83458, 83489, 83490, 83491, 83492, 83493, 83494, + 83495, 83496, 83396, 83397, 83398, 83399, 83400, 83401, 83402, 83403, + 83404, 83405, 83417, 83418, 83419, 83420, 83421, 83422, 83423, 83424, + 83425, 83426, 83427, 83428, 83429, 83430, 83431, 83432, 83433, 83434, + 83435, 83436, 83437, 83438, 83439, 83440, 83441, 83442, 83443, 83444, + 83445, 83446, 83459, 83460, 83461, 83462, 83463, 83464, 83465, 83466, + 83467, 83468, 83469, 83470, 83471, 83472, 83473, 83474, 83475, 83476, + 83477, 83478, 83479, 83480, 83481, 83482, 83483, 83484, 83485, 83486, + 83487, 83488, 83526, 83497, 83498, 83499, 83500, 83501, 83502, 83503, + 83504, 83505, 83506, 83507, 83508, 83509, 83510, 83511, 83512, 83513, + 83514, 83515, 83516, 83517, 83518, 83519, 83520, 83521, 83522, 83523, + 83524, 83525, 129728, 8736, 10654, 10660, 128551, 8491, 128162, 128544, + 128028, 117768, 128246, 11150, 11148, 11149, 11151, 11119, 8630, 10560, + 8755, 128260, 10226, 8634, 10769, 9875, 10193, 9765, 9021, 9055, 9061, + 9033, 9052, 9022, 9066, 9058, 9067, 9042, 9049, 9035, 9034, 9038, 9062, + 9073, 9046, 9050, 9075, 9080, 9014, 9082, 9078, 9077, 9081, 9060, 9051, + 9063, 9029, 9109, 9020, 9056, 9017, 9018, 9036, 9047, 9044, 9037, 9043, + 9040, 9027, 9031, 9072, 9071, 9016, 9026, 9025, 9028, 9032, 9019, 9054, + 9048, 9030, 9076, 9070, 9023, 9059, 9069, 9015, 9079, 9024, 9074, 9057, + 9041, 9045, 9053, 9039, 9065, 9064, 9068, 39, 11236, 983195, 118461, + 8773, 8786, 10864, 8774, 8784, 983196, 2183, 69328, 69372, 1548, 2277, + 2280, 2276, 2279, 2278, 2281, 1615, 65144, 65145, 2302, 1612, 65138, + 1549, 2299, 2300, 69370, 2206, 1643, 2274, 1771, 1770, 1565, 1757, 1614, + 1630, 2293, 2292, 65142, 65143, 1611, 65136, 1538, 1645, 1748, 2207, + 1621, 1620, 1616, 2294, 65146, 65147, 1613, 65140, 2258, 2255, 2254, + 2257, 2236, 2244, 2235, 2237, 1593, 1886, 2227, 1696, 1887, 1885, 65226, + 65227, 65225, 65228, 1575, 2165, 2176, 2173, 2161, 2174, 2171, 2167, + 2177, 2169, 2166, 2168, 2178, 2164, 2160, 2162, 2175, 2172, 1571, 65156, + 65155, 1573, 65160, 65159, 1651, 1650, 1908, 1907, 1570, 65154, 65153, + 2163, 2170, 1649, 64337, 64336, 1609, 65264, 65263, 65166, 65165, 1749, + 1576, 2230, 1878, 2208, 1874, 1875, 1872, 1876, 1877, 1873, 2209, 65168, + 65169, 65167, 65170, 1664, 64347, 64348, 64346, 64349, 1659, 64339, + 64340, 64338, 64341, 1583, 1882, 1774, 1679, 2222, 69314, 1881, 1674, + 1675, 1680, 1673, 65194, 65193, 1676, 64389, 64388, 1590, 1787, 65214, + 65215, 65213, 65216, 1677, 64387, 64386, 1672, 64393, 64392, 1646, 1697, + 1647, 1668, 64371, 64372, 64370, 64373, 1678, 64391, 64390, 1740, 1911, + 1910, 1909, 1599, 1598, 1597, 64509, 64510, 64508, 64511, 1601, 1699, + 2212, 1698, 1889, 1701, 1888, 65234, 65235, 65233, 65236, 1711, 1716, + 1714, 1712, 2224, 64403, 64404, 64402, 64405, 1594, 2243, 1788, 65230, + 65231, 65229, 65232, 1715, 64407, 64408, 64406, 64409, 2248, 1581, 1916, + 2186, 1903, 1906, 1902, 1880, 1669, 1666, 1879, 1665, 65186, 65187, + 65185, 65188, 1569, 65152, 1607, 1729, 1730, 64423, 64424, 64422, 64425, + 1791, 1728, 64421, 64420, 1726, 64427, 64428, 64426, 64429, 65258, 65259, + 65257, 65260, 1652, 1653, 1656, 1654, 1580, 2210, 2246, 2245, 65182, + 65183, 65181, 65184, 1688, 64395, 64394, 1603, 69316, 1919, 1710, 2228, + 1708, 1707, 65242, 65243, 65241, 65244, 1568, 1705, 1892, 1596, 1891, + 2189, 1595, 2242, 1890, 64399, 64400, 64398, 64401, 1733, 64481, 64480, + 1737, 64483, 64482, 1582, 65190, 65191, 65189, 65192, 1604, 2214, 1718, + 2247, 1717, 1720, 1719, 1898, 65246, 65247, 65245, 65248, 2221, 1605, + 2215, 1894, 1893, 65250, 65251, 65249, 65252, 1564, 1709, 1713, 64411, + 64412, 64410, 64413, 64468, 64469, 64467, 64470, 1667, 64375, 64376, + 64374, 64377, 1606, 1722, 64415, 64414, 1896, 1897, 1725, 1895, 2185, + 1724, 2191, 1721, 65254, 65255, 65253, 65256, 1662, 2231, 2238, 64343, + 64344, 64342, 64345, 1702, 64367, 64368, 64366, 64369, 1602, 2213, 2229, + 1703, 1704, 65238, 65239, 65237, 65240, 1585, 1905, 2233, 1682, 1685, + 1883, 1899, 1687, 1775, 1900, 1689, 1684, 1686, 2218, 1683, 65198, 65197, + 1681, 64397, 64396, 1723, 64417, 64418, 64416, 64419, 2220, 1589, 2223, + 1694, 1693, 65210, 65211, 65209, 65212, 1587, 1690, 1918, 1904, 1691, + 1692, 1901, 1884, 1917, 65202, 65203, 65201, 65204, 1588, 1786, 65206, + 65207, 65205, 65208, 1648, 2225, 1706, 1591, 69315, 2211, 2188, 1695, + 2187, 65218, 65219, 65217, 65220, 1670, 2241, 1727, 64379, 64380, 64378, + 64381, 1671, 64383, 64384, 64382, 64385, 1578, 1577, 65172, 65171, 1731, + 65176, 2232, 2239, 1661, 1660, 65174, 65175, 65173, 1663, 64355, 64356, + 64354, 64357, 1584, 65196, 65195, 2182, 69318, 1579, 65178, 65179, 65177, + 65180, 1657, 2240, 64359, 64360, 64358, 64361, 1658, 64351, 64352, 64350, + 64353, 1735, 1655, 64477, 64472, 64471, 64488, 64489, 1739, 1700, 64363, + 64364, 64362, 64365, 64479, 64478, 1608, 2219, 1743, 1913, 1912, 1572, + 65158, 65157, 1738, 1732, 65262, 65261, 1610, 1746, 1915, 1914, 1747, + 64433, 64432, 64431, 64430, 1574, 65162, 65163, 65161, 65164, 1742, 2216, + 2234, 2217, 1745, 1741, 69319, 65266, 65267, 65265, 65268, 1736, 64476, + 64475, 1734, 64474, 64473, 1592, 65222, 65223, 65221, 65224, 1586, 65200, + 65199, 2226, 1744, 64485, 64486, 64484, 64487, 2297, 2295, 64888, 64887, + 64886, 64950, 64699, 64554, 64964, 64885, 64698, 64553, 64787, 64759, + 64788, 64760, 64465, 64973, 64842, 64839, 64466, 69331, 64841, 69330, + 64840, 69329, 64845, 65015, 64656, 64605, 64828, 64829, 65010, 65011, + 65023, 64962, 64669, 64518, 64672, 64738, 64926, 64670, 64519, 64622, + 64521, 64668, 64517, 64620, 64671, 64520, 64737, 64621, 64619, 64618, + 64623, 64522, 65021, 64878, 64939, 64693, 64547, 64880, 64879, 64694, + 64548, 64803, 64775, 64692, 64546, 64695, 64549, 64812, 64784, 64804, + 64776, 64452, 64893, 64892, 64704, 64559, 64636, 64561, 64702, 64557, + 64961, 64705, 64560, 64703, 64558, 64637, 64562, 64889, 64891, 64890, + 64701, 64556, 64789, 64761, 64700, 64555, 64790, 64762, 64462, 64463, + 64460, 64461, 64859, 64858, 64682, 64536, 64795, 64767, 64959, 64681, 64535, 64796, 64768, 64915, 64916, 64728, 64594, 64595, 64596, 64727, - 64593, 64729, 64934, 64958, 64679, 64533, 64857, 64856, 64935, 64933, - 64680, 64534, 64797, 64769, 64798, 64770, 65019, 64643, 64573, 64640, - 64567, 64708, 64568, 64641, 64711, 64571, 64747, 64710, 64570, 64709, - 64569, 64963, 64955, 64951, 64642, 64712, 64572, 64748, 64644, 64574, - 64538, 64799, 64771, 64683, 64537, 64684, 64539, 64800, 64772, 65272, - 65271, 65274, 65273, 65270, 65269, 64646, 64579, 65276, 65275, 64898, - 64949, 64896, 64897, 64714, 64576, 64717, 64899, 64900, 64954, 64956, - 64940, 64713, 64575, 64902, 64901, 64715, 64577, 64904, 64903, 64941, - 64645, 64716, 64578, 64749, 64647, 64580, 64585, 64648, 64905, 64906, - 64907, 64719, 64582, 64910, 64911, 64953, 64720, 64583, 64909, 64914, - 64908, 64960, 64718, 64581, 64945, 64649, 64721, 64584, 64586, 65012, - 64918, 64917, 64947, 64723, 64588, 64726, 64751, 64952, 64957, 64921, - 64920, 64919, 64967, 64722, 64587, 64923, 64922, 64652, 64725, 64590, - 64750, 64654, 64591, 64653, 64651, 64724, 64589, 64650, 64655, 64592, - 64895, 64948, 64894, 64946, 64707, 64564, 64638, 64565, 64706, 64563, - 64639, 64566, 65009, 64843, 64833, 64835, 64836, 64837, 64834, 64832, - 64847, 65014, 64604, 64869, 64868, 64937, 64689, 64544, 64965, 64870, + 64593, 64729, 65019, 64451, 64934, 64958, 64679, 64533, 64857, 64856, + 64935, 64933, 64680, 64534, 64797, 64769, 64798, 64770, 64643, 64573, + 64640, 64567, 64708, 64568, 64641, 64711, 64571, 64747, 64710, 64570, + 64709, 64569, 64963, 64955, 64951, 64642, 64712, 64572, 64748, 64644, + 64574, 64974, 64538, 64799, 64771, 64683, 64537, 64684, 64539, 64800, + 64772, 65272, 65271, 65274, 65273, 65270, 65269, 64646, 64579, 65276, + 65275, 64898, 64949, 64896, 64897, 64714, 64576, 64717, 64899, 64900, + 64954, 64956, 64940, 64713, 64575, 64902, 64901, 64715, 64577, 64904, + 64903, 64941, 64645, 64716, 64578, 64749, 64647, 64580, 64585, 64648, + 64905, 64906, 64907, 64719, 64582, 64910, 64911, 64953, 64720, 64583, + 64909, 64914, 64908, 64960, 64718, 64581, 64945, 64649, 64721, 64584, + 64586, 65012, 64918, 64917, 64947, 64723, 64588, 64726, 64751, 64952, + 64957, 64921, 64920, 64919, 64967, 64722, 64587, 64923, 64922, 64652, + 64725, 64590, 64750, 64654, 64591, 64653, 64651, 64724, 64589, 64650, + 64655, 64592, 69336, 64895, 64948, 64894, 64946, 64707, 64564, 64638, + 64565, 64706, 64563, 64639, 64566, 65009, 69332, 69333, 69334, 64843, + 69335, 64833, 64835, 64836, 64837, 64834, 64969, 64459, 64971, 64970, + 64832, 64968, 64847, 64456, 64457, 64458, 64912, 64454, 64455, 64913, + 64453, 65014, 64604, 64869, 64868, 64937, 64689, 64544, 64965, 64870, 64691, 64545, 64801, 64773, 64690, 64811, 64783, 64802, 64774, 65013, - 64975, 65008, 65017, 65018, 64844, 64838, 64860, 64686, 64541, 64821, - 64817, 64744, 64862, 64861, 64685, 64540, 64820, 64936, 64966, 64687, - 64542, 64822, 64864, 64863, 64865, 64867, 64866, 64688, 64543, 64743, - 64791, 64763, 64810, 64782, 64792, 64764, 64606, 64609, 64755, 64607, - 64610, 64756, 64611, 64608, 64754, 64818, 64746, 64872, 64871, 64938, - 64806, 64814, 64778, 64824, 64873, 64805, 64813, 64777, 64823, 64875, - 64874, 64877, 64876, 64808, 64816, 64780, 64745, 64793, 64765, 64807, - 64815, 64779, 64825, 64809, 64781, 64794, 64766, 65022, 64846, 64882, - 64881, 64883, 64884, 64819, 64551, 64826, 64785, 64757, 64696, 64550, - 64786, 64758, 64851, 64850, 64849, 64674, 64524, 64677, 64740, 64930, - 64852, 64929, 64675, 64525, 64928, 64848, 64927, 64673, 64523, 64932, - 64853, 64855, 64854, 64931, 64626, 64676, 64526, 64739, 64628, 64527, - 64627, 64625, 64624, 64629, 64528, 64603, 64529, 64634, 64531, 64632, - 64678, 64530, 64741, 64633, 64631, 64630, 64635, 64532, 64742, 65016, - 64661, 64601, 64616, 64515, 64491, 64490, 64493, 64492, 64503, 64504, - 64502, 64667, 64736, 64664, 64513, 64663, 64512, 64665, 64614, 64666, - 64514, 64735, 64615, 64499, 64498, 64495, 64494, 64617, 64516, 64501, - 64500, 64613, 64612, 64497, 64496, 64942, 64731, 64598, 64734, 64753, - 64660, 64658, 64943, 64730, 64597, 64732, 64599, 64925, 64924, 64944, - 64659, 64733, 64600, 64752, 64657, 64662, 64602, 64506, 64507, 64505, - 64697, 64552, 64827, 2204, 1619, 1624, 2303, 126492, 126494, 126493, - 126495, 126644, 126638, 126641, 126647, 126648, 126646, 126632, 126645, - 126630, 126650, 126626, 126636, 126625, 126640, 126643, 126633, 126631, - 126651, 126637, 126635, 126649, 126627, 126642, 126639, 126629, 126489, - 126467, 126518, 126517, 126516, 126510, 126513, 126503, 126500, 126519, - 126506, 126498, 126508, 126497, 126512, 126505, 126523, 126509, 126507, - 126514, 126511, 126521, 126612, 126606, 126609, 126615, 126592, 126607, - 126599, 126596, 126616, 126614, 126600, 126613, 126598, 126618, 126594, - 126604, 126593, 126608, 126611, 126601, 126619, 126605, 126603, 126617, - 126595, 126610, 126597, 126475, 126704, 126705, 126588, 126590, 126585, - 126582, 126568, 126581, 126580, 126574, 126577, 126567, 126564, 126583, - 126570, 126562, 126572, 126561, 126576, 126569, 126586, 126587, 126573, - 126578, 126575, 126484, 126478, 126481, 126557, 126559, 126553, 126548, - 126542, 126545, 126551, 126530, 126537, 126535, 126555, 126541, 126539, - 126546, 126543, 126472, 126488, 126486, 126485, 126464, 126479, 126487, - 126474, 126470, 126490, 126466, 126476, 126465, 126480, 126483, 126473, - 126471, 126491, 126477, 126482, 126469, 1541, 1536, 2290, 2289, 2288, - 1642, 2199, 1550, 2192, 1769, 2193, 2184, 1544, 1629, 2296, 2301, 2298, - 1772, 2183, 1623, 983633, 983635, 983640, 983634, 983638, 983636, 983639, - 983637, 983641, 1563, 1617, 65148, 65149, 1554, 1555, 1552, 1537, 1539, - 1540, 1790, 1789, 1553, 1551, 1556, 2249, 1560, 1761, 2250, 2272, 1558, - 983202, 1751, 1750, 1753, 1752, 1762, 1764, 1768, 2264, 2273, 1756, 2261, - 1755, 1557, 2200, 2260, 2266, 2268, 2267, 2269, 2252, 2271, 2270, 2291, - 1767, 2251, 1760, 1759, 1559, 2253, 1754, 2263, 2262, 2265, 2202, 2201, - 69375, 2203, 69373, 69374, 2259, 1773, 1763, 1562, 1561, 1766, 1765, - 1622, 1618, 65150, 65151, 2256, 2205, 64444, 64435, 64434, 64441, 64440, - 64439, 64438, 64446, 64445, 64437, 64436, 64449, 64448, 64443, 64442, - 64450, 64447, 1758, 1600, 2179, 2180, 65137, 2181, 65139, 2285, 2282, - 2286, 2283, 2287, 2284, 1566, 2275, 1644, 1627, 1626, 1628, 2190, 1631, - 1567, 1625, 1545, 1546, 1542, 1543, 1637, 1636, 1639, 1638, 1635, 1634, - 1632, 1641, 1633, 1640, 1375, 1370, 1359, 1337, 1349, 1362, 1347, 1353, - 1342, 1361, 1360, 1356, 1333, 1335, 1336, 1346, 1331, 1355, 1345, 1364, - 1343, 1363, 1354, 1351, 1357, 1329, 1358, 1352, 1340, 1366, 1341, 1339, - 1330, 1348, 1350, 1338, 1334, 1344, 1332, 1365, 1373, 1372, 1371, 1395, - 1401, 1390, 1409, 1408, 1404, 1381, 1383, 1384, 1394, 1379, 1403, 1393, - 1412, 1391, 1411, 1402, 1399, 1405, 1376, 1407, 1385, 1377, 1406, 1400, - 1397, 1416, 1410, 1388, 1414, 1389, 1387, 1378, 1396, 1398, 1386, 1382, - 1392, 1380, 1413, 1415, 64279, 64277, 64275, 64276, 64278, 1369, 1418, - 1423, 1417, 1374, 129201, 10549, 10548, 129200, 10550, 10551, 129968, - 128667, 127912, 9800, 8978, 9738, 65948, 42, 8727, 8258, 128562, 11225, - 9954, 8771, 8870, 128095, 9883, 128663, 127975, 128762, 8371, 127814, - 68352, 68353, 68357, 68355, 68358, 68359, 68356, 68354, 68373, 68374, - 68372, 68393, 68405, 68388, 68387, 68386, 68391, 68390, 68389, 68371, - 68370, 68369, 68403, 68401, 68404, 68399, 68394, 68395, 68378, 68381, - 68377, 68366, 68367, 68362, 68363, 68364, 68365, 68385, 68384, 68380, - 68379, 68402, 68400, 68360, 68361, 68375, 68383, 68376, 68368, 68398, - 68392, 68382, 68397, 68396, 68409, 129361, 1547, 129518, 8525, 9810, - 129683, 128118, 128036, 127868, 128124, 128700, 128281, 128386, 11101, - 11099, 983056, 10155, 128043, 129363, 127992, 129441, 129366, 129391, - 128708, 7007, 7005, 7006, 6991, 6990, 6917, 6918, 6987, 6988, 6928, 6953, - 6954, 6936, 6937, 6948, 6944, 6943, 6949, 6984, 6927, 6933, 6934, 6919, - 6920, 6929, 6930, 6921, 6922, 6938, 6939, 6931, 6981, 6932, 6982, 6958, - 6925, 6926, 6950, 6945, 6935, 6940, 6951, 6952, 6957, 6923, 6924, 6962, - 6960, 6961, 6946, 6942, 6941, 6947, 6983, 6985, 6986, 6963, 6955, 6959, - 6956, 7022, 7025, 7021, 7024, 7023, 7026, 7020, 7019, 7027, 7012, 7013, - 7018, 7015, 7017, 7016, 7010, 7014, 7009, 7011, 7032, 7036, 7033, 7034, - 7035, 7031, 7030, 7029, 7028, 7003, 7038, 7008, 7002, 7037, 7039, 6915, - 6913, 6912, 6914, 6916, 6964, 6972, 6973, 6970, 6971, 6968, 6969, 6974, - 6975, 6977, 6976, 6965, 6978, 6979, 6966, 6967, 6980, 7004, 6997, 6996, - 6999, 6998, 6995, 6994, 6992, 7001, 6993, 7000, 127880, 10057, 9744, - 128503, 128505, 128499, 128501, 11197, 9745, 9746, 128502, 128500, 10007, - 129526, 129648, 42737, 42736, 42741, 42740, 42733, 42699, 42713, 42712, - 42692, 42706, 42683, 42719, 42734, 42735, 42682, 42729, 42657, 42725, - 42659, 42670, 42718, 42666, 42716, 42701, 42675, 42671, 42722, 42720, - 42727, 42723, 42702, 42726, 42677, 42707, 42708, 42709, 42686, 42694, - 42674, 42731, 42695, 42685, 42684, 42691, 42673, 42664, 42715, 42703, - 92193, 92188, 92161, 92199, 92240, 92179, 92211, 92219, 92213, 92243, - 92207, 92216, 92183, 92184, 92238, 92171, 983268, 92174, 92203, 92186, - 92210, 92230, 92175, 92221, 92232, 92246, 92198, 92214, 92190, 92176, - 92197, 92196, 92164, 92245, 92212, 92201, 92160, 92182, 92173, 92229, - 92227, 92180, 92237, 92241, 92223, 92185, 92194, 92178, 92239, 92225, - 92233, 92167, 92191, 92231, 92244, 92204, 92226, 92236, 92200, 92189, - 92187, 92162, 92163, 92169, 92170, 92202, 92205, 92222, 92168, 92165, - 92215, 92224, 92208, 92220, 92235, 92177, 92181, 92195, 92234, 92209, - 92166, 92172, 92206, 92192, 92228, 92217, 92242, 92218, 92262, 92271, - 92272, 92270, 92294, 92253, 92301, 92256, 92273, 92259, 92251, 92297, - 92300, 92296, 92295, 92255, 92252, 92292, 92286, 92268, 92290, 92281, - 92267, 92284, 92287, 92282, 92283, 92277, 92298, 92276, 92302, 92247, - 92299, 92288, 92269, 92260, 92261, 92257, 92274, 92289, 92263, 92279, - 92265, 92266, 92250, 92249, 92285, 92248, 92264, 92280, 92258, 92254, - 92278, 92291, 92293, 92275, 92310, 92320, 92312, 92394, 92393, 92319, - 92384, 92321, 92386, 92361, 92373, 92366, 92328, 92329, 92357, 92342, - 92397, 92365, 92376, 92339, 92382, 92363, 92318, 92387, 92383, 92315, - 92385, 92354, 92311, 92381, 92343, 92326, 92364, 92324, 92391, 92344, - 92390, 92338, 92396, 92308, 92349, 92375, 92378, 92317, 92331, 92362, - 92336, 92341, 92370, 92347, 92307, 92303, 92309, 92395, 92368, 92356, - 92367, 92360, 92389, 92333, 92359, 92374, 92351, 92325, 92371, 92350, - 92345, 92372, 92314, 92340, 92323, 92304, 92313, 92316, 92398, 92399, - 92380, 92330, 92379, 92392, 92335, 92332, 92346, 92400, 92358, 92334, - 92353, 92337, 92306, 92369, 92388, 92322, 92305, 92327, 92377, 92352, - 92348, 92355, 92436, 92517, 92488, 92434, 92415, 92431, 92454, 92460, - 92420, 92489, 92422, 92474, 92479, 92449, 92502, 92439, 92484, 92495, - 92464, 92511, 92406, 92446, 92497, 92426, 92482, 92448, 92515, 92401, - 92478, 92427, 92496, 92458, 92424, 92445, 92404, 92466, 92444, 92438, - 92442, 92441, 92510, 92499, 92425, 92437, 92440, 92471, 92465, 92409, - 92483, 92475, 92467, 92485, 92457, 92432, 92476, 92435, 92412, 92414, - 92430, 92407, 92403, 92405, 92487, 92418, 92486, 92408, 92447, 92459, - 92480, 92472, 92505, 92514, 92450, 92463, 92410, 92493, 92507, 92503, - 92462, 92506, 92443, 92509, 92469, 92461, 92490, 92512, 92494, 92455, - 92417, 92501, 92413, 92508, 92500, 92504, 92473, 92419, 92498, 92423, - 92516, 92428, 92452, 92451, 92481, 92416, 92456, 92433, 92477, 92492, - 92491, 92513, 92411, 92402, 92421, 92453, 92468, 92470, 92429, 92661, - 92626, 92616, 92596, 92612, 92603, 92673, 92651, 92627, 92597, 92585, - 92578, 92615, 92565, 92552, 92602, 92674, 92570, 92646, 92599, 92532, - 92587, 92538, 92620, 92670, 92665, 92575, 92521, 92633, 92595, 92523, - 92556, 92539, 92664, 92653, 92667, 92600, 92542, 92555, 92668, 92520, - 92582, 92591, 92581, 92551, 92654, 92611, 92614, 92666, 92671, 92617, - 92637, 92562, 92518, 92592, 92558, 92528, 92607, 92535, 92557, 92563, - 92550, 92624, 92640, 92657, 92531, 92601, 92553, 92543, 92619, 92598, - 92541, 92544, 92579, 92658, 92554, 92628, 92648, 92547, 92527, 92545, - 92540, 92604, 92622, 92589, 92608, 92583, 92609, 92662, 92613, 92584, - 92625, 92634, 92524, 92536, 92605, 92647, 92548, 92663, 92593, 92621, - 92568, 92610, 92576, 92529, 92618, 92649, 92561, 92656, 92526, 92655, - 92546, 92534, 92560, 92580, 92659, 92660, 92638, 92571, 92525, 92549, - 92559, 92635, 92577, 92530, 92636, 92669, 92572, 92672, 92537, 92519, - 92630, 92586, 92569, 92566, 92573, 92652, 92522, 92574, 92650, 92533, - 92567, 92623, 92606, 92639, 92564, 92590, 92642, 92643, 92594, 92641, - 92645, 92644, 92588, 92629, 92632, 92631, 92710, 92695, 92694, 92726, - 92675, 92719, 92677, 92718, 92682, 92717, 92689, 92720, 92724, 92685, - 92722, 92723, 92711, 92712, 92698, 92688, 92697, 92696, 92702, 92687, - 92704, 92681, 92708, 92703, 92706, 92714, 92709, 92679, 92721, 92684, - 92683, 92707, 92691, 92713, 92700, 92693, 92727, 92690, 92692, 92686, - 92680, 92725, 92699, 92701, 92705, 92716, 92728, 92678, 92715, 92676, - 42693, 42698, 42711, 42696, 42667, 42717, 42704, 42661, 42721, 42669, - 42668, 42705, 42700, 42680, 42678, 42710, 42688, 42681, 42732, 42676, - 42679, 42730, 42728, 42672, 42662, 42724, 42687, 42689, 42690, 42697, - 42714, 42660, 42656, 42665, 42663, 42658, 42738, 42742, 42739, 42743, - 127974, 128183, 128180, 128182, 128181, 127820, 129685, 128202, 129532, - 128136, 129530, 127936, 92916, 92912, 92915, 92913, 92914, 92887, 92894, - 92908, 92880, 92907, 92893, 92886, 92888, 92881, 92906, 92896, 92891, - 92902, 92900, 92885, 92890, 92884, 92904, 92905, 92899, 92889, 92897, - 92892, 92895, 92882, 92898, 92883, 92901, 92903, 92909, 92917, 9918, - 129415, 7152, 7153, 7124, 7108, 7114, 7130, 7139, 7127, 7138, 7133, 7136, - 7113, 7111, 7117, 7119, 7107, 7135, 7125, 7112, 7123, 7129, 7116, 7132, - 7105, 7126, 7128, 7110, 7109, 7137, 7121, 7118, 7106, 7120, 7134, 7122, - 7115, 7131, 7104, 7140, 7141, 7154, 7155, 7167, 7165, 7166, 7164, 7142, - 7150, 7151, 7144, 7147, 7149, 7143, 7145, 7146, 7148, 128704, 128705, - 128267, 127900, 127901, 9835, 9836, 129492, 128059, 127958, 128147, - 129451, 129752, 127866, 129714, 983055, 128276, 129745, 128277, 9086, - 128718, 2557, 2432, 2519, 2548, 2552, 2551, 2550, 2549, 2553, 2454, 2510, - 983661, 2453, 2480, 2545, 2544, 2525, 2524, 2556, 2443, 2528, 2444, 2529, - 2527, 2479, 2437, 2438, 2448, 2452, 2466, 2465, 2471, 2470, 2464, 2463, - 2469, 2468, 2441, 2442, 2439, 2440, 2457, 2467, 2462, 2472, 2486, 2487, - 2488, 2477, 2476, 2459, 2458, 2456, 2455, 2461, 2460, 2475, 2474, 2489, - 2482, 2478, 2447, 2451, 2547, 2546, 983651, 983650, 983652, 2558, 2433, - 2492, 2493, 2434, 2509, 2435, 2554, 2494, 2504, 2508, 2497, 2498, 2499, - 2500, 2530, 2531, 2495, 2496, 2503, 2507, 2539, 2538, 2541, 2540, 2537, - 2536, 2534, 2543, 2535, 2542, 2555, 11102, 127857, 9004, 9187, 8812, - 8502, 8757, 129475, 128719, 72710, 72711, 72712, 72746, 72704, 72705, - 72715, 72717, 72731, 72730, 72736, 72735, 72729, 72728, 72734, 72733, - 72708, 72709, 72706, 72707, 72722, 72732, 72727, 72737, 72747, 72748, - 72749, 72741, 72740, 72724, 72723, 72721, 72720, 72726, 72725, 72719, - 72718, 72739, 72738, 72750, 72745, 72742, 72744, 72743, 72714, 72716, - 72801, 72810, 72806, 72797, 72807, 72798, 72802, 72811, 72800, 72809, - 72799, 72808, 72796, 72805, 72804, 72795, 72803, 72794, 72764, 72768, - 72765, 72767, 72766, 72756, 72757, 72758, 72751, 72761, 72763, 72754, - 72755, 72752, 72753, 72760, 72762, 72770, 72769, 72789, 72788, 72791, - 72790, 72787, 72786, 72784, 72793, 72785, 72792, 72771, 72772, 72773, - 72812, 128692, 128690, 10745, 10744, 129506, 127921, 127874, 128038, - 8383, 129766, 9763, 128089, 129452, 9679, 9864, 10733, 9865, 9210, 11176, - 11177, 11178, 11179, 11180, 11182, 11181, 11183, 9960, 10028, 9821, - 129554, 129596, 129609, 129612, 9818, 129551, 129593, 9822, 129543, - 129564, 129585, 129597, 129606, 129555, 129619, 129617, 129618, 9823, - 129556, 129598, 9819, 129552, 129594, 9820, 129553, 129595, 129575, - 129572, 129576, 129577, 129573, 129574, 9827, 9670, 11201, 10070, 10730, - 11230, 9830, 128419, 128899, 9196, 9662, 9660, 11167, 127778, 9922, 9923, - 10047, 9873, 10022, 128447, 128420, 9829, 11042, 128426, 11052, 10711, - 11044, 117867, 117870, 117869, 117868, 11035, 9194, 9198, 128896, 9668, - 9666, 9664, 11164, 8268, 9944, 128412, 9754, 9699, 9698, 10731, 9207, - 11206, 11045, 9204, 11207, 11047, 9205, 11208, 9206, 11205, 128921, - 128927, 9726, 9724, 9912, 117871, 10002, 128392, 9648, 11039, 127986, - 118451, 128413, 9755, 9193, 9197, 9654, 9199, 128898, 11091, 9658, 9656, - 10145, 10148, 11166, 8269, 127990, 9644, 11049, 11050, 11089, 9642, - 129997, 9787, 9632, 11200, 9209, 128306, 9728, 128369, 9927, 9984, - 128900, 128909, 9986, 9751, 9824, 9733, 128919, 128925, 128908, 9951, - 128383, 9742, 9942, 128418, 128897, 9195, 9652, 9650, 9700, 9701, 11165, - 9851, 9646, 11054, 128920, 128926, 11037, 11204, 10707, 10067, 8493, - 8460, 8465, 8476, 8488, 10164, 10166, 10165, 9250, 118018, 118039, - 118128, 118187, 118218, 118159, 118054, 118112, 118234, 118084, 118202, - 118143, 118031, 118062, 118120, 118240, 118179, 118090, 118210, 118151, - 118047, 118104, 118226, 118165, 118076, 118194, 118135, 118021, 118035, - 118066, 118124, 118243, 118183, 118093, 118214, 118155, 118050, 118108, - 118230, 118168, 118080, 118198, 118139, 118028, 118058, 118116, 118237, - 118175, 118087, 118206, 118147, 118043, 118100, 118222, 118162, 118072, - 118190, 118131, 118029, 118060, 118118, 118177, 118208, 118149, 118045, - 118102, 118224, 118023, 118037, 118068, 118126, 118244, 118185, 118095, - 118216, 118157, 118052, 118110, 118232, 118170, 118082, 118200, 118141, - 118074, 118192, 118133, 118020, 118033, 118064, 118122, 118242, 118181, - 118092, 118212, 118153, 118048, 118106, 118228, 118167, 118078, 118196, - 118137, 118026, 118056, 118114, 118235, 118173, 118085, 118204, 118145, - 118041, 118098, 118220, 118160, 118070, 118188, 118129, 118034, 118065, - 118123, 118182, 118213, 118154, 118049, 118107, 118229, 118079, 118197, - 118138, 118017, 118024, 118038, 118069, 118127, 118245, 118186, 118096, - 118217, 118158, 118053, 118111, 118233, 118171, 118083, 118201, 118142, - 118030, 118061, 118119, 118239, 118178, 118089, 118209, 118150, 118046, - 118103, 118225, 118164, 118075, 118193, 118134, 118027, 118057, 118115, - 118236, 118174, 118086, 118205, 118146, 118042, 118099, 118221, 118161, - 118071, 118189, 118130, 118025, 118055, 118113, 118172, 118203, 118144, - 118040, 118097, 118219, 118016, 118022, 118036, 118067, 118125, 118184, - 118094, 118215, 118156, 118051, 118109, 118231, 118169, 118081, 118199, - 118140, 118059, 118117, 118238, 118176, 118088, 118207, 118148, 118044, - 118101, 118223, 118163, 118073, 118191, 118132, 118019, 118105, 118227, - 118166, 118032, 118063, 118121, 118241, 118180, 118091, 118211, 118152, - 118077, 118195, 118136, 129792, 129794, 129798, 129806, 129821, 129836, - 129813, 129844, 129829, 129802, 129817, 129848, 129832, 129810, 129840, - 129825, 129796, 129804, 129819, 129850, 129834, 129842, 129827, 129800, - 129815, 129846, 129831, 129808, 129838, 129823, 129793, 129801, 129816, - 129847, 129797, 129805, 129820, 129851, 129835, 129812, 129843, 129828, - 129809, 129839, 129824, 129795, 129803, 129818, 129849, 129833, 129811, - 129841, 129826, 129799, 129814, 129845, 129830, 129807, 129837, 129822, - 127804, 128033, 128216, 128153, 129744, 128939, 128951, 128957, 128945, - 128902, 128912, 128932, 128366, 128278, 128209, 128218, 129667, 12731, - 12727, 12726, 12724, 12725, 12570, 12574, 12718, 12576, 12719, 12578, - 12580, 12713, 12735, 12720, 12584, 12715, 12572, 12579, 12581, 12709, - 12708, 12573, 12575, 12582, 12557, 12728, 12588, 12707, 12732, 12583, - 12714, 12723, 12589, 12716, 12712, 12585, 12555, 12587, 12717, 12591, - 12571, 12722, 12711, 12590, 12734, 12721, 12710, 12577, 12567, 12563, - 12705, 12730, 12558, 12733, 12568, 12564, 12556, 12729, 12569, 12565, - 12549, 12704, 12560, 12706, 12553, 12552, 12559, 12551, 12550, 12561, - 12566, 12554, 12586, 12562, 118257, 118258, 118259, 118260, 117854, - 11867, 117853, 118253, 118255, 11868, 117855, 118249, 118251, 118247, - 11211, 8993, 130029, 8990, 8973, 11812, 8991, 8972, 11813, 130030, 9141, - 9142, 10555, 9183, 9181, 130026, 130018, 9185, 127870, 128144, 127893, - 128335, 129379, 127923, 8904, 10705, 10706, 127993, 118282, 117792, - 117791, 118281, 9574, 9559, 9556, 9577, 9565, 9562, 9553, 9580, 9571, - 9568, 9552, 9511, 9490, 9503, 9486, 9537, 9520, 9513, 9489, 9505, 9485, - 9543, 9519, 9558, 9555, 9573, 9557, 9554, 9572, 9592, 9598, 9593, 9599, - 9499, 9531, 9495, 9475, 9547, 9515, 9507, 9595, 9523, 9491, 9487, 9551, - 9549, 9483, 9481, 9479, 9477, 9473, 9594, 9541, 9525, 9517, 9533, 9530, - 9522, 9546, 9539, 9582, 9581, 9583, 9584, 130010, 130014, 129954, 129958, - 130003, 129959, 129964, 129965, 129955, 130000, 129952, 129956, 129963, - 129960, 129953, 129961, 129957, 129962, 130007, 130005, 130004, 130012, - 9586, 130008, 130011, 130002, 130015, 130006, 9585, 130009, 130001, - 130013, 9587, 129966, 9591, 9516, 9488, 9484, 9550, 9548, 9472, 117787, - 117788, 129967, 9588, 9596, 9482, 9480, 9478, 9476, 117789, 9589, 9597, - 9524, 9496, 9492, 9474, 118297, 118295, 118296, 118294, 9532, 9508, 9500, - 117790, 9590, 9542, 9526, 9518, 9534, 9529, 9521, 9545, 9540, 9536, 9510, - 9498, 9502, 9494, 9528, 9544, 9514, 9497, 9506, 9493, 9527, 9564, 9561, - 9576, 9563, 9560, 9575, 9570, 9567, 9579, 9538, 9512, 9504, 9535, 9509, - 9501, 9569, 9566, 9578, 129354, 983263, 128163, 128102, 128713, 128023, - 129460, 69649, 69685, 69749, 69745, 69746, 69687, 69686, 69637, 69638, - 69648, 69650, 69664, 69663, 69669, 69668, 69662, 69661, 69667, 69666, - 69643, 69644, 69645, 69646, 69679, 69641, 69642, 69639, 69640, 69684, - 69678, 69655, 69665, 69660, 69670, 69680, 69681, 69682, 69674, 69673, - 69657, 69656, 69654, 69653, 69659, 69658, 69652, 69651, 69672, 69671, - 69683, 69675, 69677, 69676, 69647, 69721, 69730, 69726, 69717, 69727, - 69718, 69722, 69731, 69720, 69729, 69719, 69728, 69716, 69725, 69724, - 69715, 69723, 69714, 69732, 69733, 69759, 69709, 69707, 69706, 69705, - 69708, 69632, 69744, 69635, 69634, 69636, 69633, 69700, 69747, 69748, - 69689, 69688, 69699, 69701, 69692, 69693, 69694, 69695, 69696, 69697, - 69690, 69691, 69698, 69702, 69704, 69703, 69739, 69738, 69741, 69740, - 69737, 69736, 69734, 69743, 69735, 69742, 10241, 10243, 10247, 10255, - 10271, 10303, 10367, 10495, 10431, 10335, 10463, 10399, 10287, 10351, - 10479, 10415, 10319, 10447, 10383, 10263, 10295, 10359, 10487, 10423, - 10327, 10455, 10391, 10279, 10343, 10471, 10407, 10311, 10439, 10375, - 10251, 10267, 10299, 10363, 10491, 10427, 10331, 10459, 10395, 10283, - 10347, 10475, 10411, 10315, 10443, 10379, 10259, 10291, 10355, 10483, - 10419, 10323, 10451, 10387, 10275, 10339, 10467, 10403, 10307, 10435, - 10371, 10245, 10253, 10269, 10301, 10365, 10493, 10429, 10333, 10461, - 10397, 10285, 10349, 10477, 10413, 10317, 10445, 10381, 10261, 10293, - 10357, 10485, 10421, 10325, 10453, 10389, 10277, 10341, 10469, 10405, - 10309, 10437, 10373, 10249, 10265, 10297, 10361, 10489, 10425, 10329, - 10457, 10393, 10281, 10345, 10473, 10409, 10313, 10441, 10377, 10257, - 10289, 10353, 10481, 10417, 10321, 10449, 10385, 10273, 10337, 10465, - 10401, 10305, 10433, 10369, 10242, 10246, 10254, 10270, 10302, 10366, - 10494, 10430, 10334, 10462, 10398, 10286, 10350, 10478, 10414, 10318, - 10446, 10382, 10262, 10294, 10358, 10486, 10422, 10326, 10454, 10390, - 10278, 10342, 10470, 10406, 10310, 10438, 10374, 10250, 10266, 10298, - 10362, 10490, 10426, 10330, 10458, 10394, 10282, 10346, 10474, 10410, - 10314, 10442, 10378, 10258, 10290, 10354, 10482, 10418, 10322, 10450, - 10386, 10274, 10338, 10466, 10402, 10306, 10434, 10370, 10244, 10252, - 10268, 10300, 10364, 10492, 10428, 10332, 10460, 10396, 10284, 10348, - 10476, 10412, 10316, 10444, 10380, 10260, 10292, 10356, 10484, 10420, - 10324, 10452, 10388, 10276, 10340, 10468, 10404, 10308, 10436, 10372, - 10248, 10264, 10296, 10360, 10488, 10424, 10328, 10456, 10392, 10280, - 10344, 10472, 10408, 10312, 10440, 10376, 10256, 10288, 10352, 10480, - 10416, 10320, 10448, 10384, 10272, 10336, 10464, 10400, 10304, 10432, - 10368, 10240, 129504, 983125, 129329, 127838, 728, 127753, 128112, - 128188, 129650, 129521, 9099, 166, 128148, 129382, 129294, 129529, - 129483, 129767, 128027, 6663, 6662, 6659, 6658, 6671, 6670, 6667, 6666, - 6661, 6668, 6665, 6657, 6678, 6669, 6656, 6674, 6660, 6673, 6676, 6664, - 6675, 6672, 6677, 6683, 6681, 6679, 6682, 6680, 6687, 6686, 5957, 5960, - 5962, 5959, 5956, 5969, 5955, 5966, 5963, 5961, 5965, 5968, 5958, 5967, - 5964, 5952, 5953, 5954, 5970, 5971, 8226, 8729, 128363, 128364, 9678, - 128652, 128101, 128100, 128655, 129480, 129419, 127959, 127791, 129699, - 118939, 118940, 118944, 118943, 118941, 118942, 118938, 118945, 118882, - 118876, 118824, 118835, 118797, 118866, 118957, 118801, 118802, 118865, - 118818, 118916, 118819, 118917, 118899, 118935, 119018, 119022, 119021, - 119020, 119023, 119019, 119017, 118986, 118984, 118985, 118843, 118887, - 118808, 118870, 119003, 119002, 119004, 119005, 118937, 118991, 118995, - 118990, 118992, 118994, 118993, 118930, 118933, 118931, 118932, 119014, - 118918, 118912, 119015, 118785, 118831, 118907, 118966, 118900, 118880, - 118798, 118888, 118884, 118869, 118960, 118959, 118958, 118836, 118969, - 118977, 118978, 118971, 118976, 118975, 118973, 118970, 118987, 118979, - 118980, 118972, 118983, 983274, 118982, 118974, 118988, 118981, 119000, - 119001, 118929, 118928, 118806, 119029, 118927, 118898, 118964, 118965, - 118853, 118968, 118837, 118967, 118936, 118956, 118810, 118854, 118847, - 118839, 118906, 118791, 118800, 119024, 119026, 118862, 118812, 119025, - 119027, 118863, 118811, 118820, 119028, 118911, 118842, 118850, 118921, - 118858, 118913, 118914, 118915, 118834, 118860, 118868, 118796, 118881, - 118922, 118926, 118925, 118924, 118923, 118830, 118877, 118949, 118947, - 118948, 118963, 118955, 118962, 118946, 118961, 118953, 118952, 118951, - 118950, 118954, 118871, 118805, 118855, 118828, 118901, 118787, 118788, - 118856, 118816, 118875, 118845, 118879, 118793, 118846, 118878, 118814, - 118822, 118873, 118859, 118857, 118849, 118840, 118861, 118786, 118895, - 118841, 118874, 118892, 118897, 118807, 118784, 118821, 118844, 118825, - 118889, 119010, 119012, 119013, 119011, 119006, 119008, 119009, 119007, - 118910, 118815, 118852, 119016, 118826, 118885, 118827, 118803, 118886, - 118792, 118813, 118920, 118799, 118833, 118829, 118904, 118902, 118903, - 118905, 118804, 118934, 118919, 118832, 118893, 118838, 118851, 118883, - 118894, 118891, 118896, 118823, 118789, 118790, 118872, 118817, 118809, - 118908, 118909, 118996, 118998, 118997, 118999, 118989, 118794, 118795, - 118867, 118864, 118848, 118890, 983262, 983126, 983057, 9764, 8454, - 129305, 128197, 128247, 128248, 127957, 983098, 5130, 5131, 5122, 6322, - 5148, 5551, 5310, 5382, 5166, 6321, 5759, 5559, 5556, 5557, 5558, 5567, - 5564, 5565, 5566, 5563, 5560, 5561, 5562, 5555, 5552, 5553, 5554, 5384, - 5439, 6387, 6388, 5281, 5264, 6382, 6389, 5203, 5674, 5675, 5677, 5676, - 5673, 5672, 5706, 5707, 5709, 5708, 5705, 5704, 5204, 5574, 5575, 5577, - 5576, 5573, 5572, 6384, 6381, 5620, 6383, 5617, 5618, 5619, 5616, 5615, - 5195, 5592, 5593, 5595, 5594, 5591, 5590, 5174, 5175, 5129, 5703, 5662, - 5663, 5665, 5664, 5661, 5660, 5655, 5656, 6386, 5659, 5657, 5654, 5652, - 5633, 5629, 5630, 5632, 5631, 5628, 5627, 5623, 5624, 5626, 5625, 5622, - 5621, 5636, 5637, 5639, 5329, 5638, 5635, 5634, 5722, 5718, 5719, 5721, - 5720, 5717, 5716, 5712, 5713, 5715, 5714, 5711, 5710, 5614, 5610, 5611, - 5613, 5612, 5609, 5608, 5702, 5698, 5699, 5701, 5700, 5697, 5696, 5686, - 5687, 5689, 5688, 5685, 5684, 5692, 5693, 5695, 5694, 5691, 5690, 5737, - 5738, 5740, 5739, 5736, 5735, 5604, 5605, 5607, 5606, 5603, 5602, 5598, - 5599, 5601, 5600, 5597, 5596, 5725, 5726, 5728, 5727, 5724, 5723, 5680, - 5681, 5683, 5682, 5679, 5678, 5668, 5669, 5671, 5670, 5667, 5666, 5731, - 5732, 5734, 5733, 5730, 5729, 5642, 5643, 5645, 5644, 5641, 5640, 5580, - 5581, 5583, 5582, 5579, 5578, 5586, 5587, 5589, 5588, 5585, 5584, 5648, - 5649, 5651, 5650, 5647, 5646, 5128, 5265, 5258, 5276, 5278, 5272, 5274, - 5268, 5270, 5266, 5741, 5261, 5262, 5259, 5260, 5257, 5121, 6364, 5163, - 5469, 5465, 5466, 5460, 5461, 5158, 5157, 5162, 5155, 5156, 6367, 6366, - 5160, 5152, 5151, 5153, 5154, 5161, 5159, 5462, 5742, 5463, 5464, 5467, - 5459, 5120, 5501, 5123, 5124, 5164, 5251, 5252, 5246, 5248, 6329, 5242, - 5244, 5238, 5240, 5236, 5234, 5235, 5228, 6328, 5231, 5232, 5229, 5230, - 5227, 5354, 5542, 5540, 5541, 5538, 5539, 5536, 5537, 5338, 5339, 5332, - 6333, 5350, 5352, 5346, 5348, 5342, 5344, 5340, 5335, 5336, 5333, 5334, - 5331, 5307, 5283, 5356, 5458, 5287, 5288, 5385, 5290, 5291, 5284, 6330, - 5302, 5304, 5298, 5300, 5294, 5296, 5292, 5285, 5286, 5309, 5328, 5473, - 5475, 5471, 5319, 5386, 5390, 5391, 5388, 5389, 5380, 5387, 5142, 5147, - 5280, 5250, 5306, 5327, 5221, 5437, 72372, 72373, 72370, 72371, 72368, - 72369, 72378, 72379, 72376, 72377, 72374, 72375, 5320, 5313, 6332, 5525, - 5523, 5524, 5518, 5744, 5521, 5522, 5519, 5520, 5526, 5749, 5750, 5747, - 5748, 5745, 5746, 5499, 5497, 5498, 5495, 5496, 5493, 5494, 5492, 5500, - 5316, 5317, 6331, 5323, 5325, 6346, 6348, 6342, 6344, 5321, 5314, 5315, - 5312, 5330, 5509, 5507, 5508, 5502, 5743, 5505, 5506, 5503, 5504, 5125, - 6361, 6347, 6349, 6343, 6345, 6362, 6363, 6359, 6358, 6360, 6356, 6357, - 5165, 5126, 6320, 5193, 5184, 5186, 6326, 5188, 5190, 5180, 5182, 5178, - 5176, 5177, 5168, 6325, 5171, 5172, 6324, 5169, 5170, 5167, 5456, 6368, - 5443, 6355, 5454, 6353, 6354, 6351, 6352, 6350, 5451, 5452, 5445, 6341, - 5448, 5449, 5446, 5447, 5442, 5381, 5364, 6335, 5570, 6380, 5571, 5568, - 5569, 5653, 6385, 5658, 5529, 6379, 6378, 5530, 5527, 5528, 5441, 5282, - 5311, 5365, 5358, 5413, 5405, 5407, 6338, 5409, 5411, 5401, 5403, 5399, - 5395, 5396, 6336, 5397, 5398, 6337, 5393, 5394, 5392, 5361, 5256, 5253, - 5254, 5255, 5362, 6334, 5383, 5376, 5378, 5372, 5374, 5368, 5370, 5366, - 72383, 72380, 72381, 72382, 5359, 5360, 5357, 5222, 5482, 5550, 5548, - 5549, 5546, 5547, 5544, 5545, 5543, 6372, 5480, 6371, 5478, 5479, 5476, - 5477, 5472, 5474, 5470, 5512, 6377, 6376, 5513, 5510, 5511, 5487, 5486, - 6375, 5485, 6374, 6373, 5483, 5484, 5226, 5223, 5224, 5225, 5205, 5206, - 5197, 6327, 5217, 5219, 5213, 5215, 5209, 5211, 5207, 5491, 5488, 5489, - 5490, 5200, 5201, 5198, 5199, 5196, 5132, 5355, 5351, 5353, 5347, 5349, - 5343, 5345, 5341, 5453, 6370, 5450, 6369, 5444, 5308, 5303, 5305, 5299, - 5301, 5295, 5297, 5293, 5194, 5189, 5191, 5185, 5187, 5181, 5183, 5179, - 5440, 5434, 5436, 5430, 5432, 5426, 5428, 5424, 5324, 5326, 5322, 5457, - 5455, 5517, 5514, 5515, 5516, 5410, 5412, 5406, 5408, 5402, 5404, 5400, - 5377, 5379, 5373, 5375, 5369, 5371, 5367, 5277, 5279, 5273, 5275, 5269, - 5271, 5267, 5247, 5249, 5243, 5245, 5239, 5241, 5237, 5481, 5218, 5220, - 5214, 5216, 5210, 5212, 5208, 5468, 5144, 5146, 5139, 5141, 5135, 5137, - 5133, 6365, 5138, 5140, 5535, 5756, 5757, 5754, 5755, 5752, 5753, 5751, - 5534, 5531, 5532, 5533, 5758, 5143, 5145, 6323, 5134, 5136, 5438, 5192, - 5173, 5263, 5233, 5337, 5289, 5318, 5363, 5202, 5420, 5127, 5149, 5421, - 5422, 5415, 6340, 5418, 5419, 6339, 5433, 5435, 5429, 5431, 5425, 5427, - 5423, 5416, 5417, 5414, 5150, 983097, 983171, 917631, 128473, 9803, - 128367, 127852, 129387, 128758, 11839, 9809, 128199, 128450, 128451, - 8248, 8257, 8453, 66225, 66246, 66211, 66214, 66254, 66218, 66250, 66251, - 66252, 66253, 66229, 66238, 66244, 66227, 66224, 66222, 66223, 66242, - 66243, 66232, 66221, 66247, 66230, 66226, 66239, 66212, 66248, 66256, - 66235, 66208, 66215, 66210, 66220, 66234, 66255, 66240, 66241, 66236, - 66237, 66231, 66209, 66213, 66249, 66233, 66245, 66217, 66219, 66216, - 66228, 127904, 711, 127887, 129690, 983073, 129365, 9936, 128008, 128049, - 128569, 128572, 66888, 66864, 66912, 66882, 66873, 66902, 66889, 66890, - 66911, 66891, 66895, 66901, 66881, 66867, 66870, 66868, 66904, 66866, - 66876, 66910, 66879, 66883, 66897, 66915, 66884, 66878, 66885, 66914, - 66903, 66877, 66896, 66909, 66906, 66908, 66899, 66872, 66913, 66874, - 66871, 66875, 66869, 66893, 66887, 66892, 66907, 66900, 66894, 66905, - 66880, 66898, 66865, 66886, 66927, 9761, 127797, 9963, 8373, 184, 65102, - 65098, 8452, 162, 128328, 9907, 9939, 129681, 69908, 69907, 69913, 69912, - 69899, 69909, 69904, 69914, 69906, 69905, 69911, 69910, 69920, 69921, - 69918, 69917, 69901, 69900, 69898, 69897, 69903, 69902, 69896, 69895, - 69956, 69923, 69916, 69915, 69926, 69919, 69922, 69925, 69959, 69924, - 69891, 69894, 69892, 69893, 69888, 69890, 69889, 69952, 69927, 69957, - 69933, 69935, 69930, 69931, 69932, 69958, 69928, 69929, 69934, 69936, - 69939, 69954, 69953, 69947, 69946, 69949, 69948, 69945, 69944, 69942, - 69951, 69943, 69950, 69955, 69940, 69938, 69937, 43587, 43597, 43596, - 43573, 43572, 43574, 43571, 43590, 43586, 43595, 43588, 43585, 43584, - 43594, 43591, 43593, 43589, 43592, 43530, 43531, 43536, 43538, 43537, - 43543, 43544, 43551, 43552, 43548, 43547, 43546, 43553, 43550, 43549, - 43545, 43542, 43541, 43558, 43559, 43520, 43524, 43533, 43532, 43529, - 43528, 43535, 43534, 43527, 43526, 43540, 43539, 43560, 43556, 43555, - 43557, 43554, 43523, 43521, 43525, 43522, 43612, 43614, 43613, 43615, - 43561, 43568, 43569, 43562, 43563, 43567, 43566, 43565, 43570, 43564, - 43605, 43604, 43607, 43606, 43603, 43602, 43600, 43609, 43601, 43608, - 983058, 983140, 983137, 8256, 128200, 128185, 128201, 128638, 129941, - 10003, 129472, 128227, 5084, 5075, 5077, 5079, 5081, 5082, 5083, 5055, - 5037, 5038, 5039, 5040, 5041, 5042, 5054, 5056, 5057, 5058, 5059, 5060, - 5061, 5069, 5068, 5070, 5071, 5072, 5073, 5074, 5085, 5086, 5087, 5088, - 5089, 5090, 5091, 5092, 5093, 5094, 5095, 5096, 5076, 5078, 5080, 5030, - 5032, 5033, 5034, 5035, 5036, 5043, 5044, 5045, 5046, 5047, 5048, 5049, - 5050, 5051, 5052, 5053, 5109, 5062, 5063, 5064, 5065, 5066, 5067, 5097, - 5098, 5099, 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108, 5031, - 5024, 5025, 5026, 5027, 5028, 5029, 43948, 43939, 43941, 43943, 43945, - 43946, 43947, 43919, 43901, 43902, 43903, 43904, 43905, 43906, 43918, - 43920, 43921, 43922, 43923, 43924, 43925, 43933, 43932, 43934, 43935, - 43936, 43937, 43938, 43949, 43950, 43951, 43952, 43953, 43954, 43955, - 43956, 43957, 43958, 43959, 43960, 43940, 43942, 43944, 43894, 43896, - 43897, 43898, 43899, 43900, 43907, 43908, 43909, 43910, 43911, 43912, - 43913, 43914, 43915, 43916, 43917, 5117, 43926, 43927, 43928, 43929, - 43930, 43931, 43961, 43962, 43963, 43964, 43965, 43966, 43967, 5112, - 5113, 5114, 5115, 5116, 43895, 43888, 43889, 43890, 43891, 43892, 43893, - 127800, 127826, 127792, 127937, 129490, 128696, 9767, 128063, 128020, - 9911, 69559, 69553, 69567, 69571, 69556, 69564, 69570, 69552, 69568, - 69555, 69560, 69562, 69557, 69561, 69563, 69554, 69566, 69572, 69558, - 69569, 69565, 69578, 69574, 69575, 69577, 69573, 69579, 69576, 129378, - 127851, 127876, 9962, 127910, 9680, 9682, 10690, 10683, 10691, 9684, - 9683, 9685, 9677, 9681, 10677, 10682, 10684, 127246, 8859, 11199, 10687, - 129282, 129280, 129281, 128320, 9938, 127342, 127341, 127277, 8856, - 10808, 9316, 9315, 9318, 9317, 9314, 9313, 9450, 9320, 9312, 9319, - 127247, 8857, 8861, 12905, 12919, 12904, 12918, 12903, 12917, 12926, - 12909, 12923, 12906, 12920, 12896, 12910, 12900, 12914, 12897, 12911, - 12908, 12922, 12901, 12915, 12899, 12913, 12902, 12916, 12907, 12921, - 12898, 12912, 9097, 127343, 10162, 12975, 127569, 12959, 127568, 12963, - 12951, 12962, 12965, 12973, 12943, 12957, 12935, 12950, 12939, 12932, - 12955, 12931, 12964, 12946, 12869, 12871, 12952, 12966, 12967, 12969, - 12942, 12954, 12938, 12976, 12936, 12948, 12868, 12974, 12961, 12970, - 12968, 12953, 12934, 12972, 12956, 12944, 12870, 12947, 12949, 12971, - 12945, 12933, 12958, 12930, 12937, 12929, 12941, 12940, 12960, 12928, - 127275, 127276, 128712, 13033, 13036, 13034, 13037, 13035, 13013, 13016, - 13014, 13017, 13015, 13038, 13041, 13039, 13042, 13040, 13028, 13031, - 13029, 13032, 13030, 13046, 13049, 13047, 13050, 13048, 13018, 13021, - 13019, 13022, 13020, 13023, 13026, 13024, 13027, 13025, 13051, 13053, - 13052, 13054, 13043, 13045, 13044, 13008, 13011, 13009, 13012, 13010, - 12925, 12924, 9398, 9399, 9400, 9401, 9402, 9403, 9404, 9405, 9406, 9407, - 9408, 9409, 9410, 9411, 9412, 9413, 9414, 9415, 9416, 9417, 9418, 9419, - 9420, 9421, 9422, 9423, 9424, 9425, 9426, 9427, 9428, 9429, 9430, 9431, - 9432, 9433, 9434, 9435, 9436, 9437, 9438, 9439, 9440, 9441, 9442, 9443, - 9444, 9445, 9446, 9447, 9448, 9449, 10688, 10806, 8854, 12879, 9329, - 9322, 12991, 12876, 9326, 12981, 12875, 12982, 12986, 12985, 12988, - 12987, 12984, 12983, 12990, 12989, 9325, 12878, 9328, 12877, 9327, 9321, - 12872, 12890, 12874, 12891, 12895, 12894, 12978, 12977, 12893, 12892, - 12980, 12979, 9324, 9331, 12873, 12881, 12885, 12884, 12887, 12886, - 12883, 12882, 12889, 12888, 9323, 9330, 10050, 12342, 10679, 10681, 8853, - 8858, 10680, 128981, 9098, 8855, 10686, 10026, 127278, 127245, 8860, - 10678, 10689, 128983, 11198, 94, 10768, 127914, 127961, 127750, 195088, - 195089, 195090, 195091, 195092, 195093, 195094, 195095, 195096, 195097, - 195098, 195099, 195100, 195101, 195072, 195073, 195074, 195075, 195076, - 195077, 195078, 195079, 195080, 195081, 195082, 195083, 195084, 195085, - 195086, 195087, 194560, 194561, 194562, 194563, 194564, 194565, 194566, - 194567, 194568, 194569, 194570, 194571, 194572, 194573, 194574, 194575, - 194576, 194577, 194578, 194579, 194580, 194581, 194582, 194583, 194584, - 194585, 194586, 194587, 194588, 194589, 194590, 194591, 194592, 194593, - 194594, 194595, 194596, 194597, 194598, 194599, 194600, 194601, 194602, - 194603, 194604, 194605, 194606, 194607, 194608, 194609, 194610, 194611, - 194612, 194613, 194614, 194615, 194616, 194617, 194618, 194619, 194620, - 194621, 194622, 194623, 194624, 194625, 194626, 194627, 194628, 194629, - 194630, 194631, 194632, 194633, 194634, 194635, 194636, 194637, 194638, - 194639, 194640, 194641, 194642, 194643, 194644, 194645, 194646, 194647, - 194648, 194649, 194650, 194651, 194652, 194653, 194654, 194655, 194656, - 194657, 194658, 194659, 194660, 194661, 194662, 194663, 194664, 194665, - 194666, 194667, 194668, 194669, 194670, 194671, 194672, 194673, 194674, - 194675, 194676, 194677, 194678, 194679, 194680, 194681, 194682, 194683, - 194684, 194685, 194686, 194687, 194688, 194689, 194690, 194691, 194692, - 194693, 194694, 194695, 194696, 194697, 194698, 194699, 194700, 194701, - 194702, 194703, 194704, 194705, 194706, 194707, 194708, 194709, 194710, - 194711, 194712, 194713, 194714, 194715, 194716, 194717, 194718, 194719, - 194720, 194721, 194722, 194723, 194724, 194725, 194726, 194727, 194728, - 194729, 194730, 194731, 194732, 194733, 194734, 194735, 194736, 194737, - 194738, 194739, 194740, 194741, 194742, 194743, 194744, 194745, 194746, - 194747, 194748, 194749, 194750, 194751, 194752, 194753, 194754, 194755, - 194756, 194757, 194758, 194759, 194760, 194761, 194762, 194763, 194764, - 194765, 194766, 194767, 194768, 194769, 194770, 194771, 194772, 194773, - 194774, 194775, 194776, 194777, 194778, 194779, 194780, 194781, 194782, - 194783, 194784, 194785, 194786, 194787, 194788, 194789, 194790, 194791, - 194792, 194793, 194794, 194795, 194796, 194797, 194798, 194799, 194800, - 194801, 194802, 194803, 194804, 194805, 194806, 194807, 194808, 194809, - 194810, 194811, 194812, 194813, 194814, 194815, 194816, 194817, 194818, - 194819, 194820, 194821, 194822, 194823, 194824, 194825, 194826, 194827, - 194828, 194829, 194830, 194831, 194832, 194833, 194834, 194835, 194836, - 194837, 194838, 194839, 194840, 194841, 194842, 194843, 194844, 194845, - 194846, 194847, 194848, 194849, 194850, 194851, 194852, 194853, 194854, - 194855, 194856, 194857, 194858, 194859, 194860, 194861, 194862, 194863, - 194864, 194865, 194866, 194867, 194868, 194869, 194870, 194871, 194872, - 194873, 194874, 194875, 194876, 194877, 194878, 194879, 194880, 194881, - 194882, 194883, 194884, 194885, 194886, 194887, 194888, 194889, 194890, - 194891, 194892, 194893, 194894, 194895, 194896, 194897, 194898, 194899, - 194900, 194901, 194902, 194903, 194904, 194905, 194906, 194907, 194908, - 194909, 194910, 194911, 194912, 194913, 194914, 194915, 194916, 194917, - 194918, 194919, 194920, 194921, 194922, 194923, 194924, 194925, 194926, - 194927, 194928, 194929, 194930, 194931, 194932, 194933, 194934, 194935, - 194936, 194937, 194938, 194939, 194940, 194941, 194942, 194943, 194944, - 194945, 194946, 194947, 194948, 194949, 194950, 194951, 194952, 194953, - 194954, 194955, 194956, 194957, 194958, 194959, 194960, 194961, 194962, - 194963, 194964, 194965, 194966, 194967, 194968, 194969, 194970, 194971, - 194972, 194973, 194974, 194975, 194976, 194977, 194978, 194979, 194980, - 194981, 194982, 194983, 194984, 194985, 194986, 194987, 194988, 194989, - 194990, 194991, 194992, 194993, 194994, 194995, 194996, 194997, 194998, - 194999, 195000, 195001, 195002, 195003, 195004, 195005, 195006, 195007, - 195008, 195009, 195010, 195011, 195012, 195013, 195014, 195015, 195016, - 195017, 195018, 195019, 195020, 195021, 195022, 195023, 195024, 195025, - 195026, 195027, 195028, 195029, 195030, 195031, 195032, 195033, 195034, - 195035, 195036, 195037, 195038, 195039, 195040, 195041, 195042, 195043, - 195044, 195045, 195046, 195047, 195048, 195049, 195050, 195051, 195052, - 195053, 195054, 195055, 195056, 195057, 195058, 195059, 195060, 195061, - 195062, 195063, 195064, 195065, 195066, 195067, 195068, 195069, 195070, - 195071, 64096, 64097, 64098, 64099, 64100, 64101, 64102, 64103, 64104, - 64105, 64106, 64107, 64108, 64109, 64000, 64001, 64002, 64003, 64004, - 64005, 64006, 64007, 64008, 64009, 64010, 64011, 64012, 64013, 64014, - 64015, 64016, 64017, 64018, 64019, 64020, 64021, 64022, 64023, 64024, - 64025, 64026, 64027, 64028, 64029, 64030, 64031, 64032, 64033, 64034, - 64035, 64036, 64037, 64038, 64039, 64040, 64041, 64042, 64043, 64044, - 64045, 64046, 64047, 64048, 64049, 64050, 64051, 64052, 64053, 64054, - 64055, 64056, 64057, 64058, 64059, 64060, 64061, 64062, 64063, 64064, - 64065, 64066, 64067, 64068, 64069, 64070, 64071, 64072, 64073, 64074, - 64075, 64076, 64077, 64078, 64079, 64080, 64081, 64082, 64083, 64084, - 64085, 64086, 64087, 64088, 64089, 64090, 64091, 64092, 64093, 64094, - 64095, 64112, 64113, 64114, 64115, 64116, 64117, 64118, 64119, 64120, - 64121, 64122, 64123, 64124, 64125, 64126, 64127, 64128, 64129, 64130, - 64131, 64132, 64133, 64134, 64135, 64136, 64137, 64138, 64139, 64140, - 64141, 64142, 64143, 64144, 64145, 64146, 64147, 64148, 64149, 64150, - 64151, 64152, 64153, 64154, 64155, 64156, 64157, 64158, 64159, 64160, - 64161, 64162, 64163, 64164, 64165, 64166, 64167, 64168, 64169, 64170, - 64171, 64172, 64173, 64174, 64175, 64176, 64177, 64178, 64179, 64180, - 64181, 64182, 64183, 64184, 64185, 64186, 64187, 64188, 64189, 64190, - 64191, 64192, 64193, 64194, 64195, 64196, 64197, 64198, 64199, 64200, - 64201, 64202, 64203, 64204, 64205, 64206, 64207, 64208, 64209, 64210, - 64211, 64212, 64213, 64214, 64215, 64216, 64217, 63744, 63745, 63746, - 63747, 63748, 63749, 63750, 63751, 63752, 63753, 63754, 63755, 63756, - 63757, 63758, 63759, 63760, 63761, 63762, 63763, 63764, 63765, 63766, - 63767, 63768, 63769, 63770, 63771, 63772, 63773, 63774, 63775, 63776, - 63777, 63778, 63779, 63780, 63781, 63782, 63783, 63784, 63785, 63786, - 63787, 63788, 63789, 63790, 63791, 63792, 63793, 63794, 63795, 63796, - 63797, 63798, 63799, 63800, 63801, 63802, 63803, 63804, 63805, 63806, - 63807, 63808, 63809, 63810, 63811, 63812, 63813, 63814, 63815, 63816, - 63817, 63818, 63819, 63820, 63821, 63822, 63823, 63824, 63825, 63826, - 63827, 63828, 63829, 63830, 63831, 63832, 63833, 63834, 63835, 63836, - 63837, 63838, 63839, 63840, 63841, 63842, 63843, 63844, 63845, 63846, - 63847, 63848, 63849, 63850, 63851, 63852, 63853, 63854, 63855, 63856, - 63857, 63858, 63859, 63860, 63861, 63862, 63863, 63864, 63865, 63866, - 63867, 63868, 63869, 63870, 63871, 63872, 63873, 63874, 63875, 63876, - 63877, 63878, 63879, 63880, 63881, 63882, 63883, 63884, 63885, 63886, - 63887, 63888, 63889, 63890, 63891, 63892, 63893, 63894, 63895, 63896, - 63897, 63898, 63899, 63900, 63901, 63902, 63903, 63904, 63905, 63906, - 63907, 63908, 63909, 63910, 63911, 63912, 63913, 63914, 63915, 63916, - 63917, 63918, 63919, 63920, 63921, 63922, 63923, 63924, 63925, 63926, - 63927, 63928, 63929, 63930, 63931, 63932, 63933, 63934, 63935, 63936, - 63937, 63938, 63939, 63940, 63941, 63942, 63943, 63944, 63945, 63946, - 63947, 63948, 63949, 63950, 63951, 63952, 63953, 63954, 63955, 63956, - 63957, 63958, 63959, 63960, 63961, 63962, 63963, 63964, 63965, 63966, - 63967, 63968, 63969, 63970, 63971, 63972, 63973, 63974, 63975, 63976, - 63977, 63978, 63979, 63980, 63981, 63982, 63983, 63984, 63985, 63986, - 63987, 63988, 63989, 63990, 63991, 63992, 63993, 63994, 63995, 63996, - 63997, 63998, 63999, 11946, 12003, 11910, 11963, 11962, 11950, 11992, - 12012, 12000, 12005, 11996, 12010, 11984, 11988, 11994, 11987, 11952, - 12007, 11977, 11976, 11973, 12019, 11993, 12014, 11995, 12016, 12006, - 12002, 11979, 11936, 11983, 11905, 11970, 11943, 11931, 11914, 11934, - 11944, 11999, 11998, 11997, 11960, 11947, 11939, 11978, 11968, 11967, - 11966, 12004, 11927, 11926, 12001, 11975, 11928, 12018, 12013, 12015, - 12011, 11945, 11986, 11985, 11921, 11920, 11919, 11918, 11964, 11957, - 11990, 11989, 11935, 11965, 11933, 11941, 11940, 11909, 11991, 11959, - 11929, 11904, 11908, 11907, 11906, 11915, 11942, 11974, 11980, 12008, - 12009, 11951, 11925, 11924, 11922, 11949, 11948, 11917, 11916, 11958, - 11932, 12017, 11911, 11923, 11969, 11982, 11981, 11938, 11937, 11972, - 11971, 11956, 11955, 11954, 11953, 11913, 11912, 11961, 12752, 12743, - 12748, 12768, 12772, 12757, 12741, 12750, 12769, 12747, 12749, 12744, - 12742, 12746, 12758, 12754, 12763, 12770, 12764, 12753, 12740, 12767, - 12760, 12759, 12745, 12773, 12766, 12762, 12755, 12761, 12736, 12765, - 12739, 12737, 12738, 12756, 12751, 12771, 128079, 127916, 127963, 128385, - 129346, 127867, 128203, 128346, 128358, 128343, 128355, 128340, 128352, - 128339, 128351, 128344, 128356, 128336, 128348, 128342, 128354, 128341, - 128353, 128345, 128357, 128347, 128359, 128337, 128349, 128338, 128350, - 10561, 8754, 128259, 10227, 128472, 128257, 128258, 8631, 11118, 8635, - 8753, 10959, 10961, 10960, 10962, 127746, 10828, 10832, 128234, 128235, - 128272, 128213, 10829, 8272, 9729, 127786, 127785, 127784, 127783, - 129313, 9114, 129715, 127864, 129381, 58, 8788, 8353, 128165, 7625, 7623, - 769, 791, 833, 8410, 8404, 8423, 857, 8432, 7677, 844, 774, 7627, 814, - 810, 838, 70459, 780, 812, 784, 8409, 8405, 787, 789, 806, 65062, 65069, - 42609, 1160, 11774, 11744, 11768, 11747, 11757, 11765, 42654, 11751, - 11752, 11753, 11756, 42613, 11775, 11772, 42655, 11767, 11754, 42619, - 11763, 11762, 42618, 42615, 42612, 42617, 11770, 42614, 11771, 11773, - 11769, 11759, 42616, 11760, 11758, 11748, 11749, 11761, 11746, 11764, - 11755, 11745, 11750, 11766, 42621, 1156, 1158, 1159, 1157, 42608, 42610, - 1155, 65070, 65071, 1161, 42620, 123023, 42607, 770, 813, 807, 43248, - 43244, 43245, 43246, 43247, 43242, 43243, 43249, 43237, 43236, 43239, - 43238, 43235, 43234, 43232, 43241, 43233, 43240, 7675, 776, 6833, 804, - 775, 7672, 856, 803, 7674, 7617, 7616, 861, 860, 865, 7676, 862, 863, - 6840, 831, 6858, 6857, 6844, 866, 858, 864, 65058, 65059, 8422, 840, 782, - 779, 783, 819, 7629, 6832, 798, 6835, 8413, 8416, 8418, 8414, 8419, 8420, - 8415, 839, 850, 8412, 122892, 122884, 122891, 122890, 122921, 122919, - 122889, 122916, 122900, 122907, 122910, 122901, 122908, 122880, 122920, - 122881, 122909, 122903, 122922, 122883, 122895, 122894, 122896, 122898, - 122899, 122882, 122885, 122912, 122911, 122913, 122918, 122915, 122888, - 122886, 122904, 122902, 122897, 122893, 70508, 70507, 70506, 70505, - 70504, 70502, 70503, 70515, 70513, 70514, 70516, 70512, 768, 790, 832, - 7624, 7621, 847, 119363, 119362, 119364, 835, 834, 837, 836, 777, 843, - 795, 785, 815, 826, 811, 6855, 6834, 7632, 12442, 12441, 7671, 7670, + 64975, 65008, 65017, 64464, 64838, 64972, 64844, 65018, 64860, 64686, + 64541, 64821, 64817, 64744, 64862, 64861, 64685, 64540, 64820, 64936, + 64966, 64687, 64542, 64822, 64864, 64863, 64865, 64867, 64866, 64688, + 64543, 64743, 64791, 64763, 64810, 64782, 64792, 64764, 64606, 64609, + 64755, 64607, 64610, 64756, 64611, 64608, 64754, 64818, 64746, 64872, + 64871, 64938, 64806, 64814, 64778, 64824, 64873, 64805, 64813, 64777, + 64823, 64875, 64874, 64877, 64876, 64808, 64816, 64780, 64745, 64793, + 64765, 64807, 64815, 64779, 64825, 64809, 64781, 64794, 64766, 65022, + 64846, 64882, 64881, 64883, 64884, 64819, 64551, 64826, 64785, 64757, + 64696, 64550, 64786, 64758, 64851, 64850, 64849, 64674, 64524, 64677, + 64740, 64930, 64852, 64929, 64675, 64525, 64928, 64848, 64927, 64673, + 64523, 64932, 64853, 64855, 64854, 64931, 64626, 64676, 64526, 64739, + 64628, 64527, 64627, 64625, 64624, 64629, 64528, 64603, 64529, 64634, + 64531, 64632, 64678, 64530, 64741, 64633, 64631, 64630, 64635, 64532, + 64742, 65016, 64661, 64601, 64616, 64515, 64491, 64490, 64493, 64492, + 64503, 64504, 64502, 64667, 64736, 64664, 64513, 64663, 64512, 64665, + 64614, 64666, 64514, 64735, 64615, 64499, 64498, 64495, 64494, 64617, + 64516, 64501, 64500, 64613, 64612, 64497, 64496, 64942, 64731, 64598, + 64734, 64753, 64660, 64658, 64943, 64730, 64597, 64732, 64599, 64925, + 64924, 64944, 64659, 64733, 64600, 64752, 64657, 64662, 64602, 64506, + 64507, 64505, 64697, 64552, 64827, 2204, 1619, 1624, 2303, 126492, + 126494, 126493, 126495, 126644, 126638, 126641, 126647, 126648, 126646, + 126632, 126645, 126630, 126650, 126626, 126636, 126625, 126640, 126643, + 126633, 126631, 126651, 126637, 126635, 126649, 126627, 126642, 126639, + 126629, 126489, 126467, 126518, 126517, 126516, 126510, 126513, 126503, + 126500, 126519, 126506, 126498, 126508, 126497, 126512, 126505, 126523, + 126509, 126507, 126514, 126511, 126521, 126612, 126606, 126609, 126615, + 126592, 126607, 126599, 126596, 126616, 126614, 126600, 126613, 126598, + 126618, 126594, 126604, 126593, 126608, 126611, 126601, 126619, 126605, + 126603, 126617, 126595, 126610, 126597, 126475, 126704, 126705, 126588, + 126590, 126585, 126582, 126568, 126581, 126580, 126574, 126577, 126567, + 126564, 126583, 126570, 126562, 126572, 126561, 126576, 126569, 126586, + 126587, 126573, 126578, 126575, 126484, 126478, 126481, 126557, 126559, + 126553, 126548, 126542, 126545, 126551, 126530, 126537, 126535, 126555, + 126541, 126539, 126546, 126543, 126472, 126488, 126486, 126485, 126464, + 126479, 126487, 126474, 126470, 126490, 126466, 126476, 126465, 126480, + 126483, 126473, 126471, 126491, 126477, 126482, 126469, 1541, 1536, 2290, + 2289, 2288, 1642, 2199, 1550, 2192, 1769, 2193, 2184, 1544, 1629, 2296, + 2301, 2298, 1772, 1623, 983633, 983635, 983640, 983634, 983638, 983636, + 983639, 983637, 983641, 1563, 1617, 65148, 65149, 1554, 1555, 1552, 1537, + 1539, 1540, 1790, 1789, 1553, 1551, 1556, 2249, 1560, 1761, 2250, 2272, + 1558, 983202, 1751, 1750, 1753, 1752, 1762, 1764, 2273, 1756, 2261, 1755, + 1557, 2200, 2260, 2266, 2268, 2267, 2269, 2252, 2271, 2270, 2291, 1767, + 2251, 1768, 2264, 1760, 1759, 1559, 2253, 1754, 2263, 2262, 1766, 69317, + 69371, 2265, 2202, 2201, 69375, 2203, 69373, 69374, 2259, 1773, 1763, + 1562, 1561, 1765, 1622, 1618, 65150, 65151, 2256, 2205, 64444, 64435, + 64434, 64441, 64440, 64439, 64438, 64446, 64445, 64437, 64436, 64449, + 64448, 64443, 64442, 64450, 64447, 1758, 1600, 2179, 2180, 65137, 2181, + 65139, 2285, 2282, 2286, 2283, 2287, 2284, 1566, 2275, 1644, 1627, 1626, + 1628, 2190, 1631, 1567, 1625, 1545, 1546, 1542, 1543, 1637, 1636, 1639, + 1638, 1635, 1634, 1632, 1641, 1633, 1640, 1375, 1370, 1359, 1337, 1349, + 1362, 1347, 1353, 1342, 1361, 1360, 1356, 1333, 1335, 1336, 1346, 1331, + 1355, 1345, 1364, 1343, 1363, 1354, 1351, 1357, 1329, 1358, 1352, 1340, + 1366, 1341, 1339, 1330, 1348, 1350, 1338, 1334, 1344, 1332, 1365, 1373, + 1372, 1371, 1395, 1401, 1390, 1409, 1408, 1404, 1381, 1383, 1384, 1394, + 1379, 1403, 1393, 1412, 1391, 1411, 1402, 1399, 1405, 1376, 1407, 1385, + 1377, 1406, 1400, 1397, 1416, 1410, 1388, 1414, 1389, 1387, 1378, 1396, + 1398, 1386, 1382, 1392, 1380, 1413, 1415, 64279, 64277, 64275, 64276, + 64278, 1369, 1418, 1423, 1417, 1374, 129201, 10549, 10548, 129200, 10550, + 10551, 129968, 128667, 127912, 9800, 8978, 9738, 65948, 42, 8727, 8258, + 128562, 118477, 9954, 11225, 128888, 8771, 8870, 128095, 9883, 128663, + 127975, 128762, 8371, 127814, 68352, 68353, 68357, 68355, 68358, 68359, + 68356, 68354, 68373, 68374, 68372, 68393, 68405, 68388, 68387, 68386, + 68391, 68390, 68389, 68371, 68370, 68369, 68403, 68401, 68404, 68399, + 68394, 68395, 68378, 68381, 68377, 68366, 68367, 68362, 68363, 68364, + 68365, 68385, 68384, 68380, 68379, 68402, 68400, 68360, 68361, 68375, + 68383, 68376, 68368, 68398, 68392, 68382, 68397, 68396, 68409, 129361, + 8525, 1547, 129518, 9810, 129683, 128118, 128036, 127868, 128124, 128700, + 128281, 128386, 11101, 11099, 983056, 10155, 128043, 129363, 127992, + 129441, 129366, 129391, 128708, 7007, 7005, 7006, 6991, 6990, 6917, 6918, + 6987, 6988, 6928, 6953, 6954, 6936, 6937, 6948, 6944, 6943, 6949, 6984, + 6927, 6933, 6934, 6919, 6920, 6929, 6930, 6921, 6922, 6938, 6939, 6931, + 6981, 6932, 6982, 6958, 6925, 6926, 6950, 6945, 6935, 6940, 6951, 6952, + 6957, 6923, 6924, 6962, 6960, 6961, 6946, 6942, 6941, 6947, 6983, 6985, + 6986, 6963, 6955, 6959, 6956, 7022, 7025, 7021, 7024, 7023, 7026, 7020, + 7019, 7027, 7012, 7013, 7018, 7015, 7017, 7016, 7010, 7014, 7009, 7011, + 7032, 7036, 7033, 7034, 7035, 7031, 7030, 7029, 7028, 7003, 7038, 7008, + 7002, 7037, 7039, 6915, 6913, 6912, 6914, 6916, 6964, 6972, 6973, 6970, + 6971, 6968, 6969, 6974, 6975, 6977, 6976, 6965, 6978, 6979, 6966, 6967, + 6980, 7004, 6997, 6996, 6999, 6998, 6995, 6994, 6992, 7001, 6993, 7000, + 127880, 10057, 9744, 128503, 128505, 128499, 128501, 11197, 9745, 9746, + 128502, 128500, 10007, 129526, 129648, 42737, 42736, 42741, 42740, 42733, + 42699, 42713, 42712, 42692, 42706, 42683, 42719, 42734, 42735, 42682, + 42729, 42657, 42725, 42659, 42670, 42718, 42666, 42716, 42701, 42675, + 42671, 42722, 42720, 42727, 42723, 42702, 42726, 42677, 42707, 42708, + 42709, 42686, 42694, 42674, 42731, 42695, 42685, 42684, 42691, 42673, + 42664, 42715, 42703, 92193, 92188, 92161, 92199, 92240, 92179, 92211, + 92219, 92213, 92243, 92207, 92216, 92183, 92184, 92238, 92171, 983268, + 92174, 92203, 92186, 92210, 92230, 92175, 92221, 92232, 92246, 92198, + 92214, 92190, 92176, 92197, 92196, 92164, 92245, 92212, 92201, 92160, + 92182, 92173, 92229, 92227, 92180, 92237, 92241, 92223, 92185, 92194, + 92178, 92239, 92225, 92233, 92167, 92191, 92231, 92244, 92204, 92226, + 92236, 92200, 92189, 92187, 92162, 92163, 92169, 92170, 92202, 92205, + 92222, 92168, 92165, 92215, 92224, 92208, 92220, 92235, 92177, 92181, + 92195, 92234, 92209, 92166, 92172, 92206, 92192, 92228, 92217, 92242, + 92218, 92262, 92271, 92272, 92270, 92294, 92253, 92301, 92256, 92273, + 92259, 92251, 92297, 92300, 92296, 92295, 92255, 92252, 92292, 92286, + 92268, 92290, 92281, 92267, 92284, 92287, 92282, 92283, 92277, 92298, + 983270, 92276, 92302, 92247, 92299, 92288, 92269, 92260, 92261, 983269, + 92289, 92257, 92274, 92263, 92279, 92265, 92266, 92250, 92249, 92285, + 92248, 92264, 92280, 92258, 92254, 92278, 92291, 92293, 92275, 92310, + 92320, 92312, 92394, 92393, 92319, 92384, 92321, 92386, 92361, 92373, + 92366, 92328, 92329, 92357, 92342, 92397, 92365, 92376, 92339, 92382, + 92363, 92318, 92387, 92383, 92315, 92385, 92354, 92311, 92381, 92343, + 92326, 92364, 92324, 92391, 92344, 92390, 92338, 92396, 92308, 92349, + 92375, 92378, 92317, 92331, 92362, 92336, 92341, 92370, 92347, 92307, + 92303, 92309, 92395, 92368, 92356, 92367, 92360, 92389, 92333, 92359, + 92374, 92351, 92325, 92371, 92350, 92345, 92372, 92314, 92340, 92323, + 92304, 92313, 92316, 92398, 92399, 92380, 92330, 983271, 92379, 92392, + 92335, 92332, 92346, 92400, 92358, 92334, 92353, 92337, 92306, 92369, + 92388, 92322, 92305, 92327, 92377, 92352, 92348, 92355, 92436, 92517, + 92488, 92434, 92415, 92431, 92454, 92460, 92420, 92489, 92422, 92474, + 92479, 92449, 92502, 92439, 92484, 92495, 92464, 92511, 92406, 92446, + 92497, 92426, 92482, 92448, 92515, 92401, 92478, 92427, 92496, 92458, + 92424, 92445, 92404, 92466, 92444, 92438, 92442, 92441, 92510, 92499, + 92425, 92437, 92440, 92471, 92465, 92409, 92483, 92475, 92467, 92485, + 92457, 92432, 92476, 92435, 92412, 92414, 92430, 92407, 92403, 92405, + 92487, 92418, 92486, 92408, 92447, 92459, 92480, 92472, 92505, 92514, + 92450, 92463, 92410, 92493, 92507, 92503, 92462, 92506, 92443, 92509, + 92469, 92461, 92490, 92512, 92494, 92455, 92417, 92501, 92413, 92508, + 92500, 92504, 92473, 92419, 92498, 92423, 92516, 92428, 92452, 92451, + 92481, 92416, 92456, 92433, 92477, 92492, 92491, 92513, 92411, 92402, + 92421, 92453, 92468, 92470, 92429, 92661, 92626, 92616, 92596, 92612, + 92603, 92673, 92651, 92627, 92597, 92585, 92578, 92615, 92565, 92552, + 92602, 92674, 92570, 92646, 92599, 92532, 92587, 92538, 92620, 92670, + 92665, 92575, 92521, 92633, 92595, 92523, 92556, 92539, 92664, 92653, + 92667, 92600, 92542, 92555, 92668, 92520, 92582, 92591, 92581, 92551, + 92654, 92611, 92614, 92666, 92671, 92617, 92637, 92562, 92518, 92592, + 92558, 92528, 92607, 92535, 92557, 92563, 92550, 92624, 983272, 92640, + 92657, 92531, 92601, 92553, 92543, 92619, 92598, 92541, 92544, 92579, + 92658, 92554, 92628, 92648, 92547, 92527, 92545, 92540, 92604, 92622, + 92589, 92608, 92583, 92609, 92662, 92613, 92584, 92625, 92634, 92524, + 92536, 92605, 92647, 92548, 92663, 92593, 92621, 92568, 92610, 92576, + 92529, 92618, 92649, 92561, 92656, 92526, 92655, 92546, 92534, 92560, + 92580, 92659, 92660, 92638, 92571, 92525, 92549, 92559, 92635, 92577, + 92530, 92636, 92669, 92572, 92672, 92537, 92519, 92630, 92586, 92569, + 92566, 92573, 92652, 92522, 92574, 92650, 92533, 92567, 92623, 92606, + 92639, 92564, 92590, 92642, 92643, 92594, 92641, 92645, 92644, 92588, + 92629, 92632, 92631, 92710, 92695, 92694, 92726, 92675, 92719, 92677, + 92718, 92682, 92717, 92689, 92720, 92724, 92685, 92722, 92723, 92711, + 92712, 92698, 92688, 92697, 92696, 92702, 92687, 92704, 92681, 92708, + 92703, 92706, 92714, 92709, 92679, 92721, 92684, 92683, 92707, 92691, + 92713, 92700, 92693, 92727, 92690, 92692, 92686, 92680, 92725, 92699, + 92701, 92705, 92716, 92728, 92678, 92715, 92676, 42693, 42698, 42711, + 42696, 42667, 42717, 42704, 42661, 42721, 42669, 42668, 42705, 42700, + 42680, 42678, 42710, 42688, 42681, 42732, 42676, 42679, 42730, 42728, + 42672, 42662, 42724, 42687, 42689, 42690, 42697, 42714, 42660, 42656, + 42665, 42663, 42658, 42738, 42742, 42739, 42743, 127974, 128183, 128180, + 128182, 128181, 127820, 129685, 128202, 129532, 128136, 129530, 127936, + 92916, 92912, 92915, 92913, 92914, 92887, 92894, 92908, 92880, 92907, + 92893, 92886, 92888, 92881, 92906, 92896, 92891, 92902, 92900, 92885, + 92890, 92884, 92904, 92905, 92899, 92889, 92897, 92892, 92895, 92882, + 92898, 92883, 92901, 92903, 92909, 92917, 9918, 129415, 7152, 7153, 7124, + 7108, 7114, 7130, 7139, 7127, 7138, 7133, 7136, 7113, 7111, 7117, 7119, + 7107, 7135, 7125, 7112, 7123, 7129, 7116, 7132, 7105, 7126, 7128, 7110, + 7109, 7137, 7121, 7118, 7106, 7120, 7134, 7122, 7115, 7131, 7104, 7140, + 7141, 7154, 7155, 7167, 7165, 7166, 7164, 7142, 7150, 7151, 7144, 7147, + 7149, 7143, 7145, 7146, 7148, 128704, 128705, 128267, 127900, 127901, + 9835, 9836, 129492, 128059, 127958, 128147, 129451, 129752, 127866, + 129714, 983055, 128276, 129745, 128277, 9086, 128718, 118478, 2557, 2432, + 2519, 2548, 2552, 2551, 2550, 2549, 2553, 2454, 2510, 983661, 2453, 2480, + 2545, 2544, 2525, 2524, 2556, 2443, 2528, 2444, 2529, 2527, 2479, 2437, + 2438, 2448, 2452, 2466, 2465, 2471, 2470, 2464, 2463, 2469, 2468, 2441, + 2442, 2439, 2440, 2457, 2467, 2462, 2472, 2486, 2487, 2488, 2477, 2476, + 2459, 2458, 2456, 2455, 2461, 2460, 2475, 2474, 2489, 2482, 2478, 2447, + 2451, 2547, 2546, 983651, 983650, 983652, 2558, 2433, 2492, 2493, 2434, + 2509, 2435, 2554, 2494, 2504, 2508, 2497, 2498, 2499, 2500, 2530, 2531, + 2495, 2496, 2503, 2507, 2539, 2538, 2541, 2540, 2537, 2536, 2534, 2543, + 2535, 2542, 2555, 11102, 127857, 9004, 9187, 93856, 93880, 93858, 93864, + 93873, 93874, 93859, 93861, 93869, 93868, 93870, 93876, 93875, 93867, + 93862, 93865, 93877, 93857, 93879, 93860, 93866, 93871, 93872, 93878, + 93863, 93883, 93907, 93885, 93891, 93900, 93901, 93886, 93888, 93896, + 93895, 93897, 93903, 93902, 93894, 93889, 93892, 93904, 93884, 93906, + 93887, 93893, 93898, 93899, 93905, 93890, 8812, 8502, 8757, 129475, + 128719, 72710, 72711, 72712, 72746, 72704, 72705, 72715, 72717, 72731, + 72730, 72736, 72735, 72729, 72728, 72734, 72733, 72708, 72709, 72706, + 72707, 72722, 72732, 72727, 72737, 72747, 72748, 72749, 72741, 72740, + 72724, 72723, 72721, 72720, 72726, 72725, 72719, 72718, 72739, 72738, + 72750, 72745, 72742, 72744, 72743, 72714, 72716, 72801, 72810, 72806, + 72797, 72807, 72798, 72802, 72811, 72800, 72809, 72799, 72808, 72796, + 72805, 72804, 72795, 72803, 72794, 72764, 72768, 72765, 72767, 72766, + 72756, 72757, 72758, 72751, 72761, 72763, 72754, 72755, 72752, 72753, + 72760, 72762, 72770, 72769, 72789, 72788, 72791, 72790, 72787, 72786, + 72784, 72793, 72785, 72792, 72771, 72772, 72773, 72812, 128692, 128690, + 10745, 10744, 129506, 127921, 127874, 128038, 8383, 129766, 9763, 128089, + 129452, 9679, 9864, 10733, 9865, 9210, 11176, 11177, 11178, 11179, 11180, + 11182, 11181, 11183, 9960, 10028, 129623, 9821, 129554, 129596, 129609, + 129612, 129622, 9818, 129551, 129593, 9822, 129543, 129564, 129585, + 129597, 129606, 129555, 129619, 129617, 129618, 9823, 129556, 129598, + 9819, 129552, 129594, 9820, 129553, 129595, 129575, 129572, 129576, + 129577, 129573, 129574, 9827, 9670, 11201, 10070, 10730, 11230, 9830, + 128419, 128899, 9196, 9662, 9660, 11167, 127778, 9922, 9923, 10047, 9873, + 10022, 128447, 128420, 9829, 11042, 128426, 11052, 10711, 11044, 117867, + 117870, 117869, 117868, 11035, 9194, 9198, 128896, 9668, 9666, 9664, + 11164, 8268, 9944, 128412, 9754, 9699, 9698, 10731, 9207, 11206, 11045, + 9204, 11207, 11047, 9205, 11208, 9206, 11205, 128921, 128927, 9726, 9724, + 9912, 117871, 10002, 128392, 9648, 11039, 127986, 118451, 128413, 9755, + 9193, 9197, 9654, 9199, 128898, 11091, 9658, 9656, 10145, 10148, 11166, + 8269, 127990, 9644, 11049, 11050, 11089, 9642, 129997, 9787, 9632, 11200, + 9209, 128306, 9728, 128369, 9927, 9984, 128900, 128909, 9986, 9751, 9824, + 9733, 128919, 128925, 128908, 9951, 128383, 9742, 9942, 128418, 128897, + 9195, 9652, 9650, 9700, 9701, 11165, 9851, 9646, 11054, 128920, 128926, + 11037, 11204, 10707, 10067, 8493, 8460, 8465, 8476, 8488, 10164, 10166, + 10165, 9250, 118018, 118039, 118128, 118187, 118218, 118159, 118054, + 118112, 118234, 118084, 118202, 118143, 118031, 118062, 118120, 118240, + 118179, 118090, 118210, 118151, 118047, 118104, 118226, 118165, 118076, + 118194, 118135, 118021, 118035, 118066, 118124, 118243, 118183, 118093, + 118214, 118155, 118050, 118108, 118230, 118168, 118080, 118198, 118139, + 118028, 118058, 118116, 118237, 118175, 118087, 118206, 118147, 118043, + 118100, 118222, 118162, 118072, 118190, 118131, 118029, 118060, 118118, + 118177, 118208, 118149, 118045, 118102, 118224, 118023, 118037, 118068, + 118126, 118244, 118185, 118095, 118216, 118157, 118052, 118110, 118232, + 118170, 118082, 118200, 118141, 118074, 118192, 118133, 118020, 118033, + 118064, 118122, 118242, 118181, 118092, 118212, 118153, 118048, 118106, + 118228, 118167, 118078, 118196, 118137, 118026, 118056, 118114, 118235, + 118173, 118085, 118204, 118145, 118041, 118098, 118220, 118160, 118070, + 118188, 118129, 118034, 118065, 118123, 118182, 118213, 118154, 118049, + 118107, 118229, 118079, 118197, 118138, 118017, 118024, 118038, 118069, + 118127, 118245, 118186, 118096, 118217, 118158, 118053, 118111, 118233, + 118171, 118083, 118201, 118142, 118030, 118061, 118119, 118239, 118178, + 118089, 118209, 118150, 118046, 118103, 118225, 118164, 118075, 118193, + 118134, 118027, 118057, 118115, 118236, 118174, 118086, 118205, 118146, + 118042, 118099, 118221, 118161, 118071, 118189, 118130, 118025, 118055, + 118113, 118172, 118203, 118144, 118040, 118097, 118219, 118016, 118022, + 118036, 118067, 118125, 118184, 118094, 118215, 118156, 118051, 118109, + 118231, 118169, 118081, 118199, 118140, 118059, 118117, 118238, 118176, + 118088, 118207, 118148, 118044, 118101, 118223, 118163, 118073, 118191, + 118132, 118019, 118105, 118227, 118166, 118032, 118063, 118121, 118241, + 118180, 118091, 118211, 118152, 118077, 118195, 118136, 129792, 129794, + 129798, 129806, 129821, 129836, 129813, 129844, 129829, 129802, 129817, + 129848, 129832, 129810, 129840, 129825, 129796, 129804, 129819, 129850, + 129834, 129842, 129827, 129800, 129815, 129846, 129831, 129808, 129838, + 129823, 129793, 129801, 129816, 129847, 129797, 129805, 129820, 129851, + 129835, 129812, 129843, 129828, 129809, 129839, 129824, 129795, 129803, + 129818, 129849, 129833, 129811, 129841, 129826, 129799, 129814, 129845, + 129830, 129807, 129837, 129822, 127804, 128033, 128216, 128153, 129744, + 128939, 128951, 128957, 128945, 128902, 128912, 128932, 128366, 128278, + 128209, 128218, 129667, 12731, 12727, 12726, 12724, 12725, 12570, 12574, + 12718, 12576, 12719, 12578, 12580, 12713, 12735, 12720, 12584, 12715, + 12572, 12579, 12581, 12709, 12708, 12573, 12575, 12582, 12557, 12728, + 12588, 12707, 12732, 12583, 12714, 12723, 12589, 12716, 12712, 12585, + 12555, 12587, 12717, 12591, 12571, 12722, 12711, 12590, 12734, 12721, + 12710, 12577, 12567, 12563, 12705, 12730, 12558, 12733, 12568, 12564, + 12556, 12729, 12569, 12565, 12549, 12704, 12560, 12706, 12553, 12552, + 12559, 12551, 12550, 12561, 12566, 12554, 12586, 12562, 118257, 118258, + 118259, 118260, 117854, 11867, 117853, 118253, 118255, 11868, 117855, + 118249, 118251, 118247, 11211, 8993, 130029, 8990, 8973, 11812, 8991, + 8972, 11813, 130030, 9141, 9142, 10555, 9183, 9181, 130026, 130018, 9185, + 127870, 128144, 127893, 128335, 129379, 127923, 8904, 10705, 10706, + 127993, 118282, 117792, 117791, 118281, 9574, 9559, 9556, 9577, 9565, + 9562, 9553, 9580, 9571, 9568, 9552, 9511, 9490, 9503, 9486, 9537, 9520, + 9513, 9489, 9505, 9485, 9543, 9519, 9558, 9555, 9573, 9557, 9554, 9572, + 9592, 9598, 9593, 9599, 9499, 9531, 9495, 9475, 9547, 9515, 9507, 9595, + 9523, 9491, 9487, 9551, 9549, 9483, 9481, 9479, 9477, 9473, 9594, 9541, + 9525, 9517, 9533, 9530, 9522, 9546, 9539, 9582, 9581, 9583, 9584, 130010, + 130014, 129954, 129958, 130003, 129959, 129964, 129965, 129955, 130000, + 129952, 129956, 129963, 129960, 129953, 129961, 129957, 129962, 130007, + 130005, 130004, 130012, 9586, 130008, 130011, 130002, 130015, 130006, + 9585, 130009, 130001, 130013, 9587, 129966, 9591, 9516, 9488, 9484, 9550, + 9548, 9472, 117787, 117788, 129967, 9588, 9596, 9482, 9480, 9478, 9476, + 117789, 9589, 9597, 9524, 9496, 9492, 9474, 118297, 118295, 118296, + 118294, 9532, 9508, 9500, 117790, 9590, 9542, 9526, 9518, 9534, 9529, + 9521, 9545, 9540, 9536, 9510, 9498, 9502, 9494, 9528, 9544, 9514, 9497, + 9506, 9493, 9527, 9564, 9561, 9576, 9563, 9560, 9575, 9570, 9567, 9579, + 9538, 9512, 9504, 9535, 9509, 9501, 9569, 9566, 9578, 129354, 983263, + 128163, 128102, 128713, 128023, 129460, 69649, 69685, 69749, 69745, + 69746, 69687, 69686, 69637, 69638, 69648, 69650, 69664, 69663, 69669, + 69668, 69662, 69661, 69667, 69666, 69643, 69644, 69645, 69646, 69679, + 69641, 69642, 69639, 69640, 69684, 69678, 69655, 69665, 69660, 69670, + 69680, 69681, 69682, 69674, 69673, 69657, 69656, 69654, 69653, 69659, + 69658, 69652, 69651, 69672, 69671, 69683, 69675, 69677, 69676, 69647, + 69721, 69730, 69726, 69717, 69727, 69718, 69722, 69731, 69720, 69729, + 69719, 69728, 69716, 69725, 69724, 69715, 69723, 69714, 69732, 69733, + 69759, 69709, 69707, 69706, 69705, 69708, 69632, 69744, 69635, 69634, + 69636, 69633, 69700, 69747, 69748, 69689, 69688, 69699, 69701, 69692, + 69693, 69694, 69695, 69696, 69697, 69690, 69691, 69698, 69702, 69704, + 69703, 69739, 69738, 69741, 69740, 69737, 69736, 69734, 69743, 69735, + 69742, 10241, 10243, 10247, 10255, 10271, 10303, 10367, 10495, 10431, + 10335, 10463, 10399, 10287, 10351, 10479, 10415, 10319, 10447, 10383, + 10263, 10295, 10359, 10487, 10423, 10327, 10455, 10391, 10279, 10343, + 10471, 10407, 10311, 10439, 10375, 10251, 10267, 10299, 10363, 10491, + 10427, 10331, 10459, 10395, 10283, 10347, 10475, 10411, 10315, 10443, + 10379, 10259, 10291, 10355, 10483, 10419, 10323, 10451, 10387, 10275, + 10339, 10467, 10403, 10307, 10435, 10371, 10245, 10253, 10269, 10301, + 10365, 10493, 10429, 10333, 10461, 10397, 10285, 10349, 10477, 10413, + 10317, 10445, 10381, 10261, 10293, 10357, 10485, 10421, 10325, 10453, + 10389, 10277, 10341, 10469, 10405, 10309, 10437, 10373, 10249, 10265, + 10297, 10361, 10489, 10425, 10329, 10457, 10393, 10281, 10345, 10473, + 10409, 10313, 10441, 10377, 10257, 10289, 10353, 10481, 10417, 10321, + 10449, 10385, 10273, 10337, 10465, 10401, 10305, 10433, 10369, 10242, + 10246, 10254, 10270, 10302, 10366, 10494, 10430, 10334, 10462, 10398, + 10286, 10350, 10478, 10414, 10318, 10446, 10382, 10262, 10294, 10358, + 10486, 10422, 10326, 10454, 10390, 10278, 10342, 10470, 10406, 10310, + 10438, 10374, 10250, 10266, 10298, 10362, 10490, 10426, 10330, 10458, + 10394, 10282, 10346, 10474, 10410, 10314, 10442, 10378, 10258, 10290, + 10354, 10482, 10418, 10322, 10450, 10386, 10274, 10338, 10466, 10402, + 10306, 10434, 10370, 10244, 10252, 10268, 10300, 10364, 10492, 10428, + 10332, 10460, 10396, 10284, 10348, 10476, 10412, 10316, 10444, 10380, + 10260, 10292, 10356, 10484, 10420, 10324, 10452, 10388, 10276, 10340, + 10468, 10404, 10308, 10436, 10372, 10248, 10264, 10296, 10360, 10488, + 10424, 10328, 10456, 10392, 10280, 10344, 10472, 10408, 10312, 10440, + 10376, 10256, 10288, 10352, 10480, 10416, 10320, 10448, 10384, 10272, + 10336, 10464, 10400, 10304, 10432, 10368, 10240, 129504, 983125, 129329, + 127838, 728, 127753, 128112, 128188, 129650, 129521, 9099, 166, 128148, + 129382, 129294, 129529, 129483, 129767, 128027, 6663, 6662, 6659, 6658, + 6671, 6670, 6667, 6666, 6661, 6668, 6665, 6657, 6678, 6669, 6656, 6674, + 6660, 6673, 6676, 6664, 6675, 6672, 6677, 6683, 6681, 6679, 6682, 6680, + 6687, 6686, 5957, 5960, 5962, 5959, 5956, 5969, 5955, 5966, 5963, 5961, + 5965, 5968, 5958, 5967, 5964, 5952, 5953, 5954, 5970, 5971, 8226, 8729, + 128363, 128364, 9678, 128652, 128101, 128100, 128655, 129480, 129419, + 127959, 127791, 129699, 118939, 118940, 118944, 118943, 118941, 118942, + 118938, 118945, 118882, 118876, 118824, 118835, 118797, 118866, 118957, + 118801, 118802, 118865, 118818, 118916, 118819, 118917, 118899, 118935, + 119018, 119022, 119021, 119020, 119023, 119019, 119017, 118986, 118984, + 118985, 118843, 118887, 118808, 118870, 119003, 119002, 119004, 119005, + 118937, 118991, 118995, 118990, 118992, 118994, 118993, 118930, 118933, + 118931, 118932, 119014, 118918, 118912, 119015, 118785, 118831, 118907, + 118966, 118900, 118880, 118798, 118888, 118884, 118869, 118960, 118959, + 118958, 118836, 118969, 118977, 118978, 118971, 118976, 118975, 118973, + 118970, 118987, 118979, 118980, 118972, 118983, 983278, 118982, 118974, + 118988, 118981, 119000, 119001, 118929, 118928, 118806, 119029, 118927, + 118898, 118964, 118965, 118853, 118968, 118837, 118967, 118936, 118956, + 118810, 118854, 118847, 118839, 118906, 118791, 118800, 119024, 119026, + 118862, 118812, 119025, 119027, 118863, 118811, 118820, 119028, 118911, + 118842, 118850, 118921, 118858, 118913, 118914, 118915, 118834, 118860, + 118868, 118796, 118881, 118922, 118926, 118925, 118924, 118923, 118830, + 118877, 118949, 118947, 118948, 118963, 118955, 118962, 118946, 118961, + 118953, 118952, 118951, 118950, 118954, 118871, 118805, 118855, 118828, + 118901, 118787, 118788, 118856, 118816, 118875, 118845, 118879, 118793, + 118846, 118878, 118814, 118822, 118873, 118859, 118857, 118849, 118840, + 118861, 118786, 118895, 118841, 118874, 118892, 118897, 118807, 118784, + 118821, 118844, 118825, 118889, 119010, 119012, 119013, 119011, 119006, + 119008, 119009, 119007, 118910, 118815, 118852, 119016, 118826, 118885, + 118827, 118803, 118886, 118792, 118813, 118920, 118799, 118833, 118829, + 118904, 118902, 118903, 118905, 118804, 118934, 118919, 118832, 118893, + 118838, 118851, 118883, 118894, 118891, 118896, 118823, 118789, 118790, + 118872, 118817, 118809, 118908, 118909, 118996, 118998, 118997, 118999, + 118989, 118794, 118795, 118867, 118864, 118848, 118890, 983262, 983126, + 983057, 9764, 8454, 129305, 128197, 128247, 128248, 127957, 983098, 5130, + 5131, 5122, 6322, 5148, 5551, 5310, 5382, 5166, 6321, 5759, 5559, 5556, + 5557, 5558, 5567, 5564, 5565, 5566, 5563, 5560, 5561, 5562, 5555, 5552, + 5553, 5554, 5384, 5439, 6387, 6388, 5281, 5264, 6382, 6389, 5203, 5674, + 5675, 5677, 5676, 5673, 5672, 5706, 5707, 5709, 5708, 5705, 5704, 5204, + 5574, 5575, 5577, 5576, 5573, 5572, 6384, 6381, 5620, 6383, 5617, 5618, + 5619, 5616, 5615, 5195, 5592, 5593, 5595, 5594, 5591, 5590, 5174, 5175, + 5129, 5703, 5662, 5663, 5665, 5664, 5661, 5660, 5655, 5656, 6386, 5659, + 5657, 5654, 5652, 5633, 5629, 5630, 5632, 5631, 5628, 5627, 5623, 5624, + 5626, 5625, 5622, 5621, 5636, 5637, 5639, 5329, 5638, 5635, 5634, 5722, + 5718, 5719, 5721, 5720, 5717, 5716, 5712, 5713, 5715, 5714, 5711, 5710, + 5614, 5610, 5611, 5613, 5612, 5609, 5608, 5702, 5698, 5699, 5701, 5700, + 5697, 5696, 5686, 5687, 5689, 5688, 5685, 5684, 5692, 5693, 5695, 5694, + 5691, 5690, 5737, 5738, 5740, 5739, 5736, 5735, 5604, 5605, 5607, 5606, + 5603, 5602, 5598, 5599, 5601, 5600, 5597, 5596, 5725, 5726, 5728, 5727, + 5724, 5723, 5680, 5681, 5683, 5682, 5679, 5678, 5668, 5669, 5671, 5670, + 5667, 5666, 5731, 5732, 5734, 5733, 5730, 5729, 5642, 5643, 5645, 5644, + 5641, 5640, 5580, 5581, 5583, 5582, 5579, 5578, 5586, 5587, 5589, 5588, + 5585, 5584, 5648, 5649, 5651, 5650, 5647, 5646, 5128, 5265, 5258, 5276, + 5278, 5272, 5274, 5268, 5270, 5266, 5741, 5261, 5262, 5259, 5260, 5257, + 5121, 6364, 5163, 5469, 5465, 5466, 5460, 5461, 5158, 5157, 5162, 5155, + 5156, 6367, 6366, 5160, 5152, 5151, 5153, 5154, 5161, 5159, 5462, 5742, + 5463, 5464, 5467, 5459, 5120, 5501, 5123, 5124, 5164, 5251, 5252, 5246, + 5248, 6329, 5242, 5244, 5238, 5240, 5236, 5234, 5235, 5228, 6328, 5231, + 5232, 5229, 5230, 5227, 5354, 5542, 5540, 5541, 5538, 5539, 5536, 5537, + 5338, 5339, 5332, 6333, 5350, 5352, 5346, 5348, 5342, 5344, 5340, 5335, + 5336, 5333, 5334, 5331, 5307, 5283, 5356, 5458, 5287, 5288, 5385, 5290, + 5291, 5284, 6330, 5302, 5304, 5298, 5300, 5294, 5296, 5292, 5285, 5286, + 5309, 5328, 5473, 5475, 5471, 5319, 5391, 5388, 5389, 5386, 5390, 5380, + 5387, 5142, 5147, 5280, 5250, 5306, 5327, 5221, 5437, 72372, 72373, + 72370, 72371, 72368, 72369, 72378, 72379, 72376, 72377, 72374, 72375, + 5320, 5313, 6332, 5525, 5523, 5524, 5518, 5744, 5521, 5522, 5519, 5520, + 5526, 5749, 5750, 5747, 5748, 5745, 5746, 5499, 5497, 5498, 5495, 5496, + 5493, 5494, 5492, 5500, 5316, 5317, 6331, 5323, 5325, 6346, 6348, 6342, + 6344, 5321, 5314, 5315, 5312, 5330, 5509, 5507, 5508, 5502, 5743, 5505, + 5506, 5503, 5504, 5125, 6361, 6347, 6349, 6343, 6345, 6362, 6363, 6359, + 6358, 6360, 6356, 6357, 5165, 5126, 6320, 5193, 5184, 5186, 6326, 5188, + 5190, 5180, 5182, 5178, 5176, 5177, 5168, 6325, 5171, 5172, 6324, 5169, + 5170, 5167, 5456, 6368, 5443, 6355, 5454, 6353, 6354, 6351, 6352, 6350, + 5451, 5452, 5445, 6341, 5448, 5449, 5446, 5447, 5442, 5381, 5364, 6335, + 5570, 6380, 5571, 5568, 5569, 5653, 6385, 5658, 5529, 6379, 6378, 5530, + 5527, 5528, 5441, 5282, 5311, 5365, 5358, 5413, 5405, 5407, 6338, 5409, + 5411, 5401, 5403, 5399, 5395, 5396, 6336, 5397, 5398, 6337, 5393, 5394, + 5392, 5361, 5256, 5253, 5254, 5255, 5362, 6334, 5383, 5376, 5378, 5372, + 5374, 5368, 5370, 5366, 72383, 72380, 72381, 72382, 5359, 5360, 5357, + 5222, 5482, 5550, 5548, 5549, 5546, 5547, 5544, 5545, 5543, 6372, 5480, + 6371, 5478, 5479, 5476, 5477, 5472, 5474, 5470, 5512, 6377, 6376, 5513, + 5510, 5511, 5487, 5486, 6375, 5485, 6374, 6373, 5483, 5484, 5226, 5223, + 5224, 5225, 5205, 5206, 5197, 6327, 5217, 5219, 5213, 5215, 5209, 5211, + 5207, 5491, 5488, 5489, 5490, 5200, 5201, 5198, 5199, 5196, 5132, 5355, + 5351, 5353, 5347, 5349, 5343, 5345, 5341, 5453, 6370, 5450, 6369, 5444, + 5308, 5303, 5305, 5299, 5301, 5295, 5297, 5293, 5194, 5189, 5191, 5185, + 5187, 5181, 5183, 5179, 5440, 5434, 5436, 5430, 5432, 5426, 5428, 5424, + 5324, 5326, 5322, 5457, 5455, 5517, 5514, 5515, 5516, 5410, 5412, 5406, + 5408, 5402, 5404, 5400, 5377, 5379, 5373, 5375, 5369, 5371, 5367, 5277, + 5279, 5273, 5275, 5269, 5271, 5267, 5247, 5249, 5243, 5245, 5239, 5241, + 5237, 5481, 5218, 5220, 5214, 5216, 5210, 5212, 5208, 5468, 5144, 5146, + 5139, 5141, 5135, 5137, 5133, 6365, 5138, 5140, 5535, 5756, 5757, 5754, + 5755, 5752, 5753, 5751, 5534, 5531, 5532, 5533, 5758, 5143, 5145, 6323, + 5134, 5136, 5438, 5192, 5173, 5263, 5233, 5337, 5289, 5318, 5363, 5202, + 5420, 5127, 5149, 5421, 5422, 5415, 6340, 5418, 5419, 6339, 5433, 5435, + 5429, 5431, 5425, 5427, 5423, 5416, 5417, 5414, 5150, 983097, 983171, + 917631, 128473, 9803, 128367, 127852, 129387, 128758, 11839, 9809, + 128199, 128450, 128451, 8248, 8257, 8453, 66225, 66246, 66211, 66214, + 66254, 66218, 66250, 66251, 66252, 66253, 66229, 66238, 66244, 66227, + 66224, 66222, 66223, 66242, 66243, 66232, 66221, 66247, 66230, 66226, + 66239, 66212, 66248, 66256, 66235, 66208, 66215, 66210, 66220, 66234, + 66255, 66240, 66241, 66236, 66237, 66231, 66209, 66213, 66249, 66233, + 66245, 66217, 66219, 66216, 66228, 127904, 711, 127887, 129690, 983073, + 129365, 9936, 128008, 128049, 128569, 128572, 66888, 66864, 66912, 66882, + 66873, 66902, 66889, 66890, 66911, 66891, 66895, 66901, 66881, 66867, + 66870, 66868, 66904, 66866, 66876, 66910, 66879, 66883, 66897, 66915, + 66884, 66878, 66885, 66914, 66903, 66877, 66896, 66909, 66906, 66908, + 66899, 66872, 66913, 66874, 66871, 66875, 66869, 66893, 66887, 66892, + 66907, 66900, 66894, 66905, 66880, 66898, 66865, 66886, 66927, 9761, + 127797, 9963, 8373, 184, 65102, 65098, 8452, 162, 128328, 9907, 9939, + 129681, 69908, 69907, 69913, 69912, 69899, 69909, 69904, 69914, 69906, + 69905, 69911, 69910, 69920, 69921, 69918, 69917, 69901, 69900, 69898, + 69897, 69903, 69902, 69896, 69895, 69956, 69923, 69916, 69915, 69926, + 69919, 69922, 69925, 69959, 69924, 69891, 69894, 69892, 69893, 69888, + 69890, 69889, 69952, 69927, 69957, 69933, 69935, 69930, 69931, 69932, + 69958, 69928, 69929, 69934, 69936, 69939, 69954, 69953, 69947, 69946, + 69949, 69948, 69945, 69944, 69942, 69951, 69943, 69950, 69955, 69940, + 69938, 69937, 43587, 43597, 43596, 43573, 43572, 43574, 43571, 43590, + 43586, 43595, 43588, 43585, 43584, 43594, 43591, 43593, 43589, 43592, + 43530, 43531, 43536, 43538, 43537, 43543, 43544, 43551, 43552, 43548, + 43547, 43546, 43553, 43550, 43549, 43545, 43542, 43541, 43558, 43559, + 43520, 43524, 43533, 43532, 43529, 43528, 43535, 43534, 43527, 43526, + 43540, 43539, 43560, 43556, 43555, 43557, 43554, 43523, 43521, 43525, + 43522, 43612, 43614, 43613, 43615, 43561, 43568, 43569, 43562, 43563, + 43567, 43566, 43565, 43570, 43564, 43605, 43604, 43607, 43606, 43603, + 43602, 43600, 43609, 43601, 43608, 983058, 983140, 983137, 8256, 128200, + 128185, 128201, 128638, 129941, 10003, 129472, 128227, 5084, 5075, 5077, + 5079, 5081, 5082, 5083, 5055, 5037, 5038, 5039, 5040, 5041, 5042, 5054, + 5056, 5057, 5058, 5059, 5060, 5061, 5069, 5068, 5070, 5071, 5072, 5073, + 5074, 5085, 5086, 5087, 5088, 5089, 5090, 5091, 5092, 5093, 5094, 5095, + 5096, 5076, 5078, 5080, 5030, 5032, 5033, 5034, 5035, 5036, 5043, 5044, + 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5109, 5062, 5063, + 5064, 5065, 5066, 5067, 5097, 5098, 5099, 5100, 5101, 5102, 5103, 5104, + 5105, 5106, 5107, 5108, 5031, 5024, 5025, 5026, 5027, 5028, 5029, 43948, + 43939, 43941, 43943, 43945, 43946, 43947, 43919, 43901, 43902, 43903, + 43904, 43905, 43906, 43918, 43920, 43921, 43922, 43923, 43924, 43925, + 43933, 43932, 43934, 43935, 43936, 43937, 43938, 43949, 43950, 43951, + 43952, 43953, 43954, 43955, 43956, 43957, 43958, 43959, 43960, 43940, + 43942, 43944, 43894, 43896, 43897, 43898, 43899, 43900, 43907, 43908, + 43909, 43910, 43911, 43912, 43913, 43914, 43915, 43916, 43917, 5117, + 43926, 43927, 43928, 43929, 43930, 43931, 43961, 43962, 43963, 43964, + 43965, 43966, 43967, 5112, 5113, 5114, 5115, 5116, 43895, 43888, 43889, + 43890, 43891, 43892, 43893, 127800, 118462, 127826, 127792, 127937, + 129490, 128696, 94194, 94195, 9767, 128063, 128020, 9911, 69559, 69553, + 69567, 69571, 69556, 69564, 69570, 69552, 69568, 69555, 69560, 69562, + 69557, 69561, 69563, 69554, 69566, 69572, 69558, 69569, 69565, 69578, + 69574, 69575, 69577, 69573, 69579, 69576, 129378, 127851, 127876, 9962, + 127910, 9680, 9682, 10690, 10683, 10691, 9684, 9683, 9685, 9677, 9681, + 10677, 10682, 10684, 127246, 8859, 11199, 10687, 129282, 129280, 129281, + 128320, 9938, 127342, 127341, 127277, 8856, 10808, 9316, 9315, 9318, + 9317, 9314, 9313, 9450, 9320, 9312, 9319, 127247, 8857, 8861, 12905, + 12919, 12904, 12918, 12903, 12917, 12926, 12909, 12923, 12906, 12920, + 12896, 12910, 12900, 12914, 12897, 12911, 12908, 12922, 12901, 12915, + 12899, 12913, 12902, 12916, 12907, 12921, 12898, 12912, 9097, 127343, + 10162, 12975, 127569, 12959, 127568, 12963, 12951, 12962, 12965, 12973, + 12943, 12957, 12935, 12950, 12939, 12932, 12955, 12931, 12964, 12946, + 12869, 12871, 12952, 12966, 12967, 12969, 12942, 12954, 12938, 12976, + 12936, 12948, 12868, 12974, 12961, 12970, 12968, 12953, 12934, 12972, + 12956, 12944, 12870, 12947, 12949, 12971, 12945, 12933, 12958, 12930, + 12937, 12929, 12941, 12940, 12960, 12928, 127275, 127276, 128712, 13033, + 13036, 13034, 13037, 13035, 13013, 13016, 13014, 13017, 13015, 13038, + 13041, 13039, 13042, 13040, 13028, 13031, 13029, 13032, 13030, 13046, + 13049, 13047, 13050, 13048, 13018, 13021, 13019, 13022, 13020, 13023, + 13026, 13024, 13027, 13025, 13051, 13053, 13052, 13054, 13043, 13045, + 13044, 13008, 13011, 13009, 13012, 13010, 12925, 12924, 9398, 9399, 9400, + 9401, 9402, 9403, 9404, 9405, 9406, 9407, 9408, 9409, 9410, 9411, 9412, + 9413, 9414, 9415, 9416, 9417, 9418, 9419, 9420, 9421, 9422, 9423, 9424, + 9425, 9426, 9427, 9428, 9429, 9430, 9431, 9432, 9433, 9434, 9435, 9436, + 9437, 9438, 9439, 9440, 9441, 9442, 9443, 9444, 9445, 9446, 9447, 9448, + 9449, 10688, 10806, 8854, 12879, 9329, 9322, 12991, 12876, 9326, 12981, + 12875, 12982, 12986, 12985, 12988, 12987, 12984, 12983, 12990, 12989, + 9325, 12878, 9328, 12877, 9327, 9321, 12872, 12890, 12874, 12891, 12895, + 12894, 12978, 12977, 12893, 12892, 12980, 12979, 9324, 9331, 12873, + 12881, 12885, 12884, 12887, 12886, 12883, 12882, 12889, 12888, 9323, + 9330, 10050, 12342, 10679, 10681, 8853, 8858, 10680, 128981, 9098, 8855, + 10686, 10026, 127278, 127245, 8860, 10678, 10689, 128983, 11198, 94, + 10768, 127914, 127961, 127750, 11946, 12003, 11910, 11963, 11962, 11950, + 11992, 12012, 12000, 12005, 11996, 12010, 11984, 11988, 11994, 11987, + 11952, 12007, 11977, 11976, 11973, 12019, 11993, 12014, 11995, 12016, + 12006, 12002, 11979, 11936, 11983, 11905, 11970, 11943, 11931, 11914, + 11934, 11944, 11999, 11998, 11997, 11960, 11947, 11939, 11978, 11968, + 11967, 11966, 12004, 11927, 11926, 12001, 11975, 11928, 12018, 12013, + 12015, 12011, 11945, 11986, 11985, 11921, 11920, 11919, 11918, 11964, + 11957, 11990, 11989, 11935, 11965, 11933, 11941, 11940, 11909, 11991, + 11959, 11929, 11904, 11908, 11907, 11906, 11915, 11942, 11974, 11980, + 12008, 12009, 11951, 11925, 11924, 11922, 11949, 11948, 11917, 11916, + 11958, 11932, 12017, 11911, 11923, 11969, 11982, 11981, 11938, 11937, + 11972, 11971, 11956, 11955, 11954, 11953, 11913, 11912, 11961, 12752, + 12743, 12748, 12768, 12772, 12757, 12741, 12750, 12769, 12747, 12749, + 12744, 12742, 12746, 12758, 12754, 12763, 12770, 12764, 12753, 12740, + 12767, 12760, 12759, 12745, 12773, 12766, 12762, 12755, 12761, 12736, + 12765, 12739, 12737, 12738, 12756, 12751, 12771, 128079, 127916, 127963, + 128385, 129346, 127867, 128203, 128346, 128358, 128343, 128355, 128340, + 128352, 128339, 128351, 128344, 128356, 128336, 128348, 128342, 128354, + 128341, 128353, 128345, 128357, 128347, 128359, 128337, 128349, 128338, + 128350, 10561, 8754, 128259, 10227, 128472, 128257, 128258, 8631, 11118, + 8635, 8753, 10959, 10961, 10960, 10962, 127746, 10828, 10832, 128234, + 128235, 128272, 128213, 10829, 8272, 9729, 127786, 127785, 127784, + 127783, 129313, 9114, 129715, 127864, 129381, 58, 8788, 8353, 128165, + 6867, 7625, 7623, 769, 791, 833, 8410, 8404, 8423, 857, 8432, 7677, 844, + 774, 7627, 814, 810, 838, 70459, 780, 812, 784, 8409, 8405, 787, 789, + 806, 65062, 65069, 42609, 1160, 11774, 11744, 11768, 11747, 11757, 11765, + 42654, 11751, 11752, 11753, 11756, 42613, 11775, 11772, 42655, 11767, + 11754, 42619, 11763, 11762, 42618, 42615, 42612, 42617, 11770, 42614, + 11771, 11773, 11769, 11759, 42616, 11760, 11758, 11748, 11749, 11761, + 11746, 11764, 11755, 11745, 11750, 11766, 42621, 1156, 1158, 1159, 1157, + 42608, 42610, 1155, 65070, 65071, 1161, 42620, 123023, 42607, 770, 813, + 807, 43248, 43244, 43245, 43246, 43247, 43242, 43243, 43249, 43237, + 43236, 43239, 43238, 43235, 43234, 43232, 43241, 43233, 43240, 7675, 776, + 804, 6876, 6833, 775, 7672, 856, 803, 7674, 6877, 7617, 7616, 6886, 6887, + 779, 861, 860, 865, 7676, 6863, 7629, 862, 863, 6840, 831, 6858, 6857, + 6844, 866, 6891, 858, 864, 65058, 65059, 8422, 840, 782, 783, 819, 6832, + 798, 6875, 6835, 8413, 8416, 8418, 8414, 8419, 8420, 8415, 839, 6888, + 850, 8412, 6874, 122892, 122884, 122891, 122890, 122921, 122919, 122889, + 122916, 122900, 122907, 122910, 122901, 122908, 122880, 122920, 122881, + 122909, 122903, 122922, 122883, 122895, 122894, 122896, 122898, 122899, + 122882, 122885, 122912, 122911, 122913, 122918, 122915, 122888, 122886, + 122904, 122902, 122897, 122893, 70508, 70507, 70506, 70505, 70504, 70502, + 70503, 70515, 70513, 70514, 70516, 70512, 768, 790, 832, 6865, 7624, + 7621, 847, 119363, 119362, 119364, 835, 834, 837, 836, 777, 843, 795, + 785, 815, 826, 6883, 811, 6855, 6834, 7632, 12442, 12441, 7671, 7670, 7643, 7646, 7647, 7649, 7650, 867, 7666, 7655, 7636, 7637, 7638, 872, 7639, 868, 7663, 7641, 7659, 7635, 869, 7640, 6860, 6861, 6862, 7645, 7660, 7653, 870, 7667, 7661, 871, 7668, 7664, 876, 7651, 7626, 877, 6848, 7652, 7658, 7656, 7657, 7665, 6847, 873, 7642, 874, 7644, 875, 7648, - 7662, 878, 879, 7654, 852, 7678, 8430, 8406, 841, 794, 6849, 6851, 796, - 849, 8400, 792, 845, 8417, 8429, 8426, 65056, 65063, 65057, 65064, 6841, - 8427, 824, 822, 8402, 818, 772, 65060, 65067, 65061, 65068, 817, 7622, - 7620, 7628, 800, 6854, 842, 66424, 66425, 66423, 66426, 66422, 6839, 808, - 7630, 773, 6846, 6845, 6843, 801, 799, 6856, 8421, 788, 802, 7679, 854, - 848, 853, 8431, 8407, 825, 855, 8401, 6850, 6852, 793, 8428, 8408, 805, - 778, 7631, 859, 823, 821, 8403, 6853, 827, 6842, 7619, 7618, 828, 8411, - 771, 65065, 65066, 820, 816, 6859, 8424, 6836, 786, 846, 797, 7669, 7633, - 7634, 809, 781, 830, 7673, 8425, 6838, 6837, 851, 829, 8274, 64, 44, - 128476, 9092, 8705, 129517, 9732, 127882, 128533, 128534, 128119, 128679, - 8715, 8883, 8885, 8954, 8955, 8957, 8750, 127899, 983187, 9089, 127978, - 9010, 9740, 10861, 127859, 127850, 127834, 11505, 11504, 11503, 11464, - 11392, 11506, 11499, 11501, 11446, 11452, 11458, 11466, 11442, 11448, - 11450, 11398, 1006, 1002, 11396, 1000, 11406, 998, 11436, 11412, 11420, - 996, 11434, 11472, 11414, 11422, 11478, 11468, 11470, 11476, 11474, - 11482, 11460, 11480, 11454, 11462, 11444, 11486, 11488, 11484, 11490, - 11440, 1004, 994, 11456, 11402, 11428, 11408, 11430, 11404, 11394, 11438, - 11424, 11410, 11426, 11400, 11416, 11418, 11432, 66298, 66289, 66295, - 66286, 66294, 66285, 66299, 66290, 66291, 66297, 66288, 66296, 66287, - 66293, 66284, 66292, 66283, 66282, 66277, 66276, 66279, 66278, 66275, - 66274, 66281, 66273, 66280, 66272, 11517, 11518, 11514, 11515, 11516, - 11513, 11465, 11393, 11507, 11500, 11502, 11447, 11453, 11459, 11467, - 11443, 11449, 11451, 11399, 1007, 1003, 11397, 1001, 11407, 999, 11437, - 11413, 11421, 997, 11435, 11473, 11415, 11423, 11479, 11469, 11471, - 11477, 11475, 11483, 11461, 11481, 11455, 11463, 11445, 11487, 11489, - 11485, 11491, 11441, 1005, 995, 11457, 11403, 11429, 11409, 11431, 11405, - 11395, 11439, 11425, 11411, 11427, 11401, 11417, 11419, 11433, 11497, - 11492, 11493, 11494, 11498, 11495, 11496, 11519, 127279, 169, 11855, - 8792, 129720, 9012, 9013, 119661, 119660, 119663, 119662, 119659, 119658, - 119665, 119657, 119664, 119652, 119651, 119654, 119653, 119650, 119649, - 119656, 119648, 119655, 128715, 128145, 128004, 128046, 9904, 129689, - 129509, 983074, 128179, 127769, 129431, 127951, 9769, 9768, 11856, 11857, - 128322, 128321, 10060, 127370, 127884, 9876, 9932, 128010, 129360, - 128081, 8354, 129660, 128575, 128546, 128302, 129408, 8731, 74794, 74771, - 74861, 74780, 74820, 74821, 74765, 74758, 74853, 74856, 74855, 74854, - 74791, 74801, 74844, 74836, 74837, 74809, 74755, 74829, 74777, 74786, - 74768, 74858, 74762, 74834, 74835, 74808, 74812, 74814, 74815, 74813, - 74754, 74828, 74776, 74785, 74790, 74800, 74767, 74857, 74761, 74839, - 74838, 74822, 74825, 74823, 74824, 74795, 74772, 74862, 74781, 74766, - 74759, 74849, 74850, 74840, 74847, 74848, 74851, 74831, 74804, 74773, - 74782, 74845, 74842, 74796, 74852, 74818, 74819, 74817, 74793, 74770, - 74860, 74779, 74764, 74757, 74802, 74803, 74792, 74756, 74830, 74769, - 74859, 74778, 74816, 74763, 74806, 74807, 74833, 74788, 74789, 74798, - 74799, 74810, 74811, 74753, 74827, 74775, 74784, 74760, 74752, 74826, - 74832, 74805, 74841, 74774, 74783, 74787, 74797, 74846, 74843, 74867, - 74868, 74866, 74865, 74864, 73728, 73734, 73731, 73733, 73735, 73736, - 73730, 73732, 73729, 73738, 73742, 73741, 73744, 73745, 74881, 73747, - 73748, 73740, 73739, 74608, 74880, 73746, 73743, 73749, 73750, 73753, - 73752, 73754, 73755, 73751, 74609, 73756, 74882, 73757, 73759, 73758, - 73760, 73765, 73766, 73762, 73763, 73768, 73761, 73767, 73764, 73770, - 73769, 73771, 74610, 73772, 73773, 73776, 73777, 73775, 73774, 73778, - 73780, 73781, 73782, 73784, 73788, 73789, 73787, 73785, 73786, 73791, - 73790, 73783, 73779, 73737, 73792, 73793, 74883, 73795, 74884, 74885, - 74886, 73796, 73797, 73798, 73799, 73800, 73794, 73801, 73804, 73803, - 73802, 73805, 74887, 73806, 73807, 73808, 73809, 73810, 73811, 73812, - 73813, 73814, 73815, 73816, 73817, 73818, 73819, 73820, 73821, 73822, - 73823, 73825, 73826, 73829, 73830, 73831, 73828, 73836, 74611, 73837, - 73833, 73835, 73827, 73832, 73834, 73824, 74889, 74612, 73839, 73840, - 73841, 74888, 73838, 73842, 73844, 74891, 74890, 73845, 73846, 74892, - 73847, 73848, 73849, 74613, 73843, 73850, 73852, 73853, 73851, 73854, - 73855, 74614, 73856, 73857, 74894, 74895, 74893, 74896, 74897, 74900, - 74901, 74902, 74899, 74908, 74909, 74907, 74906, 74911, 74912, 74910, - 74913, 74914, 74915, 74916, 74920, 74919, 74898, 74905, 74903, 74904, - 74917, 74918, 73858, 73860, 73861, 73862, 73863, 73864, 73865, 73859, - 73866, 73868, 73867, 73869, 73873, 73874, 73870, 73871, 74922, 74921, - 73872, 73875, 73879, 73883, 73884, 73880, 73881, 73882, 73885, 73887, - 74923, 73886, 73888, 74924, 73889, 74927, 74930, 74931, 74925, 74928, - 74929, 74932, 74926, 73890, 73891, 73892, 73893, 73895, 73896, 73900, - 73901, 73902, 73903, 73904, 73905, 73906, 74616, 74933, 73907, 73908, - 73899, 73897, 73898, 74615, 73894, 73877, 73876, 73878, 73909, 73911, - 73912, 73914, 73913, 73916, 74617, 73917, 74618, 73918, 73915, 74934, - 73920, 73919, 73921, 73922, 73924, 73925, 74935, 74936, 74937, 73926, - 73923, 73929, 73930, 73927, 73928, 74938, 74939, 73932, 74941, 74940, - 73931, 73933, 73934, 73935, 73936, 73937, 74942, 73938, 73939, 73941, - 73940, 73943, 73942, 73945, 73944, 73946, 73947, 74943, 73948, 73949, - 74944, 74945, 74946, 73950, 74947, 73951, 74948, 74949, 74950, 73952, - 73953, 73957, 73958, 73959, 74951, 73955, 73956, 73960, 73962, 73963, - 73964, 73961, 74952, 73954, 73965, 73966, 74953, 73967, 73968, 73969, - 73970, 73971, 73972, 73974, 73975, 73978, 73977, 73976, 73910, 73979, - 73980, 73981, 73973, 73982, 73983, 74954, 74619, 73984, 73985, 73986, - 73987, 73988, 73990, 73989, 73994, 73995, 73998, 73996, 73997, 73999, - 73992, 73993, 74001, 74955, 74004, 74003, 74005, 74002, 74000, 73991, - 74620, 74006, 74008, 74009, 74010, 74956, 74012, 74011, 74013, 74014, - 74957, 74015, 74016, 74017, 74019, 74020, 74021, 74024, 74023, 74022, - 74007, 74018, 74025, 74026, 74958, 74027, 74028, 74029, 74030, 74959, - 74031, 74033, 74036, 74035, 74032, 74034, 74037, 74038, 74039, 74040, - 74043, 74044, 74042, 74041, 74045, 74046, 74621, 74047, 74050, 74052, - 74051, 74053, 74054, 74058, 74057, 74055, 74056, 74059, 74060, 74061, - 74062, 74064, 74065, 74063, 74066, 74067, 74048, 74070, 74049, 74068, - 74069, 74071, 74072, 74073, 74074, 74622, 74075, 74623, 74077, 74076, - 74078, 74079, 74960, 74080, 74081, 74082, 74085, 74086, 74084, 74083, - 74087, 74624, 74090, 74089, 74088, 74091, 74092, 74625, 74093, 74094, - 74096, 74097, 74961, 74095, 74099, 74627, 74098, 74100, 74101, 74103, - 74102, 74104, 74105, 74115, 74629, 74114, 74112, 74113, 74110, 74111, - 74117, 74116, 74118, 74630, 74119, 74962, 74963, 74631, 74122, 74123, - 74120, 74121, 74626, 74107, 74106, 74628, 74108, 74109, 74124, 74125, - 74126, 74131, 74132, 74128, 74129, 74130, 74133, 74134, 74135, 74136, - 74137, 983267, 74138, 74139, 74140, 74141, 74142, 74607, 74127, 74144, - 74146, 74147, 74145, 74152, 74153, 74150, 74151, 74148, 74149, 74154, - 74155, 74157, 74158, 74163, 74164, 74165, 74160, 74161, 74156, 74159, - 74162, 74143, 74166, 74167, 74168, 74169, 74170, 74171, 74172, 74175, - 74173, 74174, 74176, 74177, 74182, 74183, 74180, 74181, 74632, 74186, - 74184, 74185, 74188, 74190, 74189, 74187, 74194, 74195, 74193, 74191, - 74192, 74196, 74197, 74198, 74199, 74200, 74201, 74202, 74205, 74206, - 74207, 74208, 74204, 74209, 74211, 74210, 74212, 74213, 74215, 74214, - 74216, 74218, 74217, 74964, 74178, 74179, 74203, 74219, 74220, 74223, - 74224, 74221, 74222, 74966, 74967, 74974, 74973, 74972, 74969, 74970, - 74971, 74975, 74968, 74965, 74977, 74976, 74980, 74981, 74982, 74984, - 74985, 74978, 74979, 74983, 74986, 74987, 74988, 74989, 74990, 74991, - 74993, 74997, 74996, 74998, 74995, 74994, 74992, 74999, 75000, 75003, - 75004, 75005, 75006, 75001, 75002, 75009, 75015, 75016, 75019, 75017, - 75018, 75013, 75012, 75010, 75011, 75014, 75021, 75030, 75029, 75027, - 75024, 75025, 75028, 75022, 75026, 75023, 75008, 75020, 75031, 75032, - 75007, 74226, 74227, 74228, 74229, 74230, 74225, 74231, 74233, 74234, - 74232, 74235, 74237, 74258, 74259, 75033, 74261, 74633, 74260, 74240, - 74634, 74241, 74243, 75035, 74246, 74247, 74245, 74248, 74249, 74250, - 74251, 75036, 75037, 74254, 74255, 74635, 74256, 75038, 74242, 74252, - 74253, 75034, 74238, 74239, 74244, 74257, 74263, 74265, 74264, 74266, - 74269, 74270, 74271, 74236, 74262, 74267, 74268, 74272, 74273, 74274, - 74278, 74279, 74275, 74276, 74277, 74280, 74281, 74636, 74282, 75039, - 74283, 74284, 74290, 74294, 74295, 75041, 75040, 74292, 74293, 74291, - 74296, 74297, 74298, 74299, 74300, 74637, 74301, 74286, 74287, 74285, - 74289, 74288, 74302, 74304, 74307, 74305, 74306, 74308, 74310, 74309, - 74311, 74303, 74638, 74312, 74314, 74313, 74315, 74316, 74319, 74321, - 74320, 74639, 74322, 74324, 74325, 74323, 74642, 75043, 74326, 75044, - 75046, 75047, 74327, 75048, 74329, 74328, 75049, 74330, 74332, 74333, - 74331, 75050, 75051, 74334, 75052, 74335, 75042, 74641, 75045, 74640, - 74317, 74336, 74318, 74337, 74338, 983266, 983265, 74643, 74339, 74347, - 74348, 74342, 74343, 74341, 74344, 74340, 74346, 74345, 74349, 74355, - 74354, 74350, 74352, 74358, 74359, 74353, 74357, 74351, 74356, 74360, - 74361, 74362, 74363, 74364, 74365, 74366, 74644, 74367, 74375, 74376, - 74368, 74369, 74373, 74374, 74370, 74371, 74372, 74377, 74378, 74379, - 74380, 74381, 74382, 74645, 74383, 74384, 74385, 74386, 74387, 74389, - 74408, 75053, 74388, 74646, 74395, 74394, 75055, 74400, 74399, 75056, - 74401, 74406, 74402, 74403, 74404, 74405, 74391, 74392, 74396, 74398, - 75054, 74397, 74393, 74390, 74407, 74409, 74410, 74411, 74412, 74413, - 74414, 74421, 74422, 74419, 74417, 74420, 74416, 74418, 74415, 74423, - 75057, 74424, 74425, 74426, 75058, 74428, 74429, 75059, 75060, 75061, - 74427, 74432, 74434, 74433, 74430, 74431, 74435, 74437, 74436, 74438, - 74441, 74440, 74444, 74445, 74446, 74448, 74447, 74443, 74449, 74442, - 74439, 74451, 74453, 74452, 74450, 74454, 74455, 74456, 74457, 75063, - 75062, 74458, 74459, 75064, 74460, 74461, 74462, 74463, 74465, 74464, - 74466, 74468, 74469, 74471, 74472, 74473, 74474, 74467, 74470, 74475, - 74477, 74478, 74479, 74476, 74480, 74481, 74482, 74483, 74488, 74486, - 74487, 74485, 74489, 74484, 74490, 75065, 74491, 74494, 74497, 74499, - 74500, 74498, 74495, 74647, 74496, 74501, 74504, 75066, 75067, 74505, - 74506, 74502, 74503, 74492, 74493, 74507, 74510, 74512, 74511, 74649, - 74509, 74508, 74515, 74516, 74522, 74523, 74519, 74520, 74517, 74518, - 74521, 74524, 74534, 74535, 74525, 74526, 74648, 74527, 74528, 74529, - 74531, 74532, 74533, 74530, 74536, 74538, 74537, 74539, 75068, 74540, - 74541, 74542, 74545, 74546, 74547, 75069, 74544, 74543, 74549, 74550, - 74551, 74552, 74553, 75070, 74555, 74556, 74558, 74557, 74559, 74560, - 74562, 74564, 74563, 75072, 74566, 75071, 74570, 74569, 74572, 74574, - 74573, 74554, 74567, 74571, 74565, 74561, 74568, 74575, 74576, 74548, - 74577, 74581, 74579, 74580, 74578, 74584, 74583, 74582, 74586, 74587, - 74588, 74585, 74513, 74514, 74589, 74591, 74590, 74593, 75073, 74592, - 74595, 74598, 74599, 74596, 74601, 74597, 74600, 74602, 75074, 74603, - 75075, 74604, 74605, 74606, 74594, 9982, 129380, 11232, 129473, 8911, - 8910, 10160, 9130, 129356, 128177, 164, 127835, 10081, 128707, 127854, - 129362, 129385, 67594, 67595, 67596, 67597, 67598, 67599, 67600, 67601, - 67602, 67603, 67604, 67605, 67606, 67607, 67608, 67609, 67610, 67611, - 67612, 67613, 67614, 67615, 67616, 67617, 67618, 67619, 67620, 67621, - 67622, 67623, 67624, 67625, 67626, 67627, 67628, 67629, 67630, 67631, - 67632, 67633, 67634, 67635, 67636, 67637, 67589, 67592, 67644, 67647, - 67639, 67640, 67584, 67585, 67586, 67587, 67588, 77712, 77713, 77714, - 77715, 77716, 77717, 77718, 77719, 77722, 77723, 77720, 77721, 77724, - 77725, 77726, 77727, 77728, 77729, 77730, 77731, 77732, 77733, 77734, - 77735, 77736, 77737, 77738, 77739, 77740, 77741, 77742, 77743, 77744, - 77745, 77746, 77747, 77748, 77749, 77750, 77751, 77752, 77753, 77754, - 77755, 77756, 77757, 77758, 77773, 77774, 77768, 77769, 77770, 77771, - 77772, 77775, 77776, 77777, 77788, 77789, 77790, 77791, 77792, 77793, - 77794, 77795, 77796, 77759, 77760, 77761, 77762, 77763, 77764, 77765, - 77766, 77767, 77778, 77779, 77780, 77781, 77782, 77783, 77784, 77785, - 77786, 77787, 77797, 77798, 77799, 77800, 77801, 77802, 77803, 77804, - 77805, 77806, 77807, 77808, 77809, 77810, 1126, 1033, 1300, 42574, 1034, - 1055, 1190, 1316, 1136, 1146, 42564, 42592, 42580, 1296, 1302, 1058, - 42634, 1196, 1035, 42640, 42638, 1062, 42642, 7305, 42636, 1059, 1266, - 1264, 1262, 1144, 1028, 1040, 1234, 1232, 1212, 1214, 1248, 1192, 1310, - 1256, 1258, 1184, 42602, 1130, 42572, 42586, 1030, 1041, 1063, 1268, - 1206, 1208, 42584, 42650, 42630, 1026, 42568, 42604, 42648, 1029, 42562, - 1322, 42632, 1039, 42626, 1324, 42624, 1044, 1069, 1051, 1312, 1326, - 1221, 1298, 1053, 1314, 1186, 1320, 1225, 1223, 1056, 1166, 1260, 1057, - 1194, 1052, 1229, 1060, 1043, 1172, 1168, 1270, 1170, 1274, 1027, 1061, - 1202, 1276, 1278, 1066, 42644, 1048, 1252, 1037, 1250, 1045, 1024, 1238, - 1025, 42588, 1128, 1132, 42578, 42582, 1124, 42566, 1140, 1142, 1050, - 1178, 1219, 1180, 1182, 1286, 1282, 1280, 1288, 1290, 1292, 1294, 1284, - 1152, 1227, 1036, 1134, 42600, 42570, 1054, 1120, 1148, 1254, 1150, 1240, - 1242, 1049, 1162, 1038, 1210, 1318, 1065, 42646, 1064, 42596, 42598, - 1068, 42594, 1198, 1200, 1164, 1071, 1304, 1122, 1067, 1272, 42576, 1031, - 42590, 1070, 1047, 1246, 1176, 42560, 1046, 1244, 1174, 1217, 42628, - 1138, 1032, 1042, 1308, 1306, 1236, 1204, 1188, 42622, 42606, 1216, 7467, - 42623, 1072, 1235, 1233, 1213, 1215, 1249, 1193, 1311, 1257, 1259, 1185, - 42603, 1131, 42573, 42587, 1110, 1073, 1095, 1269, 1207, 1209, 42585, - 42651, 42631, 1106, 42569, 42605, 42649, 1109, 42563, 1323, 42633, 1119, - 42627, 1325, 42625, 1076, 1101, 1083, 1313, 1327, 1222, 1299, 1085, 1315, - 1187, 1321, 1226, 1224, 1088, 1167, 1261, 1089, 1195, 1084, 1230, 1092, - 1075, 1173, 1169, 1271, 1171, 1275, 1107, 1093, 1203, 1277, 1279, 1098, - 42645, 1080, 1253, 1117, 1251, 1077, 1104, 1239, 1105, 42589, 1129, 1133, - 42579, 42583, 1125, 42567, 1141, 1143, 1082, 1179, 1220, 1181, 1183, - 1287, 1283, 1281, 1289, 1291, 1293, 1295, 1285, 1153, 1228, 1116, 1135, - 1127, 7297, 1113, 1301, 42601, 42571, 42575, 7298, 1114, 1086, 1121, - 1149, 1255, 1151, 1087, 1191, 1317, 1231, 1137, 42565, 42593, 42581, - 1297, 1147, 7296, 1303, 1241, 1243, 1081, 1163, 1118, 1211, 1319, 1097, - 42647, 1096, 42597, 42599, 1100, 42595, 1199, 1201, 1165, 7302, 7303, - 7300, 1090, 42635, 1197, 1115, 42641, 42639, 1094, 7301, 42643, 7306, - 42637, 1091, 1267, 1265, 1263, 1145, 1108, 7304, 7299, 1309, 1103, 1305, - 1123, 1099, 1273, 42577, 1111, 42591, 1102, 1079, 1247, 1177, 42561, - 1078, 1245, 1175, 1218, 42629, 1139, 1112, 1074, 1307, 1237, 1205, 1189, - 122984, 122962, 122986, 122985, 122965, 122976, 122971, 122974, 122964, - 122983, 122977, 122981, 122982, 122980, 122978, 122967, 122968, 122969, - 122966, 122979, 122973, 122963, 122970, 122961, 122972, 122975, 1154, - 9005, 127744, 983201, 983188, 983172, 8224, 11830, 11831, 128481, 128131, - 127841, 128374, 9619, 11843, 128168, 10143, 65101, 65097, 8504, 983081, - 983084, 983086, 983088, 983090, 983162, 9110, 9192, 127795, 128475, 8451, - 176, 8457, 983120, 128666, 8796, 983119, 9161, 9159, 9153, 9156, 9162, - 9160, 9154, 9157, 9164, 9151, 9163, 9150, 9158, 9152, 9155, 117829, - 117828, 127980, 9739, 66589, 66591, 66596, 66597, 66587, 66585, 66594, - 66595, 66593, 66599, 66562, 66563, 66564, 66565, 66561, 66560, 66568, - 66569, 66570, 66571, 66567, 66566, 66598, 66573, 66588, 66579, 66592, - 66590, 66581, 66578, 66580, 66582, 66577, 66586, 66575, 66584, 66583, - 66574, 66572, 66576, 66629, 66631, 66636, 66637, 66627, 66625, 66634, - 66635, 66633, 66639, 66602, 66603, 66604, 66605, 66601, 66600, 66608, - 66609, 66610, 66611, 66607, 66606, 66638, 66613, 66628, 66619, 66632, - 66630, 66621, 66618, 66620, 66622, 66617, 66626, 66615, 66624, 66623, - 66614, 66612, 66616, 127964, 127965, 128421, 128468, 2388, 2416, 43258, - 2387, 43257, 72448, 72449, 43259, 2309, 2310, 2320, 2324, 2421, 43262, - 2330, 2418, 2317, 2321, 2331, 2396, 2430, 2338, 2337, 2343, 2342, 2429, - 2394, 2328, 2427, 2327, 2426, 2361, 2350, 2424, 2308, 2318, 2322, 2358, - 2359, 2360, 2323, 2420, 2419, 2431, 2349, 2348, 2393, 2326, 2325, 2397, - 2353, 2352, 2399, 2351, 2313, 2314, 2423, 2422, 2345, 2339, 2329, 2334, - 2344, 2333, 2428, 2332, 2356, 2355, 2354, 2336, 2335, 2341, 2340, 2315, - 2400, 2316, 2401, 2357, 2311, 2312, 2347, 2346, 2425, 2395, 2398, 2392, - 2319, 983644, 983643, 983646, 983647, 983649, 983648, 983642, 983645, - 72450, 72451, 72452, 72453, 2305, 43255, 43251, 43254, 43253, 72455, - 72454, 72456, 43250, 43260, 2304, 72457, 2364, 2365, 2306, 43252, 43256, - 2417, 2381, 2307, 2386, 2385, 2366, 2376, 2380, 2383, 43263, 2389, 2373, - 2377, 2379, 2363, 2362, 2382, 2369, 2370, 2391, 2390, 2374, 2378, 2371, - 2372, 2402, 2403, 2367, 2368, 2375, 2405, 2404, 2411, 2410, 2413, 2412, - 2409, 2408, 2406, 2415, 2407, 2414, 43261, 2384, 983089, 983161, 983087, - 983085, 983083, 127962, 129487, 129420, 11033, 11032, 11030, 11031, + 7662, 878, 879, 7654, 6889, 841, 794, 852, 7678, 8430, 8406, 6849, 6851, + 796, 849, 8400, 792, 6880, 845, 8417, 8429, 8426, 65056, 65063, 65057, + 65064, 6841, 8427, 824, 822, 8402, 818, 772, 65060, 65067, 65061, 65068, + 817, 6869, 7620, 6872, 7622, 7628, 800, 6882, 6854, 842, 66424, 66425, + 66423, 66426, 66422, 6839, 808, 7630, 773, 6846, 6845, 6843, 801, 799, + 6856, 8421, 788, 802, 7679, 854, 848, 853, 8431, 8407, 825, 855, 8401, + 6850, 6852, 793, 6881, 8428, 8408, 805, 778, 7631, 859, 823, 821, 8403, + 6873, 6853, 827, 6884, 6842, 7619, 7618, 828, 6885, 8411, 771, 65065, + 65066, 820, 816, 6859, 8424, 6836, 786, 797, 7669, 846, 6890, 7633, 7634, + 809, 781, 830, 6864, 6870, 6866, 6871, 6868, 7673, 8425, 6838, 6837, 851, + 829, 8274, 64, 44, 128476, 9092, 8705, 129517, 9732, 127882, 128533, + 128534, 128119, 128679, 8715, 8883, 8885, 8954, 8955, 8957, 8750, 127899, + 983187, 9089, 127978, 9010, 9740, 10861, 127859, 127850, 127834, 11505, + 11504, 11503, 11464, 11392, 11506, 11499, 11501, 11446, 11452, 11458, + 11466, 11442, 11448, 11450, 11398, 1006, 1002, 11396, 1000, 11406, 998, + 11436, 11412, 11420, 996, 11434, 11472, 11414, 11422, 11478, 11468, + 11470, 11476, 11474, 11482, 11460, 11480, 11454, 11462, 11444, 11486, + 11488, 11484, 11490, 11440, 1004, 994, 11456, 11402, 11428, 11408, 11430, + 11404, 11394, 11438, 11424, 11410, 11426, 11400, 11416, 11418, 11432, + 66298, 66289, 66295, 66286, 66294, 66285, 66299, 66290, 66291, 66297, + 66288, 66296, 66287, 66293, 66284, 66292, 66283, 66282, 66277, 66276, + 66279, 66278, 66275, 66274, 66281, 66273, 66280, 66272, 11517, 11518, + 11514, 11515, 11516, 11513, 11465, 11393, 11507, 11500, 11502, 11447, + 11453, 11459, 11467, 11443, 11449, 11451, 11399, 1007, 1003, 11397, 1001, + 11407, 999, 11437, 11413, 11421, 997, 11435, 11473, 11415, 11423, 11479, + 11469, 11471, 11477, 11475, 11483, 11461, 11481, 11455, 11463, 11445, + 11487, 11489, 11485, 11491, 11441, 1005, 995, 11457, 11403, 11429, 11409, + 11431, 11405, 11395, 11439, 11425, 11411, 11427, 11401, 11417, 11419, + 11433, 11497, 11492, 11493, 11494, 11498, 11495, 11496, 11519, 127279, + 169, 11855, 8792, 129720, 9012, 9013, 119661, 119660, 119663, 119662, + 119659, 119658, 119665, 119657, 119664, 119652, 119651, 119654, 119653, + 119650, 119649, 119656, 119648, 119655, 128715, 128145, 128004, 128046, + 9904, 129689, 129509, 983074, 128179, 127769, 129431, 127951, 9769, 9768, + 11856, 11857, 128322, 128321, 10060, 127370, 127884, 9876, 9932, 128010, + 129360, 128081, 8354, 129660, 128575, 128546, 128302, 129408, 8731, + 74794, 74771, 74861, 74780, 74820, 74821, 74765, 74758, 74853, 74856, + 74855, 74854, 74791, 74801, 74844, 74836, 74837, 74809, 74755, 74829, + 74777, 74786, 74768, 74858, 74762, 74834, 74835, 74808, 74812, 74814, + 74815, 74813, 74754, 74828, 74776, 74785, 74790, 74800, 74767, 74857, + 74761, 74839, 74838, 74822, 74825, 74823, 74824, 74795, 74772, 74862, + 74781, 74766, 74759, 74849, 74850, 74840, 74847, 74848, 74851, 74831, + 74804, 74773, 74782, 74845, 74842, 74796, 74852, 74818, 74819, 74817, + 74793, 74770, 74860, 74779, 74764, 74757, 74802, 74803, 74792, 74756, + 74830, 74769, 74859, 74778, 74816, 74763, 74806, 74807, 74833, 74788, + 74789, 74798, 74799, 74810, 74811, 74753, 74827, 74775, 74784, 74760, + 74752, 74826, 74832, 74805, 74841, 74774, 74783, 74787, 74797, 74846, + 74843, 74867, 74868, 74866, 74865, 74864, 73728, 73734, 73731, 73733, + 73735, 73736, 73730, 73732, 73729, 73738, 73742, 73741, 73744, 73745, + 74881, 73747, 73748, 73740, 73739, 74608, 74880, 73746, 73743, 73749, + 73750, 73753, 73752, 73754, 73755, 73751, 74609, 73756, 74882, 73757, + 73759, 73758, 73760, 73765, 73766, 73762, 73763, 73768, 73761, 73767, + 73764, 73770, 73769, 73771, 74610, 73772, 73773, 73776, 73777, 73775, + 73774, 73778, 73780, 73781, 73782, 73784, 73788, 73789, 73787, 73785, + 73786, 73791, 73790, 73783, 73779, 73737, 73792, 73793, 74883, 73795, + 74884, 74885, 74886, 73796, 73797, 73798, 73799, 73800, 73794, 73801, + 73804, 73803, 73802, 73805, 74887, 73806, 73807, 73808, 73809, 73810, + 73811, 73812, 73813, 73814, 73815, 73816, 73817, 73818, 73819, 73820, + 73821, 73822, 73823, 73825, 73826, 73829, 73830, 73831, 73828, 73836, + 74611, 73837, 73833, 73835, 73827, 73832, 73834, 73824, 74889, 74612, + 73839, 73840, 73841, 74888, 73838, 73842, 73844, 74891, 74890, 73845, + 73846, 74892, 73847, 73848, 73849, 74613, 73843, 73850, 73852, 73853, + 73851, 73854, 73855, 74614, 73856, 73857, 74894, 74895, 74893, 74896, + 74897, 74900, 74901, 74902, 74899, 74908, 74909, 74907, 74906, 74911, + 74912, 74910, 74913, 74914, 74915, 74916, 74920, 74919, 74898, 74905, + 74903, 74904, 74917, 74918, 73858, 73860, 73861, 73862, 73863, 73864, + 73865, 73859, 73866, 73868, 73867, 73869, 73873, 73874, 73870, 73871, + 74922, 74921, 73872, 73875, 73879, 73883, 73884, 73880, 73881, 73882, + 73885, 73887, 74923, 73886, 73888, 74924, 73889, 74927, 74930, 74931, + 74925, 74928, 74929, 74932, 74926, 73890, 73891, 73892, 73893, 73895, + 73896, 73900, 73901, 73902, 73903, 73904, 73905, 73906, 74616, 74933, + 73907, 73908, 73899, 73897, 73898, 74615, 73894, 73877, 73876, 73878, + 73909, 73911, 73912, 73914, 73913, 73916, 74617, 73917, 74618, 73918, + 73915, 74934, 73920, 73919, 73921, 73922, 73924, 73925, 74935, 74936, + 74937, 73926, 73923, 73929, 73930, 73927, 73928, 74938, 74939, 73932, + 74941, 74940, 73931, 73933, 73934, 73935, 73936, 73937, 74942, 73938, + 73939, 73941, 73940, 73943, 73942, 73945, 73944, 73946, 73947, 74943, + 73948, 73949, 74944, 74945, 74946, 73950, 74947, 73951, 74948, 74949, + 74950, 73952, 73953, 73957, 73958, 73959, 74951, 73955, 73956, 73960, + 73962, 73963, 73964, 73961, 74952, 73954, 73965, 73966, 74953, 73967, + 73968, 73969, 73970, 73971, 73972, 73974, 73975, 73978, 73977, 73976, + 73910, 73979, 73980, 73981, 73973, 73982, 73983, 74954, 74619, 73984, + 73985, 73986, 73987, 73988, 73990, 73989, 73994, 73995, 73998, 73996, + 73997, 73999, 73992, 73993, 74001, 74955, 74004, 74003, 74005, 74002, + 74000, 73991, 74620, 74006, 74008, 74009, 74010, 74956, 74012, 74011, + 74013, 74014, 74957, 74015, 74016, 74017, 74019, 74020, 74021, 74024, + 74023, 74022, 74007, 74018, 74025, 74026, 74958, 74027, 74028, 74029, + 74030, 74959, 74031, 74033, 74036, 74035, 74032, 74034, 74037, 74038, + 74039, 74040, 74043, 74044, 74042, 74041, 74045, 74046, 74621, 74047, + 74050, 74052, 74051, 74053, 74054, 74058, 74057, 74055, 74056, 74059, + 74060, 74061, 74062, 74064, 74065, 74063, 74066, 74067, 74048, 74070, + 74049, 74068, 74069, 74071, 74072, 74073, 74074, 74622, 74075, 74623, + 74077, 74076, 74078, 74079, 74960, 74080, 74081, 74082, 74085, 74086, + 74084, 74083, 74087, 74624, 74090, 74089, 74088, 74091, 74092, 74625, + 74093, 74094, 74096, 74097, 74961, 74095, 74099, 74627, 74098, 74100, + 74101, 74103, 74102, 74104, 74105, 74115, 74629, 74114, 74112, 74113, + 74110, 74111, 74117, 74116, 74118, 74630, 74119, 74962, 74963, 74631, + 74122, 74123, 74120, 74121, 74626, 74107, 74106, 74628, 74108, 74109, + 74124, 74125, 74126, 74131, 74132, 74128, 74129, 74130, 74133, 74134, + 74135, 74136, 74137, 983267, 74138, 74139, 74140, 74141, 74142, 74607, + 74127, 74144, 74146, 74147, 74145, 74152, 74153, 74150, 74151, 74148, + 74149, 74154, 74155, 74157, 74158, 74163, 74164, 74165, 74160, 74161, + 74156, 74159, 74162, 74143, 74166, 74167, 74168, 74169, 74170, 74171, + 74172, 74175, 74173, 74174, 74176, 74177, 74182, 74183, 74180, 74181, + 74632, 74186, 74184, 74185, 74188, 74190, 74189, 74187, 74194, 74195, + 74193, 74191, 74192, 74196, 74197, 74198, 74199, 74200, 74201, 74202, + 74205, 74206, 74207, 74208, 74204, 74209, 74211, 74210, 74212, 74213, + 74215, 74214, 74216, 74218, 74217, 74964, 74178, 74179, 74203, 74219, + 74220, 74223, 74224, 74221, 74222, 74966, 74967, 74974, 74973, 74972, + 74969, 74970, 74971, 74975, 74968, 74965, 74977, 74976, 74980, 74981, + 74982, 74984, 74985, 74978, 74979, 74983, 74986, 74987, 74988, 74989, + 74990, 74991, 74993, 74997, 74996, 74998, 74995, 74994, 74992, 74999, + 75000, 75003, 75004, 75005, 75006, 75001, 75002, 75009, 75015, 75016, + 75019, 75017, 75018, 75013, 75012, 75010, 75011, 75014, 75021, 75030, + 75029, 75027, 75024, 75025, 75028, 75022, 75026, 75023, 75008, 75020, + 75031, 75032, 75007, 74226, 74227, 74228, 74229, 74230, 74225, 74231, + 74233, 74234, 74232, 74235, 74237, 74258, 74259, 75033, 74261, 74633, + 74260, 74240, 74634, 74241, 74243, 75035, 74246, 74247, 74245, 74248, + 74249, 74250, 74251, 75036, 75037, 74254, 74255, 74635, 74256, 75038, + 74242, 74252, 74253, 75034, 74238, 74239, 74244, 74257, 74263, 74265, + 74264, 74266, 74269, 74270, 74271, 74236, 74262, 74267, 74268, 74272, + 74273, 74274, 74278, 74279, 74275, 74276, 74277, 74280, 74281, 74636, + 74282, 75039, 74283, 74284, 74290, 74294, 74295, 75041, 75040, 74292, + 74293, 74291, 74296, 74297, 74298, 74299, 74300, 74637, 74301, 74286, + 74287, 74285, 74289, 74288, 74302, 74304, 74307, 74305, 74306, 74308, + 74310, 74309, 74311, 74303, 74638, 74312, 74314, 74313, 74315, 74316, + 74319, 74321, 74320, 74639, 74322, 74324, 74325, 74323, 74642, 75043, + 74326, 75044, 75046, 75047, 74327, 75048, 74329, 74328, 75049, 74330, + 74332, 74333, 74331, 75050, 75051, 74334, 75052, 74335, 75042, 74641, + 75045, 74640, 74317, 74336, 74318, 74337, 74338, 983266, 983265, 74643, + 74339, 74347, 74348, 74342, 74343, 74341, 74344, 74340, 74346, 74345, + 74349, 74355, 74354, 74350, 74352, 74358, 74359, 74353, 74357, 74351, + 74356, 74360, 74361, 74362, 74363, 74364, 74365, 74366, 74644, 74367, + 74375, 74376, 74368, 74369, 74373, 74374, 74370, 74371, 74372, 74377, + 74378, 74379, 74380, 74381, 74382, 74645, 74383, 74384, 74385, 74386, + 74387, 74389, 74408, 75053, 74388, 74646, 74395, 74394, 75055, 74400, + 74399, 75056, 74401, 74406, 74402, 74403, 74404, 74405, 74391, 74392, + 74396, 74398, 75054, 74397, 74393, 74390, 74407, 74409, 74410, 74411, + 74412, 74413, 74414, 74421, 74422, 74419, 74417, 74420, 74416, 74418, + 74415, 74423, 75057, 74424, 74425, 74426, 75058, 74428, 74429, 75059, + 75060, 75061, 74427, 74432, 74434, 74433, 74430, 74431, 74435, 74437, + 74436, 74438, 74441, 74440, 74444, 74445, 74446, 74448, 74447, 74443, + 74449, 74442, 74439, 74451, 74453, 74452, 74450, 74454, 74455, 74456, + 74457, 75063, 75062, 74458, 74459, 75064, 74460, 74461, 74462, 74463, + 74465, 74464, 74466, 74468, 74469, 74471, 74472, 74473, 74474, 74467, + 74470, 74475, 74477, 74478, 74479, 74476, 74480, 74481, 74482, 74483, + 74488, 74486, 74487, 74485, 74489, 74484, 74490, 75065, 74491, 74494, + 74497, 74499, 74500, 74498, 74495, 74647, 74496, 74501, 74504, 75066, + 75067, 74505, 74506, 74502, 74503, 74492, 74493, 74507, 74510, 74512, + 74511, 74649, 74509, 74508, 74515, 74516, 74522, 74523, 74519, 74520, + 74517, 74518, 74521, 74524, 74534, 74535, 74525, 74526, 74648, 74527, + 74528, 74529, 74531, 74532, 74533, 74530, 74536, 74538, 74537, 74539, + 75068, 74540, 74541, 74542, 74545, 74546, 74547, 75069, 74544, 74543, + 74549, 74550, 74551, 74552, 74553, 75070, 74555, 74556, 74558, 74557, + 74559, 74560, 74562, 74564, 74563, 75072, 74566, 75071, 74570, 74569, + 74572, 74574, 74573, 74554, 74567, 74571, 74565, 74561, 74568, 74575, + 74576, 74548, 74577, 74581, 74579, 74580, 74578, 74584, 74583, 74582, + 74586, 74587, 74588, 74585, 74513, 74514, 74589, 74591, 74590, 74593, + 75073, 74592, 74595, 74598, 74599, 74596, 74601, 74597, 74600, 74602, + 75074, 74603, 75075, 74604, 74605, 74606, 74594, 9982, 129380, 11232, + 129473, 8911, 8910, 10160, 9130, 129356, 128177, 164, 127835, 10081, + 128707, 127854, 129362, 129385, 67594, 67595, 67596, 67597, 67598, 67599, + 67600, 67601, 67602, 67603, 67604, 67605, 67606, 67607, 67608, 67609, + 67610, 67611, 67612, 67613, 67614, 67615, 67616, 67617, 67618, 67619, + 67620, 67621, 67622, 67623, 67624, 67625, 67626, 67627, 67628, 67629, + 67630, 67631, 67632, 67633, 67634, 67635, 67636, 67637, 67589, 67592, + 67644, 67647, 67639, 67640, 67584, 67585, 67586, 67587, 67588, 77712, + 77713, 77714, 77715, 77716, 77717, 77718, 77719, 77722, 77723, 77720, + 77721, 77724, 77725, 77726, 77727, 77728, 77729, 77730, 77731, 77732, + 77733, 77734, 77735, 77736, 77737, 77738, 77739, 77740, 77741, 77742, + 77743, 77744, 77745, 77746, 77747, 77748, 77749, 77750, 77751, 77752, + 77753, 77754, 77755, 77756, 77757, 77758, 77773, 77774, 77768, 77769, + 77770, 77771, 77772, 77775, 77776, 77777, 77788, 77789, 77790, 77791, + 77792, 77793, 77794, 77795, 77796, 77759, 77760, 77761, 77762, 77763, + 77764, 77765, 77766, 77767, 77778, 77779, 77780, 77781, 77782, 77783, + 77784, 77785, 77786, 77787, 77797, 77798, 77799, 77800, 77801, 77802, + 77803, 77804, 77805, 77806, 77807, 77808, 77809, 77810, 1126, 1033, 1300, + 42574, 1034, 1055, 1190, 1316, 1136, 1146, 42564, 42592, 42580, 1296, + 1302, 1058, 42634, 1196, 1035, 42640, 42638, 1062, 42642, 7305, 42636, + 1059, 1266, 1264, 1262, 1144, 1028, 1040, 1234, 1232, 1212, 1214, 1248, + 1192, 1310, 1256, 1258, 1184, 42602, 1130, 42572, 42586, 1030, 1041, + 1063, 1268, 1206, 1208, 42584, 42650, 42630, 1026, 42568, 42604, 42648, + 1029, 42562, 1322, 42632, 1039, 42626, 1324, 42624, 1044, 1069, 1051, + 1312, 1326, 1221, 1298, 1053, 1314, 1186, 1320, 1225, 1223, 1056, 1166, + 1260, 1057, 1194, 1052, 1229, 1060, 1043, 1172, 1168, 1270, 1170, 1274, + 1027, 1061, 1202, 1276, 1278, 1066, 42644, 1048, 1252, 1037, 1250, 1045, + 1024, 1238, 1025, 42588, 1128, 1132, 42578, 42582, 1124, 42566, 1140, + 1142, 1050, 1178, 1219, 1180, 1182, 1286, 1282, 1280, 1288, 1290, 1292, + 1294, 1284, 1152, 1227, 1036, 1134, 42600, 42570, 1054, 1120, 1148, 1254, + 1150, 1240, 1242, 1049, 1162, 1038, 1210, 1318, 1065, 42646, 1064, 42596, + 42598, 1068, 42594, 1198, 1200, 1164, 1071, 1304, 1122, 1067, 1272, + 42576, 1031, 42590, 1070, 1047, 1246, 1176, 42560, 1046, 1244, 1174, + 1217, 42628, 1138, 1032, 1042, 1308, 1306, 1236, 1204, 1188, 42622, + 42606, 1216, 7467, 42623, 1072, 1235, 1233, 1213, 1215, 1249, 1193, 1311, + 1257, 1259, 1185, 42603, 1131, 42573, 42587, 1110, 1073, 1095, 1269, + 1207, 1209, 42585, 42651, 42631, 1106, 42569, 42605, 42649, 1109, 42563, + 1323, 42633, 1119, 42627, 1325, 42625, 1076, 1101, 1083, 1313, 1327, + 1222, 1299, 1085, 1315, 1187, 1321, 1226, 1224, 1088, 1167, 1261, 1089, + 1195, 1084, 1230, 1092, 1075, 1173, 1169, 1271, 1171, 1275, 1107, 1093, + 1203, 1277, 1279, 1098, 42645, 1080, 1253, 1117, 1251, 1077, 1104, 1239, + 1105, 42589, 1129, 1133, 42579, 42583, 1125, 42567, 1141, 1143, 1082, + 1179, 1220, 1181, 1183, 1287, 1283, 1281, 1289, 1291, 1293, 1295, 1285, + 1153, 1228, 1116, 1135, 1127, 7297, 1113, 1301, 42601, 42571, 42575, + 7298, 1114, 1086, 1121, 1149, 1255, 1151, 1087, 1191, 1317, 1231, 1137, + 42565, 42593, 42581, 1297, 1147, 7296, 1303, 1241, 1243, 1081, 1163, + 1118, 1211, 1319, 1097, 42647, 1096, 42597, 42599, 1100, 42595, 1199, + 1201, 1165, 7302, 7303, 7300, 1090, 42635, 1197, 1115, 42641, 42639, + 1094, 7301, 42643, 7306, 42637, 1091, 1267, 1265, 1263, 1145, 1108, 7304, + 7299, 1309, 1103, 1305, 1123, 1099, 1273, 42577, 1111, 42591, 1102, 1079, + 1247, 1177, 42561, 1078, 1245, 1175, 1218, 42629, 1139, 1112, 1074, 1307, + 1237, 1205, 1189, 122984, 122962, 122986, 122985, 122965, 122976, 122971, + 122974, 122964, 122983, 122977, 122981, 122982, 122980, 122978, 122967, + 122968, 122969, 122966, 122979, 122973, 122963, 122970, 122961, 122972, + 122975, 1154, 9005, 127744, 983201, 983188, 983172, 8224, 11830, 11831, + 128481, 128131, 127841, 128374, 9619, 11843, 128168, 10143, 65101, 65097, + 8504, 983081, 983084, 983086, 983088, 983090, 983162, 9110, 9192, 127795, + 128475, 8451, 176, 8457, 983120, 128666, 8796, 983119, 9161, 9159, 9153, + 9156, 9162, 9160, 9154, 9157, 9164, 9151, 9163, 9150, 9158, 9152, 9155, + 117829, 117828, 127980, 127962, 9739, 66589, 66591, 66596, 66597, 66587, + 66585, 66594, 66595, 66593, 66599, 66562, 66563, 66564, 66565, 66561, + 66560, 66568, 66569, 66570, 66571, 66567, 66566, 66598, 66573, 66588, + 66579, 66592, 66590, 66581, 66578, 66580, 66582, 66577, 66586, 66575, + 66584, 66583, 66574, 66572, 66576, 66629, 66631, 66636, 66637, 66627, + 66625, 66634, 66635, 66633, 66639, 66602, 66603, 66604, 66605, 66601, + 66600, 66608, 66609, 66610, 66611, 66607, 66606, 66638, 66613, 66628, + 66619, 66632, 66630, 66621, 66618, 66620, 66622, 66617, 66626, 66615, + 66624, 66623, 66614, 66612, 66616, 127964, 127965, 128421, 128468, 2388, + 2416, 43258, 2387, 43257, 72448, 72449, 43259, 2309, 2310, 2320, 2324, + 2421, 43262, 2330, 2418, 2317, 2321, 2331, 2396, 2430, 2338, 2337, 2343, + 2342, 2429, 2394, 2328, 2427, 2327, 2426, 2361, 2350, 2424, 2308, 2318, + 2322, 2358, 2359, 2360, 2431, 2349, 2348, 2393, 2326, 2325, 2397, 2353, + 2352, 2323, 2420, 2419, 2399, 2351, 2313, 2314, 2423, 2422, 2345, 2339, + 2329, 2334, 2344, 2333, 2428, 2332, 2356, 2355, 2354, 2336, 2335, 2341, + 2340, 2315, 2400, 2316, 2401, 2357, 2311, 2312, 2347, 2346, 2425, 2395, + 2398, 2392, 2319, 983644, 983643, 983646, 983647, 983649, 983648, 983642, + 983645, 72450, 72451, 72452, 72453, 2305, 43255, 43251, 43254, 43253, + 72455, 72454, 72456, 43250, 43260, 2304, 72457, 2364, 2365, 2306, 43252, + 43256, 2417, 2381, 2307, 2386, 2385, 2366, 2376, 2380, 2383, 43263, 2389, + 2373, 2377, 2379, 2363, 2362, 2382, 2369, 2370, 2391, 2390, 2374, 2378, + 2371, 2372, 2402, 2403, 2367, 2368, 2375, 2405, 2404, 2411, 2410, 2413, + 2412, 2409, 2408, 2406, 2415, 2407, 2414, 43261, 2384, 983089, 983161, + 983087, 983085, 983083, 129487, 129420, 11033, 11032, 11030, 11031, 128924, 128160, 8900, 8960, 168, 117827, 9856, 9857, 9858, 9859, 9860, 9861, 128754, 53, 127238, 9356, 52, 127237, 9355, 57, 127242, 9360, 49, 127234, 9352, 55, 127240, 9358, 54, 127239, 9357, 51, 127236, 9354, 50, @@ -10290,581 +10271,182 @@ static const unsigned int dawg_pos_to_codepoint[] = { 10128, 10127, 10124, 10123, 127244, 10130, 10122, 10129, 10111, 10106, 10105, 10108, 10107, 10104, 10103, 10110, 10102, 10109, 10121, 10116, 10115, 10118, 10117, 10114, 10113, 127243, 10120, 10112, 10119, 9107, - 127919, 128549, 128542, 9933, 9090, 129400, 72004, 72021, 72020, 72023, - 72022, 72019, 72018, 72016, 72025, 72017, 72024, 71964, 71958, 71963, - 71974, 71973, 71936, 71937, 71961, 71960, 71966, 71965, 71940, 71941, - 71938, 71939, 71982, 71976, 71952, 71962, 71957, 71967, 71978, 71979, - 71980, 71971, 71970, 71954, 71953, 71951, 71950, 71949, 71948, 71969, - 71968, 71981, 71955, 71972, 71975, 71977, 71983, 71942, 71945, 71997, - 71996, 72003, 71995, 71984, 71991, 71987, 71988, 71985, 71986, 71989, - 71992, 71998, 72002, 72000, 72005, 72006, 71999, 72001, 8725, 247, 8903, - 129343, 8739, 9902, 129684, 128171, 128565, 12291, 8783, 9009, 128462, - 128441, 128442, 128443, 8939, 8941, 8716, 8740, 10990, 8832, 8928, 8876, - 8833, 8929, 8878, 128021, 71680, 71681, 71687, 71689, 71703, 71702, - 71708, 71707, 71723, 71716, 71701, 71700, 71706, 71705, 71684, 71685, - 71682, 71683, 71694, 71704, 71699, 71709, 71719, 71720, 71721, 71713, - 71712, 71696, 71695, 71693, 71692, 71698, 71697, 71691, 71690, 71711, - 71710, 71722, 71717, 71714, 71718, 71715, 71686, 71688, 71729, 71730, - 71724, 71732, 71734, 71727, 71728, 71725, 71726, 71731, 71733, 71739, - 71738, 71735, 71737, 71736, 128054, 128044, 36, 127025, 127026, 127027, - 127028, 127029, 127030, 127031, 127032, 127033, 127034, 127035, 127036, - 127037, 127038, 127039, 127040, 127041, 127042, 127043, 127044, 127045, - 127046, 127047, 127048, 127049, 127050, 127051, 127052, 127053, 127054, - 127055, 127056, 127057, 127058, 127059, 127060, 127061, 127062, 127063, - 127064, 127065, 127066, 127067, 127068, 127069, 127070, 127071, 127072, - 127073, 127024, 127075, 127076, 127077, 127078, 127079, 127080, 127081, - 127082, 127083, 127084, 127085, 127086, 127087, 127088, 127089, 127090, - 127091, 127092, 127093, 127094, 127095, 127096, 127097, 127098, 127099, - 127100, 127101, 127102, 127103, 127104, 127105, 127106, 127107, 127108, - 127109, 127110, 127111, 127112, 127113, 127114, 127115, 127116, 127117, - 127118, 127119, 127120, 127121, 127122, 127123, 127074, 129743, 8363, - 8724, 8760, 8901, 729, 9676, 8284, 11850, 11034, 129765, 11795, 11784, - 10649, 11798, 9470, 9465, 9464, 9467, 9466, 9463, 9462, 9469, 9461, 9468, - 10175, 10868, 10986, 8225, 8223, 11840, 8914, 8748, 11842, 8222, 8215, - 10835, 10836, 10645, 10913, 10915, 10914, 10939, 8243, 12318, 10746, - 10830, 10831, 11849, 10988, 11844, 10940, 8913, 8912, 11005, 10987, 8915, - 9208, 10981, 8875, 10979, 8214, 11799, 10646, 733, 8252, 8263, 65100, - 10908, 10907, 11002, 11001, 10906, 10905, 8510, 8473, 8511, 8450, 8461, - 8469, 8474, 8477, 8484, 8518, 8519, 8520, 8521, 8517, 8509, 8508, 8512, - 10719, 9890, 9891, 11260, 127849, 8868, 10993, 10623, 8945, 8964, 117764, - 128317, 117859, 118264, 117883, 118268, 117849, 128315, 117914, 117864, - 10728, 10729, 117875, 117879, 129288, 129290, 129289, 129291, 8595, 8629, - 8671, 129035, 129031, 129179, 129043, 129027, 129047, 8626, 8627, 10504, - 8693, 129975, 8615, 10515, 11796, 11015, 129203, 11147, 11107, 11139, - 11123, 129067, 11168, 11169, 129063, 129059, 129075, 129071, 11133, - 11085, 11117, 11143, 129171, 10507, 8609, 11247, 8681, 129175, 8623, - 8659, 8675, 129079, 10597, 10607, 10593, 10585, 8643, 10589, 10581, 8642, - 129091, 129095, 129087, 10225, 128623, 129107, 129083, 8650, 128687, - 128330, 128682, 129444, 8367, 10139, 128009, 128050, 128042, 129656, - 128167, 129316, 129345, 9946, 128087, 113784, 113788, 113785, 113783, - 113782, 113786, 113787, 113795, 113798, 113793, 113800, 113781, 113794, - 113792, 113799, 113797, 113796, 113776, 113808, 113817, 113811, 113814, - 113809, 113816, 113779, 113810, 113815, 113813, 113812, 113778, 113777, - 113780, 113729, 113733, 113672, 113677, 113683, 113735, 113739, 113746, - 113668, 113678, 113674, 113726, 113691, 113699, 113700, 113695, 113709, - 113712, 113713, 113705, 113711, 113669, 113725, 113679, 113684, 113670, - 113743, 113749, 113687, 113689, 113693, 113707, 113697, 113703, 113690, - 113694, 113708, 113698, 113704, 113764, 113763, 113762, 113761, 113732, - 113753, 113731, 113755, 113754, 113666, 113766, 113765, 113676, 113675, - 113741, 113750, 113680, 113688, 113692, 113696, 113710, 113727, 113728, - 113716, 113717, 113714, 113715, 113701, 113702, 113724, 113723, 113706, - 113742, 113740, 113767, 113769, 113730, 113768, 113682, 113685, 113752, - 113737, 113667, 113719, 113718, 113681, 113745, 113748, 113751, 113738, - 113673, 113770, 113720, 113757, 113760, 113722, 113759, 113756, 113721, - 113758, 113665, 113747, 113664, 113686, 113734, 113736, 113744, 113671, - 113821, 113822, 113823, 113820, 129375, 129414, 129516, 128192, 983082, - 128066, 127805, 127806, 129467, 9793, 127758, 127759, 127757, 9178, 9841, - 129413, 11790, 77830, 77831, 77832, 77828, 77829, 77824, 77825, 77826, - 77827, 77833, 77834, 77835, 77840, 77841, 77844, 77845, 77836, 77837, - 77838, 77839, 77842, 77843, 77846, 77847, 77869, 77870, 77872, 77873, - 77874, 77875, 77877, 77878, 77871, 77876, 77879, 77880, 77881, 77882, - 77860, 77861, 77858, 77859, 77862, 77863, 77864, 77865, 77866, 77867, - 77868, 77903, 77848, 77849, 77850, 77851, 77852, 77853, 77854, 77855, - 77856, 77857, 77883, 77884, 77885, 77886, 77887, 77888, 77889, 77890, - 77891, 77892, 77893, 77894, 77895, 77896, 77897, 77898, 77899, 77900, - 77901, 77902, 78867, 78868, 78869, 78861, 78862, 78863, 78864, 78865, - 78866, 78870, 78871, 78892, 78893, 78894, 78872, 78873, 78874, 78875, - 78876, 78877, 78878, 78879, 78880, 78881, 78882, 78883, 78884, 78885, - 78886, 78887, 78888, 78889, 78890, 78891, 78910, 78908, 78903, 77908, - 77909, 77904, 77905, 77906, 77907, 77910, 77911, 77912, 77913, 77915, - 77916, 77917, 77918, 77914, 77919, 77920, 77921, 77922, 77923, 77924, - 77925, 77926, 77927, 77928, 77929, 77930, 77931, 77932, 77933, 77934, - 77935, 77936, 77937, 77938, 77939, 77940, 77941, 77949, 77950, 77942, - 77943, 77944, 77945, 77946, 77947, 77948, 77951, 77974, 77975, 77978, - 77979, 77973, 77976, 77977, 77980, 77981, 77982, 77983, 77984, 77991, - 77992, 77994, 77995, 77985, 77986, 77987, 77988, 77989, 77990, 77993, - 77996, 77997, 77998, 77999, 78000, 78001, 78002, 78003, 78004, 78005, - 78006, 78008, 78009, 78011, 78012, 78007, 78010, 78013, 78014, 78015, - 78016, 78017, 78025, 78026, 78027, 78028, 78029, 78030, 78031, 78032, - 78033, 78018, 78019, 78020, 78021, 78022, 78023, 78024, 77969, 77970, - 77962, 77963, 77964, 77965, 77966, 77967, 77968, 77971, 77972, 77952, - 77953, 77954, 77955, 77956, 77957, 77958, 77959, 77960, 77961, 78041, - 78042, 78043, 78044, 78034, 78035, 78036, 78037, 78038, 78039, 78040, - 78057, 78058, 78066, 78067, 78059, 78060, 78061, 78062, 78063, 78064, - 78065, 78068, 78073, 78074, 78069, 78070, 78071, 78072, 78075, 78076, - 78077, 78051, 78052, 78053, 78054, 78045, 78046, 78047, 78048, 78049, - 78050, 78055, 78056, 78911, 78909, 78904, 78078, 78079, 78080, 78081, - 78082, 78083, 78084, 78085, 78086, 78087, 78091, 78092, 78088, 78089, - 78090, 78093, 78094, 78095, 78096, 78097, 78098, 78111, 78112, 78118, - 78119, 78120, 78121, 78110, 78113, 78114, 78115, 78116, 78117, 78122, - 78128, 78129, 78130, 78131, 78132, 78133, 78123, 78124, 78125, 78126, - 78127, 78134, 78135, 78137, 78138, 78139, 78140, 78136, 78141, 78142, - 78100, 78101, 78099, 78102, 78103, 78104, 78105, 78106, 78107, 78108, - 78109, 78913, 78150, 78151, 78152, 78148, 78149, 78143, 78144, 78145, - 78146, 78147, 78153, 78154, 78156, 78157, 78155, 78158, 78159, 78160, - 78161, 78162, 78163, 78164, 78165, 78184, 78185, 78186, 78187, 78178, - 78179, 78180, 78181, 78182, 78183, 78188, 78189, 78193, 78194, 78196, - 78197, 78190, 78191, 78192, 78195, 78198, 78199, 78200, 78201, 78166, - 78167, 78173, 78174, 78168, 78169, 78170, 78171, 78172, 78175, 78176, - 78177, 78202, 78203, 78204, 78205, 78206, 78212, 78213, 78207, 78208, - 78209, 78210, 78211, 78214, 78215, 78914, 78916, 78897, 78220, 78221, - 78225, 78226, 78216, 78217, 78218, 78219, 78222, 78223, 78224, 78227, - 78228, 78229, 78230, 78231, 78232, 78233, 78234, 78907, 78901, 78899, - 78906, 78900, 78898, 78905, 78244, 78245, 78249, 78250, 78243, 78246, - 78247, 78248, 78251, 78252, 78915, 78253, 78254, 78255, 78257, 78258, - 78256, 78259, 78260, 78261, 78262, 78263, 78264, 78268, 78269, 78270, - 78271, 78272, 78273, 78274, 78275, 78276, 78265, 78266, 78279, 78280, - 78281, 78282, 78283, 78284, 78267, 78277, 78278, 78285, 78286, 78289, - 78290, 78292, 78293, 78297, 78298, 78287, 78288, 78291, 78294, 78295, - 78296, 78299, 78304, 78305, 78306, 78301, 78302, 78300, 78303, 78307, - 78308, 78309, 78310, 78311, 78312, 78313, 78314, 78315, 78316, 78317, - 78318, 78933, 78928, 78920, 78924, 78932, 78926, 78921, 78929, 78925, - 78923, 78931, 78919, 78927, 78922, 78930, 78912, 78336, 78337, 78338, - 78328, 78329, 78330, 78331, 78332, 78333, 78334, 78335, 78339, 78354, - 78355, 78356, 78357, 78358, 78359, 78361, 78362, 78351, 78352, 78353, - 78360, 78363, 78364, 78345, 78346, 78340, 78341, 78342, 78343, 78344, - 78347, 78348, 78349, 78350, 78365, 78366, 78367, 78319, 78320, 78321, - 78322, 78323, 78324, 78325, 78326, 78327, 78372, 78373, 78368, 78369, - 78370, 78371, 78374, 78375, 78376, 78377, 78385, 78386, 78378, 78379, - 78380, 78381, 78382, 78383, 78384, 78387, 78388, 78389, 78399, 78400, - 78401, 78402, 78409, 78410, 78403, 78404, 78405, 78406, 78407, 78408, - 78411, 78414, 78415, 78412, 78413, 78390, 78391, 78392, 78393, 78394, - 78395, 78396, 78397, 78398, 78423, 78424, 78425, 78426, 78427, 78428, - 78429, 78416, 78417, 78421, 78422, 78418, 78419, 78420, 78430, 78431, - 78432, 78433, 78434, 78435, 78436, 78445, 78446, 78437, 78438, 78439, - 78440, 78441, 78442, 78443, 78444, 78447, 78448, 78452, 78453, 78454, - 78455, 78459, 78460, 78449, 78450, 78451, 78456, 78457, 78458, 78469, - 78470, 78471, 78472, 78473, 78461, 78462, 78465, 78466, 78463, 78464, - 78467, 78468, 78474, 78475, 78476, 78487, 78488, 78489, 78490, 78477, - 78478, 78479, 78480, 78481, 78482, 78483, 78484, 78485, 78486, 78902, - 78491, 78492, 78494, 78495, 78493, 78496, 78497, 78498, 78499, 78500, - 78501, 78502, 78503, 78514, 78515, 78516, 78512, 78513, 78511, 78517, - 78518, 78519, 78520, 78521, 78522, 78523, 78524, 78530, 78531, 78525, - 78526, 78527, 78528, 78529, 78532, 78533, 78534, 78535, 78536, 78537, - 78538, 78539, 78540, 78541, 78542, 78543, 78544, 78546, 78547, 78551, - 78552, 78545, 78548, 78549, 78550, 78553, 78554, 78555, 78560, 78561, - 78562, 78565, 78566, 78556, 78557, 78558, 78559, 78563, 78564, 78567, - 78568, 78575, 78576, 78577, 78569, 78570, 78571, 78572, 78573, 78574, - 78578, 78579, 78580, 78586, 78587, 78581, 78582, 78583, 78584, 78585, - 78588, 78589, 78590, 78591, 78592, 78593, 78594, 78595, 78596, 78597, - 78598, 78601, 78602, 78606, 78607, 78608, 78609, 78610, 78611, 78599, - 78600, 78603, 78604, 78605, 78613, 78614, 78619, 78620, 78612, 78615, - 78616, 78617, 78618, 78621, 78622, 78623, 78636, 78637, 78638, 78639, - 78634, 78635, 78640, 78641, 78642, 78624, 78625, 78626, 78627, 78628, - 78629, 78630, 78631, 78632, 78633, 78917, 78648, 78649, 78650, 78643, - 78644, 78645, 78646, 78647, 78651, 78652, 78653, 78667, 78668, 78674, - 78675, 78664, 78665, 78666, 78669, 78670, 78671, 78672, 78673, 78678, - 78679, 78676, 78677, 78680, 78681, 78682, 78683, 78684, 78685, 78686, - 78687, 78688, 78689, 78654, 78655, 78656, 78657, 78658, 78659, 78660, - 78661, 78662, 78663, 78706, 78707, 78708, 78690, 78691, 78692, 78693, - 78694, 78695, 78696, 78697, 78698, 78699, 78700, 78701, 78702, 78703, - 78704, 78705, 78709, 78710, 78712, 78713, 78714, 78715, 78895, 78716, - 78717, 78718, 78711, 78719, 78720, 78721, 78722, 78723, 78724, 78725, - 78726, 78727, 78728, 78729, 78730, 78731, 78732, 78733, 78734, 78735, - 78736, 78737, 78738, 78741, 78742, 78747, 78748, 78749, 78750, 78739, - 78740, 78743, 78744, 78745, 78746, 78751, 78752, 78753, 78754, 78756, - 78757, 78761, 78762, 78755, 78758, 78759, 78760, 78763, 78764, 78765, - 78766, 78896, 78769, 78770, 78776, 78777, 78767, 78768, 78771, 78772, - 78773, 78774, 78775, 78778, 78779, 78783, 78784, 78787, 78788, 78789, - 78790, 78780, 78781, 78782, 78785, 78786, 78791, 78796, 78797, 78792, - 78793, 78794, 78795, 78798, 78918, 78802, 78803, 78804, 78806, 78807, - 78809, 78810, 78799, 78800, 78801, 78805, 78808, 78811, 78812, 78813, - 78814, 78815, 78816, 78817, 78818, 78819, 78821, 78822, 78823, 78824, - 78825, 78826, 78827, 78828, 78829, 78830, 78831, 78832, 78820, 78833, - 78834, 78835, 78836, 78842, 78843, 78844, 78845, 78846, 78847, 78848, - 78849, 78850, 78851, 78852, 78853, 78854, 78855, 78856, 78857, 78858, - 78859, 78860, 78837, 78838, 78839, 78840, 78841, 78235, 78236, 78237, - 78238, 78239, 78240, 78241, 78242, 78504, 78505, 78506, 78507, 78508, - 78509, 78510, 78944, 78945, 78946, 78947, 78948, 78949, 78950, 78951, - 78952, 78953, 78954, 78955, 78956, 78957, 78958, 78959, 78960, 78961, - 78962, 78963, 78964, 78965, 78966, 78967, 78968, 78969, 78970, 78971, - 78972, 78973, 78974, 78975, 78976, 78977, 78978, 78979, 78980, 78981, - 78982, 78983, 78984, 78985, 78986, 78987, 78988, 78989, 78990, 78991, - 78992, 78993, 78994, 78995, 78996, 78997, 78998, 78999, 79000, 79001, - 79002, 79003, 79004, 79005, 79006, 79007, 79008, 79009, 79010, 79011, - 79012, 79013, 79014, 79015, 79016, 79017, 79018, 79019, 79020, 79021, - 79022, 79023, 79024, 79025, 79026, 79027, 79028, 79029, 79030, 79031, - 79032, 79033, 79034, 79035, 79036, 79037, 79038, 79039, 79040, 79041, - 79042, 79043, 79044, 79045, 79046, 79047, 79048, 79049, 79050, 79051, - 79052, 79053, 79054, 79055, 79056, 79057, 79058, 79059, 79060, 79061, - 79062, 79063, 79064, 79065, 79066, 79067, 79068, 79069, 79070, 79071, - 79072, 79073, 79074, 79075, 79076, 79077, 79078, 79079, 79080, 79081, - 79082, 79083, 79084, 79085, 79086, 79087, 79088, 79089, 79090, 79091, - 79092, 79093, 79094, 79095, 79096, 79097, 79098, 79099, 79100, 79101, - 79102, 79103, 79104, 79105, 79106, 79107, 79108, 79109, 79110, 79111, - 79112, 79113, 79114, 79115, 79116, 79117, 79118, 79119, 79120, 79121, - 79122, 79123, 79124, 79125, 79126, 79127, 79128, 79129, 79130, 79131, - 79132, 79133, 79134, 79135, 79136, 79137, 79138, 79139, 79140, 79141, - 79142, 79143, 79144, 79145, 79146, 79147, 79148, 79149, 79150, 79151, - 79152, 79153, 79154, 79155, 79156, 79157, 79158, 79159, 79160, 79161, - 79162, 79163, 79164, 79165, 79166, 79167, 79168, 79169, 79170, 79171, - 79172, 79173, 79174, 79175, 79176, 79177, 79178, 79179, 79180, 79181, - 79182, 79183, 79184, 79185, 79186, 79187, 79188, 79189, 79190, 79191, - 79192, 79193, 79194, 79195, 79196, 79197, 79198, 79199, 79200, 79201, - 79202, 79203, 79204, 79205, 79206, 79207, 79208, 79209, 79210, 79211, - 79212, 79213, 79214, 79215, 79216, 79217, 79218, 79219, 79220, 79221, - 79222, 79223, 79224, 79225, 79226, 79227, 79228, 79229, 79230, 79231, - 79232, 79233, 79234, 79235, 79236, 79237, 79238, 79239, 79240, 79241, - 79242, 79243, 79244, 79245, 79246, 79247, 79248, 79249, 79250, 79251, - 79252, 79253, 79254, 79255, 79256, 79257, 79258, 79259, 79260, 79261, - 79262, 79263, 79264, 79265, 79266, 79267, 79268, 79269, 79270, 79271, - 79272, 79273, 79274, 79275, 79276, 79277, 79278, 79279, 79280, 79281, - 79282, 79283, 79284, 79285, 79286, 79287, 79288, 79289, 79290, 79291, - 79292, 79293, 79294, 79295, 79296, 79297, 79298, 79299, 79300, 79301, - 79302, 79303, 79304, 79305, 79306, 79307, 79308, 79309, 79310, 79311, - 79312, 79313, 79314, 79315, 79316, 79317, 79318, 79319, 79320, 79321, - 79322, 79323, 79324, 79325, 79326, 79327, 79328, 79329, 79330, 79331, - 79332, 79333, 79334, 79335, 79336, 79337, 79338, 79339, 79340, 79341, - 79342, 79343, 79344, 79345, 79346, 79347, 79348, 79349, 79350, 79351, - 79352, 79353, 79354, 79355, 79356, 79357, 79358, 79359, 79360, 79361, - 79362, 79363, 79364, 79365, 79366, 79367, 79368, 79369, 79370, 79371, - 79372, 79373, 79374, 79375, 79376, 79377, 79378, 79379, 79380, 79381, - 79382, 79383, 79384, 79385, 79386, 79387, 79388, 79389, 79390, 79391, - 79392, 79393, 79394, 79395, 79396, 79397, 79398, 79399, 79400, 79401, - 79402, 79403, 79404, 79405, 79406, 79407, 79408, 79409, 79410, 79411, - 79412, 79413, 79414, 79415, 79416, 79417, 79418, 79419, 79420, 79421, - 79422, 79423, 79424, 79425, 79426, 79427, 79428, 79429, 79430, 79431, - 79432, 79433, 79434, 79435, 79436, 79437, 79438, 79439, 79440, 79441, - 79442, 79443, 79444, 79445, 79446, 79447, 79448, 79449, 79450, 79451, - 79452, 79453, 79454, 79455, 79456, 79457, 79458, 79459, 79460, 79461, - 79462, 79463, 79464, 79465, 79466, 79467, 79468, 79469, 79470, 79471, - 79472, 79473, 79474, 79475, 79476, 79477, 79478, 79479, 79480, 79481, - 79482, 79483, 79484, 79485, 79486, 79487, 79488, 79489, 79490, 79491, - 79492, 79493, 79494, 79495, 79496, 79497, 79498, 79499, 79500, 79501, - 79502, 79503, 79504, 79505, 79506, 79507, 79508, 79509, 79510, 79511, - 79512, 79513, 79514, 79515, 79516, 79517, 79518, 79519, 79520, 79521, - 79522, 79523, 79524, 79525, 79526, 79527, 79528, 79529, 79530, 79531, - 79532, 79533, 79534, 79535, 79536, 79537, 79538, 79539, 79540, 79541, - 79542, 79543, 79544, 79545, 79546, 79547, 79548, 79549, 79550, 79551, - 79552, 79553, 79554, 79555, 79556, 79557, 79558, 79559, 79560, 79561, - 79562, 79563, 79564, 79565, 79566, 79567, 79568, 79569, 79570, 79571, - 79572, 79573, 79574, 79575, 79576, 79577, 79578, 79579, 79580, 79581, - 79582, 79583, 79584, 79585, 79586, 79587, 79588, 79589, 79590, 79591, - 79592, 79593, 79594, 79595, 79596, 79597, 79598, 79599, 79600, 79601, - 79602, 79603, 79604, 79605, 79606, 79607, 79608, 79609, 79610, 79611, - 79612, 79613, 79614, 79615, 79616, 79617, 79618, 79619, 79620, 79621, - 79622, 79623, 79624, 79625, 79626, 79627, 79628, 79629, 79630, 79631, - 79632, 79633, 79634, 79635, 79636, 79637, 79638, 79639, 79640, 79641, - 79642, 79643, 79644, 79645, 79646, 79647, 79648, 79649, 79650, 79651, - 79652, 79653, 79654, 79655, 79656, 79657, 79658, 79659, 79660, 79661, - 79662, 79663, 79664, 79665, 79666, 79667, 79668, 79669, 79670, 79671, - 79672, 79673, 79674, 79675, 79676, 79677, 79678, 79679, 79680, 79681, - 79682, 79683, 79684, 79685, 79686, 79687, 79688, 79689, 79690, 79691, - 79692, 79693, 79694, 79695, 79696, 79697, 79698, 79699, 79700, 79701, - 79702, 79703, 79704, 79705, 79706, 79707, 79708, 79709, 79710, 79711, - 79712, 79713, 79714, 79715, 79716, 79717, 79718, 79719, 79720, 79721, - 79722, 79723, 79724, 79725, 79726, 79727, 79728, 79729, 79730, 79731, - 79732, 79733, 79734, 79735, 79736, 79737, 79738, 79739, 79740, 79741, - 79742, 79743, 79744, 79745, 79746, 79747, 79748, 79749, 79750, 79751, - 79752, 79753, 79754, 79755, 79756, 79757, 79758, 79759, 79760, 79761, - 79762, 79763, 79764, 79765, 79766, 79767, 79768, 79769, 79770, 79771, - 79772, 79773, 79774, 79775, 79776, 79777, 79778, 79779, 79780, 79781, - 79782, 79783, 79784, 79785, 79786, 79787, 79788, 79789, 79790, 79791, - 79792, 79793, 79794, 79795, 79796, 79797, 79798, 79799, 79800, 79801, - 79802, 79803, 79804, 79805, 79806, 79807, 79808, 79809, 79810, 79811, - 79812, 79813, 79814, 79815, 79816, 79817, 79818, 79819, 79820, 79821, - 79822, 79823, 79824, 79825, 79826, 79827, 79828, 79829, 79830, 79831, - 79832, 79833, 79834, 79835, 79836, 79837, 79838, 79839, 79840, 79841, - 79842, 79843, 79844, 79845, 79846, 79847, 79848, 79849, 79850, 79851, - 79852, 79853, 79854, 79855, 79856, 79857, 79858, 79859, 79860, 79861, - 79862, 79863, 79864, 79865, 79866, 79867, 79868, 79869, 79870, 79871, - 79872, 79873, 79874, 79875, 79876, 79877, 79878, 79879, 79880, 79881, - 79882, 79883, 79884, 79885, 79886, 79887, 79888, 79889, 79890, 79891, - 79892, 79893, 79894, 79895, 79896, 79897, 79898, 79899, 79900, 79901, - 79902, 79903, 79904, 79905, 79906, 79907, 79908, 79909, 79910, 79911, - 79912, 79913, 79914, 79915, 79916, 79917, 79918, 79919, 79920, 79921, - 79922, 79923, 79924, 79925, 79926, 79927, 79928, 79929, 79930, 79931, - 79932, 79933, 79934, 79935, 79936, 79937, 79938, 79939, 79940, 79941, - 79942, 79943, 79944, 79945, 79946, 79947, 79948, 79949, 79950, 79951, - 79952, 79953, 79954, 79955, 79956, 79957, 79958, 79959, 79960, 79961, - 79962, 79963, 79964, 79965, 79966, 79967, 79968, 79969, 79970, 79971, - 79972, 79973, 79974, 79975, 79976, 79977, 79978, 79979, 79980, 79981, - 79982, 79983, 79984, 79985, 79986, 79987, 79988, 79989, 79990, 79991, - 79992, 79993, 79994, 79995, 79996, 79997, 79998, 79999, 80000, 80001, - 80002, 80003, 80004, 80005, 80006, 80007, 80008, 80009, 80010, 80011, - 80012, 80013, 80014, 80015, 80016, 80017, 80018, 80019, 80020, 80021, - 80022, 80023, 80024, 80025, 80026, 80027, 80028, 80029, 80030, 80031, - 80032, 80033, 80034, 80035, 80036, 80037, 80038, 80039, 80040, 80041, - 80042, 80043, 80044, 80045, 80046, 80047, 80048, 80049, 80050, 80051, - 80052, 80053, 80054, 80055, 80056, 80057, 80058, 80059, 80060, 80061, - 80062, 80063, 80064, 80065, 80066, 80067, 80068, 80069, 80070, 80071, - 80072, 80073, 80074, 80075, 80076, 80077, 80078, 80079, 80080, 80081, - 80082, 80083, 80084, 80085, 80086, 80087, 80088, 80089, 80090, 80091, - 80092, 80093, 80094, 80095, 80096, 80097, 80098, 80099, 80100, 80101, - 80102, 80103, 80104, 80105, 80106, 80107, 80108, 80109, 80110, 80111, - 80112, 80113, 80114, 80115, 80116, 80117, 80118, 80119, 80120, 80121, - 80122, 80123, 80124, 80125, 80126, 80127, 80128, 80129, 80130, 80131, - 80132, 80133, 80134, 80135, 80136, 80137, 80138, 80139, 80140, 80141, - 80142, 80143, 80144, 80145, 80146, 80147, 80148, 80149, 80150, 80151, - 80152, 80153, 80154, 80155, 80156, 80157, 80158, 80159, 80160, 80161, - 80162, 80163, 80164, 80165, 80166, 80167, 80168, 80169, 80170, 80171, - 80172, 80173, 80174, 80175, 80176, 80177, 80178, 80179, 80180, 80181, - 80182, 80183, 80184, 80185, 80186, 80187, 80188, 80189, 80190, 80191, - 80192, 80193, 80194, 80195, 80196, 80197, 80198, 80199, 80200, 80201, - 80202, 80203, 80204, 80205, 80206, 80207, 80208, 80209, 80210, 80211, - 80212, 80213, 80214, 80215, 80216, 80217, 80218, 80219, 80220, 80221, - 80222, 80223, 80224, 80225, 80226, 80227, 80228, 80229, 80230, 80231, - 80232, 80233, 80234, 80235, 80236, 80237, 80238, 80239, 80240, 80241, - 80242, 80243, 80244, 80245, 80246, 80247, 80248, 80249, 80250, 80251, - 80252, 80253, 80254, 80255, 80256, 80257, 80258, 80259, 80260, 80261, - 80262, 80263, 80264, 80265, 80266, 80267, 80268, 80269, 80270, 80271, - 80272, 80273, 80274, 80275, 80276, 80277, 80278, 80279, 80280, 80281, - 80282, 80283, 80284, 80285, 80286, 80287, 80288, 80289, 80290, 80291, - 80292, 80293, 80294, 80295, 80296, 80297, 80298, 80299, 80300, 80301, - 80302, 80303, 80304, 80305, 80306, 80307, 80308, 80309, 80310, 80311, - 80312, 80313, 80314, 80315, 80316, 80317, 80318, 80319, 80320, 80321, - 80322, 80323, 80324, 80325, 80326, 80327, 80328, 80329, 80330, 80331, - 80332, 80333, 80334, 80335, 80336, 80337, 80338, 80339, 80340, 80341, - 80342, 80343, 80344, 80345, 80346, 80347, 80348, 80349, 80350, 80351, - 80352, 80353, 80354, 80355, 80356, 80357, 80358, 80359, 80360, 80361, - 80362, 80363, 80364, 80365, 80366, 80367, 80368, 80369, 80370, 80371, - 80372, 80373, 80374, 80375, 80376, 80377, 80378, 80379, 80380, 80381, - 80382, 80383, 80384, 80385, 80386, 80387, 80388, 80389, 80390, 80391, - 80392, 80393, 80394, 80395, 80396, 80397, 80398, 80399, 80400, 80401, - 80402, 80403, 80404, 80405, 80406, 80407, 80408, 80409, 80410, 80411, - 80412, 80413, 80414, 80415, 80416, 80417, 80418, 80419, 80420, 80421, - 80422, 80423, 80424, 80425, 80426, 80427, 80428, 80429, 80430, 80431, - 80432, 80433, 80434, 80435, 80436, 80437, 80438, 80439, 80440, 80441, - 80442, 80443, 80444, 80445, 80446, 80447, 80448, 80449, 80450, 80451, - 80452, 80453, 80454, 80455, 80456, 80457, 80458, 80459, 80460, 80461, - 80462, 80463, 80464, 80465, 80466, 80467, 80468, 80469, 80470, 80471, - 80472, 80473, 80474, 80475, 80476, 80477, 80478, 80479, 80480, 80481, - 80482, 80483, 80484, 80485, 80486, 80487, 80488, 80489, 80490, 80491, - 80492, 80493, 80494, 80495, 80496, 80497, 80498, 80499, 80500, 80501, - 80502, 80503, 80504, 80505, 80506, 80507, 80508, 80509, 80510, 80511, - 80512, 80513, 80514, 80515, 80516, 80517, 80518, 80519, 80520, 80521, - 80522, 80523, 80524, 80525, 80526, 80527, 80528, 80529, 80530, 80531, - 80532, 80533, 80534, 80535, 80536, 80537, 80538, 80539, 80540, 80541, - 80542, 80543, 80544, 80545, 80546, 80547, 80548, 80549, 80550, 80551, - 80552, 80553, 80554, 80555, 80556, 80557, 80558, 80559, 80560, 80561, - 80562, 80563, 80564, 80565, 80566, 80567, 80568, 80569, 80570, 80571, - 80572, 80573, 80574, 80575, 80576, 80577, 80578, 80579, 80580, 80581, - 80582, 80583, 80584, 80585, 80586, 80587, 80588, 80589, 80590, 80591, - 80592, 80593, 80594, 80595, 80596, 80597, 80598, 80599, 80600, 80601, - 80602, 80603, 80604, 80605, 80606, 80607, 80608, 80609, 80610, 80611, - 80612, 80613, 80614, 80615, 80616, 80617, 80618, 80619, 80620, 80621, - 80622, 80623, 80624, 80625, 80626, 80627, 80628, 80629, 80630, 80631, - 80632, 80633, 80634, 80635, 80636, 80637, 80638, 80639, 80640, 80641, - 80642, 80643, 80644, 80645, 80646, 80647, 80648, 80649, 80650, 80651, - 80652, 80653, 80654, 80655, 80656, 80657, 80658, 80659, 80660, 80661, - 80662, 80663, 80664, 80665, 80666, 80667, 80668, 80669, 80670, 80671, - 80672, 80673, 80674, 80675, 80676, 80677, 80678, 80679, 80680, 80681, - 80682, 80683, 80684, 80685, 80686, 80687, 80688, 80689, 80690, 80691, - 80692, 80693, 80694, 80695, 80696, 80697, 80698, 80699, 80700, 80701, - 80702, 80703, 80704, 80705, 80706, 80707, 80708, 80709, 80710, 80711, - 80712, 80713, 80714, 80715, 80716, 80717, 80718, 80719, 80720, 80721, - 80722, 80723, 80724, 80725, 80726, 80727, 80728, 80729, 80730, 80731, - 80732, 80733, 80734, 80735, 80736, 80737, 80738, 80739, 80740, 80741, - 80742, 80743, 80744, 80745, 80746, 80747, 80748, 80749, 80750, 80751, - 80752, 80753, 80754, 80755, 80756, 80757, 80758, 80759, 80760, 80761, - 80762, 80763, 80764, 80765, 80766, 80767, 80768, 80769, 80770, 80771, - 80772, 80773, 80774, 80775, 80776, 80777, 80778, 80779, 80780, 80781, - 80782, 80783, 80784, 80785, 80786, 80787, 80788, 80789, 80790, 80791, - 80792, 80793, 80794, 80795, 80796, 80797, 80798, 80799, 80800, 80801, - 80802, 80803, 80804, 80805, 80806, 80807, 80808, 80809, 80810, 80811, - 80812, 80813, 80814, 80815, 80816, 80817, 80818, 80819, 80820, 80821, - 80822, 80823, 80824, 80825, 80826, 80827, 80828, 80829, 80830, 80831, - 80832, 80833, 80834, 80835, 80836, 80837, 80838, 80839, 80840, 80841, - 80842, 80843, 80844, 80845, 80846, 80847, 80848, 80849, 80850, 80851, - 80852, 80853, 80854, 80855, 80856, 80857, 80858, 80859, 80860, 80861, - 80862, 80863, 80864, 80865, 80866, 80867, 80868, 80869, 80870, 80871, - 80872, 80873, 80874, 80875, 80876, 80877, 80878, 80879, 80880, 80881, - 80882, 80883, 80884, 80885, 80886, 80887, 80888, 80889, 80890, 80891, - 80892, 80893, 80894, 80895, 80896, 80897, 80898, 80899, 80900, 80901, - 80902, 80903, 80904, 80905, 80906, 80907, 80908, 80909, 80910, 80911, - 80912, 80913, 80914, 80915, 80916, 80917, 80918, 80919, 80920, 80921, - 80922, 80923, 80924, 80925, 80926, 80927, 80928, 80929, 80930, 80931, - 80932, 80933, 80934, 80935, 80936, 80937, 80938, 80939, 80940, 80941, - 80942, 80943, 80944, 80945, 80946, 80947, 80948, 80949, 80950, 80951, - 80952, 80953, 80954, 80955, 80956, 80957, 80958, 80959, 80960, 80961, - 80962, 80963, 80964, 80965, 80966, 80967, 80968, 80969, 80970, 80971, - 80972, 80973, 80974, 80975, 80976, 80977, 80978, 80979, 80980, 80981, - 80982, 80983, 80984, 80985, 80986, 80987, 80988, 80989, 80990, 80991, - 80992, 80993, 80994, 80995, 80996, 80997, 80998, 80999, 81000, 81001, - 81002, 81003, 81004, 81005, 81006, 81007, 81008, 81009, 81010, 81011, - 81012, 81013, 81014, 81015, 81016, 81017, 81018, 81019, 81020, 81021, - 81022, 81023, 81024, 81025, 81026, 81027, 81028, 81029, 81030, 81031, - 81032, 81033, 81034, 81035, 81036, 81037, 81038, 81039, 81040, 81041, - 81042, 81043, 81044, 81045, 81046, 81047, 81048, 81049, 81050, 81051, - 81052, 81053, 81054, 81055, 81056, 81057, 81058, 81059, 81060, 81061, - 81062, 81063, 81064, 81065, 81066, 81067, 81068, 81069, 81070, 81071, - 81072, 81073, 81074, 81075, 81076, 81077, 81078, 81079, 81080, 81081, - 81082, 81083, 81084, 81085, 81086, 81087, 81088, 81089, 81090, 81091, - 81092, 81093, 81094, 81095, 81096, 81097, 81098, 81099, 81100, 81101, - 81102, 81103, 81104, 81105, 81106, 81107, 81108, 81109, 81110, 81111, - 81112, 81113, 81114, 81115, 81116, 81117, 81118, 81119, 81120, 81121, - 81122, 81123, 81124, 81125, 81126, 81127, 81128, 81129, 81130, 81131, - 81132, 81133, 81134, 81135, 81136, 81137, 81138, 81139, 81140, 81141, - 81142, 81143, 81144, 81145, 81146, 81147, 81148, 81149, 81150, 81151, - 81152, 81153, 81154, 81155, 81156, 81157, 81158, 81159, 81160, 81161, - 81162, 81163, 81164, 81165, 81166, 81167, 81168, 81169, 81170, 81171, - 81172, 81173, 81174, 81175, 81176, 81177, 81178, 81179, 81180, 81181, - 81182, 81183, 81184, 81185, 81186, 81187, 81188, 81189, 81190, 81191, - 81192, 81193, 81194, 81195, 81196, 81197, 81198, 81199, 81200, 81201, - 81202, 81203, 81204, 81205, 81206, 81207, 81208, 81209, 81210, 81211, - 81212, 81213, 81214, 81215, 81216, 81217, 81218, 81219, 81220, 81221, - 81222, 81223, 81224, 81225, 81226, 81227, 81228, 81229, 81230, 81231, - 81232, 81233, 81234, 81235, 81236, 81237, 81238, 81239, 81240, 81241, - 81242, 81243, 81244, 81245, 81246, 81247, 81248, 81249, 81250, 81251, - 81252, 81253, 81254, 81255, 81256, 81257, 81258, 81259, 81260, 81261, - 81262, 81263, 81264, 81265, 81266, 81267, 81268, 81269, 81270, 81271, - 81272, 81273, 81274, 81275, 81276, 81277, 81278, 81279, 81280, 81281, - 81282, 81283, 81284, 81285, 81286, 81287, 81288, 81289, 81290, 81291, - 81292, 81293, 81294, 81295, 81296, 81297, 81298, 81299, 81300, 81301, - 81302, 81303, 81304, 81305, 81306, 81307, 81308, 81309, 81310, 81311, - 81312, 81313, 81314, 81315, 81316, 81317, 81318, 81319, 81320, 81321, - 81322, 81323, 81324, 81325, 81326, 81327, 81328, 81329, 81330, 81331, - 81332, 81333, 81334, 81335, 81336, 81337, 81338, 81339, 81340, 81341, - 81342, 81343, 81344, 81345, 81346, 81347, 81348, 81349, 81350, 81351, - 81352, 81353, 81354, 81355, 81356, 81357, 81358, 81359, 81360, 81361, - 81362, 81363, 81364, 81365, 81366, 81367, 81368, 81369, 81370, 81371, - 81372, 81373, 81374, 81375, 81376, 81377, 81378, 81379, 81380, 81381, - 81382, 81383, 81384, 81385, 81386, 81387, 81388, 81389, 81390, 81391, - 81392, 81393, 81394, 81395, 81396, 81397, 81398, 81399, 81400, 81401, - 81402, 81403, 81404, 81405, 81406, 81407, 81408, 81409, 81410, 81411, - 81412, 81413, 81414, 81415, 81416, 81417, 81418, 81419, 81420, 81421, - 81422, 81423, 81424, 81425, 81426, 81427, 81428, 81429, 81430, 81431, - 81432, 81433, 81434, 81435, 81436, 81437, 81438, 81439, 81440, 81441, - 81442, 81443, 81444, 81445, 81446, 81447, 81448, 81449, 81450, 81451, - 81452, 81453, 81454, 81455, 81456, 81457, 81458, 81459, 81460, 81461, - 81462, 81463, 81464, 81465, 81466, 81467, 81468, 81469, 81470, 81471, - 81472, 81473, 81474, 81475, 81476, 81477, 81478, 81479, 81480, 81481, - 81482, 81483, 81484, 81485, 81486, 81487, 81488, 81489, 81490, 81491, - 81492, 81493, 81494, 81495, 81496, 81497, 81498, 81499, 81500, 81501, - 81502, 81503, 81504, 81505, 81506, 81507, 81508, 81509, 81510, 81511, - 81512, 81513, 81514, 81515, 81516, 81517, 81518, 81519, 81520, 81521, - 81522, 81523, 81524, 81525, 81526, 81527, 81528, 81529, 81530, 81531, - 81532, 81533, 81534, 81535, 81536, 81537, 81538, 81539, 81540, 81541, - 81542, 81543, 81544, 81545, 81546, 81547, 81548, 81549, 81550, 81551, - 81552, 81553, 81554, 81555, 81556, 81557, 81558, 81559, 81560, 81561, - 81562, 81563, 81564, 81565, 81566, 81567, 81568, 81569, 81570, 81571, - 81572, 81573, 81574, 81575, 81576, 81577, 81578, 81579, 81580, 81581, - 81582, 81583, 81584, 81585, 81586, 81587, 81588, 81589, 81590, 81591, - 81592, 81593, 81594, 81595, 81596, 81597, 81598, 81599, 81600, 81601, - 81602, 81603, 81604, 81605, 81606, 81607, 81608, 81609, 81610, 81611, - 81612, 81613, 81614, 81615, 81616, 81617, 81618, 81619, 81620, 81621, - 81622, 81623, 81624, 81625, 81626, 81627, 81628, 81629, 81630, 81631, - 81632, 81633, 81634, 81635, 81636, 81637, 81638, 81639, 81640, 81641, - 81642, 81643, 81644, 81645, 81646, 81647, 81648, 81649, 81650, 81651, - 81652, 81653, 81654, 81655, 81656, 81657, 81658, 81659, 81660, 81661, - 81662, 81663, 81664, 81665, 81666, 81667, 81668, 81669, 81670, 81671, - 81672, 81673, 81674, 81675, 81676, 81677, 81678, 81679, 81680, 81681, - 81682, 81683, 81684, 81685, 81686, 81687, 81688, 81689, 81690, 81691, - 81692, 81693, 81694, 81695, 81696, 81697, 81698, 81699, 81700, 81701, - 81702, 81703, 81704, 81705, 81706, 81707, 81708, 81709, 81710, 81711, - 81712, 81713, 81714, 81715, 81716, 81717, 81718, 81719, 81720, 81721, - 81722, 81723, 81724, 81725, 81726, 81727, 81728, 81729, 81730, 81731, - 81732, 81733, 81734, 81735, 81736, 81737, 81738, 81739, 81740, 81741, - 81742, 81743, 81744, 81745, 81746, 81747, 81748, 81749, 81750, 81751, - 81752, 81753, 81754, 81755, 81756, 81757, 81758, 81759, 81760, 81761, - 81762, 81763, 81764, 81765, 81766, 81767, 81768, 81769, 81770, 81771, - 81772, 81773, 81774, 81775, 81776, 81777, 81778, 81779, 81780, 81781, - 81782, 81783, 81784, 81785, 81786, 81787, 81788, 81789, 81790, 81791, - 81792, 81793, 81794, 81795, 81796, 81797, 81798, 81799, 81800, 81801, - 81802, 81803, 81804, 81805, 81806, 81807, 81808, 81809, 81810, 81811, - 81812, 81813, 81814, 81815, 81816, 81817, 81818, 81819, 81820, 81821, - 81822, 81823, 81824, 81825, 81826, 81827, 81828, 81829, 81830, 81831, - 81832, 81833, 81834, 81835, 81836, 81837, 81838, 81839, 81840, 81841, - 81842, 81843, 81844, 81845, 81846, 81847, 81848, 81849, 81850, 81851, - 81852, 81853, 81854, 81855, 81856, 81857, 81858, 81859, 81860, 81861, - 81862, 81863, 81864, 81865, 81866, 81867, 81868, 81869, 81870, 81871, - 81872, 81873, 81874, 81875, 81876, 81877, 81878, 81879, 81880, 81881, - 81882, 81883, 81884, 81885, 81886, 81887, 81888, 81889, 81890, 81891, - 81892, 81893, 81894, 81895, 81896, 81897, 81898, 81899, 81900, 81901, - 81902, 81903, 81904, 81905, 81906, 81907, 81908, 81909, 81910, 81911, - 81912, 81913, 81914, 81915, 81916, 81917, 81918, 81919, 82928, 82929, - 82930, 82931, 82932, 82933, 82934, 82935, 82936, 82937, 82938, 82688, - 82689, 82690, 82691, 82692, 82693, 82694, 82695, 82696, 82697, 82698, - 82699, 82700, 82701, 82702, 82703, 82704, 82705, 82706, 82707, 82708, - 82709, 82710, 82711, 82712, 82713, 82714, 82715, 82716, 82717, 82718, - 82719, 82720, 82721, 82722, 82723, 82724, 82725, 82726, 82727, 82728, - 82729, 82730, 82731, 82732, 82733, 82734, 82735, 82736, 82737, 82738, - 82739, 82740, 82741, 82742, 82743, 82744, 82745, 82746, 82747, 82748, - 82749, 82750, 82751, 82752, 82753, 82754, 82755, 82756, 82757, 82758, - 82759, 82760, 82761, 82762, 82763, 82764, 82765, 82766, 82767, 82768, - 82769, 82770, 82771, 82772, 82773, 82774, 82775, 82776, 82777, 82778, - 82779, 82780, 82781, 82782, 82783, 82784, 82785, 82786, 82787, 82788, - 82789, 82790, 82791, 82792, 82793, 82794, 82795, 82796, 82797, 82798, - 82799, 82800, 82801, 82802, 82803, 82804, 82805, 82806, 82807, 82808, - 82809, 82810, 82811, 82812, 82813, 82814, 82815, 82816, 82817, 82818, - 82819, 82820, 82821, 82822, 82823, 82824, 82825, 82826, 82827, 82828, - 82829, 82830, 82831, 82832, 82833, 82834, 82835, 82836, 82837, 82838, - 82839, 82840, 82841, 82842, 82843, 82844, 82845, 82846, 82847, 82848, - 82849, 82850, 82851, 82852, 82853, 82854, 82855, 82856, 82857, 82858, - 82859, 82860, 82861, 82862, 82863, 82864, 82865, 82866, 82867, 82868, - 82869, 82870, 82871, 82872, 82873, 82874, 82875, 82876, 82877, 82878, - 82879, 82880, 82881, 82882, 82883, 82884, 82885, 82886, 82887, 82888, - 82889, 82890, 82891, 82892, 82893, 82894, 82895, 82896, 82897, 82898, - 82899, 82900, 82901, 82902, 82903, 82904, 82905, 82906, 82907, 82908, - 82909, 82910, 82911, 82912, 82913, 82914, 82915, 82916, 82917, 82918, - 82919, 82920, 82921, 82922, 82923, 82924, 82925, 82926, 82927, 81920, - 81921, 81922, 81923, 81924, 81925, 81926, 81927, 81928, 81929, 81930, - 81931, 81932, 81933, 81934, 81935, 81936, 81937, 81938, 81939, 81940, - 81941, 81942, 81943, 81944, 81945, 81946, 81947, 81948, 81949, 81950, - 81951, 81952, 81953, 81954, 81955, 81956, 81957, 81958, 81959, 81960, - 81961, 81962, 81963, 81964, 81965, 81966, 81967, 81968, 81969, 81970, - 81971, 81972, 81973, 81974, 81975, 81976, 81977, 81978, 81979, 81980, - 81981, 81982, 81983, 81984, 81985, 81986, 81987, 81988, 81989, 81990, - 81991, 81992, 81993, 81994, 81995, 81996, 81997, 81998, 81999, 82000, - 82001, 82002, 82003, 82004, 82005, 82006, 82007, 82008, 82009, 82010, - 82011, 82012, 82013, 82014, 82015, 82016, 82017, 82018, 82019, 82020, - 82021, 82022, 82023, 82024, 82025, 82026, 82027, 82028, 82029, 82030, - 82031, 82032, 82033, 82034, 82035, 82036, 82037, 82038, 82039, 82040, - 82041, 82042, 82043, 82044, 82045, 82046, 82047, 82048, 82049, 82050, - 82051, 82052, 82053, 82054, 82055, 82056, 82057, 82058, 82059, 82060, - 82061, 82062, 82063, 82064, 82065, 82066, 82067, 82068, 82069, 82070, - 82071, 82072, 82073, 82074, 82075, 82076, 82077, 82078, 82079, 82080, - 82081, 82082, 82083, 82084, 82085, 82086, 82087, 82088, 82089, 82090, - 82091, 82092, 82093, 82094, 82095, 82096, 82097, 82098, 82099, 82100, - 82101, 82102, 82103, 82104, 82105, 82106, 82107, 82108, 82109, 82110, - 82111, 82112, 82113, 82114, 82115, 82116, 82117, 82118, 82119, 82120, - 82121, 82122, 82123, 82124, 82125, 82126, 82127, 82128, 82129, 82130, - 82131, 82132, 82133, 82134, 82135, 82136, 82137, 82138, 82139, 82140, - 82141, 82142, 82143, 82144, 82145, 82146, 82147, 82148, 82149, 82150, - 82151, 82152, 82153, 82154, 82155, 82156, 82157, 82158, 82159, 82160, - 82161, 82162, 82163, 82164, 82165, 82166, 82167, 82168, 82169, 82170, - 82171, 82172, 82173, 82174, 82175, 82176, 82177, 82178, 82179, 82180, - 82181, 82182, 82183, 82184, 82185, 82186, 82187, 82188, 82189, 82190, - 82191, 82192, 82193, 82194, 82195, 82196, 82197, 82198, 82199, 82200, - 82201, 82202, 82203, 82204, 82205, 82206, 82207, 82208, 82209, 82210, - 82211, 82212, 82213, 82214, 82215, 82216, 82217, 82218, 82219, 82220, - 82221, 82222, 82223, 82224, 82225, 82226, 82227, 82228, 82229, 82230, - 82231, 82232, 82233, 82234, 82235, 82236, 82237, 82238, 82239, 82240, - 82241, 82242, 82243, 82244, 82245, 82246, 82247, 82248, 82249, 82250, - 82251, 82252, 82253, 82254, 82255, 82256, 82257, 82258, 82259, 82260, - 82261, 82262, 82263, 82264, 82265, 82266, 82267, 82268, 82269, 82270, - 82271, 82272, 82273, 82274, 82275, 82276, 82277, 82278, 82279, 82280, - 82281, 82282, 82283, 82284, 82285, 82286, 82287, 82288, 82289, 82290, - 82291, 82292, 82293, 82294, 82295, 82296, 82297, 82298, 82299, 82300, - 82301, 82302, 82303, 82304, 82305, 82306, 82307, 82308, 82309, 82310, - 82311, 82312, 82313, 82314, 82315, 82316, 82317, 82318, 82319, 82320, - 82321, 82322, 82323, 82324, 82325, 82326, 82327, 82328, 82329, 82330, - 82331, 82332, 82333, 82334, 82335, 82336, 82337, 82338, 82339, 82340, - 82341, 82342, 82343, 82344, 82345, 82346, 82347, 82348, 82349, 82350, - 82351, 82352, 82353, 82354, 82355, 82356, 82357, 82358, 82359, 82360, - 82361, 82362, 82363, 82364, 82365, 82366, 82367, 82368, 82369, 82370, - 82371, 82372, 82373, 82374, 82375, 82376, 82377, 82378, 82379, 82380, - 82381, 82382, 82383, 82384, 82385, 82386, 82387, 82388, 82389, 82390, - 82391, 82392, 82393, 82394, 82395, 82396, 82397, 82398, 82399, 82400, - 82401, 82402, 82403, 82404, 82405, 82406, 82407, 82408, 82409, 82410, - 82411, 82412, 82413, 82414, 82415, 82416, 82417, 82418, 82419, 82420, - 82421, 82422, 82423, 82424, 82425, 82426, 82427, 82428, 82429, 82430, - 82431, 82432, 82433, 82434, 82435, 82436, 82437, 82438, 82439, 82440, - 82441, 82442, 82443, 82444, 82445, 82446, 82447, 82448, 82449, 82450, - 82451, 82452, 82453, 82454, 82455, 82456, 82457, 82458, 82459, 82460, - 82461, 82462, 82463, 82464, 82465, 82466, 82467, 82468, 82469, 82470, - 82471, 82472, 82473, 82474, 82475, 82476, 82477, 82478, 82479, 82480, - 82481, 82482, 82483, 82484, 82485, 82486, 82487, 82488, 82489, 82490, - 82491, 82492, 82493, 82494, 82495, 82496, 82497, 82498, 82499, 82500, - 82501, 82502, 82503, 82504, 82505, 82506, 82507, 82508, 82509, 82510, - 82511, 82512, 82513, 82514, 82515, 82516, 82517, 82518, 82519, 82520, - 82521, 82522, 82523, 82524, 82525, 82526, 82527, 82528, 82529, 82530, - 82531, 82532, 82533, 82534, 82535, 82536, 82537, 82538, 82539, 82540, - 82541, 82542, 82543, 82544, 82545, 82546, 82547, 82548, 82549, 82550, - 82551, 82552, 82553, 82554, 82555, 82556, 82557, 82558, 82559, 82560, - 82561, 82562, 82563, 82564, 82565, 82566, 82567, 82568, 82569, 82570, - 82571, 82572, 82573, 82574, 82575, 82576, 82577, 82578, 82579, 82580, - 82581, 82582, 82583, 82584, 82585, 82586, 82587, 82588, 82589, 82590, - 82591, 82592, 82593, 82594, 82595, 82596, 82597, 82598, 82599, 82600, - 82601, 82602, 82603, 82604, 82605, 82606, 82607, 82608, 82609, 82610, - 82611, 82612, 82613, 82614, 82615, 82616, 82617, 82618, 82619, 82620, - 82621, 82622, 82623, 82624, 82625, 82626, 82627, 82628, 82629, 82630, - 82631, 82632, 82633, 82634, 82635, 82636, 82637, 82638, 82639, 82640, - 82641, 82642, 82643, 82644, 82645, 82646, 82647, 82648, 82649, 82650, - 82651, 82652, 82653, 82654, 82655, 82656, 82657, 82658, 82659, 82660, - 82661, 82662, 82663, 82664, 82665, 82666, 82667, 82668, 82669, 82670, - 82671, 82672, 82673, 82674, 82675, 82676, 82677, 82678, 82679, 82680, - 82681, 82682, 82683, 82684, 82685, 82686, 82687, 129370, 10037, 10039, + 127919, 128549, 128542, 9933, 9090, 129770, 129400, 72004, 72021, 72020, + 72023, 72022, 72019, 72018, 72016, 72025, 72017, 72024, 71964, 71958, + 71963, 71974, 71973, 71936, 71937, 71961, 71960, 71966, 71965, 71940, + 71941, 71938, 71939, 71982, 71976, 71952, 71962, 71957, 71967, 71978, + 71979, 71980, 71971, 71970, 71954, 71953, 71951, 71950, 71949, 71948, + 71969, 71968, 71981, 71955, 71972, 71975, 71977, 71983, 71942, 71945, + 71997, 71996, 72003, 71995, 71984, 71991, 71987, 71988, 71985, 71986, + 71989, 71992, 71998, 72002, 72000, 72005, 72006, 71999, 72001, 8725, 247, + 8903, 129343, 8739, 9902, 129684, 128171, 128565, 12291, 8783, 9009, + 128462, 128441, 128442, 128443, 8939, 8941, 8716, 8740, 10990, 8832, + 8928, 8876, 8833, 8929, 8878, 128021, 71680, 71681, 71687, 71689, 71703, + 71702, 71708, 71707, 71723, 71716, 71701, 71700, 71706, 71705, 71684, + 71685, 71682, 71683, 71694, 71704, 71699, 71709, 71719, 71720, 71721, + 71713, 71712, 71696, 71695, 71693, 71692, 71698, 71697, 71691, 71690, + 71711, 71710, 71722, 71717, 71714, 71718, 71715, 71686, 71688, 71729, + 71730, 71724, 71732, 71734, 71727, 71728, 71725, 71726, 71731, 71733, + 71739, 71738, 71735, 71737, 71736, 128054, 128044, 36, 127025, 127026, + 127027, 127028, 127029, 127030, 127031, 127032, 127033, 127034, 127035, + 127036, 127037, 127038, 127039, 127040, 127041, 127042, 127043, 127044, + 127045, 127046, 127047, 127048, 127049, 127050, 127051, 127052, 127053, + 127054, 127055, 127056, 127057, 127058, 127059, 127060, 127061, 127062, + 127063, 127064, 127065, 127066, 127067, 127068, 127069, 127070, 127071, + 127072, 127073, 127024, 127075, 127076, 127077, 127078, 127079, 127080, + 127081, 127082, 127083, 127084, 127085, 127086, 127087, 127088, 127089, + 127090, 127091, 127092, 127093, 127094, 127095, 127096, 127097, 127098, + 127099, 127100, 127101, 127102, 127103, 127104, 127105, 127106, 127107, + 127108, 127109, 127110, 127111, 127112, 127113, 127114, 127115, 127116, + 127117, 127118, 127119, 127120, 127121, 127122, 127123, 127074, 129743, + 8363, 8724, 8760, 8901, 729, 9676, 8284, 11850, 11034, 129765, 11795, + 11784, 10649, 11798, 9470, 9465, 9464, 9467, 9466, 9463, 9462, 9469, + 9461, 9468, 10175, 10868, 10986, 8225, 8223, 11840, 8914, 8748, 11842, + 8222, 8215, 10835, 10836, 10645, 10913, 10915, 10914, 10939, 8243, 12318, + 10746, 10830, 10831, 11849, 10988, 11844, 10940, 8913, 8912, 11005, + 10987, 8915, 9208, 10981, 8875, 10979, 8214, 11799, 10646, 733, 8252, + 8263, 65100, 10908, 10907, 11002, 11001, 10906, 10905, 8510, 8473, 8511, + 8450, 8461, 8469, 8474, 8477, 8484, 8518, 8519, 8520, 8521, 8517, 8509, + 8508, 8512, 10719, 9890, 9891, 11260, 127849, 8868, 10993, 10623, 8945, + 8964, 117764, 128317, 117859, 118264, 117883, 118268, 117849, 128315, + 117914, 117864, 10728, 10729, 117875, 117879, 129288, 129290, 129289, + 129291, 8595, 8629, 8671, 129035, 129031, 129179, 129043, 129027, 129047, + 8626, 8627, 10504, 8693, 129975, 8615, 10515, 11796, 11015, 129203, + 11147, 11107, 11139, 11123, 129067, 11168, 11169, 129063, 129059, 129075, + 129071, 11133, 11085, 11117, 11143, 129171, 10507, 8609, 11247, 8681, + 129175, 8623, 8659, 8675, 129079, 10597, 10607, 10593, 10585, 8643, + 10589, 10581, 8642, 129091, 129095, 129087, 10225, 128623, 129107, + 129083, 8650, 128687, 128330, 128682, 129444, 8367, 10139, 128009, + 128050, 128042, 129656, 128167, 129316, 129345, 9946, 128087, 113784, + 113788, 113785, 113783, 113782, 113786, 113787, 113795, 113798, 113793, + 113800, 113781, 113794, 113792, 113799, 113797, 113796, 113776, 113808, + 113817, 113811, 113814, 113809, 113816, 113779, 113810, 113815, 113813, + 113812, 113778, 113777, 113780, 113729, 113733, 113672, 113677, 113683, + 113735, 113739, 113746, 113668, 113678, 113674, 113726, 113691, 113699, + 113700, 113695, 113709, 113712, 113713, 113705, 113711, 113669, 113725, + 113679, 113684, 113670, 113743, 113749, 113687, 113689, 113693, 113707, + 113697, 113703, 113690, 113694, 113708, 113698, 113704, 113764, 113763, + 113762, 113761, 113732, 113753, 113731, 113755, 113754, 113666, 113766, + 113765, 113676, 113675, 113741, 113750, 113680, 113688, 113692, 113696, + 113710, 113727, 113728, 113716, 113717, 113714, 113715, 113701, 113702, + 113724, 113723, 113706, 113742, 113740, 113767, 113769, 113730, 113768, + 113682, 113685, 113752, 113737, 113667, 113719, 113718, 113681, 113745, + 113748, 113751, 113738, 113673, 113770, 113720, 113757, 113760, 113722, + 113759, 113756, 113721, 113758, 113665, 113747, 113664, 113686, 113734, + 113736, 113744, 113671, 113821, 113822, 113823, 113820, 129375, 129414, + 129516, 128192, 983082, 128066, 127805, 127806, 129467, 9793, 127758, + 127759, 127757, 9178, 9841, 129413, 11790, 77830, 77831, 77832, 77828, + 77829, 77824, 77825, 77826, 77827, 77833, 77834, 77835, 77840, 77841, + 77844, 77845, 77836, 77837, 77838, 77839, 77842, 77843, 77846, 77847, + 77869, 77870, 77872, 77873, 77874, 77875, 77877, 77878, 77871, 77876, + 77879, 77880, 77881, 77882, 77860, 77861, 77858, 77859, 77862, 77863, + 77864, 77865, 77866, 77867, 77868, 77903, 77848, 77849, 77850, 77851, + 77852, 77853, 77854, 77855, 77856, 77857, 77883, 77884, 77885, 77886, + 77887, 77888, 77889, 77890, 77891, 77892, 77893, 77894, 77895, 77896, + 77897, 77898, 77899, 77900, 77901, 77902, 78867, 78868, 78869, 78861, + 78862, 78863, 78864, 78865, 78866, 78870, 78871, 78892, 78893, 78894, + 78872, 78873, 78874, 78875, 78876, 78877, 78878, 78879, 78880, 78881, + 78882, 78883, 78884, 78885, 78886, 78887, 78888, 78889, 78890, 78891, + 78910, 78908, 78903, 77908, 77909, 77904, 77905, 77906, 77907, 77910, + 77911, 77912, 77913, 77915, 77916, 77917, 77918, 77914, 77919, 77920, + 77921, 77922, 77923, 77924, 77925, 77926, 77927, 77928, 77929, 77930, + 77931, 77932, 77933, 77934, 77935, 77936, 77937, 77938, 77939, 77940, + 77941, 77949, 77950, 77942, 77943, 77944, 77945, 77946, 77947, 77948, + 77951, 77974, 77975, 77978, 77979, 77973, 77976, 77977, 77980, 77981, + 77982, 77983, 77984, 77991, 77992, 77994, 77995, 77985, 77986, 77987, + 77988, 77989, 77990, 77993, 77996, 77997, 77998, 77999, 78000, 78001, + 78002, 78003, 78004, 78005, 78006, 78008, 78009, 78011, 78012, 78007, + 78010, 78013, 78014, 78015, 78016, 78017, 78025, 78026, 78027, 78028, + 78029, 78030, 78031, 78032, 78033, 78018, 78019, 78020, 78021, 78022, + 78023, 78024, 77969, 77970, 77962, 77963, 77964, 77965, 77966, 77967, + 77968, 77971, 77972, 77952, 77953, 77954, 77955, 77956, 77957, 77958, + 77959, 77960, 77961, 78041, 78042, 78043, 78044, 78034, 78035, 78036, + 78037, 78038, 78039, 78040, 78057, 78058, 78066, 78067, 78059, 78060, + 78061, 78062, 78063, 78064, 78065, 78068, 78073, 78074, 78069, 78070, + 78071, 78072, 78075, 78076, 78077, 78051, 78052, 78053, 78054, 78045, + 78046, 78047, 78048, 78049, 78050, 78055, 78056, 78911, 78909, 78904, + 78078, 78079, 78080, 78081, 78082, 78083, 78084, 78085, 78086, 78087, + 78091, 78092, 78088, 78089, 78090, 78093, 78094, 78095, 78096, 78097, + 78098, 78111, 78112, 78118, 78119, 78120, 78121, 78110, 78113, 78114, + 78115, 78116, 78117, 78122, 78128, 78129, 78130, 78131, 78132, 78133, + 78123, 78124, 78125, 78126, 78127, 78134, 78135, 78137, 78138, 78139, + 78140, 78136, 78141, 78142, 78100, 78101, 78099, 78102, 78103, 78104, + 78105, 78106, 78107, 78108, 78109, 78913, 78150, 78151, 78152, 78148, + 78149, 78143, 78144, 78145, 78146, 78147, 78153, 78154, 78156, 78157, + 78155, 78158, 78159, 78160, 78161, 78162, 78163, 78164, 78165, 78184, + 78185, 78186, 78187, 78178, 78179, 78180, 78181, 78182, 78183, 78188, + 78189, 78193, 78194, 78196, 78197, 78190, 78191, 78192, 78195, 78198, + 78199, 78200, 78201, 78166, 78167, 78173, 78174, 78168, 78169, 78170, + 78171, 78172, 78175, 78176, 78177, 78202, 78203, 78204, 78205, 78206, + 78212, 78213, 78207, 78208, 78209, 78210, 78211, 78214, 78215, 78914, + 78916, 78897, 78220, 78221, 78225, 78226, 78216, 78217, 78218, 78219, + 78222, 78223, 78224, 78227, 78228, 78229, 78230, 78231, 78232, 78233, + 78234, 78907, 78901, 78899, 78906, 78900, 78898, 78905, 78244, 78245, + 78249, 78250, 78243, 78246, 78247, 78248, 78251, 78252, 78915, 78253, + 78254, 78255, 78257, 78258, 78256, 78259, 78260, 78261, 78262, 78263, + 78264, 78268, 78269, 78270, 78271, 78272, 78273, 78274, 78275, 78276, + 78265, 78266, 78279, 78280, 78281, 78282, 78283, 78284, 78267, 78277, + 78278, 78285, 78286, 78289, 78290, 78292, 78293, 78297, 78298, 78287, + 78288, 78291, 78294, 78295, 78296, 78299, 78304, 78305, 78306, 78301, + 78302, 78300, 78303, 78307, 78308, 78309, 78310, 78311, 78312, 78313, + 78314, 78315, 78316, 78317, 78318, 78933, 78928, 78920, 78924, 78932, + 78926, 78921, 78929, 78925, 78923, 78931, 78919, 78927, 78922, 78930, + 78912, 78336, 78337, 78338, 78328, 78329, 78330, 78331, 78332, 78333, + 78334, 78335, 78339, 78354, 78355, 78356, 78357, 78358, 78359, 78361, + 78362, 78351, 78352, 78353, 78360, 78363, 78364, 78345, 78346, 78340, + 78341, 78342, 78343, 78344, 78347, 78348, 78349, 78350, 78365, 78366, + 78367, 78319, 78320, 78321, 78322, 78323, 78324, 78325, 78326, 78327, + 78372, 78373, 78368, 78369, 78370, 78371, 78374, 78375, 78376, 78377, + 78385, 78386, 78378, 78379, 78380, 78381, 78382, 78383, 78384, 78387, + 78388, 78389, 78399, 78400, 78401, 78402, 78409, 78410, 78403, 78404, + 78405, 78406, 78407, 78408, 78411, 78414, 78415, 78412, 78413, 78390, + 78391, 78392, 78393, 78394, 78395, 78396, 78397, 78398, 78423, 78424, + 78425, 78426, 78427, 78428, 78429, 78416, 78417, 78421, 78422, 78418, + 78419, 78420, 78430, 78431, 78432, 78433, 78434, 78435, 78436, 78445, + 78446, 78437, 78438, 78439, 78440, 78441, 78442, 78443, 78444, 78447, + 78448, 78452, 78453, 78454, 78455, 78459, 78460, 78449, 78450, 78451, + 78456, 78457, 78458, 78469, 78470, 78471, 78472, 78473, 78461, 78462, + 78465, 78466, 78463, 78464, 78467, 78468, 78474, 78475, 78476, 78487, + 78488, 78489, 78490, 78477, 78478, 78479, 78480, 78481, 78482, 78483, + 78484, 78485, 78486, 78902, 78491, 78492, 78494, 78495, 78493, 78496, + 78497, 78498, 78499, 78500, 78501, 78502, 78503, 78514, 78515, 78516, + 78512, 78513, 78511, 78517, 78518, 78519, 78520, 78521, 78522, 78523, + 78524, 78530, 78531, 78525, 78526, 78527, 78528, 78529, 78532, 78533, + 78534, 78535, 78536, 78537, 78538, 78539, 78540, 78541, 78542, 78543, + 78544, 78546, 78547, 78551, 78552, 78545, 78548, 78549, 78550, 78553, + 78554, 78555, 78560, 78561, 78562, 78565, 78566, 78556, 78557, 78558, + 78559, 78563, 78564, 78567, 78568, 78575, 78576, 78577, 78569, 78570, + 78571, 78572, 78573, 78574, 78578, 78579, 78580, 78586, 78587, 78581, + 78582, 78583, 78584, 78585, 78588, 78589, 78590, 78591, 78592, 78593, + 78594, 78595, 78596, 78597, 78598, 78601, 78602, 78606, 78607, 78608, + 78609, 78610, 78611, 78599, 78600, 78603, 78604, 78605, 78613, 78614, + 78619, 78620, 78612, 78615, 78616, 78617, 78618, 78621, 78622, 78623, + 78636, 78637, 78638, 78639, 78634, 78635, 78640, 78641, 78642, 78624, + 78625, 78626, 78627, 78628, 78629, 78630, 78631, 78632, 78633, 78917, + 78648, 78649, 78650, 78643, 78644, 78645, 78646, 78647, 78651, 78652, + 78653, 78667, 78668, 78674, 78675, 78664, 78665, 78666, 78669, 78670, + 78671, 78672, 78673, 78678, 78679, 78676, 78677, 78680, 78681, 78682, + 78683, 78684, 78685, 78686, 78687, 78688, 78689, 78654, 78655, 78656, + 78657, 78658, 78659, 78660, 78661, 78662, 78663, 78706, 78707, 78708, + 78690, 78691, 78692, 78693, 78694, 78695, 78696, 78697, 78698, 78699, + 78700, 78701, 78702, 78703, 78704, 78705, 78709, 78710, 78712, 78713, + 78714, 78715, 78895, 78716, 78717, 78718, 78711, 78719, 78720, 78721, + 78722, 78723, 78724, 78725, 78726, 78727, 78728, 78729, 78730, 78731, + 78732, 78733, 78734, 78735, 78736, 78737, 78738, 78741, 78742, 78747, + 78748, 78749, 78750, 78739, 78740, 78743, 78744, 78745, 78746, 78751, + 78752, 78753, 78754, 78756, 78757, 78761, 78762, 78755, 78758, 78759, + 78760, 78763, 78764, 78765, 78766, 78896, 78769, 78770, 78776, 78777, + 78767, 78768, 78771, 78772, 78773, 78774, 78775, 78778, 78779, 78783, + 78784, 78787, 78788, 78789, 78790, 78780, 78781, 78782, 78785, 78786, + 78791, 78796, 78797, 78792, 78793, 78794, 78795, 78798, 78918, 78802, + 78803, 78804, 78806, 78807, 78809, 78810, 78799, 78800, 78801, 78805, + 78808, 78811, 78812, 78813, 78814, 78815, 78816, 78817, 78818, 78819, + 78821, 78822, 78823, 78824, 78825, 78826, 78827, 78828, 78829, 78830, + 78831, 78832, 78820, 78833, 78834, 78835, 78836, 78842, 78843, 78844, + 78845, 78846, 78847, 78848, 78849, 78850, 78851, 78852, 78853, 78854, + 78855, 78856, 78857, 78858, 78859, 78860, 78837, 78838, 78839, 78840, + 78841, 78235, 78236, 78237, 78238, 78239, 78240, 78241, 78242, 78504, + 78505, 78506, 78507, 78508, 78509, 78510, 118470, 129370, 10037, 10039, 10036, 10049, 117865, 117866, 10058, 10035, 9834, 66854, 66853, 66827, 66826, 66833, 66832, 66821, 66837, 66836, 66835, 66842, 66841, 66824, 66823, 66819, 66818, 66822, 66820, 66855, 66831, 66844, 66843, 66846, @@ -10880,74 +10462,75 @@ static const unsigned int dawg_pos_to_codepoint[] = { 983099, 983048, 983095, 983046, 983064, 128282, 983051, 983050, 9993, 128388, 128233, 9094, 983067, 983100, 983049, 8926, 8927, 8925, 8924, 8797, 8917, 61, 10865, 10867, 11072, 10609, 10723, 10724, 10926, 11257, - 10871, 10854, 10862, 8789, 10872, 8781, 8794, 10738, 10736, 10734, 10739, - 10737, 10735, 11249, 11248, 9003, 8998, 983105, 983104, 8494, 8793, - 983136, 4957, 4959, 4958, 4963, 4965, 4978, 4988, 4980, 4979, 4987, 4985, - 4982, 4981, 4986, 4984, 4983, 4968, 4966, 4964, 4960, 4999, 4998, 4711, - 4997, 43816, 43819, 43821, 43820, 43818, 43822, 43817, 4704, 4707, 4710, - 11653, 4709, 4708, 4706, 4705, 43808, 43811, 43813, 43812, 43810, 43814, - 43809, 11704, 11707, 11709, 11708, 11706, 11710, 11705, 11688, 11691, - 11693, 11692, 11690, 11694, 11689, 4904, 4907, 4910, 11664, 4909, 4908, - 4911, 4906, 4905, 4728, 4731, 4734, 11655, 4733, 4732, 4735, 4730, 4729, - 43789, 43788, 43787, 43786, 43790, 43785, 4856, 4859, 4862, 11661, 4861, - 4860, 4863, 4858, 4857, 43797, 43796, 43795, 43794, 43798, 43793, 4848, - 4851, 4854, 11660, 4853, 4852, 4855, 4850, 4849, 5003, 5002, 4943, 5001, - 4936, 4939, 4941, 4940, 4954, 4938, 4942, 4937, 4873, 124916, 124915, - 124924, 124923, 124910, 124909, 124926, 124925, 124922, 124921, 124920, - 124919, 124918, 124917, 124914, 124913, 124912, 124904, 11667, 4895, - 11670, 11669, 11668, 4888, 4891, 4893, 4892, 4890, 4894, 4889, 4768, - 4771, 4774, 11658, 4773, 4772, 4775, 4770, 4769, 4880, 4883, 4885, 4884, - 4882, 11736, 11739, 11741, 11740, 11738, 11742, 11737, 4872, 4875, 4878, - 4879, 4877, 4876, 4874, 124907, 124906, 4631, 124905, 124896, 124899, - 124901, 124900, 124898, 124902, 124897, 4624, 4627, 4629, 4628, 4626, - 4630, 4625, 4608, 4611, 4614, 4615, 4613, 4612, 4610, 4609, 4800, 4803, - 4805, 4804, 4802, 4792, 4795, 4797, 4796, 4794, 4798, 4793, 4784, 4787, - 4789, 4788, 4786, 11720, 11723, 11725, 11724, 11722, 11726, 11721, 4776, - 4779, 4782, 4783, 4781, 4780, 4778, 4777, 4995, 4994, 4639, 4993, 4632, - 4635, 4638, 11649, 4637, 4636, 4953, 4634, 4633, 4760, 4763, 4766, 11657, - 4765, 4764, 4767, 4762, 4761, 4752, 4755, 4758, 11656, 4757, 4756, 4759, - 4754, 4753, 4912, 4816, 4819, 4821, 4820, 4818, 4822, 4817, 4915, 4918, - 11665, 4917, 4916, 4919, 4914, 4913, 5007, 5006, 4951, 5005, 4944, 4947, - 4950, 11666, 4949, 4948, 4946, 4945, 4696, 4699, 4701, 4700, 4698, 4688, - 4691, 4693, 4692, 4690, 4694, 4689, 4680, 4683, 4685, 4684, 4682, 11712, - 11715, 11717, 11716, 11714, 11718, 11713, 4672, 4675, 4678, 4679, 4677, - 4676, 4674, 4673, 4648, 4651, 4654, 11650, 4653, 4652, 4655, 4952, 4650, - 4649, 4661, 4996, 5000, 4992, 5004, 4660, 4664, 4667, 4670, 11652, 4669, - 4668, 4671, 4666, 4665, 4640, 4643, 4645, 4644, 4647, 4642, 4646, 4641, - 11680, 11683, 11685, 11684, 11682, 11686, 11681, 4656, 4659, 4662, 11651, - 4663, 4658, 4657, 4896, 4899, 4902, 11663, 4901, 4900, 4903, 4898, 4897, - 43781, 43780, 43779, 43778, 43782, 43777, 4928, 4931, 4934, 4935, 4933, - 4932, 4930, 4929, 4920, 4923, 4925, 4924, 4927, 4922, 4926, 4921, 4720, - 4723, 4726, 11654, 4725, 4724, 4727, 4722, 4721, 4864, 4867, 4870, 11662, - 4869, 4868, 4871, 4866, 4865, 4616, 4619, 4622, 11648, 4621, 4620, 4623, - 4618, 4617, 4808, 4811, 4814, 4815, 4813, 4812, 4810, 4809, 4840, 4843, - 4846, 4847, 4845, 4844, 4842, 4841, 4744, 4747, 4749, 4748, 4746, 11728, - 11731, 11733, 11732, 11730, 11734, 11729, 4736, 4739, 4742, 4743, 4741, - 4740, 4738, 4737, 4832, 4835, 4837, 4836, 4839, 4834, 4838, 4833, 11696, - 11699, 11701, 11700, 11698, 11702, 11697, 4824, 4827, 4830, 11659, 4829, - 4828, 4831, 4826, 4825, 4712, 4715, 4717, 4716, 4719, 4714, 4718, 4713, - 5009, 5016, 5012, 5015, 5013, 5017, 5010, 5011, 5014, 5008, 4973, 4972, - 4975, 4974, 4971, 4970, 4977, 4969, 4976, 4962, 4967, 4961, 983096, - 983047, 127984, 127972, 8352, 8364, 8455, 8265, 33, 8761, 118269, 118270, - 118271, 118274, 128529, 128942, 128954, 128948, 128905, 128915, 128935, - 128125, 1781, 1780, 1783, 1782, 1779, 1778, 1776, 1785, 1777, 1784, - 128065, 128083, 128064, 128231, 9167, 127794, 983180, 128523, 128561, - 129312, 128531, 129301, 128567, 129488, 128582, 128558, 129326, 128560, - 129762, 129320, 128581, 129395, 129763, 129402, 128539, 128540, 128541, - 128514, 129298, 129769, 129323, 128580, 128548, 129764, 129396, 128566, - 129318, 128134, 128536, 129401, 8507, 127981, 10540, 10543, 9950, 127810, - 129478, 128439, 128224, 128106, 9771, 127877, 129498, 128552, 129718, - 170, 9792, 127905, 9972, 129338, 8210, 8199, 128193, 128452, 983107, - 128253, 127902, 129734, 10765, 128293, 128658, 129519, 127879, 127878, - 129512, 9789, 127771, 127763, 8296, 129351, 128031, 127907, 9673, 127845, - 128074, 8281, 11821, 127953, 129407, 129747, 9189, 117910, 9971, 129449, - 128170, 9884, 10086, 9880, 8277, 127924, 128190, 128563, 129672, 129712, - 128760, 117834, 117835, 129359, 128389, 127787, 127745, 129709, 128448, - 129462, 128099, 127860, 127869, 11792, 10972, 983071, 8704, 8873, 129376, - 10021, 11156, 8280, 8283, 10019, 127808, 10018, 128966, 8732, 8197, 9970, - 129749, 129418, 8260, 8543, 128444, 128446, 128445, 127839, 8355, 129398, - 10156, 128037, 8994, 128550, 128056, 127844, 127773, 127765, 10199, 9608, - 46, 65342, 65312, 65292, 65306, 65504, 65375, 65371, 65288, 65339, 65308, + 11158, 10871, 10854, 10862, 8789, 10872, 8781, 8794, 10738, 10736, 10734, + 10739, 10737, 10735, 11249, 11248, 9003, 8998, 983105, 983104, 8494, + 8793, 983136, 4957, 4959, 4958, 4963, 4965, 4978, 4988, 4980, 4979, 4987, + 4985, 4982, 4981, 4986, 4984, 4983, 4968, 4966, 4964, 4960, 4999, 4998, + 4711, 4997, 43816, 43819, 43821, 43820, 43818, 43822, 43817, 4704, 4707, + 4710, 11653, 4709, 4708, 4706, 4705, 43808, 43811, 43813, 43812, 43810, + 43814, 43809, 11704, 11707, 11709, 11708, 11706, 11710, 11705, 11688, + 11691, 11693, 11692, 11690, 11694, 11689, 4904, 4907, 4910, 11664, 4909, + 4908, 4911, 4906, 4905, 4728, 4731, 4734, 11655, 4733, 4732, 4735, 4730, + 4729, 43789, 43788, 43787, 43786, 43790, 43785, 4856, 4859, 4862, 11661, + 4861, 4860, 4863, 4858, 4857, 43797, 43796, 43795, 43794, 43798, 43793, + 4848, 4851, 4854, 11660, 4853, 4852, 4855, 4850, 4849, 5003, 5002, 4943, + 5001, 4936, 4939, 4941, 4940, 4954, 4938, 4942, 4937, 4873, 124916, + 124915, 124924, 124923, 124910, 124909, 124926, 124925, 124922, 124921, + 124920, 124919, 124918, 124917, 124914, 124913, 124912, 124904, 11667, + 4895, 11670, 11669, 11668, 4888, 4891, 4893, 4892, 4890, 4894, 4889, + 4768, 4771, 4774, 11658, 4773, 4772, 4775, 4770, 4769, 4880, 4883, 4885, + 4884, 4882, 11736, 11739, 11741, 11740, 11738, 11742, 11737, 4872, 4875, + 4878, 4879, 4877, 4876, 4874, 124907, 124906, 4631, 124905, 124896, + 124899, 124901, 124900, 124898, 124902, 124897, 4624, 4627, 4629, 4628, + 4626, 4630, 4625, 4608, 4611, 4614, 4615, 4613, 4612, 4610, 4609, 4800, + 4803, 4805, 4804, 4802, 4792, 4795, 4797, 4796, 4794, 4798, 4793, 4784, + 4787, 4789, 4788, 4786, 11720, 11723, 11725, 11724, 11722, 11726, 11721, + 4776, 4779, 4782, 4783, 4781, 4780, 4778, 4777, 4995, 4994, 4639, 4993, + 4632, 4635, 4638, 11649, 4637, 4636, 4953, 4634, 4633, 4760, 4763, 4766, + 11657, 4765, 4764, 4767, 4762, 4761, 4752, 4755, 4758, 11656, 4757, 4756, + 4759, 4754, 4753, 4912, 4816, 4819, 4821, 4820, 4818, 4822, 4817, 4915, + 4918, 11665, 4917, 4916, 4919, 4914, 4913, 5007, 5006, 4951, 5005, 4944, + 4947, 4950, 11666, 4949, 4948, 4946, 4945, 4696, 4699, 4701, 4700, 4698, + 4688, 4691, 4693, 4692, 4690, 4694, 4689, 4680, 4683, 4685, 4684, 4682, + 11712, 11715, 11717, 11716, 11714, 11718, 11713, 4672, 4675, 4678, 4679, + 4677, 4676, 4674, 4673, 4648, 4651, 4654, 11650, 4653, 4652, 4655, 4952, + 4650, 4649, 4661, 4996, 5000, 4992, 5004, 4660, 4664, 4667, 4670, 11652, + 4669, 4668, 4671, 4666, 4665, 4640, 4643, 4645, 4644, 4647, 4642, 4646, + 4641, 11680, 11683, 11685, 11684, 11682, 11686, 11681, 4656, 4659, 4662, + 11651, 4663, 4658, 4657, 4896, 4899, 4902, 11663, 4901, 4900, 4903, 4898, + 4897, 43781, 43780, 43779, 43778, 43782, 43777, 4928, 4931, 4934, 4935, + 4933, 4932, 4930, 4929, 4920, 4923, 4925, 4924, 4927, 4922, 4926, 4921, + 4720, 4723, 4726, 11654, 4725, 4724, 4727, 4722, 4721, 4864, 4867, 4870, + 11662, 4869, 4868, 4871, 4866, 4865, 4616, 4619, 4622, 11648, 4621, 4620, + 4623, 4618, 4617, 4808, 4811, 4814, 4815, 4813, 4812, 4810, 4809, 4840, + 4843, 4846, 4847, 4845, 4844, 4842, 4841, 4744, 4747, 4749, 4748, 4746, + 11728, 11731, 11733, 11732, 11730, 11734, 11729, 4736, 4739, 4742, 4743, + 4741, 4740, 4738, 4737, 4832, 4835, 4837, 4836, 4839, 4834, 4838, 4833, + 11696, 11699, 11701, 11700, 11698, 11702, 11697, 4824, 4827, 4830, 11659, + 4829, 4828, 4831, 4826, 4825, 4712, 4715, 4717, 4716, 4719, 4714, 4718, + 4713, 5009, 5016, 5012, 5015, 5013, 5017, 5010, 5011, 5014, 5008, 4973, + 4972, 4975, 4974, 4971, 4970, 4977, 4969, 4976, 4962, 4967, 4961, 983096, + 983047, 127984, 127972, 8352, 8364, 118472, 8455, 8265, 33, 8761, 118269, + 118270, 118271, 118274, 128529, 128942, 128954, 128948, 128905, 128915, + 128935, 128125, 1781, 1780, 1783, 1782, 1779, 1778, 1776, 1785, 1777, + 1784, 128065, 128083, 128064, 128231, 9167, 127794, 983180, 128523, + 128561, 129312, 128531, 129301, 128567, 129488, 128582, 128558, 129326, + 128560, 129762, 129320, 128581, 129395, 129763, 129402, 128539, 128540, + 128541, 128514, 129298, 129769, 129323, 128580, 128548, 129764, 129396, + 128566, 129318, 128134, 128536, 129401, 8507, 127981, 10540, 10543, 9950, + 127810, 129478, 128439, 128224, 128106, 9771, 127877, 129498, 128552, + 129718, 170, 9792, 127905, 9972, 129338, 8210, 8199, 129775, 128193, + 128452, 983107, 128253, 127902, 129734, 10765, 128293, 128658, 129519, + 127879, 127878, 129512, 9789, 127771, 127763, 8296, 129351, 128031, + 127907, 9673, 127845, 128074, 8281, 11821, 127953, 129407, 129747, 9189, + 117910, 9971, 129449, 128170, 9884, 118466, 10086, 9880, 8277, 127924, + 128190, 128563, 129672, 129712, 128760, 117834, 117835, 118011, 129359, + 128389, 127787, 127745, 129709, 128448, 129462, 128099, 127860, 127869, + 11792, 10972, 129376, 118476, 983071, 8704, 8873, 10021, 11156, 8280, + 8283, 10019, 127808, 10018, 128966, 8732, 8197, 9970, 129749, 129418, + 8260, 8543, 128444, 128446, 128445, 118458, 127839, 8355, 129398, 10156, + 128037, 8994, 128550, 128056, 127844, 127773, 127765, 10199, 9608, 46, + 65342, 65312, 65292, 65306, 65504, 65375, 65371, 65288, 65339, 65308, 65313, 65314, 65315, 65316, 65317, 65318, 65319, 65320, 65321, 65322, 65323, 65324, 65325, 65326, 65327, 65328, 65329, 65330, 65331, 65332, 65333, 65334, 65335, 65336, 65337, 65338, 65345, 65346, 65347, 65348, @@ -10966,121 +10549,123 @@ static const unsigned int dawg_pos_to_codepoint[] = { 68983, 68987, 68988, 68976, 68943, 68969, 68941, 68938, 68939, 68940, 68942, 68975, 68974, 69006, 69007, 68933, 68932, 68935, 68934, 68931, 68930, 68928, 68937, 68929, 68936, 129476, 127922, 9881, 9965, 9966, - 128142, 9802, 8782, 8785, 8762, 4301, 4256, 4288, 4292, 4290, 4293, 4289, - 4281, 4285, 4284, 4282, 4278, 4258, 4287, 4283, 4277, 4265, 4276, 4270, - 4280, 4273, 4271, 4262, 4263, 4274, 4266, 4272, 4257, 4267, 4286, 4268, - 4279, 4261, 4259, 4260, 4264, 4269, 4275, 4295, 4291, 11565, 11520, - 11552, 11556, 11554, 11557, 11553, 11545, 11549, 11548, 11546, 11542, - 11522, 11551, 11547, 11541, 11529, 11540, 11534, 11544, 11537, 11535, - 11526, 11527, 11538, 11530, 11536, 11521, 11531, 11550, 11532, 11543, - 11525, 11523, 11524, 11528, 11533, 11539, 11559, 11555, 983955, 4323, - 4349, 4346, 4304, 4329, 4333, 4332, 4330, 4344, 4308, 4326, 4306, 4340, - 4350, 4336, 4338, 4341, 4337, 4335, 4331, 4325, 4313, 4351, 4314, 4324, - 4318, 4328, 4321, 4345, 4311, 4322, 4319, 4310, 4320, 4305, 4315, 4334, - 4316, 4327, 4309, 4307, 4312, 4317, 4343, 4339, 4342, 7357, 7354, 7312, - 7337, 7341, 7340, 7338, 7352, 7316, 7334, 7314, 7348, 7358, 7344, 7346, - 7349, 7345, 7343, 7339, 7333, 7321, 7359, 7322, 7332, 7326, 7336, 7329, - 7353, 7319, 7330, 7327, 7318, 7328, 7313, 7323, 7342, 7324, 7335, 7317, - 7315, 7320, 7325, 7331, 7351, 7347, 7350, 4347, 8368, 12307, 129502, - 8503, 129754, 128103, 128714, 129426, 11264, 11304, 11265, 11311, 11293, - 11276, 11268, 11271, 11287, 11306, 11267, 11275, 11274, 11305, 11303, - 11307, 11273, 11310, 11278, 11279, 11280, 11281, 11289, 11282, 11290, - 11283, 11291, 11308, 11294, 11284, 11298, 11300, 11301, 11285, 11309, - 11292, 11266, 11269, 11296, 11295, 11297, 11302, 11299, 11272, 11270, - 11288, 11286, 11277, 11312, 11352, 11313, 11359, 11341, 11324, 11316, - 11319, 11335, 11354, 11315, 11323, 11322, 11353, 11351, 11355, 11321, - 11358, 11326, 11327, 11328, 11329, 11337, 11330, 11338, 11331, 11339, - 11356, 11342, 11332, 11346, 11348, 11349, 11333, 11357, 11340, 11314, - 11317, 11344, 11343, 11345, 11350, 11347, 11320, 11318, 11336, 11334, - 11325, 129371, 127760, 127775, 129508, 10726, 129349, 128016, 66356, - 66352, 66376, 66359, 66358, 66375, 66378, 66369, 66365, 66368, 66357, - 66370, 66360, 66372, 66373, 66367, 66374, 66353, 66377, 66355, 66364, - 66371, 66361, 66363, 66366, 66354, 66362, 129421, 129405, 128893, 129727, - 127948, 127891, 70495, 70494, 70411, 70496, 70412, 70497, 70453, 70405, - 70406, 70416, 70420, 70434, 70433, 70439, 70438, 70432, 70431, 70437, - 70436, 70409, 70410, 70407, 70408, 70451, 70450, 70425, 70435, 70430, - 70440, 70454, 70455, 70456, 70445, 70444, 70427, 70426, 70424, 70423, - 70429, 70428, 70422, 70421, 70443, 70442, 70419, 70415, 70457, 70446, - 70448, 70447, 70400, 70401, 70460, 70461, 70402, 70493, 70477, 70403, - 70487, 70462, 70472, 70476, 70465, 70466, 70467, 70468, 70498, 70499, - 70463, 70464, 70475, 70471, 70480, 96, 127815, 10896, 10894, 10900, - 10892, 10898, 10616, 10890, 10888, 10917, 8935, 8809, 10878, 10882, - 10884, 10880, 10886, 8819, 8805, 8823, 10916, 8807, 8923, 10919, 10921, - 10874, 10876, 8919, 62, 65860, 65863, 65878, 65866, 65873, 65859, 65861, - 65868, 65875, 65862, 65870, 65864, 65871, 65867, 65874, 65857, 65869, - 65876, 65856, 65858, 65865, 65877, 65872, 65879, 65903, 65885, 65904, - 65907, 65908, 65900, 65883, 65886, 65896, 65890, 65882, 65880, 65891, - 65902, 65906, 65897, 65899, 65893, 65892, 65884, 65881, 65898, 65905, - 65887, 65901, 65894, 65895, 65888, 65889, 903, 65927, 65926, 913, 7945, - 7949, 8077, 7947, 8075, 7951, 8079, 8073, 7944, 7948, 8076, 7946, 8074, - 7950, 8078, 8072, 8124, 8120, 8122, 8123, 902, 8121, 882, 919, 7977, - 7981, 8093, 7979, 8091, 7983, 8095, 8089, 7976, 7980, 8092, 7978, 8090, - 7982, 8094, 8088, 8140, 8139, 8138, 905, 917, 7961, 7965, 7963, 7960, - 7964, 7962, 8137, 8136, 904, 921, 7993, 7999, 7997, 7995, 938, 7992, - 7998, 7996, 7994, 8152, 8154, 8155, 906, 8153, 937, 8041, 8045, 8109, - 8043, 8107, 8047, 8111, 8105, 8040, 8044, 8108, 8042, 8106, 8046, 8110, - 8104, 8188, 8187, 8186, 911, 927, 8009, 8013, 8011, 8008, 8012, 8010, - 8185, 8184, 908, 929, 8172, 931, 1018, 1015, 933, 8025, 8031, 8029, 8027, - 939, 8168, 8170, 8171, 910, 8169, 886, 934, 936, 928, 920, 932, 916, 922, - 915, 935, 914, 880, 918, 923, 895, 924, 925, 926, 1017, 1023, 1021, 1022, - 975, 1012, 8129, 8174, 8173, 901, 65915, 8190, 8159, 8158, 8157, 65920, - 65919, 119325, 119331, 119332, 119333, 119334, 119335, 119336, 119337, - 119326, 119338, 119339, 119340, 119341, 119342, 119343, 119344, 119345, - 119346, 119347, 119348, 119349, 119327, 119350, 119351, 119352, 119353, - 119354, 119355, 119356, 119328, 119357, 119358, 119359, 119360, 119361, - 119329, 119330, 65933, 1008, 983, 8125, 65922, 7466, 7464, 7462, 43877, - 7465, 7463, 992, 986, 984, 990, 988, 1011, 885, 1010, 1013, 65923, 884, - 65921, 119365, 65925, 65909, 65910, 65931, 8189, 65924, 65916, 8126, - 8127, 8143, 8142, 8141, 8128, 981, 982, 1020, 1009, 1014, 945, 8112, - 8048, 8114, 7937, 7941, 8069, 7943, 8071, 7939, 8067, 8065, 7936, 7940, - 8068, 7942, 8070, 7938, 8066, 8064, 8118, 8119, 8049, 8116, 8115, 940, - 8113, 985, 883, 989, 948, 949, 7953, 7957, 7955, 7952, 7956, 7954, 8051, - 8050, 941, 951, 7969, 7973, 8085, 7975, 8087, 7971, 8083, 8081, 7968, - 7972, 8084, 7974, 8086, 7970, 8082, 8080, 8134, 8135, 8053, 8132, 8052, - 8130, 8131, 942, 953, 7985, 7991, 7989, 7987, 970, 8151, 8147, 8146, 912, - 7984, 7990, 7988, 7986, 8150, 8144, 8054, 8055, 943, 8145, 965, 8017, - 8023, 8021, 8019, 971, 8167, 8163, 8162, 944, 8016, 8022, 8020, 8018, - 8166, 8160, 8058, 8059, 973, 8161, 954, 991, 969, 8033, 8037, 8101, 8039, - 8103, 8035, 8099, 8097, 8032, 8036, 8100, 8038, 8102, 8034, 8098, 8096, - 8182, 8183, 8061, 8180, 8060, 8178, 8179, 974, 959, 8001, 8005, 8003, - 8000, 8004, 8002, 8057, 8056, 972, 887, 966, 968, 960, 961, 8165, 8164, - 993, 1019, 987, 963, 1016, 952, 964, 962, 947, 967, 946, 881, 950, 955, - 956, 957, 958, 893, 891, 892, 7527, 7530, 7529, 7528, 7526, 65952, 65932, - 65918, 65912, 977, 65929, 65917, 65911, 900, 65914, 979, 980, 978, 8175, - 119297, 119315, 119316, 119317, 119318, 119319, 119300, 119320, 119321, - 119322, 119323, 119324, 119296, 119305, 119306, 119307, 119308, 119309, - 119310, 119311, 119312, 119313, 119314, 119298, 119299, 119301, 119302, - 119303, 119304, 890, 65913, 976, 65928, 65930, 894, 127823, 128215, - 128154, 129367, 129654, 128512, 129322, 129321, 128513, 128568, 128556, - 983110, 11218, 128151, 8370, 128130, 127928, 129454, 2693, 2694, 2704, - 2708, 2722, 2721, 2727, 2726, 2720, 2719, 2725, 2724, 2699, 2784, 2700, - 2785, 2741, 2697, 2698, 2695, 2696, 2739, 2738, 2809, 2713, 2723, 2718, - 2728, 2742, 2743, 2744, 2733, 2732, 2715, 2714, 2712, 2711, 2717, 2716, - 2710, 2709, 2731, 2730, 2745, 2734, 2736, 2735, 2703, 2707, 2814, 2689, - 2812, 2815, 2813, 2811, 2810, 2748, 2749, 2690, 2765, 2691, 2757, 2761, - 2750, 2760, 2764, 2753, 2754, 2755, 2756, 2786, 2787, 2751, 2752, 2759, - 2763, 2701, 2705, 2800, 2801, 2795, 2794, 2797, 2796, 2793, 2792, 2790, - 2799, 2791, 2798, 2768, 73092, 73082, 73056, 73057, 73064, 73067, 73091, - 73090, 73081, 73080, 73086, 73085, 73076, 73075, 73060, 73061, 73058, - 73059, 73087, 73077, 73071, 73070, 73084, 73083, 73079, 73078, 73089, - 73088, 73074, 73073, 73094, 73093, 73066, 73063, 73095, 73072, 73096, - 73097, 73069, 73068, 73110, 73109, 73098, 73105, 73108, 73101, 73102, - 73099, 73100, 73107, 73104, 73111, 73125, 73124, 73127, 73126, 73123, - 73122, 73120, 73129, 73121, 73128, 73112, 2678, 2673, 2650, 2584, 2583, - 2649, 2582, 2581, 2565, 2566, 2576, 2580, 2594, 2593, 2599, 2598, 2652, - 2608, 2592, 2591, 2597, 2596, 2569, 2570, 2567, 2568, 2611, 2610, 2585, - 2595, 2590, 2600, 2605, 2604, 2587, 2586, 2589, 2588, 2603, 2602, 2614, - 2616, 2579, 2575, 2654, 2617, 2606, 2613, 2607, 2651, 983656, 983655, - 983654, 983653, 983658, 983657, 2561, 2562, 2641, 2620, 2677, 2637, 2563, - 2622, 2632, 2636, 2625, 2626, 2623, 2624, 2635, 2631, 2672, 2674, 2676, - 2667, 2666, 2669, 2668, 2665, 2664, 2662, 2671, 2663, 2670, 2675, 90412, - 90414, 90411, 90410, 90373, 90388, 90382, 90381, 90387, 90386, 90380, - 90379, 90385, 90384, 90392, 90391, 90375, 90374, 90372, 90371, 90377, - 90376, 90370, 90369, 90390, 90389, 90378, 90396, 90393, 90395, 90397, - 90383, 90394, 90368, 90413, 90415, 90398, 90405, 90408, 90401, 90402, - 90406, 90407, 90399, 90400, 90403, 90404, 90409, 90421, 90420, 90423, - 90422, 90419, 90418, 90416, 90425, 90417, 90424, 128123, 983111, 129710, - 8202, 128135, 65467, 65441, 65443, 65444, 65445, 65446, 65469, 65458, + 128142, 9802, 118501, 118506, 118498, 118503, 118510, 118505, 118502, + 118508, 118499, 118504, 118497, 118507, 118509, 118496, 118500, 118511, + 8782, 8785, 8762, 4301, 4256, 4288, 4292, 4290, 4293, 4289, 4281, 4285, + 4284, 4282, 4278, 4258, 4287, 4283, 4277, 4265, 4276, 4270, 4280, 4273, + 4271, 4262, 4263, 4274, 4266, 4272, 4257, 4267, 4286, 4268, 4279, 4261, + 4259, 4260, 4264, 4269, 4275, 4295, 4291, 11565, 11520, 11552, 11556, + 11554, 11557, 11553, 11545, 11549, 11548, 11546, 11542, 11522, 11551, + 11547, 11541, 11529, 11540, 11534, 11544, 11537, 11535, 11526, 11527, + 11538, 11530, 11536, 11521, 11531, 11550, 11532, 11543, 11525, 11523, + 11524, 11528, 11533, 11539, 11559, 11555, 983955, 4323, 4349, 4346, 4304, + 4329, 4333, 4332, 4330, 4344, 4308, 4326, 4306, 4340, 4350, 4336, 4338, + 4341, 4337, 4335, 4331, 4325, 4313, 4351, 4314, 4324, 4318, 4328, 4321, + 4345, 4311, 4322, 4319, 4310, 4320, 4305, 4315, 4334, 4316, 4327, 4309, + 4307, 4312, 4317, 4343, 4339, 4342, 7357, 7354, 7312, 7337, 7341, 7340, + 7338, 7352, 7316, 7334, 7314, 7348, 7358, 7344, 7346, 7349, 7345, 7343, + 7339, 7333, 7321, 7359, 7322, 7332, 7326, 7336, 7329, 7353, 7319, 7330, + 7327, 7318, 7328, 7313, 7323, 7342, 7324, 7335, 7317, 7315, 7320, 7325, + 7331, 7351, 7347, 7350, 4347, 8368, 12307, 129502, 8503, 129754, 128103, + 128714, 129426, 11264, 11304, 11265, 11311, 11293, 11276, 11268, 11271, + 11287, 11306, 11267, 11275, 11274, 11305, 11303, 11307, 11273, 11310, + 11278, 11279, 11280, 11281, 11289, 11282, 11290, 11283, 11291, 11308, + 11294, 11284, 11298, 11300, 11301, 11285, 11309, 11292, 11266, 11269, + 11296, 11295, 11297, 11302, 11299, 11272, 11270, 11288, 11286, 11277, + 11312, 11352, 11313, 11359, 11341, 11324, 11316, 11319, 11335, 11354, + 11315, 11323, 11322, 11353, 11351, 11355, 11321, 11358, 11326, 11327, + 11328, 11329, 11337, 11330, 11338, 11331, 11339, 11356, 11342, 11332, + 11346, 11348, 11349, 11333, 11357, 11340, 11314, 11317, 11344, 11343, + 11345, 11350, 11347, 11320, 11318, 11336, 11334, 11325, 129371, 127760, + 127775, 129508, 10726, 129349, 128016, 66356, 66352, 66376, 66359, 66358, + 66375, 66378, 66369, 66365, 66368, 66357, 66370, 66360, 66372, 66373, + 66367, 66374, 66353, 66377, 66355, 66364, 66361, 66363, 66371, 66366, + 66354, 66362, 129421, 129405, 128893, 129727, 127948, 127891, 70495, + 70494, 70411, 70496, 70412, 70497, 70453, 70405, 70406, 70416, 70420, + 70434, 70433, 70439, 70438, 70432, 70431, 70437, 70436, 70409, 70410, + 70407, 70408, 70451, 70450, 70425, 70435, 70430, 70440, 70454, 70455, + 70456, 70445, 70444, 70427, 70426, 70424, 70423, 70429, 70428, 70422, + 70421, 70443, 70442, 70419, 70415, 70457, 70446, 70448, 70447, 70400, + 70401, 70460, 70461, 70402, 70493, 70477, 70403, 70487, 70462, 70472, + 70476, 70465, 70466, 70467, 70468, 70498, 70499, 70463, 70464, 70475, + 70471, 70480, 96, 127815, 10896, 10894, 10900, 10892, 10898, 10616, + 10890, 10888, 10917, 8935, 8809, 10878, 10882, 10884, 10880, 10886, 8819, + 8805, 8823, 10916, 8807, 8923, 10919, 10921, 10874, 10876, 8919, 62, + 65860, 65863, 65878, 65866, 65873, 65859, 65861, 65868, 65875, 65862, + 65870, 65864, 65871, 65867, 65874, 65857, 65869, 65876, 65856, 65858, + 65865, 65877, 65872, 65879, 65903, 65885, 65904, 65907, 65908, 65900, + 65883, 65886, 65896, 65890, 65882, 65880, 65891, 65902, 65906, 65897, + 65899, 65893, 65892, 65884, 65881, 65898, 65905, 65887, 65901, 65894, + 65895, 65888, 65889, 903, 65927, 65926, 913, 7945, 7949, 8077, 7947, + 8075, 7951, 8079, 8073, 7944, 7948, 8076, 7946, 8074, 7950, 8078, 8072, + 8124, 8120, 8122, 8123, 902, 8121, 882, 919, 7977, 7981, 8093, 7979, + 8091, 7983, 8095, 8089, 7976, 7980, 8092, 7978, 8090, 7982, 8094, 8088, + 8140, 8139, 8138, 905, 917, 7961, 7965, 7963, 7960, 7964, 7962, 8137, + 8136, 904, 921, 7993, 7999, 7997, 7995, 938, 7992, 7998, 7996, 7994, + 8152, 8154, 8155, 906, 8153, 937, 8041, 8045, 8109, 8043, 8107, 8047, + 8111, 8105, 8040, 8044, 8108, 8042, 8106, 8046, 8110, 8104, 8188, 8187, + 8186, 911, 927, 8009, 8013, 8011, 8008, 8012, 8010, 8185, 8184, 908, 929, + 8172, 931, 1018, 1015, 933, 8025, 8031, 8029, 8027, 939, 8168, 8170, + 8171, 910, 8169, 886, 934, 936, 928, 920, 932, 916, 922, 915, 935, 914, + 880, 918, 923, 895, 924, 925, 926, 1017, 1023, 1021, 1022, 975, 1012, + 8129, 8174, 8173, 901, 65915, 8190, 8159, 8158, 8157, 65920, 65919, + 119325, 119331, 119332, 119333, 119334, 119335, 119336, 119337, 119326, + 119338, 119339, 119340, 119341, 119342, 119343, 119344, 119345, 119346, + 119347, 119348, 119349, 119327, 119350, 119351, 119352, 119353, 119354, + 119355, 119356, 119328, 119357, 119358, 119359, 119360, 119361, 119329, + 119330, 65933, 1008, 983, 8125, 65922, 7466, 7464, 7462, 43877, 7465, + 7463, 992, 986, 984, 990, 988, 1011, 885, 1010, 1013, 65923, 884, 65921, + 119365, 65925, 65909, 65910, 65931, 8189, 65924, 65916, 8126, 8127, 8143, + 8142, 8141, 8128, 981, 982, 1020, 1009, 1014, 945, 8112, 8048, 8114, + 7937, 7941, 8069, 7943, 8071, 7939, 8067, 8065, 7936, 7940, 8068, 7942, + 8070, 7938, 8066, 8064, 8118, 8119, 8049, 8116, 8115, 940, 8113, 985, + 883, 989, 948, 949, 7953, 7957, 7955, 7952, 7956, 7954, 8051, 8050, 941, + 951, 7969, 7973, 8085, 7975, 8087, 7971, 8083, 8081, 7968, 7972, 8084, + 7974, 8086, 7970, 8082, 8080, 8134, 8135, 8053, 8132, 8052, 8130, 8131, + 942, 953, 7985, 7991, 7989, 7987, 970, 8151, 8147, 8146, 912, 7984, 7990, + 7988, 7986, 8150, 8144, 8054, 8055, 943, 8145, 965, 8017, 8023, 8021, + 8019, 971, 8167, 8163, 8162, 944, 8016, 8022, 8020, 8018, 8166, 8160, + 8058, 8059, 973, 8161, 954, 991, 969, 8033, 8037, 8101, 8039, 8103, 8035, + 8099, 8097, 8032, 8036, 8100, 8038, 8102, 8034, 8098, 8096, 8182, 8183, + 8061, 8180, 8060, 8178, 8179, 974, 959, 8001, 8005, 8003, 8000, 8004, + 8002, 8057, 8056, 972, 887, 966, 968, 960, 961, 8165, 8164, 993, 1019, + 987, 963, 1016, 952, 964, 962, 947, 967, 946, 881, 950, 955, 956, 957, + 958, 893, 891, 892, 7527, 7530, 7529, 7528, 7526, 65952, 65932, 65918, + 65912, 977, 65929, 65917, 65911, 900, 65914, 979, 980, 978, 8175, 119297, + 119315, 119316, 119317, 119318, 119319, 119300, 119320, 119321, 119322, + 119323, 119324, 119296, 119305, 119306, 119307, 119308, 119309, 119310, + 119311, 119312, 119313, 119314, 119298, 119299, 119301, 119302, 119303, + 119304, 890, 65913, 976, 65928, 65930, 894, 127823, 128215, 128154, + 129367, 129654, 128512, 129322, 129321, 128513, 128568, 128556, 983110, + 11218, 128151, 8370, 128130, 127928, 129454, 2693, 2694, 2704, 2708, + 2722, 2721, 2727, 2726, 2720, 2719, 2725, 2724, 2699, 2784, 2700, 2785, + 2741, 2697, 2698, 2695, 2696, 2739, 2738, 2809, 2713, 2723, 2718, 2728, + 2742, 2743, 2744, 2733, 2732, 2715, 2714, 2712, 2711, 2717, 2716, 2710, + 2709, 2731, 2730, 2745, 2734, 2736, 2735, 2703, 2707, 2814, 2689, 2812, + 2815, 2813, 2811, 2810, 2748, 2749, 2690, 2765, 2691, 2757, 2761, 2750, + 2760, 2764, 2753, 2754, 2755, 2756, 2786, 2787, 2751, 2752, 2759, 2763, + 2701, 2705, 2800, 2801, 2795, 2794, 2797, 2796, 2793, 2792, 2790, 2799, + 2791, 2798, 2768, 73092, 73082, 73056, 73057, 73064, 73067, 73091, 73090, + 73081, 73080, 73086, 73085, 73076, 73075, 73060, 73061, 73058, 73059, + 73087, 73077, 73071, 73070, 73084, 73083, 73079, 73078, 73089, 73088, + 73074, 73073, 73094, 73093, 73066, 73063, 73095, 73072, 73096, 73097, + 73069, 73068, 73110, 73109, 73098, 73105, 73108, 73101, 73102, 73099, + 73100, 73107, 73104, 73111, 73125, 73124, 73127, 73126, 73123, 73122, + 73120, 73129, 73121, 73128, 73112, 2678, 2673, 2650, 2584, 2583, 2649, + 2582, 2581, 2565, 2566, 2576, 2580, 2594, 2593, 2599, 2598, 2652, 2608, + 2592, 2591, 2597, 2596, 2569, 2570, 2567, 2568, 2611, 2610, 2585, 2595, + 2590, 2600, 2605, 2604, 2587, 2586, 2589, 2588, 2603, 2602, 2614, 2616, + 2579, 2575, 2654, 2617, 2606, 2613, 2607, 2651, 983656, 983655, 983654, + 983653, 983658, 983657, 2561, 2562, 2641, 2620, 2677, 2637, 2563, 2622, + 2632, 2636, 2625, 2626, 2623, 2624, 2635, 2631, 2672, 2674, 2676, 2667, + 2666, 2669, 2668, 2665, 2664, 2662, 2671, 2663, 2670, 2675, 90412, 90414, + 90411, 90410, 90373, 90388, 90382, 90381, 90387, 90386, 90380, 90379, + 90385, 90384, 90392, 90391, 90375, 90374, 90372, 90371, 90377, 90376, + 90370, 90369, 90390, 90389, 90378, 90396, 90393, 90395, 90397, 90383, + 90394, 90368, 90413, 90415, 90398, 90405, 90408, 90401, 90402, 90406, + 90407, 90399, 90400, 90403, 90404, 90409, 90421, 90420, 90423, 90422, + 90419, 90418, 90416, 90425, 90417, 90424, 128123, 983111, 129710, 8202, + 129736, 128135, 65467, 65441, 65443, 65444, 65445, 65446, 65469, 65458, 65460, 65449, 65454, 65455, 65452, 65451, 65450, 65456, 65453, 65465, 65442, 65459, 65448, 65462, 65461, 65483, 65482, 65476, 65477, 65499, 65490, 65495, 65466, 65464, 65479, 65478, 65498, 65500, 65463, 65457, @@ -11169,81 +10754,81 @@ static const unsigned int dawg_pos_to_codepoint[] = { 64292, 64291, 64294, 1497, 64285, 64313, 64335, 1520, 1522, 64287, 1521, 1477, 1476, 1455, 1468, 1458, 1459, 1457, 1460, 1465, 1466, 1463, 1464, 1479, 1467, 1471, 1462, 1473, 1456, 1474, 1461, 64286, 1469, 1524, 1523, - 1472, 1475, 1478, 1470, 1519, 9937, 9096, 11263, 128641, 110597, 110594, - 110595, 110596, 110778, 110779, 110780, 110781, 110782, 110783, 110784, - 110785, 110750, 110759, 110760, 110751, 110752, 110753, 110754, 110755, - 110756, 110757, 110758, 110768, 110769, 110770, 110771, 110772, 110773, - 110774, 110775, 110776, 110777, 110761, 110762, 110763, 110764, 110765, - 110766, 110767, 110615, 110624, 110625, 110626, 110616, 110617, 110618, - 110619, 110620, 110621, 110622, 110623, 110651, 110648, 110649, 110650, - 110627, 110628, 110629, 110630, 110631, 110632, 110633, 110634, 110642, - 110643, 110644, 110645, 110646, 110647, 110635, 110636, 110637, 110638, - 110639, 110640, 110641, 110806, 110804, 110805, 110807, 110808, 110809, - 110810, 110811, 110812, 110786, 110787, 110788, 110789, 110790, 110791, - 110792, 110793, 110794, 110795, 110796, 110797, 110798, 110799, 110800, - 110801, 110802, 110803, 110744, 110738, 110739, 110740, 110741, 110742, - 110743, 110734, 110727, 110728, 110729, 110730, 110731, 110732, 110733, - 110718, 110719, 110720, 110721, 110722, 110723, 110724, 110725, 110726, - 110745, 110746, 110747, 110748, 110749, 110735, 110736, 110737, 110877, - 110878, 110850, 110851, 110852, 110853, 110854, 110855, 110840, 110841, - 110842, 110843, 110844, 110845, 110833, 110834, 110835, 110836, 110837, - 110838, 110839, 110829, 110830, 110831, 110832, 110846, 110847, 110848, - 110849, 110652, 110653, 110654, 110655, 110656, 110657, 110658, 110659, - 110666, 110667, 110668, 110669, 110670, 110671, 110672, 110673, 110660, - 110661, 110662, 110663, 110664, 110665, 110674, 110675, 110676, 110677, - 110678, 110679, 110680, 110681, 110682, 110683, 110684, 110685, 110702, - 110703, 110704, 110705, 110706, 110707, 110708, 110709, 110710, 110717, - 110711, 110712, 110713, 110714, 110715, 110716, 110701, 110697, 110698, - 110699, 110700, 110690, 110691, 110692, 110693, 110694, 110695, 110696, - 110686, 110687, 110688, 110689, 110856, 110857, 110858, 110859, 110860, - 110861, 110862, 110863, 110864, 110865, 110870, 110871, 110872, 110873, - 110874, 110875, 110876, 110866, 110867, 110868, 110869, 110818, 110813, - 110814, 110815, 110816, 110817, 110823, 110824, 110825, 110826, 110827, - 110828, 110819, 110820, 110821, 110822, 983273, 110607, 110608, 110609, - 110610, 110611, 110602, 110603, 110604, 110605, 110606, 110612, 110613, - 110614, 110598, 110599, 110600, 110601, 8889, 127807, 19922, 19966, - 19958, 19967, 19924, 19946, 19923, 19909, 19947, 19944, 19943, 19956, - 19906, 19962, 19935, 19939, 19920, 19916, 19948, 19917, 19937, 19931, - 19929, 19925, 19911, 19928, 19964, 19945, 19934, 19918, 19930, 19942, - 19950, 19941, 19949, 19938, 19914, 19927, 19936, 19952, 19965, 19912, - 19915, 19926, 19954, 19910, 19932, 19953, 19904, 19933, 19940, 19905, - 19951, 19959, 19957, 19960, 19955, 19961, 19913, 19908, 19921, 19963, - 19907, 19919, 129428, 128262, 9889, 983123, 128644, 128645, 128096, - 12447, 12354, 110879, 110593, 12430, 110929, 110928, 110930, 12419, - 12423, 12421, 12437, 12438, 110898, 12387, 12353, 12359, 12355, 12361, - 12357, 12373, 12379, 12375, 12381, 12377, 12403, 983997, 984000, 983998, - 984001, 983999, 12400, 12409, 12412, 12406, 12435, 12394, 12397, 12395, - 12398, 12396, 12384, 12391, 12386, 12393, 12389, 12364, 12370, 12366, - 12372, 12368, 12399, 12408, 12402, 12411, 12405, 12363, 12369, 12365, - 12371, 12367, 12414, 12417, 12415, 12418, 12416, 12401, 12410, 12404, - 12413, 12407, 12425, 12428, 12426, 12429, 12427, 12383, 12390, 12385, - 12392, 12388, 12374, 12380, 12376, 12382, 12378, 12431, 12433, 12432, - 12434, 12420, 12424, 12422, 12436, 12360, 12356, 12362, 12358, 12446, - 12445, 9964, 128725, 129406, 127802, 129435, 128616, 128617, 128371, - 128029, 127855, 11203, 11043, 8213, 118290, 118287, 117905, 9135, 117893, - 129921, 129910, 129911, 129912, 129913, 129914, 129915, 9146, 9147, 9148, - 9149, 983059, 983141, 983138, 11134, 128677, 117779, 8230, 9897, 117915, - 117769, 118448, 128014, 127943, 128052, 127798, 9749, 9832, 127789, - 127976, 8987, 9203, 8962, 127969, 127968, 127960, 127973, 128298, 8763, - 129693, 983124, 983060, 983142, 983139, 128559, 128175, 129303, 128726, - 8208, 11802, 8259, 8231, 45, 11794, 9102, 11226, 129723, 8372, 127848, - 129482, 127954, 9976, 8801, 10725, 10855, 129706, 12696, 12700, 12693, - 12697, 12695, 12703, 12692, 12699, 12691, 12694, 12688, 12698, 12690, - 12689, 12701, 12702, 12294, 12289, 12275, 12273, 12274, 12272, 12283, - 12282, 12285, 12279, 12280, 12281, 12278, 12277, 12284, 12783, 12287, - 12276, 12286, 12332, 12295, 119670, 119669, 119668, 119667, 119666, - 12999, 12995, 13309, 13310, 13292, 13282, 13299, 13304, 13303, 13306, - 13305, 13302, 13301, 13308, 13300, 13307, 13291, 13281, 13289, 13287, - 13297, 13290, 13294, 13284, 13283, 13293, 13288, 13298, 13286, 13296, - 13285, 13295, 13280, 13003, 13164, 13168, 13167, 13166, 13165, 13156, - 13146, 13157, 13147, 13154, 13152, 13162, 13155, 13159, 13149, 13148, - 13158, 13153, 13163, 13151, 13161, 13150, 13160, 13144, 13145, 12992, - 12997, 12998, 12993, 12994, 12996, 13002, 13000, 13001, 12343, 12351, - 12333, 12331, 12330, 12350, 12290, 12293, 12288, 8887, 8787, 128127, - 67676, 67673, 67675, 67679, 67674, 67672, 67677, 67678, 67656, 67669, - 67651, 67659, 67666, 67667, 67648, 67663, 67650, 67654, 67662, 67665, - 67668, 67657, 67652, 67655, 67658, 67649, 67661, 67653, 67664, 67660, - 67671, 128232, 10716, 128474, 10721, 8710, 983130, 983129, 129781, + 1472, 1475, 1478, 1470, 1519, 118464, 9937, 9096, 11263, 128641, 110597, + 110594, 110595, 110596, 110778, 110779, 110780, 110781, 110782, 110783, + 110784, 110785, 110750, 110759, 110760, 110751, 110752, 110753, 110754, + 110755, 110756, 110757, 110758, 110768, 110769, 110770, 110771, 110772, + 110773, 110774, 110775, 110776, 110777, 110761, 110762, 110763, 110764, + 110765, 110766, 110767, 110615, 110624, 110625, 110626, 110616, 110617, + 110618, 110619, 110620, 110621, 110622, 110623, 110651, 110648, 110649, + 110650, 110627, 110628, 110629, 110630, 110631, 110632, 110633, 110634, + 110642, 110643, 110644, 110645, 110646, 110647, 110635, 110636, 110637, + 110638, 110639, 110640, 110641, 110806, 110804, 110805, 110807, 110808, + 110809, 110810, 110811, 110812, 110786, 110787, 110788, 110789, 110790, + 110791, 110792, 110793, 110794, 110795, 110796, 110797, 110798, 110799, + 110800, 110801, 110802, 110803, 110744, 110738, 110739, 110740, 110741, + 110742, 110743, 110734, 110727, 110728, 110729, 110730, 110731, 110732, + 110733, 110718, 110719, 110720, 110721, 110722, 110723, 110724, 110725, + 110726, 110745, 110746, 110747, 110748, 110749, 110735, 110736, 110737, + 110877, 110878, 110850, 110851, 110852, 110853, 110854, 110855, 110840, + 110841, 110842, 110843, 110844, 110845, 110833, 110834, 110835, 110836, + 110837, 110838, 110839, 110829, 110830, 110831, 110832, 110846, 110847, + 110848, 110849, 110652, 110653, 110654, 110655, 110656, 110657, 110658, + 110659, 110666, 110667, 110668, 110669, 110670, 110671, 110672, 110673, + 110660, 110661, 110662, 110663, 110664, 110665, 110674, 110675, 110676, + 110677, 110678, 110679, 110680, 110681, 110682, 110683, 110684, 110685, + 110702, 110703, 110704, 110705, 110706, 110707, 110708, 110709, 110710, + 110717, 110711, 110712, 110713, 110714, 110715, 110716, 110701, 110697, + 110698, 110699, 110700, 110690, 110691, 110692, 110693, 110694, 110695, + 110696, 110686, 110687, 110688, 110689, 110856, 110857, 110858, 110859, + 110860, 110861, 110862, 110863, 110864, 110865, 110870, 110871, 110872, + 110873, 110874, 110875, 110876, 110866, 110867, 110868, 110869, 110818, + 110813, 110814, 110815, 110816, 110817, 110823, 110824, 110825, 110826, + 110827, 110828, 110819, 110820, 110821, 110822, 983277, 110607, 110608, + 110609, 110610, 110611, 110602, 110603, 110604, 110605, 110606, 110612, + 110613, 110614, 110598, 110599, 110600, 110601, 8889, 127807, 19922, + 19966, 19958, 19967, 19924, 19946, 19923, 19909, 19947, 19944, 19943, + 19956, 19906, 19962, 19935, 19939, 19920, 19916, 19948, 19917, 19937, + 19931, 19929, 19925, 19911, 19928, 19964, 19945, 19934, 19918, 19930, + 19942, 19950, 19941, 19949, 19938, 19914, 19927, 19936, 19952, 19965, + 19912, 19915, 19926, 19954, 19910, 19932, 19953, 19904, 19933, 19940, + 19905, 19951, 19959, 19957, 19960, 19955, 19961, 19913, 19908, 19921, + 19963, 19907, 19919, 129428, 128262, 9889, 983123, 128644, 128645, + 128096, 12447, 12354, 110879, 110593, 12430, 110929, 110928, 110930, + 12419, 12423, 12421, 12437, 12438, 110898, 12387, 12353, 12359, 12355, + 12361, 12357, 12373, 12379, 12375, 12381, 12377, 12403, 983997, 984000, + 983998, 984001, 983999, 12400, 12409, 12412, 12406, 12435, 12394, 12397, + 12395, 12398, 12396, 12384, 12391, 12386, 12393, 12389, 12364, 12370, + 12366, 12372, 12368, 12399, 12408, 12402, 12411, 12405, 12363, 12369, + 12365, 12371, 12367, 12414, 12417, 12415, 12418, 12416, 12401, 12410, + 12404, 12413, 12407, 12425, 12428, 12426, 12429, 12427, 12383, 12390, + 12385, 12392, 12388, 12374, 12380, 12376, 12382, 12378, 12431, 12433, + 12432, 12434, 12420, 12424, 12422, 12436, 12360, 12356, 12362, 12358, + 12446, 12445, 9964, 128725, 129406, 127802, 129435, 128616, 128617, + 128371, 128029, 127855, 11203, 11043, 8213, 118290, 118287, 117905, 9135, + 117893, 129921, 129910, 129911, 129912, 129913, 129914, 129915, 9146, + 9147, 9148, 9149, 983059, 983141, 983138, 11134, 128677, 117779, 8230, + 9897, 117915, 117769, 118448, 128014, 127943, 128052, 127798, 9749, 9832, + 127789, 127976, 8987, 9203, 8962, 127969, 127968, 127960, 127973, 128298, + 8763, 129693, 983124, 983060, 983142, 983139, 128559, 128175, 129303, + 128726, 11226, 128889, 8208, 11802, 8259, 8231, 45, 11794, 9102, 129723, + 8372, 127848, 129482, 127954, 9976, 8801, 10725, 10855, 129706, 12696, + 12700, 12693, 12697, 12695, 12703, 12692, 12699, 12691, 12694, 12688, + 12698, 12690, 12689, 12701, 12702, 12294, 12289, 12275, 12273, 12274, + 12272, 12283, 12282, 12285, 12279, 12280, 12281, 12278, 12277, 12284, + 12783, 12287, 12276, 12286, 12332, 12295, 119670, 119669, 119668, 119667, + 119666, 12999, 12995, 13309, 13310, 13292, 13282, 13299, 13304, 13303, + 13306, 13305, 13302, 13301, 13308, 13300, 13307, 13291, 13281, 13289, + 13287, 13297, 13290, 13294, 13284, 13283, 13293, 13288, 13298, 13286, + 13296, 13285, 13295, 13280, 13003, 13164, 13168, 13167, 13166, 13165, + 13156, 13146, 13157, 13147, 13154, 13152, 13162, 13155, 13159, 13149, + 13148, 13158, 13153, 13163, 13151, 13161, 13150, 13160, 13144, 13145, + 12992, 12997, 12998, 12993, 12994, 12996, 13002, 13000, 13001, 12343, + 12351, 12333, 12331, 12330, 12350, 12290, 12293, 12288, 8887, 8787, + 128127, 67676, 67673, 67675, 67679, 67674, 67672, 67677, 67678, 67656, + 67669, 67651, 67659, 67666, 67667, 67648, 67663, 67650, 67654, 67662, + 67665, 67668, 67657, 67652, 67655, 67658, 67649, 67661, 67653, 67664, + 67660, 67671, 128232, 10716, 128474, 10721, 8710, 983130, 983129, 129781, 126132, 126112, 126126, 126125, 126127, 126131, 126130, 126129, 126113, 126114, 126110, 126111, 126072, 126081, 126108, 126090, 126099, 126078, 126105, 126069, 126087, 126096, 126077, 126104, 126068, 126086, 126095, @@ -11263,1112 +10848,1018 @@ static const unsigned int dawg_pos_to_codepoint[] = { 9134, 65529, 65531, 65530, 9892, 8745, 10825, 10823, 10820, 10819, 10816, 10827, 8253, 8890, 10812, 117901, 117903, 9688, 129942, 129969, 129972, 9689, 129936, 11800, 11845, 11846, 8766, 9959, 161, 8487, 8276, 191, - 8290, 8292, 8291, 128229, 127982, 129311, 127875, 127983, 127971, 12292, - 9979, 127886, 128304, 128122, 128121, 43453, 43454, 43455, 43457, 43421, - 43422, 43426, 43427, 43398, 43397, 43399, 43407, 43408, 43409, 43412, - 43402, 43403, 43418, 43416, 43428, 43423, 43431, 43432, 43413, 43414, - 43410, 43411, 43429, 43430, 43401, 43435, 43436, 43441, 43439, 43440, - 43424, 43425, 43419, 43420, 43415, 43417, 43396, 43405, 43442, 43437, - 43433, 43438, 43434, 43404, 43406, 43400, 43458, 43466, 43467, 43459, - 43465, 43461, 43464, 43468, 43463, 43486, 43462, 43487, 43460, 43471, - 43456, 43469, 43393, 43443, 43392, 43395, 43394, 43448, 43449, 43444, - 43450, 43445, 43446, 43447, 43452, 43451, 43477, 43476, 43479, 43478, - 43475, 43474, 43472, 43481, 43473, 43480, 129753, 129724, 128086, 128377, - 10781, 129337, 9795, 9909, 129513, 69786, 69787, 69785, 69793, 69792, - 69763, 69764, 69770, 69772, 69784, 69783, 69791, 69790, 69767, 69768, - 69765, 69766, 69777, 69789, 69782, 69794, 69804, 69805, 69806, 69798, - 69797, 69779, 69778, 69776, 69775, 69781, 69780, 69774, 69773, 69796, - 69795, 69788, 69801, 69807, 69802, 69799, 69803, 69800, 69769, 69771, - 69760, 69818, 69761, 69817, 69762, 69822, 69823, 69825, 69824, 69826, - 69808, 69814, 69816, 69811, 69812, 69809, 69810, 69813, 69815, 69821, - 69837, 69819, 69820, 12142, 12164, 12060, 12157, 12100, 12149, 12184, - 12191, 12227, 12068, 12174, 12234, 12205, 12134, 12168, 12219, 12189, - 12088, 12090, 12096, 12160, 12182, 12224, 12190, 12147, 12114, 12118, - 12058, 12176, 12075, 12112, 12045, 12170, 12124, 12070, 12194, 12109, - 12229, 12196, 12139, 12056, 12099, 12034, 12084, 12136, 12120, 12044, - 12111, 12094, 12125, 12243, 12238, 12082, 12159, 12063, 12215, 12062, - 12042, 12241, 12067, 12235, 12043, 12140, 12119, 12207, 12123, 12133, - 12222, 12117, 12226, 12245, 12214, 12217, 12236, 12155, 12188, 12113, - 12065, 12198, 12066, 12146, 12171, 12225, 12200, 12121, 12093, 12095, - 12221, 12092, 12216, 12231, 12054, 12218, 12179, 12037, 12173, 12072, - 12046, 12127, 12152, 12049, 12074, 12107, 12208, 12212, 12041, 12210, - 12131, 12033, 12039, 12199, 12085, 12128, 12161, 12162, 12233, 12165, - 12192, 12077, 12201, 12061, 12105, 12040, 12240, 12102, 12153, 12032, - 12080, 12167, 12048, 12156, 12059, 12126, 12158, 12050, 12183, 12204, - 12097, 12239, 12053, 12078, 12150, 12071, 12187, 12186, 12223, 12228, - 12104, 12098, 12064, 12036, 12057, 12163, 12178, 12185, 12154, 12203, - 12083, 12087, 12135, 12151, 12202, 12035, 12122, 12141, 12180, 12144, - 12076, 12052, 12115, 12091, 12108, 12169, 12143, 12148, 12130, 12089, - 12211, 12073, 12101, 12138, 12103, 12209, 12047, 12220, 12172, 12129, - 12166, 12242, 12237, 12145, 12106, 12081, 12244, 12038, 12086, 12055, - 12181, 12197, 12193, 12175, 12116, 12110, 12177, 12137, 12230, 12213, - 12195, 12069, 12079, 12206, 12051, 12232, 12132, 129432, 3240, 3293, - 3225, 3235, 3230, 3205, 3206, 3216, 3220, 3234, 3233, 3239, 3238, 983205, - 3251, 3250, 3249, 3248, 3232, 3231, 3237, 3236, 3211, 3296, 3212, 3297, - 3253, 3209, 3210, 3218, 3219, 3207, 3208, 3254, 3255, 3256, 3245, 3244, - 3227, 3226, 3224, 3223, 3229, 3228, 3222, 3221, 3243, 3242, 3214, 3215, - 3294, 3257, 3246, 3247, 3285, 3315, 3201, 3200, 3204, 3260, 3261, 3202, - 3313, 3314, 3277, 3203, 3286, 3262, 3272, 3276, 3265, 3266, 3267, 3268, - 3298, 3299, 3274, 3275, 3263, 3264, 3270, 3271, 3307, 3306, 3309, 3308, - 3305, 3304, 3302, 3311, 3303, 3310, 12450, 984009, 984008, 984007, - 984010, 110881, 110880, 110882, 110592, 12499, 984002, 984005, 984003, - 984006, 984004, 12496, 12505, 12508, 12502, 12511, 110583, 110584, - 110585, 110586, 110587, 110589, 110590, 110576, 110577, 110578, 110579, - 110581, 110582, 12510, 12513, 12514, 12512, 12531, 12490, 12493, 12491, - 12494, 12492, 12789, 12792, 12790, 12793, 12791, 12795, 12798, 12796, - 12799, 12797, 12533, 12534, 110933, 12784, 12787, 12483, 12526, 110949, - 110948, 110950, 12515, 12519, 12517, 110951, 12788, 12785, 12786, 12794, - 12449, 12455, 12451, 12457, 12453, 12469, 12475, 12471, 12477, 12473, - 12480, 12487, 12482, 12489, 12485, 12460, 12466, 12462, 12468, 12464, - 12495, 12504, 12498, 12507, 12501, 12459, 12465, 12461, 12467, 12463, - 12497, 12506, 12500, 12509, 12503, 12521, 12524, 12522, 12525, 12523, - 12479, 12486, 12481, 12488, 12484, 12535, 12537, 12536, 12538, 12532, - 12470, 12476, 12472, 12478, 12474, 12527, 12529, 12528, 12530, 12516, - 12520, 12518, 12456, 12452, 12458, 12454, 12542, 12543, 12541, 12539, - 12448, 12540, 12444, 12443, 73476, 73477, 73487, 73523, 73498, 73497, - 73503, 73502, 73508, 73507, 73501, 73500, 73506, 73505, 73480, 73481, - 73482, 73483, 73484, 73485, 73478, 73479, 73494, 73504, 73499, 73509, - 73519, 73520, 73521, 73513, 73512, 73496, 73495, 73493, 73492, 73491, - 73490, 73511, 73510, 73522, 73517, 73514, 73516, 73518, 73515, 73486, - 73488, 73551, 73548, 73546, 73545, 73549, 73543, 73541, 73544, 73550, - 73542, 73547, 73537, 73472, 73562, 73474, 73475, 73473, 73525, 73524, - 73535, 73530, 73534, 73536, 73528, 73529, 73526, 73527, 73540, 73539, - 73557, 73556, 73559, 73558, 73555, 73554, 73552, 73561, 73553, 73560, - 73538, 43299, 43301, 43283, 43295, 43277, 43281, 43284, 43275, 43274, - 43286, 43285, 43279, 43278, 43294, 43282, 43289, 43297, 43288, 43276, - 43292, 43287, 43290, 43296, 43293, 43291, 43280, 43298, 43300, 43310, - 43311, 43308, 43309, 43307, 43303, 43305, 43304, 43302, 43306, 43269, - 43268, 43271, 43270, 43267, 43266, 43264, 43273, 43265, 43272, 119496, - 119506, 119499, 119503, 119493, 119492, 119502, 119497, 119507, 119495, - 119505, 119494, 119504, 119501, 119491, 119500, 119490, 119498, 119488, - 119489, 128331, 8490, 128273, 9000, 128422, 983552, 983553, 983559, - 983558, 983561, 983560, 983557, 983556, 983554, 983563, 983555, 983562, - 128287, 118449, 68163, 68162, 68161, 68160, 68113, 68146, 68112, 68147, - 68148, 68123, 68122, 68128, 68127, 68126, 68121, 68131, 68125, 68124, - 68130, 68129, 68141, 68142, 68143, 68135, 68134, 68118, 68117, 68115, - 68114, 68133, 68132, 68149, 68140, 68145, 68119, 68139, 68136, 68138, - 68137, 68144, 68096, 68165, 68164, 68166, 68167, 68179, 68178, 68183, - 68176, 68182, 68181, 68184, 68180, 68177, 68152, 68153, 68109, 68154, - 68111, 68110, 68099, 68101, 68097, 68102, 68098, 68108, 68159, 68168, - 129711, 101120, 101121, 101122, 101123, 101124, 101125, 101126, 101127, - 101128, 101129, 101130, 101131, 101132, 101133, 101134, 101135, 101136, - 101137, 101138, 101139, 101140, 101141, 101142, 101143, 101144, 101145, - 101146, 101147, 101148, 101149, 101150, 101151, 101152, 101153, 101154, - 101155, 101156, 101157, 101158, 101159, 101160, 101161, 101162, 101163, - 101164, 101165, 101166, 101167, 101168, 101169, 101170, 101171, 101172, - 101173, 101174, 101175, 101176, 101177, 101178, 101179, 101180, 101181, - 101182, 101183, 101184, 101185, 101186, 101187, 101188, 101189, 101190, - 101191, 101192, 101193, 101194, 101195, 101196, 101197, 101198, 101199, - 101200, 101201, 101202, 101203, 101204, 101205, 101206, 101207, 101208, - 101209, 101210, 101211, 101212, 101213, 101214, 101215, 101216, 101217, - 101218, 101219, 101220, 101221, 101222, 101223, 101224, 101225, 101226, - 101227, 101228, 101229, 101230, 101231, 101232, 101233, 101234, 101235, - 101236, 101237, 101238, 101239, 101240, 101241, 101242, 101243, 101244, - 101245, 101246, 101247, 101248, 101249, 101250, 101251, 101252, 101253, - 101254, 101255, 101256, 101257, 101258, 101259, 101260, 101261, 101262, - 101263, 101264, 101265, 101266, 101267, 101268, 101269, 101270, 101271, - 101272, 101273, 101274, 101275, 101276, 101277, 101278, 101279, 101280, - 101281, 101282, 101283, 101284, 101285, 101286, 101287, 101288, 101289, - 101290, 101291, 101292, 101293, 101294, 101295, 101296, 101297, 101298, - 101299, 101300, 101301, 101302, 101303, 101304, 101305, 101306, 101307, - 101308, 101309, 101310, 101311, 101312, 101313, 101314, 101315, 101316, - 101317, 101318, 101319, 101320, 101321, 101322, 101323, 101324, 101325, - 101326, 101327, 101328, 101329, 101330, 101331, 101332, 101333, 101334, - 101335, 101336, 101337, 101338, 101339, 101340, 101341, 101342, 101343, - 101344, 101345, 101346, 101347, 101348, 101349, 101350, 101351, 101352, - 101353, 101354, 101355, 101356, 101357, 101358, 101359, 101360, 101361, - 101362, 101363, 101364, 101365, 101366, 101367, 101368, 101369, 101370, - 101371, 101372, 101373, 101374, 101375, 101584, 101585, 101586, 101587, - 101588, 101589, 101376, 101377, 101378, 101379, 101380, 101381, 101382, - 101383, 101384, 101385, 101386, 101387, 101388, 101389, 101390, 101391, - 101392, 101393, 101394, 101395, 101396, 101397, 101398, 101399, 101400, - 101401, 101402, 101403, 101404, 101405, 101406, 101407, 101408, 101409, - 101410, 101411, 101412, 101413, 101414, 101415, 101416, 101417, 101418, - 101419, 101420, 101421, 101422, 101423, 101424, 101425, 101426, 101427, - 101428, 101429, 101430, 101431, 101432, 101433, 101434, 101435, 101436, - 101437, 101438, 101439, 101440, 101441, 101442, 101443, 101444, 101445, - 101446, 101447, 101448, 101449, 101450, 101451, 101452, 101453, 101454, - 101455, 101456, 101457, 101458, 101459, 101460, 101461, 101462, 101463, - 101464, 101465, 101466, 101467, 101468, 101469, 101470, 101471, 101472, - 101473, 101474, 101475, 101476, 101477, 101478, 101479, 101480, 101481, - 101482, 101483, 101484, 101485, 101486, 101487, 101488, 101489, 101490, - 101491, 101492, 101493, 101494, 101495, 101496, 101497, 101498, 101499, - 101500, 101501, 101502, 101503, 101504, 101505, 101506, 101507, 101508, - 101509, 101510, 101511, 101512, 101513, 101514, 101515, 101516, 101517, - 101518, 101519, 101520, 101521, 101522, 101523, 101524, 101525, 101526, - 101527, 101528, 101529, 101530, 101531, 101532, 101533, 101534, 101535, - 101536, 101537, 101538, 101539, 101540, 101541, 101542, 101543, 101544, - 101545, 101546, 101547, 101548, 101549, 101550, 101551, 101552, 101553, - 101554, 101555, 101556, 101557, 101558, 101559, 101560, 101561, 101562, - 101563, 101564, 101565, 101566, 101567, 101568, 101569, 101570, 101571, - 101572, 101573, 101574, 101575, 101576, 101577, 101578, 101579, 101580, - 101581, 101582, 101583, 101631, 94180, 983960, 983965, 983970, 983975, - 983962, 983964, 983961, 983963, 983957, 983959, 983956, 983958, 983977, - 983979, 983978, 983972, 983974, 983967, 983969, 983971, 983973, 983966, - 983968, 983989, 983983, 983985, 983986, 983987, 983980, 983982, 983984, - 983981, 983976, 983988, 6107, 6052, 6064, 6051, 6067, 6066, 6065, 6055, - 6057, 6058, 6056, 6053, 6054, 6063, 983994, 983991, 983992, 983993, 6061, - 6062, 6059, 6060, 6022, 6024, 6021, 6023, 6017, 6019, 6016, 6018, 6020, - 6030, 6025, 6035, 6037, 6039, 6038, 6046, 6045, 6047, 6032, 6034, 6027, - 6029, 6031, 6033, 6026, 6028, 6049, 6043, 6040, 6042, 6044, 6041, 6036, - 6048, 6050, 6108, 6109, 6095, 6096, 6099, 6091, 6101, 6104, 6102, 6098, - 6094, 6100, 6106, 6092, 6087, 6093, 6090, 6103, 6105, 6088, 6086, 6089, - 6097, 6652, 6636, 6655, 6639, 6653, 6637, 6654, 6638, 6651, 6635, 6650, - 6634, 6133, 6137, 6136, 6134, 6135, 6130, 6132, 6131, 6129, 6128, 6624, - 6648, 6632, 6649, 6633, 6647, 6631, 6646, 6630, 6645, 6629, 6642, 6626, - 6640, 6643, 6627, 6644, 6628, 6641, 6625, 6069, 6068, 6070, 983996, 6082, - 6083, 6085, 6071, 6080, 6072, 6078, 983995, 6084, 6073, 6079, 6074, - 983990, 6075, 6077, 6076, 6081, 6117, 6116, 6119, 6118, 6115, 6114, 6112, - 6121, 6113, 6120, 70204, 70201, 70200, 70208, 70185, 70178, 70179, 70177, - 70155, 70156, 70154, 70172, 70167, 70166, 70173, 70171, 70161, 70160, - 70144, 70145, 70149, 70151, 70165, 70164, 70170, 70169, 70187, 70183, - 70157, 70168, 70163, 70174, 70159, 70158, 70153, 70152, 70176, 70175, - 70186, 70180, 70207, 70182, 70184, 70181, 70148, 70146, 70150, 70147, - 70199, 70206, 70198, 70197, 70196, 70203, 70209, 70188, 70193, 70195, - 70189, 70190, 70192, 70194, 70191, 70202, 70205, 70357, 70358, 70356, - 70333, 70334, 70332, 70345, 70347, 70344, 70352, 70351, 70340, 70339, - 70338, 70320, 70321, 70327, 70329, 70346, 70361, 70343, 70342, 70350, - 70349, 70324, 70325, 70322, 70323, 70335, 70348, 70341, 70353, 70337, - 70336, 70331, 70330, 70355, 70354, 70364, 70365, 70366, 70362, 70359, - 70363, 70360, 70326, 70328, 70377, 70378, 70367, 70368, 70374, 70376, - 70371, 70372, 70369, 70370, 70373, 70375, 70389, 70388, 70391, 70390, - 70387, 70386, 70384, 70393, 70385, 70392, 93521, 93520, 93525, 93524, - 93519, 93518, 93523, 93522, 93512, 93517, 93526, 93530, 93529, 93514, - 93513, 93511, 93510, 93516, 93515, 93509, 93508, 93528, 93527, 93537, - 93536, 93538, 93534, 93531, 93533, 93535, 93532, 93507, 93505, 93549, - 93548, 93504, 93547, 93506, 93539, 93544, 93546, 93541, 93542, 93543, - 93540, 93545, 93551, 93550, 93557, 93556, 93559, 93558, 93555, 93554, - 93552, 93561, 93553, 93560, 128143, 128535, 128537, 128538, 128573, - 128139, 129373, 8365, 128088, 129665, 129486, 129698, 12927, 128040, - 11235, 129404, 127991, 129357, 128030, 129692, 3805, 3804, 983206, - 983207, 3743, 3741, 3807, 3806, 3714, 3716, 3713, 983209, 3747, 3749, - 3730, 3729, 3736, 3728, 3727, 3731, 3726, 3744, 3721, 3718, 3724, 3756, - 3740, 3742, 3739, 3752, 3753, 3754, 3722, 3734, 3735, 3733, 3755, 3758, - 3719, 3725, 3737, 3738, 3720, 3732, 3745, 983208, 3751, 3746, 3757, 3773, - 3772, 3770, 3785, 3786, 3787, 3784, 3760, 3762, 3780, 3763, 3779, 3761, - 3771, 3766, 3767, 3768, 3769, 3776, 3777, 3764, 3765, 3778, 3782, 3790, - 3759, 3797, 3796, 3799, 3798, 3795, 3794, 3792, 3801, 3793, 3800, 3788, - 3789, 128996, 129003, 128309, 128311, 128998, 128994, 129001, 68413, - 68415, 128992, 128310, 128999, 68412, 68414, 118324, 118319, 118322, - 118323, 118303, 118304, 118336, 118331, 118328, 118315, 118314, 118306, - 118316, 118318, 118334, 118335, 118333, 118327, 118339, 118341, 118342, - 118340, 118320, 118338, 118332, 118302, 118325, 118309, 118317, 118307, - 118313, 118326, 118329, 118312, 118330, 118352, 118348, 118351, 118350, - 118347, 118344, 118345, 118343, 118349, 118346, 118305, 118321, 118301, - 118299, 118298, 118310, 118311, 118308, 118300, 118337, 11004, 10201, - 10200, 10782, 128995, 129002, 128308, 128997, 128993, 129000, 9711, - 10923, 10925, 8382, 9790, 127772, 127767, 65, 258, 7858, 7856, 7854, - 7862, 7860, 550, 480, 7840, 512, 196, 478, 197, 506, 7680, 256, 983564, - 194, 7848, 7846, 7844, 7852, 7850, 461, 7842, 260, 983590, 983592, 192, - 193, 514, 195, 570, 198, 508, 482, 42946, 393, 11373, 42808, 42810, - 42802, 42804, 42806, 42812, 66, 386, 42902, 7686, 7684, 7682, 385, 579, - 42822, 42932, 67, 199, 7688, 264, 268, 262, 42948, 391, 42898, 266, 571, - 42960, 42796, 42798, 42862, 42931, 68, 498, 453, 42951, 272, 7696, 7698, - 270, 395, 7694, 7692, 7690, 394, 497, 452, 69, 552, 7708, 202, 983586, - 7874, 7872, 7870, 983584, 7878, 7876, 7704, 282, 278, 983598, 983600, - 7864, 516, 203, 7868, 7706, 274, 7700, 7702, 983570, 983572, 983574, - 7866, 280, 983594, 983596, 200, 201, 518, 276, 582, 439, 494, 440, 42786, - 42788, 42858, 208, 425, 330, 70, 401, 7710, 42904, 71, 290, 284, 486, - 500, 286, 7712, 42912, 403, 288, 484, 577, 42938, 42940, 42942, 404, - 983199, 72, 7722, 7720, 292, 542, 7718, 11367, 7716, 7714, 42922, 294, - 11381, 502, 42790, 73, 520, 7882, 304, 207, 7726, 206, 463, 298, 983566, - 296, 7724, 7880, 302, 983604, 983606, 204, 205, 522, 300, 407, 42873, - 42875, 42877, 42882, 42884, 42886, 406, 42860, 74, 308, 42930, 983608, - 584, 75, 310, 488, 42818, 11369, 7730, 42816, 42820, 7728, 7732, 42914, - 408, 76, 42925, 573, 11360, 7734, 7736, 11362, 319, 456, 321, 315, 7740, - 317, 42824, 313, 7738, 983610, 42970, 42972, 455, 77, 7742, 7746, 7744, - 983612, 11374, 7930, 7932, 42966, 78, 325, 7754, 327, 459, 544, 7752, - 413, 504, 323, 42896, 7750, 7748, 42916, 209, 458, 79, 42826, 42828, 332, - 7760, 7762, 415, 212, 7892, 7890, 7888, 7896, 7894, 465, 214, 554, 558, - 560, 7884, 524, 336, 490, 492, 216, 510, 213, 7758, 7756, 556, 983576, - 983578, 983580, 416, 7902, 7900, 7898, 7906, 7904, 7886, 210, 211, 526, - 334, 42944, 400, 390, 42934, 418, 42830, 546, 80, 42834, 11363, 42832, - 42836, 7764, 420, 7766, 81, 42840, 42838, 82, 342, 344, 7770, 7772, 7768, - 528, 11364, 983614, 340, 7774, 530, 42918, 588, 42842, 42997, 42923, - 42814, 398, 42844, 42955, 83, 352, 7782, 350, 536, 348, 346, 7780, 7778, - 7784, 7776, 42956, 42953, 11390, 983582, 42920, 42949, 586, 42926, 42891, - 7838, 42968, 42924, 399, 84, 354, 7792, 538, 356, 574, 7788, 7786, 7790, - 430, 428, 358, 42878, 11375, 11376, 42893, 42928, 42880, 412, 42929, 581, - 222, 42852, 42854, 388, 423, 444, 42794, 42792, 85, 219, 7798, 467, 220, - 473, 475, 471, 469, 7794, 532, 368, 7908, 431, 7916, 7914, 7912, 7920, - 7918, 7910, 362, 7802, 983568, 983620, 983622, 370, 983616, 983618, 360, - 7800, 7796, 217, 218, 534, 364, 366, 42936, 580, 433, 86, 42846, 7806, - 7804, 434, 42850, 42906, 42908, 42910, 42856, 42848, 87, 372, 7812, 7816, - 7814, 7808, 7810, 11378, 503, 88, 7820, 7818, 89, 374, 376, 7924, 7822, - 7922, 435, 7926, 7934, 221, 562, 7928, 590, 540, 90, 7824, 381, 377, - 11371, 7826, 379, 7828, 11391, 437, 42950, 548, 306, 338, 10013, 43007, - 43005, 43006, 43003, 43004, 42999, 450, 7461, 684, 664, 685, 662, 122638, - 446, 426, 674, 451, 122634, 7431, 7430, 7459, 618, 641, 671, 122628, - 7436, 7439, 7440, 630, 7445, 640, 7438, 7449, 43846, 42870, 7451, 11387, - 122626, 122640, 43002, 7450, 665, 7427, 610, 667, 7424, 7425, 7428, 7429, - 42800, 668, 7434, 7435, 7437, 628, 7448, 42927, 42801, 7452, 7456, 7457, - 655, 7458, 663, 122639, 42895, 443, 447, 448, 449, 660, 673, 661, 7460, - 422, 7547, 7550, 97, 259, 7859, 7857, 7855, 7863, 7861, 551, 481, 7841, - 513, 228, 479, 229, 507, 7681, 7834, 7567, 257, 983565, 226, 7849, 7847, - 7845, 7853, 7851, 462, 7843, 261, 983591, 983593, 224, 225, 515, 227, - 11365, 43825, 230, 983624, 509, 483, 42947, 593, 7568, 42809, 42811, - 42803, 42805, 42807, 42813, 98, 387, 42903, 7687, 7532, 7552, 7685, 7683, - 595, 384, 43824, 43827, 629, 43853, 43837, 43838, 43826, 42823, 7447, - 42933, 99, 231, 7689, 265, 597, 269, 263, 42900, 122653, 392, 42899, 267, - 572, 43859, 43860, 43861, 42961, 666, 631, 606, 42797, 42799, 42863, 100, - 42952, 273, 396, 598, 7697, 7699, 545, 271, 122661, 7533, 7695, 599, - 7569, 7553, 7693, 7691, 676, 122642, 122649, 7839, 567, 607, 644, 305, - 42965, 43848, 43850, 42963, 499, 454, 675, 677, 43878, 568, 42865, 101, - 553, 7709, 234, 983587, 7875, 7873, 7871, 983585, 7879, 7877, 7705, 283, - 279, 983599, 983601, 7865, 517, 235, 11384, 7869, 7707, 275, 7701, 7703, - 983571, 983573, 983575, 43828, 7867, 281, 983595, 983597, 232, 233, 519, - 277, 7570, 583, 42787, 42789, 331, 43836, 122644, 643, 122635, 122636, - 7563, 646, 7576, 658, 441, 659, 495, 122648, 7578, 442, 42859, 240, 102, - 7534, 7554, 402, 7711, 42905, 681, 122624, 103, 291, 285, 487, 501, 287, - 7713, 7555, 42913, 608, 289, 485, 578, 42939, 42941, 42943, 611, 983200, - 104, 7723, 7721, 293, 543, 7719, 11368, 7717, 7715, 7830, 42901, 614, - 295, 11382, 983631, 983632, 42791, 615, 405, 105, 238, 464, 239, 7727, - 983602, 983588, 983603, 7883, 521, 299, 983567, 303, 983605, 983607, 297, - 7725, 7881, 236, 237, 523, 301, 616, 122650, 7574, 42874, 42876, 7545, - 42883, 42885, 42887, 43876, 43840, 617, 7548, 43873, 42861, 106, 309, - 669, 496, 983609, 585, 107, 311, 489, 42819, 11370, 7731, 42817, 42821, - 7729, 7733, 7556, 42915, 409, 312, 108, 620, 122643, 410, 316, 7741, 564, - 318, 7735, 7737, 43832, 11361, 619, 43833, 320, 122662, 42825, 314, 7739, - 43831, 621, 42894, 122641, 7557, 983611, 322, 42971, 411, 622, 122629, - 43829, 383, 7836, 7835, 7837, 682, 683, 42866, 457, 109, 7743, 43834, - 7535, 7558, 7747, 7745, 983613, 625, 7931, 7933, 42967, 42867, 110, 326, - 7755, 43835, 565, 328, 414, 7753, 626, 122663, 7536, 505, 324, 42897, - 7751, 7749, 7559, 627, 42917, 241, 329, 983589, 42868, 460, 111, 244, - 7893, 7891, 7889, 7897, 7895, 466, 246, 555, 559, 561, 7885, 525, 337, - 42827, 11386, 42829, 333, 7761, 7763, 491, 493, 248, 511, 245, 7759, - 7757, 557, 983577, 983579, 983581, 417, 7903, 7901, 7899, 7907, 7905, - 7887, 242, 243, 527, 335, 122651, 42945, 596, 983625, 983626, 7575, - 43839, 43874, 603, 7571, 42935, 419, 42831, 547, 112, 42835, 7549, 42833, - 42837, 7765, 7537, 7560, 421, 7767, 632, 113, 42841, 672, 587, 42839, - 569, 114, 343, 43849, 345, 7771, 7773, 7769, 529, 638, 7539, 122646, - 7775, 636, 637, 983615, 122664, 7538, 341, 531, 7561, 42919, 589, 43847, - 42843, 42998, 604, 7572, 605, 639, 122625, 600, 122631, 8580, 42815, - 122627, 42869, 42845, 612, 115, 347, 7781, 353, 7783, 351, 537, 349, - 122654, 7779, 7785, 7777, 42957, 42954, 575, 983583, 122665, 7540, 7562, - 42921, 642, 42892, 43872, 601, 983629, 983630, 7573, 602, 43851, 43852, - 609, 43830, 223, 7441, 7442, 7443, 7455, 7454, 7453, 42969, 645, 43845, - 116, 355, 7793, 539, 566, 357, 11366, 7831, 7789, 7787, 122666, 7541, - 7791, 427, 429, 122633, 648, 359, 679, 122647, 122652, 7546, 254, 42853, - 42855, 389, 424, 445, 7446, 42795, 397, 613, 686, 687, 7433, 42879, 7444, - 43842, 43841, 43843, 43844, 7432, 633, 43880, 122645, 634, 122632, 11385, - 635, 647, 122637, 652, 983627, 983628, 592, 594, 7426, 623, 624, 654, - 122630, 43857, 477, 7543, 670, 42881, 653, 42871, 680, 678, 43879, 11383, - 42793, 117, 649, 43855, 251, 7799, 468, 252, 474, 476, 472, 470, 7795, - 533, 369, 7909, 432, 7917, 7915, 7913, 7921, 7919, 7911, 363, 7803, - 983569, 983621, 983623, 371, 983617, 983619, 7577, 367, 361, 7801, 7797, - 249, 43854, 42937, 250, 535, 365, 43858, 650, 7551, 7531, 43856, 42872, - 43875, 118, 42847, 7807, 7564, 11380, 11377, 7805, 651, 42851, 42907, - 42909, 42911, 42857, 42849, 119, 373, 7813, 7817, 7815, 7809, 7811, 7832, - 11379, 120, 7821, 7819, 43863, 43864, 43865, 43862, 7565, 121, 375, 255, - 7925, 7823, 7923, 436, 7927, 7935, 43866, 591, 253, 563, 7929, 7833, 541, - 122, 378, 7825, 657, 382, 11372, 7827, 380, 7829, 576, 438, 7542, 7566, - 656, 549, 64256, 64259, 64260, 64257, 64258, 307, 64261, 64262, 339, - 8347, 8340, 8336, 8337, 8341, 7522, 11388, 8342, 8343, 8344, 8345, 8338, - 8346, 7523, 8348, 7524, 7525, 8339, 917505, 129726, 127811, 129388, - 129897, 129916, 129947, 10203, 10202, 129899, 129917, 117902, 128494, - 12296, 10641, 10643, 11058, 11056, 10576, 10571, 10570, 10574, 12304, - 10647, 123, 9128, 9129, 9127, 12300, 8968, 9948, 10714, 12298, 8220, - 11816, 11780, 129287, 129284, 129285, 129283, 129286, 9686, 11240, 9612, - 129977, 117924, 118288, 129970, 118285, 118283, 118435, 118440, 129940, - 129932, 128379, 128709, 11804, 10204, 9614, 9615, 129999, 10197, 8596, - 8700, 8697, 8622, 10568, 8660, 10500, 8654, 8621, 11012, 8703, 11020, - 129112, 11108, 11788, 10181, 8907, 9609, 8216, 11814, 128488, 91, 9123, - 9121, 10639, 10637, 8261, 10635, 11863, 11861, 9122, 11778, 10703, - 129900, 11785, 117771, 129985, 128492, 118434, 118441, 9610, 9613, 12308, - 129998, 8867, 12302, 10627, 12310, 10629, 12314, 12312, 10712, 128398, - 9611, 10620, 8970, 130027, 130019, 8905, 40, 9117, 9115, 9116, 11808, - 9144, 9001, 117856, 118265, 10748, 171, 117774, 128269, 117920, 117846, - 117922, 117911, 117861, 117762, 117918, 117880, 10553, 10154, 1422, - 117832, 117906, 117908, 129307, 4054, 4056, 117872, 117876, 9958, 8294, - 8237, 8234, 8206, 8592, 10563, 11074, 11083, 11082, 10611, 129973, 10618, - 10615, 11070, 8676, 8633, 10525, 129032, 8619, 11064, 8698, 10566, - 129040, 129024, 8602, 11024, 11025, 8610, 11066, 11065, 129028, 129176, - 129044, 8617, 8695, 8612, 10527, 129216, 8646, 10521, 129192, 129184, - 11144, 11013, 10603, 10599, 10590, 10582, 8637, 10594, 10602, 10598, - 10586, 10578, 8636, 8651, 129778, 129088, 129092, 11104, 11136, 11130, - 983241, 11174, 11172, 129064, 129060, 129056, 129072, 129068, 11120, - 11114, 11140, 129168, 10510, 8666, 129186, 11067, 11069, 11068, 11244, - 11061, 11060, 11062, 11063, 8606, 8678, 129172, 8604, 8656, 10498, 8653, - 10502, 10523, 10508, 8672, 129194, 129076, 129188, 8701, 8647, 129783, - 129190, 128620, 8668, 129080, 129104, 129084, 11077, 9804, 128006, 7213, - 7221, 7216, 7220, 7215, 7214, 7217, 7218, 7219, 7170, 7169, 7168, 7184, - 7183, 7182, 7247, 7193, 7180, 7188, 7187, 7186, 7185, 7172, 7171, 7198, - 7197, 7190, 7189, 7173, 7177, 7181, 7192, 7191, 7246, 7245, 7179, 7178, - 7175, 7174, 7201, 7200, 7176, 7196, 7195, 7199, 7202, 7194, 7203, 7227, - 7231, 7230, 7228, 7229, 7223, 7222, 7205, 7204, 7210, 7211, 7208, 7209, - 7206, 7212, 7207, 7237, 7236, 7239, 7238, 7235, 7234, 7232, 7241, 7233, - 7240, 10897, 10895, 10893, 10899, 10891, 10614, 10889, 10887, 8922, 8934, - 8808, 10918, 10920, 10885, 10877, 10881, 10883, 10879, 8818, 8804, 8822, - 8806, 10873, 10875, 8918, 60, 128210, 127898, 127819, 129461, 128626, - 128955, 128969, 128943, 11212, 128964, 10099, 128648, 10098, 9617, - 128937, 128949, 128978, 128960, 129653, 128910, 10072, 128930, 128504, - 9735, 128498, 128497, 6429, 6404, 6403, 6412, 6430, 6411, 6421, 6410, - 6405, 6415, 6425, 6426, 6427, 6419, 6418, 6407, 6406, 6414, 6413, 6409, - 6408, 6402, 6401, 6417, 6416, 6428, 6423, 6420, 6422, 6424, 6458, 6457, - 6459, 6464, 6449, 6452, 6450, 6448, 6456, 6454, 6453, 6455, 6451, 6442, - 6443, 6441, 6432, 6436, 6438, 6440, 6437, 6439, 6435, 6433, 6434, 6400, - 6468, 6469, 6475, 6474, 6477, 6476, 6473, 6472, 6470, 6479, 6471, 6478, - 13007, 10770, 10771, 10772, 983062, 8232, 983068, 983143, 67143, 67146, - 67151, 67165, 67166, 67167, 67157, 67158, 67159, 67160, 67161, 67162, - 67163, 67164, 67171, 67172, 67173, 67168, 67169, 67170, 67174, 67175, - 67176, 67177, 67178, 67179, 67230, 67231, 67180, 67181, 67182, 67183, - 67184, 67185, 67186, 67187, 67188, 67189, 67190, 67191, 67192, 67193, - 67194, 67195, 67196, 67197, 67198, 67199, 67200, 67201, 67202, 67203, - 67204, 67205, 67206, 67207, 67208, 67209, 67210, 67211, 67212, 67213, - 67214, 67215, 67216, 67217, 67218, 67219, 67220, 67221, 67222, 67223, - 67224, 67225, 67226, 67227, 67228, 67229, 67232, 67233, 67234, 67235, - 67236, 67237, 67238, 67239, 67240, 67241, 67242, 67243, 67244, 67245, - 67246, 67247, 67248, 67249, 67250, 67251, 67252, 67253, 67254, 67255, - 67256, 67257, 67258, 67259, 67260, 67261, 67262, 67263, 67264, 67274, - 67275, 67276, 67277, 67278, 67279, 67280, 67281, 67282, 67283, 67284, - 67285, 67286, 67287, 67288, 67289, 67290, 67291, 67292, 67293, 67294, - 67295, 67296, 67297, 67298, 67299, 67300, 67301, 67302, 67303, 67304, - 67265, 67266, 67267, 67268, 67269, 67270, 67271, 67272, 67273, 67325, - 67326, 67327, 67328, 67329, 67330, 67305, 67306, 67307, 67308, 67309, - 67310, 67311, 67312, 67313, 67314, 67315, 67316, 67317, 67318, 67319, - 67320, 67321, 67322, 67323, 67324, 67331, 67332, 67333, 67334, 67335, - 67336, 67337, 67338, 67349, 67350, 67351, 67352, 67353, 67354, 67355, - 67356, 67357, 67358, 67359, 67360, 67361, 67362, 67363, 67364, 67365, - 67366, 67367, 67368, 67378, 67379, 67380, 67381, 67382, 67369, 67370, - 67371, 67372, 67373, 67374, 67375, 67376, 67377, 67339, 67340, 67341, - 67342, 67343, 67344, 67345, 67346, 67347, 67348, 67401, 67404, 67403, - 67402, 67400, 67398, 67393, 67397, 67395, 67394, 67399, 67392, 67396, - 67406, 67408, 67409, 67410, 67407, 67405, 67411, 67413, 67412, 67424, - 67425, 67426, 67427, 67428, 67429, 67430, 67431, 67081, 67082, 67083, - 67084, 67085, 67087, 67088, 67089, 67090, 67091, 67092, 67093, 67094, - 67086, 67095, 67096, 67097, 67098, 67100, 67101, 67102, 67103, 67104, - 67105, 67106, 67107, 67108, 67109, 67110, 67111, 67112, 67113, 67114, - 67115, 67116, 67117, 67118, 67119, 67120, 67121, 67122, 67123, 67124, - 67125, 67126, 67127, 67128, 67129, 67130, 67131, 67132, 67133, 67134, - 67135, 67136, 67137, 67138, 67139, 67140, 67141, 67142, 67072, 67073, - 67074, 67075, 67076, 67077, 67078, 67079, 67080, 67145, 67147, 67148, - 67149, 67150, 67154, 67155, 67144, 67152, 67153, 67156, 67099, 65667, - 65668, 65669, 65670, 65671, 65672, 65673, 65675, 65674, 65677, 65676, - 65665, 65664, 65666, 65681, 65679, 65680, 65682, 65678, 65686, 65685, - 65687, 65693, 65690, 65691, 65692, 65694, 65703, 65696, 65695, 65697, - 65698, 65699, 65701, 65702, 65707, 65706, 65704, 65705, 65708, 65709, - 65710, 65711, 65712, 65713, 65719, 65717, 65714, 65715, 65716, 65718, - 65720, 65721, 65722, 65723, 65724, 65725, 65726, 65727, 65728, 65729, - 65731, 65730, 65732, 65733, 65737, 65734, 65735, 65736, 65738, 65739, - 65740, 65741, 65743, 65742, 65744, 65745, 65747, 65748, 65752, 65749, - 65750, 65751, 65753, 65754, 65755, 65756, 65757, 65779, 65780, 65781, - 65782, 65783, 65784, 65785, 65759, 65760, 65761, 65762, 65763, 65764, - 65765, 65766, 65767, 65768, 65769, 65770, 65771, 65772, 65773, 65774, - 65775, 65776, 65777, 65778, 65758, 65786, 65683, 65684, 65689, 65688, - 65700, 65746, 65561, 65543, 65587, 65582, 65589, 65536, 65541, 65579, - 65566, 65571, 65596, 65569, 65584, 65544, 65559, 65540, 65557, 65560, - 65580, 65606, 65600, 65599, 65577, 65562, 65538, 65573, 65563, 65609, - 65588, 65549, 65568, 65537, 65581, 65574, 65601, 65542, 65593, 65583, - 65594, 65552, 65547, 65605, 65578, 65545, 65585, 65586, 65546, 65570, - 65591, 65564, 65565, 65607, 65610, 65611, 65553, 65590, 65550, 65539, - 65576, 65608, 65551, 65554, 65592, 65603, 65597, 65567, 65572, 65558, - 65555, 65612, 65602, 65556, 65613, 65604, 65620, 65621, 65623, 65624, - 65626, 65627, 65628, 65629, 65622, 65616, 65617, 65619, 65618, 65625, - 128391, 128279, 128482, 128132, 42237, 42235, 42232, 42234, 42236, 42233, - 42206, 42205, 42197, 42196, 42204, 42195, 42228, 42229, 42230, 42213, - 42208, 42224, 42225, 42203, 42202, 42221, 42198, 42216, 42214, 42200, - 42199, 42194, 42193, 42219, 42210, 73648, 42220, 42211, 42212, 42222, - 42223, 42227, 42231, 42192, 42217, 42201, 42209, 42207, 42218, 42215, - 42226, 42238, 42239, 8356, 8374, 129409, 129422, 9806, 128274, 983079, - 983076, 128271, 117785, 117786, 117784, 117783, 117782, 117781, 8743, - 10848, 10846, 10833, 10844, 10842, 10847, 8744, 10841, 10851, 10850, - 10834, 10845, 10843, 10982, 10188, 129688, 10234, 10231, 10206, 10229, - 10235, 10232, 10237, 11059, 10230, 10236, 10233, 10238, 10239, 10205, - 129524, 128884, 129719, 128140, 127977, 128261, 129707, 12319, 8270, - 11847, 11848, 95, 118273, 9691, 118276, 118291, 129935, 118436, 118447, - 9604, 9697, 117765, 128394, 129852, 129853, 129870, 129872, 129868, - 129856, 129871, 129869, 129854, 129873, 129855, 128395, 128396, 128393, - 10559, 117934, 117936, 117932, 117930, 117972, 9695, 117960, 117948, - 117964, 117968, 117952, 117956, 117944, 117940, 117817, 129951, 9722, - 117820, 128397, 118428, 117935, 117937, 117933, 117931, 117973, 9694, - 117961, 117949, 117965, 117969, 117953, 117957, 117945, 117941, 117818, - 10558, 128318, 10065, 129950, 9727, 117823, 117767, 129863, 129865, - 129867, 129864, 129861, 129866, 129859, 129862, 129860, 129857, 129858, - 10195, 118431, 10063, 9998, 9987, 118429, 117821, 118430, 117822, 130021, - 9605, 118425, 118426, 118424, 117816, 118427, 117819, 9602, 9601, 9607, - 9603, 118437, 118446, 9606, 129903, 9674, 10208, 129438, 128557, 127853, - 129523, 128886, 129729, 66190, 66192, 66187, 66196, 66178, 66179, 66199, - 66185, 66200, 66176, 66201, 66177, 66202, 66191, 66193, 66181, 66180, - 66203, 66182, 66186, 66189, 66195, 66188, 66197, 66198, 66194, 66183, - 66204, 66184, 67887, 67892, 67881, 67895, 67891, 67886, 67872, 67893, - 67876, 67894, 67883, 67896, 67873, 67897, 67875, 67889, 67874, 67878, - 67880, 67882, 67884, 67890, 67885, 67888, 67877, 67879, 67903, 129317, - 983226, 983234, 983224, 983229, 8468, 129433, 983065, 129668, 129522, - 129497, 69986, 69981, 69991, 69985, 69984, 69990, 69989, 70002, 69997, - 69983, 69982, 69988, 69987, 69995, 69994, 69978, 69977, 69976, 69975, - 69980, 69979, 69974, 69973, 69993, 69992, 70001, 69998, 69996, 70000, - 69999, 69968, 69971, 69969, 69972, 69970, 70006, 70005, 70003, 70004, - 127012, 127019, 127008, 126990, 126999, 126976, 127005, 126987, 126996, - 127004, 126986, 126995, 126979, 127009, 126991, 127000, 127001, 126983, - 126992, 127011, 127010, 126977, 127007, 126989, 126998, 127006, 126988, - 126997, 127015, 127014, 127003, 126985, 126994, 127002, 126984, 126993, - 126978, 126982, 127017, 126981, 126980, 127016, 127018, 127013, 73464, - 73442, 73451, 73448, 73444, 73449, 73447, 73441, 73450, 73440, 73454, - 73445, 73443, 73453, 73456, 73446, 73455, 73452, 73457, 73463, 73461, - 73459, 73462, 73460, 73458, 128892, 3449, 3435, 3434, 3437, 3436, 3433, - 3432, 3430, 3439, 3431, 3438, 3419, 3420, 3446, 3417, 3422, 3416, 3447, - 3444, 3443, 3448, 3418, 3421, 3445, 3333, 3423, 3334, 3344, 3348, 3453, - 3454, 3414, 3451, 3450, 3452, 3455, 3412, 3413, 3355, 3354, 3406, 3362, - 3361, 3367, 3366, 3360, 3386, 3359, 3365, 3364, 3332, 3339, 3424, 3340, - 3425, 3381, 3369, 3363, 3353, 3358, 3368, 3380, 3379, 3378, 3377, 3376, - 3337, 3338, 3346, 3347, 3335, 3336, 3382, 3383, 3384, 3373, 3372, 3352, - 3351, 3357, 3356, 3350, 3349, 3371, 3370, 3342, 3343, 3385, 3374, 3375, - 3441, 3442, 3440, 3328, 3388, 3329, 3387, 3405, 3331, 3389, 3330, 3407, - 3390, 3400, 3404, 3393, 3394, 3395, 3396, 3426, 3427, 3402, 3403, 3391, - 3392, 3398, 3399, 3415, 9895, 9894, 9893, 9794, 10016, 129443, 128104, - 128372, 129333, 128114, 128115, 128378, 128107, 2137, 2122, 2121, 2133, - 2120, 2126, 2132, 2129, 2136, 2113, 2115, 2114, 2116, 2123, 2124, 2125, - 2128, 2130, 2131, 2118, 2134, 2117, 2112, 2127, 2119, 2135, 2138, 2139, - 2142, 68288, 68314, 68313, 68290, 68289, 68293, 68308, 68292, 68291, - 68300, 68299, 68298, 68297, 68306, 68304, 68320, 68318, 68323, 68312, - 68317, 68322, 68309, 68302, 68324, 68305, 68319, 68307, 68321, 68303, - 68294, 68301, 68311, 68295, 68316, 68315, 68310, 68331, 68335, 68334, - 68333, 68332, 68340, 68339, 68338, 68342, 68337, 68341, 68336, 68296, - 68326, 68325, 128094, 129469, 8380, 128368, 129389, 9967, 127809, 129671, - 72835, 72834, 72827, 72826, 72836, 72828, 72821, 72825, 72829, 72823, - 72822, 72819, 72818, 72831, 72830, 72844, 72845, 72838, 72839, 72840, - 72832, 72820, 72846, 72824, 72843, 72833, 72842, 72837, 72841, 72847, - 72886, 72885, 72867, 72866, 72859, 72858, 72868, 72860, 72853, 72857, - 72861, 72855, 72854, 72851, 72850, 72863, 72862, 72876, 72877, 72870, - 72871, 72864, 72852, 72878, 72856, 72875, 72865, 72874, 72869, 72873, - 72879, 72880, 72883, 72881, 72884, 72882, 72816, 72817, 9901, 129355, - 73007, 72980, 72979, 72983, 72982, 72988, 73008, 72987, 72960, 72961, - 72968, 72971, 72985, 72984, 72990, 72989, 72964, 72965, 72962, 72963, - 73005, 72999, 73006, 72973, 72972, 72976, 72986, 72981, 72991, 73001, - 73002, 73003, 72995, 72994, 72978, 72977, 72975, 72974, 72993, 72992, - 73004, 72996, 72998, 73000, 72997, 72966, 72969, 73031, 73030, 73027, - 73028, 73026, 73025, 73024, 73014, 73009, 73020, 73023, 73012, 73013, - 73010, 73011, 73018, 73021, 73029, 73045, 73044, 73047, 73046, 73043, - 73042, 73040, 73049, 73041, 73048, 186, 127405, 12348, 119811, 120778, - 120491, 119827, 120495, 120505, 120507, 119808, 120488, 119809, 120489, - 119833, 120493, 119812, 120492, 120494, 119814, 120490, 119816, 120496, - 119818, 120497, 119819, 120498, 119822, 120502, 120512, 119823, 120509, - 120511, 120503, 119825, 120504, 119826, 120506, 119828, 120508, 119810, - 120510, 119820, 120499, 119821, 120500, 119831, 120501, 119813, 119815, - 119817, 119824, 119829, 119830, 119832, 119837, 120779, 120517, 119834, - 120514, 119835, 120515, 119859, 120519, 119838, 120518, 120520, 119839, - 120531, 119840, 120516, 119842, 120522, 119844, 120523, 119845, 120524, - 119848, 120528, 120538, 119849, 120535, 120537, 120529, 119851, 120530, - 119852, 120532, 119853, 120521, 120533, 119854, 120534, 119836, 120536, - 119846, 120525, 119847, 120526, 119857, 120527, 119841, 119843, 119850, - 119855, 119856, 119858, 120016, 120017, 120018, 120019, 120020, 120021, - 120022, 120023, 120024, 120025, 120026, 120027, 120028, 120029, 120030, - 120031, 120032, 120033, 120034, 120035, 120036, 120037, 120038, 120039, - 120040, 120041, 120042, 120043, 120044, 120045, 120046, 120047, 120048, - 120049, 120050, 120051, 120052, 120053, 120054, 120055, 120056, 120057, - 120058, 120059, 120060, 120061, 120062, 120063, 120064, 120065, 120066, - 120067, 119931, 120611, 120621, 120623, 119912, 120604, 119913, 120605, - 119937, 120609, 119915, 120607, 119916, 120608, 120610, 119918, 120606, - 119920, 120612, 119922, 120613, 119923, 120614, 119926, 120618, 120628, - 119927, 120625, 120627, 120619, 119929, 120620, 119930, 120622, 119932, - 120624, 119914, 120626, 119924, 120615, 119925, 120616, 119935, 120617, - 119917, 119919, 119921, 119928, 119933, 119934, 119936, 120656, 120658, - 120629, 120659, 120655, 120661, 120660, 119938, 120630, 119939, 120631, - 119963, 120635, 119941, 120633, 119942, 120634, 120636, 119943, 120647, - 119944, 120632, 119946, 120638, 119948, 120639, 119949, 120640, 119952, - 120644, 120654, 119953, 120651, 120653, 120645, 119955, 120646, 119956, - 120648, 119957, 120637, 120649, 119958, 120650, 119940, 120652, 119950, - 120641, 119951, 120642, 119961, 120643, 119945, 119947, 119954, 119959, - 119960, 119962, 120657, 120540, 120542, 120513, 120543, 120539, 120545, - 120544, 120541, 120172, 120173, 120174, 120175, 120176, 120177, 120178, - 120179, 120180, 120181, 120182, 120183, 120184, 120185, 120186, 120187, - 120188, 120189, 120190, 120191, 120192, 120193, 120194, 120195, 120196, - 120197, 120198, 120199, 120200, 120201, 120202, 120203, 120204, 120205, - 120206, 120207, 120208, 120209, 120210, 120211, 120212, 120213, 120214, - 120215, 120216, 120217, 120218, 120219, 120220, 120221, 120222, 120223, - 120787, 120786, 120789, 120788, 120785, 120784, 120782, 120791, 120783, - 120790, 120120, 120121, 120123, 120124, 120125, 120126, 120128, 120129, - 120130, 120131, 120132, 120134, 120138, 120139, 120140, 120141, 120142, - 120143, 120144, 120146, 120147, 120148, 120149, 120150, 120151, 120152, - 120153, 120154, 120155, 120156, 120157, 120158, 120159, 120160, 120161, - 120162, 120163, 120164, 120165, 120166, 120167, 120168, 120169, 120170, - 120171, 120797, 120796, 120799, 120798, 120795, 120794, 120792, 120801, - 120793, 120800, 120068, 120069, 120071, 120072, 120073, 120074, 120077, - 120078, 120079, 120080, 120081, 120082, 120083, 120084, 120086, 120087, - 120088, 120089, 120090, 120091, 120092, 120094, 120095, 120096, 120097, - 120098, 120099, 120100, 120101, 120102, 120103, 120104, 120105, 120106, - 120107, 120108, 120109, 120110, 120111, 120112, 120113, 120114, 120115, - 120116, 120117, 120118, 120119, 10189, 119889, 120484, 120485, 120575, - 119886, 120572, 119887, 120573, 119911, 120577, 119890, 120576, 120578, - 119891, 120589, 119892, 120574, 119894, 120580, 119896, 120581, 119897, - 120582, 119900, 120586, 120596, 119901, 120593, 120595, 120587, 119903, - 120588, 119904, 120590, 119905, 120579, 120591, 119906, 120592, 119888, - 120594, 119898, 120583, 119899, 120584, 119909, 120585, 119895, 119902, - 119907, 119908, 119910, 119879, 120553, 120563, 120565, 119860, 120546, - 119861, 120547, 119885, 120551, 119863, 120549, 119864, 120550, 120552, - 119866, 120548, 119868, 120554, 119870, 120555, 119871, 120556, 119874, - 120560, 120570, 119875, 120567, 120569, 120561, 119877, 120562, 119878, - 120564, 119880, 120566, 119862, 120568, 119872, 120557, 119873, 120558, - 119883, 120559, 119865, 119867, 119869, 119876, 119881, 119882, 119884, - 120598, 120600, 120571, 120601, 120597, 120603, 120602, 120599, 120432, - 120433, 120434, 120435, 120436, 120437, 120438, 120439, 120440, 120441, - 120442, 120443, 120444, 120445, 120446, 120447, 120448, 120449, 120450, - 120451, 120452, 120453, 120454, 120455, 120456, 120457, 120458, 120459, - 120460, 120461, 120462, 120463, 120464, 120465, 120466, 120467, 120468, - 120469, 120470, 120471, 120472, 120473, 120474, 120475, 120476, 120477, - 120478, 120479, 120480, 120481, 120482, 120483, 120827, 120826, 120829, - 120828, 120825, 120824, 120822, 120831, 120823, 120830, 10215, 10221, - 10219, 10217, 10223, 10187, 10214, 10220, 10218, 10216, 10222, 120399, - 120727, 120737, 120739, 120380, 120720, 120381, 120721, 120405, 120725, - 120383, 120723, 120384, 120724, 120726, 120386, 120722, 120388, 120728, - 120390, 120729, 120391, 120730, 120394, 120734, 120744, 120395, 120741, - 120743, 120735, 120397, 120736, 120398, 120738, 120400, 120740, 120382, - 120742, 120392, 120731, 120393, 120732, 120403, 120733, 120385, 120387, - 120389, 120396, 120401, 120402, 120404, 120772, 120774, 120745, 120775, - 120771, 120777, 120776, 120406, 120746, 120407, 120747, 120431, 120751, - 120409, 120749, 120410, 120750, 120752, 120411, 120763, 120412, 120748, - 120414, 120754, 120416, 120755, 120417, 120756, 120420, 120760, 120770, - 120421, 120767, 120769, 120761, 120423, 120762, 120424, 120764, 120425, - 120753, 120765, 120426, 120766, 120408, 120768, 120418, 120757, 120419, - 120758, 120429, 120759, 120413, 120415, 120422, 120427, 120428, 120430, - 120773, 120295, 120669, 120679, 120681, 120276, 120662, 120277, 120663, - 120301, 120667, 120279, 120665, 120280, 120666, 120668, 120282, 120664, - 120284, 120670, 120286, 120671, 120287, 120672, 120290, 120676, 120686, - 120291, 120683, 120685, 120677, 120293, 120678, 120294, 120680, 120296, - 120682, 120278, 120684, 120288, 120673, 120289, 120674, 120299, 120675, - 120281, 120283, 120285, 120292, 120297, 120298, 120300, 120714, 120716, - 120687, 120717, 120713, 120719, 120718, 120302, 120688, 120303, 120689, - 120327, 120693, 120305, 120691, 120306, 120692, 120694, 120307, 120705, - 120308, 120690, 120310, 120696, 120312, 120697, 120313, 120698, 120316, - 120702, 120712, 120317, 120709, 120711, 120703, 120319, 120704, 120320, - 120706, 120321, 120695, 120707, 120322, 120708, 120304, 120710, 120314, - 120699, 120315, 120700, 120325, 120701, 120309, 120311, 120318, 120323, - 120324, 120326, 120715, 120817, 120816, 120819, 120818, 120815, 120814, - 120812, 120821, 120813, 120820, 120328, 120329, 120330, 120331, 120332, - 120333, 120334, 120335, 120336, 120337, 120338, 120339, 120340, 120341, - 120342, 120343, 120344, 120345, 120346, 120347, 120348, 120349, 120350, - 120351, 120352, 120353, 120354, 120355, 120356, 120357, 120358, 120359, - 120360, 120361, 120362, 120363, 120364, 120365, 120366, 120367, 120368, - 120369, 120370, 120371, 120372, 120373, 120374, 120375, 120376, 120377, - 120378, 120379, 120224, 120225, 120226, 120227, 120228, 120229, 120230, - 120231, 120232, 120233, 120234, 120235, 120236, 120237, 120238, 120239, - 120240, 120241, 120242, 120243, 120244, 120245, 120246, 120247, 120248, - 120249, 120250, 120251, 120252, 120253, 120254, 120255, 120256, 120257, - 120258, 120259, 120260, 120261, 120262, 120263, 120264, 120265, 120266, - 120267, 120268, 120269, 120270, 120271, 120272, 120273, 120274, 120275, - 120807, 120806, 120809, 120808, 120805, 120804, 120802, 120811, 120803, - 120810, 119964, 119966, 119967, 119970, 119973, 119974, 119977, 119978, - 119979, 119980, 119982, 119983, 119984, 119985, 119986, 119987, 119988, - 119989, 119990, 119991, 119992, 119993, 119995, 119997, 119998, 119999, - 120000, 120001, 120002, 120003, 120005, 120006, 120007, 120008, 120009, - 120010, 120011, 120012, 120013, 120014, 120015, 129481, 119528, 119538, - 119531, 119535, 119525, 119524, 119534, 119529, 119539, 119527, 119537, - 119526, 119536, 119533, 119523, 119532, 119522, 119530, 119520, 119521, - 128470, 175, 8737, 10667, 10666, 10671, 10669, 10670, 10668, 10665, - 10664, 10651, 10653, 8798, 127830, 129470, 129471, 93773, 93764, 93790, - 93787, 983270, 93783, 983269, 93782, 93772, 93766, 93791, 93779, 93789, - 93786, 93776, 93777, 93785, 93775, 93770, 93769, 93771, 93774, 93780, - 93760, 93767, 93781, 93788, 93761, 93768, 93778, 93762, 93763, 93784, - 93765, 93847, 93827, 93846, 93826, 93845, 93825, 93844, 93829, 93828, - 93831, 93830, 93824, 93833, 93832, 93837, 93836, 93834, 93842, 93835, - 93838, 93839, 93843, 93840, 93841, 93805, 93796, 93822, 93819, 983272, - 93815, 983271, 93814, 93804, 93798, 93823, 93811, 93821, 93818, 93808, - 93809, 93817, 93807, 93802, 93801, 93803, 93806, 93812, 93792, 93799, - 93813, 93820, 93793, 93800, 93810, 93794, 93795, 93816, 93797, 93849, - 93850, 93848, 11859, 11852, 11860, 128901, 9899, 10090, 10091, 128967, - 128965, 128944, 10100, 10088, 10092, 10101, 10089, 10093, 8287, 9900, - 9618, 128971, 128950, 128938, 128963, 128961, 10073, 128974, 128956, - 9898, 128911, 128931, 43761, 44013, 43762, 43760, 44011, 43994, 43989, - 43974, 43746, 43993, 43991, 43751, 43750, 43992, 43986, 43987, 43990, - 43968, 43995, 43976, 43973, 43999, 43977, 44001, 43752, 43747, 43972, - 43998, 43984, 43969, 43753, 43754, 43975, 44000, 43978, 43749, 43748, - 43983, 44002, 43970, 43996, 43971, 43997, 43981, 43988, 43979, 43985, - 43980, 43982, 43744, 43745, 44012, 43763, 43764, 44005, 43757, 43759, - 43758, 44009, 44003, 44007, 44006, 44004, 43755, 44008, 43756, 44010, - 43765, 43766, 44021, 44020, 44023, 44022, 44019, 44018, 44016, 44025, - 44017, 44024, 129760, 127816, 125140, 125137, 125136, 125139, 125141, - 125138, 125142, 124928, 124929, 124930, 124936, 124937, 124949, 124950, - 124948, 124938, 124956, 124990, 124992, 124974, 124955, 124964, 124963, - 124991, 124957, 124962, 124976, 124996, 124997, 124998, 124982, 124983, - 124984, 125004, 124975, 125003, 125005, 125011, 125018, 125028, 125029, - 125012, 125019, 125020, 125027, 125013, 125035, 125048, 124942, 124934, - 125090, 125046, 125078, 125033, 124946, 125037, 125070, 125000, 125095, - 125121, 124951, 125044, 125041, 125072, 124987, 125066, 125076, 125074, - 125009, 125107, 125108, 125068, 125124, 124945, 125002, 124931, 125089, - 125022, 124980, 125099, 124986, 125100, 125080, 125119, 124933, 125021, - 125015, 125071, 124985, 125117, 125056, 124993, 125039, 125049, 125043, - 125024, 124932, 125047, 125097, 124959, 125069, 125088, 124999, 125123, - 124952, 125036, 125026, 125001, 125085, 124960, 125057, 125073, 124966, - 125098, 125014, 125091, 124989, 125007, 124978, 124940, 125106, 125050, - 125030, 125092, 124941, 125060, 125077, 125102, 125094, 125053, 125040, - 125055, 125104, 125103, 124939, 125017, 124961, 125112, 125087, 124970, - 124971, 124969, 125023, 124979, 125042, 124947, 125086, 125075, 125051, - 125111, 124968, 124944, 125038, 125096, 125016, 125118, 125109, 124953, - 125059, 125052, 125006, 124958, 125093, 125115, 125054, 124988, 125008, - 125084, 125061, 125064, 125120, 125063, 124967, 124977, 124965, 125031, - 983275, 125081, 125082, 983276, 125010, 125067, 124973, 125032, 124935, - 125116, 125122, 125101, 124994, 124995, 125113, 125058, 125079, 125114, - 125065, 125034, 125083, 124954, 125062, 125105, 125110, 125045, 124943, - 124972, 124981, 125025, 125131, 125130, 125133, 125132, 125129, 125128, - 125135, 125127, 125134, 128334, 128697, 68028, 68093, 68090, 68089, - 68086, 68029, 68092, 68091, 68095, 68088, 68087, 68094, 68000, 68016, - 68020, 68021, 68022, 68009, 68010, 68015, 68017, 68014, 68013, 68018, - 68006, 68023, 68012, 68008, 68007, 68019, 68011, 68005, 68004, 68001, - 68002, 68003, 68031, 68030, 68039, 68075, 68057, 68084, 68066, 68036, - 68054, 68081, 68063, 68045, 68072, 68035, 68053, 68080, 68062, 68044, - 68071, 68040, 68076, 68058, 68085, 68067, 68038, 68056, 68083, 68065, - 68047, 68074, 68037, 68055, 68082, 68064, 68046, 68073, 68034, 68052, - 68079, 68061, 68043, 68070, 68033, 68051, 68078, 68060, 68042, 68069, - 68041, 68068, 68032, 68050, 68077, 68059, 67974, 67975, 67982, 67983, - 67978, 67979, 67980, 67981, 67987, 67988, 67989, 67992, 67993, 67994, - 67995, 67996, 67986, 67985, 67990, 67997, 67984, 67977, 67976, 67991, - 67973, 67972, 67968, 67969, 67970, 67971, 67998, 67999, 9791, 129500, - 983173, 9170, 9172, 9177, 9176, 9175, 9173, 9174, 9171, 9169, 128647, - 128221, 94015, 93989, 93971, 93958, 94019, 94021, 93953, 94023, 93999, - 93995, 94008, 93981, 93979, 93967, 93963, 93993, 93992, 93983, 93977, - 93976, 93975, 93974, 93968, 94032, 93988, 93987, 93973, 93972, 93997, - 93996, 93969, 94106, 94107, 94108, 94109, 94110, 94111, 94002, 94026, - 94022, 94003, 94004, 94010, 93980, 93978, 94099, 94100, 94101, 94102, - 94103, 94104, 94105, 93998, 93994, 94007, 94025, 93966, 93962, 94024, - 93961, 93960, 94000, 94009, 93964, 93965, 94001, 93970, 93984, 93954, - 94017, 94014, 94016, 94013, 94006, 94012, 94005, 94011, 93986, 93985, - 93955, 93952, 94020, 93990, 93957, 93956, 93959, 93982, 94018, 93991, - 94035, 94034, 94033, 94031, 94098, 94096, 94097, 94095, 94036, 94039, - 94040, 94067, 94068, 94038, 94037, 94073, 94075, 94045, 94071, 94069, - 94046, 94047, 94085, 94074, 94049, 94050, 94051, 94052, 94053, 94086, - 94057, 94054, 94084, 94055, 94056, 94041, 94082, 94048, 94081, 94042, - 94076, 94058, 94059, 94060, 94061, 94063, 94064, 94079, 94087, 94062, - 94065, 94080, 94066, 94072, 94070, 94044, 94043, 94077, 94078, 94083, - 983239, 983240, 128300, 127908, 181, 129440, 117772, 129986, 130022, - 130023, 183, 8943, 129686, 127894, 127756, 8357, 128189, 128469, 128656, - 8722, 10793, 10794, 10796, 10795, 10810, 8770, 8723, 10751, 129694, - 129705, 128241, 128242, 128244, 129339, 8871, 71232, 71229, 71236, 71231, - 71230, 71216, 71226, 71228, 71219, 71220, 71221, 71222, 71223, 71224, - 71217, 71218, 71225, 71227, 71234, 71233, 71253, 71252, 71255, 71254, - 71251, 71250, 71248, 71257, 71249, 71256, 71168, 71169, 71179, 71181, - 71195, 71194, 71200, 71199, 71193, 71192, 71198, 71197, 71174, 71175, - 71176, 71177, 71210, 71172, 71173, 71170, 71171, 71215, 71209, 71186, - 71196, 71191, 71201, 71211, 71212, 71213, 71205, 71204, 71188, 71187, - 71185, 71184, 71190, 71189, 71183, 71182, 71203, 71202, 71214, 71206, - 71208, 71207, 71178, 71180, 71235, 43867, 67512, 714, 700, 67509, 761, - 763, 7470, 7471, 7487, 7474, 7483, 7476, 43000, 7484, 7485, 7468, 7469, - 42994, 7472, 7473, 42995, 7475, 7477, 7478, 7479, 7480, 7481, 7482, 7486, - 42996, 7488, 7489, 11389, 7490, 723, 722, 42755, 42753, 42757, 42759, - 42754, 42752, 42756, 42758, 42652, 122956, 122958, 122929, 122954, - 122932, 122952, 122943, 122987, 122946, 122938, 122939, 122942, 122960, - 122941, 122959, 122989, 122955, 122950, 122948, 122944, 122951, 122988, - 122953, 122934, 122935, 122936, 122933, 122949, 122931, 122957, 122930, - 122947, 122937, 122928, 122940, 122945, 42653, 7544, 710, 735, 42889, - 67510, 42776, 42775, 42777, 750, 698, 725, 709, 984011, 42765, 42760, - 42770, 741, 984012, 42769, 42764, 42774, 745, 762, 764, 715, 704, 67507, - 4348, 42766, 42761, 42771, 742, 721, 67511, 42888, 42768, 42763, 751, - 767, 753, 42773, 717, 754, 755, 744, 759, 719, 718, 42783, 752, 716, - 42778, 703, 43882, 706, 713, 42767, 42762, 42772, 743, 758, 757, 756, - 727, 766, 726, 697, 42780, 42782, 42781, 42779, 760, 705, 67508, 701, - 67513, 702, 43883, 707, 734, 42890, 765, 7491, 7493, 7516, 67459, 7495, - 7601, 7509, 67461, 7517, 7580, 7590, 694, 7591, 67474, 67476, 7595, - 67484, 67491, 67456, 67460, 67478, 7600, 67498, 7608, 67506, 67471, - 67492, 7581, 7521, 7496, 67468, 67469, 67467, 67466, 7519, 7585, 67480, - 67463, 67465, 67464, 7611, 7613, 7612, 7497, 7604, 7582, 7614, 7505, - 7584, 67472, 7501, 7518, 7520, 67475, 736, 688, 689, 67477, 43868, 67479, - 7504, 7596, 7588, 7589, 690, 7592, 737, 43869, 43870, 7593, 67485, 7594, - 67483, 67481, 67482, 67486, 67487, 43001, 7599, 7598, 7506, 67490, 7499, - 7507, 7510, 7602, 691, 67497, 67496, 67473, 740, 7583, 67470, 738, 67514, - 7603, 7586, 7498, 7513, 7511, 7605, 67503, 67499, 67502, 7508, 67500, - 67501, 7492, 7579, 7494, 7514, 7597, 7500, 692, 67494, 67495, 693, 67488, - 67489, 7587, 7502, 7610, 43881, 7615, 7512, 43871, 7606, 7607, 7515, - 67504, 7609, 7503, 67493, 695, 739, 696, 42784, 42785, 67458, 67457, 720, - 699, 724, 708, 749, 42864, 748, 712, 747, 746, 10762, 128184, 128176, - 129297, 71266, 6165, 6164, 6167, 6166, 6163, 6162, 6160, 6169, 6161, - 6168, 6159, 6157, 6156, 6155, 6147, 6149, 71271, 71272, 6176, 6279, 6272, - 6295, 6273, 6289, 6274, 6313, 6286, 6311, 6310, 6280, 6276, 6275, 6282, - 6287, 6278, 6285, 6284, 6288, 6277, 6291, 6290, 6293, 6294, 6292, 6283, - 6281, 6185, 6196, 6264, 6210, 6190, 6303, 6305, 6307, 6300, 6302, 6304, - 6312, 6298, 6301, 6314, 6308, 6309, 6299, 6306, 6263, 6262, 6260, 6261, - 6259, 6244, 6252, 6245, 6253, 6238, 6239, 6254, 6248, 6257, 6247, 6256, - 6242, 6258, 6255, 6241, 6240, 6249, 6251, 6250, 6243, 6246, 6237, 6193, - 6192, 6297, 6296, 6218, 6236, 6225, 6234, 6227, 6235, 6211, 6222, 6232, - 6228, 6224, 6226, 6233, 6214, 6216, 6215, 6217, 6219, 6231, 6223, 6220, - 6221, 6230, 6229, 6212, 6213, 6204, 6194, 6209, 6207, 6205, 6206, 6203, - 6202, 6208, 6191, 6177, 6183, 6179, 6181, 6180, 6182, 6186, 6195, 6201, - 6189, 6197, 6184, 6187, 6188, 6199, 6200, 6198, 6178, 71273, 71275, - 71274, 6151, 71265, 71270, 71269, 6144, 71268, 71264, 71267, 71276, 6150, - 6158, 6148, 6146, 6152, 6153, 6154, 6145, 9866, 9867, 119552, 9101, - 128669, 128018, 128053, 127889, 129390, 118261, 128496, 129742, 129439, - 128332, 129334, 128741, 128757, 129468, 128739, 9968, 128670, 128672, - 128693, 128507, 128001, 129700, 128045, 128068, 128511, 127909, 92748, - 92744, 92761, 92750, 92739, 92751, 92737, 92754, 92749, 92753, 92743, - 92752, 92757, 92766, 92736, 92741, 92746, 92764, 92745, 92765, 92755, - 92760, 92758, 92756, 92763, 92762, 92747, 92738, 92740, 92759, 92742, - 92783, 92782, 92773, 92772, 92775, 92774, 92771, 92770, 92768, 92777, - 92769, 92776, 70291, 70292, 70290, 70297, 70296, 70293, 70287, 70298, - 70312, 70311, 70306, 70285, 70284, 70289, 70288, 70295, 70294, 70303, - 70301, 70283, 70282, 70280, 70278, 70277, 70276, 70300, 70299, 70310, - 70307, 70304, 70309, 70308, 70305, 70272, 70275, 70273, 70274, 70313, - 127926, 215, 10804, 10805, 10807, 10811, 10801, 10800, 10005, 8844, 8845, - 8846, 8888, 127812, 117860, 9838, 9837, 9839, 127929, 127896, 119161, - 119159, 119155, 119157, 119061, 119060, 119224, 119235, 119132, 119058, - 119059, 119255, 119253, 119130, 119131, 119163, 119169, 119149, 119167, - 119168, 119210, 119178, 119173, 119211, 119150, 119151, 119152, 119153, - 119154, 119175, 119212, 119213, 119166, 119164, 119141, 119142, 119176, - 119179, 119143, 119144, 119145, 119165, 119177, 119170, 119174, 119092, - 119052, 119247, 119186, 119073, 119109, 119093, 119050, 119220, 119221, - 119044, 119049, 119187, 119209, 119082, 119041, 119083, 119077, 119078, - 119162, 119160, 119208, 119156, 119158, 119136, 119102, 119056, 119057, - 119146, 119147, 119148, 119042, 119066, 119065, 119069, 119185, 119074, - 119075, 119076, 119231, 119232, 119085, 119084, 119070, 119071, 119072, - 119218, 119217, 119189, 119188, 119248, 119249, 119172, 119171, 119216, - 119134, 119100, 119206, 119262, 119270, 119271, 119263, 119268, 119269, - 119272, 119264, 119267, 119266, 119265, 119274, 119223, 119234, 119233, - 119046, 119222, 119184, 119227, 119237, 119228, 119122, 119123, 119081, - 119098, 119207, 119129, 119087, 119086, 119128, 119140, 119106, 119062, - 119195, 119204, 119205, 119196, 119197, 119198, 119199, 119200, 119201, - 119202, 119203, 119094, 119095, 119126, 119108, 119215, 119214, 119261, - 119252, 119257, 119258, 119183, 119090, 119091, 119135, 119101, 119096, - 119097, 119053, 119054, 119055, 119048, 119043, 119047, 119180, 119254, - 119259, 119225, 119236, 119226, 119229, 119238, 119230, 119051, 119045, - 119089, 119088, 119040, 119067, 119068, 119137, 119103, 119139, 119105, - 119110, 119111, 119250, 119181, 119273, 119243, 119244, 119245, 119246, - 119242, 119240, 119239, 119241, 119064, 119138, 119104, 119063, 119256, - 119260, 119190, 119118, 119119, 119120, 119121, 119112, 119113, 119116, - 119117, 119114, 119115, 119124, 119125, 119191, 119193, 119194, 119127, - 119251, 119107, 119133, 119099, 119219, 119192, 119182, 127932, 127925, - 8811, 8810, 4158, 4156, 4157, 4155, 4192, 4191, 4190, 4226, 4129, 43642, - 4138, 4135, 4208, 4207, 4206, 4159, 4099, 4098, 4097, 43625, 43624, - 43626, 43623, 43622, 43621, 43627, 43618, 43617, 43630, 43629, 43620, - 43619, 983244, 43631, 43616, 43635, 43628, 43633, 43634, 4096, 4188, - 4189, 4187, 4186, 4136, 4121, 4106, 4111, 4100, 4105, 4116, 4238, 4123, - 4176, 43491, 4218, 4220, 43490, 4221, 4224, 43492, 4223, 43489, 4216, - 43488, 4215, 4214, 4213, 4219, 4222, 4225, 4217, 4130, 43646, 43647, - 4193, 4177, 4126, 4112, 43503, 43495, 43502, 43501, 43516, 43515, 43518, - 43517, 43498, 43497, 43500, 43499, 43514, 43496, 4108, 4107, 4113, 4198, - 4197, 4125, 4110, 4109, 4115, 4114, 4133, 4134, 4178, 4179, 4180, 4181, - 4131, 4132, 4128, 4124, 4120, 4119, 4102, 4101, 4104, 4103, 4118, 4117, - 4127, 4122, 4137, 43636, 43637, 43638, 43632, 43494, 4245, 4244, 4247, - 4246, 4243, 4242, 4240, 4249, 4241, 4248, 4154, 4150, 4250, 4251, 4237, - 4235, 4236, 4231, 4232, 4233, 4234, 43493, 4171, 43644, 43645, 4201, - 4202, 4203, 4204, 4205, 4151, 4239, 43643, 4170, 4153, 4152, 43639, - 43641, 43640, 4174, 4172, 4255, 4254, 4175, 4173, 71391, 71390, 71393, - 71392, 71389, 71388, 71386, 71395, 71387, 71394, 4195, 4196, 43509, - 43508, 43511, 43510, 43507, 43506, 43504, 43513, 43505, 43512, 4146, - 4252, 4253, 4140, 4209, 4212, 4210, 4211, 4147, 4148, 4228, 4229, 4230, - 4227, 4194, 4145, 4149, 4199, 4200, 4139, 4143, 4144, 4182, 4183, 4184, - 4185, 4141, 4142, 71381, 71380, 71383, 71382, 71379, 71378, 71376, 71385, - 71377, 71384, 4165, 4164, 4167, 4166, 4163, 4162, 4160, 4169, 4161, 4168, - 983218, 983232, 983174, 10753, 10754, 10752, 8720, 10761, 10757, 10758, - 8721, 8899, 10756, 10755, 11007, 8896, 8897, 8898, 8719, 67712, 67728, - 67740, 67724, 67726, 67714, 67732, 67718, 67730, 67723, 67742, 67717, - 67729, 67738, 67739, 67713, 67735, 67716, 67721, 67734, 67737, 67741, - 67725, 67719, 67722, 67727, 67715, 67733, 67720, 67736, 67731, 67758, - 67752, 67753, 67757, 67751, 67759, 67756, 67754, 67755, 8711, 124117, - 124120, 124119, 124121, 124118, 124132, 124136, 124133, 124138, 124137, - 124134, 124135, 124122, 124124, 124126, 124123, 124125, 124112, 124116, - 124114, 124113, 124115, 124127, 124128, 124129, 124130, 124131, 124140, - 124143, 124142, 124139, 124141, 124149, 124148, 124151, 124150, 124147, - 124146, 124144, 124153, 124145, 124152, 128133, 8358, 8892, 72102, 72103, - 72138, 72096, 72097, 72107, 72109, 72123, 72122, 72128, 72127, 72144, - 72136, 72121, 72120, 72126, 72125, 72100, 72101, 72098, 72099, 72143, - 72137, 72114, 72124, 72119, 72129, 72139, 72140, 72141, 72133, 72132, - 72116, 72115, 72113, 72112, 72118, 72117, 72111, 72110, 72131, 72130, - 72142, 72134, 72135, 72106, 72108, 72162, 72161, 72158, 72160, 72159, - 72164, 72150, 72151, 72145, 72155, 72157, 72148, 72149, 72146, 72147, - 72154, 72156, 72163, 8302, 127966, 129314, 128219, 129535, 8239, 983092, - 983197, 983128, 9471, 9453, 9460, 9452, 9458, 9451, 9454, 9455, 9459, - 9456, 9457, 127312, 127313, 127314, 127315, 127316, 127317, 127318, - 127319, 127320, 127321, 127322, 127323, 127324, 127325, 127326, 127327, - 127328, 127329, 127330, 127331, 127332, 127333, 127334, 127335, 127336, - 127337, 128982, 128984, 129982, 129981, 129983, 127344, 127345, 127346, - 127347, 127348, 127349, 127350, 127351, 127352, 127353, 127354, 127355, - 127356, 127357, 127358, 127359, 127360, 127361, 127362, 127363, 127364, - 127365, 127366, 127367, 127368, 127369, 129204, 129205, 129207, 129988, - 10062, 127374, 127371, 127375, 129206, 127372, 127373, 983091, 8879, - 8840, 8841, 8775, 8821, 8817, 8825, 8820, 8816, 8824, 129670, 129722, - 11228, 129540, 129544, 129565, 129586, 129603, 129607, 129561, 129536, - 129557, 129599, 129539, 129560, 129602, 129610, 129613, 129541, 129562, - 129604, 129537, 129558, 129600, 129538, 129559, 129601, 129581, 129578, - 129582, 129583, 129579, 129580, 128528, 9906, 127770, 127761, 8362, 6595, - 6594, 6599, 6598, 6597, 6596, 6593, 6566, 6530, 6567, 6531, 6570, 6537, - 6532, 6544, 6543, 6536, 6542, 6549, 6548, 6562, 6561, 6554, 6560, 6556, - 6550, 6528, 6555, 6538, 6568, 6533, 6569, 6534, 6571, 6540, 6535, 6547, - 6546, 6539, 6545, 6552, 6551, 6565, 6564, 6557, 6563, 6559, 6553, 6529, - 6558, 6541, 6622, 6623, 6600, 6601, 6618, 6577, 6587, 6582, 6586, 6578, - 6592, 6583, 6584, 6590, 6589, 6579, 6585, 6591, 6580, 6588, 6576, 6581, - 6613, 6612, 6615, 6614, 6611, 6610, 6608, 6617, 6609, 6616, 983063, - 70732, 70746, 70731, 70741, 70740, 70743, 70742, 70739, 70738, 70736, - 70745, 70737, 70744, 70734, 70675, 70674, 70681, 70680, 70692, 70686, - 70691, 70751, 70662, 70663, 70664, 70665, 70656, 70657, 70667, 70669, - 70685, 70684, 70690, 70689, 70683, 70682, 70688, 70687, 70660, 70661, - 70658, 70659, 70705, 70706, 70707, 70696, 70695, 70677, 70676, 70673, - 70672, 70679, 70678, 70671, 70670, 70703, 70702, 70698, 70697, 70694, - 70693, 70701, 70700, 70708, 70704, 70699, 70666, 70668, 70723, 70726, - 70727, 70724, 70752, 70728, 70753, 70722, 70725, 70730, 70750, 70747, - 70709, 70719, 70721, 70712, 70713, 70714, 70715, 70716, 70717, 70710, - 70711, 70718, 70720, 70735, 70749, 70733, 70729, 11154, 11155, 128240, - 9112, 983131, 9798, 11209, 128084, 129299, 983132, 128985, 129399, - 127747, 2035, 2031, 2032, 2033, 2030, 2034, 2027, 2028, 2029, 2040, 2045, - 2046, 1989, 1988, 1991, 1990, 1987, 1986, 1984, 1993, 1985, 1992, 2042, - 2008, 2001, 2025, 2024, 2026, 2006, 2002, 2019, 2016, 2018, 2023, 2010, - 2009, 2000, 1999, 2007, 1997, 1995, 2012, 2003, 2013, 2020, 2014, 2015, - 2017, 2004, 2011, 2005, 2021, 2022, 1994, 1996, 1998, 2037, 2036, 2039, - 2038, 2041, 2047, 983127, 128691, 9940, 128683, 128695, 128370, 128286, - 128245, 128685, 8303, 65934, 8209, 128689, 10973, 8893, 8599, 10542, - 10545, 10536, 10532, 129209, 10530, 128602, 128594, 128610, 11111, 11127, - 11016, 8663, 129109, 11008, 43063, 43062, 43065, 43064, 43059, 43060, - 43057, 43056, 43061, 43058, 10529, 8598, 8689, 8632, 10546, 10535, 10531, - 129208, 128600, 128592, 128608, 11110, 11126, 11017, 8662, 129108, 11009, - 128746, 8882, 8884, 8379, 8836, 8837, 8713, 8772, 8777, 8938, 8940, 8742, - 8930, 8931, 172, 8877, 8769, 8813, 8800, 8802, 8815, 8814, 9083, 128323, - 10159, 128324, 10161, 128456, 128457, 128458, 128211, 128212, 160, - 128067, 9369, 9362, 9365, 9366, 9367, 35, 9368, 9364, 9361, 9363, 9371, - 9370, 8470, 110960, 110961, 110962, 110963, 110964, 110965, 110966, - 110967, 110968, 110969, 110970, 110971, 110972, 110973, 110974, 110975, - 110976, 110977, 110978, 110979, 110980, 110981, 110982, 110983, 110984, - 110985, 110986, 110987, 110988, 110989, 110990, 110991, 110992, 110993, - 110994, 110995, 110996, 110997, 110998, 110999, 111000, 111001, 111002, - 111003, 111004, 111005, 111006, 111007, 111008, 111009, 111010, 111011, - 111012, 111013, 111014, 111015, 111016, 111017, 111018, 111019, 111020, - 111021, 111022, 111023, 111024, 111025, 111026, 111027, 111028, 111029, - 111030, 111031, 111032, 111033, 111034, 111035, 111036, 111037, 111038, - 111039, 111040, 111041, 111042, 111043, 111044, 111045, 111046, 111047, - 111048, 111049, 111050, 111051, 111052, 111053, 111054, 111055, 111056, - 111057, 111058, 111059, 111060, 111061, 111062, 111063, 111064, 111065, - 111066, 111067, 111068, 111069, 111070, 111071, 111072, 111073, 111074, - 111075, 111076, 111077, 111078, 111079, 111080, 111081, 111082, 111083, - 111084, 111085, 111086, 111087, 111088, 111089, 111090, 111091, 111092, - 111093, 111094, 111095, 111096, 111097, 111098, 111099, 111100, 111101, - 111102, 111103, 111104, 111105, 111106, 111107, 111108, 111109, 111110, - 111111, 111112, 111113, 111114, 111115, 111116, 111117, 111118, 111119, - 111120, 111121, 111122, 111123, 111124, 111125, 111126, 111127, 111128, - 111129, 111130, 111131, 111132, 111133, 111134, 111135, 111136, 111137, - 111138, 111139, 111140, 111141, 111142, 111143, 111144, 111145, 111146, - 111147, 111148, 111149, 111150, 111151, 111152, 111153, 111154, 111155, - 111156, 111157, 111158, 111159, 111160, 111161, 111162, 111163, 111164, - 111165, 111166, 111167, 111168, 111169, 111170, 111171, 111172, 111173, - 111174, 111175, 111176, 111177, 111178, 111179, 111180, 111181, 111182, - 111183, 111184, 111185, 111186, 111187, 111188, 111189, 111190, 111191, - 111192, 111193, 111194, 111195, 111196, 111197, 111198, 111199, 111200, - 111201, 111202, 111203, 111204, 111205, 111206, 111207, 111208, 111209, - 111210, 111211, 111212, 111213, 111214, 111215, 111216, 111217, 111218, - 111219, 111220, 111221, 111222, 111223, 111224, 111225, 111226, 111227, - 111228, 111229, 111230, 111231, 111232, 111233, 111234, 111235, 111236, - 111237, 111238, 111239, 111240, 111241, 111242, 111243, 111244, 111245, - 111246, 111247, 111248, 111249, 111250, 111251, 111252, 111253, 111254, - 111255, 111256, 111257, 111258, 111259, 111260, 111261, 111262, 111263, - 111264, 111265, 111266, 111267, 111268, 111269, 111270, 111271, 111272, - 111273, 111274, 111275, 111276, 111277, 111278, 111279, 111280, 111281, - 111282, 111283, 111284, 111285, 111286, 111287, 111288, 111289, 111290, - 111291, 111292, 111293, 111294, 111295, 111296, 111297, 111298, 111299, - 111300, 111301, 111302, 111303, 111304, 111305, 111306, 111307, 111308, - 111309, 111310, 111311, 111312, 111313, 111314, 111315, 111316, 111317, - 111318, 111319, 111320, 111321, 111322, 111323, 111324, 111325, 111326, - 111327, 111328, 111329, 111330, 111331, 111332, 111333, 111334, 111335, - 111336, 111337, 111338, 111339, 111340, 111341, 111342, 111343, 111344, - 111345, 111346, 111347, 111348, 111349, 111350, 111351, 111352, 111353, - 111354, 111355, 94177, 128297, 983041, 983040, 123149, 123155, 123138, - 123166, 123164, 123148, 123143, 123161, 123153, 123152, 123141, 123137, - 123156, 123139, 123163, 123142, 123172, 123173, 123140, 123167, 123171, - 123158, 123176, 123177, 123165, 123151, 123168, 123136, 123169, 123162, - 123178, 123179, 123144, 123157, 123170, 123150, 123145, 123159, 123146, - 123154, 123160, 123147, 123174, 123175, 123180, 123214, 123193, 123192, - 123195, 123194, 123191, 123196, 123197, 123184, 123190, 123189, 123186, - 123185, 123188, 123187, 123215, 123205, 123204, 123207, 123206, 123203, - 123202, 123200, 123209, 123201, 123208, 117776, 983231, 983066, 10663, - 10662, 11869, 9215, 65532, 9287, 9286, 9284, 9285, 9289, 9281, 9290, - 9288, 9282, 9283, 9280, 128721, 128025, 128885, 127970, 5787, 5788, 5776, - 5761, 5786, 5770, 5769, 5781, 5779, 5785, 5763, 5772, 5780, 5784, 5762, - 5775, 5773, 5765, 5777, 5782, 5764, 5774, 5783, 5766, 5778, 5771, 5767, - 5768, 5760, 731, 128738, 7265, 7264, 7266, 7267, 7261, 7260, 7262, 7259, - 7280, 7282, 7281, 7279, 7271, 7270, 7272, 7269, 7258, 7263, 7278, 7268, - 7283, 7273, 7284, 7285, 7287, 7286, 7276, 7274, 7275, 7277, 7290, 7288, - 7289, 7295, 7294, 7292, 7291, 7253, 7252, 7255, 7254, 7251, 7250, 7248, - 7257, 7249, 7256, 7293, 124374, 124376, 124375, 124377, 124378, 124379, - 124392, 124396, 124395, 124397, 124394, 124393, 124380, 124381, 124383, - 124384, 124385, 124382, 124368, 124371, 124370, 124369, 124372, 124373, - 124386, 124388, 124390, 124389, 124387, 124391, 124399, 124400, 124398, - 124415, 124406, 124405, 124408, 124407, 124404, 124403, 124401, 124410, - 124402, 124409, 94179, 94178, 68736, 68739, 68744, 68737, 68756, 68745, - 68760, 68769, 68761, 68775, 68785, 68741, 68762, 68772, 68773, 68740, - 68777, 68742, 68749, 68750, 68758, 68759, 68774, 68776, 68783, 68784, - 68738, 68743, 68747, 68748, 68751, 68754, 68755, 68768, 68770, 68782, - 68765, 68780, 68766, 68781, 68763, 68767, 68764, 68778, 68757, 68786, - 68779, 68746, 68752, 68753, 68771, 68800, 68803, 68808, 68801, 68820, - 68809, 68824, 68833, 68825, 68839, 68849, 68805, 68826, 68836, 68837, - 68804, 68841, 68806, 68813, 68814, 68822, 68823, 68838, 68840, 68847, - 68848, 68802, 68807, 68811, 68812, 68815, 68818, 68819, 68832, 68834, - 68846, 68829, 68844, 68830, 68845, 68827, 68831, 68828, 68842, 68821, - 68850, 68843, 68810, 68816, 68817, 68835, 68861, 68859, 68858, 68862, - 68863, 68860, 66308, 66324, 66318, 66335, 66323, 66331, 66327, 66330, - 66315, 66316, 66317, 66329, 66314, 66306, 66322, 66351, 66321, 66350, - 66326, 66334, 66313, 66333, 66328, 66320, 66312, 66325, 66332, 66305, - 66307, 66311, 66309, 66349, 66310, 66304, 66319, 66339, 66337, 66338, - 66336, 68241, 68242, 68246, 68244, 68226, 68224, 68237, 68235, 68249, - 68251, 68247, 68233, 68248, 68252, 68227, 68234, 68230, 68239, 68232, - 68240, 68231, 68250, 68236, 68225, 68243, 68245, 68228, 68229, 68238, - 68255, 68254, 68253, 66404, 66390, 66392, 66387, 66388, 66411, 66393, - 66421, 66418, 66396, 66397, 66399, 66406, 66405, 66401, 66413, 66402, - 66398, 66414, 66415, 66416, 66408, 66420, 66417, 66407, 66419, 66389, - 66391, 66395, 66400, 66386, 66409, 66410, 66385, 66384, 66394, 66412, - 66403, 66516, 66514, 66515, 66517, 66513, 66464, 66504, 66505, 66506, - 66482, 66510, 66511, 66477, 66508, 66509, 66478, 66479, 66473, 66474, - 66490, 66491, 66480, 66475, 66476, 66507, 66471, 66469, 66470, 66467, - 66468, 66484, 66485, 66492, 66493, 66486, 66487, 66488, 66497, 66498, - 66495, 66472, 66483, 66499, 66494, 66481, 66489, 66496, 66465, 66466, - 66512, 128435, 118450, 69414, 69395, 69376, 69394, 69391, 69392, 69398, - 69399, 69403, 69404, 69377, 69379, 69382, 69400, 69388, 69380, 69384, - 69393, 69397, 69401, 69386, 69381, 69385, 69387, 69378, 69390, 69402, - 69383, 69396, 69389, 69415, 69407, 69412, 69411, 69406, 69410, 69405, - 69413, 69409, 69408, 68209, 68210, 68217, 68211, 68213, 68214, 68212, - 68203, 68205, 68207, 68206, 68202, 68198, 68220, 68219, 68215, 68201, - 68216, 68193, 68196, 68199, 68218, 68192, 68194, 68200, 68204, 68197, - 68208, 68195, 68222, 68221, 68223, 68608, 68619, 68627, 68623, 68634, - 68640, 68644, 68668, 68670, 68677, 68632, 68669, 68671, 68617, 68625, - 68621, 68638, 68643, 68660, 68666, 68675, 68630, 68648, 68653, 68646, - 68650, 68641, 68673, 68658, 68642, 68655, 68628, 68611, 68657, 68662, - 68614, 68615, 68636, 68656, 68664, 68679, 68680, 68609, 68610, 68645, - 68654, 68620, 68624, 68635, 68678, 68633, 68672, 68652, 68618, 68626, - 68622, 68639, 68661, 68667, 68676, 68631, 68613, 68649, 68647, 68651, - 68674, 68659, 68629, 68612, 68663, 68616, 68637, 68665, 69509, 69508, - 69507, 69506, 69488, 69502, 69496, 69505, 69492, 69499, 69501, 69503, - 69494, 69493, 69490, 69495, 69489, 69498, 69504, 69491, 69500, 69497, - 69511, 69512, 69513, 69510, 128477, 128117, 129491, 128116, 129746, - 128283, 128664, 128753, 128662, 128660, 128653, 11819, 8228, 128431, - 129649, 129477, 128214, 9251, 10044, 10027, 10034, 10011, 128194, 128449, - 128080, 128237, 128236, 10180, 10179, 128275, 9103, 9104, 10174, 983191, - 8997, 128191, 128440, 9934, 9741, 128217, 129505, 129447, 8886, 2902, - 2903, 2933, 2934, 2931, 2930, 2935, 2932, 2928, 2909, 2908, 2864, 2911, - 2863, 2821, 2822, 2832, 2836, 2850, 2849, 2855, 2854, 2848, 2847, 2853, - 2852, 2827, 2912, 2828, 2913, 2869, 2825, 2826, 2823, 2824, 2867, 2866, - 2841, 2851, 2846, 2856, 2870, 2871, 2872, 2861, 2860, 2843, 2842, 2840, - 2839, 2845, 2844, 2838, 2837, 2859, 2858, 2873, 2862, 2929, 2831, 2835, - 983660, 983659, 2817, 2876, 2877, 2818, 2901, 2893, 2819, 2878, 2888, - 2892, 2881, 2882, 2883, 2884, 2914, 2915, 2879, 2880, 2887, 2891, 2923, - 2922, 2925, 2924, 2921, 2920, 2918, 2927, 2919, 2926, 64830, 64831, 9766, - 117826, 10183, 128895, 66736, 66737, 66738, 66739, 66743, 66763, 66761, - 66742, 66749, 66757, 66744, 66768, 66750, 66748, 66754, 66755, 66764, - 66762, 66760, 66746, 66745, 66741, 66765, 66769, 66759, 66758, 66771, - 66770, 66740, 66751, 66752, 66753, 66756, 66767, 66747, 66766, 66776, - 66777, 66778, 66779, 66783, 66803, 66801, 66782, 66789, 66797, 66784, - 66808, 66790, 66788, 66794, 66795, 66804, 66802, 66800, 66786, 66785, - 66781, 66805, 66809, 66799, 66798, 66811, 66810, 66780, 66791, 66792, - 66793, 66796, 66807, 66787, 66806, 66710, 66688, 66715, 66699, 66694, - 66698, 66703, 66693, 66697, 66696, 66705, 66702, 66713, 66717, 66704, - 66706, 66707, 66711, 66716, 66689, 66701, 66700, 66708, 66691, 66695, - 66690, 66692, 66709, 66712, 66714, 66725, 66724, 66727, 66726, 66723, - 66722, 66720, 66729, 66721, 66728, 983192, 126257, 126264, 126258, - 126259, 126265, 126260, 126263, 126267, 126255, 126266, 126256, 126262, - 126261, 126269, 126268, 126254, 126216, 126225, 126252, 126234, 126243, - 126222, 126249, 126213, 126231, 126240, 126221, 126248, 126212, 126230, - 126239, 126217, 126226, 126253, 126235, 126244, 126215, 126224, 126251, - 126233, 126242, 126214, 126223, 126250, 126232, 126241, 126220, 126247, - 126211, 126229, 126238, 126219, 126246, 126210, 126228, 126237, 126218, - 126245, 126209, 126227, 126236, 129446, 128228, 117974, 117975, 117976, - 117977, 117978, 117979, 117980, 117981, 117982, 117983, 117984, 117985, - 117986, 117987, 117988, 117989, 117990, 117991, 117992, 117993, 117994, - 117995, 117996, 117997, 117998, 117999, 10015, 9885, 10029, 10009, - 118005, 118004, 118007, 118006, 118003, 118002, 118000, 118009, 118001, - 118008, 8485, 129397, 128471, 11195, 11194, 11196, 8254, 129450, 8486, - 128076, 127842, 128329, 129417, 128002, 983122, 983121, 128463, 128195, - 128479, 128196, 128223, 128464, 128724, 93059, 93065, 93064, 93058, - 93070, 93056, 93055, 93053, 93062, 93069, 93061, 93066, 93071, 93068, - 93054, 93057, 93063, 93067, 93060, 92967, 92975, 92965, 92969, 92959, - 92968, 92971, 92957, 92962, 92960, 92972, 92970, 92963, 92958, 92966, - 92961, 92956, 92974, 92964, 92973, 92979, 92978, 92980, 92977, 92976, - 92982, 92981, 93023, 93020, 93024, 93021, 93019, 93025, 93022, 93043, - 92985, 93047, 93044, 93045, 92997, 93046, 93042, 93032, 93029, 92995, - 93038, 92993, 93041, 93035, 93033, 93037, 93030, 93039, 92987, 92992, - 92986, 92983, 92984, 93027, 92994, 93034, 92988, 92990, 92989, 92991, - 93028, 92996, 93031, 93040, 93036, 92954, 92955, 92938, 92939, 92932, - 92933, 92942, 92943, 92950, 92951, 92928, 92929, 92936, 92937, 92948, - 92949, 92930, 92931, 92944, 92945, 92934, 92935, 92940, 92941, 92946, - 92947, 92952, 92953, 93013, 93012, 93015, 93014, 93011, 93010, 93008, - 93017, 93009, 93016, 9908, 11801, 127796, 129779, 129780, 67703, 67693, - 67688, 67702, 67683, 67691, 67699, 67700, 67680, 67696, 67682, 67686, - 67695, 67698, 67701, 67689, 67684, 67687, 67690, 67681, 67694, 67685, - 67697, 67692, 67704, 67711, 67706, 67707, 67710, 67709, 67708, 67705, - 129330, 129374, 128060, 8233, 11853, 11791, 10995, 10994, 8741, 129666, - 12809, 12823, 12808, 12822, 12828, 12813, 12827, 12810, 12824, 12800, - 12814, 12804, 12818, 12801, 12815, 12812, 12826, 12805, 12819, 12803, - 12817, 12806, 12820, 12811, 12825, 12802, 12816, 12807, 12821, 12863, - 12855, 12858, 12861, 12847, 12839, 12854, 12843, 12836, 12864, 12835, - 12856, 12846, 12842, 12840, 12852, 12862, 12865, 12857, 12867, 12838, - 12866, 12851, 12853, 12859, 12849, 12860, 12848, 12837, 12834, 12841, - 12833, 12845, 12844, 12850, 12832, 12830, 12829, 9349, 9342, 9345, 9346, - 9350, 9347, 9348, 9344, 9351, 9343, 9341, 9336, 9335, 9338, 9337, 9334, - 9333, 9340, 9332, 9339, 127248, 127249, 127250, 127251, 127252, 127253, - 127254, 127255, 127256, 127257, 127258, 127259, 127260, 127261, 127262, - 127263, 127264, 127265, 127266, 127267, 127268, 127269, 127270, 127271, - 127272, 127273, 9372, 9373, 9374, 9375, 9376, 9377, 9378, 9379, 9380, - 9381, 9382, 9383, 9384, 9385, 9386, 9387, 9388, 9389, 9390, 9391, 9392, - 9393, 9394, 9395, 9396, 9397, 8706, 983147, 983146, 983149, 983150, 9853, - 127881, 12880, 12349, 129436, 128755, 11261, 9105, 9106, 128706, 72437, - 72440, 72432, 72416, 72419, 72413, 72417, 72415, 72412, 72414, 72418, - 72420, 72403, 72407, 72411, 72409, 72410, 72391, 72400, 72404, 72397, - 72394, 72385, 72401, 72384, 72399, 72398, 72396, 72388, 72393, 72392, - 72386, 72387, 72402, 72395, 72390, 72389, 72405, 72406, 72408, 72436, - 72435, 72438, 72439, 72422, 72421, 72424, 72425, 72431, 72433, 72434, - 72428, 72427, 72429, 72430, 72423, 72426, 128062, 128230, 128206, 983228, - 983237, 129434, 9774, 127825, 129372, 129755, 127824, 128039, 128532, - 9956, 128390, 9999, 8240, 8241, 8524, 10178, 10977, 129336, 129496, - 129494, 128113, 9977, 128590, 129733, 128591, 129493, 128589, 128588, - 129495, 128583, 128187, 8966, 128547, 37, 9854, 127917, 8359, 8369, - 129515, 128694, 129730, 43101, 43117, 43120, 43076, 43123, 43077, 43115, - 43090, 43082, 43094, 43098, 43099, 43119, 43118, 43109, 43074, 43075, - 43116, 43079, 43083, 43089, 43088, 43114, 43113, 43081, 43080, 43073, - 43072, 43085, 43084, 43092, 43093, 43104, 43110, 43086, 43108, 43100, - 43078, 43097, 43087, 43106, 43096, 43091, 43107, 43095, 43102, 43105, - 43103, 43127, 43126, 43124, 43121, 43111, 43112, 43122, 43125, 66033, - 66023, 66017, 66010, 66027, 66003, 66018, 66028, 66004, 66012, 66022, - 66020, 66045, 66019, 66031, 66041, 66007, 66006, 66025, 66026, 66038, - 66016, 66013, 66014, 66000, 66001, 66034, 66036, 66037, 66029, 66011, - 66024, 66015, 66021, 66042, 66043, 66002, 66008, 66032, 66005, 66044, - 66040, 66039, 66030, 66009, 66035, 5941, 5942, 67840, 67855, 67843, - 67844, 67847, 67858, 67857, 67860, 67854, 67861, 67848, 67845, 67846, - 67849, 67859, 67842, 67850, 67853, 67851, 67841, 67856, 67852, 67864, - 67866, 67867, 67863, 67862, 67865, 67871, 11227, 9935, 128763, 128022, - 128061, 128055, 128169, 182, 128138, 129292, 129295, 127885, 127821, - 10031, 129655, 129669, 9811, 128299, 8916, 10970, 129383, 8984, 128720, - 129703, 8462, 8463, 127183, 127136, 127167, 127199, 127188, 127140, - 127156, 127172, 127200, 127189, 127141, 127157, 127173, 127196, 127148, - 127164, 127180, 127198, 127150, 127166, 127182, 127192, 127144, 127160, - 127176, 127191, 127143, 127159, 127175, 127190, 127142, 127158, 127174, - 127197, 127149, 127165, 127181, 127194, 127146, 127162, 127178, 127187, - 127139, 127155, 127171, 127186, 127138, 127154, 127170, 127202, 127220, - 127221, 127201, 127210, 127211, 127212, 127213, 127214, 127215, 127216, - 127217, 127218, 127219, 127203, 127204, 127205, 127206, 127207, 127208, - 127209, 127185, 127137, 127153, 127169, 127193, 127145, 127161, 127177, - 127195, 127147, 127163, 127179, 128733, 983151, 43, 10797, 10798, 10809, - 10789, 10786, 10791, 10790, 10788, 10792, 10787, 10866, 177, 9799, 11222, - 11221, 11220, 11219, 129696, 983148, 117777, 128659, 128680, 128110, - 8297, 8236, 127871, 128254, 11239, 128239, 12306, 12320, 128238, 8982, - 128688, 129364, 127858, 129716, 127831, 129751, 128574, 128545, 163, - 128093, 9212, 9213, 9214, 9211, 128041, 128425, 129328, 129732, 129731, - 65043, 65040, 65045, 65073, 65074, 65049, 65041, 65042, 65091, 65047, - 65083, 65085, 65089, 65079, 65087, 65077, 65095, 65081, 65075, 65084, - 65086, 65092, 983261, 65048, 65090, 65080, 65088, 65078, 65096, 65082, - 65076, 65044, 65072, 65046, 8478, 8826, 10937, 10933, 10927, 10929, - 10935, 10931, 8936, 8830, 8828, 8880, 9111, 129384, 9113, 128424, 128438, - 129332, 128120, 983193, 983166, 983163, 983164, 983167, 8242, 8965, 8759, - 8733, 8522, 128711, 129455, 11224, 128255, 68507, 68508, 68480, 68483, - 68490, 68491, 68485, 68482, 68486, 68493, 68495, 68496, 68488, 68484, - 68487, 68489, 68481, 68492, 68497, 68494, 68526, 68522, 68523, 68525, - 68521, 68527, 68524, 68505, 68506, 11854, 8200, 128156, 128091, 128686, - 128204, 128226, 983165, 983168, 983194, 9624, 9625, 9626, 9627, 9628, - 9629, 9630, 9631, 9622, 9623, 10764, 8279, 10774, 9833, 128894, 8264, 63, - 8799, 34, 9915, 127949, 127950, 129437, 128251, 9762, 128280, 9143, - 128740, 128643, 9926, 127752, 11827, 11783, 11782, 9995, 128400, 128406, - 127338, 127339, 127340, 129996, 11787, 9994, 11828, 129306, 128000, 8758, - 128007, 128048, 129682, 128015, 129534, 117778, 9852, 9843, 9844, 9845, - 9846, 9847, 9848, 9849, 9850, 983113, 128665, 127822, 129511, 174, - 127462, 127463, 127464, 127465, 127466, 127467, 127468, 127469, 127470, - 127471, 127472, 127473, 127474, 127475, 127476, 127477, 127478, 127479, - 127480, 127481, 127482, 127483, 127484, 127485, 127486, 127487, 43344, - 43343, 43346, 43345, 43333, 43323, 43331, 43314, 43332, 43317, 43330, - 43320, 43319, 43321, 43316, 43313, 43329, 43322, 43312, 43326, 43318, - 43325, 43324, 43315, 43328, 43327, 43334, 43359, 43337, 43342, 43341, - 43338, 43340, 43335, 43339, 43336, 43347, 128524, 127895, 65533, 9952, - 9953, 128699, 8479, 9166, 11152, 11153, 128639, 128968, 983152, 92, - 10184, 10741, 10743, 11073, 11079, 983153, 10659, 10661, 8246, 12317, - 10989, 8976, 11793, 8267, 8245, 128401, 9753, 11262, 8515, 8271, 8765, - 8909, 8247, 128402, 128403, 128405, 11841, 11822, 128404, 10672, 128158, - 8251, 129423, 983154, 127872, 11190, 11188, 11191, 11189, 11186, 11187, - 11184, 11185, 127832, 127833, 129919, 129918, 128495, 8735, 12297, 10642, - 11777, 11776, 9084, 8894, 10652, 10644, 10228, 8692, 12305, 10648, 125, - 9132, 9133, 9131, 12301, 8969, 11781, 12299, 10608, 10715, 8221, 11817, - 129929, 10621, 8971, 118272, 9687, 11241, 9616, 129978, 117925, 118289, - 129971, 118286, 118284, 118432, 118443, 129933, 128381, 130025, 130017, - 11805, 8906, 10198, 129927, 9621, 129980, 41, 9120, 9118, 9119, 11789, - 10182, 8908, 129931, 8217, 11815, 128360, 128361, 128362, 128489, 93, - 9126, 9124, 10638, 10640, 8262, 10636, 11864, 11862, 9125, 11779, 117773, + 8290, 8292, 8291, 128229, 118471, 118465, 127982, 129311, 127875, 127983, + 127971, 12292, 9979, 127886, 128304, 128122, 128121, 43453, 43454, 43455, + 43457, 43421, 43422, 43426, 43427, 43398, 43397, 43399, 43407, 43408, + 43409, 43412, 43402, 43403, 43418, 43416, 43428, 43423, 43431, 43432, + 43413, 43414, 43410, 43411, 43429, 43430, 43401, 43435, 43436, 43441, + 43439, 43440, 43424, 43425, 43419, 43420, 43415, 43417, 43396, 43405, + 43442, 43437, 43433, 43438, 43434, 43404, 43406, 43400, 43458, 43466, + 43467, 43459, 43465, 43461, 43464, 43468, 43463, 43486, 43462, 43487, + 43460, 43471, 43456, 43469, 43393, 43443, 43395, 43394, 43392, 43448, + 43449, 43444, 43450, 43445, 43446, 43447, 43452, 43451, 43477, 43476, + 43479, 43478, 43475, 43474, 43472, 43481, 43473, 43480, 129753, 129724, + 128086, 128377, 10781, 129337, 9795, 9909, 129513, 69786, 69787, 69785, + 69793, 69792, 69763, 69764, 69770, 69772, 69784, 69783, 69791, 69790, + 69767, 69768, 69765, 69766, 69777, 69789, 69782, 69794, 69804, 69805, + 69806, 69798, 69797, 69779, 69778, 69776, 69775, 69781, 69780, 69774, + 69773, 69796, 69795, 69788, 69801, 69807, 69802, 69799, 69803, 69800, + 69769, 69771, 69760, 69818, 69761, 69817, 69762, 69822, 69823, 69825, + 69824, 69826, 69808, 69814, 69816, 69811, 69812, 69809, 69810, 69813, + 69815, 69821, 69837, 69819, 69820, 12142, 12164, 12060, 12157, 12100, + 12149, 12184, 12191, 12227, 12068, 12174, 12234, 12205, 12134, 12168, + 12219, 12189, 12088, 12090, 12096, 12160, 12182, 12224, 12190, 12147, + 12114, 12118, 12058, 12176, 12075, 12112, 12045, 12170, 12124, 12070, + 12194, 12109, 12229, 12196, 12139, 12056, 12099, 12034, 12084, 12136, + 12120, 12044, 12111, 12094, 12125, 12243, 12238, 12082, 12159, 12063, + 12215, 12062, 12042, 12241, 12067, 12235, 12043, 12140, 12119, 12207, + 12123, 12133, 12222, 12117, 12226, 12245, 12214, 12217, 12236, 12155, + 12188, 12113, 12065, 12198, 12066, 12146, 12171, 12225, 12200, 12121, + 12093, 12095, 12221, 12092, 12216, 12231, 12054, 12218, 12179, 12037, + 12173, 12072, 12046, 12127, 12152, 12049, 12074, 12107, 12208, 12212, + 12041, 12210, 12131, 12033, 12039, 12199, 12085, 12128, 12161, 12162, + 12233, 12165, 12192, 12077, 12201, 12061, 12105, 12040, 12240, 12102, + 12153, 12032, 12080, 12167, 12048, 12156, 12059, 12126, 12158, 12050, + 12183, 12204, 12097, 12239, 12053, 12078, 12150, 12071, 12187, 12186, + 12223, 12228, 12104, 12098, 12064, 12036, 12057, 12163, 12178, 12185, + 12154, 12203, 12083, 12087, 12135, 12151, 12202, 12035, 12122, 12141, + 12180, 12144, 12076, 12052, 12115, 12091, 12108, 12169, 12143, 12148, + 12130, 12089, 12211, 12073, 12101, 12138, 12103, 12209, 12047, 12220, + 12172, 12129, 12166, 12242, 12237, 12145, 12106, 12081, 12244, 12038, + 12086, 12055, 12181, 12197, 12193, 12175, 12116, 12110, 12177, 12137, + 12230, 12213, 12195, 12069, 12079, 12206, 12051, 12232, 12132, 129432, + 3240, 3293, 3225, 3235, 3230, 3205, 3206, 3216, 3220, 3234, 3233, 3239, + 3238, 983205, 3251, 3250, 3249, 3248, 3232, 3231, 3237, 3236, 3211, 3296, + 3212, 3297, 3253, 3209, 3210, 3218, 3219, 3207, 3208, 3254, 3255, 3256, + 3245, 3244, 3227, 3226, 3224, 3223, 3229, 3228, 3222, 3221, 3243, 3242, + 3214, 3215, 3294, 3257, 3246, 3247, 3285, 3315, 3201, 3200, 3204, 3260, + 3261, 3202, 3313, 3314, 3277, 3203, 3292, 3286, 3262, 3272, 3276, 3265, + 3266, 3267, 3268, 3298, 3299, 3274, 3275, 3263, 3264, 3270, 3271, 3307, + 3306, 3309, 3308, 3305, 3304, 3302, 3311, 3303, 3310, 12450, 984009, + 984008, 984007, 984010, 110881, 110880, 110882, 110592, 12499, 984002, + 984005, 984003, 984006, 984004, 12496, 12505, 12508, 12502, 12511, + 110583, 110584, 110585, 110586, 110587, 110589, 110590, 110576, 110577, + 110578, 110579, 110581, 110582, 12510, 12513, 12514, 12512, 12531, 12490, + 12493, 12491, 12494, 12492, 12789, 12792, 12790, 12793, 12791, 12795, + 12798, 12796, 12799, 12797, 12533, 12534, 110933, 12784, 12787, 12483, + 12526, 110949, 110948, 110950, 12515, 12519, 12517, 110951, 12788, 12785, + 12786, 12794, 12449, 12455, 12451, 12457, 12453, 12469, 12475, 12471, + 12477, 12473, 12480, 12487, 12482, 12489, 12485, 12460, 12466, 12462, + 12468, 12464, 12495, 12504, 12498, 12507, 12501, 12459, 12465, 12461, + 12467, 12463, 12497, 12506, 12500, 12509, 12503, 12521, 12524, 12522, + 12525, 12523, 12479, 12486, 12481, 12488, 12484, 12535, 12537, 12536, + 12538, 12532, 12470, 12476, 12472, 12478, 12474, 12527, 12529, 12528, + 12530, 12516, 12520, 12518, 12456, 12452, 12458, 12454, 12542, 12543, + 12541, 12539, 12448, 12540, 12444, 12443, 73476, 73477, 73487, 73523, + 73498, 73497, 73503, 73502, 73508, 73507, 73501, 73500, 73506, 73505, + 73480, 73481, 73482, 73483, 73484, 73485, 73478, 73479, 73494, 73504, + 73499, 73509, 73519, 73520, 73521, 73513, 73512, 73496, 73495, 73493, + 73492, 73491, 73490, 73511, 73510, 73522, 73517, 73514, 73516, 73518, + 73515, 73486, 73488, 73551, 73548, 73546, 73545, 73549, 73543, 73541, + 73544, 73550, 73542, 73547, 73537, 73472, 73562, 73474, 73475, 73473, + 73525, 73524, 73535, 73530, 73534, 73536, 73528, 73529, 73526, 73527, + 73540, 73539, 73557, 73556, 73559, 73558, 73555, 73554, 73552, 73561, + 73553, 73560, 73538, 43299, 43301, 43283, 43295, 43277, 43281, 43284, + 43275, 43274, 43286, 43285, 43279, 43278, 43294, 43282, 43289, 43297, + 43288, 43276, 43292, 43287, 43290, 43296, 43293, 43291, 43280, 43298, + 43300, 43310, 43311, 43308, 43309, 43307, 43303, 43305, 43304, 43302, + 43306, 43269, 43268, 43271, 43270, 43267, 43266, 43264, 43273, 43265, + 43272, 119496, 119506, 119499, 119503, 119493, 119492, 119502, 119497, + 119507, 119495, 119505, 119494, 119504, 119501, 119491, 119500, 119490, + 119498, 119488, 119489, 128331, 8490, 128273, 9000, 128422, 983552, + 983553, 983559, 983558, 983561, 983560, 983557, 983556, 983554, 983563, + 983555, 983562, 128287, 118449, 68163, 68162, 68161, 68160, 68113, 68146, + 68112, 68147, 68148, 68123, 68122, 68128, 68127, 68126, 68121, 68131, + 68125, 68124, 68130, 68129, 68141, 68142, 68143, 68135, 68134, 68118, + 68117, 68115, 68114, 68133, 68132, 68149, 68140, 68145, 68119, 68139, + 68136, 68138, 68137, 68144, 68096, 68165, 68164, 68166, 68167, 68179, + 68178, 68183, 68176, 68182, 68181, 68184, 68180, 68177, 68152, 68153, + 68109, 68154, 68111, 68110, 68099, 68101, 68097, 68102, 68098, 68108, + 68159, 68168, 129711, 983960, 983965, 983970, 983975, 983962, 983964, + 983961, 983963, 983957, 983959, 983956, 983958, 983977, 983979, 983978, + 983972, 983974, 983967, 983969, 983971, 983973, 983966, 983968, 983989, + 983983, 983985, 983986, 983987, 983980, 983982, 983984, 983981, 983976, + 983988, 6107, 6052, 6064, 6051, 6067, 6066, 6065, 6055, 6057, 6058, 6056, + 6053, 6054, 6063, 983994, 983991, 983992, 983993, 6061, 6062, 6059, 6060, + 6022, 6024, 6021, 6023, 6017, 6019, 6016, 6018, 6020, 6030, 6025, 6035, + 6037, 6039, 6038, 6046, 6045, 6047, 6032, 6034, 6027, 6029, 6031, 6033, + 6026, 6028, 6049, 6043, 6040, 6042, 6044, 6041, 6036, 6048, 6050, 6108, + 6109, 6095, 6096, 6099, 6091, 6101, 6104, 6102, 6098, 6094, 6100, 6106, + 6092, 6087, 6090, 6093, 6103, 6105, 6088, 6086, 6089, 6097, 6652, 6636, + 6655, 6639, 6653, 6637, 6654, 6638, 6651, 6635, 6650, 6634, 6133, 6137, + 6136, 6134, 6135, 6130, 6132, 6131, 6129, 6128, 6624, 6648, 6632, 6649, + 6633, 6647, 6631, 6646, 6630, 6645, 6629, 6642, 6626, 6640, 6643, 6627, + 6644, 6628, 6641, 6625, 6069, 6068, 6070, 983996, 6082, 6083, 6085, 6071, + 6080, 6072, 6078, 983995, 6084, 6073, 6079, 6074, 983990, 6075, 6077, + 6076, 6081, 6117, 6116, 6119, 6118, 6115, 6114, 6112, 6121, 6113, 6120, + 70204, 70201, 70200, 70208, 70185, 70178, 70179, 70177, 70155, 70156, + 70154, 70172, 70167, 70166, 70173, 70171, 70161, 70160, 70144, 70145, + 70149, 70151, 70165, 70164, 70170, 70169, 70187, 70183, 70157, 70168, + 70163, 70174, 70159, 70158, 70153, 70152, 70176, 70175, 70186, 70180, + 70207, 70182, 70184, 70181, 70148, 70146, 70150, 70147, 70199, 70206, + 70198, 70197, 70196, 70203, 70209, 70188, 70193, 70195, 70189, 70190, + 70192, 70194, 70191, 70202, 70205, 70357, 70358, 70356, 70333, 70334, + 70332, 70345, 70347, 70344, 70352, 70351, 70340, 70339, 70338, 70320, + 70321, 70327, 70329, 70346, 70361, 70343, 70342, 70350, 70349, 70324, + 70325, 70322, 70323, 70335, 70348, 70341, 70353, 70337, 70336, 70331, + 70330, 70355, 70354, 70364, 70365, 70366, 70362, 70359, 70363, 70360, + 70326, 70328, 70377, 70378, 70367, 70368, 70374, 70376, 70371, 70372, + 70369, 70370, 70373, 70375, 70389, 70388, 70391, 70390, 70387, 70386, + 70384, 70393, 70385, 70392, 94180, 93521, 93520, 93525, 93524, 93519, + 93518, 93523, 93522, 93512, 93517, 93526, 93530, 93529, 93514, 93513, + 93511, 93510, 93516, 93515, 93509, 93508, 93528, 93527, 93537, 93536, + 93538, 93534, 93531, 93533, 93535, 93532, 93507, 93505, 93549, 93548, + 93504, 93547, 93506, 93539, 93544, 93546, 93541, 93542, 93543, 93540, + 93545, 93551, 93550, 93557, 93556, 93559, 93558, 93555, 93554, 93552, + 93561, 93553, 93560, 128143, 128535, 128537, 128538, 128573, 128139, + 129373, 8365, 128088, 129665, 129486, 129698, 12927, 128040, 11235, + 129404, 127991, 129357, 128030, 129692, 128728, 917505, 3805, 3804, + 983206, 983207, 3743, 3741, 3807, 3806, 3714, 3716, 3713, 983209, 3747, + 3749, 3730, 3729, 3736, 3728, 3727, 3731, 3726, 3744, 3721, 3718, 3724, + 3756, 3740, 3742, 3739, 3752, 3753, 3754, 3722, 3734, 3735, 3733, 3755, + 3758, 3719, 3725, 3737, 3738, 3720, 3732, 3745, 983208, 3751, 3746, 3757, + 3773, 3772, 3770, 3785, 3786, 3787, 3784, 3760, 3762, 3780, 3763, 3779, + 3761, 3771, 3766, 3767, 3768, 3769, 3776, 3777, 3764, 3765, 3778, 3782, + 3790, 3759, 3797, 3796, 3799, 3798, 3795, 3794, 3792, 3801, 3793, 3800, + 3788, 3789, 128996, 129003, 128309, 128311, 128998, 128994, 129001, + 68413, 68415, 128992, 128310, 128999, 68412, 68414, 118324, 118319, + 118322, 118323, 118303, 118304, 118336, 118331, 118328, 118315, 118314, + 118306, 118316, 118318, 118334, 118335, 118333, 118327, 118339, 118341, + 118342, 118340, 118320, 118338, 118332, 118302, 118325, 118309, 118317, + 118307, 118313, 118326, 118329, 118312, 118330, 118352, 118348, 118351, + 118350, 118347, 118344, 118345, 118343, 118349, 118346, 118305, 118321, + 118301, 118299, 118298, 118310, 118311, 118308, 118300, 118337, 11004, + 10201, 10200, 10782, 128995, 129002, 128308, 128997, 128993, 129000, + 9711, 10923, 10925, 8382, 9790, 127772, 127767, 65, 258, 7858, 7856, + 7854, 7862, 7860, 550, 480, 7840, 512, 196, 478, 197, 506, 7680, 256, + 983564, 194, 7848, 7846, 7844, 7852, 7850, 461, 7842, 260, 983590, + 983592, 192, 193, 514, 195, 570, 198, 508, 482, 42946, 393, 11373, 42808, + 42810, 42802, 42804, 42806, 42812, 66, 386, 42902, 7686, 7684, 7682, 385, + 579, 42822, 42932, 67, 199, 7688, 264, 268, 262, 42948, 391, 42898, 266, + 571, 42960, 42796, 42798, 42862, 42931, 68, 498, 453, 42951, 272, 7696, + 7698, 270, 395, 7694, 7692, 7690, 394, 497, 452, 42964, 42962, 69, 552, + 7708, 202, 983586, 7874, 7872, 7870, 983584, 7878, 7876, 7704, 282, 278, + 983598, 983600, 7864, 516, 203, 7868, 7706, 274, 7700, 7702, 983570, + 983572, 983574, 7866, 280, 983594, 983596, 200, 201, 518, 276, 582, 439, + 494, 440, 42786, 42788, 42858, 208, 425, 330, 70, 401, 7710, 42904, 71, + 290, 284, 486, 500, 286, 7712, 42912, 403, 288, 484, 577, 42938, 42940, + 42942, 404, 983199, 72, 7722, 7720, 292, 542, 7718, 11367, 7716, 7714, + 42922, 294, 11381, 502, 42790, 73, 520, 7882, 304, 207, 7726, 206, 463, + 298, 983566, 296, 7724, 7880, 302, 983604, 983606, 204, 205, 522, 300, + 407, 42873, 42875, 42877, 42882, 42884, 42886, 406, 42860, 74, 308, + 42930, 983608, 584, 75, 310, 488, 42818, 11369, 7730, 42816, 42820, 7728, + 7732, 42914, 408, 76, 42925, 573, 11360, 7734, 7736, 11362, 319, 456, + 321, 315, 7740, 317, 42824, 313, 7738, 983610, 42970, 42972, 455, 77, + 7742, 7746, 7744, 983612, 11374, 7930, 7932, 42966, 78, 325, 7754, 327, + 459, 544, 7752, 413, 504, 323, 42896, 7750, 7748, 42916, 209, 458, 79, + 42826, 42828, 332, 7760, 7762, 415, 212, 7892, 7890, 7888, 7896, 7894, + 465, 214, 554, 558, 560, 7884, 524, 336, 490, 492, 216, 510, 213, 7758, + 7756, 556, 983576, 983578, 983580, 416, 7902, 7900, 7898, 7906, 7904, + 7886, 210, 211, 526, 334, 42944, 400, 390, 42934, 418, 42830, 546, 80, + 42834, 11363, 42832, 42836, 7764, 420, 7766, 42958, 81, 42840, 42838, 82, + 342, 344, 7770, 7772, 7768, 528, 983614, 11364, 340, 7774, 530, 42918, + 588, 42842, 42997, 42923, 42814, 398, 42844, 42955, 83, 352, 7782, 350, + 536, 348, 346, 7780, 7778, 7784, 7776, 42956, 42953, 11390, 983582, + 42920, 42949, 586, 42926, 42891, 7838, 42968, 42924, 399, 84, 354, 7792, + 538, 356, 574, 7788, 7786, 7790, 430, 428, 358, 42878, 11375, 11376, + 42893, 42928, 42880, 412, 42929, 581, 222, 42852, 42854, 388, 444, 423, + 42794, 42792, 85, 219, 7798, 467, 220, 473, 475, 471, 469, 7794, 532, + 368, 7908, 431, 7916, 7914, 7912, 7920, 7918, 7910, 362, 7802, 983568, + 983620, 983622, 370, 983616, 983618, 360, 7800, 7796, 217, 218, 534, 364, + 366, 42936, 580, 433, 86, 42846, 7806, 7804, 434, 42850, 42906, 42908, + 42910, 42856, 42848, 87, 372, 7812, 7816, 7814, 7808, 7810, 11378, 503, + 88, 7820, 7818, 89, 374, 376, 7924, 7822, 7922, 435, 7926, 7934, 221, + 562, 7928, 590, 540, 90, 7824, 381, 377, 11371, 7826, 379, 7828, 11391, + 437, 42950, 548, 306, 338, 10013, 43007, 43005, 43006, 43003, 43004, + 42999, 450, 7461, 684, 664, 685, 662, 122638, 446, 661, 426, 674, 451, + 122634, 7431, 7430, 7459, 618, 641, 671, 122628, 7436, 7439, 7440, 630, + 7445, 640, 7438, 7449, 43846, 42870, 7451, 11387, 122626, 122640, 43002, + 7450, 665, 7427, 610, 667, 7424, 7425, 7428, 7429, 42800, 668, 7434, + 7435, 7437, 628, 7448, 42927, 42801, 7452, 7456, 7457, 655, 7458, 663, + 122639, 42895, 443, 447, 448, 449, 660, 673, 7460, 422, 7547, 7550, 97, + 259, 7859, 7857, 7855, 7863, 7861, 551, 481, 7841, 513, 228, 479, 229, + 507, 7681, 7834, 7567, 257, 983565, 226, 7849, 7847, 7845, 7853, 7851, + 462, 7843, 261, 983591, 983593, 224, 225, 515, 227, 11365, 43825, 230, + 983624, 509, 483, 42947, 593, 7568, 42809, 42811, 42803, 42805, 42807, + 42813, 98, 387, 42903, 7687, 7532, 7552, 7685, 7683, 595, 384, 43824, + 43827, 629, 43853, 43837, 43838, 43826, 42823, 7447, 42933, 99, 231, + 7689, 265, 597, 269, 263, 42900, 122653, 392, 42899, 267, 572, 43859, + 43860, 43861, 42961, 666, 631, 606, 42797, 42799, 42863, 100, 42952, 273, + 396, 598, 7697, 7699, 545, 271, 122661, 7533, 7695, 599, 7569, 7553, + 7693, 7691, 676, 122642, 122649, 7839, 567, 607, 644, 305, 42965, 43848, + 43850, 42963, 499, 454, 675, 677, 43878, 568, 42865, 101, 553, 7709, 234, + 983587, 7875, 7873, 7871, 983585, 7879, 7877, 7705, 283, 279, 983599, + 983601, 7865, 517, 235, 11384, 7869, 7707, 275, 7701, 7703, 983571, + 983573, 983575, 43828, 7867, 281, 983595, 983597, 232, 233, 519, 277, + 7570, 583, 42787, 42789, 331, 43836, 122644, 643, 122635, 122636, 7563, + 646, 7576, 658, 441, 659, 495, 122648, 7578, 442, 42859, 240, 102, 7534, + 7554, 402, 7711, 42905, 681, 122624, 103, 291, 285, 487, 501, 287, 7713, + 7555, 42913, 608, 289, 485, 578, 42939, 42941, 42943, 611, 983200, 104, + 7723, 7721, 293, 543, 7719, 11368, 7717, 7715, 7830, 42901, 614, 295, + 11382, 983631, 983632, 42791, 615, 405, 105, 238, 464, 239, 7727, 983602, + 983588, 983603, 7883, 521, 299, 983567, 303, 983605, 983607, 297, 7725, + 7881, 236, 237, 523, 301, 616, 122650, 7574, 42874, 42876, 7545, 42883, + 42885, 42887, 43876, 43840, 617, 7548, 43873, 42861, 106, 309, 669, 496, + 983609, 585, 107, 311, 489, 42819, 11370, 7731, 42817, 42821, 7729, 7733, + 7556, 42915, 409, 312, 108, 620, 122643, 410, 316, 7741, 564, 318, 7735, + 7737, 43832, 11361, 619, 43833, 320, 122662, 42825, 314, 7739, 43831, + 621, 42894, 122641, 7557, 983611, 322, 42971, 411, 622, 122629, 43829, + 383, 7836, 7835, 7837, 682, 683, 42866, 457, 109, 7743, 43834, 7535, + 7558, 7747, 7745, 983613, 625, 7931, 7933, 42967, 42867, 110, 326, 7755, + 43835, 565, 328, 414, 7753, 626, 122663, 7536, 505, 324, 42897, 7751, + 7749, 7559, 627, 42917, 241, 329, 983589, 42868, 460, 111, 244, 7893, + 7891, 7889, 7897, 7895, 466, 246, 555, 559, 561, 7885, 525, 337, 42827, + 11386, 42829, 333, 7761, 7763, 491, 493, 248, 511, 245, 7759, 7757, 557, + 983577, 983579, 983581, 417, 7903, 7901, 7899, 7907, 7905, 7887, 242, + 243, 527, 335, 122651, 42945, 596, 983625, 983626, 7575, 43839, 43874, + 603, 7571, 42935, 419, 42831, 547, 112, 42835, 7549, 42833, 42837, 7765, + 7537, 7560, 421, 7767, 42959, 632, 113, 42841, 672, 587, 42839, 569, 114, + 343, 43849, 345, 7771, 7773, 7769, 529, 638, 7539, 122646, 7775, 636, + 983615, 637, 122664, 7538, 341, 531, 7561, 42919, 589, 43847, 42843, + 42998, 604, 7572, 605, 639, 122625, 600, 122631, 8580, 42815, 122627, + 42869, 42845, 612, 115, 347, 7781, 353, 7783, 351, 537, 349, 122654, + 7779, 7785, 7777, 42957, 42954, 575, 983583, 122665, 7540, 7562, 42921, + 642, 42892, 43872, 601, 983629, 983630, 7573, 602, 43851, 43852, 609, + 43830, 223, 7441, 7442, 7443, 7455, 7454, 7453, 42969, 645, 43845, 116, + 355, 7793, 539, 566, 357, 11366, 7831, 7789, 7787, 122666, 7541, 7791, + 427, 429, 122633, 648, 359, 679, 122647, 122652, 7546, 254, 42853, 42855, + 389, 445, 424, 7446, 42795, 397, 613, 686, 687, 7433, 42879, 7444, 43842, + 43841, 43843, 43844, 7432, 633, 43880, 122645, 634, 122632, 11385, 635, + 647, 122637, 652, 983627, 983628, 592, 594, 7426, 623, 624, 654, 122630, + 43857, 477, 7543, 670, 42881, 653, 42871, 680, 678, 43879, 11383, 42793, + 117, 649, 43855, 251, 7799, 468, 252, 474, 476, 472, 470, 7795, 533, 369, + 7909, 432, 7917, 7915, 7913, 7921, 7919, 7911, 363, 7803, 983569, 983621, + 983623, 371, 983617, 983619, 7577, 367, 361, 7801, 7797, 249, 43854, + 42937, 250, 535, 365, 43858, 650, 7551, 7531, 43856, 42872, 43875, 118, + 42847, 7807, 7564, 11380, 11377, 7805, 651, 42851, 42907, 42909, 42911, + 42857, 42849, 119, 373, 7813, 7817, 7815, 7809, 7811, 7832, 11379, 120, + 7821, 7819, 43863, 43864, 43865, 43862, 7565, 121, 375, 255, 7925, 7823, + 7923, 436, 7927, 7935, 43866, 591, 253, 563, 7929, 7833, 541, 122, 378, + 7825, 657, 382, 11372, 7827, 380, 7829, 576, 438, 7542, 7566, 656, 549, + 64256, 64259, 64260, 64257, 64258, 307, 64261, 64262, 339, 8347, 8340, + 8336, 8337, 8341, 7522, 11388, 8342, 8343, 8344, 8345, 8338, 8346, 7523, + 8348, 7524, 7525, 8339, 129726, 127811, 129388, 129897, 129916, 129947, + 10203, 10202, 129899, 129917, 117902, 128494, 12296, 10641, 10643, 11058, + 11056, 10576, 10571, 10570, 10574, 12304, 10647, 123, 9128, 9129, 9127, + 12300, 8968, 9948, 10714, 12298, 8220, 11816, 11780, 129287, 129284, + 129285, 129283, 129286, 9686, 11240, 9612, 129977, 117924, 118288, + 129970, 118285, 118283, 118435, 118440, 129940, 129932, 128379, 128709, + 11804, 10204, 9614, 9615, 129999, 10197, 8596, 8700, 8697, 8622, 10568, + 8660, 10500, 8654, 8621, 11012, 8703, 11020, 129112, 11108, 11788, 10181, + 8907, 9609, 8216, 11814, 128488, 91, 9123, 9121, 10639, 10637, 8261, + 10635, 11863, 11861, 9122, 11778, 10703, 129900, 11785, 117771, 129985, + 128492, 118434, 118441, 9610, 9613, 12308, 129998, 8867, 12302, 10627, + 12310, 10629, 12314, 12312, 10712, 128398, 9611, 10620, 8970, 130027, + 130019, 8905, 40, 9117, 9115, 9116, 11808, 9144, 9001, 117856, 118265, + 10748, 171, 117774, 128269, 117920, 117846, 117922, 117911, 117861, + 117762, 117918, 117880, 10553, 10154, 1422, 117832, 117906, 117908, + 129307, 4054, 4056, 117872, 117876, 9958, 8294, 8237, 8234, 8206, 8592, + 10563, 11074, 11083, 11082, 10611, 129973, 10618, 10615, 11070, 8676, + 8633, 10525, 129032, 8619, 11064, 8698, 10566, 129040, 129024, 8602, + 11024, 11025, 8610, 11066, 11065, 129028, 129176, 129044, 8617, 8695, + 8612, 10527, 129216, 8646, 10521, 129192, 129184, 11144, 11013, 10603, + 10599, 10590, 10582, 8637, 10594, 10602, 10598, 10586, 10578, 8636, 8651, + 129778, 129088, 129092, 11104, 11136, 11130, 983241, 11174, 11172, + 129064, 129060, 129056, 129072, 129068, 11120, 11114, 11140, 129168, + 10510, 8666, 129186, 11067, 11069, 11068, 11244, 11061, 11060, 11062, + 11063, 8606, 8678, 129172, 8604, 8656, 10498, 8653, 10502, 10523, 10508, + 8672, 129194, 129076, 129188, 8701, 8647, 129783, 129190, 128620, 8668, + 129080, 129104, 129084, 11077, 9804, 128006, 7213, 7221, 7216, 7220, + 7215, 7214, 7217, 7218, 7219, 7170, 7169, 7168, 7184, 7183, 7182, 7247, + 7193, 7180, 7188, 7187, 7186, 7185, 7172, 7171, 7198, 7197, 7190, 7189, + 7173, 7177, 7181, 7192, 7191, 7246, 7245, 7179, 7178, 7175, 7174, 7201, + 7200, 7176, 7196, 7195, 7199, 7202, 7194, 7203, 7227, 7231, 7230, 7228, + 7229, 7223, 7222, 7205, 7204, 7210, 7211, 7208, 7209, 7206, 7212, 7207, + 7237, 7236, 7239, 7238, 7235, 7234, 7232, 7241, 7233, 7240, 10897, 10895, + 10893, 10899, 10891, 10614, 10889, 10887, 8922, 8934, 8808, 10918, 10920, + 10885, 10877, 10881, 10883, 10879, 8818, 8804, 8822, 8806, 10873, 10875, + 8918, 60, 118480, 128210, 127898, 127819, 129461, 128626, 128955, 128969, + 128943, 11212, 128964, 10099, 128648, 10098, 9617, 128937, 128949, + 128978, 128960, 129653, 128910, 10072, 128930, 128504, 9735, 128498, + 128497, 6429, 6404, 6403, 6412, 6430, 6411, 6421, 6410, 6405, 6415, 6425, + 6426, 6427, 6419, 6418, 6407, 6406, 6414, 6413, 6409, 6408, 6402, 6401, + 6417, 6416, 6428, 6423, 6420, 6422, 6424, 6458, 6457, 6459, 6464, 6449, + 6452, 6450, 6448, 6456, 6454, 6453, 6455, 6451, 6442, 6443, 6441, 6432, + 6436, 6438, 6440, 6437, 6439, 6435, 6433, 6434, 6400, 6468, 6469, 6475, + 6474, 6477, 6476, 6473, 6472, 6470, 6479, 6471, 6478, 13007, 10770, + 10771, 10772, 983062, 8232, 983068, 983143, 67143, 67146, 67151, 67165, + 67166, 67167, 67157, 67158, 67159, 67160, 67161, 67162, 67163, 67164, + 67171, 67172, 67173, 67168, 67169, 67170, 67174, 67175, 67176, 67177, + 67178, 67179, 67230, 67231, 67180, 67181, 67182, 67183, 67184, 67185, + 67186, 67187, 67188, 67189, 67190, 67191, 67192, 67193, 67194, 67195, + 67196, 67197, 67198, 67199, 67200, 67201, 67202, 67203, 67204, 67205, + 67206, 67207, 67208, 67209, 67210, 67211, 67212, 67213, 67214, 67215, + 67216, 67217, 67218, 67219, 67220, 67221, 67222, 67223, 67224, 67225, + 67226, 67227, 67228, 67229, 67232, 67233, 67234, 67235, 67236, 67237, + 67238, 67239, 67240, 67241, 67242, 67243, 67244, 67245, 67246, 67247, + 67248, 67249, 67250, 67251, 67252, 67253, 67254, 67255, 67256, 67257, + 67258, 67259, 67260, 67261, 67262, 67263, 67264, 67274, 67275, 67276, + 67277, 67278, 67279, 67280, 67281, 67282, 67283, 67284, 67285, 67286, + 67287, 67288, 67289, 67290, 67291, 67292, 67293, 67294, 67295, 67296, + 67297, 67298, 67299, 67300, 67301, 67302, 67303, 67304, 67265, 67266, + 67267, 67268, 67269, 67270, 67271, 67272, 67273, 67325, 67326, 67327, + 67328, 67329, 67330, 67305, 67306, 67307, 67308, 67309, 67310, 67311, + 67312, 67313, 67314, 67315, 67316, 67317, 67318, 67319, 67320, 67321, + 67322, 67323, 67324, 67331, 67332, 67333, 67334, 67335, 67336, 67337, + 67338, 67349, 67350, 67351, 67352, 67353, 67354, 67355, 67356, 67357, + 67358, 67359, 67360, 67361, 67362, 67363, 67364, 67365, 67366, 67367, + 67368, 67378, 67379, 67380, 67381, 67382, 67369, 67370, 67371, 67372, + 67373, 67374, 67375, 67376, 67377, 67339, 67340, 67341, 67342, 67343, + 67344, 67345, 67346, 67347, 67348, 67401, 67404, 67403, 67402, 67400, + 67398, 67393, 67397, 67395, 67394, 67399, 67392, 67396, 67408, 67409, + 67410, 67406, 67407, 67405, 67411, 67413, 67412, 67424, 67425, 67426, + 67427, 67428, 67429, 67430, 67431, 67081, 67082, 67083, 67084, 67085, + 67087, 67088, 67089, 67090, 67091, 67092, 67093, 67094, 67086, 67095, + 67096, 67097, 67098, 67100, 67101, 67102, 67103, 67104, 67105, 67106, + 67107, 67108, 67109, 67110, 67111, 67112, 67113, 67114, 67115, 67116, + 67117, 67118, 67119, 67120, 67121, 67122, 67123, 67124, 67125, 67126, + 67127, 67128, 67129, 67130, 67131, 67132, 67133, 67134, 67135, 67136, + 67137, 67138, 67139, 67140, 67141, 67142, 67072, 67073, 67074, 67075, + 67076, 67077, 67078, 67079, 67080, 67145, 67147, 67148, 67149, 67150, + 67154, 67155, 67144, 67152, 67153, 67156, 67099, 65667, 65668, 65669, + 65670, 65671, 65672, 65673, 65675, 65674, 65677, 65676, 65665, 65664, + 65666, 65681, 65679, 65680, 65682, 65678, 65686, 65685, 65687, 65693, + 65690, 65691, 65692, 65694, 65703, 65696, 65695, 65697, 65698, 65699, + 65701, 65702, 65707, 65706, 65704, 65705, 65708, 65709, 65710, 65711, + 65712, 65713, 65719, 65717, 65714, 65715, 65716, 65718, 65720, 65721, + 65722, 65723, 65724, 65725, 65726, 65727, 65728, 65729, 65731, 65730, + 65732, 65733, 65737, 65734, 65735, 65736, 65738, 65739, 65740, 65741, + 65743, 65742, 65744, 65745, 65747, 65748, 65752, 65749, 65750, 65751, + 65753, 65754, 65755, 65756, 65757, 65779, 65780, 65781, 65782, 65783, + 65784, 65785, 65759, 65760, 65761, 65762, 65763, 65764, 65765, 65766, + 65767, 65768, 65769, 65770, 65771, 65772, 65773, 65774, 65775, 65776, + 65777, 65778, 65758, 65786, 65683, 65684, 65689, 65688, 65700, 65746, + 65561, 65543, 65587, 65582, 65589, 65536, 65541, 65579, 65566, 65571, + 65596, 65569, 65584, 65544, 65559, 65540, 65557, 65560, 65580, 65606, + 65600, 65599, 65577, 65562, 65538, 65573, 65563, 65609, 65588, 65549, + 65568, 65537, 65581, 65574, 65601, 65542, 65593, 65583, 65594, 65552, + 65547, 65605, 65578, 65545, 65585, 65586, 65546, 65570, 65591, 65564, + 65565, 65607, 65610, 65611, 65553, 65590, 65550, 65539, 65576, 65608, + 65551, 65554, 65592, 65603, 65597, 65567, 65572, 65558, 65555, 65612, + 65602, 65556, 65613, 65604, 65620, 65621, 65623, 65624, 65626, 65627, + 65628, 65629, 65622, 65616, 65617, 65619, 65618, 65625, 128391, 128279, + 128482, 128132, 42237, 42235, 42232, 42234, 42236, 42233, 42206, 42205, + 42197, 42196, 42204, 42195, 42228, 42229, 42230, 42213, 42208, 42224, + 42225, 42203, 42202, 42221, 42198, 42216, 42214, 42200, 42199, 42194, + 42193, 42219, 42210, 73648, 42220, 42211, 42212, 42222, 42223, 42227, + 42231, 42192, 42217, 42201, 42209, 42207, 42218, 42215, 42226, 42238, + 42239, 8356, 8374, 129409, 129422, 9806, 128274, 983079, 983076, 128271, + 117785, 117786, 117784, 117783, 117782, 117781, 8743, 10848, 10846, + 10833, 10844, 10842, 10847, 8744, 10841, 10851, 10850, 10834, 10845, + 10843, 10982, 10188, 129688, 10231, 129240, 10234, 10206, 10232, 10237, + 10229, 10235, 11059, 129236, 10230, 129238, 129239, 129232, 10236, 10233, + 10238, 129233, 129234, 10239, 10205, 129524, 128884, 129719, 128140, + 127977, 128261, 129707, 12319, 8270, 11847, 11848, 95, 118273, 9691, + 118276, 118291, 129935, 118436, 118447, 9604, 9697, 117765, 128394, + 129852, 129853, 129870, 129872, 129868, 129856, 129871, 129869, 129854, + 129873, 129855, 128395, 128396, 128393, 10559, 117934, 117936, 117932, + 117930, 117972, 9695, 117960, 117948, 117964, 117968, 117952, 117956, + 117944, 117940, 117817, 129951, 9722, 117820, 128397, 118428, 117935, + 117937, 117933, 117931, 117973, 9694, 117961, 117949, 117965, 117969, + 117953, 117957, 117945, 117941, 117818, 10558, 128318, 10065, 129950, + 9727, 117823, 117767, 129863, 129865, 129867, 129864, 129861, 129866, + 129859, 129862, 129860, 129857, 129858, 10195, 118431, 10063, 9998, 9987, + 118429, 117821, 118430, 117822, 130021, 9605, 118425, 118426, 118424, + 117816, 118427, 117819, 9602, 9601, 9607, 9603, 118437, 118446, 9606, + 129903, 9674, 10208, 129438, 128557, 127853, 129523, 128886, 129729, + 66190, 66192, 66187, 66196, 66199, 66185, 66200, 66178, 66179, 66176, + 66201, 66177, 66202, 66191, 66193, 66181, 66180, 66203, 66182, 66186, + 66189, 66195, 66188, 66197, 66198, 66194, 66183, 66204, 66184, 67887, + 67892, 67881, 67895, 67891, 67886, 67872, 67893, 67876, 67894, 67883, + 67896, 67873, 67897, 67875, 67889, 67874, 67878, 67880, 67882, 67884, + 67890, 67885, 67888, 67877, 67879, 67903, 129317, 983226, 983234, 983224, + 983229, 8468, 129433, 983065, 129668, 129522, 129497, 69986, 69981, + 69991, 69985, 69984, 69990, 69989, 70002, 69997, 69983, 69982, 69988, + 69987, 69995, 69994, 69978, 69977, 69976, 69975, 69980, 69979, 69974, + 69973, 69993, 69992, 70001, 69998, 69996, 70000, 69999, 69968, 69971, + 69969, 69972, 69970, 70006, 70005, 70003, 70004, 127012, 127019, 127008, + 126990, 126999, 126976, 127005, 126987, 126996, 127004, 126986, 126995, + 126979, 127009, 126991, 127000, 127001, 126983, 126992, 127011, 127010, + 126977, 127007, 126989, 126998, 127006, 126988, 126997, 127015, 127014, + 127003, 126985, 126994, 127002, 126984, 126993, 126978, 126982, 127017, + 126981, 126980, 127016, 127018, 127013, 73464, 73442, 73451, 73448, + 73444, 73449, 73447, 73441, 73450, 73440, 73454, 73445, 73443, 73453, + 73456, 73446, 73455, 73452, 73457, 73463, 73461, 73459, 73462, 73460, + 73458, 128892, 3449, 3435, 3434, 3437, 3436, 3433, 3432, 3430, 3439, + 3431, 3438, 3419, 3420, 3446, 3417, 3422, 3416, 3447, 3444, 3443, 3448, + 3418, 3421, 3445, 3333, 3423, 3334, 3344, 3348, 3453, 3454, 3414, 3451, + 3450, 3452, 3455, 3412, 3413, 3355, 3354, 3406, 3362, 3361, 3367, 3366, + 3360, 3386, 3359, 3365, 3364, 3332, 3339, 3424, 3340, 3425, 3381, 3369, + 3363, 3353, 3358, 3368, 3380, 3379, 3378, 3377, 3376, 3337, 3338, 3346, + 3347, 3335, 3336, 3382, 3383, 3384, 3373, 3372, 3352, 3351, 3357, 3356, + 3350, 3349, 3371, 3370, 3342, 3343, 3385, 3374, 3375, 3441, 3442, 3440, + 3328, 3388, 3329, 3387, 3405, 3331, 3389, 3330, 3407, 3390, 3400, 3404, + 3393, 3394, 3395, 3396, 3426, 3427, 3402, 3403, 3391, 3392, 3398, 3399, + 3415, 9895, 9894, 9893, 9794, 10016, 129443, 128104, 128372, 129333, + 128114, 128115, 128378, 128107, 2137, 2122, 2121, 2133, 2120, 2126, 2132, + 2129, 2136, 2113, 2115, 2114, 2116, 2123, 2124, 2125, 2128, 2130, 2131, + 2118, 2134, 2117, 2112, 2127, 2119, 2135, 2138, 2139, 2142, 68288, 68314, + 68313, 68290, 68289, 68293, 68308, 68292, 68291, 68300, 68299, 68298, + 68297, 68306, 68304, 68320, 68318, 68323, 68312, 68317, 68322, 68309, + 68302, 68324, 68305, 68319, 68307, 68321, 68303, 68294, 68301, 68311, + 68295, 68316, 68315, 68310, 68331, 68335, 68334, 68333, 68332, 68340, + 68339, 68338, 68342, 68337, 68341, 68336, 68296, 68326, 68325, 128094, + 129469, 8380, 128368, 129389, 9967, 127809, 129671, 72835, 72834, 72827, + 72826, 72836, 72828, 72821, 72825, 72829, 72823, 72822, 72819, 72818, + 72831, 72830, 72844, 72845, 72838, 72839, 72840, 72832, 72820, 72846, + 72824, 72843, 72833, 72842, 72837, 72841, 72847, 72886, 72885, 72867, + 72866, 72859, 72858, 72868, 72860, 72853, 72857, 72861, 72855, 72854, + 72851, 72850, 72863, 72862, 72876, 72877, 72870, 72871, 72864, 72852, + 72878, 72856, 72875, 72865, 72874, 72869, 72873, 72879, 72880, 72883, + 72881, 72884, 72882, 72816, 72817, 9901, 129355, 73007, 72980, 72979, + 72983, 72982, 72988, 73008, 72987, 72960, 72961, 72968, 72971, 72985, + 72984, 72990, 72989, 72964, 72965, 72962, 72963, 73005, 72999, 73006, + 72973, 72972, 72976, 72986, 72981, 72991, 73001, 73002, 73003, 72995, + 72994, 72978, 72977, 72975, 72974, 72993, 72992, 73004, 72996, 72998, + 73000, 72997, 72966, 72969, 73031, 73030, 73027, 73028, 73026, 73025, + 73024, 73014, 73009, 73020, 73023, 73012, 73013, 73010, 73011, 73018, + 73021, 73029, 73045, 73044, 73047, 73046, 73043, 73042, 73040, 73049, + 73041, 73048, 186, 127405, 12348, 119811, 120778, 120491, 119827, 120495, + 120505, 120507, 119808, 120488, 119809, 120489, 119833, 120493, 119812, + 120492, 120494, 119814, 120490, 119816, 120496, 119818, 120497, 119819, + 120498, 119822, 120502, 120512, 119823, 120509, 120511, 120503, 119825, + 120504, 119826, 120506, 119828, 120508, 119810, 120510, 119820, 120499, + 119821, 120500, 119831, 120501, 119813, 119815, 119817, 119824, 119829, + 119830, 119832, 119837, 120779, 120517, 119834, 120514, 119835, 120515, + 119859, 120519, 119838, 120518, 120520, 119839, 120531, 119840, 120516, + 119842, 120522, 119844, 120523, 119845, 120524, 119848, 120528, 120538, + 119849, 120535, 120537, 120529, 119851, 120530, 119852, 120532, 119853, + 120521, 120533, 119854, 120534, 119836, 120536, 119846, 120525, 119847, + 120526, 119857, 120527, 119841, 119843, 119850, 119855, 119856, 119858, + 120016, 120017, 120018, 120019, 120020, 120021, 120022, 120023, 120024, + 120025, 120026, 120027, 120028, 120029, 120030, 120031, 120032, 120033, + 120034, 120035, 120036, 120037, 120038, 120039, 120040, 120041, 120042, + 120043, 120044, 120045, 120046, 120047, 120048, 120049, 120050, 120051, + 120052, 120053, 120054, 120055, 120056, 120057, 120058, 120059, 120060, + 120061, 120062, 120063, 120064, 120065, 120066, 120067, 119931, 120611, + 120621, 120623, 119912, 120604, 119913, 120605, 119937, 120609, 119915, + 120607, 119916, 120608, 120610, 119918, 120606, 119920, 120612, 119922, + 120613, 119923, 120614, 119926, 120618, 120628, 119927, 120625, 120627, + 120619, 119929, 120620, 119930, 120622, 119932, 120624, 119914, 120626, + 119924, 120615, 119925, 120616, 119935, 120617, 119917, 119919, 119921, + 119928, 119933, 119934, 119936, 120656, 120658, 120629, 120659, 120655, + 120661, 120660, 119938, 120630, 119939, 120631, 119963, 120635, 119941, + 120633, 119942, 120634, 120636, 119943, 120647, 119944, 120632, 119946, + 120638, 119948, 120639, 119949, 120640, 119952, 120644, 120654, 119953, + 120651, 120653, 120645, 119955, 120646, 119956, 120648, 119957, 120637, + 120649, 119958, 120650, 119940, 120652, 119950, 120641, 119951, 120642, + 119961, 120643, 119945, 119947, 119954, 119959, 119960, 119962, 120657, + 120540, 120542, 120513, 120543, 120539, 120545, 120544, 120541, 120172, + 120173, 120174, 120175, 120176, 120177, 120178, 120179, 120180, 120181, + 120182, 120183, 120184, 120185, 120186, 120187, 120188, 120189, 120190, + 120191, 120192, 120193, 120194, 120195, 120196, 120197, 120198, 120199, + 120200, 120201, 120202, 120203, 120204, 120205, 120206, 120207, 120208, + 120209, 120210, 120211, 120212, 120213, 120214, 120215, 120216, 120217, + 120218, 120219, 120220, 120221, 120222, 120223, 120787, 120786, 120789, + 120788, 120785, 120784, 120782, 120791, 120783, 120790, 120120, 120121, + 120123, 120124, 120125, 120126, 120128, 120129, 120130, 120131, 120132, + 120134, 120138, 120139, 120140, 120141, 120142, 120143, 120144, 120146, + 120147, 120148, 120149, 120150, 120151, 120152, 120153, 120154, 120155, + 120156, 120157, 120158, 120159, 120160, 120161, 120162, 120163, 120164, + 120165, 120166, 120167, 120168, 120169, 120170, 120171, 120797, 120796, + 120799, 120798, 120795, 120794, 120792, 120801, 120793, 120800, 120068, + 120069, 120071, 120072, 120073, 120074, 120077, 120078, 120079, 120080, + 120081, 120082, 120083, 120084, 120086, 120087, 120088, 120089, 120090, + 120091, 120092, 120094, 120095, 120096, 120097, 120098, 120099, 120100, + 120101, 120102, 120103, 120104, 120105, 120106, 120107, 120108, 120109, + 120110, 120111, 120112, 120113, 120114, 120115, 120116, 120117, 120118, + 120119, 10189, 119889, 120484, 120485, 120575, 119886, 120572, 119887, + 120573, 119911, 120577, 119890, 120576, 120578, 119891, 120589, 119892, + 120574, 119894, 120580, 119896, 120581, 119897, 120582, 119900, 120586, + 120596, 119901, 120593, 120595, 120587, 119903, 120588, 119904, 120590, + 119905, 120579, 120591, 119906, 120592, 119888, 120594, 119898, 120583, + 119899, 120584, 119909, 120585, 119895, 119902, 119907, 119908, 119910, + 119879, 120553, 120563, 120565, 119860, 120546, 119861, 120547, 119885, + 120551, 119863, 120549, 119864, 120550, 120552, 119866, 120548, 119868, + 120554, 119870, 120555, 119871, 120556, 119874, 120560, 120570, 119875, + 120567, 120569, 120561, 119877, 120562, 119878, 120564, 119880, 120566, + 119862, 120568, 119872, 120557, 119873, 120558, 119883, 120559, 119865, + 119867, 119869, 119876, 119881, 119882, 119884, 120598, 120600, 120571, + 120601, 120597, 120603, 120602, 120599, 120432, 120433, 120434, 120435, + 120436, 120437, 120438, 120439, 120440, 120441, 120442, 120443, 120444, + 120445, 120446, 120447, 120448, 120449, 120450, 120451, 120452, 120453, + 120454, 120455, 120456, 120457, 120458, 120459, 120460, 120461, 120462, + 120463, 120464, 120465, 120466, 120467, 120468, 120469, 120470, 120471, + 120472, 120473, 120474, 120475, 120476, 120477, 120478, 120479, 120480, + 120481, 120482, 120483, 120827, 120826, 120829, 120828, 120825, 120824, + 120822, 120831, 120823, 120830, 10215, 10221, 10219, 10217, 10223, 10187, + 10214, 10220, 10218, 10216, 10222, 120399, 120727, 120737, 120739, + 120380, 120720, 120381, 120721, 120405, 120725, 120383, 120723, 120384, + 120724, 120726, 120386, 120722, 120388, 120728, 120390, 120729, 120391, + 120730, 120394, 120734, 120744, 120395, 120741, 120743, 120735, 120397, + 120736, 120398, 120738, 120400, 120740, 120382, 120742, 120392, 120731, + 120393, 120732, 120403, 120733, 120385, 120387, 120389, 120396, 120401, + 120402, 120404, 120772, 120774, 120745, 120775, 120771, 120777, 120776, + 120406, 120746, 120407, 120747, 120431, 120751, 120409, 120749, 120410, + 120750, 120752, 120411, 120763, 120412, 120748, 120414, 120754, 120416, + 120755, 120417, 120756, 120420, 120760, 120770, 120421, 120767, 120769, + 120761, 120423, 120762, 120424, 120764, 120425, 120753, 120765, 120426, + 120766, 120408, 120768, 120418, 120757, 120419, 120758, 120429, 120759, + 120413, 120415, 120422, 120427, 120428, 120430, 120773, 120295, 120669, + 120679, 120681, 120276, 120662, 120277, 120663, 120301, 120667, 120279, + 120665, 120280, 120666, 120668, 120282, 120664, 120284, 120670, 120286, + 120671, 120287, 120672, 120290, 120676, 120686, 120291, 120683, 120685, + 120677, 120293, 120678, 120294, 120680, 120296, 120682, 120278, 120684, + 120288, 120673, 120289, 120674, 120299, 120675, 120281, 120283, 120285, + 120292, 120297, 120298, 120300, 120714, 120716, 120687, 120717, 120713, + 120719, 120718, 120302, 120688, 120303, 120689, 120327, 120693, 120305, + 120691, 120306, 120692, 120694, 120307, 120705, 120308, 120690, 120310, + 120696, 120312, 120697, 120313, 120698, 120316, 120702, 120712, 120317, + 120709, 120711, 120703, 120319, 120704, 120320, 120706, 120321, 120695, + 120707, 120322, 120708, 120304, 120710, 120314, 120699, 120315, 120700, + 120325, 120701, 120309, 120311, 120318, 120323, 120324, 120326, 120715, + 120817, 120816, 120819, 120818, 120815, 120814, 120812, 120821, 120813, + 120820, 120328, 120329, 120330, 120331, 120332, 120333, 120334, 120335, + 120336, 120337, 120338, 120339, 120340, 120341, 120342, 120343, 120344, + 120345, 120346, 120347, 120348, 120349, 120350, 120351, 120352, 120353, + 120354, 120355, 120356, 120357, 120358, 120359, 120360, 120361, 120362, + 120363, 120364, 120365, 120366, 120367, 120368, 120369, 120370, 120371, + 120372, 120373, 120374, 120375, 120376, 120377, 120378, 120379, 120224, + 120225, 120226, 120227, 120228, 120229, 120230, 120231, 120232, 120233, + 120234, 120235, 120236, 120237, 120238, 120239, 120240, 120241, 120242, + 120243, 120244, 120245, 120246, 120247, 120248, 120249, 120250, 120251, + 120252, 120253, 120254, 120255, 120256, 120257, 120258, 120259, 120260, + 120261, 120262, 120263, 120264, 120265, 120266, 120267, 120268, 120269, + 120270, 120271, 120272, 120273, 120274, 120275, 120807, 120806, 120809, + 120808, 120805, 120804, 120802, 120811, 120803, 120810, 119964, 119966, + 119967, 119970, 119973, 119974, 119977, 119978, 119979, 119980, 119982, + 119983, 119984, 119985, 119986, 119987, 119988, 119989, 119990, 119991, + 119992, 119993, 119995, 119997, 119998, 119999, 120000, 120001, 120002, + 120003, 120005, 120006, 120007, 120008, 120009, 120010, 120011, 120012, + 120013, 120014, 120015, 129481, 119528, 119538, 119531, 119535, 119525, + 119524, 119534, 119529, 119539, 119527, 119537, 119526, 119536, 119533, + 119523, 119532, 119522, 119530, 119520, 119521, 128470, 175, 8737, 10667, + 10666, 10671, 10669, 10670, 10668, 10665, 10664, 10651, 10653, 8798, + 127830, 129470, 129471, 93773, 93764, 93790, 93787, 983274, 93783, + 983273, 93782, 93772, 93766, 93791, 93779, 93789, 93786, 93776, 93777, + 93785, 93775, 93770, 93769, 93771, 93774, 93780, 93760, 93767, 93781, + 93788, 93761, 93768, 93778, 93762, 93763, 93784, 93765, 93847, 93827, + 93846, 93826, 93845, 93825, 93844, 93829, 93828, 93831, 93830, 93824, + 93833, 93832, 93837, 93836, 93834, 93842, 93835, 93838, 93839, 93843, + 93840, 93841, 93805, 93796, 93822, 93819, 983276, 93815, 983275, 93814, + 93804, 93798, 93823, 93811, 93821, 93818, 93808, 93809, 93817, 93807, + 93802, 93801, 93803, 93806, 93812, 93792, 93799, 93813, 93820, 93793, + 93800, 93810, 93794, 93795, 93816, 93797, 93849, 93850, 93848, 11859, + 11852, 11860, 128901, 9899, 10090, 10091, 128967, 128965, 128944, 10100, + 10088, 10092, 10101, 10089, 10093, 8287, 9618, 128971, 128950, 128938, + 9900, 118512, 128963, 128961, 10073, 128974, 128956, 9898, 128911, + 128931, 43761, 44013, 43762, 43760, 44011, 43994, 43989, 43974, 43746, + 43993, 43991, 43751, 43750, 43992, 43986, 43987, 43990, 43968, 43995, + 43976, 43973, 43999, 43977, 44001, 43752, 43747, 43972, 43998, 43984, + 43969, 43753, 43754, 43975, 44000, 43978, 43749, 43748, 43983, 44002, + 43970, 43996, 43971, 43997, 43981, 43988, 43979, 43985, 43980, 43982, + 43744, 43745, 44012, 43763, 43764, 44005, 43757, 43759, 43758, 44009, + 44003, 44007, 44006, 44004, 43755, 44008, 43756, 44010, 43765, 43766, + 44021, 44020, 44023, 44022, 44019, 44018, 44016, 44025, 44017, 44024, + 118475, 129760, 127816, 125140, 125137, 125136, 125139, 125141, 125138, + 125142, 124928, 124929, 124930, 124936, 124937, 124949, 124950, 124948, + 124938, 124956, 124990, 124992, 124974, 124955, 124964, 124963, 124991, + 124957, 124962, 124976, 124996, 124997, 124998, 124982, 124983, 124984, + 125004, 124975, 125003, 125005, 125011, 125018, 125028, 125029, 125012, + 125019, 125020, 125027, 125013, 125035, 124942, 124934, 125090, 125046, + 125078, 125033, 124946, 125048, 125037, 125070, 125000, 125095, 125121, + 124951, 125044, 125041, 125072, 124987, 125066, 125076, 125074, 125009, + 125107, 125108, 125068, 125124, 124945, 125002, 124931, 125089, 125022, + 124980, 125099, 124986, 125100, 125080, 125119, 124933, 125021, 125015, + 125071, 124985, 125117, 125056, 124993, 125039, 125049, 125043, 125024, + 124932, 125047, 125097, 124959, 125069, 125088, 124999, 125123, 124952, + 125036, 125026, 125001, 125085, 124960, 125057, 125073, 124966, 125098, + 125014, 125091, 124989, 125007, 124978, 124940, 125106, 125050, 125030, + 125092, 124941, 125060, 125077, 125102, 125094, 125053, 125040, 125055, + 125104, 125103, 124939, 125017, 124961, 125112, 125087, 124970, 124971, + 124969, 125023, 124979, 125042, 124947, 125086, 125075, 125051, 125111, + 124968, 124944, 125038, 125096, 125016, 125118, 125109, 124953, 125059, + 125052, 125006, 124958, 125093, 125115, 125054, 124988, 125008, 125084, + 125061, 125064, 125120, 125063, 124967, 124977, 124965, 125031, 983279, + 125081, 125082, 983280, 125010, 125067, 124973, 125032, 124935, 125116, + 125122, 125101, 124994, 124995, 125113, 125058, 125079, 125114, 125065, + 125034, 125083, 124954, 125062, 125105, 125110, 125045, 124943, 124972, + 124981, 125025, 125131, 125130, 125133, 125132, 125129, 125128, 125135, + 125127, 125134, 128334, 128697, 68028, 68093, 68090, 68089, 68086, 68029, + 68092, 68091, 68095, 68088, 68087, 68094, 68000, 68016, 68020, 68021, + 68022, 68009, 68010, 68015, 68017, 68014, 68013, 68018, 68006, 68023, + 68012, 68008, 68007, 68019, 68011, 68005, 68004, 68001, 68002, 68003, + 68031, 68030, 68039, 68075, 68057, 68084, 68066, 68036, 68054, 68081, + 68063, 68045, 68072, 68035, 68053, 68080, 68062, 68044, 68071, 68040, + 68076, 68058, 68085, 68067, 68038, 68056, 68083, 68065, 68047, 68074, + 68037, 68055, 68082, 68064, 68046, 68073, 68034, 68052, 68079, 68061, + 68043, 68070, 68033, 68051, 68078, 68060, 68042, 68069, 68041, 68068, + 68032, 68050, 68077, 68059, 67974, 67975, 67982, 67983, 67978, 67979, + 67980, 67981, 67987, 67988, 67989, 67992, 67993, 67994, 67995, 67996, + 67986, 67985, 67990, 67997, 67984, 67977, 67976, 67991, 67973, 67972, + 67968, 67969, 67970, 67971, 67998, 67999, 9791, 129500, 983173, 9170, + 9172, 9177, 9176, 9175, 9173, 9174, 9171, 9169, 128647, 118467, 128221, + 94015, 93989, 93971, 93958, 94019, 94021, 93953, 94023, 93999, 93995, + 94008, 93981, 93979, 93967, 93963, 93993, 93992, 93983, 93977, 93976, + 93975, 93974, 93968, 94032, 93988, 93987, 93973, 93972, 93997, 93996, + 93969, 94106, 94107, 94108, 94109, 94110, 94111, 94002, 94026, 94022, + 94003, 94004, 94010, 93980, 93978, 94099, 94100, 94101, 94102, 94103, + 94104, 94105, 93998, 93994, 94007, 94025, 93966, 93962, 94024, 93961, + 93960, 94000, 94009, 93964, 93965, 94001, 93970, 93984, 93954, 94017, + 94014, 94016, 94013, 94006, 94012, 94005, 94011, 93986, 93985, 93955, + 93952, 94020, 93990, 93957, 93956, 93959, 93982, 94018, 93991, 94035, + 94034, 94033, 94031, 94098, 94096, 94097, 94095, 94036, 94039, 94040, + 94067, 94068, 94038, 94037, 94073, 94075, 94045, 94071, 94069, 94046, + 94047, 94085, 94074, 94049, 94050, 94051, 94052, 94053, 94086, 94057, + 94054, 94084, 94055, 94056, 94041, 94082, 94048, 94081, 94042, 94076, + 94058, 94059, 94060, 94061, 94063, 94064, 94079, 94087, 94062, 94065, + 94080, 94066, 94072, 94070, 94044, 94043, 94077, 94078, 94083, 983239, + 983240, 128300, 127908, 181, 129440, 117772, 129986, 130022, 130023, 183, + 8943, 129686, 127894, 127756, 8357, 128189, 128469, 128656, 8722, 10793, + 10794, 10796, 10795, 10810, 8770, 8723, 10751, 129694, 129705, 128241, + 128242, 128244, 129339, 8871, 71232, 71229, 71236, 71231, 71230, 71216, + 71226, 71228, 71219, 71220, 71221, 71222, 71223, 71224, 71217, 71218, + 71225, 71227, 71234, 71233, 71253, 71252, 71255, 71254, 71251, 71250, + 71248, 71257, 71249, 71256, 71168, 71169, 71179, 71181, 71195, 71194, + 71200, 71199, 71193, 71192, 71198, 71197, 71174, 71175, 71176, 71177, + 71210, 71172, 71173, 71170, 71171, 71215, 71209, 71186, 71196, 71191, + 71201, 71211, 71212, 71213, 71205, 71204, 71188, 71187, 71185, 71184, + 71190, 71189, 71183, 71182, 71203, 71202, 71214, 71206, 71208, 71207, + 71178, 71180, 71235, 43867, 67512, 714, 700, 67509, 761, 763, 7470, 7471, + 7487, 7474, 7483, 7476, 43000, 7484, 7485, 7468, 7469, 42994, 7472, 7473, + 42995, 7475, 7477, 7478, 7479, 7480, 7481, 7482, 7486, 42996, 42993, + 7488, 7489, 11389, 7490, 723, 722, 42755, 42753, 42757, 42759, 42754, + 42752, 42756, 42758, 42652, 122956, 122958, 122929, 122954, 122932, + 122952, 122943, 122987, 122946, 122938, 122939, 122942, 122960, 122941, + 122959, 122989, 122955, 122950, 122948, 122944, 122951, 122988, 122953, + 122934, 122935, 122936, 122933, 122949, 122931, 122957, 122930, 122947, + 122937, 122928, 122940, 122945, 42653, 7544, 710, 735, 42889, 67510, + 42776, 42775, 42777, 750, 698, 725, 709, 984011, 42765, 42760, 42770, + 741, 984012, 42769, 42764, 42774, 745, 762, 764, 715, 704, 67507, 4348, + 42766, 42761, 42771, 742, 721, 67511, 42888, 42768, 42763, 751, 767, 753, + 42773, 717, 754, 755, 744, 759, 719, 718, 42783, 752, 716, 42778, 703, + 43882, 706, 713, 42767, 42762, 42772, 743, 758, 757, 756, 727, 766, 726, + 697, 42780, 42782, 42781, 42779, 760, 705, 67508, 701, 67513, 702, 43883, + 707, 734, 42890, 765, 7491, 7493, 7516, 67459, 7495, 7601, 7509, 67461, + 7517, 7580, 7590, 694, 7591, 67474, 67476, 7595, 67484, 67491, 67456, + 67460, 67478, 7600, 67498, 7608, 67506, 67471, 67492, 7581, 7521, 7496, + 67468, 67469, 67467, 67466, 7519, 7585, 67480, 67463, 67465, 67464, 7611, + 7613, 7612, 7497, 7604, 7582, 7614, 7505, 7584, 67472, 7501, 7518, 7520, + 67475, 736, 688, 689, 67477, 43868, 67479, 7504, 7596, 7588, 7589, 690, + 7592, 737, 43869, 43870, 7593, 67485, 7594, 67483, 67481, 67482, 67486, + 67487, 43001, 7599, 7598, 7506, 67490, 7499, 7507, 7510, 7602, 691, + 67497, 67496, 67473, 740, 7583, 67470, 738, 67514, 7603, 7586, 7498, + 7513, 7511, 7605, 67503, 67499, 67502, 7508, 67500, 67501, 7492, 7579, + 7494, 7514, 7597, 7500, 692, 67494, 67495, 693, 67488, 67489, 7587, 7502, + 7610, 43881, 7615, 7512, 43871, 7606, 7607, 7515, 67504, 7609, 7503, + 67493, 695, 739, 696, 42784, 42785, 67458, 67457, 720, 699, 724, 708, + 749, 42864, 748, 712, 747, 746, 10762, 128184, 128176, 129297, 71266, + 6165, 6164, 6167, 6166, 6163, 6162, 6160, 6169, 6161, 6168, 6159, 6157, + 6156, 6155, 6147, 6149, 71271, 71272, 6176, 6279, 6272, 6295, 6273, 6289, + 6274, 6313, 6286, 6311, 6310, 6280, 6276, 6275, 6282, 6287, 6278, 6285, + 6284, 6288, 6277, 6291, 6290, 6293, 6294, 6292, 6283, 6281, 6185, 6196, + 6264, 6210, 6190, 6303, 6305, 6307, 6300, 6302, 6304, 6312, 6298, 6301, + 6314, 6308, 6309, 6299, 6306, 6263, 6262, 6260, 6261, 6259, 6244, 6252, + 6245, 6253, 6254, 6248, 6238, 6239, 6257, 6247, 6256, 6242, 6258, 6255, + 6241, 6240, 6249, 6251, 6250, 6243, 6246, 6237, 6193, 6192, 6297, 6296, + 6218, 6236, 6225, 6234, 6227, 6235, 6211, 6222, 6232, 6228, 6224, 6226, + 6233, 6214, 6216, 6215, 6217, 6219, 6231, 6223, 6220, 6221, 6230, 6229, + 6212, 6213, 6204, 6194, 6209, 6207, 6205, 6206, 6203, 6202, 6208, 6191, + 6177, 6183, 6179, 6181, 6180, 6182, 6186, 6195, 6201, 6189, 6197, 6184, + 6187, 6188, 6199, 6200, 6198, 6178, 71273, 71275, 71274, 6151, 71265, + 71270, 71269, 6144, 71268, 71264, 71267, 71276, 6150, 6158, 6148, 6146, + 6152, 6153, 6154, 6145, 9866, 9867, 119552, 9101, 128669, 128018, 128053, + 127889, 129390, 118261, 128496, 129742, 129439, 128332, 129334, 128741, + 128757, 129468, 128739, 9968, 128670, 128672, 128693, 128507, 128001, + 129700, 128045, 128068, 128511, 127909, 92748, 92744, 92761, 92750, + 92739, 92751, 92737, 92754, 92749, 92753, 92743, 92752, 92757, 92766, + 92736, 92741, 92746, 92764, 92745, 92765, 92755, 92756, 92763, 92762, + 92747, 92760, 92758, 92738, 92740, 92759, 92742, 92783, 92782, 92773, + 92772, 92775, 92774, 92771, 92770, 92768, 92777, 92769, 92776, 70291, + 70292, 70290, 70297, 70296, 70293, 70287, 70298, 70312, 70311, 70306, + 70285, 70284, 70289, 70288, 70295, 70294, 70303, 70301, 70283, 70282, + 70280, 70278, 70277, 70276, 70300, 70299, 70310, 70307, 70304, 70309, + 70308, 70305, 70272, 70275, 70273, 70274, 70313, 127926, 215, 10804, + 10805, 10807, 10811, 10801, 10800, 10005, 8844, 8845, 8846, 8888, 127812, + 117860, 9838, 9839, 9837, 127929, 127896, 119161, 119159, 119155, 119157, + 119061, 119060, 119224, 119235, 119132, 119058, 119059, 119255, 119253, + 119130, 119131, 119163, 119169, 119149, 119167, 119168, 119210, 119178, + 119173, 119211, 119150, 119151, 119152, 119153, 119154, 119175, 119212, + 119213, 119166, 119164, 119141, 119142, 119176, 119179, 119143, 119144, + 119145, 119165, 119177, 119170, 119174, 119092, 119052, 119247, 119186, + 119073, 119109, 119093, 119050, 119220, 119221, 119044, 119049, 119187, + 119209, 119082, 119041, 119083, 119077, 119078, 119162, 119160, 119208, + 119156, 119158, 119136, 119102, 119056, 119057, 119146, 119147, 119148, + 119042, 119066, 119065, 119069, 119185, 119074, 119075, 119076, 119231, + 119232, 119085, 119084, 119070, 119071, 119072, 119218, 119217, 119189, + 119188, 119248, 119249, 119172, 119171, 119216, 119134, 119100, 119206, + 119262, 119270, 119271, 119263, 119268, 119269, 119272, 119264, 119267, + 119266, 119265, 119274, 119223, 119234, 119233, 119046, 119222, 119184, + 119227, 119237, 119228, 119122, 119123, 119081, 119098, 119207, 119129, + 119087, 119086, 119128, 119140, 119106, 119062, 119195, 119204, 119205, + 119196, 119197, 119198, 119199, 119200, 119201, 119202, 119203, 119094, + 119095, 119126, 119108, 119215, 119214, 119261, 119252, 119257, 119258, + 119183, 119090, 119091, 119135, 119101, 119096, 119097, 119053, 119054, + 119055, 119048, 119043, 119047, 119180, 119254, 119259, 119225, 119236, + 119226, 119229, 119238, 119230, 119051, 119045, 119089, 119088, 119040, + 119067, 119068, 119137, 119103, 119139, 119105, 119110, 119111, 119250, + 119181, 119273, 119243, 119244, 119245, 119246, 119242, 119240, 119239, + 119241, 119064, 119138, 119104, 119063, 119256, 119260, 119190, 119118, + 119119, 119120, 119121, 119112, 119113, 119116, 119117, 119114, 119115, + 119124, 119125, 119191, 119193, 119194, 119127, 119251, 119107, 119133, + 119099, 119219, 119192, 119182, 127932, 127925, 8811, 8810, 4158, 4156, + 4157, 4155, 4192, 4191, 4190, 4226, 4129, 43642, 4138, 4135, 4208, 4207, + 4206, 4159, 4099, 4098, 4097, 43625, 43624, 43626, 43623, 43622, 43621, + 43627, 43618, 43617, 43630, 43629, 43620, 43619, 983244, 43631, 43616, + 43635, 43628, 43633, 43634, 4096, 4188, 4189, 4187, 4186, 4136, 4121, + 4106, 4111, 4100, 4105, 4116, 4238, 4123, 4176, 43491, 4218, 4220, 43490, + 4221, 4224, 43492, 4223, 43489, 4216, 43488, 4215, 4214, 4213, 4219, + 4222, 4225, 4217, 4130, 43646, 43647, 4193, 4177, 4126, 4112, 43503, + 43495, 43502, 43501, 43516, 43515, 43518, 43517, 43498, 43497, 43500, + 43499, 43514, 43496, 4108, 4107, 4113, 4198, 4197, 4125, 4110, 4109, + 4115, 4114, 4133, 4134, 4178, 4179, 4180, 4181, 4131, 4132, 4128, 4124, + 4120, 4119, 4102, 4101, 4104, 4103, 4118, 4117, 4127, 4122, 4137, 43636, + 43637, 43638, 43632, 43494, 4245, 4244, 4247, 4246, 4243, 4242, 4240, + 4249, 4241, 4248, 4154, 4150, 4250, 4251, 4237, 4235, 4236, 4231, 4232, + 4233, 4234, 43493, 4171, 43644, 43645, 4201, 4202, 4203, 4204, 4205, + 4151, 4239, 43643, 4170, 4153, 4152, 43639, 43641, 43640, 4174, 4172, + 4255, 4254, 4175, 4173, 71391, 71390, 71393, 71392, 71389, 71388, 71386, + 71395, 71387, 71394, 4195, 4196, 43509, 43508, 43511, 43510, 43507, + 43506, 43504, 43513, 43505, 43512, 4146, 4252, 4253, 4140, 4209, 4212, + 4210, 4211, 4147, 4148, 4228, 4229, 4230, 4227, 4194, 4145, 4149, 4199, + 4200, 4139, 4143, 4144, 4182, 4183, 4184, 4185, 4141, 4142, 71381, 71380, + 71383, 71382, 71379, 71378, 71376, 71385, 71377, 71384, 4165, 4164, 4167, + 4166, 4163, 4162, 4160, 4169, 4161, 4168, 983218, 983232, 983174, 10753, + 10754, 10752, 8720, 10761, 10757, 10758, 8721, 8899, 10756, 10755, 11007, + 8896, 8897, 8898, 8719, 67712, 67728, 67740, 67724, 67726, 67714, 67732, + 67718, 67730, 67723, 67742, 67717, 67729, 67738, 67739, 67713, 67735, + 67716, 67721, 67734, 67737, 67741, 67725, 67719, 67722, 67727, 67715, + 67733, 67720, 67736, 67731, 67758, 67752, 67753, 67757, 67751, 67759, + 67756, 67754, 67755, 8711, 124117, 124120, 124119, 124121, 124118, + 124132, 124136, 124133, 124138, 124137, 124134, 124135, 124122, 124124, + 124126, 124123, 124125, 124112, 124116, 124114, 124113, 124115, 124127, + 124128, 124129, 124130, 124131, 124140, 124143, 124142, 124139, 124141, + 124149, 124148, 124151, 124150, 124147, 124146, 124144, 124153, 124145, + 124152, 128133, 8358, 8892, 72102, 72103, 72138, 72096, 72097, 72107, + 72109, 72123, 72122, 72128, 72127, 72144, 72136, 72121, 72120, 72126, + 72125, 72100, 72101, 72098, 72099, 72143, 72137, 72114, 72124, 72119, + 72129, 72139, 72140, 72141, 72133, 72132, 72116, 72115, 72113, 72112, + 72118, 72117, 72111, 72110, 72131, 72130, 72142, 72134, 72135, 72106, + 72108, 72162, 72161, 72158, 72160, 72159, 72164, 72150, 72151, 72145, + 72155, 72157, 72148, 72149, 72146, 72147, 72154, 72156, 72163, 8302, + 127966, 129314, 128219, 129535, 8239, 983092, 983197, 983128, 9471, 9453, + 9460, 9452, 9458, 9451, 9454, 9455, 9459, 9456, 9457, 127312, 127313, + 127314, 127315, 127316, 127317, 127318, 127319, 127320, 127321, 127322, + 127323, 127324, 127325, 127326, 127327, 127328, 127329, 127330, 127331, + 127332, 127333, 127334, 127335, 127336, 127337, 128982, 128984, 129982, + 129981, 129983, 127344, 127345, 127346, 127347, 127348, 127349, 127350, + 127351, 127352, 127353, 127354, 127355, 127356, 127357, 127358, 127359, + 127360, 127361, 127362, 127363, 127364, 127365, 127366, 127367, 127368, + 127369, 129204, 129205, 129207, 129988, 10062, 127374, 127371, 127375, + 129206, 127372, 127373, 983091, 8879, 8840, 8841, 8775, 8821, 8817, 8825, + 8820, 8816, 8824, 129670, 129722, 11228, 129540, 129544, 129565, 129586, + 129603, 129607, 129561, 129536, 129557, 129599, 129539, 129560, 129602, + 129610, 129613, 129541, 129562, 129604, 129537, 129558, 129600, 129538, + 129559, 129601, 129581, 129578, 129582, 129583, 129579, 129580, 128528, + 9906, 127770, 127761, 8362, 6595, 6594, 6599, 6598, 6597, 6596, 6593, + 6566, 6530, 6567, 6531, 6570, 6537, 6532, 6544, 6543, 6536, 6542, 6549, + 6548, 6562, 6561, 6554, 6560, 6556, 6550, 6528, 6555, 6538, 6568, 6533, + 6569, 6534, 6571, 6540, 6535, 6547, 6546, 6539, 6545, 6552, 6551, 6565, + 6564, 6557, 6563, 6559, 6553, 6529, 6558, 6541, 6622, 6623, 6600, 6601, + 6618, 6577, 6587, 6582, 6586, 6578, 6592, 6583, 6584, 6590, 6589, 6579, + 6585, 6591, 6580, 6588, 6576, 6581, 6613, 6612, 6615, 6614, 6611, 6610, + 6608, 6617, 6609, 6616, 983063, 70732, 70746, 70731, 70741, 70740, 70743, + 70742, 70739, 70738, 70736, 70745, 70737, 70744, 70734, 70675, 70674, + 70681, 70680, 70692, 70686, 70691, 70751, 70662, 70663, 70664, 70665, + 70656, 70657, 70667, 70669, 70685, 70684, 70690, 70689, 70683, 70682, + 70688, 70687, 70660, 70661, 70658, 70659, 70705, 70706, 70707, 70696, + 70695, 70677, 70676, 70673, 70672, 70679, 70678, 70671, 70670, 70703, + 70702, 70698, 70697, 70694, 70693, 70701, 70700, 70708, 70704, 70699, + 70666, 70668, 70723, 70726, 70727, 70724, 70752, 70728, 70753, 70722, + 70725, 70730, 70750, 70747, 70709, 70719, 70721, 70712, 70713, 70714, + 70715, 70716, 70717, 70710, 70711, 70718, 70720, 70735, 70749, 70733, + 70729, 11154, 11155, 128240, 9112, 983131, 9798, 11209, 128084, 129299, + 983132, 128985, 129399, 127747, 2035, 2031, 2032, 2033, 2030, 2034, 2027, + 2028, 2029, 2040, 2045, 2046, 1989, 1988, 1991, 1990, 1987, 1986, 1984, + 1993, 1985, 1992, 2042, 2008, 2001, 2025, 2024, 2026, 2006, 2002, 2019, + 2016, 2018, 2023, 2010, 2009, 2000, 1999, 2007, 1997, 1995, 2012, 2003, + 2013, 2020, 2014, 2015, 2017, 2004, 2011, 2005, 2021, 2022, 1994, 1996, + 1998, 2037, 2036, 2039, 2038, 2041, 2047, 983127, 128691, 9940, 128683, + 128695, 128370, 128286, 128245, 128685, 8303, 65934, 8209, 128689, 10973, + 8893, 8599, 10542, 10545, 10536, 10532, 129209, 10530, 128602, 128594, + 128610, 11111, 11127, 11016, 8663, 129109, 11008, 43063, 43062, 43065, + 43064, 43059, 43060, 43057, 43056, 43061, 43058, 10529, 8598, 8689, 8632, + 10546, 10535, 10531, 129208, 128600, 128592, 128608, 11110, 11126, 11017, + 8662, 129108, 11009, 128746, 8882, 8884, 8379, 8836, 8837, 8713, 8772, + 8777, 8938, 8940, 8742, 8930, 8931, 172, 8877, 8769, 8813, 8800, 8802, + 8815, 8814, 9083, 128323, 10159, 128324, 10161, 128456, 128457, 128458, + 128211, 128212, 128067, 118012, 160, 9369, 9362, 9365, 9366, 9367, 35, + 9368, 9364, 9361, 9363, 9371, 9370, 8470, 128297, 94177, 983041, 983040, + 123149, 123155, 123138, 123166, 123164, 123148, 123143, 123161, 123153, + 123152, 123141, 123137, 123156, 123139, 123163, 123142, 123172, 123173, + 123140, 123167, 123171, 123158, 123176, 123177, 123165, 123151, 123168, + 123136, 123169, 123162, 123178, 123179, 123144, 123157, 123170, 123150, + 123145, 123159, 123146, 123154, 123160, 123147, 123174, 123175, 123180, + 123214, 123193, 123192, 123195, 123194, 123191, 123196, 123197, 123184, + 123190, 123189, 123186, 123185, 123188, 123187, 123215, 123205, 123204, + 123207, 123206, 123203, 123202, 123200, 123209, 123201, 123208, 117776, + 983231, 983066, 10663, 10662, 11869, 9215, 65532, 9287, 9286, 9284, 9285, + 9289, 9281, 9290, 9288, 9282, 9283, 9280, 128721, 128025, 128885, 5787, + 5788, 5776, 5761, 5786, 5770, 5769, 5781, 5779, 5785, 5763, 5772, 5780, + 5784, 5762, 5775, 5773, 5765, 5777, 5782, 5764, 5774, 5783, 5766, 5778, + 5771, 5767, 5768, 5760, 731, 128738, 7265, 7264, 7266, 7267, 7261, 7260, + 7262, 7259, 7280, 7282, 7281, 7279, 7271, 7270, 7272, 7269, 7258, 7263, + 7278, 7268, 7283, 7273, 7284, 7285, 7287, 7286, 7276, 7274, 7275, 7277, + 7290, 7288, 7289, 7295, 7294, 7292, 7291, 7253, 7252, 7255, 7254, 7251, + 7250, 7248, 7257, 7249, 7256, 7293, 124374, 124376, 124375, 124377, + 124378, 124379, 124392, 124396, 124395, 124397, 124394, 124393, 124380, + 124381, 124383, 124384, 124385, 124382, 124368, 124371, 124370, 124369, + 124372, 124373, 124386, 124388, 124390, 124389, 124387, 124391, 124399, + 124400, 124398, 124415, 124406, 124405, 124408, 124407, 124404, 124403, + 124401, 124410, 124402, 124409, 94179, 94178, 68736, 68739, 68744, 68737, + 68756, 68745, 68760, 68769, 68761, 68775, 68785, 68741, 68762, 68772, + 68773, 68740, 68777, 68742, 68749, 68750, 68758, 68759, 68774, 68776, + 68783, 68784, 68738, 68743, 68747, 68748, 68751, 68754, 68755, 68768, + 68770, 68782, 68765, 68780, 68766, 68781, 68763, 68767, 68764, 68771, + 68778, 68757, 68786, 68779, 68746, 68752, 68753, 68800, 68803, 68808, + 68801, 68820, 68809, 68824, 68833, 68825, 68839, 68849, 68805, 68826, + 68836, 68837, 68804, 68841, 68806, 68813, 68814, 68822, 68823, 68838, + 68840, 68847, 68848, 68802, 68807, 68811, 68812, 68815, 68818, 68819, + 68832, 68834, 68846, 68829, 68844, 68830, 68845, 68827, 68831, 68828, + 68835, 68842, 68821, 68850, 68843, 68810, 68816, 68817, 68861, 68859, + 68858, 68862, 68863, 68860, 66308, 66324, 66318, 66335, 66323, 66331, + 66327, 66330, 66315, 66316, 66317, 66329, 66314, 66306, 66322, 66351, + 66321, 66350, 66326, 66334, 66313, 66333, 66328, 66320, 66312, 66325, + 66332, 66305, 66307, 66311, 66309, 66349, 66310, 66304, 66319, 66339, + 66337, 66338, 66336, 68241, 68242, 68246, 68244, 68226, 68224, 68237, + 68235, 68249, 68251, 68247, 68233, 68248, 68252, 68227, 68234, 68230, + 68239, 68232, 68240, 68231, 68250, 68236, 68225, 68243, 68245, 68228, + 68229, 68238, 68255, 68254, 68253, 66404, 66390, 66392, 66387, 66388, + 66411, 66393, 66421, 66418, 66396, 66397, 66399, 66406, 66405, 66401, + 66413, 66402, 66398, 66414, 66415, 66416, 66408, 66420, 66417, 66407, + 66419, 66389, 66391, 66395, 66400, 66386, 66409, 66410, 66385, 66384, + 66394, 66412, 66403, 66516, 66514, 66515, 66517, 66513, 66464, 66504, + 66505, 66506, 66482, 66510, 66511, 66477, 66508, 66509, 66478, 66479, + 66473, 66474, 66490, 66491, 66480, 66475, 66476, 66507, 66471, 66469, + 66470, 66467, 66468, 66484, 66485, 66492, 66493, 66486, 66487, 66488, + 66497, 66498, 66495, 66472, 66483, 66499, 66494, 66481, 66489, 66496, + 66465, 66466, 66512, 128435, 118450, 69414, 69395, 69376, 69394, 69391, + 69392, 69398, 69399, 69403, 69404, 69377, 69379, 69382, 69400, 69388, + 69380, 69384, 69393, 69397, 69401, 69386, 69381, 69385, 69387, 69378, + 69390, 69402, 69383, 69396, 69389, 69415, 69407, 69412, 69411, 69406, + 69410, 69405, 69413, 69409, 69408, 68209, 68210, 68217, 68211, 68213, + 68214, 68212, 68203, 68205, 68207, 68206, 68202, 68198, 68220, 68219, + 68215, 68201, 68216, 68193, 68196, 68199, 68218, 68192, 68194, 68200, + 68204, 68197, 68208, 68195, 68222, 68221, 68223, 68608, 68619, 68627, + 68623, 68634, 68640, 68644, 68668, 68670, 68677, 68632, 68669, 68671, + 68617, 68625, 68621, 68638, 68643, 68660, 68666, 68675, 68630, 68648, + 68653, 68646, 68650, 68641, 68673, 68658, 68642, 68655, 68628, 68611, + 68657, 68662, 68614, 68615, 68636, 68656, 68664, 68679, 68680, 68609, + 68610, 68645, 68654, 68620, 68624, 68635, 68678, 68633, 68672, 68652, + 68618, 68626, 68622, 68639, 68661, 68667, 68676, 68631, 68613, 68649, + 68647, 68651, 68674, 68659, 68629, 68612, 68663, 68616, 68637, 68665, + 69509, 69508, 69507, 69506, 69488, 69502, 69496, 69505, 69492, 69499, + 69501, 69503, 69494, 69493, 69490, 69495, 69489, 69498, 69504, 69491, + 69500, 69497, 69511, 69512, 69513, 69510, 128477, 128117, 129491, 128116, + 129746, 128283, 128664, 128753, 128662, 128660, 128653, 11819, 8228, + 128431, 129649, 129477, 128214, 9251, 10044, 10027, 10034, 10011, 128194, + 128449, 128080, 128237, 128236, 10180, 10179, 128275, 9103, 9104, 10174, + 983191, 8997, 128191, 128440, 9934, 9741, 128217, 129505, 129447, 128895, + 129741, 8886, 2902, 2903, 2933, 2934, 2931, 2930, 2935, 2932, 2928, 2909, + 2908, 2864, 2911, 2863, 2821, 2822, 2832, 2836, 2850, 2849, 2855, 2854, + 2848, 2847, 2853, 2852, 2827, 2912, 2828, 2913, 2869, 2825, 2826, 2823, + 2824, 2867, 2866, 2841, 2851, 2846, 2856, 2870, 2871, 2872, 2861, 2860, + 2843, 2842, 2840, 2839, 2845, 2844, 2838, 2837, 2859, 2858, 2873, 2862, + 2929, 2831, 2835, 983660, 983659, 2817, 2876, 2877, 2818, 2901, 2893, + 2819, 2878, 2888, 2892, 2881, 2882, 2883, 2884, 2914, 2915, 2879, 2880, + 2887, 2891, 2923, 2922, 2925, 2924, 2921, 2920, 2918, 2927, 2919, 2926, + 64830, 64831, 9766, 117826, 10183, 66736, 66737, 66738, 66739, 66743, + 66763, 66761, 66742, 66749, 66757, 66744, 66768, 66750, 66748, 66754, + 66755, 66764, 66762, 66760, 66746, 66745, 66741, 66765, 66769, 66759, + 66758, 66771, 66770, 66740, 66751, 66752, 66753, 66756, 66767, 66747, + 66766, 66776, 66777, 66778, 66779, 66783, 66803, 66801, 66782, 66789, + 66797, 66784, 66808, 66790, 66788, 66794, 66795, 66804, 66802, 66800, + 66786, 66785, 66781, 66805, 66809, 66799, 66798, 66811, 66810, 66780, + 66791, 66792, 66793, 66796, 66807, 66787, 66806, 66710, 66688, 66715, + 66699, 66694, 66698, 66703, 66693, 66697, 66696, 66705, 66702, 66713, + 66717, 66704, 66706, 66707, 66711, 66716, 66689, 66701, 66700, 66708, + 66691, 66695, 66690, 66692, 66709, 66712, 66714, 66725, 66724, 66727, + 66726, 66723, 66722, 66720, 66729, 66721, 66728, 983192, 126257, 126264, + 126258, 126259, 126265, 126260, 126263, 126267, 126255, 126266, 126256, + 126262, 126261, 126269, 126268, 126254, 126216, 126225, 126252, 126234, + 126243, 126222, 126249, 126213, 126231, 126240, 126221, 126248, 126212, + 126230, 126239, 126217, 126226, 126253, 126235, 126244, 126215, 126224, + 126251, 126233, 126242, 126214, 126223, 126250, 126232, 126241, 126220, + 126247, 126211, 126229, 126238, 126219, 126246, 126210, 126228, 126237, + 126218, 126245, 126209, 126227, 126236, 129446, 128228, 117974, 117975, + 117976, 117977, 117978, 117979, 117980, 117981, 117982, 117983, 117984, + 117985, 117986, 117987, 117988, 117989, 117990, 117991, 117992, 117993, + 117994, 117995, 117996, 117997, 117998, 117999, 10015, 9885, 10029, + 10009, 118005, 118004, 118007, 118006, 118003, 118002, 118000, 118009, + 118001, 118008, 8485, 129397, 128471, 11195, 11194, 11196, 8254, 129450, + 127970, 118459, 8486, 128076, 127842, 128329, 129417, 128002, 983122, + 983121, 128463, 128195, 128479, 128196, 128223, 128464, 128724, 93059, + 93065, 93064, 93058, 93070, 93056, 93055, 93053, 93062, 93069, 93061, + 93066, 93071, 93068, 93054, 93057, 93063, 93067, 93060, 92967, 92975, + 92965, 92969, 92959, 92968, 92971, 92957, 92962, 92960, 92972, 92970, + 92963, 92958, 92966, 92961, 92956, 92974, 92964, 92973, 92979, 92978, + 92980, 92977, 92976, 92982, 92981, 93023, 93020, 93024, 93021, 93019, + 93025, 93022, 93043, 92985, 93047, 93044, 93045, 92997, 93046, 93042, + 93032, 93029, 92995, 93038, 92993, 93041, 93035, 93033, 93037, 93030, + 93039, 92987, 92992, 92986, 92983, 92984, 93027, 92994, 93034, 92988, + 92990, 92989, 92991, 93028, 92996, 93031, 93040, 93036, 92954, 92955, + 92938, 92939, 92932, 92933, 92942, 92943, 92950, 92951, 92928, 92929, + 92936, 92937, 92948, 92949, 92930, 92931, 92944, 92945, 92934, 92935, + 92940, 92941, 92946, 92947, 92952, 92953, 93013, 93012, 93015, 93014, + 93011, 93010, 93008, 93017, 93009, 93016, 9908, 11801, 127796, 129779, + 129780, 67703, 67693, 67688, 67702, 67683, 67691, 67699, 67700, 67680, + 67696, 67682, 67686, 67695, 67698, 67701, 67689, 67684, 67687, 67690, + 67681, 67694, 67685, 67697, 67692, 67704, 67711, 67706, 67707, 67710, + 67709, 67708, 67705, 129330, 129374, 128060, 8233, 11853, 11791, 10995, + 10994, 8741, 129666, 12809, 12823, 12808, 12822, 12828, 12813, 12827, + 12810, 12824, 12800, 12814, 12804, 12818, 12801, 12815, 12812, 12826, + 12805, 12819, 12803, 12817, 12806, 12820, 12811, 12825, 12802, 12816, + 12807, 12821, 12863, 12855, 12858, 12861, 12847, 12839, 12854, 12843, + 12836, 12864, 12835, 12856, 12846, 12842, 12840, 12852, 12862, 12865, + 12857, 12867, 12838, 12866, 12851, 12853, 12859, 12849, 12860, 12848, + 12837, 12834, 12841, 12833, 12845, 12844, 12850, 12832, 12830, 12829, + 9349, 9342, 9345, 9346, 9350, 9347, 9348, 9344, 9351, 9343, 9341, 9336, + 9335, 9338, 9337, 9334, 9333, 9340, 9332, 9339, 127248, 127249, 127250, + 127251, 127252, 127253, 127254, 127255, 127256, 127257, 127258, 127259, + 127260, 127261, 127262, 127263, 127264, 127265, 127266, 127267, 127268, + 127269, 127270, 127271, 127272, 127273, 9372, 9373, 9374, 9375, 9376, + 9377, 9378, 9379, 9380, 9381, 9382, 9383, 9384, 9385, 9386, 9387, 9388, + 9389, 9390, 9391, 9392, 9393, 9394, 9395, 9396, 9397, 8706, 983147, + 983146, 983149, 983150, 9853, 127881, 118468, 128890, 12880, 12349, + 129436, 128755, 11261, 9105, 9106, 128706, 72437, 72440, 72432, 72416, + 72419, 72413, 72417, 72415, 72412, 72414, 72418, 72420, 72403, 72407, + 72411, 72409, 72410, 72391, 72400, 72404, 72397, 72394, 72385, 72401, + 72384, 72399, 72398, 72396, 72388, 72393, 72392, 72386, 72387, 72402, + 72395, 72390, 72389, 72405, 72406, 72408, 72436, 72435, 72438, 72439, + 72422, 72421, 72424, 72425, 72431, 72433, 72434, 72428, 72427, 72429, + 72430, 72423, 72426, 128062, 128230, 128206, 983228, 983237, 129434, + 9774, 127825, 129372, 129755, 127824, 128039, 128532, 9956, 128390, 9999, + 8240, 8241, 8524, 10178, 10977, 129336, 129496, 129494, 128113, 9977, + 128590, 129733, 128591, 129493, 128589, 128588, 129495, 128583, 128187, + 8966, 128547, 37, 9854, 127917, 8359, 8369, 129515, 128694, 129730, + 43101, 43117, 43120, 43076, 43123, 43077, 43115, 43090, 43082, 43094, + 43098, 43099, 43119, 43118, 43109, 43074, 43075, 43116, 43079, 43083, + 43089, 43088, 43114, 43113, 43081, 43080, 43073, 43072, 43085, 43084, + 43092, 43093, 43104, 43110, 43086, 43108, 43100, 43078, 43097, 43087, + 43106, 43096, 43091, 43107, 43095, 43102, 43105, 43103, 43127, 43126, + 43124, 43121, 43111, 43112, 43122, 43125, 66033, 66023, 66017, 66010, + 66027, 66003, 66018, 66028, 66004, 66012, 66022, 66020, 66045, 66019, + 66031, 66041, 66007, 66006, 66025, 66026, 66038, 66016, 66013, 66014, + 66000, 66001, 66034, 66036, 66037, 66029, 66011, 66024, 66015, 66021, + 66042, 66043, 66002, 66008, 66032, 66005, 66044, 66040, 66039, 66030, + 66009, 66035, 5941, 5942, 67840, 67855, 67843, 67844, 67847, 67858, + 67857, 67860, 67854, 67861, 67848, 67845, 67846, 67849, 67859, 67842, + 67850, 67853, 67851, 67841, 67856, 67852, 67864, 67866, 67867, 67863, + 67862, 67865, 67871, 11227, 9935, 128763, 128022, 128061, 128055, 128169, + 182, 128138, 129292, 129295, 127885, 127821, 10031, 129655, 129669, 9811, + 128299, 8916, 10970, 129383, 8984, 128720, 129703, 8462, 8463, 128733, + 127183, 127136, 127167, 127199, 127188, 127140, 127156, 127172, 127200, + 127189, 127141, 127157, 127173, 127196, 127148, 127164, 127180, 127198, + 127150, 127166, 127182, 127192, 127144, 127160, 127176, 127191, 127143, + 127159, 127175, 127190, 127142, 127158, 127174, 127197, 127149, 127165, + 127181, 127194, 127146, 127162, 127178, 127187, 127139, 127155, 127171, + 127186, 127138, 127154, 127170, 127202, 127220, 127221, 127201, 127210, + 127211, 127212, 127213, 127214, 127215, 127216, 127217, 127218, 127219, + 127203, 127204, 127205, 127206, 127207, 127208, 127209, 127185, 127137, + 127153, 127169, 127193, 127145, 127161, 127177, 127195, 127147, 127163, + 127179, 983151, 43, 10797, 10798, 10809, 10789, 10786, 10791, 10790, + 10788, 10792, 10787, 10866, 177, 9799, 11222, 11221, 11220, 11219, + 129696, 983148, 117777, 128659, 128680, 128110, 8297, 8236, 127871, + 128254, 11239, 128239, 12306, 12320, 128238, 8982, 128688, 129364, + 127858, 129716, 127831, 129751, 128574, 128545, 163, 128093, 9212, 9213, + 9214, 9211, 128041, 128425, 129328, 129732, 129731, 65043, 65040, 65045, + 65073, 65074, 65049, 65041, 65042, 65091, 65047, 65083, 65085, 65089, + 65079, 65087, 65077, 65095, 65081, 65075, 65084, 65086, 65092, 983261, + 65048, 65090, 65080, 65088, 65078, 65096, 65082, 65076, 65044, 65072, + 65046, 8478, 8826, 10937, 10933, 10927, 10929, 10935, 10931, 8936, 8830, + 8828, 8880, 9111, 129384, 9113, 128424, 128438, 129332, 128120, 983193, + 983166, 983163, 983164, 983167, 8242, 8965, 8759, 8733, 8522, 11224, + 128711, 129455, 128255, 68507, 68508, 68480, 68483, 68490, 68491, 68485, + 68482, 68486, 68493, 68495, 68496, 68488, 68484, 68487, 68489, 68481, + 68492, 68497, 68494, 68526, 68522, 68523, 68525, 68521, 68527, 68524, + 68505, 68506, 118473, 11854, 8200, 128156, 128091, 128686, 128204, + 128226, 983165, 983168, 983194, 9624, 9625, 9626, 9627, 9628, 9629, 9630, + 9631, 9622, 9623, 10764, 8279, 10774, 9833, 128894, 8264, 63, 8799, 34, + 9915, 127949, 127950, 129437, 128251, 9762, 128280, 9143, 128740, 128643, + 9926, 127752, 11827, 11783, 11782, 9995, 128400, 128406, 127338, 127339, + 127340, 129996, 11787, 9994, 11828, 129306, 128000, 8758, 128007, 128048, + 129682, 128015, 129534, 117778, 9852, 9843, 9844, 9845, 9846, 9847, 9848, + 9849, 9850, 983113, 128665, 127822, 129511, 174, 127462, 127463, 127464, + 127465, 127466, 127467, 127468, 127469, 127470, 127471, 127472, 127473, + 127474, 127475, 127476, 127477, 127478, 127479, 127480, 127481, 127482, + 127483, 127484, 127485, 127486, 127487, 43344, 43343, 43346, 43345, + 43333, 43323, 43331, 43314, 43332, 43317, 43330, 43320, 43319, 43321, + 43316, 43313, 43329, 43322, 43312, 43326, 43318, 43325, 43324, 43315, + 43328, 43327, 43334, 43359, 43337, 43342, 43341, 43338, 43340, 43335, + 43339, 43336, 43347, 128524, 127895, 65533, 9952, 9953, 128699, 8479, + 9166, 11152, 11153, 128639, 128968, 983152, 92, 10184, 10741, 10743, + 11073, 11079, 983153, 10659, 10661, 8246, 12317, 10989, 8976, 11793, + 8267, 8245, 128401, 9753, 11262, 8515, 8271, 8765, 8909, 8247, 128402, + 128403, 128405, 11841, 11822, 10672, 128404, 128158, 8251, 129423, + 983154, 127872, 11190, 11188, 11191, 11189, 11186, 11187, 11184, 11185, + 127832, 127833, 129919, 129918, 128495, 8735, 12297, 10642, 11777, 11776, + 9084, 8894, 10652, 10644, 10228, 8692, 12305, 10648, 125, 9132, 9133, + 9131, 12301, 8969, 11781, 12299, 10608, 10715, 8221, 11817, 129929, + 10621, 8971, 118272, 9687, 11241, 9616, 129978, 117925, 118289, 129971, + 118286, 118284, 118432, 118443, 129933, 128381, 130025, 130017, 11805, + 8906, 10198, 129927, 9621, 129980, 41, 9120, 9118, 9119, 11789, 10182, + 8908, 129931, 8217, 11815, 128360, 128361, 128362, 128489, 93, 9126, + 9124, 10638, 10640, 8262, 10636, 11864, 11862, 9125, 11779, 117773, 129987, 128493, 118433, 118442, 129930, 129928, 11786, 8895, 10702, 129902, 12309, 8866, 11809, 9145, 117766, 12303, 10628, 12311, 10630, 12315, 12313, 10713, 1421, 117833, 117907, 117909, 129308, 4053, 4055, @@ -12379,8 +11870,8 @@ static const unsigned int dawg_pos_to_codepoint[] = { 8628, 10513, 8699, 129202, 8620, 129034, 10565, 129042, 129026, 8603, 11022, 11023, 8611, 10517, 10516, 129030, 129178, 129046, 8618, 8696, 8644, 10522, 129193, 129185, 11146, 11157, 8658, 10499, 8655, 10503, - 10524, 10509, 8674, 129195, 129078, 8652, 10601, 10605, 10591, 10583, - 8641, 10600, 10604, 10596, 10587, 10579, 8640, 129777, 129090, 129094, + 10524, 10509, 8674, 129195, 129078, 10601, 10605, 10591, 10583, 8641, + 10600, 10604, 10596, 10587, 10579, 8640, 8652, 129777, 129090, 129094, 129191, 8702, 8649, 129784, 129189, 128622, 8669, 129082, 129106, 129187, 11106, 11138, 11132, 983242, 11175, 11173, 129066, 129062, 129058, 129074, 129070, 11122, 11116, 11142, 129170, 10511, 8667, 10518, 10520, @@ -12419,50 +11910,54 @@ static const unsigned int dawg_pos_to_codepoint[] = { 43149, 43186, 43178, 43180, 43179, 43205, 43136, 43204, 43137, 43215, 43214, 43221, 43220, 43223, 43222, 43219, 43218, 43216, 43225, 43217, 43224, 43188, 43189, 43200, 43203, 43192, 43193, 43194, 43195, 43196, - 43197, 43201, 43202, 43190, 43191, 43198, 43199, 129429, 9973, 127927, - 127862, 129403, 9878, 129507, 127979, 127890, 129410, 9807, 128756, - 129691, 128437, 8492, 8496, 8497, 8459, 8464, 8466, 8499, 8472, 8475, - 128624, 8495, 8458, 8467, 8500, 8456, 128220, 983186, 129453, 128186, - 167, 8980, 129352, 127793, 128584, 8979, 130037, 130036, 130039, 130038, - 130035, 130034, 130032, 130041, 130033, 130040, 10802, 9914, 59, 117793, - 117795, 117799, 117807, 117803, 117797, 117805, 117801, 117794, 117798, - 117806, 117802, 117796, 117804, 117800, 118353, 118355, 118359, 118367, - 118383, 118415, 118399, 118375, 118407, 118391, 118363, 118379, 118411, - 118395, 118371, 118403, 118387, 118357, 118365, 118381, 118413, 118397, - 118373, 118405, 118389, 118361, 118377, 118409, 118393, 118369, 118401, - 118385, 118354, 118358, 118366, 118382, 118414, 118398, 118374, 118406, - 118390, 118362, 118378, 118410, 118394, 118370, 118402, 118386, 118356, - 118364, 118380, 118412, 118396, 118372, 118404, 118388, 118360, 118376, - 118408, 118392, 118368, 118400, 118384, 11259, 8480, 129324, 9916, 65093, - 983169, 8726, 129697, 9913, 11250, 129331, 10061, 10032, 10014, 129368, - 70086, 70085, 70101, 70100, 70103, 70102, 70099, 70098, 70096, 70105, - 70097, 70104, 70092, 70106, 70108, 70019, 70020, 70030, 70032, 70046, - 70045, 70051, 70050, 70044, 70043, 70049, 70048, 70025, 70026, 70027, - 70028, 70062, 70023, 70024, 70021, 70022, 70061, 70060, 70037, 70047, - 70042, 70052, 70063, 70064, 70065, 70056, 70055, 70039, 70038, 70036, - 70035, 70041, 70040, 70034, 70033, 70054, 70053, 70066, 70057, 70059, - 70058, 70029, 70031, 70089, 70110, 70111, 70088, 70095, 70107, 70016, - 70090, 70081, 70017, 70082, 70083, 70080, 70018, 70093, 70091, 70094, - 70067, 70077, 70079, 70070, 70071, 70072, 70073, 70074, 70075, 70068, - 70069, 70076, 70078, 70087, 70109, 70084, 129416, 127847, 66684, 66680, + 43197, 43201, 43202, 43190, 43191, 43198, 43199, 129429, 8385, 9973, + 127927, 127862, 129403, 9878, 129507, 127979, 127890, 129410, 9807, + 128756, 129691, 128437, 8492, 8496, 8497, 8459, 8464, 8466, 8499, 8472, + 8475, 128624, 8495, 8458, 8467, 8500, 8456, 128220, 983186, 129453, + 128186, 167, 8980, 129352, 127793, 128584, 8979, 130037, 130036, 130039, + 130038, 130035, 130034, 130032, 130041, 130033, 130040, 10802, 9914, 59, + 117793, 117795, 117799, 117807, 117803, 117797, 117805, 117801, 117794, + 117798, 117806, 117802, 117796, 117804, 117800, 118353, 118355, 118359, + 118367, 118383, 118415, 118399, 118375, 118407, 118391, 118363, 118379, + 118411, 118395, 118371, 118403, 118387, 118357, 118365, 118381, 118413, + 118397, 118373, 118405, 118389, 118361, 118377, 118409, 118393, 118369, + 118401, 118385, 118354, 118358, 118366, 118382, 118414, 118398, 118374, + 118406, 118390, 118362, 118378, 118410, 118394, 118370, 118402, 118386, + 118356, 118364, 118380, 118412, 118396, 118372, 118404, 118388, 118360, + 118376, 118408, 118392, 118368, 118400, 118384, 11259, 8480, 129324, + 9916, 65093, 983169, 8726, 129697, 9913, 11250, 129331, 10061, 10032, + 10014, 129368, 70086, 70085, 70101, 70100, 70103, 70102, 70099, 70098, + 70096, 70105, 70097, 70104, 70092, 70106, 70108, 70019, 70020, 70030, + 70032, 70046, 70045, 70051, 70050, 70044, 70043, 70049, 70048, 70025, + 70026, 70027, 70028, 70062, 70023, 70024, 70021, 70022, 70061, 70060, + 70037, 70047, 70042, 70052, 70063, 70064, 70065, 70056, 70055, 70039, + 70038, 70036, 70035, 70041, 70040, 70034, 70033, 70054, 70053, 70066, + 70057, 70059, 70058, 70029, 70031, 70089, 70110, 70111, 70088, 70095, + 70107, 70016, 70090, 70081, 70017, 70082, 70083, 70080, 70018, 70093, + 70091, 72550, 72551, 70078, 72545, 72544, 70094, 70070, 70071, 72547, + 72546, 72548, 72549, 70067, 70077, 70079, 70072, 70073, 70074, 70075, + 70068, 70069, 70076, 70087, 70109, 70084, 129416, 127847, 66684, 66680, 66682, 66665, 66673, 66679, 66664, 66669, 66647, 66685, 66672, 66683, 66663, 66659, 66649, 66686, 66674, 66662, 66660, 66656, 66661, 66677, 66678, 66668, 66676, 66666, 66681, 66640, 66670, 66646, 66645, 66644, 66654, 66641, 66667, 66658, 66648, 66687, 66657, 66651, 66655, 66643, 66652, 66650, 66642, 66653, 66671, 66675, 9752, 129768, 128737, 983075, - 983078, 9961, 128674, 127776, 128722, 128717, 11087, 11103, 11086, 10564, - 10976, 10985, 10984, 10974, 10975, 10983, 113825, 113824, 113826, 113827, - 127856, 129651, 9085, 129327, 129679, 128703, 128017, 129424, 129335, - 10722, 983198, 983080, 71113, 71040, 71131, 71041, 71051, 71053, 71128, - 71070, 71129, 71130, 71065, 71064, 71069, 71067, 71066, 71072, 71071, - 71046, 71047, 71048, 71049, 71082, 71044, 71045, 71042, 71043, 71058, - 71068, 71063, 71073, 71083, 71084, 71085, 71077, 71076, 71060, 71059, - 71057, 71056, 71062, 71061, 71055, 71054, 71075, 71074, 71086, 71081, - 71078, 71080, 71079, 71050, 71052, 71110, 71111, 71112, 71119, 71120, - 71127, 71126, 71125, 71123, 71124, 71117, 71118, 71116, 71121, 71115, - 71114, 71122, 71109, 71108, 71105, 71100, 71104, 71101, 71103, 71102, - 71132, 71133, 71087, 71097, 71099, 71092, 71093, 71090, 71091, 71088, - 71089, 71096, 71098, 71107, 71106, 128411, 128410, 128417, 128416, + 983078, 9961, 128674, 127776, 128722, 128717, 11087, 11103, 11086, + 129237, 10974, 10564, 129235, 10976, 10985, 10984, 10975, 10983, 113825, + 113824, 113826, 113827, 127856, 129651, 9085, 129327, 129679, 128703, + 128017, 129424, 129335, 10722, 983198, 983080, 71113, 71040, 71131, + 71041, 71051, 71053, 71128, 71070, 71129, 71130, 71065, 71064, 71069, + 71067, 71066, 71072, 71071, 71046, 71047, 71048, 71049, 71082, 71044, + 71045, 71042, 71043, 71058, 71068, 71063, 71073, 71083, 71084, 71085, + 71077, 71076, 71060, 71059, 71057, 71056, 71062, 71061, 71055, 71054, + 71075, 71074, 71086, 71081, 71078, 71080, 71079, 71050, 71052, 71110, + 71111, 71112, 71119, 71120, 71127, 71126, 71125, 71123, 71124, 71117, + 71118, 71116, 71121, 71115, 71114, 71122, 71109, 71108, 71105, 71100, + 71104, 71101, 71103, 71102, 71132, 71133, 71087, 71097, 71099, 71092, + 71093, 71090, 71091, 71088, 71089, 71096, 71098, 71107, 71106, 67923, + 67924, 67925, 67926, 67927, 67928, 67929, 67904, 67905, 67906, 67907, + 67908, 67909, 67910, 67911, 67912, 67913, 67914, 67915, 67916, 67917, + 67918, 67919, 67920, 67921, 67922, 128411, 128410, 128417, 128416, 128409, 128408, 128415, 128414, 121399, 121397, 121400, 121398, 121402, 121401, 121104, 121103, 121102, 121388, 121387, 121386, 121482, 121479, 121358, 121359, 121357, 121360, 121341, 121335, 121339, 121340, 121336, @@ -12519,18 +12014,18 @@ static const unsigned int dawg_pos_to_codepoint[] = { 121197, 121195, 121216, 121215, 121214, 121213, 121212, 121211, 121449, 121125, 121126, 121121, 121122, 121123, 121124, 121127, 121318, 121316, 121317, 121315, 121156, 121155, 121154, 121146, 121145, 121144, 121150, - 121149, 121148, 121147, 121230, 121231, 121229, 121228, 121261, 121254, + 121149, 121148, 121147, 121230, 121231, 121229, 121228, 121254, 121261, 121247, 121233, 121232, 121226, 121227, 121225, 121224, 121249, 121248, 121153, 121152, 121151, 121139, 121135, 121137, 121138, 121136, 121330, - 121329, 121128, 121236, 121262, 121255, 121235, 121234, 121237, 121240, - 121239, 121263, 121256, 121238, 121162, 121161, 121160, 121132, 121133, + 121329, 121128, 121236, 121255, 121262, 121235, 121234, 121237, 121240, + 121239, 121256, 121263, 121238, 121162, 121161, 121160, 121132, 121133, 121131, 121130, 121134, 121253, 121142, 121143, 121141, 121140, 121243, - 121242, 121241, 121246, 121245, 121244, 121270, 121269, 121268, 121264, - 121257, 121326, 121325, 121159, 121158, 121157, 121448, 121394, 121393, + 121242, 121241, 121246, 121245, 121244, 121270, 121269, 121268, 121257, + 121264, 121326, 121325, 121159, 121158, 121157, 121448, 121394, 121393, 121395, 121396, 121450, 121513, 121514, 121515, 121516, 121517, 121518, 121519, 121505, 121506, 121507, 121508, 121509, 121510, 121511, 121512, 121312, 121284, 121299, 121311, 121283, 121298, 121313, 121285, 121300, - 121267, 121260, 121252, 121251, 121266, 121259, 121250, 121265, 121258, + 121260, 121267, 121252, 121251, 121259, 121266, 121250, 121258, 121265, 121107, 121106, 121105, 121454, 121453, 121457, 121120, 121112, 121110, 121114, 121113, 121111, 121108, 121109, 121101, 121100, 121099, 121481, 121441, 121445, 121446, 121443, 121444, 121442, 121447, 121390, 121389, @@ -12560,326 +12055,346 @@ static const unsigned int dawg_pos_to_codepoint[] = { 65128, 65108, 68411, 732, 118266, 10849, 65125, 65123, 65130, 65122, 65119, 65106, 65110, 10922, 10924, 10803, 128571, 128570, 128525, 128520, 128519, 128515, 128517, 128516, 128518, 128522, 129325, 129392, 128526, - 129394, 8995, 128527, 128684, 128012, 128013, 129319, 127956, 9731, 9924, - 127938, 10052, 983077, 9917, 129510, 173, 127846, 128428, 9108, 129358, - 69453, 69452, 69454, 69456, 69447, 69449, 69446, 69448, 69455, 69451, - 69450, 69445, 69424, 69437, 69426, 69433, 69444, 69440, 69429, 69436, - 69439, 69441, 69431, 69427, 69430, 69432, 69425, 69443, 69435, 69442, - 69428, 69438, 69434, 69457, 69460, 69459, 69458, 69463, 69465, 69461, - 69462, 69464, 128618, 128619, 47, 10742, 128284, 69859, 69863, 69864, - 69846, 69847, 69857, 69849, 69842, 69843, 69844, 69845, 69854, 69856, - 69855, 69848, 69851, 69853, 69840, 69841, 69850, 69852, 69858, 69860, - 69862, 69861, 69877, 69876, 69879, 69878, 69875, 69874, 69872, 69881, - 69873, 69880, 8600, 10537, 10541, 8690, 10533, 129210, 128603, 128595, - 128611, 11112, 11128, 11018, 8664, 129110, 11010, 8601, 10538, 10534, - 129211, 128601, 128593, 128609, 11113, 11129, 11019, 8665, 129111, 11011, - 8471, 72328, 72329, 72327, 72326, 72340, 72339, 72334, 72332, 72341, - 72335, 72333, 72330, 72331, 72338, 72336, 72337, 72352, 72351, 72350, - 72297, 72296, 72302, 72311, 72301, 72323, 72285, 72284, 72288, 72298, - 72293, 72303, 72319, 72320, 72321, 72310, 72309, 72295, 72294, 72300, - 72299, 72307, 72306, 72290, 72289, 72287, 72286, 72292, 72291, 72305, - 72304, 72312, 72313, 72314, 72322, 72317, 72308, 72316, 72318, 72315, - 72272, 72349, 72348, 72347, 72346, 72324, 72343, 72325, 72342, 72345, - 72353, 72354, 72282, 72281, 72279, 72280, 72277, 72278, 72275, 72274, - 72276, 72273, 72283, 72344, 8384, 983043, 983182, 983118, 983177, 127837, - 128150, 10055, 10024, 117824, 117825, 32, 128586, 128264, 128263, 128265, - 128266, 128483, 128676, 128172, 8375, 117830, 117831, 8738, 10656, 10657, - 128375, 128376, 128467, 128466, 128026, 128166, 129759, 129525, 129348, - 128051, 127941, 129533, 13279, 117900, 13056, 13058, 13057, 13059, 13250, - 13171, 13278, 13101, 13172, 13108, 13105, 13118, 13116, 13251, 13192, - 8851, 13255, 13183, 13213, 13220, 13216, 13254, 8852, 13252, 13253, - 13170, 13092, 13175, 13177, 13176, 13093, 13094, 13256, 127376, 13207, - 13064, 13179, 13181, 13182, 13055, 13180, 13005, 13063, 13006, 117899, - 117898, 117897, 9974, 9165, 13209, 13070, 13071, 13311, 13075, 13073, - 13072, 13080, 13081, 13203, 13228, 13191, 13257, 13258, 13098, 13110, - 13113, 13121, 13122, 13119, 13107, 13106, 13109, 13259, 13169, 127488, - 13004, 13200, 13260, 13060, 13061, 8847, 8932, 8849, 13178, 13188, 13069, - 13068, 13067, 13074, 13076, 13078, 13079, 13077, 13214, 13262, 13222, - 13218, 13086, 13085, 13082, 13083, 13084, 13201, 13193, 13248, 13226, - 13189, 13199, 13261, 13208, 13263, 13240, 13246, 8977, 13266, 10957, - 13264, 13265, 13267, 13223, 13224, 13249, 13221, 13217, 13187, 13123, - 13124, 13127, 13126, 13125, 13190, 13268, 13131, 13132, 13133, 13128, - 13129, 13130, 13269, 13212, 13219, 13215, 13186, 13196, 13197, 13205, - 13211, 13234, 13238, 13244, 13239, 13241, 13245, 13247, 13202, 13270, - 13227, 13198, 13206, 13235, 13185, 13096, 13097, 13195, 13210, 13233, - 13237, 13243, 13065, 8848, 8933, 8850, 13066, 13173, 13225, 13099, 13100, - 13184, 13115, 13112, 13114, 13111, 13103, 13104, 13102, 13117, 13120, - 11216, 13273, 13174, 13194, 13271, 13272, 13274, 13232, 13236, 13242, - 13229, 13230, 13231, 13142, 13141, 10958, 13137, 13138, 13140, 13139, - 8730, 13087, 13088, 13090, 13091, 117887, 117886, 117884, 117885, 13089, - 13275, 13276, 128918, 13204, 13095, 13143, 11027, 9641, 9638, 9636, - 11029, 9706, 9703, 11026, 9705, 9639, 11028, 9640, 9637, 9704, 10720, - 13277, 13135, 13134, 13136, 13062, 127529, 127530, 127512, 127508, - 127533, 127545, 127520, 127516, 127534, 127506, 127540, 127525, 127546, - 127532, 127511, 127509, 127524, 127505, 127517, 127518, 127527, 127537, - 127504, 127528, 127535, 127519, 127515, 127513, 127543, 127542, 127526, - 127541, 127544, 127522, 127538, 127514, 127521, 127539, 127510, 127536, - 127523, 127547, 127531, 127378, 127377, 8865, 10693, 11820, 127390, - 127392, 127379, 10692, 127400, 127399, 127398, 127306, 127489, 127507, - 127490, 9919, 127397, 127280, 127281, 127282, 127283, 127284, 127285, - 127286, 127287, 127288, 127289, 127290, 127291, 127292, 127293, 127294, - 127295, 127296, 127297, 127298, 127299, 127300, 127301, 127302, 127303, - 127304, 127305, 10190, 10191, 127401, 8863, 127307, 127381, 127382, - 127396, 127383, 127310, 8862, 127388, 127393, 127402, 127395, 9949, - 10695, 10696, 127384, 127308, 127309, 127387, 127394, 127389, 8864, - 127391, 127385, 127403, 127404, 127386, 10694, 127311, 127380, 10151, - 129425, 983157, 983160, 983134, 983190, 9877, 9882, 128387, 8795, 10017, - 8902, 9770, 11242, 11243, 983175, 983133, 983181, 983176, 983042, 983044, - 128509, 128649, 129485, 127967, 128642, 127836, 11836, 129658, 129989, - 129990, 129993, 129991, 129992, 128480, 9201, 128207, 9188, 127827, - 10025, 983189, 117891, 117888, 117890, 117889, 8803, 127897, 129369, - 128723, 983170, 983045, 983103, 8333, 8332, 8328, 8330, 8334, 8331, 8325, - 8324, 8327, 8326, 8323, 8322, 8320, 8329, 8321, 10963, 10965, 10617, - 8834, 10953, 10949, 10951, 10955, 8842, 8838, 10947, 10943, 10945, 10941, - 983102, 8827, 10938, 10934, 10928, 10930, 10936, 10932, 8937, 8831, 8829, - 8881, 9139, 10763, 9138, 9737, 127774, 9925, 7098, 7073, 7074, 7075, - 7084, 7085, 7043, 983220, 7046, 7102, 7103, 7062, 7100, 7068, 7099, 7067, - 7087, 7070, 7048, 7049, 7053, 7057, 7060, 7101, 7064, 7086, 7050, 7054, - 7059, 7052, 7072, 7055, 7065, 7061, 7051, 7058, 7063, 7069, 7071, 7066, - 7056, 7044, 7047, 7045, 7367, 7366, 7365, 7364, 7363, 7361, 7362, 7360, - 7082, 7041, 7042, 7040, 7083, 7080, 7081, 7079, 7077, 7076, 7078, 7093, - 7092, 7095, 7094, 7091, 7090, 7088, 7097, 7089, 7096, 127749, 127748, - 72648, 72662, 72661, 72669, 72652, 72640, 72663, 72651, 72655, 72672, - 72646, 72667, 72666, 72653, 72657, 72645, 72665, 72649, 72644, 72658, - 72668, 72670, 72664, 72671, 72641, 72659, 72650, 72656, 72643, 72660, - 72654, 72642, 72647, 72673, 72693, 72692, 72695, 72694, 72691, 72690, - 72688, 72697, 72689, 72696, 127751, 127803, 8316, 8312, 8305, 8319, 8317, - 8314, 8318, 8315, 8309, 8308, 8311, 8310, 179, 178, 8304, 8313, 185, - 10966, 10964, 10619, 10968, 10967, 8835, 10954, 10950, 10952, 10956, - 8843, 8839, 10948, 10944, 10946, 10942, 10185, 129464, 129465, 8751, - 127940, 128671, 127843, 128629, 129442, 127946, 8275, 43027, 43026, - 43031, 43030, 43040, 43038, 43025, 43024, 43029, 43028, 43036, 43035, - 43021, 43020, 43018, 43017, 43023, 43022, 43016, 43015, 43034, 43033, - 43042, 43039, 43037, 43032, 43041, 43008, 43012, 43009, 43013, 43011, - 43048, 43049, 43050, 43051, 43052, 43019, 43014, 43010, 43047, 43043, - 43046, 43044, 43045, 9223, 9224, 9229, 9240, 9232, 9249, 9256, 9255, - 9253, 9257, 9236, 9235, 9234, 9233, 9241, 9220, 9239, 9219, 9221, 9243, - 9244, 9228, 9225, 9227, 9226, 128325, 9237, 9252, 9216, 9222, 9246, 9245, - 9247, 8527, 9230, 9231, 9217, 9218, 9242, 9254, 9238, 9248, 11159, 9007, - 983094, 983093, 128333, 1807, 1866, 1802, 1798, 1799, 1849, 1848, 1792, - 1854, 1853, 1805, 1804, 1803, 1852, 1851, 1850, 1797, 1814, 1813, 1815, - 1818, 1824, 2153, 2152, 2149, 2148, 2144, 2146, 2150, 2147, 2154, 2145, - 2151, 1825, 1830, 1837, 1839, 1838, 1831, 1834, 1827, 1869, 1870, 1871, - 1809, 1835, 1832, 1828, 1808, 1823, 1833, 1819, 1820, 1836, 1811, 1812, - 1821, 1822, 1810, 1817, 1826, 1816, 1829, 1864, 1863, 1842, 1841, 1840, - 1845, 1844, 1843, 1847, 1846, 1855, 1858, 1796, 983204, 1801, 1794, 1795, - 1800, 1793, 1862, 1861, 1860, 1859, 1865, 1856, 1857, 128137, 983184, - 128085, 129430, 983061, 127955, 917543, 917542, 917546, 917598, 917568, - 917548, 917562, 917540, 917557, 917556, 917559, 917558, 917555, 917554, - 917552, 917561, 917553, 917560, 917565, 917537, 917600, 917566, 917549, - 917569, 917570, 917571, 917572, 917573, 917574, 917575, 917576, 917577, - 917578, 917579, 917580, 917581, 917582, 917583, 917584, 917585, 917586, - 917587, 917588, 917589, 917590, 917591, 917592, 917593, 917594, 917601, - 917602, 917603, 917604, 917605, 917606, 917607, 917608, 917609, 917610, - 917611, 917612, 917613, 917614, 917615, 917616, 917617, 917618, 917619, - 917620, 917621, 917622, 917623, 917624, 917625, 917626, 917564, 917627, - 917544, 917595, 917599, 917541, 917547, 917538, 917567, 917629, 917545, - 917597, 917596, 917563, 917551, 917536, 917539, 917630, 917550, 917628, - 5888, 5919, 5893, 5896, 5898, 5895, 5892, 5905, 5891, 5902, 5899, 5897, - 5901, 5904, 5894, 5903, 5900, 5889, 5890, 5909, 5908, 5906, 5907, 5989, - 5992, 5994, 5991, 5988, 5987, 5998, 5995, 5993, 6000, 5990, 5999, 5996, - 5984, 5985, 5986, 6002, 6003, 6499, 6508, 6509, 6507, 6501, 6502, 6512, - 6513, 6514, 6515, 6516, 6497, 6483, 6487, 6486, 6482, 6498, 6505, 6504, - 6496, 6480, 6490, 6489, 6503, 6506, 6492, 6494, 6488, 6491, 6495, 6484, - 6493, 6481, 6485, 6500, 6745, 6746, 6743, 6747, 6742, 6741, 6748, 6749, - 6750, 6783, 6789, 6788, 6791, 6790, 6787, 6786, 6784, 6793, 6785, 6792, - 6805, 6804, 6807, 6806, 6803, 6802, 6800, 6809, 6801, 6808, 6740, 6689, - 6690, 6688, 6702, 6726, 6727, 6728, 6696, 6695, 6713, 6712, 6707, 6706, - 6714, 6729, 6720, 6693, 6692, 6691, 6704, 6699, 6697, 6717, 6715, 6709, - 6708, 6716, 6732, 6698, 6719, 6723, 6739, 6724, 6730, 6721, 6705, 6701, - 6722, 6735, 6736, 6733, 6734, 6694, 6700, 6710, 6738, 6737, 6711, 6703, - 6718, 6725, 6731, 6828, 6820, 6775, 6776, 6777, 6780, 6824, 6825, 6819, - 6772, 6744, 6823, 6779, 6778, 6822, 6826, 6827, 6818, 6752, 6816, 6817, - 6821, 6773, 6774, 6829, 6753, 6755, 6767, 6769, 6754, 6763, 6764, 6771, - 6768, 6765, 6756, 6770, 6761, 6762, 6760, 6759, 6757, 6758, 6766, 43653, - 43651, 43649, 43661, 43659, 43679, 43677, 43671, 43669, 43657, 43665, - 43673, 43675, 43667, 43681, 43655, 43693, 43689, 43683, 43687, 43663, - 43691, 43685, 43695, 43652, 43650, 43648, 43660, 43658, 43678, 43676, - 43670, 43668, 43656, 43664, 43672, 43674, 43666, 43680, 43654, 43692, - 43688, 43682, 43686, 43662, 43690, 43684, 43694, 43696, 43703, 43743, - 43739, 43740, 43742, 43741, 43712, 43713, 43714, 43711, 43707, 43697, - 43710, 43709, 43708, 43700, 43699, 43705, 43706, 43698, 43704, 43701, - 43702, 71353, 71296, 71352, 71297, 71303, 71305, 71319, 71318, 71324, - 71323, 71338, 71332, 71317, 71316, 71322, 71321, 71300, 71301, 71298, - 71299, 71310, 71320, 71315, 71325, 71329, 71328, 71312, 71311, 71309, - 71308, 71314, 71313, 71307, 71306, 71327, 71326, 71335, 71336, 71337, - 71333, 71330, 71334, 71331, 71302, 71304, 71351, 71339, 71350, 71340, - 71341, 71347, 71349, 71344, 71345, 71342, 71343, 71346, 71348, 71365, - 71364, 71367, 71366, 71363, 71362, 71360, 71369, 71361, 71368, 129377, - 119672, 119671, 3064, 3031, 73707, 983662, 983685, 983674, 983677, - 983676, 983669, 983667, 983679, 983663, 983665, 983668, 983666, 983683, - 983681, 983682, 983673, 983678, 983664, 983684, 983680, 983671, 983670, - 983675, 983672, 73706, 3063, 73701, 3062, 3059, 3051, 3050, 3053, 3052, - 3049, 3048, 3046, 3055, 3047, 3054, 73700, 73666, 73676, 73668, 73679, - 73681, 73682, 73665, 73673, 73674, 73667, 73664, 73669, 73672, 73675, - 73680, 73670, 73678, 73671, 73677, 73683, 73684, 73710, 2985, 2979, 2969, - 2974, 2984, 2975, 2980, 2949, 2950, 2960, 2964, 2996, 2995, 2994, 2993, - 2992, 2953, 2954, 2962, 2963, 2951, 2952, 2998, 2999, 3000, 2958, 2959, - 2970, 3001, 2972, 2965, 2990, 2986, 2997, 2991, 73702, 3057, 3058, 3056, - 3066, 73727, 3065, 73703, 73687, 2946, 73686, 73698, 73690, 73693, 73692, - 73712, 73689, 73688, 73691, 73697, 73694, 73695, 73696, 73713, 73699, - 3021, 2947, 73685, 73708, 73711, 983939, 983940, 983947, 983950, 983943, - 983944, 983948, 983949, 983941, 983942, 983945, 983946, 983686, 983693, - 983696, 983689, 983690, 983694, 983695, 983687, 983688, 983691, 983692, - 983840, 983847, 983850, 983843, 983844, 983848, 983849, 983841, 983842, - 983845, 983846, 983851, 983858, 983861, 983854, 983855, 983859, 983860, - 983852, 983853, 983856, 983857, 983818, 983825, 983828, 983821, 983822, - 983826, 983827, 983819, 983820, 983823, 983824, 983873, 983880, 983883, - 983876, 983877, 983881, 983882, 983874, 983875, 983878, 983879, 983741, - 983748, 983751, 983744, 983745, 983749, 983750, 983742, 983743, 983746, - 983747, 983697, 983704, 983707, 983700, 983701, 983705, 983706, 983698, - 983699, 983702, 983703, 983719, 983726, 983729, 983722, 983723, 983727, - 983728, 983720, 983721, 983724, 983725, 983763, 983770, 983773, 983766, - 983767, 983771, 983772, 983764, 983765, 983768, 983769, 983862, 983869, - 983872, 983865, 983866, 983870, 983871, 983863, 983864, 983867, 983868, - 983807, 983814, 983817, 983810, 983811, 983815, 983816, 983808, 983809, - 983812, 983813, 983895, 983902, 983905, 983898, 983899, 983903, 983904, - 983951, 983896, 983897, 983900, 983901, 983906, 983913, 983916, 983909, - 983910, 983914, 983915, 983907, 983908, 983911, 983912, 983917, 983924, - 983927, 983920, 983921, 983925, 983926, 983918, 983919, 983922, 983923, - 983730, 983737, 983740, 983733, 983734, 983738, 983739, 983731, 983732, - 983735, 983736, 983752, 983759, 983762, 983755, 983756, 983760, 983761, - 983753, 983754, 983757, 983758, 983708, 983715, 983718, 983711, 983712, - 983716, 983717, 983709, 983710, 983713, 983714, 983928, 983935, 983938, - 983931, 983932, 983936, 983937, 983929, 983930, 983933, 983934, 983884, - 983891, 983894, 983887, 983888, 983892, 983893, 983885, 983886, 983889, - 983890, 983785, 983792, 983795, 983788, 983789, 983793, 983794, 983786, - 983787, 983790, 983791, 983774, 983781, 983784, 983777, 983778, 983782, - 983783, 983775, 983776, 983779, 983780, 983829, 983836, 983839, 983832, - 983833, 983837, 983838, 983830, 983831, 983834, 983835, 983796, 983803, - 983806, 983799, 983800, 983804, 983805, 983797, 983798, 983801, 983802, - 73709, 73704, 73705, 3006, 3016, 3020, 3009, 3010, 3018, 3019, 3007, - 3008, 3014, 3015, 3061, 3060, 3024, 129748, 127883, 127818, 92809, 92810, - 92811, 92808, 92789, 92790, 92791, 92788, 92816, 92859, 92856, 92847, - 92845, 92817, 92846, 92843, 92829, 92830, 92831, 92828, 92835, 92851, - 92840, 92844, 92818, 92819, 92852, 92836, 92825, 92826, 92827, 92824, - 92813, 92814, 92815, 92812, 92820, 92822, 92823, 92821, 92805, 92806, - 92807, 92804, 92797, 92798, 92799, 92796, 92801, 92802, 92803, 92800, - 92785, 92786, 92787, 92784, 92793, 92794, 92795, 92792, 92857, 92854, - 92848, 92861, 92853, 92860, 92849, 92855, 92834, 92833, 92832, 92841, - 92839, 92842, 92850, 92838, 92858, 92837, 92862, 92869, 92868, 92871, - 92870, 92867, 92866, 92864, 92873, 92865, 92872, 100352, 100353, 100354, - 100355, 100356, 100357, 100358, 100359, 100360, 100361, 100362, 100363, - 100364, 100365, 100366, 100367, 100368, 100369, 100370, 100371, 100372, - 100373, 100374, 100375, 100376, 100377, 100378, 100379, 100380, 100381, - 100382, 100383, 100384, 100385, 100386, 100387, 100388, 100389, 100390, - 100391, 100392, 100393, 100394, 100395, 100396, 100397, 100398, 100399, - 100400, 100401, 100402, 100403, 100404, 100405, 100406, 100407, 100408, - 100409, 100410, 100411, 100412, 100413, 100414, 100415, 100416, 100417, - 100418, 100419, 100420, 100421, 100422, 100423, 100424, 100425, 100426, - 100427, 100428, 100429, 100430, 100431, 100432, 100433, 100434, 100435, - 100436, 100437, 100438, 100439, 100440, 100441, 100442, 100443, 100444, - 100445, 100446, 100447, 100448, 100449, 100450, 100451, 100452, 100453, - 100454, 100455, 100456, 100457, 100458, 100459, 100460, 100461, 100462, - 100463, 100464, 100465, 100466, 100467, 100468, 100469, 100470, 100471, - 100472, 100473, 100474, 100475, 100476, 100477, 100478, 100479, 100480, - 100481, 100482, 100483, 100484, 100485, 100486, 100487, 100488, 100489, - 100490, 100491, 100492, 100493, 100494, 100495, 100496, 100497, 100498, - 100499, 100500, 100501, 100502, 100503, 100504, 100505, 100506, 100507, - 100508, 100509, 100510, 100511, 100512, 100513, 100514, 100515, 100516, - 100517, 100518, 100519, 100520, 100521, 100522, 100523, 100524, 100525, - 100526, 100527, 100528, 100529, 100530, 100531, 100532, 100533, 100534, - 100535, 100536, 100537, 100538, 100539, 100540, 100541, 100542, 100543, - 100544, 100545, 100546, 100547, 100548, 100549, 100550, 100551, 100552, - 100553, 100554, 100555, 100556, 100557, 100558, 100559, 100560, 100561, - 100562, 100563, 100564, 100565, 100566, 100567, 100568, 100569, 100570, - 100571, 100572, 100573, 100574, 100575, 100576, 100577, 100578, 100579, - 100580, 100581, 100582, 100583, 100584, 100585, 100586, 100587, 100588, - 100589, 100590, 100591, 100592, 100593, 100594, 100595, 100596, 100597, - 100598, 100599, 100600, 100601, 100602, 100603, 100604, 100605, 100606, - 100607, 100608, 100609, 100610, 100611, 100612, 100613, 100614, 100615, - 100616, 100617, 100618, 100619, 100620, 100621, 100622, 100623, 100624, - 100625, 100626, 100627, 100628, 100629, 100630, 100631, 100632, 100633, - 100634, 100635, 100636, 100637, 100638, 100639, 100640, 100641, 100642, - 100643, 100644, 100645, 100646, 100647, 100648, 100649, 100650, 100651, - 100652, 100653, 100654, 100655, 100656, 100657, 100658, 100659, 100660, - 100661, 100662, 100663, 100664, 100665, 100666, 100667, 100668, 100669, - 100670, 100671, 100672, 100673, 100674, 100675, 100676, 100677, 100678, - 100679, 100680, 100681, 100682, 100683, 100684, 100685, 100686, 100687, - 100688, 100689, 100690, 100691, 100692, 100693, 100694, 100695, 100696, - 100697, 100698, 100699, 100700, 100701, 100702, 100703, 100704, 100705, - 100706, 100707, 100708, 100709, 100710, 100711, 100712, 100713, 100714, - 100715, 100716, 100717, 100718, 100719, 100720, 100721, 100722, 100723, - 100724, 100725, 100726, 100727, 100728, 100729, 100730, 100731, 100732, - 100733, 100734, 100735, 100736, 100737, 100738, 100739, 100740, 100741, - 100742, 100743, 100744, 100745, 100746, 100747, 100748, 100749, 100750, - 100751, 100752, 100753, 100754, 100755, 100756, 100757, 100758, 100759, - 100760, 100761, 100762, 100763, 100764, 100765, 100766, 100767, 100768, - 100769, 100770, 100771, 100772, 100773, 100774, 100775, 100776, 100777, - 100778, 100779, 100780, 100781, 100782, 100783, 100784, 100785, 100786, - 100787, 100788, 100789, 100790, 100791, 100792, 100793, 100794, 100795, - 100796, 100797, 100798, 100799, 100800, 100801, 100802, 100803, 100804, - 100805, 100806, 100807, 100808, 100809, 100810, 100811, 100812, 100813, - 100814, 100815, 100816, 100817, 100818, 100819, 100820, 100821, 100822, - 100823, 100824, 100825, 100826, 100827, 100828, 100829, 100830, 100831, - 100832, 100833, 100834, 100835, 100836, 100837, 100838, 100839, 100840, - 100841, 100842, 100843, 100844, 100845, 100846, 100847, 100848, 100849, - 100850, 100851, 100852, 100853, 100854, 100855, 100856, 100857, 100858, - 100859, 100860, 100861, 100862, 100863, 100864, 100865, 100866, 100867, - 100868, 100869, 100870, 100871, 100872, 100873, 100874, 100875, 100876, - 100877, 100878, 100879, 100880, 100881, 100882, 100883, 100884, 100885, - 100886, 100887, 100888, 100889, 100890, 100891, 100892, 100893, 100894, - 100895, 100896, 100897, 100898, 100899, 100900, 100901, 100902, 100903, - 100904, 100905, 100906, 100907, 100908, 100909, 100910, 100911, 100912, - 100913, 100914, 100915, 100916, 100917, 100918, 100919, 100920, 100921, - 100922, 100923, 100924, 100925, 100926, 100927, 100928, 100929, 100930, - 100931, 100932, 100933, 100934, 100935, 100936, 100937, 100938, 100939, - 100940, 100941, 100942, 100943, 100944, 100945, 100946, 100947, 100948, - 100949, 100950, 100951, 100952, 100953, 100954, 100955, 100956, 100957, - 100958, 100959, 100960, 100961, 100962, 100963, 100964, 100965, 100966, - 100967, 100968, 100969, 100970, 100971, 100972, 100973, 100974, 100975, - 100976, 100977, 100978, 100979, 100980, 100981, 100982, 100983, 100984, - 100985, 100986, 100987, 100988, 100989, 100990, 100991, 100992, 100993, - 100994, 100995, 100996, 100997, 100998, 100999, 101000, 101001, 101002, - 101003, 101004, 101005, 101006, 101007, 101008, 101009, 101010, 101011, - 101012, 101013, 101014, 101015, 101016, 101017, 101018, 101019, 101020, - 101021, 101022, 101023, 101024, 101025, 101026, 101027, 101028, 101029, - 101030, 101031, 101032, 101033, 101034, 101035, 101036, 101037, 101038, - 101039, 101040, 101041, 101042, 101043, 101044, 101045, 101046, 101047, - 101048, 101049, 101050, 101051, 101052, 101053, 101054, 101055, 101056, - 101057, 101058, 101059, 101060, 101061, 101062, 101063, 101064, 101065, - 101066, 101067, 101068, 101069, 101070, 101071, 101072, 101073, 101074, - 101075, 101076, 101077, 101078, 101079, 101080, 101081, 101082, 101083, - 101084, 101085, 101086, 101087, 101088, 101089, 101090, 101091, 101092, - 101093, 101094, 101095, 101096, 101097, 101098, 101099, 101100, 101101, - 101102, 101103, 101104, 101105, 101106, 101107, 101108, 101109, 101110, - 101111, 101112, 101113, 101114, 101115, 101116, 101117, 101118, 101119, - 94176, 128429, 9991, 9801, 127790, 128661, 127861, 128198, 10043, 10170, - 129750, 129528, 128222, 128380, 8981, 9990, 8481, 128384, 128301, 128250, - 3158, 3195, 3198, 3194, 3197, 3193, 3196, 3192, 3106, 3105, 3111, 3161, - 3110, 3112, 3165, 3097, 3107, 3102, 3162, 3121, 3120, 3104, 3103, 3109, - 3160, 3108, 3077, 3078, 3088, 3092, 3124, 3123, 3122, 3083, 3168, 3084, - 3169, 3125, 3081, 3082, 3090, 3091, 3079, 3080, 3126, 3127, 3128, 3117, - 3116, 3099, 3098, 3096, 3095, 3101, 3100, 3094, 3093, 3115, 3114, 3086, - 3087, 3129, 3118, 3119, 3157, 3076, 3072, 3073, 3191, 3199, 3132, 3133, - 3074, 3149, 3075, 3134, 3144, 3148, 3137, 3138, 3139, 3140, 3170, 3171, - 3146, 3147, 3135, 3136, 3142, 3143, 3179, 3178, 3181, 3180, 3177, 3176, - 3174, 3183, 3175, 3182, 127934, 8376, 9978, 129514, 119617, 119564, - 119577, 119633, 119587, 119566, 119561, 119585, 119613, 119590, 119631, - 119634, 119630, 119608, 119582, 119563, 119573, 119558, 119624, 119567, - 119623, 119586, 119636, 119612, 119625, 119568, 119584, 119619, 119618, - 119583, 119603, 119600, 119626, 119610, 119580, 119576, 119638, 119559, - 119595, 119632, 119606, 119592, 119615, 119599, 119602, 119614, 119629, - 119574, 119569, 119570, 119622, 119562, 119591, 119637, 119597, 119589, - 119616, 119609, 119560, 119635, 119565, 119604, 119588, 119571, 119594, - 119578, 119596, 119579, 119598, 119572, 119605, 119627, 119621, 119628, - 119601, 119593, 119607, 119575, 119620, 119611, 119581, 1959, 1958, 1964, - 1961, 1965, 1927, 1954, 1951, 1937, 1931, 1930, 1956, 1934, 1935, 1955, - 1945, 1920, 1926, 1946, 1933, 1925, 1929, 1943, 1942, 1941, 1922, 1969, - 1950, 1949, 1921, 1936, 1939, 1932, 1947, 1944, 1952, 1928, 1957, 1938, - 1948, 1953, 1924, 1923, 1940, 1967, 1966, 1963, 1960, 1962, 1968, 3610, - 3592, 3594, 3593, 3596, 3598, 3604, 3613, 3615, 3663, 3630, 3627, 3588, - 3587, 3589, 3586, 3590, 3675, 3585, 3628, 3621, 3653, 3622, 3659, 3633, - 3657, 3658, 3656, 3655, 3654, 3617, 3674, 3591, 3661, 3603, 3609, 3631, - 3642, 3612, 3614, 3616, 3611, 3619, 3620, 3632, 3652, 3651, 3634, 3649, - 3635, 3640, 3638, 3639, 3641, 3636, 3637, 3648, 3650, 3625, 3624, 3626, - 3595, 3660, 3601, 3602, 3607, 3600, 3608, 3606, 3605, 3599, 3623, 3662, - 3597, 3618, 3629, 3647, 3669, 3668, 3671, 3670, 3667, 3666, 3664, 3673, - 3665, 3672, 8708, 8707, 8756, 127777, 10727, 128936, 8201, 128929, + 129394, 8995, 128527, 128684, 128013, 118010, 128012, 129319, 127956, + 9731, 9924, 127938, 10052, 983077, 9917, 129510, 173, 127846, 128428, + 9108, 129358, 69453, 69452, 69454, 69456, 69447, 69449, 69446, 69448, + 69455, 69451, 69450, 69445, 69424, 69437, 69426, 69433, 69444, 69440, + 69429, 69436, 69439, 69441, 69431, 69427, 69430, 69432, 69425, 69443, + 69435, 69442, 69428, 69438, 69434, 69457, 69460, 69459, 69458, 69463, + 69465, 69461, 69462, 69464, 128618, 128619, 47, 10742, 128284, 69859, + 69863, 69864, 69846, 69847, 69857, 69849, 69842, 69843, 69844, 69845, + 69854, 69856, 69855, 69848, 69851, 69853, 69840, 69841, 69850, 69852, + 69858, 69860, 69862, 69861, 69877, 69876, 69879, 69878, 69875, 69874, + 69872, 69881, 69873, 69880, 8600, 10537, 10541, 8690, 10533, 129210, + 128603, 128595, 128611, 11112, 11128, 11018, 8664, 129110, 11010, 8601, + 10538, 10534, 129211, 128601, 128593, 128609, 11113, 11129, 11019, 8665, + 129111, 11011, 8471, 72328, 72329, 72327, 72326, 72340, 72339, 72334, + 72332, 72341, 72335, 72333, 72330, 72331, 72338, 72336, 72337, 72352, + 72351, 72350, 72297, 72296, 72302, 72311, 72301, 72323, 72285, 72284, + 72288, 72298, 72293, 72303, 72319, 72320, 72321, 72310, 72309, 72295, + 72294, 72300, 72299, 72307, 72306, 72290, 72289, 72287, 72286, 72292, + 72291, 72305, 72304, 72312, 72313, 72314, 72322, 72317, 72308, 72316, + 72318, 72315, 72272, 72349, 72348, 72347, 72346, 72324, 72343, 72325, + 72342, 72345, 72353, 72354, 72282, 72281, 72279, 72280, 72277, 72278, + 72275, 72274, 72276, 72273, 72283, 72344, 8384, 983043, 983182, 983118, + 983177, 127837, 128150, 10055, 10024, 117824, 117825, 32, 128586, 128264, + 128263, 128265, 128266, 128483, 128676, 128172, 8375, 117830, 117831, + 8738, 10656, 10657, 128375, 128376, 128467, 128466, 128026, 128166, + 129759, 129525, 129348, 128051, 127941, 129533, 13279, 117900, 13056, + 13058, 13057, 13059, 13250, 13171, 13278, 13101, 13172, 13108, 13105, + 13118, 13116, 13251, 13192, 8851, 13255, 13183, 13213, 13220, 13216, + 13254, 8852, 13252, 13253, 13170, 13092, 13175, 13177, 13176, 13093, + 13094, 13256, 127376, 13207, 13064, 13179, 13181, 13182, 13055, 13180, + 13005, 13063, 13006, 117899, 117898, 117897, 9974, 9165, 13209, 13070, + 13071, 13311, 13075, 13073, 13072, 13080, 13081, 13203, 13228, 13191, + 13257, 13258, 13098, 13110, 13113, 13121, 13122, 13119, 13107, 13106, + 13109, 13259, 13169, 127488, 13004, 13200, 13260, 13060, 13061, 8847, + 8932, 8849, 13178, 13188, 13068, 13069, 13067, 13076, 13078, 13079, + 13077, 13214, 13262, 13222, 13218, 13086, 13085, 13082, 13083, 13084, + 13074, 13201, 13193, 13248, 13226, 13189, 13199, 13261, 13208, 13263, + 13240, 13246, 8977, 13266, 10957, 13264, 13265, 13267, 13223, 13224, + 13249, 13221, 13217, 13187, 13123, 13124, 13127, 13126, 13125, 13190, + 13268, 13131, 13132, 13133, 13128, 13129, 13130, 13269, 13212, 13219, + 13215, 13186, 13196, 13197, 13205, 13211, 13234, 13238, 13244, 13239, + 13241, 13245, 13247, 13202, 13270, 13227, 13198, 13206, 13235, 13185, + 13096, 13097, 13195, 13210, 13233, 13237, 13243, 13065, 8848, 8933, 8850, + 13066, 13173, 13225, 13099, 13100, 13184, 13115, 13112, 13114, 13111, + 13103, 13104, 13102, 13117, 13120, 11216, 13273, 13174, 13194, 13271, + 13272, 13274, 13232, 13236, 13242, 13229, 13230, 13231, 13142, 13141, + 10958, 13137, 13138, 13140, 13139, 8730, 13087, 13088, 13090, 13091, + 117887, 117886, 117884, 117885, 13089, 13275, 13276, 128918, 13204, + 13095, 13143, 11027, 9641, 9638, 9636, 11029, 9706, 9703, 11026, 9705, + 9639, 11028, 9640, 9637, 9704, 10720, 13277, 13135, 13134, 13136, 13062, + 127529, 127530, 127512, 127508, 127533, 127545, 127520, 127516, 127534, + 127506, 127540, 127525, 127546, 127532, 127511, 127509, 127524, 127505, + 127517, 127518, 127527, 127537, 127504, 127528, 127535, 127519, 127515, + 127513, 127543, 127542, 127526, 127541, 127544, 127522, 127538, 127514, + 127521, 127539, 127510, 127536, 127523, 127547, 127531, 127378, 127377, + 8865, 10693, 11820, 127390, 127392, 127379, 10692, 127400, 127399, + 127398, 127306, 127489, 127507, 127490, 9919, 127397, 127280, 127281, + 127282, 127283, 127284, 127285, 127286, 127287, 127288, 127289, 127290, + 127291, 127292, 127293, 127294, 127295, 127296, 127297, 127298, 127299, + 127300, 127301, 127302, 127303, 127304, 127305, 10190, 10191, 127401, + 8863, 127307, 127381, 127382, 127396, 127383, 127310, 8862, 127388, + 127393, 127402, 127395, 9949, 10695, 10696, 127384, 127308, 127309, + 127387, 127394, 127389, 8864, 127391, 127385, 127403, 127404, 127386, + 10694, 127311, 127380, 10151, 129425, 983157, 983160, 983134, 983190, + 9877, 9882, 128387, 8795, 10017, 8902, 9770, 11242, 11243, 983175, + 983133, 983181, 983176, 983042, 983044, 128509, 128649, 129485, 127967, + 128642, 127836, 11836, 129658, 129989, 129990, 129993, 129991, 129992, + 128480, 9201, 128207, 9188, 127827, 118463, 10025, 983189, 117891, + 117888, 117890, 117889, 8803, 127897, 129369, 128723, 983170, 983045, + 983103, 8333, 8332, 8328, 8330, 8334, 8331, 8325, 8324, 8327, 8326, 8323, + 8322, 8320, 8329, 8321, 10963, 10965, 10617, 8834, 10953, 10949, 10951, + 10955, 8842, 8838, 10947, 10945, 10943, 10941, 983102, 8827, 10938, + 10934, 10928, 10930, 10936, 10932, 8937, 8831, 8829, 8881, 9139, 10763, + 9138, 9737, 127774, 9925, 7098, 7073, 7074, 7075, 7084, 7085, 7043, + 983220, 7046, 7102, 7103, 7062, 7100, 7068, 7099, 7067, 7087, 7070, 7048, + 7049, 7053, 7057, 7060, 7101, 7064, 7086, 7050, 7054, 7059, 7052, 7072, + 7055, 7065, 7061, 7051, 7058, 7063, 7069, 7071, 7066, 7056, 7044, 7047, + 7045, 7367, 7366, 7365, 7364, 7363, 7361, 7362, 7360, 7082, 7041, 7042, + 7040, 7083, 7080, 7081, 7079, 7077, 7076, 7078, 7093, 7092, 7095, 7094, + 7091, 7090, 7088, 7097, 7089, 7096, 127749, 127748, 72648, 72662, 72661, + 72669, 72652, 72640, 72663, 72651, 72655, 72672, 72646, 72667, 72666, + 72653, 72657, 72645, 72665, 72649, 72644, 72658, 72668, 72670, 72664, + 72671, 72641, 72659, 72656, 72650, 72643, 72660, 72654, 72642, 72647, + 72673, 72693, 72692, 72695, 72694, 72691, 72690, 72688, 72697, 72689, + 72696, 127751, 127803, 8316, 8312, 8305, 8319, 8317, 8314, 8318, 8315, + 8309, 8308, 8311, 8310, 179, 178, 8304, 8313, 185, 10966, 10964, 10619, + 10968, 10967, 8835, 10954, 10950, 10952, 10956, 8843, 8839, 10948, 10946, + 10944, 10942, 10185, 129464, 129465, 8751, 127940, 128671, 127843, + 128629, 129442, 127946, 8275, 43027, 43026, 43031, 43030, 43040, 43038, + 43025, 43024, 43029, 43028, 43036, 43035, 43021, 43020, 43018, 43017, + 43023, 43022, 43016, 43015, 43034, 43033, 43042, 43039, 43037, 43032, + 43041, 43008, 43012, 43009, 43013, 43011, 43048, 43049, 43050, 43051, + 43052, 43019, 43014, 43010, 43047, 43043, 43046, 43044, 43045, 9223, + 9224, 9229, 9240, 9232, 9249, 9256, 9255, 9253, 9257, 9236, 9235, 9234, + 9233, 9241, 9220, 9239, 9219, 9221, 9243, 9244, 9228, 9225, 9227, 9226, + 128325, 9237, 9252, 9216, 9222, 9246, 9245, 9247, 8527, 9230, 9231, 9217, + 9218, 9242, 9254, 9238, 9248, 11159, 9007, 983094, 983093, 128333, 1807, + 1866, 1802, 1798, 1799, 1849, 1848, 1792, 1854, 1853, 1805, 1804, 1803, + 1852, 1851, 1850, 1797, 1814, 1813, 1815, 1818, 1824, 2153, 2152, 2149, + 2148, 2144, 2146, 2150, 2147, 2154, 2145, 2151, 1825, 1830, 1837, 1839, + 1838, 1831, 1834, 1827, 1869, 1870, 1871, 1809, 1835, 1832, 1828, 1808, + 1823, 1833, 1819, 1820, 1836, 1811, 1812, 1821, 1822, 1810, 1817, 1826, + 1816, 1829, 1864, 1863, 1842, 1841, 1840, 1845, 1844, 1843, 1847, 1846, + 1855, 1858, 1796, 983204, 1801, 1794, 1795, 1800, 1793, 1862, 1861, 1860, + 1859, 1865, 1856, 1857, 128137, 983184, 128085, 129430, 983061, 127955, + 917543, 917542, 917546, 917598, 917568, 917548, 917562, 917540, 917557, + 917556, 917559, 917558, 917555, 917554, 917552, 917561, 917553, 917560, + 917565, 917537, 917600, 917566, 917549, 917569, 917570, 917571, 917572, + 917573, 917574, 917575, 917576, 917577, 917578, 917579, 917580, 917581, + 917582, 917583, 917584, 917585, 917586, 917587, 917588, 917589, 917590, + 917591, 917592, 917593, 917594, 917601, 917602, 917603, 917604, 917605, + 917606, 917607, 917608, 917609, 917610, 917611, 917612, 917613, 917614, + 917615, 917616, 917617, 917618, 917619, 917620, 917621, 917622, 917623, + 917624, 917625, 917626, 917564, 917627, 917544, 917595, 917599, 917541, + 917547, 917538, 917567, 917629, 917545, 917597, 917596, 917563, 917551, + 917536, 917539, 917630, 917550, 917628, 5888, 5919, 5893, 5896, 5898, + 5895, 5892, 5905, 5891, 5902, 5899, 5897, 5901, 5904, 5894, 5903, 5900, + 5889, 5890, 5909, 5908, 5906, 5907, 5989, 5992, 5994, 5991, 5988, 5987, + 5998, 5995, 5993, 6000, 5990, 5999, 5996, 5984, 5985, 5986, 6002, 6003, + 6499, 6508, 6509, 6507, 6501, 6502, 6512, 6513, 6514, 6515, 6516, 6497, + 6483, 6487, 6486, 6482, 6498, 6505, 6504, 6496, 6480, 6490, 6489, 6503, + 6506, 6492, 6494, 6488, 6491, 6495, 6484, 6493, 6481, 6485, 6500, 6745, + 6746, 6743, 6747, 6742, 6741, 6748, 6749, 6750, 6783, 6789, 6788, 6791, + 6790, 6787, 6786, 6784, 6793, 6785, 6792, 6805, 6804, 6807, 6806, 6803, + 6802, 6800, 6809, 6801, 6808, 6740, 6689, 6690, 6688, 6702, 6726, 6727, + 6728, 6696, 6695, 6713, 6712, 6707, 6706, 6714, 6729, 6720, 6693, 6692, + 6691, 6704, 6699, 6697, 6717, 6715, 6709, 6708, 6716, 6732, 6698, 6719, + 6723, 6739, 6724, 6730, 6721, 6705, 6701, 6722, 6735, 6736, 6733, 6734, + 6694, 6700, 6710, 6738, 6737, 6711, 6703, 6718, 6725, 6731, 6828, 6820, + 6775, 6776, 6777, 6780, 6824, 6825, 6819, 6772, 6744, 6823, 6779, 6778, + 6822, 6826, 6827, 6818, 6752, 6816, 6817, 6821, 6773, 6774, 6829, 6753, + 6755, 6767, 6769, 6754, 6763, 6764, 6771, 6768, 6765, 6756, 6770, 6761, + 6762, 6760, 6759, 6757, 6758, 6766, 43653, 43651, 43649, 43661, 43659, + 43679, 43677, 43671, 43669, 43657, 43665, 43673, 43675, 43667, 43681, + 43655, 43693, 43689, 43683, 43687, 43663, 43691, 43685, 43695, 43652, + 43650, 43648, 43660, 43658, 43678, 43676, 43670, 43668, 43656, 43664, + 43672, 43674, 43666, 43680, 43654, 43692, 43688, 43682, 43686, 43662, + 43690, 43684, 43694, 43696, 43703, 43743, 43739, 43740, 43742, 43741, + 43712, 43713, 43714, 43711, 43707, 43697, 43710, 43709, 43708, 43700, + 43699, 43705, 43706, 43698, 43704, 43701, 43702, 124653, 124640, 124645, + 124658, 124657, 124656, 124660, 124659, 124632, 124610, 124637, 124608, + 124617, 124628, 124634, 124625, 124620, 124615, 124611, 124638, 124609, + 124618, 124629, 124635, 124626, 124621, 124616, 124613, 124623, 124644, + 124642, 124650, 124651, 124627, 124622, 124641, 124649, 124647, 124652, + 124624, 124614, 124619, 124612, 124630, 124636, 124633, 124631, 124648, + 124655, 124646, 124654, 124643, 124661, 124670, 124671, 71353, 71296, + 71352, 71297, 71303, 71305, 71319, 71318, 71324, 71323, 71338, 71332, + 71317, 71316, 71322, 71321, 71300, 71301, 71298, 71299, 71310, 71320, + 71315, 71325, 71329, 71328, 71312, 71311, 71309, 71308, 71314, 71313, + 71307, 71306, 71327, 71326, 71335, 71336, 71337, 71333, 71330, 71334, + 71331, 71302, 71304, 71351, 71339, 71350, 71340, 71341, 71347, 71349, + 71344, 71345, 71342, 71343, 71346, 71348, 71365, 71364, 71367, 71366, + 71363, 71362, 71360, 71369, 71361, 71368, 129377, 119672, 119671, 3064, + 3031, 73707, 983662, 983685, 983674, 983677, 983676, 983669, 983667, + 983679, 983663, 983665, 983668, 983666, 983683, 983681, 983682, 983673, + 983678, 983664, 983684, 983680, 983671, 983670, 983675, 983672, 73706, + 3063, 73701, 3062, 3059, 3051, 3050, 3053, 3052, 3049, 3048, 3046, 3055, + 3047, 3054, 73700, 73666, 73676, 73668, 73679, 73681, 73682, 73665, + 73673, 73674, 73667, 73664, 73669, 73672, 73675, 73680, 73670, 73678, + 73671, 73677, 73683, 73684, 73710, 2985, 2979, 2969, 2974, 2984, 2975, + 2980, 2949, 2950, 2960, 2964, 2996, 2995, 2994, 2993, 2992, 2953, 2954, + 2962, 2963, 2951, 2952, 2998, 2999, 3000, 2958, 2959, 2970, 3001, 2972, + 2965, 2990, 2986, 2997, 2991, 73702, 3057, 3058, 3056, 3066, 73727, 3065, + 73703, 73687, 2946, 73686, 73698, 73690, 73693, 73692, 73712, 73689, + 73688, 73691, 73697, 73694, 73695, 73696, 73713, 73699, 3021, 2947, + 73685, 73708, 73711, 983939, 983940, 983947, 983950, 983943, 983944, + 983948, 983949, 983941, 983942, 983945, 983946, 983686, 983693, 983696, + 983689, 983690, 983694, 983695, 983687, 983688, 983691, 983692, 983840, + 983847, 983850, 983843, 983844, 983848, 983849, 983841, 983842, 983845, + 983846, 983851, 983858, 983861, 983854, 983855, 983859, 983860, 983852, + 983853, 983856, 983857, 983818, 983825, 983828, 983821, 983822, 983826, + 983827, 983819, 983820, 983823, 983824, 983873, 983880, 983883, 983876, + 983877, 983881, 983882, 983874, 983875, 983878, 983879, 983741, 983748, + 983751, 983744, 983745, 983749, 983750, 983742, 983743, 983746, 983747, + 983697, 983704, 983707, 983700, 983701, 983705, 983706, 983698, 983699, + 983702, 983703, 983719, 983726, 983729, 983722, 983723, 983727, 983728, + 983720, 983721, 983724, 983725, 983763, 983770, 983773, 983766, 983767, + 983771, 983772, 983764, 983765, 983768, 983769, 983862, 983869, 983872, + 983865, 983866, 983870, 983871, 983863, 983864, 983867, 983868, 983807, + 983814, 983817, 983810, 983811, 983815, 983816, 983808, 983809, 983812, + 983813, 983895, 983902, 983905, 983898, 983899, 983903, 983904, 983951, + 983896, 983897, 983900, 983901, 983906, 983913, 983916, 983909, 983910, + 983914, 983915, 983907, 983908, 983911, 983912, 983917, 983924, 983927, + 983920, 983921, 983925, 983926, 983918, 983919, 983922, 983923, 983730, + 983737, 983740, 983733, 983734, 983738, 983739, 983731, 983732, 983735, + 983736, 983752, 983759, 983762, 983755, 983756, 983760, 983761, 983753, + 983754, 983757, 983758, 983708, 983715, 983718, 983711, 983712, 983716, + 983717, 983709, 983710, 983713, 983714, 983928, 983935, 983938, 983931, + 983932, 983936, 983937, 983929, 983930, 983933, 983934, 983884, 983891, + 983894, 983887, 983888, 983892, 983893, 983885, 983886, 983889, 983890, + 983785, 983792, 983795, 983788, 983789, 983793, 983794, 983786, 983787, + 983790, 983791, 983774, 983781, 983784, 983777, 983778, 983782, 983783, + 983775, 983776, 983779, 983780, 983829, 983836, 983839, 983832, 983833, + 983837, 983838, 983830, 983831, 983834, 983835, 983796, 983803, 983806, + 983799, 983800, 983804, 983805, 983797, 983798, 983801, 983802, 73709, + 73704, 73705, 3006, 3016, 3020, 3009, 3010, 3018, 3019, 3007, 3008, 3014, + 3015, 3061, 3060, 3024, 129748, 127883, 127818, 92809, 92810, 92811, + 92808, 92789, 92790, 92791, 92788, 92816, 92859, 92856, 92847, 92845, + 92817, 92846, 92843, 92829, 92830, 92831, 92828, 92835, 92851, 92840, + 92844, 92818, 92819, 92852, 92836, 92825, 92826, 92827, 92824, 92813, + 92814, 92815, 92812, 92820, 92822, 92823, 92821, 92805, 92806, 92807, + 92804, 92797, 92798, 92799, 92796, 92801, 92802, 92803, 92800, 92785, + 92786, 92787, 92784, 92793, 92794, 92795, 92792, 92857, 92854, 92848, + 92861, 92853, 92860, 92849, 92855, 92834, 92833, 92832, 92841, 92839, + 92842, 92850, 92838, 92858, 92837, 92862, 92869, 92868, 92871, 92870, + 92867, 92866, 92864, 92873, 92865, 92872, 100352, 100353, 100354, 100355, + 100356, 100357, 100358, 100359, 100360, 100361, 100362, 100363, 100364, + 100365, 100366, 100367, 100368, 100369, 100370, 100371, 100372, 100373, + 100374, 100375, 100376, 100377, 100378, 100379, 100380, 100381, 100382, + 100383, 100384, 100385, 100386, 100387, 100388, 100389, 100390, 100391, + 100392, 100393, 100394, 100395, 100396, 100397, 100398, 100399, 100400, + 100401, 100402, 100403, 100404, 100405, 100406, 100407, 100408, 100409, + 100410, 100411, 100412, 100413, 100414, 100415, 100416, 100417, 100418, + 100419, 100420, 100421, 100422, 100423, 100424, 100425, 100426, 100427, + 100428, 100429, 100430, 100431, 100432, 100433, 100434, 100435, 100436, + 100437, 100438, 100439, 100440, 100441, 100442, 100443, 100444, 100445, + 100446, 100447, 100448, 100449, 100450, 100451, 100452, 100453, 100454, + 100455, 100456, 100457, 100458, 100459, 100460, 100461, 100462, 100463, + 100464, 100465, 100466, 100467, 100468, 100469, 100470, 100471, 100472, + 100473, 100474, 100475, 100476, 100477, 100478, 100479, 100480, 100481, + 100482, 100483, 100484, 100485, 100486, 100487, 100488, 100489, 100490, + 100491, 100492, 100493, 100494, 100495, 100496, 100497, 100498, 100499, + 100500, 100501, 100502, 100503, 100504, 100505, 100506, 100507, 100508, + 100509, 100510, 100511, 100512, 100513, 100514, 100515, 100516, 100517, + 100518, 100519, 100520, 100521, 100522, 100523, 100524, 100525, 100526, + 100527, 100528, 100529, 100530, 100531, 100532, 100533, 100534, 100535, + 100536, 100537, 100538, 100539, 100540, 100541, 100542, 100543, 100544, + 100545, 100546, 100547, 100548, 100549, 100550, 100551, 100552, 100553, + 100554, 100555, 100556, 100557, 100558, 100559, 100560, 100561, 100562, + 100563, 100564, 100565, 100566, 100567, 100568, 100569, 100570, 100571, + 100572, 100573, 100574, 100575, 100576, 100577, 100578, 100579, 100580, + 100581, 100582, 100583, 100584, 100585, 100586, 100587, 100588, 100589, + 100590, 100591, 100592, 100593, 100594, 100595, 100596, 100597, 100598, + 100599, 100600, 100601, 100602, 100603, 100604, 100605, 100606, 100607, + 100608, 100609, 100610, 100611, 100612, 100613, 100614, 100615, 100616, + 100617, 100618, 100619, 100620, 100621, 100622, 100623, 100624, 100625, + 100626, 100627, 100628, 100629, 100630, 100631, 100632, 100633, 100634, + 100635, 100636, 100637, 100638, 100639, 100640, 100641, 100642, 100643, + 100644, 100645, 100646, 100647, 100648, 100649, 100650, 100651, 100652, + 100653, 100654, 100655, 100656, 100657, 100658, 100659, 100660, 100661, + 100662, 100663, 100664, 100665, 100666, 100667, 100668, 100669, 100670, + 100671, 100672, 100673, 100674, 100675, 100676, 100677, 100678, 100679, + 100680, 100681, 100682, 100683, 100684, 100685, 100686, 100687, 100688, + 100689, 100690, 100691, 100692, 100693, 100694, 100695, 100696, 100697, + 100698, 100699, 100700, 100701, 100702, 100703, 100704, 100705, 100706, + 100707, 100708, 100709, 100710, 100711, 100712, 100713, 100714, 100715, + 100716, 100717, 100718, 100719, 100720, 100721, 100722, 100723, 100724, + 100725, 100726, 100727, 100728, 100729, 100730, 100731, 100732, 100733, + 100734, 100735, 100736, 100737, 100738, 100739, 100740, 100741, 100742, + 100743, 100744, 100745, 100746, 100747, 100748, 100749, 100750, 100751, + 100752, 100753, 100754, 100755, 100756, 100757, 100758, 100759, 100760, + 100761, 100762, 100763, 100764, 100765, 100766, 100767, 100768, 100769, + 100770, 100771, 100772, 100773, 100774, 100775, 100776, 100777, 100778, + 100779, 100780, 100781, 100782, 100783, 100784, 100785, 100786, 100787, + 100788, 100789, 100790, 100791, 100792, 100793, 100794, 100795, 100796, + 100797, 100798, 100799, 100800, 100801, 100802, 100803, 100804, 100805, + 100806, 100807, 100808, 100809, 100810, 100811, 100812, 100813, 100814, + 100815, 100816, 100817, 100818, 100819, 100820, 100821, 100822, 100823, + 100824, 100825, 100826, 100827, 100828, 100829, 100830, 100831, 100832, + 100833, 100834, 100835, 100836, 100837, 100838, 100839, 100840, 100841, + 100842, 100843, 100844, 100845, 100846, 100847, 100848, 100849, 100850, + 100851, 100852, 100853, 100854, 100855, 100856, 100857, 100858, 100859, + 100860, 100861, 100862, 100863, 100864, 100865, 100866, 100867, 100868, + 100869, 100870, 100871, 100872, 100873, 100874, 100875, 100876, 100877, + 100878, 100879, 100880, 100881, 100882, 100883, 100884, 100885, 100886, + 100887, 100888, 100889, 100890, 100891, 100892, 100893, 100894, 100895, + 100896, 100897, 100898, 100899, 100900, 100901, 100902, 100903, 100904, + 100905, 100906, 100907, 100908, 100909, 100910, 100911, 100912, 100913, + 100914, 100915, 100916, 100917, 100918, 100919, 100920, 100921, 100922, + 100923, 100924, 100925, 100926, 100927, 100928, 100929, 100930, 100931, + 100932, 100933, 100934, 100935, 100936, 100937, 100938, 100939, 100940, + 100941, 100942, 100943, 100944, 100945, 100946, 100947, 100948, 100949, + 100950, 100951, 100952, 100953, 100954, 100955, 100956, 100957, 100958, + 100959, 100960, 100961, 100962, 100963, 100964, 100965, 100966, 100967, + 100968, 100969, 100970, 100971, 100972, 100973, 100974, 100975, 100976, + 100977, 100978, 100979, 100980, 100981, 100982, 100983, 100984, 100985, + 100986, 100987, 100988, 100989, 100990, 100991, 100992, 100993, 100994, + 100995, 100996, 100997, 100998, 100999, 101000, 101001, 101002, 101003, + 101004, 101005, 101006, 101007, 101008, 101009, 101010, 101011, 101012, + 101013, 101014, 101015, 101016, 101017, 101018, 101019, 101020, 101021, + 101022, 101023, 101024, 101025, 101026, 101027, 101028, 101029, 101030, + 101031, 101032, 101033, 101034, 101035, 101036, 101037, 101038, 101039, + 101040, 101041, 101042, 101043, 101044, 101045, 101046, 101047, 101048, + 101049, 101050, 101051, 101052, 101053, 101054, 101055, 101056, 101057, + 101058, 101059, 101060, 101061, 101062, 101063, 101064, 101065, 101066, + 101067, 101068, 101069, 101070, 101071, 101072, 101073, 101074, 101075, + 101076, 101077, 101078, 101079, 101080, 101081, 101082, 101083, 101084, + 101085, 101086, 101087, 101088, 101089, 101090, 101091, 101092, 101093, + 101094, 101095, 101096, 101097, 101098, 101099, 101100, 101101, 101102, + 101103, 101104, 101105, 101106, 101107, 101108, 101109, 101110, 101111, + 101112, 101113, 101114, 101115, 101116, 101117, 101118, 101119, 101760, + 101761, 101762, 101763, 101764, 101765, 101766, 101767, 101768, 101769, + 101770, 101771, 101772, 101773, 101774, 101775, 101776, 101777, 101778, + 101779, 101780, 101781, 101782, 101783, 101784, 101785, 101786, 101787, + 101788, 101789, 101790, 101791, 101792, 101793, 101794, 101795, 101796, + 101797, 101798, 101799, 101800, 101801, 101802, 101803, 101804, 101805, + 101806, 101807, 101808, 101809, 101810, 101811, 101812, 101813, 101814, + 101815, 101816, 101817, 101818, 101819, 101820, 101821, 101822, 101823, + 101824, 101825, 101826, 101827, 101828, 101829, 101830, 101831, 101832, + 101833, 101834, 101835, 101836, 101837, 101838, 101839, 101840, 101841, + 101842, 101843, 101844, 101845, 101846, 101847, 101848, 101849, 101850, + 101851, 101852, 101853, 101854, 101855, 101856, 101857, 101858, 101859, + 101860, 101861, 101862, 101863, 101864, 101865, 101866, 101867, 101868, + 101869, 101870, 101871, 101872, 101873, 101874, 94176, 128429, 9991, + 9801, 127790, 128661, 127861, 128198, 10043, 10170, 129750, 129528, + 128222, 128380, 8981, 9990, 8481, 128384, 128301, 128250, 3164, 3158, + 3195, 3198, 3194, 3197, 3193, 3196, 3192, 3106, 3105, 3111, 3161, 3110, + 3112, 3165, 3097, 3107, 3102, 3162, 3121, 3120, 3104, 3103, 3109, 3160, + 3108, 3077, 3078, 3088, 3092, 3124, 3123, 3122, 3083, 3168, 3084, 3169, + 3125, 3081, 3082, 3090, 3091, 3079, 3080, 3126, 3127, 3128, 3117, 3116, + 3099, 3098, 3096, 3095, 3101, 3100, 3094, 3093, 3115, 3114, 3086, 3087, + 3129, 3118, 3119, 3157, 3076, 3072, 3073, 3191, 3199, 3132, 3133, 3074, + 3149, 3075, 3134, 3144, 3148, 3137, 3138, 3139, 3140, 3170, 3171, 3146, + 3147, 3135, 3136, 3142, 3143, 3179, 3178, 3181, 3180, 3177, 3176, 3174, + 3183, 3175, 3182, 127934, 8376, 9978, 129514, 119617, 119564, 119577, + 119633, 119587, 119566, 119561, 119585, 119613, 119590, 119631, 119634, + 119630, 119608, 119582, 119563, 119573, 119558, 119624, 119567, 119623, + 119586, 119636, 119612, 119625, 119568, 119584, 119619, 119618, 119583, + 119603, 119600, 119626, 119610, 119580, 119576, 119638, 119559, 119595, + 119632, 119606, 119592, 119615, 119599, 119602, 119614, 119629, 119574, + 119569, 119570, 119622, 119562, 119591, 119637, 119597, 119589, 119616, + 119609, 119560, 119635, 119565, 119604, 119588, 119571, 119594, 119578, + 119596, 119579, 119598, 119572, 119605, 119627, 119621, 119628, 119601, + 119593, 119607, 119575, 119620, 119611, 119581, 1959, 1958, 1964, 1961, + 1965, 1927, 1954, 1951, 1937, 1931, 1930, 1956, 1934, 1935, 1955, 1945, + 1920, 1926, 1946, 1933, 1925, 1929, 1943, 1942, 1941, 1922, 1969, 1950, + 1949, 1921, 1936, 1939, 1932, 1947, 1944, 1952, 1928, 1957, 1938, 1948, + 1953, 1924, 1923, 1940, 1967, 1966, 1963, 1960, 1962, 1968, 3610, 3592, + 3594, 3593, 3596, 3598, 3604, 3613, 3615, 3663, 3630, 3627, 3588, 3587, + 3589, 3586, 3590, 3675, 3585, 3628, 3621, 3653, 3622, 3659, 3633, 3657, + 3658, 3656, 3655, 3654, 3617, 3674, 3591, 3661, 3603, 3609, 3631, 3642, + 3612, 3614, 3616, 3611, 3619, 3620, 3632, 3652, 3651, 3634, 3649, 3635, + 3640, 3638, 3639, 3641, 3636, 3637, 3648, 3650, 3625, 3624, 3626, 3595, + 3660, 3601, 3602, 3607, 3600, 3608, 3606, 3605, 3599, 3623, 3662, 3597, + 3618, 3629, 3647, 3669, 3668, 3671, 3670, 3667, 3666, 3664, 3673, 3665, + 3672, 8708, 8707, 8756, 127777, 10727, 118474, 128936, 8201, 128929, 129300, 129353, 128173, 129652, 10176, 8278, 9887, 9886, 11057, 128485, 128484, 128486, 128487, 8694, 128433, 10870, 128491, 128962, 128423, 11160, 11162, 10146, 11163, 11161, 10147, 8196, 11835, 128077, 128078, - 9928, 9736, 3865, 3863, 3864, 4032, 4033, 4035, 4034, 3886, 3885, 3888, + 9928, 9736, 3865, 3863, 3864, 4035, 4032, 4033, 4034, 3886, 3885, 3888, 3887, 3884, 3883, 3891, 3890, 3882, 3889, 3877, 3876, 3879, 3878, 3875, 3874, 3872, 3881, 3873, 3880, 4030, 4031, 3945, 3905, 3947, 3904, 3948, 3938, 3946, 3917, 3916, 3932, 3931, 3922, 3921, 3908, 3918, 3913, 3923, @@ -12898,9 +12413,9 @@ static const unsigned int dawg_pos_to_codepoint[] = { 4040, 4037, 4039, 3968, 3969, 3956, 3957, 3958, 3959, 3960, 3961, 3964, 3965, 3954, 3955, 3962, 3963, 3953, 10717, 11647, 11608, 11595, 11585, 11573, 11620, 11607, 11600, 11582, 11590, 11596, 11601, 11586, 11592, - 11568, 11575, 11577, 11578, 11576, 11571, 11606, 11572, 11581, 11589, - 11583, 11569, 11570, 11584, 11587, 11609, 11611, 11610, 11612, 11613, - 11615, 11619, 11594, 11621, 11604, 11605, 11614, 11588, 11580, 11574, + 11568, 11571, 11606, 11572, 11581, 11589, 11583, 11609, 11611, 11610, + 11612, 11613, 11615, 11619, 11594, 11621, 11575, 11577, 11578, 11576, + 11569, 11570, 11584, 11587, 11604, 11605, 11614, 11588, 11580, 11574, 11597, 11598, 11599, 11602, 11591, 11616, 11617, 11618, 11622, 11579, 11593, 11623, 11603, 11631, 11632, 128005, 128047, 10053, 126, 8764, 11081, 10610, 10859, 10858, 11807, 11806, 11803, 9202, 10708, 10709, @@ -12918,155 +12433,161 @@ static const unsigned int dawg_pos_to_codepoint[] = { 67039, 67038, 67037, 67034, 67059, 67018, 67017, 67030, 67029, 67008, 67009, 67013, 67012, 67016, 67014, 67057, 67028, 67043, 67042, 67048, 67046, 67053, 67052, 67010, 67019, 67036, 67045, 67026, 67044, 67050, - 127813, 128069, 129463, 129701, 129520, 10554, 10557, 10556, 9182, - 118256, 117851, 118262, 118263, 11865, 117850, 118252, 118254, 11866, - 117852, 118248, 118250, 11833, 118246, 11210, 8992, 127913, 9180, 130024, - 130016, 130031, 8988, 8975, 11810, 118279, 8989, 8974, 11811, 130028, - 9140, 9184, 128285, 127553, 127554, 127559, 127555, 127557, 127560, - 127552, 127556, 127558, 127274, 9008, 123554, 123556, 123559, 123561, - 123564, 123537, 123544, 123543, 123553, 123555, 123558, 123560, 123546, - 123565, 123563, 123539, 123541, 123550, 123549, 123540, 123552, 123542, - 123536, 123551, 123545, 123538, 123548, 123547, 123562, 123557, 123566, - 128701, 128508, 128434, 128668, 8482, 128650, 128651, 11223, 10971, - 128646, 10701, 10699, 128710, 10698, 10141, 128681, 128208, 8227, 128305, - 9783, 9776, 9777, 9782, 9779, 9781, 9780, 9778, 10998, 10856, 10857, - 10747, 8244, 8779, 10996, 10624, 8874, 10997, 11003, 11851, 11000, 10999, - 8749, 8285, 129484, 128654, 127865, 128032, 127942, 8872, 11231, 127930, - 8366, 70601, 70608, 70613, 70612, 70610, 70528, 70529, 70542, 70545, - 70559, 70558, 70564, 70563, 70581, 70579, 70573, 70580, 70572, 70557, - 70556, 70562, 70561, 70534, 70535, 70536, 70537, 70574, 70532, 70533, - 70530, 70531, 70550, 70560, 70555, 70565, 70575, 70576, 70577, 70569, - 70568, 70552, 70551, 70549, 70548, 70554, 70553, 70547, 70546, 70567, - 70566, 70544, 70539, 70578, 70570, 70571, 70609, 70583, 70604, 70607, - 70615, 70616, 70602, 70611, 70606, 70605, 70626, 70625, 70584, 70597, - 70600, 70587, 70588, 70589, 70590, 70591, 70592, 70585, 70586, 70599, - 70594, 127799, 8378, 129411, 8523, 10658, 11202, 9930, 9929, 8498, 11826, - 11832, 8587, 8586, 128598, 128596, 8985, 128399, 8513, 8514, 8516, 11829, - 8526, 128599, 128597, 8489, 128034, 129347, 10041, 128256, 128432, 10869, - 8229, 8282, 11818, 128149, 10837, 10838, 10697, 10760, 10759, 128108, - 128109, 117896, 8273, 128490, 11834, 66432, 66451, 66454, 66433, 66436, - 66447, 66457, 66434, 66437, 66440, 66443, 66435, 66445, 66455, 66453, - 66450, 66444, 66461, 66456, 66441, 66458, 66442, 66439, 66449, 66448, - 66452, 66438, 66446, 66459, 66460, 66463, 9730, 9969, 9748, 128530, - 11217, 8255, 9100, 8746, 10824, 10822, 10826, 10817, 10818, 10821, - 983116, 11258, 9842, 129412, 9903, 8963, 8996, 11193, 10685, 10577, - 10573, 10572, 10575, 8597, 8616, 11021, 8661, 129113, 8691, 11109, 10622, - 8944, 8869, 10207, 117873, 117877, 117857, 128743, 117881, 118267, - 128742, 117847, 128314, 117912, 117862, 128744, 128316, 9709, 9710, - 117760, 129898, 129946, 129920, 129896, 9985, 118417, 117809, 118418, - 117810, 130020, 129924, 9600, 129937, 118275, 129938, 9690, 118292, - 129934, 118439, 118444, 9696, 129885, 129887, 129889, 129886, 129883, - 129888, 129881, 129884, 129882, 129879, 129880, 10196, 118416, 9136, - 129944, 129948, 9720, 117808, 9692, 117958, 117946, 117962, 117966, - 117950, 117954, 117928, 117926, 117942, 117970, 117938, 117813, 118421, - 118422, 118420, 117812, 118423, 117815, 129922, 9620, 129874, 129875, - 129892, 129894, 129890, 129878, 129893, 129891, 129876, 129895, 129877, - 10064, 118419, 9137, 10000, 9693, 117959, 117947, 117963, 117967, 117951, - 117955, 117929, 117927, 117943, 117971, 117939, 117814, 128319, 10066, - 129945, 129949, 9721, 117811, 129926, 129923, 118438, 118445, 129925, - 129901, 128579, 11797, 8593, 129976, 8645, 8670, 129033, 129029, 129177, - 129041, 129025, 129045, 8624, 8625, 10505, 8613, 10514, 11145, 11014, - 8657, 8673, 129077, 10606, 10595, 10592, 10584, 8639, 10588, 10580, 8638, - 129089, 129093, 129085, 10224, 128621, 129105, 129081, 11105, 11137, - 11121, 129065, 11170, 11171, 129061, 129057, 129073, 129069, 11131, - 11115, 11141, 129169, 10506, 8607, 11245, 10569, 8648, 8679, 8683, 8685, - 8684, 129173, 8682, 11192, 8686, 8687, 9797, 983117, 42509, 42510, 42511, - 42446, 42370, 42486, 42257, 42333, 42294, 42407, 42445, 42369, 42485, - 42256, 42332, 42293, 42406, 42449, 42373, 42489, 42260, 42336, 42297, - 42410, 42434, 42359, 42473, 42246, 42321, 42283, 42396, 42435, 42360, - 42474, 42247, 42322, 42284, 42397, 42452, 42376, 42492, 42263, 42339, - 42300, 42413, 42451, 42375, 42491, 42262, 42338, 42299, 42412, 42444, - 42368, 42484, 42255, 42331, 42292, 42405, 42443, 42367, 42483, 42254, - 42330, 42291, 42404, 42454, 42378, 42494, 42265, 42341, 42302, 42415, - 42453, 42377, 42493, 42264, 42340, 42301, 42414, 42439, 42440, 42364, - 42479, 42251, 42480, 42327, 42288, 42401, 42502, 42272, 42503, 42461, - 42385, 42349, 42309, 42422, 42429, 42430, 42355, 42468, 42242, 42469, - 42316, 42317, 42278, 42279, 42391, 42392, 42476, 42249, 42477, 42437, - 42362, 42324, 42325, 42286, 42399, 42459, 42383, 42346, 42347, 42499, - 42270, 42307, 42420, 42487, 42508, 42258, 42447, 42371, 42334, 42295, - 42408, 42436, 42361, 42475, 42248, 42323, 42285, 42398, 42438, 42363, - 42478, 42250, 42326, 42287, 42400, 42462, 42386, 42504, 42273, 42350, - 42310, 42423, 42450, 42514, 42539, 42512, 42513, 42538, 42374, 42490, - 42261, 42337, 42298, 42411, 42507, 42500, 42271, 42501, 42460, 42384, - 42348, 42308, 42421, 42315, 42467, 42428, 42457, 42381, 42497, 42268, - 42344, 42305, 42418, 42464, 42388, 42506, 42275, 42352, 42312, 42425, - 42463, 42387, 42505, 42274, 42351, 42311, 42424, 42455, 42379, 42495, - 42266, 42342, 42303, 42416, 42441, 42365, 42481, 42252, 42328, 42289, - 42402, 42456, 42380, 42496, 42267, 42343, 42304, 42417, 42433, 42358, - 42472, 42245, 42320, 42282, 42395, 42448, 42372, 42488, 42259, 42335, - 42296, 42409, 42442, 42366, 42482, 42253, 42329, 42290, 42403, 42458, - 42382, 42498, 42269, 42345, 42306, 42419, 42470, 42243, 42244, 42471, - 42431, 42356, 42357, 42432, 42318, 42319, 42280, 42281, 42393, 42394, - 42465, 42240, 42241, 42466, 42426, 42353, 42354, 42427, 42313, 42314, - 42276, 42277, 42389, 42390, 42523, 42526, 42522, 42515, 42520, 42527, - 42516, 42524, 42518, 42517, 42525, 42521, 42519, 42533, 42532, 42535, - 42534, 42531, 42530, 42528, 42537, 42529, 42536, 65024, 65033, 917843, - 917844, 917845, 917846, 917847, 917848, 917849, 917850, 917851, 917852, - 65034, 917853, 917854, 917855, 917856, 917857, 917858, 917859, 917860, - 917861, 917862, 65035, 917863, 917864, 917865, 917866, 917867, 917868, - 917869, 917870, 917871, 917872, 65036, 917873, 917874, 917875, 917876, - 917877, 917878, 917879, 917880, 917881, 917882, 65037, 917883, 917884, - 917885, 917886, 917887, 917888, 917889, 917890, 917891, 917892, 65038, - 917893, 917894, 917895, 917896, 917897, 917898, 917899, 917900, 917901, - 917902, 65039, 917903, 917904, 917905, 917906, 917907, 917908, 917909, - 917910, 917911, 917912, 917760, 917913, 917914, 917915, 917916, 917917, - 917918, 917919, 917920, 917921, 917922, 917761, 917923, 917924, 917925, - 917926, 917927, 917928, 917929, 917930, 917931, 917932, 917762, 917933, - 917934, 917935, 917936, 917937, 917938, 917939, 917940, 917941, 917942, - 65025, 917763, 917943, 917944, 917945, 917946, 917947, 917948, 917949, - 917950, 917951, 917952, 917764, 917953, 917954, 917955, 917956, 917957, - 917958, 917959, 917960, 917961, 917962, 917765, 917963, 917964, 917965, - 917966, 917967, 917968, 917969, 917970, 917971, 917972, 917766, 917973, - 917974, 917975, 917976, 917977, 917978, 917979, 917980, 917981, 917982, - 917767, 917983, 917984, 917985, 917986, 917987, 917988, 917989, 917990, - 917991, 917992, 917768, 917993, 917994, 917995, 917996, 917997, 917998, - 917999, 917769, 917770, 917771, 917772, 65026, 917773, 917774, 917775, - 917776, 917777, 917778, 917779, 917780, 917781, 917782, 65027, 917783, - 917784, 917785, 917786, 917787, 917788, 917789, 917790, 917791, 917792, - 65028, 917793, 917794, 917795, 917796, 917797, 917798, 917799, 917800, - 917801, 917802, 65029, 917803, 917804, 917805, 917806, 917807, 917808, - 917809, 917810, 917811, 917812, 65030, 917813, 917814, 917815, 917816, - 917817, 917818, 917819, 917820, 917821, 917822, 65031, 917823, 917824, - 917825, 917826, 917827, 917828, 917829, 917830, 917831, 917832, 65032, - 917833, 917834, 917835, 917836, 917837, 917838, 917839, 917840, 917841, - 917842, 129499, 983245, 983254, 983360, 983361, 983362, 983363, 983364, - 983365, 983366, 983367, 983368, 983369, 983255, 983370, 983371, 983372, - 983373, 983374, 983375, 983376, 983377, 983378, 983379, 983256, 983380, - 983381, 983382, 983383, 983384, 983385, 983386, 983387, 983388, 983389, - 983257, 983390, 983391, 983392, 983393, 983394, 983395, 983396, 983397, - 983398, 983399, 983258, 983400, 983401, 983402, 983403, 983404, 983405, - 983406, 983407, 983408, 983409, 983259, 983410, 983411, 983412, 983413, - 983414, 983415, 983416, 983417, 983418, 983419, 983260, 983420, 983421, - 983422, 983423, 983424, 983425, 983426, 983427, 983428, 983429, 983277, - 983430, 983431, 983432, 983433, 983434, 983435, 983436, 983437, 983438, - 983439, 983278, 983440, 983441, 983442, 983443, 983444, 983445, 983446, - 983447, 983448, 983449, 983279, 983450, 983451, 983452, 983453, 983454, - 983455, 983456, 983457, 983458, 983459, 983246, 983280, 983460, 983461, - 983462, 983463, 983464, 983465, 983466, 983467, 983468, 983469, 983281, - 983470, 983471, 983472, 983473, 983474, 983475, 983476, 983477, 983478, - 983479, 983282, 983480, 983481, 983482, 983483, 983484, 983485, 983486, - 983487, 983488, 983489, 983283, 983490, 983491, 983492, 983493, 983494, - 983495, 983496, 983497, 983498, 983499, 983284, 983500, 983501, 983502, - 983503, 983504, 983505, 983506, 983507, 983508, 983509, 983285, 983510, - 983511, 983512, 983513, 983514, 983515, 983516, 983286, 983287, 983288, - 983289, 983247, 983290, 983291, 983292, 983293, 983294, 983295, 983296, - 983297, 983298, 983299, 983248, 983300, 983301, 983302, 983303, 983304, - 983305, 983306, 983307, 983308, 983309, 983249, 983310, 983311, 983312, - 983313, 983314, 983315, 983316, 983317, 983318, 983319, 983250, 983320, - 983321, 983322, 983323, 983324, 983325, 983326, 983327, 983328, 983329, - 983251, 983330, 983331, 983332, 983333, 983334, 983335, 983336, 983337, - 983338, 983339, 983252, 983340, 983341, 983342, 983343, 983344, 983345, - 983346, 983347, 983348, 983349, 983253, 983350, 983351, 983352, 983353, - 983354, 983355, 983356, 983357, 983358, 983359, 7401, 7402, 7409, 7403, - 7404, 7415, 7410, 7418, 7413, 7379, 7398, 7396, 7411, 7408, 7406, 7407, - 7405, 7414, 7397, 7400, 7395, 7399, 7394, 7380, 7384, 7412, 7417, 7386, - 7389, 7376, 7388, 7378, 7416, 7392, 7391, 7387, 7390, 7381, 7382, 7383, - 7385, 7393, 7377, 8483, 10704, 10980, 10978, 10186, 117780, 8942, 8286, - 117917, 12347, 12337, 12339, 12341, 12338, 12340, 117892, 124, 9168, - 10992, 10991, 117904, 118293, 9087, 9896, 129904, 129905, 129906, 129907, - 129908, 129909, 117916, 117770, 11135, 983069, 983144, 11823, 128678, - 10650, 11837, 128976, 128959, 128947, 128637, 128941, 128953, 128636, - 128904, 128914, 128934, 8921, 8920, 128933, 10799, 9910, 128243, 9996, + 73140, 73141, 73149, 73154, 73155, 73150, 73151, 73156, 73171, 73166, + 73161, 73168, 73175, 73176, 73147, 73152, 73153, 73148, 73144, 73145, + 73157, 73158, 73164, 73165, 73159, 73160, 73162, 73163, 73142, 73143, + 73137, 73173, 73136, 73169, 73146, 73139, 73172, 73138, 73170, 73174, + 73167, 73177, 73178, 73179, 73189, 73188, 73191, 73190, 73187, 73186, + 73184, 73193, 73185, 73192, 127813, 128069, 129463, 129701, 129520, + 10554, 10557, 10556, 9182, 118256, 117851, 118262, 118263, 11865, 117850, + 118252, 118254, 11866, 117852, 118248, 118250, 11833, 118246, 11210, + 8992, 127913, 9180, 130024, 130016, 130031, 8988, 8975, 11810, 118279, + 8989, 8974, 11811, 130028, 9140, 9184, 128285, 127553, 127554, 127559, + 127555, 127557, 127560, 127552, 127556, 127558, 127274, 9008, 123554, + 123556, 123559, 123561, 123564, 123537, 123544, 123543, 123553, 123555, + 123558, 123560, 123546, 123565, 123563, 123539, 123541, 123550, 123549, + 123540, 123552, 123542, 123536, 123551, 123545, 123538, 123548, 123547, + 123562, 123557, 123566, 128701, 128508, 128434, 128668, 8482, 128650, + 128651, 11223, 10971, 128646, 129678, 118460, 10701, 10699, 128710, + 10698, 10141, 128681, 128208, 8227, 128305, 9783, 9776, 9777, 9782, 9779, + 9781, 9780, 9778, 10998, 10856, 10857, 10747, 8244, 8779, 10996, 10624, + 8874, 10997, 11003, 11851, 11000, 10999, 8749, 8285, 129484, 128654, + 129674, 127865, 128032, 127942, 8872, 11231, 127930, 8366, 70601, 70608, + 70613, 70612, 70610, 70528, 70529, 70542, 70545, 70559, 70558, 70564, + 70563, 70581, 70579, 70573, 70580, 70572, 70557, 70556, 70562, 70561, + 70534, 70535, 70536, 70537, 70574, 70532, 70533, 70530, 70531, 70550, + 70560, 70555, 70565, 70575, 70576, 70577, 70569, 70568, 70552, 70551, + 70549, 70548, 70554, 70553, 70547, 70546, 70567, 70566, 70544, 70539, + 70578, 70570, 70571, 70609, 70583, 70604, 70607, 70615, 70616, 70602, + 70611, 70606, 70605, 70626, 70625, 70584, 70597, 70600, 70587, 70588, + 70589, 70590, 70591, 70592, 70585, 70586, 70599, 70594, 127799, 8378, + 129411, 8523, 10658, 11202, 9930, 9929, 8498, 11826, 11832, 8587, 8586, + 128598, 128596, 8985, 128399, 8513, 8514, 8516, 11829, 8526, 128599, + 128597, 8489, 128034, 129347, 10041, 128256, 128432, 10869, 8229, 8282, + 11818, 128149, 10837, 10838, 10697, 10760, 10759, 128108, 128109, 117896, + 8273, 128490, 11834, 66432, 66451, 66454, 66433, 66436, 66447, 66457, + 66434, 66437, 66440, 66443, 66435, 66445, 66455, 66453, 66450, 66444, + 66461, 66456, 66441, 66458, 66442, 66439, 66449, 66448, 66452, 66438, + 66446, 66459, 66460, 66463, 9730, 9969, 9748, 128530, 11217, 8255, 9100, + 8746, 10824, 10822, 10826, 10817, 10818, 10821, 983116, 11258, 9842, + 129412, 9903, 8963, 8996, 11193, 10685, 10577, 10573, 10572, 10575, 8597, + 8616, 11021, 8661, 129113, 8691, 11109, 10622, 8944, 8869, 10207, 117873, + 117877, 117857, 128743, 117881, 118267, 128742, 117847, 128314, 117912, + 117862, 128744, 128316, 9709, 9710, 117760, 129898, 129946, 129920, + 129896, 9985, 118417, 117809, 118418, 117810, 130020, 129924, 9600, + 129937, 118275, 129938, 9690, 118292, 129934, 118439, 118444, 9696, + 129885, 129887, 129889, 129886, 129883, 129888, 129881, 129884, 129882, + 129879, 129880, 10196, 118416, 9136, 129944, 129948, 9720, 117808, 9692, + 117958, 117946, 117962, 117966, 117950, 117954, 117928, 117926, 117942, + 117970, 117938, 117813, 118421, 118422, 118420, 117812, 118423, 117815, + 129922, 9620, 129874, 129875, 129892, 129894, 129890, 129878, 129893, + 129891, 129876, 129895, 129877, 10064, 118419, 9137, 10000, 9693, 117959, + 117947, 117963, 117967, 117951, 117955, 117929, 117927, 117943, 117971, + 117939, 117814, 128319, 10066, 129945, 129949, 9721, 117811, 129926, + 129923, 118438, 118445, 129925, 129901, 128579, 11797, 8593, 129976, + 8645, 8670, 129033, 129029, 129177, 129041, 129025, 129045, 8624, 8625, + 10505, 8613, 10514, 11145, 11014, 8657, 8673, 129077, 10606, 10595, + 10592, 10584, 8639, 10588, 10580, 8638, 129089, 129093, 129085, 10224, + 128621, 129105, 129081, 11105, 11137, 11121, 129065, 11170, 11171, + 129061, 129057, 129073, 129069, 11131, 11115, 11141, 129169, 10506, 8607, + 11245, 10569, 8648, 8679, 8683, 8685, 8684, 129173, 8682, 11192, 8686, + 8687, 9797, 983117, 42509, 42510, 42511, 42446, 42370, 42486, 42257, + 42333, 42294, 42407, 42445, 42369, 42485, 42256, 42332, 42293, 42406, + 42449, 42373, 42489, 42260, 42336, 42297, 42410, 42434, 42359, 42473, + 42246, 42321, 42283, 42396, 42435, 42360, 42474, 42247, 42322, 42284, + 42397, 42452, 42376, 42492, 42263, 42339, 42300, 42413, 42451, 42375, + 42491, 42262, 42338, 42299, 42412, 42444, 42368, 42484, 42255, 42331, + 42292, 42405, 42443, 42367, 42483, 42254, 42330, 42291, 42404, 42454, + 42378, 42494, 42265, 42341, 42302, 42415, 42453, 42377, 42493, 42264, + 42340, 42301, 42414, 42439, 42440, 42364, 42479, 42251, 42480, 42327, + 42288, 42401, 42502, 42272, 42503, 42461, 42385, 42349, 42309, 42422, + 42429, 42430, 42355, 42468, 42242, 42469, 42316, 42317, 42278, 42279, + 42391, 42392, 42476, 42249, 42477, 42437, 42362, 42324, 42325, 42286, + 42399, 42459, 42383, 42346, 42347, 42499, 42270, 42307, 42420, 42487, + 42508, 42258, 42447, 42371, 42334, 42295, 42408, 42436, 42361, 42475, + 42248, 42323, 42285, 42398, 42438, 42363, 42478, 42250, 42326, 42287, + 42400, 42462, 42386, 42504, 42273, 42350, 42310, 42423, 42450, 42514, + 42539, 42512, 42513, 42538, 42374, 42490, 42261, 42337, 42298, 42411, + 42507, 42500, 42271, 42501, 42460, 42384, 42348, 42308, 42421, 42315, + 42467, 42428, 42457, 42381, 42497, 42268, 42344, 42305, 42418, 42464, + 42388, 42506, 42275, 42352, 42312, 42425, 42463, 42387, 42505, 42274, + 42351, 42311, 42424, 42455, 42379, 42495, 42266, 42342, 42303, 42416, + 42441, 42365, 42481, 42252, 42328, 42289, 42402, 42456, 42380, 42496, + 42267, 42343, 42304, 42417, 42433, 42358, 42472, 42245, 42320, 42282, + 42395, 42448, 42372, 42488, 42259, 42335, 42296, 42409, 42442, 42366, + 42482, 42253, 42329, 42290, 42403, 42458, 42382, 42498, 42269, 42345, + 42306, 42419, 42470, 42243, 42244, 42471, 42431, 42356, 42357, 42432, + 42318, 42319, 42280, 42281, 42393, 42394, 42465, 42240, 42241, 42466, + 42426, 42353, 42354, 42427, 42313, 42314, 42276, 42277, 42389, 42390, + 42523, 42526, 42522, 42515, 42520, 42527, 42516, 42524, 42518, 42517, + 42525, 42521, 42519, 42533, 42532, 42535, 42534, 42531, 42530, 42528, + 42537, 42529, 42536, 65024, 65033, 917843, 917844, 917845, 917846, + 917847, 917848, 917849, 917850, 917851, 917852, 65034, 917853, 917854, + 917855, 917856, 917857, 917858, 917859, 917860, 917861, 917862, 65035, + 917863, 917864, 917865, 917866, 917867, 917868, 917869, 917870, 917871, + 917872, 65036, 917873, 917874, 917875, 917876, 917877, 917878, 917879, + 917880, 917881, 917882, 65037, 917883, 917884, 917885, 917886, 917887, + 917888, 917889, 917890, 917891, 917892, 65038, 917893, 917894, 917895, + 917896, 917897, 917898, 917899, 917900, 917901, 917902, 65039, 917903, + 917904, 917905, 917906, 917907, 917908, 917909, 917910, 917911, 917912, + 917760, 917913, 917914, 917915, 917916, 917917, 917918, 917919, 917920, + 917921, 917922, 917761, 917923, 917924, 917925, 917926, 917927, 917928, + 917929, 917930, 917931, 917932, 917762, 917933, 917934, 917935, 917936, + 917937, 917938, 917939, 917940, 917941, 917942, 65025, 917763, 917943, + 917944, 917945, 917946, 917947, 917948, 917949, 917950, 917951, 917952, + 917764, 917953, 917954, 917955, 917956, 917957, 917958, 917959, 917960, + 917961, 917962, 917765, 917963, 917964, 917965, 917966, 917967, 917968, + 917969, 917970, 917971, 917972, 917766, 917973, 917974, 917975, 917976, + 917977, 917978, 917979, 917980, 917981, 917982, 917767, 917983, 917984, + 917985, 917986, 917987, 917988, 917989, 917990, 917991, 917992, 917768, + 917993, 917994, 917995, 917996, 917997, 917998, 917999, 917769, 917770, + 917771, 917772, 65026, 917773, 917774, 917775, 917776, 917777, 917778, + 917779, 917780, 917781, 917782, 65027, 917783, 917784, 917785, 917786, + 917787, 917788, 917789, 917790, 917791, 917792, 65028, 917793, 917794, + 917795, 917796, 917797, 917798, 917799, 917800, 917801, 917802, 65029, + 917803, 917804, 917805, 917806, 917807, 917808, 917809, 917810, 917811, + 917812, 65030, 917813, 917814, 917815, 917816, 917817, 917818, 917819, + 917820, 917821, 917822, 65031, 917823, 917824, 917825, 917826, 917827, + 917828, 917829, 917830, 917831, 917832, 65032, 917833, 917834, 917835, + 917836, 917837, 917838, 917839, 917840, 917841, 917842, 129499, 983245, + 983254, 983364, 983365, 983366, 983367, 983368, 983369, 983370, 983371, + 983372, 983373, 983255, 983374, 983375, 983376, 983377, 983378, 983379, + 983380, 983381, 983382, 983383, 983256, 983384, 983385, 983386, 983387, + 983388, 983389, 983390, 983391, 983392, 983393, 983257, 983394, 983395, + 983396, 983397, 983398, 983399, 983400, 983401, 983402, 983403, 983258, + 983404, 983405, 983406, 983407, 983408, 983409, 983410, 983411, 983412, + 983413, 983259, 983414, 983415, 983416, 983417, 983418, 983419, 983420, + 983421, 983422, 983423, 983260, 983424, 983425, 983426, 983427, 983428, + 983429, 983430, 983431, 983432, 983433, 983281, 983434, 983435, 983436, + 983437, 983438, 983439, 983440, 983441, 983442, 983443, 983282, 983444, + 983445, 983446, 983447, 983448, 983449, 983450, 983451, 983452, 983453, + 983283, 983454, 983455, 983456, 983457, 983458, 983459, 983460, 983461, + 983462, 983463, 983246, 983284, 983464, 983465, 983466, 983467, 983468, + 983469, 983470, 983471, 983472, 983473, 983285, 983474, 983475, 983476, + 983477, 983478, 983479, 983480, 983481, 983482, 983483, 983286, 983484, + 983485, 983486, 983487, 983488, 983489, 983490, 983491, 983492, 983493, + 983287, 983494, 983495, 983496, 983497, 983498, 983499, 983500, 983501, + 983502, 983503, 983288, 983504, 983505, 983506, 983507, 983508, 983509, + 983510, 983511, 983512, 983513, 983289, 983514, 983515, 983516, 983517, + 983518, 983519, 983520, 983290, 983291, 983292, 983293, 983247, 983294, + 983295, 983296, 983297, 983298, 983299, 983300, 983301, 983302, 983303, + 983248, 983304, 983305, 983306, 983307, 983308, 983309, 983310, 983311, + 983312, 983313, 983249, 983314, 983315, 983316, 983317, 983318, 983319, + 983320, 983321, 983322, 983323, 983250, 983324, 983325, 983326, 983327, + 983328, 983329, 983330, 983331, 983332, 983333, 983251, 983334, 983335, + 983336, 983337, 983338, 983339, 983340, 983341, 983342, 983343, 983252, + 983344, 983345, 983346, 983347, 983348, 983349, 983350, 983351, 983352, + 983353, 983253, 983354, 983355, 983356, 983357, 983358, 983359, 983360, + 983361, 983362, 983363, 7401, 7402, 7409, 7403, 7404, 7415, 7410, 7418, + 7413, 7379, 7398, 7396, 7411, 7408, 7406, 7407, 7405, 7414, 7397, 7400, + 7395, 7399, 7394, 7380, 7384, 7412, 7417, 7386, 7389, 7376, 7388, 7378, + 7416, 7392, 7391, 7387, 7390, 7381, 7382, 7383, 7385, 7393, 7377, 8483, + 10704, 10980, 10978, 10186, 117780, 8942, 8286, 117917, 12347, 12337, + 12339, 12341, 12338, 12340, 117892, 124, 9168, 10992, 10991, 117904, + 118293, 9087, 9896, 129904, 129905, 129906, 129907, 129908, 129909, + 117916, 117770, 11135, 983069, 983144, 11823, 128678, 10650, 11837, + 128976, 128959, 128947, 128637, 128941, 128953, 128636, 128904, 128914, + 128934, 8921, 8920, 128933, 9910, 128887, 10799, 128243, 9996, 118469, 128249, 127918, 128252, 94193, 94192, 8983, 127931, 9805, 66929, 66930, 66936, 66935, 66942, 66943, 66947, 66946, 66950, 66949, 66932, 66931, 66934, 66933, 66957, 66956, 66959, 66958, 66941, 66940, 66937, 66944, @@ -13095,193 +12616,194 @@ static const unsigned int dawg_pos_to_codepoint[] = { 71920, 71919, 71916, 71915, 71914, 71935, 9888, 128702, 127754, 128003, 129341, 127817, 8986, 12316, 11071, 10547, 127988, 127987, 128075, 12336, 65103, 8967, 65099, 128465, 128576, 128553, 10172, 128146, 983238, - 127947, 9840, 128734, 9784, 9855, 129197, 129196, 9702, 9815, 129548, - 129590, 129608, 129611, 9812, 129545, 129587, 9816, 129542, 129563, - 129584, 129591, 129605, 129549, 129616, 129614, 129615, 9817, 129550, - 129592, 9813, 129546, 129588, 9814, 129547, 129589, 129569, 129566, - 129570, 129571, 129567, 129568, 9675, 128906, 9862, 10732, 9863, 9717, - 9718, 9716, 9719, 9831, 10209, 10210, 10211, 129995, 9671, 9672, 128922, - 128923, 10192, 9826, 9931, 128071, 128407, 9759, 9663, 9661, 9920, 9921, - 10069, 9872, 9983, 10048, 128174, 11214, 10023, 9785, 128427, 129293, - 9825, 9989, 129984, 11041, 11053, 10710, 11036, 128326, 9945, 128072, - 9756, 9669, 9667, 9665, 117894, 117895, 128928, 11046, 11088, 9725, 9723, - 11048, 11229, 10001, 9649, 11040, 127985, 10068, 9645, 11092, 9659, 9657, - 9655, 128073, 9758, 9988, 65094, 9750, 11051, 11090, 9643, 9786, 9828, - 9633, 128307, 128916, 9635, 128917, 10212, 9713, 9714, 10213, 9634, 9712, - 9715, 9707, 9093, 127779, 127781, 127782, 9788, 127780, 9734, 128382, - 9743, 9186, 10177, 9943, 128070, 9757, 129994, 9653, 9651, 9708, 11055, - 9647, 118278, 11006, 11038, 10163, 128011, 129144, 129152, 129120, + 127947, 9840, 128734, 9784, 9855, 129197, 129196, 9702, 129621, 9815, + 129548, 129590, 129608, 129611, 129620, 9812, 129545, 129587, 9816, + 129542, 129563, 129584, 129591, 129605, 129549, 129616, 129614, 129615, + 9817, 129550, 129592, 9813, 129546, 129588, 9814, 129547, 129589, 129569, + 129566, 129570, 129571, 129567, 129568, 9675, 128906, 9862, 10732, 9863, + 9717, 9718, 9716, 9719, 9831, 10209, 10210, 10211, 129995, 9671, 9672, + 128922, 128923, 10192, 9826, 9931, 128071, 128407, 9759, 9663, 9661, + 9920, 9921, 10069, 9872, 9983, 10048, 128174, 11214, 10023, 9785, 128427, + 129293, 9825, 9989, 129984, 11041, 11053, 10710, 11036, 128326, 9945, + 128072, 9756, 9669, 9667, 9665, 117894, 117895, 128928, 11046, 11088, + 9725, 9723, 11048, 11229, 10001, 9649, 11040, 127985, 10068, 9645, 11092, + 9659, 9657, 9655, 128073, 9758, 9988, 65094, 9750, 11051, 11090, 9643, + 9786, 9828, 9633, 128307, 128916, 9635, 128917, 10212, 9713, 9714, 10213, + 9634, 9712, 9715, 9707, 9093, 127779, 127781, 127782, 9788, 127780, 9734, + 128382, 9743, 9186, 10177, 9943, 128070, 9757, 129994, 9653, 9651, 9708, + 11055, 9647, 118278, 11006, 11038, 10163, 128011, 129144, 129152, 129120, 129136, 129128, 129146, 129154, 129122, 129138, 129130, 129147, 129155, 129123, 129139, 129131, 129145, 129153, 129121, 129137, 129129, 129149, 129157, 129125, 129141, 129133, 129148, 129156, 129124, 129140, 129132, 129150, 129158, 129126, 129142, 129134, 129151, 129159, 129127, 129143, 129135, 11838, 129344, 127888, 127788, 129695, 127863, 128521, 129725, - 128430, 128732, 128105, 128111, 128098, 128090, 128082, 128097, 128698, + 128430, 128732, 128105, 128111, 128098, 128090, 128097, 128082, 128698, 11825, 8288, 128506, 128543, 129713, 8361, 129717, 128058, 127873, 8768, 129340, 128295, 9997, 983233, 8999, 129659, 129644, 129643, 129641, 129639, 129642, 129645, 129640, 129637, 129636, 129634, 129632, 129635, - 129638, 129633, 8891, 129393, 128155, 165, 69292, 69291, 69293, 69256, - 69255, 69254, 69281, 69268, 69259, 69248, 69271, 69289, 69286, 69287, - 69257, 69278, 69277, 69279, 69276, 69280, 69296, 69282, 69251, 69250, - 69266, 69265, 69267, 69253, 69252, 69269, 69274, 69275, 69284, 69285, - 69272, 69258, 69288, 69297, 69263, 69260, 69270, 69262, 69261, 69249, - 69283, 69273, 69264, 42139, 42149, 42173, 42172, 42132, 42147, 42179, - 42174, 42148, 42169, 42150, 42134, 42166, 42135, 42145, 42143, 42137, - 42175, 42157, 42154, 42167, 42165, 42163, 42130, 42182, 42129, 42171, - 42138, 42140, 42136, 42131, 42151, 42164, 42181, 42142, 42156, 42170, - 42176, 42178, 42160, 42133, 42144, 42152, 42159, 42161, 42158, 42141, - 42146, 42177, 42180, 42155, 42162, 42128, 42168, 42153, 41070, 41059, - 41060, 41058, 41073, 41072, 41071, 41068, 41069, 41066, 41067, 41065, - 41048, 41052, 41053, 41050, 41051, 41049, 41046, 41047, 41056, 41057, - 41054, 41055, 41063, 41064, 41061, 41062, 41076, 41077, 41074, 41075, - 41006, 40995, 40996, 40994, 41009, 41008, 41007, 41004, 41005, 41002, - 41003, 41001, 40984, 40988, 40989, 40986, 40987, 40985, 40982, 40983, - 40992, 40993, 40990, 40991, 40999, 41000, 40997, 40998, 41012, 41015, - 41014, 41013, 41010, 41011, 41842, 41831, 41832, 41829, 41830, 41845, - 41844, 41843, 41841, 41827, 41828, 41825, 41826, 41839, 41840, 41837, - 41838, 41835, 41836, 41833, 41834, 41848, 41851, 41850, 41849, 41846, - 41847, 41670, 41659, 41660, 41658, 41673, 41672, 41671, 41668, 41669, - 41666, 41667, 41665, 41648, 41652, 41653, 41650, 41651, 41649, 41646, - 41647, 41656, 41657, 41654, 41655, 41663, 41664, 41661, 41662, 41676, - 41679, 41678, 41677, 41674, 41675, 41293, 41282, 41283, 41281, 41296, - 41295, 41294, 41291, 41292, 41272, 41275, 41276, 41274, 41273, 41270, - 41271, 41289, 41290, 41288, 41279, 41280, 41277, 41278, 41286, 41287, - 41284, 41285, 41238, 41228, 41227, 41241, 41240, 41239, 41236, 41237, - 41218, 41221, 41222, 41220, 41219, 41216, 41217, 41234, 41235, 41233, - 41225, 41226, 41223, 41224, 41231, 41232, 41229, 41230, 41174, 41175, - 41173, 41171, 41172, 41169, 41170, 41167, 41168, 41165, 41166, 41184, - 41185, 41182, 41183, 41178, 41181, 41180, 41179, 41176, 41177, 41494, - 41496, 41497, 41495, 41492, 41493, 41516, 41504, 41505, 41502, 41503, - 41519, 41518, 41517, 41514, 41515, 41500, 41501, 41498, 41499, 41512, - 41513, 41510, 41511, 41508, 41509, 41506, 41507, 41460, 41448, 41449, - 41446, 41447, 41463, 41462, 41461, 41458, 41459, 41436, 41440, 41441, - 41438, 41439, 41437, 41434, 41435, 41444, 41445, 41442, 41443, 41456, - 41457, 41454, 41455, 41452, 41453, 41450, 41451, 41584, 41583, 41582, - 41389, 41379, 41380, 41378, 41392, 41391, 41390, 41387, 41388, 41369, - 41372, 41373, 41371, 41370, 41367, 41368, 41385, 41386, 41384, 41382, - 41383, 41381, 41376, 41377, 41374, 41375, 41395, 41398, 41397, 41396, - 41393, 41394, 41125, 41117, 41118, 41116, 41128, 41127, 41126, 41123, - 41124, 41107, 41110, 41111, 41109, 41108, 41105, 41106, 41114, 41115, - 41112, 41113, 41121, 41122, 41119, 41120, 41130, 41133, 41132, 41131, - 41129, 41336, 41334, 41335, 41333, 41332, 41340, 41338, 41339, 41337, - 41322, 41326, 41327, 41324, 41325, 41323, 41320, 41321, 41330, 41331, - 41328, 41329, 41556, 41557, 41554, 41555, 41563, 41564, 41562, 41544, - 41548, 41549, 41546, 41547, 41545, 41542, 41543, 41552, 41553, 41550, - 41551, 41560, 41561, 41558, 41559, 41591, 41592, 41589, 41590, 41598, - 41599, 41597, 41587, 41588, 41585, 41586, 41595, 41596, 41593, 41594, - 40962, 40960, 983243, 40966, 40967, 40964, 40965, 40963, 40961, 42025, - 42017, 42018, 42016, 42028, 42027, 42026, 42023, 42024, 42010, 42014, - 42015, 42012, 42013, 42011, 42008, 42009, 42021, 42022, 42019, 42020, - 42031, 42032, 42029, 42030, 41970, 41962, 41963, 41960, 41961, 41973, - 41972, 41971, 41968, 41969, 41954, 41958, 41959, 41956, 41957, 41955, - 41952, 41953, 41966, 41967, 41964, 41965, 41976, 41979, 41978, 41977, - 41974, 41975, 41488, 41476, 41477, 41475, 41491, 41490, 41489, 41486, - 41487, 41466, 41469, 41470, 41468, 41467, 41464, 41465, 41473, 41474, - 41471, 41472, 41484, 41485, 41482, 41483, 41480, 41481, 41478, 41479, - 41424, 41413, 41414, 41411, 41412, 41427, 41426, 41425, 41422, 41423, - 41420, 41421, 41419, 41401, 41405, 41406, 41403, 41404, 41402, 41399, - 41400, 41409, 41410, 41407, 41408, 41417, 41418, 41415, 41416, 41430, - 41433, 41432, 41431, 41428, 41429, 41538, 41527, 41528, 41526, 41541, - 41540, 41539, 41536, 41537, 41534, 41535, 41533, 41524, 41525, 41522, - 41523, 41531, 41532, 41529, 41530, 41521, 41520, 41157, 41147, 41148, - 41145, 41146, 41160, 41159, 41158, 41155, 41156, 41136, 41139, 41140, - 41138, 41137, 41134, 41135, 41143, 41144, 41141, 41142, 41151, 41152, - 41149, 41150, 41163, 41164, 41161, 41162, 41154, 41153, 41080, 41083, - 41084, 41082, 41081, 41078, 41079, 41087, 41088, 41085, 41086, 41091, - 41092, 41089, 41090, 41095, 41098, 41097, 41096, 41093, 41094, 41101, - 41104, 41103, 41102, 41099, 41100, 41299, 41302, 41301, 41300, 41297, - 41298, 41312, 41313, 41311, 41305, 41306, 41303, 41304, 41309, 41310, - 41307, 41308, 41316, 41319, 41318, 41317, 41314, 41315, 41574, 41572, - 41573, 41580, 41581, 41579, 41566, 41567, 41565, 41570, 41571, 41568, - 41569, 41577, 41578, 41575, 41576, 42048, 42042, 42041, 42051, 42050, - 42049, 42047, 42035, 42039, 42040, 42037, 42038, 42036, 42033, 42034, - 42045, 42046, 42043, 42044, 42054, 42057, 42056, 42055, 42052, 42053, - 41881, 41882, 41880, 41878, 41879, 41876, 41877, 41885, 41886, 41883, - 41884, 41889, 41892, 41891, 41890, 41887, 41888, 41895, 41898, 41897, - 41896, 41893, 41894, 42075, 42067, 42068, 42066, 42076, 42073, 42074, - 42060, 42064, 42065, 42062, 42063, 42061, 42058, 42059, 42071, 42072, - 42069, 42070, 41727, 41721, 41720, 41730, 41729, 41728, 41726, 41723, - 41722, 41711, 41714, 41715, 41713, 41712, 41709, 41710, 41718, 41719, - 41716, 41717, 41733, 41736, 41735, 41734, 41731, 41732, 41725, 41724, - 41363, 41352, 41353, 41351, 41366, 41365, 41364, 41361, 41362, 41343, - 41346, 41347, 41345, 41344, 41341, 41342, 41349, 41350, 41348, 41359, - 41360, 41358, 41356, 41357, 41354, 41355, 41036, 41028, 41029, 41027, - 41039, 41038, 41037, 41034, 41035, 41018, 41021, 41022, 41020, 41019, - 41016, 41017, 41025, 41026, 41023, 41024, 41032, 41033, 41030, 41031, - 41042, 41045, 41044, 41043, 41040, 41041, 41998, 41990, 41991, 41988, - 41989, 42001, 42000, 41999, 41996, 41997, 41982, 41986, 41987, 41984, - 41985, 41983, 41980, 41981, 41994, 41995, 41992, 41993, 42004, 42007, - 42006, 42005, 42002, 42003, 42115, 42107, 42108, 42105, 42106, 42118, - 42117, 42116, 42113, 42114, 42099, 42103, 42104, 42101, 42102, 42100, - 42097, 42098, 42111, 42112, 42109, 42110, 42121, 42124, 42123, 42122, - 42119, 42120, 41866, 41855, 41854, 41869, 41868, 41867, 41864, 41865, - 41862, 41863, 41860, 41861, 41858, 41859, 41856, 41857, 41872, 41875, - 41874, 41873, 41870, 41871, 41853, 41852, 41942, 41931, 41932, 41930, - 41945, 41944, 41943, 41940, 41941, 41938, 41939, 41937, 41928, 41929, - 41926, 41927, 41935, 41936, 41933, 41934, 41948, 41951, 41950, 41949, - 41946, 41947, 41772, 41775, 41776, 41774, 41773, 41770, 41771, 41786, - 41787, 41785, 41779, 41780, 41777, 41778, 41783, 41784, 41781, 41782, - 41790, 41791, 41788, 41789, 41794, 41797, 41796, 41795, 41792, 41793, - 41916, 41904, 41905, 41903, 41919, 41918, 41917, 41914, 41915, 41901, - 41902, 41899, 41900, 41912, 41913, 41910, 41911, 41908, 41909, 41906, - 41907, 41922, 41925, 41924, 41923, 41920, 41921, 41760, 41749, 41750, - 41748, 41763, 41762, 41761, 41758, 41759, 41739, 41742, 41743, 41741, - 41740, 41737, 41738, 41756, 41757, 41755, 41746, 41747, 41744, 41745, - 41753, 41754, 41751, 41752, 41766, 41769, 41768, 41767, 41764, 41765, - 41266, 41255, 41256, 41253, 41254, 41269, 41268, 41267, 41264, 41265, - 41244, 41247, 41248, 41246, 41245, 41242, 41243, 41262, 41263, 41261, - 41251, 41252, 41249, 41250, 41259, 41260, 41257, 41258, 41203, 41202, - 41188, 41192, 41193, 41190, 41191, 41189, 41186, 41187, 41196, 41197, - 41194, 41195, 41200, 41201, 41198, 41199, 41206, 41209, 41208, 41207, - 41204, 41205, 41212, 41215, 41214, 41213, 41210, 41211, 40981, 41605, - 41606, 41604, 41611, 41612, 41610, 41608, 41609, 41607, 41602, 41603, - 41600, 41601, 42079, 42083, 42084, 42081, 42082, 42080, 42077, 42078, - 42089, 42090, 42087, 42088, 42093, 42096, 42095, 42094, 42091, 42092, - 42086, 42085, 41815, 41803, 41804, 41802, 41818, 41817, 41816, 41813, - 41814, 41800, 41801, 41798, 41799, 41811, 41812, 41809, 41810, 41807, - 41808, 41805, 41806, 41821, 41824, 41823, 41822, 41819, 41820, 41636, - 41625, 41626, 41624, 41639, 41638, 41637, 41634, 41635, 41615, 41618, - 41619, 41617, 41616, 41613, 41614, 41696, 41697, 41695, 41693, 41694, - 41692, 41682, 41686, 41687, 41684, 41685, 41683, 41680, 41681, 41690, - 41691, 41688, 41689, 41699, 41702, 41701, 41700, 41698, 41705, 41708, - 41707, 41706, 41703, 41704, 41632, 41633, 41631, 41622, 41623, 41620, - 41621, 41629, 41630, 41627, 41628, 41642, 41645, 41644, 41643, 41640, - 41641, 40973, 40974, 40972, 40970, 40971, 40968, 40969, 40977, 40978, - 40975, 40976, 40980, 40979, 9775, 129664, 8959, 10853, 10632, 10634, - 10814, 10852, 10631, 10633, 10783, 10784, 10785, 10625, 10626, 72262, - 72256, 72253, 72252, 72254, 72251, 72250, 72261, 72255, 72243, 72215, - 72214, 72230, 72229, 72220, 72219, 72242, 72204, 72203, 72207, 72216, - 72211, 72221, 72238, 72239, 72240, 72228, 72227, 72213, 72212, 72218, - 72217, 72225, 72224, 72209, 72208, 72206, 72205, 72223, 72222, 72231, - 72232, 72233, 72241, 72210, 72236, 72226, 72235, 72237, 72234, 72192, - 72259, 72258, 72260, 72257, 72248, 72245, 72246, 72247, 72244, 72249, - 72263, 72202, 72199, 72200, 72198, 72197, 72195, 72194, 72201, 72196, - 72193, 129427, 65279, 8204, 8203, 8205, 11234, 129296, 118593, 118584, - 118585, 118591, 118580, 118589, 118528, 118540, 118531, 118543, 118559, - 118529, 118541, 118532, 118544, 118592, 118573, 118569, 118568, 118581, - 118586, 118561, 118582, 118583, 118566, 118538, 118550, 118555, 118556, - 118535, 118547, 118537, 118549, 118553, 118558, 118534, 118546, 118572, - 118562, 118571, 118554, 118533, 118545, 118587, 118588, 118530, 118542, - 118552, 118563, 118539, 118551, 118557, 118536, 118548, 118579, 118570, - 118560, 118567, 118565, 118564, 118590, 118576, 118577, 118578, 118619, - 118639, 118637, 118645, 118721, 118611, 118635, 118659, 118626, 118623, - 118625, 118624, 118622, 118638, 118612, 118660, 118608, 118609, 118657, - 118719, 118695, 118699, 118698, 118697, 118696, 118722, 118720, 118703, - 118708, 118707, 118706, 118705, 118704, 118610, 118620, 118723, 118616, - 118617, 118640, 118672, 118636, 118658, 118652, 118651, 118649, 118650, - 118648, 118646, 118647, 118644, 118643, 118641, 118642, 118653, 118656, - 118654, 118655, 118662, 118670, 118664, 118666, 118669, 118663, 118665, - 118671, 118667, 118668, 118673, 118614, 118615, 118618, 118685, 118687, - 118684, 118683, 118686, 118681, 118680, 118709, 118713, 118711, 118716, - 118717, 118714, 118715, 118712, 118718, 118710, 118676, 118679, 118690, - 118688, 118693, 118694, 118691, 118692, 118689, 118675, 118677, 118678, - 118674, 118701, 118702, 118700, 118682, 118632, 118631, 118634, 118633, - 118628, 118627, 118630, 118629, 118613, 118621, 118661, 118596, 118597, - 118594, 118595, 118598, 129503, 983264, 983222, 983221, 983223, + 129638, 129633, 8891, 94196, 94197, 94198, 129393, 128155, 165, 69292, + 69291, 69293, 69256, 69255, 69254, 69281, 69268, 69259, 69248, 69271, + 69289, 69286, 69287, 69257, 69278, 69277, 69279, 69276, 69280, 69296, + 69282, 69251, 69250, 69266, 69265, 69267, 69253, 69252, 69269, 69274, + 69275, 69284, 69285, 69272, 69258, 69288, 69297, 69263, 69260, 69270, + 69262, 69261, 69249, 69283, 69273, 69264, 42139, 42149, 42173, 42172, + 42132, 42147, 42179, 42174, 42148, 42169, 42150, 42134, 42166, 42135, + 42145, 42143, 42137, 42175, 42157, 42154, 42167, 42165, 42163, 42130, + 42182, 42129, 42171, 42138, 42140, 42136, 42131, 42151, 42164, 42181, + 42142, 42156, 42170, 42176, 42178, 42160, 42133, 42144, 42152, 42159, + 42161, 42158, 42141, 42146, 42177, 42180, 42155, 42162, 42128, 42168, + 42153, 41070, 41059, 41060, 41058, 41073, 41072, 41071, 41068, 41069, + 41066, 41067, 41065, 41048, 41052, 41053, 41050, 41051, 41049, 41046, + 41047, 41056, 41057, 41054, 41055, 41063, 41064, 41061, 41062, 41076, + 41077, 41074, 41075, 41006, 40995, 40996, 40994, 41009, 41008, 41007, + 41004, 41005, 41002, 41003, 41001, 40984, 40988, 40989, 40986, 40987, + 40985, 40982, 40983, 40992, 40993, 40990, 40991, 40999, 41000, 40997, + 40998, 41012, 41015, 41014, 41013, 41010, 41011, 41842, 41831, 41832, + 41829, 41830, 41845, 41844, 41843, 41841, 41827, 41828, 41825, 41826, + 41839, 41840, 41837, 41838, 41835, 41836, 41833, 41834, 41848, 41851, + 41850, 41849, 41846, 41847, 41670, 41659, 41660, 41658, 41673, 41672, + 41671, 41668, 41669, 41666, 41667, 41665, 41648, 41652, 41653, 41650, + 41651, 41649, 41646, 41647, 41656, 41657, 41654, 41655, 41663, 41664, + 41661, 41662, 41676, 41679, 41678, 41677, 41674, 41675, 41293, 41282, + 41283, 41281, 41296, 41295, 41294, 41291, 41292, 41272, 41275, 41276, + 41274, 41273, 41270, 41271, 41289, 41290, 41288, 41279, 41280, 41277, + 41278, 41286, 41287, 41284, 41285, 41238, 41228, 41227, 41241, 41240, + 41239, 41236, 41237, 41218, 41221, 41222, 41220, 41219, 41216, 41217, + 41234, 41235, 41233, 41225, 41226, 41223, 41224, 41231, 41232, 41229, + 41230, 41174, 41175, 41173, 41171, 41172, 41169, 41170, 41167, 41168, + 41165, 41166, 41184, 41185, 41182, 41183, 41178, 41181, 41180, 41179, + 41176, 41177, 41494, 41496, 41497, 41495, 41492, 41493, 41516, 41504, + 41505, 41502, 41503, 41519, 41518, 41517, 41514, 41515, 41500, 41501, + 41498, 41499, 41512, 41513, 41510, 41511, 41508, 41509, 41506, 41507, + 41460, 41448, 41449, 41446, 41447, 41463, 41462, 41461, 41458, 41459, + 41436, 41440, 41441, 41438, 41439, 41437, 41434, 41435, 41444, 41445, + 41442, 41443, 41456, 41457, 41454, 41455, 41452, 41453, 41450, 41451, + 41584, 41583, 41582, 41389, 41379, 41380, 41378, 41392, 41391, 41390, + 41387, 41388, 41369, 41372, 41373, 41371, 41370, 41367, 41368, 41385, + 41386, 41384, 41382, 41383, 41381, 41376, 41377, 41374, 41375, 41395, + 41398, 41397, 41396, 41393, 41394, 41125, 41117, 41118, 41116, 41128, + 41127, 41126, 41123, 41124, 41107, 41110, 41111, 41109, 41108, 41105, + 41106, 41114, 41115, 41112, 41113, 41121, 41122, 41119, 41120, 41130, + 41133, 41132, 41131, 41129, 41336, 41334, 41335, 41333, 41332, 41340, + 41338, 41339, 41337, 41322, 41326, 41327, 41324, 41325, 41323, 41320, + 41321, 41330, 41331, 41328, 41329, 41556, 41557, 41554, 41555, 41563, + 41564, 41562, 41544, 41548, 41549, 41546, 41547, 41545, 41542, 41543, + 41552, 41553, 41550, 41551, 41560, 41561, 41558, 41559, 41591, 41592, + 41589, 41590, 41598, 41599, 41597, 41587, 41588, 41585, 41586, 41595, + 41596, 41593, 41594, 40962, 40960, 983243, 40966, 40967, 40964, 40965, + 40963, 40961, 42025, 42017, 42018, 42016, 42028, 42027, 42026, 42023, + 42024, 42010, 42014, 42015, 42012, 42013, 42011, 42008, 42009, 42021, + 42022, 42019, 42020, 42031, 42032, 42029, 42030, 41970, 41962, 41963, + 41960, 41961, 41973, 41972, 41971, 41968, 41969, 41954, 41958, 41959, + 41956, 41957, 41955, 41952, 41953, 41966, 41967, 41964, 41965, 41976, + 41979, 41978, 41977, 41974, 41975, 41488, 41476, 41477, 41475, 41491, + 41490, 41489, 41486, 41487, 41466, 41469, 41470, 41468, 41467, 41464, + 41465, 41473, 41474, 41471, 41472, 41484, 41485, 41482, 41483, 41480, + 41481, 41478, 41479, 41424, 41413, 41414, 41411, 41412, 41427, 41426, + 41425, 41422, 41423, 41420, 41421, 41419, 41401, 41405, 41406, 41403, + 41404, 41402, 41399, 41400, 41409, 41410, 41407, 41408, 41417, 41418, + 41415, 41416, 41430, 41433, 41432, 41431, 41428, 41429, 41538, 41527, + 41528, 41526, 41541, 41540, 41539, 41536, 41537, 41534, 41535, 41533, + 41524, 41525, 41522, 41523, 41531, 41532, 41529, 41530, 41521, 41520, + 41157, 41147, 41148, 41145, 41146, 41160, 41159, 41158, 41155, 41156, + 41136, 41139, 41140, 41138, 41137, 41134, 41135, 41143, 41144, 41141, + 41142, 41151, 41152, 41149, 41150, 41163, 41164, 41161, 41162, 41154, + 41153, 41080, 41083, 41084, 41082, 41081, 41078, 41079, 41087, 41088, + 41085, 41086, 41091, 41092, 41089, 41090, 41095, 41098, 41097, 41096, + 41093, 41094, 41101, 41104, 41103, 41102, 41099, 41100, 41299, 41302, + 41301, 41300, 41297, 41298, 41312, 41313, 41311, 41305, 41306, 41303, + 41304, 41309, 41310, 41307, 41308, 41316, 41319, 41318, 41317, 41314, + 41315, 41574, 41572, 41573, 41580, 41581, 41579, 41566, 41567, 41565, + 41570, 41571, 41568, 41569, 41577, 41578, 41575, 41576, 42048, 42042, + 42041, 42051, 42050, 42049, 42047, 42035, 42039, 42040, 42037, 42038, + 42036, 42033, 42034, 42045, 42046, 42043, 42044, 42054, 42057, 42056, + 42055, 42052, 42053, 41881, 41882, 41880, 41878, 41879, 41876, 41877, + 41885, 41886, 41883, 41884, 41889, 41892, 41891, 41890, 41887, 41888, + 41895, 41898, 41897, 41896, 41893, 41894, 42075, 42067, 42068, 42066, + 42076, 42073, 42074, 42060, 42064, 42065, 42062, 42063, 42061, 42058, + 42059, 42071, 42072, 42069, 42070, 41727, 41721, 41720, 41730, 41729, + 41728, 41726, 41723, 41722, 41711, 41714, 41715, 41713, 41712, 41709, + 41710, 41718, 41719, 41716, 41717, 41733, 41736, 41735, 41734, 41731, + 41732, 41725, 41724, 41363, 41352, 41353, 41351, 41366, 41365, 41364, + 41361, 41362, 41343, 41346, 41347, 41345, 41344, 41341, 41342, 41349, + 41350, 41348, 41359, 41360, 41358, 41356, 41357, 41354, 41355, 41036, + 41028, 41029, 41027, 41039, 41038, 41037, 41034, 41035, 41018, 41021, + 41022, 41020, 41019, 41016, 41017, 41025, 41026, 41023, 41024, 41032, + 41033, 41030, 41031, 41042, 41045, 41044, 41043, 41040, 41041, 41998, + 41990, 41991, 41988, 41989, 42001, 42000, 41999, 41996, 41997, 41982, + 41986, 41987, 41984, 41985, 41983, 41980, 41981, 41994, 41995, 41992, + 41993, 42004, 42007, 42006, 42005, 42002, 42003, 42115, 42107, 42108, + 42105, 42106, 42118, 42117, 42116, 42113, 42114, 42099, 42103, 42104, + 42101, 42102, 42100, 42097, 42098, 42111, 42112, 42109, 42110, 42121, + 42124, 42123, 42122, 42119, 42120, 41866, 41855, 41854, 41869, 41868, + 41867, 41864, 41865, 41862, 41863, 41860, 41861, 41858, 41859, 41856, + 41857, 41872, 41875, 41874, 41873, 41870, 41871, 41853, 41852, 41942, + 41931, 41932, 41930, 41945, 41944, 41943, 41940, 41941, 41938, 41939, + 41937, 41928, 41929, 41926, 41927, 41935, 41936, 41933, 41934, 41948, + 41951, 41950, 41949, 41946, 41947, 41772, 41775, 41776, 41774, 41773, + 41770, 41771, 41786, 41787, 41785, 41779, 41780, 41777, 41778, 41783, + 41784, 41781, 41782, 41790, 41791, 41788, 41789, 41794, 41797, 41796, + 41795, 41792, 41793, 41916, 41904, 41905, 41903, 41919, 41918, 41917, + 41914, 41915, 41901, 41902, 41899, 41900, 41912, 41913, 41910, 41911, + 41908, 41909, 41906, 41907, 41922, 41925, 41924, 41923, 41920, 41921, + 41760, 41749, 41750, 41748, 41763, 41762, 41761, 41758, 41759, 41739, + 41742, 41743, 41741, 41740, 41737, 41738, 41756, 41757, 41755, 41746, + 41747, 41744, 41745, 41753, 41754, 41751, 41752, 41766, 41769, 41768, + 41767, 41764, 41765, 41266, 41255, 41256, 41253, 41254, 41269, 41268, + 41267, 41264, 41265, 41244, 41247, 41248, 41246, 41245, 41242, 41243, + 41262, 41263, 41261, 41251, 41252, 41249, 41250, 41259, 41260, 41257, + 41258, 41203, 41202, 41188, 41192, 41193, 41190, 41191, 41189, 41186, + 41187, 41196, 41197, 41194, 41195, 41200, 41201, 41198, 41199, 41206, + 41209, 41208, 41207, 41204, 41205, 41212, 41215, 41214, 41213, 41210, + 41211, 40981, 41605, 41606, 41604, 41611, 41612, 41610, 41608, 41609, + 41607, 41602, 41603, 41600, 41601, 42079, 42083, 42084, 42081, 42082, + 42080, 42077, 42078, 42089, 42090, 42087, 42088, 42093, 42096, 42095, + 42094, 42091, 42092, 42086, 42085, 41815, 41803, 41804, 41802, 41818, + 41817, 41816, 41813, 41814, 41800, 41801, 41798, 41799, 41811, 41812, + 41809, 41810, 41807, 41808, 41805, 41806, 41821, 41824, 41823, 41822, + 41819, 41820, 41636, 41625, 41626, 41624, 41639, 41638, 41637, 41634, + 41635, 41615, 41618, 41619, 41617, 41616, 41613, 41614, 41696, 41697, + 41695, 41693, 41694, 41692, 41682, 41686, 41687, 41684, 41685, 41683, + 41680, 41681, 41690, 41691, 41688, 41689, 41699, 41702, 41701, 41700, + 41698, 41705, 41708, 41707, 41706, 41703, 41704, 41632, 41633, 41631, + 41622, 41623, 41620, 41621, 41629, 41630, 41627, 41628, 41642, 41645, + 41644, 41643, 41640, 41641, 40973, 40974, 40972, 40970, 40971, 40968, + 40969, 40977, 40978, 40975, 40976, 40980, 40979, 9775, 129664, 8959, + 10853, 10632, 10634, 10814, 10852, 10631, 10633, 10783, 10784, 10785, + 10625, 10626, 72262, 72256, 72253, 72252, 72254, 72251, 72250, 72261, + 72255, 72243, 72215, 72214, 72230, 72229, 72220, 72219, 72242, 72204, + 72203, 72207, 72216, 72211, 72221, 72238, 72239, 72240, 72228, 72227, + 72213, 72212, 72218, 72217, 72225, 72224, 72209, 72208, 72206, 72205, + 72223, 72222, 72231, 72232, 72233, 72241, 72210, 72236, 72226, 72235, + 72237, 72234, 72192, 72259, 72258, 72260, 72257, 72248, 72245, 72246, + 72247, 72244, 72249, 72263, 72202, 72199, 72200, 72198, 72197, 72195, + 72194, 72201, 72196, 72193, 129427, 65279, 8204, 8203, 8205, 11234, + 129296, 118593, 118584, 118585, 118591, 118580, 118589, 118528, 118540, + 118531, 118543, 118559, 118529, 118541, 118532, 118544, 118592, 118573, + 118569, 118568, 118581, 118586, 118561, 118582, 118583, 118566, 118538, + 118550, 118555, 118556, 118535, 118547, 118537, 118549, 118553, 118558, + 118534, 118546, 118572, 118562, 118571, 118554, 118533, 118545, 118587, + 118588, 118530, 118542, 118552, 118563, 118539, 118551, 118557, 118536, + 118548, 118579, 118570, 118560, 118567, 118565, 118564, 118590, 118576, + 118577, 118578, 118619, 118639, 118637, 118645, 118721, 118611, 118635, + 118659, 118626, 118623, 118625, 118624, 118622, 118638, 118612, 118660, + 118608, 118609, 118657, 118719, 118695, 118699, 118698, 118697, 118696, + 118722, 118720, 118703, 118708, 118707, 118706, 118705, 118704, 118610, + 118620, 118723, 118616, 118617, 118640, 118672, 118636, 118658, 118652, + 118651, 118649, 118650, 118648, 118646, 118647, 118644, 118643, 118641, + 118642, 118653, 118656, 118654, 118655, 118662, 118670, 118664, 118666, + 118669, 118663, 118665, 118671, 118667, 118668, 118673, 118614, 118615, + 118618, 118685, 118687, 118684, 118683, 118686, 118681, 118680, 118709, + 118713, 118711, 118716, 118717, 118714, 118715, 118712, 118718, 118710, + 118676, 118679, 118690, 118688, 118693, 118694, 118691, 118692, 118689, + 118675, 118677, 118678, 118674, 118701, 118702, 118700, 118682, 118632, + 118631, 118634, 118633, 118628, 118627, 118630, 118629, 118613, 118621, + 118661, 118596, 118597, 118594, 118595, 118598, 129503, 983264, 983222, + 983221, 983223, }; #define DAWG_CODEPOINT_TO_POS_SHIFT 8 -#define DAWG_CODEPOINT_TO_POS_NOTFOUND 40951 +#define DAWG_CODEPOINT_TO_POS_NOTFOUND 35536 static const unsigned char dawg_codepoint_to_pos_index1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, @@ -13296,24 +12818,23 @@ static const unsigned char dawg_codepoint_to_pos_index1[] = { 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 66, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 67, 68, 69, 70, 71, 72, - 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, - 107, 108, 109, 110, 111, 52, 52, 52, 52, 52, 52, 52, 52, 52, 112, 113, - 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 136, 52, 52, 52, 52, 52, 52, 137, 138, 139, 140, 52, 141, 142, 143, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 144, 145, 146, 147, 148, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 52, 52, 52, 52, 52, 52, 52, 52, 52, 110, 111, 112, + 113, 114, 115, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 116, 117, 118, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 119, 52, 52, 52, 52, + 52, 52, 120, 121, 122, 123, 52, 124, 125, 126, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 149, 150, 151, 152, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 153, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, - 166, 167, 168, 52, 52, 52, 52, 169, 170, 171, 172, 52, 173, 174, 52, 175, - 176, 177, 52, 52, 178, 179, 180, 52, 181, 182, 183, 184, 185, 186, 187, - 188, 189, 190, 191, 192, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 127, 128, 129, 52, 52, 130, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 131, 132, 133, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 134, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 52, + 52, 52, 52, 150, 151, 152, 153, 52, 154, 155, 156, 157, 158, 159, 52, 52, + 160, 161, 162, 52, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, @@ -13326,7 +12847,6 @@ static const unsigned char dawg_codepoint_to_pos_index1[] = { 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 193, 194, 195, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, @@ -13483,8 +13003,9 @@ static const unsigned char dawg_codepoint_to_pos_index1[] = { 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 196, 197, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 175, + 176, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, @@ -13497,8 +13018,8 @@ static const unsigned char dawg_codepoint_to_pos_index1[] = { 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 198, 199, 200, 201, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 177, 178, 179, 180, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, @@ -13526,5003 +13047,4466 @@ static const unsigned char dawg_codepoint_to_pos_index1[] = { 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, }; static const unsigned short dawg_codepoint_to_pos_index2[] = { - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 34087, 17354, 31745, 29447, 11088, 31317, 368, 1052, 24153, - 31969, 2527, 31568, 8453, 20460, 17532, 33926, 10872, 10857, 10869, - 10866, 10851, 10848, 10863, 10860, 10875, 10854, 8056, 32578, 24396, - 16792, 18147, 31743, 8452, 22741, 22787, 22797, 22813, 22828, 22873, - 22877, 22894, 22908, 22937, 22942, 22954, 22974, 22983, 22999, 23049, - 23057, 23060, 23081, 23105, 23134, 23173, 23184, 23193, 23196, 23210, - 24115, 31870, 31983, 6819, 25161, 18118, 23302, 23352, 23372, 23395, - 23431, 23490, 23498, 23516, 23535, 23572, 23578, 23592, 23631, 23644, - 23668, 23725, 23736, 23742, 23780, 23822, 23895, 23943, 23957, 23966, - 23974, 23990, 24056, 38833, 31932, 37296, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 29440, - 20815, 6131, 31610, 9951, 39398, 4937, 32560, 10839, 8647, 17433, 24163, - 29422, 33877, 31793, 26847, 10554, 31580, 34712, 34711, 4, 27575, 31465, - 27581, 6127, 34715, 25814, 32030, 38965, 38963, 38971, 20818, 22770, - 22771, 22759, 22773, 22752, 22754, 22775, 22798, 22859, 22860, 22831, - 22846, 22924, 22925, 22914, 22912, 22870, 22997, 23038, 23039, 23006, - 23024, 23013, 28287, 23022, 23165, 23166, 23135, 23138, 23205, 23126, - 23812, 23333, 23334, 23322, 23336, 23313, 23315, 23339, 23373, 23464, - 23465, 23434, 23449, 23553, 23554, 23536, 23538, 23489, 23663, 23707, - 23708, 23669, 23693, 23676, 10999, 23691, 23930, 23933, 23898, 23901, - 23985, 23844, 23976, 22757, 23320, 22742, 23303, 22767, 23330, 22802, - 23378, 22800, 23375, 22806, 23383, 22801, 23377, 22820, 23403, 22817, - 23397, 22849, 23453, 22862, 23467, 22841, 23444, 22856, 23461, 22840, - 23443, 22879, 23500, 22882, 23503, 22886, 23508, 22878, 23499, 22897, - 23519, 22904, 23528, 22918, 23550, 22916, 23545, 22927, 23556, 22921, - 23547, 22911, 23419, 23222, 24010, 22938, 23573, 22943, 23579, 23591, - 22968, 23609, 22964, 23596, 22966, 23599, 22961, 23606, 22963, 23617, - 22992, 23656, 22984, 23645, 22986, 23649, 23664, 22872, 23472, 23002, - 23686, 23041, 23710, 23019, 23682, 23223, 24013, 23069, 23759, 23061, - 23743, 23062, 23745, 23087, 23781, 23086, 23787, 23084, 23785, 23082, - 23783, 23106, 23823, 23109, 23827, 23116, 23839, 23162, 23927, 23154, - 23917, 23168, 23935, 23169, 23926, 23145, 23908, 23159, 23922, 23185, - 23958, 23197, 23975, 23198, 23213, 23991, 23216, 23997, 23212, 23994, - 23623, 23361, 22793, 22788, 23353, 23129, 23847, 23044, 22804, 23381, - 22779, 22825, 22821, 23398, 23852, 23078, 23104, 23043, 22874, 23493, - 22885, 22892, 23534, 22935, 22928, 22953, 23590, 23595, 23619, 23123, - 22990, 23650, 23005, 23031, 23700, 23046, 23722, 23055, 23733, 23299, - 23130, 23848, 22871, 23239, 23835, 23115, 23836, 23114, 23147, 23910, - 23172, 23177, 23202, 23980, 23219, 24000, 22864, 22866, 23482, 23487, - 23291, 23131, 23849, 23238, 23292, 23293, 23294, 23231, 23241, 22827, - 22815, 23425, 22973, 22962, 23630, 22998, 22987, 23667, 22765, 23328, - 22915, 23537, 23012, 23675, 23137, 23900, 23142, 23905, 23141, 23904, - 23139, 23902, 23140, 23903, 23884, 22753, 23314, 22749, 23310, 22777, - 23342, 22887, 23509, 22880, 23501, 22944, 23580, 23020, 23689, 23021, - 23690, 22865, 23484, 23575, 22826, 22814, 23424, 22881, 23502, 22906, - 23192, 22991, 23655, 22755, 23316, 22776, 23341, 23023, 23692, 22751, - 23312, 22772, 23335, 22845, 23448, 22861, 23466, 22909, 23544, 22926, - 23555, 23018, 23681, 23040, 23709, 23066, 23749, 23071, 23760, 23144, - 23907, 23167, 23934, 23085, 23786, 23108, 23825, 23209, 23989, 22898, - 23520, 22988, 23402, 23048, 23724, 23221, 24004, 22748, 23309, 22829, - 23432, 23014, 23677, 23027, 23696, 23015, 23678, 23016, 23679, 23206, - 23986, 23598, 23648, 23826, 23416, 23429, 23741, 22774, 22807, 23384, - 22956, 23110, 23794, 23999, 22888, 23510, 22794, 23171, 23125, 22863, - 23469, 22941, 23577, 23098, 23739, 23073, 23763, 23208, 23984, 23876, - 23344, 23877, 23360, 23713, 23376, 23399, 23407, 23772, 23803, 23807, - 23719, 23767, 23769, 23391, 23417, 23507, 23810, 23268, 23514, 23779, - 23853, 23527, 23533, 23557, 23568, 23246, 23604, 23593, 23612, 23620, - 23879, 23880, 23639, 23652, 23661, 23279, 23364, 23253, 23390, 23735, - 23864, 23867, 23870, 23754, 23755, 23750, 23770, 23255, 23247, 23800, - 23475, 23418, 23820, 23479, 23871, 23838, 23896, 23937, 23950, 23873, - 23888, 23881, 23286, 24003, 23993, 23481, 23483, 23295, 23297, 23236, - 23288, 23234, 23266, 23389, 23269, 23275, 23574, 23886, 23248, 23738, - 23296, 23240, 23426, 23412, 23427, 23891, 23840, 23890, 23496, 23627, - 23628, 23233, 23235, 23854, 23855, 27905, 27906, 27914, 27936, 27963, - 27966, 27861, 27983, 27985, 27834, 27777, 27991, 27688, 27842, 27844, - 27820, 27793, 27840, 27822, 27846, 27993, 27779, 27769, 6060, 27997, - 27823, 27687, 27792, 27818, 27809, 27815, 27814, 27990, 27800, 27721, - 27720, 27992, 27778, 27833, 27831, 4930, 11194, 32162, 29978, 33836, - 11253, 27847, 27770, 27904, 27916, 27943, 27984, 27940, 27784, 27799, - 27827, 27812, 27789, 27999, 27998, 27996, 27994, 27776, 27805, 27817, - 27807, 27810, 27811, 27830, 27829, 27828, 27813, 27839, 27690, 27790, - 27691, 27791, 27849, 27832, 27806, 8264, 8062, 8146, 8428, 8370, 8390, - 8072, 8171, 8168, 8277, 8415, 8197, 8078, 8443, 8196, 8198, 8080, 8280, - 8436, 8083, 8398, 8084, 8265, 8063, 8355, 8411, 8349, 8279, 8352, 8438, - 8202, 8395, 8379, 8394, 8399, 8174, 8170, 8414, 8085, 8148, 8388, 8442, - 8075, 8283, 8079, 8147, 8074, 8281, 8432, 8375, 8369, 8199, 8431, 8419, - 8367, 8418, 8366, 8406, 8282, 8422, 8426, 8450, 8444, 8185, 8266, 8064, - 8274, 8273, 8276, 8275, 8076, 8211, 8195, 8348, 8381, 8278, 8071, 8356, - 8437, 8269, 8402, 8353, 8212, 8449, 8344, 8403, 8401, 8407, 8173, 8068, - 8190, 8417, 8179, 8178, 8182, 8183, 8191, 8180, 8189, 8296, 8304, 8309, - 8317, 8320, 8302, 8334, 8336, 8338, 8323, 8326, 8341, 8342, 18332, 18596, - 18227, 18463, 18414, 18410, 18321, 18578, 40951, 40951, 18653, 18603, - 18604, 18602, 18658, 18335, 40951, 40951, 40951, 40951, 18618, 18348, - 18225, 18201, 18258, 18248, 18272, 40951, 18304, 40951, 18319, 18294, - 18510, 18204, 18331, 18329, 18327, 18249, 18333, 18228, 18325, 18259, - 18328, 18334, 18336, 18337, 18338, 18295, 18324, 18305, 40951, 18307, - 18326, 18310, 18322, 18330, 18323, 18274, 18264, 18315, 18460, 18475, - 18500, 18519, 18530, 18435, 18595, 18593, 18465, 18466, 18597, 18476, - 18590, 18501, 18541, 18598, 18599, 18600, 18601, 18568, 18581, 18582, - 18592, 18588, 18591, 18521, 18579, 18594, 18580, 18543, 18506, 18526, - 18577, 18539, 18567, 18343, 18655, 18614, 18622, 18620, 18621, 18430, - 18431, 18395, 18406, 18462, 18405, 18587, 18408, 18464, 18407, 18542, - 18404, 18585, 8528, 8622, 8506, 8600, 8502, 8596, 8500, 8594, 8498, 8592, - 8527, 8621, 8497, 8591, 18394, 18433, 18411, 18409, 18344, 18412, 18434, - 18309, 18589, 18339, 18308, 18586, 18432, 18341, 18342, 18340, 10218, - 10220, 10167, 10206, 10142, 10171, 10158, 10277, 10290, 10113, 10116, - 10130, 10245, 10215, 10258, 10175, 10143, 10159, 10291, 10200, 10179, - 10217, 10284, 10280, 10213, 10256, 10230, 10181, 10197, 10186, 10249, - 10117, 10192, 10195, 10127, 10137, 10199, 10207, 10133, 10160, 10263, - 10261, 10211, 10274, 10266, 10180, 10279, 10271, 10302, 10318, 10492, - 10359, 10338, 10376, 10485, 10481, 10372, 10434, 10389, 10340, 10356, - 10345, 10415, 10420, 10351, 10354, 10452, 10463, 10358, 10366, 10458, - 10319, 10441, 10439, 10370, 10475, 10444, 10339, 10480, 10472, 10377, - 10379, 10326, 10365, 10468, 10330, 10317, 10478, 10491, 10408, 10414, - 10455, 10404, 10374, 10436, 10334, 10250, 10416, 10273, 10474, 10226, - 10385, 10112, 10406, 10222, 10381, 10155, 10314, 10223, 10382, 10246, - 10405, 10120, 10424, 10289, 10490, 10228, 10387, 10229, 10388, 10141, - 10467, 10121, 10429, 10251, 10417, 10253, 10419, 10243, 10402, 10523, - 8139, 8133, 8136, 8134, 8135, 8089, 8142, 10257, 10435, 10270, 10448, - 10193, 10352, 10202, 10361, 10204, 10363, 10201, 10360, 10286, 10487, - 10282, 10483, 10231, 10390, 10233, 10392, 10234, 10393, 10153, 10312, - 10188, 10347, 10296, 10496, 10118, 10421, 10149, 10308, 10196, 10355, - 10129, 10454, 10268, 10446, 10269, 10447, 10208, 10367, 10295, 10495, - 10162, 10321, 10163, 10322, 10259, 10437, 10146, 10305, 10147, 10306, - 10299, 10287, 10488, 10232, 10391, 10184, 10343, 10191, 10350, 10190, - 10349, 10244, 10403, 10198, 10357, 10423, 10145, 10304, 10144, 10303, - 10294, 10494, 10219, 10378, 10254, 10432, 10255, 10433, 10285, 10486, - 10281, 10482, 10148, 10307, 10216, 10375, 10214, 10373, 10252, 10418, - 10151, 10310, 10152, 10311, 10194, 10353, 10140, 10466, 10139, 10465, - 10138, 10464, 10161, 10320, 10203, 10362, 10275, 10476, 10205, 10364, - 10209, 10368, 10210, 10369, 10237, 10396, 10236, 10395, 10242, 10401, - 10235, 10394, 10238, 10397, 10239, 10398, 10240, 10399, 10241, 10400, - 10125, 10428, 10185, 10344, 10114, 10409, 10126, 10431, 10272, 10473, - 10293, 10493, 10292, 10471, 10150, 10309, 10182, 10341, 10187, 10346, - 10119, 10422, 10260, 10438, 10189, 10348, 10173, 10332, 10177, 10336, - 10183, 10342, 40951, 2445, 2452, 2436, 2458, 2432, 2456, 2433, 2434, - 2423, 2455, 2451, 2448, 2450, 2428, 2440, 2457, 2438, 2435, 2426, 2453, - 2424, 2454, 2443, 2447, 2427, 2442, 2437, 2431, 2444, 2446, 2422, 2430, - 2429, 2425, 2441, 2439, 2459, 2449, 40951, 40951, 2509, 2421, 2462, 2461, - 2460, 2513, 2420, 2482, 2485, 2495, 2473, 2501, 2469, 2499, 2470, 2471, - 2484, 2498, 2494, 2491, 2493, 2465, 2477, 2500, 2475, 2472, 2463, 2496, - 2488, 2497, 2480, 2487, 2464, 2479, 2474, 2468, 2481, 2486, 2483, 2467, - 2466, 2490, 2478, 2476, 2502, 2492, 2503, 2489, 2512, 2510, 40951, 40951, - 32017, 24176, 2511, 40951, 19805, 19808, 19809, 19816, 19817, 19813, - 19820, 19818, 19803, 19815, 19812, 19796, 19797, 19798, 19806, 19811, - 19804, 19793, 19801, 19802, 19799, 19800, 19795, 19807, 19810, 19814, - 19821, 19822, 19794, 19819, 19900, 19915, 19904, 19902, 19903, 19905, - 19917, 19913, 19908, 19909, 19906, 19907, 19911, 19901, 19919, 19925, - 19912, 19922, 19914, 19916, 19923, 19899, 19898, 19924, 19910, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 19823, 19830, 19873, - 19871, 19848, 19879, 19853, 19850, 19865, 19890, 19839, 19833, 19875, - 19845, 19877, 19844, 19851, 19855, 19829, 19841, 19836, 19843, 19869, - 19846, 19863, 19857, 19867, 40951, 40951, 40951, 40951, 19926, 19894, - 19897, 19895, 19921, 19920, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 2264, 2299, 1093, 2300, 2301, 2263, - 2408, 2409, 2275, 2406, 2407, 2598, 1062, 1075, 2270, 2305, 2298, 2304, - 2296, 2297, 2306, 2326, 2312, 2341, 2308, 2357, 2356, 2292, 1381, 1083, - 2396, 2404, 1336, 1288, 1151, 1141, 1569, 1144, 1585, 1123, 1165, 1512, - 1511, 1532, 1314, 1273, 1355, 1190, 1529, 1433, 1611, 1467, 1480, 1459, - 1206, 1489, 1606, 1113, 1260, 1342, 1339, 1235, 1234, 1233, 2384, 1240, - 1424, 1325, 1360, 1373, 1397, 1290, 1564, 1159, 1576, 1091, 1073, 1103, - 1085, 1069, 1099, 2293, 2361, 2117, 1098, 1097, 2360, 2282, 2118, 2405, - 2400, 2399, 2401, 2276, 1086, 2403, 2416, 2418, 2415, 2414, 2411, 2410, - 2413, 2412, 2419, 2417, 2268, 1079, 2398, 1094, 1218, 1220, 1486, 1156, - 1148, 1147, 1310, 1311, 1313, 1550, 1312, 1538, 1544, 1185, 1520, 1519, - 1412, 1524, 1180, 1283, 1281, 1392, 1221, 1280, 1499, 1506, 1215, 1200, - 1197, 1198, 1203, 1212, 1226, 1193, 1199, 1450, 1436, 1447, 1444, 1437, - 1445, 1440, 1322, 1443, 1468, 1471, 1472, 1462, 1461, 1493, 1116, 1219, - 1243, 1241, 1557, 1245, 1419, 1427, 1428, 1337, 1488, 1331, 1330, 1382, - 1328, 1251, 1254, 1383, 1253, 1267, 1252, 1364, 1362, 1366, 1365, 1406, - 1398, 1453, 1407, 1403, 1301, 1501, 1298, 1291, 1292, 1515, 1573, 1349, - 1603, 1549, 1600, 1352, 1572, 1556, 1229, 1594, 1590, 1566, 1615, 1595, - 1577, 1580, 1095, 1164, 2315, 2314, 2317, 2316, 2343, 2325, 2323, 1084, - 2383, 2340, 2339, 2309, 2318, 2355, 2319, 2359, 2358, 2337, 2320, 2272, - 1082, 1081, 2280, 2354, 1192, 1441, 17374, 17376, 17373, 17372, 17369, - 17368, 17371, 17370, 17377, 17375, 1481, 1207, 1262, 2303, 2302, 1297, - 34842, 34916, 34913, 34914, 34910, 34851, 34838, 34839, 34915, 34912, - 34837, 34847, 34846, 34845, 40951, 34835, 34883, 34879, 34893, 34889, - 34890, 34853, 34852, 34854, 34896, 34894, 34855, 34886, 34887, 34891, - 34892, 34884, 34856, 34868, 34895, 34875, 34882, 34897, 34869, 34873, - 34881, 34885, 34874, 34880, 34888, 34870, 34872, 34871, 34902, 34901, - 34900, 34905, 34904, 34903, 34907, 34906, 34841, 34840, 34850, 34849, - 34848, 34844, 34843, 34908, 34922, 34923, 34909, 34920, 34919, 34918, - 34917, 34899, 34898, 34921, 34836, 40951, 40951, 34876, 34877, 34878, - 1171, 1174, 1169, 1170, 1172, 1173, 1167, 1282, 1279, 1196, 1191, 1438, - 1474, 1118, 1114, 1117, 1246, 1244, 1344, 1340, 1338, 1376, 1375, 1404, - 1401, 1402, 1367, 1439, 1442, 1473, 1278, 1276, 1470, 1434, 1277, 1150, - 1149, 1232, 1231, 1230, 1568, 1567, 1579, 1578, 1274, 1475, 1469, 1327, - 36865, 36878, 36874, 36891, 36890, 36869, 36866, 36854, 36885, 36870, - 36859, 36858, 36881, 36868, 36861, 36862, 36879, 36857, 36887, 36880, - 36892, 36873, 36872, 36871, 36883, 36864, 36867, 36882, 36888, 36877, - 36876, 36856, 36884, 36889, 36855, 36863, 36860, 36886, 36850, 36849, - 36896, 36852, 36897, 36895, 36851, 36853, 36894, 36893, 36898, 36875, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 29306, 29308, 29305, 29304, 29301, 29300, - 29303, 29302, 29309, 29307, 29341, 29328, 29342, 29327, 29343, 29325, - 29324, 29312, 29317, 29330, 29336, 29338, 29316, 29326, 29311, 29323, - 29322, 29337, 29329, 29331, 29333, 29334, 29319, 29335, 29320, 29318, - 29332, 29339, 29340, 29321, 29314, 29313, 29315, 29294, 29295, 29296, - 29292, 29289, 29290, 29291, 29293, 29288, 29345, 29344, 29347, 29346, - 29297, 29348, 29310, 40951, 40951, 29298, 29299, 29349, 32389, 32376, - 32390, 32378, 32381, 32377, 32392, 32380, 32387, 32396, 32382, 32383, - 32393, 32395, 32384, 32379, 32397, 32388, 32394, 32391, 32385, 32386, - 32399, 32400, 32403, 32398, 32404, 32401, 32423, 32433, 32428, 32422, - 32432, 32427, 32421, 32431, 32405, 32429, 32425, 32435, 32406, 32424, - 32434, 32426, 32430, 32402, 40951, 40951, 32413, 32407, 32409, 32412, - 32410, 32414, 32436, 32419, 32417, 32420, 32416, 32418, 32411, 32415, - 32408, 40951, 25603, 25590, 25592, 25591, 25593, 25602, 25600, 25605, - 25585, 25583, 25582, 25594, 25595, 25596, 25586, 25604, 25597, 25588, - 25598, 25599, 25587, 25584, 25601, 25606, 25589, 25581, 25607, 25608, - 40951, 40951, 25609, 40951, 34861, 34866, 34862, 34864, 34860, 34859, - 34863, 34867, 34858, 34857, 34865, 40951, 40951, 40951, 40951, 40951, - 1137, 1127, 1138, 1154, 1136, 1124, 1133, 1130, 1134, 1132, 1155, 1129, - 1140, 1126, 1128, 1139, 1125, 1131, 1135, 2385, 2386, 2388, 1537, 2281, - 2274, 1405, 1275, 1494, 1492, 1341, 2402, 40951, 2271, 2273, 40951, - 40951, 40951, 40951, 40951, 2269, 2327, 2348, 2347, 2350, 2116, 2365, - 1076, 1096, 1168, 1175, 1315, 1491, 1242, 1425, 1361, 1374, 1591, 1593, - 1446, 1565, 1458, 1372, 1194, 1460, 1255, 1487, 1614, 1115, 1329, 1426, - 1166, 1413, 1517, 1435, 1592, 1111, 1109, 1112, 1414, 1518, 1539, 1500, - 1343, 1261, 1110, 1317, 1316, 1363, 1272, 2307, 2310, 2338, 2333, 2342, - 1107, 1106, 2364, 1108, 1105, 2353, 2328, 2324, 2345, 2344, 2321, 2346, - 2329, 2331, 2330, 2332, 2335, 2334, 2311, 2322, 1080, 2397, 1065, 1063, - 1067, 1066, 1064, 1068, 2391, 2393, 2395, 2390, 2392, 2394, 2267, 2266, - 2265, 2336, 1088, 1087, 1100, 1621, 2277, 1620, 2279, 1077, 1078, 2278, - 1072, 2119, 10772, 10762, 10776, 10781, 10697, 10671, 10672, 10741, - 10742, 10717, 10718, 10736, 10738, 10679, 10698, 10749, 10673, 10680, - 10699, 10703, 10674, 10711, 10710, 10692, 10690, 10723, 10677, 10681, - 10728, 10726, 10724, 10733, 10732, 10685, 10684, 10722, 10735, 10734, - 10687, 10686, 10725, 10721, 10744, 10743, 10708, 10707, 10695, 10716, - 10714, 10713, 10731, 10730, 10729, 10740, 10700, 10701, 10702, 10694, - 10794, 10793, 10774, 10775, 10784, 10806, 10807, 10796, 10797, 10802, - 10803, 10790, 10800, 10808, 10785, 10791, 10801, 10792, 10786, 10780, - 10795, 10787, 10822, 10783, 10782, 10666, 10663, 10789, 10799, 10798, - 10748, 10709, 10689, 10746, 10682, 10712, 10747, 10715, 10737, 10739, - 10804, 10805, 10810, 10809, 10817, 10819, 10816, 10815, 10812, 10811, - 10814, 10813, 10820, 10818, 10664, 10779, 10678, 10705, 10704, 10675, - 10720, 10719, 10696, 10745, 10693, 10691, 10727, 10688, 10683, 10706, - 3549, 3617, 3620, 3622, 40951, 3573, 3574, 3587, 3588, 3585, 3586, 3567, - 3569, 40951, 40951, 3609, 3575, 40951, 40951, 3610, 3576, 3560, 3557, - 3601, 3600, 3589, 3599, 3598, 3603, 3602, 3591, 3582, 3581, 3578, 3577, - 3590, 3584, 3583, 3580, 3579, 3592, 40951, 3605, 3604, 3597, 3596, 3608, - 3572, 3561, 40951, 3607, 40951, 40951, 40951, 3593, 3594, 3595, 3606, - 40951, 40951, 3618, 3619, 3624, 3633, 3634, 3627, 3628, 3629, 3630, - 40951, 40951, 3635, 3625, 40951, 40951, 3636, 3626, 3621, 3558, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 3550, 40951, 40951, - 40951, 40951, 3565, 3564, 40951, 3571, 3568, 3570, 3631, 3632, 40951, - 40951, 3643, 3645, 3642, 3641, 3638, 3637, 3640, 3639, 3646, 3644, 3563, - 3562, 3612, 3611, 3551, 3555, 3554, 3553, 3552, 3556, 3623, 3647, 3566, - 3548, 3616, 40951, 40951, 18887, 18888, 18893, 40951, 18839, 18840, - 18855, 18856, 18853, 18854, 40951, 40951, 40951, 40951, 18874, 18841, - 40951, 40951, 18873, 18842, 18838, 18837, 18835, 18834, 18859, 18866, - 18865, 18868, 18867, 18861, 18850, 18849, 18844, 18843, 18860, 18852, - 18851, 18846, 18845, 18862, 40951, 18870, 18869, 18864, 18863, 18877, - 18879, 18848, 40951, 18858, 18857, 40951, 18878, 18871, 40951, 18872, - 18876, 40951, 40951, 18890, 40951, 18894, 18899, 18900, 18897, 18898, - 40951, 40951, 40951, 40951, 18902, 18895, 40951, 40951, 18901, 18896, - 18892, 40951, 40951, 40951, 18889, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 18836, 18833, 18880, 18847, 40951, 18875, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 18912, 18914, 18911, 18910, 18907, - 18906, 18909, 18908, 18915, 18913, 18903, 18832, 18904, 18916, 18905, - 18891, 18831, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 18727, 18735, 18737, 40951, 18677, 18678, 18696, 18697, - 18694, 18695, 18689, 18691, 18753, 40951, 18724, 18679, 18754, 40951, - 18725, 18680, 18717, 18716, 18713, 18712, 18701, 18711, 18710, 18715, - 18714, 18703, 18686, 18685, 18682, 18681, 18702, 18688, 18687, 18684, - 18683, 18704, 40951, 18719, 18718, 18709, 18708, 18721, 18723, 18722, - 40951, 18699, 18698, 40951, 18693, 18705, 18706, 18707, 18720, 40951, - 40951, 18733, 18734, 18740, 18749, 18750, 18743, 18744, 18745, 18746, - 18738, 40951, 18751, 18741, 18739, 40951, 18752, 18742, 18736, 40951, - 40951, 18767, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 18690, 18692, 18747, - 18748, 40951, 40951, 18763, 18765, 18762, 18761, 18758, 18757, 18760, - 18759, 18766, 18764, 18755, 18756, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 18700, 18732, 18731, 18728, 18730, 18726, 18729, 40951, - 30620, 30623, 30626, 40951, 30571, 30572, 30590, 30591, 30588, 30589, - 30583, 30585, 40951, 40951, 30616, 30573, 40951, 40951, 30617, 30574, - 30610, 30609, 30606, 30605, 30594, 30604, 30603, 30608, 30607, 30596, - 30580, 30579, 30576, 30575, 30595, 30582, 30581, 30578, 30577, 30597, - 40951, 30612, 30611, 30602, 30601, 30614, 30570, 30568, 40951, 30593, - 30592, 40951, 30587, 30598, 30599, 30600, 30613, 40951, 40951, 30621, - 30622, 30627, 30636, 30637, 30630, 30631, 30632, 30633, 40951, 40951, - 30638, 30628, 40951, 40951, 30639, 30629, 30625, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 30624, 30557, 30558, 40951, 40951, 40951, - 40951, 30567, 30566, 40951, 30569, 30584, 30586, 30634, 30635, 40951, - 40951, 30646, 30648, 30645, 30644, 30641, 30640, 30643, 30642, 30649, - 30647, 30565, 30615, 30562, 30561, 30564, 30559, 30560, 30563, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 35480, - 35497, 40951, 35443, 35444, 35456, 35457, 35452, 35453, 40951, 40951, - 40951, 35461, 35462, 35445, 40951, 35454, 35455, 35446, 35466, 40951, - 40951, 40951, 35438, 35463, 40951, 35465, 40951, 35439, 35441, 40951, - 40951, 40951, 35437, 35442, 40951, 40951, 40951, 35440, 35436, 35468, - 40951, 40951, 40951, 35467, 35470, 35451, 35450, 35449, 35448, 35447, - 35469, 35458, 35459, 35460, 35464, 40951, 40951, 40951, 40951, 35770, - 35777, 35778, 35773, 35774, 40951, 40951, 40951, 35779, 35780, 35771, - 40951, 35775, 35776, 35772, 35496, 40951, 40951, 35783, 40951, 40951, - 40951, 40951, 40951, 40951, 35372, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 35409, - 35411, 35408, 35407, 35404, 35403, 35406, 35405, 35412, 35410, 35474, - 35472, 35473, 35402, 35782, 35781, 35401, 35399, 35371, 35477, 35475, - 40951, 40951, 40951, 40951, 40951, 36730, 36731, 36736, 36738, 36729, - 36690, 36691, 36706, 36707, 36702, 36703, 36697, 36699, 40951, 36723, - 36724, 36692, 40951, 36704, 36705, 36693, 36720, 36719, 36716, 36715, - 36679, 36714, 36713, 36718, 36717, 36681, 36686, 36685, 36673, 36672, - 36680, 36689, 36687, 36676, 36674, 36677, 40951, 36722, 36721, 36712, - 36711, 36726, 36727, 36684, 36683, 36696, 36695, 36694, 36701, 36708, - 36709, 36710, 36725, 40951, 40951, 36734, 36735, 36739, 36750, 36751, - 36742, 36743, 36744, 36745, 40951, 36752, 36753, 36740, 40951, 36748, - 36749, 36741, 36737, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 36728, 36664, 40951, 36688, 36675, 36682, 40951, 40951, 36678, 40951, - 40951, 36698, 36700, 36746, 36747, 40951, 40951, 36760, 36762, 36759, - 36758, 36755, 36754, 36757, 36756, 36763, 36761, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 36732, 36671, 36669, 36667, 36665, 36670, - 36668, 36666, 36733, 21274, 21273, 21278, 21282, 21275, 21222, 21223, - 21248, 21249, 21244, 21245, 21239, 21241, 40951, 21265, 21266, 21224, - 40951, 21246, 21247, 21225, 21262, 21261, 21258, 21257, 21219, 21256, - 21255, 21260, 21259, 21221, 21236, 21235, 21227, 21226, 21220, 21238, - 21237, 21229, 21228, 21217, 40951, 21264, 21263, 21254, 21253, 21269, - 21270, 21234, 21233, 21232, 21231, 40951, 21243, 21250, 21251, 21252, - 21268, 40951, 40951, 21276, 21277, 21284, 21295, 21296, 21287, 21288, - 21289, 21290, 40951, 21297, 21298, 21285, 40951, 21293, 21294, 21286, - 21281, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 21271, 21283, - 40951, 40951, 40951, 40951, 40951, 40951, 21218, 21267, 40951, 21240, - 21242, 21291, 21292, 40951, 40951, 21305, 21307, 21304, 21303, 21300, - 21299, 21302, 21301, 21308, 21306, 40951, 21279, 21280, 21272, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 25543, 25545, 25550, 25548, 25500, 25474, 25476, 25520, 25521, - 25516, 25517, 25501, 25503, 40951, 25535, 25536, 25477, 40951, 25518, - 25519, 25478, 25532, 25531, 25528, 25527, 25508, 25489, 25488, 25530, - 25529, 25509, 25497, 25495, 25492, 25491, 25507, 25499, 25498, 25494, - 25493, 25510, 25506, 25534, 25533, 25526, 25525, 25538, 25539, 25515, - 25514, 25513, 25512, 25511, 25505, 25522, 25523, 25524, 25537, 25496, - 25546, 25544, 25549, 25552, 25563, 25564, 25555, 25556, 25557, 25558, - 40951, 25565, 25566, 25553, 40951, 25561, 25562, 25554, 25547, 25490, - 25551, 40951, 40951, 40951, 40951, 25486, 25487, 25481, 25567, 25466, - 25464, 25471, 25461, 25462, 25472, 25465, 25475, 25502, 25504, 25559, - 25560, 40951, 40951, 25457, 25459, 25456, 25455, 25452, 25451, 25454, - 25453, 25460, 25458, 25542, 25540, 25541, 25469, 25468, 25473, 25463, - 25467, 25470, 25450, 25483, 25482, 25484, 25479, 25480, 25485, 40951, - 33736, 33735, 33737, 40951, 33681, 33678, 33666, 33665, 33689, 33688, - 33691, 33690, 33687, 33686, 33685, 33684, 33683, 33682, 33679, 33710, - 33709, 33680, 40951, 40951, 40951, 33675, 33700, 33673, 33698, 33723, - 33713, 33672, 33697, 33674, 33699, 33720, 33721, 33714, 33667, 33692, - 33669, 33694, 33704, 33711, 33668, 33693, 33670, 33695, 33707, 40951, - 33712, 33676, 33701, 33671, 33696, 33702, 33677, 33719, 33717, 40951, - 33706, 40951, 40951, 33718, 33722, 33705, 33708, 33716, 33703, 33715, - 40951, 40951, 40951, 33734, 40951, 40951, 40951, 40951, 33754, 33746, - 33741, 33747, 33742, 33748, 40951, 33743, 40951, 33744, 33749, 33740, - 33753, 33750, 33751, 33752, 33745, 40951, 40951, 40951, 40951, 40951, - 40951, 33730, 33732, 33729, 33728, 33725, 33724, 33727, 33726, 33733, - 33731, 40951, 40951, 33738, 33739, 33755, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 36917, 36914, - 36912, 36911, 36913, 36915, 36931, 36900, 36902, 36901, 36960, 36903, - 36972, 36904, 36969, 36965, 36962, 36963, 36933, 36905, 36968, 36967, - 36964, 36966, 36934, 36899, 36940, 36937, 36906, 36938, 36907, 36939, - 36929, 36973, 36941, 36942, 36919, 36921, 36970, 36958, 36957, 36959, - 36910, 36918, 36974, 36909, 36935, 36943, 36923, 36946, 36948, 36953, - 36954, 36950, 36951, 36949, 36952, 36936, 40951, 40951, 40951, 40951, - 36975, 36955, 36947, 36956, 36945, 36944, 36920, 36928, 36927, 36926, - 36924, 36925, 36922, 36961, 36932, 36971, 36908, 36982, 36984, 36981, - 36980, 36977, 36976, 36979, 36978, 36985, 36983, 36930, 36916, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 22578, 22576, 40951, 22577, - 40951, 22591, 22606, 22610, 22590, 22600, 40951, 22592, 22607, 22588, - 22586, 22585, 22583, 22582, 22587, 22611, 22603, 22601, 22602, 22584, - 22608, 22609, 22596, 22594, 22573, 22595, 22572, 22589, 22612, 22615, - 22580, 40951, 22581, 40951, 22614, 22597, 22598, 22599, 22604, 22593, - 22616, 22605, 22642, 22624, 22629, 22625, 22627, 22637, 22638, 22631, - 22632, 22633, 22634, 22619, 22630, 22618, 22617, 40951, 40951, 22635, - 22636, 22639, 22628, 22626, 40951, 22640, 40951, 22623, 22620, 22621, - 22622, 22653, 22654, 22641, 40951, 22649, 22651, 22648, 22647, 22644, - 22643, 22646, 22645, 22652, 22650, 40951, 40951, 22569, 22568, 22575, - 22574, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 37208, 37115, 37113, 37114, 37123, 37111, 37108, - 37112, 37132, 37103, 37101, 37124, 37139, 37133, 37129, 37136, 37128, - 37131, 37130, 37107, 37116, 37099, 37098, 37026, 37027, 37025, 37147, - 37148, 37149, 37151, 37152, 37150, 37048, 37050, 37047, 37046, 37043, - 37042, 37045, 37044, 37051, 37049, 37040, 37037, 37036, 37033, 37032, - 37035, 37034, 37041, 37039, 37038, 37104, 37126, 37106, 37125, 37109, - 37135, 37117, 37118, 37119, 37120, 37158, 37144, 37057, 37055, 37085, - 37084, 37067, 37083, 37082, 37092, 40951, 37069, 37077, 37076, 37062, - 37061, 37068, 37079, 37078, 37066, 37065, 37070, 37087, 37086, 37081, - 37080, 37094, 37075, 37074, 37064, 37063, 37095, 37088, 37089, 37090, - 37096, 37059, 37093, 37071, 37072, 37073, 37091, 37097, 37054, 37060, - 37056, 37058, 40951, 40951, 40951, 40951, 37232, 37228, 37229, 37220, - 37221, 37222, 37223, 37224, 37225, 37230, 37231, 37226, 37227, 37155, - 37156, 37218, 37219, 37146, 37160, 37121, 37138, 37142, 37157, 37143, - 37145, 37140, 37141, 37159, 37207, 37206, 37205, 37172, 37171, 37191, - 37190, 37173, 37189, 37188, 37198, 40951, 37175, 37183, 37182, 37165, - 37164, 37174, 37185, 37184, 37169, 37168, 37176, 37193, 37192, 37187, - 37186, 37200, 37181, 37180, 37167, 37166, 37202, 37194, 37195, 37196, - 37203, 37201, 37199, 37177, 37178, 37179, 37197, 37204, 37170, 37162, - 37163, 37161, 40951, 37052, 37053, 37028, 37029, 37031, 37030, 37209, - 37216, 37214, 37217, 37215, 37210, 37213, 37212, 37211, 40951, 37154, - 37153, 37102, 37105, 37127, 37122, 37110, 32022, 24181, 32023, 24182, - 37137, 37134, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 28582, - 28561, 28560, 28559, 28591, 28659, 28658, 28661, 28660, 28592, 28589, - 28637, 28636, 28643, 28642, 28590, 28621, 28638, 28645, 28644, 28593, - 28663, 28662, 28657, 28656, 28588, 28665, 28595, 28655, 28641, 28620, - 28664, 28654, 28551, 28615, 28652, 28653, 28646, 28647, 28554, 28587, - 28666, 28553, 28758, 28742, 28765, 28766, 28759, 28760, 28754, 28739, - 28747, 28748, 28755, 28683, 28702, 28707, 28706, 28682, 28546, 28544, - 28545, 28543, 28558, 28783, 28785, 28782, 28781, 28778, 28777, 28780, - 28779, 28786, 28784, 28705, 28694, 28712, 28716, 28711, 28715, 28596, - 28619, 28648, 28649, 28650, 28651, 28761, 28762, 28763, 28764, 28586, - 28585, 28583, 28584, 28549, 28548, 28547, 28618, 28753, 28727, 28728, - 28640, 28639, 28756, 28757, 28697, 28698, 28699, 28700, 28701, 28557, - 28556, 28555, 28743, 28745, 28746, 28744, 28610, 28609, 28608, 28606, - 28614, 28598, 28611, 28599, 28601, 28612, 28604, 28602, 28613, 28550, - 28752, 28749, 28750, 28751, 28689, 28690, 28691, 28692, 28687, 28688, - 28686, 28594, 28703, 28678, 28680, 28677, 28676, 28673, 28672, 28675, - 28674, 28681, 28679, 28684, 28685, 28740, 28741, 28714, 28713, 17728, - 17754, 17739, 17760, 17761, 17759, 17749, 17750, 17762, 17743, 17752, - 17755, 17757, 17763, 17745, 17748, 17753, 17747, 17751, 17764, 17744, - 17742, 17738, 17758, 17746, 17734, 17737, 17741, 17736, 17735, 17756, - 17740, 17729, 17733, 17731, 17766, 17730, 17732, 40951, 17765, 40951, - 40951, 40951, 40951, 40951, 17727, 40951, 40951, 17811, 17842, 17819, - 17848, 17817, 17847, 17840, 17837, 17849, 17829, 17831, 17843, 17845, - 17850, 17833, 17839, 17841, 17835, 17838, 17808, 17832, 17828, 17818, - 17846, 17834, 17812, 17815, 17827, 17814, 17813, 17844, 17826, 17822, - 17825, 17823, 17852, 17820, 17824, 17853, 17851, 17816, 17836, 17810, - 17900, 27795, 17809, 17821, 17830, 19147, 19221, 19155, 19226, 19219, - 19183, 19150, 19165, 19223, 19197, 19215, 19129, 19127, 19213, 19114, - 19149, 19233, 19162, 19236, 19157, 19222, 19159, 19158, 19230, 19193, - 19217, 19196, 19142, 19152, 19146, 19175, 19180, 19179, 19166, 19170, - 19168, 19171, 19172, 19169, 19177, 19176, 19178, 19173, 19144, 19145, - 19204, 19210, 19209, 19202, 19207, 19198, 19199, 19201, 19212, 19206, - 19205, 19203, 19208, 19200, 19211, 19119, 19118, 19124, 19123, 19182, - 19139, 19138, 19136, 19131, 19141, 19132, 19225, 19135, 19134, 19137, - 19130, 19234, 19128, 19121, 19117, 19126, 19122, 19115, 19116, 19120, - 19125, 19163, 19143, 19224, 19235, 19148, 19161, 19156, 19160, 19227, - 19238, 19476, 19382, 19392, 19440, 19444, 19394, 19393, 19446, 19445, - 19421, 19473, 19474, 19431, 19452, 19432, 19472, 19471, 19475, 19461, - 19398, 19450, 19405, 19390, 19391, 19442, 19441, 19396, 19397, 19395, - 19448, 19449, 19429, 19428, 19424, 19422, 19430, 19453, 19454, 19455, - 19460, 19459, 19437, 19438, 19436, 19433, 19439, 19466, 19465, 19464, - 19463, 19462, 19470, 19468, 19404, 19401, 19451, 19406, 19408, 19415, - 19419, 19417, 19407, 19383, 19385, 19388, 19387, 19420, 19389, 19443, - 19447, 19426, 19427, 19258, 19359, 19261, 19281, 19284, 19288, 19363, - 19309, 19310, 19315, 19319, 19328, 19331, 19324, 19336, 19268, 19298, - 19301, 19337, 19354, 19250, 19241, 19244, 19267, 19372, 19294, 19245, - 19260, 19262, 19287, 19286, 19290, 19289, 19285, 19370, 19364, 19312, - 19335, 19329, 19330, 19349, 19316, 19318, 19323, 19322, 19313, 19327, - 19325, 19314, 19332, 19278, 19275, 19269, 19274, 19273, 19271, 19276, - 19280, 19257, 19299, 19303, 19308, 19256, 19339, 19347, 19340, 19343, - 19291, 19252, 19253, 19362, 19251, 19373, 19377, 19380, 19296, 19255, - 19248, 19246, 19247, 19249, 19381, 19264, 19265, 19263, 19259, 19266, - 19360, 17020, 17027, 17026, 17021, 17025, 17024, 17022, 17023, 17247, - 17255, 17254, 17248, 17252, 17251, 17249, 17253, 17013, 17019, 17017, - 17014, 17016, 17015, 17018, 17004, 17064, 17072, 17071, 17065, 17069, - 17068, 17066, 17062, 17176, 17183, 17181, 17177, 17179, 17178, 17182, - 17180, 17151, 17160, 17159, 17152, 17156, 17155, 17153, 17157, 17191, - 17197, 17196, 17192, 17166, 17161, 17193, 17195, 17167, 17175, 17174, - 17168, 17172, 17171, 17169, 17173, 17143, 17150, 17149, 17144, 17148, - 17147, 17145, 17146, 17131, 40951, 17135, 17132, 17134, 17133, 40951, - 40951, 17124, 17130, 17128, 17125, 17127, 17126, 17129, 40951, 17119, - 40951, 17123, 17120, 17122, 17121, 40951, 40951, 16854, 16861, 16860, - 16855, 16859, 16858, 16856, 16845, 17316, 17323, 17321, 17317, 17319, - 17318, 17322, 17320, 17229, 17237, 17236, 17230, 17234, 17233, 17231, - 17235, 16892, 16900, 16899, 16893, 16897, 16896, 16894, 16898, 17284, - 17291, 17290, 17285, 17289, 17288, 17286, 17287, 17272, 40951, 17276, - 17273, 17275, 17274, 40951, 40951, 17082, 17090, 17089, 17083, 17087, - 17086, 17084, 17088, 17073, 17081, 17080, 17074, 17078, 17077, 17075, - 17079, 16974, 16982, 16981, 16975, 16979, 16978, 16976, 16980, 17052, - 17059, 17058, 17053, 17057, 17056, 17054, 17055, 17040, 40951, 17044, - 17041, 17043, 17042, 40951, 40951, 17033, 17039, 17037, 17034, 17036, - 17035, 17038, 40951, 17028, 40951, 17032, 17029, 17031, 17030, 40951, - 40951, 17256, 17263, 17262, 17257, 17261, 17260, 17258, 17259, 17092, - 17098, 17096, 17093, 17095, 17094, 17097, 40951, 17307, 17315, 17314, - 17308, 17312, 17311, 17309, 17313, 17292, 17299, 17297, 17293, 17295, - 17294, 17298, 17296, 17264, 17271, 17270, 17265, 17269, 17268, 17266, - 17267, 16922, 16930, 16929, 16923, 16927, 16926, 16924, 16928, 16907, - 16915, 16914, 16908, 16912, 16911, 16909, 16913, 17238, 17246, 17245, - 17239, 17243, 17242, 17240, 17244, 16995, 16943, 17001, 16996, 17000, - 16999, 16997, 16998, 16983, 40951, 16987, 16984, 16986, 16985, 40951, - 40951, 16967, 16973, 16971, 16968, 16970, 16969, 16972, 16963, 17198, - 17206, 17205, 17199, 17203, 17202, 17200, 17204, 16883, 16891, 16890, - 16884, 16888, 16887, 16885, 16889, 17091, 17106, 17105, 17099, 17103, - 17102, 17100, 17104, 17221, 17228, 17226, 17222, 17224, 17223, 17227, - 17225, 17213, 17220, 17219, 17214, 17218, 17217, 17215, 17216, 16935, - 16942, 16940, 16936, 16938, 16937, 16941, 16933, 17111, 17118, 17117, - 17112, 17116, 17115, 17113, 17109, 17158, 17070, 16939, 40951, 40951, - 16823, 16825, 16824, 16842, 17345, 17343, 16826, 16841, 16827, 16840, - 17344, 16839, 17341, 17339, 17338, 17335, 17334, 17337, 17336, 17342, - 17340, 16828, 16831, 16830, 16835, 16834, 16838, 16837, 16833, 16836, - 16832, 16829, 40951, 40951, 40951, 17164, 17063, 17061, 17060, 17162, - 16846, 16844, 16843, 17163, 16934, 16932, 16931, 17165, 17110, 17108, - 17107, 17333, 17324, 17330, 17331, 17326, 17328, 17332, 17327, 17325, - 17329, 40951, 40951, 40951, 40951, 40951, 40951, 6382, 6383, 6384, 6385, - 6386, 6387, 6345, 6381, 6346, 6347, 6348, 6349, 6350, 6310, 6311, 6312, - 6313, 6314, 6315, 6351, 6352, 6353, 6354, 6355, 6356, 6357, 6358, 6359, - 6360, 6361, 6316, 6309, 6317, 6318, 6319, 6320, 6321, 6322, 6363, 6364, - 6365, 6366, 6367, 6368, 6324, 6323, 6325, 6326, 6327, 6328, 6329, 6303, - 6342, 6304, 6343, 6305, 6344, 6306, 6307, 6308, 6302, 6330, 6331, 6332, - 6333, 6334, 6335, 6336, 6337, 6338, 6339, 6340, 6341, 6369, 6370, 6371, - 6372, 6373, 6374, 6375, 6376, 6377, 6378, 6379, 6380, 6362, 40951, 40951, - 6462, 6463, 6464, 6465, 6466, 6448, 40951, 40951, 5524, 5496, 5269, 5526, - 5527, 5676, 5690, 5973, 5480, 5340, 5267, 5268, 5850, 5940, 5960, 5938, - 5961, 5939, 5942, 5936, 5943, 5937, 5605, 5957, 5934, 5958, 5935, 5606, - 5271, 5974, 5992, 5513, 5512, 5514, 5515, 5507, 5508, 5505, 5504, 5517, - 5511, 5516, 5506, 5498, 5528, 5689, 5275, 5710, 5703, 5708, 5709, 5705, - 5706, 5964, 5338, 5339, 5701, 5702, 5700, 5879, 5698, 5877, 5699, 5878, - 5693, 5875, 5694, 5876, 5696, 5873, 5697, 5874, 5963, 5692, 5872, 5331, - 5849, 5832, 5847, 5848, 5845, 5846, 5971, 5302, 5315, 5830, 5831, 5840, - 5932, 5838, 5930, 5839, 5931, 5836, 5928, 5837, 5929, 5834, 5926, 5835, - 5927, 5611, 5792, 5827, 5828, 5829, 5826, 5547, 5541, 5545, 5546, 5543, - 5544, 5966, 5539, 5540, 5538, 5924, 5536, 5922, 5537, 5923, 5534, 5920, - 5535, 5921, 5531, 5918, 5532, 5919, 5608, 5529, 5530, 5772, 5773, 5774, - 5771, 5495, 5482, 5493, 5494, 5491, 5492, 5965, 5299, 5481, 5489, 5917, - 5487, 5915, 5488, 5916, 5485, 5913, 5486, 5914, 5483, 5911, 5484, 5912, - 5607, 5298, 5748, 5573, 5581, 5590, 5591, 5576, 5577, 5968, 5579, 5580, - 5589, 5871, 5587, 5869, 5588, 5870, 5585, 5867, 5586, 5868, 5583, 5865, - 5584, 5866, 5609, 5572, 5864, 5592, 5273, 5749, 5665, 5626, 5663, 5664, - 5653, 5654, 5969, 5597, 5625, 5662, 5890, 5656, 5888, 5657, 5889, 5610, - 5593, 5371, 5666, 5571, 5558, 5569, 5570, 5567, 5568, 5967, 5556, 5557, - 5566, 5858, 5564, 5856, 5565, 5857, 5562, 5854, 5563, 5855, 5560, 5852, - 5561, 5853, 5548, 5851, 5574, 5791, 5751, 5789, 5790, 5770, 5775, 5970, - 5731, 5750, 5784, 5910, 5782, 5908, 5783, 5909, 5780, 5906, 5781, 5907, - 5778, 5904, 5779, 5905, 5603, 5730, 5274, 5777, 5294, 5578, 5598, 5604, - 5601, 5602, 5599, 5600, 5769, 5767, 5768, 5761, 5762, 5764, 5765, 5760, - 5903, 5758, 5901, 5759, 5902, 5753, 5899, 5754, 5900, 5756, 5897, 5757, - 5898, 5752, 5991, 5977, 5989, 5990, 5979, 5980, 5972, 5975, 5976, 5988, - 5887, 5986, 5885, 5987, 5886, 5984, 5883, 5985, 5884, 5982, 5881, 5983, - 5882, 5612, 5962, 5295, 5880, 5747, 5729, 5713, 5863, 5723, 5727, 5728, - 5725, 5726, 5861, 5721, 5722, 5859, 5715, 5892, 5711, 5891, 5575, 5523, - 5502, 5503, 5518, 5520, 5521, 5500, 5501, 5522, 5933, 5499, 5811, 5596, - 5809, 5594, 5810, 5595, 5807, 5808, 5805, 5806, 5803, 5925, 5793, 5824, - 5825, 5821, 5819, 5818, 5842, 5843, 5844, 5841, 5651, 5649, 5650, 5647, - 5648, 5645, 5646, 5644, 5652, 5525, 5670, 5674, 5675, 5672, 5673, 5668, - 5669, 5667, 5816, 5817, 5812, 5815, 5894, 5895, 5896, 5893, 5631, 5635, - 5636, 5633, 5634, 5629, 5630, 5628, 5637, 5745, 5746, 5741, 5744, 5953, - 5954, 5955, 5952, 5944, 5554, 5555, 5552, 5553, 5550, 5551, 5549, 5801, - 5799, 5800, 5797, 5798, 5795, 5796, 5794, 5272, 5291, 5292, 5293, 5290, - 5279, 5280, 5281, 5278, 5287, 5288, 5289, 5286, 5283, 5284, 5285, 5282, - 5736, 5737, 5733, 5735, 5321, 5320, 5316, 5317, 5319, 5318, 5467, 5466, - 5462, 5463, 5465, 5464, 5473, 5472, 5468, 5469, 5471, 5470, 5337, 5336, - 5332, 5333, 5335, 5334, 5431, 5430, 5426, 5427, 5429, 5428, 5425, 5424, - 5420, 5421, 5423, 5422, 5394, 5393, 5389, 5390, 5392, 5391, 5388, 5330, - 5329, 5326, 5327, 5328, 5324, 5367, 5366, 5362, 5363, 5365, 5364, 5361, - 5360, 5356, 5357, 5359, 5358, 5355, 5374, 5373, 5368, 5369, 5372, 5370, - 5461, 5460, 5456, 5457, 5459, 5458, 5479, 5478, 5474, 5475, 5477, 5476, - 5354, 5738, 5353, 5348, 5349, 5352, 5740, 5351, 5347, 5346, 5342, 5343, - 5345, 5344, 5449, 5448, 5444, 5445, 5447, 5446, 5308, 5307, 5303, 5304, - 5306, 5305, 5443, 5442, 5438, 5439, 5441, 5440, 5407, 5406, 5402, 5403, - 5405, 5404, 5413, 5412, 5408, 5409, 5411, 5410, 5401, 5400, 5396, 5397, - 5399, 5398, 5395, 5341, 5314, 5313, 5309, 5310, 5312, 5311, 5387, 5386, - 5382, 5383, 5385, 5384, 5381, 5380, 5376, 5377, 5379, 5378, 5375, 5437, - 5436, 5432, 5433, 5435, 5434, 5455, 5454, 5450, 5451, 5453, 5452, 5419, - 5418, 5414, 5415, 5417, 5416, 5490, 5519, 5671, 5632, 5642, 5643, 5640, - 5641, 5638, 5639, 5951, 5949, 5950, 5947, 5948, 5945, 5946, 5956, 5277, - 29977, 29952, 29963, 29959, 29969, 29966, 29972, 29975, 29976, 29955, - 29954, 29974, 29960, 29965, 29970, 29964, 29951, 29967, 29973, 29957, - 29961, 29956, 29968, 29971, 29962, 29958, 29953, 29949, 29950, 40951, - 40951, 40951, 32296, 32352, 32346, 32350, 32349, 32344, 32342, 32289, - 32274, 32320, 32273, 32272, 32317, 32332, 32319, 32323, 32324, 32326, - 32312, 32278, 32311, 32297, 32290, 32298, 32300, 32345, 32302, 32301, - 32315, 32329, 32341, 32331, 32283, 32305, 32286, 32309, 32299, 32314, - 32335, 32306, 32348, 32275, 32338, 32337, 32333, 32276, 32354, 32343, - 32334, 32281, 32340, 32328, 32284, 32322, 32287, 32347, 32316, 32330, - 32313, 32282, 32304, 32303, 32285, 32321, 32288, 32308, 32280, 32279, - 32277, 32339, 32318, 32336, 32307, 32351, 32353, 32355, 32356, 32271, - 32357, 32358, 32270, 32310, 32327, 32325, 32294, 32293, 32295, 32292, - 32291, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 35025, 35042, - 35043, 35033, 35031, 35027, 35039, 35030, 35028, 35036, 35029, 35035, - 35041, 35037, 35034, 35040, 35038, 35032, 35046, 35047, 35045, 35044, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 35026, - 19648, 19649, 19650, 19639, 19637, 19633, 19645, 19636, 19634, 19642, - 19635, 19641, 19647, 19643, 19640, 19646, 19644, 19638, 19652, 19653, - 19651, 31427, 31428, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 4990, 4991, 4992, 4981, 4979, 4975, 4987, 4978, 4976, 4984, - 4977, 4983, 4989, 4985, 4982, 4988, 4986, 4980, 4993, 4994, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 35061, 35062, 35063, 35053, 35052, 35048, 35058, 35051, 35049, 35056, - 35050, 35055, 35060, 40951, 35054, 35059, 35057, 40951, 35064, 35065, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 22231, 22229, 22232, 22230, 22233, 22227, 22225, 22228, - 22226, 22235, 22249, 22245, 22250, 22246, 22234, 22247, 22243, 22248, - 22244, 22236, 22257, 22237, 22239, 22238, 22253, 22256, 22254, 22252, - 22255, 22241, 22240, 22242, 22258, 22251, 22259, 22206, 22204, 22214, - 22215, 22210, 22213, 22211, 22212, 22223, 22224, 22221, 22222, 22216, - 22205, 22209, 22208, 22207, 22326, 22325, 22327, 22332, 22334, 22338, - 22340, 22342, 22344, 22343, 22335, 22339, 22333, 22345, 22329, 22330, - 22337, 22331, 22280, 22274, 22279, 22281, 22276, 22265, 22273, 22275, - 22270, 22262, 22263, 22282, 22269, 22264, 22271, 22266, 22268, 22277, - 22267, 22278, 22272, 22203, 22260, 22261, 40951, 40951, 22352, 22354, - 22351, 22350, 22347, 22346, 22349, 22348, 22355, 22353, 40951, 40951, - 40951, 40951, 40951, 40951, 22304, 22303, 22300, 22302, 22301, 22295, - 22298, 22299, 22297, 22296, 40951, 40951, 40951, 40951, 40951, 40951, - 28162, 28174, 28170, 28019, 28169, 28020, 28167, 28158, 28171, 28172, - 28173, 28018, 28017, 28016, 28168, 28015, 28011, 28013, 28010, 28009, - 28006, 28005, 28008, 28007, 28014, 28012, 40951, 40951, 40951, 40951, - 40951, 40951, 28023, 28137, 28154, 28139, 28141, 28140, 28142, 28138, - 28148, 28051, 28143, 28149, 28150, 28146, 28055, 28136, 28098, 28097, - 28128, 28144, 28052, 28147, 28153, 28151, 28152, 28145, 28134, 28133, - 28127, 28131, 28132, 28130, 28135, 28129, 28054, 28107, 28125, 28126, - 28114, 28116, 28115, 28117, 28101, 28118, 28121, 28122, 28108, 28120, - 28111, 28103, 28112, 28105, 28110, 28124, 28123, 28119, 28109, 28113, - 28104, 28106, 28102, 28096, 28079, 28080, 28090, 28089, 28086, 28094, - 28075, 28077, 28095, 28084, 28082, 28091, 28093, 28092, 28076, 28078, - 28081, 28088, 28085, 28083, 28087, 28074, 28072, 28073, 28071, 28070, - 28053, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 28025, 28027, - 28029, 28036, 28035, 28043, 28039, 28024, 28034, 28050, 28037, 28049, - 28041, 28040, 28031, 28038, 28042, 28028, 28045, 28044, 28048, 28046, - 28047, 28026, 28100, 28099, 28063, 28068, 28059, 28064, 28060, 28056, - 28061, 28057, 28069, 28058, 28066, 28067, 28033, 28032, 28062, 28030, - 28065, 40951, 40951, 40951, 40951, 40951, 5691, 5276, 5270, 5959, 5707, - 5704, 5695, 5833, 5542, 5533, 5582, 5655, 5627, 5559, 5776, 5732, 5763, - 5766, 5755, 5981, 5978, 5724, 5660, 5680, 5661, 5681, 5658, 5678, 5659, - 5679, 5720, 5718, 5719, 5716, 5717, 5714, 5687, 5688, 5685, 5684, 5686, - 5677, 5682, 5683, 5497, 5941, 5510, 5509, 5712, 5862, 5860, 5804, 5802, - 5823, 5822, 5820, 5814, 5813, 5743, 5742, 5734, 5323, 5300, 5325, 5322, - 5739, 5350, 5296, 5297, 5301, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 24478, 24445, 24444, 24425, 24424, 24431, - 24439, 24438, 24443, 24442, 24430, 24428, 24426, 24441, 24440, 24432, - 24447, 24446, 24437, 24436, 24450, 24429, 24451, 24449, 24452, 24433, - 24434, 24435, 24448, 24423, 24427, 40951, 24469, 24476, 24477, 24475, - 24470, 24473, 24471, 24474, 24472, 24468, 24466, 24467, 40951, 40951, - 40951, 40951, 24460, 24457, 24459, 24465, 24458, 24463, 24462, 24464, - 24461, 24454, 24453, 24455, 40951, 40951, 40951, 40951, 24456, 40951, - 40951, 40951, 24479, 24480, 24487, 24489, 24486, 24485, 24482, 24481, - 24484, 24483, 24490, 24488, 35086, 35098, 35081, 35078, 35096, 35099, - 35080, 35079, 35093, 35088, 35087, 35094, 35091, 35097, 35092, 35095, - 35085, 35077, 35082, 35066, 35100, 35070, 35071, 35089, 35084, 35083, - 35090, 35069, 35067, 35068, 40951, 40951, 35072, 35073, 35074, 35075, - 35076, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 29120, 29142, 29102, 29104, 29107, 29124, 29126, 29129, - 29110, 29106, 29122, 29132, 29128, 29144, 29111, 29109, 29108, 29133, - 29131, 29130, 29113, 29112, 29119, 29135, 29134, 29141, 29116, 29121, - 29118, 29138, 29143, 29140, 29117, 29115, 29114, 29139, 29137, 29136, - 29101, 29103, 29123, 29125, 29105, 29127, 40951, 40951, 40951, 40951, - 29165, 29150, 29154, 29160, 29163, 29166, 29152, 29156, 29157, 29161, - 29153, 29151, 29164, 29159, 29158, 29162, 29155, 29100, 29095, 29094, - 29099, 29098, 29097, 29096, 29147, 29148, 40951, 40951, 40951, 40951, - 40951, 40951, 29173, 29175, 29172, 29171, 29168, 29167, 29170, 29169, - 29176, 29174, 29149, 40951, 40951, 40951, 29145, 29146, 22305, 22324, - 22317, 22320, 22322, 22315, 22313, 22311, 22307, 22309, 22294, 22292, - 22284, 22288, 22290, 22286, 22318, 22323, 22316, 22319, 22321, 22314, - 22312, 22310, 22306, 22308, 22293, 22291, 22283, 22287, 22289, 22285, - 4959, 4956, 4948, 4947, 4961, 4953, 4946, 4945, 4964, 4955, 4952, 4951, - 4954, 4958, 4950, 4949, 4966, 4962, 4960, 4965, 4963, 4967, 4957, 4970, - 4972, 4969, 4971, 4968, 40951, 40951, 4974, 4973, 35134, 35132, 35133, - 35150, 35149, 35148, 35174, 35140, 35139, 35153, 35160, 35152, 35175, - 35168, 35135, 35180, 35151, 35167, 35144, 35143, 35157, 35156, 35176, - 35179, 35142, 35141, 35145, 35155, 35158, 35154, 35181, 35161, 35147, - 35166, 35169, 35162, 35164, 35182, 35136, 35137, 35138, 35146, 35165, - 35183, 35159, 35172, 35173, 35170, 35171, 35178, 35177, 35163, 35131, - 35106, 35105, 35103, 35194, 35101, 35102, 35104, 35107, 35108, 35109, - 40951, 35202, 35209, 35213, 35210, 35219, 35225, 35226, 35224, 35223, - 35221, 35222, 35214, 35215, 35218, 35227, 35211, 35217, 35212, 35220, - 35216, 35193, 35206, 35207, 35186, 35187, 35188, 35197, 35196, 35189, - 40951, 40951, 35110, 35117, 35119, 35116, 35115, 35112, 35111, 35114, - 35113, 35120, 35118, 40951, 40951, 40951, 40951, 40951, 40951, 35127, - 35129, 35126, 35125, 35122, 35121, 35124, 35123, 35130, 35128, 40951, - 40951, 40951, 40951, 40951, 40951, 35203, 35204, 35201, 35192, 35185, - 35205, 35198, 35195, 35190, 35191, 35199, 35200, 35184, 35208, 40951, - 40951, 8201, 8169, 8285, 8203, 8435, 8448, 8447, 8387, 8184, 8364, 8423, - 8393, 8188, 8392, 8391, 8333, 8327, 8350, 8409, 8351, 8410, 8421, 8380, - 8284, 8396, 8187, 8186, 8433, 8311, 8312, 8313, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 2717, 2716, 2718, 2715, 2719, - 2626, 2627, 2643, 2644, 2647, 2648, 2665, 2666, 2656, 2657, 2640, 2630, - 2645, 2646, 2651, 2653, 2641, 2642, 2660, 2633, 2634, 2649, 2650, 2661, - 2672, 2671, 2637, 2636, 2659, 2670, 2673, 2635, 2638, 2658, 2662, 2663, - 2631, 2632, 2678, 2680, 2664, 2655, 2679, 2668, 2669, 2667, 2677, 2720, - 2731, 2734, 2735, 2725, 2726, 2723, 2724, 2721, 2722, 2727, 2728, 2730, - 2729, 2732, 2733, 2736, 2652, 2654, 2674, 2639, 2675, 2676, 2628, 2629, - 40951, 2625, 2624, 2744, 2746, 2743, 2742, 2739, 2738, 2741, 2740, 2747, - 2745, 2712, 2709, 2737, 2622, 2623, 2621, 2711, 2698, 2696, 2699, 2690, - 2691, 2697, 2693, 2695, 2694, 2692, 2688, 2687, 2683, 2681, 2685, 2684, - 2682, 2686, 2689, 2708, 2707, 2706, 2705, 2700, 2702, 2703, 2704, 2701, - 2713, 2710, 2714, 34633, 34631, 34632, 34584, 34619, 34621, 34586, 34620, - 34596, 34597, 34604, 34612, 34607, 34598, 34605, 34609, 34618, 34599, - 34613, 34606, 34600, 34611, 34589, 34614, 34602, 34610, 34617, 34593, - 34591, 34615, 34595, 34616, 34608, 34579, 34580, 34581, 34639, 34638, - 34640, 34637, 34635, 34636, 34630, 34634, 34582, 34583, 34603, 34594, - 34647, 34649, 34646, 34645, 34642, 34641, 34644, 34643, 34650, 34648, - 34578, 34592, 34590, 34601, 34587, 34588, 3508, 3494, 3502, 3486, 3474, - 3498, 3497, 3483, 3489, 3482, 3475, 3506, 3492, 3484, 3501, 3485, 3503, - 3500, 3505, 3490, 3473, 3488, 3495, 3478, 3496, 3491, 3476, 3507, 3493, - 3480, 3504, 3487, 3481, 3499, 3479, 3477, 3509, 3510, 3517, 3523, 3520, - 3524, 3525, 3521, 3526, 3522, 3518, 3519, 3471, 3472, 3511, 3512, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 3516, 3514, 3515, 3513, - 24308, 24307, 24306, 24320, 24319, 24325, 24335, 24334, 24338, 24326, - 24333, 24332, 24314, 24327, 24311, 24310, 24309, 24318, 24317, 24316, - 24315, 24324, 24323, 24329, 24328, 24313, 24343, 24340, 24339, 24322, - 24321, 24341, 24337, 24336, 24342, 24344, 24353, 24352, 24358, 24360, - 24356, 24357, 24354, 24355, 24359, 24297, 24302, 24301, 24299, 24303, - 24304, 24305, 24300, 24298, 24351, 24350, 40951, 40951, 40951, 24345, - 24348, 24349, 24347, 24346, 24367, 24369, 24366, 24365, 24362, 24361, - 24364, 24363, 24370, 24368, 40951, 40951, 40951, 24331, 24330, 24312, - 30023, 30025, 30022, 30021, 30018, 30017, 30020, 30019, 30026, 30024, - 29996, 29987, 29985, 29984, 29986, 29997, 29981, 29980, 29982, 29983, - 29999, 29995, 29993, 29992, 29994, 30001, 30007, 30008, 30006, 30009, - 29998, 29991, 29988, 29990, 29989, 30000, 30002, 30003, 30005, 30004, - 30011, 30012, 30010, 30016, 30015, 30027, 30014, 30013, 10430, 10407, - 10413, 10470, 10451, 10459, 10449, 10450, 10469, 10135, 10461, 40951, - 40951, 40951, 40951, 40951, 17856, 17887, 17864, 17893, 17862, 17892, - 17885, 17882, 17894, 17874, 17876, 17888, 17890, 17895, 17878, 17884, - 17886, 17880, 17883, 17896, 17877, 17873, 17863, 17891, 17879, 17857, - 17860, 17872, 17859, 17858, 17889, 17871, 17867, 17870, 17868, 17898, - 17865, 17869, 17899, 17897, 17861, 17881, 17855, 40951, 40951, 17854, - 17866, 17875, 34629, 34627, 34628, 34626, 34625, 34624, 34623, 34622, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 38803, 38816, - 38805, 38783, 38797, 38811, 38812, 38813, 38798, 38814, 38801, 38809, - 38804, 38802, 38810, 38808, 38807, 38815, 38796, 38794, 38785, 38792, - 38784, 38795, 38793, 38774, 38775, 38777, 38778, 38790, 38788, 38789, - 38787, 38776, 38780, 38786, 38799, 38782, 38791, 38779, 38806, 38800, - 38781, 40951, 40951, 40951, 40951, 40951, 23270, 23271, 23878, 23267, - 23272, 23273, 23244, 23243, 23863, 23856, 23276, 23277, 23250, 23278, - 23256, 23251, 23252, 23813, 23814, 23815, 23858, 23254, 23850, 23370, - 23280, 23257, 23265, 23260, 23283, 23818, 23817, 23816, 23284, 23285, - 23287, 23245, 23298, 23232, 18400, 18403, 18399, 18402, 18398, 10300, - 27701, 27702, 27692, 27693, 27704, 27705, 27695, 27707, 27697, 27708, - 27709, 27710, 27711, 27712, 27713, 27696, 27699, 27700, 27714, 27694, - 27716, 27717, 27719, 27850, 27957, 27851, 27959, 27854, 27879, 27893, - 27947, 27932, 27962, 27900, 27970, 27981, 27910, 27897, 27930, 27933, - 27954, 27856, 27934, 27949, 27974, 27948, 27960, 27978, 27852, 27858, - 27901, 27884, 27902, 27878, 24019, 24027, 24029, 24030, 18609, 18605, - 18608, 18607, 18606, 23939, 23356, 23405, 23491, 23634, 23654, 23731, - 23758, 23751, 23797, 23833, 24001, 23885, 27768, 23562, 23843, 23300, - 23569, 23727, 23301, 23938, 23357, 23409, 23492, 23505, 23588, 23615, - 23635, 23660, 23732, 23761, 23798, 23478, 23946, 23973, 24002, 23319, - 23345, 23408, 23468, 23720, 23768, 23806, 23559, 23716, 23480, 23925, - 23486, 27958, 27859, 27877, 27895, 27941, 27898, 27885, 27946, 27969, - 27912, 27913, 27860, 27862, 27915, 27919, 27921, 27865, 27911, 27961, - 27929, 27928, 27871, 27855, 27935, 27945, 27894, 27950, 27976, 27977, - 27873, 27980, 27971, 27890, 27892, 27891, 27896, 27973, 8177, 8176, 8425, - 8424, 8377, 8268, 8376, 8061, 8267, 8060, 8325, 8073, 8378, 8200, 8389, - 8416, 8286, 8440, 8441, 8308, 8299, 8300, 8301, 8303, 8310, 8306, 8335, - 8291, 8337, 8314, 8292, 8293, 8339, 8294, 8295, 8324, 8328, 8316, 8343, - 8298, 8330, 8331, 8329, 8307, 8315, 8319, 8340, 8305, 8322, 8332, 8297, - 8318, 8321, 8439, 8290, 8289, 8172, 8445, 8175, 8167, 8181, 8070, 8345, - 8400, 22756, 23317, 22792, 23359, 22791, 23358, 22790, 23355, 22799, - 23374, 22824, 23411, 22823, 23410, 22822, 23406, 22818, 23400, 22819, - 23401, 22850, 23454, 22851, 23455, 22839, 23442, 22848, 23452, 22830, - 23433, 22875, 23494, 22883, 23504, 22902, 23524, 22901, 23523, 22899, - 23521, 22896, 23518, 22895, 23517, 22919, 23551, 22913, 23539, 22950, - 23586, 22947, 23583, 22951, 23587, 22958, 23600, 22959, 23601, 22969, - 23610, 22965, 23597, 22975, 23632, 22977, 23637, 22976, 23636, 22995, - 23659, 22994, 23658, 22989, 23651, 22985, 23646, 23026, 23695, 23025, - 23694, 23003, 23687, 23004, 23688, 23054, 23730, 23056, 23734, 23065, - 23748, 23063, 23746, 23064, 23747, 23070, 23753, 23091, 23791, 23089, - 23789, 23088, 23782, 23083, 23784, 23090, 23790, 23112, 23831, 23111, - 23830, 23113, 23834, 23107, 23824, 23143, 23906, 23164, 23929, 23136, - 23899, 23163, 23928, 23155, 23918, 23176, 23949, 23175, 23945, 23189, - 23962, 23190, 23963, 23186, 23959, 23188, 23961, 23187, 23960, 23195, - 23968, 23194, 23967, 23200, 23978, 23211, 23992, 23215, 23996, 23217, - 23998, 23525, 23829, 23964, 23988, 23318, 23625, 23624, 23626, 23101, - 23415, 22750, 23311, 22766, 23329, 22762, 23325, 22761, 23324, 22760, - 23323, 22764, 23327, 22763, 23326, 22745, 23306, 22744, 23305, 22743, - 23304, 22747, 23308, 22746, 23307, 22844, 23447, 22855, 23460, 22847, - 23451, 22835, 23438, 22834, 23437, 22833, 23436, 22838, 23441, 22837, - 23440, 22920, 23552, 22910, 23543, 23017, 23680, 23037, 23706, 23009, - 23672, 23008, 23671, 23007, 23670, 23011, 23674, 23010, 23673, 23034, - 23703, 23033, 23702, 23032, 23701, 23036, 23705, 23035, 23704, 23146, - 23909, 23153, 23916, 23150, 23913, 23149, 23912, 23148, 23911, 23152, - 23915, 23151, 23914, 23201, 23979, 23199, 23977, 23203, 23981, 23207, - 23987, 22980, 23640, 22981, 23641, 23204, 23982, 18447, 18439, 18452, - 18444, 18448, 18440, 18450, 18442, 18213, 18205, 18216, 18208, 18214, - 18206, 18218, 18210, 18470, 18467, 18472, 18469, 18471, 18468, 40951, - 40951, 18253, 18250, 18255, 18252, 18254, 18251, 40951, 40951, 18485, - 18477, 18490, 18482, 18486, 18478, 18488, 18480, 18237, 18229, 18240, - 18232, 18238, 18230, 18242, 18234, 18511, 18502, 18514, 18505, 18513, - 18504, 18512, 18503, 18265, 18260, 18268, 18263, 18267, 18262, 18266, - 18261, 18572, 18569, 18574, 18571, 18573, 18570, 40951, 40951, 18299, - 18296, 18301, 18298, 18300, 18297, 40951, 40951, 18531, 18522, 18534, - 18525, 18533, 18524, 18532, 18523, 40951, 18311, 40951, 18314, 40951, - 18313, 40951, 18312, 18552, 18544, 18557, 18549, 18553, 18545, 18555, - 18547, 18283, 18275, 18286, 18278, 18284, 18276, 18288, 18280, 18437, - 18457, 18474, 18473, 18497, 18495, 18517, 18518, 18576, 18575, 18537, - 18538, 18564, 18562, 40951, 40951, 18454, 18446, 18453, 18445, 18449, - 18441, 18451, 18443, 18220, 18212, 18217, 18209, 18215, 18207, 18219, - 18211, 18492, 18484, 18491, 18483, 18487, 18479, 18489, 18481, 18244, - 18236, 18241, 18233, 18239, 18231, 18243, 18235, 18559, 18551, 18558, - 18550, 18554, 18546, 18556, 18548, 18290, 18282, 18287, 18279, 18285, - 18277, 18289, 18281, 18436, 18461, 18438, 18459, 18458, 40951, 18455, - 18456, 18222, 18226, 18223, 18224, 18221, 18396, 18424, 18425, 18429, - 18345, 18498, 18499, 18496, 40951, 18493, 18494, 18257, 18256, 18247, - 18246, 18245, 18428, 18427, 18426, 18516, 18520, 18509, 18508, 40951, - 40951, 18515, 18507, 18269, 18273, 18270, 18271, 40951, 18353, 18352, - 18351, 18536, 18540, 18529, 18528, 18584, 18583, 18535, 18527, 18316, - 18320, 18317, 18318, 18306, 18347, 18346, 18623, 40951, 40951, 18565, - 18566, 18563, 40951, 18560, 18561, 18303, 18302, 18293, 18292, 18291, - 18421, 18350, 40951, 16764, 16761, 16766, 16763, 37019, 17510, 33761, - 17439, 31718, 36992, 18978, 40757, 40756, 40758, 24189, 32047, 20456, - 29361, 17438, 16765, 16762, 20402, 11250, 11224, 24112, 31977, 33636, - 33634, 24065, 31942, 11223, 11218, 10529, 11217, 4995, 37548, 30526, - 37694, 20425, 20459, 24496, 31062, 24188, 32046, 31593, 24187, 32045, - 28962, 31296, 31297, 31679, 11232, 37562, 31885, 31879, 31893, 6007, - 33635, 33637, 31902, 11254, 20799, 30878, 37745, 6293, 6008, 2529, 20458, - 17514, 24120, 31988, 11255, 31742, 17353, 37393, 31884, 3854, 3896, - 25158, 31890, 8045, 37706, 8451, 34742, 20817, 17476, 36999, 31738, - 17503, 17463, 37695, 17504, 11196, 37573, 38824, 26974, 39366, 17636, - 20819, 20821, 20820, 40951, 24186, 32044, 17456, 31592, 20713, 7, 20712, - 6, 28957, 29359, 34713, 34701, 40951, 40951, 34708, 34707, 34710, 34709, - 34700, 34714, 34704, 34706, 34699, 34703, 34705, 34702, 34543, 34545, - 34542, 34541, 34538, 34537, 34540, 34539, 34533, 34544, 34534, 34536, - 34532, 34531, 34535, 40951, 24016, 24017, 24025, 24031, 24015, 24018, - 24021, 24022, 24023, 24024, 24026, 24014, 24028, 40951, 40951, 40951, - 17350, 8058, 8697, 17520, 25104, 27586, 28890, 31320, 32367, 39370, - 29093, 11190, 17351, 22555, 37582, 11372, 17901, 31321, 18673, 2540, - 20465, 6126, 25105, 34096, 36765, 20703, 37664, 29411, 25663, 32236, - 22737, 3762, 34076, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 8354, 8408, 8368, - 8420, 8066, 8082, 8347, 8405, 8413, 8081, 8065, 8427, 8213, 8204, 8207, - 8210, 8205, 8357, 8206, 8208, 8209, 8397, 8194, 8067, 8434, 8446, 8359, - 8365, 8412, 8358, 8346, 8404, 8069, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 0, - 100, 11266, 10553, 6130, 6009, 5260, 17352, 32555, 10555, 32552, 32544, - 3944, 11267, 31482, 31483, 32545, 3945, 32546, 32553, 25335, 11268, - 29454, 33992, 32548, 11264, 11269, 32549, 3946, 11270, 31655, 31863, - 32658, 36660, 37535, 38817, 11271, 30872, 30880, 20816, 3947, 37687, - 21610, 961, 32541, 3943, 16820, 32551, 32542, 32543, 37671, 32547, 32554, - 346, 3653, 17904, 10542, 20710, 32224, 17418, 11278, 11277, 11263, 11265, - 11279, 37680, 37681, 31889, 37682, 11276, 11272, 11273, 11274, 11275, - 31683, 37666, 31298, 2600, 37684, 34821, 38966, 38964, 38968, 38969, - 38974, 38962, 38973, 38972, 38960, 38967, 38959, 38961, 38970, 38958, - 38975, 17515, 32192, 32203, 32204, 32191, 32188, 32197, 32199, 32207, - 32208, 32200, 32206, 32202, 32185, 32193, 32189, 32195, 33823, 33827, - 33828, 33822, 33819, 33831, 33830, 33818, 33832, 33829, 33817, 33826, - 33821, 33824, 33820, 33825, 32196, 32190, 32201, 32205, 23774, 32198, - 32187, 32186, 32194, 38976, 37675, 37674, 40951, 40951, 40951, 40951, - 24190, 37896, 32049, 11308, 24094, 37767, 29392, 29365, 33964, 33979, - 24210, 32073, 24274, 32150, 24271, 37946, 32149, 11344, 24213, 32076, - 24221, 37909, 32056, 11322, 37768, 24219, 32082, 24204, 32068, 24102, - 24097, 11348, 37906, 37907, 11317, 11318, 32064, 11309, 972, 8029, 29394, - 24201, 977, 8031, 24240, 24234, 37923, 37920, 32110, 32104, 11359, 11356, - 32084, 37898, 24224, 24286, 37949, 32116, 11367, 24241, 32099, 24277, - 24101, 32092, 24275, 37913, 32090, 11349, 24099, 37770, 29405, 29378, - 33976, 33989, 24261, 32140, 24290, 32120, 37899, 11310, 24281, 37914, - 32096, 11350, 24200, 32063, 24272, 37950, 32151, 11346, 37955, 37951, - 37953, 37952, 37957, 37958, 32152, 29393, 33967, 37772, 31929, 11320, - 37007, 24220, 32083, 24096, 24206, 32066, 24095, 24285, 32115, 24104, - 17498, 8456, 31208, 36987, 36986, 16754, 20631, 28846, 16703, 29414, - 33803, 8464, 11016, 33796, 16768, 28805, 28793, 28797, 27590, 27597, - 11191, 10998, 32663, 2528, 32161, 4996, 34323, 8703, 17509, 31682, 20704, - 31919, 957, 26848, 34099, 11002, 11017, 31067, 29419, 25119, 25126, - 20792, 37747, 20777, 11221, 37572, 8470, 34735, 38954, 8032, 8023, 974, - 36988, 3654, 31773, 31681, 11192, 17355, 17726, 20446, 37297, 31891, - 20813, 33756, 39374, 29424, 27596, 2533, 29415, 1054, 1057, 29049, 362, - 29416, 364, 37563, 359, 16806, 17724, 11008, 1058, 17725, 1055, 20594, - 8057, 16804, 32159, 32160, 8649, 16821, 16807, 34488, 10558, 16790, - 26859, 31744, 29426, 20470, 29427, 34524, 24390, 18137, 24392, 18140, - 24381, 18130, 28542, 28541, 3652, 29425, 29429, 29428, 29054, 29051, - 24389, 18136, 29053, 29050, 24391, 18138, 29055, 29052, 31656, 34561, - 31665, 34570, 31664, 34569, 11019, 11022, 34549, 34721, 29412, 29413, - 34555, 34727, 29047, 29048, 34554, 34726, 28295, 28296, 28297, 34196, - 34285, 34198, 34287, 34131, 34138, 6804, 6750, 6809, 6542, 6555, 6805, - 6531, 6814, 6556, 34456, 34449, 34470, 34404, 32006, 24138, 11285, 37776, - 2534, 27605, 37579, 17499, 37566, 11248, 11021, 29423, 11024, 29046, - 31666, 34571, 29409, 8465, 29410, 8466, 30556, 20593, 28298, 20217, - 20800, 39395, 28891, 29364, 31925, 32002, 28802, 28803, 28804, 28798, - 10837, 11193, 34490, 11000, 4372, 24152, 31964, 24110, 31975, 31892, - 9946, 9945, 11242, 11241, 11220, 11245, 31476, 16791, 24395, 18146, - 38867, 38866, 24379, 18141, 16789, 16788, 16786, 16787, 11020, 11023, - 29420, 29421, 34197, 34286, 24380, 18129, 31663, 34568, 29417, 11014, - 29418, 11015, 38823, 27582, 37775, 11288, 16704, 16706, 33804, 16709, - 16708, 33805, 16707, 16705, 8467, 8468, 33797, 8469, 33798, 40669, 10838, - 16701, 20440, 37759, 11289, 31680, 31315, 39140, 24061, 31937, 24149, - 31946, 4355, 4352, 37483, 37479, 31882, 34229, 2524, 32565, 32561, 36658, - 31601, 38878, 31479, 37678, 39131, 20438, 37478, 37482, 4351, 4354, - 37472, 4349, 17524, 33863, 37760, 30548, 16817, 39379, 21612, 24159, - 32027, 16816, 3650, 10524, 360, 34831, 37499, 11009, 8475, 33788, 8651, - 8652, 1002, 1040, 1026, 1014, 1015, 1031, 1012, 982, 987, 1037, 1042, - 1028, 1027, 1022, 1029, 1010, 1034, 1023, 1030, 985, 994, 993, 1016, - 1019, 995, 1048, 1021, 1045, 991, 1020, 1018, 1046, 998, 1017, 1033, 992, - 999, 1008, 986, 1047, 1032, 983, 1013, 1044, 989, 1038, 1007, 984, 996, - 1009, 1050, 1049, 988, 990, 1051, 1039, 1036, 1025, 1024, 997, 1043, - 1000, 1035, 1005, 1004, 1041, 1001, 1006, 1003, 29430, 31924, 32844, - 3546, 38839, 20776, 8473, 10924, 16760, 8455, 39283, 16782, 365, 19928, - 6586, 6808, 4936, 37746, 28178, 20462, 30544, 30545, 31220, 31221, 10919, - 33880, 1011, 10549, 31667, 29278, 31669, 8052, 24155, 24156, 24154, - 31971, 31972, 31970, 24117, 24124, 24116, 31985, 31992, 31984, 24059, - 24057, 24058, 9948, 31935, 31933, 31934, 20787, 20406, 37828, 37867, - 34574, 34572, 37486, 4358, 4359, 31753, 24158, 32008, 20415, 20416, - 20417, 20418, 10571, 10569, 10573, 10562, 10566, 10574, 10563, 10567, - 10572, 10561, 10565, 10560, 10564, 10570, 10568, 34164, 31864, 17382, - 38834, 27419, 27411, 27418, 27412, 27416, 27417, 27415, 27414, 27413, - 11539, 17640, 37474, 4362, 37456, 4361, 37487, 4365, 39292, 3651, 34516, - 17468, 8, 16702, 10550, 3885, 3847, 3928, 3824, 3886, 3848, 3888, 367, - 34514, 37305, 20439, 3864, 3867, 3869, 3861, 11246, 3907, 3771, 31615, - 31612, 31613, 31614, 29932, 34816, 34824, 34825, 34805, 34803, 34806, - 34817, 34788, 34789, 34810, 34812, 34811, 34809, 34790, 34822, 34823, - 34792, 34801, 34800, 34799, 34798, 34814, 34828, 34804, 34791, 34802, - 34826, 34807, 34808, 34819, 34818, 34820, 34829, 34793, 3951, 30531, - 34815, 34796, 34827, 34795, 34794, 34797, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 29944, 29939, - 29942, 29943, 29936, 29937, 29935, 29934, 29941, 29938, 29940, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 6552, 6549, 6548, 6545, 6544, 6547, 6546, 6553, 6551, 6773, 6753, 6798, - 6786, 6768, 6756, 6772, 6770, 6752, 6799, 6787, 31154, 31152, 31151, - 31148, 31147, 31150, 31149, 31155, 31153, 31146, 31137, 31145, 31143, - 31138, 31139, 31141, 31142, 31136, 31140, 31144, 10859, 10871, 10868, - 10853, 10850, 10865, 10862, 10877, 10856, 29450, 29443, 29451, 29449, - 29444, 29445, 29446, 29448, 29442, 29453, 29452, 31182, 31183, 31184, - 31185, 31186, 31187, 31188, 31189, 31190, 31191, 31192, 31193, 31194, - 31195, 31196, 31197, 31198, 31199, 31200, 31201, 31202, 31203, 31204, - 31205, 31206, 31207, 6696, 6697, 6698, 6699, 6700, 6701, 6702, 6703, - 6704, 6705, 6706, 6707, 6708, 6709, 6710, 6711, 6712, 6713, 6714, 6715, - 6716, 6717, 6718, 6719, 6720, 6721, 6722, 6723, 6724, 6725, 6726, 6727, - 6728, 6729, 6730, 6731, 6732, 6733, 6734, 6735, 6736, 6737, 6738, 6739, - 6740, 6741, 6742, 6743, 6744, 6745, 6746, 6747, 6550, 28971, 28969, - 28967, 28972, 28973, 28975, 28976, 28970, 28974, 28968, 11212, 11210, - 11209, 11206, 11205, 11208, 11207, 11213, 11211, 11204, 28966, 4484, - 4430, 4500, 4416, 4493, 4429, 4492, 4428, 4491, 4427, 4490, 4426, 4481, - 4400, 4394, 4423, 4480, 4398, 4392, 4422, 4499, 4528, 4522, 4415, 4498, - 4526, 4520, 4413, 4507, 4544, 4521, 4393, 4541, 4399, 4527, 4419, 4506, - 4543, 4519, 4391, 4540, 4397, 4525, 4418, 4479, 4434, 4512, 4402, 4396, - 4515, 4437, 4421, 4497, 4433, 4511, 4529, 4523, 4514, 4436, 4414, 4505, - 4435, 4513, 4542, 4518, 4395, 4539, 4439, 4517, 4432, 4510, 4401, 4524, - 4516, 4438, 4417, 4483, 4425, 4482, 4424, 4390, 4386, 4407, 4404, 4382, - 4406, 4403, 4381, 4534, 4531, 4385, 4533, 4530, 4384, 4546, 4537, 4389, - 4545, 4536, 4388, 4408, 4405, 4380, 4535, 4532, 4383, 4547, 4538, 4387, - 4441, 4440, 4442, 4443, 4472, 4466, 4476, 4488, 4495, 4509, 4478, 4409, - 4411, 4431, 4420, 4489, 4496, 4410, 4412, 37805, 25259, 25258, 25261, - 25169, 25251, 25264, 25260, 17531, 24111, 24134, 24147, 24075, 24135, - 24090, 24091, 31950, 24410, 26976, 10536, 37853, 31967, 31735, 31736, - 31727, 31728, 31729, 31730, 31731, 31732, 31733, 31734, 3905, 39270, - 39279, 39273, 34342, 34351, 34341, 34348, 34350, 34340, 3902, 39267, - 3898, 39255, 3935, 39302, 3879, 39251, 3930, 39299, 3929, 39298, 3887, - 39259, 3892, 39258, 3891, 39257, 3826, 39214, 3825, 39213, 3852, 39240, - 3851, 39239, 3850, 39238, 3816, 39203, 39204, 17460, 25266, 39189, 11195, - 6525, 4999, 3767, 6517, 6526, 6518, 6523, 6522, 6524, 24073, 31948, - 20804, 20808, 37809, 25163, 37833, 37869, 25213, 25193, 37814, 25170, - 3859, 3858, 3931, 3932, 39155, 34345, 34352, 34347, 34344, 39282, 39300, - 37791, 37792, 22734, 39280, 39276, 39277, 39281, 39196, 39194, 39195, - 39197, 37831, 37886, 25204, 39247, 3874, 39246, 3873, 25227, 3909, 8046, - 37740, 33870, 8458, 3918, 39289, 24420, 37024, 34575, 2525, 10578, 8476, - 30552, 3924, 39291, 2750, 2756, 2757, 32374, 37742, 20434, 39264, 3916, - 32818, 31887, 3857, 3884, 39237, 39296, 39261, 39212, 33767, 6123, 31751, - 3764, 5259, 981, 30652, 6480, 8684, 8683, 34491, 17428, 102, 19104, - 31286, 40667, 37551, 37552, 37557, 37554, 37556, 37555, 37553, 37550, - 39151, 39224, 39268, 3904, 39287, 17453, 22738, 27408, 17434, 11535, - 25571, 20931, 32444, 37959, 29280, 31581, 2523, 36647, 17723, 5997, - 24295, 38880, 25108, 32537, 32372, 6003, 2601, 31474, 39161, 39177, - 39180, 39156, 39164, 39174, 3787, 3803, 3806, 3782, 3790, 3800, 3917, - 39227, 39208, 3815, 39269, 3836, 3821, 39198, 20435, 31740, 16657, 3532, - 3533, 28302, 28301, 28303, 39149, 11540, 37756, 31781, 31782, 31783, - 31784, 31785, 31786, 31787, 31788, 3934, 31780, 31213, 31318, 39152, - 10841, 10842, 10843, 10844, 10845, 10846, 39191, 39193, 3768, 3770, - 28175, 28176, 10881, 10884, 10883, 10882, 39218, 3832, 19105, 979, 8692, - 34485, 32532, 345, 17475, 17719, 34486, 2536, 17473, 30859, 37001, 37000, - 39125, 20285, 11281, 11282, 20791, 25570, 25569, 25568, 38840, 20426, - 26985, 26962, 26975, 25737, 11003, 37758, 8675, 17637, 29090, 6133, - 31022, 20932, 38870, 6483, 3875, 32665, 32577, 31746, 32660, 33875, 3469, - 34418, 39215, 39216, 3829, 3830, 33871, 34577, 31756, 3911, 37023, 37670, - 37669, 39209, 8693, 10923, 30551, 31459, 6065, 19927, 6538, 6134, 29352, - 366, 3925, 39294, 3855, 39235, 11381, 19785, 24062, 34461, 17422, 3922, - 31860, 31861, 2532, 19711, 31293, 32026, 24185, 20814, 3780, 32823, 6515, - 6125, 20390, 17720, 17721, 25666, 28194, 37741, 17511, 17470, 17436, - 32528, 34163, 33765, 20469, 31305, 36766, 20829, 19687, 17639, 9941, - 39219, 3912, 37798, 3915, 25245, 39262, 39228, 36659, 36646, 226, 16779, - 31769, 31761, 38872, 39377, 25244, 31295, 37868, 39250, 3877, 6299, - 19709, 28294, 19745, 2760, 19701, 30861, 19792, 30535, 19747, 23224, - 32670, 30858, 25572, 34489, 17507, 17505, 19730, 17501, 3833, 39223, - 34084, 34518, 6811, 30533, 3781, 30860, 19749, 31471, 32669, 19700, - 30534, 16656, 16651, 16649, 33759, 16650, 19723, 37690, 33762, 36652, - 30532, 19775, 33757, 3831, 39220, 16652, 6800, 19774, 33873, 37295, - 19708, 34083, 19768, 2749, 16655, 19725, 8689, 32668, 29038, 25243, - 37865, 25225, 37883, 3942, 39254, 39217, 3818, 19727, 24417, 26982, - 19791, 19758, 19759, 19719, 19720, 19742, 19741, 9953, 19728, 19734, - 19704, 32221, 17474, 32220, 26969, 26972, 26963, 26964, 26970, 26973, - 19738, 19751, 19737, 19750, 24409, 24407, 26968, 26971, 10906, 10904, - 10903, 10900, 10899, 10902, 10901, 10907, 10905, 10898, 10917, 10914, - 10913, 10910, 10909, 10912, 10911, 10918, 10916, 10908, 10896, 10893, - 10892, 10889, 10888, 10891, 10890, 10897, 10895, 10887, 19787, 19790, - 19746, 19716, 19764, 19752, 19771, 11373, 19755, 37545, 19777, 10539, - 19715, 3893, 37015, 37018, 3894, 19702, 19703, 34479, 19714, 32042, - 24175, 2613, 17522, 19743, 19782, 29432, 9947, 29434, 6588, 39306, 3948, - 3950, 3949, 19705, 19707, 19706, 36653, 19776, 39145, 19788, 30546, - 11214, 36998, 39293, 31299, 30542, 30541, 24109, 31974, 30654, 31871, - 34732, 38821, 26434, 25134, 26255, 34446, 34447, 39207, 980, 16711, - 25241, 37826, 24093, 31965, 17530, 22726, 22725, 24040, 24039, 24089, - 25149, 25138, 37777, 25267, 39199, 39200, 39201, 39275, 39278, 26435, - 26429, 26438, 26432, 26437, 26431, 26436, 26430, 26439, 26433, 37927, - 11363, 976, 8025, 31928, 25139, 25144, 25137, 25141, 25146, 25136, 25140, - 25145, 25142, 25147, 25148, 4925, 4670, 4798, 4671, 4862, 4735, 4799, - 4672, 4894, 4767, 4831, 4704, 4863, 4736, 4800, 4673, 4910, 4783, 4847, - 4720, 4879, 4752, 4816, 4689, 4895, 4768, 4832, 4705, 4864, 4737, 4801, - 4674, 4918, 4791, 4855, 4728, 4887, 4760, 4824, 4697, 4903, 4776, 4840, - 4713, 4872, 4745, 4809, 4682, 4911, 4784, 4848, 4721, 4880, 4753, 4817, - 4690, 4896, 4769, 4833, 4706, 4865, 4738, 4802, 4675, 4922, 4795, 4859, - 4732, 4891, 4764, 4828, 4701, 4907, 4780, 4844, 4717, 4876, 4749, 4813, - 4686, 4915, 4788, 4852, 4725, 4884, 4757, 4821, 4694, 4900, 4773, 4837, - 4710, 4869, 4742, 4806, 4679, 4919, 4792, 4856, 4729, 4888, 4761, 4825, - 4698, 4904, 4777, 4841, 4714, 4873, 4746, 4810, 4683, 4912, 4785, 4849, - 4722, 4881, 4754, 4818, 4691, 4897, 4770, 4834, 4707, 4866, 4739, 4803, - 4676, 4924, 4797, 4861, 4734, 4893, 4766, 4830, 4703, 4909, 4782, 4846, - 4719, 4878, 4751, 4815, 4688, 4917, 4790, 4854, 4727, 4886, 4759, 4823, - 4696, 4902, 4775, 4839, 4712, 4871, 4744, 4808, 4681, 4921, 4794, 4858, - 4731, 4890, 4763, 4827, 4700, 4906, 4779, 4843, 4716, 4875, 4748, 4812, - 4685, 4914, 4787, 4851, 4724, 4883, 4756, 4820, 4693, 4899, 4772, 4836, - 4709, 4868, 4741, 4805, 4678, 4923, 4796, 4860, 4733, 4892, 4765, 4829, - 4702, 4908, 4781, 4845, 4718, 4877, 4750, 4814, 4687, 4916, 4789, 4853, - 4726, 4885, 4758, 4822, 4695, 4901, 4774, 4838, 4711, 4870, 4743, 4807, - 4680, 4920, 4793, 4857, 4730, 4889, 4762, 4826, 4699, 4905, 4778, 4842, - 4715, 4874, 4747, 4811, 4684, 4913, 4786, 4850, 4723, 4882, 4755, 4819, - 4692, 4898, 4771, 4835, 4708, 4867, 4740, 4804, 4677, 32146, 32145, - 24276, 32091, 24100, 32147, 24278, 32093, 11319, 37908, 37945, 11343, - 24280, 32095, 24260, 32139, 32148, 32065, 37910, 11323, 32078, 32077, - 32141, 32143, 32142, 24225, 32085, 24279, 32094, 24202, 32062, 24222, - 32057, 29391, 29371, 29397, 29369, 33968, 33981, 29396, 29368, 33965, - 33980, 32165, 17420, 33966, 29366, 17421, 32166, 29367, 29395, 39134, - 2516, 2515, 2518, 2519, 32043, 24174, 37453, 4360, 37455, 37454, 25223, - 25187, 973, 8022, 32052, 24191, 32831, 32070, 24207, 32061, 24098, 37948, - 24052, 24051, 37765, 37764, 24053, 37766, 24050, 37763, 24239, 32109, - 37922, 11358, 24233, 32103, 37919, 11355, 24238, 32108, 37921, 11357, - 24232, 32102, 37918, 11354, 24235, 37917, 32107, 11352, 24237, 24231, - 32105, 32100, 24236, 24230, 32106, 32101, 37916, 11353, 31940, 16796, - 37299, 24195, 32054, 32053, 24376, 24198, 18125, 34548, 24197, 34718, - 24148, 31945, 37774, 11287, 37565, 40680, 40681, 24140, 32011, 24142, - 32013, 40675, 40671, 40676, 40672, 24121, 31989, 24119, 31986, 24118, - 31987, 24046, 31921, 24047, 31927, 11227, 11252, 24055, 31931, 11202, - 38854, 26857, 31926, 26858, 958, 5, 34100, 34101, 37667, 31877, 959, - 31878, 29930, 29929, 26856, 26855, 26850, 26849, 26854, 26852, 26853, - 26851, 31900, 16758, 16757, 16755, 16756, 6527, 6815, 6802, 6806, 6803, - 6528, 6520, 6529, 37762, 6810, 6533, 6748, 6816, 6519, 6521, 34410, - 34405, 34476, 34462, 34463, 37700, 37544, 37542, 32369, 37541, 32003, - 24126, 38818, 4373, 4374, 3941, 37306, 37307, 39232, 3840, 24145, 32016, - 24063, 31941, 20628, 37233, 20705, 11280, 34353, 20630, 32851, 16797, - 16798, 20471, 18009, 36990, 11300, 11301, 3819, 3860, 39192, 3769, 16810, - 16813, 16809, 16812, 16808, 16811, 32238, 31872, 33927, 31873, 3757, - 3756, 11234, 37561, 24162, 32029, 37308, 27598, 28792, 28790, 28791, - 28800, 28799, 28795, 28796, 37702, 37701, 28794, 28000, 34573, 31737, - 17446, 20786, 20778, 6820, 978, 24492, 24493, 24494, 20779, 31739, 20783, - 20780, 20784, 20782, 20785, 20781, 20929, 22727, 40677, 40678, 40679, - 31573, 31578, 31576, 31572, 31575, 31574, 31577, 27591, 27592, 27594, - 27593, 31569, 31570, 38869, 28293, 28292, 32576, 33848, 28288, 28289, - 6749, 28290, 6543, 31571, 27595, 28291, 20801, 32048, 40673, 372, 20797, - 37751, 37752, 20796, 20795, 37753, 37749, 20794, 37748, 20793, 37750, - 20798, 8038, 8044, 11235, 11236, 8039, 25122, 25130, 11225, 11226, 37698, - 37699, 33787, 33786, 25127, 25124, 25132, 25123, 25131, 25121, 25125, - 25120, 33838, 25129, 25128, 40674, 40670, 16802, 20472, 37559, 37560, - 37301, 37300, 33631, 8477, 16803, 363, 1056, 16793, 31579, 16794, 11215, - 37693, 37009, 16801, 16805, 24393, 18144, 24394, 18145, 24385, 18131, - 24388, 18134, 24386, 18132, 24387, 18133, 24384, 18135, 24378, 18127, - 24377, 18126, 24375, 18123, 24373, 18121, 24372, 18120, 24371, 18124, - 24374, 18122, 33772, 33770, 33773, 33771, 11262, 11261, 11258, 11257, - 33630, 33629, 33628, 33627, 11228, 11230, 11229, 18139, 18128, 24382, - 18142, 24383, 18143, 33846, 22735, 33847, 22736, 16799, 31659, 34564, - 31660, 34565, 31662, 34567, 31658, 34563, 31661, 34566, 31657, 34562, - 11231, 11240, 34559, 34731, 34557, 34729, 34558, 34730, 34556, 34728, - 34551, 34723, 34552, 34724, 34550, 34722, 34553, 34725, 34231, 34318, - 8033, 8035, 8034, 8036, 34546, 34717, 34547, 34716, 34720, 34719, 16710, - 31477, 37539, 17496, 29363, 32835, 32836, 32832, 31300, 38820, 11249, - 38819, 11247, 25133, 32837, 32834, 32833, 11216, 11244, 11238, 31881, - 11018, 38836, 38835, 11286, 31066, 31065, 37564, 37567, 37558, 37571, - 37570, 11260, 11259, 37568, 22724, 11243, 39304, 28801, 29380, 29407, - 33978, 33991, 24103, 24229, 37912, 11325, 29377, 29404, 33975, 33988, - 24105, 37769, 32074, 32075, 24211, 24212, 34346, 34339, 34349, 34343, - 10833, 10834, 10832, 10831, 11198, 3846, 39233, 3939, 39305, 3880, 39252, - 39230, 3837, 20401, 3841, 3863, 39244, 3866, 39248, 3899, 3900, 39265, - 3839, 39231, 3936, 39301, 24049, 37002, 24048, 25143, 24268, 24267, - 24269, 24270, 24205, 24215, 24214, 24263, 24265, 24264, 24199, 39133, - 16795, 31874, 24192, 32060, 32059, 24294, 32155, 31875, 32050, 37298, - 24194, 24193, 32051, 11339, 32830, 32828, 39245, 3901, 39266, 3890, - 39256, 19735, 19748, 19712, 19710, 19713, 33774, 2611, 33775, 2610, 3648, - 32829, 24245, 37931, 32124, 11328, 24107, 37773, 29402, 29375, 33973, - 33986, 24257, 37942, 32136, 11340, 8030, 971, 24256, 37933, 32135, 11330, - 40951, 40951, 29403, 29376, 33974, 33987, 24247, 37941, 32126, 11338, - 20422, 38849, 24246, 37932, 32125, 11329, 24258, 37943, 32137, 11341, - 24228, 37911, 32088, 11327, 968, 969, 967, 970, 31865, 31866, 29275, - 29276, 17502, 32089, 40951, 34830, 37013, 37017, 37014, 37016, 3853, - 3933, 3895, 3827, 11332, 11333, 37935, 37936, 24250, 32129, 24249, 32128, - 3772, 3773, 3774, 3775, 3776, 3778, 3777, 3779, 31912, 31913, 31910, - 31911, 31907, 31909, 31906, 31908, 37956, 37761, 30876, 30875, 30877, - 2755, 6818, 6532, 3906, 3817, 37668, 20400, 3940, 3870, 3862, 3865, 3868, - 29281, 37471, 4348, 24405, 32222, 39222, 32223, 34303, 37744, 18671, - 31585, 31584, 31583, 31582, 37538, 31686, 2531, 20463, 31458, 29058, - 39249, 3820, 37580, 9943, 19685, 40759, 22562, 1053, 97, 38957, 31596, - 24074, 31949, 34492, 34493, 24266, 37947, 32144, 11345, 16815, 16814, - 32666, 32364, 32362, 32363, 32361, 32365, 32366, 16800, 37755, 32657, - 11283, 31219, 31888, 19929, 17909, 17911, 17945, 17919, 17915, 17946, - 17953, 17916, 17952, 17925, 17921, 17920, 17914, 17956, 17927, 17928, - 17929, 17930, 17932, 17934, 17938, 17942, 17955, 17917, 17954, 17931, - 17933, 17935, 17944, 17913, 17937, 17948, 17947, 17949, 17939, 17951, - 17940, 17941, 17950, 17923, 17910, 17922, 17918, 17924, 17936, 17943, - 17926, 17912, 17957, 17959, 17993, 17967, 17963, 17994, 18001, 17964, - 18000, 17973, 17969, 17968, 17962, 18004, 17975, 17976, 17977, 17978, - 17980, 17982, 17986, 17990, 18003, 17965, 18002, 17979, 17981, 17983, - 17992, 17961, 17985, 17996, 17995, 17997, 17987, 17999, 17988, 17989, - 17998, 17971, 17958, 17970, 17966, 17972, 17984, 17991, 17974, 17960, - 22957, 23603, 22960, 23051, 23067, 23337, 23828, 22900, 23522, 22946, - 23582, 23214, 23995, 22780, 22979, 23118, 23119, 23948, 23191, 23965, - 23947, 22905, 23529, 23893, 23450, 23869, 23684, 23261, 24020, 27718, - 23094, 23218, 8485, 8579, 8535, 8629, 8499, 8593, 8496, 8590, 8540, 8634, - 8530, 8624, 8534, 8628, 8501, 8595, 8532, 8626, 8538, 8632, 8504, 8598, - 8509, 8603, 8541, 8635, 8542, 8636, 8505, 8599, 8510, 8604, 8537, 8631, - 8539, 8633, 8531, 8625, 8533, 8627, 8543, 8637, 8507, 8601, 8503, 8597, - 8536, 8630, 8526, 8620, 8493, 8587, 8521, 8615, 8489, 8583, 8494, 8588, - 8495, 8589, 8490, 8584, 8519, 8613, 8529, 8623, 8491, 8585, 8517, 8611, - 8520, 8614, 8484, 8578, 8492, 8586, 8512, 8606, 8513, 8607, 8508, 8602, - 8515, 8609, 8514, 8608, 8511, 8605, 8518, 8612, 8516, 8610, 8524, 8618, - 8522, 8616, 8523, 8617, 8525, 8619, 8639, 8640, 8641, 8643, 8644, 8638, - 8642, 8487, 8581, 8488, 8582, 8483, 8482, 8481, 8486, 8580, 40951, 40951, - 40951, 40951, 40951, 8577, 8574, 8575, 8576, 8572, 8573, 8645, 17768, - 17794, 17779, 17800, 17801, 17799, 17789, 17790, 17802, 17783, 17792, - 17795, 17797, 17803, 17785, 17788, 17793, 17787, 17791, 17804, 17784, - 17782, 17778, 17798, 17786, 17774, 17777, 17781, 17776, 17775, 17796, - 17780, 17769, 17773, 17771, 17806, 17770, 17772, 40951, 17805, 40951, - 40951, 40951, 40951, 40951, 17767, 40951, 40951, 37248, 37259, 37260, - 37253, 37255, 37238, 37277, 37249, 37252, 37250, 37251, 37287, 37276, - 37256, 37242, 37258, 37261, 37237, 37246, 37262, 37275, 37257, 37243, - 37282, 37247, 37288, 37270, 37236, 37244, 37278, 37279, 37280, 37241, - 37245, 37281, 37290, 37272, 37273, 37254, 37240, 37235, 37263, 37265, - 37264, 37266, 37267, 37274, 37268, 37283, 37284, 37285, 37269, 37239, - 37271, 37286, 37289, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 37291, 37292, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 37234, 17250, 17067, 17154, - 17194, 17170, 16857, 17232, 16895, 17085, 17076, 16977, 17310, 16925, - 16910, 17241, 17201, 16886, 17101, 17114, 16962, 16966, 16965, 16964, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 17184, - 17190, 17188, 17185, 17187, 17186, 17189, 40951, 16876, 16882, 16880, - 16877, 16879, 16878, 16881, 40951, 17300, 17306, 17304, 17301, 17303, - 17302, 17305, 40951, 16869, 16875, 16873, 16870, 16872, 16871, 16874, - 40951, 17136, 17142, 17140, 17137, 17139, 17138, 17141, 40951, 17045, - 17051, 17049, 17046, 17048, 17047, 17050, 40951, 17277, 17283, 17281, - 17278, 17280, 17279, 17282, 40951, 16988, 16994, 16992, 16989, 16991, - 16990, 16993, 40951, 8091, 8129, 8126, 8093, 8123, 8124, 8130, 8097, - 8098, 8099, 8106, 8128, 8100, 8094, 8122, 8119, 8121, 8125, 8109, 8108, - 8127, 8095, 8131, 8105, 8092, 8118, 8114, 8116, 8103, 8117, 8090, 8102, - 31923, 31922, 24125, 31993, 24067, 31938, 31760, 31759, 11201, 24128, - 32001, 31768, 24108, 31973, 11542, 31064, 17495, 31883, 20461, 11200, - 11324, 37895, 11203, 11251, 20810, 31023, 20457, 37304, 24088, 31963, - 37303, 37302, 24157, 32007, 37480, 37484, 4353, 4356, 24113, 31978, - 24066, 31943, 37696, 30525, 34406, 17464, 31898, 38852, 32158, 39365, - 37672, 31758, 31770, 37683, 10530, 10531, 37673, 37469, 37708, 37020, - 34506, 38855, 39348, 6002, 11219, 31897, 11222, 10537, 11239, 20811, - 20812, 25159, 25160, 11237, 11197, 37569, 26959, 31063, 31717, 8648, - 8685, 8686, 37392, 26958, 26960, 24123, 31991, 24122, 31990, 37461, - 37465, 4339, 4343, 29931, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 7916, 7869, 7919, - 7918, 7917, 7912, 7840, 7937, 7951, 7950, 7873, 7920, 7933, 7932, 7902, - 7901, 7900, 7899, 7929, 7938, 7928, 7927, 7888, 7887, 7891, 7915, 40951, - 7872, 7935, 7909, 7874, 7907, 7867, 7943, 7942, 7881, 7911, 7910, 7921, - 7871, 7875, 7896, 7838, 7880, 7931, 7930, 7843, 7926, 7854, 7949, 7948, - 7947, 7946, 7904, 7934, 7914, 7879, 7952, 7842, 7841, 7903, 7908, 7885, - 7884, 7883, 7939, 7870, 7945, 7944, 7858, 7922, 7890, 7857, 7856, 7882, - 7866, 7923, 7941, 7940, 7868, 7850, 7898, 7897, 7853, 7851, 7906, 7905, - 7913, 7844, 7860, 7852, 7862, 7848, 7878, 7877, 7876, 7846, 7889, 7865, - 7839, 7886, 7847, 7864, 7855, 7924, 7925, 7849, 7895, 7845, 7893, 7861, - 7894, 7863, 7936, 7892, 7859, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 21128, 21110, 21044, 21164, - 21152, 21096, 21196, 21111, 21124, 21107, 21059, 21063, 21048, 21033, - 21099, 21185, 21131, 21102, 21136, 21213, 21170, 21141, 21093, 21198, - 21042, 21153, 21029, 21133, 21004, 21122, 21058, 21056, 21151, 21079, - 21081, 21061, 21011, 21210, 21036, 21144, 21098, 21180, 21103, 21031, - 21169, 21120, 21142, 21211, 21129, 21194, 21054, 21159, 21045, 21113, - 21197, 21160, 21019, 21178, 21020, 21172, 21090, 21087, 21050, 21088, - 21021, 21139, 21150, 21043, 21006, 21181, 21126, 21183, 21149, 21123, - 21193, 21104, 21173, 21038, 21204, 21049, 21032, 21078, 21027, 21171, - 21203, 21070, 21028, 21065, 21047, 21086, 21165, 21067, 21035, 21051, - 21134, 21100, 21114, 21188, 21177, 21109, 21215, 21068, 21015, 21161, - 21046, 21206, 21182, 21041, 21064, 21166, 21002, 21175, 21168, 21192, - 21082, 21026, 21176, 21007, 21143, 21162, 21101, 21127, 21157, 21076, - 21132, 21005, 21135, 21055, 21022, 21115, 21116, 21154, 21003, 21118, - 21189, 21130, 21016, 21174, 21034, 21083, 21187, 21097, 21012, 21202, - 21030, 21205, 21155, 21095, 21167, 21199, 21023, 21137, 21008, 21156, - 21146, 21145, 21077, 21018, 21025, 21009, 21119, 21201, 21037, 21209, - 21040, 21200, 21080, 21112, 21085, 21121, 21163, 21158, 21138, 21014, - 21212, 21066, 21105, 21184, 21108, 21179, 21106, 21208, 21073, 21057, - 21091, 21074, 21094, 21017, 21186, 21089, 21069, 21147, 21024, 21084, - 21071, 21010, 21148, 21039, 21207, 21092, 21214, 21117, 21013, 21062, - 21075, 21191, 21053, 21140, 21125, 21060, 21190, 21052, 21195, 21072, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 20495, 20493, 20494, 20492, - 20507, 20503, 20502, 20499, 20500, 20501, 20497, 20496, 20504, 20498, - 20508, 20506, 20592, 20491, 20590, 11007, 20828, 20591, 20490, 20510, - 24045, 31920, 24064, 31939, 24060, 31936, 24139, 32010, 24054, 31930, - 31598, 17902, 24136, 32005, 24141, 32012, 24144, 32015, 24143, 32014, - 39132, 31880, 11233, 25157, 31599, 19581, 19574, 19571, 19577, 19576, - 19579, 19578, 19582, 19580, 20588, 20587, 20509, 20586, 19240, 19239, - 39138, 38827, 38830, 38828, 38831, 38829, 6801, 20584, 19575, 19573, - 19572, 38826, 25816, 31216, 20589, 20585, 40951, 20305, 20291, 20307, - 20385, 20309, 20387, 20306, 20384, 20308, 20386, 20346, 20336, 20348, - 20338, 20350, 20340, 20347, 20337, 20349, 20339, 20310, 20371, 20312, - 20373, 20314, 20375, 20311, 20372, 20313, 20374, 20366, 20331, 20368, - 20333, 20304, 20370, 20335, 20367, 20332, 20369, 20334, 20326, 20328, - 20330, 20327, 20329, 20341, 20321, 20356, 20343, 20315, 20358, 20345, - 20324, 20360, 20342, 20322, 20357, 20344, 20323, 20359, 20351, 20353, - 20355, 20352, 20354, 20298, 20380, 20300, 20382, 20299, 20381, 20361, - 20363, 20365, 20362, 20364, 20294, 20376, 20378, 20377, 20379, 20325, - 20383, 20301, 20302, 40951, 40951, 8288, 8287, 21453, 21452, 20389, - 20388, 20290, 21450, 21380, 21309, 21382, 21443, 21384, 21445, 21381, - 21442, 21383, 21444, 21405, 21395, 21407, 21397, 21409, 21399, 21406, - 21396, 21408, 21398, 21385, 21430, 21387, 21432, 21389, 21434, 21386, - 21431, 21388, 21433, 21420, 21390, 21422, 21392, 21367, 21424, 21394, - 21421, 21391, 21423, 21393, 21347, 21349, 21351, 21348, 21350, 21400, - 21324, 21410, 21402, 21318, 21412, 21404, 21327, 21414, 21401, 21325, - 21411, 21403, 21326, 21413, 21342, 21328, 21345, 21343, 21344, 21372, - 21439, 21374, 21441, 21373, 21440, 21415, 21417, 21419, 21416, 21418, - 21368, 21435, 21437, 21436, 21438, 21346, 21429, 21362, 21363, 21425, - 21427, 21426, 21428, 21449, 21451, 21448, 21446, 21447, 40951, 40951, - 40951, 40951, 40951, 4320, 4328, 4327, 4325, 4324, 4331, 4296, 4316, - 4284, 4312, 4326, 4322, 4329, 4333, 4309, 4315, 4319, 4330, 4308, 4314, - 4318, 4264, 4300, 4276, 4281, 4265, 4282, 4267, 4307, 4269, 4277, 4270, - 4278, 4283, 4289, 4274, 4295, 4332, 4297, 4286, 4292, 4303, 4299, 40951, - 19493, 19537, 19494, 19499, 19500, 19502, 19529, 19540, 19515, 19516, - 19518, 19520, 19527, 19523, 19519, 19526, 19495, 19505, 19539, 19506, - 19530, 19542, 19487, 19482, 19536, 19481, 19492, 19528, 19513, 19566, - 19477, 19480, 19563, 19564, 19484, 19483, 19550, 19549, 19567, 19546, - 19547, 19568, 19555, 19569, 19545, 19544, 19548, 19559, 19485, 19565, - 19486, 19570, 19538, 19501, 19504, 19503, 19517, 19524, 19521, 19522, - 19525, 19496, 19498, 19497, 19491, 19512, 19510, 19507, 19508, 19511, - 19509, 19489, 19490, 19532, 19533, 19535, 19534, 19531, 19514, 19543, - 19552, 19554, 19553, 19488, 19541, 19551, 19556, 19557, 19558, 19561, - 19560, 19562, 19478, 19479, 40951, 20484, 20487, 20486, 20482, 20480, - 20476, 20483, 20478, 20474, 20477, 20485, 20481, 20475, 20488, 20489, - 20479, 4321, 4310, 4323, 4287, 4280, 4279, 4306, 4302, 4294, 4271, 4290, - 4275, 4293, 4298, 4266, 4268, 4273, 4305, 4301, 4291, 4262, 4263, 4261, - 4260, 4285, 4317, 4311, 4259, 4288, 4313, 4304, 4272, 7983, 7986, 7987, - 7985, 7973, 7959, 7965, 7954, 7964, 7977, 7966, 7962, 7955, 7963, 7960, - 7989, 7953, 7972, 7968, 7981, 7988, 7958, 7967, 7976, 7975, 7982, 7980, - 7969, 7971, 7984, 7979, 7974, 7956, 7961, 7970, 7990, 7957, 7978, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 20505, 21365, - 21377, 21378, 21366, 21376, 21352, 21354, 21356, 21353, 21355, 21379, - 21357, 21359, 21361, 21358, 21360, 31078, 31082, 31094, 31088, 31080, - 31086, 31090, 31096, 31071, 31069, 31076, 31092, 31084, 31074, 31079, - 31083, 31095, 31089, 31081, 31087, 31091, 31097, 31072, 31070, 31077, - 31093, 31085, 31075, 31073, 31135, 31134, 40951, 31133, 31129, 31127, - 31108, 31106, 31126, 31118, 31103, 31112, 31128, 31111, 31105, 31131, - 31130, 31110, 31102, 31125, 31123, 31132, 31120, 31113, 31121, 31104, - 31099, 31109, 31116, 31100, 31122, 31124, 31101, 31114, 31098, 31107, - 31115, 31119, 31117, 6620, 6608, 6630, 6609, 6774, 6788, 6776, 6758, - 6755, 6771, 6769, 6751, 31215, 6789, 6795, 6794, 6791, 6790, 6793, 6792, - 6797, 6796, 6775, 6777, 6783, 6782, 6779, 6778, 6568, 6572, 6584, 6578, - 6570, 6576, 6580, 6561, 6559, 6557, 6566, 6582, 6574, 6564, 6569, 6573, - 6585, 6579, 6571, 6577, 6581, 6562, 6560, 6558, 6567, 6583, 6575, 6565, - 6695, 6694, 6563, 22560, 6643, 6639, 6637, 6605, 6603, 6635, 6626, 6600, - 6618, 6638, 6616, 6602, 6641, 6640, 6614, 6598, 6629, 6634, 6607, 6631, - 6619, 6632, 6601, 6594, 6610, 6625, 6615, 6604, 6628, 6599, 6636, 6591, - 6642, 6622, 6595, 6593, 6606, 6596, 6611, 6612, 6624, 6613, 6623, 6633, - 6627, 6597, 6621, 6589, 6617, 6781, 6780, 6785, 6784, 6757, 6759, 6765, - 6764, 6761, 6760, 6763, 6762, 6767, 6766, 6754, 20575, 20578, 20579, - 20517, 20580, 20576, 20577, 20516, 20582, 20583, 20581, 20549, 34191, - 34157, 34159, 24491, 6689, 6691, 6693, 6690, 6692, 6652, 6654, 6656, - 6653, 6655, 6672, 6674, 6676, 6673, 6675, 6677, 6679, 6681, 6678, 6680, - 6662, 6664, 6666, 6663, 6665, 6647, 6649, 6651, 6648, 6650, 6657, 6659, - 6661, 6658, 6660, 6686, 6688, 6687, 6667, 6669, 6671, 6668, 6670, 6682, - 6684, 6683, 6685, 34155, 34116, 34118, 34117, 34119, 34194, 34195, 34358, - 34158, 34151, 34284, 34288, 34203, 34202, 34201, 34166, 34167, 34171, - 34170, 34204, 34169, 34205, 34208, 34206, 34207, 34172, 34173, 34215, - 34216, 34217, 34214, 34213, 34324, 34325, 34332, 34326, 34327, 34142, - 34146, 34147, 34337, 34277, 34278, 34179, 34291, 34292, 34123, 34300, - 34298, 34299, 34126, 34186, 34185, 34125, 34187, 34180, 34297, 34295, - 34181, 34296, 34294, 34128, 34301, 34127, 34184, 34302, 34182, 34183, - 34241, 34242, 34245, 34244, 34243, 34251, 34252, 34253, 34248, 34249, - 34250, 34356, 34355, 34357, 34319, 34320, 34322, 34321, 34317, 34316, - 34338, 20573, 20574, 20556, 20558, 20565, 20564, 20571, 20569, 20560, - 20567, 20559, 20562, 20555, 20557, 20566, 20563, 20572, 20570, 20561, - 20568, 20550, 20554, 20553, 20552, 20551, 34189, 34141, 34121, 34124, - 34289, 34305, 34143, 34145, 34144, 34199, 34152, 34156, 34153, 34154, - 34133, 34293, 34276, 34258, 34240, 34200, 34222, 34246, 34176, 34130, - 34219, 34306, 34279, 34259, 34260, 34273, 34223, 34192, 34218, 34270, - 34174, 34336, 34261, 34274, 34150, 34225, 34165, 34280, 34262, 34255, - 34134, 34209, 34257, 34136, 34239, 34212, 34256, 34135, 34238, 34211, - 34235, 34236, 34290, 34221, 34272, 34175, 34313, 34314, 34315, 34310, - 34281, 34263, 34275, 34311, 34282, 34264, 34266, 34227, 34267, 34312, - 34283, 34265, 34268, 34228, 34269, 34220, 34237, 34120, 34129, 34139, - 34140, 34137, 34132, 34148, 34177, 34178, 34188, 34193, 34224, 34210, - 34226, 34232, 34233, 34230, 34234, 34247, 34254, 34271, 34307, 34308, - 34304, 34309, 34333, 34334, 34354, 34122, 34114, 20548, 20533, 20521, - 20540, 20539, 20546, 20544, 20535, 20542, 20534, 20537, 20532, 20520, - 20541, 20538, 20547, 20545, 20536, 20543, 20522, 20530, 20528, 20527, - 20524, 20523, 20526, 20525, 20531, 20529, 20518, 20519, 34168, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 20267, 20270, 20231, - 20281, 20278, 20226, 20264, 20243, 20260, 20277, 20255, 20261, 20236, - 20238, 20248, 20282, 20235, 20279, 20219, 20225, 20223, 20242, 20262, - 20256, 20244, 20241, 20249, 20240, 20265, 20268, 20247, 20233, 20257, - 20239, 20254, 20234, 20269, 20252, 20250, 20229, 20228, 20246, 20224, - 20227, 20237, 20253, 20251, 20271, 20258, 20266, 20263, 20275, 20230, - 20273, 20221, 20272, 20274, 20276, 20232, 20280, 20245, 20259, 20220, - 20222, 39884, 39891, 39883, 39890, 39888, 39889, 39886, 39887, 40659, - 40660, 40657, 40658, 40656, 40654, 40655, 40663, 40664, 40661, 40662, - 40666, 40665, 40531, 39551, 39552, 39545, 39550, 39548, 39549, 39546, - 39547, 39555, 39556, 39553, 39554, 39536, 39534, 39535, 39559, 39560, - 39557, 39558, 39544, 39542, 39543, 39540, 39541, 39533, 39539, 39538, - 39537, 39565, 39566, 39561, 39564, 39563, 39562, 40263, 40264, 40258, - 40262, 40261, 40259, 40260, 40267, 40268, 40265, 40266, 40252, 40250, - 40251, 40271, 40272, 40269, 40270, 40256, 40257, 40249, 40255, 40254, - 40253, 40277, 40278, 40273, 40276, 40275, 40274, 39519, 39520, 39513, - 39518, 39516, 39517, 39514, 39515, 39523, 39524, 39521, 39522, 39504, - 39502, 39503, 39527, 39528, 39525, 39526, 39512, 39510, 39511, 39508, - 39509, 39501, 39507, 39506, 39505, 39531, 39532, 39529, 39530, 40066, - 40067, 40061, 40065, 40064, 40062, 40063, 40070, 40071, 40068, 40069, - 40074, 40075, 40072, 40073, 40080, 40081, 40076, 40079, 40078, 40077, - 40086, 40087, 40082, 40085, 40084, 40083, 39809, 39810, 39804, 39808, - 39807, 39805, 39806, 39813, 39814, 39811, 39812, 39798, 39796, 39797, - 39817, 39818, 39815, 39816, 39802, 39803, 39795, 39801, 39800, 39799, - 39823, 39819, 39822, 39821, 39820, 40045, 40046, 40040, 40044, 40043, - 40041, 40042, 40049, 40050, 40047, 40048, 40033, 40034, 40031, 40032, - 40053, 40054, 40051, 40052, 40060, 40059, 40038, 40039, 40030, 40037, - 40036, 40035, 40057, 40058, 40055, 40056, 39690, 39691, 39688, 39689, - 39686, 39687, 39684, 39685, 39683, 39681, 39682, 39700, 39701, 39696, - 39699, 39698, 39697, 39694, 39695, 39692, 39693, 40509, 40510, 40503, - 40508, 40506, 40507, 40504, 40505, 40513, 40514, 40511, 40512, 40517, - 40518, 40515, 40516, 40502, 40501, 40523, 40524, 40519, 40522, 40521, - 40520, 40529, 40530, 40525, 40528, 40527, 40526, 39668, 39669, 39663, - 39667, 39666, 39664, 39665, 39675, 39676, 39673, 39674, 39657, 39656, - 39679, 39680, 39677, 39678, 39672, 39670, 39671, 39661, 39662, 39655, - 39660, 39659, 39658, 40488, 40489, 40483, 40487, 40486, 40484, 40485, - 40495, 40496, 40493, 40494, 40476, 40477, 40474, 40475, 40499, 40500, - 40497, 40498, 40492, 40490, 40491, 40481, 40482, 40473, 40480, 40479, - 40478, 39642, 39643, 39637, 39641, 39640, 39638, 39639, 39649, 39650, - 39647, 39648, 39631, 39629, 39630, 39653, 39654, 39651, 39652, 39646, - 39644, 39645, 39635, 39636, 39628, 39634, 39633, 39632, 40092, 40093, - 40088, 40091, 40090, 40089, 40099, 40100, 40097, 40098, 40103, 40104, - 40101, 40102, 40096, 40094, 40095, 40109, 40110, 40105, 40108, 40107, - 40106, 39839, 39840, 39833, 39838, 39836, 39837, 39834, 39835, 39843, - 39844, 39841, 39842, 39828, 39827, 39825, 39826, 39824, 39832, 39830, - 39831, 39829, 40237, 40238, 40232, 40236, 40235, 40233, 40234, 40241, - 40239, 40240, 40226, 40224, 40225, 40247, 40248, 40245, 40246, 40244, - 40242, 40243, 40230, 40231, 40223, 40229, 40228, 40227, 39777, 39778, - 39772, 39776, 39775, 39773, 39774, 39787, 39788, 39785, 39786, 39766, - 39764, 39765, 39784, 39782, 39783, 39781, 39779, 39780, 39770, 39771, - 39763, 39769, 39768, 39767, 39793, 39794, 39789, 39792, 39791, 39790, - 39992, 39993, 39986, 39991, 39989, 39990, 39987, 39988, 39996, 39997, - 39994, 39995, 39976, 39977, 39974, 39975, 40000, 40001, 39998, 39999, - 39985, 39983, 39984, 39981, 39982, 39973, 39980, 39979, 39978, 40006, - 40007, 40002, 40005, 40004, 40003, 39746, 39747, 39740, 39745, 39743, - 39744, 39741, 39742, 39750, 39751, 39748, 39749, 39733, 39734, 39731, - 39732, 39758, 39759, 39756, 39757, 39754, 39755, 39752, 39753, 39738, - 39739, 39730, 39737, 39736, 39735, 39959, 39960, 39954, 39958, 39957, - 39955, 39956, 39963, 39964, 39961, 39962, 39948, 39946, 39947, 39971, - 39972, 39969, 39970, 39967, 39968, 39965, 39966, 39952, 39953, 39945, - 39951, 39950, 39949, 39706, 39707, 39702, 39705, 39703, 39704, 39720, - 39721, 39718, 39719, 39711, 39712, 39709, 39710, 39728, 39729, 39726, - 39727, 39724, 39725, 39722, 39723, 39716, 39717, 39708, 39715, 39714, - 39713, 40029, 40028, 40022, 40023, 40020, 40021, 40011, 40009, 40010, - 40026, 40027, 40024, 40025, 40019, 40017, 40018, 40015, 40016, 40008, - 40014, 40013, 40012, 39858, 39859, 39852, 39857, 39855, 39856, 39853, - 39854, 39862, 39863, 39860, 39861, 39847, 39848, 39845, 39846, 39866, - 39867, 39864, 39865, 39851, 39849, 39850, 40119, 40117, 40118, 40122, - 40123, 40120, 40121, 40112, 40113, 40111, 40126, 40127, 40124, 40125, - 40116, 40114, 40115, 39762, 39761, 39760, 39877, 39878, 39875, 39876, - 39870, 39871, 39868, 39869, 39881, 39882, 39879, 39880, 39874, 39872, - 39873, 40543, 40544, 40541, 40542, 40534, 40532, 40533, 40540, 40538, - 40539, 40537, 40535, 40536, 40606, 40607, 40601, 40605, 40604, 40602, - 40603, 40642, 40643, 40640, 40641, 40595, 40593, 40594, 40646, 40647, - 40644, 40645, 40639, 40637, 40638, 40599, 40600, 40592, 40598, 40597, - 40596, 40652, 40653, 40648, 40651, 40650, 40649, 39612, 39613, 39606, - 39611, 39609, 39610, 39607, 39608, 39616, 39617, 39614, 39615, 39597, - 39595, 39596, 39620, 39621, 39618, 39619, 39605, 39603, 39604, 39601, - 39602, 39594, 39600, 39599, 39598, 39626, 39627, 39622, 39625, 39624, - 39623, 40620, 40621, 40614, 40619, 40617, 40618, 40615, 40616, 40624, - 40625, 40622, 40623, 40613, 40611, 40612, 40610, 40608, 40609, 40630, - 40626, 40629, 40628, 40627, 40635, 40636, 40631, 40634, 40633, 40632, - 40209, 40210, 40204, 40208, 40207, 40205, 40206, 40213, 40214, 40211, - 40212, 40197, 40196, 40203, 40202, 40222, 40221, 40201, 40195, 40200, - 40199, 40198, 40219, 40220, 40215, 40218, 40217, 40216, 40454, 40455, - 40449, 40453, 40452, 40450, 40451, 40461, 40462, 40459, 40460, 40443, - 40441, 40442, 40465, 40466, 40463, 40464, 40458, 40456, 40457, 40447, - 40448, 40440, 40446, 40445, 40444, 40471, 40472, 40467, 40470, 40469, - 40468, 40390, 40391, 40385, 40389, 40388, 40386, 40387, 40397, 40398, - 40395, 40396, 40401, 40402, 40399, 40400, 40394, 40392, 40393, 40405, - 40406, 40403, 40404, 40411, 40412, 40407, 40410, 40409, 40408, 40576, - 40577, 40574, 40575, 40568, 40566, 40567, 40584, 40585, 40582, 40583, - 40580, 40581, 40578, 40579, 40572, 40573, 40565, 40571, 40570, 40569, - 40590, 40591, 40586, 40589, 40588, 40587, 39578, 39579, 39576, 39577, - 39570, 39571, 39568, 39569, 39586, 39587, 39584, 39585, 39582, 39583, - 39580, 39581, 39575, 39567, 39574, 39573, 39572, 39592, 39593, 39588, - 39591, 39590, 39589, 40358, 40357, 40337, 40336, 40349, 40350, 40347, - 40348, 40345, 40346, 40343, 40344, 40341, 40342, 40335, 40340, 40339, - 40338, 40355, 40356, 40351, 40354, 40353, 40352, 40158, 40159, 40156, - 40157, 40155, 40153, 40154, 40162, 40163, 40160, 40161, 40168, 40169, - 40164, 40167, 40166, 40165, 40174, 40175, 40170, 40173, 40172, 40171, - 40424, 40425, 40422, 40423, 40416, 40414, 40415, 40432, 40433, 40430, - 40431, 40428, 40429, 40426, 40427, 40420, 40421, 40413, 40419, 40418, - 40417, 40438, 40439, 40434, 40437, 40436, 40435, 40373, 40374, 40371, - 40372, 40362, 40360, 40361, 40377, 40378, 40375, 40376, 40370, 40368, - 40369, 40366, 40367, 40359, 40365, 40364, 40363, 40383, 40384, 40379, - 40382, 40381, 40380, 39933, 39934, 39927, 39932, 39930, 39931, 39928, - 39929, 39920, 39921, 39918, 39919, 39937, 39938, 39935, 39936, 39925, - 39926, 39917, 39924, 39923, 39922, 39943, 39944, 39939, 39942, 39941, - 39940, 40295, 40296, 40289, 40294, 40292, 40293, 40290, 40291, 40282, - 40283, 40280, 40281, 40299, 40300, 40297, 40298, 40287, 40288, 40279, - 40286, 40285, 40284, 40305, 40306, 40301, 40304, 40303, 40302, 39907, - 39908, 39901, 39906, 39904, 39905, 39902, 39903, 39895, 39893, 39894, - 39911, 39912, 39909, 39910, 39899, 39900, 39892, 39898, 39897, 39896, - 39915, 39916, 39913, 39914, 40141, 40142, 40135, 40140, 40138, 40139, - 40136, 40137, 40130, 40129, 40145, 40146, 40143, 40144, 40134, 40128, - 40133, 40132, 40131, 40151, 40152, 40147, 40150, 40149, 40148, 40189, - 40190, 40183, 40188, 40186, 40187, 40184, 40185, 40179, 40177, 40178, - 40193, 40194, 40191, 40192, 40181, 40182, 40176, 40180, 40551, 40552, - 40545, 40550, 40548, 40549, 40546, 40547, 40564, 40563, 40555, 40556, - 40553, 40554, 40561, 40562, 40557, 40560, 40559, 40558, 40323, 40324, - 40317, 40322, 40320, 40321, 40318, 40319, 40310, 40311, 40308, 40309, - 40327, 40328, 40325, 40326, 40315, 40316, 40307, 40314, 40313, 40312, - 40333, 40334, 40329, 40332, 40331, 40330, 40951, 40951, 40951, 39498, - 39471, 39469, 39476, 39450, 39486, 39457, 39459, 39475, 39462, 39473, - 39446, 39474, 39492, 39480, 39461, 39487, 39460, 39493, 39451, 39454, - 39447, 39456, 39477, 39488, 39500, 39465, 39496, 39481, 39464, 39491, - 39489, 39485, 39490, 39497, 39468, 39478, 39467, 39458, 39466, 39499, - 39455, 39482, 39472, 39449, 39448, 39453, 39463, 39483, 39494, 39484, - 39452, 39495, 39479, 39470, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 25094, 25083, 25082, 25066, 25064, 25063, 25077, - 25081, 25080, 25096, 25075, 25074, 25065, 25062, 25061, 25098, 25071, - 25097, 25085, 25088, 25089, 25070, 25079, 25100, 25078, 25095, 25099, - 25084, 25087, 25076, 25090, 25091, 25072, 25073, 25101, 25092, 25067, - 25068, 25069, 25093, 25057, 25060, 25058, 25056, 25059, 25055, 25102, - 25103, 38225, 38226, 38062, 38211, 38212, 38185, 37988, 37995, 38098, - 38071, 38105, 38045, 38171, 38199, 38023, 38016, 37974, 37967, 38089, - 38192, 37981, 38124, 38009, 38002, 38037, 38030, 38164, 38178, 38143, - 38206, 38084, 38130, 38051, 38112, 38157, 38150, 38234, 38235, 38066, - 38067, 38220, 38221, 38187, 37990, 37997, 38100, 38077, 38107, 38048, - 38173, 38201, 38025, 38018, 37976, 37969, 38093, 38194, 37983, 38126, - 38011, 38004, 38039, 38032, 38166, 38180, 38145, 38208, 38085, 38135, - 38056, 38114, 38159, 38152, 38232, 38233, 38137, 38064, 38065, 38218, - 38219, 38186, 37989, 37996, 38099, 38075, 38076, 38106, 38047, 38172, - 38200, 38024, 38017, 37975, 37968, 38092, 38193, 37982, 38125, 38010, - 38003, 38038, 38031, 38165, 38179, 38144, 38207, 38081, 38082, 38134, - 38055, 38113, 38158, 38151, 38229, 38230, 38060, 38215, 38216, 38183, - 37986, 37993, 38096, 38074, 38103, 38043, 38169, 38197, 38021, 38014, - 37972, 37965, 38091, 38190, 37979, 38122, 38007, 38000, 38035, 38028, - 38162, 38176, 38141, 38204, 38080, 38133, 38054, 38110, 38155, 38148, - 38236, 38237, 38068, 38069, 38222, 38223, 38188, 37991, 37998, 38101, - 38078, 38108, 38049, 38174, 38202, 38026, 38019, 37977, 37970, 38094, - 38195, 37984, 38127, 38012, 38005, 38040, 38033, 38167, 38181, 38146, - 38209, 38086, 38136, 38057, 38115, 38160, 38153, 38228, 38231, 38139, - 38058, 38059, 38214, 38217, 38182, 37985, 37992, 38095, 38073, 38102, - 38041, 38042, 38168, 38196, 38020, 38013, 37971, 37964, 38090, 38189, - 37978, 38116, 38006, 37999, 38034, 38027, 38161, 38175, 38140, 38203, - 38079, 38132, 38053, 38109, 38154, 38147, 38224, 38227, 38138, 38061, - 38063, 38210, 38213, 38184, 37987, 37994, 38097, 38070, 38072, 38104, - 38044, 38046, 38170, 38198, 38022, 38015, 37973, 37966, 38087, 38191, - 37980, 38123, 38008, 38001, 38036, 38029, 38163, 38177, 38142, 38205, - 38083, 38129, 38131, 38050, 38052, 38111, 38156, 38149, 38128, 38088, - 37961, 37962, 37963, 38119, 38120, 38117, 38241, 38244, 38247, 38246, - 38250, 38242, 38249, 38240, 38238, 38245, 38248, 38239, 38243, 38257, - 38259, 38256, 38255, 38252, 38251, 38254, 38253, 38260, 38258, 38121, - 38118, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 10283, 10484, 10172, 10331, 10122, 10425, 10227, 10386, 10168, - 10327, 10248, 10411, 10156, 10315, 10115, 10412, 10276, 10477, 10224, - 10383, 10124, 10427, 10225, 10384, 10164, 10323, 10157, 10316, 10221, - 10380, 10278, 10479, 10123, 10426, 10267, 10445, 10264, 10442, 10265, - 10443, 10247, 10410, 10154, 10313, 10169, 10328, 10298, 8145, 8137, 8088, - 8138, 33776, 8112, 8101, 8115, 8111, 8120, 8113, 8110, 8107, 8143, 8132, - 10297, 10301, 10178, 10337, 10176, 10335, 10288, 10489, 10166, 10325, - 10174, 10333, 10128, 10453, 10136, 10462, 10132, 10457, 10131, 10456, - 10134, 10460, 10212, 10371, 10262, 10440, 10170, 10329, 10165, 10324, - 27730, 27767, 8096, 8104, 3413, 2779, 3416, 2781, 3412, 3388, 3405, 3415, - 2808, 3414, 2784, 3385, 3391, 3390, 2782, 2788, 3404, 2807, 2801, 2787, - 3400, 2795, 3395, 3401, 3394, 3398, 2777, 2773, 2805, 2804, 2799, 3407, - 3397, 3408, 3409, 2806, 2771, 3381, 2800, 2803, 3384, 3410, 3382, 2768, - 3393, 2786, 2793, 2810, 3387, 3392, 2772, 2796, 2797, 2798, 3396, 3383, - 2770, 2769, 3411, 2809, 2785, 3386, 2783, 2774, 2790, 3389, 2789, 2792, - 3406, 2780, 2794, 2791, 3403, 2778, 3402, 2802, 3399, 2767, 2775, 2776, - 2764, 2763, 3417, 3419, 2766, 2765, 3418, 3420, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 27727, 27723, 27726, 27722, 27728, - 27724, 27729, 27725, 27782, 27797, 27825, 27804, 27787, 27781, 27796, - 27824, 27803, 27786, 27783, 27798, 27826, 27808, 27788, 27774, 27773, - 27775, 27819, 27838, 27835, 27837, 27836, 27816, 27986, 27987, 22867, - 23470, 22868, 23471, 22907, 23532, 23133, 23894, 23132, 23851, 22809, - 23392, 22810, 23393, 23274, 23282, 22783, 23348, 22784, 23349, 22785, - 23350, 22781, 23346, 22782, 23347, 22786, 23351, 23077, 23775, 22948, - 23584, 22945, 23581, 22949, 23585, 22795, 23369, 22967, 23608, 23000, - 23683, 23001, 23685, 23047, 23723, 23052, 23728, 23050, 23726, 23053, - 23729, 23059, 23740, 23058, 23737, 23074, 23765, 23079, 23778, 23174, - 23944, 23183, 23956, 23178, 23951, 23127, 23845, 23128, 23846, 23182, - 23955, 22869, 23488, 22936, 23571, 22811, 23394, 27995, 23430, 23629, - 23643, 23666, 23777, 23259, 23889, 23941, 22929, 23560, 22930, 23561, - 22931, 23117, 23857, 23122, 23887, 22932, 23563, 22933, 23564, 22934, - 23565, 27802, 27771, 27848, 23100, 23801, 23120, 23613, 23290, 22993, - 23657, 22805, 23382, 23379, 23526, 22789, 23354, 22876, 23495, 23179, - 23952, 23180, 23953, 23181, 23954, 22884, 23506, 22952, 23589, 22996, - 23662, 23072, 23762, 23096, 23799, 22903, 23076, 23103, 22955, 23099, - 23281, 23121, 23124, 22939, 22812, 22796, 23371, 23045, 23721, 23170, - 23932, 22889, 23511, 22890, 23512, 22891, 23513, 23042, 23712, 22778, - 23343, 22803, 23097, 23220, 22816, 23396, 23093, 23793, 23080, 23092, - 23792, 40951, 40951, 22808, 23388, 40951, 23423, 40951, 23420, 22982, - 23642, 23102, 23819, 22971, 23618, 22972, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 27703, 27706, 27715, - 23075, 23766, 23230, 27698, 27927, 23264, 23228, 23229, 23226, 23227, - 23225, 34770, 34772, 34782, 34774, 34771, 34773, 34781, 34762, 34761, - 34758, 34757, 34780, 34756, 34755, 34760, 34759, 34750, 34749, 34744, - 34743, 34752, 34751, 34746, 34745, 34768, 34764, 34763, 34754, 34753, - 34767, 34748, 34766, 34747, 34769, 34765, 34784, 34786, 34787, 34785, - 34783, 34775, 34776, 34777, 34778, 34779, 40951, 40951, 40951, 29388, - 29387, 29390, 29385, 29386, 29389, 29382, 29381, 29384, 29383, 40951, - 40951, 40951, 40951, 40951, 40951, 31352, 31351, 31340, 31341, 31328, - 31330, 31362, 31343, 31350, 31349, 31333, 31344, 31354, 31353, 31359, - 31364, 31346, 31345, 31332, 31367, 31355, 31356, 31334, 31369, 31366, - 31363, 31335, 31336, 31361, 31325, 31370, 31372, 31357, 31371, 31365, - 31368, 31360, 31339, 31358, 31377, 31378, 31348, 31347, 31331, 31342, - 31326, 31338, 31337, 31327, 31376, 31379, 31329, 31375, 31380, 31374, - 31373, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 32496, - 32498, 32445, 32446, 32466, 32467, 32462, 32463, 32457, 32458, 32459, - 32460, 32489, 32490, 32447, 32464, 32465, 32448, 32486, 32485, 32482, - 32481, 32470, 32480, 32479, 32484, 32483, 32472, 32454, 32453, 32450, - 32449, 32471, 32456, 32455, 32452, 32451, 32473, 32488, 32487, 32478, - 32477, 32492, 32494, 32493, 32469, 32461, 32474, 32475, 32476, 32491, - 32468, 32511, 32512, 32523, 32524, 32515, 32516, 32517, 32518, 32519, - 32520, 32525, 32526, 32513, 32521, 32522, 32514, 32497, 32495, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 32500, 32499, 32507, - 32509, 32506, 32505, 32502, 32501, 32504, 32503, 32510, 32508, 40951, - 40951, 40951, 40951, 40951, 40951, 8163, 8165, 8162, 8161, 8158, 8157, - 8160, 8159, 8166, 8164, 8154, 8155, 8150, 8151, 8152, 8153, 8149, 8156, - 10770, 10764, 10777, 10766, 10765, 10763, 10778, 10667, 10665, 10670, - 10771, 10821, 10676, 10788, 21585, 21587, 21584, 21583, 21580, 21579, - 21582, 21581, 21588, 21586, 21549, 21548, 21559, 21545, 21553, 21552, - 21566, 21546, 21555, 21543, 21547, 21551, 21550, 21561, 21558, 21556, - 21562, 21565, 21560, 21564, 21554, 21544, 21563, 21557, 21567, 21541, - 21568, 21542, 21577, 21574, 21576, 21575, 21578, 21573, 21571, 21572, - 21569, 21570, 31838, 31835, 31827, 31843, 31834, 31829, 31840, 31832, - 31831, 31833, 31837, 31825, 31842, 31841, 31839, 31845, 31844, 31836, - 31830, 31826, 31828, 31824, 31846, 31853, 31855, 31848, 31851, 31854, - 31852, 31850, 31849, 31821, 31820, 31823, 31822, 31856, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 31847, - 19228, 19231, 19232, 19229, 19186, 19187, 19195, 19189, 19191, 19194, - 19188, 19184, 19190, 19192, 19185, 19151, 19153, 19154, 19167, 19174, - 19181, 19216, 19133, 19140, 19214, 19218, 19164, 19237, 19220, 40951, - 40951, 40951, 20903, 20901, 20905, 20904, 20875, 20843, 20842, 20844, - 20884, 20863, 20849, 20850, 20882, 20876, 20883, 20845, 20846, 20847, - 20859, 20860, 20848, 20857, 20858, 20873, 20852, 20874, 20851, 20871, - 20872, 20838, 20839, 20854, 20869, 20870, 20840, 20841, 20853, 20861, - 20862, 20855, 20856, 20879, 20881, 20864, 20865, 20878, 20880, 20867, - 20868, 20866, 20877, 20902, 20908, 20910, 20911, 20912, 20906, 20907, - 20909, 20914, 20913, 20834, 20835, 20836, 20899, 20837, 20885, 20888, - 20897, 20890, 20895, 20893, 20891, 20889, 20886, 20887, 20892, 20900, - 40951, 20898, 20921, 20923, 20920, 20919, 20916, 20915, 20918, 20917, - 20924, 20922, 40951, 40951, 40951, 40951, 20894, 20896, 28607, 28605, - 28600, 28597, 28603, 28693, 28671, 28623, 28635, 28631, 28630, 28633, - 28632, 28625, 28624, 28622, 28735, 28737, 28734, 28733, 28730, 28729, - 28732, 28731, 28738, 28736, 28634, 28627, 28626, 28629, 28628, 40951, - 6245, 6263, 6265, 6262, 6246, 6264, 6254, 6253, 6250, 6249, 6225, 6226, - 6248, 6247, 6252, 6251, 6227, 6229, 6228, 6256, 6255, 6242, 6241, 6230, - 6231, 6240, 6236, 6235, 6234, 6239, 6238, 6232, 6233, 6237, 6261, 6259, - 6258, 6260, 6243, 6244, 6257, 6270, 6273, 6274, 6279, 6277, 6276, 6275, - 6271, 6272, 6278, 6213, 6211, 6210, 6212, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 6219, 6218, 6215, 6207, 6217, 6223, - 6214, 6221, 6224, 6222, 6220, 6216, 6209, 6208, 40951, 40951, 6286, 6288, - 6285, 6284, 6281, 6280, 6283, 6282, 6289, 6287, 40951, 40951, 6266, 6268, - 6267, 6269, 28577, 28570, 28569, 28574, 28573, 28567, 28566, 28565, - 28563, 28562, 28564, 28568, 28579, 28572, 28571, 28576, 28670, 28580, - 28581, 28578, 28667, 28668, 28669, 28708, 28710, 28709, 28552, 28704, - 28695, 28696, 28616, 28617, 35254, 35230, 35253, 35229, 35252, 35228, - 35267, 35243, 35261, 35237, 35256, 35232, 35255, 35231, 35272, 35248, - 35262, 35238, 35265, 35241, 35260, 35236, 35259, 35235, 35263, 35239, - 35264, 35240, 35258, 35234, 35257, 35233, 35266, 35242, 35270, 35246, - 35274, 35250, 35271, 35247, 35269, 35245, 35273, 35249, 35268, 35244, - 35275, 35251, 35276, 35288, 35296, 35293, 35292, 35298, 35299, 35277, - 35297, 35294, 35295, 35287, 35291, 35290, 35289, 35286, 35283, 35284, - 35285, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 35279, 35280, 35282, 35281, 35278, - 27037, 27038, 26996, 27013, 27024, 27023, 27000, 26999, 27012, 27018, - 27019, 27051, 27053, 27043, 27045, 27044, 26991, 26988, 26990, 27040, - 27041, 27055, 27056, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 17212, 17210, 17209, 17208, 17207, 17211, 40951, - 40951, 16906, 16904, 16903, 16902, 16901, 16905, 40951, 40951, 16921, - 16919, 16918, 16917, 16916, 16920, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 16862, 16868, 16866, 16863, 16865, 16864, - 16867, 40951, 16847, 16853, 16851, 16848, 16850, 16849, 16852, 40951, - 23362, 23338, 23368, 23363, 23459, 23622, 23811, 23611, 23602, 23605, - 23633, 23647, 23473, 23366, 23367, 23717, 23567, 23860, 23859, 23861, - 23862, 23821, 23258, 23764, 23421, 23744, 23422, 23808, 23809, 23365, - 23931, 23897, 23940, 23883, 23936, 23385, 23386, 23387, 23972, 23969, - 23970, 23971, 23983, 27685, 27908, 27917, 27918, 27975, 23802, 23570, - 23718, 23942, 23566, 18401, 23428, 23892, 23865, 27972, 27821, 27845, - 40951, 40951, 40951, 40951, 6468, 6469, 6470, 6471, 6472, 6473, 6431, - 6467, 6432, 6433, 6434, 6435, 6436, 6396, 6397, 6398, 6399, 6400, 6401, - 6437, 6438, 6439, 6440, 6441, 6442, 6443, 6444, 6445, 6446, 6447, 6402, - 6395, 6403, 6404, 6405, 6406, 6407, 6408, 6449, 6450, 6451, 6452, 6453, - 6454, 6410, 6409, 6411, 6412, 6413, 6414, 6415, 6389, 6428, 6390, 6429, - 6391, 6430, 6392, 6393, 6394, 6388, 6416, 6417, 6418, 6419, 6420, 6421, - 6422, 6423, 6424, 6425, 6426, 6427, 6455, 6456, 6457, 6458, 6459, 6460, - 6461, 27005, 27017, 27027, 27029, 27014, 27008, 26995, 27020, 27007, - 27010, 27022, 27033, 27035, 27031, 27036, 27025, 27016, 27034, 27002, - 27003, 27032, 26994, 27004, 26998, 27001, 26997, 26993, 27006, 27028, - 27030, 27015, 27009, 27021, 27011, 27026, 27047, 27050, 27042, 27049, - 27048, 27052, 27046, 27054, 26992, 27039, 26989, 40951, 40951, 27063, - 27065, 27062, 27061, 27058, 27057, 27060, 27059, 27066, 27064, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 19425, 19423, 19456, 19457, 19458, 19434, 19435, 19467, 19469, - 19402, 19400, 19399, 19403, 19409, 19410, 19412, 19411, 19416, 19413, - 19414, 19418, 19386, 19384, 40951, 40951, 40951, 40951, 19282, 19283, - 19351, 19352, 19371, 19365, 19366, 19369, 19368, 19367, 19326, 19311, - 19350, 19317, 19321, 19320, 19334, 19333, 19254, 19279, 19272, 19357, - 19270, 19277, 19307, 19300, 19306, 19361, 19302, 19305, 19304, 19345, - 19338, 19355, 19356, 19344, 19342, 19341, 19346, 19348, 19293, 19292, - 19378, 19379, 19243, 19242, 19358, 19297, 19295, 40951, 40951, 40951, - 40951, 7582, 7583, 7584, 7585, 7586, 7587, 7588, 7589, 7590, 7591, 7592, - 7593, 7594, 7595, 7596, 7597, 7598, 7599, 7600, 7601, 7602, 7603, 7604, - 7605, 7606, 7607, 7608, 7609, 7610, 7611, 7612, 7613, 7614, 7615, 7616, - 7617, 7618, 7619, 7620, 7621, 7622, 7623, 7624, 7625, 7626, 7627, 7628, - 7629, 7630, 7631, 7632, 7633, 7634, 7635, 7636, 7637, 7638, 7639, 7640, - 7641, 7642, 7643, 7644, 7645, 7646, 7647, 7648, 7649, 7650, 7651, 7652, - 7653, 7654, 7655, 7656, 7657, 7658, 7659, 7660, 7661, 7662, 7663, 7664, - 7665, 7666, 7667, 7668, 7669, 7670, 7671, 7672, 7673, 7674, 7675, 7676, - 7677, 7678, 7679, 7680, 7681, 7682, 7683, 7684, 7685, 7686, 7687, 7688, - 7689, 7690, 7691, 7692, 7693, 7694, 7695, 7696, 7697, 7698, 7699, 7700, - 7701, 7702, 7703, 7704, 7705, 7706, 7707, 7708, 7709, 7710, 7711, 7712, - 7713, 7714, 7715, 7716, 7717, 7718, 7719, 7720, 7721, 7722, 7723, 7724, - 7725, 7726, 7727, 7728, 7729, 7730, 7731, 7732, 7733, 7734, 7735, 7736, - 7737, 7738, 7739, 7740, 7741, 7742, 7743, 7744, 7745, 7746, 7747, 7748, - 7749, 7750, 7751, 7752, 7753, 7754, 7755, 7756, 7757, 7758, 7759, 7760, - 7761, 7762, 7763, 7764, 7765, 7766, 7767, 7768, 7769, 7770, 7771, 7772, - 7773, 7774, 7775, 7776, 7777, 7778, 7779, 7780, 7781, 7782, 7783, 7784, - 7785, 7786, 7787, 7788, 7789, 7790, 7791, 7792, 7793, 7794, 7795, 7796, - 7797, 7798, 7799, 7800, 7801, 7802, 7803, 7804, 7805, 7806, 7807, 7808, - 7809, 7810, 7811, 7812, 7813, 7814, 7815, 7816, 7817, 7818, 7819, 7820, - 7821, 7822, 7823, 7824, 7825, 7826, 7827, 7828, 7829, 7830, 7831, 7832, - 7833, 7834, 7835, 7836, 7837, 7380, 7381, 7382, 7383, 7384, 7385, 7386, - 7387, 7388, 7389, 7390, 7391, 7392, 7393, 7394, 7395, 7396, 7397, 7398, - 7399, 7400, 7401, 7402, 7403, 7404, 7405, 7406, 7407, 7408, 7409, 7410, - 7411, 7412, 7413, 7414, 7415, 7416, 7417, 7418, 7419, 7420, 7421, 7422, - 7423, 7424, 7425, 7426, 7427, 7428, 7429, 7430, 7431, 7432, 7433, 7434, - 7435, 7436, 7437, 7438, 7439, 7440, 7441, 7442, 7443, 7444, 7445, 7446, - 7447, 7448, 7449, 7450, 7451, 7452, 7453, 7454, 7455, 7456, 7457, 7458, - 7459, 7460, 7461, 7462, 7463, 7464, 7465, 7466, 7467, 7468, 7469, 7470, - 7471, 7472, 7473, 7474, 7475, 7366, 7367, 7368, 7369, 7370, 7371, 7372, - 7373, 7374, 7375, 7376, 7377, 7378, 7379, 40951, 40951, 7476, 7477, 7478, - 7479, 7480, 7481, 7482, 7483, 7484, 7485, 7486, 7487, 7488, 7489, 7490, - 7491, 7492, 7493, 7494, 7495, 7496, 7497, 7498, 7499, 7500, 7501, 7502, - 7503, 7504, 7505, 7506, 7507, 7508, 7509, 7510, 7511, 7512, 7513, 7514, - 7515, 7516, 7517, 7518, 7519, 7520, 7521, 7522, 7523, 7524, 7525, 7526, - 7527, 7528, 7529, 7530, 7531, 7532, 7533, 7534, 7535, 7536, 7537, 7538, - 7539, 7540, 7541, 7542, 7543, 7544, 7545, 7546, 7547, 7548, 7549, 7550, - 7551, 7552, 7553, 7554, 7555, 7556, 7557, 7558, 7559, 7560, 7561, 7562, - 7563, 7564, 7565, 7566, 7567, 7568, 7569, 7570, 7571, 7572, 7573, 7574, - 7575, 7576, 7577, 7578, 7579, 7580, 7581, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 24005, 24008, 24009, 24006, 24007, 24011, - 24012, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 2506, 2507, 2505, 2508, 2504, 40951, 40951, 40951, - 40951, 40951, 19891, 19918, 19896, 19827, 19883, 19886, 19888, 19887, - 19882, 19889, 19885, 19884, 19828, 19861, 19862, 19859, 19860, 19825, - 19826, 19824, 19832, 19874, 19872, 19849, 19881, 19854, 40951, 19866, - 19892, 19840, 19835, 19876, 40951, 19878, 40951, 19852, 19856, 40951, - 19842, 19838, 40951, 19870, 19847, 19864, 19858, 19868, 19880, 19831, - 19834, 19837, 19893, 1158, 1157, 1188, 1186, 1187, 1189, 1417, 1415, - 1416, 1418, 1183, 1181, 1182, 1184, 1547, 1545, 1546, 1548, 1527, 1525, - 1526, 1528, 1542, 1540, 1541, 1543, 1560, 1558, 1559, 1561, 1422, 1420, - 1421, 1423, 1224, 1222, 1223, 1225, 1395, 1393, 1394, 1396, 1504, 1502, - 1503, 1505, 1509, 1507, 1508, 1510, 1214, 1213, 1205, 1204, 1228, 1227, - 1217, 1216, 1324, 1323, 1452, 1451, 1347, 1345, 1346, 1348, 1258, 1256, - 1257, 1259, 1270, 1268, 1269, 1271, 1386, 1384, 1385, 1387, 1400, 1399, - 1456, 1454, 1455, 1457, 1300, 1299, 1295, 1293, 1294, 1296, 1304, 1302, - 1303, 1305, 1584, 1583, 1582, 1581, 2368, 2367, 2376, 2375, 2372, 2371, - 2370, 2369, 2380, 2379, 2366, 2374, 2373, 2382, 2378, 2377, 2381, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 1390, 1388, 1389, 1391, 1553, 1552, - 1605, 1604, 1602, 1601, 1551, 1563, 1562, 1351, 1350, 1354, 1353, 1618, - 1616, 1617, 1619, 1554, 1555, 2058, 2057, 2060, 2059, 2079, 2078, 2087, - 2086, 2077, 2076, 2083, 2082, 2063, 2061, 2062, 2112, 2110, 2111, 1238, - 1236, 1237, 1239, 2069, 2067, 2073, 2056, 2081, 1660, 1651, 1656, 1663, - 1658, 1669, 2021, 2009, 2016, 2029, 2032, 2037, 2039, 2044, 2041, 2050, - 1738, 1744, 1723, 1718, 1777, 1773, 1779, 1928, 1921, 1933, 1941, 1900, - 1904, 1682, 1674, 1678, 1684, 2002, 1997, 2114, 1631, 1627, 1712, 1708, - 1696, 1701, 1692, 1699, 1694, 1703, 1882, 1878, 1880, 1884, 1753, 1755, - 1763, 1761, 1758, 1769, 1751, 1772, 1805, 1797, 1809, 1815, 1789, 1818, - 1836, 1825, 1830, 1840, 1819, 1841, 1857, 1847, 1869, 1862, 1865, 1872, - 1733, 1729, 1730, 1731, 2097, 2090, 2099, 2105, 2054, 2109, 2038, 1895, - 1643, 1949, 1952, 1956, 1950, 1953, 1955, 2085, 2084, 2071, 2075, 2055, - 2080, 1667, 1666, 1661, 1665, 1657, 1668, 2035, 2034, 2027, 2033, 2031, - 2036, 2048, 2047, 2042, 2046, 2040, 2049, 1693, 1702, 1879, 1883, 1752, - 1756, 1767, 1750, 1771, 1813, 1788, 1817, 1820, 1838, 1870, 1867, 1860, - 1866, 1864, 1871, 1642, 2107, 2094, 2103, 2093, 2053, 2108, 2068, 2066, - 2070, 2072, 2064, 1659, 1650, 1655, 1662, 1652, 2020, 2008, 2015, 2028, - 2010, 2043, 1737, 1743, 1722, 1717, 1776, 1778, 1927, 1920, 1932, 1940, - 1899, 1907, 1903, 1681, 1673, 1677, 1683, 2001, 2113, 1630, 1626, 1711, - 1707, 1695, 1700, 1691, 1698, 1881, 1877, 1754, 1762, 1760, 1757, 1768, - 1804, 1796, 1808, 1814, 1798, 1835, 1824, 1829, 1839, 1856, 1846, 1868, - 1861, 1848, 1732, 1728, 1734, 2096, 2089, 2098, 2104, 2091, 2074, 2065, - 1664, 1653, 2030, 2011, 2045, 2051, 1942, 1924, 1979, 1959, 1759, 1770, - 1816, 1863, 1849, 2106, 2092, 1957, 1951, 1954, 2000, 2004, 1633, 1635, - 1710, 1714, 1944, 1948, 1981, 1989, 1720, 1725, 1746, 1748, 1775, 1781, - 1906, 1911, 1680, 1688, 1970, 1965, 1984, 1978, 1987, 1946, 1909, 1686, - 1999, 2003, 1632, 1634, 1709, 1713, 1943, 1947, 1980, 1988, 1719, 1724, - 1745, 1747, 1774, 1780, 1905, 1910, 1679, 1687, 1968, 1963, 1982, 1976, - 1986, 1945, 1908, 1685, 1969, 1964, 1983, 1977, 1923, 1958, 1996, 1929, - 1922, 1934, 1971, 1966, 1985, 1998, 2115, 1644, 1645, 30650, 30651, 1892, - 1887, 1891, 1888, 1889, 1890, 1918, 1637, 1639, 1638, 1636, 1886, 1917, - 1640, 1991, 1893, 2018, 2007, 2006, 2005, 2013, 2023, 2025, 2024, 1740, - 1739, 1716, 1715, 1919, 1926, 1925, 1936, 1935, 1937, 1939, 1938, 1897, - 1896, 1902, 1961, 1960, 1967, 1973, 1972, 1975, 1974, 1671, 1676, 1675, - 1993, 1992, 1994, 1995, 1629, 1624, 1623, 1622, 1704, 1706, 1705, 1690, - 1689, 1875, 1873, 1794, 1795, 1792, 1799, 1800, 1807, 1806, 1811, 1810, - 1821, 1822, 1823, 1833, 1831, 1826, 1827, 40951, 40951, 1832, 1726, 1727, - 1844, 1843, 1854, 1853, 1852, 1859, 1858, 2101, 2100, 1654, 2019, 2017, - 2014, 2012, 2026, 2022, 1742, 1735, 1741, 1930, 1898, 1962, 1672, 1803, - 1812, 2088, 2095, 2102, 1837, 1876, 1845, 1874, 1793, 1625, 1766, 1850, - 1828, 1801, 1765, 1802, 1851, 1736, 1721, 1834, 1697, 1649, 1764, 1628, - 1901, 1931, 1855, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 1913, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 1914, 1885, 1646, 1647, 1842, 1912, 1894, 1641, 2052, 1915, - 1916, 1749, 32167, 1670, 1990, 1648, 38261, 38372, 38440, 38451, 38462, - 38473, 38484, 38495, 38506, 38262, 38273, 38284, 38295, 38306, 38317, - 38328, 31622, 31627, 31628, 31621, 31652, 31623, 31654, 31630, 31644, - 31626, 40951, 40951, 40951, 40951, 40951, 40951, 8360, 8362, 8192, 8193, - 8371, 8373, 8086, 8361, 8363, 8429, 8430, 8372, 8374, 8087, 8140, 8141, - 31653, 31624, 31625, 31639, 31651, 31636, 31648, 31634, 31646, 31638, - 31650, 31631, 31640, 31632, 31641, 31635, 31647, 31633, 31645, 31629, - 31642, 32661, 39263, 31637, 31649, 10541, 6129, 39141, 11256, 10540, - 6128, 39139, 33800, 33809, 33844, 40951, 33834, 33801, 33845, 33807, - 33808, 33811, 33815, 33810, 33814, 33812, 33816, 33843, 33791, 33793, - 33842, 33840, 33813, 33839, 33806, 40951, 33833, 33802, 33841, 33799, - 40951, 40951, 40951, 40951, 1092, 2387, 1074, 2389, 1104, 40951, 1089, - 1090, 1070, 1071, 1101, 1102, 2294, 2295, 2362, 2363, 1289, 1153, 1152, - 1143, 1142, 1571, 1570, 1146, 1145, 1588, 1586, 1587, 1589, 1163, 1162, - 1178, 1176, 1177, 1179, 1514, 1513, 1523, 1521, 1522, 1516, 1535, 1533, - 1534, 1536, 1320, 1318, 1319, 1321, 1286, 1284, 1285, 1287, 1358, 1356, - 1357, 1359, 1202, 1201, 1531, 1530, 1449, 1448, 1613, 1612, 1478, 1476, - 1477, 1479, 1484, 1482, 1483, 1485, 1465, 1463, 1464, 1466, 1210, 1208, - 1209, 1211, 1497, 1495, 1496, 1498, 1609, 1607, 1608, 1610, 1121, 1119, - 1120, 1122, 1265, 1263, 1264, 1266, 1249, 1247, 1248, 1250, 1431, 1429, - 1430, 1432, 1334, 1332, 1333, 1335, 1370, 1368, 1369, 1371, 1379, 1377, - 1378, 1380, 1410, 1408, 1409, 1411, 1308, 1306, 1307, 1309, 1575, 1574, - 1161, 1160, 1598, 1596, 1597, 1599, 1787, 1786, 1783, 1782, 1785, 1784, - 1791, 1790, 40951, 40951, 40755, 40951, 17625, 17629, 17597, 17613, - 17599, 17611, 17610, 17540, 17603, 17612, 17600, 17535, 17628, 17633, - 17607, 17620, 17622, 17619, 17618, 17615, 17614, 17617, 17616, 17623, - 17621, 17536, 17606, 17542, 17624, 17627, 17630, 17534, 17543, 17544, - 17545, 17546, 17547, 17548, 17549, 17550, 17551, 17552, 17553, 17554, - 17555, 17556, 17557, 17558, 17559, 17560, 17561, 17562, 17563, 17564, - 17565, 17566, 17567, 17568, 17541, 17605, 17604, 17533, 17595, 17626, - 17569, 17570, 17571, 17572, 17573, 17574, 17575, 17576, 17577, 17578, - 17579, 17580, 17581, 17582, 17583, 17584, 17585, 17586, 17587, 17588, - 17589, 17590, 17591, 17592, 17593, 17594, 17539, 17635, 17602, 17632, - 17538, 17601, 19098, 19091, 19093, 19097, 19089, 19081, 19036, 19038, - 19040, 19037, 19039, 19032, 19034, 19033, 19035, 19090, 19082, 19084, - 19086, 19083, 19085, 19057, 19059, 19061, 19058, 19060, 19041, 19043, - 19045, 19042, 19044, 19072, 19074, 19076, 19073, 19075, 19047, 19049, - 19051, 19048, 19050, 19052, 19054, 19056, 19053, 19055, 19062, 19064, - 19066, 19063, 19065, 19077, 19079, 19078, 19067, 19069, 19071, 19068, - 19070, 19080, 19046, 19088, 19087, 19031, 18981, 18998, 18982, 18983, - 18984, 18985, 19019, 19000, 18989, 18994, 18993, 18992, 18996, 18990, - 18991, 18995, 19017, 18987, 18999, 18988, 19002, 19001, 19016, 19011, - 18997, 19010, 18980, 19018, 18986, 19025, 40951, 40951, 40951, 19026, - 19027, 19005, 19006, 19013, 19012, 40951, 40951, 19004, 19003, 19028, - 19022, 19023, 19029, 40951, 40951, 19008, 19030, 19021, 19020, 19024, - 19009, 40951, 40951, 19014, 19007, 19015, 40951, 40951, 40951, 17537, - 17598, 17596, 17609, 17634, 17608, 17631, 40951, 19095, 19092, 19096, - 19094, 19101, 19099, 19100, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 20788, 20790, 20789, 29933, 31859, 40951, - 40951, 24968, 24994, 24987, 25020, 24978, 24969, 24998, 24964, 24976, - 25006, 25009, 25003, 40951, 24992, 25019, 25023, 25002, 25017, 25024, - 25031, 25034, 24979, 25030, 24977, 24980, 24963, 24986, 24989, 25012, - 25013, 24971, 25028, 24993, 24974, 25010, 24972, 25029, 24988, 24996, - 40951, 25021, 24985, 25005, 24970, 24981, 24995, 24966, 25000, 24975, - 25007, 25008, 24965, 24991, 24967, 25018, 25011, 25025, 24999, 25001, - 40951, 24973, 25027, 40951, 24984, 24983, 24997, 25033, 25026, 25036, - 25004, 24982, 25014, 25022, 24990, 25015, 25016, 25032, 25035, 40951, - 40951, 25046, 25047, 25049, 25048, 25037, 25038, 25045, 25039, 25040, - 25050, 25041, 25042, 25043, 25044, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 24852, - 24851, 24853, 24840, 24841, 24842, 24843, 24844, 24845, 24846, 24848, - 24847, 24850, 24849, 24858, 24855, 24856, 24854, 24857, 24957, 24958, - 24860, 24859, 24861, 24960, 24959, 24863, 24864, 24865, 24862, 24866, - 24869, 24868, 24870, 24871, 24872, 24961, 24873, 24874, 24867, 24877, - 24878, 24876, 24875, 24879, 24880, 24881, 24882, 24883, 24884, 24887, - 24888, 24889, 24886, 24890, 24885, 24891, 24892, 24893, 24894, 24895, - 24896, 24897, 24898, 24899, 24900, 24902, 24901, 24903, 24904, 24906, - 24907, 24908, 24905, 24909, 24910, 24911, 24912, 24914, 24913, 24915, - 24916, 24962, 24917, 24918, 24920, 24921, 24922, 24919, 24923, 24924, - 24925, 24926, 24927, 24955, 24935, 24936, 24937, 24938, 24939, 24940, - 24941, 24942, 24943, 24944, 24945, 24946, 24947, 24948, 24949, 24950, - 24951, 24952, 24953, 24954, 24928, 24929, 24930, 24931, 24932, 24933, - 24934, 24956, 40951, 40951, 40951, 40951, 40951, 112, 113, 159, 40951, - 40951, 40951, 40951, 156, 151, 146, 126, 121, 139, 134, 114, 129, 154, - 149, 144, 124, 119, 140, 135, 115, 130, 157, 152, 147, 127, 122, 142, - 137, 117, 132, 158, 153, 148, 128, 123, 143, 138, 118, 133, 155, 150, - 145, 125, 120, 141, 136, 116, 131, 40951, 40951, 40951, 111, 107, 109, - 110, 108, 103, 104, 105, 106, 18166, 18163, 18167, 18153, 18148, 18154, - 18157, 18149, 18159, 18168, 18151, 18161, 18155, 18164, 18158, 18160, - 18170, 18152, 18162, 18156, 18165, 18169, 18150, 18171, 18183, 18192, - 18182, 18178, 18191, 18173, 18179, 18195, 18199, 18200, 18181, 18184, - 18190, 18189, 18197, 18198, 18180, 18187, 18193, 18188, 18177, 18196, - 18185, 18172, 18174, 18194, 18186, 18175, 18176, 18418, 18419, 18617, - 18613, 18654, 18619, 18349, 18423, 18616, 18612, 18355, 18354, 18415, - 18397, 18413, 18422, 18417, 18203, 18202, 18656, 18615, 18657, 18420, - 18611, 18393, 29360, 40951, 32211, 32214, 32209, 32212, 32183, 32213, - 32181, 32184, 32210, 32182, 32215, 32180, 2526, 40951, 40951, 40951, - 18610, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 31405, 31406, - 31417, 31386, 31389, 31420, 31398, 31397, 31418, 31425, 31384, 31411, - 31390, 31403, 31404, 31413, 31402, 31383, 31387, 31394, 31392, 31414, - 31391, 31382, 31412, 31399, 31400, 31385, 31388, 31410, 31424, 31395, - 31419, 31381, 31407, 31426, 31408, 31409, 31401, 31423, 31422, 31396, - 31415, 31416, 31421, 31393, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 25283, 25285, 25278, 25279, 25290, 25289, - 25292, 25300, 25302, 25281, 25293, 25276, 25296, 25294, 25274, 25287, - 25275, 25288, 25299, 25295, 25277, 25297, 25298, 25280, 25282, 25284, - 25286, 25291, 25301, 40951, 40951, 40951, 6039, 6050, 6041, 6012, 6035, - 6051, 6013, 6040, 6057, 6055, 6015, 6056, 6042, 6030, 6025, 6026, 6024, - 6010, 6033, 6023, 6058, 6020, 6032, 6049, 6029, 6053, 6043, 6038, 6047, - 6048, 6021, 6034, 6045, 6046, 6027, 6028, 6022, 6054, 6011, 6031, 6036, - 6052, 6016, 6017, 6018, 6019, 6014, 6044, 6037, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 8571, 8569, 8567, 8566, 8563, 8562, 8565, 8564, 8570, 8568, - 8561, 8560, 8558, 8549, 8547, 8556, 8554, 8545, 8551, 8552, 8559, 8557, - 8548, 8546, 8555, 8553, 8544, 8550, 40951, 40951, 40951, 40951, 30215, - 30209, 30195, 30210, 30182, 30212, 30214, 30211, 30206, 30202, 30194, - 30190, 30191, 30192, 30184, 30216, 30205, 30198, 30196, 30186, 30183, - 30207, 30200, 30188, 30204, 30193, 30189, 30187, 30208, 30203, 30201, - 30185, 30220, 30218, 30219, 30217, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 30213, 30199, 30197, 18013, 18029, 18037, - 18031, 18012, 18022, 18016, 18015, 18024, 18034, 18038, 18035, 18032, - 18020, 18036, 18027, 18021, 18019, 18023, 18033, 18025, 18026, 18028, - 18017, 18014, 18030, 18018, 40951, 40951, 40951, 40951, 40951, 30287, - 30286, 30283, 30256, 30257, 30279, 30254, 30280, 30255, 30259, 30288, - 30281, 30262, 30263, 30270, 30264, 30282, 30267, 30269, 30290, 30253, - 30266, 30265, 30277, 30274, 30284, 30285, 30258, 30289, 30268, 30271, - 30272, 30273, 30276, 30261, 30278, 30275, 30260, 8386, 8384, 8382, 8383, - 8385, 40951, 40951, 40951, 40951, 40951, 37709, 37712, 37716, 37720, - 37713, 37717, 37735, 37731, 37718, 37728, 37730, 37719, 37725, 37721, - 37736, 37714, 37733, 37732, 37724, 37710, 37734, 37723, 37711, 37722, - 37727, 37715, 37729, 37737, 37738, 37726, 40951, 37739, 30296, 30338, - 30339, 30319, 30320, 30317, 30318, 30316, 30331, 30308, 30309, 30313, - 30314, 30303, 30306, 30307, 30312, 30335, 30300, 30332, 30321, 30322, - 30325, 30326, 30327, 30336, 30310, 30311, 30323, 30324, 30334, 30330, - 30337, 30328, 30329, 30333, 40951, 40951, 40951, 40951, 30297, 30298, - 30299, 30315, 30304, 30305, 30301, 30302, 30340, 30295, 30292, 30293, - 30291, 30294, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 10594, 10593, 10589, 10590, 10591, 10592, - 10600, 10599, 10595, 10596, 10597, 10598, 10617, 10602, 10616, 10613, - 10618, 10611, 10608, 10604, 10609, 10607, 10610, 10615, 10614, 10584, - 10612, 10583, 10603, 10579, 10606, 10580, 10605, 10587, 10585, 10586, - 10581, 10582, 10601, 10588, 10634, 10633, 10629, 10630, 10631, 10632, - 10640, 10639, 10635, 10636, 10637, 10638, 10657, 10642, 10656, 10653, - 10658, 10651, 10648, 10644, 10649, 10647, 10650, 10655, 10654, 10624, - 10652, 10623, 10643, 10619, 10646, 10620, 10645, 10627, 10625, 10626, - 10621, 10622, 10641, 10628, 32797, 32803, 32814, 32811, 32801, 32800, - 32799, 32778, 32806, 32784, 32813, 32809, 32812, 32815, 32802, 32810, - 32789, 32808, 32805, 32783, 32788, 32790, 32787, 32782, 32776, 32773, - 32795, 32804, 32793, 32777, 32798, 32816, 32780, 32774, 32786, 32817, - 32794, 32791, 32792, 32775, 32771, 32796, 32772, 32781, 32770, 32779, - 32785, 32807, 30729, 30747, 30753, 30751, 30754, 30735, 30732, 30752, - 30737, 30736, 30733, 30731, 30749, 30748, 30739, 30734, 30742, 30738, - 30743, 30744, 30750, 30755, 30728, 30745, 30756, 30740, 30757, 30730, - 30746, 30741, 40951, 40951, 30764, 30766, 30763, 30762, 30759, 30758, - 30761, 30760, 30767, 30765, 40951, 40951, 40951, 40951, 40951, 40951, - 30656, 30657, 30658, 30659, 30684, 30677, 30663, 30660, 30666, 30676, - 30675, 30690, 30669, 30664, 30668, 30685, 30686, 30687, 30670, 30671, - 30688, 30665, 30681, 30680, 30674, 30662, 30673, 30661, 30672, 30678, - 30691, 30689, 30667, 30679, 30683, 30682, 40951, 40951, 40951, 40951, - 30692, 30693, 30694, 30695, 30720, 30713, 30699, 30696, 30702, 30712, - 30711, 30726, 30705, 30700, 30704, 30721, 30722, 30723, 30706, 30707, - 30724, 30701, 30717, 30716, 30710, 30698, 30709, 30697, 30708, 30714, - 30727, 30725, 30703, 30715, 30719, 30718, 40951, 40951, 40951, 40951, - 16693, 16684, 16673, 16672, 16675, 16664, 16674, 16671, 16670, 16685, - 16661, 16660, 16686, 16694, 16687, 16677, 16663, 16662, 16688, 16667, - 16666, 16665, 16695, 16689, 16690, 16669, 16668, 16679, 16678, 16681, - 16680, 16696, 16691, 16692, 16697, 16683, 16682, 16659, 16658, 16676, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 6071, 6120, 6087, - 6083, 6085, 6110, 6084, 6108, 6105, 6074, 6107, 6109, 6088, 6099, 6095, - 6090, 6118, 6082, 6073, 6091, 6094, 6096, 6121, 6112, 6070, 6076, 6077, - 6079, 6113, 6111, 6116, 6080, 6100, 6092, 6119, 6104, 6115, 6081, 6075, - 6098, 6086, 6117, 6102, 6114, 6103, 6101, 6089, 6078, 6072, 6106, 6097, - 6093, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 6122, 38912, 38881, 38882, 38892, 38891, 38894, 38893, - 38884, 38883, 38901, 38909, 40951, 38900, 38899, 38885, 38886, 38902, - 38910, 38888, 38887, 38903, 38890, 38889, 38913, 38904, 38911, 38905, - 40951, 38896, 38895, 38898, 38897, 38914, 38906, 38907, 40951, 38915, - 38908, 40951, 38947, 38916, 38917, 38927, 38926, 38929, 38928, 38919, - 38918, 38936, 38944, 40951, 38935, 38934, 38920, 38921, 38937, 38945, - 38923, 38922, 38938, 38925, 38924, 38948, 38939, 38946, 38940, 40951, - 38931, 38930, 38933, 38932, 38949, 38941, 38942, 40951, 38950, 38943, - 40951, 40951, 40951, 37427, 37428, 37441, 37401, 37430, 37429, 37432, - 37408, 37431, 37424, 37423, 37442, 37398, 37404, 37397, 37403, 37411, - 37410, 37445, 37399, 37434, 37426, 37425, 37402, 37409, 37405, 37421, - 37413, 37443, 37420, 37419, 37418, 37415, 37414, 37436, 37435, 37446, - 37444, 37438, 37407, 37437, 37406, 37447, 37400, 37440, 37439, 37396, - 37417, 37416, 37433, 37412, 37422, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 24819, 24820, 24821, - 24822, 24823, 24824, 24825, 24826, 24827, 24758, 24759, 24760, 24761, - 24762, 24771, 24763, 24764, 24765, 24766, 24767, 24768, 24769, 24770, - 24772, 24773, 24774, 24775, 24839, 24776, 24777, 24778, 24779, 24780, - 24781, 24782, 24783, 24784, 24785, 24786, 24787, 24788, 24789, 24790, - 24791, 24792, 24793, 24794, 24795, 24796, 24797, 24798, 24799, 24800, - 24801, 24802, 24803, 24804, 24805, 24806, 24807, 24808, 24809, 24810, - 24811, 24812, 24813, 24814, 24815, 24816, 24817, 24818, 24499, 24835, - 24828, 24500, 24829, 24830, 24831, 24832, 24501, 24836, 24837, 24833, - 24834, 24838, 24505, 24506, 24507, 24508, 24509, 24510, 24511, 24512, - 24502, 24503, 24504, 24516, 24517, 24518, 24513, 24514, 24515, 24519, - 24520, 24521, 24522, 24523, 24524, 24527, 24528, 24529, 24530, 24531, - 24532, 24533, 24534, 24535, 24536, 24537, 24538, 24539, 24540, 24541, - 24542, 24543, 24544, 24545, 24546, 24547, 24548, 24549, 24550, 24551, - 24552, 24553, 24554, 24555, 24556, 24557, 24558, 24559, 24560, 24561, - 24562, 24563, 24564, 24565, 24566, 24567, 24568, 24569, 24570, 24571, - 24572, 24573, 24574, 24575, 24576, 24525, 24526, 24577, 24578, 24579, - 24580, 24581, 24582, 24583, 24584, 24585, 24586, 24587, 24588, 24589, - 24590, 24591, 24592, 24593, 24594, 24595, 24596, 24597, 24598, 24599, - 24600, 24601, 24602, 24603, 24604, 24605, 24606, 24607, 24608, 24609, - 24641, 24642, 24643, 24644, 24645, 24646, 24647, 24648, 24649, 24610, - 24611, 24612, 24613, 24614, 24615, 24616, 24617, 24618, 24619, 24620, - 24621, 24622, 24623, 24624, 24625, 24626, 24627, 24628, 24629, 24630, - 24631, 24632, 24633, 24634, 24635, 24636, 24637, 24638, 24639, 24640, - 24656, 24657, 24658, 24659, 24660, 24661, 24662, 24663, 24664, 24665, - 24666, 24667, 24668, 24669, 24670, 24671, 24672, 24673, 24674, 24675, - 24650, 24651, 24652, 24653, 24654, 24655, 24676, 24677, 24678, 24679, - 24680, 24681, 24682, 24683, 24718, 24719, 24720, 24721, 24722, 24723, - 24724, 24725, 24726, 24727, 24684, 24685, 24686, 24687, 24688, 24689, - 24690, 24691, 24692, 24693, 24694, 24695, 24696, 24697, 24698, 24699, - 24700, 24701, 24702, 24703, 24709, 24710, 24711, 24712, 24713, 24714, - 24715, 24716, 24717, 24704, 24705, 24706, 24707, 24708, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 24739, 24734, 24737, - 24736, 24740, 24735, 24733, 24738, 24732, 24728, 24731, 24730, 24729, - 24746, 24741, 24745, 24742, 24743, 24744, 24747, 24749, 24748, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 24750, - 24751, 24752, 24753, 24754, 24755, 24756, 24757, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 27868, 27989, 27988, 27853, 27869, 27857, 40951, 27887, 27889, - 27888, 27883, 27882, 27880, 27881, 27942, 27875, 27899, 27939, 27863, - 27903, 27864, 27907, 27870, 27909, 27886, 27923, 27924, 27922, 27866, - 27920, 27925, 27926, 27967, 27968, 27931, 27867, 27876, 27982, 27964, - 27965, 27938, 27937, 27872, 27952, 27955, 27956, 27953, 27951, 27979, - 40951, 27874, 27794, 27841, 27689, 27772, 27801, 27686, 27843, 27944, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 10008, - 10009, 10010, 10011, 10012, 10002, 40951, 40951, 10003, 40951, 9958, - 9959, 9960, 9961, 9962, 9963, 9964, 9965, 9966, 9967, 9968, 9969, 9970, - 9971, 9972, 9973, 9974, 9975, 9976, 9977, 9978, 9979, 9980, 9981, 9982, - 9983, 9984, 9985, 9986, 9987, 9988, 9989, 9990, 9991, 9992, 9993, 9994, - 9995, 9996, 9997, 9998, 9999, 10000, 10001, 40951, 10006, 10007, 40951, - 40951, 40951, 10004, 40951, 40951, 10005, 20610, 20621, 20612, 20606, - 20618, 20623, 20613, 20619, 20604, 20617, 20620, 20607, 20625, 20622, - 20614, 20611, 20624, 20615, 20608, 20609, 20616, 20605, 40951, 20626, - 20601, 20597, 20600, 20598, 20596, 20602, 20603, 20599, 31035, 31046, - 31037, 31031, 31043, 31048, 31038, 31044, 31029, 31042, 31045, 31032, - 31050, 31028, 31047, 31039, 31036, 31049, 31040, 31033, 31034, 31041, - 31030, 31027, 31051, 31058, 31053, 31054, 31057, 31056, 31055, 31052, - 28806, 28821, 28811, 28832, 28823, 28817, 28813, 28829, 28834, 28824, - 28830, 28815, 28809, 28828, 28810, 28831, 28807, 28818, 28814, 28836, - 28812, 28833, 28825, 28822, 28835, 28826, 28819, 28820, 28808, 28827, - 28816, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 28841, - 28838, 28839, 28844, 28845, 28843, 28840, 28837, 28842, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 19658, 19674, 19666, 19665, - 19671, 19676, 19660, 19672, 19661, 19670, 19673, 19663, 19678, 19675, - 19667, 19659, 19677, 19668, 19664, 40951, 19669, 19662, 40951, 40951, - 40951, 40951, 40951, 19679, 19683, 19682, 19681, 19680, 31429, 31448, - 31444, 31431, 31432, 31440, 31441, 31433, 31439, 31442, 31445, 31447, - 31450, 31446, 31437, 31430, 31449, 31435, 31434, 31443, 31436, 31438, - 31455, 31454, 31451, 31456, 31452, 31453, 40951, 40951, 40951, 31457, - 25309, 25315, 25319, 25317, 25311, 25327, 25320, 25328, 25321, 25305, - 25322, 25313, 25323, 25325, 25308, 25303, 25326, 25318, 25324, 25307, - 25304, 25310, 25312, 25306, 25314, 25316, 40951, 40951, 40951, 40951, - 40951, 25329, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 27402, 27403, 27404, 27405, - 27401, 27400, 27376, 27377, 27398, 27397, 27380, 27381, 27382, 27383, - 27378, 27379, 27396, 27393, 27392, 27384, 27385, 27386, 27394, 27399, - 27387, 27388, 27389, 27390, 27391, 27395, 27406, 27407, 27298, 27319, - 27320, 27321, 27318, 27317, 27310, 27314, 27313, 27303, 27304, 27316, - 27312, 27308, 27307, 27305, 27299, 27306, 27309, 27315, 27300, 27301, - 27302, 27311, 40951, 40951, 40951, 40951, 27286, 27291, 27323, 27322, - 27372, 27364, 27358, 27335, 27329, 27352, 27346, 27324, 27341, 27370, - 27368, 27362, 27339, 27333, 27356, 27350, 40951, 40951, 27373, 27365, - 27359, 27336, 27330, 27353, 27347, 27326, 27343, 27375, 27367, 27361, - 27338, 27332, 27355, 27349, 27328, 27345, 27371, 27369, 27363, 27340, - 27334, 27357, 27351, 27325, 27342, 27374, 27366, 27360, 27337, 27331, - 27354, 27348, 27327, 27344, 27290, 27296, 27295, 27289, 27288, 27293, - 27292, 27287, 27297, 27294, 21668, 21690, 21692, 21688, 40951, 21689, - 21691, 40951, 40951, 40951, 40951, 40951, 21693, 21684, 21687, 21686, - 21634, 21632, 21656, 21655, 40951, 21654, 21653, 21662, 40951, 21642, - 21638, 21637, 21645, 21644, 21641, 21640, 21639, 21647, 21646, 21643, - 21658, 21657, 21652, 21651, 21664, 21666, 21665, 21663, 21660, 21648, - 21649, 21650, 21667, 21661, 21633, 21635, 21636, 21659, 40951, 40951, - 21682, 21683, 21685, 40951, 40951, 40951, 40951, 21694, 21631, 21630, - 21629, 21628, 21670, 21669, 21671, 21672, 21695, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 21676, 21681, 21674, 21673, 21680, 21678, - 21677, 21675, 21679, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 30405, 30401, 30406, 30411, 30402, 30409, 30395, 30403, 30407, 30399, - 30394, 30390, 30408, 30391, 30393, 30392, 30410, 30383, 30384, 30386, - 30389, 30387, 30388, 30398, 30400, 30385, 30404, 30397, 30396, 30413, - 30412, 30414, 30226, 30244, 30225, 30235, 30247, 30248, 30237, 30241, - 30239, 30232, 30236, 30228, 30243, 30227, 30249, 30238, 30240, 30221, - 30222, 30245, 30224, 30246, 30223, 30231, 30233, 30229, 30242, 30230, - 30234, 30252, 30251, 30250, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 25610, 25614, 25613, 25618, - 25617, 25615, 25639, 25642, 25658, 25622, 25621, 25620, 25619, 25640, - 25632, 25638, 25624, 25634, 25623, 25636, 25616, 25631, 25645, 25641, - 25628, 25612, 25611, 25644, 25643, 25629, 25626, 25635, 25625, 25637, - 25630, 25627, 25633, 25660, 25659, 40951, 40951, 40951, 40951, 25646, - 25650, 25649, 25648, 25647, 25657, 25655, 25653, 25652, 25651, 25656, - 25654, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 2542, 2543, 2549, 2545, 2548, 2544, 2546, 2547, 2585, 2586, 2575, 2576, - 2577, 2578, 2573, 2574, 2590, 2563, 2562, 2561, 2552, 2550, 2551, 2587, - 2589, 2572, 2570, 2582, 2581, 2571, 2593, 2588, 2580, 2579, 2557, 2556, - 2555, 2560, 2559, 2558, 2592, 2553, 2568, 2569, 2595, 2594, 2591, 2567, - 2584, 2565, 2583, 2564, 2566, 2554, 40951, 40951, 40951, 2596, 37309, - 33835, 22667, 22662, 22668, 22663, 20752, 20763, 20754, 20748, 20760, - 20765, 20755, 20761, 20746, 20759, 20762, 20749, 20767, 20764, 20756, - 20753, 20766, 20757, 20750, 20751, 20758, 20747, 40951, 40951, 20772, - 20769, 20770, 20775, 20771, 20768, 20773, 20774, 20721, 20735, 20726, - 20722, 20732, 20725, 20727, 20733, 20719, 20731, 20734, 20723, 20724, - 20736, 20728, 20737, 20729, 20730, 20720, 40951, 40951, 40951, 40951, - 40951, 20742, 20739, 20740, 20745, 20741, 20738, 20743, 20744, 31690, - 31704, 31695, 31691, 31701, 31694, 31696, 31702, 31700, 31703, 31692, - 31693, 31705, 31697, 31707, 31698, 31699, 31706, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 31715, 31716, 31688, 31689, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 31712, 31709, 31710, 31714, 31711, 31708, 31713, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 30415, 30457, 30458, - 30447, 30483, 30476, 30450, 30451, 30485, 30428, 30468, 30416, 30461, - 30430, 30470, 30418, 30462, 30429, 30469, 30417, 30446, 30482, 30436, - 30475, 30425, 30465, 30419, 30463, 30452, 30486, 30431, 30471, 30420, - 30441, 30444, 30432, 30421, 30459, 30439, 30478, 30437, 30477, 30440, - 30479, 30467, 30438, 30460, 30445, 30453, 30448, 30443, 30481, 30433, - 30472, 30449, 30484, 30454, 30487, 30434, 30473, 30422, 30426, 30423, - 30427, 30466, 30442, 30480, 30435, 30474, 30424, 30464, 30455, 30456, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 30074, 30077, 30100, 30075, 30089, - 30085, 30091, 30101, 30076, 30079, 30121, 30102, 30103, 30092, 30093, - 30104, 30122, 30123, 30105, 30106, 30078, 30118, 30094, 30095, 30080, - 30082, 30086, 30114, 30116, 30110, 30112, 30115, 30107, 30081, 30108, - 30124, 30087, 30088, 30096, 30083, 30097, 30090, 30117, 30120, 30111, - 30113, 30109, 30098, 30099, 30084, 30119, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 30125, - 30128, 30151, 30126, 30140, 30136, 30142, 30152, 30127, 30130, 30172, - 30153, 30154, 30143, 30144, 30155, 30173, 30174, 30156, 30157, 30129, - 30169, 30145, 30146, 30131, 30133, 30137, 30165, 30167, 30161, 30163, - 30166, 30158, 30132, 30159, 30175, 30138, 30139, 30147, 30134, 30148, - 30141, 30168, 30171, 30162, 30164, 30160, 30149, 30150, 30135, 30170, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 30178, 30177, 30181, - 30176, 30179, 30180, 19611, 19598, 19606, 19590, 19589, 19603, 19599, - 19602, 19587, 19600, 19584, 19583, 19592, 19591, 19610, 19597, 19596, - 19588, 19601, 19604, 19605, 19595, 19608, 19585, 19609, 19586, 19593, - 19594, 19607, 19618, 19620, 19622, 19619, 19621, 19613, 19612, 19617, - 19614, 19616, 19615, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 19629, 19631, 19628, 19627, 19624, 19623, 19626, 19625, 19632, - 19630, 40951, 40951, 40951, 40951, 40951, 40951, 17713, 17715, 17712, - 17711, 17708, 17707, 17710, 17709, 17716, 17714, 17699, 17700, 17701, - 17698, 17702, 17696, 17673, 17657, 17665, 17663, 17656, 17662, 17668, - 17670, 17664, 17660, 17658, 17671, 17672, 17669, 17667, 17654, 17659, - 17655, 17666, 17661, 17652, 17653, 40951, 40951, 40951, 17697, 17651, - 17649, 17648, 17650, 17704, 17703, 17695, 17679, 17687, 17685, 17678, - 17684, 17690, 17692, 17686, 17682, 17680, 17693, 17694, 17691, 17689, - 17676, 17681, 17677, 17688, 17683, 17674, 17675, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 17705, 17706, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 32268, 32266, 32265, 32262, 32261, - 32264, 32263, 32269, 32267, 32260, 32259, 32257, 32248, 32246, 32255, - 32253, 32244, 32250, 32251, 32258, 32256, 32247, 32245, 32254, 32252, - 32243, 32249, 32240, 32241, 32239, 32242, 40951, 39408, 39442, 39422, - 39421, 39427, 39426, 39404, 39403, 39402, 39413, 39434, 39407, 39438, - 39441, 39440, 39437, 39445, 39424, 39423, 39425, 39406, 39428, 39439, - 39409, 39433, 39444, 39429, 39430, 39417, 39415, 39414, 39416, 39418, - 39405, 39420, 39443, 39431, 39432, 39411, 39412, 39435, 39410, 40951, - 39400, 39399, 39401, 40951, 40951, 39419, 39436, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 1195, 1490, 1326, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 1061, 2351, 2352, 2349, 30345, 30353, 30367, 30354, 30358, 30364, 30355, - 30370, 30359, 30365, 30363, 30366, 30357, 30372, 30368, 30347, 30348, - 30360, 30346, 30344, 30371, 30361, 30349, 30350, 30356, 30362, 30369, - 30351, 30352, 30379, 30377, 30374, 30382, 30381, 30378, 30376, 30375, - 30380, 30343, 30373, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 33894, 33908, 33896, 33905, 33912, 33900, 33906, 33904, 33907, - 33897, 33914, 33910, 33901, 33895, 33913, 33902, 33899, 33903, 33911, - 33909, 33898, 33893, 33888, 33886, 33889, 33887, 33892, 33891, 33883, - 33882, 33884, 33890, 33885, 33915, 33918, 33917, 33916, 33921, 33922, - 33919, 33923, 33920, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 30492, 30504, 30502, 30507, 30496, - 30501, 30500, 30503, 30494, 30509, 30505, 30497, 30508, 30498, 30493, - 30499, 30506, 30495, 30491, 30490, 30489, 30488, 30513, 30510, 30511, - 30512, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 6491, - 6485, 6499, 6493, 6488, 6496, 6502, 6484, 6494, 6497, 6495, 6498, 6489, - 6504, 6500, 6486, 6492, 6503, 6490, 6487, 6501, 6509, 6506, 6507, 6511, - 6508, 6505, 6510, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 16720, 16731, 16722, 16716, 16728, 16733, 16723, 16729, - 16714, 16727, 16730, 16717, 16735, 16732, 16724, 16721, 16734, 16725, - 16718, 16719, 16726, 16715, 16736, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 4635, 4640, 4638, 4637, 4639, 4562, 4563, - 4581, 4582, 4579, 4580, 4574, 4575, 4576, 4577, 4608, 4564, 4555, 4565, - 4601, 4600, 4597, 4596, 4585, 4595, 4594, 4599, 4598, 4587, 4571, 4570, - 4567, 4566, 4586, 4573, 4572, 4569, 4568, 4588, 4603, 4602, 4593, 4592, - 4605, 4607, 4606, 4584, 4578, 4589, 4590, 4591, 4604, 4583, 4556, 4561, - 4560, 4645, 4644, 4654, 4655, 4648, 4649, 4650, 4651, 4652, 4653, 4656, - 4646, 4641, 4647, 4657, 4659, 4658, 4633, 4632, 4631, 4634, 4630, 40951, - 40951, 40951, 40951, 4626, 4624, 4621, 4612, 4614, 4619, 4617, 4609, - 4615, 4625, 4623, 4622, 4611, 4613, 4620, 4618, 4610, 4616, 4627, 4628, - 4666, 4668, 4665, 4664, 4661, 4660, 4663, 4662, 4669, 4667, 4636, 4558, - 4559, 4642, 4643, 4557, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 4629, 20979, 20981, 20983, 20939, 20940, 20949, 20950, - 20947, 20948, 20977, 20941, 20978, 20942, 20967, 20966, 20963, 20962, - 20951, 20961, 20960, 20965, 20964, 20953, 20944, 20943, 20936, 20934, - 20935, 20970, 20952, 20946, 20945, 20938, 20937, 20954, 20969, 20968, - 20959, 20958, 20974, 20976, 20971, 20973, 20975, 20955, 20956, 20957, - 20972, 20989, 20994, 20995, 20992, 20993, 20996, 20990, 20997, 20991, - 20982, 20980, 21000, 21001, 20998, 20984, 20985, 20987, 20986, 20988, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 20999, 40951, 40951, 33946, 33947, 33936, 33937, 33938, 33939, 33932, - 33933, 33943, 33935, 33948, 33944, 33949, 33945, 33940, 33942, 33941, - 33934, 33950, 33929, 33951, 33953, 33952, 33930, 33931, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 33960, 33962, 33959, 33958, 33955, - 33954, 33957, 33956, 33963, 33961, 40951, 40951, 40951, 40951, 40951, - 40951, 6174, 6176, 6175, 6170, 6172, 6173, 6171, 6159, 6158, 6155, 6154, - 6140, 6153, 6152, 6157, 6156, 6142, 6145, 6144, 6137, 6136, 6141, 6147, - 6146, 6139, 6138, 6143, 6163, 6162, 6151, 6150, 6165, 6148, 6149, 6166, - 6161, 6169, 6167, 6164, 6178, 6186, 6187, 6182, 6183, 6184, 6180, 6188, - 6181, 6189, 6206, 6205, 6190, 6204, 40951, 6199, 6201, 6198, 6197, 6194, - 6193, 6196, 6195, 6202, 6200, 6177, 6192, 6191, 6203, 6160, 6179, 6185, - 6168, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 25371, - 25373, 25375, 25372, 25374, 25363, 25362, 25359, 25358, 25357, 25356, - 25361, 25360, 25342, 25351, 25350, 25345, 25344, 25341, 25353, 25352, - 25347, 25346, 25343, 25365, 25364, 25355, 25354, 25368, 25349, 25367, - 25370, 25369, 25366, 25348, 25378, 25379, 25377, 25376, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 32741, 32744, 32748, - 32687, 32688, 32706, 32707, 32704, 32705, 32699, 32700, 32701, 32702, - 32733, 32689, 32734, 32690, 32726, 32725, 32722, 32721, 32710, 32720, - 32719, 32724, 32723, 32712, 32696, 32695, 32692, 32691, 32711, 32698, - 32697, 32694, 32693, 32713, 32728, 32727, 32718, 32717, 32730, 32732, - 32731, 32709, 32708, 32703, 32714, 32715, 32716, 32729, 32752, 32761, - 32762, 32755, 32756, 32757, 32758, 32759, 32760, 32763, 32753, 32764, - 32754, 32747, 32743, 32745, 32746, 32767, 32673, 32672, 32765, 32738, - 32735, 32742, 32750, 32684, 32749, 32751, 32739, 32680, 32682, 32679, - 32678, 32675, 32674, 32677, 32676, 32683, 32681, 32685, 32740, 32686, - 32766, 32736, 32737, 40951, 33649, 33647, 33646, 33643, 33642, 33645, - 33644, 33650, 33648, 33661, 33660, 33659, 33655, 33654, 33658, 33657, - 33653, 33656, 33651, 33652, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 22374, 22375, 22401, 22403, 22400, - 22376, 22402, 22377, 22391, 22390, 22366, 22364, 22365, 22384, 22389, - 22388, 22373, 22372, 40951, 22386, 22379, 22378, 22369, 22368, 22385, - 22381, 22380, 22371, 22367, 22370, 22387, 22393, 22392, 22363, 22361, - 22362, 22395, 22399, 22397, 22383, 22398, 22360, 22394, 22382, 22411, - 22414, 22415, 22418, 22416, 22412, 22417, 22413, 22408, 22407, 22406, - 22404, 22358, 22357, 22419, 22409, 22356, 22420, 22405, 22396, 22359, - 22410, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 28281, 28283, 28284, 28282, 28272, 28271, 28270, - 40951, 28269, 40951, 28268, 28267, 28260, 28259, 40951, 28254, 28262, - 28261, 28250, 28248, 28249, 28253, 28264, 28263, 28252, 28251, 28255, - 28274, 28273, 28266, 40951, 28265, 28277, 28280, 28258, 28276, 28279, - 28278, 28275, 28257, 28256, 28285, 40951, 40951, 40951, 40951, 40951, - 40951, 22435, 22436, 22447, 22448, 22445, 22446, 22466, 22437, 22467, - 22438, 22456, 22455, 22426, 22424, 22425, 22449, 22454, 22453, 22434, - 22433, 22432, 22451, 22442, 22441, 22429, 22427, 22439, 22428, 22450, - 22444, 22443, 22431, 22430, 22452, 22458, 22457, 22423, 22421, 22422, - 22463, 22465, 22440, 22462, 22464, 22459, 22460, 22461, 22470, 22471, - 22476, 22477, 22474, 22475, 22478, 22472, 22479, 22473, 22468, 22469, - 40951, 40951, 40951, 40951, 40951, 22486, 22488, 22485, 22484, 22481, - 22480, 22483, 22482, 22489, 22487, 40951, 40951, 40951, 40951, 40951, - 40951, 18095, 18096, 18099, 18102, 40951, 18052, 18053, 18066, 18067, - 18064, 18065, 18047, 18049, 40951, 40951, 18090, 18054, 40951, 40951, - 18089, 18055, 18086, 18085, 18082, 18081, 18070, 18080, 18079, 18084, - 18083, 18072, 18061, 18060, 18057, 18056, 18071, 18063, 18062, 18059, - 18058, 18073, 40951, 18088, 18087, 18078, 18077, 18092, 18094, 18093, - 40951, 18069, 18068, 40951, 18051, 18074, 18075, 18076, 18091, 40951, - 8077, 18097, 18098, 18104, 18113, 18114, 18107, 18108, 18109, 18110, - 40951, 40951, 18116, 18105, 40951, 40951, 18115, 18106, 18101, 40951, - 40951, 18117, 40951, 40951, 40951, 40951, 40951, 40951, 18103, 40951, - 40951, 40951, 40951, 40951, 18100, 18046, 18045, 18048, 18050, 18111, - 18112, 40951, 40951, 8257, 8258, 8256, 8255, 8254, 8253, 8252, 40951, - 40951, 40951, 8263, 8260, 8261, 8259, 8262, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 37588, 37589, 37612, - 37613, 37610, 37611, 37605, 37606, 37607, 37608, 40951, 37634, 40951, - 40951, 37590, 40951, 37633, 37591, 37630, 37629, 37626, 37625, 37614, - 37624, 37623, 37628, 37627, 37616, 37602, 37601, 37593, 37592, 37615, - 37604, 37603, 37595, 37594, 37617, 37632, 37631, 37622, 37621, 37636, - 37637, 37600, 37598, 37609, 37618, 37619, 37620, 37635, 37597, 37599, - 37596, 40951, 37639, 37650, 37659, 37660, 37653, 37654, 37655, 37656, - 37657, 37658, 40951, 37662, 40951, 40951, 37651, 40951, 37661, 37652, - 37583, 37644, 40951, 37640, 37647, 37646, 37641, 37584, 37638, 37587, - 37645, 37586, 37585, 40951, 37642, 37643, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 37649, 37648, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 29204, 29205, 29218, 29219, 29216, - 29217, 29200, 29201, 29202, 29203, 29244, 29206, 29245, 29207, 29232, - 29231, 29228, 29227, 29193, 29192, 29226, 29225, 29230, 29229, 29195, - 29194, 29213, 29212, 29209, 29208, 29197, 29215, 29214, 29211, 29210, - 29198, 29196, 29238, 29237, 29224, 29223, 29236, 29235, 29243, 29240, - 29239, 29234, 29233, 29242, 29220, 29221, 29222, 29241, 29258, 29267, - 29268, 29261, 29262, 29263, 29264, 29265, 29266, 29269, 29259, 29270, - 29260, 29253, 29246, 29249, 29254, 29247, 29248, 29251, 29274, 29255, - 29180, 29178, 29273, 29191, 29271, 29187, 29189, 29186, 29185, 29182, - 29181, 29184, 29183, 29190, 29188, 29179, 29257, 40951, 29272, 29256, - 29199, 29250, 29252, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 37311, 37312, 37313, 37331, 37332, 37329, 37330, - 37324, 37325, 37326, 37327, 37357, 37314, 37358, 37315, 37349, 37348, - 37345, 37344, 37333, 37343, 37342, 37347, 37346, 37335, 37321, 37320, - 37317, 37316, 37334, 37323, 37322, 37319, 37318, 37336, 37351, 37350, - 37341, 37340, 37354, 37356, 37355, 37353, 37328, 37337, 37338, 37339, - 37352, 37367, 37376, 37377, 37370, 37371, 37372, 37373, 37374, 37375, - 37378, 37365, 37368, 37379, 37366, 37369, 37359, 37362, 37364, 37363, - 37360, 37361, 37390, 37310, 37391, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 37386, 37388, 37385, 37384, 37381, 37380, 37383, - 37382, 37389, 37387, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 32855, - 32857, 32878, 32879, 32876, 32877, 32871, 32872, 32873, 32874, 32904, - 32858, 32905, 32859, 32896, 32895, 32892, 32891, 32880, 32890, 32889, - 32894, 32893, 32882, 32865, 32864, 32868, 32867, 32881, 32866, 32861, - 32870, 32869, 32883, 32898, 32897, 32888, 32887, 32901, 32903, 32902, - 32900, 32875, 32884, 32885, 32886, 32899, 32933, 32940, 32941, 32938, - 32939, 32936, 32937, 40951, 40951, 32942, 32934, 32943, 32935, 32926, - 32928, 32930, 32929, 32927, 32925, 32945, 32944, 32924, 32923, 32906, - 32907, 32908, 32854, 32921, 32920, 32918, 32916, 32917, 32909, 32910, - 32919, 32922, 32914, 32915, 32913, 32912, 32911, 32860, 32862, 32863, - 32856, 32931, 32932, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 27636, 27637, 27655, - 27656, 27653, 27654, 27648, 27649, 27650, 27651, 27682, 27638, 27683, - 27639, 27675, 27674, 27671, 27670, 27659, 27669, 27668, 27673, 27672, - 27661, 27645, 27644, 27641, 27640, 27660, 27647, 27646, 27643, 27642, - 27662, 27677, 27676, 27667, 27666, 27679, 27681, 27680, 27658, 27652, - 27663, 27664, 27665, 27678, 27657, 27611, 27620, 27621, 27614, 27615, - 27616, 27617, 27618, 27619, 27622, 27612, 27623, 27613, 27607, 27610, - 27609, 27606, 27625, 27624, 27684, 27608, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 27632, 27634, 27631, - 27630, 27627, 27626, 27629, 27628, 27635, 27633, 40951, 40951, 40951, - 40951, 40951, 40951, 28164, 28159, 28004, 28165, 28163, 28161, 28160, - 28021, 28022, 28155, 28157, 28156, 28166, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 35301, 35303, 35318, 35319, 35316, - 35317, 35343, 35304, 35344, 35305, 35333, 35332, 35329, 35328, 35320, - 35327, 35326, 35331, 35330, 35322, 35313, 35312, 35307, 35306, 35321, - 35315, 35314, 35309, 35308, 35323, 35335, 35334, 35325, 35324, 35340, - 35342, 35311, 35339, 35341, 35336, 35337, 35338, 35310, 35346, 35348, - 35349, 35354, 35355, 35352, 35353, 35356, 35350, 35357, 35351, 35347, - 35345, 35302, 35300, 40951, 40951, 40951, 40951, 40951, 40951, 35364, - 35366, 35363, 35362, 35359, 35358, 35361, 35360, 35367, 35365, 40951, - 40951, 40951, 40951, 40951, 40951, 28773, 28775, 28772, 28771, 28768, - 28767, 28770, 28769, 28776, 28774, 28723, 28725, 28722, 28721, 28718, - 28717, 28720, 28719, 28726, 28724, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 191, 190, 178, 181, 175, 167, 193, 192, 183, 195, - 189, 184, 174, 196, 177, 197, 180, 194, 164, 171, 170, 187, 166, 186, - 182, 188, 165, 40951, 40951, 162, 163, 161, 203, 204, 210, 211, 208, 209, - 212, 207, 213, 205, 206, 200, 40951, 40951, 40951, 40951, 222, 224, 221, - 220, 217, 216, 219, 218, 225, 223, 215, 214, 198, 199, 201, 202, 185, - 173, 172, 169, 168, 179, 176, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 11026, - 11027, 11042, 11043, 11040, 11041, 11068, 11028, 11069, 11029, 11060, - 11059, 11056, 11055, 11044, 11054, 11053, 11058, 11057, 11046, 11037, - 11036, 11031, 11030, 11045, 11039, 11038, 11033, 11032, 11047, 11062, - 11061, 11052, 11051, 11065, 11067, 11035, 11064, 11066, 11048, 11049, - 11050, 11063, 11034, 11072, 11077, 11078, 11075, 11076, 11070, 11071, - 11079, 11073, 11080, 11074, 11083, 11085, 11084, 11082, 11081, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 39052, - 39041, 39070, 39060, 39062, 39063, 39069, 39059, 39045, 39054, 39042, - 39072, 39068, 39047, 39061, 39058, 39046, 39055, 39064, 39053, 39071, - 39044, 39043, 39066, 39067, 39050, 39049, 39048, 39051, 39056, 39057, - 39065, 39084, 39073, 39102, 39092, 39094, 39095, 39101, 39091, 39077, - 39086, 39074, 39104, 39100, 39079, 39093, 39090, 39078, 39087, 39096, - 39085, 39103, 39076, 39075, 39098, 39099, 39082, 39081, 39080, 39083, - 39088, 39089, 39097, 39111, 39113, 39110, 39109, 39106, 39105, 39108, - 39107, 39114, 39112, 39123, 39122, 39121, 39117, 39116, 39120, 39119, - 39115, 39118, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 39124, 10942, 10943, 10950, 10951, 10948, - 10949, 10977, 40951, 40951, 10978, 40951, 40951, 10968, 10967, 10966, - 10965, 10954, 10964, 10963, 10972, 40951, 10956, 10938, 40951, 10945, - 10944, 10955, 10939, 10937, 10947, 10946, 10957, 10970, 10969, 10962, - 10961, 10973, 10941, 10940, 10974, 10953, 10975, 10958, 10959, 10960, - 10971, 10952, 10976, 10983, 10987, 10988, 10985, 10986, 10989, 40951, - 10984, 10990, 40951, 40951, 10982, 10980, 10979, 10991, 10996, 10993, - 10997, 10992, 10981, 10926, 10994, 10995, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 10933, 10935, 10932, 10931, 10928, - 10927, 10930, 10929, 10936, 10934, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 28895, 28896, 28911, 28912, 28909, - 28910, 28892, 28893, 40951, 40951, 28937, 28897, 28938, 28898, 28931, - 28930, 28927, 28926, 28915, 28925, 28924, 28929, 28928, 28917, 28906, - 28905, 28900, 28899, 28916, 28908, 28907, 28902, 28901, 28918, 28933, - 28932, 28923, 28922, 28935, 28936, 28904, 28914, 28894, 28919, 28920, - 28921, 28934, 28913, 28903, 28947, 28952, 28953, 28950, 28951, 28945, - 28946, 40951, 40951, 28954, 28948, 28955, 28949, 28941, 28943, 28942, - 28940, 28939, 28956, 28944, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40732, 40753, 40750, 40749, 40752, 40748, 40747, 40745, 40746, - 40751, 40744, 40700, 40699, 40719, 40718, 40701, 40717, 40716, 40726, - 40703, 40711, 40710, 40693, 40692, 40702, 40713, 40712, 40697, 40696, - 40704, 40721, 40720, 40715, 40714, 40728, 40709, 40708, 40695, 40694, - 40722, 40723, 40724, 40731, 40729, 40727, 40730, 40705, 40706, 40707, - 40725, 40698, 40691, 40741, 40738, 40739, 40740, 40737, 40742, 40688, - 40687, 40685, 40684, 40686, 40690, 40683, 40736, 40734, 40733, 40735, - 40689, 40682, 40743, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 34052, 34073, 34071, 34070, 34072, 34068, 34069, 34066, 34067, - 34065, 34064, 34074, 34019, 34018, 34038, 34037, 34020, 34036, 34035, - 34040, 34039, 34022, 34030, 34029, 34013, 34012, 34021, 34032, 34031, - 34016, 34014, 34023, 34042, 34041, 34034, 34033, 34048, 34028, 34027, - 34015, 34043, 34044, 34045, 34051, 34049, 34047, 34050, 34024, 34025, - 34026, 34046, 34017, 34057, 34059, 33996, 33995, 33993, 33994, 34004, - 34005, 34000, 34003, 33999, 34002, 34007, 34008, 34006, 33998, 33997, - 34001, 34060, 34058, 34075, 34061, 34056, 34055, 34054, 34053, 34011, - 34010, 34009, 34062, 34063, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 5617, 5618, 5615, 5616, - 5613, 5614, 5623, 5624, 5621, 5622, 5619, 5620, 5786, 5787, 5788, 5785, - 31247, 31245, 31254, 31255, 31251, 31259, 31258, 31240, 31253, 31252, - 31244, 31257, 31250, 31243, 31249, 31248, 31241, 31246, 31256, 31235, - 31242, 31260, 31261, 31236, 31262, 31238, 31239, 31237, 31231, 31228, - 31232, 31230, 31226, 31229, 31233, 31227, 31234, 31268, 31267, 31278, - 31269, 31270, 31279, 31275, 31274, 31276, 31277, 31271, 31225, 31272, - 31273, 31264, 31263, 31223, 31265, 31266, 31224, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 10668, 10669, 10758, 10759, 10760, 10761, - 10768, 10767, 10769, 10773, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 34658, 34677, 34684, 34681, - 34671, 34668, 34663, 34685, 34653, 34670, 34679, 34660, 34657, 34666, - 34683, 34661, 34680, 34667, 34672, 34678, 34682, 34655, 34654, 34659, - 34675, 34669, 34665, 34664, 34673, 34656, 34674, 34676, 34662, 34686, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 34693, 34695, 34692, 34691, 34688, 34687, - 34690, 34689, 34696, 34694, 40951, 40951, 40951, 40951, 40951, 40951, - 3661, 3662, 3675, 3676, 3673, 3674, 3657, 3658, 3659, 40951, 3701, 3663, - 3702, 3664, 3693, 3692, 3689, 3688, 3677, 3687, 3686, 3691, 3690, 3679, - 3670, 3669, 3666, 3665, 3678, 3672, 3671, 3668, 3667, 3680, 3695, 3694, - 3685, 3684, 3698, 3700, 3699, 3697, 3660, 3681, 3682, 3683, 3696, 3729, - 3734, 3735, 3732, 3733, 3726, 3727, 3728, 40951, 3736, 3730, 3737, 3731, - 3721, 3723, 3725, 3724, 3722, 3739, 3738, 3750, 3751, 3752, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 3746, 3748, 3745, - 3744, 3741, 3740, 3743, 3742, 3749, 3747, 3720, 3718, 3715, 3706, 3708, - 3713, 3711, 3703, 3709, 3719, 3717, 3716, 3705, 3707, 3714, 3712, 3704, - 3710, 3753, 40951, 40951, 40951, 25735, 25736, 25681, 25680, 25690, - 25675, 25679, 25678, 25692, 25676, 25672, 25671, 25674, 25677, 25683, - 25682, 25689, 25694, 25670, 25669, 25673, 25696, 25686, 25687, 25688, - 25697, 25695, 25693, 25684, 25685, 25691, 25698, 40951, 40951, 25713, - 25712, 25721, 25707, 25711, 25710, 25723, 25708, 25704, 25703, 25706, - 25709, 25715, 25714, 25720, 25725, 25702, 25701, 25705, 25727, 25718, - 25719, 40951, 25728, 25726, 25724, 25716, 25717, 25722, 25729, 25730, - 25732, 25734, 25731, 25733, 25700, 25699, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 25747, - 25748, 25757, 25758, 25755, 25756, 25784, 40951, 25749, 25785, 40951, - 25750, 25763, 25762, 25776, 25775, 25764, 25774, 25773, 25741, 25740, - 25766, 25743, 25742, 25752, 25751, 25765, 25746, 25744, 25754, 25753, - 25767, 25778, 25777, 25772, 25771, 25780, 25783, 25781, 25760, 25782, - 25768, 25769, 25770, 25779, 25759, 25761, 25739, 25745, 25794, 25799, - 25800, 25797, 25798, 25793, 40951, 40951, 40951, 25801, 40951, 25795, - 25802, 40951, 25796, 25792, 25791, 25790, 25788, 25789, 25803, 25787, - 25786, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 25810, - 25812, 25809, 25808, 25805, 25804, 25807, 25806, 25813, 25811, 40951, - 40951, 40951, 40951, 40951, 40951, 18770, 18771, 18784, 18785, 18782, - 18783, 40951, 18801, 18772, 40951, 18800, 18773, 18807, 18806, 18789, - 18788, 18803, 18797, 18796, 18781, 18780, 18787, 18793, 18792, 18777, - 18776, 18769, 18791, 18790, 18779, 18778, 18786, 18795, 18794, 18775, - 18774, 18768, 18799, 18798, 18802, 18804, 18805, 18810, 18815, 18816, - 18813, 18814, 40951, 18818, 18811, 40951, 18817, 18812, 18809, 18808, - 18819, 18830, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 18826, - 18828, 18825, 18824, 18821, 18820, 18823, 18822, 18829, 18827, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 25433, - 25431, 25425, 25436, 25428, 25435, 25439, 25430, 25427, 25429, 25432, - 25426, 25441, 25437, 25434, 25440, 25438, 25442, 25448, 25445, 25447, - 25444, 25446, 25443, 25424, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 21513, 21517, 21515, 21516, 21454, 21455, 21474, 21475, 21468, - 21469, 21470, 21471, 21472, 21473, 21499, 21456, 21500, 40951, 21490, - 21489, 21488, 21487, 21476, 21486, 21485, 21459, 21458, 21478, 21465, - 21464, 21461, 21460, 21477, 21467, 21466, 21463, 21462, 21479, 21492, - 21491, 21484, 21483, 21495, 21498, 21496, 21494, 21497, 21480, 21481, - 21482, 21493, 21457, 21519, 21518, 21526, 21527, 21524, 21525, 21521, - 40951, 40951, 40951, 21522, 21520, 21523, 21512, 21540, 21529, 21528, - 21507, 21510, 21506, 21508, 21504, 21503, 21511, 21502, 21505, 21509, - 21501, 21536, 21538, 21535, 21534, 21531, 21530, 21533, 21532, 21539, - 21537, 21514, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 25086, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 35424, 35420, 35414, 35423, 35416, 35425, 35429, - 35431, 35426, 35421, 35422, 35427, 35415, 35432, 35430, 35417, 35428, - 35418, 35419, 35433, 35434, 35498, 35481, 35479, 35488, 35487, 35483, - 35489, 35485, 35484, 35491, 35492, 35493, 35490, 35482, 35495, 35413, - 35400, 35471, 35478, 35768, 35769, 35398, 35373, 35499, 35767, 35435, - 35500, 35486, 35494, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 35476, 8820, 8828, 8826, 8822, - 8827, 8823, 8821, 8824, 8825, 8889, 8829, 8838, 8837, 8831, 8830, 8842, - 8832, 8833, 8841, 8835, 8836, 8843, 8844, 8849, 8846, 8845, 8847, 8848, - 8851, 8853, 8855, 8854, 8856, 8862, 8859, 8860, 8864, 8857, 8858, 8863, - 8861, 8866, 8865, 8867, 8869, 8870, 8874, 8873, 8871, 8872, 8875, 8888, - 8876, 8877, 8878, 8887, 8879, 8883, 8884, 8882, 8880, 8881, 8886, 8885, - 8890, 8891, 8902, 8893, 8897, 8898, 8899, 8900, 8901, 8903, 8906, 8905, - 8904, 8907, 8909, 8910, 8911, 8912, 8913, 8914, 8915, 8916, 8917, 8918, - 8919, 8920, 8921, 8922, 8923, 8924, 8925, 8926, 8941, 8927, 8928, 8938, - 8932, 8929, 8930, 8931, 8939, 8936, 8940, 8937, 8933, 8935, 8948, 8944, - 8945, 8946, 8949, 8960, 8950, 8953, 8954, 8956, 8957, 8958, 8961, 8964, - 8962, 8963, 8965, 8966, 8968, 8969, 8998, 9005, 8999, 9000, 9001, 9002, - 9003, 9004, 9006, 9008, 9007, 9009, 9012, 9013, 9016, 9010, 9011, 9017, - 9062, 9061, 9063, 9018, 9021, 9022, 9023, 9019, 9020, 9024, 9027, 9025, - 9028, 9030, 9039, 9040, 9041, 9042, 9060, 9043, 9044, 9057, 9058, 9056, - 9045, 9046, 9047, 9048, 9049, 9050, 9051, 9054, 9055, 9064, 9154, 9065, - 9066, 9068, 9067, 9074, 9069, 9071, 9073, 9077, 9076, 9078, 9079, 9086, - 9080, 9081, 9085, 9089, 9090, 9087, 9088, 9096, 9093, 9097, 9098, 9099, - 9100, 9101, 9103, 9104, 9106, 9105, 9108, 9107, 9110, 9109, 9111, 9112, - 9114, 9115, 9119, 9121, 9125, 9126, 9139, 9131, 9132, 9127, 9128, 9129, - 9133, 9137, 9134, 9135, 9136, 9140, 9141, 9143, 9144, 9145, 9146, 9147, - 9148, 9158, 9149, 9150, 9153, 9152, 9151, 9155, 9156, 9157, 9159, 9160, - 9163, 9164, 9165, 9166, 9167, 9169, 9168, 9185, 9176, 9177, 9170, 9171, - 9173, 9174, 9172, 9175, 9184, 9178, 9183, 9181, 9180, 9182, 9187, 9206, - 9188, 9189, 9190, 9193, 9192, 9194, 9195, 9197, 9198, 9199, 9207, 9200, - 9201, 9202, 9205, 9204, 9203, 9208, 9209, 9211, 9212, 9213, 9214, 9216, - 9220, 9217, 9221, 9219, 9218, 9222, 9223, 9224, 9225, 9229, 9228, 9226, - 9227, 9230, 9231, 9233, 9252, 9254, 9234, 9236, 9235, 9237, 9238, 9241, - 9242, 9240, 9239, 9243, 9244, 9245, 9246, 9249, 9247, 9248, 9250, 9251, - 9255, 9256, 9253, 9257, 9258, 9259, 9260, 9262, 9265, 9264, 9266, 9267, - 9269, 9270, 9271, 9275, 9274, 9272, 9273, 9276, 9280, 9279, 9278, 9281, - 9282, 9284, 9285, 9289, 9286, 9287, 9292, 9290, 9293, 9294, 9296, 9295, - 9297, 9298, 9320, 9319, 9322, 9323, 9304, 9305, 9302, 9303, 9301, 9299, - 9307, 9306, 9308, 9310, 9316, 9317, 9314, 9315, 9324, 9325, 9326, 9344, - 9329, 9330, 9331, 9327, 9328, 9332, 9333, 9334, 9335, 9336, 9338, 9339, - 9340, 9341, 9342, 9367, 9345, 9348, 9346, 9347, 9353, 9354, 9351, 9352, - 9349, 9350, 9355, 9356, 9364, 9357, 9358, 9365, 9362, 9363, 9366, 9359, - 9360, 9361, 9368, 9369, 9370, 9371, 9372, 9373, 9374, 9376, 9377, 9375, - 9378, 9379, 9420, 9421, 9382, 9383, 9380, 9381, 9386, 9387, 9385, 9391, - 9388, 9390, 9389, 9395, 9396, 9394, 9392, 9393, 9397, 9398, 9399, 9400, - 9401, 9402, 9403, 9422, 9408, 9404, 9405, 9406, 9407, 9409, 9411, 9410, - 9412, 9413, 9415, 9414, 9416, 9418, 9417, 9423, 9424, 9427, 9428, 9425, - 9426, 9502, 9497, 9498, 9499, 9500, 9501, 9503, 9506, 9504, 9505, 9507, - 9549, 9508, 9538, 9539, 9515, 9517, 9534, 9518, 9540, 9522, 9520, 9521, - 9523, 9524, 9525, 9526, 9535, 9536, 9529, 9530, 9532, 9541, 9509, 9510, - 9514, 9512, 9550, 9542, 9544, 9543, 9545, 9551, 9552, 9546, 9547, 9548, - 9553, 9554, 9555, 9558, 9559, 9560, 9556, 9557, 9561, 9562, 9564, 9566, - 9567, 9585, 9583, 9584, 9587, 9586, 9568, 9575, 9573, 9574, 9569, 9570, - 9576, 9577, 9578, 9579, 9580, 9582, 9588, 9597, 9589, 9591, 9592, 9590, - 9593, 9595, 9594, 9596, 9599, 9601, 9600, 9602, 9603, 9636, 9638, 9604, - 9606, 9605, 9608, 9611, 9609, 9610, 9614, 9618, 9621, 9620, 9623, 9626, - 9624, 9625, 9629, 9631, 9637, 9639, 9640, 9644, 9651, 9649, 9647, 9648, - 9650, 9653, 9652, 9645, 9646, 9654, 9657, 9663, 9658, 9661, 9656, 9655, - 9664, 9662, 9659, 9660, 9665, 9666, 9667, 9668, 9669, 9670, 9671, 9673, - 9676, 9677, 9680, 9681, 9682, 9678, 9679, 9674, 9675, 9683, 9684, 9685, - 9686, 9687, 9688, 9690, 9691, 9692, 9693, 9694, 9698, 9695, 9719, 9712, - 9713, 9718, 9701, 9700, 9714, 9717, 9715, 9704, 9703, 9706, 9708, 9709, - 9710, 9711, 9707, 9720, 9696, 9721, 9722, 9723, 9724, 9725, 9726, 9734, - 9732, 9730, 9733, 9729, 9731, 9727, 9728, 9735, 9737, 9738, 9739, 9746, - 9741, 9742, 9750, 9751, 9747, 9749, 9748, 9752, 9754, 9753, 9755, 9766, - 9757, 9756, 9765, 9763, 9758, 9759, 9760, 9762, 9761, 9764, 9770, 9767, - 9769, 9768, 9771, 9772, 9773, 9774, 9777, 9778, 9780, 9781, 9782, 9783, - 9785, 9784, 9786, 9793, 9787, 9788, 9794, 9789, 9790, 9791, 9792, 9795, - 9799, 9796, 9797, 9798, 9800, 9801, 9802, 9803, 9809, 9807, 9805, 9806, - 9804, 9808, 9810, 9812, 9829, 9830, 9813, 9818, 9820, 9814, 9817, 9815, - 9816, 9821, 9827, 9828, 9822, 9825, 9826, 9831, 9837, 9836, 9832, 9834, - 9833, 9918, 9919, 9838, 9839, 9844, 9845, 9842, 9843, 9846, 9840, 9841, - 9847, 9850, 9851, 9853, 9854, 9855, 9859, 9856, 9857, 9858, 9848, 9849, - 9860, 9862, 9861, 9863, 9865, 9866, 9867, 9873, 9872, 9868, 9869, 9870, - 9905, 9874, 9875, 9876, 9877, 9878, 9897, 9880, 9881, 9883, 9882, 9884, - 9885, 9901, 9886, 9888, 9887, 9900, 9890, 9898, 9902, 9893, 9892, 9899, - 9894, 9896, 9895, 9903, 9904, 9906, 9910, 9908, 9909, 9907, 9913, 9912, - 9911, 9917, 9914, 9915, 9916, 9920, 9922, 9921, 9925, 9923, 9940, 9926, - 9929, 9931, 9927, 9928, 9932, 9930, 9933, 9935, 9937, 9938, 9939, 9343, - 8839, 8850, 8868, 8934, 8943, 8959, 8967, 9059, 9052, 9070, 9072, 9162, - 9186, 9232, 9261, 9263, 9277, 9283, 9318, 9291, 9321, 9300, 9309, 9313, - 9384, 9513, 9516, 9531, 9563, 9581, 9598, 9607, 9635, 9633, 9612, 9643, - 9672, 9689, 9699, 9819, 9852, 9835, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 8804, 8799, 8736, 8722, - 8783, 8779, 8711, 8756, 8803, 8744, 8728, 8789, 8778, 8710, 8755, 8742, - 8726, 8785, 8775, 8705, 8752, 8765, 8809, 8801, 8738, 8724, 8787, 8777, - 8707, 8754, 8766, 8810, 8802, 8739, 8725, 8811, 8793, 8794, 8740, 8716, - 8782, 8774, 8704, 8751, 8769, 8812, 8795, 8796, 8741, 8717, 8780, 8781, - 8764, 8807, 8790, 8791, 8731, 8721, 8797, 8798, 8732, 8735, 8733, 8734, - 8788, 8773, 8771, 8772, 8708, 8709, 8747, 8749, 8750, 8748, 8805, 8800, - 8737, 8723, 8784, 8763, 8806, 8792, 8729, 8730, 8719, 8720, 8746, 8745, - 8759, 8808, 8768, 8814, 8718, 8767, 8813, 8760, 8761, 8757, 8758, 8762, - 8770, 8712, 8715, 8714, 8713, 8743, 8727, 8786, 8776, 8706, 8753, 40951, - 8819, 8818, 8817, 8815, 8816, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 8840, 8834, 8852, 8892, 8894, 8895, - 8896, 8908, 8947, 8942, 8952, 8951, 8955, 8972, 8970, 8971, 8973, 8974, - 8992, 8978, 8975, 8976, 8977, 8994, 8995, 8993, 8982, 8981, 8979, 8980, - 8985, 8983, 8984, 8986, 8987, 8988, 8989, 8996, 8997, 8991, 8990, 9015, - 9014, 9026, 9029, 9034, 9038, 9031, 9035, 9036, 9032, 9033, 9037, 9053, - 9075, 9082, 9083, 9084, 9091, 9092, 9095, 9094, 9102, 9113, 9116, 9117, - 9118, 9120, 9122, 9123, 9124, 9130, 9138, 9142, 9161, 9179, 9191, 9196, - 9210, 9215, 9268, 9288, 9311, 9312, 9419, 9439, 9429, 9430, 9438, 9434, - 9435, 9436, 9433, 9432, 9431, 9437, 9441, 9440, 9447, 9448, 9442, 9443, - 9444, 9449, 9445, 9446, 9450, 9451, 9452, 9453, 9454, 9455, 9462, 9456, - 9461, 9460, 9458, 9457, 9459, 9463, 9464, 9469, 9470, 9465, 9466, 9467, - 9468, 9496, 9492, 9471, 9479, 9480, 9478, 9477, 9481, 9472, 9473, 9475, - 9476, 9474, 9493, 9482, 9489, 9491, 9486, 9487, 9490, 9485, 9488, 9484, - 9483, 9494, 9495, 9511, 9537, 9519, 9527, 9528, 9533, 9565, 9572, 9571, - 9632, 9613, 9615, 9634, 9616, 9617, 9619, 9622, 9627, 9628, 9630, 9697, - 9716, 9702, 9705, 9736, 9740, 9743, 9744, 9745, 9776, 9775, 9779, 9811, - 9823, 9824, 9864, 9871, 9879, 9891, 9889, 9924, 9934, 9936, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 10013, 10014, 10015, 10016, 10017, 10018, 10019, 10020, 10023, 10024, - 10021, 10022, 10025, 10026, 10027, 10028, 10029, 10030, 10031, 10032, - 10033, 10034, 10035, 10036, 10037, 10038, 10039, 10040, 10041, 10042, - 10043, 10044, 10045, 10046, 10047, 10048, 10049, 10050, 10051, 10052, - 10053, 10054, 10055, 10056, 10057, 10058, 10059, 10079, 10080, 10081, - 10082, 10083, 10084, 10085, 10086, 10087, 10062, 10063, 10064, 10065, - 10066, 10060, 10061, 10067, 10068, 10069, 10088, 10089, 10090, 10091, - 10092, 10093, 10094, 10095, 10096, 10097, 10070, 10071, 10072, 10073, - 10074, 10075, 10076, 10077, 10078, 10098, 10099, 10100, 10101, 10102, - 10103, 10104, 10105, 10106, 10107, 10108, 10109, 10110, 10111, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 11548, 11549, 11550, 11551, 11546, 11547, 11543, 11544, - 11545, 11552, 11553, 11554, 11559, 11560, 11561, 11562, 11555, 11556, - 11563, 11564, 11557, 11558, 11565, 11566, 11593, 11594, 11595, 11596, - 11597, 11598, 11599, 11600, 11601, 11602, 11583, 11584, 11581, 11582, - 11585, 11586, 11587, 11588, 11589, 11590, 11591, 11567, 11568, 11575, - 11569, 11570, 11571, 11572, 11576, 11573, 11574, 11577, 11578, 11579, - 11580, 11603, 11604, 11605, 11606, 11607, 11608, 11609, 11610, 11611, - 11612, 11613, 11614, 11615, 11616, 11617, 11618, 11619, 11620, 11621, - 11622, 11592, 11662, 11663, 11664, 11665, 11660, 11661, 11666, 11667, - 11668, 11669, 11674, 11670, 11671, 11672, 11673, 11675, 11676, 11677, - 11678, 11679, 11680, 11681, 11682, 11683, 11684, 11685, 11686, 11687, - 11688, 11689, 11690, 11691, 11692, 11693, 11694, 11695, 11696, 11697, - 11700, 11701, 11702, 11703, 11704, 11705, 11706, 11698, 11699, 11707, - 11780, 11781, 11782, 11783, 11784, 11785, 11786, 11787, 11788, 11789, - 11771, 11772, 11773, 11774, 11775, 11776, 11777, 11769, 11770, 11778, - 11779, 11712, 11708, 11709, 11713, 11714, 11710, 11711, 11715, 11716, - 11717, 11718, 11719, 11724, 11725, 11726, 11727, 11728, 11729, 11720, - 11721, 11730, 11722, 11723, 11731, 11732, 11733, 11734, 11735, 11736, - 11737, 11738, 11739, 11740, 11741, 11746, 11742, 11743, 11747, 11744, - 11745, 11748, 11749, 11750, 11751, 11752, 11762, 11763, 11764, 11765, - 11766, 11767, 11768, 11753, 11754, 11755, 11756, 11757, 11758, 11759, - 11760, 11761, 11794, 11795, 11796, 11797, 11798, 11799, 11800, 11790, - 11791, 11792, 11793, 11826, 11827, 11828, 11829, 11830, 11831, 11822, - 11823, 11824, 11825, 11832, 11833, 11801, 11802, 11805, 11806, 11807, - 11808, 11809, 11810, 11811, 11803, 11804, 11812, 11815, 11816, 11817, - 11818, 11813, 11814, 11819, 11820, 11821, 11837, 11838, 11839, 11840, - 11841, 11842, 11843, 11844, 11845, 11846, 11849, 11850, 11851, 11847, - 11848, 11852, 11853, 11854, 11855, 11856, 11857, 11893, 11891, 11892, - 11894, 11895, 11896, 11897, 11898, 11899, 11900, 11901, 11864, 11858, - 11859, 11865, 11866, 11867, 11868, 11869, 11860, 11861, 11862, 11863, - 11870, 11877, 11878, 11879, 11880, 11881, 11871, 11872, 11873, 11874, - 11875, 11876, 11882, 11883, 11888, 11884, 11885, 11886, 11887, 11889, - 11890, 11908, 11909, 11910, 11911, 11912, 11906, 11907, 11903, 11904, - 11905, 11913, 11914, 11917, 11915, 11916, 11918, 11919, 11920, 11921, - 11922, 11923, 11924, 11925, 11950, 11951, 11954, 11955, 11956, 11957, - 11958, 11952, 11953, 11959, 11960, 11961, 11930, 11931, 11932, 11933, - 11934, 11935, 11926, 11927, 11928, 11929, 11936, 11937, 11942, 11943, - 11944, 11938, 11939, 11945, 11940, 11941, 11946, 11947, 11948, 11949, - 11962, 11963, 11964, 11965, 11966, 11969, 11970, 11971, 11972, 11973, - 11967, 11968, 11974, 11975, 11983, 11984, 11985, 11986, 11979, 11980, - 11987, 11988, 11989, 11981, 11982, 11990, 11991, 11992, 11993, 11994, - 11995, 11996, 11997, 12638, 12639, 12640, 12641, 12642, 12643, 12644, - 12645, 12009, 12005, 12006, 12010, 12011, 12012, 12007, 12008, 12013, - 12014, 12016, 12017, 12018, 12021, 12019, 12020, 12022, 12023, 12024, - 12025, 12026, 12027, 12037, 12038, 12045, 12028, 12029, 12030, 12031, - 12032, 12033, 12034, 12035, 12036, 12046, 12047, 12039, 12040, 12041, - 12042, 12043, 12044, 12048, 12049, 12056, 12057, 12050, 12051, 12058, - 12052, 12053, 12059, 12060, 12061, 12054, 12055, 12062, 12068, 12066, - 12067, 12069, 12063, 12064, 12065, 12070, 12071, 12072, 12073, 12074, - 12075, 12076, 12077, 12078, 12079, 12080, 12081, 12138, 12139, 12140, - 12141, 12142, 12143, 12144, 12145, 12146, 12101, 12102, 12103, 12104, - 12105, 12106, 12107, 12108, 12098, 12099, 12100, 12109, 12126, 12127, - 12128, 12129, 12130, 12124, 12125, 12131, 12132, 12133, 12134, 12118, - 12119, 12120, 12110, 12111, 12112, 12113, 12114, 12115, 12121, 12116, - 12117, 12122, 12123, 12135, 12136, 12137, 12149, 12150, 12151, 12152, - 12147, 12148, 12153, 12154, 12155, 12156, 12159, 12160, 12161, 12162, - 12163, 12164, 12165, 12157, 12158, 12166, 12167, 12168, 12186, 12187, - 12188, 12189, 12190, 12191, 12192, 12193, 12194, 12169, 12170, 12171, - 12172, 12175, 12176, 12177, 12178, 12179, 12180, 12173, 12174, 12181, - 12184, 12185, 12182, 12183, 12202, 12203, 12206, 12207, 12208, 12204, - 12205, 12195, 12196, 12197, 12198, 12199, 12200, 12201, 12209, 12210, - 12211, 12212, 12213, 12214, 12215, 12218, 12219, 12220, 12221, 12222, - 12223, 12224, 12225, 12216, 12217, 12226, 12227, 12234, 12235, 12236, - 12228, 12229, 12230, 12231, 12237, 12238, 12239, 12232, 12233, 12245, - 12246, 12249, 12250, 12247, 12248, 12251, 12252, 12240, 12241, 12242, - 12243, 12244, 12253, 12254, 12255, 12260, 12261, 12262, 12263, 12264, - 12265, 12266, 12267, 12268, 12269, 12256, 12257, 12258, 12259, 12271, - 12272, 12275, 12273, 12274, 12276, 12277, 12278, 12279, 12280, 12281, - 12282, 12283, 12646, 12647, 12648, 12649, 12650, 12651, 12652, 12289, - 12287, 12288, 12284, 12285, 12286, 12290, 12291, 12292, 12293, 12294, - 12295, 12296, 12297, 12300, 12301, 12302, 12303, 12304, 12298, 12299, - 12305, 12306, 12307, 12308, 12309, 12310, 12311, 12312, 12313, 12314, - 12315, 12316, 12317, 12322, 12318, 12319, 12323, 12324, 12325, 12320, - 12321, 12326, 12327, 12328, 12334, 12335, 12336, 12337, 12329, 12330, - 12331, 12338, 12339, 12332, 12333, 12340, 12341, 12345, 12346, 12347, - 12348, 12349, 12350, 12342, 12343, 12344, 12351, 12352, 12353, 12356, - 12357, 12358, 12359, 12360, 12354, 12355, 12361, 12362, 12363, 12364, - 12365, 12366, 12367, 12368, 12369, 12370, 12371, 12380, 12381, 12372, - 12373, 12382, 12383, 12384, 12374, 12375, 12376, 12377, 12378, 12379, - 12389, 12385, 12386, 12390, 12391, 12392, 12393, 12387, 12388, 12394, - 12395, 12396, 12406, 12407, 12408, 12409, 12410, 12411, 12412, 12413, - 12414, 12415, 12401, 12402, 12397, 12398, 12399, 12400, 12403, 12404, - 12405, 12420, 12421, 12422, 12423, 12424, 12417, 12418, 12419, 12425, - 12426, 12427, 12454, 12455, 12456, 12457, 12458, 12459, 12460, 12461, - 12462, 12463, 12432, 12433, 12434, 12428, 12429, 12435, 12436, 12437, - 12438, 12439, 12430, 12431, 12442, 12443, 12440, 12441, 12444, 12445, - 12446, 12447, 12448, 12449, 12450, 12451, 12452, 12453, 12467, 12468, - 12469, 12470, 12471, 12472, 12473, 12474, 12475, 12476, 12477, 12478, - 12479, 12480, 12481, 12482, 12464, 12465, 12466, 12483, 12484, 12493, - 12485, 12486, 12487, 12488, 12490, 12491, 12492, 12494, 12495, 12496, - 12497, 12498, 12499, 12500, 12501, 12502, 12503, 12504, 12505, 12506, - 12507, 12508, 12509, 12510, 12511, 12512, 12513, 12520, 12521, 12514, - 12515, 12522, 12523, 12524, 12525, 12516, 12517, 12518, 12519, 12526, - 12527, 12528, 12529, 12534, 12530, 12531, 12535, 12536, 12537, 12532, - 12533, 12538, 12539, 12540, 12541, 12547, 12548, 12543, 12544, 12549, - 12550, 12551, 12552, 12553, 12545, 12546, 12554, 12555, 12562, 12563, - 12564, 12556, 12557, 12565, 12566, 12558, 12559, 12560, 12561, 12567, - 12570, 12571, 12572, 12573, 12568, 12569, 12574, 12583, 12584, 12585, - 12576, 12577, 12578, 12586, 12579, 12580, 12587, 12581, 12582, 12588, - 12589, 12590, 12591, 12592, 12593, 12594, 12595, 12596, 12609, 12597, - 12598, 12599, 12600, 12601, 12602, 12603, 12604, 12605, 12606, 12607, - 12608, 12610, 12611, 12612, 12613, 12633, 12634, 12635, 12636, 12637, - 12614, 12615, 12616, 12617, 12618, 12619, 12620, 12621, 12622, 12623, - 12624, 12625, 12626, 12627, 12628, 12629, 12630, 12631, 12632, 11626, - 11627, 11628, 11629, 11630, 11631, 11623, 11624, 11625, 11632, 11633, - 11637, 11638, 11639, 11640, 11641, 11642, 11643, 11644, 11645, 11646, - 11647, 11648, 11649, 11650, 11651, 11652, 11653, 11654, 11655, 11656, - 11634, 11635, 11636, 12489, 12542, 11978, 12003, 12000, 12002, 11999, - 12270, 11659, 11836, 12004, 12001, 11998, 11658, 11835, 11657, 11834, - 12097, 11902, 11976, 12015, 11977, 12416, 12575, 12093, 12084, 12088, - 12095, 12091, 12085, 12090, 12087, 12094, 12083, 12089, 12096, 12092, - 12086, 12082, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 12653, 12654, 12655, 12656, 12657, 12658, 12659, 12660, - 12661, 12662, 12663, 12664, 12665, 12666, 12667, 12668, 12669, 12670, - 12671, 12672, 12673, 12674, 12675, 12676, 12677, 12678, 12679, 12680, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 28435, 12481, 26055, 24148, 10207, 25626, 371, 1054, 18841, + 26279, 2570, 25877, 7571, 15612, 12664, 28274, 9990, 9975, 9987, 9984, + 9969, 9966, 9981, 9978, 9993, 9972, 7147, 26889, 19084, 11917, 13295, + 26053, 7570, 17426, 17472, 17482, 17498, 17515, 17560, 17564, 17581, + 17595, 17624, 17629, 17641, 17661, 17670, 17686, 17736, 17745, 17748, + 17769, 17793, 17822, 17861, 17872, 17881, 17884, 17898, 18803, 26180, + 26293, 6924, 19857, 13266, 17990, 18040, 18060, 18083, 18119, 18178, + 18186, 18204, 18223, 18260, 18266, 18280, 18319, 18332, 18356, 18413, + 18425, 18431, 18469, 18511, 18584, 18632, 18646, 18655, 18663, 18679, + 18744, 33411, 26242, 31817, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 24142, 15966, 6233, + 25919, 9069, 33983, 5039, 26871, 9957, 7765, 12560, 18851, 24122, 28225, + 26103, 21543, 9672, 25889, 29061, 29060, 4, 22274, 25774, 22280, 6229, + 29064, 20510, 26340, 33545, 33543, 33551, 15969, 17455, 17456, 17444, + 17458, 17437, 17439, 17460, 17483, 17546, 17547, 17518, 17533, 17611, + 17612, 17601, 17599, 17557, 17684, 17725, 17726, 17693, 17711, 17700, + 22987, 17709, 17853, 17854, 17823, 17826, 17893, 17814, 18501, 18021, + 18022, 18010, 18024, 18001, 18003, 18027, 18061, 18152, 18153, 18122, + 18137, 18241, 18242, 18224, 18226, 18177, 18351, 18395, 18396, 18357, + 18381, 18364, 10118, 18379, 18619, 18622, 18587, 18590, 18674, 18533, + 18665, 17442, 18008, 17427, 17991, 17452, 18018, 17487, 18066, 17485, + 18063, 17491, 18071, 17486, 18065, 17505, 18091, 17502, 18085, 17536, + 18141, 17549, 18155, 17528, 18132, 17543, 18149, 17527, 18131, 17566, + 18188, 17569, 18191, 17573, 18196, 17565, 18187, 17584, 18207, 17591, + 18216, 17605, 18238, 17603, 18233, 17614, 18244, 17608, 18235, 17598, + 18107, 17910, 18699, 17625, 18261, 17630, 18267, 18279, 17655, 18297, + 17651, 18284, 17653, 18287, 17648, 18294, 17650, 18305, 17679, 18344, + 17671, 18333, 17673, 18337, 18352, 17559, 18160, 17689, 18374, 17728, + 18398, 17706, 18370, 17911, 18702, 17757, 18448, 17749, 18432, 17750, + 18434, 17775, 18470, 17774, 18476, 17772, 18474, 17770, 18472, 17794, + 18512, 17797, 18516, 17804, 18528, 17850, 18616, 17842, 18606, 17856, + 18624, 17857, 18615, 17833, 18597, 17847, 18611, 17873, 18647, 17885, + 18664, 17886, 17901, 18680, 17904, 18686, 17900, 18683, 18311, 18049, + 17478, 17473, 18041, 17817, 18536, 17731, 17489, 18069, 17464, 17510, + 17506, 18086, 18541, 17766, 17792, 17730, 17561, 18181, 17572, 17579, + 18222, 17622, 17615, 17640, 18278, 18283, 18307, 17811, 17677, 18338, + 17692, 17718, 18388, 17733, 18410, 17742, 18421, 17987, 17819, 18538, + 17558, 17928, 18524, 17803, 18525, 17802, 17835, 18599, 17860, 17865, + 17890, 18669, 17907, 18689, 17551, 17553, 18170, 18175, 17980, 17818, + 18537, 17926, 17981, 17982, 17983, 17919, 17930, 17512, 17500, 18113, + 17660, 17649, 18318, 17685, 17674, 18355, 17450, 18016, 17602, 18225, + 17699, 18363, 17825, 18589, 17830, 18594, 17829, 18593, 17827, 18591, + 17828, 18592, 18573, 17438, 18002, 17434, 17998, 17462, 18030, 17574, + 18197, 17567, 18189, 17631, 18268, 17707, 18377, 17708, 18378, 17552, + 18172, 18263, 17511, 17499, 18112, 17568, 18190, 17593, 17880, 17678, + 18343, 17440, 18004, 17461, 18029, 17710, 18380, 17436, 18000, 17457, + 18023, 17532, 18136, 17548, 18154, 17596, 18232, 17613, 18243, 17705, + 18369, 17727, 18397, 17754, 18438, 17759, 18449, 17832, 18596, 17855, + 18623, 17773, 18475, 17796, 18514, 17897, 18678, 17585, 18208, 17675, + 18090, 17735, 18412, 17909, 18693, 17433, 17997, 17516, 18120, 17701, + 18365, 17714, 18384, 17702, 18366, 17703, 18367, 17894, 18675, 18286, + 18336, 18515, 18104, 18117, 18430, 17459, 17492, 18072, 17643, 17798, + 18483, 18688, 17575, 18198, 17479, 17859, 17813, 17550, 18157, 17628, + 18265, 17786, 18428, 17761, 18452, 17896, 18673, 18565, 18032, 18566, + 18048, 18401, 18064, 18087, 18095, 18461, 18492, 18496, 18407, 18456, + 18458, 18079, 18105, 18195, 18499, 17957, 18202, 18468, 18542, 18215, + 18221, 18245, 18256, 17935, 18292, 18281, 18300, 18308, 18568, 18569, + 18327, 18340, 18349, 17968, 18052, 17942, 18078, 18424, 18553, 18556, + 18559, 18443, 18445, 18439, 18459, 17944, 17936, 18489, 18163, 18106, + 18509, 18167, 18560, 18527, 18585, 18626, 18639, 18562, 18577, 18570, + 17975, 18692, 18682, 18169, 18171, 17984, 17927, 17924, 17977, 17922, + 17955, 18077, 17958, 17964, 18262, 18575, 17937, 18427, 17985, 17929, + 18114, 18100, 18115, 18580, 18529, 18579, 18184, 18315, 18316, 17921, + 17923, 18543, 18544, 22605, 22606, 22614, 22636, 22663, 22666, 22561, + 22683, 22685, 22534, 22477, 22691, 22387, 22542, 22544, 22520, 22493, + 22540, 22522, 22546, 22693, 22479, 22469, 6162, 22697, 22523, 22386, + 22492, 22518, 22509, 22515, 22514, 22690, 22500, 22421, 22420, 22692, + 22478, 22533, 22531, 5032, 10313, 26472, 24282, 28183, 10372, 22547, + 22470, 22604, 22616, 22643, 22684, 22640, 22484, 22499, 22527, 22512, + 22489, 22699, 22698, 22696, 22694, 22476, 22505, 22517, 22507, 22510, + 22511, 22530, 22529, 22528, 22513, 22539, 22389, 22490, 22390, 22491, + 22549, 22532, 22506, 7365, 7154, 7238, 7540, 7475, 7498, 7164, 7264, + 7260, 7379, 7524, 7274, 7170, 7556, 7296, 7297, 7172, 7382, 7548, 7175, + 7506, 7176, 7366, 7155, 7459, 7519, 7449, 7381, 7456, 7549, 7300, 7503, + 7486, 7502, 7507, 7267, 7261, 7523, 7177, 7240, 7496, 7555, 7167, 7386, + 7171, 7239, 7166, 7383, 7544, 7480, 7474, 7298, 7543, 7528, 7472, 7527, + 7471, 7514, 7384, 7532, 7537, 7568, 7557, 7284, 7367, 7156, 7376, 7375, + 7378, 7377, 7168, 7310, 7295, 7448, 7489, 7380, 7163, 7461, 7551, 7371, + 7510, 7457, 7312, 7567, 7450, 7511, 7509, 7515, 7266, 7160, 7290, 7526, + 7276, 7275, 7281, 7282, 7291, 7277, 7288, 7399, 7407, 7412, 7420, 7423, + 7405, 7437, 7439, 7441, 7426, 7429, 7444, 7445, 13480, 13744, 13375, + 13611, 13562, 13558, 13469, 13726, 35536, 35536, 13801, 13751, 13752, + 13750, 13806, 13483, 35536, 35536, 35536, 35536, 13766, 13496, 13373, + 13349, 13406, 13396, 13420, 35536, 13452, 35536, 13467, 13442, 13658, + 13352, 13479, 13477, 13475, 13397, 13481, 13376, 13473, 13407, 13476, + 13482, 13484, 13485, 13486, 13443, 13472, 13453, 35536, 13455, 13474, + 13458, 13470, 13478, 13471, 13422, 13412, 13463, 13608, 13623, 13648, + 13667, 13678, 13583, 13743, 13741, 13613, 13614, 13745, 13624, 13738, + 13649, 13689, 13746, 13747, 13748, 13749, 13716, 13729, 13730, 13740, + 13736, 13739, 13669, 13727, 13742, 13728, 13691, 13654, 13674, 13725, + 13687, 13715, 13491, 13803, 13762, 13770, 13768, 13769, 13578, 13579, + 13543, 13554, 13610, 13553, 13735, 13556, 13612, 13555, 13690, 13552, + 13733, 7646, 7740, 7624, 7718, 7620, 7714, 7618, 7712, 7616, 7710, 7645, + 7739, 7615, 7709, 13542, 13581, 13559, 13557, 13492, 13560, 13582, 13457, + 13737, 13487, 13456, 13734, 13580, 13489, 13490, 13488, 9336, 9338, 9285, + 9324, 9260, 9289, 9276, 9395, 9408, 9231, 9234, 9248, 9363, 9333, 9376, + 9293, 9261, 9277, 9409, 9318, 9297, 9335, 9402, 9398, 9331, 9374, 9348, + 9299, 9315, 9304, 9367, 9235, 9310, 9313, 9245, 9255, 9317, 9325, 9251, + 9278, 9381, 9379, 9329, 9392, 9384, 9298, 9397, 9389, 9420, 9436, 9610, + 9477, 9456, 9494, 9603, 9599, 9490, 9552, 9507, 9458, 9474, 9463, 9533, + 9538, 9469, 9472, 9570, 9581, 9476, 9484, 9576, 9437, 9559, 9557, 9488, + 9593, 9562, 9457, 9598, 9590, 9495, 9497, 9444, 9483, 9586, 9448, 9435, + 9596, 9609, 9526, 9532, 9573, 9522, 9492, 9554, 9452, 9368, 9534, 9391, + 9592, 9344, 9503, 9230, 9524, 9340, 9499, 9273, 9432, 9341, 9500, 9364, + 9523, 9238, 9542, 9407, 9608, 9346, 9505, 9347, 9506, 9259, 9585, 9239, + 9547, 9369, 9535, 9371, 9537, 9361, 9520, 9641, 7231, 7225, 7228, 7226, + 7227, 7181, 7234, 9375, 9553, 9388, 9566, 9311, 9470, 9320, 9479, 9322, + 9481, 9319, 9478, 9404, 9605, 9400, 9601, 9349, 9508, 9351, 9510, 9352, + 9511, 9271, 9430, 9306, 9465, 9414, 9614, 9236, 9539, 9267, 9426, 9314, + 9473, 9247, 9572, 9386, 9564, 9387, 9565, 9326, 9485, 9413, 9613, 9280, + 9439, 9281, 9440, 9377, 9555, 9264, 9423, 9265, 9424, 9417, 9405, 9606, + 9350, 9509, 9302, 9461, 9309, 9468, 9308, 9467, 9362, 9521, 9316, 9475, + 9541, 9263, 9422, 9262, 9421, 9412, 9612, 9337, 9496, 9372, 9550, 9373, + 9551, 9403, 9604, 9399, 9600, 9266, 9425, 9334, 9493, 9332, 9491, 9370, + 9536, 9269, 9428, 9270, 9429, 9312, 9471, 9258, 9584, 9257, 9583, 9256, + 9582, 9279, 9438, 9321, 9480, 9393, 9594, 9323, 9482, 9327, 9486, 9328, + 9487, 9355, 9514, 9354, 9513, 9360, 9519, 9353, 9512, 9356, 9515, 9357, + 9516, 9358, 9517, 9359, 9518, 9243, 9546, 9303, 9462, 9232, 9527, 9244, + 9549, 9390, 9591, 9411, 9611, 9410, 9589, 9268, 9427, 9300, 9459, 9305, + 9464, 9237, 9540, 9378, 9556, 9307, 9466, 9291, 9450, 9295, 9454, 9301, + 9460, 35536, 2488, 2495, 2479, 2501, 2475, 2499, 2476, 2477, 2466, 2498, + 2494, 2491, 2493, 2471, 2483, 2500, 2481, 2478, 2469, 2496, 2467, 2497, + 2486, 2490, 2470, 2485, 2480, 2474, 2487, 2489, 2465, 2473, 2472, 2468, + 2484, 2482, 2502, 2492, 35536, 35536, 2552, 2464, 2505, 2504, 2503, 2556, + 2463, 2525, 2528, 2538, 2516, 2544, 2512, 2542, 2513, 2514, 2527, 2541, + 2537, 2534, 2536, 2508, 2520, 2543, 2518, 2515, 2506, 2539, 2531, 2540, + 2523, 2530, 2507, 2522, 2517, 2511, 2524, 2529, 2526, 2510, 2509, 2533, + 2521, 2519, 2545, 2535, 2546, 2532, 2555, 2553, 35536, 35536, 26327, + 18864, 2554, 35536, 14954, 14957, 14958, 14965, 14966, 14962, 14969, + 14967, 14952, 14964, 14961, 14945, 14946, 14947, 14955, 14960, 14953, + 14942, 14950, 14951, 14948, 14949, 14944, 14956, 14959, 14963, 14970, + 14971, 14943, 14968, 15049, 15064, 15053, 15051, 15052, 15054, 15066, + 15062, 15057, 15058, 15055, 15056, 15060, 15050, 15068, 15074, 15061, + 15071, 15063, 15065, 15072, 15048, 15047, 15073, 15059, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 14972, 14979, 15022, 15020, + 14997, 15028, 15002, 14999, 15014, 15039, 14988, 14982, 15024, 14994, + 15026, 14993, 15000, 15004, 14978, 14990, 14985, 14992, 15018, 14995, + 15012, 15006, 15016, 35536, 35536, 35536, 35536, 15075, 15043, 15046, + 15044, 15070, 15069, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 2306, 2340, 1099, 2341, 2342, 2305, 2451, + 2452, 2317, 2449, 2450, 2644, 1067, 1080, 2312, 2346, 2339, 2345, 2337, + 2338, 2347, 2365, 2353, 2382, 2349, 2401, 2400, 2333, 1387, 1089, 2439, + 2447, 1342, 1294, 1157, 1147, 1577, 1150, 1593, 1129, 1171, 1519, 1518, + 1541, 1320, 1279, 1361, 1196, 1536, 1440, 1620, 1474, 1487, 1466, 1212, + 1496, 1615, 1119, 1266, 1348, 1345, 1241, 1240, 1239, 2427, 1246, 1431, + 1331, 1366, 1379, 1403, 1296, 1572, 1165, 1584, 1097, 1078, 1109, 1091, + 1074, 1105, 2334, 2404, 2159, 1104, 1103, 2403, 2323, 2160, 2448, 2443, + 2442, 2444, 2318, 1092, 2446, 2459, 2461, 2458, 2457, 2454, 2453, 2456, + 2455, 2462, 2460, 2310, 1085, 2441, 1100, 1224, 1226, 1493, 1162, 1154, + 1153, 1316, 1317, 1319, 1558, 1318, 1546, 1552, 1191, 1527, 1526, 1419, + 1531, 1186, 1289, 1287, 1398, 1227, 1286, 1506, 1513, 1221, 1206, 1203, + 1204, 1209, 1218, 1232, 1199, 1205, 1457, 1443, 1454, 1451, 1444, 1452, + 1447, 1328, 1450, 1475, 1478, 1479, 1469, 1468, 1500, 1122, 1225, 1249, + 1247, 1565, 1251, 1426, 1434, 1435, 1343, 1495, 1337, 1336, 1388, 1334, + 1257, 1260, 1389, 1259, 1273, 1258, 1370, 1368, 1372, 1371, 1414, 1404, + 1460, 1412, 1409, 1307, 1508, 1304, 1297, 1298, 1522, 1581, 1355, 1612, + 1557, 1609, 1358, 1580, 1564, 1235, 1603, 1598, 1574, 1624, 1602, 1585, + 1588, 1101, 1170, 2356, 2355, 2358, 2357, 2384, 2364, 2362, 1090, 2426, + 2381, 2380, 2350, 2359, 2399, 2360, 2402, 2387, 2376, 2378, 2314, 1088, + 1087, 2322, 2398, 1198, 1448, 12501, 12503, 12500, 12499, 12496, 12495, + 12498, 12497, 12504, 12502, 1488, 1213, 1268, 2344, 2343, 1303, 29191, + 29265, 29262, 29263, 29259, 29200, 29187, 29188, 29264, 29261, 29186, + 29196, 29195, 29194, 35536, 29184, 29232, 29228, 29242, 29238, 29239, + 29202, 29201, 29203, 29245, 29243, 29204, 29235, 29236, 29240, 29241, + 29233, 29205, 29217, 29244, 29224, 29231, 29246, 29218, 29222, 29230, + 29234, 29223, 29229, 29237, 29219, 29221, 29220, 29251, 29250, 29249, + 29254, 29253, 29252, 29256, 29255, 29190, 29189, 29199, 29198, 29197, + 29193, 29192, 29257, 29271, 29272, 29258, 29269, 29268, 29267, 29266, + 29248, 29247, 29270, 29185, 35536, 35536, 29225, 29226, 29227, 1177, + 1180, 1175, 1176, 1178, 1179, 1173, 1288, 1285, 1202, 1197, 1445, 1481, + 1124, 1120, 1123, 1252, 1250, 1350, 1346, 1344, 1382, 1381, 1410, 1407, + 1408, 1373, 1446, 1449, 1480, 1284, 1282, 1477, 1441, 1283, 1156, 1155, + 1238, 1237, 1236, 1576, 1575, 1587, 1586, 1280, 1482, 1476, 1333, 31385, + 31398, 31394, 31411, 31410, 31389, 31386, 31374, 31405, 31390, 31379, + 31378, 31401, 31388, 31381, 31382, 31399, 31377, 31407, 31400, 31412, + 31393, 31392, 31391, 31403, 31384, 31387, 31402, 31408, 31397, 31396, + 31376, 31404, 31409, 31375, 31383, 31380, 31406, 31370, 31369, 31416, + 31372, 31417, 31415, 31371, 31373, 31414, 31413, 31418, 31395, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 24006, 24008, 24005, 24004, 24001, 24000, 24003, + 24002, 24009, 24007, 24041, 24028, 24042, 24027, 24043, 24025, 24024, + 24012, 24017, 24030, 24036, 24038, 24016, 24026, 24011, 24023, 24022, + 24037, 24029, 24031, 24033, 24034, 24019, 24035, 24020, 24018, 24032, + 24039, 24040, 24021, 24014, 24013, 24015, 23994, 23995, 23996, 23992, + 23989, 23990, 23991, 23993, 23988, 24045, 24044, 24047, 24046, 23997, + 24048, 24010, 35536, 35536, 23998, 23999, 24049, 26699, 26686, 26700, + 26688, 26691, 26687, 26702, 26690, 26697, 26706, 26692, 26693, 26703, + 26705, 26694, 26689, 26707, 26698, 26704, 26701, 26695, 26696, 26709, + 26710, 26713, 26708, 26714, 26711, 26733, 26743, 26738, 26732, 26742, + 26737, 26731, 26741, 26715, 26739, 26735, 26745, 26716, 26734, 26744, + 26736, 26740, 26712, 35536, 35536, 26723, 26717, 26719, 26722, 26720, + 26724, 26746, 26729, 26727, 26730, 26726, 26728, 26721, 26725, 26718, + 35536, 20299, 20286, 20288, 20287, 20289, 20298, 20296, 20301, 20281, + 20279, 20278, 20290, 20291, 20292, 20282, 20300, 20293, 20284, 20294, + 20295, 20283, 20280, 20297, 20302, 20285, 20277, 20303, 20304, 35536, + 35536, 20305, 35536, 29210, 29215, 29211, 29213, 29209, 29208, 29212, + 29216, 29207, 29206, 29214, 35536, 35536, 35536, 35536, 35536, 1143, + 1133, 1144, 1160, 1142, 1130, 1139, 1136, 1140, 1138, 1161, 1135, 1146, + 1132, 1134, 1145, 1131, 1137, 1141, 2428, 2429, 2431, 1539, 1064, 2316, + 1411, 1281, 1501, 1499, 1347, 2445, 1413, 2313, 2315, 35536, 35536, + 35536, 35536, 35536, 2311, 2366, 2392, 2391, 2394, 2158, 2408, 1084, + 1102, 1174, 1181, 1321, 1498, 1248, 1432, 1367, 1380, 1599, 1601, 1453, + 1573, 1465, 1378, 1200, 1467, 1261, 1494, 1623, 1121, 1335, 1433, 1172, + 1420, 1524, 1442, 1600, 1117, 1115, 1118, 1421, 1525, 1547, 1507, 1349, + 1267, 1116, 1323, 1322, 1369, 1278, 2348, 2351, 2377, 2372, 2383, 1113, + 1112, 2407, 1114, 1111, 2397, 2367, 2363, 2386, 2385, 2379, 2390, 2368, + 2370, 2369, 2371, 2374, 2373, 2352, 2361, 1086, 2440, 1070, 1068, 1072, + 1071, 1069, 1073, 2434, 2436, 2438, 2433, 2435, 2437, 2309, 2308, 2307, + 2375, 1094, 1093, 1106, 1630, 2319, 1629, 2321, 1081, 1082, 2320, 1077, + 2161, 9891, 9881, 9895, 9900, 9816, 9790, 9791, 9860, 9861, 9836, 9837, + 9855, 9857, 9798, 9817, 9868, 9792, 9799, 9818, 9831, 9793, 9827, 9826, + 9811, 9809, 9842, 9796, 9800, 9847, 9845, 9843, 9852, 9851, 9804, 9803, + 9841, 9854, 9853, 9806, 9805, 9844, 9840, 9863, 9862, 9824, 9823, 9814, + 9835, 9830, 9829, 9850, 9849, 9848, 9859, 9819, 9820, 9821, 9813, 9913, + 9912, 9893, 9894, 9903, 9925, 9926, 9915, 9916, 9921, 9922, 9909, 9919, + 9927, 9904, 9910, 9920, 9911, 9905, 9899, 9914, 9906, 9941, 9902, 9901, + 9785, 9782, 9908, 9918, 9917, 9867, 9825, 9808, 9865, 9801, 9828, 9866, + 9834, 9856, 9858, 9923, 9924, 9929, 9928, 9936, 9938, 9935, 9934, 9931, + 9930, 9933, 9932, 9939, 9937, 9783, 9898, 9797, 9833, 9832, 9794, 9839, + 9838, 9815, 9864, 9812, 9810, 9846, 9807, 9802, 9822, 3599, 3667, 3670, + 3672, 35536, 3623, 3624, 3637, 3638, 3635, 3636, 3617, 3619, 35536, + 35536, 3659, 3625, 35536, 35536, 3660, 3626, 3610, 3607, 3651, 3650, + 3639, 3649, 3648, 3653, 3652, 3641, 3632, 3631, 3628, 3627, 3640, 3634, + 3633, 3630, 3629, 3642, 35536, 3655, 3654, 3647, 3646, 3658, 3622, 3611, + 35536, 3657, 35536, 35536, 35536, 3643, 3644, 3645, 3656, 35536, 35536, + 3668, 3669, 3674, 3683, 3684, 3677, 3678, 3679, 3680, 35536, 35536, 3685, + 3675, 35536, 35536, 3686, 3676, 3671, 3608, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 3600, 35536, 35536, 35536, 35536, 3615, 3614, + 35536, 3621, 3618, 3620, 3681, 3682, 35536, 35536, 3693, 3695, 3692, + 3691, 3688, 3687, 3690, 3689, 3696, 3694, 3613, 3612, 3662, 3661, 3601, + 3605, 3604, 3603, 3602, 3606, 3673, 3697, 3616, 3598, 3666, 35536, 35536, + 14035, 14036, 14041, 35536, 13987, 13988, 14003, 14004, 14001, 14002, + 35536, 35536, 35536, 35536, 14022, 13989, 35536, 35536, 14021, 13990, + 13986, 13985, 13983, 13982, 14007, 14014, 14013, 14016, 14015, 14009, + 13998, 13997, 13992, 13991, 14008, 14000, 13999, 13994, 13993, 14010, + 35536, 14018, 14017, 14012, 14011, 14025, 14027, 13996, 35536, 14006, + 14005, 35536, 14026, 14019, 35536, 14020, 14024, 35536, 35536, 14038, + 35536, 14042, 14047, 14048, 14045, 14046, 35536, 35536, 35536, 35536, + 14050, 14043, 35536, 35536, 14049, 14044, 14040, 35536, 35536, 35536, + 14037, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 13984, 13981, + 14028, 13995, 35536, 14023, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 14060, 14062, 14059, 14058, 14055, 14054, 14057, 14056, 14063, + 14061, 14051, 13980, 14052, 14064, 14053, 14039, 13979, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 13875, 13883, + 13885, 35536, 13825, 13826, 13844, 13845, 13842, 13843, 13837, 13839, + 13901, 35536, 13872, 13827, 13902, 35536, 13873, 13828, 13865, 13864, + 13861, 13860, 13849, 13859, 13858, 13863, 13862, 13851, 13834, 13833, + 13830, 13829, 13850, 13836, 13835, 13832, 13831, 13852, 35536, 13867, + 13866, 13857, 13856, 13869, 13871, 13870, 35536, 13847, 13846, 35536, + 13841, 13853, 13854, 13855, 13868, 35536, 35536, 13881, 13882, 13888, + 13897, 13898, 13891, 13892, 13893, 13894, 13886, 35536, 13899, 13889, + 13887, 35536, 13900, 13890, 13884, 35536, 35536, 13915, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 13838, 13840, 13895, 13896, 35536, 35536, 13911, + 13913, 13910, 13909, 13906, 13905, 13908, 13907, 13914, 13912, 13903, + 13904, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 13848, 13880, + 13879, 13876, 13878, 13874, 13877, 35536, 24926, 24929, 24932, 35536, + 24877, 24878, 24896, 24897, 24894, 24895, 24889, 24891, 35536, 35536, + 24922, 24879, 35536, 35536, 24923, 24880, 24916, 24915, 24912, 24911, + 24900, 24910, 24909, 24914, 24913, 24902, 24886, 24885, 24882, 24881, + 24901, 24888, 24887, 24884, 24883, 24903, 35536, 24918, 24917, 24908, + 24907, 24920, 24876, 24874, 35536, 24899, 24898, 35536, 24893, 24904, + 24905, 24906, 24919, 35536, 35536, 24927, 24928, 24933, 24942, 24943, + 24936, 24937, 24938, 24939, 35536, 35536, 24944, 24934, 35536, 35536, + 24945, 24935, 24931, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 24930, 24863, 24864, 35536, 35536, 35536, 35536, 24873, 24872, 35536, + 24875, 24890, 24892, 24940, 24941, 35536, 35536, 24952, 24954, 24951, + 24950, 24947, 24946, 24949, 24948, 24955, 24953, 24871, 24921, 24868, + 24867, 24870, 24865, 24866, 24869, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 29884, 29901, 35536, 29847, 29848, + 29860, 29861, 29856, 29857, 35536, 35536, 35536, 29865, 29866, 29849, + 35536, 29858, 29859, 29850, 29870, 35536, 35536, 35536, 29842, 29867, + 35536, 29869, 35536, 29843, 29845, 35536, 35536, 35536, 29841, 29846, + 35536, 35536, 35536, 29844, 29840, 29872, 35536, 35536, 35536, 29871, + 29874, 29855, 29854, 29853, 29852, 29851, 29873, 29862, 29863, 29864, + 29868, 35536, 35536, 35536, 35536, 30174, 30181, 30182, 30177, 30178, + 35536, 35536, 35536, 30183, 30184, 30175, 35536, 30179, 30180, 30176, + 29900, 35536, 35536, 30187, 35536, 35536, 35536, 35536, 35536, 35536, + 29776, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 29813, 29815, 29812, 29811, 29808, + 29807, 29810, 29809, 29816, 29814, 29878, 29876, 29877, 29806, 30186, + 30185, 29805, 29803, 29775, 29881, 29879, 35536, 35536, 35536, 35536, + 35536, 31250, 31251, 31256, 31258, 31249, 31210, 31211, 31226, 31227, + 31222, 31223, 31217, 31219, 35536, 31243, 31244, 31212, 35536, 31224, + 31225, 31213, 31240, 31239, 31236, 31235, 31199, 31234, 31233, 31238, + 31237, 31201, 31206, 31205, 31193, 31192, 31200, 31209, 31207, 31196, + 31194, 31197, 35536, 31242, 31241, 31232, 31231, 31246, 31247, 31204, + 31203, 31216, 31215, 31214, 31221, 31228, 31229, 31230, 31245, 35536, + 35536, 31254, 31255, 31259, 31270, 31271, 31262, 31263, 31264, 31265, + 35536, 31272, 31273, 31260, 35536, 31268, 31269, 31261, 31257, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 31248, 31184, 35536, 31208, + 31195, 31202, 35536, 31183, 31198, 35536, 35536, 31218, 31220, 31266, + 31267, 35536, 35536, 31280, 31282, 31279, 31278, 31275, 31274, 31277, + 31276, 31283, 31281, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 31252, 31191, 31189, 31187, 31185, 31190, 31188, 31186, 31253, 16427, + 16426, 16431, 16435, 16428, 16375, 16376, 16401, 16402, 16397, 16398, + 16392, 16394, 35536, 16418, 16419, 16377, 35536, 16399, 16400, 16378, + 16415, 16414, 16411, 16410, 16372, 16409, 16408, 16413, 16412, 16374, + 16389, 16388, 16380, 16379, 16373, 16391, 16390, 16382, 16381, 16370, + 35536, 16417, 16416, 16407, 16406, 16422, 16423, 16387, 16386, 16385, + 16384, 35536, 16396, 16403, 16404, 16405, 16421, 35536, 35536, 16429, + 16430, 16438, 16449, 16450, 16441, 16442, 16443, 16444, 35536, 16451, + 16452, 16439, 35536, 16447, 16448, 16440, 16434, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 16424, 16437, 35536, 35536, 35536, 35536, + 35536, 16436, 16371, 16420, 35536, 16393, 16395, 16445, 16446, 35536, + 35536, 16459, 16461, 16458, 16457, 16454, 16453, 16456, 16455, 16462, + 16460, 35536, 16432, 16433, 16425, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 20239, 20241, 20246, + 20244, 20196, 20170, 20172, 20216, 20217, 20212, 20213, 20197, 20199, + 35536, 20231, 20232, 20173, 35536, 20214, 20215, 20174, 20228, 20227, + 20224, 20223, 20204, 20185, 20184, 20226, 20225, 20205, 20193, 20191, + 20188, 20187, 20203, 20195, 20194, 20190, 20189, 20206, 20202, 20230, + 20229, 20222, 20221, 20234, 20235, 20211, 20210, 20209, 20208, 20207, + 20201, 20218, 20219, 20220, 20233, 20192, 20242, 20240, 20245, 20248, + 20259, 20260, 20251, 20252, 20253, 20254, 35536, 20261, 20262, 20249, + 35536, 20257, 20258, 20250, 20243, 20186, 20247, 35536, 35536, 35536, + 35536, 20182, 20183, 20177, 20263, 20162, 20160, 20167, 20157, 20158, + 20168, 20161, 20171, 20198, 20200, 20255, 20256, 35536, 35536, 20153, + 20155, 20152, 20151, 20148, 20147, 20150, 20149, 20156, 20154, 20238, + 20236, 20237, 20165, 20164, 20169, 20159, 20163, 20166, 20146, 20179, + 20178, 20180, 20175, 20176, 20181, 35536, 28083, 28082, 28084, 35536, + 28028, 28025, 28013, 28012, 28036, 28035, 28038, 28037, 28034, 28033, + 28032, 28031, 28030, 28029, 28026, 28057, 28056, 28027, 35536, 35536, + 35536, 28022, 28047, 28020, 28045, 28070, 28060, 28019, 28044, 28021, + 28046, 28067, 28068, 28061, 28014, 28039, 28016, 28041, 28051, 28058, + 28015, 28040, 28017, 28042, 28054, 35536, 28059, 28023, 28048, 28018, + 28043, 28049, 28024, 28066, 28064, 35536, 28053, 35536, 35536, 28065, + 28069, 28052, 28055, 28063, 28050, 28062, 35536, 35536, 35536, 28081, + 35536, 35536, 35536, 35536, 28101, 28093, 28088, 28094, 28089, 28095, + 35536, 28090, 35536, 28091, 28096, 28087, 28100, 28097, 28098, 28099, + 28092, 35536, 35536, 35536, 35536, 35536, 35536, 28077, 28079, 28076, + 28075, 28072, 28071, 28074, 28073, 28080, 28078, 35536, 35536, 28085, + 28086, 28102, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 31437, 31434, 31432, 31431, 31433, 31435, + 31451, 31420, 31422, 31421, 31480, 31423, 31492, 31424, 31489, 31485, + 31482, 31483, 31453, 31425, 31488, 31487, 31484, 31486, 31454, 31419, + 31460, 31457, 31426, 31458, 31427, 31459, 31449, 31493, 31461, 31462, + 31439, 31441, 31490, 31478, 31477, 31479, 31430, 31438, 31494, 31429, + 31455, 31463, 31443, 31466, 31468, 31473, 31474, 31470, 31471, 31469, + 31472, 31456, 35536, 35536, 35536, 35536, 31495, 31475, 31467, 31476, + 31465, 31464, 31440, 31448, 31447, 31446, 31444, 31445, 31442, 31481, + 31452, 31491, 31428, 31502, 31504, 31501, 31500, 31497, 31496, 31499, + 31498, 31505, 31503, 31450, 31436, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 17263, 17261, 35536, 17262, 35536, 17276, 17291, 17295, + 17275, 17285, 35536, 17277, 17292, 17273, 17271, 17270, 17268, 17267, + 17272, 17296, 17288, 17286, 17287, 17269, 17293, 17294, 17281, 17279, + 17258, 17280, 17257, 17274, 17297, 17300, 17265, 35536, 17266, 35536, + 17299, 17282, 17283, 17284, 17289, 17278, 17301, 17290, 17327, 17309, + 17314, 17310, 17312, 17322, 17323, 17316, 17317, 17318, 17319, 17304, + 17315, 17303, 17302, 35536, 35536, 17320, 17321, 17324, 17313, 17311, + 35536, 17325, 35536, 17308, 17305, 17306, 17307, 17338, 17339, 17326, + 35536, 17334, 17336, 17333, 17332, 17329, 17328, 17331, 17330, 17337, + 17335, 35536, 35536, 17254, 17253, 17260, 17259, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 31729, + 31636, 31634, 31635, 31644, 31632, 31629, 31633, 31653, 31624, 31622, + 31645, 31660, 31654, 31650, 31657, 31649, 31652, 31651, 31628, 31637, + 31620, 31619, 31547, 31548, 31546, 31668, 31669, 31670, 31672, 31673, + 31671, 31569, 31571, 31568, 31567, 31564, 31563, 31566, 31565, 31572, + 31570, 31561, 31558, 31557, 31554, 31553, 31556, 31555, 31562, 31560, + 31559, 31625, 31647, 31627, 31646, 31630, 31656, 31638, 31639, 31640, + 31641, 31679, 31665, 31578, 31576, 31606, 31605, 31588, 31604, 31603, + 31613, 35536, 31590, 31598, 31597, 31583, 31582, 31589, 31600, 31599, + 31587, 31586, 31591, 31608, 31607, 31602, 31601, 31615, 31596, 31595, + 31585, 31584, 31616, 31609, 31610, 31611, 31617, 31580, 31614, 31592, + 31593, 31594, 31612, 31618, 31575, 31581, 31577, 31579, 35536, 35536, + 35536, 35536, 31753, 31749, 31750, 31741, 31742, 31743, 31744, 31745, + 31746, 31751, 31752, 31747, 31748, 31676, 31677, 31739, 31740, 31667, + 31681, 31642, 31659, 31663, 31678, 31664, 31666, 31661, 31662, 31680, + 31728, 31727, 31726, 31693, 31692, 31712, 31711, 31694, 31710, 31709, + 31719, 35536, 31696, 31704, 31703, 31686, 31685, 31695, 31706, 31705, + 31690, 31689, 31697, 31714, 31713, 31708, 31707, 31721, 31702, 31701, + 31688, 31687, 31723, 31715, 31716, 31717, 31724, 31722, 31720, 31698, + 31699, 31700, 31718, 31725, 31691, 31683, 31684, 31682, 35536, 31573, + 31574, 31550, 31551, 31552, 31549, 31730, 31737, 31735, 31738, 31736, + 31731, 31734, 31733, 31732, 35536, 31675, 31674, 31623, 31626, 31648, + 31643, 31631, 26332, 18869, 26333, 18870, 31658, 31655, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 23282, 23261, 23260, 23259, 23291, + 23359, 23358, 23361, 23360, 23292, 23289, 23337, 23336, 23343, 23342, + 23290, 23321, 23338, 23345, 23344, 23293, 23363, 23362, 23357, 23356, + 23288, 23365, 23295, 23355, 23341, 23320, 23364, 23354, 23251, 23315, + 23352, 23353, 23346, 23347, 23254, 23287, 23366, 23253, 23458, 23442, + 23465, 23466, 23459, 23460, 23454, 23439, 23447, 23448, 23455, 23383, + 23402, 23407, 23406, 23382, 23246, 23244, 23245, 23243, 23258, 23483, + 23485, 23482, 23481, 23478, 23477, 23480, 23479, 23486, 23484, 23405, + 23394, 23412, 23416, 23411, 23415, 23296, 23319, 23348, 23349, 23350, + 23351, 23461, 23462, 23463, 23464, 23286, 23285, 23283, 23284, 23249, + 23248, 23247, 23318, 23453, 23427, 23428, 23340, 23339, 23456, 23457, + 23397, 23398, 23399, 23400, 23401, 23257, 23256, 23255, 23443, 23445, + 23446, 23444, 23310, 23309, 23308, 23306, 23314, 23298, 23311, 23299, + 23301, 23312, 23304, 23302, 23313, 23250, 23452, 23449, 23450, 23451, + 23389, 23390, 23391, 23392, 23387, 23388, 23386, 23294, 23403, 23378, + 23380, 23377, 23376, 23373, 23372, 23375, 23374, 23381, 23379, 23384, + 23385, 23440, 23441, 23414, 23413, 12876, 12902, 12887, 12908, 12909, + 12907, 12897, 12898, 12910, 12891, 12900, 12903, 12905, 12911, 12893, + 12896, 12901, 12895, 12899, 12912, 12892, 12890, 12886, 12906, 12894, + 12882, 12885, 12889, 12884, 12883, 12904, 12888, 12877, 12881, 12879, + 12914, 12878, 12880, 35536, 12913, 35536, 35536, 35536, 35536, 35536, + 12875, 35536, 35536, 12959, 12990, 12967, 12996, 12965, 12995, 12988, + 12985, 12997, 12977, 12979, 12991, 12993, 12998, 12981, 12987, 12989, + 12983, 12986, 12956, 12980, 12976, 12966, 12994, 12982, 12960, 12963, + 12975, 12962, 12961, 12992, 12974, 12970, 12973, 12971, 13000, 12968, + 12972, 13001, 12999, 12964, 12984, 12958, 13048, 22495, 12957, 12969, + 12978, 14296, 14370, 14304, 14375, 14368, 14332, 14299, 14314, 14372, + 14346, 14364, 14278, 14276, 14362, 14263, 14298, 14382, 14311, 14385, + 14306, 14371, 14308, 14307, 14379, 14342, 14366, 14345, 14291, 14301, + 14295, 14324, 14329, 14328, 14315, 14319, 14317, 14320, 14321, 14318, + 14326, 14325, 14327, 14322, 14293, 14294, 14353, 14359, 14358, 14351, + 14356, 14347, 14348, 14350, 14361, 14355, 14354, 14352, 14357, 14349, + 14360, 14268, 14267, 14273, 14272, 14331, 14288, 14287, 14285, 14280, + 14290, 14281, 14374, 14284, 14283, 14286, 14279, 14383, 14277, 14270, + 14266, 14275, 14271, 14264, 14265, 14269, 14274, 14312, 14292, 14373, + 14384, 14297, 14310, 14305, 14309, 14376, 14387, 14625, 14531, 14541, + 14589, 14593, 14543, 14542, 14595, 14594, 14570, 14622, 14623, 14580, + 14601, 14581, 14621, 14620, 14624, 14610, 14547, 14599, 14554, 14539, + 14540, 14591, 14590, 14545, 14546, 14544, 14597, 14598, 14578, 14577, + 14573, 14571, 14579, 14602, 14603, 14604, 14609, 14608, 14586, 14587, + 14585, 14582, 14588, 14615, 14614, 14613, 14612, 14611, 14619, 14617, + 14553, 14550, 14600, 14555, 14557, 14564, 14568, 14566, 14556, 14532, + 14534, 14537, 14536, 14569, 14538, 14592, 14596, 14575, 14576, 14407, + 14508, 14410, 14430, 14433, 14437, 14512, 14458, 14459, 14464, 14468, + 14477, 14480, 14473, 14485, 14417, 14447, 14450, 14486, 14503, 14399, + 14390, 14393, 14416, 14521, 14443, 14394, 14409, 14411, 14436, 14435, + 14439, 14438, 14434, 14519, 14513, 14461, 14484, 14478, 14479, 14498, + 14465, 14467, 14472, 14471, 14462, 14476, 14474, 14463, 14481, 14427, + 14424, 14418, 14423, 14422, 14420, 14425, 14429, 14406, 14448, 14452, + 14457, 14405, 14488, 14496, 14489, 14492, 14440, 14401, 14402, 14511, + 14400, 14522, 14526, 14529, 14445, 14404, 14397, 14395, 14396, 14398, + 14530, 14413, 14414, 14412, 14408, 14415, 14509, 12146, 12153, 12152, + 12147, 12151, 12150, 12148, 12149, 12373, 12381, 12380, 12374, 12378, + 12377, 12375, 12379, 12139, 12145, 12143, 12140, 12142, 12141, 12144, + 12130, 12190, 12198, 12197, 12191, 12195, 12194, 12192, 12188, 12302, + 12309, 12307, 12303, 12305, 12304, 12308, 12306, 12277, 12286, 12285, + 12278, 12282, 12281, 12279, 12283, 12317, 12323, 12322, 12318, 12292, + 12287, 12319, 12321, 12293, 12301, 12300, 12294, 12298, 12297, 12295, + 12299, 12269, 12276, 12275, 12270, 12274, 12273, 12271, 12272, 12257, + 35536, 12261, 12258, 12260, 12259, 35536, 35536, 12250, 12256, 12254, + 12251, 12253, 12252, 12255, 35536, 12245, 35536, 12249, 12246, 12248, + 12247, 35536, 35536, 11980, 11987, 11986, 11981, 11985, 11984, 11982, + 11971, 12442, 12449, 12447, 12443, 12445, 12444, 12448, 12446, 12355, + 12363, 12362, 12356, 12360, 12359, 12357, 12361, 12018, 12026, 12025, + 12019, 12023, 12022, 12020, 12024, 12410, 12417, 12416, 12411, 12415, + 12414, 12412, 12413, 12398, 35536, 12402, 12399, 12401, 12400, 35536, + 35536, 12208, 12216, 12215, 12209, 12213, 12212, 12210, 12214, 12199, + 12207, 12206, 12200, 12204, 12203, 12201, 12205, 12100, 12108, 12107, + 12101, 12105, 12104, 12102, 12106, 12178, 12185, 12184, 12179, 12183, + 12182, 12180, 12181, 12166, 35536, 12170, 12167, 12169, 12168, 35536, + 35536, 12159, 12165, 12163, 12160, 12162, 12161, 12164, 35536, 12154, + 35536, 12158, 12155, 12157, 12156, 35536, 35536, 12382, 12389, 12388, + 12383, 12387, 12386, 12384, 12385, 12218, 12224, 12222, 12219, 12221, + 12220, 12223, 35536, 12433, 12441, 12440, 12434, 12438, 12437, 12435, + 12439, 12418, 12425, 12423, 12419, 12421, 12420, 12424, 12422, 12390, + 12397, 12396, 12391, 12395, 12394, 12392, 12393, 12048, 12056, 12055, + 12049, 12053, 12052, 12050, 12054, 12033, 12041, 12040, 12034, 12038, + 12037, 12035, 12039, 12364, 12372, 12371, 12365, 12369, 12368, 12366, + 12370, 12121, 12069, 12127, 12122, 12126, 12125, 12123, 12124, 12109, + 35536, 12113, 12110, 12112, 12111, 35536, 35536, 12093, 12099, 12097, + 12094, 12096, 12095, 12098, 12089, 12324, 12332, 12331, 12325, 12329, + 12328, 12326, 12330, 12009, 12017, 12016, 12010, 12014, 12013, 12011, + 12015, 12217, 12232, 12231, 12225, 12229, 12228, 12226, 12230, 12347, + 12354, 12352, 12348, 12350, 12349, 12353, 12351, 12339, 12346, 12345, + 12340, 12344, 12343, 12341, 12342, 12061, 12068, 12066, 12062, 12064, + 12063, 12067, 12059, 12237, 12244, 12243, 12238, 12242, 12241, 12239, + 12235, 12284, 12196, 12065, 35536, 35536, 11949, 11951, 11950, 11968, + 12471, 12469, 11952, 11967, 11953, 11966, 12470, 11965, 12467, 12465, + 12464, 12461, 12460, 12463, 12462, 12468, 12466, 11954, 11957, 11956, + 11961, 11960, 11964, 11963, 11959, 11962, 11958, 11955, 35536, 35536, + 35536, 12290, 12189, 12187, 12186, 12288, 11972, 11970, 11969, 12289, + 12060, 12058, 12057, 12291, 12236, 12234, 12233, 12459, 12450, 12456, + 12457, 12452, 12454, 12458, 12453, 12451, 12455, 35536, 35536, 35536, + 35536, 35536, 35536, 6484, 6485, 6486, 6487, 6488, 6489, 6447, 6483, + 6448, 6449, 6450, 6451, 6452, 6412, 6413, 6414, 6415, 6416, 6417, 6453, + 6454, 6455, 6456, 6457, 6458, 6459, 6460, 6461, 6462, 6463, 6418, 6411, + 6419, 6420, 6421, 6422, 6423, 6424, 6465, 6466, 6467, 6468, 6469, 6470, + 6426, 6425, 6427, 6428, 6429, 6430, 6431, 6405, 6444, 6406, 6445, 6407, + 6446, 6408, 6409, 6410, 6404, 6432, 6433, 6434, 6435, 6436, 6437, 6438, + 6439, 6440, 6441, 6442, 6443, 6471, 6472, 6473, 6474, 6475, 6476, 6477, + 6478, 6479, 6480, 6481, 6482, 6464, 35536, 35536, 6564, 6565, 6566, 6567, + 6568, 6550, 35536, 35536, 5626, 5598, 5371, 5628, 5629, 5778, 5792, 6075, + 5582, 5442, 5369, 5370, 5952, 6042, 6062, 6040, 6063, 6041, 6044, 6038, + 6045, 6039, 5707, 6059, 6036, 6060, 6037, 5708, 5373, 6076, 6094, 5615, + 5614, 5616, 5617, 5609, 5610, 5607, 5606, 5619, 5613, 5618, 5608, 5600, + 5630, 5791, 5377, 5812, 5805, 5810, 5811, 5807, 5808, 6066, 5440, 5441, + 5803, 5804, 5802, 5981, 5800, 5979, 5801, 5980, 5795, 5977, 5796, 5978, + 5798, 5975, 5799, 5976, 6065, 5794, 5974, 5433, 5951, 5934, 5949, 5950, + 5947, 5948, 6073, 5404, 5417, 5932, 5933, 5942, 6034, 5940, 6032, 5941, + 6033, 5938, 6030, 5939, 6031, 5936, 6028, 5937, 6029, 5713, 5894, 5929, + 5930, 5931, 5928, 5649, 5643, 5647, 5648, 5645, 5646, 6068, 5641, 5642, + 5640, 6026, 5638, 6024, 5639, 6025, 5636, 6022, 5637, 6023, 5633, 6020, + 5634, 6021, 5710, 5631, 5632, 5874, 5875, 5876, 5873, 5597, 5584, 5595, + 5596, 5593, 5594, 6067, 5401, 5583, 5591, 6019, 5589, 6017, 5590, 6018, + 5587, 6015, 5588, 6016, 5585, 6013, 5586, 6014, 5709, 5400, 5850, 5675, + 5683, 5692, 5693, 5678, 5679, 6070, 5681, 5682, 5691, 5973, 5689, 5971, + 5690, 5972, 5687, 5969, 5688, 5970, 5685, 5967, 5686, 5968, 5711, 5674, + 5966, 5694, 5375, 5851, 5767, 5728, 5765, 5766, 5755, 5756, 6071, 5699, + 5727, 5764, 5992, 5758, 5990, 5759, 5991, 5712, 5695, 5473, 5768, 5673, + 5660, 5671, 5672, 5669, 5670, 6069, 5658, 5659, 5668, 5960, 5666, 5958, + 5667, 5959, 5664, 5956, 5665, 5957, 5662, 5954, 5663, 5955, 5650, 5953, + 5676, 5893, 5853, 5891, 5892, 5872, 5877, 6072, 5833, 5852, 5886, 6012, + 5884, 6010, 5885, 6011, 5882, 6008, 5883, 6009, 5880, 6006, 5881, 6007, + 5705, 5832, 5376, 5879, 5396, 5680, 5703, 5706, 5701, 5702, 5704, 5700, + 5871, 5869, 5870, 5863, 5864, 5866, 5867, 5862, 6005, 5860, 6003, 5861, + 6004, 5855, 6001, 5856, 6002, 5858, 5999, 5859, 6000, 5854, 6093, 6079, + 6091, 6092, 6081, 6082, 6074, 6077, 6078, 6090, 5989, 6088, 5987, 6089, + 5988, 6086, 5985, 6087, 5986, 6084, 5983, 6085, 5984, 5714, 6064, 5397, + 5982, 5849, 5831, 5815, 5965, 5825, 5829, 5830, 5827, 5828, 5963, 5823, + 5824, 5961, 5817, 5994, 5813, 5993, 5677, 5625, 5604, 5605, 5620, 5622, + 5623, 5602, 5603, 5624, 6035, 5601, 5913, 5698, 5911, 5696, 5912, 5697, + 5909, 5910, 5907, 5908, 5905, 6027, 5895, 5926, 5927, 5923, 5921, 5920, + 5944, 5945, 5946, 5943, 5753, 5751, 5752, 5749, 5750, 5747, 5748, 5746, + 5754, 5627, 5772, 5776, 5777, 5774, 5775, 5770, 5771, 5769, 5918, 5919, + 5914, 5917, 5996, 5997, 5998, 5995, 5733, 5737, 5738, 5735, 5736, 5731, + 5732, 5730, 5739, 5847, 5848, 5843, 5846, 6055, 6056, 6057, 6054, 6046, + 5656, 5657, 5654, 5655, 5652, 5653, 5651, 5903, 5901, 5902, 5899, 5900, + 5897, 5898, 5896, 5374, 5393, 5394, 5395, 5392, 5381, 5382, 5383, 5380, + 5389, 5390, 5391, 5388, 5385, 5386, 5387, 5384, 5838, 5839, 5835, 5837, + 5423, 5422, 5418, 5419, 5421, 5420, 5569, 5568, 5564, 5565, 5567, 5566, + 5575, 5574, 5570, 5571, 5573, 5572, 5439, 5438, 5434, 5435, 5437, 5436, + 5533, 5532, 5528, 5529, 5531, 5530, 5527, 5526, 5522, 5523, 5525, 5524, + 5496, 5495, 5491, 5492, 5494, 5493, 5490, 5432, 5431, 5428, 5429, 5430, + 5426, 5469, 5468, 5464, 5465, 5467, 5466, 5463, 5462, 5458, 5459, 5461, + 5460, 5457, 5476, 5475, 5470, 5471, 5474, 5472, 5563, 5562, 5558, 5559, + 5561, 5560, 5581, 5580, 5576, 5577, 5579, 5578, 5456, 5840, 5455, 5450, + 5451, 5454, 5842, 5453, 5449, 5448, 5444, 5445, 5447, 5446, 5551, 5550, + 5546, 5547, 5549, 5548, 5410, 5409, 5405, 5406, 5408, 5407, 5545, 5544, + 5540, 5541, 5543, 5542, 5509, 5508, 5504, 5505, 5507, 5506, 5515, 5514, + 5510, 5511, 5513, 5512, 5503, 5502, 5498, 5499, 5501, 5500, 5497, 5443, + 5416, 5415, 5411, 5412, 5414, 5413, 5489, 5488, 5484, 5485, 5487, 5486, + 5483, 5482, 5478, 5479, 5481, 5480, 5477, 5539, 5538, 5534, 5535, 5537, + 5536, 5557, 5556, 5552, 5553, 5555, 5554, 5521, 5520, 5516, 5517, 5519, + 5518, 5592, 5621, 5773, 5734, 5744, 5745, 5742, 5743, 5740, 5741, 6053, + 6051, 6052, 6049, 6050, 6047, 6048, 6058, 5379, 24281, 24256, 24267, + 24263, 24273, 24270, 24276, 24279, 24280, 24259, 24258, 24278, 24264, + 24269, 24274, 24268, 24255, 24271, 24277, 24261, 24265, 24260, 24272, + 24275, 24266, 24262, 24257, 24253, 24254, 35536, 35536, 35536, 26606, + 26662, 26656, 26660, 26659, 26654, 26652, 26599, 26584, 26630, 26583, + 26582, 26627, 26642, 26629, 26633, 26634, 26636, 26622, 26588, 26621, + 26607, 26600, 26608, 26610, 26655, 26612, 26611, 26625, 26639, 26651, + 26641, 26593, 26615, 26596, 26619, 26609, 26624, 26645, 26616, 26658, + 26585, 26648, 26647, 26643, 26586, 26664, 26653, 26644, 26591, 26650, + 26638, 26594, 26632, 26597, 26657, 26626, 26640, 26623, 26592, 26614, + 26613, 26595, 26631, 26598, 26618, 26590, 26589, 26587, 26649, 26628, + 26646, 26617, 26661, 26663, 26665, 26666, 26581, 26667, 26668, 26580, + 26620, 26637, 26635, 26604, 26603, 26605, 26602, 26601, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 29374, 29391, 29392, 29382, 29380, + 29376, 29388, 29379, 29377, 29385, 29378, 29384, 29390, 29386, 29383, + 29389, 29387, 29381, 29395, 29396, 29394, 29393, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 29375, 14797, 14798, 14799, + 14788, 14786, 14782, 14794, 14785, 14783, 14791, 14784, 14790, 14796, + 14792, 14789, 14795, 14793, 14787, 14801, 14802, 14800, 25736, 25737, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 5092, + 5093, 5094, 5083, 5081, 5077, 5089, 5080, 5078, 5086, 5079, 5085, 5091, + 5087, 5084, 5090, 5088, 5082, 5095, 5096, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 29410, 29411, + 29412, 29402, 29401, 29397, 29407, 29400, 29398, 29405, 29399, 29404, + 29409, 35536, 29403, 29408, 29406, 35536, 29413, 29414, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 16913, 16911, 16914, 16912, 16915, 16909, 16907, 16910, 16908, 16917, + 16931, 16927, 16932, 16928, 16916, 16929, 16925, 16930, 16926, 16918, + 16939, 16919, 16921, 16920, 16935, 16938, 16936, 16934, 16937, 16923, + 16922, 16924, 16940, 16933, 16941, 16888, 16886, 16896, 16897, 16892, + 16895, 16893, 16894, 16905, 16906, 16903, 16904, 16898, 16887, 16891, + 16890, 16889, 17008, 17007, 17009, 17014, 17016, 17020, 17022, 17024, + 17026, 17025, 17017, 17021, 17015, 17027, 17011, 17012, 17019, 17013, + 16962, 16956, 16961, 16963, 16957, 16947, 16955, 16958, 16952, 16944, + 16945, 16964, 16951, 16946, 16953, 16948, 16950, 16959, 16949, 16960, + 16954, 16885, 16942, 16943, 35536, 35536, 17034, 17036, 17033, 17032, + 17029, 17028, 17031, 17030, 17037, 17035, 35536, 35536, 35536, 35536, + 35536, 35536, 16986, 16985, 16982, 16984, 16983, 16977, 16980, 16981, + 16979, 16978, 35536, 35536, 35536, 35536, 35536, 35536, 22862, 22874, + 22870, 22719, 22869, 22720, 22867, 22858, 22871, 22872, 22873, 22718, + 22717, 22716, 22868, 22715, 22711, 22713, 22710, 22709, 22706, 22705, + 22708, 22707, 22714, 22712, 35536, 35536, 35536, 35536, 35536, 35536, + 22723, 22837, 22854, 22839, 22841, 22840, 22842, 22838, 22848, 22751, + 22843, 22849, 22850, 22846, 22755, 22836, 22798, 22797, 22828, 22844, + 22752, 22847, 22853, 22851, 22852, 22845, 22834, 22833, 22827, 22831, + 22832, 22830, 22835, 22829, 22754, 22807, 22825, 22826, 22814, 22816, + 22815, 22817, 22801, 22818, 22821, 22822, 22808, 22820, 22811, 22803, + 22812, 22805, 22810, 22824, 22823, 22819, 22809, 22813, 22804, 22806, + 22802, 22796, 22781, 22782, 22790, 22789, 22786, 22794, 22775, 22777, + 22795, 22784, 22780, 22791, 22793, 22792, 22776, 22778, 22779, 22788, + 22785, 22783, 22787, 22774, 22772, 22773, 22771, 22770, 22753, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 22725, 22727, 22729, 22736, + 22735, 22743, 22739, 22724, 22734, 22750, 22737, 22749, 22741, 22740, + 22731, 22738, 22742, 22728, 22745, 22744, 22748, 22746, 22747, 22726, + 22800, 22799, 22763, 22768, 22759, 22764, 22760, 22756, 22761, 22757, + 22769, 22758, 22766, 22767, 22733, 22732, 22762, 22730, 22765, 35536, + 35536, 35536, 35536, 35536, 5793, 5378, 5372, 6061, 5809, 5806, 5797, + 5935, 5644, 5635, 5684, 5757, 5729, 5661, 5878, 5834, 5865, 5868, 5857, + 6083, 6080, 5826, 5762, 5782, 5763, 5783, 5760, 5780, 5761, 5781, 5822, + 5820, 5821, 5818, 5819, 5816, 5789, 5790, 5787, 5786, 5788, 5779, 5784, + 5785, 5599, 6043, 5612, 5611, 5814, 5964, 5962, 5906, 5904, 5925, 5924, + 5922, 5916, 5915, 5845, 5844, 5836, 5425, 5402, 5427, 5424, 5841, 5452, + 5398, 5399, 5403, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 19167, 19134, 19133, 19114, 19113, 19120, 19128, 19127, + 19132, 19131, 19119, 19117, 19115, 19130, 19129, 19121, 19136, 19135, + 19126, 19125, 19139, 19118, 19140, 19138, 19141, 19122, 19123, 19124, + 19137, 19112, 19116, 35536, 19158, 19165, 19166, 19164, 19159, 19162, + 19160, 19163, 19161, 19157, 19155, 19156, 35536, 35536, 35536, 35536, + 19149, 19146, 19148, 19154, 19147, 19152, 19151, 19153, 19150, 19143, + 19142, 19144, 35536, 35536, 35536, 35536, 19145, 35536, 35536, 35536, + 19168, 19169, 19176, 19178, 19175, 19174, 19171, 19170, 19173, 19172, + 19179, 19177, 29435, 29447, 29430, 29427, 29445, 29448, 29429, 29428, + 29442, 29437, 29436, 29443, 29440, 29446, 29441, 29444, 29434, 29426, + 29431, 29415, 29449, 29419, 29420, 29438, 29433, 29432, 29439, 29418, + 29416, 29417, 35536, 35536, 29421, 29422, 29423, 29424, 29425, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 23820, 23842, 23802, 23804, 23807, 23824, 23826, 23829, 23810, 23806, + 23822, 23832, 23828, 23844, 23811, 23809, 23808, 23833, 23831, 23830, + 23813, 23812, 23819, 23835, 23834, 23841, 23816, 23821, 23818, 23838, + 23843, 23840, 23817, 23815, 23814, 23839, 23837, 23836, 23801, 23803, + 23823, 23825, 23805, 23827, 35536, 35536, 35536, 35536, 23865, 23850, + 23854, 23860, 23863, 23866, 23852, 23856, 23857, 23861, 23853, 23851, + 23864, 23859, 23858, 23862, 23855, 23800, 23795, 23794, 23799, 23798, + 23797, 23796, 23847, 23848, 35536, 35536, 35536, 35536, 35536, 35536, + 23873, 23875, 23872, 23871, 23868, 23867, 23870, 23869, 23876, 23874, + 23849, 35536, 35536, 35536, 23845, 23846, 16987, 17006, 16999, 17002, + 17004, 16997, 16995, 16993, 16989, 16991, 16976, 16974, 16966, 16970, + 16972, 16968, 17000, 17005, 16998, 17001, 17003, 16996, 16994, 16992, + 16988, 16990, 16975, 16973, 16965, 16969, 16971, 16967, 5061, 5058, 5050, + 5049, 5063, 5055, 5048, 5047, 5066, 5057, 5054, 5053, 5056, 5060, 5052, + 5051, 5068, 5064, 5062, 5067, 5065, 5069, 5059, 5072, 5074, 5071, 5073, + 5070, 35536, 35536, 5076, 5075, 29483, 29481, 29482, 29499, 29498, 29497, + 29523, 29489, 29488, 29502, 29509, 29501, 29524, 29517, 29484, 29529, + 29500, 29516, 29493, 29492, 29506, 29505, 29525, 29528, 29491, 29490, + 29494, 29504, 29507, 29503, 29530, 29510, 29496, 29515, 29518, 29511, + 29513, 29531, 29485, 29486, 29487, 29495, 29514, 29532, 29508, 29521, + 29522, 29519, 29520, 29527, 29526, 29512, 29480, 29455, 29454, 29452, + 29543, 29450, 29451, 29453, 29456, 29457, 29458, 35536, 29551, 29558, + 29562, 29559, 29568, 29574, 29575, 29573, 29572, 29570, 29571, 29563, + 29564, 29567, 29576, 29560, 29566, 29561, 29569, 29565, 29542, 29555, + 29556, 29535, 29536, 29537, 29546, 29545, 29538, 35536, 35536, 29459, + 29466, 29468, 29465, 29464, 29461, 29460, 29463, 29462, 29469, 29467, + 35536, 35536, 35536, 35536, 35536, 35536, 29476, 29478, 29475, 29474, + 29471, 29470, 29473, 29472, 29479, 29477, 35536, 35536, 35536, 35536, + 35536, 35536, 29552, 29553, 29550, 29541, 29534, 29554, 29547, 29544, + 29539, 29540, 29548, 29549, 29533, 29557, 35536, 35536, 7299, 7263, 7388, + 7302, 7547, 7566, 7565, 7495, 7283, 7469, 7534, 7501, 7287, 7500, 7499, + 7436, 7430, 7454, 7517, 7455, 7518, 7531, 7488, 7387, 7504, 7286, 7285, + 7545, 7414, 7415, 7416, 7279, 7558, 7368, 7560, 7151, 7562, 7481, 7559, + 7561, 7483, 7530, 7314, 7301, 7262, 7269, 35536, 35536, 7460, 7520, 7487, + 7385, 7533, 7538, 7272, 7273, 7311, 7447, 7552, 7289, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 2762, 2761, 2763, + 2760, 2764, 2671, 2672, 2688, 2689, 2692, 2693, 2710, 2711, 2701, 2702, + 2685, 2675, 2690, 2691, 2696, 2698, 2686, 2687, 2705, 2678, 2679, 2694, + 2695, 2706, 2717, 2716, 2682, 2681, 2704, 2715, 2718, 2680, 2683, 2703, + 2707, 2708, 2676, 2677, 2723, 2725, 2709, 2700, 2724, 2713, 2714, 2712, + 2722, 2765, 2776, 2779, 2780, 2770, 2771, 2768, 2769, 2766, 2767, 2772, + 2773, 2775, 2774, 2777, 2778, 2781, 2697, 2699, 2719, 2684, 2720, 2721, + 2673, 2674, 35536, 2670, 2669, 2789, 2791, 2788, 2787, 2784, 2783, 2786, + 2785, 2792, 2790, 2757, 2754, 2782, 2667, 2668, 2666, 2756, 2743, 2741, + 2744, 2735, 2736, 2742, 2738, 2740, 2739, 2737, 2733, 2732, 2728, 2726, + 2730, 2729, 2727, 2731, 2734, 2753, 2752, 2751, 2750, 2745, 2747, 2748, + 2749, 2746, 2758, 2755, 2759, 28982, 28980, 28981, 28933, 28968, 28970, + 28935, 28969, 28945, 28946, 28953, 28961, 28956, 28947, 28954, 28958, + 28967, 28948, 28962, 28955, 28949, 28960, 28938, 28963, 28951, 28959, + 28966, 28942, 28940, 28964, 28944, 28965, 28957, 28928, 28929, 28930, + 28988, 28987, 28989, 28986, 28984, 28985, 28979, 28983, 28931, 28932, + 28952, 28943, 28996, 28998, 28995, 28994, 28991, 28990, 28993, 28992, + 28999, 28997, 28927, 28941, 28939, 28950, 28936, 28937, 3557, 3543, 3551, + 3535, 3523, 3547, 3546, 3532, 3538, 3531, 3524, 3555, 3541, 3533, 3550, + 3534, 3552, 3549, 3554, 3539, 3522, 3537, 3544, 3527, 3545, 3540, 3525, + 3556, 3542, 3529, 3553, 3536, 3530, 3548, 3528, 3526, 3558, 3559, 3566, + 3572, 3569, 3573, 3574, 3570, 3575, 3571, 3567, 3568, 3520, 3521, 3560, + 3561, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 3565, 3563, + 3564, 3562, 18996, 18995, 18994, 19008, 19007, 19013, 19023, 19022, + 19026, 19014, 19021, 19020, 19002, 19015, 18999, 18998, 18997, 19006, + 19005, 19004, 19003, 19012, 19011, 19017, 19016, 19001, 19031, 19028, + 19027, 19010, 19009, 19029, 19025, 19024, 19030, 19032, 19041, 19040, + 19046, 19048, 19044, 19045, 19042, 19043, 19047, 18985, 18990, 18989, + 18987, 18991, 18992, 18993, 18988, 18986, 19039, 19038, 35536, 35536, + 35536, 19033, 19036, 19037, 19035, 19034, 19055, 19057, 19054, 19053, + 19050, 19049, 19052, 19051, 19058, 19056, 35536, 35536, 35536, 19019, + 19018, 19000, 24327, 24329, 24326, 24325, 24322, 24321, 24324, 24323, + 24330, 24328, 24300, 24291, 24289, 24288, 24290, 24301, 24285, 24284, + 24286, 24287, 24303, 24299, 24297, 24296, 24298, 24305, 24311, 24312, + 24310, 24313, 24302, 24295, 24292, 24294, 24293, 24304, 24306, 24307, + 24309, 24308, 24315, 24316, 24314, 24320, 24319, 24331, 24318, 24317, + 9548, 9525, 9531, 9588, 9569, 9577, 9567, 9568, 9587, 9253, 9579, 35536, + 35536, 35536, 35536, 35536, 13004, 13035, 13012, 13041, 13010, 13040, + 13033, 13030, 13042, 13022, 13024, 13036, 13038, 13043, 13026, 13032, + 13034, 13028, 13031, 13044, 13025, 13021, 13011, 13039, 13027, 13005, + 13008, 13020, 13007, 13006, 13037, 13019, 13015, 13018, 13016, 13046, + 13013, 13017, 13047, 13045, 13009, 13029, 13003, 35536, 35536, 13002, + 13014, 13023, 28978, 28976, 28977, 28975, 28974, 28973, 28972, 28971, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 33381, 33394, + 33383, 33361, 33375, 33389, 33390, 33391, 33376, 33392, 33379, 33387, + 33382, 33380, 33388, 33386, 33385, 33393, 33374, 33372, 33363, 33370, + 33362, 33373, 33371, 33352, 33353, 33355, 33356, 33368, 33366, 33367, + 33365, 33354, 33358, 33364, 33377, 33360, 33369, 33357, 33384, 33378, + 33359, 35536, 35536, 35536, 35536, 35536, 17959, 17960, 18567, 17956, + 17961, 17962, 17933, 17932, 18552, 18545, 17965, 17966, 17939, 17967, + 17945, 17940, 17941, 18502, 18503, 18504, 18547, 17943, 18539, 18058, + 17969, 17946, 17954, 17949, 17972, 18507, 18506, 18505, 17973, 17974, + 17976, 17934, 17986, 17920, 13548, 13551, 13547, 13550, 13546, 9418, + 22400, 22401, 22391, 22392, 22403, 22404, 22394, 22406, 22396, 22407, + 22408, 22409, 22410, 22411, 22412, 22395, 22398, 22399, 22413, 22393, + 22416, 22417, 22419, 22550, 22657, 22551, 22659, 22554, 22579, 22593, + 22647, 22632, 22662, 22600, 22670, 22681, 22610, 22597, 22630, 22633, + 22654, 22556, 22634, 22649, 22674, 22648, 22660, 22678, 22552, 22558, + 22601, 22584, 22602, 22578, 18708, 18716, 18718, 18719, 13757, 13753, + 13756, 13755, 13754, 18628, 18044, 18093, 18179, 18322, 18342, 18419, + 18447, 18440, 18486, 18522, 18690, 18574, 22468, 18250, 18532, 17988, + 18257, 18415, 17989, 18627, 18045, 18097, 18180, 18193, 18276, 18303, + 18323, 18348, 18420, 18450, 18487, 18166, 18635, 18662, 18691, 18007, + 18033, 18096, 18156, 18408, 18457, 18495, 18247, 18404, 18168, 18614, + 18174, 22658, 22559, 22577, 22595, 22641, 22598, 22585, 22646, 22669, + 22612, 22613, 22560, 22562, 22615, 22619, 22621, 22565, 22611, 22661, + 22629, 22628, 22571, 22555, 22635, 22645, 22594, 22650, 22676, 22677, + 22573, 22680, 22671, 22590, 22592, 22591, 22596, 22673, 7271, 7270, 7536, + 7535, 7482, 7370, 7484, 7153, 7369, 7152, 7428, 7165, 7485, 7280, 7497, + 7525, 7389, 7553, 7554, 7411, 7402, 7403, 7404, 7406, 7413, 7409, 7438, + 7394, 7440, 7417, 7395, 7396, 7442, 7397, 7398, 7427, 7431, 7419, 7446, + 7401, 7433, 7434, 7432, 7410, 7418, 7422, 7443, 7408, 7425, 7435, 7400, + 7421, 7424, 7550, 7393, 7392, 7265, 7563, 7268, 7259, 7278, 7162, 7451, + 7508, 17441, 18005, 17477, 18047, 17476, 18046, 17475, 18043, 17484, + 18062, 17509, 18099, 17508, 18098, 17507, 18094, 17503, 18088, 17504, + 18089, 17537, 18142, 17538, 18143, 17526, 18130, 17535, 18140, 17517, + 18121, 17562, 18182, 17570, 18192, 17589, 18212, 17588, 18211, 17586, + 18209, 17583, 18206, 17582, 18205, 17606, 18239, 17600, 18227, 17637, + 18274, 17634, 18271, 17638, 18275, 17645, 18288, 17646, 18289, 17656, + 18298, 17652, 18285, 17662, 18320, 17664, 18325, 17663, 18324, 17682, + 18347, 17681, 18346, 17676, 18339, 17672, 18334, 17713, 18383, 17712, + 18382, 17690, 18375, 17691, 18376, 17741, 18418, 17743, 18422, 17753, + 18437, 17751, 18435, 17752, 18436, 17758, 18442, 17779, 18480, 17777, + 18478, 17776, 18471, 17771, 18473, 17778, 18479, 17800, 18520, 17799, + 18519, 17801, 18523, 17795, 18513, 17831, 18595, 17852, 18618, 17824, + 18588, 17851, 18617, 17843, 18607, 17864, 18638, 17863, 18634, 17877, + 18651, 17878, 18652, 17874, 18648, 17876, 18650, 17875, 18649, 17883, + 18657, 17882, 18656, 17888, 18667, 17899, 18681, 17903, 18685, 17905, + 18687, 18213, 18518, 18653, 18677, 18006, 18313, 18312, 18314, 17789, + 18103, 17435, 17999, 17451, 18017, 17447, 18013, 17446, 18012, 17445, + 18011, 17449, 18015, 17448, 18014, 17430, 17994, 17429, 17993, 17428, + 17992, 17432, 17996, 17431, 17995, 17531, 18135, 17542, 18148, 17534, + 18139, 17522, 18126, 17521, 18125, 17520, 18124, 17525, 18129, 17524, + 18128, 17607, 18240, 17597, 18231, 17704, 18368, 17724, 18394, 17696, + 18360, 17695, 18359, 17694, 18358, 17698, 18362, 17697, 18361, 17721, + 18391, 17720, 18390, 17719, 18389, 17723, 18393, 17722, 18392, 17834, + 18598, 17841, 18605, 17838, 18602, 17837, 18601, 17836, 18600, 17840, + 18604, 17839, 18603, 17889, 18668, 17887, 18666, 17891, 18670, 17895, + 18676, 17667, 18328, 17668, 18329, 17892, 18671, 13595, 13587, 13600, + 13592, 13596, 13588, 13598, 13590, 13361, 13353, 13364, 13356, 13362, + 13354, 13366, 13358, 13618, 13615, 13620, 13617, 13619, 13616, 35536, + 35536, 13401, 13398, 13403, 13400, 13402, 13399, 35536, 35536, 13633, + 13625, 13638, 13630, 13634, 13626, 13636, 13628, 13385, 13377, 13388, + 13380, 13386, 13378, 13390, 13382, 13659, 13650, 13662, 13653, 13661, + 13652, 13660, 13651, 13413, 13408, 13416, 13411, 13415, 13410, 13414, + 13409, 13720, 13717, 13722, 13719, 13721, 13718, 35536, 35536, 13447, + 13444, 13449, 13446, 13448, 13445, 35536, 35536, 13679, 13670, 13682, + 13673, 13681, 13672, 13680, 13671, 35536, 13459, 35536, 13462, 35536, + 13461, 35536, 13460, 13700, 13692, 13705, 13697, 13701, 13693, 13703, + 13695, 13431, 13423, 13434, 13426, 13432, 13424, 13436, 13428, 13585, + 13605, 13622, 13621, 13645, 13643, 13665, 13666, 13724, 13723, 13685, + 13686, 13712, 13710, 35536, 35536, 13602, 13594, 13601, 13593, 13597, + 13589, 13599, 13591, 13368, 13360, 13365, 13357, 13363, 13355, 13367, + 13359, 13640, 13632, 13639, 13631, 13635, 13627, 13637, 13629, 13392, + 13384, 13389, 13381, 13387, 13379, 13391, 13383, 13707, 13699, 13706, + 13698, 13702, 13694, 13704, 13696, 13438, 13430, 13435, 13427, 13433, + 13425, 13437, 13429, 13584, 13609, 13586, 13607, 13606, 35536, 13603, + 13604, 13370, 13374, 13371, 13372, 13369, 13544, 13572, 13573, 13577, + 13493, 13646, 13647, 13644, 35536, 13641, 13642, 13405, 13404, 13395, + 13394, 13393, 13576, 13575, 13574, 13664, 13668, 13657, 13656, 35536, + 35536, 13663, 13655, 13417, 13421, 13418, 13419, 35536, 13501, 13500, + 13499, 13684, 13688, 13677, 13676, 13732, 13731, 13683, 13675, 13464, + 13468, 13465, 13466, 13454, 13495, 13494, 13771, 35536, 35536, 13713, + 13714, 13711, 35536, 13708, 13709, 13451, 13450, 13441, 13440, 13439, + 13569, 13498, 35536, 11889, 11886, 11891, 11888, 31540, 12641, 28108, + 12566, 26028, 31513, 14126, 35342, 35341, 35343, 18877, 26357, 15608, + 24061, 12565, 11890, 11887, 15552, 10369, 10343, 18800, 26287, 27983, + 27981, 18753, 26252, 10342, 10337, 9647, 10336, 5097, 32125, 24830, + 32272, 15575, 15611, 19185, 25369, 18876, 26356, 25902, 18875, 26355, + 23662, 25605, 25606, 25988, 10351, 32139, 26195, 26189, 26203, 6109, + 27982, 27984, 26212, 10373, 15950, 25183, 32323, 6395, 6110, 2572, 15610, + 12645, 18808, 26298, 10374, 26052, 12480, 31914, 26194, 3956, 3998, + 19854, 26200, 7136, 32284, 7569, 29091, 15968, 12605, 31520, 26048, + 12634, 12591, 32273, 12635, 10315, 32150, 33402, 21670, 33948, 12768, + 15970, 15972, 15971, 35536, 18874, 26354, 12584, 25901, 15864, 7, 15863, + 6, 23657, 24059, 29062, 29050, 35536, 35536, 29057, 29056, 29059, 29058, + 29049, 29063, 29053, 29055, 29048, 29052, 29054, 29051, 28892, 28894, + 28891, 28890, 28887, 28886, 28889, 28888, 28882, 28893, 28883, 28885, + 28881, 28880, 28884, 35536, 18705, 18706, 18714, 18720, 18704, 18707, + 18710, 18711, 18712, 18713, 18715, 18703, 18717, 35536, 35536, 35536, + 12476, 7149, 7815, 12652, 19793, 22285, 23590, 25629, 26677, 33952, + 23793, 10309, 12477, 17238, 32160, 10491, 13049, 25630, 13821, 2585, + 15616, 6228, 19794, 28444, 31285, 15854, 32242, 24111, 20359, 26546, + 17422, 3862, 28424, 26838, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 7458, 7516, 7473, + 7529, 7158, 7174, 7453, 7513, 7522, 7173, 7157, 7539, 7313, 7303, 7306, + 7309, 7304, 7462, 7305, 7307, 7308, 7505, 7294, 7159, 7546, 7564, 7464, + 7470, 7521, 7463, 7452, 7512, 7161, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 0, + 100, 10385, 9671, 6232, 6111, 5362, 12479, 26866, 9673, 26863, 26855, + 4046, 10386, 25791, 25792, 26856, 4047, 26857, 26864, 20031, 10387, + 24155, 28340, 26859, 10383, 10388, 26860, 4048, 10389, 25964, 26173, + 26969, 31179, 32110, 33395, 10390, 25177, 25187, 15967, 4049, 32265, + 16764, 963, 26852, 4045, 11946, 26862, 26853, 26854, 32249, 26858, 26865, + 348, 3753, 13052, 9660, 15861, 26534, 12545, 10397, 10396, 10382, 10384, + 10398, 32258, 32259, 26199, 32260, 10395, 10391, 10392, 10393, 10394, + 25992, 32244, 25607, 2643, 32262, 29170, 33546, 33544, 33548, 33549, + 33554, 33542, 33553, 33552, 33540, 33547, 33539, 33541, 33550, 33538, + 33555, 12646, 26502, 26513, 26514, 26501, 26498, 26507, 26509, 26517, + 26518, 26510, 26516, 26512, 26495, 26503, 26499, 26505, 28170, 28174, + 28175, 28169, 28166, 28178, 28177, 28165, 28179, 28176, 28164, 28173, + 28168, 28171, 28167, 28172, 26506, 26500, 26511, 26515, 18463, 26508, + 26497, 26496, 26504, 33556, 32253, 32252, 35536, 35536, 35536, 35536, + 18878, 32474, 26359, 10427, 18782, 32345, 24092, 24065, 28312, 28327, + 18898, 26383, 18962, 26460, 18959, 32524, 26459, 10463, 18901, 26386, + 18909, 32487, 26366, 10441, 32346, 18907, 26392, 18892, 26378, 18790, + 18785, 10467, 32484, 32485, 10436, 10437, 26374, 10428, 974, 7120, 24094, + 18889, 979, 7122, 18928, 18922, 32501, 32498, 26419, 26413, 10478, 10475, + 26394, 32476, 18912, 18974, 32527, 26426, 10486, 18929, 26420, 18965, + 18789, 26402, 18963, 32491, 26400, 10468, 18787, 32348, 24105, 24078, + 28324, 28337, 18949, 26450, 18978, 26430, 32477, 10429, 18969, 32492, + 26406, 10469, 18888, 26373, 18960, 32528, 26461, 10465, 32533, 32529, + 32531, 32530, 32535, 32536, 26462, 24093, 28315, 32350, 26239, 10439, + 31528, 18908, 26393, 18784, 18894, 26376, 18783, 18973, 26425, 18792, + 12630, 7574, 25515, 31507, 31506, 11879, 15782, 23546, 11828, 24114, + 28150, 7582, 10135, 28143, 11893, 23505, 23493, 23497, 22289, 22296, + 10310, 10117, 26974, 2571, 26471, 5098, 28671, 7821, 12640, 25991, 15855, + 26229, 959, 21544, 28447, 10121, 10136, 25374, 24119, 19808, 19815, + 15943, 32325, 15928, 10340, 32149, 7588, 29084, 33534, 7123, 7114, 976, + 31508, 3754, 26083, 25990, 10311, 12482, 12874, 15596, 31818, 26201, + 15964, 28103, 33956, 24124, 22295, 2578, 24115, 1058, 1061, 23749, 364, + 24116, 366, 32140, 361, 11932, 12872, 10127, 1062, 12873, 1059, 15745, + 7148, 11930, 26469, 26470, 7767, 11947, 11933, 28836, 9676, 11915, 21555, + 26054, 24126, 15621, 24127, 28873, 19078, 13285, 19080, 13288, 19069, + 13278, 23242, 23241, 3752, 24125, 24129, 24128, 23754, 23751, 19077, + 13284, 23753, 23750, 19079, 13286, 23755, 23752, 25965, 28910, 25974, + 28919, 25973, 28918, 10138, 10141, 28898, 29070, 24112, 24113, 28904, + 29076, 23747, 23748, 28903, 29075, 22995, 22996, 22997, 28544, 28633, + 28546, 28635, 28479, 28486, 6909, 6855, 6914, 6647, 6660, 6910, 6636, + 6919, 6661, 28804, 28797, 28818, 28752, 26316, 18826, 10404, 32354, 2579, + 22304, 32157, 12631, 32143, 10367, 10140, 24123, 10143, 23746, 25975, + 28920, 24109, 7583, 24110, 7584, 24862, 15744, 22998, 15367, 15951, + 33977, 23591, 24064, 26235, 26312, 23502, 23503, 23504, 23498, 9955, + 10312, 28838, 10119, 4474, 18840, 26274, 18798, 26285, 26202, 9064, 9063, + 10361, 10360, 10339, 10364, 25785, 11916, 19083, 13294, 33445, 33444, + 19067, 13289, 11914, 11913, 11911, 11912, 10139, 10142, 24120, 24121, + 28545, 28634, 19068, 13277, 25972, 28917, 24117, 10133, 24118, 10134, + 33401, 22281, 32353, 10407, 11829, 11831, 28151, 11834, 11833, 28152, + 11832, 11830, 7585, 7586, 28144, 7587, 28145, 35254, 9956, 11826, 15590, + 32337, 10408, 25989, 25624, 33720, 18749, 26247, 18837, 26256, 4457, + 4454, 32058, 32054, 26192, 28577, 2567, 26876, 26872, 31177, 25910, + 33458, 25788, 32256, 33711, 15588, 32053, 32057, 4453, 4456, 32047, 4451, + 12656, 28210, 32338, 24852, 11943, 33961, 16766, 18847, 26337, 11942, + 3700, 9642, 362, 29180, 32074, 10128, 7593, 28135, 7769, 7770, 1004, + 1042, 1028, 1016, 1017, 1033, 1014, 984, 989, 1039, 1044, 1030, 1029, + 1024, 1031, 1012, 1036, 1025, 1032, 987, 996, 995, 1018, 1021, 997, 1050, + 1023, 1047, 993, 1022, 1020, 1048, 1000, 1019, 1035, 994, 1001, 1010, + 988, 1049, 1034, 985, 1015, 1046, 991, 1040, 1009, 986, 998, 1011, 1052, + 1051, 990, 992, 1053, 1041, 1038, 1027, 1026, 999, 1045, 1002, 1037, + 1007, 1006, 1043, 1003, 1008, 1005, 24130, 26234, 27165, 3595, 33417, + 15927, 7591, 10042, 11885, 7573, 33865, 11907, 367, 15078, 6691, 6913, + 5038, 32324, 22878, 15614, 24848, 24849, 25529, 25530, 10037, 28228, + 1013, 9667, 25976, 23978, 25978, 7143, 18843, 18844, 18842, 26281, 26282, + 26280, 18805, 18812, 18804, 26295, 26302, 26294, 18747, 18745, 18746, + 9066, 26245, 26243, 26244, 15938, 15556, 32406, 32445, 28923, 28921, + 32061, 4460, 4461, 26063, 18846, 26318, 15565, 15566, 15567, 15568, 9689, + 9687, 9691, 9680, 9684, 9692, 9681, 9685, 9690, 9679, 9683, 9678, 9682, + 9688, 9686, 28512, 26174, 12509, 33412, 22117, 22109, 22116, 22110, + 22114, 22115, 22113, 22112, 22111, 10658, 12772, 32049, 4464, 32031, + 4463, 32062, 4467, 33874, 3701, 28864, 12596, 8, 11827, 9668, 3987, 3949, + 4030, 3926, 3988, 3950, 3990, 230, 28862, 31826, 15589, 3966, 3969, 3971, + 3963, 10365, 4009, 3871, 25924, 25921, 25922, 25923, 24237, 29165, 29173, + 29174, 29154, 29152, 29155, 29166, 29137, 29138, 29159, 29161, 29160, + 29158, 29139, 29171, 29172, 29141, 29150, 29149, 29148, 29147, 29163, + 29177, 29153, 29140, 29151, 29175, 29156, 29157, 29168, 29167, 29169, + 29178, 29142, 4053, 24835, 29164, 29145, 29176, 29144, 29143, 29146, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 24249, 24244, 24247, 24248, 24241, 24242, 24240, 24239, + 24246, 24243, 24245, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 6657, 6654, 6653, 6650, 6649, 6652, 6651, + 6658, 6656, 6878, 6858, 6903, 6891, 6873, 6861, 6877, 6875, 6857, 6904, + 6892, 25461, 25459, 25458, 25455, 25454, 25457, 25456, 25462, 25460, + 25453, 25444, 25452, 25450, 25445, 25446, 25448, 25449, 25443, 25447, + 25451, 9977, 9989, 9986, 9971, 9968, 9983, 9980, 9995, 9974, 24151, + 24144, 24152, 24150, 24145, 24146, 24147, 24149, 24143, 24154, 24153, + 25489, 25490, 25491, 25492, 25493, 25494, 25495, 25496, 25497, 25498, + 25499, 25500, 25501, 25502, 25503, 25504, 25505, 25506, 25507, 25508, + 25509, 25510, 25511, 25512, 25513, 25514, 6801, 6802, 6803, 6804, 6805, + 6806, 6807, 6808, 6809, 6810, 6811, 6812, 6813, 6814, 6815, 6816, 6817, + 6818, 6819, 6820, 6821, 6822, 6823, 6824, 6825, 6826, 6827, 6828, 6829, + 6830, 6831, 6832, 6833, 6834, 6835, 6836, 6837, 6838, 6839, 6840, 6841, + 6842, 6843, 6844, 6845, 6846, 6847, 6848, 6849, 6850, 6851, 6852, 6655, + 23671, 23669, 23667, 23672, 23673, 23675, 23676, 23670, 23674, 23668, + 10331, 10329, 10328, 10325, 10324, 10327, 10326, 10332, 10330, 10323, + 23666, 4586, 4532, 4602, 4518, 4595, 4531, 4594, 4530, 4593, 4529, 4592, + 4528, 4583, 4502, 4496, 4525, 4582, 4500, 4494, 4524, 4601, 4630, 4624, + 4517, 4600, 4628, 4622, 4515, 4609, 4646, 4623, 4495, 4643, 4501, 4629, + 4521, 4608, 4645, 4621, 4493, 4642, 4499, 4627, 4520, 4581, 4536, 4614, + 4504, 4498, 4617, 4539, 4523, 4599, 4535, 4613, 4631, 4625, 4616, 4538, + 4516, 4607, 4537, 4615, 4644, 4620, 4497, 4641, 4541, 4619, 4534, 4612, + 4503, 4626, 4618, 4540, 4519, 4585, 4527, 4584, 4526, 4492, 4488, 4509, + 4506, 4484, 4508, 4505, 4483, 4636, 4633, 4487, 4635, 4632, 4486, 4648, + 4639, 4491, 4647, 4638, 4490, 4510, 4507, 4482, 4637, 4634, 4485, 4649, + 4640, 4489, 4543, 4542, 4544, 4545, 4574, 4568, 4578, 4590, 4597, 4611, + 4580, 4511, 4513, 4533, 4522, 4591, 4598, 4512, 4514, 32383, 19955, + 19954, 19957, 19865, 19947, 19960, 19956, 12663, 18799, 18822, 18835, + 18763, 18823, 18778, 18779, 26260, 19099, 21671, 9654, 32431, 26277, + 26045, 26046, 26037, 26038, 26039, 26040, 26041, 26042, 26043, 26044, + 4007, 33852, 33861, 33855, 28690, 28699, 28689, 28696, 28698, 28688, + 4004, 33849, 4000, 33837, 4037, 33884, 3981, 33833, 4032, 33881, 4031, + 33880, 3989, 33841, 3994, 33840, 3993, 33839, 3928, 33796, 3927, 33795, + 3954, 33822, 3953, 33821, 3952, 33820, 3918, 33785, 33786, 12588, 19962, + 33771, 10314, 6630, 5101, 3867, 6622, 6631, 6623, 6628, 6627, 6629, + 18761, 26258, 15955, 15959, 32387, 19859, 32411, 32447, 19909, 19889, + 32392, 19866, 3961, 3960, 4033, 4034, 33735, 28693, 28700, 28695, 28692, + 33864, 33882, 32369, 32370, 17419, 33862, 33858, 33859, 33863, 33778, + 33776, 33777, 33779, 32409, 32464, 19900, 33829, 3976, 33828, 3975, + 19923, 4011, 7137, 32318, 28218, 7576, 4020, 33871, 19109, 31545, 28924, + 2568, 9697, 7594, 24856, 4026, 33873, 2795, 2801, 2802, 26684, 32320, + 15584, 33846, 4018, 27137, 26197, 3959, 3986, 33819, 33878, 33843, 33794, + 28114, 6225, 26061, 3864, 5361, 983, 24958, 6585, 7802, 7801, 28839, + 12555, 102, 14253, 25595, 35252, 32128, 32129, 32134, 32131, 32133, + 32132, 32130, 32127, 33731, 33806, 33850, 4006, 33869, 12581, 17423, + 22106, 12561, 10654, 20267, 16084, 26754, 32537, 23980, 25890, 2566, + 31166, 12855, 6099, 18983, 33460, 19797, 26848, 26682, 6105, 2646, 25783, + 33743, 33759, 33762, 33737, 33746, 33756, 3889, 3905, 3908, 3883, 3892, + 3902, 4019, 33809, 33790, 3917, 33851, 3938, 3923, 33780, 15585, 26050, + 11782, 3581, 3582, 23003, 23001, 23002, 33729, 10659, 32334, 26091, + 26092, 26093, 26094, 26095, 26096, 26097, 26098, 4036, 26090, 25520, + 25627, 33732, 9959, 9960, 9961, 9962, 9963, 9964, 33773, 33775, 3868, + 3870, 22875, 22876, 9999, 10002, 10001, 10000, 33800, 3934, 14254, 981, + 7810, 28833, 26843, 347, 12604, 12851, 28834, 2581, 12601, 25164, 31522, + 31521, 33705, 15435, 10400, 10401, 15942, 20266, 20265, 20264, 33418, + 15576, 21682, 21658, 21675, 20433, 10122, 32336, 7793, 12769, 23790, + 6235, 25329, 16085, 33447, 6588, 3977, 26976, 26888, 26056, 26971, 28223, + 3518, 28766, 33797, 33798, 3931, 3932, 28219, 28926, 26066, 4013, 31544, + 32248, 32247, 33791, 7811, 10041, 24855, 25768, 6167, 15077, 6643, 6236, + 24052, 368, 4027, 33876, 3957, 33817, 10500, 14934, 18750, 28809, 12549, + 4024, 26170, 26171, 2575, 14860, 25602, 26336, 18873, 15965, 3880, 27142, + 6620, 6227, 15540, 12852, 12853, 20362, 22894, 32319, 12642, 12598, + 12563, 26839, 28511, 28112, 15620, 25614, 31286, 15982, 14836, 12771, + 9059, 33801, 4014, 32376, 4017, 19941, 33844, 33810, 31178, 31165, 226, + 11904, 26079, 26071, 33451, 33959, 19940, 25604, 32446, 33832, 3979, + 6401, 14858, 22994, 14894, 2805, 14850, 25166, 14941, 24839, 14896, + 17912, 26981, 25163, 20268, 28837, 12638, 12636, 14879, 12632, 3935, + 33805, 28432, 28867, 6916, 24837, 3881, 25165, 14898, 25780, 26980, + 14849, 24838, 11781, 11776, 11774, 28106, 11775, 14872, 32268, 28109, + 31171, 24836, 14924, 28104, 3933, 33802, 11777, 6905, 14923, 28221, + 31816, 14857, 28431, 14917, 2794, 11780, 14874, 7807, 26979, 23738, + 19939, 32443, 19921, 32461, 4044, 33836, 33799, 3920, 14876, 19106, + 21679, 14940, 14907, 14908, 14868, 14869, 14891, 14890, 9071, 14877, + 14883, 14853, 26531, 12603, 26530, 21665, 21668, 21659, 21660, 21666, + 21669, 14887, 14900, 14886, 14899, 19098, 19096, 21664, 21667, 10024, + 10022, 10021, 10018, 10017, 10020, 10019, 10025, 10023, 10016, 10035, + 10032, 10031, 10028, 10027, 10030, 10029, 10036, 10034, 10026, 10014, + 10011, 10010, 10007, 10006, 10009, 10008, 10015, 10013, 10005, 14936, + 14939, 14895, 14865, 14913, 14901, 14920, 10492, 14904, 32122, 14926, + 9657, 14864, 3995, 31536, 31539, 3996, 14851, 14852, 28827, 14863, 26352, + 18863, 2658, 12654, 14892, 14931, 24132, 9065, 24134, 6693, 33888, 4050, + 4052, 4051, 14854, 14856, 14855, 31172, 14925, 33725, 14937, 24850, + 10333, 31519, 33875, 25608, 24846, 24845, 18797, 26284, 24960, 26181, + 29081, 33399, 21130, 19823, 20951, 28794, 28795, 33789, 982, 11836, + 19937, 32404, 18781, 26275, 12662, 17411, 17410, 18728, 18727, 18777, + 19845, 19828, 32355, 19963, 33781, 33782, 33783, 33857, 33860, 21131, + 21125, 21134, 21128, 21133, 21127, 21132, 21126, 21135, 21129, 32505, + 10482, 978, 7116, 26238, 19831, 19835, 19825, 19829, 19840, 19827, 19832, + 19839, 19830, 19841, 19844, 5027, 4772, 4900, 4773, 4964, 4837, 4901, + 4774, 4996, 4869, 4933, 4806, 4965, 4838, 4902, 4775, 5012, 4885, 4949, + 4822, 4981, 4854, 4918, 4791, 4997, 4870, 4934, 4807, 4966, 4839, 4903, + 4776, 5020, 4893, 4957, 4830, 4989, 4862, 4926, 4799, 5005, 4878, 4942, + 4815, 4974, 4847, 4911, 4784, 5013, 4886, 4950, 4823, 4982, 4855, 4919, + 4792, 4998, 4871, 4935, 4808, 4967, 4840, 4904, 4777, 5024, 4897, 4961, + 4834, 4993, 4866, 4930, 4803, 5009, 4882, 4946, 4819, 4978, 4851, 4915, + 4788, 5017, 4890, 4954, 4827, 4986, 4859, 4923, 4796, 5002, 4875, 4939, + 4812, 4971, 4844, 4908, 4781, 5021, 4894, 4958, 4831, 4990, 4863, 4927, + 4800, 5006, 4879, 4943, 4816, 4975, 4848, 4912, 4785, 5014, 4887, 4951, + 4824, 4983, 4856, 4920, 4793, 4999, 4872, 4936, 4809, 4968, 4841, 4905, + 4778, 5026, 4899, 4963, 4836, 4995, 4868, 4932, 4805, 5011, 4884, 4948, + 4821, 4980, 4853, 4917, 4790, 5019, 4892, 4956, 4829, 4988, 4861, 4925, + 4798, 5004, 4877, 4941, 4814, 4973, 4846, 4910, 4783, 5023, 4896, 4960, + 4833, 4992, 4865, 4929, 4802, 5008, 4881, 4945, 4818, 4977, 4850, 4914, + 4787, 5016, 4889, 4953, 4826, 4985, 4858, 4922, 4795, 5001, 4874, 4938, + 4811, 4970, 4843, 4907, 4780, 5025, 4898, 4962, 4835, 4994, 4867, 4931, + 4804, 5010, 4883, 4947, 4820, 4979, 4852, 4916, 4789, 5018, 4891, 4955, + 4828, 4987, 4860, 4924, 4797, 5003, 4876, 4940, 4813, 4972, 4845, 4909, + 4782, 5022, 4895, 4959, 4832, 4991, 4864, 4928, 4801, 5007, 4880, 4944, + 4817, 4976, 4849, 4913, 4786, 5015, 4888, 4952, 4825, 4984, 4857, 4921, + 4794, 5000, 4873, 4937, 4810, 4969, 4842, 4906, 4779, 26456, 26455, + 18964, 26401, 18788, 26457, 18966, 26403, 10438, 32486, 32523, 10462, + 18968, 26405, 18948, 26449, 26458, 26375, 32488, 10442, 26388, 26387, + 26451, 26453, 26452, 18913, 26395, 18967, 26404, 18890, 26372, 18910, + 26367, 24091, 24071, 24097, 24069, 28316, 28329, 24096, 24068, 28313, + 28328, 26475, 12547, 28314, 24066, 12548, 26476, 24067, 24095, 33714, + 2559, 2558, 2561, 2562, 26353, 18862, 32028, 4462, 32030, 32029, 19919, + 19883, 975, 7113, 26362, 18879, 27152, 26380, 18895, 26371, 18786, 32526, + 18740, 18739, 32343, 32342, 18741, 32344, 18738, 32341, 18927, 26418, + 32500, 10477, 18921, 26412, 32497, 10474, 18926, 26417, 32499, 10476, + 18920, 26411, 32496, 10473, 18923, 32495, 26416, 10471, 18925, 18919, + 26414, 26409, 18924, 18918, 26415, 26410, 32494, 10472, 26250, 11921, + 31820, 18883, 26364, 26363, 19064, 18886, 13273, 28897, 18885, 29067, + 18836, 26255, 32352, 10406, 32142, 35265, 35266, 18828, 26321, 18830, + 26323, 35260, 35256, 35261, 35257, 18809, 26299, 18807, 26296, 18806, + 26297, 18734, 26231, 18735, 26237, 10346, 10371, 18743, 26241, 10321, + 33432, 21553, 26236, 21554, 960, 5, 28448, 28449, 32245, 26187, 961, + 26188, 24235, 24234, 21552, 21551, 21546, 21545, 21550, 21548, 21549, + 21547, 26209, 11883, 11882, 11880, 11881, 6632, 6920, 6907, 6911, 6908, + 6633, 6625, 6634, 32340, 6915, 6638, 6853, 6921, 6624, 6626, 28758, + 28753, 28824, 28810, 28811, 32278, 32121, 32119, 26679, 32118, 26313, + 18814, 33396, 4475, 4476, 4043, 31827, 31828, 33814, 3942, 18833, 26326, + 18751, 26251, 15779, 31754, 15856, 10399, 28701, 15781, 27172, 11922, + 11923, 15622, 13157, 31510, 10419, 10420, 3921, 3962, 33774, 3869, 11936, + 11939, 11935, 11938, 11934, 11937, 26548, 26182, 28275, 26183, 3857, + 3856, 10353, 32138, 18850, 26339, 31829, 22297, 23492, 23490, 23491, + 23500, 23499, 23495, 23496, 32280, 32279, 23494, 22700, 28922, 26047, + 12574, 15937, 15929, 6925, 980, 19181, 19182, 19183, 15930, 26049, 15934, + 15931, 15935, 15933, 15936, 15932, 16082, 17412, 35262, 35263, 35264, + 25882, 25887, 25885, 25881, 25884, 25883, 25886, 22290, 22291, 22293, + 22292, 25878, 25879, 33449, 22993, 22992, 26887, 28195, 22988, 22989, + 6854, 22990, 6648, 25880, 22294, 22991, 15952, 26358, 35258, 374, 15948, + 32329, 32330, 15947, 15946, 32331, 32327, 15945, 32326, 15944, 32328, + 15949, 7129, 7135, 10354, 10355, 7130, 19811, 19819, 10344, 10345, 32276, + 32277, 28134, 28133, 19816, 19813, 19821, 19812, 19820, 19810, 19814, + 19809, 28185, 19818, 19817, 35259, 35255, 11928, 15623, 32136, 32137, + 31822, 31821, 27978, 7595, 11929, 365, 1060, 11918, 25888, 11919, 10334, + 32271, 31530, 11927, 11931, 19081, 13292, 19082, 13293, 19073, 13279, + 19076, 13282, 19074, 13280, 19075, 13281, 19072, 13283, 19066, 13275, + 19065, 13274, 19063, 13271, 19061, 13269, 19060, 13268, 19059, 13272, + 19062, 13270, 28119, 28117, 28120, 28118, 10381, 10380, 10377, 10376, + 27977, 27976, 27975, 27974, 10347, 10349, 10348, 13287, 13276, 19070, + 13290, 19071, 13291, 28193, 17420, 28194, 17421, 11924, 25968, 28913, + 25969, 28914, 25971, 28916, 25967, 28912, 25970, 28915, 25966, 28911, + 10350, 10359, 28908, 29080, 28907, 29079, 28906, 29078, 28905, 29077, + 28900, 29072, 28901, 29073, 28899, 29071, 28902, 29074, 28579, 28666, + 7124, 7126, 7125, 7127, 28895, 29066, 28896, 29065, 29069, 29068, 11835, + 25786, 32114, 12626, 24063, 27151, 27157, 27154, 25609, 33398, 10368, + 33397, 10366, 19822, 27158, 27156, 27155, 10335, 10363, 10357, 26191, + 10137, 33414, 33413, 10405, 25373, 25372, 32141, 32144, 32135, 32148, + 32147, 10379, 10378, 32145, 17409, 10362, 33886, 23501, 24080, 24107, + 28326, 28339, 18791, 18917, 32490, 10444, 24077, 24104, 28323, 28336, + 18793, 32347, 26384, 26385, 18899, 18900, 28694, 28687, 28697, 28691, + 9951, 9952, 9950, 9949, 10317, 3948, 33815, 4041, 33887, 3982, 33834, + 33812, 3939, 15551, 3943, 3965, 33826, 3968, 33830, 4001, 4002, 33847, + 3941, 33813, 4038, 33883, 18737, 31523, 18736, 19833, 18956, 18955, + 18957, 18958, 18893, 18903, 18902, 18951, 18953, 18952, 18887, 33713, + 11920, 26184, 18880, 26370, 26369, 18982, 26465, 26185, 26360, 31819, + 18882, 18881, 26361, 10458, 27149, 27147, 33827, 4003, 33848, 3992, + 33838, 14884, 14897, 14861, 14859, 14862, 28121, 2656, 28122, 2655, 3698, + 27148, 18933, 32509, 26434, 10447, 18795, 32351, 24102, 24075, 28321, + 28334, 18945, 32520, 26446, 10459, 7121, 973, 18944, 32511, 26445, 10449, + 35536, 35536, 24103, 24076, 28322, 28335, 18935, 32519, 26436, 10457, + 15572, 33427, 18934, 32510, 26435, 10448, 18946, 32521, 26447, 10460, + 18916, 32489, 26398, 10446, 970, 971, 969, 972, 26175, 26176, 23975, + 23976, 12633, 26399, 11926, 29179, 31534, 31538, 31535, 31537, 3955, + 4035, 3997, 3929, 10451, 10452, 32513, 32514, 18938, 26439, 18937, 26438, + 3872, 3873, 3874, 3875, 3876, 3878, 3877, 3879, 26222, 26223, 26220, + 26221, 26217, 26219, 26216, 26218, 32534, 32339, 25181, 25180, 25182, + 2800, 6923, 6637, 4008, 3919, 32246, 15550, 4042, 3972, 3964, 3967, 3970, + 23981, 32046, 4450, 19094, 26532, 33804, 26533, 28651, 32322, 13819, + 25894, 25893, 25892, 25891, 32113, 25993, 2576, 15606, 25767, 23758, + 33831, 3922, 32158, 9061, 14834, 35344, 17245, 1055, 97, 33537, 25905, + 18762, 26259, 28840, 28841, 18954, 32525, 26454, 10464, 11941, 11940, + 26977, 26674, 26672, 26673, 26671, 26675, 26676, 11925, 32333, 26968, + 10402, 25528, 26198, 15079, 13057, 13059, 13093, 13067, 13063, 13094, + 13101, 13064, 13100, 13073, 13069, 13068, 13062, 13104, 13075, 13076, + 13077, 13078, 13080, 13082, 13086, 13090, 13103, 13065, 13102, 13079, + 13081, 13083, 13092, 13061, 13085, 13096, 13095, 13097, 13087, 13099, + 13088, 13089, 13098, 13071, 13058, 13070, 13066, 13072, 13084, 13091, + 13074, 13060, 13105, 13107, 13141, 13115, 13111, 13142, 13149, 13112, + 13148, 13121, 13117, 13116, 13110, 13152, 13123, 13124, 13125, 13126, + 13128, 13130, 13134, 13138, 13151, 13113, 13150, 13127, 13129, 13131, + 13140, 13109, 13133, 13144, 13143, 13145, 13135, 13147, 13136, 13137, + 13146, 13119, 13106, 13118, 13114, 13120, 13132, 13139, 13122, 13108, + 17644, 18291, 17647, 17738, 17756, 18025, 18517, 17587, 18210, 17633, + 18270, 17902, 18684, 17465, 17666, 17806, 17807, 18637, 17879, 18654, + 18636, 17592, 18217, 18582, 18138, 18558, 18372, 17950, 18709, 22418, + 17782, 17906, 7603, 7697, 7653, 7747, 7617, 7711, 7614, 7708, 7658, 7752, + 7648, 7742, 7652, 7746, 7619, 7713, 7650, 7744, 7656, 7750, 7622, 7716, + 7627, 7721, 7659, 7753, 7660, 7754, 7623, 7717, 7628, 7722, 7655, 7749, + 7657, 7751, 7649, 7743, 7651, 7745, 7661, 7755, 7625, 7719, 7621, 7715, + 7654, 7748, 7644, 7738, 7611, 7705, 7639, 7733, 7607, 7701, 7612, 7706, + 7613, 7707, 7608, 7702, 7637, 7731, 7647, 7741, 7609, 7703, 7635, 7729, + 7638, 7732, 7602, 7696, 7610, 7704, 7630, 7724, 7631, 7725, 7626, 7720, + 7633, 7727, 7632, 7726, 7629, 7723, 7636, 7730, 7634, 7728, 7642, 7736, + 7640, 7734, 7641, 7735, 7643, 7737, 7757, 7758, 7759, 7761, 7762, 7756, + 7760, 7605, 7699, 7606, 7700, 7601, 7600, 7599, 7604, 7698, 35536, 35536, + 35536, 35536, 35536, 7695, 7692, 7693, 7694, 7690, 7691, 7763, 12916, + 12942, 12927, 12948, 12949, 12947, 12937, 12938, 12950, 12931, 12940, + 12943, 12945, 12951, 12933, 12936, 12941, 12935, 12939, 12952, 12932, + 12930, 12926, 12946, 12934, 12922, 12925, 12929, 12924, 12923, 12944, + 12928, 12917, 12921, 12919, 12954, 12918, 12920, 35536, 12953, 35536, + 35536, 35536, 35536, 35536, 12915, 35536, 35536, 31769, 31789, 31790, + 31770, 31772, 31759, 31798, 31785, 31788, 31786, 31787, 31808, 31797, + 31773, 31763, 31775, 31791, 31758, 31767, 31792, 31796, 31774, 31764, + 31803, 31768, 31809, 31783, 31757, 31765, 31799, 31800, 31801, 31762, + 31766, 31802, 31811, 31793, 31794, 31771, 31761, 31756, 31776, 31778, + 31777, 31779, 31780, 31795, 31781, 31804, 31805, 31806, 31782, 31760, + 31784, 31807, 31810, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 31812, 31813, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 31755, 12376, 12193, 12280, + 12320, 12296, 11983, 12358, 12021, 12211, 12202, 12103, 12436, 12051, + 12036, 12367, 12327, 12012, 12227, 12240, 12088, 12092, 12091, 12090, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 12310, + 12316, 12314, 12311, 12313, 12312, 12315, 35536, 12002, 12008, 12006, + 12003, 12005, 12004, 12007, 35536, 12426, 12432, 12430, 12427, 12429, + 12428, 12431, 35536, 11995, 12001, 11999, 11996, 11998, 11997, 12000, + 35536, 12262, 12268, 12266, 12263, 12265, 12264, 12267, 35536, 12171, + 12177, 12175, 12172, 12174, 12173, 12176, 35536, 12403, 12409, 12407, + 12404, 12406, 12405, 12408, 35536, 12114, 12120, 12118, 12115, 12117, + 12116, 12119, 35536, 7183, 7221, 7218, 7185, 7215, 7216, 7222, 7189, + 7190, 7191, 7198, 7220, 7192, 7186, 7214, 7211, 7213, 7217, 7201, 7200, + 7219, 7187, 7223, 7197, 7184, 7210, 7206, 7208, 7195, 7209, 7182, 7194, + 26233, 26232, 18813, 26303, 18755, 26248, 26070, 26069, 10320, 18816, + 26311, 26078, 18796, 26283, 10661, 25371, 12625, 26193, 15613, 10319, + 10443, 32473, 10322, 10370, 15961, 25330, 15609, 31825, 18776, 26273, + 31824, 31823, 18845, 26317, 32055, 32059, 4455, 4458, 18801, 26288, + 18754, 26253, 32274, 24829, 28754, 12592, 26208, 33430, 26468, 33947, + 32250, 26068, 26080, 32261, 9648, 9649, 32251, 32044, 32286, 31541, + 28854, 33433, 33930, 6104, 10338, 26207, 10341, 9655, 10358, 15962, + 15963, 19855, 19856, 10356, 10316, 32146, 21655, 25370, 26027, 7766, + 7803, 7804, 31913, 21654, 21656, 18811, 26301, 18810, 26300, 32036, + 32040, 4441, 4445, 24236, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 7007, 6960, 7010, + 7009, 7008, 7003, 6931, 7028, 7042, 7041, 6964, 7011, 7024, 7023, 6993, + 6992, 6991, 6990, 7020, 7029, 7019, 7018, 6979, 6978, 6982, 7006, 35536, + 6963, 7026, 7000, 6965, 6998, 6958, 7034, 7033, 6972, 7002, 7001, 7012, + 6962, 6966, 6987, 6929, 6971, 7022, 7021, 6934, 7017, 6945, 7040, 7039, + 7038, 7037, 6995, 7025, 7005, 6970, 7043, 6933, 6932, 6994, 6999, 6976, + 6975, 6974, 7030, 6961, 7036, 7035, 6949, 7013, 6981, 6948, 6947, 6973, + 6957, 7014, 7032, 7031, 6959, 6941, 6989, 6988, 6944, 6942, 6997, 6996, + 7004, 6935, 6951, 6943, 6953, 6939, 6969, 6968, 6967, 6937, 6980, 6956, + 6930, 6977, 6938, 6955, 6946, 7015, 7016, 6940, 6986, 6936, 6984, 6952, + 6985, 6954, 7027, 6983, 6950, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 16281, 16263, 16197, 16317, + 16305, 16249, 16349, 16264, 16277, 16260, 16212, 16216, 16201, 16186, + 16252, 16338, 16284, 16255, 16289, 16366, 16323, 16294, 16246, 16351, + 16195, 16306, 16182, 16286, 16157, 16275, 16211, 16209, 16304, 16232, + 16234, 16214, 16164, 16363, 16189, 16297, 16251, 16333, 16256, 16184, + 16322, 16273, 16295, 16364, 16282, 16347, 16207, 16312, 16198, 16266, + 16350, 16313, 16172, 16331, 16173, 16325, 16243, 16240, 16203, 16241, + 16174, 16292, 16303, 16196, 16159, 16334, 16279, 16336, 16302, 16276, + 16346, 16257, 16326, 16191, 16357, 16202, 16185, 16231, 16180, 16324, + 16356, 16223, 16181, 16218, 16200, 16239, 16318, 16220, 16188, 16204, + 16287, 16253, 16267, 16341, 16330, 16262, 16368, 16221, 16168, 16314, + 16199, 16359, 16335, 16194, 16217, 16319, 16155, 16328, 16321, 16345, + 16235, 16179, 16329, 16160, 16296, 16315, 16254, 16280, 16310, 16229, + 16285, 16158, 16288, 16208, 16175, 16268, 16269, 16307, 16156, 16271, + 16342, 16283, 16169, 16327, 16187, 16236, 16340, 16250, 16165, 16355, + 16183, 16358, 16308, 16248, 16320, 16352, 16176, 16290, 16161, 16309, + 16299, 16298, 16230, 16171, 16178, 16162, 16272, 16354, 16190, 16362, + 16193, 16353, 16233, 16265, 16238, 16274, 16316, 16311, 16291, 16167, + 16365, 16219, 16258, 16337, 16261, 16332, 16259, 16361, 16226, 16210, + 16244, 16227, 16247, 16170, 16339, 16242, 16222, 16300, 16177, 16237, + 16224, 16163, 16301, 16192, 16360, 16245, 16367, 16270, 16166, 16215, + 16228, 16344, 16206, 16293, 16278, 16213, 16343, 16205, 16348, 16225, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 15646, 15644, 15645, 15643, + 15658, 15654, 15653, 15650, 15651, 15652, 15648, 15647, 15655, 15649, + 15659, 15657, 15743, 15642, 15741, 10126, 15981, 15742, 15641, 15661, + 18733, 26230, 18752, 26249, 18748, 26246, 18827, 26320, 18742, 26240, + 25907, 13050, 18824, 26315, 18829, 26322, 18832, 26325, 18831, 26324, + 33712, 26190, 10352, 19853, 25908, 14730, 14723, 14720, 14726, 14725, + 14728, 14727, 14731, 14729, 15739, 15738, 15660, 15737, 14389, 14388, + 33718, 33405, 33408, 33406, 33409, 33407, 6906, 15735, 14724, 14722, + 14721, 33404, 20512, 25525, 15740, 15736, 35536, 15455, 15441, 15457, + 15535, 15459, 15537, 15456, 15534, 15458, 15536, 15496, 15486, 15498, + 15488, 15500, 15490, 15497, 15487, 15499, 15489, 15460, 15521, 15462, + 15523, 15464, 15525, 15461, 15522, 15463, 15524, 15516, 15481, 15518, + 15483, 15454, 15520, 15485, 15517, 15482, 15519, 15484, 15476, 15478, + 15480, 15477, 15479, 15491, 15471, 15506, 15493, 15465, 15508, 15495, + 15474, 15510, 15492, 15472, 15507, 15494, 15473, 15509, 15501, 15503, + 15505, 15502, 15504, 15448, 15530, 15450, 15532, 15449, 15531, 15511, + 15513, 15515, 15512, 15514, 15444, 15526, 15528, 15527, 15529, 15475, + 15533, 15451, 15452, 35536, 35536, 7391, 7390, 16607, 16606, 15539, + 15538, 15440, 16604, 16534, 16463, 16536, 16597, 16538, 16599, 16535, + 16596, 16537, 16598, 16559, 16549, 16561, 16551, 16563, 16553, 16560, + 16550, 16562, 16552, 16539, 16584, 16541, 16586, 16543, 16588, 16540, + 16585, 16542, 16587, 16574, 16544, 16576, 16546, 16521, 16578, 16548, + 16575, 16545, 16577, 16547, 16501, 16503, 16505, 16502, 16504, 16554, + 16478, 16564, 16556, 16472, 16566, 16558, 16481, 16568, 16555, 16479, + 16565, 16557, 16480, 16567, 16496, 16482, 16499, 16497, 16498, 16526, + 16593, 16528, 16595, 16527, 16594, 16569, 16571, 16573, 16570, 16572, + 16522, 16589, 16591, 16590, 16592, 16500, 16583, 16516, 16517, 16579, + 16581, 16580, 16582, 16603, 16605, 16602, 16600, 16601, 35536, 35536, + 35536, 35536, 35536, 4422, 4430, 4429, 4427, 4426, 4433, 4398, 4418, + 4386, 4414, 4428, 4424, 4431, 4435, 4411, 4417, 4421, 4432, 4410, 4416, + 4420, 4366, 4402, 4378, 4383, 4367, 4384, 4369, 4409, 4371, 4379, 4372, + 4380, 4385, 4391, 4376, 4397, 4434, 4399, 4388, 4394, 4405, 4401, 35536, + 14642, 14686, 14643, 14648, 14649, 14651, 14678, 14689, 14664, 14665, + 14667, 14669, 14676, 14672, 14668, 14675, 14644, 14654, 14688, 14655, + 14679, 14691, 14636, 14631, 14685, 14630, 14641, 14677, 14662, 14715, + 14626, 14629, 14712, 14713, 14633, 14632, 14699, 14698, 14716, 14695, + 14696, 14717, 14704, 14718, 14694, 14693, 14697, 14708, 14634, 14714, + 14635, 14719, 14687, 14650, 14653, 14652, 14666, 14673, 14670, 14671, + 14674, 14645, 14647, 14646, 14640, 14661, 14659, 14656, 14657, 14660, + 14658, 14638, 14639, 14681, 14682, 14684, 14683, 14680, 14663, 14692, + 14701, 14703, 14702, 14637, 14690, 14700, 14705, 14706, 14707, 14710, + 14709, 14711, 14627, 14628, 35536, 15635, 15638, 15637, 15633, 15631, + 15627, 15634, 15629, 15625, 15628, 15636, 15632, 15626, 15639, 15640, + 15630, 4423, 4412, 4425, 4389, 4382, 4381, 4408, 4404, 4396, 4373, 4392, + 4377, 4395, 4400, 4368, 4370, 4375, 4407, 4403, 4393, 4364, 4365, 4363, + 4362, 4387, 4419, 4413, 4361, 4390, 4415, 4406, 4374, 7074, 7077, 7078, + 7076, 7064, 7050, 7056, 7045, 7055, 7068, 7057, 7053, 7046, 7054, 7051, + 7080, 7044, 7063, 7059, 7072, 7079, 7049, 7058, 7067, 7066, 7073, 7071, + 7060, 7062, 7075, 7070, 7065, 7047, 7052, 7061, 7081, 7048, 7069, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 15656, 16519, + 16531, 16532, 16520, 16530, 16506, 16508, 16510, 16507, 16509, 16533, + 16511, 16513, 16515, 16512, 16514, 25385, 25389, 25401, 25395, 25387, + 25393, 25397, 25403, 25378, 25376, 25383, 25399, 25391, 25381, 25386, + 25390, 25402, 25396, 25388, 25394, 25398, 25404, 25379, 25377, 25384, + 25400, 25392, 25382, 25380, 25442, 25441, 35536, 25440, 25436, 25434, + 25415, 25413, 25433, 25425, 25410, 25419, 25435, 25418, 25412, 25438, + 25437, 25417, 25409, 25432, 25430, 25439, 25427, 25420, 25428, 25411, + 25406, 25416, 25423, 25407, 25429, 25431, 25408, 25421, 25405, 25414, + 25422, 25426, 25424, 6725, 6713, 6735, 6714, 6879, 6893, 6881, 6863, + 6860, 6876, 6874, 6856, 25524, 6894, 6900, 6899, 6896, 6895, 6898, 6897, + 6902, 6901, 6880, 6882, 6888, 6887, 6884, 6883, 6673, 6677, 6689, 6683, + 6675, 6681, 6685, 6666, 6664, 6662, 6671, 6687, 6679, 6669, 6674, 6678, + 6690, 6684, 6676, 6682, 6686, 6667, 6665, 6663, 6672, 6688, 6680, 6670, + 6800, 6799, 6668, 17243, 6748, 6744, 6742, 6710, 6708, 6740, 6731, 6705, + 6723, 6743, 6721, 6707, 6746, 6745, 6719, 6703, 6734, 6739, 6712, 6736, + 6724, 6737, 6706, 6699, 6715, 6730, 6720, 6709, 6733, 6704, 6741, 6696, + 6747, 6727, 6700, 6698, 6711, 6701, 6716, 6717, 6729, 6718, 6728, 6738, + 6732, 6702, 6726, 6694, 6722, 6886, 6885, 6890, 6889, 6862, 6864, 6870, + 6869, 6866, 6865, 6868, 6867, 6872, 6871, 6859, 15726, 15729, 15730, + 15668, 15731, 15727, 15728, 15667, 15733, 15734, 15732, 15700, 28539, + 28505, 28507, 19180, 6794, 6796, 6798, 6795, 6797, 6757, 6759, 6761, + 6758, 6760, 6777, 6779, 6781, 6778, 6780, 6782, 6784, 6786, 6783, 6785, + 6767, 6769, 6771, 6768, 6770, 6752, 6754, 6756, 6753, 6755, 6762, 6764, + 6766, 6763, 6765, 6791, 6793, 6792, 6772, 6774, 6776, 6773, 6775, 6787, + 6789, 6788, 6790, 28503, 28464, 28466, 28465, 28467, 28542, 28543, 28706, + 28506, 28499, 28632, 28636, 28551, 28549, 28550, 28514, 28515, 28519, + 28518, 28565, 28517, 28552, 28555, 28553, 28554, 28520, 28521, 28562, + 28563, 28564, 28561, 28560, 28672, 28673, 28680, 28674, 28675, 28490, + 28494, 28495, 28685, 28625, 28626, 28527, 28639, 28640, 28471, 28648, + 28646, 28647, 28474, 28534, 28533, 28473, 28535, 28528, 28645, 28643, + 28529, 28644, 28642, 28476, 28649, 28475, 28532, 28650, 28530, 28531, + 28589, 28590, 28593, 28592, 28591, 28599, 28600, 28601, 28596, 28597, + 28598, 28704, 28703, 28705, 28667, 28668, 28670, 28669, 28665, 28664, + 28686, 15724, 15725, 15707, 15709, 15716, 15715, 15722, 15720, 15711, + 15718, 15710, 15713, 15706, 15708, 15717, 15714, 15723, 15721, 15712, + 15719, 15701, 15705, 15704, 15703, 15702, 28537, 28489, 28469, 28472, + 28637, 28653, 28491, 28493, 28492, 28547, 28500, 28504, 28501, 28502, + 28481, 28641, 28624, 28606, 28588, 28548, 28570, 28594, 28524, 28478, + 28567, 28654, 28627, 28607, 28608, 28621, 28571, 28540, 28566, 28618, + 28522, 28684, 28609, 28622, 28498, 28573, 28513, 28628, 28610, 28603, + 28482, 28556, 28605, 28484, 28587, 28559, 28604, 28483, 28586, 28558, + 28583, 28584, 28638, 28569, 28620, 28523, 28661, 28662, 28663, 28658, + 28629, 28611, 28623, 28659, 28630, 28612, 28614, 28575, 28615, 28660, + 28631, 28613, 28616, 28576, 28617, 28568, 28585, 28468, 28477, 28487, + 28488, 28485, 28480, 28496, 28525, 28526, 28536, 28541, 28572, 28557, + 28574, 28580, 28581, 28578, 28582, 28595, 28602, 28619, 28655, 28656, + 28652, 28657, 28681, 28682, 28702, 28470, 28462, 15699, 15684, 15672, + 15691, 15690, 15697, 15695, 15686, 15693, 15685, 15688, 15683, 15671, + 15692, 15689, 15698, 15696, 15687, 15694, 15673, 15681, 15679, 15678, + 15675, 15674, 15677, 15676, 15682, 15680, 15669, 15670, 28516, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 15417, 15420, 15381, + 15431, 15428, 15376, 15414, 15393, 15410, 15427, 15405, 15411, 15386, + 15388, 15398, 15432, 15385, 15429, 15369, 15375, 15373, 15392, 15412, + 15406, 15394, 15391, 15399, 15390, 15415, 15418, 15397, 15383, 15407, + 15389, 15404, 15384, 15419, 15402, 15400, 15379, 15378, 15396, 15374, + 15377, 15387, 15403, 15401, 15421, 15408, 15416, 15413, 15425, 15380, + 15423, 15371, 15422, 15424, 15426, 15382, 15430, 15395, 15409, 15370, + 15372, 34469, 34476, 34468, 34475, 34473, 34474, 34471, 34472, 35244, + 35245, 35242, 35243, 35241, 35239, 35240, 35248, 35249, 35246, 35247, + 35251, 35250, 35116, 34136, 34137, 34130, 34135, 34133, 34134, 34131, + 34132, 34140, 34141, 34138, 34139, 34121, 34119, 34120, 34144, 34145, + 34142, 34143, 34129, 34127, 34128, 34125, 34126, 34118, 34124, 34123, + 34122, 34150, 34151, 34146, 34149, 34148, 34147, 34848, 34849, 34843, + 34847, 34846, 34844, 34845, 34852, 34853, 34850, 34851, 34837, 34835, + 34836, 34856, 34857, 34854, 34855, 34841, 34842, 34834, 34840, 34839, + 34838, 34862, 34863, 34858, 34861, 34860, 34859, 34104, 34105, 34098, + 34103, 34101, 34102, 34099, 34100, 34108, 34109, 34106, 34107, 34089, + 34087, 34088, 34112, 34113, 34110, 34111, 34097, 34095, 34096, 34093, + 34094, 34086, 34092, 34091, 34090, 34116, 34117, 34114, 34115, 34651, + 34652, 34646, 34650, 34649, 34647, 34648, 34655, 34656, 34653, 34654, + 34659, 34660, 34657, 34658, 34665, 34666, 34661, 34664, 34663, 34662, + 34671, 34672, 34667, 34670, 34669, 34668, 34394, 34395, 34389, 34393, + 34392, 34390, 34391, 34398, 34399, 34396, 34397, 34383, 34381, 34382, + 34402, 34403, 34400, 34401, 34387, 34388, 34380, 34386, 34385, 34384, + 34408, 34404, 34407, 34406, 34405, 34630, 34631, 34625, 34629, 34628, + 34626, 34627, 34634, 34635, 34632, 34633, 34618, 34619, 34616, 34617, + 34638, 34639, 34636, 34637, 34645, 34644, 34623, 34624, 34615, 34622, + 34621, 34620, 34642, 34643, 34640, 34641, 34275, 34276, 34273, 34274, + 34271, 34272, 34269, 34270, 34268, 34266, 34267, 34285, 34286, 34281, + 34284, 34283, 34282, 34279, 34280, 34277, 34278, 35094, 35095, 35088, + 35093, 35091, 35092, 35089, 35090, 35098, 35099, 35096, 35097, 35102, + 35103, 35100, 35101, 35087, 35086, 35108, 35109, 35104, 35107, 35106, + 35105, 35114, 35115, 35110, 35113, 35112, 35111, 34253, 34254, 34248, + 34252, 34251, 34249, 34250, 34260, 34261, 34258, 34259, 34242, 34241, + 34264, 34265, 34262, 34263, 34257, 34255, 34256, 34246, 34247, 34240, + 34245, 34244, 34243, 35073, 35074, 35068, 35072, 35071, 35069, 35070, + 35080, 35081, 35078, 35079, 35061, 35062, 35059, 35060, 35084, 35085, + 35082, 35083, 35077, 35075, 35076, 35066, 35067, 35058, 35065, 35064, + 35063, 34227, 34228, 34222, 34226, 34225, 34223, 34224, 34234, 34235, + 34232, 34233, 34216, 34214, 34215, 34238, 34239, 34236, 34237, 34231, + 34229, 34230, 34220, 34221, 34213, 34219, 34218, 34217, 34677, 34678, + 34673, 34676, 34675, 34674, 34684, 34685, 34682, 34683, 34688, 34689, + 34686, 34687, 34681, 34679, 34680, 34694, 34695, 34690, 34693, 34692, + 34691, 34424, 34425, 34418, 34423, 34421, 34422, 34419, 34420, 34428, + 34429, 34426, 34427, 34413, 34412, 34410, 34411, 34409, 34417, 34415, + 34416, 34414, 34822, 34823, 34817, 34821, 34820, 34818, 34819, 34826, + 34824, 34825, 34811, 34809, 34810, 34832, 34833, 34830, 34831, 34829, + 34827, 34828, 34815, 34816, 34808, 34814, 34813, 34812, 34362, 34363, + 34357, 34361, 34360, 34358, 34359, 34372, 34373, 34370, 34371, 34351, + 34349, 34350, 34369, 34367, 34368, 34366, 34364, 34365, 34355, 34356, + 34348, 34354, 34353, 34352, 34378, 34379, 34374, 34377, 34376, 34375, + 34577, 34578, 34571, 34576, 34574, 34575, 34572, 34573, 34581, 34582, + 34579, 34580, 34561, 34562, 34559, 34560, 34585, 34586, 34583, 34584, + 34570, 34568, 34569, 34566, 34567, 34558, 34565, 34564, 34563, 34591, + 34592, 34587, 34590, 34589, 34588, 34331, 34332, 34325, 34330, 34328, + 34329, 34326, 34327, 34335, 34336, 34333, 34334, 34318, 34319, 34316, + 34317, 34343, 34344, 34341, 34342, 34339, 34340, 34337, 34338, 34323, + 34324, 34315, 34322, 34321, 34320, 34544, 34545, 34539, 34543, 34542, + 34540, 34541, 34548, 34549, 34546, 34547, 34533, 34531, 34532, 34556, + 34557, 34554, 34555, 34552, 34553, 34550, 34551, 34537, 34538, 34530, + 34536, 34535, 34534, 34291, 34292, 34287, 34290, 34288, 34289, 34305, + 34306, 34303, 34304, 34296, 34297, 34294, 34295, 34313, 34314, 34311, + 34312, 34309, 34310, 34307, 34308, 34301, 34302, 34293, 34300, 34299, + 34298, 34614, 34613, 34607, 34608, 34605, 34606, 34596, 34594, 34595, + 34611, 34612, 34609, 34610, 34604, 34602, 34603, 34600, 34601, 34593, + 34599, 34598, 34597, 34443, 34444, 34437, 34442, 34440, 34441, 34438, + 34439, 34447, 34448, 34445, 34446, 34432, 34433, 34430, 34431, 34451, + 34452, 34449, 34450, 34436, 34434, 34435, 34704, 34702, 34703, 34707, + 34708, 34705, 34706, 34697, 34698, 34696, 34711, 34712, 34709, 34710, + 34701, 34699, 34700, 34347, 34346, 34345, 34462, 34463, 34460, 34461, + 34455, 34456, 34453, 34454, 34466, 34467, 34464, 34465, 34459, 34457, + 34458, 35128, 35129, 35126, 35127, 35119, 35117, 35118, 35125, 35123, + 35124, 35122, 35120, 35121, 35191, 35192, 35186, 35190, 35189, 35187, + 35188, 35227, 35228, 35225, 35226, 35180, 35178, 35179, 35231, 35232, + 35229, 35230, 35224, 35222, 35223, 35184, 35185, 35177, 35183, 35182, + 35181, 35237, 35238, 35233, 35236, 35235, 35234, 34197, 34198, 34191, + 34196, 34194, 34195, 34192, 34193, 34201, 34202, 34199, 34200, 34182, + 34180, 34181, 34205, 34206, 34203, 34204, 34190, 34188, 34189, 34186, + 34187, 34179, 34185, 34184, 34183, 34211, 34212, 34207, 34210, 34209, + 34208, 35205, 35206, 35199, 35204, 35202, 35203, 35200, 35201, 35209, + 35210, 35207, 35208, 35198, 35196, 35197, 35195, 35193, 35194, 35215, + 35211, 35214, 35213, 35212, 35220, 35221, 35216, 35219, 35218, 35217, + 34794, 34795, 34789, 34793, 34792, 34790, 34791, 34798, 34799, 34796, + 34797, 34782, 34781, 34788, 34787, 34807, 34806, 34786, 34780, 34785, + 34784, 34783, 34804, 34805, 34800, 34803, 34802, 34801, 35039, 35040, + 35034, 35038, 35037, 35035, 35036, 35046, 35047, 35044, 35045, 35028, + 35026, 35027, 35050, 35051, 35048, 35049, 35043, 35041, 35042, 35032, + 35033, 35025, 35031, 35030, 35029, 35056, 35057, 35052, 35055, 35054, + 35053, 34975, 34976, 34970, 34974, 34973, 34971, 34972, 34982, 34983, + 34980, 34981, 34986, 34987, 34984, 34985, 34979, 34977, 34978, 34990, + 34991, 34988, 34989, 34996, 34997, 34992, 34995, 34994, 34993, 35161, + 35162, 35159, 35160, 35153, 35151, 35152, 35169, 35170, 35167, 35168, + 35165, 35166, 35163, 35164, 35157, 35158, 35150, 35156, 35155, 35154, + 35175, 35176, 35171, 35174, 35173, 35172, 34163, 34164, 34161, 34162, + 34155, 34156, 34153, 34154, 34171, 34172, 34169, 34170, 34167, 34168, + 34165, 34166, 34160, 34152, 34159, 34158, 34157, 34177, 34178, 34173, + 34176, 34175, 34174, 34943, 34942, 34922, 34921, 34934, 34935, 34932, + 34933, 34930, 34931, 34928, 34929, 34926, 34927, 34920, 34925, 34924, + 34923, 34940, 34941, 34936, 34939, 34938, 34937, 34743, 34744, 34741, + 34742, 34740, 34738, 34739, 34747, 34748, 34745, 34746, 34753, 34754, + 34749, 34752, 34751, 34750, 34759, 34760, 34755, 34758, 34757, 34756, + 35009, 35010, 35007, 35008, 35001, 34999, 35000, 35017, 35018, 35015, + 35016, 35013, 35014, 35011, 35012, 35005, 35006, 34998, 35004, 35003, + 35002, 35023, 35024, 35019, 35022, 35021, 35020, 34958, 34959, 34956, + 34957, 34947, 34945, 34946, 34962, 34963, 34960, 34961, 34955, 34953, + 34954, 34951, 34952, 34944, 34950, 34949, 34948, 34968, 34969, 34964, + 34967, 34966, 34965, 34518, 34519, 34512, 34517, 34515, 34516, 34513, + 34514, 34505, 34506, 34503, 34504, 34522, 34523, 34520, 34521, 34510, + 34511, 34502, 34509, 34508, 34507, 34528, 34529, 34524, 34527, 34526, + 34525, 34880, 34881, 34874, 34879, 34877, 34878, 34875, 34876, 34867, + 34868, 34865, 34866, 34884, 34885, 34882, 34883, 34872, 34873, 34864, + 34871, 34870, 34869, 34890, 34891, 34886, 34889, 34888, 34887, 34492, + 34493, 34486, 34491, 34489, 34490, 34487, 34488, 34480, 34478, 34479, + 34496, 34497, 34494, 34495, 34484, 34485, 34477, 34483, 34482, 34481, + 34500, 34501, 34498, 34499, 34726, 34727, 34720, 34725, 34723, 34724, + 34721, 34722, 34715, 34714, 34730, 34731, 34728, 34729, 34719, 34713, + 34718, 34717, 34716, 34736, 34737, 34732, 34735, 34734, 34733, 34774, + 34775, 34768, 34773, 34771, 34772, 34769, 34770, 34764, 34762, 34763, + 34778, 34779, 34776, 34777, 34766, 34767, 34761, 34765, 35136, 35137, + 35130, 35135, 35133, 35134, 35131, 35132, 35149, 35148, 35140, 35141, + 35138, 35139, 35146, 35147, 35142, 35145, 35144, 35143, 34908, 34909, + 34902, 34907, 34905, 34906, 34903, 34904, 34895, 34896, 34893, 34894, + 34912, 34913, 34910, 34911, 34900, 34901, 34892, 34899, 34898, 34897, + 34918, 34919, 34914, 34917, 34916, 34915, 35536, 35536, 35536, 34083, + 34056, 34054, 34061, 34035, 34071, 34042, 34044, 34060, 34047, 34058, + 34031, 34059, 34077, 34065, 34046, 34072, 34045, 34078, 34036, 34039, + 34032, 34041, 34062, 34073, 34085, 34050, 34081, 34066, 34049, 34076, + 34074, 34070, 34075, 34082, 34053, 34063, 34052, 34043, 34051, 34084, + 34040, 34067, 34057, 34034, 34033, 34038, 34048, 34068, 34079, 34069, + 34037, 34080, 34064, 34055, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 19783, 19772, 19771, 19755, 19753, 19752, 19766, + 19770, 19769, 19785, 19764, 19763, 19754, 19751, 19750, 19787, 19760, + 19786, 19774, 19777, 19778, 19759, 19768, 19789, 19767, 19784, 19788, + 19773, 19776, 19765, 19779, 19780, 19761, 19762, 19790, 19781, 19756, + 19757, 19758, 19782, 19746, 19749, 19747, 19745, 19748, 19744, 19791, + 19792, 32803, 32804, 32640, 32789, 32790, 32763, 32566, 32573, 32676, + 32649, 32683, 32623, 32749, 32777, 32601, 32594, 32552, 32545, 32667, + 32770, 32559, 32702, 32587, 32580, 32615, 32608, 32742, 32756, 32721, + 32784, 32662, 32708, 32629, 32690, 32735, 32728, 32812, 32813, 32644, + 32645, 32798, 32799, 32765, 32568, 32575, 32678, 32655, 32685, 32626, + 32751, 32779, 32603, 32596, 32554, 32547, 32671, 32772, 32561, 32704, + 32589, 32582, 32617, 32610, 32744, 32758, 32723, 32786, 32663, 32713, + 32634, 32692, 32737, 32730, 32810, 32811, 32715, 32642, 32643, 32796, + 32797, 32764, 32567, 32574, 32677, 32653, 32654, 32684, 32625, 32750, + 32778, 32602, 32595, 32553, 32546, 32670, 32771, 32560, 32703, 32588, + 32581, 32616, 32609, 32743, 32757, 32722, 32785, 32659, 32660, 32712, + 32633, 32691, 32736, 32729, 32807, 32808, 32638, 32793, 32794, 32761, + 32564, 32571, 32674, 32652, 32681, 32621, 32747, 32775, 32599, 32592, + 32550, 32543, 32669, 32768, 32557, 32700, 32585, 32578, 32613, 32606, + 32740, 32754, 32719, 32782, 32658, 32711, 32632, 32688, 32733, 32726, + 32814, 32815, 32646, 32647, 32800, 32801, 32766, 32569, 32576, 32679, + 32656, 32686, 32627, 32752, 32780, 32604, 32597, 32555, 32548, 32672, + 32773, 32562, 32705, 32590, 32583, 32618, 32611, 32745, 32759, 32724, + 32787, 32664, 32714, 32635, 32693, 32738, 32731, 32806, 32809, 32717, + 32636, 32637, 32792, 32795, 32760, 32563, 32570, 32673, 32651, 32680, + 32619, 32620, 32746, 32774, 32598, 32591, 32549, 32542, 32668, 32767, + 32556, 32694, 32584, 32577, 32612, 32605, 32739, 32753, 32718, 32781, + 32657, 32710, 32631, 32687, 32732, 32725, 32802, 32805, 32716, 32639, + 32641, 32788, 32791, 32762, 32565, 32572, 32675, 32648, 32650, 32682, + 32622, 32624, 32748, 32776, 32600, 32593, 32551, 32544, 32665, 32769, + 32558, 32701, 32586, 32579, 32614, 32607, 32741, 32755, 32720, 32783, + 32661, 32707, 32709, 32628, 32630, 32689, 32734, 32727, 32706, 32666, + 32539, 32540, 32541, 32697, 32698, 32695, 32819, 32822, 32825, 32824, + 32828, 32820, 32827, 32818, 32816, 32823, 32826, 32817, 32821, 32835, + 32837, 32834, 32833, 32830, 32829, 32832, 32831, 32838, 32836, 32699, + 32696, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 9401, 9602, 9290, 9449, 9240, 9543, 9345, 9504, 9286, 9445, 9366, + 9529, 9274, 9433, 9233, 9530, 9394, 9595, 9342, 9501, 9242, 9545, 9343, + 9502, 9282, 9441, 9275, 9434, 9339, 9498, 9396, 9597, 9241, 9544, 9385, + 9563, 9382, 9560, 9383, 9561, 9365, 9528, 9272, 9431, 9287, 9446, 9416, + 7237, 7229, 7180, 7230, 28123, 7204, 7193, 7207, 7203, 7212, 7205, 7202, + 7199, 7235, 7224, 9415, 9419, 9296, 9455, 9294, 9453, 9406, 9607, 9284, + 9443, 9292, 9451, 9246, 9571, 9254, 9580, 9250, 9575, 9249, 9574, 9252, + 9578, 9330, 9489, 9380, 9558, 9288, 9447, 9283, 9442, 22430, 22467, 7188, + 7196, 3462, 2824, 3465, 2826, 3461, 3437, 3454, 3464, 2853, 3463, 2829, + 3434, 3440, 3439, 2827, 2833, 3453, 2852, 2846, 2832, 3449, 2840, 3444, + 3450, 3443, 3447, 2822, 2818, 2850, 2849, 2844, 3456, 3446, 3457, 3458, + 2851, 2816, 3430, 2845, 2848, 3433, 3459, 3431, 2813, 3442, 2831, 2838, + 2855, 3436, 3441, 2817, 2841, 2842, 2843, 3445, 3432, 2815, 2814, 3460, + 2854, 2830, 3435, 2828, 2819, 2835, 3438, 2834, 2837, 3455, 2825, 2839, + 2836, 3452, 2823, 3451, 2847, 3448, 2812, 2820, 2821, 2809, 2808, 3466, + 3468, 2811, 2810, 3467, 3469, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 22427, 22423, 22426, 22422, 22428, 22424, 22429, 22425, + 22482, 22497, 22525, 22504, 22487, 22481, 22496, 22524, 22503, 22486, + 22483, 22498, 22526, 22508, 22488, 22474, 22473, 22475, 22519, 22538, + 22535, 22537, 22536, 22516, 22686, 22687, 17554, 18158, 17555, 18159, + 17594, 18220, 17821, 18583, 17820, 18540, 17494, 18080, 17495, 18081, + 17963, 17971, 17468, 18036, 17469, 18037, 17470, 18038, 17466, 18034, + 17467, 18035, 17471, 18039, 17765, 18464, 17635, 18272, 17632, 18269, + 17636, 18273, 17480, 18057, 17654, 18296, 17687, 18371, 17688, 18373, + 17734, 18411, 17739, 18416, 17737, 18414, 17740, 18417, 17747, 18429, + 17746, 18426, 17762, 18454, 17767, 18467, 17862, 18633, 17871, 18645, + 17866, 18640, 17815, 18534, 17816, 18535, 17870, 18644, 17556, 18176, + 17623, 18259, 17496, 18082, 22695, 18118, 18317, 18331, 18354, 18466, + 17948, 18578, 18630, 17616, 18248, 17617, 18249, 17618, 17805, 18546, + 17810, 18576, 17619, 18251, 17620, 18252, 17621, 18253, 22502, 22471, + 22548, 17788, 18490, 17808, 18301, 17979, 17680, 18345, 17490, 18070, + 18067, 18214, 17474, 18042, 17563, 18183, 17867, 18641, 17868, 18642, + 17869, 18643, 17571, 18194, 17639, 18277, 17683, 18350, 17760, 18451, + 17784, 18488, 17590, 17764, 17791, 17642, 17787, 17970, 17809, 17812, + 17626, 17497, 17481, 18059, 17732, 18409, 17858, 18621, 17576, 18199, + 17577, 18200, 17578, 18201, 17729, 18400, 17463, 18031, 17488, 17785, + 17908, 17501, 18084, 17781, 18482, 17768, 17780, 18481, 17744, 18423, + 17493, 18076, 17514, 18111, 17513, 18108, 17669, 18330, 17790, 18508, + 17658, 18306, 17659, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 22415, 22402, 22405, 22414, 17763, 18455, 17918, + 22397, 22627, 17953, 17916, 17917, 17914, 17915, 17913, 29119, 29121, + 29131, 29123, 29120, 29122, 29130, 29111, 29110, 29107, 29106, 29129, + 29105, 29104, 29109, 29108, 29099, 29098, 29093, 29092, 29101, 29100, + 29095, 29094, 29117, 29113, 29112, 29103, 29102, 29116, 29097, 29115, + 29096, 29118, 29114, 29133, 29135, 29136, 29134, 29132, 29124, 29125, + 29126, 29127, 29128, 35536, 35536, 35536, 24088, 24087, 24090, 24085, + 24086, 24089, 24082, 24081, 24084, 24083, 35536, 35536, 35536, 35536, + 35536, 35536, 25661, 25660, 25649, 25650, 25637, 25639, 25671, 25652, + 25659, 25658, 25642, 25653, 25663, 25662, 25668, 25673, 25655, 25654, + 25641, 25676, 25664, 25665, 25643, 25678, 25675, 25672, 25644, 25645, + 25670, 25634, 25679, 25681, 25666, 25680, 25674, 25677, 25669, 25648, + 25667, 25686, 25687, 25657, 25656, 25640, 25651, 25635, 25647, 25646, + 25636, 25685, 25688, 25638, 25684, 25689, 25683, 25682, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 26806, 26808, 26755, 26756, + 26776, 26777, 26772, 26773, 26767, 26768, 26769, 26770, 26799, 26800, + 26757, 26774, 26775, 26758, 26796, 26795, 26792, 26791, 26780, 26790, + 26789, 26794, 26793, 26782, 26764, 26763, 26760, 26759, 26781, 26766, + 26765, 26762, 26761, 26783, 26798, 26797, 26788, 26787, 26802, 26804, + 26803, 26779, 26771, 26784, 26785, 26786, 26801, 26778, 26821, 26822, + 26833, 26834, 26825, 26826, 26827, 26828, 26829, 26830, 26835, 26836, + 26823, 26831, 26832, 26824, 26807, 26805, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 26810, 26809, 26817, 26819, 26816, 26815, + 26812, 26811, 26814, 26813, 26820, 26818, 35536, 35536, 35536, 35536, + 35536, 35536, 7255, 7257, 7254, 7253, 7250, 7249, 7252, 7251, 7258, 7256, + 7246, 7247, 7242, 7243, 7244, 7245, 7241, 7248, 9889, 9883, 9896, 9885, + 9884, 9882, 9897, 9786, 9784, 9789, 9890, 9940, 9795, 9907, 16739, 16741, + 16738, 16737, 16734, 16733, 16736, 16735, 16742, 16740, 16703, 16702, + 16713, 16699, 16707, 16706, 16720, 16700, 16709, 16697, 16701, 16705, + 16704, 16715, 16712, 16710, 16716, 16719, 16714, 16718, 16708, 16698, + 16717, 16711, 16721, 16695, 16722, 16696, 16731, 16728, 16730, 16729, + 16732, 16727, 16725, 16726, 16723, 16724, 26148, 26145, 26137, 26153, + 26144, 26139, 26150, 26142, 26141, 26143, 26147, 26135, 26152, 26151, + 26149, 26155, 26154, 26146, 26140, 26136, 26138, 26134, 26156, 26163, + 26165, 26158, 26161, 26164, 26162, 26160, 26159, 26131, 26130, 26133, + 26132, 26166, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 26157, 14377, 14380, 14381, 14378, 14335, 14336, + 14344, 14338, 14340, 14343, 14337, 14333, 14339, 14341, 14334, 14300, + 14302, 14303, 14316, 14323, 14330, 14365, 14282, 14289, 14363, 14367, + 14313, 14386, 14369, 35536, 35536, 35536, 16058, 16054, 16057, 16056, + 16028, 15996, 15995, 15997, 16037, 16016, 16002, 16003, 16035, 16029, + 16036, 15998, 15999, 16000, 16012, 16013, 16001, 16010, 16011, 16026, + 16005, 16027, 16004, 16024, 16025, 15991, 15992, 16007, 16022, 16023, + 15993, 15994, 16006, 16014, 16015, 16008, 16009, 16032, 16034, 16017, + 16018, 16031, 16033, 16020, 16021, 16019, 16030, 16055, 16061, 16063, + 16064, 16065, 16059, 16060, 16062, 16067, 16066, 15987, 15988, 15989, + 16052, 15990, 16038, 16041, 16050, 16043, 16048, 16046, 16044, 16042, + 16039, 16040, 16045, 16053, 35536, 16051, 16074, 16076, 16073, 16072, + 16069, 16068, 16071, 16070, 16077, 16075, 35536, 35536, 35536, 35536, + 16047, 16049, 23307, 23305, 23300, 23297, 23303, 23393, 23371, 23323, + 23335, 23331, 23330, 23333, 23332, 23325, 23324, 23322, 23435, 23437, + 23434, 23433, 23430, 23429, 23432, 23431, 23438, 23436, 23334, 23327, + 23326, 23329, 23328, 35536, 6347, 6365, 6367, 6364, 6348, 6366, 6356, + 6355, 6352, 6351, 6327, 6328, 6350, 6349, 6354, 6353, 6329, 6331, 6330, + 6358, 6357, 6344, 6343, 6332, 6333, 6342, 6338, 6337, 6336, 6341, 6340, + 6334, 6335, 6339, 6363, 6361, 6360, 6362, 6345, 6346, 6359, 6372, 6375, + 6376, 6381, 6379, 6378, 6377, 6373, 6374, 6380, 6315, 6313, 6312, 6314, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 6321, + 6320, 6317, 6309, 6319, 6325, 6316, 6323, 6326, 6324, 6322, 6318, 6311, + 6310, 35536, 35536, 6388, 6390, 6387, 6386, 6383, 6382, 6385, 6384, 6391, + 6389, 35536, 35536, 6368, 6370, 6369, 6371, 23277, 23270, 23269, 23274, + 23273, 23267, 23266, 23265, 23263, 23262, 23264, 23268, 23279, 23272, + 23271, 23276, 23370, 23280, 23281, 23278, 23367, 23368, 23369, 23408, + 23410, 23409, 23252, 23404, 23395, 23396, 23316, 23317, 29603, 29579, + 29602, 29578, 29601, 29577, 29616, 29592, 29610, 29586, 29605, 29581, + 29604, 29580, 29621, 29597, 29611, 29587, 29614, 29590, 29609, 29585, + 29608, 29584, 29612, 29588, 29613, 29589, 29607, 29583, 29606, 29582, + 29615, 29591, 29619, 29595, 29623, 29599, 29620, 29596, 29618, 29594, + 29622, 29598, 29617, 29593, 29624, 29600, 29625, 29637, 29645, 29642, + 29641, 29647, 29648, 29626, 29646, 29643, 29644, 29636, 29640, 29639, + 29638, 29635, 29632, 29633, 29634, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 29628, + 29629, 29631, 29630, 29627, 21734, 21735, 21693, 21710, 21721, 21720, + 21697, 21696, 21709, 21715, 21716, 21748, 21750, 21740, 21742, 21741, + 21688, 21685, 21687, 21737, 21738, 21752, 21753, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 12338, 12336, 12335, + 12334, 12333, 12337, 35536, 35536, 12032, 12030, 12029, 12028, 12027, + 12031, 35536, 35536, 12047, 12045, 12044, 12043, 12042, 12046, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 11988, 11994, + 11992, 11989, 11991, 11990, 11993, 35536, 11973, 11979, 11977, 11974, + 11976, 11975, 11978, 35536, 18050, 18026, 18056, 18051, 18147, 18310, + 18500, 18299, 18290, 18293, 18321, 18335, 18161, 18054, 18055, 18405, + 18255, 18549, 18548, 18550, 18551, 18510, 17947, 18453, 18109, 18433, + 18110, 18497, 18498, 18053, 18620, 18586, 18629, 18572, 18625, 18073, + 18074, 18075, 18661, 18658, 18659, 18660, 18672, 22384, 22608, 22617, + 22618, 22675, 18491, 18258, 18406, 18631, 18254, 13549, 18116, 18581, + 18554, 22672, 22521, 22545, 35536, 35536, 35536, 35536, 6570, 6571, 6572, + 6573, 6574, 6575, 6533, 6569, 6534, 6535, 6536, 6537, 6538, 6498, 6499, + 6500, 6501, 6502, 6503, 6539, 6540, 6541, 6542, 6543, 6544, 6545, 6546, + 6547, 6548, 6549, 6504, 6497, 6505, 6506, 6507, 6508, 6509, 6510, 6551, + 6552, 6553, 6554, 6555, 6556, 6512, 6511, 6513, 6514, 6515, 6516, 6517, + 6491, 6530, 6492, 6531, 6493, 6532, 6494, 6495, 6496, 6490, 6518, 6519, + 6520, 6521, 6522, 6523, 6524, 6525, 6526, 6527, 6528, 6529, 6557, 6558, + 6559, 6560, 6561, 6562, 6563, 21702, 21714, 21724, 21726, 21711, 21705, + 21692, 21717, 21704, 21707, 21719, 21730, 21732, 21728, 21733, 21722, + 21713, 21731, 21699, 21700, 21729, 21691, 21701, 21695, 21698, 21694, + 21690, 21703, 21725, 21727, 21712, 21706, 21718, 21708, 21723, 21744, + 21747, 21739, 21746, 21745, 21749, 21743, 21751, 21689, 21736, 21686, + 35536, 35536, 21760, 21762, 21759, 21758, 21755, 21754, 21757, 21756, + 21763, 21761, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 14574, 14572, 14605, 14606, 14607, 14583, + 14584, 14616, 14618, 14551, 14549, 14548, 14552, 14558, 14559, 14561, + 14560, 14565, 14562, 14563, 14567, 14535, 14533, 35536, 35536, 35536, + 35536, 14431, 14432, 14500, 14501, 14520, 14514, 14515, 14518, 14517, + 14516, 14475, 14460, 14499, 14466, 14470, 14469, 14483, 14482, 14403, + 14428, 14421, 14506, 14419, 14426, 14456, 14449, 14455, 14510, 14451, + 14454, 14453, 14494, 14487, 14504, 14505, 14493, 14491, 14490, 14495, + 14497, 14442, 14441, 14527, 14528, 14392, 14391, 14507, 14446, 14444, + 35536, 35536, 35536, 35536, 18694, 18697, 18698, 18695, 18696, 18700, + 18701, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 2549, 2550, 2548, 2551, 2547, 35536, 35536, 35536, + 35536, 35536, 15040, 15067, 15045, 14976, 15032, 15035, 15037, 15036, + 15031, 15038, 15034, 15033, 14977, 15010, 15011, 15008, 15009, 14974, + 14975, 14973, 14981, 15023, 15021, 14998, 15030, 15003, 35536, 15015, + 15041, 14989, 14984, 15025, 35536, 15027, 35536, 15001, 15005, 35536, + 14991, 14987, 35536, 15019, 14996, 15013, 15007, 15017, 15029, 14980, + 14983, 14986, 15042, 1164, 1163, 1194, 1192, 1193, 1195, 1424, 1422, + 1423, 1425, 1189, 1187, 1188, 1190, 1555, 1553, 1554, 1556, 1534, 1532, + 1533, 1535, 1550, 1548, 1549, 1551, 1568, 1566, 1567, 1569, 1429, 1427, + 1428, 1430, 1230, 1228, 1229, 1231, 1401, 1399, 1400, 1402, 1511, 1509, + 1510, 1512, 1516, 1514, 1515, 1517, 1220, 1219, 1211, 1210, 1234, 1233, + 1223, 1222, 1330, 1329, 1459, 1458, 1353, 1351, 1352, 1354, 1264, 1262, + 1263, 1265, 1276, 1274, 1275, 1277, 1392, 1390, 1391, 1393, 1406, 1405, + 1463, 1461, 1462, 1464, 1306, 1305, 1301, 1299, 1300, 1302, 1310, 1308, + 1309, 1311, 1592, 1591, 1590, 1589, 2411, 2410, 2419, 2418, 2415, 2414, + 2413, 2412, 2423, 2422, 2409, 2417, 2416, 2425, 2421, 2420, 2424, 1756, + 1704, 1933, 1930, 1931, 1926, 1927, 1928, 1920, 1733, 1734, 1731, 1732, + 1956, 1645, 1649, 1396, 1394, 1395, 1397, 1561, 1560, 1614, 1613, 1611, + 1610, 1559, 1571, 1570, 1357, 1356, 1360, 1359, 1627, 1625, 1626, 1628, + 1562, 1563, 2100, 2099, 2102, 2101, 2121, 2120, 2129, 2128, 2119, 2118, + 2125, 2124, 2105, 2103, 2104, 2154, 2152, 2153, 1244, 1242, 1243, 1245, + 2111, 2109, 2115, 2098, 2123, 1675, 1666, 1671, 1678, 1673, 1684, 2063, + 2051, 2058, 2071, 2074, 2079, 2081, 2086, 2083, 2092, 1760, 1766, 1743, + 1738, 1799, 1795, 1801, 1970, 1963, 1975, 1983, 1940, 1944, 1697, 1689, + 1693, 1699, 2044, 2039, 2156, 1640, 1636, 1728, 1724, 1712, 1717, 1708, + 1715, 1710, 1719, 1905, 1901, 1903, 1907, 1774, 1776, 1784, 1782, 1779, + 1790, 1772, 1793, 1827, 1819, 1831, 1837, 1811, 1840, 1858, 1847, 1852, + 1862, 1841, 1863, 1879, 1869, 1891, 1884, 1887, 1894, 1753, 1749, 1750, + 1751, 2139, 2132, 2141, 2147, 2096, 2151, 2080, 1935, 1658, 1991, 1994, + 1998, 1992, 1995, 1997, 2127, 2126, 2113, 2117, 2097, 2122, 1682, 1681, + 1676, 1680, 1672, 1683, 2077, 2076, 2069, 2075, 2073, 2078, 2090, 2089, + 2084, 2088, 2082, 2091, 1709, 1718, 1902, 1906, 1773, 1777, 1788, 1771, + 1792, 1835, 1810, 1839, 1842, 1860, 1892, 1889, 1882, 1888, 1886, 1893, + 1657, 2149, 2136, 2145, 2135, 2095, 2150, 2110, 2108, 2112, 2114, 2106, + 1674, 1665, 1670, 1677, 1667, 2062, 2050, 2057, 2070, 2052, 2085, 1759, + 1765, 1742, 1737, 1798, 1800, 1969, 1962, 1974, 1982, 1939, 1947, 1943, + 1696, 1688, 1692, 1698, 2043, 2155, 1639, 1635, 1727, 1723, 1711, 1716, + 1707, 1714, 1904, 1900, 1775, 1783, 1781, 1778, 1789, 1826, 1818, 1830, + 1836, 1820, 1857, 1846, 1851, 1861, 1878, 1868, 1890, 1883, 1870, 1752, + 1748, 1754, 2138, 2131, 2140, 2146, 2133, 2116, 2107, 1679, 1668, 2072, + 2053, 2087, 2093, 1984, 1966, 2021, 2001, 1780, 1791, 1838, 1885, 1871, + 2148, 2134, 1999, 1993, 1996, 2042, 2046, 1642, 1644, 1726, 1730, 1986, + 1990, 2023, 2031, 1740, 1745, 1768, 1770, 1797, 1803, 1946, 1951, 1695, + 1703, 2012, 2007, 2026, 2020, 2029, 1988, 1949, 1701, 2041, 2045, 1641, + 1643, 1725, 1729, 1985, 1989, 2022, 2030, 1739, 1744, 1767, 1769, 1796, + 1802, 1945, 1950, 1694, 1702, 2010, 2005, 2024, 2018, 2028, 1987, 1948, + 1700, 2011, 2006, 2025, 2019, 1965, 2000, 2038, 1971, 1964, 1976, 2013, + 2008, 2027, 2040, 2157, 1659, 1660, 24956, 24957, 1923, 1914, 1918, 1915, + 1916, 1917, 1957, 1648, 1653, 1651, 1647, 1912, 1959, 1655, 2033, 1925, + 2060, 2049, 2048, 2047, 2055, 2065, 2067, 2066, 1762, 1761, 1736, 1735, + 1961, 1968, 1967, 1978, 1977, 1979, 1981, 1980, 1937, 1936, 1942, 2003, + 2002, 2009, 2015, 2014, 2017, 2016, 1686, 1691, 1690, 2035, 2034, 2036, + 2037, 1638, 1633, 1632, 1631, 1720, 1722, 1721, 1706, 1705, 1898, 1896, + 1816, 1817, 1814, 1821, 1822, 1829, 1828, 1833, 1832, 1843, 1844, 1845, + 1855, 1853, 1848, 1849, 1929, 1932, 1854, 1746, 1747, 1866, 1865, 1876, + 1875, 1874, 1881, 1880, 2143, 2142, 1669, 2061, 2059, 2056, 2054, 2068, + 2064, 1764, 1757, 1763, 1972, 1938, 2004, 1687, 1825, 1834, 2130, 2137, + 2144, 1859, 1899, 1867, 1897, 1815, 1634, 1787, 1872, 1850, 1823, 1786, + 1824, 1873, 1758, 1741, 1856, 1713, 1664, 1785, 1637, 1941, 1973, 1877, + 1924, 1919, 1922, 1921, 1958, 1646, 1794, 1953, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 1954, + 1908, 1661, 1662, 1864, 1952, 1934, 1656, 2094, 1955, 1960, 1755, 26477, + 1685, 2032, 1663, 32839, 32950, 33018, 33029, 33040, 33051, 33062, 33073, + 33084, 32840, 32851, 32862, 32873, 32884, 32895, 32906, 25931, 25936, + 25937, 25930, 25961, 25932, 25963, 25939, 25953, 25935, 35536, 35536, + 35536, 35536, 35536, 35536, 7465, 7467, 7292, 7293, 7476, 7478, 7178, + 7466, 7468, 7541, 7542, 7477, 7479, 7179, 7232, 7233, 25962, 25933, + 25934, 25948, 25960, 25945, 25957, 25943, 25955, 25947, 25959, 25940, + 25949, 25941, 25950, 25944, 25956, 25942, 25954, 25938, 25951, 26972, + 33845, 25946, 25958, 9659, 6231, 33721, 10375, 9658, 6230, 33719, 28147, + 28156, 28191, 35536, 28181, 28148, 28192, 28154, 28155, 28158, 28162, + 28157, 28161, 28159, 28163, 28190, 28138, 28140, 28189, 28187, 28160, + 28186, 28153, 35536, 28180, 28149, 28188, 28146, 35536, 35536, 35536, + 35536, 1098, 2430, 1079, 2432, 1110, 35536, 1095, 1096, 1075, 1076, 1107, + 1108, 2335, 2336, 2405, 2406, 1295, 1159, 1158, 1149, 1148, 1579, 1578, + 1152, 1151, 1596, 1594, 1595, 1597, 1169, 1168, 1184, 1182, 1183, 1185, + 1521, 1520, 1530, 1528, 1529, 1523, 1544, 1542, 1543, 1545, 1326, 1324, + 1325, 1327, 1292, 1290, 1291, 1293, 1364, 1362, 1363, 1365, 1208, 1207, + 1538, 1537, 1456, 1455, 1622, 1621, 1485, 1483, 1484, 1486, 1491, 1489, + 1490, 1492, 1472, 1470, 1471, 1473, 1216, 1214, 1215, 1217, 1504, 1502, + 1503, 1505, 1618, 1616, 1617, 1619, 1127, 1125, 1126, 1128, 1271, 1269, + 1270, 1272, 1255, 1253, 1254, 1256, 1438, 1436, 1437, 1439, 1340, 1338, + 1339, 1341, 1376, 1374, 1375, 1377, 1385, 1383, 1384, 1386, 1417, 1415, + 1416, 1418, 1314, 1312, 1313, 1315, 1583, 1582, 1167, 1166, 1607, 1605, + 1606, 1608, 1809, 1808, 1805, 1804, 1807, 1806, 1813, 1812, 35536, 35536, + 35340, 35536, 12757, 12761, 12729, 12745, 12731, 12743, 12742, 12672, + 12735, 12744, 12732, 12667, 12760, 12765, 12739, 12752, 12754, 12751, + 12750, 12747, 12746, 12749, 12748, 12755, 12753, 12668, 12738, 12674, + 12756, 12759, 12762, 12666, 12675, 12676, 12677, 12678, 12679, 12680, 12681, 12682, 12683, 12684, 12685, 12686, 12687, 12688, 12689, 12690, 12691, 12692, 12693, 12694, 12695, 12696, 12697, 12698, 12699, 12700, - 12701, 12702, 12703, 12704, 12705, 12706, 12707, 12708, 12709, 12710, - 12711, 12712, 12713, 12714, 12715, 12716, 12717, 12718, 12719, 12720, - 12721, 12722, 12723, 12724, 12725, 12726, 12727, 12728, 12729, 12730, - 12731, 12732, 12733, 12734, 12735, 12736, 12737, 12738, 12739, 12740, - 12741, 12742, 12743, 12744, 12745, 12746, 12747, 12748, 12749, 12750, - 12751, 12752, 12753, 12754, 12755, 12756, 12757, 12758, 12759, 12760, - 12761, 12762, 12763, 12764, 12765, 12766, 12767, 12768, 12769, 12770, - 12771, 12772, 12773, 12774, 12775, 12776, 12777, 12778, 12779, 12780, - 12781, 12782, 12783, 12784, 12785, 12786, 12787, 12788, 12789, 12790, - 12791, 12792, 12793, 12794, 12795, 12796, 12797, 12798, 12799, 12800, - 12801, 12802, 12803, 12804, 12805, 12806, 12807, 12808, 12809, 12810, - 12811, 12812, 12813, 12814, 12815, 12816, 12817, 12818, 12819, 12820, - 12821, 12822, 12823, 12824, 12825, 12826, 12827, 12828, 12829, 12830, - 12831, 12832, 12833, 12834, 12835, 12836, 12837, 12838, 12839, 12840, - 12841, 12842, 12843, 12844, 12845, 12846, 12847, 12848, 12849, 12850, - 12851, 12852, 12853, 12854, 12855, 12856, 12857, 12858, 12859, 12860, - 12861, 12862, 12863, 12864, 12865, 12866, 12867, 12868, 12869, 12870, - 12871, 12872, 12873, 12874, 12875, 12876, 12877, 12878, 12879, 12880, - 12881, 12882, 12883, 12884, 12885, 12886, 12887, 12888, 12889, 12890, - 12891, 12892, 12893, 12894, 12895, 12896, 12897, 12898, 12899, 12900, - 12901, 12902, 12903, 12904, 12905, 12906, 12907, 12908, 12909, 12910, - 12911, 12912, 12913, 12914, 12915, 12916, 12917, 12918, 12919, 12920, - 12921, 12922, 12923, 12924, 12925, 12926, 12927, 12928, 12929, 12930, - 12931, 12932, 12933, 12934, 12935, 12936, 12937, 12938, 12939, 12940, - 12941, 12942, 12943, 12944, 12945, 12946, 12947, 12948, 12949, 12950, - 12951, 12952, 12953, 12954, 12955, 12956, 12957, 12958, 12959, 12960, - 12961, 12962, 12963, 12964, 12965, 12966, 12967, 12968, 12969, 12970, - 12971, 12972, 12973, 12974, 12975, 12976, 12977, 12978, 12979, 12980, - 12981, 12982, 12983, 12984, 12985, 12986, 12987, 12988, 12989, 12990, - 12991, 12992, 12993, 12994, 12995, 12996, 12997, 12998, 12999, 13000, - 13001, 13002, 13003, 13004, 13005, 13006, 13007, 13008, 13009, 13010, - 13011, 13012, 13013, 13014, 13015, 13016, 13017, 13018, 13019, 13020, - 13021, 13022, 13023, 13024, 13025, 13026, 13027, 13028, 13029, 13030, - 13031, 13032, 13033, 13034, 13035, 13036, 13037, 13038, 13039, 13040, - 13041, 13042, 13043, 13044, 13045, 13046, 13047, 13048, 13049, 13050, - 13051, 13052, 13053, 13054, 13055, 13056, 13057, 13058, 13059, 13060, - 13061, 13062, 13063, 13064, 13065, 13066, 13067, 13068, 13069, 13070, - 13071, 13072, 13073, 13074, 13075, 13076, 13077, 13078, 13079, 13080, - 13081, 13082, 13083, 13084, 13085, 13086, 13087, 13088, 13089, 13090, - 13091, 13092, 13093, 13094, 13095, 13096, 13097, 13098, 13099, 13100, - 13101, 13102, 13103, 13104, 13105, 13106, 13107, 13108, 13109, 13110, - 13111, 13112, 13113, 13114, 13115, 13116, 13117, 13118, 13119, 13120, - 13121, 13122, 13123, 13124, 13125, 13126, 13127, 13128, 13129, 13130, - 13131, 13132, 13133, 13134, 13135, 13136, 13137, 13138, 13139, 13140, - 13141, 13142, 13143, 13144, 13145, 13146, 13147, 13148, 13149, 13150, - 13151, 13152, 13153, 13154, 13155, 13156, 13157, 13158, 13159, 13160, - 13161, 13162, 13163, 13164, 13165, 13166, 13167, 13168, 13169, 13170, - 13171, 13172, 13173, 13174, 13175, 13176, 13177, 13178, 13179, 13180, - 13181, 13182, 13183, 13184, 13185, 13186, 13187, 13188, 13189, 13190, - 13191, 13192, 13193, 13194, 13195, 13196, 13197, 13198, 13199, 13200, - 13201, 13202, 13203, 13204, 13205, 13206, 13207, 13208, 13209, 13210, - 13211, 13212, 13213, 13214, 13215, 13216, 13217, 13218, 13219, 13220, - 13221, 13222, 13223, 13224, 13225, 13226, 13227, 13228, 13229, 13230, - 13231, 13232, 13233, 13234, 13235, 13236, 13237, 13238, 13239, 13240, - 13241, 13242, 13243, 13244, 13245, 13246, 13247, 13248, 13249, 13250, - 13251, 13252, 13253, 13254, 13255, 13256, 13257, 13258, 13259, 13260, - 13261, 13262, 13263, 13264, 13265, 13266, 13267, 13268, 13269, 13270, - 13271, 13272, 13273, 13274, 13275, 13276, 13277, 13278, 13279, 13280, - 13281, 13282, 13283, 13284, 13285, 13286, 13287, 13288, 13289, 13290, - 13291, 13292, 13293, 13294, 13295, 13296, 13297, 13298, 13299, 13300, - 13301, 13302, 13303, 13304, 13305, 13306, 13307, 13308, 13309, 13310, - 13311, 13312, 13313, 13314, 13315, 13316, 13317, 13318, 13319, 13320, - 13321, 13322, 13323, 13324, 13325, 13326, 13327, 13328, 13329, 13330, - 13331, 13332, 13333, 13334, 13335, 13336, 13337, 13338, 13339, 13340, - 13341, 13342, 13343, 13344, 13345, 13346, 13347, 13348, 13349, 13350, - 13351, 13352, 13353, 13354, 13355, 13356, 13357, 13358, 13359, 13360, - 13361, 13362, 13363, 13364, 13365, 13366, 13367, 13368, 13369, 13370, - 13371, 13372, 13373, 13374, 13375, 13376, 13377, 13378, 13379, 13380, - 13381, 13382, 13383, 13384, 13385, 13386, 13387, 13388, 13389, 13390, - 13391, 13392, 13393, 13394, 13395, 13396, 13397, 13398, 13399, 13400, - 13401, 13402, 13403, 13404, 13405, 13406, 13407, 13408, 13409, 13410, - 13411, 13412, 13413, 13414, 13415, 13416, 13417, 13418, 13419, 13420, - 13421, 13422, 13423, 13424, 13425, 13426, 13427, 13428, 13429, 13430, - 13431, 13432, 13433, 13434, 13435, 13436, 13437, 13438, 13439, 13440, - 13441, 13442, 13443, 13444, 13445, 13446, 13447, 13448, 13449, 13450, - 13451, 13452, 13453, 13454, 13455, 13456, 13457, 13458, 13459, 13460, - 13461, 13462, 13463, 13464, 13465, 13466, 13467, 13468, 13469, 13470, - 13471, 13472, 13473, 13474, 13475, 13476, 13477, 13478, 13479, 13480, - 13481, 13482, 13483, 13484, 13485, 13486, 13487, 13488, 13489, 13490, - 13491, 13492, 13493, 13494, 13495, 13496, 13497, 13498, 13499, 13500, - 13501, 13502, 13503, 13504, 13505, 13506, 13507, 13508, 13509, 13510, - 13511, 13512, 13513, 13514, 13515, 13516, 13517, 13518, 13519, 13520, - 13521, 13522, 13523, 13524, 13525, 13526, 13527, 13528, 13529, 13530, - 13531, 13532, 13533, 13534, 13535, 13536, 13537, 13538, 13539, 13540, - 13541, 13542, 13543, 13544, 13545, 13546, 13547, 13548, 13549, 13550, - 13551, 13552, 13553, 13554, 13555, 13556, 13557, 13558, 13559, 13560, - 13561, 13562, 13563, 13564, 13565, 13566, 13567, 13568, 13569, 13570, - 13571, 13572, 13573, 13574, 13575, 13576, 13577, 13578, 13579, 13580, - 13581, 13582, 13583, 13584, 13585, 13586, 13587, 13588, 13589, 13590, - 13591, 13592, 13593, 13594, 13595, 13596, 13597, 13598, 13599, 13600, - 13601, 13602, 13603, 13604, 13605, 13606, 13607, 13608, 13609, 13610, - 13611, 13612, 13613, 13614, 13615, 13616, 13617, 13618, 13619, 13620, - 13621, 13622, 13623, 13624, 13625, 13626, 13627, 13628, 13629, 13630, - 13631, 13632, 13633, 13634, 13635, 13636, 13637, 13638, 13639, 13640, - 13641, 13642, 13643, 13644, 13645, 13646, 13647, 13648, 13649, 13650, - 13651, 13652, 13653, 13654, 13655, 13656, 13657, 13658, 13659, 13660, - 13661, 13662, 13663, 13664, 13665, 13666, 13667, 13668, 13669, 13670, - 13671, 13672, 13673, 13674, 13675, 13676, 13677, 13678, 13679, 13680, - 13681, 13682, 13683, 13684, 13685, 13686, 13687, 13688, 13689, 13690, - 13691, 13692, 13693, 13694, 13695, 13696, 13697, 13698, 13699, 13700, - 13701, 13702, 13703, 13704, 13705, 13706, 13707, 13708, 13709, 13710, - 13711, 13712, 13713, 13714, 13715, 13716, 13717, 13718, 13719, 13720, - 13721, 13722, 13723, 13724, 13725, 13726, 13727, 13728, 13729, 13730, - 13731, 13732, 13733, 13734, 13735, 13736, 13737, 13738, 13739, 13740, - 13741, 13742, 13743, 13744, 13745, 13746, 13747, 13748, 13749, 13750, - 13751, 13752, 13753, 13754, 13755, 13756, 13757, 13758, 13759, 13760, - 13761, 13762, 13763, 13764, 13765, 13766, 13767, 13768, 13769, 13770, - 13771, 13772, 13773, 13774, 13775, 13776, 13777, 13778, 13779, 13780, - 13781, 13782, 13783, 13784, 13785, 13786, 13787, 13788, 13789, 13790, - 13791, 13792, 13793, 13794, 13795, 13796, 13797, 13798, 13799, 13800, - 13801, 13802, 13803, 13804, 13805, 13806, 13807, 13808, 13809, 13810, - 13811, 13812, 13813, 13814, 13815, 13816, 13817, 13818, 13819, 13820, - 13821, 13822, 13823, 13824, 13825, 13826, 13827, 13828, 13829, 13830, - 13831, 13832, 13833, 13834, 13835, 13836, 13837, 13838, 13839, 13840, - 13841, 13842, 13843, 13844, 13845, 13846, 13847, 13848, 13849, 13850, - 13851, 13852, 13853, 13854, 13855, 13856, 13857, 13858, 13859, 13860, - 13861, 13862, 13863, 13864, 13865, 13866, 13867, 13868, 13869, 13870, - 13871, 13872, 13873, 13874, 13875, 13876, 13877, 13878, 13879, 13880, - 13881, 13882, 13883, 13884, 13885, 13886, 13887, 13888, 13889, 13890, - 13891, 13892, 13893, 13894, 13895, 13896, 13897, 13898, 13899, 13900, - 13901, 13902, 13903, 13904, 13905, 13906, 13907, 13908, 13909, 13910, - 13911, 13912, 13913, 13914, 13915, 13916, 13917, 13918, 13919, 13920, - 13921, 13922, 13923, 13924, 13925, 13926, 13927, 13928, 13929, 13930, - 13931, 13932, 13933, 13934, 13935, 13936, 13937, 13938, 13939, 13940, - 13941, 13942, 13943, 13944, 13945, 13946, 13947, 13948, 13949, 13950, - 13951, 13952, 13953, 13954, 13955, 13956, 13957, 13958, 13959, 13960, - 13961, 13962, 13963, 13964, 13965, 13966, 13967, 13968, 13969, 13970, - 13971, 13972, 13973, 13974, 13975, 13976, 13977, 13978, 13979, 13980, - 13981, 13982, 13983, 13984, 13985, 13986, 13987, 13988, 13989, 13990, - 13991, 13992, 13993, 13994, 13995, 13996, 13997, 13998, 13999, 14000, - 14001, 14002, 14003, 14004, 14005, 14006, 14007, 14008, 14009, 14010, - 14011, 14012, 14013, 14014, 14015, 14016, 14017, 14018, 14019, 14020, - 14021, 14022, 14023, 14024, 14025, 14026, 14027, 14028, 14029, 14030, - 14031, 14032, 14033, 14034, 14035, 14036, 14037, 14038, 14039, 14040, - 14041, 14042, 14043, 14044, 14045, 14046, 14047, 14048, 14049, 14050, - 14051, 14052, 14053, 14054, 14055, 14056, 14057, 14058, 14059, 14060, - 14061, 14062, 14063, 14064, 14065, 14066, 14067, 14068, 14069, 14070, - 14071, 14072, 14073, 14074, 14075, 14076, 14077, 14078, 14079, 14080, - 14081, 14082, 14083, 14084, 14085, 14086, 14087, 14088, 14089, 14090, - 14091, 14092, 14093, 14094, 14095, 14096, 14097, 14098, 14099, 14100, - 14101, 14102, 14103, 14104, 14105, 14106, 14107, 14108, 14109, 14110, - 14111, 14112, 14113, 14114, 14115, 14116, 14117, 14118, 14119, 14120, - 14121, 14122, 14123, 14124, 14125, 14126, 14127, 14128, 14129, 14130, - 14131, 14132, 14133, 14134, 14135, 14136, 14137, 14138, 14139, 14140, - 14141, 14142, 14143, 14144, 14145, 14146, 14147, 14148, 14149, 14150, - 14151, 14152, 14153, 14154, 14155, 14156, 14157, 14158, 14159, 14160, - 14161, 14162, 14163, 14164, 14165, 14166, 14167, 14168, 14169, 14170, - 14171, 14172, 14173, 14174, 14175, 14176, 14177, 14178, 14179, 14180, - 14181, 14182, 14183, 14184, 14185, 14186, 14187, 14188, 14189, 14190, - 14191, 14192, 14193, 14194, 14195, 14196, 14197, 14198, 14199, 14200, - 14201, 14202, 14203, 14204, 14205, 14206, 14207, 14208, 14209, 14210, - 14211, 14212, 14213, 14214, 14215, 14216, 14217, 14218, 14219, 14220, - 14221, 14222, 14223, 14224, 14225, 14226, 14227, 14228, 14229, 14230, - 14231, 14232, 14233, 14234, 14235, 14236, 14237, 14238, 14239, 14240, - 14241, 14242, 14243, 14244, 14245, 14246, 14247, 14248, 14249, 14250, - 14251, 14252, 14253, 14254, 14255, 14256, 14257, 14258, 14259, 14260, - 14261, 14262, 14263, 14264, 14265, 14266, 14267, 14268, 14269, 14270, - 14271, 14272, 14273, 14274, 14275, 14276, 14277, 14278, 14279, 14280, - 14281, 14282, 14283, 14284, 14285, 14286, 14287, 14288, 14289, 14290, - 14291, 14292, 14293, 14294, 14295, 14296, 14297, 14298, 14299, 14300, - 14301, 14302, 14303, 14304, 14305, 14306, 14307, 14308, 14309, 14310, - 14311, 14312, 14313, 14314, 14315, 14316, 14317, 14318, 14319, 14320, - 14321, 14322, 14323, 14324, 14325, 14326, 14327, 14328, 14329, 14330, - 14331, 14332, 14333, 14334, 14335, 14336, 14337, 14338, 14339, 14340, - 14341, 14342, 14343, 14344, 14345, 14346, 14347, 14348, 14349, 14350, - 14351, 14352, 14353, 14354, 14355, 14356, 14357, 14358, 14359, 14360, - 14361, 14362, 14363, 14364, 14365, 14366, 14367, 14368, 14369, 14370, - 14371, 14372, 14373, 14374, 14375, 14376, 14377, 14378, 14379, 14380, - 14381, 14382, 14383, 14384, 14385, 14386, 14387, 14388, 14389, 14390, - 14391, 14392, 14393, 14394, 14395, 14396, 14397, 14398, 14399, 14400, - 14401, 14402, 14403, 14404, 14405, 14406, 14407, 14408, 14409, 14410, - 14411, 14412, 14413, 14414, 14415, 14416, 14417, 14418, 14419, 14420, - 14421, 14422, 14423, 14424, 14425, 14426, 14427, 14428, 14429, 14430, - 14431, 14432, 14433, 14434, 14435, 14436, 14437, 14438, 14439, 14440, - 14441, 14442, 14443, 14444, 14445, 14446, 14447, 14448, 14449, 14450, - 14451, 14452, 14453, 14454, 14455, 14456, 14457, 14458, 14459, 14460, - 14461, 14462, 14463, 14464, 14465, 14466, 14467, 14468, 14469, 14470, - 14471, 14472, 14473, 14474, 14475, 14476, 14477, 14478, 14479, 14480, - 14481, 14482, 14483, 14484, 14485, 14486, 14487, 14488, 14489, 14490, - 14491, 14492, 14493, 14494, 14495, 14496, 14497, 14498, 14499, 14500, - 14501, 14502, 14503, 14504, 14505, 14506, 14507, 14508, 14509, 14510, - 14511, 14512, 14513, 14514, 14515, 14516, 14517, 14518, 14519, 14520, - 14521, 14522, 14523, 14524, 14525, 14526, 14527, 14528, 14529, 14530, - 14531, 14532, 14533, 14534, 14535, 14536, 14537, 14538, 14539, 14540, - 14541, 14542, 14543, 14544, 14545, 14546, 14547, 14548, 14549, 14550, - 14551, 14552, 14553, 14554, 14555, 14556, 14557, 14558, 14559, 14560, - 14561, 14562, 14563, 14564, 14565, 14566, 14567, 14568, 14569, 14570, - 14571, 14572, 14573, 14574, 14575, 14576, 14577, 14578, 14579, 14580, - 14581, 14582, 14583, 14584, 14585, 14586, 14587, 14588, 14589, 14590, - 14591, 14592, 14593, 14594, 14595, 14596, 14597, 14598, 14599, 14600, - 14601, 14602, 14603, 14604, 14605, 14606, 14607, 14608, 14609, 14610, - 14611, 14612, 14613, 14614, 14615, 14616, 14617, 14618, 14619, 14620, - 14621, 14622, 14623, 14624, 14625, 14626, 14627, 14628, 14629, 14630, - 14631, 14632, 14633, 14634, 14635, 14636, 14637, 14638, 14639, 14640, - 14641, 14642, 14643, 14644, 14645, 14646, 14647, 14648, 14649, 14650, - 14651, 14652, 14653, 14654, 14655, 14656, 14657, 14658, 14659, 14660, - 14661, 14662, 14663, 14664, 14665, 14666, 14667, 14668, 14669, 14670, - 14671, 14672, 14673, 14674, 14675, 14676, 14677, 14678, 14679, 14680, - 14681, 14682, 14683, 14684, 14685, 14686, 14687, 14688, 14689, 14690, - 14691, 14692, 14693, 14694, 14695, 14696, 14697, 14698, 14699, 14700, - 14701, 14702, 14703, 14704, 14705, 14706, 14707, 14708, 14709, 14710, - 14711, 14712, 14713, 14714, 14715, 14716, 14717, 14718, 14719, 14720, - 14721, 14722, 14723, 14724, 14725, 14726, 14727, 14728, 14729, 14730, - 14731, 14732, 14733, 14734, 14735, 14736, 14737, 14738, 14739, 14740, - 14741, 14742, 14743, 14744, 14745, 14746, 14747, 14748, 14749, 14750, - 14751, 14752, 14753, 14754, 14755, 14756, 14757, 14758, 14759, 14760, - 14761, 14762, 14763, 14764, 14765, 14766, 14767, 14768, 14769, 14770, - 14771, 14772, 14773, 14774, 14775, 14776, 14777, 14778, 14779, 14780, - 14781, 14782, 14783, 14784, 14785, 14786, 14787, 14788, 14789, 14790, - 14791, 14792, 14793, 14794, 14795, 14796, 14797, 14798, 14799, 14800, - 14801, 14802, 14803, 14804, 14805, 14806, 14807, 14808, 14809, 14810, - 14811, 14812, 14813, 14814, 14815, 14816, 14817, 14818, 14819, 14820, - 14821, 14822, 14823, 14824, 14825, 14826, 14827, 14828, 14829, 14830, - 14831, 14832, 14833, 14834, 14835, 14836, 14837, 14838, 14839, 14840, - 14841, 14842, 14843, 14844, 14845, 14846, 14847, 14848, 14849, 14850, - 14851, 14852, 14853, 14854, 14855, 14856, 14857, 14858, 14859, 14860, - 14861, 14862, 14863, 14864, 14865, 14866, 14867, 14868, 14869, 14870, - 14871, 14872, 14873, 14874, 14875, 14876, 14877, 14878, 14879, 14880, - 14881, 14882, 14883, 14884, 14885, 14886, 14887, 14888, 14889, 14890, - 14891, 14892, 14893, 14894, 14895, 14896, 14897, 14898, 14899, 14900, - 14901, 14902, 14903, 14904, 14905, 14906, 14907, 14908, 14909, 14910, - 14911, 14912, 14913, 14914, 14915, 14916, 14917, 14918, 14919, 14920, - 14921, 14922, 14923, 14924, 14925, 14926, 14927, 14928, 14929, 14930, - 14931, 14932, 14933, 14934, 14935, 14936, 14937, 14938, 14939, 14940, - 14941, 14942, 14943, 14944, 14945, 14946, 14947, 14948, 14949, 14950, - 14951, 14952, 14953, 14954, 14955, 14956, 14957, 14958, 14959, 14960, - 14961, 14962, 14963, 14964, 14965, 14966, 14967, 14968, 14969, 14970, - 14971, 14972, 14973, 14974, 14975, 14976, 14977, 14978, 14979, 14980, - 14981, 14982, 14983, 14984, 14985, 14986, 14987, 14988, 14989, 14990, - 14991, 14992, 14993, 14994, 14995, 14996, 14997, 14998, 14999, 15000, - 15001, 15002, 15003, 15004, 15005, 15006, 15007, 15008, 15009, 15010, - 15011, 15012, 15013, 15014, 15015, 15016, 15017, 15018, 15019, 15020, - 15021, 15022, 15023, 15024, 15025, 15026, 15027, 15028, 15029, 15030, - 15031, 15032, 15033, 15034, 15035, 15036, 15037, 15038, 15039, 15040, - 15041, 15042, 15043, 15044, 15045, 15046, 15047, 15048, 15049, 15050, - 15051, 15052, 15053, 15054, 15055, 15056, 15057, 15058, 15059, 15060, - 15061, 15062, 15063, 15064, 15065, 15066, 15067, 15068, 15069, 15070, - 15071, 15072, 15073, 15074, 15075, 15076, 15077, 15078, 15079, 15080, - 15081, 15082, 15083, 15084, 15085, 15086, 15087, 15088, 15089, 15090, - 15091, 15092, 15093, 15094, 15095, 15096, 15097, 15098, 15099, 15100, - 15101, 15102, 15103, 15104, 15105, 15106, 15107, 15108, 15109, 15110, - 15111, 15112, 15113, 15114, 15115, 15116, 15117, 15118, 15119, 15120, - 15121, 15122, 15123, 15124, 15125, 15126, 15127, 15128, 15129, 15130, - 15131, 15132, 15133, 15134, 15135, 15136, 15137, 15138, 15139, 15140, - 15141, 15142, 15143, 15144, 15145, 15146, 15147, 15148, 15149, 15150, - 15151, 15152, 15153, 15154, 15155, 15156, 15157, 15158, 15159, 15160, - 15161, 15162, 15163, 15164, 15165, 15166, 15167, 15168, 15169, 15170, - 15171, 15172, 15173, 15174, 15175, 15176, 15177, 15178, 15179, 15180, - 15181, 15182, 15183, 15184, 15185, 15186, 15187, 15188, 15189, 15190, - 15191, 15192, 15193, 15194, 15195, 15196, 15197, 15198, 15199, 15200, - 15201, 15202, 15203, 15204, 15205, 15206, 15207, 15208, 15209, 15210, - 15211, 15212, 15213, 15214, 15215, 15216, 15217, 15218, 15219, 15220, - 15221, 15222, 15223, 15224, 15225, 15226, 15227, 15228, 15229, 15230, - 15231, 15232, 15233, 15234, 15235, 15236, 15237, 15238, 15239, 15240, - 15241, 15242, 15243, 15244, 15245, 15246, 15247, 15248, 15249, 15250, - 15251, 15252, 15253, 15254, 15255, 15256, 15257, 15258, 15259, 15260, - 15261, 15262, 15263, 15264, 15265, 15266, 15267, 15268, 15269, 15270, - 15271, 15272, 15273, 15274, 15275, 15276, 15277, 15278, 15279, 15280, - 15281, 15282, 15283, 15284, 15285, 15286, 15287, 15288, 15289, 15290, - 15291, 15292, 15293, 15294, 15295, 15296, 15297, 15298, 15299, 15300, - 15301, 15302, 15303, 15304, 15305, 15306, 15307, 15308, 15309, 15310, - 15311, 15312, 15313, 15314, 15315, 15316, 15317, 15318, 15319, 15320, - 15321, 15322, 15323, 15324, 15325, 15326, 15327, 15328, 15329, 15330, - 15331, 15332, 15333, 15334, 15335, 15336, 15337, 15338, 15339, 15340, - 15341, 15342, 15343, 15344, 15345, 15346, 15347, 15348, 15349, 15350, - 15351, 15352, 15353, 15354, 15355, 15356, 15357, 15358, 15359, 15360, - 15361, 15362, 15363, 15364, 15365, 15366, 15367, 15368, 15369, 15370, - 15371, 15372, 15373, 15374, 15375, 15376, 15377, 15378, 15379, 15380, - 15381, 15382, 15383, 15384, 15385, 15386, 15387, 15388, 15389, 15390, - 15391, 15392, 15393, 15394, 15395, 15396, 15397, 15398, 15399, 15400, - 15401, 15402, 15403, 15404, 15405, 15406, 15407, 15408, 15409, 15410, - 15411, 15412, 15413, 15414, 15415, 15416, 15417, 15418, 15419, 15420, - 15421, 15422, 15423, 15424, 15425, 15426, 15427, 15428, 15429, 15430, - 15431, 15432, 15433, 15434, 15435, 15436, 15437, 15438, 15439, 15440, - 15441, 15442, 15443, 15444, 15445, 15446, 15447, 15448, 15449, 15450, - 15451, 15452, 15453, 15454, 15455, 15456, 15457, 15458, 15459, 15460, - 15461, 15462, 15463, 15464, 15465, 15466, 15467, 15468, 15469, 15470, - 15471, 15472, 15473, 15474, 15475, 15476, 15477, 15478, 15479, 15480, - 15481, 15482, 15483, 15484, 15485, 15486, 15487, 15488, 15489, 15490, - 15491, 15492, 15493, 15494, 15495, 15496, 15497, 15498, 15499, 15500, - 15501, 15502, 15503, 15504, 15505, 15506, 15507, 15508, 15509, 15510, - 15511, 15512, 15513, 15514, 15515, 15516, 15517, 15518, 15519, 15520, - 15521, 15522, 15523, 15524, 15525, 15526, 15527, 15528, 15529, 15530, - 15531, 15532, 15533, 15534, 15535, 15536, 15537, 15538, 15539, 15540, - 15541, 15542, 15543, 15544, 15545, 15546, 15547, 15548, 15549, 15550, - 15551, 15552, 15553, 15554, 15555, 15556, 15557, 15558, 15559, 15560, - 15561, 15562, 15563, 15564, 15565, 15566, 15567, 15568, 15569, 15570, - 15571, 15572, 15573, 15574, 15575, 15576, 15577, 15578, 15579, 15580, - 15581, 15582, 15583, 15584, 15585, 15586, 15587, 15588, 15589, 15590, - 15591, 15592, 15593, 15594, 15595, 15596, 15597, 15598, 15599, 15600, - 15601, 15602, 15603, 15604, 15605, 15606, 15607, 15608, 15609, 15610, - 15611, 15612, 15613, 15614, 15615, 15616, 15617, 15618, 15619, 15620, - 15621, 15622, 15623, 15624, 15625, 15626, 15627, 15628, 15880, 15881, - 15882, 15883, 15884, 15885, 15886, 15887, 15888, 15889, 15890, 15891, - 15892, 15893, 15894, 15895, 15896, 15897, 15898, 15899, 15900, 15901, - 15902, 15903, 15904, 15905, 15906, 15907, 15908, 15909, 15910, 15911, - 15912, 15913, 15914, 15915, 15916, 15917, 15918, 15919, 15920, 15921, - 15922, 15923, 15924, 15925, 15926, 15927, 15928, 15929, 15930, 15931, - 15932, 15933, 15934, 15935, 15936, 15937, 15938, 15939, 15940, 15941, - 15942, 15943, 15944, 15945, 15946, 15947, 15948, 15949, 15950, 15951, - 15952, 15953, 15954, 15955, 15956, 15957, 15958, 15959, 15960, 15961, - 15962, 15963, 15964, 15965, 15966, 15967, 15968, 15969, 15970, 15971, - 15972, 15973, 15974, 15975, 15976, 15977, 15978, 15979, 15980, 15981, - 15982, 15983, 15984, 15985, 15986, 15987, 15988, 15989, 15990, 15991, - 15992, 15993, 15994, 15995, 15996, 15997, 15998, 15999, 16000, 16001, - 16002, 16003, 16004, 16005, 16006, 16007, 16008, 16009, 16010, 16011, - 16012, 16013, 16014, 16015, 16016, 16017, 16018, 16019, 16020, 16021, - 16022, 16023, 16024, 16025, 16026, 16027, 16028, 16029, 16030, 16031, - 16032, 16033, 16034, 16035, 16036, 16037, 16038, 16039, 16040, 16041, - 16042, 16043, 16044, 16045, 16046, 16047, 16048, 16049, 16050, 16051, - 16052, 16053, 16054, 16055, 16056, 16057, 16058, 16059, 16060, 16061, - 16062, 16063, 16064, 16065, 16066, 16067, 16068, 16069, 16070, 16071, - 16072, 16073, 16074, 16075, 16076, 16077, 16078, 16079, 16080, 16081, - 16082, 16083, 16084, 16085, 16086, 16087, 16088, 16089, 16090, 16091, - 16092, 16093, 16094, 16095, 16096, 16097, 16098, 16099, 16100, 16101, - 16102, 16103, 16104, 16105, 16106, 16107, 16108, 16109, 16110, 16111, - 16112, 16113, 16114, 16115, 16116, 16117, 16118, 16119, 16120, 16121, - 16122, 16123, 16124, 16125, 16126, 16127, 16128, 16129, 16130, 16131, - 16132, 16133, 16134, 16135, 16136, 16137, 16138, 16139, 16140, 16141, - 16142, 16143, 16144, 16145, 16146, 16147, 16148, 16149, 16150, 16151, - 16152, 16153, 16154, 16155, 16156, 16157, 16158, 16159, 16160, 16161, - 16162, 16163, 16164, 16165, 16166, 16167, 16168, 16169, 16170, 16171, - 16172, 16173, 16174, 16175, 16176, 16177, 16178, 16179, 16180, 16181, - 16182, 16183, 16184, 16185, 16186, 16187, 16188, 16189, 16190, 16191, - 16192, 16193, 16194, 16195, 16196, 16197, 16198, 16199, 16200, 16201, - 16202, 16203, 16204, 16205, 16206, 16207, 16208, 16209, 16210, 16211, - 16212, 16213, 16214, 16215, 16216, 16217, 16218, 16219, 16220, 16221, - 16222, 16223, 16224, 16225, 16226, 16227, 16228, 16229, 16230, 16231, - 16232, 16233, 16234, 16235, 16236, 16237, 16238, 16239, 16240, 16241, - 16242, 16243, 16244, 16245, 16246, 16247, 16248, 16249, 16250, 16251, - 16252, 16253, 16254, 16255, 16256, 16257, 16258, 16259, 16260, 16261, - 16262, 16263, 16264, 16265, 16266, 16267, 16268, 16269, 16270, 16271, - 16272, 16273, 16274, 16275, 16276, 16277, 16278, 16279, 16280, 16281, - 16282, 16283, 16284, 16285, 16286, 16287, 16288, 16289, 16290, 16291, - 16292, 16293, 16294, 16295, 16296, 16297, 16298, 16299, 16300, 16301, - 16302, 16303, 16304, 16305, 16306, 16307, 16308, 16309, 16310, 16311, - 16312, 16313, 16314, 16315, 16316, 16317, 16318, 16319, 16320, 16321, - 16322, 16323, 16324, 16325, 16326, 16327, 16328, 16329, 16330, 16331, - 16332, 16333, 16334, 16335, 16336, 16337, 16338, 16339, 16340, 16341, - 16342, 16343, 16344, 16345, 16346, 16347, 16348, 16349, 16350, 16351, - 16352, 16353, 16354, 16355, 16356, 16357, 16358, 16359, 16360, 16361, - 16362, 16363, 16364, 16365, 16366, 16367, 16368, 16369, 16370, 16371, - 16372, 16373, 16374, 16375, 16376, 16377, 16378, 16379, 16380, 16381, - 16382, 16383, 16384, 16385, 16386, 16387, 16388, 16389, 16390, 16391, - 16392, 16393, 16394, 16395, 16396, 16397, 16398, 16399, 16400, 16401, - 16402, 16403, 16404, 16405, 16406, 16407, 16408, 16409, 16410, 16411, - 16412, 16413, 16414, 16415, 16416, 16417, 16418, 16419, 16420, 16421, - 16422, 16423, 16424, 16425, 16426, 16427, 16428, 16429, 16430, 16431, - 16432, 16433, 16434, 16435, 16436, 16437, 16438, 16439, 16440, 16441, - 16442, 16443, 16444, 16445, 16446, 16447, 16448, 16449, 16450, 16451, - 16452, 16453, 16454, 16455, 16456, 16457, 16458, 16459, 16460, 16461, - 16462, 16463, 16464, 16465, 16466, 16467, 16468, 16469, 16470, 16471, - 16472, 16473, 16474, 16475, 16476, 16477, 16478, 16479, 16480, 16481, - 16482, 16483, 16484, 16485, 16486, 16487, 16488, 16489, 16490, 16491, - 16492, 16493, 16494, 16495, 16496, 16497, 16498, 16499, 16500, 16501, - 16502, 16503, 16504, 16505, 16506, 16507, 16508, 16509, 16510, 16511, - 16512, 16513, 16514, 16515, 16516, 16517, 16518, 16519, 16520, 16521, - 16522, 16523, 16524, 16525, 16526, 16527, 16528, 16529, 16530, 16531, - 16532, 16533, 16534, 16535, 16536, 16537, 16538, 16539, 16540, 16541, - 16542, 16543, 16544, 16545, 16546, 16547, 16548, 16549, 16550, 16551, - 16552, 16553, 16554, 16555, 16556, 16557, 16558, 16559, 16560, 16561, - 16562, 16563, 16564, 16565, 16566, 16567, 16568, 16569, 16570, 16571, - 16572, 16573, 16574, 16575, 16576, 16577, 16578, 16579, 16580, 16581, - 16582, 16583, 16584, 16585, 16586, 16587, 16588, 16589, 16590, 16591, - 16592, 16593, 16594, 16595, 16596, 16597, 16598, 16599, 16600, 16601, - 16602, 16603, 16604, 16605, 16606, 16607, 16608, 16609, 16610, 16611, - 16612, 16613, 16614, 16615, 16616, 16617, 16618, 16619, 16620, 16621, - 16622, 16623, 16624, 16625, 16626, 16627, 16628, 16629, 16630, 16631, - 16632, 16633, 16634, 16635, 16636, 16637, 16638, 16639, 16640, 16641, - 16642, 16643, 16644, 16645, 16646, 16647, 15640, 15641, 15642, 15643, - 15644, 15645, 15646, 15647, 15648, 15649, 15650, 15651, 15652, 15653, - 15654, 15655, 15656, 15657, 15658, 15659, 15660, 15661, 15662, 15663, - 15664, 15665, 15666, 15667, 15668, 15669, 15670, 15671, 15672, 15673, - 15674, 15675, 15676, 15677, 15678, 15679, 15680, 15681, 15682, 15683, - 15684, 15685, 15686, 15687, 15688, 15689, 15690, 15691, 15692, 15693, - 15694, 15695, 15696, 15697, 15698, 15699, 15700, 15701, 15702, 15703, - 15704, 15705, 15706, 15707, 15708, 15709, 15710, 15711, 15712, 15713, - 15714, 15715, 15716, 15717, 15718, 15719, 15720, 15721, 15722, 15723, - 15724, 15725, 15726, 15727, 15728, 15729, 15730, 15731, 15732, 15733, - 15734, 15735, 15736, 15737, 15738, 15739, 15740, 15741, 15742, 15743, - 15744, 15745, 15746, 15747, 15748, 15749, 15750, 15751, 15752, 15753, - 15754, 15755, 15756, 15757, 15758, 15759, 15760, 15761, 15762, 15763, - 15764, 15765, 15766, 15767, 15768, 15769, 15770, 15771, 15772, 15773, - 15774, 15775, 15776, 15777, 15778, 15779, 15780, 15781, 15782, 15783, - 15784, 15785, 15786, 15787, 15788, 15789, 15790, 15791, 15792, 15793, - 15794, 15795, 15796, 15797, 15798, 15799, 15800, 15801, 15802, 15803, - 15804, 15805, 15806, 15807, 15808, 15809, 15810, 15811, 15812, 15813, - 15814, 15815, 15816, 15817, 15818, 15819, 15820, 15821, 15822, 15823, - 15824, 15825, 15826, 15827, 15828, 15829, 15830, 15831, 15832, 15833, - 15834, 15835, 15836, 15837, 15838, 15839, 15840, 15841, 15842, 15843, - 15844, 15845, 15846, 15847, 15848, 15849, 15850, 15851, 15852, 15853, - 15854, 15855, 15856, 15857, 15858, 15859, 15860, 15861, 15862, 15863, - 15864, 15865, 15866, 15867, 15868, 15869, 15870, 15871, 15872, 15873, - 15874, 15875, 15876, 15877, 15878, 15879, 15629, 15630, 15631, 15632, - 15633, 15634, 15635, 15636, 15637, 15638, 15639, 40951, 40951, 40951, - 40951, 40951, 445, 446, 447, 448, 449, 450, 451, 452, 453, 434, 435, 436, - 437, 438, 439, 440, 441, 442, 443, 444, 375, 376, 377, 378, 379, 380, - 373, 374, 381, 382, 383, 425, 426, 427, 428, 429, 430, 431, 432, 433, - 423, 424, 391, 387, 388, 392, 393, 394, 389, 390, 384, 385, 386, 395, - 396, 397, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 402, 403, - 404, 405, 406, 407, 398, 399, 400, 401, 408, 409, 410, 464, 465, 466, - 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, - 481, 482, 483, 415, 416, 417, 418, 419, 420, 421, 411, 412, 413, 414, - 422, 495, 496, 497, 498, 499, 500, 501, 484, 485, 486, 487, 492, 493, - 494, 502, 488, 489, 490, 491, 503, 504, 505, 506, 507, 510, 511, 512, - 513, 508, 509, 514, 515, 516, 517, 520, 521, 522, 523, 524, 518, 519, - 525, 526, 527, 528, 531, 532, 533, 534, 535, 529, 530, 536, 537, 538, - 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, - 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, - 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, - 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, - 595, 596, 597, 598, 599, 607, 608, 600, 601, 602, 609, 610, 611, 612, - 603, 604, 613, 605, 606, 618, 619, 620, 621, 622, 614, 615, 616, 617, - 623, 624, 625, 651, 652, 653, 654, 655, 656, 657, 649, 650, 658, 659, - 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, - 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, - 699, 700, 662, 663, 664, 665, 666, 667, 668, 660, 661, 669, 670, 701, - 702, 703, 704, 705, 706, 707, 708, 709, 710, 640, 641, 642, 643, 644, - 645, 646, 647, 648, 638, 639, 630, 631, 632, 633, 626, 627, 634, 635, - 636, 637, 628, 629, 713, 714, 715, 716, 717, 718, 719, 720, 721, 711, - 712, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 724, 725, 726, - 727, 728, 729, 730, 731, 732, 722, 723, 751, 752, 748, 749, 750, 753, - 754, 755, 744, 745, 746, 747, 756, 757, 758, 815, 816, 817, 818, 819, - 820, 821, 822, 823, 824, 735, 736, 737, 738, 739, 740, 741, 742, 743, - 733, 734, 763, 764, 765, 766, 759, 760, 767, 768, 769, 761, 762, 770, - 796, 794, 795, 797, 798, 799, 800, 801, 802, 803, 804, 777, 773, 774, - 778, 771, 772, 779, 780, 775, 776, 781, 782, 783, 785, 786, 787, 784, - 788, 789, 790, 791, 792, 793, 856, 857, 858, 859, 860, 861, 862, 863, - 864, 865, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 866, - 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, - 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, - 895, 836, 837, 840, 841, 842, 843, 844, 845, 838, 839, 846, 847, 896, - 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, - 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, - 925, 848, 849, 850, 851, 852, 853, 854, 855, 927, 928, 929, 930, 931, - 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, - 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 926, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 18950, 18940, 18939, 18936, 18935, 18921, 18934, - 18933, 18938, 18937, 18943, 18928, 18927, 18924, 18923, 18948, 18930, - 18929, 18926, 18925, 18922, 18942, 18941, 18932, 18931, 18945, 18949, - 18946, 18944, 18947, 18953, 18960, 18961, 18956, 18957, 18962, 18963, - 18954, 18958, 18959, 18955, 18964, 18920, 18919, 18917, 18951, 18918, - 18952, 18971, 18973, 18970, 18969, 18966, 18965, 18968, 18967, 18974, - 18972, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 2847, - 2813, 2872, 2873, 2843, 2880, 2891, 2862, 2879, 2874, 2875, 2826, 2892, - 2849, 2828, 2833, 2840, 2886, 2858, 2816, 2852, 2887, 2848, 2823, 2824, - 2856, 2830, 2871, 2812, 2870, 2839, 2863, 2894, 2811, 2857, 2888, 2842, - 2841, 2837, 2814, 2869, 2846, 2876, 2829, 2866, 2877, 2893, 2821, 2883, - 2890, 2831, 2817, 2845, 2819, 2838, 2881, 2822, 2896, 2898, 2818, 2884, - 2834, 2878, 2855, 2882, 2860, 2867, 2851, 2895, 2850, 2832, 2864, 2835, - 2861, 2889, 2885, 2868, 2853, 2825, 2859, 2815, 2854, 2897, 2820, 2865, - 2844, 2836, 2930, 2946, 2944, 2943, 2909, 2915, 2904, 2950, 2914, 2906, - 2936, 2949, 2908, 2934, 2935, 2899, 2939, 2947, 2941, 2942, 2921, 2918, - 2933, 2902, 2900, 2901, 2907, 2937, 2954, 2928, 2926, 2951, 2940, 2948, - 2920, 2924, 2925, 2922, 2945, 2917, 2923, 2932, 2938, 2919, 2952, 2916, - 2953, 2903, 2913, 2912, 2910, 2927, 2931, 2911, 2905, 2929, 3006, 3026, - 3047, 3043, 3005, 2994, 3007, 2955, 2983, 2957, 3027, 3023, 2980, 3028, - 2998, 2977, 2960, 2956, 2962, 3046, 3025, 2988, 3018, 2986, 3048, 2967, - 2968, 3032, 2999, 3036, 3014, 3040, 3035, 3001, 3042, 2992, 2974, 3024, - 3002, 2970, 2985, 2990, 3021, 3037, 3004, 3051, 2995, 3020, 3017, 3050, - 3041, 2982, 3052, 3010, 2969, 3039, 3015, 3012, 2964, 3000, 2976, 2987, - 2972, 2966, 3011, 3009, 3044, 3003, 3019, 3022, 2965, 3016, 2996, 2973, - 3049, 2997, 3033, 3031, 2984, 2975, 2979, 2961, 2981, 2963, 2978, 3045, - 3013, 2991, 2989, 3034, 2959, 2958, 3008, 2993, 2971, 3029, 3030, 3038, - 3080, 3164, 3113, 3087, 3114, 3073, 3112, 3118, 3100, 3127, 3163, 3109, - 3143, 3110, 3057, 3156, 3141, 3116, 3148, 3061, 3165, 3063, 3150, 3085, - 3095, 3076, 3082, 3152, 3169, 3111, 3058, 3106, 3158, 3056, 3108, 3053, - 3096, 3090, 3068, 3097, 3092, 3091, 3133, 3089, 3086, 3074, 3119, 3078, - 3066, 3125, 3154, 3153, 3166, 3059, 3140, 3157, 3105, 3084, 3120, 3060, - 3136, 3131, 3126, 3071, 3099, 3088, 3103, 3167, 3135, 3168, 3098, 3122, - 3147, 3064, 3102, 3107, 3159, 3081, 3065, 3121, 3155, 3077, 3101, 3069, - 3104, 3117, 3115, 3055, 3062, 3137, 3161, 3160, 3128, 3139, 3070, 3083, - 3075, 3149, 3094, 3145, 3142, 3067, 3130, 3146, 3123, 3132, 3129, 3144, - 3134, 3093, 3072, 3138, 3162, 3124, 3079, 3151, 3054, 3223, 3300, 3210, - 3197, 3307, 3200, 3263, 3289, 3279, 3249, 3226, 3274, 3294, 3235, 3190, - 3310, 3282, 3228, 3264, 3299, 3192, 3202, 3251, 3241, 3207, 3238, 3242, - 3250, 3281, 3248, 3267, 3290, 3231, 3214, 3184, 3237, 3245, 3208, 3201, - 3229, 3225, 3291, 3283, 3277, 3222, 3230, 3315, 3183, 3304, 3311, 3271, - 3303, 3187, 3288, 3297, 3305, 3308, 3196, 3273, 3293, 3181, 3243, 3284, - 3213, 3211, 3256, 3260, 3180, 3302, 3191, 3323, 3254, 3316, 3212, 3224, - 3269, 3319, 3199, 3173, 3179, 3240, 3189, 3206, 3236, 3185, 3175, 3252, - 3265, 3313, 3227, 3255, 3257, 3272, 3216, 3174, 3259, 3217, 3182, 3172, - 3220, 3275, 3239, 3193, 3270, 3253, 3312, 3232, 3261, 3171, 3178, 3246, - 3324, 3301, 3326, 3325, 3198, 3262, 3292, 3295, 3221, 3287, 3314, 3233, - 3320, 3317, 3318, 3322, 3321, 3188, 3266, 3247, 3276, 3309, 3177, 3306, - 3204, 3215, 3280, 3278, 3234, 3244, 3285, 3286, 3170, 3258, 3268, 3203, - 3195, 3218, 3205, 3209, 3296, 3194, 3219, 3298, 3176, 3186, 3331, 3380, - 3333, 3378, 3358, 3371, 3352, 3335, 3361, 3360, 3340, 3370, 3350, 3346, - 3337, 3368, 3363, 3369, 3366, 3329, 3328, 3348, 3347, 3345, 3373, 3365, - 3374, 3349, 3354, 3351, 3375, 3355, 3362, 3353, 3357, 3327, 3343, 3344, - 3364, 3356, 3379, 3376, 3336, 3334, 3332, 3338, 3359, 3341, 3342, 3339, - 3372, 3330, 3367, 3377, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 28219, 28211, 28232, 28209, 28233, 28220, 28235, 28215, 28206, 28223, - 28221, 28231, 28205, 28213, 28208, 28210, 28216, 28214, 28212, 28225, - 28228, 28217, 28227, 28234, 28226, 28207, 28230, 28229, 28222, 28224, - 28218, 40951, 28244, 28246, 28243, 28242, 28239, 28238, 28241, 28240, - 28247, 28245, 40951, 40951, 40951, 40951, 28237, 28236, 35842, 35839, - 35840, 35841, 35794, 35791, 35792, 35793, 35846, 35843, 35844, 35845, - 35834, 35831, 35832, 35833, 35838, 35835, 35836, 35837, 35830, 35827, - 35828, 35829, 35790, 35787, 35788, 35789, 35822, 35819, 35820, 35821, - 35795, 35800, 35811, 35812, 35823, 35826, 35824, 35825, 35818, 35815, - 35816, 35817, 35806, 35803, 35804, 35805, 35857, 35856, 35855, 35807, - 35814, 35864, 35862, 35859, 35809, 35858, 35860, 35802, 35810, 35799, - 35801, 35798, 35849, 35853, 35861, 35808, 35813, 35851, 35848, 35854, - 35797, 35847, 35863, 35796, 35852, 35850, 35865, 40951, 35872, 35874, - 35871, 35870, 35867, 35866, 35869, 35868, 35875, 35873, 40951, 40951, - 40951, 40951, 40951, 40951, 3441, 3446, 3462, 3464, 3454, 3452, 3444, - 3438, 3445, 3458, 3453, 3449, 3460, 3443, 3439, 3461, 3448, 3459, 3463, - 3457, 3451, 3465, 3450, 3466, 3455, 3456, 3447, 3442, 3440, 3467, 40951, - 40951, 3434, 3436, 3437, 3435, 3433, 3468, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 30994, 30995, 31000, 31001, - 30988, 30989, 31004, 31005, 30996, 30997, 30986, 30987, 31006, 31007, - 30990, 30991, 31002, 31003, 31008, 31009, 30998, 30999, 30992, 30993, - 31010, 31011, 30984, 30985, 30930, 30921, 30927, 30918, 30923, 30929, - 30922, 30926, 30932, 30916, 30928, 30914, 30919, 30917, 30925, 30920, - 30924, 30933, 30931, 30915, 30938, 30937, 30935, 30934, 30936, 30940, - 30939, 30970, 30971, 30949, 30969, 30967, 30975, 30977, 30976, 30978, - 30968, 30960, 30973, 30958, 30980, 30953, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 31018, 31020, 31017, 31016, - 31013, 31012, 31015, 31014, 31021, 31019, 40951, 30945, 30942, 30944, - 30947, 30941, 30943, 30946, 40951, 30972, 30979, 30957, 30965, 30981, - 30956, 30963, 30974, 30962, 30983, 30964, 30959, 30966, 30982, 30961, - 30955, 30948, 30951, 30952, 30954, 30950, 40951, 40951, 40951, 40951, - 40951, 30902, 30909, 30901, 30900, 30910, 30898, 30895, 30913, 30905, - 30903, 30911, 30897, 30896, 30906, 30912, 30908, 30904, 30899, 30907, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 22525, 22522, 22527, 22521, - 22510, 22509, 22506, 22505, 22498, 22504, 22503, 22508, 22507, 22499, - 22495, 22494, 22491, 22490, 22497, 22496, 22493, 22492, 22500, 22512, - 22511, 22502, 22501, 22517, 22520, 22518, 22516, 22519, 22514, 22513, - 22515, 22528, 22534, 22531, 22532, 22533, 22529, 22535, 22530, 22526, - 22524, 22523, 22537, 22536, 22544, 22546, 22543, 22542, 22539, 22538, - 22541, 22540, 22547, 22545, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 26886, 26890, 26893, 26894, 26864, 26896, 26872, 26887, - 26891, 26882, 26881, 26883, 26871, 26863, 26884, 26880, 26877, 26878, - 26892, 26874, 26885, 26888, 26870, 26868, 26895, 26879, 26876, 26866, - 26889, 26875, 26865, 26873, 26944, 26948, 26951, 26952, 26922, 26954, - 26930, 26945, 26949, 26940, 26939, 26941, 26929, 26921, 26942, 26938, - 26935, 26936, 26950, 26932, 26943, 26946, 26928, 26926, 26953, 26937, - 26934, 26924, 26947, 26933, 26923, 26931, 26908, 26902, 26900, 26898, - 26905, 26904, 26907, 26906, 26910, 26909, 26913, 26915, 26912, 26911, - 26916, 26917, 26919, 26920, 26914, 26918, 26903, 26901, 26899, 26897, - 26957, 26955, 26956, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 27502, 27428, 27490, 27501, 27506, 27505, - 27425, 27507, 27482, 27481, 27479, 27436, 27485, 27486, 27478, 27435, - 27444, 27452, 27488, 27424, 27449, 27448, 27443, 27442, 27441, 27440, - 27466, 27434, 27465, 27433, 27508, 27439, 27489, 27500, 27499, 27447, - 27446, 27423, 27504, 27510, 27438, 27437, 27475, 27431, 27451, 27450, - 27474, 27430, 27483, 27487, 27459, 27462, 27463, 27497, 27495, 27476, - 27432, 27484, 27464, 27498, 27496, 27494, 27492, 27422, 27493, 27491, - 27509, 27426, 27503, 27427, 27461, 27429, 27480, 27477, 27460, 40951, - 40951, 40951, 40951, 27514, 27445, 27513, 27512, 27511, 27519, 27525, - 27524, 27520, 27521, 27546, 27550, 27567, 27566, 27528, 27531, 27532, - 27548, 27535, 27536, 27537, 27538, 27539, 27542, 27544, 27545, 27541, - 27552, 27553, 27554, 27555, 27560, 27556, 27557, 27561, 27563, 27522, - 27523, 27530, 27565, 27529, 27564, 27526, 27534, 27527, 27551, 27568, - 27569, 27558, 27562, 27549, 27547, 27570, 27543, 27533, 27540, 27559, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 27518, 27516, 27517, - 27515, 27467, 27468, 27469, 27470, 27471, 27472, 27473, 27453, 27454, - 27455, 27456, 27457, 27458, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 36644, 29851, - 30073, 30072, 22168, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 38877, 38876, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 35876, 35877, 35878, 35879, 35880, 35881, 35882, 35883, 35884, 35885, - 35886, 35887, 35888, 35889, 35890, 35891, 35892, 35893, 35894, 35895, - 35896, 35897, 35898, 35899, 35900, 35901, 35902, 35903, 35904, 35905, - 35906, 35907, 35908, 35909, 35910, 35911, 35912, 35913, 35914, 35915, - 35916, 35917, 35918, 35919, 35920, 35921, 35922, 35923, 35924, 35925, - 35926, 35927, 35928, 35929, 35930, 35931, 35932, 35933, 35934, 35935, - 35936, 35937, 35938, 35939, 35940, 35941, 35942, 35943, 35944, 35945, - 35946, 35947, 35948, 35949, 35950, 35951, 35952, 35953, 35954, 35955, - 35956, 35957, 35958, 35959, 35960, 35961, 35962, 35963, 35964, 35965, - 35966, 35967, 35968, 35969, 35970, 35971, 35972, 35973, 35974, 35975, - 35976, 35977, 35978, 35979, 35980, 35981, 35982, 35983, 35984, 35985, - 35986, 35987, 35988, 35989, 35990, 35991, 35992, 35993, 35994, 35995, - 35996, 35997, 35998, 35999, 36000, 36001, 36002, 36003, 36004, 36005, - 36006, 36007, 36008, 36009, 36010, 36011, 36012, 36013, 36014, 36015, - 36016, 36017, 36018, 36019, 36020, 36021, 36022, 36023, 36024, 36025, - 36026, 36027, 36028, 36029, 36030, 36031, 36032, 36033, 36034, 36035, - 36036, 36037, 36038, 36039, 36040, 36041, 36042, 36043, 36044, 36045, - 36046, 36047, 36048, 36049, 36050, 36051, 36052, 36053, 36054, 36055, - 36056, 36057, 36058, 36059, 36060, 36061, 36062, 36063, 36064, 36065, - 36066, 36067, 36068, 36069, 36070, 36071, 36072, 36073, 36074, 36075, - 36076, 36077, 36078, 36079, 36080, 36081, 36082, 36083, 36084, 36085, - 36086, 36087, 36088, 36089, 36090, 36091, 36092, 36093, 36094, 36095, - 36096, 36097, 36098, 36099, 36100, 36101, 36102, 36103, 36104, 36105, - 36106, 36107, 36108, 36109, 36110, 36111, 36112, 36113, 36114, 36115, - 36116, 36117, 36118, 36119, 36120, 36121, 36122, 36123, 36124, 36125, - 36126, 36127, 36128, 36129, 36130, 36131, 36132, 36133, 36134, 36135, - 36136, 36137, 36138, 36139, 36140, 36141, 36142, 36143, 36144, 36145, - 36146, 36147, 36148, 36149, 36150, 36151, 36152, 36153, 36154, 36155, - 36156, 36157, 36158, 36159, 36160, 36161, 36162, 36163, 36164, 36165, - 36166, 36167, 36168, 36169, 36170, 36171, 36172, 36173, 36174, 36175, - 36176, 36177, 36178, 36179, 36180, 36181, 36182, 36183, 36184, 36185, - 36186, 36187, 36188, 36189, 36190, 36191, 36192, 36193, 36194, 36195, - 36196, 36197, 36198, 36199, 36200, 36201, 36202, 36203, 36204, 36205, - 36206, 36207, 36208, 36209, 36210, 36211, 36212, 36213, 36214, 36215, - 36216, 36217, 36218, 36219, 36220, 36221, 36222, 36223, 36224, 36225, - 36226, 36227, 36228, 36229, 36230, 36231, 36232, 36233, 36234, 36235, - 36236, 36237, 36238, 36239, 36240, 36241, 36242, 36243, 36244, 36245, - 36246, 36247, 36248, 36249, 36250, 36251, 36252, 36253, 36254, 36255, - 36256, 36257, 36258, 36259, 36260, 36261, 36262, 36263, 36264, 36265, - 36266, 36267, 36268, 36269, 36270, 36271, 36272, 36273, 36274, 36275, - 36276, 36277, 36278, 36279, 36280, 36281, 36282, 36283, 36284, 36285, - 36286, 36287, 36288, 36289, 36290, 36291, 36292, 36293, 36294, 36295, - 36296, 36297, 36298, 36299, 36300, 36301, 36302, 36303, 36304, 36305, - 36306, 36307, 36308, 36309, 36310, 36311, 36312, 36313, 36314, 36315, - 36316, 36317, 36318, 36319, 36320, 36321, 36322, 36323, 36324, 36325, - 36326, 36327, 36328, 36329, 36330, 36331, 36332, 36333, 36334, 36335, - 36336, 36337, 36338, 36339, 36340, 36341, 36342, 36343, 36344, 36345, - 36346, 36347, 36348, 36349, 36350, 36351, 36352, 36353, 36354, 36355, - 36356, 36357, 36358, 36359, 36360, 36361, 36362, 36363, 36364, 36365, - 36366, 36367, 36368, 36369, 36370, 36371, 36372, 36373, 36374, 36375, - 36376, 36377, 36378, 36379, 36380, 36381, 36382, 36383, 36384, 36385, - 36386, 36387, 36388, 36389, 36390, 36391, 36392, 36393, 36394, 36395, - 36396, 36397, 36398, 36399, 36400, 36401, 36402, 36403, 36404, 36405, - 36406, 36407, 36408, 36409, 36410, 36411, 36412, 36413, 36414, 36415, - 36416, 36417, 36418, 36419, 36420, 36421, 36422, 36423, 36424, 36425, - 36426, 36427, 36428, 36429, 36430, 36431, 36432, 36433, 36434, 36435, - 36436, 36437, 36438, 36439, 36440, 36441, 36442, 36443, 36444, 36445, - 36446, 36447, 36448, 36449, 36450, 36451, 36452, 36453, 36454, 36455, - 36456, 36457, 36458, 36459, 36460, 36461, 36462, 36463, 36464, 36465, - 36466, 36467, 36468, 36469, 36470, 36471, 36472, 36473, 36474, 36475, - 36476, 36477, 36478, 36479, 36480, 36481, 36482, 36483, 36484, 36485, - 36486, 36487, 36488, 36489, 36490, 36491, 36492, 36493, 36494, 36495, - 36496, 36497, 36498, 36499, 36500, 36501, 36502, 36503, 36504, 36505, - 36506, 36507, 36508, 36509, 36510, 36511, 36512, 36513, 36514, 36515, - 36516, 36517, 36518, 36519, 36520, 36521, 36522, 36523, 36524, 36525, - 36526, 36527, 36528, 36529, 36530, 36531, 36532, 36533, 36534, 36535, - 36536, 36537, 36538, 36539, 36540, 36541, 36542, 36543, 36544, 36545, - 36546, 36547, 36548, 36549, 36550, 36551, 36552, 36553, 36554, 36555, - 36556, 36557, 36558, 36559, 36560, 36561, 36562, 36563, 36564, 36565, - 36566, 36567, 36568, 36569, 36570, 36571, 36572, 36573, 36574, 36575, - 36576, 36577, 36578, 36579, 36580, 36581, 36582, 36583, 36584, 36585, - 36586, 36587, 36588, 36589, 36590, 36591, 36592, 36593, 36594, 36595, - 36596, 36597, 36598, 36599, 36600, 36601, 36602, 36603, 36604, 36605, - 36606, 36607, 36608, 36609, 36610, 36611, 36612, 36613, 36614, 36615, - 36616, 36617, 36618, 36619, 36620, 36621, 36622, 36623, 36624, 36625, - 36626, 36627, 36628, 36629, 36630, 36631, 36632, 36633, 36634, 36635, - 36636, 36637, 36638, 36639, 36640, 36641, 36642, 36643, 21697, 21698, - 21699, 21700, 21701, 21702, 21703, 21704, 21705, 21706, 21707, 21708, - 21709, 21710, 21711, 21712, 21713, 21714, 21715, 21716, 21717, 21718, - 21719, 21720, 21721, 21722, 21723, 21724, 21725, 21726, 21727, 21728, - 21729, 21730, 21731, 21732, 21733, 21734, 21735, 21736, 21737, 21738, - 21739, 21740, 21741, 21742, 21743, 21744, 21745, 21746, 21747, 21748, - 21749, 21750, 21751, 21752, 21753, 21754, 21755, 21756, 21757, 21758, - 21759, 21760, 21761, 21762, 21763, 21764, 21765, 21766, 21767, 21768, - 21769, 21770, 21771, 21772, 21773, 21774, 21775, 21776, 21777, 21778, - 21779, 21780, 21781, 21782, 21783, 21784, 21785, 21786, 21787, 21788, - 21789, 21790, 21791, 21792, 21793, 21794, 21795, 21796, 21797, 21798, - 21799, 21800, 21801, 21802, 21803, 21804, 21805, 21806, 21807, 21808, - 21809, 21810, 21811, 21812, 21813, 21814, 21815, 21816, 21817, 21818, - 21819, 21820, 21821, 21822, 21823, 21824, 21825, 21826, 21827, 21828, - 21829, 21830, 21831, 21832, 21833, 21834, 21835, 21836, 21837, 21838, - 21839, 21840, 21841, 21842, 21843, 21844, 21845, 21846, 21847, 21848, - 21849, 21850, 21851, 21852, 21853, 21854, 21855, 21856, 21857, 21858, - 21859, 21860, 21861, 21862, 21863, 21864, 21865, 21866, 21867, 21868, - 21869, 21870, 21871, 21872, 21873, 21874, 21875, 21876, 21877, 21878, - 21879, 21880, 21881, 21882, 21883, 21884, 21885, 21886, 21887, 21888, - 21889, 21890, 21891, 21892, 21893, 21894, 21895, 21896, 21897, 21898, - 21899, 21900, 21901, 21902, 21903, 21904, 21905, 21906, 21907, 21908, - 21909, 21910, 21911, 21912, 21913, 21914, 21915, 21916, 21917, 21918, - 21919, 21920, 21921, 21922, 21923, 21924, 21925, 21926, 21927, 21928, - 21929, 21930, 21931, 21932, 21933, 21934, 21935, 21936, 21937, 21938, - 21939, 21940, 21941, 21942, 21943, 21944, 21945, 21946, 21947, 21948, - 21949, 21950, 21951, 21952, 21959, 21960, 21961, 21962, 21963, 21964, - 21965, 21966, 21967, 21968, 21969, 21970, 21971, 21972, 21973, 21974, - 21975, 21976, 21977, 21978, 21979, 21980, 21981, 21982, 21983, 21984, - 21985, 21986, 21987, 21988, 21989, 21990, 21991, 21992, 21993, 21994, - 21995, 21996, 21997, 21998, 21999, 22000, 22001, 22002, 22003, 22004, - 22005, 22006, 22007, 22008, 22009, 22010, 22011, 22012, 22013, 22014, - 22015, 22016, 22017, 22018, 22019, 22020, 22021, 22022, 22023, 22024, - 22025, 22026, 22027, 22028, 22029, 22030, 22031, 22032, 22033, 22034, - 22035, 22036, 22037, 22038, 22039, 22040, 22041, 22042, 22043, 22044, - 22045, 22046, 22047, 22048, 22049, 22050, 22051, 22052, 22053, 22054, - 22055, 22056, 22057, 22058, 22059, 22060, 22061, 22062, 22063, 22064, - 22065, 22066, 22067, 22068, 22069, 22070, 22071, 22072, 22073, 22074, - 22075, 22076, 22077, 22078, 22079, 22080, 22081, 22082, 22083, 22084, - 22085, 22086, 22087, 22088, 22089, 22090, 22091, 22092, 22093, 22094, - 22095, 22096, 22097, 22098, 22099, 22100, 22101, 22102, 22103, 22104, - 22105, 22106, 22107, 22108, 22109, 22110, 22111, 22112, 22113, 22114, - 22115, 22116, 22117, 22118, 22119, 22120, 22121, 22122, 22123, 22124, - 22125, 22126, 22127, 22128, 22129, 22130, 22131, 22132, 22133, 22134, - 22135, 22136, 22137, 22138, 22139, 22140, 22141, 22142, 22143, 22144, - 22145, 22146, 22147, 22148, 22149, 22150, 22151, 22152, 22153, 22154, - 22155, 22156, 22157, 22158, 22159, 22160, 22161, 22162, 22163, 22164, - 22165, 22166, 21953, 21954, 21955, 21956, 21957, 21958, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 22167, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 21336, 21337, 21338, 21339, 40951, 21340, 21341, 21329, 21330, 21331, - 21332, 21333, 40951, 21334, 21335, 40951, 21317, 20293, 19932, 19933, - 19934, 19931, 20213, 20214, 20215, 20216, 20205, 20206, 20207, 20208, - 20209, 20200, 20201, 20202, 20203, 20204, 20210, 20211, 20212, 19971, - 19975, 19976, 19977, 19978, 19979, 19980, 19981, 19982, 19972, 19973, - 19974, 19987, 19988, 19989, 19990, 19991, 19992, 19993, 19994, 20001, - 20002, 20003, 20004, 20005, 20006, 20007, 19995, 19996, 19997, 19998, - 19999, 20000, 19984, 19985, 19986, 19983, 20096, 20097, 20098, 20099, - 20100, 20101, 20102, 20103, 20112, 20113, 20114, 20115, 20116, 20117, - 20104, 20105, 20106, 20107, 20108, 20109, 20110, 20111, 20118, 20119, - 20120, 20121, 20122, 20123, 20124, 20125, 20126, 20127, 20128, 20129, - 20158, 20159, 20160, 20161, 20151, 20152, 20153, 20154, 20155, 20156, - 20157, 20147, 20148, 20149, 20150, 20146, 20130, 20131, 20132, 20133, - 20134, 20135, 20136, 20137, 20138, 20140, 20141, 20142, 20143, 20144, - 20145, 20139, 20050, 20051, 20052, 20053, 20054, 20055, 20056, 20057, - 20058, 20043, 20044, 20045, 20046, 20047, 20048, 20049, 20042, 20064, - 20065, 20066, 20036, 20037, 20038, 20039, 20040, 20041, 20035, 20059, - 20060, 20061, 20062, 20063, 19943, 19946, 19947, 19948, 19949, 19950, - 19951, 19952, 19953, 19944, 19945, 19964, 19965, 19966, 19967, 19968, - 19969, 19970, 19954, 19955, 19956, 19957, 19958, 19959, 19960, 19961, - 19962, 19963, 19935, 19936, 19937, 19938, 19939, 19940, 19941, 19942, - 20017, 20018, 20019, 20020, 20021, 20022, 20023, 20024, 20025, 20026, - 20027, 20028, 20029, 20030, 20031, 20032, 20033, 20034, 20009, 20010, - 20008, 20011, 20012, 20013, 20014, 20015, 20016, 20184, 20185, 20186, - 20187, 20188, 20183, 20195, 20196, 20197, 20198, 20189, 20190, 20191, - 20192, 20193, 20194, 20088, 20089, 20090, 20091, 20081, 20082, 20083, - 20084, 20085, 20086, 20087, 20075, 20076, 20077, 20078, 20079, 20080, - 20092, 20093, 20094, 20095, 20069, 20070, 20071, 20072, 20073, 20074, - 20162, 20163, 20164, 20165, 20166, 20167, 20168, 20169, 20170, 20171, - 20179, 20180, 20181, 20182, 20172, 20173, 20174, 20175, 20176, 20177, - 20178, 20067, 20068, 20292, 21315, 21314, 21316, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 20303, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 20296, 20295, 20297, 40951, 40951, 21364, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 21370, 21369, 21371, 21375, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 29455, 29456, 29457, 29458, 29459, 29460, - 29461, 29462, 29463, 29464, 29465, 29466, 29467, 29468, 29469, 29470, - 29471, 29472, 29473, 29474, 29475, 29476, 29477, 29478, 29479, 29480, - 29481, 29482, 29483, 29484, 29485, 29486, 29487, 29488, 29489, 29490, - 29491, 29492, 29493, 29494, 29495, 29496, 29497, 29498, 29499, 29500, - 29501, 29502, 29503, 29504, 29505, 29506, 29507, 29508, 29509, 29510, - 29511, 29512, 29513, 29514, 29515, 29516, 29517, 29518, 29519, 29520, - 29521, 29522, 29523, 29524, 29525, 29526, 29527, 29528, 29529, 29530, - 29531, 29532, 29533, 29534, 29535, 29536, 29537, 29538, 29539, 29540, - 29541, 29542, 29543, 29544, 29545, 29546, 29547, 29548, 29549, 29550, - 29551, 29552, 29553, 29554, 29555, 29556, 29557, 29558, 29559, 29560, - 29561, 29562, 29563, 29564, 29565, 29566, 29567, 29568, 29569, 29570, - 29571, 29572, 29573, 29574, 29575, 29576, 29577, 29578, 29579, 29580, - 29581, 29582, 29583, 29584, 29585, 29586, 29587, 29588, 29589, 29590, - 29591, 29592, 29593, 29594, 29595, 29596, 29597, 29598, 29599, 29600, - 29601, 29602, 29603, 29604, 29605, 29606, 29607, 29608, 29609, 29610, - 29611, 29612, 29613, 29614, 29615, 29616, 29617, 29618, 29619, 29620, - 29621, 29622, 29623, 29624, 29625, 29626, 29627, 29628, 29629, 29630, - 29631, 29632, 29633, 29634, 29635, 29636, 29637, 29638, 29639, 29640, - 29641, 29642, 29643, 29644, 29645, 29646, 29647, 29648, 29649, 29650, - 29651, 29652, 29653, 29654, 29655, 29656, 29657, 29658, 29659, 29660, - 29661, 29662, 29663, 29664, 29665, 29666, 29667, 29668, 29669, 29670, - 29671, 29672, 29673, 29674, 29675, 29676, 29677, 29678, 29679, 29680, - 29681, 29682, 29683, 29684, 29685, 29686, 29687, 29688, 29689, 29690, - 29691, 29692, 29693, 29694, 29695, 29696, 29697, 29698, 29699, 29700, - 29701, 29702, 29703, 29704, 29705, 29706, 29707, 29708, 29709, 29710, - 29711, 29712, 29713, 29714, 29715, 29716, 29717, 29718, 29719, 29720, - 29721, 29722, 29723, 29724, 29725, 29726, 29727, 29728, 29729, 29730, - 29731, 29732, 29733, 29734, 29735, 29736, 29737, 29738, 29739, 29740, - 29741, 29742, 29743, 29744, 29745, 29746, 29747, 29748, 29749, 29750, - 29751, 29752, 29753, 29754, 29755, 29756, 29757, 29758, 29759, 29760, - 29761, 29762, 29763, 29764, 29765, 29766, 29767, 29768, 29769, 29770, - 29771, 29772, 29773, 29774, 29775, 29776, 29777, 29778, 29779, 29780, - 29781, 29782, 29783, 29784, 29785, 29786, 29787, 29788, 29789, 29790, - 29791, 29792, 29793, 29794, 29795, 29796, 29797, 29798, 29799, 29800, - 29801, 29802, 29803, 29804, 29805, 29806, 29807, 29808, 29809, 29810, - 29811, 29812, 29813, 29814, 29815, 29816, 29817, 29818, 29819, 29820, - 29821, 29822, 29823, 29824, 29825, 29826, 29827, 29828, 29829, 29830, - 29831, 29832, 29833, 29834, 29835, 29836, 29837, 29838, 29839, 29840, - 29841, 29842, 29843, 29844, 29845, 29846, 29847, 29848, 29849, 29850, - 40951, 40951, 40951, 40951, 11516, 11514, 11463, 11496, 11423, 11436, - 11440, 11521, 11417, 11504, 11425, 11467, 11466, 11418, 11424, 11438, - 11470, 11499, 11492, 11419, 11439, 11493, 11517, 11443, 11471, 11444, - 11449, 11427, 11472, 11445, 11450, 11430, 11473, 11447, 11452, 11428, - 11429, 11481, 11482, 11448, 11453, 11434, 11485, 11446, 11451, 11431, - 11474, 11435, 11432, 11433, 11479, 11480, 11477, 11478, 11498, 11497, - 11506, 11512, 11509, 11484, 11483, 11437, 11426, 11475, 11476, 11415, - 11490, 11460, 11458, 11416, 11518, 11420, 11519, 11495, 11503, 11421, - 11487, 11468, 11486, 11441, 11520, 11500, 11422, 11515, 11501, 11442, - 11469, 11502, 11494, 11459, 11462, 11461, 11511, 11507, 11513, 11510, - 11508, 11457, 11456, 11455, 11454, 11465, 11464, 11488, 11491, 11489, - 11505, 40951, 40951, 40951, 40951, 40951, 11400, 11413, 11412, 11407, - 11414, 11394, 11387, 11386, 11383, 11385, 11388, 11389, 11384, 40951, - 40951, 40951, 11396, 11392, 11395, 11390, 11399, 11398, 11391, 11397, - 11393, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 11401, 11405, - 11408, 11403, 11411, 11410, 11404, 11409, 11406, 11402, 40951, 40951, - 11525, 11522, 11523, 11524, 32839, 32838, 32840, 32841, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 37793, 32041, 24171, 32038, 11290, 25171, 32009, 25229, 965, 20428, - 38848, 24129, 27577, 31994, 24164, 32031, 29926, 31588, 31779, 20424, - 38822, 25118, 25117, 25116, 25115, 25113, 25114, 4485, 4486, 4494, 4508, - 4378, 4377, 32579, 32587, 32580, 32591, 32584, 32588, 32581, 32593, - 32586, 32590, 32583, 32592, 32585, 32589, 32582, 37832, 37800, 37802, - 37887, 37849, 37845, 37881, 37851, 25255, 25202, 25222, 25257, 25205, - 25247, 25249, 25228, 34085, 34086, 30653, 10840, 10576, 10575, 34097, - 34098, 24177, 32018, 17483, 17484, 350, 349, 356, 355, 358, 357, 353, - 354, 351, 352, 24167, 37785, 32034, 11296, 37462, 37458, 37466, 4340, - 4338, 4344, 24160, 37780, 32028, 11292, 28300, 24170, 37788, 32037, - 11299, 16653, 16654, 3842, 3845, 3844, 3843, 3876, 24183, 37778, 32024, - 11302, 24184, 37779, 32025, 11303, 24173, 37782, 32040, 11294, 34330, - 34331, 34329, 34328, 34521, 34523, 34522, 34520, 38832, 20407, 39241, - 39242, 37705, 34162, 34161, 34160, 34115, 20802, 24043, 20803, 38837, - 20405, 24178, 32019, 24179, 32020, 17469, 24169, 37787, 32036, 11298, - 20427, 38847, 38825, 24172, 32039, 24166, 32033, 24168, 32035, 24077, - 31952, 37841, 37877, 37840, 37876, 25191, 25211, 25190, 25210, 25188, - 25208, 25189, 25209, 37844, 37880, 25201, 25221, 37842, 37878, 25200, - 25220, 37835, 37871, 25195, 25215, 37838, 37874, 25198, 25218, 37839, - 37875, 25199, 25219, 37834, 37870, 25194, 25214, 37836, 37872, 25196, - 25216, 37837, 37873, 25197, 25217, 37843, 37879, 25192, 25212, 30832, - 30833, 30834, 30835, 30836, 30837, 30838, 30839, 30840, 30841, 30842, - 30843, 30844, 30845, 30846, 30847, 30848, 30849, 30850, 30851, 30852, - 30853, 30854, 30855, 30856, 30857, 30868, 30870, 30867, 30866, 30863, - 30862, 30865, 30864, 30871, 30869, 40951, 40951, 40951, 40951, 40951, - 40951, 4137, 4081, 3952, 4167, 4038, 3979, 4138, 4019, 4082, 4128, 4054, - 4113, 3995, 4010, 4098, 3964, 4171, 4039, 4069, 3980, 4139, 4020, 4083, - 3953, 4134, 4062, 4121, 4003, 4160, 4016, 4106, 3972, 4047, 4075, 3988, - 4146, 4028, 4091, 3958, 4129, 4055, 4114, 3996, 4153, 4011, 4099, 3965, - 4172, 4040, 4070, 3981, 4140, 4021, 4084, 4066, 4125, 4007, 4164, 4035, - 4110, 3976, 4179, 4051, 4078, 3992, 4150, 4032, 4095, 3961, 4059, 4118, - 4000, 4157, 4103, 3969, 4176, 4044, 3985, 4143, 4025, 4088, 4135, 4063, - 4122, 4004, 4161, 4017, 4107, 3973, 4168, 4048, 4076, 3989, 4147, 4029, - 4092, 3959, 4130, 4056, 4115, 3997, 4154, 4012, 4100, 3966, 4173, 4041, - 4071, 3982, 4141, 4022, 4085, 3954, 4068, 4127, 4009, 4166, 4037, 4112, - 3978, 4181, 4053, 4080, 3994, 4152, 4034, 4097, 3963, 4133, 4061, 4120, - 4002, 4159, 4015, 4105, 3971, 4178, 4046, 4074, 3987, 4145, 4027, 4090, - 3957, 4065, 4124, 4006, 4163, 4109, 3975, 4170, 4050, 3991, 4149, 4031, - 4094, 4131, 4058, 4117, 3999, 4156, 4013, 4102, 3968, 4175, 4043, 4072, - 3984, 4142, 4024, 4087, 3955, 4067, 4126, 4008, 4165, 4036, 4111, 3977, - 4180, 4052, 4079, 3993, 4151, 4033, 4096, 3962, 4132, 4060, 4119, 4001, - 4158, 4014, 4104, 3970, 4177, 4045, 4073, 3986, 4144, 4026, 4089, 3956, - 4136, 4064, 4123, 4005, 4162, 4018, 4108, 3974, 4169, 4049, 4077, 3990, - 4148, 4030, 4093, 3960, 4057, 4116, 3998, 4155, 4101, 3967, 4174, 4042, - 3983, 4023, 4086, 37470, 4347, 37467, 4345, 37468, 4346, 37463, 4341, - 37464, 4342, 37457, 4334, 4335, 4336, 4337, 28184, 37459, 37460, 11293, - 24161, 33837, 37783, 11295, 17356, 17357, 17358, 31947, 25162, 17359, - 37807, 25164, 19784, 39303, 37481, 17638, 4379, 4376, 24081, 31956, - 24080, 31955, 20404, 24078, 31953, 20403, 25165, 37810, 38838, 4504, - 4502, 4503, 4501, 22718, 22717, 22722, 22716, 22694, 22673, 22674, 22714, - 22680, 22698, 22721, 22696, 22719, 22720, 22702, 22699, 22679, 22678, - 22681, 22697, 22682, 22670, 22691, 22715, 22671, 22672, 22669, 22695, - 22700, 22686, 22677, 22701, 22703, 22676, 22693, 22685, 22683, 22684, - 22675, 22723, 22692, 22687, 22690, 22688, 22689, 22711, 22709, 22710, - 22713, 22708, 22705, 22712, 22707, 22706, 22704, 32594, 32626, 32595, - 32642, 32611, 32627, 32596, 32650, 32619, 32635, 32604, 32643, 32612, - 32628, 32597, 32654, 32623, 32639, 32608, 32647, 32616, 32632, 32601, - 32651, 32620, 32636, 32605, 32644, 32613, 32629, 32598, 32656, 32625, - 32641, 32610, 32649, 32618, 32634, 32603, 32653, 32622, 32638, 32607, - 32646, 32615, 32631, 32600, 32655, 32624, 32640, 32609, 32648, 32617, - 32633, 32602, 32652, 32621, 32637, 32606, 32645, 32614, 32630, 32599, - 37827, 37799, 37801, 37866, 37848, 37846, 37847, 37850, 25254, 25252, - 25253, 25256, 25207, 25246, 25248, 25242, 31957, 31997, 24132, 24082, - 25167, 25262, 37890, 37812, 24083, 24133, 31998, 31958, 37813, 37891, - 25263, 25168, 20429, 21627, 30342, 3882, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40767, 40772, 40806, 40769, 40774, 40802, 40796, 40790, - 40813, 40792, 40786, 40810, 40768, 40773, 40807, 40770, 40775, 40803, - 40797, 40791, 40814, 40793, 40787, 40811, 40808, 40794, 40801, 40788, - 40789, 40812, 40795, 40771, 40817, 40782, 40799, 40809, 40820, 40819, - 40785, 40818, 40779, 40778, 40816, 40800, 40798, 40777, 40951, 40951, - 40822, 40823, 40824, 40815, 40765, 40780, 40783, 40784, 40762, 40763, - 40781, 40804, 40805, 40766, 40821, 40764, 40776, 40761, 40943, 40944, - 40941, 40942, 40945, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40841, 40842, 40858, 40830, 40839, 40938, 40893, 40894, - 40861, 40862, 40895, 40825, 40859, 40939, 40837, 40834, 40836, 40835, - 40833, 40935, 40934, 40937, 40936, 40931, 40930, 40933, 40932, 40831, - 40865, 40827, 40838, 40826, 40863, 40876, 40877, 40875, 40874, 40828, - 40872, 40873, 40871, 40869, 40870, 40868, 40867, 40878, 40880, 40881, - 40879, 40843, 40866, 40832, 40840, 40940, 40882, 40887, 40884, 40888, - 40885, 40890, 40891, 40886, 40883, 40889, 40864, 40892, 40925, 40922, - 40913, 40923, 40924, 40914, 40902, 40901, 40929, 40899, 40898, 40896, - 40900, 40897, 40916, 40921, 40915, 40919, 40920, 40917, 40918, 40845, - 40849, 40848, 40847, 40846, 40928, 40926, 40927, 40852, 40857, 40856, - 40855, 40854, 40853, 40903, 40912, 40905, 40910, 40904, 40908, 40909, - 40906, 40907, 40911, 40844, 40851, 40829, 40850, 40860, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 5194, 5066, 5187, - 5169, 5170, 5238, 5239, 5119, 5216, 5176, 5250, 5251, 5142, 5021, 5072, - 5219, 5120, 5024, 5025, 5214, 5226, 5165, 5102, 5193, 5045, 5242, 5114, - 5128, 5124, 5217, 5179, 5208, 5172, 5241, 5027, 5029, 5129, 5195, 5180, - 5237, 5019, 5197, 5211, 5213, 5167, 5221, 5149, 5067, 5229, 5220, 5139, - 5020, 5079, 5110, 5231, 5117, 5185, 5189, 5132, 5043, 5196, 5174, 5177, - 5116, 5254, 5184, 5133, 5232, 5209, 5108, 5115, 5166, 5171, 5183, 5135, - 5182, 5140, 5186, 5123, 5127, 5253, 5026, 5022, 5252, 5141, 5075, 5046, - 5164, 5240, 5181, 5190, 5173, 5018, 5150, 5178, 5175, 5071, 5143, 5017, - 5233, 5074, 5212, 5215, 5044, 5073, 5198, 5255, 5235, 5191, 5230, 5234, - 5188, 5236, 5192, 5105, 5031, 5070, 5168, 5223, 5224, 5222, 5225, 5118, - 5068, 5243, 5244, 5207, 5131, 5064, 5136, 5137, 5138, 5028, 5030, 5063, - 5228, 5218, 5134, 5144, 5148, 5147, 5146, 5145, 5104, 5101, 5100, 5058, - 5060, 5061, 5059, 5227, 5032, 5112, 5051, 5015, 5009, 5010, 5013, 5014, - 5012, 5011, 5016, 5157, 5152, 5153, 5151, 5162, 5161, 5160, 5159, 5163, - 5155, 5113, 5023, 5078, 5077, 5076, 5158, 5156, 5154, 5106, 5107, 5069, - 5111, 5109, 5080, 5087, 5083, 5091, 5086, 5095, 5085, 5084, 5081, 5082, - 5089, 5090, 5097, 5094, 5092, 5041, 5042, 5040, 5088, 5096, 5249, 5054, - 5052, 5055, 5057, 5056, 5053, 5245, 5247, 5246, 5248, 5098, 5099, 5048, - 5047, 5049, 5050, 5203, 5206, 5204, 5205, 5199, 5202, 5200, 5201, 5062, - 5065, 5210, 5039, 5033, 5038, 5036, 5035, 5034, 5037, 5121, 5125, 5122, - 5126, 5130, 5103, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 28489, 28366, 28382, 28474, 28361, 28486, 28424, 28475, - 28473, 28362, 28358, 28485, 28352, 28470, 28471, 28472, 28377, 28378, - 28315, 28316, 28311, 28310, 28441, 28512, 28509, 28384, 28383, 28490, - 28491, 28385, 28394, 28395, 28396, 28355, 28387, 28388, 28389, 28368, - 28369, 40951, 40951, 28432, 28365, 28367, 28393, 28392, 28437, 28436, - 28488, 28487, 28464, 28465, 28351, 28357, 28453, 28454, 28468, 28469, - 28433, 28535, 28407, 28467, 28376, 28493, 28511, 28495, 28440, 28533, - 28456, 28356, 28496, 28497, 28520, 28521, 28524, 28525, 28522, 28523, - 28516, 28517, 28518, 28519, 28430, 28431, 28526, 28527, 28455, 28531, - 28438, 28435, 28319, 28320, 28314, 28534, 28406, 28466, 28375, 28492, - 28510, 28494, 28439, 28340, 28341, 28344, 28345, 28346, 28379, 28380, - 28381, 28323, 28330, 28331, 28332, 28333, 28334, 28308, 28373, 28309, - 28374, 28307, 28371, 28306, 28370, 28321, 28339, 28347, 28338, 28324, - 28325, 28322, 28349, 28404, 28403, 28328, 28350, 28335, 28342, 28348, - 28327, 28343, 28476, 28499, 28538, 28463, 28426, 28386, 28354, 28363, - 28400, 28399, 28515, 28528, 28537, 28529, 28530, 28442, 28445, 28446, - 28447, 28448, 28449, 28450, 28451, 28452, 28443, 28444, 28408, 28434, - 28372, 28364, 28326, 28329, 28336, 28337, 28458, 28457, 28405, 28398, - 28397, 28536, 28359, 28360, 28425, 28421, 28312, 28479, 28481, 28427, - 28429, 28482, 28484, 28390, 28391, 28423, 28422, 28313, 28480, 28428, - 28483, 28507, 28506, 28508, 28505, 28501, 28502, 28503, 28504, 28353, - 28401, 28402, 28498, 28532, 28460, 28318, 28477, 28317, 28513, 28461, - 28462, 28478, 28514, 28459, 28409, 28412, 28416, 28419, 28418, 28417, - 28413, 28414, 28410, 28411, 28415, 28500, 28420, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 18636, 18624, - 18647, 18648, 18630, 18649, 18650, 18651, 18652, 18637, 18638, 18639, - 18640, 18641, 18642, 18643, 18644, 18645, 18646, 18625, 18626, 18627, - 18628, 18629, 18631, 18632, 18633, 18634, 18635, 18356, 18364, 18377, - 18385, 18391, 18392, 18357, 18358, 18359, 18360, 18361, 18362, 18363, - 18365, 18366, 18367, 18368, 18369, 18370, 18371, 18372, 18373, 18374, - 18375, 18376, 18378, 18379, 18380, 18381, 18382, 18383, 18384, 18386, - 18387, 18388, 18389, 18390, 8271, 8270, 8272, 18416, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 21607, - 21608, 21605, 21603, 21594, 21593, 21600, 21598, 21589, 21596, 21606, - 21591, 21604, 21602, 21595, 21592, 21601, 21599, 21590, 21597, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 26844, 26845, 26842, 26840, 26831, 26830, 26837, 26835, 26826, - 26833, 26843, 26828, 26841, 26839, 26832, 26829, 26838, 26836, 26827, - 26834, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 28177, 10885, 10886, 10880, 10879, 10878, 36785, - 36805, 36826, 36774, 36819, 36783, 36769, 36828, 36773, 36787, 36793, - 36816, 36817, 36831, 36837, 36784, 36815, 36845, 36803, 36770, 36833, - 36835, 36802, 36848, 36782, 36797, 36794, 36775, 36789, 36772, 36830, - 36823, 36777, 36820, 36809, 36843, 36832, 36806, 36834, 36822, 36836, - 36811, 36799, 36842, 36812, 36798, 36829, 36838, 36808, 36844, 36781, - 36825, 36801, 36847, 36791, 36776, 36813, 36810, 36824, 36768, 36796, - 36795, 36846, 36840, 36818, 36788, 36786, 36792, 36800, 36839, 36841, - 36814, 36780, 36778, 36807, 36771, 36779, 36827, 36790, 36821, 36804, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 8669, - 8667, 8666, 8663, 8662, 8665, 8664, 8670, 8668, 8660, 8658, 8657, 8654, - 8653, 8656, 8655, 8661, 8659, 20515, 20514, 20513, 20512, 20511, 35370, - 35369, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 25824, 25826, 25854, 25817, - 25830, 25862, 25833, 25863, 25835, 25864, 25837, 25839, 25856, 25858, - 25841, 25844, 25865, 25848, 25850, 25820, 25852, 25866, 25867, 25860, - 25868, 25828, 25872, 25874, 25907, 25869, 25878, 25881, 25883, 25915, - 25885, 25916, 25887, 25889, 25909, 25911, 25891, 25894, 25917, 25898, - 25900, 25902, 25905, 25918, 25919, 25913, 25920, 25876, 26312, 26314, - 26344, 26318, 26320, 26352, 26323, 26353, 26325, 26354, 26327, 26329, - 26346, 26348, 26331, 26334, 26355, 26338, 26340, 26308, 26342, 26356, - 26357, 26350, 26358, 26316, 26260, 26262, 26295, 26256, 26266, 26269, - 26271, 40951, 26273, 26303, 26275, 26277, 26297, 26299, 26279, 26282, - 26304, 26286, 26288, 26290, 26293, 26305, 26306, 26301, 26307, 26264, - 25977, 25979, 26009, 25983, 25985, 26017, 25988, 26018, 25990, 26019, - 25992, 25994, 26011, 26013, 25996, 25999, 26020, 26003, 26005, 25973, - 26007, 26021, 26022, 26015, 26023, 25981, 26031, 26033, 26068, 26037, - 26039, 26042, 26044, 26076, 26046, 26077, 26048, 26050, 26070, 26072, - 26052, 26055, 26078, 26059, 26061, 26063, 26066, 26079, 26080, 26074, - 26081, 26035, 26784, 40951, 26785, 26786, 40951, 40951, 26787, 40951, - 40951, 26788, 26789, 40951, 40951, 26790, 26791, 26792, 26793, 40951, - 26794, 26795, 26796, 26797, 26798, 26799, 26800, 26801, 26802, 26803, - 26804, 26805, 40951, 26806, 40951, 26807, 26808, 26809, 26810, 26811, - 26812, 26813, 40951, 26814, 26815, 26816, 26817, 26818, 26819, 26820, - 26821, 26822, 26823, 26824, 25921, 25922, 25923, 25924, 25925, 25926, - 25927, 25928, 25929, 25930, 25931, 25932, 25933, 25934, 25935, 25936, - 25937, 25938, 25939, 25940, 25941, 25942, 25943, 25944, 25945, 25946, - 25947, 25948, 25949, 25950, 25951, 25952, 25953, 25954, 25955, 25956, - 25957, 25958, 25959, 25960, 25961, 25962, 25963, 25964, 25965, 25966, - 25967, 25968, 25969, 25970, 25971, 25972, 26208, 26209, 40951, 26210, - 26211, 26212, 26213, 40951, 40951, 26214, 26215, 26216, 26217, 26218, - 26219, 26220, 26221, 40951, 26222, 26223, 26224, 26225, 26226, 26227, - 26228, 40951, 26229, 26230, 26231, 26232, 26233, 26234, 26235, 26236, - 26237, 26238, 26239, 26240, 26241, 26242, 26243, 26244, 26245, 26246, - 26247, 26248, 26249, 26250, 26251, 26252, 26253, 26254, 26153, 26154, - 40951, 26155, 26156, 26157, 26158, 40951, 26159, 26160, 26161, 26162, - 26163, 40951, 26164, 40951, 40951, 40951, 26165, 26166, 26167, 26168, - 26169, 26170, 26171, 40951, 26172, 26173, 26174, 26175, 26176, 26177, - 26178, 26179, 26180, 26181, 26182, 26183, 26184, 26185, 26186, 26187, - 26188, 26189, 26190, 26191, 26192, 26193, 26194, 26195, 26196, 26197, - 26091, 26092, 26093, 26094, 26095, 26096, 26097, 26098, 26099, 26100, - 26101, 26102, 26103, 26104, 26105, 26106, 26107, 26108, 26109, 26110, - 26111, 26112, 26113, 26114, 26115, 26116, 26117, 26118, 26119, 26120, - 26121, 26122, 26123, 26124, 26125, 26126, 26127, 26128, 26129, 26130, - 26131, 26132, 26133, 26134, 26135, 26136, 26137, 26138, 26139, 26140, - 26141, 26142, 26722, 26723, 26724, 26725, 26726, 26727, 26728, 26729, - 26730, 26731, 26732, 26733, 26734, 26735, 26736, 26737, 26738, 26739, - 26740, 26741, 26742, 26743, 26744, 26745, 26746, 26747, 26748, 26749, - 26750, 26751, 26752, 26753, 26754, 26755, 26756, 26757, 26758, 26759, - 26760, 26761, 26762, 26763, 26764, 26765, 26766, 26767, 26768, 26769, - 26770, 26771, 26772, 26773, 26554, 26556, 26586, 26560, 26562, 26594, - 26565, 26595, 26567, 26596, 26569, 26571, 26588, 26590, 26573, 26576, - 26597, 26580, 26582, 26550, 26584, 26598, 26599, 26592, 26600, 26558, - 26608, 26610, 26645, 26614, 26616, 26619, 26621, 26653, 26623, 26654, - 26625, 26627, 26647, 26649, 26629, 26632, 26655, 26636, 26638, 26640, - 26643, 26656, 26657, 26651, 26658, 26612, 26670, 26671, 26672, 26673, - 26674, 26675, 26676, 26677, 26678, 26679, 26680, 26681, 26682, 26683, - 26684, 26685, 26686, 26687, 26688, 26689, 26690, 26691, 26692, 26693, - 26694, 26695, 26696, 26697, 26698, 26699, 26700, 26701, 26702, 26703, - 26704, 26705, 26706, 26707, 26708, 26709, 26710, 26711, 26712, 26713, - 26714, 26715, 26716, 26717, 26718, 26719, 26720, 26721, 26444, 26446, - 26476, 26450, 26452, 26484, 26455, 26485, 26457, 26486, 26459, 26461, - 26478, 26480, 26463, 26466, 26487, 26470, 26472, 26440, 26474, 26488, - 26489, 26482, 26490, 26448, 26498, 26500, 26535, 26504, 26506, 26509, - 26511, 26543, 26513, 26544, 26515, 26517, 26537, 26539, 26519, 26522, - 26545, 26526, 26528, 26530, 26533, 26546, 26547, 26541, 26548, 26502, - 26367, 26368, 26369, 26370, 26371, 26372, 26373, 26374, 26375, 26376, - 26377, 26378, 26379, 26380, 26381, 26382, 26383, 26384, 26385, 26386, - 26387, 26388, 26389, 26390, 26391, 26392, 26393, 26394, 26395, 26396, - 26397, 26398, 26399, 26400, 26401, 26402, 26403, 26404, 26405, 26406, - 26407, 26408, 26409, 26410, 26411, 26412, 26413, 26414, 26415, 26416, - 26417, 26418, 26257, 26258, 40951, 40951, 25825, 25827, 25834, 25819, - 25831, 25829, 25832, 25821, 25836, 25838, 25840, 25857, 25859, 25861, - 25842, 25847, 25849, 25822, 25851, 25823, 25853, 25845, 25855, 25846, - 25843, 26085, 25873, 25875, 25884, 25871, 25879, 25877, 25880, 25903, - 25886, 25888, 25890, 25910, 25912, 25914, 25892, 25897, 25899, 25882, - 25901, 25904, 25906, 25895, 25908, 25896, 25893, 26087, 26083, 26090, - 26084, 26086, 26089, 26088, 26313, 26315, 26324, 26319, 26321, 26317, - 26322, 26309, 26326, 26328, 26330, 26347, 26349, 26351, 26332, 26337, - 26339, 26310, 26341, 26311, 26343, 26335, 26345, 26336, 26333, 26361, - 26261, 26263, 26272, 26259, 26267, 26265, 26268, 26291, 26274, 26276, - 26278, 26298, 26300, 26302, 26280, 26285, 26287, 26270, 26289, 26292, - 26294, 26283, 26296, 26284, 26281, 26363, 26359, 26366, 26360, 26362, - 26365, 26364, 25978, 25980, 25989, 25984, 25986, 25982, 25987, 25974, - 25991, 25993, 25995, 26012, 26014, 26016, 25997, 26002, 26004, 25975, - 26006, 25976, 26008, 26000, 26010, 26001, 25998, 26026, 26032, 26034, - 26045, 26038, 26040, 26036, 26041, 26064, 26047, 26049, 26051, 26071, - 26073, 26075, 26053, 26058, 26060, 26043, 26062, 26065, 26067, 26056, - 26069, 26057, 26054, 26028, 26024, 26082, 26025, 26027, 26030, 26029, - 26555, 26557, 26566, 26561, 26563, 26559, 26564, 26551, 26568, 26570, - 26572, 26589, 26591, 26593, 26574, 26579, 26581, 26552, 26583, 26553, - 26585, 26577, 26587, 26578, 26575, 26603, 26609, 26611, 26622, 26615, - 26617, 26613, 26618, 26641, 26624, 26626, 26628, 26648, 26650, 26652, - 26630, 26635, 26637, 26620, 26639, 26642, 26644, 26633, 26646, 26634, - 26631, 26605, 26601, 26659, 26602, 26604, 26607, 26606, 26445, 26447, - 26456, 26451, 26453, 26449, 26454, 26441, 26458, 26460, 26462, 26479, - 26481, 26483, 26464, 26469, 26471, 26442, 26473, 26443, 26475, 26467, - 26477, 26468, 26465, 26493, 26499, 26501, 26512, 26505, 26507, 26503, - 26508, 26531, 26514, 26516, 26518, 26538, 26540, 26542, 26520, 26525, - 26527, 26510, 26529, 26532, 26534, 26523, 26536, 26524, 26521, 26495, - 26491, 26549, 26492, 26494, 26497, 26496, 25818, 25870, 40951, 40951, - 26149, 26151, 26148, 26147, 26144, 26143, 26146, 26145, 26152, 26150, - 26204, 26206, 26203, 26202, 26199, 26198, 26201, 26200, 26207, 26205, - 26780, 26782, 26779, 26778, 26775, 26774, 26777, 26776, 26783, 26781, - 26666, 26668, 26665, 26664, 26661, 26660, 26663, 26662, 26669, 26667, - 26425, 26427, 26424, 26423, 26420, 26419, 26422, 26421, 26428, 26426, - 33092, 33048, 33073, 33289, 33244, 33030, 33093, 33057, 33206, 33158, - 33134, 33095, 33098, 33055, 33099, 33049, 33100, 33122, 33117, 33155, - 33096, 33102, 33111, 33112, 33103, 33109, 33113, 33051, 33185, 33094, - 33123, 33052, 33132, 33101, 33131, 33118, 33157, 33156, 33097, 33116, - 33126, 33127, 33130, 33129, 33197, 33105, 33106, 33107, 33179, 33147, - 33110, 33114, 33108, 33104, 33178, 33139, 33177, 33176, 33136, 33135, - 33138, 33128, 33125, 33124, 33174, 33173, 33175, 33146, 33222, 33226, - 33225, 33223, 33224, 33067, 33213, 33243, 33215, 33228, 33220, 33229, - 33221, 33230, 33219, 33077, 33078, 33242, 33283, 33216, 33217, 33218, - 33214, 33240, 33227, 33238, 33231, 33239, 33237, 33235, 33232, 33233, - 33234, 33236, 33064, 33070, 33068, 33069, 33273, 33272, 33080, 33072, - 33083, 33086, 33081, 33084, 33082, 33085, 33090, 33087, 33047, 33282, - 33288, 33285, 33287, 33261, 33263, 33241, 33271, 33264, 33268, 33262, - 33270, 33269, 33267, 33029, 33119, 33054, 33246, 33032, 33255, 33121, - 33120, 33247, 33160, 33163, 33162, 33161, 33170, 33210, 33059, 33284, - 33041, 33166, 33169, 33164, 33165, 33258, 33168, 33257, 33040, 33039, - 33167, 33058, 33256, 33038, 33133, 33053, 33248, 33265, 33031, 33115, - 33050, 33186, 33266, 33042, 33194, 33190, 33192, 33063, 33286, 33043, - 33187, 33189, 33188, 33193, 33191, 33281, 33159, 33056, 33088, 33275, - 33276, 33274, 33076, 33254, 33034, 33033, 33183, 33259, 33181, 33062, - 33171, 33182, 33280, 33180, 33184, 33172, 33060, 33089, 33079, 33260, - 33045, 33046, 33044, 33061, 33065, 33066, 33278, 33279, 33277, 33245, - 33148, 33250, 33151, 33152, 33153, 33150, 33154, 33149, 33143, 33144, - 33145, 33142, 33140, 33071, 33141, 33137, 33074, 33075, 33252, 33253, - 33249, 33251, 33036, 33037, 33035, 33195, 33200, 33203, 33204, 33205, - 33211, 33196, 33198, 33199, 33207, 33202, 33208, 33209, 33201, 33091, - 33212, 33603, 33602, 33601, 33623, 33622, 33621, 33578, 33577, 33576, - 32962, 32961, 32960, 33564, 33563, 33562, 33574, 33575, 33570, 33573, - 33569, 33572, 33571, 33019, 33022, 33018, 33021, 33020, 33568, 33438, - 33439, 33440, 33441, 33436, 33437, 33442, 33482, 33387, 33500, 33499, - 33497, 33498, 33501, 33476, 33479, 33477, 33478, 33475, 33506, 33505, - 33503, 33504, 33452, 33451, 33450, 33456, 33455, 33454, 33453, 33474, - 33473, 33472, 33449, 33448, 33447, 33522, 33521, 33520, 33496, 33495, - 33494, 33619, 33618, 33617, 33616, 33615, 33614, 33620, 33613, 33612, - 33611, 33356, 33355, 33353, 33354, 33360, 33359, 33357, 33358, 33348, - 33347, 33345, 33346, 33352, 33351, 33349, 33350, 33410, 33409, 33407, - 33408, 33411, 33425, 33428, 33426, 33427, 33384, 33415, 33414, 33413, - 33412, 33370, 33383, 33382, 33381, 33371, 33369, 33368, 33367, 33434, - 33433, 33432, 33431, 33430, 33429, 33606, 33605, 33604, 33609, 33608, - 33607, 33610, 33469, 33468, 33466, 33467, 33460, 33459, 33457, 33458, - 33465, 33464, 33487, 33486, 33483, 33488, 33493, 33490, 33489, 33509, - 33508, 33507, 33512, 33511, 33510, 33463, 33471, 33470, 33559, 33556, - 33555, 33502, 33462, 33485, 33492, 33517, 33561, 33558, 33554, 33461, - 33484, 33491, 33516, 33560, 33557, 33553, 33515, 33514, 33513, 33374, - 33373, 33391, 33389, 33390, 33388, 33400, 33398, 33399, 33397, 33417, - 33416, 33548, 33545, 33551, 33376, 33375, 33392, 33393, 33395, 33394, - 33404, 33402, 33403, 33401, 33419, 33418, 33549, 33546, 33552, 33380, - 33379, 33377, 33378, 33372, 33396, 33405, 33420, 33421, 33422, 33547, - 33544, 33550, 33406, 33446, 33444, 33445, 33443, 33366, 33364, 33362, - 33365, 33363, 33361, 33519, 33518, 33424, 33423, 33481, 33480, 33386, - 33385, 32978, 32977, 32973, 32976, 32980, 32979, 32974, 32975, 32972, - 32981, 33291, 33298, 33296, 33295, 33293, 33294, 33292, 33297, 33011, - 33009, 33010, 32987, 32986, 32985, 32970, 32968, 32969, 32971, 33026, - 33025, 33024, 33005, 33006, 33002, 32983, 32982, 33001, 33003, 33000, - 33004, 32984, 32999, 32997, 32998, 32994, 32996, 32995, 32988, 32990, - 32989, 32992, 32991, 32993, 32965, 32964, 32963, 33588, 33587, 33589, - 33008, 33525, 33524, 33526, 33527, 32955, 32957, 32954, 32956, 32959, - 32958, 33320, 33318, 33319, 33325, 33327, 33326, 33322, 33324, 33323, - 33339, 33338, 33337, 33331, 33332, 33333, 33334, 33335, 33336, 33328, - 33330, 33329, 33340, 33341, 33342, 33309, 33307, 33308, 33321, 33344, - 33343, 33596, 33595, 33593, 33594, 33592, 33597, 33591, 33590, 33580, - 33585, 33583, 33584, 33581, 33582, 33586, 33523, 33435, 33528, 33290, - 33007, 33566, 33565, 33028, 33023, 33567, 33599, 33598, 33600, 33624, - 33299, 33300, 33301, 33302, 33303, 33304, 33305, 33306, 33017, 33317, - 33316, 33311, 33314, 33313, 33310, 33312, 33315, 32967, 33027, 33579, - 32966, 33625, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 33012, 33013, 33014, - 33015, 33016, 40951, 33536, 33537, 33538, 33539, 33540, 33541, 33542, - 33543, 33529, 33530, 33531, 33532, 33533, 33534, 33535, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 23497, 23771, - 23262, 23776, 23249, 23621, 23882, 23773, 23868, 23837, 23242, 23476, - 23477, 23872, 23237, 23289, 23263, 23614, 23413, 23594, 23474, 23866, - 23752, 23841, 23485, 23414, 23558, 23711, 23842, 23380, 23788, 40951, - 40951, 40951, 40951, 40951, 40951, 23404, 23607, 23653, 23757, 23796, - 23832, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 8227, 8229, 8239, 8233, 8215, 8240, 8247, - 40951, 8246, 8220, 8217, 8216, 8214, 8251, 8235, 8234, 8236, 8250, 8237, - 8238, 8222, 8225, 8249, 8231, 8248, 40951, 40951, 8223, 8226, 8230, 8224, - 8242, 8241, 8243, 40951, 8245, 8221, 40951, 8244, 8219, 8228, 8218, 8232, - 40951, 40951, 40951, 40951, 40951, 27764, 27733, 27761, 27759, 27735, - 27757, 27754, 27755, 27756, 27763, 27740, 27741, 27765, 27744, 27742, - 27737, 27750, 27766, 27739, 27762, 27749, 27758, 27748, 27751, 27736, - 27753, 27734, 27747, 27731, 27760, 27732, 27745, 27743, 10520, 10498, - 10518, 10505, 10501, 10515, 10512, 10513, 10514, 10519, 10503, 10521, - 10517, 10504, 10522, 10502, 10507, 10511, 10516, 10510, 10508, 10509, - 10506, 10497, 10500, 10499, 27738, 27752, 27746, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 8144, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 29882, 29866, 29857, 29868, 29873, 29865, 29870, - 29861, 29887, 29891, 29893, 29896, 29860, 29855, 29890, 29880, 29864, - 29863, 29894, 29856, 29867, 29888, 29876, 29892, 29895, 29862, 29884, - 29869, 29859, 29879, 29858, 29874, 29881, 29883, 29889, 29875, 29871, - 29872, 29897, 29898, 29877, 29878, 29885, 29886, 29899, 40951, 40951, - 40951, 29908, 29912, 29911, 29914, 29913, 29910, 29909, 29905, 29902, - 29901, 29904, 29903, 29906, 29907, 40951, 40951, 29922, 29924, 29921, - 29920, 29917, 29916, 29919, 29918, 29925, 29923, 40951, 40951, 40951, - 40951, 29900, 29915, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 37522, 37505, 37525, 37515, 37519, 37516, 37521, - 37507, 37506, 37524, 37512, 37527, 37526, 37518, 37517, 37523, 37520, - 37508, 37500, 37509, 37501, 37529, 37510, 37502, 37511, 37503, 37528, - 37514, 37504, 37513, 37530, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 38979, 38978, 39010, 39011, 39012, 39014, 39003, 39006, 38992, - 38995, 39007, 38999, 38996, 39013, 39009, 39008, 39016, 39021, 39020, - 39019, 39005, 38984, 38983, 39018, 39017, 39004, 39015, 38987, 38989, - 38993, 39000, 38991, 38998, 38997, 38986, 38981, 38982, 38990, 38985, - 38988, 38980, 38994, 39001, 39002, 39025, 39026, 39023, 39024, 39033, - 39035, 39032, 39031, 39028, 39027, 39030, 39029, 39036, 39034, 40951, - 40951, 40951, 40951, 40951, 39022, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 28864, 28867, 28866, 28868, 28865, 28847, 28851, - 28849, 28848, 28850, 28859, 28862, 28860, 28863, 28861, 28869, 28870, - 28871, 28872, 28873, 28852, 28854, 28857, 28858, 28853, 28856, 28855, - 28877, 28874, 28878, 28876, 28875, 28885, 28887, 28884, 28883, 28880, - 28879, 28882, 28881, 28888, 28886, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 30046, - 30049, 30048, 30047, 30050, 30051, 30028, 30030, 30029, 30031, 30032, - 30033, 30040, 30041, 30045, 30042, 30043, 30044, 30052, 30056, 30053, - 30055, 30054, 30057, 30034, 30039, 30038, 30036, 30035, 30037, 30060, - 30058, 30059, 30068, 30070, 30067, 30066, 30063, 30062, 30065, 30064, - 30071, 30069, 40951, 40951, 40951, 40951, 30061, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 17006, 17012, 17010, 17007, 17009, 17008, 17011, 40951, 16961, - 17005, 17003, 17002, 40951, 16949, 16948, 40951, 16960, 16959, 16958, - 16945, 16944, 16957, 16956, 16955, 16954, 16953, 16952, 16947, 16946, - 16951, 16950, 40951, 27076, 27077, 27078, 27144, 27165, 27153, 27118, - 27253, 27079, 27080, 27084, 27203, 27188, 27193, 27117, 27271, 27220, - 27142, 27123, 27214, 27083, 27081, 27082, 27129, 27173, 27226, 27266, - 27089, 27085, 27093, 27230, 27168, 27178, 27205, 27094, 27091, 27090, - 27243, 27181, 27241, 27219, 27210, 27208, 27209, 27272, 27251, 27088, - 27103, 27095, 27242, 27187, 27212, 27147, 27273, 27099, 27100, 27101, - 27157, 27149, 27133, 27234, 27185, 27086, 27092, 27087, 27160, 27257, - 27258, 27096, 27097, 27098, 27171, 27126, 27176, 27143, 27104, 27102, - 27105, 27229, 27186, 27235, 27137, 27249, 27106, 27110, 27114, 27183, - 27155, 27223, 27204, 27107, 27111, 27112, 27154, 27146, 27211, 27164, - 27274, 27175, 27113, 27108, 27109, 27191, 27244, 27252, 27122, 27264, - 27115, 27174, 27124, 27221, 27161, 27199, 27131, 27213, 27163, 27130, - 27270, 27120, 27166, 27116, 27162, 27190, 27217, 27228, 27198, 27233, - 27200, 27159, 27179, 27260, 27227, 27194, 27237, 27267, 27240, 27238, - 27263, 27134, 27250, 27140, 27169, 27125, 27156, 27132, 27180, 27136, - 27216, 27135, 27195, 27121, 27261, 27151, 27246, 27247, 27265, 27236, - 27177, 27215, 27207, 27170, 27145, 27119, 27184, 27192, 27231, 27197, - 27127, 27222, 27167, 27182, 27148, 27150, 27256, 27196, 27202, 27201, - 27268, 27189, 27138, 27139, 27225, 27269, 27218, 27206, 27259, 27262, - 27232, 27254, 27158, 27224, 27152, 27239, 27128, 27255, 27172, 27141, - 40951, 40951, 27282, 27280, 27279, 27276, 27275, 27278, 27277, 27283, - 27281, 27071, 27070, 27074, 27072, 27069, 27073, 27075, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 27, 9, 24, + 12673, 12737, 12736, 12665, 12727, 12758, 12701, 12702, 12703, 12704, + 12705, 12706, 12707, 12708, 12709, 12710, 12711, 12712, 12713, 12714, + 12715, 12716, 12717, 12718, 12719, 12720, 12721, 12722, 12723, 12724, + 12725, 12726, 12671, 12767, 12734, 12764, 12670, 12733, 14247, 14240, + 14242, 14246, 14238, 14230, 14185, 14187, 14189, 14186, 14188, 14181, + 14183, 14182, 14184, 14239, 14231, 14233, 14235, 14232, 14234, 14206, + 14208, 14210, 14207, 14209, 14190, 14192, 14194, 14191, 14193, 14221, + 14223, 14225, 14222, 14224, 14196, 14198, 14200, 14197, 14199, 14201, + 14203, 14205, 14202, 14204, 14211, 14213, 14215, 14212, 14214, 14226, + 14228, 14227, 14216, 14218, 14220, 14217, 14219, 14229, 14195, 14237, + 14236, 14180, 14130, 14147, 14131, 14132, 14133, 14134, 14168, 14149, + 14138, 14143, 14142, 14141, 14145, 14139, 14140, 14144, 14166, 14136, + 14148, 14137, 14151, 14150, 14165, 14160, 14146, 14159, 14129, 14167, + 14135, 14174, 35536, 35536, 35536, 14175, 14176, 14154, 14155, 14162, + 14161, 35536, 35536, 14153, 14152, 14177, 14171, 14172, 14178, 35536, + 35536, 14157, 14179, 14170, 14169, 14173, 14158, 35536, 35536, 14163, + 14156, 14164, 35536, 35536, 35536, 12669, 12730, 12728, 12741, 12766, + 12740, 12763, 35536, 14244, 14241, 14245, 14243, 14250, 14248, 14249, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 15939, 15941, 15940, 24238, 26169, 35536, 35536, 19657, 19683, 19676, + 19709, 19667, 19658, 19687, 19653, 19665, 19695, 19698, 19692, 35536, + 19681, 19708, 19712, 19691, 19706, 19713, 19720, 19723, 19668, 19719, + 19666, 19669, 19652, 19675, 19678, 19701, 19702, 19660, 19717, 19682, + 19663, 19699, 19661, 19718, 19677, 19685, 35536, 19710, 19674, 19694, + 19659, 19670, 19684, 19655, 19689, 19664, 19696, 19697, 19654, 19680, + 19656, 19707, 19700, 19714, 19688, 19690, 35536, 19662, 19716, 35536, + 19673, 19672, 19686, 19722, 19715, 19725, 19693, 19671, 19703, 19711, + 19679, 19704, 19705, 19721, 19724, 35536, 35536, 19735, 19736, 19738, + 19737, 19726, 19727, 19734, 19728, 19729, 19739, 19730, 19731, 19732, + 19733, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 19541, 19540, 19542, 19529, 19530, + 19531, 19532, 19533, 19534, 19535, 19537, 19536, 19539, 19538, 19547, + 19544, 19545, 19543, 19546, 19646, 19647, 19549, 19548, 19550, 19649, + 19648, 19552, 19553, 19554, 19551, 19555, 19558, 19557, 19559, 19560, + 19561, 19650, 19562, 19563, 19556, 19566, 19567, 19565, 19564, 19568, + 19569, 19570, 19571, 19572, 19573, 19576, 19577, 19578, 19575, 19579, + 19574, 19580, 19581, 19582, 19583, 19584, 19585, 19586, 19587, 19588, + 19589, 19591, 19590, 19592, 19593, 19595, 19596, 19597, 19594, 19598, + 19599, 19600, 19601, 19603, 19602, 19604, 19605, 19651, 19606, 19607, + 19609, 19610, 19611, 19608, 19612, 19613, 19614, 19615, 19616, 19644, + 19624, 19625, 19626, 19627, 19628, 19629, 19630, 19631, 19632, 19633, + 19634, 19635, 19636, 19637, 19638, 19639, 19640, 19641, 19642, 19643, + 19617, 19618, 19619, 19620, 19621, 19622, 19623, 19645, 35536, 35536, + 35536, 35536, 35536, 112, 113, 159, 35536, 35536, 35536, 35536, 156, 151, + 146, 126, 121, 139, 134, 114, 129, 154, 149, 144, 124, 119, 140, 135, + 115, 130, 157, 152, 147, 127, 122, 142, 137, 117, 132, 158, 153, 148, + 128, 123, 143, 138, 118, 133, 155, 150, 145, 125, 120, 141, 136, 116, + 131, 35536, 35536, 35536, 111, 107, 109, 110, 108, 103, 104, 105, 106, + 13314, 13311, 13315, 13301, 13296, 13302, 13305, 13297, 13307, 13316, + 13299, 13309, 13303, 13312, 13306, 13308, 13318, 13300, 13310, 13304, + 13313, 13317, 13298, 13319, 13331, 13340, 13330, 13326, 13339, 13321, + 13327, 13343, 13347, 13348, 13329, 13332, 13338, 13337, 13345, 13346, + 13328, 13335, 13341, 13336, 13325, 13344, 13333, 13320, 13322, 13342, + 13334, 13323, 13324, 13566, 13567, 13765, 13761, 13802, 13767, 13497, + 13571, 13764, 13760, 13503, 13502, 13563, 13545, 13561, 13570, 13565, + 13351, 13350, 13804, 13763, 13805, 13568, 13759, 13541, 24060, 35536, + 26521, 26524, 26519, 26522, 26493, 26523, 26491, 26494, 26520, 26492, + 26525, 26490, 2569, 35536, 35536, 35536, 13758, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 25714, 25715, 25726, 25695, 25698, 25729, + 25707, 25706, 25727, 25734, 25693, 25720, 25699, 25712, 25713, 25722, + 25711, 25692, 25696, 25703, 25701, 25723, 25700, 25691, 25721, 25708, + 25709, 25694, 25697, 25719, 25733, 25704, 25728, 25690, 25716, 25735, + 25717, 25718, 25710, 25732, 25731, 25705, 25724, 25725, 25730, 25702, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 19979, 19981, 19977, 19978, 19986, 19985, 19988, 19996, 19998, 19975, + 19989, 19972, 19992, 19990, 19970, 19983, 19971, 19984, 19995, 19991, + 19973, 19993, 19994, 19974, 19976, 19980, 19982, 19987, 19997, 35536, + 35536, 35536, 6141, 6152, 6143, 6114, 6137, 6153, 6115, 6142, 6159, 6157, + 6117, 6158, 6144, 6132, 6127, 6128, 6126, 6112, 6135, 6125, 6160, 6122, + 6134, 6151, 6131, 6155, 6145, 6140, 6149, 6150, 6123, 6136, 6147, 6148, + 6129, 6130, 6124, 6156, 6113, 6133, 6138, 6154, 6118, 6119, 6120, 6121, + 6116, 6146, 6139, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 7689, 7687, 7685, 7684, + 7681, 7680, 7683, 7682, 7688, 7686, 7679, 7678, 7676, 7667, 7665, 7674, + 7672, 7663, 7669, 7670, 7677, 7675, 7666, 7664, 7673, 7671, 7662, 7668, + 35536, 35536, 35536, 35536, 24519, 24513, 24499, 24514, 24486, 24516, + 24518, 24515, 24510, 24506, 24498, 24494, 24495, 24496, 24488, 24520, + 24509, 24502, 24500, 24490, 24487, 24511, 24504, 24492, 24508, 24497, + 24493, 24491, 24512, 24507, 24505, 24489, 24524, 24522, 24523, 24521, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 24517, + 24503, 24501, 13161, 13177, 13185, 13179, 13160, 13170, 13164, 13163, + 13172, 13181, 13186, 13182, 13180, 13168, 13184, 13175, 13169, 13167, + 13171, 13183, 13173, 13174, 13176, 13165, 13162, 13178, 13166, 35536, + 35536, 35536, 35536, 35536, 24591, 24590, 24587, 24560, 24561, 24583, + 24558, 24584, 24559, 24563, 24592, 24585, 24566, 24567, 24574, 24568, + 24586, 24571, 24573, 24594, 24557, 24570, 24569, 24581, 24578, 24588, + 24589, 24562, 24593, 24572, 24575, 24576, 24577, 24580, 24565, 24582, + 24579, 24564, 7494, 7492, 7490, 7491, 7493, 35536, 35536, 35536, 35536, + 35536, 32287, 32290, 32294, 32298, 32291, 32295, 32313, 32309, 32296, + 32306, 32308, 32297, 32303, 32299, 32314, 32292, 32311, 32310, 32302, + 32288, 32312, 32301, 32289, 32300, 32305, 32293, 32307, 32315, 32316, + 32304, 35536, 32317, 24600, 24642, 24643, 24623, 24624, 24621, 24622, + 24620, 24635, 24612, 24613, 24617, 24618, 24607, 24610, 24611, 24616, + 24639, 24604, 24636, 24625, 24626, 24629, 24630, 24631, 24640, 24614, + 24615, 24627, 24628, 24638, 24634, 24641, 24632, 24633, 24637, 35536, + 35536, 35536, 35536, 24601, 24602, 24603, 24619, 24608, 24609, 24605, + 24606, 24644, 24599, 24596, 24597, 24595, 24598, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 9713, + 9712, 9708, 9709, 9710, 9711, 9719, 9718, 9714, 9715, 9716, 9717, 9736, + 9721, 9735, 9732, 9737, 9730, 9727, 9723, 9728, 9726, 9729, 9734, 9733, + 9703, 9731, 9702, 9722, 9698, 9725, 9699, 9724, 9706, 9704, 9705, 9700, + 9701, 9720, 9707, 9753, 9752, 9748, 9749, 9750, 9751, 9759, 9758, 9754, + 9755, 9756, 9757, 9776, 9761, 9775, 9772, 9777, 9770, 9767, 9763, 9768, + 9766, 9769, 9774, 9773, 9743, 9771, 9742, 9762, 9738, 9765, 9739, 9764, + 9746, 9744, 9745, 9740, 9741, 9760, 9747, 27116, 27122, 27133, 27130, + 27120, 27119, 27118, 27097, 27125, 27103, 27132, 27128, 27131, 27134, + 27121, 27129, 27108, 27127, 27124, 27102, 27107, 27109, 27106, 27101, + 27095, 27092, 27114, 27123, 27112, 27096, 27117, 27135, 27099, 27093, + 27105, 27136, 27113, 27110, 27111, 27094, 27090, 27115, 27091, 27100, + 27089, 27098, 27104, 27126, 25034, 25052, 25058, 25056, 25059, 25040, + 25037, 25057, 25042, 25041, 25038, 25036, 25054, 25053, 25044, 25039, + 25047, 25043, 25048, 25049, 25055, 25060, 25033, 25050, 25061, 25045, + 25062, 25035, 25051, 25046, 35536, 35536, 25069, 25071, 25068, 25067, + 25064, 25063, 25066, 25065, 25072, 25070, 35536, 35536, 35536, 35536, + 35536, 35536, 24961, 24962, 24963, 24964, 24989, 24982, 24968, 24965, + 24971, 24981, 24980, 24995, 24974, 24969, 24973, 24990, 24991, 24992, + 24975, 24976, 24993, 24970, 24986, 24985, 24979, 24967, 24978, 24966, + 24977, 24983, 24996, 24994, 24972, 24984, 24988, 24987, 35536, 35536, + 35536, 35536, 24997, 24998, 24999, 25000, 25025, 25018, 25004, 25001, + 25007, 25017, 25016, 25031, 25010, 25005, 25009, 25026, 25027, 25028, + 25011, 25012, 25029, 25006, 25022, 25021, 25015, 25003, 25014, 25002, + 25013, 25019, 25032, 25030, 25008, 25020, 25024, 25023, 35536, 35536, + 35536, 35536, 11818, 11809, 11798, 11797, 11800, 11789, 11799, 11796, + 11795, 11810, 11786, 11785, 11811, 11819, 11812, 11802, 11788, 11787, + 11813, 11792, 11791, 11790, 11820, 11814, 11815, 11794, 11793, 11804, + 11803, 11806, 11805, 11821, 11816, 11817, 11822, 11808, 11807, 11784, + 11783, 11801, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 6173, 6222, 6189, 6185, 6187, 6212, 6186, 6210, 6207, 6176, 6209, 6211, + 6190, 6201, 6197, 6192, 6220, 6184, 6175, 6193, 6196, 6198, 6223, 6214, + 6172, 6178, 6179, 6181, 6215, 6213, 6218, 6182, 6202, 6194, 6221, 6206, + 6217, 6183, 6177, 6200, 6188, 6219, 6204, 6216, 6205, 6203, 6191, 6180, + 6174, 6208, 6199, 6195, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 6224, 33492, 33461, 33462, 33472, 33471, + 33474, 33473, 33464, 33463, 33481, 33489, 35536, 33480, 33479, 33465, + 33466, 33482, 33490, 33468, 33467, 33483, 33470, 33469, 33493, 33484, + 33491, 33485, 35536, 33476, 33475, 33478, 33477, 33494, 33486, 33487, + 35536, 33495, 33488, 35536, 33527, 33496, 33497, 33507, 33506, 33509, + 33508, 33499, 33498, 33516, 33524, 35536, 33515, 33514, 33500, 33501, + 33517, 33525, 33503, 33502, 33518, 33505, 33504, 33528, 33519, 33526, + 33520, 35536, 33511, 33510, 33513, 33512, 33529, 33521, 33522, 35536, + 33530, 33523, 35536, 35536, 35536, 31948, 31949, 31962, 31922, 31951, + 31950, 31953, 31929, 31952, 31945, 31944, 31963, 31919, 31925, 31918, + 31924, 31932, 31931, 31966, 31920, 31955, 31947, 31946, 31923, 31930, + 31926, 31942, 31934, 31964, 31941, 31940, 31939, 31936, 31935, 31957, + 31956, 31967, 31965, 31959, 31928, 31958, 31927, 31968, 31921, 31961, + 31960, 31917, 31938, 31937, 31954, 31933, 31943, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 19508, + 19509, 19510, 19511, 19512, 19513, 19514, 19515, 19516, 19447, 19448, + 19449, 19450, 19451, 19460, 19452, 19453, 19454, 19455, 19456, 19457, + 19458, 19459, 19461, 19462, 19463, 19464, 19528, 19465, 19466, 19467, + 19468, 19469, 19470, 19471, 19472, 19473, 19474, 19475, 19476, 19477, + 19478, 19479, 19480, 19481, 19482, 19483, 19484, 19485, 19486, 19487, + 19488, 19489, 19490, 19491, 19492, 19493, 19494, 19495, 19496, 19497, + 19498, 19499, 19500, 19501, 19502, 19503, 19504, 19505, 19506, 19507, + 19188, 19524, 19517, 19189, 19518, 19519, 19520, 19521, 19190, 19525, + 19526, 19522, 19523, 19527, 19194, 19195, 19196, 19197, 19198, 19199, + 19200, 19201, 19191, 19192, 19193, 19205, 19206, 19207, 19202, 19203, + 19204, 19208, 19209, 19210, 19211, 19212, 19213, 19216, 19217, 19218, + 19219, 19220, 19221, 19222, 19223, 19224, 19225, 19226, 19227, 19228, + 19229, 19230, 19231, 19232, 19233, 19234, 19235, 19236, 19237, 19238, + 19239, 19240, 19241, 19242, 19243, 19244, 19245, 19246, 19247, 19248, + 19249, 19250, 19251, 19252, 19253, 19254, 19255, 19256, 19257, 19258, + 19259, 19260, 19261, 19262, 19263, 19264, 19265, 19214, 19215, 19266, + 19267, 19268, 19269, 19270, 19271, 19272, 19273, 19274, 19275, 19276, + 19277, 19278, 19279, 19280, 19281, 19282, 19283, 19284, 19285, 19286, + 19287, 19288, 19289, 19290, 19291, 19292, 19293, 19294, 19295, 19296, + 19297, 19298, 19330, 19331, 19332, 19333, 19334, 19335, 19336, 19337, + 19338, 19299, 19300, 19301, 19302, 19303, 19304, 19305, 19306, 19307, + 19308, 19309, 19310, 19311, 19312, 19313, 19314, 19315, 19316, 19317, + 19318, 19319, 19320, 19321, 19322, 19323, 19324, 19325, 19326, 19327, + 19328, 19329, 19345, 19346, 19347, 19348, 19349, 19350, 19351, 19352, + 19353, 19354, 19355, 19356, 19357, 19358, 19359, 19360, 19361, 19362, + 19363, 19364, 19339, 19340, 19341, 19342, 19343, 19344, 19365, 19366, + 19367, 19368, 19369, 19370, 19371, 19372, 19407, 19408, 19409, 19410, + 19411, 19412, 19413, 19414, 19415, 19416, 19373, 19374, 19375, 19376, + 19377, 19378, 19379, 19380, 19381, 19382, 19383, 19384, 19385, 19386, + 19387, 19388, 19389, 19390, 19391, 19392, 19398, 19399, 19400, 19401, + 19402, 19403, 19404, 19405, 19406, 19393, 19394, 19395, 19396, 19397, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 19428, + 19423, 19426, 19425, 19429, 19424, 19422, 19427, 19421, 19417, 19420, + 19419, 19418, 19435, 19433, 19434, 19430, 19431, 19432, 19436, 19438, + 19437, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 19439, 19440, 19441, 19442, 19443, 19444, 19445, 19446, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 22568, 22689, 22688, 22553, 22569, 22557, 35536, + 22587, 22589, 22588, 22583, 22582, 22580, 22581, 22642, 22575, 22599, + 22639, 22563, 22603, 22564, 22607, 22570, 22609, 22586, 22623, 22624, + 22622, 22566, 22620, 22625, 22626, 22667, 22668, 22631, 22567, 22576, + 22682, 22664, 22665, 22638, 22637, 22572, 22652, 22655, 22656, 22653, + 22651, 22679, 35536, 22574, 22494, 22541, 22388, 22472, 22501, 22385, + 22543, 22644, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 9126, 9127, 9128, 9129, 9130, 9120, 35536, 35536, 9121, 35536, + 9076, 9077, 9078, 9079, 9080, 9081, 9082, 9083, 9084, 9085, 9086, 9087, + 9088, 9089, 9090, 9091, 9092, 9093, 9094, 9095, 9096, 9097, 9098, 9099, + 9100, 9101, 9102, 9103, 9104, 9105, 9106, 9107, 9108, 9109, 9110, 9111, + 9112, 9113, 9114, 9115, 9116, 9117, 9118, 9119, 35536, 9124, 9125, 35536, + 35536, 35536, 9122, 35536, 35536, 9123, 15761, 15772, 15763, 15757, + 15769, 15774, 15764, 15770, 15755, 15768, 15771, 15758, 15776, 15773, + 15765, 15762, 15775, 15766, 15759, 15760, 15767, 15756, 35536, 15777, + 15752, 15748, 15751, 15749, 15747, 15753, 15754, 15750, 25342, 25353, + 25344, 25338, 25350, 25355, 25345, 25351, 25336, 25349, 25352, 25339, + 25357, 25335, 25354, 25346, 25343, 25356, 25347, 25340, 25341, 25348, + 25337, 25334, 25358, 25365, 25360, 25361, 25364, 25363, 25362, 25359, + 23506, 23521, 23511, 23532, 23523, 23517, 23513, 23529, 23534, 23524, + 23530, 23515, 23509, 23528, 23510, 23531, 23507, 23518, 23514, 23536, + 23512, 23533, 23525, 23522, 23535, 23526, 23519, 23520, 23508, 23527, + 23516, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 23541, + 23538, 23539, 23544, 23545, 23543, 23540, 23537, 23542, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 14807, 14823, 14815, 14814, + 14820, 14825, 14809, 14821, 14810, 14819, 14822, 14812, 14827, 14824, + 14816, 14808, 14826, 14817, 14813, 35536, 14818, 14811, 35536, 35536, + 35536, 35536, 35536, 14828, 14832, 14831, 14830, 14829, 25738, 25757, + 25753, 25740, 25741, 25749, 25750, 25742, 25748, 25751, 25754, 25756, + 25759, 25755, 25746, 25739, 25758, 25744, 25743, 25752, 25745, 25747, + 25764, 25763, 25760, 25765, 25761, 25762, 35536, 35536, 35536, 25766, + 20005, 20011, 20015, 20013, 20007, 20023, 20016, 20024, 20017, 20001, + 20018, 20009, 20019, 20021, 20004, 19999, 20022, 20014, 20020, 20003, + 20000, 20006, 20008, 20002, 20010, 20012, 35536, 35536, 35536, 35536, + 35536, 20025, 27274, 27275, 27276, 27277, 27278, 27279, 27280, 27281, + 27282, 27283, 27284, 27285, 27286, 27287, 27288, 27289, 27290, 27291, + 27292, 27267, 27268, 27269, 27270, 27271, 27272, 27273, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 22100, 22101, 22102, 22103, + 22099, 22098, 22074, 22075, 22096, 22095, 22078, 22079, 22080, 22081, + 22076, 22077, 22094, 22091, 22090, 22082, 22083, 22084, 22092, 22097, + 22085, 22086, 22087, 22088, 22089, 22093, 22104, 22105, 21996, 22017, + 22018, 22019, 22016, 22015, 22008, 22012, 22011, 22001, 22002, 22014, + 22010, 22006, 22005, 22003, 21997, 22004, 22007, 22013, 21998, 21999, + 22000, 22009, 35536, 35536, 35536, 35536, 21984, 21989, 22021, 22020, + 22070, 22062, 22056, 22033, 22027, 22050, 22044, 22022, 22039, 22068, + 22066, 22060, 22037, 22031, 22054, 22048, 35536, 35536, 22071, 22063, + 22057, 22034, 22028, 22051, 22045, 22024, 22041, 22073, 22065, 22059, + 22036, 22030, 22053, 22047, 22026, 22043, 22069, 22067, 22061, 22038, + 22032, 22055, 22049, 22023, 22040, 22072, 22064, 22058, 22035, 22029, + 22052, 22046, 22025, 22042, 21988, 21994, 21993, 21987, 21986, 21991, + 21990, 21985, 21995, 21992, 16822, 16844, 16846, 16842, 35536, 16843, + 16845, 35536, 35536, 35536, 35536, 35536, 16847, 16838, 16841, 16840, + 16788, 16786, 16810, 16809, 35536, 16808, 16807, 16816, 35536, 16796, + 16792, 16791, 16799, 16798, 16795, 16794, 16793, 16801, 16800, 16797, + 16812, 16811, 16806, 16805, 16818, 16820, 16819, 16817, 16814, 16802, + 16803, 16804, 16821, 16815, 16787, 16789, 16790, 16813, 35536, 35536, + 16836, 16837, 16839, 35536, 35536, 35536, 35536, 16848, 16785, 16784, + 16783, 16782, 16824, 16823, 16825, 16826, 16849, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 16830, 16835, 16828, 16827, 16834, 16832, + 16831, 16829, 16833, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 24709, 24705, 24710, 24715, 24706, 24713, 24699, 24707, 24711, 24703, + 24698, 24694, 24712, 24695, 24697, 24696, 24714, 24687, 24688, 24690, + 24693, 24691, 24692, 24702, 24704, 24689, 24708, 24701, 24700, 24717, + 24716, 24718, 24530, 24548, 24529, 24539, 24551, 24552, 24541, 24545, + 24543, 24536, 24540, 24532, 24547, 24531, 24553, 24542, 24544, 24525, + 24526, 24549, 24528, 24550, 24527, 24535, 24537, 24533, 24546, 24534, + 24538, 24556, 24555, 24554, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 20306, 20310, 20309, 20314, + 20313, 20311, 20335, 20338, 20354, 20318, 20317, 20316, 20315, 20336, + 20328, 20334, 20320, 20330, 20319, 20332, 20312, 20327, 20341, 20337, + 20324, 20308, 20307, 20340, 20339, 20325, 20322, 20331, 20321, 20333, + 20326, 20323, 20329, 20356, 20355, 35536, 35536, 35536, 35536, 20342, + 20346, 20345, 20344, 20343, 20353, 20351, 20349, 20348, 20347, 20352, + 20350, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 2587, 2588, 2594, 2590, 2593, 2589, 2591, 2592, 2630, 2631, 2620, 2621, + 2622, 2623, 2618, 2619, 2635, 2608, 2607, 2606, 2597, 2595, 2596, 2632, + 2634, 2617, 2615, 2627, 2626, 2616, 2638, 2633, 2625, 2624, 2602, 2601, + 2600, 2605, 2604, 2603, 2637, 2598, 2613, 2614, 2640, 2639, 2636, 2612, + 2629, 2610, 2628, 2609, 2611, 2599, 35536, 35536, 35536, 2641, 31830, + 28182, 17352, 17347, 17353, 17348, 15903, 15914, 15905, 15899, 15911, + 15916, 15906, 15912, 15897, 15910, 15913, 15900, 15918, 15915, 15907, + 15904, 15917, 15908, 15901, 15902, 15909, 15898, 35536, 35536, 15923, + 15920, 15921, 15926, 15922, 15919, 15924, 15925, 15872, 15886, 15877, + 15873, 15883, 15876, 15878, 15884, 15870, 15882, 15885, 15874, 15875, + 15887, 15879, 15888, 15880, 15881, 15871, 35536, 35536, 35536, 35536, + 35536, 15893, 15890, 15891, 15896, 15892, 15889, 15894, 15895, 25999, + 26013, 26004, 26000, 26010, 26003, 26005, 26011, 26009, 26012, 26001, + 26002, 26014, 26006, 26016, 26007, 26008, 26015, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 26024, 26025, 25997, 25998, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 26021, 26018, 26019, 26023, 26020, 26017, 26022, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 24719, 24761, 24762, + 24751, 24787, 24780, 24754, 24755, 24789, 24732, 24772, 24720, 24765, + 24734, 24774, 24722, 24766, 24733, 24773, 24721, 24750, 24786, 24740, + 24779, 24729, 24769, 24723, 24767, 24756, 24790, 24735, 24775, 24724, + 24745, 24748, 24736, 24725, 24763, 24743, 24782, 24741, 24781, 24744, + 24783, 24771, 24742, 24764, 24749, 24757, 24752, 24747, 24785, 24737, + 24776, 24753, 24788, 24758, 24791, 24738, 24777, 24726, 24730, 24727, + 24731, 24770, 24746, 24784, 24739, 24778, 24728, 24768, 24759, 24760, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 24378, 24381, 24404, 24379, 24393, + 24389, 24395, 24405, 24380, 24383, 24426, 24406, 24407, 24396, 24397, + 24408, 24427, 24428, 24409, 24410, 24382, 24423, 24398, 24399, 24384, + 24386, 24390, 24418, 24420, 24414, 24416, 24419, 24411, 24385, 24412, + 24421, 24391, 24392, 24400, 24387, 24401, 24394, 24422, 24425, 24415, + 24417, 24413, 24402, 24403, 24388, 24424, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 24429, + 24432, 24455, 24430, 24444, 24440, 24446, 24456, 24431, 24434, 24477, + 24457, 24458, 24447, 24448, 24459, 24478, 24479, 24460, 24461, 24433, + 24474, 24449, 24450, 24435, 24437, 24441, 24469, 24471, 24465, 24467, + 24470, 24462, 24436, 24463, 24472, 24442, 24443, 24451, 24438, 24452, + 24445, 24473, 24476, 24466, 24468, 24464, 24453, 24454, 24439, 24475, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 24482, 24481, 24485, + 24480, 24483, 24484, 14760, 14747, 14755, 14739, 14738, 14752, 14748, + 14751, 14736, 14749, 14733, 14732, 14741, 14740, 14759, 14746, 14745, + 14737, 14750, 14753, 14754, 14744, 14757, 14734, 14758, 14735, 14742, + 14743, 14756, 14767, 14769, 14771, 14768, 14770, 14762, 14761, 14766, + 14763, 14765, 14764, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 14778, 14780, 14777, 14776, 14773, 14772, 14775, 14774, 14781, + 14779, 35536, 35536, 35536, 35536, 35536, 35536, 12845, 12847, 12844, + 12843, 12840, 12839, 12842, 12841, 12848, 12846, 12831, 12832, 12833, + 12830, 12834, 12828, 12805, 12789, 12797, 12795, 12788, 12794, 12800, + 12802, 12796, 12792, 12790, 12803, 12804, 12801, 12799, 12786, 12791, + 12787, 12798, 12793, 12784, 12785, 35536, 35536, 35536, 12829, 12783, + 12781, 12780, 12782, 12836, 12835, 12827, 12811, 12819, 12817, 12810, + 12816, 12822, 12824, 12818, 12814, 12812, 12825, 12826, 12823, 12821, + 12808, 12813, 12809, 12820, 12815, 12806, 12807, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 12837, 12838, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 26578, 26576, 26575, 26572, 26571, + 26574, 26573, 26579, 26577, 26570, 26569, 26567, 26558, 26556, 26565, + 26563, 26554, 26560, 26561, 26568, 26566, 26557, 26555, 26564, 26562, + 26553, 26559, 26550, 26551, 26549, 26552, 35536, 33993, 34027, 34007, + 34006, 34012, 34011, 33989, 33988, 33987, 33998, 34019, 33992, 34023, + 34026, 34025, 34022, 34030, 34009, 34008, 34010, 33991, 34013, 34024, + 33994, 34018, 34029, 34014, 34015, 34002, 34000, 33999, 34001, 34003, + 33990, 34005, 34028, 34016, 34017, 33996, 33997, 34020, 33995, 35536, + 33985, 33984, 33986, 35536, 35536, 34004, 34021, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 1201, 1497, 1332, 2388, 1540, 1604, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 1065, 1654, 1652, 1650, 1909, + 1910, 1911, 1913, 1895, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 1083, 2389, 1066, 2395, 2396, + 2393, 24649, 24657, 24671, 24658, 24662, 24668, 24659, 24674, 24663, + 24669, 24667, 24670, 24661, 24676, 24672, 24651, 24652, 24664, 24650, + 24648, 24675, 24665, 24653, 24654, 24660, 24666, 24673, 24655, 24656, + 24683, 24681, 24678, 24686, 24685, 24682, 24680, 24679, 24684, 24647, + 24677, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 28242, + 28256, 28244, 28253, 28260, 28248, 28254, 28252, 28255, 28245, 28262, + 28258, 28249, 28243, 28261, 28250, 28247, 28251, 28259, 28257, 28246, + 28241, 28236, 28234, 28237, 28235, 28240, 28239, 28231, 28230, 28232, + 28238, 28233, 28263, 28266, 28265, 28264, 28269, 28270, 28267, 28271, + 28268, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 24796, 24808, 24806, 24811, 24800, 24805, 24804, + 24807, 24798, 24813, 24809, 24801, 24812, 24802, 24797, 24803, 24810, + 24799, 24795, 24794, 24793, 24792, 24817, 24814, 24815, 24816, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 6596, 6590, 6604, 6598, + 6593, 6601, 6607, 6589, 6599, 6602, 6600, 6603, 6594, 6609, 6605, 6591, + 6597, 6608, 6595, 6592, 6606, 6614, 6611, 6612, 6616, 6613, 6610, 6615, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 11845, 11856, 11847, 11841, 11853, 11858, 11848, 11854, 11839, 11852, + 11855, 11842, 11860, 11857, 11849, 11846, 11859, 11850, 11843, 11844, + 11851, 11840, 11861, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 4737, 4742, 4740, 4739, 4741, 4664, 4665, 4683, 4684, 4681, + 4682, 4676, 4677, 4678, 4679, 4710, 4666, 4657, 4667, 4703, 4702, 4699, + 4698, 4687, 4697, 4696, 4701, 4700, 4689, 4673, 4672, 4669, 4668, 4688, + 4675, 4674, 4671, 4670, 4690, 4705, 4704, 4695, 4694, 4707, 4709, 4708, + 4686, 4680, 4691, 4692, 4693, 4706, 4685, 4658, 4663, 4662, 4747, 4746, + 4756, 4757, 4750, 4751, 4752, 4753, 4754, 4755, 4758, 4748, 4743, 4749, + 4759, 4761, 4760, 4735, 4734, 4733, 4736, 4732, 35536, 35536, 35536, + 35536, 4728, 4726, 4723, 4714, 4716, 4721, 4719, 4711, 4717, 4727, 4725, + 4724, 4713, 4715, 4722, 4720, 4712, 4718, 4729, 4730, 4768, 4770, 4767, + 4766, 4763, 4762, 4765, 4764, 4771, 4769, 4738, 4660, 4661, 4744, 4745, + 4659, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 4731, 16132, 16134, 16136, 16092, 16093, 16102, 16103, 16100, 16101, + 16130, 16094, 16131, 16095, 16120, 16119, 16116, 16115, 16104, 16114, + 16113, 16118, 16117, 16106, 16097, 16096, 16089, 16087, 16088, 16123, + 16105, 16099, 16098, 16091, 16090, 16107, 16122, 16121, 16112, 16111, + 16127, 16129, 16124, 16126, 16128, 16108, 16109, 16110, 16125, 16142, + 16147, 16148, 16145, 16146, 16149, 16143, 16150, 16144, 16135, 16133, + 16153, 16154, 16151, 16137, 16138, 16140, 16139, 16141, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 16152, 35536, + 35536, 28294, 28295, 28284, 28285, 28286, 28287, 28280, 28281, 28291, + 28283, 28296, 28292, 28297, 28293, 28288, 28290, 28289, 28282, 28298, + 28277, 28299, 28301, 28300, 28278, 28279, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 28308, 28310, 28307, 28306, 28303, 28302, 28305, + 28304, 28311, 28309, 35536, 35536, 35536, 35536, 35536, 35536, 6276, + 6278, 6277, 6272, 6274, 6275, 6273, 6261, 6260, 6257, 6256, 6242, 6255, + 6254, 6259, 6258, 6244, 6247, 6246, 6239, 6238, 6243, 6249, 6248, 6241, + 6240, 6245, 6265, 6264, 6253, 6252, 6267, 6250, 6251, 6268, 6263, 6271, + 6269, 6266, 6280, 6288, 6289, 6284, 6285, 6286, 6282, 6290, 6283, 6291, + 6308, 6307, 6292, 6306, 35536, 6301, 6303, 6300, 6299, 6296, 6295, 6298, + 6297, 6304, 6302, 6279, 6294, 6293, 6305, 6262, 6281, 6287, 6270, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 20067, 20069, 20071, + 20068, 20070, 20059, 20058, 20055, 20054, 20053, 20052, 20057, 20056, + 20038, 20047, 20046, 20041, 20040, 20037, 20049, 20048, 20043, 20042, + 20039, 20061, 20060, 20051, 20050, 20064, 20045, 20063, 20066, 20065, + 20062, 20044, 20074, 20075, 20073, 20072, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 27052, 27055, 27059, 26998, 26999, + 27017, 27018, 27015, 27016, 27010, 27011, 27012, 27013, 27044, 27000, + 27045, 27001, 27037, 27036, 27033, 27032, 27021, 27031, 27030, 27035, + 27034, 27023, 27007, 27006, 27003, 27002, 27022, 27009, 27008, 27005, + 27004, 27024, 27039, 27038, 27029, 27028, 27041, 27043, 27042, 27020, + 27019, 27014, 27025, 27026, 27027, 27040, 27074, 27081, 27082, 27068, + 27069, 27077, 27078, 27079, 27080, 27083, 27075, 27064, 27076, 27058, + 27054, 27056, 27057, 27086, 26984, 26983, 27084, 27049, 27046, 27053, + 27061, 26995, 27060, 27067, 27050, 26991, 26993, 26990, 26989, 26986, + 26985, 26988, 26987, 26994, 26992, 26996, 27051, 26997, 27085, 27047, + 27048, 35536, 27996, 27994, 27993, 27990, 27989, 27992, 27991, 27997, + 27995, 28008, 28007, 28006, 28002, 28001, 28005, 28004, 28000, 28003, + 27998, 27999, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 17056, 17057, 17083, 17085, 17082, 17058, 17084, + 17059, 17073, 17072, 17048, 17046, 17047, 17066, 17071, 17070, 17055, + 17054, 35536, 17068, 17061, 17060, 17051, 17050, 17067, 17063, 17062, + 17053, 17049, 17052, 17069, 17075, 17074, 17045, 17043, 17044, 17077, + 17081, 17079, 17065, 17080, 17042, 17076, 17064, 17093, 17096, 17097, + 17100, 17098, 17094, 17099, 17095, 17090, 17089, 17088, 17086, 17040, + 17039, 17101, 17091, 17038, 17102, 17087, 17078, 17041, 17092, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 22981, 22983, 22984, 22982, 22972, 22971, 22970, 35536, 22969, + 35536, 22968, 22967, 22960, 22959, 35536, 22954, 22962, 22961, 22950, + 22948, 22949, 22953, 22964, 22963, 22952, 22951, 22955, 22974, 22973, + 22966, 35536, 22965, 22977, 22980, 22958, 22976, 22979, 22978, 22975, + 22957, 22956, 22985, 35536, 35536, 35536, 35536, 35536, 35536, 17117, + 17118, 17129, 17130, 17127, 17128, 17148, 17119, 17149, 17120, 17138, + 17137, 17108, 17106, 17107, 17131, 17136, 17135, 17116, 17115, 17114, + 17133, 17124, 17123, 17111, 17109, 17121, 17110, 17132, 17126, 17125, + 17113, 17112, 17134, 17140, 17139, 17105, 17103, 17104, 17145, 17147, + 17122, 17144, 17146, 17141, 17142, 17143, 17152, 17153, 17158, 17159, + 17156, 17157, 17160, 17154, 17161, 17155, 17150, 17151, 35536, 35536, + 35536, 35536, 35536, 17168, 17170, 17167, 17166, 17163, 17162, 17165, + 17164, 17171, 17169, 35536, 35536, 35536, 35536, 35536, 35536, 13243, + 13244, 13247, 13250, 35536, 13200, 13201, 13214, 13215, 13212, 13213, + 13195, 13197, 35536, 35536, 13238, 13202, 35536, 35536, 13237, 13203, + 13234, 13233, 13230, 13229, 13218, 13228, 13227, 13232, 13231, 13220, + 13209, 13208, 13205, 13204, 13219, 13211, 13210, 13207, 13206, 13221, + 35536, 13236, 13235, 13226, 13225, 13240, 13242, 13241, 35536, 13217, + 13216, 35536, 13199, 13222, 13223, 13224, 13239, 35536, 7169, 13245, + 13246, 13252, 13261, 13262, 13255, 13256, 13257, 13258, 35536, 35536, + 13264, 13253, 35536, 35536, 13263, 13254, 13249, 35536, 35536, 13265, + 35536, 35536, 35536, 35536, 35536, 35536, 13251, 35536, 35536, 35536, + 35536, 35536, 13248, 13194, 13193, 13196, 13198, 13259, 13260, 35536, + 35536, 7358, 7359, 7357, 7356, 7355, 7354, 7353, 35536, 35536, 35536, + 7364, 7361, 7362, 7360, 7363, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 32166, 32167, 32190, 32191, 32188, + 32189, 32183, 32184, 32185, 32186, 35536, 32212, 35536, 35536, 32168, + 35536, 32211, 32169, 32208, 32207, 32204, 32203, 32192, 32202, 32201, + 32206, 32205, 32194, 32180, 32179, 32171, 32170, 32193, 32182, 32181, + 32173, 32172, 32195, 32210, 32209, 32200, 32199, 32214, 32215, 32178, + 32176, 32187, 32196, 32197, 32198, 32213, 32175, 32177, 32174, 35536, + 32217, 32228, 32237, 32238, 32231, 32232, 32233, 32234, 32235, 32236, + 35536, 32240, 35536, 35536, 32229, 35536, 32239, 32230, 32161, 32222, + 35536, 32218, 32225, 32224, 32219, 32162, 32216, 32165, 32223, 32164, + 32163, 35536, 32220, 32221, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 32227, 32226, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 23904, 23905, 23918, 23919, 23916, 23917, 23900, + 23901, 23902, 23903, 23944, 23906, 23945, 23907, 23932, 23931, 23928, + 23927, 23893, 23892, 23926, 23925, 23930, 23929, 23895, 23894, 23913, + 23912, 23909, 23908, 23897, 23915, 23914, 23911, 23910, 23898, 23896, + 23938, 23937, 23924, 23923, 23936, 23935, 23943, 23940, 23939, 23934, + 23933, 23942, 23920, 23921, 23922, 23941, 23958, 23967, 23968, 23961, + 23962, 23963, 23964, 23965, 23966, 23969, 23959, 23970, 23960, 23953, + 23946, 23949, 23954, 23947, 23948, 23951, 23974, 23955, 23880, 23878, + 23973, 23891, 23971, 23887, 23889, 23886, 23885, 23882, 23881, 23884, + 23883, 23890, 23888, 23879, 23957, 35536, 23972, 23956, 23899, 23950, + 23952, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 31832, 31833, 31834, 31852, 31853, 31850, 31851, 31845, 31846, + 31847, 31848, 31878, 31835, 31879, 31836, 31870, 31869, 31866, 31865, + 31854, 31864, 31863, 31868, 31867, 31856, 31842, 31841, 31838, 31837, + 31855, 31844, 31843, 31840, 31839, 31857, 31872, 31871, 31862, 31861, + 31875, 31877, 31876, 31874, 31849, 31858, 31859, 31860, 31873, 31888, + 31897, 31898, 31891, 31892, 31893, 31894, 31895, 31896, 31899, 31886, + 31889, 31900, 31887, 31890, 31880, 31883, 31885, 31884, 31881, 31882, + 31911, 31831, 31912, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 31907, 31909, 31906, 31905, 31902, 31901, 31904, 31903, 31910, + 31908, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 27176, 27178, 27199, + 27200, 27197, 27198, 27192, 27193, 27194, 27195, 27225, 27179, 27226, + 27180, 27217, 27216, 27213, 27212, 27201, 27211, 27210, 27215, 27214, + 27203, 27186, 27185, 27189, 27188, 27202, 27187, 27182, 27191, 27190, + 27204, 27219, 27218, 27209, 27208, 27222, 27224, 27223, 27221, 27196, + 27205, 27206, 27207, 27220, 27254, 27261, 27262, 27259, 27260, 27257, + 27258, 35536, 35536, 27263, 27255, 27264, 27256, 27247, 27249, 27251, + 27250, 27248, 27246, 27266, 27265, 27245, 27244, 27227, 27228, 27229, + 27175, 27242, 27241, 27239, 27237, 27238, 27230, 27231, 27240, 27243, + 27235, 27236, 27234, 27233, 27232, 27181, 27183, 27184, 27177, 27252, + 27253, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 22335, 22336, 22354, 22355, 22352, + 22353, 22347, 22348, 22349, 22350, 22381, 22337, 22382, 22338, 22374, + 22373, 22370, 22369, 22358, 22368, 22367, 22372, 22371, 22360, 22344, + 22343, 22340, 22339, 22359, 22346, 22345, 22342, 22341, 22361, 22376, + 22375, 22366, 22365, 22378, 22380, 22379, 22357, 22351, 22362, 22363, + 22364, 22377, 22356, 22310, 22319, 22320, 22313, 22314, 22315, 22316, + 22317, 22318, 22321, 22311, 22322, 22312, 22306, 22309, 22308, 22305, + 22324, 22323, 22383, 22307, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 22331, 22333, 22330, 22329, 22326, + 22325, 22328, 22327, 22334, 22332, 35536, 35536, 35536, 35536, 35536, + 35536, 22864, 22859, 22704, 22865, 22863, 22861, 22860, 22721, 22722, + 22855, 22857, 22856, 22866, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 29705, 29707, 29722, 29723, 29720, 29721, 29747, + 29708, 29748, 29709, 29737, 29736, 29733, 29732, 29724, 29731, 29730, + 29735, 29734, 29726, 29717, 29716, 29711, 29710, 29725, 29719, 29718, + 29713, 29712, 29727, 29739, 29738, 29729, 29728, 29744, 29746, 29715, + 29743, 29745, 29740, 29741, 29742, 29714, 29750, 29752, 29753, 29758, + 29759, 29756, 29757, 29760, 29754, 29761, 29755, 29751, 29749, 29706, + 29704, 35536, 35536, 35536, 35536, 35536, 35536, 29768, 29770, 29767, + 29766, 29763, 29762, 29765, 29764, 29771, 29769, 35536, 35536, 35536, + 35536, 35536, 35536, 23473, 23475, 23472, 23471, 23468, 23467, 23470, + 23469, 23476, 23474, 23423, 23425, 23422, 23421, 23418, 23417, 23420, + 23419, 23426, 23424, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 191, 190, 178, 181, 175, 167, 193, 192, 183, 195, 189, 184, 174, + 196, 177, 197, 180, 194, 164, 171, 170, 187, 166, 186, 182, 188, 165, + 35536, 35536, 162, 163, 161, 203, 204, 210, 211, 208, 209, 212, 207, 213, + 205, 206, 200, 35536, 35536, 35536, 35536, 222, 224, 221, 220, 217, 216, + 219, 218, 225, 223, 215, 214, 198, 199, 201, 202, 185, 173, 172, 169, + 168, 179, 176, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 10145, 10146, 10161, + 10162, 10159, 10160, 10187, 10147, 10188, 10148, 10179, 10178, 10175, + 10174, 10163, 10173, 10172, 10177, 10176, 10165, 10156, 10155, 10150, + 10149, 10164, 10158, 10157, 10152, 10151, 10166, 10181, 10180, 10171, + 10170, 10184, 10186, 10154, 10183, 10185, 10167, 10168, 10169, 10182, + 10153, 10191, 10196, 10197, 10194, 10195, 10189, 10190, 10198, 10192, + 10199, 10193, 10202, 10204, 10203, 10201, 10200, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 33632, 33621, 33650, + 33640, 33642, 33643, 33649, 33639, 33625, 33634, 33622, 33652, 33648, + 33627, 33641, 33638, 33626, 33635, 33644, 33633, 33651, 33624, 33623, + 33646, 33647, 33630, 33629, 33628, 33631, 33636, 33637, 33645, 33664, + 33653, 33682, 33672, 33674, 33675, 33681, 33671, 33657, 33666, 33654, + 33684, 33680, 33659, 33673, 33670, 33658, 33667, 33676, 33665, 33683, + 33656, 33655, 33678, 33679, 33662, 33661, 33660, 33663, 33668, 33669, + 33677, 33691, 33693, 33690, 33689, 33686, 33685, 33688, 33687, 33694, + 33692, 33703, 33702, 33701, 33697, 33696, 33700, 33699, 33695, 33698, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 33704, 10061, 10062, 10069, 10070, 10067, 10068, 10096, + 35536, 35536, 10097, 35536, 35536, 10087, 10086, 10085, 10084, 10073, + 10083, 10082, 10091, 35536, 10075, 10057, 35536, 10064, 10063, 10074, + 10058, 10056, 10066, 10065, 10076, 10089, 10088, 10081, 10080, 10092, + 10060, 10059, 10093, 10072, 10094, 10077, 10078, 10079, 10090, 10071, + 10095, 10102, 10106, 10107, 10104, 10105, 10108, 35536, 10103, 10109, + 35536, 35536, 10101, 10099, 10098, 10110, 10115, 10112, 10116, 10111, + 10100, 10045, 10113, 10114, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 10052, 10054, 10051, 10050, 10047, 10046, 10049, + 10048, 10055, 10053, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 23595, 23596, 23611, 23612, 23609, 23610, 23592, + 23593, 35536, 35536, 23637, 23597, 23638, 23598, 23631, 23630, 23627, + 23626, 23615, 23625, 23624, 23629, 23628, 23617, 23606, 23605, 23600, + 23599, 23616, 23608, 23607, 23602, 23601, 23618, 23633, 23632, 23623, + 23622, 23635, 23636, 23604, 23614, 23594, 23619, 23620, 23621, 23634, + 23613, 23603, 23647, 23652, 23653, 23650, 23651, 23645, 23646, 35536, + 35536, 23654, 23648, 23655, 23649, 23641, 23643, 23642, 23640, 23639, + 23656, 23644, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35317, + 35338, 35335, 35334, 35337, 35333, 35332, 35330, 35331, 35336, 35329, + 35285, 35284, 35304, 35303, 35286, 35302, 35301, 35311, 35288, 35296, + 35295, 35278, 35277, 35287, 35298, 35297, 35282, 35281, 35289, 35306, + 35305, 35300, 35299, 35313, 35294, 35293, 35280, 35279, 35307, 35308, + 35309, 35316, 35314, 35312, 35315, 35290, 35291, 35292, 35310, 35283, + 35276, 35326, 35323, 35324, 35325, 35322, 35327, 35273, 35272, 35270, + 35269, 35271, 35275, 35268, 35321, 35319, 35318, 35320, 35274, 35267, + 35328, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 28400, + 28421, 28419, 28418, 28420, 28416, 28417, 28414, 28415, 28413, 28412, + 28422, 28367, 28366, 28386, 28385, 28368, 28384, 28383, 28388, 28387, + 28370, 28378, 28377, 28361, 28360, 28369, 28380, 28379, 28364, 28362, + 28371, 28390, 28389, 28382, 28381, 28396, 28376, 28375, 28363, 28391, + 28392, 28393, 28399, 28397, 28395, 28398, 28372, 28373, 28374, 28394, + 28365, 28405, 28407, 28344, 28343, 28341, 28342, 28352, 28353, 28348, + 28351, 28347, 28350, 28355, 28356, 28354, 28346, 28345, 28349, 28408, + 28406, 28423, 28409, 28404, 28403, 28402, 28401, 28359, 28358, 28357, + 28410, 28411, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 5719, 5720, 5717, 5718, 5715, 5716, + 5725, 5726, 5723, 5724, 5721, 5722, 5888, 5889, 5890, 5887, 25556, 25554, + 25563, 25564, 25560, 25568, 25567, 25549, 25562, 25561, 25553, 25566, + 25559, 25552, 25558, 25557, 25550, 25555, 25565, 25544, 25551, 25569, + 25570, 25545, 25571, 25547, 25548, 25546, 25540, 25537, 25541, 25539, + 25535, 25538, 25542, 25536, 25543, 25577, 25576, 25587, 25578, 25579, + 25588, 25584, 25583, 25585, 25586, 25580, 25534, 25581, 25582, 25573, + 25572, 25532, 25574, 25575, 25533, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 9787, 9788, 9877, 9878, 9879, 9880, 9887, 9886, 9888, 9892, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 27066, 27065, 27071, 27070, + 27072, 27073, 27062, 27063, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 29007, 29026, 29033, 29030, 29020, 29017, 29012, 29034, + 29002, 29019, 29029, 29009, 29006, 29015, 29032, 29010, 29028, 29016, + 29021, 29027, 29031, 29004, 29003, 29008, 29024, 29018, 29014, 29013, + 29022, 29005, 29023, 29025, 29011, 29035, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 29042, 29044, 29041, 29040, 29037, 29036, 29039, 29038, 29045, 29043, + 35536, 35536, 35536, 35536, 35536, 35536, 3761, 3762, 3775, 3776, 3773, + 3774, 3757, 3758, 3759, 35536, 3801, 3763, 3802, 3764, 3793, 3792, 3789, + 3788, 3777, 3787, 3786, 3791, 3790, 3779, 3770, 3769, 3766, 3765, 3778, + 3772, 3771, 3768, 3767, 3780, 3795, 3794, 3785, 3784, 3798, 3800, 3799, + 3797, 3760, 3781, 3782, 3783, 3796, 3829, 3834, 3835, 3832, 3833, 3826, + 3827, 3828, 35536, 3836, 3830, 3837, 3831, 3821, 3823, 3825, 3824, 3822, + 3839, 3838, 3850, 3851, 3852, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 3846, 3848, 3845, 3844, 3841, 3840, 3843, + 3842, 3849, 3847, 3820, 3818, 3815, 3806, 3808, 3813, 3811, 3803, 3809, + 3819, 3817, 3816, 3805, 3807, 3814, 3812, 3804, 3810, 3853, 35536, 35536, + 35536, 20431, 20432, 20377, 20376, 20386, 20371, 20375, 20374, 20388, + 20372, 20368, 20367, 20370, 20373, 20379, 20378, 20385, 20390, 20366, + 20365, 20369, 20392, 20382, 20383, 20384, 20393, 20391, 20389, 20380, + 20381, 20387, 20394, 35536, 35536, 20409, 20408, 20417, 20403, 20407, + 20406, 20419, 20404, 20400, 20399, 20402, 20405, 20411, 20410, 20416, + 20421, 20398, 20397, 20401, 20423, 20414, 20415, 35536, 20424, 20422, + 20420, 20412, 20413, 20418, 20425, 20426, 20428, 20430, 20427, 20429, + 20396, 20395, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 20443, 20444, 20453, 20454, 20451, + 20452, 20480, 35536, 20445, 20481, 35536, 20446, 20459, 20458, 20472, + 20471, 20460, 20470, 20469, 20437, 20436, 20462, 20439, 20438, 20448, + 20447, 20461, 20442, 20440, 20450, 20449, 20463, 20474, 20473, 20468, + 20467, 20476, 20479, 20477, 20456, 20478, 20464, 20465, 20466, 20475, + 20455, 20457, 20435, 20441, 20490, 20495, 20496, 20493, 20494, 20489, + 35536, 35536, 35536, 20497, 35536, 20491, 20498, 35536, 20492, 20488, + 20487, 20486, 20484, 20485, 20499, 20483, 20482, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 20506, 20508, 20505, 20504, 20501, + 20500, 20503, 20502, 20509, 20507, 35536, 35536, 35536, 35536, 35536, + 35536, 13918, 13919, 13932, 13933, 13930, 13931, 35536, 13949, 13920, + 35536, 13948, 13921, 13955, 13954, 13937, 13936, 13951, 13945, 13944, + 13929, 13928, 13935, 13941, 13940, 13925, 13924, 13917, 13939, 13938, + 13927, 13926, 13934, 13943, 13942, 13923, 13922, 13916, 13947, 13946, + 13950, 13952, 13953, 13958, 13963, 13964, 13961, 13962, 35536, 13966, + 13959, 35536, 13965, 13960, 13957, 13956, 13967, 13978, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 13974, 13976, 13973, 13972, 13969, + 13968, 13971, 13970, 13977, 13975, 35536, 35536, 35536, 35536, 35536, + 35536, 32001, 31999, 32006, 32004, 31969, 31970, 31997, 31998, 31987, + 31988, 32003, 31983, 31986, 31971, 31974, 31975, 31984, 31985, 31972, + 31973, 31976, 31989, 31990, 31993, 31994, 31979, 31995, 31996, 31991, + 31992, 31978, 32009, 31980, 32002, 32007, 31977, 32005, 32000, 32008, + 31981, 31982, 32010, 32011, 32012, 35536, 35536, 35536, 35536, 32019, + 32021, 32018, 32017, 32014, 32013, 32016, 32015, 32022, 32020, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 20129, 20127, 20121, 20132, 20124, + 20131, 20135, 20126, 20123, 20125, 20128, 20122, 20137, 20133, 20130, + 20136, 20134, 20138, 20144, 20141, 20143, 20140, 20142, 20139, 20120, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 16667, 16671, 16669, + 16670, 16608, 16609, 16628, 16629, 16622, 16623, 16624, 16625, 16626, + 16627, 16653, 16610, 16654, 35536, 16644, 16643, 16642, 16641, 16630, + 16640, 16639, 16613, 16612, 16632, 16619, 16618, 16615, 16614, 16631, + 16621, 16620, 16617, 16616, 16633, 16646, 16645, 16638, 16637, 16649, + 16652, 16650, 16648, 16651, 16634, 16635, 16636, 16647, 16611, 16673, + 16672, 16680, 16681, 16678, 16679, 16675, 35536, 35536, 35536, 16676, + 16674, 16677, 16666, 16694, 16683, 16682, 16661, 16664, 16660, 16662, + 16658, 16657, 16665, 16656, 16659, 16663, 16655, 16690, 16692, 16689, + 16688, 16685, 16684, 16687, 16686, 16693, 16691, 16668, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 19775, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 29828, + 29824, 29818, 29827, 29820, 29829, 29833, 29835, 29830, 29825, 29826, + 29831, 29819, 29836, 29834, 29821, 29832, 29822, 29823, 29837, 29838, + 29902, 29885, 29883, 29892, 29891, 29887, 29893, 29889, 29888, 29895, + 29896, 29897, 29894, 29886, 29899, 29817, 29804, 29875, 29882, 30172, + 30173, 29802, 29777, 29903, 30171, 29839, 29904, 29890, 29898, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 29880, 7938, 7946, 7944, 7940, 7945, 7941, 7939, 7942, + 7943, 8007, 7947, 7956, 7955, 7949, 7948, 7960, 7950, 7951, 7959, 7953, + 7954, 7961, 7962, 7967, 7964, 7963, 7965, 7966, 7969, 7971, 7973, 7972, + 7974, 7980, 7977, 7978, 7982, 7975, 7976, 7981, 7979, 7984, 7983, 7985, + 7987, 7988, 7992, 7991, 7989, 7990, 7993, 8006, 7994, 7995, 7996, 8005, + 7997, 8001, 8002, 8000, 7998, 7999, 8004, 8003, 8008, 8009, 8020, 8011, + 8015, 8016, 8017, 8018, 8019, 8021, 8024, 8023, 8022, 8025, 8027, 8028, + 8029, 8030, 8031, 8032, 8033, 8034, 8035, 8036, 8037, 8038, 8039, 8040, + 8041, 8042, 8043, 8044, 8059, 8045, 8046, 8056, 8050, 8047, 8048, 8049, + 8057, 8054, 8058, 8055, 8051, 8053, 8066, 8062, 8063, 8064, 8067, 8078, + 8068, 8071, 8072, 8074, 8075, 8076, 8079, 8082, 8080, 8081, 8083, 8084, + 8086, 8087, 8116, 8123, 8117, 8118, 8119, 8120, 8121, 8122, 8124, 8126, + 8125, 8127, 8130, 8131, 8134, 8128, 8129, 8135, 8180, 8179, 8181, 8136, + 8139, 8140, 8141, 8137, 8138, 8142, 8145, 8143, 8146, 8148, 8157, 8158, + 8159, 8160, 8178, 8161, 8162, 8175, 8176, 8174, 8163, 8164, 8165, 8166, + 8167, 8168, 8169, 8172, 8173, 8182, 8272, 8183, 8184, 8186, 8185, 8192, + 8187, 8189, 8191, 8195, 8194, 8196, 8197, 8204, 8198, 8199, 8203, 8207, + 8208, 8205, 8206, 8214, 8211, 8215, 8216, 8217, 8218, 8219, 8221, 8222, + 8224, 8223, 8226, 8225, 8228, 8227, 8229, 8230, 8232, 8233, 8237, 8239, + 8243, 8244, 8257, 8249, 8250, 8245, 8246, 8247, 8251, 8255, 8252, 8253, + 8254, 8258, 8259, 8261, 8262, 8263, 8264, 8265, 8266, 8276, 8267, 8268, + 8271, 8270, 8269, 8273, 8274, 8275, 8277, 8278, 8281, 8282, 8283, 8284, + 8285, 8287, 8286, 8303, 8294, 8295, 8288, 8289, 8291, 8292, 8290, 8293, + 8302, 8296, 8301, 8299, 8298, 8300, 8305, 8324, 8306, 8307, 8308, 8311, + 8310, 8312, 8313, 8315, 8316, 8317, 8325, 8318, 8319, 8320, 8323, 8322, + 8321, 8326, 8327, 8329, 8330, 8331, 8332, 8334, 8338, 8335, 8339, 8337, + 8336, 8340, 8341, 8342, 8343, 8347, 8346, 8344, 8345, 8348, 8349, 8351, + 8370, 8372, 8352, 8354, 8353, 8355, 8356, 8359, 8360, 8358, 8357, 8361, + 8362, 8363, 8364, 8367, 8365, 8366, 8368, 8369, 8373, 8374, 8371, 8375, + 8376, 8377, 8378, 8380, 8383, 8382, 8384, 8385, 8387, 8388, 8389, 8393, + 8392, 8390, 8391, 8394, 8398, 8397, 8396, 8399, 8400, 8402, 8403, 8407, + 8404, 8405, 8410, 8408, 8411, 8412, 8414, 8413, 8415, 8416, 8438, 8437, + 8440, 8441, 8422, 8423, 8420, 8421, 8419, 8417, 8425, 8424, 8426, 8428, + 8434, 8435, 8432, 8433, 8442, 8443, 8444, 8462, 8447, 8448, 8449, 8445, + 8446, 8450, 8451, 8452, 8453, 8454, 8456, 8457, 8458, 8459, 8460, 8485, + 8463, 8466, 8464, 8465, 8471, 8472, 8469, 8470, 8467, 8468, 8473, 8474, + 8482, 8475, 8476, 8483, 8480, 8481, 8484, 8477, 8478, 8479, 8486, 8487, + 8488, 8489, 8490, 8491, 8492, 8494, 8495, 8493, 8496, 8497, 8538, 8539, + 8500, 8501, 8498, 8499, 8504, 8505, 8503, 8509, 8506, 8508, 8507, 8513, + 8514, 8512, 8510, 8511, 8515, 8516, 8517, 8518, 8519, 8520, 8521, 8540, + 8526, 8522, 8523, 8524, 8525, 8527, 8529, 8528, 8530, 8531, 8533, 8532, + 8534, 8536, 8535, 8541, 8542, 8545, 8546, 8543, 8544, 8620, 8615, 8616, + 8617, 8618, 8619, 8621, 8624, 8622, 8623, 8625, 8667, 8626, 8656, 8657, + 8633, 8635, 8652, 8636, 8658, 8640, 8638, 8639, 8641, 8642, 8643, 8644, + 8653, 8654, 8647, 8648, 8650, 8659, 8627, 8628, 8632, 8630, 8668, 8660, + 8662, 8661, 8663, 8669, 8670, 8664, 8665, 8666, 8671, 8672, 8673, 8676, + 8677, 8678, 8674, 8675, 8679, 8680, 8682, 8684, 8685, 8703, 8701, 8702, + 8705, 8704, 8686, 8693, 8691, 8692, 8687, 8688, 8694, 8695, 8696, 8697, + 8698, 8700, 8706, 8715, 8707, 8709, 8710, 8708, 8711, 8713, 8712, 8714, + 8717, 8719, 8718, 8720, 8721, 8754, 8756, 8722, 8724, 8723, 8726, 8729, + 8727, 8728, 8732, 8736, 8739, 8738, 8741, 8744, 8742, 8743, 8747, 8749, + 8755, 8757, 8758, 8762, 8769, 8767, 8765, 8766, 8768, 8771, 8770, 8763, + 8764, 8772, 8775, 8781, 8776, 8779, 8774, 8773, 8782, 8780, 8777, 8778, + 8783, 8784, 8785, 8786, 8787, 8788, 8789, 8791, 8794, 8795, 8798, 8799, + 8800, 8796, 8797, 8792, 8793, 8801, 8802, 8803, 8804, 8805, 8806, 8808, + 8809, 8810, 8811, 8812, 8816, 8813, 8837, 8830, 8831, 8836, 8819, 8818, + 8832, 8835, 8833, 8822, 8821, 8824, 8826, 8827, 8828, 8829, 8825, 8838, + 8814, 8839, 8840, 8841, 8842, 8843, 8844, 8852, 8850, 8848, 8851, 8847, + 8849, 8845, 8846, 8853, 8855, 8856, 8857, 8864, 8859, 8860, 8868, 8869, + 8865, 8867, 8866, 8870, 8872, 8871, 8873, 8884, 8875, 8874, 8883, 8881, + 8876, 8877, 8878, 8880, 8879, 8882, 8888, 8885, 8887, 8886, 8889, 8890, + 8891, 8892, 8895, 8896, 8898, 8899, 8900, 8901, 8903, 8902, 8904, 8911, + 8905, 8906, 8912, 8907, 8908, 8909, 8910, 8913, 8917, 8914, 8915, 8916, + 8918, 8919, 8920, 8921, 8927, 8925, 8923, 8924, 8922, 8926, 8928, 8930, + 8947, 8948, 8931, 8936, 8938, 8932, 8935, 8933, 8934, 8939, 8945, 8946, + 8940, 8943, 8944, 8949, 8955, 8954, 8950, 8952, 8951, 9036, 9037, 8956, + 8957, 8962, 8963, 8960, 8961, 8964, 8958, 8959, 8965, 8968, 8969, 8971, + 8972, 8973, 8977, 8974, 8975, 8976, 8966, 8967, 8978, 8980, 8979, 8981, + 8983, 8984, 8985, 8991, 8990, 8986, 8987, 8988, 9023, 8992, 8993, 8994, + 8995, 8996, 9015, 8998, 8999, 9001, 9000, 9002, 9003, 9019, 9004, 9006, + 9005, 9018, 9008, 9016, 9020, 9011, 9010, 9017, 9012, 9014, 9013, 9021, + 9022, 9024, 9028, 9026, 9027, 9025, 9031, 9030, 9029, 9035, 9032, 9033, + 9034, 9038, 9040, 9039, 9043, 9041, 9058, 9044, 9047, 9049, 9045, 9046, + 9050, 9048, 9051, 9053, 9055, 9056, 9057, 8461, 7957, 7968, 7986, 8052, + 8061, 8077, 8085, 8177, 8170, 8188, 8190, 8280, 8304, 8350, 8379, 8381, + 8395, 8401, 8436, 8409, 8439, 8418, 8427, 8431, 8502, 8631, 8634, 8649, + 8681, 8699, 8716, 8725, 8753, 8751, 8730, 8761, 8790, 8807, 8817, 8937, + 8970, 8953, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 7922, 7917, 7854, 7840, 7901, 7897, 7829, + 7874, 7921, 7862, 7846, 7907, 7896, 7828, 7873, 7860, 7844, 7903, 7893, + 7823, 7870, 7883, 7927, 7919, 7856, 7842, 7905, 7895, 7825, 7872, 7884, + 7928, 7920, 7857, 7843, 7929, 7911, 7912, 7858, 7834, 7900, 7892, 7822, + 7869, 7887, 7930, 7913, 7914, 7859, 7835, 7898, 7899, 7882, 7925, 7908, + 7909, 7849, 7839, 7915, 7916, 7850, 7853, 7851, 7852, 7906, 7891, 7889, + 7890, 7826, 7827, 7865, 7867, 7868, 7866, 7923, 7918, 7855, 7841, 7902, + 7881, 7924, 7910, 7847, 7848, 7837, 7838, 7864, 7863, 7877, 7926, 7886, + 7932, 7836, 7885, 7931, 7878, 7879, 7875, 7876, 7880, 7888, 7830, 7833, + 7832, 7831, 7861, 7845, 7904, 7894, 7824, 7871, 35536, 7937, 7936, 7935, + 7933, 7934, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 7958, 7952, 7970, 8010, 8012, 8013, 8014, 8026, + 8065, 8060, 8070, 8069, 8073, 8090, 8088, 8089, 8091, 8092, 8110, 8096, + 8093, 8094, 8095, 8112, 8113, 8111, 8100, 8099, 8097, 8098, 8103, 8101, + 8102, 8104, 8105, 8106, 8107, 8114, 8115, 8109, 8108, 8133, 8132, 8144, + 8147, 8152, 8156, 8149, 8153, 8154, 8150, 8151, 8155, 8171, 8193, 8200, + 8201, 8202, 8209, 8210, 8213, 8212, 8220, 8231, 8234, 8235, 8236, 8238, + 8240, 8241, 8242, 8248, 8256, 8260, 8279, 8297, 8309, 8314, 8328, 8333, + 8386, 8406, 8429, 8430, 8537, 8557, 8547, 8548, 8556, 8552, 8553, 8554, + 8551, 8550, 8549, 8555, 8559, 8558, 8565, 8566, 8560, 8561, 8562, 8567, + 8563, 8564, 8568, 8569, 8570, 8571, 8572, 8573, 8580, 8574, 8579, 8578, + 8576, 8575, 8577, 8581, 8582, 8587, 8588, 8583, 8584, 8585, 8586, 8614, + 8610, 8589, 8597, 8598, 8596, 8595, 8599, 8590, 8591, 8593, 8594, 8592, + 8611, 8600, 8607, 8609, 8604, 8605, 8608, 8603, 8606, 8602, 8601, 8612, + 8613, 8629, 8655, 8637, 8645, 8646, 8651, 8683, 8690, 8689, 8750, 8731, + 8733, 8752, 8734, 8735, 8737, 8740, 8745, 8746, 8748, 8815, 8834, 8820, + 8823, 8854, 8858, 8861, 8862, 8863, 8894, 8893, 8897, 8929, 8941, 8942, + 8982, 8989, 8997, 9009, 9007, 9042, 9052, 9054, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 9131, + 9132, 9133, 9134, 9135, 9136, 9137, 9138, 9141, 9142, 9139, 9140, 9143, + 9144, 9145, 9146, 9147, 9148, 9149, 9150, 9151, 9152, 9153, 9154, 9155, + 9156, 9157, 9158, 9159, 9160, 9161, 9162, 9163, 9164, 9165, 9166, 9167, + 9168, 9169, 9170, 9171, 9172, 9173, 9174, 9175, 9176, 9177, 9197, 9198, + 9199, 9200, 9201, 9202, 9203, 9204, 9205, 9180, 9181, 9182, 9183, 9184, + 9178, 9179, 9185, 9186, 9187, 9206, 9207, 9208, 9209, 9210, 9211, 9212, + 9213, 9214, 9215, 9188, 9189, 9190, 9191, 9192, 9193, 9194, 9195, 9196, + 9216, 9217, 9218, 9219, 9220, 9221, 9222, 9223, 9224, 9225, 9226, 9227, + 9228, 9229, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 10667, 10668, 10669, 10670, 10665, + 10666, 10662, 10663, 10664, 10671, 10672, 10673, 10678, 10679, 10680, + 10681, 10674, 10675, 10682, 10683, 10676, 10677, 10684, 10685, 10712, + 10713, 10714, 10715, 10716, 10717, 10718, 10719, 10720, 10721, 10702, + 10703, 10700, 10701, 10704, 10705, 10706, 10707, 10708, 10709, 10710, + 10686, 10687, 10694, 10688, 10689, 10690, 10691, 10695, 10692, 10693, + 10696, 10697, 10698, 10699, 10722, 10723, 10724, 10725, 10726, 10727, + 10728, 10729, 10730, 10731, 10732, 10733, 10734, 10735, 10736, 10737, + 10738, 10739, 10740, 10741, 10711, 10781, 10782, 10783, 10784, 10779, + 10780, 10785, 10786, 10787, 10788, 10793, 10789, 10790, 10791, 10792, + 10794, 10795, 10796, 10797, 10798, 10799, 10800, 10801, 10802, 10803, + 10804, 10805, 10806, 10807, 10808, 10809, 10810, 10811, 10812, 10813, + 10814, 10815, 10816, 10819, 10820, 10821, 10822, 10823, 10824, 10825, + 10817, 10818, 10826, 10899, 10900, 10901, 10902, 10903, 10904, 10905, + 10906, 10907, 10908, 10890, 10891, 10892, 10893, 10894, 10895, 10896, + 10888, 10889, 10897, 10898, 10831, 10827, 10828, 10832, 10833, 10829, + 10830, 10834, 10835, 10836, 10837, 10838, 10843, 10844, 10845, 10846, + 10847, 10848, 10839, 10840, 10849, 10841, 10842, 10850, 10851, 10852, + 10853, 10854, 10855, 10856, 10857, 10858, 10859, 10860, 10865, 10861, + 10862, 10866, 10863, 10864, 10867, 10868, 10869, 10870, 10871, 10881, + 10882, 10883, 10884, 10885, 10886, 10887, 10872, 10873, 10874, 10875, + 10876, 10877, 10878, 10879, 10880, 10913, 10914, 10915, 10916, 10917, + 10918, 10919, 10909, 10910, 10911, 10912, 10945, 10946, 10947, 10948, + 10949, 10950, 10941, 10942, 10943, 10944, 10951, 10952, 10920, 10921, + 10924, 10925, 10926, 10927, 10928, 10929, 10930, 10922, 10923, 10931, + 10934, 10935, 10936, 10937, 10932, 10933, 10938, 10939, 10940, 10956, + 10957, 10958, 10959, 10960, 10961, 10962, 10963, 10964, 10965, 10968, + 10969, 10970, 10966, 10967, 10971, 10972, 10973, 10974, 10975, 10976, + 11012, 11010, 11011, 11013, 11014, 11015, 11016, 11017, 11018, 11019, + 11020, 10983, 10977, 10978, 10984, 10985, 10986, 10987, 10988, 10979, + 10980, 10981, 10982, 10989, 10996, 10997, 10998, 10999, 11000, 10990, + 10991, 10992, 10993, 10994, 10995, 11001, 11002, 11007, 11003, 11004, + 11005, 11006, 11008, 11009, 11027, 11028, 11029, 11030, 11031, 11025, + 11026, 11022, 11023, 11024, 11032, 11033, 11036, 11034, 11035, 11037, + 11038, 11039, 11040, 11041, 11042, 11043, 11044, 11069, 11070, 11073, + 11074, 11075, 11076, 11077, 11071, 11072, 11078, 11079, 11080, 11049, + 11050, 11051, 11052, 11053, 11054, 11045, 11046, 11047, 11048, 11055, + 11056, 11061, 11062, 11063, 11057, 11058, 11064, 11059, 11060, 11065, + 11066, 11067, 11068, 11081, 11082, 11083, 11084, 11085, 11088, 11089, + 11090, 11091, 11092, 11086, 11087, 11093, 11094, 11102, 11103, 11104, + 11105, 11098, 11099, 11106, 11107, 11108, 11100, 11101, 11109, 11110, + 11111, 11112, 11113, 11114, 11115, 11116, 11757, 11758, 11759, 11760, + 11761, 11762, 11763, 11764, 11128, 11124, 11125, 11129, 11130, 11131, + 11126, 11127, 11132, 11133, 11135, 11136, 11137, 11140, 11138, 11139, + 11141, 11142, 11143, 11144, 11145, 11146, 11156, 11157, 11164, 11147, + 11148, 11149, 11150, 11151, 11152, 11153, 11154, 11155, 11165, 11166, + 11158, 11159, 11160, 11161, 11162, 11163, 11167, 11168, 11175, 11176, + 11169, 11170, 11177, 11171, 11172, 11178, 11179, 11180, 11173, 11174, + 11181, 11187, 11185, 11186, 11188, 11182, 11183, 11184, 11189, 11190, + 11191, 11192, 11193, 11194, 11195, 11196, 11197, 11198, 11199, 11200, + 11257, 11258, 11259, 11260, 11261, 11262, 11263, 11264, 11265, 11220, + 11221, 11222, 11223, 11224, 11225, 11226, 11227, 11217, 11218, 11219, + 11228, 11245, 11246, 11247, 11248, 11249, 11243, 11244, 11250, 11251, + 11252, 11253, 11237, 11238, 11239, 11229, 11230, 11231, 11232, 11233, + 11234, 11240, 11235, 11236, 11241, 11242, 11254, 11255, 11256, 11268, + 11269, 11270, 11271, 11266, 11267, 11272, 11273, 11274, 11275, 11278, + 11279, 11280, 11281, 11282, 11283, 11284, 11276, 11277, 11285, 11286, + 11287, 11305, 11306, 11307, 11308, 11309, 11310, 11311, 11312, 11313, + 11288, 11289, 11290, 11291, 11294, 11295, 11296, 11297, 11298, 11299, + 11292, 11293, 11300, 11303, 11304, 11301, 11302, 11321, 11322, 11325, + 11326, 11327, 11323, 11324, 11314, 11315, 11316, 11317, 11318, 11319, + 11320, 11328, 11329, 11330, 11331, 11332, 11333, 11334, 11337, 11338, + 11339, 11340, 11341, 11342, 11343, 11344, 11335, 11336, 11345, 11346, + 11353, 11354, 11355, 11347, 11348, 11349, 11350, 11356, 11357, 11358, + 11351, 11352, 11364, 11365, 11368, 11369, 11366, 11367, 11370, 11371, + 11359, 11360, 11361, 11362, 11363, 11372, 11373, 11374, 11379, 11380, + 11381, 11382, 11383, 11384, 11385, 11386, 11387, 11388, 11375, 11376, + 11377, 11378, 11390, 11391, 11394, 11392, 11393, 11395, 11396, 11397, + 11398, 11399, 11400, 11401, 11402, 11765, 11766, 11767, 11768, 11769, + 11770, 11771, 11408, 11406, 11407, 11403, 11404, 11405, 11409, 11410, + 11411, 11412, 11413, 11414, 11415, 11416, 11419, 11420, 11421, 11422, + 11423, 11417, 11418, 11424, 11425, 11426, 11427, 11428, 11429, 11430, + 11431, 11432, 11433, 11434, 11435, 11436, 11441, 11437, 11438, 11442, + 11443, 11444, 11439, 11440, 11445, 11446, 11447, 11453, 11454, 11455, + 11456, 11448, 11449, 11450, 11457, 11458, 11451, 11452, 11459, 11460, + 11464, 11465, 11466, 11467, 11468, 11469, 11461, 11462, 11463, 11470, + 11471, 11472, 11475, 11476, 11477, 11478, 11479, 11473, 11474, 11480, + 11481, 11482, 11483, 11484, 11485, 11486, 11487, 11488, 11489, 11490, + 11499, 11500, 11491, 11492, 11501, 11502, 11503, 11493, 11494, 11495, + 11496, 11497, 11498, 11508, 11504, 11505, 11509, 11510, 11511, 11512, + 11506, 11507, 11513, 11514, 11515, 11525, 11526, 11527, 11528, 11529, + 11530, 11531, 11532, 11533, 11534, 11520, 11521, 11516, 11517, 11518, + 11519, 11522, 11523, 11524, 11539, 11540, 11541, 11542, 11543, 11536, + 11537, 11538, 11544, 11545, 11546, 11573, 11574, 11575, 11576, 11577, + 11578, 11579, 11580, 11581, 11582, 11551, 11552, 11553, 11547, 11548, + 11554, 11555, 11556, 11557, 11558, 11549, 11550, 11561, 11562, 11559, + 11560, 11563, 11564, 11565, 11566, 11567, 11568, 11569, 11570, 11571, + 11572, 11586, 11587, 11588, 11589, 11590, 11591, 11592, 11593, 11594, + 11595, 11596, 11597, 11598, 11599, 11600, 11601, 11583, 11584, 11585, + 11602, 11603, 11612, 11604, 11605, 11606, 11607, 11609, 11610, 11611, + 11613, 11614, 11615, 11616, 11617, 11618, 11619, 11620, 11621, 11622, + 11623, 11624, 11625, 11626, 11627, 11628, 11629, 11630, 11631, 11632, + 11639, 11640, 11633, 11634, 11641, 11642, 11643, 11644, 11635, 11636, + 11637, 11638, 11645, 11646, 11647, 11648, 11653, 11649, 11650, 11654, + 11655, 11656, 11651, 11652, 11657, 11658, 11659, 11660, 11666, 11667, + 11662, 11663, 11668, 11669, 11670, 11671, 11672, 11664, 11665, 11673, + 11674, 11681, 11682, 11683, 11675, 11676, 11684, 11685, 11677, 11678, + 11679, 11680, 11686, 11689, 11690, 11691, 11692, 11687, 11688, 11693, + 11702, 11703, 11704, 11695, 11696, 11697, 11705, 11698, 11699, 11706, + 11700, 11701, 11707, 11708, 11709, 11710, 11711, 11712, 11713, 11714, + 11715, 11728, 11716, 11717, 11718, 11719, 11720, 11721, 11722, 11723, + 11724, 11725, 11726, 11727, 11729, 11730, 11731, 11732, 11752, 11753, + 11754, 11755, 11756, 11733, 11734, 11735, 11736, 11737, 11738, 11739, + 11740, 11741, 11742, 11743, 11744, 11745, 11746, 11747, 11748, 11749, + 11750, 11751, 10745, 10746, 10747, 10748, 10749, 10750, 10742, 10743, + 10744, 10751, 10752, 10756, 10757, 10758, 10759, 10760, 10761, 10762, + 10763, 10764, 10765, 10766, 10767, 10768, 10769, 10770, 10771, 10772, + 10773, 10774, 10775, 10753, 10754, 10755, 11608, 11661, 11097, 11122, + 11119, 11121, 11118, 11389, 10778, 10955, 11123, 11120, 11117, 10777, + 10954, 10776, 10953, 11216, 11021, 11095, 11134, 11096, 11535, 11694, + 11212, 11203, 11207, 11214, 11210, 11204, 11209, 11206, 11213, 11202, + 11208, 11215, 11211, 11205, 11201, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 447, 448, 449, 450, 451, 452, 453, + 454, 455, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 377, + 378, 379, 380, 381, 382, 375, 376, 383, 384, 385, 427, 428, 429, 430, + 431, 432, 433, 434, 435, 425, 426, 393, 389, 390, 394, 395, 396, 391, + 392, 386, 387, 388, 397, 398, 399, 456, 457, 458, 459, 460, 461, 462, + 463, 464, 465, 404, 405, 406, 407, 408, 409, 400, 401, 402, 403, 410, + 411, 412, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, + 478, 479, 480, 481, 482, 483, 484, 485, 417, 418, 419, 420, 421, 422, + 423, 413, 414, 415, 416, 424, 497, 498, 499, 500, 501, 502, 503, 486, + 487, 488, 489, 494, 495, 496, 504, 490, 491, 492, 493, 505, 506, 507, + 508, 509, 512, 513, 514, 515, 510, 511, 516, 517, 518, 519, 522, 523, + 524, 525, 526, 520, 521, 527, 528, 529, 530, 533, 534, 535, 536, 537, + 531, 532, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, + 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, + 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, + 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, + 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 609, 610, 602, 603, + 604, 611, 612, 613, 614, 605, 606, 615, 607, 608, 620, 621, 622, 623, + 624, 616, 617, 618, 619, 625, 626, 627, 653, 654, 655, 656, 657, 658, + 659, 651, 652, 660, 661, 673, 674, 675, 676, 677, 678, 679, 680, 681, + 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, + 696, 697, 698, 699, 700, 701, 702, 664, 665, 666, 667, 668, 669, 670, + 662, 663, 671, 672, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, + 642, 643, 644, 645, 646, 647, 648, 649, 650, 640, 641, 632, 633, 634, + 635, 628, 629, 636, 637, 638, 639, 630, 631, 715, 716, 717, 718, 719, + 720, 721, 722, 723, 713, 714, 807, 808, 809, 810, 811, 812, 813, 814, + 815, 816, 726, 727, 728, 729, 730, 731, 732, 733, 734, 724, 725, 753, + 754, 750, 751, 752, 755, 756, 757, 746, 747, 748, 749, 758, 759, 760, + 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 737, 738, 739, 740, + 741, 742, 743, 744, 745, 735, 736, 765, 766, 767, 768, 761, 762, 769, + 770, 771, 763, 764, 772, 798, 796, 797, 799, 800, 801, 802, 803, 804, + 805, 806, 779, 775, 776, 780, 773, 774, 781, 782, 777, 778, 783, 784, + 785, 787, 788, 789, 786, 790, 791, 792, 793, 794, 795, 858, 859, 860, + 861, 862, 863, 864, 865, 866, 867, 827, 828, 829, 830, 831, 832, 833, + 834, 835, 836, 837, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, + 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, + 892, 893, 894, 895, 896, 897, 838, 839, 842, 843, 844, 845, 846, 847, + 840, 841, 848, 849, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, + 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, + 922, 923, 924, 925, 926, 927, 850, 851, 852, 853, 854, 855, 856, 857, + 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, + 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, + 957, 928, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 14098, 14088, 14087, 14084, + 14083, 14069, 14082, 14081, 14086, 14085, 14091, 14076, 14075, 14072, + 14071, 14096, 14078, 14077, 14074, 14073, 14070, 14090, 14089, 14080, + 14079, 14093, 14097, 14094, 14092, 14095, 14101, 14108, 14109, 14104, + 14105, 14110, 14111, 14102, 14106, 14107, 14103, 14112, 14068, 14067, + 14065, 14099, 14066, 14100, 14119, 14121, 14118, 14117, 14114, 14113, + 14116, 14115, 14122, 14120, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 2892, 2858, 2917, 2918, 2888, 2925, 2936, 2907, 2924, 2919, + 2920, 2871, 2937, 2894, 2873, 2878, 2885, 2931, 2903, 2861, 2897, 2932, + 2893, 2868, 2869, 2901, 2875, 2916, 2857, 2915, 2884, 2908, 2939, 2856, + 2902, 2933, 2887, 2886, 2882, 2859, 2914, 2891, 2921, 2874, 2911, 2922, + 2938, 2866, 2928, 2935, 2876, 2862, 2890, 2864, 2883, 2926, 2867, 2941, + 2943, 2863, 2929, 2879, 2923, 2900, 2927, 2905, 2912, 2896, 2940, 2895, + 2877, 2909, 2880, 2906, 2934, 2930, 2913, 2898, 2870, 2904, 2860, 2899, + 2942, 2865, 2910, 2889, 2881, 2976, 2993, 2991, 2990, 2954, 2960, 2949, + 2997, 2959, 2951, 2984, 2996, 2953, 2980, 2981, 2944, 2986, 2994, 2988, + 2989, 2966, 2963, 2979, 2947, 2945, 2946, 2952, 2985, 3001, 2974, 2971, + 2998, 2987, 2995, 2965, 2969, 2970, 2967, 2992, 2962, 2968, 2978, 2983, + 2964, 2999, 2961, 3000, 2948, 2958, 2957, 2955, 2972, 2977, 2956, 2950, + 2975, 3053, 3073, 3095, 3091, 3052, 3041, 3054, 3002, 3030, 3004, 3074, + 3070, 3027, 3075, 3045, 3024, 3007, 3003, 3009, 3094, 3072, 3035, 3065, + 3033, 3096, 3014, 3015, 3079, 3046, 3084, 3061, 3088, 3083, 3048, 3090, + 3039, 3021, 3071, 3049, 3017, 3032, 3037, 3068, 3085, 3051, 3099, 3042, + 3067, 3064, 3098, 3089, 3029, 3100, 3057, 3016, 3087, 3062, 3059, 3011, + 3047, 3023, 3034, 3019, 3013, 3058, 3056, 3092, 3050, 3066, 3069, 3012, + 3063, 3043, 3020, 3097, 3044, 3081, 3078, 3031, 3022, 3026, 3008, 3028, + 3010, 3025, 3093, 3060, 3038, 3036, 3082, 3006, 3005, 3055, 3040, 3018, + 3076, 3077, 3086, 3128, 3212, 3161, 3135, 3162, 3121, 3160, 3166, 3148, + 3175, 3211, 3157, 3191, 3158, 3105, 3204, 3189, 3164, 3196, 3109, 3213, + 3111, 3198, 3133, 3143, 3124, 3130, 3200, 3217, 3159, 3106, 3154, 3206, + 3104, 3156, 3101, 3144, 3138, 3116, 3145, 3140, 3139, 3181, 3137, 3134, + 3122, 3167, 3126, 3114, 3173, 3202, 3201, 3214, 3107, 3188, 3205, 3153, + 3132, 3168, 3108, 3184, 3179, 3174, 3119, 3147, 3136, 3151, 3215, 3183, + 3216, 3146, 3170, 3195, 3112, 3150, 3155, 3207, 3129, 3113, 3169, 3203, + 3125, 3149, 3117, 3152, 3165, 3163, 3103, 3110, 3185, 3209, 3208, 3176, + 3187, 3118, 3131, 3123, 3197, 3142, 3193, 3190, 3115, 3178, 3194, 3171, + 3180, 3177, 3192, 3182, 3141, 3120, 3186, 3210, 3172, 3127, 3199, 3102, + 3271, 3349, 3258, 3245, 3356, 3248, 3312, 3338, 3328, 3298, 3274, 3323, + 3343, 3284, 3238, 3359, 3331, 3276, 3313, 3348, 3240, 3250, 3300, 3290, + 3255, 3287, 3291, 3299, 3330, 3297, 3316, 3339, 3279, 3262, 3232, 3286, + 3294, 3256, 3249, 3277, 3273, 3340, 3332, 3326, 3270, 3278, 3364, 3231, + 3353, 3360, 3320, 3352, 3235, 3337, 3346, 3354, 3357, 3244, 3322, 3342, + 3229, 3292, 3333, 3261, 3259, 3305, 3309, 3228, 3351, 3239, 3372, 3303, + 3365, 3260, 3272, 3318, 3368, 3247, 3221, 3227, 3289, 3237, 3254, 3285, + 3233, 3223, 3301, 3314, 3362, 3275, 3304, 3306, 3321, 3264, 3222, 3308, + 3265, 3230, 3220, 3268, 3324, 3288, 3241, 3319, 3302, 3361, 3280, 3310, + 3219, 3226, 3295, 3373, 3350, 3375, 3374, 3246, 3311, 3341, 3344, 3269, + 3336, 3363, 3282, 3369, 3366, 3367, 3371, 3370, 3236, 3315, 3296, 3325, + 3358, 3225, 3355, 3252, 3263, 3329, 3327, 3283, 3293, 3334, 3335, 3218, + 3307, 3317, 3251, 3243, 3266, 3253, 3257, 3345, 3242, 3267, 3347, 3224, + 3234, 3380, 3429, 3382, 3427, 3407, 3420, 3401, 3384, 3410, 3409, 3389, + 3419, 3399, 3395, 3386, 3417, 3412, 3418, 3415, 3378, 3377, 3397, 3396, + 3394, 3422, 3414, 3423, 3398, 3403, 3400, 3424, 3404, 3411, 3402, 3406, + 3376, 3392, 3393, 3413, 3405, 3428, 3425, 3385, 3383, 3381, 3387, 3408, + 3390, 3391, 3388, 3421, 3379, 3416, 3426, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 22919, 22911, 22932, 22909, 22933, 22920, 22935, + 22915, 22906, 22923, 22921, 22929, 22905, 22913, 22908, 22910, 22916, + 22914, 22912, 22925, 22926, 22917, 22931, 22934, 22930, 22907, 22928, + 22927, 22922, 22924, 22918, 35536, 22944, 22946, 22943, 22942, 22939, + 22938, 22941, 22940, 22947, 22945, 35536, 35536, 35536, 35536, 22937, + 22936, 30246, 30243, 30244, 30245, 30198, 30195, 30196, 30197, 30250, + 30247, 30248, 30249, 30238, 30235, 30236, 30237, 30242, 30239, 30240, + 30241, 30234, 30231, 30232, 30233, 30194, 30191, 30192, 30193, 30226, + 30223, 30224, 30225, 30199, 30204, 30215, 30216, 30227, 30230, 30228, + 30229, 30222, 30219, 30220, 30221, 30210, 30207, 30208, 30209, 30261, + 30260, 30259, 30211, 30218, 30268, 30266, 30263, 30213, 30262, 30264, + 30206, 30214, 30203, 30205, 30202, 30253, 30257, 30265, 30212, 30217, + 30255, 30252, 30258, 30201, 30251, 30267, 30200, 30256, 30254, 30269, + 35536, 30276, 30278, 30275, 30274, 30271, 30270, 30273, 30272, 30279, + 30277, 35536, 35536, 35536, 35536, 35536, 35536, 3490, 3495, 3511, 3513, + 3503, 3501, 3493, 3487, 3494, 3507, 3502, 3498, 3509, 3492, 3488, 3510, + 3497, 3508, 3512, 3506, 3500, 3514, 3499, 3515, 3504, 3505, 3496, 3491, + 3489, 3516, 35536, 35536, 3483, 3485, 3486, 3484, 3482, 3517, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 25301, + 25302, 25307, 25308, 25295, 25296, 25311, 25312, 25303, 25304, 25293, + 25294, 25313, 25314, 25297, 25298, 25309, 25310, 25315, 25316, 25305, + 25306, 25299, 25300, 25317, 25318, 25291, 25292, 25237, 25228, 25234, + 25225, 25230, 25236, 25229, 25233, 25239, 25223, 25235, 25221, 25226, + 25224, 25232, 25227, 25231, 25240, 25238, 25222, 25245, 25244, 25242, + 25241, 25243, 25247, 25246, 25277, 25278, 25256, 25276, 25274, 25282, + 25284, 25283, 25285, 25275, 25267, 25280, 25265, 25287, 25260, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 25325, + 25327, 25324, 25323, 25320, 25319, 25322, 25321, 25328, 25326, 35536, + 25252, 25249, 25251, 25254, 25248, 25250, 25253, 35536, 25279, 25286, + 25264, 25272, 25288, 25263, 25270, 25281, 25269, 25290, 25271, 25266, + 25273, 25289, 25268, 25262, 25255, 25258, 25259, 25261, 25257, 35536, + 35536, 35536, 35536, 35536, 25209, 25216, 25208, 25207, 25217, 25205, + 25202, 25220, 25212, 25210, 25218, 25204, 25203, 25213, 25219, 25215, + 25211, 25206, 25214, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 17208, + 17205, 17210, 17204, 17193, 17192, 17189, 17188, 17181, 17187, 17186, + 17191, 17190, 17182, 17178, 17177, 17174, 17173, 17180, 17179, 17176, + 17175, 17183, 17195, 17194, 17185, 17184, 17200, 17203, 17201, 17199, + 17202, 17197, 17196, 17198, 17211, 17217, 17214, 17215, 17216, 17212, + 17218, 17213, 17209, 17207, 17206, 17220, 17219, 17227, 17229, 17226, + 17225, 17222, 17221, 17224, 17223, 17230, 17228, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 21582, 21586, 21589, 21590, 21560, + 21592, 21568, 21583, 21587, 21578, 21577, 21579, 21567, 21559, 21580, + 21576, 21573, 21574, 21588, 21570, 21581, 21584, 21566, 21564, 21591, + 21575, 21572, 21562, 21585, 21571, 21561, 21569, 21640, 21644, 21647, + 21648, 21618, 21650, 21626, 21641, 21645, 21636, 21635, 21637, 21625, + 21617, 21638, 21634, 21631, 21632, 21646, 21628, 21639, 21642, 21624, + 21622, 21649, 21633, 21630, 21620, 21643, 21629, 21619, 21627, 21604, + 21598, 21596, 21594, 21601, 21600, 21603, 21602, 21606, 21605, 21609, + 21611, 21608, 21607, 21612, 21613, 21615, 21616, 21610, 21614, 21599, + 21597, 21595, 21593, 21653, 21651, 21652, 35536, 35536, 35536, 35536, + 35536, 3702, 3719, 3704, 3708, 3721, 3709, 3716, 3726, 3705, 3717, 3722, + 3715, 3711, 3710, 3712, 3723, 3724, 3706, 3707, 3714, 3713, 3718, 3725, + 3720, 3703, 35536, 35536, 3727, 3744, 3729, 3733, 3746, 3734, 3741, 3751, + 3730, 3742, 3747, 3740, 3736, 3735, 3737, 3748, 3749, 3731, 3732, 3739, + 3738, 3743, 3750, 3745, 3728, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 22201, 22127, + 22189, 22200, 22205, 22204, 22124, 22206, 22181, 22180, 22178, 22135, + 22184, 22185, 22177, 22134, 22143, 22151, 22187, 22123, 22148, 22147, + 22142, 22141, 22140, 22139, 22165, 22133, 22164, 22132, 22207, 22138, + 22188, 22199, 22198, 22146, 22145, 22122, 22203, 22209, 22137, 22136, + 22174, 22130, 22150, 22149, 22173, 22129, 22182, 22186, 22158, 22161, + 22162, 22196, 22194, 22175, 22131, 22183, 22163, 22197, 22195, 22193, + 22191, 22121, 22192, 22190, 22208, 22125, 22202, 22126, 22160, 22128, + 22179, 22176, 22159, 35536, 35536, 35536, 35536, 22213, 22144, 22212, + 22211, 22210, 22218, 22224, 22223, 22219, 22220, 22245, 22249, 22266, + 22265, 22227, 22230, 22231, 22247, 22234, 22235, 22236, 22237, 22238, + 22241, 22243, 22244, 22240, 22251, 22252, 22253, 22254, 22259, 22255, + 22256, 22260, 22262, 22221, 22222, 22229, 22264, 22228, 22263, 22225, + 22233, 22226, 22250, 22267, 22268, 22257, 22261, 22248, 22246, 22269, + 22242, 22232, 22239, 22258, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 22217, 22215, 22216, 22214, 22166, 22167, 22168, 22169, 22170, + 22171, 22172, 22152, 22153, 22154, 22155, 22156, 22157, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 31163, 24157, 24377, 24376, 17172, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 33457, 33456, + 6583, 6584, 33978, 33979, 33980, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 30280, 30281, 30282, 30283, 30284, 30285, + 30286, 30287, 30288, 30289, 30290, 30291, 30292, 30293, 30294, 30295, + 30296, 30297, 30298, 30299, 30300, 30301, 30302, 30303, 30304, 30305, + 30306, 30307, 30308, 30309, 30310, 30311, 30312, 30313, 30314, 30315, + 30316, 30317, 30318, 30319, 30320, 30321, 30322, 30323, 30324, 30325, + 30326, 30327, 30328, 30329, 30330, 30331, 30332, 30333, 30334, 30335, + 30336, 30337, 30338, 30339, 30340, 30341, 30342, 30343, 30344, 30345, + 30346, 30347, 30348, 30349, 30350, 30351, 30352, 30353, 30354, 30355, + 30356, 30357, 30358, 30359, 30360, 30361, 30362, 30363, 30364, 30365, + 30366, 30367, 30368, 30369, 30370, 30371, 30372, 30373, 30374, 30375, + 30376, 30377, 30378, 30379, 30380, 30381, 30382, 30383, 30384, 30385, + 30386, 30387, 30388, 30389, 30390, 30391, 30392, 30393, 30394, 30395, + 30396, 30397, 30398, 30399, 30400, 30401, 30402, 30403, 30404, 30405, + 30406, 30407, 30408, 30409, 30410, 30411, 30412, 30413, 30414, 30415, + 30416, 30417, 30418, 30419, 30420, 30421, 30422, 30423, 30424, 30425, + 30426, 30427, 30428, 30429, 30430, 30431, 30432, 30433, 30434, 30435, + 30436, 30437, 30438, 30439, 30440, 30441, 30442, 30443, 30444, 30445, + 30446, 30447, 30448, 30449, 30450, 30451, 30452, 30453, 30454, 30455, + 30456, 30457, 30458, 30459, 30460, 30461, 30462, 30463, 30464, 30465, + 30466, 30467, 30468, 30469, 30470, 30471, 30472, 30473, 30474, 30475, + 30476, 30477, 30478, 30479, 30480, 30481, 30482, 30483, 30484, 30485, + 30486, 30487, 30488, 30489, 30490, 30491, 30492, 30493, 30494, 30495, + 30496, 30497, 30498, 30499, 30500, 30501, 30502, 30503, 30504, 30505, + 30506, 30507, 30508, 30509, 30510, 30511, 30512, 30513, 30514, 30515, + 30516, 30517, 30518, 30519, 30520, 30521, 30522, 30523, 30524, 30525, + 30526, 30527, 30528, 30529, 30530, 30531, 30532, 30533, 30534, 30535, + 30536, 30537, 30538, 30539, 30540, 30541, 30542, 30543, 30544, 30545, + 30546, 30547, 30548, 30549, 30550, 30551, 30552, 30553, 30554, 30555, + 30556, 30557, 30558, 30559, 30560, 30561, 30562, 30563, 30564, 30565, + 30566, 30567, 30568, 30569, 30570, 30571, 30572, 30573, 30574, 30575, + 30576, 30577, 30578, 30579, 30580, 30581, 30582, 30583, 30584, 30585, + 30586, 30587, 30588, 30589, 30590, 30591, 30592, 30593, 30594, 30595, + 30596, 30597, 30598, 30599, 30600, 30601, 30602, 30603, 30604, 30605, + 30606, 30607, 30608, 30609, 30610, 30611, 30612, 30613, 30614, 30615, + 30616, 30617, 30618, 30619, 30620, 30621, 30622, 30623, 30624, 30625, + 30626, 30627, 30628, 30629, 30630, 30631, 30632, 30633, 30634, 30635, + 30636, 30637, 30638, 30639, 30640, 30641, 30642, 30643, 30644, 30645, + 30646, 30647, 30648, 30649, 30650, 30651, 30652, 30653, 30654, 30655, + 30656, 30657, 30658, 30659, 30660, 30661, 30662, 30663, 30664, 30665, + 30666, 30667, 30668, 30669, 30670, 30671, 30672, 30673, 30674, 30675, + 30676, 30677, 30678, 30679, 30680, 30681, 30682, 30683, 30684, 30685, + 30686, 30687, 30688, 30689, 30690, 30691, 30692, 30693, 30694, 30695, + 30696, 30697, 30698, 30699, 30700, 30701, 30702, 30703, 30704, 30705, + 30706, 30707, 30708, 30709, 30710, 30711, 30712, 30713, 30714, 30715, + 30716, 30717, 30718, 30719, 30720, 30721, 30722, 30723, 30724, 30725, + 30726, 30727, 30728, 30729, 30730, 30731, 30732, 30733, 30734, 30735, + 30736, 30737, 30738, 30739, 30740, 30741, 30742, 30743, 30744, 30745, + 30746, 30747, 30748, 30749, 30750, 30751, 30752, 30753, 30754, 30755, + 30756, 30757, 30758, 30759, 30760, 30761, 30762, 30763, 30764, 30765, + 30766, 30767, 30768, 30769, 30770, 30771, 30772, 30773, 30774, 30775, + 30776, 30777, 30778, 30779, 30780, 30781, 30782, 30783, 30784, 30785, + 30786, 30787, 30788, 30789, 30790, 30791, 30792, 30793, 30794, 30795, + 30796, 30797, 30798, 30799, 30800, 30801, 30802, 30803, 30804, 30805, + 30806, 30807, 30808, 30809, 30810, 30811, 30812, 30813, 30814, 30815, + 30816, 30817, 30818, 30819, 30820, 30821, 30822, 30823, 30824, 30825, + 30826, 30827, 30828, 30829, 30830, 30831, 30832, 30833, 30834, 30835, + 30836, 30837, 30838, 30839, 30840, 30841, 30842, 30843, 30844, 30845, + 30846, 30847, 30848, 30849, 30850, 30851, 30852, 30853, 30854, 30855, + 30856, 30857, 30858, 30859, 30860, 30861, 30862, 30863, 30864, 30865, + 30866, 30867, 30868, 30869, 30870, 30871, 30872, 30873, 30874, 30875, + 30876, 30877, 30878, 30879, 30880, 30881, 30882, 30883, 30884, 30885, + 30886, 30887, 30888, 30889, 30890, 30891, 30892, 30893, 30894, 30895, + 30896, 30897, 30898, 30899, 30900, 30901, 30902, 30903, 30904, 30905, + 30906, 30907, 30908, 30909, 30910, 30911, 30912, 30913, 30914, 30915, + 30916, 30917, 30918, 30919, 30920, 30921, 30922, 30923, 30924, 30925, + 30926, 30927, 30928, 30929, 30930, 30931, 30932, 30933, 30934, 30935, + 30936, 30937, 30938, 30939, 30940, 30941, 30942, 30943, 30944, 30945, + 30946, 30947, 30948, 30949, 30950, 30951, 30952, 30953, 30954, 30955, + 30956, 30957, 30958, 30959, 30960, 30961, 30962, 30963, 30964, 30965, + 30966, 30967, 30968, 30969, 30970, 30971, 30972, 30973, 30974, 30975, + 30976, 30977, 30978, 30979, 30980, 30981, 30982, 30983, 30984, 30985, + 30986, 30987, 30988, 30989, 30990, 30991, 30992, 30993, 30994, 30995, + 30996, 30997, 30998, 30999, 31000, 31001, 31002, 31003, 31004, 31005, + 31006, 31007, 31008, 31009, 31010, 31011, 31012, 31013, 31014, 31015, + 31016, 31017, 31018, 31019, 31020, 31021, 31022, 31023, 31024, 31025, + 31026, 31027, 31028, 31029, 31030, 31031, 31032, 31033, 31034, 31035, + 31036, 31037, 31038, 31039, 31040, 31041, 31042, 31043, 31044, 31045, + 31046, 31047, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 31048, 31049, 31050, 31051, 31052, 31053, 31054, 31055, 31056, 31057, + 31058, 31059, 31060, 31061, 31062, 31063, 31064, 31065, 31066, 31067, + 31068, 31069, 31070, 31071, 31072, 31073, 31074, 31075, 31076, 31077, + 31078, 31079, 31080, 31081, 31082, 31083, 31084, 31085, 31086, 31087, + 31088, 31089, 31090, 31091, 31092, 31093, 31094, 31095, 31096, 31097, + 31098, 31099, 31100, 31101, 31102, 31103, 31104, 31105, 31106, 31107, + 31108, 31109, 31110, 31111, 31112, 31113, 31114, 31115, 31116, 31117, + 31118, 31119, 31120, 31121, 31122, 31123, 31124, 31125, 31126, 31127, + 31128, 31129, 31130, 31131, 31132, 31133, 31134, 31135, 31136, 31137, + 31138, 31139, 31140, 31141, 31142, 31143, 31144, 31145, 31146, 31147, + 31148, 31149, 31150, 31151, 31152, 31153, 31154, 31155, 31156, 31157, + 31158, 31159, 31160, 31161, 31162, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 16490, 16491, + 16492, 16493, 35536, 16494, 16495, 16483, 16484, 16485, 16486, 16487, + 35536, 16488, 16489, 35536, 16471, 15443, 15082, 15083, 15084, 15081, + 15363, 15364, 15365, 15366, 15355, 15356, 15357, 15358, 15359, 15350, + 15351, 15352, 15353, 15354, 15360, 15361, 15362, 15121, 15125, 15126, + 15127, 15128, 15129, 15130, 15131, 15132, 15122, 15123, 15124, 15137, + 15138, 15139, 15140, 15141, 15142, 15143, 15144, 15151, 15152, 15153, + 15154, 15155, 15156, 15157, 15145, 15146, 15147, 15148, 15149, 15150, + 15134, 15135, 15136, 15133, 15246, 15247, 15248, 15249, 15250, 15251, + 15252, 15253, 15262, 15263, 15264, 15265, 15266, 15267, 15254, 15255, + 15256, 15257, 15258, 15259, 15260, 15261, 15268, 15269, 15270, 15271, + 15272, 15273, 15274, 15275, 15276, 15277, 15278, 15279, 15308, 15309, + 15310, 15311, 15301, 15302, 15303, 15304, 15305, 15306, 15307, 15297, + 15298, 15299, 15300, 15296, 15280, 15281, 15282, 15283, 15284, 15285, + 15286, 15287, 15288, 15290, 15291, 15292, 15293, 15294, 15295, 15289, + 15200, 15201, 15202, 15203, 15204, 15205, 15206, 15207, 15208, 15193, + 15194, 15195, 15196, 15197, 15198, 15199, 15192, 15214, 15215, 15216, + 15186, 15187, 15188, 15189, 15190, 15191, 15185, 15209, 15210, 15211, + 15212, 15213, 15093, 15096, 15097, 15098, 15099, 15100, 15101, 15102, + 15103, 15094, 15095, 15114, 15115, 15116, 15117, 15118, 15119, 15120, + 15104, 15105, 15106, 15107, 15108, 15109, 15110, 15111, 15112, 15113, + 15085, 15086, 15087, 15088, 15089, 15090, 15091, 15092, 15167, 15168, + 15169, 15170, 15171, 15172, 15173, 15174, 15175, 15176, 15177, 15178, + 15179, 15180, 15181, 15182, 15183, 15184, 15159, 15160, 15158, 15161, + 15162, 15163, 15164, 15165, 15166, 15334, 15335, 15336, 15337, 15338, + 15333, 15345, 15346, 15347, 15348, 15339, 15340, 15341, 15342, 15343, + 15344, 15238, 15239, 15240, 15241, 15231, 15232, 15233, 15234, 15235, + 15236, 15237, 15225, 15226, 15227, 15228, 15229, 15230, 15242, 15243, + 15244, 15245, 15219, 15220, 15221, 15222, 15223, 15224, 15312, 15313, + 15314, 15315, 15316, 15317, 15318, 15319, 15320, 15321, 15329, 15330, + 15331, 15332, 15322, 15323, 15324, 15325, 15326, 15327, 15328, 15217, + 15218, 15442, 16469, 16468, 16470, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 15453, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 15446, 15445, 15447, 35536, 35536, 16518, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 16524, 16523, 16525, 16529, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 10635, 10633, 10582, 10615, + 10542, 10555, 10559, 10640, 10536, 10623, 10544, 10586, 10585, 10537, + 10543, 10557, 10589, 10618, 10611, 10538, 10558, 10612, 10636, 10562, + 10590, 10563, 10568, 10546, 10591, 10564, 10569, 10549, 10592, 10566, + 10571, 10547, 10548, 10600, 10601, 10567, 10572, 10553, 10604, 10565, + 10570, 10550, 10593, 10554, 10551, 10552, 10598, 10599, 10596, 10597, + 10617, 10616, 10625, 10631, 10628, 10603, 10602, 10556, 10545, 10594, + 10595, 10534, 10609, 10579, 10577, 10535, 10637, 10539, 10638, 10614, + 10622, 10540, 10606, 10587, 10605, 10560, 10639, 10619, 10541, 10634, + 10620, 10561, 10588, 10621, 10613, 10578, 10581, 10580, 10630, 10626, + 10632, 10629, 10627, 10576, 10575, 10574, 10573, 10584, 10583, 10607, + 10610, 10608, 10624, 35536, 35536, 35536, 35536, 35536, 10519, 10532, + 10531, 10526, 10533, 10513, 10506, 10505, 10502, 10504, 10507, 10508, + 10503, 35536, 35536, 35536, 10515, 10511, 10514, 10509, 10518, 10517, + 10510, 10516, 10512, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 10520, 10524, 10527, 10522, 10530, 10529, 10523, 10528, 10525, 10521, + 35536, 35536, 10644, 10641, 10642, 10643, 27160, 27159, 27161, 27162, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 32371, 26351, 18859, 26348, 10409, 19867, 26319, 19925, + 967, 15578, 33426, 18817, 22276, 26304, 18852, 26341, 24231, 25897, + 26089, 15574, 33400, 19807, 19806, 19805, 19804, 19802, 19803, 4587, + 4588, 4596, 4610, 4480, 4479, 26890, 26898, 26891, 26902, 26895, 26899, + 26892, 26904, 26897, 26901, 26894, 26903, 26896, 26900, 26893, 32410, + 32378, 32380, 32465, 32427, 32423, 32459, 32429, 19951, 19898, 19918, + 19953, 19901, 19943, 19945, 19924, 28433, 28434, 24959, 9958, 9694, 9693, + 28445, 28446, 18865, 26328, 12612, 12613, 352, 351, 358, 357, 360, 359, + 355, 356, 353, 354, 18855, 32363, 26344, 10415, 32037, 32033, 32041, + 4442, 4440, 4446, 18848, 32358, 26338, 10411, 23000, 18858, 32366, 26347, + 10418, 11778, 11779, 3944, 3947, 3946, 3945, 3978, 18871, 32356, 26334, + 10421, 18872, 32357, 26335, 10422, 18861, 32360, 26350, 10413, 28678, + 28679, 28677, 28676, 28870, 28872, 28871, 28869, 33410, 15557, 33823, + 33824, 32283, 28510, 28509, 28508, 28463, 15953, 18731, 15954, 33415, + 15555, 18866, 26329, 18867, 26330, 12597, 18857, 32365, 26346, 10417, + 15577, 33425, 33403, 18860, 26349, 18854, 26343, 18856, 26345, 18765, + 26262, 32419, 32455, 32418, 32454, 19887, 19907, 19886, 19906, 19884, + 19904, 19885, 19905, 32422, 32458, 19897, 19917, 32420, 32456, 19896, + 19916, 32413, 32449, 19891, 19911, 32416, 32452, 19894, 19914, 32417, + 32453, 19895, 19915, 32412, 32448, 19890, 19910, 32414, 32450, 19892, + 19912, 32415, 32451, 19893, 19913, 32421, 32457, 19888, 19908, 25137, + 25138, 25139, 25140, 25141, 25142, 25143, 25144, 25145, 25146, 25147, + 25148, 25149, 25150, 25151, 25152, 25153, 25154, 25155, 25156, 25157, + 25158, 25159, 25160, 25161, 25162, 25173, 25175, 25172, 25171, 25168, + 25167, 25170, 25169, 25176, 25174, 28214, 12614, 24141, 35536, 35536, + 35536, 4239, 4183, 4054, 4269, 4140, 4081, 4240, 4121, 4184, 4230, 4156, + 4215, 4097, 4112, 4200, 4066, 4273, 4141, 4171, 4082, 4241, 4122, 4185, + 4055, 4236, 4164, 4223, 4105, 4262, 4118, 4208, 4074, 4149, 4177, 4090, + 4248, 4130, 4193, 4060, 4231, 4157, 4216, 4098, 4255, 4113, 4201, 4067, + 4274, 4142, 4172, 4083, 4242, 4123, 4186, 4168, 4227, 4109, 4266, 4137, + 4212, 4078, 4281, 4153, 4180, 4094, 4252, 4134, 4197, 4063, 4161, 4220, + 4102, 4259, 4205, 4071, 4278, 4146, 4087, 4245, 4127, 4190, 4237, 4165, + 4224, 4106, 4263, 4119, 4209, 4075, 4270, 4150, 4178, 4091, 4249, 4131, + 4194, 4061, 4232, 4158, 4217, 4099, 4256, 4114, 4202, 4068, 4275, 4143, + 4173, 4084, 4243, 4124, 4187, 4056, 4170, 4229, 4111, 4268, 4139, 4214, + 4080, 4283, 4155, 4182, 4096, 4254, 4136, 4199, 4065, 4235, 4163, 4222, + 4104, 4261, 4117, 4207, 4073, 4280, 4148, 4176, 4089, 4247, 4129, 4192, + 4059, 4167, 4226, 4108, 4265, 4211, 4077, 4272, 4152, 4093, 4251, 4133, + 4196, 4233, 4160, 4219, 4101, 4258, 4115, 4204, 4070, 4277, 4145, 4174, + 4086, 4244, 4126, 4189, 4057, 4169, 4228, 4110, 4267, 4138, 4213, 4079, + 4282, 4154, 4181, 4095, 4253, 4135, 4198, 4064, 4234, 4162, 4221, 4103, + 4260, 4116, 4206, 4072, 4279, 4147, 4175, 4088, 4246, 4128, 4191, 4058, + 4238, 4166, 4225, 4107, 4264, 4120, 4210, 4076, 4271, 4151, 4179, 4092, + 4250, 4132, 4195, 4062, 4159, 4218, 4100, 4257, 4203, 4069, 4276, 4144, + 4085, 4125, 4188, 32045, 4449, 32042, 4447, 32043, 4448, 32038, 4443, + 32039, 4444, 32032, 4436, 4437, 4438, 4439, 22884, 32034, 32035, 10412, + 18849, 28184, 32361, 10414, 12483, 12484, 12485, 26257, 19858, 12486, + 32385, 19860, 14933, 33885, 32056, 12770, 4481, 4478, 18769, 26266, + 18768, 26265, 15554, 18766, 26263, 15553, 19861, 32388, 33416, 4606, + 4604, 4605, 4603, 17403, 17402, 17407, 17401, 17379, 17358, 17359, 17399, + 17365, 17383, 17406, 17381, 17404, 17405, 17387, 17384, 17364, 17363, + 17366, 17382, 17367, 17355, 17376, 17400, 17356, 17357, 17354, 17380, + 17385, 17371, 17362, 17386, 17388, 17361, 17378, 17370, 17368, 17369, + 17360, 17408, 17377, 17372, 17375, 17373, 17374, 17396, 17394, 17395, + 17398, 17393, 17390, 17397, 17392, 17391, 17389, 26905, 26937, 26906, + 26953, 26922, 26938, 26907, 26961, 26930, 26946, 26915, 26954, 26923, + 26939, 26908, 26965, 26934, 26950, 26919, 26958, 26927, 26943, 26912, + 26962, 26931, 26947, 26916, 26955, 26924, 26940, 26909, 26967, 26936, + 26952, 26921, 26960, 26929, 26945, 26914, 26964, 26933, 26949, 26918, + 26957, 26926, 26942, 26911, 26966, 26935, 26951, 26920, 26959, 26928, + 26944, 26913, 26963, 26932, 26948, 26917, 26956, 26925, 26941, 26910, + 32405, 32377, 32379, 32444, 32426, 32424, 32425, 32428, 19950, 19948, + 19949, 19952, 19903, 19942, 19944, 19938, 26267, 26307, 18820, 18770, + 19863, 19958, 32468, 32390, 18771, 18821, 26308, 26268, 32391, 32469, + 19959, 19864, 15579, 16781, 24646, 3984, 35536, 35536, 35536, 35536, + 35536, 35536, 12650, 25186, 32117, 1057, 6577, 28866, 15076, 15975, + 12602, 22119, 25522, 33452, 11772, 15974, 12478, 26026, 31511, 21764, + 12628, 2574, 3597, 369, 19085, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 12869, + 12866, 12858, 12864, 12870, 12856, 12862, 12859, 12865, 12861, 12857, + 12867, 12863, 12868, 12860, 12871, 21676, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35352, 35357, 35391, 35354, 35359, 35387, 35381, 35375, 35398, + 35377, 35371, 35395, 35353, 35358, 35392, 35355, 35360, 35388, 35382, + 35376, 35399, 35378, 35372, 35396, 35393, 35379, 35386, 35373, 35374, + 35397, 35380, 35356, 35402, 35367, 35384, 35394, 35405, 35404, 35370, + 35403, 35364, 35363, 35401, 35385, 35383, 35362, 35536, 35536, 35407, + 35408, 35409, 35400, 35350, 35365, 35368, 35369, 35347, 35348, 35366, + 35389, 35390, 35351, 35406, 35349, 35361, 35346, 35528, 35529, 35526, + 35527, 35530, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35426, 35427, 35443, 35415, 35424, 35523, 35478, 35479, 35446, + 35447, 35480, 35410, 35444, 35524, 35422, 35419, 35421, 35420, 35418, + 35520, 35519, 35522, 35521, 35516, 35515, 35518, 35517, 35416, 35450, + 35412, 35423, 35411, 35448, 35461, 35462, 35460, 35459, 35413, 35457, + 35458, 35456, 35454, 35455, 35453, 35452, 35463, 35465, 35466, 35464, + 35428, 35451, 35417, 35425, 35525, 35467, 35472, 35469, 35473, 35470, + 35475, 35476, 35471, 35468, 35474, 35449, 35477, 35510, 35507, 35498, + 35508, 35509, 35499, 35487, 35486, 35514, 35484, 35483, 35481, 35485, + 35482, 35501, 35506, 35500, 35504, 35505, 35502, 35503, 35430, 35434, + 35433, 35432, 35431, 35513, 35511, 35512, 35437, 35442, 35441, 35440, + 35439, 35438, 35488, 35497, 35490, 35495, 35489, 35493, 35494, 35491, + 35492, 35496, 35429, 35436, 35414, 35435, 35445, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 5296, 5168, 5289, 5271, + 5272, 5340, 5341, 5221, 5318, 5278, 5352, 5353, 5244, 5123, 5174, 5321, + 5222, 5126, 5127, 5316, 5328, 5267, 5204, 5295, 5147, 5344, 5216, 5230, + 5226, 5319, 5281, 5310, 5274, 5343, 5129, 5131, 5231, 5297, 5282, 5339, + 5121, 5299, 5313, 5315, 5269, 5323, 5251, 5169, 5331, 5322, 5241, 5122, + 5181, 5212, 5333, 5219, 5287, 5291, 5234, 5145, 5298, 5276, 5279, 5218, + 5356, 5286, 5235, 5334, 5311, 5210, 5217, 5268, 5273, 5285, 5237, 5284, + 5242, 5288, 5225, 5229, 5355, 5128, 5124, 5354, 5243, 5177, 5148, 5266, + 5342, 5283, 5292, 5275, 5120, 5252, 5280, 5277, 5173, 5245, 5119, 5335, + 5176, 5314, 5317, 5146, 5175, 5300, 5357, 5337, 5293, 5332, 5336, 5290, + 5338, 5294, 5207, 5133, 5172, 5270, 5325, 5326, 5324, 5327, 5220, 5170, + 5345, 5346, 5309, 5233, 5166, 5238, 5239, 5240, 5130, 5132, 5165, 5330, + 5320, 5236, 5246, 5250, 5249, 5248, 5247, 5206, 5203, 5202, 5160, 5162, + 5163, 5161, 5329, 5134, 5214, 5153, 5117, 5111, 5112, 5115, 5116, 5114, + 5113, 5118, 5259, 5254, 5255, 5253, 5264, 5263, 5262, 5261, 5265, 5257, + 5215, 5125, 5180, 5179, 5178, 5260, 5258, 5256, 5208, 5209, 5171, 5213, + 5211, 5182, 5189, 5185, 5193, 5188, 5197, 5187, 5186, 5183, 5184, 5191, + 5192, 5199, 5196, 5194, 5143, 5144, 5142, 5190, 5198, 5351, 5156, 5154, + 5157, 5159, 5158, 5155, 5347, 5349, 5348, 5350, 5200, 5201, 5150, 5149, + 5151, 5152, 5305, 5308, 5306, 5307, 5301, 5304, 5302, 5303, 5164, 5167, + 5312, 5141, 5135, 5140, 5138, 5137, 5136, 5139, 5223, 5227, 5224, 5228, + 5232, 5205, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 23189, 23066, 23082, 23174, 23061, 23186, 23124, 23175, + 23173, 23062, 23058, 23185, 23052, 23170, 23171, 23172, 23077, 23078, + 23015, 23016, 23011, 23010, 23141, 23212, 23209, 23084, 23083, 23190, + 23191, 23085, 23094, 23095, 23096, 23055, 23087, 23088, 23089, 23068, + 23069, 35536, 35536, 23132, 23065, 23067, 23093, 23092, 23137, 23136, + 23188, 23187, 23164, 23165, 23051, 23057, 23153, 23154, 23168, 23169, + 23133, 23235, 23107, 23167, 23076, 23193, 23211, 23195, 23140, 23233, + 23156, 23056, 23196, 23197, 23220, 23221, 23224, 23225, 23222, 23223, + 23216, 23217, 23218, 23219, 23130, 23131, 23226, 23227, 23155, 23231, + 23138, 23135, 23019, 23020, 23014, 23234, 23106, 23166, 23075, 23192, + 23210, 23194, 23139, 23040, 23041, 23044, 23045, 23046, 23079, 23080, + 23081, 23023, 23030, 23031, 23032, 23033, 23034, 23008, 23073, 23009, + 23074, 23007, 23071, 23006, 23070, 23021, 23039, 23047, 23038, 23024, + 23025, 23022, 23049, 23104, 23103, 23028, 23050, 23035, 23042, 23048, + 23027, 23043, 23176, 23199, 23238, 23163, 23126, 23086, 23054, 23063, + 23100, 23099, 23215, 23228, 23237, 23229, 23230, 23142, 23145, 23146, + 23147, 23148, 23149, 23150, 23151, 23152, 23143, 23144, 23108, 23134, + 23072, 23064, 23026, 23029, 23036, 23037, 23158, 23157, 23105, 23098, + 23097, 23236, 23059, 23060, 23125, 23121, 23012, 23179, 23181, 23127, + 23129, 23182, 23184, 23090, 23091, 23123, 23122, 23013, 23180, 23128, + 23183, 23207, 23206, 23208, 23205, 23201, 23202, 23203, 23204, 23053, + 23101, 23102, 23198, 23232, 23160, 23018, 23177, 23017, 23213, 23161, + 23162, 23178, 23214, 23159, 23109, 23112, 23116, 23119, 23118, 23117, + 23113, 23114, 23110, 23111, 23115, 23200, 23120, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 13784, 13772, + 13795, 13796, 13778, 13797, 13798, 13799, 13800, 13785, 13786, 13787, + 13788, 13789, 13790, 13791, 13792, 13793, 13794, 13773, 13774, 13775, + 13776, 13777, 13779, 13780, 13781, 13782, 13783, 13504, 13512, 13525, + 13533, 13539, 13540, 13505, 13506, 13507, 13508, 13509, 13510, 13511, + 13513, 13514, 13515, 13516, 13517, 13518, 13519, 13520, 13521, 13522, + 13523, 13524, 13526, 13527, 13528, 13529, 13530, 13531, 13532, 13534, + 13535, 13536, 13537, 13538, 7373, 7372, 7374, 13564, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 16761, + 16762, 16759, 16757, 16748, 16747, 16754, 16752, 16743, 16750, 16760, + 16745, 16758, 16756, 16749, 16746, 16755, 16753, 16744, 16751, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 21540, 21541, 21538, 21536, 21527, 21526, 21533, 21531, 21522, + 21529, 21539, 21524, 21537, 21535, 21528, 21525, 21534, 21532, 21523, + 21530, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 22877, 10003, 10004, 9998, 9997, 9996, 31305, 31325, + 31346, 31294, 31339, 31303, 31289, 31348, 31293, 31307, 31313, 31336, + 31337, 31351, 31357, 31304, 31335, 31365, 31323, 31290, 31353, 31355, + 31322, 31368, 31302, 31317, 31314, 31295, 31309, 31292, 31350, 31343, + 31297, 31340, 31329, 31363, 31352, 31326, 31354, 31342, 31356, 31331, + 31319, 31362, 31332, 31318, 31349, 31358, 31328, 31364, 31301, 31345, + 31321, 31367, 31311, 31296, 31333, 31330, 31344, 31288, 31316, 31315, + 31366, 31360, 31338, 31308, 31306, 31312, 31320, 31359, 31361, 31334, + 31300, 31298, 31327, 31291, 31299, 31347, 31310, 31341, 31324, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 7787, 7785, 7784, + 7781, 7780, 7783, 7782, 7788, 7786, 7778, 7776, 7775, 7772, 7771, 7774, + 7773, 7779, 7777, 15666, 15665, 15664, 15663, 15662, 29774, 29773, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 20520, 20522, 20550, 20513, 20526, 20558, + 20529, 20559, 20531, 20560, 20533, 20535, 20552, 20554, 20537, 20540, + 20561, 20544, 20546, 20516, 20548, 20562, 20563, 20556, 20564, 20524, + 20568, 20570, 20603, 20565, 20574, 20577, 20579, 20611, 20581, 20612, + 20583, 20585, 20605, 20607, 20587, 20590, 20613, 20594, 20596, 20598, + 20601, 20614, 20615, 20609, 20616, 20572, 21008, 21010, 21040, 21014, + 21016, 21048, 21019, 21049, 21021, 21050, 21023, 21025, 21042, 21044, + 21027, 21030, 21051, 21034, 21036, 21004, 21038, 21052, 21053, 21046, + 21054, 21012, 20956, 20958, 20991, 20952, 20962, 20965, 20967, 35536, + 20969, 20999, 20971, 20973, 20993, 20995, 20975, 20978, 21000, 20982, + 20984, 20986, 20989, 21001, 21002, 20997, 21003, 20960, 20673, 20675, + 20705, 20679, 20681, 20713, 20684, 20714, 20686, 20715, 20688, 20690, + 20707, 20709, 20692, 20695, 20716, 20699, 20701, 20669, 20703, 20717, + 20718, 20711, 20719, 20677, 20727, 20729, 20764, 20733, 20735, 20738, + 20740, 20772, 20742, 20773, 20744, 20746, 20766, 20768, 20748, 20751, + 20774, 20755, 20757, 20759, 20762, 20775, 20776, 20770, 20777, 20731, + 21480, 35536, 21481, 21482, 35536, 35536, 21483, 35536, 35536, 21484, + 21485, 35536, 35536, 21486, 21487, 21488, 21489, 35536, 21490, 21491, + 21492, 21493, 21494, 21495, 21496, 21497, 21498, 21499, 21500, 21501, + 35536, 21502, 35536, 21503, 21504, 21505, 21506, 21507, 21508, 21509, + 35536, 21510, 21511, 21512, 21513, 21514, 21515, 21516, 21517, 21518, + 21519, 21520, 20617, 20618, 20619, 20620, 20621, 20622, 20623, 20624, + 20625, 20626, 20627, 20628, 20629, 20630, 20631, 20632, 20633, 20634, + 20635, 20636, 20637, 20638, 20639, 20640, 20641, 20642, 20643, 20644, + 20645, 20646, 20647, 20648, 20649, 20650, 20651, 20652, 20653, 20654, + 20655, 20656, 20657, 20658, 20659, 20660, 20661, 20662, 20663, 20664, + 20665, 20666, 20667, 20668, 20904, 20905, 35536, 20906, 20907, 20908, + 20909, 35536, 35536, 20910, 20911, 20912, 20913, 20914, 20915, 20916, + 20917, 35536, 20918, 20919, 20920, 20921, 20922, 20923, 20924, 35536, + 20925, 20926, 20927, 20928, 20929, 20930, 20931, 20932, 20933, 20934, + 20935, 20936, 20937, 20938, 20939, 20940, 20941, 20942, 20943, 20944, + 20945, 20946, 20947, 20948, 20949, 20950, 20849, 20850, 35536, 20851, + 20852, 20853, 20854, 35536, 20855, 20856, 20857, 20858, 20859, 35536, + 20860, 35536, 35536, 35536, 20861, 20862, 20863, 20864, 20865, 20866, + 20867, 35536, 20868, 20869, 20870, 20871, 20872, 20873, 20874, 20875, + 20876, 20877, 20878, 20879, 20880, 20881, 20882, 20883, 20884, 20885, + 20886, 20887, 20888, 20889, 20890, 20891, 20892, 20893, 20787, 20788, + 20789, 20790, 20791, 20792, 20793, 20794, 20795, 20796, 20797, 20798, + 20799, 20800, 20801, 20802, 20803, 20804, 20805, 20806, 20807, 20808, + 20809, 20810, 20811, 20812, 20813, 20814, 20815, 20816, 20817, 20818, + 20819, 20820, 20821, 20822, 20823, 20824, 20825, 20826, 20827, 20828, + 20829, 20830, 20831, 20832, 20833, 20834, 20835, 20836, 20837, 20838, + 21418, 21419, 21420, 21421, 21422, 21423, 21424, 21425, 21426, 21427, + 21428, 21429, 21430, 21431, 21432, 21433, 21434, 21435, 21436, 21437, + 21438, 21439, 21440, 21441, 21442, 21443, 21444, 21445, 21446, 21447, + 21448, 21449, 21450, 21451, 21452, 21453, 21454, 21455, 21456, 21457, + 21458, 21459, 21460, 21461, 21462, 21463, 21464, 21465, 21466, 21467, + 21468, 21469, 21250, 21252, 21282, 21256, 21258, 21290, 21261, 21291, + 21263, 21292, 21265, 21267, 21284, 21286, 21269, 21272, 21293, 21276, + 21278, 21246, 21280, 21294, 21295, 21288, 21296, 21254, 21304, 21306, + 21341, 21310, 21312, 21315, 21317, 21349, 21319, 21350, 21321, 21323, + 21343, 21345, 21325, 21328, 21351, 21332, 21334, 21336, 21339, 21352, + 21353, 21347, 21354, 21308, 21366, 21367, 21368, 21369, 21370, 21371, + 21372, 21373, 21374, 21375, 21376, 21377, 21378, 21379, 21380, 21381, + 21382, 21383, 21384, 21385, 21386, 21387, 21388, 21389, 21390, 21391, + 21392, 21393, 21394, 21395, 21396, 21397, 21398, 21399, 21400, 21401, + 21402, 21403, 21404, 21405, 21406, 21407, 21408, 21409, 21410, 21411, + 21412, 21413, 21414, 21415, 21416, 21417, 21140, 21142, 21172, 21146, + 21148, 21180, 21151, 21181, 21153, 21182, 21155, 21157, 21174, 21176, + 21159, 21162, 21183, 21166, 21168, 21136, 21170, 21184, 21185, 21178, + 21186, 21144, 21194, 21196, 21231, 21200, 21202, 21205, 21207, 21239, + 21209, 21240, 21211, 21213, 21233, 21235, 21215, 21218, 21241, 21222, + 21224, 21226, 21229, 21242, 21243, 21237, 21244, 21198, 21063, 21064, + 21065, 21066, 21067, 21068, 21069, 21070, 21071, 21072, 21073, 21074, + 21075, 21076, 21077, 21078, 21079, 21080, 21081, 21082, 21083, 21084, + 21085, 21086, 21087, 21088, 21089, 21090, 21091, 21092, 21093, 21094, + 21095, 21096, 21097, 21098, 21099, 21100, 21101, 21102, 21103, 21104, + 21105, 21106, 21107, 21108, 21109, 21110, 21111, 21112, 21113, 21114, + 20953, 20954, 35536, 35536, 20521, 20523, 20530, 20515, 20527, 20525, + 20528, 20517, 20532, 20534, 20536, 20553, 20555, 20557, 20538, 20543, + 20545, 20518, 20547, 20519, 20549, 20541, 20551, 20542, 20539, 20781, + 20569, 20571, 20580, 20567, 20575, 20573, 20576, 20599, 20582, 20584, + 20586, 20606, 20608, 20610, 20588, 20593, 20595, 20578, 20597, 20600, + 20602, 20591, 20604, 20592, 20589, 20783, 20779, 20786, 20780, 20782, + 20785, 20784, 21009, 21011, 21020, 21015, 21017, 21013, 21018, 21005, + 21022, 21024, 21026, 21043, 21045, 21047, 21028, 21033, 21035, 21006, + 21037, 21007, 21039, 21031, 21041, 21032, 21029, 21057, 20957, 20959, + 20968, 20955, 20963, 20961, 20964, 20987, 20970, 20972, 20974, 20994, + 20996, 20998, 20976, 20981, 20983, 20966, 20985, 20988, 20990, 20979, + 20992, 20980, 20977, 21059, 21055, 21062, 21056, 21058, 21061, 21060, + 20674, 20676, 20685, 20680, 20682, 20678, 20683, 20670, 20687, 20689, + 20691, 20708, 20710, 20712, 20693, 20698, 20700, 20671, 20702, 20672, + 20704, 20696, 20706, 20697, 20694, 20722, 20728, 20730, 20741, 20734, + 20736, 20732, 20737, 20760, 20743, 20745, 20747, 20767, 20769, 20771, + 20749, 20754, 20756, 20739, 20758, 20761, 20763, 20752, 20765, 20753, + 20750, 20724, 20720, 20778, 20721, 20723, 20726, 20725, 21251, 21253, + 21262, 21257, 21259, 21255, 21260, 21247, 21264, 21266, 21268, 21285, + 21287, 21289, 21270, 21275, 21277, 21248, 21279, 21249, 21281, 21273, + 21283, 21274, 21271, 21299, 21305, 21307, 21318, 21311, 21313, 21309, + 21314, 21337, 21320, 21322, 21324, 21344, 21346, 21348, 21326, 21331, + 21333, 21316, 21335, 21338, 21340, 21329, 21342, 21330, 21327, 21301, + 21297, 21355, 21298, 21300, 21303, 21302, 21141, 21143, 21152, 21147, + 21149, 21145, 21150, 21137, 21154, 21156, 21158, 21175, 21177, 21179, + 21160, 21165, 21167, 21138, 21169, 21139, 21171, 21163, 21173, 21164, + 21161, 21189, 21195, 21197, 21208, 21201, 21203, 21199, 21204, 21227, + 21210, 21212, 21214, 21234, 21236, 21238, 21216, 21221, 21223, 21206, + 21225, 21228, 21230, 21219, 21232, 21220, 21217, 21191, 21187, 21245, + 21188, 21190, 21193, 21192, 20514, 20566, 35536, 35536, 20845, 20847, + 20844, 20843, 20840, 20839, 20842, 20841, 20848, 20846, 20900, 20902, + 20899, 20898, 20895, 20894, 20897, 20896, 20903, 20901, 21476, 21478, + 21475, 21474, 21471, 21470, 21473, 21472, 21479, 21477, 21362, 21364, + 21361, 21360, 21357, 21356, 21359, 21358, 21365, 21363, 21121, 21123, + 21120, 21119, 21116, 21115, 21118, 21117, 21124, 21122, 27439, 27395, + 27420, 27636, 27591, 27377, 27440, 27404, 27553, 27505, 27481, 27442, + 27445, 27402, 27446, 27396, 27447, 27469, 27464, 27502, 27443, 27449, + 27458, 27459, 27450, 27456, 27460, 27398, 27532, 27441, 27470, 27399, + 27479, 27448, 27478, 27465, 27504, 27503, 27444, 27463, 27473, 27474, + 27477, 27476, 27544, 27452, 27453, 27454, 27526, 27494, 27457, 27461, + 27455, 27451, 27525, 27486, 27524, 27523, 27483, 27482, 27485, 27475, + 27472, 27471, 27521, 27520, 27522, 27493, 27569, 27573, 27572, 27570, + 27571, 27414, 27560, 27590, 27562, 27575, 27567, 27576, 27568, 27577, + 27566, 27424, 27425, 27589, 27630, 27563, 27564, 27565, 27561, 27587, + 27574, 27585, 27578, 27586, 27584, 27582, 27579, 27580, 27581, 27583, + 27411, 27417, 27415, 27416, 27620, 27619, 27427, 27419, 27430, 27433, + 27428, 27431, 27429, 27432, 27437, 27434, 27394, 27629, 27635, 27632, + 27634, 27608, 27610, 27588, 27618, 27611, 27615, 27609, 27617, 27616, + 27614, 27376, 27466, 27401, 27593, 27379, 27602, 27468, 27467, 27594, + 27507, 27510, 27509, 27508, 27517, 27557, 27406, 27631, 27388, 27513, + 27516, 27511, 27512, 27605, 27515, 27604, 27387, 27386, 27514, 27405, + 27603, 27385, 27480, 27400, 27595, 27612, 27378, 27462, 27397, 27533, + 27613, 27389, 27541, 27537, 27539, 27410, 27633, 27390, 27534, 27536, + 27535, 27540, 27538, 27628, 27506, 27403, 27435, 27622, 27623, 27621, + 27423, 27601, 27381, 27380, 27530, 27606, 27528, 27409, 27518, 27529, + 27627, 27527, 27531, 27519, 27407, 27436, 27426, 27607, 27392, 27393, + 27391, 27408, 27412, 27413, 27625, 27626, 27624, 27592, 27495, 27597, + 27498, 27499, 27500, 27497, 27501, 27496, 27490, 27491, 27492, 27489, + 27487, 27418, 27488, 27484, 27421, 27422, 27599, 27600, 27596, 27598, + 27383, 27384, 27382, 27542, 27547, 27550, 27551, 27552, 27558, 27543, + 27545, 27546, 27554, 27549, 27555, 27556, 27548, 27438, 27559, 27950, + 27949, 27948, 27970, 27969, 27968, 27925, 27924, 27923, 27309, 27308, + 27307, 27911, 27910, 27909, 27921, 27922, 27917, 27920, 27916, 27919, + 27918, 27366, 27369, 27365, 27368, 27367, 27915, 27785, 27786, 27787, + 27788, 27783, 27784, 27789, 27829, 27734, 27847, 27846, 27844, 27845, + 27848, 27823, 27826, 27824, 27825, 27822, 27853, 27852, 27850, 27851, + 27799, 27798, 27797, 27803, 27802, 27801, 27800, 27821, 27820, 27819, + 27796, 27795, 27794, 27869, 27868, 27867, 27843, 27842, 27841, 27966, + 27965, 27964, 27963, 27962, 27961, 27967, 27960, 27959, 27958, 27703, + 27702, 27700, 27701, 27707, 27706, 27704, 27705, 27695, 27694, 27692, + 27693, 27699, 27698, 27696, 27697, 27757, 27756, 27754, 27755, 27758, + 27772, 27775, 27773, 27774, 27731, 27762, 27761, 27760, 27759, 27717, + 27730, 27729, 27728, 27718, 27716, 27715, 27714, 27781, 27780, 27779, + 27778, 27777, 27776, 27953, 27952, 27951, 27956, 27955, 27954, 27957, + 27816, 27815, 27813, 27814, 27807, 27806, 27804, 27805, 27812, 27811, + 27834, 27833, 27830, 27835, 27840, 27837, 27836, 27856, 27855, 27854, + 27859, 27858, 27857, 27810, 27818, 27817, 27906, 27903, 27902, 27849, + 27808, 27831, 27838, 27863, 27907, 27904, 27900, 27809, 27832, 27839, + 27864, 27908, 27905, 27901, 27862, 27861, 27860, 27721, 27720, 27738, + 27736, 27737, 27735, 27747, 27745, 27746, 27744, 27764, 27763, 27895, + 27892, 27898, 27723, 27722, 27739, 27740, 27742, 27741, 27751, 27749, + 27750, 27748, 27766, 27765, 27896, 27893, 27899, 27727, 27726, 27724, + 27725, 27719, 27743, 27752, 27767, 27768, 27769, 27894, 27891, 27897, + 27753, 27793, 27791, 27792, 27790, 27713, 27711, 27709, 27712, 27710, + 27708, 27866, 27865, 27771, 27770, 27828, 27827, 27733, 27732, 27325, + 27324, 27320, 27323, 27327, 27326, 27321, 27322, 27319, 27328, 27638, + 27645, 27643, 27642, 27640, 27641, 27639, 27644, 27358, 27356, 27357, + 27334, 27333, 27332, 27317, 27315, 27316, 27318, 27373, 27372, 27371, + 27352, 27353, 27349, 27330, 27329, 27348, 27350, 27347, 27351, 27331, + 27346, 27344, 27345, 27341, 27343, 27342, 27335, 27337, 27336, 27339, + 27338, 27340, 27312, 27311, 27310, 27935, 27934, 27936, 27355, 27872, + 27871, 27873, 27874, 27302, 27304, 27301, 27303, 27306, 27305, 27667, + 27665, 27666, 27672, 27674, 27673, 27669, 27671, 27670, 27686, 27685, + 27684, 27678, 27679, 27680, 27681, 27682, 27683, 27675, 27677, 27676, + 27687, 27688, 27689, 27656, 27654, 27655, 27668, 27691, 27690, 27943, + 27942, 27940, 27941, 27939, 27944, 27938, 27937, 27927, 27932, 27930, + 27931, 27928, 27929, 27933, 27870, 27782, 27875, 27637, 27354, 27913, + 27912, 27375, 27370, 27914, 27946, 27945, 27947, 27971, 27646, 27647, + 27648, 27649, 27650, 27651, 27652, 27653, 27364, 27664, 27663, 27658, + 27661, 27660, 27657, 27659, 27662, 27314, 27374, 27926, 27313, 27972, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 27359, 27360, 27361, 27362, 27363, + 35536, 27883, 27884, 27885, 27886, 27887, 27888, 27889, 27890, 27876, + 27877, 27878, 27879, 27880, 27881, 27882, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 18185, 18460, 17951, 18465, + 17938, 18309, 18571, 18462, 18557, 18526, 17931, 18164, 18165, 18561, + 17925, 17978, 17952, 18302, 18101, 18282, 18162, 18555, 18441, 18530, + 18173, 18102, 18246, 18399, 18531, 18068, 18477, 35536, 35536, 35536, + 35536, 35536, 35536, 18092, 18295, 18341, 18446, 18485, 18521, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 7328, 7330, 7340, 7334, 7316, 7341, 7348, 35536, 7347, + 7321, 7318, 7317, 7315, 7352, 7336, 7335, 7337, 7351, 7338, 7339, 7323, + 7326, 7350, 7332, 7349, 35536, 35536, 7324, 7327, 7331, 7325, 7343, 7342, + 7344, 35536, 7346, 7322, 35536, 7345, 7320, 7329, 7319, 7333, 35536, + 35536, 35536, 35536, 35536, 22464, 22433, 22461, 22459, 22435, 22457, + 22454, 22455, 22456, 22463, 22440, 22441, 22465, 22444, 22442, 22437, + 22450, 22466, 22439, 22462, 22449, 22458, 22448, 22451, 22436, 22453, + 22434, 22447, 22431, 22460, 22432, 22445, 22443, 9638, 9616, 9636, 9623, + 9619, 9633, 9630, 9631, 9632, 9637, 9621, 9639, 9635, 9622, 9640, 9620, + 9625, 9629, 9634, 9628, 9626, 9627, 9624, 9615, 9618, 9617, 22438, 22452, + 22446, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 7236, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 24187, 24171, 24162, + 24173, 24178, 24170, 24175, 24166, 24192, 24196, 24198, 24201, 24165, + 24160, 24195, 24185, 24169, 24168, 24199, 24161, 24172, 24193, 24181, + 24197, 24200, 24167, 24189, 24174, 24164, 24184, 24163, 24179, 24186, + 24188, 24194, 24180, 24176, 24177, 24202, 24203, 24182, 24183, 24190, + 24191, 24204, 35536, 35536, 35536, 24213, 24217, 24216, 24219, 24218, + 24215, 24214, 24210, 24207, 24206, 24209, 24208, 24211, 24212, 35536, + 35536, 24227, 24229, 24226, 24225, 24222, 24221, 24224, 24223, 24230, + 24228, 35536, 35536, 35536, 35536, 24205, 24220, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 32097, 32080, 32100, + 32090, 32094, 32091, 32096, 32082, 32081, 32099, 32087, 32102, 32101, + 32093, 32092, 32098, 32095, 32083, 32075, 32084, 32076, 32104, 32085, + 32077, 32086, 32078, 32103, 32089, 32079, 32088, 32105, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 33559, 33558, 33590, 33591, 33592, + 33594, 33583, 33586, 33572, 33575, 33587, 33579, 33576, 33593, 33589, + 33588, 33596, 33601, 33600, 33599, 33585, 33564, 33563, 33598, 33597, + 33584, 33595, 33567, 33569, 33573, 33580, 33571, 33578, 33577, 33566, + 33561, 33562, 33570, 33565, 33568, 33560, 33574, 33581, 33582, 33605, + 33606, 33603, 33604, 33613, 33615, 33612, 33611, 33608, 33607, 33610, + 33609, 33616, 33614, 35536, 35536, 35536, 35536, 35536, 33602, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 23564, 23567, 23566, + 23568, 23565, 23547, 23551, 23549, 23548, 23550, 23559, 23562, 23560, + 23563, 23561, 23569, 23570, 23571, 23572, 23573, 23552, 23554, 23557, + 23558, 23553, 23556, 23555, 23577, 23574, 23578, 23576, 23575, 23585, + 23587, 23584, 23583, 23580, 23579, 23582, 23581, 23588, 23586, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 24350, 24353, 24352, 24351, 24354, 24355, 24332, + 24334, 24333, 24335, 24336, 24337, 24344, 24345, 24349, 24346, 24347, + 24348, 24356, 24360, 24357, 24359, 24358, 24361, 24338, 24343, 24342, + 24340, 24339, 24341, 24364, 24362, 24363, 24372, 24374, 24371, 24370, + 24367, 24366, 24369, 24368, 24375, 24373, 35536, 35536, 35536, 35536, + 24365, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 29660, 29669, 29658, 29667, 29691, 29676, 29689, + 29666, 29675, 29661, 29670, 29690, 29665, 29674, 29683, 29677, 29688, + 29664, 29673, 29682, 29662, 29671, 29692, 29695, 29657, 29694, 29663, + 29672, 29693, 29659, 29668, 35536, 29650, 29684, 29679, 29700, 29678, + 29651, 29698, 29686, 29696, 29685, 29680, 29681, 29687, 29649, 29699, + 29697, 29654, 29653, 29652, 29656, 29655, 29701, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 29702, 29703, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 12132, 12138, 12136, 12133, 12135, 12134, 12137, 35536, 12087, + 12131, 12129, 12128, 35536, 12075, 12074, 35536, 12086, 12085, 12084, + 12071, 12070, 12083, 12082, 12081, 12080, 12079, 12078, 12073, 12072, + 12077, 12076, 35536, 21774, 21775, 21776, 21842, 21863, 21851, 21815, + 21951, 21777, 21778, 21782, 21901, 21886, 21891, 21814, 21969, 21918, + 21840, 21820, 21912, 21781, 21779, 21780, 21827, 21871, 21924, 21964, + 21787, 21783, 21791, 21928, 21866, 21876, 21903, 21792, 21789, 21788, + 21941, 21879, 21939, 21917, 21908, 21906, 21907, 21970, 21949, 21786, + 21801, 21793, 21940, 21885, 21910, 21845, 21971, 21797, 21798, 21799, + 21855, 21847, 21831, 21932, 21883, 21784, 21790, 21785, 21858, 21955, + 21956, 21794, 21795, 21796, 21869, 21824, 21874, 21841, 21802, 21800, + 21803, 21927, 21884, 21933, 21835, 21947, 21804, 21808, 21812, 21881, + 21853, 21921, 21902, 21805, 21809, 21810, 21852, 21844, 21909, 21862, + 21972, 21873, 21811, 21806, 21807, 21889, 21942, 21950, 21819, 21962, + 21813, 21872, 21822, 21919, 21859, 21897, 21829, 21911, 21861, 21828, + 21968, 21817, 21864, 21821, 21860, 21888, 21915, 21926, 21896, 21931, + 21898, 21857, 21877, 21958, 21925, 21892, 21935, 21965, 21938, 21936, + 21961, 21832, 21948, 21838, 21867, 21823, 21854, 21830, 21878, 21834, + 21914, 21833, 21893, 21818, 21959, 21849, 21944, 21945, 21963, 21934, + 21875, 21913, 21905, 21868, 21843, 21816, 21882, 21890, 21929, 21895, + 21825, 21920, 21865, 21880, 21846, 21848, 21954, 21894, 21900, 21899, + 21966, 21887, 21836, 21837, 21923, 21967, 21916, 21904, 21957, 21960, + 21930, 21952, 21856, 21922, 21850, 21937, 21826, 21953, 21870, 21839, + 35536, 35536, 21980, 21978, 21977, 21974, 21973, 21976, 21975, 21981, + 21979, 21769, 21768, 21772, 21770, 21767, 21771, 21773, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 27, 9, 24, 14, 29, 21, 34, 28, 37, 39, 35, 40, 41, 10, 30, 32, 18, 15, 31, 42, 13, 25, 36, 23, 12, 20, 33, 19, 38, 17, 11, 26, 16, 22, 62, 44, 59, 49, 64, 56, 69, 63, 72, 74, 70, 75, 76, 45, 65, 67, 53, 50, 66, 77, 48, 60, 71, 58, 47, 55, 68, 54, 73, 52, 46, 61, 51, 57, 85, 86, 79, 84, 43, 78, 83, - 82, 40951, 40951, 40951, 40951, 93, 95, 92, 91, 88, 87, 90, 89, 96, 94, - 40951, 40951, 40951, 40951, 80, 81, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 20698, 20684, - 20679, 20659, 20654, 20672, 20667, 20647, 20662, 20687, 20682, 20677, - 20657, 20652, 20673, 20668, 20648, 20663, 20699, 20685, 20680, 20660, - 20655, 20675, 20670, 20650, 20665, 20700, 20686, 20681, 20661, 20656, - 20676, 20671, 20651, 20666, 20688, 20683, 20678, 20658, 20653, 20674, - 20669, 20649, 20664, 20645, 20646, 20636, 20643, 20644, 20696, 20694, - 20693, 20690, 20689, 20692, 20691, 20697, 20695, 20702, 20638, 20637, - 20639, 20701, 20642, 20641, 20640, 20635, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 30827, 30822, 30817, 30797, 30792, 30810, 30805, 30785, - 30800, 30825, 30820, 30815, 30795, 30790, 30811, 30806, 30786, 30801, - 30828, 30823, 30818, 30798, 30793, 30813, 30808, 30788, 30803, 30829, - 30824, 30819, 30799, 30794, 30814, 30809, 30789, 30804, 30826, 30821, - 30816, 30796, 30791, 30812, 30807, 30787, 30802, 30784, 30777, 30779, - 30769, 30771, 30772, 30774, 30781, 30780, 30775, 30770, 30773, 30778, - 30776, 30783, 30782, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 2246, 2254, 2252, 2150, - 40951, 2262, 2250, 2258, 2242, 2257, 2249, 2198, 2253, 2260, 2225, 2247, - 2255, 2226, 2261, 2256, 2224, 2245, 2244, 2248, 2243, 2149, 2251, 2259, - 2120, 2122, 2121, 2123, 40951, 2162, 2160, 40951, 2157, 40951, 40951, - 2156, 40951, 2164, 2159, 2167, 2161, 2166, 2154, 2169, 2163, 2155, 2168, - 40951, 2153, 2152, 2151, 2158, 40951, 2170, 40951, 2165, 40951, 40951, - 40951, 40951, 40951, 40951, 2234, 40951, 40951, 40951, 40951, 2236, - 40951, 2235, 40951, 2239, 40951, 2238, 2231, 2241, 40951, 2232, 2240, - 40951, 2230, 40951, 40951, 2233, 40951, 2229, 40951, 2237, 40951, 2227, - 40951, 2228, 40951, 2216, 2214, 40951, 2211, 40951, 40951, 2210, 2205, - 2218, 2213, 40951, 2215, 2221, 2208, 2223, 2217, 2209, 2222, 40951, 2207, - 2206, 2204, 2212, 40951, 2203, 2219, 2220, 2201, 40951, 2202, 40951, - 2175, 2187, 2185, 2195, 2178, 2197, 2183, 2177, 2181, 2190, 40951, 2193, - 2186, 2192, 2172, 2176, 2188, 2173, 2196, 2189, 2171, 2182, 2180, 2174, - 2179, 2194, 2184, 2191, 40951, 40951, 40951, 40951, 40951, 2136, 2134, - 2145, 40951, 2148, 2132, 2140, 2130, 2139, 40951, 2143, 2135, 2142, 2125, - 2147, 2137, 2126, 2146, 2138, 2124, 2131, 2129, 2127, 2128, 2144, 2133, - 2141, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 2199, 2200, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 25385, - 25401, 25416, 25392, 25420, 25419, 25417, 25397, 25414, 25411, 25390, - 25387, 25406, 25403, 25383, 25394, 25398, 25415, 25412, 25391, 25388, - 25407, 25404, 25384, 25395, 25396, 25413, 25410, 25389, 25386, 25405, - 25402, 25382, 25393, 25400, 25399, 25380, 25423, 25409, 25408, 25421, - 25418, 25422, 25381, 40951, 40951, 40951, 40951, 11138, 11089, 11090, - 11091, 11092, 11093, 11094, 11095, 11096, 11097, 11098, 11099, 11100, - 11101, 11102, 11103, 11104, 11105, 11106, 11107, 11108, 11109, 11110, - 11111, 11112, 11113, 11114, 11115, 11116, 11117, 11118, 11119, 11120, - 11121, 11122, 11123, 11124, 11125, 11126, 11127, 11128, 11129, 11130, - 11131, 11132, 11133, 11134, 11135, 11136, 11137, 11188, 11139, 11140, - 11141, 11142, 11143, 11144, 11145, 11146, 11147, 11148, 11149, 11150, - 11151, 11152, 11153, 11154, 11155, 11156, 11157, 11158, 11159, 11160, - 11161, 11162, 11163, 11164, 11165, 11166, 11167, 11168, 11169, 11170, - 11171, 11172, 11173, 11174, 11175, 11176, 11177, 11178, 11179, 11180, - 11181, 11182, 11183, 11184, 11185, 11186, 11187, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 31485, - 31555, 31530, 31526, 31489, 31494, 31514, 31510, 31506, 31559, 31522, - 31563, 31498, 31518, 31502, 40951, 40951, 31556, 31531, 31527, 31490, - 31495, 31515, 31511, 31507, 31560, 31523, 31564, 31499, 31519, 31503, - 31486, 40951, 31557, 31532, 31528, 31491, 31496, 31516, 31512, 31508, - 31561, 31524, 31565, 31500, 31520, 31504, 31484, 40951, 31554, 31529, - 31525, 31488, 31493, 31513, 31509, 31505, 31558, 31521, 31562, 31497, - 31517, 31501, 31487, 31492, 31536, 31533, 31547, 31548, 31549, 31550, - 31551, 31552, 31553, 31537, 31538, 31539, 31540, 31541, 31542, 31543, - 31544, 31545, 31546, 31534, 31535, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 10874, 10873, 10858, 10870, 10867, - 10852, 10849, 10864, 10861, 10876, 10855, 10915, 10894, 6813, 6530, 6554, - 31156, 31157, 31158, 31159, 31160, 31161, 31162, 31163, 31164, 31165, - 31166, 31167, 31168, 31169, 31170, 31171, 31172, 31173, 31174, 31175, - 31176, 31177, 31178, 31179, 31180, 31181, 37498, 6644, 6645, 6541, 6812, - 8646, 34420, 34421, 34422, 34423, 34424, 34425, 34426, 34427, 34428, - 34429, 34430, 34431, 34432, 34433, 34434, 34435, 34436, 34437, 34438, - 34439, 34440, 34441, 34442, 34443, 34444, 34445, 34414, 34450, 34465, - 34466, 34455, 34477, 28977, 28978, 28979, 28980, 28981, 28982, 28983, - 28984, 28985, 28986, 28987, 28988, 28989, 28990, 28991, 28992, 28993, - 28994, 28995, 28996, 28997, 28998, 28999, 29000, 29001, 29002, 31764, - 31765, 31766, 6540, 6539, 6587, 29008, 29009, 29010, 29011, 29012, 29013, - 29014, 29015, 29016, 29017, 29018, 29019, 29020, 29021, 29022, 29023, - 29024, 29025, 29026, 29027, 29028, 29029, 29030, 29031, 29032, 29033, - 8690, 29040, 29043, 29044, 29039, 29041, 34149, 34403, 34402, 34409, - 34478, 34451, 34452, 34454, 34464, 34472, 34475, 34467, 34457, 34469, - 34407, 34471, 34408, 34458, 34468, 34460, 34453, 34419, 34413, 34412, - 34411, 34448, 34459, 34473, 34474, 25815, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 31794, 31795, 31796, 31797, 31798, 31799, 31800, 31801, - 31802, 31803, 31804, 31805, 31806, 31807, 31808, 31809, 31810, 31811, - 31812, 31813, 31814, 31815, 31816, 31817, 31818, 31819, 34190, 34415, - 34417, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 34381, 34376, 34368, 34416, 34362, 34374, - 34397, 34373, 34361, 34386, 34394, 34385, 34366, 34377, 34378, 34384, - 34365, 34395, 34392, 34399, 34375, 34370, 34389, 34379, 34382, 34359, - 34360, 34401, 34372, 34363, 34367, 34383, 34398, 34380, 34393, 34396, - 34369, 34390, 34388, 34387, 34391, 34364, 34371, 34400, 40951, 40951, - 40951, 40951, 37495, 37489, 37490, 37492, 37496, 37493, 37497, 37491, - 37494, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 6592, 6590, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 32230, 32231, 32228, 32232, 32227, 32229, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 10525, 17488, 8037, 29287, 34652, 34651, - 6823, 34697, 31757, 4931, 39127, 38952, 27585, 11538, 11536, 11537, - 18006, 29092, 39039, 17455, 39040, 17529, 39038, 22740, 39037, 8680, - 29091, 17454, 22739, 17528, 34576, 18007, 32825, 36989, 3828, 39284, - 39288, 39285, 39286, 8050, 8049, 8048, 8047, 17487, 39351, 20436, 36648, - 5007, 6476, 32563, 17383, 10551, 31024, 6124, 20433, 37663, 6474, 32218, - 20393, 34698, 4242, 11532, 11533, 20218, 17506, 25667, 17423, 24034, - 28299, 37448, 2541, 18119, 27068, 39130, 35786, 24399, 3426, 31470, - 31791, 18659, 31290, 31287, 6475, 34517, 19108, 33783, 26860, 31606, - 31914, 31915, 8480, 9952, 34505, 34081, 4929, 17519, 32234, 10534, 30882, - 34738, 17527, 17461, 33878, 32769, 20466, 11284, 8479, 6513, 5999, 25270, - 9955, 20399, 32842, 3649, 31604, 8478, 17493, 36650, 32530, 39353, 8054, - 37576, 3540, 7996, 2605, 17494, 4366, 31594, 31905, 39373, 3760, 20825, - 6514, 17429, 17451, 17450, 2748, 31214, 8459, 35785, 8691, 31469, 20830, - 6061, 39350, 28182, 32535, 18044, 19689, 4368, 27584, 31858, 28305, - 34525, 24398, 8471, 3530, 3531, 17444, 98, 6059, 17435, 32176, 17459, - 27574, 28204, 6516, 19688, 2522, 37473, 6821, 37395, 7992, 31319, 38874, - 10920, 33789, 3759, 17718, 4371, 17477, 28540, 28286, 32529, 18675, - 28304, 37581, 38879, 28539, 32359, 36764, 33764, 3432, 6477, 33872, - 32360, 34736, 34112, 37578, 20431, 370, 32237, 34741, 39148, 18043, - 31747, 31748, 8682, 38953, 17465, 20468, 34929, 33869, 5265, 3536, 5006, - 20443, 6822, 10828, 7993, 10659, 10660, 28958, 34503, 20442, 20441, - 29948, 20827, 17349, 20444, 3421, 2538, 20437, 25154, 8474, 32534, 10577, - 17419, 20823, 20826, 17348, 39253, 3881, 39136, 39135, 32219, 3897, - 22564, 2616, 4375, 369, 16743, 16744, 16745, 16746, 16747, 31772, 28199, - 30885, 39128, 8673, 37293, 24296, 31774, 6066, 11374, 8694, 39307, 33866, - 33867, 20430, 31777, 18011, 32848, 28180, 32217, 6482, 11025, 31461, - 4553, 16712, 29946, 34106, 4944, 964, 20398, 22566, 17458, 37577, 4243, - 37688, 19657, 2604, 17523, 3761, 31291, 22561, 31616, 11376, 2614, 11087, - 28201, 8674, 37294, 31775, 6067, 11375, 34111, 20432, 28181, 11086, - 31463, 17526, 19106, 39372, 3535, 31061, 31462, 31280, 6481, 17380, - 17378, 11531, 29441, 28202, 37449, 39295, 39210, 39236, 39260, 17462, - 39137, 30881, 37021, 37022, 7991, 30538, 8696, 39362, 17379, 29282, - 34926, 20927, 11382, 22556, 3765, 39361, 31720, 19112, 31611, 25661, - 2535, 20289, 39363, 39360, 17492, 5002, 5001, 4551, 17906, 25574, 39358, - 17427, 25580, 37703, 37704, 31591, 39359, 4932, 31304, 25577, 25578, - 30517, 30515, 2603, 8462, 31673, 20833, 20832, 18975, 2606, 17367, 348, - 20595, 33766, 20711, 18674, 10533, 25054, 28889, 17415, 18979, 3430, - 34924, 31466, 22553, 25153, 32156, 17722, 22548, 4367, 8672, 39146, 3537, - 4938, 37697, 34082, 18672, 19691, 4245, 18661, 39397, 31719, 19690, - 31901, 19692, 10836, 16700, 962, 4550, 33778, 8059, 34107, 11378, 10538, - 31464, 17472, 11005, 34095, 36996, 39221, 20453, 28002, 9950, 19721, - 8679, 3423, 3425, 3424, 3422, 28001, 6295, 32559, 31314, 4933, 27587, - 17478, 30549, 11529, 17440, 30536, 30889, 30891, 5262, 36651, 6004, 6294, - 6296, 3428, 7997, 31722, 32225, 31282, 34515, 37547, 4256, 24397, 29438, - 29439, 8043, 30530, 18660, 4244, 30553, 4257, 28960, 32556, 27421, 36656, - 30892, 17426, 32443, 31723, 6301, 30831, 20822, 31281, 17381, 20627, - 16781, 8040, 8041, 30540, 30539, 31600, 31597, 29277, 27601, 27602, - 38871, 27603, 29357, 966, 5263, 5264, 38873, 36663, 31750, 38875, 17443, - 31595, 31687, 37691, 8027, 8028, 8024, 975, 25155, 20284, 34090, 34089, - 34091, 34092, 3529, 16698, 24165, 32032, 25112, 8042, 21611, 25109, - 30543, 3543, 3545, 4255, 25052, 31752, 2608, 16776, 30519, 33928, 37488, - 29356, 21626, 20714, 20715, 20718, 20717, 20716, 17447, 16699, 39376, - 19102, 29852, 20445, 31475, 27573, 36662, 8701, 33760, 20831, 37549, - 3908, 39271, 22730, 22657, 22665, 22658, 33795, 33794, 37786, 11297, - 37790, 11291, 25224, 37882, 6537, 8688, 8687, 29431, 29433, 34813, 39234, - 19736, 6132, 30883, 11369, 21609, 28188, 34834, 27284, 4369, 8008, 8018, - 8020, 8004, 8002, 8012, 8010, 8000, 8006, 8014, 7998, 8016, 8009, 8019, - 8021, 8005, 8003, 8013, 8011, 8001, 8007, 8015, 7999, 8017, 31979, 31980, - 31981, 4997, 4998, 32164, 4254, 5998, 25664, 3910, 29355, 20397, 25575, - 33781, 10535, 34102, 34103, 20928, 25579, 24086, 36657, 31960, 39290, - 3923, 36661, 7994, 2609, 34487, 16780, 17486, 31294, 25051, 3878, 25186, - 25172, 25184, 25185, 25206, 24146, 37679, 31762, 31886, 31894, 31895, - 31899, 31896, 31763, 39211, 32951, 32950, 32947, 32946, 3856, 3883, - 32953, 32952, 32949, 32948, 3926, 3822, 3835, 10661, 21613, 37012, 31670, - 31617, 3838, 39225, 33879, 36645, 39356, 30527, 37692, 37008, 37533, - 30341, 19655, 32540, 31671, 17425, 30550, 11011, 11012, 11013, 17516, - 17518, 17517, 3834, 17490, 30537, 6005, 6006, 17441, 16748, 16749, 16750, - 29435, 29436, 29437, 16759, 16752, 16753, 11010, 30888, 30893, 39142, - 34105, 34104, 10662, 27588, 26846, 30874, 8026, 5996, 20629, 10552, 8454, - 30514, 32175, 30890, 34513, 10532, 25053, 34093, 37004, 37003, 37005, - 37006, 24114, 31982, 37707, 37010, 24131, 31996, 24044, 31918, 28185, - 24422, 24421, 2753, 2759, 2754, 2758, 2751, 24419, 2752, 39367, 28198, - 37532, 34500, 33763, 28203, 18664, 18667, 17405, 33854, 33856, 33855, - 33857, 33853, 33852, 39354, 33858, 17385, 31857, 33851, 33861, 33864, - 29089, 17360, 37743, 17388, 31292, 8460, 8461, 22549, 17416, 22550, - 22551, 17402, 17403, 17404, 10922, 39368, 963, 31609, 8700, 31316, 17410, - 10921, 17525, 960, 17431, 39144, 33780, 37394, 18669, 25269, 17393, - 20452, 17395, 17386, 2530, 17479, 33779, 11006, 17413, 17390, 18668, - 6068, 33850, 33849, 6069, 22552, 31608, 8699, 39143, 33785, 33784, 37894, - 17409, 17398, 17392, 31313, 32564, 19694, 34088, 19654, 31311, 31310, - 31306, 31308, 29400, 33984, 29373, 33971, 37677, 37686, 37676, 37685, - 29399, 33983, 29372, 33970, 19772, 19765, 19769, 19762, 29401, 33985, - 29374, 33972, 19773, 19766, 19770, 19763, 20395, 20396, 33924, 33925, - 24289, 37928, 32119, 11364, 32550, 19767, 24401, 19744, 19699, 34739, - 32437, 32438, 32439, 19789, 32440, 19756, 38862, 38859, 6297, 31867, - 32174, 19930, 34504, 31755, 20287, 20288, 37540, 27420, 24408, 34501, - 37536, 37537, 5000, 30524, 37575, 5003, 27589, 371, 17448, 31589, 30523, - 36649, 30522, 2537, 30520, 31790, 10557, 2521, 37534, 28179, 28195, - 34737, 28196, 160, 32824, 32233, 34094, 20423, 38853, 8463, 31590, 37546, - 11370, 29353, 33865, 29358, 31721, 11368, 31602, 29362, 3755, 29351, - 3754, 28197, 31323, 29354, 6479, 27285, 39364, 31862, 2607, 37531, 39126, - 32847, 3527, 3528, 31222, 9954, 2620, 24087, 37543, 31684, 6646, 4552, - 17907, 8671, 33777, 32827, 3547, 3656, 31480, 29945, 32826, 34527, 30894, - 20391, 20455, 16713, 40951, 40951, 40951, 40951, 39357, 31566, 39150, - 32157, 19103, 32820, 29979, 28193, 31754, 28190, 37784, 37781, 37789, - 33792, 29408, 227, 228, 40951, 40951, 40951, 32442, 30521, 10847, 31218, - 32538, 28191, 6001, 33782, 17482, 33769, 2539, 31460, 32177, 40951, - 40951, 40951, 295, 243, 344, 342, 339, 237, 235, 236, 233, 234, 332, 334, - 335, 321, 288, 250, 281, 282, 283, 265, 309, 286, 336, 337, 307, 308, - 270, 323, 276, 277, 259, 300, 256, 278, 319, 257, 258, 255, 310, 317, - 338, 329, 279, 238, 318, 311, 316, 333, 298, 299, 297, 301, 302, 303, - 230, 231, 284, 312, 240, 304, 305, 241, 246, 326, 327, 296, 247, 248, - 249, 232, 343, 322, 328, 271, 340, 289, 254, 331, 253, 325, 252, 330, - 313, 280, 324, 341, 274, 242, 291, 251, 290, 239, 314, 315, 320, 294, - 268, 266, 267, 293, 292, 260, 261, 262, 263, 264, 229, 244, 245, 306, - 275, 287, 269, 285, 273, 272, 25151, 29947, 25272, 40951, 40951, 40951, - 40951, 19684, 25449, 18041, 31741, 30655, 3849, 3927, 3889, 3823, 3913, - 26961, 4251, 19786, 38863, 17364, 39190, 32226, 3921, 3914, 24416, 26986, - 4252, 19783, 38864, 17365, 39272, 39274, 34335, 3919, 3937, 3871, 39205, - 39206, 10835, 3920, 3938, 3872, 39243, 36993, 24418, 26987, 4253, 38868, - 38865, 17366, 36991, 24411, 26979, 4247, 19757, 38860, 17361, 24404, - 26967, 4250, 19732, 38858, 17363, 24412, 26978, 4248, 19761, 38861, - 17362, 24402, 26984, 4249, 19726, 38857, 24414, 26981, 37011, 26980, - 24406, 26966, 17508, 26965, 31868, 24403, 19731, 26977, 19760, 33758, - 26983, 19724, 38856, 19722, 24413, 19779, 19778, 6807, 29003, 6817, - 29004, 29285, 40951, 40951, 40951, 40951, 40951, 40951, 22664, 22732, - 22660, 22728, 22655, 22731, 22659, 22666, 22733, 22661, 22729, 22656, - 40951, 40951, 40951, 40951, 19729, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 24209, 37904, 32072, 11315, 24216, 37901, 32079, 11312, 24203, 37900, - 32069, 11311, 40951, 40951, 40951, 40951, 24208, 37903, 32071, 11314, - 24218, 37905, 32081, 11316, 19740, 19781, 19754, 19718, 19739, 19780, - 19753, 19717, 24253, 37938, 32132, 11335, 24252, 37937, 32131, 11334, - 24251, 37934, 32130, 11331, 24255, 37940, 32134, 11337, 24254, 37939, - 32133, 11336, 24283, 37915, 32098, 11351, 24291, 37930, 32121, 11366, - 24293, 37926, 32154, 11362, 24243, 37924, 32112, 11360, 24244, 37925, - 32113, 11361, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 24292, 37929, 32122, 11365, 29406, 29379, 33977, 33990, 24106, 37771, - 40951, 40951, 40951, 40951, 40951, 40951, 39310, 39325, 39315, 39320, - 39335, 39330, 39340, 39345, 39312, 39327, 39317, 39322, 39337, 39332, - 39342, 39347, 39311, 39326, 39316, 39321, 39336, 39331, 39341, 39346, - 39308, 39323, 39313, 39318, 39333, 39328, 39338, 39343, 39309, 39324, - 39314, 39319, 39334, 39329, 39339, 39344, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 24259, 37944, 32138, 11342, 24273, 37954, - 32153, 11347, 24217, 37902, 32080, 11313, 19695, 19698, 19697, 19696, - 24227, 32087, 24262, 32123, 24284, 32118, 24288, 32114, 24226, 32086, - 24282, 32097, 39154, 39153, 40951, 40951, 2517, 2514, 32067, 11326, - 29034, 29035, 29042, 29036, 29398, 29370, 33969, 33982, 40951, 40951, - 40951, 40951, 24223, 32058, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 6535, 6536, 6534, 24071, 24069, - 24070, 24072, 24068, 11304, 11306, 11305, 11307, 31467, 39226, 4940, - 31468, 40760, 28003, 17406, 29283, 36994, 17389, 32235, 20454, 33626, - 5261, 31771, 24180, 32021, 19113, 19109, 20824, 17387, 8051, 28959, - 32179, 11379, 25330, 17414, 33868, 17397, 18666, 18665, 17408, 32659, - 33859, 17394, 32845, 31618, 4928, 31059, 32667, 31672, 25576, 28189, - 32850, 31301, 20930, 17437, 27604, 39375, 39129, 19111, 11001, 39349, - 11380, 7995, 37689, 34110, 18010, 32168, 17457, 32562, 36995, 4548, - 25738, 9949, 22565, 33881, 17485, 8695, 2597, 9956, 2615, 31603, 6064, - 2618, 18662, 32671, 34526, 16648, 18005, 31288, 22554, 31060, 11526, - 17500, 35368, 6512, 4370, 9942, 8055, 4939, 31478, 31668, 9957, 32441, - 6000, 24035, 25665, 28183, 2619, 33860, 39396, 33862, 17399, 17412, - 30873, 17521, 29286, 10925, 17417, 17401, 32531, 22563, 18040, 20392, - 17466, 8702, 25106, 32536, 37665, 37757, 11541, 11527, 3470, 32768, - 30884, 17513, 5005, 10830, 18039, 25107, 31903, 32849, 34480, 17908, - 40754, 20283, 32527, 34927, 8681, 21216, 25336, 31285, 20394, 31217, - 31749, 25268, 28187, 27576, 2617, 34740, 25573, 11371, 33790, 30830, - 30555, 33768, 17471, 30879, 3538, 3766, 32558, 18676, 31685, 16739, - 16740, 16742, 16741, 4554, 24400, 17491, 37450, 34733, 34734, 32370, - 11534, 28192, 25662, 26861, 26862, 6300, 9944, 32373, 3655, 17717, 30529, - 17424, 38977, 5004, 26825, 20467, 4942, 37574, 34502, 22558, 10829, - 17391, 101, 6478, 30516, 3534, 31309, 31303, 31312, 31302, 25340, 17430, - 38517, 27409, 16737, 17903, 40946, 4926, 30554, 3758, 32533, 18008, 8677, - 33876, 31792, 17452, 20933, 36767, 31322, 11528, 8457, 2599, 17449, - 37452, 4935, 25339, 25271, 25150, 34109, 2761, 32371, 36655, 4941, 3431, - 32178, 3429, 34113, 31778, 28961, 29066, 29077, 29080, 29069, 29059, - 29074, 39165, 3791, 29060, 39162, 39178, 39181, 39157, 39170, 39175, - 3788, 3804, 3807, 3783, 3796, 3801, 29067, 29078, 29081, 29070, 29065, - 29075, 39166, 3792, 29061, 39184, 39187, 39188, 39183, 39185, 39186, - 3810, 3813, 3814, 3809, 3811, 3812, 29084, 29087, 29088, 29083, 29085, - 29086, 39167, 3793, 29062, 39163, 39179, 39182, 39158, 39168, 39176, - 3789, 3805, 3808, 3784, 3794, 3802, 29068, 29079, 29082, 29071, 29063, - 29076, 39169, 3795, 29064, 39159, 3785, 29072, 39160, 3786, 29073, 39172, - 39173, 39171, 3798, 3799, 3797, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 39391, 39394, 39390, 39392, - 39389, 39388, 39393, 39384, 39387, 39383, 39385, 39382, 39381, 39386, - 40951, 40951, 2762, 30528, 4934, 32843, 36997, 24415, 18663, 31472, - 11377, 99, 34507, 39380, 8698, 40951, 40951, 40951, 40668, 22557, 31068, - 4258, 25338, 31473, 29056, 25668, 17480, 19656, 40951, 40951, 40951, - 40951, 40951, 32846, 32163, 6135, 31776, 2602, 11004, 3427, 27583, 1, - 25135, 8676, 6062, 32539, 22567, 20447, 27599, 39352, 31586, 32664, - 22559, 5008, 28200, 37451, 19686, 31481, 32173, 27600, 20473, 25156, - 19107, 17489, 18977, 21696, 17481, 39369, 3541, 8053, 31605, 39371, - 17432, 25152, 8650, 16751, 29057, 20464, 20926, 39355, 24033, 18042, 956, - 25273, 31324, 31620, 31619, 31307, 17445, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 28186, 11189, 4246, 3544, 30518, 17467, 35784, - 17512, 36654, 31607, 3539, 20925, 17905, 31289, 32216, 40951, 40951, - 34108, 27067, 32375, 17396, 17400, 17411, 11199, 3763, 4943, 32819, - 17407, 40951, 40951, 40951, 40951, 40951, 40951, 19110, 32111, 24242, - 31025, 31026, 20634, 19693, 24287, 32117, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 4182, 4212, 4183, 4227, 4198, 4216, 4184, 4235, - 4205, 4213, 4191, 4228, 4199, 4217, 4185, 4239, 4209, 4224, 4195, 4232, - 4221, 4188, 4236, 4206, 4214, 4192, 4229, 4200, 4218, 4186, 4241, 4211, - 4226, 4197, 4234, 4204, 4223, 4190, 4238, 4208, 4194, 4231, 4202, 4220, - 4187, 4240, 4210, 4225, 4196, 4233, 4203, 4222, 4189, 4237, 4207, 4215, - 4193, 4230, 4201, 4219, 25173, 25174, 25181, 25183, 25178, 25239, 25240, - 25236, 25238, 25234, 25237, 25230, 25233, 25231, 25235, 25232, 25177, - 25180, 25175, 25179, 25176, 25182, 37854, 37855, 37862, 37864, 37859, - 37824, 37825, 37821, 37823, 37819, 37822, 37815, 37818, 37816, 37820, - 37817, 37858, 37861, 37856, 37860, 37857, 37863, 37797, 24036, 37794, - 24041, 24127, 37893, 32004, 25265, 38841, 38842, 38843, 38844, 38845, - 38846, 20409, 20410, 20411, 20412, 20413, 20414, 24037, 24042, 31917, - 31916, 37796, 20408, 37852, 37889, 37804, 37892, 37888, 31966, 32000, - 31944, 31999, 31976, 24085, 31959, 37811, 25166, 20809, 37806, 37808, - 40951, 24084, 6298, 20805, 19733, 37829, 37884, 37795, 24038, 37830, - 37885, 25226, 25203, 4454, 4458, 4446, 4452, 4455, 4460, 4447, 4449, - 4457, 4459, 4461, 4456, 4450, 4451, 4477, 4487, 2520, 20806, 24079, - 31954, 20807, 24196, 32055, 11321, 37897, 24076, 31951, 38951, 31968, - 29006, 29005, 29007, 39229, 24130, 27578, 31995, 29037, 34508, 34509, - 34511, 34512, 34510, 39297, 39202, 31767, 3903, 24137, 24092, 4453, 4474, - 4469, 4448, 4464, 4463, 4471, 4462, 4467, 4473, 4444, 4468, 4465, 4475, - 4445, 4470, 37476, 31962, 4364, 24151, 37803, 25250, 27579, 27580, 37475, - 31961, 4363, 24150, 37485, 4350, 4357, 37477, 32572, 32574, 32571, 32570, - 32567, 32566, 32569, 32568, 32575, 32573, 40951, 40951, 40951, 40951, - 40951, 40951, 6854, 6855, 6856, 6857, 6858, 6859, 6860, 6861, 6862, 6863, - 6864, 6865, 6866, 6867, 6868, 6869, 6870, 6871, 6872, 6873, 6874, 6875, - 6876, 6877, 6878, 6879, 6880, 6881, 6882, 6883, 6884, 6885, 6886, 6887, - 6888, 6889, 6890, 6891, 6892, 6893, 6894, 6895, 6896, 6897, 6898, 6899, - 6900, 6901, 6902, 6903, 6904, 6905, 6906, 6907, 6908, 6909, 6910, 6911, - 6912, 6913, 6914, 6915, 6916, 6917, 6918, 6919, 6920, 6921, 6922, 6923, - 6924, 6925, 6926, 6927, 6928, 6929, 6930, 6931, 6932, 6933, 6934, 6935, - 6936, 6937, 6938, 6939, 6940, 6941, 6942, 6943, 6944, 6945, 6946, 6947, - 6948, 6949, 6950, 6951, 6952, 6953, 6954, 6955, 6956, 6957, 6958, 6959, - 6960, 6961, 6962, 6963, 6964, 6965, 6966, 6967, 6968, 6969, 6970, 6971, - 6972, 6973, 6974, 6975, 6976, 6977, 6978, 6979, 6980, 6981, 6982, 6983, - 6984, 6985, 6986, 6987, 6988, 6989, 6990, 6991, 6992, 6993, 6994, 6995, - 6996, 6997, 6998, 6999, 7000, 7001, 7002, 7003, 7004, 7005, 7006, 7007, - 7008, 7009, 7010, 7011, 7012, 7013, 7014, 7015, 7016, 7017, 7018, 7019, - 7020, 7021, 7022, 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, 7031, - 7032, 7033, 7034, 7035, 7036, 7037, 7038, 7039, 7040, 7041, 7042, 7043, - 7044, 7045, 7046, 7047, 7048, 7049, 7050, 7051, 7052, 7053, 7054, 7055, - 7056, 7057, 7058, 7059, 7060, 7061, 7062, 7063, 7064, 7065, 7066, 7067, - 7068, 7069, 7070, 7071, 7072, 7073, 7074, 7075, 7076, 7077, 7078, 7079, - 7080, 7081, 7082, 7083, 7084, 7085, 7086, 7087, 7088, 7089, 7090, 7091, - 7092, 7093, 7094, 7095, 7096, 7097, 7098, 7099, 7100, 7101, 7102, 7103, - 7104, 7105, 7106, 7107, 7108, 7109, 7110, 7111, 7112, 7113, 7114, 7115, - 7116, 7117, 7118, 7119, 7120, 7121, 7122, 7123, 7124, 7125, 7126, 7127, - 7128, 7129, 7130, 7131, 7132, 7133, 7134, 7135, 7136, 7137, 7138, 7139, - 7140, 7141, 7142, 7143, 7144, 7145, 7146, 7147, 7148, 7149, 7150, 7151, - 7152, 7153, 7154, 7155, 7156, 7157, 7158, 7159, 7160, 7161, 7162, 7163, - 7164, 7165, 7166, 7167, 7168, 7169, 7170, 7171, 7172, 7173, 7174, 7175, - 7176, 7177, 7178, 7179, 7180, 7181, 7182, 7183, 7184, 7185, 7186, 7187, - 7188, 7189, 7190, 7191, 7192, 7193, 7194, 7195, 7196, 7197, 7198, 7199, - 7200, 7201, 7202, 7203, 7204, 7205, 7206, 7207, 7208, 7209, 7210, 7211, - 7212, 7213, 7214, 7215, 7216, 7217, 7218, 7219, 7220, 7221, 7222, 7223, - 7224, 7225, 7226, 7227, 7228, 7229, 7230, 7231, 7232, 7233, 7234, 7235, - 7236, 7237, 7238, 7239, 7240, 7241, 7242, 7243, 7244, 7245, 7246, 7247, - 7248, 7249, 7250, 7251, 7252, 7253, 7254, 7255, 7256, 7257, 7258, 7259, - 7260, 7261, 7262, 7263, 7264, 7265, 7266, 7267, 7268, 7269, 7270, 7271, - 7272, 7273, 7274, 7275, 7276, 7277, 7278, 7279, 7280, 7281, 7282, 7283, - 7284, 7285, 7286, 7287, 7288, 7289, 7290, 7291, 7292, 7293, 7294, 7295, - 7296, 7297, 7298, 7299, 7300, 7301, 7302, 7303, 7304, 7305, 7306, 7307, - 7308, 7309, 7310, 7311, 7312, 7313, 7314, 7315, 7316, 7317, 7318, 7319, - 7320, 7321, 7322, 7323, 7324, 7325, 7326, 7327, 7328, 7329, 7330, 7331, - 7332, 7333, 7334, 7335, 7336, 7337, 7338, 7339, 7340, 7341, 7342, 7343, - 7344, 7345, 7346, 7347, 7348, 7349, 7350, 7351, 7352, 7353, 7354, 7355, - 7356, 7357, 7358, 7359, 7360, 7361, 7362, 7363, 7364, 7365, 6838, 6839, - 6840, 6841, 6842, 6843, 6844, 6845, 6846, 6847, 6848, 6849, 6850, 6851, - 6852, 6853, 6824, 6825, 6826, 6827, 6828, 6829, 6830, 6831, 6832, 6833, - 6834, 6835, 6836, 6837, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 24032, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 35020, 34949, 35012, 35021, 34937, 35010, 34931, 34930, 35007, - 35015, 34932, 35011, 34935, 34952, 35023, 35019, 34944, 34946, 34943, - 34942, 34939, 34938, 34941, 34940, 34947, 34945, 34936, 35018, 35005, - 34948, 34951, 35013, 34934, 34953, 34954, 34955, 34956, 34957, 34958, - 34959, 34960, 34961, 34962, 34963, 34964, 34965, 34966, 34967, 34968, - 34969, 34970, 34971, 34972, 34973, 34974, 34975, 34976, 34977, 34978, - 35008, 35017, 35016, 34933, 35009, 34950, 34979, 34980, 34981, 34982, - 34983, 34984, 34985, 34986, 34987, 34988, 34989, 34990, 34991, 34992, - 34993, 34994, 34995, 34996, 34997, 34998, 34999, 35000, 35001, 35002, - 35003, 35004, 35006, 35024, 35014, 35022, 5995, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 38339, 38350, 38361, 38373, 38384, - 38395, 38406, 38417, 38428, 38436, 38437, 38438, 38439, 38441, 38442, - 38443, 38444, 38445, 38446, 38447, 38448, 38449, 38450, 38452, 38453, - 38454, 38455, 38456, 38457, 38458, 38459, 38460, 38461, 38463, 38464, - 38465, 38466, 38467, 38468, 38469, 38470, 38471, 38472, 38474, 38475, - 38476, 38477, 38478, 38479, 38480, 38481, 38482, 38483, 38485, 38486, - 38487, 38488, 38489, 38490, 38491, 38492, 38493, 38494, 38496, 38497, - 38498, 38499, 38500, 38501, 38502, 38503, 38504, 38505, 38507, 38508, - 38509, 38510, 38511, 38512, 38513, 38514, 38515, 38516, 38263, 38264, - 38265, 38266, 38267, 38268, 38269, 38270, 38271, 38272, 38274, 38275, - 38276, 38277, 38278, 38279, 38280, 38281, 38282, 38283, 38285, 38286, - 38287, 38288, 38289, 38290, 38291, 38292, 38293, 38294, 38296, 38297, - 38298, 38299, 38300, 38301, 38302, 38303, 38304, 38305, 38307, 38308, - 38309, 38310, 38311, 38312, 38313, 38314, 38315, 38316, 38318, 38319, - 38320, 38321, 38322, 38323, 38324, 38325, 38326, 38327, 38329, 38330, - 38331, 38332, 38333, 38334, 38335, 38336, 38337, 38338, 38340, 38341, - 38342, 38343, 38344, 38345, 38346, 38347, 38348, 38349, 38351, 38352, - 38353, 38354, 38355, 38356, 38357, 38358, 38359, 38360, 38362, 38363, - 38364, 38365, 38366, 38367, 38368, 38369, 38370, 38371, 38374, 38375, - 38376, 38377, 38378, 38379, 38380, 38381, 38382, 38383, 38385, 38386, - 38387, 38388, 38389, 38390, 38391, 38392, 38393, 38394, 38396, 38397, - 38398, 38399, 38400, 38401, 38402, 38403, 38404, 38405, 38407, 38408, - 38409, 38410, 38411, 38412, 38413, 38414, 38415, 38416, 38418, 38419, - 38420, 38421, 38422, 38423, 38424, 38425, 38426, 38427, 38429, 38430, - 38431, 38432, 38433, 38434, 38435, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 29854, 29853, 34498, 34077, 34499, 34529, 16774, 17347, 16772, - 16785, 16778, 16777, 3, 2, 347, 3542, 2612, 5258, 6290, 20419, 20449, - 34928, 24495, 29177, 16775, 25337, 29928, 16783, 24497, 38850, 38955, - 17497, 17647, 6063, 8678, 32821, 25111, 33874, 32822, 25110, 32853, - 10543, 11530, 10827, 10544, 10826, 10545, 10825, 10546, 10823, 10547, - 29045, 28963, 34833, 34832, 16773, 17346, 5993, 5266, 16771, 16784, - 16738, 34560, 34530, 16819, 16818, 20706, 17442, 17645, 20707, 18670, - 18976, 20708, 31789, 32368, 20709, 37754, 37960, 34079, 10559, 10556, - 30887, 30886, 20286, 20448, 4927, 5257, 29350, 28965, 20633, 20632, - 29279, 29284, 34495, 34483, 16770, 16822, 6292, 20421, 20451, 6291, - 20420, 20450, 24498, 38851, 38956, 31210, 31209, 31587, 31211, 31212, - 31567, 31869, 31876, 31904, 33639, 33640, 34481, 33638, 33641, 34482, - 10824, 10548, 31676, 31677, 31724, 31675, 31678, 31725, 32662, 34528, - 5994, 10528, 27410, 28789, 34494, 34497, 34080, 16769, 16767, 17384, - 34496, 34078, 33633, 34925, 33632, 32557, 8472, 10527, 34519, 34484, - 30547, 30768, 31674, 31726, 1059, 1060, 28964, 32852, 22893, 23515, - 10526, 2313, 361, 34911, 21230, 22570, 22571, 22613, 22579, 37100, 19375, - 19376, 19353, 19374, 17641, 17642, 17643, 28787, 17644, 34585, 40949, - 40948, 40950, 25333, 32171, 25331, 32169, 31283, 25334, 32172, 29927, - 28788, 39378, 25332, 32170, 17646, 31284, 39147, 27571, 27572, 24248, - 32127, 39885, 28575, 38518, 38629, 38697, 38708, 38719, 38730, 38741, - 38752, 38763, 38519, 38530, 38541, 38552, 38563, 38574, 38585, 31643, - 5256, 4549, 40947, 9642, 9641, 9337, 2827, 26869, 26867, 26927, 26925, - 20199, 5093, 27245, 27248, 38596, 38607, 38618, 38630, 38641, 38652, - 38663, 38674, 38685, 38693, 38694, 38695, 38696, 38698, 38699, 38700, - 38701, 38702, 38703, 38704, 38705, 38706, 38707, 38709, 38710, 38711, - 38712, 38713, 38714, 38715, 38716, 38717, 38718, 38720, 38721, 38722, - 38723, 38724, 38725, 38726, 38727, 38728, 38729, 38731, 38732, 38733, - 38734, 38735, 38736, 38737, 38738, 38739, 38740, 38742, 38743, 38744, - 38745, 38746, 38747, 38748, 38749, 38750, 38751, 38753, 38754, 38755, - 38756, 38757, 38758, 38759, 38760, 38761, 38762, 38764, 38765, 38766, - 38767, 38768, 38769, 38770, 38771, 38772, 38773, 38520, 38521, 38522, - 38523, 38524, 38525, 38526, 38527, 38528, 38529, 38531, 38532, 38533, - 38534, 38535, 38536, 38537, 38538, 38539, 38540, 38542, 38543, 38544, - 38545, 38546, 38547, 38548, 38549, 38550, 38551, 38553, 38554, 38555, - 38556, 38557, 38558, 38559, 38560, 38561, 38562, 38564, 38565, 38566, - 38567, 38568, 38569, 38570, 38571, 38572, 38573, 38575, 38576, 38577, - 38578, 38579, 38580, 38581, 38582, 38583, 38584, 38586, 38587, 38588, - 38589, 38590, 38591, 38592, 38593, 38594, 38595, 38597, 38598, 38599, - 38600, 38601, 38602, 38603, 38604, 38605, 38606, 38608, 38609, 38610, - 38611, 38612, 38613, 38614, 38615, 38616, 38617, 38619, 38620, 38621, - 38622, 38623, 38624, 38625, 38626, 38627, 38628, 38631, 38632, 38633, - 38634, 38635, 38636, 38637, 38638, 38639, 38640, 38642, 38643, 38644, - 38645, 38646, 38647, 38648, 38649, 38650, 38651, 38653, 38654, 38655, - 38656, 38657, 38658, 38659, 38660, 38661, 38662, 38664, 38665, 38666, - 38667, 38668, 38669, 38670, 38671, 38672, 38673, 38675, 38676, 38677, - 38678, 38679, 38680, 38681, 38682, 38683, 38684, 38686, 38687, 38688, - 38689, 38690, 38691, 38692, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 21614, - 21615, 21622, 21624, 21621, 21620, 21617, 21616, 21619, 21618, 21625, - 21623, 22758, 23321, 22917, 23546, 23156, 23919, 22852, 23456, 22853, - 23457, 22854, 23458, 23028, 23697, 23029, 23698, 23030, 23699, 23095, - 23795, 22836, 23439, 22832, 23435, 23541, 23665, 22768, 23331, 22769, - 23332, 22857, 23462, 22858, 23463, 22842, 23445, 22843, 23446, 23540, - 23542, 22922, 23548, 22923, 23549, 22940, 23576, 22970, 23616, 22978, - 23638, 23068, 23756, 23160, 23923, 23161, 23924, 23157, 23920, 23158, - 23921, 23340, 23714, 23715, 23874, 23875, 23804, 23805, 23530, 23531, - 2283, 2286, 2284, 2288, 2290, 2287, 2289, 2285, 2291, 10756, 10751, - 10750, 10757, 10752, 10753, 10755, 10754, 3614, 3613, 3615, 18884, 18883, - 18882, 18881, 18886, 18885, 30619, 30618, 3559, 35374, 35382, 35391, - 35383, 35385, 35380, 35384, 35379, 35395, 35394, 35397, 35389, 35376, - 35396, 35378, 35377, 35390, 35381, 35393, 35387, 35388, 35386, 35392, - 35375, 35513, 35520, 35521, 35516, 35517, 35522, 35523, 35514, 35518, - 35519, 35515, 35579, 35586, 35587, 35582, 35583, 35588, 35589, 35580, - 35584, 35585, 35581, 35690, 35697, 35698, 35693, 35694, 35699, 35700, - 35691, 35695, 35696, 35692, 35590, 35597, 35598, 35593, 35594, 35599, - 35600, 35591, 35595, 35596, 35592, 35668, 35675, 35676, 35671, 35672, - 35677, 35678, 35669, 35673, 35674, 35670, 35568, 35575, 35576, 35571, - 35572, 35577, 35578, 35569, 35573, 35574, 35570, 35679, 35686, 35687, - 35682, 35683, 35688, 35689, 35680, 35684, 35685, 35681, 35601, 35608, - 35609, 35604, 35605, 35610, 35611, 35602, 35606, 35607, 35603, 35734, - 35741, 35742, 35737, 35738, 35743, 35744, 35735, 35739, 35740, 35736, - 35723, 35730, 35731, 35726, 35727, 35732, 35733, 35724, 35728, 35729, - 35725, 35756, 35763, 35764, 35759, 35760, 35765, 35766, 35757, 35761, - 35762, 35758, 35623, 35630, 35631, 35626, 35627, 35632, 35633, 35624, - 35628, 35629, 35625, 35546, 35553, 35554, 35549, 35550, 35555, 35556, - 35547, 35551, 35552, 35548, 35745, 35752, 35753, 35748, 35749, 35754, - 35755, 35746, 35750, 35751, 35747, 35524, 35531, 35532, 35527, 35528, - 35533, 35534, 35525, 35529, 35530, 35526, 35535, 35542, 35543, 35538, - 35539, 35544, 35545, 35536, 35540, 35541, 35537, 35612, 35619, 35620, - 35615, 35616, 35621, 35622, 35613, 35617, 35618, 35614, 35557, 35564, - 35565, 35560, 35561, 35566, 35567, 35558, 35562, 35563, 35559, 35712, - 35719, 35720, 35715, 35716, 35721, 35722, 35713, 35717, 35718, 35714, - 35634, 35642, 35643, 35637, 35638, 35644, 35645, 35635, 35639, 35640, - 35636, 35646, 35653, 35654, 35649, 35650, 35655, 35656, 35647, 35651, - 35652, 35648, 35657, 35664, 35665, 35660, 35661, 35666, 35667, 35658, - 35662, 35663, 35659, 35701, 35708, 35709, 35704, 35705, 35710, 35711, - 35702, 35706, 35707, 35703, 35501, 35502, 35509, 35510, 35505, 35506, - 35511, 35512, 35503, 35507, 35508, 35504, 35641, 33664, 33662, 33663, - 17807, 22179, 22177, 22180, 22178, 22169, 22175, 22173, 22176, 22174, - 22170, 22190, 22186, 22191, 22187, 22171, 22188, 22184, 22189, 22185, - 22172, 22201, 22181, 22183, 22182, 22197, 22200, 22198, 22193, 22199, - 22194, 22195, 22196, 22202, 22192, 22341, 22218, 22219, 22220, 22217, - 22336, 22328, 20316, 20318, 20320, 20317, 20319, 21319, 21321, 21323, - 21320, 21322, 21312, 21311, 21310, 21313, 27780, 27785, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, - 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, 40951, + 82, 35536, 35536, 35536, 35536, 93, 95, 92, 91, 88, 87, 90, 89, 96, 94, + 35536, 35536, 35536, 35536, 80, 81, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 15849, 15835, + 15830, 15810, 15805, 15823, 15818, 15798, 15813, 15838, 15833, 15828, + 15808, 15803, 15824, 15819, 15799, 15814, 15850, 15836, 15831, 15811, + 15806, 15826, 15821, 15801, 15816, 15851, 15837, 15832, 15812, 15807, + 15827, 15822, 15802, 15817, 15839, 15834, 15829, 15809, 15804, 15825, + 15820, 15800, 15815, 15796, 15797, 15787, 15794, 15795, 15847, 15845, + 15844, 15841, 15840, 15843, 15842, 15848, 15846, 15853, 15789, 15788, + 15790, 15852, 15793, 15792, 15791, 15786, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 25132, 25127, 25122, 25102, 25097, 25115, 25110, 25090, + 25105, 25130, 25125, 25120, 25100, 25095, 25116, 25111, 25091, 25106, + 25133, 25128, 25123, 25103, 25098, 25118, 25113, 25093, 25108, 25134, + 25129, 25124, 25104, 25099, 25119, 25114, 25094, 25109, 25131, 25126, + 25121, 25101, 25096, 25117, 25112, 25092, 25107, 25089, 25082, 25084, + 25074, 25076, 25077, 25079, 25086, 25085, 25080, 25075, 25078, 25083, + 25081, 25088, 25087, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 2288, 2296, 2294, 2192, + 35536, 2304, 2292, 2300, 2284, 2299, 2291, 2240, 2295, 2302, 2267, 2289, + 2297, 2268, 2303, 2298, 2266, 2287, 2286, 2290, 2285, 2191, 2293, 2301, + 2162, 2164, 2163, 2165, 35536, 2204, 2202, 35536, 2199, 35536, 35536, + 2198, 35536, 2206, 2201, 2209, 2203, 2208, 2196, 2211, 2205, 2197, 2210, + 35536, 2195, 2194, 2193, 2200, 35536, 2212, 35536, 2207, 35536, 35536, + 35536, 35536, 35536, 35536, 2276, 35536, 35536, 35536, 35536, 2278, + 35536, 2277, 35536, 2281, 35536, 2280, 2273, 2283, 35536, 2274, 2282, + 35536, 2272, 35536, 35536, 2275, 35536, 2271, 35536, 2279, 35536, 2269, + 35536, 2270, 35536, 2258, 2256, 35536, 2253, 35536, 35536, 2252, 2247, + 2260, 2255, 35536, 2257, 2263, 2250, 2265, 2259, 2251, 2264, 35536, 2249, + 2248, 2246, 2254, 35536, 2245, 2261, 2262, 2243, 35536, 2244, 35536, + 2217, 2229, 2227, 2237, 2220, 2239, 2225, 2219, 2223, 2232, 35536, 2235, + 2228, 2234, 2214, 2218, 2230, 2215, 2238, 2231, 2213, 2224, 2222, 2216, + 2221, 2236, 2226, 2233, 35536, 35536, 35536, 35536, 35536, 2178, 2176, + 2187, 35536, 2190, 2174, 2182, 2172, 2181, 35536, 2185, 2177, 2184, 2167, + 2189, 2179, 2168, 2188, 2180, 2166, 2173, 2171, 2169, 2170, 2186, 2175, + 2183, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 2241, 2242, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 20081, + 20097, 20112, 20088, 20116, 20115, 20113, 20093, 20110, 20107, 20086, + 20083, 20102, 20099, 20079, 20090, 20094, 20111, 20108, 20087, 20084, + 20103, 20100, 20080, 20091, 20092, 20109, 20106, 20085, 20082, 20101, + 20098, 20078, 20089, 20096, 20095, 20076, 20119, 20105, 20104, 20117, + 20114, 20118, 20077, 35536, 35536, 35536, 35536, 10257, 10208, 10209, + 10210, 10211, 10212, 10213, 10214, 10215, 10216, 10217, 10218, 10219, + 10220, 10221, 10222, 10223, 10224, 10225, 10226, 10227, 10228, 10229, + 10230, 10231, 10232, 10233, 10234, 10235, 10236, 10237, 10238, 10239, + 10240, 10241, 10242, 10243, 10244, 10245, 10246, 10247, 10248, 10249, + 10250, 10251, 10252, 10253, 10254, 10255, 10256, 10307, 10258, 10259, + 10260, 10261, 10262, 10263, 10264, 10265, 10266, 10267, 10268, 10269, + 10270, 10271, 10272, 10273, 10274, 10275, 10276, 10277, 10278, 10279, + 10280, 10281, 10282, 10283, 10284, 10285, 10286, 10287, 10288, 10289, + 10290, 10291, 10292, 10293, 10294, 10295, 10296, 10297, 10298, 10299, + 10300, 10301, 10302, 10303, 10304, 10305, 10306, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 25795, + 25865, 25840, 25836, 25799, 25804, 25824, 25820, 25816, 25869, 25832, + 25873, 25808, 25828, 25812, 35536, 35536, 25866, 25841, 25837, 25800, + 25805, 25825, 25821, 25817, 25870, 25833, 25874, 25809, 25829, 25813, + 25796, 35536, 25867, 25842, 25838, 25801, 25806, 25826, 25822, 25818, + 25871, 25834, 25875, 25810, 25830, 25814, 25794, 35536, 25864, 25839, + 25835, 25798, 25803, 25823, 25819, 25815, 25868, 25831, 25872, 25807, + 25827, 25811, 25797, 25802, 25846, 25843, 25857, 25858, 25859, 25860, + 25861, 25862, 25863, 25847, 25848, 25849, 25850, 25851, 25852, 25853, + 25854, 25855, 25856, 25844, 25845, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 9992, 9991, 9976, 9988, 9985, 9970, + 9967, 9982, 9979, 9994, 9973, 10033, 10012, 6918, 6635, 6659, 25463, + 25464, 25465, 25466, 25467, 25468, 25469, 25470, 25471, 25472, 25473, + 25474, 25475, 25476, 25477, 25478, 25479, 25480, 25481, 25482, 25483, + 25484, 25485, 25486, 25487, 25488, 32073, 6749, 6750, 6646, 6917, 7764, + 28768, 28769, 28770, 28771, 28772, 28773, 28774, 28775, 28776, 28777, + 28778, 28779, 28780, 28781, 28782, 28783, 28784, 28785, 28786, 28787, + 28788, 28789, 28790, 28791, 28792, 28793, 28762, 28798, 28813, 28814, + 28803, 28825, 23677, 23678, 23679, 23680, 23681, 23682, 23683, 23684, + 23685, 23686, 23687, 23688, 23689, 23690, 23691, 23692, 23693, 23694, + 23695, 23696, 23697, 23698, 23699, 23700, 23701, 23702, 26074, 26075, + 26076, 6645, 6644, 6692, 23708, 23709, 23710, 23711, 23712, 23713, 23714, + 23715, 23716, 23717, 23718, 23719, 23720, 23721, 23722, 23723, 23724, + 23725, 23726, 23727, 23728, 23729, 23730, 23731, 23732, 23733, 7808, + 23740, 23743, 23744, 23739, 23741, 28497, 28751, 28750, 28757, 28826, + 28799, 28800, 28802, 28812, 28820, 28823, 28815, 28805, 28817, 28755, + 28819, 28756, 28806, 28816, 28808, 28801, 28767, 28761, 28760, 28759, + 28796, 28807, 28821, 28822, 20511, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 26104, 26105, 26106, 26107, 26108, 26109, 26110, 26111, 26112, + 26113, 26114, 26115, 26116, 26117, 26118, 26119, 26120, 26121, 26122, + 26123, 26124, 26125, 26126, 26127, 26128, 26129, 28538, 28763, 28765, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 28729, 28724, 28716, 28764, 28710, 28722, 28745, + 28721, 28709, 28734, 28742, 28733, 28714, 28725, 28726, 28732, 28713, + 28743, 28740, 28747, 28723, 28718, 28737, 28727, 28730, 28707, 28708, + 28749, 28720, 28711, 28715, 28731, 28746, 28728, 28741, 28744, 28717, + 28738, 28736, 28735, 28739, 28712, 28719, 28748, 35536, 35536, 35536, + 35536, 32070, 32064, 32065, 32067, 32071, 32068, 32072, 32066, 32069, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 6697, 6695, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 26540, 26541, 26538, 26542, 26537, 26539, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 9643, 12618, 7128, 23987, 29001, 29000, 6928, 29046, + 26067, 5033, 33707, 33532, 22284, 10657, 10655, 10656, 13154, 23792, + 33619, 12583, 33620, 12661, 33618, 17425, 33617, 7798, 23791, 12582, + 17424, 12660, 28925, 13155, 27144, 31509, 3930, 33866, 33870, 33867, + 33868, 7141, 7140, 7139, 7138, 12617, 33933, 15586, 31167, 5109, 6579, + 26874, 12510, 9669, 25331, 6226, 15583, 32241, 6576, 26528, 15543, 29047, + 4344, 10651, 10652, 15368, 12637, 20363, 12550, 18722, 22999, 32023, + 2586, 13267, 21766, 33710, 30190, 19088, 3475, 25779, 26101, 13807, + 25599, 25596, 6578, 28865, 14257, 28130, 21556, 25915, 26224, 26225, + 7598, 9070, 28853, 28429, 5031, 12651, 26544, 9652, 25189, 29087, 12659, + 12589, 28226, 27088, 15617, 10403, 7597, 6618, 6101, 19966, 9073, 15549, + 27163, 3699, 25913, 7596, 12623, 31169, 26841, 33935, 7145, 32154, 3589, + 7087, 2650, 12624, 4468, 25903, 26215, 33955, 3860, 15978, 6619, 12556, + 12579, 12578, 2793, 25521, 7577, 30189, 7809, 25778, 15983, 6163, 33932, + 22882, 26846, 13192, 14838, 4470, 22283, 26168, 23005, 28874, 19087, + 7589, 3579, 3580, 12572, 98, 6161, 12562, 26486, 12587, 22273, 22904, + 6621, 14837, 2565, 32048, 6926, 31916, 7083, 25628, 33454, 10038, 28136, + 3859, 12850, 4473, 12606, 23240, 22986, 26840, 13823, 23004, 32159, + 33459, 23239, 26669, 31284, 28111, 3481, 6580, 28220, 26670, 29085, + 28460, 32156, 15581, 372, 26547, 29090, 33728, 13191, 26057, 26058, 7800, + 33533, 12593, 15619, 29278, 28217, 5367, 3585, 5108, 15593, 6927, 9696, + 7084, 9778, 9779, 23658, 28851, 15592, 15591, 25185, 15980, 12475, 15594, + 3470, 2583, 15587, 19850, 7592, 26845, 9695, 12546, 15976, 15979, 12474, + 33835, 3983, 33716, 33715, 26529, 3999, 17247, 2661, 4477, 370, 11868, + 11869, 11870, 11871, 11872, 26082, 22899, 25192, 33708, 7791, 31814, + 18984, 26084, 6168, 10493, 7812, 33889, 28215, 28213, 15580, 26087, + 13159, 27169, 22880, 26527, 6587, 10144, 25770, 4655, 11837, 24251, + 28454, 5046, 966, 15548, 17249, 12586, 32155, 4345, 32266, 14806, 2649, + 12655, 3861, 25600, 17244, 25925, 10495, 2659, 10206, 22901, 7792, 31815, + 26085, 6169, 10494, 28459, 15582, 22881, 10205, 25772, 12658, 14255, + 33954, 3584, 25368, 25771, 25589, 6586, 12507, 12505, 10650, 24140, + 22902, 32024, 33877, 33792, 33818, 33842, 12590, 33717, 25188, 31542, + 31543, 7082, 24842, 7814, 33945, 12506, 23982, 29275, 16080, 10501, + 17239, 3865, 33943, 26030, 14261, 25920, 20357, 2580, 15439, 33944, + 33942, 12622, 5104, 5103, 4653, 13054, 20270, 33940, 12554, 20276, 32281, + 32282, 25900, 33941, 5034, 25613, 20273, 20274, 24821, 24819, 2648, 7580, + 25982, 15986, 15985, 14123, 2651, 12494, 350, 15746, 28113, 15862, 13822, + 9651, 19743, 23589, 12542, 14128, 3479, 29273, 25775, 17236, 19849, + 26466, 12854, 17231, 4469, 7790, 33726, 3586, 5040, 32275, 28430, 13820, + 14840, 4347, 13809, 33982, 26029, 14839, 26211, 14841, 9954, 11825, 964, + 4652, 28125, 7150, 28455, 10497, 9656, 25773, 12600, 10124, 28443, 31517, + 33803, 15603, 22702, 9068, 14870, 7797, 3472, 3474, 3473, 3471, 22701, + 6397, 26870, 25623, 5035, 22286, 12607, 24853, 10648, 12568, 24840, + 25196, 25198, 5364, 31170, 6106, 6396, 6398, 3477, 7088, 26032, 26535, + 25591, 28863, 32124, 4358, 19086, 24138, 24139, 7134, 24834, 13808, 4346, + 24857, 4359, 23660, 26867, 22120, 31175, 25199, 12553, 26753, 26033, + 6403, 25136, 15973, 25590, 12508, 15778, 11906, 7131, 7132, 24844, 24843, + 25909, 25906, 23977, 22300, 22301, 33450, 22302, 24057, 968, 5365, 5366, + 33453, 31182, 26060, 33455, 12571, 25904, 25996, 32269, 7118, 7119, 7115, + 977, 19851, 15434, 28438, 28437, 28439, 28440, 3578, 11823, 18853, 26342, + 19801, 7133, 16765, 19798, 24847, 3592, 3594, 4357, 19741, 26062, 2653, + 11901, 24823, 28276, 32063, 24056, 16780, 15865, 15866, 15869, 15868, + 15867, 12575, 11824, 33958, 14251, 24156, 15595, 25784, 22272, 31181, + 7819, 28107, 15984, 32126, 4010, 33853, 17415, 17342, 17350, 17343, + 28142, 28141, 32364, 10416, 32368, 10410, 19920, 32460, 6642, 7806, 7805, + 24131, 24133, 29162, 33816, 14885, 6234, 25190, 10488, 16763, 22888, + 29183, 21982, 4471, 7099, 7109, 7111, 7095, 7093, 7103, 7101, 7091, 7097, + 7105, 7089, 7107, 7100, 7110, 7112, 7096, 7094, 7104, 7102, 7092, 7098, + 7106, 7090, 7108, 26289, 26290, 26291, 5099, 5100, 26474, 4356, 6100, + 20360, 4012, 24055, 15547, 20271, 28128, 9653, 28450, 28451, 16081, + 20275, 18774, 31176, 26270, 33872, 4025, 31180, 7085, 2654, 28835, 11905, + 12616, 25603, 19740, 3980, 19882, 19868, 19880, 19881, 19902, 18834, + 32257, 26072, 26196, 26204, 26205, 26210, 26206, 26073, 33793, 27298, + 27297, 27294, 27293, 3958, 3985, 27300, 27299, 27296, 27295, 4028, 3924, + 3937, 9780, 16767, 31533, 25979, 25926, 3940, 33807, 28227, 31164, 33938, + 24831, 32270, 31529, 32108, 24645, 14804, 26851, 25980, 12552, 24854, + 10130, 10131, 10132, 12647, 12649, 12648, 3936, 12620, 24841, 6107, 6108, + 12569, 11873, 11874, 11875, 24135, 24136, 24137, 11884, 11877, 11878, + 10129, 25195, 25200, 33722, 28453, 28452, 9781, 22287, 21542, 25179, + 7117, 6098, 15780, 9670, 7572, 24818, 26485, 25197, 28861, 9650, 19742, + 28441, 31525, 31524, 31526, 31527, 18802, 26292, 32285, 31531, 18819, + 26306, 18732, 26228, 22885, 19111, 19110, 2798, 2804, 2799, 2803, 2796, + 19108, 2797, 33949, 22898, 32107, 28848, 28110, 22903, 13812, 13815, + 12532, 28201, 28203, 28202, 28204, 28200, 28199, 33936, 28205, 12512, + 26167, 28198, 28208, 28211, 23789, 12487, 32321, 12515, 25601, 7578, + 7579, 17232, 12543, 17233, 17234, 12529, 12530, 12531, 10040, 33950, 965, + 25918, 7818, 25625, 12537, 10039, 12657, 962, 12558, 33724, 28127, 31915, + 13817, 19965, 12520, 15602, 12522, 12513, 2573, 12608, 28126, 10125, + 12540, 12517, 13816, 6170, 28197, 28196, 6171, 17235, 25917, 7817, 33723, + 28132, 28131, 32472, 12536, 12525, 12519, 25622, 26875, 14843, 28436, + 14803, 25620, 25619, 25615, 25617, 24100, 28332, 24073, 28319, 32255, + 32264, 32254, 32263, 24099, 28331, 24072, 28318, 14921, 14914, 14918, + 14911, 24101, 28333, 24074, 28320, 14922, 14915, 14919, 14912, 15545, + 15546, 28272, 28273, 18977, 32506, 26429, 10483, 26861, 14916, 19090, + 14893, 14848, 29088, 26747, 26748, 26749, 14938, 26750, 14905, 33440, + 33437, 6399, 26177, 26484, 15080, 28852, 26065, 15437, 15438, 32115, + 22118, 19097, 28849, 32111, 32112, 5102, 24828, 32152, 5105, 22288, 373, + 12576, 25898, 24827, 31168, 24826, 2582, 24824, 26100, 9675, 2564, 32109, + 22879, 22895, 29086, 22896, 160, 27143, 26543, 28442, 15573, 33431, 7581, + 25899, 32123, 10489, 24053, 28212, 24058, 26031, 10487, 25911, 24062, + 3855, 24051, 3854, 22897, 25632, 24054, 6582, 21983, 33946, 26172, 2652, + 32106, 33706, 27168, 3576, 3577, 25531, 9072, 2665, 18775, 32120, 25994, + 6751, 4654, 13055, 7789, 28124, 27146, 3596, 3756, 25789, 24250, 27145, + 28876, 25201, 15541, 15605, 11838, 17251, 35536, 35536, 35536, 33939, + 25793, 33730, 26467, 14252, 27139, 24283, 22893, 26064, 22890, 32362, + 32359, 32367, 28139, 24108, 227, 228, 35536, 35536, 35536, 26752, 24825, + 9965, 25527, 26849, 22891, 6103, 28129, 12611, 28116, 2584, 25769, 26487, + 35536, 35536, 35536, 297, 245, 345, 344, 341, 239, 237, 238, 235, 236, + 334, 336, 337, 323, 290, 252, 283, 284, 285, 267, 311, 288, 338, 339, + 309, 310, 272, 325, 278, 279, 261, 302, 258, 280, 321, 259, 260, 257, + 312, 319, 340, 331, 281, 240, 320, 313, 318, 335, 300, 301, 299, 303, + 304, 305, 232, 233, 286, 314, 242, 306, 307, 243, 248, 328, 329, 298, + 249, 250, 251, 234, 346, 324, 330, 273, 342, 291, 256, 333, 255, 327, + 254, 332, 315, 282, 326, 343, 276, 244, 293, 253, 292, 241, 316, 317, + 322, 296, 270, 268, 269, 295, 294, 262, 263, 264, 265, 266, 231, 246, + 247, 308, 277, 289, 271, 287, 275, 274, 19847, 24252, 19968, 33448, 2577, + 15607, 25523, 14833, 20145, 13189, 26051, 24860, 3951, 4029, 3991, 3925, + 4015, 21657, 4353, 14935, 33441, 12491, 33772, 26536, 4023, 4016, 19105, + 21683, 4354, 14932, 33442, 12492, 33854, 33856, 28683, 4021, 4039, 3973, + 33787, 33788, 9953, 4022, 4040, 3974, 33825, 31514, 19107, 21684, 4355, + 33446, 33443, 12493, 31512, 19100, 21674, 4349, 14906, 33438, 12488, + 19093, 21663, 4352, 14881, 33436, 12490, 19101, 21673, 4350, 14910, + 33439, 12489, 19091, 21681, 4351, 14875, 33435, 19103, 21678, 31532, + 21677, 19095, 21662, 12639, 21661, 26178, 19092, 14880, 21672, 14909, + 28105, 21680, 14873, 33434, 14871, 19102, 14928, 14927, 6912, 23703, + 6922, 23704, 23985, 35536, 35536, 35536, 35536, 35536, 35536, 17349, + 17417, 17345, 17413, 17340, 17416, 17344, 17351, 17418, 17346, 17414, + 17341, 35536, 35536, 35536, 35536, 14878, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 18897, 32482, 26382, 10434, 18904, 32479, 26389, 10431, 18891, + 32478, 26379, 10430, 35536, 35536, 35536, 35536, 18896, 32481, 26381, + 10433, 18906, 32483, 26391, 10435, 14889, 14930, 14903, 14867, 14888, + 14929, 14902, 14866, 18941, 32516, 26442, 10454, 18940, 32515, 26441, + 10453, 18939, 32512, 26440, 10450, 18943, 32518, 26444, 10456, 18942, + 32517, 26443, 10455, 18971, 32493, 26408, 10470, 18979, 32508, 26431, + 10485, 18981, 32504, 26464, 10481, 18931, 32502, 26422, 10479, 18932, + 32503, 26423, 10480, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 18980, 32507, 26432, 10484, 24106, 24079, 28325, 28338, 18794, + 32349, 35536, 35536, 35536, 35536, 35536, 35536, 33892, 33907, 33897, + 33902, 33917, 33912, 33922, 33927, 33894, 33909, 33899, 33904, 33919, + 33914, 33924, 33929, 33893, 33908, 33898, 33903, 33918, 33913, 33923, + 33928, 33890, 33905, 33895, 33900, 33915, 33910, 33920, 33925, 33891, + 33906, 33896, 33901, 33916, 33911, 33921, 33926, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 18947, 32522, 26448, 10461, 18961, + 32532, 26463, 10466, 18905, 32480, 26390, 10432, 14844, 14847, 14846, + 14845, 18915, 26397, 18950, 26433, 18972, 26428, 18976, 26424, 18914, + 26396, 18970, 26407, 33734, 33733, 35536, 35536, 2560, 2557, 26377, + 10445, 23734, 23735, 23742, 23736, 24098, 24070, 28317, 28330, 35536, + 35536, 35536, 35536, 18911, 26368, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 19838, + 19842, 19843, 27153, 19834, 27150, 19836, 19837, 19826, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 6640, 6641, 6639, 18759, + 18757, 18758, 18760, 18756, 10423, 10425, 10424, 10426, 25776, 33808, + 5042, 25777, 35345, 22703, 12533, 23983, 31515, 12516, 26545, 15604, + 27973, 5363, 26081, 18868, 26331, 14262, 14258, 15977, 12514, 7142, + 23659, 26489, 10498, 20026, 12541, 28216, 12524, 13814, 13813, 12535, + 26970, 28206, 12521, 27166, 25927, 5030, 25366, 26978, 25981, 20272, + 22889, 27171, 25610, 16083, 12564, 22303, 33957, 33709, 14260, 10120, + 33931, 10499, 7086, 32267, 28458, 13158, 26478, 12585, 26873, 31516, + 4650, 20434, 9067, 17248, 28229, 12615, 7813, 2642, 9074, 2660, 25912, + 6166, 2663, 13810, 26982, 28875, 11773, 13153, 25597, 17237, 25367, + 10645, 12627, 29772, 6617, 4472, 9060, 7146, 5041, 25787, 25977, 9075, + 26751, 6102, 18723, 20361, 22883, 2664, 28207, 33981, 28209, 12526, + 12539, 25178, 12653, 23986, 10044, 12544, 12528, 26842, 17246, 13188, + 15542, 12594, 7820, 19795, 26847, 32243, 32335, 10660, 10646, 3519, + 27087, 25191, 12644, 5107, 9948, 13187, 19796, 26213, 27170, 28828, + 13056, 35339, 15433, 26837, 29276, 7799, 16369, 20032, 25594, 15544, + 25526, 26059, 19964, 22887, 22275, 2662, 29089, 20269, 10490, 28137, + 25135, 24859, 28115, 12599, 25184, 3587, 3866, 26869, 13824, 25995, + 11864, 11865, 11867, 11866, 4656, 19089, 12621, 32025, 29082, 29083, + 26680, 10653, 22892, 20358, 21557, 21558, 6402, 9062, 26683, 3755, 12849, + 24833, 12551, 33557, 5106, 21521, 15618, 5044, 32151, 28850, 17241, 9947, + 12518, 101, 6581, 24820, 3583, 25618, 25612, 25621, 25611, 20036, 12557, + 33095, 22107, 11862, 13051, 35531, 5028, 24858, 3858, 26844, 13156, 7795, + 28224, 26102, 12580, 16086, 31287, 25631, 10647, 7575, 2645, 12577, + 32027, 5037, 20035, 19967, 19846, 28457, 2806, 26681, 31174, 5043, 3480, + 26488, 3478, 28461, 26088, 23661, 23766, 23777, 23780, 23769, 23759, + 23774, 33747, 3893, 23760, 33744, 33760, 33763, 33738, 33752, 33757, + 3890, 3906, 3909, 3884, 3898, 3903, 23767, 23778, 23781, 23770, 23765, + 23775, 33748, 3894, 23761, 33766, 33769, 33770, 33765, 33767, 33768, + 3912, 3915, 3916, 3911, 3913, 3914, 23784, 23787, 23788, 23783, 23785, + 23786, 33749, 3895, 23762, 33745, 33761, 33764, 33739, 33750, 33758, + 3891, 3907, 3910, 3885, 3896, 3904, 23768, 23779, 23782, 23771, 23763, + 23776, 33751, 3897, 23764, 33740, 3886, 23772, 33741, 3887, 23773, 33754, + 33755, 33753, 3900, 3901, 3899, 33742, 33736, 3888, 3882, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 33973, 33976, 33972, 33974, + 33971, 33970, 33975, 33966, 33969, 33965, 33967, 33964, 33963, 33968, + 35536, 35536, 2807, 24832, 5036, 27164, 31518, 19104, 13811, 25781, + 10496, 99, 28855, 33962, 7816, 35536, 35536, 35536, 35253, 17240, 25375, + 4360, 20034, 25782, 23756, 20364, 12609, 14805, 32153, 35536, 35536, + 35536, 32116, 27167, 26473, 6237, 26086, 2647, 10123, 3476, 22282, 1, + 19824, 7794, 6164, 26850, 17250, 15597, 22298, 33934, 25895, 26975, + 17242, 5110, 22900, 32026, 14835, 25790, 26483, 22299, 15624, 19852, + 14256, 12619, 14125, 16850, 12610, 33951, 3590, 7144, 25914, 33953, + 12559, 19848, 7768, 11876, 23757, 15615, 16079, 33937, 18721, 13190, 958, + 19969, 25633, 25929, 25928, 25616, 12573, 35536, 14127, 35536, 35536, + 35536, 35536, 24861, 22886, 10308, 4348, 3593, 24822, 12595, 30188, + 12643, 31173, 25916, 3588, 16078, 13053, 25598, 26526, 35536, 35536, + 28456, 21765, 26685, 12523, 12527, 12538, 10318, 3863, 5045, 27138, + 12534, 10043, 35536, 35536, 35536, 35536, 12567, 14259, 26421, 18930, + 25332, 25333, 15785, 14842, 18975, 26427, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 4284, 4314, 4285, 4329, 4300, 4318, 4286, 4337, + 4307, 4315, 4293, 4330, 4301, 4319, 4287, 4341, 4311, 4326, 4297, 4334, + 4323, 4290, 4338, 4308, 4316, 4294, 4331, 4302, 4320, 4288, 4343, 4313, + 4328, 4299, 4336, 4306, 4325, 4292, 4340, 4310, 4296, 4333, 4304, 4322, + 4289, 4342, 4312, 4327, 4298, 4335, 4305, 4324, 4291, 4339, 4309, 4317, + 4295, 4332, 4303, 4321, 19869, 19870, 19877, 19879, 19874, 19935, 19936, + 19932, 19934, 19930, 19933, 19926, 19929, 19927, 19931, 19928, 19873, + 19876, 19871, 19875, 19872, 19878, 32432, 32433, 32440, 32442, 32437, + 32402, 32403, 32399, 32401, 32397, 32400, 32393, 32396, 32394, 32398, + 32395, 32436, 32439, 32434, 32438, 32435, 32441, 32375, 18724, 32372, + 18729, 18815, 32471, 26314, 19961, 33419, 33420, 33421, 33422, 33423, + 33424, 15559, 15560, 15561, 15562, 15563, 15564, 18725, 18730, 26227, + 26226, 32374, 15558, 32430, 32467, 32382, 32470, 32466, 26276, 26310, + 26254, 26309, 26286, 18773, 26269, 32389, 19862, 15960, 32384, 32386, + 35536, 18772, 6400, 15956, 14882, 32407, 32462, 32373, 18726, 32408, + 32463, 19922, 19899, 4556, 4560, 4548, 4554, 4557, 4562, 4549, 4551, + 4559, 4561, 4563, 4558, 4552, 4553, 4579, 4589, 2563, 15957, 18767, + 26264, 15958, 18884, 26365, 10440, 32475, 18764, 26261, 33531, 26278, + 23706, 23705, 23707, 33811, 18818, 22277, 26305, 23737, 28856, 28857, + 28859, 28860, 28858, 33879, 33784, 26077, 4005, 18825, 18780, 4555, 4576, + 4571, 4550, 4566, 4565, 4573, 4564, 4569, 4575, 4546, 4570, 4567, 4577, + 4547, 4572, 32051, 26272, 4466, 18839, 32381, 19946, 22278, 22279, 32050, + 26271, 4465, 18838, 32060, 4452, 4459, 32052, 26883, 26885, 26882, 26881, + 26878, 26877, 26880, 26879, 26886, 26884, 229, 35536, 35536, 35536, + 35536, 35536, 35536, 17252, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 29369, 29298, 29361, 29370, 29286, 29359, + 29280, 29279, 29356, 29364, 29281, 29360, 29284, 29301, 29372, 29368, + 29293, 29295, 29292, 29291, 29288, 29287, 29290, 29289, 29296, 29294, + 29285, 29367, 29354, 29297, 29300, 29362, 29283, 29302, 29303, 29304, + 29305, 29306, 29307, 29308, 29309, 29310, 29311, 29312, 29313, 29314, + 29315, 29316, 29317, 29318, 29319, 29320, 29321, 29322, 29323, 29324, + 29325, 29326, 29327, 29357, 29366, 29365, 29282, 29358, 29299, 29328, + 29329, 29330, 29331, 29332, 29333, 29334, 29335, 29336, 29337, 29338, + 29339, 29340, 29341, 29342, 29343, 29344, 29345, 29346, 29347, 29348, + 29349, 29350, 29351, 29352, 29353, 29355, 29373, 29363, 29371, 6097, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 32917, 32928, + 32939, 32951, 32962, 32973, 32984, 32995, 33006, 33014, 33015, 33016, + 33017, 33019, 33020, 33021, 33022, 33023, 33024, 33025, 33026, 33027, + 33028, 33030, 33031, 33032, 33033, 33034, 33035, 33036, 33037, 33038, + 33039, 33041, 33042, 33043, 33044, 33045, 33046, 33047, 33048, 33049, + 33050, 33052, 33053, 33054, 33055, 33056, 33057, 33058, 33059, 33060, + 33061, 33063, 33064, 33065, 33066, 33067, 33068, 33069, 33070, 33071, + 33072, 33074, 33075, 33076, 33077, 33078, 33079, 33080, 33081, 33082, + 33083, 33085, 33086, 33087, 33088, 33089, 33090, 33091, 33092, 33093, + 33094, 32841, 32842, 32843, 32844, 32845, 32846, 32847, 32848, 32849, + 32850, 32852, 32853, 32854, 32855, 32856, 32857, 32858, 32859, 32860, + 32861, 32863, 32864, 32865, 32866, 32867, 32868, 32869, 32870, 32871, + 32872, 32874, 32875, 32876, 32877, 32878, 32879, 32880, 32881, 32882, + 32883, 32885, 32886, 32887, 32888, 32889, 32890, 32891, 32892, 32893, + 32894, 32896, 32897, 32898, 32899, 32900, 32901, 32902, 32903, 32904, + 32905, 32907, 32908, 32909, 32910, 32911, 32912, 32913, 32914, 32915, + 32916, 32918, 32919, 32920, 32921, 32922, 32923, 32924, 32925, 32926, + 32927, 32929, 32930, 32931, 32932, 32933, 32934, 32935, 32936, 32937, + 32938, 32940, 32941, 32942, 32943, 32944, 32945, 32946, 32947, 32948, + 32949, 32952, 32953, 32954, 32955, 32956, 32957, 32958, 32959, 32960, + 32961, 32963, 32964, 32965, 32966, 32967, 32968, 32969, 32970, 32971, + 32972, 32974, 32975, 32976, 32977, 32978, 32979, 32980, 32981, 32982, + 32983, 32985, 32986, 32987, 32988, 32989, 32990, 32991, 32992, 32993, + 32994, 32996, 32997, 32998, 32999, 33000, 33001, 33002, 33003, 33004, + 33005, 33007, 33008, 33009, 33010, 33011, 33012, 33013, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 24159, 24158, 28846, 28425, 28847, 28878, + 11899, 12473, 11897, 11910, 11903, 11902, 3, 2, 349, 3591, 2657, 5360, + 6392, 15569, 15599, 29277, 19184, 23877, 11900, 20033, 24233, 11908, + 19186, 33428, 33535, 12629, 12779, 6165, 7796, 27140, 19800, 28222, + 27141, 19799, 27174, 9661, 10649, 9946, 9662, 9945, 9663, 9944, 9664, + 9942, 9665, 23745, 23663, 29182, 29181, 11898, 12472, 6095, 5368, 11896, + 11909, 11863, 28909, 28879, 11945, 11944, 15857, 12570, 12777, 15858, + 13818, 14124, 15859, 26099, 26678, 15860, 32332, 32538, 28427, 9677, + 9674, 25194, 25193, 15436, 15598, 5029, 5359, 24050, 23665, 15784, 15783, + 23979, 23984, 28843, 28831, 11895, 11948, 6394, 15571, 15601, 6393, + 15570, 15600, 19187, 33429, 33536, 25517, 25516, 25896, 25518, 25519, + 25876, 26179, 26186, 26214, 27986, 27987, 28829, 27985, 27988, 28830, + 9943, 9666, 25985, 25986, 26034, 25984, 25987, 26035, 26973, 28877, 6096, + 9646, 22108, 23489, 28842, 28845, 28428, 11894, 11892, 12511, 28844, + 28426, 27980, 29274, 27979, 26868, 7590, 9645, 28868, 28832, 24851, + 25073, 25983, 26036, 1056, 1063, 23664, 27173, 17580, 18203, 9644, 2354, + 363, 29260, 16383, 17255, 17256, 17298, 17264, 31621, 14524, 14525, + 14502, 14523, 12773, 12774, 12775, 23487, 12776, 28934, 35534, 35533, + 35535, 20029, 26481, 20027, 26479, 25592, 20030, 26482, 24232, 23488, + 33960, 20028, 26480, 12778, 25593, 33727, 22270, 22271, 18936, 26437, + 34470, 23275, 33096, 33207, 33275, 33286, 33297, 33308, 33319, 33330, + 33341, 33097, 33108, 33119, 33130, 33141, 33152, 33163, 25952, 5358, + 4651, 35532, 8760, 8759, 8455, 2872, 2982, 2973, 3080, 3281, 21565, + 21563, 21623, 21621, 15349, 5195, 21943, 21946, 33174, 33185, 33196, + 33208, 33219, 33230, 33241, 33252, 33263, 33271, 33272, 33273, 33274, + 33276, 33277, 33278, 33279, 33280, 33281, 33282, 33283, 33284, 33285, + 33287, 33288, 33289, 33290, 33291, 33292, 33293, 33294, 33295, 33296, + 33298, 33299, 33300, 33301, 33302, 33303, 33304, 33305, 33306, 33307, + 33309, 33310, 33311, 33312, 33313, 33314, 33315, 33316, 33317, 33318, + 33320, 33321, 33322, 33323, 33324, 33325, 33326, 33327, 33328, 33329, + 33331, 33332, 33333, 33334, 33335, 33336, 33337, 33338, 33339, 33340, + 33342, 33343, 33344, 33345, 33346, 33347, 33348, 33349, 33350, 33351, + 33098, 33099, 33100, 33101, 33102, 33103, 33104, 33105, 33106, 33107, + 33109, 33110, 33111, 33112, 33113, 33114, 33115, 33116, 33117, 33118, + 33120, 33121, 33122, 33123, 33124, 33125, 33126, 33127, 33128, 33129, + 33131, 33132, 33133, 33134, 33135, 33136, 33137, 33138, 33139, 33140, + 33142, 33143, 33144, 33145, 33146, 33147, 33148, 33149, 33150, 33151, + 33153, 33154, 33155, 33156, 33157, 33158, 33159, 33160, 33161, 33162, + 33164, 33165, 33166, 33167, 33168, 33169, 33170, 33171, 33172, 33173, + 33175, 33176, 33177, 33178, 33179, 33180, 33181, 33182, 33183, 33184, + 33186, 33187, 33188, 33189, 33190, 33191, 33192, 33193, 33194, 33195, + 33197, 33198, 33199, 33200, 33201, 33202, 33203, 33204, 33205, 33206, + 33209, 33210, 33211, 33212, 33213, 33214, 33215, 33216, 33217, 33218, + 33220, 33221, 33222, 33223, 33224, 33225, 33226, 33227, 33228, 33229, + 33231, 33232, 33233, 33234, 33235, 33236, 33237, 33238, 33239, 33240, + 33242, 33243, 33244, 33245, 33246, 33247, 33248, 33249, 33250, 33251, + 33253, 33254, 33255, 33256, 33257, 33258, 33259, 33260, 33261, 33262, + 33264, 33265, 33266, 33267, 33268, 33269, 33270, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 16768, 16769, + 16776, 16778, 16775, 16774, 16771, 16770, 16773, 16772, 16779, 16777, + 17443, 18009, 17604, 18234, 17844, 18608, 17539, 18144, 17540, 18145, + 17541, 18146, 17715, 18385, 17716, 18386, 17717, 18387, 17783, 18484, + 17523, 18127, 17519, 18123, 18229, 18353, 17453, 18019, 17454, 18020, + 17544, 18150, 17545, 18151, 17529, 18133, 17530, 18134, 18228, 18230, + 17609, 18236, 17610, 18237, 17627, 18264, 17657, 18304, 17665, 18326, + 17755, 18444, 17848, 18612, 17849, 18613, 17845, 18609, 17846, 18610, + 18028, 18402, 18403, 18563, 18564, 18493, 18494, 18218, 18219, 2324, + 2327, 2325, 2329, 2331, 2328, 2330, 2326, 2332, 9875, 9870, 9869, 9876, + 9871, 9872, 9874, 9873, 3664, 3663, 3665, 14032, 14031, 14030, 14029, + 14034, 14033, 24925, 24924, 3609, 29778, 29786, 29795, 29787, 29789, + 29784, 29788, 29783, 29799, 29798, 29801, 29793, 29780, 29800, 29782, + 29781, 29794, 29785, 29797, 29791, 29792, 29790, 29796, 29779, 29917, + 29924, 29925, 29920, 29921, 29926, 29927, 29918, 29922, 29923, 29919, + 29983, 29990, 29991, 29986, 29987, 29992, 29993, 29984, 29988, 29989, + 29985, 30094, 30101, 30102, 30097, 30098, 30103, 30104, 30095, 30099, + 30100, 30096, 29994, 30001, 30002, 29997, 29998, 30003, 30004, 29995, + 29999, 30000, 29996, 30072, 30079, 30080, 30075, 30076, 30081, 30082, + 30073, 30077, 30078, 30074, 29972, 29979, 29980, 29975, 29976, 29981, + 29982, 29973, 29977, 29978, 29974, 30083, 30090, 30091, 30086, 30087, + 30092, 30093, 30084, 30088, 30089, 30085, 30005, 30012, 30013, 30008, + 30009, 30014, 30015, 30006, 30010, 30011, 30007, 30138, 30145, 30146, + 30141, 30142, 30147, 30148, 30139, 30143, 30144, 30140, 30127, 30134, + 30135, 30130, 30131, 30136, 30137, 30128, 30132, 30133, 30129, 30160, + 30167, 30168, 30163, 30164, 30169, 30170, 30161, 30165, 30166, 30162, + 30027, 30034, 30035, 30030, 30031, 30036, 30037, 30028, 30032, 30033, + 30029, 29950, 29957, 29958, 29953, 29954, 29959, 29960, 29951, 29955, + 29956, 29952, 30149, 30156, 30157, 30152, 30153, 30158, 30159, 30150, + 30154, 30155, 30151, 29928, 29935, 29936, 29931, 29932, 29937, 29938, + 29929, 29933, 29934, 29930, 29939, 29946, 29947, 29942, 29943, 29948, + 29949, 29940, 29944, 29945, 29941, 30016, 30023, 30024, 30019, 30020, + 30025, 30026, 30017, 30021, 30022, 30018, 29961, 29968, 29969, 29964, + 29965, 29970, 29971, 29962, 29966, 29967, 29963, 30116, 30123, 30124, + 30119, 30120, 30125, 30126, 30117, 30121, 30122, 30118, 30038, 30046, + 30047, 30041, 30042, 30048, 30049, 30039, 30043, 30044, 30040, 30050, + 30057, 30058, 30053, 30054, 30059, 30060, 30051, 30055, 30056, 30052, + 30061, 30068, 30069, 30064, 30065, 30070, 30071, 30062, 30066, 30067, + 30063, 30105, 30112, 30113, 30108, 30109, 30114, 30115, 30106, 30110, + 30111, 30107, 29905, 29906, 29913, 29914, 29909, 29910, 29915, 29916, + 29907, 29911, 29912, 29908, 30045, 28011, 28009, 28010, 12955, 16861, + 16859, 16862, 16860, 16851, 16857, 16855, 16858, 16856, 16852, 16872, + 16868, 16873, 16869, 16853, 16870, 16866, 16871, 16867, 16854, 16883, + 16863, 16865, 16864, 16879, 16882, 16880, 16875, 16881, 16876, 16877, + 16878, 16884, 16874, 17023, 16900, 16901, 16902, 16899, 17018, 17010, + 15466, 15468, 15470, 15467, 15469, 16473, 16475, 16477, 16474, 16476, + 16466, 16465, 16464, 16467, 22480, 22485, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, 35536, + 35536, 35536, 35536, 35536, 35536, 35536, 35536, }; static const unsigned int aliases_start = 0xf0000; -static const unsigned int aliases_end = 0xf01dd; +static const unsigned int aliases_end = 0xf01e1; static const unsigned int name_aliases[] = { 0x0000, 0x0000, @@ -18753,6 +17737,10 @@ static const unsigned int name_aliases[] = { 0x122D5, 0x12327, 0x1680B, + 0x16881, + 0x1688E, + 0x168DC, + 0x1697D, 0x16E56, 0x16E57, 0x16E76, @@ -19473,3 +18461,42 @@ static const named_sequence named_sequences[] = { {2, {0x02E5, 0x02E9}}, {2, {0x02E9, 0x02E5}}, }; + +typedef struct { + Py_UCS4 first; + Py_UCS4 last; + int prefixid; +} derived_name_range; + +static const derived_name_range derived_name_ranges[] = { + {0x3400, 0x4DBF, 1}, + {0x4E00, 0x9FFF, 1}, + {0xAC00, 0xD7A3, 0}, + {0xF900, 0xFA6D, 3}, + {0xFA70, 0xFAD9, 3}, + {0x13460, 0x143FA, 4}, + {0x17000, 0x187FF, 2}, + {0x18B00, 0x18CD5, 5}, + {0x18CFF, 0x18CFF, 5}, + {0x18D00, 0x18D1E, 2}, + {0x1B170, 0x1B2FB, 6}, + {0x20000, 0x2A6DF, 1}, + {0x2A700, 0x2B73F, 1}, + {0x2B740, 0x2B81D, 1}, + {0x2B820, 0x2CEAD, 1}, + {0x2CEB0, 0x2EBE0, 1}, + {0x2EBF0, 0x2EE5D, 1}, + {0x2F800, 0x2FA1D, 3}, + {0x30000, 0x3134A, 1}, + {0x31350, 0x323AF, 1}, + {0x323B0, 0x33479, 1}, +}; +static const char * const derived_name_prefixes[] = { + "HANGUL SYLLABLE ", + "CJK UNIFIED IDEOGRAPH-", + "TANGUT IDEOGRAPH-", + "CJK COMPATIBILITY IDEOGRAPH-", + "EGYPTIAN HIEROGLYPH-", + "KHITAN SMALL SCRIPT CHARACTER-", + "NUSHU CHARACTER-", +}; diff --git a/Modules/xxlimited.c b/Modules/xxlimited.c index 0480fb0849876b..96454ee5e83eab 100644 --- a/Modules/xxlimited.c +++ b/Modules/xxlimited.c @@ -11,10 +11,18 @@ other files, you'll have to create a file "foobarobject.h"; see floatobject.h for an example. - This module roughly corresponds to:: + This module uses Limited API 3.15. + See ``xxlimited_3_13.c`` if you want to support older CPython versions. + + This module roughly corresponds to the following. + (All underscore-prefixed attributes are not accessible from Python.) + + :: class Xxo: - """A class that explicitly stores attributes in an internal dict""" + """A class that explicitly stores attributes in an internal dict + (to simulate custom attribute handling). + """ def __init__(self): # In the C class, "_x_attr" is not accessible from Python code @@ -25,6 +33,8 @@ return self._x_attr[name] def __setattr__(self, name, value): + if name == "reserved": + raise AttributeError("cannot set 'reserved'") self._x_attr[name] = value def __delattr__(self, name): @@ -62,11 +72,13 @@ pass */ -// Need limited C API version 3.13 for Py_mod_gil -#include "pyconfig.h" // Py_GIL_DISABLED -#ifndef Py_GIL_DISABLED -# define Py_LIMITED_API 0x030d0000 -#endif +// Target both flavors of the Stable ABI. +// Both are set to version 3.15, which adds PyModExport +// (When using a build tool, check if it has an option to set these +// so they do not need to be defined in the source.) +#define Py_LIMITED_API 0x030f0000 // abi3 (GIL-enabled builds) +#define Py_TARGET_ABI3T 0x030f0000 // abi3t (free-threaded builds) + #include "Python.h" #include <string.h> @@ -75,92 +87,224 @@ // Module state typedef struct { - PyObject *Xxo_Type; // Xxo class + PyTypeObject *Xxo_Type; // Xxo class PyObject *Error_Type; // Error class } xx_state; -/* Xxo objects */ +/* Xxo objects. + * + * A non-trivial extension type, intentionally showing a number of features + * that aren't easy to implement in the Limited API. + */ + +// Forward declaration +static PyType_Spec Xxo_Type_spec; + +// Get the module state (xx_state*) from a given type object 'type', which +// must be a subclass of Xxo (the type we're defining). +// This is complicated by the fact that the Xxo type is dynamically allocated, +// and there may be several such types in a given Python process -- for +// example, in different subinterpreters, or through loading this +// extension module several times. +// So, we don't have a "global" pointer to the type, or to the module, etc.; +// instead we search based on `Xxo_Type_spec` (which is static, immutable, +// and process-global). +// +// When possible, it's better to avoid `PyType_GetBaseByToken` -- for an +// example, see the `demo` method (Xxo_demo C function), which uses a +// "defining class". But, in many cases it's the best solution. +static xx_state * +Xxo_state_from_type(PyTypeObject *type) +{ + PyTypeObject *base; + // Search all superclasses of 'type' for one that was defined using + // "Xxo_Type_spec". That must be our 'Xxo' class. + if (PyType_GetBaseByToken(type, &Xxo_Type_spec, &base) < 0) { + return NULL; + } + if (base == NULL) { + PyErr_SetString(PyExc_TypeError, "need Xxo subclass"); + return NULL; + } + // From this type, get the associated module. That must be the + // relevant `xxlimited` module. + xx_state *state = PyType_GetModuleState(base); + Py_DECREF(base); + return state; +} -// Instance state +// Structure for data needed by the XxoObject type. +// Since the object may be shared across threads, access to the fields +// usually needs to be synchronized (using Py_BEGIN_CRITICAL_SECTION). typedef struct { - PyObject_HEAD - PyObject *x_attr; /* Attributes dictionary */ - char x_buffer[BUFSIZE]; /* buffer for Py_buffer */ - Py_ssize_t x_exports; /* how many buffer are exported */ -} XxoObject; - -#define XxoObject_CAST(op) ((XxoObject *)(op)) -// XXX: no good way to do this yet -// #define XxoObject_Check(v) Py_IS_TYPE(v, Xxo_Type) - -static XxoObject * -newXxoObject(PyObject *module) + PyObject *x_attr; /* Attributes dictionary. + * May be NULL, which acts as an + * empty dict. + */ + Py_ssize_t x_exports; /* how many buffers are exported */ + char x_buffer[BUFSIZE]; /* buffer for Py_buffer (for simplicity, + * this is constant, so does not need + * synchronization) + */ +} XxoObject_Data; + +// Get the `XxoObject_Data` structure for a given instance of our type. +static XxoObject_Data * +Xxo_get_data(PyObject *self) { - xx_state *state = PyModule_GetState(module); + xx_state *state = Xxo_state_from_type(Py_TYPE(self)); + if (!state) { + return NULL; + } + XxoObject_Data *data = PyObject_GetTypeData(self, state->Xxo_Type); + return data; +} + +// A variant of Xxo_get_data to be used in the tp_traverse handler. +// This function cannot have side effects (including reference count +// manipulation, creating objects, and raising exceptions), and must not +// call API functions that might have side effects. +// See: https://docs.python.org/3.15/c-api/gcsupport.html#traversal +static XxoObject_Data * +Xxo_get_data_DuringGC(PyObject *self) +{ + PyTypeObject *base; + PyType_GetBaseByToken_DuringGC(Py_TYPE(self), &Xxo_Type_spec, &base); + if (base == NULL) { + return NULL; + } + xx_state *state = PyType_GetModuleState_DuringGC(base); if (state == NULL) { return NULL; } - XxoObject *self; - self = PyObject_GC_New(XxoObject, (PyTypeObject*)state->Xxo_Type); + XxoObject_Data *data = PyObject_GetTypeData_DuringGC(self, state->Xxo_Type); + return data; +} + +// Xxo initialization +// This is the implementation of Xxo.__new__ +static PyObject * +Xxo_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + // Validate that we did not get any arguments. + if ((args != NULL && PyObject_Length(args)) + || (kwargs != NULL && PyObject_Length(kwargs))) + { + PyErr_SetString(PyExc_TypeError, "Xxo.__new__() takes no arguments"); + return NULL; + } + // Create an instance of *type* (which may be a subclass) + allocfunc alloc = PyType_GetSlot(type, Py_tp_alloc); + PyObject *self = alloc(type, 0); if (self == NULL) { return NULL; } - self->x_attr = NULL; - memset(self->x_buffer, 0, BUFSIZE); - self->x_exports = 0; + + // Initialize the C members on the instance. + // This is only included for the sake of example. The default alloc + // function zeroes instance memory; we don't need to do it again. + // Note that we during initialization (and finalization), we hold the only + // reference to the object, so we don't need to synchronize with + // other threads. + XxoObject_Data *xxo_data = Xxo_get_data(self); + if (xxo_data == NULL) { + Py_DECREF(self); + return NULL; + } + + xxo_data->x_attr = NULL; + memset(xxo_data->x_buffer, 0, BUFSIZE); + xxo_data->x_exports = 0; return self; } -/* Xxo finalization */ +/* Xxo finalization. + * + * Types that store references to other PyObjects generally need to implement + * the GC slots: traverse, clear, dealloc, and (optionally) finalize. + */ +// traverse: Visit all references from an object, including its type static int -Xxo_traverse(PyObject *op, visitproc visit, void *arg) +Xxo_traverse(PyObject *self, visitproc visit, void *arg) { // Visit the type - Py_VISIT(Py_TYPE(op)); + Py_VISIT(Py_TYPE(self)); // Visit the attribute dict - XxoObject *self = XxoObject_CAST(op); - Py_VISIT(self->x_attr); + XxoObject_Data *data = Xxo_get_data_DuringGC(self); + if (data == NULL) { + return 0; + } + Py_VISIT(data->x_attr); return 0; } +// clear: drop references in order to break all reference cycles static int -Xxo_clear(PyObject *op) +Xxo_clear(PyObject *self) { - XxoObject *self = XxoObject_CAST(op); - Py_CLEAR(self->x_attr); + XxoObject_Data *data = Xxo_get_data(self); + if (data == NULL) { + return 0; + } + Py_CLEAR(data->x_attr); return 0; } +// finalize: like clear, but should leave the object in a consistent state. +// Equivalent to `__del__` in Python. static void -Xxo_finalize(PyObject *op) +Xxo_finalize(PyObject *self) { - XxoObject *self = XxoObject_CAST(op); - Py_CLEAR(self->x_attr); + XxoObject_Data *data = Xxo_get_data(self); + if (data == NULL) { + return; + } + Py_CLEAR(data->x_attr); } +// dealloc: drop all remaining references and free memory static void Xxo_dealloc(PyObject *self) { + // This function must preserve currently raised exception, if any. + PyObject *exc = PyErr_GetRaisedException(); + PyObject_GC_UnTrack(self); Xxo_finalize(self); + PyTypeObject *tp = Py_TYPE(self); freefunc free = PyType_GetSlot(tp, Py_tp_free); free(self); Py_DECREF(tp); + + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(NULL); + } + PyErr_SetRaisedException(exc); } /* Xxo attribute handling */ +// Get an attribute. static PyObject * -Xxo_getattro(PyObject *op, PyObject *name) +Xxo_getattro(PyObject *self, PyObject *name) { - XxoObject *self = XxoObject_CAST(op); - if (self->x_attr != NULL) { - PyObject *v = PyDict_GetItemWithError(self->x_attr, name); + XxoObject_Data *data = Xxo_get_data(self); + if (data == NULL) { + return 0; + } + + PyObject *x_attr; + Py_BEGIN_CRITICAL_SECTION(self); + x_attr = data->x_attr; + Py_END_CRITICAL_SECTION(); + + if (x_attr != NULL) { + PyObject *v = PyDict_GetItemWithError(x_attr, name); if (v != NULL) { return Py_NewRef(v); } @@ -168,23 +312,44 @@ Xxo_getattro(PyObject *op, PyObject *name) return NULL; } } - return PyObject_GenericGetAttr(op, name); + // Fall back to generic implementation (this handles special attributes, + // raising AttributeError, etc.) + return PyObject_GenericGetAttr(self, name); } +// Set or delete an attribute. static int -Xxo_setattro(PyObject *op, PyObject *name, PyObject *v) +Xxo_setattro(PyObject *self, PyObject *name, PyObject *v) { - XxoObject *self = XxoObject_CAST(op); - if (self->x_attr == NULL) { + // filter a specific attribute name + if (PyUnicode_Check(name) && PyUnicode_EqualToUTF8(name, "reserved")) { + PyErr_Format(PyExc_AttributeError, "cannot set %R", name); + return -1; + } + + XxoObject_Data *data = Xxo_get_data(self); + if (data == NULL) { + return -1; + } + + // If the attribute dict is not created yet, make one. + // This needs to be protected by a critical section to avoid another thread + // creating a duplicate dict. + PyObject *x_attr; + Py_BEGIN_CRITICAL_SECTION(self); + x_attr = data->x_attr; + if (x_attr == NULL) { // prepare the attribute dict - self->x_attr = PyDict_New(); - if (self->x_attr == NULL) { - return -1; - } + data->x_attr = x_attr = PyDict_New(); + } + Py_END_CRITICAL_SECTION(); + if (x_attr == NULL) { + return -1; } + if (v == NULL) { // delete an attribute - int rv = PyDict_DelItem(self->x_attr, name); + int rv = PyDict_DelItem(x_attr, name); if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) { PyErr_SetString(PyExc_AttributeError, "delete non-existing Xxo attribute"); @@ -194,14 +359,16 @@ Xxo_setattro(PyObject *op, PyObject *name, PyObject *v) } else { // set an attribute - return PyDict_SetItem(self->x_attr, name, v); + return PyDict_SetItem(x_attr, name, v); } } -/* Xxo methods */ +/* Xxo methods: C functions plus a PyMethodDef array that lists them and + * specifies metadata. + */ static PyObject * -Xxo_demo(PyObject *op, PyTypeObject *defining_class, +Xxo_demo(PyObject *self, PyTypeObject *defining_class, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { if (kwnames != NULL && PyObject_Length(kwnames)) { @@ -234,33 +401,55 @@ static PyMethodDef Xxo_methods[] = { {NULL, NULL} /* sentinel */ }; -/* Xxo buffer interface */ +/* Xxo buffer interface: C functions later referenced from PyType_Slot array. + * Other interfaces (e.g. for sequence-like or number-like types) are defined + * similarly. + */ static int -Xxo_getbuffer(PyObject *op, Py_buffer *view, int flags) +Xxo_getbuffer(PyObject *self, Py_buffer *view, int flags) { - XxoObject *self = XxoObject_CAST(op); - int res = PyBuffer_FillInfo(view, op, - (void *)self->x_buffer, BUFSIZE, + XxoObject_Data *data = Xxo_get_data(self); + if (data == NULL) { + return -1; + } + int res = PyBuffer_FillInfo(view, self, + (void *)data->x_buffer, BUFSIZE, 0, flags); if (res == 0) { - self->x_exports++; + Py_BEGIN_CRITICAL_SECTION(self); + data->x_exports++; + Py_END_CRITICAL_SECTION(); } return res; } static void -Xxo_releasebuffer(PyObject *op, Py_buffer *Py_UNUSED(view)) +Xxo_releasebuffer(PyObject *self, Py_buffer *Py_UNUSED(view)) { - XxoObject *self = XxoObject_CAST(op); - self->x_exports--; + XxoObject_Data *data = Xxo_get_data(self); + if (data == NULL) { + return; + } + Py_BEGIN_CRITICAL_SECTION(self); + data->x_exports--; + Py_END_CRITICAL_SECTION(); } static PyObject * -Xxo_get_x_exports(PyObject *op, void *Py_UNUSED(closure)) +Xxo_get_x_exports(PyObject *self, void *Py_UNUSED(closure)) { - XxoObject *self = XxoObject_CAST(op); - return PyLong_FromSsize_t(self->x_exports); + XxoObject_Data *data = Xxo_get_data(self); + if (data == NULL) { + return NULL; + } + Py_ssize_t result; + + Py_BEGIN_CRITICAL_SECTION(self); + result = data->x_exports; + Py_END_CRITICAL_SECTION(); + + return PyLong_FromSsize_t(result); } /* Xxo type definition */ @@ -276,6 +465,7 @@ static PyGetSetDef Xxo_getsetlist[] = { static PyType_Slot Xxo_Type_slots[] = { {Py_tp_doc, (char *)Xxo_doc}, + {Py_tp_new, Xxo_new}, {Py_tp_traverse, Xxo_traverse}, {Py_tp_clear, Xxo_clear}, {Py_tp_finalize, Xxo_finalize}, @@ -286,13 +476,14 @@ static PyType_Slot Xxo_Type_slots[] = { {Py_bf_getbuffer, Xxo_getbuffer}, {Py_bf_releasebuffer, Xxo_releasebuffer}, {Py_tp_getset, Xxo_getsetlist}, + {Py_tp_token, Py_TP_USE_SPEC}, {0, 0}, /* sentinel */ }; static PyType_Spec Xxo_Type_spec = { .name = "xxlimited.Xxo", - .basicsize = sizeof(XxoObject), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .basicsize = -(Py_ssize_t)sizeof(XxoObject_Data), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, .slots = Xxo_Type_slots, }; @@ -300,6 +491,7 @@ static PyType_Spec Xxo_Type_spec = { /* Str type definition*/ static PyType_Slot Str_Type_slots[] = { + // slots array intentionally kept empty {0, 0}, /* sentinel */ }; @@ -330,17 +522,17 @@ xx_foo(PyObject *module, PyObject *args) } -/* Function of no arguments returning new Xxo object */ +/* Function of no arguments returning new Xxo object. + * Note that a function exposed to Python with METH_NOARGS requires an unused + * second argument, so we cannot use newXxoObject directly. + */ static PyObject * xx_new(PyObject *module, PyObject *Py_UNUSED(unused)) { - XxoObject *rv; + xx_state *state = PyModule_GetState(module); - rv = newXxoObject(module); - if (rv == NULL) - return NULL; - return (PyObject *)rv; + return Xxo_new(state->Xxo_Type, NULL, NULL); } @@ -374,11 +566,12 @@ xx_modexec(PyObject *m) return -1; } - state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL); + state->Xxo_Type = (PyTypeObject*)PyType_FromModuleAndSpec( + m, &Xxo_Type_spec, NULL); if (state->Xxo_Type == NULL) { return -1; } - if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) { + if (PyModule_AddType(m, state->Xxo_Type) < 0) { return -1; } @@ -386,12 +579,13 @@ xx_modexec(PyObject *m) // added to the module dict. // It does not inherit from "object" (PyObject_Type), but from "str" // (PyUnincode_Type). - PyObject *Str_Type = PyType_FromModuleAndSpec( + PyTypeObject *Str_Type = (PyTypeObject*)PyType_FromModuleAndSpec( m, &Str_Type_spec, (PyObject *)&PyUnicode_Type); if (Str_Type == NULL) { return -1; } - if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) { + if (PyModule_AddType(m, Str_Type) < 0) { + Py_DECREF(Str_Type); return -1; } Py_DECREF(Str_Type); @@ -399,17 +593,17 @@ xx_modexec(PyObject *m) return 0; } -static PyModuleDef_Slot xx_slots[] = { - {Py_mod_exec, xx_modexec}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0, NULL} -}; +// Module finalization: modules that hold references in their module state +// need to implement the fullowing GC hooks. They're similar to the ones for +// types (see "Xxo finalization"). static int xx_traverse(PyObject *module, visitproc visit, void *arg) { - xx_state *state = PyModule_GetState(module); + xx_state *state = PyModule_GetState_DuringGC(module); + if (state == NULL) { + return 0; + } Py_VISIT(state->Xxo_Type); Py_VISIT(state->Error_Type); return 0; @@ -419,6 +613,9 @@ static int xx_clear(PyObject *module) { xx_state *state = PyModule_GetState(module); + if (state == NULL) { + return 0; + } Py_CLEAR(state->Xxo_Type); Py_CLEAR(state->Error_Type); return 0; @@ -429,25 +626,59 @@ xx_free(void *module) { // allow xx_modexec to omit calling xx_clear on error (void)xx_clear((PyObject *)module); + + xx_state *state = PyModule_GetState(module); + if (state == NULL) { + return; + } } -static struct PyModuleDef xxmodule = { - PyModuleDef_HEAD_INIT, - .m_name = "xxlimited", - .m_doc = module_doc, - .m_size = sizeof(xx_state), - .m_methods = xx_methods, - .m_slots = xx_slots, - .m_traverse = xx_traverse, - .m_clear = xx_clear, - .m_free = xx_free, +// Information that CPython uses to prevent loading incompatible extenstions +PyABIInfo_VAR(abi_info); + +static PySlot xx_slots[] = { + /* Basic metadata */ + PySlot_STATIC_DATA(Py_mod_name, "xxlimited"), + PySlot_STATIC_DATA(Py_mod_doc, (void*)module_doc), + PySlot_DATA(Py_mod_abi, &abi_info), + + /* The method table */ + PySlot_STATIC_DATA(Py_mod_methods, xx_methods), + + /* exec function to initialize the module (called as part of import + * after the object was added to sys.modules) + */ + PySlot_FUNC(Py_mod_exec, xx_modexec), + + /* Module state and associated functions */ + PySlot_SIZE(Py_mod_state_size, sizeof(xx_state)), + PySlot_FUNC(Py_mod_state_traverse, xx_traverse), + PySlot_FUNC(Py_mod_state_clear, xx_clear), + PySlot_FUNC(Py_mod_state_free, xx_free), + + /* Signal that this module supports being loaded in multiple interpreters + * with separate GILs (global interpreter locks). + * See "Isolating Extension Modules" on how to prepare a module for this: + * https://docs.python.org/3/howto/isolating-extensions.html + */ + PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED), + + /* Signal that this module does not rely on the GIL for its own needs. + * Without this slot, free-threaded builds of CPython will enable + * the GIL when this module is loaded. + */ + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), + + PySlot_END }; -/* Export function for the module (*must* be called PyInit_xx) */ +/* Export function for the module. *Must* be called PyModExport_xx; usually + * it is the only non-`static` object in a module definition. + */ -PyMODINIT_FUNC -PyInit_xxlimited(void) +PyMODEXPORT_FUNC +PyModExport_xxlimited(void) { - return PyModuleDef_Init(&xxmodule); + return xx_slots; } diff --git a/Modules/xxlimited_35.c b/Modules/xxlimited_35.c index b0a682ac4e6bb6..9ef0eac9a924e6 100644 --- a/Modules/xxlimited_35.c +++ b/Modules/xxlimited_35.c @@ -305,7 +305,7 @@ xx_modexec(PyObject *m) static PyModuleDef_Slot xx_slots[] = { {Py_mod_exec, xx_modexec}, #ifdef Py_GIL_DISABLED - // These definitions are in the limited API, but not until 3.13. + // In a free-threaded build, we don't use Limited API. {Py_mod_gil, Py_MOD_GIL_NOT_USED}, #endif {0, NULL} diff --git a/Modules/xxlimited_3_13.c b/Modules/xxlimited_3_13.c new file mode 100644 index 00000000000000..4f100f9150fc2a --- /dev/null +++ b/Modules/xxlimited_3_13.c @@ -0,0 +1,499 @@ +/* Use this file as a template to start implementing a module that + also declares object types. All occurrences of 'Xxo' should be changed + to something reasonable for your objects. After that, all other + occurrences of 'xx' should be changed to something reasonable for your + module. If your module is named foo your source file should be named + foo.c or foomodule.c. + + You will probably want to delete all references to 'x_attr' and add + your own types of attributes instead. Maybe you want to name your + local variables other than 'self'. If your object type is needed in + other files, you'll have to create a file "foobarobject.h"; see + floatobject.h for an example. + + This module roughly corresponds to:: + + class Xxo: + """A class that explicitly stores attributes in an internal dict + (to simulate custom attribute handling). + """ + + def __init__(self): + # In the C class, "_x_attr" is not accessible from Python code + self._x_attr = {} + self._x_exports = 0 + + def __getattr__(self, name): + return self._x_attr[name] + + def __setattr__(self, name, value): + self._x_attr[name] = value + + def __delattr__(self, name): + del self._x_attr[name] + + @property + def x_exports(self): + """Return the number of times an internal buffer is exported.""" + # Each Xxo instance has a 10-byte buffer that can be + # accessed via the buffer interface (e.g. `memoryview`). + return self._x_exports + + def demo(o, /): + if isinstance(o, str): + return o + elif isinstance(o, Xxo): + return o + else: + raise Error('argument must be str or Xxo') + + class Error(Exception): + """Exception raised by the xxlimited module""" + + def foo(i: int, j: int, /): + """Return the sum of i and j.""" + # Unlike this pseudocode, the C function will *only* work with + # integers and perform C long int arithmetic + return i + j + + def new(): + return Xxo() + + def Str(str): + # A trivial subclass of a built-in type + pass + */ + +// Need limited C API version 3.13 for Py_mod_gil +#include "pyconfig.h" // Py_GIL_DISABLED +#ifndef Py_GIL_DISABLED +# define Py_LIMITED_API 0x030d0000 +#endif + +#include "Python.h" +#include <string.h> + +#define BUFSIZE 10 + +// Module state +typedef struct { + PyObject *Xxo_Type; // Xxo class + PyObject *Error_Type; // Error class +} xx_state; + + +/* Xxo objects */ + +// Instance state +typedef struct { + PyObject_HEAD + PyObject *x_attr; /* Attributes dictionary. + * May be NULL, which acts as an + * empty dict. + */ + char x_buffer[BUFSIZE]; /* buffer for Py_buffer */ + Py_ssize_t x_exports; /* how many buffer are exported */ +} XxoObject; + +#define XxoObject_CAST(op) ((XxoObject *)(op)) +// TODO: full support for type-checking was added in 3.14 (Py_tp_token) +// #define XxoObject_Check(v) Py_IS_TYPE(v, Xxo_Type) + +static XxoObject * +newXxoObject(PyObject *module) +{ + xx_state *state = PyModule_GetState(module); + if (state == NULL) { + return NULL; + } + XxoObject *self; + self = PyObject_GC_New(XxoObject, (PyTypeObject*)state->Xxo_Type); + if (self == NULL) { + return NULL; + } + self->x_attr = NULL; + memset(self->x_buffer, 0, BUFSIZE); + self->x_exports = 0; + return self; +} + +/* Xxo finalization. + * + * Types that store references to other PyObjects generally need to implement + * the GC slots: traverse, clear, dealloc, and (optionally) finalize. + */ + +// traverse: Visit all references from an object, including its type +static int +Xxo_traverse(PyObject *op, visitproc visit, void *arg) +{ + // Visit the type + Py_VISIT(Py_TYPE(op)); + + // Visit the attribute dict + XxoObject *self = XxoObject_CAST(op); + Py_VISIT(self->x_attr); + return 0; +} + +// clear: drop references in order to break all reference cycles +static int +Xxo_clear(PyObject *op) +{ + XxoObject *self = XxoObject_CAST(op); + Py_CLEAR(self->x_attr); + return 0; +} + +// finalize: like clear, but should leave the object in a consistent state. +// Equivalent to `__del__` in Python. +static void +Xxo_finalize(PyObject *op) +{ + XxoObject *self = XxoObject_CAST(op); + Py_CLEAR(self->x_attr); +} + +// dealloc: drop all remaining references and free memory +static void +Xxo_dealloc(PyObject *self) +{ + PyObject_GC_UnTrack(self); + Xxo_finalize(self); + PyTypeObject *tp = Py_TYPE(self); + freefunc free = PyType_GetSlot(tp, Py_tp_free); + free(self); + Py_DECREF(tp); +} + + +/* Xxo attribute handling */ + +// Get an attribute. +static PyObject * +Xxo_getattro(PyObject *op, PyObject *name) +{ + XxoObject *self = XxoObject_CAST(op); + if (self->x_attr != NULL) { + PyObject *v = PyDict_GetItemWithError(self->x_attr, name); + if (v != NULL) { + return Py_NewRef(v); + } + else if (PyErr_Occurred()) { + return NULL; + } + } + // Fall back to generic implementation (this handles special attributes, + // raising AttributeError, etc.) + return PyObject_GenericGetAttr(op, name); +} + +// Set or delete an attribute. +static int +Xxo_setattro(PyObject *op, PyObject *name, PyObject *v) +{ + XxoObject *self = XxoObject_CAST(op); + if (self->x_attr == NULL) { + // prepare the attribute dict + self->x_attr = PyDict_New(); + if (self->x_attr == NULL) { + return -1; + } + } + if (v == NULL) { + // delete an attribute + int rv = PyDict_DelItem(self->x_attr, name); + if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_SetString(PyExc_AttributeError, + "delete non-existing Xxo attribute"); + return -1; + } + return rv; + } + else { + // set an attribute + return PyDict_SetItem(self->x_attr, name, v); + } +} + +/* Xxo methods: C functions plus a PyMethodDef array that lists them and + * specifies metadata. + */ + +static PyObject * +Xxo_demo(PyObject *op, PyTypeObject *defining_class, + PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (kwnames != NULL && PyObject_Length(kwnames)) { + PyErr_SetString(PyExc_TypeError, "demo() takes no keyword arguments"); + return NULL; + } + if (nargs != 1) { + PyErr_SetString(PyExc_TypeError, "demo() takes exactly 1 argument"); + return NULL; + } + + PyObject *o = args[0]; + + /* Test if the argument is "str" */ + if (PyUnicode_Check(o)) { + return Py_NewRef(o); + } + + /* test if the argument is of the Xxo class */ + if (PyObject_TypeCheck(o, defining_class)) { + return Py_NewRef(o); + } + + return Py_NewRef(Py_None); +} + +static PyMethodDef Xxo_methods[] = { + {"demo", _PyCFunction_CAST(Xxo_demo), + METH_METHOD | METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("demo(o) -> o")}, + {NULL, NULL} /* sentinel */ +}; + +/* Xxo buffer interface: C functions later referenced from PyType_Slot array. + * Other interfaces (e.g. for sequence-like or number-like types) are defined + * similarly. + */ + +static int +Xxo_getbuffer(PyObject *op, Py_buffer *view, int flags) +{ + XxoObject *self = XxoObject_CAST(op); + int res = PyBuffer_FillInfo(view, op, + (void *)self->x_buffer, BUFSIZE, + 0, flags); + if (res == 0) { + self->x_exports++; + } + return res; +} + +static void +Xxo_releasebuffer(PyObject *op, Py_buffer *Py_UNUSED(view)) +{ + XxoObject *self = XxoObject_CAST(op); + self->x_exports--; +} + +static PyObject * +Xxo_get_x_exports(PyObject *op, void *Py_UNUSED(closure)) +{ + XxoObject *self = XxoObject_CAST(op); + return PyLong_FromSsize_t(self->x_exports); +} + +/* Xxo type definition */ + +PyDoc_STRVAR(Xxo_doc, + "A class that explicitly stores attributes in an internal dict"); + +static PyGetSetDef Xxo_getsetlist[] = { + {"x_exports", Xxo_get_x_exports, NULL, NULL}, + {NULL}, +}; + + +static PyType_Slot Xxo_Type_slots[] = { + {Py_tp_doc, (char *)Xxo_doc}, + {Py_tp_traverse, Xxo_traverse}, + {Py_tp_clear, Xxo_clear}, + {Py_tp_finalize, Xxo_finalize}, + {Py_tp_dealloc, Xxo_dealloc}, + {Py_tp_getattro, Xxo_getattro}, + {Py_tp_setattro, Xxo_setattro}, + {Py_tp_methods, Xxo_methods}, + {Py_bf_getbuffer, Xxo_getbuffer}, + {Py_bf_releasebuffer, Xxo_releasebuffer}, + {Py_tp_getset, Xxo_getsetlist}, + {0, 0}, /* sentinel */ +}; + +static PyType_Spec Xxo_Type_spec = { + .name = "xxlimited_3_13.Xxo", + .basicsize = sizeof(XxoObject), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .slots = Xxo_Type_slots, +}; + + +/* Str type definition*/ + +static PyType_Slot Str_Type_slots[] = { + // slots array intentionally kept empty + {0, 0}, /* sentinel */ +}; + +static PyType_Spec Str_Type_spec = { + .name = "xxlimited_3_13.Str", + .basicsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .slots = Str_Type_slots, +}; + + +/* Function of two integers returning integer (with C "long int" arithmetic) */ + +PyDoc_STRVAR(xx_foo_doc, +"foo(i,j)\n\ +\n\ +Return the sum of i and j."); + +static PyObject * +xx_foo(PyObject *module, PyObject *args) +{ + long i, j; + long res; + if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) + return NULL; + res = i+j; /* XXX Do something here */ + return PyLong_FromLong(res); +} + + +/* Function of no arguments returning new Xxo object */ + +static PyObject * +xx_new(PyObject *module, PyObject *Py_UNUSED(unused)) +{ + XxoObject *rv; + + rv = newXxoObject(module); + if (rv == NULL) + return NULL; + return (PyObject *)rv; +} + + + +/* List of functions defined in the module */ + +static PyMethodDef xx_methods[] = { + {"foo", xx_foo, METH_VARARGS, + xx_foo_doc}, + {"new", xx_new, METH_NOARGS, + PyDoc_STR("new() -> new Xx object")}, + {NULL, NULL} /* sentinel */ +}; + + +/* The module itself */ + +PyDoc_STRVAR(module_doc, +"This is a template module just for instruction."); + +static int +xx_modexec(PyObject *m) +{ + xx_state *state = PyModule_GetState(m); + + state->Error_Type = PyErr_NewException("xxlimited_3_13.Error", NULL, NULL); + if (state->Error_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, (PyTypeObject*)state->Error_Type) < 0) { + return -1; + } + + state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL); + if (state->Xxo_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) { + return -1; + } + + // Add the Str type. It is not needed from C code, so it is only + // added to the module dict. + // It does not inherit from "object" (PyObject_Type), but from "str" + // (PyUnincode_Type). + PyObject *Str_Type = PyType_FromModuleAndSpec( + m, &Str_Type_spec, (PyObject *)&PyUnicode_Type); + if (Str_Type == NULL) { + return -1; + } + if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) { + return -1; + } + Py_DECREF(Str_Type); + + return 0; +} + +static PyModuleDef_Slot xx_slots[] = { + + /* exec function to initialize the module (called as part of import + * after the object was added to sys.modules) + */ + {Py_mod_exec, xx_modexec}, + + /* Signal that this module supports being loaded in multiple interpreters + * with separate GILs (global interpreter locks). + * See "Isolating Extension Modules" on how to prepare a module for this: + * https://docs.python.org/3/howto/isolating-extensions.html + */ + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + + /* Signal that this module does not rely on the GIL for its own needs. + * Without this slot, free-threaded builds of CPython will enable + * the GIL when this module is loaded. + */ + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + + {0, NULL} +}; + +// Module finalization: modules that hold references in their module state +// need to implement the fullowing GC hooks. They're similar to the ones for +// types (see "Xxo finalization"). + +static int +xx_traverse(PyObject *module, visitproc visit, void *arg) +{ + xx_state *state = PyModule_GetState(module); + Py_VISIT(state->Xxo_Type); + Py_VISIT(state->Error_Type); + return 0; +} + +static int +xx_clear(PyObject *module) +{ + xx_state *state = PyModule_GetState(module); + Py_CLEAR(state->Xxo_Type); + Py_CLEAR(state->Error_Type); + return 0; +} + +static void +xx_free(void *module) +{ + // allow xx_modexec to omit calling xx_clear on error + (void)xx_clear((PyObject *)module); +} + +static struct PyModuleDef xxmodule = { + PyModuleDef_HEAD_INIT, + .m_name = "xxlimited_3_13", + .m_doc = module_doc, + .m_size = sizeof(xx_state), + .m_methods = xx_methods, + .m_slots = xx_slots, + .m_traverse = xx_traverse, + .m_clear = xx_clear, + .m_free = xx_free, +}; + + +/* Export function for the module. *Must* be called PyInit_xx; usually it is + * the only non-`static` object in a module definition. + */ + +PyMODINIT_FUNC +PyInit_xxlimited_3_13(void) +{ + return PyModuleDef_Init(&xxmodule); +} diff --git a/Modules/xxmodule.c b/Modules/xxmodule.c index e8749331c6a11f..aeab78fd77d83b 100644 --- a/Modules/xxmodule.c +++ b/Modules/xxmodule.c @@ -386,6 +386,7 @@ xx_exec(PyObject *m) } static struct PyModuleDef_Slot xx_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, xx_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/xxsubtype.c b/Modules/xxsubtype.c index a8a1417f40efef..7a31ba00b981eb 100644 --- a/Modules/xxsubtype.c +++ b/Modules/xxsubtype.c @@ -301,7 +301,10 @@ xxsubtype_exec(PyObject* m) return 0; } +PyABIInfo_VAR(abi_info); + static struct PyModuleDef_Slot xxsubtype_slots[] = { + {Py_mod_abi, &abi_info}, {Py_mod_exec, xxsubtype_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index f7009364644b7e..0a6732835eb51f 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -8,6 +8,7 @@ #endif #include "Python.h" +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_STORE_CHAR_RELAXED #include "zlib.h" #include "stdbool.h" @@ -181,15 +182,6 @@ OutputBuffer_WindowOnError(_BlocksOutputBuffer *buffer, _Uint32Window *window) } -#define ENTER_ZLIB(obj) do { \ - if (!PyThread_acquire_lock((obj)->lock, 0)) { \ - Py_BEGIN_ALLOW_THREADS \ - PyThread_acquire_lock((obj)->lock, 1); \ - Py_END_ALLOW_THREADS \ - } } while (0) -#define LEAVE_ZLIB(obj) PyThread_release_lock((obj)->lock); - - /* The following parameters are copied from zutil.h, version 0.95 */ #define DEFLATED 8 #if MAX_MEM_LEVEL >= 8 @@ -228,7 +220,7 @@ typedef struct char eof; bool is_initialised; PyObject *zdict; - PyThread_type_lock lock; + PyMutex mutex; } compobject; #define _compobject_CAST(op) ((compobject *)op) @@ -273,7 +265,9 @@ static compobject * newcompobject(PyTypeObject *type) { compobject *self; - self = PyObject_New(compobject, type); + assert(type != NULL); + assert(type->tp_alloc != NULL); + self = _compobject_CAST(type->tp_alloc(type, 0)); if (self == NULL) return NULL; self->eof = 0; @@ -289,12 +283,7 @@ newcompobject(PyTypeObject *type) Py_DECREF(self); return NULL; } - self->lock = PyThread_allocate_lock(); - if (self->lock == NULL) { - Py_DECREF(self); - PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); - return NULL; - } + self->mutex = (PyMutex){0}; return self; } @@ -342,7 +331,7 @@ zlib_compress_impl(PyObject *module, Py_buffer *data, int level, int wbits) PyObject *return_value; int flush; z_stream zst; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; zlibstate *state = get_zlib_state(module); @@ -427,7 +416,7 @@ zlib.decompress / wbits: int(c_default="MAX_WBITS") = MAX_WBITS The window buffer size and container format. - bufsize: Py_ssize_t(c_default="DEF_BUF_SIZE") = DEF_BUF_SIZE + bufsize: Py_ssize_t(c_default="DEF_BUF_SIZE", allow_negative=False) = DEF_BUF_SIZE The initial output buffer size. Returns a bytes object containing the uncompressed data. @@ -436,22 +425,19 @@ Returns a bytes object containing the uncompressed data. static PyObject * zlib_decompress_impl(PyObject *module, Py_buffer *data, int wbits, Py_ssize_t bufsize) -/*[clinic end generated code: output=77c7e35111dc8c42 input=a9ac17beff1f893f]*/ +/*[clinic end generated code: output=77c7e35111dc8c42 input=530077065b3a2233]*/ { PyObject *return_value; Byte *ibuf; Py_ssize_t ibuflen; int err, flush; z_stream zst; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; _Uint32Window window; // output buffer's UINT32_MAX sliding window zlibstate *state = get_zlib_state(module); - if (bufsize < 0) { - PyErr_SetString(PyExc_ValueError, "bufsize must be non-negative"); - return NULL; - } else if (bufsize == 0) { + if (bufsize == 0) { bufsize = 1; } @@ -564,7 +550,7 @@ zlib.compressobj strategy: int(c_default="Z_DEFAULT_STRATEGY") = Z_DEFAULT_STRATEGY Used to tune the compression algorithm. Possible values are Z_DEFAULT_STRATEGY, Z_FILTERED, and Z_HUFFMAN_ONLY. - zdict: Py_buffer = None + zdict: Py_buffer = NULL The predefined compression dictionary - a sequence of bytes containing subsequences that are likely to occur in the input data. @@ -574,7 +560,7 @@ Return a compressor object. static PyObject * zlib_compressobj_impl(PyObject *module, int level, int method, int wbits, int memLevel, int strategy, Py_buffer *zdict) -/*[clinic end generated code: output=8b5bed9c8fc3814d input=2fa3d026f90ab8d5]*/ +/*[clinic end generated code: output=8b5bed9c8fc3814d input=1a6f61d8a8885c0d]*/ { zlibstate *state = get_zlib_state(module); if (zdict->buf != NULL && (size_t)zdict->len > UINT_MAX) { @@ -716,33 +702,41 @@ zlib_decompressobj_impl(PyObject *module, int wbits, PyObject *zdict) } static void -Dealloc(compobject *self) +compobject_dealloc_impl(PyObject *op, int (*dealloc)(z_streamp)) { - PyTypeObject *type = Py_TYPE(self); - PyThread_free_lock(self->lock); + PyTypeObject *type = Py_TYPE(op); + PyObject_GC_UnTrack(op); + compobject *self = _compobject_CAST(op); + assert(!PyMutex_IsLocked(&self->mutex)); + if (self->is_initialised) { + (void)dealloc(&self->zst); + } Py_XDECREF(self->unused_data); Py_XDECREF(self->unconsumed_tail); Py_XDECREF(self->zdict); - PyObject_Free(self); + type->tp_free(self); Py_DECREF(type); } +static int +compobject_traverse(PyObject *op, visitproc visit, void *arg) +{ + compobject *self = _compobject_CAST(op); + Py_VISIT(Py_TYPE(op)); + Py_VISIT(self->zdict); + return 0; +} + static void Comp_dealloc(PyObject *op) { - compobject *self = _compobject_CAST(op); - if (self->is_initialised) - (void)deflateEnd(&self->zst); - Dealloc(self); + compobject_dealloc_impl(op, &deflateEnd); } static void Decomp_dealloc(PyObject *op) { - compobject *self = _compobject_CAST(op); - if (self->is_initialised) - (void)inflateEnd(&self->zst); - Dealloc(self); + compobject_dealloc_impl(op, &inflateEnd); } /*[clinic input] @@ -767,10 +761,10 @@ zlib_Compress_compress_impl(compobject *self, PyTypeObject *cls, { PyObject *return_value; int err; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; zlibstate *state = PyType_GetModuleState(cls); - ENTER_ZLIB(self); + PyMutex_Lock(&self->mutex); self->zst.next_in = data->buf; Py_ssize_t ibuflen = data->len; @@ -812,7 +806,7 @@ zlib_Compress_compress_impl(compobject *self, PyTypeObject *cls, OutputBuffer_OnError(&buffer); return_value = NULL; success: - LEAVE_ZLIB(self); + PyMutex_Unlock(&self->mutex); return return_value; } @@ -826,22 +820,24 @@ save_unconsumed_input(compobject *self, Py_buffer *data, int err) input data in self->unused_data. */ if (self->zst.avail_in > 0) { Py_ssize_t old_size = PyBytes_GET_SIZE(self->unused_data); - Py_ssize_t new_size, left_size; - PyObject *new_data; + Py_ssize_t left_size; left_size = (Byte *)data->buf + data->len - self->zst.next_in; if (left_size > (PY_SSIZE_T_MAX - old_size)) { PyErr_NoMemory(); return -1; } - new_size = old_size + left_size; - new_data = PyBytes_FromStringAndSize(NULL, new_size); - if (new_data == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(old_size + left_size); + if (writer == NULL) { return -1; - memcpy(PyBytes_AS_STRING(new_data), - PyBytes_AS_STRING(self->unused_data), old_size); - memcpy(PyBytes_AS_STRING(new_data) + old_size, - self->zst.next_in, left_size); - Py_SETREF(self->unused_data, new_data); + } + char *new_data = PyBytesWriter_GetData(writer); + memcpy(new_data, PyBytes_AS_STRING(self->unused_data), old_size); + memcpy(new_data + old_size, self->zst.next_in, left_size); + PyObject *new_unused_data = PyBytesWriter_Finish(writer); + if (new_unused_data == NULL) { + return -1; + } + Py_SETREF(self->unused_data, new_unused_data); self->zst.avail_in = 0; } } @@ -862,47 +858,45 @@ save_unconsumed_input(compobject *self, Py_buffer *data, int err) } /*[clinic input] +@permit_long_summary zlib.Decompress.decompress cls: defining_class data: Py_buffer The binary data to decompress. / - max_length: Py_ssize_t = 0 + max_length: Py_ssize_t(allow_negative=False) = 0 The maximum allowable length of the decompressed data. Unconsumed input data will be stored in the unconsumed_tail attribute. Return a bytes object containing the decompressed version of the data. -After calling this function, some of the input data may still be stored in -internal buffers for later processing. +After calling this function, some of the input data may still be +stored in internal buffers for later processing. Call the flush() method to clear these buffers. [clinic start generated code]*/ static PyObject * zlib_Decompress_decompress_impl(compobject *self, PyTypeObject *cls, Py_buffer *data, Py_ssize_t max_length) -/*[clinic end generated code: output=b024a93c2c922d57 input=bfb37b3864cfb606]*/ +/*[clinic end generated code: output=b024a93c2c922d57 input=9035027c9e4be7fd]*/ { int err = Z_OK; Py_ssize_t ibuflen; PyObject *return_value; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; PyObject *module = PyType_GetModule(cls); if (module == NULL) return NULL; zlibstate *state = get_zlib_state(module); - if (max_length < 0) { - PyErr_SetString(PyExc_ValueError, "max_length must be non-negative"); - return NULL; - } else if (max_length == 0) { + if (max_length == 0) { max_length = -1; } - ENTER_ZLIB(self); + PyMutex_Lock(&self->mutex); self->zst.next_in = data->buf; ibuflen = data->len; @@ -955,7 +949,7 @@ zlib_Decompress_decompress_impl(compobject *self, PyTypeObject *cls, if (err == Z_STREAM_END) { /* This is the logical place to call inflateEnd, but the old behaviour of only calling it on flush() is preserved. */ - self->eof = 1; + FT_ATOMIC_STORE_CHAR_RELAXED(self->eof, 1); } else if (err != Z_OK && err != Z_BUF_ERROR) { /* We will only get Z_BUF_ERROR if the output buffer was full but there wasn't more output when we tried again, so it is @@ -974,7 +968,7 @@ zlib_Decompress_decompress_impl(compobject *self, PyTypeObject *cls, OutputBuffer_OnError(&buffer); return_value = NULL; success: - LEAVE_ZLIB(self); + PyMutex_Unlock(&self->mutex); return return_value; } @@ -998,16 +992,16 @@ zlib_Compress_flush_impl(compobject *self, PyTypeObject *cls, int mode) { int err; PyObject *return_value; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; zlibstate *state = PyType_GetModuleState(cls); /* Flushing with Z_NO_FLUSH is a no-op, so there's no point in doing any work at all; just return an empty string. */ if (mode == Z_NO_FLUSH) { - return PyBytes_FromStringAndSize(NULL, 0); + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } - ENTER_ZLIB(self); + PyMutex_Lock(&self->mutex); self->zst.avail_in = 0; @@ -1063,7 +1057,7 @@ zlib_Compress_flush_impl(compobject *self, PyTypeObject *cls, int mode) OutputBuffer_OnError(&buffer); return_value = NULL; success: - LEAVE_ZLIB(self); + PyMutex_Unlock(&self->mutex); return return_value; } @@ -1087,9 +1081,9 @@ zlib_Compress_copy_impl(compobject *self, PyTypeObject *cls) if (!return_value) return NULL; /* Copy the zstream state - * We use ENTER_ZLIB / LEAVE_ZLIB to make this thread-safe + * We use mutex to make this thread-safe */ - ENTER_ZLIB(self); + PyMutex_Lock(&self->mutex); int err = deflateCopy(&return_value->zst, &self->zst); switch (err) { case Z_OK: @@ -1113,11 +1107,11 @@ zlib_Compress_copy_impl(compobject *self, PyTypeObject *cls) /* Mark it as being initialized */ return_value->is_initialised = 1; - LEAVE_ZLIB(self); + PyMutex_Unlock(&self->mutex); return (PyObject *)return_value; error: - LEAVE_ZLIB(self); + PyMutex_Unlock(&self->mutex); Py_XDECREF(return_value); return NULL; } @@ -1171,9 +1165,9 @@ zlib_Decompress_copy_impl(compobject *self, PyTypeObject *cls) if (!return_value) return NULL; /* Copy the zstream state - * We use ENTER_ZLIB / LEAVE_ZLIB to make this thread-safe + * We use mutex to make this thread-safe */ - ENTER_ZLIB(self); + PyMutex_Lock(&self->mutex); int err = inflateCopy(&return_value->zst, &self->zst); switch (err) { case Z_OK: @@ -1198,11 +1192,11 @@ zlib_Decompress_copy_impl(compobject *self, PyTypeObject *cls) /* Mark it as being initialized */ return_value->is_initialised = 1; - LEAVE_ZLIB(self); + PyMutex_Unlock(&self->mutex); return (PyObject *)return_value; error: - LEAVE_ZLIB(self); + PyMutex_Unlock(&self->mutex); Py_XDECREF(return_value); return NULL; } @@ -1260,7 +1254,7 @@ zlib_Decompress_flush_impl(compobject *self, PyTypeObject *cls, Py_buffer data; PyObject *return_value; Py_ssize_t ibuflen; - _BlocksOutputBuffer buffer = {.list = NULL}; + _BlocksOutputBuffer buffer = {.writer = NULL}; _Uint32Window window; // output buffer's UINT32_MAX sliding window PyObject *module = PyType_GetModule(cls); @@ -1275,10 +1269,10 @@ zlib_Decompress_flush_impl(compobject *self, PyTypeObject *cls, return NULL; } - ENTER_ZLIB(self); + PyMutex_Lock(&self->mutex); if (PyObject_GetBuffer(self->unconsumed_tail, &data, PyBUF_SIMPLE) == -1) { - LEAVE_ZLIB(self); + PyMutex_Unlock(&self->mutex); return NULL; } @@ -1326,7 +1320,7 @@ zlib_Decompress_flush_impl(compobject *self, PyTypeObject *cls, /* If at end of stream, clean up any memory allocated by zlib. */ if (err == Z_STREAM_END) { - self->eof = 1; + FT_ATOMIC_STORE_CHAR_RELAXED(self->eof, 1); self->is_initialised = 0; err = inflateEnd(&self->zst); if (err != Z_OK) { @@ -1345,7 +1339,7 @@ zlib_Decompress_flush_impl(compobject *self, PyTypeObject *cls, return_value = NULL; success: PyBuffer_Release(&data); - LEAVE_ZLIB(self); + PyMutex_Unlock(&self->mutex); return return_value; } @@ -1354,7 +1348,7 @@ typedef struct { PyObject_HEAD z_stream zst; PyObject *zdict; - PyThread_type_lock lock; + PyMutex mutex; PyObject *unused_data; uint8_t *input_buffer; Py_ssize_t input_buffer_size; @@ -1367,27 +1361,39 @@ typedef struct { char needs_input; } ZlibDecompressor; +#define ZlibDecompressor_CAST(op) ((ZlibDecompressor *)(op)) + /*[clinic input] -class zlib.ZlibDecompressor "ZlibDecompressor *" "&ZlibDecompressorType" +class zlib._ZlibDecompressor "ZlibDecompressor *" "&ZlibDecompressorType" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=0658178ab94645df]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=49151d1d703e6bcc]*/ static void ZlibDecompressor_dealloc(PyObject *op) { - ZlibDecompressor *self = (ZlibDecompressor*)op; - PyObject *type = (PyObject *)Py_TYPE(self); - PyThread_free_lock(self->lock); + PyTypeObject *type = Py_TYPE(op); + PyObject_GC_UnTrack(op); + ZlibDecompressor *self = ZlibDecompressor_CAST(op); + assert(!PyMutex_IsLocked(&self->mutex)); if (self->is_initialised) { inflateEnd(&self->zst); } PyMem_Free(self->input_buffer); Py_CLEAR(self->unused_data); Py_CLEAR(self->zdict); - PyObject_Free(self); + type->tp_free(self); Py_DECREF(type); } +static int +ZlibDecompressor_traverse(PyObject *op, visitproc visit, void *arg) +{ + ZlibDecompressor *self = ZlibDecompressor_CAST(op); + Py_VISIT(Py_TYPE(op)); + Py_VISIT(self->zdict); + return 0; +} + static int set_inflate_zdict_ZlibDecompressor(zlibstate *state, ZlibDecompressor *self) { @@ -1526,7 +1532,7 @@ decompress_buf(ZlibDecompressor *self, Py_ssize_t max_length) } while(err != Z_STREAM_END && self->avail_in_real != 0); if (err == Z_STREAM_END) { - self->eof = 1; + FT_ATOMIC_STORE_CHAR_RELAXED(self->eof, 1); self->is_initialised = 0; /* Unlike the Decompress object we call inflateEnd here as there are no backwards compatibility issues */ @@ -1614,7 +1620,7 @@ decompress(ZlibDecompressor *self, uint8_t *data, } if (self->eof) { - self->needs_input = 0; + FT_ATOMIC_STORE_CHAR_RELAXED(self->needs_input, 0); if (self->avail_in_real > 0) { PyObject *unused_data = PyBytes_FromStringAndSize( @@ -1627,10 +1633,10 @@ decompress(ZlibDecompressor *self, uint8_t *data, } else if (self->avail_in_real == 0) { self->zst.next_in = NULL; - self->needs_input = 1; + FT_ATOMIC_STORE_CHAR_RELAXED(self->needs_input, 1); } else { - self->needs_input = 0; + FT_ATOMIC_STORE_CHAR_RELAXED(self->needs_input, 0); /* If we did not use the input buffer, we now have to copy the tail from the caller's buffer into the @@ -1663,83 +1669,77 @@ decompress(ZlibDecompressor *self, uint8_t *data, return result; error: + self->zst.next_in = NULL; Py_XDECREF(result); return NULL; } /*[clinic input] -zlib.ZlibDecompressor.decompress +zlib._ZlibDecompressor.decompress data: Py_buffer max_length: Py_ssize_t=-1 Decompress *data*, returning uncompressed data as bytes. -If *max_length* is nonnegative, returns at most *max_length* bytes of -decompressed data. If this limit is reached and further output can be -produced, *self.needs_input* will be set to ``False``. In this case, the next -call to *decompress()* may provide *data* as b'' to obtain more of the output. +If *max_length* is nonnegative, returns at most *max_length* bytes +of decompressed data. If this limit is reached and further output +can be produced, *self.needs_input* will be set to ``False``. In +this case, the next call to *decompress()* may provide *data* as b'' +to obtain more of the output. -If all of the input data was decompressed and returned (either because this -was less than *max_length* bytes, or because *max_length* was negative), -*self.needs_input* will be set to True. +If all of the input data was decompressed and returned (either +because this was less than *max_length* bytes, or because +*max_length* was negative), *self.needs_input* will be set to True. -Attempting to decompress data after the end of stream is reached raises an -EOFError. Any data found after the end of the stream is ignored and saved in -the unused_data attribute. +Attempting to decompress data after the end of stream is reached +raises an EOFError. Any data found after the end of the stream is +ignored and saved in the unused_data attribute. [clinic start generated code]*/ static PyObject * -zlib_ZlibDecompressor_decompress_impl(ZlibDecompressor *self, - Py_buffer *data, Py_ssize_t max_length) -/*[clinic end generated code: output=990d32787b775f85 input=0b29d99715250b96]*/ +zlib__ZlibDecompressor_decompress_impl(ZlibDecompressor *self, + Py_buffer *data, + Py_ssize_t max_length) +/*[clinic end generated code: output=ac00dcf73e843e99 input=d7862eade3f29d56]*/ { PyObject *result = NULL; - ENTER_ZLIB(self); + PyMutex_Lock(&self->mutex); if (self->eof) { PyErr_SetString(PyExc_EOFError, "End of stream already reached"); } else { result = decompress(self, data->buf, data->len, max_length); } - LEAVE_ZLIB(self); + PyMutex_Unlock(&self->mutex); return result; } -PyDoc_STRVAR(ZlibDecompressor__new____doc__, -"_ZlibDecompressor(wbits=15, zdict=b\'\')\n" -"--\n" -"\n" -"Create a decompressor object for decompressing data incrementally.\n" -"\n" -" wbits = 15\n" -" zdict\n" -" The predefined compression dictionary. This is a sequence of bytes\n" -" (such as a bytes object) containing subsequences that are expected\n" -" to occur frequently in the data that is to be compressed. Those\n" -" subsequences that are expected to be most common should come at the\n" -" end of the dictionary. This must be the same dictionary as used by the\n" -" compressor that produced the input data.\n" -"\n"); +/*[clinic input] +@classmethod +zlib._ZlibDecompressor.__new__ + + wbits: int(c_default='MAX_WBITS') = MAX_WBITS + zdict: object(c_default='NULL') = b'' + The predefined compression dictionary. This is a sequence of bytes + (such as a bytes object) containing subsequences that are expected + to occur frequently in the data that is to be compressed. Those + subsequences that are expected to be most common should come at the + end of the dictionary. This must be the same dictionary as used by the + compressor that produced the input data. + +Create a decompressor object for decompressing data incrementally. +[clinic start generated code]*/ static PyObject * -ZlibDecompressor__new__(PyTypeObject *cls, - PyObject *args, - PyObject *kwargs) +zlib__ZlibDecompressor_impl(PyTypeObject *type, int wbits, PyObject *zdict) +/*[clinic end generated code: output=1065607df0d33baa input=9ebad0be6de226e2]*/ { - static char *keywords[] = {"wbits", "zdict", NULL}; - static const char * const format = "|iO:_ZlibDecompressor"; - int wbits = MAX_WBITS; - PyObject *zdict = NULL; - zlibstate *state = PyType_GetModuleState(cls); - - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, format, keywords, &wbits, &zdict)) { - return NULL; - } - ZlibDecompressor *self = PyObject_New(ZlibDecompressor, cls); + assert(type != NULL && type->tp_alloc != NULL); + zlibstate *state = PyType_GetModuleState(type); + ZlibDecompressor *self = ZlibDecompressor_CAST(type->tp_alloc(type, 0)); if (self == NULL) { return NULL; } @@ -1754,17 +1754,8 @@ ZlibDecompressor__new__(PyTypeObject *cls, self->zst.zfree = PyZlib_Free; self->zst.next_in = NULL; self->zst.avail_in = 0; - self->unused_data = PyBytes_FromStringAndSize(NULL, 0); - if (self->unused_data == NULL) { - Py_CLEAR(self); - return NULL; - } - self->lock = PyThread_allocate_lock(); - if (self->lock == NULL) { - Py_DECREF(self); - PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); - return NULL; - } + self->unused_data = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + self->mutex = (PyMutex){0}; int err = inflateInit2(&(self->zst), wbits); switch (err) { case Z_OK: @@ -1815,14 +1806,40 @@ static PyMethodDef Decomp_methods[] = }; static PyMethodDef ZlibDecompressor_methods[] = { - ZLIB_ZLIBDECOMPRESSOR_DECOMPRESS_METHODDEF + ZLIB__ZLIBDECOMPRESSOR_DECOMPRESS_METHODDEF {NULL} }; +static PyObject * +Decomp_unused_data_get(PyObject *op, void *Py_UNUSED(ignored)) +{ + compobject *self = _compobject_CAST(op); + PyMutex_Lock(&self->mutex); + assert(self->unused_data != NULL); + PyObject *result = Py_NewRef(self->unused_data); + PyMutex_Unlock(&self->mutex); + return result; +} + +static PyObject * +Decomp_unconsumed_tail_get(PyObject *op, void *Py_UNUSED(ignored)) +{ + compobject *self = _compobject_CAST(op); + PyMutex_Lock(&self->mutex); + assert(self->unconsumed_tail != NULL); + PyObject *result = Py_NewRef(self->unconsumed_tail); + PyMutex_Unlock(&self->mutex); + return result; +} + +static PyGetSetDef Decomp_getset[] = { + {"unused_data", Decomp_unused_data_get, NULL, NULL}, + {"unconsumed_tail", Decomp_unconsumed_tail_get, NULL, NULL}, + {NULL}, +}; + #define COMP_OFF(x) offsetof(compobject, x) static PyMemberDef Decomp_members[] = { - {"unused_data", _Py_T_OBJECT, COMP_OFF(unused_data), Py_READONLY}, - {"unconsumed_tail", _Py_T_OBJECT, COMP_OFF(unconsumed_tail), Py_READONLY}, {"eof", Py_T_BOOL, COMP_OFF(eof), Py_READONLY}, {NULL}, }; @@ -1836,11 +1853,26 @@ PyDoc_STRVAR(ZlibDecompressor_unused_data__doc__, PyDoc_STRVAR(ZlibDecompressor_needs_input_doc, "True if more input is needed before more decompressed data can be produced."); +static PyObject * +ZlibDecompressor_unused_data_get(PyObject *op, void *Py_UNUSED(ignored)) +{ + ZlibDecompressor *self = ZlibDecompressor_CAST(op); + PyMutex_Lock(&self->mutex); + assert(self->unused_data != NULL); + PyObject *result = Py_NewRef(self->unused_data); + PyMutex_Unlock(&self->mutex); + return result; +} + +static PyGetSetDef ZlibDecompressor_getset[] = { + {"unused_data", ZlibDecompressor_unused_data_get, NULL, + ZlibDecompressor_unused_data__doc__}, + {NULL}, +}; + static PyMemberDef ZlibDecompressor_members[] = { {"eof", Py_T_BOOL, offsetof(ZlibDecompressor, eof), Py_READONLY, ZlibDecompressor_eof__doc__}, - {"unused_data", Py_T_OBJECT_EX, offsetof(ZlibDecompressor, unused_data), - Py_READONLY, ZlibDecompressor_unused_data__doc__}, {"needs_input", Py_T_BOOL, offsetof(ZlibDecompressor, needs_input), Py_READONLY, ZlibDecompressor_needs_input_doc}, {NULL}, @@ -2007,6 +2039,27 @@ zlib_crc32_combine_impl(PyObject *module, unsigned int crc1, return crc32_combine(crc1, crc2, len); } +static PyObject * +zlib_getattr(PyObject *self, PyObject *args) +{ + PyObject *name; + if (!PyArg_UnpackTuple(args, "__getattr__", 1, 1, &name)) { + return NULL; + } + + if (PyUnicode_Check(name) && PyUnicode_EqualToUTF8(name, "__version__")) { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "'__version__' is deprecated and slated for removal in Python 3.20", + 1) < 0) { + return NULL; + } + return PyUnicode_FromString("1.0"); + } + + PyErr_Format(PyExc_AttributeError, "module 'zlib' has no attribute %R", name); + return NULL; +} + static PyMethodDef zlib_methods[] = { ZLIB_ADLER32_METHODDEF @@ -2017,11 +2070,13 @@ static PyMethodDef zlib_methods[] = ZLIB_CRC32_COMBINE_METHODDEF ZLIB_DECOMPRESS_METHODDEF ZLIB_DECOMPRESSOBJ_METHODDEF + {"__getattr__", zlib_getattr, METH_VARARGS, "Module __getattr__"}, {NULL, NULL} }; static PyType_Slot Comptype_slots[] = { {Py_tp_dealloc, Comp_dealloc}, + {Py_tp_traverse, compobject_traverse}, {Py_tp_methods, comp_methods}, {0, 0}, }; @@ -2029,29 +2084,43 @@ static PyType_Slot Comptype_slots[] = { static PyType_Spec Comptype_spec = { .name = "zlib.Compress", .basicsize = sizeof(compobject), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_HAVE_GC + ), .slots= Comptype_slots, }; static PyType_Slot Decomptype_slots[] = { {Py_tp_dealloc, Decomp_dealloc}, + {Py_tp_traverse, compobject_traverse}, {Py_tp_methods, Decomp_methods}, {Py_tp_members, Decomp_members}, + {Py_tp_getset, Decomp_getset}, {0, 0}, }; static PyType_Spec Decomptype_spec = { .name = "zlib.Decompress", .basicsize = sizeof(compobject), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_DISALLOW_INSTANTIATION + | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_HAVE_GC + ), .slots = Decomptype_slots, }; static PyType_Slot ZlibDecompressor_type_slots[] = { {Py_tp_dealloc, ZlibDecompressor_dealloc}, + {Py_tp_traverse, ZlibDecompressor_traverse}, {Py_tp_members, ZlibDecompressor_members}, - {Py_tp_new, ZlibDecompressor__new__}, - {Py_tp_doc, (char *)ZlibDecompressor__new____doc__}, + {Py_tp_getset, ZlibDecompressor_getset}, + {Py_tp_new, zlib__ZlibDecompressor}, + {Py_tp_doc, (char *)zlib__ZlibDecompressor__doc__}, {Py_tp_methods, ZlibDecompressor_methods}, {0, 0}, }; @@ -2063,7 +2132,11 @@ static PyType_Spec ZlibDecompressor_type_spec = { // ZlibDecompressor_type_spec does not have Py_TPFLAGS_BASETYPE flag // which prevents to create a subclass. // So calling PyType_GetModuleState() in this file is always safe. - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE), + .flags = ( + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_HAVE_GC + ), .slots = ZlibDecompressor_type_slots, }; @@ -2196,13 +2269,11 @@ zlib_exec(PyObject *mod) return -1; } #endif - if (PyModule_AddStringConstant(mod, "__version__", "1.0") < 0) { - return -1; - } return 0; } static PyModuleDef_Slot zlib_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, zlib_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Objects/abstract.c b/Objects/abstract.c index df96b935eccb44..48b3137152e7bf 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -132,8 +132,9 @@ PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue) return defaultvalue; } if (!PyLong_Check(result)) { - PyErr_Format(PyExc_TypeError, "__length_hint__ must be an integer, not %.100s", - Py_TYPE(result)->tp_name); + PyErr_Format(PyExc_TypeError, + "%T.__length_hint__() must return an int, not %T", + o, result); Py_DECREF(result); return -1; } @@ -143,7 +144,8 @@ PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue) return -1; } if (res < 0) { - PyErr_Format(PyExc_ValueError, "__length_hint__() should return >= 0"); + PyErr_Format(PyExc_ValueError, + "%T.__length_hint__() must return a non-negative int", o); return -1; } return res; @@ -222,6 +224,14 @@ PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result) return 0; } +PyObject* +_PyMapping_GetOptionalItem2(PyObject *obj, PyObject *key, int *err) +{ + PyObject* result; + *err = PyMapping_GetOptionalItem(obj, key, &result); + return result; +} + int PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value) { @@ -614,7 +624,7 @@ PyBuffer_SizeFromFormat(const char *format) } int -PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, Py_ssize_t len, char fort) +PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, Py_ssize_t len, char order) { int k; void (*addone)(int, Py_ssize_t *, const Py_ssize_t *); @@ -626,7 +636,7 @@ PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, Py_ssize_t len, len = view->len; } - if (PyBuffer_IsContiguous(view, fort)) { + if (PyBuffer_IsContiguous(view, order)) { /* simplest copy is all that is needed */ memcpy(view->buf, buf, len); return 0; @@ -644,7 +654,7 @@ PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, Py_ssize_t len, indices[k] = 0; } - if (fort == 'F') { + if (order == 'F') { addone = _Py_add_one_to_index_F; } else { @@ -739,13 +749,13 @@ int PyObject_CopyData(PyObject *dest, PyObject *src) void PyBuffer_FillContiguousStrides(int nd, Py_ssize_t *shape, Py_ssize_t *strides, int itemsize, - char fort) + char order) { int k; Py_ssize_t sd; sd = itemsize; - if (fort == 'F') { + if (order == 'F') { for (k=0; k<nd; k++) { strides[k] = sd; sd *= shape[k]; @@ -887,8 +897,8 @@ PyObject_Format(PyObject *obj, PyObject *format_spec) if (result && !PyUnicode_Check(result)) { PyErr_Format(PyExc_TypeError, - "__format__ must return a str, not %.200s", - Py_TYPE(result)->tp_name); + "%T.__format__() must return a str, not %T", + obj, result); Py_SETREF(result, NULL); goto done; } @@ -1421,17 +1431,17 @@ _PyNumber_Index(PyObject *item) if (!PyLong_Check(result)) { PyErr_Format(PyExc_TypeError, - "__index__ returned non-int (type %.200s)", - Py_TYPE(result)->tp_name); + "%T.__index__() must return an int, not %T", + item, result); Py_DECREF(result); return NULL; } /* Issue #17576: warn if 'result' not of exact type int. */ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "__index__ returned non-int (type %.200s). " + "%T.__index__() must return an int, not %T. " "The ability to return an instance of a strict subclass of int " "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(result)->tp_name)) { + item, result)) { Py_DECREF(result); return NULL; } @@ -1531,17 +1541,17 @@ PyNumber_Long(PyObject *o) if (!PyLong_Check(result)) { PyErr_Format(PyExc_TypeError, - "__int__ returned non-int (type %.200s)", - Py_TYPE(result)->tp_name); + "%T.__int__() must return an int, not %T", + o, result); Py_DECREF(result); return NULL; } /* Issue #17576: warn if 'result' not of exact type int. */ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "__int__ returned non-int (type %.200s). " + "%T.__int__() must return an int, not %T. " "The ability to return an instance of a strict subclass of int " "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(result)->tp_name)) { + o, result)) { Py_DECREF(result); return NULL; } @@ -1609,17 +1619,16 @@ PyNumber_Float(PyObject *o) if (!PyFloat_Check(res)) { PyErr_Format(PyExc_TypeError, - "%.50s.__float__ returned non-float (type %.50s)", - Py_TYPE(o)->tp_name, Py_TYPE(res)->tp_name); + "%T.__float__() must return a float, not %T", o, res); Py_DECREF(res); return NULL; } /* Issue #26983: warn if 'res' not of exact type float. */ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "%.50s.__float__ returned non-float (type %.50s). " + "%T.__float__() must return a float, not %T. " "The ability to return an instance of a strict subclass of float " "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(o)->tp_name, Py_TYPE(res)->tp_name)) { + o, res)) { Py_DECREF(res); return NULL; } @@ -2435,10 +2444,8 @@ method_output_as_list(PyObject *o, PyObject *meth) PyThreadState *tstate = _PyThreadState_GET(); if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError)) { _PyErr_Format(tstate, PyExc_TypeError, - "%.200s.%U() returned a non-iterable (type %.200s)", - Py_TYPE(o)->tp_name, - meth, - Py_TYPE(meth_output)->tp_name); + "%T.%U() must return an iterable, not %T", + o, meth, meth_output); } Py_DECREF(meth_output); return NULL; @@ -2818,9 +2825,8 @@ PyObject_GetIter(PyObject *o) PyObject *res = (*f)(o); if (res != NULL && !PyIter_Check(res)) { PyErr_Format(PyExc_TypeError, - "iter() returned non-iterator " - "of type '%.100s'", - Py_TYPE(res)->tp_name); + "%T.__iter__() must return an iterator, not %T", + o, res); Py_SETREF(res, NULL); } return res; @@ -2839,8 +2845,8 @@ PyObject_GetAIter(PyObject *o) { PyObject *it = (*f)(o); if (it != NULL && !PyAIter_Check(it)) { PyErr_Format(PyExc_TypeError, - "aiter() returned not an async iterator of type '%.100s'", - Py_TYPE(it)->tp_name); + "%T.__aiter__() must return an async iterator, not %T", + o, it); Py_SETREF(it, NULL); } return it; @@ -2945,3 +2951,11 @@ PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result) } return PYGEN_ERROR; } + +PySendResultPair +_PyIter_Send(PyObject *iter, PyObject *arg) +{ + PySendResultPair pair; + pair.kind = PyIter_Send(iter, arg, &pair.object); + return pair; +} diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index bf30c06af5d8fa..696ddad8efaae5 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -17,8 +17,8 @@ class bytearray "PyByteArrayObject *" "&PyByteArray_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=5535b77c37a119e0]*/ -/* For PyByteArray_AS_STRING(). */ -char _PyByteArray_empty_string[] = ""; +/* Max number of bytes a bytearray can contain */ +#define PyByteArray_SIZE_MAX ((Py_ssize_t)(PY_SSIZE_T_MAX - _PyBytesObject_SIZE)) /* Helpers */ @@ -43,6 +43,14 @@ _getbytevalue(PyObject* arg, int *value) return 1; } +static void +bytearray_reinit_from_bytes(PyByteArrayObject *self, Py_ssize_t size, + Py_ssize_t alloc) { + self->ob_bytes = self->ob_start = PyBytes_AS_STRING(self->ob_bytes_object); + Py_SET_SIZE(self, size); + FT_ATOMIC_STORE_SSIZE_RELAXED(self->ob_alloc, alloc); +} + static int bytearray_getbuffer_lock_held(PyObject *self, Py_buffer *view, int flags) { @@ -82,6 +90,25 @@ bytearray_releasebuffer(PyObject *self, Py_buffer *view) Py_END_CRITICAL_SECTION(); } +typedef PyObject* (*_ba_bytes_op)(const char *buf, Py_ssize_t len, + PyObject *sub, Py_ssize_t start, + Py_ssize_t end); + +static PyObject * +_bytearray_with_buffer(PyByteArrayObject *self, _ba_bytes_op op, PyObject *sub, + Py_ssize_t start, Py_ssize_t end) +{ + PyObject *res; + + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); + + /* Increase exports to prevent bytearray storage from changing during op. */ + self->ob_exports++; + res = op(PyByteArray_AS_STRING(self), Py_SIZE(self), sub, start, end); + self->ob_exports--; + return res; +} + static int _canresize(PyByteArrayObject *self) { @@ -127,7 +154,6 @@ PyObject * PyByteArray_FromStringAndSize(const char *bytes, Py_ssize_t size) { PyByteArrayObject *new; - Py_ssize_t alloc; if (size < 0) { PyErr_SetString(PyExc_SystemError, @@ -135,34 +161,31 @@ PyByteArray_FromStringAndSize(const char *bytes, Py_ssize_t size) return NULL; } - /* Prevent buffer overflow when setting alloc to size+1. */ - if (size == PY_SSIZE_T_MAX) { - return PyErr_NoMemory(); - } - new = PyObject_New(PyByteArrayObject, &PyByteArray_Type); - if (new == NULL) + if (new == NULL) { return NULL; + } + + /* Fill values used in bytearray_dealloc. - if (size == 0) { - new->ob_bytes = NULL; - alloc = 0; + In an optimized build the memory isn't zeroed and ob_exports would be + uninitialized when when PyBytes_FromStringAndSize errored leading to + intermittent test failures. */ + new->ob_exports = 0; + + /* Optimization: size=0 bytearray should not allocate space + + PyBytes_FromStringAndSize returns the empty bytes global when size=0 so + no allocation occurs. */ + new->ob_bytes_object = PyBytes_FromStringAndSize(NULL, size); + if (new->ob_bytes_object == NULL) { + Py_DECREF(new); + return NULL; } - else { - alloc = size + 1; - new->ob_bytes = PyMem_Malloc(alloc); - if (new->ob_bytes == NULL) { - Py_DECREF(new); - return PyErr_NoMemory(); - } - if (bytes != NULL && size > 0) - memcpy(new->ob_bytes, bytes, size); - new->ob_bytes[size] = '\0'; /* Trailing null byte */ + bytearray_reinit_from_bytes(new, size, size); + if (bytes != NULL && size > 0) { + memcpy(new->ob_bytes, bytes, size); } - Py_SET_SIZE(new, size); - new->ob_alloc = alloc; - new->ob_start = new->ob_bytes; - new->ob_exports = 0; return (PyObject *)new; } @@ -189,7 +212,6 @@ static int bytearray_resize_lock_held(PyObject *self, Py_ssize_t requested_size) { _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); - void *sval; PyByteArrayObject *obj = ((PyByteArrayObject *)self); /* All computations are done unsigned to avoid integer overflows (see issue #22335). */ @@ -214,16 +236,17 @@ bytearray_resize_lock_held(PyObject *self, Py_ssize_t requested_size) return -1; } - if (size + logical_offset + 1 <= alloc) { + if (size + logical_offset <= alloc) { /* Current buffer is large enough to host the requested size, decide on a strategy. */ if (size < alloc / 2) { /* Major downsize; resize down to exact size */ - alloc = size + 1; + alloc = size; } else { /* Minor downsize; quick exit */ Py_SET_SIZE(self, size); + /* Add mid-buffer null; end provided by bytes. */ PyByteArray_AS_STRING(self)[size] = '\0'; /* Trailing null */ return 0; } @@ -236,38 +259,36 @@ bytearray_resize_lock_held(PyObject *self, Py_ssize_t requested_size) } else { /* Major upsize; resize up to exact size */ - alloc = size + 1; + alloc = size; } } - if (alloc > PY_SSIZE_T_MAX) { + if (alloc > PyByteArray_SIZE_MAX) { PyErr_NoMemory(); return -1; } + /* Re-align data to the start of the allocation. */ if (logical_offset > 0) { - sval = PyMem_Malloc(alloc); - if (sval == NULL) { - PyErr_NoMemory(); - return -1; - } - memcpy(sval, PyByteArray_AS_STRING(self), - Py_MIN((size_t)requested_size, (size_t)Py_SIZE(self))); - PyMem_Free(obj->ob_bytes); - } - else { - sval = PyMem_Realloc(obj->ob_bytes, alloc); - if (sval == NULL) { - PyErr_NoMemory(); - return -1; - } + /* optimization tradeoff: This is faster than a new allocation when + the number of bytes being removed in a resize is small; for large + size changes it may be better to just make a new bytes object as + _PyBytes_Resize will do a malloc + memcpy internally. */ + memmove(obj->ob_bytes, obj->ob_start, + Py_MIN(requested_size, Py_SIZE(self))); } - obj->ob_bytes = obj->ob_start = sval; - Py_SET_SIZE(self, size); - FT_ATOMIC_STORE_SSIZE_RELAXED(obj->ob_alloc, alloc); - obj->ob_bytes[size] = '\0'; /* Trailing null byte */ + int ret = _PyBytes_Resize(&obj->ob_bytes_object, alloc); + if (ret == -1) { + obj->ob_bytes_object = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + size = alloc = 0; + } + bytearray_reinit_from_bytes(obj, size, alloc); + if (alloc != size) { + /* Add mid-buffer null; end provided by bytes. */ + obj->ob_bytes[size] = '\0'; + } - return 0; + return ret; } int @@ -295,7 +316,7 @@ PyByteArray_Concat(PyObject *a, PyObject *b) goto done; } - if (va.len > PY_SSIZE_T_MAX - vb.len) { + if (va.len > PyByteArray_SIZE_MAX - vb.len) { PyErr_NoMemory(); goto done; } @@ -339,7 +360,7 @@ bytearray_iconcat_lock_held(PyObject *op, PyObject *other) } Py_ssize_t size = Py_SIZE(self); - if (size > PY_SSIZE_T_MAX - vo.len) { + if (size > PyByteArray_SIZE_MAX - vo.len) { PyBuffer_Release(&vo); return PyErr_NoMemory(); } @@ -373,7 +394,7 @@ bytearray_repeat_lock_held(PyObject *op, Py_ssize_t count) count = 0; } const Py_ssize_t mysize = Py_SIZE(self); - if (count > 0 && mysize > PY_SSIZE_T_MAX / count) { + if (count > 0 && mysize > PyByteArray_SIZE_MAX / count) { return PyErr_NoMemory(); } Py_ssize_t size = mysize * count; @@ -381,7 +402,7 @@ bytearray_repeat_lock_held(PyObject *op, Py_ssize_t count) PyByteArrayObject* result = (PyByteArrayObject *)PyByteArray_FromStringAndSize(NULL, size); const char* buf = PyByteArray_AS_STRING(self); if (result != NULL && size != 0) { - _PyBytes_Repeat(result->ob_bytes, size, buf, mysize); + _PyBytes_RepeatBuffer(result->ob_bytes, size, buf, mysize); } return (PyObject *)result; } @@ -409,7 +430,7 @@ bytearray_irepeat_lock_held(PyObject *op, Py_ssize_t count) } const Py_ssize_t mysize = Py_SIZE(self); - if (count > 0 && mysize > PY_SSIZE_T_MAX / count) { + if (count > 0 && mysize > PyByteArray_SIZE_MAX / count) { return PyErr_NoMemory(); } const Py_ssize_t size = mysize * count; @@ -418,7 +439,7 @@ bytearray_irepeat_lock_held(PyObject *op, Py_ssize_t count) } char* buf = PyByteArray_AS_STRING(self); - _PyBytes_Repeat(buf, size, buf, mysize); + _PyBytes_RepeatBuffer(buf, size, buf, mysize); return Py_NewRef(self); } @@ -585,7 +606,7 @@ bytearray_setslice_linear(PyByteArrayObject *self, buf = PyByteArray_AS_STRING(self); } else if (growth > 0) { - if (Py_SIZE(self) > (Py_ssize_t)PY_SSIZE_T_MAX - growth) { + if (Py_SIZE(self) > PyByteArray_SIZE_MAX - growth) { PyErr_NoMemory(); return -1; } @@ -899,12 +920,23 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg, PyObject *it; PyObject *(*iternext)(PyObject *); + /* First __init__; set ob_bytes_object so ob_bytes is always non-null. */ + if (self->ob_bytes_object == NULL) { + self->ob_bytes_object = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + bytearray_reinit_from_bytes(self, 0, 0); + self->ob_exports = 0; + } + if (Py_SIZE(self) != 0) { /* Empty previous contents (yes, do this first of all!) */ if (PyByteArray_Resize((PyObject *)self, 0) < 0) return -1; } + /* Should be caused by first init or the resize to 0. */ + assert(self->ob_bytes_object == Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_BYTES)); + assert(self->ob_exports == 0); + /* Make a quick exit if no first argument */ if (arg == NULL) { if (encoding != NULL || errors != NULL) { @@ -926,9 +958,20 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg, return -1; } encoded = PyUnicode_AsEncodedString(arg, encoding, errors); - if (encoded == NULL) + if (encoded == NULL) { return -1; + } assert(PyBytes_Check(encoded)); + + /* Most encodes return a new unique bytes, just use it as buffer. */ + if (_PyObject_IsUniquelyReferenced(encoded) + && PyBytes_CheckExact(encoded)) + { + Py_ssize_t size = Py_SIZE(encoded); + self->ob_bytes_object = encoded; + bytearray_reinit_from_bytes(self, size, size); + return 0; + } new = bytearray_iconcat((PyObject*)self, encoded); Py_DECREF(encoded); if (new == NULL) @@ -1067,95 +1110,20 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg, return -1; } -/* Mostly copied from string_repr, but without the - "smart quote" functionality. */ static PyObject * bytearray_repr_lock_held(PyObject *op) { _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op); - PyByteArrayObject *self = _PyByteArray_CAST(op); - const char *className = _PyType_Name(Py_TYPE(self)); - const char *quote_prefix = "(b"; - const char *quote_postfix = ")"; - Py_ssize_t length = Py_SIZE(self); - /* 6 == strlen(quote_prefix) + 2 + strlen(quote_postfix) + 1 */ - Py_ssize_t newsize; - PyObject *v; - Py_ssize_t i; - char *bytes; - char c; - char *p; - int quote; - char *test, *start; - char *buffer; - - newsize = strlen(className); - if (length > (PY_SSIZE_T_MAX - 6 - newsize) / 4) { - PyErr_SetString(PyExc_OverflowError, - "bytearray object is too large to make repr"); + const char *className = _PyType_Name(Py_TYPE(op)); + PyObject *bytes_repr = _Py_bytes_repr(PyByteArray_AS_STRING(op), + PyByteArray_GET_SIZE(op), 1, + "bytearray"); + if (bytes_repr == NULL) { return NULL; } - - newsize += 6 + length * 4; - buffer = PyMem_Malloc(newsize); - if (buffer == NULL) { - PyErr_NoMemory(); - return NULL; - } - - /* Figure out which quote to use; single is preferred */ - quote = '\''; - start = PyByteArray_AS_STRING(self); - for (test = start; test < start+length; ++test) { - if (*test == '"') { - quote = '\''; /* back to single */ - break; - } - else if (*test == '\'') - quote = '"'; - } - - p = buffer; - while (*className) - *p++ = *className++; - while (*quote_prefix) - *p++ = *quote_prefix++; - *p++ = quote; - - bytes = PyByteArray_AS_STRING(self); - for (i = 0; i < length; i++) { - /* There's at least enough room for a hex escape - and a closing quote. */ - assert(newsize - (p - buffer) >= 5); - c = bytes[i]; - if (c == '\'' || c == '\\') - *p++ = '\\', *p++ = c; - else if (c == '\t') - *p++ = '\\', *p++ = 't'; - else if (c == '\n') - *p++ = '\\', *p++ = 'n'; - else if (c == '\r') - *p++ = '\\', *p++ = 'r'; - else if (c == 0) - *p++ = '\\', *p++ = 'x', *p++ = '0', *p++ = '0'; - else if (c < ' ' || c >= 0x7f) { - *p++ = '\\'; - *p++ = 'x'; - *p++ = Py_hexdigits[(c & 0xf0) >> 4]; - *p++ = Py_hexdigits[c & 0xf]; - } - else - *p++ = c; - } - assert(newsize - (p - buffer) >= 1); - *p++ = quote; - while (*quote_postfix) { - *p++ = *quote_postfix++; - } - - v = PyUnicode_FromStringAndSize(buffer, p - buffer); - PyMem_Free(buffer); - return v; + PyObject *res = PyUnicode_FromFormat("%s(%U)", className, bytes_repr); + Py_DECREF(bytes_repr); + return res; } static PyObject * @@ -1244,9 +1212,7 @@ bytearray_dealloc(PyObject *op) "deallocated bytearray object has exported buffers"); PyErr_Print(); } - if (self->ob_bytes != 0) { - PyMem_Free(self->ob_bytes); - } + Py_XDECREF(self->ob_bytes_object); Py_TYPE(self)->tp_free((PyObject *)self); } @@ -1279,6 +1245,7 @@ bytearray_dealloc(PyObject *op) /*[clinic input] +@permit_long_summary @critical_section @text_signature "($self, sub[, start[, end]], /)" bytearray.find @@ -1298,13 +1265,13 @@ Return -1 on failure. static PyObject * bytearray_find_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=413e1cab2ae87da0 input=1de9f4558df68336]*/ +/*[clinic end generated code: output=413e1cab2ae87da0 input=df3aa94840d893a7]*/ { - return _Py_bytes_find(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + return _bytearray_with_buffer(self, _Py_bytes_find, sub, start, end); } /*[clinic input] +@permit_long_summary @critical_section bytearray.count = bytearray.find @@ -1314,10 +1281,9 @@ Return the number of non-overlapping occurrences of subsection 'sub' in bytes B[ static PyObject * bytearray_count_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=a21ee2692e4f1233 input=2608c30644614724]*/ +/*[clinic end generated code: output=a21ee2692e4f1233 input=e8fcdca8272857e0]*/ { - return _Py_bytes_count(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + return _bytearray_with_buffer(self, _Py_bytes_count, sub, start, end); } /*[clinic input] @@ -1351,6 +1317,7 @@ bytearray_copy_impl(PyByteArrayObject *self) } /*[clinic input] +@permit_long_summary @critical_section bytearray.index = bytearray.find @@ -1362,13 +1329,13 @@ Raise ValueError if the subsection is not found. static PyObject * bytearray_index_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=067a1e78efc672a7 input=0086ba0ab9bf44a5]*/ +/*[clinic end generated code: output=067a1e78efc672a7 input=c37f177cfee19fe4]*/ { - return _Py_bytes_index(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + return _bytearray_with_buffer(self, _Py_bytes_index, sub, start, end); } /*[clinic input] +@permit_long_summary @critical_section bytearray.rfind = bytearray.find @@ -1380,13 +1347,13 @@ Return -1 on failure. static PyObject * bytearray_rfind_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=51bf886f932b283c input=ac73593305d5c1d1]*/ +/*[clinic end generated code: output=51bf886f932b283c input=1265b11c437d2750]*/ { - return _Py_bytes_rfind(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + return _bytearray_with_buffer(self, _Py_bytes_rfind, sub, start, end); } /*[clinic input] +@permit_long_summary @critical_section bytearray.rindex = bytearray.find @@ -1398,25 +1365,30 @@ Raise ValueError if the subsection is not found. static PyObject * bytearray_rindex_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=38e1cf66bafb08b9 input=0cf331bf5ebe0e91]*/ +/*[clinic end generated code: output=38e1cf66bafb08b9 input=7d198b3d6b0a62ce]*/ { - return _Py_bytes_rindex(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - sub, start, end); + return _bytearray_with_buffer(self, _Py_bytes_rindex, sub, start, end); } static int bytearray_contains(PyObject *self, PyObject *arg) { - int ret; + int ret = -1; Py_BEGIN_CRITICAL_SECTION(self); - ret = _Py_bytes_contains(PyByteArray_AS_STRING(self), + PyByteArrayObject *ba = _PyByteArray_CAST(self); + + /* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */ + ba->ob_exports++; + ret = _Py_bytes_contains(PyByteArray_AS_STRING(ba), PyByteArray_GET_SIZE(self), arg); + ba->ob_exports--; Py_END_CRITICAL_SECTION(); return ret; } /*[clinic input] +@permit_long_summary @critical_section @text_signature "($self, prefix[, start[, end]], /)" bytearray.startswith @@ -1435,13 +1407,13 @@ Return True if the bytearray starts with the specified prefix, False otherwise. static PyObject * bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=a3d9b6d44d3662a6 input=ea8d036d09df34b2]*/ +/*[clinic end generated code: output=a3d9b6d44d3662a6 input=93f9ffee684f109a]*/ { - return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - subobj, start, end); + return _bytearray_with_buffer(self, _Py_bytes_startswith, subobj, start, end); } /*[clinic input] +@permit_long_summary @critical_section @text_signature "($self, suffix[, start[, end]], /)" bytearray.endswith @@ -1460,10 +1432,9 @@ Return True if the bytearray ends with the specified suffix, False otherwise. static PyObject * bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=e75ea8c227954caa input=c61b90bb23a689ce]*/ +/*[clinic end generated code: output=e75ea8c227954caa input=d158b030a11d0b06]*/ { - return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), - subobj, start, end); + return _bytearray_with_buffer(self, _Py_bytes_endswith, subobj, start, end); } /*[clinic input] @@ -1535,19 +1506,20 @@ bytearray_removesuffix_impl(PyByteArrayObject *self, Py_buffer *suffix) /*[clinic input] +@critical_section bytearray.resize size: Py_ssize_t - New size to resize to.. + New size to resize to. / Resize the internal buffer of bytearray to len. [clinic start generated code]*/ static PyObject * bytearray_resize_impl(PyByteArrayObject *self, Py_ssize_t size) -/*[clinic end generated code: output=f73524922990b2d9 input=75fd4d17c4aa47d3]*/ +/*[clinic end generated code: output=f73524922990b2d9 input=116046316a2b5cfc]*/ { Py_ssize_t start_size = PyByteArray_GET_SIZE(self); - int result = PyByteArray_Resize((PyObject *)self, size); + int result = bytearray_resize_lock_held((PyObject *)self, size); if (result < 0) { return NULL; } @@ -1561,6 +1533,95 @@ bytearray_resize_impl(PyByteArrayObject *self, Py_ssize_t size) /*[clinic input] @critical_section +bytearray.take_bytes + n: object = None + Bytes to take, negative indexes from end. None indicates all bytes. + / +Take *n* bytes from the bytearray and return them as a bytes object. +[clinic start generated code]*/ + +static PyObject * +bytearray_take_bytes_impl(PyByteArrayObject *self, PyObject *n) +/*[clinic end generated code: output=3147fbc0bbbe8d94 input=b15b5172cdc6deda]*/ +{ + Py_ssize_t to_take; + Py_ssize_t size = Py_SIZE(self); + if (Py_IsNone(n)) { + to_take = size; + } + // Integer index, from start (zero, positive) or end (negative). + else if (_PyIndex_Check(n)) { + to_take = PyNumber_AsSsize_t(n, PyExc_IndexError); + if (to_take == -1 && PyErr_Occurred()) { + return NULL; + } + if (to_take < 0) { + to_take += size; + } + } + else { + PyErr_SetString(PyExc_TypeError, "n must be an integer or None"); + return NULL; + } + + if (to_take < 0 || to_take > size) { + PyErr_Format(PyExc_IndexError, + "can't take %zd bytes outside size %zd", + to_take, size); + return NULL; + } + + // Exports may change the contents. No mutable bytes allowed. + if (!_canresize(self)) { + return NULL; + } + + if (to_take == 0 || size == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } + + Py_ssize_t remaining_length = size - to_take; + // optimization: If taking less than leaving, just copy the small to_take + // portion out and move ob_start. + if (to_take < remaining_length) { + PyObject *ret = PyBytes_FromStringAndSize(self->ob_start, to_take); + if (ret == NULL) { + return NULL; + } + self->ob_start += to_take; + Py_SET_SIZE(self, remaining_length); + return ret; + } + + // Copy remaining bytes to a new bytes. + PyObject *remaining = PyBytes_FromStringAndSize(self->ob_start + to_take, + remaining_length); + if (remaining == NULL) { + return NULL; + } + + // If the bytes are offset inside the buffer must first align. + if (self->ob_start != self->ob_bytes) { + memmove(self->ob_bytes, self->ob_start, to_take); + self->ob_start = self->ob_bytes; + } + + if (_PyBytes_Resize(&self->ob_bytes_object, to_take) == -1) { + Py_DECREF(remaining); + return NULL; + } + + // Point the bytearray towards the buffer with the remaining data. + PyObject *result = self->ob_bytes_object; + self->ob_bytes_object = remaining; + bytearray_reinit_from_bytes(self, remaining_length, remaining_length); + return result; +} + + +/*[clinic input] +@permit_long_summary +@critical_section bytearray.translate table: object @@ -1570,14 +1631,15 @@ bytearray.translate Return a copy with each character mapped by the given translation table. -All characters occurring in the optional argument delete are removed. -The remaining characters are mapped through the given translation table. +All characters occurring in the optional argument delete are +removed. The remaining characters are mapped through the given +translation table. [clinic start generated code]*/ static PyObject * bytearray_translate_impl(PyByteArrayObject *self, PyObject *table, PyObject *deletechars) -/*[clinic end generated code: output=b6a8f01c2a74e446 input=cd6fa93ca04e05bc]*/ +/*[clinic end generated code: output=b6a8f01c2a74e446 input=e30d2ae004365ed9]*/ { char *input, *output; const char *table_chars; @@ -1666,6 +1728,7 @@ bytearray_translate_impl(PyByteArrayObject *self, PyObject *table, /*[clinic input] +@permit_long_summary @staticmethod bytearray.maketrans @@ -1675,15 +1738,15 @@ bytearray.maketrans Return a translation table usable for the bytes or bytearray translate method. -The returned table will be one where each byte in frm is mapped to the byte at -the same position in to. +The returned table will be one where each byte in frm is mapped to +the byte at the same position in to. The bytes objects frm and to must be of the same length. [clinic start generated code]*/ static PyObject * bytearray_maketrans_impl(Py_buffer *frm, Py_buffer *to) -/*[clinic end generated code: output=1df267d99f56b15e input=b10de38c85950a63]*/ +/*[clinic end generated code: output=1df267d99f56b15e input=c2f5f6e7e6b0221d]*/ { return _Py_bytes_maketrans(frm, to); } @@ -1695,21 +1758,21 @@ bytearray.replace old: Py_buffer new: Py_buffer + / count: Py_ssize_t = -1 Maximum number of occurrences to replace. -1 (the default value) means replace all occurrences. - / Return a copy with all occurrences of substring old replaced by new. -If the optional argument count is given, only the first count occurrences are -replaced. +If count is given, only the first count occurrences are replaced. +If count is not specified or -1, then all occurrences are replaced. [clinic start generated code]*/ static PyObject * bytearray_replace_impl(PyByteArrayObject *self, Py_buffer *old, Py_buffer *new, Py_ssize_t count) -/*[clinic end generated code: output=d39884c4dc59412a input=6992755672c8a807]*/ +/*[clinic end generated code: output=d39884c4dc59412a input=e2591806f954aec3]*/ { return stringlib_replace((PyObject *)self, (const char *)old->buf, old->len, @@ -1717,13 +1780,14 @@ bytearray_replace_impl(PyByteArrayObject *self, Py_buffer *old, } /*[clinic input] +@permit_long_summary @critical_section bytearray.split sep: object = None The delimiter according which to split the bytearray. - None (the default value) means split on ASCII whitespace characters - (space, tab, return, newline, formfeed, vertical tab). + None (the default value) means split on ASCII whitespace + characters (space, tab, return, newline, formfeed, vertical tab). maxsplit: Py_ssize_t = -1 Maximum number of splits to do. -1 (the default value) means no limit. @@ -1734,28 +1798,34 @@ Return a list of the sections in the bytearray, using sep as the delimiter. static PyObject * bytearray_split_impl(PyByteArrayObject *self, PyObject *sep, Py_ssize_t maxsplit) -/*[clinic end generated code: output=833e2cf385d9a04d input=1c367486b9938909]*/ +/*[clinic end generated code: output=833e2cf385d9a04d input=45605178023b52ac]*/ { - Py_ssize_t len = PyByteArray_GET_SIZE(self), n; - const char *s = PyByteArray_AS_STRING(self), *sub; - PyObject *list; - Py_buffer vsub; + PyObject *list = NULL; + + /* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */ + self->ob_exports++; + const char *sbuf = PyByteArray_AS_STRING(self); + Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self); if (maxsplit < 0) maxsplit = PY_SSIZE_T_MAX; - if (sep == Py_None) - return stringlib_split_whitespace((PyObject*) self, s, len, maxsplit); + if (sep == Py_None) { + list = stringlib_split_whitespace((PyObject*)self, sbuf, slen, maxsplit); + goto done; + } - if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) - return NULL; - sub = vsub.buf; - n = vsub.len; + Py_buffer vsub; + if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) { + goto done; + } - list = stringlib_split( - (PyObject*) self, s, len, sub, n, maxsplit - ); + list = stringlib_split((PyObject*)self, sbuf, slen, + (const char *)vsub.buf, vsub.len, maxsplit); PyBuffer_Release(&vsub); + +done: + self->ob_exports--; return list; } @@ -1768,17 +1838,18 @@ bytearray.partition Partition the bytearray into three parts using the given separator. -This will search for the separator sep in the bytearray. If the separator is -found, returns a 3-tuple containing the part before the separator, the -separator itself, and the part after it as new bytearray objects. +This will search for the separator sep in the bytearray. If the +separator is found, returns a 3-tuple containing the part before the +separator, the separator itself, and the part after it as new +bytearray objects. -If the separator is not found, returns a 3-tuple containing the copy of the -original bytearray object and two empty bytearray objects. +If the separator is not found, returns a 3-tuple containing the copy +of the original bytearray object and two empty bytearray objects. [clinic start generated code]*/ static PyObject * bytearray_partition_impl(PyByteArrayObject *self, PyObject *sep) -/*[clinic end generated code: output=b5fa1e03f10cfccb input=632855f986733f34]*/ +/*[clinic end generated code: output=b5fa1e03f10cfccb input=d76673ed03acf5dd]*/ { PyObject *bytesep, *result; @@ -1806,18 +1877,19 @@ bytearray.rpartition Partition the bytearray into three parts using the given separator. -This will search for the separator sep in the bytearray, starting at the end. -If the separator is found, returns a 3-tuple containing the part before the -separator, the separator itself, and the part after it as new bytearray -objects. +This will search for the separator sep in the bytearray, starting at +the end. If the separator is found, returns a 3-tuple containing +the part before the separator, the separator itself, and the part +after it as new bytearray objects. -If the separator is not found, returns a 3-tuple containing two empty bytearray -objects and the copy of the original bytearray object. +If the separator is not found, returns a 3-tuple containing two +empty bytearray objects and the copy of the original bytearray +object. [clinic start generated code]*/ static PyObject * bytearray_rpartition_impl(PyByteArrayObject *self, PyObject *sep) -/*[clinic end generated code: output=0186ce7b1ef61289 input=4318e3d125497450]*/ +/*[clinic end generated code: output=0186ce7b1ef61289 input=b9216a2074174a36]*/ { PyObject *bytesep, *result; @@ -1837,39 +1909,47 @@ bytearray_rpartition_impl(PyByteArrayObject *self, PyObject *sep) } /*[clinic input] +@permit_long_summary @critical_section bytearray.rsplit = bytearray.split Return a list of the sections in the bytearray, using sep as the delimiter. -Splitting is done starting at the end of the bytearray and working to the front. +Splitting is done starting at the end of the bytearray and working +to the front. [clinic start generated code]*/ static PyObject * bytearray_rsplit_impl(PyByteArrayObject *self, PyObject *sep, Py_ssize_t maxsplit) -/*[clinic end generated code: output=a55e0b5a03cb6190 input=3cd513c2b94a53c1]*/ +/*[clinic end generated code: output=a55e0b5a03cb6190 input=e201671c9a0c19ee]*/ { - Py_ssize_t len = PyByteArray_GET_SIZE(self), n; - const char *s = PyByteArray_AS_STRING(self), *sub; - PyObject *list; - Py_buffer vsub; + PyObject *list = NULL; + + /* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */ + self->ob_exports++; + const char *sbuf = PyByteArray_AS_STRING(self); + Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self); if (maxsplit < 0) maxsplit = PY_SSIZE_T_MAX; - if (sep == Py_None) - return stringlib_rsplit_whitespace((PyObject*) self, s, len, maxsplit); + if (sep == Py_None) { + list = stringlib_rsplit_whitespace((PyObject*)self, sbuf, slen, maxsplit); + goto done; + } - if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) - return NULL; - sub = vsub.buf; - n = vsub.len; + Py_buffer vsub; + if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) { + goto done; + } - list = stringlib_rsplit( - (PyObject*) self, s, len, sub, n, maxsplit - ); + list = stringlib_rsplit((PyObject*)self, sbuf, slen, + (const char *)vsub.buf, vsub.len, maxsplit); PyBuffer_Release(&vsub); + +done: + self->ob_exports--; return list; } @@ -1928,11 +2008,6 @@ bytearray_insert_impl(PyByteArrayObject *self, Py_ssize_t index, int item) Py_ssize_t n = Py_SIZE(self); char *buf; - if (n == PY_SSIZE_T_MAX) { - PyErr_SetString(PyExc_OverflowError, - "cannot add more objects to bytearray"); - return NULL; - } if (bytearray_resize_lock_held((PyObject *)self, n + 1) < 0) return NULL; buf = PyByteArray_AS_STRING(self); @@ -2047,11 +2122,6 @@ bytearray_append_impl(PyByteArrayObject *self, int item) { Py_ssize_t n = Py_SIZE(self); - if (n == PY_SSIZE_T_MAX) { - PyErr_SetString(PyExc_OverflowError, - "cannot add more objects to bytearray"); - return NULL; - } if (bytearray_resize_lock_held((PyObject *)self, n + 1) < 0) return NULL; @@ -2091,6 +2161,7 @@ bytearray_expandtabs(PyObject *self, PyObject *const *args, Py_ssize_t nargs, Py } /*[clinic input] +@permit_long_summary @critical_section bytearray.extend @@ -2103,7 +2174,7 @@ Append all the items from the iterator or sequence to the end of the bytearray. static PyObject * bytearray_extend_impl(PyByteArrayObject *self, PyObject *iterable_of_ints) -/*[clinic end generated code: output=2f25e0ce72b98748 input=86e65beaba444650]*/ +/*[clinic end generated code: output=2f25e0ce72b98748 input=aeed44b025146632]*/ { PyObject *it, *item, *bytearray_obj; Py_ssize_t buf_size = 0, len = 0; @@ -2153,21 +2224,20 @@ bytearray_extend_impl(PyByteArrayObject *self, PyObject *iterable_of_ints) Py_DECREF(bytearray_obj); return NULL; } - buf[len++] = value; Py_DECREF(item); if (len >= buf_size) { Py_ssize_t addition; - if (len == PY_SSIZE_T_MAX) { + if (len == PyByteArray_SIZE_MAX) { Py_DECREF(it); Py_DECREF(bytearray_obj); return PyErr_NoMemory(); } - addition = len >> 1; - if (addition > PY_SSIZE_T_MAX - len - 1) - buf_size = PY_SSIZE_T_MAX; + addition = len ? len >> 1 : 1; + if (addition > PyByteArray_SIZE_MAX - len) + buf_size = PyByteArray_SIZE_MAX; else - buf_size = len + addition + 1; + buf_size = len + addition; if (bytearray_resize_lock_held((PyObject *)bytearray_obj, buf_size) < 0) { Py_DECREF(it); Py_DECREF(bytearray_obj); @@ -2177,6 +2247,7 @@ bytearray_extend_impl(PyByteArrayObject *self, PyObject *iterable_of_ints) have invalidated it. */ buf = PyByteArray_AS_STRING(bytearray_obj); } + buf[len++] = value; } Py_DECREF(it); @@ -2330,12 +2401,13 @@ bytearray.strip Strip leading and trailing bytes contained in the argument. -If the argument is omitted or None, strip leading and trailing ASCII whitespace. +If the argument is omitted or None, strip leading and trailing ASCII +whitespace. [clinic start generated code]*/ static PyObject * bytearray_strip_impl(PyByteArrayObject *self, PyObject *bytes) -/*[clinic end generated code: output=760412661a34ad5a input=1f9026e5ad35388a]*/ +/*[clinic end generated code: output=760412661a34ad5a input=f4ec5fa609df7d14]*/ { return bytearray_strip_impl_helper(self, bytes, BOTHSTRIP); } @@ -2435,11 +2507,11 @@ bytearray.decode encoding: str(c_default="NULL") = 'utf-8' The encoding with which to decode the bytearray. errors: str(c_default="NULL") = 'strict' - The error handling scheme to use for the handling of decoding errors. - The default is 'strict' meaning that decoding errors raise a - UnicodeDecodeError. Other possible values are 'ignore' and 'replace' - as well as any other name registered with codecs.register_error that - can handle UnicodeDecodeErrors. + The error handling scheme to use for the handling of decoding + errors. The default is 'strict' meaning that decoding errors + raise a UnicodeDecodeError. Other possible values are 'ignore' + and 'replace' as well as any other name registered with + codecs.register_error that can handle UnicodeDecodeErrors. Decode the bytearray using the codec registered for encoding. [clinic start generated code]*/ @@ -2447,7 +2519,7 @@ Decode the bytearray using the codec registered for encoding. static PyObject * bytearray_decode_impl(PyByteArrayObject *self, const char *encoding, const char *errors) -/*[clinic end generated code: output=f57d43f4a00b42c5 input=86c303ee376b8453]*/ +/*[clinic end generated code: output=f57d43f4a00b42c5 input=e51ce9b82b51e2ca]*/ { if (encoding == NULL) encoding = PyUnicode_GetDefaultEncoding(); @@ -2463,7 +2535,11 @@ static PyObject * bytearray_alloc(PyObject *op, PyObject *Py_UNUSED(ignored)) { PyByteArrayObject *self = _PyByteArray_CAST(op); - return PyLong_FromSsize_t(FT_ATOMIC_LOAD_SSIZE_RELAXED(self->ob_alloc)); + Py_ssize_t alloc = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->ob_alloc); + if (alloc > 0) { + alloc += _PyBytesObject_SIZE; + } + return PyLong_FromSsize_t(alloc); } /*[clinic input] @@ -2475,14 +2551,15 @@ bytearray.join Concatenate any number of bytes/bytearray objects. -The bytearray whose method is called is inserted in between each pair. +The bytearray whose method is called is inserted in between each +pair. The result is returned as a new bytearray object. [clinic start generated code]*/ static PyObject * bytearray_join_impl(PyByteArrayObject *self, PyObject *iterable_of_bytes) -/*[clinic end generated code: output=0ced382b5846a7ee input=49627e07ca31ca26]*/ +/*[clinic end generated code: output=0ced382b5846a7ee input=0a31db349efcd7fa]*/ { PyObject *ret; self->ob_exports++; // this protects `self` from being cleared/resized if `iterable_of_bytes` is a custom iterator @@ -2512,6 +2589,7 @@ bytearray_rjust(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } /*[clinic input] +@permit_long_summary @critical_section bytearray.splitlines @@ -2519,13 +2597,13 @@ bytearray.splitlines Return a list of the lines in the bytearray, breaking at line boundaries. -Line breaks are not included in the resulting list unless keepends is given and -true. +Line breaks are not included in the resulting list unless keepends +is given and true. [clinic start generated code]*/ static PyObject * bytearray_splitlines_impl(PyByteArrayObject *self, int keepends) -/*[clinic end generated code: output=4223c94b895f6ad9 input=874cd662866a66a1]*/ +/*[clinic end generated code: output=4223c94b895f6ad9 input=cc2bb740eed19f27]*/ { return stringlib_splitlines( (PyObject*) self, PyByteArray_AS_STRING(self), @@ -2543,12 +2621,13 @@ bytearray.fromhex Create a bytearray object from a string of hexadecimal numbers. Spaces between two numbers are accepted. -Example: bytearray.fromhex('B9 01EF') -> bytearray(b'\\xb9\\x01\\xef') +Example: + bytearray.fromhex('B9 01EF') -> bytearray(b'\\xb9\\x01\\xef') [clinic start generated code]*/ static PyObject * bytearray_fromhex_impl(PyTypeObject *type, PyObject *string) -/*[clinic end generated code: output=8f0f0b6d30fb3ba0 input=7e314e5b2d7ab484]*/ +/*[clinic end generated code: output=8f0f0b6d30fb3ba0 input=2243a8b0b9e66cd5]*/ { PyObject *result = _PyBytes_FromHex(string, type == &PyByteArray_Type); if (type != &PyByteArray_Type && result != NULL) { @@ -2563,9 +2642,9 @@ bytearray.hex sep: object = NULL An optional single character or byte to separate hex bytes. - bytes_per_sep: int = 1 - How many bytes between separators. Positive values count from the - right, negative values count from the left. + bytes_per_sep: Py_ssize_t = 1 + How many bytes between separators. Positive values count from + the right, negative values count from the left. Create a string of hexadecimal numbers from a bytearray object. @@ -2582,12 +2661,19 @@ Create a string of hexadecimal numbers from a bytearray object. [clinic start generated code]*/ static PyObject * -bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, int bytes_per_sep) -/*[clinic end generated code: output=29c4e5ef72c565a0 input=7784107de7048873]*/ +bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, + Py_ssize_t bytes_per_sep) +/*[clinic end generated code: output=c9563921aff1262b input=9ed746203691e894]*/ { char* argbuf = PyByteArray_AS_STRING(self); Py_ssize_t arglen = PyByteArray_GET_SIZE(self); - return _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep); + // Prevent 'self' from being freed if computing len(sep) mutates 'self' + // in _Py_strhex_with_sep(). + // See: https://github.com/python/cpython/issues/143195. + self->ob_exports++; + PyObject *res = _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep); + self->ob_exports--; + return res; } static PyObject * @@ -2657,9 +2743,13 @@ static PyObject * bytearray_sizeof_impl(PyByteArrayObject *self) /*[clinic end generated code: output=738abdd17951c427 input=e27320fd98a4bc5a]*/ { - size_t res = _PyObject_SIZE(Py_TYPE(self)); - res += (size_t)FT_ATOMIC_LOAD_SSIZE_RELAXED(self->ob_alloc) * sizeof(char); - return PyLong_FromSize_t(res); + Py_ssize_t res = _PyObject_SIZE(Py_TYPE(self)); + Py_ssize_t alloc = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->ob_alloc); + if (alloc > 0) { + res += _PyBytesObject_SIZE + alloc; + } + + return PyLong_FromSsize_t(res); } static PySequenceMethods bytearray_as_sequence = { @@ -2742,6 +2832,7 @@ static PyMethodDef bytearray_methods[] = { BYTEARRAY_STARTSWITH_METHODDEF BYTEARRAY_STRIP_METHODDEF {"swapcase", bytearray_swapcase, METH_NOARGS, _Py_swapcase__doc__}, + BYTEARRAY_TAKE_BYTES_METHODDEF {"title", bytearray_title, METH_NOARGS, _Py_title__doc__}, BYTEARRAY_TRANSLATE_METHODDEF {"upper", bytearray_upper, METH_NOARGS, _Py_upper__doc__}, @@ -2755,7 +2846,15 @@ bytearray_mod_lock_held(PyObject *v, PyObject *w) _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(v); if (!PyByteArray_Check(v)) Py_RETURN_NOTIMPLEMENTED; - return _PyBytes_FormatEx(PyByteArray_AS_STRING(v), PyByteArray_GET_SIZE(v), w, 1); + + PyByteArrayObject *self = _PyByteArray_CAST(v); + /* Increase exports to prevent bytearray storage from changing during op. */ + self->ob_exports++; + PyObject *res = _PyBytes_FormatEx( + PyByteArray_AS_STRING(v), PyByteArray_GET_SIZE(v), w, 1 + ); + self->ob_exports--; + return res; } static PyObject * diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c index c239ae18a593e3..414afeb7bb003c 100644 --- a/Objects/bytes_methods.c +++ b/Objects/bytes_methods.c @@ -277,8 +277,8 @@ _Py_bytes_upper(char *result, const char *cptr, Py_ssize_t len) PyDoc_STRVAR_shared(_Py_title__doc__, "B.title() -> copy of B\n\ \n\ -Return a titlecased version of B, i.e. ASCII words start with uppercase\n\ -characters, all remaining cased characters have lowercase."); +Return a titlecased version of B, i.e. ASCII words start with\n\ +uppercase characters, all remaining cased characters have lowercase."); void _Py_bytes_title(char *result, const char *s, Py_ssize_t len) @@ -356,26 +356,24 @@ The bytes objects frm and to must be of the same length."); PyObject * _Py_bytes_maketrans(Py_buffer *frm, Py_buffer *to) { - PyObject *res = NULL; - Py_ssize_t i; - char *p; - if (frm->len != to->len) { PyErr_Format(PyExc_ValueError, "maketrans arguments must have same length"); return NULL; } - res = PyBytes_FromStringAndSize(NULL, 256); - if (!res) + PyBytesWriter *writer = PyBytesWriter_Create(256); + if (!writer) { return NULL; - p = PyBytes_AS_STRING(res); + } + char *p = PyBytesWriter_GetData(writer); + Py_ssize_t i; for (i = 0; i < 256; i++) p[i] = (char) i; for (i = 0; i < frm->len; i++) { p[((unsigned char *)frm->buf)[i]] = ((char *)to->buf)[i]; } - return res; + return PyBytesWriter_Finish(writer); } #define FASTSEARCH fastsearch diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 87ea1162e03513..f63185e14284b1 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -3,13 +3,15 @@ #include "Python.h" #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_bytes_methods.h" // _Py_bytes_startswith() -#include "pycore_bytesobject.h" // _PyBytes_Find(), _PyBytes_Repeat() +#include "pycore_bytesobject.h" // _PyBytes_Find(), _PyBytes_RepeatBuffer() #include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_ceval.h" // _PyEval_GetBuiltin() #include "pycore_format.h" // F_LJUST +#include "pycore_freelist.h" // _Py_FREELIST_FREE() #include "pycore_global_objects.h"// _Py_GET_GLOBAL_OBJECT() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_long.h" // _PyLong_DigitValue +#include "pycore_list.h" // _PyList_GetItemRef #include "pycore_object.h" // _PyObject_GC_TRACK #include "pycore_pymem.h" // PYMEM_CLEANBYTE #include "pycore_strhex.h" // _Py_strhex_with_sep() @@ -24,17 +26,12 @@ class bytes "PyBytesObject *" "&PyBytes_Type" #include "clinic/bytesobject.c.h" -/* PyBytesObject_SIZE gives the basic size of a bytes object; any memory allocation - for a bytes object of length n should request PyBytesObject_SIZE + n bytes. - - Using PyBytesObject_SIZE instead of sizeof(PyBytesObject) saves - 3 or 7 bytes per bytes object allocation on a typical system. -*/ -#define PyBytesObject_SIZE (offsetof(PyBytesObject, ob_sval) + 1) +#define PyBytesObject_SIZE _PyBytesObject_SIZE /* Forward declaration */ -Py_LOCAL_INLINE(Py_ssize_t) _PyBytesWriter_GetSize(_PyBytesWriter *writer, - char *str); +static void* _PyBytesWriter_ResizeAndUpdatePointer(PyBytesWriter *writer, + Py_ssize_t size, void *data); +static Py_ssize_t _PyBytesWriter_GetAllocated(PyBytesWriter *writer); #define CHARACTERS _Py_SINGLETON(bytes_characters) @@ -195,10 +192,11 @@ PyBytes_FromString(const char *str) return (PyObject *) op; } -PyObject * -PyBytes_FromFormatV(const char *format, va_list vargs) + +static char* +bytes_fromformat(PyBytesWriter *writer, Py_ssize_t writer_pos, + const char *format, va_list vargs) { - char *s; const char *f; const char *p; Py_ssize_t prec; @@ -212,21 +210,20 @@ PyBytes_FromFormatV(const char *format, va_list vargs) Longest 64-bit pointer representation: "0xffffffffffffffff\0" (19 bytes). */ char buffer[21]; - _PyBytesWriter writer; - _PyBytesWriter_Init(&writer); + char *s = (char*)PyBytesWriter_GetData(writer) + writer_pos; - s = _PyBytesWriter_Alloc(&writer, strlen(format)); - if (s == NULL) - return NULL; - writer.overallocate = 1; - -#define WRITE_BYTES(str) \ +#define WRITE_BYTES_LEN(str, len_expr) \ do { \ - s = _PyBytesWriter_WriteBytes(&writer, s, (str), strlen(str)); \ - if (s == NULL) \ + size_t len = (len_expr); \ + s = PyBytesWriter_GrowAndUpdatePointer(writer, len, s); \ + if (s == NULL) { \ goto error; \ + } \ + memcpy(s, (str), len); \ + s += len; \ } while (0) +#define WRITE_BYTES(str) WRITE_BYTES_LEN(str, strlen(str)) for (f = format; *f; f++) { if (*f != '%') { @@ -267,10 +264,6 @@ PyBytes_FromFormatV(const char *format, va_list vargs) ++f; } - /* subtract bytes preallocated for the format string - (ex: 2 for "%s") */ - writer.min_size -= (f - p + 1); - switch (*f) { case 'c': { @@ -281,7 +274,6 @@ PyBytes_FromFormatV(const char *format, va_list vargs) "expects an integer in range [0; 255]"); goto error; } - writer.min_size++; *s++ = (unsigned char)c; break; } @@ -340,9 +332,7 @@ PyBytes_FromFormatV(const char *format, va_list vargs) i++; } } - s = _PyBytesWriter_WriteBytes(&writer, s, p, i); - if (s == NULL) - goto error; + WRITE_BYTES_LEN(p, i); break; } @@ -361,31 +351,45 @@ PyBytes_FromFormatV(const char *format, va_list vargs) break; case '%': - writer.min_size++; *s++ = '%'; break; default: - if (*f == 0) { - /* fix min_size if we reached the end of the format string */ - writer.min_size++; - } - /* invalid format string: copy unformatted string and exit */ WRITE_BYTES(p); - return _PyBytesWriter_Finish(&writer, s); + return s; } } #undef WRITE_BYTES +#undef WRITE_BYTES_LEN - return _PyBytesWriter_Finish(&writer, s); + return s; error: - _PyBytesWriter_Dealloc(&writer); return NULL; } + +PyObject * +PyBytes_FromFormatV(const char *format, va_list vargs) +{ + Py_ssize_t alloc = strlen(format); + PyBytesWriter *writer = PyBytesWriter_Create(alloc); + if (writer == NULL) { + return NULL; + } + + char *s = bytes_fromformat(writer, 0, format, vargs); + if (s == NULL) { + PyBytesWriter_Discard(writer); + return NULL; + } + + return PyBytesWriter_FinishWithPointer(writer, s); +} + + PyObject * PyBytes_FromFormat(const char *format, ...) { @@ -398,29 +402,48 @@ PyBytes_FromFormat(const char *format, ...) return ret; } + /* Helpers for formatstring */ +#define FORMAT_ERROR(EXC, FMT, ...) do { \ + if (key != NULL) { \ + PyErr_Format((EXC), "format argument %R: " FMT, \ + key, __VA_ARGS__); \ + } \ + else if (argidx >= 0) { \ + PyErr_Format((EXC), "format argument %zd: " FMT, \ + argidx, __VA_ARGS__); \ + } \ + else { \ + PyErr_Format((EXC), "format argument: " FMT, __VA_ARGS__); \ + } \ +} while (0) + Py_LOCAL_INLINE(PyObject *) -getnextarg(PyObject *args, Py_ssize_t arglen, Py_ssize_t *p_argidx) +getnextarg(PyObject *args, Py_ssize_t arglen, Py_ssize_t *p_argidx, int allowone) { Py_ssize_t argidx = *p_argidx; if (argidx < arglen) { (*p_argidx)++; - if (arglen < 0) - return args; - else + if (arglen >= 0) { return PyTuple_GetItem(args, argidx); + } + else if (allowone) { + return args; + } } - PyErr_SetString(PyExc_TypeError, - "not enough arguments for format string"); + PyErr_Format(PyExc_TypeError, + "not enough arguments for format string (got %zd)", + arglen < 0 ? 1 : arglen); return NULL; } /* Returns a new reference to a PyBytes object, or NULL on failure. */ static char* -formatfloat(PyObject *v, int flags, int prec, int type, - PyObject **p_result, _PyBytesWriter *writer, char *str) +formatfloat(PyObject *v, Py_ssize_t argidx, PyObject *key, + int flags, int prec, int type, + PyObject **p_result, PyBytesWriter *writer, char *str) { char *p; PyObject *result; @@ -430,8 +453,11 @@ formatfloat(PyObject *v, int flags, int prec, int type, x = PyFloat_AsDouble(v); if (x == -1.0 && PyErr_Occurred()) { - PyErr_Format(PyExc_TypeError, "float argument required, " - "not %.200s", Py_TYPE(v)->tp_name); + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + FORMAT_ERROR(PyExc_TypeError, + "%%%c requires a real number, not %T", + type, v); + } return NULL; } @@ -448,7 +474,7 @@ formatfloat(PyObject *v, int flags, int prec, int type, len = strlen(p); if (writer != NULL) { - str = _PyBytesWriter_Prepare(writer, str, len); + str = PyBytesWriter_GrowAndUpdatePointer(writer, len, str); if (str == NULL) { PyMem_Free(p); return NULL; @@ -466,7 +492,8 @@ formatfloat(PyObject *v, int flags, int prec, int type, } static PyObject * -formatlong(PyObject *v, int flags, int prec, int type) +formatlong(PyObject *v, Py_ssize_t argidx, PyObject *key, + int flags, int prec, int type) { PyObject *result, *iobj; if (PyLong_Check(v)) @@ -486,20 +513,21 @@ formatlong(PyObject *v, int flags, int prec, int type) if (!PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; } - PyErr_Format(PyExc_TypeError, - "%%%c format: %s is required, not %.200s", type, - (type == 'o' || type == 'x' || type == 'X') ? "an integer" - : "a real number", - Py_TYPE(v)->tp_name); + FORMAT_ERROR(PyExc_TypeError, + "%%%c requires %s, not %T", + type, + (type == 'o' || type == 'x' || type == 'X') ? "an integer" + : "a real number", + v); return NULL; } static int -byte_converter(PyObject *arg, char *p) +byte_converter(PyObject *arg, Py_ssize_t argidx, PyObject *key, char *p) { if (PyBytes_Check(arg)) { if (PyBytes_GET_SIZE(arg) != 1) { - PyErr_Format(PyExc_TypeError, + FORMAT_ERROR(PyExc_TypeError, "%%c requires an integer in range(256) or " "a single byte, not a bytes object of length %zd", PyBytes_GET_SIZE(arg)); @@ -510,7 +538,7 @@ byte_converter(PyObject *arg, char *p) } else if (PyByteArray_Check(arg)) { if (PyByteArray_GET_SIZE(arg) != 1) { - PyErr_Format(PyExc_TypeError, + FORMAT_ERROR(PyExc_TypeError, "%%c requires an integer in range(256) or " "a single byte, not a bytearray object of length %zd", PyByteArray_GET_SIZE(arg)); @@ -527,23 +555,25 @@ byte_converter(PyObject *arg, char *p) } if (!(0 <= ival && ival <= 255)) { /* this includes an overflow in converting to C long */ - PyErr_SetString(PyExc_OverflowError, - "%c arg not in range(256)"); + FORMAT_ERROR(PyExc_OverflowError, + "%%c argument not in range(256)%s", ""); return 0; } *p = (char)ival; return 1; } - PyErr_Format(PyExc_TypeError, - "%%c requires an integer in range(256) or a single byte, not %T", - arg); + FORMAT_ERROR(PyExc_TypeError, + "%%c requires an integer in range(256) or " + "a single byte, not %T", + arg); return 0; } static PyObject *_PyBytes_FromBuffer(PyObject *x); static PyObject * -format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen) +format_obj(PyObject *v, Py_ssize_t argidx, PyObject *key, + const char **pbuf, Py_ssize_t *plen) { PyObject *func, *result; /* is it a bytes object? */ @@ -566,8 +596,8 @@ format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen) return NULL; if (!PyBytes_Check(result)) { PyErr_Format(PyExc_TypeError, - "__bytes__ returned non-bytes (type %.200s)", - Py_TYPE(result)->tp_name); + "%T.__bytes__() must return a bytes, not %T", + v, result); Py_DECREF(result); return NULL; } @@ -585,10 +615,10 @@ format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen) *plen = PyBytes_GET_SIZE(result); return result; } - PyErr_Format(PyExc_TypeError, + FORMAT_ERROR(PyExc_TypeError, "%%b requires a bytes-like object, " - "or an object that implements __bytes__, not '%.100s'", - Py_TYPE(v)->tp_name); + "or an object that implements __bytes__, not %T", + v); return NULL; } @@ -599,12 +629,11 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, PyObject *args, int use_bytearray) { const char *fmt; - char *res; Py_ssize_t arglen, argidx; Py_ssize_t fmtcnt; int args_owned = 0; PyObject *dict = NULL; - _PyBytesWriter writer; + PyObject *key = NULL; if (args == NULL) { PyErr_BadInternalCall(); @@ -613,14 +642,17 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, fmt = format; fmtcnt = format_len; - _PyBytesWriter_Init(&writer); - writer.use_bytearray = use_bytearray; - - res = _PyBytesWriter_Alloc(&writer, fmtcnt); - if (res == NULL) + PyBytesWriter *writer; + if (use_bytearray) { + writer = _PyBytesWriter_CreateByteArray(fmtcnt); + } + else { + writer = PyBytesWriter_Create(fmtcnt); + } + if (writer == NULL) { return NULL; - if (!use_bytearray) - writer.overallocate = 1; + } + char *res = PyBytesWriter_GetData(writer); if (PyTuple_Check(args)) { arglen = PyTuple_GET_SIZE(args); @@ -675,15 +707,17 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, fmtcnt--; continue; } + Py_CLEAR(key); + const char *fmtstart = fmt; if (*fmt == '(') { const char *keystart; Py_ssize_t keylen; - PyObject *key; int pcount = 1; if (dict == NULL) { - PyErr_SetString(PyExc_TypeError, - "format requires a mapping"); + PyErr_Format(PyExc_TypeError, + "format requires a mapping, not %T", + args); goto error; } ++fmt; @@ -699,8 +733,10 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, } keylen = fmt - keystart - 1; if (fmtcnt < 0 || pcount > 0) { - PyErr_SetString(PyExc_ValueError, - "incomplete format key"); + PyErr_Format(PyExc_ValueError, + "stray %% or incomplete format key " + "at position %zd", + (Py_ssize_t)(fmtstart - format - 1)); goto error; } key = PyBytes_FromStringAndSize(keystart, @@ -712,13 +748,21 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, args_owned = 0; } args = PyObject_GetItem(dict, key); - Py_DECREF(key); if (args == NULL) { goto error; } args_owned = 1; - arglen = -1; - argidx = -2; + arglen = -3; + argidx = -4; + } + else { + if (arglen < -1) { + PyErr_Format(PyExc_ValueError, + "format requires a parenthesised mapping key " + "at position %zd", + (Py_ssize_t)(fmtstart - format - 1)); + goto error; + } } /* Parse flags. Example: "%+i" => flags=F_SIGN. */ @@ -735,17 +779,28 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, /* Parse width. Example: "%10s" => width=10 */ if (c == '*') { - v = getnextarg(args, arglen, &argidx); + if (arglen < -1) { + PyErr_Format(PyExc_ValueError, + "* cannot be used with a parenthesised mapping key " + "at position %zd", + (Py_ssize_t)(fmtstart - format - 1)); + goto error; + } + v = getnextarg(args, arglen, &argidx, 0); if (v == NULL) goto error; if (!PyLong_Check(v)) { - PyErr_SetString(PyExc_TypeError, - "* wants int"); + FORMAT_ERROR(PyExc_TypeError, "* requires int, not %T", v); goto error; } width = PyLong_AsSsize_t(v); - if (width == -1 && PyErr_Occurred()) + if (width == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + FORMAT_ERROR(PyExc_OverflowError, + "too big for width%s", ""); + } goto error; + } if (width < 0) { flags |= F_LJUST; width = -width; @@ -760,9 +815,9 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, if (!Py_ISDIGIT(c)) break; if (width > (PY_SSIZE_T_MAX - ((int)c - '0')) / 10) { - PyErr_SetString( - PyExc_ValueError, - "width too big"); + PyErr_Format(PyExc_ValueError, + "width too big at position %zd", + (Py_ssize_t)(fmtstart - format - 1)); goto error; } width = width*10 + (c - '0'); @@ -775,18 +830,29 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, if (--fmtcnt >= 0) c = *fmt++; if (c == '*') { - v = getnextarg(args, arglen, &argidx); + if (arglen < -1) { + PyErr_Format(PyExc_ValueError, + "* cannot be used with a parenthesised mapping key " + "at position %zd", + (Py_ssize_t)(fmtstart - format - 1)); + goto error; + } + v = getnextarg(args, arglen, &argidx, 0); if (v == NULL) goto error; if (!PyLong_Check(v)) { - PyErr_SetString( - PyExc_TypeError, - "* wants int"); + FORMAT_ERROR(PyExc_TypeError, + "* requires int, not %T", v); goto error; } prec = PyLong_AsInt(v); - if (prec == -1 && PyErr_Occurred()) + if (prec == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + FORMAT_ERROR(PyExc_OverflowError, + "too big for precision%s", ""); + } goto error; + } if (prec < 0) prec = 0; if (--fmtcnt >= 0) @@ -799,9 +865,9 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, if (!Py_ISDIGIT(c)) break; if (prec > (INT_MAX - ((int)c - '0')) / 10) { - PyErr_SetString( - PyExc_ValueError, - "prec too big"); + PyErr_Format(PyExc_ValueError, + "precision too big at position %zd", + (Py_ssize_t)(fmtstart - format - 1)); goto error; } prec = prec*10 + (c - '0'); @@ -815,17 +881,18 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, } } if (fmtcnt < 0) { - PyErr_SetString(PyExc_ValueError, - "incomplete format"); + PyErr_Format(PyExc_ValueError, + "stray %% at position %zd", + (Py_ssize_t)(fmtstart - format - 1)); goto error; } - v = getnextarg(args, arglen, &argidx); + v = getnextarg(args, arglen, &argidx, 1); if (v == NULL) goto error; if (fmtcnt == 0) { /* last write: disable writer overallocation */ - writer.overallocate = 0; + writer->overallocate = 0; } sign = 0; @@ -847,7 +914,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, case 's': // %s is only for 2/3 code; 3 only code should use %b case 'b': - temp = format_obj(v, &pbuf, &len); + temp = format_obj(v, argidx, key, &pbuf, &len); if (temp == NULL) goto error; if (prec >= 0 && len > prec) @@ -888,15 +955,14 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, } /* Fast path */ - writer.min_size -= 2; /* size preallocated for "%d" */ - res = _PyLong_FormatBytesWriter(&writer, res, + res = _PyLong_FormatBytesWriter(writer, res, v, base, alternate); if (res == NULL) goto error; continue; } - temp = formatlong(v, flags, prec, c); + temp = formatlong(v, argidx, key, flags, prec, c); if (!temp) goto error; assert(PyUnicode_IS_ASCII(temp)); @@ -917,14 +983,13 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, && !(flags & (F_SIGN | F_BLANK))) { /* Fast path */ - writer.min_size -= 2; /* size preallocated for "%f" */ - res = formatfloat(v, flags, prec, c, NULL, &writer, res); + res = formatfloat(v, argidx, key, flags, prec, c, NULL, writer, res); if (res == NULL) goto error; continue; } - if (!formatfloat(v, flags, prec, c, &temp, NULL, res)) + if (!formatfloat(v, argidx, key, flags, prec, c, &temp, NULL, res)) goto error; pbuf = PyBytes_AS_STRING(temp); len = PyBytes_GET_SIZE(temp); @@ -935,7 +1000,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, case 'c': pbuf = &onechar; - len = byte_converter(v, &onechar); + len = byte_converter(v, argidx, key, &onechar); if (!len) goto error; if (width == -1) { @@ -946,11 +1011,36 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, break; default: - PyErr_Format(PyExc_ValueError, - "unsupported format character '%c' (0x%x) " - "at index %zd", - c, c, - (Py_ssize_t)(fmt - 1 - format)); + if (Py_ISALPHA(c)) { + PyErr_Format(PyExc_ValueError, + "unsupported format %%%c at position %zd", + c, (Py_ssize_t)(fmtstart - format - 1)); + } + else if (c == '\'') { + PyErr_Format(PyExc_ValueError, + "stray %% at position %zd or unexpected " + "format character \"'\" " + "at position %zd", + (Py_ssize_t)(fmtstart - format - 1), + (Py_ssize_t)(fmt - format - 1)); + } + else if (c >= 32 && c < 127 && c != '\'') { + PyErr_Format(PyExc_ValueError, + "stray %% at position %zd or unexpected " + "format character '%c' " + "at position %zd", + (Py_ssize_t)(fmtstart - format - 1), + c, (Py_ssize_t)(fmt - format - 1)); + } + else { + PyErr_Format(PyExc_ValueError, + "stray %% at position %zd or unexpected " + "format character with code 0x%02x " + "at position %zd", + (Py_ssize_t)(fmtstart - format - 1), + Py_CHARMASK(c), + (Py_ssize_t)(fmt - format - 1)); + } goto error; } @@ -974,9 +1064,11 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, alloc++; /* 2: size preallocated for %s */ if (alloc > 2) { - res = _PyBytesWriter_Prepare(&writer, res, alloc - 2); - if (res == NULL) + res = PyBytesWriter_GrowAndUpdatePointer(writer, alloc - 2, res); + if (res == NULL) { + Py_XDECREF(temp); goto error; + } } #ifndef NDEBUG char *before = res; @@ -1037,6 +1129,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, } if (dict && (argidx < arglen)) { + // XXX: Never happens? PyErr_SetString(PyExc_TypeError, "not all arguments converted during bytes formatting"); Py_XDECREF(temp); @@ -1052,22 +1145,27 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, /* If overallocation was disabled, ensure that it was the last write. Otherwise, we missed an optimization */ - assert(writer.overallocate || fmtcnt == 0 || use_bytearray); + assert(writer->overallocate || fmtcnt == 0 || use_bytearray); } /* until end */ if (argidx < arglen && !dict) { - PyErr_SetString(PyExc_TypeError, - "not all arguments converted during bytes formatting"); + PyErr_Format(PyExc_TypeError, + "not all arguments converted during bytes formatting " + "(required %zd, got %zd)", + arglen < 0 ? 0 : argidx, + arglen < 0 ? 1 : arglen); goto error; } + Py_XDECREF(key); if (args_owned) { Py_DECREF(args); } - return _PyBytesWriter_Finish(&writer, res); + return PyBytesWriter_FinishWithPointer(writer, res); error: - _PyBytesWriter_Dealloc(&writer); + Py_XDECREF(key); + PyBytesWriter_Discard(writer); if (args_owned) { Py_DECREF(args); } @@ -1081,22 +1179,16 @@ PyObject *_PyBytes_DecodeEscape2(const char *s, int *first_invalid_escape_char, const char **first_invalid_escape_ptr) { - int c; - char *p; - const char *end; - _PyBytesWriter writer; - - _PyBytesWriter_Init(&writer); - - p = _PyBytesWriter_Alloc(&writer, len); - if (p == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(len); + if (writer == NULL) { return NULL; - writer.overallocate = 1; + } + char *p = PyBytesWriter_GetData(writer); *first_invalid_escape_char = -1; *first_invalid_escape_ptr = NULL; - end = s + len; + const char *end = s + len; while (s < end) { if (*s != '\\') { *p++ = *s++; @@ -1125,7 +1217,8 @@ PyObject *_PyBytes_DecodeEscape2(const char *s, case 'a': *p++ = '\007'; break; /* BEL, not classic C */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': - c = s[-1] - '0'; + { + int c = s[-1] - '0'; if (s < end && '0' <= *s && *s <= '7') { c = (c<<3) + *s++ - '0'; if (s < end && '0' <= *s && *s <= '7') @@ -1140,6 +1233,7 @@ PyObject *_PyBytes_DecodeEscape2(const char *s, } *p++ = c; break; + } case 'x': if (s+1 < end) { int digit1, digit2; @@ -1186,10 +1280,10 @@ PyObject *_PyBytes_DecodeEscape2(const char *s, } } - return _PyBytesWriter_Finish(&writer, p); + return PyBytesWriter_FinishWithPointer(writer, p); failed: - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter_Discard(writer); return NULL; } @@ -1340,27 +1434,33 @@ _PyBytes_ReverseFind(const char *haystack, Py_ssize_t len_haystack, PyObject * PyBytes_Repr(PyObject *obj, int smartquotes) { - PyBytesObject* op = (PyBytesObject*) obj; - Py_ssize_t i, length = Py_SIZE(op); + return _Py_bytes_repr(PyBytes_AS_STRING(obj), PyBytes_GET_SIZE(obj), + smartquotes, "bytes"); +} + +PyObject * +_Py_bytes_repr(const char *data, Py_ssize_t length, int smartquotes, + const char *classname) +{ + Py_ssize_t i; Py_ssize_t newsize, squotes, dquotes; PyObject *v; unsigned char quote; - const unsigned char *s; Py_UCS1 *p; /* Compute size of output string */ squotes = dquotes = 0; newsize = 3; /* b'' */ - s = (const unsigned char*)op->ob_sval; for (i = 0; i < length; i++) { + unsigned char c = data[i]; Py_ssize_t incr = 1; - switch(s[i]) { + switch(c) { case '\'': squotes++; break; case '"': dquotes++; break; case '\\': case '\t': case '\n': case '\r': incr = 2; break; /* \C */ default: - if (s[i] < ' ' || s[i] >= 0x7f) + if (c < ' ' || c >= 0x7f) incr = 4; /* \xHH */ } if (newsize > PY_SSIZE_T_MAX - incr) @@ -1384,7 +1484,7 @@ PyBytes_Repr(PyObject *obj, int smartquotes) *p++ = 'b', *p++ = quote; for (i = 0; i < length; i++) { - unsigned char c = op->ob_sval[i]; + unsigned char c = data[i]; if (c == quote || c == '\\') *p++ = '\\', *p++ = c; else if (c == '\t') @@ -1407,8 +1507,8 @@ PyBytes_Repr(PyObject *obj, int smartquotes) return v; overflow: - PyErr_SetString(PyExc_OverflowError, - "bytes object is too large to make repr"); + PyErr_Format(PyExc_OverflowError, + "%s object is too large to make repr", classname); return NULL; } @@ -1437,9 +1537,9 @@ bytes_length(PyObject *self) return Py_SIZE(a); } -/* This is also used by PyBytes_Concat() */ -static PyObject * -bytes_concat(PyObject *a, PyObject *b) +/* This is also used by PyBytes_Concat() and the specializing interpreter. */ +PyObject * +_PyBytes_Concat(PyObject *a, PyObject *b) { Py_buffer va, vb; PyObject *result = NULL; @@ -1482,8 +1582,8 @@ bytes_concat(PyObject *a, PyObject *b) return result; } -static PyObject * -bytes_repeat(PyObject *self, Py_ssize_t n) +PyObject * +_PyBytes_Repeat(PyObject *self, Py_ssize_t n) { PyBytesObject *a = _PyBytes_CAST(self); if (n < 0) @@ -1514,7 +1614,7 @@ bytes_repeat(PyObject *self, Py_ssize_t n) set_ob_shash(op, -1); op->ob_sval[size] = '\0'; - _PyBytes_Repeat(op->ob_sval, size, a->ob_sval, Py_SIZE(a)); + _PyBytes_RepeatBuffer(op->ob_sval, size, a->ob_sval, Py_SIZE(a)); return (PyObject *) op; } @@ -1705,8 +1805,8 @@ bytes_buffer_getbuffer(PyObject *op, Py_buffer *view, int flags) static PySequenceMethods bytes_as_sequence = { bytes_length, /*sq_length*/ - bytes_concat, /*sq_concat*/ - bytes_repeat, /*sq_repeat*/ + _PyBytes_Concat, /*sq_concat*/ + _PyBytes_Repeat, /*sq_repeat*/ bytes_item, /*sq_item*/ 0, /*sq_slice*/ 0, /*sq_ass_item*/ @@ -1749,12 +1849,13 @@ bytes___bytes___impl(PyBytesObject *self) #define BOTHSTRIP 2 /*[clinic input] +@permit_long_summary bytes.split sep: object = None The delimiter according which to split the bytes. - None (the default value) means split on ASCII whitespace characters - (space, tab, return, newline, formfeed, vertical tab). + None (the default value) means split on ASCII whitespace + characters (space, tab, return, newline, formfeed, vertical tab). maxsplit: Py_ssize_t = -1 Maximum number of splits to do. -1 (the default value) means no limit. @@ -1764,7 +1865,7 @@ Return a list of the sections in the bytes, using sep as the delimiter. static PyObject * bytes_split_impl(PyBytesObject *self, PyObject *sep, Py_ssize_t maxsplit) -/*[clinic end generated code: output=52126b5844c1d8ef input=8b809b39074abbfa]*/ +/*[clinic end generated code: output=52126b5844c1d8ef input=330ff95d92544b05]*/ { Py_ssize_t len = PyBytes_GET_SIZE(self), n; const char *s = PyBytes_AS_STRING(self), *sub; @@ -1793,17 +1894,17 @@ bytes.partition Partition the bytes into three parts using the given separator. -This will search for the separator sep in the bytes. If the separator is found, -returns a 3-tuple containing the part before the separator, the separator -itself, and the part after it. +This will search for the separator sep in the bytes. If the +separator is found, returns a 3-tuple containing the part before the +separator, the separator itself, and the part after it. -If the separator is not found, returns a 3-tuple containing the original bytes -object and two empty bytes objects. +If the separator is not found, returns a 3-tuple containing the +original bytes object and two empty bytes objects. [clinic start generated code]*/ static PyObject * bytes_partition_impl(PyBytesObject *self, Py_buffer *sep) -/*[clinic end generated code: output=f532b392a17ff695 input=61cca95519406099]*/ +/*[clinic end generated code: output=f532b392a17ff695 input=2e6e551ea4f8b95a]*/ { return stringlib_partition( (PyObject*) self, @@ -1820,17 +1921,18 @@ bytes.rpartition Partition the bytes into three parts using the given separator. -This will search for the separator sep in the bytes, starting at the end. If -the separator is found, returns a 3-tuple containing the part before the -separator, the separator itself, and the part after it. +This will search for the separator sep in the bytes, starting at the +end. If the separator is found, returns a 3-tuple containing the +part before the separator, the separator itself, and the part after +it. -If the separator is not found, returns a 3-tuple containing two empty bytes -objects and the original bytes object. +If the separator is not found, returns a 3-tuple containing two +empty bytes objects and the original bytes object. [clinic start generated code]*/ static PyObject * bytes_rpartition_impl(PyBytesObject *self, Py_buffer *sep) -/*[clinic end generated code: output=191b114cbb028e50 input=d78db010c8cfdbe1]*/ +/*[clinic end generated code: output=191b114cbb028e50 input=f7d24f722a5470a4]*/ { return stringlib_rpartition( (PyObject*) self, @@ -1840,16 +1942,18 @@ bytes_rpartition_impl(PyBytesObject *self, Py_buffer *sep) } /*[clinic input] +@permit_long_summary bytes.rsplit = bytes.split Return a list of the sections in the bytes, using sep as the delimiter. -Splitting is done starting at the end of the bytes and working to the front. +Splitting is done starting at the end of the bytes and working to +the front. [clinic start generated code]*/ static PyObject * bytes_rsplit_impl(PyBytesObject *self, PyObject *sep, Py_ssize_t maxsplit) -/*[clinic end generated code: output=ba698d9ea01e1c8f input=0f86c9f28f7d7b7b]*/ +/*[clinic end generated code: output=ba698d9ea01e1c8f input=ba9bee56285f43e4]*/ { Py_ssize_t len = PyBytes_GET_SIZE(self), n; const char *s = PyBytes_AS_STRING(self), *sub; @@ -1910,6 +2014,7 @@ PyBytes_Join(PyObject *sep, PyObject *iterable) } /*[clinic input] +@permit_long_summary @text_signature "($self, sub[, start[, end]], /)" bytes.find @@ -1928,13 +2033,14 @@ Return -1 on failure. static PyObject * bytes_find_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=d5961a1c77b472a1 input=3171e62a8ae7f240]*/ +/*[clinic end generated code: output=d5961a1c77b472a1 input=47d0929adafc6b0b]*/ { return _Py_bytes_find(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), sub, start, end); } /*[clinic input] +@permit_long_summary bytes.index = bytes.find Return the lowest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end]. @@ -1945,13 +2051,14 @@ Raise ValueError if the subsection is not found. static PyObject * bytes_index_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=0da25cc74683ba42 input=aa34ad71ba0bafe3]*/ +/*[clinic end generated code: output=0da25cc74683ba42 input=1cb45ce71456a269]*/ { return _Py_bytes_index(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), sub, start, end); } /*[clinic input] +@permit_long_summary bytes.rfind = bytes.find Return the highest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end]. @@ -1962,13 +2069,14 @@ Return -1 on failure. static PyObject * bytes_rfind_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=51b60fa4ad011c09 input=864c3e7f3010b33c]*/ +/*[clinic end generated code: output=51b60fa4ad011c09 input=c9473d714251f1ab]*/ { return _Py_bytes_rfind(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), sub, start, end); } /*[clinic input] +@permit_long_summary bytes.rindex = bytes.find Return the highest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end]. @@ -1979,7 +2087,7 @@ Raise ValueError if the subsection is not found. static PyObject * bytes_rindex_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=42bf674e0a0aabf6 input=21051fc5cfeacf2c]*/ +/*[clinic end generated code: output=42bf674e0a0aabf6 input=bb5f473c64610c43]*/ { return _Py_bytes_rindex(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), sub, start, end); @@ -2072,12 +2180,13 @@ bytes.strip Strip leading and trailing bytes contained in the argument. -If the argument is omitted or None, strip leading and trailing ASCII whitespace. +If the argument is omitted or None, strip leading and trailing ASCII +whitespace. [clinic start generated code]*/ static PyObject * bytes_strip_impl(PyBytesObject *self, PyObject *bytes) -/*[clinic end generated code: output=c7c228d3bd104a1b input=8a354640e4e0b3ef]*/ +/*[clinic end generated code: output=c7c228d3bd104a1b input=9ffea5f752032bd0]*/ { return do_argstrip(self, BOTHSTRIP, bytes); } @@ -2120,6 +2229,7 @@ bytes_rstrip_impl(PyBytesObject *self, PyObject *bytes) /*[clinic input] +@permit_long_summary bytes.count = bytes.find Return the number of non-overlapping occurrences of subsection 'sub' in bytes B[start:end]. @@ -2128,7 +2238,7 @@ Return the number of non-overlapping occurrences of subsection 'sub' in bytes B[ static PyObject * bytes_count_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=9848140b9be17d0f input=b6e4a5ed515e1e59]*/ +/*[clinic end generated code: output=9848140b9be17d0f input=bb2f136f83f0d30e]*/ { return _Py_bytes_count(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), sub, start, end); @@ -2136,6 +2246,7 @@ bytes_count_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start, /*[clinic input] +@permit_long_summary bytes.translate table: object @@ -2145,14 +2256,15 @@ bytes.translate Return a copy with each character mapped by the given translation table. -All characters occurring in the optional argument delete are removed. -The remaining characters are mapped through the given translation table. +All characters occurring in the optional argument delete are +removed. The remaining characters are mapped through the given +translation table. [clinic start generated code]*/ static PyObject * bytes_translate_impl(PyBytesObject *self, PyObject *table, PyObject *deletechars) -/*[clinic end generated code: output=43be3437f1956211 input=0ecdf159f654233c]*/ +/*[clinic end generated code: output=43be3437f1956211 input=bddcdef0a87895d2]*/ { const char *input; char *output; @@ -2221,11 +2333,15 @@ bytes_translate_impl(PyBytesObject *self, PyObject *table, /* If no deletions are required, use faster code */ for (i = inlen; --i >= 0; ) { c = Py_CHARMASK(*input++); - if (Py_CHARMASK((*output++ = table_chars[c])) != c) - changed = 1; - } - if (!changed && PyBytes_CheckExact(input_obj)) { - Py_SETREF(result, Py_NewRef(input_obj)); + *output++ = table_chars[c]; + } + /* Check if anything changed (for returning original object) */ + /* We save this check until the end so that the compiler will */ + /* unroll the loop above leading to MUCH faster code. */ + if (PyBytes_CheckExact(input_obj)) { + if (memcmp(PyBytes_AS_STRING(input_obj), output_start, inlen) == 0) { + Py_SETREF(result, Py_NewRef(input_obj)); + } } PyBuffer_Release(&del_table_view); PyBuffer_Release(&table_view); @@ -2265,6 +2381,7 @@ bytes_translate_impl(PyBytesObject *self, PyObject *table, /*[clinic input] +@permit_long_summary @staticmethod bytes.maketrans @@ -2274,15 +2391,15 @@ bytes.maketrans Return a translation table usable for the bytes or bytearray translate method. -The returned table will be one where each byte in frm is mapped to the byte at -the same position in to. +The returned table will be one where each byte in frm is mapped to +the byte at the same position in to. The bytes objects frm and to must be of the same length. [clinic start generated code]*/ static PyObject * bytes_maketrans_impl(Py_buffer *frm, Py_buffer *to) -/*[clinic end generated code: output=a36f6399d4b77f6f input=a3bd00d430a0979f]*/ +/*[clinic end generated code: output=a36f6399d4b77f6f input=3a577e5badfea8f7]*/ { return _Py_bytes_maketrans(frm, to); } @@ -2293,21 +2410,21 @@ bytes.replace old: Py_buffer new: Py_buffer + / count: Py_ssize_t = -1 Maximum number of occurrences to replace. -1 (the default value) means replace all occurrences. - / Return a copy with all occurrences of substring old replaced by new. -If the optional argument count is given, only the first count occurrences are -replaced. +If count is given, only the first count occurrences are replaced. +If count is not specified or -1, then all occurrences are replaced. [clinic start generated code]*/ static PyObject * bytes_replace_impl(PyBytesObject *self, Py_buffer *old, Py_buffer *new, Py_ssize_t count) -/*[clinic end generated code: output=994fa588b6b9c104 input=b2fbbf0bf04de8e5]*/ +/*[clinic end generated code: output=994fa588b6b9c104 input=cdf3cf8639297745]*/ { return stringlib_replace((PyObject *)self, (const char *)old->buf, old->len, @@ -2317,6 +2434,7 @@ bytes_replace_impl(PyBytesObject *self, Py_buffer *old, Py_buffer *new, /** End DALKE **/ /*[clinic input] +@permit_long_summary bytes.removeprefix as bytes_removeprefix prefix: Py_buffer @@ -2324,13 +2442,14 @@ bytes.removeprefix as bytes_removeprefix Return a bytes object with the given prefix string removed if present. -If the bytes starts with the prefix string, return bytes[len(prefix):]. -Otherwise, return a copy of the original bytes. +If the bytes starts with the prefix string, return +bytes[len(prefix):]. Otherwise, return a copy of the original +bytes. [clinic start generated code]*/ static PyObject * bytes_removeprefix_impl(PyBytesObject *self, Py_buffer *prefix) -/*[clinic end generated code: output=f006865331a06ab6 input=0c93bac817a8502c]*/ +/*[clinic end generated code: output=f006865331a06ab6 input=3a2672bcee61d7a7]*/ { const char *self_start = PyBytes_AS_STRING(self); Py_ssize_t self_len = PyBytes_GET_SIZE(self); @@ -2353,6 +2472,7 @@ bytes_removeprefix_impl(PyBytesObject *self, Py_buffer *prefix) } /*[clinic input] +@permit_long_summary bytes.removesuffix as bytes_removesuffix suffix: Py_buffer @@ -2360,14 +2480,14 @@ bytes.removesuffix as bytes_removesuffix Return a bytes object with the given suffix string removed if present. -If the bytes ends with the suffix string and that suffix is not empty, -return bytes[:-len(prefix)]. Otherwise, return a copy of the original -bytes. +If the bytes ends with the suffix string and that suffix is not +empty, return bytes[:-len(prefix)]. Otherwise, return a copy of the +original bytes. [clinic start generated code]*/ static PyObject * bytes_removesuffix_impl(PyBytesObject *self, Py_buffer *suffix) -/*[clinic end generated code: output=d887d308e3242eeb input=9f4e1da8c637bbf1]*/ +/*[clinic end generated code: output=d887d308e3242eeb input=04df5f18a36f69d7]*/ { const char *self_start = PyBytes_AS_STRING(self); Py_ssize_t self_len = PyBytes_GET_SIZE(self); @@ -2391,6 +2511,7 @@ bytes_removesuffix_impl(PyBytesObject *self, Py_buffer *suffix) } /*[clinic input] +@permit_long_summary @text_signature "($self, prefix[, start[, end]], /)" bytes.startswith @@ -2408,13 +2529,14 @@ Return True if the bytes starts with the specified prefix, False otherwise. static PyObject * bytes_startswith_impl(PyBytesObject *self, PyObject *subobj, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=b1e8da1cbd528e8c input=8a4165df8adfa6c9]*/ +/*[clinic end generated code: output=b1e8da1cbd528e8c input=a14efd070f15be80]*/ { return _Py_bytes_startswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), subobj, start, end); } /*[clinic input] +@permit_long_summary @text_signature "($self, suffix[, start[, end]], /)" bytes.endswith @@ -2432,7 +2554,7 @@ Return True if the bytes ends with the specified suffix, False otherwise. static PyObject * bytes_endswith_impl(PyBytesObject *self, PyObject *subobj, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=038b633111f3629d input=b5c3407a2a5c9aac]*/ +/*[clinic end generated code: output=038b633111f3629d input=49e383eaaf292713]*/ { return _Py_bytes_endswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), subobj, start, end); @@ -2445,11 +2567,11 @@ bytes.decode encoding: str(c_default="NULL") = 'utf-8' The encoding with which to decode the bytes. errors: str(c_default="NULL") = 'strict' - The error handling scheme to use for the handling of decoding errors. - The default is 'strict' meaning that decoding errors raise a - UnicodeDecodeError. Other possible values are 'ignore' and 'replace' - as well as any other name registered with codecs.register_error that - can handle UnicodeDecodeErrors. + The error handling scheme to use for the handling of decoding + errors. The default is 'strict' meaning that decoding errors + raise a UnicodeDecodeError. Other possible values are 'ignore' + and 'replace' as well as any other name registered with + codecs.register_error that can handle UnicodeDecodeErrors. Decode the bytes using the codec registered for encoding. [clinic start generated code]*/ @@ -2457,26 +2579,27 @@ Decode the bytes using the codec registered for encoding. static PyObject * bytes_decode_impl(PyBytesObject *self, const char *encoding, const char *errors) -/*[clinic end generated code: output=5649a53dde27b314 input=958174769d2a40ca]*/ +/*[clinic end generated code: output=5649a53dde27b314 input=94e9b8524f1d7f37]*/ { return PyUnicode_FromEncodedObject((PyObject*)self, encoding, errors); } /*[clinic input] +@permit_long_summary bytes.splitlines keepends: bool = False Return a list of the lines in the bytes, breaking at line boundaries. -Line breaks are not included in the resulting list unless keepends is given and -true. +Line breaks are not included in the resulting list unless keepends +is given and true. [clinic start generated code]*/ static PyObject * bytes_splitlines_impl(PyBytesObject *self, int keepends) -/*[clinic end generated code: output=3484149a5d880ffb input=5d7b898af2fe55c0]*/ +/*[clinic end generated code: output=3484149a5d880ffb input=8734672f34430514]*/ { return stringlib_splitlines( (PyObject*) self, PyBytes_AS_STRING(self), @@ -2511,17 +2634,13 @@ bytes_fromhex_impl(PyTypeObject *type, PyObject *string) PyObject* _PyBytes_FromHex(PyObject *string, int use_bytearray) { - char *buf; Py_ssize_t hexlen, invalid_char; unsigned int top, bot; const Py_UCS1 *str, *start, *end; - _PyBytesWriter writer; + PyBytesWriter *writer = NULL; Py_buffer view; view.obj = NULL; - _PyBytesWriter_Init(&writer); - writer.use_bytearray = use_bytearray; - if (PyUnicode_Check(string)) { hexlen = PyUnicode_GET_LENGTH(string); @@ -2557,10 +2676,16 @@ _PyBytes_FromHex(PyObject *string, int use_bytearray) } /* This overestimates if there are spaces */ - buf = _PyBytesWriter_Alloc(&writer, hexlen / 2); - if (buf == NULL) { + if (use_bytearray) { + writer = _PyBytesWriter_CreateByteArray(hexlen / 2); + } + else { + writer = PyBytesWriter_Create(hexlen / 2); + } + if (writer == NULL) { goto release_buffer; } + char *buf = PyBytesWriter_GetData(writer); start = str; end = str + hexlen; @@ -2599,7 +2724,7 @@ _PyBytes_FromHex(PyObject *string, int use_bytearray) if (view.obj != NULL) { PyBuffer_Release(&view); } - return _PyBytesWriter_Finish(&writer, buf); + return PyBytesWriter_FinishWithPointer(writer, buf); error: if (invalid_char == -1) { @@ -2610,7 +2735,7 @@ _PyBytes_FromHex(PyObject *string, int use_bytearray) "non-hexadecimal number found in " "fromhex() arg at position %zd", invalid_char); } - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter_Discard(writer); release_buffer: if (view.obj != NULL) { @@ -2624,9 +2749,9 @@ bytes.hex sep: object = NULL An optional single character or byte to separate hex bytes. - bytes_per_sep: int = 1 - How many bytes between separators. Positive values count from the - right, negative values count from the left. + bytes_per_sep: Py_ssize_t = 1 + How many bytes between separators. Positive values count from + the right, negative values count from the left. Create a string of hexadecimal numbers from a bytes object. @@ -2643,8 +2768,8 @@ Create a string of hexadecimal numbers from a bytes object. [clinic start generated code]*/ static PyObject * -bytes_hex_impl(PyBytesObject *self, PyObject *sep, int bytes_per_sep) -/*[clinic end generated code: output=1f134da504064139 input=1a21282b1f1ae595]*/ +bytes_hex_impl(PyBytesObject *self, PyObject *sep, Py_ssize_t bytes_per_sep) +/*[clinic end generated code: output=588821f02cb9d8f5 input=b8d40cf203d172dc]*/ { const char *argbuf = PyBytes_AS_STRING(self); Py_ssize_t arglen = PyBytes_GET_SIZE(self); @@ -2765,7 +2890,7 @@ bytes_new_impl(PyTypeObject *type, PyObject *x, const char *encoding, "errors without a string argument"); return NULL; } - bytes = PyBytes_FromStringAndSize(NULL, 0); + bytes = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } else if (encoding != NULL) { /* Encode via the codec registry */ @@ -2793,8 +2918,8 @@ bytes_new_impl(PyTypeObject *type, PyObject *x, const char *encoding, return NULL; if (!PyBytes_Check(bytes)) { PyErr_Format(PyExc_TypeError, - "__bytes__ returned non-bytes (type %.200s)", - Py_TYPE(bytes)->tp_name); + "%T.__bytes__() must return a bytes, not %T", + x, bytes); Py_DECREF(bytes); return NULL; } @@ -2837,23 +2962,25 @@ bytes_new_impl(PyTypeObject *type, PyObject *x, const char *encoding, static PyObject* _PyBytes_FromBuffer(PyObject *x) { - PyObject *new; Py_buffer view; - if (PyObject_GetBuffer(x, &view, PyBUF_FULL_RO) < 0) return NULL; - new = PyBytes_FromStringAndSize(NULL, view.len); - if (!new) + PyBytesWriter *writer = PyBytesWriter_Create(view.len); + if (writer == NULL) { goto fail; - if (PyBuffer_ToContiguous(((PyBytesObject *)new)->ob_sval, - &view, view.len, 'C') < 0) + } + + if (PyBuffer_ToContiguous(PyBytesWriter_GetData(writer), + &view, view.len, 'C') < 0) { goto fail; + } + PyBuffer_Release(&view); - return new; + return PyBytesWriter_Finish(writer); fail: - Py_XDECREF(new); + PyBytesWriter_Discard(writer); PyBuffer_Release(&view); return NULL; } @@ -2861,23 +2988,20 @@ _PyBytes_FromBuffer(PyObject *x) static PyObject* _PyBytes_FromList(PyObject *x) { - Py_ssize_t i, size = PyList_GET_SIZE(x); - Py_ssize_t value; - char *str; - PyObject *item; - _PyBytesWriter writer; - - _PyBytesWriter_Init(&writer); - str = _PyBytesWriter_Alloc(&writer, size); - if (str == NULL) + Py_ssize_t size = PyList_GET_SIZE(x); + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; - writer.overallocate = 1; - size = writer.allocated; + } + char *str = PyBytesWriter_GetData(writer); + size = _PyBytesWriter_GetAllocated(writer); - for (i = 0; i < PyList_GET_SIZE(x); i++) { - item = PyList_GET_ITEM(x, i); - Py_INCREF(item); - value = PyNumber_AsSsize_t(item, NULL); + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(x); i++) { + PyObject *item = _PyList_GetItemRef((PyListObject *)x, i); + if (item == NULL) { + goto error; + } + Py_ssize_t value = PyNumber_AsSsize_t(item, NULL); Py_DECREF(item); if (value == -1 && PyErr_Occurred()) goto error; @@ -2889,33 +3013,33 @@ _PyBytes_FromList(PyObject *x) } if (i >= size) { - str = _PyBytesWriter_Resize(&writer, str, size+1); - if (str == NULL) - return NULL; - size = writer.allocated; + str = _PyBytesWriter_ResizeAndUpdatePointer(writer, size + 1, str); + if (str == NULL) { + goto error; + } + size = _PyBytesWriter_GetAllocated(writer); } *str++ = (char) value; } - return _PyBytesWriter_Finish(&writer, str); + return PyBytesWriter_FinishWithPointer(writer, str); - error: - _PyBytesWriter_Dealloc(&writer); +error: + PyBytesWriter_Discard(writer); return NULL; } static PyObject* _PyBytes_FromTuple(PyObject *x) { - PyObject *bytes; Py_ssize_t i, size = PyTuple_GET_SIZE(x); Py_ssize_t value; - char *str; PyObject *item; - bytes = PyBytes_FromStringAndSize(NULL, size); - if (bytes == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; - str = ((PyBytesObject *)bytes)->ob_sval; + } + char *str = PyBytesWriter_GetData(writer); for (i = 0; i < size; i++) { item = PyTuple_GET_ITEM(x, i); @@ -2930,31 +3054,29 @@ _PyBytes_FromTuple(PyObject *x) } *str++ = (char) value; } - return bytes; + return PyBytesWriter_Finish(writer); error: - Py_DECREF(bytes); + PyBytesWriter_Discard(writer); return NULL; } static PyObject * _PyBytes_FromIterator(PyObject *it, PyObject *x) { - char *str; Py_ssize_t i, size; - _PyBytesWriter writer; /* For iterator version, create a bytes object and resize as needed */ size = PyObject_LengthHint(x, 64); if (size == -1 && PyErr_Occurred()) return NULL; - _PyBytesWriter_Init(&writer); - str = _PyBytesWriter_Alloc(&writer, size); - if (str == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; - writer.overallocate = 1; - size = writer.allocated; + } + char *str = PyBytesWriter_GetData(writer); + size = _PyBytesWriter_GetAllocated(writer); /* Run the iterator to exhaustion */ for (i = 0; ; i++) { @@ -2984,18 +3106,18 @@ _PyBytes_FromIterator(PyObject *it, PyObject *x) /* Append the byte */ if (i >= size) { - str = _PyBytesWriter_Resize(&writer, str, size+1); - if (str == NULL) - return NULL; - size = writer.allocated; + str = _PyBytesWriter_ResizeAndUpdatePointer(writer, size + 1, str); + if (str == NULL) { + goto error; + } + size = _PyBytesWriter_GetAllocated(writer); } *str++ = (char) value; } - - return _PyBytesWriter_Finish(&writer, str); + return PyBytesWriter_FinishWithPointer(writer, str); error: - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter_Discard(writer); return NULL; } @@ -3091,6 +3213,18 @@ Construct an immutable array of bytes from:\n\ static PyObject *bytes_iter(PyObject *seq); + +static _PyObjectIndexPair +bytes_iteritem(PyObject *obj, Py_ssize_t index) +{ + PyBytesObject *a = _PyBytes_CAST(obj); + if (index >= Py_SIZE(a)) { + return (_PyObjectIndexPair) { .object = NULL, .index = index }; + } + PyObject *l = _PyLong_FromUnsignedChar((unsigned char)a->ob_sval[index]); + return (_PyObjectIndexPair) { .object = l, .index = index + 1 }; +} + PyTypeObject PyBytes_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "bytes", @@ -3134,6 +3268,7 @@ PyTypeObject PyBytes_Type = { bytes_new, /* tp_new */ PyObject_Free, /* tp_free */ .tp_version_tag = _Py_TYPE_VERSION_BYTES, + ._tp_iteritem = bytes_iteritem, }; void @@ -3147,7 +3282,7 @@ PyBytes_Concat(PyObject **pv, PyObject *w) return; } - if (Py_REFCNT(*pv) == 1 && PyBytes_CheckExact(*pv)) { + if (_PyObject_IsUniquelyReferenced(*pv) && PyBytes_CheckExact(*pv)) { /* Only one reference, so we can resize in place */ Py_ssize_t oldsize; Py_buffer wb; @@ -3180,7 +3315,7 @@ PyBytes_Concat(PyObject **pv, PyObject *w) else { /* Multiple references, need to create new object */ PyObject *v; - v = bytes_concat(*pv, w); + v = _PyBytes_Concat(*pv, w); Py_SETREF(*pv, v); } } @@ -3232,7 +3367,7 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize) Py_DECREF(v); return 0; } - if (Py_REFCNT(v) != 1) { + if (!_PyObject_IsUniquelyReferenced(v)) { if (oldsize < newsize) { *pv = _PyBytes_FromSize(newsize, 0); if (*pv) { @@ -3427,7 +3562,53 @@ bytes_iter(PyObject *seq) } -/* _PyBytesWriter API */ +void +_PyBytes_RepeatBuffer(char* dest, Py_ssize_t len_dest, + const char* src, Py_ssize_t len_src) +{ + if (len_dest == 0) { + return; + } + if (len_src == 1) { + memset(dest, src[0], len_dest); + } + else { + if (src != dest) { + memcpy(dest, src, len_src); + } + Py_ssize_t copied = len_src; + while (copied < len_dest) { + Py_ssize_t bytes_to_copy = Py_MIN(copied, len_dest - copied); + memcpy(dest + copied, dest, bytes_to_copy); + copied += bytes_to_copy; + } + } +} + + +// --- PyBytesWriter API ----------------------------------------------------- + +static inline char* +byteswriter_data(PyBytesWriter *writer) +{ + return _PyBytesWriter_GetData(writer); +} + + +static inline Py_ssize_t +byteswriter_allocated(PyBytesWriter *writer) +{ + if (writer->obj == NULL) { + return sizeof(writer->small_buffer); + } + else if (writer->use_bytearray) { + return PyByteArray_GET_SIZE(writer->obj); + } + else { + return PyBytes_GET_SIZE(writer->obj); + } +} + #ifdef MS_WINDOWS /* On Windows, overallocate by 50% is the best factor */ @@ -3437,298 +3618,308 @@ bytes_iter(PyObject *seq) # define OVERALLOCATE_FACTOR 4 #endif -void -_PyBytesWriter_Init(_PyBytesWriter *writer) +static inline int +byteswriter_resize(PyBytesWriter *writer, Py_ssize_t size, int resize) { - /* Set all attributes before small_buffer to 0 */ - memset(writer, 0, offsetof(_PyBytesWriter, small_buffer)); -#ifndef NDEBUG - memset(writer->small_buffer, PYMEM_CLEANBYTE, - sizeof(writer->small_buffer)); -#endif -} + assert(size >= 0); -void -_PyBytesWriter_Dealloc(_PyBytesWriter *writer) -{ - Py_CLEAR(writer->buffer); -} + Py_ssize_t old_allocated = byteswriter_allocated(writer); + if (size <= old_allocated) { + return 0; + } -Py_LOCAL_INLINE(char*) -_PyBytesWriter_AsString(_PyBytesWriter *writer) -{ - if (writer->use_small_buffer) { - assert(writer->buffer == NULL); - return writer->small_buffer; + if (resize & writer->overallocate) { + if (size <= (PY_SSIZE_T_MAX - size / OVERALLOCATE_FACTOR)) { + size += size / OVERALLOCATE_FACTOR; + } + } + + if (writer->obj != NULL) { + if (writer->use_bytearray) { + if (PyByteArray_Resize(writer->obj, size)) { + return -1; + } + } + else { + if (_PyBytes_Resize(&writer->obj, size)) { + return -1; + } + } + assert(writer->obj != NULL); } else if (writer->use_bytearray) { - assert(writer->buffer != NULL); - return PyByteArray_AS_STRING(writer->buffer); + writer->obj = PyByteArray_FromStringAndSize(NULL, size); + if (writer->obj == NULL) { + return -1; + } + if (resize) { + assert((size_t)size > sizeof(writer->small_buffer)); + memcpy(PyByteArray_AS_STRING(writer->obj), + writer->small_buffer, + sizeof(writer->small_buffer)); + } } else { - assert(writer->buffer != NULL); - return PyBytes_AS_STRING(writer->buffer); + writer->obj = PyBytes_FromStringAndSize(NULL, size); + if (writer->obj == NULL) { + return -1; + } + if (resize) { + assert((size_t)size > sizeof(writer->small_buffer)); + memcpy(PyBytes_AS_STRING(writer->obj), + writer->small_buffer, + sizeof(writer->small_buffer)); + } } -} -Py_LOCAL_INLINE(Py_ssize_t) -_PyBytesWriter_GetSize(_PyBytesWriter *writer, char *str) -{ - const char *start = _PyBytesWriter_AsString(writer); - assert(str != NULL); - assert(str >= start); - assert(str - start <= writer->allocated); - return str - start; +#ifdef Py_DEBUG + Py_ssize_t allocated = byteswriter_allocated(writer); + if (resize && allocated > old_allocated) { + memset(byteswriter_data(writer) + old_allocated, 0xff, + allocated - old_allocated); + } +#endif + + return 0; } -#ifndef NDEBUG -Py_LOCAL_INLINE(int) -_PyBytesWriter_CheckConsistency(_PyBytesWriter *writer, char *str) -{ - const char *start, *end; - if (writer->use_small_buffer) { - assert(writer->buffer == NULL); - } - else { - assert(writer->buffer != NULL); - if (writer->use_bytearray) - assert(PyByteArray_CheckExact(writer->buffer)); - else - assert(PyBytes_CheckExact(writer->buffer)); - assert(Py_REFCNT(writer->buffer) == 1); +static PyBytesWriter* +byteswriter_create(Py_ssize_t size, int use_bytearray) +{ + if (size < 0) { + PyErr_SetString(PyExc_ValueError, "size must be >= 0"); + return NULL; } - if (writer->use_bytearray) { - /* bytearray has its own overallocation algorithm, - writer overallocation must be disabled */ - assert(!writer->overallocate); + PyBytesWriter *writer = _Py_FREELIST_POP_MEM(bytes_writers); + if (writer == NULL) { + writer = (PyBytesWriter *)PyMem_Malloc(sizeof(PyBytesWriter)); + if (writer == NULL) { + PyErr_NoMemory(); + return NULL; + } } + writer->obj = NULL; + writer->size = 0; + writer->use_bytearray = use_bytearray; + writer->overallocate = !use_bytearray; - assert(0 <= writer->allocated); - assert(0 <= writer->min_size && writer->min_size <= writer->allocated); - /* the last byte must always be null */ - start = _PyBytesWriter_AsString(writer); - assert(start[writer->allocated] == 0); + if (size >= 1) { + if (byteswriter_resize(writer, size, 0) < 0) { + PyBytesWriter_Discard(writer); + return NULL; + } + writer->size = size; + } +#ifdef Py_DEBUG + memset(byteswriter_data(writer), 0xff, byteswriter_allocated(writer)); +#endif + return writer; +} - end = start + writer->allocated; - assert(str != NULL); - assert(start <= str && str <= end); - return 1; +PyBytesWriter* +PyBytesWriter_Create(Py_ssize_t size) +{ + return byteswriter_create(size, 0); } -#endif -void* -_PyBytesWriter_Resize(_PyBytesWriter *writer, void *str, Py_ssize_t size) +PyBytesWriter* +_PyBytesWriter_CreateByteArray(Py_ssize_t size) { - Py_ssize_t allocated, pos; + return byteswriter_create(size, 1); +} - assert(_PyBytesWriter_CheckConsistency(writer, str)); - assert(writer->allocated < size); - allocated = size; - if (writer->overallocate - && allocated <= (PY_SSIZE_T_MAX - allocated / OVERALLOCATE_FACTOR)) { - /* overallocate to limit the number of realloc() */ - allocated += allocated / OVERALLOCATE_FACTOR; +void +PyBytesWriter_Discard(PyBytesWriter *writer) +{ + if (writer == NULL) { + return; } - pos = _PyBytesWriter_GetSize(writer, str); - if (!writer->use_small_buffer) { + Py_XDECREF(writer->obj); + _Py_FREELIST_FREE(bytes_writers, writer, PyMem_Free); +} + + +PyObject* +PyBytesWriter_FinishWithSize(PyBytesWriter *writer, Py_ssize_t size) +{ + PyObject *result; + if (size == 0) { + result = bytes_get_empty(); + } + else if (writer->obj != NULL) { if (writer->use_bytearray) { - if (PyByteArray_Resize(writer->buffer, allocated)) - goto error; - /* writer->allocated can be smaller than writer->buffer->ob_alloc, - but we cannot use ob_alloc because bytes may need to be moved - to use the whole buffer. bytearray uses an internal optimization - to avoid moving or copying bytes when bytes are removed at the - beginning (ex: del bytearray[:1]). */ + if (size != PyByteArray_GET_SIZE(writer->obj)) { + if (PyByteArray_Resize(writer->obj, size)) { + goto error; + } + } } else { - if (_PyBytes_Resize(&writer->buffer, allocated)) - goto error; + if (size != PyBytes_GET_SIZE(writer->obj)) { + if (_PyBytes_Resize(&writer->obj, size)) { + goto error; + } + } } + result = writer->obj; + writer->obj = NULL; + } + else if (writer->use_bytearray) { + result = PyByteArray_FromStringAndSize(writer->small_buffer, size); } else { - /* convert from stack buffer to bytes object buffer */ - assert(writer->buffer == NULL); - - if (writer->use_bytearray) - writer->buffer = PyByteArray_FromStringAndSize(NULL, allocated); - else - writer->buffer = PyBytes_FromStringAndSize(NULL, allocated); - if (writer->buffer == NULL) - goto error; - - if (pos != 0) { - char *dest; - if (writer->use_bytearray) - dest = PyByteArray_AS_STRING(writer->buffer); - else - dest = PyBytes_AS_STRING(writer->buffer); - memcpy(dest, - writer->small_buffer, - pos); - } - - writer->use_small_buffer = 0; -#ifndef NDEBUG - memset(writer->small_buffer, PYMEM_CLEANBYTE, - sizeof(writer->small_buffer)); -#endif + result = PyBytes_FromStringAndSize(writer->small_buffer, size); } - writer->allocated = allocated; - - str = _PyBytesWriter_AsString(writer) + pos; - assert(_PyBytesWriter_CheckConsistency(writer, str)); - return str; + PyBytesWriter_Discard(writer); + return result; error: - _PyBytesWriter_Dealloc(writer); + PyBytesWriter_Discard(writer); return NULL; } -void* -_PyBytesWriter_Prepare(_PyBytesWriter *writer, void *str, Py_ssize_t size) +PyObject* +PyBytesWriter_Finish(PyBytesWriter *writer) { - Py_ssize_t new_min_size; - - assert(_PyBytesWriter_CheckConsistency(writer, str)); - assert(size >= 0); + return PyBytesWriter_FinishWithSize(writer, writer->size); +} - if (size == 0) { - /* nothing to do */ - return str; - } - if (writer->min_size > PY_SSIZE_T_MAX - size) { - PyErr_NoMemory(); - _PyBytesWriter_Dealloc(writer); +PyObject* +PyBytesWriter_FinishWithPointer(PyBytesWriter *writer, void *buf) +{ + Py_ssize_t size = (char*)buf - byteswriter_data(writer); + if (size < 0 || size > byteswriter_allocated(writer)) { + PyBytesWriter_Discard(writer); + PyErr_SetString(PyExc_ValueError, "invalid end pointer"); return NULL; } - new_min_size = writer->min_size + size; - - if (new_min_size > writer->allocated) - str = _PyBytesWriter_Resize(writer, str, new_min_size); - writer->min_size = new_min_size; - return str; + return PyBytesWriter_FinishWithSize(writer, size); } -/* Allocate the buffer to write size bytes. - Return the pointer to the beginning of buffer data. - Raise an exception and return NULL on error. */ + void* -_PyBytesWriter_Alloc(_PyBytesWriter *writer, Py_ssize_t size) +PyBytesWriter_GetData(PyBytesWriter *writer) { - /* ensure that _PyBytesWriter_Alloc() is only called once */ - assert(writer->min_size == 0 && writer->buffer == NULL); - assert(size >= 0); + return byteswriter_data(writer); +} - writer->use_small_buffer = 1; -#ifndef NDEBUG - writer->allocated = sizeof(writer->small_buffer) - 1; - /* In debug mode, don't use the full small buffer because it is less - efficient than bytes and bytearray objects to detect buffer underflow - and buffer overflow. Use 10 bytes of the small buffer to test also - code using the smaller buffer in debug mode. - - Don't modify the _PyBytesWriter structure (use a shorter small buffer) - in debug mode to also be able to detect stack overflow when running - tests in debug mode. The _PyBytesWriter is large (more than 512 bytes), - if _Py_EnterRecursiveCall() is not used in deep C callback, we may hit a - stack overflow. */ - writer->allocated = Py_MIN(writer->allocated, 10); - /* _PyBytesWriter_CheckConsistency() requires the last byte to be 0, - to detect buffer overflow */ - writer->small_buffer[writer->allocated] = 0; -#else - writer->allocated = sizeof(writer->small_buffer); -#endif - return _PyBytesWriter_Prepare(writer, writer->small_buffer, size); + +Py_ssize_t +PyBytesWriter_GetSize(PyBytesWriter *writer) +{ + return _PyBytesWriter_GetSize(writer); } -PyObject * -_PyBytesWriter_Finish(_PyBytesWriter *writer, void *str) + +static Py_ssize_t +_PyBytesWriter_GetAllocated(PyBytesWriter *writer) { - Py_ssize_t size; - PyObject *result; + return byteswriter_allocated(writer); +} - assert(_PyBytesWriter_CheckConsistency(writer, str)); - size = _PyBytesWriter_GetSize(writer, str); - if (size == 0 && !writer->use_bytearray) { - Py_CLEAR(writer->buffer); - /* Get the empty byte string singleton */ - result = PyBytes_FromStringAndSize(NULL, 0); +int +PyBytesWriter_Resize(PyBytesWriter *writer, Py_ssize_t size) +{ + if (size < 0) { + PyErr_SetString(PyExc_ValueError, "size must be >= 0"); + return -1; } - else if (writer->use_small_buffer) { - if (writer->use_bytearray) { - result = PyByteArray_FromStringAndSize(writer->small_buffer, size); - } - else { - result = PyBytes_FromStringAndSize(writer->small_buffer, size); - } + if (byteswriter_resize(writer, size, 1) < 0) { + return -1; } - else { - result = writer->buffer; - writer->buffer = NULL; + writer->size = size; + return 0; +} - if (size != writer->allocated) { - if (writer->use_bytearray) { - if (PyByteArray_Resize(result, size)) { - Py_DECREF(result); - return NULL; - } - } - else { - if (_PyBytes_Resize(&result, size)) { - assert(result == NULL); - return NULL; - } - } - } + +static void* +_PyBytesWriter_ResizeAndUpdatePointer(PyBytesWriter *writer, Py_ssize_t size, + void *data) +{ + Py_ssize_t pos = (char*)data - byteswriter_data(writer); + if (PyBytesWriter_Resize(writer, size) < 0) { + return NULL; } - return result; + return byteswriter_data(writer) + pos; } -void* -_PyBytesWriter_WriteBytes(_PyBytesWriter *writer, void *ptr, - const void *bytes, Py_ssize_t size) + +int +PyBytesWriter_Grow(PyBytesWriter *writer, Py_ssize_t size) { - char *str = (char *)ptr; + if (size < 0 && writer->size + size < 0) { + PyErr_SetString(PyExc_ValueError, "invalid size"); + return -1; + } + if (size > PY_SSIZE_T_MAX - writer->size) { + PyErr_NoMemory(); + return -1; + } + size = writer->size + size; - str = _PyBytesWriter_Prepare(writer, str, size); - if (str == NULL) - return NULL; + if (byteswriter_resize(writer, size, 1) < 0) { + return -1; + } + writer->size = size; + return 0; +} - memcpy(str, bytes, size); - str += size; - return str; +void* +PyBytesWriter_GrowAndUpdatePointer(PyBytesWriter *writer, Py_ssize_t size, + void *buf) +{ + Py_ssize_t pos = (char*)buf - byteswriter_data(writer); + if (PyBytesWriter_Grow(writer, size) < 0) { + return NULL; + } + return byteswriter_data(writer) + pos; } -void -_PyBytes_Repeat(char* dest, Py_ssize_t len_dest, - const char* src, Py_ssize_t len_src) +int +PyBytesWriter_WriteBytes(PyBytesWriter *writer, + const void *bytes, Py_ssize_t size) { - if (len_dest == 0) { - return; - } - if (len_src == 1) { - memset(dest, src[0], len_dest); - } - else { - if (src != dest) { - memcpy(dest, src, len_src); - } - Py_ssize_t copied = len_src; - while (copied < len_dest) { - Py_ssize_t bytes_to_copy = Py_MIN(copied, len_dest - copied); - memcpy(dest + copied, dest, bytes_to_copy); - copied += bytes_to_copy; + if (size < 0) { + size_t len = strlen(bytes); + if (len > (size_t)PY_SSIZE_T_MAX) { + PyErr_NoMemory(); + return -1; } + size = (Py_ssize_t)len; } + + Py_ssize_t pos = writer->size; + if (PyBytesWriter_Grow(writer, size) < 0) { + return -1; + } + char *buf = byteswriter_data(writer); + memcpy(buf + pos, bytes, size); + return 0; } + +int +PyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...) +{ + Py_ssize_t pos = writer->size; + if (PyBytesWriter_Grow(writer, strlen(format)) < 0) { + return -1; + } + + va_list vargs; + va_start(vargs, format); + char *buf = bytes_fromformat(writer, pos, format, vargs); + va_end(vargs); + + Py_ssize_t size = buf - byteswriter_data(writer); + return PyBytesWriter_Resize(writer, size); +} diff --git a/Objects/call.c b/Objects/call.c index c9a18bcc3da60b..9718642473103c 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -213,7 +213,7 @@ _PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable, return NULL; } - PyObject *argstuple = _PyTuple_FromArray(args, nargs); + PyObject *argstuple = PyTuple_FromArray(args, nargs); if (argstuple == NULL) { return NULL; } @@ -708,7 +708,10 @@ _PyObject_CallMethodId(PyObject *obj, _Py_Identifier *name, return null_error(tstate); } +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_DEPR_DECLS PyObject *callable = _PyObject_GetAttrId(obj, name); +_Py_COMP_DIAG_POP if (callable == NULL) { return NULL; } @@ -726,6 +729,7 @@ _PyObject_CallMethodId(PyObject *obj, _Py_Identifier *name, PyObject * _PyObject_CallMethodFormat(PyThreadState *tstate, PyObject *callable, const char *format, ...) { + assert(callable != NULL); va_list va; va_start(va, format); PyObject *retval = callmethod(tstate, callable, format, va); @@ -824,6 +828,60 @@ object_vacall(PyThreadState *tstate, PyObject *base, return result; } +PyObject * +_PyObject_VectorcallPrepend(PyThreadState *tstate, PyObject *callable, + PyObject *arg, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + assert(nargs == 0 || args[nargs-1]); + + PyObject *result; + if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) { + /* PY_VECTORCALL_ARGUMENTS_OFFSET is set, so we are allowed to mutate the vector */ + PyObject **newargs = (PyObject**)args - 1; + nargs += 1; + PyObject *tmp = newargs[0]; + newargs[0] = arg; + assert(newargs[nargs-1]); + result = _PyObject_VectorcallTstate(tstate, callable, newargs, + nargs, kwnames); + newargs[0] = tmp; + } + else { + Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); + Py_ssize_t totalargs = nargs + nkwargs; + if (totalargs == 0) { + return _PyObject_VectorcallTstate(tstate, callable, &arg, 1, NULL); + } + + PyObject *newargs_stack[_PY_FASTCALL_SMALL_STACK]; + PyObject **newargs; + if (totalargs <= (Py_ssize_t)Py_ARRAY_LENGTH(newargs_stack) - 1) { + newargs = newargs_stack; + } + else { + newargs = PyMem_Malloc((totalargs+1) * sizeof(PyObject *)); + if (newargs == NULL) { + _PyErr_NoMemory(tstate); + return NULL; + } + } + /* use borrowed references */ + newargs[0] = arg; + /* bpo-37138: since totalargs > 0, it's impossible that args is NULL. + * We need this, since calling memcpy() with a NULL pointer is + * undefined behaviour. */ + assert(args != NULL); + memcpy(newargs + 1, args, totalargs * sizeof(PyObject *)); + result = _PyObject_VectorcallTstate(tstate, callable, + newargs, nargs+1, kwnames); + if (newargs != newargs_stack) { + PyMem_Free(newargs); + } + } + return result; +} PyObject * PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, @@ -834,31 +892,44 @@ PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, assert(PyVectorcall_NARGS(nargsf) >= 1); PyThreadState *tstate = _PyThreadState_GET(); - _PyCStackRef method; + _PyCStackRef self, method; + _PyThreadState_PushCStackRef(tstate, &self); _PyThreadState_PushCStackRef(tstate, &method); /* Use args[0] as "self" argument */ - int unbound = _PyObject_GetMethodStackRef(tstate, args[0], name, &method.ref); - if (PyStackRef_IsNull(method.ref)) { + self.ref = PyStackRef_FromPyObjectBorrow(args[0]); + int unbound = _PyObject_GetMethodStackRef(tstate, &self.ref, name, &method.ref); + if (unbound < 0) { _PyThreadState_PopCStackRef(tstate, &method); + _PyThreadState_PopCStackRef(tstate, &self); return NULL; } + PyObject *callable = PyStackRef_AsPyObjectBorrow(method.ref); + PyObject *self_obj = PyStackRef_AsPyObjectBorrow(self.ref); + PyObject *result; - if (unbound) { + EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_METHOD, callable); + if (self_obj == NULL) { + /* Skip "self". We can keep PY_VECTORCALL_ARGUMENTS_OFFSET since + * args[-1] in the onward call is args[0] here. */ + result = _PyObject_VectorcallTstate(tstate, callable, + args + 1, nargsf - 1, kwnames); + } + else if (self_obj == args[0]) { /* We must remove PY_VECTORCALL_ARGUMENTS_OFFSET since * that would be interpreted as allowing to change args[-1] */ - nargsf &= ~PY_VECTORCALL_ARGUMENTS_OFFSET; + result = _PyObject_VectorcallTstate(tstate, callable, args, + nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET, + kwnames); } else { - /* Skip "self". We can keep PY_VECTORCALL_ARGUMENTS_OFFSET since - * args[-1] in the onward call is args[0] here. */ - args++; - nargsf--; + /* classmethod: self_obj is the type, not args[0]. Replace + * args[0] with self_obj and call the underlying callable. */ + result = _PyObject_VectorcallPrepend(tstate, callable, self_obj, + args + 1, nargsf - 1, kwnames); } - EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_METHOD, callable); - PyObject *result = _PyObject_VectorcallTstate(tstate, callable, - args, nargsf, kwnames); _PyThreadState_PopCStackRef(tstate, &method); + _PyThreadState_PopCStackRef(tstate, &self); return result; } @@ -871,55 +942,26 @@ PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...) return null_error(tstate); } - _PyCStackRef method; - _PyThreadState_PushCStackRef(tstate, &method); - int is_method = _PyObject_GetMethodStackRef(tstate, obj, name, &method.ref); - if (PyStackRef_IsNull(method.ref)) { - _PyThreadState_PopCStackRef(tstate, &method); - return NULL; - } - PyObject *callable = PyStackRef_AsPyObjectBorrow(method.ref); - obj = is_method ? obj : NULL; - - va_list vargs; - va_start(vargs, name); - PyObject *result = object_vacall(tstate, obj, callable, vargs); - va_end(vargs); - - _PyThreadState_PopCStackRef(tstate, &method); - return result; -} - - -PyObject * -_PyObject_CallMethodIdObjArgs(PyObject *obj, _Py_Identifier *name, ...) -{ - PyThreadState *tstate = _PyThreadState_GET(); - if (obj == NULL || name == NULL) { - return null_error(tstate); - } - - PyObject *oname = _PyUnicode_FromId(name); /* borrowed */ - if (!oname) { - return NULL; - } - _PyCStackRef method; + _PyCStackRef self, method; + _PyThreadState_PushCStackRef(tstate, &self); _PyThreadState_PushCStackRef(tstate, &method); - int is_method = _PyObject_GetMethodStackRef(tstate, obj, oname, &method.ref); - if (PyStackRef_IsNull(method.ref)) { + self.ref = PyStackRef_FromPyObjectBorrow(obj); + int res = _PyObject_GetMethodStackRef(tstate, &self.ref, name, &method.ref); + if (res < 0) { _PyThreadState_PopCStackRef(tstate, &method); + _PyThreadState_PopCStackRef(tstate, &self); return NULL; } PyObject *callable = PyStackRef_AsPyObjectBorrow(method.ref); - - obj = is_method ? obj : NULL; + PyObject *self_obj = PyStackRef_AsPyObjectBorrow(self.ref); va_list vargs; va_start(vargs, name); - PyObject *result = object_vacall(tstate, obj, callable, vargs); + PyObject *result = object_vacall(tstate, self_obj, callable, vargs); va_end(vargs); _PyThreadState_PopCStackRef(tstate, &method); + _PyThreadState_PopCStackRef(tstate, &self); return result; } @@ -964,6 +1006,10 @@ _PyStack_AsDict(PyObject *const *values, PyObject *kwnames) The newly allocated argument vector supports PY_VECTORCALL_ARGUMENTS_OFFSET. + The positional arguments are borrowed references from the input array + (which must be kept alive by the caller). The keyword argument values + are new references. + When done, you must call _PyStack_UnpackDict_Free(stack, nargs, kwnames) */ PyObject *const * _PyStack_UnpackDict(PyThreadState *tstate, @@ -999,9 +1045,9 @@ _PyStack_UnpackDict(PyThreadState *tstate, stack++; /* For PY_VECTORCALL_ARGUMENTS_OFFSET */ - /* Copy positional arguments */ + /* Copy positional arguments (borrowed references) */ for (Py_ssize_t i = 0; i < nargs; i++) { - stack[i] = Py_NewRef(args[i]); + stack[i] = args[i]; } PyObject **kwstack = stack + nargs; @@ -1038,9 +1084,10 @@ void _PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs, PyObject *kwnames) { - Py_ssize_t n = PyTuple_GET_SIZE(kwnames) + nargs; - for (Py_ssize_t i = 0; i < n; i++) { - Py_DECREF(stack[i]); + /* Only decref kwargs values, positional args are borrowed */ + Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwnames); + for (Py_ssize_t i = 0; i < nkwargs; i++) { + Py_DECREF(stack[nargs + i]); } _PyStack_UnpackDict_FreeNoDecRef(stack, kwnames); } diff --git a/Objects/classobject.c b/Objects/classobject.c index e71f301f2efd77..238f1c1dad7d86 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -7,6 +7,7 @@ #include "pycore_object.h" #include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_symtable.h" // _Py_Mangle() #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() @@ -51,54 +52,7 @@ method_vectorcall(PyObject *method, PyObject *const *args, PyThreadState *tstate = _PyThreadState_GET(); PyObject *self = PyMethod_GET_SELF(method); PyObject *func = PyMethod_GET_FUNCTION(method); - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); - assert(nargs == 0 || args[nargs-1]); - - PyObject *result; - if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) { - /* PY_VECTORCALL_ARGUMENTS_OFFSET is set, so we are allowed to mutate the vector */ - PyObject **newargs = (PyObject**)args - 1; - nargs += 1; - PyObject *tmp = newargs[0]; - newargs[0] = self; - assert(newargs[nargs-1]); - result = _PyObject_VectorcallTstate(tstate, func, newargs, - nargs, kwnames); - newargs[0] = tmp; - } - else { - Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); - Py_ssize_t totalargs = nargs + nkwargs; - if (totalargs == 0) { - return _PyObject_VectorcallTstate(tstate, func, &self, 1, NULL); - } - - PyObject *newargs_stack[_PY_FASTCALL_SMALL_STACK]; - PyObject **newargs; - if (totalargs <= (Py_ssize_t)Py_ARRAY_LENGTH(newargs_stack) - 1) { - newargs = newargs_stack; - } - else { - newargs = PyMem_Malloc((totalargs+1) * sizeof(PyObject *)); - if (newargs == NULL) { - _PyErr_NoMemory(tstate); - return NULL; - } - } - /* use borrowed references */ - newargs[0] = self; - /* bpo-37138: since totalargs > 0, it's impossible that args is NULL. - * We need this, since calling memcpy() with a NULL pointer is - * undefined behaviour. */ - assert(args != NULL); - memcpy(newargs + 1, args, totalargs * sizeof(PyObject *)); - result = _PyObject_VectorcallTstate(tstate, func, - newargs, nargs+1, kwnames); - if (newargs != newargs_stack) { - PyMem_Free(newargs); - } - } - return result; + return _PyObject_VectorcallPrepend(tstate, func, self, args, nargsf, kwnames); } @@ -143,6 +97,20 @@ method___reduce___impl(PyMethodObject *self) if (funcname == NULL) { return NULL; } + if (_Py_IsPrivateName(funcname)) { + PyObject *classname = PyType_Check(funcself) + ? PyType_GetName((PyTypeObject *)funcself) + : PyType_GetName(Py_TYPE(funcself)); + if (classname == NULL) { + Py_DECREF(funcname); + return NULL; + } + Py_SETREF(funcname, _Py_Mangle(classname, funcname)); + Py_DECREF(classname); + if (funcname == NULL) { + return NULL; + } + } return Py_BuildValue( "N(ON)", _PyEval_GetBuiltin(&_Py_ID(getattr)), funcself, funcname); } diff --git a/Objects/clinic/bytearrayobject.c.h b/Objects/clinic/bytearrayobject.c.h index ffb45ade11f6dc..41ce82c05c57d9 100644 --- a/Objects/clinic/bytearrayobject.c.h +++ b/Objects/clinic/bytearrayobject.c.h @@ -599,7 +599,7 @@ PyDoc_STRVAR(bytearray_resize__doc__, "Resize the internal buffer of bytearray to len.\n" "\n" " size\n" -" New size to resize to.."); +" New size to resize to."); #define BYTEARRAY_RESIZE_METHODDEF \ {"resize", (PyCFunction)bytearray_resize, METH_O, bytearray_resize__doc__}, @@ -625,7 +625,46 @@ bytearray_resize(PyObject *self, PyObject *arg) } size = ival; } + Py_BEGIN_CRITICAL_SECTION(self); return_value = bytearray_resize_impl((PyByteArrayObject *)self, size); + Py_END_CRITICAL_SECTION(); + +exit: + return return_value; +} + +PyDoc_STRVAR(bytearray_take_bytes__doc__, +"take_bytes($self, n=None, /)\n" +"--\n" +"\n" +"Take *n* bytes from the bytearray and return them as a bytes object.\n" +"\n" +" n\n" +" Bytes to take, negative indexes from end. None indicates all bytes."); + +#define BYTEARRAY_TAKE_BYTES_METHODDEF \ + {"take_bytes", _PyCFunction_CAST(bytearray_take_bytes), METH_FASTCALL, bytearray_take_bytes__doc__}, + +static PyObject * +bytearray_take_bytes_impl(PyByteArrayObject *self, PyObject *n); + +static PyObject * +bytearray_take_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *n = Py_None; + + if (!_PyArg_CheckPositional("take_bytes", nargs, 0, 1)) { + goto exit; + } + if (nargs < 1) { + goto skip_optional; + } + n = args[0]; +skip_optional: + Py_BEGIN_CRITICAL_SECTION(self); + return_value = bytearray_take_bytes_impl((PyByteArrayObject *)self, n); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -640,8 +679,9 @@ PyDoc_STRVAR(bytearray_translate__doc__, " table\n" " Translation table, which must be a bytes object of length 256.\n" "\n" -"All characters occurring in the optional argument delete are removed.\n" -"The remaining characters are mapped through the given translation table."); +"All characters occurring in the optional argument delete are\n" +"removed. The remaining characters are mapped through the given\n" +"translation table."); #define BYTEARRAY_TRANSLATE_METHODDEF \ {"translate", _PyCFunction_CAST(bytearray_translate), METH_FASTCALL|METH_KEYWORDS, bytearray_translate__doc__}, @@ -711,8 +751,8 @@ PyDoc_STRVAR(bytearray_maketrans__doc__, "\n" "Return a translation table usable for the bytes or bytearray translate method.\n" "\n" -"The returned table will be one where each byte in frm is mapped to the byte at\n" -"the same position in to.\n" +"The returned table will be one where each byte in frm is mapped to\n" +"the byte at the same position in to.\n" "\n" "The bytes objects frm and to must be of the same length."); @@ -754,7 +794,7 @@ bytearray_maketrans(PyObject *null, PyObject *const *args, Py_ssize_t nargs) } PyDoc_STRVAR(bytearray_replace__doc__, -"replace($self, old, new, count=-1, /)\n" +"replace($self, old, new, /, count=-1)\n" "--\n" "\n" "Return a copy with all occurrences of substring old replaced by new.\n" @@ -763,25 +803,56 @@ PyDoc_STRVAR(bytearray_replace__doc__, " Maximum number of occurrences to replace.\n" " -1 (the default value) means replace all occurrences.\n" "\n" -"If the optional argument count is given, only the first count occurrences are\n" -"replaced."); +"If count is given, only the first count occurrences are replaced.\n" +"If count is not specified or -1, then all occurrences are replaced."); #define BYTEARRAY_REPLACE_METHODDEF \ - {"replace", _PyCFunction_CAST(bytearray_replace), METH_FASTCALL, bytearray_replace__doc__}, + {"replace", _PyCFunction_CAST(bytearray_replace), METH_FASTCALL|METH_KEYWORDS, bytearray_replace__doc__}, static PyObject * bytearray_replace_impl(PyByteArrayObject *self, Py_buffer *old, Py_buffer *new, Py_ssize_t count); static PyObject * -bytearray_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +bytearray_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(count), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "", "count", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "replace", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; Py_buffer old = {NULL, NULL}; Py_buffer new = {NULL, NULL}; Py_ssize_t count = -1; - if (!_PyArg_CheckPositional("replace", nargs, 2, 3)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { goto exit; } if (PyObject_GetBuffer(args[0], &old, PyBUF_SIMPLE) != 0) { @@ -790,8 +861,8 @@ bytearray_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs) if (PyObject_GetBuffer(args[1], &new, PyBUF_SIMPLE) != 0) { goto exit; } - if (nargs < 3) { - goto skip_optional; + if (!noptargs) { + goto skip_optional_pos; } { Py_ssize_t ival = -1; @@ -805,7 +876,7 @@ bytearray_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } count = ival; } -skip_optional: +skip_optional_pos: Py_BEGIN_CRITICAL_SECTION(self); return_value = bytearray_replace_impl((PyByteArrayObject *)self, &old, &new, count); Py_END_CRITICAL_SECTION(); @@ -831,8 +902,8 @@ PyDoc_STRVAR(bytearray_split__doc__, "\n" " sep\n" " The delimiter according which to split the bytearray.\n" -" None (the default value) means split on ASCII whitespace characters\n" -" (space, tab, return, newline, formfeed, vertical tab).\n" +" None (the default value) means split on ASCII whitespace\n" +" characters (space, tab, return, newline, formfeed, vertical tab).\n" " maxsplit\n" " Maximum number of splits to do.\n" " -1 (the default value) means no limit."); @@ -921,12 +992,13 @@ PyDoc_STRVAR(bytearray_partition__doc__, "\n" "Partition the bytearray into three parts using the given separator.\n" "\n" -"This will search for the separator sep in the bytearray. If the separator is\n" -"found, returns a 3-tuple containing the part before the separator, the\n" -"separator itself, and the part after it as new bytearray objects.\n" +"This will search for the separator sep in the bytearray. If the\n" +"separator is found, returns a 3-tuple containing the part before the\n" +"separator, the separator itself, and the part after it as new\n" +"bytearray objects.\n" "\n" -"If the separator is not found, returns a 3-tuple containing the copy of the\n" -"original bytearray object and two empty bytearray objects."); +"If the separator is not found, returns a 3-tuple containing the copy\n" +"of the original bytearray object and two empty bytearray objects."); #define BYTEARRAY_PARTITION_METHODDEF \ {"partition", (PyCFunction)bytearray_partition, METH_O, bytearray_partition__doc__}, @@ -952,13 +1024,14 @@ PyDoc_STRVAR(bytearray_rpartition__doc__, "\n" "Partition the bytearray into three parts using the given separator.\n" "\n" -"This will search for the separator sep in the bytearray, starting at the end.\n" -"If the separator is found, returns a 3-tuple containing the part before the\n" -"separator, the separator itself, and the part after it as new bytearray\n" -"objects.\n" +"This will search for the separator sep in the bytearray, starting at\n" +"the end. If the separator is found, returns a 3-tuple containing\n" +"the part before the separator, the separator itself, and the part\n" +"after it as new bytearray objects.\n" "\n" -"If the separator is not found, returns a 3-tuple containing two empty bytearray\n" -"objects and the copy of the original bytearray object."); +"If the separator is not found, returns a 3-tuple containing two\n" +"empty bytearray objects and the copy of the original bytearray\n" +"object."); #define BYTEARRAY_RPARTITION_METHODDEF \ {"rpartition", (PyCFunction)bytearray_rpartition, METH_O, bytearray_rpartition__doc__}, @@ -986,13 +1059,14 @@ PyDoc_STRVAR(bytearray_rsplit__doc__, "\n" " sep\n" " The delimiter according which to split the bytearray.\n" -" None (the default value) means split on ASCII whitespace characters\n" -" (space, tab, return, newline, formfeed, vertical tab).\n" +" None (the default value) means split on ASCII whitespace\n" +" characters (space, tab, return, newline, formfeed, vertical tab).\n" " maxsplit\n" " Maximum number of splits to do.\n" " -1 (the default value) means no limit.\n" "\n" -"Splitting is done starting at the end of the bytearray and working to the front."); +"Splitting is done starting at the end of the bytearray and working\n" +"to the front."); #define BYTEARRAY_RSPLIT_METHODDEF \ {"rsplit", _PyCFunction_CAST(bytearray_rsplit), METH_FASTCALL|METH_KEYWORDS, bytearray_rsplit__doc__}, @@ -1294,7 +1368,8 @@ PyDoc_STRVAR(bytearray_strip__doc__, "\n" "Strip leading and trailing bytes contained in the argument.\n" "\n" -"If the argument is omitted or None, strip leading and trailing ASCII whitespace."); +"If the argument is omitted or None, strip leading and trailing ASCII\n" +"whitespace."); #define BYTEARRAY_STRIP_METHODDEF \ {"strip", _PyCFunction_CAST(bytearray_strip), METH_FASTCALL, bytearray_strip__doc__}, @@ -1405,11 +1480,11 @@ PyDoc_STRVAR(bytearray_decode__doc__, " encoding\n" " The encoding with which to decode the bytearray.\n" " errors\n" -" The error handling scheme to use for the handling of decoding errors.\n" -" The default is \'strict\' meaning that decoding errors raise a\n" -" UnicodeDecodeError. Other possible values are \'ignore\' and \'replace\'\n" -" as well as any other name registered with codecs.register_error that\n" -" can handle UnicodeDecodeErrors."); +" The error handling scheme to use for the handling of decoding\n" +" errors. The default is \'strict\' meaning that decoding errors\n" +" raise a UnicodeDecodeError. Other possible values are \'ignore\'\n" +" and \'replace\' as well as any other name registered with\n" +" codecs.register_error that can handle UnicodeDecodeErrors."); #define BYTEARRAY_DECODE_METHODDEF \ {"decode", _PyCFunction_CAST(bytearray_decode), METH_FASTCALL|METH_KEYWORDS, bytearray_decode__doc__}, @@ -1508,7 +1583,8 @@ PyDoc_STRVAR(bytearray_join__doc__, "\n" "Concatenate any number of bytes/bytearray objects.\n" "\n" -"The bytearray whose method is called is inserted in between each pair.\n" +"The bytearray whose method is called is inserted in between each\n" +"pair.\n" "\n" "The result is returned as a new bytearray object."); @@ -1536,8 +1612,8 @@ PyDoc_STRVAR(bytearray_splitlines__doc__, "\n" "Return a list of the lines in the bytearray, breaking at line boundaries.\n" "\n" -"Line breaks are not included in the resulting list unless keepends is given and\n" -"true."); +"Line breaks are not included in the resulting list unless keepends\n" +"is given and true."); #define BYTEARRAY_SPLITLINES_METHODDEF \ {"splitlines", _PyCFunction_CAST(bytearray_splitlines), METH_FASTCALL|METH_KEYWORDS, bytearray_splitlines__doc__}, @@ -1608,7 +1684,8 @@ PyDoc_STRVAR(bytearray_fromhex__doc__, "Create a bytearray object from a string of hexadecimal numbers.\n" "\n" "Spaces between two numbers are accepted.\n" -"Example: bytearray.fromhex(\'B9 01EF\') -> bytearray(b\'\\\\xb9\\\\x01\\\\xef\')"); +"Example:\n" +" bytearray.fromhex(\'B9 01EF\') -> bytearray(b\'\\\\xb9\\\\x01\\\\xef\')"); #define BYTEARRAY_FROMHEX_METHODDEF \ {"fromhex", (PyCFunction)bytearray_fromhex, METH_O|METH_CLASS, bytearray_fromhex__doc__}, @@ -1635,8 +1712,8 @@ PyDoc_STRVAR(bytearray_hex__doc__, " sep\n" " An optional single character or byte to separate hex bytes.\n" " bytes_per_sep\n" -" How many bytes between separators. Positive values count from the\n" -" right, negative values count from the left.\n" +" How many bytes between separators. Positive values count from\n" +" the right, negative values count from the left.\n" "\n" "Example:\n" ">>> value = bytearray([0xb9, 0x01, 0xef])\n" @@ -1653,7 +1730,8 @@ PyDoc_STRVAR(bytearray_hex__doc__, {"hex", _PyCFunction_CAST(bytearray_hex), METH_FASTCALL|METH_KEYWORDS, bytearray_hex__doc__}, static PyObject * -bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, int bytes_per_sep); +bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, + Py_ssize_t bytes_per_sep); static PyObject * bytearray_hex(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -1689,7 +1767,7 @@ bytearray_hex(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *sep = NULL; - int bytes_per_sep = 1; + Py_ssize_t bytes_per_sep = 1; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -1705,9 +1783,17 @@ bytearray_hex(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject goto skip_optional_pos; } } - bytes_per_sep = PyLong_AsInt(args[1]); - if (bytes_per_sep == -1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + bytes_per_sep = ival; } skip_optional_pos: Py_BEGIN_CRITICAL_SECTION(self); @@ -1796,4 +1882,4 @@ bytearray_sizeof(PyObject *self, PyObject *Py_UNUSED(ignored)) { return bytearray_sizeof_impl((PyByteArrayObject *)self); } -/*[clinic end generated code: output=be6d28193bc96a2c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6dc315d35de3e670 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/bytesobject.c.h b/Objects/clinic/bytesobject.c.h index 00cf13d422d900..ee2b737f9e63f9 100644 --- a/Objects/clinic/bytesobject.c.h +++ b/Objects/clinic/bytesobject.c.h @@ -35,8 +35,8 @@ PyDoc_STRVAR(bytes_split__doc__, "\n" " sep\n" " The delimiter according which to split the bytes.\n" -" None (the default value) means split on ASCII whitespace characters\n" -" (space, tab, return, newline, formfeed, vertical tab).\n" +" None (the default value) means split on ASCII whitespace\n" +" characters (space, tab, return, newline, formfeed, vertical tab).\n" " maxsplit\n" " Maximum number of splits to do.\n" " -1 (the default value) means no limit."); @@ -122,12 +122,12 @@ PyDoc_STRVAR(bytes_partition__doc__, "\n" "Partition the bytes into three parts using the given separator.\n" "\n" -"This will search for the separator sep in the bytes. If the separator is found,\n" -"returns a 3-tuple containing the part before the separator, the separator\n" -"itself, and the part after it.\n" +"This will search for the separator sep in the bytes. If the\n" +"separator is found, returns a 3-tuple containing the part before the\n" +"separator, the separator itself, and the part after it.\n" "\n" -"If the separator is not found, returns a 3-tuple containing the original bytes\n" -"object and two empty bytes objects."); +"If the separator is not found, returns a 3-tuple containing the\n" +"original bytes object and two empty bytes objects."); #define BYTES_PARTITION_METHODDEF \ {"partition", (PyCFunction)bytes_partition, METH_O, bytes_partition__doc__}, @@ -161,12 +161,13 @@ PyDoc_STRVAR(bytes_rpartition__doc__, "\n" "Partition the bytes into three parts using the given separator.\n" "\n" -"This will search for the separator sep in the bytes, starting at the end. If\n" -"the separator is found, returns a 3-tuple containing the part before the\n" -"separator, the separator itself, and the part after it.\n" +"This will search for the separator sep in the bytes, starting at the\n" +"end. If the separator is found, returns a 3-tuple containing the\n" +"part before the separator, the separator itself, and the part after\n" +"it.\n" "\n" -"If the separator is not found, returns a 3-tuple containing two empty bytes\n" -"objects and the original bytes object."); +"If the separator is not found, returns a 3-tuple containing two\n" +"empty bytes objects and the original bytes object."); #define BYTES_RPARTITION_METHODDEF \ {"rpartition", (PyCFunction)bytes_rpartition, METH_O, bytes_rpartition__doc__}, @@ -202,13 +203,14 @@ PyDoc_STRVAR(bytes_rsplit__doc__, "\n" " sep\n" " The delimiter according which to split the bytes.\n" -" None (the default value) means split on ASCII whitespace characters\n" -" (space, tab, return, newline, formfeed, vertical tab).\n" +" None (the default value) means split on ASCII whitespace\n" +" characters (space, tab, return, newline, formfeed, vertical tab).\n" " maxsplit\n" " Maximum number of splits to do.\n" " -1 (the default value) means no limit.\n" "\n" -"Splitting is done starting at the end of the bytes and working to the front."); +"Splitting is done starting at the end of the bytes and working to\n" +"the front."); #define BYTES_RSPLIT_METHODDEF \ {"rsplit", _PyCFunction_CAST(bytes_rsplit), METH_FASTCALL|METH_KEYWORDS, bytes_rsplit__doc__}, @@ -523,7 +525,8 @@ PyDoc_STRVAR(bytes_strip__doc__, "\n" "Strip leading and trailing bytes contained in the argument.\n" "\n" -"If the argument is omitted or None, strip leading and trailing ASCII whitespace."); +"If the argument is omitted or None, strip leading and trailing ASCII\n" +"whitespace."); #define BYTES_STRIP_METHODDEF \ {"strip", _PyCFunction_CAST(bytes_strip), METH_FASTCALL, bytes_strip__doc__}, @@ -677,8 +680,9 @@ PyDoc_STRVAR(bytes_translate__doc__, " table\n" " Translation table, which must be a bytes object of length 256.\n" "\n" -"All characters occurring in the optional argument delete are removed.\n" -"The remaining characters are mapped through the given translation table."); +"All characters occurring in the optional argument delete are\n" +"removed. The remaining characters are mapped through the given\n" +"translation table."); #define BYTES_TRANSLATE_METHODDEF \ {"translate", _PyCFunction_CAST(bytes_translate), METH_FASTCALL|METH_KEYWORDS, bytes_translate__doc__}, @@ -746,8 +750,8 @@ PyDoc_STRVAR(bytes_maketrans__doc__, "\n" "Return a translation table usable for the bytes or bytearray translate method.\n" "\n" -"The returned table will be one where each byte in frm is mapped to the byte at\n" -"the same position in to.\n" +"The returned table will be one where each byte in frm is mapped to\n" +"the byte at the same position in to.\n" "\n" "The bytes objects frm and to must be of the same length."); @@ -789,7 +793,7 @@ bytes_maketrans(PyObject *null, PyObject *const *args, Py_ssize_t nargs) } PyDoc_STRVAR(bytes_replace__doc__, -"replace($self, old, new, count=-1, /)\n" +"replace($self, old, new, /, count=-1)\n" "--\n" "\n" "Return a copy with all occurrences of substring old replaced by new.\n" @@ -798,25 +802,56 @@ PyDoc_STRVAR(bytes_replace__doc__, " Maximum number of occurrences to replace.\n" " -1 (the default value) means replace all occurrences.\n" "\n" -"If the optional argument count is given, only the first count occurrences are\n" -"replaced."); +"If count is given, only the first count occurrences are replaced.\n" +"If count is not specified or -1, then all occurrences are replaced."); #define BYTES_REPLACE_METHODDEF \ - {"replace", _PyCFunction_CAST(bytes_replace), METH_FASTCALL, bytes_replace__doc__}, + {"replace", _PyCFunction_CAST(bytes_replace), METH_FASTCALL|METH_KEYWORDS, bytes_replace__doc__}, static PyObject * bytes_replace_impl(PyBytesObject *self, Py_buffer *old, Py_buffer *new, Py_ssize_t count); static PyObject * -bytes_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +bytes_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(count), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "", "count", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "replace", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[3]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; Py_buffer old = {NULL, NULL}; Py_buffer new = {NULL, NULL}; Py_ssize_t count = -1; - if (!_PyArg_CheckPositional("replace", nargs, 2, 3)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 2, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { goto exit; } if (PyObject_GetBuffer(args[0], &old, PyBUF_SIMPLE) != 0) { @@ -825,8 +860,8 @@ bytes_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs) if (PyObject_GetBuffer(args[1], &new, PyBUF_SIMPLE) != 0) { goto exit; } - if (nargs < 3) { - goto skip_optional; + if (!noptargs) { + goto skip_optional_pos; } { Py_ssize_t ival = -1; @@ -840,7 +875,7 @@ bytes_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } count = ival; } -skip_optional: +skip_optional_pos: return_value = bytes_replace_impl((PyBytesObject *)self, &old, &new, count); exit: @@ -862,8 +897,9 @@ PyDoc_STRVAR(bytes_removeprefix__doc__, "\n" "Return a bytes object with the given prefix string removed if present.\n" "\n" -"If the bytes starts with the prefix string, return bytes[len(prefix):].\n" -"Otherwise, return a copy of the original bytes."); +"If the bytes starts with the prefix string, return\n" +"bytes[len(prefix):]. Otherwise, return a copy of the original\n" +"bytes."); #define BYTES_REMOVEPREFIX_METHODDEF \ {"removeprefix", (PyCFunction)bytes_removeprefix, METH_O, bytes_removeprefix__doc__}, @@ -897,9 +933,9 @@ PyDoc_STRVAR(bytes_removesuffix__doc__, "\n" "Return a bytes object with the given suffix string removed if present.\n" "\n" -"If the bytes ends with the suffix string and that suffix is not empty,\n" -"return bytes[:-len(prefix)]. Otherwise, return a copy of the original\n" -"bytes."); +"If the bytes ends with the suffix string and that suffix is not\n" +"empty, return bytes[:-len(prefix)]. Otherwise, return a copy of the\n" +"original bytes."); #define BYTES_REMOVESUFFIX_METHODDEF \ {"removesuffix", (PyCFunction)bytes_removesuffix, METH_O, bytes_removesuffix__doc__}, @@ -1038,11 +1074,11 @@ PyDoc_STRVAR(bytes_decode__doc__, " encoding\n" " The encoding with which to decode the bytes.\n" " errors\n" -" The error handling scheme to use for the handling of decoding errors.\n" -" The default is \'strict\' meaning that decoding errors raise a\n" -" UnicodeDecodeError. Other possible values are \'ignore\' and \'replace\'\n" -" as well as any other name registered with codecs.register_error that\n" -" can handle UnicodeDecodeErrors."); +" The error handling scheme to use for the handling of decoding\n" +" errors. The default is \'strict\' meaning that decoding errors\n" +" raise a UnicodeDecodeError. Other possible values are \'ignore\'\n" +" and \'replace\' as well as any other name registered with\n" +" codecs.register_error that can handle UnicodeDecodeErrors."); #define BYTES_DECODE_METHODDEF \ {"decode", _PyCFunction_CAST(bytes_decode), METH_FASTCALL|METH_KEYWORDS, bytes_decode__doc__}, @@ -1139,8 +1175,8 @@ PyDoc_STRVAR(bytes_splitlines__doc__, "\n" "Return a list of the lines in the bytes, breaking at line boundaries.\n" "\n" -"Line breaks are not included in the resulting list unless keepends is given and\n" -"true."); +"Line breaks are not included in the resulting list unless keepends\n" +"is given and true."); #define BYTES_SPLITLINES_METHODDEF \ {"splitlines", _PyCFunction_CAST(bytes_splitlines), METH_FASTCALL|METH_KEYWORDS, bytes_splitlines__doc__}, @@ -1236,8 +1272,8 @@ PyDoc_STRVAR(bytes_hex__doc__, " sep\n" " An optional single character or byte to separate hex bytes.\n" " bytes_per_sep\n" -" How many bytes between separators. Positive values count from the\n" -" right, negative values count from the left.\n" +" How many bytes between separators. Positive values count from\n" +" the right, negative values count from the left.\n" "\n" "Example:\n" ">>> value = b\'\\xb9\\x01\\xef\'\n" @@ -1254,7 +1290,7 @@ PyDoc_STRVAR(bytes_hex__doc__, {"hex", _PyCFunction_CAST(bytes_hex), METH_FASTCALL|METH_KEYWORDS, bytes_hex__doc__}, static PyObject * -bytes_hex_impl(PyBytesObject *self, PyObject *sep, int bytes_per_sep); +bytes_hex_impl(PyBytesObject *self, PyObject *sep, Py_ssize_t bytes_per_sep); static PyObject * bytes_hex(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -1290,7 +1326,7 @@ bytes_hex(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *sep = NULL; - int bytes_per_sep = 1; + Py_ssize_t bytes_per_sep = 1; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -1306,9 +1342,17 @@ bytes_hex(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwn goto skip_optional_pos; } } - bytes_per_sep = PyLong_AsInt(args[1]); - if (bytes_per_sep == -1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + bytes_per_sep = ival; } skip_optional_pos: return_value = bytes_hex_impl((PyBytesObject *)self, sep, bytes_per_sep); @@ -1411,4 +1455,4 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=08b9507244f73638 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c20458db7a2123db input=a9049054013a1b77]*/ diff --git a/Objects/clinic/codeobject.c.h b/Objects/clinic/codeobject.c.h index 0cd6e0b56620e8..88333e9d3363eb 100644 --- a/Objects/clinic/codeobject.c.h +++ b/Objects/clinic/codeobject.c.h @@ -414,7 +414,8 @@ PyDoc_STRVAR(code__varname_from_oparg__doc__, "\n" "(internal-only) Return the local variable name for the given oparg.\n" "\n" -"WARNING: this method is for internal use only and may change or go away."); +"WARNING: this method is for internal use only and may change or go\n" +"away."); #define CODE__VARNAME_FROM_OPARG_METHODDEF \ {"_varname_from_oparg", _PyCFunction_CAST(code__varname_from_oparg), METH_FASTCALL|METH_KEYWORDS, code__varname_from_oparg__doc__}, @@ -470,4 +471,4 @@ code__varname_from_oparg(PyObject *self, PyObject *const *args, Py_ssize_t nargs exit: return return_value; } -/*[clinic end generated code: output=c5c6e40fc357defe input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5c22e29e430401b4 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/dictobject.c.h b/Objects/clinic/dictobject.c.h index abf6b38449fcb0..15b8705d9c78e3 100644 --- a/Objects/clinic/dictobject.c.h +++ b/Objects/clinic/dictobject.c.h @@ -323,4 +323,22 @@ dict_values(PyObject *self, PyObject *Py_UNUSED(ignored)) { return dict_values_impl((PyDictObject *)self); } -/*[clinic end generated code: output=9007b74432217017 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(frozendict_copy__doc__, +"copy($self, /)\n" +"--\n" +"\n" +"Return a shallow copy of the frozendict."); + +#define FROZENDICT_COPY_METHODDEF \ + {"copy", (PyCFunction)frozendict_copy, METH_NOARGS, frozendict_copy__doc__}, + +static PyObject * +frozendict_copy_impl(PyFrozenDictObject *self); + +static PyObject * +frozendict_copy(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return frozendict_copy_impl((PyFrozenDictObject *)self); +} +/*[clinic end generated code: output=f4c88a3464928ae3 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/enumobject.c.h b/Objects/clinic/enumobject.c.h index 29ff11e89b9d1d..1bda482f4955ae 100644 --- a/Objects/clinic/enumobject.c.h +++ b/Objects/clinic/enumobject.c.h @@ -82,7 +82,7 @@ enum_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) } PyDoc_STRVAR(reversed_new__doc__, -"reversed(sequence, /)\n" +"reversed(object, /)\n" "--\n" "\n" "Return a reverse iterator over the values of the given sequence."); @@ -110,4 +110,4 @@ reversed_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=3300305b351674d3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=155cc9483d5f9eab input=a9049054013a1b77]*/ diff --git a/Objects/clinic/exceptions.c.h b/Objects/clinic/exceptions.c.h index 9baac8b1cc660b..5047a673e579c6 100644 --- a/Objects/clinic/exceptions.c.h +++ b/Objects/clinic/exceptions.c.h @@ -44,7 +44,7 @@ BaseException___setstate__(PyObject *self, PyObject *state) { PyObject *return_value = NULL; - Py_BEGIN_CRITICAL_SECTION(self); + Py_BEGIN_CRITICAL_SECTION(state); return_value = BaseException___setstate___impl((PyBaseExceptionObject *)self, state); Py_END_CRITICAL_SECTION(); @@ -380,4 +380,4 @@ BaseExceptionGroup_subgroup(PyObject *self, PyObject *matcher_value) return return_value; } -/*[clinic end generated code: output=fcf70b3b71f3d14a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e63b88d0443b4f92 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/floatobject.c.h b/Objects/clinic/floatobject.c.h index 4051131f480ccb..8768555c909257 100644 --- a/Objects/clinic/floatobject.c.h +++ b/Objects/clinic/floatobject.c.h @@ -290,9 +290,9 @@ PyDoc_STRVAR(float___getformat____doc__, "\n" "It exists mainly to be used in Python\'s test suite.\n" "\n" -"This function returns whichever of \'unknown\', \'IEEE, big-endian\' or \'IEEE,\n" -"little-endian\' best describes the format of floating-point numbers used by the\n" -"C type named by typestr."); +"This function returns whichever of \'IEEE, big-endian\' or \'IEEE,\n" +"little-endian\' best describes the format of floating-point numbers\n" +"used by the C type named by typestr."); #define FLOAT___GETFORMAT___METHODDEF \ {"__getformat__", (PyCFunction)float___getformat__, METH_O|METH_CLASS, float___getformat____doc__}, @@ -353,4 +353,4 @@ float___format__(PyObject *self, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=927035897ea3573f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5d7b0bf9e47ff997 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/listobject.c.h b/Objects/clinic/listobject.c.h index 26ba5b954336da..f3821ef5f70c21 100644 --- a/Objects/clinic/listobject.c.h +++ b/Objects/clinic/listobject.c.h @@ -200,11 +200,11 @@ PyDoc_STRVAR(list_sort__doc__, "\n" "Sort the list in ascending order and return None.\n" "\n" -"The sort is in-place (i.e. the list itself is modified) and stable (i.e. the\n" -"order of two equal elements is maintained).\n" +"The sort is in-place (i.e. the list itself is modified) and stable\n" +"(i.e. the order of two equal elements is maintained).\n" "\n" -"If a key function is given, apply it once to each list item and sort them,\n" -"ascending or descending, according to their function values.\n" +"If a key function is given, apply it once to each list item and sort\n" +"them, ascending or descending, according to their function values.\n" "\n" "The reverse flag can be set to sort in descending order."); @@ -468,4 +468,4 @@ list___reversed__(PyObject *self, PyObject *Py_UNUSED(ignored)) { return list___reversed___impl((PyListObject *)self); } -/*[clinic end generated code: output=ae13fc2b56dc27c2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=06c21b0bffbe8d84 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/longobject.c.h b/Objects/clinic/longobject.c.h index a236a32c091c4c..52ecaffa1f4cf3 100644 --- a/Objects/clinic/longobject.c.h +++ b/Objects/clinic/longobject.c.h @@ -262,19 +262,20 @@ PyDoc_STRVAR(int_to_bytes__doc__, "Return an array of bytes representing an integer.\n" "\n" " length\n" -" Length of bytes object to use. An OverflowError is raised if the\n" -" integer is not representable with the given number of bytes. Default\n" -" is length 1.\n" +" Length of bytes object to use. An OverflowError is raised if\n" +" the integer is not representable with the given number of bytes.\n" +" Default is length 1.\n" " byteorder\n" -" The byte order used to represent the integer. If byteorder is \'big\',\n" -" the most significant byte is at the beginning of the byte array. If\n" -" byteorder is \'little\', the most significant byte is at the end of the\n" -" byte array. To request the native byte order of the host system, use\n" -" sys.byteorder as the byte order value. Default is to use \'big\'.\n" +" The byte order used to represent the integer. If byteorder is\n" +" \'big\', the most significant byte is at the beginning of the byte\n" +" array. If byteorder is \'little\', the most significant byte is at\n" +" the end of the byte array. To request the native byte order of\n" +" the host system, use sys.byteorder as the byte order value.\n" +" Default is to use \'big\'.\n" " signed\n" -" Determines whether two\'s complement is used to represent the integer.\n" -" If signed is False and a negative integer is given, an OverflowError\n" -" is raised."); +" Determines whether two\'s complement is used to represent the\n" +" integer. If signed is False and a negative integer is given,\n" +" an OverflowError is raised."); #define INT_TO_BYTES_METHODDEF \ {"to_bytes", _PyCFunction_CAST(int_to_bytes), METH_FASTCALL|METH_KEYWORDS, int_to_bytes__doc__}, @@ -340,6 +341,11 @@ int_to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject * goto exit; } length = ival; + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length cannot be negative"); + goto exit; + } } if (!--noptargs) { goto skip_optional_pos; @@ -378,17 +384,19 @@ PyDoc_STRVAR(int_from_bytes__doc__, "\n" " bytes\n" " Holds the array of bytes to convert. The argument must either\n" -" support the buffer protocol or be an iterable object producing bytes.\n" -" Bytes and bytearray are examples of built-in objects that support the\n" -" buffer protocol.\n" +" support the buffer protocol or be an iterable object producing\n" +" bytes. Bytes and bytearray are examples of built-in objects that\n" +" support the buffer protocol.\n" " byteorder\n" -" The byte order used to represent the integer. If byteorder is \'big\',\n" -" the most significant byte is at the beginning of the byte array. If\n" -" byteorder is \'little\', the most significant byte is at the end of the\n" -" byte array. To request the native byte order of the host system, use\n" -" sys.byteorder as the byte order value. Default is to use \'big\'.\n" +" The byte order used to represent the integer. If byteorder is\n" +" \'big\', the most significant byte is at the beginning of the byte\n" +" array. If byteorder is \'little\', the most significant byte is at\n" +" the end of the byte array. To request the native byte order of\n" +" the host system, use sys.byteorder as the byte order value.\n" +" Default is to use \'big\'.\n" " signed\n" -" Indicates whether two\'s complement is used to represent the integer."); +" Indicates whether two\'s complement is used to represent the\n" +" integer."); #define INT_FROM_BYTES_METHODDEF \ {"from_bytes", _PyCFunction_CAST(int_from_bytes), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, int_from_bytes__doc__}, @@ -485,4 +493,4 @@ int_is_integer(PyObject *self, PyObject *Py_UNUSED(ignored)) { return int_is_integer_impl(self); } -/*[clinic end generated code: output=d23f8ce5bdf08a30 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d95766fb7ff46963 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/memoryobject.c.h b/Objects/clinic/memoryobject.c.h index 28cfd1a22080c9..a0cf3243edc08a 100644 --- a/Objects/clinic/memoryobject.c.h +++ b/Objects/clinic/memoryobject.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(memoryview__doc__, @@ -258,11 +259,12 @@ PyDoc_STRVAR(memoryview_tobytes__doc__, "\n" "Return the data in the buffer as a byte string.\n" "\n" -"Order can be {\'C\', \'F\', \'A\'}. When order is \'C\' or \'F\', the data of the\n" -"original array is converted to C or Fortran order. For contiguous views,\n" -"\'A\' returns an exact copy of the physical memory. In particular, in-memory\n" -"Fortran order is preserved. For non-contiguous views, the data is converted\n" -"to C first. order=None is the same as order=\'C\'."); +"Order can be {\'C\', \'F\', \'A\'}. When order is \'C\' or \'F\', the data of\n" +"the original array is converted to C or Fortran order. For\n" +"contiguous views, \'A\' returns an exact copy of the physical memory.\n" +"In particular, in-memory Fortran order is preserved. For\n" +"non-contiguous views, the data is converted to C first. order=None\n" +"is the same as order=\'C\'."); #define MEMORYVIEW_TOBYTES_METHODDEF \ {"tobytes", _PyCFunction_CAST(memoryview_tobytes), METH_FASTCALL|METH_KEYWORDS, memoryview_tobytes__doc__}, @@ -347,8 +349,8 @@ PyDoc_STRVAR(memoryview_hex__doc__, " sep\n" " An optional single character or byte to separate hex bytes.\n" " bytes_per_sep\n" -" How many bytes between separators. Positive values count from the\n" -" right, negative values count from the left.\n" +" How many bytes between separators. Positive values count from\n" +" the right, negative values count from the left.\n" "\n" "Example:\n" ">>> value = memoryview(b\'\\xb9\\x01\\xef\')\n" @@ -366,7 +368,7 @@ PyDoc_STRVAR(memoryview_hex__doc__, static PyObject * memoryview_hex_impl(PyMemoryViewObject *self, PyObject *sep, - int bytes_per_sep); + Py_ssize_t bytes_per_sep); static PyObject * memoryview_hex(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -402,7 +404,7 @@ memoryview_hex(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *argsbuf[2]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; PyObject *sep = NULL; - int bytes_per_sep = 1; + Py_ssize_t bytes_per_sep = 1; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -418,9 +420,17 @@ memoryview_hex(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject goto skip_optional_pos; } } - bytes_per_sep = PyLong_AsInt(args[1]); - if (bytes_per_sep == -1 && PyErr_Occurred()) { - goto exit; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(args[1]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + bytes_per_sep = ival; } skip_optional_pos: return_value = memoryview_hex_impl((PyMemoryViewObject *)self, sep, bytes_per_sep); @@ -496,4 +506,4 @@ memoryview_index(PyObject *self, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=154f4c04263ccb24 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3abf9c80cd49229a input=a9049054013a1b77]*/ diff --git a/Objects/clinic/odictobject.c.h b/Objects/clinic/odictobject.c.h index e71c29a1b268d0..92129e6444810f 100644 --- a/Objects/clinic/odictobject.c.h +++ b/Objects/clinic/odictobject.c.h @@ -6,6 +6,7 @@ preserve # include "pycore_gc.h" // PyGC_Head # include "pycore_runtime.h" // _Py_ID() #endif +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() PyDoc_STRVAR(OrderedDict_fromkeys__doc__, @@ -73,6 +74,53 @@ OrderedDict_fromkeys(PyObject *type, PyObject *const *args, Py_ssize_t nargs, Py return return_value; } +PyDoc_STRVAR(OrderedDict___sizeof____doc__, +"__sizeof__($self, /)\n" +"--\n" +"\n"); + +#define ORDEREDDICT___SIZEOF___METHODDEF \ + {"__sizeof__", (PyCFunction)OrderedDict___sizeof__, METH_NOARGS, OrderedDict___sizeof____doc__}, + +static Py_ssize_t +OrderedDict___sizeof___impl(PyODictObject *self); + +static PyObject * +OrderedDict___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + Py_ssize_t _return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + _return_value = OrderedDict___sizeof___impl((PyODictObject *)self); + Py_END_CRITICAL_SECTION(); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromSsize_t(_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(OrderedDict___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n" +"Return state information for pickling"); + +#define ORDEREDDICT___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)OrderedDict___reduce__, METH_NOARGS, OrderedDict___reduce____doc__}, + +static PyObject * +OrderedDict___reduce___impl(PyODictObject *od); + +static PyObject * +OrderedDict___reduce__(PyObject *od, PyObject *Py_UNUSED(ignored)) +{ + return OrderedDict___reduce___impl((PyODictObject *)od); +} + PyDoc_STRVAR(OrderedDict_setdefault__doc__, "setdefault($self, /, key, default=None)\n" "--\n" @@ -135,7 +183,9 @@ OrderedDict_setdefault(PyObject *self, PyObject *const *args, Py_ssize_t nargs, } default_value = args[1]; skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = OrderedDict_setdefault_impl((PyODictObject *)self, key, default_value); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -204,7 +254,9 @@ OrderedDict_pop(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObjec } default_value = args[1]; skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = OrderedDict_pop_impl((PyODictObject *)self, key, default_value); + Py_END_CRITICAL_SECTION(); exit: return return_value; @@ -216,7 +268,8 @@ PyDoc_STRVAR(OrderedDict_popitem__doc__, "\n" "Remove and return a (key, value) pair from the dictionary.\n" "\n" -"Pairs are returned in LIFO order if last is true or FIFO order if false."); +"Pairs are returned in LIFO order if last is true or FIFO order if\n" +"false."); #define ORDEREDDICT_POPITEM_METHODDEF \ {"popitem", _PyCFunction_CAST(OrderedDict_popitem), METH_FASTCALL|METH_KEYWORDS, OrderedDict_popitem__doc__}, @@ -272,12 +325,62 @@ OrderedDict_popitem(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyO goto exit; } skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = OrderedDict_popitem_impl((PyODictObject *)self, last); + Py_END_CRITICAL_SECTION(); exit: return return_value; } +PyDoc_STRVAR(OrderedDict_clear__doc__, +"clear($self, /)\n" +"--\n" +"\n" +"Remove all items from ordered dict."); + +#define ORDEREDDICT_CLEAR_METHODDEF \ + {"clear", (PyCFunction)OrderedDict_clear, METH_NOARGS, OrderedDict_clear__doc__}, + +static PyObject * +OrderedDict_clear_impl(PyODictObject *self); + +static PyObject * +OrderedDict_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = OrderedDict_clear_impl((PyODictObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(OrderedDict_copy__doc__, +"copy($self, /)\n" +"--\n" +"\n" +"A shallow copy of ordered dict."); + +#define ORDEREDDICT_COPY_METHODDEF \ + {"copy", (PyCFunction)OrderedDict_copy, METH_NOARGS, OrderedDict_copy__doc__}, + +static PyObject * +OrderedDict_copy_impl(PyObject *od); + +static PyObject * +OrderedDict_copy(PyObject *od, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(od); + return_value = OrderedDict_copy_impl(od); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(OrderedDict_move_to_end__doc__, "move_to_end($self, /, key, last=True)\n" "--\n" @@ -342,9 +445,11 @@ OrderedDict_move_to_end(PyObject *self, PyObject *const *args, Py_ssize_t nargs, goto exit; } skip_optional_pos: + Py_BEGIN_CRITICAL_SECTION(self); return_value = OrderedDict_move_to_end_impl((PyODictObject *)self, key, last); + Py_END_CRITICAL_SECTION(); exit: return return_value; } -/*[clinic end generated code: output=7d8206823bb1f419 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=89f7e92de998f9a4 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/rangeobject.c.h b/Objects/clinic/rangeobject.c.h new file mode 100644 index 00000000000000..d9142eddde4b17 --- /dev/null +++ b/Objects/clinic/rangeobject.c.h @@ -0,0 +1,150 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() + +PyDoc_STRVAR(range_iterator___length_hint____doc__, +"__length_hint__($self, /)\n" +"--\n" +"\n" +"Private method returning an estimate of len(list(it))."); + +#define RANGE_ITERATOR___LENGTH_HINT___METHODDEF \ + {"__length_hint__", (PyCFunction)range_iterator___length_hint__, METH_NOARGS, range_iterator___length_hint____doc__}, + +static PyObject * +range_iterator___length_hint___impl(_PyRangeIterObject *r); + +static PyObject * +range_iterator___length_hint__(PyObject *r, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = range_iterator___length_hint___impl((_PyRangeIterObject *)r); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(range_iterator___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n" +"Return state information for pickling."); + +#define RANGE_ITERATOR___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)range_iterator___reduce__, METH_NOARGS, range_iterator___reduce____doc__}, + +static PyObject * +range_iterator___reduce___impl(_PyRangeIterObject *r); + +static PyObject * +range_iterator___reduce__(PyObject *r, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = range_iterator___reduce___impl((_PyRangeIterObject *)r); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(range_iterator___setstate____doc__, +"__setstate__($self, state, /)\n" +"--\n" +"\n" +"Set state information for unpickling."); + +#define RANGE_ITERATOR___SETSTATE___METHODDEF \ + {"__setstate__", (PyCFunction)range_iterator___setstate__, METH_O, range_iterator___setstate____doc__}, + +static PyObject * +range_iterator___setstate___impl(_PyRangeIterObject *r, PyObject *state); + +static PyObject * +range_iterator___setstate__(PyObject *r, PyObject *state) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = range_iterator___setstate___impl((_PyRangeIterObject *)r, state); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(longrange_iterator___length_hint____doc__, +"__length_hint__($self, /)\n" +"--\n" +"\n" +"Private method returning an estimate of len(list(it))."); + +#define LONGRANGE_ITERATOR___LENGTH_HINT___METHODDEF \ + {"__length_hint__", (PyCFunction)longrange_iterator___length_hint__, METH_NOARGS, longrange_iterator___length_hint____doc__}, + +static PyObject * +longrange_iterator___length_hint___impl(longrangeiterobject *r); + +static PyObject * +longrange_iterator___length_hint__(PyObject *r, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = longrange_iterator___length_hint___impl((longrangeiterobject *)r); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(longrange_iterator___reduce____doc__, +"__reduce__($self, /)\n" +"--\n" +"\n" +"Return state information for pickling."); + +#define LONGRANGE_ITERATOR___REDUCE___METHODDEF \ + {"__reduce__", (PyCFunction)longrange_iterator___reduce__, METH_NOARGS, longrange_iterator___reduce____doc__}, + +static PyObject * +longrange_iterator___reduce___impl(longrangeiterobject *r); + +static PyObject * +longrange_iterator___reduce__(PyObject *r, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = longrange_iterator___reduce___impl((longrangeiterobject *)r); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(longrange_iterator___setstate____doc__, +"__setstate__($self, state, /)\n" +"--\n" +"\n" +"Set state information for unpickling."); + +#define LONGRANGE_ITERATOR___SETSTATE___METHODDEF \ + {"__setstate__", (PyCFunction)longrange_iterator___setstate__, METH_O, longrange_iterator___setstate____doc__}, + +static PyObject * +longrange_iterator___setstate___impl(longrangeiterobject *r, PyObject *state); + +static PyObject * +longrange_iterator___setstate__(PyObject *r, PyObject *state) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(r); + return_value = longrange_iterator___setstate___impl((longrangeiterobject *)r, state); + Py_END_CRITICAL_SECTION(); + + return return_value; +} +/*[clinic end generated code: output=719c0e4c81fe0f4a input=a9049054013a1b77]*/ diff --git a/Objects/clinic/sentinelobject.c.h b/Objects/clinic/sentinelobject.c.h new file mode 100644 index 00000000000000..f8503194ae5c74 --- /dev/null +++ b/Objects/clinic/sentinelobject.c.h @@ -0,0 +1,72 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +static PyObject * +sentinel_new_impl(PyTypeObject *type, PyObject *name, PyObject *repr); + +static PyObject * +sentinel_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(repr), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"", "repr", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "sentinel", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[2]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + PyObject *name; + PyObject *repr = Py_None; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!PyUnicode_Check(fastargs[0])) { + _PyArg_BadArgument("sentinel", "argument 1", "str", fastargs[0]); + goto exit; + } + name = fastargs[0]; + if (!noptargs) { + goto skip_optional_kwonly; + } + repr = fastargs[1]; +skip_optional_kwonly: + return_value = sentinel_new_impl(type, name, repr); + +exit: + return return_value; +} +/*[clinic end generated code: output=958842ece254c82f input=a9049054013a1b77]*/ diff --git a/Objects/clinic/setobject.c.h b/Objects/clinic/setobject.c.h index 488be4435f81d7..098c4bcb53d3fb 100644 --- a/Objects/clinic/setobject.c.h +++ b/Objects/clinic/setobject.c.h @@ -425,9 +425,7 @@ set___contains__(PyObject *so, PyObject *key) { PyObject *return_value = NULL; - Py_BEGIN_CRITICAL_SECTION(so); return_value = set___contains___impl((PySetObject *)so, key); - Py_END_CRITICAL_SECTION(); return return_value; } @@ -554,4 +552,4 @@ set___sizeof__(PyObject *so, PyObject *Py_UNUSED(ignored)) return return_value; } -/*[clinic end generated code: output=7f7fe845ca165078 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5800c0bf136a5a0a input=a9049054013a1b77]*/ diff --git a/Objects/clinic/typevarobject.c.h b/Objects/clinic/typevarobject.c.h index 9ae2f51f758403..d2f350a3487f08 100644 --- a/Objects/clinic/typevarobject.c.h +++ b/Objects/clinic/typevarobject.c.h @@ -517,13 +517,15 @@ paramspec_has_default(PyObject *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(typevartuple__doc__, -"typevartuple(name, *, default=typing.NoDefault)\n" +"typevartuple(name, *, bound=None, covariant=False, contravariant=False,\n" +" infer_variance=False, default=typing.NoDefault)\n" "--\n" "\n" "Create a new TypeVarTuple with the given name."); static PyObject * -typevartuple_impl(PyTypeObject *type, PyObject *name, +typevartuple_impl(PyTypeObject *type, PyObject *name, PyObject *bound, + int covariant, int contravariant, int infer_variance, PyObject *default_value); static PyObject * @@ -532,7 +534,7 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 2 + #define NUM_KEYWORDS 6 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD @@ -541,7 +543,7 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs) } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) .ob_hash = -1, - .ob_item = { &_Py_ID(name), &_Py_ID(default), }, + .ob_item = { &_Py_ID(name), &_Py_ID(bound), &_Py_ID(covariant), &_Py_ID(contravariant), &_Py_ID(infer_variance), &_Py_ID(default), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -550,18 +552,22 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs) # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"name", "default", NULL}; + static const char * const _keywords[] = {"name", "bound", "covariant", "contravariant", "infer_variance", "default", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "typevartuple", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[6]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; PyObject *name; + PyObject *bound = Py_None; + int covariant = 0; + int contravariant = 0; + int infer_variance = 0; PyObject *default_value = &_Py_NoDefaultStruct; fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, @@ -577,9 +583,42 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs) if (!noptargs) { goto skip_optional_kwonly; } - default_value = fastargs[1]; + if (fastargs[1]) { + bound = fastargs[1]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[2]) { + covariant = PyObject_IsTrue(fastargs[2]); + if (covariant < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[3]) { + contravariant = PyObject_IsTrue(fastargs[3]); + if (contravariant < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + if (fastargs[4]) { + infer_variance = PyObject_IsTrue(fastargs[4]); + if (infer_variance < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + default_value = fastargs[5]; skip_optional_kwonly: - return_value = typevartuple_impl(type, name, default_value); + return_value = typevartuple_impl(type, name, bound, covariant, contravariant, infer_variance, default_value); exit: return return_value; @@ -688,14 +727,14 @@ typealias_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(typealias_new__doc__, -"typealias(name, value, *, type_params=<unrepresentable>)\n" +"typealias(name, value, *, type_params=<unrepresentable>, qualname=None)\n" "--\n" "\n" "Create a TypeAliasType."); static PyObject * typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value, - PyObject *type_params); + PyObject *type_params, PyObject *qualname); static PyObject * typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) @@ -703,7 +742,7 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 3 + #define NUM_KEYWORDS 4 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD @@ -712,7 +751,7 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) .ob_hash = -1, - .ob_item = { &_Py_ID(name), &_Py_ID(value), &_Py_ID(type_params), }, + .ob_item = { &_Py_ID(name), &_Py_ID(value), &_Py_ID(type_params), &_Py_ID(qualname), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -721,20 +760,21 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"name", "value", "type_params", NULL}; + static const char * const _keywords[] = {"name", "value", "type_params", "qualname", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "typealias", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[4]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 2; PyObject *name; PyObject *value; PyObject *type_params = NULL; + PyObject *qualname = NULL; fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, /*minpos*/ 2, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); @@ -750,11 +790,17 @@ typealias_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) if (!noptargs) { goto skip_optional_kwonly; } - type_params = fastargs[2]; + if (fastargs[2]) { + type_params = fastargs[2]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + qualname = fastargs[3]; skip_optional_kwonly: - return_value = typealias_new_impl(type, name, value, type_params); + return_value = typealias_new_impl(type, name, value, type_params, qualname); exit: return return_value; } -/*[clinic end generated code: output=9dad71445e079303 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2e7dd170924d92e5 input=a9049054013a1b77]*/ diff --git a/Objects/clinic/unicodeobject.c.h b/Objects/clinic/unicodeobject.c.h index 1819fbaea220a3..d0753b38843fcc 100644 --- a/Objects/clinic/unicodeobject.c.h +++ b/Objects/clinic/unicodeobject.c.h @@ -33,8 +33,8 @@ PyDoc_STRVAR(unicode_title__doc__, "\n" "Return a version of the string where each word is titlecased.\n" "\n" -"More specifically, words start with uppercased characters and all remaining\n" -"cased characters have lower case."); +"More specifically, words start with uppercased characters and all\n" +"remaining cased characters have lower case."); #define UNICODE_TITLE_METHODDEF \ {"title", (PyCFunction)unicode_title, METH_NOARGS, unicode_title__doc__}, @@ -54,8 +54,8 @@ PyDoc_STRVAR(unicode_capitalize__doc__, "\n" "Return a capitalized version of the string.\n" "\n" -"More specifically, make the first character have upper case and the rest lower\n" -"case."); +"More specifically, make the first character have upper case and the\n" +"rest lower case."); #define UNICODE_CAPITALIZE_METHODDEF \ {"capitalize", (PyCFunction)unicode_capitalize, METH_NOARGS, unicode_capitalize__doc__}, @@ -93,7 +93,8 @@ PyDoc_STRVAR(unicode_center__doc__, "\n" "Return a centered string of length width.\n" "\n" -"Padding is done using the specified fill character (default is a space)."); +"Padding is done using the specified fill character (default is\n" +"a space)."); #define UNICODE_CENTER_METHODDEF \ {"center", _PyCFunction_CAST(unicode_center), METH_FASTCALL, unicode_center__doc__}, @@ -142,7 +143,8 @@ PyDoc_STRVAR(unicode_count__doc__, "\n" "Return the number of non-overlapping occurrences of substring sub in string S[start:end].\n" "\n" -"Optional arguments start and end are interpreted as in slice notation."); +"Optional arguments start and end are interpreted as in slice\n" +"notation."); #define UNICODE_COUNT_METHODDEF \ {"count", _PyCFunction_CAST(unicode_count), METH_FASTCALL, unicode_count__doc__}, @@ -202,8 +204,8 @@ PyDoc_STRVAR(unicode_encode__doc__, " errors\n" " The error handling scheme to use for encoding errors.\n" " The default is \'strict\' meaning that encoding errors raise a\n" -" UnicodeEncodeError. Other possible values are \'ignore\', \'replace\' and\n" -" \'xmlcharrefreplace\' as well as any other name registered with\n" +" UnicodeEncodeError. Other possible values are \'ignore\', \'replace\'\n" +" and \'xmlcharrefreplace\' as well as any other name registered with\n" " codecs.register_error that can handle UnicodeEncodeErrors."); #define UNICODE_ENCODE_METHODDEF \ @@ -368,8 +370,8 @@ PyDoc_STRVAR(unicode_find__doc__, "\n" "Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end].\n" "\n" -"Optional arguments start and end are interpreted as in slice notation.\n" -"Return -1 on failure."); +"Optional arguments start and end are interpreted as in slice\n" +"notation. Return -1 on failure."); #define UNICODE_FIND_METHODDEF \ {"find", _PyCFunction_CAST(unicode_find), METH_FASTCALL, unicode_find__doc__}, @@ -424,8 +426,8 @@ PyDoc_STRVAR(unicode_index__doc__, "\n" "Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end].\n" "\n" -"Optional arguments start and end are interpreted as in slice notation.\n" -"Raises ValueError when the substring is not found."); +"Optional arguments start and end are interpreted as in slice\n" +"notation. Raises ValueError when the substring is not found."); #define UNICODE_INDEX_METHODDEF \ {"index", _PyCFunction_CAST(unicode_index), METH_FASTCALL, unicode_index__doc__}, @@ -501,8 +503,8 @@ PyDoc_STRVAR(unicode_islower__doc__, "\n" "Return True if the string is a lowercase string, False otherwise.\n" "\n" -"A string is lowercase if all cased characters in the string are lowercase and\n" -"there is at least one cased character in the string."); +"A string is lowercase if all cased characters in the string are\n" +"lowercase and there is at least one cased character in the string."); #define UNICODE_ISLOWER_METHODDEF \ {"islower", (PyCFunction)unicode_islower, METH_NOARGS, unicode_islower__doc__}, @@ -522,8 +524,8 @@ PyDoc_STRVAR(unicode_isupper__doc__, "\n" "Return True if the string is an uppercase string, False otherwise.\n" "\n" -"A string is uppercase if all cased characters in the string are uppercase and\n" -"there is at least one cased character in the string."); +"A string is uppercase if all cased characters in the string are\n" +"uppercase and there is at least one cased character in the string."); #define UNICODE_ISUPPER_METHODDEF \ {"isupper", (PyCFunction)unicode_isupper, METH_NOARGS, unicode_isupper__doc__}, @@ -564,8 +566,8 @@ PyDoc_STRVAR(unicode_isspace__doc__, "\n" "Return True if the string is a whitespace string, False otherwise.\n" "\n" -"A string is whitespace if all characters in the string are whitespace and there\n" -"is at least one character in the string."); +"A string is whitespace if all characters in the string are\n" +"whitespace and there is at least one character in the string."); #define UNICODE_ISSPACE_METHODDEF \ {"isspace", (PyCFunction)unicode_isspace, METH_NOARGS, unicode_isspace__doc__}, @@ -585,8 +587,8 @@ PyDoc_STRVAR(unicode_isalpha__doc__, "\n" "Return True if the string is an alphabetic string, False otherwise.\n" "\n" -"A string is alphabetic if all characters in the string are alphabetic and there\n" -"is at least one character in the string."); +"A string is alphabetic if all characters in the string are\n" +"alphabetic and there is at least one character in the string."); #define UNICODE_ISALPHA_METHODDEF \ {"isalpha", (PyCFunction)unicode_isalpha, METH_NOARGS, unicode_isalpha__doc__}, @@ -606,8 +608,8 @@ PyDoc_STRVAR(unicode_isalnum__doc__, "\n" "Return True if the string is an alpha-numeric string, False otherwise.\n" "\n" -"A string is alpha-numeric if all characters in the string are alpha-numeric and\n" -"there is at least one character in the string."); +"A string is alpha-numeric if all characters in the string are\n" +"alpha-numeric and there is at least one character in the string."); #define UNICODE_ISALNUM_METHODDEF \ {"isalnum", (PyCFunction)unicode_isalnum, METH_NOARGS, unicode_isalnum__doc__}, @@ -627,8 +629,8 @@ PyDoc_STRVAR(unicode_isdecimal__doc__, "\n" "Return True if the string is a decimal string, False otherwise.\n" "\n" -"A string is a decimal string if all characters in the string are decimal and\n" -"there is at least one character in the string."); +"A string is a decimal string if all characters in the string are\n" +"decimal and there is at least one character in the string."); #define UNICODE_ISDECIMAL_METHODDEF \ {"isdecimal", (PyCFunction)unicode_isdecimal, METH_NOARGS, unicode_isdecimal__doc__}, @@ -648,8 +650,8 @@ PyDoc_STRVAR(unicode_isdigit__doc__, "\n" "Return True if the string is a digit string, False otherwise.\n" "\n" -"A string is a digit string if all characters in the string are digits and there\n" -"is at least one character in the string."); +"A string is a digit string if all characters in the string are\n" +"digits and there is at least one character in the string."); #define UNICODE_ISDIGIT_METHODDEF \ {"isdigit", (PyCFunction)unicode_isdigit, METH_NOARGS, unicode_isdigit__doc__}, @@ -669,8 +671,8 @@ PyDoc_STRVAR(unicode_isnumeric__doc__, "\n" "Return True if the string is a numeric string, False otherwise.\n" "\n" -"A string is numeric if all characters in the string are numeric and there is at\n" -"least one character in the string."); +"A string is numeric if all characters in the string are numeric and\n" +"there is at least one character in the string."); #define UNICODE_ISNUMERIC_METHODDEF \ {"isnumeric", (PyCFunction)unicode_isnumeric, METH_NOARGS, unicode_isnumeric__doc__}, @@ -690,8 +692,8 @@ PyDoc_STRVAR(unicode_isidentifier__doc__, "\n" "Return True if the string is a valid Python identifier, False otherwise.\n" "\n" -"Call keyword.iskeyword(s) to test whether string s is a reserved identifier,\n" -"such as \"def\" or \"class\"."); +"Call keyword.iskeyword(s) to test whether string s is a reserved\n" +"identifier, such as \"def\" or \"class\"."); #define UNICODE_ISIDENTIFIER_METHODDEF \ {"isidentifier", (PyCFunction)unicode_isidentifier, METH_NOARGS, unicode_isidentifier__doc__}, @@ -731,8 +733,8 @@ PyDoc_STRVAR(unicode_join__doc__, "\n" "Concatenate any number of strings.\n" "\n" -"The string whose method is called is inserted in between each given string.\n" -"The result is returned as a new string.\n" +"The string whose method is called is inserted in between each given\n" +"string. The result is returned as a new string.\n" "\n" "Example: \'.\'.join([\'ab\', \'pq\', \'rs\']) -> \'ab.pq.rs\'"); @@ -745,7 +747,8 @@ PyDoc_STRVAR(unicode_ljust__doc__, "\n" "Return a left-justified string of length width.\n" "\n" -"Padding is done using the specified fill character (default is a space)."); +"Padding is done using the specified fill character (default is\n" +"a space)."); #define UNICODE_LJUST_METHODDEF \ {"ljust", _PyCFunction_CAST(unicode_ljust), METH_FASTCALL, unicode_ljust__doc__}, @@ -918,8 +921,8 @@ PyDoc_STRVAR(unicode_replace__doc__, " Maximum number of occurrences to replace.\n" " -1 (the default value) means replace all occurrences.\n" "\n" -"If the optional argument count is given, only the first count occurrences are\n" -"replaced."); +"If count is given, only the first count occurrences are replaced.\n" +"If count is not specified or -1, then all occurrences are replaced."); #define UNICODE_REPLACE_METHODDEF \ {"replace", _PyCFunction_CAST(unicode_replace), METH_FASTCALL|METH_KEYWORDS, unicode_replace__doc__}, @@ -1008,8 +1011,9 @@ PyDoc_STRVAR(unicode_removeprefix__doc__, "\n" "Return a str with the given prefix string removed if present.\n" "\n" -"If the string starts with the prefix string, return string[len(prefix):].\n" -"Otherwise, return a copy of the original string."); +"If the string starts with the prefix string, return\n" +"string[len(prefix):]. Otherwise, return a copy of the original\n" +"string."); #define UNICODE_REMOVEPREFIX_METHODDEF \ {"removeprefix", (PyCFunction)unicode_removeprefix, METH_O, unicode_removeprefix__doc__}, @@ -1040,9 +1044,9 @@ PyDoc_STRVAR(unicode_removesuffix__doc__, "\n" "Return a str with the given suffix string removed if present.\n" "\n" -"If the string ends with the suffix string and that suffix is not empty,\n" -"return string[:-len(suffix)]. Otherwise, return a copy of the original\n" -"string."); +"If the string ends with the suffix string and that suffix is not\n" +"empty, return string[:-len(suffix)]. Otherwise, return a copy of\n" +"the original string."); #define UNICODE_REMOVESUFFIX_METHODDEF \ {"removesuffix", (PyCFunction)unicode_removesuffix, METH_O, unicode_removesuffix__doc__}, @@ -1073,8 +1077,8 @@ PyDoc_STRVAR(unicode_rfind__doc__, "\n" "Return the highest index in S where substring sub is found, such that sub is contained within S[start:end].\n" "\n" -"Optional arguments start and end are interpreted as in slice notation.\n" -"Return -1 on failure."); +"Optional arguments start and end are interpreted as in slice\n" +"notation. Return -1 on failure."); #define UNICODE_RFIND_METHODDEF \ {"rfind", _PyCFunction_CAST(unicode_rfind), METH_FASTCALL, unicode_rfind__doc__}, @@ -1129,8 +1133,8 @@ PyDoc_STRVAR(unicode_rindex__doc__, "\n" "Return the highest index in S where substring sub is found, such that sub is contained within S[start:end].\n" "\n" -"Optional arguments start and end are interpreted as in slice notation.\n" -"Raises ValueError when the substring is not found."); +"Optional arguments start and end are interpreted as in slice\n" +"notation. Raises ValueError when the substring is not found."); #define UNICODE_RINDEX_METHODDEF \ {"rindex", _PyCFunction_CAST(unicode_rindex), METH_FASTCALL, unicode_rindex__doc__}, @@ -1185,7 +1189,8 @@ PyDoc_STRVAR(unicode_rjust__doc__, "\n" "Return a right-justified string of length width.\n" "\n" -"Padding is done using the specified fill character (default is a space)."); +"Padding is done using the specified fill character (default is\n" +"a space)."); #define UNICODE_RJUST_METHODDEF \ {"rjust", _PyCFunction_CAST(unicode_rjust), METH_FASTCALL, unicode_rjust__doc__}, @@ -1237,18 +1242,18 @@ PyDoc_STRVAR(unicode_split__doc__, " sep\n" " The separator used to split the string.\n" "\n" -" When set to None (the default value), will split on any whitespace\n" -" character (including \\n \\r \\t \\f and spaces) and will discard\n" -" empty strings from the result.\n" +" When set to None (the default value), will split on any\n" +" whitespace character (including \\n \\r \\t \\f and spaces) and\n" +" will discard empty strings from the result.\n" " maxsplit\n" " Maximum number of splits.\n" " -1 (the default value) means no limit.\n" "\n" "Splitting starts at the front of the string and works to the end.\n" "\n" -"Note, str.split() is mainly useful for data that has been intentionally\n" -"delimited. With natural text that includes punctuation, consider using\n" -"the regular expression module."); +"Note, str.split() is mainly useful for data that has been\n" +"intentionally delimited. With natural text that includes\n" +"punctuation, consider using the regular expression module."); #define UNICODE_SPLIT_METHODDEF \ {"split", _PyCFunction_CAST(unicode_split), METH_FASTCALL|METH_KEYWORDS, unicode_split__doc__}, @@ -1331,12 +1336,12 @@ PyDoc_STRVAR(unicode_partition__doc__, "\n" "Partition the string into three parts using the given separator.\n" "\n" -"This will search for the separator in the string. If the separator is found,\n" -"returns a 3-tuple containing the part before the separator, the separator\n" -"itself, and the part after it.\n" +"This will search for the separator in the string. If the separator\n" +"is found, returns a 3-tuple containing the part before the\n" +"separator, the separator itself, and the part after it.\n" "\n" -"If the separator is not found, returns a 3-tuple containing the original string\n" -"and two empty strings."); +"If the separator is not found, returns a 3-tuple containing\n" +"the original string and two empty strings."); #define UNICODE_PARTITION_METHODDEF \ {"partition", (PyCFunction)unicode_partition, METH_O, unicode_partition__doc__}, @@ -1347,12 +1352,13 @@ PyDoc_STRVAR(unicode_rpartition__doc__, "\n" "Partition the string into three parts using the given separator.\n" "\n" -"This will search for the separator in the string, starting at the end. If\n" -"the separator is found, returns a 3-tuple containing the part before the\n" -"separator, the separator itself, and the part after it.\n" +"This will search for the separator in the string, starting at the\n" +"end. If the separator is found, returns a 3-tuple containing the\n" +"part before the separator, the separator itself, and the part after\n" +"it.\n" "\n" -"If the separator is not found, returns a 3-tuple containing two empty strings\n" -"and the original string."); +"If the separator is not found, returns a 3-tuple containing two\n" +"empty strings and the original string."); #define UNICODE_RPARTITION_METHODDEF \ {"rpartition", (PyCFunction)unicode_rpartition, METH_O, unicode_rpartition__doc__}, @@ -1366,9 +1372,9 @@ PyDoc_STRVAR(unicode_rsplit__doc__, " sep\n" " The separator used to split the string.\n" "\n" -" When set to None (the default value), will split on any whitespace\n" -" character (including \\n \\r \\t \\f and spaces) and will discard\n" -" empty strings from the result.\n" +" When set to None (the default value), will split on any\n" +" whitespace character (including \\n \\r \\t \\f and spaces) and\n" +" will discard empty strings from the result.\n" " maxsplit\n" " Maximum number of splits.\n" " -1 (the default value) means no limit.\n" @@ -1456,8 +1462,8 @@ PyDoc_STRVAR(unicode_splitlines__doc__, "\n" "Return a list of the lines in the string, breaking at line boundaries.\n" "\n" -"Line breaks are not included in the resulting list unless keepends is given and\n" -"true."); +"Line breaks are not included in the resulting list unless keepends\n" +"is given and true."); #define UNICODE_SPLITLINES_METHODDEF \ {"splitlines", _PyCFunction_CAST(unicode_splitlines), METH_FASTCALL|METH_KEYWORDS, unicode_splitlines__doc__}, @@ -1543,13 +1549,14 @@ PyDoc_STRVAR(unicode_maketrans__doc__, "\n" "Return a translation table usable for str.translate().\n" "\n" -"If there is only one argument, it must be a dictionary mapping Unicode\n" -"ordinals (integers) or characters to Unicode ordinals, strings or None.\n" -"Character keys will be then converted to ordinals.\n" -"If there are two arguments, they must be strings of equal length, and\n" -"in the resulting dictionary, each character in x will be mapped to the\n" -"character at the same position in y. If there is a third argument, it\n" -"must be a string, whose characters will be mapped to None in the result."); +"If there is only one argument, it must be a dictionary mapping\n" +"Unicode ordinals (integers) or characters to Unicode ordinals,\n" +"strings or None. Character keys will be then converted to ordinals.\n" +"If there are two arguments, they must be strings of equal length,\n" +"and in the resulting dictionary, each character in x will be mapped\n" +"to the character at the same position in y. If there is a third\n" +"argument, it must be a string, whose characters will be mapped to\n" +"None in the result."); #define UNICODE_MAKETRANS_METHODDEF \ {"maketrans", _PyCFunction_CAST(unicode_maketrans), METH_FASTCALL|METH_STATIC, unicode_maketrans__doc__}, @@ -1599,12 +1606,13 @@ PyDoc_STRVAR(unicode_translate__doc__, "Replace each character in the string using the given translation table.\n" "\n" " table\n" -" Translation table, which must be a mapping of Unicode ordinals to\n" -" Unicode ordinals, strings, or None.\n" +" Translation table, which must be a mapping of Unicode ordinals\n" +" to Unicode ordinals, strings, or None.\n" "\n" -"The table must implement lookup/indexing via __getitem__, for instance a\n" -"dictionary or list. If this operation raises LookupError, the character is\n" -"left untouched. Characters mapped to None are deleted."); +"The table must implement lookup/indexing via __getitem__, for\n" +"instance a dictionary or list. If this operation raises\n" +"LookupError, the character is left untouched. Characters mapped to\n" +"None are deleted."); #define UNICODE_TRANSLATE_METHODDEF \ {"translate", (PyCFunction)unicode_translate, METH_O, unicode_translate__doc__}, @@ -1908,4 +1916,4 @@ unicode_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=238917fe66120bde input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9d243c63e951e31d input=a9049054013a1b77]*/ diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 42e021679b583f..4ede8de6e8adc5 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -501,7 +501,7 @@ _PyCode_Validate(struct _PyCodeConstructor *con) } extern void -_PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters); +_PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters, int flags); #ifdef Py_GIL_DISABLED static _PyCodeArray * _PyCodeArray_New(Py_ssize_t size); @@ -550,16 +550,12 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE; co->co_ncellvars = ncellvars; co->co_nfreevars = nfreevars; -#ifdef Py_GIL_DISABLED - PyMutex_Lock(&interp->func_state.mutex); -#endif + FT_MUTEX_LOCK(&interp->func_state.mutex); co->co_version = interp->func_state.next_version; if (interp->func_state.next_version != 0) { interp->func_state.next_version++; } -#ifdef Py_GIL_DISABLED - PyMutex_Unlock(&interp->func_state.mutex); -#endif + FT_MUTEX_UNLOCK(&interp->func_state.mutex); co->_co_monitoring = NULL; co->_co_instrumentation_version = 0; /* not set */ @@ -578,15 +574,21 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con) co->co_tlbc->entries[0] = co->co_code_adaptive; #endif int entry_point = 0; - while (entry_point < Py_SIZE(co) && - _PyCode_CODE(co)[entry_point].op.code != RESUME) { + while (entry_point < Py_SIZE(co)) { + if (_PyCode_CODE(co)[entry_point].op.code == RESUME && + (_PyCode_CODE(co)[entry_point].op.arg & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_GEN_EXPR_START + ) { + break; + } entry_point++; } co->_co_firsttraceable = entry_point; + #ifdef Py_GIL_DISABLED - _PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), interp->config.tlbc_enabled); + int enable_counters = interp->config.tlbc_enabled && interp->opt_config.specialization_enabled; + _PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), enable_counters, co->co_flags); #else - _PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), 1); + _PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), interp->opt_config.specialization_enabled, co->co_flags); #endif notify_code_watchers(PY_CODE_EVENT_CREATE, co); return 0; @@ -689,7 +691,7 @@ intern_code_constants(struct _PyCodeConstructor *con) #ifdef Py_GIL_DISABLED PyInterpreterState *interp = _PyInterpreterState_GET(); struct _py_code_state *state = &interp->code_state; - PyMutex_Lock(&state->mutex); + FT_MUTEX_LOCK(&state->mutex); #endif if (intern_strings(con->names) < 0) { goto error; @@ -700,15 +702,11 @@ intern_code_constants(struct _PyCodeConstructor *con) if (intern_strings(con->localsplusnames) < 0) { goto error; } -#ifdef Py_GIL_DISABLED - PyMutex_Unlock(&state->mutex); -#endif + FT_MUTEX_UNLOCK(&state->mutex); return 0; error: -#ifdef Py_GIL_DISABLED - PyMutex_Unlock(&state->mutex); -#endif + FT_MUTEX_UNLOCK(&state->mutex); return -1; } @@ -938,8 +936,9 @@ PyUnstable_Code_New(int argcount, int kwonlyargcount, // NOTE: When modifying the construction of PyCode_NewEmpty, please also change // test.test_code.CodeLocationTest.test_code_new_empty to keep it in sync! -static const uint8_t assert0[6] = { +static const uint8_t assert0[8] = { RESUME, RESUME_AT_FUNC_START, + CACHE, 0, LOAD_COMMON_CONSTANT, CONSTANT_ASSERTIONERROR, RAISE_VARARGS, 1 }; @@ -947,7 +946,7 @@ static const uint8_t assert0[6] = { static const uint8_t linetable[2] = { (1 << 7) // New entry. | (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3) - | (3 - 1), // Three code units. + | (4 - 1), // Four code units. 0, // Offset from co_firstlineno. }; @@ -973,7 +972,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) if (filename_ob == NULL) { goto failed; } - code_ob = PyBytes_FromStringAndSize((const char *)assert0, 6); + code_ob = PyBytes_FromStringAndSize((const char *)assert0, 8); if (code_ob == NULL) { goto failed; } @@ -1019,8 +1018,12 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) if (addrq < 0) { return co->co_firstlineno; } - if (co->_co_monitoring && co->_co_monitoring->lines) { - return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); + _PyCoMonitoringData *data = _Py_atomic_load_ptr_acquire(&co->_co_monitoring); + if (data) { + _PyCoLineInstrumentationData *lines = _Py_atomic_load_ptr_acquire(&data->lines); + if (lines) { + return _Py_Instrumentation_GetLine(co, lines, addrq/sizeof(_Py_CODEUNIT)); + } } assert(addrq >= 0 && addrq < _PyCode_NBYTES(co)); PyCodeAddressRange bounds; @@ -1028,6 +1031,23 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) return _PyCode_CheckLineNumber(addrq, &bounds); } +int +_PyCode_SafeAddr2Line(PyCodeObject *co, int addrq) +{ + if (addrq < 0) { + return co->co_firstlineno; + } + if (co->_co_monitoring && co->_co_monitoring->lines) { + return _Py_Instrumentation_GetLine(co, co->_co_monitoring->lines, addrq/sizeof(_Py_CODEUNIT)); + } + if (!(addrq >= 0 && addrq < _PyCode_NBYTES(co))) { + return -1; + } + PyCodeAddressRange bounds; + _PyCode_InitAddressRange(co, &bounds); + return _PyCode_CheckLineNumber(addrq, &bounds); +} + void _PyLineTable_InitAddressRange(const char *linetable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) { @@ -1276,77 +1296,6 @@ _PyLineTable_NextAddressRange(PyCodeAddressRange *range) return 1; } -static int -emit_pair(PyObject **bytes, int *offset, int a, int b) -{ - Py_ssize_t len = PyBytes_GET_SIZE(*bytes); - if (*offset + 2 >= len) { - if (_PyBytes_Resize(bytes, len * 2) < 0) - return 0; - } - unsigned char *lnotab = (unsigned char *) PyBytes_AS_STRING(*bytes); - lnotab += *offset; - *lnotab++ = a; - *lnotab++ = b; - *offset += 2; - return 1; -} - -static int -emit_delta(PyObject **bytes, int bdelta, int ldelta, int *offset) -{ - while (bdelta > 255) { - if (!emit_pair(bytes, offset, 255, 0)) { - return 0; - } - bdelta -= 255; - } - while (ldelta > 127) { - if (!emit_pair(bytes, offset, bdelta, 127)) { - return 0; - } - bdelta = 0; - ldelta -= 127; - } - while (ldelta < -128) { - if (!emit_pair(bytes, offset, bdelta, -128)) { - return 0; - } - bdelta = 0; - ldelta += 128; - } - return emit_pair(bytes, offset, bdelta, ldelta); -} - -static PyObject * -decode_linetable(PyCodeObject *code) -{ - PyCodeAddressRange bounds; - PyObject *bytes; - int table_offset = 0; - int code_offset = 0; - int line = code->co_firstlineno; - bytes = PyBytes_FromStringAndSize(NULL, 64); - if (bytes == NULL) { - return NULL; - } - _PyCode_InitAddressRange(code, &bounds); - while (_PyLineTable_NextAddressRange(&bounds)) { - if (bounds.opaque.computed_line != line) { - int bdelta = bounds.ar_start - code_offset; - int ldelta = bounds.opaque.computed_line - line; - if (!emit_delta(&bytes, bdelta, ldelta, &table_offset)) { - Py_DECREF(bytes); - return NULL; - } - code_offset = bounds.ar_start; - line = bounds.opaque.computed_line; - } - } - _PyBytes_Resize(&bytes, table_offset); - return bytes; -} - typedef struct { PyObject_HEAD @@ -1555,6 +1504,67 @@ typedef struct { } _PyCodeObjectExtra; +static inline size_t +code_extra_size(Py_ssize_t n) +{ + return sizeof(_PyCodeObjectExtra) + (n - 1) * sizeof(void *); +} + +#ifdef Py_GIL_DISABLED +static int +code_extra_grow_ft(PyCodeObject *co, _PyCodeObjectExtra *old_co_extra, + Py_ssize_t old_ce_size, Py_ssize_t new_ce_size, + Py_ssize_t index, void *extra) +{ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(co); + _PyCodeObjectExtra *new_co_extra = PyMem_Malloc( + code_extra_size(new_ce_size)); + if (new_co_extra == NULL) { + PyErr_NoMemory(); + return -1; + } + + if (old_ce_size > 0) { + memcpy(new_co_extra->ce_extras, old_co_extra->ce_extras, + old_ce_size * sizeof(void *)); + } + for (Py_ssize_t i = old_ce_size; i < new_ce_size; i++) { + new_co_extra->ce_extras[i] = NULL; + } + new_co_extra->ce_size = new_ce_size; + new_co_extra->ce_extras[index] = extra; + + // Publish new buffer and its contents to lock-free readers. + FT_ATOMIC_STORE_PTR_RELEASE(co->co_extra, new_co_extra); + if (old_co_extra != NULL) { + // QSBR: defer old-buffer free until lock-free readers quiesce. + _PyMem_FreeDelayed(old_co_extra, code_extra_size(old_ce_size)); + } + return 0; +} +#else +static int +code_extra_grow_gil(PyCodeObject *co, _PyCodeObjectExtra *old_co_extra, + Py_ssize_t old_ce_size, Py_ssize_t new_ce_size, + Py_ssize_t index, void *extra) +{ + _PyCodeObjectExtra *new_co_extra = PyMem_Realloc( + old_co_extra, code_extra_size(new_ce_size)); + if (new_co_extra == NULL) { + PyErr_NoMemory(); + return -1; + } + + for (Py_ssize_t i = old_ce_size; i < new_ce_size; i++) { + new_co_extra->ce_extras[i] = NULL; + } + new_co_extra->ce_size = new_ce_size; + new_co_extra->ce_extras[index] = extra; + co->co_extra = new_co_extra; + return 0; +} +#endif + int PyUnstable_Code_GetExtra(PyObject *code, Py_ssize_t index, void **extra) { @@ -1563,15 +1573,19 @@ PyUnstable_Code_GetExtra(PyObject *code, Py_ssize_t index, void **extra) return -1; } - PyCodeObject *o = (PyCodeObject*) code; - _PyCodeObjectExtra *co_extra = (_PyCodeObjectExtra*) o->co_extra; + PyCodeObject *co = (PyCodeObject *)code; + *extra = NULL; - if (co_extra == NULL || index < 0 || co_extra->ce_size <= index) { - *extra = NULL; + if (index < 0) { return 0; } - *extra = co_extra->ce_extras[index]; + // Lock-free read; pairs with release stores in SetExtra. + _PyCodeObjectExtra *co_extra = FT_ATOMIC_LOAD_PTR_ACQUIRE(co->co_extra); + if (co_extra != NULL && index < co_extra->ce_size) { + *extra = FT_ATOMIC_LOAD_PTR_ACQUIRE(co_extra->ce_extras[index]); + } + return 0; } @@ -1581,40 +1595,59 @@ PyUnstable_Code_SetExtra(PyObject *code, Py_ssize_t index, void *extra) { PyInterpreterState *interp = _PyInterpreterState_GET(); - if (!PyCode_Check(code) || index < 0 || - index >= interp->co_extra_user_count) { + // co_extra_user_count is monotonically increasing and published with + // release store in RequestCodeExtraIndex, so once an index is valid + // it stays valid. + Py_ssize_t user_count = FT_ATOMIC_LOAD_SSIZE_ACQUIRE( + interp->co_extra_user_count); + + if (!PyCode_Check(code) || index < 0 || index >= user_count) { PyErr_BadInternalCall(); return -1; } - PyCodeObject *o = (PyCodeObject*) code; - _PyCodeObjectExtra *co_extra = (_PyCodeObjectExtra *) o->co_extra; + PyCodeObject *co = (PyCodeObject *)code; + int result = 0; + void *old_slot_value = NULL; - if (co_extra == NULL || co_extra->ce_size <= index) { - Py_ssize_t i = (co_extra == NULL ? 0 : co_extra->ce_size); - co_extra = PyMem_Realloc( - co_extra, - sizeof(_PyCodeObjectExtra) + - (interp->co_extra_user_count-1) * sizeof(void*)); - if (co_extra == NULL) { - return -1; - } - for (; i < interp->co_extra_user_count; i++) { - co_extra->ce_extras[i] = NULL; - } - co_extra->ce_size = interp->co_extra_user_count; - o->co_extra = co_extra; + Py_BEGIN_CRITICAL_SECTION(co); + + _PyCodeObjectExtra *old_co_extra = (_PyCodeObjectExtra *)co->co_extra; + Py_ssize_t old_ce_size = (old_co_extra == NULL) + ? 0 : old_co_extra->ce_size; + + // Fast path: slot already exists, update in place. + if (index < old_ce_size) { + old_slot_value = old_co_extra->ce_extras[index]; + FT_ATOMIC_STORE_PTR_RELEASE(old_co_extra->ce_extras[index], extra); + goto done; } - if (co_extra->ce_extras[index] != NULL) { - freefunc free = interp->co_extra_freefuncs[index]; - if (free != NULL) { - free(co_extra->ce_extras[index]); + // Slow path: buffer needs to grow. + Py_ssize_t new_ce_size = user_count; +#ifdef Py_GIL_DISABLED + // FT build: allocate new buffer and swap; QSBR reclaims the old one. + result = code_extra_grow_ft( + co, old_co_extra, old_ce_size, new_ce_size, index, extra); +#else + // GIL build: grow with realloc. + result = code_extra_grow_gil( + co, old_co_extra, old_ce_size, new_ce_size, index, extra); +#endif + +done:; + Py_END_CRITICAL_SECTION(); + if (old_slot_value != NULL) { + // Free the old slot value if a free function was registered. + // The caller must ensure no other thread can still access the old + // value after this overwrite. + freefunc free_extra = interp->co_extra_freefuncs[index]; + if (free_extra != NULL) { + free_extra(old_slot_value); } } - co_extra->ce_extras[index] = extra; - return 0; + return result; } @@ -1726,7 +1759,7 @@ identify_unbound_names(PyThreadState *tstate, PyCodeObject *co, assert(attrnames != NULL); assert(PySet_Check(attrnames)); assert(PySet_GET_SIZE(attrnames) == 0 || counts != NULL); - assert(globalsns == NULL || PyDict_Check(globalsns)); + assert(globalsns == NULL || PyAnyDict_Check(globalsns)); assert(builtinsns == NULL || PyDict_Check(builtinsns)); assert(counts == NULL || counts->total == 0); struct co_unbound_counts unbound = {0}; @@ -2095,10 +2128,6 @@ code_returns_only_none(PyCodeObject *co) int len = (int)Py_SIZE(co); assert(len > 0); - // The last instruction either returns or raises. We can take advantage - // of that for a quick exit. - _Py_CODEUNIT final = _Py_GetBaseCodeUnit(co, len-1); - // Look up None in co_consts. Py_ssize_t nconsts = PyTuple_Size(co->co_consts); int none_index = 0; @@ -2107,45 +2136,25 @@ code_returns_only_none(PyCodeObject *co) break; } } - if (none_index == nconsts) { - // None wasn't there, which means there was no implicit return, - // "return", or "return None". - - // That means there must be - // an explicit return (non-None), or it only raises. - if (IS_RETURN_OPCODE(final.op.code)) { - // It was an explicit return (non-None). - return 0; + /* We don't worry about EXTENDED_ARG for now. */ + for (int i = 0; i < len; i += _PyInstruction_GetLength(co, i)) { + _Py_CODEUNIT inst = _Py_GetBaseCodeUnit(co, i); + if (!IS_RETURN_OPCODE(inst.op.code)) { + continue; } - // It must end with a raise then. We still have to walk the - // bytecode to see if there's any explicit return (non-None). - assert(IS_RAISE_OPCODE(final.op.code)); - for (int i = 0; i < len; i += _PyInstruction_GetLength(co, i)) { - _Py_CODEUNIT inst = _Py_GetBaseCodeUnit(co, i); - if (IS_RETURN_OPCODE(inst.op.code)) { - // We alraedy know it isn't returning None. - return 0; - } + assert(i != 0); + _Py_CODEUNIT prev = _Py_GetBaseCodeUnit(co, i-1); + if (prev.op.code == LOAD_COMMON_CONSTANT && + prev.op.arg == CONSTANT_NONE) + { + continue; } - // It must only raise. - } - else { - // Walk the bytecode, looking for RETURN_VALUE. - for (int i = 0; i < len; i += _PyInstruction_GetLength(co, i)) { - _Py_CODEUNIT inst = _Py_GetBaseCodeUnit(co, i); - if (IS_RETURN_OPCODE(inst.op.code)) { - assert(i != 0); - // Ignore it if it returns None. - _Py_CODEUNIT prev = _Py_GetBaseCodeUnit(co, i-1); - if (prev.op.code == LOAD_CONST) { - // We don't worry about EXTENDED_ARG for now. - if (prev.op.arg == none_index) { - continue; - } - } - return 0; - } + if (none_index < nconsts && prev.op.code == LOAD_CONST + && prev.op.arg == none_index) + { + continue; } + return 0; } return 1; } @@ -2501,7 +2510,7 @@ code_richcompare(PyObject *self, PyObject *other, int op) cp = (PyCodeObject *)other; eq = PyObject_RichCompareBool(co->co_name, cp->co_name, Py_EQ); - if (!eq) goto unequal; + if (eq <= 0) goto unequal; eq = co->co_argcount == cp->co_argcount; if (!eq) goto unequal; eq = co->co_posonlyargcount == cp->co_posonlyargcount; @@ -2635,18 +2644,6 @@ static PyMemberDef code_memberlist[] = { }; -static PyObject * -code_getlnotab(PyObject *self, void *closure) -{ - PyCodeObject *code = _PyCodeObject_CAST(self); - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "co_lnotab is deprecated, use co_lines instead.", - 1) < 0) { - return NULL; - } - return decode_linetable(code); -} - static PyObject * code_getvarnames(PyObject *self, void *closure) { @@ -2684,7 +2681,6 @@ code_getcode(PyObject *self, void *closure) } static PyGetSetDef code_getsetlist[] = { - {"co_lnotab", code_getlnotab, NULL, NULL}, {"_co_code_adaptive", code_getcodeadaptive, NULL, NULL}, // The following old names are kept for backward compatibility. {"co_varnames", code_getvarnames, NULL, NULL}, @@ -2723,6 +2719,7 @@ code_branchesiterator(PyObject *self, PyObject *Py_UNUSED(args)) } /*[clinic input] +@permit_long_summary @text_signature "($self, /, **changes)" code.replace @@ -2759,7 +2756,7 @@ code_replace_impl(PyCodeObject *self, int co_argcount, PyObject *co_filename, PyObject *co_name, PyObject *co_qualname, PyObject *co_linetable, PyObject *co_exceptiontable) -/*[clinic end generated code: output=e75c48a15def18b9 input=a455a89c57ac9d42]*/ +/*[clinic end generated code: output=e75c48a15def18b9 input=e944fdac8b456114]*/ { #define CHECK_INT_ARG(ARG) \ if (ARG < 0) { \ @@ -2843,12 +2840,13 @@ code._varname_from_oparg (internal-only) Return the local variable name for the given oparg. -WARNING: this method is for internal use only and may change or go away. +WARNING: this method is for internal use only and may change or go +away. [clinic start generated code]*/ static PyObject * code__varname_from_oparg_impl(PyCodeObject *self, int oparg) -/*[clinic end generated code: output=1fd1130413184206 input=c5fa3ee9bac7d4ca]*/ +/*[clinic end generated code: output=1fd1130413184206 input=6ba7d6df0d566463]*/ { PyObject *name = PyTuple_GetItem(self->co_localsplusnames, oparg); if (name == NULL) { @@ -2945,7 +2943,7 @@ _PyCode_ConstantKey(PyObject *op) else if (PyBool_Check(op) || PyBytes_CheckExact(op)) { /* Make booleans different from integers 0 and 1. * Avoid BytesWarning from comparing bytes with strings. */ - key = PyTuple_Pack(2, Py_TYPE(op), op); + key = _PyTuple_FromPair((PyObject *)Py_TYPE(op), op); } else if (PyFloat_CheckExact(op)) { double d = PyFloat_AS_DOUBLE(op); @@ -2955,7 +2953,7 @@ _PyCode_ConstantKey(PyObject *op) if (d == 0.0 && copysign(1.0, d) < 0.0) key = PyTuple_Pack(3, Py_TYPE(op), op, Py_None); else - key = PyTuple_Pack(2, Py_TYPE(op), op); + key = _PyTuple_FromPair((PyObject *)Py_TYPE(op), op); } else if (PyComplex_CheckExact(op)) { Py_complex z; @@ -2979,7 +2977,7 @@ _PyCode_ConstantKey(PyObject *op) key = PyTuple_Pack(3, Py_TYPE(op), op, Py_None); } else { - key = PyTuple_Pack(2, Py_TYPE(op), op); + key = _PyTuple_FromPair((PyObject *)Py_TYPE(op), op); } } else if (PyTuple_CheckExact(op)) { @@ -3004,7 +3002,7 @@ _PyCode_ConstantKey(PyObject *op) PyTuple_SET_ITEM(tuple, i, item_key); } - key = PyTuple_Pack(2, tuple, op); + key = _PyTuple_FromPair(tuple, op); Py_DECREF(tuple); } else if (PyFrozenSet_CheckExact(op)) { @@ -3038,7 +3036,7 @@ _PyCode_ConstantKey(PyObject *op) if (set == NULL) return NULL; - key = PyTuple_Pack(2, set, op); + key = _PyTuple_FromPair(set, op); Py_DECREF(set); return key; } @@ -3069,7 +3067,7 @@ _PyCode_ConstantKey(PyObject *op) goto slice_exit; } - key = PyTuple_Pack(2, slice_key, op); + key = _PyTuple_FromPair(slice_key, op); Py_DECREF(slice_key); slice_exit: Py_XDECREF(start_key); @@ -3083,7 +3081,7 @@ _PyCode_ConstantKey(PyObject *op) if (obj_id == NULL) return NULL; - key = PyTuple_Pack(2, obj_id, op); + key = _PyTuple_FromPair(obj_id, op); Py_DECREF(obj_id); } return key; @@ -3348,13 +3346,13 @@ deopt_code_unit(PyCodeObject *code, int i) } static void -copy_code(_Py_CODEUNIT *dst, PyCodeObject *co) +copy_code(PyInterpreterState *interp, _Py_CODEUNIT *dst, PyCodeObject *co) { int code_len = (int) Py_SIZE(co); for (int i = 0; i < code_len; i += _PyInstruction_GetLength(co, i)) { dst[i] = deopt_code_unit(co, i); } - _PyCode_Quicken(dst, code_len, 1); + _PyCode_Quicken(dst, code_len, interp->opt_config.specialization_enabled, co->co_flags); } static Py_ssize_t @@ -3370,7 +3368,7 @@ get_pow2_greater(Py_ssize_t initial, Py_ssize_t limit) } static _Py_CODEUNIT * -create_tlbc_lock_held(PyCodeObject *co, Py_ssize_t idx) +create_tlbc_lock_held(PyInterpreterState *interp, PyCodeObject *co, Py_ssize_t idx) { _PyCodeArray *tlbc = co->co_tlbc; if (idx >= tlbc->size) { @@ -3393,7 +3391,7 @@ create_tlbc_lock_held(PyCodeObject *co, Py_ssize_t idx) PyErr_NoMemory(); return NULL; } - copy_code((_Py_CODEUNIT *) bc, co); + copy_code(interp, (_Py_CODEUNIT *) bc, co); assert(tlbc->entries[idx] == NULL); tlbc->entries[idx] = bc; return (_Py_CODEUNIT *) bc; @@ -3408,7 +3406,8 @@ get_tlbc_lock_held(PyCodeObject *co) if (idx < tlbc->size && tlbc->entries[idx] != NULL) { return (_Py_CODEUNIT *)tlbc->entries[idx]; } - return create_tlbc_lock_held(co, idx); + PyInterpreterState *interp = tstate->base.interp; + return create_tlbc_lock_held(interp, co, idx); } _Py_CODEUNIT * diff --git a/Objects/complexobject.c b/Objects/complexobject.c index b66ebe131ae605..3612c2699a557d 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -139,8 +139,8 @@ _Py_c_prod(Py_complex z, Py_complex w) recalc = 1; } if (recalc) { - r.real = Py_INFINITY*(a*c - b*d); - r.imag = Py_INFINITY*(a*d + b*c); + r.real = INFINITY*(a*c - b*d); + r.imag = INFINITY*(a*d + b*c); } } @@ -229,8 +229,8 @@ _Py_c_quot(Py_complex a, Py_complex b) { const double x = copysign(isinf(a.real) ? 1.0 : 0.0, a.real); const double y = copysign(isinf(a.imag) ? 1.0 : 0.0, a.imag); - r.real = Py_INFINITY * (x*b.real + y*b.imag); - r.imag = Py_INFINITY * (y*b.real - x*b.imag); + r.real = INFINITY * (x*b.real + y*b.imag); + r.imag = INFINITY * (y*b.real - x*b.imag); } else if ((isinf(abs_breal) || isinf(abs_bimag)) && isfinite(a.real) && isfinite(a.imag)) @@ -515,17 +515,17 @@ try_complex_special_method(PyObject *op) } if (!PyComplex_Check(res)) { PyErr_Format(PyExc_TypeError, - "__complex__ returned non-complex (type %.200s)", - Py_TYPE(res)->tp_name); + "%T.__complex__() must return a complex, not %T", + op, res); Py_DECREF(res); return NULL; } /* Issue #29894: warn if 'res' not of exact type complex. */ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "__complex__ returned non-complex (type %.200s). " + "%T.__complex__() must return a complex, not %T. " "The ability to return an instance of a strict subclass of complex " "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(res)->tp_name)) { + op, res)) { Py_DECREF(res); return NULL; } @@ -644,7 +644,7 @@ complex_hash(PyObject *op) * compare equal must have the same hash value, so that * hash(x + 0*j) must equal hash(x). */ - combined = hashreal + _PyHASH_IMAG * hashimag; + combined = hashreal + PyHASH_IMAG * hashimag; if (combined == (Py_uhash_t)-1) combined = (Py_uhash_t)-2; return (Py_hash_t)combined; @@ -869,6 +869,7 @@ complex_richcompare(PyObject *v, PyObject *w, int op) } /*[clinic input] +@permit_long_summary complex.conjugate Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j. @@ -876,7 +877,7 @@ Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j. static PyObject * complex_conjugate_impl(PyComplexObject *self) -/*[clinic end generated code: output=5059ef162edfc68e input=5fea33e9747ec2c4]*/ +/*[clinic end generated code: output=5059ef162edfc68e input=71b8ab003e1cec95]*/ { Py_complex c = self->cval; c.imag = -c.imag; diff --git a/Objects/descrobject.c b/Objects/descrobject.c index d3d17e92b6d1e8..30444b7d677424 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -39,41 +39,41 @@ descr_name(PyDescrObject *descr) } static PyObject * -descr_repr(PyDescrObject *descr, const char *format) +descr_repr(PyDescrObject *descr, const char *kind) { PyObject *name = NULL; if (descr->d_name != NULL && PyUnicode_Check(descr->d_name)) name = descr->d_name; - return PyUnicode_FromFormat(format, name, "?", descr->d_type->tp_name); + if (descr->d_type == &PyBaseObject_Type) { + return PyUnicode_FromFormat("<%s '%V'>", kind, name, "?"); + } + return PyUnicode_FromFormat("<%s '%V' of '%s' objects>", + kind, name, "?", descr->d_type->tp_name); } static PyObject * method_repr(PyObject *descr) { - return descr_repr((PyDescrObject *)descr, - "<method '%V' of '%s' objects>"); + return descr_repr((PyDescrObject *)descr, "method"); } static PyObject * member_repr(PyObject *descr) { - return descr_repr((PyDescrObject *)descr, - "<member '%V' of '%s' objects>"); + return descr_repr((PyDescrObject *)descr, "member"); } static PyObject * getset_repr(PyObject *descr) { - return descr_repr((PyDescrObject *)descr, - "<attribute '%V' of '%s' objects>"); + return descr_repr((PyDescrObject *)descr, "attribute"); } static PyObject * wrapperdescr_repr(PyObject *descr) { - return descr_repr((PyDescrObject *)descr, - "<slot wrapper '%V' of '%s' objects>"); + return descr_repr((PyDescrObject *)descr, "slot wrapper"); } static int @@ -150,7 +150,7 @@ method_get(PyObject *self, PyObject *obj, PyObject *type) } else { PyErr_Format(PyExc_TypeError, "descriptor '%V' needs a type, not '%s', as arg 2", - descr_name((PyDescrObject *)descr), + descr_name((PyDescrObject *)descr), "?", Py_TYPE(type)->tp_name); return NULL; } @@ -313,7 +313,7 @@ method_vectorcall_VARARGS( if (method_check_args(func, args, nargs, kwnames)) { return NULL; } - PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1); + PyObject *argstuple = PyTuple_FromArray(args+1, nargs-1); if (argstuple == NULL) { return NULL; } @@ -338,7 +338,7 @@ method_vectorcall_VARARGS_KEYWORDS( if (method_check_args(func, args, nargs, NULL)) { return NULL; } - PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1); + PyObject *argstuple = PyTuple_FromArray(args+1, nargs-1); if (argstuple == NULL) { return NULL; } @@ -1178,7 +1178,7 @@ static PyMethodDef mappingproxy_methods[] = { {"copy", mappingproxy_copy, METH_NOARGS, PyDoc_STR("D.copy() -> a shallow copy of D")}, {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, - PyDoc_STR("See PEP 585")}, + PyDoc_STR("mappingproxy objects are generic over two types, signifying (respectively) the types of their keys and values")}, {"__reversed__", mappingproxy_reversed, METH_NOARGS, PyDoc_STR("D.__reversed__() -> reverse iterator")}, {0} @@ -1610,7 +1610,7 @@ property_set_name(PyObject *self, PyObject *args) { if (PyTuple_GET_SIZE(args) != 2) { PyErr_Format( PyExc_TypeError, - "__set_name__() takes 2 positional arguments but %d were given", + "__set_name__() takes 2 positional arguments but %zd were given", PyTuple_GET_SIZE(args)); return NULL; } diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 0ed52ac5e87b6e..0a6d0f9199641e 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -135,11 +135,20 @@ As a consequence of this, split keys have a maximum size of 16. #include "stringlib/eq.h" // unicode_eq() #include <stdbool.h> +// Forward declarations +static PyObject* frozendict_new(PyTypeObject *type, PyObject *args, + PyObject *kwds); +static PyObject* dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds); +static int dict_merge(PyObject *a, PyObject *b, int override, PyObject **dupkey); +static int dict_contains(PyObject *op, PyObject *key); +static int dict_merge_from_seq2(PyObject *d, PyObject *seq2, int override); + /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" +class frozendict "PyFrozenDictObject *" "&PyFrozenDict_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5dfa93bac68e7c54]*/ /* @@ -278,6 +287,31 @@ load_keys_nentries(PyDictObject *mp) #endif +#ifndef NDEBUG +// Check if it's possible to modify a dictionary. +// Usage: assert(can_modify_dict(mp)). +static inline int +can_modify_dict(PyDictObject *mp) +{ + if (PyFrozenDict_Check(mp)) { + // No locking required to modify a newly created frozendict + // since it's only accessible from the current thread. + return PyUnstable_Object_IsUniquelyReferenced(_PyObject_CAST(mp)); + } + else { + // Locking is only required if the dictionary is not + // uniquely referenced. + ASSERT_DICT_LOCKED(mp); + return 1; + } +} +#endif + +#define _PyAnyDict_CAST(op) \ + (assert(PyAnyDict_Check(op)), _Py_CAST(PyDictObject*, op)) + +#define GET_USED(ep) FT_ATOMIC_LOAD_SSIZE_RELAXED((ep)->ma_used) + #define STORE_KEY(ep, key) FT_ATOMIC_STORE_PTR_RELEASE((ep)->me_key, key) #define STORE_VALUE(ep, value) FT_ATOMIC_STORE_PTR_RELEASE((ep)->me_value, value) #define STORE_SPLIT_VALUE(mp, idx, value) FT_ATOMIC_STORE_PTR_RELEASE(mp->ma_values->values[idx], value) @@ -400,8 +434,7 @@ static int _PyObject_InlineValuesConsistencyCheck(PyObject *obj); static inline Py_hash_t unicode_get_hash(PyObject *o) { - assert(PyUnicode_CheckExact(o)); - return FT_ATOMIC_LOAD_SSIZE_RELAXED(_PyASCIIObject_CAST(o)->hash); + return PyUnstable_Unicode_GET_CACHED_HASH(o); } /* Print summary info about the state of the optimized allocator */ @@ -410,7 +443,7 @@ _PyDict_DebugMallocStats(FILE *out) { _PyDebugAllocatorStats(out, "free PyDictObject", _Py_FREELIST_SIZE(dicts), - sizeof(PyDictObject)); + _PyType_PreHeaderSize(&PyDict_Type) + sizeof(PyDictObject)); _PyDebugAllocatorStats(out, "free PyDictKeysObject", _Py_FREELIST_SIZE(dictkeys), sizeof(PyDictKeysObject)); @@ -655,7 +688,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content) do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) assert(op != NULL); - CHECK(PyDict_Check(op)); + CHECK(PyAnyDict_Check(op)); PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; @@ -751,16 +784,12 @@ _PyDict_CheckConsistency(PyObject *op, int check_content) } -static PyDictKeysObject* -new_keys_object(uint8_t log2_size, bool unicode) +static inline int +get_log2_bytes(uint8_t log2_size) { - Py_ssize_t usable; int log2_bytes; - size_t entry_size = unicode ? sizeof(PyDictUnicodeEntry) : sizeof(PyDictKeyEntry); - assert(log2_size >= PyDict_LOG_MINSIZE); - usable = USABLE_FRACTION((size_t)1<<log2_size); if (log2_size < 8) { log2_bytes = log2_size; } @@ -776,26 +805,20 @@ new_keys_object(uint8_t log2_size, bool unicode) log2_bytes = log2_size + 2; } - PyDictKeysObject *dk = NULL; - if (log2_size == PyDict_LOG_MINSIZE && unicode) { - dk = _Py_FREELIST_POP_MEM(dictkeys); - } - if (dk == NULL) { - dk = PyMem_Malloc(sizeof(PyDictKeysObject) - + ((size_t)1 << log2_bytes) - + entry_size * usable); - if (dk == NULL) { - PyErr_NoMemory(); - return NULL; - } - } + return log2_bytes; +} + +static inline void +init_keys_object(PyDictKeysObject* dk, uint8_t log2_size, int log2_bytes, int kind, + Py_ssize_t usable, Py_ssize_t entry_size) +{ #ifdef Py_REF_DEBUG _Py_IncRefTotal(_PyThreadState_GET()); #endif dk->dk_refcnt = 1; dk->dk_log2_size = log2_size; dk->dk_log2_index_bytes = log2_bytes; - dk->dk_kind = unicode ? DICT_KEYS_UNICODE : DICT_KEYS_GENERAL; + dk->dk_kind = kind; #ifdef Py_GIL_DISABLED dk->dk_mutex = (PyMutex){0}; #endif @@ -804,15 +827,51 @@ new_keys_object(uint8_t log2_size, bool unicode) dk->dk_version = 0; memset(&dk->dk_indices[0], 0xff, ((size_t)1 << log2_bytes)); memset(&dk->dk_indices[(size_t)1 << log2_bytes], 0, entry_size * usable); +} + +static PyDictKeysObject* +new_keys_object(uint8_t log2_size, bool unicode) +{ + Py_ssize_t usable = USABLE_FRACTION((size_t)1<<log2_size); + size_t entry_size = unicode ? sizeof(PyDictUnicodeEntry) : sizeof(PyDictKeyEntry); + + int log2_bytes = get_log2_bytes(log2_size); + + PyDictKeysObject *dk = NULL; + if (log2_size == PyDict_LOG_MINSIZE && unicode) { + dk = _Py_FREELIST_POP_MEM(dictkeys); + } + if (dk == NULL) { + dk = PyMem_Malloc(sizeof(PyDictKeysObject) + + ((size_t)1 << log2_bytes) + + entry_size * usable); + if (dk == NULL) { + PyErr_NoMemory(); + return NULL; + } + } + init_keys_object(dk, log2_size, log2_bytes, + unicode ? DICT_KEYS_UNICODE : DICT_KEYS_GENERAL, + usable, entry_size); return dk; } static void free_keys_object(PyDictKeysObject *keys, bool use_qsbr) { + void *ptr = keys; +#ifdef Py_GIL_DISABLED + size_t size = _PyDict_KeysSize(keys); +#endif + if (keys->dk_kind == DICT_KEYS_SPLIT) { + ptr = _PyDictKeys_AsSharedKeys(keys); +#ifdef Py_GIL_DISABLED + size += offsetof(struct _instancekeysobject, dsk_keys); +#endif + } #ifdef Py_GIL_DISABLED if (use_qsbr) { - _PyMem_FreeDelayed(keys, _PyDict_KeysSize(keys)); + _PyMem_FreeDelayed(ptr, size); return; } #endif @@ -820,7 +879,7 @@ free_keys_object(PyDictKeysObject *keys, bool use_qsbr) _Py_FREELIST_FREE(dictkeys, keys, PyMem_Free); } else { - PyMem_Free(keys); + PyMem_Free(ptr); } } @@ -864,33 +923,55 @@ free_values(PyDictValues *values, bool use_qsbr) PyMem_Free(values); } -/* Consumes a reference to the keys object */ -static PyObject * -new_dict(PyDictKeysObject *keys, PyDictValues *values, - Py_ssize_t used, int free_values_on_failure) +static inline PyObject * +new_dict_impl(PyDictObject *mp, PyDictKeysObject *keys, + PyDictValues *values, Py_ssize_t used, + int free_values_on_failure, int frozendict) { assert(keys != NULL); - PyDictObject *mp = _Py_FREELIST_POP(PyDictObject, dicts); if (mp == NULL) { - mp = PyObject_GC_New(PyDictObject, &PyDict_Type); - if (mp == NULL) { - dictkeys_decref(keys, false); - if (free_values_on_failure) { - free_values(values, false); - } - return NULL; + dictkeys_decref(keys, false); + if (free_values_on_failure) { + free_values(values, false); } + return NULL; } - assert(Py_IS_TYPE(mp, &PyDict_Type)); + mp->ma_keys = keys; mp->ma_values = values; mp->ma_used = used; mp->_ma_watcher_tag = 0; + if (frozendict) { + ((PyFrozenDictObject *)mp)->ma_hash = -1; + } ASSERT_CONSISTENT(mp); _PyObject_GC_TRACK(mp); return (PyObject *)mp; } +/* Consumes a reference to the keys object */ +static PyObject * +new_dict(PyDictKeysObject *keys, PyDictValues *values, + Py_ssize_t used, int free_values_on_failure) +{ + PyDictObject *mp = _Py_FREELIST_POP(PyDictObject, dicts); + if (mp == NULL) { + mp = PyObject_GC_New(PyDictObject, &PyDict_Type); + } + assert(mp == NULL || Py_IS_TYPE(mp, &PyDict_Type)); + + return new_dict_impl(mp, keys, values, used, free_values_on_failure, 0); +} + +/* Consumes a reference to the keys object */ +static PyObject * +new_frozendict(PyDictKeysObject *keys, PyDictValues *values, + Py_ssize_t used, int free_values_on_failure) +{ + PyDictObject *mp = PyObject_GC_New(PyDictObject, &PyFrozenDict_Type); + return new_dict_impl(mp, keys, values, used, free_values_on_failure, 1); +} + static PyObject * new_dict_with_shared_keys(PyDictKeysObject *keys) { @@ -910,13 +991,15 @@ new_dict_with_shared_keys(PyDictKeysObject *keys) static PyDictKeysObject * clone_combined_dict_keys(PyDictObject *orig) { - assert(PyDict_Check(orig)); + assert(PyAnyDict_Check(orig)); assert(Py_TYPE(orig)->tp_iter == dict_iter); assert(orig->ma_values == NULL); assert(orig->ma_keys != Py_EMPTY_KEYS); assert(orig->ma_keys->dk_refcnt == 1); - ASSERT_DICT_LOCKED(orig); + if (!PyFrozenDict_Check(orig)) { + ASSERT_DICT_LOCKED(orig); + } size_t keys_size = _PyDict_KeysSize(orig->ma_keys); PyDictKeysObject *keys = PyMem_Malloc(keys_size); @@ -1079,7 +1162,7 @@ compare_unicode_unicode(PyDictObject *mp, PyDictKeysObject *dk, void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash) { PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix]; - PyObject *ep_key = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_key); + PyObject *ep_key = FT_ATOMIC_LOAD_PTR_CONSUME(ep->me_key); assert(ep_key != NULL); assert(PyUnicode_CheckExact(ep_key)); if (ep_key == key || @@ -1372,7 +1455,7 @@ compare_unicode_generic_threadsafe(PyDictObject *mp, PyDictKeysObject *dk, void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash) { PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix]; - PyObject *startkey = _Py_atomic_load_ptr_relaxed(&ep->me_key); + PyObject *startkey = _Py_atomic_load_ptr_consume(&ep->me_key); assert(startkey == NULL || PyUnicode_CheckExact(ep->me_key)); assert(!PyUnicode_CheckExact(key)); @@ -1415,7 +1498,7 @@ compare_unicode_unicode_threadsafe(PyDictObject *mp, PyDictKeysObject *dk, void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash) { PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix]; - PyObject *startkey = _Py_atomic_load_ptr_relaxed(&ep->me_key); + PyObject *startkey = _Py_atomic_load_ptr_consume(&ep->me_key); if (startkey == key) { assert(PyUnicode_CheckExact(startkey)); return 1; @@ -1451,7 +1534,7 @@ compare_generic_threadsafe(PyDictObject *mp, PyDictKeysObject *dk, void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash) { PyDictKeyEntry *ep = &((PyDictKeyEntry *)ep0)[ix]; - PyObject *startkey = _Py_atomic_load_ptr_relaxed(&ep->me_key); + PyObject *startkey = _Py_atomic_load_ptr_consume(&ep->me_key); if (startkey == key) { return 1; } @@ -1581,32 +1664,49 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb return ix; } +static Py_ssize_t +lookup_threadsafe_unicode(PyDictKeysObject *dk, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr) +{ + assert(dk->dk_kind == DICT_KEYS_UNICODE); + assert(PyUnicode_CheckExact(key)); + + Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash); + if (ix == DKIX_EMPTY) { + *value_addr = PyStackRef_NULL; + return ix; + } + else if (ix >= 0) { + PyObject **addr_of_value = &DK_UNICODE_ENTRIES(dk)[ix].me_value; + PyObject *value = _Py_atomic_load_ptr(addr_of_value); + if (value == NULL) { + *value_addr = PyStackRef_NULL; + return DKIX_EMPTY; + } + if (_PyObject_HasDeferredRefcount(value)) { + *value_addr = (_PyStackRef){ .bits = (uintptr_t)value | Py_TAG_REFCNT }; + return ix; + } + if (_Py_TryIncrefCompare(addr_of_value, value)) { + *value_addr = PyStackRef_FromPyObjectSteal(value); + return ix; + } + return DKIX_KEY_CHANGED; + } + assert(ix == DKIX_KEY_CHANGED); + return ix; +} + Py_ssize_t _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr) { - PyDictKeysObject *dk = _Py_atomic_load_ptr(&mp->ma_keys); + ensure_shared_on_read(mp); + + PyDictKeysObject *dk = _Py_atomic_load_ptr_acquire(&mp->ma_keys); if (dk->dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact(key)) { - Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash); - if (ix == DKIX_EMPTY) { - *value_addr = PyStackRef_NULL; + Py_ssize_t ix = lookup_threadsafe_unicode(dk, key, hash, value_addr); + if (ix != DKIX_KEY_CHANGED) { return ix; } - else if (ix >= 0) { - PyObject **addr_of_value = &DK_UNICODE_ENTRIES(dk)[ix].me_value; - PyObject *value = _Py_atomic_load_ptr(addr_of_value); - if (value == NULL) { - *value_addr = PyStackRef_NULL; - return DKIX_EMPTY; - } - if (_PyObject_HasDeferredRefcount(value)) { - *value_addr = (_PyStackRef){ .bits = (uintptr_t)value | Py_TAG_DEFERRED }; - return ix; - } - if (_Py_TryIncrefCompare(addr_of_value, value)) { - *value_addr = PyStackRef_FromPyObjectSteal(value); - return ix; - } - } } PyObject *obj; @@ -1646,6 +1746,54 @@ _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t h #endif +// Looks up the unicode key `key` in the dictionary. Note that `*method` may +// already contain a valid value! See _PyObject_GetMethodStackRef(). +int +_PyDict_GetMethodStackRef(PyDictObject *mp, PyObject *key, _PyStackRef *method) +{ + assert(PyUnicode_CheckExact(key)); + Py_hash_t hash = hash_unicode_key(key); + +#ifdef Py_GIL_DISABLED + // NOTE: We can only do the fast-path lookup if we are on the owning + // thread or if the dict is already marked as shared so that the load + // of ma_keys is safe without a lock. We cannot call ensure_shared_on_read() + // in this code path without incref'ing the dict because the dict is a + // borrowed reference protected by QSBR, and acquiring the lock could lead + // to a quiescent state (allowing the dict to be freed). + if (_Py_IsOwnedByCurrentThread((PyObject *)mp) || IS_DICT_SHARED(mp)) { + PyDictKeysObject *dk = _Py_atomic_load_ptr_acquire(&mp->ma_keys); + if (dk->dk_kind == DICT_KEYS_UNICODE) { + _PyStackRef ref; + Py_ssize_t ix = lookup_threadsafe_unicode(dk, key, hash, &ref); + if (ix >= 0) { + assert(!PyStackRef_IsNull(ref)); + PyStackRef_XSETREF(*method, ref); + return 1; + } + else if (ix == DKIX_EMPTY) { + return 0; + } + assert(ix == DKIX_KEY_CHANGED); + } + } +#endif + + PyObject *obj; + Py_INCREF(mp); + Py_ssize_t ix = _Py_dict_lookup_threadsafe(mp, key, hash, &obj); + Py_DECREF(mp); + if (ix == DKIX_ERROR) { + PyStackRef_CLEAR(*method); + return -1; + } + else if (ix >= 0 && obj != NULL) { + PyStackRef_XSETREF(*method, PyStackRef_FromPyObjectSteal(obj)); + return 1; + } + return 0; // not found +} + int _PyDict_HasOnlyStringKeys(PyObject *dict) { @@ -1717,9 +1865,17 @@ insertion_resize(PyDictObject *mp, int unicode) } static inline int -insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp, +insert_combined_dict(PyDictObject *mp, Py_hash_t hash, PyObject *key, PyObject *value) { + // gh-140551: If dict was cleared in _Py_dict_lookup, + // we have to resize one more time to force general key kind. + if (DK_IS_UNICODE(mp->ma_keys) && !PyUnicode_CheckExact(key)) { + if (insertion_resize(mp, 0) < 0) + return -1; + assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL); + } + if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (insertion_resize(mp, 1) < 0) { @@ -1727,7 +1883,7 @@ insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp, } } - _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value); + _PyDict_NotifyEvent(PyDict_EVENT_ADDED, mp, key, value); FT_ATOMIC_STORE_UINT32_RELAXED(mp->ma_keys->dk_version, 0); Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); @@ -1771,6 +1927,7 @@ insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash) if (ix == DKIX_EMPTY && keys->dk_usable > 0) { // Insert into new slot FT_ATOMIC_STORE_UINT32_RELAXED(keys->dk_version, 0); + _PyDict_SplitKeysInvalidated(keys); Py_ssize_t hashpos = find_empty_slot(keys, hash); ix = keys->dk_nentries; dictkeys_set_index(keys, hashpos, ix); @@ -1783,20 +1940,21 @@ insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash) return ix; } -static void -insert_split_value(PyInterpreterState *interp, PyDictObject *mp, PyObject *key, PyObject *value, Py_ssize_t ix) +void +_PyDict_InsertSplitValue(PyDictObject *mp, PyObject *key, PyObject *value, Py_ssize_t ix) { + assert(can_modify_dict(mp)); assert(PyUnicode_CheckExact(key)); - ASSERT_DICT_LOCKED(mp); + PyObject *old_value = mp->ma_values->values[ix]; if (old_value == NULL) { - _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value); + _PyDict_NotifyEvent(PyDict_EVENT_ADDED, mp, key, value); STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value)); _PyDictValues_AddToInsertionOrder(mp->ma_values, ix); STORE_USED(mp, mp->ma_used + 1); } else { - _PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, mp, key, value); STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value)); // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. @@ -1812,43 +1970,39 @@ Returns -1 if an error occurred, or 0 on success. Consumes key and value references. */ static int -insertdict(PyInterpreterState *interp, PyDictObject *mp, +insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { - PyObject *old_value; - - ASSERT_DICT_LOCKED(mp); + assert(can_modify_dict(mp)); - if (DK_IS_UNICODE(mp->ma_keys) && !PyUnicode_CheckExact(key)) { - if (insertion_resize(mp, 0) < 0) - goto Fail; - assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL); - } + PyObject *old_value = NULL; + Py_ssize_t ix; - if (_PyDict_HasSplitTable(mp)) { - Py_ssize_t ix = insert_split_key(mp->ma_keys, key, hash); + if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) { + ix = insert_split_key(mp->ma_keys, key, hash); if (ix != DKIX_EMPTY) { - insert_split_value(interp, mp, key, value, ix); + _PyDict_InsertSplitValue(mp, key, value, ix); Py_DECREF(key); Py_DECREF(value); return 0; } - - /* No space in shared keys. Resize and continue below. */ - if (insertion_resize(mp, 1) < 0) { + // No space in shared keys. Go to insert_combined_dict() below. + } + else { + ix = _Py_dict_lookup(mp, key, hash, &old_value); + if (ix == DKIX_ERROR) goto Fail; - } } - Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &old_value); - if (ix == DKIX_ERROR) - goto Fail; - - if (ix == DKIX_EMPTY) { - assert(!_PyDict_HasSplitTable(mp)); - /* Insert into new slot. */ - assert(old_value == NULL); - if (insert_combined_dict(interp, mp, hash, key, value) < 0) { + if (old_value == NULL) { + // insert_combined_dict() will convert from non DICT_KEYS_GENERAL table + // into DICT_KEYS_GENERAL table if key is not Unicode. + // We don't convert it before _Py_dict_lookup because non-Unicode key + // may change generic table into Unicode table. + // + // NOTE: ix may not be DKIX_EMPTY because split table may have key + // without value. + if (insert_combined_dict(mp, hash, key, value) < 0) { goto Fail; } STORE_USED(mp, mp->ma_used + 1); @@ -1857,12 +2011,16 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, } if (old_value != value) { - _PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, mp, key, value); assert(old_value != NULL); - assert(!_PyDict_HasSplitTable(mp)); if (DK_IS_UNICODE(mp->ma_keys)) { - PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[ix]; - STORE_VALUE(ep, value); + if (_PyDict_HasSplitTable(mp)) { + STORE_SPLIT_VALUE(mp, ix, value); + } + else { + PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[ix]; + STORE_VALUE(ep, value); + } } else { PyDictKeyEntry *ep = &DK_ENTRIES(mp->ma_keys)[ix]; @@ -1883,11 +2041,11 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, // Same as insertdict but specialized for ma_keys == Py_EMPTY_KEYS. // Consumes key and value references. static int -insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp, +insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { + assert(can_modify_dict(mp)); assert(mp->ma_keys == Py_EMPTY_KEYS); - ASSERT_DICT_LOCKED(mp); int unicode = PyUnicode_CheckExact(key); PyDictKeysObject *newkeys = new_keys_object(PyDict_LOG_MINSIZE, unicode); @@ -1896,7 +2054,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp, Py_DECREF(value); return -1; } - _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value); + _PyDict_NotifyEvent(PyDict_EVENT_ADDED, mp, key, value); /* We don't decref Py_EMPTY_KEYS here because it is immortal. */ assert(mp->ma_values == NULL); @@ -1988,11 +2146,11 @@ static int dictresize(PyDictObject *mp, uint8_t log2_newsize, int unicode) { + assert(can_modify_dict(mp)); + PyDictKeysObject *oldkeys, *newkeys; PyDictValues *oldvalues; - ASSERT_DICT_LOCKED(mp); - if (log2_newsize >= SIZEOF_SIZE_T*8) { PyErr_NoMemory(); return -1; @@ -2221,7 +2379,7 @@ _PyDict_FromItems(PyObject *const *keys, Py_ssize_t keys_offset, static PyObject * dict_getitem(PyObject *op, PyObject *key, const char *warnmsg) { - if (!PyDict_Check(op)) { + if (!PyAnyDict_Check(op)) { return NULL; } PyDictObject *mp = (PyDictObject *)op; @@ -2271,7 +2429,7 @@ PyDict_GetItem(PyObject *op, PyObject *key) } static void -dict_unhashable_type(PyObject *key) +dict_unhashable_type(PyObject *op, PyObject *key) { PyObject *exc = PyErr_GetRaisedException(); assert(exc != NULL); @@ -2280,27 +2438,38 @@ dict_unhashable_type(PyObject *key) return; } - PyErr_Format(PyExc_TypeError, - "cannot use '%T' as a dict key (%S)", - key, exc); + const char *errmsg; + if (PyFrozenDict_Check(op)) { + errmsg = "cannot use '%T' as a frozendict key (%S)"; + } + else { + errmsg = "cannot use '%T' as a dict key (%S)"; + } + PyErr_Format(PyExc_TypeError, errmsg, key, exc); Py_DECREF(exc); } Py_ssize_t -_PyDict_LookupIndex(PyDictObject *mp, PyObject *key) +_PyDict_LookupIndexAndValue(PyDictObject *mp, PyObject *key, PyObject **value) { // TODO: Thread safety - PyObject *value; assert(PyDict_CheckExact((PyObject*)mp)); assert(PyUnicode_CheckExact(key)); Py_hash_t hash = _PyObject_HashFast(key); if (hash == -1) { - dict_unhashable_type(key); + dict_unhashable_type((PyObject*)mp, key); return -1; } - return _Py_dict_lookup(mp, key, hash, &value); + return _Py_dict_lookup(mp, key, hash, value); +} + +Py_ssize_t +_PyDict_LookupIndex(PyDictObject *mp, PyObject *key) +{ + PyObject *value; // discarded + return _PyDict_LookupIndexAndValue(mp, key, &value); } /* Same as PyDict_GetItemWithError() but with hash supplied by caller. @@ -2314,7 +2483,7 @@ _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) PyDictObject *mp = (PyDictObject *)op; PyObject *value; - if (!PyDict_Check(op)) { + if (!PyAnyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } @@ -2385,7 +2554,7 @@ _PyDict_GetItemRef_KnownHash(PyDictObject *op, PyObject *key, Py_hash_t hash, Py int PyDict_GetItemRef(PyObject *op, PyObject *key, PyObject **result) { - if (!PyDict_Check(op)) { + if (!PyAnyDict_Check(op)) { PyErr_BadInternalCall(); *result = NULL; return -1; @@ -2393,7 +2562,7 @@ PyDict_GetItemRef(PyObject *op, PyObject *key, PyObject **result) Py_hash_t hash = _PyObject_HashFast(key); if (hash == -1) { - dict_unhashable_type(key); + dict_unhashable_type(op, key); *result = NULL; return -1; } @@ -2409,7 +2578,7 @@ _PyDict_GetItemRef_Unicode_LockHeld(PyDictObject *op, PyObject *key, PyObject ** Py_hash_t hash = _PyObject_HashFast(key); if (hash == -1) { - dict_unhashable_type(key); + dict_unhashable_type((PyObject*)op, key); *result = NULL; return -1; } @@ -2441,13 +2610,13 @@ PyDict_GetItemWithError(PyObject *op, PyObject *key) PyDictObject*mp = (PyDictObject *)op; PyObject *value; - if (!PyDict_Check(op)) { + if (!PyAnyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } hash = _PyObject_HashFast(key); if (hash == -1) { - dict_unhashable_type(key); + dict_unhashable_type(op, key); return NULL; } @@ -2472,18 +2641,6 @@ _PyDict_GetItemWithError(PyObject *dp, PyObject *kv) return _PyDict_GetItem_KnownHash(dp, kv, hash); // borrowed reference } -PyObject * -_PyDict_GetItemIdWithError(PyObject *dp, _Py_Identifier *key) -{ - PyObject *kv; - kv = _PyUnicode_FromId(key); /* borrowed */ - if (kv == NULL) - return NULL; - Py_hash_t hash = unicode_get_hash(kv); - assert (hash != -1); /* interned strings have their hash value initialised */ - return _PyDict_GetItem_KnownHash(dp, kv, hash); // borrowed reference -} - PyObject * _PyDict_GetItemStringWithError(PyObject *v, const char *key) { @@ -2563,7 +2720,7 @@ _PyDict_LoadGlobalStackRef(PyDictObject *globals, PyDictObject *builtins, PyObje PyObject * _PyDict_LoadBuiltinsFromGlobals(PyObject *globals) { - if (!PyDict_Check(globals)) { + if (!PyAnyDict_Check(globals)) { PyErr_BadInternalCall(); return NULL; } @@ -2594,30 +2751,38 @@ _PyDict_LoadBuiltinsFromGlobals(PyObject *globals) return builtins; } +#define frozendict_does_not_support(WHAT) \ + PyErr_SetString(PyExc_TypeError, "frozendict object does " \ + "not support item " WHAT) + /* Consumes references to key and value */ static int -setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) +setitem_take2_lock_held_known_hash(PyDictObject *mp, PyObject *key, PyObject *value, Py_hash_t hash) { - ASSERT_DICT_LOCKED(mp); - + assert(PyAnyDict_Check(mp)); + assert(can_modify_dict(mp)); assert(key); assert(value); - assert(PyDict_Check(mp)); + + if (mp->ma_keys == Py_EMPTY_KEYS) { + return insert_to_emptydict(mp, key, hash, value); + } + /* insertdict() handles any resizing that might be necessary */ + return insertdict(mp, key, hash, value); +} + +static int +setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) +{ Py_hash_t hash = _PyObject_HashFast(key); if (hash == -1) { - dict_unhashable_type(key); + dict_unhashable_type((PyObject*)mp, key); Py_DECREF(key); Py_DECREF(value); return -1; } - PyInterpreterState *interp = _PyInterpreterState_GET(); - - if (mp->ma_keys == Py_EMPTY_KEYS) { - return insert_to_emptydict(interp, mp, key, hash, value); - } - /* insertdict() handles any resizing that might be necessary */ - return insertdict(interp, mp, key, hash, value); + return setitem_take2_lock_held_known_hash(mp, key, value, hash); } int @@ -2630,6 +2795,16 @@ _PyDict_SetItem_Take2(PyDictObject *mp, PyObject *key, PyObject *value) return res; } +int +_PyDict_SetItem_Take2_KnownHash(PyDictObject *mp, PyObject *key, PyObject *value, Py_hash_t hash) +{ + int res; + Py_BEGIN_CRITICAL_SECTION(mp); + res = setitem_take2_lock_held_known_hash(mp, key, value, hash); + Py_END_CRITICAL_SECTION(); + return res; +} + /* CAUTION: PyDict_SetItem() must guarantee that it won't resize the * dictionary if it's merely replacing the value for an existing key. * This means that it's safe to loop over a dictionary with PyDict_Next() @@ -2639,10 +2814,27 @@ _PyDict_SetItem_Take2(PyDictObject *mp, PyObject *key, PyObject *value) int PyDict_SetItem(PyObject *op, PyObject *key, PyObject *value) { + assert(key); + assert(value); + if (!PyDict_Check(op)) { - PyErr_BadInternalCall(); + if (PyFrozenDict_Check(op)) { + frozendict_does_not_support("assignment"); + } + else { + PyErr_BadInternalCall(); + } return -1; } + + return _PyDict_SetItem_Take2((PyDictObject *)op, + Py_NewRef(key), Py_NewRef(value)); +} + +static int +_PyAnyDict_SetItem(PyObject *op, PyObject *key, PyObject *value) +{ + assert(PyAnyDict_Check(op)); assert(key); assert(value); return _PyDict_SetItem_Take2((PyDictObject *)op, @@ -2663,32 +2855,37 @@ int _PyDict_SetItem_KnownHash_LockHeld(PyDictObject *mp, PyObject *key, PyObject *value, Py_hash_t hash) { - PyInterpreterState *interp = _PyInterpreterState_GET(); if (mp->ma_keys == Py_EMPTY_KEYS) { - return insert_to_emptydict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value)); + return insert_to_emptydict(mp, Py_NewRef(key), hash, Py_NewRef(value)); } /* insertdict() handles any resizing that might be necessary */ - return insertdict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value)); + return insertdict(mp, Py_NewRef(key), hash, Py_NewRef(value)); } int _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value, Py_hash_t hash) { - if (!PyDict_Check(op)) { - PyErr_BadInternalCall(); - return -1; - } assert(key); assert(value); assert(hash != -1); - int res; - Py_BEGIN_CRITICAL_SECTION(op); - res = _PyDict_SetItem_KnownHash_LockHeld((PyDictObject *)op, key, value, hash); - Py_END_CRITICAL_SECTION(); - return res; -} + if (!PyDict_Check(op)) { + if (PyFrozenDict_Check(op)) { + frozendict_does_not_support("assignment"); + } + else { + PyErr_BadInternalCall(); + } + return -1; + } + + int res; + Py_BEGIN_CRITICAL_SECTION(op); + res = _PyDict_SetItem_KnownHash_LockHeld((PyDictObject *)op, key, value, hash); + Py_END_CRITICAL_SECTION(); + return res; +} static void delete_index_from_values(PyDictValues *values, Py_ssize_t ix) @@ -2712,9 +2909,9 @@ static void delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, PyObject *old_value) { - PyObject *old_key; + assert(can_modify_dict(mp)); - ASSERT_DICT_LOCKED(mp); + PyObject *old_key; Py_ssize_t hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); @@ -2757,30 +2954,33 @@ PyDict_DelItem(PyObject *op, PyObject *key) assert(key); Py_hash_t hash = _PyObject_HashFast(key); if (hash == -1) { - dict_unhashable_type(key); + dict_unhashable_type(op, key); return -1; } return _PyDict_DelItem_KnownHash(op, key, hash); } -static int -delitem_knownhash_lock_held(PyObject *op, PyObject *key, Py_hash_t hash) +int +_PyDict_DelItem_KnownHash_LockHeld(PyObject *op, PyObject *key, Py_hash_t hash) { - Py_ssize_t ix; - PyDictObject *mp; - PyObject *old_value; - if (!PyDict_Check(op)) { - PyErr_BadInternalCall(); + if (PyFrozenDict_Check(op)) { + frozendict_does_not_support("deletion"); + } + else { + PyErr_BadInternalCall(); + } return -1; } - ASSERT_DICT_LOCKED(op); + Py_ssize_t ix; + PyObject *old_value; + PyDictObject *mp = (PyDictObject *)op; + assert(can_modify_dict(mp)); assert(key); assert(hash != -1); - mp = (PyDictObject *)op; ix = _Py_dict_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return -1; @@ -2789,8 +2989,7 @@ delitem_knownhash_lock_held(PyObject *op, PyObject *key, Py_hash_t hash) return -1; } - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DELETED, mp, key, NULL); delitem_common(mp, hash, ix, old_value); return 0; } @@ -2800,7 +2999,7 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { int res; Py_BEGIN_CRITICAL_SECTION(op); - res = delitem_knownhash_lock_held(op, key, hash); + res = _PyDict_DelItem_KnownHash_LockHeld(op, key, hash); Py_END_CRITICAL_SECTION(); return res; } @@ -2810,19 +3009,18 @@ delitemif_lock_held(PyObject *op, PyObject *key, int (*predicate)(PyObject *value, void *arg), void *arg) { + PyDictObject *mp = _PyAnyDict_CAST(op); + assert(can_modify_dict(mp)); + Py_ssize_t ix; - PyDictObject *mp; Py_hash_t hash; PyObject *old_value; int res; - ASSERT_DICT_LOCKED(op); - assert(key); hash = PyObject_Hash(key); if (hash == -1) return -1; - mp = (PyDictObject *)op; ix = _Py_dict_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) { return -1; @@ -2836,8 +3034,7 @@ delitemif_lock_held(PyObject *op, PyObject *key, return -1; if (res > 0) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DELETED, mp, key, NULL); delitem_common(mp, hash, ix, old_value); return 1; } else { @@ -2862,27 +3059,41 @@ _PyDict_DelItemIf(PyObject *op, PyObject *key, return res; } +static void +clear_embedded_values(PyDictValues *values, Py_ssize_t nentries) +{ + PyObject *refs[SHARED_KEYS_MAX_SIZE]; + assert(nentries <= SHARED_KEYS_MAX_SIZE); + for (Py_ssize_t i = 0; i < nentries; i++) { + refs[i] = values->values[i]; + FT_ATOMIC_STORE_PTR_RELEASE(values->values[i], NULL); + } + values->size = 0; + for (Py_ssize_t i = 0; i < nentries; i++) { + Py_XDECREF(refs[i]); + } +} + static void clear_lock_held(PyObject *op) { - PyDictObject *mp; + if (!PyDict_Check(op)) { + return; + } + PyDictObject *mp = (PyDictObject *)op; + assert(can_modify_dict(mp)); + PyDictKeysObject *oldkeys; PyDictValues *oldvalues; Py_ssize_t i, n; - ASSERT_DICT_LOCKED(op); - - if (!PyDict_Check(op)) - return; - mp = ((PyDictObject *)op); oldkeys = mp->ma_keys; oldvalues = mp->ma_values; if (oldkeys == Py_EMPTY_KEYS) { return; } /* Empty the dict... */ - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyDict_NotifyEvent(interp, PyDict_EVENT_CLEARED, mp, NULL, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_CLEARED, mp, NULL, NULL); // We don't inc ref empty keys because they're immortal ensure_shared_on_resize(mp); STORE_USED(mp, 0); @@ -2891,20 +3102,20 @@ clear_lock_held(PyObject *op) assert(oldkeys->dk_refcnt == 1); dictkeys_decref(oldkeys, IS_DICT_SHARED(mp)); } + else if (oldvalues->embedded) { + clear_embedded_values(oldvalues, oldkeys->dk_nentries); + } else { + set_values(mp, NULL); + set_keys(mp, Py_EMPTY_KEYS); n = oldkeys->dk_nentries; for (i = 0; i < n; i++) { - Py_CLEAR(oldvalues->values[i]); - } - if (oldvalues->embedded) { - oldvalues->size = 0; - } - else { - set_values(mp, NULL); - set_keys(mp, Py_EMPTY_KEYS); - free_values(oldvalues, IS_DICT_SHARED(mp)); - dictkeys_decref(oldkeys, false); + PyObject *tmp = oldvalues->values[i]; + FT_ATOMIC_STORE_PTR_RELEASE(oldvalues->values[i], NULL); + Py_XDECREF(tmp); } + free_values(oldvalues, IS_DICT_SHARED(mp)); + dictkeys_decref(oldkeys, IS_DICT_SHARED(mp)); } ASSERT_CONSISTENT(mp); } @@ -2936,7 +3147,7 @@ _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject *key, *value; Py_hash_t hash; - if (!PyDict_Check(op)) + if (!PyAnyDict_Check(op)) return 0; mp = (PyDictObject *)op; @@ -3021,8 +3232,7 @@ _PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **result) { assert(PyDict_Check(mp)); - - ASSERT_DICT_LOCKED(mp); + assert(can_modify_dict(mp)); if (mp->ma_used == 0) { if (result) { @@ -3048,8 +3258,7 @@ _PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash, } assert(old_value != NULL); - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DELETED, mp, key, NULL); delitem_common(mp, hash, ix, Py_NewRef(old_value)); ASSERT_CONSISTENT(mp); @@ -3065,16 +3274,20 @@ _PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash, static int pop_lock_held(PyObject *op, PyObject *key, PyObject **result) { - ASSERT_DICT_LOCKED(op); - if (!PyDict_Check(op)) { if (result) { *result = NULL; } - PyErr_BadInternalCall(); + if (PyFrozenDict_Check(op)) { + frozendict_does_not_support("deletion"); + } + else { + PyErr_BadInternalCall(); + } return -1; } PyDictObject *dict = (PyDictObject *)op; + assert(can_modify_dict(dict)); if (dict->ma_used == 0) { if (result) { @@ -3085,7 +3298,7 @@ pop_lock_held(PyObject *op, PyObject *key, PyObject **result) Py_hash_t hash = _PyObject_HashFast(key); if (hash == -1) { - dict_unhashable_type(key); + dict_unhashable_type(op, key); if (result) { *result = NULL; } @@ -3144,9 +3357,10 @@ _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *default_value) } static PyDictObject * -dict_dict_fromkeys(PyInterpreterState *interp, PyDictObject *mp, - PyObject *iterable, PyObject *value) +dict_dict_fromkeys(PyDictObject *mp, PyObject *iterable, PyObject *value) { + assert(can_modify_dict(mp)); + PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; @@ -3161,8 +3375,7 @@ dict_dict_fromkeys(PyInterpreterState *interp, PyDictObject *mp, } while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { - if (insertdict(interp, mp, - Py_NewRef(key), hash, Py_NewRef(value))) { + if (insertdict(mp, Py_NewRef(key), hash, Py_NewRef(value))) { Py_DECREF(mp); return NULL; } @@ -3171,9 +3384,10 @@ dict_dict_fromkeys(PyInterpreterState *interp, PyDictObject *mp, } static PyDictObject * -dict_set_fromkeys(PyInterpreterState *interp, PyDictObject *mp, - PyObject *iterable, PyObject *value) +dict_set_fromkeys(PyDictObject *mp, PyObject *iterable, PyObject *value) { + assert(can_modify_dict(mp)); + Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; @@ -3187,7 +3401,7 @@ dict_set_fromkeys(PyInterpreterState *interp, PyDictObject *mp, _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(iterable); while (_PySet_NextEntryRef(iterable, &pos, &key, &hash)) { - if (insertdict(interp, mp, key, hash, Py_NewRef(value))) { + if (insertdict(mp, key, hash, Py_NewRef(value))) { Py_DECREF(mp); return NULL; } @@ -3203,31 +3417,86 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) PyObject *key; PyObject *d; int status; - PyInterpreterState *interp = _PyInterpreterState_GET(); d = _PyObject_CallNoArgs(cls); - if (d == NULL) + if (d == NULL) { return NULL; + } + // If cls is a dict or frozendict subclass with overridden constructor, + // copy the frozendict. + PyTypeObject *cls_type = _PyType_CAST(cls); + if (PyFrozenDict_Check(d) && cls_type->tp_new != frozendict_new) { + // Subclass-friendly copy + PyObject *copy; + if (PyObject_IsSubclass(cls, (PyObject*)&PyFrozenDict_Type)) { + copy = frozendict_new(cls_type, NULL, NULL); + } + else { + copy = dict_new(cls_type, NULL, NULL); + } + if (copy == NULL) { + Py_DECREF(d); + return NULL; + } + if (dict_merge(copy, d, 1, NULL) < 0) { + Py_DECREF(d); + Py_DECREF(copy); + return NULL; + } + Py_SETREF(d, copy); + } + assert(!PyFrozenDict_Check(d) || can_modify_dict((PyDictObject*)d)); if (PyDict_CheckExact(d)) { if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; Py_BEGIN_CRITICAL_SECTION2(d, iterable); - d = (PyObject *)dict_dict_fromkeys(interp, mp, iterable, value); + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); Py_END_CRITICAL_SECTION2(); return d; } + else if (PyFrozenDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION(d); + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION(); + return d; + } else if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; Py_BEGIN_CRITICAL_SECTION2(d, iterable); - d = (PyObject *)dict_set_fromkeys(interp, mp, iterable, value); + d = (PyObject *)dict_set_fromkeys(mp, iterable, value); Py_END_CRITICAL_SECTION2(); return d; } } + else if (PyFrozenDict_CheckExact(d)) { + if (PyDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION(iterable); + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION(); + return d; + } + else if (PyFrozenDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + return d; + } + else if (PyAnySet_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION(iterable); + d = (PyObject *)dict_set_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION(); + return d; + } + } it = PyObject_GetIter(iterable); if (it == NULL){ @@ -3247,7 +3516,19 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) } dict_iter_exit:; Py_END_CRITICAL_SECTION(); - } else { + } + else if (PyFrozenDict_Check(d)) { + while ((key = PyIter_Next(it)) != NULL) { + // setitem_take2_lock_held consumes a reference to key + status = setitem_take2_lock_held((PyDictObject *)d, + key, Py_NewRef(value)); + if (status < 0) { + assert(PyErr_Occurred()); + goto Fail; + } + } + } + else { while ((key = PyIter_Next(it)) != NULL) { status = PyObject_SetItem(d, key, value); Py_DECREF(key); @@ -3273,9 +3554,8 @@ static void dict_dealloc(PyObject *self) { PyDictObject *mp = (PyDictObject *)self; - PyInterpreterState *interp = _PyInterpreterState_GET(); _PyObject_ResurrectStart(self); - _PyDict_NotifyEvent(interp, PyDict_EVENT_DEALLOCATED, mp, NULL, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DEALLOCATED, mp, NULL, NULL); if (_PyObject_ResurrectEnd(self)) { return; } @@ -3308,19 +3588,18 @@ dict_dealloc(PyObject *self) static PyObject * -dict_repr_lock_held(PyObject *self) +anydict_repr_impl(PyObject *self) { PyDictObject *mp = (PyDictObject *)self; PyObject *key = NULL, *value = NULL; - ASSERT_DICT_LOCKED(mp); - int res = Py_ReprEnter((PyObject *)mp); + int res = Py_ReprEnter(self); if (res != 0) { return (res > 0 ? PyUnicode_FromString("{...}") : NULL); } if (mp->ma_used == 0) { - Py_ReprLeave((PyObject *)mp); + Py_ReprLeave(self); return PyUnicode_FromString("{}"); } @@ -3339,7 +3618,7 @@ dict_repr_lock_held(PyObject *self) Note that repr may mutate the dict. */ Py_ssize_t i = 0; int first = 1; - while (_PyDict_Next((PyObject *)mp, &i, &key, &value, NULL)) { + while (_PyDict_Next(self, &i, &key, &value, NULL)) { // Prevent repr from deleting key or value during key format. Py_INCREF(key); Py_INCREF(value); @@ -3381,18 +3660,25 @@ dict_repr_lock_held(PyObject *self) goto error; } - Py_ReprLeave((PyObject *)mp); + Py_ReprLeave(self); return PyUnicodeWriter_Finish(writer); error: - Py_ReprLeave((PyObject *)mp); + Py_ReprLeave(self); PyUnicodeWriter_Discard(writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } +static PyObject * +dict_repr_lock_held(PyObject *self) +{ + ASSERT_DICT_LOCKED((PyDictObject *)self); + return anydict_repr_impl(self); +} + static PyObject * dict_repr(PyObject *self) { @@ -3406,27 +3692,27 @@ dict_repr(PyObject *self) static Py_ssize_t dict_length(PyObject *self) { - return FT_ATOMIC_LOAD_SSIZE_RELAXED(((PyDictObject *)self)->ma_used); + return GET_USED(_PyAnyDict_CAST(self)); } -static PyObject * -dict_subscript(PyObject *self, PyObject *key) +static Py_ssize_t +frozendict_length(PyObject *self) +{ + return _PyAnyDict_CAST(self)->ma_used; +} + +PyObject * +_PyDict_SubscriptKnownHash(PyObject *self, PyObject *key, Py_hash_t hash) { PyDictObject *mp = (PyDictObject *)self; Py_ssize_t ix; - Py_hash_t hash; PyObject *value; - hash = _PyObject_HashFast(key); - if (hash == -1) { - dict_unhashable_type(key); - return NULL; - } ix = _Py_dict_lookup_threadsafe(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) { - if (!PyDict_CheckExact(mp)) { + if (!PyAnyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; missing = _PyObject_LookupSpecial( @@ -3445,8 +3731,19 @@ dict_subscript(PyObject *self, PyObject *key) return value; } -static int -dict_ass_sub(PyObject *mp, PyObject *v, PyObject *w) +PyObject * +_PyDict_Subscript(PyObject *self, PyObject *key) +{ + Py_hash_t hash = _PyObject_HashFast(key); + if (hash == -1) { + dict_unhashable_type(self, key); + return NULL; + } + return _PyDict_SubscriptKnownHash(self, key, hash); +} + +int +_PyDict_StoreSubscript(PyObject *mp, PyObject *v, PyObject *w) { if (w == NULL) return PyDict_DelItem(mp, v); @@ -3456,8 +3753,8 @@ dict_ass_sub(PyObject *mp, PyObject *v, PyObject *w) static PyMappingMethods dict_as_mapping = { dict_length, /*mp_length*/ - dict_subscript, /*mp_subscript*/ - dict_ass_sub, /*mp_ass_subscript*/ + _PyDict_Subscript, /*mp_subscript*/ + _PyDict_StoreSubscript, /*mp_ass_subscript*/ }; static PyObject * @@ -3465,7 +3762,7 @@ keys_lock_held(PyObject *dict) { ASSERT_DICT_LOCKED(dict); - if (dict == NULL || !PyDict_Check(dict)) { + if (dict == NULL || !PyAnyDict_Check(dict)) { PyErr_BadInternalCall(); return NULL; } @@ -3514,7 +3811,7 @@ values_lock_held(PyObject *dict) { ASSERT_DICT_LOCKED(dict); - if (dict == NULL || !PyDict_Check(dict)) { + if (dict == NULL || !PyAnyDict_Check(dict)) { PyErr_BadInternalCall(); return NULL; } @@ -3562,7 +3859,7 @@ items_lock_held(PyObject *dict) { ASSERT_DICT_LOCKED(dict); - if (dict == NULL || !PyDict_Check(dict)) { + if (dict == NULL || !PyAnyDict_Check(dict)) { PyErr_BadInternalCall(); return NULL; } @@ -3622,6 +3919,7 @@ PyDict_Items(PyObject *dict) } /*[clinic input] +@permit_long_summary @classmethod dict.fromkeys iterable: object @@ -3633,7 +3931,7 @@ Create a new dictionary with keys from iterable and values set to value. static PyObject * dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) -/*[clinic end generated code: output=8fb98e4b10384999 input=382ba4855d0f74c3]*/ +/*[clinic end generated code: output=8fb98e4b10384999 input=3903715eb48b287e]*/ { return _PyDict_FromKeys((PyObject *)type, iterable, value); } @@ -3642,17 +3940,17 @@ dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) static int dict_update_arg(PyObject *self, PyObject *arg) { - if (PyDict_CheckExact(arg)) { - return PyDict_Merge(self, arg, 1); + if (PyAnyDict_CheckExact(arg)) { + return dict_merge(self, arg, 1, NULL); } int has_keys = PyObject_HasAttrWithError(arg, &_Py_ID(keys)); if (has_keys < 0) { return -1; } if (has_keys) { - return PyDict_Merge(self, arg, 1); + return dict_merge(self, arg, 1, NULL); } - return PyDict_MergeFromSeq2(self, arg, 1); + return dict_merge_from_seq2(self, arg, 1); } static int @@ -3671,7 +3969,7 @@ dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) - result = PyDict_Merge(self, kwds, 1); + result = dict_merge(self, kwds, 1, NULL); else result = -1; } @@ -3708,7 +4006,7 @@ merge_from_seq2_lock_held(PyObject *d, PyObject *seq2, int override) PyObject *fast; /* item as a 2-tuple or 2-list */ assert(d != NULL); - assert(PyDict_Check(d)); + assert(PyAnyDict_Check(d)); assert(seq2 != NULL); it = PyObject_GetIter(seq2); @@ -3785,8 +4083,8 @@ merge_from_seq2_lock_held(PyObject *d, PyObject *seq2, int override) return Py_SAFE_DOWNCAST(i, Py_ssize_t, int); } -int -PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) +static int +dict_merge_from_seq2(PyObject *d, PyObject *seq2, int override) { int res; Py_BEGIN_CRITICAL_SECTION(d); @@ -3796,20 +4094,38 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) return res; } +int +PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) +{ + assert(d != NULL); + assert(seq2 != NULL); + if (!PyDict_Check(d)) { + if (PyFrozenDict_Check(d)) { + frozendict_does_not_support("assignment"); + } + else { + PyErr_BadInternalCall(); + } + return -1; + } + + return dict_merge_from_seq2(d, seq2, override); +} + static int -dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *other, int override) +dict_dict_merge(PyDictObject *mp, PyDictObject *other, int override, PyObject **dupkey) { - ASSERT_DICT_LOCKED(mp); + assert(can_modify_dict(mp)); ASSERT_DICT_LOCKED(other); if (other == mp || other->ma_used == 0) /* a.update(a) or a.update({}); nothing to do */ return 0; if (mp->ma_used == 0) { - /* Since the target dict is empty, PyDict_GetItem() - * always returns NULL. Setting override to 1 - * skips the unnecessary test. - */ + /* Since the target dict is empty, _PyDict_Contains_KnownHash() + * always returns 0. Setting override to 1 + * skips the unnecessary test. + */ override = 1; PyDictKeysObject *okeys = other->ma_keys; @@ -3820,7 +4136,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe (DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE || USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used) ) { - _PyDict_NotifyEvent(interp, PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL); PyDictKeysObject *keys = clone_combined_dict_keys(other); if (keys == NULL) return -1; @@ -3861,21 +4177,18 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe Py_INCREF(key); Py_INCREF(value); if (override == 1) { - err = insertdict(interp, mp, - Py_NewRef(key), hash, Py_NewRef(value)); + err = insertdict(mp, Py_NewRef(key), hash, Py_NewRef(value)); } else { err = _PyDict_Contains_KnownHash((PyObject *)mp, key, hash); if (err == 0) { - err = insertdict(interp, mp, - Py_NewRef(key), hash, Py_NewRef(value)); + err = insertdict(mp, Py_NewRef(key), hash, Py_NewRef(value)); } else if (err > 0) { - if (override != 0) { - _PyErr_SetKeyError(key); + if (dupkey != NULL) { + *dupkey = key; Py_DECREF(value); - Py_DECREF(key); - return -1; + return -2; } err = 0; } @@ -3895,28 +4208,19 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe } static int -dict_merge(PyInterpreterState *interp, PyObject *a, PyObject *b, int override) +dict_merge(PyObject *a, PyObject *b, int override, PyObject **dupkey) { - PyDictObject *mp, *other; - + assert(a != NULL); + assert(b != NULL); assert(0 <= override && override <= 2); - /* We accept for the argument either a concrete dictionary object, - * or an abstract "mapping" object. For the former, we can do - * things quite efficiently. For the latter, we only require that - * PyMapping_Keys() and PyObject_GetItem() be supported. - */ - if (a == NULL || !PyDict_Check(a) || b == NULL) { - PyErr_BadInternalCall(); - return -1; - } - mp = (PyDictObject*)a; + PyDictObject *mp = _PyAnyDict_CAST(a); int res = 0; - if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == dict_iter)) { - other = (PyDictObject*)b; + if (PyAnyDict_Check(b) && (Py_TYPE(b)->tp_iter == dict_iter)) { + PyDictObject *other = (PyDictObject*)b; int res; Py_BEGIN_CRITICAL_SECTION2(a, b); - res = dict_dict_merge(interp, (PyDictObject *)a, other, override); + res = dict_dict_merge((PyDictObject *)a, other, override, dupkey); ASSERT_CONSISTENT(a); Py_END_CRITICAL_SECTION2(); return res; @@ -3948,18 +4252,21 @@ dict_merge(PyInterpreterState *interp, PyObject *a, PyObject *b, int override) for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { if (override != 1) { - status = PyDict_Contains(a, key); + status = dict_contains(a, key); if (status != 0) { if (status > 0) { - if (override == 0) { + if (dupkey == NULL) { Py_DECREF(key); continue; } - _PyErr_SetKeyError(key); + *dupkey = key; + res = -2; + } + else { + Py_DECREF(key); + res = -1; } - Py_DECREF(key); Py_DECREF(iter); - res = -1; goto slow_exit; } } @@ -3994,26 +4301,43 @@ dict_merge(PyInterpreterState *interp, PyObject *a, PyObject *b, int override) } } +static int +dict_merge_api(PyObject *a, PyObject *b, int override, PyObject **dupkey) +{ + /* We accept for the argument either a concrete dictionary object, + * or an abstract "mapping" object. For the former, we can do + * things quite efficiently. For the latter, we only require that + * PyMapping_Keys() and PyObject_GetItem() be supported. + */ + if (a == NULL || !PyDict_Check(a) || b == NULL) { + if (a != NULL && PyFrozenDict_Check(a)) { + frozendict_does_not_support("assignment"); + } + else { + PyErr_BadInternalCall(); + } + return -1; + } + return dict_merge(a, b, override, dupkey); +} + int PyDict_Update(PyObject *a, PyObject *b) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return dict_merge(interp, a, b, 1); + return dict_merge_api(a, b, 1, NULL); } int PyDict_Merge(PyObject *a, PyObject *b, int override) { - PyInterpreterState *interp = _PyInterpreterState_GET(); /* XXX Deprecate override not in (0, 1). */ - return dict_merge(interp, a, b, override != 0); + return dict_merge_api(a, b, override != 0, NULL); } int -_PyDict_MergeEx(PyObject *a, PyObject *b, int override) +_PyDict_MergeUniq(PyObject *a, PyObject *b, PyObject **dupkey) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return dict_merge(interp, a, b, override); + return dict_merge_api(a, b, 2, dupkey); } /*[clinic input] @@ -4051,18 +4375,25 @@ copy_values(PyDictValues *values) } static PyObject * -copy_lock_held(PyObject *o) +copy_lock_held(PyObject *o, int as_frozendict) { PyObject *copy; PyDictObject *mp; - PyInterpreterState *interp = _PyInterpreterState_GET(); - ASSERT_DICT_LOCKED(o); + // frozendict is immutable and so doesn't need critical section + if (!PyFrozenDict_Check(o)) { + ASSERT_DICT_LOCKED(o); + } mp = (PyDictObject *)o; if (mp->ma_used == 0) { /* The dict is empty; just return a new dict. */ - return PyDict_New(); + if (as_frozendict) { + return PyFrozenDict_New(NULL); + } + else { + return PyDict_New(); + } } if (_PyDict_HasSplitTable(mp)) { @@ -4071,7 +4402,13 @@ copy_lock_held(PyObject *o) if (newvalues == NULL) { return PyErr_NoMemory(); } - split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type); + if (as_frozendict) { + split_copy = (PyDictObject *)PyObject_GC_New(PyFrozenDictObject, + &PyFrozenDict_Type); + } + else { + split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type); + } if (split_copy == NULL) { free_values(newvalues, false); return NULL; @@ -4084,6 +4421,10 @@ copy_lock_held(PyObject *o) split_copy->ma_used = mp->ma_used; split_copy->_ma_watcher_tag = 0; dictkeys_incref(mp->ma_keys); + if (as_frozendict) { + PyFrozenDictObject *frozen = (PyFrozenDictObject *)split_copy; + frozen->ma_hash = -1; + } _PyObject_GC_TRACK(split_copy); return (PyObject *)split_copy; } @@ -4110,9 +4451,15 @@ copy_lock_held(PyObject *o) if (keys == NULL) { return NULL; } - PyDictObject *new = (PyDictObject *)new_dict(keys, NULL, 0, 0); + PyDictObject *new; + if (as_frozendict) { + new = (PyDictObject *)new_frozendict(keys, NULL, 0, 0); + } + else { + new = (PyDictObject *)new_dict(keys, NULL, 0, 0); + } if (new == NULL) { - /* In case of an error, `new_dict()` takes care of + /* In case of an error, new_dict()/new_frozendict() takes care of cleaning up `keys`. */ return NULL; } @@ -4122,10 +4469,15 @@ copy_lock_held(PyObject *o) return (PyObject *)new; } - copy = PyDict_New(); + if (as_frozendict) { + copy = PyFrozenDict_New(NULL); + } + else { + copy = PyDict_New(); + } if (copy == NULL) return NULL; - if (dict_merge(interp, copy, o, 1) == 0) + if (dict_merge(copy, o, 1, NULL) == 0) return copy; Py_DECREF(copy); return NULL; @@ -4141,21 +4493,57 @@ PyDict_Copy(PyObject *o) PyObject *res; Py_BEGIN_CRITICAL_SECTION(o); + res = copy_lock_held(o, 0); + Py_END_CRITICAL_SECTION(); + return res; +} - res = copy_lock_held(o); +// Similar to PyDict_Copy(), but return a frozendict if the argument +// is a frozendict. +static PyObject * +anydict_copy(PyObject *o) +{ + assert(PyAnyDict_Check(o)); - Py_END_CRITICAL_SECTION(); + PyObject *res; + if (PyFrozenDict_Check(o)) { + res = copy_lock_held(o, 1); + } + else { + Py_BEGIN_CRITICAL_SECTION(o); + res = copy_lock_held(o, 0); + Py_END_CRITICAL_SECTION(); + } + return res; +} + +// Similar to PyDict_Copy(), but accept also frozendict: +// convert frozendict to a new dict. +PyObject* +_PyDict_CopyAsDict(PyObject *o) +{ + assert(PyAnyDict_Check(o)); + + PyObject *res; + if (PyFrozenDict_Check(o)) { + res = copy_lock_held(o, 0); + } + else { + Py_BEGIN_CRITICAL_SECTION(o); + res = copy_lock_held(o, 0); + Py_END_CRITICAL_SECTION(); + } return res; } Py_ssize_t PyDict_Size(PyObject *mp) { - if (mp == NULL || !PyDict_Check(mp)) { + if (mp == NULL || !PyAnyDict_Check(mp)) { PyErr_BadInternalCall(); return -1; } - return FT_ATOMIC_LOAD_SSIZE_RELAXED(((PyDictObject *)mp)->ma_used); + return GET_USED((PyDictObject *)mp); } /* Return 1 if dicts equal, 0 if not, -1 if error. @@ -4241,7 +4629,7 @@ dict_richcompare(PyObject *v, PyObject *w, int op) int cmp; PyObject *res; - if (!PyDict_Check(v) || !PyDict_Check(w)) { + if (!PyAnyDict_Check(v) || !PyAnyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { @@ -4270,7 +4658,7 @@ static PyObject * dict___contains___impl(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=1b314e6da7687dae input=fe1cb42ad831e820]*/ { - int contains = PyDict_Contains((PyObject *)self, key); + int contains = dict_contains((PyObject *)self, key); if (contains < 0) { return NULL; } @@ -4300,7 +4688,7 @@ dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) hash = _PyObject_HashFast(key); if (hash == -1) { - dict_unhashable_type(key); + dict_unhashable_type((PyObject*)self, key); return NULL; } ix = _Py_dict_lookup_threadsafe(self, key, hash, &val); @@ -4316,24 +4704,28 @@ static int dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_value, PyObject **result, int incref_result) { - PyDictObject *mp = (PyDictObject *)d; - PyObject *value; - Py_hash_t hash; - PyInterpreterState *interp = _PyInterpreterState_GET(); - - ASSERT_DICT_LOCKED(d); - if (!PyDict_Check(d)) { - PyErr_BadInternalCall(); + if (PyFrozenDict_Check(d)) { + frozendict_does_not_support("assignment"); + } + else { + PyErr_BadInternalCall(); + } if (result) { *result = NULL; } return -1; } + assert(can_modify_dict((PyDictObject*)d)); + + PyDictObject *mp = (PyDictObject *)d; + PyObject *value; + Py_hash_t hash; + Py_ssize_t ix; hash = _PyObject_HashFast(key); if (hash == -1) { - dict_unhashable_type(key); + dict_unhashable_type(d, key); if (result) { *result = NULL; } @@ -4341,7 +4733,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu } if (mp->ma_keys == Py_EMPTY_KEYS) { - if (insert_to_emptydict(interp, mp, Py_NewRef(key), hash, + if (insert_to_emptydict(mp, Py_NewRef(key), hash, Py_NewRef(default_value)) < 0) { if (result) { *result = NULL; @@ -4354,22 +4746,13 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu return 0; } - if (!PyUnicode_CheckExact(key) && DK_IS_UNICODE(mp->ma_keys)) { - if (insertion_resize(mp, 0) < 0) { - if (result) { - *result = NULL; - } - return -1; - } - } - - if (_PyDict_HasSplitTable(mp)) { - Py_ssize_t ix = insert_split_key(mp->ma_keys, key, hash); + if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) { + ix = insert_split_key(mp->ma_keys, key, hash); if (ix != DKIX_EMPTY) { PyObject *value = mp->ma_values->values[ix]; int already_present = value != NULL; if (!already_present) { - insert_split_value(interp, mp, key, default_value, ix); + _PyDict_InsertSplitValue(mp, key, default_value, ix); value = default_value; } if (result) { @@ -4377,28 +4760,23 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu } return already_present; } - - /* No space in shared keys. Resize and continue below. */ - if (insertion_resize(mp, 1) < 0) { - goto error; - } + // No space in shared keys. Go to insert_combined_dict() below. } - - assert(!_PyDict_HasSplitTable(mp)); - - Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &value); - if (ix == DKIX_ERROR) { - if (result) { - *result = NULL; + else { + ix = _Py_dict_lookup(mp, key, hash, &value); + if (ix == DKIX_ERROR) { + if (result) { + *result = NULL; + } + return -1; } - return -1; } if (ix == DKIX_EMPTY) { - assert(!_PyDict_HasSplitTable(mp)); value = default_value; - if (insert_combined_dict(interp, mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) { + // See comment to this function in insertdict. + if (insert_combined_dict(mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) { Py_DECREF(key); Py_DECREF(value); if (result) { @@ -4422,12 +4800,6 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu *result = incref_result ? Py_NewRef(value) : value; } return 1; - -error: - if (result) { - *result = NULL; - } - return -1; } int @@ -4490,6 +4862,7 @@ dict_clear_impl(PyDictObject *self) } /*[clinic input] +@permit_long_summary dict.pop key: object @@ -4504,7 +4877,7 @@ raise a KeyError. static PyObject * dict_pop_impl(PyDictObject *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=3abb47b89f24c21c input=e221baa01044c44c]*/ +/*[clinic end generated code: output=3abb47b89f24c21c input=d409c7eb2de67e38]*/ { return dict_pop_default((PyObject*)self, key, default_value); } @@ -4523,11 +4896,10 @@ static PyObject * dict_popitem_impl(PyDictObject *self) /*[clinic end generated code: output=e65fcb04420d230d input=ef28b4da5f0f762e]*/ { - Py_ssize_t i, j; - PyObject *res; - PyInterpreterState *interp = _PyInterpreterState_GET(); + assert(can_modify_dict(self)); - ASSERT_DICT_LOCKED(self); + Py_ssize_t i, j; + PyObject *res; /* Allocate the result tuple before checking the size. Believe it * or not, this allocation could trigger a garbage collection which @@ -4567,7 +4939,7 @@ dict_popitem_impl(PyDictObject *self) assert(i >= 0); key = ep0[i].me_key; - _PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, self, key, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DELETED, self, key, NULL); hash = unicode_get_hash(key); value = ep0[i].me_value; STORE_KEY(&ep0[i], NULL); @@ -4582,7 +4954,7 @@ dict_popitem_impl(PyDictObject *self) assert(i >= 0); key = ep0[i].me_key; - _PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, self, key, NULL); + _PyDict_NotifyEvent(PyDict_EVENT_DELETED, self, key, NULL); hash = ep0[i].me_hash; value = ep0[i].me_value; STORE_KEY(&ep0[i], NULL); @@ -4613,10 +4985,8 @@ dict_traverse(PyObject *op, visitproc visit, void *arg) if (DK_IS_UNICODE(keys)) { if (_PyDict_HasSplitTable(mp)) { - if (!mp->ma_values->embedded) { - for (i = 0; i < n; i++) { - Py_VISIT(mp->ma_values->values[i]); - } + for (i = 0; i < n; i++) { + Py_VISIT(mp->ma_values->values[i]); } } else { @@ -4647,9 +5017,11 @@ dict_tp_clear(PyObject *op) static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); -static Py_ssize_t -sizeof_lock_held(PyDictObject *mp) +Py_ssize_t +_PyDict_SizeOf_LockHeld(PyDictObject *mp) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp); + size_t res = _PyObject_SIZE(Py_TYPE(mp)); if (_PyDict_HasSplitTable(mp)) { res += shared_keys_usable_size(mp->ma_keys) * sizeof(PyObject*); @@ -4663,12 +5035,21 @@ sizeof_lock_held(PyDictObject *mp) return (Py_ssize_t)res; } +void +_PyDict_ClearKeysVersionLockHeld(PyObject *op) +{ + PyDictObject *mp = _PyAnyDict_CAST(op); + assert(can_modify_dict(mp)); + + FT_ATOMIC_STORE_UINT32_RELAXED(mp->ma_keys->dk_version, 0); +} + Py_ssize_t _PyDict_SizeOf(PyDictObject *mp) { Py_ssize_t res; Py_BEGIN_CRITICAL_SECTION(mp); - res = sizeof_lock_held(mp); + res = _PyDict_SizeOf_LockHeld(mp); Py_END_CRITICAL_SECTION(); return res; @@ -4698,13 +5079,13 @@ dict___sizeof___impl(PyDictObject *self) return PyLong_FromSsize_t(_PyDict_SizeOf(self)); } -static PyObject * -dict_or(PyObject *self, PyObject *other) +PyObject * +_PyDict_Or(PyObject *self, PyObject *other) { - if (!PyDict_Check(self) || !PyDict_Check(other)) { + if (!PyAnyDict_Check(self) || !PyAnyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } - PyObject *new = PyDict_Copy(self); + PyObject *new = anydict_copy(self); if (new == NULL) { return NULL; } @@ -4716,7 +5097,30 @@ dict_or(PyObject *self, PyObject *other) } static PyObject * -dict_ior(PyObject *self, PyObject *other) +frozendict_or(PyObject *self, PyObject *other) +{ + if (PyFrozenDict_CheckExact(self)) { + // frozendict() | frozendict(...) => frozendict(...) + if (GET_USED((PyDictObject *)self) == 0 + && PyFrozenDict_CheckExact(other)) + { + return Py_NewRef(other); + } + + // frozendict(...) | frozendict() => frozendict(...) + if (PyAnyDict_CheckExact(other) + && GET_USED((PyDictObject *)other) == 0) + { + return Py_NewRef(self); + } + } + + return _PyDict_Or(self, other); +} + + +PyObject * +_PyDict_IOr(PyObject *self, PyObject *other) { if (dict_update_arg(self, other)) { return NULL; @@ -4737,7 +5141,7 @@ In either case, this is followed by: for k in F: D[k] = F[k]"); static PyMethodDef mapp_methods[] = { DICT___CONTAINS___METHODDEF - {"__getitem__", dict_subscript, METH_O | METH_COEXIST, + {"__getitem__", _PyDict_Subscript, METH_O | METH_COEXIST, getitem__doc__}, DICT___SIZEOF___METHODDEF DICT_GET_METHODDEF @@ -4753,23 +5157,35 @@ static PyMethodDef mapp_methods[] = { DICT_CLEAR_METHODDEF DICT_COPY_METHODDEF DICT___REVERSED___METHODDEF - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("dicts are generic over two types, signifying (respectively) the types of their keys and values")}, {NULL, NULL} /* sentinel */ }; -/* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */ -int -PyDict_Contains(PyObject *op, PyObject *key) +static int +dict_contains(PyObject *op, PyObject *key) { Py_hash_t hash = _PyObject_HashFast(key); if (hash == -1) { - dict_unhashable_type(key); + dict_unhashable_type(op, key); return -1; } return _PyDict_Contains_KnownHash(op, key, hash); } +/* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */ +int +PyDict_Contains(PyObject *op, PyObject *key) +{ + if (!PyAnyDict_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + + return dict_contains(op, key); +} + int PyDict_ContainsString(PyObject *op, const char *key) { @@ -4786,7 +5202,7 @@ PyDict_ContainsString(PyObject *op, const char *key) int _PyDict_Contains_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { - PyDictObject *mp = (PyDictObject *)op; + PyDictObject *mp = _PyAnyDict_CAST(op); PyObject *value; Py_ssize_t ix; @@ -4806,16 +5222,6 @@ _PyDict_Contains_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) return 0; } -int -_PyDict_ContainsId(PyObject *op, _Py_Identifier *key) -{ - PyObject *kv = _PyUnicode_FromId(key); /* borrowed */ - if (kv == NULL) { - return -1; - } - return PyDict_Contains(op, kv); -} - /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ @@ -4825,14 +5231,14 @@ static PySequenceMethods dict_as_sequence = { 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ - PyDict_Contains, /* sq_contains */ + dict_contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; static PyNumberMethods dict_as_number = { - .nb_or = dict_or, - .nb_inplace_or = dict_ior, + .nb_or = _PyDict_Or, + .nb_inplace_or = _PyDict_IOr, }; static PyObject * @@ -4890,7 +5296,48 @@ dict_vectorcall(PyObject *type, PyObject * const*args, } if (kwnames != NULL) { for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwnames); i++) { - if (PyDict_SetItem(self, PyTuple_GET_ITEM(kwnames, i), args[i]) < 0) { + PyObject *key = PyTuple_GET_ITEM(kwnames, i); // borrowed + if (PyDict_SetItem(self, key, args[i]) < 0) { + Py_DECREF(self); + return NULL; + } + } + } + return self; +} + +static PyObject * +frozendict_vectorcall(PyObject *type, PyObject * const*args, + size_t nargsf, PyObject *kwnames) +{ + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (!_PyArg_CheckPositional("frozendict", nargs, 0, 1)) { + return NULL; + } + + if (nargs == 1 && kwnames == NULL + && PyFrozenDict_CheckExact(args[0]) + && Py_Is((PyTypeObject*)type, &PyFrozenDict_Type)) + { + // frozendict(frozendict) returns the same object unmodified + return Py_NewRef(args[0]); + } + + PyObject *self = frozendict_new(_PyType_CAST(type), NULL, NULL); + if (self == NULL) { + return NULL; + } + if (nargs == 1) { + if (dict_update_arg(self, args[0]) < 0) { + Py_DECREF(self); + return NULL; + } + args++; + } + if (kwnames != NULL) { + for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwnames); i++) { + PyObject *key = PyTuple_GET_ITEM(kwnames, i); // borrowed + if (_PyAnyDict_SetItem(self, key, args[i]) < 0) { Py_DECREF(self); return NULL; } @@ -4963,6 +5410,7 @@ PyTypeObject PyDict_Type = { .tp_version_tag = _Py_TYPE_VERSION_DICT, }; + /* For backward compatibility with old dictionary interface */ PyObject * @@ -4996,16 +5444,6 @@ PyDict_GetItemStringRef(PyObject *v, const char *key, PyObject **result) return res; } -int -_PyDict_SetItemId(PyObject *v, _Py_Identifier *key, PyObject *item) -{ - PyObject *kv; - kv = _PyUnicode_FromId(key); /* borrowed */ - if (kv == NULL) - return -1; - return PyDict_SetItem(v, kv, item); -} - int PyDict_SetItemString(PyObject *v, const char *key, PyObject *item) { @@ -5021,15 +5459,6 @@ PyDict_SetItemString(PyObject *v, const char *key, PyObject *item) return err; } -int -_PyDict_DelItemId(PyObject *v, _Py_Identifier *key) -{ - PyObject *kv = _PyUnicode_FromId(key); /* borrowed */ - if (kv == NULL) - return -1; - return PyDict_DelItem(v, kv); -} - int PyDict_DelItemString(PyObject *v, const char *key) { @@ -5064,7 +5493,7 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype) return NULL; } di->di_dict = (PyDictObject*)Py_NewRef(dict); - used = FT_ATOMIC_LOAD_SSIZE_RELAXED(dict->ma_used); + used = GET_USED(dict); di->di_used = used; di->len = used; if (itertype == &PyDictRevIterKey_Type || @@ -5082,7 +5511,7 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype) } if (itertype == &PyDictIterItem_Type || itertype == &PyDictRevIterItem_Type) { - di->di_result = PyTuple_Pack(2, Py_None, Py_None); + di->di_result = _PyTuple_FromPairSteal(Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; @@ -5120,7 +5549,7 @@ dictiter_len(PyObject *self, PyObject *Py_UNUSED(ignored)) { dictiterobject *di = (dictiterobject *)self; Py_ssize_t len = 0; - if (di->di_dict != NULL && di->di_used == FT_ATOMIC_LOAD_SSIZE_RELAXED(di->di_dict->ma_used)) + if (di->di_dict != NULL && di->di_used == GET_USED(di->di_dict)) len = FT_ATOMIC_LOAD_SSIZE_RELAXED(di->len); return PyLong_FromSize_t(len); } @@ -5157,7 +5586,7 @@ dictiter_iternextkey_lock_held(PyDictObject *d, PyObject *self) Py_ssize_t i; PyDictKeysObject *k; - assert (PyDict_Check(d)); + assert (PyAnyDict_Check(d)); ASSERT_DICT_LOCKED(d); if (di->di_used != d->ma_used) { @@ -5281,7 +5710,7 @@ dictiter_iternextvalue_lock_held(PyDictObject *d, PyObject *self) PyObject *value; Py_ssize_t i; - assert (PyDict_Check(d)); + assert (PyAnyDict_Check(d)); ASSERT_DICT_LOCKED(d); if (di->di_used != d->ma_used) { @@ -5403,7 +5832,7 @@ dictiter_iternextitem_lock_held(PyDictObject *d, PyObject *self, PyObject *key, *value; Py_ssize_t i; - assert (PyDict_Check(d)); + assert (PyAnyDict_Check(d)); ASSERT_DICT_LOCKED(d); if (di->di_used != d->ma_used) { @@ -5509,7 +5938,7 @@ dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self, Py_ssize_t i; PyDictKeysObject *k; - assert (PyDict_Check(d)); + assert (PyAnyDict_Check(d)); if (di->di_used != _Py_atomic_load_ssize_relaxed(&d->ma_used)) { PyErr_SetString(PyExc_RuntimeError, @@ -5524,7 +5953,7 @@ dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self, k = _Py_atomic_load_ptr_acquire(&d->ma_keys); assert(i >= 0); if (_PyDict_HasSplitTable(d)) { - PyDictValues *values = _Py_atomic_load_ptr_relaxed(&d->ma_values); + PyDictValues *values = _Py_atomic_load_ptr_consume(&d->ma_values); if (values == NULL) { goto concurrent_modification; } @@ -5609,22 +6038,10 @@ dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self, #endif -static bool -has_unique_reference(PyObject *op) -{ -#ifdef Py_GIL_DISABLED - return (_Py_IsOwnedByCurrentThread(op) && - op->ob_ref_local == 1 && - _Py_atomic_load_ssize_relaxed(&op->ob_ref_shared) == 0); -#else - return Py_REFCNT(op) == 1; -#endif -} - static bool acquire_iter_result(PyObject *result) { - if (has_unique_reference(result)) { + if (_PyObject_IsUniquelyReferenced(result)) { Py_INCREF(result); return true; } @@ -5660,11 +6077,7 @@ dictiter_iternextitem(PyObject *self) _PyTuple_Recycle(result); } else { - result = PyTuple_New(2); - if (result == NULL) - return NULL; - PyTuple_SET_ITEM(result, 0, key); - PyTuple_SET_ITEM(result, 1, value); + result = _PyTuple_FromPairSteal(key, value); } return result; } @@ -5712,7 +6125,7 @@ dictreviter_iter_lock_held(PyDictObject *d, PyObject *self) { dictiterobject *di = (dictiterobject *)self; - assert (PyDict_Check(d)); + assert (PyAnyDict_Check(d)); ASSERT_DICT_LOCKED(d); if (di->di_used != d->ma_used) { @@ -5770,7 +6183,7 @@ dictreviter_iter_lock_held(PyDictObject *d, PyObject *self) } else if (Py_IS_TYPE(di, &PyDictRevIterItem_Type)) { result = di->di_result; - if (Py_REFCNT(result) == 1) { + if (_PyObject_IsUniquelyReferenced(result)) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, Py_NewRef(key)); @@ -5783,12 +6196,7 @@ dictreviter_iter_lock_held(PyDictObject *d, PyObject *self) _PyTuple_Recycle(result); } else { - result = PyTuple_New(2); - if (result == NULL) { - return NULL; - } - PyTuple_SET_ITEM(result, 0, Py_NewRef(key)); - PyTuple_SET_ITEM(result, 1, Py_NewRef(value)); + result = _PyTuple_FromPair(key, value); } return result; } @@ -5842,7 +6250,7 @@ static PyObject * dict___reversed___impl(PyDictObject *self) /*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/ { - assert (PyDict_Check(self)); + assert (PyAnyDict_Check(self)); return dictiter_new(self, &PyDictRevIterKey_Type); } @@ -5915,7 +6323,7 @@ dictview_len(PyObject *self) _PyDictViewObject *dv = (_PyDictViewObject *)self; Py_ssize_t len = 0; if (dv->dv_dict != NULL) - len = FT_ATOMIC_LOAD_SSIZE_RELAXED(dv->dv_dict->ma_used); + len = GET_USED(dv->dv_dict); return len; } @@ -5927,7 +6335,7 @@ _PyDictView_New(PyObject *dict, PyTypeObject *type) PyErr_BadInternalCall(); return NULL; } - if (!PyDict_Check(dict)) { + if (!PyAnyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", @@ -6093,7 +6501,7 @@ dictkeys_contains(PyObject *self, PyObject *obj) _PyDictViewObject *dv = (_PyDictViewObject *)self; if (dv->dv_dict == NULL) return 0; - return PyDict_Contains((PyObject *)dv->dv_dict, obj); + return dict_contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { @@ -6117,7 +6525,7 @@ dictviews_to_set(PyObject *self) if (PyDictKeys_Check(self)) { // PySet_New() has fast path for the dict object. PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; - if (PyDict_CheckExact(dict)) { + if (PyAnyDict_CheckExact(dict)) { left = dict; } } @@ -6252,7 +6660,7 @@ dictitems_xor_lock_held(PyObject *d1, PyObject *d2) ASSERT_DICT_LOCKED(d1); ASSERT_DICT_LOCKED(d2); - PyObject *temp_dict = copy_lock_held(d1); + PyObject *temp_dict = copy_lock_held(d1, 0); if (temp_dict == NULL) { return NULL; } @@ -6281,18 +6689,22 @@ dictitems_xor_lock_held(PyObject *d1, PyObject *d2) else { Py_INCREF(val1); to_delete = PyObject_RichCompareBool(val1, val2, Py_EQ); + Py_CLEAR(val1); if (to_delete < 0) { goto error; } } if (to_delete) { + Py_CLEAR(val2); if (_PyDict_DelItem_KnownHash(temp_dict, key, hash) < 0) { goto error; } + Py_CLEAR(key); } else { - PyObject *pair = PyTuple_Pack(2, key, val2); + PyObject *pair = _PyTuple_FromPairSteal(key, val2); + key = val2 = NULL; if (pair == NULL) { goto error; } @@ -6302,11 +6714,7 @@ dictitems_xor_lock_held(PyObject *d1, PyObject *d2) } Py_DECREF(pair); } - Py_DECREF(key); - Py_XDECREF(val1); - Py_DECREF(val2); } - key = val1 = val2 = NULL; PyObject *remaining_pairs = PyObject_CallMethodNoArgs( temp_dict, &_Py_ID(items)); @@ -6718,16 +7126,24 @@ dictvalues_reversed(PyObject *self, PyObject *Py_UNUSED(ignored)) PyDictKeysObject * _PyDict_NewKeysForClass(PyHeapTypeObject *cls) { - PyDictKeysObject *keys = new_keys_object(NEXT_LOG2_SHARED_KEYS_MAX_SIZE, 1); - if (keys == NULL) { + int log2_bytes = get_log2_bytes(NEXT_LOG2_SHARED_KEYS_MAX_SIZE); + Py_ssize_t usable = USABLE_FRACTION((size_t)1<<NEXT_LOG2_SHARED_KEYS_MAX_SIZE); + + struct _instancekeysobject *shared_keys = + PyMem_Malloc(sizeof(struct _instancekeysobject) + + ((size_t)1 << log2_bytes) + + sizeof(PyDictUnicodeEntry) * usable); + if (shared_keys == NULL) { PyErr_Clear(); + return NULL; } - else { - assert(keys->dk_nentries == 0); - /* Set to max size+1 as it will shrink by one before each new object */ - keys->dk_usable = SHARED_KEYS_MAX_SIZE; - keys->dk_kind = DICT_KEYS_SPLIT; - } + + shared_keys->dsk_owning_type = (PyTypeObject *)cls; + PyDictKeysObject* keys = &shared_keys->dsk_keys; + init_keys_object(keys, NEXT_LOG2_SHARED_KEYS_MAX_SIZE, log2_bytes, DICT_KEYS_SPLIT, + SHARED_KEYS_MAX_SIZE, sizeof(PyDictUnicodeEntry)); + assert(keys->dk_nentries == 0); + /* Set to max size+1 as it will shrink by one before each new object */ if (cls->ht_type.tp_dict) { PyObject *attrs = PyDict_GetItem(cls->ht_type.tp_dict, &_Py_ID(__static_attributes__)); if (attrs != NULL && PyTuple_Check(attrs)) { @@ -6745,6 +7161,25 @@ _PyDict_NewKeysForClass(PyHeapTypeObject *cls) return keys; } +void +_PyDict_RemoveKeysForClass(PyHeapTypeObject *cls) +{ + struct _instancekeysobject *shared_keys = _PyDictKeys_AsSharedKeys(cls->ht_cached_keys); + FT_ATOMIC_STORE_PTR_RELEASE(shared_keys->dsk_owning_type, NULL); + + _PyDictKeys_DecRef(cls->ht_cached_keys); +} + +void +_PyDict_SplitKeysInvalidated(PyDictKeysObject* keys) +{ + struct _instancekeysobject *shared_keys = _PyDictKeys_AsSharedKeys(keys); + PyTypeObject *type = FT_ATOMIC_LOAD_PTR_ACQUIRE(shared_keys->dsk_owning_type); + if (type) { + PyType_Modified(type); + } +} + void _PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp) { @@ -6847,13 +7282,28 @@ _PyObject_MaterializeManagedDict(PyObject *obj) int _PyDict_SetItem_LockHeld(PyDictObject *dict, PyObject *name, PyObject *value) { + if (!PyDict_Check(dict)) { + if (PyFrozenDict_Check(dict)) { + if (value == NULL) { + frozendict_does_not_support("deletion"); + } + else { + frozendict_does_not_support("assignment"); + } + } + else { + PyErr_BadInternalCall(); + } + return -1; + } + if (value == NULL) { Py_hash_t hash = _PyObject_HashFast(name); if (hash == -1) { - dict_unhashable_type(name); + dict_unhashable_type((PyObject*)dict, name); return -1; } - return delitem_knownhash_lock_held((PyObject *)dict, name, hash); + return _PyDict_DelItem_KnownHash_LockHeld((PyObject *)dict, name, hash); } else { return setitem_lock_held(dict, name, value); } @@ -6927,15 +7377,15 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values, PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%U'", Py_TYPE(obj)->tp_name, name); + (void)_PyObject_SetAttributeErrorContext(obj, name); return -1; } if (dict) { - PyInterpreterState *interp = _PyInterpreterState_GET(); PyDict_WatchEvent event = (old_value == NULL ? PyDict_EVENT_ADDED : value == NULL ? PyDict_EVENT_DELETED : PyDict_EVENT_MODIFIED); - _PyDict_NotifyEvent(interp, event, dict, name, value); + _PyDict_NotifyEvent(event, dict, name, value); } FT_ATOMIC_STORE_PTR_RELEASE(values->values[ix], Py_XNewRef(value)); @@ -7120,7 +7570,7 @@ _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name, PyObject **attr Py_BEGIN_CRITICAL_SECTION(dict); if (dict->ma_values == values && FT_ATOMIC_LOAD_UINT8(values->valid)) { - value = _Py_atomic_load_ptr_relaxed(&values->values[ix]); + value = _Py_atomic_load_ptr_consume(&values->values[ix]); *attr = _Py_XNewRefWithLock(value); success = true; } else { @@ -7169,7 +7619,7 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj) if (dict == NULL) { return 1; } - return FT_ATOMIC_LOAD_SSIZE_RELAXED(((PyDictObject *)dict)->ma_used) == 0; + return GET_USED((PyDictObject *)dict) == 0; } int @@ -7179,16 +7629,21 @@ PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { return 0; } - if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { + PyDictObject *dict = _PyObject_ManagedDictPointer(obj)->dict; + if (dict != NULL) { + // GH-130327: If there's a managed dictionary available, we should + // *always* traverse it. The dict is responsible for traversing the + // inline values if it points to them. + Py_VISIT(dict); + } + else if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) { PyDictValues *values = _PyObject_InlineValues(obj); if (values->valid) { for (Py_ssize_t i = 0; i < values->capacity; i++) { Py_VISIT(values->values[i]); } - return 0; } } - Py_VISIT(_PyObject_ManagedDictPointer(obj)->dict); return 0; } @@ -7621,13 +8076,19 @@ validate_watcher_id(PyInterpreterState *interp, int watcher_id) PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id); return -1; } - if (!interp->dict_state.watchers[watcher_id]) { + PyDict_WatchCallback cb = FT_ATOMIC_LOAD_PTR_RELAXED( + interp->dict_state.watchers[watcher_id]); + if (cb == NULL) { PyErr_Format(PyExc_ValueError, "No dict watcher set for ID %d", watcher_id); return -1; } return 0; } +// In free-threaded builds, Add/Clear serialize on watcher_mutex and publish +// callbacks with release stores. SendEvent reads them lock-free using +// acquire loads. + int PyDict_Watch(int watcher_id, PyObject* dict) { @@ -7639,7 +8100,8 @@ PyDict_Watch(int watcher_id, PyObject* dict) if (validate_watcher_id(interp, watcher_id)) { return -1; } - ((PyDictObject*)dict)->_ma_watcher_tag |= (1LL << watcher_id); + FT_ATOMIC_OR_UINT64(((PyDictObject*)dict)->_ma_watcher_tag, + 1ULL << watcher_id); return 0; } @@ -7654,36 +8116,48 @@ PyDict_Unwatch(int watcher_id, PyObject* dict) if (validate_watcher_id(interp, watcher_id)) { return -1; } - ((PyDictObject*)dict)->_ma_watcher_tag &= ~(1LL << watcher_id); + FT_ATOMIC_AND_UINT64(((PyDictObject*)dict)->_ma_watcher_tag, + ~(1ULL << watcher_id)); return 0; } int PyDict_AddWatcher(PyDict_WatchCallback callback) { + int watcher_id = -1; PyInterpreterState *interp = _PyInterpreterState_GET(); - /* Start at 2, as 0 and 1 are reserved for CPython */ - for (int i = 2; i < DICT_MAX_WATCHERS; i++) { + FT_MUTEX_LOCK_FLAGS(&interp->dict_state.watcher_mutex, + _Py_LOCK_DONT_DETACH); + /* Some watchers are reserved for CPython, start at the first available one */ + for (int i = FIRST_AVAILABLE_WATCHER; i < DICT_MAX_WATCHERS; i++) { if (!interp->dict_state.watchers[i]) { - interp->dict_state.watchers[i] = callback; - return i; + FT_ATOMIC_STORE_PTR_RELEASE(interp->dict_state.watchers[i], callback); + watcher_id = i; + goto done; } } - PyErr_SetString(PyExc_RuntimeError, "no more dict watcher IDs available"); - return -1; +done: + FT_MUTEX_UNLOCK(&interp->dict_state.watcher_mutex); + return watcher_id; } int PyDict_ClearWatcher(int watcher_id) { + int res = 0; PyInterpreterState *interp = _PyInterpreterState_GET(); + FT_MUTEX_LOCK_FLAGS(&interp->dict_state.watcher_mutex, + _Py_LOCK_DONT_DETACH); if (validate_watcher_id(interp, watcher_id)) { - return -1; + res = -1; + goto done; } - interp->dict_state.watchers[watcher_id] = NULL; - return 0; + FT_ATOMIC_STORE_PTR_RELEASE(interp->dict_state.watchers[watcher_id], NULL); +done: + FT_MUTEX_UNLOCK(&interp->dict_state.watcher_mutex); + return res; } static const char * @@ -7708,7 +8182,8 @@ _PyDict_SendEvent(int watcher_bits, PyInterpreterState *interp = _PyInterpreterState_GET(); for (int i = 0; i < DICT_MAX_WATCHERS; i++) { if (watcher_bits & 1) { - PyDict_WatchCallback cb = interp->dict_state.watchers[i]; + PyDict_WatchCallback cb = FT_ATOMIC_LOAD_PTR_ACQUIRE( + interp->dict_state.watchers[i]); if (cb && (cb(event, (PyObject*)mp, key, value) < 0)) { // We don't want to resurrect the dict by potentially having an // unraisablehook keep a reference to it, so we don't pass the @@ -7743,3 +8218,241 @@ _PyObject_InlineValuesConsistencyCheck(PyObject *obj) return 0; } #endif + +// --- frozendict implementation --------------------------------------------- + +static PyObject * +frozendict_getnewargs(PyObject *op, PyObject *Py_UNUSED(dummy)) +{ + // Call dict(op): convert 'op' frozendict to a dict + PyObject *arg = PyObject_CallOneArg((PyObject*)&PyDict_Type, op); + if (arg == NULL) { + return NULL; + } + return Py_BuildValue("(N)", arg); +} + + +static PyNumberMethods frozendict_as_number = { + .nb_or = frozendict_or, +}; + +static PyMappingMethods frozendict_as_mapping = { + .mp_length = frozendict_length, + .mp_subscript = _PyDict_Subscript, +}; + +static PyMethodDef frozendict_methods[] = { + DICT___CONTAINS___METHODDEF + {"__getitem__", _PyDict_Subscript, METH_O | METH_COEXIST, getitem__doc__}, + DICT___SIZEOF___METHODDEF + DICT_GET_METHODDEF + DICT_KEYS_METHODDEF + DICT_ITEMS_METHODDEF + DICT_VALUES_METHODDEF + DICT_FROMKEYS_METHODDEF + FROZENDICT_COPY_METHODDEF + DICT___REVERSED___METHODDEF + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("frozendicts are generic over two types, signifying (respectively) the types of the frozendict's keys and values")}, + {"__getnewargs__", frozendict_getnewargs, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + + +static PyObject * +frozendict_repr(PyObject *self) +{ + PyDictObject *mp = _PyAnyDict_CAST(self); + if (mp->ma_used == 0) { + return PyUnicode_FromFormat("%s()", Py_TYPE(self)->tp_name); + } + + PyObject *repr = anydict_repr_impl(self); + if (repr == NULL) { + return NULL; + } + assert(PyUnicode_Check(repr)); + + PyObject *res = PyUnicode_FromFormat("%s(%U)", + Py_TYPE(self)->tp_name, + repr); + Py_DECREF(repr); + return res; +} + +static Py_uhash_t +_shuffle_bits(Py_uhash_t h) +{ + return ((h ^ 89869747UL) ^ (h << 16)) * 3644798167UL; +} + +// Compute hash((key, value)). +// Code copied from tuple_hash(). +static Py_hash_t +frozendict_pair_hash(Py_hash_t key_hash, PyObject *value) +{ + assert(key_hash != -1); + + const Py_ssize_t len = 2; + Py_uhash_t acc = _PyTuple_HASH_XXPRIME_5; + + Py_uhash_t lane = key_hash; + acc += lane * _PyTuple_HASH_XXPRIME_2; + acc = _PyTuple_HASH_XXROTATE(acc); + acc *= _PyTuple_HASH_XXPRIME_1; + + lane = PyObject_Hash(value); + if (lane == (Py_uhash_t)-1) { + return -1; + } + acc += lane * _PyTuple_HASH_XXPRIME_2; + acc = _PyTuple_HASH_XXROTATE(acc); + acc *= _PyTuple_HASH_XXPRIME_1; + + /* Add input length, mangled to keep the historical value of hash(()). */ + acc += len ^ (_PyTuple_HASH_XXPRIME_5 ^ 3527539UL); + + if (acc == (Py_uhash_t)-1) { + acc = 1546275796; + } + return acc; +} + + +// Code copied from frozenset_hash() +static Py_hash_t +frozendict_hash(PyObject *op) +{ + PyFrozenDictObject *self = _PyFrozenDictObject_CAST(op); + Py_hash_t shash = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->ma_hash); + if (shash != -1) { + return shash; + } + + PyDictObject *mp = _PyAnyDict_CAST(op); + Py_uhash_t hash = 0; + + PyObject *value; // borrowed ref + Py_ssize_t pos = 0; + Py_hash_t key_hash; + while (_PyDict_Next(op, &pos, NULL, &value, &key_hash)) { + Py_hash_t pair_hash = frozendict_pair_hash(key_hash, value); + if (pair_hash == -1) { + return -1; + } + hash ^= _shuffle_bits(pair_hash); + } + + /* Factor in the number of active entries */ + hash ^= ((Py_uhash_t)mp->ma_used + 1) * 1927868237UL; + + /* Disperse patterns arising in nested frozendicts */ + hash ^= (hash >> 11) ^ (hash >> 25); + hash = hash * 69069U + 907133923UL; + + /* -1 is reserved as an error code */ + if (hash == (Py_uhash_t)-1) { + hash = 590923713UL; + } + + FT_ATOMIC_STORE_SSIZE_RELAXED(self->ma_hash, (Py_hash_t)hash); + return (Py_hash_t)hash; +} + + +static PyObject * +frozendict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *d = dict_new(type, args, kwds); + if (d == NULL) { + return NULL; + } + assert(can_modify_dict(_PyAnyDict_CAST(d))); + + PyFrozenDictObject *self = _PyFrozenDictObject_CAST(d); + self->ma_hash = -1; + + if (args != NULL) { + if (dict_update_common(d, args, kwds, "frozendict") < 0) { + Py_DECREF(d); + return NULL; + } + } + else { + assert(kwds == NULL); + } + + return d; +} + + +PyObject* +PyFrozenDict_New(PyObject *iterable) +{ + if (iterable != NULL) { + if (PyFrozenDict_CheckExact(iterable)) { + // PyFrozenDict_New(frozendict) returns the same object unmodified + return Py_NewRef(iterable); + } + + PyObject *args = PyTuple_Pack(1, iterable); + if (args == NULL) { + return NULL; + } + PyObject *frozendict = frozendict_new(&PyFrozenDict_Type, args, NULL); + Py_DECREF(args); + return frozendict; + } + else { + PyObject *args = Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_TUPLE); + return frozendict_new(&PyFrozenDict_Type, args, NULL); + } +} + +/*[clinic input] +frozendict.copy + +Return a shallow copy of the frozendict. +[clinic start generated code]*/ + +static PyObject * +frozendict_copy_impl(PyFrozenDictObject *self) +/*[clinic end generated code: output=e580fd91d9fc2cf7 input=35f6abeaa08fd4bc]*/ +{ + assert(PyFrozenDict_Check(self)); + + if (PyFrozenDict_CheckExact(self)) { + return Py_NewRef(self); + } + + return anydict_copy((PyObject*)self); +} + + +PyTypeObject PyFrozenDict_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "frozendict", + .tp_basicsize = sizeof(PyFrozenDictObject), + .tp_dealloc = dict_dealloc, + .tp_repr = frozendict_repr, + .tp_as_number = &frozendict_as_number, + .tp_as_sequence = &dict_as_sequence, + .tp_as_mapping = &frozendict_as_mapping, + .tp_hash = frozendict_hash, + .tp_getattro = PyObject_GenericGetAttr, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_BASETYPE + | _Py_TPFLAGS_MATCH_SELF | Py_TPFLAGS_MAPPING, + .tp_doc = dictionary_doc, + .tp_traverse = dict_traverse, + .tp_clear = dict_tp_clear, + .tp_richcompare = dict_richcompare, + .tp_iter = dict_iter, + .tp_methods = frozendict_methods, + .tp_alloc = _PyType_AllocNoTrack, + .tp_new = frozendict_new, + .tp_free = PyObject_GC_Del, + .tp_vectorcall = frozendict_vectorcall, + .tp_version_tag = _Py_TYPE_VERSION_FROZENDICT, +}; diff --git a/Objects/enumobject.c b/Objects/enumobject.c index 1123b140c7fda8..fc53f1bfee8dde 100644 --- a/Objects/enumobject.c +++ b/Objects/enumobject.c @@ -78,7 +78,7 @@ enum_new_impl(PyTypeObject *type, PyObject *iterable, PyObject *start) Py_DECREF(en); return NULL; } - en->en_result = PyTuple_Pack(2, Py_None, Py_None); + en->en_result = _PyTuple_FromPairSteal(Py_None, Py_None); if (en->en_result == NULL) { Py_DECREF(en); return NULL; @@ -148,7 +148,7 @@ enumerate_vectorcall(PyObject *type, PyObject *const *args, } PyErr_Format(PyExc_TypeError, - "enumerate() takes at most 2 arguments (%d given)", nargs + nkwargs); + "enumerate() takes at most 2 arguments (%zd given)", nargs + nkwargs); return NULL; } @@ -178,14 +178,16 @@ enum_traverse(PyObject *op, visitproc visit, void *arg) static inline PyObject * increment_longindex_lock_held(enumobject *en) { - PyObject *next_index = en->en_longindex; - if (next_index == NULL) { - next_index = PyLong_FromSsize_t(PY_SSIZE_T_MAX); - if (next_index == NULL) { + if (en->en_longindex == NULL) { + en->en_longindex = PyLong_FromSsize_t(PY_SSIZE_T_MAX); + if (en->en_longindex == NULL) { return NULL; } } - assert(next_index != NULL); + assert(en->en_longindex != NULL); + // We hold one reference to "next_index" (a.k.a. the old value of + // en->en_longindex); we'll either return it or keep it in en->en_longindex + PyObject *next_index = en->en_longindex; PyObject *stepped_up = PyNumber_Add(next_index, en->one); if (stepped_up == NULL) { return NULL; @@ -224,15 +226,7 @@ enum_next_long(enumobject *en, PyObject* next_item) _PyTuple_Recycle(result); return result; } - result = PyTuple_New(2); - if (result == NULL) { - Py_DECREF(next_index); - Py_DECREF(next_item); - return NULL; - } - PyTuple_SET_ITEM(result, 0, next_index); - PyTuple_SET_ITEM(result, 1, next_item); - return result; + return _PyTuple_FromPairSteal(next_index, next_item); } static PyObject * @@ -274,15 +268,7 @@ enum_next(PyObject *op) _PyTuple_Recycle(result); return result; } - result = PyTuple_New(2); - if (result == NULL) { - Py_DECREF(next_index); - Py_DECREF(next_item); - return NULL; - } - PyTuple_SET_ITEM(result, 0, next_index); - PyTuple_SET_ITEM(result, 1, next_item); - return result; + return _PyTuple_FromPairSteal(next_index, next_item); } static PyObject * @@ -304,7 +290,7 @@ PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef enum_methods[] = { {"__reduce__", enum_reduce, METH_NOARGS, reduce_doc}, {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, PyDoc_STR("'enumerate' objects are generic over the type of their values")}, {NULL, NULL} /* sentinel */ }; @@ -367,7 +353,7 @@ typedef struct { @classmethod reversed.__new__ as reversed_new - sequence as seq: object + object as seq: object / Return a reverse iterator over the values of the given sequence. @@ -375,7 +361,7 @@ Return a reverse iterator over the values of the given sequence. static PyObject * reversed_new_impl(PyTypeObject *type, PyObject *seq) -/*[clinic end generated code: output=f7854cc1df26f570 input=aeb720361e5e3f1d]*/ +/*[clinic end generated code: output=f7854cc1df26f570 input=4781869729e3ba50]*/ { Py_ssize_t n; PyObject *reversed_meth; diff --git a/Objects/exceptions.c b/Objects/exceptions.c index b17cac83551670..149595e64cec14 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -13,7 +13,7 @@ #include "pycore_modsupport.h" // _PyArg_NoKeywords() #include "pycore_object.h" #include "pycore_pyerrors.h" // struct _PyErr_SetRaisedException -#include "pycore_tuple.h" // _PyTuple_FromArray() +#include "pycore_tuple.h" // _PyTuple_FromPair #include "osdefs.h" // SEP #include "clinic/exceptions.c.h" @@ -26,14 +26,6 @@ class BaseExceptionGroup "PyBaseExceptionGroupObject *" "&PyExc_BaseExceptionGro /*[clinic end generated code: output=da39a3ee5e6b4b0d input=b7c45e78cff8edc3]*/ -/* Compatibility aliases */ -PyObject *PyExc_EnvironmentError = NULL; // borrowed ref -PyObject *PyExc_IOError = NULL; // borrowed ref -#ifdef MS_WINDOWS -PyObject *PyExc_WindowsError = NULL; // borrowed ref -#endif - - static struct _Py_exc_state* get_exc_state(void) { @@ -119,7 +111,7 @@ BaseException_vectorcall(PyObject *type_obj, PyObject * const*args, self->context = NULL; self->suppress_context = 0; - self->args = _PyTuple_FromArray(args, PyVectorcall_NARGS(nargsf)); + self->args = PyTuple_FromArray(args, PyVectorcall_NARGS(nargsf)); if (!self->args) { Py_DECREF(self); return NULL; @@ -223,7 +215,7 @@ BaseException___reduce___impl(PyBaseExceptionObject *self) if (self->args && self->dict) return PyTuple_Pack(3, Py_TYPE(self), self->args, self->dict); else - return PyTuple_Pack(2, Py_TYPE(self), self->args); + return _PyTuple_FromPair((PyObject *)Py_TYPE(self), self->args); } /* @@ -233,7 +225,7 @@ BaseException___reduce___impl(PyBaseExceptionObject *self) */ /*[clinic input] -@critical_section +@critical_section state BaseException.__setstate__ state: object / @@ -241,7 +233,7 @@ BaseException.__setstate__ static PyObject * BaseException___setstate___impl(PyBaseExceptionObject *self, PyObject *state) -/*[clinic end generated code: output=f3834889950453ab input=5524b61cfe9b9856]*/ +/*[clinic end generated code: output=f3834889950453ab input=f9b1aea70382cdb6]*/ { PyObject *d_key, *d_value; Py_ssize_t i = 0; @@ -695,12 +687,12 @@ PyTypeObject _PyExc_ ## EXCNAME = { \ #define ComplexExtendsException(EXCBASE, EXCNAME, EXCSTORE, EXCNEW, \ EXCMETHODS, EXCMEMBERS, EXCGETSET, \ - EXCSTR, EXCDOC) \ + EXCSTR, EXCREPR, EXCDOC) \ static PyTypeObject _PyExc_ ## EXCNAME = { \ PyVarObject_HEAD_INIT(NULL, 0) \ # EXCNAME, \ sizeof(Py ## EXCSTORE ## Object), 0, \ - EXCSTORE ## _dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + EXCSTORE ## _dealloc, 0, 0, 0, 0, EXCREPR, 0, 0, 0, 0, 0, \ EXCSTR, 0, 0, 0, \ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \ PyDoc_STR(EXCDOC), EXCSTORE ## _traverse, \ @@ -793,7 +785,7 @@ StopIteration_traverse(PyObject *op, visitproc visit, void *arg) } ComplexExtendsException(PyExc_Exception, StopIteration, StopIteration, - 0, 0, StopIteration_members, 0, 0, + 0, 0, StopIteration_members, 0, 0, 0, "Signal the end from iterator.__next__()."); @@ -866,7 +858,7 @@ static PyMemberDef SystemExit_members[] = { }; ComplexExtendsException(PyExc_BaseException, SystemExit, SystemExit, - 0, 0, SystemExit_members, 0, 0, + 0, 0, SystemExit_members, 0, 0, 0, "Request to exit from the interpreter."); /* @@ -891,6 +883,7 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyObject *message = NULL; PyObject *exceptions = NULL; + PyObject *exceptions_str = NULL; if (!PyArg_ParseTuple(args, "UO:BaseExceptionGroup.__new__", @@ -906,9 +899,21 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } + /* Save initial exceptions sequence as a string in case sequence is mutated */ + if (!PyList_Check(exceptions) && !PyTuple_Check(exceptions)) { + exceptions_str = PyObject_Repr(exceptions); + if (exceptions_str == NULL) { + /* We don't hold a reference to exceptions, so clear it before + * attempting a decref in the cleanup. + */ + exceptions = NULL; + goto error; + } + } + exceptions = PySequence_Tuple(exceptions); if (!exceptions) { - return NULL; + goto error; } /* We are now holding a ref to the exceptions tuple */ @@ -930,7 +935,7 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!PyExceptionInstance_Check(exc)) { PyErr_Format( PyExc_ValueError, - "Item %d of second argument (exceptions) is not an exception", + "Item %zd of second argument (exceptions) is not an exception", i); goto error; } @@ -989,9 +994,11 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self->msg = Py_NewRef(message); self->excs = exceptions; + self->excs_str = exceptions_str; return (PyObject*)self; error: - Py_DECREF(exceptions); + Py_XDECREF(exceptions); + Py_XDECREF(exceptions_str); return NULL; } @@ -1002,8 +1009,7 @@ _PyExc_CreateExceptionGroup(const char *msg_str, PyObject *excs) if (!msg) { return NULL; } - PyObject *args = PyTuple_Pack(2, msg, excs); - Py_DECREF(msg); + PyObject *args = _PyTuple_FromPairSteal(msg, Py_NewRef(excs)); if (!args) { return NULL; } @@ -1030,6 +1036,7 @@ BaseExceptionGroup_clear(PyObject *op) PyBaseExceptionGroupObject *self = PyBaseExceptionGroupObject_CAST(op); Py_CLEAR(self->msg); Py_CLEAR(self->excs); + Py_CLEAR(self->excs_str); return BaseException_clear(op); } @@ -1047,6 +1054,7 @@ BaseExceptionGroup_traverse(PyObject *op, visitproc visit, void *arg) PyBaseExceptionGroupObject *self = PyBaseExceptionGroupObject_CAST(op); Py_VISIT(self->msg); Py_VISIT(self->excs); + Py_VISIT(self->excs_str); return BaseException_traverse(op, visit, arg); } @@ -1064,6 +1072,55 @@ BaseExceptionGroup_str(PyObject *op) self->msg, num_excs, num_excs > 1 ? "s" : ""); } +static PyObject * +BaseExceptionGroup_repr(PyObject *op) +{ + PyBaseExceptionGroupObject *self = PyBaseExceptionGroupObject_CAST(op); + assert(self->msg); + + PyObject *exceptions_str = NULL; + + /* Use the saved exceptions string for custom sequences. */ + if (self->excs_str) { + exceptions_str = Py_NewRef(self->excs_str); + } + else { + assert(self->excs); + + /* Older versions delegated to BaseException, inserting the current + * value of self.args[1]; but this can be mutable and go out-of-sync + * with self.exceptions. Instead, use self.exceptions for accuracy, + * making it look like self.args[1] for backwards compatibility. */ + assert(PyTuple_Check(self->args)); + if (PyTuple_GET_SIZE(self->args) == 2 && PyList_Check(PyTuple_GET_ITEM(self->args, 1))) { + PyObject *exceptions_list = PySequence_List(self->excs); + if (!exceptions_list) { + return NULL; + } + + exceptions_str = PyObject_Repr(exceptions_list); + Py_DECREF(exceptions_list); + } + else { + exceptions_str = PyObject_Repr(self->excs); + } + + if (!exceptions_str) { + return NULL; + } + } + + assert(exceptions_str != NULL); + + const char *name = _PyType_Name(Py_TYPE(self)); + PyObject *repr = PyUnicode_FromFormat( + "%s(%R, %U)", name, + self->msg, exceptions_str); + + Py_DECREF(exceptions_str); + return repr; +} + /*[clinic input] @critical_section BaseExceptionGroup.derive @@ -1076,7 +1133,7 @@ BaseExceptionGroup_derive_impl(PyBaseExceptionGroupObject *self, PyObject *excs) /*[clinic end generated code: output=4307564218dfbf06 input=f72009d38e98cec1]*/ { - PyObject *init_args = PyTuple_Pack(2, self->msg, excs); + PyObject *init_args = _PyTuple_FromPair(self->msg, excs); if (!init_args) { return NULL; } @@ -1393,13 +1450,11 @@ BaseExceptionGroup_split_impl(PyBaseExceptionGroupObject *self, return NULL; } - PyObject *result = PyTuple_Pack( - 2, + assert(_Py_IsStaticImmortal(Py_None)); + PyObject *result = _PyTuple_FromPairSteal( split_result.match ? split_result.match : Py_None, split_result.rest ? split_result.rest : Py_None); - Py_XDECREF(split_result.match); - Py_XDECREF(split_result.rest); return result; } @@ -1659,7 +1714,7 @@ PyUnstable_Exc_PrepReraiseStar(PyObject *orig, PyObject *excs) PyObject *exc = PyList_GET_ITEM(excs, i); if (exc == NULL || !(PyExceptionInstance_Check(exc) || Py_IsNone(exc))) { PyErr_Format(PyExc_TypeError, - "item %d of excs is not an exception", i); + "item %zd of excs is not an exception", i); return NULL; } } @@ -1688,7 +1743,8 @@ static PyMemberDef BaseExceptionGroup_members[] = { static PyMethodDef BaseExceptionGroup_methods[] = { {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, + PyDoc_STR("Exception groups are generic over the type of their contained exceptions")}, BASEEXCEPTIONGROUP_DERIVE_METHODDEF BASEEXCEPTIONGROUP_SPLIT_METHODDEF BASEEXCEPTIONGROUP_SUBGROUP_METHODDEF @@ -1698,7 +1754,7 @@ static PyMethodDef BaseExceptionGroup_methods[] = { ComplexExtendsException(PyExc_BaseException, BaseExceptionGroup, BaseExceptionGroup, BaseExceptionGroup_new /* new */, BaseExceptionGroup_methods, BaseExceptionGroup_members, - 0 /* getset */, BaseExceptionGroup_str, + 0 /* getset */, BaseExceptionGroup_str, BaseExceptionGroup_repr, "A combination of multiple unrelated exceptions."); /* @@ -1708,8 +1764,8 @@ static PyObject* create_exception_group_class(void) { struct _Py_exc_state *state = get_exc_state(); - PyObject *bases = PyTuple_Pack( - 2, PyExc_BaseExceptionGroup, PyExc_Exception); + PyObject *bases = _PyTuple_FromPair( + PyExc_BaseExceptionGroup, PyExc_Exception); if (bases == NULL) { return NULL; } @@ -1857,13 +1913,69 @@ ImportError_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) return NULL; PyBaseExceptionObject *exc = PyBaseExceptionObject_CAST(self); if (state == Py_None) - res = PyTuple_Pack(2, Py_TYPE(self), exc->args); + res = _PyTuple_FromPair((PyObject *)Py_TYPE(self), exc->args); else res = PyTuple_Pack(3, Py_TYPE(self), exc->args, state); Py_DECREF(state); return res; } +static PyObject * +ImportError_repr(PyObject *self) +{ + int hasargs = PyTuple_GET_SIZE(((PyBaseExceptionObject *)self)->args) != 0; + PyImportErrorObject *exc = PyImportErrorObject_CAST(self); + if (exc->name == NULL && exc->path == NULL) { + return BaseException_repr(self); + } + PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); + if (writer == NULL) { + goto error; + } + PyObject *r = BaseException_repr(self); + if (r == NULL) { + goto error; + } + if (PyUnicodeWriter_WriteSubstring( + writer, r, 0, PyUnicode_GET_LENGTH(r) - 1) < 0) + { + Py_DECREF(r); + goto error; + } + Py_DECREF(r); + if (exc->name) { + if (hasargs) { + if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) { + goto error; + } + } + if (PyUnicodeWriter_Format(writer, "name=%R", exc->name) < 0) { + goto error; + } + hasargs = 1; + } + if (exc->path) { + if (hasargs) { + if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) { + goto error; + } + } + if (PyUnicodeWriter_Format(writer, "path=%R", exc->path) < 0) { + goto error; + } + } + + if (PyUnicodeWriter_WriteChar(writer, ')') < 0) { + goto error; + } + + return PyUnicodeWriter_Finish(writer); + +error: + PyUnicodeWriter_Discard(writer); + return NULL; +} + static PyMemberDef ImportError_members[] = { {"msg", _Py_T_OBJECT, offsetof(PyImportErrorObject, msg), 0, PyDoc_STR("exception message")}, @@ -1881,13 +1993,33 @@ static PyMethodDef ImportError_methods[] = { {NULL} }; -ComplexExtendsException(PyExc_Exception, ImportError, - ImportError, 0 /* new */, - ImportError_methods, ImportError_members, - 0 /* getset */, ImportError_str, - "Import can't find module, or can't find name in " - "module."); +static PyTypeObject _PyExc_ImportError = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "ImportError", + .tp_basicsize = sizeof(PyImportErrorObject), + .tp_dealloc = ImportError_dealloc, + .tp_repr = ImportError_repr, + .tp_str = ImportError_str, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + .tp_doc = PyDoc_STR( + "Import can't find module, " + "or can't find name in module."), + .tp_traverse = ImportError_traverse, + .tp_clear = ImportError_clear, + .tp_methods = ImportError_methods, + .tp_members = ImportError_members, + .tp_base = &_PyExc_Exception, + .tp_dictoffset = offsetof(PyImportErrorObject, dict), + .tp_init = ImportError_init, +}; +PyObject *PyExc_ImportError = (PyObject *)&_PyExc_ImportError; + +/* + * ImportCycleError extends ImportError + */ +MiddlingExtendsException(PyExc_ImportError, ImportCycleError, ImportError, + "Import produces a cycle."); /* * ModuleNotFoundError extends ImportError */ @@ -2008,10 +2140,10 @@ oserror_init(PyOSErrorObject *self, PyObject **p_args, return -1; } else { - self->filename = Py_NewRef(filename); + Py_XSETREF(self->filename, Py_NewRef(filename)); if (filename2 && filename2 != Py_None) { - self->filename2 = Py_NewRef(filename2); + Py_XSETREF(self->filename2, Py_NewRef(filename2)); } if (nargs >= 2 && nargs <= 5) { @@ -2026,10 +2158,10 @@ oserror_init(PyOSErrorObject *self, PyObject **p_args, } } } - self->myerrno = Py_XNewRef(myerrno); - self->strerror = Py_XNewRef(strerror); + Py_XSETREF(self->myerrno, Py_XNewRef(myerrno)); + Py_XSETREF(self->strerror, Py_XNewRef(strerror)); #ifdef MS_WINDOWS - self->winerror = Py_XNewRef(winerror); + Py_XSETREF(self->winerror, Py_XNewRef(winerror)); #endif /* Steals the reference to args */ @@ -2289,7 +2421,7 @@ OSError_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) if (self->dict) res = PyTuple_Pack(3, Py_TYPE(self), args, self->dict); else - res = PyTuple_Pack(2, Py_TYPE(self), args); + res = _PyTuple_FromPair((PyObject *)Py_TYPE(self), args); Py_DECREF(args); return res; } @@ -2356,7 +2488,7 @@ static PyGetSetDef OSError_getset[] = { ComplexExtendsException(PyExc_Exception, OSError, OSError, OSError_new, OSError_methods, OSError_members, OSError_getset, - OSError_str, + OSError_str, 0, "Base class for I/O related errors."); @@ -2394,6 +2526,13 @@ MiddlingExtendsException(PyExc_OSError, ProcessLookupError, OSError, MiddlingExtendsException(PyExc_OSError, TimeoutError, OSError, "Timeout expired."); +/* Compatibility aliases */ +PyObject *PyExc_EnvironmentError = (PyObject *)&_PyExc_OSError; // borrowed ref +PyObject *PyExc_IOError = (PyObject *)&_PyExc_OSError; // borrowed ref +#ifdef MS_WINDOWS +PyObject *PyExc_WindowsError = (PyObject *)&_PyExc_OSError; // borrowed ref +#endif + /* * EOFError extends Exception */ @@ -2497,7 +2636,7 @@ static PyMethodDef NameError_methods[] = { ComplexExtendsException(PyExc_Exception, NameError, NameError, 0, NameError_methods, NameError_members, - 0, BaseException_str, "Name not found globally."); + 0, BaseException_str, 0, "Name not found globally."); /* * UnboundLocalError extends NameError @@ -2631,7 +2770,7 @@ static PyMethodDef AttributeError_methods[] = { ComplexExtendsException(PyExc_Exception, AttributeError, AttributeError, 0, AttributeError_methods, AttributeError_members, - 0, BaseException_str, "Attribute not found."); + 0, BaseException_str, 0, "Attribute not found."); /* * SyntaxError extends Exception @@ -2664,23 +2803,25 @@ SyntaxError_init(PyObject *op, PyObject *args, PyObject *kwds) return -1; } - self->end_lineno = NULL; - self->end_offset = NULL; + PyObject *filename, *lineno, *offset, *text; + PyObject *end_lineno = NULL; + PyObject *end_offset = NULL; + PyObject *metadata = NULL; if (!PyArg_ParseTuple(info, "OOOO|OOO", - &self->filename, &self->lineno, - &self->offset, &self->text, - &self->end_lineno, &self->end_offset, &self->metadata)) { + &filename, &lineno, + &offset, &text, + &end_lineno, &end_offset, &metadata)) { Py_DECREF(info); return -1; } - Py_INCREF(self->filename); - Py_INCREF(self->lineno); - Py_INCREF(self->offset); - Py_INCREF(self->text); - Py_XINCREF(self->end_lineno); - Py_XINCREF(self->end_offset); - Py_XINCREF(self->metadata); + Py_XSETREF(self->filename, Py_NewRef(filename)); + Py_XSETREF(self->lineno, Py_NewRef(lineno)); + Py_XSETREF(self->offset, Py_NewRef(offset)); + Py_XSETREF(self->text, Py_NewRef(text)); + Py_XSETREF(self->end_lineno, Py_XNewRef(end_lineno)); + Py_XSETREF(self->end_offset, Py_XNewRef(end_offset)); + Py_XSETREF(self->metadata, Py_XNewRef(metadata)); Py_DECREF(info); if (self->end_lineno != NULL && self->end_offset == NULL) { @@ -2830,7 +2971,7 @@ static PyMemberDef SyntaxError_members[] = { ComplexExtendsException(PyExc_Exception, SyntaxError, SyntaxError, 0, 0, SyntaxError_members, 0, - SyntaxError_str, "Invalid syntax."); + SyntaxError_str, 0, "Invalid syntax."); /* @@ -2890,7 +3031,7 @@ KeyError_str(PyObject *op) } ComplexExtendsException(PyExc_LookupError, KeyError, BaseException, - 0, 0, 0, 0, KeyError_str, "Mapping key not found."); + 0, 0, 0, 0, KeyError_str, 0, "Mapping key not found."); /* @@ -4321,6 +4462,7 @@ static struct static_exception static_exceptions[] = { {&_PyExc_IncompleteInputError, "_IncompleteInputError"}, // base: SyntaxError(Exception) ITEM(IndexError), // base: LookupError(Exception) ITEM(KeyError), // base: LookupError(Exception) + ITEM(ImportCycleError), // base: ImportError(Exception) ITEM(ModuleNotFoundError), // base: ImportError(Exception) ITEM(NotImplementedError), // base: RuntimeError(Exception) ITEM(PythonFinalizationError), // base: RuntimeError(Exception) @@ -4465,23 +4607,17 @@ _PyBuiltins_AddExceptions(PyObject *bltinmod) if (PyDict_SetItemString(mod_dict, "ExceptionGroup", PyExc_ExceptionGroup)) { return -1; } - -#define INIT_ALIAS(NAME, TYPE) \ - do { \ - PyExc_ ## NAME = PyExc_ ## TYPE; \ - if (PyDict_SetItemString(mod_dict, # NAME, PyExc_ ## TYPE)) { \ - return -1; \ - } \ - } while (0) - - INIT_ALIAS(EnvironmentError, OSError); - INIT_ALIAS(IOError, OSError); + if (PyDict_SetItemString(mod_dict, "EnvironmentError", PyExc_OSError)) { + return -1; + } + if (PyDict_SetItemString(mod_dict, "IOError", PyExc_OSError)) { + return -1; + } #ifdef MS_WINDOWS - INIT_ALIAS(WindowsError, OSError); + if (PyDict_SetItemString(mod_dict, "WindowsError", PyExc_OSError)) { + return -1; + } #endif - -#undef INIT_ALIAS - return 0; } @@ -4516,4 +4652,3 @@ _PyException_AddNote(PyObject *exc, PyObject *note) Py_XDECREF(r); return res; } - diff --git a/Objects/fileobject.c b/Objects/fileobject.c index e624405bd5f62f..05c3e75b4642ee 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -68,9 +68,9 @@ PyFile_GetLine(PyObject *f, int n) } if (result != NULL && !PyBytes_Check(result) && !PyUnicode_Check(result)) { + PyErr_Format(PyExc_TypeError, + "%T.readline() must return a str, not %T", f, result); Py_SETREF(result, NULL); - PyErr_SetString(PyExc_TypeError, - "object.readline() returned non-string"); } if (n < 0 && result != NULL && PyBytes_Check(result)) { @@ -193,8 +193,8 @@ PyObject_AsFileDescriptor(PyObject *o) Py_DECREF(fno); } else { - PyErr_SetString(PyExc_TypeError, - "fileno() returned a non-integer"); + PyErr_Format(PyExc_TypeError, + "%T.fileno() must return an int, not %T", o, fno); Py_DECREF(fno); return -1; } diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 93e1973d6b32fc..17e6a729dcd83f 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -16,6 +16,7 @@ #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_stackref.h" // PyStackRef_AsPyObjectBorrow() #include "pycore_structseq.h" // _PyStructSequence_FiniBuiltin() +#include "pycore_tuple.h" // _PyTuple_FromPair #include <float.h> // DBL_MAX #include <stdlib.h> // strtol() @@ -135,13 +136,6 @@ PyFloat_FromDouble(double fval) return (PyObject *) op; } -_PyStackRef _PyFloat_FromDouble_ConsumeInputs(_PyStackRef left, _PyStackRef right, double value) -{ - PyStackRef_CLOSE_SPECIALIZED(left, _PyFloat_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(right, _PyFloat_ExactDealloc); - return PyStackRef_FromPyObjectSteal(PyFloat_FromDouble(value)); -} - static PyObject * float_from_string_inner(const char *s, Py_ssize_t len, void *obj) { @@ -288,16 +282,16 @@ PyFloat_AsDouble(PyObject *op) if (!PyFloat_CheckExact(res)) { if (!PyFloat_Check(res)) { PyErr_Format(PyExc_TypeError, - "%.50s.__float__ returned non-float (type %.50s)", - Py_TYPE(op)->tp_name, Py_TYPE(res)->tp_name); + "%T.__float__() must return a float, not %T", + op, res); Py_DECREF(res); return -1; } if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "%.50s.__float__ returned non-float (type %.50s). " + "%T.__float__() must return a float, not %T. " "The ability to return an instance of a strict subclass of float " "is deprecated, and may be removed in a future version of Python.", - Py_TYPE(op)->tp_name, Py_TYPE(res)->tp_name)) { + op, res)) { Py_DECREF(res); return -1; } @@ -442,82 +436,67 @@ float_richcompare(PyObject *v, PyObject *w, int op) assert(vsign != 0); /* if vsign were 0, then since wsign is * not 0, we would have taken the * vsign != wsign branch at the start */ - /* We want to work with non-negative numbers. */ - if (vsign < 0) { - /* "Multiply both sides" by -1; this also swaps the - * comparator. - */ - i = -i; - op = _Py_SwappedOp[op]; - } - assert(i > 0.0); (void) frexp(i, &exponent); /* exponent is the # of bits in v before the radix point; * we know that nbits (the # of bits in w) > 48 at this point */ if (exponent < nbits) { - i = 1.0; - j = 2.0; + j = i; + i = 0.0; goto Compare; } if (exponent > nbits) { - i = 2.0; - j = 1.0; + j = 0.0; goto Compare; } /* v and w have the same number of bits before the radix - * point. Construct two ints that have the same comparison - * outcome. + * point. Construct an int from the integer part of v and + * update op if necessary, so comparing two ints has the same outcome. */ { double fracpart; double intpart; PyObject *result = NULL; PyObject *vv = NULL; - PyObject *ww = w; - if (wsign < 0) { - ww = PyNumber_Negative(w); - if (ww == NULL) - goto Error; + fracpart = modf(i, &intpart); + if (fracpart != 0.0) { + switch (op) { + /* Non-integer float never equals to an int. */ + case Py_EQ: + Py_RETURN_FALSE; + case Py_NE: + Py_RETURN_TRUE; + /* For non-integer float, v <= w <=> v < w. + * If v > 0: trunc(v) < v < trunc(v) + 1 + * v < w => trunc(v) < w + * trunc(v) < w => trunc(v) + 1 <= w => v < w + * If v < 0: trunc(v) - 1 < v < trunc(v) + * v < w => trunc(v) - 1 < w => trunc(v) <= w + * trunc(v) <= w => v < w + */ + case Py_LT: + case Py_LE: + op = vsign > 0 ? Py_LT : Py_LE; + break; + /* The same as above, but with opposite directions. */ + case Py_GT: + case Py_GE: + op = vsign > 0 ? Py_GE : Py_GT; + break; + } } - else - Py_INCREF(ww); - fracpart = modf(i, &intpart); vv = PyLong_FromDouble(intpart); if (vv == NULL) goto Error; - if (fracpart != 0.0) { - /* Shift left, and or a 1 bit into vv - * to represent the lost fraction. - */ - PyObject *temp; - - temp = _PyLong_Lshift(ww, 1); - if (temp == NULL) - goto Error; - Py_SETREF(ww, temp); - - temp = _PyLong_Lshift(vv, 1); - if (temp == NULL) - goto Error; - Py_SETREF(vv, temp); - - temp = PyNumber_Or(vv, _PyLong_GetOne()); - if (temp == NULL) - goto Error; - Py_SETREF(vv, temp); - } - - r = PyObject_RichCompareBool(vv, ww, op); + r = PyObject_RichCompareBool(vv, w, op); if (r < 0) goto Error; result = PyBool_FromLong(r); Error: Py_XDECREF(vv); - Py_XDECREF(ww); return result; } } /* else if (PyLong_Check(w)) */ @@ -1484,6 +1463,7 @@ float_fromhex_impl(PyTypeObject *type, PyObject *string) } /*[clinic input] +@permit_long_summary float.as_integer_ratio Return a pair of integers, whose ratio is exactly equal to the original float. @@ -1501,7 +1481,7 @@ OverflowError on infinities and a ValueError on NaNs. static PyObject * float_as_integer_ratio_impl(PyObject *self) -/*[clinic end generated code: output=65f25f0d8d30a712 input=d5ba7765655d75bd]*/ +/*[clinic end generated code: output=65f25f0d8d30a712 input=75ae9be7cecd82a3]*/ { double self_double; double float_part; @@ -1560,8 +1540,9 @@ float_as_integer_ratio_impl(PyObject *self) if (denominator == NULL) goto error; } + Py_DECREF(py_exponent); - result_pair = PyTuple_Pack(2, numerator, denominator); + return _PyTuple_FromPairSteal(numerator, denominator); error: Py_XDECREF(py_exponent); @@ -1687,15 +1668,6 @@ float___getnewargs___impl(PyObject *self) return Py_BuildValue("(d)", ((PyFloatObject *)self)->ob_fval); } -/* this is for the benefit of the pack/unpack routines below */ -typedef enum _py_float_format_type float_format_type; -#define unknown_format _py_float_format_unknown -#define ieee_big_endian_format _py_float_format_ieee_big_endian -#define ieee_little_endian_format _py_float_format_ieee_little_endian - -#define float_format (_PyRuntime.float_state.float_format) -#define double_format (_PyRuntime.float_state.double_format) - /*[clinic input] @classmethod @@ -1709,45 +1681,25 @@ You probably don't want to use this function. It exists mainly to be used in Python's test suite. -This function returns whichever of 'unknown', 'IEEE, big-endian' or 'IEEE, -little-endian' best describes the format of floating-point numbers used by the -C type named by typestr. +This function returns whichever of 'IEEE, big-endian' or 'IEEE, +little-endian' best describes the format of floating-point numbers +used by the C type named by typestr. [clinic start generated code]*/ static PyObject * float___getformat___impl(PyTypeObject *type, const char *typestr) -/*[clinic end generated code: output=2bfb987228cc9628 input=90d5e246409a246e]*/ +/*[clinic end generated code: output=2bfb987228cc9628 input=eb1cf45e9bddab72]*/ { - float_format_type r; - - if (strcmp(typestr, "double") == 0) { - r = double_format; - } - else if (strcmp(typestr, "float") == 0) { - r = float_format; - } - else { + if (strcmp(typestr, "double") != 0 && strcmp(typestr, "float") != 0) { PyErr_SetString(PyExc_ValueError, "__getformat__() argument 1 must be " "'double' or 'float'"); return NULL; } - - switch (r) { - case unknown_format: - return PyUnicode_FromString("unknown"); - case ieee_little_endian_format: - return PyUnicode_FromString("IEEE, little-endian"); - case ieee_big_endian_format: - return PyUnicode_FromString("IEEE, big-endian"); - default: - PyErr_SetString(PyExc_RuntimeError, - "insane float_format or double_format"); - return NULL; - } + return PyUnicode_FromString(_PY_FLOAT_LITTLE_ENDIAN ? + "IEEE, little-endian" : "IEEE, big-endian"); } - static PyObject * float_getreal(PyObject *v, void *Py_UNUSED(closure)) { @@ -1898,67 +1850,6 @@ PyTypeObject PyFloat_Type = { .tp_version_tag = _Py_TYPE_VERSION_FLOAT, }; -static void -_init_global_state(void) -{ - float_format_type detected_double_format, detected_float_format; - - /* We attempt to determine if this machine is using IEEE - floating-point formats by peering at the bits of some - carefully chosen values. If it looks like we are on an - IEEE platform, the float packing/unpacking routines can - just copy bits, if not they resort to arithmetic & shifts - and masks. The shifts & masks approach works on all finite - values, but what happens to infinities, NaNs and signed - zeroes on packing is an accident, and attempting to unpack - a NaN or an infinity will raise an exception. - - Note that if we're on some whacked-out platform which uses - IEEE formats but isn't strictly little-endian or big- - endian, we will fall back to the portable shifts & masks - method. */ - -#if SIZEOF_DOUBLE == 8 - { - double x = 9006104071832581.0; - if (memcmp(&x, "\x43\x3f\xff\x01\x02\x03\x04\x05", 8) == 0) - detected_double_format = ieee_big_endian_format; - else if (memcmp(&x, "\x05\x04\x03\x02\x01\xff\x3f\x43", 8) == 0) - detected_double_format = ieee_little_endian_format; - else - detected_double_format = unknown_format; - } -#else - detected_double_format = unknown_format; -#endif - -#if SIZEOF_FLOAT == 4 - { - float y = 16711938.0; - if (memcmp(&y, "\x4b\x7f\x01\x02", 4) == 0) - detected_float_format = ieee_big_endian_format; - else if (memcmp(&y, "\x02\x01\x7f\x4b", 4) == 0) - detected_float_format = ieee_little_endian_format; - else - detected_float_format = unknown_format; - } -#else - detected_float_format = unknown_format; -#endif - - double_format = detected_double_format; - float_format = detected_float_format; -} - -void -_PyFloat_InitState(PyInterpreterState *interp) -{ - if (!_Py_IsMainInterpreter(interp)) { - return; - } - _init_global_state(); -} - PyStatus _PyFloat_InitTypes(PyInterpreterState *interp) { @@ -2028,6 +1919,10 @@ PyFloat_Pack2(double x, char *data, int le) memcpy(&v, &x, sizeof(v)); v &= 0xffc0000000000ULL; bits = (unsigned short)(v >> 42); /* NaN's type & payload */ + /* set qNaN if no payload */ + if (!bits) { + bits |= (1<<9); + } } else { sign = (x < 0.0); @@ -2108,274 +2003,87 @@ int PyFloat_Pack4(double x, char *data, int le) { unsigned char *p = (unsigned char *)data; - if (float_format == unknown_format) { - unsigned char sign; - int e; - double f; - unsigned int fbits; - int incr = 1; - - if (le) { - p += 3; - incr = -1; - } - - if (x < 0) { - sign = 1; - x = -x; - } - else - sign = 0; - - f = frexp(x, &e); - - /* Normalize f to be in the range [1.0, 2.0) */ - if (0.5 <= f && f < 1.0) { - f *= 2.0; - e--; - } - else if (f == 0.0) - e = 0; - else { - PyErr_SetString(PyExc_SystemError, - "frexp() result out of range"); - return -1; - } - - if (e >= 128) - goto Overflow; - else if (e < -126) { - /* Gradual underflow */ - f = ldexp(f, 126 + e); - e = 0; - } - else if (!(e == 0 && f == 0.0)) { - e += 127; - f -= 1.0; /* Get rid of leading 1 */ - } - - f *= 8388608.0; /* 2**23 */ - fbits = (unsigned int)(f + 0.5); /* Round */ - assert(fbits <= 8388608); - if (fbits >> 23) { - /* The carry propagated out of a string of 23 1 bits. */ - fbits = 0; - ++e; - if (e >= 255) - goto Overflow; - } - - /* First byte */ - *p = (sign << 7) | (e >> 1); - p += incr; - - /* Second byte */ - *p = (char) (((e & 1) << 7) | (fbits >> 16)); - p += incr; - - /* Third byte */ - *p = (fbits >> 8) & 0xFF; - p += incr; - - /* Fourth byte */ - *p = fbits & 0xFF; - - /* Done */ - return 0; + float y = (float)x; + int i, incr = 1; + if (isinf(y) && !isinf(x)) { + PyErr_SetString(PyExc_OverflowError, + "float too large to pack with f format"); + return -1; } - else { - float y = (float)x; - int i, incr = 1; - - if (isinf(y) && !isinf(x)) - goto Overflow; - /* correct y if x was a sNaN, transformed to qNaN by conversion */ - if (isnan(x)) { - uint64_t v; + /* correct y if x was a sNaN, transformed to qNaN by conversion */ + if (isnan(x)) { + uint64_t v; - memcpy(&v, &x, 8); + memcpy(&v, &x, 8); #ifndef __riscv - if ((v & (1ULL << 51)) == 0) { - uint32_t u32; - memcpy(&u32, &y, 4); - u32 &= ~(1 << 22); /* make sNaN */ - memcpy(&y, &u32, 4); - } -#else + if ((v & (1ULL << 51)) == 0) { uint32_t u32; - memcpy(&u32, &y, 4); - if ((v & (1ULL << 51)) == 0) { + /* if have payload, make sNaN */ + if (u32 & 0x3fffff) { u32 &= ~(1 << 22); } - /* Workaround RISC-V: "If a NaN value is converted to a - * different floating-point type, the result is the - * canonical NaN of the new type". The canonical NaN here - * is a positive qNaN with zero payload. */ - if (v & (1ULL << 63)) { - u32 |= (1 << 31); /* set sign */ - } - /* add payload */ - u32 -= (u32 & 0x3fffff); - u32 += (uint32_t)((v & 0x7ffffffffffffULL) >> 29); - memcpy(&y, &u32, 4); -#endif + } +#else + uint32_t u32; + + memcpy(&u32, &y, 4); + /* Workaround RISC-V: "If a NaN value is converted to a + * different floating-point type, the result is the + * canonical NaN of the new type". The canonical NaN here + * is a positive qNaN with zero payload. */ + if (v & (1ULL << 63)) { + u32 |= (1 << 31); /* set sign */ + } + /* add payload */ + u32 -= (u32 & 0x3fffff); + u32 += (uint32_t)((v & 0x7ffffffffffffULL) >> 29); + /* if have payload, make sNaN */ + if ((v & (1ULL << 51)) == 0 && (u32 & 0x3fffff)) { + u32 &= ~(1 << 22); } - unsigned char s[sizeof(float)]; - memcpy(s, &y, sizeof(float)); + memcpy(&y, &u32, 4); +#endif + } - if ((float_format == ieee_little_endian_format && !le) - || (float_format == ieee_big_endian_format && le)) { - p += 3; - incr = -1; - } + unsigned char s[sizeof(float)]; + memcpy(s, &y, sizeof(float)); - for (i = 0; i < 4; i++) { - *p = s[i]; - p += incr; - } - return 0; + if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) { + p += 3; + incr = -1; } - Overflow: - PyErr_SetString(PyExc_OverflowError, - "float too large to pack with f format"); - return -1; + + for (i = 0; i < 4; i++) { + *p = s[i]; + p += incr; + } + return 0; } int PyFloat_Pack8(double x, char *data, int le) { unsigned char *p = (unsigned char *)data; - if (double_format == unknown_format) { - unsigned char sign; - int e; - double f; - unsigned int fhi, flo; - int incr = 1; - - if (le) { - p += 7; - incr = -1; - } - - if (x < 0) { - sign = 1; - x = -x; - } - else - sign = 0; - - f = frexp(x, &e); - - /* Normalize f to be in the range [1.0, 2.0) */ - if (0.5 <= f && f < 1.0) { - f *= 2.0; - e--; - } - else if (f == 0.0) - e = 0; - else { - PyErr_SetString(PyExc_SystemError, - "frexp() result out of range"); - return -1; - } + unsigned char as_bytes[8]; + memcpy(as_bytes, &x, 8); + const unsigned char *s = as_bytes; + int i, incr = 1; - if (e >= 1024) - goto Overflow; - else if (e < -1022) { - /* Gradual underflow */ - f = ldexp(f, 1022 + e); - e = 0; - } - else if (!(e == 0 && f == 0.0)) { - e += 1023; - f -= 1.0; /* Get rid of leading 1 */ - } - - /* fhi receives the high 28 bits; flo the low 24 bits (== 52 bits) */ - f *= 268435456.0; /* 2**28 */ - fhi = (unsigned int)f; /* Truncate */ - assert(fhi < 268435456); - - f -= (double)fhi; - f *= 16777216.0; /* 2**24 */ - flo = (unsigned int)(f + 0.5); /* Round */ - assert(flo <= 16777216); - if (flo >> 24) { - /* The carry propagated out of a string of 24 1 bits. */ - flo = 0; - ++fhi; - if (fhi >> 28) { - /* And it also propagated out of the next 28 bits. */ - fhi = 0; - ++e; - if (e >= 2047) - goto Overflow; - } - } - - /* First byte */ - *p = (sign << 7) | (e >> 4); - p += incr; - - /* Second byte */ - *p = (unsigned char) (((e & 0xF) << 4) | (fhi >> 24)); - p += incr; - - /* Third byte */ - *p = (fhi >> 16) & 0xFF; - p += incr; - - /* Fourth byte */ - *p = (fhi >> 8) & 0xFF; - p += incr; - - /* Fifth byte */ - *p = fhi & 0xFF; - p += incr; - - /* Sixth byte */ - *p = (flo >> 16) & 0xFF; - p += incr; - - /* Seventh byte */ - *p = (flo >> 8) & 0xFF; - p += incr; - - /* Eighth byte */ - *p = flo & 0xFF; - /* p += incr; */ - - /* Done */ - return 0; - - Overflow: - PyErr_SetString(PyExc_OverflowError, - "float too large to pack with d format"); - return -1; + if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) { + p += 7; + incr = -1; } - else { - unsigned char as_bytes[8]; - memcpy(as_bytes, &x, 8); - const unsigned char *s = as_bytes; - int i, incr = 1; - - if ((double_format == ieee_little_endian_format && !le) - || (double_format == ieee_big_endian_format && le)) { - p += 7; - incr = -1; - } - for (i = 0; i < 8; i++) { - *p = *s++; - p += incr; - } - return 0; + for (i = 0; i < 8; i++) { + *p = *s++; + p += incr; } + return 0; } double @@ -2405,7 +2113,7 @@ PyFloat_Unpack2(const char *data, int le) if (e == 0x1f) { if (f == 0) { /* Infinity */ - return sign ? -Py_INFINITY : Py_INFINITY; + return sign ? -INFINITY : INFINITY; } else { /* NaN */ @@ -2438,208 +2146,79 @@ double PyFloat_Unpack4(const char *data, int le) { unsigned char *p = (unsigned char *)data; - if (float_format == unknown_format) { - unsigned char sign; - int e; - unsigned int f; - double x; - int incr = 1; - - if (le) { - p += 3; - incr = -1; - } - - /* First byte */ - sign = (*p >> 7) & 1; - e = (*p & 0x7F) << 1; - p += incr; - - /* Second byte */ - e |= (*p >> 7) & 1; - f = (*p & 0x7F) << 16; - p += incr; - - if (e == 255) { - PyErr_SetString( - PyExc_ValueError, - "can't unpack IEEE 754 special value " - "on non-IEEE platform"); - return -1; - } - - /* Third byte */ - f |= *p << 8; - p += incr; - - /* Fourth byte */ - f |= *p; + float x; - x = (double)f / 8388608.0; + if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) { + char buf[4]; + char *d = &buf[3]; + int i; - /* XXX This sadly ignores Inf/NaN issues */ - if (e == 0) - e = -126; - else { - x += 1.0; - e -= 127; + for (i = 0; i < 4; i++) { + *d-- = *p++; } - x = ldexp(x, e); - - if (sign) - x = -x; - - return x; + memcpy(&x, buf, 4); } else { - float x; - - if ((float_format == ieee_little_endian_format && !le) - || (float_format == ieee_big_endian_format && le)) { - char buf[4]; - char *d = &buf[3]; - int i; - - for (i = 0; i < 4; i++) { - *d-- = *p++; - } - memcpy(&x, buf, 4); - } - else { - memcpy(&x, p, 4); - } + memcpy(&x, p, 4); + } - /* return sNaN double if x was sNaN float */ - if (isnan(x)) { - uint32_t v; - memcpy(&v, &x, 4); + /* return sNaN double if x was sNaN float */ + if (isnan(x)) { + uint32_t v; + memcpy(&v, &x, 4); #ifndef __riscv - if ((v & (1 << 22)) == 0) { - double y = x; /* will make qNaN double */ - uint64_t u64; - memcpy(&u64, &y, 8); - u64 &= ~(1ULL << 51); /* make sNaN */ - memcpy(&y, &u64, 8); - return y; - } -#else - double y = x; + if ((v & (1 << 22)) == 0) { + double y = x; /* will make qNaN double */ uint64_t u64; - memcpy(&u64, &y, 8); - if ((v & (1 << 22)) == 0) { - u64 &= ~(1ULL << 51); - } - /* Workaround RISC-V, see PyFloat_Pack4() */ - if (v & (1 << 31)) { - u64 |= (1ULL << 63); /* set sign */ - } - /* add payload */ - u64 -= (u64 & 0x7ffffffffffffULL); - u64 += ((v & 0x3fffffULL) << 29); - + u64 &= ~(1ULL << 51); /* make sNaN */ memcpy(&y, &u64, 8); return y; -#endif } +#else + double y = x; + uint64_t u64; - return x; + memcpy(&u64, &y, 8); + if ((v & (1 << 22)) == 0) { + u64 &= ~(1ULL << 51); + } + /* Workaround RISC-V, see PyFloat_Pack4() */ + if (v & (1 << 31)) { + u64 |= (1ULL << 63); /* set sign */ + } + /* add payload */ + u64 -= (u64 & 0x7ffffffffffffULL); + u64 += ((v & 0x3fffffULL) << 29); + + memcpy(&y, &u64, 8); + return y; +#endif } + + return x; } double PyFloat_Unpack8(const char *data, int le) { unsigned char *p = (unsigned char *)data; - if (double_format == unknown_format) { - unsigned char sign; - int e; - unsigned int fhi, flo; - double x; - int incr = 1; - - if (le) { - p += 7; - incr = -1; - } - - /* First byte */ - sign = (*p >> 7) & 1; - e = (*p & 0x7F) << 4; - - p += incr; - - /* Second byte */ - e |= (*p >> 4) & 0xF; - fhi = (*p & 0xF) << 24; - p += incr; - - if (e == 2047) { - PyErr_SetString( - PyExc_ValueError, - "can't unpack IEEE 754 special value " - "on non-IEEE platform"); - return -1.0; - } - - /* Third byte */ - fhi |= *p << 16; - p += incr; - - /* Fourth byte */ - fhi |= *p << 8; - p += incr; - - /* Fifth byte */ - fhi |= *p; - p += incr; - - /* Sixth byte */ - flo = *p << 16; - p += incr; - - /* Seventh byte */ - flo |= *p << 8; - p += incr; - - /* Eighth byte */ - flo |= *p; + double x; - x = (double)fhi + (double)flo / 16777216.0; /* 2**24 */ - x /= 268435456.0; /* 2**28 */ + if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) { + char buf[8]; + char *d = &buf[7]; + int i; - if (e == 0) - e = -1022; - else { - x += 1.0; - e -= 1023; + for (i = 0; i < 8; i++) { + *d-- = *p++; } - x = ldexp(x, e); - - if (sign) - x = -x; - - return x; + memcpy(&x, buf, 8); } else { - double x; - - if ((double_format == ieee_little_endian_format && !le) - || (double_format == ieee_big_endian_format && le)) { - char buf[8]; - char *d = &buf[7]; - int i; - - for (i = 0; i < 8; i++) { - *d-- = *p++; - } - memcpy(&x, buf, 8); - } - else { - memcpy(&x, p, 8); - } - - return x; + memcpy(&x, p, 8); } + + return x; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 97de1e06efe1b2..b19889d3034e71 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -9,10 +9,12 @@ #include "pycore_function.h" // _PyFunction_FromConstructor() #include "pycore_genobject.h" // _PyGen_GetGeneratorFromFrame() #include "pycore_interpframe.h" // _PyFrame_GetLocalsArray() +#include "pycore_list.h" // _PyList_AppendTakeRef() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_opcode_metadata.h" // _PyOpcode_Caches #include "pycore_optimizer.h" // _Py_Executors_InvalidateDependency() +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_unicodeobject.h" // _PyUnicode_Equal() #include "frameobject.h" // PyFrameLocalsProxyObject @@ -260,7 +262,9 @@ framelocalsproxy_setitem(PyObject *self, PyObject *key, PyObject *value) return -1; } - _Py_Executors_InvalidateDependency(PyInterpreterState_Get(), co, 1); +#if _Py_TIER2 + _Py_Executors_InvalidateDependency(_PyInterpreterState_GET(), co, 1); +#endif _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i); _PyStackRef oldvalue = fast[i]; @@ -628,22 +632,14 @@ framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored)) PyObject *value = framelocalsproxy_getval(frame->f_frame, co, i); if (value) { - PyObject *pair = PyTuple_Pack(2, name, value); + PyObject *pair = _PyTuple_FromPairSteal(Py_NewRef(name), value); if (pair == NULL) { - Py_DECREF(items); - Py_DECREF(value); - return NULL; + goto error; } - if (PyList_Append(items, pair) < 0) { - Py_DECREF(items); - Py_DECREF(pair); - Py_DECREF(value); - return NULL; + if (_PyList_AppendTakeRef((PyListObject *)items, pair) < 0) { + goto error; } - - Py_DECREF(pair); - Py_DECREF(value); } } @@ -653,23 +649,22 @@ framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored)) PyObject *key = NULL; PyObject *value = NULL; while (PyDict_Next(frame->f_extra_locals, &j, &key, &value)) { - PyObject *pair = PyTuple_Pack(2, key, value); + PyObject *pair = _PyTuple_FromPair(key, value); if (pair == NULL) { - Py_DECREF(items); - return NULL; + goto error; } - if (PyList_Append(items, pair) < 0) { - Py_DECREF(items); - Py_DECREF(pair); - return NULL; + if (_PyList_AppendTakeRef((PyListObject *)items, pair) < 0) { + goto error; } - - Py_DECREF(pair); } } return items; + +error: + Py_DECREF(items); + return NULL; } static Py_ssize_t @@ -1046,11 +1041,11 @@ static PyObject * frame_lasti_get_impl(PyFrameObject *self) /*[clinic end generated code: output=03275b4f0327d1a2 input=0225ed49cb1fbeeb]*/ { - int lasti = _PyInterpreterFrame_LASTI(self->f_frame); + int lasti = PyUnstable_InterpreterFrame_GetLasti(self->f_frame); if (lasti < 0) { return PyLong_FromLong(-1); } - return PyLong_FromLong(lasti * sizeof(_Py_CODEUNIT)); + return PyLong_FromLong(lasti); } /*[clinic input] @@ -1685,6 +1680,8 @@ frame_lineno_set_impl(PyFrameObject *self, PyObject *value) case PY_MONITORING_EVENT_PY_RESUME: case PY_MONITORING_EVENT_JUMP: case PY_MONITORING_EVENT_BRANCH: + case PY_MONITORING_EVENT_BRANCH_LEFT: + case PY_MONITORING_EVENT_BRANCH_RIGHT: case PY_MONITORING_EVENT_LINE: case PY_MONITORING_EVENT_PY_YIELD: /* Setting f_lineno is allowed for the above events */ @@ -1847,6 +1844,7 @@ frame_lineno_set_impl(PyFrameObject *self, PyObject *value) } /*[clinic input] +@permit_long_summary @critical_section @getter frame.f_trace as frame_trace @@ -1856,7 +1854,7 @@ Return the trace function for this frame, or None if no trace function is set. static PyObject * frame_trace_get_impl(PyFrameObject *self) -/*[clinic end generated code: output=5475cbfce07826cd input=f382612525829773]*/ +/*[clinic end generated code: output=5475cbfce07826cd input=e4eacf2c68cac577]*/ { PyObject* trace = self->f_trace; if (trace == NULL) { @@ -1866,6 +1864,7 @@ frame_trace_get_impl(PyFrameObject *self) } /*[clinic input] +@permit_long_summary @critical_section @setter frame.f_trace as frame_trace @@ -1873,7 +1872,7 @@ frame.f_trace as frame_trace static int frame_trace_set_impl(PyFrameObject *self, PyObject *value) -/*[clinic end generated code: output=d6fe08335cf76ae4 input=d96a18bda085707f]*/ +/*[clinic end generated code: output=d6fe08335cf76ae4 input=e57380734815dac5]*/ { if (value == Py_None) { value = NULL; @@ -1888,6 +1887,7 @@ frame_trace_set_impl(PyFrameObject *self, PyObject *value) } /*[clinic input] +@permit_long_summary @critical_section @getter frame.f_generator as frame_generator @@ -1897,7 +1897,7 @@ Return the generator or coroutine associated with this frame, or None. static PyObject * frame_generator_get_impl(PyFrameObject *self) -/*[clinic end generated code: output=97aeb2392562e55b input=00a2bd008b239ab0]*/ +/*[clinic end generated code: output=97aeb2392562e55b input=3ffba57ba10f84be]*/ { if (self->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { PyObject *gen = (PyObject *)_PyGen_GetGeneratorFromFrame(self->f_frame); @@ -2008,30 +2008,20 @@ frame_clear_impl(PyFrameObject *self) { if (self->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { PyGenObject *gen = _PyGen_GetGeneratorFromFrame(self->f_frame); - if (gen->gi_frame_state == FRAME_EXECUTING) { - goto running; - } - if (FRAME_STATE_SUSPENDED(gen->gi_frame_state)) { - goto suspended; + if (_PyGen_ClearFrame(gen) < 0) { + return NULL; } - _PyGen_Finalize((PyObject *)gen); } else if (self->f_frame->owner == FRAME_OWNED_BY_THREAD) { - goto running; + PyErr_SetString(PyExc_RuntimeError, + "cannot clear an executing frame"); + return NULL; } else { assert(self->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT); (void)frame_tp_clear((PyObject *)self); } Py_RETURN_NONE; -running: - PyErr_SetString(PyExc_RuntimeError, - "cannot clear an executing frame"); - return NULL; -suspended: - PyErr_SetString(PyExc_RuntimeError, - "cannot clear a suspended frame"); - return NULL; } /*[clinic input] @@ -2056,11 +2046,15 @@ static PyObject * frame_repr(PyObject *op) { PyFrameObject *f = PyFrameObject_CAST(op); + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(f); int lineno = PyFrame_GetLineNumber(f); PyCodeObject *code = _PyFrame_GetCode(f->f_frame); - return PyUnicode_FromFormat( + result = PyUnicode_FromFormat( "<frame at %p, file %R, line %d, code %S>", f, code->co_filename, lineno, code->co_name); + Py_END_CRITICAL_SECTION(); + return result; } static PyMethodDef frame_methods[] = { @@ -2296,6 +2290,9 @@ _PyFrame_GetLocals(_PyInterpreterFrame *frame) } PyFrameObject* f = _PyFrame_GetFrameObject(frame); + if (f == NULL) { + return NULL; + } return _PyFrameLocalsProxy_New(f); } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 9532c21fc7082e..0fffd36ad462da 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -7,12 +7,13 @@ #include "pycore_long.h" // _PyLong_GetOne() #include "pycore_modsupport.h" // _PyArg_NoKeywords() #include "pycore_object.h" // _PyObject_GC_UNTRACK() +#include "pycore_object_deferred.h" // _PyObject_SetDeferredRefcount() +#include "pycore_optimizer.h" // _Py_Executors_InvalidateDependency() #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_stats.h" #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() - static const char * func_event_name(PyFunction_WatchEvent event) { switch (event) { @@ -62,6 +63,14 @@ handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func, case PyFunction_EVENT_MODIFY_CODE: case PyFunction_EVENT_MODIFY_DEFAULTS: case PyFunction_EVENT_MODIFY_KWDEFAULTS: + case PyFunction_EVENT_MODIFY_QUALNAME: +#if _Py_TIER2 + // Note: we only invalidate JIT code if a function version changes. + // Not when the function is deallocated. + // Function deallocation occurs frequently (think: lambdas), + // so we want to minimize dependency invalidation there. + _Py_Executors_InvalidateDependency(interp, func, 1); +#endif RARE_EVENT_INTERP_INC(interp, func_modification); break; default: @@ -148,7 +157,7 @@ PyObject * PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) { assert(globals != NULL); - assert(PyDict_Check(globals)); + assert(PyAnyDict_Check(globals)); _Py_INCREF_DICT(globals); PyCodeObject *code_obj = (PyCodeObject *)code; @@ -371,32 +380,6 @@ _PyFunction_ClearCodeByVersion(uint32_t version) #endif } -PyFunctionObject * -_PyFunction_LookupByVersion(uint32_t version, PyObject **p_code) -{ -#ifdef Py_GIL_DISABLED - return NULL; -#else - PyInterpreterState *interp = _PyInterpreterState_GET(); - struct _func_version_cache_item *slot = get_cache_item(interp, version); - if (slot->code) { - assert(PyCode_Check(slot->code)); - PyCodeObject *code = (PyCodeObject *)slot->code; - if (code->co_version == version) { - *p_code = slot->code; - } - } - else { - *p_code = NULL; - } - if (slot->func && slot->func->func_version == version) { - assert(slot->func->func_code == slot->code); - return slot->func; - } - return NULL; -#endif -} - uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func) { @@ -560,8 +543,9 @@ func_get_annotation_dict(PyFunctionObject *op) return NULL; } if (!PyDict_Check(ann_dict)) { - PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'", - Py_TYPE(ann_dict)->tp_name); + PyErr_Format(PyExc_TypeError, + "__annotate__() must return a dict, not %T", + ann_dict); Py_DECREF(ann_dict); return NULL; } @@ -681,7 +665,7 @@ func_set_code(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) if (nclosure != nfree) { PyErr_Format(PyExc_ValueError, "%U() requires a code object with %zd free vars," - " not %zd", + " not %d", op->func_name, nclosure, nfree); return -1; @@ -746,6 +730,7 @@ func_set_qualname(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) "__qualname__ must be set to a string object"); return -1; } + handle_func_event(PyFunction_EVENT_MODIFY_QUALNAME, (PyFunctionObject *) op, value); Py_XSETREF(op->func_qualname, Py_NewRef(value)); return 0; } @@ -1067,7 +1052,7 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals, nclosure = closure == Py_None ? 0 : PyTuple_GET_SIZE(closure); if (code->co_nfreevars != nclosure) return PyErr_Format(PyExc_ValueError, - "%U requires closure of length %zd, not %zd", + "%U requires closure of length %d, not %zd", code->co_name, code->co_nfreevars, nclosure); if (nclosure) { Py_ssize_t i; @@ -1459,33 +1444,60 @@ static PyObject * cm_descr_get(PyObject *self, PyObject *obj, PyObject *type) { classmethod *cm = (classmethod *)self; - - if (cm->cm_callable == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "uninitialized classmethod object"); - return NULL; - } if (type == NULL) type = (PyObject *)(Py_TYPE(obj)); return PyMethod_New(cm->cm_callable, type); } static int -cm_init(PyObject *self, PyObject *args, PyObject *kwds) +cm_set_callable(classmethod *cm, PyObject *callable) { - classmethod *cm = (classmethod *)self; - PyObject *callable; + assert(callable != NULL); + if (cm->cm_callable == callable) { + // cm_init() sets the same callable than cm_new() + return 0; + } - if (!_PyArg_NoKeywords("classmethod", kwds)) - return -1; - if (!PyArg_UnpackTuple(args, "classmethod", 1, 1, &callable)) - return -1; Py_XSETREF(cm->cm_callable, Py_NewRef(callable)); + return functools_wraps((PyObject *)cm, cm->cm_callable); +} + +static PyObject * +cm_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + if (!_PyArg_NoKeywords("classmethod", kwds)) { + return NULL; + } + PyObject *callable; // borrowed ref + if (!PyArg_UnpackTuple(args, "classmethod", 1, 1, &callable)) { + return NULL; + } - if (functools_wraps((PyObject *)cm, cm->cm_callable) < 0) { + classmethod *cm = (classmethod *)PyType_GenericAlloc(type, 0); + if (cm == NULL) { + return NULL; + } + _PyObject_SetDeferredRefcount((PyObject *)cm); + if (cm_set_callable(cm, callable) < 0) { + Py_DECREF(cm); + return NULL; + } + return (PyObject *)cm; +} + +static int +cm_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + if (!_PyArg_NoKeywords("classmethod", kwds)) { return -1; } - return 0; + PyObject *callable; // borrowed ref + if (!PyArg_UnpackTuple(args, "classmethod", 1, 1, &callable)) { + return -1; + } + + classmethod *cm = (classmethod *)self; + return cm_set_callable(cm, callable); } static PyMemberDef cm_memberlist[] = { @@ -1616,7 +1628,7 @@ PyTypeObject PyClassMethod_Type = { offsetof(classmethod, cm_dict), /* tp_dictoffset */ cm_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ + cm_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; @@ -1625,8 +1637,12 @@ PyClassMethod_New(PyObject *callable) { classmethod *cm = (classmethod *) PyType_GenericAlloc(&PyClassMethod_Type, 0); - if (cm != NULL) { - cm->cm_callable = Py_NewRef(callable); + if (cm == NULL) { + return NULL; + } + if (cm_set_callable(cm, callable) < 0) { + Py_DECREF(cm); + return NULL; } return (PyObject *)cm; } @@ -1692,31 +1708,58 @@ static PyObject * sm_descr_get(PyObject *self, PyObject *obj, PyObject *type) { staticmethod *sm = (staticmethod *)self; + return Py_NewRef(sm->sm_callable); +} + +static int +sm_set_callable(staticmethod *sm, PyObject *callable) +{ + assert(callable != NULL); + if (sm->sm_callable == callable) { + // sm_init() sets the same callable than sm_new() + return 0; + } - if (sm->sm_callable == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "uninitialized staticmethod object"); + Py_XSETREF(sm->sm_callable, Py_NewRef(callable)); + return functools_wraps((PyObject *)sm, sm->sm_callable); +} + +static PyObject * +sm_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + if (!_PyArg_NoKeywords("staticmethod", kwds)) { return NULL; } - return Py_NewRef(sm->sm_callable); + PyObject *callable; // borrowed ref + if (!PyArg_UnpackTuple(args, "staticmethod", 1, 1, &callable)) { + return NULL; + } + + staticmethod *sm = (staticmethod *)PyType_GenericAlloc(type, 0); + if (sm == NULL) { + return NULL; + } + _PyObject_SetDeferredRefcount((PyObject *)sm); + if (sm_set_callable(sm, callable) < 0) { + Py_DECREF(sm); + return NULL; + } + return (PyObject *)sm; } static int sm_init(PyObject *self, PyObject *args, PyObject *kwds) { - staticmethod *sm = (staticmethod *)self; - PyObject *callable; - - if (!_PyArg_NoKeywords("staticmethod", kwds)) - return -1; - if (!PyArg_UnpackTuple(args, "staticmethod", 1, 1, &callable)) + if (!_PyArg_NoKeywords("staticmethod", kwds)) { return -1; - Py_XSETREF(sm->sm_callable, Py_NewRef(callable)); - - if (functools_wraps((PyObject *)sm, sm->sm_callable) < 0) { + } + PyObject *callable; // borrowed ref + if (!PyArg_UnpackTuple(args, "staticmethod", 1, 1, &callable)) { return -1; } - return 0; + + staticmethod *sm = (staticmethod *)self; + return sm_set_callable(sm, callable); } static PyObject* @@ -1851,7 +1894,7 @@ PyTypeObject PyStaticMethod_Type = { offsetof(staticmethod, sm_dict), /* tp_dictoffset */ sm_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ + sm_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; @@ -1860,8 +1903,27 @@ PyStaticMethod_New(PyObject *callable) { staticmethod *sm = (staticmethod *) PyType_GenericAlloc(&PyStaticMethod_Type, 0); - if (sm != NULL) { - sm->sm_callable = Py_NewRef(callable); + if (sm == NULL) { + return NULL; + } + _PyObject_SetDeferredRefcount((PyObject *)sm); + if (sm_set_callable(sm, callable) < 0) { + Py_DECREF(sm); + return NULL; } return (PyObject *)sm; } + +PyObject * +_PyClassMethod_GetFunc(PyObject *self) +{ + classmethod *cm = _PyClassMethod_CAST(self); + return cm->cm_callable; +} + +PyObject * +_PyStaticMethod_GetFunc(PyObject *self) +{ + staticmethod *sm = _PyStaticMethod_CAST(self); + return sm->sm_callable; +} diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 3bb961aa2b619d..c2083e6fcb77f4 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -68,10 +68,15 @@ ga_repr_items_list(PyUnicodeWriter *writer, PyObject *p) return -1; } } - PyObject *item = PyList_GET_ITEM(p, i); + PyObject *item = PyList_GetItemRef(p, i); + if (item == NULL) { + return -1; // list can be mutated in a callback + } if (_Py_typing_type_repr(writer, item) < 0) { + Py_DECREF(item); return -1; } + Py_DECREF(item); } if (PyUnicodeWriter_WriteChar(writer, ']') < 0) { @@ -237,7 +242,6 @@ _Py_make_parameters(PyObject *args) len += needed; if (_PyTuple_Resize(&parameters, len) < 0) { Py_DECREF(subparams); - Py_DECREF(parameters); Py_XDECREF(tuple_args); return NULL; } @@ -294,6 +298,8 @@ subs_tvars(PyObject *obj, PyObject *params, &PyTuple_GET_ITEM(arg, 0), PyTuple_GET_SIZE(arg)); if (j < 0) { + Py_DECREF(subparams); + Py_DECREF(subargs); return NULL; } continue; @@ -406,6 +412,9 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje self); } item = _unpack_args(item); + if (item == NULL) { + return NULL; + } for (Py_ssize_t i = 0; i < nparams; i++) { PyObject *param = PyTuple_GET_ITEM(parameters, i); PyObject *prepare, *tmp; @@ -450,6 +459,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje if (is_args_list) { args = tuple_args = PySequence_Tuple(args); if (args == NULL) { + Py_DECREF(item); return NULL; } } @@ -525,12 +535,24 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje return NULL; } if (unpack) { + if (!PyTuple_Check(arg)) { + Py_DECREF(newargs); + Py_DECREF(item); + Py_XDECREF(tuple_args); + PyObject *original = PyTuple_GET_ITEM(args, iarg); + PyErr_Format(PyExc_TypeError, + "expected __typing_subst__ of %T objects to return a tuple, not %T", + original, arg); + Py_DECREF(arg); + return NULL; + } jarg = tuple_extend(&newargs, jarg, &PyTuple_GET_ITEM(arg, 0), PyTuple_GET_SIZE(arg)); Py_DECREF(arg); if (jarg < 0) { Py_DECREF(item); Py_XDECREF(tuple_args); + assert(newargs == NULL); return NULL; } } @@ -550,7 +572,8 @@ PyDoc_STRVAR(genericalias__doc__, "--\n\n" "Represent a PEP 585 generic type\n" "\n" -"E.g. for t = list[int], t.__origin__ is list and t.__args__ is (int,)."); +"For example, for t = list[int], t.__origin__ is list and t.__args__\n" +"is (int,)."); static PyObject * ga_getitem(PyObject *self, PyObject *item) @@ -630,13 +653,12 @@ ga_vectorcall(PyObject *self, PyObject *const *args, size_t nargsf, PyObject *kwnames) { gaobject *alias = (gaobject *) self; - PyObject *obj = PyVectorcall_Function(alias->origin)(alias->origin, args, nargsf, kwnames); + PyObject *obj = PyObject_Vectorcall(alias->origin, args, nargsf, kwnames); return set_orig_class(obj, self); } static const char* const attr_exceptions[] = { "__class__", - "__bases__", "__origin__", "__args__", "__unpacked__", @@ -645,6 +667,11 @@ static const char* const attr_exceptions[] = { "__mro_entries__", "__reduce_ex__", // needed so we don't look up object.__reduce_ex__ "__reduce__", + NULL, +}; + +static const char* const attr_blocked[] = { + "__bases__", "__copy__", "__deepcopy__", NULL, @@ -655,15 +682,29 @@ ga_getattro(PyObject *self, PyObject *name) { gaobject *alias = (gaobject *)self; if (PyUnicode_Check(name)) { + // When we check blocked attrs, we don't allow to proxy them to `__origin__`. + // Otherwise, we can break existing code. + for (const char * const *p = attr_blocked; ; p++) { + if (*p == NULL) { + break; + } + if (_PyUnicode_EqualToASCIIString(name, *p)) { + goto generic_getattr; + } + } + + // When we see own attrs, it has a priority over `__origin__`'s attr. for (const char * const *p = attr_exceptions; ; p++) { if (*p == NULL) { return PyObject_GetAttr(alias->origin, name); } if (_PyUnicode_EqualToASCIIString(name, *p)) { - break; + goto generic_getattr; } } } + +generic_getattr: return PyObject_GenericGetAttr(self, name); } diff --git a/Objects/genobject.c b/Objects/genobject.c index 3e7d6257006cfd..38d493343454fc 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -10,6 +10,7 @@ #include "pycore_gc.h" // _PyGC_CLEAR_FINALIZED() #include "pycore_genobject.h" // _PyGen_SetStopIterationValue() #include "pycore_interpframe.h" // _PyFrame_GetCode() +#include "pycore_lock.h" // _Py_yield() #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_opcode_utils.h" // RESUME_AFTER_YIELD_FROM @@ -36,6 +37,26 @@ static PyObject* async_gen_athrow_new(PyAsyncGenObject *, PyObject *); #define _PyAsyncGenObject_CAST(op) \ _Py_CAST(PyAsyncGenObject*, (op)) +#ifdef Py_GIL_DISABLED +static bool +gen_try_set_frame_state(PyGenObject *gen, int8_t *expected, int8_t state) +{ + if (*expected == FRAME_SUSPENDED_YIELD_FROM_LOCKED) { + // Wait for the in-progress gi_yieldfrom read to complete + _Py_yield(); + *expected = _Py_atomic_load_int8_relaxed(&gen->gi_frame_state); + return false; + } + return _Py_atomic_compare_exchange_int8(&gen->gi_frame_state, expected, state); +} + +# define _Py_GEN_TRY_SET_FRAME_STATE(gen, expected, state) \ + gen_try_set_frame_state((gen), &(expected), (state)) +#else +# define _Py_GEN_TRY_SET_FRAME_STATE(gen, expected, state) \ + ((gen)->gi_frame_state = (state), true) +#endif + static const char *NON_INIT_CORO_MSG = "can't send non-None value to a " "just-started coroutine"; @@ -83,8 +104,8 @@ gen_traverse(PyObject *self, visitproc visit, void *arg) return 0; } -void -_PyGen_Finalize(PyObject *self) +static void +gen_finalize(PyObject *self) { PyGenObject *gen = (PyGenObject *)self; @@ -145,16 +166,41 @@ _PyGen_Finalize(PyObject *self) static void gen_clear_frame(PyGenObject *gen) { - if (gen->gi_frame_state == FRAME_CLEARED) - return; - - gen->gi_frame_state = FRAME_CLEARED; + assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_CLEARED); _PyInterpreterFrame *frame = &gen->gi_iframe; frame->previous = NULL; _PyFrame_ClearExceptCode(frame); _PyErr_ClearExcState(&gen->gi_exc_state); } +int +_PyGen_ClearFrame(PyGenObject *gen) +{ + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + do { + if (FRAME_STATE_FINISHED(frame_state)) { + return 0; + } + else if (frame_state == FRAME_EXECUTING) { + PyErr_SetString(PyExc_RuntimeError, + "cannot clear an executing frame"); + return -1; + } + else if (FRAME_STATE_SUSPENDED(frame_state)) { + PyErr_SetString(PyExc_RuntimeError, + "cannot clear an suspended frame"); + return -1; + } + assert(frame_state == FRAME_CREATED); + } while (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_CLEARED)); + + if (_PyGen_GetCode(gen)->co_flags & CO_COROUTINE) { + _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); + } + gen_clear_frame(gen); + return 0; +} + static void gen_dealloc(PyObject *self) { @@ -179,7 +225,10 @@ gen_dealloc(PyObject *self) if (PyCoro_CheckExact(gen)) { Py_CLEAR(((PyCoroObject *)gen)->cr_origin_or_finalizer); } - gen_clear_frame(gen); + if (gen->gi_frame_state != FRAME_CLEARED) { + gen->gi_frame_state = FRAME_CLEARED; + gen_clear_frame(gen); + } assert(gen->gi_exc_state.exc_value == NULL); PyStackRef_CLEAR(gen->gi_iframe.f_executable); Py_CLEAR(gen->gi_name); @@ -188,58 +237,31 @@ gen_dealloc(PyObject *self) PyObject_GC_Del(gen); } -static PySendResult -gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, - int exc, int closing) +static void +gen_raise_already_executing_error(PyGenObject *gen) { - PyThreadState *tstate = _PyThreadState_GET(); - _PyInterpreterFrame *frame = &gen->gi_iframe; - - *presult = NULL; - if (gen->gi_frame_state == FRAME_CREATED && arg && arg != Py_None) { - const char *msg = "can't send non-None value to a " - "just-started generator"; - if (PyCoro_CheckExact(gen)) { - msg = NON_INIT_CORO_MSG; - } - else if (PyAsyncGen_CheckExact(gen)) { - msg = "can't send non-None value to a " - "just-started async generator"; - } - PyErr_SetString(PyExc_TypeError, msg); - return PYGEN_ERROR; - } - if (gen->gi_frame_state == FRAME_EXECUTING) { - const char *msg = "generator already executing"; - if (PyCoro_CheckExact(gen)) { - msg = "coroutine already executing"; - } - else if (PyAsyncGen_CheckExact(gen)) { - msg = "async generator already executing"; - } - PyErr_SetString(PyExc_ValueError, msg); - return PYGEN_ERROR; + const char *msg = "generator already executing"; + if (PyCoro_CheckExact(gen)) { + msg = "coroutine already executing"; } - if (FRAME_STATE_FINISHED(gen->gi_frame_state)) { - if (PyCoro_CheckExact(gen) && !closing) { - /* `gen` is an exhausted coroutine: raise an error, - except when called from gen_close(), which should - always be a silent method. */ - PyErr_SetString( - PyExc_RuntimeError, - "cannot reuse already awaited coroutine"); - } - else if (arg && !exc) { - /* `gen` is an exhausted generator: - only return value if called from send(). */ - *presult = Py_NewRef(Py_None); - return PYGEN_RETURN; - } - return PYGEN_ERROR; + else if (PyAsyncGen_CheckExact(gen)) { + msg = "async generator already executing"; } + PyErr_SetString(PyExc_ValueError, msg); +} - assert((gen->gi_frame_state == FRAME_CREATED) || - FRAME_STATE_SUSPENDED(gen->gi_frame_state)); +// Send 'arg' into 'gen'. On success, return PYGEN_NEXT or PYGEN_RETURN. +// Returns PYGEN_ERROR on failure. 'presult' is set to the yielded or +// returned value. +// The generator must be in the FRAME_EXECUTING state when this function +// is called. +static PySendResult +gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc) +{ + assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_EXECUTING); + + PyThreadState *tstate = _PyThreadState_GET(); + _PyInterpreterFrame *frame = &gen->gi_iframe; /* Push arg onto the frame's value stack */ PyObject *arg_obj = arg ? arg : Py_None; @@ -254,21 +276,37 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ChainStackItem(); } - gen->gi_frame_state = FRAME_EXECUTING; EVAL_CALL_STAT_INC(EVAL_CALL_GENERATOR); PyObject *result = _PyEval_EvalFrame(tstate, frame, exc); assert(tstate->exc_info == prev_exc_info); +#ifndef Py_GIL_DISABLED assert(gen->gi_exc_state.previous_item == NULL); - assert(gen->gi_frame_state != FRAME_EXECUTING); assert(frame->previous == NULL); + assert(gen->gi_frame_state != FRAME_EXECUTING); +#endif + + // The generator_return_kind field is used to distinguish between a + // yield and a return from within _PyEval_EvalFrame(). Earlier versions + // of CPython (prior to 3.15) used gi_frame_state for this purpose, but + // that requires the GIL for thread-safety. + int return_kind = ((_PyThreadStateImpl *)tstate)->generator_return_kind; + + if (return_kind == GENERATOR_YIELD) { + assert(result != NULL && !_PyErr_Occurred(tstate)); +#ifndef Py_GIL_DISABLED + assert(FRAME_STATE_SUSPENDED(gen->gi_frame_state)); +#endif + *presult = result; + return PYGEN_NEXT; + } + + assert(return_kind == GENERATOR_RETURN); + assert(gen->gi_exc_state.exc_value == NULL); + assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_CLEARED); /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ if (result) { - if (FRAME_STATE_SUSPENDED(gen->gi_frame_state)) { - *presult = result; - return PYGEN_NEXT; - } assert(result == Py_None || !PyAsyncGen_CheckExact(gen)); if (result == Py_None && !PyAsyncGen_CheckExact(gen) && !arg) { /* Return NULL if called by gen_iternext() */ @@ -281,47 +319,97 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, !PyErr_ExceptionMatches(PyExc_StopAsyncIteration)); } - assert(gen->gi_exc_state.exc_value == NULL); - assert(gen->gi_frame_state == FRAME_CLEARED); *presult = result; return result ? PYGEN_RETURN : PYGEN_ERROR; } +// Set the generator 'gen' to the executing state and send 'arg' into it. +// See gen_send_ex2() for details. +static PySendResult +gen_send_ex(PyGenObject *gen, PyObject *arg, PyObject **presult) +{ + *presult = NULL; + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + do { + if (frame_state == FRAME_CREATED && arg && arg != Py_None) { + const char *msg = "can't send non-None value to a " + "just-started generator"; + if (PyCoro_CheckExact(gen)) { + msg = NON_INIT_CORO_MSG; + } + else if (PyAsyncGen_CheckExact(gen)) { + msg = "can't send non-None value to a " + "just-started async generator"; + } + PyErr_SetString(PyExc_TypeError, msg); + return PYGEN_ERROR; + } + if (frame_state == FRAME_EXECUTING) { + gen_raise_already_executing_error(gen); + return PYGEN_ERROR; + } + if (FRAME_STATE_FINISHED(frame_state)) { + if (PyCoro_CheckExact(gen)) { + /* `gen` is an exhausted coroutine: raise an error, + except when called from gen_close(), which should + always be a silent method. */ + PyErr_SetString( + PyExc_RuntimeError, + "cannot reuse already awaited coroutine"); + } + else if (arg) { + /* `gen` is an exhausted generator: + only return value if called from send(). */ + *presult = Py_None; + return PYGEN_RETURN; + } + return PYGEN_ERROR; + } + + assert((frame_state == FRAME_CREATED) || + FRAME_STATE_SUSPENDED(frame_state)); + } while (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_EXECUTING)); + + return gen_send_ex2(gen, arg, presult, 0); +} + static PySendResult PyGen_am_send(PyObject *self, PyObject *arg, PyObject **result) { PyGenObject *gen = _PyGen_CAST(self); - return gen_send_ex2(gen, arg, result, 0, 0); + return gen_send_ex(gen, arg, result); } static PyObject * -gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) +gen_set_stop_iteration(PyGenObject *gen, PyObject *result) { - PyObject *result; - if (gen_send_ex2(gen, arg, &result, exc, closing) == PYGEN_RETURN) { - if (PyAsyncGen_CheckExact(gen)) { - assert(result == Py_None); - PyErr_SetNone(PyExc_StopAsyncIteration); - } - else if (result == Py_None) { - PyErr_SetNone(PyExc_StopIteration); - } - else { - _PyGen_SetStopIterationValue(result); - } - Py_CLEAR(result); + if (PyAsyncGen_CheckExact(gen)) { + assert(result == Py_None); + PyErr_SetNone(PyExc_StopAsyncIteration); } - return result; + else if (result == Py_None) { + PyErr_SetNone(PyExc_StopIteration); + } + else { + _PyGen_SetStopIterationValue(result); + } + Py_DECREF(result); + return NULL; } PyDoc_STRVAR(send_doc, -"send(arg) -> send 'arg' into generator,\n\ +"send(value) -> send 'value' into generator,\n\ return next yielded value or raise StopIteration."); static PyObject * -gen_send(PyObject *gen, PyObject *arg) +gen_send(PyObject *op, PyObject *arg) { - return gen_send_ex((PyGenObject*)gen, arg, 0, 0); + PyObject *result; + PyGenObject *gen = _PyGen_CAST(op); + if (gen_send_ex(gen, arg, &result) == PYGEN_RETURN) { + return gen_set_stop_iteration(gen, result); + } + return result; } PyDoc_STRVAR(close_doc, @@ -366,55 +454,57 @@ is_resume(_Py_CODEUNIT *instr) return ( code == RESUME || code == RESUME_CHECK || + code == RESUME_CHECK_JIT || code == INSTRUMENTED_RESUME ); } -PyObject * -_PyGen_yf(PyGenObject *gen) -{ - if (gen->gi_frame_state == FRAME_SUSPENDED_YIELD_FROM) { - _PyInterpreterFrame *frame = &gen->gi_iframe; - // GH-122390: These asserts are wrong in the presence of ENTER_EXECUTOR! - // assert(is_resume(frame->instr_ptr)); - // assert((frame->instr_ptr->op.arg & RESUME_OPARG_LOCATION_MASK) >= RESUME_AFTER_YIELD_FROM); - return PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(frame)); - } - return NULL; -} - static PyObject * gen_close(PyObject *self, PyObject *args) { PyGenObject *gen = _PyGen_CAST(self); - if (gen->gi_frame_state == FRAME_CREATED) { - gen->gi_frame_state = FRAME_COMPLETED; - Py_RETURN_NONE; - } - if (FRAME_STATE_FINISHED(gen->gi_frame_state)) { - Py_RETURN_NONE; - } + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + do { + if (frame_state == FRAME_CREATED) { + // && (1) to avoid -Wunreachable-code warning on Clang + if (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_CLEARED) && (1)) { + continue; + } + gen_clear_frame(gen); + Py_RETURN_NONE; + } + + if (FRAME_STATE_FINISHED(frame_state)) { + Py_RETURN_NONE; + } + + if (frame_state == FRAME_EXECUTING) { + gen_raise_already_executing_error(gen); + return NULL; + } + + assert(FRAME_STATE_SUSPENDED(frame_state)); + } while (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_EXECUTING)); - PyObject *yf = _PyGen_yf(gen); int err = 0; - if (yf) { - PyFrameState state = gen->gi_frame_state; - gen->gi_frame_state = FRAME_EXECUTING; + _PyInterpreterFrame *frame = &gen->gi_iframe; + if (frame_state == FRAME_SUSPENDED_YIELD_FROM) { + PyObject *yf = PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(frame, 2)); err = gen_close_iter(yf); - gen->gi_frame_state = state; Py_DECREF(yf); } - _PyInterpreterFrame *frame = &gen->gi_iframe; + if (is_resume(frame->instr_ptr)) { + bool no_unwind_tools = _PyEval_NoToolsForUnwind(_PyThreadState_GET(), frame); /* We can safely ignore the outermost try block * as it is automatically generated to handle * StopIteration. */ int oparg = frame->instr_ptr->op.arg; - if (oparg & RESUME_OPARG_DEPTH1_MASK) { + if (oparg & RESUME_OPARG_DEPTH1_MASK && no_unwind_tools) { // RESUME after YIELD_VALUE and exception depth is 1 assert((oparg & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_FUNC_START); - gen->gi_frame_state = FRAME_COMPLETED; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_CLEARED); gen_clear_frame(gen); Py_RETURN_NONE; } @@ -423,8 +513,13 @@ gen_close(PyObject *self, PyObject *args) PyErr_SetNone(PyExc_GeneratorExit); } - PyObject *retval = gen_send_ex(gen, Py_None, 1, 1); - if (retval) { + PyObject *retval; + if (gen_send_ex2(gen, Py_None, &retval, 1) == PYGEN_RETURN) { + // the generator returned a value while closing, return the value here + assert(!PyErr_Occurred()); + return retval; + } + else if (retval) { const char *msg = "generator ignored GeneratorExit"; if (PyCoro_CheckExact(gen)) { msg = "coroutine ignored GeneratorExit"; @@ -441,15 +536,80 @@ gen_close(PyObject *self, PyObject *args) PyErr_Clear(); /* ignore this error */ Py_RETURN_NONE; } + return NULL; +} - /* if the generator returned a value while closing, StopIteration was - * raised in gen_send_ex() above; retrieve and return the value here */ - if (_PyGen_FetchStopIterationValue(&retval) == 0) { - return retval; +// Set an exception for a gen.throw() call. +// Return 0 on success, -1 on failure. +static int +gen_set_exception(PyObject *typ, PyObject *val, PyObject *tb) +{ + /* First, check the traceback argument, replacing None with + NULL. */ + if (tb == Py_None) { + tb = NULL; } - return NULL; + else if (tb != NULL && !PyTraceBack_Check(tb)) { + PyErr_SetString(PyExc_TypeError, + "throw() third argument must be a traceback object"); + return -1; + } + + Py_INCREF(typ); + Py_XINCREF(val); + Py_XINCREF(tb); + + if (PyExceptionClass_Check(typ)) { + PyErr_NormalizeException(&typ, &val, &tb); + } + else if (PyExceptionInstance_Check(typ)) { + /* Raising an instance. The value should be a dummy. */ + if (val && val != Py_None) { + PyErr_SetString(PyExc_TypeError, + "instance exception may not have a separate value"); + goto failed_throw; + } + else { + /* Normalize to raise <class>, <instance> */ + Py_XSETREF(val, typ); + typ = Py_NewRef(PyExceptionInstance_Class(typ)); + + if (tb == NULL) + /* Returns NULL if there's no traceback */ + tb = PyException_GetTraceback(val); + } + } + else { + /* Not something you can raise. throw() fails. */ + PyErr_Format(PyExc_TypeError, + "exceptions must be classes or instances " + "deriving from BaseException, not %s", + Py_TYPE(typ)->tp_name); + goto failed_throw; + } + + PyErr_Restore(typ, val, tb); + return 0; + +failed_throw: + /* Didn't use our arguments, so restore their original refcounts */ + Py_DECREF(typ); + Py_XDECREF(val); + Py_XDECREF(tb); + return -1; } +static PyObject * +gen_throw_current_exception(PyGenObject *gen) +{ + assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_EXECUTING); + + PyObject *result; + if (gen_send_ex2(gen, Py_None, &result, 1) == PYGEN_RETURN) { + return gen_set_stop_iteration(gen, result); + } + return result; +} PyDoc_STRVAR(throw_doc, "throw(value)\n\ @@ -464,10 +624,32 @@ static PyObject * _gen_throw(PyGenObject *gen, int close_on_genexit, PyObject *typ, PyObject *val, PyObject *tb) { - PyObject *yf = _PyGen_yf(gen); + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + do { + if (frame_state == FRAME_EXECUTING) { + gen_raise_already_executing_error(gen); + return NULL; + } - if (yf) { + if (FRAME_STATE_FINISHED(frame_state)) { + if (PyCoro_CheckExact(gen)) { + /* `gen` is an exhausted coroutine: raise an error */ + PyErr_SetString( + PyExc_RuntimeError, + "cannot reuse already awaited coroutine"); + return NULL; + } + gen_set_exception(typ, val, tb); + return NULL; + } + + assert((frame_state == FRAME_CREATED) || + FRAME_STATE_SUSPENDED(frame_state)); + } while (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_EXECUTING)); + + if (frame_state == FRAME_SUSPENDED_YIELD_FROM) { _PyInterpreterFrame *frame = &gen->gi_iframe; + PyObject *yf = PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(frame, 2)); PyObject *ret; int err; if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) && @@ -477,13 +659,11 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, We have to allow some awaits to work it through, hence the `close_on_genexit` parameter here. */ - PyFrameState state = gen->gi_frame_state; - gen->gi_frame_state = FRAME_EXECUTING; err = gen_close_iter(yf); - gen->gi_frame_state = state; Py_DECREF(yf); - if (err < 0) - return gen_send_ex(gen, Py_None, 1, 0); + if (err < 0) { + return gen_throw_current_exception(gen); + } goto throw_here; } PyThreadState *tstate = _PyThreadState_GET(); @@ -499,18 +679,17 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, tstate->current_frame = frame; /* Close the generator that we are currently iterating with 'yield from' or awaiting on with 'await'. */ - PyFrameState state = gen->gi_frame_state; - gen->gi_frame_state = FRAME_EXECUTING; ret = _gen_throw((PyGenObject *)yf, close_on_genexit, typ, val, tb); - gen->gi_frame_state = state; tstate->current_frame = prev; frame->previous = NULL; - } else { + } + else { /* `yf` is an iterator or a coroutine-like object. */ PyObject *meth; if (PyObject_GetOptionalAttr(yf, &_Py_ID(throw), &meth) < 0) { Py_DECREF(yf); + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, frame_state); return NULL; } if (meth == NULL) { @@ -521,75 +700,26 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, _PyInterpreterFrame *prev = tstate->current_frame; frame->previous = prev; tstate->current_frame = frame; - PyFrameState state = gen->gi_frame_state; - gen->gi_frame_state = FRAME_EXECUTING; ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); - gen->gi_frame_state = state; tstate->current_frame = prev; frame->previous = NULL; Py_DECREF(meth); } Py_DECREF(yf); if (!ret) { - ret = gen_send_ex(gen, Py_None, 1, 0); + return gen_throw_current_exception(gen); } + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, frame_state); return ret; } throw_here: - /* First, check the traceback argument, replacing None with - NULL. */ - if (tb == Py_None) { - tb = NULL; - } - else if (tb != NULL && !PyTraceBack_Check(tb)) { - PyErr_SetString(PyExc_TypeError, - "throw() third argument must be a traceback object"); + assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_EXECUTING); + if (gen_set_exception(typ, val, tb) < 0) { + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, frame_state); return NULL; } - - Py_INCREF(typ); - Py_XINCREF(val); - Py_XINCREF(tb); - - if (PyExceptionClass_Check(typ)) - PyErr_NormalizeException(&typ, &val, &tb); - - else if (PyExceptionInstance_Check(typ)) { - /* Raising an instance. The value should be a dummy. */ - if (val && val != Py_None) { - PyErr_SetString(PyExc_TypeError, - "instance exception may not have a separate value"); - goto failed_throw; - } - else { - /* Normalize to raise <class>, <instance> */ - Py_XSETREF(val, typ); - typ = Py_NewRef(PyExceptionInstance_Class(typ)); - - if (tb == NULL) - /* Returns NULL if there's no traceback */ - tb = PyException_GetTraceback(val); - } - } - else { - /* Not something you can raise. throw() fails. */ - PyErr_Format(PyExc_TypeError, - "exceptions must be classes or instances " - "deriving from BaseException, not %s", - Py_TYPE(typ)->tp_name); - goto failed_throw; - } - - PyErr_Restore(typ, val, tb); - return gen_send_ex(gen, Py_None, 1, 0); - -failed_throw: - /* Didn't use our arguments, so restore their original refcounts */ - Py_DECREF(typ); - Py_XDECREF(val); - Py_XDECREF(tb); - return NULL; + return gen_throw_current_exception(gen); } @@ -631,7 +761,7 @@ gen_iternext(PyObject *self) PyGenObject *gen = _PyGen_CAST(self); PyObject *result; - if (gen_send_ex2(gen, NULL, &result, 0, 0) == PYGEN_RETURN) { + if (gen_send_ex(gen, NULL, &result) == PYGEN_RETURN) { if (result != Py_None) { _PyGen_SetStopIterationValue(result); } @@ -755,13 +885,29 @@ gen_set_qualname(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) } static PyObject * -gen_getyieldfrom(PyObject *gen, void *Py_UNUSED(ignored)) +gen_getyieldfrom(PyObject *self, void *Py_UNUSED(ignored)) { - PyObject *yf = _PyGen_yf(_PyGen_CAST(gen)); - if (yf == NULL) { + PyGenObject *gen = _PyGen_CAST(self); +#ifdef Py_GIL_DISABLED + int8_t frame_state = _Py_atomic_load_int8_relaxed(&gen->gi_frame_state); + do { + if (frame_state != FRAME_SUSPENDED_YIELD_FROM && + frame_state != FRAME_SUSPENDED_YIELD_FROM_LOCKED) + { + Py_RETURN_NONE; + } + } while (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_SUSPENDED_YIELD_FROM_LOCKED)); + + PyObject *result = PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(&gen->gi_iframe, 2)); + _Py_atomic_store_int8_release(&gen->gi_frame_state, FRAME_SUSPENDED_YIELD_FROM); + return result; +#else + int8_t frame_state = gen->gi_frame_state; + if (frame_state != FRAME_SUSPENDED_YIELD_FROM) { Py_RETURN_NONE; } - return yf; + return PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(&gen->gi_iframe, 2)); +#endif } @@ -769,17 +915,36 @@ static PyObject * gen_getrunning(PyObject *self, void *Py_UNUSED(ignored)) { PyGenObject *gen = _PyGen_CAST(self); - if (gen->gi_frame_state == FRAME_EXECUTING) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + return frame_state == FRAME_EXECUTING ? Py_True : Py_False; } static PyObject * gen_getsuspended(PyObject *self, void *Py_UNUSED(ignored)) { PyGenObject *gen = _PyGen_CAST(self); - return PyBool_FromLong(FRAME_STATE_SUSPENDED(gen->gi_frame_state)); + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + return FRAME_STATE_SUSPENDED(frame_state) ? Py_True : Py_False; +} + +static PyObject * +gen_getstate(PyObject *self, void *Py_UNUSED(ignored)) +{ + PyGenObject *gen = _PyGen_CAST(self); + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + + static PyObject *const state_strings[] = { + [FRAME_CREATED] = &_Py_ID(GEN_CREATED), + [FRAME_SUSPENDED] = &_Py_ID(GEN_SUSPENDED), + [FRAME_SUSPENDED_YIELD_FROM] = &_Py_ID(GEN_SUSPENDED), + [FRAME_SUSPENDED_YIELD_FROM_LOCKED] = &_Py_ID(GEN_SUSPENDED), + [FRAME_EXECUTING] = &_Py_ID(GEN_RUNNING), + [FRAME_CLEARED] = &_Py_ID(GEN_CLOSED), + }; + + assert(frame_state >= 0 && + (size_t)frame_state < Py_ARRAY_LENGTH(state_strings)); + return state_strings[frame_state]; } static PyObject * @@ -788,9 +953,11 @@ _gen_getframe(PyGenObject *gen, const char *const name) if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) { return NULL; } - if (FRAME_STATE_FINISHED(gen->gi_frame_state)) { + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + if (FRAME_STATE_FINISHED(frame_state)) { Py_RETURN_NONE; } + // TODO: still not thread-safe with free threading return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject(&gen->gi_iframe)); } @@ -828,6 +995,8 @@ static PyGetSetDef gen_getsetlist[] = { {"gi_frame", gen_getframe, NULL, NULL}, {"gi_suspended", gen_getsuspended, NULL, NULL}, {"gi_code", gen_getcode, NULL, NULL}, + {"gi_state", gen_getstate, NULL, + PyDoc_STR("state of the generator")}, {NULL} /* Sentinel */ }; @@ -854,7 +1023,8 @@ static PyMethodDef gen_methods[] = { {"throw", _PyCFunction_CAST(gen_throw), METH_FASTCALL, throw_doc}, {"close", gen_close, METH_NOARGS, close_doc}, {"__sizeof__", gen_sizeof, METH_NOARGS, sizeof__doc__}, - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("generators are generic over the types of their yield, send, and return values")}, {NULL, NULL} /* Sentinel */ }; @@ -916,7 +1086,7 @@ PyTypeObject PyGen_Type = { 0, /* tp_weaklist */ 0, /* tp_del */ 0, /* tp_version_tag */ - _PyGen_Finalize, /* tp_finalize */ + gen_finalize, /* tp_finalize */ }; static PyObject * @@ -932,6 +1102,7 @@ make_gen(PyTypeObject *type, PyFunctionObject *func) gen->gi_weakreflist = NULL; gen->gi_exc_state.exc_value = NULL; gen->gi_exc_state.previous_item = NULL; + gen->gi_iframe.f_executable = PyStackRef_None; assert(func->func_name != NULL); gen->gi_name = Py_NewRef(func->func_name); assert(func->func_qualname != NULL); @@ -940,9 +1111,6 @@ make_gen(PyTypeObject *type, PyFunctionObject *func) return (PyObject *)gen; } -static PyObject * -compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame); - PyObject * _Py_MakeCoro(PyFunctionObject *func) { @@ -980,7 +1148,7 @@ _Py_MakeCoro(PyFunctionObject *func) assert(frame); assert(_PyFrame_IsIncomplete(frame)); frame = _PyFrame_GetFirstComplete(frame->previous); - PyObject *cr_origin = compute_cr_origin(origin_depth, frame); + PyObject *cr_origin = _PyCoro_ComputeOrigin(origin_depth, frame); ((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin; if (!cr_origin) { Py_DECREF(coro); @@ -1092,14 +1260,14 @@ _PyCoro_GetAwaitableIter(PyObject *o) if (PyCoro_CheckExact(res) || gen_is_coroutine(res)) { /* __await__ must return an *iterator*, not a coroutine or another awaitable (see PEP 492) */ - PyErr_SetString(PyExc_TypeError, - "__await__() returned a coroutine"); + PyErr_Format(PyExc_TypeError, + "%T.__await__() must return an iterator, " + "not coroutine", o); Py_CLEAR(res); } else if (!PyIter_Check(res)) { PyErr_Format(PyExc_TypeError, - "__await__() returned non-iterator " - "of type '%.100s'", - Py_TYPE(res)->tp_name); + "%T.__await__() must return an iterator, " + "not %T", o, res); Py_CLEAR(res); } } @@ -1133,44 +1301,35 @@ coro_await(PyObject *coro) } static PyObject * -coro_get_cr_await(PyObject *coro, void *Py_UNUSED(ignored)) +cr_getframe(PyObject *coro, void *Py_UNUSED(ignored)) { - PyObject *yf = _PyGen_yf((PyGenObject *) coro); - if (yf == NULL) - Py_RETURN_NONE; - return yf; + return _gen_getframe(_PyGen_CAST(coro), "cr_frame"); } static PyObject * -cr_getsuspended(PyObject *self, void *Py_UNUSED(ignored)) +cr_getcode(PyObject *coro, void *Py_UNUSED(ignored)) { - PyCoroObject *coro = _PyCoroObject_CAST(self); - if (FRAME_STATE_SUSPENDED(coro->cr_frame_state)) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; + return _gen_getcode(_PyGen_CAST(coro), "cr_code"); } static PyObject * -cr_getrunning(PyObject *self, void *Py_UNUSED(ignored)) +cr_getstate(PyObject *self, void *Py_UNUSED(ignored)) { - PyCoroObject *coro = _PyCoroObject_CAST(self); - if (coro->cr_frame_state == FRAME_EXECUTING) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; -} + PyGenObject *gen = _PyGen_CAST(self); + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); -static PyObject * -cr_getframe(PyObject *coro, void *Py_UNUSED(ignored)) -{ - return _gen_getframe(_PyGen_CAST(coro), "cr_frame"); -} + static PyObject *const state_strings[] = { + [FRAME_CREATED] = &_Py_ID(CORO_CREATED), + [FRAME_SUSPENDED] = &_Py_ID(CORO_SUSPENDED), + [FRAME_SUSPENDED_YIELD_FROM] = &_Py_ID(CORO_SUSPENDED), + [FRAME_SUSPENDED_YIELD_FROM_LOCKED] = &_Py_ID(CORO_SUSPENDED), + [FRAME_EXECUTING] = &_Py_ID(CORO_RUNNING), + [FRAME_CLEARED] = &_Py_ID(CORO_CLOSED), + }; -static PyObject * -cr_getcode(PyObject *coro, void *Py_UNUSED(ignored)) -{ - return _gen_getcode(_PyGen_CAST(coro), "cr_code"); + assert(frame_state >= 0 && + (size_t)frame_state < Py_ARRAY_LENGTH(state_strings)); + return state_strings[frame_state]; } static PyGetSetDef coro_getsetlist[] = { @@ -1178,12 +1337,14 @@ static PyGetSetDef coro_getsetlist[] = { PyDoc_STR("name of the coroutine")}, {"__qualname__", gen_get_qualname, gen_set_qualname, PyDoc_STR("qualified name of the coroutine")}, - {"cr_await", coro_get_cr_await, NULL, + {"cr_await", gen_getyieldfrom, NULL, PyDoc_STR("object being awaited on, or None")}, - {"cr_running", cr_getrunning, NULL, NULL}, + {"cr_running", gen_getrunning, NULL, NULL}, {"cr_frame", cr_getframe, NULL, NULL}, {"cr_code", cr_getcode, NULL, NULL}, - {"cr_suspended", cr_getsuspended, NULL, NULL}, + {"cr_suspended", gen_getsuspended, NULL, NULL}, + {"cr_state", cr_getstate, NULL, + PyDoc_STR("state of the coroutine")}, {NULL} /* Sentinel */ }; @@ -1214,7 +1375,8 @@ static PyMethodDef coro_methods[] = { {"throw",_PyCFunction_CAST(gen_throw), METH_FASTCALL, coro_throw_doc}, {"close", gen_close, METH_NOARGS, coro_close_doc}, {"__sizeof__", gen_sizeof, METH_NOARGS, sizeof__doc__}, - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("coroutines are generic over the types of their yield, send, and return values")}, {NULL, NULL} /* Sentinel */ }; @@ -1274,7 +1436,7 @@ PyTypeObject PyCoro_Type = { 0, /* tp_weaklist */ 0, /* tp_del */ 0, /* tp_version_tag */ - _PyGen_Finalize, /* tp_finalize */ + gen_finalize, /* tp_finalize */ }; static void @@ -1372,8 +1534,8 @@ PyTypeObject _PyCoroWrapper_Type = { 0, /* tp_free */ }; -static PyObject * -compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame) +PyObject * +_PyCoro_ComputeOrigin(int origin_depth, _PyInterpreterFrame *current_frame) { _PyInterpreterFrame *frame = current_frame; /* First count how many frames we have */ @@ -1418,7 +1580,7 @@ PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname) if (origin_depth == 0) { ((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL; } else { - PyObject *cr_origin = compute_cr_origin(origin_depth, _PyEval_GetFrame()); + PyObject *cr_origin = _PyCoro_ComputeOrigin(origin_depth, _PyEval_GetFrame()); ((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin; if (!cr_origin) { Py_DECREF(coro); @@ -1600,13 +1762,23 @@ ag_getcode(PyObject *gen, void *Py_UNUSED(ignored)) } static PyObject * -ag_getsuspended(PyObject *self, void *Py_UNUSED(ignored)) +ag_getstate(PyObject *self, void *Py_UNUSED(ignored)) { - PyAsyncGenObject *ag = _PyAsyncGenObject_CAST(self); - if (FRAME_STATE_SUSPENDED(ag->ag_frame_state)) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; + PyGenObject *gen = _PyGen_CAST(self); + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); + + static PyObject *const state_strings[] = { + [FRAME_CREATED] = &_Py_ID(AGEN_CREATED), + [FRAME_SUSPENDED] = &_Py_ID(AGEN_SUSPENDED), + [FRAME_SUSPENDED_YIELD_FROM] = &_Py_ID(AGEN_SUSPENDED), + [FRAME_SUSPENDED_YIELD_FROM_LOCKED] = &_Py_ID(AGEN_SUSPENDED), + [FRAME_EXECUTING] = &_Py_ID(AGEN_RUNNING), + [FRAME_CLEARED] = &_Py_ID(AGEN_CLOSED), + }; + + assert(frame_state >= 0 && + (size_t)frame_state < Py_ARRAY_LENGTH(state_strings)); + return state_strings[frame_state]; } static PyGetSetDef async_gen_getsetlist[] = { @@ -1614,11 +1786,13 @@ static PyGetSetDef async_gen_getsetlist[] = { PyDoc_STR("name of the async generator")}, {"__qualname__", gen_get_qualname, gen_set_qualname, PyDoc_STR("qualified name of the async generator")}, - {"ag_await", coro_get_cr_await, NULL, + {"ag_await", gen_getyieldfrom, NULL, PyDoc_STR("object being awaited on, or None")}, {"ag_frame", ag_getframe, NULL, NULL}, {"ag_code", ag_getcode, NULL, NULL}, - {"ag_suspended", ag_getsuspended, NULL, NULL}, + {"ag_suspended", gen_getsuspended, NULL, NULL}, + {"ag_state", ag_getstate, NULL, + PyDoc_STR("state of the async generator")}, {NULL} /* Sentinel */ }; @@ -1648,7 +1822,7 @@ static PyMethodDef async_gen_methods[] = { {"aclose", async_gen_aclose, METH_NOARGS, async_aclose_doc}, {"__sizeof__", gen_sizeof, METH_NOARGS, sizeof__doc__}, {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, PyDoc_STR("async generators are generic over the types of their yield and send values")}, {NULL, NULL} /* Sentinel */ }; @@ -1710,7 +1884,7 @@ PyTypeObject PyAsyncGen_Type = { 0, /* tp_weaklist */ 0, /* tp_del */ 0, /* tp_version_tag */ - _PyGen_Finalize, /* tp_finalize */ + gen_finalize, /* tp_finalize */ }; @@ -1830,6 +2004,19 @@ async_gen_asend_send(PyObject *self, PyObject *arg) return result; } +PySendResult +_PyAsyncGenASend_Send(PyObject *iter, PyObject *arg, PyObject **result) +{ + *result = async_gen_asend_send(iter, arg); + if (*result != NULL) { + return PYGEN_NEXT; + } + if (_PyGen_FetchStopIterationValue(result) == 0) { + return PYGEN_RETURN; + } + return PYGEN_ERROR; +} + static PyObject * async_gen_asend_iternext(PyObject *ags) @@ -1918,10 +2105,8 @@ static PyMethodDef async_gen_asend_methods[] = { static PyAsyncMethods async_gen_asend_as_async = { - PyObject_SelfIter, /* am_await */ - 0, /* am_aiter */ - 0, /* am_anext */ - 0, /* am_send */ + .am_await = PyObject_SelfIter, + .am_send = _PyAsyncGenASend_Send, }; diff --git a/Objects/interpolationobject.c b/Objects/interpolationobject.c index b58adb693f0cae..e37724fb7852a2 100644 --- a/Objects/interpolationobject.c +++ b/Objects/interpolationobject.c @@ -138,7 +138,7 @@ static PyMethodDef interpolation_methods[] = { {"__reduce__", interpolation_reduce, METH_NOARGS, PyDoc_STR("__reduce__() -> (cls, state)")}, {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, PyDoc_STR("Interpolations are generic over the types of their values")}, {NULL, NULL}, }; diff --git a/Objects/iterobject.c b/Objects/iterobject.c index 5712e02ae828ab..e323987601d5d4 100644 --- a/Objects/iterobject.c +++ b/Objects/iterobject.c @@ -357,8 +357,9 @@ anextawaitable_getiter(anextawaitableobject *obj) } Py_SETREF(awaitable, new_awaitable); if (!PyIter_Check(awaitable)) { - PyErr_SetString(PyExc_TypeError, - "__await__ returned a non-iterable"); + PyErr_Format(PyExc_TypeError, + "%T.__await__() must return an iterable, not %T", + obj, awaitable); Py_DECREF(awaitable); return NULL; } diff --git a/Objects/lazyimportobject.c b/Objects/lazyimportobject.c new file mode 100644 index 00000000000000..fa1eb25047d961 --- /dev/null +++ b/Objects/lazyimportobject.c @@ -0,0 +1,157 @@ +// Lazy object implementation. + +#include "Python.h" +#include "pycore_ceval.h" +#include "pycore_frame.h" +#include "pycore_import.h" +#include "pycore_interpframe.h" +#include "pycore_lazyimportobject.h" +#include "pycore_modsupport.h" + +#define PyLazyImportObject_CAST(op) ((PyLazyImportObject *)(op)) + +PyObject * +_PyLazyImport_New(_PyInterpreterFrame *frame, PyObject *builtins, PyObject *name, PyObject *fromlist) +{ + PyLazyImportObject *m; + if (!name || !PyUnicode_Check(name)) { + PyErr_SetString(PyExc_TypeError, "expected str for name"); + return NULL; + } + if (fromlist == Py_None || fromlist == NULL) { + fromlist = NULL; + } + else if (!PyUnicode_Check(fromlist) && !PyTuple_Check(fromlist)) { + PyErr_SetString(PyExc_TypeError, + "lazy_import: fromlist must be None, a string, or a tuple"); + return NULL; + } + m = PyObject_GC_New(PyLazyImportObject, &PyLazyImport_Type); + if (m == NULL) { + return NULL; + } + m->lz_builtins = Py_XNewRef(builtins); + m->lz_from = Py_NewRef(name); + m->lz_attr = Py_XNewRef(fromlist); + + // Capture frame information for the original import location. + m->lz_code = NULL; + m->lz_instr_offset = -1; + + if (frame != NULL) { + PyCodeObject *code = _PyFrame_GetCode(frame); + if (code != NULL) { + m->lz_code = (PyCodeObject *)Py_NewRef(code); + // Calculate the instruction offset from the current frame. + m->lz_instr_offset = _PyInterpreterFrame_LASTI(frame); + } + } + + _PyObject_GC_TRACK(m); + return (PyObject *)m; +} + +static int +lazy_import_traverse(PyObject *op, visitproc visit, void *arg) +{ + PyLazyImportObject *m = PyLazyImportObject_CAST(op); + Py_VISIT(m->lz_builtins); + Py_VISIT(m->lz_from); + Py_VISIT(m->lz_attr); + Py_VISIT(m->lz_code); + return 0; +} + +static int +lazy_import_clear(PyObject *op) +{ + PyLazyImportObject *m = PyLazyImportObject_CAST(op); + Py_CLEAR(m->lz_builtins); + Py_CLEAR(m->lz_from); + Py_CLEAR(m->lz_attr); + Py_CLEAR(m->lz_code); + return 0; +} + +static void +lazy_import_dealloc(PyObject *op) +{ + _PyObject_GC_UNTRACK(op); + (void)lazy_import_clear(op); + Py_TYPE(op)->tp_free(op); +} + +static PyObject * +lazy_import_name(PyLazyImportObject *m) +{ + if (m->lz_attr != NULL) { + if (PyUnicode_Check(m->lz_attr)) { + return PyUnicode_FromFormat("%U.%U", m->lz_from, m->lz_attr); + } + else { + return PyUnicode_FromFormat("%U...", m->lz_from); + } + } + return Py_NewRef(m->lz_from); +} + +static PyObject * +lazy_import_repr(PyObject *op) +{ + PyLazyImportObject *m = PyLazyImportObject_CAST(op); + PyObject *name = lazy_import_name(m); + if (name == NULL) { + return NULL; + } + PyObject *res = PyUnicode_FromFormat("<%T '%U'>", op, name); + Py_DECREF(name); + return res; +} + +PyObject * +_PyLazyImport_GetName(PyObject *op) +{ + PyLazyImportObject *lazy_import = PyLazyImportObject_CAST(op); + assert(PyLazyImport_CheckExact(lazy_import)); + return lazy_import_name(lazy_import); +} + +static PyObject * +lazy_import_resolve(PyObject *self, PyObject *args) +{ + return _PyImport_LoadLazyImportTstate(PyThreadState_GET(), self); +} + +static PyMethodDef lazy_import_methods[] = { + { + "resolve", lazy_import_resolve, METH_NOARGS, + PyDoc_STR("resolves the lazy import and returns the actual object") + }, + {NULL, NULL} +}; + + +PyDoc_STRVAR(lazy_import_doc, +"lazy_import(builtins, name, fromlist=None, /)\n" +"--\n" +"\n" +"Represents a lazy import that will be resolved on first use.\n" +"\n" +"Instances of this object accessed from the global scope will be\n" +"automatically imported based upon their name and then replaced with\n" +"the imported value."); + +PyTypeObject PyLazyImport_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "lazy_import", + .tp_basicsize = sizeof(PyLazyImportObject), + .tp_dealloc = lazy_import_dealloc, + .tp_repr = lazy_import_repr, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .tp_doc = lazy_import_doc, + .tp_traverse = lazy_import_traverse, + .tp_clear = lazy_import_clear, + .tp_methods = lazy_import_methods, + .tp_alloc = PyType_GenericAlloc, + .tp_free = PyObject_GC_Del, +}; diff --git a/Objects/listobject.c b/Objects/listobject.c index 1b36f4c25abf4d..8a9c9bda68269b 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -6,16 +6,16 @@ #include "pycore_critical_section.h" // _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED() #include "pycore_dict.h" // _PyDictViewObject #include "pycore_freelist.h" // _Py_FREELIST_FREE(), _Py_FREELIST_POP() -#include "pycore_pyatomic_ft_wrappers.h" #include "pycore_interp.h" // PyInterpreterState.list #include "pycore_list.h" // struct _Py_list_freelist, _PyListIterObject #include "pycore_long.h" // _PyLong_DigitCount #include "pycore_modsupport.h" // _PyArg_NoKwnames() #include "pycore_object.h" // _PyObject_GC_TRACK(), _PyDebugAllocatorStats() +#include "pycore_pyatomic_ft_wrappers.h" +#include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_stackref.h" // _Py_TryIncrefCompareStackRef() -#include "pycore_tuple.h" // _PyTuple_FromArray() +#include "pycore_tuple.h" // _PyTuple_FromArraySteal() #include "pycore_typeobject.h" // _Py_TYPE_VERSION_LIST -#include "pycore_setobject.h" // _PySet_NextEntry() #include <stddef.h> /*[clinic input] @@ -96,11 +96,7 @@ ensure_shared_on_resize(PyListObject *self) * of the new slots at exit is undefined heap trash; it's the caller's * responsibility to overwrite them with sane values. * The number of allocated elements may grow, shrink, or stay the same. - * Failure is impossible if newsize <= self.allocated on entry, although - * that partly relies on an assumption that the system realloc() never - * fails when passed a number of bytes <= the number of bytes last - * allocated (the C standard doesn't guarantee this, but it's hard to - * imagine a realloc implementation where it wouldn't be true). + * Failure is impossible if newsize <= self.allocated on entry. * Note that self->ob_item may change, and even if newsize is less * than ob_size on entry. */ @@ -145,6 +141,11 @@ list_resize(PyListObject *self, Py_ssize_t newsize) #ifdef Py_GIL_DISABLED _PyListArray *array = list_allocate_array(new_allocated); if (array == NULL) { + if (newsize < allocated) { + // Never fail when shrinking allocations + Py_SET_SIZE(self, newsize); + return 0; + } PyErr_NoMemory(); return -1; } @@ -178,6 +179,11 @@ list_resize(PyListObject *self, Py_ssize_t newsize) items = NULL; } if (items == NULL) { + if (newsize < allocated) { + // Never fail when shrinking allocations + Py_SET_SIZE(self, newsize); + return 0; + } PyErr_NoMemory(); return -1; } @@ -228,7 +234,7 @@ _PyList_DebugMallocStats(FILE *out) _PyDebugAllocatorStats(out, "free PyListObject", _Py_FREELIST_SIZE(lists), - sizeof(PyListObject)); + _PyType_PreHeaderSize(&PyList_Type) + sizeof(PyListObject)); } PyObject * @@ -495,7 +501,7 @@ ins1(PyListObject *self, Py_ssize_t where, PyObject *v) where = n; items = self->ob_item; for (i = n; --i >= where; ) - FT_ATOMIC_STORE_PTR_RELAXED(items[i+1], items[i]); + FT_ATOMIC_STORE_PTR_RELEASE(items[i+1], items[i]); FT_ATOMIC_STORE_PTR_RELEASE(items[where], Py_NewRef(v)); return 0; } @@ -595,7 +601,7 @@ list_repr_impl(PyListObject *v) so must refetch the list size on each iteration. */ for (Py_ssize_t i = 0; i < Py_SIZE(v); ++i) { /* Hold a strong reference since repr(item) can mutate the list */ - item = Py_NewRef(v->ob_item[i]); + item = Py_XNewRef(v->ob_item[i]); if (i > 0) { if (PyUnicodeWriter_WriteChar(writer, ',') < 0) { @@ -710,6 +716,30 @@ list_slice_lock_held(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) return (PyObject *)np; } +PyObject * +_PyList_BinarySlice(PyObject *container, PyObject *start, PyObject *stop) +{ + assert(PyList_CheckExact(container)); + Py_ssize_t istart = 0; + Py_ssize_t istop = PY_SSIZE_T_MAX; + /* Unpack the index values before acquiring the lock, since + * _PyEval_SliceIndex may call __index__ which could execute + * arbitrary Python code. */ + if (!_PyEval_SliceIndex(start, &istart)) { + return NULL; + } + if (!_PyEval_SliceIndex(stop, &istop)) { + return NULL; + } + PyObject *ret; + Py_BEGIN_CRITICAL_SECTION(container); + Py_ssize_t len = Py_SIZE(container); + PySlice_AdjustIndices(len, &istart, &istop, 1); + ret = list_slice_lock_held((PyListObject *)container, istart, istop); + Py_END_CRITICAL_SECTION(); + return ret; +} + PyObject * PyList_GetSlice(PyObject *a, Py_ssize_t ilow, Py_ssize_t ihigh) { @@ -768,8 +798,8 @@ list_concat_lock_held(PyListObject *a, PyListObject *b) return (PyObject *)np; } -static PyObject * -list_concat(PyObject *aa, PyObject *bb) +PyObject * +_PyList_Concat(PyObject *aa, PyObject *bb) { if (!PyList_Check(bb)) { PyErr_Format(PyExc_TypeError, @@ -818,8 +848,8 @@ list_repeat_lock_held(PyListObject *a, Py_ssize_t n) _Py_RefcntAdd(*src, n); *dest++ = *src++; } - // TODO: _Py_memory_repeat calls are not safe for shared lists in - // GIL_DISABLED builds. (See issue #129069) + // This list is not yet visible to other threads, so atomic repeat + // is not necessary even in Py_GIL_DISABLED builds. _Py_memory_repeat((char *)np->ob_item, sizeof(PyObject *)*output_size, sizeof(PyObject *)*input_size); } @@ -882,6 +912,34 @@ list_clear_slot(PyObject *self) return 0; } +// Pointer-by-pointer memmove for PyObject** arrays that is safe +// for shared lists in Py_GIL_DISABLED builds. +static void +ptr_wise_atomic_memmove(PyListObject *a, PyObject **dest, PyObject **src, Py_ssize_t n) +{ +#ifndef Py_GIL_DISABLED + memmove(dest, src, n * sizeof(PyObject *)); +#else + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(a); + if (_Py_IsOwnedByCurrentThread((PyObject *)a) && !_PyObject_GC_IS_SHARED(a)) { + // No other threads can read this list concurrently + memmove(dest, src, n * sizeof(PyObject *)); + return; + } + if (dest < src) { + for (Py_ssize_t i = 0; i != n; i++) { + _Py_atomic_store_ptr_release(&dest[i], src[i]); + } + } + else { + // copy backwards to avoid overwriting src before it's read + for (Py_ssize_t i = n; i != 0; i--) { + _Py_atomic_store_ptr_release(&dest[i - 1], src[i - 1]); + } + } +#endif +} + /* a[ilow:ihigh] = v if v != NULL. * del a[ilow:ihigh] if v == NULL. * @@ -952,16 +1010,9 @@ list_ass_slice_lock_held(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyO } if (d < 0) { /* Delete -d items */ - Py_ssize_t tail; - tail = (Py_SIZE(a) - ihigh) * sizeof(PyObject *); - // TODO: these memmove/memcpy calls are not safe for shared lists in - // GIL_DISABLED builds. (See issue #129069) - memmove(&item[ihigh+d], &item[ihigh], tail); - if (list_resize(a, Py_SIZE(a) + d) < 0) { - memmove(&item[ihigh], &item[ihigh+d], tail); - memcpy(&item[ilow], recycle, s); - goto Error; - } + Py_ssize_t tail = Py_SIZE(a) - ihigh; + ptr_wise_atomic_memmove(a, &item[ihigh+d], &item[ihigh], tail); + (void)list_resize(a, Py_SIZE(a) + d); // NB: shrinking a list can't fail item = a->ob_item; } else if (d > 0) { /* Insert d items */ @@ -969,10 +1020,7 @@ list_ass_slice_lock_held(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyO if (list_resize(a, k+d) < 0) goto Error; item = a->ob_item; - // TODO: these memmove/memcpy calls are not safe for shared lists in - // GIL_DISABLED builds. (See issue #129069) - memmove(&item[ihigh+d], &item[ihigh], - (k - ihigh)*sizeof(PyObject *)); + ptr_wise_atomic_memmove(a, &item[ihigh+d], &item[ihigh], k - ihigh); } for (k = 0; k < n; k++, ilow++) { PyObject *w = vitem[k]; @@ -1056,10 +1104,17 @@ list_inplace_repeat_lock_held(PyListObject *self, Py_ssize_t n) for (Py_ssize_t j = 0; j < input_size; j++) { _Py_RefcntAdd(items[j], n-1); } - // TODO: _Py_memory_repeat calls are not safe for shared lists in - // GIL_DISABLED builds. (See issue #129069) +#ifndef Py_GIL_DISABLED _Py_memory_repeat((char *)items, sizeof(PyObject *)*output_size, sizeof(PyObject *)*input_size); +#else + Py_ssize_t copied = input_size; + while (copied < output_size) { + Py_ssize_t items_to_copy = Py_MIN(copied, output_size - copied); + ptr_wise_atomic_memmove(self, items + copied, items, items_to_copy); + copied += items_to_copy; + } +#endif return 0; } @@ -1091,7 +1146,7 @@ list_ass_item_lock_held(PyListObject *a, Py_ssize_t i, PyObject *v) if (v == NULL) { Py_ssize_t size = Py_SIZE(a); for (Py_ssize_t idx = i; idx < size - 1; idx++) { - FT_ATOMIC_STORE_PTR_RELAXED(a->ob_item[idx], a->ob_item[idx + 1]); + FT_ATOMIC_STORE_PTR_RELEASE(a->ob_item[idx], a->ob_item[idx + 1]); } Py_SET_SIZE(a, size - 1); } @@ -1384,7 +1439,7 @@ list_extend_dictitems(PyListObject *self, PyDictObject *dict) Py_ssize_t i = 0; PyObject *key, *value; while (_PyDict_Next((PyObject *)dict, &pos, &key, &value, NULL)) { - PyObject *item = PyTuple_Pack(2, key, value); + PyObject *item = _PyTuple_FromPair(key, value); if (item == NULL) { Py_SET_SIZE(self, m + i); return -1; @@ -1532,7 +1587,6 @@ list_pop_impl(PyListObject *self, Py_ssize_t index) /*[clinic end generated code: output=6bd69dcb3f17eca8 input=c269141068ae4b8f]*/ { PyObject *v; - int status; if (Py_SIZE(self) == 0) { /* Special-case most common failure cause */ @@ -1548,27 +1602,18 @@ list_pop_impl(PyListObject *self, Py_ssize_t index) PyObject **items = self->ob_item; v = items[index]; - const Py_ssize_t size_after_pop = Py_SIZE(self) - 1; - if (size_after_pop == 0) { + if (Py_SIZE(self) == 1) { Py_INCREF(v); list_clear(self); - status = 0; - } - else { - if ((size_after_pop - index) > 0) { - memmove(&items[index], &items[index+1], (size_after_pop - index) * sizeof(PyObject *)); - } - status = list_resize(self, size_after_pop); - } - if (status >= 0) { - return v; // and v now owns the reference the list had + return v; } - else { - // list resize failed, need to restore - memmove(&items[index+1], &items[index], (size_after_pop - index)* sizeof(PyObject *)); - items[index] = v; - return NULL; + Py_ssize_t size_after_pop = Py_SIZE(self) - 1; + if (index < size_after_pop) { + ptr_wise_atomic_memmove(self, &items[index], &items[index+1], + size_after_pop - index); } + list_resize(self, size_after_pop); // NB: shrinking a list can't fail + return v; } /* Reverse a slice of a list in place, from lo up to (exclusive) hi. */ @@ -1580,8 +1625,8 @@ reverse_slice(PyObject **lo, PyObject **hi) --hi; while (lo < hi) { PyObject *t = *lo; - *lo = *hi; - *hi = t; + FT_ATOMIC_STORE_PTR_RELEASE(*lo, *hi); + FT_ATOMIC_STORE_PTR_RELEASE(*hi, t); ++lo; --hi; } @@ -2750,11 +2795,12 @@ unsafe_object_compare(PyObject *v, PyObject *w, MergeState *ms) if (PyBool_Check(res_obj)) { res = (res_obj == Py_True); + assert(_Py_IsImmortal(res_obj)); } else { res = PyObject_IsTrue(res_obj); + Py_DECREF(res_obj); } - Py_DECREF(res_obj); /* Note that we can't assert * res == PyObject_RichCompareBool(v, w, Py_LT); @@ -2885,18 +2931,18 @@ list.sort Sort the list in ascending order and return None. -The sort is in-place (i.e. the list itself is modified) and stable (i.e. the -order of two equal elements is maintained). +The sort is in-place (i.e. the list itself is modified) and stable +(i.e. the order of two equal elements is maintained). -If a key function is given, apply it once to each list item and sort them, -ascending or descending, according to their function values. +If a key function is given, apply it once to each list item and sort +them, ascending or descending, according to their function values. The reverse flag can be set to sort in descending order. [clinic start generated code]*/ static PyObject * list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse) -/*[clinic end generated code: output=57b9f9c5e23fbe42 input=667bf25d0e3a3676]*/ +/*[clinic end generated code: output=57b9f9c5e23fbe42 input=c145526281e1fb9f]*/ { MergeState ms; Py_ssize_t nremaining; @@ -3220,7 +3266,7 @@ PyList_AsTuple(PyObject *v) PyObject *ret; PyListObject *self = (PyListObject *)v; Py_BEGIN_CRITICAL_SECTION(self); - ret = _PyTuple_FromArray(self->ob_item, Py_SIZE(v)); + ret = PyTuple_FromArray(self->ob_item, Py_SIZE(v)); Py_END_CRITICAL_SECTION(); return ret; } @@ -3236,10 +3282,8 @@ _PyList_AsTupleAndClear(PyListObject *self) Py_BEGIN_CRITICAL_SECTION(self); PyObject **items = self->ob_item; Py_ssize_t size = Py_SIZE(self); - self->ob_item = NULL; Py_SET_SIZE(self, 0); ret = _PyTuple_FromArraySteal(items, size); - free_list_items(items, false); Py_END_CRITICAL_SECTION(); return ret; } @@ -3536,8 +3580,14 @@ list___sizeof___impl(PyListObject *self) /*[clinic end generated code: output=3417541f95f9a53e input=b8030a5d5ce8a187]*/ { size_t res = _PyObject_SIZE(Py_TYPE(self)); - Py_ssize_t allocated = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->allocated); - res += (size_t)allocated * sizeof(void*); +#ifdef Py_GIL_DISABLED + PyObject **ob_item = _Py_atomic_load_ptr(&self->ob_item); + if (ob_item != NULL) { + res += list_capacity(ob_item) * sizeof(PyObject *); + } +#else + res += (size_t)self->allocated * sizeof(PyObject *); +#endif return PyLong_FromSize_t(res); } @@ -3560,13 +3610,14 @@ static PyMethodDef list_methods[] = { LIST_COUNT_METHODDEF LIST_REVERSE_METHODDEF LIST_SORT_METHODDEF - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("lists are generic over the type of their contents")}, {NULL, NULL} /* sentinel */ }; static PySequenceMethods list_as_sequence = { list_length, /* sq_length */ - list_concat, /* sq_concat */ + _PyList_Concat, /* sq_concat */ list_repeat, /* sq_repeat */ list_item, /* sq_item */ 0, /* sq_slice */ @@ -3742,16 +3793,13 @@ list_ass_subscript_lock_held(PyObject *_self, PyObject *item, PyObject *value) lim = Py_SIZE(self) - cur - 1; } - memmove(self->ob_item + cur - i, - self->ob_item + cur + 1, - lim * sizeof(PyObject *)); + ptr_wise_atomic_memmove(self, self->ob_item + cur - i, + self->ob_item + cur + 1, lim); } cur = start + (size_t)slicelength * step; if (cur < (size_t)Py_SIZE(self)) { - memmove(self->ob_item + cur - slicelength, - self->ob_item + cur, - (Py_SIZE(self) - cur) * - sizeof(PyObject *)); + ptr_wise_atomic_memmove(self, self->ob_item + cur - slicelength, + self->ob_item + cur, Py_SIZE(self) - cur); } Py_SET_SIZE(self, Py_SIZE(self) - slicelength); @@ -3823,7 +3871,7 @@ list_ass_subscript_lock_held(PyObject *_self, PyObject *item, PyObject *value) cur += (size_t)step, i++) { garbage[i] = selfitems[cur]; ins = Py_NewRef(seqitems[i]); - selfitems[cur] = ins; + FT_ATOMIC_STORE_PTR_RELEASE(selfitems[cur], ins); } for (i = 0; i < slicelength; i++) { @@ -3862,6 +3910,13 @@ list_ass_subscript(PyObject *self, PyObject *item, PyObject *value) return res; } +static _PyObjectIndexPair +list_iteritem(PyObject *obj, Py_ssize_t index) +{ + PyObject *result = list_get_item_ref((PyListObject *)obj, index); + return (_PyObjectIndexPair) { .object = result, .index = index + 1 }; +} + static PyMappingMethods list_as_mapping = { list_length, list_subscript, @@ -3912,6 +3967,7 @@ PyTypeObject PyList_Type = { PyObject_GC_Del, /* tp_free */ .tp_vectorcall = list_vectorcall, .tp_version_tag = _Py_TYPE_VERSION_LIST, + ._tp_iteritem = list_iteritem, }; /*********************** List Iterator **************************/ @@ -4248,7 +4304,9 @@ listiter_reduce_general(void *_it, int forward) } /* empty iterator, create an empty list */ list = PyList_New(0); - if (list == NULL) + if (list == NULL) { + Py_DECREF(iter); return NULL; + } return Py_BuildValue("N(N)", iter, list); } diff --git a/Objects/lnotab_notes.txt b/Objects/lnotab_notes.txt deleted file mode 100644 index 335e441cfded3d..00000000000000 --- a/Objects/lnotab_notes.txt +++ /dev/null @@ -1,228 +0,0 @@ -Description of the internal format of the line number table in Python 3.10 -and earlier. - -(For 3.11 onwards, see InternalDocs/code_objects.md) - -Conceptually, the line number table consists of a sequence of triples: - start-offset (inclusive), end-offset (exclusive), line-number. - -Note that not all byte codes have a line number so we need handle `None` for the line-number. - -However, storing the above sequence directly would be very inefficient as we would need 12 bytes per entry. - -First, note that the end of one entry is the same as the start of the next, so we can overlap entries. -Second, we don't really need arbitrary access to the sequence, so we can store deltas. - -We just need to store (end - start, line delta) pairs. The start offset of the first entry is always zero. - -Third, most deltas are small, so we can use a single byte for each value, as long we allow several entries for the same line. - -Consider the following table - Start End Line - 0 6 1 - 6 50 2 - 50 350 7 - 350 360 No line number - 360 376 8 - 376 380 208 - -Stripping the redundant ends gives: - - End-Start Line-delta - 6 +1 - 44 +1 - 300 +5 - 10 No line number - 16 +1 - 4 +200 - - -Note that the end - start value is always positive. - -Finally, in order to fit into a single byte we need to convert start deltas to the range 0 <= delta <= 254, -and line deltas to the range -127 <= delta <= 127. -A line delta of -128 is used to indicate no line number. -Also note that a delta of zero indicates that there are no bytecodes in the given range, -which means we can use an invalid line number for that range. - -Final form: - - Start delta Line delta - 6 +1 - 44 +1 - 254 +5 - 46 0 - 10 -128 (No line number, treated as a delta of zero) - 16 +1 - 0 +127 (line 135, but the range is empty as no bytecodes are at line 135) - 4 +73 - -Iterating over the table. -------------------------- - -For the `co_lines` method we want to emit the full form, omitting the (350, 360, No line number) and empty entries. - -The code is as follows: - -def co_lines(code): - line = code.co_firstlineno - end = 0 - table_iter = iter(code.internal_line_table): - for sdelta, ldelta in table_iter: - if ldelta == 0: # No change to line number, just accumulate changes to end - end += sdelta - continue - start = end - end = start + sdelta - if ldelta == -128: # No valid line number -- skip entry - continue - line += ldelta - if end == start: # Empty range, omit. - continue - yield start, end, line - - - - -The historical co_lnotab format -------------------------------- - -prior to 3.10 code objects stored a field named co_lnotab. -This was an array of unsigned bytes disguised as a Python bytes object. - -The old co_lnotab did not account for the presence of bytecodes without a line number, -nor was it well suited to tracing as a number of workarounds were required. - -The old format can still be accessed via `code.co_lnotab`, which is lazily computed from the new format. - -Below is the description of the old co_lnotab format: - - -The array is conceptually a compressed list of - (bytecode offset increment, line number increment) -pairs. The details are important and delicate, best illustrated by example: - - byte code offset source code line number - 0 1 - 6 2 - 50 7 - 350 207 - 361 208 - -Instead of storing these numbers literally, we compress the list by storing only -the difference from one row to the next. Conceptually, the stored list might -look like: - - 0, 1, 6, 1, 44, 5, 300, 200, 11, 1 - -The above doesn't really work, but it's a start. An unsigned byte (byte code -offset) can't hold negative values, or values larger than 255, a signed byte -(line number) can't hold values larger than 127 or less than -128, and the -above example contains two such values. (Note that before 3.6, line number -was also encoded by an unsigned byte.) So we make two tweaks: - - (a) there's a deep assumption that byte code offsets increase monotonically, - and - (b) if byte code offset jumps by more than 255 from one row to the next, or if - source code line number jumps by more than 127 or less than -128 from one row - to the next, more than one pair is written to the table. In case #b, - there's no way to know from looking at the table later how many were written. - That's the delicate part. A user of co_lnotab desiring to find the source - line number corresponding to a bytecode address A should do something like - this: - - lineno = addr = 0 - for addr_incr, line_incr in co_lnotab: - addr += addr_incr - if addr > A: - return lineno - if line_incr >= 0x80: - line_incr -= 0x100 - lineno += line_incr - -(In C, this is implemented by PyCode_Addr2Line().) In order for this to work, -when the addr field increments by more than 255, the line # increment in each -pair generated must be 0 until the remaining addr increment is < 256. So, in -the example above, assemble_lnotab in compile.c should not (as was actually done -until 2.2) expand 300, 200 to - 255, 255, 45, 45, -but to - 255, 0, 45, 127, 0, 73. - -The above is sufficient to reconstruct line numbers for tracebacks, but not for -line tracing. Tracing is handled by PyCode_CheckLineNumber() in codeobject.c -and maybe_call_line_trace() in ceval.c. - -*** Tracing *** - -To a first approximation, we want to call the tracing function when the line -number of the current instruction changes. Re-computing the current line for -every instruction is a little slow, though, so each time we compute the line -number we save the bytecode indices where it's valid: - - *instr_lb <= frame->f_lasti < *instr_ub - -is true so long as execution does not change lines. That is, *instr_lb holds -the first bytecode index of the current line, and *instr_ub holds the first -bytecode index of the next line. As long as the above expression is true, -maybe_call_line_trace() does not need to call PyCode_CheckLineNumber(). Note -that the same line may appear multiple times in the lnotab, either because the -bytecode jumped more than 255 indices between line number changes or because -the compiler inserted the same line twice. Even in that case, *instr_ub holds -the first index of the next line. - -However, we don't *always* want to call the line trace function when the above -test fails. - -Consider this code: - -1: def f(a): -2: while a: -3: print(1) -4: break -5: else: -6: print(2) - -which compiles to this: - - 2 0 SETUP_LOOP 26 (to 28) - >> 2 LOAD_FAST 0 (a) - 4 POP_JUMP_IF_FALSE 18 - - 3 6 LOAD_GLOBAL 0 (print) - 8 LOAD_CONST 1 (1) - 10 CALL_NO_KW 1 - 12 POP_TOP - - 4 14 BREAK_LOOP - 16 JUMP_ABSOLUTE 2 - >> 18 POP_BLOCK - - 6 20 LOAD_GLOBAL 0 (print) - 22 LOAD_CONST 2 (2) - 24 CALL_NO_KW 1 - 26 POP_TOP - >> 28 LOAD_CONST 0 (None) - 30 RETURN_VALUE - -If 'a' is false, execution will jump to the POP_BLOCK instruction at offset 18 -and the co_lnotab will claim that execution has moved to line 4, which is wrong. -In this case, we could instead associate the POP_BLOCK with line 5, but that -would break jumps around loops without else clauses. - -We fix this by only calling the line trace function for a forward jump if the -co_lnotab indicates we have jumped to the *start* of a line, i.e. if the current -instruction offset matches the offset given for the start of a line by the -co_lnotab. For backward jumps, however, we always call the line trace function, -which lets a debugger stop on every evaluation of a loop guard (which usually -won't be the first opcode in a line). - -Why do we set f_lineno when tracing, and only just before calling the trace -function? Well, consider the code above when 'a' is true. If stepping through -this with 'n' in pdb, you would stop at line 1 with a "call" type event, then -line events on lines 2, 3, and 4, then a "return" type event -- but because the -code for the return actually falls in the range of the "line 6" opcodes, you -would be shown line 6 during this event. This is a change from the behaviour in -2.2 and before, and I've found it confusing in practice. By setting and using -f_lineno when tracing, one can report a line number different from that -suggested by f_lasti on this one occasion where it's desirable. diff --git a/Objects/longobject.c b/Objects/longobject.c index 581db10b54ab57..6e6011cb19aab5 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -12,6 +12,7 @@ #include "pycore_runtime.h" // _PY_NSMALLPOSINTS #include "pycore_stackref.h" #include "pycore_structseq.h" // _PyStructSequence_FiniBuiltin() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #include "pycore_unicodeobject.h" // _PyUnicode_Equal() #include <float.h> // DBL_MANT_DIG @@ -25,7 +26,7 @@ class int "PyObject *" "&PyLong_Type" #define medium_value(x) ((stwodigits)_PyLong_CompactValue(x)) -#define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS) +#define IS_SMALL_INT(ival) _PY_IS_SMALL_INT(ival) #define IS_SMALL_UINT(ival) ((ival) < _PY_NSMALLPOSINTS) #define _MAX_STR_DIGITS_ERROR_FMT_TO_INT "Exceeds the limit (%d digits) for integer string conversion: value has %zd digits; use sys.set_int_max_str_digits() to increase the limit" @@ -184,11 +185,14 @@ long_alloc(Py_ssize_t size) return NULL; } _PyObject_Init((PyObject*)result, &PyLong_Type); + _PyLong_InitTag(result); } _PyLong_SetSignAndDigitCount(result, size != 0, size); - /* The digit has to be initialized explicitly to avoid - * use-of-uninitialized-value. */ - result->long_value.ob_digit[0] = 0; +#ifdef Py_DEBUG + // gh-147988: Fill digits with an invalid pattern to catch usage + // of uninitialized digits. + memset(result->long_value.ob_digit, 0xFF, ndigits * sizeof(digit)); +#endif return result; } @@ -257,6 +261,7 @@ _PyLong_FromMedium(sdigit x) return NULL; } _PyObject_Init((PyObject*)v, &PyLong_Type); + _PyLong_InitTag(v); } digit abs_x = x < 0 ? -x : x; _PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1); @@ -336,6 +341,7 @@ medium_from_stwodigits(stwodigits x) return PyStackRef_NULL; } _PyObject_Init((PyObject*)v, &PyLong_Type); + _PyLong_InitTag(v); } digit abs_x = x < 0 ? (digit)(-x) : (digit)x; _PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1); @@ -352,7 +358,7 @@ _PyLong_Negate(PyLongObject **x_p) PyLongObject *x; x = (PyLongObject *)*x_p; - if (Py_REFCNT(x) == 1) { + if (_PyObject_IsUniquelyReferenced((PyObject *)x)) { _PyLong_FlipSign(x); return; } @@ -1093,6 +1099,7 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, int sign = is_signed ? -1: 1; if (idigit == 0) { sign = 0; + v->long_value.ob_digit[0] = 0; } _PyLong_SetSignAndDigitCount(v, sign, idigit); return (PyObject *)maybe_small_long(long_normalize(v)); @@ -1209,13 +1216,19 @@ _PyLong_AsByteArray(PyLongObject* v, *p = (unsigned char)(accum & 0xff); p += pincr; } - else if (j == n && n > 0 && is_signed) { + else if (j == n && is_signed) { /* The main loop filled the byte array exactly, so the code just above didn't get to ensure there's a sign bit, and the loop below wouldn't add one either. Make sure a sign bit exists. */ - unsigned char msb = *(p - pincr); - int sign_bit_set = msb >= 0x80; + int sign_bit_set; + if (n > 0) { + unsigned char msb = *(p - pincr); + sign_bit_set = msb >= 0x80; + } + else { + sign_bit_set = 0; + } assert(accumbits == 0); if (sign_bit_set == do_twos_comp) return 0; @@ -2014,7 +2027,7 @@ static int pylong_int_to_decimal_string(PyObject *aa, PyObject **p_output, _PyUnicodeWriter *writer, - _PyBytesWriter *bytes_writer, + PyBytesWriter *bytes_writer, char **bytes_str) { PyObject *s = NULL; @@ -2045,7 +2058,8 @@ pylong_int_to_decimal_string(PyObject *aa, Py_ssize_t size = PyUnicode_GET_LENGTH(s); const void *data = PyUnicode_DATA(s); int kind = PyUnicode_KIND(s); - *bytes_str = _PyBytesWriter_Prepare(bytes_writer, *bytes_str, size); + *bytes_str = PyBytesWriter_GrowAndUpdatePointer(bytes_writer, size, + *bytes_str); if (*bytes_str == NULL) { goto error; } @@ -2082,7 +2096,7 @@ static int long_to_decimal_string_internal(PyObject *aa, PyObject **p_output, _PyUnicodeWriter *writer, - _PyBytesWriter *bytes_writer, + PyBytesWriter *bytes_writer, char **bytes_str) { PyLongObject *scratch, *a; @@ -2208,7 +2222,8 @@ long_to_decimal_string_internal(PyObject *aa, } } else if (bytes_writer) { - *bytes_str = _PyBytesWriter_Prepare(bytes_writer, *bytes_str, strlen); + *bytes_str = PyBytesWriter_GrowAndUpdatePointer(bytes_writer, strlen, + *bytes_str); if (*bytes_str == NULL) { Py_DECREF(scratch); return -1; @@ -2318,7 +2333,7 @@ long_to_decimal_string(PyObject *aa) static int long_format_binary(PyObject *aa, int base, int alternate, PyObject **p_output, _PyUnicodeWriter *writer, - _PyBytesWriter *bytes_writer, char **bytes_str) + PyBytesWriter *bytes_writer, char **bytes_str) { PyLongObject *a = (PyLongObject *)aa; PyObject *v = NULL; @@ -2379,7 +2394,8 @@ long_format_binary(PyObject *aa, int base, int alternate, return -1; } else if (bytes_writer) { - *bytes_str = _PyBytesWriter_Prepare(bytes_writer, *bytes_str, sz); + *bytes_str = PyBytesWriter_GrowAndUpdatePointer(bytes_writer, sz, + *bytes_str); if (*bytes_str == NULL) return -1; } @@ -2508,7 +2524,7 @@ _PyLong_FormatWriter(_PyUnicodeWriter *writer, } char* -_PyLong_FormatBytesWriter(_PyBytesWriter *writer, char *str, +_PyLong_FormatBytesWriter(PyBytesWriter *writer, char *str, PyObject *obj, int base, int alternate) { @@ -2842,6 +2858,7 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits, *res = NULL; return 0; } + z->long_value.ob_digit[0] = 0; _PyLong_SetSignAndDigitCount(z, 0, 0); /* `convwidth` consecutive input digits are treated as a single @@ -3109,11 +3126,11 @@ PyLong_FromString(const char *str, char **pend, int base) } /* Set sign and normalize */ - if (sign < 0) { - _PyLong_FlipSign(z); - } long_normalize(z); z = maybe_small_long(z); + if (sign < 0) { + _PyLong_Negate(&z); + } if (pend != NULL) { *pend = (char *)str; @@ -3355,6 +3372,7 @@ x_divrem(PyLongObject *v1, PyLongObject *w1, PyLongObject **prem) *prem = NULL; return NULL; } + a->long_value.ob_digit[0] = 0; v0 = v->long_value.ob_digit; w0 = w->long_value.ob_digit; wm1 = w0[size_w-1]; @@ -3613,21 +3631,11 @@ long_richcompare(PyObject *self, PyObject *other, int op) Py_RETURN_RICHCOMPARE(result, 0, op); } -static inline int -/// Return 1 if the object is one of the immortal small ints -_long_is_small_int(PyObject *op) -{ - PyLongObject *long_object = (PyLongObject *)op; - int is_small_int = (long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0; - assert((!is_small_int) || PyLong_CheckExact(op)); - return is_small_int; -} - void _PyLong_ExactDealloc(PyObject *self) { assert(PyLong_CheckExact(self)); - if (_long_is_small_int(self)) { + if (_PyLong_IsSmallInt((PyLongObject *)self)) { // See PEP 683, section Accidental De-Immortalizing for details _Py_SetImmortal(self); return; @@ -3642,7 +3650,7 @@ _PyLong_ExactDealloc(PyObject *self) static void long_dealloc(PyObject *self) { - if (_long_is_small_int(self)) { + if (_PyLong_IsSmallInt((PyLongObject *)self)) { /* This should never get called, but we also don't want to SEGV if * we accidentally decref small Ints out of existence. Instead, * since small Ints are immortal, re-set the reference count. @@ -3676,38 +3684,54 @@ long_hash(PyObject *obj) } i = _PyLong_DigitCount(v); sign = _PyLong_NonCompactSign(v); - x = 0; + + // unroll first digit + Py_BUILD_ASSERT(PyHASH_BITS > PyLong_SHIFT); + assert(i >= 1); + --i; + x = v->long_value.ob_digit[i]; + assert(x < PyHASH_MODULUS); + +#if PyHASH_BITS >= 2 * PyLong_SHIFT + // unroll second digit + assert(i >= 1); + --i; + x <<= PyLong_SHIFT; + x += v->long_value.ob_digit[i]; + assert(x < PyHASH_MODULUS); +#endif + while (--i >= 0) { - /* Here x is a quantity in the range [0, _PyHASH_MODULUS); we + /* Here x is a quantity in the range [0, PyHASH_MODULUS); we want to compute x * 2**PyLong_SHIFT + v->long_value.ob_digit[i] modulo - _PyHASH_MODULUS. + PyHASH_MODULUS. - The computation of x * 2**PyLong_SHIFT % _PyHASH_MODULUS + The computation of x * 2**PyLong_SHIFT % PyHASH_MODULUS amounts to a rotation of the bits of x. To see this, write - x * 2**PyLong_SHIFT = y * 2**_PyHASH_BITS + z + x * 2**PyLong_SHIFT = y * 2**PyHASH_BITS + z - where y = x >> (_PyHASH_BITS - PyLong_SHIFT) gives the top + where y = x >> (PyHASH_BITS - PyLong_SHIFT) gives the top PyLong_SHIFT bits of x (those that are shifted out of the - original _PyHASH_BITS bits, and z = (x << PyLong_SHIFT) & - _PyHASH_MODULUS gives the bottom _PyHASH_BITS - PyLong_SHIFT - bits of x, shifted up. Then since 2**_PyHASH_BITS is - congruent to 1 modulo _PyHASH_MODULUS, y*2**_PyHASH_BITS is - congruent to y modulo _PyHASH_MODULUS. So + original PyHASH_BITS bits, and z = (x << PyLong_SHIFT) & + PyHASH_MODULUS gives the bottom PyHASH_BITS - PyLong_SHIFT + bits of x, shifted up. Then since 2**PyHASH_BITS is + congruent to 1 modulo PyHASH_MODULUS, y*2**PyHASH_BITS is + congruent to y modulo PyHASH_MODULUS. So - x * 2**PyLong_SHIFT = y + z (mod _PyHASH_MODULUS). + x * 2**PyLong_SHIFT = y + z (mod PyHASH_MODULUS). The right-hand side is just the result of rotating the - _PyHASH_BITS bits of x left by PyLong_SHIFT places; since - not all _PyHASH_BITS bits of x are 1s, the same is true - after rotation, so 0 <= y+z < _PyHASH_MODULUS and y + z is + PyHASH_BITS bits of x left by PyLong_SHIFT places; since + not all PyHASH_BITS bits of x are 1s, the same is true + after rotation, so 0 <= y+z < PyHASH_MODULUS and y + z is the reduction of x*2**PyLong_SHIFT modulo - _PyHASH_MODULUS. */ - x = ((x << PyLong_SHIFT) & _PyHASH_MODULUS) | - (x >> (_PyHASH_BITS - PyLong_SHIFT)); + PyHASH_MODULUS. */ + x = ((x << PyLong_SHIFT) & PyHASH_MODULUS) | + (x >> (PyHASH_BITS - PyLong_SHIFT)); x += v->long_value.ob_digit[i]; - if (x >= _PyHASH_MODULUS) - x -= _PyHASH_MODULUS; + if (x >= PyHASH_MODULUS) + x -= PyHASH_MODULUS; } x = x * sign; if (x == (Py_uhash_t)-1) @@ -4125,10 +4149,6 @@ k_mul(PyLongObject *a, PyLongObject *b) /* 1. Allocate result space. */ ret = long_alloc(asize + bsize); if (ret == NULL) goto fail; -#ifdef Py_DEBUG - /* Fill with trash, to catch reference to uninitialized digits. */ - memset(ret->long_value.ob_digit, 0xDF, _PyLong_DigitCount(ret) * sizeof(digit)); -#endif /* 2. t1 <- ah*bh, and copy into high digits of result. */ if ((t1 = k_mul(ah, bh)) == NULL) goto fail; @@ -4409,10 +4429,10 @@ pylong_int_divmod(PyLongObject *v, PyLongObject *w, if (result == NULL) { return -1; } - if (!PyTuple_Check(result)) { + if (!PyTuple_Check(result) || PyTuple_GET_SIZE(result) != 2) { Py_DECREF(result); PyErr_SetString(PyExc_ValueError, - "tuple is required from int_divmod()"); + "tuple of length 2 is required from int_divmod()"); return -1; } PyObject *q = PyTuple_GET_ITEM(result, 0); @@ -4853,23 +4873,12 @@ static PyObject * long_divmod(PyObject *a, PyObject *b) { PyLongObject *div, *mod; - PyObject *z; - CHECK_BINOP(a, b); if (l_divmod((PyLongObject*)a, (PyLongObject*)b, &div, &mod) < 0) { return NULL; } - z = PyTuple_New(2); - if (z != NULL) { - PyTuple_SET_ITEM(z, 0, (PyObject *) div); - PyTuple_SET_ITEM(z, 1, (PyObject *) mod); - } - else { - Py_DECREF(div); - Py_DECREF(mod); - } - return z; + return _PyTuple_FromPairSteal((PyObject *)div, (PyObject *)mod); } @@ -5564,46 +5573,45 @@ long_bitwise(PyLongObject *a, Py_ssize_t size_a, size_b, size_z, i; PyLongObject *z; + PyLongObject *new_a = NULL; + PyLongObject *new_b = NULL; + /* Bitwise operations for negative numbers operate as though on a two's complement representation. So convert arguments from sign-magnitude to two's complement, and convert the result back to sign-magnitude at the end. */ - /* If a is negative, replace it by its two's complement. */ size_a = _PyLong_DigitCount(a); + size_b = _PyLong_DigitCount(b); + /* Swap a and b if necessary to ensure size_a >= size_b. */ + if (size_a < size_b) { + z = a; a = b; b = z; + size_z = size_a; size_a = size_b; size_b = size_z; + } + + /* If a is negative, replace it by its two's complement. */ nega = _PyLong_IsNegative(a); if (nega) { z = long_alloc(size_a); if (z == NULL) return NULL; v_complement(z->long_value.ob_digit, a->long_value.ob_digit, size_a); + new_a = z; // reference to decrement instead of a itself a = z; } - else - /* Keep reference count consistent. */ - Py_INCREF(a); /* Same for b. */ - size_b = _PyLong_DigitCount(b); negb = _PyLong_IsNegative(b); if (negb) { z = long_alloc(size_b); if (z == NULL) { - Py_DECREF(a); + Py_XDECREF(new_a); return NULL; } v_complement(z->long_value.ob_digit, b->long_value.ob_digit, size_b); + new_b = z; // reference to decrement instead of b itself b = z; } - else - Py_INCREF(b); - - /* Swap a and b if necessary to ensure size_a >= size_b. */ - if (size_a < size_b) { - z = a; a = b; b = z; - size_z = size_a; size_a = size_b; size_b = size_z; - negz = nega; nega = negb; negb = negz; - } /* JRH: The original logic here was to allocate the result value (z) as the longer of the two operands. However, there are some cases @@ -5629,12 +5637,18 @@ long_bitwise(PyLongObject *a, Py_UNREACHABLE(); } + if ((size_z + negz) == 0) { + Py_XDECREF(new_a); + Py_XDECREF(new_b); + return get_small_int(0); + } + /* We allow an extra digit if z is negative, to make sure that the final two's complement of z doesn't overflow. */ z = long_alloc(size_z + negz); if (z == NULL) { - Py_DECREF(a); - Py_DECREF(b); + Py_XDECREF(new_a); + Py_XDECREF(new_b); return NULL; } @@ -5671,8 +5685,8 @@ long_bitwise(PyLongObject *a, v_complement(z->long_value.ob_digit, z->long_value.ob_digit, size_z+1); } - Py_DECREF(a); - Py_DECREF(b); + Py_XDECREF(new_a); + Py_XDECREF(new_b); return (PyObject *)maybe_small_long(long_normalize(z)); } @@ -5824,7 +5838,7 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) assert(size_a >= 0); _PyLong_SetSignAndDigitCount(c, 1, size_a); } - else if (Py_REFCNT(a) == 1) { + else if (_PyObject_IsUniquelyReferenced((PyObject *)a)) { c = (PyLongObject*)Py_NewRef(a); } else { @@ -5838,7 +5852,8 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg) assert(size_a >= 0); _PyLong_SetSignAndDigitCount(d, 1, size_a); } - else if (Py_REFCNT(b) == 1 && size_a <= alloc_b) { + else if (_PyObject_IsUniquelyReferenced((PyObject *)b) + && size_a <= alloc_b) { d = (PyLongObject*)Py_NewRef(b); assert(size_a >= 0); _PyLong_SetSignAndDigitCount(d, 1, size_a); @@ -6006,29 +6021,34 @@ static PyObject * long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase) { PyLongObject *tmp, *newobj; - Py_ssize_t i, n; + Py_ssize_t size, ndigits; + int sign; assert(PyType_IsSubtype(type, &PyLong_Type)); tmp = (PyLongObject *)long_new_impl(&PyLong_Type, x, obase); if (tmp == NULL) return NULL; assert(PyLong_Check(tmp)); - n = _PyLong_DigitCount(tmp); + size = _PyLong_DigitCount(tmp); /* Fast operations for single digit integers (including zero) * assume that there is always at least one digit present. */ - if (n == 0) { - n = 1; - } - newobj = (PyLongObject *)type->tp_alloc(type, n); + ndigits = size ? size : 1; + newobj = (PyLongObject *)type->tp_alloc(type, ndigits); if (newobj == NULL) { Py_DECREF(tmp); return NULL; } assert(PyLong_Check(newobj)); - newobj->long_value.lv_tag = tmp->long_value.lv_tag & ~IMMORTALITY_BIT_MASK; - for (i = 0; i < n; i++) { - newobj->long_value.ob_digit[i] = tmp->long_value.ob_digit[i]; + if (_PyLong_IsCompact(tmp)) { + sign = _PyLong_CompactSign(tmp); } + else { + sign = _PyLong_NonCompactSign(tmp); + } + _PyLong_InitTag(newobj); + _PyLong_SetSignAndDigitCount(newobj, sign, size); + memcpy(newobj->long_value.ob_digit, tmp->long_value.ob_digit, + ndigits * sizeof(digit)); Py_DECREF(tmp); return (PyObject *)newobj; } @@ -6093,7 +6113,7 @@ PyObject * _PyLong_DivmodNear(PyObject *a, PyObject *b) { PyLongObject *quo = NULL, *rem = NULL; - PyObject *twice_rem, *result, *temp; + PyObject *twice_rem, *temp; int quo_is_odd, quo_is_neg; Py_ssize_t cmp; @@ -6159,14 +6179,7 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b) goto error; } - result = PyTuple_New(2); - if (result == NULL) - goto error; - - /* PyTuple_SET_ITEM steals references */ - PyTuple_SET_ITEM(result, 0, (PyObject *)quo); - PyTuple_SET_ITEM(result, 1, (PyObject *)rem); - return result; + return _PyTuple_FromPairSteal((PyObject *)quo, (PyObject *)rem); error: Py_XDECREF(quo); @@ -6293,6 +6306,7 @@ popcount_digit(digit d) } /*[clinic input] +@permit_long_summary int.bit_count Number of ones in the binary representation of the absolute value of self. @@ -6307,7 +6321,7 @@ Also known as the population count. static PyObject * int_bit_count_impl(PyObject *self) -/*[clinic end generated code: output=2e571970daf1e5c3 input=7e0adef8e8ccdf2e]*/ +/*[clinic end generated code: output=2e571970daf1e5c3 input=f2510a306761db15]*/ { assert(self != NULL); assert(PyLong_Check(self)); @@ -6342,34 +6356,32 @@ static PyObject * int_as_integer_ratio_impl(PyObject *self) /*[clinic end generated code: output=e60803ae1cc8621a input=384ff1766634bec2]*/ { - PyObject *ratio_tuple; PyObject *numerator = long_long(self); if (numerator == NULL) { return NULL; } - ratio_tuple = PyTuple_Pack(2, numerator, _PyLong_GetOne()); - Py_DECREF(numerator); - return ratio_tuple; + return _PyTuple_FromPairSteal(numerator, _PyLong_GetOne()); } /*[clinic input] int.to_bytes - length: Py_ssize_t = 1 - Length of bytes object to use. An OverflowError is raised if the - integer is not representable with the given number of bytes. Default - is length 1. + length: Py_ssize_t(allow_negative=False) = 1 + Length of bytes object to use. An OverflowError is raised if + the integer is not representable with the given number of bytes. + Default is length 1. byteorder: unicode(c_default="NULL") = "big" - The byte order used to represent the integer. If byteorder is 'big', - the most significant byte is at the beginning of the byte array. If - byteorder is 'little', the most significant byte is at the end of the - byte array. To request the native byte order of the host system, use - sys.byteorder as the byte order value. Default is to use 'big'. + The byte order used to represent the integer. If byteorder is + 'big', the most significant byte is at the beginning of the byte + array. If byteorder is 'little', the most significant byte is at + the end of the byte array. To request the native byte order of + the host system, use sys.byteorder as the byte order value. + Default is to use 'big'. * signed as is_signed: bool = False - Determines whether two's complement is used to represent the integer. - If signed is False and a negative integer is given, an OverflowError - is raised. + Determines whether two's complement is used to represent the + integer. If signed is False and a negative integer is given, + an OverflowError is raised. Return an array of bytes representing an integer. [clinic start generated code]*/ @@ -6377,11 +6389,9 @@ Return an array of bytes representing an integer. static PyObject * int_to_bytes_impl(PyObject *self, Py_ssize_t length, PyObject *byteorder, int is_signed) -/*[clinic end generated code: output=89c801df114050a3 input=a0103d0e9ad85c2b]*/ +/*[clinic end generated code: output=89c801df114050a3 input=c74a93c07b2f6526]*/ { int little_endian; - PyObject *bytes; - if (byteorder == NULL) little_endian = 0; else if (_PyUnicode_Equal(byteorder, &_Py_ID(little))) @@ -6394,24 +6404,19 @@ int_to_bytes_impl(PyObject *self, Py_ssize_t length, PyObject *byteorder, return NULL; } - if (length < 0) { - PyErr_SetString(PyExc_ValueError, - "length argument must be non-negative"); + PyBytesWriter *writer = PyBytesWriter_Create(length); + if (writer == NULL) { return NULL; } - bytes = PyBytes_FromStringAndSize(NULL, length); - if (bytes == NULL) - return NULL; - if (_PyLong_AsByteArray((PyLongObject *)self, - (unsigned char *)PyBytes_AS_STRING(bytes), + PyBytesWriter_GetData(writer), length, little_endian, is_signed, 1) < 0) { - Py_DECREF(bytes); + PyBytesWriter_Discard(writer); return NULL; } - return bytes; + return PyBytesWriter_Finish(writer); } /*[clinic input] @@ -6420,18 +6425,20 @@ int.from_bytes bytes as bytes_obj: object Holds the array of bytes to convert. The argument must either - support the buffer protocol or be an iterable object producing bytes. - Bytes and bytearray are examples of built-in objects that support the - buffer protocol. + support the buffer protocol or be an iterable object producing + bytes. Bytes and bytearray are examples of built-in objects that + support the buffer protocol. byteorder: unicode(c_default="NULL") = "big" - The byte order used to represent the integer. If byteorder is 'big', - the most significant byte is at the beginning of the byte array. If - byteorder is 'little', the most significant byte is at the end of the - byte array. To request the native byte order of the host system, use - sys.byteorder as the byte order value. Default is to use 'big'. + The byte order used to represent the integer. If byteorder is + 'big', the most significant byte is at the beginning of the byte + array. If byteorder is 'little', the most significant byte is at + the end of the byte array. To request the native byte order of + the host system, use sys.byteorder as the byte order value. + Default is to use 'big'. * signed as is_signed: bool = False - Indicates whether two's complement is used to represent the integer. + Indicates whether two's complement is used to represent the + integer. Return the integer represented by the given array of bytes. [clinic start generated code]*/ @@ -6439,7 +6446,7 @@ Return the integer represented by the given array of bytes. static PyObject * int_from_bytes_impl(PyTypeObject *type, PyObject *bytes_obj, PyObject *byteorder, int is_signed) -/*[clinic end generated code: output=efc5d68e31f9314f input=2ff527997fe7b0c5]*/ +/*[clinic end generated code: output=efc5d68e31f9314f input=95801e50b942e164]*/ { int little_endian; PyObject *long_obj, *bytes; @@ -6456,14 +6463,33 @@ int_from_bytes_impl(PyTypeObject *type, PyObject *bytes_obj, return NULL; } - bytes = PyObject_Bytes(bytes_obj); - if (bytes == NULL) - return NULL; - - long_obj = _PyLong_FromByteArray( - (unsigned char *)PyBytes_AS_STRING(bytes), Py_SIZE(bytes), - little_endian, is_signed); - Py_DECREF(bytes); + /* Fast-path exact bytes. */ + if (PyBytes_CheckExact(bytes_obj)) { + long_obj = _PyLong_FromByteArray( + (unsigned char *)PyBytes_AS_STRING(bytes_obj), Py_SIZE(bytes_obj), + little_endian, is_signed); + } + /* Use buffer protocol to avoid copies. */ + else if (PyObject_CheckBuffer(bytes_obj)) { + Py_buffer view; + if (PyObject_GetBuffer(bytes_obj, &view, PyBUF_SIMPLE) != 0) { + return NULL; + } + long_obj = _PyLong_FromByteArray(view.buf, view.len, little_endian, + is_signed); + PyBuffer_Release(&view); + } + else { + /* fallback: Construct a bytes then convert. */ + bytes = PyObject_Bytes(bytes_obj); + if (bytes == NULL) { + return NULL; + } + long_obj = _PyLong_FromByteArray( + (unsigned char *)PyBytes_AS_STRING(bytes), Py_SIZE(bytes), + little_endian, is_signed); + Py_DECREF(bytes); + } if (long_obj != NULL && type != &PyLong_Type) { Py_SETREF(long_obj, PyObject_CallOneArg((PyObject *)type, long_obj)); @@ -6485,6 +6511,7 @@ long_long_getter(PyObject *self, void *Py_UNUSED(ignored)) } /*[clinic input] +@permit_long_summary int.is_integer Returns True. Exists for duck type compatibility with float.is_integer. @@ -6492,7 +6519,7 @@ Returns True. Exists for duck type compatibility with float.is_integer. static PyObject * int_is_integer_impl(PyObject *self) -/*[clinic end generated code: output=90f8e794ce5430ef input=7e41c4d4416e05f2]*/ +/*[clinic end generated code: output=90f8e794ce5430ef input=aacf01a2c81c0244]*/ { Py_RETURN_TRUE; } @@ -6574,7 +6601,8 @@ If x is not a number or if base is given, then x must be a string,\n\ bytes, or bytearray instance representing an integer literal in the\n\ given base. The literal can be preceded by '+' or '-' and be surrounded\n\ by whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\n\ -Base 0 means to interpret the base from the string as an integer literal.\n\ +Base 0 means to interpret the base from the string as an integer\n\ +iteral.\n\ >>> int('0b100', base=0)\n\ 4"); @@ -6943,6 +6971,28 @@ PyLongWriter_Finish(PyLongWriter *writer) PyLongObject *obj = (PyLongObject *)writer; assert(Py_REFCNT(obj) == 1); +#ifdef Py_DEBUG + // gh-147988: Detect uninitialized digits: long_alloc() fills digits with + // 0xFF byte pattern. It's posssible because PyLong_BASE is smaller than + // the maximum value of the C digit type (uint32_t or unsigned short): + // most significan bits are unused by the API. + Py_ssize_t ndigits = _PyLong_DigitCount(obj); + if (ndigits == 0) { + // Check ob_digit[0] digit for the number zero + ndigits = 1; + } + for (Py_ssize_t i = 0; i < ndigits; i++) { + digit d = obj->long_value.ob_digit[i]; + if (d & ~(digit)PyLong_MASK) { + Py_DECREF(obj); + PyErr_Format(PyExc_SystemError, + "PyLongWriter_Finish: digit %zd is uninitialized", + i); + return NULL; + } + } +#endif + // Normalize and get singleton if possible obj = maybe_small_long(long_normalize(obj)); diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index cf673fb379edcd..d92c7daff15dbf 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -281,7 +281,7 @@ last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src) /* This is not a general function for determining format equivalence. It is used in copy_single() and copy_buffer() to weed out non-matching formats. Skipping the '@' character is specifically used in slice - assignments, where the lvalue is already known to have a single character + assignments, where the lvalue is already known to have a format. This is a performance hack that could be rewritten (if properly benchmarked). */ static inline int @@ -1197,10 +1197,11 @@ memory_exit(PyObject *self, PyObject *args) /* Casting format and shape */ /****************************************************************************/ -#define IS_BYTE_FORMAT(f) (f == 'b' || f == 'B' || f == 'c') +#define IS_BYTE_FORMAT(f) \ + (strcmp(f, "b") == 0 || strcmp(f, "B") == 0 || strcmp(f, "c") == 0) static inline Py_ssize_t -get_native_fmtchar(char *result, const char *fmt) +get_native_fmtchar(const char **result, const char *fmt) { Py_ssize_t size = -1; @@ -1218,10 +1219,21 @@ get_native_fmtchar(char *result, const char *fmt) case 'e': size = sizeof(float) / 2; break; case '?': size = sizeof(_Bool); break; case 'P': size = sizeof(void *); break; + case 'Z': { + switch (fmt[1]) { + case 'f': size = 2*sizeof(float); break; + case 'd': size = 2*sizeof(double); break; + } + if (size > 0 && fmt[2] == '\0') { + *result = fmt; + return size; + } + break; + } } if (size > 0 && fmt[1] == '\0') { - *result = fmt[0]; + *result = fmt; return size; } @@ -1237,9 +1249,19 @@ get_native_fmtstr(const char *fmt) at = 1; fmt++; } - if (fmt[0] == '\0' || fmt[1] != '\0') { + if (fmt[0] == '\0') { return NULL; } + if (fmt[0] == 'Z') { + if (fmt[1] == '\0' || fmt[2] != '\0') { + return NULL; + } + } + else { + if (fmt[1] != '\0') { + return NULL; + } + } #define RETURN(s) do { return at ? "@" s : s; } while (0) @@ -1260,6 +1282,13 @@ get_native_fmtstr(const char *fmt) case 'f': RETURN("f"); case 'd': RETURN("d"); case 'e': RETURN("e"); + case 'Z': { + switch (fmt[1]) { + case 'f': RETURN("Zf"); + case 'd': RETURN("Zd"); + } + break; + } case '?': RETURN("?"); case 'P': RETURN("P"); } @@ -1277,7 +1306,7 @@ cast_to_1D(PyMemoryViewObject *mv, PyObject *format) { Py_buffer *view = &mv->view; PyObject *asciifmt; - char srcchar, destchar; + const char *srcfmt, *destfmt; Py_ssize_t itemsize; int ret = -1; @@ -1291,16 +1320,16 @@ cast_to_1D(PyMemoryViewObject *mv, PyObject *format) if (asciifmt == NULL) return ret; - itemsize = get_native_fmtchar(&destchar, PyBytes_AS_STRING(asciifmt)); + itemsize = get_native_fmtchar(&destfmt, PyBytes_AS_STRING(asciifmt)); if (itemsize < 0) { PyErr_SetString(PyExc_ValueError, - "memoryview: destination format must be a native single " - "character format prefixed with an optional '@'"); + "memoryview: destination format must be a native " + "format prefixed with an optional '@'"); goto out; } - if ((get_native_fmtchar(&srcchar, view->format) < 0 || - !IS_BYTE_FORMAT(srcchar)) && !IS_BYTE_FORMAT(destchar)) { + if ((get_native_fmtchar(&srcfmt, view->format) < 0 || + !IS_BYTE_FORMAT(srcfmt)) && !IS_BYTE_FORMAT(destfmt)) { PyErr_SetString(PyExc_TypeError, "memoryview: cannot cast between two non-byte formats"); goto out; @@ -1600,11 +1629,7 @@ memory_getbuf(PyObject *_self, Py_buffer *view, int flags) view->obj = Py_NewRef(self); -#ifdef Py_GIL_DISABLED - _Py_atomic_add_ssize(&self->exports, 1); -#else - self->exports++; -#endif + FT_ATOMIC_ADD_SSIZE(self->exports, 1); return 0; } @@ -1613,11 +1638,7 @@ static void memory_releasebuf(PyObject *_self, Py_buffer *view) { PyMemoryViewObject *self = (PyMemoryViewObject *)_self; -#ifdef Py_GIL_DISABLED - _Py_atomic_add_ssize(&self->exports, -1); -#else - self->exports--; -#endif + FT_ATOMIC_ADD_SSIZE(self->exports, -1); return; /* PyBuffer_Release() decrements view->obj after this function returns. */ } @@ -1672,6 +1693,10 @@ fix_error_int(const char *fmt) return -1; } +// UNPACK_TO_BOOL: Return 0 if PTR represents "false", and 1 otherwise. +static const _Bool bool_false = 0; +#define UNPACK_TO_BOOL(PTR) (memcmp((PTR), &bool_false, sizeof(_Bool)) != 0) + /* Accept integer objects or objects with an __index__() method. */ static long pylong_as_ld(PyObject *item) @@ -1773,7 +1798,7 @@ pylong_as_zu(PyObject *item) dest = x; \ } while (0) -/* Unpack a single item. 'fmt' can be any native format character in struct +/* Unpack a single item. 'fmt' can be any native format in struct module syntax. This function is very sensitive to small changes. With this layout gcc automatically generates a fast jump table. */ static inline PyObject * @@ -1785,7 +1810,7 @@ unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) long long lld; long ld; Py_ssize_t zd; - double d; + double d[2]; unsigned char uc; void *p; @@ -1807,7 +1832,7 @@ unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) case 'l': UNPACK_SINGLE(ld, ptr, long); goto convert_ld; /* boolean */ - case '?': UNPACK_SINGLE(ld, ptr, _Bool); goto convert_bool; + case '?': ld = UNPACK_TO_BOOL(ptr); goto convert_bool; /* unsigned integers */ case 'H': UNPACK_SINGLE(lu, ptr, unsigned short); goto convert_lu; @@ -1823,9 +1848,27 @@ unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) case 'N': UNPACK_SINGLE(zu, ptr, size_t); goto convert_zu; /* floats */ - case 'f': UNPACK_SINGLE(d, ptr, float); goto convert_double; - case 'd': UNPACK_SINGLE(d, ptr, double); goto convert_double; - case 'e': d = PyFloat_Unpack2(ptr, endian); goto convert_double; + case 'f': UNPACK_SINGLE(d[0], ptr, float); goto convert_double; + case 'd': UNPACK_SINGLE(d[0], ptr, double); goto convert_double; + case 'e': d[0] = PyFloat_Unpack2(ptr, endian); goto convert_double; + + /* complexes */ + case 'Z': { + switch (fmt[1]) { + case 'f': + d[0] = PyFloat_Unpack4(ptr, endian); + d[1] = PyFloat_Unpack4(ptr + sizeof(float), endian); + goto convert_double_complex; + + case 'd': + d[0] = PyFloat_Unpack8(ptr, endian); + d[1] = PyFloat_Unpack8(ptr + sizeof(double), endian); + goto convert_double_complex; + + default: goto err_format; + } + break; + } /* bytes object */ case 'c': goto convert_bytes; @@ -1853,7 +1896,9 @@ unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) convert_zu: return PyLong_FromSize_t(zu); convert_double: - return PyFloat_FromDouble(d); + return PyFloat_FromDouble(d[0]); +convert_double_complex: + return PyComplex_FromDoubles(d[0], d[1]); convert_bool: return PyBool_FromLong(ld); convert_bytes: @@ -1873,7 +1918,7 @@ unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt) memcpy(ptr, (char *)&x, sizeof x); \ } while (0) -/* Pack a single item. 'fmt' can be any native format character in +/* Pack a single item. 'fmt' can be any native format in struct module syntax. */ static int pack_single(PyMemoryViewObject *self, char *ptr, PyObject *item, const char *fmt) @@ -1885,6 +1930,7 @@ pack_single(PyMemoryViewObject *self, char *ptr, PyObject *item, const char *fmt long ld; Py_ssize_t zd; double d; + Py_complex c; void *p; #if PY_LITTLE_ENDIAN @@ -1986,6 +2032,32 @@ pack_single(PyMemoryViewObject *self, char *ptr, PyObject *item, const char *fmt } break; + /* complexes */ + case 'Z': { + switch (fmt[1]) { + case 'f': case 'd': + c = PyComplex_AsCComplex(item); + if (c.real == -1.0 && PyErr_Occurred()) { + goto err_occurred; + } + CHECK_RELEASED_INT_AGAIN(self); + if (fmt[1] == 'd') { + double x[2] = {c.real, c.imag}; + + memcpy(ptr, &x, sizeof(x)); + } + else { + float x[2] = {(float)c.real, (float)c.imag}; + + memcpy(ptr, &x, sizeof(x)); + } + break; + + default: goto err_format; + } + break; + } + /* bool */ case '?': ld = PyObject_IsTrue(item); @@ -2159,6 +2231,8 @@ adjust_fmt(const Py_buffer *view) const char *fmt; fmt = (view->format[0] == '@') ? view->format+1 : view->format; + if (fmt[0] == 'Z' && fmt[1] && fmt[2] == '\0') + return fmt; if (fmt[0] && fmt[1] == '\0') return fmt; @@ -2271,20 +2345,20 @@ memoryview.tobytes Return the data in the buffer as a byte string. -Order can be {'C', 'F', 'A'}. When order is 'C' or 'F', the data of the -original array is converted to C or Fortran order. For contiguous views, -'A' returns an exact copy of the physical memory. In particular, in-memory -Fortran order is preserved. For non-contiguous views, the data is converted -to C first. order=None is the same as order='C'. +Order can be {'C', 'F', 'A'}. When order is 'C' or 'F', the data of +the original array is converted to C or Fortran order. For +contiguous views, 'A' returns an exact copy of the physical memory. +In particular, in-memory Fortran order is preserved. For +non-contiguous views, the data is converted to C first. order=None +is the same as order='C'. [clinic start generated code]*/ static PyObject * memoryview_tobytes_impl(PyMemoryViewObject *self, const char *order) -/*[clinic end generated code: output=1288b62560a32a23 input=0efa3ddaeda573a8]*/ +/*[clinic end generated code: output=1288b62560a32a23 input=119c70aa91791dc8]*/ { Py_buffer *src = VIEW_ADDR(self); char ord = 'C'; - PyObject *bytes; CHECK_RELEASED(self); @@ -2302,16 +2376,18 @@ memoryview_tobytes_impl(PyMemoryViewObject *self, const char *order) } } - bytes = PyBytes_FromStringAndSize(NULL, src->len); - if (bytes == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(src->len); + if (writer == NULL) { return NULL; + } - if (PyBuffer_ToContiguous(PyBytes_AS_STRING(bytes), src, src->len, ord) < 0) { - Py_DECREF(bytes); + if (PyBuffer_ToContiguous(PyBytesWriter_GetData(writer), + src, src->len, ord) < 0) { + PyBytesWriter_Discard(writer); return NULL; } - return bytes; + return PyBytesWriter_Finish(writer); } /*[clinic input] @@ -2319,9 +2395,9 @@ memoryview.hex sep: object = NULL An optional single character or byte to separate hex bytes. - bytes_per_sep: int = 1 - How many bytes between separators. Positive values count from the - right, negative values count from the left. + bytes_per_sep: Py_ssize_t = 1 + How many bytes between separators. Positive values count from + the right, negative values count from the left. Return the data in the buffer as a str of hexadecimal numbers. @@ -2339,32 +2415,39 @@ Return the data in the buffer as a str of hexadecimal numbers. static PyObject * memoryview_hex_impl(PyMemoryViewObject *self, PyObject *sep, - int bytes_per_sep) -/*[clinic end generated code: output=430ca760f94f3ca7 input=539f6a3a5fb56946]*/ + Py_ssize_t bytes_per_sep) +/*[clinic end generated code: output=c9bb00c7a8e86056 input=3f1c5d08906e3b70]*/ { Py_buffer *src = VIEW_ADDR(self); - PyObject *bytes; - PyObject *ret; CHECK_RELEASED(self); if (MV_C_CONTIGUOUS(self->flags)) { - return _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep); + // Prevent 'self' from being freed if computing len(sep) mutates 'self' + // in _Py_strhex_with_sep(). + // See: https://github.com/python/cpython/issues/143195. + FT_ATOMIC_ADD_SSIZE(self->exports, 1); + PyObject *ret = _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep); + FT_ATOMIC_ADD_SSIZE(self->exports, -1); + return ret; } - bytes = PyBytes_FromStringAndSize(NULL, src->len); - if (bytes == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(src->len); + if (writer == NULL) { return NULL; + } - if (PyBuffer_ToContiguous(PyBytes_AS_STRING(bytes), src, src->len, 'C') < 0) { - Py_DECREF(bytes); + if (PyBuffer_ToContiguous(PyBytesWriter_GetData(writer), + src, src->len, 'C') < 0) { + PyBytesWriter_Discard(writer); return NULL; } - ret = _Py_strhex_with_sep( - PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes), - sep, bytes_per_sep); - Py_DECREF(bytes); + PyObject *ret = _Py_strhex_with_sep( + PyBytesWriter_GetData(writer), + PyBytesWriter_GetSize(writer), + sep, bytes_per_sep); + PyBytesWriter_Discard(writer); return ret; } @@ -2426,7 +2509,7 @@ ptr_from_tuple(const Py_buffer *view, PyObject *tup) if (nindices > view->ndim) { PyErr_Format(PyExc_TypeError, - "cannot index %zd-dimension view with %zd-element tuple", + "cannot index %d-dimension view with %zd-element tuple", view->ndim, nindices); return NULL; } @@ -2968,12 +3051,12 @@ struct_unpack_cmp(const char *p, const char *q, } while (0) static inline int -unpack_cmp(const char *p, const char *q, char fmt, +unpack_cmp(const char *p, const char *q, const char *fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) { int equal; - switch (fmt) { + switch (fmt[0]) { /* signed integers and fast path for 'B' */ case 'B': return *((const unsigned char *)p) == *((const unsigned char *)q); @@ -2983,7 +3066,7 @@ unpack_cmp(const char *p, const char *q, char fmt, case 'l': CMP_SINGLE(p, q, long); return equal; /* boolean */ - case '?': CMP_SINGLE(p, q, _Bool); return equal; + case '?': return UNPACK_TO_BOOL(p) == UNPACK_TO_BOOL(q); /* unsigned integers */ case 'H': CMP_SINGLE(p, q, unsigned short); return equal; @@ -3014,6 +3097,29 @@ unpack_cmp(const char *p, const char *q, char fmt, return (u == v); } + /* complexes */ + case 'Z': { + switch (fmt[1]) { + case 'f': + { + float x[2], y[2]; + + memcpy(&x, p, sizeof(x)); + memcpy(&y, q, sizeof(y)); + return (x[0] == y[0]) && (x[1] == y[1]); + } + case 'd': + { + double x[2], y[2]; + + memcpy(&x, p, sizeof(x)); + memcpy(&y, q, sizeof(y)); + return (x[0] == y[0]) && (x[1] == y[1]); + } + } + break; + } + /* bytes object */ case 'c': return *p == *q; @@ -3038,7 +3144,7 @@ static int cmp_base(const char *p, const char *q, const Py_ssize_t *shape, const Py_ssize_t *pstrides, const Py_ssize_t *psuboffsets, const Py_ssize_t *qstrides, const Py_ssize_t *qsuboffsets, - char fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) + const char *fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) { Py_ssize_t i; int equal; @@ -3061,7 +3167,7 @@ cmp_rec(const char *p, const char *q, Py_ssize_t ndim, const Py_ssize_t *shape, const Py_ssize_t *pstrides, const Py_ssize_t *psuboffsets, const Py_ssize_t *qstrides, const Py_ssize_t *qsuboffsets, - char fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) + const char *fmt, struct unpacker *unpack_p, struct unpacker *unpack_q) { Py_ssize_t i; int equal; @@ -3100,7 +3206,7 @@ memory_richcompare(PyObject *v, PyObject *w, int op) Py_buffer *ww = NULL; struct unpacker *unpack_v = NULL; struct unpacker *unpack_w = NULL; - char vfmt, wfmt; + const char *vfmt, *wfmt; int equal = MV_COMPARE_NOT_IMPL; if (op != Py_EQ && op != Py_NE) @@ -3113,6 +3219,30 @@ memory_richcompare(PyObject *v, PyObject *w, int op) } vv = VIEW_ADDR(v); + // For formats supported by the struct module a memoryview is equal to + // itself: there is no need to compare individual values. + // This is not true for float values since they can be NaN, and NaN + // is not equal to itself. So only use this optimization on format known to + // not use floats. + if (v == w) { + const char *format = vv->format; + if (format != NULL) { + if (*format == '@') { + format++; + } + // Include only formats known by struct, exclude float formats + // "d" (double), "f" (float) and "e" (16-bit float). + // Do not optimize "P" format. + if (format[0] != 0 + && strchr("bBchHiIlLnNqQ?", format[0]) != NULL + && format[1] == 0) + { + equal = 1; + goto result; + } + } + } + if (PyMemoryView_Check(w)) { if (BASE_INACCESSIBLE(w)) { equal = (v == w); @@ -3136,15 +3266,15 @@ memory_richcompare(PyObject *v, PyObject *w, int op) /* Use fast unpacking for identical primitive C type formats. */ if (get_native_fmtchar(&vfmt, vv->format) < 0) - vfmt = '_'; + vfmt = "_"; if (get_native_fmtchar(&wfmt, ww->format) < 0) - wfmt = '_'; - if (vfmt == '_' || wfmt == '_' || vfmt != wfmt) { + wfmt = "_"; + if (strcmp(vfmt, "_") == 0 || strcmp(wfmt, "_") == 0 || strcmp(vfmt, wfmt) != 0) { /* Use struct module unpacking. NOTE: Even for equal format strings, memcmp() cannot be used for item comparison since it would give incorrect results in the case of NaNs or uninitialized padding bytes. */ - vfmt = '_'; + vfmt = "_"; unpack_v = struct_get_unpacker(vv->format, vv->itemsize); if (unpack_v == NULL) { equal = fix_struct_error_int(); @@ -3207,7 +3337,7 @@ memory_hash(PyObject *_self) Py_buffer *view = &self->view; char *mem = view->buf; Py_ssize_t ret; - char fmt; + const char *fmt; CHECK_RELEASED_INT(self); @@ -3222,9 +3352,16 @@ memory_hash(PyObject *_self) "memoryview: hashing is restricted to formats 'B', 'b' or 'c'"); return -1; } - if (view->obj != NULL && PyObject_Hash(view->obj) == -1) { - /* Keep the original error message */ - return -1; + if (view->obj != NULL) { + // Prevent 'self' from being freed when computing the item's hash. + // See https://github.com/python/cpython/issues/142664. + FT_ATOMIC_ADD_SSIZE(self->exports, 1); + Py_hash_t h = PyObject_Hash(view->obj); + FT_ATOMIC_ADD_SSIZE(self->exports, -1); + if (h == -1) { + /* Keep the original error message */ + return -1; + } } if (!MV_C_CONTIGUOUS(self->flags)) { @@ -3442,7 +3579,8 @@ static PyMethodDef memory_methods[] = { MEMORYVIEW_INDEX_METHODDEF {"__enter__", memory_enter, METH_NOARGS, NULL}, {"__exit__", memory_exit, METH_VARARGS, memory_exit_doc}, - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("memoryviews are generic over the type of their underlying data")}, {NULL, NULL} }; diff --git a/Objects/mimalloc/heap.c b/Objects/mimalloc/heap.c index d92dc768e5ec28..5fbfb82baa0204 100644 --- a/Objects/mimalloc/heap.c +++ b/Objects/mimalloc/heap.c @@ -100,7 +100,10 @@ static bool mi_heap_page_collect(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t // note: this will free retired pages as well. bool freed = _PyMem_mi_page_maybe_free(page, pq, collect >= MI_FORCE); if (!freed && collect == MI_ABANDON) { - _mi_page_abandon(page, pq); + // _PyMem_mi_page_maybe_free may have moved the page to a different + // page queue, so we need to re-fetch the correct queue. + uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : _mi_bin(page->xblock_size)); + _mi_page_abandon(page, &heap->pages[bin]); } } else if (collect == MI_ABANDON) { diff --git a/Objects/mimalloc/init.c b/Objects/mimalloc/init.c index 81b241063ff40f..7711c827a58b1c 100644 --- a/Objects/mimalloc/init.c +++ b/Objects/mimalloc/init.c @@ -183,9 +183,9 @@ mi_heap_t* _mi_heap_main_get(void) { // note: in x64 in release build `sizeof(mi_thread_data_t)` is under 4KiB (= OS page size). typedef struct mi_thread_data_s { - mi_heap_t heap; // must come first due to cast in `_mi_heap_done` + mi_heap_t heap; // must come first due to cast in `_mi_heap_done` mi_tld_t tld; - mi_memid_t memid; + mi_memid_t memid; // must come last due to zero'ing } mi_thread_data_t; @@ -231,7 +231,7 @@ static mi_thread_data_t* mi_thread_data_zalloc(void) { } if (td != NULL && !is_zero) { - _mi_memzero_aligned(td, sizeof(*td)); + _mi_memzero_aligned(td, offsetof(mi_thread_data_t,memid)); } return td; } diff --git a/Objects/mimalloc/page.c b/Objects/mimalloc/page.c index ff7444cce10923..ded59f8eb1ccaa 100644 --- a/Objects/mimalloc/page.c +++ b/Objects/mimalloc/page.c @@ -213,6 +213,13 @@ static void _mi_page_thread_free_collect(mi_page_t* page) // update counts now page->used -= count; + + if (page->used == 0) { + // The page may have had a QSBR goal set from a previous point when it + // was all-free. That goal is no longer valid because the page was + // allocated from and then freed again by other threads. + _PyMem_mi_page_clear_qsbr(page); + } } void _mi_page_free_collect(mi_page_t* page, bool force) { @@ -225,9 +232,6 @@ void _mi_page_free_collect(mi_page_t* page, bool force) { // and the local free list if (page->local_free != NULL) { - // any previous QSBR goals are no longer valid because we reused the page - _PyMem_mi_page_clear_qsbr(page); - if mi_likely(page->free == NULL) { // usual case page->free = page->local_free; diff --git a/Objects/mimalloc/prim/unix/prim.c b/Objects/mimalloc/prim/unix/prim.c index 1598ebabf0a4da..6f78eb72789e8e 100644 --- a/Objects/mimalloc/prim/unix/prim.c +++ b/Objects/mimalloc/prim/unix/prim.c @@ -751,7 +751,7 @@ bool _mi_prim_random_buf(void* buf, size_t buf_len) { #elif defined(__ANDROID__) || defined(__DragonFly__) || \ defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ - defined(__sun) + defined(__sun) || defined(__CYGWIN__) #include <stdlib.h> bool _mi_prim_random_buf(void* buf, size_t buf_len) { diff --git a/Objects/mimalloc/segment.c b/Objects/mimalloc/segment.c index 9b092b9b734d4c..9dad69c995e7a0 100644 --- a/Objects/mimalloc/segment.c +++ b/Objects/mimalloc/segment.c @@ -1286,6 +1286,7 @@ static bool mi_segment_check_free(mi_segment_t* segment, size_t slices_needed, s _mi_stat_decrease(&tld->stats->pages_abandoned, 1); #ifdef Py_GIL_DISABLED page->qsbr_goal = 0; + mi_assert_internal(page->qsbr_node.next == NULL); #endif segment->abandoned--; slice = mi_segment_page_clear(page, tld); // re-assign slice due to coalesce! @@ -1361,6 +1362,7 @@ static mi_segment_t* mi_segment_reclaim(mi_segment_t* segment, mi_heap_t* heap, // if everything free by now, free the page #ifdef Py_GIL_DISABLED page->qsbr_goal = 0; + mi_assert_internal(page->qsbr_node.next == NULL); #endif slice = mi_segment_page_clear(page, tld); // set slice again due to coalesceing } diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 862395e7881870..f447403ef31b43 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -2,22 +2,24 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_ceval.h" // _PyEval_EnableGILTransient() #include "pycore_dict.h" // _PyDict_EnablePerThreadRefcounting() #include "pycore_fileutils.h" // _Py_wgetcwd #include "pycore_import.h" // _PyImport_GetNextModuleIndex() #include "pycore_interp.h" // PyInterpreterState.importlib +#include "pycore_lazyimportobject.h" // _PyLazyImportObject_Check() #include "pycore_long.h" // _PyLong_GetOne() #include "pycore_modsupport.h" // _PyModule_CreateInitialized() -#include "pycore_moduleobject.h" // _PyModule_GetDef() +#include "pycore_moduleobject.h" // _PyModule_GetDefOrNull() #include "pycore_object.h" // _PyType_AllocNoTrack #include "pycore_pyerrors.h" // _PyErr_FormatFromCause() #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_slots.h" // _PySlotIterator_Init #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() #include "osdefs.h" // MAXPATHLEN - #define _PyModule_CAST(op) \ (assert(PyModule_Check(op)), _Py_CAST(PyModuleObject*, (op))) @@ -27,6 +29,27 @@ static PyMemberDef module_members[] = { {0} }; +static void +assert_def_missing_or_redundant(PyModuleObject *m) +{ + /* We copy all relevant info into the module object. + * Modules created using a def keep a reference to that (statically + * allocated) def; the info there should match what we have in the module. + */ +#ifndef NDEBUG + if (m->md_token_is_def) { + PyModuleDef *def = (PyModuleDef *)m->md_token; + assert(def); +#define DO_ASSERT(F) assert (def->m_ ## F == m->md_state_ ## F); + DO_ASSERT(size); + DO_ASSERT(traverse); + DO_ASSERT(clear); + DO_ASSERT(free); +#undef DO_ASSERT + } +#endif // NDEBUG +} + PyTypeObject PyModuleDef_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) @@ -44,14 +67,73 @@ _PyModule_IsExtension(PyObject *obj) } PyModuleObject *module = (PyModuleObject*)obj; - PyModuleDef *def = module->md_def; - return (def != NULL && def->m_methods != NULL); + if (module->md_exec) { + return 1; + } + if (module->md_token_is_def) { + PyModuleDef *def = (PyModuleDef *)module->md_token; + return (module->md_token_is_def && def->m_methods != NULL); + } + return 0; } PyObject* PyModuleDef_Init(PyModuleDef* def) { +#ifdef Py_GIL_DISABLED + // Check that this def does not come from a non-free-threading ABI. + // + // This is meant as a "sanity check"; users should never rely on it. + // In particular, if we run out of ob_flags bits, or otherwise need to + // change some of the internals, this check can go away. Still, it + // would be nice to keep it for the free-threading transition. + // + // A PyModuleDef must be initialized with PyModuleDef_HEAD_INIT, + // which (via PyObject_HEAD_INIT) sets _Py_STATICALLY_ALLOCATED_FLAG + // and not _Py_LEGACY_ABI_CHECK_FLAG. For PyModuleDef, these flags never + // change. + // This means that the lower nibble of a valid PyModuleDef's ob_flags is + // always `_10_` (in binary; `_` is don't care). + // + // So, a check for these bits won't reject valid PyModuleDef. + // Rejecting incompatible extensions is slightly less important; here's + // how that works: + // + // In the pre-free-threading stable ABI, PyModuleDef_HEAD_INIT is big + // enough to overlap with free-threading ABI's ob_flags, is all zeros + // except for the refcount field. + // The refcount field can be: + // - 1 (3.11 and below) + // - UINT_MAX >> 2 (32-bit 3.12 & 3.13) + // - UINT_MAX (64-bit 3.12 & 3.13) + // - 7L << 28 (3.14) + // + // This means that the lower nibble of *any byte* in PyModuleDef_HEAD_INIT + // is not `_10_` -- it can be: + // - 0b0000 + // - 0b0001 + // - 0b0011 (from UINT_MAX >> 2) + // - 0b0111 (from 7L << 28) + // - 0b1111 (e.g. from UINT_MAX) + // (The values may change at runtime as the PyModuleDef is used, but + // PyModuleDef_Init is required before using the def as a Python object, + // so we check at least once with the initial values. + uint16_t flags = ((PyObject*)def)->ob_flags; + uint16_t bits = _Py_STATICALLY_ALLOCATED_FLAG | _Py_LEGACY_ABI_CHECK_FLAG; + if ((flags & bits) != _Py_STATICALLY_ALLOCATED_FLAG) { + const char *message = "invalid PyModuleDef, extension possibly " + "compiled for non-free-threaded Python"; + // Write the error as unraisable: if the extension tries calling + // any API, it's likely to segfault and lose the exception. + PyErr_SetString(PyExc_SystemError, message); + PyErr_WriteUnraisable(NULL); + // But also raise the exception normally -- this is technically + // a recoverable state. + PyErr_SetString(PyExc_SystemError, message); + return NULL; + } +#endif assert(PyModuleDef_Type.tp_flags & Py_TPFLAGS_READY); if (def->m_base.m_index == 0) { Py_SET_REFCNT(def, 1); @@ -93,10 +175,19 @@ new_module_notrack(PyTypeObject *mt) m = (PyModuleObject *)_PyType_AllocNoTrack(mt, 0); if (m == NULL) return NULL; - m->md_def = NULL; m->md_state = NULL; m->md_weaklist = NULL; m->md_name = NULL; + m->md_token_is_def = false; +#ifdef Py_GIL_DISABLED + m->md_requires_gil = true; +#endif + m->md_state_size = 0; + m->md_state_traverse = NULL; + m->md_state_clear = NULL; + m->md_state_free = NULL; + m->md_exec = NULL; + m->md_token = NULL; m->md_dict = PyDict_New(); if (m->md_dict == NULL) { Py_DECREF(m); @@ -105,11 +196,40 @@ new_module_notrack(PyTypeObject *mt) return m; } +/* Module dict watcher callback. + * When a module dictionary is modified, we need to clear the keys version + * to invalidate any cached lookups that depend on the dictionary structure. + */ +static int +module_dict_watcher(PyDict_WatchEvent event, PyObject *dict, + PyObject *key, PyObject *new_value) +{ + assert(PyDict_Check(dict)); + // Only if a new lazy object shows up do we need to clear the dictionary. If + // this is adding a new key then the version will be reset anyway. + if (event == PyDict_EVENT_MODIFIED && + new_value != NULL && + PyLazyImport_CheckExact(new_value)) { + _PyDict_ClearKeysVersionLockHeld(dict); + } + return 0; +} + +int +_PyModule_InitModuleDictWatcher(PyInterpreterState *interp) +{ + // This is a reserved watcher for CPython so there's no need to check for non-NULL. + assert(interp->dict_state.watchers[MODULE_WATCHER_ID] == NULL); + interp->dict_state.watchers[MODULE_WATCHER_ID] = &module_dict_watcher; + return 0; +} + static void track_module(PyModuleObject *m) { _PyDict_EnablePerThreadRefcounting(m->md_dict); _PyObject_SetDeferredRefcount((PyObject *)m); + PyDict_Watch(MODULE_WATCHER_ID, m->md_dict); PyObject_GC_Track(m); } @@ -211,6 +331,17 @@ PyModule_Create2(PyModuleDef* module, int module_api_version) return _PyModule_CreateInitialized(module, module_api_version); } +static void +module_copy_members_from_deflike( + PyModuleObject *md, + PyModuleDef *def_like /* not necessarily a valid Python object */) +{ + md->md_state_size = def_like->m_size; + md->md_state_traverse = def_like->m_traverse; + md->md_state_clear = def_like->m_clear; + md->md_state_free = def_like->m_free; +} + PyObject * _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version) { @@ -257,31 +388,35 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version) return NULL; } } - m->md_def = module; + m->md_token = module; + m->md_token_is_def = true; + module_copy_members_from_deflike(m, module); #ifdef Py_GIL_DISABLED - m->md_gil = Py_MOD_GIL_USED; + m->md_requires_gil = true; #endif return (PyObject*)m; } -PyObject * -PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_version) +typedef PyObject *(*createfunc_t)(PyObject *, PyModuleDef*); + +static PyObject * +module_from_slots_and_spec( + const PySlot *slots, + PyObject *spec, + int module_api_version, + PyModuleDef* original_def /* NULL if not defined by a def */) { - PyModuleDef_Slot* cur_slot; - PyObject *(*create)(PyObject *, PyModuleDef*) = NULL; + createfunc_t create = NULL; PyObject *nameobj; PyObject *m = NULL; - int has_multiple_interpreters_slot = 0; - void *multiple_interpreters = (void *)0; - int has_gil_slot = 0; - void *gil_slot = Py_MOD_GIL_USED; - int has_execution_slots = 0; + uint64_t multiple_interpreters = (uint64_t)Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED; + bool requires_gil = true; const char *name; int ret; + void *token = NULL; + _Py_modexecfunc m_exec = NULL; PyInterpreterState *interp = _PyInterpreterState_GET(); - PyModuleDef_Init(def); - nameobj = PyObject_GetAttrString(spec, "name"); if (nameobj == NULL) { return NULL; @@ -295,74 +430,140 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio goto error; } - if (def->m_size < 0) { - PyErr_Format( - PyExc_SystemError, - "module %s: m_size may not be negative for multi-phase initialization", - name); - goto error; + _PySlotIterator it; + + PyModuleDef _dummy_def = {0}; + PyModuleDef *def_like; + if (slots) { + assert(!original_def); + def_like = &_dummy_def; + _PySlotIterator_Init(&it, slots, _PySlot_KIND_MOD); } + else { + assert(original_def); + def_like = original_def; + _PySlotIterator_InitLegacy(&it, def_like->m_slots, _PySlot_KIND_MOD); + } + it.name = name; - for (cur_slot = def->m_slots; cur_slot && cur_slot->slot; cur_slot++) { - switch (cur_slot->slot) { + while (_PySlotIterator_Next(&it)) { + switch (it.current.sl_id) { + case Py_slot_invalid: + goto error; case Py_mod_create: - if (create) { - PyErr_Format( - PyExc_SystemError, - "module %s has multiple create slots", - name); - goto error; - } - create = cur_slot->value; + create = (createfunc_t)it.current.sl_func; break; case Py_mod_exec: - has_execution_slots = 1; + if (!original_def) { + if (m_exec) { + PyErr_Format( + PyExc_SystemError, + "module %s has multiple Py_mod_exec slots", + name); + goto error; + } + m_exec = (_Py_modexecfunc)it.current.sl_func; + } break; case Py_mod_multiple_interpreters: - if (has_multiple_interpreters_slot) { - PyErr_Format( - PyExc_SystemError, - "module %s has more than one 'multiple interpreters' slots", - name); + multiple_interpreters = it.current.sl_uint64; + break; + case Py_mod_gil: + { + uint64_t val = it.current.sl_uint64; + requires_gil = (val != (uint64_t)Py_MOD_GIL_NOT_USED); + } + break; + case Py_mod_abi: + if (PyABIInfo_Check(it.current.sl_ptr, name) < 0) { goto error; } - multiple_interpreters = cur_slot->value; - has_multiple_interpreters_slot = 1; break; - case Py_mod_gil: - if (has_gil_slot) { + case Py_mod_token: + if (original_def && original_def != it.current.sl_ptr) { PyErr_Format( - PyExc_SystemError, - "module %s has more than one 'gil' slot", - name); + PyExc_SystemError, + "module %s: arbitrary Py_mod_token not " + "allowed with PyModuleDef", + name); goto error; } - gil_slot = cur_slot->value; - has_gil_slot = 1; + token = it.current.sl_ptr; break; - default: - assert(cur_slot->slot < 0 || cur_slot->slot > _Py_mod_LAST_SLOT); - PyErr_Format( - PyExc_SystemError, - "module %s uses unknown slot ID %i", - name, cur_slot->slot); - goto error; + // Common case: Copy a PEP 793 slot to def_like +#define DEF_SLOT_CASE(SLOT, TYPE, SL_MEMBER, MEMBER) \ + case SLOT: \ + do { \ + if (original_def) { \ + TYPE orig_value = (TYPE)original_def->MEMBER; \ + TYPE new_value = (TYPE)it.current.SL_MEMBER; \ + if (orig_value != new_value) { \ + PyErr_Format( \ + PyExc_SystemError, \ + "module %s: %s conflicts with " \ + "PyModuleDef." #MEMBER, \ + name, _PySlot_GetName(it.current.sl_id)); \ + goto error; \ + } \ + } \ + (def_like->MEMBER) = (TYPE)it.current.SL_MEMBER; \ + } while (0); \ + break; \ + ///////////////////////////////////////////////////////////////// + DEF_SLOT_CASE(Py_mod_name, char*, sl_ptr, m_name) + DEF_SLOT_CASE(Py_mod_doc, char*, sl_ptr, m_doc) + DEF_SLOT_CASE(Py_mod_state_size, Py_ssize_t, sl_size, m_size) + DEF_SLOT_CASE(Py_mod_methods, PyMethodDef*, sl_ptr, m_methods) + DEF_SLOT_CASE(Py_mod_state_traverse, + traverseproc, sl_func, m_traverse) + DEF_SLOT_CASE(Py_mod_state_clear, inquiry, sl_func, m_clear) + DEF_SLOT_CASE(Py_mod_state_free, freefunc, sl_func, m_free) +#undef DEF_SLOT_CASE + } + } + if (!original_def && !_PySlotIterator_SawSlot(&it, Py_mod_abi)) { + PyErr_Format( + PyExc_SystemError, + "module %s does not define Py_mod_abi," + " which is mandatory for modules defined from slots only.", + name); + goto error; + } + +#ifdef Py_GIL_DISABLED + // For modules created directly from slots (not from a def), we enable + // the GIL here (pairing `_PyEval_EnableGILTransient` with + // an immediate `_PyImport_EnableGILAndWarn`). + // For modules created from a def, the caller is responsible for this. + if (!original_def && requires_gil) { + PyThreadState *tstate = _PyThreadState_GET(); + if (_PyEval_EnableGILTransient(tstate) < 0) { + goto error; + } + if (_PyImport_EnableGILAndWarn(tstate, nameobj) < 0) { + goto error; } } +#endif + + if (def_like->m_size < 0) { + PyErr_Format( + PyExc_SystemError, + "module %s: m_size may not be negative for multi-phase initialization", + name); + goto error; + } /* By default, multi-phase init modules are expected to work under multiple interpreters. */ - if (!has_multiple_interpreters_slot) { - multiple_interpreters = Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED; - } - if (multiple_interpreters == Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) { + if (multiple_interpreters == (int64_t)(intptr_t)Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) { if (!_Py_IsMainInterpreter(interp) && _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0) { goto error; } } - else if (multiple_interpreters != Py_MOD_PER_INTERPRETER_GIL_SUPPORTED + else if (multiple_interpreters != (int64_t)(intptr_t)Py_MOD_PER_INTERPRETER_GIL_SUPPORTED && interp->ceval.own_gil && !_Py_IsMainInterpreter(interp) && _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0) @@ -371,7 +572,7 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio } if (create) { - m = create(spec, def); + m = create(spec, original_def); if (m == NULL) { if (!PyErr_Occurred()) { PyErr_Format( @@ -397,22 +598,34 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio } if (PyModule_Check(m)) { - ((PyModuleObject*)m)->md_state = NULL; - ((PyModuleObject*)m)->md_def = def; + PyModuleObject *mod = (PyModuleObject*)m; + mod->md_state = NULL; + module_copy_members_from_deflike(mod, def_like); + if (original_def) { + assert (!token || token == original_def); + mod->md_token = original_def; + mod->md_token_is_def = 1; + } + else { + mod->md_token = token; + } #ifdef Py_GIL_DISABLED - ((PyModuleObject*)m)->md_gil = gil_slot; + mod->md_requires_gil = requires_gil; #else - (void)gil_slot; + (void)requires_gil; #endif + mod->md_exec = m_exec; } else { - if (def->m_size > 0 || def->m_traverse || def->m_clear || def->m_free) { + if (def_like->m_size > 0 || def_like->m_traverse || def_like->m_clear + || def_like->m_free) + { PyErr_Format( PyExc_SystemError, "module %s is not a module object, but requests module state", name); goto error; } - if (has_execution_slots) { + if (_PySlotIterator_SawSlot(&it, Py_mod_exec)) { PyErr_Format( PyExc_SystemError, "module %s specifies execution slots, but did not create " @@ -420,17 +633,25 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio name); goto error; } + if (token) { + PyErr_Format( + PyExc_SystemError, + "module %s specifies a token, but did not create " + "a ModuleType instance", + name); + goto error; + } } - if (def->m_methods != NULL) { - ret = _add_methods_to_object(m, nameobj, def->m_methods); + if (def_like->m_methods != NULL) { + ret = _add_methods_to_object(m, nameobj, def_like->m_methods); if (ret != 0) { goto error; } } - if (def->m_doc != NULL) { - ret = PyModule_SetDocString(m, def->m_doc); + if (def_like->m_doc != NULL) { + ret = PyModule_SetDocString(m, def_like->m_doc); if (ret != 0) { goto error; } @@ -445,83 +666,133 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio return NULL; } +PyObject * +PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_version) +{ + PyModuleDef_Init(def); + return module_from_slots_and_spec(NULL, spec, module_api_version, def); +} + +PyObject * +PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec) +{ + if (!slots) { + PyErr_SetString( + PyExc_SystemError, + "PyModule_FromSlotsAndSpec called with NULL slots"); + return NULL; + } + + return module_from_slots_and_spec(slots, spec, PYTHON_API_VERSION, + NULL); +} + #ifdef Py_GIL_DISABLED int PyUnstable_Module_SetGIL(PyObject *module, void *gil) { + bool requires_gil = (gil != Py_MOD_GIL_NOT_USED); if (!PyModule_Check(module)) { PyErr_BadInternalCall(); return -1; } - ((PyModuleObject *)module)->md_gil = gil; + ((PyModuleObject *)module)->md_requires_gil = requires_gil; return 0; } #endif -int -PyModule_ExecDef(PyObject *module, PyModuleDef *def) +static int +run_exec_func(PyObject *module, int (*exec)(PyObject *)) { - PyModuleDef_Slot *cur_slot; - const char *name; - int ret; + int ret = exec(module); + if (ret != 0) { + if (!PyErr_Occurred()) { + PyErr_Format( + PyExc_SystemError, + "execution of %R failed without setting an exception", + module); + } + return -1; + } + if (PyErr_Occurred()) { + _PyErr_FormatFromCause( + PyExc_SystemError, + "execution of module %R raised unreported exception", + module); + return -1; + } + return 0; +} - name = PyModule_GetName(module); - if (name == NULL) { +static int +alloc_state(PyObject *module) +{ + if (!PyModule_Check(module)) { + PyErr_Format(PyExc_TypeError, "expected module, got %T", module); return -1; } + PyModuleObject *md = (PyModuleObject*)module; - if (def->m_size >= 0) { - PyModuleObject *md = (PyModuleObject*)module; + if (md->md_state_size >= 0) { if (md->md_state == NULL) { /* Always set a state pointer; this serves as a marker to skip * multiple initialization (importlib.reload() is no-op) */ - md->md_state = PyMem_Malloc(def->m_size); + md->md_state = PyMem_Malloc(md->md_state_size); if (!md->md_state) { PyErr_NoMemory(); return -1; } - memset(md->md_state, 0, def->m_size); + memset(md->md_state, 0, md->md_state_size); } } + return 0; +} + +int +PyModule_Exec(PyObject *module) +{ + if (alloc_state(module) < 0) { + return -1; + } + PyModuleObject *md = (PyModuleObject*)module; + if (md->md_exec) { + assert(!md->md_token_is_def); + return run_exec_func(module, md->md_exec); + } + + PyModuleDef *def = _PyModule_GetDefOrNull(module); + if (def) { + return PyModule_ExecDef(module, def); + } + return 0; +} + +int +PyModule_ExecDef(PyObject *module, PyModuleDef *def) +{ + if (alloc_state(module) < 0) { + return -1; + } + + assert(PyModule_Check(module)); if (def->m_slots == NULL) { return 0; } - for (cur_slot = def->m_slots; cur_slot && cur_slot->slot; cur_slot++) { - switch (cur_slot->slot) { - case Py_mod_create: - /* handled in PyModule_FromDefAndSpec2 */ - break; + _PySlotIterator it; + _PySlotIterator_InitLegacy(&it, def->m_slots, _PySlot_KIND_MOD); + while (_PySlotIterator_Next(&it)) { + _Py_modexecfunc func; + switch (it.current.sl_id) { + case Py_slot_invalid: + return -1; case Py_mod_exec: - ret = ((int (*)(PyObject *))cur_slot->value)(module); - if (ret != 0) { - if (!PyErr_Occurred()) { - PyErr_Format( - PyExc_SystemError, - "execution of module %s failed without setting an exception", - name); - } + func = (_Py_modexecfunc)it.current.sl_func; + if (run_exec_func(module, func) < 0) { return -1; } - if (PyErr_Occurred()) { - _PyErr_FormatFromCause( - PyExc_SystemError, - "execution of module %s raised unreported exception", - name); - return -1; - } - break; - case Py_mod_multiple_interpreters: - case Py_mod_gil: - /* handled in PyModule_FromDefAndSpec2 */ break; - default: - PyErr_Format( - PyExc_SystemError, - "module %s initialized with unknown slot %i", - name, cur_slot->slot); - return -1; } } return 0; @@ -565,6 +836,42 @@ PyModule_GetDict(PyObject *m) return _PyModule_GetDict(m); // borrowed reference } +int +PyModule_GetStateSize(PyObject *m, Py_ssize_t *size_p) +{ + *size_p = -1; + if (!PyModule_Check(m)) { + PyErr_Format(PyExc_TypeError, "expected module, got %T", m); + return -1; + } + PyModuleObject *mod = (PyModuleObject *)m; + *size_p = mod->md_state_size; + return 0; +} + +int +PyModule_GetToken_DuringGC(PyObject *m, void **token_p) +{ + *token_p = NULL; + if (!PyModule_Check(m)) { + return -1; + } + *token_p = _PyModule_GetToken(m); + return 0; +} + +int +PyModule_GetToken(PyObject *m, void **token_p) +{ + *token_p = NULL; + if (!PyModule_Check(m)) { + PyErr_Format(PyExc_TypeError, "expected module, got %T", m); + return -1; + } + *token_p = _PyModule_GetToken(m); + return 0; +} + PyObject* PyModule_GetNameObject(PyObject *mod) { @@ -705,7 +1012,16 @@ PyModule_GetDef(PyObject* m) PyErr_BadArgument(); return NULL; } - return _PyModule_GetDef(m); + return _PyModule_GetDefOrNull(m); +} + +void* +PyModule_GetState_DuringGC(PyObject* m) +{ + if (!PyModule_Check(m)) { + return NULL; + } + return _PyModule_GetState(m); } void* @@ -829,17 +1145,18 @@ module_dealloc(PyObject *self) } FT_CLEAR_WEAKREFS(self, m->md_weaklist); + assert_def_missing_or_redundant(m); /* bpo-39824: Don't call m_free() if m_size > 0 and md_state=NULL */ - if (m->md_def && m->md_def->m_free - && (m->md_def->m_size <= 0 || m->md_state != NULL)) + if (m->md_state_free && (m->md_state_size <= 0 || m->md_state != NULL)) { - m->md_def->m_free(m); + m->md_state_free(m); } Py_XDECREF(m->md_dict); Py_XDECREF(m->md_name); - if (m->md_state != NULL) + if (m->md_state != NULL) { PyMem_Free(m->md_state); + } Py_TYPE(m)->tp_free((PyObject *)m); } @@ -982,6 +1299,33 @@ _PyModule_IsPossiblyShadowing(PyObject *origin) return result; } +// Check if `name` is a lazily pending submodule of module `m`. +// Returns a new reference on success, or NULL with no error set. +static PyObject * +try_load_lazy_submodule(PyModuleObject *m, PyObject *name) +{ + PyObject *mod_name; + int rc = PyDict_GetItemRef(m->md_dict, &_Py_ID(__name__), &mod_name); + if (rc <= 0) { + return NULL; + } + if (!PyUnicode_Check(mod_name)) { + Py_DECREF(mod_name); + return NULL; + } + PyObject *result = _PyImport_TryLoadLazySubmodule(mod_name, name); + Py_DECREF(mod_name); + if (result == NULL) { + PyErr_Clear(); + return NULL; + } + if (PyDict_SetItem(m->md_dict, name, result) < 0) { + Py_DECREF(result); + return NULL; + } + return result; +} + PyObject* _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) { @@ -989,6 +1333,47 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) PyObject *attr, *mod_name, *getattr; attr = _PyObject_GenericGetAttrWithDict((PyObject *)m, name, NULL, suppress); if (attr) { + if (PyLazyImport_CheckExact(attr)) { + // gh-144957: Module __getattr__ should get a chance to provide + // the attribute before resolving a lazy import placeholder. + if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__getattr__), &getattr) < 0) { + Py_DECREF(attr); + return NULL; + } + if (getattr) { + PyObject *result = PyObject_CallOneArg(getattr, name); + Py_DECREF(getattr); + if (result != NULL) { + Py_DECREF(attr); + return result; + } + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { + Py_DECREF(attr); + return NULL; + } + PyErr_Clear(); + } + PyObject *new_value = _PyImport_LoadLazyImportTstate( + PyThreadState_GET(), attr); + if (new_value == NULL) { + if (suppress && + PyErr_ExceptionMatches(PyExc_ImportCycleError)) { + // ImportCycleError is raised when a lazy object tries + // to import itself. In this case, the error should not + // propagate to the caller and instead treated as if the + // attribute doesn't exist. + PyErr_Clear(); + } + Py_DECREF(attr); + return NULL; + } + + if (PyDict_SetItem(m->md_dict, name, new_value) < 0) { + Py_CLEAR(new_value); + } + Py_DECREF(attr); + return new_value; + } return attr; } if (suppress == 1) { @@ -1005,6 +1390,13 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) PyErr_Clear(); } assert(m->md_dict != NULL); + attr = try_load_lazy_submodule(m, name); + if (attr != NULL) { + return attr; + } + if (PyErr_Occurred()) { + return NULL; + } if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__getattr__), &getattr) < 0) { return NULL; } @@ -1147,11 +1539,11 @@ module_traverse(PyObject *self, visitproc visit, void *arg) { PyModuleObject *m = _PyModule_CAST(self); + assert_def_missing_or_redundant(m); /* bpo-39824: Don't call m_traverse() if m_size > 0 and md_state=NULL */ - if (m->md_def && m->md_def->m_traverse - && (m->md_def->m_size <= 0 || m->md_state != NULL)) + if (m->md_state_traverse && (m->md_state_size <= 0 || m->md_state != NULL)) { - int res = m->md_def->m_traverse((PyObject*)m, visit, arg); + int res = m->md_state_traverse((PyObject*)m, visit, arg); if (res) return res; } @@ -1165,18 +1557,19 @@ module_clear(PyObject *self) { PyModuleObject *m = _PyModule_CAST(self); + assert_def_missing_or_redundant(m); /* bpo-39824: Don't call m_clear() if m_size > 0 and md_state=NULL */ - if (m->md_def && m->md_def->m_clear - && (m->md_def->m_size <= 0 || m->md_state != NULL)) + if (m->md_state_clear && (m->md_state_size <= 0 || m->md_state != NULL)) { - int res = m->md_def->m_clear((PyObject*)m); + int res = m->md_state_clear((PyObject*)m); if (PyErr_Occurred()) { PyErr_FormatUnraisable("Exception ignored in m_clear of module%s%V", m->md_name ? " " : "", m->md_name, ""); } - if (res) + if (res) { return res; + } } Py_CLEAR(m->md_dict); return 0; @@ -1186,7 +1579,12 @@ static PyObject * module_dir(PyObject *self, PyObject *args) { PyObject *result = NULL; - PyObject *dict = PyObject_GetAttr(self, &_Py_ID(__dict__)); + PyObject *dict; + if (PyModule_CheckExact(self)) { + dict = Py_NewRef(((PyModuleObject *)self)->md_dict); + } else { + dict = PyObject_GetAttr(self, &_Py_ID(__dict__)); + } if (dict != NULL) { if (PyDict_Check(dict)) { @@ -1214,7 +1612,7 @@ static PyMethodDef module_methods[] = { }; static PyObject * -module_get_dict(PyModuleObject *m) +module_load_dict(PyModuleObject *m) { PyObject *dict = PyObject_GetAttr((PyObject *)m, &_Py_ID(__dict__)); if (dict == NULL) { @@ -1233,7 +1631,7 @@ module_get_annotate(PyObject *self, void *Py_UNUSED(ignored)) { PyModuleObject *m = _PyModule_CAST(self); - PyObject *dict = module_get_dict(m); + PyObject *dict = module_load_dict(m); if (dict == NULL) { return NULL; } @@ -1258,7 +1656,7 @@ module_set_annotate(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) return -1; } - PyObject *dict = module_get_dict(m); + PyObject *dict = module_load_dict(m); if (dict == NULL) { return -1; } @@ -1288,7 +1686,7 @@ module_get_annotations(PyObject *self, void *Py_UNUSED(ignored)) { PyModuleObject *m = _PyModule_CAST(self); - PyObject *dict = module_get_dict(m); + PyObject *dict = module_load_dict(m); if (dict == NULL) { return NULL; } @@ -1329,8 +1727,9 @@ module_get_annotations(PyObject *self, void *Py_UNUSED(ignored)) return NULL; } if (!PyDict_Check(annotations)) { - PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'", - Py_TYPE(annotations)->tp_name); + PyErr_Format(PyExc_TypeError, + "__annotate__() must return a dict, not %T", + annotations); Py_DECREF(annotate); Py_DECREF(annotations); Py_DECREF(dict); @@ -1359,7 +1758,7 @@ module_set_annotations(PyObject *self, PyObject *value, void *Py_UNUSED(ignored) { PyModuleObject *m = _PyModule_CAST(self); - PyObject *dict = module_get_dict(m); + PyObject *dict = module_load_dict(m); if (dict == NULL) { return -1; } @@ -1388,7 +1787,6 @@ module_set_annotations(PyObject *self, PyObject *value, void *Py_UNUSED(ignored) return ret; } - static PyGetSetDef module_getsets[] = { {"__annotations__", module_get_annotations, module_set_annotations}, {"__annotate__", module_get_annotate, module_set_annotate}, diff --git a/Objects/namespaceobject.c b/Objects/namespaceobject.c index 201cb8a7df8da1..3803c41027dd85 100644 --- a/Objects/namespaceobject.c +++ b/Objects/namespaceobject.c @@ -13,6 +13,7 @@ typedef struct { } _PyNamespaceObject; #define _PyNamespace_CAST(op) _Py_CAST(_PyNamespaceObject*, (op)) +#define _PyNamespace_Check(op) PyObject_TypeCheck((op), &_PyNamespace_Type) static PyMemberDef namespace_members[] = { @@ -234,6 +235,14 @@ namespace_replace(PyObject *self, PyObject *args, PyObject *kwargs) if (!result) { return NULL; } + if (!_PyNamespace_Check(result)) { + PyErr_Format(PyExc_TypeError, + "expect %N type, but %T() returned '%T' object", + &_PyNamespace_Type, self, result); + Py_DECREF(result); + return NULL; + } + if (PyDict_Update(((_PyNamespaceObject*)result)->ns_dict, ((_PyNamespaceObject*)self)->ns_dict) < 0) { diff --git a/Objects/object.c b/Objects/object.c index 3ed7d55593dffa..bd23c2e2388194 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -10,6 +10,7 @@ #include "pycore_descrobject.h" // _PyMethodWrapper_Type #include "pycore_dict.h" // _PyObject_MaterializeManagedDict() #include "pycore_floatobject.h" // _PyFloat_DebugMallocStats() +#include "pycore_function.h" // _PyClassMethod_GetFunc() #include "pycore_freelist.h" // _PyObject_ClearFreeLists() #include "pycore_genobject.h" // _PyAsyncGenAThrow_Type #include "pycore_hamt.h" // _PyHamtItems_Type @@ -17,6 +18,7 @@ #include "pycore_instruction_sequence.h" // _PyInstructionSequence_Type #include "pycore_interpframe.h" // _PyFrame_Stackbase() #include "pycore_interpolation.h" // _PyInterpolation_Type +#include "pycore_lazyimportobject.h" // PyLazyImport_Type #include "pycore_list.h" // _PyList_DebugMallocStats() #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_memoryobject.h" // _PyManagedBuffer_Type @@ -31,6 +33,7 @@ #include "pycore_tuple.h" // _PyTuple_DebugMallocStats() #include "pycore_typeobject.h" // _PyBufferWrapper_Type #include "pycore_typevarobject.h" // _PyTypeAlias_Type +#include "pycore_stackref.h" // PyStackRef_FromPyObjectSteal #include "pycore_unionobject.h" // _PyUnion_Type @@ -57,7 +60,7 @@ _PyObject_CheckConsistency(PyObject *op, int check_content) if (PyUnicode_Check(op)) { _PyUnicode_CheckConsistency(op, check_content); } - else if (PyDict_Check(op)) { + else if (PyAnyDict_Check(op)) { _PyDict_CheckConsistency(op, check_content); } return 1; @@ -685,6 +688,8 @@ PyObject_Print(PyObject *op, FILE *fp, int flags) ret = -1; } } + + _Py_LeaveRecursiveCall(); return ret; } @@ -711,9 +716,9 @@ _PyObject_IsFreed(PyObject *op) } -/* For debugging convenience. See Misc/gdbinit for some useful gdb hooks */ +/* For debugging convenience. */ void -_PyObject_Dump(PyObject* op) +PyObject_Dump(PyObject* op) { if (_PyObject_IsFreed(op)) { /* It seems like the object memory has been freed: @@ -784,8 +789,7 @@ PyObject_Repr(PyObject *v) } if (!PyUnicode_Check(res)) { _PyErr_Format(tstate, PyExc_TypeError, - "__repr__ returned non-string (type %.200s)", - Py_TYPE(res)->tp_name); + "%T.__repr__() must return a str, not %T", v, res); Py_DECREF(res); return NULL; } @@ -827,8 +831,7 @@ PyObject_Str(PyObject *v) } if (!PyUnicode_Check(res)) { _PyErr_Format(tstate, PyExc_TypeError, - "__str__ returned non-string (type %.200s)", - Py_TYPE(res)->tp_name); + "%T.__str__() must return a str, not %T", v, res); Py_DECREF(res); return NULL; } @@ -883,8 +886,8 @@ PyObject_Bytes(PyObject *v) return NULL; if (!PyBytes_Check(result)) { PyErr_Format(PyExc_TypeError, - "__bytes__ returned non-bytes (type %.200s)", - Py_TYPE(result)->tp_name); + "%T.__bytes__() must return a bytes, not %T", + v, result); Py_DECREF(result); return NULL; } @@ -947,6 +950,7 @@ _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization) clear_freelist(&freelists->object_stack_chunks, 1, PyMem_RawFree); } clear_freelist(&freelists->unicode_writers, is_finalization, PyMem_Free); + clear_freelist(&freelists->bytes_writers, is_finalization, PyMem_Free); clear_freelist(&freelists->ints, is_finalization, free_object); clear_freelist(&freelists->pycfunctionobject, is_finalization, PyObject_GC_Del); clear_freelist(&freelists->pycmethodobject, is_finalization, PyObject_GC_Del); @@ -1264,7 +1268,10 @@ PyObject * _PyObject_GetAttrId(PyObject *v, _Py_Identifier *name) { PyObject *result; +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_DEPR_DECLS PyObject *oname = _PyUnicode_FromId(name); /* borrowed */ +_Py_COMP_DIAG_POP if (!oname) return NULL; result = PyObject_GetAttr(v, oname); @@ -1291,6 +1298,7 @@ _PyObject_SetAttributeErrorContext(PyObject* v, PyObject* name) // Augment the exception with the name and object if (PyObject_SetAttr(exc, &_Py_ID(name), name) || PyObject_SetAttr(exc, &_Py_ID(obj), v)) { + Py_DECREF(exc); return 1; } restore: @@ -1332,6 +1340,54 @@ PyObject_GetAttr(PyObject *v, PyObject *name) return result; } +/* Like PyObject_GetAttr but returns a _PyStackRef. + For types (tp_getattro == _Py_type_getattro), this can return + a deferred reference to reduce reference count contention. */ +_PyStackRef +_PyObject_GetAttrStackRef(PyObject *v, PyObject *name) +{ + PyTypeObject *tp = Py_TYPE(v); + if (!PyUnicode_Check(name)) { + PyErr_Format(PyExc_TypeError, + "attribute name must be string, not '%.200s'", + Py_TYPE(name)->tp_name); + return PyStackRef_NULL; + } + + /* Fast path for types - can return deferred references */ + if (tp->tp_getattro == _Py_type_getattro) { + _PyStackRef result = _Py_type_getattro_stackref((PyTypeObject *)v, name, NULL); + if (PyStackRef_IsNull(result)) { + _PyObject_SetAttributeErrorContext(v, name); + } + return result; + } + + /* Fall back to regular PyObject_GetAttr and convert to stackref */ + PyObject *result = NULL; + if (tp->tp_getattro != NULL) { + result = (*tp->tp_getattro)(v, name); + } + else if (tp->tp_getattr != NULL) { + const char *name_str = PyUnicode_AsUTF8(name); + if (name_str == NULL) { + return PyStackRef_NULL; + } + result = (*tp->tp_getattr)(v, (char *)name_str); + } + else { + PyErr_Format(PyExc_AttributeError, + "'%.100s' object has no attribute '%U'", + tp->tp_name, name); + } + + if (result == NULL) { + _PyObject_SetAttributeErrorContext(v, name); + return PyStackRef_NULL; + } + return PyStackRef_FromPyObjectSteal(result); +} + int PyObject_GetOptionalAttr(PyObject *v, PyObject *name, PyObject **result) { @@ -1688,27 +1744,39 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) return 0; } +// Look up a method on `self` by `name`. +// +// On success, `*method` is set and the function returns 0 or 1. If the +// return value is 1, the call is an unbound method and `*self` is the +// "self" or "cls" argument to pass. If the return value is 0, the call is +// a regular function and `*self` is cleared. +// +// On error, returns -1, clears `*self`, and sets an exception. int -_PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, +_PyObject_GetMethodStackRef(PyThreadState *ts, _PyStackRef *self, PyObject *name, _PyStackRef *method) { int meth_found = 0; + PyObject *obj = PyStackRef_AsPyObjectBorrow(*self); assert(PyStackRef_IsNull(*method)); PyTypeObject *tp = Py_TYPE(obj); if (!_PyType_IsReady(tp)) { if (PyType_Ready(tp) < 0) { - return 0; + PyStackRef_CLEAR(*self); + return -1; } } if (tp->tp_getattro != PyObject_GenericGetAttr || !PyUnicode_CheckExact(name)) { PyObject *res = PyObject_GetAttr(obj, name); + PyStackRef_CLEAR(*self); if (res != NULL) { *method = PyStackRef_FromPyObjectSteal(res); + return 0; } - return 0; + return -1; } _PyType_LookupStackRefAndVersion(tp, name, method); @@ -1723,10 +1791,12 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, if (f != NULL && PyDescr_IsData(descr)) { PyObject *value = f(descr, obj, (PyObject *)Py_TYPE(obj)); PyStackRef_CLEAR(*method); + PyStackRef_CLEAR(*self); if (value != NULL) { *method = PyStackRef_FromPyObjectSteal(value); + return 0; } - return 0; + return -1; } } } @@ -1734,9 +1804,9 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) && _PyObject_TryGetInstanceAttribute(obj, name, &attr)) { if (attr != NULL) { - PyStackRef_CLEAR(*method); - *method = PyStackRef_FromPyObjectSteal(attr); - return 0; + PyStackRef_XSETREF(*method, PyStackRef_FromPyObjectSteal(attr)); + PyStackRef_CLEAR(*self); + return 0; } dict = NULL; } @@ -1753,20 +1823,17 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, } } if (dict != NULL) { - // TODO: use _Py_dict_lookup_threadsafe_stackref - Py_INCREF(dict); - PyObject *value; - if (PyDict_GetItemRef(dict, name, &value) != 0) { - // found or error - Py_DECREF(dict); - PyStackRef_CLEAR(*method); - if (value != NULL) { - *method = PyStackRef_FromPyObjectSteal(value); - } + assert(PyUnicode_CheckExact(name)); + int found = _PyDict_GetMethodStackRef((PyDictObject *)dict, name, method); + if (found < 0) { + assert(PyStackRef_IsNull(*method)); + PyStackRef_CLEAR(*self); + return -1; + } + else if (found) { + PyStackRef_CLEAR(*self); return 0; } - // not found - Py_DECREF(dict); } if (meth_found) { @@ -1775,16 +1842,31 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, } if (f != NULL) { - PyObject *value = f(descr, obj, (PyObject *)Py_TYPE(obj)); + if (Py_IS_TYPE(descr, &PyClassMethod_Type)) { + PyObject *callable = _PyClassMethod_GetFunc(descr); + PyStackRef_XSETREF(*method, PyStackRef_FromPyObjectNew(callable)); + PyStackRef_XSETREF(*self, PyStackRef_FromPyObjectNew((PyObject *)tp)); + return 1; + } + else if (Py_IS_TYPE(descr, &PyStaticMethod_Type)) { + PyObject *callable = _PyStaticMethod_GetFunc(descr); + PyStackRef_XSETREF(*method, PyStackRef_FromPyObjectNew(callable)); + PyStackRef_CLEAR(*self); + return 0; + } + PyObject *value = f(descr, obj, (PyObject *)tp); PyStackRef_CLEAR(*method); + PyStackRef_CLEAR(*self); if (value) { *method = PyStackRef_FromPyObjectSteal(value); + return 0; } - return 0; + return -1; } if (descr != NULL) { assert(!PyStackRef_IsNull(*method)); + PyStackRef_CLEAR(*self); return 0; } @@ -1794,7 +1876,8 @@ _PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj, _PyObject_SetAttributeErrorContext(obj, name); assert(PyStackRef_IsNull(*method)); - return 0; + PyStackRef_CLEAR(*self); + return -1; } @@ -1951,7 +2034,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, } Py_INCREF(name); - Py_INCREF(tp); + _Py_INCREF_TYPE(tp); PyThreadState *tstate = _PyThreadState_GET(); _PyCStackRef cref; @@ -2026,7 +2109,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, } done: _PyThreadState_PopCStackRef(tstate, &cref); - Py_DECREF(tp); + _Py_DECREF_TYPE(tp); Py_DECREF(name); return res; } @@ -2443,19 +2526,23 @@ extern PyTypeObject _PyMemoryIter_Type; extern PyTypeObject _PyPositionsIterator; extern PyTypeObject _Py_GenericAliasIterType; -static PyTypeObject* static_types[] = { +static PyTypeObject* static_types[_Py_NUM_MANAGED_PREINITIALIZED_TYPES] = { // The two most important base types: must be initialized first and // deallocated last. &PyBaseObject_Type, &PyType_Type, + // PyStaticMethod_Type and PyCFunction_Type are used by PyType_Ready() + // on other types and so must be initialized first. + &PyStaticMethod_Type, + &PyCFunction_Type, + // Static types with base=&PyBaseObject_Type &PyAsyncGen_Type, &PyByteArrayIter_Type, &PyByteArray_Type, &PyBytesIter_Type, &PyBytes_Type, - &PyCFunction_Type, &PyCallIter_Type, &PyCapsule_Type, &PyCell_Type, @@ -2482,13 +2569,15 @@ static PyTypeObject* static_types[] = { &PyEnum_Type, &PyFilter_Type, &PyFloat_Type, - &PyFrame_Type, &PyFrameLocalsProxy_Type, + &PyFrame_Type, + &PyFrozenDict_Type, &PyFrozenSet_Type, &PyFunction_Type, &PyGen_Type, &PyGetSetDescr_Type, &PyInstanceMethod_Type, + &PyLazyImport_Type, &PyListIter_Type, &PyListRevIter_Type, &PyList_Type, @@ -2508,11 +2597,11 @@ static PyTypeObject* static_types[] = { &PyRange_Type, &PyReversed_Type, &PySTEntry_Type, + &PySentinel_Type, &PySeqIter_Type, &PySetIter_Type, &PySet_Type, &PySlice_Type, - &PyStaticMethod_Type, &PyStdPrinter_Type, &PySuper_Type, &PyTraceBack_Type, @@ -2555,6 +2644,9 @@ static PyTypeObject* static_types[] = { &_PyUnion_Type, #ifdef _Py_TIER2 &_PyUOpExecutor_Type, +#else + // The array should have the same size on all builds; see gh-149139 + NULL, #endif &_PyWeakref_CallableProxyType, &_PyWeakref_ProxyType, @@ -2579,6 +2671,9 @@ _PyTypes_InitTypes(PyInterpreterState *interp) // All other static types (unless initialized elsewhere) for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) { PyTypeObject *type = static_types[i]; + if (type == NULL) { + continue; + } if (_PyStaticType_InitBuiltin(interp, type) < 0) { return _PyStatus_ERR("Can't initialize builtin type"); } @@ -2619,6 +2714,9 @@ _PyTypes_FiniTypes(PyInterpreterState *interp) // their base classes. for (Py_ssize_t i=Py_ARRAY_LENGTH(static_types)-1; i>=0; i--) { PyTypeObject *type = static_types[i]; + if (type == NULL) { + continue; + } _PyStaticType_FiniBuiltin(interp, type); } } @@ -2675,21 +2773,14 @@ _Py_NewReferenceNoTotal(PyObject *op) void _Py_SetImmortalUntracked(PyObject *op) { -#ifdef Py_DEBUG - // For strings, use _PyUnicode_InternImmortal instead. - if (PyUnicode_CheckExact(op)) { - assert(PyUnicode_CHECK_INTERNED(op) == SSTATE_INTERNED_IMMORTAL - || PyUnicode_CHECK_INTERNED(op) == SSTATE_INTERNED_IMMORTAL_STATIC); - } -#endif // Check if already immortal to avoid degrading from static immortal to plain immortal if (_Py_IsImmortal(op)) { return; } #ifdef Py_GIL_DISABLED - op->ob_tid = _Py_UNOWNED_TID; - op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL; - op->ob_ref_shared = 0; + _Py_atomic_store_uintptr_relaxed(&op->ob_tid, _Py_UNOWNED_TID); + _Py_atomic_store_uint32_relaxed(&op->ob_ref_local, _Py_IMMORTAL_REFCNT_LOCAL); + _Py_atomic_store_ssize_relaxed(&op->ob_ref_shared, 0); _Py_atomic_or_uint8(&op->ob_gc_bits, _PyGC_BITS_DEFERRED); #elif SIZEOF_VOID_P > 4 op->ob_flags = _Py_IMMORTAL_FLAGS; @@ -2730,6 +2821,13 @@ PyUnstable_Object_EnableDeferredRefcount(PyObject *op) return 0; } + if (!PyObject_GC_IsTracked(op)) { + // When deferred refcount is enabled, the object will only be + // deallocated by the tracing garbage collector. So it must be tracked + // by the garbage collector. + return 0; + } + uint8_t bits = _Py_atomic_load_uint8(&op->ob_gc_bits); if ((bits & _PyGC_BITS_DEFERRED) != 0) { @@ -2765,8 +2863,12 @@ PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *op) _PyStackRef *stackpointer = frame->stackpointer; while (stackpointer > base) { stackpointer--; - if (op == PyStackRef_AsPyObjectBorrow(*stackpointer)) { - return PyStackRef_IsHeapSafe(*stackpointer); + _PyStackRef ref = *stackpointer; + if (PyStackRef_IsTaggedInt(ref)) { + continue; + } + if (op == PyStackRef_AsPyObjectBorrow(ref)) { + return PyStackRef_IsHeapSafe(ref); } } return 0; @@ -2786,6 +2888,17 @@ PyUnstable_EnableTryIncRef(PyObject *op) #endif } +int +PyUnstable_SetImmortal(PyObject *op) +{ + assert(op != NULL); + if (!_PyObject_IsUniquelyReferenced(op) || PyUnicode_Check(op)) { + return 0; + } + _Py_SetImmortal(op); + return 1; +} + void _Py_ResurrectReference(PyObject *op) { @@ -3010,9 +3123,9 @@ Py_ReprEnter(PyObject *obj) list = PyList_New(0); if (list == NULL) return -1; - if (PyDict_SetItem(dict, &_Py_ID(Py_Repr), list) < 0) + if (_PyDict_SetItem_Take2((PyDictObject *)dict, &_Py_ID(Py_Repr), list) < 0) { return -1; - Py_DECREF(list); + } } i = PyList_GET_SIZE(list); while (--i >= 0) { @@ -3057,7 +3170,7 @@ Py_ReprLeave(PyObject *obj) /* Trashcan support. */ -/* Add op to the gcstate->trash_delete_later list. Called when the current +/* Add op to the tstate->delete_later list. Called when the current * call-stack depth gets large. op must be a gc'ed object, with refcount 0. * Py_DECREF must already have been called on it. */ @@ -3083,7 +3196,7 @@ _PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op) tstate->delete_later = op; } -/* Deallocate all the objects in the gcstate->trash_delete_later list. +/* Deallocate all the objects in the tstate->delete_later list. * Called when the call-stack unwinds again. */ void _PyTrash_thread_destroy_chain(PyThreadState *tstate) @@ -3156,7 +3269,7 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg, /* This might succeed or fail, but we're about to abort, so at least try to provide any extra info we can: */ - _PyObject_Dump(obj); + PyObject_Dump(obj); fprintf(stderr, "\n"); fflush(stderr); @@ -3293,8 +3406,18 @@ _Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt) int PyRefTracer_SetTracer(PyRefTracer tracer, void *data) { _Py_AssertHoldsTstate(); + + _PyEval_StopTheWorldAll(&_PyRuntime); + if (_PyRuntime.ref_tracer.tracer_func != NULL) { + _PyReftracerTrack(NULL, PyRefTracer_TRACKER_REMOVED); + if (PyErr_Occurred()) { + _PyEval_StartTheWorldAll(&_PyRuntime); + return -1; + } + } _PyRuntime.ref_tracer.tracer_func = tracer; _PyRuntime.ref_tracer.tracer_data = data; + _PyEval_StartTheWorldAll(&_PyRuntime); return 0; } @@ -3357,24 +3480,6 @@ Py_GetConstantBorrowed(unsigned int constant_id) return Py_GetConstant(constant_id); } - -// Py_TYPE() implementation for the stable ABI -#undef Py_TYPE -PyTypeObject* -Py_TYPE(PyObject *ob) -{ - return _Py_TYPE(ob); -} - - -// Py_REFCNT() implementation for the stable ABI -#undef Py_REFCNT -Py_ssize_t -Py_REFCNT(PyObject *ob) -{ - return _Py_REFCNT(ob); -} - int PyUnstable_IsImmortal(PyObject *op) { @@ -3391,3 +3496,26 @@ PyUnstable_Object_IsUniquelyReferenced(PyObject *op) assert(op != NULL); return _PyObject_IsUniquelyReferenced(op); } + +int +_PyObject_VisitType(PyObject *op, visitproc visit, void *arg) +{ + assert(op != NULL); + PyTypeObject *tp = Py_TYPE(op); + _PyObject_ASSERT((PyObject *)tp, PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)); + Py_VISIT(tp); + return 0; +} + +// Implementations for the stable ABI +// Keep these at the end. +#undef Py_TYPE +#undef Py_REFCNT +#undef Py_SIZE +#undef Py_IS_TYPE +#undef Py_SET_SIZE +PyTypeObject* Py_TYPE(PyObject *ob) { return _Py_TYPE_impl(ob); } +Py_ssize_t Py_REFCNT(PyObject *ob) { return _Py_REFCNT(ob); } +Py_ssize_t Py_SIZE(PyObject *o) { return _Py_SIZE_impl(o); } +int Py_IS_TYPE(PyObject *o, PyTypeObject *t) { return _Py_IS_TYPE_impl(o, t); } +void Py_SET_SIZE(PyVarObject *o, Py_ssize_t s) { _Py_SET_SIZE_impl(o, s); } diff --git a/Objects/object_layout_312.png b/Objects/object_layout_312.png index a63d095ea0b19e..9ccb99f9db1b6d 100644 Binary files a/Objects/object_layout_312.png and b/Objects/object_layout_312.png differ diff --git a/Objects/object_layout_313.png b/Objects/object_layout_313.png index f7059c286c84e6..f3df59253da131 100644 Binary files a/Objects/object_layout_313.png and b/Objects/object_layout_313.png differ diff --git a/Objects/object_layout_full_312.png b/Objects/object_layout_full_312.png index 4f46ca86091d45..8c0aca4e06a302 100644 Binary files a/Objects/object_layout_full_312.png and b/Objects/object_layout_full_312.png differ diff --git a/Objects/object_layout_full_313.png b/Objects/object_layout_full_313.png index 7352ec69e5d8db..2d5c8569f628de 100644 Binary files a/Objects/object_layout_full_313.png and b/Objects/object_layout_full_313.png differ diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index deb7fd957e57dd..0947d47c8a5558 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -2,6 +2,7 @@ #include "Python.h" #include "pycore_interp.h" // _PyInterpreterState_HasFeature +#include "pycore_mmap.h" // _PyAnnotateMemoryMap() #include "pycore_object.h" // _PyDebugAllocatorStats() definition #include "pycore_obmalloc.h" #include "pycore_obmalloc_init.h" @@ -12,6 +13,8 @@ #include <stdlib.h> // malloc() #include <stdbool.h> +#include <stdio.h> // fopen(), fgets(), sscanf() +#include <errno.h> // errno #ifdef WITH_MIMALLOC // Forward declarations of functions used in our mimalloc modifications static void _PyMem_mi_page_clear_qsbr(mi_page_t *page); @@ -149,6 +152,12 @@ should_advance_qsbr_for_page(struct _qsbr_thread_state *qsbr, mi_page_t *page) } return false; } + +static _PyThreadStateImpl * +tstate_from_heap(mi_heap_t *heap) +{ + return _Py_CONTAINER_OF(heap->tld, _PyThreadStateImpl, mimalloc.tld); +} #endif static bool @@ -157,23 +166,35 @@ _PyMem_mi_page_maybe_free(mi_page_t *page, mi_page_queue_t *pq, bool force) #ifdef Py_GIL_DISABLED assert(mi_page_all_free(page)); if (page->use_qsbr) { - _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)PyThreadState_GET(); - if (page->qsbr_goal != 0 && _Py_qbsr_goal_reached(tstate->qsbr, page->qsbr_goal)) { + struct _qsbr_thread_state *qsbr = ((_PyThreadStateImpl *)PyThreadState_GET())->qsbr; + if (page->qsbr_goal != 0 && _Py_qbsr_goal_reached(qsbr, page->qsbr_goal)) { _PyMem_mi_page_clear_qsbr(page); _mi_page_free(page, pq, force); return true; } + // gh-145615: since we are not freeing this page yet, we want to + // make it available for allocations. Note that the QSBR goal and + // linked list node remain set even if the page is later used for + // an allocation. We only detect and clear the QSBR goal when the + // page becomes empty again (used == 0). + if (mi_page_is_in_full(page)) { + _mi_page_unfull(page); + } + _PyMem_mi_page_clear_qsbr(page); page->retire_expire = 0; - if (should_advance_qsbr_for_page(tstate->qsbr, page)) { - page->qsbr_goal = _Py_qsbr_advance(tstate->qsbr->shared); + if (should_advance_qsbr_for_page(qsbr, page)) { + page->qsbr_goal = _Py_qsbr_advance(qsbr->shared); } else { - page->qsbr_goal = _Py_qsbr_shared_next(tstate->qsbr->shared); + page->qsbr_goal = _Py_qsbr_shared_next(qsbr->shared); } + // We may be freeing a page belonging to a different thread during a + // stop-the-world event. Find the _PyThreadStateImpl for the page. + _PyThreadStateImpl *tstate = tstate_from_heap(mi_page_heap(page)); llist_insert_tail(&tstate->mimalloc.page_list, &page->qsbr_node); return false; } @@ -190,7 +211,8 @@ _PyMem_mi_page_reclaimed(mi_page_t *page) if (page->qsbr_goal != 0) { if (mi_page_all_free(page)) { assert(page->qsbr_node.next == NULL); - _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)PyThreadState_GET(); + _PyThreadStateImpl *tstate = tstate_from_heap(mi_page_heap(page)); + assert(tstate == (_PyThreadStateImpl *)_PyThreadState_GET()); page->retire_expire = 0; llist_insert_tail(&tstate->mimalloc.page_list, &page->qsbr_node); } @@ -306,8 +328,48 @@ _PyObject_MiRealloc(void *ctx, void *ptr, size_t nbytes) { #ifdef Py_GIL_DISABLED _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET(); + // Implement our own realloc logic so that we can copy PyObject header + // in a thread-safe way. + size_t size = mi_usable_size(ptr); + if (nbytes <= size && nbytes >= (size / 2) && nbytes > 0) { + return ptr; + } + mi_heap_t *heap = tstate->mimalloc.current_object_heap; - return mi_heap_realloc(heap, ptr, nbytes); + void* newp = mi_heap_malloc(heap, nbytes); + if (newp == NULL) { + return NULL; + } + + // Free threaded Python allows access from other threads to the PyObject reference count + // fields for a period of time after the object is freed (see InternalDocs/qsbr.md). + // These fields are typically initialized by PyObject_Init() using relaxed + // atomic stores. We need to copy these fields in a thread-safe way here. + // We use the "debug_offset" to determine how many bytes to copy -- it + // includes the PyObject header and plus any extra pre-headers. + size_t offset = heap->debug_offset; + assert(offset % sizeof(void*) == 0); + + size_t copy_size = (size < nbytes ? size : nbytes); + if (copy_size >= offset) { + for (size_t i = 0; i != offset; i += sizeof(void*)) { + // Use memcpy to avoid strict-aliasing issues. However, we probably + // still have unavoidable strict-aliasing issues with + // _Py_atomic_store_ptr_relaxed here. + void *word; + memcpy(&word, (char*)ptr + i, sizeof(void*)); + _Py_atomic_store_ptr_relaxed((void**)((char*)newp + i), word); + } + _mi_memcpy((char*)newp + offset, (char*)ptr + offset, copy_size - offset); + } + else { + // memcpy(dst, NULL, 0) is undefined behavior. See gh-151297. + if mi_likely(ptr) { + _mi_memcpy(newp, ptr, copy_size); + } + } + mi_free(ptr); + return newp; #else return mi_realloc(ptr, nbytes); #endif @@ -319,6 +381,29 @@ _PyObject_MiFree(void *ctx, void *ptr) mi_free(ptr); } +void * +_PyMem_MiRawMalloc(void *ctx, size_t size) +{ + return mi_malloc(size); +} + +void * +_PyMem_MiRawCalloc(void *ctx, size_t nelem, size_t elsize) +{ + return mi_calloc(nelem, elsize); +} + +void * +_PyMem_MiRawRealloc(void *ctx, void *ptr, size_t size) +{ + return mi_realloc(ptr, size); +} + +void +_PyMem_MiRawFree(void *ctx, void *ptr) +{ + mi_free(ptr); +} #endif // WITH_MIMALLOC @@ -326,7 +411,8 @@ _PyObject_MiFree(void *ctx, void *ptr) #ifdef WITH_MIMALLOC -# define MIMALLOC_ALLOC {NULL, _PyMem_MiMalloc, _PyMem_MiCalloc, _PyMem_MiRealloc, _PyMem_MiFree} +# define MIMALLOC_ALLOC {NULL, _PyMem_MiMalloc, _PyMem_MiCalloc, _PyMem_MiRealloc, _PyMem_MiFree} +# define MIMALLOC_RAWALLOC {NULL, _PyMem_MiRawMalloc, _PyMem_MiRawCalloc, _PyMem_MiRawRealloc, _PyMem_MiRawFree} # define MIMALLOC_OBJALLOC {NULL, _PyObject_MiMalloc, _PyObject_MiCalloc, _PyObject_MiRealloc, _PyObject_MiFree} #endif @@ -344,7 +430,7 @@ void* _PyObject_Realloc(void *ctx, void *ptr, size_t size); #if defined(Py_GIL_DISABLED) // Py_GIL_DISABLED requires using mimalloc for "mem" and "obj" domains. -# define PYRAW_ALLOC MALLOC_ALLOC +# define PYRAW_ALLOC MIMALLOC_RAWALLOC # define PYMEM_ALLOC MIMALLOC_ALLOC # define PYOBJ_ALLOC MIMALLOC_OBJALLOC #elif defined(WITH_PYMALLOC) @@ -454,19 +540,138 @@ _PyMem_DefaultRawWcsdup(const wchar_t *str) # endif #endif +/* Return the system's default huge page size in bytes, or 0 if it + * cannot be determined. The result is cached after the first call. + * + * This is Linux-only (/proc/meminfo). On other systems that define + * MAP_HUGETLB the caller should skip huge pages gracefully. */ +#if defined(PYMALLOC_USE_HUGEPAGES) && defined(ARENAS_USE_MMAP) && defined(MAP_HUGETLB) +static size_t +_pymalloc_system_hugepage_size(void) +{ + static size_t hp_size = 0; + static int initialized = 0; + + if (initialized) { + return hp_size; + } + +#ifdef __linux__ + FILE *f = fopen("/proc/meminfo", "r"); + if (f != NULL) { + char line[256]; + while (fgets(line, sizeof(line), f)) { + unsigned long size_kb; + if (sscanf(line, "Hugepagesize: %lu kB", &size_kb) == 1) { + hp_size = (size_t)size_kb * 1024; + break; + } + } + fclose(f); + } +#endif + + initialized = 1; + return hp_size; +} +#endif + +#if (defined(MS_WINDOWS) && defined(PYMALLOC_USE_HUGEPAGES)) || \ + (defined(PYMALLOC_USE_HUGEPAGES) && defined(ARENAS_USE_MMAP) && defined(MAP_HUGETLB)) +static size_t +_pymalloc_round_up_to_multiple(size_t size, size_t multiple) +{ + if (multiple == 0 || size == 0) { + return size; + } + + size_t remainder = size % multiple; + if (remainder == 0) { + return size; + } + + size_t padding = multiple - remainder; + if (size > SIZE_MAX - padding) { + return 0; + } + return size + padding; +} +#endif + +static size_t +_pymalloc_virtual_alloc_size(size_t size) +{ +#if defined(MS_WINDOWS) && defined(PYMALLOC_USE_HUGEPAGES) + if (_PyRuntime.allocators.use_hugepages) { + SIZE_T large_page_size = GetLargePageMinimum(); + if (large_page_size > 0) { + return _pymalloc_round_up_to_multiple(size, (size_t)large_page_size); + } + } +#elif defined(PYMALLOC_USE_HUGEPAGES) && defined(ARENAS_USE_MMAP) && defined(MAP_HUGETLB) + if (_PyRuntime.allocators.use_hugepages) { + size_t hp_size = _pymalloc_system_hugepage_size(); + if (hp_size > 0) { + return _pymalloc_round_up_to_multiple(size, hp_size); + } + } +#endif + return size; +} + void * _PyMem_ArenaAlloc(void *Py_UNUSED(ctx), size_t size) { #ifdef MS_WINDOWS +# ifdef PYMALLOC_USE_HUGEPAGES + if (_PyRuntime.allocators.use_hugepages) { + SIZE_T lp_size = GetLargePageMinimum(); + if (lp_size > 0 && size % lp_size == 0) { + void *ptr = VirtualAlloc(NULL, size, + MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, + PAGE_READWRITE); + if (ptr != NULL) + return ptr; + } + } + /* Fall back to regular pages */ +# endif return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); #elif defined(ARENAS_USE_MMAP) void *ptr; +# ifdef PYMALLOC_USE_HUGEPAGES +# ifdef MAP_HUGETLB + if (_PyRuntime.allocators.use_hugepages) { + size_t hp_size = _pymalloc_system_hugepage_size(); + /* Only use huge pages if the arena size is a multiple of the + * system's default huge page size. When the arena is smaller + * than the huge page, mmap still succeeds but silently + * allocates an entire huge page; the subsequent munmap with + * the smaller arena size then fails with EINVAL, leaking + * all of that memory. */ + if (hp_size > 0 && size % hp_size == 0) { + ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB, -1, 0); + if (ptr != MAP_FAILED) { + assert(ptr != NULL); + (void)_PyAnnotateMemoryMap(ptr, size, "cpython:pymalloc:hugepage"); + return ptr; + } + } + } + /* Fall back to regular pages */ +# endif +# endif ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if (ptr == MAP_FAILED) return NULL; assert(ptr != NULL); +#ifdef MADV_HUGEPAGE + (void)madvise(ptr, size, MADV_HUGEPAGE); +#endif + (void)_PyAnnotateMemoryMap(ptr, size, "cpython:pymalloc"); return ptr; #else return malloc(size); @@ -493,7 +698,11 @@ _PyMem_ArenaFree(void *Py_UNUSED(ctx), void *ptr, if (ptr == NULL) { return; } - munmap(ptr, size); + if (munmap(ptr, size) < 0) { + _Py_FatalErrorFormat(__func__, + "munmap(%p, %zu) failed with errno %d", + ptr, size, errno); + } #else free(ptr); #endif @@ -646,7 +855,7 @@ set_up_allocators_unlocked(PyMemAllocatorName allocator) case PYMEM_ALLOCATOR_MIMALLOC: case PYMEM_ALLOCATOR_MIMALLOC_DEBUG: { - PyMemAllocatorEx malloc_alloc = MALLOC_ALLOC; + PyMemAllocatorEx malloc_alloc = MIMALLOC_RAWALLOC; set_allocator_unlocked(PYMEM_DOMAIN_RAW, &malloc_alloc); PyMemAllocatorEx pymalloc = MIMALLOC_ALLOC; @@ -716,6 +925,7 @@ get_current_allocator_name_unlocked(void) #ifdef WITH_MIMALLOC PyMemAllocatorEx mimalloc = MIMALLOC_ALLOC; PyMemAllocatorEx mimalloc_obj = MIMALLOC_OBJALLOC; + PyMemAllocatorEx mimalloc_raw = MIMALLOC_RAWALLOC; #endif if (pymemallocator_eq(&_PyMem_Raw, &malloc_alloc) && @@ -733,7 +943,7 @@ get_current_allocator_name_unlocked(void) } #endif #ifdef WITH_MIMALLOC - if (pymemallocator_eq(&_PyMem_Raw, &malloc_alloc) && + if (pymemallocator_eq(&_PyMem_Raw, &mimalloc_raw) && pymemallocator_eq(&_PyMem, &mimalloc) && pymemallocator_eq(&_PyObject, &mimalloc_obj)) { @@ -765,7 +975,7 @@ get_current_allocator_name_unlocked(void) } #endif #ifdef WITH_MIMALLOC - if (pymemallocator_eq(&_PyMem_Debug.raw.alloc, &malloc_alloc) && + if (pymemallocator_eq(&_PyMem_Debug.raw.alloc, &mimalloc_raw) && pymemallocator_eq(&_PyMem_Debug.mem.alloc, &mimalloc) && pymemallocator_eq(&_PyMem_Debug.obj.alloc, &mimalloc_obj)) { @@ -972,13 +1182,19 @@ PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator) void * _PyObject_VirtualAlloc(size_t size) { - return _PyObject_Arena.alloc(_PyObject_Arena.ctx, size); + size_t alloc_size = _pymalloc_virtual_alloc_size(size); + if (alloc_size == 0 && size != 0) { + return NULL; + } + return _PyObject_Arena.alloc(_PyObject_Arena.ctx, alloc_size); } void _PyObject_VirtualFree(void *obj, size_t size) { - _PyObject_Arena.free(_PyObject_Arena.ctx, obj, size); + size_t alloc_size = _pymalloc_virtual_alloc_size(size); + assert(alloc_size != 0 || size == 0); + _PyObject_Arena.free(_PyObject_Arena.ctx, obj, alloc_size); } @@ -1523,9 +1739,9 @@ PyObject_Free(void *ptr) } -/* If we're using GCC, use __builtin_expect() to reduce overhead of +/* Use __builtin_expect() where available to reduce overhead of the valgrind checks */ -#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +#if (defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 2))) && defined(__OPTIMIZE__) # define UNLIKELY(value) __builtin_expect((value), 0) # define LIKELY(value) __builtin_expect((value), 1) #else diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 02fcbbaa0d4536..6f05395b18d781 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -223,7 +223,6 @@ PyDict_DelItem PyMapping_DelItem PyDict_DelItemString PyMapping_DelItemString PyDict_GetItem - PyDict_GetItemWithError PyObject_GetItem -_PyDict_GetItemIdWithError - PyDict_GetItemString PyMapping_GetItemString PyDict_Items PyMapping_Items PyDict_Keys PyMapping_Keys @@ -536,6 +535,7 @@ struct _odictnode { static Py_ssize_t _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); PyObject *value = NULL; PyDictKeysObject *keys = ((PyDictObject *)od)->ma_keys; Py_ssize_t ix; @@ -560,6 +560,7 @@ _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash) static int _odict_resize(PyODictObject *od) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_ssize_t size, i; _ODictNode **fast_nodes, *node; @@ -596,6 +597,7 @@ _odict_resize(PyODictObject *od) static Py_ssize_t _odict_get_index(PyODictObject *od, PyObject *key, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); PyDictKeysObject *keys; assert(key != NULL); @@ -616,6 +618,7 @@ _odict_get_index(PyODictObject *od, PyObject *key, Py_hash_t hash) static _ODictNode * _odict_find_node_hash(PyODictObject *od, PyObject *key, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_ssize_t index; if (_odict_EMPTY(od)) @@ -630,6 +633,7 @@ _odict_find_node_hash(PyODictObject *od, PyObject *key, Py_hash_t hash) static _ODictNode * _odict_find_node(PyODictObject *od, PyObject *key) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_ssize_t index; Py_hash_t hash; @@ -648,6 +652,7 @@ _odict_find_node(PyODictObject *od, PyObject *key) static void _odict_add_head(PyODictObject *od, _ODictNode *node) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); _odictnode_PREV(node) = NULL; _odictnode_NEXT(node) = _odict_FIRST(od); if (_odict_FIRST(od) == NULL) @@ -661,6 +666,7 @@ _odict_add_head(PyODictObject *od, _ODictNode *node) static void _odict_add_tail(PyODictObject *od, _ODictNode *node) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); _odictnode_PREV(node) = _odict_LAST(od); _odictnode_NEXT(node) = NULL; if (_odict_LAST(od) == NULL) @@ -675,6 +681,7 @@ _odict_add_tail(PyODictObject *od, _ODictNode *node) static int _odict_add_new_node(PyODictObject *od, PyObject *key, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_ssize_t i; _ODictNode *node; @@ -719,6 +726,7 @@ _odict_add_new_node(PyODictObject *od, PyObject *key, Py_hash_t hash) static void _odict_remove_node(PyODictObject *od, _ODictNode *node) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); if (_odict_FIRST(od) == node) _odict_FIRST(od) = _odictnode_NEXT(node); else if (_odictnode_PREV(node) != NULL) @@ -754,6 +762,7 @@ static int _odict_clear_node(PyODictObject *od, _ODictNode *node, PyObject *key, Py_hash_t hash) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_ssize_t i; assert(key != NULL); @@ -897,7 +906,7 @@ odict_or(PyObject *left, PyObject *right) type = Py_TYPE(right); other = left; } - if (!PyDict_Check(other)) { + if (!PyAnyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } PyObject *new = PyObject_CallOneArg((PyObject*)type, left); @@ -935,6 +944,7 @@ static PyNumberMethods odict_as_number = { /* fromkeys() */ /*[clinic input] +@permit_long_summary @classmethod OrderedDict.fromkeys @@ -946,36 +956,39 @@ Create a new ordered dictionary with keys from iterable and values set to value. static PyObject * OrderedDict_fromkeys_impl(PyTypeObject *type, PyObject *seq, PyObject *value) -/*[clinic end generated code: output=c10390d452d78d6d input=1a0476c229c597b3]*/ +/*[clinic end generated code: output=c10390d452d78d6d input=1277ae0769083848]*/ { return _PyDict_FromKeys((PyObject *)type, seq, value); } -/* __sizeof__() */ - -/* OrderedDict.__sizeof__() does not have a docstring. */ -PyDoc_STRVAR(odict_sizeof__doc__, ""); +/*[clinic input] +@critical_section +OrderedDict.__sizeof__ -> Py_ssize_t +[clinic start generated code]*/ -static PyObject * -odict_sizeof(PyObject *op, PyObject *Py_UNUSED(ignored)) +static Py_ssize_t +OrderedDict___sizeof___impl(PyODictObject *self) +/*[clinic end generated code: output=1a8560db8cf83ac5 input=655e989ae24daa6a]*/ { - PyODictObject *od = _PyODictObject_CAST(op); - Py_ssize_t res = _PyDict_SizeOf((PyDictObject *)od); - res += sizeof(_ODictNode *) * od->od_fast_nodes_size; /* od_fast_nodes */ - if (!_odict_EMPTY(od)) { - res += sizeof(_ODictNode) * PyODict_SIZE(od); /* linked-list */ + Py_ssize_t res = _PyDict_SizeOf_LockHeld((PyDictObject *)self); + res += sizeof(_ODictNode *) * self->od_fast_nodes_size; /* od_fast_nodes */ + if (!_odict_EMPTY(self)) { + res += sizeof(_ODictNode) * PyODict_SIZE(self); /* linked-list */ } - return PyLong_FromSsize_t(res); + return res; } -/* __reduce__() */ +/*[clinic input] +OrderedDict.__reduce__ + self as od: self(type="PyODictObject *") -PyDoc_STRVAR(odict_reduce__doc__, "Return state information for pickling"); +Return state information for pickling +[clinic start generated code]*/ static PyObject * -odict_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) +OrderedDict___reduce___impl(PyODictObject *od) +/*[clinic end generated code: output=71eeb81f760a6a8e input=b0467c7ec400fe5e]*/ { - register PyODictObject *od = _PyODictObject_CAST(op); PyObject *state, *result = NULL; PyObject *items_iter, *items, *args = NULL; @@ -1010,8 +1023,10 @@ odict_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) /* setdefault(): Skips __missing__() calls. */ +static int PyODict_SetItem_LockHeld(PyObject *self, PyObject *key, PyObject *value); /*[clinic input] +@critical_section OrderedDict.setdefault key: object @@ -1025,7 +1040,7 @@ Return the value for key if key is in the dictionary, else default. static PyObject * OrderedDict_setdefault_impl(PyODictObject *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=97537cb7c28464b6 input=38e098381c1efbc6]*/ +/*[clinic end generated code: output=97537cb7c28464b6 input=d7b93e92734f99b5]*/ { PyObject *result = NULL; @@ -1035,7 +1050,7 @@ OrderedDict_setdefault_impl(PyODictObject *self, PyObject *key, if (PyErr_Occurred()) return NULL; assert(_odict_find_node(self, key) == NULL); - if (PyODict_SetItem((PyObject *)self, key, default_value) >= 0) { + if (PyODict_SetItem_LockHeld((PyObject *)self, key, default_value) >= 0) { result = Py_NewRef(default_value); } } @@ -1065,10 +1080,9 @@ static PyObject * _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj, Py_hash_t hash) { - PyObject *value = NULL; - - Py_BEGIN_CRITICAL_SECTION(od); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); + PyObject *value = NULL; _ODictNode *node = _odict_find_node_hash(_PyODictObject_CAST(od), key, hash); if (node != NULL) { /* Pop the node first to avoid a possible dict resize (due to @@ -1093,7 +1107,6 @@ _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj, PyErr_SetObject(PyExc_KeyError, key); } } - Py_END_CRITICAL_SECTION(); done: return value; @@ -1101,6 +1114,8 @@ _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj, /* Skips __missing__() calls. */ /*[clinic input] +@critical_section +@permit_long_summary OrderedDict.pop key: object @@ -1115,7 +1130,7 @@ raise a KeyError. static PyObject * OrderedDict_pop_impl(PyODictObject *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=7a6447d104e7494b input=7efe36601007dff7]*/ +/*[clinic end generated code: output=7a6447d104e7494b input=0742e3c9bf076a72]*/ { Py_hash_t hash = PyObject_Hash(key); if (hash == -1) @@ -1127,20 +1142,22 @@ OrderedDict_pop_impl(PyODictObject *self, PyObject *key, /* popitem() */ /*[clinic input] +@critical_section OrderedDict.popitem last: bool = True Remove and return a (key, value) pair from the dictionary. -Pairs are returned in LIFO order if last is true or FIFO order if false. +Pairs are returned in LIFO order if last is true or FIFO order if +false. [clinic start generated code]*/ static PyObject * OrderedDict_popitem_impl(PyODictObject *self, int last) -/*[clinic end generated code: output=98e7d986690d49eb input=d992ac5ee8305e1a]*/ +/*[clinic end generated code: output=98e7d986690d49eb input=ebf1cc91579c9e54]*/ { - PyObject *key, *value, *item = NULL; + PyObject *key, *value; _ODictNode *node; /* pull the item */ @@ -1153,12 +1170,11 @@ OrderedDict_popitem_impl(PyODictObject *self, int last) node = last ? _odict_LAST(self) : _odict_FIRST(self); key = Py_NewRef(_odictnode_KEY(node)); value = _odict_popkey_hash((PyObject *)self, key, NULL, _odictnode_HASH(node)); - if (value == NULL) + if (value == NULL) { + Py_DECREF(key); return NULL; - item = PyTuple_Pack(2, key, value); - Py_DECREF(key); - Py_DECREF(value); - return item; + } + return _PyTuple_FromPairSteal(key, value); } /* keys() */ @@ -1167,6 +1183,9 @@ OrderedDict_popitem_impl(PyODictObject *self, int last) PyDoc_STRVAR(odict_keys__doc__, ""); static PyObject * odictkeys_new(PyObject *od, PyObject *Py_UNUSED(ignored)); /* forward */ +static int +_PyODict_SetItem_KnownHash_LockHeld(PyObject *od, PyObject *key, PyObject *value, + Py_hash_t hash); /* forward */ /* values() */ @@ -1192,32 +1211,36 @@ static PyObject * mutablemapping_update(PyObject *, PyObject *, PyObject *); #define odict_update mutablemapping_update -/* clear() */ +/*[clinic input] +@critical_section +OrderedDict.clear -PyDoc_STRVAR(odict_clear__doc__, - "od.clear() -> None. Remove all items from od."); +Remove all items from ordered dict. +[clinic start generated code]*/ static PyObject * -odict_clear(PyObject *op, PyObject *Py_UNUSED(ignored)) +OrderedDict_clear_impl(PyODictObject *self) +/*[clinic end generated code: output=a1a76d1322f556c5 input=08b12322e74c535c]*/ { - register PyODictObject *od = _PyODictObject_CAST(op); - PyDict_Clear(op); - _odict_clear_nodes(od); + _PyDict_Clear_LockHeld((PyObject *)self); + _odict_clear_nodes(self); Py_RETURN_NONE; } /* copy() */ -/* forward */ -static int _PyODict_SetItem_KnownHash(PyObject *, PyObject *, PyObject *, - Py_hash_t); +/*[clinic input] +@critical_section +OrderedDict.copy + self as od: self -PyDoc_STRVAR(odict_copy__doc__, "od.copy() -> a shallow copy of od"); +A shallow copy of ordered dict. +[clinic start generated code]*/ static PyObject * -odict_copy(PyObject *op, PyObject *Py_UNUSED(ignored)) +OrderedDict_copy_impl(PyObject *od) +/*[clinic end generated code: output=9cdbe7394aecc576 input=e329951ae617ed48]*/ { - register PyODictObject *od = _PyODictObject_CAST(op); _ODictNode *node; PyObject *od_copy; @@ -1237,8 +1260,8 @@ odict_copy(PyObject *op, PyObject *Py_UNUSED(ignored)) PyErr_SetObject(PyExc_KeyError, key); goto fail; } - if (_PyODict_SetItem_KnownHash((PyObject *)od_copy, key, value, - _odictnode_HASH(node)) != 0) + if (_PyODict_SetItem_KnownHash_LockHeld((PyObject *)od_copy, key, value, + _odictnode_HASH(node)) != 0) goto fail; } } @@ -1286,6 +1309,7 @@ odict_reversed(PyObject *op, PyObject *Py_UNUSED(ignored)) /* move_to_end() */ /*[clinic input] +@critical_section OrderedDict.move_to_end key: object @@ -1298,7 +1322,7 @@ Raise KeyError if the element does not exist. static PyObject * OrderedDict_move_to_end_impl(PyODictObject *self, PyObject *key, int last) -/*[clinic end generated code: output=fafa4c5cc9b92f20 input=d6ceff7132a2fcd7]*/ +/*[clinic end generated code: output=fafa4c5cc9b92f20 input=09f8bc7053c0f6d4]*/ { _ODictNode *node; @@ -1339,10 +1363,8 @@ static PyMethodDef odict_methods[] = { /* overridden dict methods */ ORDEREDDICT_FROMKEYS_METHODDEF - {"__sizeof__", odict_sizeof, METH_NOARGS, - odict_sizeof__doc__}, - {"__reduce__", odict_reduce, METH_NOARGS, - odict_reduce__doc__}, + ORDEREDDICT___SIZEOF___METHODDEF + ORDEREDDICT___REDUCE___METHODDEF ORDEREDDICT_SETDEFAULT_METHODDEF ORDEREDDICT_POP_METHODDEF ORDEREDDICT_POPITEM_METHODDEF @@ -1354,11 +1376,8 @@ static PyMethodDef odict_methods[] = { odict_items__doc__}, {"update", _PyCFunction_CAST(odict_update), METH_VARARGS | METH_KEYWORDS, odict_update__doc__}, - {"clear", odict_clear, METH_NOARGS, - odict_clear__doc__}, - {"copy", odict_copy, METH_NOARGS, - odict_copy__doc__}, - + ORDEREDDICT_CLEAR_METHODDEF + ORDEREDDICT_COPY_METHODDEF /* new methods */ {"__reversed__", odict_reversed, METH_NOARGS, odict_reversed__doc__}, @@ -1457,7 +1476,8 @@ odict_tp_clear(PyObject *op) { PyODictObject *od = _PyODictObject_CAST(op); Py_CLEAR(od->od_inst_dict); - PyDict_Clear((PyObject *)od); + // cannot use lock held variant as critical section is not held here + PyDict_Clear(op); _odict_clear_nodes(od); return 0; } @@ -1465,7 +1485,7 @@ odict_tp_clear(PyObject *op) /* tp_richcompare */ static PyObject * -odict_richcompare(PyObject *v, PyObject *w, int op) +odict_richcompare_lock_held(PyObject *v, PyObject *w, int op) { if (!PyODict_Check(v) || !PyDict_Check(w)) { Py_RETURN_NOTIMPLEMENTED; @@ -1498,6 +1518,16 @@ odict_richcompare(PyObject *v, PyObject *w, int op) } } +static PyObject * +odict_richcompare(PyObject *v, PyObject *w, int op) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION2(v, w); + res = odict_richcompare_lock_held(v, w, op); + Py_END_CRITICAL_SECTION2(); + return res; +} + /* tp_iter */ static PyObject * @@ -1517,7 +1547,7 @@ odict_init(PyObject *self, PyObject *args, PyObject *kwds) if (len == -1) return -1; if (len > 1) { - const char *msg = "expected at most 1 arguments, got %zd"; + const char *msg = "expected at most 1 argument, got %zd"; PyErr_Format(PyExc_TypeError, msg, len); return -1; } @@ -1588,10 +1618,11 @@ PyODict_New(void) } static int -_PyODict_SetItem_KnownHash(PyObject *od, PyObject *key, PyObject *value, - Py_hash_t hash) +_PyODict_SetItem_KnownHash_LockHeld(PyObject *od, PyObject *key, PyObject *value, + Py_hash_t hash) { - int res = _PyDict_SetItem_KnownHash(od, key, value, hash); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); + int res = _PyDict_SetItem_KnownHash_LockHeld((PyDictObject *)od, key, value, hash); if (res == 0) { res = _odict_add_new_node(_PyODictObject_CAST(od), key, hash); if (res < 0) { @@ -1604,18 +1635,32 @@ _PyODict_SetItem_KnownHash(PyObject *od, PyObject *key, PyObject *value, return res; } -int -PyODict_SetItem(PyObject *od, PyObject *key, PyObject *value) + +static int +PyODict_SetItem_LockHeld(PyObject *od, PyObject *key, PyObject *value) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); Py_hash_t hash = PyObject_Hash(key); - if (hash == -1) + if (hash == -1) { return -1; - return _PyODict_SetItem_KnownHash(od, key, value, hash); + } + return _PyODict_SetItem_KnownHash_LockHeld(od, key, value, hash); } int -PyODict_DelItem(PyObject *od, PyObject *key) +PyODict_SetItem(PyObject *od, PyObject *key, PyObject *value) { + int res; + Py_BEGIN_CRITICAL_SECTION(od); + res = PyODict_SetItem_LockHeld(od, key, value); + Py_END_CRITICAL_SECTION(); + return res; +} + +int +PyODict_DelItem_LockHeld(PyObject *od, PyObject *key) +{ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(od); int res; Py_hash_t hash = PyObject_Hash(key); if (hash == -1) @@ -1623,9 +1668,18 @@ PyODict_DelItem(PyObject *od, PyObject *key) res = _odict_clear_node(_PyODictObject_CAST(od), NULL, key, hash); if (res < 0) return -1; - return _PyDict_DelItem_KnownHash(od, key, hash); + return _PyDict_DelItem_KnownHash_LockHeld(od, key, hash); } +int +PyODict_DelItem(PyObject *od, PyObject *key) +{ + int res; + Py_BEGIN_CRITICAL_SECTION(od); + res = PyODict_DelItem_LockHeld(od, key); + Py_END_CRITICAL_SECTION(); + return res; +} /* ------------------------------------------- * The OrderedDict views (keys/values/items) @@ -1667,14 +1721,14 @@ odictiter_traverse(PyObject *op, visitproc visit, void *arg) /* In order to protect against modifications during iteration, we track * the current key instead of the current node. */ static PyObject * -odictiter_nextkey(odictiterobject *di) +odictiter_nextkey_lock_held(odictiterobject *di) { + assert(di->di_odict != NULL); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(di->di_odict); PyObject *key = NULL; _ODictNode *node; int reversed = di->kind & _odict_ITER_REVERSED; - if (di->di_odict == NULL) - return NULL; if (di->di_current == NULL) goto done; /* We're already done. */ @@ -1719,8 +1773,23 @@ odictiter_nextkey(odictiterobject *di) return key; } + static PyObject * -odictiter_iternext(PyObject *op) +odictiter_nextkey(odictiterobject *di) +{ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(di); + if (di->di_odict == NULL) { + return NULL; + } + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(di->di_odict); + res = odictiter_nextkey_lock_held(di); + Py_END_CRITICAL_SECTION(); + return res; +} + +static PyObject * +odictiter_iternext_lock_held(PyObject *op) { odictiterobject *di = (odictiterobject*)op; PyObject *result, *value; @@ -1734,14 +1803,12 @@ odictiter_iternext(PyObject *op) return key; } - value = PyODict_GetItem((PyObject *)di->di_odict, key); /* borrowed */ - if (value == NULL) { + if (PyDict_GetItemRef((PyObject *)di->di_odict, key, &value) != 1) { if (!PyErr_Occurred()) PyErr_SetObject(PyExc_KeyError, key); Py_DECREF(key); - goto done; + goto error; } - Py_INCREF(value); /* Handle the values case. */ if (!(di->kind & _odict_ITER_KEYS)) { @@ -1752,7 +1819,7 @@ odictiter_iternext(PyObject *op) /* Handle the items case. */ result = di->di_result; - if (Py_REFCNT(result) == 1) { + if (_PyObject_IsUniquelyReferenced(result)) { /* not in use so we can reuse it * (the common case during iteration) */ Py_INCREF(result); @@ -1761,26 +1828,35 @@ odictiter_iternext(PyObject *op) // bpo-42536: The GC may have untracked this result tuple. Since we're // recycling it, make sure it's tracked again: _PyTuple_Recycle(result); + PyTuple_SET_ITEM(result, 0, key); /* steals reference */ + PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } else { - result = PyTuple_New(2); + result = _PyTuple_FromPairSteal(key, value); if (result == NULL) { - Py_DECREF(key); - Py_DECREF(value); - goto done; + goto error; } } - PyTuple_SET_ITEM(result, 0, key); /* steals reference */ - PyTuple_SET_ITEM(result, 1, value); /* steals reference */ return result; -done: +error: Py_CLEAR(di->di_current); Py_CLEAR(di->di_odict); return NULL; } +static PyObject * +odictiter_iternext(PyObject *op) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(op); + res = odictiter_iternext_lock_held(op); + Py_END_CRITICAL_SECTION(); + return res; +} + + /* No need for tp_clear because odictiterobject is not mutable. */ PyDoc_STRVAR(reduce_doc, "Return state information for pickling"); @@ -1855,7 +1931,7 @@ odictiter_new(PyODictObject *od, int kind) return NULL; if ((kind & _odict_ITER_ITEMS) == _odict_ITER_ITEMS) { - di->di_result = PyTuple_Pack(2, Py_None, Py_None); + di->di_result = _PyTuple_FromPairSteal(Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; @@ -2193,7 +2269,7 @@ static int mutablemapping_update_arg(PyObject *self, PyObject *arg) { int res = 0; - if (PyDict_CheckExact(arg)) { + if (PyAnyDict_CheckExact(arg)) { PyObject *items = PyDict_Items(arg); if (items == NULL) { return -1; diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index f8cdfe68a6435e..55b7f108730728 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -9,6 +9,21 @@ #include "pycore_range.h" #include "pycore_tuple.h" // _PyTuple_ITEMS() +typedef struct { + PyObject_HEAD + PyObject *start; + PyObject *step; + PyObject *len; +} longrangeiterobject; + +/*[clinic input] +class range_iterator "_PyRangeIterObject *" "&PyRangeIter_Type" +class longrange_iterator "longrangeiterobject *" "&PyLongRangeIter_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c7d97a63d1cfa6b3]*/ + +#include "clinic/rangeobject.c.h" + /* Support objects whose length is > PY_SSIZE_T_MAX. @@ -830,30 +845,46 @@ PyTypeObject PyRange_Type = { static PyObject * rangeiter_next(PyObject *op) { + PyObject *ret = NULL; + Py_BEGIN_CRITICAL_SECTION(op); _PyRangeIterObject *r = (_PyRangeIterObject*)op; if (r->len > 0) { long result = r->start; r->start = result + r->step; r->len--; - return PyLong_FromLong(result); + ret = PyLong_FromLong(result); } - return NULL; + Py_END_CRITICAL_SECTION(); + return ret; } +/*[clinic input] +@critical_section +range_iterator.__length_hint__ + self as r: self(type="_PyRangeIterObject *") + +Private method returning an estimate of len(list(it)). +[clinic start generated code]*/ + static PyObject * -rangeiter_len(PyObject *op, PyObject *Py_UNUSED(ignored)) +range_iterator___length_hint___impl(_PyRangeIterObject *r) +/*[clinic end generated code: output=9ba6f22b1fc23dcc input=e3eb311e99d76e43]*/ { - _PyRangeIterObject *r = (_PyRangeIterObject*)op; return PyLong_FromLong(r->len); } -PyDoc_STRVAR(length_hint_doc, - "Private method returning an estimate of len(list(it))."); +/*[clinic input] +@critical_section +range_iterator.__reduce__ + self as r: self(type="_PyRangeIterObject *") + +Return state information for pickling. +[clinic start generated code]*/ static PyObject * -rangeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) +range_iterator___reduce___impl(_PyRangeIterObject *r) +/*[clinic end generated code: output=c44d53750c388415 input=75a25b7076dc2c54]*/ { - _PyRangeIterObject *r = (_PyRangeIterObject*)op; PyObject *start=NULL, *stop=NULL, *step=NULL; PyObject *range; @@ -881,10 +912,20 @@ rangeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) return NULL; } +/*[clinic input] +@critical_section +range_iterator.__setstate__ + self as r: self(type="_PyRangeIterObject *") + state: object + / + +Set state information for unpickling. +[clinic start generated code]*/ + static PyObject * -rangeiter_setstate(PyObject *op, PyObject *state) +range_iterator___setstate___impl(_PyRangeIterObject *r, PyObject *state) +/*[clinic end generated code: output=464b3cbafc2e3562 input=c8c84fab2519d200]*/ { - _PyRangeIterObject *r = (_PyRangeIterObject*)op; long index = PyLong_AsLong(state); if (index == -1 && PyErr_Occurred()) return NULL; @@ -904,13 +945,10 @@ rangeiter_dealloc(PyObject *self) _Py_FREELIST_FREE(range_iters, (_PyRangeIterObject *)self, PyObject_Free); } -PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); -PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); - static PyMethodDef rangeiter_methods[] = { - {"__length_hint__", rangeiter_len, METH_NOARGS, length_hint_doc}, - {"__reduce__", rangeiter_reduce, METH_NOARGS, reduce_doc}, - {"__setstate__", rangeiter_setstate, METH_O, setstate_doc}, + RANGE_ITERATOR___LENGTH_HINT___METHODDEF + RANGE_ITERATOR___REDUCE___METHODDEF + RANGE_ITERATOR___SETSTATE___METHODDEF {NULL, NULL} /* sentinel */ }; @@ -995,25 +1033,34 @@ fast_range_iter(long start, long stop, long step, long len) return (PyObject *)it; } -typedef struct { - PyObject_HEAD - PyObject *start; - PyObject *step; - PyObject *len; -} longrangeiterobject; +/*[clinic input] +@critical_section +longrange_iterator.__length_hint__ + self as r: self(type="longrangeiterobject *") + +Private method returning an estimate of len(list(it)). +[clinic start generated code]*/ static PyObject * -longrangeiter_len(PyObject *op, PyObject *Py_UNUSED(ignored)) +longrange_iterator___length_hint___impl(longrangeiterobject *r) +/*[clinic end generated code: output=e1bce24da7e8bfde input=ba94b050d940411e]*/ { - longrangeiterobject *r = (longrangeiterobject*)op; Py_INCREF(r->len); return r->len; } +/*[clinic input] +@critical_section +longrange_iterator.__reduce__ + self as r: self(type="longrangeiterobject *") + +Return state information for pickling. +[clinic start generated code]*/ + static PyObject * -longrangeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) +longrange_iterator___reduce___impl(longrangeiterobject *r) +/*[clinic end generated code: output=0077f94ae2a4e99a input=2e8930e897ace086]*/ { - longrangeiterobject *r = (longrangeiterobject*)op; PyObject *product, *stop=NULL; PyObject *range; @@ -1039,10 +1086,25 @@ longrangeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) range, Py_None); } +/*[clinic input] +@critical_section +longrange_iterator.__setstate__ + self as r: self(type="longrangeiterobject *") + state: object + / + +Set state information for unpickling. +[clinic start generated code]*/ + static PyObject * -longrangeiter_setstate(PyObject *op, PyObject *state) +longrange_iterator___setstate___impl(longrangeiterobject *r, PyObject *state) +/*[clinic end generated code: output=870787f0574f0da4 input=8b116de3018de824]*/ { - longrangeiterobject *r = (longrangeiterobject*)op; + if (!PyLong_CheckExact(state)) { + PyErr_Format(PyExc_TypeError, "state must be an int, not %T", state); + return NULL; + } + PyObject *zero = _PyLong_GetZero(); // borrowed reference int cmp; @@ -1080,9 +1142,9 @@ longrangeiter_setstate(PyObject *op, PyObject *state) } static PyMethodDef longrangeiter_methods[] = { - {"__length_hint__", longrangeiter_len, METH_NOARGS, length_hint_doc}, - {"__reduce__", longrangeiter_reduce, METH_NOARGS, reduce_doc}, - {"__setstate__", longrangeiter_setstate, METH_O, setstate_doc}, + LONGRANGE_ITERATOR___LENGTH_HINT___METHODDEF + LONGRANGE_ITERATOR___REDUCE___METHODDEF + LONGRANGE_ITERATOR___SETSTATE___METHODDEF {NULL, NULL} /* sentinel */ }; @@ -1097,7 +1159,7 @@ longrangeiter_dealloc(PyObject *op) } static PyObject * -longrangeiter_next(PyObject *op) +longrangeiter_next_lock_held(PyObject *op) { longrangeiterobject *r = (longrangeiterobject*)op; if (PyObject_RichCompareBool(r->len, _PyLong_GetZero(), Py_GT) != 1) @@ -1118,6 +1180,16 @@ longrangeiter_next(PyObject *op) return result; } +static PyObject * +longrangeiter_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = longrangeiter_next_lock_held(op); + Py_END_CRITICAL_SECTION(); + return result; +} + PyTypeObject PyLongRangeIter_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "longrange_iterator", /* tp_name */ diff --git a/Objects/sentinelobject.c b/Objects/sentinelobject.c new file mode 100644 index 00000000000000..77bffbc397be58 --- /dev/null +++ b/Objects/sentinelobject.c @@ -0,0 +1,222 @@ +/* Sentinel object implementation */ + +#include "Python.h" +#include "descrobject.h" // PyMemberDef +#include "pycore_ceval.h" // _PyThreadState_GET() +#include "pycore_interpframe.h" // _PyFrame_IsIncomplete() +#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK() +#include "pycore_stackref.h" // PyStackRef_AsPyObjectBorrow() +#include "pycore_tuple.h" // _PyTuple_FromPair +#include "pycore_typeobject.h" // _Py_BaseObject_RichCompare() +#include "pycore_unionobject.h" // _Py_union_type_or() + +typedef struct { + PyObject_HEAD + PyObject *name; + PyObject *module; + PyObject *repr; +} sentinelobject; + +#define sentinelobject_CAST(op) \ + (assert(PySentinel_Check(op)), _Py_CAST(sentinelobject *, (op))) + +/*[clinic input] +class sentinel "sentinelobject *" "&PySentinel_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=8b88f8268d3b5775]*/ + +#include "clinic/sentinelobject.c.h" + + +static PyObject * +caller(void) +{ + _PyInterpreterFrame *f = _PyThreadState_GET()->current_frame; + if (f == NULL || PyStackRef_IsNull(f->f_funcobj)) { + assert(!PyErr_Occurred()); + Py_RETURN_NONE; + } + PyFunctionObject *func = _PyFrame_GetFunction(f); + assert(PyFunction_Check(func)); + PyObject *r = PyFunction_GetModule((PyObject *)func); + if (!r) { + assert(!PyErr_Occurred()); + Py_RETURN_NONE; + } + return Py_NewRef(r); +} + +static PyObject * +sentinel_new_with_module(PyTypeObject *type, PyObject *name, PyObject *module, PyObject *repr) +{ + assert(PyUnicode_Check(name)); + + sentinelobject *self = PyObject_GC_New(sentinelobject, type); + if (self == NULL) { + return NULL; + } + self->name = Py_NewRef(name); + self->module = Py_NewRef(module); + self->repr = Py_XNewRef(repr); + _PyObject_GC_TRACK(self); + return (PyObject *)self; +} + +/*[clinic input] +@classmethod +sentinel.__new__ as sentinel_new + + name: object(subclass_of='&PyUnicode_Type') + / + * + repr: object = None +[clinic start generated code]*/ + +static PyObject * +sentinel_new_impl(PyTypeObject *type, PyObject *name, PyObject *repr) +/*[clinic end generated code: output=1eb7fab52e57d8c8 input=28cab6c468997b35]*/ +{ + if (repr == Py_None) { + repr = NULL; + } + else if (!PyUnicode_Check(repr)) { + _PyArg_BadArgument("sentinel", "argument 'repr'", "str or None", repr); + return NULL; + } + PyObject *module = caller(); + PyObject *self = sentinel_new_with_module(type, name, module, repr); + Py_DECREF(module); + return self; +} + +PyObject * +PySentinel_New(const char *name, const char *module_name, const char *repr) +{ + PyObject *name_obj = PyUnicode_FromString(name); + if (name_obj == NULL) { + return NULL; + } + PyObject *repr_obj = NULL; + if (repr != NULL) { + repr_obj = PyUnicode_FromString(repr); + if (repr_obj == NULL) { + Py_DECREF(name_obj); + return NULL; + } + } + PyObject *module_obj = module_name == NULL + ? Py_None + : PyUnicode_FromString(module_name); + if (module_obj == NULL) { + Py_DECREF(name_obj); + Py_XDECREF(repr_obj); + return NULL; + } + + PyObject *sentinel = sentinel_new_with_module( + &PySentinel_Type, name_obj, module_obj, repr_obj); + Py_DECREF(module_obj); + Py_DECREF(name_obj); + Py_XDECREF(repr_obj); + return sentinel; +} + +static int +sentinel_clear(PyObject *op) +{ + sentinelobject *self = sentinelobject_CAST(op); + Py_CLEAR(self->name); + Py_CLEAR(self->module); + Py_CLEAR(self->repr); + return 0; +} + +static void +sentinel_dealloc(PyObject *op) +{ + _PyObject_GC_UNTRACK(op); + (void)sentinel_clear(op); + Py_TYPE(op)->tp_free(op); +} + +static int +sentinel_traverse(PyObject *op, visitproc visit, void *arg) +{ + sentinelobject *self = sentinelobject_CAST(op); + Py_VISIT(self->name); + Py_VISIT(self->module); + Py_VISIT(self->repr); + return 0; +} + +static PyObject * +sentinel_repr(PyObject *op) +{ + sentinelobject *self = sentinelobject_CAST(op); + if (self->repr != NULL) { + return Py_NewRef(self->repr); + } + return Py_NewRef(self->name); +} + +static PyObject * +sentinel_copy(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return Py_NewRef(self); +} + +static PyObject * +sentinel_deepcopy(PyObject *self, PyObject *Py_UNUSED(memo)) +{ + return Py_NewRef(self); +} + +static PyObject * +sentinel_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) +{ + sentinelobject *self = sentinelobject_CAST(op); + return Py_NewRef(self->name); +} + +static PyMethodDef sentinel_methods[] = { + {"__copy__", sentinel_copy, METH_NOARGS, NULL}, + {"__deepcopy__", sentinel_deepcopy, METH_O, NULL}, + {"__reduce__", sentinel_reduce, METH_NOARGS, NULL}, + {NULL, NULL} +}; + +static PyMemberDef sentinel_members[] = { + {"__name__", Py_T_OBJECT_EX, offsetof(sentinelobject, name), Py_READONLY}, + {"__module__", Py_T_OBJECT_EX, offsetof(sentinelobject, module), 0}, + {NULL} +}; + +static PyNumberMethods sentinel_as_number = { + .nb_or = _Py_union_type_or, +}; + +PyDoc_STRVAR(sentinel_doc, +"sentinel(name, /, *, repr=None)\n" +"--\n\n" +"Create a unique sentinel object with the given name."); + +PyTypeObject PySentinel_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "sentinel", + .tp_basicsize = sizeof(sentinelobject), + .tp_dealloc = sentinel_dealloc, + .tp_repr = sentinel_repr, + .tp_as_number = &sentinel_as_number, + .tp_hash = PyObject_GenericHash, + .tp_getattro = PyObject_GenericGetAttr, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE + | Py_TPFLAGS_HAVE_GC, + .tp_doc = sentinel_doc, + .tp_traverse = sentinel_traverse, + .tp_clear = sentinel_clear, + .tp_richcompare = _Py_BaseObject_RichCompare, + .tp_methods = sentinel_methods, + .tp_members = sentinel_members, + .tp_new = sentinel_new, + .tp_free = PyObject_GC_Del, +}; diff --git a/Objects/setobject.c b/Objects/setobject.c index 6e4fc5957cad7f..50c5f66c2afc1b 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -63,6 +63,148 @@ static PyObject _dummy_struct; #define dummy (&_dummy_struct) +#define SET_LOOKKEY_FOUND 1 +#define SET_LOOKKEY_NO_MATCH 0 +#define SET_LOOKKEY_ERROR (-1) +#define SET_LOOKKEY_CHANGED (-2) +#define SET_LOOKKEY_EMPTY (-3) + +typedef int (*compare_func)(PySetObject *so, setentry *table, setentry *ep, + PyObject *key, Py_hash_t hash); + +#ifdef Py_GIL_DISABLED + +#define SET_IS_SHARED(so) _PyObject_GC_IS_SHARED(so) +#define SET_MARK_SHARED(so) _PyObject_GC_SET_SHARED(so) + +static void +ensure_shared_on_read(PySetObject *so) +{ + if (!_Py_IsOwnedByCurrentThread((PyObject *)so) && !SET_IS_SHARED(so)) { + // The first time we access a set from a non-owning thread we mark it + // as shared. This ensures that a concurrent resize operation will + // delay freeing the old entries using QSBR, which is necessary + // to safely allow concurrent reads without locking... + Py_BEGIN_CRITICAL_SECTION(so); + if (!SET_IS_SHARED(so)) { + SET_MARK_SHARED(so); + } + Py_END_CRITICAL_SECTION(); + } +} + +static inline Py_ALWAYS_INLINE int +set_compare_threadsafe(PySetObject *so, setentry *table, setentry *ep, + PyObject *key, Py_hash_t hash) +{ + PyObject *startkey = FT_ATOMIC_LOAD_PTR_ACQUIRE(ep->key); + if (startkey == NULL) { + return SET_LOOKKEY_EMPTY; + } + if (startkey == key) { + return SET_LOOKKEY_FOUND; + } + Py_ssize_t ep_hash = FT_ATOMIC_LOAD_SSIZE_ACQUIRE(ep->hash); + if (ep_hash == hash) { + if (!_Py_TryIncrefCompare(&ep->key, startkey)) { + return SET_LOOKKEY_CHANGED; + } + int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + Py_DECREF(startkey); + if (cmp < 0) { + return SET_LOOKKEY_ERROR; + } + if (table == FT_ATOMIC_LOAD_PTR_ACQUIRE(so->table) && + startkey == FT_ATOMIC_LOAD_PTR_ACQUIRE(ep->key)) { + assert(cmp == SET_LOOKKEY_FOUND || cmp == SET_LOOKKEY_NO_MATCH); + return cmp; + } + else { + /* The set was mutated, restart */ + return SET_LOOKKEY_CHANGED; + } + } + return SET_LOOKKEY_NO_MATCH; +} + +#else + +#define SET_IS_SHARED(so) 0 +#define SET_MARK_SHARED(so) + +#endif + +static inline Py_ALWAYS_INLINE int +set_compare_entry_lock_held(PySetObject *so, setentry *table, setentry *entry, + PyObject *key, Py_hash_t hash) +{ + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so); + if (entry->hash == 0 && entry->key == NULL) + return SET_LOOKKEY_EMPTY; + if (entry->hash == hash) { + PyObject *startkey = entry->key; + assert(startkey != dummy); + if (startkey == key) + return SET_LOOKKEY_FOUND; + if (PyUnicode_CheckExact(startkey) + && PyUnicode_CheckExact(key) + && unicode_eq(startkey, key)) + return SET_LOOKKEY_FOUND; + table = so->table; + Py_INCREF(startkey); + int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + Py_DECREF(startkey); + if (cmp < 0) + return SET_LOOKKEY_ERROR; + if (table != so->table || entry->key != startkey) + return SET_LOOKKEY_CHANGED; + if (cmp > 0) + return SET_LOOKKEY_FOUND; + } + return SET_LOOKKEY_NO_MATCH; +} + +// This is similar to set_compare_entry_lock_held() but we don't need to +// incref startkey before comparing and we don't need to check if the set has +// changed. This also omits the PyUnicode_CheckExact() special case since it +// doesn't help much for frozensets. +static inline Py_ALWAYS_INLINE int +set_compare_frozenset(PySetObject *so, setentry *table, setentry *ep, + PyObject *key, Py_hash_t hash) +{ + assert(PyFrozenSet_Check(so)); + PyObject *startkey = ep->key; + if (startkey == NULL) { + return SET_LOOKKEY_EMPTY; + } + if (startkey == key) { + return SET_LOOKKEY_FOUND; + } + Py_ssize_t ep_hash = ep->hash; + if (ep_hash == hash) { + int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + if (cmp < 0) { + return SET_LOOKKEY_ERROR; + } + assert(cmp == SET_LOOKKEY_FOUND || cmp == SET_LOOKKEY_NO_MATCH); + return cmp; + } + return SET_LOOKKEY_NO_MATCH; +} + +static void +set_zero_table(setentry *table, size_t size) +{ +#ifdef Py_GIL_DISABLED + for (size_t i = 0; i < size; i++) { + setentry *entry = &table[i]; + FT_ATOMIC_STORE_SSIZE_RELAXED(entry->hash, 0); + FT_ATOMIC_STORE_PTR_RELEASE(entry->key, NULL); + } +#else + memset(table, 0, sizeof(setentry)*size); +#endif +} /* ======================================================================== */ /* ======= Begin logic for probing the hash table ========================= */ @@ -75,49 +217,34 @@ static PyObject _dummy_struct; /* This must be >= 1 */ #define PERTURB_SHIFT 5 -static setentry * -set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash) +static int +set_do_lookup(PySetObject *so, setentry *table, size_t mask, PyObject *key, + Py_hash_t hash, setentry **epp, compare_func compare_entry) { - setentry *table; setentry *entry; size_t perturb = hash; - size_t mask = so->mask; size_t i = (size_t)hash & mask; /* Unsigned for defined overflow behavior */ int probes; - int cmp; + int status; while (1) { - entry = &so->table[i]; + entry = &table[i]; probes = (i + LINEAR_PROBES <= mask) ? LINEAR_PROBES: 0; do { - if (entry->hash == 0 && entry->key == NULL) - return entry; - if (entry->hash == hash) { - PyObject *startkey = entry->key; - assert(startkey != dummy); - if (startkey == key) - return entry; - if (PyUnicode_CheckExact(startkey) - && PyUnicode_CheckExact(key) - && unicode_eq(startkey, key)) - return entry; - table = so->table; - Py_INCREF(startkey); - cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); - Py_DECREF(startkey); - if (cmp < 0) - return NULL; - if (table != so->table || entry->key != startkey) - return set_lookkey(so, key, hash); - if (cmp > 0) - return entry; - mask = so->mask; + status = compare_entry(so, table, entry, key, hash); + if (status != SET_LOOKKEY_NO_MATCH) { + if (status == SET_LOOKKEY_EMPTY) { + return SET_LOOKKEY_NO_MATCH; + } + *epp = entry; + return status; } entry++; } while (probes--); perturb >>= PERTURB_SHIFT; i = (i * 5 + 1 + perturb) & mask; } + Py_UNREACHABLE(); } static int set_table_resize(PySetObject *, Py_ssize_t); @@ -181,16 +308,19 @@ set_add_entry_takeref(PySetObject *so, PyObject *key, Py_hash_t hash) found_unused_or_dummy: if (freeslot == NULL) goto found_unused; + if (freeslot->hash != -1) { + goto restart; + } FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1); - freeslot->key = key; - freeslot->hash = hash; + FT_ATOMIC_STORE_SSIZE_RELAXED(freeslot->hash, hash); + FT_ATOMIC_STORE_PTR_RELEASE(freeslot->key, key); return 0; found_unused: so->fill++; FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1); - entry->key = key; - entry->hash = hash; + FT_ATOMIC_STORE_SSIZE_RELAXED(entry->hash, hash); + FT_ATOMIC_STORE_PTR_RELEASE(entry->key, key); if ((size_t)so->fill*5 < mask*3) return 0; return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4); @@ -273,13 +403,78 @@ set_insert_clean(setentry *table, size_t mask, PyObject *key, Py_hash_t hash) i = (i * 5 + 1 + perturb) & mask; } found_null: - entry->key = key; - entry->hash = hash; + FT_ATOMIC_STORE_SSIZE_RELAXED(entry->hash, hash); + FT_ATOMIC_STORE_PTR_RELEASE(entry->key, key); } /* ======== End logic for probing the hash table ========================== */ /* ======================================================================== */ +static int +set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash, setentry **epp) +{ + int status; + if (PyFrozenSet_CheckExact(so)) { + status = set_do_lookup(so, so->table, so->mask, key, hash, epp, + set_compare_frozenset); + } + else { + Py_BEGIN_CRITICAL_SECTION(so); + do { + status = set_do_lookup(so, so->table, so->mask, key, hash, epp, + set_compare_entry_lock_held); + } while (status == SET_LOOKKEY_CHANGED); + Py_END_CRITICAL_SECTION(); + } + assert(status == SET_LOOKKEY_FOUND || + status == SET_LOOKKEY_NO_MATCH || + status == SET_LOOKKEY_ERROR); + return status; +} + +#ifdef Py_GIL_DISABLED +static int +set_lookkey_threadsafe(PySetObject *so, PyObject *key, Py_hash_t hash) +{ + int status; + setentry *entry; + if (PyFrozenSet_CheckExact(so)) { + status = set_do_lookup(so, so->table, so->mask, key, hash, &entry, + set_compare_frozenset); + assert(status == SET_LOOKKEY_FOUND || + status == SET_LOOKKEY_NO_MATCH || + status == SET_LOOKKEY_ERROR); + return status; + } + ensure_shared_on_read(so); + setentry *table = FT_ATOMIC_LOAD_PTR_ACQUIRE(so->table); + size_t mask = FT_ATOMIC_LOAD_SSIZE_ACQUIRE(so->mask); + if (table == NULL || table != FT_ATOMIC_LOAD_PTR_ACQUIRE(so->table)) { + return set_lookkey(so, key, hash, &entry); + } + status = set_do_lookup(so, table, mask, key, hash, &entry, + set_compare_threadsafe); + if (status == SET_LOOKKEY_CHANGED) { + return set_lookkey(so, key, hash, &entry); + } + assert(status == SET_LOOKKEY_FOUND || + status == SET_LOOKKEY_NO_MATCH || + status == SET_LOOKKEY_ERROR); + return status; +} +#endif + +static void free_entries(setentry *entries, size_t size, bool use_qsbr) +{ +#ifdef Py_GIL_DISABLED + if (use_qsbr) { + _PyMem_FreeDelayed(entries, size * sizeof(setentry)); + return; + } +#endif + PyMem_Free(entries); +} + /* Restructure the table by allocating a new table and reinserting all keys again. When entries have been deleted, the new table may @@ -290,6 +485,7 @@ set_table_resize(PySetObject *so, Py_ssize_t minused) { setentry *oldtable, *newtable, *entry; Py_ssize_t oldmask = so->mask; + Py_ssize_t oldsize = (size_t)oldmask + 1; size_t newmask; int is_oldtable_malloced; setentry small_copy[PySet_MINSIZE]; @@ -337,9 +533,9 @@ set_table_resize(PySetObject *so, Py_ssize_t minused) /* Make the set empty, using the new table. */ assert(newtable != oldtable); - memset(newtable, 0, sizeof(setentry) * newsize); - so->mask = newsize - 1; - so->table = newtable; + set_zero_table(newtable, newsize); + FT_ATOMIC_STORE_PTR_RELEASE(so->table, NULL); + FT_ATOMIC_STORE_SSIZE_RELEASE(so->mask, newsize - 1); /* Copy the data over; this is refcount-neutral for active entries; dummy entries aren't copied over, of course */ @@ -359,20 +555,22 @@ set_table_resize(PySetObject *so, Py_ssize_t minused) } } + FT_ATOMIC_STORE_PTR_RELEASE(so->table, newtable); + if (is_oldtable_malloced) - PyMem_Free(oldtable); + free_entries(oldtable, oldsize, SET_IS_SHARED(so)); return 0; } static int set_contains_entry(PySetObject *so, PyObject *key, Py_hash_t hash) { - setentry *entry; - - entry = set_lookkey(so, key, hash); - if (entry != NULL) - return entry->key != NULL; - return -1; +#ifdef Py_GIL_DISABLED + return set_lookkey_threadsafe(so, key, hash); +#else + setentry *entry; // unused + return set_lookkey(so, key, hash, &entry); +#endif } #define DISCARD_NOTFOUND 0 @@ -383,16 +581,18 @@ set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash) { setentry *entry; PyObject *old_key; - - entry = set_lookkey(so, key, hash); - if (entry == NULL) + int status = set_lookkey(so, key, hash, &entry); + if (status < 0) { return -1; - if (entry->key == NULL) + } + if (status == SET_LOOKKEY_NO_MATCH) { return DISCARD_NOTFOUND; + } + assert(status == SET_LOOKKEY_FOUND); old_key = entry->key; - entry->key = dummy; - entry->hash = -1; + FT_ATOMIC_STORE_SSIZE_RELAXED(entry->hash, -1); FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used - 1); + FT_ATOMIC_STORE_PTR_RELEASE(entry->key, dummy); Py_DECREF(old_key); return DISCARD_FOUND; } @@ -433,12 +633,13 @@ set_discard_key(PySetObject *so, PyObject *key) static void set_empty_to_minsize(PySetObject *so) { - memset(so->smalltable, 0, sizeof(so->smalltable)); + FT_ATOMIC_STORE_PTR_RELEASE(so->table, NULL); + set_zero_table(so->smalltable, PySet_MINSIZE); so->fill = 0; FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, 0); - so->mask = PySet_MINSIZE - 1; - so->table = so->smalltable; + FT_ATOMIC_STORE_SSIZE_RELEASE(so->mask, PySet_MINSIZE - 1); FT_ATOMIC_STORE_SSIZE_RELAXED(so->hash, -1); + FT_ATOMIC_STORE_PTR_RELEASE(so->table, so->smalltable); } static int @@ -449,6 +650,7 @@ set_clear_internal(PyObject *self) setentry *table = so->table; Py_ssize_t fill = so->fill; Py_ssize_t used = so->used; + Py_ssize_t oldsize = (size_t)so->mask + 1; int table_is_malloced = table != so->smalltable; setentry small_copy[PySet_MINSIZE]; @@ -487,7 +689,7 @@ set_clear_internal(PyObject *self) } if (table_is_malloced) - PyMem_Free(table); + free_entries(table, oldsize, SET_IS_SHARED(so)); return 0; } @@ -534,6 +736,7 @@ set_dealloc(PyObject *self) PySetObject *so = _PySet_CAST(self); setentry *entry; Py_ssize_t used = so->used; + Py_ssize_t oldsize = (size_t)so->mask + 1; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(so); @@ -546,7 +749,7 @@ set_dealloc(PyObject *self) } } if (so->table != so->smalltable) - PyMem_Free(so->table); + free_entries(so->table, oldsize, SET_IS_SHARED(so)); Py_TYPE(so)->tp_free(so); } @@ -657,8 +860,8 @@ set_merge_lock_held(PySetObject *so, PyObject *otherset) key = other_entry->key; if (key != NULL) { assert(so_entry->key == NULL); - so_entry->key = Py_NewRef(key); - so_entry->hash = other_entry->hash; + FT_ATOMIC_STORE_SSIZE_RELAXED(so_entry->hash, other_entry->hash); + FT_ATOMIC_STORE_PTR_RELEASE(so_entry->key, Py_NewRef(key)); } } so->fill = other->fill; @@ -722,10 +925,10 @@ set_pop_impl(PySetObject *so) if (entry > limit) entry = so->table; } - key = entry->key; - entry->key = dummy; - entry->hash = -1; + FT_ATOMIC_STORE_SSIZE_RELAXED(entry->hash, -1); FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used - 1); + key = entry->key; + FT_ATOMIC_STORE_PTR_RELEASE(entry->key, dummy); so->finger = entry - so->table + 1; /* next place to start */ return key; } @@ -761,7 +964,10 @@ _shuffle_bits(Py_uhash_t h) This hash algorithm can be used on either a frozenset or a set. When it is used on a set, it computes the hash value of the equivalent - frozenset without creating a new frozenset object. */ + frozenset without creating a new frozenset object. + + If you update this code, update also frozendict_hash() which copied this + code. */ static Py_hash_t frozenset_hash_impl(PyObject *self) @@ -812,11 +1018,11 @@ frozenset_hash(PyObject *self) Py_uhash_t hash; if (FT_ATOMIC_LOAD_SSIZE_RELAXED(so->hash) != -1) { - return FT_ATOMIC_LOAD_SSIZE_RELAXED(so->hash); + return FT_ATOMIC_LOAD_SSIZE_ACQUIRE(so->hash); } hash = frozenset_hash_impl(self); - FT_ATOMIC_STORE_SSIZE_RELAXED(so->hash, hash); + FT_ATOMIC_STORE_SSIZE_RELEASE(so->hash, hash); return hash; } @@ -898,8 +1104,8 @@ static PyObject *setiter_iternext(PyObject *self) return NULL; assert (PyAnySet_Check(so)); - Py_ssize_t so_used = FT_ATOMIC_LOAD_SSIZE(so->used); - Py_ssize_t si_used = FT_ATOMIC_LOAD_SSIZE(si->si_used); + Py_ssize_t so_used = FT_ATOMIC_LOAD_SSIZE_RELAXED(so->used); + Py_ssize_t si_used = FT_ATOMIC_LOAD_SSIZE_RELAXED(si->si_used); if (si_used != so_used) { PyErr_SetString(PyExc_RuntimeError, "Set changed size during iteration"); @@ -980,10 +1186,14 @@ set_iter(PyObject *so) static int set_update_dict_lock_held(PySetObject *so, PyObject *other) { - assert(PyDict_CheckExact(other)); + assert(PyAnyDict_CheckExact(other)); _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so); - _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other); +#ifdef Py_DEBUG + if (!PyFrozenDict_CheckExact(other)) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other); + } +#endif /* Do one big resize at the start, rather than * incrementally resizing as we insert new keys. Expect @@ -1039,7 +1249,7 @@ set_update_lock_held(PySetObject *so, PyObject *other) if (PyAnySet_Check(other)) { return set_merge_lock_held(so, other); } - else if (PyDict_CheckExact(other)) { + else if (PyAnyDict_CheckExact(other)) { return set_update_dict_lock_held(so, other); } return set_update_iterable_lock_held(so, other); @@ -1064,6 +1274,9 @@ set_update_local(PySetObject *so, PyObject *other) Py_END_CRITICAL_SECTION(); return rv; } + else if (PyFrozenDict_CheckExact(other)) { + return set_update_dict_lock_held(so, other); + } return set_update_iterable_lock_held(so, other); } @@ -1087,6 +1300,13 @@ set_update_internal(PySetObject *so, PyObject *other) Py_END_CRITICAL_SECTION2(); return rv; } + else if (PyFrozenDict_CheckExact(other)) { + int rv; + Py_BEGIN_CRITICAL_SECTION(so); + rv = set_update_dict_lock_held(so, other); + Py_END_CRITICAL_SECTION(); + return rv; + } else { int rv; Py_BEGIN_CRITICAL_SECTION(so); @@ -1165,6 +1385,26 @@ make_new_set_basetype(PyTypeObject *type, PyObject *iterable) return make_new_set(type, iterable); } +// gh-140232: check whether a frozenset can be untracked from the GC +static void +_PyFrozenSet_MaybeUntrack(PyObject *op) +{ + assert(op != NULL); + // subclasses of a frozenset can generate reference cycles, so do not untrack + if (!PyFrozenSet_CheckExact(op)) { + return; + } + // if no elements of a frozenset are tracked by the GC, we untrack the object + Py_ssize_t pos = 0; + setentry *entry; + while (set_next((PySetObject *)op, &pos, &entry)) { + if (_PyObject_GC_MAY_BE_TRACKED(entry->key)) { + return; + } + } + _PyObject_GC_UNTRACK(op); +} + static PyObject * make_new_frozenset(PyTypeObject *type, PyObject *iterable) { @@ -1176,7 +1416,11 @@ make_new_frozenset(PyTypeObject *type, PyObject *iterable) /* frozenset(f) is idempotent */ return Py_NewRef(iterable); } - return make_new_set(type, iterable); + PyObject *obj = make_new_set(type, iterable); + if (obj != NULL) { + _PyFrozenSet_MaybeUntrack(obj); + } + return obj; } static PyObject * @@ -1220,6 +1464,17 @@ set_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return make_new_set(type, NULL); } +#ifdef Py_GIL_DISABLED +static void +copy_small_table(setentry *dest, setentry *src) +{ + for (Py_ssize_t i = 0; i < PySet_MINSIZE; i++) { + _Py_atomic_store_ptr_release(&dest[i].key, src[i].key); + _Py_atomic_store_ssize_relaxed(&dest[i].hash, src[i].hash); + } +} +#endif + /* set_swap_bodies() switches the contents of any two sets by moving their internal data pointers and, if needed, copying the internal smalltables. Semantically equivalent to: @@ -1239,24 +1494,36 @@ set_swap_bodies(PySetObject *a, PySetObject *b) setentry tab[PySet_MINSIZE]; Py_hash_t h; + setentry *a_table = a->table; + setentry *b_table = b->table; + FT_ATOMIC_STORE_PTR_RELEASE(a->table, NULL); + FT_ATOMIC_STORE_PTR_RELEASE(b->table, NULL); + t = a->fill; a->fill = b->fill; b->fill = t; t = a->used; FT_ATOMIC_STORE_SSIZE_RELAXED(a->used, b->used); FT_ATOMIC_STORE_SSIZE_RELAXED(b->used, t); - t = a->mask; a->mask = b->mask; b->mask = t; + t = a->mask; + FT_ATOMIC_STORE_SSIZE_RELEASE(a->mask, b->mask); + FT_ATOMIC_STORE_SSIZE_RELEASE(b->mask, t); - u = a->table; - if (a->table == a->smalltable) + u = a_table; + if (a_table == a->smalltable) u = b->smalltable; - a->table = b->table; - if (b->table == b->smalltable) - a->table = a->smalltable; - b->table = u; + a_table = b_table; + if (b_table == b->smalltable) + a_table = a->smalltable; + b_table = u; - if (a->table == a->smalltable || b->table == b->smalltable) { + if (a_table == a->smalltable || b_table == b->smalltable) { memcpy(tab, a->smalltable, sizeof(tab)); +#ifndef Py_GIL_DISABLED memcpy(a->smalltable, b->smalltable, sizeof(tab)); memcpy(b->smalltable, tab, sizeof(tab)); +#else + copy_small_table(a->smalltable, b->smalltable); + copy_small_table(b->smalltable, tab); +#endif } if (PyType_IsSubtype(Py_TYPE(a), &PyFrozenSet_Type) && @@ -1268,6 +1535,24 @@ set_swap_bodies(PySetObject *a, PySetObject *b) FT_ATOMIC_STORE_SSIZE_RELAXED(a->hash, -1); FT_ATOMIC_STORE_SSIZE_RELAXED(b->hash, -1); } + if (!SET_IS_SHARED(b) && SET_IS_SHARED(a)) { + SET_MARK_SHARED(b); + } + if (!SET_IS_SHARED(a) && SET_IS_SHARED(b)) { + SET_MARK_SHARED(a); + } + FT_ATOMIC_STORE_PTR_RELEASE(a->table, a_table); + FT_ATOMIC_STORE_PTR_RELEASE(b->table, b_table); +} + +PyObject * +_PySet_Freeze(PyObject *set) +{ + assert(set != NULL); + assert(PySet_CheckExact(set)); + assert(_PyObject_IsUniquelyReferenced(set)); + set->ob_type = &PyFrozenSet_Type; + return Py_NewRef(set); } /*[clinic input] @@ -1772,7 +2057,7 @@ set_difference(PySetObject *so, PyObject *other) if (PyAnySet_Check(other)) { other_size = PySet_GET_SIZE(other); } - else if (PyDict_CheckExact(other)) { + else if (PyAnyDict_CheckExact(other)) { other_size = PyDict_GET_SIZE(other); } else { @@ -1789,7 +2074,7 @@ set_difference(PySetObject *so, PyObject *other) if (result == NULL) return NULL; - if (PyDict_CheckExact(other)) { + if (PyAnyDict_CheckExact(other)) { while (set_next(so, &pos, &entry)) { key = entry->key; hash = entry->hash; @@ -1836,6 +2121,7 @@ set_difference(PySetObject *so, PyObject *other) } /*[clinic input] +@permit_long_summary set.difference as set_difference_multi so: setobject *others: array @@ -1846,7 +2132,7 @@ Return a new set with elements in the set that are not in the others. static PyObject * set_difference_multi_impl(PySetObject *so, PyObject * const *others, Py_ssize_t others_length) -/*[clinic end generated code: output=b0d33fb05d5477a7 input=c1eb448d483416ad]*/ +/*[clinic end generated code: output=b0d33fb05d5477a7 input=e0fbedbf79d91d4e]*/ { Py_ssize_t i; PyObject *result, *other; @@ -1911,7 +2197,11 @@ static int set_symmetric_difference_update_dict(PySetObject *so, PyObject *other) { _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so); - _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other); +#ifdef Py_DEBUG + if (!PyFrozenDict_CheckExact(other)) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other); + } +#endif Py_ssize_t pos = 0; PyObject *key, *value; @@ -1962,6 +2252,7 @@ set_symmetric_difference_update_set(PySetObject *so, PySetObject *other) } /*[clinic input] +@permit_long_summary set.symmetric_difference_update so: setobject other: object @@ -1972,7 +2263,7 @@ Update the set, keeping only elements found in either set, but not in both. static PyObject * set_symmetric_difference_update_impl(PySetObject *so, PyObject *other) -/*[clinic end generated code: output=79f80b4ee5da66c1 input=a50acf0365e1f0a5]*/ +/*[clinic end generated code: output=79f80b4ee5da66c1 input=86a3dddac9bfb15e]*/ { if (Py_Is((PyObject *)so, other)) { return set_clear((PyObject *)so, NULL); @@ -1984,6 +2275,11 @@ set_symmetric_difference_update_impl(PySetObject *so, PyObject *other) rv = set_symmetric_difference_update_dict(so, other); Py_END_CRITICAL_SECTION2(); } + else if (PyFrozenDict_CheckExact(other)) { + Py_BEGIN_CRITICAL_SECTION(so); + rv = set_symmetric_difference_update_dict(so, other); + Py_END_CRITICAL_SECTION(); + } else if (PyAnySet_Check(other)) { Py_BEGIN_CRITICAL_SECTION2(so, other); rv = set_symmetric_difference_update_set(so, (PySetObject *)other); @@ -2008,6 +2304,7 @@ set_symmetric_difference_update_impl(PySetObject *so, PyObject *other) } /*[clinic input] +@permit_long_summary @critical_section so other set.symmetric_difference so: setobject @@ -2019,7 +2316,7 @@ Return a new set with elements in either the set or other but not both. static PyObject * set_symmetric_difference_impl(PySetObject *so, PyObject *other) -/*[clinic end generated code: output=270ee0b5d42b0797 input=624f6e7bbdf70db1]*/ +/*[clinic end generated code: output=270ee0b5d42b0797 input=8c29b0be90d47feb]*/ { PySetObject *result = (PySetObject *)make_new_set_basetype(Py_TYPE(so), NULL); if (result == NULL) { @@ -2212,33 +2509,26 @@ set_add_impl(PySetObject *so, PyObject *key) Py_RETURN_NONE; } -static int -set_contains_lock_held(PySetObject *so, PyObject *key) +int +_PySet_Contains(PySetObject *so, PyObject *key) { - int rv; + assert(so); - rv = set_contains_key(so, key); - if (rv < 0) { - if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) + Py_hash_t hash = _PyObject_HashFast(key); + if (hash == -1) { + if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) { + set_unhashable_type(key); return -1; + } PyErr_Clear(); - Py_hash_t hash; + // Note that 'key' could be a set() or frozenset() object. Unlike most + // container types, set allows membership testing with a set key, even + // though it is not hashable. Py_BEGIN_CRITICAL_SECTION(key); hash = frozenset_hash_impl(key); Py_END_CRITICAL_SECTION(); - rv = set_contains_entry(so, key, hash); } - return rv; -} - -int -_PySet_Contains(PySetObject *so, PyObject *key) -{ - int rv; - Py_BEGIN_CRITICAL_SECTION(so); - rv = set_contains_lock_held(so, key); - Py_END_CRITICAL_SECTION(); - return rv; + return set_contains_entry(so, key, hash); } static int @@ -2249,7 +2539,6 @@ set_contains(PyObject *self, PyObject *key) } /*[clinic input] -@critical_section @coexist set.__contains__ so: setobject @@ -2261,11 +2550,11 @@ x.__contains__(y) <==> y in x. static PyObject * set___contains___impl(PySetObject *so, PyObject *key) -/*[clinic end generated code: output=b44863d034b3c70e input=4a7d568459617f24]*/ +/*[clinic end generated code: output=b44863d034b3c70e input=cf4c72db704e4cf0]*/ { long result; - result = set_contains_lock_held(so, key); + result = _PySet_Contains(so, key); if (result < 0) return NULL; return PyBool_FromLong(result); @@ -2285,12 +2574,23 @@ static PyObject * frozenset___contains___impl(PySetObject *so, PyObject *key) /*[clinic end generated code: output=2301ed91bc3a6dd5 input=2f04922a98d8bab7]*/ { - long result; - - result = set_contains_lock_held(so, key); - if (result < 0) + Py_hash_t hash = _PyObject_HashFast(key); + if (hash == -1) { + if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) { + set_unhashable_type(key); + return NULL; + } + PyErr_Clear(); + Py_BEGIN_CRITICAL_SECTION(key); + hash = frozenset_hash_impl(key); + Py_END_CRITICAL_SECTION(); + } + setentry *entry; // unused + int status = set_do_lookup(so, so->table, so->mask, key, hash, &entry, + set_compare_frozenset); + if (status < 0) return NULL; - return PyBool_FromLong(result); + return PyBool_FromLong(status); } /*[clinic input] @@ -2428,7 +2728,7 @@ set_init(PyObject *so, PyObject *args, PyObject *kwds) if (!PyArg_UnpackTuple(args, Py_TYPE(self)->tp_name, 0, 1, &iterable)) return -1; - if (Py_REFCNT(self) == 1 && self->fill == 0) { + if (_PyObject_IsUniquelyReferenced((PyObject *)self) && self->fill == 0) { self->hash = -1; if (iterable == NULL) { return 0; @@ -2502,7 +2802,8 @@ static PyMethodDef set_methods[] = { SET_SYMMETRIC_DIFFERENCE_UPDATE_METHODDEF SET_UNION_METHODDEF SET_UPDATE_METHODDEF - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("sets are generic over the type of their elements")}, {NULL, NULL} /* sentinel */ }; @@ -2606,7 +2907,8 @@ static PyMethodDef frozenset_methods[] = { SET___SIZEOF___METHODDEF SET_SYMMETRIC_DIFFERENCE_METHODDEF SET_UNION_METHODDEF - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + PyDoc_STR("frozensets are generic over the type of their elements")}, {NULL, NULL} /* sentinel */ }; @@ -2694,7 +2996,11 @@ PySet_New(PyObject *iterable) PyObject * PyFrozenSet_New(PyObject *iterable) { - return make_new_set(&PyFrozenSet_Type, iterable); + PyObject *result = make_new_set(&PyFrozenSet_Type, iterable); + if (result != NULL) { + _PyFrozenSet_MaybeUntrack(result); + } + return result; } Py_ssize_t @@ -2732,11 +3038,13 @@ PySet_Contains(PyObject *anyset, PyObject *key) return -1; } - int rv; - Py_BEGIN_CRITICAL_SECTION(anyset); - rv = set_contains_key((PySetObject *)anyset, key); - Py_END_CRITICAL_SECTION(); - return rv; + PySetObject *so = (PySetObject *)anyset; + Py_hash_t hash = _PyObject_HashFast(key); + if (hash == -1) { + set_unhashable_type(key); + return -1; + } + return set_contains_entry(so, key, hash); } int @@ -2757,17 +3065,29 @@ PySet_Discard(PyObject *set, PyObject *key) int PySet_Add(PyObject *anyset, PyObject *key) { - if (!PySet_Check(anyset) && - (!PyFrozenSet_Check(anyset) || Py_REFCNT(anyset) != 1)) { - PyErr_BadInternalCall(); - return -1; + if (PySet_Check(anyset)) { + int rv; + Py_BEGIN_CRITICAL_SECTION(anyset); + rv = set_add_key((PySetObject *)anyset, key); + Py_END_CRITICAL_SECTION(); + return rv; } - int rv; - Py_BEGIN_CRITICAL_SECTION(anyset); - rv = set_add_key((PySetObject *)anyset, key); - Py_END_CRITICAL_SECTION(); - return rv; + if (PyFrozenSet_Check(anyset) && _PyObject_IsUniquelyReferenced(anyset)) { + // We can only change frozensets if they are uniquely referenced. The + // API limits the usage of `PySet_Add` to "fill in the values of brand + // new frozensets before they are exposed to other code". In this case, + // this can be done without a lock. + // Since another key is added to the set, we must track the frozenset + // if needed. + if (PyFrozenSet_CheckExact(anyset) && !PyObject_GC_IsTracked(anyset) && PyObject_GC_IsTracked(key)) { + _PyObject_GC_TRACK(anyset); + } + return set_add_key((PySetObject *)anyset, key); + } + + PyErr_BadInternalCall(); + return -1; } int diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 5186ff4f6f0cf5..91d1753b822fc1 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -19,6 +19,7 @@ this type and there is exactly one in existence. #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_modsupport.h" // _PyArg_NoKeywords() #include "pycore_object.h" // _PyObject_GC_TRACK() +#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs #define _PySlice_CAST(op) _Py_CAST(PySliceObject*, (op)) @@ -116,8 +117,8 @@ PyObject _Py_EllipsisObject = _PyObject_HEAD_INIT(&PyEllipsis_Type); index is present. */ -static PySliceObject * -_PyBuildSlice_Consume2(PyObject *start, PyObject *stop, PyObject *step) +PyObject * +_PyBuildSlice_ConsumeRefs(PyObject *start, PyObject *stop, PyObject *step) { assert(start != NULL && stop != NULL && step != NULL); PySliceObject *obj = _Py_FREELIST_POP(PySliceObject, slices); @@ -130,13 +131,14 @@ _PyBuildSlice_Consume2(PyObject *start, PyObject *stop, PyObject *step) obj->start = start; obj->stop = stop; - obj->step = Py_NewRef(step); + obj->step = step; _PyObject_GC_TRACK(obj); - return obj; + return (PyObject *)obj; error: Py_DECREF(start); Py_DECREF(stop); + Py_DECREF(step); return NULL; } @@ -152,15 +154,8 @@ PySlice_New(PyObject *start, PyObject *stop, PyObject *step) if (stop == NULL) { stop = Py_None; } - return (PyObject *)_PyBuildSlice_Consume2(Py_NewRef(start), - Py_NewRef(stop), step); -} - -PyObject * -_PyBuildSlice_ConsumeRefs(PyObject *start, PyObject *stop) -{ - assert(start != NULL && stop != NULL); - return (PyObject *)_PyBuildSlice_Consume2(start, stop, Py_None); + return _PyBuildSlice_ConsumeRefs(Py_NewRef(start), + Py_NewRef(stop), Py_NewRef(step)); } PyObject * @@ -176,9 +171,7 @@ _PySlice_FromIndices(Py_ssize_t istart, Py_ssize_t istop) return NULL; } - slice = PySlice_New(start, end, NULL); - Py_DECREF(start); - Py_DECREF(end); + slice = _PyBuildSlice_ConsumeRefs(start, end, Py_None); return slice; } @@ -341,7 +334,9 @@ PyDoc_STRVAR(slice_doc, "slice(stop)\n\ slice(start, stop[, step])\n\ \n\ -Create a slice object. This is used for extended slicing (e.g. a[0:10:2])."); +Create a slice object.\n\ +\n\ +This is used for extended slicing (e.g. a[0:10:2])."); static void slice_dealloc(PyObject *op) @@ -569,6 +564,8 @@ PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef slice_methods[] = { {"indices", slice_indices, METH_O, slice_indices_doc}, {"__reduce__", slice_reduce, METH_NOARGS, reduce_doc}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, + "slices are generic over the types of their start, end, and step values"}, {NULL, NULL} }; diff --git a/Objects/stringlib/codecs.h b/Objects/stringlib/codecs.h index 440410d0aef17d..9e53fab842909a 100644 --- a/Objects/stringlib/codecs.h +++ b/Objects/stringlib/codecs.h @@ -257,16 +257,14 @@ STRINGLIB(utf8_decode)(const char **inptr, const char *end, /* UTF-8 encoder specialized for a Unicode kind to avoid the slow PyUnicode_READ() macro. Delete some parts of the code depending on the kind: UCS-1 strings don't need to handle surrogates for example. */ -Py_LOCAL_INLINE(char *) -STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, - PyObject *unicode, +Py_LOCAL_INLINE(PyBytesWriter*) +STRINGLIB(utf8_encoder)(PyObject *unicode, const STRINGLIB_CHAR *data, Py_ssize_t size, _Py_error_handler error_handler, - const char *errors) + const char *errors, + char **end) { - Py_ssize_t i; /* index into data of next input character */ - char *p; /* next free byte in output buffer */ #if STRINGLIB_SIZEOF_CHAR > 1 PyObject *error_handler_obj = NULL; PyObject *exc = NULL; @@ -284,14 +282,19 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, if (size > PY_SSIZE_T_MAX / max_char_size) { /* integer overflow */ PyErr_NoMemory(); + *end = NULL; return NULL; } - _PyBytesWriter_Init(writer); - p = _PyBytesWriter_Alloc(writer, size * max_char_size); - if (p == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(size * max_char_size); + if (writer == NULL) { + *end = NULL; return NULL; + } + /* next free byte in output buffer */ + char *p = PyBytesWriter_GetData(writer); + Py_ssize_t i; /* index into data of next input character */ for (i = 0; i < size;) { Py_UCS4 ch = data[i++]; @@ -348,7 +351,7 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, case _Py_ERROR_BACKSLASHREPLACE: /* subtract preallocated bytes */ - writer->min_size -= max_char_size * (endpos - startpos); + writer->size -= max_char_size * (endpos - startpos); p = backslashreplace(writer, p, unicode, startpos, endpos); if (p == NULL) @@ -358,7 +361,7 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, case _Py_ERROR_XMLCHARREFREPLACE: /* subtract preallocated bytes */ - writer->min_size -= max_char_size * (endpos - startpos); + writer->size -= max_char_size * (endpos - startpos); p = xmlcharrefreplace(writer, p, unicode, startpos, endpos); if (p == NULL) @@ -389,22 +392,25 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, if (newpos < startpos) { writer->overallocate = 1; - p = _PyBytesWriter_Prepare(writer, p, - max_char_size * (startpos - newpos)); - if (p == NULL) + p = PyBytesWriter_GrowAndUpdatePointer(writer, + max_char_size * (startpos - newpos), + p); + if (p == NULL) { goto error; + } } else { /* subtract preallocated bytes */ - writer->min_size -= max_char_size * (newpos - startpos); + writer->size -= max_char_size * (newpos - startpos); /* Only overallocate the buffer if it's not the last write */ writer->overallocate = (newpos < size); } + char *rep_str; + Py_ssize_t rep_len; if (PyBytes_Check(rep)) { - p = _PyBytesWriter_WriteBytes(writer, p, - PyBytes_AS_STRING(rep), - PyBytes_GET_SIZE(rep)); + rep_str = PyBytes_AS_STRING(rep); + rep_len = PyBytes_GET_SIZE(rep); } else { /* rep is unicode */ @@ -415,13 +421,16 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, goto error; } - p = _PyBytesWriter_WriteBytes(writer, p, - PyUnicode_DATA(rep), - PyUnicode_GET_LENGTH(rep)); + rep_str = PyUnicode_DATA(rep); + rep_len = PyUnicode_GET_LENGTH(rep); } - if (p == NULL) + p = PyBytesWriter_GrowAndUpdatePointer(writer, rep_len, p); + if (p == NULL) { goto error; + } + memcpy(p, rep_str, rep_len); + p += rep_len; Py_CLEAR(rep); i = newpos; @@ -458,13 +467,16 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, Py_XDECREF(error_handler_obj); Py_XDECREF(exc); #endif - return p; + *end = p; + return writer; #if STRINGLIB_SIZEOF_CHAR > 1 error: + PyBytesWriter_Discard(writer); Py_XDECREF(rep); Py_XDECREF(error_handler_obj); Py_XDECREF(exc); + *end = NULL; return NULL; #endif } diff --git a/Objects/stringlib/fastsearch.h b/Objects/stringlib/fastsearch.h index b447865c986bef..26bb0555ca9b6c 100644 --- a/Objects/stringlib/fastsearch.h +++ b/Objects/stringlib/fastsearch.h @@ -69,8 +69,8 @@ STRINGLIB(find_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch) and UCS4 representations. */ if (needle != 0) { do { - void *candidate = memchr(p, needle, - (e - p) * sizeof(STRINGLIB_CHAR)); + const void *candidate = memchr(p, needle, + (e - p) * sizeof(STRINGLIB_CHAR)); if (candidate == NULL) return -1; s1 = p; diff --git a/Objects/stringlib/join.h b/Objects/stringlib/join.h index de6bd83ffe4c8b..deebfeadc0f4fd 100644 --- a/Objects/stringlib/join.h +++ b/Objects/stringlib/join.h @@ -68,13 +68,18 @@ STRINGLIB(bytes_join)(PyObject *sep, PyObject *iterable) buffers[i].len = PyBytes_GET_SIZE(item); } else { + /* item is only borrowed; its __buffer__() may run Python that + drops the sequence's last reference to it. */ + Py_INCREF(item); if (PyObject_GetBuffer(item, &buffers[i], PyBUF_SIMPLE) != 0) { + Py_DECREF(item); PyErr_Format(PyExc_TypeError, "sequence item %zd: expected a bytes-like object, " "%.80s found", i, Py_TYPE(item)->tp_name); goto error; } + Py_DECREF(item); /* If the backing objects are mutable, then dropping the GIL * opens up race conditions where another thread tries to modify * the object which we hold a buffer on it. Such code has data diff --git a/Objects/stringlib/localeutil.h b/Objects/stringlib/localeutil.h deleted file mode 100644 index a4ab701de004c8..00000000000000 --- a/Objects/stringlib/localeutil.h +++ /dev/null @@ -1,97 +0,0 @@ -/* _PyUnicode_InsertThousandsGrouping() helper functions */ - -typedef struct { - const char *grouping; - char previous; - Py_ssize_t i; /* Where we're currently pointing in grouping. */ -} GroupGenerator; - - -static void -GroupGenerator_init(GroupGenerator *self, const char *grouping) -{ - self->grouping = grouping; - self->i = 0; - self->previous = 0; -} - - -/* Returns the next grouping, or 0 to signify end. */ -static Py_ssize_t -GroupGenerator_next(GroupGenerator *self) -{ - /* Note that we don't really do much error checking here. If a - grouping string contains just CHAR_MAX, for example, then just - terminate the generator. That shouldn't happen, but at least we - fail gracefully. */ - switch (self->grouping[self->i]) { - case 0: - return self->previous; - case CHAR_MAX: - /* Stop the generator. */ - return 0; - default: { - char ch = self->grouping[self->i]; - self->previous = ch; - self->i++; - return (Py_ssize_t)ch; - } - } -} - - -/* Fill in some digits, leading zeros, and thousands separator. All - are optional, depending on when we're called. */ -static void -InsertThousandsGrouping_fill(_PyUnicodeWriter *writer, Py_ssize_t *buffer_pos, - PyObject *digits, Py_ssize_t *digits_pos, - Py_ssize_t n_chars, Py_ssize_t n_zeros, - PyObject *thousands_sep, Py_ssize_t thousands_sep_len, - Py_UCS4 *maxchar, int forward) -{ - if (!writer) { - /* if maxchar > 127, maxchar is already set */ - if (*maxchar == 127 && thousands_sep) { - Py_UCS4 maxchar2 = PyUnicode_MAX_CHAR_VALUE(thousands_sep); - *maxchar = Py_MAX(*maxchar, maxchar2); - } - return; - } - - if (thousands_sep) { - if (!forward) { - *buffer_pos -= thousands_sep_len; - } - /* Copy the thousands_sep chars into the buffer. */ - _PyUnicode_FastCopyCharacters(writer->buffer, *buffer_pos, - thousands_sep, 0, - thousands_sep_len); - if (forward) { - *buffer_pos += thousands_sep_len; - } - } - - if (!forward) { - *buffer_pos -= n_chars; - *digits_pos -= n_chars; - } - _PyUnicode_FastCopyCharacters(writer->buffer, *buffer_pos, - digits, *digits_pos, - n_chars); - if (forward) { - *buffer_pos += n_chars; - *digits_pos += n_chars; - } - - if (n_zeros) { - if (!forward) { - *buffer_pos -= n_zeros; - } - int kind = PyUnicode_KIND(writer->buffer); - void *data = PyUnicode_DATA(writer->buffer); - unicode_fill(kind, data, '0', *buffer_pos, n_zeros); - if (forward) { - *buffer_pos += n_zeros; - } - } -} diff --git a/Objects/stringlib/transmogrify.h b/Objects/stringlib/transmogrify.h index 71099bb586e809..591bf55d1a9406 100644 --- a/Objects/stringlib/transmogrify.h +++ b/Objects/stringlib/transmogrify.h @@ -207,6 +207,7 @@ stringlib_center_impl(PyObject *self, Py_ssize_t width, char fillchar) } /*[clinic input] +@permit_long_summary B.zfill as stringlib_zfill width: Py_ssize_t @@ -219,7 +220,7 @@ The original string is never truncated. static PyObject * stringlib_zfill_impl(PyObject *self, Py_ssize_t width) -/*[clinic end generated code: output=0b3c684a7f1b2319 input=2da6d7b8e9bcb19a]*/ +/*[clinic end generated code: output=0b3c684a7f1b2319 input=dfb9cbb16f521756]*/ { Py_ssize_t fill; PyObject *s; diff --git a/Objects/stringlib/unicode_format.h b/Objects/stringlib/unicode_format.h index ff32db65b11a0b..c9c46283840d18 100644 --- a/Objects/stringlib/unicode_format.h +++ b/Objects/stringlib/unicode_format.h @@ -4,6 +4,7 @@ #include "pycore_complexobject.h" // _PyComplex_FormatAdvancedWriter() #include "pycore_floatobject.h" // _PyFloat_FormatAdvancedWriter() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal /************************************************************************/ /*********** Global data structures and forward declarations *********/ @@ -1172,7 +1173,7 @@ fieldnameiter_next(PyObject *op) is_attr_obj = PyBool_FromLong(is_attr); if (is_attr_obj == NULL) - goto done; + goto error; /* either an integer or a string */ if (idx != -1) @@ -1180,12 +1181,12 @@ fieldnameiter_next(PyObject *op) else obj = SubString_new_object(&name); if (obj == NULL) - goto done; + goto error; /* return a tuple of values */ - result = PyTuple_Pack(2, is_attr_obj, obj); + return _PyTuple_FromPairSteal(is_attr_obj, obj); - done: + error: Py_XDECREF(is_attr_obj); Py_XDECREF(obj); return result; @@ -1262,7 +1263,7 @@ formatter_field_name_split(PyObject *Py_UNUSED(module), PyObject *self) first_obj in that case. */ if (!field_name_split((PyObject*)self, 0, PyUnicode_GET_LENGTH(self), &first, &first_idx, &it->it_field, NULL)) - goto done; + goto error; /* first becomes an integer, if possible; else a string */ if (first_idx != -1) @@ -1271,12 +1272,12 @@ formatter_field_name_split(PyObject *Py_UNUSED(module), PyObject *self) /* convert "first" into a string object */ first_obj = SubString_new_object(&first); if (first_obj == NULL) - goto done; + goto error; /* return a tuple of values */ - result = PyTuple_Pack(2, first_obj, it); + return _PyTuple_FromPairSteal(first_obj, (PyObject *)it); -done: +error: Py_XDECREF(it); Py_XDECREF(first_obj); return result; diff --git a/Objects/structseq.c b/Objects/structseq.c index c05bcde24c441b..9130fe6a133b1e 100644 --- a/Objects/structseq.c +++ b/Objects/structseq.c @@ -12,7 +12,7 @@ #include "pycore_modsupport.h" // _PyArg_NoPositional() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_structseq.h" // PyStructSequence_InitType() -#include "pycore_tuple.h" // _PyTuple_FromArray() +#include "pycore_tuple.h" // _PyTuple_RESET_HASH_CACHE() #include "pycore_typeobject.h" // _PyStaticType_FiniBuiltin() static const char visible_length_key[] = "n_sequence_fields"; @@ -28,7 +28,11 @@ static Py_ssize_t get_type_attr_as_size(PyTypeObject *tp, PyObject *name) { PyObject *v = PyDict_GetItemWithError(_PyType_GetDict(tp), name); - if (v == NULL && !PyErr_Occurred()) { + + if (v == NULL) { + if (PyErr_Occurred()) { + return -1; + } PyErr_Format(PyExc_TypeError, "Missed attribute '%U' of type %s", name, tp->tp_name); @@ -353,7 +357,7 @@ structseq_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) if (n_unnamed_fields < 0) { return NULL; } - tup = _PyTuple_FromArray(self->ob_item, n_visible_fields); + tup = PyTuple_FromArray(self->ob_item, n_visible_fields); if (!tup) goto error; @@ -445,6 +449,7 @@ structseq_replace(PyObject *op, PyObject *args, PyObject *kwargs) } } + _PyObject_GC_TRACK(result); return (PyObject *)result; error: @@ -515,7 +520,8 @@ initialize_structseq_dict(PyStructSequence_Desc *desc, PyObject* dict, } if (_PyTuple_Resize(&keys, k) == -1) { - goto error; + assert(keys == NULL); + return -1; } if (PyDict_SetItemString(dict, match_args_key, keys) < 0) { diff --git a/Objects/templateobject.c b/Objects/templateobject.c index ac38e4de435d5d..1609e82b444516 100644 --- a/Objects/templateobject.c +++ b/Objects/templateobject.c @@ -148,13 +148,14 @@ template_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (last_was_str) { PyObject *laststring = PyTuple_GET_ITEM(strings, stringsidx - 1); PyObject *concat = PyUnicode_Concat(laststring, item); - Py_DECREF(laststring); if (!concat) { Py_DECREF(strings); Py_DECREF(interpolations); return NULL; } + /* Replace laststring with concat */ PyTuple_SET_ITEM(strings, stringsidx - 1, concat); + Py_DECREF(laststring); } else { PyTuple_SET_ITEM(strings, stringsidx++, Py_NewRef(item)); @@ -371,7 +372,11 @@ template_reduce(PyObject *op, PyObject *Py_UNUSED(dummy)) static PyMethodDef template_methods[] = { {"__reduce__", template_reduce, METH_NOARGS, NULL}, {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, + // note that this is not supported in typeshed, and it is not clear if the + // type for this is a simple TypeVar or a TypeVarTuple + // for details, see: https://github.com/python/typeshed/issues/14878 + PyDoc_STR("Template supports [] for generic usage")}, {NULL, NULL}, }; diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 9b31758485ca5e..bb5e18cb790acf 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -118,7 +118,7 @@ int PyTuple_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem) { PyObject **p; - if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) { + if (!PyTuple_Check(op) || !_PyObject_IsUniquelyReferenced(op)) { Py_XDECREF(newitem); PyErr_BadInternalCall(); return -1; @@ -156,6 +156,18 @@ _PyTuple_MaybeUntrack(PyObject *op) _PyObject_GC_UNTRACK(op); } +/* Fast, but conservative check if an object maybe tracked + May return true for an object that is not tracked, + Will always return true for an object that is tracked. + This is a temporary workaround until _PyObject_GC_IS_TRACKED + becomes fast and safe to call on non-GC objects. +*/ +static bool +maybe_tracked(PyObject *ob) +{ + return _PyType_IS_GC(Py_TYPE(ob)); +} + PyObject * PyTuple_Pack(Py_ssize_t n, ...) { @@ -163,6 +175,7 @@ PyTuple_Pack(Py_ssize_t n, ...) PyObject *o; PyObject **items; va_list vargs; + bool track = false; if (n == 0) { return tuple_get_empty(); @@ -177,16 +190,67 @@ PyTuple_Pack(Py_ssize_t n, ...) items = result->ob_item; for (i = 0; i < n; i++) { o = va_arg(vargs, PyObject *); + if (!track && maybe_tracked(o)) { + track = true; + } items[i] = Py_NewRef(o); } va_end(vargs); - _PyObject_GC_TRACK(result); + if (track) { + _PyObject_GC_TRACK(result); + } return (PyObject *)result; } +PyObject * +_PyTuple_FromPair(PyObject *first, PyObject *second) +{ + assert(first != NULL); + assert(second != NULL); + + return _PyTuple_FromPairSteal(Py_NewRef(first), Py_NewRef(second)); +} + +PyObject * +_PyTuple_FromPairSteal(PyObject *first, PyObject *second) +{ + assert(first != NULL); + assert(second != NULL); + + PyTupleObject *op = tuple_alloc(2); + if (op == NULL) { + Py_DECREF(first); + Py_DECREF(second); + return NULL; + } + PyObject **items = op->ob_item; + items[0] = first; + items[1] = second; + if (maybe_tracked(first) || maybe_tracked(second)) { + _PyObject_GC_TRACK(op); + } + return (PyObject *)op; +} /* Methods */ +/* + Free of a tuple where all contents have been stolen and + is now untracked by GC. This operation is thus non-escaping. + */ +void +_PyStolenTuple_Free(PyObject *obj) +{ + assert(PyTuple_CheckExact(obj)); + PyTupleObject *op = _PyTuple_CAST(obj); + assert(Py_SIZE(op) != 0); + assert(!_PyObject_GC_IS_TRACKED(obj)); + // This will abort on the empty singleton (if there is one). + if (!maybe_freelist_push(op)) { + PyTuple_Type.tp_free((PyObject *)op); + } +} + static void tuple_dealloc(PyObject *self) { @@ -299,6 +363,9 @@ tuple_repr(PyObject *self) https://github.com/Cyan4973/xxHash/blob/master/doc/xxhash_spec.md The constants for the hash function are defined in pycore_tuple.h. + + If you update this code, update also frozendict_pair_hash() which copied + this code. */ static Py_hash_t @@ -366,7 +433,7 @@ tuple_item(PyObject *op, Py_ssize_t i) } PyObject * -_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) +PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) { if (n == 0) { return tuple_get_empty(); @@ -377,11 +444,17 @@ _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) return NULL; } PyObject **dst = tuple->ob_item; + bool track = false; for (Py_ssize_t i = 0; i < n; i++) { PyObject *item = src[i]; + if (!track && maybe_tracked(item)) { + track = true; + } dst[i] = Py_NewRef(item); } - _PyObject_GC_TRACK(tuple); + if (track) { + _PyObject_GC_TRACK(tuple); + } return (PyObject *)tuple; } @@ -396,10 +469,17 @@ _PyTuple_FromStackRefStealOnSuccess(const _PyStackRef *src, Py_ssize_t n) return NULL; } PyObject **dst = tuple->ob_item; + bool track = false; for (Py_ssize_t i = 0; i < n; i++) { - dst[i] = PyStackRef_AsPyObjectSteal(src[i]); + PyObject *item = PyStackRef_AsPyObjectSteal(src[i]); + if (!track && maybe_tracked(item)) { + track = true; + } + dst[i] = item; + } + if (track) { + _PyObject_GC_TRACK(tuple); } - _PyObject_GC_TRACK(tuple); return (PyObject *)tuple; } @@ -438,7 +518,26 @@ tuple_slice(PyTupleObject *a, Py_ssize_t ilow, if (ilow == 0 && ihigh == Py_SIZE(a) && PyTuple_CheckExact(a)) { return Py_NewRef(a); } - return _PyTuple_FromArray(a->ob_item + ilow, ihigh - ilow); + return PyTuple_FromArray(a->ob_item + ilow, ihigh - ilow); +} + +PyObject * +_PyTuple_BinarySlice(PyObject *container, PyObject *start, PyObject *stop) +{ + assert(PyTuple_CheckExact(container)); + Py_ssize_t len = Py_SIZE(container); + Py_ssize_t istart, istop; + if (!_PyEval_UnpackIndices(start, stop, len, &istart, &istop)) { + return NULL; + } + if (istart == 0 && istop == len) { + return Py_NewRef(container); + } + if (istop < istart) { + istop = istart; + } + return PyTuple_FromArray(((PyTupleObject *)container)->ob_item + istart, + istop - istart); } PyObject * @@ -451,8 +550,8 @@ PyTuple_GetSlice(PyObject *op, Py_ssize_t i, Py_ssize_t j) return tuple_slice((PyTupleObject *)op, i, j); } -static PyObject * -tuple_concat(PyObject *aa, PyObject *bb) +PyObject * +_PyTuple_Concat(PyObject *aa, PyObject *bb) { PyTupleObject *a = _PyTuple_CAST(aa); if (Py_SIZE(a) == 0 && PyTuple_CheckExact(bb)) { @@ -498,8 +597,8 @@ tuple_concat(PyObject *aa, PyObject *bb) return (PyObject *)np; } -static PyObject * -tuple_repeat(PyObject *self, Py_ssize_t n) +PyObject * +_PyTuple_Repeat(PyObject *self, Py_ssize_t n) { PyTupleObject *a = _PyTuple_CAST(self); const Py_ssize_t input_size = Py_SIZE(a); @@ -768,8 +867,8 @@ tuple_subtype_new(PyTypeObject *type, PyObject *iterable) static PySequenceMethods tuple_as_sequence = { tuple_length, /* sq_length */ - tuple_concat, /* sq_concat */ - tuple_repeat, /* sq_repeat */ + _PyTuple_Concat, /* sq_concat */ + _PyTuple_Repeat, /* sq_repeat */ tuple_item, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ @@ -777,6 +876,17 @@ static PySequenceMethods tuple_as_sequence = { tuple_contains, /* sq_contains */ }; +static _PyObjectIndexPair +tuple_iteritem(PyObject *obj, Py_ssize_t index) +{ + if (index >= PyTuple_GET_SIZE(obj)) { + return (_PyObjectIndexPair) { .object = NULL, .index = index }; + } + PyObject *result = PyTuple_GET_ITEM(obj, index); + Py_INCREF(result); + return (_PyObjectIndexPair) { .object = result, .index = index + 1 }; +} + static PyObject* tuple_subscript(PyObject *op, PyObject* item) { @@ -844,11 +954,19 @@ tuple___getnewargs___impl(PyTupleObject *self) return Py_BuildValue("(N)", tuple_slice(self, 0, Py_SIZE(self))); } + +PyDoc_STRVAR(tuple_class_getitem_doc, +"Tuples are generic over the types of their contents.\n\n\ +For example, use ``tuple[int, str]`` for a pair whose first element\n\ +is an int and second element is a string.\n\n\ +Tuples also support the form ``tuple[T, ...]`` to indicate\n\ +an arbitrary length tuple of elements of type T."); + static PyMethodDef tuple_methods[] = { TUPLE___GETNEWARGS___METHODDEF TUPLE_INDEX_METHODDEF TUPLE_COUNT_METHODDEF - {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, tuple_class_getitem_doc}, {NULL, NULL} /* sentinel */ }; @@ -904,6 +1022,7 @@ PyTypeObject PyTuple_Type = { PyObject_GC_Del, /* tp_free */ .tp_vectorcall = tuple_vectorcall, .tp_version_tag = _Py_TYPE_VERSION_TUPLE, + ._tp_iteritem = tuple_iteritem, }; /* The following function breaks the notion that tuples are immutable: @@ -923,7 +1042,7 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize) v = (PyTupleObject *) *pv; if (v == NULL || !Py_IS_TYPE(v, &PyTuple_Type) || - (Py_SIZE(v) != 0 && Py_REFCNT(v) != 1)) { + (Py_SIZE(v) != 0 && !_PyObject_IsUniquelyReferenced(*pv))) { *pv = 0; Py_XDECREF(v); PyErr_BadInternalCall(); @@ -1178,6 +1297,6 @@ _PyTuple_DebugMallocStats(FILE *out) PyOS_snprintf(buf, sizeof(buf), "free %d-sized PyTupleObject", len); _PyDebugAllocatorStats(out, buf, _Py_FREELIST_SIZE(tuples[i]), - _PyObject_VAR_SIZE(&PyTuple_Type, len)); + _PyType_PreHeaderSize(&PyTuple_Type) + _PyObject_VAR_SIZE(&PyTuple_Type, len)); } } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index d952a58d94af55..388e8891c9c739 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4,6 +4,7 @@ #include "pycore_abstract.h" // _PySequence_IterSearch() #include "pycore_call.h" // _PyObject_VectorcallTstate() #include "pycore_code.h" // CO_FAST_FREE +#include "pycore_descrobject.h" // _PyMember_GetOffset() #include "pycore_dict.h" // _PyDict_KeysSize() #include "pycore_function.h" // _PyFunction_GetVersionForCurrentState() #include "pycore_interpframe.h" // _PyInterpreterFrame @@ -17,7 +18,9 @@ #include "pycore_pyatomic_ft_wrappers.h" #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_slots.h" // _PySlotIterator_Init #include "pycore_symtable.h" // _Py_Mangle() +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_typeobject.h" // struct type_cache #include "pycore_unicodeobject.h" // _PyUnicode_Copy #include "pycore_unionobject.h" // _Py_union_type_or @@ -51,8 +54,8 @@ class object "PyObject *" "&PyBaseObject_Type" MCACHE_HASH(FT_ATOMIC_LOAD_UINT_RELAXED((type)->tp_version_tag), \ ((Py_ssize_t)(name)) >> 3) #define MCACHE_CACHEABLE_NAME(name) \ - PyUnicode_CheckExact(name) && \ - (PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE) + (PyUnicode_CheckExact(name) && \ + (PyUnicode_GET_LENGTH(name) <= MCACHE_MAX_ATTR_SIZE)) #define NEXT_VERSION_TAG(interp) \ (interp)->types.next_version_tag @@ -72,7 +75,7 @@ class object "PyObject *" "&PyBaseObject_Type" // the type has been revealed to other threads or we only do those updates // while the stop-the-world mechanism is active. The slots and flags are read // in many places without holding a lock and without atomics. -#define TYPE_LOCK &PyInterpreterState_Get()->types.mutex +#define TYPE_LOCK &_PyInterpreterState_GET()->types.mutex #define BEGIN_TYPE_LOCK() Py_BEGIN_CRITICAL_SECTION_MUTEX(TYPE_LOCK) #define END_TYPE_LOCK() Py_END_CRITICAL_SECTION() @@ -81,7 +84,7 @@ class object "PyObject *" "&PyBaseObject_Type" #define END_TYPE_DICT_LOCK() Py_END_CRITICAL_SECTION2() -#ifdef Py_DEBUG +#if !defined(NDEBUG) || defined(Py_DEBUG) // Return true if the world is currently stopped. static bool types_world_is_stopped(void) @@ -189,16 +192,13 @@ type_lock_allow_release(void) #define types_world_is_stopped() 1 #define types_stop_world() #define types_start_world() +#define type_lock_prevent_release() +#define type_lock_allow_release() #endif #define PyTypeObject_CAST(op) ((PyTypeObject *)(op)) -typedef struct PySlot_Offset { - short subslot_offset; - short slot_offset; -} PySlot_Offset; - static void slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer); @@ -644,32 +644,9 @@ clear_tp_bases(PyTypeObject *self, int final) static inline PyObject * lookup_tp_mro(PyTypeObject *self) { - ASSERT_NEW_TYPE_OR_LOCKED(self); return self->tp_mro; } -PyObject * -_PyType_GetMRO(PyTypeObject *self) -{ -#ifdef Py_GIL_DISABLED - PyObject *mro = _Py_atomic_load_ptr_relaxed(&self->tp_mro); - if (mro == NULL) { - return NULL; - } - if (_Py_TryIncrefCompare(&self->tp_mro, mro)) { - return mro; - } - - BEGIN_TYPE_LOCK(); - mro = lookup_tp_mro(self); - Py_XINCREF(mro); - END_TYPE_LOCK(); - return mro; -#else - return Py_XNewRef(lookup_tp_mro(self)); -#endif -} - static inline void set_tp_mro(PyTypeObject *self, PyObject *mro, int initial) { @@ -683,8 +660,19 @@ set_tp_mro(PyTypeObject *self, PyObject *mro, int initial) /* Other checks are done via set_tp_bases. */ _Py_SetImmortal(mro); } + else { + PyUnstable_Object_EnableDeferredRefcount(mro); + } + } + if (!initial) { + type_lock_prevent_release(); + types_stop_world(); } self->tp_mro = mro; + if (!initial) { + types_start_world(); + type_lock_allow_release(); + } } static inline void @@ -752,6 +740,22 @@ lookup_tp_subclasses(PyTypeObject *self) return (PyObject *)self->tp_subclasses; } +PyObject * +_PyType_LookupSubclasses(PyTypeObject *self) +{ + return lookup_tp_subclasses(self); +} + +PyObject * +_PyType_InitSubclasses(PyTypeObject *self) +{ + PyObject *existing = lookup_tp_subclasses(self); + if (existing != NULL) { + return existing; + } + return init_tp_subclasses(self); +} + int _PyType_HasSubclasses(PyTypeObject *self) { @@ -1342,6 +1346,35 @@ _PyType_LookupByVersion(unsigned int version) #ifdef Py_GIL_DISABLED return NULL; #else + switch (version) { + case _Py_TYPE_VERSION_INT: + return &PyLong_Type; + case _Py_TYPE_VERSION_FLOAT: + return &PyFloat_Type; + case _Py_TYPE_VERSION_LIST: + return &PyList_Type; + case _Py_TYPE_VERSION_TUPLE: + return &PyTuple_Type; + case _Py_TYPE_VERSION_STR: + return &PyUnicode_Type; + case _Py_TYPE_VERSION_SET: + return &PySet_Type; + case _Py_TYPE_VERSION_FROZEN_SET: + return &PyFrozenSet_Type; + case _Py_TYPE_VERSION_DICT: + return &PyDict_Type; + case _Py_TYPE_VERSION_BYTEARRAY: + return &PyByteArray_Type; + case _Py_TYPE_VERSION_BYTES: + return &PyBytes_Type; + case _Py_TYPE_VERSION_COMPLEX: + return &PyComplex_Type; + case _Py_TYPE_VERSION_FROZENDICT: + return &PyFrozenDict_Type; + default: + break; + } + PyInterpreterState *interp = _PyInterpreterState_GET(); PyTypeObject **slot = interp->types.type_version_cache @@ -1449,15 +1482,15 @@ static PyMemberDef type_members[] = { static int check_set_special_type_attr(PyTypeObject *type, PyObject *value, const char *name) { - if (_PyType_HasFeature(type, Py_TPFLAGS_IMMUTABLETYPE)) { + if (!value) { PyErr_Format(PyExc_TypeError, - "cannot set '%s' attribute of immutable type '%s'", + "cannot delete '%s' attribute of type '%s'", name, type->tp_name); return 0; } - if (!value) { + if (_PyType_HasFeature(type, Py_TPFLAGS_IMMUTABLETYPE)) { PyErr_Format(PyExc_TypeError, - "cannot delete '%s' attribute of immutable type '%s'", + "cannot set '%s' attribute of immutable type '%s'", name, type->tp_name); return 0; } @@ -1535,9 +1568,13 @@ type_set_name(PyObject *tp, PyObject *value, void *Py_UNUSED(closure)) return -1; } + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); type->tp_name = tp_name; - Py_SETREF(((PyHeapTypeObject*)type)->ht_name, Py_NewRef(value)); - + PyObject *old_name = ((PyHeapTypeObject*)type)->ht_name; + ((PyHeapTypeObject*)type)->ht_name = Py_NewRef(value); + _PyEval_StartTheWorld(interp); + Py_DECREF(old_name); return 0; } @@ -1557,7 +1594,12 @@ type_set_qualname(PyObject *tp, PyObject *value, void *context) } et = (PyHeapTypeObject*)type; - Py_SETREF(et->ht_qualname, Py_NewRef(value)); + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); + PyObject *old_qualname = et->ht_qualname; + et->ht_qualname = Py_NewRef(value); + _PyEval_StartTheWorld(interp); + Py_DECREF(old_qualname); return 0; } @@ -1727,24 +1769,17 @@ static PyObject * type_get_mro(PyObject *tp, void *Py_UNUSED(closure)) { PyTypeObject *type = PyTypeObject_CAST(tp); - PyObject *mro; - - BEGIN_TYPE_LOCK(); - mro = lookup_tp_mro(type); + PyObject *mro = lookup_tp_mro(type); if (mro == NULL) { - mro = Py_None; - } else { - Py_INCREF(mro); + Py_RETURN_NONE; } - - END_TYPE_LOCK(); - return mro; + return Py_NewRef(mro); } static PyTypeObject *find_best_base(PyObject *); static int mro_internal(PyTypeObject *, int, PyObject **); static int type_is_subtype_base_chain(PyTypeObject *, PyTypeObject *); -static int compatible_for_assignment(PyTypeObject *, PyTypeObject *, const char *); +static int compatible_for_assignment(PyTypeObject *, PyTypeObject *, const char *, int); static int add_subclass(PyTypeObject*, PyTypeObject*); static int add_all_subclasses(PyTypeObject *type, PyObject *bases); static void remove_subclass(PyTypeObject *, PyTypeObject *); @@ -1760,7 +1795,7 @@ static int recurse_down_subclasses(PyTypeObject *type, PyObject *name, // Compute tp_mro for this type and all of its subclasses. This // is called after __bases__ is assigned to an existing type. static int -mro_hierarchy(PyTypeObject *type, PyObject *temp) +mro_hierarchy_for_complete_type(PyTypeObject *type, PyObject *temp) { ASSERT_TYPE_LOCK_HELD(); @@ -1771,13 +1806,14 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp) return res; } PyObject *new_mro = lookup_tp_mro(type); + assert(new_mro != NULL); PyObject *tuple; if (old_mro != NULL) { tuple = PyTuple_Pack(3, type, new_mro, old_mro); } else { - tuple = PyTuple_Pack(2, type, new_mro); + tuple = _PyTuple_FromPair((PyObject *)type, new_mro); } if (tuple != NULL) { @@ -1815,7 +1851,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp) Py_ssize_t n = PyList_GET_SIZE(subclasses); for (Py_ssize_t i = 0; i < n; i++) { PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i)); - res = mro_hierarchy(subclass, temp); + res = mro_hierarchy_for_complete_type(subclass, temp); if (res < 0) { break; } @@ -1882,7 +1918,7 @@ type_check_new_bases(PyTypeObject *type, PyObject *new_bases, PyTypeObject **bes if (*best_base == NULL) return -1; - if (!compatible_for_assignment(type->tp_base, *best_base, "__bases__")) { + if (!compatible_for_assignment(type, *best_base, "__bases__", 0)) { return -1; } @@ -1899,14 +1935,18 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, PyTypeObject *b assert(old_bases != NULL); PyTypeObject *old_base = type->tp_base; + type_lock_prevent_release(); + types_stop_world(); set_tp_bases(type, Py_NewRef(new_bases), 0); type->tp_base = (PyTypeObject *)Py_NewRef(best_base); + types_start_world(); + type_lock_allow_release(); PyObject *temp = PyList_New(0); if (temp == NULL) { goto bail; } - if (mro_hierarchy(type, temp) < 0) { + if (mro_hierarchy_for_complete_type(type, temp) < 0) { goto undo; } Py_DECREF(temp); @@ -1961,8 +2001,12 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, PyTypeObject *b if (lookup_tp_bases(type) == new_bases) { assert(type->tp_base == best_base); + type_lock_prevent_release(); + types_stop_world(); set_tp_bases(type, old_bases, 0); type->tp_base = old_base; + types_start_world(); + type_lock_allow_release(); Py_DECREF(new_bases); Py_DECREF(best_base); @@ -2164,8 +2208,9 @@ type_get_annotations(PyObject *tp, void *Py_UNUSED(closure)) return NULL; } if (!PyDict_Check(annotations)) { - PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'", - Py_TYPE(annotations)->tp_name); + PyErr_Format(PyExc_TypeError, + "__annotate__() must return a dict, not %T", + annotations); Py_DECREF(annotations); Py_DECREF(annotate); Py_DECREF(dict); @@ -2546,7 +2591,7 @@ traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg) mp = _PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type); for (i = 0; i < n; i++, mp++) { if (mp->type == Py_T_OBJECT_EX) { - char *addr = (char *)self + mp->offset; + void *addr = _PyMember_GetOffset(self, mp); PyObject *obj = *(PyObject **)addr; if (obj != NULL) { int err = visit(obj, arg); @@ -2621,7 +2666,7 @@ clear_slots(PyTypeObject *type, PyObject *self) mp = _PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type); for (i = 0; i < n; i++, mp++) { if (mp->type == Py_T_OBJECT_EX && !(mp->flags & Py_READONLY)) { - char *addr = (char *)self + mp->offset; + void *addr = _PyMember_GetOffset(self, mp); PyObject *obj = *(PyObject **)addr; if (obj != NULL) { *(PyObject **)addr = NULL; @@ -3411,6 +3456,7 @@ mro_implementation_unlocked(PyTypeObject *type) */ PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0)); PyObject *base_mro = lookup_tp_mro(base); + assert(base_mro != NULL); Py_ssize_t k = PyTuple_GET_SIZE(base_mro); PyObject *result = PyTuple_New(k + 1); if (result == NULL) { @@ -3445,9 +3491,12 @@ mro_implementation_unlocked(PyTypeObject *type) return NULL; } + PyObject *mro_to_merge; for (Py_ssize_t i = 0; i < n; i++) { PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i)); - to_merge[i] = lookup_tp_mro(base); + mro_to_merge = lookup_tp_mro(base); + assert(mro_to_merge != NULL); + to_merge[i] = mro_to_merge; } to_merge[n] = bases; @@ -3506,10 +3555,8 @@ mro_check(PyTypeObject *type, PyObject *mro) for (i = 0; i < n; i++) { PyObject *obj = PyTuple_GET_ITEM(mro, i); if (!PyType_Check(obj)) { - PyErr_Format( - PyExc_TypeError, - "mro() returned a non-class ('%.500s')", - Py_TYPE(obj)->tp_name); + PyErr_Format(PyExc_TypeError, + "%N.mro() returned a non-class ('%T')", type, obj); return -1; } PyTypeObject *base = (PyTypeObject*)obj; @@ -3517,8 +3564,8 @@ mro_check(PyTypeObject *type, PyObject *mro) if (!is_subtype_with_mro(lookup_tp_mro(solid), solid, solid_base(base))) { PyErr_Format( PyExc_TypeError, - "mro() returned base with unsuitable layout ('%.500s')", - base->tp_name); + "%N.mro() returned base with unsuitable layout ('%N')", + type, base); return -1; } } @@ -4036,26 +4083,15 @@ subtype_getweakref(PyObject *obj, void *context) return Py_NewRef(result); } -/* Three variants on the subtype_getsets list. */ - -static PyGetSetDef subtype_getsets_full[] = { - {"__dict__", subtype_dict, subtype_setdict, - PyDoc_STR("dictionary for instance variables")}, - {"__weakref__", subtype_getweakref, NULL, - PyDoc_STR("list of weak references to the object")}, - {0} -}; - -static PyGetSetDef subtype_getsets_dict_only[] = { - {"__dict__", subtype_dict, subtype_setdict, - PyDoc_STR("dictionary for instance variables")}, - {0} +/* getset definitions for common descriptors */ +static PyGetSetDef subtype_getset_dict = { + "__dict__", subtype_dict, subtype_setdict, + PyDoc_STR("dictionary for instance variables"), }; -static PyGetSetDef subtype_getsets_weakref_only[] = { - {"__weakref__", subtype_getweakref, NULL, - PyDoc_STR("list of weak references to the object")}, - {0} +static PyGetSetDef subtype_getset_weakref = { + "__weakref__", subtype_getweakref, NULL, + PyDoc_STR("list of weak references to the object"), }; static int @@ -4320,14 +4356,6 @@ type_new_slots_bases(type_new_ctx *ctx) static int type_new_slots_impl(type_new_ctx *ctx, PyObject *dict) { - /* Are slots allowed? */ - if (ctx->nslot > 0 && ctx->base->tp_itemsize != 0) { - PyErr_Format(PyExc_TypeError, - "nonempty __slots__ not supported for subtype of '%s'", - ctx->base->tp_name); - return -1; - } - if (type_new_visit_slots(ctx) < 0) { return -1; } @@ -4354,14 +4382,13 @@ type_new_slots(type_new_ctx *ctx, PyObject *dict) ctx->add_dict = 0; ctx->add_weak = 0; ctx->may_add_dict = (ctx->base->tp_dictoffset == 0); - ctx->may_add_weak = (ctx->base->tp_weaklistoffset == 0 - && ctx->base->tp_itemsize == 0); + ctx->may_add_weak = (ctx->base->tp_weaklistoffset == 0); if (ctx->slots == NULL) { if (ctx->may_add_dict) { ctx->add_dict++; } - if (ctx->may_add_weak) { + if (ctx->may_add_weak && ctx->base->tp_itemsize == 0) { ctx->add_weak++; } } @@ -4591,16 +4618,52 @@ type_new_classmethod(PyObject *dict, PyObject *attr) return 0; } +/* Add __dict__ or __weakref__ descriptor */ +static int +type_add_common_descriptor(PyInterpreterState *interp, + PyObject **cache, + PyGetSetDef *getset_def, + PyObject *dict) +{ +#ifdef Py_GIL_DISABLED + PyMutex_Lock(&interp->cached_objects.descriptor_mutex); +#endif + PyObject *descr = *cache; + if (!descr) { + descr = PyDescr_NewGetSet(&PyBaseObject_Type, getset_def); + *cache = descr; + } +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&interp->cached_objects.descriptor_mutex); +#endif + if (!descr) { + return -1; + } + if (PyDict_SetDefaultRef(dict, PyDescr_NAME(descr), descr, NULL) < 0) { + return -1; + } + return 0; +} /* Add descriptors for custom slots from __slots__, or for __dict__ */ static int -type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type) +type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type, PyObject *dict) { PyHeapTypeObject *et = (PyHeapTypeObject *)type; Py_ssize_t slotoffset = ctx->base->tp_basicsize; if (et->ht_slots != NULL) { PyMemberDef *mp = _PyHeapType_GET_MEMBERS(et); Py_ssize_t nslot = PyTuple_GET_SIZE(et->ht_slots); + int after_items = (ctx->base->tp_itemsize != 0 && + !(ctx->base->tp_flags & Py_TPFLAGS_ITEMS_AT_END)); + if (ctx->base->tp_itemsize != 0 && + !(ctx->base->tp_flags & Py_TPFLAGS_TUPLE_SUBCLASS)) + { + PyErr_Format(PyExc_TypeError, + "arbitrary __slots__ not supported for subtype of '%s'", + ctx->base->tp_name); + return -1; + } for (Py_ssize_t i = 0; i < nslot; i++, mp++) { mp->name = PyUnicode_AsUTF8( PyTuple_GET_ITEM(et->ht_slots, i)); @@ -4609,6 +4672,9 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type) } mp->type = Py_T_OBJECT_EX; mp->offset = slotoffset; + if (after_items) { + mp->flags |= _Py_AFTER_ITEMS; + } /* __dict__ and __weakref__ are already filtered out */ assert(strcmp(mp->name, "__dict__") != 0); @@ -4632,6 +4698,30 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type) type->tp_basicsize = slotoffset; type->tp_itemsize = ctx->base->tp_itemsize; type->tp_members = _PyHeapType_GET_MEMBERS(et); + + PyInterpreterState *interp = _PyInterpreterState_GET(); + + if (type->tp_dictoffset) { + if (type_add_common_descriptor( + interp, + &interp->cached_objects.dict_descriptor, + &subtype_getset_dict, + dict) < 0) + { + return -1; + } + } + if (type->tp_weaklistoffset) { + if (type_add_common_descriptor( + interp, + &interp->cached_objects.weakref_descriptor, + &subtype_getset_weakref, + dict) < 0) + { + return -1; + } + } + return 0; } @@ -4639,18 +4729,7 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type) static void type_new_set_slots(const type_new_ctx *ctx, PyTypeObject *type) { - if (type->tp_weaklistoffset && type->tp_dictoffset) { - type->tp_getset = subtype_getsets_full; - } - else if (type->tp_weaklistoffset && !type->tp_dictoffset) { - type->tp_getset = subtype_getsets_weakref_only; - } - else if (!type->tp_weaklistoffset && type->tp_dictoffset) { - type->tp_getset = subtype_getsets_dict_only; - } - else { - type->tp_getset = NULL; - } + type->tp_getset = NULL; /* Special case some slots */ if (type->tp_dictoffset != 0 || ctx->nslot > 0) { @@ -4755,7 +4834,7 @@ type_new_set_attrs(const type_new_ctx *ctx, PyTypeObject *type) return -1; } - if (type_new_descriptors(ctx, type) < 0) { + if (type_new_descriptors(ctx, type, dict) < 0) { return -1; } @@ -4767,6 +4846,18 @@ type_new_set_attrs(const type_new_ctx *ctx, PyTypeObject *type) if (type_new_set_classdictcell(dict) < 0) { return -1; } + +#ifdef Py_GIL_DISABLED + // enable deferred reference counting on functions and descriptors + Py_ssize_t pos = 0; + PyObject *key, *value; + while (PyDict_Next(dict, &pos, &key, &value)) { + if (PyFunction_Check(value) || Py_TYPE(value)->tp_descr_get != NULL) { + PyUnstable_Object_EnableDeferredRefcount(value); + } + } +#endif + return 0; } @@ -4805,7 +4896,7 @@ type_new_get_slots(type_new_ctx *ctx, PyObject *dict) static PyTypeObject* type_new_init(type_new_ctx *ctx) { - PyObject *dict = PyDict_Copy(ctx->orig_dict); + PyObject *dict = _PyDict_CopyAsDict(ctx->orig_dict); if (dict == NULL) { goto error; } @@ -4827,8 +4918,14 @@ type_new_init(type_new_ctx *ctx) set_tp_dict(type, dict); PyHeapTypeObject *et = (PyHeapTypeObject*)type; - et->ht_slots = ctx->slots; - ctx->slots = NULL; + if (ctx->slots && PyTuple_GET_SIZE(ctx->slots)) { + et->ht_slots = ctx->slots; + ctx->slots = NULL; + } + else { + et->ht_slots = NULL; + Py_CLEAR(ctx->slots); + } return type; @@ -4964,13 +5061,19 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) /* Parse arguments: (name, bases, dict) */ PyObject *name, *bases, *orig_dict; - if (!PyArg_ParseTuple(args, "UO!O!:type.__new__", + if (!PyArg_ParseTuple(args, "UO!O:type.__new__", &name, &PyTuple_Type, &bases, - &PyDict_Type, &orig_dict)) + &orig_dict)) { return NULL; } + if (!PyAnyDict_Check(orig_dict)) { + PyErr_Format(PyExc_TypeError, + "type.__new__() argument 3 must be dict or frozendict, not %T", + orig_dict); + return NULL; + } type_new_ctx ctx = { .metatype = metatype, @@ -5022,21 +5125,6 @@ type_vectorcall(PyObject *metatype, PyObject *const *args, return _PyObject_MakeTpCall(tstate, metatype, args, nargs, kwnames); } -/* An array of type slot offsets corresponding to Py_tp_* constants, - * for use in e.g. PyType_Spec and PyType_GetSlot. - * Each entry has two offsets: "slot_offset" and "subslot_offset". - * If is subslot_offset is -1, slot_offset is an offset within the - * PyTypeObject struct. - * Otherwise slot_offset is an offset to a pointer to a sub-slots struct - * (such as "tp_as_number"), and subslot_offset is the offset within - * that struct. - * The actual table is generated by a script. - */ -static const PySlot_Offset pyslot_offsets[] = { - {0, 0}, -#include "typeslots.inc" -}; - /* Align up to the nearest multiple of alignof(max_align_t) * (like _Py_ALIGN_UP, but for a size rather than pointer) */ @@ -5046,43 +5134,6 @@ _align_up(Py_ssize_t size) return (size + ALIGNOF_MAX_ALIGN_T - 1) & ~(ALIGNOF_MAX_ALIGN_T - 1); } -/* Given a PyType_FromMetaclass `bases` argument (NULL, type, or tuple of - * types), return a tuple of types. - */ -inline static PyObject * -get_bases_tuple(PyObject *bases_in, PyType_Spec *spec) -{ - if (!bases_in) { - /* Default: look in the spec, fall back to (type,). */ - PyTypeObject *base = &PyBaseObject_Type; // borrowed ref - PyObject *bases = NULL; // borrowed ref - const PyType_Slot *slot; - for (slot = spec->slots; slot->slot; slot++) { - switch (slot->slot) { - case Py_tp_base: - base = slot->pfunc; - break; - case Py_tp_bases: - bases = slot->pfunc; - break; - } - } - if (!bases) { - return PyTuple_Pack(1, base); - } - if (PyTuple_Check(bases)) { - return Py_NewRef(bases); - } - PyErr_SetString(PyExc_SystemError, "Py_tp_bases is not a tuple"); - return NULL; - } - if (PyTuple_Check(bases_in)) { - return Py_NewRef(bases_in); - } - // Not a tuple, should be a single type - return PyTuple_Pack(1, bases_in); -} - static inline int check_basicsize_includes_size_and_offsets(PyTypeObject* type) { @@ -5094,28 +5145,28 @@ check_basicsize_includes_size_and_offsets(PyTypeObject* type) if (type->tp_base && type->tp_base->tp_basicsize > type->tp_basicsize) { PyErr_Format(PyExc_TypeError, - "tp_basicsize for type '%s' (%d) is too small for base '%s' (%d)", + "tp_basicsize for type '%s' (%zd) is too small for base '%s' (%zd)", type->tp_name, type->tp_basicsize, type->tp_base->tp_name, type->tp_base->tp_basicsize); return 0; } if (type->tp_weaklistoffset + (Py_ssize_t)sizeof(PyObject*) > max) { PyErr_Format(PyExc_TypeError, - "weaklist offset %d is out of bounds for type '%s' (tp_basicsize = %d)", + "weaklist offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)", type->tp_weaklistoffset, type->tp_name, type->tp_basicsize); return 0; } if (type->tp_dictoffset + (Py_ssize_t)sizeof(PyObject*) > max) { PyErr_Format(PyExc_TypeError, - "dict offset %d is out of bounds for type '%s' (tp_basicsize = %d)", + "dict offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)", type->tp_dictoffset, type->tp_name, type->tp_basicsize); return 0; } if (type->tp_vectorcall_offset + (Py_ssize_t)sizeof(vectorcallfunc*) > max) { PyErr_Format(PyExc_TypeError, - "vectorcall offset %d is out of bounds for type '%s' (tp_basicsize = %d)", + "vectorcall offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)", type->tp_vectorcall_offset, type->tp_name, type->tp_basicsize); return 0; @@ -5184,10 +5235,11 @@ special_offset_from_member( return -1; } -PyObject * -PyType_FromMetaclass( - PyTypeObject *metaclass, PyObject *module, - PyType_Spec *spec, PyObject *bases_in) + +static PyObject * +type_from_slots_or_spec( + PySlot *slots, PyType_Spec *spec, + PyTypeObject *metaclass, PyObject *module, PyObject *bases_in) { /* Invariant: A non-NULL value in one of these means this function holds * a strong reference or owns allocated memory. @@ -5202,47 +5254,130 @@ PyType_FromMetaclass( int r; - /* Prepare slots that need special handling. - * Keep in mind that a slot can be given multiple times: - * if that would cause trouble (leaks, UB, ...), raise an exception. - */ + /* First pass of slots */ - const PyType_Slot *slot; Py_ssize_t nmembers = 0; const PyMemberDef *weaklistoffset_member = NULL; const PyMemberDef *dictoffset_member = NULL; const PyMemberDef *vectorcalloffset_member = NULL; - char *res_start; + Py_ssize_t basicsize = 0; + Py_ssize_t extra_basicsize = 0; + Py_ssize_t itemsize = 0; + int flags = 0; + void *token = NULL; - for (slot = spec->slots; slot->slot; slot++) { - if (slot->slot < 0 - || (size_t)slot->slot >= Py_ARRAY_LENGTH(pyslot_offsets)) { - PyErr_SetString(PyExc_RuntimeError, "invalid slot offset"); - goto finally; + bool have_relative_members = false; + Py_ssize_t max_relative_offset = 0; + + PyObject *bases_slot = NULL; /* borrowed from the slots */ + + _PySlotIterator it; + + if (spec) { + assert(!slots); + if (spec->basicsize > 0) { + basicsize = spec->basicsize; } - switch (slot->slot) { - case Py_tp_members: - if (nmembers != 0) { + if (spec->basicsize < 0) { + extra_basicsize = -spec->basicsize; + } + itemsize = spec->itemsize; + flags = spec->flags; + _PySlotIterator_InitLegacy(&it, spec->slots, _PySlot_KIND_TYPE); + it.name = spec->name; + } + else { + assert(!spec); + assert(!metaclass); + assert(!module); + assert(!bases_in); + _PySlotIterator_Init(&it, slots, _PySlot_KIND_TYPE); + } + + #define NO_SPEC \ + if (spec) { \ + PyErr_Format( \ + PyExc_SystemError, \ + "%s must not be used with PyType_Spec", \ + _PySlot_GetName(it.current.sl_id)); \ + goto finally; \ + } \ + ///////////////////////////////////////////////////// + + while (_PySlotIterator_Next(&it)) { + switch (it.current.sl_id) { + case Py_slot_invalid: + goto finally; + case Py_tp_name: + NO_SPEC; + it.name = it.current.sl_ptr; + break; + case Py_tp_metaclass: + NO_SPEC; + metaclass = it.current.sl_ptr; + break; + case Py_tp_module: + NO_SPEC; + module = it.current.sl_ptr; + break; + case Py_tp_bases: + bases_slot = it.current.sl_ptr; + break; + case Py_tp_base: + if (!_PySlotIterator_SawSlot(&it, Py_tp_bases)) { + bases_slot = it.current.sl_ptr; + } + break; + case Py_tp_basicsize: + NO_SPEC; + basicsize = it.current.sl_size; + if (basicsize <= 0) { + PyErr_SetString( + PyExc_SystemError, + "Py_tp_basicsize must be positive"); + goto finally; + } + break; + case Py_tp_extra_basicsize: + NO_SPEC; + extra_basicsize = it.current.sl_size; + if (extra_basicsize <= 0) { + PyErr_SetString( + PyExc_SystemError, + "Py_tp_extra_basicsize must be positive"); + goto finally; + } + break; + case Py_tp_itemsize: + NO_SPEC; + itemsize = it.current.sl_size; + if (itemsize <= 0) { PyErr_SetString( PyExc_SystemError, - "Multiple Py_tp_members slots are not supported."); + "Py_tp_itemsize must be positive"); goto finally; } - for (const PyMemberDef *memb = slot->pfunc; memb->name != NULL; memb++) { + break; + case Py_tp_flags: + NO_SPEC; + flags = (int)it.current.sl_uint64; + break; + case Py_tp_members: + for (const PyMemberDef *memb = it.current.sl_ptr; + memb->name != NULL; + memb++) + { nmembers++; if (memb->flags & Py_RELATIVE_OFFSET) { - if (spec->basicsize > 0) { - PyErr_SetString( - PyExc_SystemError, - "With Py_RELATIVE_OFFSET, basicsize must be negative."); - goto finally; - } - if (memb->offset < 0 || memb->offset >= -spec->basicsize) { + if (memb->offset < 0) { PyErr_SetString( PyExc_SystemError, - "Member offset out of range (0..-basicsize)"); + "Member offset must not be negative"); goto finally; } + have_relative_members = true; + max_relative_offset = Py_MAX(max_relative_offset, + memb->offset); } if (strcmp(memb->name, "__weaklistoffset__") == 0) { weaklistoffset_member = memb; @@ -5255,43 +5390,86 @@ PyType_FromMetaclass( } } break; + case Py_tp_token: + token = it.current.sl_ptr; + if (token == Py_TP_USE_SPEC) { + if (!spec) { + PyErr_SetString( + PyExc_SystemError, + "Py_tp_token: Py_TP_USE_SPEC (NULL) can only be " + "used with PyType_Spec"); + goto finally; + } + token = spec; + } + break; case Py_tp_doc: /* For the docstring slot, which usually points to a static string literal, we need to make a copy */ - if (tp_doc != NULL) { - PyErr_SetString( - PyExc_SystemError, - "Multiple Py_tp_doc slots are not supported."); - goto finally; - } - if (slot->pfunc == NULL) { + if (it.current.sl_ptr == NULL) { PyMem_Free(tp_doc); tp_doc = NULL; } else { - size_t len = strlen(slot->pfunc)+1; + size_t len = strlen(it.current.sl_ptr)+1; tp_doc = PyMem_Malloc(len); if (tp_doc == NULL) { PyErr_NoMemory(); goto finally; } - memcpy(tp_doc, slot->pfunc, len); + memcpy(tp_doc, it.current.sl_ptr, len); } break; } } + #undef NO_SPEC - /* Prepare the type name and qualname */ + /* Required slots & bad combinations */ - if (spec->name == NULL) { - PyErr_SetString(PyExc_SystemError, - "Type spec does not define the name field."); + if (it.name == NULL) { + if (spec) { + PyErr_SetString(PyExc_SystemError, + "Type spec does not define the name field."); + } + else { + PyErr_SetString(PyExc_SystemError, + "Py_tp_name slot is required."); + } goto finally; } - const char *s = strrchr(spec->name, '.'); + if (_PySlotIterator_SawSlot(&it, Py_tp_basicsize) + && _PySlotIterator_SawSlot(&it, Py_tp_extra_basicsize)) + { + PyErr_Format( + PyExc_SystemError, + "type %s: Py_tp_basicsize and Py_tp_extra_basicsize are " + "mutually exclusive", + it.name); + goto finally; + } + + if (have_relative_members) { + if (!extra_basicsize) { + PyErr_SetString( + PyExc_SystemError, + "With Py_RELATIVE_OFFSET, basicsize must be extended"); + goto finally; + } + if (max_relative_offset >= extra_basicsize) { + PyErr_SetString( + PyExc_SystemError, + "Member offset out of range (0..extra_basicsize)"); + goto finally; + } + } + + /* Prepare the type name and qualname */ + + assert(it.name); + const char *s = strrchr(it.name, '.'); if (s == NULL) { - s = spec->name; + s = it.name; } else { s++; @@ -5302,7 +5480,7 @@ PyType_FromMetaclass( goto finally; } - /* Copy spec->name to a buffer we own. + /* Copy the name to a buffer we own. * * Unfortunately, we can't use tp_name directly (with some * flag saying that it should be deallocated with the type), @@ -5311,28 +5489,42 @@ PyType_FromMetaclass( * So, we use a separate buffer, _ht_tpname, that's always * deallocated with the type (if it's non-NULL). */ - Py_ssize_t name_buf_len = strlen(spec->name) + 1; + Py_ssize_t name_buf_len = strlen(it.name) + 1; _ht_tpname = PyMem_Malloc(name_buf_len); if (_ht_tpname == NULL) { goto finally; } - memcpy(_ht_tpname, spec->name, name_buf_len); + memcpy(_ht_tpname, it.name, name_buf_len); /* Get a tuple of bases. * bases is a strong reference (unlike bases_in). + * (This is convoluted for backwards compatibility -- preserving priority + * of the various ways to specify bases) */ - bases = get_bases_tuple(bases_in, spec); + if (!bases_in) { + bases_in = bases_slot; + } + if (bases_in) { + if (PyTuple_Check(bases_in)) { + bases = Py_NewRef(bases_in); + } + else { + bases = PyTuple_Pack(1, bases_in); + } + } + else { + bases = PyTuple_Pack(1, &PyBaseObject_Type); + } if (!bases) { goto finally; } - /* If this is an immutable type, check if all bases are also immutable, - * and (for now) fire a deprecation warning if not. + /* If this is an immutable type, check if all bases are also immutable. * (This isn't necessary for static types: those can't have heap bases, * and only heap types can be mutable.) */ - if (spec->flags & Py_TPFLAGS_IMMUTABLETYPE) { - if (check_immutable_bases(spec->name, bases, 0) < 0) { + if (flags & Py_TPFLAGS_IMMUTABLETYPE) { + if (check_immutable_bases(it.name, bases, 0) < 0) { goto finally; } } @@ -5370,20 +5562,16 @@ PyType_FromMetaclass( /* Calculate sizes */ - Py_ssize_t basicsize = spec->basicsize; - Py_ssize_t type_data_offset = spec->basicsize; - if (basicsize == 0) { - /* Inherit */ - basicsize = base->tp_basicsize; - } - else if (basicsize < 0) { + Py_ssize_t type_data_offset = basicsize; + if (extra_basicsize) { /* Extend */ + assert(basicsize == 0); type_data_offset = _align_up(base->tp_basicsize); - basicsize = type_data_offset + _align_up(-spec->basicsize); + basicsize = type_data_offset + _align_up(extra_basicsize); /* Inheriting variable-sized types is limited */ if (base->tp_itemsize - && !((base->tp_flags | spec->flags) & Py_TPFLAGS_ITEMS_AT_END)) + && !((base->tp_flags | flags) & Py_TPFLAGS_ITEMS_AT_END)) { PyErr_SetString( PyExc_SystemError, @@ -5391,8 +5579,10 @@ PyType_FromMetaclass( goto finally; } } - - Py_ssize_t itemsize = spec->itemsize; + if (basicsize == 0) { + /* Inherit */ + basicsize = base->tp_basicsize; + } /* Compute special offsets */ @@ -5424,11 +5614,10 @@ PyType_FromMetaclass( if (res == NULL) { goto finally; } - res_start = (char*)res; type = &res->ht_type; /* The flags must be initialized early, before the GC traverses us */ - type_set_flags(type, spec->flags | Py_TPFLAGS_HEAPTYPE); + type_set_flags(type, flags | Py_TPFLAGS_HEAPTYPE); res->ht_module = Py_XNewRef(module); @@ -5457,15 +5646,20 @@ PyType_FromMetaclass( res->_ht_tpname = _ht_tpname; _ht_tpname = NULL; // Give ownership to the type + res->ht_token = token; + /* Copy the sizes */ type->tp_basicsize = basicsize; type->tp_itemsize = itemsize; - /* Copy all the ordinary slots */ + /* Second pass of slots: copy most of them into the type */ - for (slot = spec->slots; slot->slot; slot++) { - switch (slot->slot) { + _PySlotIterator_Rewind(&it, spec ? (void*)spec->slots : (void*)slots); + while (_PySlotIterator_Next(&it)) { + switch (it.current.sl_id) { + case Py_slot_invalid: + goto finally; case Py_tp_base: case Py_tp_bases: case Py_tp_doc: @@ -5475,7 +5669,7 @@ PyType_FromMetaclass( { /* Move the slots to the heap type itself */ size_t len = Py_TYPE(type)->tp_itemsize * nmembers; - memcpy(_PyHeapType_GET_MEMBERS(res), slot->pfunc, len); + memcpy(_PyHeapType_GET_MEMBERS(res), it.current.sl_ptr, len); type->tp_members = _PyHeapType_GET_MEMBERS(res); PyMemberDef *memb; Py_ssize_t i; @@ -5489,26 +5683,8 @@ PyType_FromMetaclass( } } break; - case Py_tp_token: - { - res->ht_token = slot->pfunc == Py_TP_USE_SPEC ? spec : slot->pfunc; - } - break; default: - { - /* Copy other slots directly */ - PySlot_Offset slotoffsets = pyslot_offsets[slot->slot]; - short slot_offset = slotoffsets.slot_offset; - if (slotoffsets.subslot_offset == -1) { - /* Set a slot in the main PyTypeObject */ - *(void**)((char*)res_start + slot_offset) = slot->pfunc; - } - else { - void *procs = *(void**)((char*)res_start + slot_offset); - short subslot_offset = slotoffsets.subslot_offset; - *(void**)((char*)procs + subslot_offset) = slot->pfunc; - } - } + _PySlot_heaptype_apply_field_slot(res, it.current); break; } } @@ -5574,10 +5750,10 @@ PyType_FromMetaclass( goto finally; } if (r == 0) { - s = strrchr(spec->name, '.'); + s = strrchr(it.name, '.'); if (s != NULL) { PyObject *modname = PyUnicode_FromStringAndSize( - spec->name, (Py_ssize_t)(s - spec->name)); + it.name, (Py_ssize_t)(s - it.name)); if (modname == NULL) { goto finally; } @@ -5590,7 +5766,7 @@ PyType_FromMetaclass( else { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, "builtin type %.200s has no __module__ attribute", - spec->name)) + it.name)) goto finally; } } @@ -5612,22 +5788,36 @@ PyType_FromMetaclass( return (PyObject*)res; } +PyObject * +PyType_FromSlots(PySlot *slots) +{ + return type_from_slots_or_spec(slots, NULL, NULL, NULL, NULL); +} + +PyObject * +PyType_FromMetaclass( + PyTypeObject *metaclass, PyObject *module, + PyType_Spec *spec, PyObject *bases) +{ + return type_from_slots_or_spec(NULL, spec, metaclass, module, bases); +} + PyObject * PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) { - return PyType_FromMetaclass(NULL, module, spec, bases); + return type_from_slots_or_spec(NULL, spec, NULL, module, bases); } PyObject * PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) { - return PyType_FromMetaclass(NULL, NULL, spec, bases); + return type_from_slots_or_spec(NULL, spec, NULL, NULL, bases); } PyObject * PyType_FromSpec(PyType_Spec *spec) { - return PyType_FromMetaclass(NULL, NULL, spec, NULL); + return type_from_slots_or_spec(NULL, spec, NULL, NULL, NULL); } PyObject * @@ -5649,32 +5839,21 @@ PyType_GetModuleName(PyTypeObject *type) } void * -PyType_GetSlot(PyTypeObject *type, int slot) +PyType_GetSlot(PyTypeObject *type, int slot_in) { - void *parent_slot; - int slots_len = Py_ARRAY_LENGTH(pyslot_offsets); - - if (slot <= 0 || slot >= slots_len) { - PyErr_BadInternalCall(); - return NULL; - } - int slot_offset = pyslot_offsets[slot].slot_offset; - - if (slot_offset >= (int)sizeof(PyTypeObject)) { - if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { - return NULL; - } - } + uint16_t slot = _PySlot_resolve_type_slot(slot_in); + return _PySlot_type_getslot(type, slot); +} - parent_slot = *(void**)((char*)type + slot_offset); - if (parent_slot == NULL) { +PyObject * +PyType_GetModule_DuringGC(PyTypeObject *type) +{ + assert(PyType_Check(type)); + if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { return NULL; } - /* Return slot directly if we have no sub slot. */ - if (pyslot_offsets[slot].subslot_offset == -1) { - return parent_slot; - } - return *(void**)((char*)parent_slot + pyslot_offsets[slot].subslot_offset); + PyHeapTypeObject* et = (PyHeapTypeObject*)type; + return et->ht_module; } PyObject * @@ -5698,7 +5877,16 @@ PyType_GetModule(PyTypeObject *type) return NULL; } return et->ht_module; +} +void * +PyType_GetModuleState_DuringGC(PyTypeObject *type) +{ + PyObject *m = PyType_GetModule_DuringGC(type); + if (m == NULL) { + return NULL; + } + return _PyModule_GetState(m); } void * @@ -5711,24 +5899,23 @@ PyType_GetModuleState(PyTypeObject *type) return _PyModule_GetState(m); } - -/* Get the module of the first superclass where the module has the - * given PyModuleDef. +/* Return borrowed ref to the module of the first superclass where the module + * has the given token. */ PyObject * -PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def) +PyType_GetModuleByToken_DuringGC(PyTypeObject *type, const void *token) { assert(PyType_Check(type)); if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { // type_ready_mro() ensures that no heap type is // contained in a static type MRO. - goto error; + return NULL; } else { PyHeapTypeObject *ht = (PyHeapTypeObject*)type; PyObject *module = ht->ht_module; - if (module && _PyModule_GetDef(module) == def) { + if (module && _PyModule_GetToken(module) == token) { return module; } } @@ -5756,22 +5943,34 @@ PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def) PyHeapTypeObject *ht = (PyHeapTypeObject*)super; PyObject *module = ht->ht_module; - if (module && _PyModule_GetDef(module) == def) { + if (module && _PyModule_GetToken(module) == token) { res = module; break; } } END_TYPE_LOCK(); - if (res != NULL) { - return res; + return res; +} + +PyObject * +PyType_GetModuleByToken(PyTypeObject *type, const void *token) +{ + return Py_XNewRef(PyType_GetModuleByDef(type, (PyModuleDef *)token)); +} + +PyObject * +PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def) +{ + PyObject *mod = PyType_GetModuleByToken_DuringGC(type, def); + if (!mod) { + PyErr_Format( + PyExc_TypeError, + "PyType_GetModuleByDef: No superclass of '%s' has the given module", + type->tp_name); + return NULL; } -error: - PyErr_Format( - PyExc_TypeError, - "PyType_GetModuleByDef: No superclass of '%s' has the given module", - type->tp_name); - return NULL; + return mod; } @@ -5800,20 +5999,15 @@ get_base_by_token_recursive(PyObject *bases, void *token) } int -PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result) +PyType_GetBaseByToken_DuringGC(PyTypeObject *type, void *token, PyTypeObject **result) { if (result != NULL) { *result = NULL; } - if (token == NULL) { - PyErr_Format(PyExc_SystemError, - "PyType_GetBaseByToken called with token=NULL"); return -1; } if (!PyType_Check(type)) { - PyErr_Format(PyExc_TypeError, - "expected a type, got a '%T' object", type); return -1; } @@ -5825,7 +6019,7 @@ PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result) if (((PyHeapTypeObject*)type)->ht_token == token) { found: if (result != NULL) { - *result = (PyTypeObject *)Py_NewRef(type); + *result = type; } return 1; } @@ -5859,14 +6053,44 @@ PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result) return 0; } +int +PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result) +{ + if (result != NULL) { + *result = NULL; + } + if (token == NULL) { + PyErr_Format(PyExc_SystemError, + "PyType_GetBaseByToken called with token=NULL"); + return -1; + } + if (!PyType_Check(type)) { + PyErr_Format(PyExc_TypeError, + "expected a type, got a '%T' object", type); + return -1; + } + + int res = PyType_GetBaseByToken_DuringGC(type, token, result); + if (res > 0 && result) { + Py_INCREF(*result); + } + return res; +} + void * -PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls) +PyObject_GetTypeData_DuringGC(PyObject *obj, PyTypeObject *cls) { assert(PyObject_TypeCheck(obj, cls)); return (char *)obj + _align_up(cls->tp_base->tp_basicsize); } +void * +PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls) +{ + return PyObject_GetTypeData_DuringGC(obj, cls); +} + Py_ssize_t PyType_GetTypeDataSize(PyTypeObject *cls) { @@ -5877,28 +6101,43 @@ PyType_GetTypeDataSize(PyTypeObject *cls) return result; } -void * -PyObject_GetItemData(PyObject *obj) +static inline void * +getitemdata(PyObject *obj, bool raise) { - if (!PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_ITEMS_AT_END)) { - PyErr_Format(PyExc_TypeError, - "type '%s' does not have Py_TPFLAGS_ITEMS_AT_END", - Py_TYPE(obj)->tp_name); + if (!_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_ITEMS_AT_END)) { + if (raise) { + PyErr_Format(PyExc_TypeError, + "type '%T' does not have Py_TPFLAGS_ITEMS_AT_END", + obj); + } return NULL; } return (char *)obj + Py_TYPE(obj)->tp_basicsize; } +void * +PyObject_GetItemData_DuringGC(PyObject *obj) +{ + return getitemdata(obj, false); +} + +void * +PyObject_GetItemData(PyObject *obj) +{ + return getitemdata(obj, true); +} + /* Internal API to look for a name through the MRO, bypassing the method cache. - This returns a borrowed reference, and might set an exception. - 'error' is set to: -1: error with exception; 1: error without exception; 0: ok */ -static PyObject * -find_name_in_mro(PyTypeObject *type, PyObject *name, int *error) + The result is stored as a _PyStackRef in `out`. It never set an exception. + Returns -1 if there was an error, 0 if the name was not found, and 1 if + the name was found. */ +static int +find_name_in_mro(PyTypeObject *type, PyObject *name, _PyStackRef *out) { Py_hash_t hash = _PyObject_HashFast(name); if (hash == -1) { - *error = -1; - return NULL; + PyErr_Clear(); + return -1; } /* Look in tp_dict of types in MRO */ @@ -5906,37 +6145,42 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error) if (mro == NULL) { if (!is_readying(type)) { if (PyType_Ready(type) < 0) { - *error = -1; - return NULL; + PyErr_Clear(); + return -1; } mro = lookup_tp_mro(type); } if (mro == NULL) { - *error = 1; - return NULL; + return -1; } } - PyObject *res = NULL; + int res = 0; + PyThreadState *tstate = _PyThreadState_GET(); /* Keep a strong reference to mro because type->tp_mro can be replaced during dict lookup, e.g. when comparing to non-string keys. */ - Py_INCREF(mro); + _PyCStackRef mro_ref; + _PyThreadState_PushCStackRef(tstate, &mro_ref); + mro_ref.ref = PyStackRef_FromPyObjectNew(mro); Py_ssize_t n = PyTuple_GET_SIZE(mro); for (Py_ssize_t i = 0; i < n; i++) { PyObject *base = PyTuple_GET_ITEM(mro, i); PyObject *dict = lookup_tp_dict(_PyType_CAST(base)); assert(dict && PyDict_Check(dict)); - if (_PyDict_GetItemRef_KnownHash((PyDictObject *)dict, name, hash, &res) < 0) { - *error = -1; + Py_ssize_t ix = _Py_dict_lookup_threadsafe_stackref( + (PyDictObject *)dict, name, hash, out); + if (ix == DKIX_ERROR) { + PyErr_Clear(); + res = -1; goto done; } - if (res != NULL) { + if (!PyStackRef_IsNull(*out)) { + res = 1; break; } } - *error = 0; done: - Py_DECREF(mro); + _PyThreadState_PopCStackRef(tstate, &mro_ref); return res; } @@ -5962,7 +6206,7 @@ static PyObject * update_cache(struct type_cache_entry *entry, PyObject *name, unsigned int version_tag, PyObject *value) { _Py_atomic_store_ptr_relaxed(&entry->value, value); /* borrowed */ - assert(_PyASCIIObject_CAST(name)->hash != -1); + assert(PyUnstable_Unicode_GET_CACHED_HASH(name) != -1); OBJECT_STAT_INC_COND(type_cache_collisions, entry->name != Py_None && entry->name != name); // We're releasing this under the lock for simplicity sake because it's always a // exact unicode object or Py_None so it's safe to do so. @@ -6040,6 +6284,14 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve return PyStackRef_AsPyObjectSteal(out); } +static int +should_assign_version_tag(PyTypeObject *type, PyObject *name, unsigned int version_tag) +{ + return (version_tag == 0 + && FT_ATOMIC_LOAD_UINT16_RELAXED(type->tp_versions_used) < MAX_VERSIONS_PER_CLASS + && MCACHE_CACHEABLE_NAME(name)); +} + unsigned int _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef *out) { @@ -6088,52 +6340,39 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef /* We may end up clearing live exceptions below, so make sure it's ours. */ assert(!PyErr_Occurred()); - // We need to atomically do the lookup and capture the version before - // anyone else can modify our mro or mutate the type. - - PyObject *res; - int error; + int res; PyInterpreterState *interp = _PyInterpreterState_GET(); - int has_version = 0; - unsigned int assigned_version = 0; - BEGIN_TYPE_LOCK(); - // We must assign the version before doing the lookup. If - // find_name_in_mro() blocks and releases the critical section - // then the type version can change. - if (MCACHE_CACHEABLE_NAME(name)) { - has_version = assign_version_tag(interp, type); - assigned_version = type->tp_version_tag; - } - res = find_name_in_mro(type, name, &error); - END_TYPE_LOCK(); + + unsigned int version_tag = FT_ATOMIC_LOAD_UINT(type->tp_version_tag); + if (should_assign_version_tag(type, name, version_tag)) { + BEGIN_TYPE_LOCK(); + assign_version_tag(interp, type); + version_tag = type->tp_version_tag; + res = find_name_in_mro(type, name, out); + END_TYPE_LOCK(); + } + else { + res = find_name_in_mro(type, name, out); + } /* Only put NULL results into cache if there was no error. */ - if (error) { - /* It's not ideal to clear the error condition, - but this function is documented as not setting - an exception, and I don't want to change that. - E.g., when PyType_Ready() can't proceed, it won't - set the "ready" flag, so future attempts to ready - the same type will call it again -- hopefully - in a context that propagates the exception out. - */ - if (error == -1) { - PyErr_Clear(); - } + if (res < 0) { *out = PyStackRef_NULL; return 0; } - if (has_version) { + if (version_tag == 0 || !MCACHE_CACHEABLE_NAME(name)) { + return 0; + } + + PyObject *res_obj = PyStackRef_AsPyObjectBorrow(*out); #if Py_GIL_DISABLED - update_cache_gil_disabled(entry, name, assigned_version, res); + update_cache_gil_disabled(entry, name, version_tag, res_obj); #else - PyObject *old_value = update_cache(entry, name, assigned_version, res); - Py_DECREF(old_value); + PyObject *old_value = update_cache(entry, name, version_tag, res_obj); + Py_DECREF(old_value); #endif - } - *out = res ? PyStackRef_FromPyObjectSteal(res) : PyStackRef_NULL; - return has_version ? assigned_version : 0; + return version_tag; } /* Internal API to look for a name through the MRO. @@ -6256,9 +6495,25 @@ set_flags_recursive(PyTypeObject *self, unsigned long mask, unsigned long flags) void _PyType_SetFlagsRecursive(PyTypeObject *self, unsigned long mask, unsigned long flags) { + BEGIN_TYPE_LOCK(); + /* Ideally, changing flags and invalidating the old version tag would + happen in one step. But type_modified_unlocked() is re-entrant and + cannot run with the world stopped, so we must invalidate first. + Immutable/static-builtin types are skipped because + set_flags_recursive() does not modify them. */ + if (!PyType_HasFeature(self, Py_TPFLAGS_IMMUTABLETYPE) && + (self->tp_flags & mask) != flags) + { + type_modified_unlocked(self); + } + /* Keep TYPE_LOCK held while waiting for stop-the-world so no thread + can reassign a version tag before the flag update. */ + type_lock_prevent_release(); types_stop_world(); set_flags_recursive(self, mask, flags); types_start_world(); + type_lock_allow_release(); + END_TYPE_LOCK(); } /* This is similar to PyObject_GenericGetAttr(), @@ -6274,102 +6529,153 @@ _PyType_SetFlagsRecursive(PyTypeObject *self, unsigned long mask, unsigned long */ PyObject * -_Py_type_getattro_impl(PyTypeObject *type, PyObject *name, int * suppress_missing_attribute) +_Py_type_getattro_impl(PyTypeObject *type, PyObject *name, int *suppress_missing_attribute) +{ + _PyStackRef ref = _Py_type_getattro_stackref(type, name, suppress_missing_attribute); + if (PyStackRef_IsNull(ref)) { + return NULL; + } + return PyStackRef_AsPyObjectSteal(ref); +} + +/* This is similar to PyObject_GenericGetAttr(), + but uses _PyType_LookupRef() instead of just looking in type->tp_dict. */ +PyObject * +_Py_type_getattro(PyObject *tp, PyObject *name) +{ + PyTypeObject *type = PyTypeObject_CAST(tp); + return _Py_type_getattro_impl(type, name, NULL); +} + +/* Like _Py_type_getattro but returns a _PyStackRef. + This can return a deferred reference in the free-threaded build + when the attribute is found without going through a descriptor. + + suppress_missing_attribute (optional): + * NULL: do not suppress the exception + * Non-zero pointer: suppress the PyExc_AttributeError and + set *suppress_missing_attribute to 1 to signal we are returning NULL while + having suppressed the exception (other exceptions are not suppressed) +*/ +_PyStackRef +_Py_type_getattro_stackref(PyTypeObject *type, PyObject *name, + int *suppress_missing_attribute) { PyTypeObject *metatype = Py_TYPE(type); - PyObject *meta_attribute, *attribute; - descrgetfunc meta_get; - PyObject* res; + descrgetfunc meta_get = NULL; if (!PyUnicode_Check(name)) { PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", Py_TYPE(name)->tp_name); - return NULL; + return PyStackRef_NULL; } /* Initialize this type (we'll assume the metatype is initialized) */ if (!_PyType_IsReady(type)) { if (PyType_Ready(type) < 0) - return NULL; + return PyStackRef_NULL; } - /* No readable descriptor found yet */ - meta_get = NULL; + /* Set up GC-visible stack refs */ + _PyCStackRef result_ref, meta_attribute_ref, attribute_ref; + PyThreadState *tstate = _PyThreadState_GET(); + _PyThreadState_PushCStackRef(tstate, &result_ref); + _PyThreadState_PushCStackRef(tstate, &meta_attribute_ref); + _PyThreadState_PushCStackRef(tstate, &attribute_ref); /* Look for the attribute in the metatype */ - meta_attribute = _PyType_LookupRef(metatype, name); + _PyType_LookupStackRefAndVersion(metatype, name, &meta_attribute_ref.ref); - if (meta_attribute != NULL) { - meta_get = Py_TYPE(meta_attribute)->tp_descr_get; + if (!PyStackRef_IsNull(meta_attribute_ref.ref)) { + PyObject *meta_attr_obj = PyStackRef_AsPyObjectBorrow(meta_attribute_ref.ref); + meta_get = Py_TYPE(meta_attr_obj)->tp_descr_get; - if (meta_get != NULL && PyDescr_IsData(meta_attribute)) { + if (meta_get != NULL && PyDescr_IsData(meta_attr_obj)) { /* Data descriptors implement tp_descr_set to intercept * writes. Assume the attribute is not overridden in * type's tp_dict (and bases): call the descriptor now. */ - res = meta_get(meta_attribute, (PyObject *)type, - (PyObject *)metatype); - Py_DECREF(meta_attribute); - return res; + PyObject *res = meta_get(meta_attr_obj, (PyObject *)type, + (PyObject *)metatype); + if (res != NULL) { + result_ref.ref = PyStackRef_FromPyObjectSteal(res); + } + goto done; } } /* No data descriptor found on metatype. Look in tp_dict of this * type and its bases */ - attribute = _PyType_LookupRef(type, name); - if (attribute != NULL) { + _PyType_LookupStackRefAndVersion(type, name, &attribute_ref.ref); + if (!PyStackRef_IsNull(attribute_ref.ref)) { /* Implement descriptor functionality, if any */ - descrgetfunc local_get = Py_TYPE(attribute)->tp_descr_get; + PyObject *attr_obj = PyStackRef_AsPyObjectBorrow(attribute_ref.ref); + descrgetfunc local_get = Py_TYPE(attr_obj)->tp_descr_get; - Py_XDECREF(meta_attribute); + /* Release meta_attribute early since we found in local dict */ + PyStackRef_CLEAR(meta_attribute_ref.ref); if (local_get != NULL) { + /* Special case staticmethod to avoid descriptor call overhead. + * staticmethod.__get__ just returns the wrapped callable. */ + if (Py_TYPE(attr_obj) == &PyStaticMethod_Type) { + PyObject *callable = _PyStaticMethod_GetFunc(attr_obj); + if (callable) { + result_ref.ref = PyStackRef_FromPyObjectNew(callable); + goto done; + } + } /* NULL 2nd argument indicates the descriptor was * found on the target object itself (or a base) */ - res = local_get(attribute, (PyObject *)NULL, - (PyObject *)type); - Py_DECREF(attribute); - return res; + PyObject *res = local_get(attr_obj, (PyObject *)NULL, + (PyObject *)type); + if (res != NULL) { + result_ref.ref = PyStackRef_FromPyObjectSteal(res); + } + goto done; } - return attribute; + /* No descriptor, return the attribute directly */ + result_ref.ref = attribute_ref.ref; + attribute_ref.ref = PyStackRef_NULL; + goto done; } /* No attribute found in local __dict__ (or bases): use the * descriptor from the metatype, if any */ if (meta_get != NULL) { - PyObject *res; - res = meta_get(meta_attribute, (PyObject *)type, - (PyObject *)metatype); - Py_DECREF(meta_attribute); - return res; + PyObject *meta_attr_obj = PyStackRef_AsPyObjectBorrow(meta_attribute_ref.ref); + PyObject *res = meta_get(meta_attr_obj, (PyObject *)type, + (PyObject *)metatype); + if (res != NULL) { + result_ref.ref = PyStackRef_FromPyObjectSteal(res); + } + goto done; } /* If an ordinary attribute was found on the metatype, return it now */ - if (meta_attribute != NULL) { - return meta_attribute; + if (!PyStackRef_IsNull(meta_attribute_ref.ref)) { + result_ref.ref = meta_attribute_ref.ref; + meta_attribute_ref.ref = PyStackRef_NULL; + goto done; } /* Give up */ if (suppress_missing_attribute == NULL) { PyErr_Format(PyExc_AttributeError, - "type object '%.100s' has no attribute '%U'", - type->tp_name, name); - } else { + "type object '%.100s' has no attribute '%U'", + type->tp_name, name); + } + else { // signal the caller we have not set an PyExc_AttributeError and gave up *suppress_missing_attribute = 1; } - return NULL; -} -/* This is similar to PyObject_GenericGetAttr(), - but uses _PyType_LookupRef() instead of just looking in type->tp_dict. */ -PyObject * -_Py_type_getattro(PyObject *tp, PyObject *name) -{ - PyTypeObject *type = PyTypeObject_CAST(tp); - return _Py_type_getattro_impl(type, name, NULL); +done: + _PyThreadState_PopCStackRef(tstate, &attribute_ref); + _PyThreadState_PopCStackRef(tstate, &meta_attribute_ref); + return _PyThreadState_PopCStackRefSteal(tstate, &result_ref); } // Called by type_setattro(). Updates both the type dict and @@ -6472,6 +6778,17 @@ type_setattro(PyObject *self, PyObject *name, PyObject *value) assert(!_PyType_HasFeature(metatype, Py_TPFLAGS_INLINE_VALUES)); assert(!_PyType_HasFeature(metatype, Py_TPFLAGS_MANAGED_DICT)); +#ifdef Py_GIL_DISABLED + // gh-139103: Enable deferred refcounting for functions and descriptors + // assigned to type objects. This is important for `dataclass.__init__`, + // which is generated dynamically, and for descriptor scaling on + // free-threaded builds. + if (value != NULL && (PyFunction_Check(value) || Py_TYPE(value)->tp_descr_get != NULL)) + { + PyUnstable_Object_EnableDeferredRefcount(value); + } +#endif + PyObject *old_value = NULL; PyObject *descr = _PyType_LookupRef(metatype, name); if (descr != NULL) { @@ -6639,6 +6956,14 @@ _PyStaticType_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type) } +void +_PyTypes_FiniCachedDescriptors(PyInterpreterState *interp) +{ + Py_CLEAR(interp->cached_objects.dict_descriptor); + Py_CLEAR(interp->cached_objects.weakref_descriptor); +} + + static void type_dealloc(PyObject *self) { @@ -6647,6 +6972,33 @@ type_dealloc(PyObject *self) // Assert this is a heap-allocated type object _PyObject_ASSERT((PyObject *)type, type->tp_flags & Py_TPFLAGS_HEAPTYPE); + // Notify type watchers before teardown. The type object is still fully + // intact at this point (dict, bases, mro, name are all valid), so + // callbacks can safely inspect it. + if (type->tp_watched) { + _PyObject_ResurrectStart(self); + PyInterpreterState *interp = _PyInterpreterState_GET(); + int bits = type->tp_watched; + int i = 0; + while (bits) { + assert(i < TYPE_MAX_WATCHERS); + if (bits & 1) { + PyType_WatchCallback cb = interp->type_watchers[i]; + if (cb && (cb(type) < 0)) { + PyErr_FormatUnraisable( + "Exception ignored in type watcher callback #%d " + "for %R", + i, type); + } + } + i++; + bits >>= 1; + } + if (_PyObject_ResurrectEnd(self)) { + return; // callback resurrected the object + } + } + _PyObject_GC_UNTRACK(type); type_dealloc_common(type); @@ -6671,7 +7023,7 @@ type_dealloc(PyObject *self) Py_XDECREF(et->ht_qualname); Py_XDECREF(et->ht_slots); if (et->ht_cached_keys) { - _PyDictKeys_DecRef(et->ht_cached_keys); + _PyDict_RemoveKeysForClass(et); } Py_XDECREF(et->ht_module); PyMem_Free(et->_ht_tpname); @@ -7007,12 +7359,6 @@ PyTypeObject PyType_Type = { symmetrically, __new__() complains about excess arguments unless __init__() is overridden and __new__() is not overridden (IOW, if __new__() is overridden or __init__() is not overridden). - - However, for backwards compatibility, this breaks too much code. - Therefore, in 2.6, we'll *warn* about excess arguments when both - methods are overridden; for all other cases we'll use the above - rules. - */ /* Forward */ @@ -7224,10 +7570,6 @@ compatible_with_tp_base(PyTypeObject *child) return (parent != NULL && child->tp_basicsize == parent->tp_basicsize && child->tp_itemsize == parent->tp_itemsize && - child->tp_dictoffset == parent->tp_dictoffset && - child->tp_weaklistoffset == parent->tp_weaklistoffset && - ((child->tp_flags & Py_TPFLAGS_HAVE_GC) == - (parent->tp_flags & Py_TPFLAGS_HAVE_GC)) && (child->tp_dealloc == subtype_dealloc || child->tp_dealloc == parent->tp_dealloc)); } @@ -7262,11 +7604,24 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b) } static int -compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char* attr) +compatible_flags(int setclass, PyTypeObject *origto, PyTypeObject *newto, unsigned long flags) +{ + /* For __class__ assignment, the flags should be the same. + For __bases__ assignment, the new base flags can only be set + if the original class flags are set. + */ + return setclass ? (origto->tp_flags & flags) == (newto->tp_flags & flags) + : !(~(origto->tp_flags & flags) & (newto->tp_flags & flags)); +} + +static int +compatible_for_assignment(PyTypeObject *origto, PyTypeObject *newto, + const char *attr, int setclass) { PyTypeObject *newbase, *oldbase; + PyTypeObject *oldto = setclass ? origto : origto->tp_base; - if (newto->tp_free != oldto->tp_free) { + if (setclass && newto->tp_free != oldto->tp_free) { PyErr_Format(PyExc_TypeError, "%s assignment: " "'%s' deallocator differs from '%s'", @@ -7275,6 +7630,28 @@ compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char* oldto->tp_name); return 0; } + if (!compatible_flags(setclass, origto, newto, + Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_INLINE_VALUES | + Py_TPFLAGS_PREHEADER)) + { + goto differs; + } + /* For __class__ assignment, tp_dictoffset and tp_weaklistoffset should + be the same for old and new types. + For __bases__ assignment, they can only be set in the new base + if they are set in the original class with the same value. + */ + if ((setclass || newto->tp_dictoffset) + && origto->tp_dictoffset != newto->tp_dictoffset) + { + goto differs; + } + if ((setclass || newto->tp_weaklistoffset) + && origto->tp_weaklistoffset != newto->tp_weaklistoffset) + { + goto differs; + } /* It's tricky to tell if two arbitrary types are sufficiently compatible as to be interchangeable; e.g., even if they have the same tp_basicsize, they @@ -7296,17 +7673,7 @@ compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char* !same_slots_added(newbase, oldbase))) { goto differs; } - if ((oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) != - ((newto->tp_flags & Py_TPFLAGS_INLINE_VALUES))) - { - goto differs; - } - /* The above does not check for the preheader */ - if ((oldto->tp_flags & Py_TPFLAGS_PREHEADER) == - ((newto->tp_flags & Py_TPFLAGS_PREHEADER))) - { - return 1; - } + return 1; differs: PyErr_Format(PyExc_TypeError, "%s assignment: " @@ -7383,7 +7750,7 @@ object_set_class_world_stopped(PyObject *self, PyTypeObject *newto) return -1; } - if (compatible_for_assignment(oldto, newto, "__class__")) { + if (compatible_for_assignment(oldto, newto, "__class__", 1)) { /* Changing the class will change the implicit dict keys, * so we must materialize the dictionary first. */ if (oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) { @@ -7397,7 +7764,11 @@ object_set_class_world_stopped(PyObject *self, PyTypeObject *newto) assert(_PyObject_GetManagedDict(self) == dict); - if (_PyDict_DetachFromObject(dict, self) < 0) { + int err; + Py_BEGIN_CRITICAL_SECTION(dict); + err = _PyDict_DetachFromObject(dict, self); + Py_END_CRITICAL_SECTION(); + if (err < 0) { return -1; } @@ -7437,10 +7808,15 @@ object_set_class(PyObject *self, PyObject *value, void *closure) return -1; } - types_stop_world(); + int unique = _PyObject_IsUniquelyReferenced(self); + if (!unique) { + types_stop_world(); + } PyTypeObject *oldto = Py_TYPE(self); int res = object_set_class_world_stopped(self, newto); - types_start_world(); + if (!unique) { + types_start_world(); + } if (res == 0) { if (oldto->tp_flags & Py_TPFLAGS_HEAPTYPE) { Py_DECREF(oldto); @@ -7642,7 +8018,7 @@ object_getstate_default(PyObject *obj, int required) if (PyDict_GET_SIZE(slots) > 0) { PyObject *state2; - state2 = PyTuple_Pack(2, state, slots); + state2 = _PyTuple_FromPair(state, slots); Py_DECREF(state); if (state2 == NULL) { Py_DECREF(slotnames); @@ -8225,7 +8601,15 @@ type_add_method(PyTypeObject *type, PyMethodDef *meth) descr = PyDescr_NewClassMethod(type, meth); } else if (meth->ml_flags & METH_STATIC) { - PyObject *cfunc = PyCFunction_NewEx(meth, (PyObject*)type, NULL); + PyObject *mod = type_module(type); + if (mod == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { + return -1; + } + PyErr_Clear(); + } + PyObject *cfunc = PyCFunction_NewEx(meth, (PyObject*)type, mod); + Py_XDECREF(mod); if (cfunc == NULL) { return -1; } @@ -8817,6 +9201,13 @@ type_ready_preheader(PyTypeObject *type) type->tp_name); return -1; } + if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) { + PyErr_Format(PyExc_SystemError, + "type %s has the Py_TPFLAGS_MANAGED_DICT flag " + "but not Py_TPFLAGS_HAVE_GC flag", + type->tp_name); + return -1; + } type->tp_dictoffset = -1; } if (type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) { @@ -8829,6 +9220,13 @@ type_ready_preheader(PyTypeObject *type) type->tp_name); return -1; } + if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) { + PyErr_Format(PyExc_SystemError, + "type %s has the Py_TPFLAGS_MANAGED_WEAKREF flag " + "but not Py_TPFLAGS_HAVE_GC flag", + type->tp_name); + return -1; + } type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET; } return 0; @@ -8922,6 +9320,7 @@ type_ready_inherit(PyTypeObject *type) // Inherit slots PyObject *mro = lookup_tp_mro(type); + assert(mro != NULL); Py_ssize_t n = PyTuple_GET_SIZE(mro); for (Py_ssize_t i = 1; i < n; i++) { PyObject *b = PyTuple_GET_ITEM(mro, i); @@ -9108,6 +9507,7 @@ type_ready_post_checks(PyTypeObject *type) PyErr_Format(PyExc_SystemError, "type %s has a tp_dictoffset that is too small", type->tp_name); + return -1; } } return 0; @@ -10000,7 +10400,7 @@ wrap_init(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds) } static PyObject * -tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) +tp_new_wrapper(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyTypeObject *staticbase; PyObject *arg0, *res; @@ -10012,13 +10412,13 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) } PyTypeObject *type = (PyTypeObject *)self; - if (!PyTuple_Check(args) || PyTuple_GET_SIZE(args) < 1) { + if (nargs < 1) { PyErr_Format(PyExc_TypeError, "%s.__new__(): not enough arguments", type->tp_name); return NULL; } - arg0 = PyTuple_GET_ITEM(args, 0); + arg0 = args[0]; if (!PyType_Check(arg0)) { PyErr_Format(PyExc_TypeError, "%s.__new__(X): X is not a type object (%s)", @@ -10060,16 +10460,26 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); - if (args == NULL) + PyObject *args_tuple = PyTuple_FromArray(args + 1, nargs - 1); + if (args_tuple == NULL) { return NULL; - res = type->tp_new(subtype, args, kwds); - Py_DECREF(args); + } + PyObject *kwds = NULL; + if (kwnames != NULL) { + kwds = _PyStack_AsDict(args + nargs, kwnames); + if (kwds == NULL) { + Py_DECREF(args_tuple); + return NULL; + } + } + res = type->tp_new(subtype, args_tuple, kwds); + Py_DECREF(args_tuple); + Py_XDECREF(kwds); return res; } static struct PyMethodDef tp_new_methoddef[] = { - {"__new__", _PyCFunction_CAST(tp_new_wrapper), METH_VARARGS|METH_KEYWORDS, + {"__new__", _PyCFunction_CAST(tp_new_wrapper), METH_FASTCALL|METH_KEYWORDS, PyDoc_STR("__new__($type, *args, **kwargs)\n--\n\n" "Create and return a new object. " "See help(type) for accurate signature.")}, @@ -10415,9 +10825,8 @@ slot_nb_bool(PyObject *self) } else { PyErr_Format(PyExc_TypeError, - "__bool__ should return " - "bool, returned %s", - Py_TYPE(value)->tp_name); + "%T.__bool__() must return a bool, not %T", + self, value); result = -1; } Py_DECREF(value); @@ -10494,6 +10903,7 @@ slot_tp_hash(PyObject *self) return PyObject_HashNotImplemented(self); } if (!PyLong_Check(res)) { + Py_DECREF(res); PyErr_SetString(PyExc_TypeError, "__hash__ method should return an integer"); return -1; @@ -10581,7 +10991,10 @@ _Py_slot_tp_getattr_hook(PyObject *self, PyObject *name) getattr = _PyType_LookupRef(tp, &_Py_ID(__getattr__)); if (getattr == NULL) { /* No __getattr__ hook: use a simpler dispatcher */ +#ifndef Py_GIL_DISABLED + // Replacing the slot is only thread-safe if there is a GIL. tp->tp_getattro = _Py_slot_tp_getattro; +#endif return _Py_slot_tp_getattro(self, name); } /* speed hack: we could use lookup_maybe, but that would resolve the @@ -10698,26 +11111,37 @@ slot_tp_iternext(PyObject *self) return vectorcall_method(&_Py_ID(__next__), stack, 1); } +int +_PyType_HasSlotTpIternext(PyTypeObject *type) +{ + return type->tp_iternext == slot_tp_iternext; +} + static PyObject * slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type) { PyTypeObject *tp = Py_TYPE(self); - PyObject *get; - - get = _PyType_LookupRef(tp, &_Py_ID(__get__)); - if (get == NULL) { + PyThreadState *tstate = _PyThreadState_GET(); + _PyCStackRef cref; + _PyThreadState_PushCStackRef(tstate, &cref); + _PyType_LookupStackRefAndVersion(tp, &_Py_ID(__get__), &cref.ref); + if (PyStackRef_IsNull(cref.ref)) { + _PyThreadState_PopCStackRef(tstate, &cref); +#ifndef Py_GIL_DISABLED /* Avoid further slowdowns */ if (tp->tp_descr_get == slot_tp_descr_get) tp->tp_descr_get = NULL; +#endif return Py_NewRef(self); } if (obj == NULL) obj = Py_None; if (type == NULL) type = Py_None; + PyObject *get = PyStackRef_AsPyObjectBorrow(cref.ref); PyObject *stack[3] = {self, obj, type}; PyObject *res = PyObject_Vectorcall(get, stack, 3, NULL); - Py_DECREF(get); + _PyThreadState_PopCStackRef(tstate, &cref); return res; } @@ -10763,15 +11187,19 @@ static PyObject * slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyThreadState *tstate = _PyThreadState_GET(); - PyObject *func, *result; + PyObject *result; - func = PyObject_GetAttr((PyObject *)type, &_Py_ID(__new__)); - if (func == NULL) { + _PyCStackRef func_ref; + _PyThreadState_PushCStackRef(tstate, &func_ref); + func_ref.ref = _PyObject_GetAttrStackRef((PyObject *)type, &_Py_ID(__new__)); + if (PyStackRef_IsNull(func_ref.ref)) { + _PyThreadState_PopCStackRef(tstate, &func_ref); return NULL; } + PyObject *func = PyStackRef_AsPyObjectBorrow(func_ref.ref); result = _PyObject_Call_Prepend(tstate, func, (PyObject *)type, args, kwds); - Py_DECREF(func); + _PyThreadState_PopCStackRef(tstate, &func_ref); return result; } @@ -10892,7 +11320,8 @@ slot_bf_getbuffer(PyObject *self, Py_buffer *buffer, int flags) } if (!PyMemoryView_Check(ret)) { PyErr_Format(PyExc_TypeError, - "__buffer__ returned non-memoryview object"); + "%T.__buffer__() must return a memoryview, not %T", + self, ret); goto fail; } @@ -11341,6 +11770,11 @@ static pytype_slotdef slotdefs[] = { {NULL} }; +/* Stores the number of times where slotdefs has elements with same name. + This counter precalculated by _PyType_InitSlotDefs() when the main + interpreter starts. */ +static uint8_t slotdefs_dups[Py_ARRAY_LENGTH(slotdefs)][1 + MAX_EQUIV]; + /* Given a type pointer and an offset gotten from a slotdef entry, return a pointer to the actual slot. This is not quite the same as simply adding the offset to the type pointer, since it takes care to indirect through the @@ -11383,61 +11817,6 @@ slotptr(PyTypeObject *type, int ioffset) return (void **)ptr; } -/* Return a slot pointer for a given name, but ONLY if the attribute has - exactly one slot function. The name must be an interned string. */ -static void ** -resolve_slotdups(PyTypeObject *type, PyObject *name) -{ - /* XXX Maybe this could be optimized more -- but is it worth it? */ - -#ifdef Py_GIL_DISABLED - pytype_slotdef *ptrs[MAX_EQUIV]; - pytype_slotdef **pp = ptrs; - /* Collect all slotdefs that match name into ptrs. */ - for (pytype_slotdef *p = slotdefs; p->name_strobj; p++) { - if (p->name_strobj == name) - *pp++ = p; - } - *pp = NULL; -#else - /* pname and ptrs act as a little cache */ - PyInterpreterState *interp = _PyInterpreterState_GET(); -#define pname _Py_INTERP_CACHED_OBJECT(interp, type_slots_pname) -#define ptrs _Py_INTERP_CACHED_OBJECT(interp, type_slots_ptrs) - pytype_slotdef *p, **pp; - - if (pname != name) { - /* Collect all slotdefs that match name into ptrs. */ - pname = name; - pp = ptrs; - for (p = slotdefs; p->name_strobj; p++) { - if (p->name_strobj == name) - *pp++ = p; - } - *pp = NULL; - } -#endif - - /* Look in all slots of the type matching the name. If exactly one of these - has a filled-in slot, return a pointer to that slot. - Otherwise, return NULL. */ - void **res, **ptr; - res = NULL; - for (pp = ptrs; *pp; pp++) { - ptr = slotptr(type, (*pp)->offset); - if (ptr == NULL || *ptr == NULL) - continue; - if (res != NULL) - return NULL; - res = ptr; - } -#ifndef Py_GIL_DISABLED -#undef pname -#undef ptrs -#endif - return res; -} - // Return true if "name" corresponds to at least one slot definition. This is // a more accurate but more expensive test compared to is_dunder_name(). static bool @@ -11533,7 +11912,6 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p, int use_generic = 0; int offset = p->offset; - int error; void **ptr = slotptr(type, offset); if (ptr == NULL) { @@ -11549,22 +11927,37 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p, assert(!PyErr_Occurred()); do { /* Use faster uncached lookup as we won't get any cache hits during type setup. */ - descr = find_name_in_mro(type, p->name_strobj, &error); - if (descr == NULL) { - if (error == -1) { - /* It is unlikely but not impossible that there has been an exception - during lookup. Since this function originally expected no errors, - we ignore them here in order to keep up the interface. */ - PyErr_Clear(); - } + _PyStackRef descr_ref; + int res = find_name_in_mro(type, p->name_strobj, &descr_ref); + if (res <= 0) { if (ptr == (void**)&type->tp_iternext) { specific = (void *)_PyObject_NextNotImplemented; } continue; } + descr = PyStackRef_AsPyObjectBorrow(descr_ref); if (Py_IS_TYPE(descr, &PyWrapperDescr_Type) && ((PyWrapperDescrObject *)descr)->d_base->name_strobj == p->name_strobj) { - void **tptr = resolve_slotdups(type, p->name_strobj); + void **tptr; + size_t index = (p - slotdefs); + if (slotdefs_dups[index][0] > 1) { + tptr = NULL; + for (size_t i = 1; i <= slotdefs_dups[index][0]; i++) { + pytype_slotdef *q = &slotdefs[slotdefs_dups[index][i]]; + void **qptr = slotptr(type, q->offset); + if (qptr == NULL || *qptr == NULL) + continue; + if (tptr != NULL) { + tptr = NULL; + break; + } + tptr = qptr; + } + } + else { + tptr = slotptr(type, offset); + } + if (tptr == NULL || tptr == ptr) generic = p->function; d = (PyWrapperDescrObject *)descr; @@ -11631,7 +12024,7 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p, } } } - Py_DECREF(descr); + PyStackRef_CLOSE(descr_ref); } while ((++p)->offset == offset); void *slot_value; @@ -11777,6 +12170,76 @@ update_all_slots(PyTypeObject* type) #endif +int +_PyType_InitSlotDefs(PyInterpreterState *interp) +{ + if (!_Py_IsMainInterpreter(interp)) { + return 0; + } + PyObject *bytearray = NULL; + PyObject *cache = PyDict_New(); + if (!cache) { + return -1; + } + + pytype_slotdef *p; + Py_ssize_t idx = 0; + for (p = slotdefs; p->name_strobj; p++, idx++) { + assert(idx < 255); + + if (PyDict_GetItemRef(cache, p->name_strobj, &bytearray) < 0) { + goto error; + } + + if (!bytearray) { + Py_ssize_t size = sizeof(uint8_t) * (1 + MAX_EQUIV); + bytearray = PyByteArray_FromStringAndSize(NULL, size); + if (!bytearray) { + goto error; + } + + uint8_t *data = (uint8_t *)PyByteArray_AS_STRING(bytearray); + data[0] = 0; + + if (PyDict_SetItem(cache, p->name_strobj, bytearray) < 0) { + goto error; + } + } + + assert(PyByteArray_CheckExact(bytearray)); + uint8_t *data = (uint8_t *)PyByteArray_AS_STRING(bytearray); + + data[0] += 1; + assert(data[0] < MAX_EQUIV); + + data[data[0]] = (uint8_t)idx; + + Py_CLEAR(bytearray); + } + + memset(slotdefs_dups, -1, sizeof(slotdefs_dups)); + + Py_ssize_t pos = 0; + PyObject *key = NULL; + PyObject *value = NULL; + while (PyDict_Next(cache, &pos, &key, &value)) { + uint8_t *data = (uint8_t *)PyByteArray_AS_STRING(value); + uint8_t n = data[0]; + for (uint8_t i = 0; i < n; i++) { + uint8_t idx = data[i + 1]; + memcpy(&slotdefs_dups[idx], data, sizeof(uint8_t) * (n + 1)); + } + } + + Py_DECREF(cache); + return 0; + +error: + Py_XDECREF(bytearray); + Py_DECREF(cache); + return -1; +} + PyObject * _PyType_GetSlotWrapperNames(void) @@ -12110,24 +12573,22 @@ super_repr(PyObject *self) on the super object itself. May return NULL with or without an exception set, like PyDict_GetItemWithError. */ -static PyObject * -_super_lookup_descr(PyTypeObject *su_type, PyTypeObject *su_obj_type, PyObject *name) +PyObject * +_PySuper_LookupDescr(PyTypeObject *su_type, PyTypeObject *su_obj_type, PyObject *name) { PyObject *mro, *res; Py_ssize_t i, n; - BEGIN_TYPE_LOCK(); mro = lookup_tp_mro(su_obj_type); - /* keep a strong reference to mro because su_obj_type->tp_mro can be - replaced during PyDict_GetItemRef(dict, name, &res) and because - another thread can modify it after we end the critical section - below */ - Py_XINCREF(mro); - END_TYPE_LOCK(); - if (mro == NULL) return NULL; + /* Keep a strong reference to mro because su_obj_type->tp_mro can be + replaced during PyDict_GetItemRef(dict, name, &res). */ + PyThreadState *tstate = _PyThreadState_GET(); + _PyCStackRef mro_ref; + _PyThreadState_PushCStackRefNew(tstate, &mro_ref, mro); + assert(PyTuple_Check(mro)); n = PyTuple_GET_SIZE(mro); @@ -12138,7 +12599,7 @@ _super_lookup_descr(PyTypeObject *su_type, PyTypeObject *su_obj_type, PyObject * } i++; /* skip su->type (if any) */ if (i >= n) { - Py_DECREF(mro); + _PyThreadState_PopCStackRef(tstate, &mro_ref); return NULL; } @@ -12149,13 +12610,13 @@ _super_lookup_descr(PyTypeObject *su_type, PyTypeObject *su_obj_type, PyObject * if (PyDict_GetItemRef(dict, name, &res) != 0) { // found or error - Py_DECREF(mro); + _PyThreadState_PopCStackRef(tstate, &mro_ref); return res; } i++; } while (i < n); - Py_DECREF(mro); + _PyThreadState_PopCStackRef(tstate, &mro_ref); return NULL; } @@ -12172,7 +12633,7 @@ do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj, goto skip; } - res = _super_lookup_descr(su_type, su_obj_type, name); + res = _PySuper_LookupDescr(su_type, su_obj_type, name); if (res != NULL) { if (method && _PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) { *method = 1; @@ -12485,7 +12946,8 @@ PyDoc_STRVAR(super_doc, "super() -> same as super(__class__, <first argument>)\n" "super(type) -> unbound super object\n" "super(type, obj) -> bound super object; requires isinstance(obj, type)\n" -"super(type, type2) -> bound super object; requires issubclass(type2, type)\n" +"super(type, type2) -> bound super object; requires\n" +" issubclass(type2, type)\n" "Typical use to call a cooperative superclass method:\n" "class C(B):\n" " def meth(self, arg):\n" diff --git a/Objects/typeslots.inc b/Objects/typeslots.inc deleted file mode 100644 index 642160fe0bd8bc..00000000000000 --- a/Objects/typeslots.inc +++ /dev/null @@ -1,84 +0,0 @@ -/* Generated by typeslots.py */ -{offsetof(PyBufferProcs, bf_getbuffer), offsetof(PyTypeObject, tp_as_buffer)}, -{offsetof(PyBufferProcs, bf_releasebuffer), offsetof(PyTypeObject, tp_as_buffer)}, -{offsetof(PyMappingMethods, mp_ass_subscript), offsetof(PyTypeObject, tp_as_mapping)}, -{offsetof(PyMappingMethods, mp_length), offsetof(PyTypeObject, tp_as_mapping)}, -{offsetof(PyMappingMethods, mp_subscript), offsetof(PyTypeObject, tp_as_mapping)}, -{offsetof(PyNumberMethods, nb_absolute), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_add), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_and), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_bool), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_divmod), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_float), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_floor_divide), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_index), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_add), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_and), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_floor_divide), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_lshift), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_multiply), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_or), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_power), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_remainder), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_rshift), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_subtract), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_true_divide), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_xor), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_int), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_invert), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_lshift), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_multiply), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_negative), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_or), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_positive), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_power), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_remainder), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_rshift), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_subtract), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_true_divide), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_xor), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PySequenceMethods, sq_ass_item), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_concat), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_contains), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_inplace_concat), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_inplace_repeat), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_item), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_length), offsetof(PyTypeObject, tp_as_sequence)}, -{offsetof(PySequenceMethods, sq_repeat), offsetof(PyTypeObject, tp_as_sequence)}, -{-1, offsetof(PyTypeObject, tp_alloc)}, -{-1, offsetof(PyTypeObject, tp_base)}, -{-1, offsetof(PyTypeObject, tp_bases)}, -{-1, offsetof(PyTypeObject, tp_call)}, -{-1, offsetof(PyTypeObject, tp_clear)}, -{-1, offsetof(PyTypeObject, tp_dealloc)}, -{-1, offsetof(PyTypeObject, tp_del)}, -{-1, offsetof(PyTypeObject, tp_descr_get)}, -{-1, offsetof(PyTypeObject, tp_descr_set)}, -{-1, offsetof(PyTypeObject, tp_doc)}, -{-1, offsetof(PyTypeObject, tp_getattr)}, -{-1, offsetof(PyTypeObject, tp_getattro)}, -{-1, offsetof(PyTypeObject, tp_hash)}, -{-1, offsetof(PyTypeObject, tp_init)}, -{-1, offsetof(PyTypeObject, tp_is_gc)}, -{-1, offsetof(PyTypeObject, tp_iter)}, -{-1, offsetof(PyTypeObject, tp_iternext)}, -{-1, offsetof(PyTypeObject, tp_methods)}, -{-1, offsetof(PyTypeObject, tp_new)}, -{-1, offsetof(PyTypeObject, tp_repr)}, -{-1, offsetof(PyTypeObject, tp_richcompare)}, -{-1, offsetof(PyTypeObject, tp_setattr)}, -{-1, offsetof(PyTypeObject, tp_setattro)}, -{-1, offsetof(PyTypeObject, tp_str)}, -{-1, offsetof(PyTypeObject, tp_traverse)}, -{-1, offsetof(PyTypeObject, tp_members)}, -{-1, offsetof(PyTypeObject, tp_getset)}, -{-1, offsetof(PyTypeObject, tp_free)}, -{offsetof(PyNumberMethods, nb_matrix_multiply), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyNumberMethods, nb_inplace_matrix_multiply), offsetof(PyTypeObject, tp_as_number)}, -{offsetof(PyAsyncMethods, am_await), offsetof(PyTypeObject, tp_as_async)}, -{offsetof(PyAsyncMethods, am_aiter), offsetof(PyTypeObject, tp_as_async)}, -{offsetof(PyAsyncMethods, am_anext), offsetof(PyTypeObject, tp_as_async)}, -{-1, offsetof(PyTypeObject, tp_finalize)}, -{offsetof(PyAsyncMethods, am_send), offsetof(PyTypeObject, tp_as_async)}, -{-1, offsetof(PyTypeObject, tp_vectorcall)}, -{-1, offsetof(PyHeapTypeObject, ht_token)}, diff --git a/Objects/typeslots.py b/Objects/typeslots.py deleted file mode 100755 index c7f8a33bb1e74e..00000000000000 --- a/Objects/typeslots.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python -# Usage: typeslots.py < Include/typeslots.h typeslots.inc - -import sys, re - - -def generate_typeslots(out=sys.stdout): - out.write("/* Generated by typeslots.py */\n") - res = {} - for line in sys.stdin: - m = re.match("#define Py_([a-z_]+) ([0-9]+)", line) - if not m: - continue - - member = m.group(1) - if member == "tp_token": - # The heap type structure (ht_*) is an implementation detail; - # the public slot for it has a familiar `tp_` prefix - member = '{-1, offsetof(PyHeapTypeObject, ht_token)}' - elif member.startswith("tp_"): - member = f'{{-1, offsetof(PyTypeObject, {member})}}' - elif member.startswith("am_"): - member = (f'{{offsetof(PyAsyncMethods, {member}),'+ - ' offsetof(PyTypeObject, tp_as_async)}') - elif member.startswith("nb_"): - member = (f'{{offsetof(PyNumberMethods, {member}),'+ - ' offsetof(PyTypeObject, tp_as_number)}') - elif member.startswith("mp_"): - member = (f'{{offsetof(PyMappingMethods, {member}),'+ - ' offsetof(PyTypeObject, tp_as_mapping)}') - elif member.startswith("sq_"): - member = (f'{{offsetof(PySequenceMethods, {member}),'+ - ' offsetof(PyTypeObject, tp_as_sequence)}') - elif member.startswith("bf_"): - member = (f'{{offsetof(PyBufferProcs, {member}),'+ - ' offsetof(PyTypeObject, tp_as_buffer)}') - res[int(m.group(2))] = member - - M = max(res.keys())+1 - for i in range(1,M): - if i in res: - out.write("%s,\n" % res[i]) - else: - out.write("{0, 0},\n") - - -def main(): - if len(sys.argv) == 2: - with open(sys.argv[1], "w") as f: - generate_typeslots(f) - else: - generate_typeslots() - -if __name__ == "__main__": - main() diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index cead6e69af5451..78750a955d2a49 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -2,6 +2,7 @@ #include "Python.h" #include "pycore_interpframe.h" // _PyInterpreterFrame #include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK, PyAnnotateFormat +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_typevarobject.h" #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() #include "pycore_unionobject.h" // _Py_union_type_or, _Py_union_from_tuple @@ -35,8 +36,12 @@ typedef struct { typedef struct { PyObject_HEAD PyObject *name; + PyObject *bound; PyObject *default_value; PyObject *evaluate_default; + bool covariant; + bool contravariant; + bool infer_variance; } typevartupleobject; typedef struct { @@ -53,6 +58,7 @@ typedef struct { typedef struct { PyObject_HEAD PyObject *name; + PyObject *qualname; PyObject *type_params; PyObject *compute_value; PyObject *value; @@ -250,7 +256,7 @@ static PyType_Slot constevaluator_slots[] = { {0, NULL}, }; -PyType_Spec constevaluator_spec = { +static PyType_Spec constevaluator_spec = { .name = "_typing._ConstEvaluator", .basicsize = sizeof(constevaluatorobject), .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE @@ -372,7 +378,7 @@ type_check(PyObject *arg, const char *msg) static PyObject * make_union(PyObject *self, PyObject *other) { - PyObject *args = PyTuple_Pack(2, self, other); + PyObject *args = _PyTuple_FromPair(self, other); if (args == NULL) { return NULL; } @@ -472,7 +478,7 @@ typevar_dealloc(PyObject *self) _PyObject_GC_UNTRACK(self); - Py_DECREF(tv->name); + Py_XDECREF(tv->name); Py_XDECREF(tv->bound); Py_XDECREF(tv->evaluate_bound); Py_XDECREF(tv->constraints); @@ -491,20 +497,21 @@ typevar_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); typevarobject *tv = typevarobject_CAST(self); + Py_VISIT(tv->name); Py_VISIT(tv->bound); Py_VISIT(tv->evaluate_bound); Py_VISIT(tv->constraints); Py_VISIT(tv->evaluate_constraints); Py_VISIT(tv->default_value); Py_VISIT(tv->evaluate_default); - PyObject_VisitManagedDict(self, visit, arg); - return 0; + return PyObject_VisitManagedDict(self, visit, arg); } static int typevar_clear(PyObject *op) { typevarobject *self = typevarobject_CAST(op); + Py_CLEAR(self->name); Py_CLEAR(self->bound); Py_CLEAR(self->evaluate_bound); Py_CLEAR(self->constraints); @@ -814,7 +821,7 @@ typevar_typing_prepare_subst_impl(typevarobject *self, PyObject *alias, } Py_DECREF(params); PyErr_Format(PyExc_TypeError, - "Too few arguments for %S; actual %d, expected at least %d", + "Too few arguments for %S; actual %zd, expected at least %zd", alias, args_len, i + 1); return NULL; } @@ -927,7 +934,7 @@ static PyType_Slot typevar_slots[] = { {0, NULL}, }; -PyType_Spec typevar_spec = { +static PyType_Spec typevar_spec = { .name = "typing.TypeVar", .basicsize = sizeof(typevarobject), .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE @@ -1075,7 +1082,7 @@ static PyType_Slot paramspecargs_slots[] = { {0, NULL}, }; -PyType_Spec paramspecargs_spec = { +static PyType_Spec paramspecargs_spec = { .name = "typing.ParamSpecArgs", .basicsize = sizeof(paramspecattrobject), .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE @@ -1155,7 +1162,7 @@ static PyType_Slot paramspeckwargs_slots[] = { {0, NULL}, }; -PyType_Spec paramspeckwargs_spec = { +static PyType_Spec paramspeckwargs_spec = { .name = "typing.ParamSpecKwargs", .basicsize = sizeof(paramspecattrobject), .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE @@ -1171,7 +1178,7 @@ paramspec_dealloc(PyObject *self) _PyObject_GC_UNTRACK(self); - Py_DECREF(ps->name); + Py_XDECREF(ps->name); Py_XDECREF(ps->bound); Py_XDECREF(ps->default_value); Py_XDECREF(ps->evaluate_default); @@ -1187,17 +1194,18 @@ paramspec_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); paramspecobject *ps = paramspecobject_CAST(self); + Py_VISIT(ps->name); Py_VISIT(ps->bound); Py_VISIT(ps->default_value); Py_VISIT(ps->evaluate_default); - PyObject_VisitManagedDict(self, visit, arg); - return 0; + return PyObject_VisitManagedDict(self, visit, arg); } static int paramspec_clear(PyObject *op) { paramspecobject *self = paramspecobject_CAST(op); + Py_CLEAR(self->name); Py_CLEAR(self->bound); Py_CLEAR(self->default_value); Py_CLEAR(self->evaluate_default); @@ -1446,13 +1454,13 @@ The following syntax creates a parameter specification that defaults\n\ to a callable accepting two positional-only arguments of types int\n\ and str:\n\ \n\ - type IntFuncDefault[**P = (int, str)] = Callable[P, int]\n\ + type IntFuncDefault[**P = [int, str]] = Callable[P, int]\n\ \n\ For compatibility with Python 3.11 and earlier, ParamSpec objects\n\ can also be created as follows::\n\ \n\ P = ParamSpec('P')\n\ - DefaultP = ParamSpec('DefaultP', default=(int, str))\n\ + DefaultP = ParamSpec('DefaultP', default=[int, str])\n\ \n\ Parameter specification variables exist primarily for the benefit of\n\ static type checkers. They are used to forward the parameter types of\n\ @@ -1504,7 +1512,7 @@ static PyType_Slot paramspec_slots[] = { {0, 0}, }; -PyType_Spec paramspec_spec = { +static PyType_Spec paramspec_spec = { .name = "typing.ParamSpec", .basicsize = sizeof(paramspecobject), .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE @@ -1519,7 +1527,8 @@ typevartuple_dealloc(PyObject *self) _PyObject_GC_UNTRACK(self); typevartupleobject *tvt = typevartupleobject_CAST(self); - Py_DECREF(tvt->name); + Py_XDECREF(tvt->name); + Py_XDECREF(tvt->bound); Py_XDECREF(tvt->default_value); Py_XDECREF(tvt->evaluate_default); PyObject_ClearManagedDict(self); @@ -1551,16 +1560,28 @@ static PyObject * typevartuple_repr(PyObject *self) { typevartupleobject *tvt = typevartupleobject_CAST(self); - return Py_NewRef(tvt->name); + + if (tvt->infer_variance) { + return Py_NewRef(tvt->name); + } + + char variance = tvt->covariant ? '+' : tvt->contravariant ? '-' : '~'; + return PyUnicode_FromFormat("%c%U", variance, tvt->name); } static PyMemberDef typevartuple_members[] = { {"__name__", _Py_T_OBJECT, offsetof(typevartupleobject, name), Py_READONLY}, + {"__bound__", _Py_T_OBJECT, offsetof(typevartupleobject, bound), Py_READONLY}, + {"__covariant__", Py_T_BOOL, offsetof(typevartupleobject, covariant), Py_READONLY}, + {"__contravariant__", Py_T_BOOL, offsetof(typevartupleobject, contravariant), Py_READONLY}, + {"__infer_variance__", Py_T_BOOL, offsetof(typevartupleobject, infer_variance), Py_READONLY}, {0} }; static typevartupleobject * -typevartuple_alloc(PyObject *name, PyObject *module, PyObject *default_value) +typevartuple_alloc(PyObject *name, PyObject *bound, PyObject *default_value, + bool covariant, bool contravariant, bool infer_variance, + PyObject *module) { PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.typevartuple_type; typevartupleobject *tvt = PyObject_GC_New(typevartupleobject, tp); @@ -1568,6 +1589,10 @@ typevartuple_alloc(PyObject *name, PyObject *module, PyObject *default_value) return NULL; } tvt->name = Py_NewRef(name); + tvt->bound = Py_XNewRef(bound); + tvt->covariant = covariant; + tvt->contravariant = contravariant; + tvt->infer_variance = infer_variance; tvt->default_value = Py_XNewRef(default_value); tvt->evaluate_default = NULL; _PyObject_GC_TRACK(tvt); @@ -1586,21 +1611,46 @@ typevartuple.__new__ name: object(subclass_of="&PyUnicode_Type") * + bound: object = None + covariant: bool = False + contravariant: bool = False + infer_variance: bool = False default as default_value: object(c_default="&_Py_NoDefaultStruct") = typing.NoDefault Create a new TypeVarTuple with the given name. [clinic start generated code]*/ static PyObject * -typevartuple_impl(PyTypeObject *type, PyObject *name, +typevartuple_impl(PyTypeObject *type, PyObject *name, PyObject *bound, + int covariant, int contravariant, int infer_variance, PyObject *default_value) -/*[clinic end generated code: output=9d6b76dfe95aae51 input=e149739929a866d0]*/ +/*[clinic end generated code: output=40bc9ca10f64e392 input=56e28c725a8da40b]*/ { + if (covariant && contravariant) { + PyErr_SetString(PyExc_ValueError, "Bivariant types are not supported."); + return NULL; + } + if (infer_variance && (covariant || contravariant)) { + PyErr_SetString(PyExc_ValueError, "Variance cannot be specified with infer_variance."); + return NULL; + } + if (Py_IsNone(bound)) { + bound = NULL; + } + if (bound != NULL) { + bound = type_check(bound, "Bound must be a type."); + if (bound == NULL) { + return NULL; + } + } PyObject *module = caller(); if (module == NULL) { + Py_XDECREF(bound); return NULL; } - PyObject *result = (PyObject *)typevartuple_alloc(name, module, default_value); + PyObject *result = (PyObject *)typevartuple_alloc( + name, bound, default_value, covariant, contravariant, infer_variance, module); + Py_XDECREF(bound); Py_DECREF(module); return result; } @@ -1683,16 +1733,19 @@ typevartuple_traverse(PyObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); typevartupleobject *tvt = typevartupleobject_CAST(self); + Py_VISIT(tvt->name); + Py_VISIT(tvt->bound); Py_VISIT(tvt->default_value); Py_VISIT(tvt->evaluate_default); - PyObject_VisitManagedDict(self, visit, arg); - return 0; + return PyObject_VisitManagedDict(self, visit, arg); } static int typevartuple_clear(PyObject *self) { typevartupleobject *tvt = typevartupleobject_CAST(self); + Py_CLEAR(tvt->name); + Py_CLEAR(tvt->bound); Py_CLEAR(tvt->default_value); Py_CLEAR(tvt->evaluate_default); PyObject_ClearManagedDict(self); @@ -1782,7 +1835,7 @@ Note that only TypeVarTuples defined in the global scope can be\n\ pickled.\n\ "); -PyType_Slot typevartuple_slots[] = { +static PyType_Slot typevartuple_slots[] = { {Py_tp_doc, (void *)typevartuple_doc}, {Py_tp_members, typevartuple_members}, {Py_tp_methods, typevartuple_methods}, @@ -1798,7 +1851,7 @@ PyType_Slot typevartuple_slots[] = { {0, 0}, }; -PyType_Spec typevartuple_spec = { +static PyType_Spec typevartuple_spec = { .name = "typing.TypeVarTuple", .basicsize = sizeof(typevartupleobject), .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_MANAGED_DICT @@ -1824,7 +1877,7 @@ PyObject * _Py_make_typevartuple(PyThreadState *Py_UNUSED(ignored), PyObject *v) { assert(PyUnicode_Check(v)); - return (PyObject *)typevartuple_alloc(v, NULL, NULL); + return (PyObject *)typevartuple_alloc(v, NULL, NULL, false, false, true, NULL); } static PyObject * @@ -1851,7 +1904,8 @@ typealias_dealloc(PyObject *self) PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); typealiasobject *ta = typealiasobject_CAST(self); - Py_DECREF(ta->name); + Py_XDECREF(ta->name); + Py_XDECREF(ta->qualname); Py_XDECREF(ta->type_params); Py_XDECREF(ta->compute_value); Py_XDECREF(ta->value); @@ -1878,11 +1932,12 @@ static PyObject * typealias_repr(PyObject *self) { typealiasobject *ta = (typealiasobject *)self; - return Py_NewRef(ta->name); + return Py_NewRef(ta->qualname); } static PyMemberDef typealias_members[] = { {"__name__", _Py_T_OBJECT, offsetof(typealiasobject, name), Py_READONLY}, + {"__qualname__", _Py_T_OBJECT, offsetof(typealiasobject, qualname), Py_READONLY}, {0} }; @@ -1928,8 +1983,12 @@ static PyObject * typealias_module(PyObject *self, void *Py_UNUSED(closure)) { typealiasobject *ta = typealiasobject_CAST(self); - if (ta->module != NULL) { - return Py_NewRef(ta->module); + PyObject *module; + Py_BEGIN_CRITICAL_SECTION(self); + module = Py_XNewRef(ta->module); + Py_END_CRITICAL_SECTION(); + if (module != NULL) { + return module; } if (ta->compute_value != NULL) { PyObject* mod = PyFunction_GetModule(ta->compute_value); @@ -1943,12 +2002,25 @@ typealias_module(PyObject *self, void *Py_UNUSED(closure)) Py_RETURN_NONE; } +static int +typealias_set_module(PyObject *self, PyObject *value, void *Py_UNUSED(closure)) +{ + PyObject *old; + typealiasobject *ta = typealiasobject_CAST(self); + Py_BEGIN_CRITICAL_SECTION(self); + old = ta->module; + ta->module = Py_XNewRef(value); + Py_END_CRITICAL_SECTION(); + Py_XDECREF(old); + return 0; +} + static PyGetSetDef typealias_getset[] = { {"__parameters__", typealias_parameters, NULL, NULL, NULL}, {"__type_params__", typealias_type_params, NULL, NULL, NULL}, {"__value__", typealias_value, NULL, NULL, NULL}, {"evaluate_value", typealias_evaluate_value, NULL, NULL, NULL}, - {"__module__", typealias_module, NULL, NULL, NULL}, + {"__module__", typealias_module, typealias_set_module, NULL, NULL}, {0} }; @@ -1997,7 +2069,7 @@ typealias_check_type_params(PyObject *type_params, int *err) { } static PyObject * -typelias_convert_type_params(PyObject *type_params) +typealias_convert_type_params(PyObject *type_params) { if ( type_params == NULL @@ -2012,14 +2084,15 @@ typelias_convert_type_params(PyObject *type_params) } static typealiasobject * -typealias_alloc(PyObject *name, PyObject *type_params, PyObject *compute_value, - PyObject *value, PyObject *module) +typealias_alloc(PyObject *name, PyObject *qualname, PyObject *type_params, + PyObject *compute_value, PyObject *value, PyObject *module) { typealiasobject *ta = PyObject_GC_New(typealiasobject, &_PyTypeAlias_Type); if (ta == NULL) { return NULL; } ta->name = Py_NewRef(name); + ta->qualname = Py_NewRef(qualname); ta->type_params = Py_XNewRef(type_params); ta->compute_value = Py_XNewRef(compute_value); ta->value = Py_XNewRef(value); @@ -2032,6 +2105,8 @@ static int typealias_traverse(PyObject *op, visitproc visit, void *arg) { typealiasobject *self = typealiasobject_CAST(op); + Py_VISIT(self->name); + Py_VISIT(self->qualname); Py_VISIT(self->type_params); Py_VISIT(self->compute_value); Py_VISIT(self->value); @@ -2043,6 +2118,8 @@ static int typealias_clear(PyObject *op) { typealiasobject *self = typealiasobject_CAST(op); + Py_CLEAR(self->name); + Py_CLEAR(self->qualname); Py_CLEAR(self->type_params); Py_CLEAR(self->compute_value); Py_CLEAR(self->value); @@ -2088,14 +2165,15 @@ typealias.__new__ as typealias_new value: object * type_params: object = NULL + qualname: object(c_default="NULL") = None Create a TypeAliasType. [clinic start generated code]*/ static PyObject * typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value, - PyObject *type_params) -/*[clinic end generated code: output=8920ce6bdff86f00 input=df163c34e17e1a35]*/ + PyObject *type_params, PyObject *qualname) +/*[clinic end generated code: output=b7f6d9f1c577cd9c input=cbec290f8c4886ef]*/ { if (type_params != NULL && !PyTuple_Check(type_params)) { PyErr_SetString(PyExc_TypeError, "type_params must be a tuple"); @@ -2108,12 +2186,23 @@ typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value, return NULL; } + if (qualname == NULL || qualname == Py_None) { + // If qualname was not set directly, we use name instead. + qualname = name; + } else { + if (!PyUnicode_Check(qualname)) { + PyErr_SetString(PyExc_TypeError, "qualname must be a string"); + return NULL; + } + } + PyObject *module = caller(); if (module == NULL) { return NULL; } - PyObject *ta = (PyObject *)typealias_alloc(name, checked_params, NULL, value, - module); + + PyObject *ta = (PyObject *)typealias_alloc( + name, qualname, checked_params, NULL, value, module); Py_DECREF(module); return ta; } @@ -2131,7 +2220,9 @@ type checkers.\n\ At runtime, Alias is an instance of TypeAliasType. The __name__\n\ attribute holds the name of the type alias. The value of the type alias\n\ is stored in the __value__ attribute. It is evaluated lazily, so the\n\ -value is computed only if the attribute is accessed.\n\ +value is computed only if the attribute is accessed. The __module__\n\ +attribute holds the name of the module in which the type alias was\n\ +defined; it can be assigned to.\n\ \n\ Type aliases can also be generic::\n\ \n\ @@ -2179,10 +2270,17 @@ _Py_make_typealias(PyThreadState* unused, PyObject *args) assert(PyTuple_GET_SIZE(args) == 3); PyObject *name = PyTuple_GET_ITEM(args, 0); assert(PyUnicode_Check(name)); - PyObject *type_params = typelias_convert_type_params(PyTuple_GET_ITEM(args, 1)); + PyObject *type_params = typealias_convert_type_params(PyTuple_GET_ITEM(args, 1)); PyObject *compute_value = PyTuple_GET_ITEM(args, 2); assert(PyFunction_Check(compute_value)); - return (PyObject *)typealias_alloc(name, type_params, compute_value, NULL, NULL); + + PyFunctionObject *compute_func = (PyFunctionObject *)compute_value; + PyCodeObject *code_obj = (PyCodeObject *)compute_func->func_code; + PyObject *qualname = code_obj->co_qualname; + assert(qualname != NULL); + + return (PyObject *)typealias_alloc( + name, qualname, type_params, compute_value, NULL, NULL); } PyDoc_STRVAR(generic_doc, @@ -2213,8 +2311,9 @@ PyDoc_STRVAR(generic_class_getitem_doc, "Parameterizes a generic class.\n\ \n\ At least, parameterizing a generic class is the *main* thing this\n\ -method does. For example, for some generic class `Foo`, this is called\n\ -when we do `Foo[int]` - there, with `cls=Foo` and `params=int`.\n\ +method does. For example, for some generic class `Foo`, this is\n\ +called when we do `Foo[int]` - there, with `cls=Foo` and\n\ +`params=int`.\n\ \n\ However, note that this method is also called when defining generic\n\ classes in the first place with `class Foo[T]: ...`.\n\ @@ -2272,13 +2371,12 @@ generic_class_getitem(PyObject *cls, PyObject *args, PyObject *kwargs) PyObject * _Py_subscript_generic(PyThreadState* unused, PyObject *params) { - params = unpack_typevartuples(params); - PyInterpreterState *interp = _PyInterpreterState_GET(); if (interp->cached_objects.generic_type == NULL) { PyErr_SetString(PyExc_SystemError, "Cannot find Generic type"); return NULL; } + params = unpack_typevartuples(params); PyObject *args[2] = {(PyObject *)interp->cached_objects.generic_type, params}; PyObject *result = call_typing_func_object("_GenericAlias", args, 2); Py_DECREF(params); @@ -2304,24 +2402,17 @@ generic_dealloc(PyObject *self) Py_DECREF(tp); } -static int -generic_traverse(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static PyType_Slot generic_slots[] = { {Py_tp_doc, (void *)generic_doc}, {Py_tp_methods, generic_methods}, {Py_tp_dealloc, generic_dealloc}, {Py_tp_alloc, PyType_GenericAlloc}, {Py_tp_free, PyObject_GC_Del}, - {Py_tp_traverse, generic_traverse}, + {Py_tp_traverse, _PyObject_VisitType}, {0, NULL}, }; -PyType_Spec generic_spec = { +static PyType_Spec generic_spec = { .name = "typing.Generic", .basicsize = sizeof(PyObject), .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, diff --git a/Objects/unicode_format.c b/Objects/unicode_format.c new file mode 100644 index 00000000000000..e2790c8c1d4343 --- /dev/null +++ b/Objects/unicode_format.c @@ -0,0 +1,1105 @@ +/* + +Unicode implementation based on original code by Fredrik Lundh, +modified by Marc-Andre Lemburg <mal@lemburg.com>. + +Major speed upgrades to the method implementations at the Reykjavik +NeedForSpeed sprint, by Fredrik Lundh and Andrew Dalke. + +Copyright (c) Corporation for National Research Initiatives. + +-------------------------------------------------------------------- +The original string type implementation is: + + Copyright (c) 1999 by Secret Labs AB + Copyright (c) 1999 by Fredrik Lundh + +By obtaining, using, and/or copying this software and/or its +associated documentation, you agree that you have read, understood, +and will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its +associated documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies, and that both that copyright notice and this permission notice +appear in supporting documentation, and that the name of Secret Labs +AB or the author not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR +ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +-------------------------------------------------------------------- + +*/ + +// PyUnicode_Format() implementation + +#include "Python.h" +#include "pycore_abstract.h" // _PyIndex_Check() +#include "pycore_format.h" // F_ALT +#include "pycore_long.h" // _PyLong_FormatWriter() +#include "pycore_object.h" // _PyObject_IsUniquelyReferenced() +#include "pycore_unicodeobject.h" // _Py_MAX_UNICODE + + +#define MAX_UNICODE _Py_MAX_UNICODE +#define ensure_unicode _PyUnicode_EnsureUnicode + +struct unicode_formatter_t { + PyObject *args; + int args_owned; + Py_ssize_t arglen, argidx; + PyObject *dict; + + int fmtkind; + Py_ssize_t fmtcnt, fmtpos; + const void *fmtdata; + PyObject *fmtstr; + + _PyUnicodeWriter writer; +}; + + +struct unicode_format_arg_t { + Py_UCS4 ch; + int flags; + Py_ssize_t width; + int prec; + int sign; + Py_ssize_t fmtstart; + PyObject *key; +}; + + +// Use FORMAT_ERROR("...%s", "") when there is no arguments. +#define FORMAT_ERROR(EXC, FMT, ...) do { \ + if (arg->key != NULL) { \ + PyErr_Format((EXC), "format argument %R: " FMT, \ + arg->key, __VA_ARGS__); \ + } \ + else if (ctx->argidx >= 0) { \ + PyErr_Format((EXC), "format argument %zd: " FMT, \ + ctx->argidx, __VA_ARGS__); \ + } \ + else { \ + PyErr_Format((EXC), "format argument: " FMT, __VA_ARGS__); \ + } \ +} while (0) + + +static PyObject * +unicode_format_getnextarg(struct unicode_formatter_t *ctx, int allowone) +{ + Py_ssize_t argidx = ctx->argidx; + + if (argidx < ctx->arglen && (allowone || ctx->arglen >= 0)) { + ctx->argidx++; + if (ctx->arglen >= 0) { + return PyTuple_GetItem(ctx->args, argidx); + } + else if (allowone) { + return ctx->args; + } + } + PyErr_Format(PyExc_TypeError, + "not enough arguments for format string (got %zd)", + ctx->arglen < 0 ? 1 : ctx->arglen); + return NULL; +} + + +/* Returns a new reference to a PyUnicode object, or NULL on failure. */ + +/* Format a float into the writer if the writer is not NULL, or into *p_output + otherwise. + + Return 0 on success, raise an exception and return -1 on error. */ +static int +formatfloat(PyObject *v, + struct unicode_formatter_t *ctx, + struct unicode_format_arg_t *arg, + PyObject **p_output, + _PyUnicodeWriter *writer) +{ + char *p; + double x; + Py_ssize_t len; + int prec; + int dtoa_flags = 0; + + x = PyFloat_AsDouble(v); + if (x == -1.0 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + FORMAT_ERROR(PyExc_TypeError, + "%%%c requires a real number, not %T", + arg->ch, v); + } + return -1; + } + + prec = arg->prec; + if (prec < 0) + prec = 6; + + if (arg->flags & F_ALT) + dtoa_flags |= Py_DTSF_ALT; + p = PyOS_double_to_string(x, arg->ch, prec, dtoa_flags, NULL); + if (p == NULL) + return -1; + len = strlen(p); + if (writer) { + if (_PyUnicodeWriter_WriteASCIIString(writer, p, len) < 0) { + PyMem_Free(p); + return -1; + } + } + else + *p_output = _PyUnicode_FromASCII(p, len); + PyMem_Free(p); + return 0; +} + + +/* formatlong() emulates the format codes d, u, o, x and X, and + * the F_ALT flag, for Python's long (unbounded) ints. It's not used for + * Python's regular ints. + * Return value: a new PyUnicodeObject*, or NULL if error. + * The output string is of the form + * "-"? ("0x" | "0X")? digit+ + * "0x"/"0X" are present only for x and X conversions, with F_ALT + * set in flags. The case of hex digits will be correct, + * There will be at least prec digits, zero-filled on the left if + * necessary to get that many. + * val object to be converted + * flags bitmask of format flags; only F_ALT is looked at + * prec minimum number of digits; 0-fill on left if needed + * type a character in [duoxX]; u acts the same as d + * + * CAUTION: o, x and X conversions on regular ints can never + * produce a '-' sign, but can for Python's unbounded ints. + */ +PyObject * +_PyUnicode_FormatLong(PyObject *val, int alt, int prec, int type) +{ + PyObject *result = NULL; + char *buf; + Py_ssize_t i; + int sign; /* 1 if '-', else 0 */ + int len; /* number of characters */ + Py_ssize_t llen; + int numdigits; /* len == numnondigits + numdigits */ + int numnondigits = 0; + + /* Avoid exceeding SSIZE_T_MAX */ + if (prec > INT_MAX-3) { + PyErr_SetString(PyExc_OverflowError, + "precision too large"); + return NULL; + } + + assert(PyLong_Check(val)); + + switch (type) { + default: + Py_UNREACHABLE(); + case 'd': + case 'i': + case 'u': + /* int and int subclasses should print numerically when a numeric */ + /* format code is used (see issue18780) */ + result = PyNumber_ToBase(val, 10); + break; + case 'o': + numnondigits = 2; + result = PyNumber_ToBase(val, 8); + break; + case 'x': + case 'X': + numnondigits = 2; + result = PyNumber_ToBase(val, 16); + break; + } + if (!result) + return NULL; + + assert(_PyUnicode_IsModifiable(result)); + assert(PyUnicode_IS_ASCII(result)); + + /* To modify the string in-place, there can only be one reference. */ + if (!_PyObject_IsUniquelyReferenced(result)) { + Py_DECREF(result); + PyErr_BadInternalCall(); + return NULL; + } + buf = PyUnicode_DATA(result); + llen = PyUnicode_GET_LENGTH(result); + if (llen > INT_MAX) { + Py_DECREF(result); + PyErr_SetString(PyExc_ValueError, + "string too large in _PyUnicode_FormatLong"); + return NULL; + } + len = (int)llen; + sign = buf[0] == '-'; + numnondigits += sign; + numdigits = len - numnondigits; + assert(numdigits > 0); + + /* Get rid of base marker unless F_ALT */ + if (((alt) == 0 && + (type == 'o' || type == 'x' || type == 'X'))) { + assert(buf[sign] == '0'); + assert(buf[sign+1] == 'x' || buf[sign+1] == 'X' || + buf[sign+1] == 'o'); + numnondigits -= 2; + buf += 2; + len -= 2; + if (sign) + buf[0] = '-'; + assert(len == numnondigits + numdigits); + assert(numdigits > 0); + } + + /* Fill with leading zeroes to meet minimum width. */ + if (prec > numdigits) { + PyObject *r1 = PyBytes_FromStringAndSize(NULL, + numnondigits + prec); + char *b1; + if (!r1) { + Py_DECREF(result); + return NULL; + } + b1 = PyBytes_AS_STRING(r1); + for (i = 0; i < numnondigits; ++i) + *b1++ = *buf++; + for (i = 0; i < prec - numdigits; i++) + *b1++ = '0'; + for (i = 0; i < numdigits; i++) + *b1++ = *buf++; + *b1 = '\0'; + Py_SETREF(result, r1); + buf = PyBytes_AS_STRING(result); + len = numnondigits + prec; + } + + /* Fix up case for hex conversions. */ + if (type == 'X') { + /* Need to convert all lower case letters to upper case. + and need to convert 0x to 0X (and -0x to -0X). */ + for (i = 0; i < len; i++) + if (buf[i] >= 'a' && buf[i] <= 'x') + buf[i] -= 'a'-'A'; + } + if (!PyUnicode_Check(result) + || buf != PyUnicode_DATA(result)) { + PyObject *unicode; + unicode = _PyUnicode_FromASCII(buf, len); + Py_SETREF(result, unicode); + } + else if (len != PyUnicode_GET_LENGTH(result)) { + if (PyUnicode_Resize(&result, len) < 0) + Py_CLEAR(result); + } + return result; +} + + +/* Format an integer or a float as an integer. + * Return 1 if the number has been formatted into the writer, + * 0 if the number has been formatted into *p_output + * -1 and raise an exception on error */ +static int +mainformatlong(PyObject *v, + struct unicode_formatter_t *ctx, + struct unicode_format_arg_t *arg, + PyObject **p_output, + _PyUnicodeWriter *writer) +{ + PyObject *iobj, *res; + char type = (char)arg->ch; + + if (!PyNumber_Check(v)) + goto wrongtype; + + /* make sure number is a type of integer for o, x, and X */ + if (!PyLong_Check(v)) { + if (type == 'o' || type == 'x' || type == 'X') { + iobj = _PyNumber_Index(v); + } + else { + iobj = PyNumber_Long(v); + } + if (iobj == NULL ) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + goto wrongtype; + return -1; + } + assert(PyLong_Check(iobj)); + } + else { + iobj = Py_NewRef(v); + } + + if (PyLong_CheckExact(v) + && arg->width == -1 && arg->prec == -1 + && !(arg->flags & (F_SIGN | F_BLANK)) + && type != 'X') + { + /* Fast path */ + int alternate = arg->flags & F_ALT; + int base; + + switch(type) + { + default: + Py_UNREACHABLE(); + case 'd': + case 'i': + case 'u': + base = 10; + break; + case 'o': + base = 8; + break; + case 'x': + case 'X': + base = 16; + break; + } + + if (_PyLong_FormatWriter(writer, v, base, alternate) == -1) { + Py_DECREF(iobj); + return -1; + } + Py_DECREF(iobj); + return 1; + } + + res = _PyUnicode_FormatLong(iobj, arg->flags & F_ALT, arg->prec, type); + Py_DECREF(iobj); + if (res == NULL) + return -1; + *p_output = res; + return 0; + +wrongtype: + switch(type) + { + case 'o': + case 'x': + case 'X': + FORMAT_ERROR(PyExc_TypeError, + "%%%c requires an integer, not %T", + arg->ch, v); + break; + default: + FORMAT_ERROR(PyExc_TypeError, + "%%%c requires a real number, not %T", + arg->ch, v); + break; + } + return -1; +} + + +static Py_UCS4 +formatchar(PyObject *v, + struct unicode_formatter_t *ctx, + struct unicode_format_arg_t *arg) +{ + /* presume that the buffer is at least 3 characters long */ + if (PyUnicode_Check(v)) { + if (PyUnicode_GET_LENGTH(v) == 1) { + return PyUnicode_READ_CHAR(v, 0); + } + FORMAT_ERROR(PyExc_TypeError, + "%%c requires an integer or a unicode character, " + "not a string of length %zd", + PyUnicode_GET_LENGTH(v)); + return (Py_UCS4) -1; + } + else { + int overflow; + long x = PyLong_AsLongAndOverflow(v, &overflow); + if (x == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + FORMAT_ERROR(PyExc_TypeError, + "%%c requires an integer or a unicode character, " + "not %T", + v); + } + return (Py_UCS4) -1; + } + + if (x < 0 || x > MAX_UNICODE) { + /* this includes an overflow in converting to C long */ + FORMAT_ERROR(PyExc_OverflowError, + "%%c argument not in range(0x110000)%s", ""); + return (Py_UCS4) -1; + } + + return (Py_UCS4) x; + } +} + + +/* Parse options of an argument: flags, width, precision. + Handle also "%(name)" syntax. + + Return 0 if the argument has been formatted into arg->str. + Return 1 if the argument has been written into ctx->writer, + Raise an exception and return -1 on error. */ +static int +unicode_format_arg_parse(struct unicode_formatter_t *ctx, + struct unicode_format_arg_t *arg) +{ +#define FORMAT_READ(ctx) \ + PyUnicode_READ((ctx)->fmtkind, (ctx)->fmtdata, (ctx)->fmtpos) + + PyObject *v; + + if (arg->ch == '(') { + /* Get argument value from a dictionary. Example: "%(name)s". */ + Py_ssize_t keystart; + Py_ssize_t keylen; + int pcount = 1; + + if (ctx->dict == NULL) { + PyErr_Format(PyExc_TypeError, + "format requires a mapping, not %T", + ctx->args); + return -1; + } + ++ctx->fmtpos; + --ctx->fmtcnt; + keystart = ctx->fmtpos; + /* Skip over balanced parentheses */ + while (pcount > 0 && --ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + if (arg->ch == ')') + --pcount; + else if (arg->ch == '(') + ++pcount; + ctx->fmtpos++; + } + keylen = ctx->fmtpos - keystart - 1; + if (ctx->fmtcnt < 0 || pcount > 0) { + PyErr_Format(PyExc_ValueError, + "stray %% or incomplete format key at position %zd", + arg->fmtstart); + return -1; + } + arg->key = PyUnicode_Substring(ctx->fmtstr, + keystart, keystart + keylen); + if (arg->key == NULL) + return -1; + if (ctx->args_owned) { + ctx->args_owned = 0; + Py_DECREF(ctx->args); + } + ctx->args = PyObject_GetItem(ctx->dict, arg->key); + if (ctx->args == NULL) + return -1; + ctx->args_owned = 1; + ctx->arglen = -3; + ctx->argidx = -4; + } + else { + if (ctx->arglen < -1) { + PyErr_Format(PyExc_ValueError, + "format requires a parenthesised mapping key " + "at position %zd", + arg->fmtstart); + return -1; + } + } + + /* Parse flags. Example: "%+i" => flags=F_SIGN. */ + while (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + switch (arg->ch) { + case '-': arg->flags |= F_LJUST; continue; + case '+': arg->flags |= F_SIGN; continue; + case ' ': arg->flags |= F_BLANK; continue; + case '#': arg->flags |= F_ALT; continue; + case '0': arg->flags |= F_ZERO; continue; + } + break; + } + + /* Parse width. Example: "%10s" => width=10 */ + if (arg->ch == '*') { + if (ctx->arglen < -1) { + PyErr_Format(PyExc_ValueError, + "* cannot be used with a parenthesised mapping key " + "at position %zd", + arg->fmtstart); + return -1; + } + v = unicode_format_getnextarg(ctx, 0); + if (v == NULL) + return -1; + if (!PyLong_Check(v)) { + FORMAT_ERROR(PyExc_TypeError, "* requires int, not %T", v); + return -1; + } + arg->width = PyLong_AsSsize_t(v); + if (arg->width == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + FORMAT_ERROR(PyExc_OverflowError, + "too big for width%s", ""); + } + return -1; + } + if (arg->width < 0) { + arg->flags |= F_LJUST; + arg->width = -arg->width; + } + if (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + } + } + else if (arg->ch >= '0' && arg->ch <= '9') { + arg->width = arg->ch - '0'; + while (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + if (arg->ch < '0' || arg->ch > '9') + break; + /* Since arg->ch is unsigned, the RHS would end up as unsigned, + mixing signed and unsigned comparison. Since arg->ch is between + '0' and '9', casting to int is safe. */ + if (arg->width > (PY_SSIZE_T_MAX - ((int)arg->ch - '0')) / 10) { + PyErr_Format(PyExc_ValueError, + "width too big at position %zd", + arg->fmtstart); + return -1; + } + arg->width = arg->width*10 + (arg->ch - '0'); + } + } + + /* Parse precision. Example: "%.3f" => prec=3 */ + if (arg->ch == '.') { + arg->prec = 0; + if (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + } + if (arg->ch == '*') { + if (ctx->arglen < -1) { + PyErr_Format(PyExc_ValueError, + "* cannot be used with a parenthesised mapping key " + "at position %zd", + arg->fmtstart); + return -1; + } + v = unicode_format_getnextarg(ctx, 0); + if (v == NULL) + return -1; + if (!PyLong_Check(v)) { + FORMAT_ERROR(PyExc_TypeError, "* requires int, not %T", v); + return -1; + } + arg->prec = PyLong_AsInt(v); + if (arg->prec == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + FORMAT_ERROR(PyExc_OverflowError, + "too big for precision%s", ""); + } + return -1; + } + if (arg->prec < 0) + arg->prec = 0; + if (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + } + } + else if (arg->ch >= '0' && arg->ch <= '9') { + arg->prec = arg->ch - '0'; + while (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + if (arg->ch < '0' || arg->ch > '9') + break; + if (arg->prec > (INT_MAX - ((int)arg->ch - '0')) / 10) { + PyErr_Format(PyExc_ValueError, + "precision too big at position %zd", + arg->fmtstart); + return -1; + } + arg->prec = arg->prec*10 + (arg->ch - '0'); + } + } + } + + /* Ignore "h", "l" and "L" format prefix (ex: "%hi" or "%ls") */ + if (ctx->fmtcnt >= 0) { + if (arg->ch == 'h' || arg->ch == 'l' || arg->ch == 'L') { + if (--ctx->fmtcnt >= 0) { + arg->ch = FORMAT_READ(ctx); + ctx->fmtpos++; + } + } + } + if (ctx->fmtcnt < 0) { + PyErr_Format(PyExc_ValueError, + "stray %% at position %zd", arg->fmtstart); + return -1; + } + return 0; + +#undef FORMAT_READ +} + + +/* Format one argument. Supported conversion specifiers: + + - "s", "r", "a": any type + - "i", "d", "u": int or float + - "o", "x", "X": int + - "e", "E", "f", "F", "g", "G": float + - "c": int or str (1 character) + + When possible, the output is written directly into the Unicode writer + (ctx->writer). A string is created when padding is required. + + Return 0 if the argument has been formatted into *p_str, + 1 if the argument has been written into ctx->writer, + -1 on error. */ +static int +unicode_format_arg_format(struct unicode_formatter_t *ctx, + struct unicode_format_arg_t *arg, + PyObject **p_str) +{ + PyObject *v; + _PyUnicodeWriter *writer = &ctx->writer; + + if (ctx->fmtcnt == 0) + ctx->writer.overallocate = 0; + + v = unicode_format_getnextarg(ctx, 1); + if (v == NULL) + return -1; + + + switch (arg->ch) { + case 's': + case 'r': + case 'a': + if (PyLong_CheckExact(v) && arg->width == -1 && arg->prec == -1) { + /* Fast path */ + if (_PyLong_FormatWriter(writer, v, 10, arg->flags & F_ALT) == -1) + return -1; + return 1; + } + + if (PyUnicode_CheckExact(v) && arg->ch == 's') { + *p_str = Py_NewRef(v); + } + else { + if (arg->ch == 's') + *p_str = PyObject_Str(v); + else if (arg->ch == 'r') + *p_str = PyObject_Repr(v); + else + *p_str = PyObject_ASCII(v); + } + break; + + case 'i': + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + { + int ret = mainformatlong(v, ctx, arg, p_str, writer); + if (ret != 0) + return ret; + arg->sign = 1; + break; + } + + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (arg->width == -1 && arg->prec == -1 + && !(arg->flags & (F_SIGN | F_BLANK))) + { + /* Fast path */ + if (formatfloat(v, ctx, arg, NULL, writer) == -1) + return -1; + return 1; + } + + arg->sign = 1; + if (formatfloat(v, ctx, arg, p_str, NULL) == -1) + return -1; + break; + + case 'c': + { + Py_UCS4 ch = formatchar(v, ctx, arg); + if (ch == (Py_UCS4) -1) + return -1; + if (arg->width == -1 && arg->prec == -1) { + /* Fast path */ + if (_PyUnicodeWriter_WriteCharInline(writer, ch) < 0) + return -1; + return 1; + } + *p_str = PyUnicode_FromOrdinal(ch); + break; + } + + default: + if (arg->ch < 128 && Py_ISALPHA(arg->ch)) { + PyErr_Format(PyExc_ValueError, + "unsupported format %%%c at position %zd", + (int)arg->ch, arg->fmtstart); + } + else if (arg->ch == '\'') { + PyErr_Format(PyExc_ValueError, + "stray %% at position %zd or unexpected " + "format character \"'\" at position %zd", + arg->fmtstart, + ctx->fmtpos - 1); + } + else if (arg->ch >= 32 && arg->ch < 127) { + PyErr_Format(PyExc_ValueError, + "stray %% at position %zd or unexpected " + "format character '%c' at position %zd", + arg->fmtstart, + (int)arg->ch, ctx->fmtpos - 1); + } + else if (Py_UNICODE_ISPRINTABLE(arg->ch)) { + PyErr_Format(PyExc_ValueError, + "stray %% at position %zd or unexpected " + "format character '%c' (U+%04X) at position %zd", + arg->fmtstart, + (int)arg->ch, (int)arg->ch, ctx->fmtpos - 1); + } + else { + PyErr_Format(PyExc_ValueError, + "stray %% at position %zd or unexpected " + "format character U+%04X at position %zd", + arg->fmtstart, (int)arg->ch, ctx->fmtpos - 1); + } + return -1; + } + if (*p_str == NULL) + return -1; + assert (PyUnicode_Check(*p_str)); + return 0; +} + + +static int +unicode_format_arg_output(struct unicode_formatter_t *ctx, + struct unicode_format_arg_t *arg, + PyObject *str) +{ + Py_ssize_t len; + int kind; + const void *pbuf; + Py_ssize_t pindex; + Py_UCS4 signchar; + Py_ssize_t buflen; + Py_UCS4 maxchar; + Py_ssize_t sublen; + _PyUnicodeWriter *writer = &ctx->writer; + Py_UCS4 fill; + + fill = ' '; + if (arg->sign && arg->flags & F_ZERO) + fill = '0'; + + len = PyUnicode_GET_LENGTH(str); + if ((arg->width == -1 || arg->width <= len) + && (arg->prec == -1 || arg->prec >= len) + && !(arg->flags & (F_SIGN | F_BLANK))) + { + /* Fast path */ + if (_PyUnicodeWriter_WriteStr(writer, str) == -1) + return -1; + return 0; + } + + /* Truncate the string for "s", "r" and "a" formats + if the precision is set */ + if (arg->ch == 's' || arg->ch == 'r' || arg->ch == 'a') { + if (arg->prec >= 0 && len > arg->prec) + len = arg->prec; + } + + /* Adjust sign and width */ + kind = PyUnicode_KIND(str); + pbuf = PyUnicode_DATA(str); + pindex = 0; + signchar = '\0'; + if (arg->sign) { + Py_UCS4 ch = PyUnicode_READ(kind, pbuf, pindex); + if (ch == '-' || ch == '+') { + signchar = ch; + len--; + pindex++; + } + else if (arg->flags & F_SIGN) + signchar = '+'; + else if (arg->flags & F_BLANK) + signchar = ' '; + else + arg->sign = 0; + } + if (arg->width < len) + arg->width = len; + + /* Prepare the writer */ + maxchar = writer->maxchar; + if (!(arg->flags & F_LJUST)) { + if (arg->sign) { + if ((arg->width-1) > len) + maxchar = Py_MAX(maxchar, fill); + } + else { + if (arg->width > len) + maxchar = Py_MAX(maxchar, fill); + } + } + if (PyUnicode_MAX_CHAR_VALUE(str) > maxchar) { + Py_UCS4 strmaxchar = _PyUnicode_FindMaxChar(str, 0, pindex+len); + maxchar = Py_MAX(maxchar, strmaxchar); + } + + buflen = arg->width; + if (arg->sign && len == arg->width) + buflen++; + if (_PyUnicodeWriter_Prepare(writer, buflen, maxchar) == -1) + return -1; + + /* Write the sign if needed */ + if (arg->sign) { + if (fill != ' ') { + PyUnicode_WRITE(writer->kind, writer->data, writer->pos, signchar); + writer->pos += 1; + } + if (arg->width > len) + arg->width--; + } + + /* Write the numeric prefix for "x", "X" and "o" formats + if the alternate form is used. + For example, write "0x" for the "%#x" format. */ + if ((arg->flags & F_ALT) && (arg->ch == 'x' || arg->ch == 'X' || arg->ch == 'o')) { + assert(PyUnicode_READ(kind, pbuf, pindex) == '0'); + assert(PyUnicode_READ(kind, pbuf, pindex + 1) == arg->ch); + if (fill != ' ') { + PyUnicode_WRITE(writer->kind, writer->data, writer->pos, '0'); + PyUnicode_WRITE(writer->kind, writer->data, writer->pos+1, arg->ch); + writer->pos += 2; + pindex += 2; + } + arg->width -= 2; + if (arg->width < 0) + arg->width = 0; + len -= 2; + } + + /* Pad left with the fill character if needed */ + if (arg->width > len && !(arg->flags & F_LJUST)) { + sublen = arg->width - len; + _PyUnicode_Fill(writer->kind, writer->data, fill, writer->pos, sublen); + writer->pos += sublen; + arg->width = len; + } + + /* If padding with spaces: write sign if needed and/or numeric prefix if + the alternate form is used */ + if (fill == ' ') { + if (arg->sign) { + PyUnicode_WRITE(writer->kind, writer->data, writer->pos, signchar); + writer->pos += 1; + } + if ((arg->flags & F_ALT) && (arg->ch == 'x' || arg->ch == 'X' || arg->ch == 'o')) { + assert(PyUnicode_READ(kind, pbuf, pindex) == '0'); + assert(PyUnicode_READ(kind, pbuf, pindex+1) == arg->ch); + PyUnicode_WRITE(writer->kind, writer->data, writer->pos, '0'); + PyUnicode_WRITE(writer->kind, writer->data, writer->pos+1, arg->ch); + writer->pos += 2; + pindex += 2; + } + } + + /* Write characters */ + if (len) { + _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, + str, pindex, len); + writer->pos += len; + } + + /* Pad right with the fill character if needed */ + if (arg->width > len) { + sublen = arg->width - len; + _PyUnicode_Fill(writer->kind, writer->data, ' ', writer->pos, sublen); + writer->pos += sublen; + } + return 0; +} + + +/* Helper of PyUnicode_Format(): format one arg. + Return 0 on success, raise an exception and return -1 on error. */ +static int +unicode_format_arg(struct unicode_formatter_t *ctx) +{ + struct unicode_format_arg_t arg; + PyObject *str; + int ret; + + arg.ch = PyUnicode_READ(ctx->fmtkind, ctx->fmtdata, ctx->fmtpos); + if (arg.ch == '%') { + ctx->fmtpos++; + ctx->fmtcnt--; + if (_PyUnicodeWriter_WriteCharInline(&ctx->writer, '%') < 0) + return -1; + return 0; + } + arg.flags = 0; + arg.width = -1; + arg.prec = -1; + arg.sign = 0; + arg.fmtstart = ctx->fmtpos - 1; + arg.key = NULL; + str = NULL; + + ret = unicode_format_arg_parse(ctx, &arg); + if (ret == -1) { + goto onError; + } + + ret = unicode_format_arg_format(ctx, &arg, &str); + if (ret == -1) { + goto onError; + } + + if (ret != 1) { + ret = unicode_format_arg_output(ctx, &arg, str); + Py_DECREF(str); + if (ret == -1) { + goto onError; + } + } + + if (ctx->dict && (ctx->argidx < ctx->arglen)) { + // XXX: Never happens? + PyErr_SetString(PyExc_TypeError, + "not all arguments converted during string formatting"); + goto onError; + } + Py_XDECREF(arg.key); + return 0; + + onError: + Py_XDECREF(arg.key); + return -1; +} + + +PyObject * +PyUnicode_Format(PyObject *format, PyObject *args) +{ + struct unicode_formatter_t ctx; + + if (format == NULL || args == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + + if (ensure_unicode(format) < 0) + return NULL; + + ctx.fmtstr = format; + ctx.fmtdata = PyUnicode_DATA(ctx.fmtstr); + ctx.fmtkind = PyUnicode_KIND(ctx.fmtstr); + ctx.fmtcnt = PyUnicode_GET_LENGTH(ctx.fmtstr); + ctx.fmtpos = 0; + + _PyUnicodeWriter_Init(&ctx.writer); + ctx.writer.min_length = ctx.fmtcnt + 100; + ctx.writer.overallocate = 1; + + if (PyTuple_Check(args)) { + ctx.arglen = PyTuple_Size(args); + ctx.argidx = 0; + } + else { + ctx.arglen = -1; + ctx.argidx = -2; + } + ctx.args_owned = 0; + if (PyMapping_Check(args) && !PyTuple_Check(args) && !PyUnicode_Check(args)) + ctx.dict = args; + else + ctx.dict = NULL; + ctx.args = args; + + while (--ctx.fmtcnt >= 0) { + if (PyUnicode_READ(ctx.fmtkind, ctx.fmtdata, ctx.fmtpos) != '%') { + Py_ssize_t nonfmtpos; + + nonfmtpos = ctx.fmtpos++; + while (ctx.fmtcnt >= 0 && + PyUnicode_READ(ctx.fmtkind, ctx.fmtdata, ctx.fmtpos) != '%') { + ctx.fmtpos++; + ctx.fmtcnt--; + } + if (ctx.fmtcnt < 0) { + ctx.fmtpos--; + ctx.writer.overallocate = 0; + } + + if (_PyUnicodeWriter_WriteSubstring(&ctx.writer, ctx.fmtstr, + nonfmtpos, ctx.fmtpos) < 0) + goto onError; + } + else { + ctx.fmtpos++; + if (unicode_format_arg(&ctx) == -1) + goto onError; + } + } + + if (ctx.argidx < ctx.arglen && !ctx.dict) { + PyErr_Format(PyExc_TypeError, + "not all arguments converted during string formatting " + "(required %zd, got %zd)", + ctx.arglen < 0 ? 0 : ctx.argidx, + ctx.arglen < 0 ? 1 : ctx.arglen); + goto onError; + } + + if (ctx.args_owned) { + Py_DECREF(ctx.args); + } + return _PyUnicodeWriter_Finish(&ctx.writer); + + onError: + _PyUnicodeWriter_Dealloc(&ctx.writer); + if (ctx.args_owned) { + Py_DECREF(ctx.args); + } + return NULL; +} diff --git a/Objects/unicode_formatter.c b/Objects/unicode_formatter.c new file mode 100644 index 00000000000000..b8604d1355940a --- /dev/null +++ b/Objects/unicode_formatter.c @@ -0,0 +1,1961 @@ +/* implements the unicode (as opposed to string) version of the + built-in formatters for string, int, float. that is, the versions + of int.__float__, etc., that take and return unicode objects */ + +#include "Python.h" +#include "pycore_fileutils.h" // _Py_GetLocaleconvNumeric() +#include "pycore_long.h" // _PyLong_FormatWriter() +#include "pycore_unicodeobject.h" // PyUnicode_MAX_CHAR_VALUE() +#include <locale.h> + + +/* _PyUnicode_InsertThousandsGrouping() helper functions */ + +typedef struct { + const char *grouping; + char previous; + Py_ssize_t i; /* Where we're currently pointing in grouping. */ +} GroupGenerator; + + +static void +GroupGenerator_init(GroupGenerator *self, const char *grouping) +{ + self->grouping = grouping; + self->i = 0; + self->previous = 0; +} + + +/* Returns the next grouping, or 0 to signify end. */ +static Py_ssize_t +GroupGenerator_next(GroupGenerator *self) +{ + /* Note that we don't really do much error checking here. If a + grouping string contains just CHAR_MAX, for example, then just + terminate the generator. That shouldn't happen, but at least we + fail gracefully. */ + switch (self->grouping[self->i]) { + case 0: + return self->previous; + case CHAR_MAX: + /* Stop the generator. */ + return 0; + default: { + char ch = self->grouping[self->i]; + self->previous = ch; + self->i++; + return (Py_ssize_t)ch; + } + } +} + + +/* Fill in some digits, leading zeros, and thousands separator. All + are optional, depending on when we're called. */ +static void +InsertThousandsGrouping_fill(_PyUnicodeWriter *writer, Py_ssize_t *buffer_pos, + PyObject *digits, Py_ssize_t *digits_pos, + Py_ssize_t n_chars, Py_ssize_t n_zeros, + PyObject *thousands_sep, Py_ssize_t thousands_sep_len, + Py_UCS4 *maxchar, int forward) +{ + if (!writer) { + /* if maxchar > 127, maxchar is already set */ + if (*maxchar == 127 && thousands_sep) { + Py_UCS4 maxchar2 = PyUnicode_MAX_CHAR_VALUE(thousands_sep); + *maxchar = Py_MAX(*maxchar, maxchar2); + } + return; + } + + if (thousands_sep) { + if (!forward) { + *buffer_pos -= thousands_sep_len; + } + /* Copy the thousands_sep chars into the buffer. */ + _PyUnicode_FastCopyCharacters(writer->buffer, *buffer_pos, + thousands_sep, 0, + thousands_sep_len); + if (forward) { + *buffer_pos += thousands_sep_len; + } + } + + if (!forward) { + *buffer_pos -= n_chars; + *digits_pos -= n_chars; + } + _PyUnicode_FastCopyCharacters(writer->buffer, *buffer_pos, + digits, *digits_pos, + n_chars); + if (forward) { + *buffer_pos += n_chars; + *digits_pos += n_chars; + } + + if (n_zeros) { + if (!forward) { + *buffer_pos -= n_zeros; + } + int kind = PyUnicode_KIND(writer->buffer); + void *data = PyUnicode_DATA(writer->buffer); + _PyUnicode_Fill(kind, data, '0', *buffer_pos, n_zeros); + if (forward) { + *buffer_pos += n_zeros; + } + } +} + + +/** + * InsertThousandsGrouping: + * @writer: Unicode writer. + * @n_buffer: Number of characters in @buffer. + * @digits: Digits we're reading from. If count is non-NULL, this is unused. + * @d_pos: Start of digits string. + * @n_digits: The number of digits in the string, in which we want + * to put the grouping chars. + * @min_width: The minimum width of the digits in the output string. + * Output will be zero-padded on the left to fill. + * @grouping: see definition in localeconv(). + * @thousands_sep: see definition in localeconv(). + * + * There are 2 modes: counting and filling. If @writer is NULL, + * we are in counting mode, else filling mode. + * If counting, the required buffer size is returned. + * If filling, we know the buffer will be large enough, so we don't + * need to pass in the buffer size. + * Inserts thousand grouping characters (as defined by grouping and + * thousands_sep) into @writer. + * + * Return value: -1 on error, number of characters otherwise. + **/ +static Py_ssize_t +_PyUnicode_InsertThousandsGrouping( + _PyUnicodeWriter *writer, + Py_ssize_t n_buffer, + PyObject *digits, + Py_ssize_t d_pos, + Py_ssize_t n_digits, + Py_ssize_t min_width, + const char *grouping, + PyObject *thousands_sep, + Py_UCS4 *maxchar, + int forward) +{ + min_width = Py_MAX(0, min_width); + if (writer) { + assert(digits != NULL); + assert(maxchar == NULL); + } + else { + assert(digits == NULL); + assert(maxchar != NULL); + } + assert(0 <= d_pos); + assert(0 <= n_digits); + assert(grouping != NULL); + + Py_ssize_t count = 0; + Py_ssize_t n_zeros; + int loop_broken = 0; + int use_separator = 0; /* First time through, don't append the + separator. They only go between + groups. */ + Py_ssize_t buffer_pos; + Py_ssize_t digits_pos; + Py_ssize_t len; + Py_ssize_t n_chars; + Py_ssize_t remaining = n_digits; /* Number of chars remaining to + be looked at */ + /* A generator that returns all of the grouping widths, until it + returns 0. */ + GroupGenerator groupgen; + GroupGenerator_init(&groupgen, grouping); + const Py_ssize_t thousands_sep_len = PyUnicode_GET_LENGTH(thousands_sep); + + /* if digits are not grouped, thousands separator + should be an empty string */ + assert(!(grouping[0] == CHAR_MAX && thousands_sep_len != 0)); + + digits_pos = d_pos + (forward ? 0 : n_digits); + if (writer) { + buffer_pos = writer->pos + (forward ? 0 : n_buffer); + assert(buffer_pos <= PyUnicode_GET_LENGTH(writer->buffer)); + assert(digits_pos <= PyUnicode_GET_LENGTH(digits)); + } + else { + buffer_pos = forward ? 0 : n_buffer; + } + + if (!writer) { + *maxchar = 127; + } + + while ((len = GroupGenerator_next(&groupgen)) > 0) { + len = Py_MIN(len, Py_MAX(Py_MAX(remaining, min_width), 1)); + n_zeros = Py_MAX(0, len - remaining); + n_chars = Py_MAX(0, Py_MIN(remaining, len)); + + /* Use n_zero zero's and n_chars chars */ + + /* Count only, don't do anything. */ + count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars; + + /* Copy into the writer. */ + InsertThousandsGrouping_fill(writer, &buffer_pos, + digits, &digits_pos, + n_chars, n_zeros, + use_separator ? thousands_sep : NULL, + thousands_sep_len, maxchar, forward); + + /* Use a separator next time. */ + use_separator = 1; + + remaining -= n_chars; + min_width -= len; + + if (remaining <= 0 && min_width <= 0) { + loop_broken = 1; + break; + } + min_width -= thousands_sep_len; + } + if (!loop_broken) { + /* We left the loop without using a break statement. */ + + len = Py_MAX(Py_MAX(remaining, min_width), 1); + n_zeros = Py_MAX(0, len - remaining); + n_chars = Py_MAX(0, Py_MIN(remaining, len)); + + /* Use n_zero zero's and n_chars chars */ + count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars; + + /* Copy into the writer. */ + InsertThousandsGrouping_fill(writer, &buffer_pos, + digits, &digits_pos, + n_chars, n_zeros, + use_separator ? thousands_sep : NULL, + thousands_sep_len, maxchar, forward); + } + return count; +} + + +/* Raises an exception about an unknown presentation type for this + * type. */ + +static void +unknown_presentation_type(Py_UCS4 presentation_type, + const char* type_name) +{ + /* %c might be out-of-range, hence the two cases. */ + if (presentation_type > 32 && presentation_type < 128) + PyErr_Format(PyExc_ValueError, + "Unknown format code '%c' " + "for object of type '%.200s'", + (char)presentation_type, + type_name); + else + PyErr_Format(PyExc_ValueError, + "Unknown format code '\\x%x' " + "for object of type '%.200s'", + (unsigned int)presentation_type, + type_name); +} + +static void +invalid_thousands_separator_type(char specifier, Py_UCS4 presentation_type) +{ + assert(specifier == ',' || specifier == '_'); + if (presentation_type > 32 && presentation_type < 128) + PyErr_Format(PyExc_ValueError, + "Cannot specify '%c' with '%c'.", + specifier, (char)presentation_type); + else + PyErr_Format(PyExc_ValueError, + "Cannot specify '%c' with '\\x%x'.", + specifier, (unsigned int)presentation_type); +} + +static void +invalid_comma_and_underscore(void) +{ + PyErr_Format(PyExc_ValueError, "Cannot specify both ',' and '_'."); +} + +/* + get_integer consumes 0 or more decimal digit characters from an + input string, updates *result with the corresponding positive + integer, and returns the number of digits consumed. + + returns -1 on error. +*/ +static int +get_integer(PyObject *str, Py_ssize_t *ppos, Py_ssize_t end, + Py_ssize_t *result) +{ + Py_ssize_t accumulator, digitval, pos = *ppos; + int numdigits; + int kind = PyUnicode_KIND(str); + const void *data = PyUnicode_DATA(str); + + accumulator = numdigits = 0; + for (; pos < end; pos++, numdigits++) { + digitval = Py_UNICODE_TODECIMAL(PyUnicode_READ(kind, data, pos)); + if (digitval < 0) + break; + /* + Detect possible overflow before it happens: + + accumulator * 10 + digitval > PY_SSIZE_T_MAX if and only if + accumulator > (PY_SSIZE_T_MAX - digitval) / 10. + */ + if (accumulator > (PY_SSIZE_T_MAX - digitval) / 10) { + PyErr_Format(PyExc_ValueError, + "Too many decimal digits in format string"); + *ppos = pos; + return -1; + } + accumulator = accumulator * 10 + digitval; + } + *ppos = pos; + *result = accumulator; + return numdigits; +} + +/************************************************************************/ +/*********** standard format specifier parsing **************************/ +/************************************************************************/ + +/* returns true if this character is a specifier alignment token */ +Py_LOCAL_INLINE(int) +is_alignment_token(Py_UCS4 c) +{ + switch (c) { + case '<': case '>': case '=': case '^': + return 1; + default: + return 0; + } +} + +/* returns true if this character is a sign element */ +Py_LOCAL_INLINE(int) +is_sign_element(Py_UCS4 c) +{ + switch (c) { + case ' ': case '+': case '-': + return 1; + default: + return 0; + } +} + +/* Locale type codes. LT_NO_LOCALE must be zero. */ +enum LocaleType { + LT_NO_LOCALE = 0, + LT_DEFAULT_LOCALE = ',', + LT_UNDERSCORE_LOCALE = '_', + LT_UNDER_FOUR_LOCALE, + LT_CURRENT_LOCALE +}; + +typedef struct { + Py_UCS4 fill_char; + Py_UCS4 align; + int alternate; + int no_neg_0; + Py_UCS4 sign; + Py_ssize_t width; + enum LocaleType thousands_separators; + Py_ssize_t precision; + enum LocaleType frac_thousands_separator; + Py_UCS4 type; +} InternalFormatSpec; + + +/* + ptr points to the start of the format_spec, end points just past its end. + fills in format with the parsed information. + returns 1 on success, 0 on failure. + if failure, sets the exception +*/ +static int +parse_internal_render_format_spec(PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end, + InternalFormatSpec *format, + char default_type, + char default_align) +{ + Py_ssize_t pos = start; + int kind = PyUnicode_KIND(format_spec); + const void *data = PyUnicode_DATA(format_spec); + /* end-pos is used throughout this code to specify the length of + the input string */ +#define READ_spec(index) PyUnicode_READ(kind, data, index) + + Py_ssize_t consumed; + int align_specified = 0; + int fill_char_specified = 0; + + format->fill_char = ' '; + format->align = default_align; + format->alternate = 0; + format->no_neg_0 = 0; + format->sign = '\0'; + format->width = -1; + format->thousands_separators = LT_NO_LOCALE; + format->frac_thousands_separator = LT_NO_LOCALE; + format->precision = -1; + format->type = default_type; + + /* If the second char is an alignment token, + then parse the fill char */ + if (end-pos >= 2 && is_alignment_token(READ_spec(pos+1))) { + format->align = READ_spec(pos+1); + format->fill_char = READ_spec(pos); + fill_char_specified = 1; + align_specified = 1; + pos += 2; + } + else if (end-pos >= 1 && is_alignment_token(READ_spec(pos))) { + format->align = READ_spec(pos); + align_specified = 1; + ++pos; + } + + /* Parse the various sign options */ + if (end-pos >= 1 && is_sign_element(READ_spec(pos))) { + format->sign = READ_spec(pos); + ++pos; + } + + /* If the next character is z, request coercion of negative 0. + Applies only to floats. */ + if (end-pos >= 1 && READ_spec(pos) == 'z') { + format->no_neg_0 = 1; + ++pos; + } + + /* If the next character is #, we're in alternate mode. This only + applies to integers. */ + if (end-pos >= 1 && READ_spec(pos) == '#') { + format->alternate = 1; + ++pos; + } + + /* The special case for 0-padding (backwards compat) */ + if (!fill_char_specified && end-pos >= 1 && READ_spec(pos) == '0') { + format->fill_char = '0'; + if (!align_specified && default_align == '>') { + format->align = '='; + } + ++pos; + } + + consumed = get_integer(format_spec, &pos, end, &format->width); + if (consumed == -1) + /* Overflow error. Exception already set. */ + return 0; + + /* If consumed is 0, we didn't consume any characters for the + width. In that case, reset the width to -1, because + get_integer() will have set it to zero. -1 is how we record + that the width wasn't specified. */ + if (consumed == 0) + format->width = -1; + + /* Comma signifies add thousands separators */ + if (end-pos && READ_spec(pos) == ',') { + format->thousands_separators = LT_DEFAULT_LOCALE; + ++pos; + } + /* Underscore signifies add thousands separators */ + if (end-pos && READ_spec(pos) == '_') { + if (format->thousands_separators != LT_NO_LOCALE) { + invalid_comma_and_underscore(); + return 0; + } + format->thousands_separators = LT_UNDERSCORE_LOCALE; + ++pos; + } + if (end-pos && READ_spec(pos) == ',') { + if (format->thousands_separators == LT_UNDERSCORE_LOCALE) { + invalid_comma_and_underscore(); + return 0; + } + } + + /* Parse field precision */ + if (end-pos && READ_spec(pos) == '.') { + ++pos; + + consumed = get_integer(format_spec, &pos, end, &format->precision); + if (consumed == -1) + /* Overflow error. Exception already set. */ + return 0; + + if (end-pos && READ_spec(pos) == ',') { + if (consumed == 0) { + format->precision = -1; + } + format->frac_thousands_separator = LT_DEFAULT_LOCALE; + ++pos; + ++consumed; + } + if (end-pos && READ_spec(pos) == '_') { + if (format->frac_thousands_separator != LT_NO_LOCALE) { + invalid_comma_and_underscore(); + return 0; + } + if (consumed == 0) { + format->precision = -1; + } + format->frac_thousands_separator = LT_UNDERSCORE_LOCALE; + ++pos; + ++consumed; + } + if (end-pos && READ_spec(pos) == ',') { + if (format->frac_thousands_separator == LT_UNDERSCORE_LOCALE) { + invalid_comma_and_underscore(); + return 0; + } + } + + /* Not having a precision or underscore/comma after a dot + is an error. */ + if (consumed == 0) { + PyErr_Format(PyExc_ValueError, + "Format specifier missing precision"); + return 0; + } + + } + + /* Finally, parse the type field. */ + + if (end-pos > 1) { + /* More than one char remains, so this is an invalid format + specifier. */ + /* Create a temporary object that contains the format spec we're + operating on. It's format_spec[start:end] (in Python syntax). */ + PyObject* actual_format_spec = PyUnicode_FromKindAndData(kind, + (char*)data + kind*start, + end-start); + if (actual_format_spec != NULL) { + PyErr_Format(PyExc_ValueError, + "Invalid format specifier '%U' for object of type '%.200s'", + actual_format_spec, Py_TYPE(obj)->tp_name); + Py_DECREF(actual_format_spec); + } + return 0; + } + + if (end-pos == 1) { + format->type = READ_spec(pos); + ++pos; + } + + /* Do as much validating as we can, just by looking at the format + specifier. Do not take into account what type of formatting + we're doing (int, float, string). */ + + if (format->thousands_separators) { + switch (format->type) { + case 'd': + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + case '%': + case 'F': + case '\0': + /* These are allowed. See PEP 378.*/ + break; + case 'b': + case 'o': + case 'x': + case 'X': + /* Underscores are allowed in bin/oct/hex. See PEP 515. */ + if (format->thousands_separators == LT_UNDERSCORE_LOCALE) { + /* Every four digits, not every three, in bin/oct/hex. */ + format->thousands_separators = LT_UNDER_FOUR_LOCALE; + break; + } + _Py_FALLTHROUGH; + default: + invalid_thousands_separator_type(format->thousands_separators, format->type); + return 0; + } + } + + if (format->type == 'n' + && format->frac_thousands_separator != LT_NO_LOCALE) + { + invalid_thousands_separator_type(format->frac_thousands_separator, + format->type); + return 0; + } + + assert (format->align <= 127); + assert (format->sign <= 127); + return 1; +} + +/* Calculate the padding needed. */ +static void +calc_padding(Py_ssize_t nchars, Py_ssize_t width, Py_UCS4 align, + Py_ssize_t *n_lpadding, Py_ssize_t *n_rpadding, + Py_ssize_t *n_total) +{ + if (width >= 0) { + if (nchars > width) + *n_total = nchars; + else + *n_total = width; + } + else { + /* not specified, use all of the chars and no more */ + *n_total = nchars; + } + + /* Figure out how much leading space we need, based on the + aligning */ + if (align == '>') + *n_lpadding = *n_total - nchars; + else if (align == '^') + *n_lpadding = (*n_total - nchars) / 2; + else if (align == '<' || align == '=') + *n_lpadding = 0; + else { + /* We should never have an unspecified alignment. */ + Py_UNREACHABLE(); + } + + *n_rpadding = *n_total - nchars - *n_lpadding; +} + +/* Do the padding, and return a pointer to where the caller-supplied + content goes. */ +static int +fill_padding(_PyUnicodeWriter *writer, + Py_ssize_t nchars, + Py_UCS4 fill_char, Py_ssize_t n_lpadding, + Py_ssize_t n_rpadding) +{ + Py_ssize_t pos; + + /* Pad on left. */ + if (n_lpadding) { + pos = writer->pos; + _PyUnicode_FastFill(writer->buffer, pos, n_lpadding, fill_char); + } + + /* Pad on right. */ + if (n_rpadding) { + pos = writer->pos + nchars + n_lpadding; + _PyUnicode_FastFill(writer->buffer, pos, n_rpadding, fill_char); + } + + /* Pointer to the user content. */ + writer->pos += n_lpadding; + return 0; +} + +/************************************************************************/ +/*********** common routines for numeric formatting *********************/ +/************************************************************************/ + +/* Locale info needed for formatting integers and the part of floats + before and including the decimal. Note that locales only support + 8-bit chars, not unicode. */ +typedef struct { + PyObject *decimal_point; + PyObject *thousands_sep; + PyObject *frac_thousands_sep; + const char *grouping; + char *grouping_buffer; +} LocaleInfo; + +#define LocaleInfo_STATIC_INIT {0, 0, 0, 0} + +/* describes the layout for an integer, see the comment in + calc_number_widths() for details */ +typedef struct { + Py_ssize_t n_lpadding; + Py_ssize_t n_prefix; + Py_ssize_t n_spadding; + Py_ssize_t n_rpadding; + char sign; + Py_ssize_t n_sign; /* number of digits needed for sign (0/1) */ + Py_ssize_t n_grouped_digits; /* Space taken up by the digits, including + any grouping chars. */ + Py_ssize_t n_decimal; /* 0 if only an integer */ + Py_ssize_t n_remainder; /* Digits in decimal and/or exponent part, + excluding the decimal itself, if + present. */ + Py_ssize_t n_frac; + Py_ssize_t n_grouped_frac_digits; + + /* These 2 are not the widths of fields, but are needed by + STRINGLIB_GROUPING. */ + Py_ssize_t n_digits; /* The number of digits before a decimal + or exponent. */ + Py_ssize_t n_min_width; /* The min_width we used when we computed + the n_grouped_digits width. */ +} NumberFieldWidths; + + +/* Given a number of the form: + digits[remainder] + where ptr points to the start and end points to the end, find where + the integer part ends. This could be a decimal, an exponent, both, + or neither. + If a decimal point is present, set *has_decimal and increment + remainder beyond it. + Results are undefined (but shouldn't crash) for improperly + formatted strings. +*/ +static void +parse_number(PyObject *s, Py_ssize_t pos, Py_ssize_t end, + Py_ssize_t *n_remainder, Py_ssize_t *n_frac, int *has_decimal) +{ + Py_ssize_t frac; + int kind = PyUnicode_KIND(s); + const void *data = PyUnicode_DATA(s); + + while (pos<end && Py_ISDIGIT(PyUnicode_READ(kind, data, pos))) { + ++pos; + } + frac = pos; + + /* Does remainder start with a decimal point? */ + *has_decimal = pos<end && PyUnicode_READ(kind, data, frac) == '.'; + + /* Skip the decimal point. */ + if (*has_decimal) { + frac++; + pos++; + } + + while (pos<end && Py_ISDIGIT(PyUnicode_READ(kind, data, pos))) { + ++pos; + } + + *n_frac = pos - frac; + *n_remainder = end - pos; +} + +/* not all fields of format are used. for example, precision is + unused. should this take discrete params in order to be more clear + about what it does? or is passing a single format parameter easier + and more efficient enough to justify a little obfuscation? + Return -1 on error. */ +static Py_ssize_t +calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, + Py_UCS4 sign_char, Py_ssize_t n_start, + Py_ssize_t n_end, Py_ssize_t n_remainder, Py_ssize_t n_frac, + int has_decimal, const LocaleInfo *locale, + const InternalFormatSpec *format, Py_UCS4 *maxchar) +{ + Py_ssize_t n_non_digit_non_padding; + Py_ssize_t n_padding; + + spec->n_digits = n_end - n_start - n_frac - n_remainder - (has_decimal?1:0); + spec->n_lpadding = 0; + spec->n_prefix = n_prefix; + spec->n_decimal = has_decimal ? PyUnicode_GET_LENGTH(locale->decimal_point) : 0; + spec->n_remainder = n_remainder; + spec->n_frac = n_frac; + spec->n_spadding = 0; + spec->n_rpadding = 0; + spec->sign = '\0'; + spec->n_sign = 0; + + /* the output will look like: + | | + | <lpadding> <sign> <prefix> <spadding> <grouped_digits> <decimal> <remainder> <rpadding> | + | | + + sign is computed from format->sign and the actual + sign of the number + + prefix is given (it's for the '0x' prefix) + + digits is already known + + the total width is either given, or computed from the + actual digits + + only one of lpadding, spadding, and rpadding can be non-zero, + and it's calculated from the width and other fields + */ + + /* compute the various parts we're going to write */ + switch (format->sign) { + case '+': + /* always put a + or - */ + spec->n_sign = 1; + spec->sign = (sign_char == '-' ? '-' : '+'); + break; + case ' ': + spec->n_sign = 1; + spec->sign = (sign_char == '-' ? '-' : ' '); + break; + default: + /* Not specified, or the default (-) */ + if (sign_char == '-') { + spec->n_sign = 1; + spec->sign = '-'; + } + } + + if (spec->n_frac == 0) { + spec->n_grouped_frac_digits = 0; + } + else { + Py_UCS4 grouping_maxchar; + spec->n_grouped_frac_digits = _PyUnicode_InsertThousandsGrouping( + NULL, 0, + NULL, 0, spec->n_frac, + spec->n_frac, + locale->grouping, locale->frac_thousands_sep, &grouping_maxchar, 1); + if (spec->n_grouped_frac_digits == -1) { + return -1; + } + *maxchar = Py_MAX(*maxchar, grouping_maxchar); + } + + /* The number of chars used for non-digits and non-padding. */ + n_non_digit_non_padding = spec->n_sign + spec->n_prefix + spec->n_decimal + + + spec->n_frac + spec->n_remainder; + + /* min_width can go negative, that's okay. format->width == -1 means + we don't care. */ + if (format->fill_char == '0' && format->align == '=') + spec->n_min_width = (format->width - n_non_digit_non_padding + + spec->n_frac - spec->n_grouped_frac_digits); + else + spec->n_min_width = 0; + + if (spec->n_digits == 0) + /* This case only occurs when using 'c' formatting, we need + to special case it because the grouping code always wants + to have at least one character. */ + spec->n_grouped_digits = 0; + else { + Py_UCS4 grouping_maxchar; + spec->n_grouped_digits = _PyUnicode_InsertThousandsGrouping( + NULL, 0, + NULL, 0, spec->n_digits, + spec->n_min_width, + locale->grouping, locale->thousands_sep, &grouping_maxchar, 0); + if (spec->n_grouped_digits == -1) { + return -1; + } + *maxchar = Py_MAX(*maxchar, grouping_maxchar); + } + + /* Given the desired width and the total of digit and non-digit + space we consume, see if we need any padding. format->width can + be negative (meaning no padding), but this code still works in + that case. */ + n_padding = format->width - + (n_non_digit_non_padding + spec->n_grouped_digits + + spec->n_grouped_frac_digits - spec->n_frac); + if (n_padding > 0) { + /* Some padding is needed. Determine if it's left, space, or right. */ + switch (format->align) { + case '<': + spec->n_rpadding = n_padding; + break; + case '^': + spec->n_lpadding = n_padding / 2; + spec->n_rpadding = n_padding - spec->n_lpadding; + break; + case '=': + spec->n_spadding = n_padding; + break; + case '>': + spec->n_lpadding = n_padding; + break; + default: + /* Shouldn't get here */ + Py_UNREACHABLE(); + } + } + + if (spec->n_lpadding || spec->n_spadding || spec->n_rpadding) + *maxchar = Py_MAX(*maxchar, format->fill_char); + + if (spec->n_decimal) + *maxchar = Py_MAX(*maxchar, PyUnicode_MAX_CHAR_VALUE(locale->decimal_point)); + + return spec->n_lpadding + spec->n_sign + spec->n_prefix + + spec->n_spadding + spec->n_grouped_digits + spec->n_decimal + + spec->n_grouped_frac_digits + spec->n_remainder + spec->n_rpadding; +} + +/* Fill in the digit parts of a number's string representation, + as determined in calc_number_widths(). + Return -1 on error, or 0 on success. */ +static int +fill_number(_PyUnicodeWriter *writer, const NumberFieldWidths *spec, + PyObject *digits, Py_ssize_t d_start, + PyObject *prefix, Py_ssize_t p_start, + Py_UCS4 fill_char, + LocaleInfo *locale, int toupper) +{ + /* Used to keep track of digits, decimal, and remainder. */ + Py_ssize_t d_pos = d_start; + const int kind = writer->kind; + const void *data = writer->data; + Py_ssize_t r; + + if (spec->n_lpadding) { + _PyUnicode_FastFill(writer->buffer, + writer->pos, spec->n_lpadding, fill_char); + writer->pos += spec->n_lpadding; + } + if (spec->n_sign == 1) { + PyUnicode_WRITE(kind, data, writer->pos, spec->sign); + writer->pos++; + } + if (spec->n_prefix) { + _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, + prefix, p_start, + spec->n_prefix); + if (toupper) { + Py_ssize_t t; + for (t = 0; t < spec->n_prefix; t++) { + Py_UCS4 c = PyUnicode_READ(kind, data, writer->pos + t); + c = Py_TOUPPER(c); + assert (c <= 127); + PyUnicode_WRITE(kind, data, writer->pos + t, c); + } + } + writer->pos += spec->n_prefix; + } + if (spec->n_spadding) { + _PyUnicode_FastFill(writer->buffer, + writer->pos, spec->n_spadding, fill_char); + writer->pos += spec->n_spadding; + } + + /* Only for type 'c' special case, it has no digits. */ + if (spec->n_digits != 0) { + /* Fill the digits with InsertThousandsGrouping. */ + r = _PyUnicode_InsertThousandsGrouping( + writer, spec->n_grouped_digits, + digits, d_pos, spec->n_digits, + spec->n_min_width, + locale->grouping, locale->thousands_sep, NULL, 0); + if (r == -1) + return -1; + assert(r == spec->n_grouped_digits); + d_pos += spec->n_digits; + } + if (toupper) { + Py_ssize_t t; + for (t = 0; t < spec->n_grouped_digits; t++) { + Py_UCS4 c = PyUnicode_READ(kind, data, writer->pos + t); + c = Py_TOUPPER(c); + if (c > 127) { + PyErr_SetString(PyExc_SystemError, "non-ascii grouped digit"); + return -1; + } + PyUnicode_WRITE(kind, data, writer->pos + t, c); + } + } + writer->pos += spec->n_grouped_digits; + + if (spec->n_decimal) { + _PyUnicode_FastCopyCharacters( + writer->buffer, writer->pos, + locale->decimal_point, 0, spec->n_decimal); + writer->pos += spec->n_decimal; + d_pos += 1; + } + + if (spec->n_frac) { + r = _PyUnicode_InsertThousandsGrouping( + writer, spec->n_grouped_frac_digits, + digits, d_pos, spec->n_frac, spec->n_frac, + locale->grouping, locale->frac_thousands_sep, NULL, 1); + if (r == -1) { + return -1; + } + assert(r == spec->n_grouped_frac_digits); + d_pos += spec->n_frac; + writer->pos += spec->n_grouped_frac_digits; + } + + if (spec->n_remainder) { + _PyUnicode_FastCopyCharacters( + writer->buffer, writer->pos, + digits, d_pos, spec->n_remainder); + writer->pos += spec->n_remainder; + /* d_pos += spec->n_remainder; */ + } + + if (spec->n_rpadding) { + _PyUnicode_FastFill(writer->buffer, + writer->pos, spec->n_rpadding, + fill_char); + writer->pos += spec->n_rpadding; + } + return 0; +} + +static const char no_grouping[1] = {CHAR_MAX}; + +/* Find the decimal point character(s?), thousands_separator(s?), and + grouping description, either for the current locale if type is + LT_CURRENT_LOCALE, a hard-coded locale if LT_DEFAULT_LOCALE or + LT_UNDERSCORE_LOCALE/LT_UNDER_FOUR_LOCALE, or none if LT_NO_LOCALE. */ +static int +get_locale_info(enum LocaleType type, enum LocaleType frac_type, + LocaleInfo *locale_info) +{ + switch (type) { + case LT_CURRENT_LOCALE: { + struct lconv *lc = localeconv(); + if (_Py_GetLocaleconvNumeric(lc, + &locale_info->decimal_point, + &locale_info->thousands_sep) < 0) { + return -1; + } + + /* localeconv() grouping can become a dangling pointer or point + to a different string if another thread calls localeconv() during + the string formatting. Copy the string to avoid this risk. */ + locale_info->grouping_buffer = _PyMem_Strdup(lc->grouping); + if (locale_info->grouping_buffer == NULL) { + PyErr_NoMemory(); + return -1; + } + locale_info->grouping = locale_info->grouping_buffer; + break; + } + case LT_DEFAULT_LOCALE: + case LT_UNDERSCORE_LOCALE: + case LT_UNDER_FOUR_LOCALE: + locale_info->decimal_point = PyUnicode_FromOrdinal('.'); + locale_info->thousands_sep = PyUnicode_FromOrdinal( + type == LT_DEFAULT_LOCALE ? ',' : '_'); + if (!locale_info->decimal_point || !locale_info->thousands_sep) + return -1; + if (type != LT_UNDER_FOUR_LOCALE) + locale_info->grouping = "\3"; /* Group every 3 characters. The + (implicit) trailing 0 means repeat + infinitely. */ + else + locale_info->grouping = "\4"; /* Bin/oct/hex group every four. */ + break; + case LT_NO_LOCALE: + locale_info->decimal_point = PyUnicode_FromOrdinal('.'); + locale_info->thousands_sep = Py_GetConstant(Py_CONSTANT_EMPTY_STR); + if (!locale_info->decimal_point || !locale_info->thousands_sep) + return -1; + locale_info->grouping = no_grouping; + break; + } + if (frac_type != LT_NO_LOCALE) { + locale_info->frac_thousands_sep = PyUnicode_FromOrdinal( + frac_type == LT_DEFAULT_LOCALE ? ',' : '_'); + if (!locale_info->frac_thousands_sep) { + return -1; + } + if (locale_info->grouping == no_grouping) { + locale_info->grouping = "\3"; + } + } + else { + locale_info->frac_thousands_sep = Py_GetConstant(Py_CONSTANT_EMPTY_STR); + } + return 0; +} + +static void +free_locale_info(LocaleInfo *locale_info) +{ + Py_XDECREF(locale_info->decimal_point); + Py_XDECREF(locale_info->thousands_sep); + Py_XDECREF(locale_info->frac_thousands_sep); + PyMem_Free(locale_info->grouping_buffer); +} + +/************************************************************************/ +/*********** string formatting ******************************************/ +/************************************************************************/ + +static int +format_string_internal(PyObject *value, const InternalFormatSpec *format, + _PyUnicodeWriter *writer) +{ + Py_ssize_t lpad; + Py_ssize_t rpad; + Py_ssize_t total; + Py_ssize_t len; + int result = -1; + Py_UCS4 maxchar; + + len = PyUnicode_GET_LENGTH(value); + + /* sign is not allowed on strings */ + if (format->sign != '\0') { + if (format->sign == ' ') { + PyErr_SetString(PyExc_ValueError, + "Space not allowed in string format specifier"); + } + else { + PyErr_SetString(PyExc_ValueError, + "Sign not allowed in string format specifier"); + } + goto done; + } + + /* negative 0 coercion is not allowed on strings */ + if (format->no_neg_0) { + PyErr_SetString(PyExc_ValueError, + "Negative zero coercion (z) not allowed in string format " + "specifier"); + goto done; + } + + /* alternate is not allowed on strings */ + if (format->alternate) { + PyErr_SetString(PyExc_ValueError, + "Alternate form (#) not allowed in string format " + "specifier"); + goto done; + } + + /* '=' alignment not allowed on strings */ + if (format->align == '=') { + PyErr_SetString(PyExc_ValueError, + "'=' alignment not allowed " + "in string format specifier"); + goto done; + } + + if ((format->width == -1 || format->width <= len) + && (format->precision == -1 || format->precision >= len)) { + /* Fast path */ + return _PyUnicodeWriter_WriteStr(writer, value); + } + + /* if precision is specified, output no more that format.precision + characters */ + if (format->precision >= 0 && len >= format->precision) { + len = format->precision; + } + + calc_padding(len, format->width, format->align, &lpad, &rpad, &total); + + maxchar = writer->maxchar; + if (lpad != 0 || rpad != 0) + maxchar = Py_MAX(maxchar, format->fill_char); + if (PyUnicode_MAX_CHAR_VALUE(value) > maxchar) { + Py_UCS4 valmaxchar = _PyUnicode_FindMaxChar(value, 0, len); + maxchar = Py_MAX(maxchar, valmaxchar); + } + + /* allocate the resulting string */ + if (_PyUnicodeWriter_Prepare(writer, total, maxchar) == -1) + goto done; + + /* Write into that space. First the padding. */ + result = fill_padding(writer, len, format->fill_char, lpad, rpad); + if (result == -1) + goto done; + + /* Then the source string. */ + if (len) { + _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, + value, 0, len); + } + writer->pos += (len + rpad); + result = 0; + +done: + return result; +} + + +/************************************************************************/ +/*********** long formatting ********************************************/ +/************************************************************************/ + +static int +format_long_internal(PyObject *value, const InternalFormatSpec *format, + _PyUnicodeWriter *writer) +{ + int result = -1; + Py_UCS4 maxchar = 127; + PyObject *tmp = NULL; + Py_ssize_t inumeric_chars; + Py_UCS4 sign_char = '\0'; + Py_ssize_t n_digits; /* count of digits need from the computed + string */ + Py_ssize_t n_remainder = 0; /* Used only for 'c' formatting, which + produces non-digits */ + Py_ssize_t n_prefix = 0; /* Count of prefix chars, (e.g., '0x') */ + Py_ssize_t n_total; + Py_ssize_t prefix = 0; + NumberFieldWidths spec; + long x; + + /* Locale settings, either from the actual locale or + from a hard-code pseudo-locale */ + LocaleInfo locale = LocaleInfo_STATIC_INIT; + + /* no precision allowed on integers */ + if (format->precision != -1) { + PyErr_SetString(PyExc_ValueError, + "Precision not allowed in integer format specifier"); + goto done; + } + /* no negative zero coercion on integers */ + if (format->no_neg_0) { + PyErr_SetString(PyExc_ValueError, + "Negative zero coercion (z) not allowed in integer" + " format specifier"); + goto done; + } + + /* special case for character formatting */ + if (format->type == 'c') { + /* error to specify a sign */ + if (format->sign != '\0') { + PyErr_SetString(PyExc_ValueError, + "Sign not allowed with integer" + " format specifier 'c'"); + goto done; + } + /* error to request alternate format */ + if (format->alternate) { + PyErr_SetString(PyExc_ValueError, + "Alternate form (#) not allowed with integer" + " format specifier 'c'"); + goto done; + } + + /* taken from unicodeobject.c formatchar() */ + /* Integer input truncated to a character */ + x = PyLong_AsLong(value); + if (x == -1 && PyErr_Occurred()) + goto done; + if (x < 0 || x > 0x10ffff) { + PyErr_SetString(PyExc_OverflowError, + "%c arg not in range(0x110000)"); + goto done; + } + tmp = PyUnicode_FromOrdinal(x); + inumeric_chars = 0; + n_digits = 1; + maxchar = Py_MAX(maxchar, (Py_UCS4)x); + + /* As a sort-of hack, we tell calc_number_widths that we only + have "remainder" characters. calc_number_widths thinks + these are characters that don't get formatted, only copied + into the output string. We do this for 'c' formatting, + because the characters are likely to be non-digits. */ + n_remainder = 1; + } + else { + int base; + int leading_chars_to_skip = 0; /* Number of characters added by + PyNumber_ToBase that we want to + skip over. */ + + /* Compute the base and how many characters will be added by + PyNumber_ToBase */ + switch (format->type) { + case 'b': + base = 2; + leading_chars_to_skip = 2; /* 0b */ + break; + case 'o': + base = 8; + leading_chars_to_skip = 2; /* 0o */ + break; + case 'x': + case 'X': + base = 16; + leading_chars_to_skip = 2; /* 0x */ + break; + default: /* shouldn't be needed, but stops a compiler warning */ + case 'd': + case 'n': + base = 10; + break; + } + + if (format->sign != '+' && format->sign != ' ' + && format->width == -1 + && format->type != 'X' && format->type != 'n' + && !format->thousands_separators + && PyLong_CheckExact(value)) + { + /* Fast path */ + return _PyLong_FormatWriter(writer, value, base, format->alternate); + } + + /* The number of prefix chars is the same as the leading + chars to skip */ + if (format->alternate) + n_prefix = leading_chars_to_skip; + + /* Do the hard part, converting to a string in a given base */ + tmp = _PyLong_Format(value, base); + if (tmp == NULL) + goto done; + + inumeric_chars = 0; + n_digits = PyUnicode_GET_LENGTH(tmp); + + prefix = inumeric_chars; + + /* Is a sign character present in the output? If so, remember it + and skip it */ + if (PyUnicode_READ_CHAR(tmp, inumeric_chars) == '-') { + sign_char = '-'; + ++prefix; + ++leading_chars_to_skip; + } + + /* Skip over the leading chars (0x, 0b, etc.) */ + n_digits -= leading_chars_to_skip; + inumeric_chars += leading_chars_to_skip; + } + + /* Determine the grouping, separator, and decimal point, if any. */ + if (get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : + format->thousands_separators, 0, + &locale) == -1) + goto done; + + /* Calculate how much memory we'll need. */ + n_total = calc_number_widths(&spec, n_prefix, sign_char, inumeric_chars, + inumeric_chars + n_digits, n_remainder, 0, 0, + &locale, format, &maxchar); + if (n_total == -1) { + goto done; + } + + /* Allocate the memory. */ + if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1) + goto done; + + /* Populate the memory. */ + result = fill_number(writer, &spec, + tmp, inumeric_chars, + tmp, prefix, format->fill_char, + &locale, format->type == 'X'); + +done: + Py_XDECREF(tmp); + free_locale_info(&locale); + return result; +} + +/************************************************************************/ +/*********** float formatting *******************************************/ +/************************************************************************/ + +/* much of this is taken from unicodeobject.c */ +static int +format_float_internal(PyObject *value, + const InternalFormatSpec *format, + _PyUnicodeWriter *writer) +{ + char *buf = NULL; /* buffer returned from PyOS_double_to_string */ + Py_ssize_t n_digits; + Py_ssize_t n_remainder; + Py_ssize_t n_frac; + Py_ssize_t n_total; + int has_decimal; + double val; + int precision, default_precision = 6; + Py_UCS4 type = format->type; + int add_pct = 0; + Py_ssize_t index; + NumberFieldWidths spec; + int flags = 0; + int result = -1; + Py_UCS4 maxchar = 127; + Py_UCS4 sign_char = '\0'; + int float_type; /* Used to see if we have a nan, inf, or regular float. */ + PyObject *unicode_tmp = NULL; + + /* Locale settings, either from the actual locale or + from a hard-code pseudo-locale */ + LocaleInfo locale = LocaleInfo_STATIC_INIT; + + if (format->precision > INT_MAX) { + PyErr_SetString(PyExc_ValueError, "precision too big"); + goto done; + } + precision = (int)format->precision; + + if (format->alternate) + flags |= Py_DTSF_ALT; + if (format->no_neg_0) + flags |= Py_DTSF_NO_NEG_0; + + if (type == '\0') { + /* Omitted type specifier. Behaves in the same way as repr(x) + and str(x) if no precision is given, else like 'g', but with + at least one digit after the decimal point. */ + flags |= Py_DTSF_ADD_DOT_0; + type = 'r'; + default_precision = 0; + } + + if (type == 'n') + /* 'n' is the same as 'g', except for the locale used to + format the result. We take care of that later. */ + type = 'g'; + + val = PyFloat_AsDouble(value); + if (val == -1.0 && PyErr_Occurred()) + goto done; + + if (type == '%') { + type = 'f'; + val *= 100; + add_pct = 1; + } + + if (precision < 0) + precision = default_precision; + else if (type == 'r') + type = 'g'; + + /* Cast "type", because if we're in unicode we need to pass an + 8-bit char. This is safe, because we've restricted what "type" + can be. */ + buf = PyOS_double_to_string(val, (char)type, precision, flags, + &float_type); + if (buf == NULL) + goto done; + n_digits = strlen(buf); + + if (add_pct) { + /* We know that buf has a trailing zero (since we just called + strlen() on it), and we don't use that fact any more. So we + can just write over the trailing zero. */ + buf[n_digits] = '%'; + n_digits += 1; + } + + if (format->sign != '+' && format->sign != ' ' + && format->width == -1 + && format->type != 'n' + && !format->thousands_separators + && !format->frac_thousands_separator) + { + /* Fast path */ + result = _PyUnicodeWriter_WriteASCIIString(writer, buf, n_digits); + PyMem_Free(buf); + return result; + } + + /* Since there is no unicode version of PyOS_double_to_string, + just use the 8 bit version and then convert to unicode. */ + unicode_tmp = _PyUnicode_FromASCII(buf, n_digits); + PyMem_Free(buf); + if (unicode_tmp == NULL) + goto done; + + /* Is a sign character present in the output? If so, remember it + and skip it */ + index = 0; + if (PyUnicode_READ_CHAR(unicode_tmp, index) == '-') { + sign_char = '-'; + ++index; + --n_digits; + } + + /* Determine if we have any "remainder" (after the digits, might include + decimal or exponent or both (or neither)) */ + parse_number(unicode_tmp, index, index + n_digits, + &n_remainder, &n_frac, &has_decimal); + + /* Determine the grouping, separator, and decimal point, if any. */ + if (get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : + format->thousands_separators, + format->frac_thousands_separator, + &locale) == -1) + goto done; + + /* Calculate how much memory we'll need. */ + n_total = calc_number_widths(&spec, 0, sign_char, index, + index + n_digits, n_remainder, n_frac, + has_decimal, &locale, format, &maxchar); + if (n_total == -1) { + goto done; + } + + /* Allocate the memory. */ + if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1) + goto done; + + /* Populate the memory. */ + result = fill_number(writer, &spec, + unicode_tmp, index, + NULL, 0, format->fill_char, + &locale, 0); + +done: + Py_XDECREF(unicode_tmp); + free_locale_info(&locale); + return result; +} + +/************************************************************************/ +/*********** complex formatting *****************************************/ +/************************************************************************/ + +static int +format_complex_internal(PyObject *value, + const InternalFormatSpec *format, + _PyUnicodeWriter *writer) +{ + double re; + double im; + char *re_buf = NULL; /* buffer returned from PyOS_double_to_string */ + char *im_buf = NULL; /* buffer returned from PyOS_double_to_string */ + + InternalFormatSpec tmp_format = *format; + Py_ssize_t n_re_digits; + Py_ssize_t n_im_digits; + Py_ssize_t n_re_remainder; + Py_ssize_t n_im_remainder; + Py_ssize_t n_re_frac; + Py_ssize_t n_im_frac; + Py_ssize_t n_re_total; + Py_ssize_t n_im_total; + int re_has_decimal; + int im_has_decimal; + int precision, default_precision = 6; + Py_UCS4 type = format->type; + Py_ssize_t i_re; + Py_ssize_t i_im; + NumberFieldWidths re_spec; + NumberFieldWidths im_spec; + int flags = 0; + int result = -1; + Py_UCS4 maxchar = 127; + int rkind; + void *rdata; + Py_UCS4 re_sign_char = '\0'; + Py_UCS4 im_sign_char = '\0'; + int re_float_type; /* Used to see if we have a nan, inf, or regular float. */ + int im_float_type; + int add_parens = 0; + int skip_re = 0; + Py_ssize_t lpad; + Py_ssize_t rpad; + Py_ssize_t total; + PyObject *re_unicode_tmp = NULL; + PyObject *im_unicode_tmp = NULL; + + /* Locale settings, either from the actual locale or + from a hard-code pseudo-locale */ + LocaleInfo locale = LocaleInfo_STATIC_INIT; + + if (format->precision > INT_MAX) { + PyErr_SetString(PyExc_ValueError, "precision too big"); + goto done; + } + precision = (int)format->precision; + + /* Zero padding is not allowed. */ + if (format->fill_char == '0') { + PyErr_SetString(PyExc_ValueError, + "Zero padding is not allowed in complex format " + "specifier"); + goto done; + } + + /* Neither is '=' alignment . */ + if (format->align == '=') { + PyErr_SetString(PyExc_ValueError, + "'=' alignment flag is not allowed in complex format " + "specifier"); + goto done; + } + + re = PyComplex_RealAsDouble(value); + if (re == -1.0 && PyErr_Occurred()) + goto done; + im = PyComplex_ImagAsDouble(value); + if (im == -1.0 && PyErr_Occurred()) + goto done; + + if (format->alternate) + flags |= Py_DTSF_ALT; + if (format->no_neg_0) + flags |= Py_DTSF_NO_NEG_0; + + if (type == '\0') { + /* Omitted type specifier. Should be like str(self). */ + type = 'r'; + default_precision = 0; + if (re == 0.0 && copysign(1.0, re) == 1.0) + skip_re = 1; + else + add_parens = 1; + } + + if (type == 'n') + /* 'n' is the same as 'g', except for the locale used to + format the result. We take care of that later. */ + type = 'g'; + + if (precision < 0) + precision = default_precision; + else if (type == 'r') + type = 'g'; + + /* Cast "type", because if we're in unicode we need to pass an + 8-bit char. This is safe, because we've restricted what "type" + can be. */ + re_buf = PyOS_double_to_string(re, (char)type, precision, flags, + &re_float_type); + if (re_buf == NULL) + goto done; + im_buf = PyOS_double_to_string(im, (char)type, precision, flags, + &im_float_type); + if (im_buf == NULL) + goto done; + + n_re_digits = strlen(re_buf); + n_im_digits = strlen(im_buf); + + /* Since there is no unicode version of PyOS_double_to_string, + just use the 8 bit version and then convert to unicode. */ + re_unicode_tmp = _PyUnicode_FromASCII(re_buf, n_re_digits); + if (re_unicode_tmp == NULL) + goto done; + i_re = 0; + + im_unicode_tmp = _PyUnicode_FromASCII(im_buf, n_im_digits); + if (im_unicode_tmp == NULL) + goto done; + i_im = 0; + + /* Is a sign character present in the output? If so, remember it + and skip it */ + if (PyUnicode_READ_CHAR(re_unicode_tmp, i_re) == '-') { + re_sign_char = '-'; + ++i_re; + --n_re_digits; + } + if (PyUnicode_READ_CHAR(im_unicode_tmp, i_im) == '-') { + im_sign_char = '-'; + ++i_im; + --n_im_digits; + } + + /* Determine if we have any "remainder" (after the digits, might include + decimal or exponent or both (or neither)) */ + parse_number(re_unicode_tmp, i_re, i_re + n_re_digits, + &n_re_remainder, &n_re_frac, &re_has_decimal); + parse_number(im_unicode_tmp, i_im, i_im + n_im_digits, + &n_im_remainder, &n_im_frac, &im_has_decimal); + + /* Determine the grouping, separator, and decimal point, if any. */ + if (get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : + format->thousands_separators, + format->frac_thousands_separator, + &locale) == -1) + goto done; + + /* Turn off any padding. We'll do it later after we've composed + the numbers without padding. */ + tmp_format.fill_char = '\0'; + tmp_format.align = '<'; + tmp_format.width = -1; + + /* Calculate how much memory we'll need. */ + n_re_total = calc_number_widths(&re_spec, 0, re_sign_char, + i_re, i_re + n_re_digits, n_re_remainder, + n_re_frac, re_has_decimal, &locale, + &tmp_format, &maxchar); + if (n_re_total == -1) { + goto done; + } + + /* Same formatting, but always include a sign, unless the real part is + * going to be omitted, in which case we use whatever sign convention was + * requested by the original format. */ + if (!skip_re) + tmp_format.sign = '+'; + n_im_total = calc_number_widths(&im_spec, 0, im_sign_char, + i_im, i_im + n_im_digits, n_im_remainder, + n_im_frac, im_has_decimal, &locale, + &tmp_format, &maxchar); + if (n_im_total == -1) { + goto done; + } + + if (skip_re) + n_re_total = 0; + + /* Add 1 for the 'j', and optionally 2 for parens. */ + calc_padding(n_re_total + n_im_total + 1 + add_parens * 2, + format->width, format->align, &lpad, &rpad, &total); + + if (lpad || rpad) + maxchar = Py_MAX(maxchar, format->fill_char); + + if (_PyUnicodeWriter_Prepare(writer, total, maxchar) == -1) + goto done; + rkind = writer->kind; + rdata = writer->data; + + /* Populate the memory. First, the padding. */ + result = fill_padding(writer, + n_re_total + n_im_total + 1 + add_parens * 2, + format->fill_char, lpad, rpad); + if (result == -1) + goto done; + + if (add_parens) { + PyUnicode_WRITE(rkind, rdata, writer->pos, '('); + writer->pos++; + } + + if (!skip_re) { + result = fill_number(writer, &re_spec, + re_unicode_tmp, i_re, + NULL, 0, + 0, + &locale, 0); + if (result == -1) + goto done; + } + result = fill_number(writer, &im_spec, + im_unicode_tmp, i_im, + NULL, 0, + 0, + &locale, 0); + if (result == -1) + goto done; + PyUnicode_WRITE(rkind, rdata, writer->pos, 'j'); + writer->pos++; + + if (add_parens) { + PyUnicode_WRITE(rkind, rdata, writer->pos, ')'); + writer->pos++; + } + + writer->pos += rpad; + +done: + PyMem_Free(re_buf); + PyMem_Free(im_buf); + Py_XDECREF(re_unicode_tmp); + Py_XDECREF(im_unicode_tmp); + free_locale_info(&locale); + return result; +} + +/************************************************************************/ +/*********** built in formatters ****************************************/ +/************************************************************************/ +static int +format_obj(PyObject *obj, _PyUnicodeWriter *writer) +{ + PyObject *str; + int err; + + str = PyObject_Str(obj); + if (str == NULL) + return -1; + err = _PyUnicodeWriter_WriteStr(writer, str); + Py_DECREF(str); + return err; +} + +int +_PyUnicode_FormatAdvancedWriter(_PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) +{ + InternalFormatSpec format; + + assert(PyUnicode_Check(obj)); + + /* check for the special case of zero length format spec, make + it equivalent to str(obj) */ + if (start == end) { + if (PyUnicode_CheckExact(obj)) + return _PyUnicodeWriter_WriteStr(writer, obj); + else + return format_obj(obj, writer); + } + + /* parse the format_spec */ + if (!parse_internal_render_format_spec(obj, format_spec, start, end, + &format, 's', '<')) + return -1; + + /* type conversion? */ + switch (format.type) { + case 's': + /* no type conversion needed, already a string. do the formatting */ + return format_string_internal(obj, &format, writer); + default: + /* unknown */ + unknown_presentation_type(format.type, Py_TYPE(obj)->tp_name); + return -1; + } +} + +int +_PyLong_FormatAdvancedWriter(_PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) +{ + PyObject *tmp = NULL; + InternalFormatSpec format; + int result = -1; + + /* check for the special case of zero length format spec, make + it equivalent to str(obj) */ + if (start == end) { + if (PyLong_CheckExact(obj)) + return _PyLong_FormatWriter(writer, obj, 10, 0); + else + return format_obj(obj, writer); + } + + /* parse the format_spec */ + if (!parse_internal_render_format_spec(obj, format_spec, start, end, + &format, 'd', '>')) + goto done; + + /* type conversion? */ + switch (format.type) { + case 'b': + case 'c': + case 'd': + case 'o': + case 'x': + case 'X': + case 'n': + /* no type conversion needed, already an int. do the formatting */ + result = format_long_internal(obj, &format, writer); + break; + + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case '%': + /* convert to float */ + tmp = PyNumber_Float(obj); + if (tmp == NULL) + goto done; + result = format_float_internal(tmp, &format, writer); + break; + + default: + /* unknown */ + unknown_presentation_type(format.type, Py_TYPE(obj)->tp_name); + goto done; + } + +done: + Py_XDECREF(tmp); + return result; +} + +int +_PyFloat_FormatAdvancedWriter(_PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) +{ + InternalFormatSpec format; + + /* check for the special case of zero length format spec, make + it equivalent to str(obj) */ + if (start == end) + return format_obj(obj, writer); + + /* parse the format_spec */ + if (!parse_internal_render_format_spec(obj, format_spec, start, end, + &format, '\0', '>')) + return -1; + + /* type conversion? */ + switch (format.type) { + case '\0': /* No format code: like 'g', but with at least one decimal. */ + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'n': + case '%': + /* no conversion, already a float. do the formatting */ + return format_float_internal(obj, &format, writer); + + default: + /* unknown */ + unknown_presentation_type(format.type, Py_TYPE(obj)->tp_name); + return -1; + } +} + +int +_PyComplex_FormatAdvancedWriter(_PyUnicodeWriter *writer, + PyObject *obj, + PyObject *format_spec, + Py_ssize_t start, Py_ssize_t end) +{ + InternalFormatSpec format; + + /* check for the special case of zero length format spec, make + it equivalent to str(obj) */ + if (start == end) + return format_obj(obj, writer); + + /* parse the format_spec */ + if (!parse_internal_render_format_spec(obj, format_spec, start, end, + &format, '\0', '>')) + return -1; + + /* type conversion? */ + switch (format.type) { + case '\0': /* No format code: like 'g', but with at least one decimal. */ + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'n': + /* no conversion, already a complex. do the formatting */ + return format_complex_internal(obj, &format, writer); + + default: + /* unknown */ + unknown_presentation_type(format.type, Py_TYPE(obj)->tp_name); + return -1; + } +} diff --git a/Objects/unicode_writer.c b/Objects/unicode_writer.c new file mode 100644 index 00000000000000..a753c9b971c702 --- /dev/null +++ b/Objects/unicode_writer.c @@ -0,0 +1,647 @@ +/* + +Unicode implementation based on original code by Fredrik Lundh, +modified by Marc-Andre Lemburg <mal@lemburg.com>. + +Major speed upgrades to the method implementations at the Reykjavik +NeedForSpeed sprint, by Fredrik Lundh and Andrew Dalke. + +Copyright (c) Corporation for National Research Initiatives. + +-------------------------------------------------------------------- +The original string type implementation is: + + Copyright (c) 1999 by Secret Labs AB + Copyright (c) 1999 by Fredrik Lundh + +By obtaining, using, and/or copying this software and/or its +associated documentation, you agree that you have read, understood, +and will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its +associated documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies, and that both that copyright notice and this permission notice +appear in supporting documentation, and that the name of Secret Labs +AB or the author not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR +ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +-------------------------------------------------------------------- + +*/ + +#include "Python.h" +#include "pycore_freelist.h" // _Py_FREELIST_FREE() +#include "pycore_long.h" // _PyLong_FormatWriter() +#include "pycore_unicodeobject.h" // _PyUnicode_Result() + + +#ifdef MS_WINDOWS + /* On Windows, overallocate by 50% is the best factor */ +# define OVERALLOCATE_FACTOR 2 +#else + /* On Linux, overallocate by 25% is the best factor */ +# define OVERALLOCATE_FACTOR 4 +#endif + + +/* Compilation of templated routines */ + +#define STRINGLIB_GET_EMPTY() _PyUnicode_GetEmpty() + +#include "stringlib/ucs1lib.h" +#include "stringlib/find_max_char.h" +#include "stringlib/undef.h" + + +/* Copy an ASCII or latin1 char* string into a Python Unicode string. + + WARNING: The function doesn't copy the terminating null character and + doesn't check the maximum character (may write a latin1 character in an + ASCII string). */ +static void +unicode_write_cstr(PyObject *unicode, Py_ssize_t index, + const char *str, Py_ssize_t len) +{ + int kind = PyUnicode_KIND(unicode); + const void *data = PyUnicode_DATA(unicode); + const char *end = str + len; + + assert(index + len <= PyUnicode_GET_LENGTH(unicode)); + switch (kind) { + case PyUnicode_1BYTE_KIND: { +#ifdef Py_DEBUG + if (PyUnicode_IS_ASCII(unicode)) { + Py_UCS4 maxchar = ucs1lib_find_max_char( + (const Py_UCS1*)str, + (const Py_UCS1*)str + len); + assert(maxchar < 128); + } +#endif + memcpy((char *) data + index, str, len); + break; + } + case PyUnicode_2BYTE_KIND: { + Py_UCS2 *start = (Py_UCS2 *)data + index; + Py_UCS2 *ucs2 = start; + + for (; str < end; ++ucs2, ++str) + *ucs2 = (Py_UCS2)*str; + + assert((ucs2 - start) <= PyUnicode_GET_LENGTH(unicode)); + break; + } + case PyUnicode_4BYTE_KIND: { + Py_UCS4 *start = (Py_UCS4 *)data + index; + Py_UCS4 *ucs4 = start; + + for (; str < end; ++ucs4, ++str) + *ucs4 = (Py_UCS4)*str; + + assert((ucs4 - start) <= PyUnicode_GET_LENGTH(unicode)); + break; + } + default: + Py_UNREACHABLE(); + } +} + + +static inline void +_PyUnicodeWriter_Update(_PyUnicodeWriter *writer) +{ + writer->maxchar = PyUnicode_MAX_CHAR_VALUE(writer->buffer); + writer->data = PyUnicode_DATA(writer->buffer); + + if (!writer->readonly) { + writer->kind = PyUnicode_KIND(writer->buffer); + writer->size = PyUnicode_GET_LENGTH(writer->buffer); + } + else { + /* use a value smaller than PyUnicode_1BYTE_KIND() so + _PyUnicodeWriter_PrepareKind() will copy the buffer. */ + writer->kind = 0; + assert(writer->kind <= PyUnicode_1BYTE_KIND); + + /* Copy-on-write mode: set buffer size to 0 so + * _PyUnicodeWriter_Prepare() will copy (and enlarge) the buffer on + * next write. */ + writer->size = 0; + } +} + + +void +_PyUnicodeWriter_Init(_PyUnicodeWriter *writer) +{ + memset(writer, 0, sizeof(*writer)); + + /* ASCII is the bare minimum */ + writer->min_char = 127; + + /* use a kind value smaller than PyUnicode_1BYTE_KIND so + _PyUnicodeWriter_PrepareKind() will copy the buffer. */ + assert(writer->kind == 0); + assert(writer->kind < PyUnicode_1BYTE_KIND); +} + + +PyUnicodeWriter* +PyUnicodeWriter_Create(Py_ssize_t length) +{ + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length must be positive"); + return NULL; + } + + const size_t size = sizeof(_PyUnicodeWriter); + PyUnicodeWriter *pub_writer; + pub_writer = _Py_FREELIST_POP_MEM(unicode_writers); + if (pub_writer == NULL) { + pub_writer = (PyUnicodeWriter *)PyMem_Malloc(size); + if (pub_writer == NULL) { + return (PyUnicodeWriter *)PyErr_NoMemory(); + } + } + _PyUnicodeWriter *writer = (_PyUnicodeWriter *)pub_writer; + + _PyUnicodeWriter_Init(writer); + if (_PyUnicodeWriter_Prepare(writer, length, 127) < 0) { + PyUnicodeWriter_Discard(pub_writer); + return NULL; + } + writer->overallocate = 1; + + return pub_writer; +} + + +void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) +{ + if (writer == NULL) { + return; + } + _PyUnicodeWriter_Dealloc((_PyUnicodeWriter*)writer); + _Py_FREELIST_FREE(unicode_writers, writer, PyMem_Free); +} + + +// Initialize _PyUnicodeWriter with initial buffer +void +_PyUnicodeWriter_InitWithBuffer(_PyUnicodeWriter *writer, PyObject *buffer) +{ + memset(writer, 0, sizeof(*writer)); + writer->buffer = buffer; + _PyUnicodeWriter_Update(writer); + writer->min_length = writer->size; +} + + +int +_PyUnicodeWriter_PrepareInternal(_PyUnicodeWriter *writer, + Py_ssize_t length, Py_UCS4 maxchar) +{ + Py_ssize_t newlen; + PyObject *newbuffer; + + assert(length >= 0); + assert(maxchar <= _Py_MAX_UNICODE); + + /* ensure that the _PyUnicodeWriter_Prepare macro was used */ + assert((maxchar > writer->maxchar && length >= 0) + || length > 0); + + if (length > PY_SSIZE_T_MAX - writer->pos) { + PyErr_NoMemory(); + return -1; + } + newlen = writer->pos + length; + + maxchar = Py_MAX(maxchar, writer->min_char); + + if (writer->buffer == NULL) { + assert(!writer->readonly); + if (writer->overallocate + && newlen <= (PY_SSIZE_T_MAX - newlen / OVERALLOCATE_FACTOR)) { + /* overallocate to limit the number of realloc() */ + newlen += newlen / OVERALLOCATE_FACTOR; + } + if (newlen < writer->min_length) + newlen = writer->min_length; + + writer->buffer = PyUnicode_New(newlen, maxchar); + if (writer->buffer == NULL) + return -1; + } + else if (newlen > writer->size) { + if (writer->overallocate + && newlen <= (PY_SSIZE_T_MAX - newlen / OVERALLOCATE_FACTOR)) { + /* overallocate to limit the number of realloc() */ + newlen += newlen / OVERALLOCATE_FACTOR; + } + if (newlen < writer->min_length) + newlen = writer->min_length; + + if (maxchar > writer->maxchar || writer->readonly) { + /* resize + widen */ + maxchar = Py_MAX(maxchar, writer->maxchar); + newbuffer = PyUnicode_New(newlen, maxchar); + if (newbuffer == NULL) + return -1; + _PyUnicode_FastCopyCharacters(newbuffer, 0, + writer->buffer, 0, writer->pos); + Py_DECREF(writer->buffer); + writer->readonly = 0; + } + else { + newbuffer = _PyUnicode_ResizeCompact(writer->buffer, newlen); + if (newbuffer == NULL) + return -1; + } + writer->buffer = newbuffer; + } + else if (maxchar > writer->maxchar) { + assert(!writer->readonly); + newbuffer = PyUnicode_New(writer->size, maxchar); + if (newbuffer == NULL) + return -1; + _PyUnicode_FastCopyCharacters(newbuffer, 0, + writer->buffer, 0, writer->pos); + Py_SETREF(writer->buffer, newbuffer); + } + _PyUnicodeWriter_Update(writer); + return 0; + +#undef OVERALLOCATE_FACTOR +} + +int +_PyUnicodeWriter_PrepareKindInternal(_PyUnicodeWriter *writer, + int kind) +{ + Py_UCS4 maxchar; + + /* ensure that the _PyUnicodeWriter_PrepareKind macro was used */ + assert(writer->kind < kind); + + switch (kind) + { + case PyUnicode_1BYTE_KIND: maxchar = 0xff; break; + case PyUnicode_2BYTE_KIND: maxchar = 0xffff; break; + case PyUnicode_4BYTE_KIND: maxchar = _Py_MAX_UNICODE; break; + default: + Py_UNREACHABLE(); + } + + return _PyUnicodeWriter_PrepareInternal(writer, 0, maxchar); +} + + +int +_PyUnicodeWriter_WriteChar(_PyUnicodeWriter *writer, Py_UCS4 ch) +{ + return _PyUnicodeWriter_WriteCharInline(writer, ch); +} + + +int +PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) +{ + if (ch > _Py_MAX_UNICODE) { + PyErr_SetString(PyExc_ValueError, + "character must be in range(0x110000)"); + return -1; + } + + return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter*)writer, ch); +} + + +int +_PyUnicodeWriter_WriteStr(_PyUnicodeWriter *writer, PyObject *str) +{ + assert(PyUnicode_Check(str)); + + Py_UCS4 maxchar; + Py_ssize_t len; + + len = PyUnicode_GET_LENGTH(str); + if (len == 0) + return 0; + maxchar = PyUnicode_MAX_CHAR_VALUE(str); + if (maxchar > writer->maxchar || len > writer->size - writer->pos) { + if (writer->buffer == NULL && !writer->overallocate) { + assert(_PyUnicode_CheckConsistency(str, 1)); + writer->readonly = 1; + writer->buffer = Py_NewRef(str); + _PyUnicodeWriter_Update(writer); + writer->pos += len; + return 0; + } + if (_PyUnicodeWriter_PrepareInternal(writer, len, maxchar) == -1) + return -1; + } + _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, + str, 0, len); + writer->pos += len; + return 0; +} + + +int +PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyTypeObject *type = Py_TYPE(obj); + if (type == &PyUnicode_Type) { + return _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, obj); + } + + if (type == &PyLong_Type) { + return _PyLong_FormatWriter((_PyUnicodeWriter*)writer, obj, 10, 0); + } + + PyObject *str = PyObject_Str(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + + +int +PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) +{ + if (obj == NULL) { + return _PyUnicodeWriter_WriteASCIIString((_PyUnicodeWriter*)writer, "<NULL>", 6); + } + + if (Py_TYPE(obj) == &PyLong_Type) { + return _PyLong_FormatWriter((_PyUnicodeWriter*)writer, obj, 10, 0); + } + + PyObject *repr = PyObject_Repr(obj); + if (repr == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, repr); + Py_DECREF(repr); + return res; +} + + +int +_PyUnicodeWriter_WriteSubstring(_PyUnicodeWriter *writer, PyObject *str, + Py_ssize_t start, Py_ssize_t end) +{ + assert(0 <= start); + assert(end <= PyUnicode_GET_LENGTH(str)); + assert(start <= end); + + if (start == 0 && end == PyUnicode_GET_LENGTH(str)) + return _PyUnicodeWriter_WriteStr(writer, str); + + Py_ssize_t len = end - start; + if (len == 0) { + return 0; + } + + Py_UCS4 maxchar; + if (PyUnicode_MAX_CHAR_VALUE(str) > writer->maxchar) { + maxchar = _PyUnicode_FindMaxChar(str, start, end); + } + else { + maxchar = writer->maxchar; + } + if (_PyUnicodeWriter_Prepare(writer, len, maxchar) < 0) { + return -1; + } + + _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, + str, start, len); + writer->pos += len; + return 0; +} + + +int +PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, + Py_ssize_t start, Py_ssize_t end) +{ + if (!PyUnicode_Check(str)) { + PyErr_Format(PyExc_TypeError, "expect str, not %T", str); + return -1; + } + if (start < 0 || start > end) { + PyErr_Format(PyExc_ValueError, "invalid start argument"); + return -1; + } + if (end > PyUnicode_GET_LENGTH(str)) { + PyErr_Format(PyExc_ValueError, "invalid end argument"); + return -1; + } + + return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter*)writer, str, + start, end); +} + + +int +_PyUnicodeWriter_WriteASCIIString(_PyUnicodeWriter *writer, + const char *ascii, Py_ssize_t len) +{ + if (len == -1) + len = strlen(ascii); + + if (len == 0) { + return 0; + } + + assert(ucs1lib_find_max_char((const Py_UCS1*)ascii, (const Py_UCS1*)ascii + len) < 128); + + if (writer->buffer == NULL && !writer->overallocate) { + PyObject *str; + + str = _PyUnicode_FromASCII(ascii, len); + if (str == NULL) + return -1; + + writer->readonly = 1; + writer->buffer = str; + _PyUnicodeWriter_Update(writer); + writer->pos += len; + return 0; + } + + if (_PyUnicodeWriter_Prepare(writer, len, 127) == -1) + return -1; + + switch (writer->kind) + { + case PyUnicode_1BYTE_KIND: + { + const Py_UCS1 *str = (const Py_UCS1 *)ascii; + Py_UCS1 *data = writer->data; + + memcpy(data + writer->pos, str, len); + break; + } + case PyUnicode_2BYTE_KIND: + { + _PyUnicode_CONVERT_BYTES( + Py_UCS1, Py_UCS2, + ascii, ascii + len, + (Py_UCS2 *)writer->data + writer->pos); + break; + } + case PyUnicode_4BYTE_KIND: + { + _PyUnicode_CONVERT_BYTES( + Py_UCS1, Py_UCS4, + ascii, ascii + len, + (Py_UCS4 *)writer->data + writer->pos); + break; + } + default: + Py_UNREACHABLE(); + } + + writer->pos += len; + return 0; +} + + +int +PyUnicodeWriter_WriteASCII(PyUnicodeWriter *writer, + const char *str, + Py_ssize_t size) +{ + assert(writer != NULL); + _Py_AssertHoldsTstate(); + + _PyUnicodeWriter *priv_writer = (_PyUnicodeWriter*)writer; + return _PyUnicodeWriter_WriteASCIIString(priv_writer, str, size); +} + + +int +PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, + const char *str, + Py_ssize_t size) +{ + if (size < 0) { + size = strlen(str); + } + + _PyUnicodeWriter *_writer = (_PyUnicodeWriter*)writer; + Py_ssize_t old_pos = _writer->pos; + int res = _PyUnicode_DecodeUTF8Writer(_writer, str, size, + _Py_ERROR_STRICT, NULL, NULL); + if (res < 0) { + _writer->pos = old_pos; + } + return res; +} + + +int +PyUnicodeWriter_DecodeUTF8Stateful(PyUnicodeWriter *writer, + const char *string, + Py_ssize_t length, + const char *errors, + Py_ssize_t *consumed) +{ + if (length < 0) { + length = strlen(string); + } + + _PyUnicodeWriter *_writer = (_PyUnicodeWriter*)writer; + Py_ssize_t old_pos = _writer->pos; + int res = _PyUnicode_DecodeUTF8Writer(_writer, string, length, + _Py_ERROR_UNKNOWN, errors, + consumed); + if (res < 0) { + _writer->pos = old_pos; + if (consumed) { + *consumed = 0; + } + } + return res; +} + + +int +_PyUnicodeWriter_WriteLatin1String(_PyUnicodeWriter *writer, + const char *str, Py_ssize_t len) +{ + Py_UCS4 maxchar; + + maxchar = ucs1lib_find_max_char((const Py_UCS1*)str, (const Py_UCS1*)str + len); + if (_PyUnicodeWriter_Prepare(writer, len, maxchar) == -1) + return -1; + unicode_write_cstr(writer->buffer, writer->pos, str, len); + writer->pos += len; + return 0; +} + + +PyObject * +_PyUnicodeWriter_Finish(_PyUnicodeWriter *writer) +{ + PyObject *str; + + if (writer->pos == 0) { + Py_CLEAR(writer->buffer); + return _PyUnicode_GetEmpty(); + } + + str = writer->buffer; + writer->buffer = NULL; + + if (writer->readonly) { + assert(PyUnicode_GET_LENGTH(str) == writer->pos); + return str; + } + + if (PyUnicode_GET_LENGTH(str) != writer->pos) { + PyObject *str2; + str2 = _PyUnicode_ResizeCompact(str, writer->pos); + if (str2 == NULL) { + Py_DECREF(str); + return NULL; + } + str = str2; + } + + assert(_PyUnicode_CheckConsistency(str, 1)); + return _PyUnicode_Result(str); +} + + +PyObject* +PyUnicodeWriter_Finish(PyUnicodeWriter *writer) +{ + PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter*)writer); + assert(((_PyUnicodeWriter*)writer)->buffer == NULL); + _Py_FREELIST_FREE(unicode_writers, writer, PyMem_Free); + return str; +} + + +void +_PyUnicodeWriter_Dealloc(_PyUnicodeWriter *writer) +{ + Py_CLEAR(writer->buffer); +} diff --git a/Objects/unicodectype.c b/Objects/unicodectype.c index 7cd0dca3d13545..fdd380190ac1ec 100644 --- a/Objects/unicodectype.c +++ b/Objects/unicodectype.c @@ -9,6 +9,7 @@ */ #include "Python.h" +#include "pycore_unicodectype.h" // export _PyUnicode_IsXidStart(), _PyUnicode_IsXidContinue() #define ALPHA_MASK 0x01 #define DECIMAL_MASK 0x02 diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 8df7a48284dccd..3c689761de9b19 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -41,12 +41,11 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "Python.h" #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_bytes_methods.h" // _Py_bytes_lower() -#include "pycore_bytesobject.h" // _PyBytes_Repeat() +#include "pycore_bytesobject.h" // _PyBytes_RepeatBuffer() #include "pycore_ceval.h" // _PyEval_GetBuiltin() #include "pycore_codecs.h" // _PyCodec_Lookup() #include "pycore_critical_section.h" // Py_*_CRITICAL_SECTION_SEQUENCE_FAST #include "pycore_format.h" // F_LJUST -#include "pycore_freelist.h" // _Py_FREELIST_FREE(), _Py_FREELIST_POP() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_interp.h" // PyInterpreterState.fs_codec #include "pycore_long.h" // _PyLong_FormatWriter() @@ -56,8 +55,8 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "pycore_pyhash.h" // _Py_HashSecret_t #include "pycore_pylifecycle.h" // _Py_SetFileSystemEncoding() #include "pycore_pystate.h" // _PyInterpreterState_GET() -#include "pycore_tuple.h" // _PyTuple_FromArray() #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI +#include "pycore_unicodectype.h" // _PyUnicode_IsXidStart #include "pycore_unicodeobject.h" // struct _Py_unicode_state #include "pycore_unicodeobject_generated.h" // _PyUnicode_InitStaticStrings() @@ -87,14 +86,12 @@ class Py_UCS4_converter(CConverter): type = 'Py_UCS4' converter = 'convert_uc' - def converter_init(self): - if self.default is not unspecified: - self.c_default = ascii(self.default) - if len(self.c_default) > 4 or self.c_default[0] != "'": - self.c_default = hex(ord(self.default)) + def c_default_init(self): + import libclinic + self.c_default = libclinic.c_unichar_repr(self.default) [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=88f5dd06cd8e7a61]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=22f057b68fd9a65a]*/ /* --- Globals ------------------------------------------------------------ @@ -104,9 +101,8 @@ NOTE: In the interpreter's initialization phase, some globals are currently */ -// Maximum code point of Unicode 6.0: 0x10ffff (1,114,111). -// The value must be the same in fileutils.c. -#define MAX_UNICODE 0x10ffff +#define MAX_UNICODE _Py_MAX_UNICODE +#define ensure_unicode _PyUnicode_EnsureUnicode #ifdef Py_DEBUG # define _PyUnicode_CHECK(op) _PyUnicode_CheckConsistency(op, 0) @@ -114,14 +110,6 @@ NOTE: In the interpreter's initialization phase, some globals are currently # define _PyUnicode_CHECK(op) PyUnicode_Check(op) #endif -#ifdef Py_GIL_DISABLED -# define LOCK_INTERNED(interp) PyMutex_Lock(&_Py_INTERP_CACHED_OBJECT(interp, interned_mutex)) -# define UNLOCK_INTERNED(interp) PyMutex_Unlock(&_Py_INTERP_CACHED_OBJECT(interp, interned_mutex)) -#else -# define LOCK_INTERNED(interp) -# define UNLOCK_INTERNED(interp) -#endif - static inline char* _PyUnicode_UTF8(PyObject *op) { return FT_ATOMIC_LOAD_PTR_ACQUIRE(_PyCompactUnicodeObject_CAST(op)->utf8); @@ -193,45 +181,9 @@ static inline int _PyUnicode_HAS_UTF8_MEMORY(PyObject *op) } -/* Generic helper macro to convert characters of different types. - from_type and to_type have to be valid type names, begin and end - are pointers to the source characters which should be of type - "from_type *". to is a pointer of type "to_type *" and points to the - buffer where the result characters are written to. */ -#define _PyUnicode_CONVERT_BYTES(from_type, to_type, begin, end, to) \ - do { \ - to_type *_to = (to_type *)(to); \ - const from_type *_iter = (const from_type *)(begin);\ - const from_type *_end = (const from_type *)(end);\ - Py_ssize_t n = (_end) - (_iter); \ - const from_type *_unrolled_end = \ - _iter + _Py_SIZE_ROUND_DOWN(n, 4); \ - while (_iter < (_unrolled_end)) { \ - _to[0] = (to_type) _iter[0]; \ - _to[1] = (to_type) _iter[1]; \ - _to[2] = (to_type) _iter[2]; \ - _to[3] = (to_type) _iter[3]; \ - _iter += 4; _to += 4; \ - } \ - while (_iter < (_end)) \ - *_to++ = (to_type) *_iter++; \ - } while (0) - #define LATIN1 _Py_LATIN1_CHR -#ifdef MS_WINDOWS - /* On Windows, overallocate by 50% is the best factor */ -# define OVERALLOCATE_FACTOR 2 -#else - /* On Linux, overallocate by 25% is the best factor */ -# define OVERALLOCATE_FACTOR 4 -#endif - /* Forward declaration */ -static inline int -_PyUnicodeWriter_WriteCharInline(_PyUnicodeWriter *writer, Py_UCS4 ch); -static inline void -_PyUnicodeWriter_InitWithBuffer(_PyUnicodeWriter *writer, PyObject *buffer); static PyObject * unicode_encode_utf8(PyObject *unicode, _Py_error_handler error_handler, const char *errors); @@ -239,11 +191,6 @@ static PyObject * unicode_decode_utf8(const char *s, Py_ssize_t size, _Py_error_handler error_handler, const char *errors, Py_ssize_t *consumed); -static int -unicode_decode_utf8_writer(_PyUnicodeWriter *writer, - const char *s, Py_ssize_t size, - _Py_error_handler error_handler, const char *errors, - Py_ssize_t *consumed); #ifdef Py_DEBUG static inline int unicode_is_finalizing(void); static int unicode_is_singleton(PyObject *unicode); @@ -251,7 +198,8 @@ static int unicode_is_singleton(PyObject *unicode); // Return a reference to the immortal empty string singleton. -static inline PyObject* unicode_get_empty(void) +PyObject* +_PyUnicode_GetEmpty(void) { _Py_DECLARE_STR(empty, ""); return &_Py_STR(empty); @@ -425,42 +373,9 @@ static void clear_global_interned_strings(void) #define _Py_RETURN_UNICODE_EMPTY() \ do { \ - return unicode_get_empty(); \ + return _PyUnicode_GetEmpty();\ } while (0) -static inline void -unicode_fill(int kind, void *data, Py_UCS4 value, - Py_ssize_t start, Py_ssize_t length) -{ - assert(0 <= start); - switch (kind) { - case PyUnicode_1BYTE_KIND: { - assert(value <= 0xff); - Py_UCS1 ch = (unsigned char)value; - Py_UCS1 *to = (Py_UCS1 *)data + start; - memset(to, ch, length); - break; - } - case PyUnicode_2BYTE_KIND: { - assert(value <= 0xffff); - Py_UCS2 ch = (Py_UCS2)value; - Py_UCS2 *to = (Py_UCS2 *)data + start; - const Py_UCS2 *end = to + length; - for (; to < end; ++to) *to = ch; - break; - } - case PyUnicode_4BYTE_KIND: { - assert(value <= MAX_UNICODE); - Py_UCS4 ch = value; - Py_UCS4 * to = (Py_UCS4 *)data + start; - const Py_UCS4 *end = to + length; - for (; to < end; ++to) *to = ch; - break; - } - default: Py_UNREACHABLE(); - } -} - /* Fast detection of the most frequent whitespace characters */ const unsigned char _Py_ascii_whitespace[] = { @@ -495,7 +410,6 @@ const unsigned char _Py_ascii_whitespace[] = { /* forward */ static PyObject* get_latin1_char(unsigned char ch); -static int unicode_modifiable(PyObject *unicode); static PyObject * @@ -631,7 +545,8 @@ unicode_check_encoding_errors(const char *encoding, const char *errors) } /* Disable checks during Python finalization. For example, it allows to - call _PyObject_Dump() during finalization for debugging purpose. */ + * call PyObject_Dump() during finalization for debugging purpose. + */ if (_PyInterpreterState_GetFinalizing(interp) != NULL) { return 0; } @@ -674,6 +589,14 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content) { #define CHECK(expr) \ do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) +#ifdef Py_GIL_DISABLED +# define CHECK_IF_GIL(expr) (void)(expr) +# define CHECK_IF_FT(expr) CHECK(expr) +#else +# define CHECK_IF_GIL(expr) CHECK(expr) +# define CHECK_IF_FT(expr) (void)(expr) +#endif + assert(op != NULL); CHECK(PyUnicode_Check(op)); @@ -754,11 +677,9 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content) /* Check interning state */ #ifdef Py_DEBUG - // Note that we do not check `_Py_IsImmortal(op)`, since stable ABI - // extensions can make immortal strings mortal (but with a high enough - // refcount). - // The other way is extremely unlikely (worth a potential failed assertion - // in a debug build), so we do check `!_Py_IsImmortal(op)`. + // Note that we do not check `_Py_IsImmortal(op)` in the GIL-enabled build + // since stable ABI extensions can make immortal strings mortal (but with a + // high enough refcount). switch (PyUnicode_CHECK_INTERNED(op)) { case SSTATE_NOT_INTERNED: if (ascii->state.statically_allocated) { @@ -768,18 +689,20 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content) // are static but use SSTATE_NOT_INTERNED } else { - CHECK(!_Py_IsImmortal(op)); + CHECK_IF_GIL(!_Py_IsImmortal(op)); } break; case SSTATE_INTERNED_MORTAL: CHECK(!ascii->state.statically_allocated); - CHECK(!_Py_IsImmortal(op)); + CHECK_IF_GIL(!_Py_IsImmortal(op)); break; case SSTATE_INTERNED_IMMORTAL: CHECK(!ascii->state.statically_allocated); + CHECK_IF_FT(_Py_IsImmortal(op)); break; case SSTATE_INTERNED_IMMORTAL_STATIC: CHECK(ascii->state.statically_allocated); + CHECK_IF_FT(_Py_IsImmortal(op)); break; default: Py_UNREACHABLE(); @@ -791,14 +714,14 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content) #undef CHECK } -static PyObject* -unicode_result(PyObject *unicode) +PyObject* +_PyUnicode_Result(PyObject *unicode) { assert(_PyUnicode_CHECK(unicode)); Py_ssize_t length = PyUnicode_GET_LENGTH(unicode); if (length == 0) { - PyObject *empty = unicode_get_empty(); + PyObject *empty = _PyUnicode_GetEmpty(); if (unicode != empty) { Py_DECREF(unicode); } @@ -821,6 +744,7 @@ unicode_result(PyObject *unicode) assert(_PyUnicode_CheckConsistency(unicode, 1)); return unicode; } +#define unicode_result _PyUnicode_Result static PyObject* unicode_result_unchanged(PyObject *unicode) @@ -836,7 +760,7 @@ unicode_result_unchanged(PyObject *unicode) /* Implementation of the "backslashreplace" error handler for 8-bit encodings: ASCII, Latin1, UTF-8, etc. */ static char* -backslashreplace(_PyBytesWriter *writer, char *str, +backslashreplace(PyBytesWriter *writer, char *str, PyObject *unicode, Py_ssize_t collstart, Py_ssize_t collend) { Py_ssize_t size, i; @@ -869,9 +793,10 @@ backslashreplace(_PyBytesWriter *writer, char *str, size += incr; } - str = _PyBytesWriter_Prepare(writer, str, size); - if (str == NULL) + str = PyBytesWriter_GrowAndUpdatePointer(writer, size, str); + if (str == NULL) { return NULL; + } /* generate replacement */ for (i = collstart; i < collend; ++i) { @@ -902,7 +827,7 @@ backslashreplace(_PyBytesWriter *writer, char *str, /* Implementation of the "xmlcharrefreplace" error handler for 8-bit encodings: ASCII, Latin1, UTF-8, etc. */ static char* -xmlcharrefreplace(_PyBytesWriter *writer, char *str, +xmlcharrefreplace(PyBytesWriter *writer, char *str, PyObject *unicode, Py_ssize_t collstart, Py_ssize_t collend) { Py_ssize_t size, i; @@ -943,9 +868,10 @@ xmlcharrefreplace(_PyBytesWriter *writer, char *str, size += incr; } - str = _PyBytesWriter_Prepare(writer, str, size); - if (str == NULL) + str = PyBytesWriter_GrowAndUpdatePointer(writer, size, str); + if (str == NULL) { return NULL; + } /* generate replacement */ for (i = collstart; i < collend; ++i) { @@ -1024,21 +950,9 @@ make_bloom_mask(int kind, const void* ptr, Py_ssize_t len) #undef BLOOM_UPDATE } -static int -ensure_unicode(PyObject *obj) -{ - if (!PyUnicode_Check(obj)) { - PyErr_Format(PyExc_TypeError, - "must be str, not %.100s", - Py_TYPE(obj)->tp_name); - return -1; - } - return 0; -} - /* Compilation of templated routines */ -#define STRINGLIB_GET_EMPTY() unicode_get_empty() +#define STRINGLIB_GET_EMPTY() _PyUnicode_GetEmpty() #include "stringlib/asciilib.h" #include "stringlib/fastsearch.h" @@ -1150,8 +1064,8 @@ resize_copy(PyObject *unicode, Py_ssize_t length) return copy; } -static PyObject* -resize_compact(PyObject *unicode, Py_ssize_t length) +PyObject* +_PyUnicode_ResizeCompact(PyObject *unicode, Py_ssize_t length) { Py_ssize_t char_size; Py_ssize_t struct_size; @@ -1161,7 +1075,7 @@ resize_compact(PyObject *unicode, Py_ssize_t length) Py_ssize_t old_length = _PyUnicode_LENGTH(unicode); #endif - if (!unicode_modifiable(unicode)) { + if (!_PyUnicode_IsModifiable(unicode)) { PyObject *copy = resize_copy(unicode, length); if (copy == NULL) { return NULL; @@ -1359,7 +1273,7 @@ PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar) { /* Optimization for empty strings */ if (size == 0) { - return unicode_get_empty(); + return _PyUnicode_GetEmpty(); } PyObject *obj; @@ -1453,7 +1367,7 @@ PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar) static int unicode_check_modifiable(PyObject *unicode) { - if (!unicode_modifiable(unicode)) { + if (!_PyUnicode_IsModifiable(unicode)) { PyErr_SetString(PyExc_SystemError, "Cannot modify a string currently used"); return -1; @@ -1815,8 +1729,8 @@ unicode_is_singleton(PyObject *unicode) } #endif -static int -unicode_modifiable(PyObject *unicode) +int +_PyUnicode_IsModifiable(PyObject *unicode) { assert(_PyUnicode_CHECK(unicode)); if (!_PyObject_IsUniquelyReferenced(unicode)) @@ -1852,12 +1766,12 @@ unicode_resize(PyObject **p_unicode, Py_ssize_t length) return 0; if (length == 0) { - PyObject *empty = unicode_get_empty(); + PyObject *empty = _PyUnicode_GetEmpty(); Py_SETREF(*p_unicode, empty); return 0; } - if (!unicode_modifiable(unicode)) { + if (!_PyUnicode_IsModifiable(unicode)) { PyObject *copy = resize_copy(unicode, length); if (copy == NULL) return -1; @@ -1866,7 +1780,7 @@ unicode_resize(PyObject **p_unicode, Py_ssize_t length) } if (PyUnicode_IS_COMPACT(unicode)) { - PyObject *new_unicode = resize_compact(unicode, length); + PyObject *new_unicode = _PyUnicode_ResizeCompact(unicode, length); if (new_unicode == NULL) return -1; *p_unicode = new_unicode; @@ -1892,58 +1806,6 @@ PyUnicode_Resize(PyObject **p_unicode, Py_ssize_t length) return unicode_resize(p_unicode, length); } -/* Copy an ASCII or latin1 char* string into a Python Unicode string. - - WARNING: The function doesn't copy the terminating null character and - doesn't check the maximum character (may write a latin1 character in an - ASCII string). */ -static void -unicode_write_cstr(PyObject *unicode, Py_ssize_t index, - const char *str, Py_ssize_t len) -{ - int kind = PyUnicode_KIND(unicode); - const void *data = PyUnicode_DATA(unicode); - const char *end = str + len; - - assert(index + len <= PyUnicode_GET_LENGTH(unicode)); - switch (kind) { - case PyUnicode_1BYTE_KIND: { -#ifdef Py_DEBUG - if (PyUnicode_IS_ASCII(unicode)) { - Py_UCS4 maxchar = ucs1lib_find_max_char( - (const Py_UCS1*)str, - (const Py_UCS1*)str + len); - assert(maxchar < 128); - } -#endif - memcpy((char *) data + index, str, len); - break; - } - case PyUnicode_2BYTE_KIND: { - Py_UCS2 *start = (Py_UCS2 *)data + index; - Py_UCS2 *ucs2 = start; - - for (; str < end; ++ucs2, ++str) - *ucs2 = (Py_UCS2)*str; - - assert((ucs2 - start) <= PyUnicode_GET_LENGTH(unicode)); - break; - } - case PyUnicode_4BYTE_KIND: { - Py_UCS4 *start = (Py_UCS4 *)data + index; - Py_UCS4 *ucs4 = start; - - for (; str < end; ++ucs4, ++str) - *ucs4 = (Py_UCS4)*str; - - assert((ucs4 - start) <= PyUnicode_GET_LENGTH(unicode)); - break; - } - default: - Py_UNREACHABLE(); - } -} - static PyObject* get_latin1_char(Py_UCS1 ch) { @@ -2158,7 +2020,7 @@ PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size) "NULL string with positive size with NULL passed to PyUnicode_FromStringAndSize"); return NULL; } - return unicode_get_empty(); + return _PyUnicode_GetEmpty(); } PyObject * @@ -2370,7 +2232,7 @@ _PyUnicode_FromUCS4(const Py_UCS4 *u, Py_ssize_t size) int PyUnicodeWriter_WriteUCS4(PyUnicodeWriter *pub_writer, - Py_UCS4 *str, + const Py_UCS4 *str, Py_ssize_t size) { _PyUnicodeWriter *writer = (_PyUnicodeWriter*)pub_writer; @@ -2725,8 +2587,8 @@ unicode_fromformat_write_utf8(_PyUnicodeWriter *writer, const char *str, } if (width < 0) { - return unicode_decode_utf8_writer(writer, str, length, - _Py_ERROR_REPLACE, "replace", pconsumed); + return _PyUnicode_DecodeUTF8Writer(writer, str, length, + _Py_ERROR_REPLACE, "replace", pconsumed); } PyObject *unicode = PyUnicode_DecodeUTF8Stateful(str, length, @@ -3271,14 +3133,22 @@ PyUnicode_FromFormat(const char *format, ...) int PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) +{ + va_list vargs; + va_start(vargs, format); + int res = _PyUnicodeWriter_FormatV(writer, format, vargs); + va_end(vargs); + return res; +} + +int +_PyUnicodeWriter_FormatV(PyUnicodeWriter *writer, const char *format, + va_list vargs) { _PyUnicodeWriter *_writer = (_PyUnicodeWriter*)writer; Py_ssize_t old_pos = _writer->pos; - va_list vargs; - va_start(vargs, format); int res = unicode_from_format(_writer, format, vargs); - va_end(vargs); if (res < 0) { _writer->pos = old_pos; @@ -3586,13 +3456,14 @@ PyUnicode_FromEncodedObject(PyObject *obj, return v; } -/* Normalize an encoding name: similar to encodings.normalize_encoding(), but - also convert to lowercase. Return 1 on success, or 0 on error (encoding is - longer than lower_len-1). */ +/* Normalize an encoding name like encodings.normalize_encoding() + but allow to convert to lowercase if *to_lower* is true. + Return 1 on success, or 0 on error (encoding is longer than lower_len-1). */ int _Py_normalize_encoding(const char *encoding, char *lower, - size_t lower_len) + size_t lower_len, + int to_lower) { const char *e; char *l; @@ -3623,7 +3494,7 @@ _Py_normalize_encoding(const char *encoding, if (l == l_end) { return 0; } - *l++ = Py_TOLOWER(c); + *l++ = to_lower ? Py_TOLOWER(c) : c; } else { punct = 1; @@ -3658,7 +3529,7 @@ PyUnicode_Decode(const char *s, } /* Shortcuts for common default encodings */ - if (_Py_normalize_encoding(encoding, buflower, sizeof(buflower))) { + if (_Py_normalize_encoding(encoding, buflower, sizeof(buflower), 1)) { char *lower = buflower; /* Fast paths */ @@ -3915,7 +3786,7 @@ PyUnicode_AsEncodedString(PyObject *unicode, } /* Shortcuts for common default encodings */ - if (_Py_normalize_encoding(encoding, buflower, sizeof(buflower))) { + if (_Py_normalize_encoding(encoding, buflower, sizeof(buflower), 1)) { char *lower = buflower; /* Fast paths */ @@ -4668,15 +4539,12 @@ char utf7_category[128] = { /* ENCODE_DIRECT: this character should be encoded as itself. The * answer depends on whether we are encoding set O as itself, and also - * on whether we are encoding whitespace as itself. RFC2152 makes it + * on whether we are encoding whitespace as itself. RFC 2152 makes it * clear that the answers to these questions vary between * applications, so this code needs to be flexible. */ -#define ENCODE_DIRECT(c, directO, directWS) \ - ((c) < 128 && (c) > 0 && \ - ((utf7_category[(c)] == 0) || \ - (directWS && (utf7_category[(c)] == 2)) || \ - (directO && (utf7_category[(c)] == 1)))) +#define ENCODE_DIRECT(c) \ + ((c) < 128 && (c) > 0 && ((utf7_category[(c)] != 3))) PyObject * PyUnicode_DecodeUTF7(const char *s, @@ -4893,41 +4761,33 @@ PyUnicode_DecodeUTF7Stateful(const char *s, PyObject * _PyUnicode_EncodeUTF7(PyObject *str, - int base64SetO, - int base64WhiteSpace, const char *errors) { - int kind; - const void *data; - Py_ssize_t len; - PyObject *v; - int inShift = 0; - Py_ssize_t i; - unsigned int base64bits = 0; - unsigned long base64buffer = 0; - char * out; - const char * start; - - kind = PyUnicode_KIND(str); - data = PyUnicode_DATA(str); - len = PyUnicode_GET_LENGTH(str); - - if (len == 0) - return PyBytes_FromStringAndSize(NULL, 0); + Py_ssize_t len = PyUnicode_GET_LENGTH(str); + if (len == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } + int kind = PyUnicode_KIND(str); + const void *data = PyUnicode_DATA(str); /* It might be possible to tighten this worst case */ - if (len > PY_SSIZE_T_MAX / 8) + if (len > PY_SSIZE_T_MAX / 8) { return PyErr_NoMemory(); - v = PyBytes_FromStringAndSize(NULL, len * 8); - if (v == NULL) + } + PyBytesWriter *writer = PyBytesWriter_Create(len * 8); + if (writer == NULL) { return NULL; + } - start = out = PyBytes_AS_STRING(v); - for (i = 0; i < len; ++i) { + int inShift = 0; + unsigned int base64bits = 0; + unsigned long base64buffer = 0; + char *out = PyBytesWriter_GetData(writer); + for (Py_ssize_t i = 0; i < len; ++i) { Py_UCS4 ch = PyUnicode_READ(kind, data, i); if (inShift) { - if (ENCODE_DIRECT(ch, !base64SetO, !base64WhiteSpace)) { + if (ENCODE_DIRECT(ch)) { /* shifting out */ if (base64bits) { /* output remaining bits */ *out++ = TO_BASE64(base64buffer << (6-base64bits)); @@ -4951,7 +4811,7 @@ _PyUnicode_EncodeUTF7(PyObject *str, *out++ = '+'; *out++ = '-'; } - else if (ENCODE_DIRECT(ch, !base64SetO, !base64WhiteSpace)) { + else if (ENCODE_DIRECT(ch)) { *out++ = (char) ch; } else { @@ -4986,9 +4846,7 @@ _PyUnicode_EncodeUTF7(PyObject *str, *out++= TO_BASE64(base64buffer << (6-base64bits) ); if (inShift) *out++ = '-'; - if (_PyBytes_Resize(&v, out - start) < 0) - return NULL; - return v; + return PyBytesWriter_FinishWithPointer(writer, out); } #undef IS_BASE64 @@ -5368,7 +5226,7 @@ unicode_decode_utf8_impl(_PyUnicodeWriter *writer, } if (_PyUnicodeWriter_Prepare(writer, end - s, 127) < 0) { - return -1; + goto onError; } } } @@ -5468,7 +5326,6 @@ unicode_decode_utf8(const char *s, Py_ssize_t size, if (maxchr <= 255) { memcpy(PyUnicode_1BYTE_DATA(u), s, pos); s += pos; - size -= pos; writer.pos = pos; } @@ -5483,11 +5340,11 @@ unicode_decode_utf8(const char *s, Py_ssize_t size, // Used by PyUnicodeWriter_WriteUTF8() implementation -static int -unicode_decode_utf8_writer(_PyUnicodeWriter *writer, - const char *s, Py_ssize_t size, - _Py_error_handler error_handler, const char *errors, - Py_ssize_t *consumed) +int +_PyUnicode_DecodeUTF8Writer(_PyUnicodeWriter *writer, + const char *s, Py_ssize_t size, + _Py_error_handler error_handler, const char *errors, + Py_ssize_t *consumed) { if (size == 0) { if (consumed) { @@ -5516,7 +5373,6 @@ unicode_decode_utf8_writer(_PyUnicodeWriter *writer, return 0; } s += decoded; - size -= decoded; } return unicode_decode_utf8_impl(writer, starts, s, end, @@ -5732,15 +5588,14 @@ _Py_EncodeUTF8Ex(const wchar_t *text, char **str, size_t *error_pos, Py_ssize_t ch_pos = i; Py_UCS4 ch = text[i]; i++; -#if Py_UNICODE_SIZE == 2 - if (Py_UNICODE_IS_HIGH_SURROGATE(ch) + if (sizeof(wchar_t) == 2 + && Py_UNICODE_IS_HIGH_SURROGATE(ch) && i < len && Py_UNICODE_IS_LOW_SURROGATE(text[i])) { ch = Py_UNICODE_JOIN_SURROGATES(ch, text[i]); i++; } -#endif if (ch < 0x80) { /* Encode ASCII */ @@ -5836,7 +5691,7 @@ unicode_encode_utf8(PyObject *unicode, _Py_error_handler error_handler, const void *data = PyUnicode_DATA(unicode); Py_ssize_t size = PyUnicode_GET_LENGTH(unicode); - _PyBytesWriter writer; + PyBytesWriter *writer; char *end; switch (kind) { @@ -5845,21 +5700,24 @@ unicode_encode_utf8(PyObject *unicode, _Py_error_handler error_handler, case PyUnicode_1BYTE_KIND: /* the string cannot be ASCII, or PyUnicode_UTF8() would be set */ assert(!PyUnicode_IS_ASCII(unicode)); - end = ucs1lib_utf8_encoder(&writer, unicode, data, size, error_handler, errors); + writer = ucs1lib_utf8_encoder(unicode, data, size, + error_handler, errors, &end); break; case PyUnicode_2BYTE_KIND: - end = ucs2lib_utf8_encoder(&writer, unicode, data, size, error_handler, errors); + writer = ucs2lib_utf8_encoder(unicode, data, size, + error_handler, errors, &end); break; case PyUnicode_4BYTE_KIND: - end = ucs4lib_utf8_encoder(&writer, unicode, data, size, error_handler, errors); + writer = ucs4lib_utf8_encoder(unicode, data, size, + error_handler, errors, &end); break; } - if (end == NULL) { - _PyBytesWriter_Dealloc(&writer); + if (writer == NULL) { + PyBytesWriter_Discard(writer); return NULL; } - return _PyBytesWriter_Finish(&writer, end); + return PyBytesWriter_FinishWithPointer(writer, end); } static int @@ -5873,37 +5731,35 @@ unicode_fill_utf8(PyObject *unicode) const void *data = PyUnicode_DATA(unicode); Py_ssize_t size = PyUnicode_GET_LENGTH(unicode); - _PyBytesWriter writer; + PyBytesWriter *writer; char *end; switch (kind) { default: Py_UNREACHABLE(); case PyUnicode_1BYTE_KIND: - end = ucs1lib_utf8_encoder(&writer, unicode, data, size, - _Py_ERROR_STRICT, NULL); + writer = ucs1lib_utf8_encoder(unicode, data, size, + _Py_ERROR_STRICT, NULL, &end); break; case PyUnicode_2BYTE_KIND: - end = ucs2lib_utf8_encoder(&writer, unicode, data, size, - _Py_ERROR_STRICT, NULL); + writer = ucs2lib_utf8_encoder(unicode, data, size, + _Py_ERROR_STRICT, NULL, &end); break; case PyUnicode_4BYTE_KIND: - end = ucs4lib_utf8_encoder(&writer, unicode, data, size, - _Py_ERROR_STRICT, NULL); + writer = ucs4lib_utf8_encoder(unicode, data, size, + _Py_ERROR_STRICT, NULL, &end); break; } - if (end == NULL) { - _PyBytesWriter_Dealloc(&writer); + if (writer == NULL) { return -1; } - const char *start = writer.use_small_buffer ? writer.small_buffer : - PyBytes_AS_STRING(writer.buffer); + const char *start = PyBytesWriter_GetData(writer); Py_ssize_t len = end - start; char *cache = PyMem_Malloc(len + 1); if (cache == NULL) { - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter_Discard(writer); PyErr_NoMemory(); return -1; } @@ -5911,7 +5767,7 @@ unicode_fill_utf8(PyObject *unicode) cache[len] = '\0'; PyUnicode_SET_UTF8_LENGTH(unicode, len); PyUnicode_SET_UTF8(unicode, cache); - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter_Discard(writer); return 0; } @@ -6089,45 +5945,61 @@ _PyUnicode_EncodeUTF32(PyObject *str, const char *errors, int byteorder) { - int kind; - const void *data; - Py_ssize_t len; - PyObject *v; - uint32_t *out; + if (!PyUnicode_Check(str)) { + PyErr_BadArgument(); + return NULL; + } + int kind = PyUnicode_KIND(str); + const void *data = PyUnicode_DATA(str); + Py_ssize_t len = PyUnicode_GET_LENGTH(str); + + if (len > PY_SSIZE_T_MAX / 4 - (byteorder == 0)) + return PyErr_NoMemory(); + Py_ssize_t nsize = len + (byteorder == 0); + #if PY_LITTLE_ENDIAN int native_ordering = byteorder <= 0; #else int native_ordering = byteorder >= 0; #endif - const char *encoding; - Py_ssize_t nsize, pos; - PyObject *errorHandler = NULL; - PyObject *exc = NULL; - PyObject *rep = NULL; - if (!PyUnicode_Check(str)) { - PyErr_BadArgument(); - return NULL; + if (kind == PyUnicode_1BYTE_KIND) { + // gh-139156: Don't use PyBytesWriter API here since it has an overhead + // on short strings + PyObject *v = PyBytes_FromStringAndSize(NULL, nsize * 4); + if (v == NULL) { + return NULL; + } + + /* output buffer is 4-bytes aligned */ + assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(v), 4)); + uint32_t *out = (uint32_t *)PyBytes_AS_STRING(v); + if (byteorder == 0) { + *out++ = 0xFEFF; + } + if (len > 0) { + ucs1lib_utf32_encode((const Py_UCS1 *)data, len, + &out, native_ordering); + } + return v; } - kind = PyUnicode_KIND(str); - data = PyUnicode_DATA(str); - len = PyUnicode_GET_LENGTH(str); - if (len > PY_SSIZE_T_MAX / 4 - (byteorder == 0)) - return PyErr_NoMemory(); - nsize = len + (byteorder == 0); - v = PyBytes_FromStringAndSize(NULL, nsize * 4); - if (v == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(nsize * 4); + if (writer == NULL) { return NULL; + } /* output buffer is 4-bytes aligned */ - assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(v), 4)); - out = (uint32_t *)PyBytes_AS_STRING(v); - if (byteorder == 0) + assert(_Py_IS_ALIGNED(PyBytesWriter_GetData(writer), 4)); + uint32_t *out = (uint32_t *)PyBytesWriter_GetData(writer); + if (byteorder == 0) { *out++ = 0xFEFF; - if (len == 0) - goto done; + } + if (len == 0) { + return PyBytesWriter_Finish(writer); + } + const char *encoding; if (byteorder == -1) encoding = "utf-32-le"; else if (byteorder == 1) @@ -6135,15 +6007,11 @@ _PyUnicode_EncodeUTF32(PyObject *str, else encoding = "utf-32"; - if (kind == PyUnicode_1BYTE_KIND) { - ucs1lib_utf32_encode((const Py_UCS1 *)data, len, &out, native_ordering); - goto done; - } - - pos = 0; - while (pos < len) { - Py_ssize_t newpos, repsize, moreunits; + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + PyObject *rep = NULL; + for (Py_ssize_t pos = 0; pos < len; ) { if (kind == PyUnicode_2BYTE_KIND) { pos += ucs2lib_utf32_encode((const Py_UCS2 *)data + pos, len - pos, &out, native_ordering); @@ -6156,6 +6024,7 @@ _PyUnicode_EncodeUTF32(PyObject *str, if (pos == len) break; + Py_ssize_t newpos; rep = unicode_encode_call_errorhandler( errors, &errorHandler, encoding, "surrogates not allowed", @@ -6163,6 +6032,7 @@ _PyUnicode_EncodeUTF32(PyObject *str, if (!rep) goto error; + Py_ssize_t repsize, moreunits; if (PyBytes_Check(rep)) { repsize = PyBytes_GET_SIZE(rep); if (repsize & 3) { @@ -6188,21 +6058,18 @@ _PyUnicode_EncodeUTF32(PyObject *str, /* four bytes are reserved for each surrogate */ if (moreunits > 0) { - Py_ssize_t outpos = out - (uint32_t*) PyBytes_AS_STRING(v); - if (moreunits >= (PY_SSIZE_T_MAX - PyBytes_GET_SIZE(v)) / 4) { - /* integer overflow */ - PyErr_NoMemory(); + out = PyBytesWriter_GrowAndUpdatePointer(writer, 4 * moreunits, out); + if (out == NULL) { goto error; } - if (_PyBytes_Resize(&v, PyBytes_GET_SIZE(v) + 4 * moreunits) < 0) - goto error; - out = (uint32_t*) PyBytes_AS_STRING(v) + outpos; } if (PyBytes_Check(rep)) { memcpy(out, PyBytes_AS_STRING(rep), repsize); out += repsize / 4; - } else /* rep is unicode */ { + } + else { + /* rep is unicode */ assert(PyUnicode_KIND(rep) == PyUnicode_1BYTE_KIND); ucs1lib_utf32_encode(PyUnicode_1BYTE_DATA(rep), repsize, &out, native_ordering); @@ -6211,21 +6078,19 @@ _PyUnicode_EncodeUTF32(PyObject *str, Py_CLEAR(rep); } + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + /* Cut back to size actually needed. This is necessary for, for example, encoding of a string containing isolated surrogates and the 'ignore' handler is used. */ - nsize = (unsigned char*) out - (unsigned char*) PyBytes_AS_STRING(v); - if (nsize != PyBytes_GET_SIZE(v)) - _PyBytes_Resize(&v, nsize); - Py_XDECREF(errorHandler); - Py_XDECREF(exc); - done: - return v; + return PyBytesWriter_FinishWithPointer(writer, out); + error: Py_XDECREF(rep); Py_XDECREF(errorHandler); Py_XDECREF(exc); - Py_XDECREF(v); + PyBytesWriter_Discard(writer); return NULL; } @@ -6406,32 +6271,15 @@ _PyUnicode_EncodeUTF16(PyObject *str, const char *errors, int byteorder) { - int kind; - const void *data; - Py_ssize_t len; - PyObject *v; - unsigned short *out; - Py_ssize_t pairs; -#if PY_BIG_ENDIAN - int native_ordering = byteorder >= 0; -#else - int native_ordering = byteorder <= 0; -#endif - const char *encoding; - Py_ssize_t nsize, pos; - PyObject *errorHandler = NULL; - PyObject *exc = NULL; - PyObject *rep = NULL; - if (!PyUnicode_Check(str)) { PyErr_BadArgument(); return NULL; } - kind = PyUnicode_KIND(str); - data = PyUnicode_DATA(str); - len = PyUnicode_GET_LENGTH(str); + int kind = PyUnicode_KIND(str); + const void *data = PyUnicode_DATA(str); + Py_ssize_t len = PyUnicode_GET_LENGTH(str); - pairs = 0; + Py_ssize_t pairs = 0; if (kind == PyUnicode_4BYTE_KIND) { const Py_UCS4 *in = (const Py_UCS4 *)data; const Py_UCS4 *end = in + len; @@ -6444,27 +6292,50 @@ _PyUnicode_EncodeUTF16(PyObject *str, if (len > PY_SSIZE_T_MAX / 2 - pairs - (byteorder == 0)) { return PyErr_NoMemory(); } - nsize = len + pairs + (byteorder == 0); - v = PyBytes_FromStringAndSize(NULL, nsize * 2); - if (v == NULL) { + Py_ssize_t nsize = len + pairs + (byteorder == 0); + +#if PY_BIG_ENDIAN + int native_ordering = byteorder >= 0; +#else + int native_ordering = byteorder <= 0; +#endif + + if (kind == PyUnicode_1BYTE_KIND) { + // gh-139156: Don't use PyBytesWriter API here since it has an overhead + // on short strings + PyObject *v = PyBytes_FromStringAndSize(NULL, nsize * 2); + if (v == NULL) { + return NULL; + } + + /* output buffer is 2-bytes aligned */ + assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(v), 2)); + unsigned short *out = (unsigned short *)PyBytes_AS_STRING(v); + if (byteorder == 0) { + *out++ = 0xFEFF; + } + if (len > 0) { + ucs1lib_utf16_encode((const Py_UCS1 *)data, len, &out, native_ordering); + } + return v; + } + + PyBytesWriter *writer = PyBytesWriter_Create(nsize * 2); + if (writer == NULL) { return NULL; } /* output buffer is 2-bytes aligned */ - assert(_Py_IS_ALIGNED(PyBytes_AS_STRING(v), 2)); - out = (unsigned short *)PyBytes_AS_STRING(v); + assert(_Py_IS_ALIGNED(PyBytesWriter_GetData(writer), 2)); + unsigned short *out = PyBytesWriter_GetData(writer); if (byteorder == 0) { *out++ = 0xFEFF; } if (len == 0) { - goto done; - } - - if (kind == PyUnicode_1BYTE_KIND) { - ucs1lib_utf16_encode((const Py_UCS1 *)data, len, &out, native_ordering); - goto done; + return PyBytesWriter_Finish(writer); } + const char *encoding; if (byteorder < 0) { encoding = "utf-16-le"; } @@ -6475,10 +6346,11 @@ _PyUnicode_EncodeUTF16(PyObject *str, encoding = "utf-16"; } - pos = 0; - while (pos < len) { - Py_ssize_t newpos, repsize, moreunits; + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + PyObject *rep = NULL; + for (Py_ssize_t pos = 0; pos < len; ) { if (kind == PyUnicode_2BYTE_KIND) { pos += ucs2lib_utf16_encode((const Py_UCS2 *)data + pos, len - pos, &out, native_ordering); @@ -6491,6 +6363,7 @@ _PyUnicode_EncodeUTF16(PyObject *str, if (pos == len) break; + Py_ssize_t newpos; rep = unicode_encode_call_errorhandler( errors, &errorHandler, encoding, "surrogates not allowed", @@ -6498,6 +6371,7 @@ _PyUnicode_EncodeUTF16(PyObject *str, if (!rep) goto error; + Py_ssize_t repsize, moreunits; if (PyBytes_Check(rep)) { repsize = PyBytes_GET_SIZE(rep); if (repsize & 1) { @@ -6523,21 +6397,17 @@ _PyUnicode_EncodeUTF16(PyObject *str, /* two bytes are reserved for each surrogate */ if (moreunits > 0) { - Py_ssize_t outpos = out - (unsigned short*) PyBytes_AS_STRING(v); - if (moreunits >= (PY_SSIZE_T_MAX - PyBytes_GET_SIZE(v)) / 2) { - /* integer overflow */ - PyErr_NoMemory(); + out = PyBytesWriter_GrowAndUpdatePointer(writer, 2 * moreunits, out); + if (out == NULL) { goto error; } - if (_PyBytes_Resize(&v, PyBytes_GET_SIZE(v) + 2 * moreunits) < 0) - goto error; - out = (unsigned short*) PyBytes_AS_STRING(v) + outpos; } if (PyBytes_Check(rep)) { memcpy(out, PyBytes_AS_STRING(rep), repsize); out += repsize / 2; - } else /* rep is unicode */ { + } else { + /* rep is unicode */ assert(PyUnicode_KIND(rep) == PyUnicode_1BYTE_KIND); ucs1lib_utf16_encode(PyUnicode_1BYTE_DATA(rep), repsize, &out, native_ordering); @@ -6546,23 +6416,20 @@ _PyUnicode_EncodeUTF16(PyObject *str, Py_CLEAR(rep); } + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + /* Cut back to size actually needed. This is necessary for, for example, encoding of a string containing isolated surrogates and the 'ignore' handler is used. */ - nsize = (unsigned char*) out - (unsigned char*) PyBytes_AS_STRING(v); - if (nsize != PyBytes_GET_SIZE(v)) - _PyBytes_Resize(&v, nsize); - Py_XDECREF(errorHandler); - Py_XDECREF(exc); - done: - return v; + return PyBytesWriter_FinishWithPointer(writer, out); + error: Py_XDECREF(rep); Py_XDECREF(errorHandler); Py_XDECREF(exc); - Py_XDECREF(v); + PyBytesWriter_Discard(writer); return NULL; -#undef STORECHAR } PyObject * @@ -6892,46 +6759,36 @@ PyUnicode_DecodeUnicodeEscape(const char *s, PyObject * PyUnicode_AsUnicodeEscapeString(PyObject *unicode) { - Py_ssize_t i, len; - PyObject *repr; - char *p; - int kind; - const void *data; - Py_ssize_t expandsize; - - /* Initial allocation is based on the longest-possible character - escape. - - For UCS1 strings it's '\xxx', 4 bytes per source character. - For UCS2 strings it's '\uxxxx', 6 bytes per source character. - For UCS4 strings it's '\U00xxxxxx', 10 bytes per source character. - */ - if (!PyUnicode_Check(unicode)) { PyErr_BadArgument(); return NULL; } - len = PyUnicode_GET_LENGTH(unicode); + Py_ssize_t len = PyUnicode_GET_LENGTH(unicode); if (len == 0) { - return PyBytes_FromStringAndSize(NULL, 0); + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); } + int kind = PyUnicode_KIND(unicode); + const void *data = PyUnicode_DATA(unicode); - kind = PyUnicode_KIND(unicode); - data = PyUnicode_DATA(unicode); - /* 4 byte characters can take up 10 bytes, 2 byte characters can take up 6 - bytes, and 1 byte characters 4. */ - expandsize = kind * 2 + 2; + /* Initial allocation is based on the longest-possible character + * escape. + * + * For UCS1 strings it's '\xxx', 4 bytes per source character. + * For UCS2 strings it's '\uxxxx', 6 bytes per source character. + * For UCS4 strings it's '\U00xxxxxx', 10 bytes per source character. */ + Py_ssize_t expandsize = kind * 2 + 2; if (len > PY_SSIZE_T_MAX / expandsize) { return PyErr_NoMemory(); } - repr = PyBytes_FromStringAndSize(NULL, expandsize * len); - if (repr == NULL) { + + PyBytesWriter *writer = PyBytesWriter_Create(expandsize * len); + if (writer == NULL) { return NULL; } + char *p = PyBytesWriter_GetData(writer); - p = PyBytes_AS_STRING(repr); - for (i = 0; i < len; i++) { + for (Py_ssize_t i = 0; i < len; i++) { Py_UCS4 ch = PyUnicode_READ(kind, data, i); /* U+0000-U+00ff range */ @@ -6997,11 +6854,7 @@ PyUnicode_AsUnicodeEscapeString(PyObject *unicode) } } - assert(p - PyBytes_AS_STRING(repr) > 0); - if (_PyBytes_Resize(&repr, p - PyBytes_AS_STRING(repr)) < 0) { - return NULL; - } - return repr; + return PyBytesWriter_FinishWithPointer(writer, p); } /* --- Raw Unicode Escape Codec ------------------------------------------- */ @@ -7154,41 +7007,34 @@ PyUnicode_DecodeRawUnicodeEscape(const char *s, PyObject * PyUnicode_AsRawUnicodeEscapeString(PyObject *unicode) { - PyObject *repr; - char *p; - Py_ssize_t expandsize, pos; - int kind; - const void *data; - Py_ssize_t len; - if (!PyUnicode_Check(unicode)) { PyErr_BadArgument(); return NULL; } - kind = PyUnicode_KIND(unicode); - data = PyUnicode_DATA(unicode); - len = PyUnicode_GET_LENGTH(unicode); + int kind = PyUnicode_KIND(unicode); + const void *data = PyUnicode_DATA(unicode); + Py_ssize_t len = PyUnicode_GET_LENGTH(unicode); + if (len == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } if (kind == PyUnicode_1BYTE_KIND) { return PyBytes_FromStringAndSize(data, len); } /* 4 byte characters can take up 10 bytes, 2 byte characters can take up 6 bytes, and 1 byte characters 4. */ - expandsize = kind * 2 + 2; - + Py_ssize_t expandsize = kind * 2 + 2; if (len > PY_SSIZE_T_MAX / expandsize) { return PyErr_NoMemory(); } - repr = PyBytes_FromStringAndSize(NULL, expandsize * len); - if (repr == NULL) { + + PyBytesWriter *writer = PyBytesWriter_Create(expandsize * len); + if (writer == NULL) { return NULL; } - if (len == 0) { - return repr; - } + char *p = PyBytesWriter_GetData(writer); - p = PyBytes_AS_STRING(repr); - for (pos = 0; pos < len; pos++) { + for (Py_ssize_t pos = 0; pos < len; pos++) { Py_UCS4 ch = PyUnicode_READ(kind, data, pos); /* U+0000-U+00ff range: Copy 8-bit characters as-is */ @@ -7220,11 +7066,7 @@ PyUnicode_AsRawUnicodeEscapeString(PyObject *unicode) } } - assert(p > PyBytes_AS_STRING(repr)); - if (_PyBytes_Resize(&repr, p - PyBytes_AS_STRING(repr)) < 0) { - return NULL; - } - return repr; + return PyBytesWriter_FinishWithPointer(writer, p); } /* --- Latin-1 Codec ------------------------------------------------------ */ @@ -7347,16 +7189,12 @@ unicode_encode_ucs1(PyObject *unicode, Py_ssize_t pos=0, size; int kind; const void *data; - /* pointer into the output */ - char *str; const char *encoding = (limit == 256) ? "latin-1" : "ascii"; const char *reason = (limit == 256) ? "ordinal not in range(256)" : "ordinal not in range(128)"; PyObject *error_handler_obj = NULL; PyObject *exc = NULL; _Py_error_handler error_handler = _Py_ERROR_UNKNOWN; PyObject *rep = NULL; - /* output object */ - _PyBytesWriter writer; size = PyUnicode_GET_LENGTH(unicode); kind = PyUnicode_KIND(unicode); @@ -7364,12 +7202,15 @@ unicode_encode_ucs1(PyObject *unicode, /* allocate enough for a simple encoding without replacements, if we need more, we'll resize */ if (size == 0) - return PyBytes_FromStringAndSize(NULL, 0); + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); - _PyBytesWriter_Init(&writer); - str = _PyBytesWriter_Alloc(&writer, size); - if (str == NULL) + /* output object */ + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; + } + /* pointer into the output */ + char *str = PyBytesWriter_GetData(writer); while (pos < size) { Py_UCS4 ch = PyUnicode_READ(kind, data, pos); @@ -7391,7 +7232,7 @@ unicode_encode_ucs1(PyObject *unicode, ++collend; /* Only overallocate the buffer if it's not the last write */ - writer.overallocate = (collend < size); + writer->overallocate = (collend < size); /* cache callback name lookup (if not done yet, i.e. it's the first error) */ if (error_handler == _Py_ERROR_UNKNOWN) @@ -7412,8 +7253,8 @@ unicode_encode_ucs1(PyObject *unicode, case _Py_ERROR_BACKSLASHREPLACE: /* subtract preallocated bytes */ - writer.min_size -= (collend - collstart); - str = backslashreplace(&writer, str, + writer->size -= (collend - collstart); + str = backslashreplace(writer, str, unicode, collstart, collend); if (str == NULL) goto onError; @@ -7422,8 +7263,8 @@ unicode_encode_ucs1(PyObject *unicode, case _Py_ERROR_XMLCHARREFREPLACE: /* subtract preallocated bytes */ - writer.min_size -= (collend - collstart); - str = xmlcharrefreplace(&writer, str, + writer->size -= (collend - collstart); + str = xmlcharrefreplace(writer, str, unicode, collstart, collend); if (str == NULL) goto onError; @@ -7454,24 +7295,27 @@ unicode_encode_ucs1(PyObject *unicode, goto onError; if (newpos < collstart) { - writer.overallocate = 1; - str = _PyBytesWriter_Prepare(&writer, str, - collstart - newpos); - if (str == NULL) + writer->overallocate = 1; + str = PyBytesWriter_GrowAndUpdatePointer(writer, + collstart - newpos, + str); + if (str == NULL) { goto onError; + } } else { /* subtract preallocated bytes */ - writer.min_size -= newpos - collstart; + writer->size -= newpos - collstart; /* Only overallocate the buffer if it's not the last write */ - writer.overallocate = (newpos < size); + writer->overallocate = (newpos < size); } + char *rep_str; + Py_ssize_t rep_len; if (PyBytes_Check(rep)) { /* Directly copy bytes result to output. */ - str = _PyBytesWriter_WriteBytes(&writer, str, - PyBytes_AS_STRING(rep), - PyBytes_GET_SIZE(rep)); + rep_str = PyBytes_AS_STRING(rep); + rep_len = PyBytes_GET_SIZE(rep); } else { assert(PyUnicode_Check(rep)); @@ -7486,12 +7330,16 @@ unicode_encode_ucs1(PyObject *unicode, goto onError; } assert(PyUnicode_KIND(rep) == PyUnicode_1BYTE_KIND); - str = _PyBytesWriter_WriteBytes(&writer, str, - PyUnicode_DATA(rep), - PyUnicode_GET_LENGTH(rep)); + rep_str = PyUnicode_DATA(rep); + rep_len = PyUnicode_GET_LENGTH(rep); } - if (str == NULL) + + str = PyBytesWriter_GrowAndUpdatePointer(writer, rep_len, str); + if (str == NULL) { goto onError; + } + memcpy(str, rep_str, rep_len); + str += rep_len; pos = newpos; Py_CLEAR(rep); @@ -7499,17 +7347,17 @@ unicode_encode_ucs1(PyObject *unicode, /* If overallocation was disabled, ensure that it was the last write. Otherwise, we missed an optimization */ - assert(writer.overallocate || pos == size); + assert(writer->overallocate || pos == size); } } Py_XDECREF(error_handler_obj); Py_XDECREF(exc); - return _PyBytesWriter_Finish(&writer, str); + return PyBytesWriter_FinishWithPointer(writer, str); onError: Py_XDECREF(rep); - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter_Discard(writer); Py_XDECREF(error_handler_obj); Py_XDECREF(exc); return NULL; @@ -7684,10 +7532,6 @@ code_page_name(UINT code_page, PyObject **obj) *obj = NULL; if (code_page == CP_ACP) return "mbcs"; - if (code_page == CP_UTF7) - return "CP_UTF7"; - if (code_page == CP_UTF8) - return "CP_UTF8"; *obj = PyBytes_FromFormat("cp%u", code_page); if (*obj == NULL) @@ -8007,7 +7851,7 @@ encode_code_page_flags(UINT code_page, const char *errors) * an OSError and returns -1 on other error. */ static int -encode_code_page_strict(UINT code_page, PyObject **outbytes, +encode_code_page_strict(UINT code_page, PyBytesWriter **writer, PyObject *unicode, Py_ssize_t offset, int len, const char* errors) { @@ -8053,25 +7897,21 @@ encode_code_page_strict(UINT code_page, PyObject **outbytes, goto done; } - if (*outbytes == NULL) { + if (*writer == NULL) { /* Create string object */ - *outbytes = PyBytes_FromStringAndSize(NULL, outsize); - if (*outbytes == NULL) { + *writer = PyBytesWriter_Create(outsize); + if (*writer == NULL) { goto done; } - out = PyBytes_AS_STRING(*outbytes); + out = PyBytesWriter_GetData(*writer); } else { /* Extend string object */ - const Py_ssize_t n = PyBytes_Size(*outbytes); - if (outsize > PY_SSIZE_T_MAX - n) { - PyErr_NoMemory(); + Py_ssize_t n = PyBytesWriter_GetSize(*writer); + if (PyBytesWriter_Grow(*writer, outsize) < 0) { goto done; } - if (_PyBytes_Resize(outbytes, n + outsize) < 0) { - goto done; - } - out = PyBytes_AS_STRING(*outbytes) + n; + out = (char*)PyBytesWriter_GetData(*writer) + n; } /* Do the conversion */ @@ -8108,7 +7948,7 @@ encode_code_page_strict(UINT code_page, PyObject **outbytes, * -1 on other error. */ static int -encode_code_page_errors(UINT code_page, PyObject **outbytes, +encode_code_page_errors(UINT code_page, PyBytesWriter **writer, PyObject *unicode, Py_ssize_t unicode_offset, Py_ssize_t insize, const char* errors) { @@ -8127,7 +7967,7 @@ encode_code_page_errors(UINT code_page, PyObject **outbytes, PyObject *exc = NULL; PyObject *encoding_obj = NULL; const char *encoding; - Py_ssize_t newpos, newoutsize; + Py_ssize_t newpos; PyObject *rep; int ret = -1; @@ -8160,23 +8000,21 @@ encode_code_page_errors(UINT code_page, PyObject **outbytes, } outsize = insize * Py_ARRAY_LENGTH(buffer); - if (*outbytes == NULL) { + if (*writer == NULL) { /* Create string object */ - *outbytes = PyBytes_FromStringAndSize(NULL, outsize); - if (*outbytes == NULL) + *writer = PyBytesWriter_Create(outsize); + if (*writer == NULL) { goto error; - out = PyBytes_AS_STRING(*outbytes); + } + out = PyBytesWriter_GetData(*writer); } else { /* Extend string object */ - Py_ssize_t n = PyBytes_Size(*outbytes); - if (n > PY_SSIZE_T_MAX - outsize) { - PyErr_NoMemory(); + Py_ssize_t n = PyBytesWriter_GetSize(*writer); + if (PyBytesWriter_Grow(*writer, outsize) < 0) { goto error; } - if (_PyBytes_Resize(outbytes, n + outsize) < 0) - goto error; - out = PyBytes_AS_STRING(*outbytes) + n; + out = (char*)PyBytesWriter_GetData(*writer) + n; } /* Encode the string character per character */ @@ -8225,13 +8063,11 @@ encode_code_page_errors(UINT code_page, PyObject **outbytes, outsize = PyBytes_GET_SIZE(rep); morebytes += outsize; if (morebytes > 0) { - Py_ssize_t offset = out - PyBytes_AS_STRING(*outbytes); - newoutsize = PyBytes_GET_SIZE(*outbytes) + morebytes; - if (_PyBytes_Resize(outbytes, newoutsize) < 0) { + out = PyBytesWriter_GrowAndUpdatePointer(*writer, morebytes, out); + if (out == NULL) { Py_DECREF(rep); goto error; } - out = PyBytes_AS_STRING(*outbytes) + offset; } memcpy(out, PyBytes_AS_STRING(rep), outsize); out += outsize; @@ -8244,13 +8080,11 @@ encode_code_page_errors(UINT code_page, PyObject **outbytes, outsize = PyUnicode_GET_LENGTH(rep); morebytes += outsize; if (morebytes > 0) { - Py_ssize_t offset = out - PyBytes_AS_STRING(*outbytes); - newoutsize = PyBytes_GET_SIZE(*outbytes) + morebytes; - if (_PyBytes_Resize(outbytes, newoutsize) < 0) { + out = PyBytesWriter_GrowAndUpdatePointer(*writer, morebytes, out); + if (out == NULL) { Py_DECREF(rep); goto error; } - out = PyBytes_AS_STRING(*outbytes) + offset; } kind = PyUnicode_KIND(rep); data = PyUnicode_DATA(rep); @@ -8273,10 +8107,11 @@ encode_code_page_errors(UINT code_page, PyObject **outbytes, } /* write a NUL byte */ *out = 0; - outsize = out - PyBytes_AS_STRING(*outbytes); - assert(outsize <= PyBytes_GET_SIZE(*outbytes)); - if (_PyBytes_Resize(outbytes, outsize) < 0) + outsize = out - (char*)PyBytesWriter_GetData(*writer); + assert(outsize <= PyBytesWriter_GetSize(*writer)); + if (PyBytesWriter_Resize(*writer, outsize) < 0) { goto error; + } ret = 0; error: @@ -8286,13 +8121,14 @@ encode_code_page_errors(UINT code_page, PyObject **outbytes, return ret; } -static PyObject * -encode_code_page(int code_page, - PyObject *unicode, - const char *errors) + +PyObject * +PyUnicode_EncodeCodePage(int code_page, + PyObject *unicode, + const char *errors) { Py_ssize_t len; - PyObject *outbytes = NULL; + PyBytesWriter *writer = NULL; Py_ssize_t offset; int chunk_len, ret, done; @@ -8309,7 +8145,7 @@ encode_code_page(int code_page, } if (len == 0) - return PyBytes_FromStringAndSize(NULL, 0); + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); offset = 0; do @@ -8326,15 +8162,15 @@ encode_code_page(int code_page, done = 1; } - ret = encode_code_page_strict(code_page, &outbytes, + ret = encode_code_page_strict(code_page, &writer, unicode, offset, chunk_len, errors); if (ret == -2) - ret = encode_code_page_errors(code_page, &outbytes, + ret = encode_code_page_errors(code_page, &writer, unicode, offset, chunk_len, errors); if (ret < 0) { - Py_XDECREF(outbytes); + PyBytesWriter_Discard(writer); return NULL; } @@ -8342,16 +8178,9 @@ encode_code_page(int code_page, len -= chunk_len; } while (!done); - return outbytes; + return PyBytesWriter_Finish(writer); } -PyObject * -PyUnicode_EncodeCodePage(int code_page, - PyObject *unicode, - const char *errors) -{ - return encode_code_page(code_page, unicode, errors); -} PyObject * PyUnicode_AsMBCSString(PyObject *unicode) @@ -8529,7 +8358,7 @@ charmap_decode_mapping(const char *s, goto Undefined; if (value < 0 || value > MAX_UNICODE) { PyErr_Format(PyExc_TypeError, - "character mapping must be in range(0x%x)", + "character mapping must be in range(0x%lx)", (unsigned long)MAX_UNICODE + 1); goto onError; } @@ -8861,15 +8690,13 @@ charmapencode_lookup(Py_UCS4 c, PyObject *mapping, unsigned char *replace) } static int -charmapencode_resize(PyObject **outobj, Py_ssize_t *outpos, Py_ssize_t requiredsize) +charmapencode_resize(PyBytesWriter *writer, Py_ssize_t *outpos, Py_ssize_t requiredsize) { - Py_ssize_t outsize = PyBytes_GET_SIZE(*outobj); + Py_ssize_t outsize = PyBytesWriter_GetSize(writer); /* exponentially overallocate to minimize reallocations */ - if (requiredsize < 2*outsize) - requiredsize = 2*outsize; - if (_PyBytes_Resize(outobj, requiredsize)) - return -1; - return 0; + if (requiredsize < 2 * outsize) + requiredsize = 2 * outsize; + return PyBytesWriter_Resize(writer, requiredsize); } typedef enum charmapencode_result { @@ -8883,22 +8710,26 @@ typedef enum charmapencode_result { reallocation error occurred. The caller must decref the result */ static charmapencode_result charmapencode_output(Py_UCS4 c, PyObject *mapping, - PyObject **outobj, Py_ssize_t *outpos) + PyBytesWriter *writer, Py_ssize_t *outpos) { PyObject *rep; unsigned char replace; char *outstart; - Py_ssize_t outsize = PyBytes_GET_SIZE(*outobj); + Py_ssize_t outsize = _PyBytesWriter_GetSize(writer); if (Py_IS_TYPE(mapping, &EncodingMapType)) { int res = encoding_map_lookup(c, mapping); Py_ssize_t requiredsize = *outpos+1; - if (res == -1) + if (res == -1) { return enc_FAILED; - if (outsize<requiredsize) - if (charmapencode_resize(outobj, outpos, requiredsize)) + } + + if (outsize<requiredsize) { + if (charmapencode_resize(writer, outpos, requiredsize)) { return enc_EXCEPTION; - outstart = PyBytes_AS_STRING(*outobj); + } + } + outstart = _PyBytesWriter_GetData(writer); outstart[(*outpos)++] = (char)res; return enc_SUCCESS; } @@ -8913,11 +8744,11 @@ charmapencode_output(Py_UCS4 c, PyObject *mapping, if (PyLong_Check(rep)) { Py_ssize_t requiredsize = *outpos+1; if (outsize<requiredsize) - if (charmapencode_resize(outobj, outpos, requiredsize)) { + if (charmapencode_resize(writer, outpos, requiredsize)) { Py_DECREF(rep); return enc_EXCEPTION; } - outstart = PyBytes_AS_STRING(*outobj); + outstart = _PyBytesWriter_GetData(writer); outstart[(*outpos)++] = (char)replace; } else { @@ -8925,11 +8756,11 @@ charmapencode_output(Py_UCS4 c, PyObject *mapping, Py_ssize_t repsize = PyBytes_GET_SIZE(rep); Py_ssize_t requiredsize = *outpos+repsize; if (outsize<requiredsize) - if (charmapencode_resize(outobj, outpos, requiredsize)) { + if (charmapencode_resize(writer, outpos, requiredsize)) { Py_DECREF(rep); return enc_EXCEPTION; } - outstart = PyBytes_AS_STRING(*outobj); + outstart = _PyBytesWriter_GetData(writer); memcpy(outstart + *outpos, repchars, repsize); *outpos += repsize; } @@ -8938,14 +8769,14 @@ charmapencode_output(Py_UCS4 c, PyObject *mapping, return enc_SUCCESS; } -/* handle an error in PyUnicode_EncodeCharmap +/* handle an error in _PyUnicode_EncodeCharmap() Return 0 on success, -1 on error */ static int charmap_encoding_error( PyObject *unicode, Py_ssize_t *inpos, PyObject *mapping, PyObject **exceptionObject, _Py_error_handler *error_handler, PyObject **error_handler_obj, const char *errors, - PyObject **res, Py_ssize_t *respos) + PyBytesWriter *writer, Py_ssize_t *respos) { PyObject *repunicode = NULL; /* initialize to prevent gcc warning */ Py_ssize_t size, repsize; @@ -9000,7 +8831,7 @@ charmap_encoding_error( case _Py_ERROR_REPLACE: for (collpos = collstartpos; collpos<collendpos; ++collpos) { - x = charmapencode_output('?', mapping, res, respos); + x = charmapencode_output('?', mapping, writer, respos); if (x==enc_EXCEPTION) { return -1; } @@ -9021,7 +8852,7 @@ charmap_encoding_error( char *cp; sprintf(buffer, "&#%d;", (int)PyUnicode_READ_CHAR(unicode, collpos)); for (cp = buffer; *cp; ++cp) { - x = charmapencode_output(*cp, mapping, res, respos); + x = charmapencode_output(*cp, mapping, writer, respos); if (x==enc_EXCEPTION) return -1; else if (x==enc_FAILED) { @@ -9041,17 +8872,17 @@ charmap_encoding_error( return -1; if (PyBytes_Check(repunicode)) { /* Directly copy bytes result to output. */ - Py_ssize_t outsize = PyBytes_Size(*res); + Py_ssize_t outsize = PyBytesWriter_GetSize(writer); Py_ssize_t requiredsize; repsize = PyBytes_Size(repunicode); requiredsize = *respos + repsize; if (requiredsize > outsize) /* Make room for all additional bytes. */ - if (charmapencode_resize(res, respos, requiredsize)) { + if (charmapencode_resize(writer, respos, requiredsize)) { Py_DECREF(repunicode); return -1; } - memcpy(PyBytes_AsString(*res) + *respos, + memcpy((char*)PyBytesWriter_GetData(writer) + *respos, PyBytes_AsString(repunicode), repsize); *respos += repsize; *inpos = newpos; @@ -9064,7 +8895,7 @@ charmap_encoding_error( kind = PyUnicode_KIND(repunicode); for (index = 0; index < repsize; index++) { Py_UCS4 repch = PyUnicode_READ(kind, data, index); - x = charmapencode_output(repch, mapping, res, respos); + x = charmapencode_output(repch, mapping, writer, respos); if (x==enc_EXCEPTION) { Py_DECREF(repunicode); return -1; @@ -9086,65 +8917,105 @@ _PyUnicode_EncodeCharmap(PyObject *unicode, PyObject *mapping, const char *errors) { + /* Default to Latin-1 */ + if (mapping == NULL) { + return unicode_encode_ucs1(unicode, errors, 256); + } + + Py_ssize_t size = PyUnicode_GET_LENGTH(unicode); + if (size == 0) { + return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); + } + const void *data = PyUnicode_DATA(unicode); + int kind = PyUnicode_KIND(unicode); + + PyObject *error_handler_obj = NULL; + PyObject *exc = NULL; + /* output object */ - PyObject *res = NULL; + PyBytesWriter *writer; + /* allocate enough for a simple encoding without + replacements, if we need more, we'll resize */ + writer = PyBytesWriter_Create(size); + if (writer == NULL) { + goto onError; + } + /* current input position */ Py_ssize_t inpos = 0; - Py_ssize_t size; /* current output position */ Py_ssize_t respos = 0; - PyObject *error_handler_obj = NULL; - PyObject *exc = NULL; _Py_error_handler error_handler = _Py_ERROR_UNKNOWN; - const void *data; - int kind; - size = PyUnicode_GET_LENGTH(unicode); - data = PyUnicode_DATA(unicode); - kind = PyUnicode_KIND(unicode); + if (Py_IS_TYPE(mapping, &EncodingMapType)) { + char *outstart = _PyBytesWriter_GetData(writer); + Py_ssize_t outsize = _PyBytesWriter_GetSize(writer); - /* Default to Latin-1 */ - if (mapping == NULL) - return unicode_encode_ucs1(unicode, errors, 256); + while (inpos<size) { + Py_UCS4 ch = PyUnicode_READ(kind, data, inpos); - /* allocate enough for a simple encoding without - replacements, if we need more, we'll resize */ - res = PyBytes_FromStringAndSize(NULL, size); - if (res == NULL) - goto onError; - if (size == 0) - return res; + /* try to encode it */ + int res = encoding_map_lookup(ch, mapping); + Py_ssize_t requiredsize = respos+1; + if (res == -1) { + goto enc_FAILED; + } - while (inpos<size) { - Py_UCS4 ch = PyUnicode_READ(kind, data, inpos); - /* try to encode it */ - charmapencode_result x = charmapencode_output(ch, mapping, &res, &respos); - if (x==enc_EXCEPTION) /* error */ - goto onError; - if (x==enc_FAILED) { /* unencodable character */ + if (outsize<requiredsize) { + if (charmapencode_resize(writer, &respos, requiredsize)) { + goto onError; + } + outstart = _PyBytesWriter_GetData(writer); + outsize = _PyBytesWriter_GetSize(writer); + } + outstart[respos++] = (char)res; + + /* done with this character => adjust input position */ + ++inpos; + continue; + +enc_FAILED: if (charmap_encoding_error(unicode, &inpos, mapping, &exc, &error_handler, &error_handler_obj, errors, - &res, &respos)) { + writer, &respos)) { goto onError; } + outstart = _PyBytesWriter_GetData(writer); + outsize = _PyBytesWriter_GetSize(writer); + } + } + else { + while (inpos<size) { + Py_UCS4 ch = PyUnicode_READ(kind, data, inpos); + /* try to encode it */ + charmapencode_result x = charmapencode_output(ch, mapping, writer, &respos); + if (x==enc_EXCEPTION) { /* error */ + goto onError; + } + if (x==enc_FAILED) { /* unencodable character */ + if (charmap_encoding_error(unicode, &inpos, mapping, + &exc, + &error_handler, &error_handler_obj, errors, + writer, &respos)) { + goto onError; + } + } + else { + /* done with this character => adjust input position */ + ++inpos; + } } - else - /* done with this character => adjust input position */ - ++inpos; } - - /* Resize if we allocated to much */ - if (respos<PyBytes_GET_SIZE(res)) - if (_PyBytes_Resize(&res, respos) < 0) - goto onError; Py_XDECREF(exc); Py_XDECREF(error_handler_obj); - return res; + + /* Resize if we allocated too much */ + return PyBytesWriter_FinishWithSize(writer, respos); onError: - Py_XDECREF(res); + PyBytesWriter_Discard(writer); Py_XDECREF(exc); Py_XDECREF(error_handler_obj); return NULL; @@ -9278,8 +9149,8 @@ charmaptranslate_lookup(Py_UCS4 c, PyObject *mapping, PyObject **result, Py_UCS4 long value = PyLong_AsLong(x); if (value < 0 || value > MAX_UNICODE) { PyErr_Format(PyExc_ValueError, - "character mapping must be in range(0x%x)", - MAX_UNICODE+1); + "character mapping must be in range(0x%lx)", + (unsigned long)MAX_UNICODE + 1); Py_DECREF(x); return -1; } @@ -9730,142 +9601,6 @@ any_find_slice(PyObject* s1, PyObject* s2, return result; } -/* _PyUnicode_InsertThousandsGrouping() helper functions */ -#include "stringlib/localeutil.h" - -/** - * InsertThousandsGrouping: - * @writer: Unicode writer. - * @n_buffer: Number of characters in @buffer. - * @digits: Digits we're reading from. If count is non-NULL, this is unused. - * @d_pos: Start of digits string. - * @n_digits: The number of digits in the string, in which we want - * to put the grouping chars. - * @min_width: The minimum width of the digits in the output string. - * Output will be zero-padded on the left to fill. - * @grouping: see definition in localeconv(). - * @thousands_sep: see definition in localeconv(). - * - * There are 2 modes: counting and filling. If @writer is NULL, - * we are in counting mode, else filling mode. - * If counting, the required buffer size is returned. - * If filling, we know the buffer will be large enough, so we don't - * need to pass in the buffer size. - * Inserts thousand grouping characters (as defined by grouping and - * thousands_sep) into @writer. - * - * Return value: -1 on error, number of characters otherwise. - **/ -Py_ssize_t -_PyUnicode_InsertThousandsGrouping( - _PyUnicodeWriter *writer, - Py_ssize_t n_buffer, - PyObject *digits, - Py_ssize_t d_pos, - Py_ssize_t n_digits, - Py_ssize_t min_width, - const char *grouping, - PyObject *thousands_sep, - Py_UCS4 *maxchar, - int forward) -{ - min_width = Py_MAX(0, min_width); - if (writer) { - assert(digits != NULL); - assert(maxchar == NULL); - } - else { - assert(digits == NULL); - assert(maxchar != NULL); - } - assert(0 <= d_pos); - assert(0 <= n_digits); - assert(grouping != NULL); - - Py_ssize_t count = 0; - Py_ssize_t n_zeros; - int loop_broken = 0; - int use_separator = 0; /* First time through, don't append the - separator. They only go between - groups. */ - Py_ssize_t buffer_pos; - Py_ssize_t digits_pos; - Py_ssize_t len; - Py_ssize_t n_chars; - Py_ssize_t remaining = n_digits; /* Number of chars remaining to - be looked at */ - /* A generator that returns all of the grouping widths, until it - returns 0. */ - GroupGenerator groupgen; - GroupGenerator_init(&groupgen, grouping); - const Py_ssize_t thousands_sep_len = PyUnicode_GET_LENGTH(thousands_sep); - - /* if digits are not grouped, thousands separator - should be an empty string */ - assert(!(grouping[0] == CHAR_MAX && thousands_sep_len != 0)); - - digits_pos = d_pos + (forward ? 0 : n_digits); - if (writer) { - buffer_pos = writer->pos + (forward ? 0 : n_buffer); - assert(buffer_pos <= PyUnicode_GET_LENGTH(writer->buffer)); - assert(digits_pos <= PyUnicode_GET_LENGTH(digits)); - } - else { - buffer_pos = forward ? 0 : n_buffer; - } - - if (!writer) { - *maxchar = 127; - } - - while ((len = GroupGenerator_next(&groupgen)) > 0) { - len = Py_MIN(len, Py_MAX(Py_MAX(remaining, min_width), 1)); - n_zeros = Py_MAX(0, len - remaining); - n_chars = Py_MAX(0, Py_MIN(remaining, len)); - - /* Use n_zero zero's and n_chars chars */ - - /* Count only, don't do anything. */ - count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars; - - /* Copy into the writer. */ - InsertThousandsGrouping_fill(writer, &buffer_pos, - digits, &digits_pos, - n_chars, n_zeros, - use_separator ? thousands_sep : NULL, - thousands_sep_len, maxchar, forward); - - /* Use a separator next time. */ - use_separator = 1; - - remaining -= n_chars; - min_width -= len; - - if (remaining <= 0 && min_width <= 0) { - loop_broken = 1; - break; - } - min_width -= thousands_sep_len; - } - if (!loop_broken) { - /* We left the loop without using a break statement. */ - - len = Py_MAX(Py_MAX(remaining, min_width), 1); - n_zeros = Py_MAX(0, len - remaining); - n_chars = Py_MAX(0, Py_MIN(remaining, len)); - - /* Use n_zero zero's and n_chars chars */ - count += (use_separator ? thousands_sep_len : 0) + n_zeros + n_chars; - - /* Copy into the writer. */ - InsertThousandsGrouping_fill(writer, &buffer_pos, - digits, &digits_pos, - n_chars, n_zeros, - use_separator ? thousands_sep : NULL, - thousands_sep_len, maxchar, forward); - } - return count; -} Py_ssize_t PyUnicode_Count(PyObject *str, @@ -10418,11 +10153,11 @@ _PyUnicode_FastFill(PyObject *unicode, Py_ssize_t start, Py_ssize_t length, { const int kind = PyUnicode_KIND(unicode); void *data = PyUnicode_DATA(unicode); - assert(unicode_modifiable(unicode)); + assert(_PyUnicode_IsModifiable(unicode)); assert(fill_char <= PyUnicode_MAX_CHAR_VALUE(unicode)); assert(start >= 0); assert(start + length <= PyUnicode_GET_LENGTH(unicode)); - unicode_fill(kind, data, fill_char, start, length); + _PyUnicode_Fill(kind, data, fill_char, start, length); } Py_ssize_t @@ -10491,9 +10226,10 @@ pad(PyObject *self, kind = PyUnicode_KIND(u); data = PyUnicode_DATA(u); if (left) - unicode_fill(kind, data, fill, 0, left); + _PyUnicode_Fill(kind, data, fill, 0, left); if (right) - unicode_fill(kind, data, fill, left + _PyUnicode_LENGTH(self), right); + _PyUnicode_Fill(kind, data, fill, + left + _PyUnicode_LENGTH(self), right); _PyUnicode_FastCopyCharacters(u, left, self, 0, _PyUnicode_LENGTH(self)); assert(_PyUnicode_CheckConsistency(u, 1)); return u; @@ -10945,7 +10681,7 @@ replace(PyObject *self, PyObject *str1, } new_size = slen + n * (len2 - len1); if (new_size == 0) { - u = unicode_get_empty(); + u = _PyUnicode_GetEmpty(); goto done; } if (new_size > (PY_SSIZE_T_MAX / rkind)) { @@ -11062,13 +10798,13 @@ str.title as unicode_title Return a version of the string where each word is titlecased. -More specifically, words start with uppercased characters and all remaining -cased characters have lower case. +More specifically, words start with uppercased characters and all +remaining cased characters have lower case. [clinic start generated code]*/ static PyObject * unicode_title_impl(PyObject *self) -/*[clinic end generated code: output=c75ae03809574902 input=fa945d669b26e683]*/ +/*[clinic end generated code: output=c75ae03809574902 input=2a07e2c7df94627a]*/ { return case_operation(self, do_title); } @@ -11078,13 +10814,13 @@ str.capitalize as unicode_capitalize Return a capitalized version of the string. -More specifically, make the first character have upper case and the rest lower -case. +More specifically, make the first character have upper case and the +rest lower case. [clinic start generated code]*/ static PyObject * unicode_capitalize_impl(PyObject *self) -/*[clinic end generated code: output=e49a4c333cdb7667 input=f4cbf1016938da6d]*/ +/*[clinic end generated code: output=e49a4c333cdb7667 input=e50e50ed45a654cf]*/ { if (PyUnicode_GET_LENGTH(self) == 0) return unicode_result_unchanged(self); @@ -11138,12 +10874,13 @@ str.center as unicode_center Return a centered string of length width. -Padding is done using the specified fill character (default is a space). +Padding is done using the specified fill character (default is +a space). [clinic start generated code]*/ static PyObject * unicode_center_impl(PyObject *self, Py_ssize_t width, Py_UCS4 fillchar) -/*[clinic end generated code: output=420c8859effc7c0c input=b42b247eb26e6519]*/ +/*[clinic end generated code: output=420c8859effc7c0c input=df91017dfd186a78]*/ { Py_ssize_t marg, left; @@ -11461,47 +11198,6 @@ _PyUnicode_EqualToASCIIString(PyObject *unicode, const char *str) memcmp(PyUnicode_1BYTE_DATA(unicode), str, len) == 0; } -int -_PyUnicode_EqualToASCIIId(PyObject *left, _Py_Identifier *right) -{ - PyObject *right_uni; - - assert(_PyUnicode_CHECK(left)); - assert(right->string); -#ifndef NDEBUG - for (const char *p = right->string; *p; p++) { - assert((unsigned char)*p < 128); - } -#endif - - if (!PyUnicode_IS_ASCII(left)) - return 0; - - right_uni = _PyUnicode_FromId(right); /* borrowed */ - if (right_uni == NULL) { - /* memory error or bad data */ - PyErr_Clear(); - return _PyUnicode_EqualToASCIIString(left, right->string); - } - - if (left == right_uni) - return 1; - - assert(PyUnicode_CHECK_INTERNED(right_uni)); - if (PyUnicode_CHECK_INTERNED(left)) { - return 0; - } - - Py_hash_t right_hash = PyUnicode_HASH(right_uni); - assert(right_hash != -1); - Py_hash_t hash = PyUnicode_HASH(left); - if (hash != -1 && hash != right_hash) { - return 0; - } - - return unicode_eq(left, right_uni); -} - PyObject * PyUnicode_RichCompare(PyObject *left, PyObject *right, int op) { @@ -11616,7 +11312,7 @@ PyUnicode_Concat(PyObject *left, PyObject *right) } /* Shortcuts */ - PyObject *empty = unicode_get_empty(); // Borrowed reference + PyObject *empty = _PyUnicode_GetEmpty(); // Borrowed reference if (left == empty) { return PyUnicode_FromObject(right); } @@ -11668,7 +11364,7 @@ PyUnicode_Append(PyObject **p_left, PyObject *right) } /* Shortcuts */ - PyObject *empty = unicode_get_empty(); // Borrowed reference + PyObject *empty = _PyUnicode_GetEmpty(); // Borrowed reference if (left == empty) { Py_DECREF(left); *p_left = Py_NewRef(right); @@ -11687,7 +11383,7 @@ PyUnicode_Append(PyObject **p_left, PyObject *right) } new_len = left_len + right_len; - if (unicode_modifiable(left) + if (_PyUnicode_IsModifiable(left) && PyUnicode_CheckExact(right) && PyUnicode_KIND(right) <= PyUnicode_KIND(left) /* Don't resize for ascii += latin1. Convert ascii to latin1 requires @@ -11732,6 +11428,7 @@ PyUnicode_AppendAndDel(PyObject **pleft, PyObject *right) } /*[clinic input] +@permit_long_summary @text_signature "($self, sub[, start[, end]], /)" str.count as unicode_count -> Py_ssize_t @@ -11743,13 +11440,14 @@ str.count as unicode_count -> Py_ssize_t Return the number of non-overlapping occurrences of substring sub in string S[start:end]. -Optional arguments start and end are interpreted as in slice notation. +Optional arguments start and end are interpreted as in slice +notation. [clinic start generated code]*/ static Py_ssize_t unicode_count_impl(PyObject *str, PyObject *substr, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=8fcc3aef0b18edbf input=6f168ffd94be8785]*/ +/*[clinic end generated code: output=8fcc3aef0b18edbf input=c9209e05438cc352]*/ { assert(PyUnicode_Check(str)); assert(PyUnicode_Check(substr)); @@ -11822,8 +11520,8 @@ str.encode as unicode_encode errors: str(c_default="NULL") = 'strict' The error handling scheme to use for encoding errors. The default is 'strict' meaning that encoding errors raise a - UnicodeEncodeError. Other possible values are 'ignore', 'replace' and - 'xmlcharrefreplace' as well as any other name registered with + UnicodeEncodeError. Other possible values are 'ignore', 'replace' + and 'xmlcharrefreplace' as well as any other name registered with codecs.register_error that can handle UnicodeEncodeErrors. Encode the string using the codec registered for encoding. @@ -11831,7 +11529,7 @@ Encode the string using the codec registered for encoding. static PyObject * unicode_encode_impl(PyObject *self, const char *encoding, const char *errors) -/*[clinic end generated code: output=bf78b6e2a9470e3c input=f0a9eb293d08fe02]*/ +/*[clinic end generated code: output=bf78b6e2a9470e3c input=b85a9645cb33b729]*/ { return PyUnicode_AsEncodedString(self, encoding, errors); } @@ -11902,7 +11600,7 @@ unicode_expandtabs_impl(PyObject *self, int tabsize) if (tabsize > 0) { incr = tabsize - (line_pos % tabsize); line_pos += incr; - unicode_fill(kind, dest_data, ' ', j, incr); + _PyUnicode_Fill(kind, dest_data, ' ', j, incr); j += incr; } } @@ -11923,18 +11621,19 @@ unicode_expandtabs_impl(PyObject *self, int tabsize) } /*[clinic input] +@permit_long_summary str.find as unicode_find = str.count Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end]. -Optional arguments start and end are interpreted as in slice notation. -Return -1 on failure. +Optional arguments start and end are interpreted as in slice +notation. Return -1 on failure. [clinic start generated code]*/ static Py_ssize_t unicode_find_impl(PyObject *str, PyObject *substr, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=51dbe6255712e278 input=4a89d2d68ef57256]*/ +/*[clinic end generated code: output=51dbe6255712e278 input=f57e93c59d1ee927]*/ { Py_ssize_t result = any_find_slice(str, substr, start, end, 1); if (result < 0) { @@ -11986,18 +11685,19 @@ unicode_hash(PyObject *self) } /*[clinic input] +@permit_long_summary str.index as unicode_index = str.count Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end]. -Optional arguments start and end are interpreted as in slice notation. -Raises ValueError when the substring is not found. +Optional arguments start and end are interpreted as in slice +notation. Raises ValueError when the substring is not found. [clinic start generated code]*/ static Py_ssize_t unicode_index_impl(PyObject *str, PyObject *substr, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=77558288837cdf40 input=d986aeac0be14a1c]*/ +/*[clinic end generated code: output=77558288837cdf40 input=5900ab84de55e628]*/ { Py_ssize_t result = any_find_slice(str, substr, start, end, 1); if (result == -1) { @@ -12010,6 +11710,7 @@ unicode_index_impl(PyObject *str, PyObject *substr, Py_ssize_t start, } /*[clinic input] +@permit_long_summary str.isascii as unicode_isascii Return True if all characters in the string are ASCII, False otherwise. @@ -12020,7 +11721,7 @@ Empty string is ASCII too. static PyObject * unicode_isascii_impl(PyObject *self) -/*[clinic end generated code: output=c5910d64b5a8003f input=5a43cbc6399621d5]*/ +/*[clinic end generated code: output=c5910d64b5a8003f input=dc74e1ced821159f]*/ { return PyBool_FromLong(PyUnicode_IS_ASCII(self)); } @@ -12030,13 +11731,13 @@ str.islower as unicode_islower Return True if the string is a lowercase string, False otherwise. -A string is lowercase if all cased characters in the string are lowercase and -there is at least one cased character in the string. +A string is lowercase if all cased characters in the string are +lowercase and there is at least one cased character in the string. [clinic start generated code]*/ static PyObject * unicode_islower_impl(PyObject *self) -/*[clinic end generated code: output=dbd41995bd005b81 input=acec65ac6821ae47]*/ +/*[clinic end generated code: output=dbd41995bd005b81 input=1879b48dfc628366]*/ { Py_ssize_t i, length; int kind; @@ -12073,13 +11774,13 @@ str.isupper as unicode_isupper Return True if the string is an uppercase string, False otherwise. -A string is uppercase if all cased characters in the string are uppercase and -there is at least one cased character in the string. +A string is uppercase if all cased characters in the string are +uppercase and there is at least one cased character in the string. [clinic start generated code]*/ static PyObject * unicode_isupper_impl(PyObject *self) -/*[clinic end generated code: output=049209c8e7f15f59 input=e9b1feda5d17f2d3]*/ +/*[clinic end generated code: output=049209c8e7f15f59 input=77d29904aef0e3a0]*/ { Py_ssize_t i, length; int kind; @@ -12172,13 +11873,13 @@ str.isspace as unicode_isspace Return True if the string is a whitespace string, False otherwise. -A string is whitespace if all characters in the string are whitespace and there -is at least one character in the string. +A string is whitespace if all characters in the string are +whitespace and there is at least one character in the string. [clinic start generated code]*/ static PyObject * unicode_isspace_impl(PyObject *self) -/*[clinic end generated code: output=163a63bfa08ac2b9 input=fe462cb74f8437d8]*/ +/*[clinic end generated code: output=163a63bfa08ac2b9 input=29e09560fc23fbeb]*/ { Py_ssize_t i, length; int kind; @@ -12210,13 +11911,13 @@ str.isalpha as unicode_isalpha Return True if the string is an alphabetic string, False otherwise. -A string is alphabetic if all characters in the string are alphabetic and there -is at least one character in the string. +A string is alphabetic if all characters in the string are +alphabetic and there is at least one character in the string. [clinic start generated code]*/ static PyObject * unicode_isalpha_impl(PyObject *self) -/*[clinic end generated code: output=cc81b9ac3883ec4f input=d0fd18a96cbca5eb]*/ +/*[clinic end generated code: output=cc81b9ac3883ec4f input=9906a07f3e04892e]*/ { Py_ssize_t i, length; int kind; @@ -12243,17 +11944,18 @@ unicode_isalpha_impl(PyObject *self) } /*[clinic input] +@permit_long_summary str.isalnum as unicode_isalnum Return True if the string is an alpha-numeric string, False otherwise. -A string is alpha-numeric if all characters in the string are alpha-numeric and -there is at least one character in the string. +A string is alpha-numeric if all characters in the string are +alpha-numeric and there is at least one character in the string. [clinic start generated code]*/ static PyObject * unicode_isalnum_impl(PyObject *self) -/*[clinic end generated code: output=a5a23490ffc3660c input=5c6579bf2e04758c]*/ +/*[clinic end generated code: output=a5a23490ffc3660c input=892f64ebc171fd4f]*/ { int kind; const void *data; @@ -12286,13 +11988,13 @@ str.isdecimal as unicode_isdecimal Return True if the string is a decimal string, False otherwise. -A string is a decimal string if all characters in the string are decimal and -there is at least one character in the string. +A string is a decimal string if all characters in the string are +decimal and there is at least one character in the string. [clinic start generated code]*/ static PyObject * unicode_isdecimal_impl(PyObject *self) -/*[clinic end generated code: output=fb2dcdb62d3fc548 input=336bc97ab4c8268f]*/ +/*[clinic end generated code: output=fb2dcdb62d3fc548 input=63b0453c48cad0af]*/ { Py_ssize_t i, length; int kind; @@ -12323,13 +12025,13 @@ str.isdigit as unicode_isdigit Return True if the string is a digit string, False otherwise. -A string is a digit string if all characters in the string are digits and there -is at least one character in the string. +A string is a digit string if all characters in the string are +digits and there is at least one character in the string. [clinic start generated code]*/ static PyObject * unicode_isdigit_impl(PyObject *self) -/*[clinic end generated code: output=10a6985311da6858 input=901116c31deeea4c]*/ +/*[clinic end generated code: output=10a6985311da6858 input=353b03747b062e4b]*/ { Py_ssize_t i, length; int kind; @@ -12361,13 +12063,13 @@ str.isnumeric as unicode_isnumeric Return True if the string is a numeric string, False otherwise. -A string is numeric if all characters in the string are numeric and there is at -least one character in the string. +A string is numeric if all characters in the string are numeric and +there is at least one character in the string. [clinic start generated code]*/ static PyObject * unicode_isnumeric_impl(PyObject *self) -/*[clinic end generated code: output=9172a32d9013051a input=722507db976f826c]*/ +/*[clinic end generated code: output=9172a32d9013051a input=83b2a072ed7aff48]*/ { Py_ssize_t i, length; int kind; @@ -12437,22 +12139,24 @@ PyUnicode_IsIdentifier(PyObject *self) } /*[clinic input] +@permit_long_summary str.isidentifier as unicode_isidentifier Return True if the string is a valid Python identifier, False otherwise. -Call keyword.iskeyword(s) to test whether string s is a reserved identifier, -such as "def" or "class". +Call keyword.iskeyword(s) to test whether string s is a reserved +identifier, such as "def" or "class". [clinic start generated code]*/ static PyObject * unicode_isidentifier_impl(PyObject *self) -/*[clinic end generated code: output=fe585a9666572905 input=2d807a104f21c0c5]*/ +/*[clinic end generated code: output=fe585a9666572905 input=cabde62c20a3be6b]*/ { return PyBool_FromLong(PyUnicode_IsIdentifier(self)); } /*[clinic input] +@permit_long_summary str.isprintable as unicode_isprintable Return True if all characters in the string are printable, False otherwise. @@ -12462,7 +12166,7 @@ A character is printable if repr() may use it in its output. static PyObject * unicode_isprintable_impl(PyObject *self) -/*[clinic end generated code: output=3ab9626cd32dd1a0 input=4e56bcc6b06ca18c]*/ +/*[clinic end generated code: output=3ab9626cd32dd1a0 input=18345ba847084ec5]*/ { Py_ssize_t i, length; int kind; @@ -12493,15 +12197,15 @@ str.join as unicode_join Concatenate any number of strings. -The string whose method is called is inserted in between each given string. -The result is returned as a new string. +The string whose method is called is inserted in between each given +string. The result is returned as a new string. Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs' [clinic start generated code]*/ static PyObject * unicode_join(PyObject *self, PyObject *iterable) -/*[clinic end generated code: output=6857e7cecfe7bf98 input=2f70422bfb8fa189]*/ +/*[clinic end generated code: output=6857e7cecfe7bf98 input=fd330a11ee845fb2]*/ { return PyUnicode_Join(self, iterable); } @@ -12521,12 +12225,13 @@ str.ljust as unicode_ljust Return a left-justified string of length width. -Padding is done using the specified fill character (default is a space). +Padding is done using the specified fill character (default is +a space). [clinic start generated code]*/ static PyObject * unicode_ljust_impl(PyObject *self, Py_ssize_t width, Py_UCS4 fillchar) -/*[clinic end generated code: output=1cce0e0e0a0b84b3 input=3ab599e335e60a32]*/ +/*[clinic end generated code: output=1cce0e0e0a0b84b3 input=8a55f06694c20ed6]*/ { if (PyUnicode_GET_LENGTH(self) >= width) return unicode_result_unchanged(self); @@ -12606,6 +12311,18 @@ _PyUnicode_XStrip(PyObject *self, int striptype, PyObject *sepobj) return PyUnicode_Substring(self, i, j); } +PyObject* +_PyUnicode_BinarySlice(PyObject *container, PyObject *start_o, PyObject *stop_o) +{ + assert(PyUnicode_CheckExact(container)); + Py_ssize_t len = PyUnicode_GET_LENGTH(container); + Py_ssize_t istart, istop; + if (!_PyEval_UnpackIndices(start_o, stop_o, len, &istart, &istop)) { + return NULL; + } + return PyUnicode_Substring(container, istart, istop); +} + PyObject* PyUnicode_Substring(PyObject *self, Py_ssize_t start, Py_ssize_t end) { @@ -12722,6 +12439,7 @@ do_argstrip(PyObject *self, int striptype, PyObject *sep) /*[clinic input] +@permit_long_summary str.strip as unicode_strip chars: object = None @@ -12734,7 +12452,7 @@ If chars is given and not None, remove characters in chars instead. static PyObject * unicode_strip_impl(PyObject *self, PyObject *chars) -/*[clinic end generated code: output=ca19018454345d57 input=385289c6f423b954]*/ +/*[clinic end generated code: output=ca19018454345d57 input=8bc6353450345fbd]*/ { return do_argstrip(self, BOTHSTRIP, chars); } @@ -12778,8 +12496,8 @@ unicode_rstrip_impl(PyObject *self, PyObject *chars) } -static PyObject* -unicode_repeat(PyObject *str, Py_ssize_t len) +PyObject * +_PyUnicode_Repeat(PyObject *str, Py_ssize_t len) { PyObject *u; Py_ssize_t nchars, n; @@ -12824,7 +12542,7 @@ unicode_repeat(PyObject *str, Py_ssize_t len) else { Py_ssize_t char_size = PyUnicode_KIND(str); char *to = (char *) PyUnicode_DATA(u); - _PyBytes_Repeat(to, nchars * char_size, PyUnicode_DATA(str), + _PyBytes_RepeatBuffer(to, nchars * char_size, PyUnicode_DATA(str), PyUnicode_GET_LENGTH(str) * char_size); } @@ -12856,14 +12574,14 @@ str.replace as unicode_replace Return a copy with all occurrences of substring old replaced by new. -If the optional argument count is given, only the first count occurrences are -replaced. +If count is given, only the first count occurrences are replaced. +If count is not specified or -1, then all occurrences are replaced. [clinic start generated code]*/ static PyObject * unicode_replace_impl(PyObject *self, PyObject *old, PyObject *new, Py_ssize_t count) -/*[clinic end generated code: output=b63f1a8b5eebf448 input=3345c455d60a5499]*/ +/*[clinic end generated code: output=b63f1a8b5eebf448 input=d15a6886b05e2edc]*/ { return replace(self, old, new, count); } @@ -12876,13 +12594,14 @@ str.removeprefix as unicode_removeprefix Return a str with the given prefix string removed if present. -If the string starts with the prefix string, return string[len(prefix):]. -Otherwise, return a copy of the original string. +If the string starts with the prefix string, return +string[len(prefix):]. Otherwise, return a copy of the original +string. [clinic start generated code]*/ static PyObject * unicode_removeprefix_impl(PyObject *self, PyObject *prefix) -/*[clinic end generated code: output=f1e5945e9763bcb9 input=27ec40b99a37eb88]*/ +/*[clinic end generated code: output=f1e5945e9763bcb9 input=90d162724944bfa7]*/ { int match = tailmatch(self, prefix, 0, PY_SSIZE_T_MAX, -1); if (match == -1) { @@ -12903,14 +12622,14 @@ str.removesuffix as unicode_removesuffix Return a str with the given suffix string removed if present. -If the string ends with the suffix string and that suffix is not empty, -return string[:-len(suffix)]. Otherwise, return a copy of the original -string. +If the string ends with the suffix string and that suffix is not +empty, return string[:-len(suffix)]. Otherwise, return a copy of +the original string. [clinic start generated code]*/ static PyObject * unicode_removesuffix_impl(PyObject *self, PyObject *suffix) -/*[clinic end generated code: output=d36629e227636822 input=12cc32561e769be4]*/ +/*[clinic end generated code: output=d36629e227636822 input=6efc96152d4bfcd5]*/ { int match = tailmatch(self, suffix, 0, PY_SSIZE_T_MAX, +1); if (match == -1) { @@ -13015,18 +12734,19 @@ unicode_repr(PyObject *unicode) } /*[clinic input] +@permit_long_summary str.rfind as unicode_rfind = str.count Return the highest index in S where substring sub is found, such that sub is contained within S[start:end]. -Optional arguments start and end are interpreted as in slice notation. -Return -1 on failure. +Optional arguments start and end are interpreted as in slice +notation. Return -1 on failure. [clinic start generated code]*/ static Py_ssize_t unicode_rfind_impl(PyObject *str, PyObject *substr, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=880b29f01dd014c8 input=898361fb71f59294]*/ +/*[clinic end generated code: output=880b29f01dd014c8 input=2e67789533baf2f5]*/ { Py_ssize_t result = any_find_slice(str, substr, start, end, -1); if (result < 0) { @@ -13036,18 +12756,19 @@ unicode_rfind_impl(PyObject *str, PyObject *substr, Py_ssize_t start, } /*[clinic input] +@permit_long_summary str.rindex as unicode_rindex = str.count Return the highest index in S where substring sub is found, such that sub is contained within S[start:end]. -Optional arguments start and end are interpreted as in slice notation. -Raises ValueError when the substring is not found. +Optional arguments start and end are interpreted as in slice +notation. Raises ValueError when the substring is not found. [clinic start generated code]*/ static Py_ssize_t unicode_rindex_impl(PyObject *str, PyObject *substr, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=5f3aef124c867fe1 input=35943dead6c1ea9d]*/ +/*[clinic end generated code: output=5f3aef124c867fe1 input=e29d446c8234c9d9]*/ { Py_ssize_t result = any_find_slice(str, substr, start, end, -1); if (result == -1) { @@ -13068,12 +12789,13 @@ str.rjust as unicode_rjust Return a right-justified string of length width. -Padding is done using the specified fill character (default is a space). +Padding is done using the specified fill character (default is +a space). [clinic start generated code]*/ static PyObject * unicode_rjust_impl(PyObject *self, Py_ssize_t width, Py_UCS4 fillchar) -/*[clinic end generated code: output=804a1a57fbe8d5cf input=d05f550b5beb1f72]*/ +/*[clinic end generated code: output=804a1a57fbe8d5cf input=1256a8d659589907]*/ { if (PyUnicode_GET_LENGTH(self) >= width) return unicode_result_unchanged(self); @@ -13091,14 +12813,15 @@ PyUnicode_Split(PyObject *s, PyObject *sep, Py_ssize_t maxsplit) } /*[clinic input] +@permit_long_summary str.split as unicode_split sep: object = None The separator used to split the string. - When set to None (the default value), will split on any whitespace - character (including \n \r \t \f and spaces) and will discard - empty strings from the result. + When set to None (the default value), will split on any + whitespace character (including \n \r \t \f and spaces) and + will discard empty strings from the result. maxsplit: Py_ssize_t = -1 Maximum number of splits. -1 (the default value) means no limit. @@ -13107,15 +12830,15 @@ Return a list of the substrings in the string, using sep as the separator string Splitting starts at the front of the string and works to the end. -Note, str.split() is mainly useful for data that has been intentionally -delimited. With natural text that includes punctuation, consider using -the regular expression module. +Note, str.split() is mainly useful for data that has been +intentionally delimited. With natural text that includes +punctuation, consider using the regular expression module. [clinic start generated code]*/ static PyObject * unicode_split_impl(PyObject *self, PyObject *sep, Py_ssize_t maxsplit) -/*[clinic end generated code: output=3a65b1db356948dc input=a29bcc0c7a5af0eb]*/ +/*[clinic end generated code: output=3a65b1db356948dc input=288cfd6bc8828f5a]*/ { if (sep == Py_None) return split(self, NULL, maxsplit); @@ -13144,7 +12867,7 @@ PyUnicode_Partition(PyObject *str_obj, PyObject *sep_obj) len1 = PyUnicode_GET_LENGTH(str_obj); len2 = PyUnicode_GET_LENGTH(sep_obj); if (kind1 < kind2 || len1 < len2) { - PyObject *empty = unicode_get_empty(); // Borrowed reference + PyObject *empty = _PyUnicode_GetEmpty(); // Borrowed reference return PyTuple_Pack(3, str_obj, empty, empty); } buf1 = PyUnicode_DATA(str_obj); @@ -13196,7 +12919,7 @@ PyUnicode_RPartition(PyObject *str_obj, PyObject *sep_obj) len1 = PyUnicode_GET_LENGTH(str_obj); len2 = PyUnicode_GET_LENGTH(sep_obj); if (kind1 < kind2 || len1 < len2) { - PyObject *empty = unicode_get_empty(); // Borrowed reference + PyObject *empty = _PyUnicode_GetEmpty(); // Borrowed reference return PyTuple_Pack(3, empty, empty, str_obj); } buf1 = PyUnicode_DATA(str_obj); @@ -13239,17 +12962,17 @@ str.partition as unicode_partition Partition the string into three parts using the given separator. -This will search for the separator in the string. If the separator is found, -returns a 3-tuple containing the part before the separator, the separator -itself, and the part after it. +This will search for the separator in the string. If the separator +is found, returns a 3-tuple containing the part before the +separator, the separator itself, and the part after it. -If the separator is not found, returns a 3-tuple containing the original string -and two empty strings. +If the separator is not found, returns a 3-tuple containing +the original string and two empty strings. [clinic start generated code]*/ static PyObject * unicode_partition(PyObject *self, PyObject *sep) -/*[clinic end generated code: output=e4ced7bd253ca3c4 input=f29b8d06c63e50be]*/ +/*[clinic end generated code: output=e4ced7bd253ca3c4 input=e45faa8c26270cb1]*/ { return PyUnicode_Partition(self, sep); } @@ -13259,17 +12982,18 @@ str.rpartition as unicode_rpartition = str.partition Partition the string into three parts using the given separator. -This will search for the separator in the string, starting at the end. If -the separator is found, returns a 3-tuple containing the part before the -separator, the separator itself, and the part after it. +This will search for the separator in the string, starting at the +end. If the separator is found, returns a 3-tuple containing the +part before the separator, the separator itself, and the part after +it. -If the separator is not found, returns a 3-tuple containing two empty strings -and the original string. +If the separator is not found, returns a 3-tuple containing two +empty strings and the original string. [clinic start generated code]*/ static PyObject * unicode_rpartition(PyObject *self, PyObject *sep) -/*[clinic end generated code: output=1aa13cf1156572aa input=c4b7db3ef5cf336a]*/ +/*[clinic end generated code: output=1aa13cf1156572aa input=53a7f8cb19975b7c]*/ { return PyUnicode_RPartition(self, sep); } @@ -13284,6 +13008,7 @@ PyUnicode_RSplit(PyObject *s, PyObject *sep, Py_ssize_t maxsplit) } /*[clinic input] +@permit_long_summary str.rsplit as unicode_rsplit = str.split Return a list of the substrings in the string, using sep as the separator string. @@ -13293,7 +13018,7 @@ Splitting starts at the end of the string and works to the front. static PyObject * unicode_rsplit_impl(PyObject *self, PyObject *sep, Py_ssize_t maxsplit) -/*[clinic end generated code: output=c2b815c63bcabffc input=ea78406060fce33c]*/ +/*[clinic end generated code: output=c2b815c63bcabffc input=0f762e30d267fa83]*/ { if (sep == Py_None) return rsplit(self, NULL, maxsplit); @@ -13307,19 +13032,20 @@ unicode_rsplit_impl(PyObject *self, PyObject *sep, Py_ssize_t maxsplit) } /*[clinic input] +@permit_long_summary str.splitlines as unicode_splitlines keepends: bool = False Return a list of the lines in the string, breaking at line boundaries. -Line breaks are not included in the resulting list unless keepends is given and -true. +Line breaks are not included in the resulting list unless keepends +is given and true. [clinic start generated code]*/ static PyObject * unicode_splitlines_impl(PyObject *self, int keepends) -/*[clinic end generated code: output=f664dcdad153ec40 input=ba6ad05ee85d2b55]*/ +/*[clinic end generated code: output=f664dcdad153ec40 input=b45ea0f87645a06d]*/ { return PyUnicode_Splitlines(self, keepends); } @@ -13331,6 +13057,7 @@ PyObject *unicode_str(PyObject *self) } /*[clinic input] +@permit_long_summary str.swapcase as unicode_swapcase Convert uppercase characters to lowercase and lowercase characters to uppercase. @@ -13338,11 +13065,50 @@ Convert uppercase characters to lowercase and lowercase characters to uppercase. static PyObject * unicode_swapcase_impl(PyObject *self) -/*[clinic end generated code: output=5d28966bf6d7b2af input=3f3ef96d5798a7bb]*/ +/*[clinic end generated code: output=5d28966bf6d7b2af input=85bc39a9b4e8ee91]*/ { return case_operation(self, do_swapcase); } +static int +unicode_maketrans_from_dict(PyObject *x, PyObject *newdict) +{ + PyObject *key, *value; + Py_ssize_t i = 0; + int res; + while (PyDict_Next(x, &i, &key, &value)) { + if (PyUnicode_Check(key)) { + PyObject *newkey; + int kind; + const void *data; + if (PyUnicode_GET_LENGTH(key) != 1) { + PyErr_SetString(PyExc_ValueError, "string keys in translate" + "table must be of length 1"); + return -1; + } + kind = PyUnicode_KIND(key); + data = PyUnicode_DATA(key); + newkey = PyLong_FromLong(PyUnicode_READ(kind, data, 0)); + if (!newkey) + return -1; + res = PyDict_SetItem(newdict, newkey, value); + Py_DECREF(newkey); + if (res < 0) + return -1; + } + else if (PyLong_Check(key)) { + if (PyDict_SetItem(newdict, key, value) < 0) + return -1; + } + else { + PyErr_SetString(PyExc_TypeError, "keys in translate table must" + "be strings or integers"); + return -1; + } + } + return 0; +} + /*[clinic input] @staticmethod @@ -13358,18 +13124,19 @@ str.maketrans as unicode_maketrans Return a translation table usable for str.translate(). -If there is only one argument, it must be a dictionary mapping Unicode -ordinals (integers) or characters to Unicode ordinals, strings or None. -Character keys will be then converted to ordinals. -If there are two arguments, they must be strings of equal length, and -in the resulting dictionary, each character in x will be mapped to the -character at the same position in y. If there is a third argument, it -must be a string, whose characters will be mapped to None in the result. +If there is only one argument, it must be a dictionary mapping +Unicode ordinals (integers) or characters to Unicode ordinals, +strings or None. Character keys will be then converted to ordinals. +If there are two arguments, they must be strings of equal length, +and in the resulting dictionary, each character in x will be mapped +to the character at the same position in y. If there is a third +argument, it must be a string, whose characters will be mapped to +None in the result. [clinic start generated code]*/ static PyObject * unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z) -/*[clinic end generated code: output=a925c89452bd5881 input=7bfbf529a293c6c5]*/ +/*[clinic end generated code: output=a925c89452bd5881 input=66bc00a1b4258a6e]*/ { PyObject *new = NULL, *key, *value; Py_ssize_t i = 0; @@ -13428,44 +13195,19 @@ unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z) } } } else { - int kind; - const void *data; - /* x must be a dict */ - if (!PyDict_CheckExact(x)) { + if (!PyAnyDict_CheckExact(x)) { PyErr_SetString(PyExc_TypeError, "if you give only one argument " "to maketrans it must be a dict"); goto err; } /* copy entries into the new dict, converting string keys to int keys */ - while (PyDict_Next(x, &i, &key, &value)) { - if (PyUnicode_Check(key)) { - /* convert string keys to integer keys */ - PyObject *newkey; - if (PyUnicode_GET_LENGTH(key) != 1) { - PyErr_SetString(PyExc_ValueError, "string keys in translate " - "table must be of length 1"); - goto err; - } - kind = PyUnicode_KIND(key); - data = PyUnicode_DATA(key); - newkey = PyLong_FromLong(PyUnicode_READ(kind, data, 0)); - if (!newkey) - goto err; - res = PyDict_SetItem(new, newkey, value); - Py_DECREF(newkey); - if (res < 0) - goto err; - } else if (PyLong_Check(key)) { - /* just keep integer keys */ - if (PyDict_SetItem(new, key, value) < 0) - goto err; - } else { - PyErr_SetString(PyExc_TypeError, "keys in translate table must " - "be strings or integers"); - goto err; - } - } + int errcode; + Py_BEGIN_CRITICAL_SECTION(x); + errcode = unicode_maketrans_from_dict(x, new); + Py_END_CRITICAL_SECTION(); + if (errcode < 0) + goto err; } return new; err: @@ -13474,23 +13216,25 @@ unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z) } /*[clinic input] +@permit_long_summary str.translate as unicode_translate table: object - Translation table, which must be a mapping of Unicode ordinals to - Unicode ordinals, strings, or None. + Translation table, which must be a mapping of Unicode ordinals + to Unicode ordinals, strings, or None. / Replace each character in the string using the given translation table. -The table must implement lookup/indexing via __getitem__, for instance a -dictionary or list. If this operation raises LookupError, the character is -left untouched. Characters mapped to None are deleted. +The table must implement lookup/indexing via __getitem__, for +instance a dictionary or list. If this operation raises +LookupError, the character is left untouched. Characters mapped to +None are deleted. [clinic start generated code]*/ static PyObject * unicode_translate(PyObject *self, PyObject *table) -/*[clinic end generated code: output=3cb448ff2fd96bf3 input=6d38343db63d8eb0]*/ +/*[clinic end generated code: output=3cb448ff2fd96bf3 input=48cf0efe06bc1b75]*/ { return _PyUnicode_TranslateCharmap(self, table, "ignore"); } @@ -13511,6 +13255,7 @@ unicode_upper_impl(PyObject *self) } /*[clinic input] +@permit_long_summary str.zfill as unicode_zfill width: Py_ssize_t @@ -13523,7 +13268,7 @@ The string is never truncated. static PyObject * unicode_zfill_impl(PyObject *self, Py_ssize_t width) -/*[clinic end generated code: output=e13fb6bdf8e3b9df input=c6b2f772c6f27799]*/ +/*[clinic end generated code: output=e13fb6bdf8e3b9df input=25a4ee0ea3e58ce0]*/ { Py_ssize_t fill; PyObject *u; @@ -13556,6 +13301,7 @@ unicode_zfill_impl(PyObject *self, Py_ssize_t width) } /*[clinic input] +@permit_long_summary @text_signature "($self, prefix[, start[, end]], /)" str.startswith as unicode_startswith @@ -13573,7 +13319,7 @@ Return True if the string starts with the specified prefix, False otherwise. static PyObject * unicode_startswith_impl(PyObject *self, PyObject *subobj, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=4bd7cfd0803051d4 input=5f918b5f5f89d856]*/ +/*[clinic end generated code: output=4bd7cfd0803051d4 input=766bdbd33df251dc]*/ { if (PyTuple_Check(subobj)) { Py_ssize_t i; @@ -13612,6 +13358,7 @@ unicode_startswith_impl(PyObject *self, PyObject *subobj, Py_ssize_t start, /*[clinic input] +@permit_long_summary @text_signature "($self, suffix[, start[, end]], /)" str.endswith as unicode_endswith @@ -13629,7 +13376,7 @@ Return True if the string ends with the specified suffix, False otherwise. static PyObject * unicode_endswith_impl(PyObject *self, PyObject *subobj, Py_ssize_t start, Py_ssize_t end) -/*[clinic end generated code: output=cce6f8ceb0102ca9 input=00fbdc774a7d4d71]*/ +/*[clinic end generated code: output=cce6f8ceb0102ca9 input=b66bf6d5547ba1aa]*/ { if (PyTuple_Check(subobj)) { Py_ssize_t i; @@ -13666,1875 +13413,408 @@ unicode_endswith_impl(PyObject *self, PyObject *subobj, Py_ssize_t start, } -static inline void -_PyUnicodeWriter_Update(_PyUnicodeWriter *writer) -{ - writer->maxchar = PyUnicode_MAX_CHAR_VALUE(writer->buffer); - writer->data = PyUnicode_DATA(writer->buffer); +#include "stringlib/unicode_format.h" - if (!writer->readonly) { - writer->kind = PyUnicode_KIND(writer->buffer); - writer->size = PyUnicode_GET_LENGTH(writer->buffer); - } - else { - /* use a value smaller than PyUnicode_1BYTE_KIND() so - _PyUnicodeWriter_PrepareKind() will copy the buffer. */ - writer->kind = 0; - assert(writer->kind <= PyUnicode_1BYTE_KIND); +PyDoc_STRVAR(format__doc__, + "format($self, /, *args, **kwargs)\n\ +--\n\ +\n\ +Return a formatted version of the string, using substitutions from args and kwargs.\n\ +The substitutions are identified by braces ('{' and '}')."); - /* Copy-on-write mode: set buffer size to 0 so - * _PyUnicodeWriter_Prepare() will copy (and enlarge) the buffer on - * next write. */ - writer->size = 0; - } -} +PyDoc_STRVAR(format_map__doc__, + "format_map($self, mapping, /)\n\ +--\n\ +\n\ +Return a formatted version of the string, using substitutions from mapping.\n\ +The substitutions are identified by braces ('{' and '}')."); +/*[clinic input] +@permit_long_summary +str.__format__ as unicode___format__ -void -_PyUnicodeWriter_Init(_PyUnicodeWriter *writer) -{ - memset(writer, 0, sizeof(*writer)); + format_spec: unicode + / - /* ASCII is the bare minimum */ - writer->min_char = 127; +Return a formatted version of the string as described by format_spec. +[clinic start generated code]*/ - /* use a kind value smaller than PyUnicode_1BYTE_KIND so - _PyUnicodeWriter_PrepareKind() will copy the buffer. */ - assert(writer->kind == 0); - assert(writer->kind < PyUnicode_1BYTE_KIND); -} - - -PyUnicodeWriter* -PyUnicodeWriter_Create(Py_ssize_t length) +static PyObject * +unicode___format___impl(PyObject *self, PyObject *format_spec) +/*[clinic end generated code: output=45fceaca6d2ba4c8 input=77a2a19f3f7969f2]*/ { - if (length < 0) { - PyErr_SetString(PyExc_ValueError, - "length must be positive"); - return NULL; - } - - const size_t size = sizeof(_PyUnicodeWriter); - PyUnicodeWriter *pub_writer; - pub_writer = _Py_FREELIST_POP_MEM(unicode_writers); - if (pub_writer == NULL) { - pub_writer = (PyUnicodeWriter *)PyMem_Malloc(size); - if (pub_writer == NULL) { - return (PyUnicodeWriter *)PyErr_NoMemory(); - } - } - _PyUnicodeWriter *writer = (_PyUnicodeWriter *)pub_writer; + _PyUnicodeWriter writer; + int ret; - _PyUnicodeWriter_Init(writer); - if (_PyUnicodeWriter_Prepare(writer, length, 127) < 0) { - PyUnicodeWriter_Discard(pub_writer); + _PyUnicodeWriter_Init(&writer); + ret = _PyUnicode_FormatAdvancedWriter(&writer, + self, format_spec, 0, + PyUnicode_GET_LENGTH(format_spec)); + if (ret == -1) { + _PyUnicodeWriter_Dealloc(&writer); return NULL; } - writer->overallocate = 1; - - return pub_writer; -} - - -void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) -{ - if (writer == NULL) { - return; - } - _PyUnicodeWriter_Dealloc((_PyUnicodeWriter*)writer); - _Py_FREELIST_FREE(unicode_writers, writer, PyMem_Free); + return _PyUnicodeWriter_Finish(&writer); } +/*[clinic input] +str.__sizeof__ as unicode_sizeof -// Initialize _PyUnicodeWriter with initial buffer -static inline void -_PyUnicodeWriter_InitWithBuffer(_PyUnicodeWriter *writer, PyObject *buffer) -{ - memset(writer, 0, sizeof(*writer)); - writer->buffer = buffer; - _PyUnicodeWriter_Update(writer); - writer->min_length = writer->size; -} - +Return the size of the string in memory, in bytes. +[clinic start generated code]*/ -int -_PyUnicodeWriter_PrepareInternal(_PyUnicodeWriter *writer, - Py_ssize_t length, Py_UCS4 maxchar) +static PyObject * +unicode_sizeof_impl(PyObject *self) +/*[clinic end generated code: output=6dbc2f5a408b6d4f input=6dd011c108e33fb0]*/ { - Py_ssize_t newlen; - PyObject *newbuffer; - - assert(length >= 0); - assert(maxchar <= MAX_UNICODE); - - /* ensure that the _PyUnicodeWriter_Prepare macro was used */ - assert((maxchar > writer->maxchar && length >= 0) - || length > 0); - - if (length > PY_SSIZE_T_MAX - writer->pos) { - PyErr_NoMemory(); - return -1; - } - newlen = writer->pos + length; - - maxchar = Py_MAX(maxchar, writer->min_char); - - if (writer->buffer == NULL) { - assert(!writer->readonly); - if (writer->overallocate - && newlen <= (PY_SSIZE_T_MAX - newlen / OVERALLOCATE_FACTOR)) { - /* overallocate to limit the number of realloc() */ - newlen += newlen / OVERALLOCATE_FACTOR; - } - if (newlen < writer->min_length) - newlen = writer->min_length; + Py_ssize_t size; - writer->buffer = PyUnicode_New(newlen, maxchar); - if (writer->buffer == NULL) - return -1; + /* If it's a compact object, account for base structure + + character data. */ + if (PyUnicode_IS_COMPACT_ASCII(self)) { + size = sizeof(PyASCIIObject) + PyUnicode_GET_LENGTH(self) + 1; } - else if (newlen > writer->size) { - if (writer->overallocate - && newlen <= (PY_SSIZE_T_MAX - newlen / OVERALLOCATE_FACTOR)) { - /* overallocate to limit the number of realloc() */ - newlen += newlen / OVERALLOCATE_FACTOR; - } - if (newlen < writer->min_length) - newlen = writer->min_length; - - if (maxchar > writer->maxchar || writer->readonly) { - /* resize + widen */ - maxchar = Py_MAX(maxchar, writer->maxchar); - newbuffer = PyUnicode_New(newlen, maxchar); - if (newbuffer == NULL) - return -1; - _PyUnicode_FastCopyCharacters(newbuffer, 0, - writer->buffer, 0, writer->pos); - Py_DECREF(writer->buffer); - writer->readonly = 0; - } - else { - newbuffer = resize_compact(writer->buffer, newlen); - if (newbuffer == NULL) - return -1; - } - writer->buffer = newbuffer; + else if (PyUnicode_IS_COMPACT(self)) { + size = sizeof(PyCompactUnicodeObject) + + (PyUnicode_GET_LENGTH(self) + 1) * PyUnicode_KIND(self); } - else if (maxchar > writer->maxchar) { - assert(!writer->readonly); - newbuffer = PyUnicode_New(writer->size, maxchar); - if (newbuffer == NULL) - return -1; - _PyUnicode_FastCopyCharacters(newbuffer, 0, - writer->buffer, 0, writer->pos); - Py_SETREF(writer->buffer, newbuffer); + else { + /* If it is a two-block object, account for base object, and + for character block if present. */ + size = sizeof(PyUnicodeObject); + if (_PyUnicode_DATA_ANY(self)) + size += (PyUnicode_GET_LENGTH(self) + 1) * + PyUnicode_KIND(self); } - _PyUnicodeWriter_Update(writer); - return 0; + if (_PyUnicode_HAS_UTF8_MEMORY(self)) + size += PyUnicode_UTF8_LENGTH(self) + 1; -#undef OVERALLOCATE_FACTOR + return PyLong_FromSsize_t(size); } -int -_PyUnicodeWriter_PrepareKindInternal(_PyUnicodeWriter *writer, - int kind) +static PyObject * +unicode_getnewargs(PyObject *v, PyObject *Py_UNUSED(ignored)) { - Py_UCS4 maxchar; - - /* ensure that the _PyUnicodeWriter_PrepareKind macro was used */ - assert(writer->kind < kind); - - switch (kind) - { - case PyUnicode_1BYTE_KIND: maxchar = 0xff; break; - case PyUnicode_2BYTE_KIND: maxchar = 0xffff; break; - case PyUnicode_4BYTE_KIND: maxchar = MAX_UNICODE; break; - default: - Py_UNREACHABLE(); - } - - return _PyUnicodeWriter_PrepareInternal(writer, 0, maxchar); + PyObject *copy = _PyUnicode_Copy(v); + if (!copy) + return NULL; + return Py_BuildValue("(N)", copy); } -static inline int -_PyUnicodeWriter_WriteCharInline(_PyUnicodeWriter *writer, Py_UCS4 ch) +/* +This function searchs the longest common leading whitespace +of all lines in the [src, end). +It returns the length of the common leading whitespace and sets `output` to +point to the beginning of the common leading whitespace if length > 0. +*/ +static Py_ssize_t +search_longest_common_leading_whitespace( + const char *const src, + const char *const end, + const char **output) { - assert(ch <= MAX_UNICODE); - if (_PyUnicodeWriter_Prepare(writer, 1, ch) < 0) - return -1; - PyUnicode_WRITE(writer->kind, writer->data, writer->pos, ch); - writer->pos++; - return 0; -} + // [_start, _start + _len) + // describes the current longest common leading whitespace + const char *_start = NULL; + Py_ssize_t _len = 0; -int -_PyUnicodeWriter_WriteChar(_PyUnicodeWriter *writer, Py_UCS4 ch) -{ - return _PyUnicodeWriter_WriteCharInline(writer, ch); -} + for (const char *iter = src; iter < end; ++iter) { + const char *line_start = iter; + const char *leading_whitespace_end = NULL; -int -PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) -{ - if (ch > MAX_UNICODE) { - PyErr_SetString(PyExc_ValueError, - "character must be in range(0x110000)"); - return -1; - } + // scan the whole line + while (iter < end && *iter != '\n') { + if (!leading_whitespace_end && *iter != ' ' && *iter != '\t') { + /* `iter` points to the first non-whitespace character + in this line */ + if (iter == line_start) { + // some line has no indent, fast exit! + return 0; + } + leading_whitespace_end = iter; + } + ++iter; + } - return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter*)writer, ch); -} + // if this line has all white space, skip it + if (!leading_whitespace_end) { + continue; + } -int -_PyUnicodeWriter_WriteStr(_PyUnicodeWriter *writer, PyObject *str) -{ - assert(PyUnicode_Check(str)); + if (!_start) { + // update the first leading whitespace + _start = line_start; + _len = leading_whitespace_end - line_start; + assert(_len > 0); + } + else { + /* We then compare with the current longest leading whitespace. - Py_UCS4 maxchar; - Py_ssize_t len; + [line_start, leading_whitespace_end) is the leading + whitespace of this line, - len = PyUnicode_GET_LENGTH(str); - if (len == 0) - return 0; - maxchar = PyUnicode_MAX_CHAR_VALUE(str); - if (maxchar > writer->maxchar || len > writer->size - writer->pos) { - if (writer->buffer == NULL && !writer->overallocate) { - assert(_PyUnicode_CheckConsistency(str, 1)); - writer->readonly = 1; - writer->buffer = Py_NewRef(str); - _PyUnicodeWriter_Update(writer); - writer->pos += len; - return 0; - } - if (_PyUnicodeWriter_PrepareInternal(writer, len, maxchar) == -1) - return -1; - } - _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, - str, 0, len); - writer->pos += len; - return 0; -} + [_start, _start + _len) is the leading whitespace of the + current longest leading whitespace. */ + Py_ssize_t new_len = 0; + const char *_iter = _start, *line_iter = line_start; -int -PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) -{ - PyTypeObject *type = Py_TYPE(obj); - if (type == &PyUnicode_Type) { - return _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, obj); - } + while (_iter < _start + _len && line_iter < leading_whitespace_end + && *_iter == *line_iter) + { + ++_iter; + ++line_iter; + ++new_len; + } - if (type == &PyLong_Type) { - return _PyLong_FormatWriter((_PyUnicodeWriter*)writer, obj, 10, 0); + _len = new_len; + if (_len == 0) { + // No common things now, fast exit! + return 0; + } + } } - PyObject *str = PyObject_Str(obj); - if (str == NULL) { - return -1; + assert(_len >= 0); + if (_len > 0) { + *output = _start; } - - int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); - Py_DECREF(str); - return res; + return _len; } - -int -PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) +/* Dedent a string. + Intended to dedent Python source. Unlike `textwrap.dedent`, this + only supports spaces and tabs and doesn't normalize empty lines. + Return a new reference on success, NULL with exception set on error. + */ +PyObject * +_PyUnicode_Dedent(PyObject *unicode) { - if (Py_TYPE(obj) == &PyLong_Type) { - return _PyLong_FormatWriter((_PyUnicodeWriter*)writer, obj, 10, 0); + Py_ssize_t src_len = 0; + const char *src = PyUnicode_AsUTF8AndSize(unicode, &src_len); + if (!src) { + return NULL; } - - PyObject *repr = PyObject_Repr(obj); - if (repr == NULL) { - return -1; + assert(src_len >= 0); + if (src_len == 0) { + return Py_NewRef(unicode); } - int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, repr); - Py_DECREF(repr); - return res; -} - - -int -_PyUnicodeWriter_WriteSubstring(_PyUnicodeWriter *writer, PyObject *str, - Py_ssize_t start, Py_ssize_t end) -{ - assert(0 <= start); - assert(end <= PyUnicode_GET_LENGTH(str)); - assert(start <= end); + const char *const end = src + src_len; - if (start == 0 && end == PyUnicode_GET_LENGTH(str)) - return _PyUnicodeWriter_WriteStr(writer, str); + // [whitespace_start, whitespace_start + whitespace_len) + // describes the current longest common leading whitespace + const char *whitespace_start = NULL; + Py_ssize_t whitespace_len = search_longest_common_leading_whitespace( + src, end, &whitespace_start); - Py_ssize_t len = end - start; - if (len == 0) { - return 0; + if (whitespace_len == 0) { + return Py_NewRef(unicode); } - Py_UCS4 maxchar; - if (PyUnicode_MAX_CHAR_VALUE(str) > writer->maxchar) { - maxchar = _PyUnicode_FindMaxChar(str, start, end); - } - else { - maxchar = writer->maxchar; - } - if (_PyUnicodeWriter_Prepare(writer, len, maxchar) < 0) { - return -1; + // now we should trigger a dedent + char *dest = PyMem_Malloc(src_len); + if (!dest) { + PyErr_NoMemory(); + return NULL; } + char *dest_iter = dest; - _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, - str, start, len); - writer->pos += len; - return 0; -} - + for (const char *iter = src; iter < end; ++iter) { + const char *line_start = iter; + bool in_leading_space = true; -int -PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, - Py_ssize_t start, Py_ssize_t end) -{ - if (!PyUnicode_Check(str)) { - PyErr_Format(PyExc_TypeError, "expect str, not %T", str); - return -1; - } - if (start < 0 || start > end) { - PyErr_Format(PyExc_ValueError, "invalid start argument"); - return -1; - } - if (end > PyUnicode_GET_LENGTH(str)) { - PyErr_Format(PyExc_ValueError, "invalid end argument"); - return -1; - } - - return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter*)writer, str, - start, end); -} - - -int -_PyUnicodeWriter_WriteASCIIString(_PyUnicodeWriter *writer, - const char *ascii, Py_ssize_t len) -{ - if (len == -1) - len = strlen(ascii); - - assert(ucs1lib_find_max_char((const Py_UCS1*)ascii, (const Py_UCS1*)ascii + len) < 128); - - if (writer->buffer == NULL && !writer->overallocate) { - PyObject *str; - - str = _PyUnicode_FromASCII(ascii, len); - if (str == NULL) - return -1; - - writer->readonly = 1; - writer->buffer = str; - _PyUnicodeWriter_Update(writer); - writer->pos += len; - return 0; - } - - if (_PyUnicodeWriter_Prepare(writer, len, 127) == -1) - return -1; - - switch (writer->kind) - { - case PyUnicode_1BYTE_KIND: - { - const Py_UCS1 *str = (const Py_UCS1 *)ascii; - Py_UCS1 *data = writer->data; - - memcpy(data + writer->pos, str, len); - break; - } - case PyUnicode_2BYTE_KIND: - { - _PyUnicode_CONVERT_BYTES( - Py_UCS1, Py_UCS2, - ascii, ascii + len, - (Py_UCS2 *)writer->data + writer->pos); - break; - } - case PyUnicode_4BYTE_KIND: - { - _PyUnicode_CONVERT_BYTES( - Py_UCS1, Py_UCS4, - ascii, ascii + len, - (Py_UCS4 *)writer->data + writer->pos); - break; - } - default: - Py_UNREACHABLE(); - } - - writer->pos += len; - return 0; -} - - -int -PyUnicodeWriter_WriteASCII(PyUnicodeWriter *writer, - const char *str, - Py_ssize_t size) -{ - assert(writer != NULL); - _Py_AssertHoldsTstate(); - - _PyUnicodeWriter *priv_writer = (_PyUnicodeWriter*)writer; - return _PyUnicodeWriter_WriteASCIIString(priv_writer, str, size); -} - - -int -PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, - const char *str, - Py_ssize_t size) -{ - if (size < 0) { - size = strlen(str); - } - - _PyUnicodeWriter *_writer = (_PyUnicodeWriter*)writer; - Py_ssize_t old_pos = _writer->pos; - int res = unicode_decode_utf8_writer(_writer, str, size, - _Py_ERROR_STRICT, NULL, NULL); - if (res < 0) { - _writer->pos = old_pos; - } - return res; -} - - -int -PyUnicodeWriter_DecodeUTF8Stateful(PyUnicodeWriter *writer, - const char *string, - Py_ssize_t length, - const char *errors, - Py_ssize_t *consumed) -{ - if (length < 0) { - length = strlen(string); - } - - _PyUnicodeWriter *_writer = (_PyUnicodeWriter*)writer; - Py_ssize_t old_pos = _writer->pos; - int res = unicode_decode_utf8_writer(_writer, string, length, - _Py_ERROR_UNKNOWN, errors, consumed); - if (res < 0) { - _writer->pos = old_pos; - if (consumed) { - *consumed = 0; - } - } - return res; -} - - -int -_PyUnicodeWriter_WriteLatin1String(_PyUnicodeWriter *writer, - const char *str, Py_ssize_t len) -{ - Py_UCS4 maxchar; - - maxchar = ucs1lib_find_max_char((const Py_UCS1*)str, (const Py_UCS1*)str + len); - if (_PyUnicodeWriter_Prepare(writer, len, maxchar) == -1) - return -1; - unicode_write_cstr(writer->buffer, writer->pos, str, len); - writer->pos += len; - return 0; -} - -PyObject * -_PyUnicodeWriter_Finish(_PyUnicodeWriter *writer) -{ - PyObject *str; - - if (writer->pos == 0) { - Py_CLEAR(writer->buffer); - _Py_RETURN_UNICODE_EMPTY(); - } - - str = writer->buffer; - writer->buffer = NULL; - - if (writer->readonly) { - assert(PyUnicode_GET_LENGTH(str) == writer->pos); - return str; - } - - if (PyUnicode_GET_LENGTH(str) != writer->pos) { - PyObject *str2; - str2 = resize_compact(str, writer->pos); - if (str2 == NULL) { - Py_DECREF(str); - return NULL; - } - str = str2; - } - - assert(_PyUnicode_CheckConsistency(str, 1)); - return unicode_result(str); -} - - -PyObject* -PyUnicodeWriter_Finish(PyUnicodeWriter *writer) -{ - PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter*)writer); - assert(((_PyUnicodeWriter*)writer)->buffer == NULL); - _Py_FREELIST_FREE(unicode_writers, writer, PyMem_Free); - return str; -} - - -void -_PyUnicodeWriter_Dealloc(_PyUnicodeWriter *writer) -{ - Py_CLEAR(writer->buffer); -} - -#include "stringlib/unicode_format.h" - -PyDoc_STRVAR(format__doc__, - "format($self, /, *args, **kwargs)\n\ ---\n\ -\n\ -Return a formatted version of the string, using substitutions from args and kwargs.\n\ -The substitutions are identified by braces ('{' and '}')."); - -PyDoc_STRVAR(format_map__doc__, - "format_map($self, mapping, /)\n\ ---\n\ -\n\ -Return a formatted version of the string, using substitutions from mapping.\n\ -The substitutions are identified by braces ('{' and '}')."); - -/*[clinic input] -str.__format__ as unicode___format__ - - format_spec: unicode - / - -Return a formatted version of the string as described by format_spec. -[clinic start generated code]*/ - -static PyObject * -unicode___format___impl(PyObject *self, PyObject *format_spec) -/*[clinic end generated code: output=45fceaca6d2ba4c8 input=5e135645d167a214]*/ -{ - _PyUnicodeWriter writer; - int ret; - - _PyUnicodeWriter_Init(&writer); - ret = _PyUnicode_FormatAdvancedWriter(&writer, - self, format_spec, 0, - PyUnicode_GET_LENGTH(format_spec)); - if (ret == -1) { - _PyUnicodeWriter_Dealloc(&writer); - return NULL; - } - return _PyUnicodeWriter_Finish(&writer); -} - -/*[clinic input] -str.__sizeof__ as unicode_sizeof - -Return the size of the string in memory, in bytes. -[clinic start generated code]*/ - -static PyObject * -unicode_sizeof_impl(PyObject *self) -/*[clinic end generated code: output=6dbc2f5a408b6d4f input=6dd011c108e33fb0]*/ -{ - Py_ssize_t size; - - /* If it's a compact object, account for base structure + - character data. */ - if (PyUnicode_IS_COMPACT_ASCII(self)) { - size = sizeof(PyASCIIObject) + PyUnicode_GET_LENGTH(self) + 1; - } - else if (PyUnicode_IS_COMPACT(self)) { - size = sizeof(PyCompactUnicodeObject) + - (PyUnicode_GET_LENGTH(self) + 1) * PyUnicode_KIND(self); - } - else { - /* If it is a two-block object, account for base object, and - for character block if present. */ - size = sizeof(PyUnicodeObject); - if (_PyUnicode_DATA_ANY(self)) - size += (PyUnicode_GET_LENGTH(self) + 1) * - PyUnicode_KIND(self); - } - if (_PyUnicode_HAS_UTF8_MEMORY(self)) - size += PyUnicode_UTF8_LENGTH(self) + 1; - - return PyLong_FromSsize_t(size); -} - -static PyObject * -unicode_getnewargs(PyObject *v, PyObject *Py_UNUSED(ignored)) -{ - PyObject *copy = _PyUnicode_Copy(v); - if (!copy) - return NULL; - return Py_BuildValue("(N)", copy); -} - -/* -This function searchs the longest common leading whitespace -of all lines in the [src, end). -It returns the length of the common leading whitespace and sets `output` to -point to the beginning of the common leading whitespace if length > 0. -*/ -static Py_ssize_t -search_longest_common_leading_whitespace( - const char *const src, - const char *const end, - const char **output) -{ - // [_start, _start + _len) - // describes the current longest common leading whitespace - const char *_start = NULL; - Py_ssize_t _len = 0; - - for (const char *iter = src; iter < end; ++iter) { - const char *line_start = iter; - const char *leading_whitespace_end = NULL; - - // scan the whole line - while (iter < end && *iter != '\n') { - if (!leading_whitespace_end && *iter != ' ' && *iter != '\t') { - /* `iter` points to the first non-whitespace character - in this line */ - if (iter == line_start) { - // some line has no indent, fast exit! - return 0; - } - leading_whitespace_end = iter; - } - ++iter; - } - - // if this line has all white space, skip it - if (!leading_whitespace_end) { - continue; - } - - if (!_start) { - // update the first leading whitespace - _start = line_start; - _len = leading_whitespace_end - line_start; - assert(_len > 0); - } - else { - /* We then compare with the current longest leading whitespace. - - [line_start, leading_whitespace_end) is the leading - whitespace of this line, - - [_start, _start + _len) is the leading whitespace of the - current longest leading whitespace. */ - Py_ssize_t new_len = 0; - const char *_iter = _start, *line_iter = line_start; - - while (_iter < _start + _len && line_iter < leading_whitespace_end - && *_iter == *line_iter) - { - ++_iter; - ++line_iter; - ++new_len; - } - - _len = new_len; - if (_len == 0) { - // No common things now, fast exit! - return 0; - } - } - } - - assert(_len >= 0); - if (_len > 0) { - *output = _start; - } - return _len; -} - -/* Dedent a string. - Behaviour is expected to be an exact match of `textwrap.dedent`. - Return a new reference on success, NULL with exception set on error. - */ -PyObject * -_PyUnicode_Dedent(PyObject *unicode) -{ - Py_ssize_t src_len = 0; - const char *src = PyUnicode_AsUTF8AndSize(unicode, &src_len); - if (!src) { - return NULL; - } - assert(src_len >= 0); - if (src_len == 0) { - return Py_NewRef(unicode); - } - - const char *const end = src + src_len; - - // [whitespace_start, whitespace_start + whitespace_len) - // describes the current longest common leading whitespace - const char *whitespace_start = NULL; - Py_ssize_t whitespace_len = search_longest_common_leading_whitespace( - src, end, &whitespace_start); - - if (whitespace_len == 0) { - return Py_NewRef(unicode); - } - - // now we should trigger a dedent - char *dest = PyMem_Malloc(src_len); - if (!dest) { - PyErr_NoMemory(); - return NULL; - } - char *dest_iter = dest; - - for (const char *iter = src; iter < end; ++iter) { - const char *line_start = iter; - bool in_leading_space = true; - - // iterate over a line to find the end of a line + // iterate over a line to find the end of a line while (iter < end && *iter != '\n') { if (in_leading_space && *iter != ' ' && *iter != '\t') { - in_leading_space = false; - } - ++iter; - } - - // invariant: *iter == '\n' or iter == end - bool append_newline = iter < end; - - // if this line has all white space, write '\n' and continue - if (in_leading_space && append_newline) { - *dest_iter++ = '\n'; - continue; - } - - /* copy [new_line_start + whitespace_len, iter) to buffer, then - conditionally append '\n' */ - - Py_ssize_t new_line_len = iter - line_start - whitespace_len; - assert(new_line_len >= 0); - memcpy(dest_iter, line_start + whitespace_len, new_line_len); - - dest_iter += new_line_len; - - if (append_newline) { - *dest_iter++ = '\n'; - } - } - - PyObject *res = PyUnicode_FromStringAndSize(dest, dest_iter - dest); - PyMem_Free(dest); - return res; -} - -static PyMethodDef unicode_methods[] = { - UNICODE_ENCODE_METHODDEF - UNICODE_REPLACE_METHODDEF - UNICODE_SPLIT_METHODDEF - UNICODE_RSPLIT_METHODDEF - UNICODE_JOIN_METHODDEF - UNICODE_CAPITALIZE_METHODDEF - UNICODE_CASEFOLD_METHODDEF - UNICODE_TITLE_METHODDEF - UNICODE_CENTER_METHODDEF - UNICODE_COUNT_METHODDEF - UNICODE_EXPANDTABS_METHODDEF - UNICODE_FIND_METHODDEF - UNICODE_PARTITION_METHODDEF - UNICODE_INDEX_METHODDEF - UNICODE_LJUST_METHODDEF - UNICODE_LOWER_METHODDEF - UNICODE_LSTRIP_METHODDEF - UNICODE_RFIND_METHODDEF - UNICODE_RINDEX_METHODDEF - UNICODE_RJUST_METHODDEF - UNICODE_RSTRIP_METHODDEF - UNICODE_RPARTITION_METHODDEF - UNICODE_SPLITLINES_METHODDEF - UNICODE_STRIP_METHODDEF - UNICODE_SWAPCASE_METHODDEF - UNICODE_TRANSLATE_METHODDEF - UNICODE_UPPER_METHODDEF - UNICODE_STARTSWITH_METHODDEF - UNICODE_ENDSWITH_METHODDEF - UNICODE_REMOVEPREFIX_METHODDEF - UNICODE_REMOVESUFFIX_METHODDEF - UNICODE_ISASCII_METHODDEF - UNICODE_ISLOWER_METHODDEF - UNICODE_ISUPPER_METHODDEF - UNICODE_ISTITLE_METHODDEF - UNICODE_ISSPACE_METHODDEF - UNICODE_ISDECIMAL_METHODDEF - UNICODE_ISDIGIT_METHODDEF - UNICODE_ISNUMERIC_METHODDEF - UNICODE_ISALPHA_METHODDEF - UNICODE_ISALNUM_METHODDEF - UNICODE_ISIDENTIFIER_METHODDEF - UNICODE_ISPRINTABLE_METHODDEF - UNICODE_ZFILL_METHODDEF - {"format", _PyCFunction_CAST(do_string_format), METH_VARARGS | METH_KEYWORDS, format__doc__}, - {"format_map", do_string_format_map, METH_O, format_map__doc__}, - UNICODE___FORMAT___METHODDEF - UNICODE_MAKETRANS_METHODDEF - UNICODE_SIZEOF_METHODDEF - {"__getnewargs__", unicode_getnewargs, METH_NOARGS}, - {NULL, NULL} -}; - -static PyObject * -unicode_mod(PyObject *v, PyObject *w) -{ - if (!PyUnicode_Check(v)) - Py_RETURN_NOTIMPLEMENTED; - return PyUnicode_Format(v, w); -} - -static PyNumberMethods unicode_as_number = { - 0, /*nb_add*/ - 0, /*nb_subtract*/ - 0, /*nb_multiply*/ - unicode_mod, /*nb_remainder*/ -}; - -static PySequenceMethods unicode_as_sequence = { - unicode_length, /* sq_length */ - PyUnicode_Concat, /* sq_concat */ - unicode_repeat, /* sq_repeat */ - unicode_getitem, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - PyUnicode_Contains, /* sq_contains */ -}; - -static PyObject* -unicode_subscript(PyObject* self, PyObject* item) -{ - if (_PyIndex_Check(item)) { - Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) - return NULL; - if (i < 0) - i += PyUnicode_GET_LENGTH(self); - return unicode_getitem(self, i); - } else if (PySlice_Check(item)) { - Py_ssize_t start, stop, step, slicelength, i; - size_t cur; - PyObject *result; - const void *src_data; - void *dest_data; - int src_kind, dest_kind; - Py_UCS4 ch, max_char, kind_limit; - - if (PySlice_Unpack(item, &start, &stop, &step) < 0) { - return NULL; - } - slicelength = PySlice_AdjustIndices(PyUnicode_GET_LENGTH(self), - &start, &stop, step); - - if (slicelength <= 0) { - _Py_RETURN_UNICODE_EMPTY(); - } else if (start == 0 && step == 1 && - slicelength == PyUnicode_GET_LENGTH(self)) { - return unicode_result_unchanged(self); - } else if (step == 1) { - return PyUnicode_Substring(self, - start, start + slicelength); - } - /* General case */ - src_kind = PyUnicode_KIND(self); - src_data = PyUnicode_DATA(self); - if (!PyUnicode_IS_ASCII(self)) { - kind_limit = kind_maxchar_limit(src_kind); - max_char = 0; - for (cur = start, i = 0; i < slicelength; cur += step, i++) { - ch = PyUnicode_READ(src_kind, src_data, cur); - if (ch > max_char) { - max_char = ch; - if (max_char >= kind_limit) - break; - } - } - } - else - max_char = 127; - result = PyUnicode_New(slicelength, max_char); - if (result == NULL) - return NULL; - dest_kind = PyUnicode_KIND(result); - dest_data = PyUnicode_DATA(result); - - for (cur = start, i = 0; i < slicelength; cur += step, i++) { - Py_UCS4 ch = PyUnicode_READ(src_kind, src_data, cur); - PyUnicode_WRITE(dest_kind, dest_data, i, ch); - } - assert(_PyUnicode_CheckConsistency(result, 1)); - return result; - } else { - PyErr_Format(PyExc_TypeError, "string indices must be integers, not '%.200s'", - Py_TYPE(item)->tp_name); - return NULL; - } -} - -static PyMappingMethods unicode_as_mapping = { - unicode_length, /* mp_length */ - unicode_subscript, /* mp_subscript */ - 0, /* mp_ass_subscript */ -}; - - -/* Helpers for PyUnicode_Format() */ - -struct unicode_formatter_t { - PyObject *args; - int args_owned; - Py_ssize_t arglen, argidx; - PyObject *dict; - - int fmtkind; - Py_ssize_t fmtcnt, fmtpos; - const void *fmtdata; - PyObject *fmtstr; - - _PyUnicodeWriter writer; -}; - -struct unicode_format_arg_t { - Py_UCS4 ch; - int flags; - Py_ssize_t width; - int prec; - int sign; -}; - -static PyObject * -unicode_format_getnextarg(struct unicode_formatter_t *ctx) -{ - Py_ssize_t argidx = ctx->argidx; - - if (argidx < ctx->arglen) { - ctx->argidx++; - if (ctx->arglen < 0) - return ctx->args; - else - return PyTuple_GetItem(ctx->args, argidx); - } - PyErr_SetString(PyExc_TypeError, - "not enough arguments for format string"); - return NULL; -} - -/* Returns a new reference to a PyUnicode object, or NULL on failure. */ - -/* Format a float into the writer if the writer is not NULL, or into *p_output - otherwise. - - Return 0 on success, raise an exception and return -1 on error. */ -static int -formatfloat(PyObject *v, struct unicode_format_arg_t *arg, - PyObject **p_output, - _PyUnicodeWriter *writer) -{ - char *p; - double x; - Py_ssize_t len; - int prec; - int dtoa_flags = 0; - - x = PyFloat_AsDouble(v); - if (x == -1.0 && PyErr_Occurred()) - return -1; - - prec = arg->prec; - if (prec < 0) - prec = 6; - - if (arg->flags & F_ALT) - dtoa_flags |= Py_DTSF_ALT; - p = PyOS_double_to_string(x, arg->ch, prec, dtoa_flags, NULL); - if (p == NULL) - return -1; - len = strlen(p); - if (writer) { - if (_PyUnicodeWriter_WriteASCIIString(writer, p, len) < 0) { - PyMem_Free(p); - return -1; - } - } - else - *p_output = _PyUnicode_FromASCII(p, len); - PyMem_Free(p); - return 0; -} - -/* formatlong() emulates the format codes d, u, o, x and X, and - * the F_ALT flag, for Python's long (unbounded) ints. It's not used for - * Python's regular ints. - * Return value: a new PyUnicodeObject*, or NULL if error. - * The output string is of the form - * "-"? ("0x" | "0X")? digit+ - * "0x"/"0X" are present only for x and X conversions, with F_ALT - * set in flags. The case of hex digits will be correct, - * There will be at least prec digits, zero-filled on the left if - * necessary to get that many. - * val object to be converted - * flags bitmask of format flags; only F_ALT is looked at - * prec minimum number of digits; 0-fill on left if needed - * type a character in [duoxX]; u acts the same as d - * - * CAUTION: o, x and X conversions on regular ints can never - * produce a '-' sign, but can for Python's unbounded ints. - */ -PyObject * -_PyUnicode_FormatLong(PyObject *val, int alt, int prec, int type) -{ - PyObject *result = NULL; - char *buf; - Py_ssize_t i; - int sign; /* 1 if '-', else 0 */ - int len; /* number of characters */ - Py_ssize_t llen; - int numdigits; /* len == numnondigits + numdigits */ - int numnondigits = 0; - - /* Avoid exceeding SSIZE_T_MAX */ - if (prec > INT_MAX-3) { - PyErr_SetString(PyExc_OverflowError, - "precision too large"); - return NULL; - } - - assert(PyLong_Check(val)); - - switch (type) { - default: - Py_UNREACHABLE(); - case 'd': - case 'i': - case 'u': - /* int and int subclasses should print numerically when a numeric */ - /* format code is used (see issue18780) */ - result = PyNumber_ToBase(val, 10); - break; - case 'o': - numnondigits = 2; - result = PyNumber_ToBase(val, 8); - break; - case 'x': - case 'X': - numnondigits = 2; - result = PyNumber_ToBase(val, 16); - break; - } - if (!result) - return NULL; - - assert(unicode_modifiable(result)); - assert(PyUnicode_IS_ASCII(result)); - - /* To modify the string in-place, there can only be one reference. */ - if (!_PyObject_IsUniquelyReferenced(result)) { - Py_DECREF(result); - PyErr_BadInternalCall(); - return NULL; - } - buf = PyUnicode_DATA(result); - llen = PyUnicode_GET_LENGTH(result); - if (llen > INT_MAX) { - Py_DECREF(result); - PyErr_SetString(PyExc_ValueError, - "string too large in _PyUnicode_FormatLong"); - return NULL; - } - len = (int)llen; - sign = buf[0] == '-'; - numnondigits += sign; - numdigits = len - numnondigits; - assert(numdigits > 0); - - /* Get rid of base marker unless F_ALT */ - if (((alt) == 0 && - (type == 'o' || type == 'x' || type == 'X'))) { - assert(buf[sign] == '0'); - assert(buf[sign+1] == 'x' || buf[sign+1] == 'X' || - buf[sign+1] == 'o'); - numnondigits -= 2; - buf += 2; - len -= 2; - if (sign) - buf[0] = '-'; - assert(len == numnondigits + numdigits); - assert(numdigits > 0); - } - - /* Fill with leading zeroes to meet minimum width. */ - if (prec > numdigits) { - PyObject *r1 = PyBytes_FromStringAndSize(NULL, - numnondigits + prec); - char *b1; - if (!r1) { - Py_DECREF(result); - return NULL; - } - b1 = PyBytes_AS_STRING(r1); - for (i = 0; i < numnondigits; ++i) - *b1++ = *buf++; - for (i = 0; i < prec - numdigits; i++) - *b1++ = '0'; - for (i = 0; i < numdigits; i++) - *b1++ = *buf++; - *b1 = '\0'; - Py_SETREF(result, r1); - buf = PyBytes_AS_STRING(result); - len = numnondigits + prec; - } - - /* Fix up case for hex conversions. */ - if (type == 'X') { - /* Need to convert all lower case letters to upper case. - and need to convert 0x to 0X (and -0x to -0X). */ - for (i = 0; i < len; i++) - if (buf[i] >= 'a' && buf[i] <= 'x') - buf[i] -= 'a'-'A'; - } - if (!PyUnicode_Check(result) - || buf != PyUnicode_DATA(result)) { - PyObject *unicode; - unicode = _PyUnicode_FromASCII(buf, len); - Py_SETREF(result, unicode); - } - else if (len != PyUnicode_GET_LENGTH(result)) { - if (PyUnicode_Resize(&result, len) < 0) - Py_CLEAR(result); - } - return result; -} - -/* Format an integer or a float as an integer. - * Return 1 if the number has been formatted into the writer, - * 0 if the number has been formatted into *p_output - * -1 and raise an exception on error */ -static int -mainformatlong(PyObject *v, - struct unicode_format_arg_t *arg, - PyObject **p_output, - _PyUnicodeWriter *writer) -{ - PyObject *iobj, *res; - char type = (char)arg->ch; - - if (!PyNumber_Check(v)) - goto wrongtype; - - /* make sure number is a type of integer for o, x, and X */ - if (!PyLong_Check(v)) { - if (type == 'o' || type == 'x' || type == 'X') { - iobj = _PyNumber_Index(v); - } - else { - iobj = PyNumber_Long(v); - } - if (iobj == NULL ) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) - goto wrongtype; - return -1; - } - assert(PyLong_Check(iobj)); - } - else { - iobj = Py_NewRef(v); - } - - if (PyLong_CheckExact(v) - && arg->width == -1 && arg->prec == -1 - && !(arg->flags & (F_SIGN | F_BLANK)) - && type != 'X') - { - /* Fast path */ - int alternate = arg->flags & F_ALT; - int base; - - switch(type) - { - default: - Py_UNREACHABLE(); - case 'd': - case 'i': - case 'u': - base = 10; - break; - case 'o': - base = 8; - break; - case 'x': - case 'X': - base = 16; - break; - } - - if (_PyLong_FormatWriter(writer, v, base, alternate) == -1) { - Py_DECREF(iobj); - return -1; - } - Py_DECREF(iobj); - return 1; - } - - res = _PyUnicode_FormatLong(iobj, arg->flags & F_ALT, arg->prec, type); - Py_DECREF(iobj); - if (res == NULL) - return -1; - *p_output = res; - return 0; - -wrongtype: - switch(type) - { - case 'o': - case 'x': - case 'X': - PyErr_Format(PyExc_TypeError, - "%%%c format: an integer is required, " - "not %.200s", - type, Py_TYPE(v)->tp_name); - break; - default: - PyErr_Format(PyExc_TypeError, - "%%%c format: a real number is required, " - "not %.200s", - type, Py_TYPE(v)->tp_name); - break; - } - return -1; -} - -static Py_UCS4 -formatchar(PyObject *v) -{ - /* presume that the buffer is at least 3 characters long */ - if (PyUnicode_Check(v)) { - if (PyUnicode_GET_LENGTH(v) == 1) { - return PyUnicode_READ_CHAR(v, 0); - } - PyErr_Format(PyExc_TypeError, - "%%c requires an int or a unicode character, " - "not a string of length %zd", - PyUnicode_GET_LENGTH(v)); - return (Py_UCS4) -1; - } - else { - int overflow; - long x = PyLong_AsLongAndOverflow(v, &overflow); - if (x == -1 && PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "%%c requires an int or a unicode character, not %T", - v); - return (Py_UCS4) -1; - } - return (Py_UCS4) -1; - } - - if (x < 0 || x > MAX_UNICODE) { - /* this includes an overflow in converting to C long */ - PyErr_SetString(PyExc_OverflowError, - "%c arg not in range(0x110000)"); - return (Py_UCS4) -1; - } - - return (Py_UCS4) x; - } -} - -/* Parse options of an argument: flags, width, precision. - Handle also "%(name)" syntax. - - Return 0 if the argument has been formatted into arg->str. - Return 1 if the argument has been written into ctx->writer, - Raise an exception and return -1 on error. */ -static int -unicode_format_arg_parse(struct unicode_formatter_t *ctx, - struct unicode_format_arg_t *arg) -{ -#define FORMAT_READ(ctx) \ - PyUnicode_READ((ctx)->fmtkind, (ctx)->fmtdata, (ctx)->fmtpos) - - PyObject *v; - - if (arg->ch == '(') { - /* Get argument value from a dictionary. Example: "%(name)s". */ - Py_ssize_t keystart; - Py_ssize_t keylen; - PyObject *key; - int pcount = 1; - - if (ctx->dict == NULL) { - PyErr_SetString(PyExc_TypeError, - "format requires a mapping"); - return -1; - } - ++ctx->fmtpos; - --ctx->fmtcnt; - keystart = ctx->fmtpos; - /* Skip over balanced parentheses */ - while (pcount > 0 && --ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - if (arg->ch == ')') - --pcount; - else if (arg->ch == '(') - ++pcount; - ctx->fmtpos++; - } - keylen = ctx->fmtpos - keystart - 1; - if (ctx->fmtcnt < 0 || pcount > 0) { - PyErr_SetString(PyExc_ValueError, - "incomplete format key"); - return -1; - } - key = PyUnicode_Substring(ctx->fmtstr, - keystart, keystart + keylen); - if (key == NULL) - return -1; - if (ctx->args_owned) { - ctx->args_owned = 0; - Py_DECREF(ctx->args); - } - ctx->args = PyObject_GetItem(ctx->dict, key); - Py_DECREF(key); - if (ctx->args == NULL) - return -1; - ctx->args_owned = 1; - ctx->arglen = -1; - ctx->argidx = -2; - } - - /* Parse flags. Example: "%+i" => flags=F_SIGN. */ - while (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - switch (arg->ch) { - case '-': arg->flags |= F_LJUST; continue; - case '+': arg->flags |= F_SIGN; continue; - case ' ': arg->flags |= F_BLANK; continue; - case '#': arg->flags |= F_ALT; continue; - case '0': arg->flags |= F_ZERO; continue; - } - break; - } - - /* Parse width. Example: "%10s" => width=10 */ - if (arg->ch == '*') { - v = unicode_format_getnextarg(ctx); - if (v == NULL) - return -1; - if (!PyLong_Check(v)) { - PyErr_SetString(PyExc_TypeError, - "* wants int"); - return -1; - } - arg->width = PyLong_AsSsize_t(v); - if (arg->width == -1 && PyErr_Occurred()) - return -1; - if (arg->width < 0) { - arg->flags |= F_LJUST; - arg->width = -arg->width; - } - if (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - } - } - else if (arg->ch >= '0' && arg->ch <= '9') { - arg->width = arg->ch - '0'; - while (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - if (arg->ch < '0' || arg->ch > '9') - break; - /* Since arg->ch is unsigned, the RHS would end up as unsigned, - mixing signed and unsigned comparison. Since arg->ch is between - '0' and '9', casting to int is safe. */ - if (arg->width > (PY_SSIZE_T_MAX - ((int)arg->ch - '0')) / 10) { - PyErr_SetString(PyExc_ValueError, - "width too big"); - return -1; - } - arg->width = arg->width*10 + (arg->ch - '0'); - } - } - - /* Parse precision. Example: "%.3f" => prec=3 */ - if (arg->ch == '.') { - arg->prec = 0; - if (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - } - if (arg->ch == '*') { - v = unicode_format_getnextarg(ctx); - if (v == NULL) - return -1; - if (!PyLong_Check(v)) { - PyErr_SetString(PyExc_TypeError, - "* wants int"); - return -1; - } - arg->prec = PyLong_AsInt(v); - if (arg->prec == -1 && PyErr_Occurred()) - return -1; - if (arg->prec < 0) - arg->prec = 0; - if (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - } - } - else if (arg->ch >= '0' && arg->ch <= '9') { - arg->prec = arg->ch - '0'; - while (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - if (arg->ch < '0' || arg->ch > '9') - break; - if (arg->prec > (INT_MAX - ((int)arg->ch - '0')) / 10) { - PyErr_SetString(PyExc_ValueError, - "precision too big"); - return -1; - } - arg->prec = arg->prec*10 + (arg->ch - '0'); - } - } - } - - /* Ignore "h", "l" and "L" format prefix (ex: "%hi" or "%ls") */ - if (ctx->fmtcnt >= 0) { - if (arg->ch == 'h' || arg->ch == 'l' || arg->ch == 'L') { - if (--ctx->fmtcnt >= 0) { - arg->ch = FORMAT_READ(ctx); - ctx->fmtpos++; - } - } - } - if (ctx->fmtcnt < 0) { - PyErr_SetString(PyExc_ValueError, - "incomplete format"); - return -1; - } - return 0; - -#undef FORMAT_READ -} - -/* Format one argument. Supported conversion specifiers: - - - "s", "r", "a": any type - - "i", "d", "u": int or float - - "o", "x", "X": int - - "e", "E", "f", "F", "g", "G": float - - "c": int or str (1 character) - - When possible, the output is written directly into the Unicode writer - (ctx->writer). A string is created when padding is required. - - Return 0 if the argument has been formatted into *p_str, - 1 if the argument has been written into ctx->writer, - -1 on error. */ -static int -unicode_format_arg_format(struct unicode_formatter_t *ctx, - struct unicode_format_arg_t *arg, - PyObject **p_str) -{ - PyObject *v; - _PyUnicodeWriter *writer = &ctx->writer; - - if (ctx->fmtcnt == 0) - ctx->writer.overallocate = 0; - - v = unicode_format_getnextarg(ctx); - if (v == NULL) - return -1; - - - switch (arg->ch) { - case 's': - case 'r': - case 'a': - if (PyLong_CheckExact(v) && arg->width == -1 && arg->prec == -1) { - /* Fast path */ - if (_PyLong_FormatWriter(writer, v, 10, arg->flags & F_ALT) == -1) - return -1; - return 1; - } - - if (PyUnicode_CheckExact(v) && arg->ch == 's') { - *p_str = Py_NewRef(v); - } - else { - if (arg->ch == 's') - *p_str = PyObject_Str(v); - else if (arg->ch == 'r') - *p_str = PyObject_Repr(v); - else - *p_str = PyObject_ASCII(v); - } - break; - - case 'i': - case 'd': - case 'u': - case 'o': - case 'x': - case 'X': - { - int ret = mainformatlong(v, arg, p_str, writer); - if (ret != 0) - return ret; - arg->sign = 1; - break; - } - - case 'e': - case 'E': - case 'f': - case 'F': - case 'g': - case 'G': - if (arg->width == -1 && arg->prec == -1 - && !(arg->flags & (F_SIGN | F_BLANK))) - { - /* Fast path */ - if (formatfloat(v, arg, NULL, writer) == -1) - return -1; - return 1; - } - - arg->sign = 1; - if (formatfloat(v, arg, p_str, NULL) == -1) - return -1; - break; - - case 'c': - { - Py_UCS4 ch = formatchar(v); - if (ch == (Py_UCS4) -1) - return -1; - if (arg->width == -1 && arg->prec == -1) { - /* Fast path */ - if (_PyUnicodeWriter_WriteCharInline(writer, ch) < 0) - return -1; - return 1; - } - *p_str = PyUnicode_FromOrdinal(ch); - break; - } - - default: - PyErr_Format(PyExc_ValueError, - "unsupported format character '%c' (0x%x) " - "at index %zd", - (31<=arg->ch && arg->ch<=126) ? (char)arg->ch : '?', - (int)arg->ch, - ctx->fmtpos - 1); - return -1; - } - if (*p_str == NULL) - return -1; - assert (PyUnicode_Check(*p_str)); - return 0; -} - -static int -unicode_format_arg_output(struct unicode_formatter_t *ctx, - struct unicode_format_arg_t *arg, - PyObject *str) -{ - Py_ssize_t len; - int kind; - const void *pbuf; - Py_ssize_t pindex; - Py_UCS4 signchar; - Py_ssize_t buflen; - Py_UCS4 maxchar; - Py_ssize_t sublen; - _PyUnicodeWriter *writer = &ctx->writer; - Py_UCS4 fill; - - fill = ' '; - if (arg->sign && arg->flags & F_ZERO) - fill = '0'; - - len = PyUnicode_GET_LENGTH(str); - if ((arg->width == -1 || arg->width <= len) - && (arg->prec == -1 || arg->prec >= len) - && !(arg->flags & (F_SIGN | F_BLANK))) - { - /* Fast path */ - if (_PyUnicodeWriter_WriteStr(writer, str) == -1) - return -1; - return 0; - } - - /* Truncate the string for "s", "r" and "a" formats - if the precision is set */ - if (arg->ch == 's' || arg->ch == 'r' || arg->ch == 'a') { - if (arg->prec >= 0 && len > arg->prec) - len = arg->prec; - } - - /* Adjust sign and width */ - kind = PyUnicode_KIND(str); - pbuf = PyUnicode_DATA(str); - pindex = 0; - signchar = '\0'; - if (arg->sign) { - Py_UCS4 ch = PyUnicode_READ(kind, pbuf, pindex); - if (ch == '-' || ch == '+') { - signchar = ch; - len--; - pindex++; - } - else if (arg->flags & F_SIGN) - signchar = '+'; - else if (arg->flags & F_BLANK) - signchar = ' '; - else - arg->sign = 0; - } - if (arg->width < len) - arg->width = len; - - /* Prepare the writer */ - maxchar = writer->maxchar; - if (!(arg->flags & F_LJUST)) { - if (arg->sign) { - if ((arg->width-1) > len) - maxchar = Py_MAX(maxchar, fill); - } - else { - if (arg->width > len) - maxchar = Py_MAX(maxchar, fill); - } - } - if (PyUnicode_MAX_CHAR_VALUE(str) > maxchar) { - Py_UCS4 strmaxchar = _PyUnicode_FindMaxChar(str, 0, pindex+len); - maxchar = Py_MAX(maxchar, strmaxchar); - } - - buflen = arg->width; - if (arg->sign && len == arg->width) - buflen++; - if (_PyUnicodeWriter_Prepare(writer, buflen, maxchar) == -1) - return -1; - - /* Write the sign if needed */ - if (arg->sign) { - if (fill != ' ') { - PyUnicode_WRITE(writer->kind, writer->data, writer->pos, signchar); - writer->pos += 1; - } - if (arg->width > len) - arg->width--; - } - - /* Write the numeric prefix for "x", "X" and "o" formats - if the alternate form is used. - For example, write "0x" for the "%#x" format. */ - if ((arg->flags & F_ALT) && (arg->ch == 'x' || arg->ch == 'X' || arg->ch == 'o')) { - assert(PyUnicode_READ(kind, pbuf, pindex) == '0'); - assert(PyUnicode_READ(kind, pbuf, pindex + 1) == arg->ch); - if (fill != ' ') { - PyUnicode_WRITE(writer->kind, writer->data, writer->pos, '0'); - PyUnicode_WRITE(writer->kind, writer->data, writer->pos+1, arg->ch); - writer->pos += 2; - pindex += 2; - } - arg->width -= 2; - if (arg->width < 0) - arg->width = 0; - len -= 2; - } - - /* Pad left with the fill character if needed */ - if (arg->width > len && !(arg->flags & F_LJUST)) { - sublen = arg->width - len; - unicode_fill(writer->kind, writer->data, fill, writer->pos, sublen); - writer->pos += sublen; - arg->width = len; - } - - /* If padding with spaces: write sign if needed and/or numeric prefix if - the alternate form is used */ - if (fill == ' ') { - if (arg->sign) { - PyUnicode_WRITE(writer->kind, writer->data, writer->pos, signchar); - writer->pos += 1; - } - if ((arg->flags & F_ALT) && (arg->ch == 'x' || arg->ch == 'X' || arg->ch == 'o')) { - assert(PyUnicode_READ(kind, pbuf, pindex) == '0'); - assert(PyUnicode_READ(kind, pbuf, pindex+1) == arg->ch); - PyUnicode_WRITE(writer->kind, writer->data, writer->pos, '0'); - PyUnicode_WRITE(writer->kind, writer->data, writer->pos+1, arg->ch); - writer->pos += 2; - pindex += 2; + in_leading_space = false; + } + ++iter; } - } - /* Write characters */ - if (len) { - _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, - str, pindex, len); - writer->pos += len; - } - - /* Pad right with the fill character if needed */ - if (arg->width > len) { - sublen = arg->width - len; - unicode_fill(writer->kind, writer->data, ' ', writer->pos, sublen); - writer->pos += sublen; - } - return 0; -} + // invariant: *iter == '\n' or iter == end + bool append_newline = iter < end; -/* Helper of PyUnicode_Format(): format one arg. - Return 0 on success, raise an exception and return -1 on error. */ -static int -unicode_format_arg(struct unicode_formatter_t *ctx) -{ - struct unicode_format_arg_t arg; - PyObject *str; - int ret; + // if this line has all white space, write '\n' and continue + if (in_leading_space && append_newline) { + *dest_iter++ = '\n'; + continue; + } - arg.ch = PyUnicode_READ(ctx->fmtkind, ctx->fmtdata, ctx->fmtpos); - if (arg.ch == '%') { - ctx->fmtpos++; - ctx->fmtcnt--; - if (_PyUnicodeWriter_WriteCharInline(&ctx->writer, '%') < 0) - return -1; - return 0; - } - arg.flags = 0; - arg.width = -1; - arg.prec = -1; - arg.sign = 0; - str = NULL; + /* copy [new_line_start + whitespace_len, iter) to buffer, then + conditionally append '\n' */ - ret = unicode_format_arg_parse(ctx, &arg); - if (ret == -1) - return -1; + Py_ssize_t new_line_len = iter - line_start - whitespace_len; + assert(new_line_len >= 0); + memcpy(dest_iter, line_start + whitespace_len, new_line_len); - ret = unicode_format_arg_format(ctx, &arg, &str); - if (ret == -1) - return -1; + dest_iter += new_line_len; - if (ret != 1) { - ret = unicode_format_arg_output(ctx, &arg, str); - Py_DECREF(str); - if (ret == -1) - return -1; + if (append_newline) { + *dest_iter++ = '\n'; + } } - if (ctx->dict && (ctx->argidx < ctx->arglen)) { - PyErr_SetString(PyExc_TypeError, - "not all arguments converted during string formatting"); - return -1; - } - return 0; + PyObject *res = PyUnicode_FromStringAndSize(dest, dest_iter - dest); + PyMem_Free(dest); + return res; } -PyObject * -PyUnicode_Format(PyObject *format, PyObject *args) -{ - struct unicode_formatter_t ctx; +static PyMethodDef unicode_methods[] = { + UNICODE_ENCODE_METHODDEF + UNICODE_REPLACE_METHODDEF + UNICODE_SPLIT_METHODDEF + UNICODE_RSPLIT_METHODDEF + UNICODE_JOIN_METHODDEF + UNICODE_CAPITALIZE_METHODDEF + UNICODE_CASEFOLD_METHODDEF + UNICODE_TITLE_METHODDEF + UNICODE_CENTER_METHODDEF + UNICODE_COUNT_METHODDEF + UNICODE_EXPANDTABS_METHODDEF + UNICODE_FIND_METHODDEF + UNICODE_PARTITION_METHODDEF + UNICODE_INDEX_METHODDEF + UNICODE_LJUST_METHODDEF + UNICODE_LOWER_METHODDEF + UNICODE_LSTRIP_METHODDEF + UNICODE_RFIND_METHODDEF + UNICODE_RINDEX_METHODDEF + UNICODE_RJUST_METHODDEF + UNICODE_RSTRIP_METHODDEF + UNICODE_RPARTITION_METHODDEF + UNICODE_SPLITLINES_METHODDEF + UNICODE_STRIP_METHODDEF + UNICODE_SWAPCASE_METHODDEF + UNICODE_TRANSLATE_METHODDEF + UNICODE_UPPER_METHODDEF + UNICODE_STARTSWITH_METHODDEF + UNICODE_ENDSWITH_METHODDEF + UNICODE_REMOVEPREFIX_METHODDEF + UNICODE_REMOVESUFFIX_METHODDEF + UNICODE_ISASCII_METHODDEF + UNICODE_ISLOWER_METHODDEF + UNICODE_ISUPPER_METHODDEF + UNICODE_ISTITLE_METHODDEF + UNICODE_ISSPACE_METHODDEF + UNICODE_ISDECIMAL_METHODDEF + UNICODE_ISDIGIT_METHODDEF + UNICODE_ISNUMERIC_METHODDEF + UNICODE_ISALPHA_METHODDEF + UNICODE_ISALNUM_METHODDEF + UNICODE_ISIDENTIFIER_METHODDEF + UNICODE_ISPRINTABLE_METHODDEF + UNICODE_ZFILL_METHODDEF + {"format", _PyCFunction_CAST(do_string_format), METH_VARARGS | METH_KEYWORDS, format__doc__}, + {"format_map", do_string_format_map, METH_O, format_map__doc__}, + UNICODE___FORMAT___METHODDEF + UNICODE_MAKETRANS_METHODDEF + UNICODE_SIZEOF_METHODDEF + {"__getnewargs__", unicode_getnewargs, METH_NOARGS}, + {NULL, NULL} +}; - if (format == NULL || args == NULL) { - PyErr_BadInternalCall(); - return NULL; - } +static PyObject * +unicode_mod(PyObject *v, PyObject *w) +{ + if (!PyUnicode_Check(v)) + Py_RETURN_NOTIMPLEMENTED; + return PyUnicode_Format(v, w); +} - if (ensure_unicode(format) < 0) - return NULL; +static PyNumberMethods unicode_as_number = { + 0, /*nb_add*/ + 0, /*nb_subtract*/ + 0, /*nb_multiply*/ + unicode_mod, /*nb_remainder*/ +}; - ctx.fmtstr = format; - ctx.fmtdata = PyUnicode_DATA(ctx.fmtstr); - ctx.fmtkind = PyUnicode_KIND(ctx.fmtstr); - ctx.fmtcnt = PyUnicode_GET_LENGTH(ctx.fmtstr); - ctx.fmtpos = 0; +static PySequenceMethods unicode_as_sequence = { + unicode_length, /* sq_length */ + PyUnicode_Concat, /* sq_concat */ + _PyUnicode_Repeat, /* sq_repeat */ + unicode_getitem, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + PyUnicode_Contains, /* sq_contains */ +}; - _PyUnicodeWriter_Init(&ctx.writer); - ctx.writer.min_length = ctx.fmtcnt + 100; - ctx.writer.overallocate = 1; +static PyObject* +unicode_subscript(PyObject* self, PyObject* item) +{ + if (_PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += PyUnicode_GET_LENGTH(self); + return unicode_getitem(self, i); + } else if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength, i; + size_t cur; + PyObject *result; + const void *src_data; + void *dest_data; + int src_kind, dest_kind; + Py_UCS4 ch, max_char, kind_limit; - if (PyTuple_Check(args)) { - ctx.arglen = PyTuple_Size(args); - ctx.argidx = 0; - } - else { - ctx.arglen = -1; - ctx.argidx = -2; - } - ctx.args_owned = 0; - if (PyMapping_Check(args) && !PyTuple_Check(args) && !PyUnicode_Check(args)) - ctx.dict = args; - else - ctx.dict = NULL; - ctx.args = args; - - while (--ctx.fmtcnt >= 0) { - if (PyUnicode_READ(ctx.fmtkind, ctx.fmtdata, ctx.fmtpos) != '%') { - Py_ssize_t nonfmtpos; - - nonfmtpos = ctx.fmtpos++; - while (ctx.fmtcnt >= 0 && - PyUnicode_READ(ctx.fmtkind, ctx.fmtdata, ctx.fmtpos) != '%') { - ctx.fmtpos++; - ctx.fmtcnt--; - } - if (ctx.fmtcnt < 0) { - ctx.fmtpos--; - ctx.writer.overallocate = 0; - } + if (PySlice_Unpack(item, &start, &stop, &step) < 0) { + return NULL; + } + slicelength = PySlice_AdjustIndices(PyUnicode_GET_LENGTH(self), + &start, &stop, step); - if (_PyUnicodeWriter_WriteSubstring(&ctx.writer, ctx.fmtstr, - nonfmtpos, ctx.fmtpos) < 0) - goto onError; + if (slicelength <= 0) { + _Py_RETURN_UNICODE_EMPTY(); + } else if (start == 0 && step == 1 && + slicelength == PyUnicode_GET_LENGTH(self)) { + return unicode_result_unchanged(self); + } else if (step == 1) { + return PyUnicode_Substring(self, + start, start + slicelength); } - else { - ctx.fmtpos++; - if (unicode_format_arg(&ctx) == -1) - goto onError; + /* General case */ + src_kind = PyUnicode_KIND(self); + src_data = PyUnicode_DATA(self); + if (!PyUnicode_IS_ASCII(self)) { + kind_limit = kind_maxchar_limit(src_kind); + max_char = 0; + for (cur = start, i = 0; i < slicelength; cur += step, i++) { + ch = PyUnicode_READ(src_kind, src_data, cur); + if (ch > max_char) { + max_char = ch; + if (max_char >= kind_limit) + break; + } + } } - } + else + max_char = 127; + result = PyUnicode_New(slicelength, max_char); + if (result == NULL) + return NULL; + dest_kind = PyUnicode_KIND(result); + dest_data = PyUnicode_DATA(result); - if (ctx.argidx < ctx.arglen && !ctx.dict) { - PyErr_SetString(PyExc_TypeError, - "not all arguments converted during string formatting"); - goto onError; + for (cur = start, i = 0; i < slicelength; cur += step, i++) { + Py_UCS4 ch = PyUnicode_READ(src_kind, src_data, cur); + PyUnicode_WRITE(dest_kind, dest_data, i, ch); + } + assert(_PyUnicode_CheckConsistency(result, 1)); + return result; + } else { + PyErr_Format(PyExc_TypeError, "string indices must be integers, not '%.200s'", + Py_TYPE(item)->tp_name); + return NULL; } +} - if (ctx.args_owned) { - Py_DECREF(ctx.args); - } - return _PyUnicodeWriter_Finish(&ctx.writer); +static PyMappingMethods unicode_as_mapping = { + unicode_length, /* mp_length */ + unicode_subscript, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; - onError: - _PyUnicodeWriter_Dealloc(&ctx.writer); - if (ctx.args_owned) { - Py_DECREF(ctx.args); - } - return NULL; -} static PyObject * unicode_subtype_new(PyTypeObject *type, PyObject *unicode); @@ -15556,7 +13836,7 @@ unicode_new_impl(PyTypeObject *type, PyObject *x, const char *encoding, { PyObject *unicode; if (x == NULL) { - unicode = unicode_get_empty(); + unicode = _PyUnicode_GetEmpty(); } else if (encoding == NULL && errors == NULL) { unicode = PyObject_Str(x); @@ -15592,7 +13872,7 @@ unicode_vectorcall(PyObject *type, PyObject *const *args, Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) { // Fallback to unicode_new() - PyObject *tuple = _PyTuple_FromArray(args, nargs); + PyObject *tuple = PyTuple_FromArray(args, nargs); if (tuple == NULL) { return NULL; } @@ -15610,7 +13890,7 @@ unicode_vectorcall(PyObject *type, PyObject *const *args, return NULL; } if (nargs == 0) { - return unicode_get_empty(); + return _PyUnicode_GetEmpty(); } PyObject *object = args[0]; if (nargs == 1) { @@ -15707,6 +13987,20 @@ unicode_subtype_new(PyTypeObject *type, PyObject *unicode) return NULL; } +static _PyObjectIndexPair +unicode_iteritem(PyObject *obj, Py_ssize_t index) +{ + if (index >= PyUnicode_GET_LENGTH(obj)) { + return (_PyObjectIndexPair) { .object = NULL, .index = index }; + } + const void *data = PyUnicode_DATA(obj); + int kind = PyUnicode_KIND(obj); + Py_UCS4 ch = PyUnicode_READ(kind, data, index); + PyObject *result = unicode_char(ch); + index = (result == NULL) ? -1 : index + 1; + return (_PyObjectIndexPair) { .object = result, .index = index }; +} + void _PyUnicode_ExactDealloc(PyObject *op) { @@ -15772,6 +14066,7 @@ PyTypeObject PyUnicode_Type = { unicode_new, /* tp_new */ PyObject_Free, /* tp_free */ .tp_vectorcall = unicode_vectorcall, + ._tp_iteritem = unicode_iteritem, }; /* Initialize the Unicode implementation */ @@ -15911,9 +14206,24 @@ immortalize_interned(PyObject *s) _Py_DecRefTotal(_PyThreadState_GET()); } #endif - FT_ATOMIC_STORE_UINT8_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_INTERNED_IMMORTAL); _Py_SetImmortal(s); + // The switch to SSTATE_INTERNED_IMMORTAL must be the last thing done here + // to synchronize with the check in intern_common() that avoids locking if + // the string is already immortal. + FT_ATOMIC_STORE_UINT8(_PyUnicode_STATE(s).interned, SSTATE_INTERNED_IMMORTAL); +} + +#ifdef Py_GIL_DISABLED +static bool +can_immortalize_safely(PyObject *s) +{ + if (_Py_IsOwnedByCurrentThread(s) || _Py_IsImmortal(s)) { + return true; + } + Py_ssize_t shared = _Py_atomic_load_ssize(&s->ob_ref_shared); + return _Py_REF_IS_MERGED(shared); } +#endif static /* non-null */ PyObject* intern_common(PyInterpreterState *interp, PyObject *s /* stolen */, @@ -15943,11 +14253,16 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */, // no, go on break; case SSTATE_INTERNED_MORTAL: +#ifndef Py_GIL_DISABLED // yes but we might need to make it immortal if (immortalize) { immortalize_interned(s); } return s; +#else + // not fully interned yet; fall through to the locking path + break; +#endif default: // all done return s; @@ -15992,14 +14307,50 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */, /* Do a setdefault on the per-interpreter cache. */ PyObject *interned = get_interned_dict(interp); assert(interned != NULL); +#ifdef Py_GIL_DISABLED +# define INTERN_MUTEX &_Py_INTERP_CACHED_OBJECT(interp, interned_mutex) + // Lock-free fast path: check if there's already an interned copy that + // is in its final immortal state. + PyObject *r; + int res = PyDict_GetItemRef(interned, s, &r); + if (res < 0) { + PyErr_Clear(); + return s; + } + if (res > 0) { + unsigned int state = _Py_atomic_load_uint8(&_PyUnicode_STATE(r).interned); + if (state == SSTATE_INTERNED_IMMORTAL) { + Py_DECREF(s); + return r; + } + // Not yet fully interned; fall through to the locking path. + Py_DECREF(r); + } +#endif + +#ifdef Py_GIL_DISABLED + // Immortalization writes to the refcount fields non-atomically. That + // races with Py_INCREF / Py_DECREF on the thread that owns `s`. If we + // don't own it (and its refcount hasn't been merged), intern a copy + // we own instead. + if (!can_immortalize_safely(s)) { + PyObject *copy = _PyUnicode_Copy(s); + if (copy == NULL) { + PyErr_Clear(); + return s; + } + Py_DECREF(s); + s = copy; + } +#endif - LOCK_INTERNED(interp); + FT_MUTEX_LOCK(INTERN_MUTEX); PyObject *t; { int res = PyDict_SetDefaultRef(interned, s, s, &t); if (res < 0) { PyErr_Clear(); - UNLOCK_INTERNED(interp); + FT_MUTEX_UNLOCK(INTERN_MUTEX); return s; } else if (res == 1) { @@ -16009,7 +14360,7 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */, PyUnicode_CHECK_INTERNED(t) == SSTATE_INTERNED_MORTAL) { immortalize_interned(t); } - UNLOCK_INTERNED(interp); + FT_MUTEX_UNLOCK(INTERN_MUTEX); return t; } else { @@ -16029,7 +14380,7 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */, Py_DECREF(s); Py_DECREF(s); } - FT_ATOMIC_STORE_UINT8_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_INTERNED_MORTAL); + FT_ATOMIC_STORE_UINT8(_PyUnicode_STATE(s).interned, SSTATE_INTERNED_MORTAL); /* INTERNED_MORTAL -> INTERNED_IMMORTAL (if needed) */ @@ -16042,7 +14393,7 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */, immortalize_interned(s); } - UNLOCK_INTERNED(interp); + FT_MUTEX_UNLOCK(INTERN_MUTEX); return s; } @@ -16284,7 +14635,7 @@ unicodeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) if (it->it_seq != NULL) { return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index); } else { - PyObject *u = unicode_get_empty(); + PyObject *u = _PyUnicode_GetEmpty(); if (u == NULL) { Py_XDECREF(iter); return NULL; @@ -16574,33 +14925,6 @@ _PyUnicode_FiniEncodings(struct _Py_unicode_fs_codec *fs_codec) } -#ifdef MS_WINDOWS -int -_PyUnicode_EnableLegacyWindowsFSEncoding(void) -{ - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyConfig *config = (PyConfig *)_PyInterpreterState_GetConfig(interp); - - /* Set the filesystem encoding to mbcs/replace (PEP 529) */ - wchar_t *encoding = _PyMem_RawWcsdup(L"mbcs"); - wchar_t *errors = _PyMem_RawWcsdup(L"replace"); - if (encoding == NULL || errors == NULL) { - PyMem_RawFree(encoding); - PyMem_RawFree(errors); - PyErr_NoMemory(); - return -1; - } - - PyMem_RawFree(config->filesystem_encoding); - config->filesystem_encoding = encoding; - PyMem_RawFree(config->filesystem_errors); - config->filesystem_errors = errors; - - return init_fs_codec(interp); -} -#endif - - #ifdef Py_DEBUG static inline int unicode_is_finalizing(void) @@ -16650,6 +14974,7 @@ static PyMethodDef _string_methods[] = { }; static PyModuleDef_Slot module_slots[] = { + _Py_ABI_SLOT, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, {0, NULL} diff --git a/Objects/unicodetype_db.h b/Objects/unicodetype_db.h index 5be810dd67426a..536f51324aeee8 100644 --- a/Objects/unicodetype_db.h +++ b/Objects/unicodetype_db.h @@ -508,6 +508,8 @@ const _PyUnicode_TypeRecord _PyUnicode_TypeRecords[] = { {-40, 0, -40, 0, 0, 9993}, {0, 39, 0, 0, 0, 10113}, {-39, 0, -39, 0, 0, 9993}, + {0, 27, 0, 0, 0, 10113}, + {-27, 0, -27, 0, 0, 9993}, {0, 34, 0, 0, 0, 10113}, {-34, 0, -34, 0, 0, 9993}, {0, 0, 0, 0, 0, 9344}, @@ -1755,1218 +1757,612 @@ const Py_UCS4 _PyUnicode_ExtendedCase[] = { }; /* type indexes */ -#define SHIFT 6 +#define SHIFT 7 static const unsigned short index1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 26, 26, 26, 26, 26, 68, 69, - 70, 71, 72, 73, 74, 75, 26, 26, 26, 26, 26, 26, 26, 26, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105, 12, 106, 106, 107, 106, 108, 109, - 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 120, 121, 122, 123, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 124, 125, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 126, 127, 119, 128, 129, 106, 130, 131, 132, - 133, 134, 135, 136, 137, 138, 119, 119, 119, 139, 140, 141, 142, 143, - 144, 26, 145, 146, 147, 148, 149, 119, 119, 119, 119, 119, 150, 26, 151, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 152, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 153, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 119, 154, 155, 156, 157, 153, - 158, 26, 159, 160, 26, 26, 26, 161, 162, 26, 26, 26, 26, 26, 26, 26, 163, - 26, 164, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 165, 26, 26, 26, 26, - 26, 26, 26, 166, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 167, 26, 168, 169, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 170, 26, 171, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 172, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 173, 26, 26, 26, 26, - 26, 26, 26, 160, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 174, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 175, 176, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 177, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 160, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 178, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 179, 26, 158, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 180, 26, 26, 26, 26, 26, 26, 26, 26, 26, 159, 26, 26, 26, 26, 26, 181, - 182, 26, 183, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 184, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 185, 186, 26, 26, 26, 26, 187, 188, 189, 190, 191, 192, 193, - 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, - 208, 209, 210, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 211, 212, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 26, 214, 215, 216, 26, 217, 26, 218, 219, - 220, 221, 222, 26, 223, 26, 26, 224, 225, 226, 227, 228, 229, 26, 230, - 231, 232, 233, 234, 235, 236, 26, 237, 238, 239, 240, 241, 213, 213, 242, - 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 26, 26, - 26, 26, 256, 257, 258, 213, 259, 260, 261, 262, 263, 213, 264, 265, 266, - 267, 268, 269, 270, 271, 272, 213, 26, 273, 274, 275, 276, 277, 278, 213, - 213, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, - 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, - 213, 213, 306, 307, 308, 309, 310, 311, 312, 313, 213, 213, 314, 213, - 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 213, 213, 326, - 327, 328, 329, 213, 330, 331, 332, 213, 213, 213, 213, 333, 334, 335, - 336, 337, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 218, - 213, 338, 339, 26, 26, 26, 340, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 341, 342, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 343, 344, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 237, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 313, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 345, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 26, 26, - 26, 26, 26, 26, 26, 26, 324, 346, 347, 348, 349, 350, 351, 213, 213, 213, - 213, 213, 213, 352, 213, 213, 213, 353, 354, 213, 26, 355, 356, 357, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 358, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 359, 273, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 360, 26, 26, 26, 26, 361, 362, 26, 26, 26, 26, 26, - 363, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 26, 364, 365, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 119, 119, 119, 366, 119, 119, 119, 119, 119, 119, 138, 213, - 367, 368, 119, 369, 119, 119, 119, 370, 371, 372, 373, 374, 119, 375, - 213, 376, 119, 377, 213, 213, 378, 379, 380, 381, 382, 383, 384, 385, - 386, 387, 388, 389, 390, 391, 392, 393, 119, 119, 119, 119, 119, 119, - 119, 119, 394, 395, 396, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 397, 213, 213, 213, 398, 399, - 400, 213, 401, 402, 213, 213, 213, 213, 403, 404, 213, 213, 213, 213, - 213, 213, 213, 405, 213, 213, 213, 406, 213, 213, 213, 213, 213, 213, - 213, 407, 26, 26, 26, 408, 409, 410, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 411, 412, 213, 413, 213, 213, 213, 414, 415, 416, - 417, 213, 213, 213, 213, 418, 119, 419, 420, 421, 422, 423, 424, 425, - 426, 213, 213, 119, 119, 119, 427, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 428, 119, 429, 119, 430, 431, 432, 433, 434, 119, - 119, 119, 119, 119, 435, 436, 437, 119, 119, 438, 366, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 439, - 440, 26, 441, 181, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 152, 26, 442, 26, 26, 26, 26, 443, 444, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 445, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 446, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 165, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 177, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 447, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 448, 26, 26, 26, 449, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 450, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 451, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 452, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 26, 26, 445, 26, - 26, 26, 26, 26, 452, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 453, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 454, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 455, 456, 213, 213, 12, 12, 12, 457, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, - 213, 213, 213, 213, 213, 213, 213, 213, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 34, 35, 36, 37, + 38, 39, 34, 34, 34, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 65, 66, 64, + 64, 64, 64, 67, 68, 64, 64, 64, 64, 64, 64, 69, 64, 70, 71, 72, 73, 74, + 75, 64, 76, 77, 78, 79, 80, 81, 82, 64, 64, 83, 84, 34, 34, 34, 34, 34, + 34, 85, 34, 34, 34, 34, 34, 86, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 87, 88, 89, 90, 91, 92, 34, 93, 34, 34, + 34, 94, 95, 34, 34, 34, 34, 34, 96, 34, 34, 34, 97, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 98, 99, 100, 34, 34, 34, 34, 34, 34, 101, 102, 34, + 34, 34, 34, 34, 34, 34, 34, 103, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 104, 34, 34, 34, 92, 34, 34, 34, 34, 34, 34, 34, 34, 105, 34, 34, 34, 34, + 106, 107, 34, 34, 34, 34, 34, 108, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 92, 34, 34, 34, 34, 34, 34, 109, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 110, 111, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 112, 34, 34, 34, 34, 113, 34, 34, 114, 115, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 116, 34, 34, 34, + 34, 34, 34, 34, 34, 117, 34, 34, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 130, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 132, 133, + 134, 135, 136, 137, 138, 34, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 131, 149, 150, 151, 152, 153, 154, 155, 34, 34, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 131, 184, 185, 186, + 187, 131, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 131, 200, 201, 202, 203, 34, 34, 34, 204, 34, 205, 206, 207, 34, 208, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 209, 34, 34, 34, 34, 34, 34, 34, 34, 210, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 146, 34, 34, 34, 34, 211, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 212, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 34, 34, 34, 34, + 213, 214, 215, 216, 131, 131, 217, 131, 218, 219, 220, 221, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 222, 223, 224, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 225, 34, 34, 226, 34, 34, 227, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 228, 229, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 64, + 230, 64, 64, 64, 231, 232, 233, 64, 234, 235, 236, 237, 238, 239, 131, + 240, 241, 242, 243, 244, 245, 246, 247, 64, 64, 64, 64, 248, 249, 131, + 131, 131, 131, 131, 131, 131, 131, 250, 131, 251, 252, 253, 131, 131, + 254, 131, 131, 131, 255, 131, 256, 131, 257, 131, 258, 34, 259, 260, 131, + 131, 131, 131, 131, 261, 262, 263, 131, 264, 265, 131, 131, 266, 267, + 268, 269, 270, 131, 64, 271, 64, 64, 64, 64, 64, 272, 64, 273, 274, 275, + 64, 64, 276, 277, 64, 278, 131, 131, 131, 131, 131, 131, 131, 131, 279, + 280, 281, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 85, + 282, 34, 283, 284, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 285, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 286, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 287, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 108, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 288, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 289, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 290, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 291, 34, 34, 34, 34, 292, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 34, 285, 34, + 34, 293, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 294, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 295, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 296, 131, 297, 298, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, }; static const unsigned short index2[] = { @@ -3005,7 +2401,7 @@ static const unsigned short index2[] = { 19, 77, 81, 19, 82, 83, 84, 85, 19, 86, 87, 85, 88, 89, 19, 19, 87, 19, 90, 91, 19, 19, 92, 19, 19, 19, 19, 19, 19, 19, 93, 19, 19, 94, 19, 95, 94, 19, 19, 19, 96, 94, 97, 98, 98, 99, 19, 19, 19, 19, 19, 100, 19, 55, - 19, 19, 19, 19, 19, 19, 19, 19, 101, 102, 19, 19, 19, 19, 19, 19, 19, 19, + 55, 19, 19, 19, 19, 19, 19, 19, 101, 102, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 103, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 103, 103, 5, 5, 5, 5, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 5, 5, 5, 5, 5, 5, @@ -3089,7 +2485,7 @@ static const unsigned short index2[] = { 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 0, 0, 4, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 5, 55, 55, 55, 55, 55, 55, 0, 20, 20, 0, 0, 0, 0, 0, 24, + 55, 55, 55, 55, 5, 55, 55, 55, 55, 55, 55, 55, 20, 20, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, 24, 24, 24, 24, @@ -3139,14 +2535,14 @@ static const unsigned short index2[] = { 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 24, 55, 24, 24, 24, 17, 17, 17, 17, 0, 24, 24, 24, 0, 24, 24, 24, 24, 0, 0, 0, 0, - 0, 0, 0, 24, 24, 0, 55, 55, 55, 0, 0, 55, 0, 0, 55, 55, 24, 24, 0, 0, 6, + 0, 0, 0, 24, 24, 0, 55, 55, 55, 0, 55, 55, 0, 0, 55, 55, 24, 24, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 4, 26, 26, 26, 26, 26, 26, 26, 4, 55, 24, 17, 17, 4, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 0, 0, 24, 55, 17, 24, 17, 17, 17, 17, 17, 0, 24, 17, 17, 0, 17, 17, 24, 24, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, - 0, 0, 55, 55, 0, 55, 55, 24, 24, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 0, 55, 55, 55, 0, 55, 55, 24, 24, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 55, 55, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 17, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, @@ -3202,112 +2598,130 @@ static const unsigned short index2[] = { 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 4, 103, 142, 142, 142, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 0, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 0, - 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 0, - 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 0, 55, 55, 55, 55, 0, 0, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, - 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 0, 0, 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 143, 144, 145, 146, 147, - 148, 149, 150, 151, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 152, 153, 154, 155, 156, 157, 158, - 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, - 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, - 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, - 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, - 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, - 229, 230, 231, 232, 233, 234, 235, 236, 237, 0, 0, 238, 239, 240, 241, - 242, 243, 0, 0, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, + 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 0, 55, 55, 55, 55, 0, 0, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 0, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, + 55, 55, 0, 55, 0, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 1, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 24, 24, 24, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 143, 144, 145, 146, 147, 148, 149, 150, 151, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, + 0, 0, 0, 0, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 0, 0, 238, 239, 240, 241, 242, 243, 0, 0, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 4, 4, 4, 244, 244, 244, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 1, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 0, 0, 0, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 4, + 244, 244, 244, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, + 24, 24, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 17, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 24, 24, 24, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, - 17, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, - 0, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 0, 24, 24, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 17, 24, 24, 24, 24, 24, - 24, 24, 17, 17, 17, 17, 17, 17, 17, 17, 24, 17, 17, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 4, 4, 4, 104, 4, 4, 4, 4, 55, 24, 0, 0, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 24, - 24, 20, 24, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 55, 55, + 55, 55, 55, 55, 55, 24, 24, 17, 24, 24, 24, 24, 24, 24, 24, 17, 17, 17, + 17, 17, 17, 17, 17, 24, 17, 17, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 4, 4, 4, 104, 4, 4, 4, 4, 55, 24, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, + 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 24, 24, 20, 24, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, - 0, 0, 0, 55, 55, 55, 55, 55, 245, 245, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, + 55, 55, 245, 245, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 24, 55, 0, 0, 0, 0, 0, 55, 55, 55, 55, + 55, 55, 24, 55, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 24, 24, 24, - 17, 17, 17, 17, 24, 24, 17, 17, 17, 0, 0, 0, 0, 17, 17, 24, 17, 17, 17, - 17, 17, 17, 24, 24, 24, 0, 0, 0, 0, 4, 0, 0, 0, 4, 4, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 55, - 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 0, 24, 24, 24, 17, 17, 17, 17, 24, 24, + 17, 17, 17, 0, 0, 0, 0, 17, 17, 24, 17, 17, 17, 17, 17, 17, 24, 24, 24, + 0, 0, 0, 0, 4, 0, 0, 0, 4, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 143, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 24, 24, 17, 17, 24, 0, 0, 4, 4, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 143, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 24, 24, 17, 17, 24, 0, 0, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 24, 17, 24, 24, - 24, 24, 24, 24, 24, 0, 24, 17, 24, 17, 17, 24, 24, 24, 24, 24, 24, 24, - 24, 17, 17, 17, 17, 17, 17, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, - 24, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 104, 4, 4, 4, - 4, 4, 4, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 5, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, - 24, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 17, 24, 24, - 24, 24, 24, 17, 24, 17, 17, 17, 17, 17, 24, 17, 17, 55, 55, 55, 55, 55, - 55, 55, 55, 0, 4, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 24, 17, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 17, 24, 17, 24, 24, 24, 24, 24, 24, 24, 0, + 24, 17, 24, 17, 17, 24, 24, 24, 24, 24, 24, 24, 24, 17, 17, 17, 17, 17, + 17, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 24, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, + 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 104, 4, 4, 4, 4, 4, 4, 0, 0, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 5, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 24, 24, 24, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 17, + 24, 24, 24, 24, 24, 17, 24, 17, 17, 17, 17, 17, 24, 17, 17, 55, 55, 55, + 55, 55, 55, 55, 55, 0, 4, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 17, 24, 24, 24, 24, 17, 17, 24, 24, 17, 24, 24, - 24, 55, 55, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 17, 24, 24, 24, 24, 17, 17, 24, 24, 17, 24, + 24, 24, 55, 55, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 24, 17, 24, 24, 17, 17, 17, 24, 17, 24, 24, 24, 17, 17, 0, 0, 0, - 0, 0, 0, 0, 0, 4, 4, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 24, 17, 24, 24, 17, 17, 17, 24, 17, 24, 24, 24, 17, 17, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 17, 17, 17, 17, 17, 17, 24, 24, 24, 24, 24, 24, 24, 24, 17, 17, 24, 24, 0, 0, 0, 4, 4, 4, 4, 4, 6, 7, 8, 9, @@ -3333,44 +2747,55 @@ static const unsigned short index2[] = { 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 258, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 29, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, - 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 259, 260, 261, 262, - 263, 264, 19, 19, 265, 19, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 259, 260, + 261, 262, 263, 264, 19, 19, 265, 19, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, - 30, 29, 30, 266, 266, 266, 266, 266, 266, 266, 266, 267, 267, 267, 267, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 266, 266, + 266, 266, 266, 266, 266, 266, 267, 267, 267, 267, 267, 267, 267, 267, + 266, 266, 266, 266, 266, 266, 0, 0, 267, 267, 267, 267, 267, 267, 0, 0, + 266, 266, 266, 266, 266, 266, 266, 266, 267, 267, 267, 267, 267, 267, + 267, 267, 266, 266, 266, 266, 266, 266, 266, 266, 267, 267, 267, 267, 267, 267, 267, 267, 266, 266, 266, 266, 266, 266, 0, 0, 267, 267, 267, - 267, 267, 267, 0, 0, 266, 266, 266, 266, 266, 266, 266, 266, 267, 267, - 267, 267, 267, 267, 267, 267, 266, 266, 266, 266, 266, 266, 266, 266, - 267, 267, 267, 267, 267, 267, 267, 267, 266, 266, 266, 266, 266, 266, 0, - 0, 267, 267, 267, 267, 267, 267, 0, 0, 268, 266, 269, 266, 270, 266, 271, - 266, 0, 267, 0, 267, 0, 267, 0, 267, 266, 266, 266, 266, 266, 266, 266, - 266, 267, 267, 267, 267, 267, 267, 267, 267, 272, 272, 273, 273, 273, - 273, 274, 274, 275, 275, 276, 276, 277, 277, 0, 0, 278, 279, 280, 281, - 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, - 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, - 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, - 324, 325, 266, 266, 326, 327, 328, 0, 329, 330, 267, 267, 331, 331, 332, - 5, 333, 5, 5, 5, 334, 335, 336, 0, 337, 338, 339, 339, 339, 339, 340, 5, - 5, 5, 266, 266, 341, 342, 0, 0, 343, 344, 267, 267, 345, 345, 0, 5, 5, 5, - 266, 266, 346, 347, 348, 128, 349, 350, 267, 267, 351, 351, 132, 5, 5, 5, - 0, 0, 352, 353, 354, 0, 355, 356, 357, 357, 358, 358, 359, 5, 5, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 20, 360, 360, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 5, 2, 2, 20, 20, 20, 20, 20, - 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17, 17, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 1, 20, 20, 20, 20, 20, 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 361, - 103, 0, 0, 362, 363, 364, 365, 366, 367, 4, 4, 4, 4, 4, 103, 361, 25, 21, - 22, 362, 363, 364, 365, 366, 367, 4, 4, 4, 4, 4, 0, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 4, 4, 4, 4, 4, 4, + 267, 267, 267, 0, 0, 268, 266, 269, 266, 270, 266, 271, 266, 0, 267, 0, + 267, 0, 267, 0, 267, 266, 266, 266, 266, 266, 266, 266, 266, 267, 267, + 267, 267, 267, 267, 267, 267, 272, 272, 273, 273, 273, 273, 274, 274, + 275, 275, 276, 276, 277, 277, 0, 0, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 266, + 266, 326, 327, 328, 0, 329, 330, 267, 267, 331, 331, 332, 5, 333, 5, 5, + 5, 334, 335, 336, 0, 337, 338, 339, 339, 339, 339, 340, 5, 5, 5, 266, + 266, 341, 342, 0, 0, 343, 344, 267, 267, 345, 345, 0, 5, 5, 5, 266, 266, + 346, 347, 348, 128, 349, 350, 267, 267, 351, 351, 132, 5, 5, 5, 0, 0, + 352, 353, 354, 0, 355, 356, 357, 357, 358, 358, 359, 5, 5, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 20, 360, 360, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 5, 2, 2, 20, 20, 20, 20, 20, 1, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17, 17, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 20, + 20, 20, 20, 20, 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 361, 103, 0, + 0, 362, 363, 364, 365, 366, 367, 4, 4, 4, 4, 4, 103, 361, 25, 21, 22, + 362, 363, 364, 365, 366, 367, 4, 4, 4, 4, 4, 0, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 5, 5, 5, 5, 24, 5, 5, 5, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 4, 4, 122, 4, 4, 4, 4, 122, 4, 4, 19, 122, 122, 122, 19, 19, 122, 122, + 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 5, 5, 5, 5, 24, 5, 5, 5, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 122, 4, 4, 4, 4, 122, 4, 4, 19, 122, 122, 122, 19, 19, 122, 122, 122, 19, 4, 122, 4, 4, 368, 122, 122, 122, 122, 122, 4, 4, 4, 4, 4, 4, 122, 4, 369, 4, 122, 4, 370, 371, 122, 122, 368, 19, 122, 122, 372, 122, 19, 55, 55, 55, 55, 19, 4, 4, 19, 19, 122, 122, 4, 4, 4, 4, 4, 122, 19, @@ -3384,594 +2809,775 @@ static const unsigned short index2[] = { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 25, 21, 22, 362, 363, 364, 365, 366, 367, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 25, 21, 22, 362, 363, 364, 365, 366, 367, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 25, 21, 22, 362, 363, 364, 365, 366, 367, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 376, 376, 376, 376, 376, - 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, - 376, 376, 376, 376, 376, 376, 376, 377, 377, 377, 377, 377, 377, 377, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 21, 22, 362, 363, 364, 365, 366, 367, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 21, 22, 362, 363, 364, + 365, 366, 367, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 21, 22, + 362, 363, 364, 365, 366, 367, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, + 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, - 377, 377, 377, 377, 377, 361, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, - 21, 22, 362, 363, 364, 365, 366, 367, 26, 361, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, 377, 361, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 25, 21, 22, 362, 363, 364, 365, 366, 367, 26, + 361, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 25, 21, - 22, 362, 363, 364, 365, 366, 367, 26, 25, 21, 22, 362, 363, 364, 365, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 25, + 21, 22, 362, 363, 364, 365, 366, 367, 26, 25, 21, 22, 362, 363, 364, 365, 366, 367, 26, 25, 21, 22, 362, 363, 364, 365, 366, 367, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, + 137, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, - 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 29, 30, 378, - 379, 380, 381, 382, 29, 30, 29, 30, 29, 30, 383, 384, 385, 386, 19, 29, - 30, 19, 29, 30, 19, 19, 19, 19, 19, 103, 103, 387, 387, 29, 30, 29, 30, + 138, 138, 138, 138, 138, 138, 138, 29, 30, 378, 379, 380, 381, 382, 29, + 30, 29, 30, 29, 30, 383, 384, 385, 386, 19, 29, 30, 19, 29, 30, 19, 19, + 19, 19, 19, 103, 103, 387, 387, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, - 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 19, 4, 4, 4, 4, - 4, 4, 29, 30, 29, 30, 24, 24, 24, 29, 30, 0, 0, 0, 0, 0, 4, 4, 4, 4, 26, - 4, 4, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, + 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, + 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, + 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, + 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, + 19, 4, 4, 4, 4, 4, 4, 29, 30, 29, 30, 24, 24, 24, 29, 30, 0, 0, 0, 0, 0, + 4, 4, 4, 4, 26, 4, 4, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, - 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 0, 388, 0, 0, 0, - 0, 0, 388, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 0, + 388, 0, 0, 0, 0, 0, 388, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 104, 4, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 24, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, - 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 104, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, - 55, 55, 55, 55, 55, 55, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, + 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 389, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 389, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 4, 4, 4, 4, 104, 55, 244, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 244, - 244, 244, 244, 244, 244, 244, 244, 244, 24, 24, 24, 24, 17, 17, 4, 104, - 104, 104, 104, 104, 4, 4, 244, 244, 244, 104, 55, 4, 4, 4, 0, 55, 55, 55, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 4, 4, 4, 4, 104, 55, 244, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 24, 24, 24, 24, 17, 17, 4, 104, 104, 104, + 104, 104, 4, 4, 244, 244, 244, 104, 55, 4, 4, 4, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 24, 24, 5, 5, 104, 104, - 55, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 24, 24, 5, 5, 104, 104, 55, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 17, 104, 104, 104, 55, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 17, 104, 104, 104, 55, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 4, 4, 26, 26, 26, - 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 4, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 0, 4, 4, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 26, 26, 26, 26, 26, 26, 26, 26, 4, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 26, 26, 26, 26, 26, 26, + 26, 26, 4, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 55, 55, 55, - 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 4, 4, 4, 4, 4, 4, 4, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 55, 55, + 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 390, 55, 55, 390, 55, 55, 55, 390, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, - 55, 55, 55, 55, 55, 55, 390, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 390, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, - 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 390, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 390, 55, 55, 390, 55, 55, 55, 390, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 390, 55, 390, 390, 390, 55, 55, 55, 55, 55, 55, 390, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 390, 390, - 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 390, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, + 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 390, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 390, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, - 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 390, 390, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 390, 390, 390, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 390, 55, 390, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, + 55, 390, 390, 390, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 390, 390, 390, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, - 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 390, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 390, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 104, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 104, 104, 104, 104, 104, 104, 4, 4, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 104, 4, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 55, - 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, - 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, - 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, - 29, 30, 29, 30, 29, 30, 29, 30, 55, 24, 5, 5, 5, 4, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 4, 104, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, - 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 103, - 103, 24, 24, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 24, 24, 4, 4, 4, 4, - 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 104, 104, 104, 104, 104, 104, 104, 104, 104, - 5, 5, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 19, 19, 29, - 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, - 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, - 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, - 30, 29, 30, 29, 30, 29, 30, 103, 19, 19, 19, 19, 19, 19, 19, 19, 29, 30, - 29, 30, 391, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 104, 5, 5, 29, 30, - 392, 19, 55, 29, 30, 29, 30, 393, 19, 29, 30, 29, 30, 29, 30, 29, 30, 29, - 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 394, 395, 396, 397, 394, 19, - 398, 399, 400, 401, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, - 30, 29, 30, 402, 403, 404, 29, 30, 29, 30, 405, 29, 30, 0, 0, 29, 30, 0, - 19, 0, 19, 29, 30, 29, 30, 29, 30, 406, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 103, 103, 29, 30, 55, 103, 103, 19, - 55, 55, 55, 55, 55, 55, 55, 24, 55, 55, 55, 24, 55, 55, 55, 55, 24, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 17, 17, 24, 24, 17, 4, 4, 4, 4, 24, 0, 0, 0, 26, 26, 26, - 26, 26, 26, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 17, - 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 24, 24, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 55, 55, 55, 55, 55, 55, 4, 4, 4, 55, 4, 55, 55, 24, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, - 24, 24, 24, 24, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 55, 55, 55, 55, + 390, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 390, 390, + 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 24, 24, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 24, 17, 17, 24, 24, 24, 24, 17, 17, 24, 24, 17, - 17, 17, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 104, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 0, 0, 0, 0, 4, 4, 55, 55, 55, 55, 55, 24, 104, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 55, - 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 17, 17, - 24, 24, 17, 17, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 24, 55, - 55, 55, 55, 55, 55, 55, 55, 24, 17, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 0, 0, 4, 4, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 104, 55, 55, 55, 55, 55, 55, 4, 4, 4, 55, 17, 24, 17, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 55, 24, 24, 24, 55, - 55, 24, 24, 55, 55, 55, 55, 55, 24, 24, 55, 24, 55, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 104, 4, 4, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 24, 24, 17, 17, 4, 4, 55, - 104, 104, 17, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, - 0, 0, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, - 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 407, 19, 19, 19, 19, 19, 19, 19, 5, 103, 103, 103, 103, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 103, 5, 5, 0, 0, 0, 0, 408, 409, 410, 411, 412, 413, - 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, - 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, - 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, - 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, - 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, - 484, 485, 486, 487, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 17, 17, 24, 17, 17, 24, 17, 17, 4, 17, 24, 0, 0, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 390, 55, - 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 390, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 488, 489, 490, 491, 492, 493, 494, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 495, 496, 497, 498, 499, 0, 0, 0, 0, 0, 55, 24, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 0, 55, 0, 55, 55, 0, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 500, 500, 500, 500, 500, 500, 55, + 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 500, 500, 4, 4, 4, 4, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, - 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 4, 4, 4, 17, 17, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 17, 17, 17, 4, 4, 5, 0, 4, 5, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 0, 0, 0, 0, 500, 55, 500, - 55, 500, 0, 500, 55, 500, 55, 500, 55, 500, 55, 500, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 0, 0, 20, 0, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 5, 4, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 5, 4, 4, 4, 4, 4, 4, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 4, 4, 4, 5, 17, 5, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 501, 501, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 0, 0, 0, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, - 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 0, 0, 0, 4, - 4, 4, 5, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 20, 20, 20, 4, 4, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 0, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 244, 244, 244, 244, 244, 244, - 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, + 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 390, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 104, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, 104, 104, + 104, 104, 104, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, + 4, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 55, + 24, 5, 5, 5, 4, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 4, 104, 29, 30, + 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, + 29, 30, 29, 30, 29, 30, 29, 30, 103, 103, 24, 24, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 24, 24, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 5, 5, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 19, 19, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 103, + 19, 19, 19, 19, 19, 19, 19, 19, 29, 30, 29, 30, 391, 29, 30, 29, 30, 29, + 30, 29, 30, 29, 30, 104, 5, 5, 29, 30, 392, 19, 55, 29, 30, 29, 30, 393, + 19, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, + 30, 29, 30, 394, 395, 396, 397, 394, 19, 398, 399, 400, 401, 29, 30, 29, + 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 402, 403, 404, 29, + 30, 29, 30, 405, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, + 29, 30, 406, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 103, 103, 103, 103, 29, 30, 55, 103, 103, 19, 55, 55, 55, 55, 55, 55, 55, + 24, 55, 55, 55, 24, 55, 55, 55, 55, 24, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 24, + 24, 17, 4, 4, 4, 4, 24, 0, 0, 0, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 0, + 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 55, 55, 55, 55, 55, 55, + 4, 4, 4, 55, 4, 55, 55, 24, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 24, 4, 4, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 17, 17, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 0, 0, 0, 24, 24, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, + 17, 17, 24, 24, 24, 24, 17, 17, 24, 24, 17, 17, 17, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 0, 104, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, + 4, 4, 55, 55, 55, 55, 55, 24, 104, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 24, 24, 24, 24, 24, 24, 17, 17, 24, 24, 17, 17, 24, 24, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 55, 55, 55, 24, 55, 55, 55, 55, 55, 55, 55, 55, 24, 17, 0, + 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 4, 4, 4, 4, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, 55, 55, 55, 55, 55, + 55, 4, 4, 4, 55, 17, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 24, 55, 24, 24, 24, 55, 55, 24, 24, 55, 55, 55, 55, 55, 24, + 24, 55, 24, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 55, 55, 104, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 17, 24, 24, 17, 17, 4, 4, 55, 104, 104, 17, 24, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 0, 0, 55, + 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, + 55, 0, 55, 55, 55, 55, 55, 55, 55, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 407, 19, 19, 19, 19, 19, 19, 19, 5, 103, + 103, 103, 103, 19, 19, 19, 19, 19, 19, 19, 19, 19, 103, 5, 5, 0, 0, 0, 0, + 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, + 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, + 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, + 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, + 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, + 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 24, 17, 17, 24, 17, + 17, 4, 17, 24, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, + 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 390, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, + 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 488, 489, 490, 491, 492, 493, 494, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 495, 496, 497, 498, 499, 0, 0, 0, 0, 0, 55, 24, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 0, 55, 55, 55, 55, 55, 0, 55, 0, 55, 55, 0, 55, 55, 0, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 500, + 500, 500, 500, 500, 500, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 4, 4, 4, 4, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 500, 500, + 4, 4, 4, 4, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 17, 17, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17, 17, 17, + 4, 4, 5, 0, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, + 4, 4, 4, 4, 0, 0, 0, 0, 500, 55, 500, 55, 500, 0, 500, 55, 500, 55, 500, + 55, 500, 55, 500, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 0, 0, 20, 0, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 5, 4, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 5, 4, 4, 4, 4, 4, 4, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 4, 4, 4, 5, 17, 5, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 501, 501, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 0, 0, 0, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, + 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 0, 0, 0, + 4, 4, 4, 5, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 20, 20, 20, 4, 4, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 0, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 4, 4, + 4, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, - 244, 244, 244, 244, 244, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 26, 26, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 26, + 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 26, 26, 4, + 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 24, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 55, 55, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 24, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 26, 26, 26, 26, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 244, 55, 55, 55, 55, 55, 55, 55, 55, 244, 0, - 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 0, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 244, 55, 55, 55, 55, 55, 55, 55, 55, 244, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, - 55, 55, 4, 244, 244, 244, 244, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, + 24, 24, 24, 24, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 0, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 4, 244, 244, 244, + 244, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, - 502, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, + 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, - 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, + 503, 503, 503, 503, 503, 503, 503, 503, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, - 0, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, - 502, 502, 502, 502, 502, 502, 502, 502, 0, 0, 0, 0, 503, 503, 503, 503, - 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, + 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, + 502, 502, 502, 502, 0, 0, 0, 0, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, - 503, 503, 503, 503, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, + 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 0, + 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 4, 504, 504, 504, 504, 504, 504, 504, 504, 504, - 504, 504, 0, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, - 504, 504, 504, 0, 504, 504, 504, 504, 504, 504, 504, 0, 504, 504, 0, 505, - 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 0, 505, 505, 505, 505, - 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 0, 505, 505, 505, - 505, 505, 505, 505, 0, 505, 505, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 0, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 0, 504, + 504, 504, 504, 504, 504, 504, 0, 504, 504, 0, 505, 505, 505, 505, 505, + 505, 505, 505, 505, 505, 505, 0, 505, 505, 505, 505, 505, 505, 505, 505, + 505, 505, 505, 505, 505, 505, 505, 0, 505, 505, 505, 505, 505, 505, 505, + 0, 505, 505, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 104, 104, 103, 103, 103, 0, 103, 103, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, + 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 103, 104, 104, 103, 103, 103, 0, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, - 0, 0, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 0, 0, 0, 55, 0, 0, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 0, 4, 26, 26, 26, 26, 26, 26, 26, 26, 55, 55, 55, 55, + 103, 103, 103, 103, 103, 103, 103, 0, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 0, 0, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 4, 4, 26, 26, 26, 26, 26, 26, 26, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, + 55, 55, 0, 0, 0, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 4, 26, 26, 26, 26, 26, + 26, 26, 26, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 26, 26, 26, 26, 26, 26, 26, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 0, 55, 55, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 26, - 26, 26, 26, 26, 26, 0, 0, 0, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 0, 0, 0, 0, 0, 26, 26, 26, 26, + 26, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 26, 26, 26, 26, 26, 26, 0, 0, 0, 4, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 0, 0, 0, 0, 0, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, - 0, 0, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 0, 0, 0, 26, 26, 55, 55, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 26, 26, 55, 55, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 55, 24, 24, 24, 0, 24, 24, 0, 0, 0, 0, 0, 24, 24, 24, 24, 55, 55, - 55, 55, 0, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, - 24, 24, 24, 0, 0, 0, 0, 24, 25, 21, 22, 362, 26, 26, 26, 26, 26, 0, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, + 26, 26, 26, 26, 26, 26, 26, 26, 55, 24, 24, 24, 0, 24, 24, 0, 0, 0, 0, 0, + 24, 24, 24, 24, 55, 55, 55, 55, 0, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 26, 26, 4, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 0, 0, 24, 24, 24, 0, 0, 0, 0, 24, 25, 21, 22, 362, + 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, + 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 26, 26, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, - 55, 55, 55, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 0, 0, 0, - 0, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 26, 26, 26, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 24, 24, 0, 0, 0, 0, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 26, 26, 26, 26, 26, 26, - 26, 26, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, - 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, - 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, + 26, 26, 26, 26, 26, 26, 26, 26, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, + 26, 26, 26, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 110, 110, 110, 110, - 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, - 110, 110, 110, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 117, 117, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, - 26, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 55, 55, 55, - 55, 104, 55, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 0, 0, 0, 24, 24, 24, 24, 24, 4, 104, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 0, 0, 0, + 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 55, 55, 55, 55, 104, 55, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 0, 0, 24, + 24, 24, 24, 24, 4, 104, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 21, 22, - 362, 363, 364, 365, 366, 367, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 0, 24, 24, 4, 0, 0, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 21, 22, 362, 363, 364, + 365, 366, 367, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 55, 0, 0, - 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 26, 26, 26, 26, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 24, 24, 4, + 0, 0, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, + 104, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 55, 0, 0, 0, 0, 0, 0, + 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 26, 26, + 26, 26, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 24, 24, 24, 24, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 17, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 25, 21, 22, 362, - 363, 364, 365, 366, 367, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 24, 55, 55, 24, 24, 55, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 24, 24, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 25, 21, 22, 362, 363, 364, + 365, 366, 367, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 24, 55, 55, 24, 24, 55, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 24, 24, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, - 17, 24, 24, 24, 24, 17, 17, 24, 24, 4, 4, 20, 4, 4, 4, 4, 24, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 20, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, - 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 24, 24, 24, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 17, 24, + 24, 24, 24, 17, 17, 24, 24, 4, 4, 20, 4, 4, 4, 4, 24, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 24, 24, 24, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, - 24, 24, 24, 24, 17, 24, 24, 24, 24, 24, 24, 24, 24, 0, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 4, 4, 4, 4, 55, 17, 17, 55, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, + 24, 24, 24, 17, 24, 24, 24, 24, 24, 24, 24, 24, 0, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 4, 4, 4, 4, 55, 17, 17, 55, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, - 4, 4, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 17, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 4, 4, + 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 17, 17, 17, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 17, 17, 55, 55, 55, 55, 4, 4, 4, 4, 24, 24, 24, 24, 4, 17, 24, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 55, 4, 55, 4, 4, 4, 0, 26, 26, 26, 26, 26, 26, + 55, 55, 55, 55, 55, 17, 17, 17, 24, 24, 24, 24, 24, 24, 24, 24, 24, 17, + 17, 55, 55, 55, 55, 4, 4, 4, 4, 24, 24, 24, 24, 4, 17, 24, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 55, 4, 55, 4, 4, 4, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, @@ -4041,38 +3647,46 @@ static const unsigned short index2[] = { 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 17, 24, 24, 24, 24, 24, 24, 24, 24, 24, 17, 24, 24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, - 55, 0, 0, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 0, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 17, 17, 17, 17, 17, 17, 0, 17, 17, 0, 0, 24, 24, 17, 24, - 55, 17, 55, 17, 24, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 55, 0, 0, 55, 55, + 55, 55, 55, 55, 55, 55, 0, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 17, + 17, 17, 17, 0, 17, 17, 0, 0, 24, 24, 17, 24, 55, 17, 55, 17, 24, 4, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, + 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 17, - 24, 24, 24, 24, 0, 0, 24, 24, 17, 17, 17, 17, 24, 55, 4, 55, 17, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 55, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 17, 24, 24, 24, 24, 0, 0, 24, 24, + 17, 17, 17, 17, 24, 55, 4, 55, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, - 24, 24, 24, 17, 55, 24, 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 24, 0, 0, 0, - 0, 0, 0, 0, 0, 55, 24, 24, 24, 24, 24, 24, 17, 17, 24, 24, 24, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 17, 55, 24, 24, 24, + 24, 4, 4, 4, 4, 4, 4, 4, 4, 24, 0, 0, 0, 0, 0, 0, 0, 0, 55, 24, 24, 24, + 24, 24, 24, 17, 17, 24, 24, 24, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 17, 24, 24, 4, 4, 4, 55, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 17, 24, 24, 4, 4, 4, + 55, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, + 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 24, 17, 24, 24, 24, 17, 24, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, @@ -4088,105 +3702,188 @@ static const unsigned short index2[] = { 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 17, 24, 24, 24, 24, 24, 24, 24, 17, 24, 24, 17, - 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 24, 24, 24, 24, 24, 24, 0, 0, 0, 24, 0, 24, 24, 0, 24, 24, - 24, 24, 24, 24, 24, 55, 24, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 0, 55, 55, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, + 24, 24, 24, 0, 0, 0, 24, 0, 24, 24, 0, 24, 24, 24, 24, 24, 24, 24, 55, + 24, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, + 0, 0, 0, 55, 55, 55, 55, 55, 55, 0, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 17, 17, - 17, 0, 24, 24, 0, 17, 17, 24, 17, 24, 55, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 17, 17, 17, 0, 24, 24, 0, 17, 17, + 24, 17, 24, 55, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, 55, 55, 0, 0, 0, 0, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 17, 17, 4, 4, 0, - 0, 0, 0, 0, 0, 0, 24, 24, 55, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 24, 24, 17, 17, 4, 4, 0, 0, 0, 0, 0, 0, 0, 24, 24, + 55, 17, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 17, 17, 24, 24, 24, 24, 24, 0, 0, 0, 17, 17, 24, 17, 24, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 24, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 17, 17, 24, 24, + 24, 24, 24, 0, 0, 0, 17, 17, 24, 17, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 390, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 390, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 390, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, - 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 0, - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 0, 4, 4, 4, 4, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 24, 55, + 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 17, 17, + 17, 24, 24, 24, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 24, 55, 55, 55, 55, 55, 55, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 17, 17, 17, 24, 24, 24, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 0, 0, 24, 24, 24, 24, 24, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, + 24, 24, 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 104, 104, 104, 104, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 26, + 26, 26, 26, 26, 26, 26, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 104, 104, 104, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, 104, 4, 4, 4, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 4, 4, 4, 4, 0, 0, 0, 0, 0, 506, 506, 506, 506, 506, 506, 506, + 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, + 506, 506, 506, 506, 0, 0, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 4, 4, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 0, 0, 0, 0, 24, 55, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 104, 4, 104, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 17, 17, 104, 104, 244, 244, 244, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 24, 24, 24, 24, - 24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 104, - 104, 104, 104, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 0, 26, 26, 26, 26, 26, 26, 26, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 104, 104, 104, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, 104, 4, 4, 4, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 24, 55, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 104, 104, 104, 104, 104, 104, 104, - 104, 104, 104, 104, 104, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 104, 104, 4, 104, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 104, 104, 104, 0, 104, 104, 104, 104, 104, 104, 104, 0, 104, 104, 0, 55, @@ -4199,58 +3896,89 @@ static const unsigned short index2[] = { 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, - 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 0, 0, 4, 24, 24, 4, 20, 20, 20, 20, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, + 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, + 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 0, 0, 4, 24, 24, 4, 20, 20, 20, 20, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 4, 4, 4, + 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, + 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17, 17, 24, 24, 24, 4, 4, 4, 17, 17, 17, - 17, 17, 17, 20, 20, 20, 20, 20, 20, 20, 20, 24, 24, 24, 24, 24, 24, 24, - 24, 4, 4, 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 24, 24, 24, 4, + 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17, 17, 24, 24, 24, 4, 4, 4, + 17, 17, 17, 17, 17, 17, 20, 20, 20, 20, 20, 20, 20, 20, 24, 24, 24, 24, + 24, 24, 24, 24, 4, 4, 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 24, + 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 24, 24, 24, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 24, 24, 24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, - 0, 0, 0, 0, 0, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 0, 0, 0, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 19, 19, 19, 19, 19, 19, 19, 0, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 19, 19, 19, 19, 19, 19, 19, 0, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 122, 122, 19, 19, 19, 19, 19, 19, 19, + 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 122, 0, 122, 122, 0, 0, 122, 0, 0, 122, 122, 0, 0, 122, 122, 122, + 19, 19, 122, 0, 122, 122, 0, 0, 122, 0, 0, 122, 122, 0, 0, 122, 122, 122, 122, 0, 122, 122, 122, 122, 122, 122, 122, 122, 19, 19, 19, 19, 0, 19, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, @@ -4312,199 +4040,320 @@ static const unsigned short index2[] = { 4, 4, 4, 4, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 55, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, - 24, 24, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 0, 0, 24, 24, 24, 24, 24, 24, 24, 0, 24, 24, 0, 24, 24, 24, 24, - 24, 0, 0, 0, 0, 0, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 55, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, + 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 24, 24, 24, 24, 24, 24, 24, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 0, 0, 24, 24, 24, 24, 24, 24, 24, 0, 24, 24, + 0, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 104, + 104, 104, 104, 104, 104, 104, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 0, 0, 0, 55, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 24, 24, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 104, 24, 24, 24, 24, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 24, 24, 24, 24, 24, 24, - 24, 104, 104, 104, 104, 104, 104, 104, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 0, 0, 0, 0, 55, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, 55, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, + 24, 55, 55, 24, 55, 55, 55, 55, 55, 55, 55, 24, 24, 55, 55, 55, 55, 55, + 24, 0, 0, 0, 0, 0, 0, 0, 0, 55, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, + 55, 55, 55, 0, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 24, 24, - 24, 24, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 104, 24, 24, 24, 24, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 24, 24, 55, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 0, - 55, 55, 55, 55, 0, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 0, 0, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 24, 24, 24, 24, 24, 24, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, + 508, 508, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, + 509, 509, 509, 509, 509, 509, 509, 509, 24, 24, 24, 24, 24, 24, 24, 104, + 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 4, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, - 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, - 506, 506, 506, 506, 506, 506, 506, 506, 506, 507, 507, 507, 507, 507, - 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, - 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, - 507, 24, 24, 24, 24, 24, 24, 24, 104, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 4, 26, 26, 26, 4, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 26, 26, 26, 26, 4, 26, 26, 26, 4, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 4, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 0, 0, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 55, 55, 0, 55, 0, 0, 55, 0, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 55, 55, 55, 55, 0, 55, 0, 55, 0, 0, 0, 0, 0, 0, 55, 0, - 0, 0, 0, 55, 0, 55, 0, 55, 0, 55, 55, 55, 0, 55, 55, 0, 55, 0, 0, 55, 0, - 55, 0, 55, 0, 55, 0, 55, 0, 55, 55, 0, 55, 0, 0, 55, 55, 55, 55, 0, 55, - 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 0, 55, 55, 55, 55, 0, 55, 0, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 55, 55, 55, 0, 55, - 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 26, 26, 26, 26, 26, 26, 4, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, - 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 361, 361, 25, 21, 22, 362, 363, 364, 365, 366, 367, 26, 26, 4, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, + 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 0, 55, 0, + 0, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 0, + 55, 0, 55, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 55, 0, 55, 0, 55, 0, 55, 55, + 55, 0, 55, 55, 0, 55, 0, 0, 55, 0, 55, 0, 55, 0, 55, 0, 55, 0, 55, 55, 0, + 55, 0, 0, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, 55, 0, 55, 55, 55, + 55, 0, 55, 55, 55, 55, 0, 55, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, + 0, 0, 0, 0, 55, 55, 55, 0, 55, 55, 55, 55, 55, 0, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, - 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, - 508, 508, 4, 4, 4, 4, 4, 4, 508, 508, 508, 508, 508, 508, 508, 508, 508, - 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, - 508, 508, 508, 4, 4, 4, 4, 4, 4, 508, 508, 508, 508, 508, 508, 508, 508, - 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, - 508, 508, 508, 508, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, + 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 361, 361, 25, 21, 22, 362, 363, 364, 365, 366, 367, 26, 26, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 510, 510, 510, 510, 510, 510, 510, 510, 510, + 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, + 510, 510, 510, 4, 4, 4, 4, 4, 4, 510, 510, 510, 510, 510, 510, 510, 510, + 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, + 510, 510, 510, 510, 4, 4, 4, 4, 4, 4, 510, 510, 510, 510, 510, 510, 510, + 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, + 510, 510, 510, 510, 510, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, + 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, - 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, - 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, - 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 55, 390, 55, 55, 55, 55, 55, 55, 55, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 4, 0, 0, 0, 0, 0, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, + 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 390, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 0, 0, 0, 0, 0, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 20, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, }; /* Returns the numeric value as double for Unicode characters @@ -4586,6 +4435,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11C50: case 0x11D50: case 0x11DA0: + case 0x11DE0: case 0x11F50: case 0x16130: case 0x16A60: @@ -4726,7 +4576,12 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11C5A: case 0x11D51: case 0x11DA1: + case 0x11DE1: case 0x11F51: + case 0x12038: + case 0x12039: + case 0x12079: + case 0x1230B: case 0x12415: case 0x1241E: case 0x1242C: @@ -4740,6 +4595,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x16D71: case 0x16E81: case 0x16E94: + case 0x16FF4: case 0x1CCF1: case 0x1D2C1: case 0x1D2E1: @@ -4796,6 +4652,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x10F26: case 0x11FD1: case 0x11FD2: + case 0x12226: case 0x12464: case 0x1ECAE: case 0x1ED3C: @@ -5000,7 +4857,6 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x1ECA0: case 0x1ECB4: return (double) 100000.0; - case 0x5146: case 0x16B5E: return (double) 1000000.0; case 0x1ECA1: @@ -5013,6 +4869,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) return (double) 1000000000.0; case 0x16B60: return (double) 10000000000.0; + case 0x5146: case 0x16B61: return (double) 1000000000000.0; case 0x4EAC: @@ -5219,7 +5076,10 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11C5B: case 0x11D52: case 0x11DA2: + case 0x11DE2: case 0x11F52: + case 0x1222B: + case 0x12399: case 0x12400: case 0x12416: case 0x1241F: @@ -5237,6 +5097,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x16D72: case 0x16E82: case 0x16E95: + case 0x16FF6: case 0x1CCF2: case 0x1D2C2: case 0x1D2E2: @@ -5457,7 +5318,9 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11C5C: case 0x11D53: case 0x11DA3: + case 0x11DE3: case 0x11F53: + case 0x1230D: case 0x12401: case 0x12408: case 0x12417: @@ -5516,6 +5379,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11FCE: return (double) 3.0/16.0; case 0x0F2B: + case 0x16FF5: return (double) 3.0/2.0; case 0x0D5D: case 0x11FCD: @@ -5691,6 +5555,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11C5D: case 0x11D54: case 0x11DA4: + case 0x11DE4: case 0x11F54: case 0x12402: case 0x12409: @@ -5901,6 +5766,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11C5E: case 0x11D55: case 0x11DA5: + case 0x11DE5: case 0x11F55: case 0x12403: case 0x1240A: @@ -6108,6 +5974,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11C5F: case 0x11D56: case 0x11DA6: + case 0x11DE6: case 0x11F56: case 0x12404: case 0x1240B: @@ -6269,6 +6136,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11C60: case 0x11D57: case 0x11DA7: + case 0x11DE7: case 0x11F57: case 0x12405: case 0x1240C: @@ -6431,6 +6299,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11C61: case 0x11D58: case 0x11DA8: + case 0x11DE8: case 0x11F58: case 0x12406: case 0x1240D: @@ -6589,6 +6458,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch) case 0x11C62: case 0x11D59: case 0x11DA9: + case 0x11DE9: case 0x11F59: case 0x12407: case 0x1240E: diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 2206ed80ef03fd..1dc2927b6e6ac7 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -61,7 +61,7 @@ union_hash(PyObject *self) } // The unhashable values somehow became hashable again. Still raise // an error. - PyErr_Format(PyExc_TypeError, "union contains %d unhashable elements", n); + PyErr_Format(PyExc_TypeError, "union contains %zd unhashable elements", n); return -1; } return PyObject_Hash(alias->hashable_args); @@ -245,6 +245,7 @@ is_unionable(PyObject *obj) { if (obj == Py_None || PyType_Check(obj) || + PySentinel_Check(obj) || _PyGenericAlias_Check(obj) || _PyUnion_Check(obj) || Py_IS_TYPE(obj, &_PyTypeAlias_Type)) { @@ -393,8 +394,23 @@ static PyGetSetDef union_properties[] = { {0} }; +static PyObject * +union_nb_or(PyObject *a, PyObject *b) +{ + unionbuilder ub; + if (!unionbuilder_init(&ub, true)) { + return NULL; + } + if (!unionbuilder_add_single(&ub, a) || + !unionbuilder_add_single(&ub, b)) { + unionbuilder_finalize(&ub); + return NULL; + } + return make_union(&ub); +} + static PyNumberMethods union_as_number = { - .nb_or = _Py_union_type_or, // Add __or__ function + .nb_or = union_nb_or, // Add __or__ function }; static const char* const cls_attrs[] = { @@ -474,11 +490,13 @@ _Py_union_from_tuple(PyObject *args) } if (PyTuple_CheckExact(args)) { if (!unionbuilder_add_tuple(&ub, args)) { + unionbuilder_finalize(&ub); return NULL; } } else { if (!unionbuilder_add_single(&ub, args)) { + unionbuilder_finalize(&ub); return NULL; } } @@ -500,7 +518,8 @@ union_mro_entries(PyObject *self, PyObject *args) static PyMethodDef union_methods[] = { {"__mro_entries__", union_mro_entries, METH_O}, - {"__class_getitem__", union_class_getitem, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {"__class_getitem__", union_class_getitem, METH_O|METH_CLASS, + PyDoc_STR("Create a union containing the given types")}, {0} }; diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index bd4c4ac9b3475a..daeba5368b2166 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -416,8 +416,15 @@ get_or_create_weakref(PyTypeObject *type, PyObject *obj, PyObject *callback) Py_TYPE(obj)->tp_name); return NULL; } - if (callback == Py_None) + if (callback == Py_None) { callback = NULL; + } + if (callback != NULL && !PyCallable_Check(callback)) { + PyErr_Format(PyExc_TypeError, + "callback must be callable or None, not '%T'", + callback); + return NULL; + } PyWeakReference **list = GET_WEAKREFS_LISTPTR(obj); if ((type == &_PyWeakref_RefType) || @@ -491,7 +498,8 @@ static PyMemberDef weakref_members[] = { static PyMethodDef weakref_methods[] = { {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, + PyDoc_STR("Weakrefs are generic over the type of the referenced object.")}, {NULL} /* Sentinel */ }; @@ -964,7 +972,8 @@ PyWeakref_GetRef(PyObject *ref, PyObject **pobj) } -PyObject * +/* removed in 3.15, but kept for stable ABI compatibility */ +PyAPI_FUNC(PyObject *) PyWeakref_GetObject(PyObject *ref) { if (ref == NULL || !PyWeakref_Check(ref)) { diff --git a/PC/_wmimodule.cpp b/PC/_wmimodule.cpp index 30d61c86587fbe..b9a229b1398ec8 100644 --- a/PC/_wmimodule.cpp +++ b/PC/_wmimodule.cpp @@ -230,13 +230,13 @@ _wmi.exec_query Runs a WMI query against the local machine. -This returns a single string with 'name=value' pairs in a flat array separated -by null characters. +This returns a single string with 'name=value' pairs in a flat array +separated by null characters. [clinic start generated code]*/ static PyObject * _wmi_exec_query_impl(PyObject *module, PyObject *query) -/*[clinic end generated code: output=a62303d5bb5e003f input=48d2d0a1e1a7e3c2]*/ +/*[clinic end generated code: output=a62303d5bb5e003f input=a8d5710acdfbf515]*/ /*[clinic end generated code]*/ { diff --git a/PC/clinic/_wmimodule.cpp.h b/PC/clinic/_wmimodule.cpp.h index 38d52d0329dcc0..6c18990f056b5f 100644 --- a/PC/clinic/_wmimodule.cpp.h +++ b/PC/clinic/_wmimodule.cpp.h @@ -14,8 +14,8 @@ PyDoc_STRVAR(_wmi_exec_query__doc__, "\n" "Runs a WMI query against the local machine.\n" "\n" -"This returns a single string with \'name=value\' pairs in a flat array separated\n" -"by null characters."); +"This returns a single string with \'name=value\' pairs in a flat array\n" +"separated by null characters."); #define _WMI_EXEC_QUERY_METHODDEF \ {"exec_query", _PyCFunction_CAST(_wmi_exec_query), METH_FASTCALL|METH_KEYWORDS, _wmi_exec_query__doc__}, @@ -72,4 +72,4 @@ _wmi_exec_query(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj exit: return return_value; } -/*[clinic end generated code: output=802bcbcba69e8d0e input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f246d0e568cc2d2c input=a9049054013a1b77]*/ diff --git a/PC/clinic/winreg.c.h b/PC/clinic/winreg.c.h index 45d54e3c90289a..92cf6e8a9be187 100644 --- a/PC/clinic/winreg.c.h +++ b/PC/clinic/winreg.c.h @@ -73,19 +73,13 @@ PyDoc_STRVAR(winreg_HKEYType___enter____doc__, #define WINREG_HKEYTYPE___ENTER___METHODDEF \ {"__enter__", (PyCFunction)winreg_HKEYType___enter__, METH_NOARGS, winreg_HKEYType___enter____doc__}, -static PyHKEYObject * +static PyObject * winreg_HKEYType___enter___impl(PyHKEYObject *self); static PyObject * winreg_HKEYType___enter__(PyObject *self, PyObject *Py_UNUSED(ignored)) { - PyObject *return_value = NULL; - PyHKEYObject *_return_value; - - _return_value = winreg_HKEYType___enter___impl((PyHKEYObject *)self); - return_value = (PyObject *)_return_value; - - return return_value; + return winreg_HKEYType___enter___impl((PyHKEYObject *)self); } #endif /* (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)) */ @@ -1633,6 +1627,70 @@ winreg_EnableReflectionKey(PyObject *module, PyObject *arg) #if (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)) && (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)) +PyDoc_STRVAR(winreg_DeleteTree__doc__, +"DeleteTree($module, key, sub_key=None, /)\n" +"--\n" +"\n" +"Deletes the specified key and all its subkeys and values recursively.\n" +"\n" +" key\n" +" An already open key, or any one of the predefined HKEY_* constants.\n" +" sub_key\n" +" A string that names the subkey to delete. If None, deletes all subkeys\n" +" and values of the specified key.\n" +"\n" +"This function deletes a key and all its descendants. If sub_key is None,\n" +"all subkeys and values of the specified key are deleted."); + +#define WINREG_DELETETREE_METHODDEF \ + {"DeleteTree", _PyCFunction_CAST(winreg_DeleteTree), METH_FASTCALL, winreg_DeleteTree__doc__}, + +static PyObject * +winreg_DeleteTree_impl(PyObject *module, HKEY key, const wchar_t *sub_key); + +static PyObject * +winreg_DeleteTree(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + HKEY key; + const wchar_t *sub_key = NULL; + + if (!_PyArg_CheckPositional("DeleteTree", nargs, 1, 2)) { + goto exit; + } + if (!clinic_HKEY_converter(_PyModule_GetState(module), args[0], &key)) { + goto exit; + } + if (nargs < 2) { + goto skip_optional; + } + if (args[1] == Py_None) { + sub_key = NULL; + } + else if (PyUnicode_Check(args[1])) { + sub_key = PyUnicode_AsWideCharString(args[1], NULL); + if (sub_key == NULL) { + goto exit; + } + } + else { + _PyArg_BadArgument("DeleteTree", "argument 2", "str or None", args[1]); + goto exit; + } +skip_optional: + return_value = winreg_DeleteTree_impl(module, key, sub_key); + +exit: + /* Cleanup for sub_key */ + PyMem_Free((void *)sub_key); + + return return_value; +} + +#endif /* (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)) && (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)) */ + +#if (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)) && (defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)) + PyDoc_STRVAR(winreg_QueryReflectionKey__doc__, "QueryReflectionKey($module, key, /)\n" "--\n" @@ -1771,7 +1829,11 @@ winreg_QueryReflectionKey(PyObject *module, PyObject *arg) #define WINREG_ENABLEREFLECTIONKEY_METHODDEF #endif /* !defined(WINREG_ENABLEREFLECTIONKEY_METHODDEF) */ +#ifndef WINREG_DELETETREE_METHODDEF + #define WINREG_DELETETREE_METHODDEF +#endif /* !defined(WINREG_DELETETREE_METHODDEF) */ + #ifndef WINREG_QUERYREFLECTIONKEY_METHODDEF #define WINREG_QUERYREFLECTIONKEY_METHODDEF #endif /* !defined(WINREG_QUERYREFLECTIONKEY_METHODDEF) */ -/*[clinic end generated code: output=be4b6857b95558b5 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=97295995db2c24e9 input=a9049054013a1b77]*/ diff --git a/PC/config.c b/PC/config.c index 6ce2131c7b84d0..51b46c64d99b81 100644 --- a/PC/config.c +++ b/PC/config.c @@ -13,6 +13,7 @@ extern PyObject* PyInit_errno(void); extern PyObject* PyInit_faulthandler(void); extern PyObject* PyInit__tracemalloc(void); extern PyObject* PyInit_gc(void); +extern PyObject* PyInit__math_integer(void); extern PyObject* PyInit_math(void); extern PyObject* PyInit_nt(void); extern PyObject* PyInit__operator(void); @@ -100,6 +101,7 @@ struct _inittab _PyImport_Inittab[] = { {"errno", PyInit_errno}, {"faulthandler", PyInit_faulthandler}, {"gc", PyInit_gc}, + {"_math_integer", PyInit__math_integer}, {"math", PyInit_math}, {"nt", PyInit_nt}, /* Use the NT os functions, not posix */ {"_operator", PyInit__operator}, diff --git a/PC/icons/idlex150.png b/PC/icons/idlex150.png index 806cb0c8aa219b..dbdca5b00f8812 100644 Binary files a/PC/icons/idlex150.png and b/PC/icons/idlex150.png differ diff --git a/PC/launcher.c b/PC/launcher.c index fed5e156b92cb3..5667fc851aa590 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -1273,6 +1273,7 @@ static PYC_MAGIC magic_values[] = { { 3550, 3599, L"3.13" }, { 3600, 3649, L"3.14" }, { 3650, 3699, L"3.15" }, + { 3700, 3749, L"3.16" }, { 0 } }; diff --git a/PC/launcher2.c b/PC/launcher2.c index 832935c5cc6c1c..4dd18c8eb5462e 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -922,6 +922,20 @@ _readIni(const wchar_t *section, const wchar_t *settingName, wchar_t *buffer, in { wchar_t iniPath[MAXLEN]; int n; + // Check for _PYLAUNCHER_INIDIR override (used for test isolation) + DWORD len = GetEnvironmentVariableW(L"_PYLAUNCHER_INIDIR", iniPath, MAXLEN); + if (len && len < MAXLEN) { + if (join(iniPath, MAXLEN, L"py.ini")) { + debug(L"# Reading from %s for %s/%s\n", iniPath, section, settingName); + n = GetPrivateProfileStringW(section, settingName, NULL, buffer, bufferLength, iniPath); + if (n) { + debug(L"# Found %s in %s\n", settingName, iniPath); + return n; + } + } + // When _PYLAUNCHER_INIDIR is set, skip the default locations + return 0; + } if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, iniPath)) && join(iniPath, MAXLEN, L"py.ini")) { debug(L"# Reading from %s for %s/%s\n", iniPath, section, settingName); diff --git a/PC/layout/main.py b/PC/layout/main.py index 8543e7c56e1c41..f70a26b2b29659 100644 --- a/PC/layout/main.py +++ b/PC/layout/main.py @@ -22,6 +22,7 @@ __path__ = [str(Path(__file__).resolve().parent)] from .support.appxmanifest import * +from .support.builddetails import * from .support.catalog import * from .support.constants import * from .support.filesets import * @@ -32,7 +33,8 @@ from .support.pymanager import * from .support.nuspec import * -TEST_PYDS_ONLY = FileStemSet("xxlimited", "xxlimited_35", "_ctypes_test", "_test*") +TEST_PYDS_ONLY = FileStemSet("xxlimited", "xxlimited_3_13", "xxlimited_35", + "_ctypes_test", "_test*") TEST_DLLS_ONLY = set() TEST_DIRS_ONLY = FileNameSet("test", "tests") @@ -127,7 +129,10 @@ def get_tcltk_lib(ns): def get_layout(ns): def in_build(f, dest="", new_name=None, no_lib=False): n, _, x = f.rpartition(".") - n = new_name or n + if new_name and new_name.endswith(f".{x}"): + n = new_name.rpartition(".")[0] + else: + n = new_name or n src = ns.build / f if ns.debug and src not in REQUIRED_DLLS: if not "_d." in src.name: @@ -161,11 +166,12 @@ def in_build(f, dest="", new_name=None, no_lib=False): source = "python_uwp.exe" sourcew = "pythonw_uwp.exe" elif ns.include_freethreaded: - source = "python{}t.exe".format(VER_DOT) - sourcew = "pythonw{}t.exe".format(VER_DOT) if not ns.include_alias: alias = [] aliasw = [] + if (VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4) < (3, 15, 0, 0xB0): + source = "python{}t.exe".format(VER_DOT) + sourcew = "pythonw{}t.exe".format(VER_DOT) alias.extend([ "python{}t".format(VER_DOT), "python{}t".format(VER_MAJOR) if ns.include_alias3 else None, @@ -196,6 +202,8 @@ def in_build(f, dest="", new_name=None, no_lib=False): yield from in_build(FREETHREADED_PYTHON_STABLE_DLL_NAME) else: yield from in_build(PYTHON_STABLE_DLL_NAME) + if (VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4) >= (3, 15, 0, 0xB0): + yield from in_build(FREETHREADED_PYTHON_STABLE_DLL_NAME) found_any = False for dest, src in rglob(ns.build, "vcruntime*.dll"): @@ -310,6 +318,9 @@ def _c(d): for dest, src in get_appx_layout(ns): yield dest, src + for dest, src in get_builddetails(ns): + yield dest, src + if ns.include_cat: if ns.flat_dlls: yield ns.include_cat.name, ns.include_cat diff --git a/PC/layout/support/builddetails.py b/PC/layout/support/builddetails.py new file mode 100644 index 00000000000000..6ef860eeb04354 --- /dev/null +++ b/PC/layout/support/builddetails.py @@ -0,0 +1,119 @@ +import io +import json +from . import constants + +_LEVELS = { + 0xA0: "alpha", + 0xB0: "beta", + 0xC0: "candidate", + 0xF0: "final", +} + + +_TEMPLATE = { + "schema_version": "1.0", + "base_prefix": ".", + "base_interpreter": "python.exe", + "platform": None, # Set later + "language": { + "version": f"{constants.VER_MAJOR}.{constants.VER_MINOR}", + "version_info": { + "major": constants.VER_MAJOR, + "minor": constants.VER_MINOR, + "micro": constants.VER_MICRO, + "releaselevel": _LEVELS.get(constants.VER_FIELD4 & 0xF0, "final"), + "serial": constants.VER_FIELD4 & 0x0F, + }, + }, + "implementation": { + "name": "cpython", + "cache_tag": f"cpython-{constants.VER_MAJOR}{constants.VER_MINOR}", + "version": { + "major": constants.VER_MAJOR, + "minor": constants.VER_MINOR, + "micro": constants.VER_MICRO, + "releaselevel": _LEVELS.get(constants.VER_FIELD4 & 0xF0, "final"), + "serial": constants.VER_FIELD4 & 0x0F, + }, + "hexversion": constants.VER_HEXVERSION, + }, + "abi": { + "flags": [], + "extension_suffix": ".pyd", + "stable_abi_suffix": ".pyd", + }, + "suffixes": { + "source": [".py", ".pyw"], + "bytecode": [".pyc"], + "extensions": [".pyd"], + }, + "libpython": { + "dynamic": constants.PYTHON_DLL_NAME, + "dynamic_stableabi": constants.PYTHON_STABLE_DLL_NAME, + "link_extensions": True, + }, + "c_api": { + }, +} + + +def _with_d(path): + pre, sep, post = path.partition(".") + return pre + "_d" + sep + post + + +def _add_d(data, *args): + for a in args[:-1]: + data = data[a] + a = args[-1] + v = data[a] + if isinstance(v, list): + data[a] = [_with_d(i) for i in data[a]] + else: + data[a] = _with_d(data[a]) + + +def get_builddetails(ns): + if not ns.include_builddetails_json: + return + + details = dict(_TEMPLATE) + + plat = { + "win32": "win32", + "amd64": "win-amd64", + "arm64": "win-arm64", + }.get(ns.arch, ns.arch) + + pyd_abi_flags = "" + if ns.include_freethreaded: + details["abi"]["flags"].append("t") + pyd_abi_flags += "t" + if ns.debug: + details["abi"]["flags"].append("d") + + norm_plat = plat.replace("-", "_") + ext_suffix = f".cp{constants.VER_MAJOR}{constants.VER_MINOR}{pyd_abi_flags}-{norm_plat}.pyd" + details["abi"]["extension_suffix"] = ext_suffix + details["suffixes"]["extensions"].insert(0, ext_suffix) + + details["platform"] = plat + + if ns.include_dev: + details["c_api"]["headers"] = "Include" + + if ns.include_freethreaded: + details["libpython"]["dynamic"] = constants.FREETHREADED_PYTHON_DLL_NAME + details["libpython"]["dynamic_stableabi"] = constants.FREETHREADED_PYTHON_STABLE_DLL_NAME + + if ns.debug: + _add_d(details, "base_interpreter") + _add_d(details, "abi", "stable_abi_suffix") + _add_d(details, "abi", "extension_suffix") + _add_d(details, "suffixes", "extensions") + _add_d(details, "libpython", "dynamic") + _add_d(details, "libpython", "dynamic_stableabi") + + buffer = io.StringIO() + json.dump(details, buffer, indent=2) + yield "build-details.json", ("build-details.json", buffer.getvalue().encode()) diff --git a/PC/layout/support/constants.py b/PC/layout/support/constants.py index 6b8c915e519743..cb16f534685c8f 100644 --- a/PC/layout/support/constants.py +++ b/PC/layout/support/constants.py @@ -23,6 +23,14 @@ def _unpack_hexversion(): return _read_patchlevel_version(pathlib.Path(os.getenv("PYTHONINCLUDE"))) except OSError: pass + # Manual search for a '-s <source dir>` arument + try: + src = sys.argv[sys.argv.index("-s") + 1] + return _read_patchlevel_version(pathlib.Path(src) / "Include") + except (IndexError, ValueError): + pass + except OSError: + pass return struct.pack(">i", sys.hexversion) @@ -68,6 +76,7 @@ def check_patchlevel_version(sources): VER_MAJOR, VER_MINOR, VER_MICRO, VER_FIELD4 = _unpack_hexversion() +VER_HEXVERSION = (VER_MAJOR << 24) | (VER_MINOR << 16) | (VER_MICRO << 8) | (VER_FIELD4) VER_SUFFIX = _get_suffix(VER_FIELD4) VER_FIELD3 = VER_MICRO << 8 | VER_FIELD4 VER_DOT = "{}.{}".format(VER_MAJOR, VER_MINOR) diff --git a/PC/layout/support/options.py b/PC/layout/support/options.py index e8c393385425e7..f67d8ba04d9070 100644 --- a/PC/layout/support/options.py +++ b/PC/layout/support/options.py @@ -39,6 +39,7 @@ def public(f): "install-json": {"help": "a PyManager __install__.json file"}, "install-embed-json": {"help": "a PyManager __install__.json file for embeddable distro"}, "install-test-json": {"help": "a PyManager __install__.json for the test distro"}, + "builddetails-json": {"help": "a PEP 739 build-details.json"}, } @@ -69,6 +70,7 @@ def public(f): "props", "nuspec", "alias", + "builddetails-json", ], }, "iot": {"help": "Windows IoT Core", "options": ["alias", "stable", "pip"]}, @@ -85,6 +87,7 @@ def public(f): "symbols", "html-doc", "alias", + "builddetails-json", ], }, "embed": { @@ -96,6 +99,7 @@ def public(f): "flat-dlls", "underpth", "precompile", + "builddetails-json", ], }, "pymanager": { @@ -108,7 +112,9 @@ def public(f): "venv", "dev", "html-doc", + "alias", "install-json", + "builddetails-json", ], }, "pymanager-test": { @@ -123,7 +129,9 @@ def public(f): "html-doc", "symbols", "tests", + "alias", "install-test-json", + "builddetails-json", ], }, } diff --git a/PC/layout/support/pymanager.py b/PC/layout/support/pymanager.py index 667c89cdd2cc7a..f6316e0295c74a 100644 --- a/PC/layout/support/pymanager.py +++ b/PC/layout/support/pymanager.py @@ -66,8 +66,9 @@ def calculate_install_json(ns, *, for_embed=False, for_test=False): if ns.include_freethreaded: # Free-threaded distro comes with a tag suffix TAG_SUFFIX = "t" - TARGET = f"python{VER_MAJOR}.{VER_MINOR}t.exe" - TARGETW = f"pythonw{VER_MAJOR}.{VER_MINOR}t.exe" + if not ns.include_alias: + TARGET = f"python{VER_MAJOR}.{VER_MINOR}t.exe" + TARGETW = f"pythonw{VER_MAJOR}.{VER_MINOR}t.exe" DISPLAY_TAGS.append("free-threaded") FILE_SUFFIX = f"t-{ns.arch}" @@ -80,7 +81,9 @@ def calculate_install_json(ns, *, for_embed=False, for_test=False): # Tag used in runtime ID (for side-by-side install/updates) ID_TAG = XY_ARCH_TAG - # Tag shown in 'py list' output + # Tag shown in 'py list' output. + # gh-139810: We only include '-dev' here for prereleases, even though it + # works for final releases too. DISPLAY_TAG = f"{XY_TAG}-dev{TAG_ARCH}" if VER_SUFFIX else XY_ARCH_TAG # Tag used for PEP 514 registration SYS_WINVER = XY_TAG + (TAG_ARCH if TAG_ARCH != '-64' else '') @@ -102,9 +105,10 @@ def calculate_install_json(ns, *, for_embed=False, for_test=False): FULL_ARCH_TAG, XY_ARCH_TAG, X_ARCH_TAG, - # X_TAG and XY_TAG doesn't include VER_SUFFIX, so create -dev versions - f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG and VER_SUFFIX else "", - f"{X_TAG}-dev{TAG_ARCH}" if X_TAG and VER_SUFFIX else "", + # gh-139810: The -dev tags are always included so that the latest + # release is chosen, no matter whether it's prerelease or final. + f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG else "", + f"{X_TAG}-dev{TAG_ARCH}" if X_TAG else "", ] # Generate run-for entries for each target. @@ -115,16 +119,15 @@ def calculate_install_json(ns, *, for_embed=False, for_test=False): ]: if not base["target"]: continue - STD_RUN_FOR.append({**base, "tag": FULL_ARCH_TAG}) + STD_RUN_FOR.extend([ + {**base, "tag": FULL_ARCH_TAG}, + {**base, "tag": f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG else ""}, + {**base, "tag": f"{X_TAG}-dev{TAG_ARCH}" if X_TAG else ""}, + ]) if XY_TAG: STD_RUN_FOR.append({**base, "tag": XY_ARCH_TAG}) if X_TAG: STD_RUN_FOR.append({**base, "tag": X_ARCH_TAG}) - if VER_SUFFIX: - STD_RUN_FOR.extend([ - {**base, "tag": f"{XY_TAG}-dev{TAG_ARCH}" if XY_TAG else ""}, - {**base, "tag": f"{X_TAG}-dev{TAG_ARCH}" if X_TAG else ""}, - ]) # Generate alias entries for each target. We need both arch and non-arch # versions as well as windowed/non-windowed versions to make sure that all diff --git a/PC/msvcrtmodule.c b/PC/msvcrtmodule.c index b170e06b47dd59..8826e7e85a7f5a 100644 --- a/PC/msvcrtmodule.c +++ b/PC/msvcrtmodule.c @@ -218,6 +218,7 @@ msvcrt_get_osfhandle_impl(PyObject *module, int fd) /* Console I/O */ /*[clinic input] +@permit_long_summary msvcrt.kbhit -> long Returns a nonzero value if a keypress is waiting to be read. Otherwise, return 0. @@ -225,7 +226,7 @@ Returns a nonzero value if a keypress is waiting to be read. Otherwise, return 0 static long msvcrt_kbhit_impl(PyObject *module) -/*[clinic end generated code: output=940dfce6587c1890 input=d0f4cb3289ff51e2]*/ +/*[clinic end generated code: output=940dfce6587c1890 input=52c0c44143f3fba5]*/ { return _kbhit(); } diff --git a/PC/pyconfig.h b/PC/pyconfig.h index 0e8379387cd025..2381ed3772b109 100644 --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -118,7 +118,7 @@ WIN32 is still required for the locale module. /* Microsoft C defines _MSC_VER, as does clang-cl.exe */ #ifdef _MSC_VER -/* We want COMPILER to expand to a string containing _MSC_VER's *value*. +/* We want _Py_COMPILER to expand to a string containing _MSC_VER's *value*. * This is horridly tricky, because the stringization operator only works * on macro arguments, and doesn't evaluate macros passed *as* arguments. */ @@ -148,7 +148,7 @@ WIN32 is still required for the locale module. #define MS_WIN64 #endif -/* set the COMPILER and support tier +/* set the _Py_COMPILER and support tier * * win_amd64 MSVC (x86_64-pc-windows-msvc): 1 * win32 MSVC (i686-pc-windows-msvc): 1 @@ -158,22 +158,22 @@ WIN32 is still required for the locale module. #ifdef MS_WIN64 #if defined(_M_X64) || defined(_M_AMD64) #if defined(__clang__) -#define COMPILER ("[Clang " __clang_version__ "] 64 bit (AMD64) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") +#define _Py_COMPILER ("[Clang " __clang_version__ "] 64 bit (AMD64) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") #define PY_SUPPORT_TIER 0 #elif defined(__INTEL_COMPILER) -#define COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 64 bit (amd64) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") +#define _Py_COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 64 bit (amd64) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") #define PY_SUPPORT_TIER 0 #else -#define COMPILER _Py_PASTE_VERSION("64 bit (AMD64)") +#define _Py_COMPILER _Py_PASTE_VERSION("64 bit (AMD64)") #define PY_SUPPORT_TIER 1 #endif /* __clang__ */ #define PYD_PLATFORM_TAG "win_amd64" #elif defined(_M_ARM64) -#define COMPILER _Py_PASTE_VERSION("64 bit (ARM64)") +#define _Py_COMPILER _Py_PASTE_VERSION("64 bit (ARM64)") #define PY_SUPPORT_TIER 3 #define PYD_PLATFORM_TAG "win_arm64" #else -#define COMPILER _Py_PASTE_VERSION("64 bit (Unknown)") +#define _Py_COMPILER _Py_PASTE_VERSION("64 bit (Unknown)") #define PY_SUPPORT_TIER 0 #endif #endif /* MS_WIN64 */ @@ -220,22 +220,22 @@ typedef _W64 int Py_ssize_t; #if defined(MS_WIN32) && !defined(MS_WIN64) #if defined(_M_IX86) #if defined(__clang__) -#define COMPILER ("[Clang " __clang_version__ "] 32 bit (Intel) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") +#define _Py_COMPILER ("[Clang " __clang_version__ "] 32 bit (Intel) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") #define PY_SUPPORT_TIER 0 #elif defined(__INTEL_COMPILER) -#define COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 32 bit (Intel) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") +#define _Py_COMPILER ("[ICC v." _Py_STRINGIZE(__INTEL_COMPILER) " 32 bit (Intel) with MSC v." _Py_STRINGIZE(_MSC_VER) " CRT]") #define PY_SUPPORT_TIER 0 #else -#define COMPILER _Py_PASTE_VERSION("32 bit (Intel)") +#define _Py_COMPILER _Py_PASTE_VERSION("32 bit (Intel)") #define PY_SUPPORT_TIER 1 #endif /* __clang__ */ #define PYD_PLATFORM_TAG "win32" #elif defined(_M_ARM) -#define COMPILER _Py_PASTE_VERSION("32 bit (ARM)") +#define _Py_COMPILER _Py_PASTE_VERSION("32 bit (ARM)") #define PYD_PLATFORM_TAG "win_arm32" #define PY_SUPPORT_TIER 0 #else -#define COMPILER _Py_PASTE_VERSION("32 bit (Unknown)") +#define _Py_COMPILER _Py_PASTE_VERSION("32 bit (Unknown)") #define PY_SUPPORT_TIER 0 #endif #endif /* MS_WIN32 && !MS_WIN64 */ @@ -273,7 +273,7 @@ typedef int pid_t; #warning "Please use an up-to-date version of gcc! (>2.91 recommended)" #endif -#define COMPILER "[gcc]" +#define _Py_COMPILER "[gcc]" #define PY_LONG_LONG long long #define PY_LLONG_MIN LLONG_MIN #define PY_LLONG_MAX LLONG_MAX @@ -286,7 +286,7 @@ typedef int pid_t; /* XXX These defines are likely incomplete, but should be easy to fix. They should be complete enough to build extension modules. */ -#define COMPILER "[lcc-win32]" +#define _Py_COMPILER "[lcc-win32]" typedef int pid_t; /* __declspec() is supported here too - do nothing to get the defaults */ @@ -330,19 +330,21 @@ Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ the linking is explicitly handled */ # if defined(Py_GIL_DISABLED) # if defined(Py_DEBUG) -# pragma comment(lib,"python315t_d.lib") -# elif defined(Py_LIMITED_API) +# pragma comment(lib,"python316t_d.lib") +# elif defined(Py_LIMITED_API) || defined(Py_TARGET_ABI3T) # pragma comment(lib,"python3t.lib") # else -# pragma comment(lib,"python315t.lib") +# pragma comment(lib,"python316t.lib") # endif /* Py_DEBUG */ # else /* Py_GIL_DISABLED */ # if defined(Py_DEBUG) -# pragma comment(lib,"python315_d.lib") +# pragma comment(lib,"python316_d.lib") +# elif defined(Py_TARGET_ABI3T) +# pragma comment(lib,"python3t.lib") # elif defined(Py_LIMITED_API) # pragma comment(lib,"python3.lib") # else -# pragma comment(lib,"python315.lib") +# pragma comment(lib,"python316.lib") # endif /* Py_DEBUG */ # endif /* Py_GIL_DISABLED */ # endif /* _MSC_VER && !Py_NO_LINK_LIB */ diff --git a/PC/python3dll.c b/PC/python3dll.c index 8ec791f8280f13..e0be9d65a93cda 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -71,6 +71,7 @@ EXPORT_FUNC(Py_IncRef) EXPORT_FUNC(Py_Initialize) EXPORT_FUNC(Py_InitializeEx) EXPORT_FUNC(Py_Is) +EXPORT_FUNC(Py_IS_TYPE) EXPORT_FUNC(Py_IsFalse) EXPORT_FUNC(Py_IsFinalizing) EXPORT_FUNC(Py_IsInitialized) @@ -86,13 +87,16 @@ EXPORT_FUNC(Py_PACK_VERSION) EXPORT_FUNC(Py_REFCNT) EXPORT_FUNC(Py_ReprEnter) EXPORT_FUNC(Py_ReprLeave) +EXPORT_FUNC(Py_SET_SIZE) EXPORT_FUNC(Py_SetPath) EXPORT_FUNC(Py_SetProgramName) EXPORT_FUNC(Py_SetPythonHome) EXPORT_FUNC(Py_SetRecursionLimit) +EXPORT_FUNC(Py_SIZE) EXPORT_FUNC(Py_TYPE) EXPORT_FUNC(Py_VaBuildValue) EXPORT_FUNC(Py_XNewRef) +EXPORT_FUNC(PyABIInfo_Check) EXPORT_FUNC(PyAIter_Check) EXPORT_FUNC(PyArg_Parse) EXPORT_FUNC(PyArg_ParseTuple) @@ -170,6 +174,10 @@ EXPORT_FUNC(PyCodec_XMLCharRefReplaceErrors) EXPORT_FUNC(PyComplex_FromDoubles) EXPORT_FUNC(PyComplex_ImagAsDouble) EXPORT_FUNC(PyComplex_RealAsDouble) +EXPORT_FUNC(PyCriticalSection2_Begin) +EXPORT_FUNC(PyCriticalSection2_End) +EXPORT_FUNC(PyCriticalSection_Begin) +EXPORT_FUNC(PyCriticalSection_End) EXPORT_FUNC(PyDescr_NewClassMethod) EXPORT_FUNC(PyDescr_NewGetSet) EXPORT_FUNC(PyDescr_NewMember) @@ -190,6 +198,7 @@ EXPORT_FUNC(PyDict_Merge) EXPORT_FUNC(PyDict_MergeFromSeq2) EXPORT_FUNC(PyDict_New) EXPORT_FUNC(PyDict_Next) +EXPORT_FUNC(PyDict_SetDefaultRef) EXPORT_FUNC(PyDict_SetItem) EXPORT_FUNC(PyDict_SetItemString) EXPORT_FUNC(PyDict_Size) @@ -321,12 +330,18 @@ EXPORT_FUNC(PyImport_ImportModuleLevelObject) EXPORT_FUNC(PyImport_ImportModuleNoBlock) EXPORT_FUNC(PyImport_ReloadModule) EXPORT_FUNC(PyIndex_Check) +EXPORT_FUNC(PyInterpreterGuard_Close) +EXPORT_FUNC(PyInterpreterGuard_FromCurrent) +EXPORT_FUNC(PyInterpreterGuard_FromView) EXPORT_FUNC(PyInterpreterState_Clear) EXPORT_FUNC(PyInterpreterState_Delete) EXPORT_FUNC(PyInterpreterState_Get) EXPORT_FUNC(PyInterpreterState_GetDict) EXPORT_FUNC(PyInterpreterState_GetID) EXPORT_FUNC(PyInterpreterState_New) +EXPORT_FUNC(PyInterpreterView_Close) +EXPORT_FUNC(PyInterpreterView_FromCurrent) +EXPORT_FUNC(PyInterpreterView_FromMain) EXPORT_FUNC(PyIter_Check) EXPORT_FUNC(PyIter_Next) EXPORT_FUNC(PyIter_NextItem) @@ -361,6 +376,8 @@ EXPORT_FUNC(PyLong_AsUnsignedLongLong) EXPORT_FUNC(PyLong_AsUnsignedLongLongMask) EXPORT_FUNC(PyLong_AsUnsignedLongMask) EXPORT_FUNC(PyLong_AsVoidPtr) +EXPORT_FUNC(PyLong_Export) +EXPORT_FUNC(PyLong_FreeExport) EXPORT_FUNC(PyLong_FromDouble) EXPORT_FUNC(PyLong_FromInt32) EXPORT_FUNC(PyLong_FromInt64) @@ -377,6 +394,10 @@ EXPORT_FUNC(PyLong_FromUnsignedLongLong) EXPORT_FUNC(PyLong_FromUnsignedNativeBytes) EXPORT_FUNC(PyLong_FromVoidPtr) EXPORT_FUNC(PyLong_GetInfo) +EXPORT_FUNC(PyLong_GetNativeLayout) +EXPORT_FUNC(PyLongWriter_Create) +EXPORT_FUNC(PyLongWriter_Discard) +EXPORT_FUNC(PyLongWriter_Finish) EXPORT_FUNC(PyMapping_Check) EXPORT_FUNC(PyMapping_GetItemString) EXPORT_FUNC(PyMapping_GetOptionalItem) @@ -415,8 +436,10 @@ EXPORT_FUNC(PyModule_AddObjectRef) EXPORT_FUNC(PyModule_AddStringConstant) EXPORT_FUNC(PyModule_AddType) EXPORT_FUNC(PyModule_Create2) +EXPORT_FUNC(PyModule_Exec) EXPORT_FUNC(PyModule_ExecDef) EXPORT_FUNC(PyModule_FromDefAndSpec2) +EXPORT_FUNC(PyModule_FromSlotsAndSpec) EXPORT_FUNC(PyModule_GetDef) EXPORT_FUNC(PyModule_GetDict) EXPORT_FUNC(PyModule_GetFilename) @@ -424,6 +447,10 @@ EXPORT_FUNC(PyModule_GetFilenameObject) EXPORT_FUNC(PyModule_GetName) EXPORT_FUNC(PyModule_GetNameObject) EXPORT_FUNC(PyModule_GetState) +EXPORT_FUNC(PyModule_GetState_DuringGC) +EXPORT_FUNC(PyModule_GetStateSize) +EXPORT_FUNC(PyModule_GetToken) +EXPORT_FUNC(PyModule_GetToken_DuringGC) EXPORT_FUNC(PyModule_New) EXPORT_FUNC(PyModule_NewObject) EXPORT_FUNC(PyModule_SetDocString) @@ -472,6 +499,7 @@ EXPORT_FUNC(PyObject_AsReadBuffer) EXPORT_FUNC(PyObject_AsWriteBuffer) EXPORT_FUNC(PyObject_Bytes) EXPORT_FUNC(PyObject_Call) +EXPORT_FUNC(PyObject_CallFinalizerFromDealloc) EXPORT_FUNC(PyObject_CallFunction) EXPORT_FUNC(PyObject_CallFunctionObjArgs) EXPORT_FUNC(PyObject_CallMethod) @@ -508,6 +536,7 @@ EXPORT_FUNC(PyObject_GetIter) EXPORT_FUNC(PyObject_GetOptionalAttr) EXPORT_FUNC(PyObject_GetOptionalAttrString) EXPORT_FUNC(PyObject_GetTypeData) +EXPORT_FUNC(PyObject_GetTypeData_DuringGC) EXPORT_FUNC(PyObject_HasAttr) EXPORT_FUNC(PyObject_HasAttrString) EXPORT_FUNC(PyObject_HasAttrStringWithError) @@ -638,12 +667,15 @@ EXPORT_FUNC(PyThread_tss_set) EXPORT_FUNC(PyThreadState_Clear) EXPORT_FUNC(PyThreadState_Delete) EXPORT_FUNC(PyThreadState_DeleteCurrent) +EXPORT_FUNC(PyThreadState_Ensure) +EXPORT_FUNC(PyThreadState_EnsureFromView) EXPORT_FUNC(PyThreadState_Get) EXPORT_FUNC(PyThreadState_GetDict) EXPORT_FUNC(PyThreadState_GetFrame) EXPORT_FUNC(PyThreadState_GetID) EXPORT_FUNC(PyThreadState_GetInterpreter) EXPORT_FUNC(PyThreadState_New) +EXPORT_FUNC(PyThreadState_Release) EXPORT_FUNC(PyThreadState_SetAsyncExc) EXPORT_FUNC(PyThreadState_Swap) EXPORT_FUNC(PyTraceBack_Here) @@ -658,17 +690,23 @@ EXPORT_FUNC(PyType_ClearCache) EXPORT_FUNC(PyType_Freeze) EXPORT_FUNC(PyType_FromMetaclass) EXPORT_FUNC(PyType_FromModuleAndSpec) +EXPORT_FUNC(PyType_FromSlots) EXPORT_FUNC(PyType_FromSpec) EXPORT_FUNC(PyType_FromSpecWithBases) EXPORT_FUNC(PyType_GenericAlloc) EXPORT_FUNC(PyType_GenericNew) EXPORT_FUNC(PyType_GetBaseByToken) +EXPORT_FUNC(PyType_GetBaseByToken_DuringGC) EXPORT_FUNC(PyType_GetFlags) EXPORT_FUNC(PyType_GetFullyQualifiedName) EXPORT_FUNC(PyType_GetModule) +EXPORT_FUNC(PyType_GetModule_DuringGC) EXPORT_FUNC(PyType_GetModuleByDef) +EXPORT_FUNC(PyType_GetModuleByToken) +EXPORT_FUNC(PyType_GetModuleByToken_DuringGC) EXPORT_FUNC(PyType_GetModuleName) EXPORT_FUNC(PyType_GetModuleState) +EXPORT_FUNC(PyType_GetModuleState_DuringGC) EXPORT_FUNC(PyType_GetName) EXPORT_FUNC(PyType_GetQualName) EXPORT_FUNC(PyType_GetSlot) diff --git a/PC/python_uwp.cpp b/PC/python_uwp.cpp index 8cdb8d722cdb9a..1b44216dc20d1e 100644 --- a/PC/python_uwp.cpp +++ b/PC/python_uwp.cpp @@ -13,6 +13,7 @@ #if defined(__clang__) #define _SILENCE_CLANG_COROUTINE_MESSAGE #endif +#define _SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS #include <appmodel.h> #include <winrt\Windows.ApplicationModel.h> diff --git a/PC/python_ver_rc.h b/PC/python_ver_rc.h index bb98144cd03f15..70c805d3da8427 100644 --- a/PC/python_ver_rc.h +++ b/PC/python_ver_rc.h @@ -8,7 +8,6 @@ #define PYTHON_COPYRIGHT "Copyright \xA9 2001 Python Software Foundation. Copyright \xA9 2000 BeOpen.com. Copyright \xA9 1995-2001 CNRI. Copyright \xA9 1991-1995 SMC." #define MS_WINDOWS -#include "modsupport.h" #include "patchlevel.h" #ifdef Py_DEBUG # define PYTHON_DEBUG_EXT "_d" diff --git a/PC/winreg.c b/PC/winreg.c index d1a1c3d1c97850..26bcd259efd987 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -49,8 +49,11 @@ PyDoc_STRVAR(module_doc, "ConnectRegistry() - Establishes a connection to a predefined registry handle\n" " on another computer.\n" "CreateKey() - Creates the specified key, or opens it if it already exists.\n" +"CreateKeyEx() - Creates the specified key, or opens it if it already exists.\n" "DeleteKey() - Deletes the specified key.\n" +"DeleteKeyEx() - Deletes the specified key.\n" "DeleteValue() - Removes a named value from the specified registry key.\n" +"DeleteTree() - Deletes the specified key and all its subkeys and values recursively.\n" "EnumKey() - Enumerates subkeys of the specified open registry key.\n" "EnumValue() - Enumerates values of the specified open registry key.\n" "ExpandEnvironmentStrings() - Expand the env strings in a REG_EXPAND_SZ\n" @@ -69,6 +72,9 @@ PyDoc_STRVAR(module_doc, "SaveKey() - Saves the specified key, and all its subkeys a file.\n" "SetValue() - Associates a value with a specified key.\n" "SetValueEx() - Stores data in the value field of an open registry key.\n" +"DisableReflectionKey() - Disables registry reflection for 32bit processes running on a 64bit OS.\n" +"EnableReflectionKey() - Restores registry reflection for a key.\n" +"QueryReflectionKey() - Determines the reflection state for a key.\n" "\n" "Special objects:\n" "\n" @@ -102,7 +108,9 @@ PyDoc_STRVAR(PyHKEY_doc, "Operations:\n" "__bool__ - Handles with an open object return true, otherwise false.\n" "__int__ - Converting a handle to an integer returns the Win32 handle.\n" -"rich comparison - Handle objects are compared using the handle value."); +"__enter__, __exit__ - Context manager support for 'with' statement,\n" +"automatically closes handle.\n" +"__eq__, __ne__ - Equality comparison based on Windows handle value."); @@ -156,13 +164,6 @@ PyHKEY_deallocFunc(PyObject *ob) Py_DECREF(tp); } -static int -PyHKEY_traverseFunc(PyObject *self, visitproc visit, void *arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - static int PyHKEY_boolFunc(PyObject *ob) { @@ -183,13 +184,38 @@ PyHKEY_strFunc(PyObject *ob) return PyUnicode_FromFormat("<PyHKEY:%p>", pyhkey->hkey); } -static int -PyHKEY_compareFunc(PyObject *ob1, PyObject *ob2) +static PyObject * +PyHKEY_richcompare(PyObject *ob1, PyObject *ob2, int op) { + /* Both objects must be PyHKEY objects from the same module */ + if (Py_TYPE(ob1) != Py_TYPE(ob2)) { + Py_RETURN_NOTIMPLEMENTED; + } + PyHKEYObject *pyhkey1 = (PyHKEYObject *)ob1; PyHKEYObject *pyhkey2 = (PyHKEYObject *)ob2; - return pyhkey1 == pyhkey2 ? 0 : - (pyhkey1 < pyhkey2 ? -1 : 1); + HKEY hkey1 = pyhkey1->hkey; + HKEY hkey2 = pyhkey2->hkey; + int result; + + switch (op) { + case Py_EQ: + result = (hkey1 == hkey2); + break; + case Py_NE: + result = (hkey1 != hkey2); + break; + default: + /* Only support equality comparisons, not ordering */ + Py_RETURN_NOTIMPLEMENTED; + } + + if (result) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } } static Py_hash_t @@ -238,19 +264,8 @@ class HKEY_return_converter(CReturnConverter): self.err_occurred_if_null_pointer("_return_value", data) data.return_conversion.append( 'return_value = PyHKEY_FromHKEY(_PyModule_GetState(module), _return_value);\n') - -# HACK: this only works for PyHKEYObjects, nothing else. -# Should this be generalized and enshrined in clinic.py, -# destroy this converter with prejudice. -class self_return_converter(CReturnConverter): - type = 'PyHKEYObject *' - - def render(self, function, data): - self.declare(data) - data.return_conversion.append( - 'return_value = (PyObject *)_return_value;\n') [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=4979f33998ffb6f8]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=b34c8217647f5fef]*/ #include "clinic/winreg.c.h" @@ -307,14 +322,14 @@ winreg_HKEYType_Detach_impl(PyHKEYObject *self) } /*[clinic input] -winreg.HKEYType.__enter__ -> self +winreg.HKEYType.__enter__ [clinic start generated code]*/ -static PyHKEYObject * +static PyObject * winreg_HKEYType___enter___impl(PyHKEYObject *self) -/*[clinic end generated code: output=52c34986dab28990 input=c40fab1f0690a8e2]*/ +/*[clinic end generated code: output=70ec10933068a08c input=85f6abf60774c88c]*/ { - return (PyHKEYObject*)Py_XNewRef(self); + return Py_XNewRef(self); } @@ -364,9 +379,10 @@ static PyType_Slot pyhkey_type_slots[] = { {Py_tp_members, PyHKEY_memberlist}, {Py_tp_methods, PyHKEY_methods}, {Py_tp_doc, (char *)PyHKEY_doc}, - {Py_tp_traverse, PyHKEY_traverseFunc}, + {Py_tp_traverse, _PyObject_VisitType}, {Py_tp_hash, PyHKEY_hashFunc}, {Py_tp_str, PyHKEY_strFunc}, + {Py_tp_richcompare, PyHKEY_richcompare}, // Number protocol {Py_nb_add, PyHKEY_binaryFailureFunc}, @@ -401,19 +417,6 @@ static PyType_Spec pyhkey_type_spec = { /************************************************************************ The public PyHKEY API (well, not public yet :-) ************************************************************************/ -PyObject * -PyHKEY_New(PyObject *m, HKEY hInit) -{ - winreg_state *st = _PyModule_GetState(m); - PyHKEYObject *key = PyObject_GC_New(PyHKEYObject, st->PyHKEY_Type); - if (key == NULL) { - return NULL; - } - key->hkey = hInit; - PyObject_GC_Track(key); - return (PyObject *)key; -} - BOOL PyHKEY_Close(winreg_state *st, PyObject *ob_handle) { @@ -489,48 +492,6 @@ PyHKEY_FromHKEY(winreg_state *st, HKEY h) } -/************************************************************************ - The module methods -************************************************************************/ -BOOL -PyWinObject_CloseHKEY(winreg_state *st, PyObject *obHandle) -{ - BOOL ok; - if (PyHKEY_Check(st, obHandle)) { - ok = PyHKEY_Close(st, obHandle); - } -#if SIZEOF_LONG >= SIZEOF_HKEY - else if (PyLong_Check(obHandle)) { - long rc; - Py_BEGIN_ALLOW_THREADS - rc = RegCloseKey((HKEY)PyLong_AsLong(obHandle)); - Py_END_ALLOW_THREADS - ok = (rc == ERROR_SUCCESS); - if (!ok) - PyErr_SetFromWindowsErrWithFunction(rc, "RegCloseKey"); - } -#else - else if (PyLong_Check(obHandle)) { - long rc; - HKEY hkey = (HKEY)PyLong_AsVoidPtr(obHandle); - Py_BEGIN_ALLOW_THREADS - rc = RegCloseKey(hkey); - Py_END_ALLOW_THREADS - ok = (rc == ERROR_SUCCESS); - if (!ok) - PyErr_SetFromWindowsErrWithFunction(rc, "RegCloseKey"); - } -#endif - else { - PyErr_SetString( - PyExc_TypeError, - "A handle must be a HKEY object or an integer"); - return FALSE; - } - return ok; -} - - /* Private Helper functions for the registry interfaces @@ -1679,7 +1640,7 @@ winreg_QueryValueEx_impl(PyObject *module, HKEY key, const wchar_t *name) return PyErr_SetFromWindowsErrWithFunction(rc, "RegQueryValueEx"); } - obData = Reg2Py(retBuf, bufSize, typ); + obData = Reg2Py(retBuf, retSize, typ); PyMem_Free(retBuf); if (obData == NULL) return NULL; @@ -2021,6 +1982,45 @@ winreg_EnableReflectionKey_impl(PyObject *module, HKEY key) Py_RETURN_NONE; } +/*[clinic input] +winreg.DeleteTree + + key: HKEY + An already open key, or any one of the predefined HKEY_* constants. + sub_key: Py_UNICODE(accept={str, NoneType}) = None + A string that names the subkey to delete. If None, deletes all subkeys + and values of the specified key. + / + +Deletes the specified key and all its subkeys and values recursively. + +This function deletes a key and all its descendants. If sub_key is None, +all subkeys and values of the specified key are deleted. +[clinic start generated code]*/ + +static PyObject * +winreg_DeleteTree_impl(PyObject *module, HKEY key, const wchar_t *sub_key) +/*[clinic end generated code: output=c34395ee59290501 input=419ef9bb8b06e4bf]*/ +{ + LONG rc; + + if (PySys_Audit("winreg.DeleteTree", "nu", + (Py_ssize_t)key, sub_key) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + rc = RegDeleteTreeW(key, sub_key); + Py_END_ALLOW_THREADS + + if (rc != ERROR_SUCCESS) { + PyErr_SetFromWindowsErrWithFunction(rc, "RegDeleteTreeW"); + return NULL; + } + + Py_RETURN_NONE; +} + /*[clinic input] winreg.QueryReflectionKey @@ -2079,6 +2079,7 @@ static struct PyMethodDef winreg_methods[] = { WINREG_DELETEKEY_METHODDEF WINREG_DELETEKEYEX_METHODDEF WINREG_DELETEVALUE_METHODDEF + WINREG_DELETETREE_METHODDEF WINREG_DISABLEREFLECTIONKEY_METHODDEF WINREG_ENABLEREFLECTIONKEY_METHODDEF WINREG_ENUMKEY_METHODDEF diff --git a/PCbuild/_decimal.vcxproj b/PCbuild/_decimal.vcxproj index ee7421484b5312..3ba49370d583dd 100644 --- a/PCbuild/_decimal.vcxproj +++ b/PCbuild/_decimal.vcxproj @@ -108,7 +108,6 @@ <ClInclude Include="$(mpdecimalDir)\libmpdec\convolute.h" /> <ClInclude Include="$(mpdecimalDir)\libmpdec\crt.h" /> <ClInclude Include="$(mpdecimalDir)\libmpdec\difradix2.h" /> - <ClInclude Include="..\Modules\_decimal\docstrings.h" /> <ClInclude Include="$(mpdecimalDir)\libmpdec\fnt.h" /> <ClInclude Include="$(mpdecimalDir)\libmpdec\fourstep.h" /> <ClInclude Include="..\Modules\_decimal\windows\mpdecimal.h" /> diff --git a/PCbuild/_decimal.vcxproj.filters b/PCbuild/_decimal.vcxproj.filters index e4bdb64ec1fb9f..7cb6b867a47c78 100644 --- a/PCbuild/_decimal.vcxproj.filters +++ b/PCbuild/_decimal.vcxproj.filters @@ -18,9 +18,6 @@ </Filter> </ItemGroup> <ItemGroup> - <ClInclude Include="..\Modules\_decimal\docstrings.h"> - <Filter>Header Files</Filter> - </ClInclude> <ClInclude Include="$(mpdecimalDir)\libmpdec\basearith.h"> <Filter>Header Files\libmpdec</Filter> </ClInclude> diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj index 5ceddf759b8f3b..17b98c9d9ec345 100644 --- a/PCbuild/_freeze_module.vcxproj +++ b/PCbuild/_freeze_module.vcxproj @@ -146,6 +146,7 @@ <ClCompile Include="..\Objects\genobject.c" /> <ClCompile Include="..\Objects\interpolationobject.c" /> <ClCompile Include="..\Objects\iterobject.c" /> + <ClCompile Include="..\Objects\lazyimportobject.c" /> <ClCompile Include="..\Objects\listobject.c" /> <ClCompile Include="..\Objects\longobject.c" /> <ClCompile Include="..\Objects\memoryobject.c" /> @@ -157,6 +158,7 @@ <ClCompile Include="..\Objects\odictobject.c" /> <ClCompile Include="..\Objects\picklebufobject.c" /> <ClCompile Include="..\Objects\rangeobject.c" /> + <ClCompile Include="..\Objects\sentinelobject.c" /> <ClCompile Include="..\Objects\setobject.c" /> <ClCompile Include="..\Objects\sliceobject.c" /> <ClCompile Include="..\Objects\structseq.c" /> @@ -164,7 +166,10 @@ <ClCompile Include="..\Objects\tupleobject.c" /> <ClCompile Include="..\Objects\typeobject.c" /> <ClCompile Include="..\Objects\typevarobject.c" /> + <ClCompile Include="..\Objects\unicode_format.c" /> <ClCompile Include="..\Objects\unicodectype.c" /> + <ClCompile Include="..\Objects\unicode_formatter.c" /> + <ClCompile Include="..\Objects\unicode_writer.c" /> <ClCompile Include="..\Objects\unicodeobject.c" /> <ClCompile Include="..\Objects\unionobject.c" /> <ClCompile Include="..\Objects\weakrefobject.c" /> @@ -209,7 +214,6 @@ <ClCompile Include="..\Python\errors.c" /> <ClCompile Include="..\Python\fileutils.c" /> <ClCompile Include="..\Python\flowgraph.c" /> - <ClCompile Include="..\Python\formatter_unicode.c" /> <ClCompile Include="..\Python\frame.c" /> <ClCompile Include="..\Python\future.c" /> <ClCompile Include="..\Python\gc.c" /> @@ -233,6 +237,7 @@ <ClCompile Include="..\Python\intrinsics.c" /> <ClCompile Include="..\Python\instrumentation.c" /> <ClCompile Include="..\Python\jit.c" /> + <ClCompile Include="..\Python\jit_publish.c" /> <ClCompile Include="..\Python\legacy_tracing.c" /> <ClCompile Include="..\Python\lock.c" /> <ClCompile Include="..\Python\marshal.c" /> @@ -255,6 +260,7 @@ <ClCompile Include="..\Python\pylifecycle.c" /> <ClCompile Include="..\Python\pymath.c" /> <ClCompile Include="..\Python\pystate.c" /> + <ClCompile Include="..\Python\pystats.c" /> <ClCompile Include="..\Python\pystrcmp.c" /> <ClCompile Include="..\Python\pystrhex.c" /> <ClCompile Include="..\Python\pystrtod.c" /> @@ -264,6 +270,8 @@ <ClCompile Include="..\Python\pytime.c" /> <ClCompile Include="..\Python\qsbr.c" /> <ClCompile Include="..\Python\remote_debugging.c" /> + <ClCompile Include="..\Python\slots.c" /> + <ClCompile Include="..\Python\slots_generated.c" /> <ClCompile Include="..\Python\specialize.c" /> <ClCompile Include="..\Python\structmember.c" /> <ClCompile Include="..\Python\suggestions.c" /> @@ -306,6 +314,26 @@ <IntFile>$(IntDir)codecs.g.h</IntFile> <OutFile>$(GeneratedFrozenModulesDir)Python\frozen_modules\codecs.h</OutFile> </None> + <None Include="..\Lib\encodings\__init__.py"> + <ModName>encodings</ModName> + <IntFile>$(IntDir)encodings.g.h</IntFile> + <OutFile>$(GeneratedFrozenModulesDir)Python\frozen_modules\encodings.h</OutFile> + </None> + <None Include="..\Lib\encodings\aliases.py"> + <ModName>encodings.aliases</ModName> + <IntFile>$(IntDir)encodings.aliases.g.h</IntFile> + <OutFile>$(GeneratedFrozenModulesDir)Python\frozen_modules\encodings.aliases.h</OutFile> + </None> + <None Include="..\Lib\encodings\utf_8.py"> + <ModName>encodings.utf_8</ModName> + <IntFile>$(IntDir)encodings.utf_8.g.h</IntFile> + <OutFile>$(GeneratedFrozenModulesDir)Python\frozen_modules\encodings.utf_8.h</OutFile> + </None> + <None Include="..\Lib\encodings\_win_cp_codecs.py"> + <ModName>encodings._win_cp_codecs</ModName> + <IntFile>$(IntDir)encodings._win_cp_codecs.g.h</IntFile> + <OutFile>$(GeneratedFrozenModulesDir)Python\frozen_modules\encodings._win_cp_codecs.h</OutFile> + </None> <None Include="..\Lib\io.py"> <ModName>io</ModName> <IntFile>$(IntDir)io.g.h</IntFile> @@ -351,6 +379,11 @@ <IntFile>$(IntDir)stat.g.h</IntFile> <OutFile>$(GeneratedFrozenModulesDir)Python\frozen_modules\stat.h</OutFile> </None> + <None Include="..\Lib\linecache.py"> + <ModName>linecache</ModName> + <IntFile>$(IntDir)linecache.g.h</IntFile> + <OutFile>$(GeneratedFrozenModulesDir)Python\frozen_modules\linecache.h</OutFile> + </None> <None Include="..\Lib\importlib\util.py"> <ModName>importlib.util</ModName> <IntFile>$(IntDir)importlib.util.g.h</IntFile> diff --git a/PCbuild/_freeze_module.vcxproj.filters b/PCbuild/_freeze_module.vcxproj.filters index 332d466b1f7409..af3fded0dabf2d 100644 --- a/PCbuild/_freeze_module.vcxproj.filters +++ b/PCbuild/_freeze_module.vcxproj.filters @@ -160,9 +160,6 @@ <ClCompile Include="..\Python\flowgraph.c"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\Python\formatter_unicode.c"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\Python\frame.c"> <Filter>Source Files</Filter> </ClCompile> @@ -268,6 +265,12 @@ <ClCompile Include="..\Python\jit.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\Python\jit_publish.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Objects\lazyimportobject.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\Objects\listobject.c"> <Filter>Source Files</Filter> </ClCompile> @@ -370,6 +373,9 @@ <ClCompile Include="..\Python\pystate.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\Python\pystats.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\Python\pystrcmp.c"> <Filter>Source Files</Filter> </ClCompile> @@ -397,6 +403,9 @@ <ClCompile Include="..\Objects\rangeobject.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\Objects\sentinelobject.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\Objects\setobject.c"> <Filter>Source Files</Filter> </ClCompile> @@ -409,6 +418,12 @@ <ClCompile Include="..\Python\remote_debugging.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\Python\slots.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Python\slots_generated.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\Python\specialize.c"> <Filter>Source Files</Filter> </ClCompile> @@ -484,9 +499,18 @@ <ClCompile Include="..\Objects\typeobject.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\Objects\unicode_format.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\Objects\unicodectype.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\Objects\unicode_formatter.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Objects\unicode_writer.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\Objects\unicodeobject.c"> <Filter>Source Files</Filter> </ClCompile> @@ -525,6 +549,18 @@ <None Include="..\Lib\codecs.py"> <Filter>Python Files</Filter> </None> + <None Include="..\Lib\encodings\__init__.py"> + <Filter>Python Files</Filter> + </None> + <None Include="..\Lib\encodings\aliases.py"> + <Filter>Python Files</Filter> + </None> + <None Include="..\Lib\encodings\utf_8.py"> + <Filter>Python Files</Filter> + </None> + <None Include="..\Lib\encodings\_win_cp_codecs.py"> + <Filter>Python Files</Filter> + </None> <None Include="..\Lib\io.py"> <Filter>Python Files</Filter> </None> @@ -552,6 +588,9 @@ <None Include="..\Lib\stat.py"> <Filter>Python Files</Filter> </None> + <None Include="..\Lib\linecache.py"> + <Filter>Python Files</Filter> + </None> <None Include="..\Lib\importlib\util.py"> <Filter>Python Files</Filter> </None> diff --git a/PCbuild/_remote_debugging.vcxproj b/PCbuild/_remote_debugging.vcxproj index c55f2908e03d33..41aadbd13d0995 100644 --- a/PCbuild/_remote_debugging.vcxproj +++ b/PCbuild/_remote_debugging.vcxproj @@ -92,8 +92,29 @@ <PropertyGroup> <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> </PropertyGroup> + <ItemDefinitionGroup> + <Link> + <AdditionalDependencies>ntdll.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> +</ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="..\Modules\_remote_debugging_module.c" /> + <ClCompile Include="..\Modules\_remote_debugging\module.c" /> + <ClCompile Include="..\Modules\_remote_debugging\gc_stats.c" /> + <ClCompile Include="..\Modules\_remote_debugging\object_reading.c" /> + <ClCompile Include="..\Modules\_remote_debugging\code_objects.c" /> + <ClCompile Include="..\Modules\_remote_debugging\frames.c" /> + <ClCompile Include="..\Modules\_remote_debugging\frame_cache.c" /> + <ClCompile Include="..\Modules\_remote_debugging\threads.c" /> + <ClCompile Include="..\Modules\_remote_debugging\asyncio.c" /> + <ClCompile Include="..\Modules\_remote_debugging\binary_io_writer.c" /> + <ClCompile Include="..\Modules\_remote_debugging\binary_io_reader.c" /> + <ClCompile Include="..\Modules\_remote_debugging\subprocess.c" /> + <ClCompile Include="..\Modules\_remote_debugging\interpreters.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\Modules\_remote_debugging\_remote_debugging.h" /> + <ClInclude Include="..\Modules\_remote_debugging\binary_io.h" /> + <ClInclude Include="..\Modules\_remote_debugging\gc_stats.h" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\PC\python_nt.rc" /> @@ -107,6 +128,10 @@ <Project>{885d4898-d08d-4091-9c40-c700cfe3fc5a}</Project> <ReferenceOutputAssembly>false</ReferenceOutputAssembly> </ProjectReference> + <ProjectReference Include="python3tdll.vcxproj"> + <Project>{947BB5F5-6025-4A4F-8182-1B175469F8D2}</Project> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/PCbuild/_remote_debugging.vcxproj.filters b/PCbuild/_remote_debugging.vcxproj.filters index ce4437f74e00cd..001f214805f93f 100644 --- a/PCbuild/_remote_debugging.vcxproj.filters +++ b/PCbuild/_remote_debugging.vcxproj.filters @@ -4,12 +4,61 @@ <Filter Include="Source Files"> <UniqueIdentifier>{6d101329-41df-49a0-8639-f35408ad7c6d}</UniqueIdentifier> </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + </Filter> <Filter Include="Resource Files"> <UniqueIdentifier>{711941d1-269c-49cb-a733-759b2b91fc61}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> - <ClCompile Include="..\Modules\_remote_debugging_module.c" /> + <ClCompile Include="..\Modules\_remote_debugging\module.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\gc_stats.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\object_reading.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\code_objects.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\frames.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\frame_cache.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\threads.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\asyncio.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\binary_io_writer.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\binary_io_reader.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\subprocess.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_remote_debugging\interpreters.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\Modules\_remote_debugging\_remote_debugging.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\Modules\_remote_debugging\binary_io.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\Modules\_remote_debugging\gc_stats.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\PC\python_nt.rc"> diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index a68f15d25aabb7..64e50b67be4656 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -125,12 +125,15 @@ <ClCompile Include="..\Modules\_testcapi\immortal.c" /> <ClCompile Include="..\Modules\_testcapi\gc.c" /> <ClCompile Include="..\Modules\_testcapi\run.c" /> + <ClCompile Include="..\Modules\_testcapi\modsupport.c" /> + <ClCompile Include="..\Modules\_testcapi\module.c" /> <ClCompile Include="..\Modules\_testcapi\monitoring.c" /> <ClCompile Include="..\Modules\_testcapi\config.c" /> <ClCompile Include="..\Modules\_testcapi\import.c" /> <ClCompile Include="..\Modules\_testcapi\frame.c" /> <ClCompile Include="..\Modules\_testcapi\type.c" /> <ClCompile Include="..\Modules\_testcapi\function.c" /> + <ClCompile Include="..\Modules\_testcapi\weakref.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\PC\python_nt.rc" /> @@ -144,6 +147,10 @@ <Project>{885d4898-d08d-4091-9c40-c700cfe3fc5a}</Project> <ReferenceOutputAssembly>false</ReferenceOutputAssembly> </ProjectReference> + <ProjectReference Include="python3tdll.vcxproj"> + <Project>{947BB5F5-6025-4A4F-8182-1B175469F8D2}</Project> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters index 21091e9dc1aa16..a3b62e1df663e0 100644 --- a/PCbuild/_testcapi.vcxproj.filters +++ b/PCbuild/_testcapi.vcxproj.filters @@ -108,6 +108,12 @@ <ClCompile Include="..\Modules\_testcapi\run.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\Modules\_testcapi\modsupport.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\Modules\_testcapi\module.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\Modules\_testcapi\monitoring.c"> <Filter>Source Files</Filter> </ClCompile> @@ -126,6 +132,9 @@ <ClCompile Include="..\Modules\_testcapi\function.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\Modules\_testcapi\weakref.c"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\PC\python_nt.rc"> diff --git a/PCbuild/_testinternalcapi.vcxproj b/PCbuild/_testinternalcapi.vcxproj index f7e050b7c74180..f3e423fa04668e 100644 --- a/PCbuild/_testinternalcapi.vcxproj +++ b/PCbuild/_testinternalcapi.vcxproj @@ -99,6 +99,8 @@ <ClCompile Include="..\Modules\_testinternalcapi\test_lock.c" /> <ClCompile Include="..\Modules\_testinternalcapi\set.c" /> <ClCompile Include="..\Modules\_testinternalcapi\complex.c" /> + <ClCompile Include="..\Modules\_testinternalcapi\interpreter.c" /> + <ClCompile Include="..\Modules\_testinternalcapi\tuple.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\PC\python_nt.rc" /> diff --git a/PCbuild/_testinternalcapi.vcxproj.filters b/PCbuild/_testinternalcapi.vcxproj.filters index 012d709bd1ce5d..7ab242c2c230b6 100644 --- a/PCbuild/_testinternalcapi.vcxproj.filters +++ b/PCbuild/_testinternalcapi.vcxproj.filters @@ -27,6 +27,9 @@ <ClCompile Include="..\Modules\_testinternalcapi\complex.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\Modules\_testinternalcapi\tuple.c"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\PC\python_nt.rc"> diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj index 36c41fc9824fda..69558d204dbb8e 100644 --- a/PCbuild/_testlimitedcapi.vcxproj +++ b/PCbuild/_testlimitedcapi.vcxproj @@ -109,12 +109,15 @@ <ClCompile Include="..\Modules\_testlimitedcapi\object.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\set.c" /> + <ClCompile Include="..\Modules\_testlimitedcapi\slots.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\sys.c" /> + <ClCompile Include="..\Modules\_testlimitedcapi\threadstate.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\tuple.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\unicode.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\version.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\file.c" /> + <ClCompile Include="..\Modules\_testlimitedcapi\weakref.c" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\PC\python_nt.rc" /> @@ -128,6 +131,10 @@ <Project>{885d4898-d08d-4091-9c40-c700cfe3fc5a}</Project> <ReferenceOutputAssembly>false</ReferenceOutputAssembly> </ProjectReference> + <ProjectReference Include="python3tdll.vcxproj"> + <Project>{947BB5F5-6025-4A4F-8182-1B175469F8D2}</Project> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters b/PCbuild/_testlimitedcapi.vcxproj.filters index 62ecb2f70ffa2d..2bcc3f6ff176bd 100644 --- a/PCbuild/_testlimitedcapi.vcxproj.filters +++ b/PCbuild/_testlimitedcapi.vcxproj.filters @@ -24,13 +24,16 @@ <ClCompile Include="..\Modules\_testlimitedcapi\object.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\set.c" /> + <ClCompile Include="..\Modules\_testlimitedcapi\slots.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\sys.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\testcapi_long.h" /> + <ClCompile Include="..\Modules\_testlimitedcapi\threadstate.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\tuple.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\unicode.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\version.c" /> <ClCompile Include="..\Modules\_testlimitedcapi\file.c" /> + <ClCompile Include="..\Modules\_testlimitedcapi\weakref.c" /> <ClCompile Include="..\Modules\_testlimitedcapi.c" /> </ItemGroup> <ItemGroup> diff --git a/PCbuild/build.bat b/PCbuild/build.bat index 602357048867d6..9d2f032f5a9355 100644 --- a/PCbuild/build.bat +++ b/PCbuild/build.bat @@ -41,6 +41,8 @@ echo. --experimental-jit-off Ditto but off by default (PYTHON_JIT=1 enable echo. --experimental-jit-interpreter Enable the experimental Tier 2 interpreter. echo. --pystats Enable PyStats collection. echo. --tail-call-interp Enable tail-calling interpreter (requires LLVM 19 or higher). +echo. --enable-stackref-debug Enable stackref debugging mode. +echo. --pymalloc-hugepages Enable huge page support for pymalloc arenas. echo. echo.Available flags to avoid building certain modules. echo.These flags have no effect if '-e' is not given: @@ -64,6 +66,7 @@ setlocal set platf=x64 set conf=Release set target=Build +set clean=false set dir=%~dp0 set parallel=/m set verbose=/nologo /v:m /clp:summary @@ -98,6 +101,8 @@ if "%~1"=="--experimental-jit-interpreter-off" (set UseTIER2=6) & shift & goto C if "%~1"=="--without-remote-debug" (set DisableRemoteDebug=true) & shift & goto CheckOpts if "%~1"=="--pystats" (set PyStats=1) & shift & goto CheckOpts if "%~1"=="--tail-call-interp" (set UseTailCallInterp=true) & shift & goto CheckOpts +if "%~1"=="--enable-stackref-debug" (set StackRefDebug=true) & shift & goto CheckOpts +if "%~1"=="--pymalloc-hugepages" (set UsePymallocHugepages=true) & shift & goto CheckOpts rem These use the actual property names used by MSBuild. We could just let rem them in through the environment, but we specify them on the command line rem anyway for visibility so set defaults after this @@ -115,6 +120,9 @@ if "%UseJIT%" NEQ "true" set IncludeLLVM=false if "%IncludeExternals%"=="true" call "%dir%get_externals.bat" +if /I "%target%"=="Clean" set clean=true +if /I "%target%"=="CleanAll" set clean=true + if "%do_pgo%" EQU "true" if "%platf%" EQU "x64" ( if "%PROCESSOR_ARCHITEW6432%" NEQ "AMD64" if "%PROCESSOR_ARCHITECTURE%" NEQ "AMD64" ( echo.ERROR: Cannot cross-compile with PGO @@ -155,18 +163,27 @@ if "%do_pgo%"=="true" ( rem %VARS% are evaluated eagerly, which would lose the ERRORLEVEL rem value if we didn't split it out here. if "%do_pgo%"=="true" if ERRORLEVEL 1 exit /B %ERRORLEVEL% + +rem In case of a PGO build, we switch the conf here to PGUpdate +rem to get it cleaned or built as well. if "%do_pgo%"=="true" ( del /s "%dir%\*.pgc" del /s "%dir%\..\Lib\*.pyc" - echo on - call "%dir%\..\python.bat" %pgo_job% - @echo off - call :Kill set conf=PGUpdate - set target=Build + if "%clean%"=="false" goto :RunPgoJob ) goto :Build +:RunPgoJob +echo on +call "%dir%\..\python.bat" %pgo_job% +@echo off +set pgo_errorlevel=%ERRORLEVEL% +call :Kill +if %pgo_errorlevel% NEQ 0 exit /B %pgo_errorlevel% +set target=Build +goto :Build + :Kill echo on %MSBUILD% "%dir%\pythoncore.vcxproj" /t:KillPython %verbose%^ @@ -202,6 +219,8 @@ echo on /p:PyStats=%PyStats%^ /p:UseTailCallInterp=%UseTailCallInterp%^ /p:DisableRemoteDebug=%DisableRemoteDebug%^ + /p:StackRefDebug=%StackRefDebug%^ + /p:UsePymallocHugepages=%UsePymallocHugepages%^ %1 %2 %3 %4 %5 %6 %7 %8 %9 @echo off diff --git a/PCbuild/field3.py b/PCbuild/field3.py deleted file mode 100644 index edcbe36ae083ba..00000000000000 --- a/PCbuild/field3.py +++ /dev/null @@ -1,35 +0,0 @@ -# An absurd workaround for the lack of arithmetic in MS's resource compiler. -# After building Python, run this, then paste the output into the appropriate -# part of PC\python_nt.rc. -# Example output: -# -# * For 2.3a0, -# * PY_MICRO_VERSION = 0 -# * PY_RELEASE_LEVEL = 'alpha' = 0xA -# * PY_RELEASE_SERIAL = 1 -# * -# * and 0*1000 + 10*10 + 1 = 101. -# */ -# #define FIELD3 101 - -import sys - -major, minor, micro, level, serial = sys.version_info -levelnum = {'alpha': 0xA, - 'beta': 0xB, - 'candidate': 0xC, - 'final': 0xF, - }[level] -string = sys.version.split()[0] # like '2.3a0' - -print(" * For %s," % string) -print(" * PY_MICRO_VERSION = %d" % micro) -print(" * PY_RELEASE_LEVEL = %r = %s" % (level, hex(levelnum))) -print(" * PY_RELEASE_SERIAL = %d" % serial) -print(" *") - -field3 = micro * 1000 + levelnum * 10 + serial - -print(" * and %d*1000 + %d*10 + %d = %d" % (micro, levelnum, serial, field3)) -print(" */") -print("#define FIELD3", field3) diff --git a/PCbuild/find_python.bat b/PCbuild/find_python.bat index d65d080ca71a90..841d83968c60be 100644 --- a/PCbuild/find_python.bat +++ b/PCbuild/find_python.bat @@ -47,7 +47,7 @@ @rem If py.exe finds a recent enough version, use that one @rem It is fine to add new versions to this list when they have released, @rem but we do not use prerelease builds here. -@for %%p in (3.13 3.12 3.11 3.10) do @py -%%p -EV >nul 2>&1 && (set PYTHON=py -%%p) && (set _Py_Python_Source=found %%p with py.exe) && goto :found +@for %%p in (3.14 3.13 3.12 3.11 3.10) do @py -%%p -EV >nul 2>&1 && (set PYTHON=py -%%p) && (set _Py_Python_Source=found %%p with py.exe) && goto :found @if NOT exist "%_Py_EXTERNALS_DIR%" mkdir "%_Py_EXTERNALS_DIR%" @set _Py_NUGET=%NUGET% diff --git a/PCbuild/get_external.py b/PCbuild/get_external.py index a78aa6a23041ad..494b22809e0844 100755 --- a/PCbuild/get_external.py +++ b/PCbuild/get_external.py @@ -1,15 +1,40 @@ #!/usr/bin/env python3 import argparse +import functools import os import pathlib +import platform +import subprocess import sys +import tarfile import time import urllib.error import urllib.request import zipfile +@functools.cache +def trigger_automatic_root_certificate_update(url: str, timeout: int = 30) -> None: + escaped_url = url.replace("'", "''") + try: + subprocess.run( + [ + "powershell", + "-NoProfile", + "-Command", + f"Invoke-WebRequest -Uri '{escaped_url}'" + f" -UseBasicParsing -Method HEAD -MaximumRedirection 0" + f" -TimeoutSec {timeout}", + ], + check=True, + capture_output=True, + timeout=timeout + 5, + ) + except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e: + print(e) + + def retrieve_with_retries(download_location, output_path, reporthook, max_retries=7): """Download a file with exponential backoff retry and save to disk.""" @@ -22,15 +47,14 @@ def retrieve_with_retries(download_location, output_path, reporthook, ) except (urllib.error.URLError, ConnectionError) as ex: if attempt == max_retries: - msg = f"Download from {download_location} failed." - raise OSError(msg) from ex + raise OSError(f'Download from {download_location} failed.') from ex + trigger_automatic_root_certificate_update(download_location) time.sleep(2.25**attempt) else: return resp - def fetch_zip(commit_hash, zip_dir, *, org='python', binary=False, verbose): - repo = f'cpython-{"bin" if binary else "source"}-deps' + repo = 'cpython-bin-deps' if binary else 'cpython-source-deps' url = f'https://github.com/{org}/{repo}/archive/{commit_hash}.zip' reporthook = None if verbose: @@ -44,6 +68,42 @@ def fetch_zip(commit_hash, zip_dir, *, org='python', binary=False, verbose): return filename +def fetch_release(tag, tarball_dir, *, org='python', verbose=False): + arch = os.environ.get('PreferredToolArchitecture') + if not arch: + machine = platform.machine() + arch = 'ARM64' if machine == 'ARM64' else 'AMD64' + elif arch.lower() in ('x86', 'x64'): + arch = 'AMD64' + reporthook = None + if verbose: + reporthook = print + tarball_dir.mkdir(parents=True, exist_ok=True) + + arch_filename = f'{tag}-{arch}.tar.xz' + arch_url = f'https://github.com/{org}/cpython-bin-deps/releases/download/{tag}/{arch_filename}' + try: + output_path = tarball_dir / arch_filename + retrieve_with_retries(arch_url, output_path, reporthook) + return output_path + except OSError: + if verbose: + print(f'{arch_filename} not found, trying generic binary...') + + generic_filename = f'{tag}.tar.xz' + generic_url = f'https://github.com/{org}/cpython-bin-deps/releases/download/{tag}/{generic_filename}' + output_path = tarball_dir / generic_filename + retrieve_with_retries(generic_url, output_path, reporthook) + return output_path + + +def extract_tarball(externals_dir, tarball_path, tag): + output_path = externals_dir / tag + with tarfile.open(tarball_path) as tf: + tf.extractall(os.fspath(externals_dir), filter='data') + return output_path + + def extract_zip(externals_dir, zip_path): with zipfile.ZipFile(os.fspath(zip_path)) as zf: zf.extractall(os.fspath(externals_dir)) @@ -55,6 +115,8 @@ def parse_args(): p.add_argument('-v', '--verbose', action='store_true') p.add_argument('-b', '--binary', action='store_true', help='Is the dependency in the binary repo?') + p.add_argument('-r', '--release', action='store_true', + help='Download from GitHub release assets instead of branch') p.add_argument('-O', '--organization', help='Organization owning the deps repos', default='python') p.add_argument('-e', '--externals-dir', type=pathlib.Path, @@ -67,30 +129,53 @@ def parse_args(): def main(): args = parse_args() - zip_path = fetch_zip( - args.tag, - args.externals_dir / 'zips', - org=args.organization, - binary=args.binary, - verbose=args.verbose, - ) final_name = args.externals_dir / args.tag - extracted = extract_zip(args.externals_dir, zip_path) - for wait in [1, 2, 3, 5, 8, 0]: - try: - extracted.replace(final_name) - break - except PermissionError as ex: - retry = f" Retrying in {wait}s..." if wait else "" - print(f"Encountered permission error '{ex}'.{retry}", file=sys.stderr) - time.sleep(wait) + + # Check if the dependency already exists in externals/ directory + # (either already downloaded/extracted, or checked into the git tree) + if final_name.exists(): + if args.verbose: + print(f'{args.tag} already exists at {final_name}, skipping download.') + return + + # Determine download method: release artifacts for large deps (like LLVM), + # otherwise zip download from GitHub branches + if args.release: + tarball_path = fetch_release( + args.tag, + args.externals_dir / 'tarballs', + org=args.organization, + verbose=args.verbose, + ) + extracted = extract_tarball(args.externals_dir, tarball_path, args.tag) else: - print( - f"ERROR: Failed to extract {final_name}.", - "You may need to restart your build", - file=sys.stderr, + # Use zip download from GitHub branches + # (cpython-bin-deps if --binary, cpython-source-deps otherwise) + zip_path = fetch_zip( + args.tag, + args.externals_dir / 'zips', + org=args.organization, + binary=args.binary, + verbose=args.verbose, ) - sys.exit(1) + extracted = extract_zip(args.externals_dir, zip_path) + + if extracted != final_name: + for wait in [1, 2, 3, 5, 8, 0]: + try: + extracted.replace(final_name) + break + except PermissionError as ex: + retry = f" Retrying in {wait}s..." if wait else "" + print(f"Encountered permission error '{ex}'.{retry}", file=sys.stderr) + time.sleep(wait) + else: + print( + f"ERROR: Failed to rename {extracted} to {final_name}.", + "You may need to restart your build", + file=sys.stderr, + ) + sys.exit(1) if __name__ == '__main__': diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index e29054f5734d49..47399fe65d1e54 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -54,12 +54,12 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.4.4 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.0.16 +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-3.5.7 set libraries=%libraries% mpdecimal-4.0.0 -set libraries=%libraries% sqlite-3.49.1.0 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.15.0 -if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.15.0 -set libraries=%libraries% xz-5.2.5 +set libraries=%libraries% sqlite-3.53.2.0 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-9.0.3.0 +if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-9.0.3.1 +set libraries=%libraries% xz-5.8.1.1 set libraries=%libraries% zlib-ng-2.2.4 set libraries=%libraries% zstd-1.5.7 @@ -79,10 +79,10 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.4.4 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.0.16.2 -if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.15.0 +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-3.5.7 +if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-9.0.3.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 -if NOT "%IncludeLLVM%"=="false" set binaries=%binaries% llvm-19.1.7.0 +if NOT "%IncludeLLVM%"=="false" set binaries=%binaries% llvm-21.1.4.0 for %%b in (%binaries%) do ( if exist "%EXTERNALS_DIR%\%%b" ( @@ -92,7 +92,11 @@ for %%b in (%binaries%) do ( git clone --depth 1 https://github.com/%ORG%/cpython-bin-deps --branch %%b "%EXTERNALS_DIR%\%%b" ) else ( echo.Fetching %%b... - %PYTHON% -E "%PCBUILD%\get_external.py" -b -O %ORG% -e "%EXTERNALS_DIR%" %%b + if "%%b"=="llvm-21.1.4.0" ( + %PYTHON% -E "%PCBUILD%\get_external.py" --release --organization %ORG% --externals-dir "%EXTERNALS_DIR%" %%b + ) else ( + %PYTHON% -E "%PCBUILD%\get_external.py" --binary --organization %ORG% --externals-dir "%EXTERNALS_DIR%" %%b + ) ) ) diff --git a/PCbuild/liblzma.vcxproj b/PCbuild/liblzma.vcxproj index 97938692328495..75d4e16234618e 100644 --- a/PCbuild/liblzma.vcxproj +++ b/PCbuild/liblzma.vcxproj @@ -92,7 +92,7 @@ <ItemDefinitionGroup> <ClCompile> <PreprocessorDefinitions>WIN32;HAVE_CONFIG_H;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories>$(lzmaDir)windows/vs2019;$(lzmaDir)src/liblzma/common;$(lzmaDir)src/common;$(lzmaDir)src/liblzma/api;$(lzmaDir)src/liblzma/check;$(lzmaDir)src/liblzma/delta;$(lzmaDir)src/liblzma/lz;$(lzmaDir)src/liblzma/lzma;$(lzmaDir)src/liblzma/rangecoder;$(lzmaDir)src/liblzma/simple;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories>$(lzmaDir)windows;$(lzmaDir)src/liblzma/common;$(lzmaDir)src/common;$(lzmaDir)src/liblzma/api;$(lzmaDir)src/liblzma/check;$(lzmaDir)src/liblzma/delta;$(lzmaDir)src/liblzma/lz;$(lzmaDir)src/liblzma/lzma;$(lzmaDir)src/liblzma/rangecoder;$(lzmaDir)src/liblzma/simple;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <DisableSpecificWarnings>4244;4267;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> <AdditionalOptions Condition="$(PlatformToolset) == 'ClangCL'">%(AdditionalOptions) -Wno-deprecated-declarations</AdditionalOptions> </ClCompile> @@ -102,9 +102,7 @@ <ClCompile Include="$(lzmaDir)src\common\tuklib_physmem.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\check\check.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\check\crc32_fast.c" /> - <ClCompile Include="$(lzmaDir)src\liblzma\check\crc32_table.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\check\crc64_fast.c" /> - <ClCompile Include="$(lzmaDir)src\liblzma\check\crc64_table.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\check\sha256.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\common\alone_decoder.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\common\alone_encoder.c" /> @@ -163,6 +161,7 @@ <ClCompile Include="$(lzmaDir)src\liblzma\lz\lz_encoder_mf.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\rangecoder\price_table.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\simple\arm.c" /> + <ClCompile Include="$(lzmaDir)src\liblzma\simple\arm64.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\simple\armthumb.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\simple\ia64.c" /> <ClCompile Include="$(lzmaDir)src\liblzma\simple\powerpc.c" /> @@ -239,7 +238,7 @@ <ClInclude Include="$(lzmaDir)src\liblzma\simple\simple_decoder.h" /> <ClInclude Include="$(lzmaDir)src\liblzma\simple\simple_encoder.h" /> <ClInclude Include="$(lzmaDir)src\liblzma\simple\simple_private.h" /> - <ClInclude Include="$(lzmaDir)windows\vs2019\config.h" /> + <ClInclude Include="$(lzmaDir)windows\config.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/PCbuild/liblzma.vcxproj.filters b/PCbuild/liblzma.vcxproj.filters index ebe2a7d5fa9e5d..42feca5a3414bf 100644 --- a/PCbuild/liblzma.vcxproj.filters +++ b/PCbuild/liblzma.vcxproj.filters @@ -18,6 +18,9 @@ <ClCompile Include="$(lzmaDir)src\liblzma\simple\arm.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="$(lzmaDir)src\liblzma\simple\arm64.c"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="$(lzmaDir)src\liblzma\simple\armthumb.c"> <Filter>Source Files</Filter> </ClCompile> @@ -54,15 +57,9 @@ <ClCompile Include="$(lzmaDir)src\liblzma\check\crc32_fast.c"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="$(lzmaDir)src\liblzma\check\crc32_table.c"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="$(lzmaDir)src\liblzma\check\crc64_fast.c"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="$(lzmaDir)src\liblzma\check\crc64_table.c"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="$(lzmaDir)src\liblzma\delta\delta_common.c"> <Filter>Source Files</Filter> </ClCompile> @@ -428,7 +425,7 @@ <ClInclude Include="$(lzmaDir)src\liblzma\simple\simple_private.h"> <Filter>Header Files</Filter> </ClInclude> - <ClInclude Include="$(lzmaDir)windows\vs2019\config.h"> + <ClInclude Include="$(lzmaDir)windows\config.h"> <Filter>Header Files</Filter> </ClInclude> </ItemGroup> diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj index 7a5327bf016cea..9d077bbd3f0ba2 100644 --- a/PCbuild/pcbuild.proj +++ b/PCbuild/pcbuild.proj @@ -61,6 +61,8 @@ </Projects> <!-- python3.dll --> <Projects Include="python3dll.vcxproj" /> + <!-- python3t.dll --> + <Projects Include="python3tdll.vcxproj" /> <!-- py[w].exe --> <Projects Include="pylauncher.vcxproj;pywlauncher.vcxproj" /> <!-- pyshellext.dll --> @@ -82,6 +84,7 @@ <TestModules Include="_ctypes_test;_testbuffer;_testcapi;_testlimitedcapi;_testinternalcapi;_testembed;_testimportmultiple;_testmultiphase;_testsinglephase;_testconsole;_testclinic;_testclinic_limited" /> <TestModules Include="xxlimited" Condition="'$(Configuration)' == 'Release'" /> <TestModules Include="xxlimited_35" Condition="'$(Configuration)' == 'Release'" /> + <TestModules Include="xxlimited_3_13" Condition="'$(Configuration)' == 'Release'" /> <Projects Include="@(TestModules->'%(Identity).vcxproj')" Condition="$(IncludeTests)"> <!-- Disable parallel build for test modules --> <BuildInParallel>false</BuildInParallel> diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index 7296ea75301157..09a989d38648df 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -33,6 +33,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python", "python.vcxproj", {78D80A15-BD8C-44E2-B49E-1F05B0A0A687} = {78D80A15-BD8C-44E2-B49E-1F05B0A0A687} {86937F53-C189-40EF-8CE8-8759D8E7D480} = {86937F53-C189-40EF-8CE8-8759D8E7D480} {885D4898-D08D-4091-9C40-C700CFE3FC5A} = {885D4898-D08D-4091-9C40-C700CFE3FC5A} + {947BB5F5-6025-4A4F-8182-1B175469F8D2} = {947BB5F5-6025-4A4F-8182-1B175469F8D2} {900342D7-516A-4469-B1AD-59A66E49A25F} = {900342D7-516A-4469-B1AD-59A66E49A25F} {9E48B300-37D1-11DD-8C41-005056C00008} = {9E48B300-37D1-11DD-8C41-005056C00008} {9EC7190A-249F-4180-A900-548FDCF3055F} = {9EC7190A-249F-4180-A900-548FDCF3055F} @@ -104,6 +105,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_multiprocessing", "_multip EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python3dll", "python3dll.vcxproj", "{885D4898-D08D-4091-9C40-C700CFE3FC5A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python3tdll", "python3tdll.vcxproj", "{947BB5F5-6025-4A4F-8182-1B175469F8D2}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xxlimited", "xxlimited.vcxproj", "{F749B822-B489-4CA5-A3AD-CE078F5F338A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_testbuffer", "_testbuffer.vcxproj", "{A2697BD3-28C1-4AEC-9106-8B748639FD16}" @@ -168,6 +171,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_remote_debugging", "_remot EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_zstd", "_zstd.vcxproj", "{07029B86-F3E9-443E-86FB-78AA6D47FED1}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xxlimited_35", "xxlimited_35.vcxproj", "{FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -984,6 +989,38 @@ Global {885D4898-D08D-4091-9C40-C700CFE3FC5A}.Release|Win32.Build.0 = Release|Win32 {885D4898-D08D-4091-9C40-C700CFE3FC5A}.Release|x64.ActiveCfg = Release|x64 {885D4898-D08D-4091-9C40-C700CFE3FC5A}.Release|x64.Build.0 = Release|x64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Debug|ARM.ActiveCfg = Debug|ARM + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Debug|ARM.Build.0 = Debug|ARM + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Debug|ARM64.Build.0 = Debug|ARM64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Debug|Win32.ActiveCfg = Debug|Win32 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Debug|Win32.Build.0 = Debug|Win32 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Debug|x64.ActiveCfg = Debug|x64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Debug|x64.Build.0 = Debug|x64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGInstrument|ARM.ActiveCfg = PGInstrument|ARM + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGInstrument|ARM.Build.0 = PGInstrument|ARM + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGInstrument|ARM64.ActiveCfg = PGInstrument|ARM64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGInstrument|ARM64.Build.0 = PGInstrument|ARM64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGInstrument|Win32.ActiveCfg = Debug|Win32 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGInstrument|Win32.Build.0 = Debug|Win32 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGInstrument|x64.ActiveCfg = Debug|x64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGInstrument|x64.Build.0 = Debug|x64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGUpdate|ARM.ActiveCfg = PGUpdate|ARM + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGUpdate|ARM.Build.0 = PGUpdate|ARM + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGUpdate|ARM64.ActiveCfg = PGUpdate|ARM64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGUpdate|ARM64.Build.0 = PGUpdate|ARM64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGUpdate|Win32.ActiveCfg = Debug|Win32 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGUpdate|Win32.Build.0 = Debug|Win32 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGUpdate|x64.ActiveCfg = Debug|x64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.PGUpdate|x64.Build.0 = Debug|x64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Release|ARM.ActiveCfg = Release|ARM + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Release|ARM.Build.0 = Release|ARM + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Release|ARM64.ActiveCfg = Release|ARM64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Release|ARM64.Build.0 = Release|ARM64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Release|Win32.ActiveCfg = Release|Win32 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Release|Win32.Build.0 = Release|Win32 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Release|x64.ActiveCfg = Release|x64 + {947BB5F5-6025-4A4F-8182-1B175469F8D2}.Release|x64.Build.0 = Release|x64 {F749B822-B489-4CA5-A3AD-CE078F5F338A}.Debug|ARM.ActiveCfg = Debug|ARM {F749B822-B489-4CA5-A3AD-CE078F5F338A}.Debug|ARM64.ActiveCfg = Debug|ARM64 {F749B822-B489-4CA5-A3AD-CE078F5F338A}.Debug|Win32.ActiveCfg = Release|Win32 @@ -1785,6 +1822,38 @@ Global {07029B86-F3E9-443E-86FB-78AA6D47FED1}.Release|Win32.Build.0 = Release|Win32 {07029B86-F3E9-443E-86FB-78AA6D47FED1}.Release|x64.ActiveCfg = Release|x64 {07029B86-F3E9-443E-86FB-78AA6D47FED1}.Release|x64.Build.0 = Release|x64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Debug|ARM.ActiveCfg = Debug|ARM + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Debug|ARM.Build.0 = Debug|ARM + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Debug|ARM64.Build.0 = Debug|ARM64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Debug|Win32.ActiveCfg = Debug|Win32 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Debug|Win32.Build.0 = Debug|Win32 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Debug|x64.ActiveCfg = Debug|x64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Debug|x64.Build.0 = Debug|x64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGInstrument|ARM.ActiveCfg = PGInstrument|ARM + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGInstrument|ARM.Build.0 = PGInstrument|ARM + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGInstrument|ARM64.ActiveCfg = PGInstrument|ARM64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGInstrument|ARM64.Build.0 = PGInstrument|ARM64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGUpdate|ARM.ActiveCfg = PGUpdate|ARM + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGUpdate|ARM.Build.0 = PGUpdate|ARM + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGUpdate|ARM64.ActiveCfg = PGUpdate|ARM64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGUpdate|ARM64.Build.0 = PGUpdate|ARM64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Release|ARM.ActiveCfg = Release|ARM + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Release|ARM.Build.0 = Release|ARM + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Release|ARM64.ActiveCfg = Release|ARM64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Release|ARM64.Build.0 = Release|ARM64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Release|Win32.ActiveCfg = Release|Win32 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Release|Win32.Build.0 = Release|Win32 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Release|x64.ActiveCfg = Release|x64 + {FB868EA7-F93A-4D9B-BE78-CA4E9BA14FFF}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PCbuild/pyexpat.vcxproj b/PCbuild/pyexpat.vcxproj index dc9161a8b290f9..8e0f5f6311220a 100644 --- a/PCbuild/pyexpat.vcxproj +++ b/PCbuild/pyexpat.vcxproj @@ -91,7 +91,7 @@ <ItemDefinitionGroup> <ClCompile> <AdditionalIncludeDirectories>$(PySourcePath)Modules\expat;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;PYEXPAT_EXPORTS;XML_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;XML_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props index cf35e705f355a7..7435c53e3fdcf9 100644 --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -9,10 +9,13 @@ <OutDir Condition="!HasTrailingSlash($(OutDir))">$(OutDir)\</OutDir> <Py_IntDir Condition="'$(Py_IntDir)' == ''">$(MSBuildThisFileDirectory)obj\</Py_IntDir> <IntDir>$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)$(ArchName)_$(Configuration)\$(ProjectName)\</IntDir> + <IntDir Condition="$(DisableGil) == 'true'">$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)$(ArchName)t_$(Configuration)\$(ProjectName)\</IntDir> <IntDir>$(IntDir.Replace(`\\`, `\`))</IntDir> <GeneratedFrozenModulesDir>$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)_frozen\</GeneratedFrozenModulesDir> <GeneratedZlibNgDir>$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)$(ArchName)_$(Configuration)\zlib-ng\</GeneratedZlibNgDir> - <GeneratedJitStencilsDir>$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)_$(Configuration)</GeneratedJitStencilsDir> + <GeneratedJitStencilsDir>$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)_$(Configuration)\</GeneratedJitStencilsDir> + <GeneratedJitStencilsDir Condition="$(Configuration) == 'PGUpdate'">$(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)_PGInstrument\</GeneratedJitStencilsDir> + <GeneratedJitStencilsDir>$(GeneratedJitStencilsDir.Replace(`\\`, `\`))</GeneratedJitStencilsDir> <TargetName Condition="'$(TargetName)' == ''">$(ProjectName)</TargetName> <TargetName>$(TargetName)$(PyDebugExt)</TargetName> <GenerateManifest>false</GenerateManifest> @@ -40,6 +43,10 @@ </PropertyGroup> <PropertyGroup> + <!-- + We conditionally define preprocessor definitions here, and then include all + the properties *unconditionally* in the <PreprocessorDefinition> field below + --> <_DebugPreprocessorDefinition>NDEBUG;</_DebugPreprocessorDefinition> <_DebugPreprocessorDefinition Condition="$(Configuration) == 'Debug'">_DEBUG;</_DebugPreprocessorDefinition> <_PyStatsPreprocessorDefinition>PyStats;</_PyStatsPreprocessorDefinition> @@ -47,14 +54,14 @@ <_PlatformPreprocessorDefinition>_WIN32;</_PlatformPreprocessorDefinition> <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64;</_PlatformPreprocessorDefinition> <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64' and $(PlatformToolset) != 'ClangCL'">_M_X64;$(_PlatformPreprocessorDefinition)</_PlatformPreprocessorDefinition> - <_Py3NamePreprocessorDefinition>PY3_DLLNAME=L"$(Py3DllName)$(PyDebugExt)";</_Py3NamePreprocessorDefinition> <_FreeThreadedPreprocessorDefinition Condition="$(DisableGil) == 'true'">Py_GIL_DISABLED=1;</_FreeThreadedPreprocessorDefinition> + <_PymallocHugepagesPreprocessorDefinition Condition="$(UsePymallocHugepages) == 'true'">PYMALLOC_USE_HUGEPAGES=1;</_PymallocHugepagesPreprocessorDefinition> + <_PyUsingPgoPreprocessorDefinition Condition="'$(SupportPGO)' and ($(Configuration) == 'PGInstrument' or $(Configuration) == 'PGUpdate')">_Py_USING_PGO=1;</_PyUsingPgoPreprocessorDefinition> </PropertyGroup> <ItemDefinitionGroup> <ClCompile> <AdditionalIncludeDirectories>$(PySourcePath)Include;$(PySourcePath)Include\internal;$(PySourcePath)Include\internal\mimalloc;$(PySourcePath)PC;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <PreprocessorDefinitions>WIN32;$(_Py3NamePreprocessorDefinition)$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PyStatsPreprocessorDefinition)$(_PydPreprocessorDefinition)$(_FreeThreadedPreprocessorDefinition)%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(SupportPGO)' and ($(Configuration) == 'PGInstrument' or $(Configuration) == 'PGUpdate')">_Py_USING_PGO=1;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>WIN32;$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PyStatsPreprocessorDefinition)$(_PydPreprocessorDefinition)$(_FreeThreadedPreprocessorDefinition)$(_PymallocHugepagesPreprocessorDefinition)$(_PyUsingPgoPreprocessorDefinition)%(PreprocessorDefinitions)</PreprocessorDefinitions> <Optimization>MaxSpeed</Optimization> <IntrinsicFunctions>true</IntrinsicFunctions> diff --git a/PCbuild/python.props b/PCbuild/python.props index ddc7696d2762fe..8d931bba28a389 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -11,6 +11,7 @@ We set BasePlatformToolset for ICC's benefit, it's otherwise ignored. --> + <BasePlatformToolset Condition="'$(BasePlatformToolset)' == '' and '$(VisualStudioVersion)' == '18.0'">v145</BasePlatformToolset> <BasePlatformToolset Condition="'$(BasePlatformToolset)' == '' and '$(VisualStudioVersion)' == '17.0'">v143</BasePlatformToolset> <BasePlatformToolset Condition="'$(BasePlatformToolset)' == '' and '$(VisualStudioVersion)' == '16.0'">v142</BasePlatformToolset> <BasePlatformToolset Condition="'$(BasePlatformToolset)' == '' and ('$(MSBuildToolsVersion)' == '15.0' or '$(VisualStudioVersion)' == '15.0')">v141</BasePlatformToolset> @@ -42,7 +43,7 @@ <PySourcePath Condition="'$(PySourcePath)' == ''">$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\))</PySourcePath> <PySourcePath Condition="!HasTrailingSlash($(PySourcePath))">$(PySourcePath)\</PySourcePath> - <!-- Directory where build outputs are put --> + <!-- Directories where build outputs are put --> <BuildPath32 Condition="'$(Py_OutDir)' == ''">$(PySourcePath)PCbuild\win32\</BuildPath32> <BuildPath32 Condition="'$(Py_OutDir)' != ''">$(Py_OutDir)\win32\</BuildPath32> <BuildPath64 Condition="'$(Py_OutDir)' == ''">$(PySourcePath)PCbuild\amd64\</BuildPath64> @@ -51,11 +52,34 @@ <BuildPathArm32 Condition="'$(Py_OutDir)' != ''">$(Py_OutDir)\arm32\</BuildPathArm32> <BuildPathArm64 Condition="'$(Py_OutDir)' == ''">$(PySourcePath)PCbuild\arm64\</BuildPathArm64> <BuildPathArm64 Condition="'$(Py_OutDir)' != ''">$(Py_OutDir)\arm64\</BuildPathArm64> + <BuildPath32t Condition="'$(Py_OutDir)' == ''">$(PySourcePath)PCbuild\win32t\</BuildPath32t> + <BuildPath32t Condition="'$(Py_OutDir)' != ''">$(Py_OutDir)\win32t\</BuildPath32t> + <BuildPath64t Condition="'$(Py_OutDir)' == ''">$(PySourcePath)PCbuild\amd64t\</BuildPath64t> + <BuildPath64t Condition="'$(Py_OutDir)' != ''">$(Py_OutDir)\amd64t\</BuildPath64t> + <BuildPathArm32t Condition="'$(Py_OutDir)' == ''">$(PySourcePath)PCbuild\arm32t\</BuildPathArm32t> + <BuildPathArm32t Condition="'$(Py_OutDir)' != ''">$(Py_OutDir)\arm32t\</BuildPathArm32t> + <BuildPathArm64t Condition="'$(Py_OutDir)' == ''">$(PySourcePath)PCbuild\arm64t\</BuildPathArm64t> + <BuildPathArm64t Condition="'$(Py_OutDir)' != ''">$(Py_OutDir)\arm64t\</BuildPathArm64t> + </PropertyGroup> + + <PropertyGroup Condition="$(DisableGil) != 'true'"> <BuildPath Condition="'$(ArchName)' == 'win32'">$(BuildPath32)</BuildPath> <BuildPath Condition="'$(ArchName)' == 'amd64'">$(BuildPath64)</BuildPath> <BuildPath Condition="'$(ArchName)' == 'arm32'">$(BuildPathArm32)</BuildPath> <BuildPath Condition="'$(ArchName)' == 'arm64'">$(BuildPathArm64)</BuildPath> <BuildPath Condition="'$(BuildPath)' == ''">$(PySourcePath)PCbuild\$(ArchName)\</BuildPath> + </PropertyGroup> + + <PropertyGroup Condition="$(DisableGil) == 'true'"> + <!-- Directory where build outputs are put --> + <BuildPath Condition="'$(ArchName)' == 'win32'">$(BuildPath32t)</BuildPath> + <BuildPath Condition="'$(ArchName)' == 'amd64'">$(BuildPath64t)</BuildPath> + <BuildPath Condition="'$(ArchName)' == 'arm32'">$(BuildPathArm32t)</BuildPath> + <BuildPath Condition="'$(ArchName)' == 'arm64'">$(BuildPathArm64t)</BuildPath> + <BuildPath Condition="'$(BuildPath)' == ''">$(PySourcePath)PCbuild\$(ArchName)t\</BuildPath> + </PropertyGroup> + + <PropertyGroup> <BuildPath Condition="!HasTrailingSlash($(BuildPath))">$(BuildPath)\</BuildPath> <BuildPath Condition="$(Configuration) == 'PGInstrument'">$(BuildPath)instrumented\</BuildPath> @@ -74,15 +98,15 @@ <Import Project="$(ExternalProps)" Condition="$(ExternalProps) != '' and Exists('$(ExternalProps)')" /> <PropertyGroup> - <sqlite3Dir Condition="$(sqlite3Dir) == ''">$(ExternalsDir)sqlite-3.49.1.0\</sqlite3Dir> + <sqlite3Dir Condition="$(sqlite3Dir) == ''">$(ExternalsDir)sqlite-3.53.2.0\</sqlite3Dir> <bz2Dir Condition="$(bz2Dir) == ''">$(ExternalsDir)bzip2-1.0.8\</bz2Dir> - <lzmaDir Condition="$(lzmaDir) == ''">$(ExternalsDir)xz-5.2.5\</lzmaDir> + <lzmaDir Condition="$(lzmaDir) == ''">$(ExternalsDir)xz-5.8.1.1\</lzmaDir> <libffiDir Condition="$(libffiDir) == ''">$(ExternalsDir)libffi-3.4.4\</libffiDir> <libffiOutDir Condition="$(libffiOutDir) == ''">$(libffiDir)$(ArchName)\</libffiOutDir> <libffiIncludeDir Condition="$(libffiIncludeDir) == ''">$(libffiOutDir)include</libffiIncludeDir> <mpdecimalDir Condition="$(mpdecimalDir) == ''">$(ExternalsDir)\mpdecimal-4.0.0\</mpdecimalDir> - <opensslDir Condition="$(opensslDir) == ''">$(ExternalsDir)openssl-3.0.16\</opensslDir> - <opensslOutDir Condition="$(opensslOutDir) == ''">$(ExternalsDir)openssl-bin-3.0.16.2\$(ArchName)\</opensslOutDir> + <opensslDir Condition="$(opensslDir) == ''">$(ExternalsDir)openssl-3.5.7\</opensslDir> + <opensslOutDir Condition="$(opensslOutDir) == ''">$(ExternalsDir)openssl-bin-3.5.7\$(ArchName)\</opensslOutDir> <opensslIncludeDir Condition="$(opensslIncludeDir) == ''">$(opensslOutDir)include</opensslIncludeDir> <nasmDir Condition="$(nasmDir) == ''">$(ExternalsDir)\nasm-2.11.06\</nasmDir> <zlibDir Condition="$(zlibDir) == ''">$(ExternalsDir)\zlib-1.3.1\</zlibDir> @@ -231,18 +255,17 @@ <Field3Value Condition="$(UseTestMarker) == 'true'">$([msbuild]::Add($(Field3Value), 9000))</Field3Value> <!-- Name and full path of the resulting python.exe binary --> - <PyExeName Condition="$(DisableGil) == 'true'">python$(MajorVersionNumber).$(MinorVersionNumber)t</PyExeName> <PyExeName Condition="$(PyExeName) == ''">python</PyExeName> <PythonExe Condition="'$(PythonExe)' == ''">$(BuildPath)$(PyExeName)$(PyDebugExt).exe</PythonExe> - <PyWExeName Condition="$(DisableGil) == 'true'">pythonw$(MajorVersionNumber).$(MinorVersionNumber)t</PyWExeName> <PyWExeName Condition="$(PyWExeName) == ''">pythonw</PyWExeName> <!-- The name of the resulting pythonXY.dll (without the extension) --> <PyDllName Condition="$(DisableGil) == 'true'">python$(MajorVersionNumber)$(MinorVersionNumber)t$(PyDebugExt)</PyDllName> <PyDllName Condition="$(PyDllName) == ''">python$(MajorVersionNumber)$(MinorVersionNumber)$(PyDebugExt)</PyDllName> + <!-- The name of the resulting pythonX.dll (without the extension) --> - <Py3DllName Condition="$(DisableGil) == 'true'">python3t</Py3DllName> <Py3DllName Condition="$(Py3DllName) == ''">python3</Py3DllName> + <Abi3tDllName Condition="$(Abi3tDllName) == ''">python3t</Abi3tDllName> <!-- The version and platform tag to include in .pyd filenames --> <PydTag Condition="$(ArchName) == 'win32'">.cp$(MajorVersionNumber)$(MinorVersionNumber)-win32</PydTag> diff --git a/PCbuild/python.vcxproj b/PCbuild/python.vcxproj index 70dabaa3c8bc02..417ede34c54af3 100644 --- a/PCbuild/python.vcxproj +++ b/PCbuild/python.vcxproj @@ -135,6 +135,14 @@ set PYTHONPATH=$(PySourcePath)Lib "$(OutDir)$(PyExeName)$(PyDebugExt).exe" "$(PySourcePath)PC\validate_ucrtbase.py" $(UcrtName)' ContinueOnError="true" /> </Target> + <Target Name="CopyFreethreadedBinary" AfterTargets="AfterBuild" + Condition="$(DisableGil) == 'true' and $(Configuration) != 'PGInstrument'"> + <Message Text="Duplicating $(TargetPath) to $(PyExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe for free-threaded compatibility" /> + <Copy SourceFiles="$(TargetPath)" + DestinationFiles="$(OutDir)\$(PyExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe" + SkipUnchangedFiles="true" + UseHardLinksIfPossible="true" /> + </Target> <Target Name="GeneratePythonBat" AfterTargets="AfterBuild"> <PropertyGroup> <_Content>@rem This script invokes the most recently built Python with all arguments diff --git a/PCbuild/python3dll.vcxproj b/PCbuild/python3dll.vcxproj index 235ea1cf9d33fb..3d8ac1b23532c1 100644 --- a/PCbuild/python3dll.vcxproj +++ b/PCbuild/python3dll.vcxproj @@ -75,7 +75,7 @@ <Import Project="python.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration"> - <TargetName>$(Py3DllName)</TargetName> + <TargetName>python3</TargetName> <ConfigurationType>DynamicLibrary</ConfigurationType> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> diff --git a/PCbuild/python3tdll.vcxproj b/PCbuild/python3tdll.vcxproj new file mode 100644 index 00000000000000..796712cca3146c --- /dev/null +++ b/PCbuild/python3tdll.vcxproj @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|ARM"> + <Configuration>Debug</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|ARM64"> + <Configuration>Debug</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGInstrument|ARM"> + <Configuration>PGInstrument</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGInstrument|ARM64"> + <Configuration>PGInstrument</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGInstrument|Win32"> + <Configuration>PGInstrument</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGInstrument|x64"> + <Configuration>PGInstrument</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGUpdate|ARM"> + <Configuration>PGUpdate</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGUpdate|ARM64"> + <Configuration>PGUpdate</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGUpdate|Win32"> + <Configuration>PGUpdate</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGUpdate|x64"> + <Configuration>PGUpdate</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM"> + <Configuration>Release</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{947BB5F5-6025-4A4F-8182-1B175469F8D2}</ProjectGuid> + <RootNamespace>python3tdll</RootNamespace> + <Keyword>Win32Proj</Keyword> + <SupportPGO>false</SupportPGO> + </PropertyGroup> + <Import Project="python.props" /> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration"> + <TargetName>python3t</TargetName> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="pyproject.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> + </PropertyGroup> + <ItemDefinitionGroup> + <ClCompile> + <PreprocessorDefinitions>PYTHON_DLL_NAME="$(PyDllName)";%(PreprocessorDefinitions)</PreprocessorDefinitions> + <BufferSecurityCheck>false</BufferSecurityCheck> + </ClCompile> + <Link> + <NoEntryPoint>true</NoEntryPoint> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\PC\python3dll.c" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="..\PC\python_nt.rc" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/PCbuild/python3tdll.vcxproj.filters b/PCbuild/python3tdll.vcxproj.filters new file mode 100644 index 00000000000000..37510e3c7398f2 --- /dev/null +++ b/PCbuild/python3tdll.vcxproj.filters @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\PC\python3dll.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="..\PC\python_nt.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project> diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 517103acea8d8e..e255ed5af19125 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -107,13 +107,17 @@ <PreprocessorDefinitions Condition="$(IncludeExternals)">_Py_HAVE_ZLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions Condition="'$(UseJIT)' == 'true'">_Py_JIT;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions Condition="'$(UseTIER2)' != '' and '$(UseTIER2)' != '0'">_Py_TIER2=$(UseTIER2);%(PreprocessorDefinitions)</PreprocessorDefinitions> - <PreprocessorDefinitions Condition="'$(UseTailCallInterp)' == 'true'">Py_TAIL_CALL_INTERP=1;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions Condition="'$(UseTailCallInterp)' == 'true'">_Py_TAIL_CALL_INTERP=1;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions Condition="'$(WITH_COMPUTED_GOTOS)' != ''">HAVE_COMPUTED_GOTOS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions Condition="'$(DisableRemoteDebug)' != 'true'">Py_REMOTE_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions Condition="'$(StackRefDebug)' == 'true'">Py_STACKREF_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <AdditionalDependencies>version.lib;ws2_32.lib;pathcch.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies Condition="$(IncludeExternals)">zlib-ng$(PyDebugExt).lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(UseJIT)' == 'true' and $(Platform) == 'ARM64'">$(GeneratedJitStencilsDir)jit_shim-aarch64-pc-windows-msvc.o;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(UseJIT)' == 'true' and $(Platform) == 'Win32'">$(GeneratedJitStencilsDir)jit_shim-i686-pc-windows-msvc.o;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(UseJIT)' == 'true' and $(Platform) == 'x64'">$(GeneratedJitStencilsDir)jit_shim-x86_64-pc-windows-msvc.o;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> @@ -168,9 +172,11 @@ <ClInclude Include="..\Include\cpython\pylock.h" /> <ClInclude Include="..\Include\cpython\longintrepr.h" /> <ClInclude Include="..\Include\cpython\longobject.h" /> + <ClInclude Include="..\Include\cpython\marshal.h" /> <ClInclude Include="..\Include\cpython\memoryobject.h" /> <ClInclude Include="..\Include\cpython\methodobject.h" /> <ClInclude Include="..\Include\cpython\modsupport.h" /> + <ClInclude Include="..\Include\cpython\monitoring.h" /> <ClInclude Include="..\Include\cpython\object.h" /> <ClInclude Include="..\Include\cpython\objimpl.h" /> <ClInclude Include="..\Include\cpython\odictobject.h" /> @@ -189,7 +195,10 @@ <ClInclude Include="..\Include\cpython\pystats.h" /> <ClInclude Include="..\Include\cpython\pythonrun.h" /> <ClInclude Include="..\Include\cpython\pythread.h" /> + <ClInclude Include="..\Include\cpython\sentinelobject.h" /> <ClInclude Include="..\Include\cpython\setobject.h" /> + <ClInclude Include="..\Include\cpython\sliceobject.h" /> + <ClInclude Include="..\Include\cpython\structseq.h" /> <ClInclude Include="..\Include\cpython\traceback.h" /> <ClInclude Include="..\Include\cpython\tracemalloc.h" /> <ClInclude Include="..\Include\cpython\tupleobject.h" /> @@ -268,10 +277,12 @@ <ClInclude Include="..\Include\internal\pycore_interpolation.h" /> <ClInclude Include="..\Include\internal\pycore_intrinsics.h" /> <ClInclude Include="..\Include\internal\pycore_jit.h" /> + <ClInclude Include="..\Include\internal\pycore_lazyimportobject.h" /> <ClInclude Include="..\Include\internal\pycore_list.h" /> <ClInclude Include="..\Include\internal\pycore_llist.h" /> <ClInclude Include="..\Include\internal\pycore_lock.h" /> <ClInclude Include="..\Include\internal\pycore_long.h" /> + <ClInclude Include="..\Include\internal\pycore_mmap.h" /> <ClInclude Include="..\Include\internal\pycore_modsupport.h" /> <ClInclude Include="..\Include\internal\pycore_moduleobject.h" /> <ClInclude Include="..\Include\internal\pycore_namespace.h" /> @@ -305,6 +316,8 @@ <ClInclude Include="..\Include\internal\pycore_setobject.h" /> <ClInclude Include="..\Include\internal\pycore_signal.h" /> <ClInclude Include="..\Include\internal\pycore_sliceobject.h" /> + <ClInclude Include="..\Include\internal\pycore_slots.h" /> + <ClInclude Include="..\Include\internal\pycore_slots_generated.h" /> <ClInclude Include="..\Include\internal\pycore_stackref.h" /> <ClInclude Include="..\Include\internal\pycore_stats.h" /> <ClInclude Include="..\Include\internal\pycore_strhex.h" /> @@ -324,6 +337,7 @@ <ClInclude Include="..\Include\internal\pycore_typevarobject.h" /> <ClInclude Include="..\Include\internal\pycore_ucnhash.h" /> <ClInclude Include="..\Include\internal\pycore_unionobject.h" /> + <ClInclude Include="..\Include\internal\pycore_unicodectype.h" /> <ClInclude Include="..\Include\internal\pycore_unicodeobject.h" /> <ClInclude Include="..\Include\internal\pycore_unicodeobject_generated.h" /> <ClInclude Include="..\Include\internal\pycore_uniqueid.h" /> @@ -332,7 +346,6 @@ <ClInclude Include="..\Include\intrcheck.h" /> <ClInclude Include="..\Include\iterobject.h" /> <ClInclude Include="..\Include\listobject.h" /> - <ClInclude Include="..\Include\pylock.h" /> <ClInclude Include="..\Include\longobject.h" /> <ClInclude Include="..\Include\marshal.h" /> <ClInclude Include="..\Include\memoryobject.h" /> @@ -352,6 +365,7 @@ <ClInclude Include="..\Include\osmodule.h" /> <ClInclude Include="..\Include\patchlevel.h" /> <ClInclude Include="..\Include\py_curses.h" /> + <ClInclude Include="..\Include\pyabi.h" /> <ClInclude Include="..\Include\pyatomic.h" /> <ClInclude Include="..\Include\pybuffer.h" /> <ClInclude Include="..\Include\pycapsule.h" /> @@ -375,6 +389,8 @@ <ClInclude Include="..\Include\refcount.h" /> <ClInclude Include="..\Include\setobject.h" /> <ClInclude Include="..\Include\sliceobject.h" /> + <ClInclude Include="..\Include\slots.h" /> + <ClInclude Include="..\Include\slots_generated.h" /> <ClInclude Include="..\Include\structmember.h" /> <ClInclude Include="..\Include\structseq.h" /> <ClInclude Include="..\Include\sysmodule.h" /> @@ -475,6 +491,7 @@ <ClCompile Include="..\Modules\hmacmodule.c" /> <ClCompile Include="..\Modules\itertoolsmodule.c" /> <ClCompile Include="..\Modules\main.c" /> + <ClCompile Include="..\Modules\mathintegermodule.c" /> <ClCompile Include="..\Modules\mathmodule.c" /> <ClCompile Include="..\Modules\md5module.c" /> <ClCompile Include="..\Modules\mmapmodule.c" /> @@ -541,6 +558,7 @@ <ClCompile Include="..\Objects\longobject.c" /> <ClCompile Include="..\Objects\memoryobject.c" /> <ClCompile Include="..\Objects\methodobject.c" /> + <ClCompile Include="..\Objects\lazyimportobject.c" /> <ClCompile Include="..\Objects\moduleobject.c" /> <ClCompile Include="..\Objects\namespaceobject.c" /> <ClCompile Include="..\Objects\object.c" /> @@ -548,6 +566,7 @@ <ClCompile Include="..\Objects\odictobject.c" /> <ClCompile Include="..\Objects\picklebufobject.c" /> <ClCompile Include="..\Objects\rangeobject.c" /> + <ClCompile Include="..\Objects\sentinelobject.c" /> <ClCompile Include="..\Objects\setobject.c" /> <ClCompile Include="..\Objects\sliceobject.c" /> <ClCompile Include="..\Objects\structseq.c" /> @@ -555,7 +574,10 @@ <ClCompile Include="..\Objects\tupleobject.c" /> <ClCompile Include="..\Objects\typeobject.c" /> <ClCompile Include="..\Objects\typevarobject.c" /> + <ClCompile Include="..\Objects\unicode_format.c" /> <ClCompile Include="..\Objects\unicodectype.c" /> + <ClCompile Include="..\Objects\unicode_formatter.c" /> + <ClCompile Include="..\Objects\unicode_writer.c" /> <ClCompile Include="..\Objects\unicodeobject.c" /> <ClCompile Include="..\Objects\unionobject.c" /> <ClCompile Include="..\Objects\weakrefobject.c" /> @@ -590,7 +612,9 @@ <ClCompile Include="..\Python\bltinmodule.c" /> <ClCompile Include="..\Python\bootstrap_hash.c" /> <ClCompile Include="..\Python\brc.c" /> - <ClCompile Include="..\Python\ceval.c" /> + <ClCompile Include="..\Python\ceval.c"> + <AdditionalOptions Condition="'$(UseTailCallInterp)' == 'true' and $(PlatformToolset) != 'ClangCL'">/std:clatest %(AdditionalOptions)</AdditionalOptions> + </ClCompile> <ClCompile Include="..\Python\codecs.c" /> <ClCompile Include="..\Python\codegen.c" /> <ClCompile Include="..\Python\compile.c" /> @@ -598,11 +622,13 @@ <ClCompile Include="..\Python\critical_section.c" /> <ClCompile Include="..\Python\crossinterp.c" /> <ClCompile Include="..\Python\dynamic_annotations.c" /> - <ClCompile Include="..\Python\dynload_win.c" /> + <ClCompile Include="..\Python\dynload_win.c"> + <PreprocessorDefinitions Condition="$(DisableGil) != 'true'">PY3_DLLNAME=L"$(Py3DllName)";%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>ABI3T_DLLNAME=L"$(Abi3tDllName)";%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> <ClCompile Include="..\Python\errors.c" /> <ClCompile Include="..\Python\fileutils.c" /> <ClCompile Include="..\Python\flowgraph.c" /> - <ClCompile Include="..\Python\formatter_unicode.c" /> <ClCompile Include="..\Python\frame.c" /> <ClCompile Include="..\Python\frozen.c"> <AdditionalIncludeDirectories>$(GeneratedFrozenModulesDir)Python;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> @@ -629,6 +655,7 @@ <ClCompile Include="..\Python\instruction_sequence.c" /> <ClCompile Include="..\Python\instrumentation.c" /> <ClCompile Include="..\Python\jit.c" /> + <ClCompile Include="..\Python\jit_publish.c" /> <ClCompile Include="..\Python\legacy_tracing.c" /> <ClCompile Include="..\Python\lock.c" /> <ClCompile Include="..\Python\marshal.c" /> @@ -651,6 +678,7 @@ <ClCompile Include="..\Python\pymath.c" /> <ClCompile Include="..\Python\pytime.c" /> <ClCompile Include="..\Python\pystate.c" /> + <ClCompile Include="..\Python\pystats.c" /> <ClCompile Include="..\Python\pystrcmp.c" /> <ClCompile Include="..\Python\pystrhex.c" /> <ClCompile Include="..\Python\pystrtod.c" /> @@ -662,6 +690,8 @@ <ClCompile Include="..\Python\pythonrun.c" /> <ClCompile Include="..\Python\specialize.c" /> <ClCompile Include="..\Python\suggestions.c" /> + <ClCompile Include="..\Python\slots.c" /> + <ClCompile Include="..\Python\slots_generated.c" /> <ClCompile Include="..\Python\structmember.c" /> <ClCompile Include="..\Python\symtable.c" /> <ClCompile Include="..\Python\sysmodule.c"> @@ -716,7 +746,7 @@ </ClCompile> </ItemGroup> </Target> - <Target Name="_WarnAboutToolset" BeforeTargets="PrepareForBuild" Condition="$(PlatformToolset) != 'v141' and $(PlatformToolset) != 'v142' and $(PlatformToolset) != 'v143'"> + <Target Name="_WarnAboutToolset" BeforeTargets="PrepareForBuild" Condition="$(PlatformToolset) != 'v143' and $(PlatformToolset) != 'v145'"> <Warning Text="Toolset $(PlatformToolset) is not used for official builds. Your build may have errors or incompatibilities." /> </Target> <Target Name="_WarnAboutZlib" BeforeTargets="PrepareForBuild" Condition="!$(IncludeExternals)"> diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index e9eedfd1312fae..649ee1859ff996 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -120,9 +120,6 @@ <ClInclude Include="..\Include\listobject.h"> <Filter>Include</Filter> </ClInclude> - <ClInclude Include="..\Include\pylock.h"> - <Filter>Include</Filter> - </ClInclude> <ClInclude Include="..\Include\longobject.h"> <Filter>Include</Filter> </ClInclude> @@ -159,6 +156,9 @@ <ClInclude Include="..\Include\py_curses.h"> <Filter>Include</Filter> </ClInclude> + <ClInclude Include="..\Include\pyabi.h"> + <Filter>Include</Filter> + </ClInclude> <ClInclude Include="..\Include\pyatomic.h"> <Filter>Include</Filter> </ClInclude> @@ -228,6 +228,12 @@ <ClInclude Include="..\Include\sliceobject.h"> <Filter>Include</Filter> </ClInclude> + <ClInclude Include="..\Include\slots.h"> + <Filter>Include</Filter> + </ClInclude> + <ClInclude Include="..\Include\slots_generated.h"> + <Filter>Include</Filter> + </ClInclude> <ClInclude Include="..\Include\stats.h"> <Filter>Include</Filter> </ClInclude> @@ -489,9 +495,15 @@ <ClInclude Include="..\Include\cpython\pylifecycle.h"> <Filter>Include\cpython</Filter> </ClInclude> + <ClInclude Include="..\Include\cpython\structseq.h"> + <Filter>Include\cpython</Filter> + </ClInclude> <ClInclude Include="..\Include\cpython\tupleobject.h"> <Filter>Include\cpython</Filter> </ClInclude> + <ClInclude Include="..\Include\cpython\sliceobject.h"> + <Filter>Include\cpython</Filter> + </ClInclude> <ClInclude Include="..\Include\cpython\traceback.h"> <Filter>Include\cpython</Filter> </ClInclude> @@ -513,6 +525,9 @@ <ClInclude Include="..\Include\cpython\pythread.h"> <Filter>Include\cpython</Filter> </ClInclude> + <ClInclude Include="..\Include\cpython\sentinelobject.h"> + <Filter>Include</Filter> + </ClInclude> <ClInclude Include="..\Include\cpython\setobject.h"> <Filter>Include\cpython</Filter> </ClInclude> @@ -525,6 +540,9 @@ <ClInclude Include="..\Include\cpython\initconfig.h"> <Filter>Include\cpython</Filter> </ClInclude> + <ClInclude Include="..\Include\internal\pycore_unicodectype.h"> + <Filter>Include\internal</Filter> + </ClInclude> <ClInclude Include="..\Include\internal\pycore_unicodeobject.h"> <Filter>Include\internal</Filter> </ClInclude> @@ -729,6 +747,12 @@ <ClInclude Include="..\Include\internal\pycore_long.h"> <Filter>Include\internal</Filter> </ClInclude> + <ClInclude Include="..\Include\internal\pycore_lazyimportobject.h"> + <Filter>Include\internal</Filter> + </ClInclude> + <ClInclude Include="..\Include\internal\pycore_mmap.h"> + <Filter>Include\internal</Filter> + </ClInclude> <ClInclude Include="..\Include\internal\pycore_modsupport.h"> <Filter>Include\internal</Filter> </ClInclude> @@ -828,6 +852,12 @@ <ClInclude Include="..\Include\internal\pycore_sliceobject.h"> <Filter>Include\internal</Filter> </ClInclude> + <ClInclude Include="..\Include\internal\pycore_slots.h"> + <Filter>Include\internal</Filter> + </ClInclude> + <ClInclude Include="..\Include\internal\pycore_slots_generated.h"> + <Filter>Include\internal</Filter> + </ClInclude> <ClInclude Include="..\Include\internal\pycore_strhex.h"> <Filter>Include\internal</Filter> </ClInclude> @@ -882,6 +912,15 @@ <ClInclude Include="..\Include\internal\pycore_uniqueid.h"> <Filter>Include\internal</Filter> </ClInclude> + <ClInclude Include="..\Include\internal\pycore_uop.h"> + <Filter>Include\internal</Filter> + </ClInclude> + <ClInclude Include="..\Include\internal\pycore_uop_ids.h"> + <Filter>Include\internal</Filter> + </ClInclude> + <ClInclude Include="..\Include\internal\pycore_uop_metadata.h"> + <Filter>Include\internal</Filter> + </ClInclude> <ClInclude Include="..\Include\internal\mimalloc\mimalloc.h"> <Filter>Include\internal\mimalloc</Filter> </ClInclude> @@ -1055,6 +1094,9 @@ <ClCompile Include="..\Modules\main.c"> <Filter>Modules</Filter> </ClCompile> + <ClCompile Include="..\Modules\mathintegermodule.c"> + <Filter>Modules</Filter> + </ClCompile> <ClCompile Include="..\Modules\mathmodule.c"> <Filter>Modules</Filter> </ClCompile> @@ -1229,6 +1271,9 @@ <ClCompile Include="..\Objects\methodobject.c"> <Filter>Objects</Filter> </ClCompile> + <ClCompile Include="..\Objects\lazyimportobject.c"> + <Filter>Objects</Filter> + </ClCompile> <ClCompile Include="..\Objects\moduleobject.c"> <Filter>Objects</Filter> </ClCompile> @@ -1244,6 +1289,9 @@ <ClCompile Include="..\Objects\rangeobject.c"> <Filter>Objects</Filter> </ClCompile> + <ClCompile Include="..\Objects\sentinelobject.c"> + <Filter>Objects</Filter> + </ClCompile> <ClCompile Include="..\Objects\setobject.c"> <Filter>Objects</Filter> </ClCompile> @@ -1259,9 +1307,18 @@ <ClCompile Include="..\Objects\typeobject.c"> <Filter>Objects</Filter> </ClCompile> + <ClCompile Include="..\Objects\unicode_format.c"> + <Filter>Objects</Filter> + </ClCompile> <ClCompile Include="..\Objects\unicodectype.c"> <Filter>Objects</Filter> </ClCompile> + <ClCompile Include="..\Objects\unicode_formatter.c"> + <Filter>Objects</Filter> + </ClCompile> + <ClCompile Include="..\Objects\unicode_writer.c"> + <Filter>Objects</Filter> + </ClCompile> <ClCompile Include="..\Objects\unicodeobject.c"> <Filter>Objects</Filter> </ClCompile> @@ -1370,9 +1427,6 @@ <ClCompile Include="..\Python\flowgraph.c"> <Filter>Python</Filter> </ClCompile> - <ClCompile Include="..\Python\formatter_unicode.c"> - <Filter>Python</Filter> - </ClCompile> <ClCompile Include="..\Python\frozen.c"> <Filter>Python</Filter> </ClCompile> @@ -1439,6 +1493,9 @@ <ClCompile Include="..\Python\jit.c"> <Filter>Python</Filter> </ClCompile> + <ClCompile Include="..\Python\jit_publish.c"> + <Filter>Python</Filter> + </ClCompile> <ClCompile Include="..\Python\legacy_tracing.c"> <Filter>Source Files</Filter> </ClCompile> @@ -1532,6 +1589,12 @@ <ClCompile Include="..\Python\specialize.c"> <Filter>Python</Filter> </ClCompile> + <ClCompile Include="..\Objects\slots.c"> + <Filter>Objects</Filter> + </ClCompile> + <ClCompile Include="..\Objects\slots_generated.c"> + <Filter>Objects</Filter> + </ClCompile> <ClCompile Include="..\Python\structmember.c"> <Filter>Python</Filter> </ClCompile> diff --git a/PCbuild/pythonw.vcxproj b/PCbuild/pythonw.vcxproj index c6a5b8ce90a0d9..244cdf622ad915 100644 --- a/PCbuild/pythonw.vcxproj +++ b/PCbuild/pythonw.vcxproj @@ -115,4 +115,12 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> + <Target Name="CopyFreethreadedBinary" AfterTargets="AfterBuild" + Condition="$(DisableGil) == 'true' and $(Configuration) != 'PGInstrument'"> + <Message Text="Duplicating $(TargetPath) to $(PyWExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe for free-threaded compatibility" /> + <Copy SourceFiles="$(TargetPath)" + DestinationFiles="$(OutDir)\$(PyWExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe" + SkipUnchangedFiles="true" + UseHardLinksIfPossible="true" /> + </Target> </Project> \ No newline at end of file diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 3ae3255d933967..7c5eab2eb75a1d 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -6,7 +6,7 @@ Quick Start Guide 1a. Optionally install Python 3.10 or later. If not installed, get_externals.bat (via build.bat) will download and use Python via NuGet. -2. Run "build.bat" to build Python in 32-bit Release configuration. +2. Run "build.bat" to build Python in 64-bit Release configuration. 3. (Optional, but recommended) Run the test suite with "rt.bat -q". @@ -160,12 +160,17 @@ pyshellext pyshellext.dll, the shell extension deployed with the launcher python3dll python3.dll, the PEP 384 Stable ABI dll + (not installed on free-threaded builds) +python3tdll + python3t.dll, the PEP 803 free-threading Stable ABI dll + (built from the same source as python3.dll) xxlimited builds an example module that makes use of the PEP 384 Stable ABI, see Modules\xxlimited.c xxlimited_35 - ditto for testing the Python 3.5 stable ABI, see - Modules\xxlimited_35.c +xxlimited_3_13 + ditto for testing older Limited API, see + Modules\xxlimited_*.c The following sub-projects are for individual modules of the standard library which are implemented in C; each one builds a DLL (renamed to @@ -218,7 +223,7 @@ _lzma https://tukaani.org/xz/ _ssl - Python wrapper for version 3.0.15 of the OpenSSL secure sockets + Python wrapper for version 3.5 of the OpenSSL secure sockets library, which is itself downloaded from our binaries repository at https://github.com/python/cpython-bin-deps and built by openssl.vcxproj. @@ -237,12 +242,12 @@ _ssl again when building. _sqlite3 - Wraps SQLite 3.49.1, which is itself built by sqlite3.vcxproj + Wraps SQLite 3.53.2, which is itself built by sqlite3.vcxproj Homepage: https://www.sqlite.org/ _tkinter - Wraps version 8.6.15 of the Tk windowing system, which is downloaded + Wraps version 9.0.3 of the Tk windowing system, which is downloaded from our binaries repository at https://github.com/python/cpython-bin-deps. @@ -359,6 +364,11 @@ Supported flags are: * WITH_COMPUTED_GOTOS: build the interpreter using "computed gotos". Currently only supported by clang-cl. +* UsePymallocHugepages: enable huge page support for pymalloc arenas. + When enabled, the arena size on 64-bit platforms is increased to 2 MiB + and arena allocation uses MEM_LARGE_PAGES with automatic fallback to + regular pages. Can also be enabled via `--pymalloc-hugepages` flag. + Static library -------------- diff --git a/PCbuild/regen.targets b/PCbuild/regen.targets index 742597f5cb5ebd..9552e73ef6a2ec 100644 --- a/PCbuild/regen.targets +++ b/PCbuild/regen.targets @@ -35,6 +35,9 @@ <_JITOutputs Include="$(GeneratedJitStencilsDir)jit_stencils-aarch64-pc-windows-msvc.h" Condition="$(Platform) == 'ARM64'"/> <_JITOutputs Include="$(GeneratedJitStencilsDir)jit_stencils-i686-pc-windows-msvc.h" Condition="$(Platform) == 'Win32'"/> <_JITOutputs Include="$(GeneratedJitStencilsDir)jit_stencils-x86_64-pc-windows-msvc.h" Condition="$(Platform) == 'x64'"/> + <_JITOutputs Include="$(GeneratedJitStencilsDir)jit_shim-aarch64-pc-windows-msvc.o" Condition="$(Platform) == 'ARM64'"/> + <_JITOutputs Include="$(GeneratedJitStencilsDir)jit_shim-i686-pc-windows-msvc.o" Condition="$(Platform) == 'Win32'"/> + <_JITOutputs Include="$(GeneratedJitStencilsDir)jit_shim-x86_64-pc-windows-msvc.o" Condition="$(Platform) == 'x64'"/> <_CasesSources Include="$(PySourcePath)Python\bytecodes.c;$(PySourcePath)Python\optimizer_bytecodes.c;"/> <_CasesOutputs Include="$(PySourcePath)Python\generated_cases.c.h;$(PySourcePath)Include\opcode_ids.h;$(PySourcePath)Include\internal\pycore_uop_ids.h;$(PySourcePath)Python\opcode_targets.h;$(PySourcePath)Include\internal\pycore_opcode_metadata.h;$(PySourcePath)Include\internal\pycore_uop_metadata.h;$(PySourcePath)Python\optimizer_cases.c.h;$(PySourcePath)Lib\_opcode_metadata.py"/> <_SbomSources Include="$(PySourcePath)PCbuild\get_externals.bat" /> @@ -112,6 +115,10 @@ WorkingDirectory="$(PySourcePath)" /> <Exec Command="$(PythonForBuild) Tools\cases_generator\uop_metadata_generator.py Python\bytecodes.c" WorkingDirectory="$(PySourcePath)" /> + <Exec Command="$(PythonForBuild) Tools\cases_generator\target_generator.py -o Modules\_testinternalcapi\test_targets.h Python\bytecodes.c Modules\_testinternalcapi\testbytecodes.c" + WorkingDirectory="$(PySourcePath)" /> + <Exec Command="$(PythonForBuild) Tools\cases_generator\tier1_generator.py -o Modules\_testinternalcapi\test_cases.c.h Python\bytecodes.c Modules\_testinternalcapi\testbytecodes.c" + WorkingDirectory="$(PySourcePath)" /> </Target> <Target Name="_RegenJIT" @@ -125,7 +132,7 @@ <JITArgs Condition="$(Platform) == 'x64'">x86_64-pc-windows-msvc</JITArgs> <JITArgs Condition="$(Configuration) == 'Debug'">$(JITArgs) --debug</JITArgs> </PropertyGroup> - <Exec Command='$(PythonForBuild) "$(PySourcePath)Tools\jit\build.py" $(JITArgs) --output-dir "$(GeneratedJitStencilsDir)" --pyconfig-dir "$(PySourcePath)PC"'/> + <Exec Command='$(PythonForBuild) "$(PySourcePath)Tools\jit\build.py" $(JITArgs) --output-dir "$(GeneratedJitStencilsDir.TrimEnd(`\`))" --pyconfig-dir "$(PySourcePath)PC" --llvm-version="$(LLVM_VERSION)" --llvm-tools-install-dir="$(LLVM_TOOLS_INSTALL_DIR)"'/> </Target> <Target Name="_CleanJIT" AfterTargets="Clean"> <Delete Files="@(_JITOutputs)"/> diff --git a/PCbuild/rt.bat b/PCbuild/rt.bat index f1e0607393405b..d5c9a24f292327 100644 --- a/PCbuild/rt.bat +++ b/PCbuild/rt.bat @@ -32,6 +32,7 @@ setlocal set pcbuild=%~dp0 set pyname=python set suffix= +set suffix1= set qmode= set dashO= set regrtestargs=--fast-ci @@ -41,8 +42,7 @@ set exe= if "%~1"=="-O" (set dashO=-O) & shift & goto CheckOpts if "%~1"=="-q" (set qmode=yes) & shift & goto CheckOpts if "%~1"=="-d" (set suffix=_d) & shift & goto CheckOpts -rem HACK: Need some way to infer the version number in this script -if "%~1"=="--disable-gil" (set pyname=python3.15t) & shift & goto CheckOpts +if "%~1"=="--disable-gil" (set suffix1=t) & shift & goto CheckOpts if "%~1"=="-win32" (set prefix=%pcbuild%win32) & shift & goto CheckOpts if "%~1"=="-x64" (set prefix=%pcbuild%amd64) & shift & goto CheckOpts if "%~1"=="-amd64" (set prefix=%pcbuild%amd64) & shift & goto CheckOpts @@ -52,7 +52,7 @@ if "%~1"=="-p" (call :SetPlatform %~2) & shift & shift & goto CheckOpts if NOT "%~1"=="" (set regrtestargs=%regrtestargs% %~1) & shift & goto CheckOpts if not defined prefix set prefix=%pcbuild%amd64 -set exe=%prefix%\%pyname%%suffix%.exe +set exe=%prefix%%suffix1%\%pyname%%suffix%.exe set cmd="%exe%" %dashO% -m test %regrtestargs% if defined qmode goto Qmode @@ -60,7 +60,7 @@ echo Deleting .pyc files ... "%exe%" "%pcbuild%rmpyc.py" echo Cleaning _pth files ... -if exist %prefix%\*._pth del %prefix%\*._pth +if exist %prefix%%suffix1%\*._pth del %prefix%%suffix1%\*._pth echo on %cmd% diff --git a/PCbuild/tcl.vcxproj b/PCbuild/tcl.vcxproj index ab68db9210fbe4..2233559dfc8f5a 100644 --- a/PCbuild/tcl.vcxproj +++ b/PCbuild/tcl.vcxproj @@ -63,8 +63,8 @@ <NMakeBuildCommandLine>setlocal set VCINSTALLDIR=$(VCInstallDir) cd /D "$(tclDir)win" -nmake -f makefile.vc MACHINE=$(TclMachine) OPTS=$(TclOpts) $(TclDirs) $(DebugFlags) $(WarningsFlags) TCLSH_NATIVE="$(tclWin32Exe)" core shell dlls -nmake -f makefile.vc MACHINE=$(TclMachine) OPTS=$(TclOpts) $(TclDirs) $(DebugFlags) $(WarningsFlags) TCLSH_NATIVE="$(tclWin32Exe)" install-binaries install-libraries +nmake -f makefile.vc MACHINE=$(TclMachine) OPTS=$(TclOpts) $(TclDirs) $(DebugFlags) $(WarningsFlags) $(TclshNativeFlag) core shell dlls +nmake -f makefile.vc MACHINE=$(TclMachine) OPTS=$(TclOpts) $(TclDirs) $(DebugFlags) $(WarningsFlags) $(TclshNativeFlag) install-binaries install-libraries copy /Y ..\license.terms "$(OutDir)\tcllicense.terms" </NMakeBuildCommandLine> </PropertyGroup> @@ -74,4 +74,4 @@ copy /Y ..\license.terms "$(OutDir)\tcllicense.terms" <Target Name="Clean" /> <Target Name="ResolveAssemblyReferences" /> -</Project> \ No newline at end of file +</Project> diff --git a/PCbuild/tcltk.props b/PCbuild/tcltk.props index d26b36ba98e493..28e8c0db4d1eaf 100644 --- a/PCbuild/tcltk.props +++ b/PCbuild/tcltk.props @@ -2,7 +2,7 @@ <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="pyproject.props" Condition="$(__PyProject_Props_Imported) != 'true'" /> <PropertyGroup> - <TclVersion Condition="$(TclVersion) == ''">8.6.15.0</TclVersion> + <TclVersion Condition="$(TclVersion) == ''">9.0.3.0</TclVersion> <TkVersion Condition="$(TkVersion) == ''">$(TclVersion)</TkVersion> <TclMajorVersion>$([System.Version]::Parse($(TclVersion)).Major)</TclMajorVersion> <TclMinorVersion>$([System.Version]::Parse($(TclVersion)).Minor)</TclMinorVersion> @@ -12,16 +12,17 @@ <TkMinorVersion>$([System.Version]::Parse($(TkVersion)).Minor)</TkMinorVersion> <TkPatchLevel>$([System.Version]::Parse($(TkVersion)).Build)</TkPatchLevel> <TkRevision>$([System.Version]::Parse($(TkVersion)).Revision)</TkRevision> - <tclDir Condition="$(tclDir) == ''">$(ExternalsDir)tcl-core-$(TclVersion)\</tclDir> + <!-- Back compat hack. If you're here wondering about -core vs not, do us both a favor and just override the whole tclDir variable --> + <tclDir Condition="$(tclDir) == '' and $(TclMajorVersion) == '8'">$(ExternalsDir)tcl-core-$(TclVersion)\</tclDir> + <tclDir Condition="$(tclDir) == ''">$(ExternalsDir)tcl-$(TclVersion)\</tclDir> <tkDir Condition="$(tkDir) == ''">$(ExternalsDir)tk-$(TkVersion)\</tkDir> <tcltkDir Condition="$(tcltkDir) == ''">$(ExternalsDir)tcltk-$(TclVersion)\$(ArchName)\</tcltkDir> - <tclWin32Exe Condition="$(Platform) == 'Win32'">$(tcltkDir)\bin\tclsh$(TclMajorVersion)$(TclMinorVersion)t.exe</tclWin32Exe> - <tclWin32Exe Condition="$(Platform) != 'Win32'">$(tcltkDir)\..\win32\bin\tclsh$(TclMajorVersion)$(TclMinorVersion)t.exe</tclWin32Exe> + <tcltkSuffix Condition="'$(TclMajorVersion)' == '8'">t</tcltkSuffix> + <tkPrefix Condition="'$(TclMajorVersion)' == '9'">tcl9</tkPrefix> + <TclshNativeFlag Condition="$(Platform) != 'Win32'">TCLSH_NATIVE="$(tcltkDir)\..\win32\bin\tclsh$(TclMajorVersion)$(TclMinorVersion)$(tcltkSuffix).exe"</TclshNativeFlag> <tclExternalTommath Condition="$(TclMajorVersion) == '9'">TCL_WITH_EXTERNAL_TOMMATH;</tclExternalTommath> <!--<TclDebugExt Condition="'$(Configuration)' == 'Debug'">g</TclDebugExt>--> - <tcltkSuffix Condition="'$(TclMajorVersion)' == '8'">t</tcltkSuffix> - <tkPrefix Condition="'$(TclMajorVersion)' == '9'">tcl9</tkPrefix> <tclDLLName >tcl$(TclMajorVersion)$(TclMinorVersion)$(tcltkSuffix)$(TclDebugExt).dll</tclDLLName> <tclLibName>tcl$(TclMajorVersion)$(TclMinorVersion)$(tcltkSuffix)$(TclDebugExt).lib</tclLibName> <tclShExeName>tclsh$(TclMajorVersion)$(TclMinorVersion)$(tcltkSuffix)$(TclDebugExt).exe</tclShExeName> diff --git a/PCbuild/tk.vcxproj b/PCbuild/tk.vcxproj index b111969ca5de6c..204244db0d3e4b 100644 --- a/PCbuild/tk.vcxproj +++ b/PCbuild/tk.vcxproj @@ -64,8 +64,8 @@ <NMakeBuildCommandLine>setlocal set VCINSTALLDIR=$(VCInstallDir) cd /D "$(tkDir)win" -nmake /nologo -f makefile.vc RC=rc MACHINE=$(TclMachine) OPTS=$(TkOpts) $(TkDirs) $(DebugFlags) $(WarningsFlags) TCLSH_NATIVE="$(tclWin32Exe)" all -nmake /nologo -f makefile.vc RC=rc MACHINE=$(TclMachine) OPTS=$(TkOpts) $(TkDirs) $(DebugFlags) $(WarningsFlags) TCLSH_NATIVE="$(tclWin32Exe)" install-binaries install-libraries +nmake /nologo -f makefile.vc RC=rc MACHINE=$(TclMachine) OPTS=$(TkOpts) $(TkDirs) $(DebugFlags) $(WarningsFlags) $(TclshNativeFlag) all +nmake /nologo -f makefile.vc RC=rc MACHINE=$(TclMachine) OPTS=$(TkOpts) $(TkDirs) $(DebugFlags) $(WarningsFlags) $(TclshNativeFlag) install-binaries install-libraries copy /Y ..\license.terms "$(OutDir)\tklicense.terms" </NMakeBuildCommandLine> </PropertyGroup> @@ -80,4 +80,4 @@ copy /Y ..\license.terms "$(OutDir)\tklicense.terms" <Target Name="Clean" /> <Target Name="ResolveAssemblyReferences" /> -</Project> \ No newline at end of file +</Project> diff --git a/PCbuild/venvlauncher.vcxproj b/PCbuild/venvlauncher.vcxproj index abaf3a979af268..a2e8ffa82b10eb 100644 --- a/PCbuild/venvlauncher.vcxproj +++ b/PCbuild/venvlauncher.vcxproj @@ -89,10 +89,13 @@ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="pyproject.props" /> </ImportGroup> - <PropertyGroup Label="UserMacros" /> + <PropertyGroup Label="UserMacros"> + <ExeName>$(PyExeName)$(PyDebugExt).exe</ExeName> + <ExeName Condition="$(DisableGil) == 'true'">$(PyExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe</ExeName> + </PropertyGroup> <ItemDefinitionGroup> <ClCompile> - <PreprocessorDefinitions>EXENAME=L"$(PyExeName)$(PyDebugExt).exe";_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>EXENAME=L"$(ExeName)";_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> <ResourceCompile> diff --git a/PCbuild/venvwlauncher.vcxproj b/PCbuild/venvwlauncher.vcxproj index c58280deb8abeb..f2aaf83fe2b378 100644 --- a/PCbuild/venvwlauncher.vcxproj +++ b/PCbuild/venvwlauncher.vcxproj @@ -89,10 +89,13 @@ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="pyproject.props" /> </ImportGroup> - <PropertyGroup Label="UserMacros" /> + <PropertyGroup Label="UserMacros"> + <ExeName>$(PyWExeName)$(PyDebugExt).exe</ExeName> + <ExeName Condition="$(DisableGil) == 'true'">$(PyWExeName)$(MajorVersionNumber).$(MinorVersionNumber)t$(PyDebugExt).exe</ExeName> + </PropertyGroup> <ItemDefinitionGroup> <ClCompile> - <PreprocessorDefinitions>EXENAME=L"$(PyWExeName)$(PyDebugExt).exe";_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>EXENAME=L"$(ExeName)";_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> <ResourceCompile> diff --git a/PCbuild/xxlimited.vcxproj b/PCbuild/xxlimited.vcxproj index 093e6920c0b76c..f0c3616600148f 100644 --- a/PCbuild/xxlimited.vcxproj +++ b/PCbuild/xxlimited.vcxproj @@ -104,6 +104,9 @@ <ProjectReference Include="python3dll.vcxproj"> <Project>{885d4898-d08d-4091-9c40-c700cfe3fc5a}</Project> </ProjectReference> + <ProjectReference Include="python3tdll.vcxproj"> + <Project>{947BB5F5-6025-4A4F-8182-1B175469F8D2}</Project> + </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/PCbuild/xxlimited_35.vcxproj b/PCbuild/xxlimited_35.vcxproj index 3f4d4463f24af0..bfaf4e253664d4 100644 --- a/PCbuild/xxlimited_35.vcxproj +++ b/PCbuild/xxlimited_35.vcxproj @@ -104,6 +104,9 @@ <ProjectReference Include="python3dll.vcxproj"> <Project>{885d4898-d08d-4091-9c40-c700cfe3fc5a}</Project> </ProjectReference> + <ProjectReference Include="python3tdll.vcxproj"> + <Project>{947BB5F5-6025-4A4F-8182-1B175469F8D2}</Project> + </ProjectReference> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/PCbuild/xxlimited_3_13.vcxproj b/PCbuild/xxlimited_3_13.vcxproj new file mode 100644 index 00000000000000..7a9760fd43121e --- /dev/null +++ b/PCbuild/xxlimited_3_13.vcxproj @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|ARM"> + <Configuration>Debug</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|ARM64"> + <Configuration>Debug</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGInstrument|ARM"> + <Configuration>PGInstrument</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGInstrument|ARM64"> + <Configuration>PGInstrument</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGInstrument|Win32"> + <Configuration>PGInstrument</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGInstrument|x64"> + <Configuration>PGInstrument</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGUpdate|ARM"> + <Configuration>PGUpdate</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGUpdate|ARM64"> + <Configuration>PGUpdate</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGUpdate|Win32"> + <Configuration>PGUpdate</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="PGUpdate|x64"> + <Configuration>PGUpdate</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM"> + <Configuration>Release</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{fb868ea7-f93a-4d9b-be78-ca4e9ba14fff}</ProjectGuid> + <RootNamespace>xxlimited_3_13</RootNamespace> + <Keyword>Win32Proj</Keyword> + </PropertyGroup> + <Import Project="python.props" /> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>NotSet</CharacterSet> + <SupportPGO>false</SupportPGO> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <TargetExt>$(PyStdlibPydExt)</TargetExt> + </PropertyGroup> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="pyproject.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> + </PropertyGroup> + <ItemDefinitionGroup> + <Link> + <AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\Modules\xxlimited_3_13.c" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="python3dll.vcxproj"> + <Project>{885d4898-d08d-4091-9c40-c700cfe3fc5a}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/PCbuild/xxlimited_3_13.vcxproj.filters b/PCbuild/xxlimited_3_13.vcxproj.filters new file mode 100644 index 00000000000000..3dfb7800edc441 --- /dev/null +++ b/PCbuild/xxlimited_3_13.vcxproj.filters @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{5be27194-6530-452d-8d86-3767b991fa83}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\Modules\xxlimited_3_13.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> diff --git a/PCbuild/zlib-ng.vcxproj b/PCbuild/zlib-ng.vcxproj index de1698ae718473..ffe8e70f2dbbc7 100644 --- a/PCbuild/zlib-ng.vcxproj +++ b/PCbuild/zlib-ng.vcxproj @@ -219,13 +219,15 @@ <PropertyGroup> <Text>$([System.IO.File]::ReadAllText('$(zlibNgDir)\zlib.h.in').Replace('@ZLIB_SYMBOL_PREFIX@', ''))</Text> </PropertyGroup> - <WriteLinesToFile File="$(IntDir)zlib.h" Lines="$(Text)" /> + <MakeDir Directories="$(GeneratedZlibNgDir)" /> + <WriteLinesToFile File="$(GeneratedZlibNgDir)zlib.h" Lines="$(Text)" /> </Target> <Target Name="_EnsureZlibNgH" Inputs="$(zlibNgDir)\zlib-ng.h.in" Outputs="$(IntDir)zlib-ng.h"> <PropertyGroup> <Text>$([System.IO.File]::ReadAllText('$(zlibNgDir)\zlib-ng.h.in').Replace('@ZLIB_SYMBOL_PREFIX@', ''))</Text> </PropertyGroup> - <WriteLinesToFile File="$(IntDir)zlib-ng.h" Lines="$(Text)" /> + <MakeDir Directories="$(GeneratedZlibNgDir)" /> + <WriteLinesToFile File="$(GeneratedZlibNgDir)zlib-ng.h" Lines="$(Text)" /> </Target> <Target Name="_EnsureZlibNgHeaders" BeforeTargets="PrepareForBuild" diff --git a/Parser/Python.asdl b/Parser/Python.asdl index 96f3914b029d4c..2f0b123858f8d1 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -45,8 +45,8 @@ module Python | TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody) | Assert(expr test, expr? msg) - | Import(alias* names) - | ImportFrom(identifier? module, alias* names, int? level) + | Import(alias* names, int? is_lazy) + | ImportFrom(identifier? module, alias* names, int? level, int? is_lazy) | Global(identifier* names) | Nonlocal(identifier* names) @@ -67,7 +67,7 @@ module Python | Set(expr* elts) | ListComp(expr elt, comprehension* generators) | SetComp(expr elt, comprehension* generators) - | DictComp(expr key, expr value, comprehension* generators) + | DictComp(expr key, expr? value, comprehension* generators) | GeneratorExp(expr elt, comprehension* generators) -- the grammar constrains where yield expressions can occur | Await(expr value) @@ -114,7 +114,7 @@ module Python attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset) arguments = (arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs, - expr* kw_defaults, arg? kwarg, expr* defaults) + expr?* kw_defaults, arg? kwarg, expr* defaults) arg = (identifier arg, expr? annotation, string? type_comment) attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset) diff --git a/Parser/action_helpers.c b/Parser/action_helpers.c index ebc94715b6f361..c74eb34deb7c35 100644 --- a/Parser/action_helpers.c +++ b/Parser/action_helpers.c @@ -435,6 +435,9 @@ _PyPegen_name_default_pair(Parser *p, arg_ty arg, expr_ty value, Token *tc) return NULL; } a->arg = _PyPegen_add_type_comment_to_arg(p, arg, tc); + if (!a->arg) { + return NULL; + } a->value = value; return a; } @@ -947,6 +950,35 @@ _PyPegen_check_legacy_stmt(Parser *p, expr_ty name) { return 0; } +void * +_PyPegen_raise_error_for_missing_comma(Parser *p, expr_ty a, expr_ty b) +{ + // Don't raise for legacy statements like "print x" or "exec x" + if (_PyPegen_check_legacy_stmt(p, a)) { + return NULL; + } + // Only raise inside parentheses/brackets (level > 0) + if (p->tokens[p->mark - 1]->level == 0) { + return NULL; + } + // For multi-line expressions (like string concatenations), point to the + // last line instead of the first for a more helpful error message. + // Use a->col_offset as the starting column since all strings in the + // concatenation typically share the same indentation. + if (a->end_lineno > a->lineno) { + return RAISE_ERROR_KNOWN_LOCATION( + p, PyExc_SyntaxError, a->end_lineno, a->col_offset, + a->end_lineno, a->end_col_offset, + "invalid syntax. Perhaps you forgot a comma?" + ); + } + return RAISE_ERROR_KNOWN_LOCATION( + p, PyExc_SyntaxError, a->lineno, a->col_offset, + b->end_lineno, b->end_col_offset, + "invalid syntax. Perhaps you forgot a comma?" + ); +} + static ResultTokenWithMetadata * result_token_with_metadata(Parser *p, void *result, PyObject *metadata) { @@ -1384,6 +1416,9 @@ expr_ty _PyPegen_template_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions, Token *b) { asdl_expr_seq *resized_exprs = _get_resized_exprs(p, a, raw_expressions, b, TSTRING); + if (resized_exprs == NULL) { + return NULL; + } return _PyAST_TemplateStr(resized_exprs, a->lineno, a->col_offset, b->end_lineno, b->end_col_offset, p->arena); @@ -1393,6 +1428,9 @@ expr_ty _PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* raw_expressions, Token*b) { asdl_expr_seq *resized_exprs = _get_resized_exprs(p, a, raw_expressions, b, FSTRING); + if (resized_exprs == NULL) { + return NULL; + } return _PyAST_JoinedStr(resized_exprs, a->lineno, a->col_offset, b->end_lineno, b->end_col_offset, p->arena); @@ -1404,7 +1442,15 @@ expr_ty _PyPegen_decoded_constant_from_token(Parser* p, Token* tok) { if (PyBytes_AsStringAndSize(tok->bytes, &bstr, &bsize) == -1) { return NULL; } - PyObject* str = _PyPegen_decode_string(p, 0, bstr, bsize, tok); + + // Check if we're inside a raw f-string for format spec decoding + int is_raw = 0; + if (INSIDE_FSTRING(p->tok)) { + tokenizer_mode *mode = TOK_GET_MODE(p->tok); + is_raw = mode->raw; + } + + PyObject* str = _PyPegen_decode_string(p, is_raw, bstr, bsize, tok); if (str == NULL) { return NULL; } @@ -1604,19 +1650,46 @@ _build_concatenated_bytes(Parser *p, asdl_expr_seq *strings, int lineno, Py_ssize_t len = asdl_seq_LEN(strings); assert(len > 0); - PyObject* res = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); - /* Bytes literals never get a kind, but just for consistency since they are represented as Constant nodes, we'll mirror the same behavior as unicode strings for determining the kind. */ - PyObject* kind = asdl_seq_GET(strings, 0)->v.Constant.kind; + PyObject *kind = asdl_seq_GET(strings, 0)->v.Constant.kind; + + Py_ssize_t total = 0; for (Py_ssize_t i = 0; i < len; i++) { expr_ty elem = asdl_seq_GET(strings, i); - PyBytes_Concat(&res, elem->v.Constant.value); + PyObject *bytes = elem->v.Constant.value; + Py_ssize_t part = PyBytes_GET_SIZE(bytes); + if (part > PY_SSIZE_T_MAX - total) { + PyErr_NoMemory(); + return NULL; + } + total += part; } - if (!res || _PyArena_AddPyObject(arena, res) < 0) { - Py_XDECREF(res); + + PyBytesWriter *writer = PyBytesWriter_Create(total); + if (writer == NULL) { + return NULL; + } + char *out = PyBytesWriter_GetData(writer); + + for (Py_ssize_t i = 0; i < len; i++) { + expr_ty elem = asdl_seq_GET(strings, i); + PyObject *bytes = elem->v.Constant.value; + Py_ssize_t part = PyBytes_GET_SIZE(bytes); + if (part > 0) { + memcpy(out, PyBytes_AS_STRING(bytes), part); + out += part; + } + } + + PyObject *res = PyBytesWriter_Finish(writer); + if (res == NULL) { + return NULL; + } + if (_PyArena_AddPyObject(arena, res) < 0) { + Py_DECREF(res); return NULL; } return _PyAST_Constant(res, kind, lineno, col_offset, end_lineno, end_col_offset, p->arena); @@ -1907,10 +1980,16 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings, } stmt_ty -_PyPegen_checked_future_import(Parser *p, identifier module, asdl_alias_seq * names, int level, - int lineno, int col_offset, int end_lineno, int end_col_offset, - PyArena *arena) { +_PyPegen_checked_future_import(Parser *p, identifier module, asdl_alias_seq * names, + int level, expr_ty lazy_token, int lineno, + int col_offset, int end_lineno, int end_col_offset, + PyArena *arena) { if (level == 0 && PyUnicode_CompareWithASCIIString(module, "__future__") == 0) { + if (lazy_token) { + RAISE_SYNTAX_ERROR_KNOWN_LOCATION(lazy_token, + "lazy from __future__ import is not allowed"); + return NULL; + } for (Py_ssize_t i = 0; i < asdl_seq_LEN(names); i++) { alias_ty alias = asdl_seq_GET(names, i); if (PyUnicode_CompareWithASCIIString(alias->name, "barry_as_FLUFL") == 0) { @@ -1918,7 +1997,8 @@ _PyPegen_checked_future_import(Parser *p, identifier module, asdl_alias_seq * na } } } - return _PyAST_ImportFrom(module, names, level, lineno, col_offset, end_lineno, end_col_offset, arena); + return _PyAST_ImportFrom(module, names, level, lazy_token ? 1 : 0, lineno, + col_offset, end_lineno, end_col_offset, arena); } asdl_stmt_seq* @@ -1931,6 +2011,9 @@ _PyPegen_register_stmts(Parser *p, asdl_stmt_seq* stmts) { return stmts; } stmt_ty last_stmt = asdl_seq_GET(stmts, len - 1); + if (p->last_stmt_location.lineno > last_stmt->lineno) { + return stmts; + } p->last_stmt_location.lineno = last_stmt->lineno; p->last_stmt_location.col_offset = last_stmt->col_offset; p->last_stmt_location.end_lineno = last_stmt->end_lineno; diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index dba20226c3283a..e2a57177d20afb 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -487,7 +487,7 @@ def visitField(self, sum): class Obj2ModPrototypeVisitor(PickleVisitor): def visitProduct(self, prod, name): - code = "static int obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, PyArena* arena);" + code = "static int obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, const char* field, PyArena* arena);" self.emit(code % (name, get_c_type(name)), 0) visitSum = visitProduct @@ -511,7 +511,7 @@ def recursive_call(self, node, level): def funcHeader(self, name): ctype = get_c_type(name) self.emit("int", 0) - self.emit("obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, PyArena* arena)" % (name, ctype), 0) + self.emit("obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, const char* field, PyArena* arena)" % (name, ctype), 0) self.emit("{", 0) self.emit("int isinstance;", 1) self.emit("", 0) @@ -547,6 +547,18 @@ def simpleSum(self, sum, name): def buildArgs(self, fields): return ", ".join(fields + ["arena"]) + def typeCheck(self, name): + self.emit("tp = state->%s_type;" % name, 1) + self.emit("isinstance = PyObject_IsInstance(obj, tp);", 1) + self.emit("if (isinstance == -1) {", 1) + self.emit("return 1;", 2) + self.emit("}", 1) + self.emit("if (!isinstance && field != NULL) {", 1) + error = "field '%%s' was expecting node of type '%s', got '%%s'" % name + self.emit("PyErr_Format(PyExc_TypeError, \"%s\", field, _PyType_Name(Py_TYPE(obj)));" % error, 2, reflow=False) + self.emit("return 1;", 2) + self.emit("}", 1) + def complexSum(self, sum, name): self.funcHeader(name) self.emit("PyObject *tmp = NULL;", 1) @@ -559,6 +571,7 @@ def complexSum(self, sum, name): self.emit("*out = NULL;", 2) self.emit("return 0;", 2) self.emit("}", 1) + self.typeCheck(name) for a in sum.attributes: self.visitField(a, name, sum=sum, depth=1) for t in sum.types: @@ -593,7 +606,7 @@ def visitSum(self, sum, name): def visitProduct(self, prod, name): ctype = get_c_type(name) self.emit("int", 0) - self.emit("obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, PyArena* arena)" % (name, ctype), 0) + self.emit("obj2ast_%s(struct ast_state *state, PyObject* obj, %s* out, const char* field, PyArena* arena)" % (name, ctype), 0) self.emit("{", 0) self.emit("PyObject* tmp = NULL;", 1) for f in prod.fields: @@ -694,8 +707,8 @@ def visitField(self, field, name, sum=None, prod=None, depth=0): self.emit("%s val;" % ctype, depth+2) self.emit("PyObject *tmp2 = Py_NewRef(PyList_GET_ITEM(tmp, i));", depth+2) with self.recursive_call(name, depth+2): - self.emit("res = obj2ast_%s(state, tmp2, &val, arena);" % - field.type, depth+2, reflow=False) + self.emit("res = obj2ast_%s(state, tmp2, &val, \"%s\", arena);" % + (field.type, field.name), depth+2, reflow=False) self.emit("Py_DECREF(tmp2);", depth+2) self.emit("if (res != 0) goto failed;", depth+2) self.emit("if (len != PyList_GET_SIZE(tmp)) {", depth+2) @@ -709,8 +722,8 @@ def visitField(self, field, name, sum=None, prod=None, depth=0): self.emit("}", depth+1) else: with self.recursive_call(name, depth+1): - self.emit("res = obj2ast_%s(state, tmp, &%s, arena);" % - (field.type, field.name), depth+1) + self.emit("res = obj2ast_%s(state, tmp, &%s, \"%s\", arena);" % + (field.type, field.name, field.name), depth+1) self.emit("if (res != 0) goto failed;", depth+1) self.emit("Py_CLEAR(tmp);", depth+1) @@ -873,6 +886,70 @@ def visitModule(self, mod): return 0; } +/* + * Format the names in the set 'missing' into a natural language list, + * sorted in the order in which they appear in 'fields'. + * + * Similar to format_missing() from 'Python/ceval.c'. + * + * Parameters + * + * missing Set of missing field names to render. + * fields Sequence of AST node field names (self._fields). + */ +static PyObject * +format_missing(PyObject *missing, PyObject *fields) +{ + Py_ssize_t num_fields, num_total, num_left; + num_fields = PySequence_Size(fields); + if (num_fields == -1) { + return NULL; + } + num_total = num_left = PySet_GET_SIZE(missing); + PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); + if (writer == NULL) { + goto error; + } + // Iterate all AST node fields in order so that the missing positional + // arguments are rendered in the order in which __init__ expects them. + for (Py_ssize_t i = 0; i < num_fields; i++) { + PyObject *name = PySequence_GetItem(fields, i); + if (name == NULL) { + goto error; + } + int contains = PySet_Contains(missing, name); + if (contains == -1) { + Py_DECREF(name); + goto error; + } + else if (contains == 1) { + const char* fmt = NULL; + if (num_left == 1) { + fmt = "'%U'"; + } + else if (num_total == 2) { + fmt = "'%U' and "; + } + else if (num_left == 2) { + fmt = "'%U', and "; + } + else { + fmt = "'%U', "; + } + num_left--; + if (PyUnicodeWriter_Format(writer, fmt, name) < 0) { + Py_DECREF(name); + goto error; + } + } + Py_DECREF(name); + } + return PyUnicodeWriter_Finish(writer); +error: + PyUnicodeWriter_Discard(writer); + return NULL; +} + static int ast_type_init(PyObject *self, PyObject *args, PyObject *kw) { @@ -881,6 +958,19 @@ def visitModule(self, mod): return -1; } + int contains = PySet_Contains(state->abstract_types, (PyObject *)Py_TYPE(self)); + if (contains == -1) { + return -1; + } + else if (contains == 1) { + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, 1, + "Instantiating abstract AST node class %T is deprecated. " + "This will become an error in Python 3.20", self) < 0) { + return -1; + } + } + Py_ssize_t i, numfields = 0; int res = -1; PyObject *key, *value, *fields, *attributes = NULL, *remaining_fields = NULL; @@ -942,8 +1032,8 @@ def visitModule(self, mod): } if (p == 0) { PyErr_Format(PyExc_TypeError, - "%.400s got multiple values for argument '%U'", - Py_TYPE(self)->tp_name, key); + "%T got multiple values for argument %R", + self, key); res = -1; goto cleanup; } @@ -963,16 +1053,11 @@ def visitModule(self, mod): goto cleanup; } else if (contains == 0) { - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, 1, - "%.400s.__init__ got an unexpected keyword argument '%U'. " - "Support for arbitrary keyword arguments is deprecated " - "and will be removed in Python 3.15.", - Py_TYPE(self)->tp_name, key - ) < 0) { - res = -1; - goto cleanup; - } + PyErr_Format(PyExc_TypeError, + "%T.__init__ got an unexpected keyword argument %R", + self, key); + res = -1; + goto cleanup; } } res = PyObject_SetAttr(self, key, value); @@ -982,7 +1067,7 @@ def visitModule(self, mod): } } Py_ssize_t size = PySet_Size(remaining_fields); - PyObject *field_types = NULL, *remaining_list = NULL; + PyObject *field_types = NULL, *remaining_list = NULL, *missing_names = NULL; if (size > 0) { if (PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self), &_Py_ID(_field_types), &field_types) < 0) { @@ -999,6 +1084,10 @@ def visitModule(self, mod): if (!remaining_list) { goto set_remaining_cleanup; } + missing_names = PySet_New(NULL); + if (!missing_names) { + goto set_remaining_cleanup; + } for (Py_ssize_t i = 0; i < size; i++) { PyObject *name = PyList_GET_ITEM(remaining_list, i); PyObject *type = PyDict_GetItemWithError(field_types, name); @@ -1007,14 +1096,10 @@ def visitModule(self, mod): goto set_remaining_cleanup; } else { - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, 1, - "Field '%U' is missing from %.400s._field_types. " - "This will become an error in Python 3.15.", - name, Py_TYPE(self)->tp_name - ) < 0) { - goto set_remaining_cleanup; - } + PyErr_Format(PyExc_TypeError, + "Field %R is missing from %T._field_types", + name, self); + goto set_remaining_cleanup; } } else if (_PyUnion_Check(type)) { @@ -1042,16 +1127,25 @@ def visitModule(self, mod): } else { // simple field (e.g., identifier) - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, 1, - "%.400s.__init__ missing 1 required positional argument: '%U'. " - "This will become an error in Python 3.15.", - Py_TYPE(self)->tp_name, name - ) < 0) { + res = PySet_Add(missing_names, name); + if (res < 0) { goto set_remaining_cleanup; } } } + Py_ssize_t num_missing = PySet_GET_SIZE(missing_names); + if (num_missing > 0) { + PyObject *name_str = format_missing(missing_names, fields); + if (!name_str) { + goto set_remaining_cleanup; + } + PyErr_Format(PyExc_TypeError, + "%T.__init__ missing %d required positional argument%s: %U", + self, num_missing, num_missing == 1 ? "" : "s", name_str); + Py_DECREF(name_str); + goto set_remaining_cleanup; + } + Py_DECREF(missing_names); Py_DECREF(remaining_list); Py_DECREF(field_types); } @@ -1061,6 +1155,7 @@ def visitModule(self, mod): Py_XDECREF(remaining_fields); return res; set_remaining_cleanup: + Py_XDECREF(missing_names); Py_XDECREF(remaining_list); Py_XDECREF(field_types); res = -1; @@ -1144,182 +1239,6 @@ def visitModule(self, mod): return result; } -/* - * Perform the following validations: - * - * - All keyword arguments are known 'fields' or 'attributes'. - * - No field or attribute would be left unfilled after copy.replace(). - * - * On success, this returns 1. Otherwise, set a TypeError - * exception and returns -1 (no exception is set if some - * other internal errors occur). - * - * Parameters - * - * self The AST node instance. - * dict The AST node instance dictionary (self.__dict__). - * fields The list of fields (self._fields). - * attributes The list of attributes (self._attributes). - * kwargs Keyword arguments passed to ast_type_replace(). - * - * The 'dict', 'fields', 'attributes' and 'kwargs' arguments can be NULL. - * - * Note: this function can be removed in 3.15 since the verification - * will be done inside the constructor. - */ -static inline int -ast_type_replace_check(PyObject *self, - PyObject *dict, - PyObject *fields, - PyObject *attributes, - PyObject *kwargs) -{ - // While it is possible to make some fast paths that would avoid - // allocating objects on the stack, this would cost us readability. - // For instance, if 'fields' and 'attributes' are both empty, and - // 'kwargs' is not empty, we could raise a TypeError immediately. - PyObject *expecting = PySet_New(fields); - if (expecting == NULL) { - return -1; - } - if (attributes) { - if (_PySet_Update(expecting, attributes) < 0) { - Py_DECREF(expecting); - return -1; - } - } - // Any keyword argument that is neither a field nor attribute is rejected. - // We first need to check whether a keyword argument is accepted or not. - // If all keyword arguments are accepted, we compute the required fields - // and attributes. A field or attribute is not needed if: - // - // 1) it is given in 'kwargs', or - // 2) it already exists on 'self'. - if (kwargs) { - Py_ssize_t pos = 0; - PyObject *key, *value; - while (PyDict_Next(kwargs, &pos, &key, &value)) { - int rc = PySet_Discard(expecting, key); - if (rc < 0) { - Py_DECREF(expecting); - return -1; - } - if (rc == 0) { - PyErr_Format(PyExc_TypeError, - "%.400s.__replace__ got an unexpected keyword " - "argument '%U'.", Py_TYPE(self)->tp_name, key); - Py_DECREF(expecting); - return -1; - } - } - } - // check that the remaining fields or attributes would be filled - if (dict) { - Py_ssize_t pos = 0; - PyObject *key, *value; - while (PyDict_Next(dict, &pos, &key, &value)) { - // Mark fields or attributes that are found on the instance - // as non-mandatory. If they are not given in 'kwargs', they - // will be shallow-coied; otherwise, they would be replaced - // (not in this function). - if (PySet_Discard(expecting, key) < 0) { - Py_DECREF(expecting); - return -1; - } - } - if (attributes) { - // Some attributes may or may not be present at runtime. - // In particular, now that we checked whether 'kwargs' - // is correct or not, we allow any attribute to be missing. - // - // Note that fields must still be entirely determined when - // calling the constructor later. - PyObject *unused = PyObject_CallMethodOneArg(expecting, - &_Py_ID(difference_update), - attributes); - if (unused == NULL) { - Py_DECREF(expecting); - return -1; - } - Py_DECREF(unused); - } - } - - // Discard fields from 'expecting' that default to None - PyObject *field_types = NULL; - if (PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self), - &_Py_ID(_field_types), - &field_types) < 0) - { - Py_DECREF(expecting); - return -1; - } - if (field_types != NULL) { - Py_ssize_t pos = 0; - PyObject *field_name, *field_type; - while (PyDict_Next(field_types, &pos, &field_name, &field_type)) { - if (_PyUnion_Check(field_type)) { - // optional field - if (PySet_Discard(expecting, field_name) < 0) { - Py_DECREF(expecting); - Py_DECREF(field_types); - return -1; - } - } - } - Py_DECREF(field_types); - } - - // Now 'expecting' contains the fields or attributes - // that would not be filled inside ast_type_replace(). - Py_ssize_t m = PySet_GET_SIZE(expecting); - if (m > 0) { - PyObject *names = PyList_New(m); - if (names == NULL) { - Py_DECREF(expecting); - return -1; - } - Py_ssize_t i = 0, pos = 0; - PyObject *item; - Py_hash_t hash; - while (_PySet_NextEntry(expecting, &pos, &item, &hash)) { - PyObject *name = PyObject_Repr(item); - if (name == NULL) { - Py_DECREF(expecting); - Py_DECREF(names); - return -1; - } - // steal the reference 'name' - PyList_SET_ITEM(names, i++, name); - } - Py_DECREF(expecting); - if (PyList_Sort(names) < 0) { - Py_DECREF(names); - return -1; - } - PyObject *sep = PyUnicode_FromString(", "); - if (sep == NULL) { - Py_DECREF(names); - return -1; - } - PyObject *str_names = PyUnicode_Join(sep, names); - Py_DECREF(sep); - Py_DECREF(names); - if (str_names == NULL) { - return -1; - } - PyErr_Format(PyExc_TypeError, - "%.400s.__replace__ missing %ld keyword argument%s: %U.", - Py_TYPE(self)->tp_name, m, m == 1 ? "" : "s", str_names); - Py_DECREF(str_names); - return -1; - } - else { - Py_DECREF(expecting); - return 1; - } -} - /* * Python equivalent: * @@ -1409,9 +1328,6 @@ def visitModule(self, mod): if (PyObject_GetOptionalAttr(self, state->__dict__, &dict) < 0) { goto cleanup; } - if (ast_type_replace_check(self, dict, fields, attributes, kwargs) < 0) { - goto cleanup; - } empty_tuple = PyTuple_New(0); if (empty_tuple == NULL) { goto cleanup; @@ -1798,7 +1714,9 @@ def visitModule(self, mod): /* Conversion Python -> AST */ -static int obj2ast_object(struct ast_state *Py_UNUSED(state), PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_object(struct ast_state *Py_UNUSED(state), PyObject* obj, + PyObject** out, + const char* Py_UNUSED(field), PyArena* arena) { if (obj == Py_None) obj = NULL; @@ -1815,7 +1733,9 @@ def visitModule(self, mod): return 0; } -static int obj2ast_constant(struct ast_state *Py_UNUSED(state), PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_constant(struct ast_state *Py_UNUSED(state), PyObject* obj, + PyObject** out, + const char* Py_UNUSED(field), PyArena* arena) { if (_PyArena_AddPyObject(arena, obj) < 0) { *out = NULL; @@ -1825,29 +1745,29 @@ def visitModule(self, mod): return 0; } -static int obj2ast_identifier(struct ast_state *state, PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_identifier(struct ast_state *state, PyObject* obj, PyObject** out, const char* field, PyArena* arena) { if (!PyUnicode_CheckExact(obj) && obj != Py_None) { - PyErr_SetString(PyExc_TypeError, "AST identifier must be of type str"); + PyErr_Format(PyExc_TypeError, "field '%s' was expecting a string object", field); return -1; } - return obj2ast_object(state, obj, out, arena); + return obj2ast_object(state, obj, out, field, arena); } -static int obj2ast_string(struct ast_state *state, PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_string(struct ast_state *state, PyObject* obj, PyObject** out, const char* field, PyArena* arena) { if (!PyUnicode_CheckExact(obj) && !PyBytes_CheckExact(obj)) { - PyErr_SetString(PyExc_TypeError, "AST string must be of type str"); + PyErr_Format(PyExc_TypeError, "field '%s' was expecting a string or bytes object", field); return -1; } - return obj2ast_object(state, obj, out, arena); + return obj2ast_object(state, obj, out, field, arena); } -static int obj2ast_int(struct ast_state* Py_UNUSED(state), PyObject* obj, int* out, PyArena* arena) +static int obj2ast_int(struct ast_state* Py_UNUSED(state), PyObject* obj, int* out, const char* field, PyArena* arena) { int i; if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_ValueError, "invalid integer value: %R", obj); + PyErr_Format(PyExc_ValueError, "field \\"%s\\" got an invalid integer value: %R", field, obj); return -1; } @@ -1887,6 +1807,13 @@ def visitModule(self, mod): if (!state->AST_type) { return -1; } + state->abstract_types = PySet_New(NULL); + if (!state->abstract_types) { + return -1; + } + if (PySet_Add(state->abstract_types, state->AST_type) < 0) { + return -1; + } if (add_ast_fields(state) < 0) { return -1; } @@ -1928,6 +1855,7 @@ def visitSum(self, sum, name): (name, name, len(sum.attributes)), 1) else: self.emit("if (add_attributes(state, state->%s_type, NULL, 0) < 0) return -1;" % name, 1) + self.emit("if (PySet_Add(state->abstract_types, state->%s_type) < 0) return -1;" % name, 1) self.emit_defaults(name, sum.attributes, 1) simple = is_simple(sum) for t in sum.types: @@ -1960,6 +1888,30 @@ def emit_defaults(self, name, fields, depth): class ASTModuleVisitor(PickleVisitor): def visitModule(self, mod): + self.emit(""" +/* Helper for checking if a node class is abstract in the tests. */ +static PyObject * +ast_is_abstract(PyObject *Py_UNUSED(module), PyObject *cls) { + struct ast_state *state = get_ast_state(); + if (state == NULL) { + return NULL; + } + int contains = PySet_Contains(state->abstract_types, cls); + if (contains == -1) { + return NULL; + } + else if (contains == 1) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +static struct PyMethodDef astmodule_methods[] = { + {"_is_abstract", ast_is_abstract, METH_O, NULL}, + {NULL} /* Sentinel */ +}; +""".strip(), 0, reflow=False) + self.emit("", 0) self.emit("static int", 0) self.emit("astmodule_exec(PyObject *m)", 0) self.emit("{", 0) @@ -1989,6 +1941,7 @@ def visitModule(self, mod): self.emit("", 0) self.emit(""" static PyModuleDef_Slot astmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, astmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -2000,7 +1953,8 @@ def visitModule(self, mod): .m_name = "_ast", // The _ast module uses a per-interpreter state (PyInterpreterState.ast) .m_size = 0, - .m_slots = astmodule_slots, + .m_methods = astmodule_methods, + .m_slots = astmodule_slots }; PyMODINIT_FUNC @@ -2213,7 +2167,7 @@ class PartingShots(StaticVisitor): } mod_ty res = NULL; - if (obj2ast_mod(state, ast, &res, arena) != 0) + if (obj2ast_mod(state, ast, &res, NULL, arena) != 0) return NULL; else return res; @@ -2289,6 +2243,7 @@ def generate_module_def(mod, metadata, f, internal_h): "%s_type" % type for type in metadata.types ) + module_state.add("abstract_types") state_strings = sorted(state_strings) module_state = sorted(module_state) diff --git a/Parser/lexer/buffer.c b/Parser/lexer/buffer.c index 63aa1ea2ad4f60..e122fd0d9878ea 100644 --- a/Parser/lexer/buffer.c +++ b/Parser/lexer/buffer.c @@ -13,8 +13,8 @@ _PyLexer_remember_fstring_buffers(struct tok_state *tok) for (index = tok->tok_mode_stack_index; index >= 0; --index) { mode = &(tok->tok_mode_stack[index]); - mode->start_offset = mode->start - tok->buf; - mode->multi_line_start_offset = mode->multi_line_start - tok->buf; + mode->start_offset = mode->start == NULL ? -1 : mode->start - tok->buf; + mode->multi_line_start_offset = mode->multi_line_start == NULL ? -1 : mode->multi_line_start - tok->buf; } } @@ -27,8 +27,8 @@ _PyLexer_restore_fstring_buffers(struct tok_state *tok) for (index = tok->tok_mode_stack_index; index >= 0; --index) { mode = &(tok->tok_mode_stack[index]); - mode->start = tok->buf + mode->start_offset; - mode->multi_line_start = tok->buf + mode->multi_line_start_offset; + mode->start = mode->start_offset < 0 ? NULL : tok->buf + mode->start_offset; + mode->multi_line_start = mode->multi_line_start_offset < 0 ? NULL : tok->buf + mode->multi_line_start_offset; } } diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index 81363cf8e810fe..7f25afec302c22 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -539,6 +539,9 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t return MAKE_TOKEN(ERRORTOKEN); } } + else if (c == EOF && PyErr_Occurred()) { + return MAKE_TOKEN(ERRORTOKEN); + } else { break; } @@ -1376,7 +1379,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "invalid non-printable character U+%04X", c)); } - if( c == '=' && INSIDE_FSTRING_EXPR(current_tok)) { + if( c == '=' && INSIDE_FSTRING_EXPR_AT_TOP(current_tok)) { current_tok->in_debug = 1; } diff --git a/Parser/lexer/state.c b/Parser/lexer/state.c index 2de9004fe084f2..5cf9b4d768c3eb 100644 --- a/Parser/lexer/state.c +++ b/Parser/lexer/state.c @@ -15,8 +15,11 @@ _PyTokenizer_tok_new(void) struct tok_state *tok = (struct tok_state *)PyMem_Calloc( 1, sizeof(struct tok_state)); - if (tok == NULL) + if (tok == NULL) { + PyErr_NoMemory(); return NULL; + } + tok->buf = tok->cur = tok->inp = NULL; tok->fp_interactive = 0; tok->interactive_src_start = NULL; @@ -43,6 +46,7 @@ _PyTokenizer_tok_new(void) tok->encoding = NULL; tok->cont_line = 0; tok->filename = NULL; + tok->module = NULL; tok->decoding_readline = NULL; tok->decoding_buffer = NULL; tok->readline = NULL; @@ -91,6 +95,7 @@ _PyTokenizer_Free(struct tok_state *tok) Py_XDECREF(tok->decoding_buffer); Py_XDECREF(tok->readline); Py_XDECREF(tok->filename); + Py_XDECREF(tok->module); if ((tok->readline != NULL || tok->fp != NULL ) && tok->buf != NULL) { PyMem_Free(tok->buf); } diff --git a/Parser/lexer/state.h b/Parser/lexer/state.h index 5e8cac7249b21c..9cd196a114c7cb 100644 --- a/Parser/lexer/state.h +++ b/Parser/lexer/state.h @@ -9,6 +9,8 @@ #define INSIDE_FSTRING(tok) (tok->tok_mode_stack_index > 0) #define INSIDE_FSTRING_EXPR(tok) (tok->curly_bracket_expr_start_depth >= 0) +#define INSIDE_FSTRING_EXPR_AT_TOP(tok) \ + (tok->curly_bracket_depth - tok->curly_bracket_expr_start_depth == 1) enum decoding_state { STATE_INIT, @@ -100,6 +102,7 @@ struct tok_state { int parenlinenostack[MAXLEVEL]; int parencolstack[MAXLEVEL]; PyObject *filename; + PyObject *module; /* Stuff for checking on different tab sizes */ int altindstack[MAXINDENT]; /* Stack of alternate indents */ /* Stuff for PEP 0263 */ diff --git a/Parser/myreadline.c b/Parser/myreadline.c index 64e8f5383f0602..ee77479ba7bdcc 100644 --- a/Parser/myreadline.c +++ b/Parser/myreadline.c @@ -344,7 +344,7 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) break; } n += strlen(p + n); - } while (p[n-1] != '\n'); + } while (n == 0 || p[n-1] != '\n'); pr = (char *)PyMem_RawRealloc(p, n+1); if (pr == NULL) { diff --git a/Parser/parser.c b/Parser/parser.c index de5cdc9b6f7f34..c55c081dfc3d8e 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -1,5 +1,6 @@ // @generated by pegen from python.gram #include "pegen.h" +#include "pycore_ceval.h" #if defined(Py_DEBUG) && defined(Py_BUILD_CORE) # define D(x) if (p->debug) { x; } @@ -21,54 +22,54 @@ static KeywordToken *reserved_keywords[] = { (KeywordToken[]) {{NULL, -1}}, (KeywordToken[]) {{NULL, -1}}, (KeywordToken[]) { - {"if", 687}, - {"as", 685}, - {"in", 700}, + {"if", 698}, + {"as", 696}, + {"in", 711}, {"or", 589}, {"is", 597}, {NULL, -1}, }, (KeywordToken[]) { - {"del", 630}, - {"def", 704}, - {"for", 699}, - {"try", 661}, + {"del", 634}, + {"def", 715}, + {"for", 710}, + {"try", 672}, {"and", 590}, - {"not", 708}, + {"not", 719}, {NULL, -1}, }, (KeywordToken[]) { - {"from", 638}, + {"from", 646}, {"pass", 527}, - {"with", 652}, - {"elif", 692}, - {"else", 691}, - {"None", 624}, - {"True", 623}, + {"with", 663}, + {"elif", 703}, + {"else", 702}, + {"None", 628}, + {"True", 627}, {NULL, -1}, }, (KeywordToken[]) { - {"raise", 628}, + {"raise", 632}, {"yield", 588}, {"break", 528}, - {"async", 703}, - {"class", 706}, - {"while", 694}, - {"False", 625}, + {"async", 714}, + {"class", 717}, + {"while", 705}, + {"False", 629}, {"await", 598}, {NULL, -1}, }, (KeywordToken[]) { + {"import", 647}, {"return", 522}, - {"import", 639}, - {"assert", 533}, + {"assert", 638}, {"global", 530}, - {"except", 682}, + {"except", 693}, {"lambda", 622}, {NULL, -1}, }, (KeywordToken[]) { - {"finally", 678}, + {"finally", 689}, {NULL, -1}, }, (KeywordToken[]) { @@ -80,6 +81,7 @@ static KeywordToken *reserved_keywords[] = { static char *soft_keywords[] = { "_", "case", + "lazy", "match", "type", NULL, @@ -194,341 +196,352 @@ static char *soft_keywords[] = { #define type_param_starred_default_type 1107 #define expressions_type 1108 #define expression_type 1109 -#define yield_expr_type 1110 -#define star_expressions_type 1111 -#define star_expression_type 1112 -#define star_named_expressions_type 1113 -#define star_named_expression_type 1114 -#define assignment_expression_type 1115 -#define named_expression_type 1116 -#define disjunction_type 1117 -#define conjunction_type 1118 -#define inversion_type 1119 -#define comparison_type 1120 -#define compare_op_bitwise_or_pair_type 1121 -#define eq_bitwise_or_type 1122 -#define noteq_bitwise_or_type 1123 -#define lte_bitwise_or_type 1124 -#define lt_bitwise_or_type 1125 -#define gte_bitwise_or_type 1126 -#define gt_bitwise_or_type 1127 -#define notin_bitwise_or_type 1128 -#define in_bitwise_or_type 1129 -#define isnot_bitwise_or_type 1130 -#define is_bitwise_or_type 1131 -#define bitwise_or_type 1132 // Left-recursive -#define bitwise_xor_type 1133 // Left-recursive -#define bitwise_and_type 1134 // Left-recursive -#define shift_expr_type 1135 // Left-recursive -#define sum_type 1136 // Left-recursive -#define term_type 1137 // Left-recursive -#define factor_type 1138 -#define power_type 1139 -#define await_primary_type 1140 -#define primary_type 1141 // Left-recursive -#define slices_type 1142 -#define slice_type 1143 -#define atom_type 1144 -#define group_type 1145 -#define lambdef_type 1146 -#define lambda_params_type 1147 -#define lambda_parameters_type 1148 -#define lambda_slash_no_default_type 1149 -#define lambda_slash_with_default_type 1150 -#define lambda_star_etc_type 1151 -#define lambda_kwds_type 1152 -#define lambda_param_no_default_type 1153 -#define lambda_param_with_default_type 1154 -#define lambda_param_maybe_default_type 1155 -#define lambda_param_type 1156 -#define fstring_middle_type 1157 -#define fstring_replacement_field_type 1158 -#define fstring_conversion_type 1159 -#define fstring_full_format_spec_type 1160 -#define fstring_format_spec_type 1161 -#define fstring_type 1162 -#define tstring_format_spec_replacement_field_type 1163 -#define tstring_format_spec_type 1164 -#define tstring_full_format_spec_type 1165 -#define tstring_replacement_field_type 1166 -#define tstring_middle_type 1167 -#define tstring_type 1168 -#define string_type 1169 -#define strings_type 1170 -#define list_type 1171 -#define tuple_type 1172 -#define set_type 1173 -#define dict_type 1174 -#define double_starred_kvpairs_type 1175 -#define double_starred_kvpair_type 1176 -#define kvpair_type 1177 -#define for_if_clauses_type 1178 -#define for_if_clause_type 1179 -#define listcomp_type 1180 -#define setcomp_type 1181 -#define genexp_type 1182 -#define dictcomp_type 1183 -#define arguments_type 1184 -#define args_type 1185 -#define kwargs_type 1186 -#define starred_expression_type 1187 -#define kwarg_or_starred_type 1188 -#define kwarg_or_double_starred_type 1189 -#define star_targets_type 1190 -#define star_targets_list_seq_type 1191 -#define star_targets_tuple_seq_type 1192 -#define star_target_type 1193 -#define target_with_star_atom_type 1194 -#define star_atom_type 1195 -#define single_target_type 1196 -#define single_subscript_attribute_target_type 1197 -#define t_primary_type 1198 // Left-recursive -#define t_lookahead_type 1199 -#define del_targets_type 1200 -#define del_target_type 1201 -#define del_t_atom_type 1202 -#define type_expressions_type 1203 -#define func_type_comment_type 1204 -#define invalid_arguments_type 1205 -#define invalid_kwarg_type 1206 -#define expression_without_invalid_type 1207 -#define invalid_legacy_expression_type 1208 -#define invalid_type_param_type 1209 -#define invalid_expression_type 1210 -#define invalid_named_expression_type 1211 -#define invalid_assignment_type 1212 -#define invalid_ann_assign_target_type 1213 -#define invalid_raise_stmt_type 1214 -#define invalid_del_stmt_type 1215 -#define invalid_block_type 1216 -#define invalid_comprehension_type 1217 -#define invalid_dict_comprehension_type 1218 -#define invalid_parameters_type 1219 -#define invalid_default_type 1220 -#define invalid_star_etc_type 1221 -#define invalid_kwds_type 1222 -#define invalid_parameters_helper_type 1223 -#define invalid_lambda_parameters_type 1224 -#define invalid_lambda_parameters_helper_type 1225 -#define invalid_lambda_star_etc_type 1226 -#define invalid_lambda_kwds_type 1227 -#define invalid_double_type_comments_type 1228 -#define invalid_with_item_type 1229 -#define invalid_for_if_clause_type 1230 -#define invalid_for_target_type 1231 -#define invalid_group_type 1232 -#define invalid_import_type 1233 -#define invalid_dotted_as_name_type 1234 -#define invalid_import_from_as_name_type 1235 -#define invalid_import_from_targets_type 1236 -#define invalid_with_stmt_type 1237 -#define invalid_with_stmt_indent_type 1238 -#define invalid_try_stmt_type 1239 -#define invalid_except_stmt_type 1240 -#define invalid_except_star_stmt_type 1241 -#define invalid_finally_stmt_type 1242 -#define invalid_except_stmt_indent_type 1243 -#define invalid_except_star_stmt_indent_type 1244 -#define invalid_match_stmt_type 1245 -#define invalid_case_block_type 1246 -#define invalid_as_pattern_type 1247 -#define invalid_class_pattern_type 1248 -#define invalid_class_argument_pattern_type 1249 -#define invalid_if_stmt_type 1250 -#define invalid_elif_stmt_type 1251 -#define invalid_else_stmt_type 1252 -#define invalid_while_stmt_type 1253 -#define invalid_for_stmt_type 1254 -#define invalid_def_raw_type 1255 -#define invalid_class_def_raw_type 1256 -#define invalid_double_starred_kvpairs_type 1257 -#define invalid_kvpair_type 1258 -#define invalid_starred_expression_unpacking_type 1259 -#define invalid_starred_expression_type 1260 -#define invalid_fstring_replacement_field_type 1261 -#define invalid_fstring_conversion_character_type 1262 -#define invalid_tstring_replacement_field_type 1263 -#define invalid_tstring_conversion_character_type 1264 -#define invalid_string_tstring_concat_type 1265 -#define invalid_arithmetic_type 1266 -#define invalid_factor_type 1267 -#define invalid_type_params_type 1268 -#define _loop0_1_type 1269 -#define _loop1_2_type 1270 -#define _loop0_3_type 1271 -#define _gather_4_type 1272 -#define _tmp_5_type 1273 -#define _tmp_6_type 1274 -#define _tmp_7_type 1275 -#define _tmp_8_type 1276 -#define _tmp_9_type 1277 -#define _tmp_10_type 1278 -#define _tmp_11_type 1279 -#define _loop1_12_type 1280 -#define _loop0_13_type 1281 -#define _gather_14_type 1282 -#define _tmp_15_type 1283 -#define _tmp_16_type 1284 -#define _loop0_17_type 1285 -#define _loop1_18_type 1286 -#define _loop0_19_type 1287 -#define _gather_20_type 1288 -#define _tmp_21_type 1289 -#define _loop0_22_type 1290 -#define _gather_23_type 1291 -#define _loop1_24_type 1292 -#define _tmp_25_type 1293 -#define _tmp_26_type 1294 -#define _loop0_27_type 1295 -#define _loop0_28_type 1296 -#define _loop1_29_type 1297 -#define _loop1_30_type 1298 -#define _loop0_31_type 1299 -#define _loop1_32_type 1300 -#define _loop0_33_type 1301 -#define _gather_34_type 1302 -#define _tmp_35_type 1303 -#define _loop1_36_type 1304 -#define _loop1_37_type 1305 -#define _loop1_38_type 1306 -#define _loop0_39_type 1307 -#define _gather_40_type 1308 -#define _tmp_41_type 1309 -#define _tmp_42_type 1310 -#define _tmp_43_type 1311 -#define _loop0_44_type 1312 -#define _gather_45_type 1313 -#define _loop0_46_type 1314 -#define _gather_47_type 1315 -#define _tmp_48_type 1316 -#define _loop0_49_type 1317 -#define _gather_50_type 1318 -#define _loop0_51_type 1319 -#define _gather_52_type 1320 -#define _loop0_53_type 1321 -#define _gather_54_type 1322 -#define _loop1_55_type 1323 -#define _loop1_56_type 1324 -#define _loop0_57_type 1325 -#define _gather_58_type 1326 -#define _loop1_59_type 1327 -#define _loop1_60_type 1328 -#define _loop1_61_type 1329 -#define _tmp_62_type 1330 -#define _loop0_63_type 1331 -#define _gather_64_type 1332 -#define _tmp_65_type 1333 -#define _tmp_66_type 1334 -#define _tmp_67_type 1335 -#define _tmp_68_type 1336 -#define _tmp_69_type 1337 -#define _loop0_70_type 1338 -#define _loop0_71_type 1339 -#define _loop1_72_type 1340 -#define _loop1_73_type 1341 -#define _loop0_74_type 1342 -#define _loop1_75_type 1343 -#define _loop0_76_type 1344 -#define _loop0_77_type 1345 -#define _loop0_78_type 1346 -#define _loop0_79_type 1347 -#define _loop1_80_type 1348 -#define _loop1_81_type 1349 -#define _tmp_82_type 1350 -#define _loop0_83_type 1351 -#define _gather_84_type 1352 -#define _loop1_85_type 1353 -#define _loop0_86_type 1354 -#define _tmp_87_type 1355 -#define _loop0_88_type 1356 -#define _gather_89_type 1357 -#define _tmp_90_type 1358 -#define _loop0_91_type 1359 -#define _gather_92_type 1360 -#define _loop0_93_type 1361 -#define _gather_94_type 1362 -#define _loop0_95_type 1363 -#define _loop0_96_type 1364 -#define _gather_97_type 1365 -#define _loop1_98_type 1366 -#define _tmp_99_type 1367 -#define _loop0_100_type 1368 -#define _gather_101_type 1369 -#define _loop0_102_type 1370 -#define _gather_103_type 1371 -#define _tmp_104_type 1372 -#define _tmp_105_type 1373 -#define _loop0_106_type 1374 -#define _gather_107_type 1375 -#define _tmp_108_type 1376 -#define _tmp_109_type 1377 -#define _tmp_110_type 1378 -#define _tmp_111_type 1379 -#define _tmp_112_type 1380 -#define _loop1_113_type 1381 -#define _tmp_114_type 1382 -#define _tmp_115_type 1383 -#define _tmp_116_type 1384 -#define _tmp_117_type 1385 -#define _tmp_118_type 1386 -#define _loop0_119_type 1387 -#define _loop0_120_type 1388 -#define _tmp_121_type 1389 -#define _tmp_122_type 1390 -#define _tmp_123_type 1391 -#define _tmp_124_type 1392 -#define _tmp_125_type 1393 -#define _tmp_126_type 1394 -#define _tmp_127_type 1395 -#define _tmp_128_type 1396 -#define _tmp_129_type 1397 -#define _loop0_130_type 1398 -#define _gather_131_type 1399 -#define _tmp_132_type 1400 -#define _tmp_133_type 1401 -#define _tmp_134_type 1402 -#define _tmp_135_type 1403 -#define _loop0_136_type 1404 -#define _gather_137_type 1405 -#define _tmp_138_type 1406 -#define _loop0_139_type 1407 -#define _gather_140_type 1408 -#define _loop0_141_type 1409 -#define _gather_142_type 1410 -#define _tmp_143_type 1411 -#define _loop0_144_type 1412 -#define _tmp_145_type 1413 -#define _tmp_146_type 1414 -#define _tmp_147_type 1415 -#define _tmp_148_type 1416 -#define _tmp_149_type 1417 -#define _tmp_150_type 1418 -#define _tmp_151_type 1419 -#define _tmp_152_type 1420 -#define _tmp_153_type 1421 -#define _tmp_154_type 1422 -#define _tmp_155_type 1423 -#define _tmp_156_type 1424 -#define _tmp_157_type 1425 -#define _tmp_158_type 1426 -#define _tmp_159_type 1427 -#define _tmp_160_type 1428 -#define _tmp_161_type 1429 -#define _tmp_162_type 1430 -#define _tmp_163_type 1431 -#define _tmp_164_type 1432 -#define _tmp_165_type 1433 -#define _tmp_166_type 1434 -#define _tmp_167_type 1435 -#define _tmp_168_type 1436 -#define _tmp_169_type 1437 -#define _tmp_170_type 1438 -#define _loop0_171_type 1439 -#define _tmp_172_type 1440 -#define _tmp_173_type 1441 -#define _tmp_174_type 1442 -#define _tmp_175_type 1443 -#define _tmp_176_type 1444 +#define if_expression_type 1110 +#define yield_expr_type 1111 +#define star_expressions_type 1112 +#define star_expression_type 1113 +#define star_named_expressions_type 1114 +#define star_named_expressions_sequence_type 1115 +#define star_named_expression_type 1116 +#define star_named_expression_sequence_type 1117 +#define assignment_expression_type 1118 +#define named_expression_type 1119 +#define disjunction_type 1120 +#define conjunction_type 1121 +#define inversion_type 1122 +#define comparison_type 1123 +#define compare_op_bitwise_or_pair_type 1124 +#define eq_bitwise_or_type 1125 +#define noteq_bitwise_or_type 1126 +#define lte_bitwise_or_type 1127 +#define lt_bitwise_or_type 1128 +#define gte_bitwise_or_type 1129 +#define gt_bitwise_or_type 1130 +#define notin_bitwise_or_type 1131 +#define in_bitwise_or_type 1132 +#define isnot_bitwise_or_type 1133 +#define is_bitwise_or_type 1134 +#define bitwise_or_type 1135 // Left-recursive +#define bitwise_xor_type 1136 // Left-recursive +#define bitwise_and_type 1137 // Left-recursive +#define shift_expr_type 1138 // Left-recursive +#define sum_type 1139 // Left-recursive +#define term_type 1140 // Left-recursive +#define factor_type 1141 +#define power_type 1142 +#define await_primary_type 1143 +#define primary_type 1144 // Left-recursive +#define slices_type 1145 +#define slice_type 1146 +#define atom_type 1147 +#define group_type 1148 +#define lambdef_type 1149 +#define lambda_params_type 1150 +#define lambda_parameters_type 1151 +#define lambda_slash_no_default_type 1152 +#define lambda_slash_with_default_type 1153 +#define lambda_star_etc_type 1154 +#define lambda_kwds_type 1155 +#define lambda_param_no_default_type 1156 +#define lambda_param_with_default_type 1157 +#define lambda_param_maybe_default_type 1158 +#define lambda_param_type 1159 +#define fstring_middle_type 1160 +#define fstring_replacement_field_type 1161 +#define fstring_conversion_type 1162 +#define fstring_full_format_spec_type 1163 +#define fstring_format_spec_type 1164 +#define fstring_type 1165 +#define tstring_format_spec_replacement_field_type 1166 +#define tstring_format_spec_type 1167 +#define tstring_full_format_spec_type 1168 +#define tstring_replacement_field_type 1169 +#define tstring_middle_type 1170 +#define tstring_type 1171 +#define string_type 1172 +#define strings_type 1173 +#define list_type 1174 +#define tuple_type 1175 +#define set_type 1176 +#define dict_type 1177 +#define double_starred_kvpairs_type 1178 +#define double_starred_kvpair_type 1179 +#define kvpair_type 1180 +#define for_if_clauses_type 1181 +#define for_if_clause_type 1182 +#define listcomp_type 1183 +#define setcomp_type 1184 +#define genexp_type 1185 +#define dictcomp_type 1186 +#define arguments_type 1187 +#define args_type 1188 +#define kwargs_type 1189 +#define starred_expression_type 1190 +#define kwarg_or_starred_type 1191 +#define kwarg_or_double_starred_type 1192 +#define star_targets_type 1193 +#define star_targets_list_seq_type 1194 +#define star_targets_tuple_seq_type 1195 +#define star_target_type 1196 +#define target_with_star_atom_type 1197 +#define star_atom_type 1198 +#define single_target_type 1199 +#define single_subscript_attribute_target_type 1200 +#define t_primary_type 1201 // Left-recursive +#define t_lookahead_type 1202 +#define del_targets_type 1203 +#define del_target_type 1204 +#define del_t_atom_type 1205 +#define type_expressions_type 1206 +#define func_type_comment_type 1207 +#define invalid_arguments_type 1208 +#define invalid_kwarg_type 1209 +#define expression_without_invalid_type 1210 +#define invalid_legacy_expression_type 1211 +#define invalid_type_param_type 1212 +#define invalid_expression_type 1213 +#define invalid_if_expression_type 1214 +#define invalid_named_expression_type 1215 +#define invalid_assignment_type 1216 +#define invalid_ann_assign_target_type 1217 +#define invalid_raise_stmt_type 1218 +#define invalid_del_stmt_type 1219 +#define invalid_assert_stmt_type 1220 +#define invalid_block_type 1221 +#define invalid_comprehension_type 1222 +#define invalid_parameters_type 1223 +#define invalid_default_type 1224 +#define invalid_star_etc_type 1225 +#define invalid_kwds_type 1226 +#define invalid_parameters_helper_type 1227 +#define invalid_lambda_parameters_type 1228 +#define invalid_lambda_parameters_helper_type 1229 +#define invalid_lambda_star_etc_type 1230 +#define invalid_lambda_kwds_type 1231 +#define invalid_double_type_comments_type 1232 +#define invalid_with_item_type 1233 +#define invalid_for_if_clause_type 1234 +#define invalid_for_target_type 1235 +#define invalid_group_type 1236 +#define invalid_import_type 1237 +#define invalid_dotted_as_name_type 1238 +#define invalid_import_from_as_name_type 1239 +#define invalid_import_from_targets_type 1240 +#define invalid_with_stmt_type 1241 +#define invalid_with_stmt_indent_type 1242 +#define invalid_try_stmt_type 1243 +#define invalid_except_stmt_type 1244 +#define invalid_except_star_stmt_type 1245 +#define invalid_finally_stmt_type 1246 +#define invalid_except_stmt_indent_type 1247 +#define invalid_except_star_stmt_indent_type 1248 +#define invalid_match_stmt_type 1249 +#define invalid_case_block_type 1250 +#define invalid_as_pattern_type 1251 +#define invalid_class_pattern_type 1252 +#define invalid_mapping_pattern_type 1253 +#define invalid_class_argument_pattern_type 1254 +#define invalid_if_stmt_type 1255 +#define invalid_elif_stmt_type 1256 +#define invalid_else_stmt_type 1257 +#define invalid_while_stmt_type 1258 +#define invalid_for_stmt_type 1259 +#define invalid_def_raw_type 1260 +#define invalid_class_def_raw_type 1261 +#define invalid_double_starred_kvpairs_type 1262 +#define invalid_kvpair_unpacking_type 1263 +#define invalid_kvpair_type 1264 +#define invalid_starred_expression_unpacking_type 1265 +#define invalid_starred_expression_unpacking_sequence_type 1266 +#define invalid_starred_expression_type 1267 +#define invalid_fstring_replacement_field_type 1268 +#define invalid_fstring_conversion_character_type 1269 +#define invalid_tstring_replacement_field_type 1270 +#define invalid_tstring_conversion_character_type 1271 +#define invalid_string_tstring_concat_type 1272 +#define invalid_arithmetic_type 1273 +#define invalid_factor_type 1274 +#define invalid_type_params_type 1275 +#define _loop0_1_type 1276 +#define _loop1_2_type 1277 +#define _loop0_3_type 1278 +#define _gather_4_type 1279 +#define _tmp_5_type 1280 +#define _tmp_6_type 1281 +#define _tmp_7_type 1282 +#define _tmp_8_type 1283 +#define _tmp_9_type 1284 +#define _tmp_10_type 1285 +#define _tmp_11_type 1286 +#define _loop1_12_type 1287 +#define _loop0_13_type 1288 +#define _gather_14_type 1289 +#define _tmp_15_type 1290 +#define _tmp_16_type 1291 +#define _loop0_17_type 1292 +#define _loop1_18_type 1293 +#define _loop0_19_type 1294 +#define _gather_20_type 1295 +#define _tmp_21_type 1296 +#define _loop0_22_type 1297 +#define _gather_23_type 1298 +#define _loop1_24_type 1299 +#define _tmp_25_type 1300 +#define _tmp_26_type 1301 +#define _loop0_27_type 1302 +#define _loop0_28_type 1303 +#define _loop1_29_type 1304 +#define _loop1_30_type 1305 +#define _loop0_31_type 1306 +#define _loop1_32_type 1307 +#define _loop0_33_type 1308 +#define _gather_34_type 1309 +#define _tmp_35_type 1310 +#define _loop1_36_type 1311 +#define _loop1_37_type 1312 +#define _loop1_38_type 1313 +#define _loop0_39_type 1314 +#define _gather_40_type 1315 +#define _tmp_41_type 1316 +#define _tmp_42_type 1317 +#define _tmp_43_type 1318 +#define _loop0_44_type 1319 +#define _gather_45_type 1320 +#define _loop0_46_type 1321 +#define _gather_47_type 1322 +#define _tmp_48_type 1323 +#define _loop0_49_type 1324 +#define _gather_50_type 1325 +#define _loop0_51_type 1326 +#define _gather_52_type 1327 +#define _loop0_53_type 1328 +#define _gather_54_type 1329 +#define _loop1_55_type 1330 +#define _loop1_56_type 1331 +#define _loop0_57_type 1332 +#define _gather_58_type 1333 +#define _loop0_59_type 1334 +#define _gather_60_type 1335 +#define _loop1_61_type 1336 +#define _loop1_62_type 1337 +#define _loop1_63_type 1338 +#define _tmp_64_type 1339 +#define _loop0_65_type 1340 +#define _gather_66_type 1341 +#define _tmp_67_type 1342 +#define _tmp_68_type 1343 +#define _tmp_69_type 1344 +#define _tmp_70_type 1345 +#define _tmp_71_type 1346 +#define _loop0_72_type 1347 +#define _loop0_73_type 1348 +#define _loop1_74_type 1349 +#define _loop1_75_type 1350 +#define _loop0_76_type 1351 +#define _loop1_77_type 1352 +#define _loop0_78_type 1353 +#define _loop0_79_type 1354 +#define _loop0_80_type 1355 +#define _loop0_81_type 1356 +#define _loop1_82_type 1357 +#define _loop1_83_type 1358 +#define _tmp_84_type 1359 +#define _loop0_85_type 1360 +#define _gather_86_type 1361 +#define _loop1_87_type 1362 +#define _loop0_88_type 1363 +#define _tmp_89_type 1364 +#define _loop0_90_type 1365 +#define _gather_91_type 1366 +#define _tmp_92_type 1367 +#define _loop0_93_type 1368 +#define _gather_94_type 1369 +#define _loop0_95_type 1370 +#define _gather_96_type 1371 +#define _loop0_97_type 1372 +#define _loop0_98_type 1373 +#define _gather_99_type 1374 +#define _loop1_100_type 1375 +#define _tmp_101_type 1376 +#define _loop0_102_type 1377 +#define _gather_103_type 1378 +#define _loop0_104_type 1379 +#define _gather_105_type 1380 +#define _tmp_106_type 1381 +#define _tmp_107_type 1382 +#define _loop0_108_type 1383 +#define _gather_109_type 1384 +#define _tmp_110_type 1385 +#define _tmp_111_type 1386 +#define _tmp_112_type 1387 +#define _tmp_113_type 1388 +#define _tmp_114_type 1389 +#define _loop1_115_type 1390 +#define _tmp_116_type 1391 +#define _tmp_117_type 1392 +#define _tmp_118_type 1393 +#define _tmp_119_type 1394 +#define _tmp_120_type 1395 +#define _loop0_121_type 1396 +#define _loop0_122_type 1397 +#define _tmp_123_type 1398 +#define _tmp_124_type 1399 +#define _tmp_125_type 1400 +#define _tmp_126_type 1401 +#define _tmp_127_type 1402 +#define _tmp_128_type 1403 +#define _tmp_129_type 1404 +#define _tmp_130_type 1405 +#define _loop0_131_type 1406 +#define _gather_132_type 1407 +#define _tmp_133_type 1408 +#define _tmp_134_type 1409 +#define _tmp_135_type 1410 +#define _tmp_136_type 1411 +#define _loop0_137_type 1412 +#define _gather_138_type 1413 +#define _tmp_139_type 1414 +#define _loop0_140_type 1415 +#define _gather_141_type 1416 +#define _loop0_142_type 1417 +#define _gather_143_type 1418 +#define _tmp_144_type 1419 +#define _loop0_145_type 1420 +#define _tmp_146_type 1421 +#define _tmp_147_type 1422 +#define _tmp_148_type 1423 +#define _tmp_149_type 1424 +#define _tmp_150_type 1425 +#define _tmp_151_type 1426 +#define _tmp_152_type 1427 +#define _tmp_153_type 1428 +#define _tmp_154_type 1429 +#define _tmp_155_type 1430 +#define _tmp_156_type 1431 +#define _tmp_157_type 1432 +#define _tmp_158_type 1433 +#define _tmp_159_type 1434 +#define _tmp_160_type 1435 +#define _tmp_161_type 1436 +#define _tmp_162_type 1437 +#define _tmp_163_type 1438 +#define _tmp_164_type 1439 +#define _tmp_165_type 1440 +#define _tmp_166_type 1441 +#define _tmp_167_type 1442 +#define _tmp_168_type 1443 +#define _tmp_169_type 1444 +#define _tmp_170_type 1445 +#define _tmp_171_type 1446 +#define _tmp_172_type 1447 +#define _tmp_173_type 1448 +#define _loop0_174_type 1449 +#define _tmp_175_type 1450 +#define _tmp_176_type 1451 +#define _tmp_177_type 1452 +#define _tmp_178_type 1453 +#define _tmp_179_type 1454 +#define _tmp_180_type 1455 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -640,11 +653,14 @@ static expr_ty type_param_default_rule(Parser *p); static expr_ty type_param_starred_default_rule(Parser *p); static expr_ty expressions_rule(Parser *p); static expr_ty expression_rule(Parser *p); +static expr_ty if_expression_rule(Parser *p); static expr_ty yield_expr_rule(Parser *p); static expr_ty star_expressions_rule(Parser *p); static expr_ty star_expression_rule(Parser *p); static asdl_expr_seq* star_named_expressions_rule(Parser *p); +static asdl_expr_seq* star_named_expressions_sequence_rule(Parser *p); static expr_ty star_named_expression_rule(Parser *p); +static expr_ty star_named_expression_sequence_rule(Parser *p); static expr_ty assignment_expression_rule(Parser *p); static expr_ty named_expression_rule(Parser *p); static expr_ty disjunction_rule(Parser *p); @@ -741,14 +757,15 @@ static expr_ty expression_without_invalid_rule(Parser *p); static void *invalid_legacy_expression_rule(Parser *p); static void *invalid_type_param_rule(Parser *p); static void *invalid_expression_rule(Parser *p); +static void *invalid_if_expression_rule(Parser *p); static void *invalid_named_expression_rule(Parser *p); static void *invalid_assignment_rule(Parser *p); static expr_ty invalid_ann_assign_target_rule(Parser *p); static void *invalid_raise_stmt_rule(Parser *p); static void *invalid_del_stmt_rule(Parser *p); +static void *invalid_assert_stmt_rule(Parser *p); static void *invalid_block_rule(Parser *p); static void *invalid_comprehension_rule(Parser *p); -static void *invalid_dict_comprehension_rule(Parser *p); static void *invalid_parameters_rule(Parser *p); static void *invalid_default_rule(Parser *p); static void *invalid_star_etc_rule(Parser *p); @@ -779,6 +796,7 @@ static void *invalid_match_stmt_rule(Parser *p); static void *invalid_case_block_rule(Parser *p); static void *invalid_as_pattern_rule(Parser *p); static void *invalid_class_pattern_rule(Parser *p); +static void *invalid_mapping_pattern_rule(Parser *p); static asdl_pattern_seq* invalid_class_argument_pattern_rule(Parser *p); static void *invalid_if_stmt_rule(Parser *p); static void *invalid_elif_stmt_rule(Parser *p); @@ -788,8 +806,10 @@ static void *invalid_for_stmt_rule(Parser *p); static void *invalid_def_raw_rule(Parser *p); static void *invalid_class_def_raw_rule(Parser *p); static void *invalid_double_starred_kvpairs_rule(Parser *p); +static void *invalid_kvpair_unpacking_rule(Parser *p); static void *invalid_kvpair_rule(Parser *p); static void *invalid_starred_expression_unpacking_rule(Parser *p); +static void *invalid_starred_expression_unpacking_sequence_rule(Parser *p); static void *invalid_starred_expression_rule(Parser *p); static void *invalid_fstring_replacement_field_rule(Parser *p); static void *invalid_fstring_conversion_character_rule(Parser *p); @@ -857,70 +877,70 @@ static asdl_seq *_loop1_55_rule(Parser *p); static asdl_seq *_loop1_56_rule(Parser *p); static asdl_seq *_loop0_57_rule(Parser *p); static asdl_seq *_gather_58_rule(Parser *p); -static asdl_seq *_loop1_59_rule(Parser *p); -static asdl_seq *_loop1_60_rule(Parser *p); +static asdl_seq *_loop0_59_rule(Parser *p); +static asdl_seq *_gather_60_rule(Parser *p); static asdl_seq *_loop1_61_rule(Parser *p); -static void *_tmp_62_rule(Parser *p); -static asdl_seq *_loop0_63_rule(Parser *p); -static asdl_seq *_gather_64_rule(Parser *p); -static void *_tmp_65_rule(Parser *p); -static void *_tmp_66_rule(Parser *p); +static asdl_seq *_loop1_62_rule(Parser *p); +static asdl_seq *_loop1_63_rule(Parser *p); +static void *_tmp_64_rule(Parser *p); +static asdl_seq *_loop0_65_rule(Parser *p); +static asdl_seq *_gather_66_rule(Parser *p); static void *_tmp_67_rule(Parser *p); static void *_tmp_68_rule(Parser *p); static void *_tmp_69_rule(Parser *p); -static asdl_seq *_loop0_70_rule(Parser *p); -static asdl_seq *_loop0_71_rule(Parser *p); -static asdl_seq *_loop1_72_rule(Parser *p); -static asdl_seq *_loop1_73_rule(Parser *p); -static asdl_seq *_loop0_74_rule(Parser *p); +static void *_tmp_70_rule(Parser *p); +static void *_tmp_71_rule(Parser *p); +static asdl_seq *_loop0_72_rule(Parser *p); +static asdl_seq *_loop0_73_rule(Parser *p); +static asdl_seq *_loop1_74_rule(Parser *p); static asdl_seq *_loop1_75_rule(Parser *p); static asdl_seq *_loop0_76_rule(Parser *p); -static asdl_seq *_loop0_77_rule(Parser *p); +static asdl_seq *_loop1_77_rule(Parser *p); static asdl_seq *_loop0_78_rule(Parser *p); static asdl_seq *_loop0_79_rule(Parser *p); -static asdl_seq *_loop1_80_rule(Parser *p); -static asdl_seq *_loop1_81_rule(Parser *p); -static void *_tmp_82_rule(Parser *p); -static asdl_seq *_loop0_83_rule(Parser *p); -static asdl_seq *_gather_84_rule(Parser *p); -static asdl_seq *_loop1_85_rule(Parser *p); -static asdl_seq *_loop0_86_rule(Parser *p); -static void *_tmp_87_rule(Parser *p); +static asdl_seq *_loop0_80_rule(Parser *p); +static asdl_seq *_loop0_81_rule(Parser *p); +static asdl_seq *_loop1_82_rule(Parser *p); +static asdl_seq *_loop1_83_rule(Parser *p); +static void *_tmp_84_rule(Parser *p); +static asdl_seq *_loop0_85_rule(Parser *p); +static asdl_seq *_gather_86_rule(Parser *p); +static asdl_seq *_loop1_87_rule(Parser *p); static asdl_seq *_loop0_88_rule(Parser *p); -static asdl_seq *_gather_89_rule(Parser *p); -static void *_tmp_90_rule(Parser *p); -static asdl_seq *_loop0_91_rule(Parser *p); -static asdl_seq *_gather_92_rule(Parser *p); +static void *_tmp_89_rule(Parser *p); +static asdl_seq *_loop0_90_rule(Parser *p); +static asdl_seq *_gather_91_rule(Parser *p); +static void *_tmp_92_rule(Parser *p); static asdl_seq *_loop0_93_rule(Parser *p); static asdl_seq *_gather_94_rule(Parser *p); static asdl_seq *_loop0_95_rule(Parser *p); -static asdl_seq *_loop0_96_rule(Parser *p); -static asdl_seq *_gather_97_rule(Parser *p); -static asdl_seq *_loop1_98_rule(Parser *p); -static void *_tmp_99_rule(Parser *p); -static asdl_seq *_loop0_100_rule(Parser *p); -static asdl_seq *_gather_101_rule(Parser *p); +static asdl_seq *_gather_96_rule(Parser *p); +static asdl_seq *_loop0_97_rule(Parser *p); +static asdl_seq *_loop0_98_rule(Parser *p); +static asdl_seq *_gather_99_rule(Parser *p); +static asdl_seq *_loop1_100_rule(Parser *p); +static void *_tmp_101_rule(Parser *p); static asdl_seq *_loop0_102_rule(Parser *p); static asdl_seq *_gather_103_rule(Parser *p); -static void *_tmp_104_rule(Parser *p); -static void *_tmp_105_rule(Parser *p); -static asdl_seq *_loop0_106_rule(Parser *p); -static asdl_seq *_gather_107_rule(Parser *p); -static void *_tmp_108_rule(Parser *p); -static void *_tmp_109_rule(Parser *p); +static asdl_seq *_loop0_104_rule(Parser *p); +static asdl_seq *_gather_105_rule(Parser *p); +static void *_tmp_106_rule(Parser *p); +static void *_tmp_107_rule(Parser *p); +static asdl_seq *_loop0_108_rule(Parser *p); +static asdl_seq *_gather_109_rule(Parser *p); static void *_tmp_110_rule(Parser *p); static void *_tmp_111_rule(Parser *p); static void *_tmp_112_rule(Parser *p); -static asdl_seq *_loop1_113_rule(Parser *p); +static void *_tmp_113_rule(Parser *p); static void *_tmp_114_rule(Parser *p); -static void *_tmp_115_rule(Parser *p); +static asdl_seq *_loop1_115_rule(Parser *p); static void *_tmp_116_rule(Parser *p); static void *_tmp_117_rule(Parser *p); static void *_tmp_118_rule(Parser *p); -static asdl_seq *_loop0_119_rule(Parser *p); -static asdl_seq *_loop0_120_rule(Parser *p); -static void *_tmp_121_rule(Parser *p); -static void *_tmp_122_rule(Parser *p); +static void *_tmp_119_rule(Parser *p); +static void *_tmp_120_rule(Parser *p); +static asdl_seq *_loop0_121_rule(Parser *p); +static asdl_seq *_loop0_122_rule(Parser *p); static void *_tmp_123_rule(Parser *p); static void *_tmp_124_rule(Parser *p); static void *_tmp_125_rule(Parser *p); @@ -928,22 +948,22 @@ static void *_tmp_126_rule(Parser *p); static void *_tmp_127_rule(Parser *p); static void *_tmp_128_rule(Parser *p); static void *_tmp_129_rule(Parser *p); -static asdl_seq *_loop0_130_rule(Parser *p); -static asdl_seq *_gather_131_rule(Parser *p); -static void *_tmp_132_rule(Parser *p); +static void *_tmp_130_rule(Parser *p); +static asdl_seq *_loop0_131_rule(Parser *p); +static asdl_seq *_gather_132_rule(Parser *p); static void *_tmp_133_rule(Parser *p); static void *_tmp_134_rule(Parser *p); static void *_tmp_135_rule(Parser *p); -static asdl_seq *_loop0_136_rule(Parser *p); -static asdl_seq *_gather_137_rule(Parser *p); -static void *_tmp_138_rule(Parser *p); -static asdl_seq *_loop0_139_rule(Parser *p); -static asdl_seq *_gather_140_rule(Parser *p); -static asdl_seq *_loop0_141_rule(Parser *p); -static asdl_seq *_gather_142_rule(Parser *p); -static void *_tmp_143_rule(Parser *p); -static asdl_seq *_loop0_144_rule(Parser *p); -static void *_tmp_145_rule(Parser *p); +static void *_tmp_136_rule(Parser *p); +static asdl_seq *_loop0_137_rule(Parser *p); +static asdl_seq *_gather_138_rule(Parser *p); +static void *_tmp_139_rule(Parser *p); +static asdl_seq *_loop0_140_rule(Parser *p); +static asdl_seq *_gather_141_rule(Parser *p); +static asdl_seq *_loop0_142_rule(Parser *p); +static asdl_seq *_gather_143_rule(Parser *p); +static void *_tmp_144_rule(Parser *p); +static asdl_seq *_loop0_145_rule(Parser *p); static void *_tmp_146_rule(Parser *p); static void *_tmp_147_rule(Parser *p); static void *_tmp_148_rule(Parser *p); @@ -969,12 +989,16 @@ static void *_tmp_167_rule(Parser *p); static void *_tmp_168_rule(Parser *p); static void *_tmp_169_rule(Parser *p); static void *_tmp_170_rule(Parser *p); -static asdl_seq *_loop0_171_rule(Parser *p); +static void *_tmp_171_rule(Parser *p); static void *_tmp_172_rule(Parser *p); static void *_tmp_173_rule(Parser *p); -static void *_tmp_174_rule(Parser *p); +static asdl_seq *_loop0_174_rule(Parser *p); static void *_tmp_175_rule(Parser *p); static void *_tmp_176_rule(Parser *p); +static void *_tmp_177_rule(Parser *p); +static void *_tmp_178_rule(Parser *p); +static void *_tmp_179_rule(Parser *p); +static void *_tmp_180_rule(Parser *p); // file: statements? $ @@ -1006,7 +1030,7 @@ file_rule(Parser *p) { D(fprintf(stderr, "%*c+ file[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "statements? $")); _res = _PyPegen_make_module ( p , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1049,7 +1073,7 @@ interactive_rule(Parser *p) { D(fprintf(stderr, "%*c+ interactive[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "statement_newline")); _res = _PyAST_Interactive ( a , p -> arena ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1098,7 +1122,7 @@ eval_rule(Parser *p) { D(fprintf(stderr, "%*c+ eval[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions NEWLINE* $")); _res = _PyAST_Expression ( a , p -> arena ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1159,7 +1183,7 @@ func_type_rule(Parser *p) { D(fprintf(stderr, "%*c+ func_type[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' type_expressions? ')' '->' expression NEWLINE* $")); _res = _PyAST_FunctionType ( a , b , p -> arena ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1201,8 +1225,8 @@ statements_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ statements[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "statement+")); - _res = _PyPegen_register_stmts ( p , ( asdl_stmt_seq* ) _PyPegen_seq_flatten ( p , a ) ); - if (_res == NULL && PyErr_Occurred()) { + _res = ( asdl_stmt_seq* ) _PyPegen_seq_flatten ( p , a ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1244,8 +1268,8 @@ statement_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ statement[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "compound_stmt")); - _res = ( asdl_stmt_seq* ) _PyPegen_singleton_seq ( p , a ); - if (_res == NULL && PyErr_Occurred()) { + _res = _PyPegen_register_stmts ( p , ( asdl_stmt_seq* ) _PyPegen_singleton_seq ( p , a ) ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1269,7 +1293,7 @@ statement_rule(Parser *p) { D(fprintf(stderr, "%*c+ statement[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "simple_stmts")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1312,7 +1336,7 @@ single_compound_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ single_compound_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "compound_stmt")); _res = _PyPegen_register_stmts ( p , ( asdl_stmt_seq* ) _PyPegen_singleton_seq ( p , a ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1367,7 +1391,7 @@ statement_newline_rule(Parser *p) { D(fprintf(stderr, "%*c+ statement_newline[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "single_compound_stmt NEWLINE")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1419,7 +1443,7 @@ statement_newline_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = ( asdl_stmt_seq* ) _PyPegen_singleton_seq ( p , CHECK ( stmt_ty , _PyAST_Pass ( EXTRA ) ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1443,7 +1467,7 @@ statement_newline_rule(Parser *p) { D(fprintf(stderr, "%*c+ statement_newline[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "$")); _res = _PyPegen_interactive_exit ( p ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1491,7 +1515,7 @@ simple_stmts_rule(Parser *p) { D(fprintf(stderr, "%*c+ simple_stmts[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "simple_stmt !';' NEWLINE")); _res = ( asdl_stmt_seq* ) _PyPegen_singleton_seq ( p , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1522,7 +1546,7 @@ simple_stmts_rule(Parser *p) { D(fprintf(stderr, "%*c+ simple_stmts[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "';'.simple_stmt+ ';'? NEWLINE")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1542,9 +1566,9 @@ simple_stmts_rule(Parser *p) // simple_stmt: // | assignment // | &"type" type_alias +// | &('import' | 'from' | "lazy") import_stmt // | star_expressions // | &'return' return_stmt -// | &('import' | 'from') import_stmt // | &'raise' raise_stmt // | &'pass' pass_stmt // | &'del' del_stmt @@ -1619,6 +1643,27 @@ simple_stmt_rule(Parser *p) D(fprintf(stderr, "%*c%s simple_stmt[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&\"type\" type_alias")); } + { // &('import' | 'from' | "lazy") import_stmt + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> simple_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&('import' | 'from' | \"lazy\") import_stmt")); + stmt_ty import_stmt_var; + if ( + _PyPegen_lookahead(1, _tmp_5_rule, p) + && + (import_stmt_var = import_stmt_rule(p)) // import_stmt + ) + { + D(fprintf(stderr, "%*c+ simple_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&('import' | 'from' | \"lazy\") import_stmt")); + _res = import_stmt_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s simple_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&('import' | 'from' | \"lazy\") import_stmt")); + } { // star_expressions if (p->error_indicator) { p->level--; @@ -1641,7 +1686,7 @@ simple_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Expr ( e , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -1673,27 +1718,6 @@ simple_stmt_rule(Parser *p) D(fprintf(stderr, "%*c%s simple_stmt[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&'return' return_stmt")); } - { // &('import' | 'from') import_stmt - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> simple_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&('import' | 'from') import_stmt")); - stmt_ty import_stmt_var; - if ( - _PyPegen_lookahead(1, _tmp_5_rule, p) - && - (import_stmt_var = import_stmt_rule(p)) // import_stmt - ) - { - D(fprintf(stderr, "%*c+ simple_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&('import' | 'from') import_stmt")); - _res = import_stmt_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s simple_stmt[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&('import' | 'from') import_stmt")); - } { // &'raise' raise_stmt if (p->error_indicator) { p->level--; @@ -1702,7 +1726,7 @@ simple_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> simple_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'raise' raise_stmt")); stmt_ty raise_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 628) // token='raise' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 632) // token='raise' && (raise_stmt_var = raise_stmt_rule(p)) // raise_stmt ) @@ -1744,7 +1768,7 @@ simple_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> simple_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'del' del_stmt")); stmt_ty del_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 630) // token='del' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 634) // token='del' && (del_stmt_var = del_stmt_rule(p)) // del_stmt ) @@ -1786,7 +1810,7 @@ simple_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> simple_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'assert' assert_stmt")); stmt_ty assert_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 533) // token='assert' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 638) // token='assert' && (assert_stmt_var = assert_stmt_rule(p)) // assert_stmt ) @@ -1940,7 +1964,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'if' if_stmt")); stmt_ty if_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 687) // token='if' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 698) // token='if' && (if_stmt_var = if_stmt_rule(p)) // if_stmt ) @@ -2024,7 +2048,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'try' try_stmt")); stmt_ty try_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 661) // token='try' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 672) // token='try' && (try_stmt_var = try_stmt_rule(p)) // try_stmt ) @@ -2045,7 +2069,7 @@ compound_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> compound_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'while' while_stmt")); stmt_ty while_stmt_var; if ( - _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 694) // token='while' + _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 705) // token='while' && (while_stmt_var = while_stmt_rule(p)) // while_stmt ) @@ -2141,7 +2165,7 @@ assignment_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( stmt_ty , 6 , "Variable annotation syntax is" , _PyAST_AnnAssign ( CHECK ( expr_ty , _PyPegen_set_expr_context ( p , a , Store ) ) , b , c , 1 , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2183,7 +2207,7 @@ assignment_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( stmt_ty , 6 , "Variable annotations syntax is" , _PyAST_AnnAssign ( a , b , c , 0 , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2224,7 +2248,7 @@ assignment_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Assign ( a , b , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2266,7 +2290,7 @@ assignment_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_AugAssign ( a , b -> kind , c , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2402,7 +2426,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+='")); _res = _PyPegen_augoperator ( p , Add ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2426,7 +2450,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-='")); _res = _PyPegen_augoperator ( p , Sub ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2450,7 +2474,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*='")); _res = _PyPegen_augoperator ( p , Mult ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2474,7 +2498,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@='")); _res = CHECK_VERSION ( AugOperator* , 5 , "The '@' operator is" , _PyPegen_augoperator ( p , MatMult ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2498,7 +2522,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/='")); _res = _PyPegen_augoperator ( p , Div ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2522,7 +2546,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'%='")); _res = _PyPegen_augoperator ( p , Mod ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2546,7 +2570,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'&='")); _res = _PyPegen_augoperator ( p , BitAnd ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2570,7 +2594,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'|='")); _res = _PyPegen_augoperator ( p , BitOr ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2594,7 +2618,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'^='")); _res = _PyPegen_augoperator ( p , BitXor ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2618,7 +2642,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'<<='")); _res = _PyPegen_augoperator ( p , LShift ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2642,7 +2666,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'>>='")); _res = _PyPegen_augoperator ( p , RShift ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2666,7 +2690,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**='")); _res = _PyPegen_augoperator ( p , Pow ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2690,7 +2714,7 @@ augassign_rule(Parser *p) { D(fprintf(stderr, "%*c+ augassign[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'//='")); _res = _PyPegen_augoperator ( p , FloorDiv ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2754,7 +2778,7 @@ return_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Return ( a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2808,11 +2832,11 @@ raise_stmt_rule(Parser *p) expr_ty a; expr_ty b; if ( - (_keyword = _PyPegen_expect_token(p, 628)) // token='raise' + (_keyword = _PyPegen_expect_token(p, 632)) // token='raise' && (a = expression_rule(p)) // expression && - (_keyword_1 = _PyPegen_expect_token(p, 638)) // token='from' + (_keyword_1 = _PyPegen_expect_token(p, 646)) // token='from' && (b = expression_rule(p)) // expression ) @@ -2828,7 +2852,7 @@ raise_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Raise ( a , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2867,7 +2891,7 @@ raise_stmt_rule(Parser *p) Token * _keyword; expr_ty a; if ( - (_keyword = _PyPegen_expect_token(p, 628)) // token='raise' + (_keyword = _PyPegen_expect_token(p, 632)) // token='raise' && (a = expression_rule(p)) // expression ) @@ -2883,7 +2907,7 @@ raise_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Raise ( a , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2902,7 +2926,7 @@ raise_stmt_rule(Parser *p) D(fprintf(stderr, "%*c> raise_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'raise'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 628)) // token='raise' + (_keyword = _PyPegen_expect_token(p, 632)) // token='raise' ) { D(fprintf(stderr, "%*c+ raise_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'raise'")); @@ -2916,7 +2940,7 @@ raise_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Raise ( NULL , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -2977,7 +3001,7 @@ pass_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Pass ( EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3038,7 +3062,7 @@ break_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Break ( EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3099,7 +3123,7 @@ continue_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Continue ( EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3163,7 +3187,7 @@ global_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Global ( CHECK ( asdl_identifier_seq* , _PyPegen_map_names_to_ids ( p , a ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3227,7 +3251,7 @@ nonlocal_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Nonlocal ( CHECK ( asdl_identifier_seq* , _PyPegen_map_names_to_ids ( p , a ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3275,7 +3299,7 @@ del_stmt_rule(Parser *p) Token * _keyword; asdl_expr_seq* a; if ( - (_keyword = _PyPegen_expect_token(p, 630)) // token='del' + (_keyword = _PyPegen_expect_token(p, 634)) // token='del' && (a = del_targets_rule(p)) // del_targets && @@ -3293,7 +3317,7 @@ del_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Delete ( a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3373,7 +3397,7 @@ yield_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Expr ( y , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3390,7 +3414,7 @@ yield_stmt_rule(Parser *p) return _res; } -// assert_stmt: 'assert' expression [',' expression] +// assert_stmt: invalid_assert_stmt | 'assert' expression [',' expression] static stmt_ty assert_stmt_rule(Parser *p) { @@ -3412,6 +3436,25 @@ assert_stmt_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro + if (p->call_invalid_rules) { // invalid_assert_stmt + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> assert_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_assert_stmt")); + void *invalid_assert_stmt_var; + if ( + (invalid_assert_stmt_var = invalid_assert_stmt_rule(p)) // invalid_assert_stmt + ) + { + D(fprintf(stderr, "%*c+ assert_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_assert_stmt")); + _res = invalid_assert_stmt_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s assert_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_assert_stmt")); + } { // 'assert' expression [',' expression] if (p->error_indicator) { p->level--; @@ -3422,7 +3465,7 @@ assert_stmt_rule(Parser *p) expr_ty a; void *b; if ( - (_keyword = _PyPegen_expect_token(p, 533)) // token='assert' + (_keyword = _PyPegen_expect_token(p, 638)) // token='assert' && (a = expression_rule(p)) // expression && @@ -3440,7 +3483,7 @@ assert_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Assert ( a , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3469,6 +3512,10 @@ import_stmt_rule(Parser *p) return NULL; } stmt_ty _res = NULL; + if (_PyPegen_is_memoized(p, import_stmt_type, &_res)) { + p->level--; + return _res; + } int _mark = p->mark; if (p->call_invalid_rules) { // invalid_import if (p->error_indicator) { @@ -3529,11 +3576,12 @@ import_stmt_rule(Parser *p) } _res = NULL; done: + _PyPegen_insert_memo(p, _mark, import_stmt_type, _res); p->level--; return _res; } -// import_name: 'import' dotted_as_names +// import_name: "lazy"? 'import' dotted_as_names static stmt_ty import_name_rule(Parser *p) { @@ -3555,21 +3603,24 @@ import_name_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro - { // 'import' dotted_as_names + { // "lazy"? 'import' dotted_as_names if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> import_name[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'import' dotted_as_names")); + D(fprintf(stderr, "%*c> import_name[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "\"lazy\"? 'import' dotted_as_names")); Token * _keyword; asdl_alias_seq* a; + void *lazy; if ( - (_keyword = _PyPegen_expect_token(p, 639)) // token='import' + (lazy = _PyPegen_expect_soft_keyword(p, "lazy"), !p->error_indicator) // "lazy"? + && + (_keyword = _PyPegen_expect_token(p, 647)) // token='import' && (a = dotted_as_names_rule(p)) // dotted_as_names ) { - D(fprintf(stderr, "%*c+ import_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'import' dotted_as_names")); + D(fprintf(stderr, "%*c+ import_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"lazy\"? 'import' dotted_as_names")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -3579,8 +3630,8 @@ import_name_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyAST_Import ( a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + _res = _PyAST_Import ( a , lazy ? 1 : 0 , EXTRA ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3589,7 +3640,7 @@ import_name_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s import_name[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'import' dotted_as_names")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "\"lazy\"? 'import' dotted_as_names")); } _res = NULL; done: @@ -3598,8 +3649,8 @@ import_name_rule(Parser *p) } // import_from: -// | 'from' (('.' | '...'))* dotted_name 'import' import_from_targets -// | 'from' (('.' | '...'))+ 'import' import_from_targets +// | "lazy"? 'from' (('.' | '...'))* dotted_name 'import' import_from_targets +// | "lazy"? 'from' (('.' | '...'))+ 'import' import_from_targets static stmt_ty import_from_rule(Parser *p) { @@ -3621,30 +3672,33 @@ import_from_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro - { // 'from' (('.' | '...'))* dotted_name 'import' import_from_targets + { // "lazy"? 'from' (('.' | '...'))* dotted_name 'import' import_from_targets if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> import_from[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'from' (('.' | '...'))* dotted_name 'import' import_from_targets")); + D(fprintf(stderr, "%*c> import_from[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "\"lazy\"? 'from' (('.' | '...'))* dotted_name 'import' import_from_targets")); Token * _keyword; Token * _keyword_1; asdl_seq * a; expr_ty b; asdl_alias_seq* c; + void *lazy; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='from' + (lazy = _PyPegen_expect_soft_keyword(p, "lazy"), !p->error_indicator) // "lazy"? + && + (_keyword = _PyPegen_expect_token(p, 646)) // token='from' && (a = _loop0_17_rule(p)) // (('.' | '...'))* && (b = dotted_name_rule(p)) // dotted_name && - (_keyword_1 = _PyPegen_expect_token(p, 639)) // token='import' + (_keyword_1 = _PyPegen_expect_token(p, 647)) // token='import' && (c = import_from_targets_rule(p)) // import_from_targets ) { - D(fprintf(stderr, "%*c+ import_from[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'from' (('.' | '...'))* dotted_name 'import' import_from_targets")); + D(fprintf(stderr, "%*c+ import_from[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"lazy\"? 'from' (('.' | '...'))* dotted_name 'import' import_from_targets")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -3654,8 +3708,8 @@ import_from_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyPegen_checked_future_import ( p , b -> v . Name . id , c , _PyPegen_seq_count_dots ( a ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + _res = _PyPegen_checked_future_import ( p , b -> v . Name . id , c , _PyPegen_seq_count_dots ( a ) , lazy , EXTRA ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3664,29 +3718,32 @@ import_from_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s import_from[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'from' (('.' | '...'))* dotted_name 'import' import_from_targets")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "\"lazy\"? 'from' (('.' | '...'))* dotted_name 'import' import_from_targets")); } - { // 'from' (('.' | '...'))+ 'import' import_from_targets + { // "lazy"? 'from' (('.' | '...'))+ 'import' import_from_targets if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> import_from[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'from' (('.' | '...'))+ 'import' import_from_targets")); + D(fprintf(stderr, "%*c> import_from[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "\"lazy\"? 'from' (('.' | '...'))+ 'import' import_from_targets")); Token * _keyword; Token * _keyword_1; asdl_seq * a; asdl_alias_seq* b; + void *lazy; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='from' + (lazy = _PyPegen_expect_soft_keyword(p, "lazy"), !p->error_indicator) // "lazy"? + && + (_keyword = _PyPegen_expect_token(p, 646)) // token='from' && (a = _loop1_18_rule(p)) // (('.' | '...'))+ && - (_keyword_1 = _PyPegen_expect_token(p, 639)) // token='import' + (_keyword_1 = _PyPegen_expect_token(p, 647)) // token='import' && (b = import_from_targets_rule(p)) // import_from_targets ) { - D(fprintf(stderr, "%*c+ import_from[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'from' (('.' | '...'))+ 'import' import_from_targets")); + D(fprintf(stderr, "%*c+ import_from[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"lazy\"? 'from' (('.' | '...'))+ 'import' import_from_targets")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -3696,8 +3753,8 @@ import_from_rule(Parser *p) UNUSED(_end_lineno); // Only used by EXTRA macro int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyAST_ImportFrom ( NULL , b , _PyPegen_seq_count_dots ( a ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + _res = _PyAST_ImportFrom ( NULL , b , _PyPegen_seq_count_dots ( a ) , lazy ? 1 : 0 , EXTRA ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3706,7 +3763,7 @@ import_from_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s import_from[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'from' (('.' | '...'))+ 'import' import_from_targets")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "\"lazy\"? 'from' (('.' | '...'))+ 'import' import_from_targets")); } _res = NULL; done: @@ -3763,7 +3820,7 @@ import_from_targets_rule(Parser *p) { D(fprintf(stderr, "%*c+ import_from_targets[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' import_from_as_names ','? ')'")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3817,7 +3874,7 @@ import_from_targets_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = ( asdl_alias_seq* ) _PyPegen_singleton_seq ( p , CHECK ( alias_ty , _PyPegen_alias_for_star ( p , EXTRA ) ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3879,7 +3936,7 @@ import_from_as_names_rule(Parser *p) { D(fprintf(stderr, "%*c+ import_from_as_names[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.import_from_as_name+")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -3962,7 +4019,7 @@ import_from_as_name_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_alias ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Name . id : NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4005,7 +4062,7 @@ dotted_as_names_rule(Parser *p) { D(fprintf(stderr, "%*c+ dotted_as_names[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.dotted_as_name+")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4088,7 +4145,7 @@ dotted_as_name_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_alias ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Name . id : NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4173,7 +4230,7 @@ dotted_name_raw(Parser *p) { D(fprintf(stderr, "%*c+ dotted_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name '.' NAME")); _res = _PyPegen_join_names_with_dot ( p , a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4248,7 +4305,7 @@ block_rule(Parser *p) { D(fprintf(stderr, "%*c+ block[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT statements DEDENT")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4330,7 +4387,7 @@ decorators_rule(Parser *p) { D(fprintf(stderr, "%*c+ decorators[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(('@' named_expression NEWLINE))+")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4376,7 +4433,7 @@ class_def_rule(Parser *p) { D(fprintf(stderr, "%*c+ class_def[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "decorators class_def_raw")); _res = _PyPegen_class_def_decorators ( p , a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4468,7 +4525,7 @@ class_def_raw_rule(Parser *p) asdl_stmt_seq* c; void *t; if ( - (_keyword = _PyPegen_expect_token(p, 706)) // token='class' + (_keyword = _PyPegen_expect_token(p, 717)) // token='class' && (a = _PyPegen_name_token(p)) // NAME && @@ -4492,7 +4549,7 @@ class_def_raw_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_ClassDef ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Call . args : NULL , ( b ) ? ( ( expr_ty ) b ) -> v . Call . keywords : NULL , c , NULL , t , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4538,7 +4595,7 @@ function_def_rule(Parser *p) { D(fprintf(stderr, "%*c+ function_def[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "decorators function_def_raw")); _res = _PyPegen_function_def_decorators ( p , d , f ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4635,7 +4692,7 @@ function_def_raw_rule(Parser *p) void *t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 704)) // token='def' + (_keyword = _PyPegen_expect_token(p, 715)) // token='def' && (n = _PyPegen_name_token(p)) // NAME && @@ -4667,7 +4724,7 @@ function_def_raw_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_FunctionDef ( n -> v . Name . id , ( params ) ? params : CHECK ( arguments_ty , _PyPegen_empty_arguments ( p ) ) , b , NULL , a , NEW_TYPE_COMMENT ( p , tc ) , t , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4696,9 +4753,9 @@ function_def_raw_rule(Parser *p) void *t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 703)) // token='async' + (_keyword = _PyPegen_expect_token(p, 714)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 704)) // token='def' + (_keyword_1 = _PyPegen_expect_token(p, 715)) // token='def' && (n = _PyPegen_name_token(p)) // NAME && @@ -4730,7 +4787,7 @@ function_def_raw_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( stmt_ty , 5 , "Async functions are" , _PyAST_AsyncFunctionDef ( n -> v . Name . id , ( params ) ? params : CHECK ( arguments_ty , _PyPegen_empty_arguments ( p ) ) , b , NULL , a , NEW_TYPE_COMMENT ( p , tc ) , t , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4844,7 +4901,7 @@ parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default param_no_default* param_with_default* star_etc?")); _res = CHECK_VERSION ( arguments_ty , 8 , "Positional-only parameters are" , _PyPegen_make_arguments ( p , a , NULL , b , c , d ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4874,7 +4931,7 @@ parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default param_with_default* star_etc?")); _res = CHECK_VERSION ( arguments_ty , 8 , "Positional-only parameters are" , _PyPegen_make_arguments ( p , NULL , a , NULL , b , c ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4904,7 +4961,7 @@ parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default+ param_with_default* star_etc?")); _res = _PyPegen_make_arguments ( p , NULL , NULL , a , b , c ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4931,7 +4988,7 @@ parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_with_default+ star_etc?")); _res = _PyPegen_make_arguments ( p , NULL , NULL , NULL , a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -4955,7 +5012,7 @@ parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_etc")); _res = _PyPegen_make_arguments ( p , NULL , NULL , NULL , NULL , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5004,7 +5061,7 @@ slash_no_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ slash_no_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default+ '/' ','")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5033,7 +5090,7 @@ slash_no_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ slash_no_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default+ '/' &')'")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5087,7 +5144,7 @@ slash_with_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ slash_with_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default* param_with_default+ '/' ','")); _res = _PyPegen_slash_with_default ( p , ( asdl_arg_seq* ) a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5119,7 +5176,7 @@ slash_with_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ slash_with_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default* param_with_default+ '/' &')'")); _res = _PyPegen_slash_with_default ( p , ( asdl_arg_seq* ) a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5195,7 +5252,7 @@ star_etc_rule(Parser *p) { D(fprintf(stderr, "%*c+ star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' param_no_default param_maybe_default* kwds?")); _res = _PyPegen_star_etc ( p , a , b , c ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5228,7 +5285,7 @@ star_etc_rule(Parser *p) { D(fprintf(stderr, "%*c+ star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' param_no_default_star_annotation param_maybe_default* kwds?")); _res = _PyPegen_star_etc ( p , a , b , c ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5261,7 +5318,7 @@ star_etc_rule(Parser *p) { D(fprintf(stderr, "%*c+ star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' ',' param_maybe_default+ kwds?")); _res = _PyPegen_star_etc ( p , NULL , b , c ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5285,7 +5342,7 @@ star_etc_rule(Parser *p) { D(fprintf(stderr, "%*c+ star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwds")); _res = _PyPegen_star_etc ( p , NULL , NULL , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5350,7 +5407,7 @@ kwds_rule(Parser *p) { D(fprintf(stderr, "%*c+ kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' param_no_default")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5399,7 +5456,7 @@ param_no_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ param_no_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param ',' TYPE_COMMENT?")); _res = _PyPegen_add_type_comment_to_arg ( p , a , tc ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5428,7 +5485,7 @@ param_no_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ param_no_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param TYPE_COMMENT? &')'")); _res = _PyPegen_add_type_comment_to_arg ( p , a , tc ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5479,7 +5536,7 @@ param_no_default_star_annotation_rule(Parser *p) { D(fprintf(stderr, "%*c+ param_no_default_star_annotation[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_star_annotation ',' TYPE_COMMENT?")); _res = _PyPegen_add_type_comment_to_arg ( p , a , tc ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5508,7 +5565,7 @@ param_no_default_star_annotation_rule(Parser *p) { D(fprintf(stderr, "%*c+ param_no_default_star_annotation[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_star_annotation TYPE_COMMENT? &')'")); _res = _PyPegen_add_type_comment_to_arg ( p , a , tc ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5560,7 +5617,7 @@ param_with_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ param_with_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param default ',' TYPE_COMMENT?")); _res = _PyPegen_name_default_pair ( p , a , c , tc ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5592,7 +5649,7 @@ param_with_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ param_with_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param default TYPE_COMMENT? &')'")); _res = _PyPegen_name_default_pair ( p , a , c , tc ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5646,7 +5703,7 @@ param_maybe_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ param_maybe_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param default? ',' TYPE_COMMENT?")); _res = _PyPegen_name_default_pair ( p , a , c , tc ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5678,7 +5735,7 @@ param_maybe_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ param_maybe_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param default? TYPE_COMMENT? &')'")); _res = _PyPegen_name_default_pair ( p , a , c , tc ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5742,7 +5799,7 @@ param_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_arg ( a -> v . Name . id , b , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5806,7 +5863,7 @@ param_star_annotation_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_arg ( a -> v . Name . id , b , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5852,7 +5909,7 @@ annotation_rule(Parser *p) { D(fprintf(stderr, "%*c+ annotation[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' expression")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5898,7 +5955,7 @@ star_annotation_rule(Parser *p) { D(fprintf(stderr, "%*c+ star_annotation[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' star_expression")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -5944,7 +6001,7 @@ default_rule(Parser *p) { D(fprintf(stderr, "%*c+ default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'=' expression")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6036,7 +6093,7 @@ if_stmt_rule(Parser *p) asdl_stmt_seq* b; stmt_ty c; if ( - (_keyword = _PyPegen_expect_token(p, 687)) // token='if' + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' && (a = named_expression_rule(p)) // named_expression && @@ -6058,7 +6115,7 @@ if_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_If ( a , b , CHECK ( asdl_stmt_seq* , _PyPegen_singleton_seq ( p , c ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6081,7 +6138,7 @@ if_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 687)) // token='if' + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' && (a = named_expression_rule(p)) // named_expression && @@ -6103,7 +6160,7 @@ if_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_If ( a , b , c , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6176,7 +6233,7 @@ elif_stmt_rule(Parser *p) asdl_stmt_seq* b; stmt_ty c; if ( - (_keyword = _PyPegen_expect_token(p, 692)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 703)) // token='elif' && (a = named_expression_rule(p)) // named_expression && @@ -6198,7 +6255,7 @@ elif_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_If ( a , b , CHECK ( asdl_stmt_seq* , _PyPegen_singleton_seq ( p , c ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6221,7 +6278,7 @@ elif_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 692)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 703)) // token='elif' && (a = named_expression_rule(p)) // named_expression && @@ -6243,7 +6300,7 @@ elif_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_If ( a , b , c , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6302,7 +6359,7 @@ else_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 691)) // token='else' + (_keyword = _PyPegen_expect_token(p, 702)) // token='else' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -6311,7 +6368,7 @@ else_block_rule(Parser *p) { D(fprintf(stderr, "%*c+ else_block[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else' &&':' block")); _res = b; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6381,7 +6438,7 @@ while_stmt_rule(Parser *p) asdl_stmt_seq* b; void *c; if ( - (_keyword = _PyPegen_expect_token(p, 694)) // token='while' + (_keyword = _PyPegen_expect_token(p, 705)) // token='while' && (a = named_expression_rule(p)) // named_expression && @@ -6403,7 +6460,7 @@ while_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_While ( a , b , c , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6481,11 +6538,11 @@ for_stmt_rule(Parser *p) expr_ty t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 699)) // token='for' + (_keyword = _PyPegen_expect_token(p, 710)) // token='for' && (t = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 700)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 711)) // token='in' && (_cut_var = 1) && @@ -6511,7 +6568,7 @@ for_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_For ( t , ex , b , el , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6543,13 +6600,13 @@ for_stmt_rule(Parser *p) expr_ty t; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 703)) // token='async' + (_keyword = _PyPegen_expect_token(p, 714)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 699)) // token='for' + (_keyword_1 = _PyPegen_expect_token(p, 710)) // token='for' && (t = star_targets_rule(p)) // star_targets && - (_keyword_2 = _PyPegen_expect_token(p, 700)) // token='in' + (_keyword_2 = _PyPegen_expect_token(p, 711)) // token='in' && (_cut_var = 1) && @@ -6575,7 +6632,7 @@ for_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( stmt_ty , 5 , "Async for loops are" , _PyAST_AsyncFor ( t , ex , b , el , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6678,7 +6735,7 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 652)) // token='with' + (_keyword = _PyPegen_expect_token(p, 663)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -6706,7 +6763,7 @@ with_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_With ( a , b , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6729,7 +6786,7 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 652)) // token='with' + (_keyword = _PyPegen_expect_token(p, 663)) // token='with' && (a = (asdl_withitem_seq*)_gather_34_rule(p)) // ','.with_item+ && @@ -6751,7 +6808,7 @@ with_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_With ( a , b , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6778,9 +6835,9 @@ with_stmt_rule(Parser *p) asdl_withitem_seq* a; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 703)) // token='async' + (_keyword = _PyPegen_expect_token(p, 714)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 652)) // token='with' + (_keyword_1 = _PyPegen_expect_token(p, 663)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && @@ -6806,7 +6863,7 @@ with_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( stmt_ty , 5 , "Async with statements are" , _PyAST_AsyncWith ( a , b , NULL , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6830,9 +6887,9 @@ with_stmt_rule(Parser *p) asdl_stmt_seq* b; void *tc; if ( - (_keyword = _PyPegen_expect_token(p, 703)) // token='async' + (_keyword = _PyPegen_expect_token(p, 714)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 652)) // token='with' + (_keyword_1 = _PyPegen_expect_token(p, 663)) // token='with' && (a = (asdl_withitem_seq*)_gather_34_rule(p)) // ','.with_item+ && @@ -6854,7 +6911,7 @@ with_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( stmt_ty , 5 , "Async with statements are" , _PyAST_AsyncWith ( a , b , NEW_TYPE_COMMENT ( p , tc ) , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6918,7 +6975,7 @@ with_item_rule(Parser *p) if ( (e = expression_rule(p)) // expression && - (_keyword = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword = _PyPegen_expect_token(p, 696)) // token='as' && (t = star_target_rule(p)) // star_target && @@ -6927,7 +6984,7 @@ with_item_rule(Parser *p) { D(fprintf(stderr, "%*c+ with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' star_target &(',' | ')' | ':')")); _res = _PyAST_withitem ( e , t , p -> arena ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -6970,7 +7027,7 @@ with_item_rule(Parser *p) { D(fprintf(stderr, "%*c+ with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression")); _res = _PyAST_withitem ( e , NULL , p -> arena ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7043,7 +7100,7 @@ try_stmt_rule(Parser *p) asdl_stmt_seq* b; asdl_stmt_seq* f; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='try' + (_keyword = _PyPegen_expect_token(p, 672)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7063,7 +7120,7 @@ try_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Try ( b , NULL , NULL , f , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7087,7 +7144,7 @@ try_stmt_rule(Parser *p) asdl_excepthandler_seq* ex; void *f; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='try' + (_keyword = _PyPegen_expect_token(p, 672)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7111,7 +7168,7 @@ try_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Try ( b , ex , el , f , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7135,7 +7192,7 @@ try_stmt_rule(Parser *p) asdl_excepthandler_seq* ex; void *f; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='try' + (_keyword = _PyPegen_expect_token(p, 672)) // token='try' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7159,7 +7216,7 @@ try_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( stmt_ty , 11 , "Exception groups are" , _PyAST_TryStar ( b , ex , el , f , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7234,7 +7291,7 @@ except_block_rule(Parser *p) asdl_stmt_seq* b; expr_ty e; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (e = expression_rule(p)) // expression && @@ -7254,7 +7311,7 @@ except_block_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_ExceptHandler ( e , NULL , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7278,11 +7335,11 @@ except_block_rule(Parser *p) expr_ty e; expr_ty t; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (e = expression_rule(p)) // expression && - (_keyword_1 = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword_1 = _PyPegen_expect_token(p, 696)) // token='as' && (t = _PyPegen_name_token(p)) // NAME && @@ -7302,7 +7359,7 @@ except_block_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_ExceptHandler ( e , ( ( expr_ty ) t ) -> v . Name . id , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7324,7 +7381,7 @@ except_block_rule(Parser *p) asdl_stmt_seq* b; expr_ty e; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (e = expressions_rule(p)) // expressions && @@ -7344,7 +7401,7 @@ except_block_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( excepthandler_ty , 14 , "except expressions without parentheses are" , _PyAST_ExceptHandler ( e , NULL , b , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7365,7 +7422,7 @@ except_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* b; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -7383,7 +7440,7 @@ except_block_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_ExceptHandler ( NULL , NULL , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7477,7 +7534,7 @@ except_star_block_rule(Parser *p) asdl_stmt_seq* b; expr_ty e; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -7499,7 +7556,7 @@ except_star_block_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_ExceptHandler ( e , NULL , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7524,13 +7581,13 @@ except_star_block_rule(Parser *p) expr_ty e; expr_ty t; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && (e = expression_rule(p)) // expression && - (_keyword_1 = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword_1 = _PyPegen_expect_token(p, 696)) // token='as' && (t = _PyPegen_name_token(p)) // NAME && @@ -7550,7 +7607,7 @@ except_star_block_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_ExceptHandler ( e , ( ( expr_ty ) t ) -> v . Name . id , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7573,7 +7630,7 @@ except_star_block_rule(Parser *p) asdl_stmt_seq* b; expr_ty e; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -7595,7 +7652,7 @@ except_star_block_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( excepthandler_ty , 14 , "except expressions without parentheses are" , _PyAST_ExceptHandler ( e , NULL , b , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7673,7 +7730,7 @@ finally_block_rule(Parser *p) Token * _literal; asdl_stmt_seq* a; if ( - (_keyword = _PyPegen_expect_token(p, 678)) // token='finally' + (_keyword = _PyPegen_expect_token(p, 689)) // token='finally' && (_literal = _PyPegen_expect_forced_token(p, 11, ":")) // forced_token=':' && @@ -7682,7 +7739,7 @@ finally_block_rule(Parser *p) { D(fprintf(stderr, "%*c+ finally_block[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally' &&':' block")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7763,7 +7820,7 @@ match_stmt_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( stmt_ty , 10 , "Pattern matching is" , _PyAST_Match ( subject , cases , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7849,7 +7906,7 @@ subject_expr_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Tuple ( CHECK ( asdl_expr_seq* , _PyPegen_seq_insert_in_front ( p , value , values ) ) , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7942,7 +7999,7 @@ case_block_rule(Parser *p) { D(fprintf(stderr, "%*c+ case_block[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"case\" patterns guard? ':' block")); _res = _PyAST_match_case ( pattern , guard , body , p -> arena ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -7981,14 +8038,14 @@ guard_rule(Parser *p) Token * _keyword; expr_ty guard; if ( - (_keyword = _PyPegen_expect_token(p, 687)) // token='if' + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' && (guard = named_expression_rule(p)) // named_expression ) { D(fprintf(stderr, "%*c+ guard[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' named_expression")); _res = guard; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8049,7 +8106,7 @@ patterns_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchSequence ( patterns , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8176,7 +8233,7 @@ as_pattern_rule(Parser *p) if ( (pattern = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword = _PyPegen_expect_token(p, 696)) // token='as' && (target = pattern_capture_target_rule(p)) // pattern_capture_target ) @@ -8192,7 +8249,7 @@ as_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchAs ( pattern , target -> v . Name . id , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8272,7 +8329,7 @@ or_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = asdl_seq_LEN ( patterns ) == 1 ? asdl_seq_GET ( patterns , 0 ) : _PyAST_MatchOr ( patterns , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8525,7 +8582,7 @@ literal_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchValue ( value , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8558,7 +8615,7 @@ literal_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchValue ( value , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8591,7 +8648,7 @@ literal_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchValue ( value , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8610,7 +8667,7 @@ literal_pattern_rule(Parser *p) D(fprintf(stderr, "%*c> literal_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 624)) // token='None' + (_keyword = _PyPegen_expect_token(p, 628)) // token='None' ) { D(fprintf(stderr, "%*c+ literal_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); @@ -8624,7 +8681,7 @@ literal_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchSingleton ( Py_None , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8643,7 +8700,7 @@ literal_pattern_rule(Parser *p) D(fprintf(stderr, "%*c> literal_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 623)) // token='True' + (_keyword = _PyPegen_expect_token(p, 627)) // token='True' ) { D(fprintf(stderr, "%*c+ literal_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); @@ -8657,7 +8714,7 @@ literal_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchSingleton ( Py_True , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8676,7 +8733,7 @@ literal_pattern_rule(Parser *p) D(fprintf(stderr, "%*c> literal_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 625)) // token='False' + (_keyword = _PyPegen_expect_token(p, 629)) // token='False' ) { D(fprintf(stderr, "%*c+ literal_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); @@ -8690,7 +8747,7 @@ literal_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchSingleton ( Py_False , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8804,7 +8861,7 @@ literal_expr_rule(Parser *p) D(fprintf(stderr, "%*c> literal_expr[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 624)) // token='None' + (_keyword = _PyPegen_expect_token(p, 628)) // token='None' ) { D(fprintf(stderr, "%*c+ literal_expr[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); @@ -8818,7 +8875,7 @@ literal_expr_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Constant ( Py_None , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8837,7 +8894,7 @@ literal_expr_rule(Parser *p) D(fprintf(stderr, "%*c> literal_expr[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 623)) // token='True' + (_keyword = _PyPegen_expect_token(p, 627)) // token='True' ) { D(fprintf(stderr, "%*c+ literal_expr[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); @@ -8851,7 +8908,7 @@ literal_expr_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Constant ( Py_True , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8870,7 +8927,7 @@ literal_expr_rule(Parser *p) D(fprintf(stderr, "%*c> literal_expr[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 625)) // token='False' + (_keyword = _PyPegen_expect_token(p, 629)) // token='False' ) { D(fprintf(stderr, "%*c+ literal_expr[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); @@ -8884,7 +8941,7 @@ literal_expr_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Constant ( Py_False , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8953,7 +9010,7 @@ complex_number_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( real , Add , imag , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -8992,7 +9049,7 @@ complex_number_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( real , Sub , imag , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9009,7 +9066,7 @@ complex_number_rule(Parser *p) return _res; } -// signed_number: NUMBER | '-' NUMBER +// signed_number: NUMBER | '+' NUMBER | '-' NUMBER static expr_ty signed_number_rule(Parser *p) { @@ -9050,6 +9107,33 @@ signed_number_rule(Parser *p) D(fprintf(stderr, "%*c%s signed_number[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NUMBER")); } + { // '+' NUMBER + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> signed_number[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+' NUMBER")); + Token * _literal; + expr_ty number; + if ( + (_literal = _PyPegen_expect_token(p, 14)) // token='+' + && + (number = _PyPegen_number_token(p)) // NUMBER + ) + { + D(fprintf(stderr, "%*c+ signed_number[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+' NUMBER")); + _res = number; + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s signed_number[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+' NUMBER")); + } { // '-' NUMBER if (p->error_indicator) { p->level--; @@ -9075,7 +9159,7 @@ signed_number_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_UnaryOp ( USub , number , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9092,7 +9176,7 @@ signed_number_rule(Parser *p) return _res; } -// signed_real_number: real_number | '-' real_number +// signed_real_number: real_number | '+' real_number | '-' real_number static expr_ty signed_real_number_rule(Parser *p) { @@ -9133,6 +9217,33 @@ signed_real_number_rule(Parser *p) D(fprintf(stderr, "%*c%s signed_real_number[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "real_number")); } + { // '+' real_number + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> signed_real_number[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+' real_number")); + Token * _literal; + expr_ty real; + if ( + (_literal = _PyPegen_expect_token(p, 14)) // token='+' + && + (real = real_number_rule(p)) // real_number + ) + { + D(fprintf(stderr, "%*c+ signed_real_number[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+' real_number")); + _res = real; + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s signed_real_number[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+' real_number")); + } { // '-' real_number if (p->error_indicator) { p->level--; @@ -9158,7 +9269,7 @@ signed_real_number_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_UnaryOp ( USub , real , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9201,7 +9312,7 @@ real_number_rule(Parser *p) { D(fprintf(stderr, "%*c+ real_number[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NUMBER")); _res = _PyPegen_ensure_real ( p , real ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9218,7 +9329,7 @@ real_number_rule(Parser *p) return _res; } -// imaginary_number: NUMBER +// imaginary_number: NUMBER | '+' NUMBER static expr_ty imaginary_number_rule(Parser *p) { @@ -9244,7 +9355,7 @@ imaginary_number_rule(Parser *p) { D(fprintf(stderr, "%*c+ imaginary_number[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NUMBER")); _res = _PyPegen_ensure_imaginary ( p , imag ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9255,6 +9366,33 @@ imaginary_number_rule(Parser *p) D(fprintf(stderr, "%*c%s imaginary_number[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NUMBER")); } + { // '+' NUMBER + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> imaginary_number[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+' NUMBER")); + Token * _literal; + expr_ty imag; + if ( + (_literal = _PyPegen_expect_token(p, 14)) // token='+' + && + (imag = _PyPegen_number_token(p)) // NUMBER + ) + { + D(fprintf(stderr, "%*c+ imaginary_number[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+' NUMBER")); + _res = _PyPegen_ensure_imaginary ( p , imag ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s imaginary_number[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+' NUMBER")); + } _res = NULL; done: p->level--; @@ -9305,7 +9443,7 @@ capture_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchAs ( NULL , target -> v . Name . id , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9352,7 +9490,7 @@ pattern_capture_target_rule(Parser *p) { D(fprintf(stderr, "%*c+ pattern_capture_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!\"_\" NAME !('.' | '(' | '=')")); _res = _PyPegen_set_expr_context ( p , name , Store ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9413,7 +9551,7 @@ wildcard_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchAs ( NULL , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9476,7 +9614,7 @@ value_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchValue ( attr , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9579,7 +9717,7 @@ attr_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Attribute ( value , attr -> v . Name . id , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9686,7 +9824,7 @@ group_pattern_rule(Parser *p) { D(fprintf(stderr, "%*c+ group_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' pattern ')'")); _res = pattern; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9753,7 +9891,7 @@ sequence_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchSequence ( patterns , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9792,7 +9930,7 @@ sequence_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchSequence ( patterns , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9841,7 +9979,7 @@ open_sequence_pattern_rule(Parser *p) { D(fprintf(stderr, "%*c+ open_sequence_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "maybe_star_pattern ',' maybe_sequence_pattern?")); _res = _PyPegen_seq_insert_in_front ( p , pattern , patterns ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -9888,7 +10026,7 @@ maybe_sequence_pattern_rule(Parser *p) { D(fprintf(stderr, "%*c+ maybe_sequence_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.maybe_star_pattern+ ','?")); _res = patterns; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10013,7 +10151,7 @@ star_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchStar ( target -> v . Name . id , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10049,7 +10187,7 @@ star_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchStar ( NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10072,6 +10210,7 @@ star_pattern_rule(Parser *p) // | '{' double_star_pattern ','? '}' // | '{' items_pattern ',' double_star_pattern ','? '}' // | '{' items_pattern ','? '}' +// | invalid_mapping_pattern static pattern_ty mapping_pattern_rule(Parser *p) { @@ -10118,7 +10257,7 @@ mapping_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchMapping ( NULL , NULL , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10161,7 +10300,7 @@ mapping_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchMapping ( NULL , NULL , rest -> v . Name . id , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10210,7 +10349,7 @@ mapping_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchMapping ( CHECK ( asdl_expr_seq* , _PyPegen_get_pattern_keys ( p , items ) ) , CHECK ( asdl_pattern_seq* , _PyPegen_get_patterns ( p , items ) ) , rest -> v . Name . id , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10253,7 +10392,7 @@ mapping_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchMapping ( CHECK ( asdl_expr_seq* , _PyPegen_get_pattern_keys ( p , items ) ) , CHECK ( asdl_pattern_seq* , _PyPegen_get_patterns ( p , items ) ) , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10264,6 +10403,25 @@ mapping_pattern_rule(Parser *p) D(fprintf(stderr, "%*c%s mapping_pattern[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' items_pattern ','? '}'")); } + if (p->call_invalid_rules) { // invalid_mapping_pattern + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> mapping_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_mapping_pattern")); + void *invalid_mapping_pattern_var; + if ( + (invalid_mapping_pattern_var = invalid_mapping_pattern_rule(p)) // invalid_mapping_pattern + ) + { + D(fprintf(stderr, "%*c+ mapping_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_mapping_pattern")); + _res = invalid_mapping_pattern_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s mapping_pattern[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_mapping_pattern")); + } _res = NULL; done: p->level--; @@ -10340,7 +10498,7 @@ key_value_pattern_rule(Parser *p) { D(fprintf(stderr, "%*c+ key_value_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(literal_expr | attr) ':' pattern")); _res = _PyPegen_key_pattern_pair ( p , key , pattern ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10386,7 +10544,7 @@ double_star_pattern_rule(Parser *p) { D(fprintf(stderr, "%*c+ double_star_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' pattern_capture_target")); _res = target; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10458,7 +10616,7 @@ class_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchClass ( cls , NULL , NULL , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10504,7 +10662,7 @@ class_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchClass ( cls , patterns , NULL , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10550,7 +10708,7 @@ class_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchClass ( cls , NULL , CHECK ( asdl_identifier_seq* , _PyPegen_map_names_to_ids ( p , CHECK ( asdl_expr_seq* , _PyPegen_get_pattern_keys ( p , keywords ) ) ) ) , CHECK ( asdl_pattern_seq* , _PyPegen_get_patterns ( p , keywords ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10602,7 +10760,7 @@ class_pattern_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_MatchClass ( cls , patterns , CHECK ( asdl_identifier_seq* , _PyPegen_map_names_to_ids ( p , CHECK ( asdl_expr_seq* , _PyPegen_get_pattern_keys ( p , keywords ) ) ) ) , CHECK ( asdl_pattern_seq* , _PyPegen_get_patterns ( p , keywords ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10664,7 +10822,7 @@ positional_patterns_rule(Parser *p) { D(fprintf(stderr, "%*c+ positional_patterns[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.pattern+")); _res = args; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10751,7 +10909,7 @@ keyword_pattern_rule(Parser *p) { D(fprintf(stderr, "%*c+ keyword_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '=' pattern")); _res = _PyPegen_key_pattern_pair ( p , arg , value ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10824,7 +10982,7 @@ type_alias_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( stmt_ty , 12 , "Type statement is" , _PyAST_TypeAlias ( CHECK ( expr_ty , _PyPegen_set_expr_context ( p , n , Store ) ) , t , b , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10892,7 +11050,7 @@ type_params_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_params[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'[' type_param_seq ']'")); _res = CHECK_VERSION ( asdl_type_param_seq* , 12 , "Type parameter lists are" , t ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -10939,7 +11097,7 @@ type_param_seq_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_param_seq[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.type_param+ ','?")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11014,7 +11172,7 @@ type_param_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_TypeVar ( a -> v . Name . id , b , c , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11072,7 +11230,7 @@ type_param_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_TypeVarTuple ( a -> v . Name . id , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11111,7 +11269,7 @@ type_param_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_ParamSpec ( a -> v . Name . id , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11158,7 +11316,7 @@ type_param_bound_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_param_bound[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' expression")); _res = e; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11204,7 +11362,7 @@ type_param_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_param_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'=' expression")); _res = CHECK_VERSION ( expr_ty , 13 , "Type parameter defaults are" , e ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11250,7 +11408,7 @@ type_param_starred_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_param_starred_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'=' star_expression")); _res = CHECK_VERSION ( expr_ty , 13 , "Type parameter defaults are" , e ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11318,7 +11476,7 @@ expressions_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Tuple ( CHECK ( asdl_expr_seq* , _PyPegen_seq_insert_in_front ( p , a , b ) ) , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11354,7 +11512,7 @@ expressions_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Tuple ( CHECK ( asdl_expr_seq* , _PyPegen_singleton_seq ( p , a ) ) , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11391,9 +11549,10 @@ expressions_rule(Parser *p) } // expression: +// | invalid_if_expression // | invalid_expression // | invalid_legacy_expression -// | disjunction 'if' disjunction 'else' expression +// | if_expression // | disjunction // | lambdef static expr_ty @@ -11412,15 +11571,25 @@ expression_rule(Parser *p) return _res; } int _mark = p->mark; - if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) { - p->error_indicator = 1; - p->level--; - return NULL; + if (p->call_invalid_rules) { // invalid_if_expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> expression[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_if_expression")); + void *invalid_if_expression_var; + if ( + (invalid_if_expression_var = invalid_if_expression_rule(p)) // invalid_if_expression + ) + { + D(fprintf(stderr, "%*c+ expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_if_expression")); + _res = invalid_if_expression_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s expression[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_if_expression")); } - int _start_lineno = p->tokens[_mark]->lineno; - UNUSED(_start_lineno); // Only used by EXTRA macro - int _start_col_offset = p->tokens[_mark]->col_offset; - UNUSED(_start_col_offset); // Only used by EXTRA macro if (p->call_invalid_rules) { // invalid_expression if (p->error_indicator) { p->level--; @@ -11459,50 +11628,24 @@ expression_rule(Parser *p) D(fprintf(stderr, "%*c%s expression[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_legacy_expression")); } - { // disjunction 'if' disjunction 'else' expression + { // if_expression if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> expression[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction 'else' expression")); - Token * _keyword; - Token * _keyword_1; - expr_ty a; - expr_ty b; - expr_ty c; + D(fprintf(stderr, "%*c> expression[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "if_expression")); + expr_ty if_expression_var; if ( - (a = disjunction_rule(p)) // disjunction - && - (_keyword = _PyPegen_expect_token(p, 687)) // token='if' - && - (b = disjunction_rule(p)) // disjunction - && - (_keyword_1 = _PyPegen_expect_token(p, 691)) // token='else' - && - (c = expression_rule(p)) // expression + (if_expression_var = if_expression_rule(p)) // if_expression ) { - D(fprintf(stderr, "%*c+ expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction 'else' expression")); - Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); - if (_token == NULL) { - p->level--; - return NULL; - } - int _end_lineno = _token->end_lineno; - UNUSED(_end_lineno); // Only used by EXTRA macro - int _end_col_offset = _token->end_col_offset; - UNUSED(_end_col_offset); // Only used by EXTRA macro - _res = _PyAST_IfExp ( b , a , c , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { - p->error_indicator = 1; - p->level--; - return NULL; - } + D(fprintf(stderr, "%*c+ expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "if_expression")); + _res = if_expression_var; goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s expression[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "disjunction 'if' disjunction 'else' expression")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "if_expression")); } { // disjunction if (p->error_indicator) { @@ -11549,6 +11692,79 @@ expression_rule(Parser *p) return _res; } +// if_expression: disjunction 'if' disjunction 'else' expression +static expr_ty +if_expression_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + expr_ty _res = NULL; + int _mark = p->mark; + if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) { + p->error_indicator = 1; + p->level--; + return NULL; + } + int _start_lineno = p->tokens[_mark]->lineno; + UNUSED(_start_lineno); // Only used by EXTRA macro + int _start_col_offset = p->tokens[_mark]->col_offset; + UNUSED(_start_col_offset); // Only used by EXTRA macro + { // disjunction 'if' disjunction 'else' expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> if_expression[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction 'else' expression")); + Token * _keyword; + Token * _keyword_1; + expr_ty a; + expr_ty b; + expr_ty c; + if ( + (a = disjunction_rule(p)) // disjunction + && + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' + && + (b = disjunction_rule(p)) // disjunction + && + (_keyword_1 = _PyPegen_expect_token(p, 702)) // token='else' + && + (c = expression_rule(p)) // expression + ) + { + D(fprintf(stderr, "%*c+ if_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction 'else' expression")); + Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); + if (_token == NULL) { + p->level--; + return NULL; + } + int _end_lineno = _token->end_lineno; + UNUSED(_end_lineno); // Only used by EXTRA macro + int _end_col_offset = _token->end_col_offset; + UNUSED(_end_col_offset); // Only used by EXTRA macro + _res = _PyAST_IfExp ( b , a , c , EXTRA ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s if_expression[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "disjunction 'if' disjunction 'else' expression")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // yield_expr: 'yield' 'from' expression | 'yield' star_expressions? static expr_ty yield_expr_rule(Parser *p) @@ -11583,7 +11799,7 @@ yield_expr_rule(Parser *p) if ( (_keyword = _PyPegen_expect_token(p, 588)) // token='yield' && - (_keyword_1 = _PyPegen_expect_token(p, 638)) // token='from' + (_keyword_1 = _PyPegen_expect_token(p, 646)) // token='from' && (a = expression_rule(p)) // expression ) @@ -11599,7 +11815,7 @@ yield_expr_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_YieldFrom ( a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11635,7 +11851,7 @@ yield_expr_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Yield ( a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11706,7 +11922,7 @@ star_expressions_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Tuple ( CHECK ( asdl_expr_seq* , _PyPegen_seq_insert_in_front ( p , a , b ) ) , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11742,7 +11958,7 @@ star_expressions_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Tuple ( CHECK ( asdl_expr_seq* , _PyPegen_singleton_seq ( p , a ) ) , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11829,7 +12045,7 @@ star_expression_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Starred ( a , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11896,7 +12112,7 @@ star_named_expressions_rule(Parser *p) { D(fprintf(stderr, "%*c+ star_named_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.star_named_expression+ ','?")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11913,6 +12129,53 @@ star_named_expressions_rule(Parser *p) return _res; } +// star_named_expressions_sequence: ','.star_named_expression_sequence+ ','? +static asdl_expr_seq* +star_named_expressions_sequence_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + asdl_expr_seq* _res = NULL; + int _mark = p->mark; + { // ','.star_named_expression_sequence+ ','? + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> star_named_expressions_sequence[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.star_named_expression_sequence+ ','?")); + void *_opt_var; + UNUSED(_opt_var); // Silence compiler warnings + asdl_expr_seq* a; + if ( + (a = (asdl_expr_seq*)_gather_60_rule(p)) // ','.star_named_expression_sequence+ + && + (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? + ) + { + D(fprintf(stderr, "%*c+ star_named_expressions_sequence[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.star_named_expression_sequence+ ','?")); + _res = a; + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s star_named_expressions_sequence[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.star_named_expression_sequence+ ','?")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // star_named_expression: '*' bitwise_or | named_expression static expr_ty star_named_expression_rule(Parser *p) @@ -11960,7 +12223,7 @@ star_named_expression_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Starred ( a , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -11996,6 +12259,65 @@ star_named_expression_rule(Parser *p) return _res; } +// star_named_expression_sequence: +// | invalid_starred_expression_unpacking_sequence +// | star_named_expression +static expr_ty +star_named_expression_sequence_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + expr_ty _res = NULL; + int _mark = p->mark; + if (p->call_invalid_rules) { // invalid_starred_expression_unpacking_sequence + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> star_named_expression_sequence[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_starred_expression_unpacking_sequence")); + void *invalid_starred_expression_unpacking_sequence_var; + if ( + (invalid_starred_expression_unpacking_sequence_var = invalid_starred_expression_unpacking_sequence_rule(p)) // invalid_starred_expression_unpacking_sequence + ) + { + D(fprintf(stderr, "%*c+ star_named_expression_sequence[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_starred_expression_unpacking_sequence")); + _res = invalid_starred_expression_unpacking_sequence_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s star_named_expression_sequence[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_starred_expression_unpacking_sequence")); + } + { // star_named_expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> star_named_expression_sequence[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression")); + expr_ty star_named_expression_var; + if ( + (star_named_expression_var = star_named_expression_rule(p)) // star_named_expression + ) + { + D(fprintf(stderr, "%*c+ star_named_expression_sequence[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression")); + _res = star_named_expression_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s star_named_expression_sequence[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // assignment_expression: NAME ':=' ~ expression static expr_ty assignment_expression_rule(Parser *p) @@ -12049,7 +12371,7 @@ assignment_expression_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( expr_ty , 8 , "Assignment expressions are" , _PyAST_NamedExpr ( CHECK ( expr_ty , _PyPegen_set_expr_context ( p , a , Store ) ) , b , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -12185,7 +12507,7 @@ disjunction_rule(Parser *p) if ( (a = conjunction_rule(p)) // conjunction && - (b = _loop1_59_rule(p)) // (('or' conjunction))+ + (b = _loop1_61_rule(p)) // (('or' conjunction))+ ) { D(fprintf(stderr, "%*c+ disjunction[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "conjunction (('or' conjunction))+")); @@ -12199,7 +12521,7 @@ disjunction_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BoolOp ( Or , CHECK ( asdl_expr_seq* , _PyPegen_seq_insert_in_front ( p , a , b ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -12273,7 +12595,7 @@ conjunction_rule(Parser *p) if ( (a = inversion_rule(p)) // inversion && - (b = _loop1_60_rule(p)) // (('and' inversion))+ + (b = _loop1_62_rule(p)) // (('and' inversion))+ ) { D(fprintf(stderr, "%*c+ conjunction[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "inversion (('and' inversion))+")); @@ -12287,7 +12609,7 @@ conjunction_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BoolOp ( And , CHECK ( asdl_expr_seq* , _PyPegen_seq_insert_in_front ( p , a , b ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -12359,7 +12681,7 @@ inversion_rule(Parser *p) Token * _keyword; expr_ty a; if ( - (_keyword = _PyPegen_expect_token(p, 708)) // token='not' + (_keyword = _PyPegen_expect_token(p, 719)) // token='not' && (a = inversion_rule(p)) // inversion ) @@ -12375,7 +12697,7 @@ inversion_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_UnaryOp ( Not , a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -12445,7 +12767,7 @@ comparison_rule(Parser *p) if ( (a = bitwise_or_rule(p)) // bitwise_or && - (b = _loop1_61_rule(p)) // compare_op_bitwise_or_pair+ + (b = _loop1_63_rule(p)) // compare_op_bitwise_or_pair+ ) { D(fprintf(stderr, "%*c+ comparison[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or compare_op_bitwise_or_pair+")); @@ -12459,7 +12781,7 @@ comparison_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Compare ( a , CHECK ( asdl_int_seq* , _PyPegen_get_cmpops ( p , b ) ) , CHECK ( asdl_expr_seq* , _PyPegen_get_exprs ( p , b ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -12743,7 +13065,7 @@ eq_bitwise_or_rule(Parser *p) { D(fprintf(stderr, "%*c+ eq_bitwise_or[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'==' bitwise_or")); _res = _PyPegen_cmpop_expr_pair ( p , Eq , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -12779,17 +13101,17 @@ noteq_bitwise_or_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> noteq_bitwise_or[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('!=') bitwise_or")); - void *_tmp_62_var; + void *_tmp_64_var; expr_ty a; if ( - (_tmp_62_var = _tmp_62_rule(p)) // '!=' + (_tmp_64_var = _tmp_64_rule(p)) // '!=' && (a = bitwise_or_rule(p)) // bitwise_or ) { D(fprintf(stderr, "%*c+ noteq_bitwise_or[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "('!=') bitwise_or")); _res = _PyPegen_cmpop_expr_pair ( p , NotEq , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -12835,7 +13157,7 @@ lte_bitwise_or_rule(Parser *p) { D(fprintf(stderr, "%*c+ lte_bitwise_or[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'<=' bitwise_or")); _res = _PyPegen_cmpop_expr_pair ( p , LtE , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -12881,7 +13203,7 @@ lt_bitwise_or_rule(Parser *p) { D(fprintf(stderr, "%*c+ lt_bitwise_or[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'<' bitwise_or")); _res = _PyPegen_cmpop_expr_pair ( p , Lt , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -12927,7 +13249,7 @@ gte_bitwise_or_rule(Parser *p) { D(fprintf(stderr, "%*c+ gte_bitwise_or[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'>=' bitwise_or")); _res = _PyPegen_cmpop_expr_pair ( p , GtE , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -12973,7 +13295,7 @@ gt_bitwise_or_rule(Parser *p) { D(fprintf(stderr, "%*c+ gt_bitwise_or[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'>' bitwise_or")); _res = _PyPegen_cmpop_expr_pair ( p , Gt , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13013,16 +13335,16 @@ notin_bitwise_or_rule(Parser *p) Token * _keyword_1; expr_ty a; if ( - (_keyword = _PyPegen_expect_token(p, 708)) // token='not' + (_keyword = _PyPegen_expect_token(p, 719)) // token='not' && - (_keyword_1 = _PyPegen_expect_token(p, 700)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 711)) // token='in' && (a = bitwise_or_rule(p)) // bitwise_or ) { D(fprintf(stderr, "%*c+ notin_bitwise_or[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'not' 'in' bitwise_or")); _res = _PyPegen_cmpop_expr_pair ( p , NotIn , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13061,14 +13383,14 @@ in_bitwise_or_rule(Parser *p) Token * _keyword; expr_ty a; if ( - (_keyword = _PyPegen_expect_token(p, 700)) // token='in' + (_keyword = _PyPegen_expect_token(p, 711)) // token='in' && (a = bitwise_or_rule(p)) // bitwise_or ) { D(fprintf(stderr, "%*c+ in_bitwise_or[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'in' bitwise_or")); _res = _PyPegen_cmpop_expr_pair ( p , In , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13110,14 +13432,14 @@ isnot_bitwise_or_rule(Parser *p) if ( (_keyword = _PyPegen_expect_token(p, 597)) // token='is' && - (_keyword_1 = _PyPegen_expect_token(p, 708)) // token='not' + (_keyword_1 = _PyPegen_expect_token(p, 719)) // token='not' && (a = bitwise_or_rule(p)) // bitwise_or ) { D(fprintf(stderr, "%*c+ isnot_bitwise_or[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'is' 'not' bitwise_or")); _res = _PyPegen_cmpop_expr_pair ( p , IsNot , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13163,7 +13485,7 @@ is_bitwise_or_rule(Parser *p) { D(fprintf(stderr, "%*c+ is_bitwise_or[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'is' bitwise_or")); _res = _PyPegen_cmpop_expr_pair ( p , Is , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13266,7 +13588,7 @@ bitwise_or_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , BitOr , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13388,7 +13710,7 @@ bitwise_xor_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , BitXor , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13510,7 +13832,7 @@ bitwise_and_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , BitAnd , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13632,7 +13954,7 @@ shift_expr_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , LShift , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13671,7 +13993,7 @@ shift_expr_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , RShift , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13812,7 +14134,7 @@ sum_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , Add , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13851,7 +14173,7 @@ sum_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , Sub , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -13980,7 +14302,7 @@ term_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , Mult , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14019,7 +14341,7 @@ term_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , Div , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14058,7 +14380,7 @@ term_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , FloorDiv , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14097,7 +14419,7 @@ term_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , Mod , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14136,7 +14458,7 @@ term_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( expr_ty , 5 , "The '@' operator is" , _PyAST_BinOp ( a , MatMult , b , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14242,7 +14564,7 @@ factor_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_UnaryOp ( UAdd , a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14278,7 +14600,7 @@ factor_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_UnaryOp ( USub , a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14314,7 +14636,7 @@ factor_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_UnaryOp ( Invert , a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14401,7 +14723,7 @@ power_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_BinOp ( a , Pow , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14488,7 +14810,7 @@ await_primary_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = CHECK_VERSION ( expr_ty , 5 , "Await expressions are" , _PyAST_Await ( a , EXTRA ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14616,7 +14938,7 @@ primary_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Attribute ( a , b -> v . Name . id , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14652,7 +14974,7 @@ primary_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Call ( a , CHECK ( asdl_expr_seq* , ( asdl_expr_seq* ) _PyPegen_singleton_seq ( p , b ) ) , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14694,7 +15016,7 @@ primary_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Call ( a , ( b ) ? ( ( expr_ty ) b ) -> v . Call . args : NULL , ( b ) ? ( ( expr_ty ) b ) -> v . Call . keywords : NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14736,7 +15058,7 @@ primary_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Subscript ( a , b , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14809,7 +15131,7 @@ slices_rule(Parser *p) { D(fprintf(stderr, "%*c+ slices[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice !','")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14830,7 +15152,7 @@ slices_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_gather_64_rule(p)) // ','.(slice | starred_expression)+ + (a = (asdl_expr_seq*)_gather_66_rule(p)) // ','.(slice | starred_expression)+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) @@ -14846,7 +15168,7 @@ slices_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Tuple ( a , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14902,7 +15224,7 @@ slice_rule(Parser *p) && (b = expression_rule(p), !p->error_indicator) // expression? && - (c = _tmp_65_rule(p), !p->error_indicator) // [':' expression?] + (c = _tmp_67_rule(p), !p->error_indicator) // [':' expression?] ) { D(fprintf(stderr, "%*c+ slice[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression? ':' expression? [':' expression?]")); @@ -14916,7 +15238,7 @@ slice_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Slice ( a , b , c , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14940,7 +15262,7 @@ slice_rule(Parser *p) { D(fprintf(stderr, "%*c+ slice[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "named_expression")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -14964,9 +15286,9 @@ slice_rule(Parser *p) // | 'None' // | &(STRING | FSTRING_START | TSTRING_START) strings // | NUMBER -// | &'(' (tuple | group | genexp) -// | &'[' (list | listcomp) -// | &'{' (dict | set | dictcomp | setcomp) +// | &'(' (genexp | tuple | group) +// | &'[' (listcomp | list) +// | &'{' (dictcomp | setcomp | dict | set) // | '...' static expr_ty atom_rule(Parser *p) @@ -15016,7 +15338,7 @@ atom_rule(Parser *p) D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 623)) // token='True' + (_keyword = _PyPegen_expect_token(p, 627)) // token='True' ) { D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); @@ -15030,7 +15352,7 @@ atom_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Constant ( Py_True , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15049,7 +15371,7 @@ atom_rule(Parser *p) D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 625)) // token='False' + (_keyword = _PyPegen_expect_token(p, 629)) // token='False' ) { D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); @@ -15063,7 +15385,7 @@ atom_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Constant ( Py_False , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15082,7 +15404,7 @@ atom_rule(Parser *p) D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 624)) // token='None' + (_keyword = _PyPegen_expect_token(p, 628)) // token='None' ) { D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); @@ -15096,7 +15418,7 @@ atom_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Constant ( Py_None , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15147,68 +15469,68 @@ atom_rule(Parser *p) D(fprintf(stderr, "%*c%s atom[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NUMBER")); } - { // &'(' (tuple | group | genexp) + { // &'(' (genexp | tuple | group) if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'(' (tuple | group | genexp)")); - void *_tmp_66_var; + D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'(' (genexp | tuple | group)")); + void *_tmp_68_var; if ( _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 7) // token='(' && - (_tmp_66_var = _tmp_66_rule(p)) // tuple | group | genexp + (_tmp_68_var = _tmp_68_rule(p)) // genexp | tuple | group ) { - D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&'(' (tuple | group | genexp)")); - _res = _tmp_66_var; + D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&'(' (genexp | tuple | group)")); + _res = _tmp_68_var; goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s atom[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&'(' (tuple | group | genexp)")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&'(' (genexp | tuple | group)")); } - { // &'[' (list | listcomp) + { // &'[' (listcomp | list) if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'[' (list | listcomp)")); - void *_tmp_67_var; + D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'[' (listcomp | list)")); + void *_tmp_69_var; if ( _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 9) // token='[' && - (_tmp_67_var = _tmp_67_rule(p)) // list | listcomp + (_tmp_69_var = _tmp_69_rule(p)) // listcomp | list ) { - D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&'[' (list | listcomp)")); - _res = _tmp_67_var; + D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&'[' (listcomp | list)")); + _res = _tmp_69_var; goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s atom[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&'[' (list | listcomp)")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&'[' (listcomp | list)")); } - { // &'{' (dict | set | dictcomp | setcomp) + { // &'{' (dictcomp | setcomp | dict | set) if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'{' (dict | set | dictcomp | setcomp)")); - void *_tmp_68_var; + D(fprintf(stderr, "%*c> atom[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "&'{' (dictcomp | setcomp | dict | set)")); + void *_tmp_70_var; if ( _PyPegen_lookahead_with_int(1, _PyPegen_expect_token, p, 25) // token='{' && - (_tmp_68_var = _tmp_68_rule(p)) // dict | set | dictcomp | setcomp + (_tmp_70_var = _tmp_70_rule(p)) // dictcomp | setcomp | dict | set ) { - D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&'{' (dict | set | dictcomp | setcomp)")); - _res = _tmp_68_var; + D(fprintf(stderr, "%*c+ atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "&'{' (dictcomp | setcomp | dict | set)")); + _res = _tmp_70_var; goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s atom[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&'{' (dict | set | dictcomp | setcomp)")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "&'{' (dictcomp | setcomp | dict | set)")); } { // '...' if (p->error_indicator) { @@ -15232,7 +15554,7 @@ atom_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Constant ( Py_Ellipsis , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15274,14 +15596,14 @@ group_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (a = _tmp_69_rule(p)) // yield_expr | named_expression + (a = _tmp_71_rule(p)) // yield_expr | named_expression && (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' ) { D(fprintf(stderr, "%*c+ group[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' (yield_expr | named_expression) ')'")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15370,7 +15692,7 @@ lambdef_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Lambda ( ( a ) ? a : CHECK ( arguments_ty , _PyPegen_empty_arguments ( p ) ) , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15475,16 +15797,16 @@ lambda_parameters_rule(Parser *p) if ( (a = lambda_slash_no_default_rule(p)) // lambda_slash_no_default && - (b = (asdl_arg_seq*)_loop0_70_rule(p)) // lambda_param_no_default* + (b = (asdl_arg_seq*)_loop0_72_rule(p)) // lambda_param_no_default* && - (c = _loop0_71_rule(p)) // lambda_param_with_default* + (c = _loop0_73_rule(p)) // lambda_param_with_default* && (d = lambda_star_etc_rule(p), !p->error_indicator) // lambda_star_etc? ) { D(fprintf(stderr, "%*c+ lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default lambda_param_no_default* lambda_param_with_default* lambda_star_etc?")); _res = CHECK_VERSION ( arguments_ty , 8 , "Positional-only parameters are" , _PyPegen_make_arguments ( p , a , NULL , b , c , d ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15507,14 +15829,14 @@ lambda_parameters_rule(Parser *p) if ( (a = lambda_slash_with_default_rule(p)) // lambda_slash_with_default && - (b = _loop0_71_rule(p)) // lambda_param_with_default* + (b = _loop0_73_rule(p)) // lambda_param_with_default* && (c = lambda_star_etc_rule(p), !p->error_indicator) // lambda_star_etc? ) { D(fprintf(stderr, "%*c+ lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default lambda_param_with_default* lambda_star_etc?")); _res = CHECK_VERSION ( arguments_ty , 8 , "Positional-only parameters are" , _PyPegen_make_arguments ( p , NULL , a , NULL , b , c ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15535,16 +15857,16 @@ lambda_parameters_rule(Parser *p) asdl_seq * b; void *c; if ( - (a = (asdl_arg_seq*)_loop1_72_rule(p)) // lambda_param_no_default+ + (a = (asdl_arg_seq*)_loop1_74_rule(p)) // lambda_param_no_default+ && - (b = _loop0_71_rule(p)) // lambda_param_with_default* + (b = _loop0_73_rule(p)) // lambda_param_with_default* && (c = lambda_star_etc_rule(p), !p->error_indicator) // lambda_star_etc? ) { D(fprintf(stderr, "%*c+ lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default+ lambda_param_with_default* lambda_star_etc?")); _res = _PyPegen_make_arguments ( p , NULL , NULL , a , b , c ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15564,14 +15886,14 @@ lambda_parameters_rule(Parser *p) asdl_seq * a; void *b; if ( - (a = _loop1_73_rule(p)) // lambda_param_with_default+ + (a = _loop1_75_rule(p)) // lambda_param_with_default+ && (b = lambda_star_etc_rule(p), !p->error_indicator) // lambda_star_etc? ) { D(fprintf(stderr, "%*c+ lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+ lambda_star_etc?")); _res = _PyPegen_make_arguments ( p , NULL , NULL , NULL , a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15595,7 +15917,7 @@ lambda_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_star_etc")); _res = _PyPegen_make_arguments ( p , NULL , NULL , NULL , NULL , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15637,7 +15959,7 @@ lambda_slash_no_default_rule(Parser *p) Token * _literal_1; asdl_arg_seq* a; if ( - (a = (asdl_arg_seq*)_loop1_72_rule(p)) // lambda_param_no_default+ + (a = (asdl_arg_seq*)_loop1_74_rule(p)) // lambda_param_no_default+ && (_literal = _PyPegen_expect_token(p, 17)) // token='/' && @@ -15646,7 +15968,7 @@ lambda_slash_no_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_slash_no_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default+ '/' ','")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15666,7 +15988,7 @@ lambda_slash_no_default_rule(Parser *p) Token * _literal; asdl_arg_seq* a; if ( - (a = (asdl_arg_seq*)_loop1_72_rule(p)) // lambda_param_no_default+ + (a = (asdl_arg_seq*)_loop1_74_rule(p)) // lambda_param_no_default+ && (_literal = _PyPegen_expect_token(p, 17)) // token='/' && @@ -15675,7 +15997,7 @@ lambda_slash_no_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_slash_no_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default+ '/' &':'")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15718,9 +16040,9 @@ lambda_slash_with_default_rule(Parser *p) asdl_seq * a; asdl_seq * b; if ( - (a = _loop0_70_rule(p)) // lambda_param_no_default* + (a = _loop0_72_rule(p)) // lambda_param_no_default* && - (b = _loop1_73_rule(p)) // lambda_param_with_default+ + (b = _loop1_75_rule(p)) // lambda_param_with_default+ && (_literal = _PyPegen_expect_token(p, 17)) // token='/' && @@ -15729,7 +16051,7 @@ lambda_slash_with_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_slash_with_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default* lambda_param_with_default+ '/' ','")); _res = _PyPegen_slash_with_default ( p , ( asdl_arg_seq* ) a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15750,9 +16072,9 @@ lambda_slash_with_default_rule(Parser *p) asdl_seq * a; asdl_seq * b; if ( - (a = _loop0_70_rule(p)) // lambda_param_no_default* + (a = _loop0_72_rule(p)) // lambda_param_no_default* && - (b = _loop1_73_rule(p)) // lambda_param_with_default+ + (b = _loop1_75_rule(p)) // lambda_param_with_default+ && (_literal = _PyPegen_expect_token(p, 17)) // token='/' && @@ -15761,7 +16083,7 @@ lambda_slash_with_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_slash_with_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default* lambda_param_with_default+ '/' &':'")); _res = _PyPegen_slash_with_default ( p , ( asdl_arg_seq* ) a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15829,14 +16151,14 @@ lambda_star_etc_rule(Parser *p) && (a = lambda_param_no_default_rule(p)) // lambda_param_no_default && - (b = _loop0_74_rule(p)) // lambda_param_maybe_default* + (b = _loop0_76_rule(p)) // lambda_param_maybe_default* && (c = lambda_kwds_rule(p), !p->error_indicator) // lambda_kwds? ) { D(fprintf(stderr, "%*c+ lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' lambda_param_no_default lambda_param_maybe_default* lambda_kwds?")); _res = _PyPegen_star_etc ( p , a , b , c ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15862,14 +16184,14 @@ lambda_star_etc_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 12)) // token=',' && - (b = _loop1_75_rule(p)) // lambda_param_maybe_default+ + (b = _loop1_77_rule(p)) // lambda_param_maybe_default+ && (c = lambda_kwds_rule(p), !p->error_indicator) // lambda_kwds? ) { D(fprintf(stderr, "%*c+ lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' ',' lambda_param_maybe_default+ lambda_kwds?")); _res = _PyPegen_star_etc ( p , NULL , b , c ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15893,7 +16215,7 @@ lambda_star_etc_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_kwds")); _res = _PyPegen_star_etc ( p , NULL , NULL , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -15958,7 +16280,7 @@ lambda_kwds_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' lambda_param_no_default")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16004,7 +16326,7 @@ lambda_param_no_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_param_no_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param ','")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16030,7 +16352,7 @@ lambda_param_no_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_param_no_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param &':'")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16079,7 +16401,7 @@ lambda_param_with_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_param_with_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param default ','")); _res = _PyPegen_name_default_pair ( p , a , c , NULL ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16108,7 +16430,7 @@ lambda_param_with_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_param_with_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param default &':'")); _res = _PyPegen_name_default_pair ( p , a , c , NULL ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16157,7 +16479,7 @@ lambda_param_maybe_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_param_maybe_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param default? ','")); _res = _PyPegen_name_default_pair ( p , a , c , NULL ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16186,7 +16508,7 @@ lambda_param_maybe_default_rule(Parser *p) { D(fprintf(stderr, "%*c+ lambda_param_maybe_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param default? &':'")); _res = _PyPegen_name_default_pair ( p , a , c , NULL ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16247,7 +16569,7 @@ lambda_param_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_arg ( a -> v . Name . id , NULL , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16309,7 +16631,7 @@ fstring_middle_rule(Parser *p) { D(fprintf(stderr, "%*c+ fstring_middle[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "FSTRING_MIDDLE")); _res = _PyPegen_constant_from_token ( p , t ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16387,7 +16709,7 @@ fstring_replacement_field_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_formatted_value ( p , a , debug_expr , conversion , format , rbrace , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16452,7 +16774,7 @@ fstring_conversion_rule(Parser *p) { D(fprintf(stderr, "%*c+ fstring_conversion[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"!\" NAME")); _res = _PyPegen_check_fstring_conversion ( p , conv_token , conv ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16502,7 +16824,7 @@ fstring_full_format_spec_rule(Parser *p) if ( (colon = _PyPegen_expect_token(p, 11)) // token=':' && - (spec = _loop0_76_rule(p)) // fstring_format_spec* + (spec = _loop0_78_rule(p)) // fstring_format_spec* ) { D(fprintf(stderr, "%*c+ fstring_full_format_spec[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' fstring_format_spec*")); @@ -16516,7 +16838,7 @@ fstring_full_format_spec_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_setup_full_format_spec ( p , colon , ( asdl_expr_seq* ) spec , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16559,7 +16881,7 @@ fstring_format_spec_rule(Parser *p) { D(fprintf(stderr, "%*c+ fstring_format_spec[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "FSTRING_MIDDLE")); _res = _PyPegen_decoded_constant_from_token ( p , t ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16620,14 +16942,14 @@ fstring_rule(Parser *p) if ( (a = _PyPegen_expect_token(p, FSTRING_START)) // token='FSTRING_START' && - (b = _loop0_77_rule(p)) // fstring_middle* + (b = _loop0_79_rule(p)) // fstring_middle* && (c = _PyPegen_expect_token(p, FSTRING_END)) // token='FSTRING_END' ) { D(fprintf(stderr, "%*c+ fstring[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "FSTRING_START fstring_middle* FSTRING_END")); _res = _PyPegen_joined_str ( p , a , ( asdl_expr_seq* ) b , c ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16705,7 +17027,7 @@ tstring_format_spec_replacement_field_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_formatted_value ( p , a , debug_expr , conversion , format , rbrace , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16767,7 +17089,7 @@ tstring_format_spec_rule(Parser *p) { D(fprintf(stderr, "%*c+ tstring_format_spec[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "TSTRING_MIDDLE")); _res = _PyPegen_decoded_constant_from_token ( p , t ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16836,7 +17158,7 @@ tstring_full_format_spec_rule(Parser *p) if ( (colon = _PyPegen_expect_token(p, 11)) // token=':' && - (spec = _loop0_78_rule(p)) // tstring_format_spec* + (spec = _loop0_80_rule(p)) // tstring_format_spec* ) { D(fprintf(stderr, "%*c+ tstring_full_format_spec[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' tstring_format_spec*")); @@ -16850,7 +17172,7 @@ tstring_full_format_spec_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_setup_full_format_spec ( p , colon , ( asdl_expr_seq* ) spec , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -16928,7 +17250,7 @@ tstring_replacement_field_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_interpolation ( p , a , debug_expr , conversion , format , rbrace , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17009,7 +17331,7 @@ tstring_middle_rule(Parser *p) { D(fprintf(stderr, "%*c+ tstring_middle[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "TSTRING_MIDDLE")); _res = _PyPegen_constant_from_token ( p , t ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17055,14 +17377,14 @@ tstring_rule(Parser *p) if ( (a = _PyPegen_expect_token(p, TSTRING_START)) // token='TSTRING_START' && - (b = _loop0_79_rule(p)) // tstring_middle* + (b = _loop0_81_rule(p)) // tstring_middle* && (c = _PyPegen_expect_token(p, TSTRING_END)) // token='TSTRING_END' ) { D(fprintf(stderr, "%*c+ tstring[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "TSTRING_START tstring_middle* TSTRING_END")); _res = CHECK_VERSION ( expr_ty , 14 , "t-strings are" , _PyPegen_template_str ( p , a , ( asdl_expr_seq* ) b , c ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17106,7 +17428,7 @@ string_rule(Parser *p) { D(fprintf(stderr, "%*c+ string[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "STRING")); _res = _PyPegen_constant_from_string ( p , s ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17176,7 +17498,7 @@ strings_rule(Parser *p) D(fprintf(stderr, "%*c> strings[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((fstring | string))+")); asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_loop1_80_rule(p)) // ((fstring | string))+ + (a = (asdl_expr_seq*)_loop1_82_rule(p)) // ((fstring | string))+ ) { D(fprintf(stderr, "%*c+ strings[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((fstring | string))+")); @@ -17190,7 +17512,7 @@ strings_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_concatenate_strings ( p , a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17209,7 +17531,7 @@ strings_rule(Parser *p) D(fprintf(stderr, "%*c> strings[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring+")); asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_loop1_81_rule(p)) // tstring+ + (a = (asdl_expr_seq*)_loop1_83_rule(p)) // tstring+ ) { D(fprintf(stderr, "%*c+ strings[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tstring+")); @@ -17223,7 +17545,7 @@ strings_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_concatenate_tstrings ( p , a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17241,7 +17563,7 @@ strings_rule(Parser *p) return _res; } -// list: '[' star_named_expressions? ']' +// list: '[' star_named_expressions_sequence? ']' static expr_ty list_rule(Parser *p) { @@ -17263,24 +17585,24 @@ list_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro - { // '[' star_named_expressions? ']' + { // '[' star_named_expressions_sequence? ']' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> list[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'[' star_named_expressions? ']'")); + D(fprintf(stderr, "%*c> list[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'[' star_named_expressions_sequence? ']'")); Token * _literal; Token * _literal_1; void *a; if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' && - (a = star_named_expressions_rule(p), !p->error_indicator) // star_named_expressions? + (a = star_named_expressions_sequence_rule(p), !p->error_indicator) // star_named_expressions_sequence? && (_literal_1 = _PyPegen_expect_token(p, 10)) // token=']' ) { - D(fprintf(stderr, "%*c+ list[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'[' star_named_expressions? ']'")); + D(fprintf(stderr, "%*c+ list[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'[' star_named_expressions_sequence? ']'")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -17291,7 +17613,7 @@ list_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_List ( a , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17300,7 +17622,7 @@ list_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s list[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'[' star_named_expressions? ']'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'[' star_named_expressions_sequence? ']'")); } _res = NULL; done: @@ -17308,7 +17630,7 @@ list_rule(Parser *p) return _res; } -// tuple: '(' [star_named_expression ',' star_named_expressions?] ')' +// tuple: '(' [star_named_expression_sequence ',' star_named_expressions_sequence?] ')' static expr_ty tuple_rule(Parser *p) { @@ -17330,24 +17652,24 @@ tuple_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro - { // '(' [star_named_expression ',' star_named_expressions?] ')' + { // '(' [star_named_expression_sequence ',' star_named_expressions_sequence?] ')' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> tuple[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' [star_named_expression ',' star_named_expressions?] ')'")); + D(fprintf(stderr, "%*c> tuple[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' [star_named_expression_sequence ',' star_named_expressions_sequence?] ')'")); Token * _literal; Token * _literal_1; void *a; if ( (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (a = _tmp_82_rule(p), !p->error_indicator) // [star_named_expression ',' star_named_expressions?] + (a = _tmp_84_rule(p), !p->error_indicator) // [star_named_expression_sequence ',' star_named_expressions_sequence?] && (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ tuple[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' [star_named_expression ',' star_named_expressions?] ')'")); + D(fprintf(stderr, "%*c+ tuple[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' [star_named_expression_sequence ',' star_named_expressions_sequence?] ')'")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -17358,7 +17680,7 @@ tuple_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Tuple ( a , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17367,7 +17689,7 @@ tuple_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s tuple[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' [star_named_expression ',' star_named_expressions?] ')'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' [star_named_expression_sequence ',' star_named_expressions_sequence?] ')'")); } _res = NULL; done: @@ -17375,7 +17697,7 @@ tuple_rule(Parser *p) return _res; } -// set: '{' star_named_expressions '}' +// set: '{' star_named_expressions_sequence '}' static expr_ty set_rule(Parser *p) { @@ -17397,24 +17719,24 @@ set_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro - { // '{' star_named_expressions '}' + { // '{' star_named_expressions_sequence '}' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> set[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' star_named_expressions '}'")); + D(fprintf(stderr, "%*c> set[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' star_named_expressions_sequence '}'")); Token * _literal; Token * _literal_1; asdl_expr_seq* a; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' && - (a = star_named_expressions_rule(p)) // star_named_expressions + (a = star_named_expressions_sequence_rule(p)) // star_named_expressions_sequence && (_literal_1 = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ set[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' star_named_expressions '}'")); + D(fprintf(stderr, "%*c+ set[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' star_named_expressions_sequence '}'")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -17425,7 +17747,7 @@ set_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Set ( a , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17434,7 +17756,7 @@ set_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s set[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' star_named_expressions '}'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' star_named_expressions_sequence '}'")); } _res = NULL; done: @@ -17492,7 +17814,7 @@ dict_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Dict ( CHECK ( asdl_expr_seq* , _PyPegen_get_keys ( p , a ) ) , CHECK ( asdl_expr_seq* , _PyPegen_get_values ( p , a ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17557,14 +17879,14 @@ double_starred_kvpairs_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings asdl_seq * a; if ( - (a = _gather_84_rule(p)) // ','.double_starred_kvpair+ + (a = _gather_86_rule(p)) // ','.double_starred_kvpair+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) { D(fprintf(stderr, "%*c+ double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ','?")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17610,7 +17932,7 @@ double_starred_kvpair_rule(Parser *p) { D(fprintf(stderr, "%*c+ double_starred_kvpair[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' bitwise_or")); _res = _PyPegen_key_value_pair ( p , NULL , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17678,7 +18000,7 @@ kvpair_rule(Parser *p) { D(fprintf(stderr, "%*c+ kvpair[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' expression")); _res = _PyPegen_key_value_pair ( p , a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17716,12 +18038,12 @@ for_if_clauses_rule(Parser *p) D(fprintf(stderr, "%*c> for_if_clauses[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause+")); asdl_comprehension_seq* a; if ( - (a = (asdl_comprehension_seq*)_loop1_85_rule(p)) // for_if_clause+ + (a = (asdl_comprehension_seq*)_loop1_87_rule(p)) // for_if_clause+ ) { D(fprintf(stderr, "%*c+ for_if_clauses[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "for_if_clause+")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17769,24 +18091,24 @@ for_if_clause_rule(Parser *p) expr_ty b; asdl_expr_seq* c; if ( - (_keyword = _PyPegen_expect_token(p, 703)) // token='async' + (_keyword = _PyPegen_expect_token(p, 714)) // token='async' && - (_keyword_1 = _PyPegen_expect_token(p, 699)) // token='for' + (_keyword_1 = _PyPegen_expect_token(p, 710)) // token='for' && (a = star_targets_rule(p)) // star_targets && - (_keyword_2 = _PyPegen_expect_token(p, 700)) // token='in' + (_keyword_2 = _PyPegen_expect_token(p, 711)) // token='in' && (_cut_var = 1) && (b = disjunction_rule(p)) // disjunction && - (c = (asdl_expr_seq*)_loop0_86_rule(p)) // (('if' disjunction))* + (c = (asdl_expr_seq*)_loop0_88_rule(p)) // (('if' disjunction))* ) { D(fprintf(stderr, "%*c+ for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async' 'for' star_targets 'in' ~ disjunction (('if' disjunction))*")); _res = CHECK_VERSION ( comprehension_ty , 6 , "Async comprehensions are" , _PyAST_comprehension ( a , b , c , 1 , p -> arena ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17814,22 +18136,22 @@ for_if_clause_rule(Parser *p) expr_ty b; asdl_expr_seq* c; if ( - (_keyword = _PyPegen_expect_token(p, 699)) // token='for' + (_keyword = _PyPegen_expect_token(p, 710)) // token='for' && (a = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 700)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 711)) // token='in' && (_cut_var = 1) && (b = disjunction_rule(p)) // disjunction && - (c = (asdl_expr_seq*)_loop0_86_rule(p)) // (('if' disjunction))* + (c = (asdl_expr_seq*)_loop0_88_rule(p)) // (('if' disjunction))* ) { D(fprintf(stderr, "%*c+ for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'for' star_targets 'in' ~ disjunction (('if' disjunction))*")); _res = _PyAST_comprehension ( a , b , c , 0 , p -> arena ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17888,7 +18210,7 @@ for_if_clause_rule(Parser *p) return _res; } -// listcomp: '[' named_expression for_if_clauses ']' | invalid_comprehension +// listcomp: '[' star_named_expression for_if_clauses ']' | invalid_comprehension static expr_ty listcomp_rule(Parser *p) { @@ -17910,12 +18232,12 @@ listcomp_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro - { // '[' named_expression for_if_clauses ']' + { // '[' star_named_expression for_if_clauses ']' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> listcomp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'[' named_expression for_if_clauses ']'")); + D(fprintf(stderr, "%*c> listcomp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'[' star_named_expression for_if_clauses ']'")); Token * _literal; Token * _literal_1; expr_ty a; @@ -17923,14 +18245,14 @@ listcomp_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' && - (a = named_expression_rule(p)) // named_expression + (a = star_named_expression_rule(p)) // star_named_expression && (b = for_if_clauses_rule(p)) // for_if_clauses && (_literal_1 = _PyPegen_expect_token(p, 10)) // token=']' ) { - D(fprintf(stderr, "%*c+ listcomp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'[' named_expression for_if_clauses ']'")); + D(fprintf(stderr, "%*c+ listcomp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'[' star_named_expression for_if_clauses ']'")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -17941,7 +18263,7 @@ listcomp_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_ListComp ( a , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -17950,7 +18272,7 @@ listcomp_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s listcomp[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'[' named_expression for_if_clauses ']'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'[' star_named_expression for_if_clauses ']'")); } if (p->call_invalid_rules) { // invalid_comprehension if (p->error_indicator) { @@ -17977,7 +18299,7 @@ listcomp_rule(Parser *p) return _res; } -// setcomp: '{' named_expression for_if_clauses '}' | invalid_comprehension +// setcomp: '{' star_named_expression for_if_clauses '}' | invalid_comprehension static expr_ty setcomp_rule(Parser *p) { @@ -17999,12 +18321,12 @@ setcomp_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro - { // '{' named_expression for_if_clauses '}' + { // '{' star_named_expression for_if_clauses '}' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> setcomp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' named_expression for_if_clauses '}'")); + D(fprintf(stderr, "%*c> setcomp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' star_named_expression for_if_clauses '}'")); Token * _literal; Token * _literal_1; expr_ty a; @@ -18012,14 +18334,14 @@ setcomp_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' && - (a = named_expression_rule(p)) // named_expression + (a = star_named_expression_rule(p)) // star_named_expression && (b = for_if_clauses_rule(p)) // for_if_clauses && (_literal_1 = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ setcomp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' named_expression for_if_clauses '}'")); + D(fprintf(stderr, "%*c+ setcomp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' star_named_expression for_if_clauses '}'")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -18030,7 +18352,7 @@ setcomp_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_SetComp ( a , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18039,7 +18361,7 @@ setcomp_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s setcomp[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' named_expression for_if_clauses '}'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' star_named_expression for_if_clauses '}'")); } if (p->call_invalid_rules) { // invalid_comprehension if (p->error_indicator) { @@ -18067,7 +18389,7 @@ setcomp_rule(Parser *p) } // genexp: -// | '(' (assignment_expression | expression !':=') for_if_clauses ')' +// | '(' (assignment_expression | expression !':=' | starred_expression) for_if_clauses ')' // | invalid_comprehension static expr_ty genexp_rule(Parser *p) @@ -18090,12 +18412,12 @@ genexp_rule(Parser *p) UNUSED(_start_lineno); // Only used by EXTRA macro int _start_col_offset = p->tokens[_mark]->col_offset; UNUSED(_start_col_offset); // Only used by EXTRA macro - { // '(' (assignment_expression | expression !':=') for_if_clauses ')' + { // '(' (assignment_expression | expression !':=' | starred_expression) for_if_clauses ')' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> genexp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' (assignment_expression | expression !':=') for_if_clauses ')'")); + D(fprintf(stderr, "%*c> genexp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' (assignment_expression | expression !':=' | starred_expression) for_if_clauses ')'")); Token * _literal; Token * _literal_1; void *a; @@ -18103,14 +18425,14 @@ genexp_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (a = _tmp_87_rule(p)) // assignment_expression | expression !':=' + (a = _tmp_89_rule(p)) // assignment_expression | expression !':=' | starred_expression && (b = for_if_clauses_rule(p)) // for_if_clauses && (_literal_1 = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ genexp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' (assignment_expression | expression !':=') for_if_clauses ')'")); + D(fprintf(stderr, "%*c+ genexp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' (assignment_expression | expression !':=' | starred_expression) for_if_clauses ')'")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -18121,7 +18443,7 @@ genexp_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_GeneratorExp ( a , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18130,7 +18452,7 @@ genexp_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s genexp[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' (assignment_expression | expression !':=') for_if_clauses ')'")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' (assignment_expression | expression !':=' | starred_expression) for_if_clauses ')'")); } if (p->call_invalid_rules) { // invalid_comprehension if (p->error_indicator) { @@ -18157,7 +18479,7 @@ genexp_rule(Parser *p) return _res; } -// dictcomp: '{' kvpair for_if_clauses '}' | invalid_dict_comprehension +// dictcomp: '{' kvpair for_if_clauses '}' | '{' '**' expression for_if_clauses '}' static expr_ty dictcomp_rule(Parser *p) { @@ -18210,7 +18532,7 @@ dictcomp_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_DictComp ( a -> key , a -> value , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18221,24 +18543,50 @@ dictcomp_rule(Parser *p) D(fprintf(stderr, "%*c%s dictcomp[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' kvpair for_if_clauses '}'")); } - if (p->call_invalid_rules) { // invalid_dict_comprehension + { // '{' '**' expression for_if_clauses '}' if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> dictcomp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_dict_comprehension")); - void *invalid_dict_comprehension_var; + D(fprintf(stderr, "%*c> dictcomp[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '**' expression for_if_clauses '}'")); + Token * _literal; + Token * _literal_1; + Token * _literal_2; + expr_ty a; + asdl_comprehension_seq* b; if ( - (invalid_dict_comprehension_var = invalid_dict_comprehension_rule(p)) // invalid_dict_comprehension + (_literal = _PyPegen_expect_token(p, 25)) // token='{' + && + (_literal_1 = _PyPegen_expect_token(p, 35)) // token='**' + && + (a = expression_rule(p)) // expression + && + (b = for_if_clauses_rule(p)) // for_if_clauses + && + (_literal_2 = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ dictcomp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_dict_comprehension")); - _res = invalid_dict_comprehension_var; + D(fprintf(stderr, "%*c+ dictcomp[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '**' expression for_if_clauses '}'")); + Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); + if (_token == NULL) { + p->level--; + return NULL; + } + int _end_lineno = _token->end_lineno; + UNUSED(_end_lineno); // Only used by EXTRA macro + int _end_col_offset = _token->end_col_offset; + UNUSED(_end_col_offset); // Only used by EXTRA macro + _res = _PyAST_DictComp ( a , NULL , b , EXTRA ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s dictcomp[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_dict_comprehension")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' '**' expression for_if_clauses '}'")); } _res = NULL; done: @@ -18282,7 +18630,7 @@ arguments_rule(Parser *p) { D(fprintf(stderr, "%*c+ arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ','? &')'")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18352,9 +18700,9 @@ args_rule(Parser *p) asdl_expr_seq* a; void *b; if ( - (a = (asdl_expr_seq*)_gather_89_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ + (a = (asdl_expr_seq*)_gather_91_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ && - (b = _tmp_90_rule(p), !p->error_indicator) // [',' kwargs] + (b = _tmp_92_rule(p), !p->error_indicator) // [',' kwargs] ) { D(fprintf(stderr, "%*c+ args[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ [',' kwargs]")); @@ -18368,7 +18716,7 @@ args_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_collect_call_seqs ( p , a , b , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18401,7 +18749,7 @@ args_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Call ( _PyPegen_dummy_name ( p ) , CHECK_NULL_ALLOWED ( asdl_expr_seq* , _PyPegen_seq_extract_starred_exprs ( p , a ) ) , CHECK_NULL_ALLOWED ( asdl_keyword_seq* , _PyPegen_seq_delete_starred_exprs ( p , a ) ) , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18444,16 +18792,16 @@ kwargs_rule(Parser *p) asdl_seq * a; asdl_seq * b; if ( - (a = _gather_92_rule(p)) // ','.kwarg_or_starred+ + (a = _gather_94_rule(p)) // ','.kwarg_or_starred+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (b = _gather_94_rule(p)) // ','.kwarg_or_double_starred+ + (b = _gather_96_rule(p)) // ','.kwarg_or_double_starred+ ) { D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+ ',' ','.kwarg_or_double_starred+")); _res = _PyPegen_join_sequences ( p , a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18470,13 +18818,13 @@ kwargs_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> kwargs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+")); - asdl_seq * _gather_92_var; + asdl_seq * _gather_94_var; if ( - (_gather_92_var = _gather_92_rule(p)) // ','.kwarg_or_starred+ + (_gather_94_var = _gather_94_rule(p)) // ','.kwarg_or_starred+ ) { D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_starred+")); - _res = _gather_92_var; + _res = _gather_94_var; goto done; } p->mark = _mark; @@ -18489,13 +18837,13 @@ kwargs_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> kwargs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_double_starred+")); - asdl_seq * _gather_94_var; + asdl_seq * _gather_96_var; if ( - (_gather_94_var = _gather_94_rule(p)) // ','.kwarg_or_double_starred+ + (_gather_96_var = _gather_96_rule(p)) // ','.kwarg_or_double_starred+ ) { D(fprintf(stderr, "%*c+ kwargs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.kwarg_or_double_starred+")); - _res = _gather_94_var; + _res = _gather_96_var; goto done; } p->mark = _mark; @@ -18577,7 +18925,7 @@ starred_expression_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Starred ( a , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18682,7 +19030,7 @@ kwarg_or_starred_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_keyword_or_starred ( p , CHECK ( keyword_ty , _PyAST_keyword ( a -> v . Name . id , b , EXTRA ) ) , 1 ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18706,7 +19054,7 @@ kwarg_or_starred_rule(Parser *p) { D(fprintf(stderr, "%*c+ kwarg_or_starred[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = _PyPegen_keyword_or_starred ( p , a , 0 ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18792,7 +19140,7 @@ kwarg_or_double_starred_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_keyword_or_starred ( p , CHECK ( keyword_ty , _PyAST_keyword ( a -> v . Name . id , b , EXTRA ) ) , 1 ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18828,7 +19176,7 @@ kwarg_or_double_starred_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyPegen_keyword_or_starred ( p , CHECK ( keyword_ty , _PyAST_keyword ( NULL , a , EXTRA ) ) , 1 ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18882,7 +19230,7 @@ star_targets_rule(Parser *p) { D(fprintf(stderr, "%*c+ star_targets[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target !','")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18906,7 +19254,7 @@ star_targets_rule(Parser *p) if ( (a = star_target_rule(p)) // star_target && - (b = _loop0_95_rule(p)) // ((',' star_target))* + (b = _loop0_97_rule(p)) // ((',' star_target))* && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) @@ -18922,7 +19270,7 @@ star_targets_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Tuple ( CHECK ( asdl_expr_seq* , _PyPegen_seq_insert_in_front ( p , a , b ) ) , Store , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -18962,14 +19310,14 @@ star_targets_list_seq_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_gather_97_rule(p)) // ','.star_target+ + (a = (asdl_expr_seq*)_gather_99_rule(p)) // ','.star_target+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) { D(fprintf(stderr, "%*c+ star_targets_list_seq[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.star_target+ ','?")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19012,14 +19360,14 @@ star_targets_tuple_seq_rule(Parser *p) if ( (a = star_target_rule(p)) // star_target && - (b = _loop1_98_rule(p)) // ((',' star_target))+ + (b = _loop1_100_rule(p)) // ((',' star_target))+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) { D(fprintf(stderr, "%*c+ star_targets_tuple_seq[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target ((',' star_target))+ ','?")); _res = ( asdl_expr_seq* ) _PyPegen_seq_insert_in_front ( p , a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19046,7 +19394,7 @@ star_targets_tuple_seq_rule(Parser *p) { D(fprintf(stderr, "%*c+ star_targets_tuple_seq[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target ','")); _res = ( asdl_expr_seq* ) _PyPegen_singleton_seq ( p , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19100,7 +19448,7 @@ star_target_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (a = _tmp_99_rule(p)) // !'*' star_target + (a = _tmp_101_rule(p)) // !'*' star_target ) { D(fprintf(stderr, "%*c+ star_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (!'*' star_target)")); @@ -19114,7 +19462,7 @@ star_target_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Starred ( CHECK ( expr_ty , _PyPegen_set_expr_context ( p , a , Store ) ) , Store , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19210,7 +19558,7 @@ target_with_star_atom_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Attribute ( a , b -> v . Name . id , Store , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19254,7 +19602,7 @@ target_with_star_atom_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Subscript ( a , b , Store , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19330,7 +19678,7 @@ star_atom_rule(Parser *p) { D(fprintf(stderr, "%*c+ star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME")); _res = _PyPegen_set_expr_context ( p , a , Store ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19360,7 +19708,7 @@ star_atom_rule(Parser *p) { D(fprintf(stderr, "%*c+ star_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' target_with_star_atom ')'")); _res = _PyPegen_set_expr_context ( p , a , Store ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19399,7 +19747,7 @@ star_atom_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Tuple ( a , Store , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19438,7 +19786,7 @@ star_atom_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_List ( a , Store , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19500,7 +19848,7 @@ single_target_rule(Parser *p) { D(fprintf(stderr, "%*c+ single_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME")); _res = _PyPegen_set_expr_context ( p , a , Store ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19530,7 +19878,7 @@ single_target_rule(Parser *p) { D(fprintf(stderr, "%*c+ single_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' single_target ')'")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19601,7 +19949,7 @@ single_subscript_attribute_target_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Attribute ( a , b -> v . Name . id , Store , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19645,7 +19993,7 @@ single_subscript_attribute_target_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Subscript ( a , b , Store , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19755,7 +20103,7 @@ t_primary_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Attribute ( a , b -> v . Name . id , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19799,7 +20147,7 @@ t_primary_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Subscript ( a , b , Load , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19837,7 +20185,7 @@ t_primary_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Call ( a , CHECK ( asdl_expr_seq* , ( asdl_expr_seq* ) _PyPegen_singleton_seq ( p , b ) ) , NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19881,7 +20229,7 @@ t_primary_raw(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Call ( a , ( b ) ? ( ( expr_ty ) b ) -> v . Call . args : NULL , ( b ) ? ( ( expr_ty ) b ) -> v . Call . keywords : NULL , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -19907,7 +20255,7 @@ t_primary_raw(Parser *p) { D(fprintf(stderr, "%*c+ t_primary[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "atom &t_lookahead")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20023,14 +20371,14 @@ del_targets_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_gather_101_rule(p)) // ','.del_target+ + (a = (asdl_expr_seq*)_gather_103_rule(p)) // ','.del_target+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) { D(fprintf(stderr, "%*c+ del_targets[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.del_target+ ','?")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20106,7 +20454,7 @@ del_target_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Attribute ( a , b -> v . Name . id , Del , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20150,7 +20498,7 @@ del_target_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Subscript ( a , b , Del , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20222,7 +20570,7 @@ del_t_atom_rule(Parser *p) { D(fprintf(stderr, "%*c+ del_t_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME")); _res = _PyPegen_set_expr_context ( p , a , Del ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20252,7 +20600,7 @@ del_t_atom_rule(Parser *p) { D(fprintf(stderr, "%*c+ del_t_atom[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' del_target ')'")); _res = _PyPegen_set_expr_context ( p , a , Del ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20291,7 +20639,7 @@ del_t_atom_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_Tuple ( a , Del , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20330,7 +20678,7 @@ del_t_atom_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_List ( a , Del , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20381,7 +20729,7 @@ type_expressions_rule(Parser *p) expr_ty b; expr_ty c; if ( - (a = _gather_103_rule(p)) // ','.expression+ + (a = _gather_105_rule(p)) // ','.expression+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && @@ -20398,7 +20746,7 @@ type_expressions_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.expression+ ',' '*' expression ',' '**' expression")); _res = ( asdl_expr_seq* ) _PyPegen_seq_append_to_end ( p , CHECK ( asdl_seq* , _PyPegen_seq_append_to_end ( p , a , b ) ) , c ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20420,7 +20768,7 @@ type_expressions_rule(Parser *p) asdl_seq * a; expr_ty b; if ( - (a = _gather_103_rule(p)) // ','.expression+ + (a = _gather_105_rule(p)) // ','.expression+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && @@ -20431,7 +20779,7 @@ type_expressions_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.expression+ ',' '*' expression")); _res = ( asdl_expr_seq* ) _PyPegen_seq_append_to_end ( p , a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20453,7 +20801,7 @@ type_expressions_rule(Parser *p) asdl_seq * a; expr_ty b; if ( - (a = _gather_103_rule(p)) // ','.expression+ + (a = _gather_105_rule(p)) // ','.expression+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && @@ -20464,7 +20812,7 @@ type_expressions_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.expression+ ',' '**' expression")); _res = ( asdl_expr_seq* ) _PyPegen_seq_append_to_end ( p , a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20500,7 +20848,7 @@ type_expressions_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' expression ',' '**' expression")); _res = ( asdl_expr_seq* ) _PyPegen_seq_append_to_end ( p , CHECK ( asdl_seq* , _PyPegen_singleton_seq ( p , a ) ) , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20527,7 +20875,7 @@ type_expressions_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' expression")); _res = ( asdl_expr_seq* ) _PyPegen_singleton_seq ( p , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20554,7 +20902,7 @@ type_expressions_rule(Parser *p) { D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' expression")); _res = ( asdl_expr_seq* ) _PyPegen_singleton_seq ( p , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20573,12 +20921,12 @@ type_expressions_rule(Parser *p) D(fprintf(stderr, "%*c> type_expressions[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.expression+")); asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_gather_103_rule(p)) // ','.expression+ + (a = (asdl_expr_seq*)_gather_105_rule(p)) // ','.expression+ ) { D(fprintf(stderr, "%*c+ type_expressions[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.expression+")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20624,12 +20972,12 @@ func_type_comment_rule(Parser *p) && (t = _PyPegen_expect_token(p, TYPE_COMMENT)) // token='TYPE_COMMENT' && - _PyPegen_lookahead(1, _tmp_104_rule, p) + _PyPegen_lookahead(1, _tmp_106_rule, p) ) { D(fprintf(stderr, "%*c+ func_type_comment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE TYPE_COMMENT &(NEWLINE INDENT)")); _res = t; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20710,20 +21058,20 @@ invalid_arguments_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_arguments[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' ','.(starred_expression !'=')+")); - asdl_seq * _gather_107_var; - void *_tmp_105_var; + asdl_seq * _gather_109_var; + void *_tmp_107_var; Token * a; if ( - (_tmp_105_var = _tmp_105_rule(p)) // (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs + (_tmp_107_var = _tmp_107_rule(p)) // (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs && (a = _PyPegen_expect_token(p, 12)) // token=',' && - (_gather_107_var = _gather_107_rule(p)) // ','.(starred_expression !'=')+ + (_gather_109_var = _gather_109_rule(p)) // ','.(starred_expression !'=')+ ) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) | kwargs) ',' ','.(starred_expression !'=')+")); _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "iterable argument unpacking follows keyword argument unpacking" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20752,12 +21100,12 @@ invalid_arguments_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_opt_var = _tmp_108_rule(p), !p->error_indicator) // [args | expression for_if_clauses] + (_opt_var = _tmp_110_rule(p), !p->error_indicator) // [args | expression for_if_clauses] ) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , _PyPegen_get_last_comprehension_item ( PyPegen_last_item ( b , comprehension_ty ) ) , "Generator expression must be parenthesized" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20790,7 +21138,7 @@ invalid_arguments_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '=' expression for_if_clauses")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "invalid syntax. Maybe you meant '==' or ':=' instead of '='?" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20812,18 +21160,18 @@ invalid_arguments_rule(Parser *p) expr_ty a; Token * b; if ( - (_opt_var = _tmp_109_rule(p), !p->error_indicator) // [(args ',')] + (_opt_var = _tmp_111_rule(p), !p->error_indicator) // [(args ',')] && (a = _PyPegen_name_token(p)) // NAME && (b = _PyPegen_expect_token(p, 22)) // token='=' && - _PyPegen_lookahead(1, _tmp_110_rule, p) + _PyPegen_lookahead(1, _tmp_112_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "[(args ',')] NAME '=' &(',' | ')')")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "expected argument value expression" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20850,7 +21198,7 @@ invalid_arguments_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args for_if_clauses")); _res = _PyPegen_nonparen_genexp_in_call ( p , a , b ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20883,7 +21231,7 @@ invalid_arguments_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , _PyPegen_get_last_comprehension_item ( PyPegen_last_item ( b , comprehension_ty ) ) , "Generator expression must be parenthesized" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20913,7 +21261,7 @@ invalid_arguments_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' args")); _res = _PyPegen_arguments_parsing_error ( p , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20956,14 +21304,14 @@ invalid_kwarg_rule(Parser *p) Token* a; Token * b; if ( - (a = (Token*)_tmp_111_rule(p)) // 'True' | 'False' | 'None' + (a = (Token*)_tmp_113_rule(p)) // 'True' | 'False' | 'None' && (b = _PyPegen_expect_token(p, 22)) // token='=' ) { D(fprintf(stderr, "%*c+ invalid_kwarg[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "('True' | 'False' | 'None') '='")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot assign to %s" , PyBytes_AS_STRING ( a -> bytes ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -20996,7 +21344,7 @@ invalid_kwarg_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_kwarg[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '=' expression for_if_clauses")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "invalid syntax. Maybe you meant '==' or ':=' instead of '='?" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21016,7 +21364,7 @@ invalid_kwarg_rule(Parser *p) expr_ty a; Token * b; if ( - _PyPegen_lookahead(0, _tmp_112_rule, p) + _PyPegen_lookahead(0, _tmp_114_rule, p) && (a = expression_rule(p)) // expression && @@ -21025,7 +21373,7 @@ invalid_kwarg_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_kwarg[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!(NAME '=') expression '='")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "expression cannot contain assignment, perhaps you meant \"==\"?" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21058,7 +21406,7 @@ invalid_kwarg_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_kwarg[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' expression '=' expression")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot assign to keyword argument unpacking" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21119,11 +21467,11 @@ expression_without_invalid_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 687)) // token='if' + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' && (b = disjunction_rule(p)) // disjunction && - (_keyword_1 = _PyPegen_expect_token(p, 691)) // token='else' + (_keyword_1 = _PyPegen_expect_token(p, 702)) // token='else' && (c = expression_rule(p)) // expression ) @@ -21140,7 +21488,7 @@ expression_without_invalid_rule(Parser *p) int _end_col_offset = _token->end_col_offset; UNUSED(_end_col_offset); // Only used by EXTRA macro _res = _PyAST_IfExp ( b , a , c , EXTRA ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->call_invalid_rules = _prev_call_invalid; p->level--; @@ -21230,7 +21578,7 @@ invalid_legacy_expression_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_legacy_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME !'(' star_expressions")); _res = _PyPegen_check_legacy_stmt ( p , a ) ? RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "Missing parentheses in call to '%U'. Did you mean %U(...)?" , a -> v . Name . id , a -> v . Name . id ) : NULL; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21282,7 +21630,7 @@ invalid_type_param_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_type_param[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' NAME ':' expression")); _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( colon , e -> kind == Tuple_kind ? "cannot use constraints with TypeVarTuple" : "cannot use bound with TypeVarTuple" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21315,7 +21663,7 @@ invalid_type_param_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_type_param[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' NAME ':' expression")); _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( colon , e -> kind == Tuple_kind ? "cannot use constraints with ParamSpec" : "cannot use bound with ParamSpec" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21364,14 +21712,14 @@ invalid_expression_rule(Parser *p) if ( (string_var = _PyPegen_string_token(p)) // STRING && - (a = _loop1_113_rule(p)) // ((!STRING expression_without_invalid))+ + (a = _loop1_115_rule(p)) // ((!STRING expression_without_invalid))+ && (string_var_1 = _PyPegen_string_token(p)) // STRING ) { D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "STRING ((!STRING expression_without_invalid))+ STRING")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( PyPegen_first_item ( a , expr_ty ) , PyPegen_last_item ( a , expr_ty ) , "invalid syntax. Is this intended to be part of the string?" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21391,7 +21739,7 @@ invalid_expression_rule(Parser *p) expr_ty a; expr_ty b; if ( - _PyPegen_lookahead(0, _tmp_114_rule, p) + _PyPegen_lookahead(0, _tmp_116_rule, p) && (a = disjunction_rule(p)) // disjunction && @@ -21399,8 +21747,8 @@ invalid_expression_rule(Parser *p) ) { D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!(NAME STRING | SOFT_KEYWORD) disjunction expression_without_invalid")); - _res = _PyPegen_check_legacy_stmt ( p , a ) ? NULL : p -> tokens [p -> mark - 1] -> level == 0 ? NULL : RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "invalid syntax. Perhaps you forgot a comma?" ); - if (_res == NULL && PyErr_Occurred()) { + _res = _PyPegen_raise_error_for_missing_comma ( p , a , b ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21423,16 +21771,16 @@ invalid_expression_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 687)) // token='if' + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' && (b = disjunction_rule(p)) // disjunction && - _PyPegen_lookahead(0, _tmp_115_rule, p) + _PyPegen_lookahead(0, _tmp_117_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction !('else' | ':')")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "expected 'else' after 'if' expression" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21456,18 +21804,18 @@ invalid_expression_rule(Parser *p) if ( (a = disjunction_rule(p)) // disjunction && - (_keyword = _PyPegen_expect_token(p, 687)) // token='if' + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' && (b = disjunction_rule(p)) // disjunction && - (_keyword_1 = _PyPegen_expect_token(p, 691)) // token='else' + (_keyword_1 = _PyPegen_expect_token(p, 702)) // token='else' && _PyPegen_lookahead_for_expr(0, expression_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction 'else' !expression")); _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "expected expression after 'else', but statement is given" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21490,20 +21838,20 @@ invalid_expression_rule(Parser *p) expr_ty b; stmt_ty c; if ( - (a = (stmt_ty)_tmp_116_rule(p)) // pass_stmt | break_stmt | continue_stmt + (a = (stmt_ty)_tmp_118_rule(p)) // pass_stmt | break_stmt | continue_stmt && - (_keyword = _PyPegen_expect_token(p, 687)) // token='if' + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' && (b = disjunction_rule(p)) // disjunction && - (_keyword_1 = _PyPegen_expect_token(p, 691)) // token='else' + (_keyword_1 = _PyPegen_expect_token(p, 702)) // token='else' && (c = simple_stmt_rule(p)) // simple_stmt ) { D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(pass_stmt | break_stmt | continue_stmt) 'if' disjunction 'else' simple_stmt")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "expected expression before 'if', but statement is given" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21536,7 +21884,7 @@ invalid_expression_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'lambda' lambda_params? ':' &FSTRING_MIDDLE")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "f-string: lambda expressions are not allowed without parentheses" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21569,7 +21917,7 @@ invalid_expression_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'lambda' lambda_params? ':' &TSTRING_MIDDLE")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "t-string: lambda expressions are not allowed without parentheses" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21586,6 +21934,99 @@ invalid_expression_rule(Parser *p) return _res; } +// invalid_if_expression: +// | disjunction 'if' disjunction 'else' '*' +// | disjunction 'if' disjunction 'else' '**' +static void * +invalid_if_expression_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // disjunction 'if' disjunction 'else' '*' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_if_expression[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction 'else' '*'")); + Token * _keyword; + Token * _keyword_1; + Token * a; + expr_ty b; + expr_ty disjunction_var; + if ( + (disjunction_var = disjunction_rule(p)) // disjunction + && + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' + && + (b = disjunction_rule(p)) // disjunction + && + (_keyword_1 = _PyPegen_expect_token(p, 702)) // token='else' + && + (a = _PyPegen_expect_token(p, 16)) // token='*' + ) + { + D(fprintf(stderr, "%*c+ invalid_if_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction 'else' '*'")); + _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot unpack only part of a conditional expression" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_if_expression[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "disjunction 'if' disjunction 'else' '*'")); + } + { // disjunction 'if' disjunction 'else' '**' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_if_expression[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction 'else' '**'")); + Token * _keyword; + Token * _keyword_1; + Token * a; + expr_ty b; + expr_ty disjunction_var; + if ( + (disjunction_var = disjunction_rule(p)) // disjunction + && + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' + && + (b = disjunction_rule(p)) // disjunction + && + (_keyword_1 = _PyPegen_expect_token(p, 702)) // token='else' + && + (a = _PyPegen_expect_token(p, 35)) // token='**' + ) + { + D(fprintf(stderr, "%*c+ invalid_if_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "disjunction 'if' disjunction 'else' '**'")); + _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use dict unpacking on only part of a conditional expression" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_if_expression[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "disjunction 'if' disjunction 'else' '**'")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // invalid_named_expression: // | expression ':=' expression // | NAME '=' bitwise_or !('=' | ':=') @@ -21625,7 +22066,7 @@ invalid_named_expression_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':=' expression")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use assignment expressions with %s" , _PyPegen_get_expr_name ( a ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21652,12 +22093,12 @@ invalid_named_expression_rule(Parser *p) && (b = bitwise_or_rule(p)) // bitwise_or && - _PyPegen_lookahead(0, _tmp_117_rule, p) + _PyPegen_lookahead(0, _tmp_119_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '=' bitwise_or !('=' | ':=')")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "invalid syntax. Maybe you meant '==' or ':=' instead of '='?" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21678,7 +22119,7 @@ invalid_named_expression_rule(Parser *p) Token * b; expr_ty bitwise_or_var; if ( - _PyPegen_lookahead(0, _tmp_118_rule, p) + _PyPegen_lookahead(0, _tmp_120_rule, p) && (a = bitwise_or_rule(p)) // bitwise_or && @@ -21686,12 +22127,12 @@ invalid_named_expression_rule(Parser *p) && (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or && - _PyPegen_lookahead(0, _tmp_117_rule, p) + _PyPegen_lookahead(0, _tmp_119_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!(list | tuple | genexp | 'True' | 'None' | 'False') bitwise_or '=' bitwise_or !('=' | ':=')")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot assign to %s here. Maybe you meant '==' instead of '='?" , _PyPegen_get_expr_name ( a ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21747,7 +22188,7 @@ invalid_assignment_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_ann_assign_target ':' expression")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "only single target (not %s) can be annotated" , _PyPegen_get_expr_name ( a ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21766,7 +22207,7 @@ invalid_assignment_rule(Parser *p) D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions* ':' expression")); Token * _literal; Token * _literal_1; - asdl_seq * _loop0_119_var; + asdl_seq * _loop0_121_var; expr_ty a; expr_ty expression_var; if ( @@ -21774,7 +22215,7 @@ invalid_assignment_rule(Parser *p) && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_loop0_119_var = _loop0_119_rule(p)) // star_named_expressions* + (_loop0_121_var = _loop0_121_rule(p)) // star_named_expressions* && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && @@ -21783,7 +22224,7 @@ invalid_assignment_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions* ':' expression")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "only single target (not tuple) can be annotated" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21813,7 +22254,7 @@ invalid_assignment_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' expression")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "illegal target for annotation" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21831,10 +22272,10 @@ invalid_assignment_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* star_expressions '='")); Token * _literal; - asdl_seq * _loop0_120_var; + asdl_seq * _loop0_122_var; expr_ty a; if ( - (_loop0_120_var = _loop0_120_rule(p)) // ((star_targets '='))* + (_loop0_122_var = _loop0_122_rule(p)) // ((star_targets '='))* && (a = star_expressions_rule(p)) // star_expressions && @@ -21843,7 +22284,7 @@ invalid_assignment_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* star_expressions '='")); _res = RAISE_SYNTAX_ERROR_INVALID_TARGET ( STAR_TARGETS , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21861,10 +22302,10 @@ invalid_assignment_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_assignment[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* yield_expr '='")); Token * _literal; - asdl_seq * _loop0_120_var; + asdl_seq * _loop0_122_var; expr_ty a; if ( - (_loop0_120_var = _loop0_120_rule(p)) // ((star_targets '='))* + (_loop0_122_var = _loop0_122_rule(p)) // ((star_targets '='))* && (a = yield_expr_rule(p)) // yield_expr && @@ -21873,7 +22314,7 @@ invalid_assignment_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((star_targets '='))* yield_expr '='")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "assignment to yield expression not possible" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21903,7 +22344,7 @@ invalid_assignment_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_assignment[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_expressions augassign annotated_rhs")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "'%s' is an illegal expression for augmented assignment" , _PyPegen_get_expr_name ( a ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -21990,7 +22431,7 @@ invalid_ann_assign_target_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_ann_assign_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' invalid_ann_assign_target ')'")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22029,14 +22470,14 @@ invalid_raise_stmt_rule(Parser *p) Token * a; Token * b; if ( - (a = _PyPegen_expect_token(p, 628)) // token='raise' + (a = _PyPegen_expect_token(p, 632)) // token='raise' && - (b = _PyPegen_expect_token(p, 638)) // token='from' + (b = _PyPegen_expect_token(p, 646)) // token='from' ) { D(fprintf(stderr, "%*c+ invalid_raise_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'raise' 'from'")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "did you forget an expression between 'raise' and 'from'?" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22057,16 +22498,16 @@ invalid_raise_stmt_rule(Parser *p) Token * a; expr_ty expression_var; if ( - (_keyword = _PyPegen_expect_token(p, 628)) // token='raise' + (_keyword = _PyPegen_expect_token(p, 632)) // token='raise' && (expression_var = expression_rule(p)) // expression && - (a = _PyPegen_expect_token(p, 638)) // token='from' + (a = _PyPegen_expect_token(p, 646)) // token='from' ) { D(fprintf(stderr, "%*c+ invalid_raise_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'raise' expression 'from'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "did you forget an expression after 'from'?" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22105,14 +22546,14 @@ invalid_del_stmt_rule(Parser *p) Token * _keyword; expr_ty a; if ( - (_keyword = _PyPegen_expect_token(p, 630)) // token='del' + (_keyword = _PyPegen_expect_token(p, 634)) // token='del' && (a = star_expressions_rule(p)) // star_expressions ) { D(fprintf(stderr, "%*c+ invalid_del_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'del' star_expressions")); _res = RAISE_SYNTAX_ERROR_INVALID_TARGET ( DEL_TARGETS , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22129,6 +22570,173 @@ invalid_del_stmt_rule(Parser *p) return _res; } +// invalid_assert_stmt: +// | 'assert' expression '=' expression +// | 'assert' expression ',' expression '=' expression +// | 'assert' expression ':=' expression +// | 'assert' expression ',' expression ':=' expression +static void * +invalid_assert_stmt_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // 'assert' expression '=' expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_assert_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'assert' expression '=' expression")); + Token * _keyword; + Token * _literal; + expr_ty a; + expr_ty b; + if ( + (_keyword = _PyPegen_expect_token(p, 638)) // token='assert' + && + (a = expression_rule(p)) // expression + && + (_literal = _PyPegen_expect_token(p, 22)) // token='=' + && + (b = expression_rule(p)) // expression + ) + { + D(fprintf(stderr, "%*c+ invalid_assert_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'assert' expression '=' expression")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot assign to %s here. Maybe you meant '==' instead of '='?" , _PyPegen_get_expr_name ( a ) ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_assert_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'assert' expression '=' expression")); + } + { // 'assert' expression ',' expression '=' expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_assert_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'assert' expression ',' expression '=' expression")); + Token * _keyword; + Token * _literal; + Token * _literal_1; + expr_ty a; + expr_ty b; + expr_ty expression_var; + if ( + (_keyword = _PyPegen_expect_token(p, 638)) // token='assert' + && + (expression_var = expression_rule(p)) // expression + && + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + && + (a = expression_rule(p)) // expression + && + (_literal_1 = _PyPegen_expect_token(p, 22)) // token='=' + && + (b = expression_rule(p)) // expression + ) + { + D(fprintf(stderr, "%*c+ invalid_assert_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'assert' expression ',' expression '=' expression")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot assign to %s here. Maybe you meant '==' instead of '='?" , _PyPegen_get_expr_name ( a ) ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_assert_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'assert' expression ',' expression '=' expression")); + } + { // 'assert' expression ':=' expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_assert_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'assert' expression ':=' expression")); + Token * _keyword; + Token * _literal; + expr_ty a; + expr_ty b; + if ( + (_keyword = _PyPegen_expect_token(p, 638)) // token='assert' + && + (a = expression_rule(p)) // expression + && + (_literal = _PyPegen_expect_token(p, 53)) // token=':=' + && + (b = expression_rule(p)) // expression + ) + { + D(fprintf(stderr, "%*c+ invalid_assert_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'assert' expression ':=' expression")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot use named expression without parentheses here" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_assert_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'assert' expression ':=' expression")); + } + { // 'assert' expression ',' expression ':=' expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_assert_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'assert' expression ',' expression ':=' expression")); + Token * _keyword; + Token * _literal; + Token * _literal_1; + expr_ty a; + expr_ty b; + expr_ty expression_var; + if ( + (_keyword = _PyPegen_expect_token(p, 638)) // token='assert' + && + (expression_var = expression_rule(p)) // expression + && + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + && + (a = expression_rule(p)) // expression + && + (_literal_1 = _PyPegen_expect_token(p, 53)) // token=':=' + && + (b = expression_rule(p)) // expression + ) + { + D(fprintf(stderr, "%*c+ invalid_assert_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'assert' expression ',' expression ':=' expression")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot use named expression without parentheses here" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_assert_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'assert' expression ',' expression ':=' expression")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // invalid_block: NEWLINE !INDENT static void * invalid_block_rule(Parser *p) @@ -22157,7 +22765,7 @@ invalid_block_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_block[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22175,7 +22783,8 @@ invalid_block_rule(Parser *p) } // invalid_comprehension: -// | ('[' | '(' | '{') starred_expression for_if_clauses +// | '[' '**' expression for_if_clauses +// | '(' '**' expression for_if_clauses // | ('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses // | ('[' | '{') star_named_expression ',' for_if_clauses static void * @@ -22190,26 +22799,29 @@ invalid_comprehension_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // ('[' | '(' | '{') starred_expression for_if_clauses + { // '[' '**' expression for_if_clauses if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '(' | '{') starred_expression for_if_clauses")); - void *_tmp_121_var; - expr_ty a; + D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'[' '**' expression for_if_clauses")); + Token * _literal; + Token * a; + expr_ty b; asdl_comprehension_seq* for_if_clauses_var; if ( - (_tmp_121_var = _tmp_121_rule(p)) // '[' | '(' | '{' + (_literal = _PyPegen_expect_token(p, 9)) // token='[' && - (a = starred_expression_rule(p)) // starred_expression + (a = _PyPegen_expect_token(p, 35)) // token='**' + && + (b = expression_rule(p)) // expression && (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ invalid_comprehension[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "('[' | '(' | '{') starred_expression for_if_clauses")); - _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "iterable unpacking cannot be used in comprehension" ); - if (_res == NULL && PyErr_Occurred()) { + D(fprintf(stderr, "%*c+ invalid_comprehension[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'[' '**' expression for_if_clauses")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot use dict unpacking in list comprehension" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22218,34 +22830,31 @@ invalid_comprehension_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_comprehension[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('[' | '(' | '{') starred_expression for_if_clauses")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'[' '**' expression for_if_clauses")); } - { // ('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses + { // '(' '**' expression for_if_clauses if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses")); + D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' '**' expression for_if_clauses")); Token * _literal; - void *_tmp_122_var; - expr_ty a; - asdl_expr_seq* b; + Token * a; + expr_ty b; asdl_comprehension_seq* for_if_clauses_var; if ( - (_tmp_122_var = _tmp_122_rule(p)) // '[' | '{' - && - (a = star_named_expression_rule(p)) // star_named_expression + (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (_literal = _PyPegen_expect_token(p, 12)) // token=',' + (a = _PyPegen_expect_token(p, 35)) // token='**' && - (b = star_named_expressions_rule(p)) // star_named_expressions + (b = expression_rule(p)) // expression && (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ invalid_comprehension[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses")); - _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , PyPegen_last_item ( b , expr_ty ) , "did you forget parentheses around the comprehension target?" ); - if (_res == NULL && PyErr_Occurred()) { + D(fprintf(stderr, "%*c+ invalid_comprehension[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' '**' expression for_if_clauses")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot use dict unpacking in generator expression" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22254,31 +22863,34 @@ invalid_comprehension_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_comprehension[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' '**' expression for_if_clauses")); } - { // ('[' | '{') star_named_expression ',' for_if_clauses + { // ('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' for_if_clauses")); - void *_tmp_122_var; + D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses")); + Token * _literal; + void *_tmp_123_var; expr_ty a; - Token * b; + asdl_expr_seq* b; asdl_comprehension_seq* for_if_clauses_var; if ( - (_tmp_122_var = _tmp_122_rule(p)) // '[' | '{' + (_tmp_123_var = _tmp_123_rule(p)) // '[' | '{' && (a = star_named_expression_rule(p)) // star_named_expression && - (b = _PyPegen_expect_token(p, 12)) // token=',' + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + && + (b = star_named_expressions_rule(p)) // star_named_expressions && (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ invalid_comprehension[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' for_if_clauses")); - _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "did you forget parentheses around the comprehension target?" ); - if (_res == NULL && PyErr_Occurred()) { + D(fprintf(stderr, "%*c+ invalid_comprehension[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , PyPegen_last_item ( b , expr_ty ) , "did you forget parentheses around the comprehension target?" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22287,53 +22899,31 @@ invalid_comprehension_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_comprehension[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('[' | '{') star_named_expression ',' for_if_clauses")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// invalid_dict_comprehension: '{' '**' bitwise_or for_if_clauses '}' -static void * -invalid_dict_comprehension_rule(Parser *p) -{ - if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('[' | '{') star_named_expression ',' star_named_expressions for_if_clauses")); } - void * _res = NULL; - int _mark = p->mark; - { // '{' '**' bitwise_or for_if_clauses '}' + { // ('[' | '{') star_named_expression ',' for_if_clauses if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_dict_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' '**' bitwise_or for_if_clauses '}'")); - Token * _literal; - Token * _literal_1; - Token * a; - expr_ty bitwise_or_var; + D(fprintf(stderr, "%*c> invalid_comprehension[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' for_if_clauses")); + void *_tmp_123_var; + expr_ty a; + Token * b; asdl_comprehension_seq* for_if_clauses_var; if ( - (_literal = _PyPegen_expect_token(p, 25)) // token='{' + (_tmp_123_var = _tmp_123_rule(p)) // '[' | '{' && - (a = _PyPegen_expect_token(p, 35)) // token='**' + (a = star_named_expression_rule(p)) // star_named_expression && - (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or + (b = _PyPegen_expect_token(p, 12)) // token=',' && (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses - && - (_literal_1 = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ invalid_dict_comprehension[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '**' bitwise_or for_if_clauses '}'")); - _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "dict unpacking cannot be used in dict comprehension" ); - if (_res == NULL && PyErr_Occurred()) { + D(fprintf(stderr, "%*c+ invalid_comprehension[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "('[' | '{') star_named_expression ',' for_if_clauses")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "did you forget parentheses around the comprehension target?" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22341,8 +22931,8 @@ invalid_dict_comprehension_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s invalid_dict_comprehension[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' '**' bitwise_or for_if_clauses '}'")); + D(fprintf(stderr, "%*c%s invalid_comprehension[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('[' | '{') star_named_expression ',' for_if_clauses")); } _res = NULL; done: @@ -22385,7 +22975,7 @@ invalid_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"/\" ','")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "at least one parameter must precede /" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22403,10 +22993,10 @@ invalid_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(slash_no_default | slash_with_default) param_maybe_default* '/'")); asdl_seq * _loop0_31_var; - void *_tmp_123_var; + void *_tmp_124_var; Token * a; if ( - (_tmp_123_var = _tmp_123_rule(p)) // slash_no_default | slash_with_default + (_tmp_124_var = _tmp_124_rule(p)) // slash_no_default | slash_with_default && (_loop0_31_var = _loop0_31_rule(p)) // param_maybe_default* && @@ -22415,7 +23005,7 @@ invalid_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(slash_no_default | slash_with_default) param_maybe_default* '/'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "/ may appear only once" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22449,7 +23039,7 @@ invalid_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default? param_no_default* invalid_parameters_helper param_no_default")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "parameter without a default follows parameter with a default" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22486,7 +23076,7 @@ invalid_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default* '(' param_no_default+ ','? ')'")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "Function parameters cannot be parenthesized" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22508,16 +23098,16 @@ invalid_parameters_rule(Parser *p) asdl_seq * _loop0_31_var_1; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings - void *_tmp_124_var; + void *_tmp_125_var; Token * a; if ( - (_opt_var = _tmp_123_rule(p), !p->error_indicator) // [(slash_no_default | slash_with_default)] + (_opt_var = _tmp_124_rule(p), !p->error_indicator) // [(slash_no_default | slash_with_default)] && (_loop0_31_var = _loop0_31_rule(p)) // param_maybe_default* && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_124_var = _tmp_124_rule(p)) // ',' | param_no_default + (_tmp_125_var = _tmp_125_rule(p)) // ',' | param_no_default && (_loop0_31_var_1 = _loop0_31_rule(p)) // param_maybe_default* && @@ -22526,7 +23116,7 @@ invalid_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "[(slash_no_default | slash_with_default)] param_maybe_default* '*' (',' | param_no_default) param_maybe_default* '/'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "/ must be ahead of *" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22556,7 +23146,7 @@ invalid_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_maybe_default+ '/' '*'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "expected comma between / and *" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22596,12 +23186,12 @@ invalid_default_rule(Parser *p) if ( (a = _PyPegen_expect_token(p, 22)) // token='=' && - _PyPegen_lookahead(1, _tmp_125_rule, p) + _PyPegen_lookahead(1, _tmp_126_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_default[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'=' &(')' | ',')")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "expected default value expression" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22641,17 +23231,17 @@ invalid_star_etc_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))")); - void *_tmp_126_var; + void *_tmp_127_var; Token * a; if ( (a = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_126_var = _tmp_126_rule(p)) // ')' | ',' (')' | '**') + (_tmp_127_var = _tmp_127_rule(p)) // ')' | ',' (')' | '**') ) { D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (')' | ',' (')' | '**'))")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "named parameters must follow bare *" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22681,7 +23271,7 @@ invalid_star_etc_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' ',' TYPE_COMMENT")); _res = RAISE_SYNTAX_ERROR ( "bare * has associated type comment" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22711,7 +23301,7 @@ invalid_star_etc_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' param '='")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "var-positional parameter cannot have default value" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22730,24 +23320,24 @@ invalid_star_etc_rule(Parser *p) D(fprintf(stderr, "%*c> invalid_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (param_no_default | ',') param_maybe_default* '*' (param_no_default | ',')")); Token * _literal; asdl_seq * _loop0_31_var; - void *_tmp_127_var; - void *_tmp_127_var_1; + void *_tmp_128_var; + void *_tmp_128_var_1; Token * a; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_127_var = _tmp_127_rule(p)) // param_no_default | ',' + (_tmp_128_var = _tmp_128_rule(p)) // param_no_default | ',' && (_loop0_31_var = _loop0_31_rule(p)) // param_maybe_default* && (a = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_127_var_1 = _tmp_127_rule(p)) // param_no_default | ',' + (_tmp_128_var_1 = _tmp_128_rule(p)) // param_no_default | ',' ) { D(fprintf(stderr, "%*c+ invalid_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (param_no_default | ',') param_maybe_default* '*' (param_no_default | ',')")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "* may appear only once" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22796,7 +23386,7 @@ invalid_kwds_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' param '='")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "var-keyword parameter cannot have default value" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22829,7 +23419,7 @@ invalid_kwds_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' param ',' param")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "parameters cannot follow var-keyword parameter" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22857,12 +23447,12 @@ invalid_kwds_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 12)) // token=',' && - (a = (Token*)_tmp_128_rule(p)) // '*' | '**' | '/' + (a = (Token*)_tmp_129_rule(p)) // '*' | '**' | '/' ) { D(fprintf(stderr, "%*c+ invalid_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' param ',' ('*' | '**' | '/')")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "parameters cannot follow var-keyword parameter" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22905,7 +23495,7 @@ invalid_parameters_helper_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_parameters_helper[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); _res = _PyPegen_singleton_seq ( p , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22976,7 +23566,7 @@ invalid_lambda_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"/\" ','")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "at least one parameter must precede /" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -22993,20 +23583,20 @@ invalid_lambda_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(lambda_slash_no_default | lambda_slash_with_default) lambda_param_maybe_default* '/'")); - asdl_seq * _loop0_74_var; - void *_tmp_129_var; + asdl_seq * _loop0_76_var; + void *_tmp_130_var; Token * a; if ( - (_tmp_129_var = _tmp_129_rule(p)) // lambda_slash_no_default | lambda_slash_with_default + (_tmp_130_var = _tmp_130_rule(p)) // lambda_slash_no_default | lambda_slash_with_default && - (_loop0_74_var = _loop0_74_rule(p)) // lambda_param_maybe_default* + (_loop0_76_var = _loop0_76_rule(p)) // lambda_param_maybe_default* && (a = _PyPegen_expect_token(p, 17)) // token='/' ) { D(fprintf(stderr, "%*c+ invalid_lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(lambda_slash_no_default | lambda_slash_with_default) lambda_param_maybe_default* '/'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "/ may appear only once" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23023,7 +23613,7 @@ invalid_lambda_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default? lambda_param_no_default* invalid_lambda_parameters_helper lambda_param_no_default")); - asdl_seq * _loop0_70_var; + asdl_seq * _loop0_72_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings arg_ty a; @@ -23031,7 +23621,7 @@ invalid_lambda_parameters_rule(Parser *p) if ( (_opt_var = lambda_slash_no_default_rule(p), !p->error_indicator) // lambda_slash_no_default? && - (_loop0_70_var = _loop0_70_rule(p)) // lambda_param_no_default* + (_loop0_72_var = _loop0_72_rule(p)) // lambda_param_no_default* && (invalid_lambda_parameters_helper_var = invalid_lambda_parameters_helper_rule(p)) // invalid_lambda_parameters_helper && @@ -23040,7 +23630,7 @@ invalid_lambda_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default? lambda_param_no_default* invalid_lambda_parameters_helper lambda_param_no_default")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "parameter without a default follows parameter with a default" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23057,18 +23647,18 @@ invalid_lambda_parameters_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default* '(' ','.lambda_param+ ','? ')'")); - asdl_seq * _gather_131_var; - asdl_seq * _loop0_70_var; + asdl_seq * _gather_132_var; + asdl_seq * _loop0_72_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; Token * b; if ( - (_loop0_70_var = _loop0_70_rule(p)) // lambda_param_no_default* + (_loop0_72_var = _loop0_72_rule(p)) // lambda_param_no_default* && (a = _PyPegen_expect_token(p, 7)) // token='(' && - (_gather_131_var = _gather_131_rule(p)) // ','.lambda_param+ + (_gather_132_var = _gather_132_rule(p)) // ','.lambda_param+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -23077,7 +23667,7 @@ invalid_lambda_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default* '(' ','.lambda_param+ ','? ')'")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "Lambda expression parameters cannot be parenthesized" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23095,29 +23685,29 @@ invalid_lambda_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "[(lambda_slash_no_default | lambda_slash_with_default)] lambda_param_maybe_default* '*' (',' | lambda_param_no_default) lambda_param_maybe_default* '/'")); Token * _literal; - asdl_seq * _loop0_74_var; - asdl_seq * _loop0_74_var_1; + asdl_seq * _loop0_76_var; + asdl_seq * _loop0_76_var_1; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings - void *_tmp_132_var; + void *_tmp_133_var; Token * a; if ( - (_opt_var = _tmp_129_rule(p), !p->error_indicator) // [(lambda_slash_no_default | lambda_slash_with_default)] + (_opt_var = _tmp_130_rule(p), !p->error_indicator) // [(lambda_slash_no_default | lambda_slash_with_default)] && - (_loop0_74_var = _loop0_74_rule(p)) // lambda_param_maybe_default* + (_loop0_76_var = _loop0_76_rule(p)) // lambda_param_maybe_default* && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_132_var = _tmp_132_rule(p)) // ',' | lambda_param_no_default + (_tmp_133_var = _tmp_133_rule(p)) // ',' | lambda_param_no_default && - (_loop0_74_var_1 = _loop0_74_rule(p)) // lambda_param_maybe_default* + (_loop0_76_var_1 = _loop0_76_rule(p)) // lambda_param_maybe_default* && (a = _PyPegen_expect_token(p, 17)) // token='/' ) { D(fprintf(stderr, "%*c+ invalid_lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "[(lambda_slash_no_default | lambda_slash_with_default)] lambda_param_maybe_default* '*' (',' | lambda_param_no_default) lambda_param_maybe_default* '/'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "/ must be ahead of *" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23135,10 +23725,10 @@ invalid_lambda_parameters_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default+ '/' '*'")); Token * _literal; - asdl_seq * _loop1_75_var; + asdl_seq * _loop1_77_var; Token * a; if ( - (_loop1_75_var = _loop1_75_rule(p)) // lambda_param_maybe_default+ + (_loop1_77_var = _loop1_77_rule(p)) // lambda_param_maybe_default+ && (_literal = _PyPegen_expect_token(p, 17)) // token='/' && @@ -23147,7 +23737,7 @@ invalid_lambda_parameters_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default+ '/' '*'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "expected comma between / and *" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23192,7 +23782,7 @@ invalid_lambda_parameters_helper_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_lambda_parameters_helper[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); _res = _PyPegen_singleton_seq ( p , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23209,13 +23799,13 @@ invalid_lambda_parameters_helper_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_lambda_parameters_helper[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+")); - asdl_seq * _loop1_73_var; + asdl_seq * _loop1_75_var; if ( - (_loop1_73_var = _loop1_73_rule(p)) // lambda_param_with_default+ + (_loop1_75_var = _loop1_75_rule(p)) // lambda_param_with_default+ ) { D(fprintf(stderr, "%*c+ invalid_lambda_parameters_helper[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default+")); - _res = _loop1_73_var; + _res = _loop1_75_var; goto done; } p->mark = _mark; @@ -23251,16 +23841,16 @@ invalid_lambda_star_etc_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))")); Token * _literal; - void *_tmp_133_var; + void *_tmp_134_var; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_133_var = _tmp_133_rule(p)) // ':' | ',' (':' | '**') + (_tmp_134_var = _tmp_134_rule(p)) // ':' | ',' (':' | '**') ) { D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (':' | ',' (':' | '**'))")); _res = RAISE_SYNTAX_ERROR ( "named parameters must follow bare *" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23290,7 +23880,7 @@ invalid_lambda_star_etc_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' lambda_param '='")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "var-positional parameter cannot have default value" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23308,25 +23898,25 @@ invalid_lambda_star_etc_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_lambda_star_etc[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' (lambda_param_no_default | ',') lambda_param_maybe_default* '*' (lambda_param_no_default | ',')")); Token * _literal; - asdl_seq * _loop0_74_var; - void *_tmp_134_var; - void *_tmp_134_var_1; + asdl_seq * _loop0_76_var; + void *_tmp_135_var; + void *_tmp_135_var_1; Token * a; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_134_var = _tmp_134_rule(p)) // lambda_param_no_default | ',' + (_tmp_135_var = _tmp_135_rule(p)) // lambda_param_no_default | ',' && - (_loop0_74_var = _loop0_74_rule(p)) // lambda_param_maybe_default* + (_loop0_76_var = _loop0_76_rule(p)) // lambda_param_maybe_default* && (a = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_134_var_1 = _tmp_134_rule(p)) // lambda_param_no_default | ',' + (_tmp_135_var_1 = _tmp_135_rule(p)) // lambda_param_no_default | ',' ) { D(fprintf(stderr, "%*c+ invalid_lambda_star_etc[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' (lambda_param_no_default | ',') lambda_param_maybe_default* '*' (lambda_param_no_default | ',')")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "* may appear only once" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23378,7 +23968,7 @@ invalid_lambda_kwds_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_lambda_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' lambda_param '='")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "var-keyword parameter cannot have default value" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23411,7 +24001,7 @@ invalid_lambda_kwds_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_lambda_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' lambda_param ',' lambda_param")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "parameters cannot follow var-keyword parameter" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23439,12 +24029,12 @@ invalid_lambda_kwds_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 12)) // token=',' && - (a = (Token*)_tmp_128_rule(p)) // '*' | '**' | '/' + (a = (Token*)_tmp_129_rule(p)) // '*' | '**' | '/' ) { D(fprintf(stderr, "%*c+ invalid_lambda_kwds[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' lambda_param ',' ('*' | '**' | '/')")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "parameters cannot follow var-keyword parameter" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23499,7 +24089,7 @@ invalid_double_type_comments_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_double_type_comments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "TYPE_COMMENT NEWLINE TYPE_COMMENT NEWLINE INDENT")); _res = RAISE_SYNTAX_ERROR ( "Cannot have two type comments on def" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23541,7 +24131,7 @@ invalid_with_item_rule(Parser *p) if ( (expression_var = expression_rule(p)) // expression && - (_keyword = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword = _PyPegen_expect_token(p, 696)) // token='as' && (a = expression_rule(p)) // expression && @@ -23550,7 +24140,7 @@ invalid_with_item_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_with_item[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression 'as' expression &(',' | ')' | ':')")); _res = RAISE_SYNTAX_ERROR_INVALID_TARGET ( STAR_TARGETS , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23589,20 +24179,20 @@ invalid_for_if_clause_rule(Parser *p) Token * _keyword; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings - void *_tmp_135_var; + void *_tmp_136_var; if ( - (_opt_var = _PyPegen_expect_token(p, 703), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 699)) // token='for' + (_keyword = _PyPegen_expect_token(p, 710)) // token='for' && - (_tmp_135_var = _tmp_135_rule(p)) // bitwise_or ((',' bitwise_or))* ','? + (_tmp_136_var = _tmp_136_rule(p)) // bitwise_or ((',' bitwise_or))* ','? && - _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 700) // token='in' + _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 711) // token='in' ) { D(fprintf(stderr, "%*c+ invalid_for_if_clause[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'for' (bitwise_or ((',' bitwise_or))* ','?) !'in'")); _res = RAISE_SYNTAX_ERROR ( "'in' expected after for-loop variables" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23643,16 +24233,16 @@ invalid_for_target_rule(Parser *p) UNUSED(_opt_var); // Silence compiler warnings expr_ty a; if ( - (_opt_var = _PyPegen_expect_token(p, 703), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 699)) // token='for' + (_keyword = _PyPegen_expect_token(p, 710)) // token='for' && (a = star_expressions_rule(p)) // star_expressions ) { D(fprintf(stderr, "%*c+ invalid_for_target[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'for' star_expressions")); _res = RAISE_SYNTAX_ERROR_INVALID_TARGET ( FOR_TARGETS , a ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23701,7 +24291,7 @@ invalid_group_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_group[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' starred_expression ')'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use starred expression here" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23734,7 +24324,7 @@ invalid_group_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_group[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' '**' expression ')'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use double starred expression here" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23770,23 +24360,23 @@ invalid_import_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_import[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'import' ','.dotted_name+ 'from' dotted_name")); - asdl_seq * _gather_137_var; + asdl_seq * _gather_138_var; Token * _keyword; Token * a; expr_ty dotted_name_var; if ( - (a = _PyPegen_expect_token(p, 639)) // token='import' + (a = _PyPegen_expect_token(p, 647)) // token='import' && - (_gather_137_var = _gather_137_rule(p)) // ','.dotted_name+ + (_gather_138_var = _gather_138_rule(p)) // ','.dotted_name+ && - (_keyword = _PyPegen_expect_token(p, 638)) // token='from' + (_keyword = _PyPegen_expect_token(p, 646)) // token='from' && (dotted_name_var = dotted_name_rule(p)) // dotted_name ) { D(fprintf(stderr, "%*c+ invalid_import[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'import' ','.dotted_name+ 'from' dotted_name")); _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "Did you mean to use 'from ... import ...' instead?" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23806,14 +24396,14 @@ invalid_import_rule(Parser *p) Token * _keyword; Token * token; if ( - (_keyword = _PyPegen_expect_token(p, 639)) // token='import' + (_keyword = _PyPegen_expect_token(p, 647)) // token='import' && (token = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { D(fprintf(stderr, "%*c+ invalid_import[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'import' NEWLINE")); _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( token , "Expected one or more names after 'import'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23830,7 +24420,7 @@ invalid_import_rule(Parser *p) return _res; } -// invalid_dotted_as_name: dotted_name 'as' !(NAME (',' | ')' | NEWLINE)) expression +// invalid_dotted_as_name: dotted_name 'as' !(NAME (',' | ')' | ';' | NEWLINE)) expression static void * invalid_dotted_as_name_rule(Parser *p) { @@ -23843,28 +24433,28 @@ invalid_dotted_as_name_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // dotted_name 'as' !(NAME (',' | ')' | NEWLINE)) expression + { // dotted_name 'as' !(NAME (',' | ')' | ';' | NEWLINE)) expression if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_dotted_as_name[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dotted_name 'as' !(NAME (',' | ')' | NEWLINE)) expression")); + D(fprintf(stderr, "%*c> invalid_dotted_as_name[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dotted_name 'as' !(NAME (',' | ')' | ';' | NEWLINE)) expression")); Token * _keyword; expr_ty a; expr_ty dotted_name_var; if ( (dotted_name_var = dotted_name_rule(p)) // dotted_name && - (_keyword = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword = _PyPegen_expect_token(p, 696)) // token='as' && - _PyPegen_lookahead(0, _tmp_138_rule, p) + _PyPegen_lookahead(0, _tmp_139_rule, p) && (a = expression_rule(p)) // expression ) { - D(fprintf(stderr, "%*c+ invalid_dotted_as_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name 'as' !(NAME (',' | ')' | NEWLINE)) expression")); + D(fprintf(stderr, "%*c+ invalid_dotted_as_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name 'as' !(NAME (',' | ')' | ';' | NEWLINE)) expression")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use %s as import target" , _PyPegen_get_expr_name ( a ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23873,7 +24463,7 @@ invalid_dotted_as_name_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_dotted_as_name[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dotted_name 'as' !(NAME (',' | ')' | NEWLINE)) expression")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dotted_name 'as' !(NAME (',' | ')' | ';' | NEWLINE)) expression")); } _res = NULL; done: @@ -23881,7 +24471,7 @@ invalid_dotted_as_name_rule(Parser *p) return _res; } -// invalid_import_from_as_name: NAME 'as' !(NAME (',' | ')' | NEWLINE)) expression +// invalid_import_from_as_name: NAME 'as' !(NAME (',' | ')' | ';' | NEWLINE)) expression static void * invalid_import_from_as_name_rule(Parser *p) { @@ -23894,28 +24484,28 @@ invalid_import_from_as_name_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // NAME 'as' !(NAME (',' | ')' | NEWLINE)) expression + { // NAME 'as' !(NAME (',' | ')' | ';' | NEWLINE)) expression if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_import_from_as_name[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME 'as' !(NAME (',' | ')' | NEWLINE)) expression")); + D(fprintf(stderr, "%*c> invalid_import_from_as_name[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME 'as' !(NAME (',' | ')' | ';' | NEWLINE)) expression")); Token * _keyword; expr_ty a; expr_ty name_var; if ( (name_var = _PyPegen_name_token(p)) // NAME && - (_keyword = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword = _PyPegen_expect_token(p, 696)) // token='as' && - _PyPegen_lookahead(0, _tmp_138_rule, p) + _PyPegen_lookahead(0, _tmp_139_rule, p) && (a = expression_rule(p)) // expression ) { - D(fprintf(stderr, "%*c+ invalid_import_from_as_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME 'as' !(NAME (',' | ')' | NEWLINE)) expression")); + D(fprintf(stderr, "%*c+ invalid_import_from_as_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME 'as' !(NAME (',' | ')' | ';' | NEWLINE)) expression")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use %s as import target" , _PyPegen_get_expr_name ( a ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23924,7 +24514,7 @@ invalid_import_from_as_name_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_import_from_as_name[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME 'as' !(NAME (',' | ')' | NEWLINE)) expression")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME 'as' !(NAME (',' | ')' | ';' | NEWLINE)) expression")); } _res = NULL; done: @@ -23964,7 +24554,7 @@ invalid_import_from_targets_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_import_from_targets[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "import_from_as_names ',' NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "trailing comma not allowed without surrounding parentheses" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -23988,7 +24578,7 @@ invalid_import_from_targets_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_import_from_targets[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( token , "Expected one or more names after 'import'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24006,6 +24596,7 @@ invalid_import_from_targets_rule(Parser *p) } // invalid_with_stmt: +// | 'async'? 'with' ','.(expression ['as' star_target])+ ',' ':' // | 'async'? 'with' ','.(expression ['as' star_target])+ NEWLINE // | 'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE static void * @@ -24020,30 +24611,67 @@ invalid_with_stmt_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; + { // 'async'? 'with' ','.(expression ['as' star_target])+ ',' ':' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_with_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ ',' ':'")); + asdl_seq * _gather_141_var; + Token * _keyword; + Token * _literal; + void *_opt_var; + UNUSED(_opt_var); // Silence compiler warnings + Token * trailing; + if ( + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? + && + (_keyword = _PyPegen_expect_token(p, 663)) // token='with' + && + (_gather_141_var = _gather_141_rule(p)) // ','.(expression ['as' star_target])+ + && + (trailing = _PyPegen_expect_token(p, 12)) // token=',' + && + (_literal = _PyPegen_expect_token(p, 11)) // token=':' + ) + { + D(fprintf(stderr, "%*c+ invalid_with_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ ',' ':'")); + _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( trailing , "the last 'with' item has a trailing comma" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_with_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ ',' ':'")); + } { // 'async'? 'with' ','.(expression ['as' star_target])+ NEWLINE if (p->error_indicator) { p->level--; return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ NEWLINE")); - asdl_seq * _gather_140_var; + asdl_seq * _gather_141_var; Token * _keyword; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 703), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 652)) // token='with' + (_keyword = _PyPegen_expect_token(p, 663)) // token='with' && - (_gather_140_var = _gather_140_rule(p)) // ','.(expression ['as' star_target])+ + (_gather_141_var = _gather_141_rule(p)) // ','.(expression ['as' star_target])+ && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { D(fprintf(stderr, "%*c+ invalid_with_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24060,7 +24688,7 @@ invalid_with_stmt_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE")); - asdl_seq * _gather_142_var; + asdl_seq * _gather_143_var; Token * _keyword; Token * _literal; Token * _literal_1; @@ -24070,13 +24698,13 @@ invalid_with_stmt_rule(Parser *p) UNUSED(_opt_var_1); // Silence compiler warnings Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 703), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 652)) // token='with' + (_keyword = _PyPegen_expect_token(p, 663)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (_gather_142_var = _gather_142_rule(p)) // ','.(expressions ['as' star_target])+ + (_gather_143_var = _gather_143_rule(p)) // ','.(expressions ['as' star_target])+ && (_opt_var_1 = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -24087,7 +24715,7 @@ invalid_with_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_with_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24125,18 +24753,18 @@ invalid_with_stmt_indent_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt_indent[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ ':' NEWLINE !INDENT")); - asdl_seq * _gather_140_var; + asdl_seq * _gather_141_var; Token * _literal; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 703), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 652)) // token='with' + (a = _PyPegen_expect_token(p, 663)) // token='with' && - (_gather_140_var = _gather_140_rule(p)) // ','.(expression ['as' star_target])+ + (_gather_141_var = _gather_141_rule(p)) // ','.(expression ['as' star_target])+ && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -24147,7 +24775,7 @@ invalid_with_stmt_indent_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_with_stmt_indent[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'with' ','.(expression ['as' star_target])+ ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'with' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24164,7 +24792,7 @@ invalid_with_stmt_indent_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_with_stmt_indent[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' ':' NEWLINE !INDENT")); - asdl_seq * _gather_142_var; + asdl_seq * _gather_143_var; Token * _literal; Token * _literal_1; Token * _literal_2; @@ -24175,13 +24803,13 @@ invalid_with_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 703), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 652)) // token='with' + (a = _PyPegen_expect_token(p, 663)) // token='with' && (_literal = _PyPegen_expect_token(p, 7)) // token='(' && - (_gather_142_var = _gather_142_rule(p)) // ','.(expressions ['as' star_target])+ + (_gather_143_var = _gather_143_rule(p)) // ','.(expressions ['as' star_target])+ && (_opt_var_1 = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? && @@ -24196,7 +24824,7 @@ invalid_with_stmt_indent_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_with_stmt_indent[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'with' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24240,7 +24868,7 @@ invalid_try_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 661)) // token='try' + (a = _PyPegen_expect_token(p, 672)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -24251,7 +24879,7 @@ invalid_try_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_try_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'try' ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'try' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24272,18 +24900,18 @@ invalid_try_stmt_rule(Parser *p) Token * _literal; asdl_stmt_seq* block_var; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='try' + (_keyword = _PyPegen_expect_token(p, 672)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && (block_var = block_rule(p)) // block && - _PyPegen_lookahead(0, _tmp_143_rule, p) + _PyPegen_lookahead(0, _tmp_144_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_try_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'try' ':' block !('except' | 'finally')")); _res = RAISE_SYNTAX_ERROR ( "expected 'except' or 'finally' block" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24303,7 +24931,7 @@ invalid_try_stmt_rule(Parser *p) Token * _keyword; Token * _literal; Token * _literal_1; - asdl_seq * _loop0_144_var; + asdl_seq * _loop0_145_var; asdl_seq * _loop1_36_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings @@ -24311,15 +24939,15 @@ invalid_try_stmt_rule(Parser *p) Token * b; expr_ty expression_var; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='try' + (_keyword = _PyPegen_expect_token(p, 672)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && - (_loop0_144_var = _loop0_144_rule(p)) // block* + (_loop0_145_var = _loop0_145_rule(p)) // block* && (_loop1_36_var = _loop1_36_rule(p)) // except_block+ && - (a = _PyPegen_expect_token(p, 682)) // token='except' + (a = _PyPegen_expect_token(p, 693)) // token='except' && (b = _PyPegen_expect_token(p, 16)) // token='*' && @@ -24332,7 +24960,7 @@ invalid_try_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_try_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'try' ':' block* except_block+ 'except' '*' expression ['as' NAME] ':'")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot have both 'except' and 'except*' on the same 'try'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24352,30 +24980,30 @@ invalid_try_stmt_rule(Parser *p) Token * _keyword; Token * _literal; Token * _literal_1; - asdl_seq * _loop0_144_var; + asdl_seq * _loop0_145_var; asdl_seq * _loop1_37_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings Token * a; if ( - (_keyword = _PyPegen_expect_token(p, 661)) // token='try' + (_keyword = _PyPegen_expect_token(p, 672)) // token='try' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && - (_loop0_144_var = _loop0_144_rule(p)) // block* + (_loop0_145_var = _loop0_145_rule(p)) // block* && (_loop1_37_var = _loop1_37_rule(p)) // except_star_block+ && - (a = _PyPegen_expect_token(p, 682)) // token='except' + (a = _PyPegen_expect_token(p, 693)) // token='except' && - (_opt_var = _tmp_145_rule(p), !p->error_indicator) // [expression ['as' NAME]] + (_opt_var = _tmp_146_rule(p), !p->error_indicator) // [expression ['as' NAME]] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' ) { D(fprintf(stderr, "%*c+ invalid_try_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'try' ':' block* except_star_block+ 'except' [expression ['as' NAME]] ':'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot have both 'except' and 'except*' on the same 'try'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24423,7 +25051,7 @@ invalid_except_stmt_rule(Parser *p) expr_ty expressions_var; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (a = expression_rule(p)) // expression && @@ -24431,7 +25059,7 @@ invalid_except_stmt_rule(Parser *p) && (expressions_var = expressions_rule(p)) // expressions && - (_keyword_1 = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword_1 = _PyPegen_expect_token(p, 696)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -24440,7 +25068,7 @@ invalid_except_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_except_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' expression ',' expressions 'as' NAME ':'")); _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "multiple exception types must be parenthesized when using 'as'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24463,7 +25091,7 @@ invalid_except_stmt_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 682)) // token='except' + (a = _PyPegen_expect_token(p, 693)) // token='except' && (expression_var = expression_rule(p)) // expression && @@ -24474,7 +25102,7 @@ invalid_except_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_except_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' expression ['as' NAME] NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24494,14 +25122,14 @@ invalid_except_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 682)) // token='except' + (a = _PyPegen_expect_token(p, 693)) // token='except' && (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { D(fprintf(stderr, "%*c+ invalid_except_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24525,11 +25153,11 @@ invalid_except_stmt_rule(Parser *p) asdl_stmt_seq* block_var; expr_ty expression_var; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (expression_var = expression_rule(p)) // expression && - (_keyword_1 = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword_1 = _PyPegen_expect_token(p, 696)) // token='as' && (a = expression_rule(p)) // expression && @@ -24540,7 +25168,7 @@ invalid_except_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_except_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' expression 'as' expression ':' block")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use except statement with %s" , _PyPegen_get_expr_name ( a ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24589,7 +25217,7 @@ invalid_except_star_stmt_rule(Parser *p) expr_ty expressions_var; expr_ty name_var; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -24599,7 +25227,7 @@ invalid_except_star_stmt_rule(Parser *p) && (expressions_var = expressions_rule(p)) // expressions && - (_keyword_1 = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword_1 = _PyPegen_expect_token(p, 696)) // token='as' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -24608,7 +25236,7 @@ invalid_except_star_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_except_star_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' '*' expression ',' expressions 'as' NAME ':'")); _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "multiple exception types must be parenthesized when using 'as'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24632,7 +25260,7 @@ invalid_except_star_stmt_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 682)) // token='except' + (a = _PyPegen_expect_token(p, 693)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -24645,7 +25273,7 @@ invalid_except_star_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_except_star_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' '*' expression ['as' NAME] NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24663,19 +25291,19 @@ invalid_except_star_stmt_rule(Parser *p) } D(fprintf(stderr, "%*c> invalid_except_star_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except' '*' (NEWLINE | ':')")); Token * _literal; - void *_tmp_146_var; + void *_tmp_147_var; Token * a; if ( - (a = _PyPegen_expect_token(p, 682)) // token='except' + (a = _PyPegen_expect_token(p, 693)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && - (_tmp_146_var = _tmp_146_rule(p)) // NEWLINE | ':' + (_tmp_147_var = _tmp_147_rule(p)) // NEWLINE | ':' ) { D(fprintf(stderr, "%*c+ invalid_except_star_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' '*' (NEWLINE | ':')")); _res = RAISE_SYNTAX_ERROR ( "expected one or more exception types" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24700,13 +25328,13 @@ invalid_except_star_stmt_rule(Parser *p) asdl_stmt_seq* block_var; expr_ty expression_var; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && (expression_var = expression_rule(p)) // expression && - (_keyword_1 = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword_1 = _PyPegen_expect_token(p, 696)) // token='as' && (a = expression_rule(p)) // expression && @@ -24717,7 +25345,7 @@ invalid_except_star_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_except_star_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' '*' expression 'as' expression ':' block")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use except* statement with %s" , _PyPegen_get_expr_name ( a ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24757,7 +25385,7 @@ invalid_finally_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 678)) // token='finally' + (a = _PyPegen_expect_token(p, 689)) // token='finally' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -24768,7 +25396,7 @@ invalid_finally_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_finally_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally' ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'finally' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24813,7 +25441,7 @@ invalid_except_stmt_indent_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 682)) // token='except' + (a = _PyPegen_expect_token(p, 693)) // token='except' && (expression_var = expression_rule(p)) // expression && @@ -24828,7 +25456,7 @@ invalid_except_stmt_indent_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_except_stmt_indent[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' expression ['as' NAME] ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'except' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24849,7 +25477,7 @@ invalid_except_stmt_indent_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 682)) // token='except' + (a = _PyPegen_expect_token(p, 693)) // token='except' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -24860,7 +25488,7 @@ invalid_except_stmt_indent_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_except_stmt_indent[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'except' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24905,7 +25533,7 @@ invalid_except_star_stmt_indent_rule(Parser *p) expr_ty expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 682)) // token='except' + (a = _PyPegen_expect_token(p, 693)) // token='except' && (_literal = _PyPegen_expect_token(p, 16)) // token='*' && @@ -24922,7 +25550,7 @@ invalid_except_star_stmt_indent_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_except_star_stmt_indent[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' '*' expression ['as' NAME] ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'except*' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -24942,6 +25570,7 @@ invalid_except_star_stmt_indent_rule(Parser *p) // invalid_match_stmt: // | "match" subject_expr NEWLINE // | "match" subject_expr ':' NEWLINE !INDENT +// | "case" patterns guard? ':' block static void * invalid_match_stmt_rule(Parser *p) { @@ -24973,7 +25602,7 @@ invalid_match_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_match_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"match\" subject_expr NEWLINE")); _res = CHECK_VERSION ( void* , 10 , "Pattern matching is" , RAISE_SYNTAX_ERROR ( "expected ':'" ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25008,7 +25637,7 @@ invalid_match_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_match_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"match\" subject_expr ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'match' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25019,6 +25648,43 @@ invalid_match_stmt_rule(Parser *p) D(fprintf(stderr, "%*c%s invalid_match_stmt[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "\"match\" subject_expr ':' NEWLINE !INDENT")); } + { // "case" patterns guard? ':' block + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_match_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "\"case\" patterns guard? ':' block")); + void *_opt_var; + UNUSED(_opt_var); // Silence compiler warnings + expr_ty a; + Token * b; + asdl_stmt_seq* block_var; + pattern_ty patterns_var; + if ( + (a = _PyPegen_expect_soft_keyword(p, "case")) // soft_keyword='"case"' + && + (patterns_var = patterns_rule(p)) // patterns + && + (_opt_var = guard_rule(p), !p->error_indicator) // guard? + && + (b = _PyPegen_expect_token(p, 11)) // token=':' + && + (block_var = block_rule(p)) // block + ) + { + D(fprintf(stderr, "%*c+ invalid_match_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"case\" patterns guard? ':' block")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "case statement must be inside match statement" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_match_stmt[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "\"case\" patterns guard? ':' block")); + } _res = NULL; done: p->level--; @@ -25063,7 +25729,7 @@ invalid_case_block_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_case_block[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"case\" patterns guard? NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25102,7 +25768,7 @@ invalid_case_block_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_case_block[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"case\" patterns guard? ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'case' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25144,14 +25810,14 @@ invalid_as_pattern_rule(Parser *p) if ( (or_pattern_var = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword = _PyPegen_expect_token(p, 696)) // token='as' && (a = _PyPegen_expect_soft_keyword(p, "_")) // soft_keyword='"_"' ) { D(fprintf(stderr, "%*c+ invalid_as_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "or_pattern 'as' \"_\"")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use '_' as a target" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25174,14 +25840,14 @@ invalid_as_pattern_rule(Parser *p) if ( (or_pattern_var = or_pattern_rule(p)) // or_pattern && - (_keyword = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword = _PyPegen_expect_token(p, 696)) // token='as' && (a = expression_rule(p)) // expression ) { D(fprintf(stderr, "%*c+ invalid_as_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "or_pattern 'as' expression")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use %s as pattern target" , _PyPegen_get_expr_name ( a ) ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25230,7 +25896,7 @@ invalid_class_pattern_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_class_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "name_or_attr '(' invalid_class_argument_pattern")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( PyPegen_first_item ( a , pattern_ty ) , PyPegen_last_item ( a , pattern_ty ) , "positional patterns follow keyword patterns" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25247,6 +25913,70 @@ invalid_class_pattern_rule(Parser *p) return _res; } +// invalid_mapping_pattern: +// | '{' [(items_pattern ',')] double_star_pattern ',' items_pattern ','? '}' +static void * +invalid_mapping_pattern_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // '{' [(items_pattern ',')] double_star_pattern ',' items_pattern ','? '}' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_mapping_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' [(items_pattern ',')] double_star_pattern ',' items_pattern ','? '}'")); + Token * _literal; + Token * _literal_1; + Token * _literal_2; + void *_opt_var; + UNUSED(_opt_var); // Silence compiler warnings + void *_opt_var_1; + UNUSED(_opt_var_1); // Silence compiler warnings + asdl_seq* items_pattern_var; + expr_ty rest; + if ( + (_literal = _PyPegen_expect_token(p, 25)) // token='{' + && + (_opt_var = _tmp_148_rule(p), !p->error_indicator) // [(items_pattern ',')] + && + (rest = double_star_pattern_rule(p)) // double_star_pattern + && + (_literal_1 = _PyPegen_expect_token(p, 12)) // token=',' + && + (items_pattern_var = items_pattern_rule(p)) // items_pattern + && + (_opt_var_1 = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? + && + (_literal_2 = _PyPegen_expect_token(p, 26)) // token='}' + ) + { + D(fprintf(stderr, "%*c+ invalid_mapping_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' [(items_pattern ',')] double_star_pattern ',' items_pattern ','? '}'")); + _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( rest , "double star pattern must be the last (right-most) subpattern in the mapping pattern" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_mapping_pattern[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{' [(items_pattern ',')] double_star_pattern ',' items_pattern ','? '}'")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // invalid_class_argument_pattern: // | [positional_patterns ','] keyword_patterns ',' positional_patterns static asdl_pattern_seq* @@ -25273,7 +26003,7 @@ invalid_class_argument_pattern_rule(Parser *p) asdl_pattern_seq* a; asdl_seq* keyword_patterns_var; if ( - (_opt_var = _tmp_147_rule(p), !p->error_indicator) // [positional_patterns ','] + (_opt_var = _tmp_149_rule(p), !p->error_indicator) // [positional_patterns ','] && (keyword_patterns_var = keyword_patterns_rule(p)) // keyword_patterns && @@ -25284,7 +26014,7 @@ invalid_class_argument_pattern_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_class_argument_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "[positional_patterns ','] keyword_patterns ',' positional_patterns")); _res = a; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25326,7 +26056,7 @@ invalid_if_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 687)) // token='if' + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -25335,7 +26065,7 @@ invalid_if_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_if_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' named_expression NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25357,7 +26087,7 @@ invalid_if_stmt_rule(Parser *p) expr_ty a_1; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 687)) // token='if' + (a = _PyPegen_expect_token(p, 698)) // token='if' && (a_1 = named_expression_rule(p)) // named_expression && @@ -25370,7 +26100,7 @@ invalid_if_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_if_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' named_expression ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'if' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25412,7 +26142,7 @@ invalid_elif_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 692)) // token='elif' + (_keyword = _PyPegen_expect_token(p, 703)) // token='elif' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -25421,7 +26151,7 @@ invalid_elif_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_elif_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'elif' named_expression NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25443,7 +26173,7 @@ invalid_elif_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 692)) // token='elif' + (a = _PyPegen_expect_token(p, 703)) // token='elif' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -25456,7 +26186,7 @@ invalid_elif_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_elif_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'elif' named_expression ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'elif' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25496,7 +26226,7 @@ invalid_else_stmt_rule(Parser *p) Token * a; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 691)) // token='else' + (a = _PyPegen_expect_token(p, 702)) // token='else' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && @@ -25507,7 +26237,7 @@ invalid_else_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_else_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else' ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'else' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25529,18 +26259,18 @@ invalid_else_stmt_rule(Parser *p) Token * _literal; asdl_stmt_seq* block_var; if ( - (_keyword = _PyPegen_expect_token(p, 691)) // token='else' + (_keyword = _PyPegen_expect_token(p, 702)) // token='else' && (_literal = _PyPegen_expect_token(p, 11)) // token=':' && (block_var = block_rule(p)) // block && - (_keyword_1 = _PyPegen_expect_token(p, 692)) // token='elif' + (_keyword_1 = _PyPegen_expect_token(p, 703)) // token='elif' ) { D(fprintf(stderr, "%*c+ invalid_else_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else' ':' block 'elif'")); _res = RAISE_SYNTAX_ERROR ( "'elif' block follows an 'else' block" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25582,7 +26312,7 @@ invalid_while_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 694)) // token='while' + (_keyword = _PyPegen_expect_token(p, 705)) // token='while' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -25591,7 +26321,7 @@ invalid_while_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_while_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'while' named_expression NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25613,7 +26343,7 @@ invalid_while_stmt_rule(Parser *p) expr_ty named_expression_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 694)) // token='while' + (a = _PyPegen_expect_token(p, 705)) // token='while' && (named_expression_var = named_expression_rule(p)) // named_expression && @@ -25626,7 +26356,7 @@ invalid_while_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_while_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'while' named_expression ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'while' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25672,13 +26402,13 @@ invalid_for_stmt_rule(Parser *p) expr_ty star_expressions_var; expr_ty star_targets_var; if ( - (_opt_var = _PyPegen_expect_token(p, 703), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 699)) // token='for' + (_keyword = _PyPegen_expect_token(p, 710)) // token='for' && (star_targets_var = star_targets_rule(p)) // star_targets && - (_keyword_1 = _PyPegen_expect_token(p, 700)) // token='in' + (_keyword_1 = _PyPegen_expect_token(p, 711)) // token='in' && (star_expressions_var = star_expressions_rule(p)) // star_expressions && @@ -25687,7 +26417,7 @@ invalid_for_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_for_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'for' star_targets 'in' star_expressions NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25713,13 +26443,13 @@ invalid_for_stmt_rule(Parser *p) expr_ty star_expressions_var; expr_ty star_targets_var; if ( - (_opt_var = _PyPegen_expect_token(p, 703), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 699)) // token='for' + (a = _PyPegen_expect_token(p, 710)) // token='for' && (star_targets_var = star_targets_rule(p)) // star_targets && - (_keyword = _PyPegen_expect_token(p, 700)) // token='in' + (_keyword = _PyPegen_expect_token(p, 711)) // token='in' && (star_expressions_var = star_expressions_rule(p)) // star_expressions && @@ -25732,7 +26462,7 @@ invalid_for_stmt_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_for_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'for' star_targets 'in' star_expressions ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'for' statement on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25785,9 +26515,9 @@ invalid_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (_opt_var = _PyPegen_expect_token(p, 703), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? && - (a = _PyPegen_expect_token(p, 704)) // token='def' + (a = _PyPegen_expect_token(p, 715)) // token='def' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -25810,7 +26540,7 @@ invalid_def_raw_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_def_raw[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'? 'def' NAME type_params? '(' params? ')' ['->' expression] ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after function definition on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25844,9 +26574,9 @@ invalid_def_raw_rule(Parser *p) asdl_stmt_seq* block_var; expr_ty name_var; if ( - (_opt_var = _PyPegen_expect_token(p, 703), !p->error_indicator) // 'async'? + (_opt_var = _PyPegen_expect_token(p, 714), !p->error_indicator) // 'async'? && - (_keyword = _PyPegen_expect_token(p, 704)) // token='def' + (_keyword = _PyPegen_expect_token(p, 715)) // token='def' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -25910,7 +26640,7 @@ invalid_class_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (_keyword = _PyPegen_expect_token(p, 706)) // token='class' + (_keyword = _PyPegen_expect_token(p, 717)) // token='class' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -25923,7 +26653,7 @@ invalid_class_def_raw_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_class_def_raw[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'class' NAME type_params? ['(' arguments? ')'] NEWLINE")); _res = RAISE_SYNTAX_ERROR ( "expected ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25949,7 +26679,7 @@ invalid_class_def_raw_rule(Parser *p) expr_ty name_var; Token * newline_var; if ( - (a = _PyPegen_expect_token(p, 706)) // token='class' + (a = _PyPegen_expect_token(p, 717)) // token='class' && (name_var = _PyPegen_name_token(p)) // NAME && @@ -25966,7 +26696,7 @@ invalid_class_def_raw_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_class_def_raw[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'class' NAME type_params? ['(' arguments? ')'] ':' NEWLINE !INDENT")); _res = RAISE_INDENTATION_ERROR ( "expected an indented block after class definition on line %d" , a -> lineno ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -25984,8 +26714,8 @@ invalid_class_def_raw_rule(Parser *p) } // invalid_double_starred_kvpairs: -// | ','.double_starred_kvpair+ ',' invalid_kvpair -// | expression ':' '*' bitwise_or +// | invalid_kvpair_unpacking ','? +// | ','.double_starred_kvpair+ ',' (invalid_kvpair | invalid_kvpair_unpacking) // | expression ':' &('}' | ',') static void * invalid_double_starred_kvpairs_rule(Parser *p) @@ -25999,40 +26729,209 @@ invalid_double_starred_kvpairs_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // ','.double_starred_kvpair+ ',' invalid_kvpair + { // invalid_kvpair_unpacking ','? + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_double_starred_kvpairs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_kvpair_unpacking ','?")); + void *_opt_var; + UNUSED(_opt_var); // Silence compiler warnings + void *invalid_kvpair_unpacking_var; + if ( + (invalid_kvpair_unpacking_var = invalid_kvpair_unpacking_rule(p)) // invalid_kvpair_unpacking + && + (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? + ) + { + D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_kvpair_unpacking ','?")); + _res = _PyPegen_dummy_name(p, invalid_kvpair_unpacking_var, _opt_var); + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_double_starred_kvpairs[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_kvpair_unpacking ','?")); + } + { // ','.double_starred_kvpair+ ',' (invalid_kvpair | invalid_kvpair_unpacking) if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_double_starred_kvpairs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ',' invalid_kvpair")); - asdl_seq * _gather_84_var; + D(fprintf(stderr, "%*c> invalid_double_starred_kvpairs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ',' (invalid_kvpair | invalid_kvpair_unpacking)")); + asdl_seq * _gather_86_var; Token * _literal; - void *invalid_kvpair_var; + void *_tmp_150_var; if ( - (_gather_84_var = _gather_84_rule(p)) // ','.double_starred_kvpair+ + (_gather_86_var = _gather_86_rule(p)) // ','.double_starred_kvpair+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (invalid_kvpair_var = invalid_kvpair_rule(p)) // invalid_kvpair + (_tmp_150_var = _tmp_150_rule(p)) // invalid_kvpair | invalid_kvpair_unpacking ) { - D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ',' invalid_kvpair")); - _res = _PyPegen_dummy_name(p, _gather_84_var, _literal, invalid_kvpair_var); + D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.double_starred_kvpair+ ',' (invalid_kvpair | invalid_kvpair_unpacking)")); + _res = _PyPegen_dummy_name(p, _gather_86_var, _literal, _tmp_150_var); goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s invalid_double_starred_kvpairs[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.double_starred_kvpair+ ',' invalid_kvpair")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.double_starred_kvpair+ ',' (invalid_kvpair | invalid_kvpair_unpacking)")); + } + { // expression ':' &('}' | ',') + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_double_starred_kvpairs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); + Token * a; + expr_ty expression_var; + if ( + (expression_var = expression_rule(p)) // expression + && + (a = _PyPegen_expect_token(p, 11)) // token=':' + && + _PyPegen_lookahead(1, _tmp_151_rule, p) + ) + { + D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); + _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "expression expected after dictionary key and ':'" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_double_starred_kvpairs[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ':' &('}' | ',')")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// invalid_kvpair_unpacking: +// | '**' if_expression +// | '*' bitwise_or ':' expression +// | '**' bitwise_or ':' expression +// | expression ':' '*' bitwise_or +// | expression ':' '**' bitwise_or +static void * +invalid_kvpair_unpacking_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // '**' if_expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_kvpair_unpacking[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**' if_expression")); + Token * a; + expr_ty b; + if ( + (a = _PyPegen_expect_token(p, 35)) // token='**' + && + (b = if_expression_rule(p)) // if_expression + ) + { + D(fprintf(stderr, "%*c+ invalid_kvpair_unpacking[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' if_expression")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "invalid double starred expression. Did you forget to wrap the conditional expression in parentheses?" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_kvpair_unpacking[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**' if_expression")); + } + { // '*' bitwise_or ':' expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_kvpair_unpacking[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' bitwise_or ':' expression")); + Token * _literal; + Token * a; + expr_ty b; + expr_ty expression_var; + if ( + (a = _PyPegen_expect_token(p, 16)) // token='*' + && + (b = bitwise_or_rule(p)) // bitwise_or + && + (_literal = _PyPegen_expect_token(p, 11)) // token=':' + && + (expression_var = expression_rule(p)) // expression + ) + { + D(fprintf(stderr, "%*c+ invalid_kvpair_unpacking[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' bitwise_or ':' expression")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot use a starred expression in a dictionary key" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_kvpair_unpacking[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*' bitwise_or ':' expression")); + } + { // '**' bitwise_or ':' expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_kvpair_unpacking[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**' bitwise_or ':' expression")); + Token * _literal; + Token * a; + expr_ty b; + expr_ty expression_var; + if ( + (a = _PyPegen_expect_token(p, 35)) // token='**' + && + (b = bitwise_or_rule(p)) // bitwise_or + && + (_literal = _PyPegen_expect_token(p, 11)) // token=':' + && + (expression_var = expression_rule(p)) // expression + ) + { + D(fprintf(stderr, "%*c+ invalid_kvpair_unpacking[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' bitwise_or ':' expression")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot use dict unpacking in a dictionary key" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_kvpair_unpacking[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**' bitwise_or ':' expression")); } { // expression ':' '*' bitwise_or if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_double_starred_kvpairs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ':' '*' bitwise_or")); + D(fprintf(stderr, "%*c> invalid_kvpair_unpacking[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ':' '*' bitwise_or")); Token * _literal; Token * a; - expr_ty bitwise_or_var; + expr_ty b; expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression @@ -26041,12 +26940,12 @@ invalid_double_starred_kvpairs_rule(Parser *p) && (a = _PyPegen_expect_token(p, 16)) // token='*' && - (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or + (b = bitwise_or_rule(p)) // bitwise_or ) { - D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' '*' bitwise_or")); - _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "cannot use a starred expression in a dictionary value" ); - if (_res == NULL && PyErr_Occurred()) { + D(fprintf(stderr, "%*c+ invalid_kvpair_unpacking[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' '*' bitwise_or")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot use a starred expression in a dictionary value" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26054,28 +26953,32 @@ invalid_double_starred_kvpairs_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s invalid_double_starred_kvpairs[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s invalid_kvpair_unpacking[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ':' '*' bitwise_or")); } - { // expression ':' &('}' | ',') + { // expression ':' '**' bitwise_or if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> invalid_double_starred_kvpairs[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); + D(fprintf(stderr, "%*c> invalid_kvpair_unpacking[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ':' '**' bitwise_or")); + Token * _literal; Token * a; + expr_ty b; expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression && - (a = _PyPegen_expect_token(p, 11)) // token=':' + (_literal = _PyPegen_expect_token(p, 11)) // token=':' && - _PyPegen_lookahead(1, _tmp_148_rule, p) + (a = _PyPegen_expect_token(p, 35)) // token='**' + && + (b = bitwise_or_rule(p)) // bitwise_or ) { - D(fprintf(stderr, "%*c+ invalid_double_starred_kvpairs[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); - _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "expression expected after dictionary key and ':'" ); - if (_res == NULL && PyErr_Occurred()) { + D(fprintf(stderr, "%*c+ invalid_kvpair_unpacking[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' '**' bitwise_or")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot use dict unpacking in a dictionary value" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26083,8 +26986,8 @@ invalid_double_starred_kvpairs_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s invalid_double_starred_kvpairs[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ':' &('}' | ',')")); + D(fprintf(stderr, "%*c%s invalid_kvpair_unpacking[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ':' '**' bitwise_or")); } _res = NULL; done: @@ -26095,6 +26998,7 @@ invalid_double_starred_kvpairs_rule(Parser *p) // invalid_kvpair: // | expression !(':') // | expression ':' '*' bitwise_or +// | expression ':' '**' bitwise_or // | expression ':' &('}' | ',') static void * invalid_kvpair_rule(Parser *p) @@ -26123,7 +27027,7 @@ invalid_kvpair_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_kvpair[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !(':')")); _res = RAISE_ERROR_KNOWN_LOCATION ( p , PyExc_SyntaxError , a -> lineno , a -> end_col_offset - 1 , a -> end_lineno , - 1 , "':' expected after dictionary key" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26156,7 +27060,7 @@ invalid_kvpair_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_kvpair[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' '*' bitwise_or")); _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "cannot use a starred expression in a dictionary value" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26167,6 +27071,39 @@ invalid_kvpair_rule(Parser *p) D(fprintf(stderr, "%*c%s invalid_kvpair[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ':' '*' bitwise_or")); } + { // expression ':' '**' bitwise_or + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_kvpair[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ':' '**' bitwise_or")); + Token * _literal; + Token * a; + expr_ty bitwise_or_var; + expr_ty expression_var; + if ( + (expression_var = expression_rule(p)) // expression + && + (_literal = _PyPegen_expect_token(p, 11)) // token=':' + && + (a = _PyPegen_expect_token(p, 35)) // token='**' + && + (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or + ) + { + D(fprintf(stderr, "%*c+ invalid_kvpair[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' '**' bitwise_or")); + _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( a , "cannot use dict unpacking in a dictionary value" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_kvpair[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ':' '**' bitwise_or")); + } { // expression ':' &('}' | ',') if (p->error_indicator) { p->level--; @@ -26180,12 +27117,12 @@ invalid_kvpair_rule(Parser *p) && (a = _PyPegen_expect_token(p, 11)) // token=':' && - _PyPegen_lookahead(1, _tmp_148_rule, p) + _PyPegen_lookahead(1, _tmp_151_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_kvpair[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ':' &('}' | ',')")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "expression expected after dictionary key and ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26202,7 +27139,7 @@ invalid_kvpair_rule(Parser *p) return _res; } -// invalid_starred_expression_unpacking: '*' expression '=' expression +// invalid_starred_expression_unpacking: '*' if_expression | '*' expression '=' expression static void * invalid_starred_expression_unpacking_rule(Parser *p) { @@ -26215,6 +27152,33 @@ invalid_starred_expression_unpacking_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; + { // '*' if_expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_starred_expression_unpacking[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*' if_expression")); + Token * a; + expr_ty b; + if ( + (a = _PyPegen_expect_token(p, 16)) // token='*' + && + (b = if_expression_rule(p)) // if_expression + ) + { + D(fprintf(stderr, "%*c+ invalid_starred_expression_unpacking[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' if_expression")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "invalid starred expression. Did you forget to wrap the conditional expression in parentheses?" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_starred_expression_unpacking[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*' if_expression")); + } { // '*' expression '=' expression if (p->error_indicator) { p->level--; @@ -26237,7 +27201,7 @@ invalid_starred_expression_unpacking_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_starred_expression_unpacking[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*' expression '=' expression")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "cannot assign to iterable argument unpacking" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26254,6 +27218,73 @@ invalid_starred_expression_unpacking_rule(Parser *p) return _res; } +// invalid_starred_expression_unpacking_sequence: +// | '**' bitwise_or +// | invalid_starred_expression_unpacking +static void * +invalid_starred_expression_unpacking_sequence_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // '**' bitwise_or + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_starred_expression_unpacking_sequence[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**' bitwise_or")); + Token * a; + expr_ty bitwise_or_var; + if ( + (a = _PyPegen_expect_token(p, 35)) // token='**' + && + (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or + ) + { + D(fprintf(stderr, "%*c+ invalid_starred_expression_unpacking_sequence[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**' bitwise_or")); + _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use dict unpacking here" ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_starred_expression_unpacking_sequence[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**' bitwise_or")); + } + if (p->call_invalid_rules) { // invalid_starred_expression_unpacking + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_starred_expression_unpacking_sequence[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_starred_expression_unpacking")); + void *invalid_starred_expression_unpacking_var; + if ( + (invalid_starred_expression_unpacking_var = invalid_starred_expression_unpacking_rule(p)) // invalid_starred_expression_unpacking + ) + { + D(fprintf(stderr, "%*c+ invalid_starred_expression_unpacking_sequence[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_starred_expression_unpacking")); + _res = invalid_starred_expression_unpacking_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_starred_expression_unpacking_sequence[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_starred_expression_unpacking")); + } + _res = NULL; + done: + p->level--; + return _res; +} + // invalid_starred_expression: '*' static void * invalid_starred_expression_rule(Parser *p) @@ -26280,7 +27311,7 @@ invalid_starred_expression_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_starred_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); _res = RAISE_SYNTAX_ERROR ( "Invalid star expression" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26337,7 +27368,7 @@ invalid_fstring_replacement_field_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '='")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "f-string: valid expression required before '='" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26364,7 +27395,7 @@ invalid_fstring_replacement_field_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '!'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "f-string: valid expression required before '!'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26391,7 +27422,7 @@ invalid_fstring_replacement_field_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' ':'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "f-string: valid expression required before ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26418,7 +27449,7 @@ invalid_fstring_replacement_field_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '}'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "f-string: valid expression required before '}'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26444,7 +27475,7 @@ invalid_fstring_replacement_field_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' !annotated_rhs")); _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting a valid expression after '{'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26468,12 +27499,12 @@ invalid_fstring_replacement_field_rule(Parser *p) && (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs && - _PyPegen_lookahead(0, _tmp_149_rule, p) + _PyPegen_lookahead(0, _tmp_152_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '=', or '!', or ':', or '}'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26500,12 +27531,12 @@ invalid_fstring_replacement_field_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 22)) // token='=' && - _PyPegen_lookahead(0, _tmp_150_rule, p) + _PyPegen_lookahead(0, _tmp_153_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '!', or ':', or '}'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26564,14 +27595,14 @@ invalid_fstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_154_rule(p), !p->error_indicator) // ['!' NAME] && - _PyPegen_lookahead(0, _tmp_152_rule, p) + _PyPegen_lookahead(0, _tmp_155_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting ':' or '}'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26590,7 +27621,7 @@ invalid_fstring_replacement_field_rule(Parser *p) D(fprintf(stderr, "%*c> invalid_fstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'")); Token * _literal; Token * _literal_1; - asdl_seq * _loop0_76_var; + asdl_seq * _loop0_78_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings void *_opt_var_1; @@ -26603,18 +27634,18 @@ invalid_fstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_154_rule(p), !p->error_indicator) // ['!' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && - (_loop0_76_var = _loop0_76_rule(p)) // fstring_format_spec* + (_loop0_78_var = _loop0_78_rule(p)) // fstring_format_spec* && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26) // token='}' ) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '}', or format specs" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26644,14 +27675,14 @@ invalid_fstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_154_rule(p), !p->error_indicator) // ['!' NAME] && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26) // token='}' ) { D(fprintf(stderr, "%*c+ invalid_fstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: expecting '}'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26691,12 +27722,12 @@ invalid_fstring_conversion_character_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 54)) // token='!' && - _PyPegen_lookahead(1, _tmp_152_rule, p) + _PyPegen_lookahead(1, _tmp_155_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_fstring_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')")); _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: missing conversion character" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26722,7 +27753,7 @@ invalid_fstring_conversion_character_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_fstring_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' !NAME")); _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "f-string: invalid conversion character" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26779,7 +27810,7 @@ invalid_tstring_replacement_field_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '='")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "t-string: valid expression required before '='" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26806,7 +27837,7 @@ invalid_tstring_replacement_field_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '!'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "t-string: valid expression required before '!'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26833,7 +27864,7 @@ invalid_tstring_replacement_field_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' ':'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "t-string: valid expression required before ':'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26860,7 +27891,7 @@ invalid_tstring_replacement_field_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' '}'")); _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "t-string: valid expression required before '}'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26886,7 +27917,7 @@ invalid_tstring_replacement_field_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' !annotated_rhs")); _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting a valid expression after '{'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26910,12 +27941,12 @@ invalid_tstring_replacement_field_rule(Parser *p) && (annotated_rhs_var = annotated_rhs_rule(p)) // annotated_rhs && - _PyPegen_lookahead(0, _tmp_149_rule, p) + _PyPegen_lookahead(0, _tmp_152_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs !('=' | '!' | ':' | '}')")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting '=', or '!', or ':', or '}'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -26942,12 +27973,12 @@ invalid_tstring_replacement_field_rule(Parser *p) && (_literal_1 = _PyPegen_expect_token(p, 22)) // token='=' && - _PyPegen_lookahead(0, _tmp_150_rule, p) + _PyPegen_lookahead(0, _tmp_153_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '=' !('!' | ':' | '}')")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting '!', or ':', or '}'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27006,14 +28037,14 @@ invalid_tstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_154_rule(p), !p->error_indicator) // ['!' NAME] && - _PyPegen_lookahead(0, _tmp_152_rule, p) + _PyPegen_lookahead(0, _tmp_155_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !(':' | '}')")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting ':' or '}'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27032,7 +28063,7 @@ invalid_tstring_replacement_field_rule(Parser *p) D(fprintf(stderr, "%*c> invalid_tstring_replacement_field[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'")); Token * _literal; Token * _literal_1; - asdl_seq * _loop0_76_var; + asdl_seq * _loop0_78_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings void *_opt_var_1; @@ -27045,18 +28076,18 @@ invalid_tstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_154_rule(p), !p->error_indicator) // ['!' NAME] && (_literal_1 = _PyPegen_expect_token(p, 11)) // token=':' && - (_loop0_76_var = _loop0_76_rule(p)) // fstring_format_spec* + (_loop0_78_var = _loop0_78_rule(p)) // fstring_format_spec* && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26) // token='}' ) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] ':' fstring_format_spec* !'}'")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting '}', or format specs" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27086,14 +28117,14 @@ invalid_tstring_replacement_field_rule(Parser *p) && (_opt_var = _PyPegen_expect_token(p, 22), !p->error_indicator) // '='? && - (_opt_var_1 = _tmp_151_rule(p), !p->error_indicator) // ['!' NAME] + (_opt_var_1 = _tmp_154_rule(p), !p->error_indicator) // ['!' NAME] && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 26) // token='}' ) { D(fprintf(stderr, "%*c+ invalid_tstring_replacement_field[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{' annotated_rhs '='? ['!' NAME] !'}'")); _res = PyErr_Occurred ( ) ? NULL : RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: expecting '}'" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27133,12 +28164,12 @@ invalid_tstring_conversion_character_rule(Parser *p) if ( (_literal = _PyPegen_expect_token(p, 54)) // token='!' && - _PyPegen_lookahead(1, _tmp_152_rule, p) + _PyPegen_lookahead(1, _tmp_155_rule, p) ) { D(fprintf(stderr, "%*c+ invalid_tstring_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' &(':' | '}')")); _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: missing conversion character" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27164,7 +28195,7 @@ invalid_tstring_conversion_character_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_tstring_conversion_character[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' !NAME")); _res = RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN ( "t-string: invalid conversion character" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27205,14 +28236,14 @@ invalid_string_tstring_concat_rule(Parser *p) asdl_seq * a; expr_ty b; if ( - (a = _loop1_80_rule(p)) // ((fstring | string))+ + (a = _loop1_82_rule(p)) // ((fstring | string))+ && (b = (expr_ty)tstring_rule(p)) // tstring ) { D(fprintf(stderr, "%*c+ invalid_string_tstring_concat[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "((fstring | string))+ tstring")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( PyPegen_last_item ( a , expr_ty ) , b , "cannot mix t-string literals with string or bytes literals" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27232,14 +28263,14 @@ invalid_string_tstring_concat_rule(Parser *p) asdl_seq * a; expr_ty b; if ( - (a = _loop1_81_rule(p)) // tstring+ + (a = _loop1_83_rule(p)) // tstring+ && - (b = (expr_ty)_tmp_153_rule(p)) // fstring | string + (b = (expr_ty)_tmp_156_rule(p)) // fstring | string ) { D(fprintf(stderr, "%*c+ invalid_string_tstring_concat[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tstring+ (fstring | string)")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( PyPegen_last_item ( a , expr_ty ) , b , "cannot mix t-string literals with string or bytes literals" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27275,23 +28306,23 @@ invalid_arithmetic_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_arithmetic[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "sum ('+' | '-' | '*' | '/' | '%' | '//' | '@') 'not' inversion")); - void *_tmp_154_var; + void *_tmp_157_var; Token * a; expr_ty b; expr_ty sum_var; if ( (sum_var = sum_rule(p)) // sum && - (_tmp_154_var = _tmp_154_rule(p)) // '+' | '-' | '*' | '/' | '%' | '//' | '@' + (_tmp_157_var = _tmp_157_rule(p)) // '+' | '-' | '*' | '/' | '%' | '//' | '@' && - (a = _PyPegen_expect_token(p, 708)) // token='not' + (a = _PyPegen_expect_token(p, 719)) // token='not' && (b = inversion_rule(p)) // inversion ) { D(fprintf(stderr, "%*c+ invalid_arithmetic[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "sum ('+' | '-' | '*' | '/' | '%' | '//' | '@') 'not' inversion")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "'not' after an operator must be parenthesized" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27327,20 +28358,20 @@ invalid_factor_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> invalid_factor[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('+' | '-' | '~') 'not' factor")); - void *_tmp_155_var; + void *_tmp_158_var; Token * a; expr_ty b; if ( - (_tmp_155_var = _tmp_155_rule(p)) // '+' | '-' | '~' + (_tmp_158_var = _tmp_158_rule(p)) // '+' | '-' | '~' && - (a = _PyPegen_expect_token(p, 708)) // token='not' + (a = _PyPegen_expect_token(p, 719)) // token='not' && (b = factor_rule(p)) // factor ) { D(fprintf(stderr, "%*c+ invalid_factor[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "('+' | '-' | '~') 'not' factor")); _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "'not' after an operator must be parenthesized" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27386,7 +28417,7 @@ invalid_type_params_rule(Parser *p) { D(fprintf(stderr, "%*c+ invalid_type_params[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'[' ']'")); _res = RAISE_SYNTAX_ERROR_STARTING_FROM ( token , "Type parameter list cannot be empty" ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -27579,7 +28610,7 @@ _loop0_3_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -27659,7 +28690,7 @@ _gather_4_rule(Parser *p) return _res; } -// _tmp_5: 'import' | 'from' +// _tmp_5: 'import' | 'from' | "lazy" static void * _tmp_5_rule(Parser *p) { @@ -27680,7 +28711,7 @@ _tmp_5_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_5[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'import'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 639)) // token='import' + (_keyword = _PyPegen_expect_token(p, 647)) // token='import' ) { D(fprintf(stderr, "%*c+ _tmp_5[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'import'")); @@ -27699,7 +28730,7 @@ _tmp_5_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_5[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'from'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 638)) // token='from' + (_keyword = _PyPegen_expect_token(p, 646)) // token='from' ) { D(fprintf(stderr, "%*c+ _tmp_5[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'from'")); @@ -27710,6 +28741,25 @@ _tmp_5_rule(Parser *p) D(fprintf(stderr, "%*c%s _tmp_5[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'from'")); } + { // "lazy" + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_5[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "\"lazy\"")); + expr_ty _keyword; + if ( + (_keyword = _PyPegen_expect_soft_keyword(p, "lazy")) // soft_keyword='"lazy"' + ) + { + D(fprintf(stderr, "%*c+ _tmp_5[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"lazy\"")); + _res = _keyword; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_5[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "\"lazy\"")); + } _res = NULL; done: p->level--; @@ -27737,7 +28787,7 @@ _tmp_6_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_6[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'def'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 704)) // token='def' + (_keyword = _PyPegen_expect_token(p, 715)) // token='def' ) { D(fprintf(stderr, "%*c+ _tmp_6[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'def'")); @@ -27775,7 +28825,7 @@ _tmp_6_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_6[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 703)) // token='async' + (_keyword = _PyPegen_expect_token(p, 714)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_6[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -27813,7 +28863,7 @@ _tmp_7_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_7[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'class'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 706)) // token='class' + (_keyword = _PyPegen_expect_token(p, 717)) // token='class' ) { D(fprintf(stderr, "%*c+ _tmp_7[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'class'")); @@ -27870,7 +28920,7 @@ _tmp_8_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_8[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'with'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 652)) // token='with' + (_keyword = _PyPegen_expect_token(p, 663)) // token='with' ) { D(fprintf(stderr, "%*c+ _tmp_8[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'with'")); @@ -27889,7 +28939,7 @@ _tmp_8_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_8[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 703)) // token='async' + (_keyword = _PyPegen_expect_token(p, 714)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_8[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -27927,7 +28977,7 @@ _tmp_9_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_9[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'for'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 699)) // token='for' + (_keyword = _PyPegen_expect_token(p, 710)) // token='for' ) { D(fprintf(stderr, "%*c+ _tmp_9[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'for'")); @@ -27946,7 +28996,7 @@ _tmp_9_rule(Parser *p) D(fprintf(stderr, "%*c> _tmp_9[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'async'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 703)) // token='async' + (_keyword = _PyPegen_expect_token(p, 714)) // token='async' ) { D(fprintf(stderr, "%*c+ _tmp_9[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'async'")); @@ -27992,7 +29042,7 @@ _tmp_10_rule(Parser *p) { D(fprintf(stderr, "%*c+ _tmp_10[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'=' annotated_rhs")); _res = d; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -28041,7 +29091,7 @@ _tmp_11_rule(Parser *p) { D(fprintf(stderr, "%*c+ _tmp_11[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' single_target ')'")); _res = b; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -28105,12 +29155,12 @@ _loop1_12_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_12[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_156_var; + void *_tmp_159_var; while ( - (_tmp_156_var = _tmp_156_rule(p)) // star_targets '=' + (_tmp_159_var = _tmp_159_rule(p)) // star_targets '=' ) { - _res = _tmp_156_var; + _res = _tmp_159_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -28186,7 +29236,7 @@ _loop0_13_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -28352,7 +29402,7 @@ _tmp_16_rule(Parser *p) { D(fprintf(stderr, "%*c+ _tmp_16[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' expression")); _res = z; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -28397,12 +29447,12 @@ _loop0_17_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_17[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')")); - void *_tmp_157_var; + void *_tmp_160_var; while ( - (_tmp_157_var = _tmp_157_rule(p)) // '.' | '...' + (_tmp_160_var = _tmp_160_rule(p)) // '.' | '...' ) { - _res = _tmp_157_var; + _res = _tmp_160_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -28464,12 +29514,12 @@ _loop1_18_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_18[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('.' | '...')")); - void *_tmp_157_var; + void *_tmp_160_var; while ( - (_tmp_157_var = _tmp_157_rule(p)) // '.' | '...' + (_tmp_160_var = _tmp_160_rule(p)) // '.' | '...' ) { - _res = _tmp_157_var; + _res = _tmp_160_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -28545,7 +29595,7 @@ _loop0_19_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -28647,14 +29697,14 @@ _tmp_21_rule(Parser *p) Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword = _PyPegen_expect_token(p, 696)) // token='as' && (z = _PyPegen_name_token(p)) // NAME ) { D(fprintf(stderr, "%*c+ _tmp_21[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' NAME")); _res = z; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -28708,7 +29758,7 @@ _loop0_22_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -28816,12 +29866,12 @@ _loop1_24_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_24[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('@' named_expression NEWLINE)")); - void *_tmp_158_var; + void *_tmp_161_var; while ( - (_tmp_158_var = _tmp_158_rule(p)) // '@' named_expression NEWLINE + (_tmp_161_var = _tmp_161_rule(p)) // '@' named_expression NEWLINE ) { - _res = _tmp_158_var; + _res = _tmp_161_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -28892,7 +29942,7 @@ _tmp_25_rule(Parser *p) { D(fprintf(stderr, "%*c+ _tmp_25[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' arguments? ')'")); _res = z; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -28938,7 +29988,7 @@ _tmp_26_rule(Parser *p) { D(fprintf(stderr, "%*c+ _tmp_26[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'->' expression")); _res = z; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -29409,7 +30459,7 @@ _loop0_33_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -29818,7 +30868,7 @@ _loop0_39_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -30144,7 +31194,7 @@ _loop0_44_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -30261,7 +31311,7 @@ _loop0_46_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -30435,7 +31485,7 @@ _loop0_49_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -30552,7 +31602,7 @@ _loop0_51_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -30669,7 +31719,7 @@ _loop0_53_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -30849,12 +31899,12 @@ _loop1_56_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_56[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_expression)")); - void *_tmp_159_var; + void *_tmp_162_var; while ( - (_tmp_159_var = _tmp_159_rule(p)) // ',' star_expression + (_tmp_162_var = _tmp_162_rule(p)) // ',' star_expression ) { - _res = _tmp_159_var; + _res = _tmp_162_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -30930,7 +31980,7 @@ _loop0_57_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -31010,9 +32060,126 @@ _gather_58_rule(Parser *p) return _res; } -// _loop1_59: ('or' conjunction) +// _loop0_59: ',' star_named_expression_sequence static asdl_seq * -_loop1_59_rule(Parser *p) +_loop0_59_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void *_res = NULL; + int _mark = p->mark; + void **_children = PyMem_Malloc(sizeof(void *)); + if (!_children) { + p->error_indicator = 1; + PyErr_NoMemory(); + p->level--; + return NULL; + } + Py_ssize_t _children_capacity = 1; + Py_ssize_t _n = 0; + { // ',' star_named_expression_sequence + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _loop0_59[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_named_expression_sequence")); + Token * _literal; + expr_ty elem; + while ( + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + && + (elem = star_named_expression_sequence_rule(p)) // star_named_expression_sequence + ) + { + _res = elem; + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + PyMem_Free(_children); + p->level--; + return NULL; + } + if (_n == _children_capacity) { + _children_capacity *= 2; + void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); + if (!_new_children) { + PyMem_Free(_children); + p->error_indicator = 1; + PyErr_NoMemory(); + p->level--; + return NULL; + } + _children = _new_children; + } + _children[_n++] = _res; + _mark = p->mark; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _loop0_59[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_named_expression_sequence")); + } + asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); + if (!_seq) { + PyMem_Free(_children); + p->error_indicator = 1; + PyErr_NoMemory(); + p->level--; + return NULL; + } + for (Py_ssize_t i = 0; i < _n; i++) asdl_seq_SET_UNTYPED(_seq, i, _children[i]); + PyMem_Free(_children); + p->level--; + return _seq; +} + +// _gather_60: star_named_expression_sequence _loop0_59 +static asdl_seq * +_gather_60_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + asdl_seq * _res = NULL; + int _mark = p->mark; + { // star_named_expression_sequence _loop0_59 + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _gather_60[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression_sequence _loop0_59")); + expr_ty elem; + asdl_seq * seq; + if ( + (elem = star_named_expression_sequence_rule(p)) // star_named_expression_sequence + && + (seq = _loop0_59_rule(p)) // _loop0_59 + ) + { + D(fprintf(stderr, "%*c+ _gather_60[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression_sequence _loop0_59")); + _res = _PyPegen_seq_insert_in_front(p, elem, seq); + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _gather_60[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression_sequence _loop0_59")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _loop1_61: ('or' conjunction) +static asdl_seq * +_loop1_61_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31037,13 +32204,13 @@ _loop1_59_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_59[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('or' conjunction)")); - void *_tmp_160_var; + D(fprintf(stderr, "%*c> _loop1_61[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('or' conjunction)")); + void *_tmp_163_var; while ( - (_tmp_160_var = _tmp_160_rule(p)) // 'or' conjunction + (_tmp_163_var = _tmp_163_rule(p)) // 'or' conjunction ) { - _res = _tmp_160_var; + _res = _tmp_163_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -31060,7 +32227,7 @@ _loop1_59_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_59[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_61[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('or' conjunction)")); } if (_n == 0 || p->error_indicator) { @@ -31082,9 +32249,9 @@ _loop1_59_rule(Parser *p) return _seq; } -// _loop1_60: ('and' inversion) +// _loop1_62: ('and' inversion) static asdl_seq * -_loop1_60_rule(Parser *p) +_loop1_62_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31109,13 +32276,13 @@ _loop1_60_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_60[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('and' inversion)")); - void *_tmp_161_var; + D(fprintf(stderr, "%*c> _loop1_62[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('and' inversion)")); + void *_tmp_164_var; while ( - (_tmp_161_var = _tmp_161_rule(p)) // 'and' inversion + (_tmp_164_var = _tmp_164_rule(p)) // 'and' inversion ) { - _res = _tmp_161_var; + _res = _tmp_164_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -31132,7 +32299,7 @@ _loop1_60_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_60[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_62[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('and' inversion)")); } if (_n == 0 || p->error_indicator) { @@ -31154,9 +32321,9 @@ _loop1_60_rule(Parser *p) return _seq; } -// _loop1_61: compare_op_bitwise_or_pair +// _loop1_63: compare_op_bitwise_or_pair static asdl_seq * -_loop1_61_rule(Parser *p) +_loop1_63_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31181,7 +32348,7 @@ _loop1_61_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_61[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "compare_op_bitwise_or_pair")); + D(fprintf(stderr, "%*c> _loop1_63[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "compare_op_bitwise_or_pair")); CmpopExprPair* compare_op_bitwise_or_pair_var; while ( (compare_op_bitwise_or_pair_var = compare_op_bitwise_or_pair_rule(p)) // compare_op_bitwise_or_pair @@ -31204,7 +32371,7 @@ _loop1_61_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_61[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_63[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "compare_op_bitwise_or_pair")); } if (_n == 0 || p->error_indicator) { @@ -31226,9 +32393,9 @@ _loop1_61_rule(Parser *p) return _seq; } -// _tmp_62: '!=' +// _tmp_64: '!=' static void * -_tmp_62_rule(Parser *p) +_tmp_64_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31244,15 +32411,15 @@ _tmp_62_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_62[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!='")); + D(fprintf(stderr, "%*c> _tmp_64[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!='")); Token * tok; if ( (tok = _PyPegen_expect_token(p, 28)) // token='!=' ) { - D(fprintf(stderr, "%*c+ _tmp_62[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!='")); + D(fprintf(stderr, "%*c+ _tmp_64[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!='")); _res = _PyPegen_check_barry_as_flufl ( p , tok ) ? NULL : tok; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -31260,7 +32427,7 @@ _tmp_62_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_62[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_64[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!='")); } _res = NULL; @@ -31269,9 +32436,9 @@ _tmp_62_rule(Parser *p) return _res; } -// _loop0_63: ',' (slice | starred_expression) +// _loop0_65: ',' (slice | starred_expression) static asdl_seq * -_loop0_63_rule(Parser *p) +_loop0_65_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31296,17 +32463,17 @@ _loop0_63_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_63[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (slice | starred_expression)")); + D(fprintf(stderr, "%*c> _loop0_65[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (slice | starred_expression)")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_162_rule(p)) // slice | starred_expression + (elem = _tmp_165_rule(p)) // slice | starred_expression ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -31328,7 +32495,7 @@ _loop0_63_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_63[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_65[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (slice | starred_expression)")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -31345,9 +32512,9 @@ _loop0_63_rule(Parser *p) return _seq; } -// _gather_64: (slice | starred_expression) _loop0_63 +// _gather_66: (slice | starred_expression) _loop0_65 static asdl_seq * -_gather_64_rule(Parser *p) +_gather_66_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31358,27 +32525,27 @@ _gather_64_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (slice | starred_expression) _loop0_63 + { // (slice | starred_expression) _loop0_65 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_64[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(slice | starred_expression) _loop0_63")); + D(fprintf(stderr, "%*c> _gather_66[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(slice | starred_expression) _loop0_65")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_162_rule(p)) // slice | starred_expression + (elem = _tmp_165_rule(p)) // slice | starred_expression && - (seq = _loop0_63_rule(p)) // _loop0_63 + (seq = _loop0_65_rule(p)) // _loop0_65 ) { - D(fprintf(stderr, "%*c+ _gather_64[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(slice | starred_expression) _loop0_63")); + D(fprintf(stderr, "%*c+ _gather_66[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(slice | starred_expression) _loop0_65")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_64[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(slice | starred_expression) _loop0_63")); + D(fprintf(stderr, "%*c%s _gather_66[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(slice | starred_expression) _loop0_65")); } _res = NULL; done: @@ -31386,9 +32553,9 @@ _gather_64_rule(Parser *p) return _res; } -// _tmp_65: ':' expression? +// _tmp_67: ':' expression? static void * -_tmp_65_rule(Parser *p) +_tmp_67_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31404,7 +32571,7 @@ _tmp_65_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_65[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':' expression?")); + D(fprintf(stderr, "%*c> _tmp_67[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':' expression?")); Token * _literal; void *d; if ( @@ -31413,9 +32580,9 @@ _tmp_65_rule(Parser *p) (d = expression_rule(p), !p->error_indicator) // expression? ) { - D(fprintf(stderr, "%*c+ _tmp_65[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' expression?")); + D(fprintf(stderr, "%*c+ _tmp_67[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':' expression?")); _res = d; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -31423,7 +32590,7 @@ _tmp_65_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_65[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_67[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':' expression?")); } _res = NULL; @@ -31432,9 +32599,9 @@ _tmp_65_rule(Parser *p) return _res; } -// _tmp_66: tuple | group | genexp +// _tmp_68: genexp | tuple | group static void * -_tmp_66_rule(Parser *p) +_tmp_68_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31445,62 +32612,62 @@ _tmp_66_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // tuple + { // genexp if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_66[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple")); - expr_ty tuple_var; + D(fprintf(stderr, "%*c> _tmp_68[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp")); + expr_ty genexp_var; if ( - (tuple_var = tuple_rule(p)) // tuple + (genexp_var = genexp_rule(p)) // genexp ) { - D(fprintf(stderr, "%*c+ _tmp_66[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple")); - _res = tuple_var; + D(fprintf(stderr, "%*c+ _tmp_68[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp")); + _res = genexp_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_66[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tuple")); + D(fprintf(stderr, "%*c%s _tmp_68[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "genexp")); } - { // group + { // tuple if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_66[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "group")); - expr_ty group_var; + D(fprintf(stderr, "%*c> _tmp_68[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple")); + expr_ty tuple_var; if ( - (group_var = group_rule(p)) // group + (tuple_var = tuple_rule(p)) // tuple ) { - D(fprintf(stderr, "%*c+ _tmp_66[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "group")); - _res = group_var; + D(fprintf(stderr, "%*c+ _tmp_68[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple")); + _res = tuple_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_66[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "group")); + D(fprintf(stderr, "%*c%s _tmp_68[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tuple")); } - { // genexp + { // group if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_66[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp")); - expr_ty genexp_var; + D(fprintf(stderr, "%*c> _tmp_68[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "group")); + expr_ty group_var; if ( - (genexp_var = genexp_rule(p)) // genexp + (group_var = group_rule(p)) // group ) { - D(fprintf(stderr, "%*c+ _tmp_66[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp")); - _res = genexp_var; + D(fprintf(stderr, "%*c+ _tmp_68[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "group")); + _res = group_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_66[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "genexp")); + D(fprintf(stderr, "%*c%s _tmp_68[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "group")); } _res = NULL; done: @@ -31508,9 +32675,9 @@ _tmp_66_rule(Parser *p) return _res; } -// _tmp_67: list | listcomp +// _tmp_69: listcomp | list static void * -_tmp_67_rule(Parser *p) +_tmp_69_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31521,43 +32688,43 @@ _tmp_67_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // list + { // listcomp if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_67[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list")); - expr_ty list_var; + D(fprintf(stderr, "%*c> _tmp_69[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "listcomp")); + expr_ty listcomp_var; if ( - (list_var = list_rule(p)) // list + (listcomp_var = listcomp_rule(p)) // listcomp ) { - D(fprintf(stderr, "%*c+ _tmp_67[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list")); - _res = list_var; + D(fprintf(stderr, "%*c+ _tmp_69[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "listcomp")); + _res = listcomp_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_67[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "list")); + D(fprintf(stderr, "%*c%s _tmp_69[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "listcomp")); } - { // listcomp + { // list if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_67[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "listcomp")); - expr_ty listcomp_var; + D(fprintf(stderr, "%*c> _tmp_69[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list")); + expr_ty list_var; if ( - (listcomp_var = listcomp_rule(p)) // listcomp + (list_var = list_rule(p)) // list ) { - D(fprintf(stderr, "%*c+ _tmp_67[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "listcomp")); - _res = listcomp_var; + D(fprintf(stderr, "%*c+ _tmp_69[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list")); + _res = list_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_67[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "listcomp")); + D(fprintf(stderr, "%*c%s _tmp_69[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "list")); } _res = NULL; done: @@ -31565,9 +32732,9 @@ _tmp_67_rule(Parser *p) return _res; } -// _tmp_68: dict | set | dictcomp | setcomp +// _tmp_70: dictcomp | setcomp | dict | set static void * -_tmp_68_rule(Parser *p) +_tmp_70_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31578,81 +32745,81 @@ _tmp_68_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // dict + { // dictcomp if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_68[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dict")); - expr_ty dict_var; + D(fprintf(stderr, "%*c> _tmp_70[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dictcomp")); + expr_ty dictcomp_var; if ( - (dict_var = dict_rule(p)) // dict + (dictcomp_var = dictcomp_rule(p)) // dictcomp ) { - D(fprintf(stderr, "%*c+ _tmp_68[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dict")); - _res = dict_var; + D(fprintf(stderr, "%*c+ _tmp_70[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dictcomp")); + _res = dictcomp_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_68[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dict")); + D(fprintf(stderr, "%*c%s _tmp_70[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dictcomp")); } - { // set + { // setcomp if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_68[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "set")); - expr_ty set_var; + D(fprintf(stderr, "%*c> _tmp_70[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "setcomp")); + expr_ty setcomp_var; if ( - (set_var = set_rule(p)) // set + (setcomp_var = setcomp_rule(p)) // setcomp ) { - D(fprintf(stderr, "%*c+ _tmp_68[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "set")); - _res = set_var; + D(fprintf(stderr, "%*c+ _tmp_70[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "setcomp")); + _res = setcomp_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_68[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "set")); + D(fprintf(stderr, "%*c%s _tmp_70[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "setcomp")); } - { // dictcomp + { // dict if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_68[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dictcomp")); - expr_ty dictcomp_var; + D(fprintf(stderr, "%*c> _tmp_70[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dict")); + expr_ty dict_var; if ( - (dictcomp_var = dictcomp_rule(p)) // dictcomp + (dict_var = dict_rule(p)) // dict ) { - D(fprintf(stderr, "%*c+ _tmp_68[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dictcomp")); - _res = dictcomp_var; + D(fprintf(stderr, "%*c+ _tmp_70[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dict")); + _res = dict_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_68[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dictcomp")); + D(fprintf(stderr, "%*c%s _tmp_70[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dict")); } - { // setcomp + { // set if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_68[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "setcomp")); - expr_ty setcomp_var; + D(fprintf(stderr, "%*c> _tmp_70[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "set")); + expr_ty set_var; if ( - (setcomp_var = setcomp_rule(p)) // setcomp + (set_var = set_rule(p)) // set ) { - D(fprintf(stderr, "%*c+ _tmp_68[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "setcomp")); - _res = setcomp_var; + D(fprintf(stderr, "%*c+ _tmp_70[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "set")); + _res = set_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_68[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "setcomp")); + D(fprintf(stderr, "%*c%s _tmp_70[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "set")); } _res = NULL; done: @@ -31660,9 +32827,9 @@ _tmp_68_rule(Parser *p) return _res; } -// _tmp_69: yield_expr | named_expression +// _tmp_71: yield_expr | named_expression static void * -_tmp_69_rule(Parser *p) +_tmp_71_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31678,18 +32845,18 @@ _tmp_69_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_69[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); + D(fprintf(stderr, "%*c> _tmp_71[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "yield_expr")); expr_ty yield_expr_var; if ( (yield_expr_var = yield_expr_rule(p)) // yield_expr ) { - D(fprintf(stderr, "%*c+ _tmp_69[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); + D(fprintf(stderr, "%*c+ _tmp_71[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "yield_expr")); _res = yield_expr_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_69[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_71[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "yield_expr")); } { // named_expression @@ -31697,18 +32864,18 @@ _tmp_69_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_69[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "named_expression")); + D(fprintf(stderr, "%*c> _tmp_71[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "named_expression")); expr_ty named_expression_var; if ( (named_expression_var = named_expression_rule(p)) // named_expression ) { - D(fprintf(stderr, "%*c+ _tmp_69[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "named_expression")); + D(fprintf(stderr, "%*c+ _tmp_71[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "named_expression")); _res = named_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_69[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_71[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "named_expression")); } _res = NULL; @@ -31717,9 +32884,9 @@ _tmp_69_rule(Parser *p) return _res; } -// _loop0_70: lambda_param_no_default +// _loop0_72: lambda_param_no_default static asdl_seq * -_loop0_70_rule(Parser *p) +_loop0_72_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31744,7 +32911,7 @@ _loop0_70_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_70[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _loop0_72[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; while ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default @@ -31767,7 +32934,7 @@ _loop0_70_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_70[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_72[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -31784,9 +32951,9 @@ _loop0_70_rule(Parser *p) return _seq; } -// _loop0_71: lambda_param_with_default +// _loop0_73: lambda_param_with_default static asdl_seq * -_loop0_71_rule(Parser *p) +_loop0_73_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31811,7 +32978,7 @@ _loop0_71_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_71[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default")); + D(fprintf(stderr, "%*c> _loop0_73[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default")); NameDefaultPair* lambda_param_with_default_var; while ( (lambda_param_with_default_var = lambda_param_with_default_rule(p)) // lambda_param_with_default @@ -31834,7 +33001,7 @@ _loop0_71_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_71[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_73[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -31851,9 +33018,9 @@ _loop0_71_rule(Parser *p) return _seq; } -// _loop1_72: lambda_param_no_default +// _loop1_74: lambda_param_no_default static asdl_seq * -_loop1_72_rule(Parser *p) +_loop1_74_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31878,7 +33045,7 @@ _loop1_72_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_72[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _loop1_74[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; while ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default @@ -31901,7 +33068,7 @@ _loop1_72_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_72[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_74[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } if (_n == 0 || p->error_indicator) { @@ -31923,9 +33090,9 @@ _loop1_72_rule(Parser *p) return _seq; } -// _loop1_73: lambda_param_with_default +// _loop1_75: lambda_param_with_default static asdl_seq * -_loop1_73_rule(Parser *p) +_loop1_75_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -31950,7 +33117,7 @@ _loop1_73_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_73[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default")); + D(fprintf(stderr, "%*c> _loop1_75[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_with_default")); NameDefaultPair* lambda_param_with_default_var; while ( (lambda_param_with_default_var = lambda_param_with_default_rule(p)) // lambda_param_with_default @@ -31973,7 +33140,7 @@ _loop1_73_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_73[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_75[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_with_default")); } if (_n == 0 || p->error_indicator) { @@ -31995,9 +33162,9 @@ _loop1_73_rule(Parser *p) return _seq; } -// _loop0_74: lambda_param_maybe_default +// _loop0_76: lambda_param_maybe_default static asdl_seq * -_loop0_74_rule(Parser *p) +_loop0_76_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32022,7 +33189,7 @@ _loop0_74_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_74[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop0_76[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -32045,7 +33212,7 @@ _loop0_74_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_74[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_76[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32062,9 +33229,9 @@ _loop0_74_rule(Parser *p) return _seq; } -// _loop1_75: lambda_param_maybe_default +// _loop1_77: lambda_param_maybe_default static asdl_seq * -_loop1_75_rule(Parser *p) +_loop1_77_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32089,7 +33256,7 @@ _loop1_75_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_75[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); + D(fprintf(stderr, "%*c> _loop1_77[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_maybe_default")); NameDefaultPair* lambda_param_maybe_default_var; while ( (lambda_param_maybe_default_var = lambda_param_maybe_default_rule(p)) // lambda_param_maybe_default @@ -32112,7 +33279,7 @@ _loop1_75_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_75[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_77[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default")); } if (_n == 0 || p->error_indicator) { @@ -32134,9 +33301,9 @@ _loop1_75_rule(Parser *p) return _seq; } -// _loop0_76: fstring_format_spec +// _loop0_78: fstring_format_spec static asdl_seq * -_loop0_76_rule(Parser *p) +_loop0_78_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32161,7 +33328,7 @@ _loop0_76_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_76[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring_format_spec")); + D(fprintf(stderr, "%*c> _loop0_78[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring_format_spec")); expr_ty fstring_format_spec_var; while ( (fstring_format_spec_var = fstring_format_spec_rule(p)) // fstring_format_spec @@ -32184,7 +33351,7 @@ _loop0_76_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_76[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_78[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "fstring_format_spec")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32201,9 +33368,9 @@ _loop0_76_rule(Parser *p) return _seq; } -// _loop0_77: fstring_middle +// _loop0_79: fstring_middle static asdl_seq * -_loop0_77_rule(Parser *p) +_loop0_79_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32228,7 +33395,7 @@ _loop0_77_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_77[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring_middle")); + D(fprintf(stderr, "%*c> _loop0_79[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring_middle")); expr_ty fstring_middle_var; while ( (fstring_middle_var = fstring_middle_rule(p)) // fstring_middle @@ -32251,7 +33418,7 @@ _loop0_77_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_77[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_79[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "fstring_middle")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32268,9 +33435,9 @@ _loop0_77_rule(Parser *p) return _seq; } -// _loop0_78: tstring_format_spec +// _loop0_80: tstring_format_spec static asdl_seq * -_loop0_78_rule(Parser *p) +_loop0_80_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32295,7 +33462,7 @@ _loop0_78_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_78[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring_format_spec")); + D(fprintf(stderr, "%*c> _loop0_80[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring_format_spec")); expr_ty tstring_format_spec_var; while ( (tstring_format_spec_var = tstring_format_spec_rule(p)) // tstring_format_spec @@ -32318,7 +33485,7 @@ _loop0_78_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_78[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_80[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tstring_format_spec")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32335,9 +33502,9 @@ _loop0_78_rule(Parser *p) return _seq; } -// _loop0_79: tstring_middle +// _loop0_81: tstring_middle static asdl_seq * -_loop0_79_rule(Parser *p) +_loop0_81_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32362,7 +33529,7 @@ _loop0_79_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_79[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring_middle")); + D(fprintf(stderr, "%*c> _loop0_81[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring_middle")); expr_ty tstring_middle_var; while ( (tstring_middle_var = tstring_middle_rule(p)) // tstring_middle @@ -32385,7 +33552,7 @@ _loop0_79_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_79[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_81[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tstring_middle")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32402,9 +33569,9 @@ _loop0_79_rule(Parser *p) return _seq; } -// _loop1_80: (fstring | string) +// _loop1_82: (fstring | string) static asdl_seq * -_loop1_80_rule(Parser *p) +_loop1_82_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32429,13 +33596,13 @@ _loop1_80_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_80[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(fstring | string)")); - void *_tmp_153_var; + D(fprintf(stderr, "%*c> _loop1_82[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(fstring | string)")); + void *_tmp_156_var; while ( - (_tmp_153_var = _tmp_153_rule(p)) // fstring | string + (_tmp_156_var = _tmp_156_rule(p)) // fstring | string ) { - _res = _tmp_153_var; + _res = _tmp_156_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -32452,7 +33619,7 @@ _loop1_80_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_80[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_82[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(fstring | string)")); } if (_n == 0 || p->error_indicator) { @@ -32474,9 +33641,9 @@ _loop1_80_rule(Parser *p) return _seq; } -// _loop1_81: tstring +// _loop1_83: tstring static asdl_seq * -_loop1_81_rule(Parser *p) +_loop1_83_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32501,7 +33668,7 @@ _loop1_81_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_81[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring")); + D(fprintf(stderr, "%*c> _loop1_83[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tstring")); expr_ty tstring_var; while ( (tstring_var = tstring_rule(p)) // tstring @@ -32524,7 +33691,7 @@ _loop1_81_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_81[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_83[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tstring")); } if (_n == 0 || p->error_indicator) { @@ -32546,9 +33713,9 @@ _loop1_81_rule(Parser *p) return _seq; } -// _tmp_82: star_named_expression ',' star_named_expressions? +// _tmp_84: star_named_expression_sequence ',' star_named_expressions_sequence? static void * -_tmp_82_rule(Parser *p) +_tmp_84_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32559,26 +33726,26 @@ _tmp_82_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // star_named_expression ',' star_named_expressions? + { // star_named_expression_sequence ',' star_named_expressions_sequence? if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_82[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?")); + D(fprintf(stderr, "%*c> _tmp_84[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expression_sequence ',' star_named_expressions_sequence?")); Token * _literal; expr_ty y; void *z; if ( - (y = star_named_expression_rule(p)) // star_named_expression + (y = star_named_expression_sequence_rule(p)) // star_named_expression_sequence && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (z = star_named_expressions_rule(p), !p->error_indicator) // star_named_expressions? + (z = star_named_expressions_sequence_rule(p), !p->error_indicator) // star_named_expressions_sequence? ) { - D(fprintf(stderr, "%*c+ _tmp_82[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression ',' star_named_expressions?")); + D(fprintf(stderr, "%*c+ _tmp_84[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_named_expression_sequence ',' star_named_expressions_sequence?")); _res = _PyPegen_seq_insert_in_front ( p , y , z ); - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -32586,8 +33753,8 @@ _tmp_82_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_82[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression ',' star_named_expressions?")); + D(fprintf(stderr, "%*c%s _tmp_84[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expression_sequence ',' star_named_expressions_sequence?")); } _res = NULL; done: @@ -32595,9 +33762,9 @@ _tmp_82_rule(Parser *p) return _res; } -// _loop0_83: ',' double_starred_kvpair +// _loop0_85: ',' double_starred_kvpair static asdl_seq * -_loop0_83_rule(Parser *p) +_loop0_85_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32622,7 +33789,7 @@ _loop0_83_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_83[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair")); + D(fprintf(stderr, "%*c> _loop0_85[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' double_starred_kvpair")); Token * _literal; KeyValuePair* elem; while ( @@ -32632,7 +33799,7 @@ _loop0_83_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -32654,7 +33821,7 @@ _loop0_83_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_83[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_85[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' double_starred_kvpair")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32671,9 +33838,9 @@ _loop0_83_rule(Parser *p) return _seq; } -// _gather_84: double_starred_kvpair _loop0_83 +// _gather_86: double_starred_kvpair _loop0_85 static asdl_seq * -_gather_84_rule(Parser *p) +_gather_86_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32684,27 +33851,27 @@ _gather_84_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // double_starred_kvpair _loop0_83 + { // double_starred_kvpair _loop0_85 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_84[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_83")); + D(fprintf(stderr, "%*c> _gather_86[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_85")); KeyValuePair* elem; asdl_seq * seq; if ( (elem = double_starred_kvpair_rule(p)) // double_starred_kvpair && - (seq = _loop0_83_rule(p)) // _loop0_83 + (seq = _loop0_85_rule(p)) // _loop0_85 ) { - D(fprintf(stderr, "%*c+ _gather_84[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_83")); + D(fprintf(stderr, "%*c+ _gather_86[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "double_starred_kvpair _loop0_85")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_84[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_83")); + D(fprintf(stderr, "%*c%s _gather_86[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "double_starred_kvpair _loop0_85")); } _res = NULL; done: @@ -32712,9 +33879,9 @@ _gather_84_rule(Parser *p) return _res; } -// _loop1_85: for_if_clause +// _loop1_87: for_if_clause static asdl_seq * -_loop1_85_rule(Parser *p) +_loop1_87_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32739,7 +33906,7 @@ _loop1_85_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_85[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause")); + D(fprintf(stderr, "%*c> _loop1_87[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "for_if_clause")); comprehension_ty for_if_clause_var; while ( (for_if_clause_var = for_if_clause_rule(p)) // for_if_clause @@ -32762,7 +33929,7 @@ _loop1_85_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_85[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_87[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "for_if_clause")); } if (_n == 0 || p->error_indicator) { @@ -32784,9 +33951,9 @@ _loop1_85_rule(Parser *p) return _seq; } -// _loop0_86: ('if' disjunction) +// _loop0_88: ('if' disjunction) static asdl_seq * -_loop0_86_rule(Parser *p) +_loop0_88_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32811,13 +33978,13 @@ _loop0_86_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_86[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); - void *_tmp_163_var; + D(fprintf(stderr, "%*c> _loop0_88[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); + void *_tmp_166_var; while ( - (_tmp_163_var = _tmp_163_rule(p)) // 'if' disjunction + (_tmp_166_var = _tmp_166_rule(p)) // 'if' disjunction ) { - _res = _tmp_163_var; + _res = _tmp_166_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -32834,7 +34001,7 @@ _loop0_86_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_86[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_88[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "('if' disjunction)")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32851,9 +34018,9 @@ _loop0_86_rule(Parser *p) return _seq; } -// _tmp_87: assignment_expression | expression !':=' +// _tmp_89: assignment_expression | expression !':=' | starred_expression static void * -_tmp_87_rule(Parser *p) +_tmp_89_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32869,18 +34036,18 @@ _tmp_87_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_87[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + D(fprintf(stderr, "%*c> _tmp_89[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); expr_ty assignment_expression_var; if ( (assignment_expression_var = assignment_expression_rule(p)) // assignment_expression ) { - D(fprintf(stderr, "%*c+ _tmp_87[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + D(fprintf(stderr, "%*c+ _tmp_89[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); _res = assignment_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_87[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_89[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "assignment_expression")); } { // expression !':=' @@ -32888,7 +34055,7 @@ _tmp_87_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_87[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); + D(fprintf(stderr, "%*c> _tmp_89[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression @@ -32896,23 +34063,42 @@ _tmp_87_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 53) // token=':=' ) { - D(fprintf(stderr, "%*c+ _tmp_87[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); + D(fprintf(stderr, "%*c+ _tmp_89[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); _res = expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_87[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_89[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='")); } + { // starred_expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_89[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + expr_ty starred_expression_var; + if ( + (starred_expression_var = starred_expression_rule(p)) // starred_expression + ) + { + D(fprintf(stderr, "%*c+ _tmp_89[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + _res = starred_expression_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_89[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); + } _res = NULL; done: p->level--; return _res; } -// _loop0_88: ',' (starred_expression | (assignment_expression | expression !':=') !'=') +// _loop0_90: ',' (starred_expression | (assignment_expression | expression !':=') !'=') static asdl_seq * -_loop0_88_rule(Parser *p) +_loop0_90_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -32937,17 +34123,17 @@ _loop0_88_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_88[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); + D(fprintf(stderr, "%*c> _loop0_90[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_164_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_167_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -32969,7 +34155,7 @@ _loop0_88_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_88[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_90[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (starred_expression | (assignment_expression | expression !':=') !'=')")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -32986,10 +34172,10 @@ _loop0_88_rule(Parser *p) return _seq; } -// _gather_89: -// | (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_88 +// _gather_91: +// | (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_90 static asdl_seq * -_gather_89_rule(Parser *p) +_gather_91_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33000,27 +34186,27 @@ _gather_89_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_88 + { // (starred_expression | (assignment_expression | expression !':=') !'=') _loop0_90 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_89[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_88")); + D(fprintf(stderr, "%*c> _gather_91[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_90")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_164_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_167_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' && - (seq = _loop0_88_rule(p)) // _loop0_88 + (seq = _loop0_90_rule(p)) // _loop0_90 ) { - D(fprintf(stderr, "%*c+ _gather_89[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_88")); + D(fprintf(stderr, "%*c+ _gather_91[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_90")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_89[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_88")); + D(fprintf(stderr, "%*c%s _gather_91[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression | (assignment_expression | expression !':=') !'=') _loop0_90")); } _res = NULL; done: @@ -33028,9 +34214,9 @@ _gather_89_rule(Parser *p) return _res; } -// _tmp_90: ',' kwargs +// _tmp_92: ',' kwargs static void * -_tmp_90_rule(Parser *p) +_tmp_92_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33046,7 +34232,7 @@ _tmp_90_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_90[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwargs")); + D(fprintf(stderr, "%*c> _tmp_92[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwargs")); Token * _literal; asdl_seq* k; if ( @@ -33055,9 +34241,9 @@ _tmp_90_rule(Parser *p) (k = kwargs_rule(p)) // kwargs ) { - D(fprintf(stderr, "%*c+ _tmp_90[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' kwargs")); + D(fprintf(stderr, "%*c+ _tmp_92[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' kwargs")); _res = k; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -33065,7 +34251,7 @@ _tmp_90_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_90[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_92[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwargs")); } _res = NULL; @@ -33074,9 +34260,9 @@ _tmp_90_rule(Parser *p) return _res; } -// _loop0_91: ',' kwarg_or_starred +// _loop0_93: ',' kwarg_or_starred static asdl_seq * -_loop0_91_rule(Parser *p) +_loop0_93_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33101,7 +34287,7 @@ _loop0_91_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_91[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred")); + D(fprintf(stderr, "%*c> _loop0_93[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_starred")); Token * _literal; KeywordOrStarred* elem; while ( @@ -33111,7 +34297,7 @@ _loop0_91_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -33133,7 +34319,7 @@ _loop0_91_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_91[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_93[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_starred")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33150,9 +34336,9 @@ _loop0_91_rule(Parser *p) return _seq; } -// _gather_92: kwarg_or_starred _loop0_91 +// _gather_94: kwarg_or_starred _loop0_93 static asdl_seq * -_gather_92_rule(Parser *p) +_gather_94_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33163,27 +34349,27 @@ _gather_92_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // kwarg_or_starred _loop0_91 + { // kwarg_or_starred _loop0_93 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_92[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_91")); + D(fprintf(stderr, "%*c> _gather_94[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_93")); KeywordOrStarred* elem; asdl_seq * seq; if ( (elem = kwarg_or_starred_rule(p)) // kwarg_or_starred && - (seq = _loop0_91_rule(p)) // _loop0_91 + (seq = _loop0_93_rule(p)) // _loop0_93 ) { - D(fprintf(stderr, "%*c+ _gather_92[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_91")); + D(fprintf(stderr, "%*c+ _gather_94[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_starred _loop0_93")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_92[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_91")); + D(fprintf(stderr, "%*c%s _gather_94[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_starred _loop0_93")); } _res = NULL; done: @@ -33191,9 +34377,9 @@ _gather_92_rule(Parser *p) return _res; } -// _loop0_93: ',' kwarg_or_double_starred +// _loop0_95: ',' kwarg_or_double_starred static asdl_seq * -_loop0_93_rule(Parser *p) +_loop0_95_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33218,7 +34404,7 @@ _loop0_93_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_93[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred")); + D(fprintf(stderr, "%*c> _loop0_95[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' kwarg_or_double_starred")); Token * _literal; KeywordOrStarred* elem; while ( @@ -33228,7 +34414,7 @@ _loop0_93_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -33250,7 +34436,7 @@ _loop0_93_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_93[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_95[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' kwarg_or_double_starred")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33267,9 +34453,9 @@ _loop0_93_rule(Parser *p) return _seq; } -// _gather_94: kwarg_or_double_starred _loop0_93 +// _gather_96: kwarg_or_double_starred _loop0_95 static asdl_seq * -_gather_94_rule(Parser *p) +_gather_96_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33280,27 +34466,27 @@ _gather_94_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // kwarg_or_double_starred _loop0_93 + { // kwarg_or_double_starred _loop0_95 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_94[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_93")); + D(fprintf(stderr, "%*c> _gather_96[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_95")); KeywordOrStarred* elem; asdl_seq * seq; if ( (elem = kwarg_or_double_starred_rule(p)) // kwarg_or_double_starred && - (seq = _loop0_93_rule(p)) // _loop0_93 + (seq = _loop0_95_rule(p)) // _loop0_95 ) { - D(fprintf(stderr, "%*c+ _gather_94[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_93")); + D(fprintf(stderr, "%*c+ _gather_96[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwarg_or_double_starred _loop0_95")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_94[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_93")); + D(fprintf(stderr, "%*c%s _gather_96[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwarg_or_double_starred _loop0_95")); } _res = NULL; done: @@ -33308,9 +34494,9 @@ _gather_94_rule(Parser *p) return _res; } -// _loop0_95: (',' star_target) +// _loop0_97: (',' star_target) static asdl_seq * -_loop0_95_rule(Parser *p) +_loop0_97_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33335,13 +34521,13 @@ _loop0_95_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_95[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_165_var; + D(fprintf(stderr, "%*c> _loop0_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); + void *_tmp_168_var; while ( - (_tmp_165_var = _tmp_165_rule(p)) // ',' star_target + (_tmp_168_var = _tmp_168_rule(p)) // ',' star_target ) { - _res = _tmp_165_var; + _res = _tmp_168_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33358,7 +34544,7 @@ _loop0_95_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_95[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_97[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' star_target)")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33375,9 +34561,9 @@ _loop0_95_rule(Parser *p) return _seq; } -// _loop0_96: ',' star_target +// _loop0_98: ',' star_target static asdl_seq * -_loop0_96_rule(Parser *p) +_loop0_98_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33402,7 +34588,7 @@ _loop0_96_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_96[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c> _loop0_98[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); Token * _literal; expr_ty elem; while ( @@ -33412,7 +34598,7 @@ _loop0_96_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -33434,7 +34620,7 @@ _loop0_96_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_96[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_98[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33451,9 +34637,9 @@ _loop0_96_rule(Parser *p) return _seq; } -// _gather_97: star_target _loop0_96 +// _gather_99: star_target _loop0_98 static asdl_seq * -_gather_97_rule(Parser *p) +_gather_99_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33464,27 +34650,27 @@ _gather_97_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // star_target _loop0_96 + { // star_target _loop0_98 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_97[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_target _loop0_96")); + D(fprintf(stderr, "%*c> _gather_99[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_target _loop0_98")); expr_ty elem; asdl_seq * seq; if ( (elem = star_target_rule(p)) // star_target && - (seq = _loop0_96_rule(p)) // _loop0_96 + (seq = _loop0_98_rule(p)) // _loop0_98 ) { - D(fprintf(stderr, "%*c+ _gather_97[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target _loop0_96")); + D(fprintf(stderr, "%*c+ _gather_99[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_target _loop0_98")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_97[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_target _loop0_96")); + D(fprintf(stderr, "%*c%s _gather_99[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_target _loop0_98")); } _res = NULL; done: @@ -33492,9 +34678,9 @@ _gather_97_rule(Parser *p) return _res; } -// _loop1_98: (',' star_target) +// _loop1_100: (',' star_target) static asdl_seq * -_loop1_98_rule(Parser *p) +_loop1_100_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33519,13 +34705,13 @@ _loop1_98_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_98[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_165_var; + D(fprintf(stderr, "%*c> _loop1_100[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); + void *_tmp_168_var; while ( - (_tmp_165_var = _tmp_165_rule(p)) // ',' star_target + (_tmp_168_var = _tmp_168_rule(p)) // ',' star_target ) { - _res = _tmp_165_var; + _res = _tmp_168_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33542,7 +34728,7 @@ _loop1_98_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_98[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_100[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' star_target)")); } if (_n == 0 || p->error_indicator) { @@ -33564,9 +34750,9 @@ _loop1_98_rule(Parser *p) return _seq; } -// _tmp_99: !'*' star_target +// _tmp_101: !'*' star_target static void * -_tmp_99_rule(Parser *p) +_tmp_101_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33582,7 +34768,7 @@ _tmp_99_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_99[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); + D(fprintf(stderr, "%*c> _tmp_101[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); expr_ty star_target_var; if ( _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 16) // token='*' @@ -33590,12 +34776,12 @@ _tmp_99_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_99[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); + D(fprintf(stderr, "%*c+ _tmp_101[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!'*' star_target")); _res = star_target_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_99[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_101[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "!'*' star_target")); } _res = NULL; @@ -33604,9 +34790,9 @@ _tmp_99_rule(Parser *p) return _res; } -// _loop0_100: ',' del_target +// _loop0_102: ',' del_target static asdl_seq * -_loop0_100_rule(Parser *p) +_loop0_102_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33631,7 +34817,7 @@ _loop0_100_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_100[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' del_target")); + D(fprintf(stderr, "%*c> _loop0_102[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' del_target")); Token * _literal; expr_ty elem; while ( @@ -33641,7 +34827,7 @@ _loop0_100_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -33663,7 +34849,7 @@ _loop0_100_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_100[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_102[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' del_target")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33680,9 +34866,9 @@ _loop0_100_rule(Parser *p) return _seq; } -// _gather_101: del_target _loop0_100 +// _gather_103: del_target _loop0_102 static asdl_seq * -_gather_101_rule(Parser *p) +_gather_103_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33693,27 +34879,27 @@ _gather_101_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // del_target _loop0_100 + { // del_target _loop0_102 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_101[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "del_target _loop0_100")); + D(fprintf(stderr, "%*c> _gather_103[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "del_target _loop0_102")); expr_ty elem; asdl_seq * seq; if ( (elem = del_target_rule(p)) // del_target && - (seq = _loop0_100_rule(p)) // _loop0_100 + (seq = _loop0_102_rule(p)) // _loop0_102 ) { - D(fprintf(stderr, "%*c+ _gather_101[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "del_target _loop0_100")); + D(fprintf(stderr, "%*c+ _gather_103[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "del_target _loop0_102")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_101[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "del_target _loop0_100")); + D(fprintf(stderr, "%*c%s _gather_103[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "del_target _loop0_102")); } _res = NULL; done: @@ -33721,9 +34907,9 @@ _gather_101_rule(Parser *p) return _res; } -// _loop0_102: ',' expression +// _loop0_104: ',' expression static asdl_seq * -_loop0_102_rule(Parser *p) +_loop0_104_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33748,7 +34934,7 @@ _loop0_102_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_102[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); + D(fprintf(stderr, "%*c> _loop0_104[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' expression")); Token * _literal; expr_ty elem; while ( @@ -33758,7 +34944,7 @@ _loop0_102_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -33780,7 +34966,7 @@ _loop0_102_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_102[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_104[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' expression")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -33797,9 +34983,9 @@ _loop0_102_rule(Parser *p) return _seq; } -// _gather_103: expression _loop0_102 +// _gather_105: expression _loop0_104 static asdl_seq * -_gather_103_rule(Parser *p) +_gather_105_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33810,27 +34996,27 @@ _gather_103_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // expression _loop0_102 + { // expression _loop0_104 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_103[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_102")); + D(fprintf(stderr, "%*c> _gather_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression _loop0_104")); expr_ty elem; asdl_seq * seq; if ( (elem = expression_rule(p)) // expression && - (seq = _loop0_102_rule(p)) // _loop0_102 + (seq = _loop0_104_rule(p)) // _loop0_104 ) { - D(fprintf(stderr, "%*c+ _gather_103[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_102")); + D(fprintf(stderr, "%*c+ _gather_105[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression _loop0_104")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_103[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_102")); + D(fprintf(stderr, "%*c%s _gather_105[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression _loop0_104")); } _res = NULL; done: @@ -33838,9 +35024,9 @@ _gather_103_rule(Parser *p) return _res; } -// _tmp_104: NEWLINE INDENT +// _tmp_106: NEWLINE INDENT static void * -_tmp_104_rule(Parser *p) +_tmp_106_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33856,7 +35042,7 @@ _tmp_104_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_104[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT")); + D(fprintf(stderr, "%*c> _tmp_106[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT")); Token * indent_var; Token * newline_var; if ( @@ -33865,12 +35051,12 @@ _tmp_104_rule(Parser *p) (indent_var = _PyPegen_expect_token(p, INDENT)) // token='INDENT' ) { - D(fprintf(stderr, "%*c+ _tmp_104[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT")); + D(fprintf(stderr, "%*c+ _tmp_106[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE INDENT")); _res = _PyPegen_dummy_name(p, newline_var, indent_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_104[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_106[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE INDENT")); } _res = NULL; @@ -33879,11 +35065,11 @@ _tmp_104_rule(Parser *p) return _res; } -// _tmp_105: +// _tmp_107: // | (','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs) // | kwargs static void * -_tmp_105_rule(Parser *p) +_tmp_107_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33899,18 +35085,18 @@ _tmp_105_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); - void *_tmp_166_var; + D(fprintf(stderr, "%*c> _tmp_107[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); + void *_tmp_169_var; if ( - (_tmp_166_var = _tmp_166_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs + (_tmp_169_var = _tmp_169_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs ) { - D(fprintf(stderr, "%*c+ _tmp_105[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); - _res = _tmp_166_var; + D(fprintf(stderr, "%*c+ _tmp_107[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); + _res = _tmp_169_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_105[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_107[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); } { // kwargs @@ -33918,18 +35104,18 @@ _tmp_105_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwargs")); + D(fprintf(stderr, "%*c> _tmp_107[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "kwargs")); asdl_seq* kwargs_var; if ( (kwargs_var = kwargs_rule(p)) // kwargs ) { - D(fprintf(stderr, "%*c+ _tmp_105[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwargs")); + D(fprintf(stderr, "%*c+ _tmp_107[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "kwargs")); _res = kwargs_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_105[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_107[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "kwargs")); } _res = NULL; @@ -33938,9 +35124,9 @@ _tmp_105_rule(Parser *p) return _res; } -// _loop0_106: ',' (starred_expression !'=') +// _loop0_108: ',' (starred_expression !'=') static asdl_seq * -_loop0_106_rule(Parser *p) +_loop0_108_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -33965,17 +35151,17 @@ _loop0_106_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_106[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression !'=')")); + D(fprintf(stderr, "%*c> _loop0_108[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (starred_expression !'=')")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_167_rule(p)) // starred_expression !'=' + (elem = _tmp_170_rule(p)) // starred_expression !'=' ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -33997,7 +35183,7 @@ _loop0_106_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_106[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_108[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (starred_expression !'=')")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34014,9 +35200,9 @@ _loop0_106_rule(Parser *p) return _seq; } -// _gather_107: (starred_expression !'=') _loop0_106 +// _gather_109: (starred_expression !'=') _loop0_108 static asdl_seq * -_gather_107_rule(Parser *p) +_gather_109_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34027,27 +35213,27 @@ _gather_107_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (starred_expression !'=') _loop0_106 + { // (starred_expression !'=') _loop0_108 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_107[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_106")); + D(fprintf(stderr, "%*c> _gather_109[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_108")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_167_rule(p)) // starred_expression !'=' + (elem = _tmp_170_rule(p)) // starred_expression !'=' && - (seq = _loop0_106_rule(p)) // _loop0_106 + (seq = _loop0_108_rule(p)) // _loop0_108 ) { - D(fprintf(stderr, "%*c+ _gather_107[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_106")); + D(fprintf(stderr, "%*c+ _gather_109[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(starred_expression !'=') _loop0_108")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_107[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression !'=') _loop0_106")); + D(fprintf(stderr, "%*c%s _gather_109[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(starred_expression !'=') _loop0_108")); } _res = NULL; done: @@ -34055,9 +35241,9 @@ _gather_107_rule(Parser *p) return _res; } -// _tmp_108: args | expression for_if_clauses +// _tmp_110: args | expression for_if_clauses static void * -_tmp_108_rule(Parser *p) +_tmp_110_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34073,18 +35259,18 @@ _tmp_108_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_108[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args")); + D(fprintf(stderr, "%*c> _tmp_110[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args")); expr_ty args_var; if ( (args_var = args_rule(p)) // args ) { - D(fprintf(stderr, "%*c+ _tmp_108[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args")); + D(fprintf(stderr, "%*c+ _tmp_110[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args")); _res = args_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_108[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_110[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args")); } { // expression for_if_clauses @@ -34092,7 +35278,7 @@ _tmp_108_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_108[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); + D(fprintf(stderr, "%*c> _tmp_110[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); expr_ty expression_var; asdl_comprehension_seq* for_if_clauses_var; if ( @@ -34101,12 +35287,12 @@ _tmp_108_rule(Parser *p) (for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses ) { - D(fprintf(stderr, "%*c+ _tmp_108[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); + D(fprintf(stderr, "%*c+ _tmp_110[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses")); _res = _PyPegen_dummy_name(p, expression_var, for_if_clauses_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_108[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_110[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression for_if_clauses")); } _res = NULL; @@ -34115,9 +35301,9 @@ _tmp_108_rule(Parser *p) return _res; } -// _tmp_109: args ',' +// _tmp_111: args ',' static void * -_tmp_109_rule(Parser *p) +_tmp_111_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34133,7 +35319,7 @@ _tmp_109_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_109[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ','")); + D(fprintf(stderr, "%*c> _tmp_111[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "args ','")); Token * _literal; expr_ty args_var; if ( @@ -34142,12 +35328,12 @@ _tmp_109_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_109[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ','")); + D(fprintf(stderr, "%*c+ _tmp_111[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ','")); _res = _PyPegen_dummy_name(p, args_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_109[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_111[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "args ','")); } _res = NULL; @@ -34156,9 +35342,9 @@ _tmp_109_rule(Parser *p) return _res; } -// _tmp_110: ',' | ')' +// _tmp_112: ',' | ')' static void * -_tmp_110_rule(Parser *p) +_tmp_112_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34174,18 +35360,18 @@ _tmp_110_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_110[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_110[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_112[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_110[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_112[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // ')' @@ -34193,18 +35379,18 @@ _tmp_110_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_110[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_110[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_112[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_110[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_112[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } _res = NULL; @@ -34213,9 +35399,9 @@ _tmp_110_rule(Parser *p) return _res; } -// _tmp_111: 'True' | 'False' | 'None' +// _tmp_113: 'True' | 'False' | 'None' static void * -_tmp_111_rule(Parser *p) +_tmp_113_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34231,18 +35417,18 @@ _tmp_111_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_111[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c> _tmp_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 623)) // token='True' + (_keyword = _PyPegen_expect_token(p, 627)) // token='True' ) { - D(fprintf(stderr, "%*c+ _tmp_111[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c+ _tmp_113[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_111[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_113[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'True'")); } { // 'False' @@ -34250,18 +35436,18 @@ _tmp_111_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_111[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c> _tmp_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 625)) // token='False' + (_keyword = _PyPegen_expect_token(p, 629)) // token='False' ) { - D(fprintf(stderr, "%*c+ _tmp_111[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c+ _tmp_113[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_111[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_113[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'False'")); } { // 'None' @@ -34269,18 +35455,18 @@ _tmp_111_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_111[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c> _tmp_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 624)) // token='None' + (_keyword = _PyPegen_expect_token(p, 628)) // token='None' ) { - D(fprintf(stderr, "%*c+ _tmp_111[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c+ _tmp_113[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_111[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_113[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'None'")); } _res = NULL; @@ -34289,9 +35475,9 @@ _tmp_111_rule(Parser *p) return _res; } -// _tmp_112: NAME '=' +// _tmp_114: NAME '=' static void * -_tmp_112_rule(Parser *p) +_tmp_114_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34307,7 +35493,7 @@ _tmp_112_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_112[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME '='")); + D(fprintf(stderr, "%*c> _tmp_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME '='")); Token * _literal; expr_ty name_var; if ( @@ -34316,12 +35502,12 @@ _tmp_112_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_112[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '='")); + D(fprintf(stderr, "%*c+ _tmp_114[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME '='")); _res = _PyPegen_dummy_name(p, name_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_112[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_114[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME '='")); } _res = NULL; @@ -34330,9 +35516,9 @@ _tmp_112_rule(Parser *p) return _res; } -// _loop1_113: (!STRING expression_without_invalid) +// _loop1_115: (!STRING expression_without_invalid) static asdl_seq * -_loop1_113_rule(Parser *p) +_loop1_115_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34357,13 +35543,13 @@ _loop1_113_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop1_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(!STRING expression_without_invalid)")); - void *_tmp_168_var; + D(fprintf(stderr, "%*c> _loop1_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(!STRING expression_without_invalid)")); + void *_tmp_171_var; while ( - (_tmp_168_var = _tmp_168_rule(p)) // !STRING expression_without_invalid + (_tmp_171_var = _tmp_171_rule(p)) // !STRING expression_without_invalid ) { - _res = _tmp_168_var; + _res = _tmp_171_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -34380,7 +35566,7 @@ _loop1_113_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop1_113[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop1_115[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(!STRING expression_without_invalid)")); } if (_n == 0 || p->error_indicator) { @@ -34402,9 +35588,9 @@ _loop1_113_rule(Parser *p) return _seq; } -// _tmp_114: NAME STRING | SOFT_KEYWORD +// _tmp_116: NAME STRING | SOFT_KEYWORD static void * -_tmp_114_rule(Parser *p) +_tmp_116_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34420,7 +35606,7 @@ _tmp_114_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME STRING")); + D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME STRING")); expr_ty name_var; expr_ty string_var; if ( @@ -34429,12 +35615,12 @@ _tmp_114_rule(Parser *p) (string_var = _PyPegen_string_token(p)) // STRING ) { - D(fprintf(stderr, "%*c+ _tmp_114[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME STRING")); + D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME STRING")); _res = _PyPegen_dummy_name(p, name_var, string_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_114[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME STRING")); } { // SOFT_KEYWORD @@ -34442,18 +35628,18 @@ _tmp_114_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_114[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); + D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); expr_ty soft_keyword_var; if ( (soft_keyword_var = _PyPegen_soft_keyword_token(p)) // SOFT_KEYWORD ) { - D(fprintf(stderr, "%*c+ _tmp_114[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); + D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "SOFT_KEYWORD")); _res = soft_keyword_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_114[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "SOFT_KEYWORD")); } _res = NULL; @@ -34462,9 +35648,9 @@ _tmp_114_rule(Parser *p) return _res; } -// _tmp_115: 'else' | ':' +// _tmp_117: 'else' | ':' static void * -_tmp_115_rule(Parser *p) +_tmp_117_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34480,18 +35666,18 @@ _tmp_115_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'")); + D(fprintf(stderr, "%*c> _tmp_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'else'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 691)) // token='else' + (_keyword = _PyPegen_expect_token(p, 702)) // token='else' ) { - D(fprintf(stderr, "%*c+ _tmp_115[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'")); + D(fprintf(stderr, "%*c+ _tmp_117[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'else'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_115[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_117[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'else'")); } { // ':' @@ -34499,18 +35685,18 @@ _tmp_115_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_115[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_115[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_117[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_115[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_117[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } _res = NULL; @@ -34519,9 +35705,9 @@ _tmp_115_rule(Parser *p) return _res; } -// _tmp_116: pass_stmt | break_stmt | continue_stmt +// _tmp_118: pass_stmt | break_stmt | continue_stmt static void * -_tmp_116_rule(Parser *p) +_tmp_118_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34537,18 +35723,18 @@ _tmp_116_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "pass_stmt")); + D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "pass_stmt")); stmt_ty pass_stmt_var; if ( (pass_stmt_var = pass_stmt_rule(p)) // pass_stmt ) { - D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "pass_stmt")); + D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "pass_stmt")); _res = pass_stmt_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "pass_stmt")); } { // break_stmt @@ -34556,18 +35742,18 @@ _tmp_116_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "break_stmt")); + D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "break_stmt")); stmt_ty break_stmt_var; if ( (break_stmt_var = break_stmt_rule(p)) // break_stmt ) { - D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "break_stmt")); + D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "break_stmt")); _res = break_stmt_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "break_stmt")); } { // continue_stmt @@ -34575,18 +35761,18 @@ _tmp_116_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_116[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "continue_stmt")); + D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "continue_stmt")); stmt_ty continue_stmt_var; if ( (continue_stmt_var = continue_stmt_rule(p)) // continue_stmt ) { - D(fprintf(stderr, "%*c+ _tmp_116[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "continue_stmt")); + D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "continue_stmt")); _res = continue_stmt_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_116[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "continue_stmt")); } _res = NULL; @@ -34595,9 +35781,9 @@ _tmp_116_rule(Parser *p) return _res; } -// _tmp_117: '=' | ':=' +// _tmp_119: '=' | ':=' static void * -_tmp_117_rule(Parser *p) +_tmp_119_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34613,18 +35799,18 @@ _tmp_117_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c> _tmp_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_117[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c+ _tmp_119[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_117[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_119[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='")); } { // ':=' @@ -34632,18 +35818,18 @@ _tmp_117_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_117[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='")); + D(fprintf(stderr, "%*c> _tmp_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 53)) // token=':=' ) { - D(fprintf(stderr, "%*c+ _tmp_117[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='")); + D(fprintf(stderr, "%*c+ _tmp_119[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_117[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_119[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':='")); } _res = NULL; @@ -34652,9 +35838,9 @@ _tmp_117_rule(Parser *p) return _res; } -// _tmp_118: list | tuple | genexp | 'True' | 'None' | 'False' +// _tmp_120: list | tuple | genexp | 'True' | 'None' | 'False' static void * -_tmp_118_rule(Parser *p) +_tmp_120_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34670,18 +35856,18 @@ _tmp_118_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list")); + D(fprintf(stderr, "%*c> _tmp_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "list")); expr_ty list_var; if ( (list_var = list_rule(p)) // list ) { - D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list")); + D(fprintf(stderr, "%*c+ _tmp_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "list")); _res = list_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_120[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "list")); } { // tuple @@ -34689,18 +35875,18 @@ _tmp_118_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple")); + D(fprintf(stderr, "%*c> _tmp_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "tuple")); expr_ty tuple_var; if ( (tuple_var = tuple_rule(p)) // tuple ) { - D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple")); + D(fprintf(stderr, "%*c+ _tmp_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "tuple")); _res = tuple_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_120[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "tuple")); } { // genexp @@ -34708,18 +35894,18 @@ _tmp_118_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp")); + D(fprintf(stderr, "%*c> _tmp_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "genexp")); expr_ty genexp_var; if ( (genexp_var = genexp_rule(p)) // genexp ) { - D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp")); + D(fprintf(stderr, "%*c+ _tmp_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "genexp")); _res = genexp_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_120[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "genexp")); } { // 'True' @@ -34727,18 +35913,18 @@ _tmp_118_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c> _tmp_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'True'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 623)) // token='True' + (_keyword = _PyPegen_expect_token(p, 627)) // token='True' ) { - D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); + D(fprintf(stderr, "%*c+ _tmp_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'True'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_120[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'True'")); } { // 'None' @@ -34746,18 +35932,18 @@ _tmp_118_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c> _tmp_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'None'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 624)) // token='None' + (_keyword = _PyPegen_expect_token(p, 628)) // token='None' ) { - D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); + D(fprintf(stderr, "%*c+ _tmp_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'None'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_120[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'None'")); } { // 'False' @@ -34765,18 +35951,18 @@ _tmp_118_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_118[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c> _tmp_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'False'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 625)) // token='False' + (_keyword = _PyPegen_expect_token(p, 629)) // token='False' ) { - D(fprintf(stderr, "%*c+ _tmp_118[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); + D(fprintf(stderr, "%*c+ _tmp_120[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'False'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_118[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_120[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'False'")); } _res = NULL; @@ -34785,9 +35971,9 @@ _tmp_118_rule(Parser *p) return _res; } -// _loop0_119: star_named_expressions +// _loop0_121: star_named_expressions static asdl_seq * -_loop0_119_rule(Parser *p) +_loop0_121_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34812,7 +35998,7 @@ _loop0_119_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_119[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions")); + D(fprintf(stderr, "%*c> _loop0_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_named_expressions")); asdl_expr_seq* star_named_expressions_var; while ( (star_named_expressions_var = star_named_expressions_rule(p)) // star_named_expressions @@ -34835,7 +36021,7 @@ _loop0_119_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_119[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_121[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_named_expressions")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34852,9 +36038,9 @@ _loop0_119_rule(Parser *p) return _seq; } -// _loop0_120: (star_targets '=') +// _loop0_122: (star_targets '=') static asdl_seq * -_loop0_120_rule(Parser *p) +_loop0_122_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -34879,13 +36065,13 @@ _loop0_120_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_120[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); - void *_tmp_156_var; + D(fprintf(stderr, "%*c> _loop0_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(star_targets '=')")); + void *_tmp_159_var; while ( - (_tmp_156_var = _tmp_156_rule(p)) // star_targets '=' + (_tmp_159_var = _tmp_159_rule(p)) // star_targets '=' ) { - _res = _tmp_156_var; + _res = _tmp_159_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -34902,7 +36088,7 @@ _loop0_120_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_120[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_122[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(star_targets '=')")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -34919,85 +36105,9 @@ _loop0_120_rule(Parser *p) return _seq; } -// _tmp_121: '[' | '(' | '{' +// _tmp_123: '[' | '{' static void * -_tmp_121_rule(Parser *p) -{ - if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // '[' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 9)) // token='[' - ) - { - D(fprintf(stderr, "%*c+ _tmp_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_121[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['")); - } - { // '(' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'('")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 7)) // token='(' - ) - { - D(fprintf(stderr, "%*c+ _tmp_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'('")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_121[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'('")); - } - { // '{' - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_121[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); - Token * _literal; - if ( - (_literal = _PyPegen_expect_token(p, 25)) // token='{' - ) - { - D(fprintf(stderr, "%*c+ _tmp_121[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); - _res = _literal; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_121[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_122: '[' | '{' -static void * -_tmp_122_rule(Parser *p) +_tmp_123_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35013,18 +36123,18 @@ _tmp_122_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c> _tmp_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'['")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 9)) // token='[' ) { - D(fprintf(stderr, "%*c+ _tmp_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); + D(fprintf(stderr, "%*c+ _tmp_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'['")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_122[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_123[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'['")); } { // '{' @@ -35032,18 +36142,18 @@ _tmp_122_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_122[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c> _tmp_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'{'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 25)) // token='{' ) { - D(fprintf(stderr, "%*c+ _tmp_122[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); + D(fprintf(stderr, "%*c+ _tmp_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'{'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_122[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_123[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'{'")); } _res = NULL; @@ -35052,9 +36162,9 @@ _tmp_122_rule(Parser *p) return _res; } -// _tmp_123: slash_no_default | slash_with_default +// _tmp_124: slash_no_default | slash_with_default static void * -_tmp_123_rule(Parser *p) +_tmp_124_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35070,18 +36180,18 @@ _tmp_123_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default")); + D(fprintf(stderr, "%*c> _tmp_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_no_default")); asdl_arg_seq* slash_no_default_var; if ( (slash_no_default_var = slash_no_default_rule(p)) // slash_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default")); + D(fprintf(stderr, "%*c+ _tmp_124[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_no_default")); _res = slash_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_123[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_124[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_no_default")); } { // slash_with_default @@ -35089,18 +36199,18 @@ _tmp_123_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_123[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slash_with_default")); SlashWithDefault* slash_with_default_var; if ( (slash_with_default_var = slash_with_default_rule(p)) // slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_123[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_124[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slash_with_default")); _res = slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_123[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_124[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slash_with_default")); } _res = NULL; @@ -35109,9 +36219,9 @@ _tmp_123_rule(Parser *p) return _res; } -// _tmp_124: ',' | param_no_default +// _tmp_125: ',' | param_no_default static void * -_tmp_124_rule(Parser *p) +_tmp_125_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35127,18 +36237,18 @@ _tmp_124_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_124[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_124[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // param_no_default @@ -35146,18 +36256,18 @@ _tmp_124_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_124[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; if ( (param_no_default_var = param_no_default_rule(p)) // param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_124[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); _res = param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_124[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } _res = NULL; @@ -35166,9 +36276,9 @@ _tmp_124_rule(Parser *p) return _res; } -// _tmp_125: ')' | ',' +// _tmp_126: ')' | ',' static void * -_tmp_125_rule(Parser *p) +_tmp_126_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35184,18 +36294,18 @@ _tmp_125_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // ',' @@ -35203,18 +36313,18 @@ _tmp_125_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_125[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_125[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_125[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -35223,9 +36333,9 @@ _tmp_125_rule(Parser *p) return _res; } -// _tmp_126: ')' | ',' (')' | '**') +// _tmp_127: ')' | ',' (')' | '**') static void * -_tmp_126_rule(Parser *p) +_tmp_127_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35241,18 +36351,18 @@ _tmp_126_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_127[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // ',' (')' | '**') @@ -35260,21 +36370,21 @@ _tmp_126_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); + D(fprintf(stderr, "%*c> _tmp_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); Token * _literal; - void *_tmp_169_var; + void *_tmp_172_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_169_var = _tmp_169_rule(p)) // ')' | '**' + (_tmp_172_var = _tmp_172_rule(p)) // ')' | '**' ) { - D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_169_var); + D(fprintf(stderr, "%*c+ _tmp_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); + _res = _PyPegen_dummy_name(p, _literal, _tmp_172_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_126[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_127[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (')' | '**')")); } _res = NULL; @@ -35283,9 +36393,9 @@ _tmp_126_rule(Parser *p) return _res; } -// _tmp_127: param_no_default | ',' +// _tmp_128: param_no_default | ',' static void * -_tmp_127_rule(Parser *p) +_tmp_128_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35301,18 +36411,18 @@ _tmp_127_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c> _tmp_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "param_no_default")); arg_ty param_no_default_var; if ( (param_no_default_var = param_no_default_rule(p)) // param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_128[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "param_no_default")); _res = param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_127[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_128[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_no_default")); } { // ',' @@ -35320,18 +36430,18 @@ _tmp_127_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_127[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_127[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_128[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_127[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_128[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -35340,9 +36450,9 @@ _tmp_127_rule(Parser *p) return _res; } -// _tmp_128: '*' | '**' | '/' +// _tmp_129: '*' | '**' | '/' static void * -_tmp_128_rule(Parser *p) +_tmp_129_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35358,18 +36468,18 @@ _tmp_128_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ _tmp_128[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_128[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); } { // '**' @@ -35377,18 +36487,18 @@ _tmp_128_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_128[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_128[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } { // '/' @@ -35396,18 +36506,18 @@ _tmp_128_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_128[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 17)) // token='/' ) { - D(fprintf(stderr, "%*c+ _tmp_128[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_128[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'")); } _res = NULL; @@ -35416,9 +36526,9 @@ _tmp_128_rule(Parser *p) return _res; } -// _tmp_129: lambda_slash_no_default | lambda_slash_with_default +// _tmp_130: lambda_slash_no_default | lambda_slash_with_default static void * -_tmp_129_rule(Parser *p) +_tmp_130_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35434,18 +36544,18 @@ _tmp_129_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); + D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); asdl_arg_seq* lambda_slash_no_default_var; if ( (lambda_slash_no_default_var = lambda_slash_no_default_rule(p)) // lambda_slash_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); + D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_no_default")); _res = lambda_slash_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_no_default")); } { // lambda_slash_with_default @@ -35453,18 +36563,18 @@ _tmp_129_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_129[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c> _tmp_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); SlashWithDefault* lambda_slash_with_default_var; if ( (lambda_slash_with_default_var = lambda_slash_with_default_rule(p)) // lambda_slash_with_default ) { - D(fprintf(stderr, "%*c+ _tmp_129[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); + D(fprintf(stderr, "%*c+ _tmp_130[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_slash_with_default")); _res = lambda_slash_with_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_129[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_130[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_slash_with_default")); } _res = NULL; @@ -35473,9 +36583,9 @@ _tmp_129_rule(Parser *p) return _res; } -// _loop0_130: ',' lambda_param +// _loop0_131: ',' lambda_param static asdl_seq * -_loop0_130_rule(Parser *p) +_loop0_131_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35500,7 +36610,7 @@ _loop0_130_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_130[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' lambda_param")); + D(fprintf(stderr, "%*c> _loop0_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' lambda_param")); Token * _literal; arg_ty elem; while ( @@ -35510,7 +36620,7 @@ _loop0_130_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -35532,7 +36642,7 @@ _loop0_130_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_130[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_131[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' lambda_param")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -35549,9 +36659,9 @@ _loop0_130_rule(Parser *p) return _seq; } -// _gather_131: lambda_param _loop0_130 +// _gather_132: lambda_param _loop0_131 static asdl_seq * -_gather_131_rule(Parser *p) +_gather_132_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35562,27 +36672,27 @@ _gather_131_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // lambda_param _loop0_130 + { // lambda_param _loop0_131 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_131[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_130")); + D(fprintf(stderr, "%*c> _gather_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_131")); arg_ty elem; asdl_seq * seq; if ( (elem = lambda_param_rule(p)) // lambda_param && - (seq = _loop0_130_rule(p)) // _loop0_130 + (seq = _loop0_131_rule(p)) // _loop0_131 ) { - D(fprintf(stderr, "%*c+ _gather_131[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_130")); + D(fprintf(stderr, "%*c+ _gather_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param _loop0_131")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_131[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param _loop0_130")); + D(fprintf(stderr, "%*c%s _gather_132[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param _loop0_131")); } _res = NULL; done: @@ -35590,9 +36700,9 @@ _gather_131_rule(Parser *p) return _res; } -// _tmp_132: ',' | lambda_param_no_default +// _tmp_133: ',' | lambda_param_no_default static void * -_tmp_132_rule(Parser *p) +_tmp_133_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35608,18 +36718,18 @@ _tmp_132_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_133[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // lambda_param_no_default @@ -35627,18 +36737,18 @@ _tmp_132_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_132[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; if ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_132[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); _res = lambda_param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_132[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_133[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } _res = NULL; @@ -35647,9 +36757,9 @@ _tmp_132_rule(Parser *p) return _res; } -// _tmp_133: ':' | ',' (':' | '**') +// _tmp_134: ':' | ',' (':' | '**') static void * -_tmp_133_rule(Parser *p) +_tmp_134_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35665,18 +36775,18 @@ _tmp_133_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_133[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // ',' (':' | '**') @@ -35684,21 +36794,21 @@ _tmp_133_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); + D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); Token * _literal; - void *_tmp_170_var; + void *_tmp_173_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_170_var = _tmp_170_rule(p)) // ':' | '**' + (_tmp_173_var = _tmp_173_rule(p)) // ':' | '**' ) { - D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_170_var); + D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); + _res = _PyPegen_dummy_name(p, _literal, _tmp_173_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_133[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (':' | '**')")); } _res = NULL; @@ -35707,9 +36817,9 @@ _tmp_133_rule(Parser *p) return _res; } -// _tmp_134: lambda_param_no_default | ',' +// _tmp_135: lambda_param_no_default | ',' static void * -_tmp_134_rule(Parser *p) +_tmp_135_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35725,18 +36835,18 @@ _tmp_134_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); arg_ty lambda_param_no_default_var; if ( (lambda_param_no_default_var = lambda_param_no_default_rule(p)) // lambda_param_no_default ) { - D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); + D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "lambda_param_no_default")); _res = lambda_param_no_default_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_135[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_no_default")); } { // ',' @@ -35744,18 +36854,18 @@ _tmp_134_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_134[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_134[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_134[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_135[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -35764,9 +36874,9 @@ _tmp_134_rule(Parser *p) return _res; } -// _tmp_135: bitwise_or ((',' bitwise_or))* ','? +// _tmp_136: bitwise_or ((',' bitwise_or))* ','? static void * -_tmp_135_rule(Parser *p) +_tmp_136_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35782,25 +36892,25 @@ _tmp_135_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); - asdl_seq * _loop0_171_var; + D(fprintf(stderr, "%*c> _tmp_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); + asdl_seq * _loop0_174_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty bitwise_or_var; if ( (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or && - (_loop0_171_var = _loop0_171_rule(p)) // ((',' bitwise_or))* + (_loop0_174_var = _loop0_174_rule(p)) // ((',' bitwise_or))* && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) { - D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); - _res = _PyPegen_dummy_name(p, bitwise_or_var, _loop0_171_var, _opt_var); + D(fprintf(stderr, "%*c+ _tmp_136[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); + _res = _PyPegen_dummy_name(p, bitwise_or_var, _loop0_174_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_135[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_136[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); } _res = NULL; @@ -35809,9 +36919,9 @@ _tmp_135_rule(Parser *p) return _res; } -// _loop0_136: ',' dotted_name +// _loop0_137: ',' dotted_name static asdl_seq * -_loop0_136_rule(Parser *p) +_loop0_137_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35836,7 +36946,7 @@ _loop0_136_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_136[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' dotted_name")); + D(fprintf(stderr, "%*c> _loop0_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' dotted_name")); Token * _literal; expr_ty elem; while ( @@ -35846,7 +36956,7 @@ _loop0_136_rule(Parser *p) ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -35868,7 +36978,7 @@ _loop0_136_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_136[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_137[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' dotted_name")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -35885,9 +36995,9 @@ _loop0_136_rule(Parser *p) return _seq; } -// _gather_137: dotted_name _loop0_136 +// _gather_138: dotted_name _loop0_137 static asdl_seq * -_gather_137_rule(Parser *p) +_gather_138_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35898,27 +37008,27 @@ _gather_137_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // dotted_name _loop0_136 + { // dotted_name _loop0_137 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_137[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_136")); + D(fprintf(stderr, "%*c> _gather_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_137")); expr_ty elem; asdl_seq * seq; if ( (elem = dotted_name_rule(p)) // dotted_name && - (seq = _loop0_136_rule(p)) // _loop0_136 + (seq = _loop0_137_rule(p)) // _loop0_137 ) { - D(fprintf(stderr, "%*c+ _gather_137[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_136")); + D(fprintf(stderr, "%*c+ _gather_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name _loop0_137")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_137[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dotted_name _loop0_136")); + D(fprintf(stderr, "%*c%s _gather_138[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "dotted_name _loop0_137")); } _res = NULL; done: @@ -35926,9 +37036,9 @@ _gather_137_rule(Parser *p) return _res; } -// _tmp_138: NAME (',' | ')' | NEWLINE) +// _tmp_139: NAME (',' | ')' | ';' | NEWLINE) static void * -_tmp_138_rule(Parser *p) +_tmp_139_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35939,27 +37049,27 @@ _tmp_138_rule(Parser *p) } void * _res = NULL; int _mark = p->mark; - { // NAME (',' | ')' | NEWLINE) + { // NAME (',' | ')' | ';' | NEWLINE) if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME (',' | ')' | NEWLINE)")); - void *_tmp_172_var; + D(fprintf(stderr, "%*c> _tmp_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME (',' | ')' | ';' | NEWLINE)")); + void *_tmp_175_var; expr_ty name_var; if ( (name_var = _PyPegen_name_token(p)) // NAME && - (_tmp_172_var = _tmp_172_rule(p)) // ',' | ')' | NEWLINE + (_tmp_175_var = _tmp_175_rule(p)) // ',' | ')' | ';' | NEWLINE ) { - D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME (',' | ')' | NEWLINE)")); - _res = _PyPegen_dummy_name(p, name_var, _tmp_172_var); + D(fprintf(stderr, "%*c+ _tmp_139[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME (',' | ')' | ';' | NEWLINE)")); + _res = _PyPegen_dummy_name(p, name_var, _tmp_175_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_138[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME (',' | ')' | NEWLINE)")); + D(fprintf(stderr, "%*c%s _tmp_139[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME (',' | ')' | ';' | NEWLINE)")); } _res = NULL; done: @@ -35967,9 +37077,9 @@ _tmp_138_rule(Parser *p) return _res; } -// _loop0_139: ',' (expression ['as' star_target]) +// _loop0_140: ',' (expression ['as' star_target]) static asdl_seq * -_loop0_139_rule(Parser *p) +_loop0_140_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -35994,17 +37104,17 @@ _loop0_139_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_139[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])")); + D(fprintf(stderr, "%*c> _loop0_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expression ['as' star_target])")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_173_rule(p)) // expression ['as' star_target] + (elem = _tmp_176_rule(p)) // expression ['as' star_target] ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -36026,7 +37136,7 @@ _loop0_139_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_139[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_140[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expression ['as' star_target])")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36043,9 +37153,9 @@ _loop0_139_rule(Parser *p) return _seq; } -// _gather_140: (expression ['as' star_target]) _loop0_139 +// _gather_141: (expression ['as' star_target]) _loop0_140 static asdl_seq * -_gather_140_rule(Parser *p) +_gather_141_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36056,27 +37166,27 @@ _gather_140_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (expression ['as' star_target]) _loop0_139 + { // (expression ['as' star_target]) _loop0_140 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_140[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_139")); + D(fprintf(stderr, "%*c> _gather_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_140")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_173_rule(p)) // expression ['as' star_target] + (elem = _tmp_176_rule(p)) // expression ['as' star_target] && - (seq = _loop0_139_rule(p)) // _loop0_139 + (seq = _loop0_140_rule(p)) // _loop0_140 ) { - D(fprintf(stderr, "%*c+ _gather_140[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_139")); + D(fprintf(stderr, "%*c+ _gather_141[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expression ['as' star_target]) _loop0_140")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_140[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_139")); + D(fprintf(stderr, "%*c%s _gather_141[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expression ['as' star_target]) _loop0_140")); } _res = NULL; done: @@ -36084,9 +37194,9 @@ _gather_140_rule(Parser *p) return _res; } -// _loop0_141: ',' (expressions ['as' star_target]) +// _loop0_142: ',' (expressions ['as' star_target]) static asdl_seq * -_loop0_141_rule(Parser *p) +_loop0_142_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36111,17 +37221,17 @@ _loop0_141_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_141[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])")); + D(fprintf(stderr, "%*c> _loop0_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (expressions ['as' star_target])")); Token * _literal; void *elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_174_rule(p)) // expressions ['as' star_target] + (elem = _tmp_177_rule(p)) // expressions ['as' star_target] ) { _res = elem; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; PyMem_Free(_children); p->level--; @@ -36143,7 +37253,7 @@ _loop0_141_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_141[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_142[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (expressions ['as' star_target])")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36160,9 +37270,9 @@ _loop0_141_rule(Parser *p) return _seq; } -// _gather_142: (expressions ['as' star_target]) _loop0_141 +// _gather_143: (expressions ['as' star_target]) _loop0_142 static asdl_seq * -_gather_142_rule(Parser *p) +_gather_143_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36173,27 +37283,27 @@ _gather_142_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (expressions ['as' star_target]) _loop0_141 + { // (expressions ['as' star_target]) _loop0_142 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_142[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_141")); + D(fprintf(stderr, "%*c> _gather_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_142")); void *elem; asdl_seq * seq; if ( - (elem = _tmp_174_rule(p)) // expressions ['as' star_target] + (elem = _tmp_177_rule(p)) // expressions ['as' star_target] && - (seq = _loop0_141_rule(p)) // _loop0_141 + (seq = _loop0_142_rule(p)) // _loop0_142 ) { - D(fprintf(stderr, "%*c+ _gather_142[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_141")); + D(fprintf(stderr, "%*c+ _gather_143[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(expressions ['as' star_target]) _loop0_142")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _gather_142[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_141")); + D(fprintf(stderr, "%*c%s _gather_143[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(expressions ['as' star_target]) _loop0_142")); } _res = NULL; done: @@ -36201,9 +37311,9 @@ _gather_142_rule(Parser *p) return _res; } -// _tmp_143: 'except' | 'finally' +// _tmp_144: 'except' | 'finally' static void * -_tmp_143_rule(Parser *p) +_tmp_144_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36219,18 +37329,18 @@ _tmp_143_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'")); + D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'except'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 682)) // token='except' + (_keyword = _PyPegen_expect_token(p, 693)) // token='except' ) { - D(fprintf(stderr, "%*c+ _tmp_143[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'")); + D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_143[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'except'")); } { // 'finally' @@ -36238,18 +37348,18 @@ _tmp_143_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_143[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'")); + D(fprintf(stderr, "%*c> _tmp_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'finally'")); Token * _keyword; if ( - (_keyword = _PyPegen_expect_token(p, 678)) // token='finally' + (_keyword = _PyPegen_expect_token(p, 689)) // token='finally' ) { - D(fprintf(stderr, "%*c+ _tmp_143[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'")); + D(fprintf(stderr, "%*c+ _tmp_144[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'finally'")); _res = _keyword; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_143[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_144[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'finally'")); } _res = NULL; @@ -36258,9 +37368,9 @@ _tmp_143_rule(Parser *p) return _res; } -// _loop0_144: block +// _loop0_145: block static asdl_seq * -_loop0_144_rule(Parser *p) +_loop0_145_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36285,7 +37395,7 @@ _loop0_144_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_144[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block")); + D(fprintf(stderr, "%*c> _loop0_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "block")); asdl_stmt_seq* block_var; while ( (block_var = block_rule(p)) // block @@ -36308,7 +37418,7 @@ _loop0_144_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_144[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_145[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "block")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -36325,9 +37435,9 @@ _loop0_144_rule(Parser *p) return _seq; } -// _tmp_145: expression ['as' NAME] +// _tmp_146: expression ['as' NAME] static void * -_tmp_145_rule(Parser *p) +_tmp_146_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36343,7 +37453,7 @@ _tmp_145_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_145[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); + D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expression_var; @@ -36353,12 +37463,12 @@ _tmp_145_rule(Parser *p) (_opt_var = _tmp_21_rule(p), !p->error_indicator) // ['as' NAME] ) { - D(fprintf(stderr, "%*c+ _tmp_145[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); + D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' NAME]")); _res = _PyPegen_dummy_name(p, expression_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_145[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' NAME]")); } _res = NULL; @@ -36367,9 +37477,9 @@ _tmp_145_rule(Parser *p) return _res; } -// _tmp_146: NEWLINE | ':' +// _tmp_147: NEWLINE | ':' static void * -_tmp_146_rule(Parser *p) +_tmp_147_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36385,18 +37495,18 @@ _tmp_146_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); Token * newline_var; if ( (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); _res = newline_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE")); } { // ':' @@ -36404,18 +37514,18 @@ _tmp_146_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_146[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_146[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_146[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } _res = NULL; @@ -36424,9 +37534,50 @@ _tmp_146_rule(Parser *p) return _res; } -// _tmp_147: positional_patterns ',' +// _tmp_148: items_pattern ',' static void * -_tmp_147_rule(Parser *p) +_tmp_148_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // items_pattern ',' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "items_pattern ','")); + Token * _literal; + asdl_seq* items_pattern_var; + if ( + (items_pattern_var = items_pattern_rule(p)) // items_pattern + && + (_literal = _PyPegen_expect_token(p, 12)) // token=',' + ) + { + D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "items_pattern ','")); + _res = _PyPegen_dummy_name(p, items_pattern_var, _literal); + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "items_pattern ','")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_149: positional_patterns ',' +static void * +_tmp_149_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36442,7 +37593,7 @@ _tmp_147_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_147[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); + D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); Token * _literal; asdl_pattern_seq* positional_patterns_var; if ( @@ -36451,12 +37602,12 @@ _tmp_147_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_147[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); + D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "positional_patterns ','")); _res = _PyPegen_dummy_name(p, positional_patterns_var, _literal); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_147[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "positional_patterns ','")); } _res = NULL; @@ -36465,9 +37616,66 @@ _tmp_147_rule(Parser *p) return _res; } -// _tmp_148: '}' | ',' +// _tmp_150: invalid_kvpair | invalid_kvpair_unpacking static void * -_tmp_148_rule(Parser *p) +_tmp_150_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + if (p->call_invalid_rules) { // invalid_kvpair + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_kvpair")); + void *invalid_kvpair_var; + if ( + (invalid_kvpair_var = invalid_kvpair_rule(p)) // invalid_kvpair + ) + { + D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_kvpair")); + _res = invalid_kvpair_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_kvpair")); + } + if (p->call_invalid_rules) { // invalid_kvpair_unpacking + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_kvpair_unpacking")); + void *invalid_kvpair_unpacking_var; + if ( + (invalid_kvpair_unpacking_var = invalid_kvpair_unpacking_rule(p)) // invalid_kvpair_unpacking + ) + { + D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_kvpair_unpacking")); + _res = invalid_kvpair_unpacking_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_kvpair_unpacking")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_151: '}' | ',' +static void * +_tmp_151_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36483,18 +37691,18 @@ _tmp_148_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } { // ',' @@ -36502,18 +37710,18 @@ _tmp_148_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_148[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_148[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_148[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } _res = NULL; @@ -36522,9 +37730,9 @@ _tmp_148_rule(Parser *p) return _res; } -// _tmp_149: '=' | '!' | ':' | '}' +// _tmp_152: '=' | '!' | ':' | '}' static void * -_tmp_149_rule(Parser *p) +_tmp_152_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36540,18 +37748,18 @@ _tmp_149_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'='")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); + D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'='")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'='")); } { // '!' @@ -36559,18 +37767,18 @@ _tmp_149_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'")); + D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 54)) // token='!' ) { - D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'")); + D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!'")); } { // ':' @@ -36578,18 +37786,18 @@ _tmp_149_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '}' @@ -36597,18 +37805,18 @@ _tmp_149_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_149[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_149[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_149[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } _res = NULL; @@ -36617,9 +37825,9 @@ _tmp_149_rule(Parser *p) return _res; } -// _tmp_150: '!' | ':' | '}' +// _tmp_153: '!' | ':' | '}' static void * -_tmp_150_rule(Parser *p) +_tmp_153_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36635,18 +37843,18 @@ _tmp_150_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'")); + D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 54)) // token='!' ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'")); + D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!'")); } { // ':' @@ -36654,18 +37862,18 @@ _tmp_150_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '}' @@ -36673,18 +37881,18 @@ _tmp_150_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_150[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_150[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_150[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } _res = NULL; @@ -36693,9 +37901,9 @@ _tmp_150_rule(Parser *p) return _res; } -// _tmp_151: '!' NAME +// _tmp_154: '!' NAME static void * -_tmp_151_rule(Parser *p) +_tmp_154_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36711,7 +37919,7 @@ _tmp_151_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_151[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME")); + D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'!' NAME")); Token * _literal; expr_ty name_var; if ( @@ -36720,12 +37928,12 @@ _tmp_151_rule(Parser *p) (name_var = _PyPegen_name_token(p)) // NAME ) { - D(fprintf(stderr, "%*c+ _tmp_151[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME")); + D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'!' NAME")); _res = _PyPegen_dummy_name(p, _literal, name_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_151[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'!' NAME")); } _res = NULL; @@ -36734,9 +37942,9 @@ _tmp_151_rule(Parser *p) return _res; } -// _tmp_152: ':' | '}' +// _tmp_155: ':' | '}' static void * -_tmp_152_rule(Parser *p) +_tmp_155_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36752,18 +37960,18 @@ _tmp_152_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '}' @@ -36771,18 +37979,18 @@ _tmp_152_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_152[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'}'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 26)) // token='}' ) { - D(fprintf(stderr, "%*c+ _tmp_152[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); + D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'}'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_152[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'}'")); } _res = NULL; @@ -36791,9 +37999,9 @@ _tmp_152_rule(Parser *p) return _res; } -// _tmp_153: fstring | string +// _tmp_156: fstring | string static void * -_tmp_153_rule(Parser *p) +_tmp_156_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36809,18 +38017,18 @@ _tmp_153_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring")); + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "fstring")); expr_ty fstring_var; if ( (fstring_var = fstring_rule(p)) // fstring ) { - D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "fstring")); + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "fstring")); _res = fstring_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "fstring")); } { // string @@ -36828,18 +38036,18 @@ _tmp_153_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_153[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "string")); + D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "string")); expr_ty string_var; if ( (string_var = string_rule(p)) // string ) { - D(fprintf(stderr, "%*c+ _tmp_153[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "string")); + D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "string")); _res = string_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_153[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "string")); } _res = NULL; @@ -36848,9 +38056,9 @@ _tmp_153_rule(Parser *p) return _res; } -// _tmp_154: '+' | '-' | '*' | '/' | '%' | '//' | '@' +// _tmp_157: '+' | '-' | '*' | '/' | '%' | '//' | '@' static void * -_tmp_154_rule(Parser *p) +_tmp_157_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -36866,18 +38074,18 @@ _tmp_154_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 14)) // token='+' ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+'")); } { // '-' @@ -36885,18 +38093,18 @@ _tmp_154_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 15)) // token='-' ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'-'")); } { // '*' @@ -36904,18 +38112,18 @@ _tmp_154_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'*'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 16)) // token='*' ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'*'")); } { // '/' @@ -36923,18 +38131,18 @@ _tmp_154_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'/'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 17)) // token='/' ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'/'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'/'")); } { // '%' @@ -36942,18 +38150,18 @@ _tmp_154_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'%'")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'%'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 24)) // token='%' ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'%'")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'%'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'%'")); } { // '//' @@ -36961,18 +38169,18 @@ _tmp_154_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'//'")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'//'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 47)) // token='//' ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'//'")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'//'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'//'")); } { // '@' @@ -36980,18 +38188,18 @@ _tmp_154_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_154[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@'")); + D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 49)) // token='@' ) { - D(fprintf(stderr, "%*c+ _tmp_154[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@'")); + D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_154[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@'")); } _res = NULL; @@ -37000,9 +38208,9 @@ _tmp_154_rule(Parser *p) return _res; } -// _tmp_155: '+' | '-' | '~' +// _tmp_158: '+' | '-' | '~' static void * -_tmp_155_rule(Parser *p) +_tmp_158_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37018,18 +38226,18 @@ _tmp_155_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'")); + D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'+'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 14)) // token='+' ) { - D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'")); + D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'+'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'+'")); } { // '-' @@ -37037,18 +38245,18 @@ _tmp_155_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'")); + D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'-'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 15)) // token='-' ) { - D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'")); + D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'-'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'-'")); } { // '~' @@ -37056,18 +38264,18 @@ _tmp_155_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_155[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'~'")); + D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'~'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 31)) // token='~' ) { - D(fprintf(stderr, "%*c+ _tmp_155[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'~'")); + D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'~'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_155[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'~'")); } _res = NULL; @@ -37076,9 +38284,9 @@ _tmp_155_rule(Parser *p) return _res; } -// _tmp_156: star_targets '=' +// _tmp_159: star_targets '=' static void * -_tmp_156_rule(Parser *p) +_tmp_159_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37094,7 +38302,7 @@ _tmp_156_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_156[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "star_targets '='")); Token * _literal; expr_ty z; if ( @@ -37103,9 +38311,9 @@ _tmp_156_rule(Parser *p) (_literal = _PyPegen_expect_token(p, 22)) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_156[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); + D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "star_targets '='")); _res = z; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -37113,7 +38321,7 @@ _tmp_156_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_156[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "star_targets '='")); } _res = NULL; @@ -37122,9 +38330,9 @@ _tmp_156_rule(Parser *p) return _res; } -// _tmp_157: '.' | '...' +// _tmp_160: '.' | '...' static void * -_tmp_157_rule(Parser *p) +_tmp_160_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37140,18 +38348,18 @@ _tmp_157_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'.'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 23)) // token='.' ) { - D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); + D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'.'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'.'")); } { // '...' @@ -37159,18 +38367,18 @@ _tmp_157_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_157[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'...'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 52)) // token='...' ) { - D(fprintf(stderr, "%*c+ _tmp_157[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); + D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'...'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_157[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'...'")); } _res = NULL; @@ -37179,9 +38387,9 @@ _tmp_157_rule(Parser *p) return _res; } -// _tmp_158: '@' named_expression NEWLINE +// _tmp_161: '@' named_expression NEWLINE static void * -_tmp_158_rule(Parser *p) +_tmp_161_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37197,7 +38405,7 @@ _tmp_158_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_158[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); Token * _literal; expr_ty f; Token * newline_var; @@ -37209,9 +38417,9 @@ _tmp_158_rule(Parser *p) (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_158[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'@' named_expression NEWLINE")); _res = f; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -37219,7 +38427,7 @@ _tmp_158_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_158[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'@' named_expression NEWLINE")); } _res = NULL; @@ -37228,9 +38436,9 @@ _tmp_158_rule(Parser *p) return _res; } -// _tmp_159: ',' star_expression +// _tmp_162: ',' star_expression static void * -_tmp_159_rule(Parser *p) +_tmp_162_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37246,7 +38454,7 @@ _tmp_159_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_159[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression")); + D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_expression")); Token * _literal; expr_ty c; if ( @@ -37255,9 +38463,9 @@ _tmp_159_rule(Parser *p) (c = star_expression_rule(p)) // star_expression ) { - D(fprintf(stderr, "%*c+ _tmp_159[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression")); + D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_expression")); _res = c; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -37265,7 +38473,7 @@ _tmp_159_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_159[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_expression")); } _res = NULL; @@ -37274,9 +38482,9 @@ _tmp_159_rule(Parser *p) return _res; } -// _tmp_160: 'or' conjunction +// _tmp_163: 'or' conjunction static void * -_tmp_160_rule(Parser *p) +_tmp_163_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37292,7 +38500,7 @@ _tmp_160_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_160[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); + D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); Token * _keyword; expr_ty c; if ( @@ -37301,9 +38509,9 @@ _tmp_160_rule(Parser *p) (c = conjunction_rule(p)) // conjunction ) { - D(fprintf(stderr, "%*c+ _tmp_160[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); + D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'or' conjunction")); _res = c; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -37311,7 +38519,7 @@ _tmp_160_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_160[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'or' conjunction")); } _res = NULL; @@ -37320,9 +38528,9 @@ _tmp_160_rule(Parser *p) return _res; } -// _tmp_161: 'and' inversion +// _tmp_164: 'and' inversion static void * -_tmp_161_rule(Parser *p) +_tmp_164_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37338,7 +38546,7 @@ _tmp_161_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_161[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion")); + D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'and' inversion")); Token * _keyword; expr_ty c; if ( @@ -37347,9 +38555,9 @@ _tmp_161_rule(Parser *p) (c = inversion_rule(p)) // inversion ) { - D(fprintf(stderr, "%*c+ _tmp_161[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion")); + D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'and' inversion")); _res = c; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -37357,7 +38565,7 @@ _tmp_161_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_161[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'and' inversion")); } _res = NULL; @@ -37366,9 +38574,9 @@ _tmp_161_rule(Parser *p) return _res; } -// _tmp_162: slice | starred_expression +// _tmp_165: slice | starred_expression static void * -_tmp_162_rule(Parser *p) +_tmp_165_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37384,18 +38592,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice")); + D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice")); expr_ty slice_var; if ( (slice_var = slice_rule(p)) // slice ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice")); + D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice")); _res = slice_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slice")); } { // starred_expression @@ -37403,18 +38611,18 @@ _tmp_162_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } _res = NULL; @@ -37423,9 +38631,9 @@ _tmp_162_rule(Parser *p) return _res; } -// _tmp_163: 'if' disjunction +// _tmp_166: 'if' disjunction static void * -_tmp_163_rule(Parser *p) +_tmp_166_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37441,18 +38649,18 @@ _tmp_163_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); Token * _keyword; expr_ty z; if ( - (_keyword = _PyPegen_expect_token(p, 687)) // token='if' + (_keyword = _PyPegen_expect_token(p, 698)) // token='if' && (z = disjunction_rule(p)) // disjunction ) { - D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); _res = z; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -37460,7 +38668,7 @@ _tmp_163_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction")); } _res = NULL; @@ -37469,9 +38677,9 @@ _tmp_163_rule(Parser *p) return _res; } -// _tmp_164: starred_expression | (assignment_expression | expression !':=') !'=' +// _tmp_167: starred_expression | (assignment_expression | expression !':=') !'=' static void * -_tmp_164_rule(Parser *p) +_tmp_167_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37487,18 +38695,18 @@ _tmp_164_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } { // (assignment_expression | expression !':=') !'=' @@ -37506,20 +38714,20 @@ _tmp_164_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); - void *_tmp_87_var; + D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + void *_tmp_178_var; if ( - (_tmp_87_var = _tmp_87_rule(p)) // assignment_expression | expression !':=' + (_tmp_178_var = _tmp_178_rule(p)) // assignment_expression | expression !':=' && _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); - _res = _tmp_87_var; + D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + _res = _tmp_178_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(assignment_expression | expression !':=') !'='")); } _res = NULL; @@ -37528,9 +38736,9 @@ _tmp_164_rule(Parser *p) return _res; } -// _tmp_165: ',' star_target +// _tmp_168: ',' star_target static void * -_tmp_165_rule(Parser *p) +_tmp_168_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37546,7 +38754,7 @@ _tmp_165_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); Token * _literal; expr_ty c; if ( @@ -37555,9 +38763,9 @@ _tmp_165_rule(Parser *p) (c = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); _res = c; - if (_res == NULL && PyErr_Occurred()) { + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { p->error_indicator = 1; p->level--; return NULL; @@ -37565,7 +38773,7 @@ _tmp_165_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); } _res = NULL; @@ -37574,10 +38782,10 @@ _tmp_165_rule(Parser *p) return _res; } -// _tmp_166: +// _tmp_169: // | ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs static void * -_tmp_166_rule(Parser *p) +_tmp_169_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37593,24 +38801,24 @@ _tmp_166_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); - asdl_seq * _gather_89_var; + D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + asdl_seq * _gather_91_var; Token * _literal; asdl_seq* kwargs_var; if ( - (_gather_89_var = _gather_89_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ + (_gather_91_var = _gather_91_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ && (_literal = _PyPegen_expect_token(p, 12)) // token=',' && (kwargs_var = kwargs_rule(p)) // kwargs ) { - D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); - _res = _PyPegen_dummy_name(p, _gather_89_var, _literal, kwargs_var); + D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + _res = _PyPegen_dummy_name(p, _gather_91_var, _literal, kwargs_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); } _res = NULL; @@ -37619,9 +38827,9 @@ _tmp_166_rule(Parser *p) return _res; } -// _tmp_167: starred_expression !'=' +// _tmp_170: starred_expression !'=' static void * -_tmp_167_rule(Parser *p) +_tmp_170_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37637,7 +38845,7 @@ _tmp_167_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); + D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression @@ -37645,12 +38853,12 @@ _tmp_167_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); + D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression !'='")); } _res = NULL; @@ -37659,9 +38867,9 @@ _tmp_167_rule(Parser *p) return _res; } -// _tmp_168: !STRING expression_without_invalid +// _tmp_171: !STRING expression_without_invalid static void * -_tmp_168_rule(Parser *p) +_tmp_171_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37677,7 +38885,7 @@ _tmp_168_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); + D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); expr_ty expression_without_invalid_var; if ( _PyPegen_lookahead(0, _PyPegen_string_token, p) @@ -37685,12 +38893,12 @@ _tmp_168_rule(Parser *p) (expression_without_invalid_var = expression_without_invalid_rule(p)) // expression_without_invalid ) { - D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); + D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); _res = expression_without_invalid_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "!STRING expression_without_invalid")); } _res = NULL; @@ -37699,9 +38907,9 @@ _tmp_168_rule(Parser *p) return _res; } -// _tmp_169: ')' | '**' +// _tmp_172: ')' | '**' static void * -_tmp_169_rule(Parser *p) +_tmp_172_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37717,18 +38925,18 @@ _tmp_169_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // '**' @@ -37736,18 +38944,18 @@ _tmp_169_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } _res = NULL; @@ -37756,9 +38964,9 @@ _tmp_169_rule(Parser *p) return _res; } -// _tmp_170: ':' | '**' +// _tmp_173: ':' | '**' static void * -_tmp_170_rule(Parser *p) +_tmp_173_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37774,18 +38982,18 @@ _tmp_170_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '**' @@ -37793,18 +39001,18 @@ _tmp_170_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } _res = NULL; @@ -37813,9 +39021,9 @@ _tmp_170_rule(Parser *p) return _res; } -// _loop0_171: (',' bitwise_or) +// _loop0_174: (',' bitwise_or) static asdl_seq * -_loop0_171_rule(Parser *p) +_loop0_174_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37840,13 +39048,13 @@ _loop0_171_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' bitwise_or)")); - void *_tmp_175_var; + D(fprintf(stderr, "%*c> _loop0_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' bitwise_or)")); + void *_tmp_179_var; while ( - (_tmp_175_var = _tmp_175_rule(p)) // ',' bitwise_or + (_tmp_179_var = _tmp_179_rule(p)) // ',' bitwise_or ) { - _res = _tmp_175_var; + _res = _tmp_179_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -37863,7 +39071,7 @@ _loop0_171_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_171[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_174[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' bitwise_or)")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -37880,9 +39088,9 @@ _loop0_171_rule(Parser *p) return _seq; } -// _tmp_172: ',' | ')' | NEWLINE +// _tmp_175: ',' | ')' | ';' | NEWLINE static void * -_tmp_172_rule(Parser *p) +_tmp_175_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37898,18 +39106,18 @@ _tmp_172_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // ')' @@ -37917,37 +39125,56 @@ _tmp_172_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } + { // ';' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "';'")); + Token * _literal; + if ( + (_literal = _PyPegen_expect_token(p, 13)) // token=';' + ) + { + D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "';'")); + _res = _literal; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "';'")); + } { // NEWLINE if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); Token * newline_var; if ( (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); _res = newline_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE")); } _res = NULL; @@ -37956,9 +39183,9 @@ _tmp_172_rule(Parser *p) return _res; } -// _tmp_173: expression ['as' star_target] +// _tmp_176: expression ['as' star_target] static void * -_tmp_173_rule(Parser *p) +_tmp_176_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37974,22 +39201,22 @@ _tmp_173_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_176_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_180_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_176[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); _res = _PyPegen_dummy_name(p, expression_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_176[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' star_target]")); } _res = NULL; @@ -37998,9 +39225,9 @@ _tmp_173_rule(Parser *p) return _res; } -// _tmp_174: expressions ['as' star_target] +// _tmp_177: expressions ['as' star_target] static void * -_tmp_174_rule(Parser *p) +_tmp_177_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38016,22 +39243,22 @@ _tmp_174_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_177[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expressions_var; if ( (expressions_var = expressions_rule(p)) // expressions && - (_opt_var = _tmp_176_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_180_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_177[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); _res = _PyPegen_dummy_name(p, expressions_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_177[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expressions ['as' star_target]")); } _res = NULL; @@ -38040,9 +39267,68 @@ _tmp_174_rule(Parser *p) return _res; } -// _tmp_175: ',' bitwise_or +// _tmp_178: assignment_expression | expression !':=' static void * -_tmp_175_rule(Parser *p) +_tmp_178_rule(Parser *p) +{ + if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { + _Pypegen_stack_overflow(p); + } + if (p->error_indicator) { + p->level--; + return NULL; + } + void * _res = NULL; + int _mark = p->mark; + { // assignment_expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_178[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + expr_ty assignment_expression_var; + if ( + (assignment_expression_var = assignment_expression_rule(p)) // assignment_expression + ) + { + D(fprintf(stderr, "%*c+ _tmp_178[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "assignment_expression")); + _res = assignment_expression_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_178[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "assignment_expression")); + } + { // expression !':=' + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> _tmp_178[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression !':='")); + expr_ty expression_var; + if ( + (expression_var = expression_rule(p)) // expression + && + _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 53) // token=':=' + ) + { + D(fprintf(stderr, "%*c+ _tmp_178[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression !':='")); + _res = expression_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s _tmp_178[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression !':='")); + } + _res = NULL; + done: + p->level--; + return _res; +} + +// _tmp_179: ',' bitwise_or +static void * +_tmp_179_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38058,7 +39344,7 @@ _tmp_175_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); + D(fprintf(stderr, "%*c> _tmp_179[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); Token * _literal; expr_ty bitwise_or_var; if ( @@ -38067,12 +39353,12 @@ _tmp_175_rule(Parser *p) (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or ) { - D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); + D(fprintf(stderr, "%*c+ _tmp_179[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); _res = _PyPegen_dummy_name(p, _literal, bitwise_or_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_179[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' bitwise_or")); } _res = NULL; @@ -38081,9 +39367,9 @@ _tmp_175_rule(Parser *p) return _res; } -// _tmp_176: 'as' star_target +// _tmp_180: 'as' star_target static void * -_tmp_176_rule(Parser *p) +_tmp_180_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38099,21 +39385,21 @@ _tmp_176_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_180[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( - (_keyword = _PyPegen_expect_token(p, 685)) // token='as' + (_keyword = _PyPegen_expect_token(p, 696)) // token='as' && (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_176[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_180[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_176[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_180[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; diff --git a/Parser/peg_api.c b/Parser/peg_api.c index d4acc3e4935d10..e30ca0453bd3e1 100644 --- a/Parser/peg_api.c +++ b/Parser/peg_api.c @@ -4,13 +4,15 @@ mod_ty _PyParser_ASTFromString(const char *str, PyObject* filename, int mode, - PyCompilerFlags *flags, PyArena *arena) + PyCompilerFlags *flags, PyArena *arena, + PyObject *module) { if (PySys_Audit("compile", "yO", str, filename) < 0) { return NULL; } - mod_ty result = _PyPegen_run_parser_from_string(str, mode, filename, flags, arena); + mod_ty result = _PyPegen_run_parser_from_string(str, mode, filename, flags, + arena, module); return result; } diff --git a/Parser/pegen.c b/Parser/pegen.c index 50641de27d37fd..bb222b50fc095f 100644 --- a/Parser/pegen.c +++ b/Parser/pegen.c @@ -3,12 +3,13 @@ #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_parser.h" // _PYPEGEN_NSTATISTICS #include "pycore_pyerrors.h" // PyExc_IncompleteInputError -#include "pycore_runtime.h" // _PyRuntime +#include "pycore_runtime.h" // _PyRuntime #include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal #include <errcode.h> #include "lexer/lexer.h" #include "tokenizer/tokenizer.h" +#include "tokenizer/helpers.h" #include "pegen.h" // Internal parser functions @@ -299,22 +300,14 @@ _PyPegen_fill_token(Parser *p) #define NSTATISTICS _PYPEGEN_NSTATISTICS #define memo_statistics _PyRuntime.parser.memo_statistics -#ifdef Py_GIL_DISABLED -#define MUTEX_LOCK() PyMutex_Lock(&_PyRuntime.parser.mutex) -#define MUTEX_UNLOCK() PyMutex_Unlock(&_PyRuntime.parser.mutex) -#else -#define MUTEX_LOCK() -#define MUTEX_UNLOCK() -#endif - void _PyPegen_clear_memo_statistics(void) { - MUTEX_LOCK(); + PyMutex_Lock(&_PyRuntime.parser.mutex); for (int i = 0; i < NSTATISTICS; i++) { memo_statistics[i] = 0; } - MUTEX_UNLOCK(); + PyMutex_Unlock(&_PyRuntime.parser.mutex); } PyObject * @@ -325,22 +318,22 @@ _PyPegen_get_memo_statistics(void) return NULL; } - MUTEX_LOCK(); + PyMutex_Lock(&_PyRuntime.parser.mutex); for (int i = 0; i < NSTATISTICS; i++) { PyObject *value = PyLong_FromLong(memo_statistics[i]); if (value == NULL) { - MUTEX_UNLOCK(); + PyMutex_Unlock(&_PyRuntime.parser.mutex); Py_DECREF(ret); return NULL; } // PyList_SetItem borrows a reference to value. if (PyList_SetItem(ret, i, value) < 0) { - MUTEX_UNLOCK(); + PyMutex_Unlock(&_PyRuntime.parser.mutex); Py_DECREF(ret); return NULL; } } - MUTEX_UNLOCK(); + PyMutex_Unlock(&_PyRuntime.parser.mutex); return ret; } #endif @@ -366,9 +359,9 @@ _PyPegen_is_memoized(Parser *p, int type, void *pres) if (count <= 0) { count = 1; } - MUTEX_LOCK(); + PyMutex_Lock(&_PyRuntime.parser.mutex); memo_statistics[type] += count; - MUTEX_UNLOCK(); + PyMutex_Unlock(&_PyRuntime.parser.mutex); } #endif p->mark = m->mark; @@ -932,7 +925,6 @@ _PyPegen_set_syntax_error_metadata(Parser *p) { the_source // N gives ownership to metadata ); if (!metadata) { - Py_DECREF(the_source); PyErr_Clear(); return; } @@ -1002,7 +994,7 @@ _PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filena struct tok_state *tok = _PyTokenizer_FromFile(fp, enc, ps1, ps2); if (tok == NULL) { if (PyErr_Occurred()) { - _PyPegen_raise_tokenizer_init_error(filename_ob); + _PyTokenizer_raise_init_error(filename_ob); return NULL; } return NULL; @@ -1017,6 +1009,11 @@ _PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filena // From here on we need to clean up even if there's an error mod_ty result = NULL; + tok->module = PyUnicode_FromString("__main__"); + if (tok->module == NULL) { + goto error; + } + int parser_flags = compute_parser_flags(flags); Parser *p = _PyPegen_Parser_New(tok, start_rule, parser_flags, PY_MINOR_VERSION, errcode, NULL, arena); @@ -1029,8 +1026,8 @@ _PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filena if (tok->fp_interactive && tok->interactive_src_start && result && interactive_src != NULL) { *interactive_src = PyUnicode_FromString(tok->interactive_src_start); - if (!interactive_src || _PyArena_AddPyObject(arena, *interactive_src) < 0) { - Py_XDECREF(interactive_src); + if (!*interactive_src || _PyArena_AddPyObject(arena, *interactive_src) < 0) { + Py_XDECREF(*interactive_src); result = NULL; goto error; } @@ -1043,7 +1040,7 @@ _PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filena mod_ty _PyPegen_run_parser_from_string(const char *str, int start_rule, PyObject *filename_ob, - PyCompilerFlags *flags, PyArena *arena) + PyCompilerFlags *flags, PyArena *arena, PyObject *module) { int exec_input = start_rule == Py_file_input; @@ -1055,12 +1052,13 @@ _PyPegen_run_parser_from_string(const char *str, int start_rule, PyObject *filen } if (tok == NULL) { if (PyErr_Occurred()) { - _PyPegen_raise_tokenizer_init_error(filename_ob); + _PyTokenizer_raise_init_error(filename_ob); } return NULL; } // This transfers the ownership to the tokenizer tok->filename = Py_NewRef(filename_ob); + tok->module = Py_XNewRef(module); // We need to clear up from here on mod_ty result = NULL; diff --git a/Parser/pegen.h b/Parser/pegen.h index dfa2b0b4fe726c..5c461e82a7f0fa 100644 --- a/Parser/pegen.h +++ b/Parser/pegen.h @@ -162,7 +162,6 @@ int _PyPegen_fill_token(Parser *p); expr_ty _PyPegen_name_token(Parser *p); expr_ty _PyPegen_number_token(Parser *p); void *_PyPegen_string_token(Parser *p); -PyObject *_PyPegen_set_source_in_metadata(Parser *p, Token *t); Py_ssize_t _PyPegen_byte_offset_to_character_offset_line(PyObject *line, Py_ssize_t col_offset, Py_ssize_t end_col_offset); Py_ssize_t _PyPegen_byte_offset_to_character_offset(PyObject *line, Py_ssize_t col_offset); Py_ssize_t _PyPegen_byte_offset_to_character_offset_raw(const char*, Py_ssize_t col_offset); @@ -175,7 +174,6 @@ typedef enum { } TARGETS_TYPE; int _Pypegen_raise_decode_error(Parser *p); -void _PyPegen_raise_tokenizer_init_error(PyObject *filename); int _Pypegen_tokenizer_error(Parser *p); void *_PyPegen_raise_error(Parser *p, PyObject *errtype, int use_mark, const char *errmsg, ...); void *_PyPegen_raise_error_known_location(Parser *p, PyObject *errtype, @@ -359,6 +357,7 @@ expr_ty _PyPegen_ensure_real(Parser *p, expr_ty); asdl_seq *_PyPegen_join_sequences(Parser *, asdl_seq *, asdl_seq *); int _PyPegen_check_barry_as_flufl(Parser *, Token *); int _PyPegen_check_legacy_stmt(Parser *p, expr_ty t); +void *_PyPegen_raise_error_for_missing_comma(Parser *p, expr_ty a, expr_ty b); ResultTokenWithMetadata *_PyPegen_check_fstring_conversion(Parser *p, Token *, expr_ty t); ResultTokenWithMetadata *_PyPegen_setup_full_format_spec(Parser *, Token *, asdl_expr_seq *, int, int, int, int, PyArena *); @@ -367,7 +366,7 @@ void *_PyPegen_arguments_parsing_error(Parser *, expr_ty); expr_ty _PyPegen_get_last_comprehension_item(comprehension_ty comprehension); void *_PyPegen_nonparen_genexp_in_call(Parser *p, expr_ty args, asdl_comprehension_seq *comprehensions); stmt_ty _PyPegen_checked_future_import(Parser *p, identifier module, asdl_alias_seq *, - int , int, int , int , int , PyArena *); + int, expr_ty, int, int, int, int, PyArena *); asdl_stmt_seq* _PyPegen_register_stmts(Parser *p, asdl_stmt_seq* stmts); stmt_ty _PyPegen_register_stmt(Parser *p, stmt_ty s); @@ -379,7 +378,7 @@ mod_ty _PyPegen_run_parser_from_file_pointer(FILE *, int, PyObject *, const char const char *, const char *, PyCompilerFlags *, int *, PyObject **, PyArena *); void *_PyPegen_run_parser(Parser *); -mod_ty _PyPegen_run_parser_from_string(const char *, int, PyObject *, PyCompilerFlags *, PyArena *); +mod_ty _PyPegen_run_parser_from_string(const char *, int, PyObject *, PyCompilerFlags *, PyArena *, PyObject *); asdl_stmt_seq *_PyPegen_interactive_exit(Parser *); // Generated function in parse.c - function definition in python.gram diff --git a/Parser/pegen_errors.c b/Parser/pegen_errors.c index f62b8695995617..b13e1c079220a9 100644 --- a/Parser/pegen_errors.c +++ b/Parser/pegen_errors.c @@ -2,52 +2,14 @@ #include <errcode.h> #include "pycore_pyerrors.h" // _PyErr_ProgramDecodedTextObject() +#include "pycore_runtime.h" // _Py_ID() +#include "pycore_tuple.h" // _PyTuple_FromPair #include "lexer/state.h" #include "lexer/lexer.h" #include "pegen.h" // TOKENIZER ERRORS -void -_PyPegen_raise_tokenizer_init_error(PyObject *filename) -{ - if (!(PyErr_ExceptionMatches(PyExc_LookupError) - || PyErr_ExceptionMatches(PyExc_SyntaxError) - || PyErr_ExceptionMatches(PyExc_ValueError) - || PyErr_ExceptionMatches(PyExc_UnicodeDecodeError))) { - return; - } - PyObject *errstr = NULL; - PyObject *tuple = NULL; - PyObject *type; - PyObject *value; - PyObject *tback; - PyErr_Fetch(&type, &value, &tback); - errstr = PyObject_Str(value); - if (!errstr) { - goto error; - } - - PyObject *tmp = Py_BuildValue("(OiiO)", filename, 0, -1, Py_None); - if (!tmp) { - goto error; - } - - tuple = PyTuple_Pack(2, errstr, tmp); - Py_DECREF(tmp); - if (!value) { - goto error; - } - PyErr_SetObject(PyExc_SyntaxError, tuple); - -error: - Py_XDECREF(type); - Py_XDECREF(value); - Py_XDECREF(tback); - Py_XDECREF(errstr); - Py_XDECREF(tuple); -} - static inline void raise_unclosed_parentheses_error(Parser *p) { int error_lineno = p->tok->parenlinenostack[p->tok->level-1]; @@ -385,7 +347,7 @@ _PyPegen_raise_error_known_location(Parser *p, PyObject *errtype, if (!tmp) { goto error; } - value = PyTuple_Pack(2, errstr, tmp); + value = _PyTuple_FromPair(errstr, tmp); Py_DECREF(tmp); if (!value) { goto error; diff --git a/Parser/string_parser.c b/Parser/string_parser.c index ebe68989d1af58..b164dfbc81a933 100644 --- a/Parser/string_parser.c +++ b/Parser/string_parser.c @@ -88,7 +88,7 @@ warn_invalid_escape_sequence(Parser *p, const char* buffer, const char *first_in } if (PyErr_WarnExplicitObject(category, msg, p->tok->filename, - lineno, NULL, NULL) < 0) { + lineno, p->tok->module, NULL) < 0) { if (PyErr_ExceptionMatches(category)) { /* Replace the Syntax/DeprecationWarning exception with a SyntaxError to get a more accurate error report */ diff --git a/Parser/tokenizer/file_tokenizer.c b/Parser/tokenizer/file_tokenizer.c index 01e473f58a0777..a11702557a07af 100644 --- a/Parser/tokenizer/file_tokenizer.c +++ b/Parser/tokenizer/file_tokenizer.c @@ -282,10 +282,8 @@ tok_underflow_interactive(struct tok_state *tok) { } static int -tok_underflow_file(struct tok_state *tok) { - if (tok->start == NULL && !INSIDE_FSTRING(tok)) { - tok->cur = tok->inp = tok->buf; - } +tok_underflow_file(struct tok_state *tok) +{ if (tok->decoding_state == STATE_INIT) { /* We have not yet determined the encoding. If an encoding is found, use the file-pointer @@ -296,8 +294,16 @@ tok_underflow_file(struct tok_state *tok) { } assert(tok->decoding_state != STATE_INIT); } + int raw = tok->decoding_readline == NULL; + if (raw && tok->decoding_state != STATE_NORMAL) { + /* Keep the first line in the buffer to validate it later if + * the encoding has not yet been determined. */ + } + else if (tok->start == NULL && !INSIDE_FSTRING(tok)) { + tok->cur = tok->inp = tok->buf; + } /* Read until '\n' or EOF */ - if (tok->decoding_readline != NULL) { + if (!raw) { /* We already have a codec associated with this input. */ if (!tok_readline_recode(tok)) { return 0; @@ -328,20 +334,35 @@ tok_underflow_file(struct tok_state *tok) { ADVANCE_LINENO(); if (tok->decoding_state != STATE_NORMAL) { - if (tok->lineno > 2) { - tok->decoding_state = STATE_NORMAL; - } - else if (!_PyTokenizer_check_coding_spec(tok->cur, strlen(tok->cur), + if (!_PyTokenizer_check_coding_spec(tok->cur, strlen(tok->cur), tok, fp_setreadl)) { return 0; } + if (tok->lineno >= 2) { + tok->decoding_state = STATE_NORMAL; + } } - /* The default encoding is UTF-8, so make sure we don't have any - non-UTF-8 sequences in it. */ - if (!tok->encoding && !_PyTokenizer_ensure_utf8(tok->cur, tok)) { - _PyTokenizer_error_ret(tok); - return 0; + if (raw && tok->decoding_state == STATE_NORMAL) { + const char *line = tok->lineno <= 2 ? tok->buf : tok->cur; + int lineno = tok->lineno <= 2 ? 1 : tok->lineno; + if (!tok->encoding) { + /* The default encoding is UTF-8, so make sure we don't have any + non-UTF-8 sequences in it. */ + if (!_PyTokenizer_ensure_utf8(line, tok, lineno)) { + _PyTokenizer_error_ret(tok); + return 0; + } + } + else { + PyObject *tmp = PyUnicode_Decode(line, strlen(line), + tok->encoding, NULL); + if (tmp == NULL) { + _PyTokenizer_error_ret(tok); + return 0; + } + Py_DECREF(tmp); + } } assert(tok->done == E_OK); return tok->done == E_OK; @@ -357,6 +378,7 @@ _PyTokenizer_FromFile(FILE *fp, const char* enc, return NULL; if ((tok->buf = (char *)PyMem_Malloc(BUFSIZ)) == NULL) { _PyTokenizer_Free(tok); + PyErr_NoMemory(); return NULL; } tok->cur = tok->inp = tok->buf; diff --git a/Parser/tokenizer/helpers.c b/Parser/tokenizer/helpers.c index 5a416adb875aa1..62b0971d418c39 100644 --- a/Parser/tokenizer/helpers.c +++ b/Parser/tokenizer/helpers.c @@ -1,6 +1,8 @@ #include "Python.h" #include "errcode.h" +#include "pycore_runtime.h" // _Py_ID() #include "pycore_token.h" +#include "pycore_tuple.h" // _PyTuple_FromPair #include "../lexer/state.h" @@ -47,8 +49,10 @@ _syntaxerror_range(struct tok_state *tok, const char *format, goto error; } - args = Py_BuildValue("(O(OiiNii))", errmsg, tok->filename, tok->lineno, - col_offset, errtext, tok->lineno, end_col_offset); + args = Py_BuildValue("(O(OiiNii))", errmsg, + tok->filename ? tok->filename : Py_None, + tok->lineno, col_offset, errtext, + tok->lineno, end_col_offset); if (args) { PyErr_SetObject(PyExc_SyntaxError, args); Py_DECREF(args); @@ -63,7 +67,7 @@ _syntaxerror_range(struct tok_state *tok, const char *format, int _PyTokenizer_syntaxerror(struct tok_state *tok, const char *format, ...) { - // This errors are cleaned on startup. Todo: Fix it. + // These errors are cleaned on startup. Todo: Fix it. va_list vargs; va_start(vargs, format); int ret = _syntaxerror_range(tok, format, -1, -1, vargs); @@ -125,7 +129,7 @@ _PyTokenizer_warn_invalid_escape_sequence(struct tok_state *tok, int first_inval } if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, tok->filename, - tok->lineno, NULL, NULL) < 0) { + tok->lineno, tok->module, NULL) < 0) { Py_DECREF(msg); if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) { @@ -147,6 +151,53 @@ _PyTokenizer_warn_invalid_escape_sequence(struct tok_state *tok, int first_inval return 0; } +void +_PyTokenizer_raise_init_error(PyObject *filename) +{ + if (!(PyErr_ExceptionMatches(PyExc_LookupError) + || PyErr_ExceptionMatches(PyExc_SyntaxError) + || PyErr_ExceptionMatches(PyExc_ValueError) + || PyErr_ExceptionMatches(PyExc_UnicodeDecodeError))) { + return; + } + PyObject *errstr = NULL; + PyObject *tuple = NULL; + PyObject *type; + PyObject *value; + PyObject *tback; + PyErr_Fetch(&type, &value, &tback); + if (PyErr_GivenExceptionMatches(value, PyExc_SyntaxError)) { + if (PyObject_SetAttr(value, &_Py_ID(filename), filename)) { + goto error; + } + PyErr_Restore(type, value, tback); + return; + } + errstr = PyObject_Str(value); + if (!errstr) { + goto error; + } + + PyObject *tmp = Py_BuildValue("(OiiO)", filename, 0, -1, Py_None); + if (!tmp) { + goto error; + } + + tuple = _PyTuple_FromPair(errstr, tmp); + Py_DECREF(tmp); + if (!tuple) { + goto error; + } + PyErr_SetObject(PyExc_SyntaxError, tuple); + +error: + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(tback); + Py_XDECREF(errstr); + Py_XDECREF(tuple); +} + int _PyTokenizer_parser_warn(struct tok_state *tok, PyObject *category, const char *format, ...) { @@ -164,7 +215,7 @@ _PyTokenizer_parser_warn(struct tok_state *tok, PyObject *category, const char * } if (PyErr_WarnExplicitObject(category, errmsg, tok->filename, - tok->lineno, NULL, NULL) < 0) { + tok->lineno, tok->module, NULL) < 0) { if (PyErr_ExceptionMatches(category)) { /* Replace the DeprecationWarning exception with a SyntaxError to get a more accurate error report */ @@ -191,6 +242,7 @@ _PyTokenizer_new_string(const char *s, Py_ssize_t len, struct tok_state *tok) char* result = (char *)PyMem_Malloc(len + 1); if (!result) { tok->done = E_NOMEM; + PyErr_NoMemory(); return NULL; } memcpy(result, s, len); @@ -219,6 +271,7 @@ _PyTokenizer_translate_newlines(const char *s, int exec_input, int preserve_crlf buf = PyMem_Malloc(needed_length); if (buf == NULL) { tok->done = E_NOMEM; + PyErr_NoMemory(); return NULL; } for (current = buf; *s; s++, current++) { @@ -414,18 +467,21 @@ _PyTokenizer_check_coding_spec(const char* line, Py_ssize_t size, struct tok_sta if (tok->encoding == NULL) { assert(tok->decoding_readline == NULL); if (strcmp(cs, "utf-8") != 0 && !set_readline(tok, cs)) { + _PyTokenizer_raise_init_error(tok->filename); _PyTokenizer_error_ret(tok); - PyErr_Format(PyExc_SyntaxError, "encoding problem: %s", cs); PyMem_Free(cs); return 0; } tok->encoding = cs; } else { /* then, compare cs with BOM */ if (strcmp(tok->encoding, cs) != 0) { - _PyTokenizer_error_ret(tok); - PyErr_Format(PyExc_SyntaxError, - "encoding problem: %s with BOM", cs); + tok->line_start = line; + tok->cur = (char *)line; + assert(size <= INT_MAX); + _PyTokenizer_syntaxerror_known_range(tok, 0, (int)size, + "encoding problem: %s with BOM", cs); PyMem_Free(cs); + _PyTokenizer_error_ret(tok); return 0; } PyMem_Free(cs); @@ -489,31 +545,47 @@ valid_utf8(const unsigned char* s) return 0; } length = expected + 1; - for (; expected; expected--) - if (s[expected] < 0x80 || s[expected] >= 0xC0) + for (int i = 1; i <= expected; i++) { + if (s[i] < 0x80 || s[i] >= 0xC0) { return 0; + } + } return length; } int -_PyTokenizer_ensure_utf8(char *line, struct tok_state *tok) +_PyTokenizer_ensure_utf8(const char *line, struct tok_state *tok, int lineno) { - int badchar = 0; - unsigned char *c; + const char *badchar = NULL; + const char *c; int length; - for (c = (unsigned char *)line; *c; c += length) { - if (!(length = valid_utf8(c))) { - badchar = *c; + int col_offset = 0; + const char *line_start = line; + for (c = line; *c; c += length) { + if (!(length = valid_utf8((const unsigned char *)c))) { + badchar = c; break; } + col_offset++; + if (*c == '\n') { + lineno++; + col_offset = 0; + line_start = c + 1; + } } if (badchar) { - PyErr_Format(PyExc_SyntaxError, - "Non-UTF-8 code starting with '\\x%.2x' " - "in file %U on line %i, " - "but no encoding declared; " - "see https://peps.python.org/pep-0263/ for details", - badchar, tok->filename, tok->lineno); + tok->lineno = lineno; + tok->line_start = line_start; + tok->cur = (char *)badchar; + _PyTokenizer_syntaxerror_known_range(tok, + col_offset + 1, col_offset + 1, + "Non-UTF-8 code starting with '\\x%.2x'" + "%s%V on line %i, " + "but no encoding declared; " + "see https://peps.python.org/pep-0263/ for details", + (unsigned char)*badchar, + tok->filename ? " in file " : "", tok->filename, "", + lineno); return 0; } return 1; diff --git a/Parser/tokenizer/helpers.h b/Parser/tokenizer/helpers.h index 42ea13cd1f853f..34303999a60aff 100644 --- a/Parser/tokenizer/helpers.h +++ b/Parser/tokenizer/helpers.h @@ -15,6 +15,7 @@ int _PyTokenizer_indenterror(struct tok_state *tok); int _PyTokenizer_warn_invalid_escape_sequence(struct tok_state *tok, int first_invalid_escape_char); int _PyTokenizer_parser_warn(struct tok_state *tok, PyObject *category, const char *format, ...); char *_PyTokenizer_error_ret(struct tok_state *tok); +void _PyTokenizer_raise_init_error(PyObject *filename); char *_PyTokenizer_new_string(const char *s, Py_ssize_t len, struct tok_state *tok); char *_PyTokenizer_translate_newlines(const char *s, int exec_input, int preserve_crlf, struct tok_state *tok); @@ -26,7 +27,7 @@ int _PyTokenizer_check_bom(int get_char(struct tok_state *), struct tok_state *tok); int _PyTokenizer_check_coding_spec(const char* line, Py_ssize_t size, struct tok_state *tok, int set_readline(struct tok_state *, const char *)); -int _PyTokenizer_ensure_utf8(char *line, struct tok_state *tok); +int _PyTokenizer_ensure_utf8(const char *line, struct tok_state *tok, int lineno); #ifdef Py_DEBUG void _PyTokenizer_print_escape(FILE *f, const char *s, Py_ssize_t size); diff --git a/Parser/tokenizer/readline_tokenizer.c b/Parser/tokenizer/readline_tokenizer.c index 22f84c77a12b47..917f7b40cfbbfe 100644 --- a/Parser/tokenizer/readline_tokenizer.c +++ b/Parser/tokenizer/readline_tokenizer.c @@ -97,7 +97,7 @@ tok_underflow_readline(struct tok_state* tok) { ADVANCE_LINENO(); /* The default encoding is UTF-8, so make sure we don't have any non-UTF-8 sequences in it. */ - if (!tok->encoding && !_PyTokenizer_ensure_utf8(tok->cur, tok)) { + if (!tok->encoding && !_PyTokenizer_ensure_utf8(tok->cur, tok, tok->lineno)) { _PyTokenizer_error_ret(tok); return 0; } @@ -114,6 +114,7 @@ _PyTokenizer_FromReadline(PyObject* readline, const char* enc, return NULL; if ((tok->buf = (char *)PyMem_Malloc(BUFSIZ)) == NULL) { _PyTokenizer_Free(tok); + PyErr_NoMemory(); return NULL; } tok->cur = tok->inp = tok->buf; diff --git a/Parser/tokenizer/string_tokenizer.c b/Parser/tokenizer/string_tokenizer.c index 0c26d5df8d4a40..7f07cca37ee019 100644 --- a/Parser/tokenizer/string_tokenizer.c +++ b/Parser/tokenizer/string_tokenizer.c @@ -86,15 +86,18 @@ decode_str(const char *input, int single, struct tok_state *tok, int preserve_cr /* need to check line 1 and 2 separately since check_coding_spec assumes a single line as input */ if (newl[0]) { + tok->lineno = 1; if (!_PyTokenizer_check_coding_spec(str, newl[0] - str, tok, buf_setreadl)) { return NULL; } if (tok->enc == NULL && tok->decoding_state != STATE_NORMAL && newl[1]) { + tok->lineno = 2; if (!_PyTokenizer_check_coding_spec(newl[0]+1, newl[1] - newl[0], tok, buf_setreadl)) return NULL; } } + tok->lineno = 0; if (tok->enc != NULL) { assert(utf8 == NULL); utf8 = _PyTokenizer_translate_into_utf8(str, tok->enc); @@ -102,6 +105,22 @@ decode_str(const char *input, int single, struct tok_state *tok, int preserve_cr return _PyTokenizer_error_ret(tok); str = PyBytes_AS_STRING(utf8); } + else if (!_PyTokenizer_ensure_utf8(str, tok, 1)) { + return _PyTokenizer_error_ret(tok); + } + if (utf8 != NULL) { + char *translated = _PyTokenizer_translate_newlines( + str, single, preserve_crlf, tok); + if (translated == NULL) { + Py_DECREF(utf8); + return _PyTokenizer_error_ret(tok); + } + PyMem_Free(tok->input); + tok->input = translated; + str = translated; + Py_CLEAR(utf8); + } + tok->str = str; assert(tok->decoding_buffer == NULL); tok->decoding_buffer = utf8; /* CAUTION */ return str; diff --git a/Platforms/Android/README.md b/Platforms/Android/README.md new file mode 100644 index 00000000000000..d6f95c365c63a0 --- /dev/null +++ b/Platforms/Android/README.md @@ -0,0 +1,173 @@ +# Python for Android + +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 + +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 + tools package into it. +* Rename `android-sdk/cmdline-tools/cmdline-tools` to + `android-sdk/cmdline-tools/latest`. +* `export ANDROID_HOME=/path/to/android-sdk` + +The `Platforms/Android` 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) + +## Building + +Python can be built for Android on any POSIX platform supported by the 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 `Platforms/Android` script. You can either +have it perform the entire build process from start to finish in one step, or +you can do it in discrete steps that mirror running `configure` and `make` for +each of the two builds of Python you end up producing. + +The discrete steps for building via `Platforms/Android` are: + +```sh +python3 Platforms/Android configure-build +python3 Platforms/Android make-build +python3 Platforms/Android configure-host HOST +python3 Platforms/Android make-host HOST +``` + +`HOST` identifies which architecture to build. To see the possible values, run +`python3 Platforms/Android configure-host --help`. + +To do all steps in a single command, run: + +```sh +python3 Platforms/Android 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` +call. For example, if you want a pydebug build that also caches the results from +`configure`, you can do: + +```sh +python3 Platforms/Android build HOST -- -C --with-pydebug +``` + +## Packaging + +After building an architecture as described in the section above, you can +package it for release with this command: + +```sh +python3 Platforms/Android 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 + +Tests can be run on Linux, macOS, or Windows, using either an Android emulator +or a physical device. + +On Linux, the emulator needs access to the KVM virtualization interface. This may +require adding your user to a group, or changing your udev rules. On GitHub +Actions, the test script will do this automatically using the commands shown +[here](https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/). + +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 + `$ANDROID_HOME/platform-tools/adb devices -l`, then pass a device ID to the + script like this: + + ```sh + python3 Platforms/Android test --connected emulator-5554 + ``` + +* In `--managed` mode, it uses a temporary headless emulator defined in the + `managedDevices` section of testbed/app/build.gradle.kts. This mode is slower, + but more reproducible. + + We currently define two devices: `minVersion` and `maxVersion`, corresponding + to our minimum and maximum supported Android versions. For example: + + ```sh + python3 Platforms/Android test --managed maxVersion + ``` + +By default, the only messages the script will show are Python's own stdout and +stderr. Add the `-v` option to also show Gradle output, and non-Python logcat +messages. + +### Testing Python + +You can run the test suite by doing a build as described above, and then running +`python3 Platforms/Android test`. 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. + +Extra arguments on the `Platforms/Android test` command line will be passed through +to `python -m test` – use `--` to separate them from `Platforms/Android`'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, +such as `-j`. + +Every time you run `python3 Platforms/Android test`, changes in pure-Python files in the +repository's `Lib` directory will be picked up immediately. Changes in C files, +and architecture-specific files such as sysconfigdata, will not take effect +until you re-run `python3 Platforms/Android make-host` or `build`. + +### Testing a third-party package + +The `Platforms/Android` script is also included as `android.py` in the root of a +release package (i.e., the one built using `Platforms/Android package`). + +You can use this script to test third-party packages by taking a release +package, extracting it wherever you want, and using the `android.py` script to +run the test suite for your third-party package. + +Any argument that can be passed to `python3 Platforms/Android test` can also be +passed to `android.py`. The following options will be of particular use when +configuring the execution of a third-party test suite: + +* `--cwd`: the directory of content to copy into the testbed app as the working + directory. +* `--site-packages`: the directory to copy into the testbed app to use as site + packages. + +Extra arguments on the `android.py test` command line will be passed through to +Python – use `--` to separate them from `android.py`'s own options. You must include +either a `-c` or `-m` argument to specify how the test suite should be started. + +For more details, run `android.py test --help`. + +## Using in your own app + +See https://docs.python.org/3/using/android.html. diff --git a/Platforms/Android/__main__.py b/Platforms/Android/__main__.py new file mode 100755 index 00000000000000..063e1f31603144 --- /dev/null +++ b/Platforms/Android/__main__.py @@ -0,0 +1,1059 @@ +#!/usr/bin/env python3 + +import asyncio +import argparse +import json +import os +import platform +import re +import shlex +import shutil +import signal +import subprocess +import sys +import sysconfig +from asyncio import wait_for +from contextlib import asynccontextmanager +from datetime import datetime, timezone +from enum import IntEnum, auto +from glob import glob +from os.path import abspath, basename, relpath +from pathlib import Path +from subprocess import CalledProcessError +from tempfile import TemporaryDirectory + + +SCRIPT_NAME = Path(__file__).name +if SCRIPT_NAME.startswith("__"): + SCRIPT_NAME = "Platforms/Android" + +ANDROID_DIR = Path(__file__).resolve().parent +PYTHON_DIR = ANDROID_DIR.parent.parent +in_source_tree = ( + ANDROID_DIR.name == "Android" and (PYTHON_DIR / "pyconfig.h.in").exists() +) + +ENV_SCRIPT = ANDROID_DIR / "android-env.sh" +TESTBED_DIR = ANDROID_DIR / "testbed" +CROSS_BUILD_DIR = PYTHON_DIR / "cross-build" + +HOSTS = [ + "aarch64-linux-android", + "arm-linux-androideabi", + "i686-linux-android", + "x86_64-linux-android", +] +APP_ID = "org.python.testbed" +DECODE_ARGS = ("UTF-8", "backslashreplace") + + +try: + android_home = Path(os.environ['ANDROID_HOME']) +except KeyError: + sys.exit("The ANDROID_HOME environment variable is required.") + +adb = Path( + f"{android_home}/platform-tools/adb" + + (".exe" if os.name == "nt" else "") +) + +gradlew = Path( + f"{TESTBED_DIR}/gradlew" + + (".bat" if os.name == "nt" else "") +) + +# Whether we've seen any output from Python yet. +python_started = False + +# Buffer for verbose output which will be displayed only if a test fails and +# there has been no output from Python. +hidden_output = [] + + +# Based on android/log.h in the NDK. +class LogPriority(IntEnum): + UNKNOWN = 0 + DEFAULT = auto() + VERBOSE = auto() + DEBUG = auto() + INFO = auto() + WARN = auto() + ERROR = auto() + FATAL = auto() + SILENT = auto() + + +def log_verbose(context, line, stream=sys.stdout): + if context.verbose: + stream.write(line) + else: + hidden_output.append((stream, line)) + + +def delete_glob(pattern): + # Path.glob doesn't accept non-relative patterns. + for path in glob(str(pattern)): + path = Path(path) + print(f"Deleting {path} ...") + if path.is_dir() and not path.is_symlink(): + shutil.rmtree(path) + else: + path.unlink() + + +def subdir(*parts, create=False): + path = CROSS_BUILD_DIR.joinpath(*parts) + if not path.exists(): + if not create: + sys.exit( + f"{path} does not exist. Create it by running the appropriate " + f"`configure` subcommand of {SCRIPT_NAME}.") + else: + path.mkdir(parents=True) + return path + + +def run(command, *, host=None, env=None, log=True, **kwargs): + kwargs.setdefault("check", True) + if env is None: + env = os.environ.copy() + + if host: + host_env = android_env(host) + print_env(host_env) + env.update(host_env) + + if log: + print(">", join_command(command)) + return subprocess.run(command, env=env, **kwargs) + + +# Format a command so it can be copied into a shell. Like shlex.join, but also +# accepts arguments which are Paths, or a single string/Path outside of a list. +def join_command(args): + if isinstance(args, (str, Path)): + return str(args) + else: + return shlex.join(map(str, args)) + + +# Format the environment so it can be pasted into a shell. +def print_env(env): + for key, value in sorted(env.items()): + print(f"export {key}={shlex.quote(value)}") + + +def android_env(host): + if host: + prefix = subdir(host) / "prefix" + else: + prefix = ANDROID_DIR / "prefix" + sysconfig_files = prefix.glob("lib/python*/_sysconfigdata__android_*.py") + sysconfig_filename = next(sysconfig_files).name + host = re.fullmatch(r"_sysconfigdata__android_(.+).py", sysconfig_filename)[1] + + env_output = subprocess.run( + f"set -eu; " + f"HOST={host}; " + f"PREFIX={prefix}; " + f". {ENV_SCRIPT}; " + f"export", + check=True, shell=True, capture_output=True, encoding='utf-8', + ).stdout + + env = {} + for line in env_output.splitlines(): + # We don't require every line to match, as there may be some other + # output from installing the NDK. + if match := re.search( + "^(declare -x |export )?(\\w+)=['\"]?(.*?)['\"]?$", line + ): + key, value = match[2], match[3] + if os.environ.get(key) != value: + env[key] = value + + if not env: + raise ValueError(f"Found no variables in {ENV_SCRIPT.name} output:\n" + + env_output) + return env + + +def build_python_path(): + """The path to the build Python binary.""" + build_dir = subdir("build") + binary = build_dir / "python" + if not binary.is_file(): + binary = binary.with_suffix(".exe") + if not binary.is_file(): + raise FileNotFoundError("Unable to find `python(.exe)` in " + f"{build_dir}") + + return binary + + +def configure_build_python(context): + if context.clean: + clean("build") + os.chdir(subdir("build", create=True)) + + command = [relpath(PYTHON_DIR / "configure")] + if context.args: + command.extend(context.args) + run(command) + + +def make_build_python(context): + os.chdir(subdir("build")) + run(["make", "-j", str(os.cpu_count())]) + + +# To create new builds of these dependencies, usually all that's necessary is to +# push a tag to the cpython-android-source-deps repository, and GitHub Actions +# will do the rest. +# +# If you're a member of the Python core team, and you'd like to be able to push +# these tags yourself, please contact Malcolm Smith or Russell Keith-Magee. +def unpack_deps(host, prefix_dir, cache_dir): + os.chdir(prefix_dir) + deps_url = "https://github.com/beeware/cpython-android-source-deps/releases/download" + for name_ver in [ + "bzip2-1.0.8-3", + "libffi-3.4.4-3", + "openssl-3.5.7-0", + "sqlite-3.53.2-0", + "xz-5.4.6-1", + "zstd-1.5.7-2" + ]: + filename = f"{name_ver}-{host}.tar.gz" + out_path = download(f"{deps_url}/{name_ver}/{filename}", cache_dir) + shutil.unpack_archive(out_path) + + +def download(url, cache_dir): + out_path = cache_dir / basename(url) + cache_dir.mkdir(parents=True, exist_ok=True) + if not out_path.is_file(): + run(["curl", "-Lf", "--retry", "5", "--retry-all-errors", "-o", out_path, url]) + else: + print(f"Using cached version of {basename(url)}") + return out_path + + +def configure_host_python(context, host=None): + if host is None: + host = context.host + if context.clean: + clean(host) + + host_dir = subdir(host, create=True) + prefix_dir = host_dir / "prefix" + if not prefix_dir.exists(): + prefix_dir.mkdir() + cache_dir = ( + Path(context.cache_dir).resolve() + if context.cache_dir + else CROSS_BUILD_DIR / "downloads" + ) + unpack_deps(host, prefix_dir, cache_dir) + + os.chdir(host_dir) + command = [ + # Basic cross-compiling configuration + relpath(PYTHON_DIR / "configure"), + f"--host={host}", + f"--build={sysconfig.get_config_var('BUILD_GNU_TYPE')}", + f"--with-build-python={build_python_path()}", + "--without-ensurepip", + + # Android always uses a shared libpython. + "--enable-shared", + "--without-static-libpython", + + # Dependent libraries. The others are found using pkg-config: see + # android-env.sh. + f"--with-openssl={prefix_dir}", + ] + + if context.args: + command.extend(context.args) + run(command, host=host) + + +def make_host_python(context, host=None): + if host is None: + host = context.host + # The CFLAGS and LDFLAGS set in android-env include the prefix dir, so + # delete any previous Python installation to prevent it being used during + # the build. + host_dir = subdir(host) + prefix_dir = host_dir / "prefix" + for pattern in ("include/python*", "lib/libpython*", "lib/python*"): + delete_glob(f"{prefix_dir}/{pattern}") + + # The Android environment variables were already captured in the Makefile by + # `configure`, and passing them again when running `make` may cause some + # flags to be duplicated. So we don't use the `host` argument here. + os.chdir(host_dir) + run(["make", "-j", str(os.cpu_count())]) + + # The `make install` output is very verbose and rarely useful, so + # suppress it by default. + run( + ["make", "install", f"prefix={prefix_dir}"], + capture_output=not context.verbose, + ) + + +def build_targets(context): + if context.target in {"all", "build"}: + configure_build_python(context) + make_build_python(context) + + for host in HOSTS: + if context.target in {"all", "hosts", host}: + configure_host_python(context, host) + make_host_python(context, host) + + +def clean(host): + delete_glob(CROSS_BUILD_DIR / host) + + +def clean_targets(context): + if context.target in {"all", "build"}: + clean("build") + + for host in HOSTS: + if context.target in {"all", "hosts", host}: + clean(host) + + +def setup_ci(): + if "GITHUB_ACTIONS" in os.environ: + # Enable emulator hardware acceleration + # (https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/). + if platform.system() == "Linux": + run( + ["sudo", "tee", "/etc/udev/rules.d/99-kvm4all.rules"], + input='KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"\n', + text=True, + ) + run(["sudo", "udevadm", "control", "--reload-rules"]) + run(["sudo", "udevadm", "trigger", "--name-match=kvm"]) + + # Free up disk space by deleting unused versions of the NDK + # (https://github.com/freakboy3742/pyspamsum/pull/108). + for line in ENV_SCRIPT.read_text().splitlines(): + if match := re.fullmatch(r"ndk_version=(.+)", line): + ndk_version = match[1] + break + else: + raise ValueError(f"Failed to find NDK version in {ENV_SCRIPT.name}") + + for item in (android_home / "ndk").iterdir(): + if item.name[0].isdigit() and item.name != ndk_version: + delete_glob(item) + + +def setup_sdk(): + sdkmanager = android_home / ( + "cmdline-tools/latest/bin/sdkmanager" + + (".bat" if os.name == "nt" else "") + ) + + # Gradle will fail if it needs to install an SDK package whose license + # hasn't been accepted, so pre-accept all licenses. + if not all((android_home / "licenses" / path).exists() for path in [ + "android-sdk-arm-dbt-license", "android-sdk-license" + ]): + run( + [sdkmanager, "--licenses"], + text=True, + capture_output=True, + input="y\n" * 100, + ) + + # Gradle may install this automatically, but we can't rely on that because + # we need to run adb within the logcat task. + if not adb.exists(): + run([sdkmanager, "platform-tools"]) + + +# 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 GitHub repository. +def setup_testbed(): + paths = ["gradlew", "gradlew.bat", "gradle/wrapper/gradle-wrapper.jar"] + if all((TESTBED_DIR / path).exists() for path in paths): + return + + # 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 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) + + +# Work around a bug involving sys.exit and TaskGroups +# (https://github.com/python/cpython/issues/101515). +def exit(*args): + raise MySystemExit(*args) + + +class MySystemExit(Exception): + pass + + +# The `test` subcommand runs all subprocesses through this context manager so +# that no matter what happens, they can always be cancelled from another task, +# and they will always be cleaned up on exit. +@asynccontextmanager +async def async_process(*args, **kwargs): + process = await asyncio.create_subprocess_exec(*args, **kwargs) + try: + yield process + finally: + if process.returncode is None: + # Allow a reasonably long time for Gradle to clean itself up, + # because we don't want stale emulators left behind. + timeout = 10 + process.terminate() + try: + await wait_for(process.wait(), timeout) + except TimeoutError: + print( + f"Command {args} did not terminate after {timeout} seconds " + f" - sending SIGKILL" + ) + process.kill() + + # Even after killing the process we must still wait for it, + # otherwise we'll get the warning "Exception ignored in __del__". + await wait_for(process.wait(), timeout=1) + + +async def async_check_output(*args, **kwargs): + async with async_process( + *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs + ) as process: + stdout, stderr = await process.communicate() + if process.returncode == 0: + return stdout.decode(*DECODE_ARGS) + else: + raise CalledProcessError( + process.returncode, args, + stdout.decode(*DECODE_ARGS), stderr.decode(*DECODE_ARGS) + ) + + +# Return a list of the serial numbers of connected devices. Emulators will have +# serials of the form "emulator-5678". +async def list_devices(): + serials = [] + header_found = False + + lines = (await async_check_output(adb, "devices")).splitlines() + for line in lines: + # Ignore blank lines, and all lines before the header. + line = line.strip() + if line == "List of devices attached": + header_found = True + elif header_found and line: + try: + serial, status = line.split() + except ValueError: + raise ValueError(f"failed to parse {line!r}") + if status == "device": + serials.append(serial) + + if not header_found: + raise ValueError(f"failed to parse {lines}") + return serials + + +async def find_device(context, initial_devices): + if context.managed: + print("Waiting for managed device - this may take several minutes") + while True: + new_devices = set(await list_devices()).difference(initial_devices) + if len(new_devices) == 0: + await asyncio.sleep(1) + elif len(new_devices) == 1: + serial = new_devices.pop() + print(f"Serial: {serial}") + return serial + else: + exit(f"Found more than one new device: {new_devices}") + else: + return context.connected + + +# An older version of this script in #121595 filtered the logs by UID instead. +# But logcat can't filter by UID until API level 31. If we ever switch back to +# filtering by UID, we'll also have to filter by time so we only show messages +# produced after the initial call to `stop_app`. +# +# We're more likely to miss the PID because it's shorter-lived, so there's a +# workaround in PythonSuite.kt to stop it being *too* short-lived. +async def find_pid(serial): + print("Waiting for app to start - this may take several minutes") + shown_error = False + while True: + try: + # `pidof` requires API level 24 or higher. The level 23 emulator + # includes it, but it doesn't work (it returns all processes). + pid = (await async_check_output( + adb, "-s", serial, "shell", "pidof", "-s", APP_ID + )).strip() + except CalledProcessError as e: + # If the app isn't running yet, pidof gives no output. So if there + # is output, there must have been some other error. However, this + # sometimes happens transiently, especially when running a managed + # emulator for the first time, so don't make it fatal. + if (e.stdout or e.stderr) and not shown_error: + print_called_process_error(e) + print("This may be transient, so continuing to wait") + shown_error = True + else: + # Some older devices (e.g. Nexus 4) return zero even when no process + # was found, so check whether we actually got any output. + if pid: + print(f"PID: {pid}") + return pid + + # Loop fairly rapidly to avoid missing a short-lived process. + await asyncio.sleep(0.2) + + +async def logcat_task(context, initial_devices): + # Gradle may need to do some large downloads of libraries and emulator + # images. This will happen during find_device in --managed mode, or find_pid + # in --connected mode. + startup_timeout = 600 + serial = await wait_for(find_device(context, initial_devices), startup_timeout) + pid = await wait_for(find_pid(serial), startup_timeout) + + # `--pid` requires API level 24 or higher. + # + # `--binary` mode is used in order to detect which messages end with a + # newline, which most of the other modes don't indicate (except `--format + # long`). For example, every time pytest runs a test, it prints a "." and + # flushes the stream. Each "." becomes a separate log message, but we should + # show them all on the same line. + args = [adb, "-s", serial, "logcat", "--pid", pid, "--binary"] + logcat_started = False + async with async_process( + *args, stdout=subprocess.PIPE, stderr=None + ) as process: + while True: + try: + priority, tag, message = await read_logcat(process.stdout) + logcat_started = True + except asyncio.IncompleteReadError: + break + + # Exclude high-volume messages which are rarely useful. + if context.verbose < 2 and "from python test_syslog" in message: + continue + + # Put high-level messages on stderr so they're highlighted in the + # buildbot logs. This will include Python's own stderr. + stream = sys.stderr if priority >= LogPriority.WARN else sys.stdout + + # The app's stdout and stderr should be passed through transparently + # to our own corresponding streams. + if tag in ["python.stdout", "python.stderr"]: + global python_started + python_started = True + stream.write(message) + stream.flush() + else: + # Non-Python messages add a lot of noise, but they may + # sometimes help explain a failure. Format them in the same way + # as `logcat --format tag`. + formatted = f"{priority.name[0]}/{tag}: {message}" + if not formatted.endswith("\n"): + formatted += "\n" + log_verbose(context, formatted, stream) + + # If the device disconnects while logcat is running, which always + # happens in --managed mode, some versions of adb return non-zero. + # Distinguish this from a logcat startup error by checking whether we've + # received any logcat messages yet. + status = await wait_for(process.wait(), timeout=1) + if status != 0 and not logcat_started: + raise CalledProcessError(status, args) + + +# Read one binary log message from the given StreamReader. The message format is +# described at https://android.stackexchange.com/a/74660. All supported versions +# of Android use format version 2 or later. +async def read_logcat(stream): + async def read_bytes(size): + return await stream.readexactly(size) + + async def read_int(size): + return int.from_bytes(await read_bytes(size), "little") + + payload_len = await read_int(2) + if payload_len < 2: + # 1 byte for priority, 1 byte for null terminator of tag. + raise ValueError(f"payload length {payload_len} is too short") + + header_len = await read_int(2) + if header_len < 4: + raise ValueError(f"header length {header_len} is too short") + await read_bytes(header_len - 4) # Ignore other header fields. + + priority_int = await read_int(1) + try: + priority = LogPriority(priority_int) + except ValueError: + priority = LogPriority.UNKNOWN + + payload_fields = (await read_bytes(payload_len - 1)).split(b"\0") + if len(payload_fields) < 2: + raise ValueError( + f"payload {payload!r} does not contain at least 2 " + f"null-separated fields" + ) + tag, message, *_ = [ + field.decode(*DECODE_ARGS) for field in payload_fields + ] + return priority, tag, message + + +def stop_app(serial): + run([adb, "-s", serial, "shell", "am", "force-stop", APP_ID], log=False) + + +async def gradle_task(context): + env = os.environ.copy() + if context.managed: + task_prefix = context.managed + else: + task_prefix = "connected" + env["ANDROID_SERIAL"] = context.connected + + # Ensure that CROSS_BUILD_DIR is in the Gradle environment, regardless + # of whether it was set by environment variable or `--cross-build-dir`. + env["CROSS_BUILD_DIR"] = CROSS_BUILD_DIR + + if context.ci_mode: + context.args[0:0] = [ + # See _add_ci_python_opts in libregrtest/main.py. + "-W", "error", "-bb", "-E", + + # Randomization is disabled because order-dependent failures are + # much less likely to pass on a rerun in single-process mode. + "-m", "test", + f"--{context.ci_mode}-ci", "--single-process", "--no-randomize", + "--pythoninfo", + ] + + if not any(arg in context.args for arg in ["-c", "-m"]): + context.args[0:0] = ["-m", "test"] + + args = [ + gradlew, "--console", "plain", f"{task_prefix}DebugAndroidTest", + ] + [ + f"-P{name}={value}" + for name, value in [ + ("python.sitePackages", context.site_packages), + ("python.cwd", context.cwd), + ( + "android.testInstrumentationRunnerArguments.pythonArgs", + json.dumps(context.args), + ), + ] + if value + ] + if context.verbose >= 2: + args.append("--info") + log_verbose(context, f"> {join_command(args)}\n") + + try: + async with async_process( + *args, cwd=TESTBED_DIR, env=env, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + ) as process: + while line := (await process.stdout.readline()).decode(*DECODE_ARGS): + # Gradle may take several minutes to install SDK packages, so + # it's worth showing those messages even in non-verbose mode. + if line.startswith('Preparing "Install'): + sys.stdout.write(line) + else: + log_verbose(context, line) + + status = await wait_for(process.wait(), timeout=1) + if status == 0: + exit(0) + else: + raise CalledProcessError(status, args) + finally: + # Gradle does not stop the tests when interrupted. + if context.connected: + stop_app(context.connected) + + +async def run_testbed(context): + setup_ci() + setup_sdk() + setup_testbed() + + if context.managed: + # In this mode, Gradle will create a device with an unpredictable name. + # So we save a list of the running devices before starting Gradle, and + # find_device then waits for a new device to appear. + initial_devices = await list_devices() + else: + # In case the previous shutdown was unclean, make sure the app isn't + # running, otherwise we might show logs from a previous run. This is + # unnecessary in --managed mode, because Gradle creates a new emulator + # every time. + stop_app(context.connected) + initial_devices = None + + try: + async with asyncio.TaskGroup() as tg: + tg.create_task(logcat_task(context, initial_devices)) + tg.create_task(gradle_task(context)) + except* MySystemExit as e: + raise SystemExit(*e.exceptions[0].args) from None + except* CalledProcessError as e: + # If Python produced no output, then the user probably wants to see the + # verbose output to explain why the test failed. + if not python_started: + for stream, line in hidden_output: + stream.write(line) + + # Extract it from the ExceptionGroup so it can be handled by `main`. + 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.replace("/", "-")) 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 + # "__main__.py" is renamed "android.py" for distribution purpose + dst = temp_dir / { + "__main__.py": "android.py" + }.get(line, 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) + + # Strip debug information. + if not context.debug: + so_files = glob(f"{temp_dir}/**/*.so", recursive=True) + run([android_env(context.host)["STRIP"], *so_files], log=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}") + return package_path + + +def ci(context): + for step in [ + configure_build_python, + make_build_python, + configure_host_python, + make_host_python, + package, + ]: + caption = ( + step.__name__.replace("_", " ") + .capitalize() + .replace("python", "Python") + ) + print(f"::group::{caption}") + result = step(context) + if step is package: + package_path = result + print("::endgroup::") + + if ( + "GITHUB_ACTIONS" in os.environ + and (platform.system(), platform.machine()) != ("Linux", "x86_64") + ): + print( + "Skipping tests: GitHub Actions does not support the Android " + "emulator on this platform." + ) + else: + with TemporaryDirectory(prefix=SCRIPT_NAME.replace("/", "-")) as temp_dir: + print("::group::Tests") + + # Prove the package is self-contained by using it to run the tests. + shutil.unpack_archive(package_path, temp_dir) + launcher_args = [ + "--managed", "maxVersion", "-v", f"--{context.ci_mode}-ci" + ] + run( + ["./android.py", "test", *launcher_args], + cwd=temp_dir + ) + print("::endgroup::") + + +def env(context): + print_env(android_env(getattr(context, "host", None))) + + +# 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(): + def signal_handler(*args): + os.kill(os.getpid(), signal.SIGINT) + + signal.signal(signal.SIGTERM, signal_handler) + + +def parse_args(): + parser = argparse.ArgumentParser() + subcommands = parser.add_subparsers(dest="subcommand", required=True) + + def add_parser(*args, **kwargs): + parser = subcommands.add_parser(*args, **kwargs) + parser.add_argument( + "--cross-build-dir", + action="store", + default=os.environ.get("CROSS_BUILD_DIR"), + dest="cross_build_dir", + type=Path, + help=( + "Path to the cross-build directory " + f"(default: {CROSS_BUILD_DIR}). Can also be set " + "with the CROSS_BUILD_DIR environment variable." + ), + ) + parser.add_argument( + "-v", "--verbose", action="count", default=0, + help="Show verbose output. Use twice to be even more verbose.") + return parser + + # Subcommands + build = add_parser( + "build", + help="Run configure and make for the selected target" + ) + configure_build = add_parser( + "configure-build", help="Run `configure` for the build Python") + add_parser( + "make-build", help="Run `make` for the build Python") + configure_host = add_parser( + "configure-host", help="Run `configure` for Android") + make_host = add_parser( + "make-host", help="Run `make` for Android") + + clean = add_parser( + "clean", + help="Delete build directories for the selected target" + ) + + test = add_parser("test", help="Run the testbed app") + package = add_parser("package", help="Make a release package") + ci = add_parser("ci", help="Run build, package and test") + env = add_parser("env", help="Print environment variables") + + # Common arguments + # --cache-dir option + for cmd in [configure_host, build, ci]: + cmd.add_argument( + "--cache-dir", + default=os.environ.get("CACHE_DIR"), + help="The directory to store cached downloads.", + ) + + # --clean option + for subcommand in [build, configure_build, configure_host, ci]: + subcommand.add_argument( + "--clean", action="store_true", default=False, dest="clean", + help="Delete the relevant build directories first") + + # Allow "all", "build" and "hosts" targets for some commands + for subcommand in [clean, build]: + subcommand.add_argument( + "target", + nargs="?", + default="all", + choices=["all", "build", "hosts"] + HOSTS, + help=( + "The host triplet (e.g., aarch64-linux-android), " + "or 'build' for just the build platform, or 'hosts' for all " + "host platforms, or 'all' for the build platform and all " + "hosts. Defaults to 'all'" + ), + ) + + host_commands = [configure_host, make_host, package, ci] + if in_source_tree: + host_commands.append(env) + for subcommand in host_commands: + subcommand.add_argument( + "host", metavar="HOST", choices=HOSTS, + help="Host triplet: choices=[%(choices)s]") + + for subcommand in [build, configure_build, configure_host, ci]: + subcommand.add_argument("args", nargs="*", + help="Extra arguments to pass to `configure`") + + # Test arguments + device_group = test.add_mutually_exclusive_group(required=True) + device_group.add_argument( + "--connected", metavar="SERIAL", help="Run on a connected device. " + "Connect it yourself, then get its serial from `adb devices`.") + device_group.add_argument( + "--managed", metavar="NAME", help="Run on a Gradle-managed device. " + "These are defined in `managedDevices` in testbed/app/build.gradle.kts.") + + test.add_argument( + "--site-packages", metavar="DIR", type=abspath, + help="Directory to copy as the app's site-packages.") + test.add_argument( + "--cwd", metavar="DIR", type=abspath, + help="Directory to copy as the app's working directory.") + test.add_argument( + "args", nargs="*", help=f"Python command-line arguments. " + f"Separate them from {SCRIPT_NAME}'s own arguments with `--`. " + f"If neither -c nor -m are included, `-m test` will be prepended, " + f"which will run Python's own test suite.") + + # Package arguments. + for subcommand in [package, ci]: + subcommand.add_argument( + "-g", action="store_true", default=False, dest="debug", + help="Include debug information in package") + + # CI arguments + for subcommand in [test, ci]: + group = subcommand.add_mutually_exclusive_group(required=subcommand is ci) + group.add_argument( + "--fast-ci", action="store_const", dest="ci_mode", const="fast", + help="Add test arguments for GitHub Actions") + group.add_argument( + "--slow-ci", action="store_const", dest="ci_mode", const="slow", + help="Add test arguments for buildbots") + + return parser.parse_args() + + +def main(): + install_signal_handler() + + # Under the buildbot, stdout is not a TTY, but we must still flush after + # every line to make sure our output appears in the correct order relative + # to the output of our subprocesses. + for stream in [sys.stdout, sys.stderr]: + stream.reconfigure(line_buffering=True) + + context = parse_args() + + # Set the CROSS_BUILD_DIR if an argument was provided + if context.cross_build_dir: + global CROSS_BUILD_DIR + CROSS_BUILD_DIR = context.cross_build_dir.resolve() + + dispatch = { + "configure-build": configure_build_python, + "make-build": make_build_python, + "configure-host": configure_host_python, + "make-host": make_host_python, + "build": build_targets, + "clean": clean_targets, + "test": run_testbed, + "package": package, + "ci": ci, + "env": env, + } + + try: + result = dispatch[context.subcommand](context) + if asyncio.iscoroutine(result): + asyncio.run(result) + except CalledProcessError as e: + print_called_process_error(e) + sys.exit(1) + + +def print_called_process_error(e): + for stream_name in ["stdout", "stderr"]: + content = getattr(e, stream_name) + if isinstance(content, bytes): + content = content.decode(*DECODE_ARGS) + stream = getattr(sys, stream_name) + if content: + stream.write(content) + if not content.endswith("\n"): + stream.write("\n") + + # shlex uses single quotes, so we surround the command with double quotes. + print( + f'Command "{join_command(e.cmd)}" returned exit status {e.returncode}' + ) + + +if __name__ == "__main__": + main() diff --git a/Android/android-env.sh b/Platforms/Android/android-env.sh similarity index 99% rename from Android/android-env.sh rename to Platforms/Android/android-env.sh index 7b381a013cf0ba..5859c0eac4a88f 100644 --- a/Android/android-env.sh +++ b/Platforms/Android/android-env.sh @@ -24,7 +24,7 @@ fail() { # * https://android.googlesource.com/platform/ndk/+/ndk-rXX-release/docs/BuildSystemMaintainers.md # where XX is the NDK version. Do a diff against the version you're upgrading from, e.g.: # https://android.googlesource.com/platform/ndk/+/ndk-r25-release..ndk-r26-release/docs/BuildSystemMaintainers.md -ndk_version=27.2.12479018 +ndk_version=27.3.13750724 ndk=$ANDROID_HOME/ndk/$ndk_version if ! [ -e "$ndk" ]; then diff --git a/Android/testbed/.gitignore b/Platforms/Android/testbed/.gitignore similarity index 100% rename from Android/testbed/.gitignore rename to Platforms/Android/testbed/.gitignore diff --git a/Android/testbed/.idea/inspectionProfiles/Project_Default.xml b/Platforms/Android/testbed/.idea/inspectionProfiles/Project_Default.xml similarity index 100% rename from Android/testbed/.idea/inspectionProfiles/Project_Default.xml rename to Platforms/Android/testbed/.idea/inspectionProfiles/Project_Default.xml diff --git a/Android/testbed/app/.gitignore b/Platforms/Android/testbed/app/.gitignore similarity index 100% rename from Android/testbed/app/.gitignore rename to Platforms/Android/testbed/app/.gitignore diff --git a/Platforms/Android/testbed/app/build.gradle.kts b/Platforms/Android/testbed/app/build.gradle.kts new file mode 100644 index 00000000000000..e51398fce81e26 --- /dev/null +++ b/Platforms/Android/testbed/app/build.gradle.kts @@ -0,0 +1,429 @@ +import com.android.build.api.variant.* +import kotlin.math.max + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +val ANDROID_DIR = file("../..") +val PYTHON_DIR = ANDROID_DIR.parentFile.parentFile!! +val PYTHON_CROSS_DIR = file(System.getenv("CROSS_BUILD_DIR") ?: "$PYTHON_DIR/cross-build") +val inSourceTree = ( + ANDROID_DIR.name == "Android" && file("$PYTHON_DIR/pyconfig.h.in").exists() +) + +val KNOWN_ABIS = mapOf( + "aarch64-linux-android" to "arm64-v8a", + "arm-linux-androideabi" to "armeabi-v7a", + "i686-linux-android" to "x86", + "x86_64-linux-android" to "x86_64", +) + +val osArch = System.getProperty("os.arch") +val NATIVE_ABI = mapOf( + "aarch64" to "arm64-v8a", + "amd64" to "x86_64", + "arm64" to "arm64-v8a", + "x86_64" to "x86_64", +)[osArch] ?: throw GradleException("Unknown os.arch '$osArch'") + +// 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 prefixes found: see README.md for testing instructions" + ) +} + +// 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+[a-z]*)""".toRegex().matchEntire(filename)?.let { + return@run it.groupValues[1] + } + } + throw GradleException("Failed to find Python version in $libDir") + } + 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_[a-z]*_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]!! +} + + +android { + val androidEnvFile = file("../../android-env.sh").absoluteFile + + namespace = "org.python.testbed" + compileSdk = 35 + + defaultConfig { + applicationId = "org.python.testbed" + + minSdk = androidEnvFile.useLines { + for (line in it) { + """ANDROID_API_LEVEL:=(\d+)""".toRegex().find(line)?.let { + return@useLines it.groupValues[1].toInt() + } + } + throw GradleException("Failed to find API level in $androidEnvFile") + } + + // This controls the API level of the maxVersion managed emulator, which is used + // by CI and cibuildwheel. + // * 33 has excessive buffering in the logcat client + // (https://cs.android.com/android/_/android/platform/system/logging/+/d340721894f223327339010df59b0ac514308826). + // * 34 consumes too much disk space on GitHub Actions (#142289). + // * 35 has issues connecting to the internet (#142387). + // * 36 and later are not available as aosp_atd images yet. + targetSdk = 32 + + versionCode = 1 + versionName = "1.0" + + ndk.abiFilters.addAll(abis.values) + externalNativeBuild.cmake.arguments( + "-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", + ) + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + ndkVersion = androidEnvFile.useLines { + for (line in it) { + """ndk_version=(\S+)""".toRegex().find(line)?.let { + return@useLines it.groupValues[1] + } + } + throw GradleException("Failed to find NDK version in $androidEnvFile") + } + externalNativeBuild.cmake { + path("src/main/c/CMakeLists.txt") + } + + // Set this property to something nonexistent but non-empty. Otherwise it'll use the + // default list, which ignores asset directories beginning with an underscore, and + // maybe also other files required by tests. + aaptOptions.ignoreAssetsPattern = "android-testbed-dont-ignore-anything" + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + + testOptions { + managedDevices { + localDevices { + // systemImageSource should use what its documentation calls an + // "explicit source", i.e. the sdkmanager package name format, because + // that will be required in CreateEmulatorTask below. + create("minVersion") { + device = "Small Phone" + + // Managed devices have a minimum API level of 27. + apiLevel = max(27, defaultConfig.minSdk!!) + + // ATD devices are smaller and faster, but have a minimum + // API level of 30. + systemImageSource = if (apiLevel >= 30) "aosp_atd" else "default" + } + + create("maxVersion") { + device = "Small Phone" + apiLevel = defaultConfig.targetSdk!! + systemImageSource = "aosp_atd" + } + } + + // If the previous test run succeeded and nothing has changed, + // Gradle thinks there's no need to run it again. Override that. + afterEvaluate { + (localDevices.names + listOf("connected")).forEach { + tasks.named("${it}DebugAndroidTest") { + outputs.upToDateWhen { false } + } + } + } + } + } +} + +dependencies { + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.11.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test:rules:1.5.0") +} + + +afterEvaluate { + // Every new emulator has a maximum of 2 GB RAM, regardless of its hardware profile + // (https://cs.android.com/android-studio/platform/tools/base/+/refs/tags/studio-2025.3.2:sdklib/src/main/java/com/android/sdklib/internal/avd/EmulatedProperties.java;l=68). + // This is barely enough to test Python, and not enough to test Pandas + // (https://github.com/python/cpython/pull/137186#issuecomment-3136301023, + // https://github.com/pandas-dev/pandas/pull/63405#issuecomment-3667846159). + // So we'll increase it by editing the emulator configuration files. + // + // If the emulator doesn't exist yet, we want to edit it after it's created, but + // before it starts for the first time. Otherwise it'll need to be cold-booted + // again, which would slow down the first run, which is likely the only run in CI + // environments. But the Setup task both creates and starts the emulator if it + // doesn't already exist. So we create it ourselves before the Setup task runs. + for (device in android.testOptions.managedDevices.localDevices) { + val createTask = tasks.register<CreateEmulatorTask>("${device.name}Create") { + this.device = device.device + apiLevel = device.apiLevel + systemImageSource = device.systemImageSource + abi = NATIVE_ABI + } + tasks.named("${device.name}Setup") { + dependsOn(createTask) + } + } +} + +abstract class CreateEmulatorTask : DefaultTask() { + @get:Input abstract val device: Property<String> + @get:Input abstract val apiLevel: Property<Int> + @get:Input abstract val systemImageSource: Property<String> + @get:Input abstract val abi: Property<String> + @get:Inject abstract val execOps: ExecOperations + + private val avdName by lazy { + listOf( + "dev${apiLevel.get()}", + systemImageSource.get(), + abi.get(), + device.get().replace(' ', '_'), + ).joinToString("_") + } + + private val avdDir by lazy { + // XDG_CONFIG_HOME is respected by both avdmanager and Gradle. + val userHome = System.getenv("ANDROID_USER_HOME") ?: ( + (System.getenv("XDG_CONFIG_HOME") ?: System.getProperty("user.home")!!) + + "/.android" + ) + File("$userHome/avd/gradle-managed", "$avdName.avd") + } + + @TaskAction + fun run() { + if (!avdDir.exists()) { + createAvd() + } + updateAvd() + } + + fun createAvd() { + val systemImage = listOf( + "system-images", + "android-${apiLevel.get()}", + systemImageSource.get(), + abi.get(), + ).joinToString(";") + + runCmdlineTool("sdkmanager", systemImage) + runCmdlineTool( + "avdmanager", "create", "avd", + "--name", avdName, + "--path", avdDir, + "--device", device.get().lowercase().replace(" ", "_"), + "--package", systemImage, + ) + + val iniName = "$avdName.ini" + if (!File(avdDir.parentFile.parentFile, iniName).renameTo( + File(avdDir.parentFile, iniName) + )) { + throw GradleException("Failed to rename $iniName") + } + } + + fun updateAvd() { + for (filename in listOf( + "config.ini", // Created by avdmanager; always exists + "hardware-qemu.ini", // Created on first run; might not exist + )) { + val iniFile = File(avdDir, filename) + if (!iniFile.exists()) { + if (filename == "config.ini") { + throw GradleException("$iniFile does not exist") + } + continue + } + + val iniText = iniFile.readText() + val pattern = Regex( + """^\s*hw.ramSize\s*=\s*(.+?)\s*$""", RegexOption.MULTILINE + ) + val matches = pattern.findAll(iniText).toList() + if (matches.size != 1) { + throw GradleException( + "Found ${matches.size} instances of $pattern in $iniFile; expected 1" + ) + } + + val expectedRam = "4096" + if (matches[0].groupValues[1] != expectedRam) { + iniFile.writeText( + iniText.replace(pattern, "hw.ramSize = $expectedRam") + ) + } + } + } + + fun runCmdlineTool(tool: String, vararg args: Any) { + val androidHome = System.getenv("ANDROID_HOME")!! + val exeSuffix = + if (System.getProperty("os.name").lowercase().startsWith("win")) ".exe" + else "" + val command = + listOf("$androidHome/cmdline-tools/latest/bin/$tool$exeSuffix", *args) + println(command.joinToString(" ")) + execOps.exec { + commandLine(command) + } + } +} + + +// Create some custom tasks to copy Python and its standard library from +// elsewhere in the repository. +androidComponents.onVariants { variant -> + 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 (prefix in prefixes) { + from("$prefix/include/$pyPlusVer") + } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + + into("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") { + from("$projectDir/src/main/python") + + val sitePackages = findProperty("python.sitePackages") as String? + if (!sitePackages.isNullOrEmpty()) { + if (!file(sitePackages).exists()) { + throw GradleException("$sitePackages does not exist") + } + from(sitePackages) + } + } + + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + exclude("**/__pycache__") + } + + into("cwd") { + val cwd = findProperty("python.cwd") as String? + if (!cwd.isNullOrEmpty()) { + if (!file(cwd).exists()) { + throw GradleException("$cwd does not exist") + } + from(cwd) + } + } + + // A filename ending with .gz will be automatically decompressed + // while building the APK. Avoid this by adding a dash to the end, + // and add an extra dash to any filenames that already end with one. + // This will be undone in MainActivity.kt. + rename(""".*(\.gz|-)""", "$0-") + } + } + + generateTask(variant, variant.sources.jniLibs!!) { + for ((prefix, abi) in abis.entries) { + into(abi) { + from("$prefix/lib") + include("libpython*.*.so") + include("lib*_python.so") + } + } + } +} + + +fun generateTask( + variant: ApplicationVariant, directories: SourceDirectories, + configure: GenerateTask.() -> Unit +) { + val taskName = "generate" + + listOf(variant.name, "Python", directories.name) + .map { it.replaceFirstChar(Char::uppercase) } + .joinToString("") + + directories.addGeneratedSourceDirectory( + tasks.register<GenerateTask>(taskName) { + into(outputDir) + configure() + }, + GenerateTask::outputDir) +} + + +// addGeneratedSourceDirectory requires the task to have a DirectoryProperty. +abstract class GenerateTask: Sync() { + @get:OutputDirectory + abstract val outputDir: DirectoryProperty +} diff --git a/Android/testbed/app/src/androidTest/java/org/python/testbed/PythonSuite.kt b/Platforms/Android/testbed/app/src/androidTest/java/org/python/testbed/PythonSuite.kt similarity index 91% rename from Android/testbed/app/src/androidTest/java/org/python/testbed/PythonSuite.kt rename to Platforms/Android/testbed/app/src/androidTest/java/org/python/testbed/PythonSuite.kt index 94be52dd2dc870..e57243566f91dc 100644 --- a/Android/testbed/app/src/androidTest/java/org/python/testbed/PythonSuite.kt +++ b/Platforms/Android/testbed/app/src/androidTest/java/org/python/testbed/PythonSuite.kt @@ -20,7 +20,7 @@ class PythonSuite { val status = PythonTestRunner( InstrumentationRegistry.getInstrumentation().targetContext ).run( - InstrumentationRegistry.getArguments() + InstrumentationRegistry.getArguments().getString("pythonArgs")!!, ) assertEquals(0, status) } finally { diff --git a/Android/testbed/app/src/main/AndroidManifest.xml b/Platforms/Android/testbed/app/src/main/AndroidManifest.xml similarity index 100% rename from Android/testbed/app/src/main/AndroidManifest.xml rename to Platforms/Android/testbed/app/src/main/AndroidManifest.xml diff --git a/Android/testbed/app/src/main/c/CMakeLists.txt b/Platforms/Android/testbed/app/src/main/c/CMakeLists.txt similarity index 100% rename from Android/testbed/app/src/main/c/CMakeLists.txt rename to Platforms/Android/testbed/app/src/main/c/CMakeLists.txt diff --git a/Platforms/Android/testbed/app/src/main/c/main_activity.c b/Platforms/Android/testbed/app/src/main/c/main_activity.c new file mode 100644 index 00000000000000..7f024f0a348b61 --- /dev/null +++ b/Platforms/Android/testbed/app/src/main/c/main_activity.c @@ -0,0 +1,202 @@ +#include <android/log.h> +#include <errno.h> +#include <jni.h> +#include <pthread.h> +#include <Python.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + + +static void throw_runtime_exception(JNIEnv *env, const char *message) { + (*env)->ThrowNew( + env, + (*env)->FindClass(env, "java/lang/RuntimeException"), + message); +} + +static void throw_errno(JNIEnv *env, const char *error_prefix) { + char error_message[1024]; + snprintf(error_message, sizeof(error_message), + "%s: %s", error_prefix, strerror(errno)); + throw_runtime_exception(env, error_message); +} + + +// --- Stdio redirection ------------------------------------------------------ + +// Most apps won't need this, because the Python-level sys.stdout and sys.stderr +// are redirected to the Android logcat by Python itself. However, in the +// testbed it's useful to redirect the native streams as well, to debug problems +// in the Python startup or redirection process. +// +// Based on +// https://github.com/beeware/briefcase-android-gradle-template/blob/v0.3.11/%7B%7B%20cookiecutter.safe_formal_name%20%7D%7D/app/src/main/cpp/native-lib.cpp + +typedef struct { + FILE *file; + int fd; + android_LogPriority priority; + char *tag; + int pipe[2]; +} StreamInfo; + +// The FILE member can't be initialized here because stdout and stderr are not +// compile-time constants. Instead, it's initialized immediately before the +// redirection. +static StreamInfo STREAMS[] = { + {NULL, STDOUT_FILENO, ANDROID_LOG_INFO, "native.stdout", {-1, -1}}, + {NULL, STDERR_FILENO, ANDROID_LOG_WARN, "native.stderr", {-1, -1}}, + {NULL, -1, ANDROID_LOG_UNKNOWN, NULL, {-1, -1}}, +}; + +// The maximum length of a log message in bytes, including the level marker and +// tag, is defined as LOGGER_ENTRY_MAX_PAYLOAD in +// platform/system/logging/liblog/include/log/log.h. As of API level 30, messages +// longer than this will be be truncated by logcat. This limit has already been +// reduced at least once in the history of Android (from 4076 to 4068 between API +// level 23 and 26), so leave some headroom. +static const int MAX_BYTES_PER_WRITE = 4000; + +static void *redirection_thread(void *arg) { + StreamInfo *si = (StreamInfo*)arg; + ssize_t read_size; + char buf[MAX_BYTES_PER_WRITE]; + while ((read_size = read(si->pipe[0], buf, sizeof buf - 1)) > 0) { + buf[read_size] = '\0'; /* add null-terminator */ + __android_log_write(si->priority, si->tag, buf); + } + return 0; +} + +static char *redirect_stream(StreamInfo *si) { + /* make the FILE unbuffered, to ensure messages are never lost */ + if (setvbuf(si->file, 0, _IONBF, 0)) { + return "setvbuf"; + } + + /* create the pipe and redirect the file descriptor */ + if (pipe(si->pipe)) { + return "pipe"; + } + if (dup2(si->pipe[1], si->fd) == -1) { + return "dup2"; + } + + /* start the logging thread */ + pthread_t thr; + if ((errno = pthread_create(&thr, 0, redirection_thread, si))) { + return "pthread_create"; + } + if ((errno = pthread_detach(thr))) { + return "pthread_detach"; + } + return 0; +} + +JNIEXPORT void JNICALL Java_org_python_testbed_PythonTestRunner_redirectStdioToLogcat( + JNIEnv *env, jobject obj +) { + STREAMS[0].file = stdout; + STREAMS[1].file = stderr; + for (StreamInfo *si = STREAMS; si->file; si++) { + char *error_prefix; + if ((error_prefix = redirect_stream(si))) { + throw_errno(env, error_prefix); + return; + } + } +} + + +// --- Python initialization --------------------------------------------------- + +static char *init_signals() { + // Some tests use SIGUSR1, but that's blocked by default in an Android app in + // order to make it available to `sigwait` in the Signal Catcher thread. + // (https://cs.android.com/android/platform/superproject/+/android14-qpr3-release:art/runtime/signal_catcher.cc). + // That thread's functionality is only useful for debugging the JVM, so disabling + // it should not weaken the tests. + // + // There's no safe way of stopping the thread completely (#123982), but simply + // unblocking SIGUSR1 is enough to fix most tests. + // + // However, in tests that generate multiple different signals in quick + // succession, it's possible for SIGUSR1 to arrive while the main thread is busy + // running the C-level handler for a different signal. In that case, the SIGUSR1 + // may be sent to the Signal Catcher thread instead, which will generate a log + // message containing the text "reacting to signal". + // + // Such tests may need to be changed in one of the following ways: + // * Use a signal other than SIGUSR1 (e.g. test_stress_delivery_simultaneous in + // test_signal.py). + // * Send the signal to a specific thread rather than the whole process (e.g. + // test_signals in test_threadsignals.py. + sigset_t set; + if (sigemptyset(&set)) { + return "sigemptyset"; + } + if (sigaddset(&set, SIGUSR1)) { + return "sigaddset"; + } + if ((errno = pthread_sigmask(SIG_UNBLOCK, &set, NULL))) { + return "pthread_sigmask"; + } + return NULL; +} + +static void throw_status(JNIEnv *env, PyStatus status) { + throw_runtime_exception(env, status.err_msg ? status.err_msg : ""); +} + +JNIEXPORT int JNICALL Java_org_python_testbed_PythonTestRunner_runPython( + JNIEnv *env, jobject obj, jstring home, jarray args +) { + const char *home_utf8 = (*env)->GetStringUTFChars(env, home, NULL); + char cwd[PATH_MAX]; + snprintf(cwd, sizeof(cwd), "%s/%s", home_utf8, "cwd"); + if (chdir(cwd)) { + throw_errno(env, "chdir"); + return 1; + } + + char *error_prefix; + if ((error_prefix = init_signals())) { + throw_errno(env, error_prefix); + return 1; + } + + PyConfig config; + PyStatus status; + PyConfig_InitPythonConfig(&config); + + jsize argc = (*env)->GetArrayLength(env, args); + const char *argv[argc + 1]; + for (int i = 0; i < argc; i++) { + jobject arg = (*env)->GetObjectArrayElement(env, args, i); + argv[i] = (*env)->GetStringUTFChars(env, arg, NULL); + } + argv[argc] = NULL; + + // PyConfig_SetBytesArgv "must be called before other methods, since the + // preinitialization configuration depends on command line arguments" + if (PyStatus_Exception(status = PyConfig_SetBytesArgv(&config, argc, (char**)argv))) { + throw_status(env, status); + return 1; + } + + status = PyConfig_SetBytesString(&config, &config.home, home_utf8); + if (PyStatus_Exception(status)) { + throw_status(env, status); + return 1; + } + + status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + throw_status(env, status); + return 1; + } + + return Py_RunMain(); +} diff --git a/Platforms/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt b/Platforms/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt new file mode 100644 index 00000000000000..dc49cdb9a9f739 --- /dev/null +++ b/Platforms/Android/testbed/app/src/main/java/org/python/testbed/MainActivity.kt @@ -0,0 +1,94 @@ +package org.python.testbed + +import android.content.Context +import android.os.* +import android.system.Os +import android.widget.TextView +import androidx.appcompat.app.* +import org.json.JSONArray +import java.io.* + + +// Launching the tests from an activity is OK for a quick check, but for +// anything more complicated it'll be more convenient to use `android.py test` +// to launch the tests via PythonSuite. +class MainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + val status = PythonTestRunner(this).run("""["-m", "test", "-W", "-uall"]""") + findViewById<TextView>(R.id.tvHello).text = "Exit status $status" + } +} + + +class PythonTestRunner(val context: Context) { + /** Run Python. + * + * @param args Python command-line, encoded as JSON. + * @return The Python exit status: zero on success, nonzero on failure. */ + fun run(args: String) : Int { + // We leave argument 0 as an empty string, which is a placeholder for the + // executable name in embedded mode. + val argsJsonArray = JSONArray(args) + val argsStringArray = Array<String>(argsJsonArray.length() + 1) { it -> ""} + for (i in 0..<argsJsonArray.length()) { + argsStringArray[i + 1] = argsJsonArray.getString(i) + } + + // Python needs this variable to help it find the temporary directory, + // but Android only sets it on API level 33 and later. + Os.setenv("TMPDIR", context.cacheDir.toString(), false) + + val pythonHome = extractAssets() + System.loadLibrary("main_activity") + redirectStdioToLogcat() + return runPython(pythonHome.toString(), argsStringArray) + } + + private fun extractAssets() : File { + val pythonHome = File(context.filesDir, "python") + if (pythonHome.exists() && !pythonHome.deleteRecursively()) { + throw RuntimeException("Failed to delete $pythonHome") + } + extractAssetDir("python", context.filesDir) + + // Empty directories are lost in the asset packing/unpacking process. + val cwd = File(pythonHome, "cwd") + if (!cwd.exists()) { + cwd.mkdir() + } + + return pythonHome + } + + private fun extractAssetDir(path: String, targetDir: File) { + val names = context.assets.list(path) + ?: throw RuntimeException("Failed to list $path") + val targetSubdir = File(targetDir, path) + if (!targetSubdir.mkdirs()) { + throw RuntimeException("Failed to create $targetSubdir") + } + + for (name in names) { + val subPath = "$path/$name" + val input: InputStream + try { + input = context.assets.open(subPath) + } catch (e: FileNotFoundException) { + extractAssetDir(subPath, targetDir) + continue + } + input.use { + // Undo the .gz workaround from build.gradle.kts. + val outputName = name.replace(Regex("""(.*)-"""), "$1") + File(targetSubdir, outputName).outputStream().use { output -> + input.copyTo(output) + } + } + } + } + + private external fun redirectStdioToLogcat() + private external fun runPython(home: String, args: Array<String>) : Int +} diff --git a/Android/testbed/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/Platforms/Android/testbed/app/src/main/res/drawable-xxhdpi/ic_launcher.png similarity index 100% rename from Android/testbed/app/src/main/res/drawable-xxhdpi/ic_launcher.png rename to Platforms/Android/testbed/app/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/Android/testbed/app/src/main/res/layout/activity_main.xml b/Platforms/Android/testbed/app/src/main/res/layout/activity_main.xml similarity index 100% rename from Android/testbed/app/src/main/res/layout/activity_main.xml rename to Platforms/Android/testbed/app/src/main/res/layout/activity_main.xml diff --git a/Android/testbed/app/src/main/res/values/strings.xml b/Platforms/Android/testbed/app/src/main/res/values/strings.xml similarity index 100% rename from Android/testbed/app/src/main/res/values/strings.xml rename to Platforms/Android/testbed/app/src/main/res/values/strings.xml diff --git a/Android/testbed/build.gradle.kts b/Platforms/Android/testbed/build.gradle.kts similarity index 100% rename from Android/testbed/build.gradle.kts rename to Platforms/Android/testbed/build.gradle.kts diff --git a/Android/testbed/gradle.properties b/Platforms/Android/testbed/gradle.properties similarity index 100% rename from Android/testbed/gradle.properties rename to Platforms/Android/testbed/gradle.properties diff --git a/Android/testbed/gradle/wrapper/gradle-wrapper.properties b/Platforms/Android/testbed/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from Android/testbed/gradle/wrapper/gradle-wrapper.properties rename to Platforms/Android/testbed/gradle/wrapper/gradle-wrapper.properties diff --git a/Android/testbed/settings.gradle.kts b/Platforms/Android/testbed/settings.gradle.kts similarity index 100% rename from Android/testbed/settings.gradle.kts rename to Platforms/Android/testbed/settings.gradle.kts diff --git a/Platforms/Apple/.ruff.toml b/Platforms/Apple/.ruff.toml new file mode 100644 index 00000000000000..f5d74fdb6afe87 --- /dev/null +++ b/Platforms/Apple/.ruff.toml @@ -0,0 +1,22 @@ +extend = "../../.ruff.toml" # Inherit the project-wide settings + +[format] +preview = true +docstring-code-format = true + +[lint] +select = [ + "C4", # flake8-comprehensions + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "PGH", # pygrep-hooks + "PT", # flake8-pytest-style + "PYI", # flake8-pyi + "RUF100", # Ban unused `# noqa` comments + "UP", # pyupgrade + "W", # pycodestyle + "YTT", # flake8-2020 +] diff --git a/Platforms/Apple/__main__.py b/Platforms/Apple/__main__.py new file mode 100644 index 00000000000000..9f2d2afb0aa0f6 --- /dev/null +++ b/Platforms/Apple/__main__.py @@ -0,0 +1,1108 @@ +#!/usr/bin/env python3 +########################################################################## +# Apple XCframework build script +# +# This script simplifies the process of configuring, compiling and packaging an +# XCframework for an Apple platform. +# +# At present, it only supports iOS, but it has been constructed so that it +# could be used on any Apple platform. +# +# The simplest entry point is: +# +# $ python Platforms/Apple ci iOS +# +# which will: +# * Clean any pre-existing build artefacts +# * Configure and make a Python that can be used for the build +# * Configure and make a Python for each supported iOS architecture and ABI +# * Combine the outputs of the builds from the previous step into a single +# XCframework, merging binaries into a "fat" binary if necessary +# * Clone a copy of the testbed, configured to use the XCframework +# * Construct a tarball containing the release artefacts +# * Run the test suite using the generated XCframework. +# +# This is the complete sequence that would be needed in CI to build and test +# a candidate release artefact. +# +# Each individual step can be invoked individually - there are commands to +# clean, configure-build, make-build, configure-host, make-host, package, and +# test. +# +# There is also a build command that can be used to combine the configure and +# make steps for the build Python, an individual host, all hosts, or all +# builds. +########################################################################## +from __future__ import annotations + +import argparse +import os +import platform +import re +import shlex +import shutil +import signal +import subprocess +import sys +import sysconfig +import time +from collections.abc import Callable, Sequence +from contextlib import contextmanager +from datetime import datetime, timezone +from os.path import basename, relpath +from pathlib import Path +from subprocess import CalledProcessError + +EnvironmentT = dict[str, str] +ArgsT = Sequence[str | Path] + +SCRIPT_NAME = Path(__file__).name +PYTHON_DIR = Path(__file__).resolve().parent.parent.parent + +CROSS_BUILD_DIR = PYTHON_DIR / "cross-build" + +HOSTS: dict[str, dict[str, dict[str, str]]] = { + # Structure of this data: + # * Platform identifier + # * an XCframework slice that must exist for that platform + # * a host triple: the multiarch spec for that host + "iOS": { + "ios-arm64": { + "arm64-apple-ios": "arm64-iphoneos", + }, + "ios-arm64_x86_64-simulator": { + "arm64-apple-ios-simulator": "arm64-iphonesimulator", + "x86_64-apple-ios-simulator": "x86_64-iphonesimulator", + }, + }, +} + + +def subdir(name: str, create: bool = False) -> Path: + """Ensure that a cross-build directory for the given name exists.""" + path = CROSS_BUILD_DIR / name + if not path.exists(): + if not create: + sys.exit( + f"{path} does not exist. Create it by running the appropriate " + f"`configure` subcommand of {SCRIPT_NAME}." + ) + else: + path.mkdir(parents=True) + return path + + +def run( + command: ArgsT, + *, + host: str | None = None, + env: EnvironmentT | None = None, + log: bool | None = True, + **kwargs, +) -> subprocess.CompletedProcess: + """Run a command in an Apple development environment. + + Optionally logs the executed command to the console. + """ + kwargs.setdefault("check", True) + if env is None: + env = os.environ.copy() + + if host: + host_env = apple_env(host) + print_env(host_env) + env.update(host_env) + + if log: + print(">", join_command(command)) + return subprocess.run(command, env=env, **kwargs) + + +def join_command(args: str | Path | ArgsT) -> str: + """Format a command so it can be copied into a shell. + + Similar to `shlex.join`, but also accepts arguments which are Paths, or a + single string/Path outside of a list. + """ + if isinstance(args, (str, Path)): + return str(args) + else: + return shlex.join(map(str, args)) + + +def print_env(env: EnvironmentT) -> None: + """Format the environment so it can be pasted into a shell.""" + for key, value in sorted(env.items()): + print(f"export {key}={shlex.quote(value)}") + + +def apple_env(host: str) -> EnvironmentT: + """Construct an Apple development environment for the given host.""" + env = { + "PATH": ":".join([ + str(PYTHON_DIR / "Platforms/Apple/iOS/Resources/bin"), + str(subdir(host) / "prefix"), + "/usr/bin", + "/bin", + "/usr/sbin", + "/sbin", + "/Library/Apple/usr/bin", + ]), + } + + return env + + +def delete_path(name: str) -> None: + """Delete the named cross-build directory, if it exists.""" + path = CROSS_BUILD_DIR / name + if path.exists(): + print(f"Deleting {path} ...") + shutil.rmtree(path) + + +def all_host_triples(platform: str) -> list[str]: + """Return all host triples for the given platform. + + The host triples are the platform definitions used as input to configure + (e.g., "arm64-apple-ios-simulator"). + """ + triples = [] + for slice_name, slice_parts in HOSTS[platform].items(): + triples.extend(list(slice_parts)) + return triples + + +def clean(context: argparse.Namespace, target: str | None = None) -> None: + """The implementation of the "clean" command.""" + if target is None: + target = context.host + + # If we're explicitly targeting the build, there's no platform or + # distribution artefacts. If we're cleaning tests, we keep all built + # artefacts. Otherwise, the built artefacts must be dirty, so we remove + # them. + if target not in {"build", "test"}: + paths = ["dist", context.platform] + list(HOSTS[context.platform]) + else: + paths = [] + + if target in {"all", "build"}: + paths.append("build") + + if target in {"all", "hosts"}: + paths.extend(all_host_triples(context.platform)) + elif target not in {"build", "test", "package"}: + paths.append(target) + + if target in {"all", "hosts", "test"}: + paths.extend([ + path.name + for path in CROSS_BUILD_DIR.glob(f"{context.platform}-testbed.*") + ]) + + for path in paths: + delete_path(path) + + +def build_python_path() -> Path: + """The path to the build Python binary.""" + build_dir = subdir("build") + binary = build_dir / "python" + if not binary.is_file(): + binary = binary.with_suffix(".exe") + if not binary.is_file(): + raise FileNotFoundError( + f"Unable to find `python(.exe)` in {build_dir}" + ) + + return binary + + +@contextmanager +def group(text: str): + """A context manager that outputs a log marker around a section of a build. + + If running in a GitHub Actions environment, the GitHub syntax for + collapsible log sections is used. + """ + if "GITHUB_ACTIONS" in os.environ: + print(f"::group::{text}") + else: + print(f"===== {text} " + "=" * (70 - len(text))) + + yield + + if "GITHUB_ACTIONS" in os.environ: + print("::endgroup::") + else: + print() + + +@contextmanager +def cwd(subdir: Path): + """A context manager that sets the current working directory.""" + orig = os.getcwd() + os.chdir(subdir) + yield + os.chdir(orig) + + +def configure_build_python(context: argparse.Namespace) -> None: + """The implementation of the "configure-build" command.""" + if context.clean: + clean(context, "build") + + with ( + group("Configuring build Python"), + cwd(subdir("build", create=True)), + ): + command = [relpath(PYTHON_DIR / "configure")] + if context.args: + command.extend(context.args) + run(command) + + +def make_build_python(context: argparse.Namespace) -> None: + """The implementation of the "make-build" command.""" + with ( + group("Compiling build Python"), + cwd(subdir("build")), + ): + run(["make", "-j", str(os.cpu_count())]) + + +def apple_target(host: str) -> str: + """Return the Apple platform identifier for a given host triple.""" + for _, platform_slices in HOSTS.items(): + for slice_name, slice_parts in platform_slices.items(): + for host_triple, multiarch in slice_parts.items(): + if host == host_triple: + return ".".join(multiarch.split("-")[::-1]) + + raise KeyError(host) + + +def apple_multiarch(host: str) -> str: + """Return the multiarch descriptor for a given host triple.""" + for _, platform_slices in HOSTS.items(): + for slice_name, slice_parts in platform_slices.items(): + for host_triple, multiarch in slice_parts.items(): + if host == host_triple: + return multiarch + + raise KeyError(host) + + +def unpack_deps( + platform: str, + host: str, + prefix_dir: Path, + cache_dir: Path, +) -> None: + """Unpack binary dependencies into a provided directory. + + Downloads binaries if they aren't already present. Downloads will be stored + in provided cache directory. + + On iOS, as a safety mechanism, any dynamic libraries will be purged from + the unpacked dependencies. + """ + # To create new builds of these dependencies, usually all that's necessary + # is to push a tag to the cpython-apple-source-deps repository, and GitHub + # Actions will do the rest. + # + # If you're a member of the Python core team, and you'd like to be able to + # push these tags yourself, please contact Malcolm Smith or Russell + # Keith-Magee. + deps_url = "https://github.com/beeware/cpython-apple-source-deps/releases/download" + for name_ver in [ + "BZip2-1.0.8-2", + "libFFI-3.4.7-2", + "OpenSSL-3.5.7-1", + "XZ-5.6.4-2", + "mpdecimal-4.0.0-2", + "zstd-1.5.7-1", + ]: + filename = f"{name_ver.lower()}-{apple_target(host)}.tar.gz" + archive_path = download( + f"{deps_url}/{name_ver}/{filename}", + target_dir=cache_dir, + ) + shutil.unpack_archive(archive_path, prefix_dir) + + # Dynamic libraries will be preferentially linked over static; + # On iOS, ensure that no dylibs are available in the prefix folder. + if platform == "iOS": + for dylib in prefix_dir.glob("**/*.dylib"): + dylib.unlink() + + +def download(url: str, target_dir: Path) -> Path: + """Download the specified URL into the given directory. + + :return: The path to the downloaded archive. + """ + target_path = Path(target_dir).resolve() + target_path.mkdir(exist_ok=True, parents=True) + + out_path = target_path / basename(url) + if not Path(out_path).is_file(): + run([ + "curl", + "-Lf", + "--retry", + "5", + "--retry-all-errors", + "-o", + out_path, + url, + ]) + else: + print(f"Using cached version of {basename(url)}") + return out_path + + +def configure_host_python( + context: argparse.Namespace, + host: str | None = None, +) -> None: + """The implementation of the "configure-host" command.""" + if host is None: + host = context.host + + if context.clean: + clean(context, host) + + host_dir = subdir(host, create=True) + prefix_dir = host_dir / "prefix" + + with group(f"Downloading dependencies ({host})"): + if not prefix_dir.exists(): + prefix_dir.mkdir() + cache_dir = ( + Path(context.cache_dir).resolve() + if context.cache_dir + else CROSS_BUILD_DIR / "downloads" + ) + unpack_deps(context.platform, host, prefix_dir, cache_dir) + else: + print("Dependencies already installed") + + with ( + group(f"Configuring host Python ({host})"), + cwd(host_dir), + ): + command = [ + # Basic cross-compiling configuration + relpath(PYTHON_DIR / "configure"), + f"--host={host}", + f"--build={sysconfig.get_config_var('BUILD_GNU_TYPE')}", + f"--with-build-python={build_python_path()}", + "--with-system-libmpdec", + "--enable-framework", + # Dependent libraries. + f"--with-openssl={prefix_dir}", + f"LIBLZMA_CFLAGS=-I{prefix_dir}/include", + f"LIBLZMA_LIBS=-L{prefix_dir}/lib -llzma", + f"LIBFFI_CFLAGS=-I{prefix_dir}/include", + f"LIBFFI_LIBS=-L{prefix_dir}/lib -lffi", + f"LIBMPDEC_CFLAGS=-I{prefix_dir}/include", + f"LIBMPDEC_LIBS=-L{prefix_dir}/lib -lmpdec", + f"LIBZSTD_CFLAGS=-I{prefix_dir}/include", + f"LIBZSTD_LIBS=-L{prefix_dir}/lib -lzstd", + ] + + if context.args: + command.extend(context.args) + run(command, host=host) + + +def make_host_python( + context: argparse.Namespace, + host: str | None = None, +) -> None: + """The implementation of the "make-host" command.""" + if host is None: + host = context.host + + with ( + group(f"Compiling host Python ({host})"), + cwd(subdir(host)), + ): + run(["make", "-j", str(os.cpu_count())], host=host) + run(["make", "install"], host=host) + + +def framework_path(host_triple: str, multiarch: str) -> Path: + """The path to a built single-architecture framework product. + + :param host_triple: The host triple (e.g., arm64-apple-ios-simulator) + :param multiarch: The multiarch identifier (e.g., arm64-simulator) + """ + return ( + CROSS_BUILD_DIR + / f"{host_triple}/Platforms/Apple/iOS/Frameworks/{multiarch}" + ) + + +def package_version(prefix_path: Path) -> str: + """Extract the Python version being built from patchlevel.h.""" + for path in prefix_path.glob("**/patchlevel.h"): + text = path.read_text(encoding="utf-8") + if match := re.search( + r'\n\s*#define\s+PY_VERSION\s+"(.+)"\s*\n', text + ): + version = match[1] + # 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. The version will have a + # `+` suffix once any official release has been made; a freshly + # forked main branch will have a version of 3.X.0a0. + if version.endswith("a0"): + version += "+" + if version.endswith("+"): + version += datetime.now(timezone.utc).strftime("%Y%m%d.%H%M%S") + + return version + + sys.exit("Unable to determine Python version being packaged.") + + +def lib_platform_files(dirname, names): + """A file filter that ignores platform-specific files in lib.""" + path = Path(dirname) + if ( + path.parts[-3] == "lib" + and path.parts[-2].startswith("python") + and path.parts[-1] == "lib-dynload" + ): + return names + elif path.parts[-2] == "lib" and path.parts[-1].startswith("python"): + ignored_names = { + name + for name in names + if ( + name.startswith("_sysconfigdata_") + or name.startswith("_sysconfig_vars_") + or name == "build-details.json" + ) + } + elif path.parts[-1] == "lib": + ignored_names = { + name + for name in names + if name.startswith("libpython") and name.endswith(".dylib") + } + else: + ignored_names = set() + + return ignored_names + + +def lib_non_platform_files(dirname, names): + """A file filter that ignores anything *except* platform-specific files + in the lib directory. + """ + path = Path(dirname) + if path.parts[-2] == "lib" and path.parts[-1].startswith("python"): + return ( + set(names) - lib_platform_files(dirname, names) - {"lib-dynload"} + ) + else: + return set() + + +def create_xcframework(platform: str) -> str: + """Build an XCframework from the component parts for the platform. + + :return: The version number of the Python version that was packaged. + """ + package_path = CROSS_BUILD_DIR / platform + try: + package_path.mkdir() + except FileExistsError: + raise RuntimeError( + f"{platform} XCframework already exists; do you need to run " + "with --clean?" + ) from None + + frameworks = [] + # Merge Frameworks for each component SDK. If there's only one architecture + # for the SDK, we can use the compiled Python.framework as-is. However, if + # there's more than architecture, we need to merge the individual built + # frameworks into a merged "fat" framework. + for slice_name, slice_parts in HOSTS[platform].items(): + # Some parts are the same across all slices, so we use can any of the + # host frameworks as the source for the merged version. Use the first + # one on the list, as it's as representative as any other. + first_host_triple, first_multiarch = next(iter(slice_parts.items())) + first_framework = ( + framework_path(first_host_triple, first_multiarch) + / "Python.framework" + ) + + if len(slice_parts) == 1: + # The first framework is the only framework, so copy it. + print(f"Copying framework for {slice_name}...") + frameworks.append(first_framework) + else: + print(f"Merging framework for {slice_name}...") + slice_path = CROSS_BUILD_DIR / slice_name + slice_framework = slice_path / "Python.framework" + slice_framework.mkdir(exist_ok=True, parents=True) + + # Copy the Info.plist + shutil.copy( + first_framework / "Info.plist", + slice_framework / "Info.plist", + ) + + # Copy the headers + shutil.copytree( + first_framework / "Headers", + slice_framework / "Headers", + ) + + # Create the "fat" library binary for the slice + run( + ["lipo", "-create", "-output", slice_framework / "Python"] + + [ + ( + framework_path(host_triple, multiarch) + / "Python.framework/Python" + ) + for host_triple, multiarch in slice_parts.items() + ] + ) + + # Add this merged slice to the list to be added to the XCframework + frameworks.append(slice_framework) + + print() + print("Build XCframework...") + cmd = [ + "xcodebuild", + "-create-xcframework", + "-output", + package_path / "Python.xcframework", + ] + for framework in frameworks: + cmd.extend(["-framework", framework]) + + run(cmd) + + # Extract the package version from the merged framework + version = package_version(package_path / "Python.xcframework") + version_tag = ".".join(version.split(".")[:2]) + + # On non-macOS platforms, each framework in XCframework only contains the + # headers, libPython, plus an Info.plist. Other resources like the standard + # library and binary shims aren't allowed to live in framework; they need + # to be copied in separately. + print() + print("Copy additional resources...") + has_common_stdlib = False + for slice_name, slice_parts in HOSTS[platform].items(): + # Some parts are the same across all slices, so we can any of the + # host frameworks as the source for the merged version. + first_host_triple, first_multiarch = next(iter(slice_parts.items())) + first_path = framework_path(first_host_triple, first_multiarch) + first_framework = first_path / "Python.framework" + + slice_path = package_path / f"Python.xcframework/{slice_name}" + slice_framework = slice_path / "Python.framework" + + # Copy the binary helpers + print(f" - {slice_name} binaries") + shutil.copytree(first_path / "bin", slice_path / "bin") + + # Copy the include path (a symlink to the framework headers) + print(f" - {slice_name} include files") + shutil.copytree( + first_path / "include", + slice_path / "include", + symlinks=True, + ) + + # Copy in the cross-architecture pyconfig.h + shutil.copy( + PYTHON_DIR / f"Platforms/Apple/{platform}/Resources/pyconfig.h", + slice_framework / "Headers/pyconfig.h", + ) + + print(f" - {slice_name} shared library") + # Create a simlink for the fat library + shared_lib = slice_path / f"lib/libpython{version_tag}.dylib" + shared_lib.parent.mkdir() + shared_lib.symlink_to("../Python.framework/Python") + + print(f" - {slice_name} architecture-specific files") + for host_triple, multiarch in slice_parts.items(): + print(f" - {multiarch} standard library") + arch, _ = multiarch.split("-", 1) + + if not has_common_stdlib: + print(" - using this architecture as the common stdlib") + shutil.copytree( + framework_path(host_triple, multiarch) / "lib", + package_path / "Python.xcframework/lib", + ignore=lib_platform_files, + symlinks=True, + ) + has_common_stdlib = True + + shutil.copytree( + framework_path(host_triple, multiarch) / "lib", + slice_path / f"lib-{arch}", + ignore=lib_non_platform_files, + symlinks=True, + ) + + # Copy the host's pyconfig.h to an architecture-specific name. + arch = multiarch.split("-")[0] + host_path = ( + CROSS_BUILD_DIR + / host_triple + / "Platforms/Apple/iOS/Frameworks" + / multiarch + ) + host_framework = host_path / "Python.framework" + shutil.copy( + host_framework / "Headers/pyconfig.h", + slice_framework / f"Headers/pyconfig-{arch}.h", + ) + + # Apple identifies certain libraries as "security risks"; if you + # statically link those libraries into a Framework, you become + # responsible for providing a privacy manifest for that framework. + xcprivacy_file = { + "OpenSSL": subdir(host_triple) + / "prefix/share/OpenSSL.xcprivacy" + } + print(f" - {multiarch} xcprivacy files") + for module, lib in [ + ("_hashlib", "OpenSSL"), + ("_ssl", "OpenSSL"), + ]: + shutil.copy( + xcprivacy_file[lib], + slice_path + / f"lib-{arch}/python{version_tag}" + / f"lib-dynload/{module}.xcprivacy", + ) + + print(" - build tools") + shutil.copytree( + PYTHON_DIR / "Platforms/Apple/testbed/Python.xcframework/build", + package_path / "Python.xcframework/build", + ) + + return version + + +def package(context: argparse.Namespace) -> None: + """The implementation of the "package" command.""" + if context.clean: + clean(context, "package") + + with group("Building package"): + # Create an XCframework + version = create_xcframework(context.platform) + + # Clone testbed + print() + run([ + sys.executable, + "Platforms/Apple/testbed", + "clone", + "--platform", + context.platform, + "--framework", + CROSS_BUILD_DIR / context.platform / "Python.xcframework", + CROSS_BUILD_DIR / context.platform / "testbed", + ]) + + # Build the final archive + archive_name = ( + CROSS_BUILD_DIR + / "dist" + / f"python-{version}-{context.platform}-XCframework" + ) + + print() + print("Create package archive...") + shutil.make_archive( + str(CROSS_BUILD_DIR / archive_name), + format="gztar", + root_dir=CROSS_BUILD_DIR / context.platform, + base_dir=".", + ) + print() + print(f"{archive_name.relative_to(PYTHON_DIR)}.tar.gz created.") + + +def build(context: argparse.Namespace, host: str | None = None) -> None: + """The implementation of the "build" command.""" + if host is None: + host = context.host + + if context.clean: + clean(context, host) + + if host in {"all", "build"}: + for step in [ + configure_build_python, + make_build_python, + ]: + step(context) + + if host == "build": + hosts = [] + elif host in {"all", "hosts"}: + hosts = all_host_triples(context.platform) + else: + hosts = [host] + + for step_host in hosts: + for step in [ + configure_host_python, + make_host_python, + ]: + step(context, host=step_host) + + if host == "all": + package(context) + + +def test(context: argparse.Namespace, host: str | None = None) -> None: # noqa: PT028 + """The implementation of the "test" command.""" + if host is None: + host = context.host + + if context.clean: + clean(context, "test") + + with group(f"Test {'XCframework' if host in {'all', 'hosts'} else host}"): + timestamp = str(time.time_ns())[:-6] + testbed_dir = ( + CROSS_BUILD_DIR / f"{context.platform}-testbed.{timestamp}" + ) + if host in {"all", "hosts"}: + framework_path = ( + CROSS_BUILD_DIR / context.platform / "Python.xcframework" + ) + else: + build_arch = platform.machine() + host_arch = host.split("-")[0] + + if not host.endswith("-simulator"): + print("Skipping test suite non-simulator build.") + return + elif build_arch != host_arch: + print( + f"Skipping test suite for an {host_arch} build " + f"on an {build_arch} machine." + ) + return + else: + framework_path = ( + CROSS_BUILD_DIR + / host + / f"Platforms/Apple/{context.platform}" + / f"Frameworks/{apple_multiarch(host)}" + ) + + run([ + sys.executable, + "Platforms/Apple/testbed", + "clone", + "--platform", + context.platform, + "--framework", + framework_path, + testbed_dir, + ]) + + run( + [ + sys.executable, + testbed_dir, + "run", + "--verbose", + ] + + ( + ["--simulator", str(context.simulator)] + if context.simulator + else [] + ) + + [ + "--", + "test", + f"--{context.ci_mode or 'fast'}-ci", + "--single-process", + "--no-randomize", + "--pythoninfo", + # Timeout handling requires subprocesses; explicitly setting + # the timeout to -1 disables the faulthandler. + "--timeout=-1", + # Adding Python options requires the use of a subprocess to + # start a new Python interpreter. + "--dont-add-python-opts", + ] + ) + + +def apple_sim_host(platform_name: str) -> str: + """Determine the native simulator target for this platform.""" + for _, slice_parts in HOSTS[platform_name].items(): + for host_triple in slice_parts: + parts = host_triple.split("-") + if parts[0] == platform.machine() and parts[-1] == "simulator": + return host_triple + + raise KeyError(platform_name) + + +def ci(context: argparse.Namespace) -> None: + """The implementation of the "ci" command. + + In "Fast" mode, this compiles the build python, and the simulator for the + build machine's architecture; and runs the test suite with `--fast-ci` + configuration. + + In "Slow" mode, it compiles the build python, plus all candidate + architectures (both device and simulator); then runs the test suite with + `--slow-ci` configuration. + """ + clean(context, "all") + if context.ci_mode == "slow": + # In slow mode, build and test the full XCframework + build(context, host="all") + test(context, host="all") + else: + # In fast mode, just build the simulator platform. + sim_host = apple_sim_host(context.platform) + build(context, host="build") + build(context, host=sim_host) + test(context, host=sim_host) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description=( + "A tool for managing the build, package and test process of " + "CPython on Apple platforms." + ), + ) + parser.suggest_on_error = True + subcommands = parser.add_subparsers(dest="subcommand", required=True) + + clean = subcommands.add_parser( + "clean", + help="Delete all build directories", + ) + + configure_build = subcommands.add_parser( + "configure-build", help="Run `configure` for the build Python" + ) + make_build = subcommands.add_parser( + "make-build", help="Run `make` for the build Python" + ) + configure_host = subcommands.add_parser( + "configure-host", + help="Run `configure` for a specific platform and target", + ) + make_host = subcommands.add_parser( + "make-host", + help="Run `make` for a specific platform and target", + ) + package = subcommands.add_parser( + "package", + help="Create a release package for the platform", + ) + build = subcommands.add_parser( + "build", + help="Build all platform targets and create the XCframework", + ) + test = subcommands.add_parser( + "test", + help="Run the testbed for a specific platform", + ) + ci = subcommands.add_parser( + "ci", + help="Run build, package, and test", + ) + + # platform argument + for cmd in [clean, configure_host, make_host, package, build, test, ci]: + cmd.add_argument( + "platform", + choices=HOSTS.keys(), + help="The target platform to build", + ) + + # host triple argument + for cmd in [configure_host, make_host]: + cmd.add_argument( + "host", + help="The host triple to build (e.g., arm64-apple-ios-simulator)", + ) + # optional host triple argument + for cmd in [clean, build, test]: + cmd.add_argument( + "host", + nargs="?", + default="all", + help=( + "The host triple to build (e.g., arm64-apple-ios-simulator), " + "or 'build' for just the build platform, or 'hosts' for all " + "host platforms, or 'all' for the build platform and all " + "hosts. Defaults to 'all'" + ), + ) + + # --cross-build-dir argument + for cmd in [ + clean, + configure_build, + make_build, + configure_host, + make_host, + build, + package, + test, + ci, + ]: + cmd.add_argument( + "--cross-build-dir", + action="store", + default=os.environ.get("CROSS_BUILD_DIR"), + dest="cross_build_dir", + type=Path, + help=( + "Path to the cross-build directory " + f"(default: {CROSS_BUILD_DIR}). Can also be set " + "with the CROSS_BUILD_DIR environment variable." + ), + ) + + # --clean option + for cmd in [configure_build, configure_host, build, package, test, ci]: + cmd.add_argument( + "--clean", + action="store_true", + default=False, + dest="clean", + help="Delete the relevant build directories first", + ) + + # --cache-dir option + for cmd in [configure_host, build, ci]: + cmd.add_argument( + "--cache-dir", + default=os.environ.get("CACHE_DIR"), + help="The directory to store cached downloads.", + ) + + # --simulator option + for cmd in [test, ci]: + cmd.add_argument( + "--simulator", + help=( + "The name of the simulator to use (eg: 'iPhone 16e'). " + "Defaults to the most recently released 'entry level' " + "iPhone device. Device architecture and OS version can also " + "be specified; e.g., " + "`--simulator 'iPhone 16 Pro,arch=arm64,OS=26.0'` would " + "run on an ARM64 iPhone 16 Pro simulator running iOS 26.0." + ), + ) + group = cmd.add_mutually_exclusive_group() + group.add_argument( + "--fast-ci", + action="store_const", + dest="ci_mode", + const="fast", + help="Add test arguments for GitHub Actions", + ) + group.add_argument( + "--slow-ci", + action="store_const", + dest="ci_mode", + const="slow", + help="Add test arguments for buildbots", + ) + + for subcommand in [configure_build, configure_host, build, ci]: + subcommand.add_argument( + "args", nargs="*", help="Extra arguments to pass to `configure`" + ) + + return parser.parse_args() + + +def print_called_process_error(e: subprocess.CalledProcessError) -> None: + for stream_name in ["stdout", "stderr"]: + content = getattr(e, stream_name) + stream = getattr(sys, stream_name) + if content: + stream.write(content) + if not content.endswith("\n"): + stream.write("\n") + + # shlex uses single quotes, so we surround the command with double quotes. + print( + f'Command "{join_command(e.cmd)}" returned exit status {e.returncode}' + ) + + +def main() -> None: + # 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 signal_handler(*args): + os.kill(os.getpid(), signal.SIGINT) + + signal.signal(signal.SIGTERM, signal_handler) + + # Process command line arguments + context = parse_args() + + # Set the CROSS_BUILD_DIR if an argument was provided + if context.cross_build_dir: + global CROSS_BUILD_DIR + CROSS_BUILD_DIR = context.cross_build_dir.resolve() + + dispatch: dict[str, Callable] = { + "clean": clean, + "configure-build": configure_build_python, + "make-build": make_build_python, + "configure-host": configure_host_python, + "make-host": make_host_python, + "package": package, + "build": build, + "test": test, + "ci": ci, + } + + try: + dispatch[context.subcommand](context) + except CalledProcessError as e: + print() + print_called_process_error(e) + sys.exit(1) + except RuntimeError as e: + print() + print(e) + sys.exit(2) + + +if __name__ == "__main__": + # Under the buildbot, stdout is not a TTY, but we must still flush after + # every line to make sure our output appears in the correct order relative + # to the output of our subprocesses. + for stream in [sys.stdout, sys.stderr]: + stream.reconfigure(line_buffering=True) + + main() diff --git a/Platforms/Apple/iOS/README.md b/Platforms/Apple/iOS/README.md new file mode 100644 index 00000000000000..faeeead1df03a2 --- /dev/null +++ b/Platforms/Apple/iOS/README.md @@ -0,0 +1,339 @@ +# Python on iOS README + +**iOS support is [tier 3](https://peps.python.org/pep-0011/#tier-3).** + +This document provides a quick overview of some iOS specific features in the +Python distribution. + +These instructions are only needed if you're planning to compile Python for iOS +yourself. Most users should *not* need to do this. If you're looking to +experiment with writing an iOS app in Python, tools such as [BeeWare's +Briefcase](https://briefcase.readthedocs.io) and [Kivy's +Buildozer](https://buildozer.readthedocs.io) will provide a much more +approachable user experience. + +## Compilers for building on iOS + +Building for iOS requires the use of Apple's Xcode tooling. It is strongly +recommended that you use the most recent stable release of Xcode. This will +require the use of the most (or second-most) recently released macOS version, +as Apple does not maintain Xcode for older macOS versions. The Xcode Command +Line Tools are not sufficient for iOS development; you need a *full* Xcode +install. + +If you want to run your code on the iOS simulator, you'll also need to install +an iOS Simulator Platform. You should be prompted to select an iOS Simulator +Platform when you first run Xcode. Alternatively, you can add an iOS Simulator +Platform by selecting an open the Platforms tab of the Xcode Settings panel. + +## Building Python on iOS + +### ABIs and Architectures + +iOS apps can be deployed on physical devices, and on the iOS simulator. Although +the API used on these devices is identical, the ABI is different - you need to +link against different libraries for an iOS device build (`iphoneos`) or an +iOS simulator build (`iphonesimulator`). + +Apple uses the `XCframework` format to allow specifying a single dependency +that supports multiple ABIs. An `XCframework` is a wrapper around multiple +ABI-specific frameworks that share a common API. + +iOS can also support different CPU architectures within each ABI. At present, +there is only a single supported architecture on physical devices - ARM64. +However, the *simulator* supports 2 architectures - ARM64 (for running on Apple +Silicon machines), and x86_64 (for running on older Intel-based machines). + +To support multiple CPU architectures on a single platform, Apple uses a "fat +binary" format - a single physical file that contains support for multiple +architectures. It is possible to compile and use a "thin" single architecture +version of a binary for testing purposes; however, the "thin" binary will not be +portable to machines using other architectures. + +### Building a multi-architecture iOS XCframework + +The `Platforms/Apple` subfolder of the Python repository acts as a build script that +can be used to coordinate the compilation of a complete iOS XCframework. To use +it, run:: + + python Platforms/Apple build iOS + +This will: + +* Configure and compile a version of Python to run on the build machine +* Download pre-compiled binary dependencies for each platform +* Configure and build a `Python.framework` for each required architecture and + iOS SDK +* Merge the multiple `Python.framework` folders into a single `Python.xcframework` +* Produce a `.tar.gz` archive in the `cross-build/dist` folder containing + the `Python.xcframework`, plus a copy of the Testbed app pre-configured to + use the XCframework. + +The `Platforms/Apple` build script has other entry points that will perform the +individual parts of the overall `build` target, plus targets to test the +build, clean the `cross-build` folder of iOS build products, and perform a +complete "build and test" CI run. The `--clean` flag can also be used on +individual commands to ensure that a stale build product are removed before +building. + +### Building a single-architecture framework + +If you're using the `Platforms/Apple` build script, you won't need to build +individual frameworks. However, if you do need to manually configure an iOS +Python build for a single framework, the following options are available. + +#### iOS specific arguments to configure + +* `--enable-framework[=DIR]` + + This argument specifies the location where the Python.framework will be + installed. If `DIR` is not specified, the framework will be installed into + a subdirectory of the `iOS/Frameworks` folder. + + This argument *must* be provided when configuring iOS builds. iOS does not + support non-framework builds. + +* `--with-framework-name=NAME` + + Specify the name for the Python framework; defaults to `Python`. + + > [!NOTE] + > Unless you know what you're doing, changing the name of the Python + > framework on iOS is not advised. If you use this option, you won't be able + > to run the `Platforms/Apple` build script without making significant manual + > alterations, and you won't be able to use any binary packages unless you + > compile them yourself using your own framework name. + +#### Building Python for iOS + +The Python build system will create a `Python.framework` that supports a +*single* ABI with a *single* architecture. Unlike macOS, iOS does not allow a +framework to contain non-library content, so the iOS build will produce a +`bin` and `lib` folder in the same output folder as `Python.framework`. +The `lib` folder will be needed at runtime to support the Python library. + +If you want to use Python in a real iOS project, you need to produce multiple +`Python.framework` builds, one for each ABI and architecture. iOS builds of +Python *must* be constructed as framework builds. To support this, you must +provide the `--enable-framework` flag when configuring the build. The build +also requires the use of cross-compilation. The minimal commands for building +Python for the ARM64 iOS simulator will look something like: +``` +export PATH="$(pwd)/Platforms/Apple/iOS/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin" +./configure \ + --enable-framework \ + --host=arm64-apple-ios-simulator \ + --build=arm64-apple-darwin \ + --with-build-python=/path/to/python.exe +make +make install +``` + +In this invocation: + +* `Platforms/Apple/iOS/Resources/bin` has been added to the path, providing some shims for the + compilers and linkers needed by the build. Xcode requires the use of `xcrun` + to invoke compiler tooling. However, if `xcrun` is pre-evaluated and the + result passed to `configure`, these results can embed user- and + version-specific paths into the sysconfig data, which limits the portability + of the compiled Python. Alternatively, if `xcrun` is used *as* the compiler, + it requires that compiler variables like `CC` include spaces, which can + cause significant problems with many C configuration systems which assume that + `CC` will be a single executable. + + To work around this problem, the `Platforms/Apple/iOS/Resources/bin` folder contains some + wrapper scripts that present as simple compilers and linkers, but wrap + underlying calls to `xcrun`. This allows configure to use a `CC` + definition without spaces, and without user- or version-specific paths, while + retaining the ability to adapt to the local Xcode install. These scripts are + included in the `bin` directory of an iOS install. + + These scripts will, by default, use the currently active Xcode installation. + If you want to use a different Xcode installation, you can use + `xcode-select` to set a new default Xcode globally, or you can use the + `DEVELOPER_DIR` environment variable to specify an Xcode install. The + scripts will use the default `iphoneos`/`iphonesimulator` SDK version for + the select Xcode install; if you want to use a different SDK, you can set the + `IOS_SDK_VERSION` environment variable. (e.g, setting + `IOS_SDK_VERSION=17.1` would cause the scripts to use the `iphoneos17.1` + and `iphonesimulator17.1` SDKs, regardless of the Xcode default.) + + The path has also been cleared of any user customizations. A common source of + bugs is for tools like Homebrew to accidentally leak macOS binaries into an iOS + build. Resetting the path to a known "bare bones" value is the easiest way to + avoid these problems. + +* `--host` is the architecture and ABI that you want to build, in GNU compiler + triple format. This will be one of: + + - `arm64-apple-ios` for ARM64 iOS devices. + - `arm64-apple-ios-simulator` for the iOS simulator running on Apple + Silicon devices. + - `x86_64-apple-ios-simulator` for the iOS simulator running on Intel + devices. + +* `--build` is the GNU compiler triple for the machine that will be running + the compiler. This is one of: + + - `arm64-apple-darwin` for Apple Silicon devices. + - `x86_64-apple-darwin` for Intel devices. + +* `/path/to/python.exe` is the path to a Python binary on the machine that + will be running the compiler. This is needed because the Python compilation + process involves running some Python code. On a normal desktop build of + Python, you can compile a python interpreter and then use that interpreter to + run Python code. However, the binaries produced for iOS won't run on macOS, so + you need to provide an external Python interpreter. This interpreter must be + the same version as the Python that is being compiled. To be completely safe, + this should be the *exact* same commit hash. However, the longer a Python + release has been stable, the more likely it is that this constraint can be + relaxed - the same micro version will often be sufficient. + +* The `install` target for iOS builds is slightly different to other + platforms. On most platforms, `make install` will install the build into + the final runtime location. This won't be the case for iOS, as the final + runtime location will be on a physical device. + + However, you still need to run the `install` target for iOS builds, as it + performs some final framework assembly steps. The location specified with + `--enable-framework` will be the location where `make install` will + assemble the complete iOS framework. This completed framework can then + be copied and relocated as required. + +For a full CPython build, you also need to specify the paths to iOS builds of +the binary libraries that CPython depends on (such as XZ, LibFFI and OpenSSL). +This can be done by defining library specific environment variables (such as +`LIBLZMA_CFLAGS`, `LIBLZMA_LIBS`), and the `--with-openssl` configure +option. Versions of these libraries pre-compiled for iOS can be found in [this +repository](https://github.com/beeware/cpython-apple-source-deps/releases). +LibFFI is especially important, as many parts of the standard library +(including the `platform`, `sysconfig` and `webbrowser` modules) require +the use of the `ctypes` module at runtime. + +By default, Python will be compiled with an iOS deployment target (i.e., the +minimum supported iOS version) of 13.0. To specify a different deployment +target, provide the version number as part of the `--host` argument - for +example, `--host=arm64-apple-ios15.4-simulator` would compile an ARM64 +simulator build with a deployment target of 15.4. + +## Testing Python on iOS + +### Testing a multi-architecture framework + +Once you have a built an XCframework, you can test that framework by running: + + $ python Platforms/Apple test iOS + +This test will attempt to find an "SE-class" simulator (i.e., an iPhone SE, or +iPhone 16e, or similar), and run the test suite on the most recent version of +iOS that is available. You can specify a simulator using the `--simulator` +command line argument, providing the name of the simulator (e.g., `--simulator +'iPhone 16 Pro'`). You can also use this argument to control the OS version used +for testing; `--simulator 'iPhone 16 Pro,OS=18.2'` would attempt to run the +tests on an iPhone 16 Pro running iOS 18.2. + +If the test runner is executed on GitHub Actions, the `GITHUB_ACTIONS` +environment variable will be exposed to the iOS process at runtime. + +### Testing a single-architecture framework + +The `Platforms/Apple/testbed` folder that contains an Xcode project that is able to run +the Python test suite on Apple platforms. This project converts the Python test +suite into a single test case in Xcode's XCTest framework. The single XCTest +passes if the test suite passes. + +To run the test suite, configure a Python build for an iOS simulator (i.e., +`--host=arm64-apple-ios-simulator` or `--host=x86_64-apple-ios-simulator` +), specifying a framework build (i.e. `--enable-framework`). Ensure that your +`PATH` has been configured to include the `Platforms/Apple/iOS/Resources/bin` folder and +exclude any non-iOS tools, then run: +``` +make all +make install +make testios +``` + +This will: + +* Build an iOS framework for your chosen architecture; +* Finalize the single-platform framework; +* Make a clean copy of the testbed project; +* Install the Python iOS framework into the copy of the testbed project; and +* Run the test suite on an "entry-level device" simulator (i.e., an iPhone SE, + iPhone 16e, or a similar). + +On success, the test suite will exit and report successful completion of the +test suite. On a 2022 M1 MacBook Pro, the test suite takes approximately 15 +minutes to run; a couple of extra minutes is required to compile the testbed +project, and then boot and prepare the iOS simulator. + +### Debugging test failures + +Running `python Platforms/Apple test iOS` generates a standalone version of the +`Platforms/Apple/testbed` project, and runs the full test suite. It does this using +`Platforms/Apple/testbed` itself - the folder is an executable module that can be used +to create and run a clone of the testbed project. The standalone version of the +testbed will be created in a directory named +`cross-build/iOS-testbed.<timestamp>`. + +You can generate your own standalone testbed instance by running: +``` +python cross-build/iOS/testbed clone my-testbed +``` + +In this invocation, `my-testbed` is the name of the folder for the new +testbed clone. + +If you've built your own XCframework, or you only want to test a single architecture, +you can construct a standalone testbed instance by running: +``` +python Platforms/Apple/testbed clone --platform iOS --framework <path/to/framework> my-testbed +``` + +The framework path can be the path path to a `Python.xcframework`, or the +path to a folder that contains a single-platform `Python.framework`. + +You can then use the `my-testbed` folder to run the Python test suite, +passing in any command line arguments you may require. For example, if you're +trying to diagnose a failure in the `os` module, you might run: +``` +python my-testbed run -- test -W test_os +``` + +This is the equivalent of running `python -m test -W test_os` on a desktop +Python build. Any arguments after the `--` will be passed to testbed as if +they were arguments to `python -m` on a desktop machine. + +### Testing in Xcode + +You can also open the testbed project in Xcode by running: +``` +open my-testbed/iOSTestbed.xcodeproj +``` + +This will allow you to use the full Xcode suite of tools for debugging. + +The arguments used to run the test suite are defined as part of the test plan. +To modify the test plan, select the test plan node of the project tree (it +should be the first child of the root node), and select the "Configurations" +tab. Modify the "Arguments Passed On Launch" value to change the testing +arguments. + +The test plan also disables parallel testing, and specifies the use of the +`Testbed.lldbinit` file for providing configuration of the debugger. The +default debugger configuration disables automatic breakpoints on the +`SIGINT`, `SIGUSR1`, `SIGUSR2`, and `SIGXFSZ` signals. + +### Testing on an iOS device + +To test on an iOS device, the app needs to be signed with known developer +credentials. To obtain these credentials, you must have an iOS Developer +account, and your Xcode install will need to be logged into your account (see +the Accounts tab of the Preferences dialog). + +Once the project is open, and you're signed into your Apple Developer account, +select the root node of the project tree (labeled "iOSTestbed"), then the +"Signing & Capabilities" tab in the details page. Select a development team +(this will likely be your own name), and plug in a physical device to your +macOS machine with a USB cable. You should then be able to select your physical +device from the list of targets in the pulldown in the Xcode titlebar. diff --git a/iOS/Resources/Info.plist.in b/Platforms/Apple/iOS/Resources/Info.plist.in similarity index 100% rename from iOS/Resources/Info.plist.in rename to Platforms/Apple/iOS/Resources/Info.plist.in diff --git a/iOS/Resources/bin/arm64-apple-ios-ar b/Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-ar similarity index 100% rename from iOS/Resources/bin/arm64-apple-ios-ar rename to Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-ar diff --git a/iOS/Resources/bin/arm64-apple-ios-clang b/Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-clang similarity index 100% rename from iOS/Resources/bin/arm64-apple-ios-clang rename to Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-clang diff --git a/iOS/Resources/bin/arm64-apple-ios-clang++ b/Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-clang++ similarity index 100% rename from iOS/Resources/bin/arm64-apple-ios-clang++ rename to Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-clang++ diff --git a/iOS/Resources/bin/arm64-apple-ios-cpp b/Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-cpp similarity index 100% rename from iOS/Resources/bin/arm64-apple-ios-cpp rename to Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-cpp diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-ar b/Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-ar similarity index 100% rename from iOS/Resources/bin/arm64-apple-ios-simulator-ar rename to Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-ar diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-clang b/Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-clang similarity index 100% rename from iOS/Resources/bin/arm64-apple-ios-simulator-clang rename to Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-clang diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ b/Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ similarity index 100% rename from iOS/Resources/bin/arm64-apple-ios-simulator-clang++ rename to Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-cpp b/Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-cpp similarity index 100% rename from iOS/Resources/bin/arm64-apple-ios-simulator-cpp rename to Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-cpp diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-strip b/Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-strip similarity index 100% rename from iOS/Resources/bin/arm64-apple-ios-simulator-strip rename to Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-strip diff --git a/iOS/Resources/bin/arm64-apple-ios-strip b/Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-strip similarity index 100% rename from iOS/Resources/bin/arm64-apple-ios-strip rename to Platforms/Apple/iOS/Resources/bin/arm64-apple-ios-strip diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-ar b/Platforms/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-ar similarity index 100% rename from iOS/Resources/bin/x86_64-apple-ios-simulator-ar rename to Platforms/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-ar diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang b/Platforms/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-clang similarity index 100% rename from iOS/Resources/bin/x86_64-apple-ios-simulator-clang rename to Platforms/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-clang diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ b/Platforms/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ similarity index 100% rename from iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ rename to Platforms/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp b/Platforms/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp similarity index 100% rename from iOS/Resources/bin/x86_64-apple-ios-simulator-cpp rename to Platforms/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-strip b/Platforms/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-strip similarity index 100% rename from iOS/Resources/bin/x86_64-apple-ios-simulator-strip rename to Platforms/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-strip diff --git a/iOS/Resources/pyconfig.h b/Platforms/Apple/iOS/Resources/pyconfig.h similarity index 100% rename from iOS/Resources/pyconfig.h rename to Platforms/Apple/iOS/Resources/pyconfig.h diff --git a/iOS/testbed/Python.xcframework/Info.plist b/Platforms/Apple/testbed/Python.xcframework/Info.plist similarity index 100% rename from iOS/testbed/Python.xcframework/Info.plist rename to Platforms/Apple/testbed/Python.xcframework/Info.plist diff --git a/Platforms/Apple/testbed/Python.xcframework/build/iOS-dylib-Info-template.plist b/Platforms/Apple/testbed/Python.xcframework/build/iOS-dylib-Info-template.plist new file mode 100644 index 00000000000000..d6caa01c1e44b9 --- /dev/null +++ b/Platforms/Apple/testbed/Python.xcframework/build/iOS-dylib-Info-template.plist @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string></string> + <key>CFBundleIdentifier</key> + <string></string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>iPhoneOS</string> + </array> + <key>MinimumOSVersion</key> + <string>13.0</string> + <key>CFBundleVersion</key> + <string>1</string> +</dict> +</plist> diff --git a/Platforms/Apple/testbed/Python.xcframework/build/utils.sh b/Platforms/Apple/testbed/Python.xcframework/build/utils.sh new file mode 100755 index 00000000000000..e54471f68b7cb2 --- /dev/null +++ b/Platforms/Apple/testbed/Python.xcframework/build/utils.sh @@ -0,0 +1,151 @@ +# Utility methods for use in an Xcode project. +# +# An iOS XCframework cannot include any content other than the library binary +# and relevant metadata. However, Python requires a standard library at runtime. +# Therefore, it is necessary to add a build step to an Xcode app target that +# processes the standard library and puts the content into the final app. +# +# In general, these tools will be invoked after bundle resources have been +# copied into the app, but before framework embedding (and signing). +# +# The following is an example script, assuming that: +# * Python.xcframework is in the root of the project +# * There is an `app` folder that contains the app code +# * There is an `app_packages` folder that contains installed Python packages. +# ----- +# set -e +# source $PROJECT_DIR/Python.xcframework/build/build_utils.sh +# install_python Python.xcframework app app_packages +# ----- + +# Copy the standard library from the XCframework into the app bundle. +# +# Accepts one argument: +# 1. The path, relative to the root of the Xcode project, where the Python +# XCframework can be found. +install_stdlib() { + PYTHON_XCFRAMEWORK_PATH=$1 + + mkdir -p "$CODESIGNING_FOLDER_PATH/python/lib" + if [ "$EFFECTIVE_PLATFORM_NAME" = "-iphonesimulator" ]; then + echo "Installing Python modules for iOS Simulator" + if [ -d "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/ios-arm64-simulator" ]; then + SLICE_FOLDER="ios-arm64-simulator" + else + SLICE_FOLDER="ios-arm64_x86_64-simulator" + fi + else + echo "Installing Python modules for iOS Device" + SLICE_FOLDER="ios-arm64" + fi + + # If the XCframework has a shared lib folder, then it's a full framework. + # Copy both the common and slice-specific part of the lib directory. + # Otherwise, it's a single-arch framework; use the "full" lib folder. + # Don't include any libpython symlink; that can't be included at runtime + if [ -d "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/lib" ]; then + rsync -au --delete "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" --exclude 'libpython*.dylib' + rsync -au "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/$SLICE_FOLDER/lib-$ARCHS/" "$CODESIGNING_FOLDER_PATH/python/lib/" --exclude 'libpython*.dylib' + else + rsync -au --delete "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/$SLICE_FOLDER/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" --exclude 'libpython*.dylib' + fi +} + +# Convert a single .so library into a framework that iOS can load. +# +# Accepts three arguments: +# 1. The path, relative to the root of the Xcode project, where the Python +# XCframework can be found. +# 2. The base path, relative to the installed location in the app bundle, that +# needs to be processed. Any .so file found in this path (or a subdirectory +# of it) will be processed. +# 2. The full path to a single .so file to process. This path should include +# the base path. +install_dylib () { + PYTHON_XCFRAMEWORK_PATH=$1 + INSTALL_BASE=$2 + FULL_EXT=$3 + + # The name of the extension file + EXT=$(basename "$FULL_EXT") + # The name and location of the module + MODULE_PATH=$(dirname "$FULL_EXT") + MODULE_NAME=$(echo $EXT | cut -d "." -f 1) + # The location of the extension file, relative to the bundle + RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} + # The path to the extension file, relative to the install base + PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/} + # The full dotted name of the extension module, constructed from the file path. + FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d "." -f 1 | tr "/" "."); + # A bundle identifier; not actually used, but required by Xcode framework packaging + FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr "_" "-") + # The name of the framework folder. + FRAMEWORK_FOLDER="Frameworks/$FULL_MODULE_NAME.framework" + + # If the framework folder doesn't exist, create it. + if [ ! -d "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" ]; then + echo "Creating framework for $RELATIVE_EXT" + mkdir -p "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" + cp "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/build/$PLATFORM_FAMILY_NAME-dylib-Info-template.plist" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" + plutil -replace CFBundleExecutable -string "$FULL_MODULE_NAME" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" + plutil -replace CFBundleIdentifier -string "$FRAMEWORK_BUNDLE_ID" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" + fi + + echo "Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME" + mv "$FULL_EXT" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME" + # Create a placeholder .fwork file where the .so was + echo "$FRAMEWORK_FOLDER/$FULL_MODULE_NAME" > ${FULL_EXT%.so}.fwork + # Create a back reference to the .so file location in the framework + echo "${RELATIVE_EXT%.so}.fwork" > "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin" + + # If the framework provides an xcprivacy file, install it. + if [ -e "$MODULE_PATH/$MODULE_NAME.xcprivacy" ]; then + echo "Installing XCPrivacy file for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME" + XCPRIVACY_FILE="$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/PrivacyInfo.xcprivacy" + if [ -e "$XCPRIVACY_FILE" ]; then + rm -rf "$XCPRIVACY_FILE" + fi + mv "$MODULE_PATH/$MODULE_NAME.xcprivacy" "$XCPRIVACY_FILE" + fi + + echo "Signing framework as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)..." + /usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" +} + +# Process all the dynamic libraries in a path into Framework format. +# +# Accepts two arguments: +# 1. The path, relative to the root of the Xcode project, where the Python +# XCframework can be found. +# 2. The base path, relative to the installed location in the app bundle, that +# needs to be processed. Any .so file found in this path (or a subdirectory +# of it) will be processed. +process_dylibs () { + PYTHON_XCFRAMEWORK_PATH=$1 + LIB_PATH=$2 + find "$CODESIGNING_FOLDER_PATH/$LIB_PATH" -name "*.so" | while read FULL_EXT; do + install_dylib $PYTHON_XCFRAMEWORK_PATH "$LIB_PATH/" "$FULL_EXT" + done +} + +# The entry point for post-processing a Python XCframework. +# +# Accepts 1 or more arguments: +# 1. The path, relative to the root of the Xcode project, where the Python +# XCframework can be found. If the XCframework is in the root of the project, +# 2+. The path of a package, relative to the root of the packaged app, that contains +# library content that should be processed for binary libraries. +install_python() { + PYTHON_XCFRAMEWORK_PATH=$1 + shift + + install_stdlib $PYTHON_XCFRAMEWORK_PATH + PYTHON_VER=$(ls -1 "$CODESIGNING_FOLDER_PATH/python/lib" | grep -E "^python3\.\d+$") + echo "Install Python $PYTHON_VER standard library extension modules..." + process_dylibs $PYTHON_XCFRAMEWORK_PATH python/lib/$PYTHON_VER/lib-dynload + + for package_path in $@; do + echo "Installing $package_path extension modules ..." + process_dylibs $PYTHON_XCFRAMEWORK_PATH $package_path + done +} diff --git a/iOS/testbed/Python.xcframework/ios-arm64/README b/Platforms/Apple/testbed/Python.xcframework/ios-arm64/README similarity index 100% rename from iOS/testbed/Python.xcframework/ios-arm64/README rename to Platforms/Apple/testbed/Python.xcframework/ios-arm64/README diff --git a/iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/README b/Platforms/Apple/testbed/Python.xcframework/ios-arm64_x86_64-simulator/README similarity index 100% rename from iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/README rename to Platforms/Apple/testbed/Python.xcframework/ios-arm64_x86_64-simulator/README diff --git a/Platforms/Apple/testbed/Testbed.lldbinit b/Platforms/Apple/testbed/Testbed.lldbinit new file mode 100644 index 00000000000000..4cf00dd0f9de1d --- /dev/null +++ b/Platforms/Apple/testbed/Testbed.lldbinit @@ -0,0 +1,4 @@ +process handle SIGINT -n true -p true -s false +process handle SIGUSR1 -n true -p true -s false +process handle SIGUSR2 -n true -p true -s false +process handle SIGXFSZ -n true -p true -s false diff --git a/Platforms/Apple/testbed/TestbedTests/TestbedTests.m b/Platforms/Apple/testbed/TestbedTests/TestbedTests.m new file mode 100644 index 00000000000000..f7788c47f2c229 --- /dev/null +++ b/Platforms/Apple/testbed/TestbedTests/TestbedTests.m @@ -0,0 +1,200 @@ +#import <XCTest/XCTest.h> +#import <Python/Python.h> + +@interface TestbedTests : XCTestCase + +@end + +@implementation TestbedTests + + +- (void)testPython { + const char **argv; + int exit_code; + int failed; + PyStatus status; + PyPreConfig preconfig; + PyConfig config; + PyObject *app_packages_path; + PyObject *method_args; + PyObject *result; + PyObject *site_module; + PyObject *site_addsitedir_attr; + PyObject *sys_module; + PyObject *sys_path_attr; + NSArray *test_args; + NSString *python_home; + NSString *path; + wchar_t *wtmp_str; + + NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; + + // Set some other common environment indicators to disable color, as the + // Xcode log can't display color. Stdout will report that it is *not* a + // TTY. + setenv("NO_COLOR", "1", true); + setenv("PYTHON_COLORS", "0", true); + + if (getenv("GITHUB_ACTIONS")) { + NSLog(@"Running in a GitHub Actions environment"); + } + // Arguments to pass into the test suite runner. + // argv[0] must identify the process; any subsequent arg + // will be handled as if it were an argument to `python -m test` + // The processInfo arguments contain the binary that is running, + // followed by the arguments defined in the test plan. This means: + // run_module = test_args[1] + // argv = ["Testbed"] + test_args[2:] + test_args = [[NSProcessInfo processInfo] arguments]; + if (test_args == NULL) { + NSLog(@"Unable to identify test arguments."); + } + NSLog(@"Test arguments: %@", test_args); + argv = malloc(sizeof(char *) * ([test_args count] - 1)); + argv[0] = "Testbed"; + for (int i = 1; i < [test_args count] - 1; i++) { + argv[i] = [[test_args objectAtIndex:i+1] UTF8String]; + } + + // Generate an isolated Python configuration. + NSLog(@"Configuring isolated Python..."); + PyPreConfig_InitIsolatedConfig(&preconfig); + PyConfig_InitIsolatedConfig(&config); + + // Configure the Python interpreter: + // Enforce UTF-8 encoding for stderr, stdout, file-system encoding and locale. + // See https://docs.python.org/3/library/os.html#python-utf-8-mode. + preconfig.utf8_mode = 1; + // Use the system logger for stdout/err + config.use_system_logger = 1; + // Don't buffer stdio. We want output to appears in the log immediately + config.buffered_stdio = 0; + // Don't write bytecode; we can't modify the app bundle + // after it has been signed. + config.write_bytecode = 0; + // Ensure that signal handlers are installed + config.install_signal_handlers = 1; + // Run the test module. + config.run_module = Py_DecodeLocale([[test_args objectAtIndex:1] UTF8String], NULL); + // For debugging - enable verbose mode. + // config.verbose = 1; + + NSLog(@"Pre-initializing Python runtime..."); + status = Py_PreInitialize(&preconfig); + if (PyStatus_Exception(status)) { + XCTFail(@"Unable to pre-initialize Python interpreter: %s", status.err_msg); + PyConfig_Clear(&config); + return; + } + + // Set the home for the Python interpreter + python_home = [NSString stringWithFormat:@"%@/python", resourcePath, nil]; + NSLog(@"PythonHome: %@", python_home); + wtmp_str = Py_DecodeLocale([python_home UTF8String], NULL); + status = PyConfig_SetString(&config, &config.home, wtmp_str); + if (PyStatus_Exception(status)) { + XCTFail(@"Unable to set PYTHONHOME: %s", status.err_msg); + PyConfig_Clear(&config); + return; + } + PyMem_RawFree(wtmp_str); + + // Read the site config + status = PyConfig_Read(&config); + if (PyStatus_Exception(status)) { + XCTFail(@"Unable to read site config: %s", status.err_msg); + PyConfig_Clear(&config); + return; + } + + NSLog(@"Configure argc/argv..."); + status = PyConfig_SetBytesArgv(&config, [test_args count] - 1, (char**) argv); + if (PyStatus_Exception(status)) { + XCTFail(@"Unable to configure argc/argv: %s", status.err_msg); + PyConfig_Clear(&config); + return; + } + + NSLog(@"Initializing Python runtime..."); + status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + XCTFail(@"Unable to initialize Python interpreter: %s", status.err_msg); + PyConfig_Clear(&config); + return; + } + + // Add app_packages as a site directory. This both adds to sys.path, + // and ensures that any .pth files in that directory will be executed. + site_module = PyImport_ImportModule("site"); + if (site_module == NULL) { + XCTFail(@"Could not import site module"); + return; + } + + site_addsitedir_attr = PyObject_GetAttrString(site_module, "addsitedir"); + if (site_addsitedir_attr == NULL || !PyCallable_Check(site_addsitedir_attr)) { + XCTFail(@"Could not access site.addsitedir"); + return; + } + + path = [NSString stringWithFormat:@"%@/app_packages", resourcePath, nil]; + NSLog(@"App packages path: %@", path); + wtmp_str = Py_DecodeLocale([path UTF8String], NULL); + app_packages_path = PyUnicode_FromWideChar(wtmp_str, wcslen(wtmp_str)); + if (app_packages_path == NULL) { + XCTFail(@"Could not convert app_packages path to unicode"); + return; + } + PyMem_RawFree(wtmp_str); + + method_args = Py_BuildValue("(O)", app_packages_path); + if (method_args == NULL) { + XCTFail(@"Could not create arguments for site.addsitedir"); + return; + } + + result = PyObject_CallObject(site_addsitedir_attr, method_args); + if (result == NULL) { + XCTFail(@"Could not add app_packages directory using site.addsitedir"); + return; + } + + // Add test code to sys.path + sys_module = PyImport_ImportModule("sys"); + if (sys_module == NULL) { + XCTFail(@"Could not import sys module"); + return; + } + + sys_path_attr = PyObject_GetAttrString(sys_module, "path"); + if (sys_path_attr == NULL) { + XCTFail(@"Could not access sys.path"); + return; + } + + path = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; + NSLog(@"App path: %@", path); + wtmp_str = Py_DecodeLocale([path UTF8String], NULL); + failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String])); + if (failed) { + XCTFail(@"Unable to add app to sys.path"); + return; + } + PyMem_RawFree(wtmp_str); + + // Ensure the working directory is the app folder. + chdir([path UTF8String]); + + // Start the test suite. Print a separator to differentiate Python startup logs from app logs + NSLog(@"---------------------------------------------------------------------------"); + + exit_code = Py_RunMain(); + XCTAssertEqual(exit_code, 0, @"Test suite did not pass"); + + NSLog(@"---------------------------------------------------------------------------"); + + Py_Finalize(); +} + + +@end diff --git a/Platforms/Apple/testbed/__main__.py b/Platforms/Apple/testbed/__main__.py new file mode 100644 index 00000000000000..b3eed38571d970 --- /dev/null +++ b/Platforms/Apple/testbed/__main__.py @@ -0,0 +1,435 @@ +import argparse +import json +import os +import re +import shlex +import shutil +import subprocess +import sys +from pathlib import Path + +TEST_SLICES = { + "iOS": "ios-arm64_x86_64-simulator", +} + +DECODE_ARGS = ("UTF-8", "backslashreplace") + +# The system log prefixes each line: +# 2025-01-17 16:14:29.093742+0800 iOSTestbed[23987:1fd393b4] ... +# 2025-01-17 16:14:29.093742+0800 iOSTestbed[23987:1fd393b4] ... + +LOG_PREFIX_REGEX = re.compile( + r"^\d{4}-\d{2}-\d{2}" # YYYY-MM-DD + r"\s+\d+:\d{2}:\d{2}\.\d+\+\d{4}" # HH:MM:SS.ssssss+ZZZZ + r"\s+iOSTestbed\[\d+:\w+\] " # Process/thread ID +) + + +# Select a simulator device to use. +def select_simulator_device(platform): + # List the testing simulators, in JSON format + raw_json = subprocess.check_output(["xcrun", "simctl", "list", "-j"]) + json_data = json.loads(raw_json) + + if platform == "iOS": + # Any iOS device will do; we'll look for "SE" devices - but the name + # isn't consistent over time. Older Xcode versions will use "iPhone SE + # (Nth generation)"; As of 2025, they've started using "iPhone 16e". + # + # When Xcode is updated after a new release, new devices will be + # available and old ones will be dropped from the set available on the + # latest iOS version. Select the one with the highest minimum runtime + # version - this is an indicator of the "newest" released device, which + # should always be supported on the "most recent" iOS version. + se_simulators = sorted( + (devicetype["minRuntimeVersion"], devicetype["name"]) + for devicetype in json_data["devicetypes"] + if devicetype["productFamily"] == "iPhone" + and ( + ( + "iPhone " in devicetype["name"] + and devicetype["name"].endswith("e") + ) + or "iPhone SE " in devicetype["name"] + ) + ) + simulator = se_simulators[-1][1] + else: + raise ValueError(f"Unknown platform {platform}") + + return simulator + + +def xcode_test(location: Path, platform: str, simulator: str, verbose: bool): + # Build and run the test suite on the named simulator. + args = [ + "-project", + str(location / f"{platform}Testbed.xcodeproj"), + "-scheme", + f"{platform}Testbed", + "-destination", + f"platform={platform} Simulator,name={simulator}", + "-derivedDataPath", + str(location / "DerivedData"), + ] + verbosity_args = [] if verbose else ["-quiet"] + + print("Building test project...") + subprocess.run( + ["xcodebuild", "build-for-testing"] + args + verbosity_args, + check=True, + ) + + # Any environment variable prefixed with TEST_RUNNER_ is exposed into the + # test runner environment. There are some variables (like those identifying + # CI platforms) that can be useful to have access to. + test_env = os.environ.copy() + if "GITHUB_ACTIONS" in os.environ: + test_env["TEST_RUNNER_GITHUB_ACTIONS"] = os.environ["GITHUB_ACTIONS"] + + print("Running test project...") + # Test execution *can't* be run -quiet; verbose mode + # is how we see the output of the test output. + process = subprocess.Popen( + ["xcodebuild", "test-without-building"] + args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=test_env, + ) + while line := (process.stdout.readline()).decode(*DECODE_ARGS): + # Strip the timestamp/process prefix from each log line + line = LOG_PREFIX_REGEX.sub("", line) + sys.stdout.write(line) + sys.stdout.flush() + + status = process.wait(timeout=5) + exit(status) + + +def copy(src, tgt): + """An all-purpose copy. + + If src is a file, it is copied. If src is a symlink, it is copied *as a + symlink*. If src is a directory, the full tree is duplicated, with symlinks + being preserved. + """ + if src.is_file() or src.is_symlink(): + shutil.copyfile(src, tgt, follow_symlinks=False) + else: + shutil.copytree(src, tgt, symlinks=True) + + +def clone_testbed( + source: Path, + target: Path, + framework: Path, + platform: str, + apps: list[Path], +) -> None: + if target.exists(): + print(f"{target} already exists; aborting without creating project.") + sys.exit(10) + + if framework is None: + if not ( + source / "Python.xcframework" / TEST_SLICES[platform] / "bin" + ).is_dir(): + print( + f"The testbed being cloned ({source}) does not contain " + "a framework with slices. Re-run with --framework" + ) + sys.exit(11) + else: + if not framework.is_dir(): + print(f"{framework} does not exist.") + sys.exit(12) + elif not ( + framework.suffix == ".xcframework" + or (framework / "Python.framework").is_dir() + ): + print( + f"{framework} is not an XCframework, " + f"or a simulator slice of a framework build." + ) + sys.exit(13) + + print("Cloning testbed project:") + print(f" Cloning {source}...", end="") + # Only copy the files for the platform being cloned plus the files common + # to all platforms. The XCframework will be copied later, if needed. + target.mkdir(parents=True) + + for name in [ + "__main__.py", + "TestbedTests", + "Testbed.lldbinit", + f"{platform}Testbed", + f"{platform}Testbed.xcodeproj", + f"{platform}Testbed.xctestplan", + ]: + copy(source / name, target / name) + + print(" done") + + orig_xc_framework_path = source / "Python.xcframework" + xc_framework_path = target / "Python.xcframework" + test_framework_path = xc_framework_path / TEST_SLICES[platform] + if framework is not None: + if framework.suffix == ".xcframework": + print(" Installing XCFramework...", end="") + xc_framework_path.symlink_to( + framework.relative_to(xc_framework_path.parent, walk_up=True) + ) + print(" done") + else: + print(" Installing simulator framework...", end="") + # We're only installing a slice of a framework; we need + # to do a full tree copy to make sure we don't damage + # symlinked content. + shutil.copytree(orig_xc_framework_path, xc_framework_path) + if test_framework_path.is_dir(): + shutil.rmtree(test_framework_path) + else: + test_framework_path.unlink(missing_ok=True) + test_framework_path.symlink_to( + framework.relative_to(test_framework_path.parent, walk_up=True) + ) + print(" done") + else: + copy(orig_xc_framework_path, xc_framework_path) + + if ( + xc_framework_path.is_symlink() + and not xc_framework_path.readlink().is_absolute() + ): + # XCFramework is a relative symlink. Rewrite the symlink relative + # to the new location. + print(" Rewriting symlink to XCframework...", end="") + resolved_xc_framework_path = ( + source / xc_framework_path.readlink() + ).resolve() + xc_framework_path.unlink() + xc_framework_path.symlink_to( + resolved_xc_framework_path.relative_to( + xc_framework_path.parent, walk_up=True + ) + ) + print(" done") + elif ( + test_framework_path.is_symlink() + and not test_framework_path.readlink().is_absolute() + ): + print(" Rewriting symlink to simulator framework...", end="") + # Simulator framework is a relative symlink. Rewrite the symlink + # relative to the new location. + orig_test_framework_path = ( + source / "Python.XCframework" / test_framework_path.readlink() + ).resolve() + test_framework_path.unlink() + test_framework_path.symlink_to( + orig_test_framework_path.relative_to( + test_framework_path.parent, walk_up=True + ) + ) + print(" done") + else: + print(" Using pre-existing Python framework.") + + for app_src in apps: + print(f" Installing app {app_src.name!r}...", end="") + app_target = target / f"Testbed/app/{app_src.name}" + if app_target.is_dir(): + shutil.rmtree(app_target) + shutil.copytree(app_src, app_target) + print(" done") + + print(f"Successfully cloned testbed: {target.resolve()}") + + +def update_test_plan(testbed_path, platform, args): + # Modify the test plan to use the requested test arguments. + test_plan_path = testbed_path / f"{platform}Testbed.xctestplan" + with test_plan_path.open("r", encoding="utf-8") as f: + test_plan = json.load(f) + + test_plan["defaultOptions"]["commandLineArgumentEntries"] = [ + {"argument": shlex.quote(arg)} for arg in args + ] + + with test_plan_path.open("w", encoding="utf-8") as f: + json.dump(test_plan, f, indent=2) + + +def run_testbed( + platform: str, + simulator: str | None, + args: list[str], + verbose: bool = False, +): + location = Path(__file__).parent + print("Updating test plan...", end="") + update_test_plan(location, platform, args) + print(" done.") + + if simulator is None: + simulator = select_simulator_device(platform) + print(f"Running test on {simulator}") + + xcode_test( + location, + platform=platform, + simulator=simulator, + verbose=verbose, + ) + + +def main(): + # Look for directories like `iOSTestbed` as an indicator of the platforms + # that the testbed folder supports. The original source testbed can support + # many platforms, but when cloned, only one platform is preserved. + available_platforms = [ + platform + for platform in ["iOS"] + if (Path(__file__).parent / f"{platform}Testbed").is_dir() + ] + + parser = argparse.ArgumentParser( + description=( + "Manages the process of testing an Apple Python project " + "through Xcode." + ), + ) + + subcommands = parser.add_subparsers(dest="subcommand") + clone = subcommands.add_parser( + "clone", + description=( + "Clone the testbed project, copying in a Python framework and" + "any specified application code." + ), + help="Clone a testbed project to a new location.", + ) + clone.add_argument( + "--framework", + help=( + "The location of the XCFramework (or simulator-only slice of an " + "XCFramework) to use when running the testbed" + ), + ) + clone.add_argument( + "--platform", + dest="platform", + choices=available_platforms, + default=available_platforms[0], + help=f"The platform to target (default: {available_platforms[0]})", + ) + clone.add_argument( + "--app", + dest="apps", + action="append", + default=[], + help="The location of any code to include in the testbed project", + ) + clone.add_argument( + "location", + help="The path where the testbed will be cloned.", + ) + + run = subcommands.add_parser( + "run", + usage=( + "%(prog)s [-h] [--simulator SIMULATOR] -- " + "<test arg> [<test arg> ...]" + ), + description=( + "Run a testbed project. The arguments provided after `--` will be " + "passed to the running iOS process as if they were arguments to " + "`python -m`." + ), + help="Run a testbed project", + ) + run.add_argument( + "--platform", + dest="platform", + choices=available_platforms, + default=available_platforms[0], + help=f"The platform to target (default: {available_platforms[0]})", + ) + run.add_argument( + "--simulator", + help=( + "The name of the simulator to use (eg: 'iPhone 16e'). Defaults to " + "the most recently released 'entry level' iPhone device. Device " + "architecture and OS version can also be specified; e.g., " + "`--simulator 'iPhone 16 Pro,arch=arm64,OS=26.0'` would run on " + "an ARM64 iPhone 16 Pro simulator running iOS 26.0." + ), + ) + run.add_argument( + "-v", + "--verbose", + action="store_true", + help="Enable verbose output", + ) + + try: + pos = sys.argv.index("--") + testbed_args = sys.argv[1:pos] + test_args = sys.argv[pos + 1 :] + except ValueError: + testbed_args = sys.argv[1:] + test_args = [] + + context = parser.parse_args(testbed_args) + + if context.subcommand == "clone": + clone_testbed( + source=Path(__file__).parent.resolve(), + target=Path(context.location).resolve(), + framework=Path(context.framework).resolve() + if context.framework + else None, + platform=context.platform, + apps=[Path(app) for app in context.apps], + ) + elif context.subcommand == "run": + if test_args: + if not ( + Path(__file__).parent + / "Python.xcframework" + / TEST_SLICES[context.platform] + / "bin" + ).is_dir(): + print( + "Testbed does not contain a compiled Python framework. " + f"Use `python {sys.argv[0]} clone ...` to create a " + "runnable clone of this testbed." + ) + sys.exit(20) + + run_testbed( + platform=context.platform, + simulator=context.simulator, + verbose=context.verbose, + args=test_args, + ) + else: + print( + "Must specify test arguments " + f"(e.g., {sys.argv[0]} run -- test)" + ) + print() + parser.print_help(sys.stderr) + sys.exit(21) + else: + parser.print_help(sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + # Under the buildbot, stdout is not a TTY, but we must still flush after + # every line to make sure our output appears in the correct order relative + # to the output of our subprocesses. + for stream in [sys.stdout, sys.stderr]: + stream.reconfigure(line_buffering=True) + main() diff --git a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj b/Platforms/Apple/testbed/iOSTestbed.xcodeproj/project.pbxproj similarity index 78% rename from iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj rename to Platforms/Apple/testbed/iOSTestbed.xcodeproj/project.pbxproj index c7d63909ee2453..f8835a3bc587df 100644 --- a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj +++ b/Platforms/Apple/testbed/iOSTestbed.xcodeproj/project.pbxproj @@ -11,12 +11,11 @@ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607A66212B0EFA390010BFC8 /* Assets.xcassets */; }; 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */; }; 607A66282B0EFA390010BFC8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66272B0EFA390010BFC8 /* main.m */; }; - 607A66322B0EFA3A0010BFC8 /* iOSTestbedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.m */; }; + 607A66322B0EFA3A0010BFC8 /* TestbedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66312B0EFA3A0010BFC8 /* TestbedTests.m */; }; 607A664C2B0EFC080010BFC8 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; }; 607A664D2B0EFC080010BFC8 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 607A66502B0EFFE00010BFC8 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; }; 607A66512B0EFFE00010BFC8 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */ = {isa = PBXBuildFile; fileRef = 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */; }; 608619542CB77BA900F46182 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = 608619532CB77BA900F46182 /* app_packages */; }; 608619562CB7819B00F46182 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 608619552CB7819B00F46182 /* app */; }; /* End PBXBuildFile section */ @@ -64,12 +63,12 @@ 607A66242B0EFA390010BFC8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 607A66272B0EFA390010BFC8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; 607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSTestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iOSTestbedTests.m; sourceTree = "<group>"; }; + 607A66312B0EFA3A0010BFC8 /* TestbedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestbedTests.m; sourceTree = "<group>"; }; 607A664A2B0EFB310010BFC8 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = "<group>"; }; - 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "dylib-Info-template.plist"; sourceTree = "<group>"; }; 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOSTestbed-Info.plist"; sourceTree = "<group>"; }; 608619532CB77BA900F46182 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = "<group>"; }; 608619552CB7819B00F46182 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = "<group>"; }; + 60FE0EFB2E56BB6D00524F87 /* iOSTestbed.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = iOSTestbed.xctestplan; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -95,9 +94,10 @@ 607A66092B0EFA380010BFC8 = { isa = PBXGroup; children = ( + 60FE0EFB2E56BB6D00524F87 /* iOSTestbed.xctestplan */, 607A664A2B0EFB310010BFC8 /* Python.xcframework */, 607A66142B0EFA380010BFC8 /* iOSTestbed */, - 607A66302B0EFA3A0010BFC8 /* iOSTestbedTests */, + 607A66302B0EFA3A0010BFC8 /* TestbedTests */, 607A66132B0EFA380010BFC8 /* Products */, 607A664F2B0EFFE00010BFC8 /* Frameworks */, ); @@ -118,7 +118,6 @@ 608619552CB7819B00F46182 /* app */, 608619532CB77BA900F46182 /* app_packages */, 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */, - 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */, 607A66152B0EFA380010BFC8 /* AppDelegate.h */, 607A66162B0EFA380010BFC8 /* AppDelegate.m */, 607A66212B0EFA390010BFC8 /* Assets.xcassets */, @@ -128,12 +127,12 @@ path = iOSTestbed; sourceTree = "<group>"; }; - 607A66302B0EFA3A0010BFC8 /* iOSTestbedTests */ = { + 607A66302B0EFA3A0010BFC8 /* TestbedTests */ = { isa = PBXGroup; children = ( - 607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.m */, + 607A66312B0EFA3A0010BFC8 /* TestbedTests.m */, ); - path = iOSTestbedTests; + path = TestbedTests; sourceTree = "<group>"; }; 607A664F2B0EFFE00010BFC8 /* Frameworks */ = { @@ -153,8 +152,7 @@ 607A660E2B0EFA380010BFC8 /* Sources */, 607A660F2B0EFA380010BFC8 /* Frameworks */, 607A66102B0EFA380010BFC8 /* Resources */, - 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */, - 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */, + 607A66552B0F061D0010BFC8 /* Process Python libraries */, 607A664E2B0EFC080010BFC8 /* Embed Frameworks */, ); buildRules = ( @@ -228,7 +226,6 @@ buildActionMask = 2147483647; files = ( 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */, - 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */, 608619562CB7819B00F46182 /* app in Resources */, 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */, 608619542CB77BA900F46182 /* app_packages in Resources */, @@ -245,7 +242,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */ = { + 607A66552B0F061D0010BFC8 /* Process Python libraries */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; @@ -255,34 +252,14 @@ ); inputPaths = ( ); - name = "Install Target Specific Python Standard Library"; + name = "Process Python libraries"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; - showEnvVarsInLog = 0; - }; - 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Prepare Python Binary Modules"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; + shellScript = "set -e\nsource $PROJECT_DIR/Python.xcframework/build/utils.sh\ninstall_python Python.xcframework app app_packages\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -301,7 +278,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 607A66322B0EFA3A0010BFC8 /* iOSTestbedTests.m in Sources */, + 607A66322B0EFA3A0010BFC8 /* TestbedTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -379,7 +356,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -434,7 +411,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -460,7 +437,7 @@ INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -491,7 +468,7 @@ INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -514,7 +491,7 @@ DEVELOPMENT_TEAM = 3HEZE76D99; GENERATE_INFOPLIST_FILE = YES; HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbedTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -534,7 +511,7 @@ DEVELOPMENT_TEAM = 3HEZE76D99; GENERATE_INFOPLIST_FILE = YES; HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbedTests; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Platforms/Apple/testbed/iOSTestbed.xcodeproj/xcshareddata/xcschemes/iOSTestbed.xcscheme b/Platforms/Apple/testbed/iOSTestbed.xcodeproj/xcshareddata/xcschemes/iOSTestbed.xcscheme new file mode 100644 index 00000000000000..3c330a4152bf92 --- /dev/null +++ b/Platforms/Apple/testbed/iOSTestbed.xcodeproj/xcshareddata/xcschemes/iOSTestbed.xcscheme @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1640" + version = "1.7"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES" + buildArchitectures = "Automatic"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "607A66112B0EFA380010BFC8" + BuildableName = "iOSTestbed.app" + BlueprintName = "iOSTestbed" + ReferencedContainer = "container:iOSTestbed.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SOURCE_ROOT)/Testbed.lldbinit" + shouldUseLaunchSchemeArgsEnv = "YES"> + <TestPlans> + <TestPlanReference + reference = "container:iOSTestbed.xctestplan" + default = "YES"> + </TestPlanReference> + </TestPlans> + <Testables> + <TestableReference + skipped = "NO" + parallelizable = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "607A662C2B0EFA3A0010BFC8" + BuildableName = "iOSTestbedTests.xctest" + BlueprintName = "iOSTestbedTests" + ReferencedContainer = "container:iOSTestbed.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "607A66112B0EFA380010BFC8" + BuildableName = "iOSTestbed.app" + BlueprintName = "iOSTestbed" + ReferencedContainer = "container:iOSTestbed.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "607A66112B0EFA380010BFC8" + BuildableName = "iOSTestbed.app" + BlueprintName = "iOSTestbed" + ReferencedContainer = "container:iOSTestbed.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/Platforms/Apple/testbed/iOSTestbed.xctestplan b/Platforms/Apple/testbed/iOSTestbed.xctestplan new file mode 100644 index 00000000000000..0c4ab9eb2bad30 --- /dev/null +++ b/Platforms/Apple/testbed/iOSTestbed.xctestplan @@ -0,0 +1,46 @@ +{ + "configurations" : [ + { + "id" : "F5A95CE4-1ADE-4A6E-A0E1-CDBAE26DF0C5", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + "commandLineArgumentEntries" : [ + { + "argument" : "test" + }, + { + "argument" : "-uall" + }, + { + "argument" : "--single-process" + }, + { + "argument" : "--rerun" + }, + { + "argument" : "-W" + } + ], + "targetForVariableExpansion" : { + "containerPath" : "container:iOSTestbed.xcodeproj", + "identifier" : "607A66112B0EFA380010BFC8", + "name" : "iOSTestbed" + } + }, + "testTargets" : [ + { + "parallelizable" : false, + "target" : { + "containerPath" : "container:iOSTestbed.xcodeproj", + "identifier" : "607A662C2B0EFA3A0010BFC8", + "name" : "iOSTestbedTests" + } + } + ], + "version" : 1 +} diff --git a/iOS/testbed/iOSTestbed/AppDelegate.h b/Platforms/Apple/testbed/iOSTestbed/AppDelegate.h similarity index 100% rename from iOS/testbed/iOSTestbed/AppDelegate.h rename to Platforms/Apple/testbed/iOSTestbed/AppDelegate.h diff --git a/iOS/testbed/iOSTestbed/AppDelegate.m b/Platforms/Apple/testbed/iOSTestbed/AppDelegate.m similarity index 100% rename from iOS/testbed/iOSTestbed/AppDelegate.m rename to Platforms/Apple/testbed/iOSTestbed/AppDelegate.m diff --git a/iOS/testbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json b/Platforms/Apple/testbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from iOS/testbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json rename to Platforms/Apple/testbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/iOS/testbed/iOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json b/Platforms/Apple/testbed/iOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from iOS/testbed/iOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Platforms/Apple/testbed/iOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/iOS/testbed/iOSTestbed/Assets.xcassets/Contents.json b/Platforms/Apple/testbed/iOSTestbed/Assets.xcassets/Contents.json similarity index 100% rename from iOS/testbed/iOSTestbed/Assets.xcassets/Contents.json rename to Platforms/Apple/testbed/iOSTestbed/Assets.xcassets/Contents.json diff --git a/iOS/testbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard b/Platforms/Apple/testbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from iOS/testbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard rename to Platforms/Apple/testbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard diff --git a/Platforms/Apple/testbed/iOSTestbed/app/README b/Platforms/Apple/testbed/iOSTestbed/app/README new file mode 100644 index 00000000000000..46c0e8e2a29a1c --- /dev/null +++ b/Platforms/Apple/testbed/iOSTestbed/app/README @@ -0,0 +1,7 @@ +This folder can contain any Python application code. + +During the build, any binary modules found in this folder will be processed into +Framework form. + +When the test suite runs, this folder will be on the PYTHONPATH, and will be the +working directory for the test suite. diff --git a/Platforms/Apple/testbed/iOSTestbed/app_packages/README b/Platforms/Apple/testbed/iOSTestbed/app_packages/README new file mode 100644 index 00000000000000..02c2beccfbdaed --- /dev/null +++ b/Platforms/Apple/testbed/iOSTestbed/app_packages/README @@ -0,0 +1,7 @@ +This folder can be a target for installing any Python dependencies needed by the +test suite. + +During the build, any binary modules found in this folder will be processed into +Framework form. + +When the test suite runs, this folder will be on the PYTHONPATH. diff --git a/Platforms/Apple/testbed/iOSTestbed/iOSTestbed-Info.plist b/Platforms/Apple/testbed/iOSTestbed/iOSTestbed-Info.plist new file mode 100644 index 00000000000000..fea45e1fad6f6f --- /dev/null +++ b/Platforms/Apple/testbed/iOSTestbed/iOSTestbed-Info.plist @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleDisplayName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>org.python.iOSTestbed</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>LSRequiresIPhoneOS</key> + <true/> + <key>UIRequiresFullScreen</key> + <true/> + <key>UILaunchStoryboardName</key> + <string>Launch Screen</string> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>UISupportedInterfaceOrientations~ipad</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> + <key>UIApplicationSceneManifest</key> + <dict> + <key>UIApplicationSupportsMultipleScenes</key> + <false/> + <key>UISceneConfigurations</key> + <dict/> + </dict> +</dict> +</plist> diff --git a/iOS/testbed/iOSTestbed/main.m b/Platforms/Apple/testbed/iOSTestbed/main.m similarity index 100% rename from iOS/testbed/iOSTestbed/main.m rename to Platforms/Apple/testbed/iOSTestbed/main.m diff --git a/Platforms/WASI/.ruff.toml b/Platforms/WASI/.ruff.toml new file mode 100644 index 00000000000000..492713c1520000 --- /dev/null +++ b/Platforms/WASI/.ruff.toml @@ -0,0 +1,27 @@ +extend = "../../.ruff.toml" # Inherit the project-wide settings + +target-version = "py314" + +[format] +preview = true +docstring-code-format = true + +[lint] +select = [ + "C4", # flake8-comprehensions + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "PGH", # pygrep-hooks + "PT", # flake8-pytest-style + "PYI", # flake8-pyi + "RUF100", # Ban unused `# noqa` comments + "UP", # pyupgrade + "W", # pycodestyle + "YTT", # flake8-2020 +] +ignore = [ + "E501", # Line too long +] diff --git a/Platforms/WASI/README.md b/Platforms/WASI/README.md new file mode 100644 index 00000000000000..62c82924d437b1 --- /dev/null +++ b/Platforms/WASI/README.md @@ -0,0 +1,59 @@ +# Python WASI (wasm32-wasi) build + +**WASI support is [tier 2](https://peps.python.org/pep-0011/#tier-2).** + +This directory contains configuration and helpers to facilitate cross +compilation of CPython to WebAssembly (WASM) using WASI. WASI builds +use WASM runtimes such as [wasmtime](https://wasmtime.dev/). + +**NOTE**: If you are looking for general information about WebAssembly that is +not directly related to CPython, please see https://github.com/psf/webassembly. + +## Build + +See [the devguide on how to build and run for WASI](https://devguide.python.org/getting-started/setup-building/#wasi). + +## Detecting WASI builds + +### Python code + +```python +import os, sys + +if sys.platform == "wasi": + # Python on WASI + ... + +if os.name == "posix": + # WASM platforms identify as POSIX-like. + # Windows does not provide os.uname(). + machine = os.uname().machine + if machine.startswith("wasm"): + # WebAssembly (wasm32, wasm64 potentially in the future) +``` + +```python +>>> import os, sys +>>> os.uname() +posix.uname_result( + sysname='wasi', + nodename='(none)', + release='0.0.0', + version='0.0.0', + machine='wasm32' +) +>>> os.name +'posix' +>>> sys.platform +'wasi' +``` + +### C code + +WASI SDK defines several built-in macros. You can dump a full list of built-ins +with ``/path/to/wasi-sdk/bin/clang -dM -E - < /dev/null``. + +* WebAssembly ``__wasm__`` (also ``__wasm``) +* wasm32 ``__wasm32__`` (also ``__wasm32``) +* wasm64 ``__wasm64__`` +* WASI ``__wasi__`` diff --git a/Platforms/WASI/__main__.py b/Platforms/WASI/__main__.py new file mode 100644 index 00000000000000..b8513a004f18e5 --- /dev/null +++ b/Platforms/WASI/__main__.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 + +__lazy_modules__ = ["_build"] + +import argparse +import os +import pathlib + +import _build + +HERE = pathlib.Path(__file__).parent + + +def main(): + default_host_runner = ( + "{WASMTIME} run " + # Set argv0 so that getpath.py can auto-discover the sysconfig data directory. + "--argv0 {ARGV0} " + # Map the checkout to / to load the stdlib from /Lib. + "--dir {CHECKOUT}::/ " + # Flags involving --optimize, --codegen, --debug, --wasm, and --wasi can be kept + # in a config file. + # We are using such a file to act as defaults in case a user wants to override + # only some of the settings themselves, make it easy to modify settings + # post-build so that they immediately apply to the Makefile instead of having to + # regenerate it, and allow for easy copying of the settings for anyone else who + # may want to use them. + "--config {WASMTIME_CONFIG_PATH}" + ) + + parser = argparse.ArgumentParser() + subcommands = parser.add_subparsers(dest="subcommand") + build = subcommands.add_parser("build", help="Build everything") + configure_build = subcommands.add_parser( + "configure-build-python", help="Run `configure` for the build Python" + ) + make_build = subcommands.add_parser( + "make-build-python", help="Run `make` for the build Python" + ) + build_python = subcommands.add_parser( + "build-python", help="Build the build Python" + ) + configure_host = subcommands.add_parser( + "configure-host", + help="Run `configure` for the " + "host/WASI (pydebug builds " + "are inferred from the build " + "Python)", + ) + make_host = subcommands.add_parser( + "make-host", help="Run `make` for the host/WASI" + ) + build_host = subcommands.add_parser( + "build-host", help="Build the host/WASI Python" + ) + subcommands.add_parser( + "clean", help="Delete files and directories created by this script" + ) + for subcommand in ( + build, + configure_build, + make_build, + build_python, + configure_host, + make_host, + build_host, + ): + subcommand.add_argument( + "--quiet", + action="store_true", + default=False, + dest="quiet", + help="Redirect output from subprocesses to a log file", + ) + subcommand.add_argument( + "--logdir", + type=pathlib.Path, + default=None, + help="Directory to store log files", + ) + for subcommand in ( + configure_build, + configure_host, + build_python, + build_host, + ): + subcommand.add_argument( + "--clean", + action="store_true", + default=False, + dest="clean", + help="Delete any relevant directories before building", + ) + for subcommand in ( + build, + configure_build, + configure_host, + build_python, + build_host, + ): + subcommand.add_argument( + "args", nargs="*", help="Extra arguments to pass to `configure`" + ) + for subcommand in build, configure_host, build_host: + subcommand.add_argument( + "--wasi-sdk", + type=pathlib.Path, + dest="wasi_sdk_path", + default=None, + help="Path to the WASI SDK; defaults to WASI_SDK_PATH environment variable " + "or the appropriate version found in /opt", + ) + subcommand.add_argument( + "--host-runner", + action="store", + default=default_host_runner, + dest="host_runner", + help="Command template for running the WASI host; defaults to " + f"`{default_host_runner}`", + ) + for subcommand in build, configure_host, make_host, build_host: + subcommand.add_argument( + "--host-triple", + action="store", + default=None, + help="The target triple for the WASI host build; " + f"defaults to the value found in {os.fsdecode(HERE / 'config.toml')}", + ) + + context = parser.parse_args() + + match context.subcommand: + case "configure-build-python": + _build.configure_build_python(context) + case "make-build-python": + _build.make_build_python(context) + case "build-python": + _build.configure_build_python(context) + _build.make_build_python(context) + case "configure-host": + _build.configure_wasi_python(context) + case "make-host": + _build.make_wasi_python(context) + case "build-host": + _build.configure_wasi_python(context) + _build.make_wasi_python(context) + case "build": + _build.configure_build_python(context) + _build.make_build_python(context) + _build.configure_wasi_python(context) + _build.make_wasi_python(context) + case "clean": + _build.clean_contents(context) + case _: + raise ValueError(f"Unknown subcommand {context.subcommand!r}") + + +if __name__ == "__main__": + main() diff --git a/Platforms/WASI/_build.py b/Platforms/WASI/_build.py new file mode 100644 index 00000000000000..c1a91a9c833b8e --- /dev/null +++ b/Platforms/WASI/_build.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python3 + +__lazy_modules__ = ["shutil", "sys", "tempfile", "tomllib"] + +import contextlib +import functools +import os +import pathlib +import shutil +import subprocess +import sys +import sysconfig +import tempfile +import tomllib + +try: + from os import process_cpu_count as cpu_count +except ImportError: + from os import cpu_count + + +CHECKOUT = HERE = pathlib.Path(__file__).parent + +while CHECKOUT != CHECKOUT.parent: + if (CHECKOUT / "configure").is_file(): + break + CHECKOUT = CHECKOUT.parent +else: + raise FileNotFoundError( + "Unable to find the root of the CPython checkout by looking for 'configure'" + ) + +CROSS_BUILD_DIR = CHECKOUT / "cross-build" +# Build platform can also be found via `config.guess`. +BUILD_DIR = CROSS_BUILD_DIR / sysconfig.get_config_var("BUILD_GNU_TYPE") + +LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" +LOCAL_SETUP_MARKER = ( + b"# Generated by Platforms/WASI .\n" + b"# Required to statically build extension modules." +) + +WASMTIME_VAR_NAME = "WASMTIME" +WASMTIME_HOST_RUNNER_VAR = f"{{{WASMTIME_VAR_NAME}}}" + + +def separator(): + """Print a separator line across the terminal width.""" + try: + tput_output = subprocess.check_output( + ["tput", "cols"], encoding="utf-8" + ) + except subprocess.CalledProcessError: + terminal_width = 80 + else: + terminal_width = int(tput_output.strip()) + print("⎯" * terminal_width) + + +def log(emoji, message, *, spacing=None): + """Print a notification with an emoji. + + If 'spacing' is None, calculate the spacing based on the number of code points + in the emoji as terminals "eat" a space when the emoji has multiple code points. + """ + if spacing is None: + spacing = " " if len(emoji) == 1 else " " + print("".join([emoji, spacing, message])) + + +def updated_env(updates={}): + """Create a new dict representing the environment to use. + + The changes made to the execution environment are printed out. + """ + env_defaults = {} + # https://reproducible-builds.org/docs/source-date-epoch/ + git_epoch_cmd = ["git", "log", "-1", "--pretty=%ct"] + try: + epoch = subprocess.check_output( + git_epoch_cmd, encoding="utf-8" + ).strip() + env_defaults["SOURCE_DATE_EPOCH"] = epoch + except subprocess.CalledProcessError: + pass # Might be building from a tarball. + # This layering lets SOURCE_DATE_EPOCH from os.environ takes precedence. + environment = env_defaults | os.environ | updates + + env_diff = {} + for key, value in environment.items(): + if os.environ.get(key) != value: + env_diff[key] = value + + env_vars = [ + f"\n {key}={item}" for key, item in sorted(env_diff.items()) + ] + log("🌎", f"Environment changes:{''.join(env_vars)}") + + return environment + + +def subdir(working_dir, *, clean_ok=False): + """Decorator to change to a working directory.""" + + def decorator(func): + @functools.wraps(func) + def wrapper(context): + nonlocal working_dir + + if callable(working_dir): + working_dir = working_dir(context) + separator() + log("📁", os.fsdecode(working_dir)) + if ( + clean_ok + and getattr(context, "clean", False) + and working_dir.exists() + ): + log("🚮", "Deleting directory (--clean)...") + shutil.rmtree(working_dir) + + working_dir.mkdir(parents=True, exist_ok=True) + + with contextlib.chdir(working_dir): + return func(context, working_dir) + + return wrapper + + return decorator + + +def call(command, *, context=None, quiet=False, **kwargs): + """Execute a command. + + If 'quiet' is true, then redirect stdout and stderr to a temporary file. + """ + if context is not None: + quiet = context.quiet + + log("❯", " ".join(map(str, command)), spacing=" ") + if not quiet: + stdout = None + stderr = None + else: + if (logdir := getattr(context, "logdir", None)) is None: + logdir = pathlib.Path(tempfile.gettempdir()) + stdout = tempfile.NamedTemporaryFile( + "w", + encoding="utf-8", + delete=False, + dir=logdir, + prefix="cpython-wasi-", + suffix=".log", + ) + stderr = subprocess.STDOUT + log("📝", f"Logging output to {stdout.name} (--quiet)...") + + subprocess.check_call(command, **kwargs, stdout=stdout, stderr=stderr) + + +def build_python_path(): + """The path to the build Python binary.""" + binary = BUILD_DIR / "python" + if not binary.is_file(): + binary = binary.with_suffix(".exe") + if not binary.is_file(): + raise FileNotFoundError( + f"Unable to find `python(.exe)` in {BUILD_DIR}" + ) + + return binary + + +def build_python_is_pydebug(): + """Find out if the build Python is a pydebug build.""" + test = "import sys, test.support; sys.exit(test.support.Py_DEBUG)" + result = subprocess.run( + [build_python_path(), "-c", test], + capture_output=True, + ) + return bool(result.returncode) + + +@subdir(BUILD_DIR, clean_ok=True) +def configure_build_python(context, working_dir): + """Configure the build/host Python.""" + if LOCAL_SETUP.exists(): + if LOCAL_SETUP.read_bytes() == LOCAL_SETUP_MARKER: + log("👍", f"{LOCAL_SETUP} exists ...") + else: + log("⚠️", f"{LOCAL_SETUP} exists, but has unexpected contents") + else: + log("📝", f"Creating {LOCAL_SETUP} ...") + LOCAL_SETUP.write_bytes(LOCAL_SETUP_MARKER) + + configure = [os.path.relpath(CHECKOUT / "configure", working_dir)] + if context.args: + configure.extend(context.args) + + call(configure, context=context) + + +@subdir(BUILD_DIR) +def make_build_python(context, working_dir): + """Make/build the build Python.""" + call(["make", "--jobs", str(cpu_count()), "all"], context=context) + + binary = build_python_path() + cmd = [ + binary, + "-c", + "import sys; " + "print(f'{sys.version_info.major}.{sys.version_info.minor}')", + ] + version = subprocess.check_output(cmd, encoding="utf-8").strip() + + log("🎉", f"{binary} {version}") + + +def wasi_sdk(context): + """Find the path to the WASI SDK.""" + if wasi_sdk_path := context.wasi_sdk_path: + if not wasi_sdk_path.exists(): + raise ValueError( + "WASI SDK not found at " + f"{os.fsdecode(wasi_sdk_path)!r} (via --wasi-sdk)" + ) + return wasi_sdk_path + + with (HERE / "config.toml").open("rb") as file: + config = tomllib.load(file) + wasi_sdk_version = config["targets"]["wasi-sdk"] + + if wasi_sdk_path_env_var := os.environ.get("WASI_SDK_PATH"): + wasi_sdk_path = pathlib.Path(wasi_sdk_path_env_var) + if not wasi_sdk_path.exists(): + raise ValueError( + f"WASI SDK not found at {os.fsdecode(wasi_sdk_path)!r} " + "(via $WASI_SDK_PATH)" + ) + else: + opt_path = pathlib.Path("/opt") + # WASI SDK versions have a ``.0`` suffix, but it's a constant; the WASI SDK team + # has said they don't plan to ever do a point release and all of their Git tags + # lack the ``.0`` suffix. + # Starting with WASI SDK 23, the tarballs went from containing a directory named + # ``wasi-sdk-{WASI_SDK_VERSION}.0`` to e.g. + # ``wasi-sdk-{WASI_SDK_VERSION}.0-x86_64-linux``. + potential_sdks = [ + path + for path in opt_path.glob(f"wasi-sdk-{wasi_sdk_version}.0*") + if path.is_dir() + ] + if len(potential_sdks) == 1: + wasi_sdk_path = potential_sdks[0] + elif (default_path := opt_path / "wasi-sdk").is_dir(): + wasi_sdk_path = default_path + + # Starting with WASI SDK 25, a VERSION file is included in the root + # of the SDK directory that we can read to warn folks when they are using + # an unsupported version. + if wasi_sdk_path and (version_file := wasi_sdk_path / "VERSION").is_file(): + version_details = version_file.read_text(encoding="utf-8") + found_version = version_details.splitlines()[0] + # Make sure there's a trailing dot to avoid false positives if somehow the + # supported version is a prefix of the found version (e.g. `25` and `2567`). + if not found_version.startswith(f"{wasi_sdk_version}."): + major_version = found_version.partition(".")[0] + log( + "⚠️", + f" Found WASI SDK {major_version}, " + f"but WASI SDK {wasi_sdk_version} is the supported version", + ) + elif not wasi_sdk_path: + raise ValueError( + f"WASI SDK {wasi_sdk_version} not found; " + "download from " + "https://github.com/WebAssembly/wasi-sdk and install in " + f"{os.fsdecode(opt_path)!r} or specify the SDK via " + "$WASI_SDK_PATH or --wasi-sdk" + ) + + # Cache the result. + context.wasi_sdk_path = wasi_sdk_path + return wasi_sdk_path + + +def wasi_sdk_env(context): + """Calculate environment variables for building with wasi-sdk.""" + wasi_sdk_path = wasi_sdk(context) + sysroot = wasi_sdk_path / "share" / "wasi-sysroot" + env = { + "CC": "clang", + "CPP": "clang-cpp", + "CXX": "clang++", + "AR": "llvm-ar", + "RANLIB": "ranlib", + } + + for env_var, binary_name in list(env.items()): + env[env_var] = os.fsdecode(wasi_sdk_path / "bin" / binary_name) + + if not wasi_sdk_path.name.startswith("wasi-sdk"): + for compiler in ["CC", "CPP", "CXX"]: + env[compiler] += f" --sysroot={sysroot}" + + env["PKG_CONFIG_PATH"] = "" + env["PKG_CONFIG_LIBDIR"] = os.pathsep.join( + map( + os.fsdecode, + [sysroot / "lib" / "pkgconfig", sysroot / "share" / "pkgconfig"], + ) + ) + env["PKG_CONFIG_SYSROOT_DIR"] = os.fsdecode(sysroot) + + env["WASI_SDK_PATH"] = os.fsdecode(wasi_sdk_path) + env["WASI_SYSROOT"] = os.fsdecode(sysroot) + + env["PATH"] = os.pathsep.join([ + os.fsdecode(wasi_sdk_path / "bin"), + os.environ["PATH"], + ]) + + return env + + +def host_triple(context): + """Determine the target triple for the WASI host build.""" + if context.host_triple: + return context.host_triple + + with (HERE / "config.toml").open("rb") as file: + config = tomllib.load(file) + + # Cache the result. + context.host_triple = config["targets"]["host-triple"] + return context.host_triple + + +@subdir(lambda context: CROSS_BUILD_DIR / host_triple(context), clean_ok=True) +def configure_wasi_python(context, working_dir): + """Configure the WASI/host build.""" + config_site = os.fsdecode(HERE / "config.site-wasm32-wasi") + + wasi_build_dir = working_dir.relative_to(CHECKOUT) + + args = { + "WASMTIME": "wasmtime", + "ARGV0": f"/{wasi_build_dir}/python.wasm", + "CHECKOUT": os.fsdecode(CHECKOUT), + "WASMTIME_CONFIG_PATH": os.fsdecode(HERE / "wasmtime.toml"), + } + # Check dynamically for wasmtime in case it was specified manually via + # `--host-runner`. + if "{WASMTIME}" in context.host_runner: + if wasmtime := shutil.which("wasmtime"): + args["WASMTIME"] = wasmtime + else: + raise FileNotFoundError( + "wasmtime not found; download from " + "https://github.com/bytecodealliance/wasmtime" + ) + host_runner = context.host_runner.format_map(args) + env_additions = {"CONFIG_SITE": config_site, "HOSTRUNNER": host_runner} + build_python = os.fsdecode(build_python_path()) + # The path to `configure` MUST be relative, else `python.wasm` is unable + # to find the stdlib due to Python not recognizing that it's being + # executed from within a checkout. + configure = [ + os.path.relpath(CHECKOUT / "configure", working_dir), + f"--host={host_triple(context)}", + f"--build={BUILD_DIR.name}", + f"--with-build-python={build_python}", + ] + if build_python_is_pydebug(): + configure.append("--with-pydebug") + if context.args: + configure.extend(context.args) + call( + configure, + env=updated_env(env_additions | wasi_sdk_env(context)), + context=context, + ) + + python_wasm = working_dir / "python.wasm" + exec_script = working_dir / "python.sh" + with exec_script.open("w", encoding="utf-8") as file: + file.write(f'#!/bin/sh\nexec {host_runner} {python_wasm} "$@"\n') + exec_script.chmod(0o755) + log("🏃", f"Created {exec_script} (--host-runner)... ") + sys.stdout.flush() + + +@subdir(lambda context: CROSS_BUILD_DIR / host_triple(context)) +def make_wasi_python(context, working_dir): + """Run `make` for the WASI/host build.""" + call( + ["make", "--jobs", str(cpu_count()), "all"], + env=updated_env(), + context=context, + ) + + exec_script = working_dir / "python.sh" + call([exec_script, "--version"], quiet=False) + log( + "🎉", + f"Use `{exec_script.relative_to(pathlib.Path().absolute())}` " + "to run CPython w/ the WASI host specified by --host-runner", + ) + + +def clean_contents(context): + """Delete all files created by this script.""" + if CROSS_BUILD_DIR.exists(): + log("🧹", f"Deleting {CROSS_BUILD_DIR} ...") + shutil.rmtree(CROSS_BUILD_DIR) + + if LOCAL_SETUP.exists(): + if LOCAL_SETUP.read_bytes() == LOCAL_SETUP_MARKER: + log("🧹", f"Deleting generated {LOCAL_SETUP} ...") diff --git a/Tools/wasm/wasi/config.site-wasm32-wasi b/Platforms/WASI/config.site-wasm32-wasi similarity index 100% rename from Tools/wasm/wasi/config.site-wasm32-wasi rename to Platforms/WASI/config.site-wasm32-wasi diff --git a/Platforms/WASI/config.toml b/Platforms/WASI/config.toml new file mode 100644 index 00000000000000..6a6d5713ee9673 --- /dev/null +++ b/Platforms/WASI/config.toml @@ -0,0 +1,6 @@ +# Any data that can vary between Python versions is to be kept in this file. +# This allows for blanket copying of the WASI build code between supported +# Python versions. +[targets] +wasi-sdk = 33 +host-triple = "wasm32-wasip1" diff --git a/Platforms/WASI/wasmtime.toml b/Platforms/WASI/wasmtime.toml new file mode 100644 index 00000000000000..5a45e8c3db94a6 --- /dev/null +++ b/Platforms/WASI/wasmtime.toml @@ -0,0 +1,5 @@ +# https://docs.wasmtime.dev/cli-options.html#cli-options-using-toml-file + +[wasm] +# 32 MiB; big enough for the test suite to pass under a debug build. +max-wasm-stack = 33_554_432 diff --git a/Platforms/emscripten/README.md b/Platforms/emscripten/README.md new file mode 100644 index 00000000000000..ce230c4b74a273 --- /dev/null +++ b/Platforms/emscripten/README.md @@ -0,0 +1,254 @@ +# Python Emscripten (wasm32-emscripten) build + +**Emscripten support is [tier 3](https://peps.python.org/pep-0011/#tier-3).** + +This directory contains configuration and helpers to facilitate cross +compilation of CPython to WebAssembly (WASM) using Emscripten. Emscripten builds +run in modern browsers and JavaScript runtimes like *Node.js*. + +For WASI builds, see [Platforms/WASI](../../Platforms/WASI/README.md). + +**NOTE**: If you are looking for general information about WebAssembly that is +not directly related to CPython, please see https://github.com/psf/webassembly. + +### Build + +See [the devguide instructions for building for Emscripten](https://devguide.python.org/getting-started/setup-building/#emscripten). + +### Running from node + +If you want to run the normal Python CLI, you can use `python.sh`. It takes the +same options as the normal Python CLI entrypoint, though the REPL does not +function and will crash. + +`python.sh` invokes `node_entry.mjs` which imports the Emscripten module for the +Python process and starts it up with the appropriate settings. If you wish to +make a node application that "embeds" the interpreter instead of acting like the +CLI you will need to write your own alternative to `node_entry.mjs`. + + +### Running tests + +After building, you can run the full test suite with: +```shell +./cross-build/wasm32-emscripten/build/python/python.sh -m test -uall +``` +You can run the browser smoke test with: +```shell +./Platforms/emscripten/browser_test/run_test.sh +``` + +### The Web Example + +When building for Emscripten, a small web example will be built automatically +in the ``web_example`` directory. To run the web example, ``cd`` into the +``web_example`` directory, then run ``python server.py``. This will start a web +server; you can then visit ``http://localhost:8000/`` in a browser to see a +simple REPL example. + +The web example relies on a bug fix in Emscripten version 3.1.73 so if you build +with earlier versions of Emscripten it may not work. The web example uses +``SharedArrayBuffer``. For security reasons browsers only provide +``SharedArrayBuffer`` in secure environments with cross-origin isolation. The +webserver must send cross-origin headers and correct MIME types for the +JavaScript and WebAssembly files. Otherwise the terminal will fail to load with +an error message like ``ReferenceError: SharedArrayBuffer is not defined``. See +more information here: +https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements + +Note that ``SharedArrayBuffer`` is _not required_ to use Python itself, only the +web example. If cross-origin isolation is not appropriate for your use case you +may make your own application embedding `python.mjs` which does not use +``SharedArrayBuffer`` and serve it without the cross-origin isolation headers. + +### Embedding Python in a custom JavaScript application + +You can look at `python.worker.mjs` and `node_entry.mjs` for inspiration. At a +minimum you must import ``createEmscriptenModule`` and you need to call +``createEmscriptenModule`` with an appropriate settings object. This settings +object will need a prerun hook that installs the Python standard library into +the Emscripten file system. + +#### NodeJs + +In Node, you can use the NodeFS to mount the standard library in your native +file system into the Emscripten file system: +```js +import createEmscriptenModule from "./python.mjs"; + +await createEmscriptenModule({ + preRun(Module) { + Module.FS.mount( + Module.FS.filesystems.NODEFS, + { root: "/path/to/python/stdlib" }, + "/lib/", + ); + }, +}); +``` + +#### Browser + +In the browser, the simplest approach is to put the standard library in a zip +file it and install it. With Python 3.14 this could look like: +```js +import createEmscriptenModule from "./python.mjs"; + +await createEmscriptenModule({ + async preRun(Module) { + Module.FS.mkdirTree("/lib/python3.14/lib-dynload/"); + Module.addRunDependency("install-stdlib"); + const resp = await fetch("python3.14.zip"); + const stdlibBuffer = await resp.arrayBuffer(); + Module.FS.writeFile(`/lib/python314.zip`, new Uint8Array(stdlibBuffer), { + canOwn: true, + }); + Module.removeRunDependency("install-stdlib"); + }, +}); +``` + +### Limitations and issues + +#### Network stack + +- Python's socket module does not work with Emscripten's emulated POSIX + sockets yet. Network modules like ``asyncio``, ``urllib``, ``selectors``, + etc. are not available. +- Only ``AF_INET`` and ``AF_INET6`` with ``SOCK_STREAM`` (TCP) or + ``SOCK_DGRAM`` (UDP) are available. ``AF_UNIX`` is not supported. +- ``socketpair`` does not work. +- Blocking sockets are not available and non-blocking sockets don't work + correctly, e.g. ``socket.accept`` crashes the runtime. ``gethostbyname`` + does not resolve to a real IP address. IPv6 is not available. +- The ``select`` module is limited. ``select.select()`` crashes the runtime + due to lack of exectfd support. + +#### processes, signals + +- Processes are not supported. System calls like fork, popen, and subprocess + fail with ``ENOSYS`` or ``ENOSUP``. +- Signal support is limited. ``signal.alarm``, ``itimer``, ``sigaction`` + are not available or do not work correctly. ``SIGTERM`` exits the runtime. +- Keyboard interrupt (CTRL+C) handling is not implemented yet. +- Resource-related functions like ``os.nice`` and most functions of the + ``resource`` module are not available. + +#### threading + +- Threading is disabled by default. The ``configure`` option + ``--enable-wasm-pthreads`` adds compiler flag ``-pthread`` and + linker flags ``-sUSE_PTHREADS -sPROXY_TO_PTHREAD``. +- pthread support requires WASM threads and SharedArrayBuffer (bulk memory). + The Node.JS runtime keeps a pool of web workers around. Each web worker + uses several file descriptors (eventfd, epoll, pipe). +- It's not advised to enable threading when building for browsers or with + dynamic linking support; there are performance and stability issues. + +#### file system + +- Most user, group, and permission related function and modules are not + supported or don't work as expected, e.g.``pwd`` module, ``grp`` module, + ``os.setgroups``, ``os.chown``, and so on. ``lchown`` and ``lchmod`` are + not available. +- ``umask`` is a no-op. +- hard links (``os.link``) are not supported. +- Offset and iovec I/O functions (e.g. ``os.pread``, ``os.preadv``) are not + available. +- ``os.mknod`` and ``os.mkfifo`` + [don't work](https://github.com/emscripten-core/emscripten/issues/16158) + and are disabled. +- Large file support crashes the runtime and is disabled. +- ``mmap`` module is unstable. flush (``msync``) can crash the runtime. + +#### Misc + +- Heap memory and stack size are limited. Recursion or extensive memory + consumption can crash Python. +- Most stdlib modules with a dependency on external libraries are missing, + e.g. ``ctypes``, ``readline``, ``ssl``, and more. +- Shared extension modules are not implemented yet. All extension modules + are statically linked into the main binary. The experimental configure + option ``--enable-wasm-dynamic-linking`` enables dynamic extension + support. It's currently known to crash in combination with threading. +- glibc extensions for date and time formatting are not available. +- ``locales`` module is affected by musl libc issues, + [gh-90548](https://github.com/python/cpython/issues/90548). +- Python's object allocator ``obmalloc`` is disabled by default. +- ``ensurepip`` is not available. + +#### In the browser + +- The interactive shell does not handle copy 'n paste and unicode support + well. +- The bundled stdlib is limited. Network-related modules, + multiprocessing, dbm, tests and similar modules + are not shipped. All other modules are bundled as pre-compiled + ``pyc`` files. +- In-memory file system (MEMFS) is not persistent and limited. +- Test modules are built by default. Use ``--disable-test-modules`` to disable + building test modules like ``_testcapi``. + +## Detecting Emscripten builds + +### Python code + +```python +import os, sys + +if sys.platform == "emscripten": + # Python on Emscripten + ... + +if os.name == "posix": + # WASM platforms identify as POSIX-like. + # Windows does not provide os.uname(). + machine = os.uname().machine + if machine.startswith("wasm"): + # WebAssembly (wasm32, wasm64 potentially in the future) +``` + +```python +>>> import os, sys +>>> os.uname() +posix.uname_result( + sysname='Emscripten', + nodename='emscripten', + release='4.0.2', + version='#1', + machine='wasm32' +) +>>> os.name +'posix' +>>> sys.platform +'emscripten' +>>> sys._emscripten_info +sys._emscripten_info( + emscripten_version=(3, 1, 10), + runtime='Mozilla/5.0 (X11; Linux x86_64; rv:104.0) Gecko/20100101 Firefox/104.0', + pthreads=False, + shared_memory=False +) +``` + +```python +>>> sys._emscripten_info +sys._emscripten_info( + emscripten_version=(3, 1, 19), + runtime='Node.js v14.18.2', + pthreads=True, + shared_memory=True +) +``` + +### C code + +Emscripten SDK defines several built-in macros. You can dump a full list of +built-ins with ``emcc -dM -E - < /dev/null``. + +* WebAssembly ``__wasm__`` (also ``__wasm``) +* wasm32 ``__wasm32__`` (also ``__wasm32``) +* wasm64 ``__wasm64__`` +* Emscripten ``__EMSCRIPTEN__`` (also ``EMSCRIPTEN``) +* Emscripten version ``__EMSCRIPTEN_major__``, ``__EMSCRIPTEN_minor__``, ``__EMSCRIPTEN_tiny__`` + diff --git a/Platforms/emscripten/__main__.py b/Platforms/emscripten/__main__.py new file mode 100644 index 00000000000000..c2fb1c4c36e608 --- /dev/null +++ b/Platforms/emscripten/__main__.py @@ -0,0 +1,863 @@ +#!/usr/bin/env python3 + +import argparse +import contextlib +import functools +import hashlib +import json +import os +import shutil +import subprocess +import sys +import sysconfig +import tempfile +from pathlib import Path +from textwrap import dedent +from urllib.request import urlopen + +import tomllib + +try: + from os import process_cpu_count as cpu_count +except ImportError: + from os import cpu_count + + +EMSCRIPTEN_DIR = Path(__file__).parent +CHECKOUT = EMSCRIPTEN_DIR.parent.parent +CONFIG_FILE = EMSCRIPTEN_DIR / "config.toml" + +DEFAULT_CROSS_BUILD_DIR = CHECKOUT / "cross-build" +HOST_TRIPLE = "wasm32-emscripten" + + +@functools.cache +def load_config_toml(): + with CONFIG_FILE.open("rb") as file: + return tomllib.load(file) + + +@functools.cache +def required_emscripten_version(): + return load_config_toml()["emscripten-version"] + + +@functools.cache +def emsdk_cache_root(emsdk_cache): + required_version = required_emscripten_version() + return Path(emsdk_cache).absolute() / required_version + + +@functools.cache +def emsdk_activate_path(emsdk_cache): + return emsdk_cache_root(emsdk_cache) / "emsdk/emsdk_env.sh" + + +def get_build_paths(cross_build_dir=None, emsdk_cache=None): + """Compute all build paths from the given cross-build directory.""" + if cross_build_dir is None: + cross_build_dir = DEFAULT_CROSS_BUILD_DIR + cross_build_dir = Path(cross_build_dir).absolute() + host_triple_dir = cross_build_dir / HOST_TRIPLE + prefix_dir = host_triple_dir / "prefix" + if emsdk_cache: + prefix_dir = emsdk_cache_root(emsdk_cache) / "prefix" + + return { + "cross_build_dir": cross_build_dir, + "native_build_dir": cross_build_dir / "build", + "host_triple_dir": host_triple_dir, + "host_build_dir": host_triple_dir / "build", + "host_dir": host_triple_dir / "build" / "python", + "prefix_dir": prefix_dir, + } + + +LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" +LOCAL_SETUP_MARKER = b"# Generated by Platforms/wasm/emscripten.py\n" + + +@functools.cache +def validate_emsdk_version(emsdk_cache): + """Validate that the emsdk cache contains the required emscripten version.""" + if emsdk_cache is None: + print("Build will use EMSDK from current environment.") + return + required_version = required_emscripten_version() + emsdk_env = emsdk_activate_path(emsdk_cache) + if not emsdk_env.is_file(): + print( + f"Required emscripten version {required_version} not found in {emsdk_cache}", + file=sys.stderr, + ) + sys.exit(1) + print(f"✅ Emscripten version {required_version} found in {emsdk_cache}") + + +def parse_env(text): + result = {} + for line in text.splitlines(): + key, val = line.split("=", 1) + result[key] = val + return result + + +@functools.cache +def get_emsdk_environ(emsdk_cache): + """Returns os.environ updated by sourcing emsdk_env.sh""" + if not emsdk_cache: + return os.environ + env_text = subprocess.check_output( + [ + "bash", + "-c", + f"EMSDK_QUIET=1 source {emsdk_activate_path(emsdk_cache)} && env", + ], + text=True, + ) + return parse_env(env_text) + + +def updated_env(updates, emsdk_cache): + """Create a new dict representing the environment to use. + + The changes made to the execution environment are printed out. + """ + env_defaults = {} + # https://reproducible-builds.org/docs/source-date-epoch/ + git_epoch_cmd = ["git", "log", "-1", "--pretty=%ct"] + try: + epoch = subprocess.check_output( + git_epoch_cmd, encoding="utf-8" + ).strip() + env_defaults["SOURCE_DATE_EPOCH"] = epoch + except subprocess.CalledProcessError: + pass # Might be building from a tarball. + # This layering lets SOURCE_DATE_EPOCH from os.environ takes precedence. + environment = env_defaults | get_emsdk_environ(emsdk_cache) | updates + env_diff = {} + for key, value in environment.items(): + if os.environ.get(key) != value: + env_diff[key] = value + + print("🌎 Environment changes:") + for key in sorted(env_diff.keys()): + print(f" {key}={env_diff[key]}") + + return environment + + +def subdir(path_key, *, clean_ok=False): + """Decorator to change to a working directory. + + path_key is a key into context.build_paths, used to resolve the working + directory at call time. + """ + + def decorator(func): + @functools.wraps(func) + def wrapper(context): + working_dir = context.build_paths[path_key] + try: + tput_output = subprocess.check_output( + ["tput", "cols"], encoding="utf-8" + ) + terminal_width = int(tput_output.strip()) + except subprocess.CalledProcessError: + terminal_width = 80 + print("⎯" * terminal_width) + print("📁", working_dir) + if ( + clean_ok + and getattr(context, "clean", False) + and working_dir.exists() + ): + print("🚮 Deleting directory (--clean)...") + shutil.rmtree(working_dir) + + working_dir.mkdir(parents=True, exist_ok=True) + + with contextlib.chdir(working_dir): + return func(context, working_dir) + + return wrapper + + return decorator + + +def call(command, *, quiet, **kwargs): + """Execute a command. + + If 'quiet' is true, then redirect stdout and stderr to a temporary file. + """ + print("❯", " ".join(map(str, command))) + if not quiet: + stdout = None + stderr = None + else: + stdout = tempfile.NamedTemporaryFile( + "w", + encoding="utf-8", + delete=False, + prefix="cpython-emscripten-", + suffix=".log", + ) + stderr = subprocess.STDOUT + print(f"📝 Logging output to {stdout.name} (--quiet)...") + + subprocess.check_call(command, **kwargs, stdout=stdout, stderr=stderr) + + +def build_platform(): + """The name of the build/host platform.""" + # Can also be found via `config.guess`.` + return sysconfig.get_config_var("BUILD_GNU_TYPE") + + +def build_python_path(context): + """The path to the build Python binary.""" + native_build_dir = context.build_paths["native_build_dir"] + binary = native_build_dir / "python" + if not binary.is_file(): + binary = binary.with_suffix(".exe") + if not binary.is_file(): + raise FileNotFoundError( + f"Unable to find `python(.exe)` in {native_build_dir}" + ) + + return binary + + +def install_emscripten(context): + emsdk_cache = context.emsdk_cache + if emsdk_cache is None: + print("install-emscripten requires --emsdk-cache", file=sys.stderr) + sys.exit(1) + version = required_emscripten_version() + emsdk_target = emsdk_cache_root(emsdk_cache) / "emsdk" + if emsdk_target.exists(): + if not context.quiet: + print(f"Emscripten version {version} already installed") + return + if not context.quiet: + print(f"Installing emscripten version {version}") + emsdk_target.mkdir(parents=True) + call( + [ + "git", + "clone", + "https://github.com/emscripten-core/emsdk.git", + emsdk_target, + ], + quiet=context.quiet, + ) + call([emsdk_target / "emsdk", "install", version], quiet=context.quiet) + call([emsdk_target / "emsdk", "activate", version], quiet=context.quiet) + if not context.quiet: + print(f"Installed emscripten version {version}") + + +@subdir("native_build_dir", clean_ok=True) +def configure_build_python(context, working_dir): + """Configure the build/host Python.""" + if LOCAL_SETUP.exists(): + print(f"👍 {LOCAL_SETUP} exists ...") + else: + print(f"📝 Touching {LOCAL_SETUP} ...") + LOCAL_SETUP.write_bytes(LOCAL_SETUP_MARKER) + + configure = [os.path.relpath(CHECKOUT / "configure", working_dir)] + if context.args: + configure.extend(context.args) + + call(configure, quiet=context.quiet) + + +@subdir("native_build_dir") +def make_build_python(context, working_dir): + """Make/build the build Python.""" + call(["make", "--jobs", str(cpu_count()), "all"], quiet=context.quiet) + + binary = build_python_path(context) + cmd = [ + binary, + "-c", + "import sys; " + "print(f'{sys.version_info.major}.{sys.version_info.minor}')", + ] + version = subprocess.check_output(cmd, encoding="utf-8").strip() + + print(f"🎉 {binary} {version}") + + +def check_shasum(file: str, expected_shasum: str): + with open(file, "rb") as f: + digest = hashlib.file_digest(f, "sha256") + if digest.hexdigest() != expected_shasum: + raise RuntimeError(f"Unexpected shasum for {file}") + + +def download_and_unpack(working_dir: Path, url: str, expected_shasum: str): + with tempfile.NamedTemporaryFile( + suffix=".tar.gz", delete_on_close=False + ) as tmp_file: + with urlopen(url) as response: + shutil.copyfileobj(response, tmp_file) + tmp_file.close() + check_shasum(tmp_file.name, expected_shasum) + shutil.unpack_archive(tmp_file.name, working_dir) + + +def should_build_library(prefix, name, config, quiet): + cached_config = prefix / (name + ".json") + if not cached_config.exists(): + if not quiet: + print( + f"No cached build of {name} version {config['version']} found, building" + ) + return True + + try: + with cached_config.open("rb") as f: + cached_config = json.load(f) + except json.JSONDecodeError: + if not quiet: + print(f"Cached data for {name} invalid, rebuilding") + return True + if config == cached_config: + if not quiet: + print( + f"Found cached build of {name} version {config['version']}, not rebuilding" + ) + return False + + if not quiet: + print( + f"Found cached build of {name} version {config['version']} but it's out of date, rebuilding" + ) + return True + + +def write_library_config(prefix, name, config, quiet): + cached_config = prefix / (name + ".json") + with cached_config.open("w") as f: + json.dump(config, f) + if not quiet: + print(f"Succeded building {name}, wrote config to {cached_config}") + + +@subdir("host_build_dir", clean_ok=True) +def make_emscripten_libffi(context, working_dir): + validate_emsdk_version(context.emsdk_cache) + prefix = context.build_paths["prefix_dir"] + libffi_config = load_config_toml()["dependencies"]["libffi"] + with open(EMSCRIPTEN_DIR / "make_libffi.sh", "rb") as f: + libffi_config["make_libffi_shasum"] = hashlib.file_digest(f, "sha256").hexdigest() + if not should_build_library( + prefix, "libffi", libffi_config, context.quiet + ): + return + + if context.check_up_to_date: + print("libffi out of date, expected to be up to date", file=sys.stderr) + sys.exit(1) + + url = libffi_config["url"] + version = libffi_config["version"] + shasum = libffi_config["shasum"] + libffi_dir = working_dir / f"libffi-{version}" + shutil.rmtree(libffi_dir, ignore_errors=True) + download_and_unpack( + working_dir, + url.format(version=version), + shasum, + ) + call( + [EMSCRIPTEN_DIR / "make_libffi.sh"], + env=updated_env({"PREFIX": prefix}, context.emsdk_cache), + cwd=libffi_dir, + quiet=context.quiet, + ) + write_library_config(prefix, "libffi", libffi_config, context.quiet) + + +@subdir("host_build_dir", clean_ok=True) +def make_mpdec(context, working_dir): + validate_emsdk_version(context.emsdk_cache) + prefix = context.build_paths["prefix_dir"] + mpdec_config = load_config_toml()["dependencies"]["mpdec"] + if not should_build_library(prefix, "mpdec", mpdec_config, context.quiet): + return + + if context.check_up_to_date: + print("libmpdec out of date, expected to be up to date", file=sys.stderr) + sys.exit(1) + + url = mpdec_config["url"] + version = mpdec_config["version"] + shasum = mpdec_config["shasum"] + mpdec_dir = working_dir / f"mpdecimal-{version}" + shutil.rmtree(mpdec_dir, ignore_errors=True) + download_and_unpack( + working_dir, + url.format(version=version), + shasum, + ) + call( + [ + "emconfigure", + mpdec_dir / "configure", + "CFLAGS=-fPIC", + "--prefix", + prefix, + "--disable-shared", + ], + cwd=mpdec_dir, + quiet=context.quiet, + env=updated_env({}, context.emsdk_cache), + ) + call( + ["make", "install"], + cwd=mpdec_dir, + quiet=context.quiet, + ) + write_library_config(prefix, "mpdec", mpdec_config, context.quiet) + + +def make_dependencies(context): + make_emscripten_libffi(context) + make_mpdec(context) + + +def calculate_node_path(): + node_version = os.environ.get("PYTHON_NODE_VERSION", None) + if node_version is None: + node_version = load_config_toml()["node-version"] + + subprocess.run( + [ + "bash", + "-c", + f"source ~/.nvm/nvm.sh && nvm install {node_version}", + ], + check=True, + ) + + res = subprocess.run( + [ + "bash", + "-c", + f"source ~/.nvm/nvm.sh && nvm which {node_version}", + ], + text=True, + capture_output=True, + check=True, + ) + return res.stdout.strip() + + +@subdir("host_dir", clean_ok=True) +def configure_emscripten_python(context, working_dir): + """Configure the emscripten/host build.""" + validate_emsdk_version(context.emsdk_cache) + host_runner = context.host_runner + if host_runner is None: + host_runner = calculate_node_path() + + paths = context.build_paths + config_site = os.fsdecode(EMSCRIPTEN_DIR / "config.site-wasm32-emscripten") + + emscripten_build_dir = working_dir.relative_to(CHECKOUT) + + python_build_dir = paths["native_build_dir"] / "build" + lib_dirs = list(python_build_dir.glob("lib.*")) + assert len(lib_dirs) == 1, ( + f"Expected a single lib.* directory in {python_build_dir}" + ) + lib_dir = os.fsdecode(lib_dirs[0]) + pydebug = lib_dir.endswith("-pydebug") + python_version = lib_dir.removesuffix("-pydebug").rpartition("-")[-1] + sysconfig_data = ( + f"{emscripten_build_dir}/build/lib.emscripten-wasm32-{python_version}" + ) + if pydebug: + sysconfig_data += "-pydebug" + pkg_config_path_dir = (paths["prefix_dir"] / "lib/pkgconfig/").resolve() + env_additions = { + "CONFIG_SITE": config_site, + "HOSTRUNNER": host_runner, + "EM_PKG_CONFIG_PATH": str(pkg_config_path_dir), + } + build_python = os.fsdecode(build_python_path(context)) + configure = [ + "emconfigure", + os.path.relpath(CHECKOUT / "configure", working_dir), + "CFLAGS=-DPY_CALL_TRAMPOLINE -sUSE_BZIP2", + "PKG_CONFIG=pkg-config", + f"--host={HOST_TRIPLE}", + f"--build={build_platform()}", + f"--with-build-python={build_python}", + "--without-pymalloc", + "--disable-shared", + "--disable-ipv6", + "--enable-big-digits=30", + "--enable-wasm-dynamic-linking", + f"--prefix={paths['prefix_dir']}", + ] + if pydebug: + configure.append("--with-pydebug") + if context.args: + configure.extend(context.args) + call( + configure, + env=updated_env(env_additions, context.emsdk_cache), + quiet=context.quiet, + ) + + shutil.copy( + EMSCRIPTEN_DIR / "node_entry.mjs", working_dir / "node_entry.mjs" + ) + + shutil.copy( + EMSCRIPTEN_DIR / "streams.mjs", working_dir / "streams.mjs" + ) + + node_entry = working_dir / "node_entry.mjs" + exec_script = working_dir / "python.sh" + exec_script.write_text( + dedent( + f"""\ + #!/bin/sh + + # Macs come with FreeBSD coreutils which doesn't have the -s option + # so feature detect and work around it. + if which grealpath > /dev/null 2>&1; then + # It has brew installed gnu core utils, use that + REALPATH="grealpath -s" + elif which realpath > /dev/null 2>&1 && realpath --version > /dev/null 2>&1 && realpath --version | grep GNU > /dev/null 2>&1; then + # realpath points to GNU realpath so use it. + REALPATH="realpath -s" + else + # Shim for macs without GNU coreutils + abs_path () {{ + echo "$(cd "$(dirname "$1")" || exit; pwd)/$(basename "$1")" + }} + REALPATH=abs_path + fi + + # Before node 24, --experimental-wasm-jspi uses different API, + # After node 24 JSPI is on by default. + ARGS=$({host_runner} -e "$(cat <<"EOF" + const major_version = Number(process.version.split(".")[0].slice(1)); + if (major_version === 24) {{ + process.stdout.write("--experimental-wasm-jspi"); + }} + EOF + )") + + # We compute our own path, not following symlinks and pass it in so that + # node_entry.mjs can set sys.executable correctly. + # Intentionally allow word splitting on NODEFLAGS. + exec {host_runner} $NODEFLAGS $ARGS {node_entry} --this-program="$($REALPATH "$0")" "$@" + """ + ) + ) + exec_script.chmod(0o755) + print(f"🏃‍♀️ Created {exec_script} ... ") + sys.stdout.flush() + + +@subdir("host_dir") +def make_emscripten_python(context, working_dir): + """Run `make` for the emscripten/host build.""" + call( + ["make", "--jobs", str(cpu_count()), "all"], + env=updated_env({}, context.emsdk_cache), + quiet=context.quiet, + ) + + exec_script = working_dir / "python.sh" + subprocess.check_call([exec_script, "--version"]) + + +def run_emscripten_python(context): + """Run the built emscripten Python.""" + host_dir = context.build_paths["host_dir"] + exec_script = host_dir / "python.sh" + if not exec_script.is_file(): + print("Emscripten not built", file=sys.stderr) + sys.exit(1) + + args = context.args + # Strip the "--" separator if present + if args and args[0] == "--": + args = args[1:] + + if context.test: + args = load_config_toml()["test-args"] + args + elif context.pythoninfo: + args = load_config_toml()["pythoninfo-args"] + args + + os.execv(str(exec_script), [str(exec_script), *args]) + + +def build_target(context): + """Build one or more targets.""" + steps = [] + if context.target in {"build", "all"}: + steps.extend([ + configure_build_python, + make_build_python, + ]) + if context.target in {"host", "all"}: + steps.extend([ + make_emscripten_libffi, + make_mpdec, + configure_emscripten_python, + make_emscripten_python, + ]) + + for step in steps: + step(context) + + +def clean_contents(context): + """Delete all files created by this script.""" + if context.target in {"all", "build"}: + build_dir = context.build_paths["native_build_dir"] + if build_dir.exists(): + print(f"🧹 Deleting {build_dir} ...") + shutil.rmtree(build_dir) + + if context.target in {"all", "host"}: + host_triple_dir = context.build_paths["host_triple_dir"] + if host_triple_dir.exists(): + print(f"🧹 Deleting {host_triple_dir} ...") + shutil.rmtree(host_triple_dir) + + if LOCAL_SETUP.exists(): + with LOCAL_SETUP.open("rb") as file: + if file.read(len(LOCAL_SETUP_MARKER)) == LOCAL_SETUP_MARKER: + print(f"🧹 Deleting generated {LOCAL_SETUP} ...") + + +def add_cross_build_dir_option(subcommand): + subcommand.add_argument( + "--cross-build-dir", + action="store", + default=os.environ.get("CROSS_BUILD_DIR"), + dest="cross_build_dir", + help=( + "Path to the cross-build directory " + f"(default: {DEFAULT_CROSS_BUILD_DIR}). " + "Can also be set with the CROSS_BUILD_DIR environment variable." + ), + ) + + +def main(): + parser = argparse.ArgumentParser() + subcommands = parser.add_subparsers(dest="subcommand") + + install_emscripten_cmd = subcommands.add_parser( + "install-emscripten", + help="Install the appropriate version of Emscripten", + ) + + build = subcommands.add_parser("build", help="Build everything") + build.add_argument( + "target", + nargs="?", + default="all", + choices=["all", "host", "build"], + help=( + "What should be built. 'build' for just the build platform, or " + "'host' for the host platform, or 'all' for both. Defaults to 'all'." + ), + ) + + configure_build = subcommands.add_parser( + "configure-build-python", help="Run `configure` for the build Python" + ) + + make_mpdec_cmd = subcommands.add_parser( + "make-mpdec", + help="Clone mpdec repo, configure and build it for emscripten", + ) + + make_libffi_cmd = subcommands.add_parser( + "make-libffi", + help="Clone libffi repo, configure and build it for emscripten", + ) + + make_dependencies_cmd = subcommands.add_parser( + "make-dependencies", + help="Build all static library dependencies", + ) + + for cmd in [make_mpdec_cmd, make_libffi_cmd, make_dependencies_cmd]: + cmd.add_argument( + "--check-up-to-date", + action="store_true", + default=False, + help=("If passed, will fail if dependency is out of date"), + ) + + make_build = subcommands.add_parser( + "make-build-python", help="Run `make` for the build Python" + ) + + configure_host = subcommands.add_parser( + "configure-host", + help=( + "Run `configure` for the host/emscripten " + "(pydebug builds are inferred from the build Python)" + ), + ) + + make_host = subcommands.add_parser( + "make-host", help="Run `make` for the host/emscripten" + ) + + run = subcommands.add_parser( + "run", + help="Run the built emscripten Python", + ) + run.add_argument( + "--test", + action="store_true", + default=False, + help=( + "Add the default test arguments to the beginning of the command. " + "Default arguments loaded from Platforms/emscripten/config.toml" + ), + ) + run.add_argument( + "--pythoninfo", + action="store_true", + default=False, + help="Run -m test.pythoninfo", + ) + run.add_argument( + "args", + nargs=argparse.REMAINDER, + help=( + "Arguments to pass to the emscripten Python " + "(use '--' to separate from run options)" + ), + ) + add_cross_build_dir_option(run) + + clean = subcommands.add_parser( + "clean", help="Delete files and directories created by this script" + ) + clean.add_argument( + "target", + nargs="?", + default="host", + choices=["all", "host", "build"], + help=( + "What should be cleaned. 'build' for just the build platform, or " + "'host' for the host platform, or 'all' for both. Defaults to 'host'." + ), + ) + + for subcommand in ( + install_emscripten_cmd, + build, + configure_build, + make_libffi_cmd, + make_mpdec_cmd, + make_dependencies_cmd, + make_build, + configure_host, + make_host, + clean, + ): + subcommand.add_argument( + "--quiet", + action="store_true", + default="QUIET" in os.environ, + dest="quiet", + help=( + "Redirect output from subprocesses to a log file. " + "Can also be set with the QUIET environment variable." + ), + ) + add_cross_build_dir_option(subcommand) + subcommand.add_argument( + "--emsdk-cache", + action="store", + default=os.environ.get("EMSDK_CACHE"), + dest="emsdk_cache", + help=( + "Path to emsdk cache directory. If provided, validates that " + "the required emscripten version is installed. " + "Can also be set with the EMSDK_CACHE environment variable." + ), + ) + + for subcommand in 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_build, configure_host: + subcommand.add_argument( + "args", nargs="*", help="Extra arguments to pass to `configure`" + ) + + for subcommand in build, configure_host: + subcommand.add_argument( + "--host-runner", + action="store", + default=None, + dest="host_runner", + help="Command template for running the emscripten host " + "(default: use nvm to install the node version specified in config.toml)", + ) + + context = parser.parse_args() + context.emsdk_cache = getattr(context, "emsdk_cache", None) + context.cross_build_dir = getattr(context, "cross_build_dir", None) + context.check_up_to_date = getattr(context, "check_up_to_date", False) + + if context.emsdk_cache: + context.emsdk_cache = Path(context.emsdk_cache).absolute() + + context.build_paths = get_build_paths( + context.cross_build_dir, context.emsdk_cache + ) + + dispatch = { + "install-emscripten": install_emscripten, + "make-libffi": make_emscripten_libffi, + "make-mpdec": make_mpdec, + "make-dependencies": make_dependencies, + "configure-build-python": configure_build_python, + "make-build-python": make_build_python, + "configure-host": configure_emscripten_python, + "make-host": make_emscripten_python, + "build": build_target, + "run": run_emscripten_python, + "clean": clean_contents, + } + + if not context.subcommand: + # No command provided, display help and exit + print( + "Expected one of", + ", ".join(sorted(dispatch.keys())), + file=sys.stderr, + ) + parser.print_help(sys.stderr) + sys.exit(1) + dispatch[context.subcommand](context) + + +if __name__ == "__main__": + main() diff --git a/Tools/wasm/emscripten/browser_test/.gitignore b/Platforms/emscripten/browser_test/.gitignore similarity index 100% rename from Tools/wasm/emscripten/browser_test/.gitignore rename to Platforms/emscripten/browser_test/.gitignore diff --git a/Tools/wasm/emscripten/browser_test/index.spec.ts b/Platforms/emscripten/browser_test/index.spec.ts similarity index 100% rename from Tools/wasm/emscripten/browser_test/index.spec.ts rename to Platforms/emscripten/browser_test/index.spec.ts diff --git a/Platforms/emscripten/browser_test/package-lock.json b/Platforms/emscripten/browser_test/package-lock.json new file mode 100644 index 00000000000000..978aea0147bc28 --- /dev/null +++ b/Platforms/emscripten/browser_test/package-lock.json @@ -0,0 +1,1276 @@ +{ + "name": "browser_test", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "browser_test", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@playwright/test": "^1.54.1", + "@types/node": "^24.12.0", + "get-port-cli": "^3.0.0", + "http-server": "^14.1.1", + "playwright": "^1.54.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@playwright/test": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", + "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "license": "MIT" + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", + "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", + "license": "MIT", + "dependencies": { + "camelcase": "^6.3.0", + "map-obj": "^4.1.0", + "quick-lru": "^5.1.1", + "type-fest": "^1.2.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-6.1.2.tgz", + "integrity": "sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-port-cli": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-port-cli/-/get-port-cli-3.0.0.tgz", + "integrity": "sha512-060GMr81KapTzSobWNrQVAqHeUaFRZhPj/lNnzdCcfVodFN497wRgEamnTCNgldJuiR6TXxdtkFidcYQ/nSVDA==", + "license": "MIT", + "dependencies": { + "get-port": "^6.0.0", + "meow": "^10.1.1" + }, + "bin": { + "get-port": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-server": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", + "license": "MIT", + "dependencies": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + }, + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/meow": { + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", + "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.2", + "camelcase-keys": "^7.0.0", + "decamelize": "^5.0.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.2", + "read-pkg-up": "^8.0.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^1.2.2", + "yargs-parser": "^20.2.9" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/playwright": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/portfinder": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.37.tgz", + "integrity": "sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==", + "license": "MIT", + "dependencies": { + "async": "^3.2.6", + "debug": "^4.3.6" + }, + "engines": { + "node": ">= 10.12" + } + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", + "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", + "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0", + "read-pkg": "^6.0.0", + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/redent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", + "license": "MIT", + "dependencies": { + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "license": "CC0-1.0" + }, + "node_modules/strip-indent": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.1.1.tgz", + "integrity": "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/trim-newlines": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", + "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/Tools/wasm/emscripten/browser_test/package.json b/Platforms/emscripten/browser_test/package.json similarity index 84% rename from Tools/wasm/emscripten/browser_test/package.json rename to Platforms/emscripten/browser_test/package.json index 3320d4cccd0594..540c9b8034e7c7 100644 --- a/Tools/wasm/emscripten/browser_test/package.json +++ b/Platforms/emscripten/browser_test/package.json @@ -11,7 +11,8 @@ "description": "", "dependencies": { "@playwright/test": "^1.54.1", - "@types/node": "^24.1.0", + "@types/node": "^24.12.0", + "get-port-cli": "^3.0.0", "http-server": "^14.1.1", "playwright": "^1.54.1" } diff --git a/Platforms/emscripten/browser_test/playwright.config.ts b/Platforms/emscripten/browser_test/playwright.config.ts new file mode 100644 index 00000000000000..d170789a5970ec --- /dev/null +++ b/Platforms/emscripten/browser_test/playwright.config.ts @@ -0,0 +1,26 @@ +import { defineConfig, devices } from '@playwright/test'; +import { resolve } from "node:path"; + +const port = process.env.PORT ?? "8787"; +const crossBuildDir = resolve("../../../", process.env.CROSS_BUILD_DIR ?? "cross-build"); + +export default defineConfig({ + testDir: '.', + forbidOnly: true, + retries: 2, + reporter: process.env.CI ? 'dot' : 'html', + use: { + baseURL: `http://localhost:${port}`, + trace: 'on-first-retry', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], + webServer: { + command: `npx http-server ${crossBuildDir}/wasm32-emscripten/build/python/web_example_pyrepl_jspi/ -p ${port}`, + url: `http://localhost:${port}`, + }, +}); diff --git a/Platforms/emscripten/browser_test/run_test.sh b/Platforms/emscripten/browser_test/run_test.sh new file mode 100755 index 00000000000000..cc89b3a91607ed --- /dev/null +++ b/Platforms/emscripten/browser_test/run_test.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -euo pipefail +cd "$(dirname "$0")" +rm -f test_log.txt +echo "Installing node packages" | tee test_log.txt +npm ci >> test_log.txt 2>&1 +echo "Installing playwright browsers" | tee test_log.txt +npx playwright install 2>> test_log.txt +export PORT=$(npx get-port-cli) +echo "Running tests with webserver on port $PORT" | tee test_log.txt +CI=1 npx playwright test | tee test_log.txt diff --git a/Platforms/emscripten/browser_test/tsconfig.json b/Platforms/emscripten/browser_test/tsconfig.json new file mode 100644 index 00000000000000..29a2d833656b53 --- /dev/null +++ b/Platforms/emscripten/browser_test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "nodenext", + "lib": ["ES2020"], + "strict": true, + "esModuleInterop": true, + "types": ["node"] + }, + "include": ["**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/Tools/wasm/emscripten/config.site-wasm32-emscripten b/Platforms/emscripten/config.site-wasm32-emscripten similarity index 97% rename from Tools/wasm/emscripten/config.site-wasm32-emscripten rename to Platforms/emscripten/config.site-wasm32-emscripten index 9f98e3f3c3bb1f..f69dbb8e779a42 100644 --- a/Tools/wasm/emscripten/config.site-wasm32-emscripten +++ b/Platforms/emscripten/config.site-wasm32-emscripten @@ -1,6 +1,6 @@ # config.site override for cross compiling to wasm32-emscripten platform # -# CONFIG_SITE=Tools/wasm/emscripten/config.site-wasm32-emscripten \ +# CONFIG_SITE=Platforms/emscripten/config.site-wasm32-emscripten \ # emconfigure ./configure --host=wasm32-unknown-emscripten --build=... # # Written by Christian Heimes <christian@python.org> diff --git a/Platforms/emscripten/config.toml b/Platforms/emscripten/config.toml new file mode 100644 index 00000000000000..401e9396ddbb00 --- /dev/null +++ b/Platforms/emscripten/config.toml @@ -0,0 +1,26 @@ +# Any data that can vary between Python versions is to be kept in this file. +# This allows for blanket copying of the Emscripten build code between supported +# Python versions. +emscripten-version = "4.0.19" +node-version = "24" +test-args = [ + "-m", "test", + "-v", + "-uall", + "--rerun", + "--single-process", + "-W", +] +pythoninfo-args = [ + "-m", "test.pythoninfo", +] + +[dependencies.libffi] +url = "https://github.com/libffi/libffi/releases/download/v{version}/libffi-{version}.tar.gz" +version = "3.4.6" +shasum = "b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e" + +[dependencies.mpdec] +url = "https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-{version}.tar.gz" +version = "4.0.1" +shasum = "96d33abb4bb0070c7be0fed4246cd38416188325f820468214471938545b1ac8" diff --git a/Tools/wasm/emscripten/make_libffi.sh b/Platforms/emscripten/make_libffi.sh similarity index 100% rename from Tools/wasm/emscripten/make_libffi.sh rename to Platforms/emscripten/make_libffi.sh diff --git a/Tools/wasm/emscripten/node_entry.mjs b/Platforms/emscripten/node_entry.mjs similarity index 88% rename from Tools/wasm/emscripten/node_entry.mjs rename to Platforms/emscripten/node_entry.mjs index 166df40742b7fc..110aadc5de1014 100644 --- a/Tools/wasm/emscripten/node_entry.mjs +++ b/Platforms/emscripten/node_entry.mjs @@ -1,5 +1,6 @@ import EmscriptenModule from "./python.mjs"; import fs from "node:fs"; +import { initializeStreams } from "./streams.mjs"; if (process?.versions?.node) { const nodeVersion = Number(process.versions.node.split(".", 1)[0]); @@ -39,6 +40,9 @@ const settings = { Object.assign(Module.ENV, process.env); delete Module.ENV.PATH; }, + onRuntimeInitialized() { + initializeStreams(Module.FS); + }, // Ensure that sys.executable, sys._base_executable, etc point to python.sh // not to this file. To properly handle symlinks, python.sh needs to compute // its own path. @@ -49,10 +53,10 @@ const settings = { try { await EmscriptenModule(settings); -} catch(e) { +} catch (e) { // Show JavaScript exception and traceback console.warn(e); // Show Python exception and traceback - Module.__Py_DumpTraceback(2, Module._PyGILState_GetThisThreadState()); + Module.PyUnstable_DumpTraceback(2, Module._PyGILState_GetThisThreadState()); process.exit(1); } diff --git a/Platforms/emscripten/prepare_external_wasm.py b/Platforms/emscripten/prepare_external_wasm.py new file mode 100644 index 00000000000000..1b0a9de4b1fe8d --- /dev/null +++ b/Platforms/emscripten/prepare_external_wasm.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +import argparse +import sys +from pathlib import Path + +JS_TEMPLATE = """ +#include "emscripten.h" + +EM_JS(void, {function_name}, (void), {{ + return new WebAssembly.Module(hexStringToUTF8Array("{hex_string}")); +}} +function hexStringToUTF8Array(hex) {{ + const bytes = []; + for (let i = 0; i < hex.length; i += 2) {{ + bytes.push(parseInt(hex.substr(i, 2), 16)); + }} + return new Uint8Array(bytes); +}}); +""" + + +def prepare_wasm(input_file, output_file, function_name): + # Read the compiled WASM as binary and convert to hex + wasm_bytes = Path(input_file).read_bytes() + + hex_string = "".join(f"{byte:02x}" for byte in wasm_bytes) + + # Generate JavaScript module + js_content = JS_TEMPLATE.format( + function_name=function_name, hex_string=hex_string + ) + Path(output_file).write_text(js_content) + + print(f"Successfully compiled {input_file} and generated {output_file}") + return 0 + + +def main(): + parser = argparse.ArgumentParser( + description="Compile WebAssembly text files using wasm-as" + ) + parser.add_argument("input_file", help="Input .wat file to compile") + parser.add_argument("output_file", help="Output file name") + parser.add_argument("function_name", help="Name of the export function") + + args = parser.parse_args() + + return prepare_wasm(args.input_file, args.output_file, args.function_name) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Platforms/emscripten/streams.mjs b/Platforms/emscripten/streams.mjs new file mode 100644 index 00000000000000..1b121d48d4e76c --- /dev/null +++ b/Platforms/emscripten/streams.mjs @@ -0,0 +1,245 @@ +/** + * This is a pared down version of + * https://github.com/pyodide/pyodide/blob/main/src/js/streams.ts + * + * It replaces the standard streams devices that Emscripten provides with our + * own better ones. It fixes the following deficiencies: + * + * 1. The emscripten std streams always have isatty set to true. These set + * isatty to match the value for the stdin/stdout/stderr that node sees. + * 2. The emscripten std streams don't support the ttygetwinsize ioctl. If + * isatty() returns true, then these do, and it returns the actual window + * size as the OS reports it to Node. + * 3. The emscripten std streams introduce an extra layer of buffering which has + * to be flushed with fsync(). + * 4. The emscripten std streams are slow and complex because they go through a + * character-based handler layer. This is particularly awkward because both + * sides of this character based layer deal with buffers and so we need + * complex adaptors, buffering, etc on both sides. Removing this + * character-based middle layer makes everything better. + * https://github.com/emscripten-core/emscripten/blob/1aa7fb531f11e11e7ae49b75a24e1a8fe6fa4a7d/src/lib/libtty.js?plain=1#L104-L114 + * + * Ideally some version of this should go upstream to Emscripten since it is not + * in any way specific to Python. But I (Hood) haven't gotten around to it yet. + */ + +import * as tty from "node:tty"; +import * as fs from "node:fs"; + +let FS; +const DEVOPS = {}; +const DEVS = {}; + +function isErrnoError(e) { + return e && typeof e === "object" && "errno" in e; +} + +const waitBuffer = new Int32Array( + new WebAssembly.Memory({ shared: true, initial: 1, maximum: 1 }).buffer, +); +function syncSleep(timeout) { + try { + Atomics.wait(waitBuffer, 0, 0, timeout); + return true; + } catch (_) { + return false; + } +} + +/** + * Calls the callback and handle node EAGAIN errors. + */ +function handleEAGAIN(cb) { + while (true) { + try { + return cb(); + } catch (e) { + if (e && e.code === "EAGAIN") { + // Presumably this means we're in node and tried to read from/write to + // an O_NONBLOCK file descriptor. Synchronously sleep for 10ms then try + // again. In case for some reason we fail to sleep, propagate the error + // (it will turn into an EOFError). + if (syncSleep(10)) { + continue; + } + } + throw e; + } + } +} + +function readWriteHelper(stream, cb, method) { + let nbytes; + try { + nbytes = handleEAGAIN(cb); + } catch (e) { + if (e && e.code && Module.ERRNO_CODES[e.code]) { + throw new FS.ErrnoError(Module.ERRNO_CODES[e.code]); + } + if (isErrnoError(e)) { + // the handler set an errno, propagate it + throw e; + } + console.error("Error thrown in read:"); + console.error(e); + throw new FS.ErrnoError(Module.ERRNO_CODES.EIO); + } + if (nbytes === undefined) { + // Prevent an infinite loop caused by incorrect code that doesn't return a + // value. + // Maybe we should set nbytes = buffer.length here instead? + console.warn( + `${method} returned undefined; a correct implementation must return a number`, + ); + throw new FS.ErrnoError(Module.ERRNO_CODES.EIO); + } + if (nbytes !== 0) { + stream.node.timestamp = Date.now(); + } + return nbytes; +} + +function asUint8Array(arg) { + if (ArrayBuffer.isView(arg)) { + return new Uint8Array(arg.buffer, arg.byteOffset, arg.byteLength); + } else { + return new Uint8Array(arg); + } +} + +const prepareBuffer = (buffer, offset, length) => + asUint8Array(buffer).subarray(offset, offset + length); + +const TTY_OPS = { + ioctl_tiocgwinsz(tty) { + return tty.devops.ioctl_tiocgwinsz?.() ?? [24, 80]; + }, +}; + +const stream_ops = { + open: function (stream) { + const devops = DEVOPS[stream.node.rdev]; + if (!devops) { + throw new FS.ErrnoError(Module.ERRNO_CODES.ENODEV); + } + stream.devops = devops; + stream.tty = stream.devops.isatty ? { ops: TTY_OPS, devops } : undefined; + stream.seekable = false; + }, + close: function (stream) { + // flush any pending line data + stream.stream_ops.fsync(stream); + }, + fsync: function (stream) { + const ops = stream.devops; + if (ops.fsync) { + ops.fsync(); + } + }, + read: function (stream, buffer, offset, length, pos /* ignored */) { + buffer = prepareBuffer(buffer, offset, length); + return readWriteHelper(stream, () => stream.devops.read(buffer), "read"); + }, + write: function (stream, buffer, offset, length, pos /* ignored */) { + buffer = prepareBuffer(buffer, offset, length); + return readWriteHelper(stream, () => stream.devops.write(buffer), "write"); + }, +}; + +function nodeFsync(fd) { + try { + fs.fsyncSync(fd); + } catch (e) { + if (e?.code === "EINVAL") { + return; + } + // On Mac, calling fsync when not isatty returns ENOTSUP + // On Windows, stdin/stdout/stderr may be closed, returning EBADF or EPERM + if ( + e?.code === "ENOTSUP" || e?.code === "EBADF" || e?.code === "EPERM" + ) { + return; + } + + throw e; + } +} + +class NodeReader { + constructor(nodeStream) { + this.nodeStream = nodeStream; + this.isatty = tty.isatty(nodeStream.fd); + } + + read(buffer) { + try { + return fs.readSync(this.nodeStream.fd, buffer); + } catch (e) { + // Platform differences: on Windows, reading EOF throws an exception, + // but on other OSes, reading EOF returns 0. Uniformize behavior by + // catching the EOF exception and returning 0. + if (e.toString().includes("EOF")) { + return 0; + } + throw e; + } + } + + fsync() { + nodeFsync(this.nodeStream.fd); + } + + ioctl_tiocgwinsz() { + return [this.nodeStream.rows ?? 24, this.nodeStream.columns ?? 80]; + } +} + +class NodeWriter { + constructor(nodeStream) { + this.nodeStream = nodeStream; + this.isatty = tty.isatty(nodeStream.fd); + } + + write(buffer) { + return fs.writeSync(this.nodeStream.fd, buffer); + } + + fsync() { + nodeFsync(this.nodeStream.fd); + } + + ioctl_tiocgwinsz() { + return [this.nodeStream.rows ?? 24, this.nodeStream.columns ?? 80]; + } +} + +export function initializeStreams(fsarg) { + FS = fsarg; + const major = FS.createDevice.major++; + DEVS.stdin = FS.makedev(major, 0); + DEVS.stdout = FS.makedev(major, 1); + DEVS.stderr = FS.makedev(major, 2); + + FS.registerDevice(DEVS.stdin, stream_ops); + FS.registerDevice(DEVS.stdout, stream_ops); + FS.registerDevice(DEVS.stderr, stream_ops); + + FS.unlink("/dev/stdin"); + FS.unlink("/dev/stdout"); + FS.unlink("/dev/stderr"); + + FS.mkdev("/dev/stdin", DEVS.stdin); + FS.mkdev("/dev/stdout", DEVS.stdout); + FS.mkdev("/dev/stderr", DEVS.stderr); + + DEVOPS[DEVS.stdin] = new NodeReader(process.stdin); + DEVOPS[DEVS.stdout] = new NodeWriter(process.stdout); + DEVOPS[DEVS.stderr] = new NodeWriter(process.stderr); + + FS.closeStream(0 /* stdin */); + FS.closeStream(1 /* stdout */); + FS.closeStream(2 /* stderr */); + FS.open("/dev/stdin", 0 /* O_RDONLY */); + FS.open("/dev/stdout", 1 /* O_WRONLY */); + FS.open("/dev/stderr", 1 /* O_WRONLY */); +} diff --git a/Tools/wasm/emscripten/wasm_assets.py b/Platforms/emscripten/wasm_assets.py similarity index 95% rename from Tools/wasm/emscripten/wasm_assets.py rename to Platforms/emscripten/wasm_assets.py index b08e7ce1114a4a..8743e76e4449af 100755 --- a/Tools/wasm/emscripten/wasm_assets.py +++ b/Platforms/emscripten/wasm_assets.py @@ -15,10 +15,9 @@ import sys import sysconfig import zipfile -from typing import Dict # source directory -SRCDIR = pathlib.Path(__file__).parents[3].absolute() +SRCDIR = pathlib.Path(__file__).parents[2].absolute() SRCDIR_LIB = SRCDIR / "Lib" @@ -27,7 +26,9 @@ WASM_STDLIB_ZIP = ( WASM_LIB / f"python{sys.version_info.major}{sys.version_info.minor}.zip" ) -WASM_STDLIB = WASM_LIB / f"python{sys.version_info.major}.{sys.version_info.minor}" +WASM_STDLIB = ( + WASM_LIB / f"python{sys.version_info.major}.{sys.version_info.minor}" +) WASM_DYNLOAD = WASM_STDLIB / "lib-dynload" @@ -58,7 +59,6 @@ # socket.create_connection() raises an exception: # "BlockingIOError: [Errno 26] Operation in progress". OMIT_NETWORKING_FILES = ( - "email/", "ftplib.py", "http/", "imaplib.py", @@ -133,7 +133,7 @@ def filterfunc(filename: str) -> bool: pzf.writepy(entry, filterfunc=filterfunc) -def detect_extension_modules(args: argparse.Namespace) -> Dict[str, bool]: +def detect_extension_modules(args: argparse.Namespace) -> dict[str, bool]: modules = {} # disabled by Modules/Setup.local ? @@ -148,7 +148,7 @@ def detect_extension_modules(args: argparse.Namespace) -> Dict[str, bool]: # disabled by configure? with open(args.sysconfig_data) as f: data = f.read() - loc: Dict[str, Dict[str, str]] = {} + loc: dict[str, dict[str, str]] = {} exec(data, globals(), loc) for key, value in loc["build_time_vars"].items(): diff --git a/Tools/wasm/emscripten/web_example/index.html b/Platforms/emscripten/web_example/index.html similarity index 99% rename from Tools/wasm/emscripten/web_example/index.html rename to Platforms/emscripten/web_example/index.html index 9c89c9c0ed3bf5..3a207b92015451 100644 --- a/Tools/wasm/emscripten/web_example/index.html +++ b/Platforms/emscripten/web_example/index.html @@ -663,9 +663,9 @@ <h1>Simple REPL for Python WASM</h1> The simple REPL provides a limited Python experience in the browser. <a - href="https://github.com/python/cpython/blob/main/Tools/wasm/README.md" + href="https://github.com/python/cpython/blob/main/Platforms/emscripten/README.md" > - Tools/wasm/README.md + Platforms/emscripten/README.md </a> contains a list of known limitations and issues. Networking, subprocesses, and threading are not available. @@ -679,9 +679,9 @@ <h1>Simple REPL for Python WASM</h1> your browser instead of using <code>server.py</code> as described in <a - href="https://github.com/python/cpython/blob/main/Tools/wasm/README.md#the-web-example" + href="https://github.com/python/cpython/blob/main/Platforms/emscripten/README.md#the-web-example" > - Tools/wasm/README.md + Platforms/emscripten/README.md </a>. </p> <p> diff --git a/Tools/wasm/emscripten/web_example/python.worker.mjs b/Platforms/emscripten/web_example/python.worker.mjs similarity index 100% rename from Tools/wasm/emscripten/web_example/python.worker.mjs rename to Platforms/emscripten/web_example/python.worker.mjs diff --git a/Tools/wasm/emscripten/web_example/server.py b/Platforms/emscripten/web_example/server.py similarity index 82% rename from Tools/wasm/emscripten/web_example/server.py rename to Platforms/emscripten/web_example/server.py index 768e6f84e07798..f2e6ed56c6bcff 100755 --- a/Tools/wasm/emscripten/web_example/server.py +++ b/Platforms/emscripten/web_example/server.py @@ -6,10 +6,16 @@ description="Start a local webserver with a Python terminal." ) parser.add_argument( - "--port", type=int, default=8000, help="port for the http server to listen on" + "--port", + type=int, + default=8000, + help="port for the http server to listen on", ) parser.add_argument( - "--bind", type=str, default="127.0.0.1", help="Bind address (empty for all)" + "--bind", + type=str, + default="127.0.0.1", + help="Bind address (empty for all)", ) diff --git a/Tools/wasm/emscripten/web_example_pyrepl_jspi/index.html b/Platforms/emscripten/web_example_pyrepl_jspi/index.html similarity index 100% rename from Tools/wasm/emscripten/web_example_pyrepl_jspi/index.html rename to Platforms/emscripten/web_example_pyrepl_jspi/index.html diff --git a/Tools/wasm/emscripten/web_example_pyrepl_jspi/src.mjs b/Platforms/emscripten/web_example_pyrepl_jspi/src.mjs similarity index 98% rename from Tools/wasm/emscripten/web_example_pyrepl_jspi/src.mjs rename to Platforms/emscripten/web_example_pyrepl_jspi/src.mjs index 5642372c9d2472..38a622117c2a50 100644 --- a/Tools/wasm/emscripten/web_example_pyrepl_jspi/src.mjs +++ b/Platforms/emscripten/web_example_pyrepl_jspi/src.mjs @@ -189,6 +189,6 @@ try { // Show JavaScript exception and traceback console.warn(e); // Show Python exception and traceback - Module.__Py_DumpTraceback(2, Module._PyGILState_GetThisThreadState()); + Module.PyUnstable_DumpTraceback(2, Module._PyGILState_GetThisThreadState()); process.exit(1); } diff --git a/Programs/_freeze_module.c b/Programs/_freeze_module.c index 06d1ee016dc2a8..27a60171f3eca8 100644 --- a/Programs/_freeze_module.c +++ b/Programs/_freeze_module.c @@ -45,27 +45,40 @@ static const char header[] = static void runtime_init(void) { - PyConfig config; - PyConfig_InitIsolatedConfig(&config); - - config.site_import = 0; + PyInitConfig *config = PyInitConfig_Create(); + if (config == NULL) { + printf("memory allocation failed\n"); + exit(1); + } - PyStatus status; - status = PyConfig_SetString(&config, &config.program_name, - L"./_freeze_module"); - if (PyStatus_Exception(status)) { - PyConfig_Clear(&config); - Py_ExitStatusException(status); + if (PyInitConfig_SetInt(config, "site_import", 0) < 0) { + goto error; + } + if (PyInitConfig_SetStr(config, "program_name", "./_freeze_module") < 0) { + goto error; } /* Don't install importlib, since it could execute outdated bytecode. */ - config._install_importlib = 0; - config._init_main = 0; + if (PyInitConfig_SetInt(config, "_install_importlib", 0) < 0) { + goto error; + } + if (PyInitConfig_SetInt(config, "_init_main", 0) < 0) { + goto error; + } + + if (Py_InitializeFromInitConfig(config) < 0) { + goto error; + } + PyInitConfig_Free(config); + return; - status = Py_InitializeFromConfig(&config); - PyConfig_Clear(&config); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); +error: + { + const char *err_msg; + (void)PyInitConfig_GetError(config, &err_msg); + printf("Python init error: %s\n", err_msg); + PyInitConfig_Free(config); + exit(1); } } @@ -121,7 +134,7 @@ compile_and_marshal(const char *name, const char *text) return NULL; } - assert(Py_MARSHAL_VERSION >= 5); + assert(Py_MARSHAL_VERSION >= 6); PyObject *marshalled = PyMarshal_WriteObjectToString(code, Py_MARSHAL_VERSION); Py_CLEAR(code); if (marshalled == NULL) { diff --git a/Programs/_freeze_module.py b/Programs/_freeze_module.py index ba638eef6c4cd6..62274e4aa9ce11 100644 --- a/Programs/_freeze_module.py +++ b/Programs/_freeze_module.py @@ -23,7 +23,7 @@ def read_text(inpath: str) -> bytes: def compile_and_marshal(name: str, text: bytes) -> bytes: filename = f"<frozen {name}>" # exec == Py_file_input - code = compile(text, filename, "exec", optimize=0, dont_inherit=True) + code = compile(text, filename, "exec", optimize=0, dont_inherit=True, module=name) return marshal.dumps(code) diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 88936bbc699c30..74d0fe37ddaead 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -10,6 +10,7 @@ #include "pycore_runtime.h" // _PyRuntime #include "pycore_lock.h" // PyEvent #include "pycore_pythread.h" // PyThread_start_joinable_thread() +#include "pycore_pystate.h" // _PyInterpreterState_GuardCountdown #include "pycore_import.h" // _PyImport_FrozenBootstrap #include <inttypes.h> #include <stdio.h> @@ -22,6 +23,10 @@ extern void PySys_AddWarnOption(const wchar_t *s); extern void PySys_AddXOption(const wchar_t *s); extern void Py_SetPath(const wchar_t *path); +// These functions were removed from Python 3.15 API but are still exported +// for the stable ABI. We want to test them in this program. +extern void PySys_ResetWarnOptions(void); + int main_argc; char **main_argv; @@ -36,8 +41,13 @@ char **main_argv; #define PROGRAM "test_embed" /* Use path starting with "./" avoids a search along the PATH */ -#define PROGRAM_NAME L"./_testembed" -#define PROGRAM_NAME_UTF8 "./_testembed" +#ifdef __CYGWIN__ +# define PROGRAM_NAME L"./_testembed.exe" +# define PROGRAM_NAME_UTF8 "./_testembed.exe" +#else +# define PROGRAM_NAME L"./_testembed" +# define PROGRAM_NAME_UTF8 "./_testembed" +#endif #define INIT_LOOPS 4 @@ -162,6 +172,8 @@ static PyModuleDef embedded_ext = { static PyObject* PyInit_embedded_ext(void) { + // keep this as a single-phase initialization module; + // see test_create_module_from_initfunc return PyModule_Create(&embedded_ext); } @@ -336,8 +348,18 @@ static int test_pre_initialization_sys_options(void) size_t xoption_len = wcslen(static_xoption); wchar_t *dynamic_once_warnoption = \ (wchar_t *) calloc(warnoption_len+1, sizeof(wchar_t)); + if (dynamic_once_warnoption == NULL) { + error("out of memory allocating warnoption"); + return 1; + } wchar_t *dynamic_xoption = \ (wchar_t *) calloc(xoption_len+1, sizeof(wchar_t)); + if (dynamic_xoption == NULL) { + free(dynamic_once_warnoption); + error("out of memory allocating xoption"); + return 1; + } + wcsncpy(dynamic_once_warnoption, static_warnoption, warnoption_len+1); wcsncpy(dynamic_xoption, static_xoption, xoption_len+1); @@ -379,9 +401,9 @@ static int test_pre_initialization_sys_options(void) /* bpo-20891: Avoid race condition when initialising the GIL */ -static void bpo20891_thread(void *lockp) +static void bpo20891_thread(void *eventp) { - PyThread_type_lock lock = *((PyThread_type_lock*)lockp); + PyEvent *event = (PyEvent *)eventp; PyGILState_STATE state = PyGILState_Ensure(); if (!PyGILState_Check()) { @@ -390,8 +412,7 @@ static void bpo20891_thread(void *lockp) } PyGILState_Release(state); - - PyThread_release_lock(lock); + _PyEvent_Notify(event); } static int test_bpo20891(void) @@ -401,27 +422,16 @@ static int test_bpo20891(void) /* bpo-20891: Calling PyGILState_Ensure in a non-Python thread must not crash. */ - PyThread_type_lock lock = PyThread_allocate_lock(); - if (!lock) { - error("PyThread_allocate_lock failed!"); - return 1; - } - + PyEvent event = {0}; _testembed_initialize(); - unsigned long thrd = PyThread_start_new_thread(bpo20891_thread, &lock); + unsigned long thrd = PyThread_start_new_thread(bpo20891_thread, &event); if (thrd == PYTHREAD_INVALID_THREAD_ID) { error("PyThread_start_new_thread failed!"); return 1; } - PyThread_acquire_lock(lock, WAIT_LOCK); - - Py_BEGIN_ALLOW_THREADS - /* wait until the thread exit */ - PyThread_acquire_lock(lock, WAIT_LOCK); - Py_END_ALLOW_THREADS - PyThread_free_lock(lock); + PyEvent_Wait(&event); Py_Finalize(); @@ -635,6 +645,7 @@ static int test_init_from_config(void) putenv("PYTHONMALLOCSTATS=0"); config.malloc_stats = 1; + config.pymalloc_hugepages = 1; putenv("PYTHONPYCACHEPREFIX=env_pycache_prefix"); config_set_string(&config, &config.pycache_prefix, L"conf_pycache_prefix"); @@ -791,6 +802,7 @@ static void set_most_env_vars(void) putenv("PYTHONPROFILEIMPORTTIME=1"); putenv("PYTHONNODEBUGRANGES=1"); putenv("PYTHONMALLOCSTATS=1"); + putenv("PYTHON_PYMALLOC_HUGEPAGES=1"); putenv("PYTHONUTF8=1"); putenv("PYTHONVERBOSE=1"); putenv("PYTHONINSPECT=1"); @@ -1396,9 +1408,12 @@ static int test_audit_subinterpreter(void) PySys_AddAuditHook(_audit_subinterpreter_hook, NULL); _testembed_initialize(); - Py_NewInterpreter(); - Py_NewInterpreter(); - Py_NewInterpreter(); + PyThreadState *tstate = PyThreadState_Get(); + for (int i = 0; i < 3; ++i) + { + Py_EndInterpreter(Py_NewInterpreter()); + PyThreadState_Swap(tstate); + } Py_Finalize(); @@ -1501,44 +1516,6 @@ static int test_audit_run_stdin(void) return run_audit_run_test(Py_ARRAY_LENGTH(argv), argv, &test); } -static int test_init_read_set(void) -{ - PyStatus status; - PyConfig config; - PyConfig_InitPythonConfig(&config); - - config_set_string(&config, &config.program_name, L"./init_read_set"); - - status = PyConfig_Read(&config); - if (PyStatus_Exception(status)) { - goto fail; - } - - status = PyWideStringList_Insert(&config.module_search_paths, - 1, L"test_path_insert1"); - if (PyStatus_Exception(status)) { - goto fail; - } - - status = PyWideStringList_Append(&config.module_search_paths, - L"test_path_append"); - if (PyStatus_Exception(status)) { - goto fail; - } - - /* override executable computed by PyConfig_Read() */ - config_set_string(&config, &config.executable, L"my_executable"); - init_from_config_clear(&config); - - dump_config(); - Py_Finalize(); - return 0; - -fail: - PyConfig_Clear(&config); - Py_ExitStatusException(status); -} - static int test_init_sys_add(void) { @@ -1927,8 +1904,16 @@ static int test_initconfig_exit(void) } +int +extension_module_exec(PyObject *mod) +{ + return PyModule_AddStringConstant(mod, "exec_slot_ran", "yes"); +} + + static PyModuleDef_Slot extension_slots[] = { {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {Py_mod_exec, extension_module_exec}, {0, NULL} }; @@ -2012,6 +1997,35 @@ static int test_init_run_main(void) } +static int test_init_main(void) +{ + PyConfig config; + PyConfig_InitPythonConfig(&config); + + configure_init_main(&config); + config._init_main = 0; + init_from_config_clear(&config); + + assert(Py_IsInitialized() == 0); + + /* sys.stdout don't exist yet: it is created by _Py_InitializeMain() */ + int res = PyRun_SimpleString( + "import sys; " + "print('Run Python code before _Py_InitializeMain', " + "file=sys.stderr)"); + if (res < 0) { + exit(1); + } + + PyStatus status = _Py_InitializeMain(); + if (PyStatus_Exception(status)) { + Py_ExitStatusException(status); + } + + return Py_RunMain(); +} + + static int test_run_main(void) { PyConfig config; @@ -2086,15 +2100,20 @@ static int check_use_frozen_modules(const char *rawval) if (rawval == NULL) { wcscpy(optval, L"frozen_modules"); } - else if (swprintf(optval, 100, -#if defined(_MSC_VER) - L"frozen_modules=%S", -#else - L"frozen_modules=%s", -#endif - rawval) < 0) { - error("rawval is too long"); - return -1; + else { + wchar_t *val = Py_DecodeLocale(rawval, NULL); + if (val == NULL) { + error("unable to decode TESTFROZEN"); + return -1; + } + wcscpy(optval, L"frozen_modules="); + if ((wcslen(optval) + wcslen(val)) >= Py_ARRAY_LENGTH(optval)) { + error("TESTFROZEN is too long"); + PyMem_RawFree(val); + return -1; + } + wcscat(optval, val); + PyMem_RawFree(val); } PyConfig config; @@ -2192,6 +2211,52 @@ static int test_init_in_background_thread(void) return PyThread_join_thread(handle); } +/* gh-146302: Py_IsInitialized() must not return true during site import. */ +static int _initialized_during_site_import = -1; /* -1 = not observed */ + +static int hook_check_initialized_on_site_import( + const char *event, PyObject *args, void *userData) +{ + if (strcmp(event, "import") == 0 && args != NULL) { + PyObject *name = PyTuple_GetItem(args, 0); + if (name != NULL && PyUnicode_Check(name) + && PyUnicode_CompareWithASCIIString(name, "site") == 0 + && _initialized_during_site_import == -1) + { + _initialized_during_site_import = Py_IsInitialized(); + } + } + return 0; +} + +static int test_isinitialized_false_during_site_import(void) +{ + _initialized_during_site_import = -1; + + /* Register audit hook before initialization */ + PySys_AddAuditHook(hook_check_initialized_on_site_import, NULL); + + _testembed_initialize(); + + if (_initialized_during_site_import == -1) { + error("audit hook never observed site import"); + Py_Finalize(); + return 1; + } + if (_initialized_during_site_import != 0) { + error("Py_IsInitialized() was true during site import"); + Py_Finalize(); + return 1; + } + if (!Py_IsInitialized()) { + error("Py_IsInitialized() was false after Py_Initialize()"); + return 1; + } + + Py_Finalize(); + return 0; +} + #ifndef MS_WINDOWS #include "test_frozenmain.h" // M_test_frozenmain @@ -2246,6 +2311,277 @@ static int test_repeated_init_and_inittab(void) return 0; } +static PyObject* +create_module(PyObject* self, PyObject* spec) +{ + PyObject *name = PyObject_GetAttrString(spec, "name"); + if (!name) { + return NULL; + } + if (PyUnicode_EqualToUTF8(name, "my_test_extension")) { + Py_DECREF(name); + return PyImport_CreateModuleFromInitfunc(spec, init_my_test_extension); + } + if (PyUnicode_EqualToUTF8(name, "embedded_ext")) { + Py_DECREF(name); + return PyImport_CreateModuleFromInitfunc(spec, PyInit_embedded_ext); + } + PyErr_Format(PyExc_LookupError, "static module %R not found", name); + Py_DECREF(name); + return NULL; +} + +static PyObject* +exec_module(PyObject* self, PyObject* mod) +{ + if (PyModule_Exec(mod) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyMethodDef create_static_module_methods[] = { + {"create_module", create_module, METH_O, NULL}, + {"exec_module", exec_module, METH_O, NULL}, + {NULL} +}; + +static struct PyModuleDef create_static_module_def = { + PyModuleDef_HEAD_INIT, + .m_name = "create_static_module", + .m_size = 0, + .m_methods = create_static_module_methods, + .m_slots = extension_slots, +}; + +PyMODINIT_FUNC PyInit_create_static_module(void) { + return PyModuleDef_Init(&create_static_module_def); +} + +static int +test_create_module_from_initfunc(void) +{ + wchar_t* argv[] = { + PROGRAM_NAME, + L"-c", + // Multi-phase initialization + L"import my_test_extension;" + L"print(my_test_extension);" + L"print(f'{my_test_extension.executed=}');" + L"print(f'{my_test_extension.exec_slot_ran=}');" + // Single-phase initialization + L"import embedded_ext;" + L"print(embedded_ext);" + L"print(f'{embedded_ext.executed=}');" + }; + PyConfig config; + if (PyImport_AppendInittab("create_static_module", + &PyInit_create_static_module) != 0) { + fprintf(stderr, "PyImport_AppendInittab() failed\n"); + return 1; + } + PyConfig_InitPythonConfig(&config); + config.isolated = 1; + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + init_from_config_clear(&config); + int result = PyRun_SimpleString( + "import sys\n" + "from importlib.util import spec_from_loader\n" + "import create_static_module\n" + "class StaticExtensionImporter:\n" + " _ORIGIN = \"static-extension\"\n" + " @classmethod\n" + " def find_spec(cls, fullname, path, target=None):\n" + " if fullname in {'my_test_extension', 'embedded_ext'}:\n" + " return spec_from_loader(fullname, cls, origin=cls._ORIGIN)\n" + " return None\n" + " @staticmethod\n" + " def create_module(spec):\n" + " return create_static_module.create_module(spec)\n" + " @staticmethod\n" + " def exec_module(module):\n" + " create_static_module.exec_module(module)\n" + " module.executed = 'yes'\n" + "sys.meta_path.append(StaticExtensionImporter)\n" + ); + if (result < 0) { + fprintf(stderr, "PyRun_SimpleString() failed\n"); + return 1; + } + return Py_RunMain(); +} + +/// Multi-phase initialization package & submodule /// + +int +mp_pkg_exec(PyObject *mod) +{ + // make this a namespace package + // empty list = namespace package + if (PyModule_Add(mod, "__path__", PyList_New(0)) < 0) { + return -1; + } + if (PyModule_AddStringConstant(mod, "mp_pkg_exec_slot_ran", "yes") < 0) { + return -1; + } + return 0; +} + +static PyModuleDef_Slot mp_pkg_slots[] = { + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {Py_mod_exec, mp_pkg_exec}, + {0, NULL} +}; + +static struct PyModuleDef mp_pkg_def = { + PyModuleDef_HEAD_INIT, + .m_name = "mp_pkg", + .m_size = 0, + .m_slots = mp_pkg_slots, +}; + +PyMODINIT_FUNC +PyInit_mp_pkg(void) +{ + return PyModuleDef_Init(&mp_pkg_def); +} + +static PyObject * +submod_greet(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return PyUnicode_FromString("Hello from sub-module"); +} + +static PyMethodDef submod_methods[] = { + {"greet", submod_greet, METH_NOARGS, NULL}, + {NULL}, +}; + +int +mp_submod_exec(PyObject *mod) +{ + return PyModule_AddStringConstant(mod, "mp_submod_exec_slot_ran", "yes"); +} + +static PyModuleDef_Slot mp_submod_slots[] = { + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {Py_mod_exec, mp_submod_exec}, + {0, NULL} +}; + +static struct PyModuleDef mp_submod_def = { + PyModuleDef_HEAD_INIT, + .m_name = "mp_pkg.mp_submod", + .m_size = 0, + .m_methods = submod_methods, + .m_slots = mp_submod_slots, +}; + +PyMODINIT_FUNC +PyInit_mp_submod(void) +{ + return PyModuleDef_Init(&mp_submod_def); +} + +static int +test_inittab_submodule_multiphase(void) +{ + wchar_t* argv[] = { + PROGRAM_NAME, + L"-c", + L"import sys;" + L"import mp_pkg.mp_submod;" + L"print(mp_pkg.mp_submod);" + L"print(sys.modules['mp_pkg.mp_submod']);" + L"print(mp_pkg.mp_submod.greet());" + L"print(f'{mp_pkg.mp_submod.mp_submod_exec_slot_ran=}');" + L"print(f'{mp_pkg.mp_pkg_exec_slot_ran=}');" + }; + PyConfig config; + if (PyImport_AppendInittab("mp_pkg", + &PyInit_mp_pkg) != 0) { + fprintf(stderr, "PyImport_AppendInittab() failed\n"); + return 1; + } + if (PyImport_AppendInittab("mp_pkg.mp_submod", + &PyInit_mp_submod) != 0) { + fprintf(stderr, "PyImport_AppendInittab() failed\n"); + return 1; + } + PyConfig_InitPythonConfig(&config); + config.isolated = 1; + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + init_from_config_clear(&config); + return Py_RunMain(); +} + +/// Single-phase initialization package & submodule /// + +static struct PyModuleDef sp_pkg_def = { + PyModuleDef_HEAD_INIT, + .m_name = "sp_pkg", + .m_size = 0, +}; + +PyMODINIT_FUNC +PyInit_sp_pkg(void) +{ + PyObject *mod = PyModule_Create(&sp_pkg_def); + if (mod == NULL) { + return NULL; + } + // make this a namespace package + // empty list = namespace package + if (PyModule_Add(mod, "__path__", PyList_New(0)) < 0) { + Py_DECREF(mod); + return NULL; + } + return mod; +} + +static struct PyModuleDef sp_submod_def = { + PyModuleDef_HEAD_INIT, + .m_name = "sp_pkg.sp_submod", + .m_size = 0, + .m_methods = submod_methods, +}; + +PyMODINIT_FUNC +PyInit_sp_submod(void) +{ + return PyModule_Create(&sp_submod_def); +} + +static int +test_inittab_submodule_singlephase(void) +{ + wchar_t* argv[] = { + PROGRAM_NAME, + L"-c", + L"import sys;" + L"import sp_pkg.sp_submod;" + L"print(sp_pkg.sp_submod);" + L"print(sys.modules['sp_pkg.sp_submod']);" + L"print(sp_pkg.sp_submod.greet());" + }; + PyConfig config; + if (PyImport_AppendInittab("sp_pkg", + &PyInit_sp_pkg) != 0) { + fprintf(stderr, "PyImport_AppendInittab() failed\n"); + return 1; + } + if (PyImport_AppendInittab("sp_pkg.sp_submod", + &PyInit_sp_submod) != 0) { + fprintf(stderr, "PyImport_AppendInittab() failed\n"); + return 1; + } + PyConfig_InitPythonConfig(&config); + config.isolated = 1; + config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv); + init_from_config_clear(&config); + return Py_RunMain(); +} + static void wrap_allocator(PyMemAllocatorEx *allocator); static void unwrap_allocator(PyMemAllocatorEx *allocator); @@ -2340,6 +2676,214 @@ test_gilstate_after_finalization(void) return PyThread_detach_thread(handle); } + +const char *THREAD_CODE = \ + "import time\n" + "time.sleep(0.2)\n" + "def fib(n):\n" + " if n <= 1:\n" + " return n\n" + " else:\n" + " return fib(n - 1) + fib(n - 2)\n" + "fib(10)"; + +typedef struct { + void *argument; + int done; + PyEvent event; +} ThreadData; + +static void +do_tstate_ensure(void *arg) +{ + ThreadData *data = (ThreadData *)arg; + PyThreadStateToken *tokens[4]; + PyInterpreterGuard *guard = data->argument; + tokens[0] = PyThreadState_Ensure(guard); + tokens[1] = PyThreadState_Ensure(guard); + tokens[2] = PyThreadState_Ensure(guard); + PyGILState_STATE gstate = PyGILState_Ensure(); + tokens[3] = PyThreadState_Ensure(guard); + assert(tokens[0] != NULL); + assert(tokens[1] != NULL); + assert(tokens[2] != NULL); + assert(tokens[3] != NULL); + int res = PyRun_SimpleString(THREAD_CODE); + assert(res == 0); + PyThreadState_Release(tokens[3]); + PyGILState_Release(gstate); + PyThreadState_Release(tokens[2]); + PyThreadState_Release(tokens[1]); + PyThreadState_Release(tokens[0]); + _Py_atomic_store_int(&data->done, 1); + PyInterpreterGuard_Close(guard); +} + +static int +test_thread_state_ensure(void) +{ + _testembed_initialize(); + assert(_PyInterpreterState_GuardCountdown(_PyInterpreterState_GET()) == 0); + PyThread_handle_t handle; + PyThread_ident_t ident; + PyInterpreterGuard *guard = PyInterpreterGuard_FromCurrent(); + assert(guard != NULL); + ThreadData data = { guard }; + if (PyThread_start_joinable_thread(do_tstate_ensure, &data, + &ident, &handle) < 0) { + PyInterpreterGuard_Close(guard); + return -1; + } + // We hold an interpreter guard, so we don't + // have to worry about the interpreter shutting down before + // we finalize. + Py_Finalize(); + assert(_Py_atomic_load_int(&data.done) == 1); + PyThread_join_thread(handle); + return 0; +} + +static int +test_main_interpreter_view(void) +{ + PyInterpreterView *view = PyInterpreterView_FromMain(); + assert(view != NULL); + // These should fail -- the main interpreter is not available yet. + assert(PyInterpreterGuard_FromView(view) == NULL); + assert(PyThreadState_EnsureFromView(view) == NULL); + + _testembed_initialize(); + assert(_PyInterpreterState_GuardCountdown(_PyInterpreterState_GET()) == 0); + // Main interpreter is initialized and ready at this point. + + PyInterpreterGuard *guard = PyInterpreterGuard_FromView(view); + assert(guard != NULL); + PyInterpreterGuard_Close(guard); + + Py_Finalize(); + + // We shouldn't be able to get locks for the interpreter now + guard = PyInterpreterGuard_FromView(view); + assert(guard == NULL); + + PyInterpreterView_Close(view); + + return 0; +} + +static void +do_tstate_ensure_from_view(void *arg) +{ + ThreadData *data = (ThreadData *)arg; + PyInterpreterView *view = data->argument; + assert(view != NULL); + PyThreadStateToken *token = PyThreadState_EnsureFromView(view); + assert(token != NULL); + _PyEvent_Notify(&data->event); + int res = PyRun_SimpleString(THREAD_CODE); + assert(res == 0); + _Py_atomic_store_int(&data->done, 1); + PyThreadState_Release(token); +} + +static int +test_thread_state_ensure_from_view(void) +{ + _testembed_initialize(); + assert(_PyInterpreterState_GuardCountdown(_PyInterpreterState_GET()) == 0); + PyThread_handle_t handle; + PyThread_ident_t ident; + PyInterpreterView *view = PyInterpreterView_FromCurrent(); + assert(view != NULL); + + ThreadData data = { view }; + if (PyThread_start_joinable_thread(do_tstate_ensure_from_view, &data, + &ident, &handle) < 0) { + PyInterpreterView_Close(view); + return -1; + } + + PyEvent_Wait(&data.event); + Py_Finalize(); + assert(_Py_atomic_load_int(&data.done) == 1); + PyThread_join_thread(handle); + return 0; +} + +#define NUM_THREADS 4 + +static void +stress_func(void *arg) +{ + PyInterpreterGuard *guard = (PyInterpreterGuard *)arg; + + for (int i = 0; i < 1000; ++i) { + assert(guard != NULL); + PyThreadStateToken *token = PyThreadState_Ensure(guard); + assert(token != NULL); + + PyGILState_STATE gstate = PyGILState_Ensure(); + + PyInterpreterView *view = PyInterpreterView_FromCurrent(); + assert(view != NULL); + + PyThreadStateToken *token2 = PyThreadState_EnsureFromView(view); + assert(token2 != NULL); + PyThreadState_Release(token2); + + PyGILState_Release(gstate); + + PyThreadState_Release(token); + + PyInterpreterGuard_Close(guard); + + guard = PyInterpreterGuard_FromView(view); + PyInterpreterView_Close(view); + + if (guard == NULL) { + // The interpreter is shutting down. Bail out now. + return; + } + } + + PyInterpreterGuard_Close(guard); +} + +static int +test_concurrent_finalization_stress(void) +{ + for (int j = 0; j < 50; ++j) { + _testembed_initialize(); + assert(_PyInterpreterState_GuardCountdown(_PyInterpreterState_GET()) == 0); + PyThread_handle_t handles[NUM_THREADS]; + PyThread_ident_t idents[NUM_THREADS]; + PyInterpreterGuard *guards[NUM_THREADS]; + + for (int i = 0; i < NUM_THREADS; ++i) { + guards[i] = PyInterpreterGuard_FromCurrent(); + assert(guards[i] != NULL); + if (PyThread_start_joinable_thread(stress_func, guards[i], &idents[i], &handles[i]) < 0) { + for (int x = 0; x < i; ++x) { + PyInterpreterGuard_Close(guards[x]); + PyThread_detach_thread(handles[x]); + } + return -1; + } + } + + Py_Finalize(); + + for (int i = 0; i < NUM_THREADS; ++i) { + PyThread_join_thread(handles[i]); + } + } + + return 0; +} + +#undef NUM_THREADS + + /* ********************************************************* * List of test cases and the function that implements it. * @@ -2393,8 +2937,8 @@ static struct TestCase TestCases[] = { {"test_preinit_isolated2", test_preinit_isolated2}, {"test_preinit_parse_argv", test_preinit_parse_argv}, {"test_preinit_dont_parse_argv", test_preinit_dont_parse_argv}, - {"test_init_read_set", test_init_read_set}, {"test_init_run_main", test_init_run_main}, + {"test_init_main", test_init_main}, {"test_init_sys_add", test_init_sys_add}, {"test_init_setpath", test_init_setpath}, {"test_init_setpath_config", test_init_setpath_config}, @@ -2411,6 +2955,7 @@ static struct TestCase TestCases[] = { {"test_init_use_frozen_modules", test_init_use_frozen_modules}, {"test_init_main_interpreter_settings", test_init_main_interpreter_settings}, {"test_init_in_background_thread", test_init_in_background_thread}, + {"test_isinitialized_false_during_site_import", test_isinitialized_false_during_site_import}, // Audit {"test_open_code_hook", test_open_code_hook}, @@ -2430,6 +2975,13 @@ static struct TestCase TestCases[] = { #endif {"test_get_incomplete_frame", test_get_incomplete_frame}, {"test_gilstate_after_finalization", test_gilstate_after_finalization}, + {"test_create_module_from_initfunc", test_create_module_from_initfunc}, + {"test_inittab_submodule_multiphase", test_inittab_submodule_multiphase}, + {"test_inittab_submodule_singlephase", test_inittab_submodule_singlephase}, + {"test_thread_state_ensure", test_thread_state_ensure}, + {"test_main_interpreter_view", test_main_interpreter_view}, + {"test_thread_state_ensure_from_view", test_thread_state_ensure_from_view}, + {"test_concurrent_finalization_stress", test_concurrent_finalization_stress}, {NULL, NULL} }; diff --git a/Programs/freeze_test_frozenmain.py b/Programs/freeze_test_frozenmain.py index 848fc31b3d6f44..1a986bbac2afc7 100644 --- a/Programs/freeze_test_frozenmain.py +++ b/Programs/freeze_test_frozenmain.py @@ -24,7 +24,7 @@ def dump(fp, filename, name): with tokenize.open(filename) as source_fp: source = source_fp.read() - code = compile(source, code_filename, 'exec') + code = compile(source, code_filename, 'exec', module=name) data = marshal.dumps(code) writecode(fp, name, data) diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index dbeedb7ffe0ce6..1bcf98a7600851 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,39 +1,39 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { 227,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0, - 0,0,0,0,0,243,184,0,0,0,128,0,94,0,82,1, - 73,0,116,0,94,0,82,1,73,1,116,1,93,2,33,0, - 82,2,52,1,0,0,0,0,0,0,31,0,93,2,33,0, - 82,3,93,0,80,6,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,52,2,0,0,0,0,0,0, - 31,0,93,1,80,8,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,33,0,52,0,0,0,0,0, - 0,0,82,4,44,26,0,0,0,0,0,0,0,0,0,0, - 116,5,82,7,16,0,70,24,0,0,116,6,93,2,33,0, - 82,5,93,6,12,0,82,6,93,5,93,6,44,26,0,0, - 0,0,0,0,0,0,0,0,12,0,50,4,52,1,0,0, - 0,0,0,0,31,0,75,26,0,0,9,0,30,0,82,1, - 35,0,41,8,233,0,0,0,0,78,122,18,70,114,111,122, - 101,110,32,72,101,108,108,111,32,87,111,114,108,100,122,8, - 115,121,115,46,97,114,103,118,218,6,99,111,110,102,105,103, - 122,7,99,111,110,102,105,103,32,122,2,58,32,41,5,218, - 12,112,114,111,103,114,97,109,95,110,97,109,101,218,10,101, - 120,101,99,117,116,97,98,108,101,218,15,117,115,101,95,101, - 110,118,105,114,111,110,109,101,110,116,218,17,99,111,110,102, - 105,103,117,114,101,95,99,95,115,116,100,105,111,218,14,98, - 117,102,102,101,114,101,100,95,115,116,100,105,111,41,7,218, - 3,115,121,115,218,17,95,116,101,115,116,105,110,116,101,114, - 110,97,108,99,97,112,105,218,5,112,114,105,110,116,218,4, - 97,114,103,118,218,11,103,101,116,95,99,111,110,102,105,103, - 115,114,3,0,0,0,218,3,107,101,121,169,0,243,0,0, - 0,0,218,18,116,101,115,116,95,102,114,111,122,101,110,109, - 97,105,110,46,112,121,218,8,60,109,111,100,117,108,101,62, - 114,18,0,0,0,1,0,0,0,115,94,0,0,0,240,3, - 1,1,1,243,8,0,1,11,219,0,24,225,0,5,208,6, - 26,212,0,27,217,0,5,128,106,144,35,151,40,145,40,212, - 0,27,216,9,26,215,9,38,210,9,38,211,9,40,168,24, - 213,9,50,128,6,243,2,6,12,2,128,67,241,14,0,5, - 10,136,71,144,67,144,53,152,2,152,54,160,35,157,59,152, - 45,208,10,40,214,4,41,243,15,6,12,2,114,16,0,0, - 0, + 0,0,0,0,0,243,188,0,0,0,128,0,0,0,93,0, + 80,7,72,0,115,0,93,0,80,7,72,4,115,1,92,2, + 31,0,81,1,50,1,0,0,0,0,0,0,29,0,92,2, + 31,0,81,2,92,0,79,6,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,50,2,0,0,0,0, + 0,0,29,0,92,1,79,8,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,31,0,50,0,0,0, + 0,0,0,0,81,3,42,26,0,0,0,0,0,0,0,0, + 0,0,115,5,81,6,70,0,0,0,68,24,0,0,115,6, + 92,2,31,0,81,4,92,6,12,0,81,5,92,5,92,6, + 42,26,0,0,0,0,0,0,0,0,0,0,12,0,48,4, + 50,1,0,0,0,0,0,0,29,0,74,26,0,0,9,0, + 28,0,80,7,33,0,41,7,233,0,0,0,0,122,18,70, + 114,111,122,101,110,32,72,101,108,108,111,32,87,111,114,108, + 100,122,8,115,121,115,46,97,114,103,118,218,6,99,111,110, + 102,105,103,122,7,99,111,110,102,105,103,32,122,2,58,32, + 41,5,218,12,112,114,111,103,114,97,109,95,110,97,109,101, + 218,10,101,120,101,99,117,116,97,98,108,101,218,15,117,115, + 101,95,101,110,118,105,114,111,110,109,101,110,116,218,17,99, + 111,110,102,105,103,117,114,101,95,99,95,115,116,100,105,111, + 218,14,98,117,102,102,101,114,101,100,95,115,116,100,105,111, + 41,7,218,3,115,121,115,218,17,95,116,101,115,116,105,110, + 116,101,114,110,97,108,99,97,112,105,218,5,112,114,105,110, + 116,218,4,97,114,103,118,218,11,103,101,116,95,99,111,110, + 102,105,103,115,114,3,0,0,0,218,3,107,101,121,169,0, + 243,0,0,0,0,218,18,116,101,115,116,95,102,114,111,122, + 101,110,109,97,105,110,46,112,121,218,8,60,109,111,100,117, + 108,101,62,114,18,0,0,0,1,0,0,0,115,94,0,0, + 0,241,3,1,1,1,243,8,0,1,11,219,0,24,225,0, + 5,208,6,26,212,0,27,217,0,5,128,106,144,35,151,40, + 145,40,212,0,27,216,9,26,215,9,38,210,9,38,211,9, + 40,168,24,213,9,50,128,6,244,2,6,12,2,128,67,241, + 14,0,5,10,136,71,144,67,144,53,152,2,152,54,160,35, + 157,59,152,45,208,10,40,214,4,41,243,15,6,12,2,114, + 16,0,0,0, }; diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 660bc598a4862c..49b6bf1d12b6fa 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -178,6 +178,7 @@ void _PyAST_Fini(PyInterpreterState *interp) Py_CLEAR(state->__module__); Py_CLEAR(state->_attributes); Py_CLEAR(state->_fields); + Py_CLEAR(state->abstract_types); Py_CLEAR(state->alias_type); Py_CLEAR(state->annotation); Py_CLEAR(state->arg); @@ -222,6 +223,7 @@ void _PyAST_Fini(PyInterpreterState *interp) Py_CLEAR(state->id); Py_CLEAR(state->ifs); Py_CLEAR(state->is_async); + Py_CLEAR(state->is_lazy); Py_CLEAR(state->items); Py_CLEAR(state->iter); Py_CLEAR(state->key); @@ -327,6 +329,7 @@ static int init_identifiers(struct ast_state *state) if ((state->id = PyUnicode_InternFromString("id")) == NULL) return -1; if ((state->ifs = PyUnicode_InternFromString("ifs")) == NULL) return -1; if ((state->is_async = PyUnicode_InternFromString("is_async")) == NULL) return -1; + if ((state->is_lazy = PyUnicode_InternFromString("is_lazy")) == NULL) return -1; if ((state->items = PyUnicode_InternFromString("items")) == NULL) return -1; if ((state->iter = PyUnicode_InternFromString("iter")) == NULL) return -1; if ((state->key = PyUnicode_InternFromString("key")) == NULL) return -1; @@ -527,11 +530,13 @@ static const char * const Assert_fields[]={ }; static const char * const Import_fields[]={ "names", + "is_lazy", }; static const char * const ImportFrom_fields[]={ "module", "names", "level", + "is_lazy", }; static const char * const Global_fields[]={ "names", @@ -2254,6 +2259,21 @@ add_ast_annotations(struct ast_state *state) return 0; } } + { + PyObject *type = (PyObject *)&PyLong_Type; + type = _Py_union_type_or(type, Py_None); + cond = type != NULL; + if (!cond) { + Py_DECREF(Import_annotations); + return 0; + } + cond = PyDict_SetItemString(Import_annotations, "is_lazy", type) == 0; + Py_DECREF(type); + if (!cond) { + Py_DECREF(Import_annotations); + return 0; + } + } cond = PyObject_SetAttrString(state->Import_type, "_field_types", Import_annotations) == 0; if (!cond) { @@ -2315,6 +2335,22 @@ add_ast_annotations(struct ast_state *state) return 0; } } + { + PyObject *type = (PyObject *)&PyLong_Type; + type = _Py_union_type_or(type, Py_None); + cond = type != NULL; + if (!cond) { + Py_DECREF(ImportFrom_annotations); + return 0; + } + cond = PyDict_SetItemString(ImportFrom_annotations, "is_lazy", type) == + 0; + Py_DECREF(type); + if (!cond) { + Py_DECREF(ImportFrom_annotations); + return 0; + } + } cond = PyObject_SetAttrString(state->ImportFrom_type, "_field_types", ImportFrom_annotations) == 0; if (!cond) { @@ -2864,7 +2900,12 @@ add_ast_annotations(struct ast_state *state) } { PyObject *type = state->expr_type; - Py_INCREF(type); + type = _Py_union_type_or(type, Py_None); + cond = type != NULL; + if (!cond) { + Py_DECREF(DictComp_annotations); + return 0; + } cond = PyDict_SetItemString(DictComp_annotations, "value", type) == 0; Py_DECREF(type); if (!cond) { @@ -5157,6 +5198,70 @@ ast_clear(PyObject *op) return 0; } +/* + * Format the names in the set 'missing' into a natural language list, + * sorted in the order in which they appear in 'fields'. + * + * Similar to format_missing() from 'Python/ceval.c'. + * + * Parameters + * + * missing Set of missing field names to render. + * fields Sequence of AST node field names (self._fields). + */ +static PyObject * +format_missing(PyObject *missing, PyObject *fields) +{ + Py_ssize_t num_fields, num_total, num_left; + num_fields = PySequence_Size(fields); + if (num_fields == -1) { + return NULL; + } + num_total = num_left = PySet_GET_SIZE(missing); + PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); + if (writer == NULL) { + goto error; + } + // Iterate all AST node fields in order so that the missing positional + // arguments are rendered in the order in which __init__ expects them. + for (Py_ssize_t i = 0; i < num_fields; i++) { + PyObject *name = PySequence_GetItem(fields, i); + if (name == NULL) { + goto error; + } + int contains = PySet_Contains(missing, name); + if (contains == -1) { + Py_DECREF(name); + goto error; + } + else if (contains == 1) { + const char* fmt = NULL; + if (num_left == 1) { + fmt = "'%U'"; + } + else if (num_total == 2) { + fmt = "'%U' and "; + } + else if (num_left == 2) { + fmt = "'%U', and "; + } + else { + fmt = "'%U', "; + } + num_left--; + if (PyUnicodeWriter_Format(writer, fmt, name) < 0) { + Py_DECREF(name); + goto error; + } + } + Py_DECREF(name); + } + return PyUnicodeWriter_Finish(writer); +error: + PyUnicodeWriter_Discard(writer); + return NULL; +} + static int ast_type_init(PyObject *self, PyObject *args, PyObject *kw) { @@ -5165,6 +5270,19 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) return -1; } + int contains = PySet_Contains(state->abstract_types, (PyObject *)Py_TYPE(self)); + if (contains == -1) { + return -1; + } + else if (contains == 1) { + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, 1, + "Instantiating abstract AST node class %T is deprecated. " + "This will become an error in Python 3.20", self) < 0) { + return -1; + } + } + Py_ssize_t i, numfields = 0; int res = -1; PyObject *key, *value, *fields, *attributes = NULL, *remaining_fields = NULL; @@ -5226,8 +5344,8 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) } if (p == 0) { PyErr_Format(PyExc_TypeError, - "%.400s got multiple values for argument '%U'", - Py_TYPE(self)->tp_name, key); + "%T got multiple values for argument %R", + self, key); res = -1; goto cleanup; } @@ -5247,16 +5365,11 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) goto cleanup; } else if (contains == 0) { - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, 1, - "%.400s.__init__ got an unexpected keyword argument '%U'. " - "Support for arbitrary keyword arguments is deprecated " - "and will be removed in Python 3.15.", - Py_TYPE(self)->tp_name, key - ) < 0) { - res = -1; - goto cleanup; - } + PyErr_Format(PyExc_TypeError, + "%T.__init__ got an unexpected keyword argument %R", + self, key); + res = -1; + goto cleanup; } } res = PyObject_SetAttr(self, key, value); @@ -5266,7 +5379,7 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) } } Py_ssize_t size = PySet_Size(remaining_fields); - PyObject *field_types = NULL, *remaining_list = NULL; + PyObject *field_types = NULL, *remaining_list = NULL, *missing_names = NULL; if (size > 0) { if (PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self), &_Py_ID(_field_types), &field_types) < 0) { @@ -5283,6 +5396,10 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) if (!remaining_list) { goto set_remaining_cleanup; } + missing_names = PySet_New(NULL); + if (!missing_names) { + goto set_remaining_cleanup; + } for (Py_ssize_t i = 0; i < size; i++) { PyObject *name = PyList_GET_ITEM(remaining_list, i); PyObject *type = PyDict_GetItemWithError(field_types, name); @@ -5291,14 +5408,10 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) goto set_remaining_cleanup; } else { - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, 1, - "Field '%U' is missing from %.400s._field_types. " - "This will become an error in Python 3.15.", - name, Py_TYPE(self)->tp_name - ) < 0) { - goto set_remaining_cleanup; - } + PyErr_Format(PyExc_TypeError, + "Field %R is missing from %T._field_types", + name, self); + goto set_remaining_cleanup; } } else if (_PyUnion_Check(type)) { @@ -5326,16 +5439,25 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) } else { // simple field (e.g., identifier) - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, 1, - "%.400s.__init__ missing 1 required positional argument: '%U'. " - "This will become an error in Python 3.15.", - Py_TYPE(self)->tp_name, name - ) < 0) { + res = PySet_Add(missing_names, name); + if (res < 0) { goto set_remaining_cleanup; } } } + Py_ssize_t num_missing = PySet_GET_SIZE(missing_names); + if (num_missing > 0) { + PyObject *name_str = format_missing(missing_names, fields); + if (!name_str) { + goto set_remaining_cleanup; + } + PyErr_Format(PyExc_TypeError, + "%T.__init__ missing %d required positional argument%s: %U", + self, num_missing, num_missing == 1 ? "" : "s", name_str); + Py_DECREF(name_str); + goto set_remaining_cleanup; + } + Py_DECREF(missing_names); Py_DECREF(remaining_list); Py_DECREF(field_types); } @@ -5345,6 +5467,7 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw) Py_XDECREF(remaining_fields); return res; set_remaining_cleanup: + Py_XDECREF(missing_names); Py_XDECREF(remaining_list); Py_XDECREF(field_types); res = -1; @@ -5428,182 +5551,6 @@ ast_type_reduce(PyObject *self, PyObject *unused) return result; } -/* - * Perform the following validations: - * - * - All keyword arguments are known 'fields' or 'attributes'. - * - No field or attribute would be left unfilled after copy.replace(). - * - * On success, this returns 1. Otherwise, set a TypeError - * exception and returns -1 (no exception is set if some - * other internal errors occur). - * - * Parameters - * - * self The AST node instance. - * dict The AST node instance dictionary (self.__dict__). - * fields The list of fields (self._fields). - * attributes The list of attributes (self._attributes). - * kwargs Keyword arguments passed to ast_type_replace(). - * - * The 'dict', 'fields', 'attributes' and 'kwargs' arguments can be NULL. - * - * Note: this function can be removed in 3.15 since the verification - * will be done inside the constructor. - */ -static inline int -ast_type_replace_check(PyObject *self, - PyObject *dict, - PyObject *fields, - PyObject *attributes, - PyObject *kwargs) -{ - // While it is possible to make some fast paths that would avoid - // allocating objects on the stack, this would cost us readability. - // For instance, if 'fields' and 'attributes' are both empty, and - // 'kwargs' is not empty, we could raise a TypeError immediately. - PyObject *expecting = PySet_New(fields); - if (expecting == NULL) { - return -1; - } - if (attributes) { - if (_PySet_Update(expecting, attributes) < 0) { - Py_DECREF(expecting); - return -1; - } - } - // Any keyword argument that is neither a field nor attribute is rejected. - // We first need to check whether a keyword argument is accepted or not. - // If all keyword arguments are accepted, we compute the required fields - // and attributes. A field or attribute is not needed if: - // - // 1) it is given in 'kwargs', or - // 2) it already exists on 'self'. - if (kwargs) { - Py_ssize_t pos = 0; - PyObject *key, *value; - while (PyDict_Next(kwargs, &pos, &key, &value)) { - int rc = PySet_Discard(expecting, key); - if (rc < 0) { - Py_DECREF(expecting); - return -1; - } - if (rc == 0) { - PyErr_Format(PyExc_TypeError, - "%.400s.__replace__ got an unexpected keyword " - "argument '%U'.", Py_TYPE(self)->tp_name, key); - Py_DECREF(expecting); - return -1; - } - } - } - // check that the remaining fields or attributes would be filled - if (dict) { - Py_ssize_t pos = 0; - PyObject *key, *value; - while (PyDict_Next(dict, &pos, &key, &value)) { - // Mark fields or attributes that are found on the instance - // as non-mandatory. If they are not given in 'kwargs', they - // will be shallow-coied; otherwise, they would be replaced - // (not in this function). - if (PySet_Discard(expecting, key) < 0) { - Py_DECREF(expecting); - return -1; - } - } - if (attributes) { - // Some attributes may or may not be present at runtime. - // In particular, now that we checked whether 'kwargs' - // is correct or not, we allow any attribute to be missing. - // - // Note that fields must still be entirely determined when - // calling the constructor later. - PyObject *unused = PyObject_CallMethodOneArg(expecting, - &_Py_ID(difference_update), - attributes); - if (unused == NULL) { - Py_DECREF(expecting); - return -1; - } - Py_DECREF(unused); - } - } - - // Discard fields from 'expecting' that default to None - PyObject *field_types = NULL; - if (PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self), - &_Py_ID(_field_types), - &field_types) < 0) - { - Py_DECREF(expecting); - return -1; - } - if (field_types != NULL) { - Py_ssize_t pos = 0; - PyObject *field_name, *field_type; - while (PyDict_Next(field_types, &pos, &field_name, &field_type)) { - if (_PyUnion_Check(field_type)) { - // optional field - if (PySet_Discard(expecting, field_name) < 0) { - Py_DECREF(expecting); - Py_DECREF(field_types); - return -1; - } - } - } - Py_DECREF(field_types); - } - - // Now 'expecting' contains the fields or attributes - // that would not be filled inside ast_type_replace(). - Py_ssize_t m = PySet_GET_SIZE(expecting); - if (m > 0) { - PyObject *names = PyList_New(m); - if (names == NULL) { - Py_DECREF(expecting); - return -1; - } - Py_ssize_t i = 0, pos = 0; - PyObject *item; - Py_hash_t hash; - while (_PySet_NextEntry(expecting, &pos, &item, &hash)) { - PyObject *name = PyObject_Repr(item); - if (name == NULL) { - Py_DECREF(expecting); - Py_DECREF(names); - return -1; - } - // steal the reference 'name' - PyList_SET_ITEM(names, i++, name); - } - Py_DECREF(expecting); - if (PyList_Sort(names) < 0) { - Py_DECREF(names); - return -1; - } - PyObject *sep = PyUnicode_FromString(", "); - if (sep == NULL) { - Py_DECREF(names); - return -1; - } - PyObject *str_names = PyUnicode_Join(sep, names); - Py_DECREF(sep); - Py_DECREF(names); - if (str_names == NULL) { - return -1; - } - PyErr_Format(PyExc_TypeError, - "%.400s.__replace__ missing %ld keyword argument%s: %U.", - Py_TYPE(self)->tp_name, m, m == 1 ? "" : "s", str_names); - Py_DECREF(str_names); - return -1; - } - else { - Py_DECREF(expecting); - return 1; - } -} - /* * Python equivalent: * @@ -5693,9 +5640,6 @@ ast_type_replace(PyObject *self, PyObject *args, PyObject *kwargs) if (PyObject_GetOptionalAttr(self, state->__dict__, &dict) < 0) { goto cleanup; } - if (ast_type_replace_check(self, dict, fields, attributes, kwargs) < 0) { - goto cleanup; - } empty_tuple = PyTuple_New(0); if (empty_tuple == NULL) { goto cleanup; @@ -6082,7 +6026,9 @@ static PyObject* ast2obj_int(struct ast_state *Py_UNUSED(state), long b) /* Conversion Python -> AST */ -static int obj2ast_object(struct ast_state *Py_UNUSED(state), PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_object(struct ast_state *Py_UNUSED(state), PyObject* obj, + PyObject** out, + const char* Py_UNUSED(field), PyArena* arena) { if (obj == Py_None) obj = NULL; @@ -6099,7 +6045,9 @@ static int obj2ast_object(struct ast_state *Py_UNUSED(state), PyObject* obj, PyO return 0; } -static int obj2ast_constant(struct ast_state *Py_UNUSED(state), PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_constant(struct ast_state *Py_UNUSED(state), PyObject* obj, + PyObject** out, + const char* Py_UNUSED(field), PyArena* arena) { if (_PyArena_AddPyObject(arena, obj) < 0) { *out = NULL; @@ -6109,29 +6057,29 @@ static int obj2ast_constant(struct ast_state *Py_UNUSED(state), PyObject* obj, P return 0; } -static int obj2ast_identifier(struct ast_state *state, PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_identifier(struct ast_state *state, PyObject* obj, PyObject** out, const char* field, PyArena* arena) { if (!PyUnicode_CheckExact(obj) && obj != Py_None) { - PyErr_SetString(PyExc_TypeError, "AST identifier must be of type str"); + PyErr_Format(PyExc_TypeError, "field '%s' was expecting a string object", field); return -1; } - return obj2ast_object(state, obj, out, arena); + return obj2ast_object(state, obj, out, field, arena); } -static int obj2ast_string(struct ast_state *state, PyObject* obj, PyObject** out, PyArena* arena) +static int obj2ast_string(struct ast_state *state, PyObject* obj, PyObject** out, const char* field, PyArena* arena) { if (!PyUnicode_CheckExact(obj) && !PyBytes_CheckExact(obj)) { - PyErr_SetString(PyExc_TypeError, "AST string must be of type str"); + PyErr_Format(PyExc_TypeError, "field '%s' was expecting a string or bytes object", field); return -1; } - return obj2ast_object(state, obj, out, arena); + return obj2ast_object(state, obj, out, field, arena); } -static int obj2ast_int(struct ast_state* Py_UNUSED(state), PyObject* obj, int* out, PyArena* arena) +static int obj2ast_int(struct ast_state* Py_UNUSED(state), PyObject* obj, int* out, const char* field, PyArena* arena) { int i; if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_ValueError, "invalid integer value: %R", obj); + PyErr_Format(PyExc_ValueError, "field \"%s\" got an invalid integer value: %R", field, obj); return -1; } @@ -6170,6 +6118,13 @@ init_types(void *arg) if (!state->AST_type) { return -1; } + state->abstract_types = PySet_New(NULL); + if (!state->abstract_types) { + return -1; + } + if (PySet_Add(state->abstract_types, state->AST_type) < 0) { + return -1; + } if (add_ast_fields(state) < 0) { return -1; } @@ -6180,6 +6135,7 @@ init_types(void *arg) " | FunctionType(expr* argtypes, expr returns)"); if (!state->mod_type) return -1; if (add_attributes(state, state->mod_type, NULL, 0) < 0) return -1; + if (PySet_Add(state->abstract_types, state->mod_type) < 0) return -1; state->Module_type = make_type(state, "Module", state->mod_type, Module_fields, 2, "Module(stmt* body, type_ignore* type_ignores)"); @@ -6218,8 +6174,8 @@ init_types(void *arg) " | Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n" " | TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)\n" " | Assert(expr test, expr? msg)\n" - " | Import(alias* names)\n" - " | ImportFrom(identifier? module, alias* names, int? level)\n" + " | Import(alias* names, int? is_lazy)\n" + " | ImportFrom(identifier? module, alias* names, int? level, int? is_lazy)\n" " | Global(identifier* names)\n" " | Nonlocal(identifier* names)\n" " | Expr(expr value)\n" @@ -6229,6 +6185,7 @@ init_types(void *arg) if (!state->stmt_type) return -1; if (add_attributes(state, state->stmt_type, stmt_attributes, 4) < 0) return -1; + if (PySet_Add(state->abstract_types, state->stmt_type) < 0) return -1; if (PyObject_SetAttr(state->stmt_type, state->end_lineno, Py_None) == -1) return -1; if (PyObject_SetAttr(state->stmt_type, state->end_col_offset, Py_None) == @@ -6348,17 +6305,21 @@ init_types(void *arg) if (PyObject_SetAttr(state->Assert_type, state->msg, Py_None) == -1) return -1; state->Import_type = make_type(state, "Import", state->stmt_type, - Import_fields, 1, - "Import(alias* names)"); + Import_fields, 2, + "Import(alias* names, int? is_lazy)"); if (!state->Import_type) return -1; + if (PyObject_SetAttr(state->Import_type, state->is_lazy, Py_None) == -1) + return -1; state->ImportFrom_type = make_type(state, "ImportFrom", state->stmt_type, - ImportFrom_fields, 3, - "ImportFrom(identifier? module, alias* names, int? level)"); + ImportFrom_fields, 4, + "ImportFrom(identifier? module, alias* names, int? level, int? is_lazy)"); if (!state->ImportFrom_type) return -1; if (PyObject_SetAttr(state->ImportFrom_type, state->module, Py_None) == -1) return -1; if (PyObject_SetAttr(state->ImportFrom_type, state->level, Py_None) == -1) return -1; + if (PyObject_SetAttr(state->ImportFrom_type, state->is_lazy, Py_None) == -1) + return -1; state->Global_type = make_type(state, "Global", state->stmt_type, Global_fields, 1, "Global(identifier* names)"); @@ -6392,7 +6353,7 @@ init_types(void *arg) " | Set(expr* elts)\n" " | ListComp(expr elt, comprehension* generators)\n" " | SetComp(expr elt, comprehension* generators)\n" - " | DictComp(expr key, expr value, comprehension* generators)\n" + " | DictComp(expr key, expr? value, comprehension* generators)\n" " | GeneratorExp(expr elt, comprehension* generators)\n" " | Await(expr value)\n" " | Yield(expr? value)\n" @@ -6414,6 +6375,7 @@ init_types(void *arg) if (!state->expr_type) return -1; if (add_attributes(state, state->expr_type, expr_attributes, 4) < 0) return -1; + if (PySet_Add(state->abstract_types, state->expr_type) < 0) return -1; if (PyObject_SetAttr(state->expr_type, state->end_lineno, Py_None) == -1) return -1; if (PyObject_SetAttr(state->expr_type, state->end_col_offset, Py_None) == @@ -6460,8 +6422,10 @@ init_types(void *arg) if (!state->SetComp_type) return -1; state->DictComp_type = make_type(state, "DictComp", state->expr_type, DictComp_fields, 3, - "DictComp(expr key, expr value, comprehension* generators)"); + "DictComp(expr key, expr? value, comprehension* generators)"); if (!state->DictComp_type) return -1; + if (PyObject_SetAttr(state->DictComp_type, state->value, Py_None) == -1) + return -1; state->GeneratorExp_type = make_type(state, "GeneratorExp", state->expr_type, GeneratorExp_fields, 2, @@ -6558,6 +6522,8 @@ init_types(void *arg) "expr_context = Load | Store | Del"); if (!state->expr_context_type) return -1; if (add_attributes(state, state->expr_context_type, NULL, 0) < 0) return -1; + if (PySet_Add(state->abstract_types, state->expr_context_type) < 0) return + -1; state->Load_type = make_type(state, "Load", state->expr_context_type, NULL, 0, "Load"); @@ -6582,6 +6548,7 @@ init_types(void *arg) "boolop = And | Or"); if (!state->boolop_type) return -1; if (add_attributes(state, state->boolop_type, NULL, 0) < 0) return -1; + if (PySet_Add(state->abstract_types, state->boolop_type) < 0) return -1; state->And_type = make_type(state, "And", state->boolop_type, NULL, 0, "And"); if (!state->And_type) return -1; @@ -6599,6 +6566,7 @@ init_types(void *arg) "operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift | RShift | BitOr | BitXor | BitAnd | FloorDiv"); if (!state->operator_type) return -1; if (add_attributes(state, state->operator_type, NULL, 0) < 0) return -1; + if (PySet_Add(state->abstract_types, state->operator_type) < 0) return -1; state->Add_type = make_type(state, "Add", state->operator_type, NULL, 0, "Add"); if (!state->Add_type) return -1; @@ -6693,6 +6661,7 @@ init_types(void *arg) "unaryop = Invert | Not | UAdd | USub"); if (!state->unaryop_type) return -1; if (add_attributes(state, state->unaryop_type, NULL, 0) < 0) return -1; + if (PySet_Add(state->abstract_types, state->unaryop_type) < 0) return -1; state->Invert_type = make_type(state, "Invert", state->unaryop_type, NULL, 0, "Invert"); @@ -6723,6 +6692,7 @@ init_types(void *arg) "cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn"); if (!state->cmpop_type) return -1; if (add_attributes(state, state->cmpop_type, NULL, 0) < 0) return -1; + if (PySet_Add(state->abstract_types, state->cmpop_type) < 0) return -1; state->Eq_type = make_type(state, "Eq", state->cmpop_type, NULL, 0, "Eq"); if (!state->Eq_type) return -1; @@ -6796,6 +6766,8 @@ init_types(void *arg) if (!state->excepthandler_type) return -1; if (add_attributes(state, state->excepthandler_type, excepthandler_attributes, 4) < 0) return -1; + if (PySet_Add(state->abstract_types, state->excepthandler_type) < 0) return + -1; if (PyObject_SetAttr(state->excepthandler_type, state->end_lineno, Py_None) == -1) return -1; @@ -6813,7 +6785,7 @@ init_types(void *arg) return -1; state->arguments_type = make_type(state, "arguments", state->AST_type, arguments_fields, 7, - "arguments(arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults, arg? kwarg, expr* defaults)"); + "arguments(arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs, expr?* kw_defaults, arg? kwarg, expr* defaults)"); if (!state->arguments_type) return -1; if (add_attributes(state, state->arguments_type, NULL, 0) < 0) return -1; if (PyObject_SetAttr(state->arguments_type, state->vararg, Py_None) == -1) @@ -6886,6 +6858,7 @@ init_types(void *arg) if (!state->pattern_type) return -1; if (add_attributes(state, state->pattern_type, pattern_attributes, 4) < 0) return -1; + if (PySet_Add(state->abstract_types, state->pattern_type) < 0) return -1; state->MatchValue_type = make_type(state, "MatchValue", state->pattern_type, MatchValue_fields, 1, @@ -6936,6 +6909,8 @@ init_types(void *arg) "type_ignore = TypeIgnore(int lineno, string tag)"); if (!state->type_ignore_type) return -1; if (add_attributes(state, state->type_ignore_type, NULL, 0) < 0) return -1; + if (PySet_Add(state->abstract_types, state->type_ignore_type) < 0) return + -1; state->TypeIgnore_type = make_type(state, "TypeIgnore", state->type_ignore_type, TypeIgnore_fields, 2, @@ -6949,6 +6924,7 @@ init_types(void *arg) if (!state->type_param_type) return -1; if (add_attributes(state, state->type_param_type, type_param_attributes, 4) < 0) return -1; + if (PySet_Add(state->abstract_types, state->type_param_type) < 0) return -1; state->TypeVar_type = make_type(state, "TypeVar", state->type_param_type, TypeVar_fields, 3, "TypeVar(identifier name, expr? bound, expr? default_value)"); @@ -6982,43 +6958,52 @@ init_types(void *arg) } static int obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_expr_context(struct ast_state *state, PyObject* obj, - expr_context_ty* out, PyArena* arena); + expr_context_ty* out, const char* field, + PyArena* arena); static int obj2ast_boolop(struct ast_state *state, PyObject* obj, boolop_ty* - out, PyArena* arena); + out, const char* field, PyArena* arena); static int obj2ast_operator(struct ast_state *state, PyObject* obj, - operator_ty* out, PyArena* arena); + operator_ty* out, const char* field, PyArena* + arena); static int obj2ast_unaryop(struct ast_state *state, PyObject* obj, unaryop_ty* - out, PyArena* arena); + out, const char* field, PyArena* arena); static int obj2ast_cmpop(struct ast_state *state, PyObject* obj, cmpop_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_comprehension(struct ast_state *state, PyObject* obj, - comprehension_ty* out, PyArena* arena); + comprehension_ty* out, const char* field, + PyArena* arena); static int obj2ast_excepthandler(struct ast_state *state, PyObject* obj, - excepthandler_ty* out, PyArena* arena); + excepthandler_ty* out, const char* field, + PyArena* arena); static int obj2ast_arguments(struct ast_state *state, PyObject* obj, - arguments_ty* out, PyArena* arena); + arguments_ty* out, const char* field, PyArena* + arena); static int obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* - out, PyArena* arena); + out, const char* field, PyArena* arena); static int obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, - PyArena* arena); + const char* field, PyArena* arena); static int obj2ast_withitem(struct ast_state *state, PyObject* obj, - withitem_ty* out, PyArena* arena); + withitem_ty* out, const char* field, PyArena* + arena); static int obj2ast_match_case(struct ast_state *state, PyObject* obj, - match_case_ty* out, PyArena* arena); + match_case_ty* out, const char* field, PyArena* + arena); static int obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* - out, PyArena* arena); + out, const char* field, PyArena* arena); static int obj2ast_type_ignore(struct ast_state *state, PyObject* obj, - type_ignore_ty* out, PyArena* arena); + type_ignore_ty* out, const char* field, PyArena* + arena); static int obj2ast_type_param(struct ast_state *state, PyObject* obj, - type_param_ty* out, PyArena* arena); + type_param_ty* out, const char* field, PyArena* + arena); mod_ty _PyAST_Module(asdl_stmt_seq * body, asdl_type_ignore_seq * type_ignores, @@ -7598,8 +7583,8 @@ _PyAST_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset, int } stmt_ty -_PyAST_Import(asdl_alias_seq * names, int lineno, int col_offset, int - end_lineno, int end_col_offset, PyArena *arena) +_PyAST_Import(asdl_alias_seq * names, int is_lazy, int lineno, int col_offset, + int end_lineno, int end_col_offset, PyArena *arena) { stmt_ty p; p = (stmt_ty)_PyArena_Malloc(arena, sizeof(*p)); @@ -7607,6 +7592,7 @@ _PyAST_Import(asdl_alias_seq * names, int lineno, int col_offset, int return NULL; p->kind = Import_kind; p->v.Import.names = names; + p->v.Import.is_lazy = is_lazy; p->lineno = lineno; p->col_offset = col_offset; p->end_lineno = end_lineno; @@ -7616,8 +7602,8 @@ _PyAST_Import(asdl_alias_seq * names, int lineno, int col_offset, int stmt_ty _PyAST_ImportFrom(identifier module, asdl_alias_seq * names, int level, int - lineno, int col_offset, int end_lineno, int end_col_offset, - PyArena *arena) + is_lazy, int lineno, int col_offset, int end_lineno, int + end_col_offset, PyArena *arena) { stmt_ty p; p = (stmt_ty)_PyArena_Malloc(arena, sizeof(*p)); @@ -7627,6 +7613,7 @@ _PyAST_ImportFrom(identifier module, asdl_alias_seq * names, int level, int p->v.ImportFrom.module = module; p->v.ImportFrom.names = names; p->v.ImportFrom.level = level; + p->v.ImportFrom.is_lazy = is_lazy; p->lineno = lineno; p->col_offset = col_offset; p->end_lineno = end_lineno; @@ -8007,11 +7994,6 @@ _PyAST_DictComp(expr_ty key, expr_ty value, asdl_comprehension_seq * "field 'key' is required for DictComp"); return NULL; } - if (!value) { - PyErr_SetString(PyExc_ValueError, - "field 'value' is required for DictComp"); - return NULL; - } p = (expr_ty)_PyArena_Malloc(arena, sizeof(*p)); if (!p) return NULL; @@ -9465,6 +9447,11 @@ ast2obj_stmt(struct ast_state *state, void* _o) if (PyObject_SetAttr(result, state->names, value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_int(state, o->v.Import.is_lazy); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->is_lazy, value) == -1) + goto failed; + Py_DECREF(value); break; case ImportFrom_kind: tp = (PyTypeObject *)state->ImportFrom_type; @@ -9486,6 +9473,11 @@ ast2obj_stmt(struct ast_state *state, void* _o) if (PyObject_SetAttr(result, state->level, value) == -1) goto failed; Py_DECREF(value); + value = ast2obj_int(state, o->v.ImportFrom.is_lazy); + if (!value) goto failed; + if (PyObject_SetAttr(result, state->is_lazy, value) == -1) + goto failed; + Py_DECREF(value); break; case Global_kind: tp = (PyTypeObject *)state->Global_type; @@ -10876,7 +10868,8 @@ ast2obj_type_param(struct ast_state *state, void* _o) int -obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) +obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, const char* + field, PyArena* arena) { int isinstance; @@ -10887,6 +10880,15 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) *out = NULL; return 0; } + tp = state->mod_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'mod', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } tp = state->Module_type; isinstance = PyObject_IsInstance(obj, tp); if (isinstance == -1) { @@ -10922,7 +10924,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'Module' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -10960,7 +10962,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'Module' node")) { goto failed; } - res = obj2ast_type_ignore(state, tmp2, &val, arena); + res = obj2ast_type_ignore(state, tmp2, &val, "type_ignores", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11010,7 +11012,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'Interactive' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11046,7 +11048,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'Expression' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &body, arena); + res = obj2ast_expr(state, tmp, &body, "body", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11090,7 +11092,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'FunctionType' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "argtypes", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11114,7 +11116,7 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'FunctionType' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &returns, arena); + res = obj2ast_expr(state, tmp, &returns, "returns", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11131,8 +11133,8 @@ obj2ast_mod(struct ast_state *state, PyObject* obj, mod_ty* out, PyArena* arena) } int -obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* - arena) +obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, const char* + field, PyArena* arena) { int isinstance; @@ -11147,6 +11149,15 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* *out = NULL; return 0; } + tp = state->stmt_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'stmt', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } if (PyObject_GetOptionalAttr(obj, state->lineno, &tmp) < 0) { return -1; } @@ -11159,7 +11170,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'stmt' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11176,7 +11187,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'stmt' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11193,7 +11204,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'stmt' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11210,7 +11221,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'stmt' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11241,7 +11252,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11258,7 +11269,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_arguments(state, tmp, &args, arena); + res = obj2ast_arguments(state, tmp, &args, "args", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11289,7 +11300,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11327,7 +11338,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "decorator_list", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11351,7 +11362,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &returns, arena); + res = obj2ast_expr(state, tmp, &returns, "returns", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11368,7 +11379,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11399,7 +11411,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FunctionDef' node")) { goto failed; } - res = obj2ast_type_param(state, tmp2, &val, arena); + res = obj2ast_type_param(state, tmp2, &val, "type_params", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11444,7 +11456,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11461,7 +11473,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_arguments(state, tmp, &args, arena); + res = obj2ast_arguments(state, tmp, &args, "args", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11492,7 +11504,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11530,7 +11542,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "decorator_list", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11554,7 +11566,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &returns, arena); + res = obj2ast_expr(state, tmp, &returns, "returns", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11571,7 +11583,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11602,7 +11615,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFunctionDef' node")) { goto failed; } - res = obj2ast_type_param(state, tmp2, &val, arena); + res = obj2ast_type_param(state, tmp2, &val, "type_params", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11646,7 +11659,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11677,7 +11690,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "bases", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11715,7 +11728,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_keyword(state, tmp2, &val, arena); + res = obj2ast_keyword(state, tmp2, &val, "keywords", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11753,7 +11766,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11791,7 +11804,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "decorator_list", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11829,7 +11842,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ClassDef' node")) { goto failed; } - res = obj2ast_type_param(state, tmp2, &val, arena); + res = obj2ast_type_param(state, tmp2, &val, "type_params", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11867,7 +11880,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Return' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -11911,7 +11924,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Delete' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "targets", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11964,7 +11977,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Assign' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "targets", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -11988,7 +12001,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Assign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12005,7 +12018,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Assign' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12037,7 +12051,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TypeAlias' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &name, arena); + res = obj2ast_expr(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12068,7 +12082,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TypeAlias' node")) { goto failed; } - res = obj2ast_type_param(state, tmp2, &val, arena); + res = obj2ast_type_param(state, tmp2, &val, "type_params", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12092,7 +12106,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TypeAlias' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12124,7 +12138,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AugAssign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12141,7 +12155,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AugAssign' node")) { goto failed; } - res = obj2ast_operator(state, tmp, &op, arena); + res = obj2ast_operator(state, tmp, &op, "op", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12158,7 +12172,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AugAssign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12191,7 +12205,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AnnAssign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12208,7 +12222,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AnnAssign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &annotation, arena); + res = obj2ast_expr(state, tmp, &annotation, "annotation", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12225,7 +12239,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AnnAssign' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12242,7 +12256,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AnnAssign' node")) { goto failed; } - res = obj2ast_int(state, tmp, &simple, arena); + res = obj2ast_int(state, tmp, &simple, "simple", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12276,7 +12290,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'For' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12293,7 +12307,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'For' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &iter, arena); + res = obj2ast_expr(state, tmp, &iter, "iter", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12324,7 +12338,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'For' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12362,7 +12376,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'For' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12386,7 +12400,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'For' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12420,7 +12435,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFor' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12437,7 +12452,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFor' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &iter, arena); + res = obj2ast_expr(state, tmp, &iter, "iter", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12468,7 +12483,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFor' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12506,7 +12521,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFor' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12530,7 +12545,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncFor' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12563,7 +12579,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'While' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &test, arena); + res = obj2ast_expr(state, tmp, &test, "test", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12594,7 +12610,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'While' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12632,7 +12648,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'While' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12671,7 +12687,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'If' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &test, arena); + res = obj2ast_expr(state, tmp, &test, "test", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12702,7 +12718,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'If' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12740,7 +12756,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'If' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12793,7 +12809,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'With' node")) { goto failed; } - res = obj2ast_withitem(state, tmp2, &val, arena); + res = obj2ast_withitem(state, tmp2, &val, "items", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12831,7 +12847,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'With' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12855,7 +12871,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'With' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12901,7 +12918,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncWith' node")) { goto failed; } - res = obj2ast_withitem(state, tmp2, &val, arena); + res = obj2ast_withitem(state, tmp2, &val, "items", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12939,7 +12956,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncWith' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -12963,7 +12980,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'AsyncWith' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -12994,7 +13012,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Match' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &subject, arena); + res = obj2ast_expr(state, tmp, &subject, "subject", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13025,7 +13043,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Match' node")) { goto failed; } - res = obj2ast_match_case(state, tmp2, &val, arena); + res = obj2ast_match_case(state, tmp2, &val, "cases", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13063,7 +13081,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Raise' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &exc, arena); + res = obj2ast_expr(state, tmp, &exc, "exc", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13080,7 +13098,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Raise' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &cause, arena); + res = obj2ast_expr(state, tmp, &cause, "cause", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13127,7 +13145,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Try' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13165,7 +13183,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Try' node")) { goto failed; } - res = obj2ast_excepthandler(state, tmp2, &val, arena); + res = obj2ast_excepthandler(state, tmp2, &val, "handlers", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13203,7 +13221,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Try' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13241,7 +13259,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Try' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "finalbody", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13295,7 +13313,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13333,7 +13351,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { goto failed; } - res = obj2ast_excepthandler(state, tmp2, &val, arena); + res = obj2ast_excepthandler(state, tmp2, &val, "handlers", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13371,7 +13389,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "orelse", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13409,7 +13427,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TryStar' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "finalbody", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13447,7 +13465,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Assert' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &test, arena); + res = obj2ast_expr(state, tmp, &test, "test", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13464,7 +13482,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Assert' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &msg, arena); + res = obj2ast_expr(state, tmp, &msg, "msg", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13481,6 +13499,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* } if (isinstance) { asdl_alias_seq* names; + int is_lazy; if (PyObject_GetOptionalAttr(obj, state->names, &tmp) < 0) { return -1; @@ -13508,7 +13527,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Import' node")) { goto failed; } - res = obj2ast_alias(state, tmp2, &val, arena); + res = obj2ast_alias(state, tmp2, &val, "names", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13520,7 +13539,24 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* } Py_CLEAR(tmp); } - *out = _PyAST_Import(names, lineno, col_offset, end_lineno, + if (PyObject_GetOptionalAttr(obj, state->is_lazy, &tmp) < 0) { + return -1; + } + if (tmp == NULL || tmp == Py_None) { + Py_CLEAR(tmp); + is_lazy = 0; + } + else { + int res; + if (_Py_EnterRecursiveCall(" while traversing 'Import' node")) { + goto failed; + } + res = obj2ast_int(state, tmp, &is_lazy, "is_lazy", arena); + _Py_LeaveRecursiveCall(); + if (res != 0) goto failed; + Py_CLEAR(tmp); + } + *out = _PyAST_Import(names, is_lazy, lineno, col_offset, end_lineno, end_col_offset, arena); if (*out == NULL) goto failed; return 0; @@ -13534,6 +13570,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* identifier module; asdl_alias_seq* names; int level; + int is_lazy; if (PyObject_GetOptionalAttr(obj, state->module, &tmp) < 0) { return -1; @@ -13547,7 +13584,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ImportFrom' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &module, arena); + res = obj2ast_identifier(state, tmp, &module, "module", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13578,7 +13615,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ImportFrom' node")) { goto failed; } - res = obj2ast_alias(state, tmp2, &val, arena); + res = obj2ast_alias(state, tmp2, &val, "names", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13602,13 +13639,30 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ImportFrom' node")) { goto failed; } - res = obj2ast_int(state, tmp, &level, arena); + res = obj2ast_int(state, tmp, &level, "level", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); } - *out = _PyAST_ImportFrom(module, names, level, lineno, col_offset, - end_lineno, end_col_offset, arena); + if (PyObject_GetOptionalAttr(obj, state->is_lazy, &tmp) < 0) { + return -1; + } + if (tmp == NULL || tmp == Py_None) { + Py_CLEAR(tmp); + is_lazy = 0; + } + else { + int res; + if (_Py_EnterRecursiveCall(" while traversing 'ImportFrom' node")) { + goto failed; + } + res = obj2ast_int(state, tmp, &is_lazy, "is_lazy", arena); + _Py_LeaveRecursiveCall(); + if (res != 0) goto failed; + Py_CLEAR(tmp); + } + *out = _PyAST_ImportFrom(module, names, level, is_lazy, lineno, + col_offset, end_lineno, end_col_offset, arena); if (*out == NULL) goto failed; return 0; } @@ -13646,7 +13700,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Global' node")) { goto failed; } - res = obj2ast_identifier(state, tmp2, &val, arena); + res = obj2ast_identifier(state, tmp2, &val, "names", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13697,7 +13751,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Nonlocal' node")) { goto failed; } - res = obj2ast_identifier(state, tmp2, &val, arena); + res = obj2ast_identifier(state, tmp2, &val, "names", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13734,7 +13788,7 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Expr' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13788,8 +13842,8 @@ obj2ast_stmt(struct ast_state *state, PyObject* obj, stmt_ty* out, PyArena* } int -obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* - arena) +obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, const char* + field, PyArena* arena) { int isinstance; @@ -13804,6 +13858,15 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* *out = NULL; return 0; } + tp = state->expr_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'expr', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } if (PyObject_GetOptionalAttr(obj, state->lineno, &tmp) < 0) { return -1; } @@ -13816,7 +13879,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'expr' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13833,7 +13896,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'expr' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13850,7 +13913,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'expr' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13867,7 +13930,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'expr' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13893,7 +13956,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'BoolOp' node")) { goto failed; } - res = obj2ast_boolop(state, tmp, &op, arena); + res = obj2ast_boolop(state, tmp, &op, "op", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13924,7 +13987,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'BoolOp' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "values", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -13962,7 +14025,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'NamedExpr' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -13979,7 +14042,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'NamedExpr' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14011,7 +14074,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'BinOp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &left, arena); + res = obj2ast_expr(state, tmp, &left, "left", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14028,7 +14091,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'BinOp' node")) { goto failed; } - res = obj2ast_operator(state, tmp, &op, arena); + res = obj2ast_operator(state, tmp, &op, "op", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14045,7 +14108,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'BinOp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &right, arena); + res = obj2ast_expr(state, tmp, &right, "right", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14076,7 +14139,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'UnaryOp' node")) { goto failed; } - res = obj2ast_unaryop(state, tmp, &op, arena); + res = obj2ast_unaryop(state, tmp, &op, "op", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14093,7 +14156,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'UnaryOp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &operand, arena); + res = obj2ast_expr(state, tmp, &operand, "operand", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14124,7 +14187,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Lambda' node")) { goto failed; } - res = obj2ast_arguments(state, tmp, &args, arena); + res = obj2ast_arguments(state, tmp, &args, "args", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14141,7 +14204,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Lambda' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &body, arena); + res = obj2ast_expr(state, tmp, &body, "body", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14173,7 +14236,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'IfExp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &test, arena); + res = obj2ast_expr(state, tmp, &test, "test", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14190,7 +14253,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'IfExp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &body, arena); + res = obj2ast_expr(state, tmp, &body, "body", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14207,7 +14270,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'IfExp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &orelse, arena); + res = obj2ast_expr(state, tmp, &orelse, "orelse", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14252,7 +14315,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Dict' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "keys", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14290,7 +14353,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Dict' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "values", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14341,7 +14404,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Set' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "elts", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14379,7 +14442,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ListComp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &elt, arena); + res = obj2ast_expr(state, tmp, &elt, "elt", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14410,7 +14473,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'ListComp' node")) { goto failed; } - res = obj2ast_comprehension(state, tmp2, &val, arena); + res = obj2ast_comprehension(state, tmp2, &val, "generators", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14448,7 +14511,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'SetComp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &elt, arena); + res = obj2ast_expr(state, tmp, &elt, "elt", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14479,7 +14542,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'SetComp' node")) { goto failed; } - res = obj2ast_comprehension(state, tmp2, &val, arena); + res = obj2ast_comprehension(state, tmp2, &val, "generators", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14518,7 +14581,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'DictComp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &key, arena); + res = obj2ast_expr(state, tmp, &key, "key", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14526,16 +14589,16 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (PyObject_GetOptionalAttr(obj, state->value, &tmp) < 0) { return -1; } - if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from DictComp"); - return -1; + if (tmp == NULL || tmp == Py_None) { + Py_CLEAR(tmp); + value = NULL; } else { int res; if (_Py_EnterRecursiveCall(" while traversing 'DictComp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14566,7 +14629,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'DictComp' node")) { goto failed; } - res = obj2ast_comprehension(state, tmp2, &val, arena); + res = obj2ast_comprehension(state, tmp2, &val, "generators", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14604,7 +14667,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'GeneratorExp' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &elt, arena); + res = obj2ast_expr(state, tmp, &elt, "elt", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14635,7 +14698,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'GeneratorExp' node")) { goto failed; } - res = obj2ast_comprehension(state, tmp2, &val, arena); + res = obj2ast_comprehension(state, tmp2, &val, "generators", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14672,7 +14735,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Await' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14702,7 +14765,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Yield' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14732,7 +14795,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'YieldFrom' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14764,7 +14827,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Compare' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &left, arena); + res = obj2ast_expr(state, tmp, &left, "left", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14795,7 +14858,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Compare' node")) { goto failed; } - res = obj2ast_cmpop(state, tmp2, &val, arena); + res = obj2ast_cmpop(state, tmp2, &val, "ops", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14833,7 +14896,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Compare' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "comparators", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14872,7 +14935,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Call' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &func, arena); + res = obj2ast_expr(state, tmp, &func, "func", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14903,7 +14966,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Call' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "args", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14941,7 +15004,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Call' node")) { goto failed; } - res = obj2ast_keyword(state, tmp2, &val, arena); + res = obj2ast_keyword(state, tmp2, &val, "keywords", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -14980,7 +15043,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FormattedValue' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -14997,7 +15060,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FormattedValue' node")) { goto failed; } - res = obj2ast_int(state, tmp, &conversion, arena); + res = obj2ast_int(state, tmp, &conversion, "conversion", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15014,7 +15077,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'FormattedValue' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &format_spec, arena); + res = obj2ast_expr(state, tmp, &format_spec, "format_spec", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15048,7 +15111,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15065,7 +15128,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) { goto failed; } - res = obj2ast_constant(state, tmp, &str, arena); + res = obj2ast_constant(state, tmp, &str, "str", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15082,7 +15145,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) { goto failed; } - res = obj2ast_int(state, tmp, &conversion, arena); + res = obj2ast_int(state, tmp, &conversion, "conversion", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15099,7 +15162,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Interpolation' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &format_spec, arena); + res = obj2ast_expr(state, tmp, &format_spec, "format_spec", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15144,7 +15207,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'JoinedStr' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "values", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -15195,7 +15258,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'TemplateStr' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "values", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -15233,7 +15296,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Constant' node")) { goto failed; } - res = obj2ast_constant(state, tmp, &value, arena); + res = obj2ast_constant(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15250,7 +15313,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Constant' node")) { goto failed; } - res = obj2ast_string(state, tmp, &kind, arena); + res = obj2ast_string(state, tmp, &kind, "kind", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15282,7 +15345,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Attribute' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15299,7 +15362,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Attribute' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &attr, arena); + res = obj2ast_identifier(state, tmp, &attr, "attr", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15316,7 +15379,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Attribute' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15348,7 +15411,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Subscript' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15365,7 +15428,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Subscript' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &slice, arena); + res = obj2ast_expr(state, tmp, &slice, "slice", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15382,7 +15445,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Subscript' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15413,7 +15476,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Starred' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15430,7 +15493,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Starred' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15461,7 +15524,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Name' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &id, arena); + res = obj2ast_identifier(state, tmp, &id, "id", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15478,7 +15541,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Name' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15523,7 +15586,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'List' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "elts", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -15547,7 +15610,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'List' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15592,7 +15655,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Tuple' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "elts", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -15616,7 +15679,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Tuple' node")) { goto failed; } - res = obj2ast_expr_context(state, tmp, &ctx, arena); + res = obj2ast_expr_context(state, tmp, &ctx, "ctx", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15648,7 +15711,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Slice' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &lower, arena); + res = obj2ast_expr(state, tmp, &lower, "lower", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15665,7 +15728,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Slice' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &upper, arena); + res = obj2ast_expr(state, tmp, &upper, "upper", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15682,7 +15745,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'Slice' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &step, arena); + res = obj2ast_expr(state, tmp, &step, "step", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -15701,7 +15764,7 @@ obj2ast_expr(struct ast_state *state, PyObject* obj, expr_ty* out, PyArena* int obj2ast_expr_context(struct ast_state *state, PyObject* obj, expr_context_ty* - out, PyArena* arena) + out, const char* field, PyArena* arena) { int isinstance; @@ -15735,8 +15798,8 @@ obj2ast_expr_context(struct ast_state *state, PyObject* obj, expr_context_ty* } int -obj2ast_boolop(struct ast_state *state, PyObject* obj, boolop_ty* out, PyArena* - arena) +obj2ast_boolop(struct ast_state *state, PyObject* obj, boolop_ty* out, const + char* field, PyArena* arena) { int isinstance; @@ -15763,7 +15826,7 @@ obj2ast_boolop(struct ast_state *state, PyObject* obj, boolop_ty* out, PyArena* int obj2ast_operator(struct ast_state *state, PyObject* obj, operator_ty* out, - PyArena* arena) + const char* field, PyArena* arena) { int isinstance; @@ -15877,8 +15940,8 @@ obj2ast_operator(struct ast_state *state, PyObject* obj, operator_ty* out, } int -obj2ast_unaryop(struct ast_state *state, PyObject* obj, unaryop_ty* out, - PyArena* arena) +obj2ast_unaryop(struct ast_state *state, PyObject* obj, unaryop_ty* out, const + char* field, PyArena* arena) { int isinstance; @@ -15920,8 +15983,8 @@ obj2ast_unaryop(struct ast_state *state, PyObject* obj, unaryop_ty* out, } int -obj2ast_cmpop(struct ast_state *state, PyObject* obj, cmpop_ty* out, PyArena* - arena) +obj2ast_cmpop(struct ast_state *state, PyObject* obj, cmpop_ty* out, const + char* field, PyArena* arena) { int isinstance; @@ -16012,7 +16075,7 @@ obj2ast_cmpop(struct ast_state *state, PyObject* obj, cmpop_ty* out, PyArena* int obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* - out, PyArena* arena) + out, const char* field, PyArena* arena) { PyObject* tmp = NULL; expr_ty target; @@ -16032,7 +16095,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* if (_Py_EnterRecursiveCall(" while traversing 'comprehension' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &target, arena); + res = obj2ast_expr(state, tmp, &target, "target", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16049,7 +16112,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* if (_Py_EnterRecursiveCall(" while traversing 'comprehension' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &iter, arena); + res = obj2ast_expr(state, tmp, &iter, "iter", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16080,7 +16143,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* if (_Py_EnterRecursiveCall(" while traversing 'comprehension' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "ifs", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16104,7 +16167,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* if (_Py_EnterRecursiveCall(" while traversing 'comprehension' node")) { goto failed; } - res = obj2ast_int(state, tmp, &is_async, arena); + res = obj2ast_int(state, tmp, &is_async, "is_async", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16119,7 +16182,7 @@ obj2ast_comprehension(struct ast_state *state, PyObject* obj, comprehension_ty* int obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* - out, PyArena* arena) + out, const char* field, PyArena* arena) { int isinstance; @@ -16134,6 +16197,15 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* *out = NULL; return 0; } + tp = state->excepthandler_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'excepthandler', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } if (PyObject_GetOptionalAttr(obj, state->lineno, &tmp) < 0) { return -1; } @@ -16146,7 +16218,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'excepthandler' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16163,7 +16235,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'excepthandler' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16180,7 +16252,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'excepthandler' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16197,7 +16269,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'excepthandler' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16224,7 +16296,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'ExceptHandler' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &type, arena); + res = obj2ast_expr(state, tmp, &type, "type", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16241,7 +16313,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'ExceptHandler' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16272,7 +16344,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* if (_Py_EnterRecursiveCall(" while traversing 'ExceptHandler' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16298,7 +16370,7 @@ obj2ast_excepthandler(struct ast_state *state, PyObject* obj, excepthandler_ty* int obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, - PyArena* arena) + const char* field, PyArena* arena) { PyObject* tmp = NULL; asdl_arg_seq* posonlyargs; @@ -16335,7 +16407,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_arg(state, tmp2, &val, arena); + res = obj2ast_arg(state, tmp2, &val, "posonlyargs", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16373,7 +16445,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_arg(state, tmp2, &val, arena); + res = obj2ast_arg(state, tmp2, &val, "args", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16397,7 +16469,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_arg(state, tmp, &vararg, arena); + res = obj2ast_arg(state, tmp, &vararg, "vararg", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16428,7 +16500,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_arg(state, tmp2, &val, arena); + res = obj2ast_arg(state, tmp2, &val, "kwonlyargs", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16466,7 +16538,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "kw_defaults", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16490,7 +16562,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_arg(state, tmp, &kwarg, arena); + res = obj2ast_arg(state, tmp, &kwarg, "kwarg", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16521,7 +16593,7 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'arguments' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "defaults", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -16543,7 +16615,8 @@ obj2ast_arguments(struct ast_state *state, PyObject* obj, arguments_ty* out, } int -obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) +obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, const char* + field, PyArena* arena) { PyObject* tmp = NULL; identifier arg; @@ -16566,7 +16639,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &arg, arena); + res = obj2ast_identifier(state, tmp, &arg, "arg", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16583,7 +16656,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &annotation, arena); + res = obj2ast_expr(state, tmp, &annotation, "annotation", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16600,7 +16673,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_string(state, tmp, &type_comment, arena); + res = obj2ast_string(state, tmp, &type_comment, "type_comment", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16617,7 +16690,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16634,7 +16707,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16651,7 +16724,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16668,7 +16741,7 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) if (_Py_EnterRecursiveCall(" while traversing 'arg' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16683,8 +16756,8 @@ obj2ast_arg(struct ast_state *state, PyObject* obj, arg_ty* out, PyArena* arena) } int -obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, - PyArena* arena) +obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, const + char* field, PyArena* arena) { PyObject* tmp = NULL; identifier arg; @@ -16706,7 +16779,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &arg, arena); + res = obj2ast_identifier(state, tmp, &arg, "arg", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16723,7 +16796,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16740,7 +16813,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16757,7 +16830,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16774,7 +16847,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16791,7 +16864,7 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'keyword' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16806,8 +16879,8 @@ obj2ast_keyword(struct ast_state *state, PyObject* obj, keyword_ty* out, } int -obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* - arena) +obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, const + char* field, PyArena* arena) { PyObject* tmp = NULL; identifier name; @@ -16829,7 +16902,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16846,7 +16919,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &asname, arena); + res = obj2ast_identifier(state, tmp, &asname, "asname", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16863,7 +16936,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16880,7 +16953,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16897,7 +16970,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16914,7 +16987,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* if (_Py_EnterRecursiveCall(" while traversing 'alias' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16930,7 +17003,7 @@ obj2ast_alias(struct ast_state *state, PyObject* obj, alias_ty* out, PyArena* int obj2ast_withitem(struct ast_state *state, PyObject* obj, withitem_ty* out, - PyArena* arena) + const char* field, PyArena* arena) { PyObject* tmp = NULL; expr_ty context_expr; @@ -16948,7 +17021,7 @@ obj2ast_withitem(struct ast_state *state, PyObject* obj, withitem_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'withitem' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &context_expr, arena); + res = obj2ast_expr(state, tmp, &context_expr, "context_expr", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16965,7 +17038,7 @@ obj2ast_withitem(struct ast_state *state, PyObject* obj, withitem_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'withitem' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &optional_vars, arena); + res = obj2ast_expr(state, tmp, &optional_vars, "optional_vars", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -16980,7 +17053,7 @@ obj2ast_withitem(struct ast_state *state, PyObject* obj, withitem_ty* out, int obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, - PyArena* arena) + const char* field, PyArena* arena) { PyObject* tmp = NULL; pattern_ty pattern; @@ -16999,7 +17072,7 @@ obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'match_case' node")) { goto failed; } - res = obj2ast_pattern(state, tmp, &pattern, arena); + res = obj2ast_pattern(state, tmp, &pattern, "pattern", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17016,7 +17089,7 @@ obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'match_case' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &guard, arena); + res = obj2ast_expr(state, tmp, &guard, "guard", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17047,7 +17120,7 @@ obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'match_case' node")) { goto failed; } - res = obj2ast_stmt(state, tmp2, &val, arena); + res = obj2ast_stmt(state, tmp2, &val, "body", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17068,8 +17141,8 @@ obj2ast_match_case(struct ast_state *state, PyObject* obj, match_case_ty* out, } int -obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, - PyArena* arena) +obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, const + char* field, PyArena* arena) { int isinstance; @@ -17084,6 +17157,15 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, *out = NULL; return 0; } + tp = state->pattern_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'pattern', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } if (PyObject_GetOptionalAttr(obj, state->lineno, &tmp) < 0) { return -1; } @@ -17096,7 +17178,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'pattern' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17113,7 +17195,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'pattern' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17130,7 +17212,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'pattern' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17147,7 +17229,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'pattern' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17172,7 +17254,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchValue' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &value, arena); + res = obj2ast_expr(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17202,7 +17284,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchSingleton' node")) { goto failed; } - res = obj2ast_constant(state, tmp, &value, arena); + res = obj2ast_constant(state, tmp, &value, "value", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17246,7 +17328,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchSequence' node")) { goto failed; } - res = obj2ast_pattern(state, tmp2, &val, arena); + res = obj2ast_pattern(state, tmp2, &val, "patterns", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17299,7 +17381,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchMapping' node")) { goto failed; } - res = obj2ast_expr(state, tmp2, &val, arena); + res = obj2ast_expr(state, tmp2, &val, "keys", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17337,7 +17419,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchMapping' node")) { goto failed; } - res = obj2ast_pattern(state, tmp2, &val, arena); + res = obj2ast_pattern(state, tmp2, &val, "patterns", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17361,7 +17443,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchMapping' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &rest, arena); + res = obj2ast_identifier(state, tmp, &rest, "rest", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17394,7 +17476,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchClass' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &cls, arena); + res = obj2ast_expr(state, tmp, &cls, "cls", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17425,7 +17507,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchClass' node")) { goto failed; } - res = obj2ast_pattern(state, tmp2, &val, arena); + res = obj2ast_pattern(state, tmp2, &val, "patterns", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17463,7 +17545,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchClass' node")) { goto failed; } - res = obj2ast_identifier(state, tmp2, &val, arena); + res = obj2ast_identifier(state, tmp2, &val, "kwd_attrs", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17501,7 +17583,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchClass' node")) { goto failed; } - res = obj2ast_pattern(state, tmp2, &val, arena); + res = obj2ast_pattern(state, tmp2, &val, "kwd_patterns", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17539,7 +17621,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchStar' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17570,7 +17652,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchAs' node")) { goto failed; } - res = obj2ast_pattern(state, tmp, &pattern, arena); + res = obj2ast_pattern(state, tmp, &pattern, "pattern", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17587,7 +17669,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchAs' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17631,7 +17713,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'MatchOr' node")) { goto failed; } - res = obj2ast_pattern(state, tmp2, &val, arena); + res = obj2ast_pattern(state, tmp2, &val, "patterns", arena); _Py_LeaveRecursiveCall(); Py_DECREF(tmp2); if (res != 0) goto failed; @@ -17657,7 +17739,7 @@ obj2ast_pattern(struct ast_state *state, PyObject* obj, pattern_ty* out, int obj2ast_type_ignore(struct ast_state *state, PyObject* obj, type_ignore_ty* - out, PyArena* arena) + out, const char* field, PyArena* arena) { int isinstance; @@ -17668,6 +17750,15 @@ obj2ast_type_ignore(struct ast_state *state, PyObject* obj, type_ignore_ty* *out = NULL; return 0; } + tp = state->type_ignore_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'type_ignore', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } tp = state->TypeIgnore_type; isinstance = PyObject_IsInstance(obj, tp); if (isinstance == -1) { @@ -17689,7 +17780,7 @@ obj2ast_type_ignore(struct ast_state *state, PyObject* obj, type_ignore_ty* if (_Py_EnterRecursiveCall(" while traversing 'TypeIgnore' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17706,7 +17797,7 @@ obj2ast_type_ignore(struct ast_state *state, PyObject* obj, type_ignore_ty* if (_Py_EnterRecursiveCall(" while traversing 'TypeIgnore' node")) { goto failed; } - res = obj2ast_string(state, tmp, &tag, arena); + res = obj2ast_string(state, tmp, &tag, "tag", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17724,7 +17815,7 @@ obj2ast_type_ignore(struct ast_state *state, PyObject* obj, type_ignore_ty* int obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, - PyArena* arena) + const char* field, PyArena* arena) { int isinstance; @@ -17739,6 +17830,15 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, *out = NULL; return 0; } + tp = state->type_param_type; + isinstance = PyObject_IsInstance(obj, tp); + if (isinstance == -1) { + return 1; + } + if (!isinstance && field != NULL) { + PyErr_Format(PyExc_TypeError, "field '%s' was expecting node of type 'type_param', got '%s'", field, _PyType_Name(Py_TYPE(obj))); + return 1; + } if (PyObject_GetOptionalAttr(obj, state->lineno, &tmp) < 0) { return -1; } @@ -17751,7 +17851,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'type_param' node")) { goto failed; } - res = obj2ast_int(state, tmp, &lineno, arena); + res = obj2ast_int(state, tmp, &lineno, "lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17768,7 +17868,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'type_param' node")) { goto failed; } - res = obj2ast_int(state, tmp, &col_offset, arena); + res = obj2ast_int(state, tmp, &col_offset, "col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17785,7 +17885,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'type_param' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_lineno, arena); + res = obj2ast_int(state, tmp, &end_lineno, "end_lineno", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17802,7 +17902,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'type_param' node")) { goto failed; } - res = obj2ast_int(state, tmp, &end_col_offset, arena); + res = obj2ast_int(state, tmp, &end_col_offset, "end_col_offset", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17829,7 +17929,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'TypeVar' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17846,7 +17946,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'TypeVar' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &bound, arena); + res = obj2ast_expr(state, tmp, &bound, "bound", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17863,7 +17963,8 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'TypeVar' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &default_value, arena); + res = obj2ast_expr(state, tmp, &default_value, "default_value", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17894,7 +17995,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'ParamSpec' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17911,7 +18012,8 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'ParamSpec' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &default_value, arena); + res = obj2ast_expr(state, tmp, &default_value, "default_value", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17942,7 +18044,7 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'TypeVarTuple' node")) { goto failed; } - res = obj2ast_identifier(state, tmp, &name, arena); + res = obj2ast_identifier(state, tmp, &name, "name", arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17959,7 +18061,8 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, if (_Py_EnterRecursiveCall(" while traversing 'TypeVarTuple' node")) { goto failed; } - res = obj2ast_expr(state, tmp, &default_value, arena); + res = obj2ast_expr(state, tmp, &default_value, "default_value", + arena); _Py_LeaveRecursiveCall(); if (res != 0) goto failed; Py_CLEAR(tmp); @@ -17977,6 +18080,28 @@ obj2ast_type_param(struct ast_state *state, PyObject* obj, type_param_ty* out, } +/* Helper for checking if a node class is abstract in the tests. */ +static PyObject * +ast_is_abstract(PyObject *Py_UNUSED(module), PyObject *cls) { + struct ast_state *state = get_ast_state(); + if (state == NULL) { + return NULL; + } + int contains = PySet_Contains(state->abstract_types, cls); + if (contains == -1) { + return NULL; + } + else if (contains == 1) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +static struct PyMethodDef astmodule_methods[] = { + {"_is_abstract", ast_is_abstract, METH_O, NULL}, + {NULL} /* Sentinel */ +}; + static int astmodule_exec(PyObject *m) { @@ -18391,6 +18516,7 @@ astmodule_exec(PyObject *m) } static PyModuleDef_Slot astmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, astmodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, @@ -18402,7 +18528,8 @@ static struct PyModuleDef _astmodule = { .m_name = "_ast", // The _ast module uses a per-interpreter state (PyInterpreterState.ast) .m_size = 0, - .m_slots = astmodule_slots, + .m_methods = astmodule_methods, + .m_slots = astmodule_slots }; PyMODINIT_FUNC @@ -18467,7 +18594,7 @@ mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode) } mod_ty res = NULL; - if (obj2ast_mod(state, ast, &res, arena) != 0) + if (obj2ast_mod(state, ast, &res, NULL, arena) != 0) return NULL; else return res; diff --git a/Python/Python-tokenize.c b/Python/Python-tokenize.c index 152d61c686722e..e6d39e4c7dc823 100644 --- a/Python/Python-tokenize.c +++ b/Python/Python-tokenize.c @@ -1,6 +1,7 @@ #include "Python.h" #include "errcode.h" #include "internal/pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION +#include "internal/pycore_tuple.h" // _PyTuple_FromPair #include "../Parser/lexer/state.h" #include "../Parser/lexer/lexer.h" #include "../Parser/tokenizer/tokenizer.h" @@ -164,7 +165,7 @@ _tokenizer_error(tokenizeriterobject *it) goto exit; } - value = PyTuple_Pack(2, errstr, tmp); + value = _PyTuple_FromPair(errstr, tmp); if (!value) { result = -1; goto exit; @@ -399,6 +400,7 @@ static PyMethodDef tokenize_methods[] = { }; static PyModuleDef_Slot tokenizemodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, tokenizemodule_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Python/_contextvars.c b/Python/_contextvars.c index 0f8b8004c1af22..86acc94fbc79cd 100644 --- a/Python/_contextvars.c +++ b/Python/_contextvars.c @@ -43,6 +43,7 @@ _contextvars_exec(PyObject *m) } static struct PyModuleDef_Slot _contextvars_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, _contextvars_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Python/_warnings.c b/Python/_warnings.c index 12e6172b0cf828..4f6de50efa14a8 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -7,6 +7,7 @@ #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_traceback.h" // _Py_DisplaySourceLine() +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() #include <stdbool.h> @@ -171,7 +172,7 @@ _PyWarnings_InitState(PyInterpreterState *interp) /*************************************************************************/ static int -check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg) +check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg, PyObject *arg2) { PyObject *result; int rc; @@ -182,6 +183,9 @@ check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg) /* An internal plain text default filter must match exactly */ if (PyUnicode_CheckExact(obj)) { + if (arg == NULL) { + return 0; + } int cmp_result = PyUnicode_Compare(obj, arg); if (cmp_result == -1 && PyErr_Occurred()) { return -1; @@ -190,10 +194,19 @@ check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg) } /* Otherwise assume a regex filter and call its match() method */ - result = PyObject_CallMethodOneArg(obj, &_Py_ID(match), arg); + if (arg != NULL) { + result = PyObject_CallMethodOneArg(obj, &_Py_ID(match), arg); + } + else { + PyObject *match = PyImport_ImportModuleAttrString("_py_warnings", "_match_filename"); + if (match == NULL) { + return -1; + } + result = PyObject_CallFunctionObjArgs(match, obj, arg2, NULL); + Py_DECREF(match); + } if (result == NULL) return -1; - rc = PyObject_IsTrue(result); Py_DECREF(result); return rc; @@ -423,7 +436,7 @@ get_default_action(PyInterpreterState *interp) static bool filter_search(PyInterpreterState *interp, PyObject *category, PyObject *text, Py_ssize_t lineno, - PyObject *module, char *list_name, PyObject *filters, + PyObject *module, PyObject *filename, char *list_name, PyObject *filters, PyObject **item, PyObject **matched_action) { bool result = true; *matched_action = NULL; @@ -459,14 +472,14 @@ filter_search(PyInterpreterState *interp, PyObject *category, break; } - good_msg = check_matched(interp, msg, text); + good_msg = check_matched(interp, msg, text, NULL); if (good_msg == -1) { Py_DECREF(tmp_item); result = false; break; } - good_mod = check_matched(interp, mod, module); + good_mod = check_matched(interp, mod, module, filename); if (good_mod == -1) { Py_DECREF(tmp_item); result = false; @@ -504,7 +517,7 @@ filter_search(PyInterpreterState *interp, PyObject *category, static PyObject* get_filter(PyInterpreterState *interp, PyObject *category, PyObject *text, Py_ssize_t lineno, - PyObject *module, PyObject **item) + PyObject *module, PyObject *filename, PyObject **item) { #ifdef Py_DEBUG WarningsState *st = warnings_get_state(interp); @@ -522,7 +535,7 @@ get_filter(PyInterpreterState *interp, PyObject *category, use_global_filters = true; } else { PyObject *context_action = NULL; - if (!filter_search(interp, category, text, lineno, module, "_warnings_context _filters", + if (!filter_search(interp, category, text, lineno, module, filename, "_warnings_context _filters", context_filters, item, &context_action)) { Py_DECREF(context_filters); return NULL; @@ -541,7 +554,7 @@ get_filter(PyInterpreterState *interp, PyObject *category, if (filters == NULL) { return NULL; } - if (!filter_search(interp, category, text, lineno, module, "filters", + if (!filter_search(interp, category, text, lineno, module, filename, "filters", filters, item, &action)) { return NULL; } @@ -612,39 +625,6 @@ already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key, return 0; } -/* New reference. */ -static PyObject * -normalize_module(PyObject *filename) -{ - PyObject *module; - int kind; - const void *data; - Py_ssize_t len; - - len = PyUnicode_GetLength(filename); - if (len < 0) - return NULL; - - if (len == 0) - return PyUnicode_FromString("<unknown>"); - - kind = PyUnicode_KIND(filename); - data = PyUnicode_DATA(filename); - - /* if filename.endswith(".py"): */ - if (len >= 3 && - PyUnicode_READ(kind, data, len-3) == '.' && - PyUnicode_READ(kind, data, len-2) == 'p' && - PyUnicode_READ(kind, data, len-1) == 'y') - { - module = PyUnicode_Substring(filename, 0, len-3); - } - else { - module = Py_NewRef(filename); - } - return module; -} - static int update_registry(PyInterpreterState *interp, PyObject *registry, PyObject *text, PyObject *category, int add_zero) @@ -655,7 +635,7 @@ update_registry(PyInterpreterState *interp, PyObject *registry, PyObject *text, if (add_zero) altkey = PyTuple_Pack(3, text, category, _PyLong_GetZero()); else - altkey = PyTuple_Pack(2, text, category); + altkey = _PyTuple_FromPair(text, category); rc = already_warned(interp, registry, altkey, 1); Py_XDECREF(altkey); @@ -736,7 +716,7 @@ static int call_show_warning(PyThreadState *tstate, PyObject *category, PyObject *text, PyObject *message, PyObject *filename, int lineno, PyObject *lineno_obj, - PyObject *sourceline, PyObject *source) + PyObject *sourceline, PyObject *source, PyObject *module) { PyObject *show_fn, *msg, *res, *warnmsg_cls = NULL; PyInterpreterState *interp = tstate->interp; @@ -767,7 +747,8 @@ call_show_warning(PyThreadState *tstate, PyObject *category, } msg = PyObject_CallFunctionObjArgs(warnmsg_cls, message, category, - filename, lineno_obj, Py_None, Py_None, source, + filename, lineno_obj, Py_None, Py_None, + source ? source : Py_None, module, NULL); Py_DECREF(warnmsg_cls); if (msg == NULL) @@ -812,22 +793,9 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message, return NULL; } - /* Normalize module. */ - if (module == NULL) { - module = normalize_module(filename); - if (module == NULL) - return NULL; - } - else - Py_INCREF(module); - /* Normalize message. */ Py_INCREF(message); /* DECREF'ed in cleanup. */ - rc = PyObject_IsInstance(message, PyExc_Warning); - if (rc == -1) { - goto cleanup; - } - if (rc == 1) { + if (PyObject_TypeCheck(message, (PyTypeObject *)PyExc_Warning)) { text = PyObject_Str(message); if (text == NULL) goto cleanup; @@ -862,7 +830,7 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message, /* Else this warning hasn't been generated before. */ } - action = get_filter(interp, category, text, lineno, module, &item); + action = get_filter(interp, category, text, lineno, module, filename, &item); if (action == NULL) goto cleanup; @@ -911,7 +879,7 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message, goto return_none; if (rc == 0) { if (call_show_warning(tstate, category, text, message, filename, - lineno, lineno_obj, sourceline, source) < 0) + lineno, lineno_obj, sourceline, source, module) < 0) goto cleanup; } else /* if (rc == -1) */ @@ -925,7 +893,6 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message, Py_XDECREF(key); Py_XDECREF(text); Py_XDECREF(lineno_obj); - Py_DECREF(module); Py_XDECREF(message); return result; /* Py_None or NULL. */ } @@ -1080,7 +1047,7 @@ setup_context(Py_ssize_t stack_level, /* Setup registry. */ assert(globals != NULL); - assert(PyDict_Check(globals)); + assert(PyAnyDict_Check(globals)); int rc = PyDict_GetItemRef(globals, &_Py_ID(__warningregistry__), registry); if (rc < 0) { @@ -1124,26 +1091,25 @@ setup_context(Py_ssize_t stack_level, static PyObject * get_category(PyObject *message, PyObject *category) { - int rc; - - /* Get category. */ - rc = PyObject_IsInstance(message, PyExc_Warning); - if (rc == -1) - return NULL; - - if (rc == 1) - category = (PyObject*)Py_TYPE(message); - else if (category == NULL || category == Py_None) - category = PyExc_UserWarning; + if (PyObject_TypeCheck(message, (PyTypeObject *)PyExc_Warning)) { + /* Ignore the category argument. */ + return (PyObject*)Py_TYPE(message); + } + if (category == NULL || category == Py_None) { + return PyExc_UserWarning; + } /* Validate category. */ - rc = PyObject_IsSubclass(category, PyExc_Warning); - /* category is not a subclass of PyExc_Warning or - PyObject_IsSubclass raised an error */ - if (rc == -1 || rc == 0) { + if (!PyType_Check(category)) { + PyErr_Format(PyExc_TypeError, + "category must be a Warning subclass, not '%T'", + category); + return NULL; + } + if (!PyType_IsSubtype((PyTypeObject *)category, (PyTypeObject *)PyExc_Warning)) { PyErr_Format(PyExc_TypeError, - "category must be a Warning subclass, not '%s'", - Py_TYPE(category)->tp_name); + "category must be a Warning subclass, not class '%N'", + (PyTypeObject *)category); return NULL; } @@ -1305,10 +1271,11 @@ warnings_warn_explicit_impl(PyObject *module, PyObject *message, } if (module_globals && module_globals != Py_None) { - if (!PyDict_Check(module_globals)) { + if (!PyAnyDict_Check(module_globals)) { PyErr_Format(PyExc_TypeError, - "module_globals must be a dict, not '%.200s'", - Py_TYPE(module_globals)->tp_name); + "module_globals must be a dict or a frozendict, " + "not %T", + module_globals); return NULL; } @@ -1478,28 +1445,6 @@ PyErr_WarnExplicitObject(PyObject *category, PyObject *message, return 0; } -/* Like PyErr_WarnExplicitObject, but automatically sets up context */ -int -_PyErr_WarnExplicitObjectWithContext(PyObject *category, PyObject *message, - PyObject *filename, int lineno) -{ - PyObject *unused_filename, *module, *registry; - int unused_lineno; - int stack_level = 1; - - if (!setup_context(stack_level, NULL, &unused_filename, &unused_lineno, - &module, &registry)) { - return -1; - } - - int rc = PyErr_WarnExplicitObject(category, message, filename, lineno, - module, registry); - Py_DECREF(unused_filename); - Py_DECREF(registry); - Py_DECREF(module); - return rc; -} - int PyErr_WarnExplicit(PyObject *category, const char *text, const char *filename_str, int lineno, @@ -1682,6 +1627,7 @@ warnings_module_exec(PyObject *module) static PyModuleDef_Slot warnings_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, warnings_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Python/asm_trampoline.S b/Python/asm_trampoline.S deleted file mode 100644 index 93adae3d99038f..00000000000000 --- a/Python/asm_trampoline.S +++ /dev/null @@ -1,72 +0,0 @@ - .text -#if defined(__APPLE__) - .globl __Py_trampoline_func_start -#else - .globl _Py_trampoline_func_start -#endif -# The following assembly is equivalent to: -# PyObject * -# trampoline(PyThreadState *ts, _PyInterpreterFrame *f, -# int throwflag, py_evaluator evaluator) -# { -# return evaluator(ts, f, throwflag); -# } -#if defined(__APPLE__) -__Py_trampoline_func_start: -#else -_Py_trampoline_func_start: -#endif -#ifdef __x86_64__ -#if defined(__CET__) && (__CET__ & 1) - endbr64 -#endif - push %rbp - mov %rsp, %rbp - call *%rcx - pop %rbp - ret -#endif // __x86_64__ -#if defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) - // ARM64 little endian, 64bit ABI - // generate with aarch64-linux-gnu-gcc 12.1 - stp x29, x30, [sp, -16]! - mov x29, sp - blr x3 - ldp x29, x30, [sp], 16 - ret -#endif -#ifdef __riscv - addi sp,sp,-16 - sd ra,8(sp) - jalr a3 - ld ra,8(sp) - addi sp,sp,16 - jr ra -#endif -#if defined(__APPLE__) - .globl __Py_trampoline_func_end -__Py_trampoline_func_end: -#else - .globl _Py_trampoline_func_end -_Py_trampoline_func_end: - .section .note.GNU-stack,"",@progbits -#endif -# Note for indicating the assembly code supports CET -#if defined(__x86_64__) && defined(__CET__) && (__CET__ & 1) - .section .note.gnu.property,"a" - .align 8 - .long 1f - 0f - .long 4f - 1f - .long 5 -0: - .string "GNU" -1: - .align 8 - .long 0xc0000002 - .long 3f - 2f -2: - .long 0x3 -3: - .align 8 -4: -#endif // __x86_64__ diff --git a/Python/asm_trampoline_aarch64.S b/Python/asm_trampoline_aarch64.S new file mode 100644 index 00000000000000..b3aeb728de200c --- /dev/null +++ b/Python/asm_trampoline_aarch64.S @@ -0,0 +1,77 @@ +/* + * References: + * - https://developer.arm.com/documentation/101028/0012/5--Feature-test-macros + * - https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst + */ + +#if defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT == 1 + #define BTI_J hint 36 /* bti j: for jumps, IE br instructions */ + #define BTI_C hint 34 /* bti c: for calls, IE bl instructions */ + #define GNU_PROPERTY_AARCH64_BTI 1 /* bit 0 GNU Notes is for BTI support */ +#else + #define BTI_J + #define BTI_C + #define GNU_PROPERTY_AARCH64_BTI 0 +#endif + +#if defined(__ARM_FEATURE_PAC_DEFAULT) + #if __ARM_FEATURE_PAC_DEFAULT & 1 + #define SIGN_LR hint 25 /* paciasp: sign with the A key */ + #define VERIFY_LR hint 29 /* autiasp: verify with the A key */ + #elif __ARM_FEATURE_PAC_DEFAULT & 2 + #define SIGN_LR hint 27 /* pacibsp: sign with the b key */ + #define VERIFY_LR hint 31 /* autibsp: verify with the b key */ + #endif + #define GNU_PROPERTY_AARCH64_POINTER_AUTH 2 /* bit 1 GNU Notes is for PAC support */ +#else + #define SIGN_LR BTI_C + #define VERIFY_LR + #define GNU_PROPERTY_AARCH64_POINTER_AUTH 0 +#endif + +#if defined(__ARM_FEATURE_GCS_DEFAULT) && __ARM_FEATURE_GCS_DEFAULT == 1 + #define GNU_PROPERTY_AARCH64_GCS 4 /* bit 2 GNU Notes is for GCS support */ +#else + #define GNU_PROPERTY_AARCH64_GCS 0 +#endif + + .text +#if defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) +#if defined(__APPLE__) + .globl __Py_trampoline_func_start +__Py_trampoline_func_start: +#else + .globl _Py_trampoline_func_start +_Py_trampoline_func_start: +#endif + SIGN_LR + stp x29, x30, [sp, -16]! + mov x29, sp + blr x3 + ldp x29, x30, [sp], 16 + VERIFY_LR + ret +#if defined(__APPLE__) + .globl __Py_trampoline_func_end +__Py_trampoline_func_end: +#else + .globl _Py_trampoline_func_end +_Py_trampoline_func_end: + .section .note.GNU-stack,"",@progbits +#endif + +/* Add the BTI, PAC and GCS support to GNU Notes section */ +#if GNU_PROPERTY_AARCH64_BTI != 0 || GNU_PROPERTY_AARCH64_POINTER_AUTH != 0 || GNU_PROPERTY_AARCH64_GCS != 0 + .pushsection .note.gnu.property, "a"; /* Start a new allocatable section */ + .balign 8; /* align it on a byte boundry */ + .long 4; /* size of "GNU\0" */ + .long 0x10; /* size of descriptor */ + .long 0x5; /* NT_GNU_PROPERTY_TYPE_0 */ + .asciz "GNU"; + .long 0xc0000000; /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ + .long 4; /* Four bytes of data */ + .long (GNU_PROPERTY_AARCH64_BTI|GNU_PROPERTY_AARCH64_POINTER_AUTH|GNU_PROPERTY_AARCH64_GCS); /* BTI, PAC or GCS is enabled */ + .long 0; /* padding for 8 byte alignment */ + .popsection; /* end the section */ +#endif +#endif // __aarch64__ && __AARCH64EL__ && !__ILP32__ diff --git a/Python/asm_trampoline_riscv64.S b/Python/asm_trampoline_riscv64.S new file mode 100644 index 00000000000000..6125ba95373f95 --- /dev/null +++ b/Python/asm_trampoline_riscv64.S @@ -0,0 +1,12 @@ + .text + .globl _Py_trampoline_func_start +_Py_trampoline_func_start: + addi sp,sp,-16 + sd ra,8(sp) + jalr a3 + ld ra,8(sp) + addi sp,sp,16 + jr ra + .globl _Py_trampoline_func_end +_Py_trampoline_func_end: + .section .note.GNU-stack,"",@progbits diff --git a/Python/asm_trampoline_x86_64.S b/Python/asm_trampoline_x86_64.S new file mode 100644 index 00000000000000..0e6b11589eafc8 --- /dev/null +++ b/Python/asm_trampoline_x86_64.S @@ -0,0 +1,45 @@ + .text +#ifdef __x86_64__ +#if defined(__APPLE__) + .globl __Py_trampoline_func_start +__Py_trampoline_func_start: +#else + .globl _Py_trampoline_func_start +_Py_trampoline_func_start: +#endif +#if defined(__CET__) && (__CET__ & 1) + endbr64 +#endif + push %rbp + mov %rsp, %rbp + call *%rcx + pop %rbp + ret +#if defined(__APPLE__) + .globl __Py_trampoline_func_end +__Py_trampoline_func_end: +#else + .globl _Py_trampoline_func_end +_Py_trampoline_func_end: + .section .note.GNU-stack,"",@progbits +#endif +# Note for indicating the assembly code supports CET +#if defined(__CET__) && (__CET__ & 1) + .section .note.gnu.property,"a" + .align 8 + .long 1f - 0f + .long 4f - 1f + .long 5 +0: + .string "GNU" +1: + .align 8 + .long 0xc0000002 + .long 3f - 2f +2: + .long 0x3 +3: + .align 8 +4: +#endif +#endif // __x86_64__ diff --git a/Python/assemble.c b/Python/assemble.c index 8cc2d50a3227f8..8bcb4c7bf3a9aa 100644 --- a/Python/assemble.c +++ b/Python/assemble.c @@ -80,9 +80,9 @@ assemble_init(struct assembler *a, int firstlineno) } return SUCCESS; error: - Py_XDECREF(a->a_bytecode); - Py_XDECREF(a->a_linetable); - Py_XDECREF(a->a_except_table); + Py_CLEAR(a->a_bytecode); + Py_CLEAR(a->a_linetable); + Py_CLEAR(a->a_except_table); return ERROR; } @@ -418,6 +418,7 @@ assemble_emit_instr(struct assembler *a, instruction *instr) int size = instr_size(instr); if (a->a_offset + size >= len / (int)sizeof(_Py_CODEUNIT)) { if (len > PY_SSIZE_T_MAX / 2) { + PyErr_NoMemory(); return ERROR; } RETURN_IF_ERROR(_PyBytes_Resize(&a->a_bytecode, len * 2)); @@ -669,7 +670,7 @@ makecode(_PyCompile_CodeUnitMetadata *umd, struct assembler *a, PyObject *const_ // The offset (in code units) of the END_SEND from the SEND in the `yield from` sequence. -#define END_SEND_OFFSET 5 +#define END_SEND_OFFSET 6 static int resolve_jump_offsets(instr_sequence *instrs) diff --git a/Python/ast.c b/Python/ast.c index e01dd0de51e596..4cfa2ff559a5f7 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -305,8 +305,10 @@ validate_expr(expr_ty exp, expr_context_ty ctx) #undef COMP case DictComp_kind: ret = validate_comprehension(exp->v.DictComp.generators) && - validate_expr(exp->v.DictComp.key, Load) && - validate_expr(exp->v.DictComp.value, Load); + validate_expr(exp->v.DictComp.key, Load); + if (ret && exp->v.DictComp.value != NULL){ + ret = validate_expr(exp->v.DictComp.value, Load); + } break; case Yield_kind: ret = !exp->v.Yield.value || validate_expr(exp->v.Yield.value, Load); diff --git a/Python/ast_preprocess.c b/Python/ast_preprocess.c index bafd67ed790b20..54dec3dfe04268 100644 --- a/Python/ast_preprocess.c +++ b/Python/ast_preprocess.c @@ -16,9 +16,11 @@ typedef struct { typedef struct { PyObject *filename; + PyObject *module; int optimize; int ff_features; int syntax_check_only; + int enable_warnings; _Py_c_array_t cf_finally; /* context for PEP 765 check */ int cf_finally_used; @@ -70,7 +72,8 @@ control_flow_in_finally_warning(const char *kw, stmt_ty n, _PyASTPreprocessState } int ret = _PyErr_EmitSyntaxWarning(msg, state->filename, n->lineno, n->col_offset + 1, n->end_lineno, - n->end_col_offset + 1); + n->end_col_offset + 1, + state->module); Py_DECREF(msg); return ret < 0 ? 0 : 1; } @@ -78,7 +81,7 @@ control_flow_in_finally_warning(const char *kw, stmt_ty n, _PyASTPreprocessState static int before_return(_PyASTPreprocessState *state, stmt_ty node_) { - if (state->cf_finally_used > 0) { + if (state->enable_warnings && state->cf_finally_used > 0) { ControlFlowInFinallyContext *ctx = get_cf_finally_top(state); if (ctx->in_finally && ! ctx->in_funcdef) { if (!control_flow_in_finally_warning("return", node_, state)) { @@ -92,7 +95,7 @@ before_return(_PyASTPreprocessState *state, stmt_ty node_) static int before_loop_exit(_PyASTPreprocessState *state, stmt_ty node_, const char *kw) { - if (state->cf_finally_used > 0) { + if (state->enable_warnings && state->cf_finally_used > 0) { ControlFlowInFinallyContext *ctx = get_cf_finally_top(state); if (ctx->in_finally && ! ctx->in_loop) { if (!control_flow_in_finally_warning(kw, node_, state)) { @@ -435,13 +438,38 @@ stmt_seq_remove_item(asdl_stmt_seq *stmts, Py_ssize_t idx) return 1; } +static int +remove_docstring(asdl_stmt_seq *stmts, Py_ssize_t idx, PyArena *ctx_) +{ + assert(_PyAST_GetDocString(stmts) != NULL); + // In case there's just the docstring in the body, replace it with `pass` + // keyword, so body won't be empty. + if (asdl_seq_LEN(stmts) == 1) { + stmt_ty docstring = (stmt_ty)asdl_seq_GET(stmts, 0); + stmt_ty pass = _PyAST_Pass( + docstring->lineno, docstring->col_offset, + // we know that `pass` always takes 4 chars and a single line, + // while docstring can span on multiple lines + docstring->lineno, docstring->col_offset + 4, + ctx_ + ); + if (pass == NULL) { + return 0; + } + asdl_seq_SET(stmts, 0, pass); + return 1; + } + // In case there are more than 1 body items, just remove the docstring. + return stmt_seq_remove_item(stmts, idx); +} + static int astfold_body(asdl_stmt_seq *stmts, PyArena *ctx_, _PyASTPreprocessState *state) { int docstring = _PyAST_GetDocString(stmts) != NULL; if (docstring && (state->optimize >= 2)) { /* remove the docstring */ - if (!stmt_seq_remove_item(stmts, 0)) { + if (!remove_docstring(stmts, 0, ctx_)) { return 0; } docstring = 0; @@ -529,7 +557,9 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTPreprocessState *state) break; case DictComp_kind: CALL(astfold_expr, expr_ty, node_->v.DictComp.key); - CALL(astfold_expr, expr_ty, node_->v.DictComp.value); + if (node_->v.DictComp.value != NULL){ + CALL(astfold_expr, expr_ty, node_->v.DictComp.value); + } CALL_SEQ(astfold_comprehension, comprehension, node_->v.DictComp.generators); break; case GeneratorExp_kind: @@ -943,14 +973,17 @@ astfold_type_param(type_param_ty node_, PyArena *ctx_, _PyASTPreprocessState *st int _PyAST_Preprocess(mod_ty mod, PyArena *arena, PyObject *filename, int optimize, - int ff_features, int syntax_check_only) + int ff_features, int syntax_check_only, int enable_warnings, + PyObject *module) { _PyASTPreprocessState state; memset(&state, 0, sizeof(_PyASTPreprocessState)); state.filename = filename; + state.module = module; state.optimize = optimize; state.ff_features = ff_features; state.syntax_check_only = syntax_check_only; + state.enable_warnings = enable_warnings; if (_Py_CArray_Init(&state.cf_finally, sizeof(ControlFlowInFinallyContext), 20) < 0) { return -1; } diff --git a/Python/ast_unparse.c b/Python/ast_unparse.c index c25699978cf651..6050c351cff68f 100644 --- a/Python/ast_unparse.c +++ b/Python/ast_unparse.c @@ -464,9 +464,15 @@ static int append_ast_dictcomp(PyUnicodeWriter *writer, expr_ty e) { APPEND_CHAR('{'); - APPEND_EXPR(e->v.DictComp.key, PR_TEST); - APPEND_STR(": "); - APPEND_EXPR(e->v.DictComp.value, PR_TEST); + if (e->v.DictComp.value) { + APPEND_EXPR(e->v.DictComp.key, PR_TEST); + APPEND_STR(": "); + APPEND_EXPR(e->v.DictComp.value, PR_TEST); + } + else { + APPEND_STR("**"); + APPEND_EXPR(e->v.DictComp.key, PR_TEST); + } APPEND(comprehensions, e->v.DictComp.generators); APPEND_CHAR_FINISH('}'); } diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 51d7297ec243cc..d5129bf6a5a6bc 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -3,19 +3,20 @@ #include "Python.h" #include "pycore_ast.h" // _PyAST_Validate() #include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_cell.h" // PyCell_GetRef() #include "pycore_ceval.h" // _PyEval_Vector() #include "pycore_compile.h" // _PyAST_Compile() #include "pycore_fileutils.h" // _PyFile_Flush #include "pycore_floatobject.h" // _PyFloat_ExactDealloc() #include "pycore_interp.h" // _PyInterpreterState_GetConfig() +#include "pycore_import.h" // _PyImport_LazyImportModuleLevelObject () #include "pycore_long.h" // _PyLong_CompactValue #include "pycore_modsupport.h" // _PyArg_NoKwnames() #include "pycore_object.h" // _Py_AddToAllObjects() #include "pycore_pyerrors.h" // _PyErr_NoMemory() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_pythonrun.h" // _Py_SourceAsString() -#include "pycore_tuple.h" // _PyTuple_FromArray() -#include "pycore_cell.h" // PyCell_GetRef() +#include "pycore_tuple.h" // _PyTuple_Recycle() #include "clinic/bltinmodule.c.h" @@ -123,7 +124,7 @@ builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs, "__build_class__: name is not a string"); return NULL; } - orig_bases = _PyTuple_FromArray(args + 2, nargs - 2); + orig_bases = PyTuple_FromArray(args + 2, nargs - 2); if (orig_bases == NULL) return NULL; @@ -271,35 +272,97 @@ should be a list of names to emulate ``from name import ...``, or an empty list to emulate ``import name``. When importing a module from a package, note that __import__('A.B', ...) returns package A when fromlist is empty, but its submodule B when -fromlist is not empty. The level argument is used to determine whether to -perform absolute or relative imports: 0 is absolute, while a positive number -is the number of parent directories to search relative to the current module. +fromlist is not empty. The level argument is used to determine whether +to perform absolute or relative imports: 0 is absolute, while a positive +number is the number of parent directories to search relative to the +current module. [clinic start generated code]*/ static PyObject * builtin___import___impl(PyObject *module, PyObject *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) -/*[clinic end generated code: output=4febeda88a0cd245 input=73f4b960ea5b9dd6]*/ +/*[clinic end generated code: output=4febeda88a0cd245 input=e3096a230383f72d]*/ { return PyImport_ImportModuleLevelObject(name, globals, locals, fromlist, level); } +/*[clinic input] +__lazy_import__ as builtin___lazy_import__ + + name: object + globals: object(c_default="NULL") = None + locals: object(c_default="NULL") = None + fromlist: object(c_default="NULL") = () + level: int = 0 + +Lazily imports a module. + +Returns either the module to be imported or a imp.lazy_module object +which indicates the module to be lazily imported. +[clinic start generated code]*/ + +static PyObject * +builtin___lazy_import___impl(PyObject *module, PyObject *name, + PyObject *globals, PyObject *locals, + PyObject *fromlist, int level) +/*[clinic end generated code: output=300f1771094b9e8c input=9c85cccd6a885b9b]*/ +{ + PyObject *builtins; + PyThreadState *tstate = PyThreadState_GET(); + if (globals == NULL) { + globals = PyEval_GetGlobals(); + } + if (locals == NULL) { + locals = globals; + } + + if (!PyDict_Check(globals)) { + PyErr_Format(PyExc_TypeError, + "expect dict for globals, got %T", globals); + return NULL; + } + + if (PyDict_GetItemRef(globals, &_Py_ID(__builtins__), &builtins) < 0) { + return NULL; + } + if (builtins == NULL) { + PyErr_SetString(PyExc_ValueError, + "unable to get builtins for lazy import"); + return NULL; + } + if (PyModule_Check(builtins)) { + PyObject *builtins_dict = Py_XNewRef(PyModule_GetDict(builtins)); + if (builtins_dict == NULL) { + Py_DECREF(builtins); + PyErr_SetString(PyExc_AttributeError, + "builtins module has no dict"); + return NULL; + } + Py_SETREF(builtins, builtins_dict); + } + + PyObject *res = _PyImport_LazyImportModuleLevelObject( + tstate, name, builtins, globals, locals, fromlist, level); + Py_DECREF(builtins); + return res; +} + /*[clinic input] abs as builtin_abs - x: object + number: object / Return the absolute value of the argument. [clinic start generated code]*/ static PyObject * -builtin_abs(PyObject *module, PyObject *x) -/*[clinic end generated code: output=b1b433b9e51356f5 input=bed4ca14e29c20d1]*/ +builtin_abs(PyObject *module, PyObject *number) +/*[clinic end generated code: output=861a9db97dee0595 input=a356196903543505]*/ { - return PyNumber_Absolute(x); + return PyNumber_Absolute(number); } /*[clinic input] @@ -425,7 +488,7 @@ builtin_ascii(PyObject *module, PyObject *obj) /*[clinic input] bin as builtin_bin - number: object + integer: object / Return the binary representation of an integer. @@ -435,10 +498,10 @@ Return the binary representation of an integer. [clinic start generated code]*/ static PyObject * -builtin_bin(PyObject *module, PyObject *number) -/*[clinic end generated code: output=b6fc4ad5e649f4f7 input=53f8a0264bacaf90]*/ +builtin_bin(PyObject *module, PyObject *integer) +/*[clinic end generated code: output=533f9388441805cc input=d16518f148341e70]*/ { - return PyNumber_ToBase(number, 2); + return PyNumber_ToBase(integer, 2); } @@ -633,8 +696,9 @@ PyDoc_STRVAR(filter_doc, "filter(function, iterable, /)\n\ --\n\ \n\ -Return an iterator yielding those items of iterable for which function(item)\n\ -is true. If function is None, return the items that are true."); +Return an iterator yielding those items of iterable for which\n\ +function(item) is true. If function is None, return the items that\n\ +are true."); PyTypeObject PyFilter_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) @@ -707,6 +771,7 @@ builtin_format_impl(PyObject *module, PyObject *value, PyObject *format_spec) } /*[clinic input] +@permit_long_summary chr as builtin_chr i: object @@ -717,7 +782,7 @@ Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff. static PyObject * builtin_chr(PyObject *module, PyObject *i) -/*[clinic end generated code: output=d34f25b8035a9b10 input=f919867f0ba2f496]*/ +/*[clinic end generated code: output=d34f25b8035a9b10 input=a9b255f2d2e503f0]*/ { int overflow; long v = PyLong_AsLongAndOverflow(i, &overflow); @@ -741,36 +806,39 @@ builtin_chr(PyObject *module, PyObject *i) /*[clinic input] +@permit_long_summary compile as builtin_compile source: object - filename: object(converter="PyUnicode_FSDecoder") + filename: unicode_fs_decoded mode: str flags: int = 0 dont_inherit: bool = False optimize: int = -1 * + module as modname: object = None _feature_version as feature_version: int = -1 Compile source into a code object that can be executed by exec() or eval(). -The source code may represent a Python module, statement or expression. +The source code may represent a Python module, statement or +expression. The filename will be used for run-time error messages. The mode must be 'exec' to compile a module, 'single' to compile a single (interactive) statement, or 'eval' to compile an expression. -The flags argument, if present, controls which future statements influence -the compilation of the code. +The flags argument, if present, controls which future statements +influence the compilation of the code. The dont_inherit argument, if true, stops the compilation inheriting the effects of any future statements in effect in the code calling -compile; if absent or false these statements do influence the compilation, -in addition to any features explicitly specified. +compile; if absent or false these statements do influence the +compilation, in addition to any features explicitly specified. [clinic start generated code]*/ static PyObject * builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename, const char *mode, int flags, int dont_inherit, - int optimize, int feature_version) -/*[clinic end generated code: output=b0c09c84f116d3d7 input=cc78e20e7c7682ba]*/ + int optimize, PyObject *modname, int feature_version) +/*[clinic end generated code: output=9a0dce1945917a86 input=444c4fe466a97279]*/ { PyObject *source_copy; const char *str; @@ -799,6 +867,15 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename, "compile(): invalid optimize value"); goto error; } + if (modname == Py_None) { + modname = NULL; + } + else if (!PyUnicode_Check(modname)) { + PyErr_Format(PyExc_TypeError, + "compile() argument 'module' must be str or None, not %T", + modname); + goto error; + } if (!dont_inherit) { PyEval_MergeCompilerFlags(&cf); @@ -844,8 +921,9 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename, goto error; } int syntax_check_only = ((flags & PyCF_OPTIMIZED_AST) == PyCF_ONLY_AST); /* unoptiomized AST */ - if (_PyCompile_AstPreprocess(mod, filename, &cf, optimize, - arena, syntax_check_only) < 0) { + if (_PyCompile_AstPreprocess(mod, filename, &cf, optimize, arena, + syntax_check_only, modname) < 0) + { _PyArena_Free(arena); goto error; } @@ -858,7 +936,7 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename, goto error; } result = (PyObject*)_PyAST_Compile(mod, filename, - &cf, optimize, arena); + &cf, optimize, arena, modname); } _PyArena_Free(arena); goto finally; @@ -876,7 +954,9 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename, tstate->suppress_co_const_immortalization++; #endif - result = Py_CompileStringObject(str, filename, start[compile_mode], &cf, optimize); + result = _Py_CompileStringObjectWithModule(str, filename, + start[compile_mode], &cf, + optimize, modname); #ifdef Py_GIL_DISABLED tstate->suppress_co_const_immortalization--; @@ -888,7 +968,6 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename, error: result = NULL; finally: - Py_DECREF(filename); return result; } @@ -907,10 +986,10 @@ PyDoc_STRVAR(dir_doc, "dir([object]) -> list of strings\n" "\n" "If called without an argument, return the names in the current scope.\n" -"Else, return an alphabetized list of names comprising (some of) the attributes\n" -"of the given object, and of attributes reachable from it.\n" -"If the object supplies a method named __dir__, it will be used; otherwise\n" -"the default dir() logic is used and returns:\n" +"Else, return an alphabetized list of names comprising (some of) the\n" +"attributes of the given object, and of attributes reachable from it.\n" +"If the object supplies a method named __dir__, it will be used;\n" +"otherwise the default dir() logic is used and returns:\n" " for a module object: the module's attributes.\n" " for a class object: its attributes, and recursively the attributes\n" " of its bases.\n" @@ -965,10 +1044,11 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, PyErr_SetString(PyExc_TypeError, "locals must be a mapping"); return NULL; } - if (globals != Py_None && !PyDict_Check(globals)) { + if (globals != Py_None && !PyAnyDict_Check(globals)) { PyErr_SetString(PyExc_TypeError, PyMapping_Check(globals) ? - "globals must be a real dict; try eval(expr, {}, mapping)" - : "globals must be a dict"); + "globals must be a real dict or a frozendict; " + "try eval(expr, {}, mapping)" + : "globals must be a dict or a frozendict"); return NULL; } @@ -1122,9 +1202,10 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, locals = Py_NewRef(globals); } - if (!PyDict_Check(globals)) { - PyErr_Format(PyExc_TypeError, "exec() globals must be a dict, not %.100s", - Py_TYPE(globals)->tp_name); + if (!PyAnyDict_Check(globals)) { + PyErr_Format(PyExc_TypeError, + "exec() globals must be a dict or a frozendict, not %T", + globals); goto error; } if (!PyMapping_Check(locals)) { @@ -1249,9 +1330,11 @@ builtin_getattr(PyObject *self, PyObject *const *args, Py_ssize_t nargs) PyDoc_STRVAR(getattr_doc, "getattr(object, name[, default]) -> value\n\ \n\ -Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.\n\ -When a default argument is given, it is returned when the attribute doesn't\n\ -exist; without it, an exception is raised in that case."); +Get a named attribute from an object.\n\ +\n\ +getattr(x, 'y') is equivalent to x.y.\n\ +When a default argument is given, it is returned when the attribute\n\ +doesn't exist; without it, an exception is raised in that case."); /*[clinic input] @@ -1259,13 +1342,13 @@ globals as builtin_globals Return the dictionary containing the current scope's global variables. -NOTE: Updates to this dictionary *will* affect name lookups in the current -global scope and vice-versa. +NOTE: Updates to this dictionary *will* affect name lookups in the +current global scope and vice-versa. [clinic start generated code]*/ static PyObject * builtin_globals_impl(PyObject *module) -/*[clinic end generated code: output=e5dd1527067b94d2 input=9327576f92bb48ba]*/ +/*[clinic end generated code: output=e5dd1527067b94d2 input=6d725a9b48d1eaeb]*/ { PyObject *globals; if (_PyEval_GetFrame() != NULL) { @@ -1501,34 +1584,27 @@ map_next(PyObject *self) } Py_ssize_t nargs = 0; - for (i=0; i < niters; i++) { + for (i = 0; i < niters; i++) { PyObject *it = PyTuple_GET_ITEM(lz->iters, i); PyObject *val = Py_TYPE(it)->tp_iternext(it); if (val == NULL) { if (lz->strict) { goto check; } - goto exit; + goto exit_no_result; } stack[i] = val; nargs++; } result = _PyObject_VectorcallTstate(tstate, lz->func, stack, nargs, NULL); + goto exit; -exit: - for (i=0; i < nargs; i++) { - Py_DECREF(stack[i]); - } - if (stack != small_stack) { - PyMem_Free(stack); - } - return result; check: if (PyErr_Occurred()) { if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { // next() on argument i raised an exception (not StopIteration) - return NULL; + goto exit_no_result; } PyErr_Clear(); } @@ -1536,9 +1612,10 @@ map_next(PyObject *self) // ValueError: map() argument 2 is shorter than argument 1 // ValueError: map() argument 3 is shorter than arguments 1-2 const char* plural = i == 1 ? " " : "s 1-"; - return PyErr_Format(PyExc_ValueError, - "map() argument %d is shorter than argument%s%d", - i + 1, plural, i); + PyErr_Format(PyExc_ValueError, + "map() argument %zd is shorter than argument%s%zd", + i + 1, plural, i); + goto exit_no_result; } for (i = 1; i < niters; i++) { PyObject *it = PyTuple_GET_ITEM(lz->iters, i); @@ -1546,21 +1623,33 @@ map_next(PyObject *self) if (val) { Py_DECREF(val); const char* plural = i == 1 ? " " : "s 1-"; - return PyErr_Format(PyExc_ValueError, - "map() argument %d is longer than argument%s%d", - i + 1, plural, i); + PyErr_Format(PyExc_ValueError, + "map() argument %zd is longer than argument%s%zd", + i + 1, plural, i); + goto exit_no_result; } if (PyErr_Occurred()) { if (!PyErr_ExceptionMatches(PyExc_StopIteration)) { // next() on argument i raised an exception (not StopIteration) - return NULL; + goto exit_no_result; } PyErr_Clear(); } // Argument i is exhausted. So far so good... } // All arguments are exhausted. Success! - goto exit; + +exit_no_result: + assert(result == NULL); + +exit: + for (i = 0; i < nargs; i++) { + Py_DECREF(stack[i]); + } + if (stack != small_stack) { + PyMem_Free(stack); + } + return result; } static PyObject * @@ -1612,8 +1701,8 @@ PyDoc_STRVAR(map_doc, Make an iterator that computes the function using arguments from\n\ each of the iterables. Stops when the shortest iterable is exhausted.\n\ \n\ -If strict is true and one of the arguments is exhausted before the others,\n\ -raise a ValueError."); +If strict is true and one of the arguments is exhausted before the\n\ +others, raise a ValueError."); PyTypeObject PyMap_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) @@ -1700,8 +1789,8 @@ builtin_next(PyObject *self, PyObject *const *args, Py_ssize_t nargs) PyDoc_STRVAR(next_doc, "next(iterator[, default])\n\ \n\ -Return the next item from the iterator. If default is given and the iterator\n\ -is exhausted, it is returned instead of raising StopIteration."); +Return the next item from the iterator. If default is given and the\n\ +iterator is exhausted, it is returned instead of raising StopIteration."); /*[clinic input] @@ -1757,15 +1846,17 @@ hash as builtin_hash obj: object / -Return the hash value for the given object. +Return the integer hash value for the given object. -Two objects that compare equal must also have the same hash value, but the -reverse is not necessarily true. +Two objects that compare equal must also have the same hash value, but +the reverse is not necessarily true. Hash values may differ between +Python processes. Not all objects are hashable; calling hash() on an +unhashable object raises TypeError. [clinic start generated code]*/ static PyObject * builtin_hash(PyObject *module, PyObject *obj) -/*[clinic end generated code: output=237668e9d7688db7 input=58c48be822bf9c54]*/ +/*[clinic end generated code: output=237668e9d7688db7 input=70a242ff65f6717c]*/ { Py_hash_t x; @@ -1779,7 +1870,7 @@ builtin_hash(PyObject *module, PyObject *obj) /*[clinic input] hex as builtin_hex - number: object + integer: object / Return the hexadecimal representation of an integer. @@ -1789,10 +1880,10 @@ Return the hexadecimal representation of an integer. [clinic start generated code]*/ static PyObject * -builtin_hex(PyObject *module, PyObject *number) -/*[clinic end generated code: output=e46b612169099408 input=e645aff5fc7d540e]*/ +builtin_hex(PyObject *module, PyObject *integer) +/*[clinic end generated code: output=e5de857ba61aae08 input=3bef4746efc62fac]*/ { - return PyNumber_ToBase(number, 16); + return PyNumber_ToBase(integer, 16); } @@ -1822,7 +1913,8 @@ iter(callable, sentinel) -> iterator\n\ \n\ Get an iterator from an object. In the first form, the argument must\n\ supply its own iterator, or be a sequence.\n\ -In the second form, the callable is called until it returns the sentinel."); +In the second form, the callable is called until it returns the\n\ +sentinel."); /*[clinic input] @@ -1846,7 +1938,7 @@ PyObject *PyAnextAwaitable_New(PyObject *, PyObject *); /*[clinic input] anext as builtin_anext - aiterator: object + async_iterator as aiterator: object default: object = NULL / @@ -1859,7 +1951,7 @@ it is returned instead of raising StopAsyncIteration. static PyObject * builtin_anext_impl(PyObject *module, PyObject *aiterator, PyObject *default_value) -/*[clinic end generated code: output=f02c060c163a81fa input=2900e4a370d39550]*/ +/*[clinic end generated code: output=f02c060c163a81fa input=f3dc5a93f073e5ac]*/ { PyTypeObject *t; PyObject *awaitable; @@ -1916,14 +2008,15 @@ locals as builtin_locals Return a dictionary containing the current scope's local variables. -NOTE: Whether or not updates to this dictionary will affect name lookups in -the local scope and vice-versa is *implementation dependent* and not -covered by any backwards compatibility guarantees. +NOTE: Whether or not updates to this dictionary will affect name +lookups in the local scope and vice-versa is *implementation +dependent* and not covered by any backwards compatibility +guarantees. [clinic start generated code]*/ static PyObject * builtin_locals_impl(PyObject *module) -/*[clinic end generated code: output=b46c94015ce11448 input=7874018d478d5c4b]*/ +/*[clinic end generated code: output=b46c94015ce11448 input=989cc75c22167c42]*/ { PyObject *locals; if (_PyEval_GetFrame() != NULL) { @@ -2098,7 +2191,7 @@ With two or more positional arguments, return the largest argument."); /*[clinic input] oct as builtin_oct - number: object + integer: object / Return the octal representation of an integer. @@ -2108,25 +2201,31 @@ Return the octal representation of an integer. [clinic start generated code]*/ static PyObject * -builtin_oct(PyObject *module, PyObject *number) -/*[clinic end generated code: output=40a34656b6875352 input=ad6b274af4016c72]*/ +builtin_oct(PyObject *module, PyObject *integer) +/*[clinic end generated code: output=8c15f2145a74c390 input=b97c377b15fedf8d]*/ { - return PyNumber_ToBase(number, 8); + return PyNumber_ToBase(integer, 8); } /*[clinic input] ord as builtin_ord - c: object + character as c: object / -Return the Unicode code point for a one-character string. +Return the ordinal value of a character. + +If the argument is a one-character string, return the Unicode code +point of that character. + +If the argument is a bytes or bytearray object of length 1, return its +single byte value. [clinic start generated code]*/ static PyObject * builtin_ord(PyObject *module, PyObject *c) -/*[clinic end generated code: output=4fa5e87a323bae71 input=3064e5d6203ad012]*/ +/*[clinic end generated code: output=4fa5e87a323bae71 input=98d38480432e1177]*/ { long ord; Py_ssize_t size; @@ -2169,6 +2268,7 @@ builtin_ord(PyObject *module, PyObject *c) /*[clinic input] +@permit_long_summary pow as builtin_pow base: object @@ -2177,14 +2277,14 @@ pow as builtin_pow Equivalent to base**exp with 2 arguments or base**exp % mod with 3 arguments -Some types, such as ints, are able to use a more efficient algorithm when -invoked using the three argument form. +Some types, such as ints, are able to use a more efficient algorithm +when invoked using the three argument form. [clinic start generated code]*/ static PyObject * builtin_pow_impl(PyObject *module, PyObject *base, PyObject *exp, PyObject *mod) -/*[clinic end generated code: output=3ca1538221bbf15f input=435dbd48a12efb23]*/ +/*[clinic end generated code: output=3ca1538221bbf15f input=0cd5c3ecc8003aec]*/ { return PyNumber_Power(base, exp, mod); } @@ -2192,7 +2292,7 @@ builtin_pow_impl(PyObject *module, PyObject *base, PyObject *exp, /*[clinic input] print as builtin_print - *args: array + *objects: array sep: object(c_default="Py_None") = ' ' string inserted between values, default a space. end: object(c_default="Py_None") = '\n' @@ -2207,10 +2307,10 @@ Prints the values to a stream, or to sys.stdout by default. [clinic start generated code]*/ static PyObject * -builtin_print_impl(PyObject *module, PyObject * const *args, - Py_ssize_t args_length, PyObject *sep, PyObject *end, +builtin_print_impl(PyObject *module, PyObject * const *objects, + Py_ssize_t objects_length, PyObject *sep, PyObject *end, PyObject *file, int flush) -/*[clinic end generated code: output=3cb7e5b66f1a8547 input=66ea4de1605a2437]*/ +/*[clinic end generated code: output=38d8def56c837bcc input=ff35cb3d59ee8115]*/ { int i, err; @@ -2251,7 +2351,7 @@ builtin_print_impl(PyObject *module, PyObject * const *args, return NULL; } - for (i = 0; i < args_length; i++) { + for (i = 0; i < objects_length; i++) { if (i > 0) { if (sep == NULL) { err = PyFile_WriteString(" ", file); @@ -2264,7 +2364,7 @@ builtin_print_impl(PyObject *module, PyObject * const *args, return NULL; } } - err = PyFile_WriteObject(args[i], file, Py_PRINT_RAW); + err = PyFile_WriteObject(objects[i], file, Py_PRINT_RAW); if (err) { Py_DECREF(file); return NULL; @@ -2305,13 +2405,14 @@ Read a string from standard input. The trailing newline is stripped. The prompt string, if given, is printed to standard output without a trailing newline before reading input. -If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError. +If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise +EOFError. On *nix systems, readline is used if available. [clinic start generated code]*/ static PyObject * builtin_input_impl(PyObject *module, PyObject *prompt) -/*[clinic end generated code: output=83db5a191e7a0d60 input=159c46d4ae40977e]*/ +/*[clinic end generated code: output=83db5a191e7a0d60 input=ebb939c954639427]*/ { PyObject *fin = NULL; PyObject *fout = NULL; @@ -2579,13 +2680,14 @@ round as builtin_round Round a number to a given precision in decimal digits. -The return value is an integer if ndigits is omitted or None. Otherwise -the return value has the same type as the number. ndigits may be negative. +The return value is an integer if ndigits is omitted or None. +Otherwise the return value has the same type as the number. ndigits +may be negative. [clinic start generated code]*/ static PyObject * builtin_round_impl(PyObject *module, PyObject *number, PyObject *ndigits) -/*[clinic end generated code: output=ff0d9dd176c02ede input=275678471d7aca15]*/ +/*[clinic end generated code: output=ff0d9dd176c02ede input=bdcb7c67bf4a4320]*/ { PyObject *result; if (ndigits == Py_None) { @@ -2617,8 +2719,8 @@ sorted as builtin_sorted Return a new list containing all items from the iterable in ascending order. -A custom key function can be supplied to customize the sort order, and the -reverse flag can be set to request the result in descending order. +A custom key function can be supplied to customize the sort order, and +the reverse flag can be set to request the result in descending order. [end disabled clinic input]*/ PyDoc_STRVAR(builtin_sorted__doc__, @@ -2752,6 +2854,7 @@ cs_to_double(CompensatedSum total) } /*[clinic input] +@permit_long_summary sum as builtin_sum iterable: object @@ -2761,13 +2864,13 @@ sum as builtin_sum Return the sum of a 'start' value (default: 0) plus an iterable of numbers When the iterable is empty, return the start value. -This function is intended specifically for use with numeric values and may -reject non-numeric types. +This function is intended specifically for use with numeric values and +may reject non-numeric types. [clinic start generated code]*/ static PyObject * builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) -/*[clinic end generated code: output=df758cec7d1d302f input=162b50765250d222]*/ +/*[clinic end generated code: output=df758cec7d1d302f input=d464d57815196b73]*/ { PyObject *result = start; PyObject *temp, *item, *iter; @@ -3003,6 +3106,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) /*[clinic input] +@permit_long_summary isinstance as builtin_isinstance obj: object @@ -3011,15 +3115,15 @@ isinstance as builtin_isinstance Return whether an object is an instance of a class or of a subclass thereof. -A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to -check against. This is equivalent to ``isinstance(x, A) or isinstance(x, B) -or ...`` etc. +A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the +target to check against. This is equivalent to ``isinstance(x, A) or +isinstance(x, B) or ...`` etc. [clinic start generated code]*/ static PyObject * builtin_isinstance_impl(PyObject *module, PyObject *obj, PyObject *class_or_tuple) -/*[clinic end generated code: output=6faf01472c13b003 input=ffa743db1daf7549]*/ +/*[clinic end generated code: output=6faf01472c13b003 input=5d74d547df498f38]*/ { int retval; @@ -3039,15 +3143,15 @@ issubclass as builtin_issubclass Return whether 'cls' is derived from another class or is the same class. -A tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the target to -check against. This is equivalent to ``issubclass(x, A) or issubclass(x, B) -or ...``. +A tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the +target to check against. This is equivalent to ``issubclass(x, A) or +issubclass(x, B) or ...``. [clinic start generated code]*/ static PyObject * builtin_issubclass_impl(PyObject *module, PyObject *cls, PyObject *class_or_tuple) -/*[clinic end generated code: output=358412410cd7a250 input=a24b9f3d58c370d6]*/ +/*[clinic end generated code: output=358412410cd7a250 input=a91ce96345a6705d]*/ { int retval; @@ -3218,7 +3322,7 @@ zip_next(PyObject *self) // ValueError: zip() argument 3 is shorter than arguments 1-2 const char* plural = i == 1 ? " " : "s 1-"; return PyErr_Format(PyExc_ValueError, - "zip() argument %d is shorter than argument%s%d", + "zip() argument %zd is shorter than argument%s%zd", i + 1, plural, i); } for (i = 1; i < tuplesize; i++) { @@ -3228,7 +3332,7 @@ zip_next(PyObject *self) Py_DECREF(item); const char* plural = i == 1 ? " " : "s 1-"; return PyErr_Format(PyExc_ValueError, - "zip() argument %d is longer than argument%s%d", + "zip() argument %zd is longer than argument%s%zd", i + 1, plural, i); } if (PyErr_Occurred()) { @@ -3252,7 +3356,7 @@ zip_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) if (lz->strict) { return PyTuple_Pack(3, Py_TYPE(lz), lz->ittuple, Py_True); } - return PyTuple_Pack(2, Py_TYPE(lz), lz->ittuple); + return _PyTuple_FromPair((PyObject *)Py_TYPE(lz), lz->ittuple); } static PyObject * @@ -3277,13 +3381,13 @@ PyDoc_STRVAR(zip_doc, "zip(*iterables, strict=False)\n\ --\n\ \n\ -The zip object yields n-length tuples, where n is the number of iterables\n\ -passed as positional arguments to zip(). The i-th element in every tuple\n\ -comes from the i-th iterable argument to zip(). This continues until the\n\ -shortest argument is exhausted.\n\ +The zip object yields n-length tuples, where n is the number of\n\ +iterables passed as positional arguments to zip(). The i-th element\n\ +in every tuple comes from the i-th iterable argument to zip(). This\n\ +continues until the shortest argument is exhausted.\n\ \n\ -If strict is true and one of the arguments is exhausted before the others,\n\ -raise a ValueError.\n\ +If strict is true and one of the arguments is exhausted before the\n\ +others, raise a ValueError.\n\ \n\ >>> list(zip('abcdefg', range(3), range(4)))\n\ [('a', 0, 0), ('b', 1, 1), ('c', 2, 2)]"); @@ -3337,6 +3441,7 @@ static PyMethodDef builtin_methods[] = { {"__build_class__", _PyCFunction_CAST(builtin___build_class__), METH_FASTCALL | METH_KEYWORDS, build_class_doc}, BUILTIN___IMPORT___METHODDEF + BUILTIN___LAZY_IMPORT___METHODDEF BUILTIN_ABS_METHODDEF BUILTIN_ALL_METHODDEF BUILTIN_ANY_METHODDEF @@ -3454,6 +3559,7 @@ _PyBuiltin_Init(PyInterpreterState *interp) SETBUILTIN("enumerate", &PyEnum_Type); SETBUILTIN("filter", &PyFilter_Type); SETBUILTIN("float", &PyFloat_Type); + SETBUILTIN("frozendict", &PyFrozenDict_Type); SETBUILTIN("frozenset", &PyFrozenSet_Type); SETBUILTIN("property", &PyProperty_Type); SETBUILTIN("int", &PyLong_Type); @@ -3462,6 +3568,7 @@ _PyBuiltin_Init(PyInterpreterState *interp) SETBUILTIN("object", &PyBaseObject_Type); SETBUILTIN("range", &PyRange_Type); SETBUILTIN("reversed", &PyReversed_Type); + SETBUILTIN("sentinel", &PySentinel_Type); SETBUILTIN("set", &PySet_Type); SETBUILTIN("slice", &PySlice_Type); SETBUILTIN("staticmethod", &PyStaticMethod_Type); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d9abc4c53d1f50..e368092b300f86 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -11,13 +11,15 @@ #include "pycore_audit.h" // _PySys_Audit() #include "pycore_backoff.h" #include "pycore_cell.h" // PyCell_GetRef() -#include "pycore_ceval.h" +#include "pycore_ceval.h" // _PyEval_LazyImportName(), _PyEval_LazyImportFrom() #include "pycore_code.h" #include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS #include "pycore_function.h" +#include "pycore_import.h" // _PyImport_LoadLazyImportTstate() #include "pycore_instruments.h" #include "pycore_interpolation.h" // _PyInterpolation_Build() #include "pycore_intrinsics.h" +#include "pycore_lazyimportobject.h" // PyLazyImport_CheckExact() #include "pycore_long.h" // _PyLong_ExactDealloc(), _PyLong_GetZero() #include "pycore_moduleobject.h" // PyModuleObject #include "pycore_object.h" // _PyObject_GC_TRACK() @@ -31,7 +33,7 @@ #include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs #include "pycore_stackref.h" #include "pycore_template.h" // _PyTemplate_Build() -#include "pycore_tuple.h" // _PyTuple_ITEMS() +#include "pycore_tuple.h" // _PyStolenTuple_Free(), _PyTuple_ITEMS() #include "pycore_typeobject.h" // _PySuper_Lookup() #include "pycore_dict.h" @@ -146,42 +148,44 @@ dummy_func( pure inst(NOP, (--)) { } - family(RESUME, 0) = { + family(RESUME, 1) = { RESUME_CHECK, + RESUME_CHECK_JIT, }; macro(NOT_TAKEN) = NOP; op(_CHECK_PERIODIC, (--)) { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - int err = _Py_HandlePending(tstate); - ERROR_IF(err != 0); - } + int err = check_periodics(tstate); + ERROR_IF(err != 0); + } + + replaced op(_CHECK_PERIODIC_AT_END, (--)) { + int err = check_periodics(tstate); + ERROR_IF(err != 0); } op(_CHECK_PERIODIC_IF_NOT_YIELD_FROM, (--)) { if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - int err = _Py_HandlePending(tstate); - ERROR_IF(err != 0); - } + int err = check_periodics(tstate); + ERROR_IF(err != 0); } } - op(_QUICKEN_RESUME, (--)) { - #if ENABLE_SPECIALIZATION_FT - if (tstate->tracing == 0 && this_instr->op.code == RESUME) { - FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); - } - #endif /* ENABLE_SPECIALIZATION_FT */ + tier1 op(_QUICKEN_RESUME, (counter/1 --)) { + _Py_Specialize_Resume(this_instr, tstate, frame); } tier1 op(_MAYBE_INSTRUMENT, (--)) { - if (tstate->tracing == 0) { + #ifdef Py_GIL_DISABLED + // For thread-safety, we need to check instrumentation version + // even when tracing. Otherwise, another thread may concurrently + // re-write the bytecode while we are executing this function. + int check_instrumentation = 1; + #else + int check_instrumentation = (tstate->tracing == 0); + #endif + if (check_instrumentation) { uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & ~_PY_EVAL_EVENTS_MASK; uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); if (code_version != global_version) { @@ -195,7 +199,7 @@ dummy_func( } } - op(_LOAD_BYTECODE, (--)) { + replaced op(_LOAD_BYTECODE, (--)) { #ifdef Py_GIL_DISABLED if (frame->tlbc_index != ((_PyThreadStateImpl *)tstate)->tlbc_index) { @@ -219,7 +223,11 @@ dummy_func( _QUICKEN_RESUME + _CHECK_PERIODIC_IF_NOT_YIELD_FROM; - inst(RESUME_CHECK, (--)) { + macro(RESUME_CHECK) = + unused/1 + + _RESUME_CHECK; + + op(_RESUME_CHECK, (--)) { #if defined(__EMSCRIPTEN__) DEOPT_IF(_Py_emscripten_signal_clock == 0); _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; @@ -234,9 +242,14 @@ dummy_func( #endif } + macro(RESUME_CHECK_JIT) = + unused/1 + + _RESUME_CHECK + + _JIT; + op(_MONITOR_RESUME, (--)) { int err = _Py_call_instrumentation( - tstate, oparg > 0, frame, this_instr); + tstate, oparg == 0 ? PY_MONITORING_EVENT_PY_START : PY_MONITORING_EVENT_PY_RESUME, frame, this_instr); ERROR_IF(err); if (frame->instr_ptr != this_instr) { /* Instrumentation has jumped */ @@ -245,6 +258,7 @@ dummy_func( } macro(INSTRUMENTED_RESUME) = + unused/1 + _LOAD_BYTECODE + _MAYBE_INSTRUMENT + _CHECK_PERIODIC_IF_NOT_YIELD_FROM + @@ -306,11 +320,13 @@ dummy_func( value = PyStackRef_FromPyObjectBorrow(obj); } - replicate(8) inst(STORE_FAST, (value --)) { + macro(STORE_FAST) = _SWAP_FAST + POP_TOP; + + replicate(8) op(_SWAP_FAST, (value -- trash)) { _PyStackRef tmp = GETLOCAL(oparg); GETLOCAL(oparg) = value; DEAD(value); - PyStackRef_XCLOSE(tmp); + trash = tmp; } pseudo(STORE_FAST_MAYBE_NULL, (unused --)) = { @@ -365,9 +381,9 @@ dummy_func( PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); } - tier2 op(_POP_TWO, (nos, tos --)) { - PyStackRef_CLOSE(tos); - PyStackRef_CLOSE(nos); + op(_POP_TOP_OPARG, (args[oparg] -- )) { + _PyStackRef_CloseStack(args, oparg); + DEAD(args); } pure inst(PUSH_NULL, (-- res)) { @@ -409,13 +425,15 @@ dummy_func( PyStackRef_CLOSE(iter); } - pure inst(END_SEND, (receiver, value -- val)) { + pure inst(END_SEND, (receiver, index_or_null, value -- val)) { val = value; + (void)index_or_null; DEAD(value); + DEAD(index_or_null); PyStackRef_CLOSE(receiver); } - tier1 inst(INSTRUMENTED_END_SEND, (receiver, value -- val)) { + tier1 inst(INSTRUMENTED_END_SEND, (receiver, index_or_null, value -- val)) { PyObject *receiver_o = PyStackRef_AsPyObjectBorrow(receiver); if (PyGen_Check(receiver_o) || PyCoro_CheckExact(receiver_o)) { int err = monitor_stop_iteration(tstate, frame, this_instr, PyStackRef_AsPyObjectBorrow(value)); @@ -424,18 +442,39 @@ dummy_func( } } val = value; + (void)index_or_null; + DEAD(index_or_null); DEAD(value); PyStackRef_CLOSE(receiver); } - inst(UNARY_NEGATIVE, (value -- res)) { + macro(UNARY_NEGATIVE) = _UNARY_NEGATIVE + POP_TOP; + + op(_UNARY_NEGATIVE, (value -- res, v)) { PyObject *res_o = PyNumber_Negative(PyStackRef_AsPyObjectBorrow(value)); - PyStackRef_CLOSE(value); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } res = PyStackRef_FromPyObjectSteal(res_o); + v = value; + DEAD(value); } - pure inst(UNARY_NOT, (value -- res)) { + // Inplace negation: negate a uniquely-referenced float in place. + // Tier 2 only. + tier2 op(_UNARY_NEGATIVE_FLOAT_INPLACE, (value -- res, v)) { + PyObject *val_o = PyStackRef_AsPyObjectBorrow(value); + assert(PyFloat_CheckExact(val_o)); + assert(_PyObject_IsUniquelyReferenced(val_o)); + STAT_INC(UNARY_NEGATIVE, hit); + double dres = -((PyFloatObject *)val_o)->ob_fval; + ((PyFloatObject *)val_o)->ob_fval = dres; + res = value; + v = PyStackRef_NULL; + INPUTS_DEAD(); + } + + inst(UNARY_NOT, (value -- res)) { assert(PyStackRef_BoolCheck(value)); res = PyStackRef_IsFalse(value) ? PyStackRef_True : PyStackRef_False; @@ -452,7 +491,7 @@ dummy_func( }; specializing op(_SPECIALIZE_TO_BOOL, (counter/1, value -- value)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_ToBool(value, next_instr); @@ -460,7 +499,7 @@ dummy_func( } OPCODE_DEFERRED_INC(TO_BOOL); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } op(_TO_BOOL, (value -- res)) { @@ -477,21 +516,17 @@ dummy_func( STAT_INC(TO_BOOL, hit); } - inst(TO_BOOL_INT, (unused/1, unused/2, value -- res)) { - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - EXIT_IF(!PyLong_CheckExact(value_o)); + op(_TO_BOOL_INT, (value -- res, v)) { STAT_INC(TO_BOOL, hit); - if (_PyLong_IsZero((PyLongObject *)value_o)) { - assert(_Py_IsImmortal(value_o)); - DEAD(value); - res = PyStackRef_False; - } - else { - PyStackRef_CLOSE(value); - res = PyStackRef_True; - } + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + res = (_PyLong_IsZero((PyLongObject *)value_o)) ? PyStackRef_False : PyStackRef_True; + v = value; + DEAD(value); } + macro(TO_BOOL_INT) = + _GUARD_TOS_INT + unused/1 + unused/2 + _TO_BOOL_INT + _POP_TOP_INT; + op(_GUARD_NOS_LIST, (nos, unused -- nos, unused)) { PyObject *o = PyStackRef_AsPyObjectBorrow(nos); EXIT_IF(!PyList_CheckExact(o)); @@ -507,14 +542,15 @@ dummy_func( EXIT_IF(!PySlice_Check(o)); } - macro(TO_BOOL_LIST) = _GUARD_TOS_LIST + unused/1 + unused/2 + _TO_BOOL_LIST; + macro(TO_BOOL_LIST) = _GUARD_TOS_LIST + unused/1 + unused/2 + _TO_BOOL_LIST + POP_TOP; - op(_TO_BOOL_LIST, (value -- res)) { + op(_TO_BOOL_LIST, (value -- res, v)) { PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); assert(PyList_CheckExact(value_o)); STAT_INC(TO_BOOL, hit); res = PyList_GET_SIZE(value_o) ? PyStackRef_True : PyStackRef_False; - DECREF_INPUTS(); + v = value; + DEAD(value); } inst(TO_BOOL_NONE, (unused/1, unused/2, value -- res)) { @@ -525,6 +561,12 @@ dummy_func( res = PyStackRef_False; } + op(_GUARD_NOS_COMPACT_ASCII, (nos, unused -- nos, unused)) { + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + EXIT_IF(!PyUnicode_CheckExact(o)); + EXIT_IF(!PyUnicode_IS_COMPACT_ASCII(o)); + } + op(_GUARD_NOS_UNICODE, (nos, unused -- nos, unused)) { PyObject *o = PyStackRef_AsPyObjectBorrow(nos); EXIT_IF(!PyUnicode_CheckExact(o)); @@ -535,39 +577,40 @@ dummy_func( EXIT_IF(!PyUnicode_CheckExact(value_o)); } - op(_TO_BOOL_STR, (value -- res)) { + op(_TO_BOOL_STR, (value -- res, v)) { STAT_INC(TO_BOOL, hit); PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (value_o == &_Py_STR(empty)) { - assert(_Py_IsImmortal(value_o)); - DEAD(value); - res = PyStackRef_False; - } - else { - assert(Py_SIZE(value_o)); - PyStackRef_CLOSE(value); - res = PyStackRef_True; - } + res = value_o == &_Py_STR(empty) ? PyStackRef_False : PyStackRef_True; + v = value; + DEAD(value); } macro(TO_BOOL_STR) = - _GUARD_TOS_UNICODE + unused/1 + unused/2 + _TO_BOOL_STR; + _GUARD_TOS_UNICODE + unused/1 + unused/2 + _TO_BOOL_STR + _POP_TOP_UNICODE; - op(_REPLACE_WITH_TRUE, (value -- res)) { - PyStackRef_CLOSE(value); + op(_REPLACE_WITH_TRUE, (value -- res, v)) { res = PyStackRef_True; + v = value; + DEAD(value); } macro(TO_BOOL_ALWAYS_TRUE) = unused/1 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + - _REPLACE_WITH_TRUE; + _REPLACE_WITH_TRUE + + POP_TOP; - inst(UNARY_INVERT, (value -- res)) { + macro(UNARY_INVERT) = _UNARY_INVERT + POP_TOP; + + op(_UNARY_INVERT, (value -- res, v)) { PyObject *res_o = PyNumber_Invert(PyStackRef_AsPyObjectBorrow(value)); - PyStackRef_CLOSE(value); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } res = PyStackRef_FromPyObjectSteal(res_o); + v = value; + DEAD(value); } family(BINARY_OP, INLINE_CACHE_ENTRIES_BINARY_OP) = { @@ -582,9 +625,10 @@ dummy_func( BINARY_OP_SUBSCR_LIST_SLICE, BINARY_OP_SUBSCR_TUPLE_INT, BINARY_OP_SUBSCR_STR_INT, + BINARY_OP_SUBSCR_USTR_INT, BINARY_OP_SUBSCR_DICT, BINARY_OP_SUBSCR_GETITEM, - // BINARY_OP_INPLACE_ADD_UNICODE, // See comments at that opcode. + BINARY_OP_INPLACE_ADD_UNICODE, BINARY_OP_EXTEND, }; @@ -610,7 +654,7 @@ dummy_func( EXIT_IF(!_PyLong_IsCompact((PyLongObject *)value_o)); } - pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) { + pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); assert(PyLong_CheckExact(left_o)); @@ -620,12 +664,12 @@ dummy_func( STAT_INC(BINARY_OP, hit); res = _PyCompactLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); EXIT_IF(PyStackRef_IsNull(res)); - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + l = left; + r = right; INPUTS_DEAD(); } - pure op(_BINARY_OP_ADD_INT, (left, right -- res)) { + pure op(_BINARY_OP_ADD_INT, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); assert(PyLong_CheckExact(left_o)); @@ -635,12 +679,12 @@ dummy_func( STAT_INC(BINARY_OP, hit); res = _PyCompactLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); EXIT_IF(PyStackRef_IsNull(res)); - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + l = left; + r = right; INPUTS_DEAD(); } - pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res)) { + pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); assert(PyLong_CheckExact(left_o)); @@ -650,19 +694,76 @@ dummy_func( STAT_INC(BINARY_OP, hit); res = _PyCompactLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); EXIT_IF(PyStackRef_IsNull(res)); - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + l = left; + r = right; INPUTS_DEAD(); } macro(BINARY_OP_MULTIPLY_INT) = - _GUARD_TOS_INT + _GUARD_NOS_INT + unused/5 + _BINARY_OP_MULTIPLY_INT; + _GUARD_TOS_INT + _GUARD_NOS_INT + unused/5 + _BINARY_OP_MULTIPLY_INT + _POP_TOP_INT + _POP_TOP_INT; macro(BINARY_OP_ADD_INT) = - _GUARD_TOS_INT + _GUARD_NOS_INT + unused/5 + _BINARY_OP_ADD_INT; + _GUARD_TOS_INT + _GUARD_NOS_INT + unused/5 + _BINARY_OP_ADD_INT + _POP_TOP_INT + _POP_TOP_INT; macro(BINARY_OP_SUBTRACT_INT) = - _GUARD_TOS_INT + _GUARD_NOS_INT + unused/5 + _BINARY_OP_SUBTRACT_INT; + _GUARD_TOS_INT + _GUARD_NOS_INT + unused/5 + _BINARY_OP_SUBTRACT_INT + _POP_TOP_INT + _POP_TOP_INT; + + // Inplace compact int ops: mutate the uniquely-referenced operand + // when possible. The op handles decref of TARGET internally so + // the following _POP_TOP_INT becomes _POP_TOP_NOP. Tier 2 only. + tier2 op(_BINARY_OP_ADD_INT_INPLACE, (left, right -- res, l, r)) { + INT_INPLACE_OP(left, right, left, +, _PyCompactLong_Add); + EXIT_IF(PyStackRef_IsNull(_int_inplace_res)); + res = _int_inplace_res; + l = left; + r = right; + INPUTS_DEAD(); + } + + tier2 op(_BINARY_OP_SUBTRACT_INT_INPLACE, (left, right -- res, l, r)) { + INT_INPLACE_OP(left, right, left, -, _PyCompactLong_Subtract); + EXIT_IF(PyStackRef_IsNull(_int_inplace_res)); + res = _int_inplace_res; + l = left; + r = right; + INPUTS_DEAD(); + } + + tier2 op(_BINARY_OP_MULTIPLY_INT_INPLACE, (left, right -- res, l, r)) { + INT_INPLACE_OP(left, right, left, *, _PyCompactLong_Multiply); + EXIT_IF(PyStackRef_IsNull(_int_inplace_res)); + res = _int_inplace_res; + l = left; + r = right; + INPUTS_DEAD(); + } + + tier2 op(_BINARY_OP_ADD_INT_INPLACE_RIGHT, (left, right -- res, l, r)) { + INT_INPLACE_OP(left, right, right, +, _PyCompactLong_Add); + EXIT_IF(PyStackRef_IsNull(_int_inplace_res)); + res = _int_inplace_res; + l = left; + r = right; + INPUTS_DEAD(); + } + + tier2 op(_BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT, (left, right -- res, l, r)) { + INT_INPLACE_OP(left, right, right, -, _PyCompactLong_Subtract); + EXIT_IF(PyStackRef_IsNull(_int_inplace_res)); + res = _int_inplace_res; + l = left; + r = right; + INPUTS_DEAD(); + } + + tier2 op(_BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT, (left, right -- res, l, r)) { + INT_INPLACE_OP(left, right, right, *, _PyCompactLong_Multiply); + EXIT_IF(PyStackRef_IsNull(_int_inplace_res)); + res = _int_inplace_res; + l = left; + r = right; + INPUTS_DEAD(); + } op(_GUARD_NOS_FLOAT, (left, unused -- left, unused)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -674,7 +775,7 @@ dummy_func( EXIT_IF(!PyFloat_CheckExact(value_o)); } - pure op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res)) { + pure op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); assert(PyFloat_CheckExact(left_o)); @@ -684,12 +785,17 @@ dummy_func( double dres = ((PyFloatObject *)left_o)->ob_fval * ((PyFloatObject *)right_o)->ob_fval; - res = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + ERROR_NO_POP(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; INPUTS_DEAD(); - ERROR_IF(PyStackRef_IsNull(res)); } - pure op(_BINARY_OP_ADD_FLOAT, (left, right -- res)) { + pure op(_BINARY_OP_ADD_FLOAT, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); assert(PyFloat_CheckExact(left_o)); @@ -699,12 +805,17 @@ dummy_func( double dres = ((PyFloatObject *)left_o)->ob_fval + ((PyFloatObject *)right_o)->ob_fval; - res = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + ERROR_NO_POP(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; INPUTS_DEAD(); - ERROR_IF(PyStackRef_IsNull(res)); } - pure op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res)) { + pure op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); assert(PyFloat_CheckExact(left_o)); @@ -714,65 +825,124 @@ dummy_func( double dres = ((PyFloatObject *)left_o)->ob_fval - ((PyFloatObject *)right_o)->ob_fval; - res = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + ERROR_NO_POP(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; INPUTS_DEAD(); - ERROR_IF(PyStackRef_IsNull(res)); } + macro(BINARY_OP_MULTIPLY_FLOAT) = + _GUARD_TOS_FLOAT + _GUARD_NOS_FLOAT + unused/5 + _BINARY_OP_MULTIPLY_FLOAT + _POP_TOP_FLOAT + _POP_TOP_FLOAT; + macro(BINARY_OP_ADD_FLOAT) = + _GUARD_TOS_FLOAT + _GUARD_NOS_FLOAT + unused/5 + _BINARY_OP_ADD_FLOAT + _POP_TOP_FLOAT + _POP_TOP_FLOAT; + macro(BINARY_OP_SUBTRACT_FLOAT) = + _GUARD_TOS_FLOAT + _GUARD_NOS_FLOAT + unused/5 + _BINARY_OP_SUBTRACT_FLOAT + _POP_TOP_FLOAT + _POP_TOP_FLOAT; + + // Inplace float ops: mutate the uniquely-referenced left operand + // instead of allocating a new float. Tier 2 only. + // The optimizer sets l to a borrowed value so the following _POP_TOP_FLOAT + // becomes _POP_TOP_NOP. + tier2 op(_BINARY_OP_ADD_FLOAT_INPLACE, (left, right -- res, l, r)) { + FLOAT_INPLACE_OP(left, right, left, +); + res = left; + l = PyStackRef_NULL; + r = right; + INPUTS_DEAD(); + } - pure op(_BINARY_OP_MULTIPLY_FLOAT__NO_DECREF_INPUTS, (left, right -- res)) { - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); + tier2 op(_BINARY_OP_SUBTRACT_FLOAT_INPLACE, (left, right -- res, l, r)) { + FLOAT_INPLACE_OP(left, right, left, -); + res = left; + l = PyStackRef_NULL; + r = right; + INPUTS_DEAD(); + } - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval * - ((PyFloatObject *)right_o)->ob_fval; - res = PyStackRef_FromPyObjectSteal(PyFloat_FromDouble(dres)); + tier2 op(_BINARY_OP_MULTIPLY_FLOAT_INPLACE, (left, right -- res, l, r)) { + FLOAT_INPLACE_OP(left, right, left, *); + res = left; + l = PyStackRef_NULL; + r = right; INPUTS_DEAD(); - ERROR_IF(PyStackRef_IsNull(res)); } - pure op(_BINARY_OP_ADD_FLOAT__NO_DECREF_INPUTS, (left, right -- res)) { - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); + // Inplace RIGHT variants: mutate the uniquely-referenced right operand. + tier2 op(_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT, (left, right -- res, l, r)) { + FLOAT_INPLACE_OP(left, right, right, +); + res = right; + l = left; + r = PyStackRef_NULL; + INPUTS_DEAD(); + } - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval + - ((PyFloatObject *)right_o)->ob_fval; - res = PyStackRef_FromPyObjectSteal(PyFloat_FromDouble(dres)); + tier2 op(_BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT, (left, right -- res, l, r)) { + FLOAT_INPLACE_OP(left, right, right, *); + res = right; + l = left; + r = PyStackRef_NULL; + INPUTS_DEAD(); + } + + tier2 op(_BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT, (left, right -- res, l, r)) { + FLOAT_INPLACE_OP(left, right, right, -); + res = right; + l = left; + r = PyStackRef_NULL; INPUTS_DEAD(); - ERROR_IF(PyStackRef_IsNull(res)); } - pure op(_BINARY_OP_SUBTRACT_FLOAT__NO_DECREF_INPUTS, (left, right -- res)) { + // Float true division --- not specialized at tier 1, emitted by the + // tier 2 optimizer when both operands are known floats. + tier2 op(_BINARY_OP_TRUEDIV_FLOAT, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); assert(PyFloat_CheckExact(left_o)); assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval - - ((PyFloatObject *)right_o)->ob_fval; - res = PyStackRef_FromPyObjectSteal(PyFloat_FromDouble(dres)); + double divisor = ((PyFloatObject *)right_o)->ob_fval; + if (divisor == 0.0) { + PyErr_SetString(PyExc_ZeroDivisionError, + "float division by zero"); + ERROR_NO_POP(); + } + double dres = ((PyFloatObject *)left_o)->ob_fval / divisor; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + ERROR_NO_POP(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; INPUTS_DEAD(); - ERROR_IF(PyStackRef_IsNull(res)); } - macro(BINARY_OP_MULTIPLY_FLOAT) = - _GUARD_TOS_FLOAT + _GUARD_NOS_FLOAT + unused/5 + _BINARY_OP_MULTIPLY_FLOAT; - macro(BINARY_OP_ADD_FLOAT) = - _GUARD_TOS_FLOAT + _GUARD_NOS_FLOAT + unused/5 + _BINARY_OP_ADD_FLOAT; - macro(BINARY_OP_SUBTRACT_FLOAT) = - _GUARD_TOS_FLOAT + _GUARD_NOS_FLOAT + unused/5 + _BINARY_OP_SUBTRACT_FLOAT; + tier2 op(_BINARY_OP_TRUEDIV_FLOAT_INPLACE, (left, right -- res, l, r)) { + FLOAT_INPLACE_DIVOP(left, right, left); + if (_divop_err) { + ERROR_NO_POP(); + } + res = left; + l = PyStackRef_NULL; + r = right; + INPUTS_DEAD(); + } + + tier2 op(_BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT, (left, right -- res, l, r)) { + FLOAT_INPLACE_DIVOP(left, right, right); + if (_divop_err) { + ERROR_NO_POP(); + } + res = right; + l = left; + r = PyStackRef_NULL; + INPUTS_DEAD(); + } - pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) { + pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); assert(PyUnicode_CheckExact(left_o)); @@ -780,23 +950,22 @@ dummy_func( STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - INPUTS_DEAD(); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + INPUTS_DEAD(); } macro(BINARY_OP_ADD_UNICODE) = - _GUARD_TOS_UNICODE + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_ADD_UNICODE; - - // This is a subtle one. It's a super-instruction for - // BINARY_OP_ADD_UNICODE followed by STORE_FAST - // where the store goes into the left argument. - // So the inputs are the same as for all BINARY_OP - // specializations, but there is no output. - // At the end we just skip over the STORE_FAST. - op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) { + _GUARD_TOS_UNICODE + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_ADD_UNICODE + _POP_TOP_UNICODE + _POP_TOP_UNICODE; + + // This is a subtle one. We write NULL to the local + // of the following STORE_FAST and leave the result for STORE_FAST + // later to store. + op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right -- res)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); assert(PyUnicode_CheckExact(left_o)); assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(right))); @@ -806,11 +975,11 @@ dummy_func( assert(next_instr->op.code == STORE_FAST); next_oparg = next_instr->op.arg; #else - next_oparg = CURRENT_OPERAND0(); + next_oparg = (int)CURRENT_OPERAND0_16(); #endif _PyStackRef *target_local = &GETLOCAL(next_oparg); assert(PyUnicode_CheckExact(left_o)); - DEOPT_IF(PyStackRef_AsPyObjectBorrow(*target_local) != left_o); + EXIT_IF(PyStackRef_AsPyObjectBorrow(*target_local) != left_o); STAT_INC(BINARY_OP, hit); /* Handle `left = left + right` or `left += right` for str. * @@ -824,20 +993,35 @@ dummy_func( * that the string is safe to mutate. */ assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left)); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - DEAD(left); PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); PyObject *right_o = PyStackRef_AsPyObjectSteal(right); + /* gh-143403: It's critical to close this reference *before* + * we append. Otherwise, append can move the underlying + * unicode object, which will cause a use after free! + */ + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); + DEAD(left); PyUnicode_Append(&temp, right_o); - *target_local = PyStackRef_FromPyObjectSteal(temp); - Py_DECREF(right_o); - ERROR_IF(PyStackRef_IsNull(*target_local)); - #if TIER_ONE - // The STORE_FAST is already done. This is done here in tier one, - // and during trace projection in tier two: - assert(next_instr->op.code == STORE_FAST); - SKIP_OVER(1); - #endif + _Py_DECREF_SPECIALIZED(right_o, _PyUnicode_ExactDealloc); + *target_local = PyStackRef_NULL; + ERROR_IF(temp == NULL); + res = PyStackRef_FromPyObjectSteal(temp); + } + + tier2 op(_GUARD_BINARY_OP_EXTEND_LHS, (descr/4, left, right -- left, right)) { + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard == NULL && d->lhs_type != NULL); + EXIT_IF(Py_TYPE(left_o) != d->lhs_type); + } + + tier2 op(_GUARD_BINARY_OP_EXTEND_RHS, (descr/4, left, right -- left, right)) { + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard == NULL && d->rhs_type != NULL); + EXIT_IF(Py_TYPE(right_o) != d->rhs_type); } op(_GUARD_BINARY_OP_EXTEND, (descr/4, left, right -- left, right)) { @@ -845,12 +1029,14 @@ dummy_func( PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); - assert(d && d->guard); - int res = d->guard(left_o, right_o); - DEOPT_IF(!res); + assert(d != NULL); + int match = (d->guard != NULL) + ? d->guard(left_o, right_o) + : (Py_TYPE(left_o) == d->lhs_type && Py_TYPE(right_o) == d->rhs_type); + EXIT_IF(!match); } - op(_BINARY_OP_EXTEND, (descr/4, left, right -- res)) { + op(_BINARY_OP_EXTEND, (descr/4, left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); @@ -859,12 +1045,26 @@ dummy_func( STAT_INC(BINARY_OP, hit); PyObject *res_o = d->action(left_o, right_o); - DECREF_INPUTS(); + if (res_o == NULL) { + ERROR_NO_POP(); + } + assert(d->result_type == NULL || Py_TYPE(res_o) == d->result_type); + assert(!d->result_unique || Py_REFCNT(res_o) == 1 || _Py_IsImmortal(res_o)); + // The JIT and tier 2 optimizer assume that float results from + // binary operations are always uniquely referenced (refcount == 1). + // If this assertion fails, update the optimizer to stop marking + // float results as unique in optimizer_bytecodes.c. + assert(!PyFloat_CheckExact(res_o) || Py_REFCNT(res_o) == 1); + res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + DEAD(left); + DEAD(right); } macro(BINARY_OP_EXTEND) = - unused/1 + _GUARD_BINARY_OP_EXTEND + rewind/-4 + _BINARY_OP_EXTEND; + unused/1 + _GUARD_BINARY_OP_EXTEND + rewind/-4 + _BINARY_OP_EXTEND + POP_TOP + POP_TOP; macro(BINARY_OP_INPLACE_ADD_UNICODE) = _GUARD_TOS_UNICODE + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_INPLACE_ADD_UNICODE; @@ -877,19 +1077,30 @@ dummy_func( } op(_BINARY_SLICE, (container, start, stop -- res)) { - PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); + PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); + PyObject *start_o = PyStackRef_AsPyObjectBorrow(start); + PyObject *stop_o = PyStackRef_AsPyObjectBorrow(stop); PyObject *res_o; - // Can't use ERROR_IF() here, because we haven't - // DECREF'ed container yet, and we still own slice. - if (slice == NULL) { - res_o = NULL; + if (PyList_CheckExact(container_o)) { + res_o = _PyList_BinarySlice(container_o, start_o, stop_o); + } + else if (PyTuple_CheckExact(container_o)) { + res_o = _PyTuple_BinarySlice(container_o, start_o, stop_o); + } + else if (PyUnicode_CheckExact(container_o)) { + res_o = _PyUnicode_BinarySlice(container_o, start_o, stop_o); } else { - res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); - Py_DECREF(slice); + PyObject *slice = PySlice_New(start_o, stop_o, NULL); + if (slice == NULL) { + res_o = NULL; + } + else { + res_o = PyObject_GetItem(container_o, slice); + Py_DECREF(slice); + } } - PyStackRef_CLOSE(container); + DECREF_INPUTS(); ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -905,7 +1116,8 @@ dummy_func( op(_STORE_SLICE, (v, container, start, stop -- )) { PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); + PyStackRef_AsPyObjectSteal(stop), + Py_None); int err; if (slice == NULL) { err = 1; @@ -921,38 +1133,39 @@ dummy_func( macro(STORE_SLICE) = _SPECIALIZE_STORE_SLICE + _STORE_SLICE; macro(BINARY_OP_SUBSCR_LIST_INT) = - _GUARD_TOS_INT + _GUARD_NOS_LIST + unused/5 + _BINARY_OP_SUBSCR_LIST_INT; + _GUARD_TOS_INT + _GUARD_NOS_LIST + unused/5 + _BINARY_OP_SUBSCR_LIST_INT + _POP_TOP_INT + POP_TOP; - op(_BINARY_OP_SUBSCR_LIST_INT, (list_st, sub_st -- res)) { + op(_BINARY_OP_SUBSCR_LIST_INT, (list_st, sub_st -- res, ls, ss)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); assert(PyLong_CheckExact(sub)); assert(PyList_CheckExact(list)); - // Deopt unless 0 <= sub < PyList_Size(list) - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)); - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub); + if (index < 0) { + index += PyList_GET_SIZE(list); + } #ifdef Py_GIL_DISABLED PyObject *res_o = _PyList_GetItemRef((PyListObject*)list, index); - DEOPT_IF(res_o == NULL); - STAT_INC(BINARY_OP, hit); + EXIT_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); #else - DEOPT_IF(index >= PyList_GET_SIZE(list)); - STAT_INC(BINARY_OP, hit); + EXIT_IF(index < 0 || index >= PyList_GET_SIZE(list)); PyObject *res_o = PyList_GET_ITEM(list, index); assert(res_o != NULL); res = PyStackRef_FromPyObjectNew(res_o); #endif STAT_INC(BINARY_OP, hit); - DECREF_INPUTS(); + ls = list_st; + ss = sub_st; + INPUTS_DEAD(); } macro(BINARY_OP_SUBSCR_LIST_SLICE) = - _GUARD_TOS_SLICE + _GUARD_NOS_LIST + unused/5 + _BINARY_OP_SUBSCR_LIST_SLICE; + _GUARD_TOS_SLICE + _GUARD_NOS_LIST + unused/5 + _BINARY_OP_SUBSCR_LIST_SLICE + POP_TOP + POP_TOP; - op(_BINARY_OP_SUBSCR_LIST_SLICE, (list_st, sub_st -- res)) { + op(_BINARY_OP_SUBSCR_LIST_SLICE, (list_st, sub_st -- res, ls, ss)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); @@ -961,31 +1174,57 @@ dummy_func( PyObject *res_o = _PyList_SliceSubscript(list, sub); STAT_INC(BINARY_OP, hit); - DECREF_INPUTS(); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } res = PyStackRef_FromPyObjectSteal(res_o); + ls = list_st; + ss = sub_st; + INPUTS_DEAD(); } macro(BINARY_OP_SUBSCR_STR_INT) = - _GUARD_TOS_INT + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_SUBSCR_STR_INT; + _GUARD_TOS_INT + _GUARD_NOS_COMPACT_ASCII + unused/5 + _BINARY_OP_SUBSCR_STR_INT + _POP_TOP_INT + _POP_TOP_UNICODE; + + op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res, s, i)) { + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); + + assert(PyLong_CheckExact(sub)); + assert(PyUnicode_CheckExact(str)); + EXIT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject*)sub)); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + EXIT_IF(PyUnicode_GET_LENGTH(str) <= index); + uint8_t c = PyUnicode_1BYTE_DATA(str)[index]; + assert(c < 128); + STAT_INC(BINARY_OP, hit); + PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; + s = str_st; + i = sub_st; + INPUTS_DEAD(); + res = PyStackRef_FromPyObjectBorrow(res_o); + } - op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res)) { + macro(BINARY_OP_SUBSCR_USTR_INT) = + _GUARD_TOS_INT + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_SUBSCR_USTR_INT + _POP_TOP_INT + _POP_TOP_UNICODE; + + op(_BINARY_OP_SUBSCR_USTR_INT, (str_st, sub_st -- res, s, i)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); assert(PyLong_CheckExact(sub)); assert(PyUnicode_CheckExact(str)); - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)); + EXIT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject*)sub)); Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(PyUnicode_GET_LENGTH(str) <= index); + EXIT_IF(PyUnicode_GET_LENGTH(str) <= index); // Specialize for reading an ASCII character from any string: Py_UCS4 c = PyUnicode_READ_CHAR(str, index); - DEOPT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c); + EXIT_IF(Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c); STAT_INC(BINARY_OP, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - DEAD(sub_st); - PyStackRef_CLOSE(str_st); + s = str_st; + i = sub_st; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectBorrow(res_o); } @@ -1000,9 +1239,16 @@ dummy_func( } macro(BINARY_OP_SUBSCR_TUPLE_INT) = - _GUARD_TOS_INT + _GUARD_NOS_TUPLE + unused/5 + _BINARY_OP_SUBSCR_TUPLE_INT; + _GUARD_TOS_INT + + _GUARD_NOS_TUPLE + + _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS + + unused/5 + + _BINARY_OP_SUBSCR_TUPLE_INT + + _POP_TOP_INT + + POP_TOP; - op(_BINARY_OP_SUBSCR_TUPLE_INT, (tuple_st, sub_st -- res)) { + // A guard that checks that the tuple subscript is within bounds + op(_GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS, (tuple_st, sub_st -- tuple_st, sub_st)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); @@ -1010,20 +1256,43 @@ dummy_func( assert(PyTuple_CheckExact(tuple)); // Deopt unless 0 <= sub < PyTuple_Size(list) - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)); + EXIT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)); Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - DEOPT_IF(index >= PyTuple_GET_SIZE(tuple)); + EXIT_IF(index >= PyTuple_GET_SIZE(tuple)); + } + + op(_BINARY_OP_SUBSCR_TUPLE_INT, (tuple_st, sub_st -- res, ts, ss)) { + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); + STAT_INC(BINARY_OP, hit); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; PyObject *res_o = PyTuple_GET_ITEM(tuple, index); assert(res_o != NULL); - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); res = PyStackRef_FromPyObjectNew(res_o); - DECREF_INPUTS(); + ts = tuple_st; + ss = sub_st; + INPUTS_DEAD(); } - op(_GUARD_NOS_DICT, (nos, unused -- nos, unused)) { + op(_GUARD_NOS_DICT_SUBSCRIPT, (nos, unused -- nos, unused)) { PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - EXIT_IF(!PyDict_CheckExact(o)); + DEOPT_IF(!Py_TYPE(o)->tp_as_mapping); + DEOPT_IF(Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript); + } + + op(_GUARD_NOS_DICT_STORE_SUBSCRIPT, (unused, nos, unused -- unused, nos, unused)) { + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + DEOPT_IF(!Py_TYPE(o)->tp_as_mapping); + DEOPT_IF(Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript); + } + + op(_GUARD_TOS_ANY_DICT, (tos -- tos)) { + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + EXIT_IF(!PyAnyDict_CheckExact(o)); } op(_GUARD_TOS_DICT, (tos -- tos)) { @@ -1031,42 +1300,62 @@ dummy_func( EXIT_IF(!PyDict_CheckExact(o)); } + op(_GUARD_TOS_FROZENDICT, (tos -- tos)) { + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + EXIT_IF(!PyFrozenDict_CheckExact(o)); + } + macro(BINARY_OP_SUBSCR_DICT) = - _GUARD_NOS_DICT + unused/5 + _BINARY_OP_SUBSCR_DICT; + _RECORD_NOS_TYPE + + _GUARD_NOS_DICT_SUBSCRIPT + unused/5 + _BINARY_OP_SUBSCR_DICT + POP_TOP + POP_TOP; - op(_BINARY_OP_SUBSCR_DICT, (dict_st, sub_st -- res)) { + tier2 op(_BINARY_OP_SUBSCR_DICT_KNOWN_HASH, (dict_st, sub_st, hash/4 -- res, ds, ss)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(Py_TYPE(dict)->tp_as_mapping->mp_subscript == _PyDict_Subscript); + STAT_INC(BINARY_OP, hit); + PyObject *res_o = _PyDict_SubscriptKnownHash(dict, sub, (Py_hash_t)hash); + if (res_o == NULL) { + ERROR_NO_POP(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + ds = dict_st; + ss = sub_st; + INPUTS_DEAD(); + } - assert(PyDict_CheckExact(dict)); + op(_BINARY_OP_SUBSCR_DICT, (dict_st, sub_st -- res, ds, ss)) { + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(Py_TYPE(dict)->tp_as_mapping->mp_subscript == _PyDict_Subscript); STAT_INC(BINARY_OP, hit); - PyObject *res_o; - int rc = PyDict_GetItemRef(dict, sub, &res_o); - if (rc == 0) { - _PyErr_SetKeyError(sub); + PyObject *res_o = _PyDict_Subscript(dict, sub); + if (res_o == NULL) { + ERROR_NO_POP(); } - DECREF_INPUTS(); - ERROR_IF(rc <= 0); // not found or error res = PyStackRef_FromPyObjectSteal(res_o); + ds = dict_st; + ss = sub_st; + INPUTS_DEAD(); } op(_BINARY_OP_SUBSCR_CHECK_FUNC, (container, unused -- container, unused, getitem)) { PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); - DEOPT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)); + EXIT_IF(!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)); PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; PyObject *getitem_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(ht->_spec_cache.getitem); - DEOPT_IF(getitem_o == NULL); + EXIT_IF(getitem_o == NULL); assert(PyFunction_Check(getitem_o)); uint32_t cached_version = FT_ATOMIC_LOAD_UINT32_RELAXED(ht->_spec_cache.getitem_version); - DEOPT_IF(((PyFunctionObject *)getitem_o)->func_version != cached_version); + EXIT_IF(((PyFunctionObject *)getitem_o)->func_version != cached_version); PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem_o); assert(code->co_argcount == 2); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); + EXIT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); getitem = PyStackRef_FromPyObjectNew(getitem_o); - STAT_INC(BINARY_OP, hit); } op(_BINARY_OP_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame)) { + STAT_INC(BINARY_OP, hit); _PyInterpreterFrame* pushed_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); pushed_frame->localsplus[0] = container; pushed_frame->localsplus[1] = sub; @@ -1076,6 +1365,7 @@ dummy_func( } macro(BINARY_OP_SUBSCR_GETITEM) = + _RECORD_NOS + unused/5 + // Skip over the counter and cache _CHECK_PEP_523 + _BINARY_OP_SUBSCR_CHECK_FUNC + @@ -1100,7 +1390,7 @@ dummy_func( }; specializing op(_SPECIALIZE_STORE_SUBSCR, (counter/1, container, sub -- container, sub)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_StoreSubscr(container, sub, next_instr); @@ -1108,7 +1398,7 @@ dummy_func( } OPCODE_DEFERRED_INC(STORE_SUBSCR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } op(_STORE_SUBSCR, (v, container, sub -- )) { @@ -1121,21 +1411,23 @@ dummy_func( macro(STORE_SUBSCR) = _SPECIALIZE_STORE_SUBSCR + _STORE_SUBSCR; macro(STORE_SUBSCR_LIST_INT) = - _GUARD_TOS_INT + _GUARD_NOS_LIST + unused/1 + _STORE_SUBSCR_LIST_INT; + _GUARD_TOS_INT + _GUARD_NOS_LIST + unused/1 + _STORE_SUBSCR_LIST_INT + _POP_TOP_INT + POP_TOP; - op(_STORE_SUBSCR_LIST_INT, (value, list_st, sub_st -- )) { + op(_STORE_SUBSCR_LIST_INT, (value, list_st, sub_st -- ls, ss)) { PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); assert(PyLong_CheckExact(sub)); assert(PyList_CheckExact(list)); - // Ensure nonnegative, zero-or-one-digit ints. - DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)); - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub); DEOPT_IF(!LOCK_OBJECT(list)); + Py_ssize_t len = PyList_GET_SIZE(list); // Ensure index < len(list) - if (index >= PyList_GET_SIZE(list)) { + if (index < 0) { + index += len; + } + if (index < 0 || index >= len) { UNLOCK_OBJECT(list); DEOPT_IF(true); } @@ -1146,25 +1438,46 @@ dummy_func( PyStackRef_AsPyObjectSteal(value)); assert(old_value != NULL); UNLOCK_OBJECT(list); // unlock before decrefs! - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - DEAD(sub_st); - PyStackRef_CLOSE(list_st); + INPUTS_DEAD(); + ls = list_st; + ss = sub_st; Py_DECREF(old_value); } macro(STORE_SUBSCR_DICT) = - _GUARD_NOS_DICT + unused/1 + _STORE_SUBSCR_DICT; + _RECORD_NOS_TYPE + + _GUARD_NOS_DICT_STORE_SUBSCRIPT + unused/1 + _STORE_SUBSCR_DICT + POP_TOP; - op(_STORE_SUBSCR_DICT, (value, dict_st, sub -- )) { + op(_STORE_SUBSCR_DICT, (value, dict_st, sub -- st)) { PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - - assert(PyDict_CheckExact(dict)); + assert(Py_TYPE(dict)->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript); STAT_INC(STORE_SUBSCR, hit); int err = _PyDict_SetItem_Take2((PyDictObject *)dict, PyStackRef_AsPyObjectSteal(sub), PyStackRef_AsPyObjectSteal(value)); - PyStackRef_CLOSE(dict_st); - ERROR_IF(err); + if (err) { + PyStackRef_CLOSE(dict_st); + ERROR_IF(1); + } + DEAD(dict_st); + st = dict_st; + } + + tier2 op(_STORE_SUBSCR_DICT_KNOWN_HASH, (value, dict_st, sub, hash/4 -- st)) { + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(Py_TYPE(dict)->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript); + STAT_INC(STORE_SUBSCR, hit); + int err = _PyDict_SetItem_Take2_KnownHash((PyDictObject *)dict, + PyStackRef_AsPyObjectSteal(sub), + PyStackRef_AsPyObjectSteal(value), + (Py_hash_t)hash); + + if (err) { + PyStackRef_CLOSE(dict_st); + ERROR_IF(1); + } + DEAD(dict_st); + st = dict_st; } inst(DELETE_SUBSCR, (container, sub --)) { @@ -1175,25 +1488,39 @@ dummy_func( ERROR_IF(err); } - inst(CALL_INTRINSIC_1, (value -- res)) { + op(_CALL_INTRINSIC_1, (value -- res, v)) { assert(oparg <= MAX_INTRINSIC_1); PyObject *res_o = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, PyStackRef_AsPyObjectBorrow(value)); - PyStackRef_CLOSE(value); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } + v = value; + DEAD(value); res = PyStackRef_FromPyObjectSteal(res_o); } - inst(CALL_INTRINSIC_2, (value2_st, value1_st -- res)) { + macro(CALL_INTRINSIC_1) = _CALL_INTRINSIC_1 + POP_TOP; + + op(_CALL_INTRINSIC_2, (value2_st, value1_st -- res, vs1, vs2)) { assert(oparg <= MAX_INTRINSIC_2); PyObject *value1 = PyStackRef_AsPyObjectBorrow(value1_st); PyObject *value2 = PyStackRef_AsPyObjectBorrow(value2_st); PyObject *res_o = _PyIntrinsics_BinaryFunctions[oparg].func(tstate, value2, value1); - DECREF_INPUTS(); - ERROR_IF(res_o == NULL); + + if (res_o == NULL) { + ERROR_NO_POP(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + + vs1 = value1_st; + vs2 = value2_st; + INPUTS_DEAD(); } + macro(CALL_INTRINSIC_2) = _CALL_INTRINSIC_2 + POP_TOP + POP_TOP; + tier1 inst(RAISE_VARARGS, (args[oparg] -- )) { assert(oparg < 3); PyObject *cause = oparg == 2 ? PyStackRef_AsPyObjectSteal(args[1]) : NULL; @@ -1214,7 +1541,7 @@ dummy_func( tstate->current_frame = frame->previous; assert(!_PyErr_Occurred(tstate)); PyObject *result = PyStackRef_AsPyObjectSteal(retval); -#if !Py_TAIL_CALL_INTERP +#if !_Py_TAIL_CALL_INTERP assert(frame == &entry.frame); #endif #ifdef _Py_TIER2 @@ -1229,15 +1556,20 @@ dummy_func( return result; } + op(_MAKE_HEAP_SAFE, (value -- value)) { + value = PyStackRef_MakeHeapSafe(value); + } + // The stack effect here is a bit misleading. // retval is popped from the stack, but res // is pushed to a different frame, the callers' frame. - inst(RETURN_VALUE, (retval -- res)) { + op(_RETURN_VALUE, (retval -- res)) { assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - _PyStackRef temp = PyStackRef_MakeHeapSafe(retval); + _PyStackRef temp = retval; DEAD(retval); SAVE_STACK(); assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); _Py_LeaveRecursiveCallPy(tstate); // GH-99729: We need to unlink the frame *before* clearing it: _PyInterpreterFrame *dying = frame; @@ -1249,6 +1581,10 @@ dummy_func( LLTRACE_RESUME_FRAME(); } + macro(RETURN_VALUE) = + _MAKE_HEAP_SAFE + + _RETURN_VALUE; + tier1 op(_RETURN_VALUE_EVENT, (val -- val)) { int err = _Py_call_instrumentation_arg( tstate, PY_MONITORING_EVENT_PY_RETURN, @@ -1258,7 +1594,8 @@ dummy_func( macro(INSTRUMENTED_RETURN_VALUE) = _RETURN_VALUE_EVENT + - RETURN_VALUE; + _MAKE_HEAP_SAFE + + _RETURN_VALUE; inst(GET_AITER, (obj -- iter)) { unaryfunc getter = NULL; @@ -1313,10 +1650,12 @@ dummy_func( family(SEND, INLINE_CACHE_ENTRIES_SEND) = { SEND_GEN, + SEND_VIRTUAL, + SEND_ASYNC_GEN, }; - specializing op(_SPECIALIZE_SEND, (counter/1, receiver, unused -- receiver, unused)) { - #if ENABLE_SPECIALIZATION_FT + specializing op(_SPECIALIZE_SEND, (counter/1, receiver, unused, unused -- receiver, unused, unused)) { + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_Send(receiver, next_instr); @@ -1324,23 +1663,21 @@ dummy_func( } OPCODE_DEFERRED_INC(SEND); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } - op(_SEND, (receiver, v -- receiver, retval)) { + tier1 op(_SEND, (receiver, null_or_index, v -- receiver, null_or_index, retval)) { PyObject *receiver_o = PyStackRef_AsPyObjectBorrow(receiver); - PyObject *retval_o; assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - if ((tstate->interp->eval_frame == NULL) && + if (!IS_PEP523_HOOKED(tstate) && (Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) && - ((PyGenObject *)receiver_o)->gi_frame_state < FRAME_EXECUTING) + gen_try_set_executing((PyGenObject *)receiver_o)) { PyGenObject *gen = (PyGenObject *)receiver_o; _PyInterpreterFrame *gen_frame = &gen->gi_iframe; _PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v)); DEAD(v); SYNC_SP(); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX); @@ -1349,44 +1686,42 @@ dummy_func( gen_frame->previous = frame; DISPATCH_INLINED(gen_frame); } - if (PyStackRef_IsNone(v) && PyIter_Check(receiver_o)) { - retval_o = Py_TYPE(receiver_o)->tp_iternext(receiver_o); + if (!PyStackRef_IsNull(null_or_index) && PyStackRef_IsNone(v)) { + _PyStackRef item = _PyForIter_VirtualIteratorNext(tstate, frame, receiver, &null_or_index); + if (!PyStackRef_IsValid(item)) { + if (PyStackRef_IsError(item)) { + ERROR_NO_POP(); + } + JUMPBY(oparg); + DISPATCH(); + } + retval = item; + DEAD(v); } else { - retval_o = PyObject_CallMethodOneArg(receiver_o, - &_Py_ID(send), - PyStackRef_AsPyObjectBorrow(v)); - } - if (retval_o == NULL) { - int matches = _PyErr_ExceptionMatches(tstate, PyExc_StopIteration); - if (matches) { - _PyEval_MonitorRaise(tstate, frame, this_instr); + PyObject *v_o = PyStackRef_AsPyObjectBorrow(v); + PySendResultPair res = _PyIter_Send(receiver_o, v_o); + if (res.kind == PYGEN_ERROR) { + ERROR_NO_POP(); } - int err = _PyGen_FetchStopIterationValue(&retval_o); - if (err == 0) { - assert(retval_o != NULL); + PyStackRef_CLOSE(v); + retval = PyStackRef_FromPyObjectSteal(res.object); + if (res.kind == PYGEN_RETURN) { JUMPBY(oparg); } - else { - PyStackRef_CLOSE(v); - ERROR_IF(true); - } } - PyStackRef_CLOSE(v); - retval = PyStackRef_FromPyObjectSteal(retval_o); } macro(SEND) = _SPECIALIZE_SEND + _SEND; - op(_SEND_GEN_FRAME, (receiver, v -- receiver, gen_frame)) { + op(_SEND_GEN_FRAME, (receiver, null, v -- receiver, null, gen_frame)) { PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver); - DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type); - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING); + EXIT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type); + EXIT_IF(!gen_try_set_executing((PyGenObject *)gen)); STAT_INC(SEND, hit); _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; _PyFrame_StackPush(pushed_frame, PyStackRef_MakeHeapSafe(v)); DEAD(v); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX); @@ -1397,11 +1732,118 @@ dummy_func( macro(SEND_GEN) = unused/1 + + _RECORD_3OS_GEN_FUNC + _CHECK_PEP_523 + _SEND_GEN_FRAME + _PUSH_FRAME; - inst(YIELD_VALUE, (retval -- value)) { + op(_GUARD_TOS_IS_NONE, (val -- val)) { + EXIT_IF(!PyStackRef_IsNone(val)); + } + + macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER; + + op(_GUARD_NOS_NOT_NULL, (nos, unused -- nos, unused)) { + EXIT_IF(PyStackRef_IsNull(nos)); + } + + replaced op(_SEND_VIRTUAL, (iter, null_or_index, none -- iter, null_or_index, next)) { + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyObjectIndexPair next_index = Py_TYPE(iter_o)->_tp_iteritem(iter_o, index); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + ERROR_NO_POP(); + } + next = none; + DEAD(none); + JUMPBY(oparg); + DISPATCH(); + } + DEAD(none); + next = PyStackRef_FromPyObjectSteal(next_o); + null_or_index = PyStackRef_TagInt(index); + } + + op(_SEND_VIRTUAL_TIER_TWO, (iter, null_or_index, none -- iter, null_or_index, next)) { + assert(PyStackRef_IsNone(none)); + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyObjectIndexPair next_index = CALL_TP_ITERITEM_NO_ESCAPE(iter_o, index); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + ERROR_NO_POP(); + } + next = none; + DEAD(none); + EXIT_IF(true); + } + DEAD(none); + next = PyStackRef_FromPyObjectSteal(next_o); + null_or_index = PyStackRef_TagInt(index); + } + + macro(SEND_VIRTUAL) = + unused/1 + + _GUARD_TOS_IS_NONE + + _GUARD_NOS_NOT_NULL + + _SEND_VIRTUAL; + + op(_GUARD_3OS_ASYNC_GEN_ASEND, (iter, null_or_index, value -- iter, null_or_index, value)) { + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + EXIT_IF(!PyAsyncGenASend_CheckExact(iter_o)); + } + + replaced op(_SEND_ASYNC_GEN, (iter, null_in, v -- asend, null_out, retval)) { + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + assert(PyAsyncGenASend_CheckExact(iter_o)); + PyObject *val = PyStackRef_AsPyObjectBorrow(v); + PyObject *retval_o; + + PySendResult what = _PyAsyncGenASend_Send(iter_o, val, &retval_o); + if (what == PYGEN_ERROR) { + ERROR_NO_POP(); + } + PyStackRef_CLOSE(v); + asend = iter; + DEAD(iter); + null_out = null_in; + DEAD(null_in); + retval = PyStackRef_FromPyObjectSteal(retval_o); + if (what == PYGEN_RETURN) { + JUMPBY(oparg); + } + } + + macro(SEND_ASYNC_GEN) = + unused/1 + + _GUARD_3OS_ASYNC_GEN_ASEND + + _SEND_ASYNC_GEN; + + tier2 op(_SEND_ASYNC_GEN_TIER_TWO, (iter, null_in, v -- asend, null_out, retval)) { + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + assert(PyAsyncGenASend_CheckExact(iter_o)); + PyObject *val = PyStackRef_AsPyObjectBorrow(v); + PyObject *retval_o; + + PySendResult what = _PyAsyncGenASend_Send(iter_o, val, &retval_o); + if (what == PYGEN_ERROR) { + ERROR_NO_POP(); + } + PyStackRef_CLOSE(v); + asend = iter; + DEAD(iter); + null_out = null_in; + DEAD(null_in); + retval = PyStackRef_FromPyObjectSteal(retval_o); + EXIT_IF(what == PYGEN_RETURN); + } + + op(_YIELD_VALUE, (retval -- value)) { // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() // or throw() call. @@ -1410,32 +1852,38 @@ dummy_func( PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; _PyStackRef temp = retval; DEAD(retval); SAVE_STACK(); + DTRACE_FUNCTION_RETURN(); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); /* We don't know which of these is relevant here, so keep them equal */ assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); - #if TIER_ONE - assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE || - frame->instr_ptr->op.code == INSTRUMENTED_INSTRUCTION || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == SEND || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == FOR_ITER || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == INTERPRETER_EXIT || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == ENTER_EXECUTOR); + #if TIER_ONE && defined(Py_DEBUG) + if (!PyStackRef_IsNone(frame->f_executable)) { + Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); + assert(i >= 0 && i <= INT_MAX); + int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; + assert(opcode == SEND || opcode == FOR_ITER); + } #endif RELOAD_STACK(); LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); - value = PyStackRef_MakeHeapSafe(temp); + value = temp; LLTRACE_RESUME_FRAME(); } + macro(YIELD_VALUE) = + _MAKE_HEAP_SAFE + + _YIELD_VALUE; + tier1 op(_YIELD_VALUE_EVENT, (val -- val)) { int err = _Py_call_instrumentation_arg( tstate, PY_MONITORING_EVENT_PY_YIELD, @@ -1451,7 +1899,8 @@ dummy_func( macro(INSTRUMENTED_YIELD_VALUE) = _YIELD_VALUE_EVENT + - YIELD_VALUE; + _MAKE_HEAP_SAFE + + _YIELD_VALUE; inst(POP_EXCEPT, (exc_value -- )) { _PyErr_StackItem *exc_info = tstate->exc_info; @@ -1502,17 +1951,17 @@ dummy_func( macro(END_ASYNC_FOR) = _END_ASYNC_FOR; - tier1 inst(CLEANUP_THROW, (sub_iter, last_sent_val, exc_value_st -- none, value)) { + tier1 inst(CLEANUP_THROW, (sub_iter, null_in, last_sent_val, exc_value_st -- none, null_out, value)) { PyObject *exc_value = PyStackRef_AsPyObjectBorrow(exc_value_st); - #if !Py_TAIL_CALL_INTERP + #if !_Py_TAIL_CALL_INTERP assert(throwflag); #endif assert(exc_value && PyExceptionInstance_Check(exc_value)); - int matches = PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration); if (matches) { value = PyStackRef_FromPyObjectNew(((PyStopIterationObject *)exc_value)->value); DECREF_INPUTS(); + null_out = null_in; none = PyStackRef_None; } else { @@ -1525,12 +1974,12 @@ dummy_func( inst(LOAD_COMMON_CONSTANT, ( -- value)) { // Keep in sync with _common_constants in opcode.py assert(oparg < NUM_COMMON_CONSTANTS); - value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); + value = PyStackRef_DupImmortal(tstate->interp->common_consts[oparg]); } inst(LOAD_BUILD_CLASS, ( -- bc)) { - PyObject *bc_o; - int err = PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc_o); + int err; + PyObject *bc_o = _PyMapping_GetOptionalItem2(BUILTINS(), &_Py_ID(__build_class__), &err); ERROR_IF(err < 0); if (bc_o == NULL) { _PyErr_SetString(tstate, PyExc_NameError, @@ -1586,7 +2035,7 @@ dummy_func( }; specializing op(_SPECIALIZE_UNPACK_SEQUENCE, (counter/1, seq -- seq)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_UnpackSequence(seq, next_instr, oparg); @@ -1594,7 +2043,7 @@ dummy_func( } OPCODE_DEFERRED_INC(UNPACK_SEQUENCE); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ (void)seq; (void)counter; } @@ -1615,20 +2064,39 @@ dummy_func( assert(oparg == 2); PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); assert(PyTuple_CheckExact(seq_o)); - DEOPT_IF(PyTuple_GET_SIZE(seq_o) != 2); + EXIT_IF(PyTuple_GET_SIZE(seq_o) != 2); STAT_INC(UNPACK_SEQUENCE, hit); val0 = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq_o, 0)); val1 = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq_o, 1)); PyStackRef_CLOSE(seq); } + op(_UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE, (seq -- val1, val0)) { + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + STAT_INC(UNPACK_SEQUENCE, hit); + val0 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 0)); + val1 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 1)); + PyObject_GC_UnTrack(seq_o); + _PyStolenTuple_Free(seq_o); + } + + op(_UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE, (seq -- val2, val1, val0)) { + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + STAT_INC(UNPACK_SEQUENCE, hit); + val0 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 0)); + val1 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 1)); + val2 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 2)); + PyObject_GC_UnTrack(seq_o); + _PyStolenTuple_Free(seq_o); + } + macro(UNPACK_SEQUENCE_TUPLE) = _GUARD_TOS_TUPLE + unused/1 + _UNPACK_SEQUENCE_TUPLE; op(_UNPACK_SEQUENCE_TUPLE, (seq -- values[oparg])) { PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); assert(PyTuple_CheckExact(seq_o)); - DEOPT_IF(PyTuple_GET_SIZE(seq_o) != oparg); + EXIT_IF(PyTuple_GET_SIZE(seq_o) != oparg); STAT_INC(UNPACK_SEQUENCE, hit); PyObject **items = _PyTuple_ITEMS(seq_o); for (int i = oparg; --i >= 0; ) { @@ -1637,6 +2105,20 @@ dummy_func( DECREF_INPUTS(); } + op(_UNPACK_SEQUENCE_UNIQUE_TUPLE, (seq -- values[oparg])) { + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + assert(PyTuple_CheckExact(seq_o)); + assert(PyTuple_GET_SIZE(seq_o) == oparg); + assert(_PyObject_IsUniquelyReferenced(seq_o)); + STAT_INC(UNPACK_SEQUENCE, hit); + PyObject **items = _PyTuple_ITEMS(seq_o); + for (int i = oparg; --i >= 0; ) { + *values++ = PyStackRef_FromPyObjectSteal(items[i]); + } + PyObject_GC_UnTrack(seq_o); + _PyStolenTuple_Free(seq_o); + } + macro(UNPACK_SEQUENCE_LIST) = _GUARD_TOS_LIST + unused/1 + _UNPACK_SEQUENCE_LIST; @@ -1671,7 +2153,7 @@ dummy_func( }; specializing op(_SPECIALIZE_STORE_ATTR, (counter/1, owner -- owner)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); next_instr = this_instr; @@ -1680,7 +2162,7 @@ dummy_func( } OPCODE_DEFERRED_INC(STORE_ATTR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } op(_STORE_ATTR, (v, owner --)) { @@ -1733,8 +2215,9 @@ dummy_func( inst(LOAD_FROM_DICT_OR_GLOBALS, (mod_or_class_dict -- v)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *v_o; - int err = PyMapping_GetOptionalItem(PyStackRef_AsPyObjectBorrow(mod_or_class_dict), name, &v_o); + int err; + PyObject *v_o = _PyMapping_GetOptionalItem2(PyStackRef_AsPyObjectBorrow(mod_or_class_dict), name, &err); + PyStackRef_CLOSE(mod_or_class_dict); ERROR_IF(err < 0); if (v_o == NULL) { @@ -1753,15 +2236,21 @@ dummy_func( } ERROR_NO_POP(); } + + if (PyLazyImport_CheckExact(v_o)) { + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + Py_SETREF(v_o, l_v); + ERROR_IF(v_o == NULL); + } } else { /* Slow-path if globals or builtins is not a dict */ /* namespace 1: globals */ - int err = PyMapping_GetOptionalItem(GLOBALS(), name, &v_o); + v_o = _PyMapping_GetOptionalItem2(GLOBALS(), name, &err); ERROR_IF(err < 0); if (v_o == NULL) { /* namespace 2: builtins */ - int err = PyMapping_GetOptionalItem(BUILTINS(), name, &v_o); + v_o = _PyMapping_GetOptionalItem2(BUILTINS(), name, &err); ERROR_IF(err < 0); if (v_o == NULL) { _PyEval_FormatExcCheckArg( @@ -1770,6 +2259,11 @@ dummy_func( ERROR_IF(true); } } + if (PyLazyImport_CheckExact(v_o)) { + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + Py_SETREF(v_o, l_v); + ERROR_IF(v_o == NULL); + } } } v = PyStackRef_FromPyObjectSteal(v_o); @@ -1779,6 +2273,22 @@ dummy_func( PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); PyObject *v_o = _PyEval_LoadName(tstate, frame, name); ERROR_IF(v_o == NULL); + if (PyLazyImport_CheckExact(v_o)) { + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + // cannot early-decref v_o as it may cause a side-effect on l_v + if (l_v == NULL) { + Py_DECREF(v_o); + ERROR_IF(true); + } + int err = PyDict_SetItem(GLOBALS(), name, l_v); + if (err < 0) { + Py_DECREF(v_o); + Py_DECREF(l_v); + ERROR_IF(true); + } + Py_SETREF(v_o, l_v); + } + v = PyStackRef_FromPyObjectSteal(v_o); } @@ -1788,7 +2298,7 @@ dummy_func( }; specializing op(_SPECIALIZE_LOAD_GLOBAL, (counter/1 -- )) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); next_instr = this_instr; @@ -1797,13 +2307,14 @@ dummy_func( } OPCODE_DEFERRED_INC(LOAD_GLOBAL); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } // res[1] because we need a pointer to res to pass it to _PyEval_LoadGlobalStackRef op(_LOAD_GLOBAL, ( -- res[1])) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); _PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res); + ERROR_IF(PyStackRef_IsNull(*res)); } @@ -1826,7 +2337,7 @@ dummy_func( DEOPT_IF(!PyDict_CheckExact(dict)); PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version); - assert(DK_IS_UNICODE(keys)); + assert(keys->dk_kind == DICT_KEYS_UNICODE); } op(_LOAD_GLOBAL_MODULE, (version/1, unused/1, index/1 -- res)) @@ -1835,7 +2346,7 @@ dummy_func( DEOPT_IF(!PyDict_CheckExact(dict)); PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version); - assert(DK_IS_UNICODE(keys)); + assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); assert(index < DK_SIZE(keys)); PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); @@ -1855,7 +2366,7 @@ dummy_func( DEOPT_IF(!PyDict_CheckExact(dict)); PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version); - assert(DK_IS_UNICODE(keys)); + assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); DEOPT_IF(res_o == NULL); @@ -1920,14 +2431,14 @@ dummy_func( } inst(LOAD_FROM_DICT_OR_DEREF, (class_dict_st -- value)) { - PyObject *value_o; PyObject *name; PyObject *class_dict = PyStackRef_AsPyObjectBorrow(class_dict_st); assert(class_dict); assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus); name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); - int err = PyMapping_GetOptionalItem(class_dict, name, &value_o); + int err; + PyObject* value_o = _PyMapping_GetOptionalItem2(class_dict, name, &err); if (err < 0) { ERROR_NO_POP(); } @@ -1972,14 +2483,8 @@ dummy_func( } inst(BUILD_STRING, (pieces[oparg] -- str)) { - STACKREFS_TO_PYOBJECTS(pieces, oparg, pieces_o); - if (CONVERSION_FAILED(pieces_o)) { - DECREF_INPUTS(); - ERROR_IF(true); - } - PyObject *str_o = _PyUnicode_JoinArray(&_Py_STR(empty), pieces_o, oparg); - STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o); - DECREF_INPUTS(); + PyObject *str_o = _Py_BuildString_StackRefSteal(pieces, oparg); + DEAD(pieces); ERROR_IF(str_o == NULL); str = PyStackRef_FromPyObjectSteal(str_o); } @@ -2036,7 +2541,7 @@ dummy_func( list = PyStackRef_FromPyObjectStealMortal(list_o); } - inst(LIST_EXTEND, (list_st, unused[oparg-1], iterable_st -- list_st, unused[oparg-1])) { + op(_LIST_EXTEND, (list_st, unused[oparg-1], iterable_st -- list_st, unused[oparg-1], i)) { PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); PyObject *iterable = PyStackRef_AsPyObjectBorrow(iterable_st); @@ -2051,20 +2556,27 @@ dummy_func( "Value after * must be an iterable, not %.200s", Py_TYPE(iterable)->tp_name); } - PyStackRef_CLOSE(iterable_st); - ERROR_IF(true); + ERROR_NO_POP(); } assert(Py_IsNone(none_val)); - PyStackRef_CLOSE(iterable_st); + i = iterable_st; + DEAD(iterable_st); } - inst(SET_UPDATE, (set, unused[oparg-1], iterable -- set, unused[oparg-1])) { + macro(LIST_EXTEND) = _LIST_EXTEND + POP_TOP; + + op(_SET_UPDATE, (set, unused[oparg-1], iterable -- set, unused[oparg-1], i)) { int err = _PySet_Update(PyStackRef_AsPyObjectBorrow(set), PyStackRef_AsPyObjectBorrow(iterable)); - PyStackRef_CLOSE(iterable); - ERROR_IF(err < 0); + if (err < 0) { + ERROR_NO_POP(); + } + i = iterable; + DEAD(iterable); } + macro(SET_UPDATE) = _SET_UPDATE + POP_TOP; + inst(BUILD_SET, (values[oparg] -- set)) { PyObject *set_o = PySet_New(NULL); if (set_o == NULL) { @@ -2094,30 +2606,22 @@ dummy_func( } inst(BUILD_MAP, (values[oparg*2] -- map)) { - STACKREFS_TO_PYOBJECTS(values, oparg*2, values_o); - if (CONVERSION_FAILED(values_o)) { - DECREF_INPUTS(); - ERROR_IF(true); - } - PyObject *map_o = _PyDict_FromItems( - values_o, 2, - values_o+1, 2, - oparg); - STACKREFS_TO_PYOBJECTS_CLEANUP(values_o); - DECREF_INPUTS(); + + PyObject *map_o = _Py_BuildMap_StackRefSteal(values, oparg); + DEAD(values); ERROR_IF(map_o == NULL); map = PyStackRef_FromPyObjectStealMortal(map_o); } inst(SETUP_ANNOTATIONS, (--)) { - PyObject *ann_dict; if (LOCALS() == NULL) { _PyErr_Format(tstate, PyExc_SystemError, "no locals found when setting up annotations"); ERROR_IF(true); } /* check if __annotations__ in locals()... */ - int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict); + int err; + PyObject* ann_dict = _PyMapping_GetOptionalItem2(LOCALS(), &_Py_ID(__annotations__), &err); ERROR_IF(err < 0); if (ann_dict == NULL) { ann_dict = PyDict_New(); @@ -2136,7 +2640,7 @@ dummy_func( NOP, }; - inst(DICT_UPDATE, (dict, unused[oparg - 1], update -- dict, unused[oparg - 1])) { + op(_DICT_UPDATE, (dict, unused[oparg - 1], update -- dict, unused[oparg - 1], upd)) { PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); @@ -2144,30 +2648,44 @@ dummy_func( if (err < 0) { int matches = _PyErr_ExceptionMatches(tstate, PyExc_AttributeError); if (matches) { - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object is not a mapping", - Py_TYPE(update_o)->tp_name); + PyObject *exc = _PyErr_GetRaisedException(tstate); + int has_keys = PyObject_HasAttrWithError(update_o, &_Py_ID(keys)); + if (has_keys == 0) { + _PyErr_Format(tstate, PyExc_TypeError, + "'%T' object is not a mapping", + update_o); + Py_DECREF(exc); + } + else { + _PyErr_ChainExceptions1(exc); + } } - PyStackRef_CLOSE(update); - ERROR_IF(true); + ERROR_NO_POP(); } - PyStackRef_CLOSE(update); + upd = update; + DEAD(update); } - inst(DICT_MERGE, (callable, unused, unused, dict, unused[oparg - 1], update -- callable, unused, unused, dict, unused[oparg - 1])) { + macro(DICT_UPDATE) = _DICT_UPDATE + POP_TOP; + + op(_DICT_MERGE, (callable, unused, unused, dict, unused[oparg - 1], update -- callable, unused, unused, dict, unused[oparg - 1], u)) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); + PyObject *dupkey = NULL; - int err = _PyDict_MergeEx(dict_o, update_o, 2); + int err = _PyDict_MergeUniq(dict_o, update_o, &dupkey); if (err < 0) { - _PyEval_FormatKwargsError(tstate, callable_o, update_o); - PyStackRef_CLOSE(update); - ERROR_IF(true); + _PyEval_FormatKwargsError(tstate, callable_o, update_o, dupkey); + Py_XDECREF(dupkey); + ERROR_NO_POP(); } - PyStackRef_CLOSE(update); + u = update; + DEAD(update); } + macro(DICT_MERGE) = _DICT_MERGE + POP_TOP; + inst(MAP_ADD, (dict_st, unused[oparg - 1], key, value -- dict_st, unused[oparg - 1])) { PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); assert(PyDict_CheckExact(dict)); @@ -2192,7 +2710,7 @@ dummy_func( }; specializing op(_SPECIALIZE_LOAD_SUPER_ATTR, (counter/1, global_super_st, class_st, unused -- global_super_st, class_st, unused)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION int load_method = oparg & 1; if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; @@ -2201,7 +2719,7 @@ dummy_func( } OPCODE_DEFERRED_INC(LOAD_SUPER_ATTR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } tier1 op(_LOAD_SUPER_ATTR, (global_super_st, class_st, self_st -- attr)) { @@ -2221,8 +2739,12 @@ dummy_func( } // we make no attempt to optimize here; specializations should // handle any case whose performance we care about - PyObject *stack[] = {class, self}; - PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); + PyObject *super; + { + // scope to tell MSVC that stack is not escaping + PyObject *stack[] = {class, self}; + super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); + } if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; if (super == NULL) { @@ -2259,8 +2781,8 @@ dummy_func( PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); assert(!(oparg & 1)); - DEOPT_IF(global_super != (PyObject *)&PySuper_Type); - DEOPT_IF(!PyType_Check(class)); + EXIT_IF(global_super != (PyObject *)&PySuper_Type); + EXIT_IF(!PyType_Check(class)); STAT_INC(LOAD_SUPER_ATTR, hit); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); PyObject *attr = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL); @@ -2269,20 +2791,42 @@ dummy_func( attr_st = PyStackRef_FromPyObjectSteal(attr); } - inst(LOAD_SUPER_ATTR_METHOD, (unused/1, global_super_st, class_st, self_st -- attr, self_or_null)) { + macro(LOAD_SUPER_ATTR_METHOD) = + _RECORD_NOS + + unused/1 + + _GUARD_LOAD_SUPER_ATTR_METHOD + + _LOAD_SUPER_ATTR_METHOD; + + op(_GUARD_NOS_TYPE_VERSION, (type_version/2, nos, unused -- nos, unused)) { + PyTypeObject *tp = (PyTypeObject *)PyStackRef_AsPyObjectBorrow(nos); + assert(type_version != 0); + EXIT_IF(!PyType_Check((PyObject *)tp)); + EXIT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version); + } + + op(_GUARD_LOAD_SUPER_ATTR_METHOD, (global_super_st, class_st, unused -- global_super_st, class_st, unused)) { PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + assert(oparg & 1); + EXIT_IF(global_super != (PyObject *)&PySuper_Type); + EXIT_IF(!PyType_Check(class)); + } + + op(_LOAD_SUPER_ATTR_METHOD, (global_super_st, class_st, self_st -- attr, self_or_null)) { PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); - assert(oparg & 1); - DEOPT_IF(global_super != (PyObject *)&PySuper_Type); - DEOPT_IF(!PyType_Check(class)); STAT_INC(LOAD_SUPER_ATTR, hit); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); PyTypeObject *cls = (PyTypeObject *)class; int method_found = 0; - PyObject *attr_o = _PySuper_Lookup(cls, self, name, - Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); + PyObject *attr_o; + { + // scope to tell MSVC that method_found_ptr is not escaping + int *method_found_ptr = &method_found; + attr_o = _PySuper_Lookup(cls, self, name, + Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? method_found_ptr : NULL); + } if (attr_o == NULL) { ERROR_NO_POP(); } @@ -2315,7 +2859,7 @@ dummy_func( }; specializing op(_SPECIALIZE_LOAD_ATTR, (counter/1, owner -- owner)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); next_instr = this_instr; @@ -2324,42 +2868,22 @@ dummy_func( } OPCODE_DEFERRED_INC(LOAD_ATTR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } - op(_LOAD_ATTR, (owner -- attr[1], self_or_null[oparg&1])) { + op(_LOAD_ATTR, (owner -- attr, self_or_null[oparg&1])) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); if (oparg & 1) { /* Designed to work in tandem with CALL, pushes two values. */ - *attr = PyStackRef_NULL; - int is_meth = _PyObject_GetMethodStackRef(tstate, PyStackRef_AsPyObjectBorrow(owner), name, attr); - if (is_meth) { - /* We can bypass temporary bound method object. - meth is unbound method and obj is self. - meth | self | arg1 | ... | argN - */ - assert(!PyStackRef_IsNull(*attr)); // No errors on this branch - self_or_null[0] = owner; // Transfer ownership - DEAD(owner); - } - else { - /* meth is not an unbound method (but a regular attr, or - something was returned by a descriptor protocol). Set - the second element of the stack to NULL, to signal - CALL that it's not a method call. - meth | NULL | arg1 | ... | argN - */ - PyStackRef_CLOSE(owner); - ERROR_IF(PyStackRef_IsNull(*attr)); - self_or_null[0] = PyStackRef_NULL; - } + attr = _Py_LoadAttr_StackRefSteal(tstate, owner, name, self_or_null); + DEAD(owner); + ERROR_IF(PyStackRef_IsNull(attr)); } else { /* Classic, pushes one value. */ - PyObject *attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name); + attr = _PyObject_GetAttrStackRef(PyStackRef_AsPyObjectBorrow(owner), name); PyStackRef_CLOSE(owner); - ERROR_IF(attr_o == NULL); - *attr = PyStackRef_FromPyObjectSteal(attr_o); + ERROR_IF(PyStackRef_IsNull(attr)); } } @@ -2374,10 +2898,9 @@ dummy_func( EXIT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version); } - op(_GUARD_TYPE_VERSION_AND_LOCK, (type_version/2, owner -- owner)) { + op(_GUARD_TYPE_VERSION_LOCKED, (type_version/2, owner -- owner)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(type_version != 0); - EXIT_IF(!LOCK_OBJECT(owner_o)); PyTypeObject *tp = Py_TYPE(owner_o); if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { UNLOCK_OBJECT(owner_o); @@ -2385,14 +2908,19 @@ dummy_func( } } + op(_GUARD_TYPE, (type/4, owner -- owner)) { + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + EXIT_IF(tp != (PyTypeObject *)type); + } + op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_dictoffset < 0); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)); + EXIT_IF(!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)); } - op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr)) { + op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr, o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); @@ -2406,113 +2934,124 @@ dummy_func( attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); - PyStackRef_CLOSE(owner); + o = owner; + DEAD(owner); } macro(LOAD_ATTR_INSTANCE_VALUE) = unused/1 + // Skip over the counter + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + _CHECK_MANAGED_OBJECT_HAS_VALUES + _LOAD_ATTR_INSTANCE_VALUE + + POP_TOP + unused/5 + _PUSH_NULL_CONDITIONAL; - op(_LOAD_ATTR_MODULE, (dict_version/2, index/1, owner -- attr)) { + op(_LOAD_ATTR_MODULE, (dict_version/2, index/1, owner -- attr, o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - DEOPT_IF(Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro); + EXIT_IF(Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; assert(dict != NULL); PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); - DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version); + EXIT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version); assert(keys->dk_kind == DICT_KEYS_UNICODE); assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries)); PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index; PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); - DEOPT_IF(attr_o == NULL); + EXIT_IF(attr_o == NULL); #ifdef Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); if (!increfed) { - DEOPT_IF(true); + EXIT_IF(true); } #else attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); - PyStackRef_CLOSE(owner); + o = owner; + DEAD(owner); } macro(LOAD_ATTR_MODULE) = unused/1 + _LOAD_ATTR_MODULE + + POP_TOP + unused/5 + _PUSH_NULL_CONDITIONAL; - op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr)) { + op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr, o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner_o); - DEOPT_IF(dict == NULL); + EXIT_IF(dict == NULL); PyDictKeysObject *dk = FT_ATOMIC_LOAD_PTR(dict->ma_keys); assert(PyDict_CheckExact((PyObject *)dict)); #ifdef Py_GIL_DISABLED - DEOPT_IF(!_Py_IsOwnedByCurrentThread((PyObject *)dict) && !_PyObject_GC_IS_SHARED(dict)); + EXIT_IF(!_Py_IsOwnedByCurrentThread((PyObject *)dict) && !_PyObject_GC_IS_SHARED(dict)); #endif PyObject *attr_o; if (hint >= (size_t)FT_ATOMIC_LOAD_SSIZE_RELAXED(dk->dk_nentries)) { - DEOPT_IF(true); + EXIT_IF(true); } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); if (dk->dk_kind != DICT_KEYS_UNICODE) { - DEOPT_IF(true); + EXIT_IF(true); } PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dk) + hint; if (FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_key) != name) { - DEOPT_IF(true); + EXIT_IF(true); } attr_o = FT_ATOMIC_LOAD_PTR(ep->me_value); if (attr_o == NULL) { - DEOPT_IF(true); + EXIT_IF(true); } STAT_INC(LOAD_ATTR, hit); #ifdef Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); if (!increfed) { - DEOPT_IF(true); + EXIT_IF(true); } #else attr = PyStackRef_FromPyObjectNew(attr_o); #endif - PyStackRef_CLOSE(owner); + o = owner; + DEAD(owner); } macro(LOAD_ATTR_WITH_HINT) = unused/1 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + _LOAD_ATTR_WITH_HINT + + POP_TOP + unused/5 + _PUSH_NULL_CONDITIONAL; - op(_LOAD_ATTR_SLOT, (index/1, owner -- attr)) { + op(_LOAD_ATTR_SLOT, (index/1, owner -- attr, o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); PyObject **addr = (PyObject **)((char *)owner_o + index); PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); - DEOPT_IF(attr_o == NULL); + EXIT_IF(attr_o == NULL); #ifdef Py_GIL_DISABLED int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); - DEOPT_IF(!increfed); + EXIT_IF(!increfed); #else attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); - DECREF_INPUTS(); + o = owner; + DEAD(owner); } macro(LOAD_ATTR_SLOT) = unused/1 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + _LOAD_ATTR_SLOT + // NOTE: This action may also deopt + POP_TOP + unused/5 + _PUSH_NULL_CONDITIONAL; @@ -2533,6 +3072,7 @@ dummy_func( macro(LOAD_ATTR_CLASS) = unused/1 + + _RECORD_TOS + _CHECK_ATTR_CLASS + unused/2 + _LOAD_ATTR_CLASS + @@ -2540,20 +3080,19 @@ dummy_func( macro(LOAD_ATTR_CLASS_WITH_METACLASS_CHECK) = unused/1 + - _CHECK_ATTR_CLASS + + _RECORD_TOS + _GUARD_TYPE_VERSION + + _CHECK_ATTR_CLASS + _LOAD_ATTR_CLASS + _PUSH_NULL_CONDITIONAL; - op(_LOAD_ATTR_PROPERTY_FRAME, (fget/4, owner -- new_frame)) { + op(_LOAD_ATTR_PROPERTY_FRAME, (func_version/2, fget/4, owner -- new_frame)) { assert((oparg & 1) == 0); assert(Py_IS_TYPE(fget, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)fget; + EXIT_IF(f->func_version != func_version); PyCodeObject *code = (PyCodeObject *)f->func_code; - DEOPT_IF((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED); - DEOPT_IF(code->co_kwonlyargcount); - DEOPT_IF(code->co_argcount != 1); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); + EXIT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); STAT_INC(LOAD_ATTR, hit); _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(fget), 1, frame); pushed_frame->localsplus[0] = owner; @@ -2563,42 +3102,41 @@ dummy_func( macro(LOAD_ATTR_PROPERTY) = unused/1 + - _CHECK_PEP_523 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + - unused/2 + + _CHECK_PEP_523 + _LOAD_ATTR_PROPERTY_FRAME + _SAVE_RETURN_OFFSET + _PUSH_FRAME; - inst(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, (unused/1, type_version/2, func_version/2, getattribute/4, owner -- unused)) { - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - + op(_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME, (func_version/2, getattribute/4, owner -- new_frame)) { assert((oparg & 1) == 0); - DEOPT_IF(tstate->interp->eval_frame); - PyTypeObject *cls = Py_TYPE(owner_o); - assert(type_version != 0); - DEOPT_IF(FT_ATOMIC_LOAD_UINT_RELAXED(cls->tp_version_tag) != type_version); assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)getattribute; assert(func_version != 0); - DEOPT_IF(f->func_version != func_version); + EXIT_IF(f->func_version != func_version); PyCodeObject *code = (PyCodeObject *)f->func_code; assert(code->co_argcount == 2); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); + EXIT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); STAT_INC(LOAD_ATTR, hit); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked( + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked( tstate, PyStackRef_FromPyObjectNew(f), 2, frame); - new_frame->localsplus[0] = owner; + pushed_frame->localsplus[0] = owner; DEAD(owner); - // Manipulate stack directly because we exit with DISPATCH_INLINED(). - SYNC_SP(); - new_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name); - frame->return_offset = INSTRUCTION_SIZE; - DISPATCH_INLINED(new_frame); + pushed_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name); + new_frame = PyStackRef_Wrap(pushed_frame); } + macro(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) = + unused/1 + + _RECORD_TOS_TYPE + + _GUARD_TYPE_VERSION + + _CHECK_PEP_523 + + _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME + + _SAVE_RETURN_OFFSET + + _PUSH_FRAME; + op(_GUARD_DORV_NO_DICT, (owner -- owner)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); @@ -2611,7 +3149,7 @@ dummy_func( } } - op(_STORE_ATTR_INSTANCE_VALUE, (offset/1, value, owner --)) { + op(_STORE_ATTR_INSTANCE_VALUE, (offset/1, value, owner -- o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); STAT_INC(STORE_ATTR, hit); @@ -2625,17 +3163,25 @@ dummy_func( _PyDictValues_AddToInsertionOrder(values, index); } UNLOCK_OBJECT(owner_o); - PyStackRef_CLOSE(owner); + o = owner; + DEAD(owner); Py_XDECREF(old_value); } + op(_LOCK_OBJECT, (value -- value)) { + DEOPT_IF(!LOCK_OBJECT(PyStackRef_AsPyObjectBorrow(value))); + } + macro(STORE_ATTR_INSTANCE_VALUE) = unused/1 + - _GUARD_TYPE_VERSION_AND_LOCK + + _RECORD_TOS_TYPE + + _LOCK_OBJECT + + _GUARD_TYPE_VERSION_LOCKED + _GUARD_DORV_NO_DICT + - _STORE_ATTR_INSTANCE_VALUE; + _STORE_ATTR_INSTANCE_VALUE + + POP_TOP; - op(_STORE_ATTR_WITH_HINT, (hint/1, value, owner --)) { + op(_STORE_ATTR_WITH_HINT, (hint/1, value, owner -- o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner_o); @@ -2644,7 +3190,7 @@ dummy_func( assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); if (hint >= (size_t)dict->ma_keys->dk_nentries || - !DK_IS_UNICODE(dict->ma_keys)) { + dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) { UNLOCK_OBJECT(dict); DEOPT_IF(true); } @@ -2658,23 +3204,26 @@ dummy_func( UNLOCK_OBJECT(dict); DEOPT_IF(true); } - _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(dict); // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault, // when dict only holds the strong reference to value in ep->me_value. STAT_INC(STORE_ATTR, hit); - PyStackRef_CLOSE(owner); + o = owner; + DEAD(owner); Py_XDECREF(old_value); } macro(STORE_ATTR_WITH_HINT) = unused/1 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + - _STORE_ATTR_WITH_HINT; + _STORE_ATTR_WITH_HINT + + POP_TOP; - op(_STORE_ATTR_SLOT, (index/1, value, owner --)) { + op(_STORE_ATTR_SLOT, (index/1, value, owner -- o)) { PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); DEOPT_IF(!LOCK_OBJECT(owner_o)); @@ -2683,14 +3232,17 @@ dummy_func( PyObject *old_value = *(PyObject **)addr; FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(owner_o); - PyStackRef_CLOSE(owner); + INPUTS_DEAD(); + o = owner; Py_XDECREF(old_value); } macro(STORE_ATTR_SLOT) = unused/1 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + - _STORE_ATTR_SLOT; + _STORE_ATTR_SLOT + + POP_TOP; family(COMPARE_OP, INLINE_CACHE_ENTRIES_COMPARE_OP) = { COMPARE_OP_FLOAT, @@ -2699,7 +3251,7 @@ dummy_func( }; specializing op(_SPECIALIZE_COMPARE_OP, (counter/1, left, right -- left, right)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_CompareOp(left, right, next_instr, oparg); @@ -2707,7 +3259,7 @@ dummy_func( } OPCODE_DEFERRED_INC(COMPARE_OP); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } op(_COMPARE_OP, (left, right -- res)) { @@ -2732,15 +3284,15 @@ dummy_func( macro(COMPARE_OP) = _SPECIALIZE_COMPARE_OP + _COMPARE_OP; macro(COMPARE_OP_FLOAT) = - _GUARD_TOS_FLOAT + _GUARD_NOS_FLOAT + unused/1 + _COMPARE_OP_FLOAT; + _GUARD_TOS_FLOAT + _GUARD_NOS_FLOAT + unused/1 + _COMPARE_OP_FLOAT + _POP_TOP_FLOAT + _POP_TOP_FLOAT; macro(COMPARE_OP_INT) = - _GUARD_TOS_INT + _GUARD_NOS_INT + unused/1 + _COMPARE_OP_INT; + _GUARD_TOS_INT + _GUARD_NOS_INT + unused/1 + _COMPARE_OP_INT + _POP_TOP_INT + _POP_TOP_INT; macro(COMPARE_OP_STR) = - _GUARD_TOS_UNICODE + _GUARD_NOS_UNICODE + unused/1 + _COMPARE_OP_STR; + _GUARD_TOS_UNICODE + _GUARD_NOS_UNICODE + unused/1 + _COMPARE_OP_STR + _POP_TOP_UNICODE + _POP_TOP_UNICODE; - op(_COMPARE_OP_FLOAT, (left, right -- res)) { + op(_COMPARE_OP_FLOAT, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); @@ -2749,16 +3301,16 @@ dummy_func( double dright = PyFloat_AS_DOUBLE(right_o); // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg int sign_ish = COMPARISON_BIT(dleft, dright); - PyStackRef_CLOSE_SPECIALIZED(left, _PyFloat_ExactDealloc); + l = left; + r = right; DEAD(left); - PyStackRef_CLOSE_SPECIALIZED(right, _PyFloat_ExactDealloc); DEAD(right); res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; // It's always a bool, so we don't care about oparg & 16. } // Similar to COMPARE_OP_FLOAT - op(_COMPARE_OP_INT, (left, right -- res)) { + op(_COMPARE_OP_INT, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); @@ -2771,25 +3323,25 @@ dummy_func( Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o); // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg int sign_ish = COMPARISON_BIT(ileft, iright); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + l = left; + r = right; DEAD(left); - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); DEAD(right); res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; // It's always a bool, so we don't care about oparg & 16. } // Similar to COMPARE_OP_FLOAT, but for ==, != only - op(_COMPARE_OP_STR, (left, right -- res)) { + op(_COMPARE_OP_STR, (left, right -- res, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); STAT_INC(COMPARE_OP, hit); int eq = _PyUnicode_Equal(left_o, right_o); assert((oparg >> 5) == Py_EQ || (oparg >> 5) == Py_NE); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); + l = left; + r = right; DEAD(left); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); DEAD(right); assert(eq == 0 || eq == 1); assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); @@ -2798,10 +3350,14 @@ dummy_func( // It's always a bool, so we don't care about oparg & 16. } - inst(IS_OP, (left, right -- b)) { + macro(IS_OP) = _IS_OP + POP_TOP + POP_TOP; + + op(_IS_OP, (left, right -- b, l, r)) { int res = Py_Is(PyStackRef_AsPyObjectBorrow(left), PyStackRef_AsPyObjectBorrow(right)) ^ oparg; - DECREF_INPUTS(); b = res ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + INPUTS_DEAD(); } family(CONTAINS_OP, INLINE_CACHE_ENTRIES_CONTAINS_OP) = { @@ -2809,18 +3365,22 @@ dummy_func( CONTAINS_OP_DICT, }; - op(_CONTAINS_OP, (left, right -- b)) { + op(_CONTAINS_OP, (left, right -- b, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); int res = PySequence_Contains(right_o, left_o); - DECREF_INPUTS(); - ERROR_IF(res < 0); + if (res < 0) { + ERROR_NO_POP(); + } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + INPUTS_DEAD(); } specializing op(_SPECIALIZE_CONTAINS_OP, (counter/1, left, right -- left, right)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_ContainsOp(right, next_instr); @@ -2828,19 +3388,29 @@ dummy_func( } OPCODE_DEFERRED_INC(CONTAINS_OP); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } - macro(CONTAINS_OP) = _SPECIALIZE_CONTAINS_OP + _CONTAINS_OP; + macro(CONTAINS_OP) = _SPECIALIZE_CONTAINS_OP + _CONTAINS_OP + POP_TOP + POP_TOP; op(_GUARD_TOS_ANY_SET, (tos -- tos)) { PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - DEOPT_IF(!PyAnySet_CheckExact(o)); + EXIT_IF(!PyAnySet_CheckExact(o)); + } + + op(_GUARD_TOS_SET, (tos -- tos)) { + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + EXIT_IF(!PySet_CheckExact(o)); + } + + op(_GUARD_TOS_FROZENSET, (tos -- tos)) { + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + EXIT_IF(!PyFrozenSet_CheckExact(o)); } - macro(CONTAINS_OP_SET) = _GUARD_TOS_ANY_SET + unused/1 + _CONTAINS_OP_SET; + macro(CONTAINS_OP_SET) = _GUARD_TOS_ANY_SET + unused/1 + _CONTAINS_OP_SET + POP_TOP + POP_TOP; - op(_CONTAINS_OP_SET, (left, right -- b)) { + op(_CONTAINS_OP_SET, (left, right -- b, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); @@ -2848,23 +3418,31 @@ dummy_func( STAT_INC(CONTAINS_OP, hit); // Note: both set and frozenset use the same seq_contains method! int res = _PySet_Contains((PySetObject *)right_o, left_o); - DECREF_INPUTS(); - ERROR_IF(res < 0); + if (res < 0) { + ERROR_NO_POP(); + } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + INPUTS_DEAD(); } - macro(CONTAINS_OP_DICT) = _GUARD_TOS_DICT + unused/1 + _CONTAINS_OP_DICT; + macro(CONTAINS_OP_DICT) = _GUARD_TOS_ANY_DICT + unused/1 + _CONTAINS_OP_DICT + POP_TOP + POP_TOP; - op(_CONTAINS_OP_DICT, (left, right -- b)) { + op(_CONTAINS_OP_DICT, (left, right -- b, l, r)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyDict_CheckExact(right_o)); + assert(PyAnyDict_CheckExact(right_o)); STAT_INC(CONTAINS_OP, hit); int res = PyDict_Contains(right_o, left_o); - DECREF_INPUTS(); - ERROR_IF(res < 0); + if (res < 0) { + ERROR_NO_POP(); + } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + INPUTS_DEAD(); } inst(CHECK_EG_MATCH, (exc_value_st, match_type_st -- rest, match)) { @@ -2908,11 +3486,23 @@ dummy_func( b = res ? PyStackRef_True : PyStackRef_False; } - inst(IMPORT_NAME, (level, fromlist -- res)) { - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *res_o = _PyEval_ImportName(tstate, frame, name, - PyStackRef_AsPyObjectBorrow(fromlist), - PyStackRef_AsPyObjectBorrow(level)); + inst(IMPORT_NAME, (level, fromlist -- res)) { + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + PyObject *res_o; + if (!(oparg & 0x02)) { + res_o = _PyEval_LazyImportName(tstate, BUILTINS(), GLOBALS(), + LOCALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level), + oparg & 0x01); + + } + else { + res_o = _PyEval_ImportName(tstate, BUILTINS(), GLOBALS(), + LOCALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level)); + } DECREF_INPUTS(); ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); @@ -2920,7 +3510,16 @@ dummy_func( inst(IMPORT_FROM, (from -- from, res)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *res_o = _PyEval_ImportFrom(tstate, PyStackRef_AsPyObjectBorrow(from), name); + PyObject *res_o; + if (PyLazyImport_CheckExact(PyStackRef_AsPyObjectBorrow(from))) { + res_o = _PyEval_LazyImportFrom( + tstate, frame, PyStackRef_AsPyObjectBorrow(from), name); + } + else { + res_o = _PyEval_ImportFrom( + tstate, PyStackRef_AsPyObjectBorrow(from), name); + } + ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -2934,10 +3533,11 @@ dummy_func( JUMP_BACKWARD_JIT, }; - tier1 op(_SPECIALIZE_JUMP_BACKWARD, (--)) { + specializing tier1 op(_SPECIALIZE_JUMP_BACKWARD, (--)) { #if ENABLE_SPECIALIZATION if (this_instr->op.code == JUMP_BACKWARD) { - this_instr->op.code = tstate->interp->jit ? JUMP_BACKWARD_JIT : JUMP_BACKWARD_NO_JIT; + uint8_t desired = tstate->interp->jit ? JUMP_BACKWARD_JIT : JUMP_BACKWARD_NO_JIT; + FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, desired); // Need to re-dispatch so the warmup counter isn't off by one: next_instr = this_instr; DISPATCH_SAME_OPARG(); @@ -2947,24 +3547,25 @@ dummy_func( tier1 op(_JIT, (--)) { #ifdef _Py_TIER2 + bool is_resume = this_instr->op.code == RESUME_CHECK_JIT; _Py_BackoffCounter counter = this_instr[1].counter; - if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD_JIT) { - _Py_CODEUNIT *start = this_instr; - /* Back up over EXTENDED_ARGs so optimizer sees the whole instruction */ + if ((backoff_counter_triggers(counter) && + !IS_JIT_TRACING() && + (this_instr->op.code == JUMP_BACKWARD_JIT || is_resume)) && + next_instr->op.code != ENTER_EXECUTOR) { + /* Back up over EXTENDED_ARGs so executor is inserted at the correct place */ + _Py_CODEUNIT *insert_exec_at = this_instr; while (oparg > 255) { oparg >>= 8; - start--; + insert_exec_at--; } - _PyExecutorObject *executor; - int optimized = _PyOptimizer_Optimize(frame, start, &executor, 0); - if (optimized <= 0) { - this_instr[1].counter = restart_backoff_counter(counter); - ERROR_IF(optimized < 0); + int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, + is_resume ? insert_exec_at : next_instr, stack_pointer, 0, NULL, oparg, NULL); + if (succ) { + ENTER_TRACING(); } else { - this_instr[1].counter = initial_jump_backoff_counter(); - assert(tstate->current_executor == NULL); - GOTO_TIER_TWO(executor); + this_instr[1].counter = restart_backoff_counter(counter); } } else { @@ -3012,14 +3613,28 @@ dummy_func( #ifdef _Py_TIER2 PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; + if (IS_JIT_TRACING()) { + int og_opcode = executor->vm_data.opcode; + int og_oparg = (oparg & ~255) | executor->vm_data.oparg; + next_instr = this_instr; + if (_PyJit_EnterExecutorShouldStopTracing(og_opcode)) { + if (_PyOpcode_Caches[_PyOpcode_Deopt[og_opcode]]) { + PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter); + } + opcode = og_opcode; + oparg = og_oparg; + DISPATCH_GOTO_NON_TRACING(); + } + goto stop_tracing; + } assert(executor->vm_data.index == INSTR_OFFSET() - 1); assert(executor->vm_data.code == code); assert(executor->vm_data.valid); assert(tstate->current_executor == NULL); - /* If the eval breaker is set then stay in tier 1. - * This avoids any potentially infinite loops - * involving _RESUME_CHECK */ - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { + /* If the eval breaker is set, or instrumentation is needed, then stay in tier 1. + * This avoids any potentially infinite loops involving _RESUME_CHECK */ + uintptr_t iversion = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(code->_co_instrumentation_version); + if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) != iversion) { opcode = executor->vm_data.opcode; oparg = (oparg & ~255) | executor->vm_data.oparg; next_instr = this_instr; @@ -3028,7 +3643,9 @@ dummy_func( } DISPATCH_GOTO(); } - GOTO_TIER_TWO(executor); + assert(executor != tstate->interp->cold_executor); + tstate->jit_exit = NULL; + TIER1_TO_TIER2(executor); #else Py_FatalError("ENTER_EXECUTOR is not supported in this build"); #endif /* _Py_TIER2 */ @@ -3069,7 +3686,7 @@ dummy_func( macro(POP_JUMP_IF_NOT_NONE) = unused/1 + _IS_NONE + _POP_JUMP_IF_FALSE; - tier1 inst(JUMP_BACKWARD_NO_INTERRUPT, (--)) { + replaced inst(JUMP_BACKWARD_NO_INTERRUPT, (--)) { /* This bytecode is used in the `yield from` or `await` loop. * If there is an interrupt, we want it handled in the innermost * generator or coroutine, so we deliberately do not check it here. @@ -3088,7 +3705,7 @@ dummy_func( len = PyStackRef_FromPyObjectSteal(len_o); } - inst(MATCH_CLASS, (subject, type, names -- attrs)) { + op(_MATCH_CLASS, (subject, type, names -- attrs, s, tp, n)) { // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or // None on failure. assert(PyTuple_CheckExact(PyStackRef_AsPyObjectBorrow(names))); @@ -3096,17 +3713,24 @@ dummy_func( PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(type), oparg, PyStackRef_AsPyObjectBorrow(names)); - DECREF_INPUTS(); if (attrs_o) { assert(PyTuple_CheckExact(attrs_o)); // Success! attrs = PyStackRef_FromPyObjectSteal(attrs_o); } else { - ERROR_IF(_PyErr_Occurred(tstate)); // Error! + if (_PyErr_Occurred(tstate)) { // Error! + ERROR_NO_POP(); + } attrs = PyStackRef_None; // Failure! } + s = subject; + tp = type; + n = names; + INPUTS_DEAD(); } + macro(MATCH_CLASS) = _MATCH_CLASS + POP_TOP + POP_TOP + POP_TOP; + inst(MATCH_MAPPING, (subject -- subject, res)) { int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; res = match ? PyStackRef_True : PyStackRef_False; @@ -3125,55 +3749,69 @@ dummy_func( values_or_none = PyStackRef_FromPyObjectSteal(values_or_none_o); } - inst(GET_ITER, (iterable -- iter, index_or_null)) { - #ifdef Py_STATS - _Py_GatherStats_GetIter(iterable); - #endif - /* before: [obj]; after [getiter(obj)] */ - PyTypeObject *tp = PyStackRef_TYPE(iterable); - if (tp == &PyTuple_Type || tp == &PyList_Type) { - iter = iterable; - DEAD(iterable); - index_or_null = PyStackRef_TagInt(0); - } - else { - PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); - PyStackRef_CLOSE(iterable); - ERROR_IF(iter_o == NULL); - iter = PyStackRef_FromPyObjectSteal(iter_o); - index_or_null = PyStackRef_NULL; - } - } - - inst(GET_YIELD_FROM_ITER, (iterable -- iter)) { - /* before: [obj]; after [getiter(obj)] */ - PyObject *iterable_o = PyStackRef_AsPyObjectBorrow(iterable); - if (PyCoro_CheckExact(iterable_o)) { - /* `iterable` is a coroutine */ - if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { - /* and it is used in a 'yield from' expression of a - regular generator. */ - _PyErr_SetString(tstate, PyExc_TypeError, - "cannot 'yield from' a coroutine object " - "in a non-coroutine generator"); - ERROR_NO_POP(); - } - iter = iterable; - DEAD(iterable); - } - else if (PyGen_CheckExact(iterable_o)) { - iter = iterable; - DEAD(iterable); - } - else { - /* `iterable` is not a generator. */ - PyObject *iter_o = PyObject_GetIter(iterable_o); - if (iter_o == NULL) { - ERROR_NO_POP(); - } - iter = PyStackRef_FromPyObjectSteal(iter_o); - DECREF_INPUTS(); + family(GET_ITER, INLINE_CACHE_ENTRIES_GET_ITER) = { + GET_ITER_SELF, + GET_ITER_VIRTUAL, + }; + + specializing op(_SPECIALIZE_GET_ITER, (counter/1, iterable -- iterable)) { + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _Py_Specialize_GetIter(iterable, next_instr); + DISPATCH_SAME_OPARG(); } + OPCODE_DEFERRED_INC(GET_ITER); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + + op(_GET_ITER, (iterable -- iter, index_or_null)) { + _PyStackRef result = _PyEval_GetIter(iterable, &index_or_null, oparg); + DEAD(iterable); + ERROR_IF(PyStackRef_IsError(result)); + iter = result; + } + + macro(GET_ITER) = + _RECORD_TOS_TYPE + + _SPECIALIZE_GET_ITER + + _GET_ITER; + + op(_GUARD_ITERATOR, (iterable -- iterable)) { + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + EXIT_IF(tp->tp_iter != PyObject_SelfIter); + STAT_INC(GET_ITER, hit); + } + + macro(GET_ITER_SELF) = + _RECORD_TOS_TYPE + + unused/1 + + _GUARD_ITERATOR + + PUSH_NULL; + + op(_GUARD_ITER_VIRTUAL, (iterable -- iterable)) { + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + EXIT_IF(tp->_tp_iteritem == NULL); + STAT_INC(GET_ITER, hit); + } + + op(_PUSH_TAGGED_ZERO, ( -- zero)) { + zero = PyStackRef_TagInt(0); + } + + macro(GET_ITER_VIRTUAL) = + _RECORD_TOS_TYPE + + unused/1 + + _GUARD_ITER_VIRTUAL + + _PUSH_TAGGED_ZERO; + + op(_GET_ITER_TRAD, (iterable -- iter, index_or_null)) { + PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); + PyStackRef_CLOSE(iterable); + ERROR_IF(iter_o == NULL); + iter = PyStackRef_FromPyObjectSteal(iter_o); + index_or_null = PyStackRef_NULL; } // Most members of this family are "secretly" super-instructions. @@ -3187,10 +3825,11 @@ dummy_func( FOR_ITER_TUPLE, FOR_ITER_RANGE, FOR_ITER_GEN, + FOR_ITER_VIRTUAL, }; specializing op(_SPECIALIZE_FOR_ITER, (counter/1, iter, null_or_index -- iter, null_or_index)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_ForIter(iter, null_or_index, next_instr, oparg); @@ -3198,7 +3837,7 @@ dummy_func( } OPCODE_DEFERRED_INC(FOR_ITER); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } replaced op(_FOR_ITER, (iter, null_or_index -- iter, null_or_index, next)) { @@ -3214,6 +3853,8 @@ dummy_func( next = item; } + macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _RECORD_NOS_TYPE + _FOR_ITER; + op(_FOR_ITER_TIER_TWO, (iter, null_or_index -- iter, null_or_index, next)) { _PyStackRef item = _PyForIter_VirtualIteratorNext(tstate, frame, iter, &null_or_index); if (!PyStackRef_IsValid(item)) { @@ -3227,9 +3868,80 @@ dummy_func( next = item; } + tier2 op(_GUARD_TYPE_ITER, (expected_type/4, iter, null_or_index -- iter, null_or_index)) { + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + EXIT_IF(Py_TYPE(iter_o) != (PyTypeObject *)expected_type); + } + + tier2 op(_ITER_NEXT_INLINE, (iternext_fn/4, iter, null_or_index -- iter, null_or_index, next)) { + assert(sizeof(iternextfunc) == sizeof(uintptr_t)); + volatile iternextfunc iternext_v = (iternextfunc)iternext_fn; + PyObject *item = iternext_v(PyStackRef_AsPyObjectBorrow(iter)); + if (item == NULL) { + if (_PyErr_Occurred(tstate)) { + if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + _PyEval_MonitorRaise(tstate, frame, frame->instr_ptr); + _PyErr_Clear(tstate); + } + else { + ERROR_NO_POP(); + } + } + EXIT_IF(true); + } + STAT_INC(FOR_ITER, hit); + next = PyStackRef_FromPyObjectSteal(item); + } + + op(_GUARD_NOS_ITER_VIRTUAL, (iter, null_or_index -- iter, null_or_index)) { + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + EXIT_IF(Py_TYPE(iter_o)->_tp_iteritem == NULL); + } - macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER; + op(_GUARD_TOS_NOT_NULL, (null_or_index -- null_or_index)) { + EXIT_IF(PyStackRef_IsNull(null_or_index)); + } + + replaced op(_FOR_ITER_VIRTUAL, (iter, null_or_index -- iter, null_or_index, next)) { + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyObjectIndexPair next_index = Py_TYPE(iter_o)->_tp_iteritem(iter_o, index); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + ERROR_NO_POP(); + } + // Jump forward by oparg and skip the following END_FOR + JUMPBY(oparg + 1); + DISPATCH(); + } + null_or_index = PyStackRef_TagInt(index); + next = PyStackRef_FromPyObjectSteal(next_o); + } + + macro(FOR_ITER_VIRTUAL) = + unused/1 + // Skip over the counter + _GUARD_TOS_NOT_NULL + + _FOR_ITER_VIRTUAL; + op(_FOR_ITER_VIRTUAL_TIER_TWO, (iter, null_or_index -- iter, null_or_index, next)) { + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyObjectIndexPair next_index = Py_TYPE(iter_o)->_tp_iteritem(iter_o, index); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + ERROR_NO_POP(); + } + /* iterator ended normally */ + /* The translator sets the deopt target just past the matching END_FOR */ + EXIT_IF(true); + } + next = PyStackRef_FromPyObjectSteal(next_o); + null_or_index = PyStackRef_TagInt(index); + } inst(INSTRUMENTED_FOR_ITER, (unused/1, iter, null_or_index -- iter, null_or_index, next)) { _PyStackRef item = _PyForIter_VirtualIteratorNext(tstate, frame, iter, &null_or_index); @@ -3423,19 +4135,11 @@ dummy_func( op(_FOR_ITER_GEN_FRAME, (iter, null -- iter, null, gen_frame)) { PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); - DEOPT_IF(Py_TYPE(gen) != &PyGen_Type); -#ifdef Py_GIL_DISABLED - // Since generators can't be used by multiple threads anyway we - // don't need to deopt here, but this lets us work on making - // generators thread-safe without necessarily having to - // specialize them thread-safely as well. - DEOPT_IF(!_PyObject_IsUniquelyReferenced((PyObject *)gen)); -#endif - DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING); + EXIT_IF(Py_TYPE(gen) != &PyGen_Type); + EXIT_IF(!gen_try_set_executing((PyGenObject *)gen)); STAT_INC(FOR_ITER, hit); _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; _PyFrame_StackPush(pushed_frame, PyStackRef_None); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; pushed_frame->previous = frame; @@ -3445,6 +4149,7 @@ dummy_func( } macro(FOR_ITER_GEN) = + _RECORD_NOS_GEN_FUNC + unused/1 + _CHECK_PEP_523 + _FOR_ITER_GEN_FRAME + @@ -3474,6 +4179,7 @@ dummy_func( } macro(LOAD_SPECIAL) = + _RECORD_TOS_TYPE + _INSERT_NULL + _LOAD_SPECIAL; @@ -3500,10 +4206,14 @@ dummy_func( } assert(PyStackRef_IsTaggedInt(lasti)); (void)lasti; // Shut up compiler warning if asserts are off - PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb}; - int has_self = !PyStackRef_IsNull(exit_self); - PyObject *res_o = PyObject_Vectorcall(exit_func_o, stack + 2 - has_self, - (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + PyObject* res_o; + { + // scope to tell MSVC that stack is not escaping + PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb}; + int has_self = !PyStackRef_IsNull(exit_self); + res_o = PyObject_Vectorcall(exit_func_o, stack + 2 - has_self, + (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + } Py_XDECREF(original_tb); ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); @@ -3552,14 +4262,7 @@ dummy_func( PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); PyDictValues *ivs = _PyObject_InlineValues(owner_o); - DEOPT_IF(!FT_ATOMIC_LOAD_UINT8(ivs->valid)); - } - - op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner)) { - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version); + EXIT_IF(!FT_ATOMIC_LOAD_UINT8(ivs->valid)); } op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self)) { @@ -3575,9 +4278,10 @@ dummy_func( macro(LOAD_ATTR_METHOD_WITH_VALUES) = unused/1 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + - _GUARD_KEYS_VERSION + + unused/2 + _LOAD_ATTR_METHOD_WITH_VALUES; op(_LOAD_ATTR_METHOD_NO_DICT, (descr/4, owner -- attr, self)) { @@ -3593,6 +4297,7 @@ dummy_func( macro(LOAD_ATTR_METHOD_NO_DICT) = unused/1 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + unused/2 + _LOAD_ATTR_METHOD_NO_DICT; @@ -3607,9 +4312,10 @@ dummy_func( macro(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES) = unused/1 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + - _GUARD_KEYS_VERSION + + unused/2 + _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; op(_LOAD_ATTR_NONDESCRIPTOR_NO_DICT, (descr/4, owner -- attr)) { @@ -3623,6 +4329,7 @@ dummy_func( macro(LOAD_ATTR_NONDESCRIPTOR_NO_DICT) = unused/1 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + unused/2 + _LOAD_ATTR_NONDESCRIPTOR_NO_DICT; @@ -3631,7 +4338,7 @@ dummy_func( char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); /* This object has a __dict__, just not yet created */ - DEOPT_IF(dict != NULL); + EXIT_IF(dict != NULL); } op(_LOAD_ATTR_METHOD_LAZY_DICT, (descr/4, owner -- attr, self)) { @@ -3646,6 +4353,7 @@ dummy_func( macro(LOAD_ATTR_METHOD_LAZY_DICT) = unused/1 + + _RECORD_TOS_TYPE + _GUARD_TYPE_VERSION + _CHECK_ATTR_METHOD_LAZY_DICT + unused/1 + @@ -3677,15 +4385,15 @@ dummy_func( }; specializing op(_SPECIALIZE_CALL, (counter/1, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; - _Py_Specialize_Call(callable, next_instr, oparg + !PyStackRef_IsNull(self_or_null)); + _Py_Specialize_Call(callable, self_or_null, next_instr, oparg + !PyStackRef_IsNull(self_or_null)); DISPATCH_SAME_OPARG(); } OPCODE_DEFERRED_INC(CALL); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } op(_MAYBE_EXPAND_METHOD, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { @@ -3713,7 +4421,7 @@ dummy_func( } // Check if the call can be inlined or not if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -3735,36 +4443,18 @@ dummy_func( frame->return_offset = INSTRUCTION_SIZE; DISPATCH_INLINED(new_frame); } - /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - DECREF_INPUTS(); - ERROR_IF(true); - } - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - NULL); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - if (opcode == INSTRUMENTED_CALL) { - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); - if (res_o == NULL) { - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, callable_o, arg); - } - else { - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, callable_o, arg); - if (err < 0) { - Py_CLEAR(res_o); - } - } - } - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - DECREF_INPUTS(); + PyObject* res_o = _Py_VectorCallInstrumentation_StackRefSteal( + callable, + arguments, + total_args, + PyStackRef_NULL, + opcode == INSTRUMENTED_CALL, + frame, + this_instr, + tstate); + DEAD(args); + DEAD(self_or_null); + DEAD(callable); ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -3790,8 +4480,8 @@ dummy_func( ERROR_IF(err); } - macro(CALL) = _SPECIALIZE_CALL + unused/2 + _MAYBE_EXPAND_METHOD + _DO_CALL + _CHECK_PERIODIC; - macro(INSTRUMENTED_CALL) = unused/3 + _MAYBE_EXPAND_METHOD + _MONITOR_CALL + _DO_CALL + _CHECK_PERIODIC; + macro(CALL) = _SPECIALIZE_CALL + unused/2 + _MAYBE_EXPAND_METHOD + _DO_CALL + _CHECK_PERIODIC_AT_END; + macro(INSTRUMENTED_CALL) = unused/3 + _MAYBE_EXPAND_METHOD + _MONITOR_CALL + _DO_CALL + _CHECK_PERIODIC_AT_END; op(_PY_FRAME_GENERAL, (callable, self_or_null, args[oparg] -- new_frame)) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); @@ -3832,6 +4522,7 @@ dummy_func( } macro(CALL_PY_GENERAL) = + _RECORD_CALLABLE + unused/1 + // Skip over the counter _CHECK_PEP_523 + _CHECK_FUNCTION_VERSION + @@ -3862,6 +4553,7 @@ dummy_func( } macro(CALL_BOUND_METHOD_GENERAL) = + _RECORD_BOUND_METHOD + unused/1 + // Skip over the counter _CHECK_PEP_523 + _CHECK_METHOD_VERSION + @@ -3882,37 +4574,31 @@ dummy_func( #if TIER_ONE assert(opcode != INSTRUMENTED_CALL); #endif - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { arguments--; total_args++; } - /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - DECREF_INPUTS(); - ERROR_IF(true); - } - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - NULL); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - DECREF_INPUTS(); + PyObject *res_o = _Py_VectorCall_StackRefSteal( + callable, + arguments, + total_args, + PyStackRef_NULL); + DEAD(args); + DEAD(self_or_null); + DEAD(callable); ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); } macro(CALL_NON_PY_GENERAL) = + _RECORD_CALLABLE + unused/1 + // Skip over the counter unused/2 + _CHECK_IS_NOT_PY_CALLABLE + _CALL_NON_PY_GENERAL + - _CHECK_PERIODIC; + _CHECK_PERIODIC_AT_END; op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable, null, unused[oparg])) { EXIT_IF(!PyStackRef_IsNull(null)); @@ -3930,7 +4616,7 @@ dummy_func( } op(_CHECK_PEP_523, (--)) { - DEOPT_IF(tstate->interp->eval_frame); + DEOPT_IF(IS_PEP523_HOOKED(tstate)); } op(_CHECK_FUNCTION_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { @@ -3966,7 +4652,7 @@ dummy_func( } op(_PUSH_FRAME, (new_frame -- )) { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); DEAD(new_frame); SYNC_SP(); @@ -3977,10 +4663,12 @@ dummy_func( tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } macro(CALL_BOUND_METHOD_EXACT_ARGS) = + _RECORD_BOUND_METHOD + unused/1 + // Skip over the counter _CHECK_PEP_523 + _CHECK_CALL_BOUND_METHOD_EXACT_ARGS + @@ -3995,6 +4683,7 @@ dummy_func( _PUSH_FRAME; macro(CALL_PY_EXACT_ARGS) = + _RECORD_CALLABLE + unused/1 + // Skip over the counter _CHECK_PEP_523 + _CHECK_FUNCTION_VERSION + @@ -4006,34 +4695,26 @@ dummy_func( _PUSH_FRAME; op(_GUARD_NOS_NULL, (null, unused -- null, unused)) { - DEOPT_IF(!PyStackRef_IsNull(null)); - } - - op(_GUARD_NOS_NOT_NULL, (nos, unused -- nos, unused)) { - PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - EXIT_IF(o == NULL); + EXIT_IF(!PyStackRef_IsNull(null)); } op(_GUARD_THIRD_NULL, (null, unused, unused -- null, unused, unused)) { - DEOPT_IF(!PyStackRef_IsNull(null)); + EXIT_IF(!PyStackRef_IsNull(null)); } op(_GUARD_CALLABLE_TYPE_1, (callable, unused, unused -- callable, unused, unused)) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - DEOPT_IF(callable_o != (PyObject *)&PyType_Type); + EXIT_IF(callable_o != (PyObject *)&PyType_Type); } - op(_CALL_TYPE_1, (callable, null, arg -- res)) { + op(_CALL_TYPE_1, (callable, null, arg -- res, a)) { PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - DEAD(null); - DEAD(callable); - (void)callable; // Silence compiler warnings about unused variables - (void)null; STAT_INC(CALL, hit); + a = arg; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); - PyStackRef_CLOSE(arg); } macro(CALL_TYPE_1) = @@ -4041,25 +4722,25 @@ dummy_func( unused/2 + _GUARD_NOS_NULL + _GUARD_CALLABLE_TYPE_1 + - _CALL_TYPE_1; + _CALL_TYPE_1 + + POP_TOP; op(_GUARD_CALLABLE_STR_1, (callable, unused, unused -- callable, unused, unused)) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - DEOPT_IF(callable_o != (PyObject *)&PyUnicode_Type); + EXIT_IF(callable_o != (PyObject *)&PyUnicode_Type); } - op(_CALL_STR_1, (callable, null, arg -- res)) { + op(_CALL_STR_1, (callable, null, arg -- res, a)) { PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); STAT_INC(CALL, hit); PyObject *res_o = PyObject_Str(arg_o); - DEAD(null); - DEAD(callable); - (void)callable; // Silence compiler warnings about unused variables - (void)null; - PyStackRef_CLOSE(arg); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } + a = arg; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4069,25 +4750,25 @@ dummy_func( _GUARD_NOS_NULL + _GUARD_CALLABLE_STR_1 + _CALL_STR_1 + - _CHECK_PERIODIC; + POP_TOP + + _CHECK_PERIODIC_AT_END; op(_GUARD_CALLABLE_TUPLE_1, (callable, unused, unused -- callable, unused, unused)) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - DEOPT_IF(callable_o != (PyObject *)&PyTuple_Type); + EXIT_IF(callable_o != (PyObject *)&PyTuple_Type); } - op(_CALL_TUPLE_1, (callable, null, arg -- res)) { + op(_CALL_TUPLE_1, (callable, null, arg -- res, a)) { PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); STAT_INC(CALL, hit); PyObject *res_o = PySequence_Tuple(arg_o); - DEAD(null); - DEAD(callable); - (void)callable; // Silence compiler warnings about unused variables - (void)null; - PyStackRef_CLOSE(arg); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } + a = arg; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4097,21 +4778,30 @@ dummy_func( _GUARD_NOS_NULL + _GUARD_CALLABLE_TUPLE_1 + _CALL_TUPLE_1 + - _CHECK_PERIODIC; + POP_TOP + + _CHECK_PERIODIC_AT_END; - op(_CHECK_AND_ALLOCATE_OBJECT, (type_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { + op(_CHECK_OBJECT, (type_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - DEOPT_IF(!PyStackRef_IsNull(self_or_null)); - DEOPT_IF(!PyType_Check(callable_o)); + EXIT_IF(!PyStackRef_IsNull(self_or_null)); + EXIT_IF(!PyType_Check(callable_o)); + PyTypeObject *tp = (PyTypeObject *)callable_o; + EXIT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(tp->tp_version_tag) != type_version); + } + + op(_ALLOCATE_OBJECT, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(PyStackRef_IsNull(self_or_null)); + assert(PyType_Check(callable_o)); PyTypeObject *tp = (PyTypeObject *)callable_o; - DEOPT_IF(FT_ATOMIC_LOAD_UINT32_RELAXED(tp->tp_version_tag) != type_version); assert(tp->tp_new == PyBaseObject_Type.tp_new); assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); assert(tp->tp_alloc == PyType_GenericAlloc); + PyHeapTypeObject *cls = (PyHeapTypeObject *)callable_o; PyFunctionObject *init_func = (PyFunctionObject *)FT_ATOMIC_LOAD_PTR_ACQUIRE(cls->_spec_cache.init); PyCodeObject *code = (PyCodeObject *)init_func->func_code; - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize)); + EXIT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize)); STAT_INC(CALL, hit); PyObject *self_o = PyType_GenericAlloc(tp, 0); if (self_o == NULL) { @@ -4149,9 +4839,12 @@ dummy_func( } macro(CALL_ALLOC_AND_ENTER_INIT) = + _RECORD_CALLABLE + unused/1 + _CHECK_PEP_523 + - _CHECK_AND_ALLOCATE_OBJECT + + _CHECK_OBJECT + + _CHECK_RECURSION_REMAINING + + _ALLOCATE_OBJECT + _CREATE_INIT_FRAME + _PUSH_FRAME; @@ -4165,160 +4858,176 @@ dummy_func( DEAD(should_be_none); } - op(_CALL_BUILTIN_CLASS, (callable, self_or_null, args[oparg] -- res)) { + op(_GUARD_CALLABLE_BUILTIN_CLASS, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - DEOPT_IF(!PyType_Check(callable_o)); + EXIT_IF(!PyType_Check(callable_o)); PyTypeObject *tp = (PyTypeObject *)callable_o; + EXIT_IF(tp->tp_vectorcall == NULL); + } + + op(_CALL_BUILTIN_CLASS, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { arguments--; total_args++; } - DEOPT_IF(tp->tp_vectorcall == NULL); STAT_INC(CALL, hit); - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - DECREF_INPUTS(); - ERROR_IF(true); + PyObject *res_o = _Py_CallBuiltinClass_StackRef( + callable, + arguments, + total_args); + if (res_o == NULL) { + ERROR_NO_POP(); } - PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - DECREF_INPUTS(); - ERROR_IF(res_o == NULL); - res = PyStackRef_FromPyObjectSteal(res_o); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + PyStackRef_CLOSE(temp); } macro(CALL_BUILTIN_CLASS) = + _RECORD_CALLABLE + unused/1 + unused/2 + + _GUARD_CALLABLE_BUILTIN_CLASS + _CALL_BUILTIN_CLASS + - _CHECK_PERIODIC; + _POP_TOP_OPARG + + POP_TOP + + _CHECK_PERIODIC_AT_END; - op(_CALL_BUILTIN_O, (callable, self_or_null, args[oparg] -- res)) { - /* Builtin METH_O functions */ + op(_GUARD_CALLABLE_BUILTIN_O, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - + EXIT_IF(!PyCFunction_CheckExact(callable_o)); + EXIT_IF(PyCFunction_GET_FLAGS(callable_o) != METH_O); int total_args = oparg; if (!PyStackRef_IsNull(self_or_null)) { - args--; total_args++; } EXIT_IF(total_args != 1); - EXIT_IF(!PyCFunction_CheckExact(callable_o)); - EXIT_IF(PyCFunction_GET_FLAGS(callable_o) != METH_O); - // CPython promises to check all non-vectorcall function calls. - EXIT_IF(_Py_ReachedRecursionLimit(tstate)); + } + + op(_CALL_BUILTIN_O, (callable, self_or_null, args[oparg] -- res, c, s)) { + /* Builtin METH_O functions */ + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + + if (!PyStackRef_IsNull(self_or_null)) { + args--; + } STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); _PyStackRef arg = args[0]; PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable_o), PyStackRef_AsPyObjectBorrow(arg)); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - - PyStackRef_CLOSE(arg); - DEAD(args); - DEAD(self_or_null); - PyStackRef_CLOSE(callable); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } + c = callable; + s = args[0]; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } macro(CALL_BUILTIN_O) = + _RECORD_CALLABLE + unused/1 + unused/2 + + _GUARD_CALLABLE_BUILTIN_O + + _CHECK_RECURSION_LIMIT + _CALL_BUILTIN_O + - _CHECK_PERIODIC; + POP_TOP + + POP_TOP + + _CHECK_PERIODIC_AT_END; - op(_CALL_BUILTIN_FAST, (callable, self_or_null, args[oparg] -- res)) { - /* Builtin METH_FASTCALL functions, without keywords */ + op(_GUARD_CALLABLE_BUILTIN_FAST, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + EXIT_IF(!PyCFunction_CheckExact(callable_o)); + EXIT_IF(PyCFunction_GET_FLAGS(callable_o) != METH_FASTCALL); + } + op(_CALL_BUILTIN_FAST, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + /* Builtin METH_FASTCALL functions, without keywords */ int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { arguments--; total_args++; } - DEOPT_IF(!PyCFunction_CheckExact(callable_o)); - DEOPT_IF(PyCFunction_GET_FLAGS(callable_o) != METH_FASTCALL); STAT_INC(CALL, hit); - PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); - /* res = func(self, args, nargs) */ - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - DECREF_INPUTS(); - ERROR_IF(true); + PyObject *res_o = _Py_BuiltinCallFast_StackRef( + callable, + arguments, + total_args + ); + if (res_o == NULL) { + ERROR_NO_POP(); } - PyObject *res_o = _PyCFunctionFast_CAST(cfunc)( - PyCFunction_GET_SELF(callable_o), - args_o, - total_args); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - DECREF_INPUTS(); - ERROR_IF(res_o == NULL); - res = PyStackRef_FromPyObjectSteal(res_o); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + PyStackRef_CLOSE(temp); } macro(CALL_BUILTIN_FAST) = + _RECORD_CALLABLE + unused/1 + unused/2 + + _GUARD_CALLABLE_BUILTIN_FAST + _CALL_BUILTIN_FAST + - _CHECK_PERIODIC; + _POP_TOP_OPARG + + POP_TOP + + _CHECK_PERIODIC_AT_END; - op(_CALL_BUILTIN_FAST_WITH_KEYWORDS, (callable, self_or_null, args[oparg] -- res)) { - /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ + op(_GUARD_CALLABLE_BUILTIN_FAST_WITH_KEYWORDS, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + EXIT_IF(!PyCFunction_CheckExact(callable_o)); + EXIT_IF(PyCFunction_GET_FLAGS(callable_o) != (METH_FASTCALL | METH_KEYWORDS)); + } + op(_CALL_BUILTIN_FAST_WITH_KEYWORDS, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { arguments--; total_args++; } - DEOPT_IF(!PyCFunction_CheckExact(callable_o)); - DEOPT_IF(PyCFunction_GET_FLAGS(callable_o) != (METH_FASTCALL | METH_KEYWORDS)); STAT_INC(CALL, hit); - /* res = func(self, arguments, nargs, kwnames) */ - PyCFunctionFastWithKeywords cfunc = - _PyCFunctionFastWithKeywords_CAST(PyCFunction_GET_FUNCTION(callable_o)); - - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - DECREF_INPUTS(); - ERROR_IF(true); + PyObject *res_o = _Py_BuiltinCallFastWithKeywords_StackRef(callable, arguments, total_args); + if (res_o == NULL) { + ERROR_NO_POP(); } - PyObject *res_o = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - DECREF_INPUTS(); - ERROR_IF(res_o == NULL); - res = PyStackRef_FromPyObjectSteal(res_o); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + PyStackRef_CLOSE(temp); } macro(CALL_BUILTIN_FAST_WITH_KEYWORDS) = + _RECORD_CALLABLE + unused/1 + unused/2 + + _GUARD_CALLABLE_BUILTIN_FAST_WITH_KEYWORDS + _CALL_BUILTIN_FAST_WITH_KEYWORDS + - _CHECK_PERIODIC; + _POP_TOP_OPARG + + POP_TOP + + _CHECK_PERIODIC_AT_END; macro(CALL_LEN) = unused/1 + unused/2 + _GUARD_NOS_NULL + _GUARD_CALLABLE_LEN + - _CALL_LEN; + _CALL_LEN + + POP_TOP + + POP_TOP; op(_GUARD_CALLABLE_LEN, (callable, unused, unused -- callable, unused, unused)){ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable_o != interp->callable_cache.len); + EXIT_IF(callable_o != interp->callable_cache.len); } - op(_CALL_LEN, (callable, null, arg -- res)) { + op(_CALL_LEN, (callable, null, arg -- res, a, c)) { /* len(o) */ - (void)null; STAT_INC(CALL, hit); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); Py_ssize_t len_i = PyObject_Length(arg_o); @@ -4330,16 +5039,16 @@ dummy_func( if (res_o == NULL) { ERROR_NO_POP(); } - PyStackRef_CLOSE(arg); - DEAD(null); - PyStackRef_CLOSE(callable); + a = arg; + c = callable; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } op(_GUARD_CALLABLE_ISINSTANCE, (callable, unused, unused, unused -- callable, unused, unused, unused)) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable_o != interp->callable_cache.isinstance); + EXIT_IF(callable_o != interp->callable_cache.isinstance); } op(_CALL_ISINSTANCE, (callable, null, instance, cls -- res)) { @@ -4373,77 +5082,114 @@ dummy_func( _GUARD_CALLABLE_LIST_APPEND + _GUARD_NOS_NOT_NULL + _GUARD_NOS_LIST + - _CALL_LIST_APPEND; + _CALL_LIST_APPEND + + POP_TOP + + POP_TOP; op(_GUARD_CALLABLE_LIST_APPEND, (callable, unused, unused -- callable, unused, unused)){ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyInterpreterState *interp = tstate->interp; - DEOPT_IF(callable_o != interp->callable_cache.list_append); + EXIT_IF(callable_o != interp->callable_cache.list_append); } - // This is secretly a super-instruction - op(_CALL_LIST_APPEND, (callable, self, arg -- )) { + op(_CALL_LIST_APPEND, (callable, self, arg -- none, c, s)) { assert(oparg == 1); PyObject *self_o = PyStackRef_AsPyObjectBorrow(self); - DEOPT_IF(!PyList_CheckExact(self_o)); DEOPT_IF(!LOCK_OBJECT(self_o)); STAT_INC(CALL, hit); int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); UNLOCK_OBJECT(self_o); - PyStackRef_CLOSE(self); - PyStackRef_CLOSE(callable); - ERROR_IF(err); - #if TIER_ONE - // Skip the following POP_TOP. This is done here in tier one, and - // during trace projection in tier two: - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); - #endif + if (err) { + ERROR_NO_POP(); + } + c = callable; + s = self; + DEAD(callable); + DEAD(self); + none = PyStackRef_None; } - op(_CALL_METHOD_DESCRIPTOR_O, (callable, self_or_null, args[oparg] -- res)) { + op(_GUARD_CALLABLE_METHOD_DESCRIPTOR_O, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + EXIT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type)); + EXIT_IF(method->d_method->ml_flags != METH_O); int total_args = oparg; - _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { - arguments--; total_args++; } + EXIT_IF(total_args != 2); + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + EXIT_IF(!Py_IS_TYPE(self, method->d_common.d_type)); + } + op(_CALL_METHOD_DESCRIPTOR_O, (callable, self_or_null, args[oparg] -- res, c, s, a)) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - EXIT_IF(total_args != 2); - EXIT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type)); - PyMethodDef *meth = method->d_method; - EXIT_IF(meth->ml_flags != METH_O); - // CPython promises to check all non-vectorcall function calls. + + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + } + STAT_INC(CALL, hit); + PyCFunction cfunc = method->d_method->ml_meth; + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + PyObject *arg = PyStackRef_AsPyObjectBorrow(arguments[1]); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, self, arg); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + ERROR_NO_POP(); + } + c = callable; + s = arguments[0]; + a = arguments[1]; + INPUTS_DEAD(); + res = PyStackRef_FromPyObjectSteal(res_o); + } + + op(_CHECK_RECURSION_LIMIT, ( -- )) { EXIT_IF(_Py_ReachedRecursionLimit(tstate)); - _PyStackRef arg_stackref = arguments[1]; - _PyStackRef self_stackref = arguments[0]; - EXIT_IF(!Py_IS_TYPE(PyStackRef_AsPyObjectBorrow(self_stackref), - method->d_common.d_type)); + } + + tier2 op(_CALL_METHOD_DESCRIPTOR_O_INLINE, (callable, args[oparg], cfunc/4 -- res, c, s, a)) { + assert(oparg == 2); STAT_INC(CALL, hit); - PyCFunction cfunc = meth->ml_meth; - PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, - PyStackRef_AsPyObjectBorrow(self_stackref), - PyStackRef_AsPyObjectBorrow(arg_stackref)); + volatile PyCFunction cfunc_v = (PyCFunction)cfunc; + PyObject *self = PyStackRef_AsPyObjectBorrow(args[0]); + PyObject *arg = PyStackRef_AsPyObjectBorrow(args[1]); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc_v, self, arg); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - DECREF_INPUTS(); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } + c = callable; + s = args[0]; + a = args[1]; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } macro(CALL_METHOD_DESCRIPTOR_O) = + _RECORD_CALLABLE + unused/1 + unused/2 + + _GUARD_CALLABLE_METHOD_DESCRIPTOR_O + + _CHECK_RECURSION_LIMIT + _CALL_METHOD_DESCRIPTOR_O + - _CHECK_PERIODIC; + POP_TOP + + POP_TOP + + POP_TOP + + _CHECK_PERIODIC_AT_END; - op(_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (callable, self_or_null, args[oparg] -- res)) { + op(_GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + EXIT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type)); + EXIT_IF(method->d_method->ml_flags != (METH_FASTCALL|METH_KEYWORDS)); int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { @@ -4451,78 +5197,155 @@ dummy_func( total_args++; } EXIT_IF(total_args == 0); + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + EXIT_IF(!Py_IS_TYPE(self, method->d_common.d_type)); + } + + op(_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - EXIT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type)); - PyMethodDef *meth = method->d_method; - EXIT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS)); - PyTypeObject *d_type = method->d_common.d_type; + + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); assert(self != NULL); - EXIT_IF(!Py_IS_TYPE(self, d_type)); STAT_INC(CALL, hit); - int nargs = total_args - 1; + PyCFunctionFastWithKeywords cfunc = _PyCFunctionFastWithKeywords_CAST(method->d_method->ml_meth); + PyObject *res_o = _PyCallMethodDescriptorFastWithKeywords_StackRef( + callable, + cfunc, + self, + arguments, + total_args + ); + if (res_o == NULL) { + ERROR_NO_POP(); + } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + PyStackRef_CLOSE(temp); + } - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - DECREF_INPUTS(); - ERROR_IF(true); + tier2 op(_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE, (callable, self_st, args[oparg], cfunc/4 -- callable, self_st, args[oparg])) { + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + STAT_INC(CALL, hit); + volatile PyCFunctionFastWithKeywords cfunc_v = _PyCFunctionFastWithKeywords_CAST(cfunc); + PyObject *res_o = _PyCallMethodDescriptorFastWithKeywords_StackRef( + callable, + cfunc_v, + self, + args - 1, + oparg + 1 + ); + if (res_o == NULL) { + ERROR_NO_POP(); } - PyCFunctionFastWithKeywords cfunc = - _PyCFunctionFastWithKeywords_CAST(meth->ml_meth); - PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - DECREF_INPUTS(); - ERROR_IF(res_o == NULL); - res = PyStackRef_FromPyObjectSteal(res_o); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + PyStackRef_CLOSE(temp); } macro(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) = + _RECORD_CALLABLE + unused/1 + unused/2 + + _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS + _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS + - _CHECK_PERIODIC; + _POP_TOP_OPARG + + POP_TOP + + _CHECK_PERIODIC_AT_END; - op(_CALL_METHOD_DESCRIPTOR_NOARGS, (callable, self_or_null, args[oparg] -- res)) { - assert(oparg == 0 || oparg == 1); + op(_GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + EXIT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type)); + EXIT_IF(method->d_method->ml_flags != METH_NOARGS); int total_args = oparg; if (!PyStackRef_IsNull(self_or_null)) { - args--; total_args++; } EXIT_IF(total_args != 1); + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + EXIT_IF(!Py_IS_TYPE(self, method->d_common.d_type)); + } + + op(_CALL_METHOD_DESCRIPTOR_NOARGS, (callable, self_or_null, args[oparg] -- res, c, s)) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - EXIT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type)); - PyMethodDef *meth = method->d_method; + + assert(oparg == 1 || !PyStackRef_IsNull(self_or_null)); + if (!PyStackRef_IsNull(self_or_null)) { + args--; + } _PyStackRef self_stackref = args[0]; PyObject *self = PyStackRef_AsPyObjectBorrow(self_stackref); - EXIT_IF(!Py_IS_TYPE(self, method->d_common.d_type)); - EXIT_IF(meth->ml_flags != METH_NOARGS); - // CPython promises to check all non-vectorcall function calls. - EXIT_IF(_Py_ReachedRecursionLimit(tstate)); STAT_INC(CALL, hit); - PyCFunction cfunc = meth->ml_meth; + PyCFunction cfunc = method->d_method->ml_meth; PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, self, NULL); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(self_stackref); - DEAD(args); - DEAD(self_or_null); - PyStackRef_CLOSE(callable); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } + c = callable; + s = args[0]; + INPUTS_DEAD(); + res = PyStackRef_FromPyObjectSteal(res_o); + } + + tier2 op(_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE, (callable, args[oparg], cfunc/4 -- res, c, s)) { + assert(oparg == 1); + _PyStackRef self_stackref = args[0]; + PyObject *self = PyStackRef_AsPyObjectBorrow(self_stackref); + STAT_INC(CALL, hit); + volatile PyCFunction cfunc_v = (PyCFunction)cfunc; + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc_v, self, NULL); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + ERROR_NO_POP(); + } + c = callable; + s = args[0]; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } macro(CALL_METHOD_DESCRIPTOR_NOARGS) = + _RECORD_CALLABLE + unused/1 + unused/2 + + _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS + + _CHECK_RECURSION_LIMIT + _CALL_METHOD_DESCRIPTOR_NOARGS + - _CHECK_PERIODIC; + POP_TOP + + POP_TOP + + _CHECK_PERIODIC_AT_END; - op(_CALL_METHOD_DESCRIPTOR_FAST, (callable, self_or_null, args[oparg] -- res)) { + op(_GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + /* Builtin METH_FASTCALL methods, without keywords */ + EXIT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type)); + EXIT_IF(method->d_method->ml_flags != METH_FASTCALL); + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + EXIT_IF(total_args == 0); + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + EXIT_IF(!Py_IS_TYPE(self, method->d_common.d_type)); + } + + op(_CALL_METHOD_DESCRIPTOR_FAST, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; int total_args = oparg; _PyStackRef *arguments = args; @@ -4530,37 +5353,53 @@ dummy_func( arguments--; total_args++; } - EXIT_IF(total_args == 0); - PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - /* Builtin METH_FASTCALL methods, without keywords */ - EXIT_IF(!Py_IS_TYPE(method, &PyMethodDescr_Type)); - PyMethodDef *meth = method->d_method; - EXIT_IF(meth->ml_flags != METH_FASTCALL); PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); assert(self != NULL); - EXIT_IF(!Py_IS_TYPE(self, method->d_common.d_type)); STAT_INC(CALL, hit); - int nargs = total_args - 1; + PyCFunctionFast cfunc = _PyCFunctionFast_CAST(method->d_method->ml_meth); + PyObject *res_o = _PyCallMethodDescriptorFast_StackRef( + callable, + cfunc, + self, + arguments, + total_args + ); + if (res_o == NULL) { + ERROR_NO_POP(); + } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + PyStackRef_CLOSE(temp); + } - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - DECREF_INPUTS(); - ERROR_IF(true); + tier2 op(_CALL_METHOD_DESCRIPTOR_FAST_INLINE, (callable, self_st, args[oparg], cfunc/4 -- callable, self_st, args[oparg])) { + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + assert(self != NULL); + STAT_INC(CALL, hit); + volatile PyCFunctionFast cfunc_v = _PyCFunctionFast_CAST(cfunc); + PyObject *res_o = _PyCallMethodDescriptorFast_StackRef( + callable, + cfunc_v, + self, + args - 1, + oparg + 1 + ); + if (res_o == NULL) { + ERROR_NO_POP(); } - PyCFunctionFast cfunc = _PyCFunctionFast_CAST(meth->ml_meth); - PyObject *res_o = cfunc(self, (args_o + 1), nargs); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - DECREF_INPUTS(); - ERROR_IF(res_o == NULL); - res = PyStackRef_FromPyObjectSteal(res_o); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + PyStackRef_CLOSE(temp); } macro(CALL_METHOD_DESCRIPTOR_FAST) = unused/1 + unused/2 + + _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST + _CALL_METHOD_DESCRIPTOR_FAST + - _CHECK_PERIODIC; + _POP_TOP_OPARG + + POP_TOP + + _CHECK_PERIODIC_AT_END; // Cache layout: counter/1, func_version/2 family(CALL_KW, INLINE_CACHE_ENTRIES_CALL_KW) = { @@ -4614,7 +5453,7 @@ dummy_func( int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); // Check if the call can be inlined or not if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -4638,35 +5477,19 @@ dummy_func( frame->return_offset = INSTRUCTION_SIZE; DISPATCH_INLINED(new_frame); } - /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - DECREF_INPUTS(); - ERROR_IF(true); - } - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames_o); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - if (opcode == INSTRUMENTED_CALL_KW) { - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); - if (res_o == NULL) { - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, callable_o, arg); - } - else { - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, callable_o, arg); - if (err < 0) { - Py_CLEAR(res_o); - } - } - } - DECREF_INPUTS(); + PyObject* res_o = _Py_VectorCallInstrumentation_StackRefSteal( + callable, + arguments, + total_args, + kwnames, + opcode == INSTRUMENTED_CALL_KW, + frame, + this_instr, + tstate); + DEAD(kwnames); + DEAD(args); + DEAD(self_or_null); + DEAD(callable); ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4709,9 +5532,11 @@ dummy_func( } macro(CALL_KW_PY) = + _RECORD_CALLABLE_KW + unused/1 + // Skip over the counter _CHECK_PEP_523 + _CHECK_FUNCTION_VERSION_KW + + _CHECK_RECURSION_REMAINING + _PY_FRAME_KW + _SAVE_RETURN_OFFSET + _PUSH_FRAME; @@ -4738,6 +5563,7 @@ dummy_func( } macro(CALL_KW_BOUND_METHOD) = + _RECORD_CALLABLE_KW + unused/1 + // Skip over the counter _CHECK_PEP_523 + _CHECK_METHOD_VERSION_KW + @@ -4748,7 +5574,7 @@ dummy_func( _PUSH_FRAME; specializing op(_SPECIALIZE_CALL_KW, (counter/1, callable, self_or_null, unused[oparg], unused -- callable, self_or_null, unused[oparg], unused)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_CallKw(callable, next_instr, oparg + !PyStackRef_IsNull(self_or_null)); @@ -4756,7 +5582,7 @@ dummy_func( } OPCODE_DEFERRED_INC(CALL_KW); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } macro(CALL_KW) = @@ -4783,30 +5609,21 @@ dummy_func( #if TIER_ONE assert(opcode != INSTRUMENTED_CALL); #endif - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { arguments--; total_args++; } - /* Callable is not a normal Python function */ - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - DECREF_INPUTS(); - ERROR_IF(true); - } - PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); - int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames_o); - PyStackRef_CLOSE(kwnames); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - DECREF_INPUTS(); + PyObject *res_o = _Py_VectorCall_StackRefSteal( + callable, + arguments, + total_args, + kwnames); + DEAD(kwnames); + DEAD(args); + DEAD(self_or_null); + DEAD(callable); ERROR_IF(res_o == NULL); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4816,7 +5633,12 @@ dummy_func( unused/2 + _CHECK_IS_NOT_PY_CALLABLE_KW + _CALL_KW_NON_PY + - _CHECK_PERIODIC; + _CHECK_PERIODIC_AT_END; + + family(CALL_FUNCTION_EX, INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX) = { + CALL_EX_PY, + CALL_EX_NON_PY_GENERAL, + }; op(_MAKE_CALLARGS_A_TUPLE, (func, unused, callargs, kwargs -- func, unused, callargs, kwargs)) { PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); @@ -4877,7 +5699,7 @@ dummy_func( } else { if (Py_TYPE(func) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); assert(PyTuple_CheckExact(callargs)); @@ -4896,8 +5718,8 @@ dummy_func( if (new_frame == NULL) { ERROR_NO_POP(); } - assert(INSTRUCTION_SIZE == 1); - frame->return_offset = 1; + assert(INSTRUCTION_SIZE == 1 + INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX); + frame->return_offset = INSTRUCTION_SIZE; DISPATCH_INLINED(new_frame); } PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); @@ -4914,30 +5736,116 @@ dummy_func( result = PyStackRef_FromPyObjectSteal(result_o); } + specializing op(_SPECIALIZE_CALL_FUNCTION_EX, (counter/1, func, unused, unused, unused -- func, unused, unused, unused)) { + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _Py_Specialize_CallFunctionEx(func, next_instr); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(CALL_FUNCTION_EX); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } + macro(CALL_FUNCTION_EX) = + _SPECIALIZE_CALL_FUNCTION_EX + _MAKE_CALLARGS_A_TUPLE + _DO_CALL_FUNCTION_EX + - _CHECK_PERIODIC; + _CHECK_PERIODIC_AT_END; + + op(_CHECK_IS_PY_CALLABLE_EX, (func_st, unused, unused, unused -- func_st, unused, unused, unused)) { + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + EXIT_IF(Py_TYPE(func) != &PyFunction_Type); + EXIT_IF(((PyFunctionObject *)func)->vectorcall != _PyFunction_Vectorcall); + } + + op(_PY_FRAME_EX, (func_st, null, callargs_st, kwargs_st -- ex_frame)) { + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); + assert(PyTuple_CheckExact(callargs)); + assert(Py_TYPE(func) == &PyFunction_Type); + assert(((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall); + PyObject *kwargs = PyStackRef_IsNull(kwargs_st) ? NULL : PyStackRef_AsPyObjectSteal(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); + int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( + tstate, func_st, locals, + nargs, callargs, kwargs, frame); + INPUTS_DEAD(); + SYNC_SP(); + if (new_frame == NULL) { + ERROR_NO_POP(); + } + ex_frame = PyStackRef_Wrap(new_frame); + } + + macro(CALL_EX_PY) = + _RECORD_4OS + + unused/1 + + _CHECK_PEP_523 + + _MAKE_CALLARGS_A_TUPLE + + _CHECK_IS_PY_CALLABLE_EX + + _PY_FRAME_EX + + _SAVE_RETURN_OFFSET + + _PUSH_FRAME; + + op(_CHECK_IS_NOT_PY_CALLABLE_EX, (func_st, unused, unused, unused -- func_st, unused, unused, unused)) { + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + EXIT_IF(Py_TYPE(func) == &PyFunction_Type && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall); + } + + op(_CALL_FUNCTION_EX_NON_PY_GENERAL, (func_st, null, callargs_st, kwargs_st -- result)) { + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + (void)null; + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + PyObject *result_o = PyObject_Call(func, callargs, kwargs); + PyStackRef_XCLOSE(kwargs_st); + PyStackRef_CLOSE(callargs_st); + DEAD(null); + PyStackRef_CLOSE(func_st); + ERROR_IF(result_o == NULL); + result = PyStackRef_FromPyObjectSteal(result_o); + } + + macro(CALL_EX_NON_PY_GENERAL) = + unused/1 + + _CHECK_IS_NOT_PY_CALLABLE_EX + + _MAKE_CALLARGS_A_TUPLE + + _CALL_FUNCTION_EX_NON_PY_GENERAL + + _CHECK_PERIODIC_AT_END; macro(INSTRUMENTED_CALL_FUNCTION_EX) = + unused/1 + _MAKE_CALLARGS_A_TUPLE + _DO_CALL_FUNCTION_EX + - _CHECK_PERIODIC; + _CHECK_PERIODIC_AT_END; - inst(MAKE_FUNCTION, (codeobj_st -- func)) { + op(_MAKE_FUNCTION, (codeobj_st -- func, co)) { PyObject *codeobj = PyStackRef_AsPyObjectBorrow(codeobj_st); PyFunctionObject *func_obj = (PyFunctionObject *) PyFunction_New(codeobj, GLOBALS()); - PyStackRef_CLOSE(codeobj_st); - ERROR_IF(func_obj == NULL); + if (func_obj == NULL) { + ERROR_NO_POP(); + } + co = codeobj_st; + DEAD(codeobj_st); _PyFunction_SetVersion( func_obj, ((PyCodeObject *)codeobj)->co_version); func = PyStackRef_FromPyObjectSteal((PyObject *)func_obj); } + macro(MAKE_FUNCTION) = _MAKE_FUNCTION + POP_TOP; + inst(SET_FUNCTION_ATTRIBUTE, (attr_st, func_in -- func_out)) { PyObject *func = PyStackRef_AsPyObjectBorrow(func_in); PyObject *attr = PyStackRef_AsPyObjectSteal(attr_st); @@ -4956,7 +5864,7 @@ dummy_func( PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); ERROR_IF(gen == NULL); - assert(STACK_LEVEL() == 0); + assert(STACK_LEVEL() <= 2); SAVE_STACK(); _PyInterpreterFrame *gen_frame = &gen->gi_iframe; frame->instr_ptr++; @@ -5022,7 +5930,7 @@ dummy_func( } specializing op(_SPECIALIZE_BINARY_OP, (counter/1, lhs, rhs -- lhs, rhs)) { - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, LOCALS_ARRAY); @@ -5030,12 +5938,12 @@ dummy_func( } OPCODE_DEFERRED_INC(BINARY_OP); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ assert(NB_ADD <= oparg); assert(oparg <= NB_OPARG_LAST); } - op(_BINARY_OP, (lhs, rhs -- res)) { + op(_BINARY_OP, (lhs, rhs -- res, l, r)) { PyObject *lhs_o = PyStackRef_AsPyObjectBorrow(lhs); PyObject *rhs_o = PyStackRef_AsPyObjectBorrow(rhs); @@ -5045,10 +5953,13 @@ dummy_func( ERROR_NO_POP(); } res = PyStackRef_FromPyObjectSteal(res_o); - DECREF_INPUTS(); + l = lhs; + r = rhs; + DEAD(lhs); + DEAD(rhs); } - macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + unused/4 + _BINARY_OP; + macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + _RECORD_TOS_TYPE + _RECORD_NOS_TYPE + unused/4 + _BINARY_OP + POP_TOP + POP_TOP; pure replicate(2:4) inst(SWAP, (bottom, unused[oparg-2], top -- bottom, unused[oparg-2], top)) { @@ -5185,23 +6096,42 @@ dummy_func( op (_GUARD_IS_TRUE_POP, (flag -- )) { int is_true = PyStackRef_IsTrue(flag); DEAD(flag); - SYNC_SP(); - EXIT_IF(!is_true); + AT_END_EXIT_IF(!is_true); } op (_GUARD_IS_FALSE_POP, (flag -- )) { int is_false = PyStackRef_IsFalse(flag); DEAD(flag); - SYNC_SP(); - EXIT_IF(!is_false); + AT_END_EXIT_IF(!is_false); + } + + replicate(4:8) op (_GUARD_BIT_IS_SET_POP, (flag -- )) { +#ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; +#else + uintptr_t bits = flag.bits; +#endif + uintptr_t set = (1 << oparg) & bits; + DEAD(flag); + AT_END_EXIT_IF(set == 0); + } + + replicate(4:8) op (_GUARD_BIT_IS_UNSET_POP, (flag -- )) { +#ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; +#else + uintptr_t bits = flag.bits; +#endif + uintptr_t set = (1 << oparg) & bits; + DEAD(flag); + AT_END_EXIT_IF(set != 0); } op (_GUARD_IS_NONE_POP, (val -- )) { int is_none = PyStackRef_IsNone(val); if (!is_none) { PyStackRef_CLOSE(val); - SYNC_SP(); - EXIT_IF(1); + AT_END_EXIT_IF(1); } DEAD(val); } @@ -5209,8 +6139,7 @@ dummy_func( op (_GUARD_IS_NOT_NONE_POP, (val -- )) { int is_none = PyStackRef_IsNone(val); PyStackRef_CLOSE(val); - SYNC_SP(); - EXIT_IF(is_none); + AT_END_EXIT_IF(is_none); } op(_JUMP_TO_TOP, (--)) { @@ -5224,7 +6153,6 @@ dummy_func( tier2 op(_CHECK_STACK_SPACE_OPERAND, (framesize/2 --)) { assert(framesize <= INT_MAX); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, framesize)); - DEOPT_IF(tstate->py_recursion_remaining <= 1); } op(_SAVE_RETURN_OFFSET, (--)) { @@ -5238,46 +6166,41 @@ dummy_func( tier2 op(_EXIT_TRACE, (exit_p/4 --)) { _PyExitData *exit = (_PyExitData *)exit_p; - PyCodeObject *code = _PyFrame_GetCode(frame); - _Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target; #if defined(Py_DEBUG) && !defined(_Py_JIT) + const _Py_CODEUNIT *target = ((frame->owner == FRAME_OWNED_BY_INTERPRETER) + ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame)) + + exit->target; OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - if (frame->lltrace >= 2) { + if (frame->lltrace >= 3) { printf("SIDE EXIT: [UOp "); _PyUOpPrint(&next_uop[-1]); - printf(", exit %lu, temp %d, target %d -> %s]\n", + printf(", exit %tu, temp %d, target %d -> %s, is_control_flow %d]\n", exit - current_executor->exits, exit->temperature.value_and_backoff, (int)(target - _PyFrame_GetBytecode(frame)), - _PyOpcode_OpName[target->op.code]); + _PyOpcode_OpName[target->op.code], exit->is_control_flow); } #endif - if (exit->executor && !exit->executor->vm_data.valid) { - exit->temperature = initial_temperature_backoff_counter(); - Py_CLEAR(exit->executor); - } - if (exit->executor == NULL) { - _Py_BackoffCounter temperature = exit->temperature; - if (!backoff_counter_triggers(temperature)) { - exit->temperature = advance_backoff_counter(temperature); - GOTO_TIER_ONE(target); - } - _PyExecutorObject *executor; - if (target->op.code == ENTER_EXECUTOR) { - executor = code->co_executors->executors[target->op.arg]; - Py_INCREF(executor); - } - else { - int chain_depth = current_executor->vm_data.chain_depth + 1; - int optimized = _PyOptimizer_Optimize(frame, target, &executor, chain_depth); - if (optimized <= 0) { - exit->temperature = restart_backoff_counter(temperature); - GOTO_TIER_ONE(optimized < 0 ? NULL : target); - } - exit->temperature = initial_temperature_backoff_counter(); - } - exit->executor = executor; + tstate->jit_exit = exit; + TIER2_TO_TIER2(exit->executor); + } + + tier2 op(_DYNAMIC_EXIT, (exit_p/4 --)) { + #if defined(Py_DEBUG) && !defined(_Py_JIT) + _PyExitData *exit = (_PyExitData *)exit_p; + _Py_CODEUNIT *target = frame->instr_ptr; + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + if (frame->lltrace >= 3) { + printf("DYNAMIC EXIT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(", exit %tu, temp %d, target %d -> %s]\n", + exit - current_executor->exits, exit->temperature.value_and_backoff, + (int)(target - _PyFrame_GetBytecode(frame)), + _PyOpcode_OpName[target->op.code]); } - GOTO_TIER_TWO(exit->executor); + #endif + // Disabled for now (gh-139109) as it slows down dynamic code tremendously. + // Compile and jump to the cold dynamic executors in the future. + GOTO_TIER_ONE(frame->instr_ptr); } tier2 op(_CHECK_VALIDITY, (--)) { @@ -5288,130 +6211,255 @@ dummy_func( value = PyStackRef_FromPyObjectNew(ptr); } - tier2 pure op (_POP_TOP_LOAD_CONST_INLINE, (ptr/4, pop -- value)) { - PyStackRef_CLOSE(pop); - value = PyStackRef_FromPyObjectNew(ptr); - } - tier2 pure op(_LOAD_CONST_INLINE_BORROW, (ptr/4 -- value)) { value = PyStackRef_FromPyObjectBorrow(ptr); } - tier2 op(_POP_CALL, (callable, null --)) { - (void)null; // Silence compiler warnings about unused variables - DEAD(null); - PyStackRef_CLOSE(callable); + tier2 pure op(_RROT_3, (bottom, middle, top -- bottom, middle, top)) { + _PyStackRef temp = top; + top = middle; + middle = bottom; + bottom = temp; } - tier2 op(_POP_CALL_ONE, (callable, null, pop --)) { - PyStackRef_CLOSE(pop); - (void)null; // Silence compiler warnings about unused variables - DEAD(null); - PyStackRef_CLOSE(callable); + tier2 op(_START_EXECUTOR, (executor/4 --)) { +#ifndef _Py_JIT + assert(current_executor == (_PyExecutorObject*)executor); +#endif + assert(tstate->jit_exit == NULL || tstate->jit_exit->executor == current_executor); + tstate->current_executor = (PyObject *)current_executor; + if (!current_executor->vm_data.valid) { + assert(tstate->jit_exit->executor == current_executor); + assert(tstate->current_executor == executor); + _PyExecutor_ClearExit(tstate->jit_exit); + DEOPT_IF(true); + } } - tier2 op(_POP_CALL_TWO, (callable, null, pop1, pop2 --)) { - PyStackRef_CLOSE(pop2); - PyStackRef_CLOSE(pop1); - (void)null; // Silence compiler warnings about unused variables - DEAD(null); - PyStackRef_CLOSE(callable); + tier2 op(_MAKE_WARM, (--)) { + current_executor->vm_data.cold = false; } - tier2 op(_POP_TOP_LOAD_CONST_INLINE_BORROW, (ptr/4, pop -- value)) { - PyStackRef_CLOSE(pop); - value = PyStackRef_FromPyObjectBorrow(ptr); + tier2 op(_FATAL_ERROR, (--)) { + assert(0); + Py_FatalError("Fatal error uop executed."); } - tier2 op(_POP_TWO_LOAD_CONST_INLINE_BORROW, (ptr/4, pop1, pop2 -- value)) { - PyStackRef_CLOSE(pop2); - PyStackRef_CLOSE(pop1); - value = PyStackRef_FromPyObjectBorrow(ptr); + tier2 op(_DEOPT, (--)) { + SYNC_SP(); + GOTO_TIER_ONE((frame->owner == FRAME_OWNED_BY_INTERPRETER) + ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + Py_UNREACHABLE(); } - tier2 op(_POP_CALL_LOAD_CONST_INLINE_BORROW, (ptr/4, callable, null -- value)) { - (void)null; // Silence compiler warnings about unused variables - DEAD(null); - PyStackRef_CLOSE(callable); - value = PyStackRef_FromPyObjectBorrow(ptr); + tier2 op(_HANDLE_PENDING_AND_DEOPT, (--)) { + SYNC_SP(); + int err = _Py_HandlePending(tstate); + GOTO_TIER_ONE(err ? NULL : _PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + Py_UNREACHABLE(); } - tier2 op(_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, (ptr/4, callable, null, pop -- value)) { - PyStackRef_CLOSE(pop); - (void)null; // Silence compiler warnings about unused variables - DEAD(null); - PyStackRef_CLOSE(callable); - value = PyStackRef_FromPyObjectBorrow(ptr); + tier2 op(_ERROR_POP_N, (target/2 --)) { + assert(oparg == 0); + frame->instr_ptr = _PyFrame_GetBytecode(frame) + target; + SYNC_SP(); + GOTO_TIER_ONE(NULL); + Py_UNREACHABLE(); } - tier2 op(_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, (ptr/4, callable, null, pop1, pop2 -- value)) { - PyStackRef_CLOSE(pop2); - PyStackRef_CLOSE(pop1); - (void)null; // Silence compiler warnings about unused variables - DEAD(null); - PyStackRef_CLOSE(callable); - value = PyStackRef_FromPyObjectBorrow(ptr); + tier2 op(_SPILL_OR_RELOAD, (--)) { } - tier2 op(_LOAD_CONST_UNDER_INLINE, (ptr/4, old -- value, new)) { - new = old; - DEAD(old); - value = PyStackRef_FromPyObjectNew(ptr); + /* Progress is guaranteed if we DEOPT on the eval breaker, because + * ENTER_EXECUTOR will not re-enter tier 2 with the eval breaker set. */ + tier2 op(_TIER2_RESUME_CHECK, (--)) { +#if defined(__EMSCRIPTEN__) + HANDLE_PENDING_AND_DEOPT_IF(_Py_emscripten_signal_clock == 0); + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; +#endif + uintptr_t iversion = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + HANDLE_PENDING_AND_DEOPT_IF(eval_breaker != iversion); } - tier2 op(_LOAD_CONST_UNDER_INLINE_BORROW, (ptr/4, old -- value, new)) { - new = old; - DEAD(old); - value = PyStackRef_FromPyObjectBorrow(ptr); + tier2 op(_COLD_EXIT, ( -- )) { + _PyExitData *exit = tstate->jit_exit; + assert(exit != NULL); + assert(frame->owner < FRAME_OWNED_BY_INTERPRETER); + _Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target; + _Py_BackoffCounter temperature = exit->temperature; + _PyExecutorObject *executor; + if (target->op.code == ENTER_EXECUTOR) { + PyCodeObject *code = _PyFrame_GetCode(frame); + executor = code->co_executors->executors[target->op.arg]; + Py_INCREF(executor); + assert(tstate->jit_exit == exit); + exit->executor = executor; + TIER2_TO_TIER2(exit->executor); + } + else { + SYNC_SP(); + if (!backoff_counter_triggers(temperature)) { + exit->temperature = advance_backoff_counter(temperature); + GOTO_TIER_ONE(target); + } + _PyExecutorObject *previous_executor = _PyExecutor_FromExit(exit); + assert(tstate->current_executor == (PyObject *)previous_executor); + // For control-flow guards, we don't want to increase the chain depth, as those don't actually + // represent deopts but rather just normal programs! + int chain_depth = previous_executor->vm_data.chain_depth + !exit->is_control_flow; + // Note: it's safe to use target->op.arg here instead of the oparg given by EXTENDED_ARG. + // The invariant in the optimizer is the deopt target always points back to the first EXTENDED_ARG. + // So setting it to anything else is wrong. + int succ = _PyJit_TryInitializeTracing(tstate, frame, target, target, target, stack_pointer, chain_depth, exit, target->op.arg, previous_executor); + exit->temperature = restart_backoff_counter(exit->temperature); + if (succ) { + GOTO_TIER_ONE_CONTINUE_TRACING(target); + } + GOTO_TIER_ONE(target); + Py_UNREACHABLE(); + } } - tier2 op(_CHECK_FUNCTION, (func_version/2 -- )) { - assert(PyStackRef_FunctionCheck(frame->f_funcobj)); - PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); - DEOPT_IF(func->func_version != func_version); + tier2 op(_COLD_DYNAMIC_EXIT, ( -- )) { + SYNC_SP(); + // TODO (gh-139109): This should be similar to _COLD_EXIT in the future. + _Py_CODEUNIT *target = frame->instr_ptr; + GOTO_TIER_ONE(target); + Py_UNREACHABLE(); } - tier2 op(_START_EXECUTOR, (executor/4 --)) { -#ifndef _Py_JIT - current_executor = (_PyExecutorObject*)executor; -#endif - assert(((_PyExecutorObject *)executor)->vm_data.valid); + tier2 op(_GUARD_CODE_VERSION__PUSH_FRAME, (version/2 -- )) { + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + EXIT_IF(true); + } } - tier2 op(_MAKE_WARM, (--)) { - current_executor->vm_data.warm = true; - // It's okay if this ends up going negative. - if (--tstate->interp->trace_run_counter == 0) { - _Py_set_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT); + tier2 op(_GUARD_CODE_VERSION_YIELD_VALUE, (version/2 -- )) { + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; + EXIT_IF(true); } } - tier2 op(_FATAL_ERROR, (--)) { - assert(0); - Py_FatalError("Fatal error uop executed."); + tier2 op(_GUARD_CODE_VERSION_RETURN_VALUE, (version/2 -- )) { + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += frame->return_offset; + EXIT_IF(true); + } } - tier2 op(_DEOPT, (--)) { - GOTO_TIER_ONE(_PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + tier2 op(_GUARD_CODE_VERSION_RETURN_GENERATOR, (version/2 -- )) { + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += frame->return_offset; + EXIT_IF(true); + } } - tier2 op(_ERROR_POP_N, (target/2 --)) { - assert(oparg == 0); - frame->instr_ptr = _PyFrame_GetBytecode(frame) + target; - SYNC_SP(); - GOTO_TIER_ONE(NULL); + tier2 op(_GUARD_IP__PUSH_FRAME, (ip/4 --)) { + _Py_CODEUNIT *target = frame->instr_ptr; + if (target != (_Py_CODEUNIT *)ip) { + EXIT_IF(true); + } } - /* Progress is guaranteed if we DEOPT on the eval breaker, because - * ENTER_EXECUTOR will not re-enter tier 2 with the eval breaker set. */ - tier2 op(_TIER2_RESUME_CHECK, (--)) { -#if defined(__EMSCRIPTEN__) - DEOPT_IF(_Py_emscripten_signal_clock == 0); - _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; -#endif - uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); - DEOPT_IF(eval_breaker & _PY_EVAL_EVENTS_MASK); - assert(tstate->tracing || eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version)); + tier2 op(_GUARD_IP_YIELD_VALUE, (ip/4 --)) { + _Py_CODEUNIT *target = frame->instr_ptr + 1 + INLINE_CACHE_ENTRIES_SEND; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; + EXIT_IF(true); + } + } + + tier2 op(_GUARD_IP_RETURN_VALUE, (ip/4 --)) { + _Py_CODEUNIT *target = frame->instr_ptr + frame->return_offset; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += frame->return_offset; + EXIT_IF(true); + } + } + + tier2 op(_GUARD_IP_RETURN_GENERATOR, (ip/4 --)) { + _Py_CODEUNIT *target = frame->instr_ptr + frame->return_offset; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += frame->return_offset; + EXIT_IF(true); + } + } + + /* Record ops for jit tracer. + * + * NOTE: These uops are NOPs for normal evaluation. + * They are only executed during trace recording */ + + tier2 op(_RECORD_TOS, (tos -- tos)) { + RECORD_VALUE(PyStackRef_AsPyObjectBorrow(tos)); + } + + tier2 op(_RECORD_TOS_TYPE, (tos -- tos)) { + RECORD_VALUE(Py_TYPE(PyStackRef_AsPyObjectBorrow(tos))); + } + + tier2 op(_RECORD_NOS, (nos, tos -- nos, tos)) { + RECORD_VALUE(PyStackRef_AsPyObjectBorrow(nos)); + } + + tier2 op(_RECORD_NOS_TYPE, (nos, tos -- nos, tos)) { + RECORD_VALUE(Py_TYPE(PyStackRef_AsPyObjectBorrow(nos))); + } + + tier2 op(_RECORD_NOS_GEN_FUNC, (nos, tos -- nos, tos)) { + PyObject *obj = PyStackRef_AsPyObjectBorrow(nos); + if (PyGen_Check(obj)) { + PyGenObject *gen = (PyGenObject *)obj; + _PyStackRef func = gen->gi_iframe.f_funcobj; + if (!PyStackRef_IsNull(func)) { + RECORD_VALUE(PyStackRef_AsPyObjectBorrow(func)); + } + } + } + + tier2 op(_RECORD_3OS_GEN_FUNC, (gen, nos, tos -- gen, nos, tos)) { + PyObject *obj = PyStackRef_AsPyObjectBorrow(gen); + if (PyGen_Check(obj)) { + PyGenObject *gen_obj = (PyGenObject *)obj; + _PyStackRef func = gen_obj->gi_iframe.f_funcobj; + if (!PyStackRef_IsNull(func)) { + RECORD_VALUE(PyStackRef_AsPyObjectBorrow(func)); + } + } + } + + tier2 op(_RECORD_4OS, (value, _3os, nos, tos -- value, _3os, nos, tos)) { + RECORD_VALUE(PyStackRef_AsPyObjectBorrow(value)); + } + + tier2 op(_RECORD_CALLABLE, (func, self, args[oparg] -- func, self, args[oparg])) { + RECORD_VALUE(PyStackRef_AsPyObjectBorrow(func)); + } + + tier2 op(_RECORD_CALLABLE_KW, (func, self, args[oparg], kwnames -- func, self, args[oparg], kwnames)) { + RECORD_VALUE(PyStackRef_AsPyObjectBorrow(func)); + } + + tier2 op(_RECORD_BOUND_METHOD, (callable, self, args[oparg] -- callable, self, args[oparg])) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (Py_TYPE(callable_o) == &PyMethod_Type) { + RECORD_VALUE(callable_o); + } + } + + /* Inserted by the JIT tracer. Never executed. */ + tier2 op(_RECORD_CODE, ( -- )) { + RECORD_VALUE(NULL); } label(pop_2_error) { @@ -5436,6 +6484,9 @@ dummy_func( #else assert(_PyErr_Occurred(tstate)); #endif + SAVE_STACK(); + STOP_TRACING(); + RELOAD_STACK(); /* Log traceback info. */ assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); @@ -5450,6 +6501,9 @@ dummy_func( } spilled label(exception_unwind) { + SAVE_STACK(); + STOP_TRACING(); + RELOAD_STACK(); /* We can't use frame->instr_ptr here, as RERAISE may have set it */ int offset = INSTR_OFFSET()-1; int level, handler, lasti; @@ -5498,13 +6552,19 @@ dummy_func( } #endif RELOAD_STACK(); -#if Py_TAIL_CALL_INTERP +#if _Py_TAIL_CALL_INTERP int opcode; #endif DISPATCH(); } spilled label(exit_unwind) { + assert(_PyErr_Occurred(tstate)); + DTRACE_FUNCTION_RETURN(); + goto exit_unwind_notrace; + } + + spilled label(exit_unwind_notrace) { assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallPy(tstate); assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); @@ -5516,7 +6576,7 @@ dummy_func( if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { /* Restore previous frame and exit */ tstate->current_frame = frame->previous; -#if !Py_TAIL_CALL_INTERP +#if !_Py_TAIL_CALL_INTERP assert(frame == &entry.frame); #endif #ifdef _Py_TIER2 @@ -5537,8 +6597,9 @@ dummy_func( spilled label(start_frame) { int too_deep = _Py_EnterRecursivePy(tstate); if (too_deep) { - goto exit_unwind; + goto exit_unwind_notrace; } + DTRACE_FUNCTION_ENTRY(); next_instr = frame->instr_ptr; #ifdef Py_DEBUG int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); @@ -5552,12 +6613,82 @@ dummy_func( assert(!_PyErr_Occurred(tstate)); #endif RELOAD_STACK(); -#if Py_TAIL_CALL_INTERP +#if _Py_TAIL_CALL_INTERP int opcode; #endif DISPATCH(); } + inst(TRACE_RECORD, (--)) { +#if _Py_TIER2 + assert(IS_JIT_TRACING()); + next_instr = this_instr; + frame->instr_ptr = prev_instr; + opcode = next_instr->op.code; + bool stop_tracing = ( + opcode == WITH_EXCEPT_START || + opcode == RERAISE || + opcode == CLEANUP_THROW || + opcode == PUSH_EXC_INFO || + opcode == INTERPRETER_EXIT || + (opcode >= MIN_INSTRUMENTED_OPCODE && opcode != ENTER_EXECUTOR) + ); + _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; + _PyJitTracerState *tracer = _tstate->jit_tracer_state; + assert(tracer != NULL); + int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing ? _DEOPT : 0); + if (full) { + LEAVE_TRACING(); + int err = stop_tracing_and_jit(tstate, frame); + ERROR_IF(err < 0); + DISPATCH(); + } + for (int i = 0; i < tracer->prev_state.recorded_count; i++) { + Py_CLEAR(tracer->prev_state.recorded_values[i]); + } + tracer->prev_state.recorded_count = 0; + tracer->prev_state.instr = next_instr; + PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + if (tracer->prev_state.instr_code != (PyCodeObject *)prev_code) { + Py_SETREF(tracer->prev_state.instr_code, (PyCodeObject*)Py_NewRef((prev_code))); + } + + tracer->prev_state.instr_frame = frame; + tracer->prev_state.instr_oparg = oparg; + tracer->prev_state.instr_stacklevel = PyStackRef_IsNone(frame->f_executable) ? 2 : STACK_LEVEL(); + if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] + // Branch opcodes use the cache for branch history, not + // specialization counters. Don't reset it. + && !IS_CONDITIONAL_JUMP_OPCODE(opcode)) { + (&next_instr[1])->counter = trigger_backoff_counter(); + } + + const _PyOpcodeRecordEntry *record_entry = &_PyOpcode_RecordEntries[opcode]; + for (int i = 0; i < record_entry->count; i++) { + _Py_RecordFuncPtr doesnt_escape = _PyOpcode_RecordFunctions[record_entry->indices[i]]; + doesnt_escape(frame, stack_pointer, oparg, &tracer->prev_state.recorded_values[i]); + } + tracer->prev_state.recorded_count = record_entry->count; + DISPATCH_GOTO_NON_TRACING(); +#else + (void)prev_instr; + Py_FatalError("JIT instruction executed in non-jit build."); +#endif + } + + label(stop_tracing) { +#if _Py_TIER2 + assert(IS_JIT_TRACING()); + int opcode = next_instr->op.code; + _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, _EXIT_TRACE); + LEAVE_TRACING(); + int err = stop_tracing_and_jit(tstate, frame); + ERROR_IF(err < 0); + DISPATCH_GOTO_NON_TRACING(); +#else + Py_FatalError("JIT label executed in non-jit build."); +#endif + } // END BYTECODES // @@ -5567,6 +6698,7 @@ dummy_func( error: exception_unwind: exit_unwind: + exit_unwind_notrace: handle_eval_breaker: resume_frame: start_frame: diff --git a/Python/ceval.c b/Python/ceval.c index 291e753dec0ce5..a9b31affca9890 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1,323 +1,7 @@ /* Execute compiled code */ -#define _PY_INTERPRETER - -#include "Python.h" -#include "pycore_abstract.h" // _PyIndex_Check() -#include "pycore_audit.h" // _PySys_Audit() -#include "pycore_backoff.h" -#include "pycore_call.h" // _PyObject_CallNoArgs() -#include "pycore_cell.h" // PyCell_GetRef() -#include "pycore_ceval.h" // SPECIAL___ENTER__ -#include "pycore_code.h" -#include "pycore_dict.h" -#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS -#include "pycore_floatobject.h" // _PyFloat_ExactDealloc() -#include "pycore_frame.h" -#include "pycore_function.h" -#include "pycore_genobject.h" // _PyCoro_GetAwaitableIter() -#include "pycore_import.h" // _PyImport_IsDefaultImportFunc() -#include "pycore_instruments.h" -#include "pycore_interpframe.h" // _PyFrame_SetStackPointer() -#include "pycore_interpolation.h" // _PyInterpolation_Build() -#include "pycore_intrinsics.h" -#include "pycore_jit.h" -#include "pycore_list.h" // _PyList_GetItemRef() -#include "pycore_long.h" // _PyLong_GetZero() -#include "pycore_moduleobject.h" // PyModuleObject -#include "pycore_object.h" // _PyObject_GC_TRACK() -#include "pycore_opcode_metadata.h" // EXTRA_CASES -#include "pycore_opcode_utils.h" // MAKE_FUNCTION_* -#include "pycore_optimizer.h" // _PyUOpExecutor_Type -#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_* -#include "pycore_pyerrors.h" // _PyErr_GetRaisedException() -#include "pycore_pystate.h" // _PyInterpreterState_GET() -#include "pycore_range.h" // _PyRangeIterObject -#include "pycore_setobject.h" // _PySet_Update() -#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs -#include "pycore_sysmodule.h" // _PySys_GetOptionalAttrString() -#include "pycore_template.h" // _PyTemplate_Build() -#include "pycore_traceback.h" // _PyTraceBack_FromFrame -#include "pycore_tuple.h" // _PyTuple_ITEMS() -#include "pycore_uop_ids.h" // Uops - -#include "dictobject.h" -#include "frameobject.h" // _PyInterpreterFrame_GetLine -#include "opcode.h" -#include "pydtrace.h" -#include "setobject.h" -#include "pycore_stackref.h" - -#include <stdbool.h> // bool - -#if !defined(Py_BUILD_CORE) -# error "ceval.c must be build with Py_BUILD_CORE define for best performance" -#endif - -#if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) -// GH-89279: The MSVC compiler does not inline these static inline functions -// in PGO build in _PyEval_EvalFrameDefault(), because this function is over -// the limit of PGO, and that limit cannot be configured. -// Define them as macros to make sure that they are always inlined by the -// preprocessor. - -#undef Py_IS_TYPE -#define Py_IS_TYPE(ob, type) \ - (_PyObject_CAST(ob)->ob_type == (type)) - -#undef Py_XDECREF -#define Py_XDECREF(arg) \ - do { \ - PyObject *xop = _PyObject_CAST(arg); \ - if (xop != NULL) { \ - Py_DECREF(xop); \ - } \ - } while (0) - -#ifndef Py_GIL_DISABLED - -#undef Py_DECREF -#define Py_DECREF(arg) \ - do { \ - PyObject *op = _PyObject_CAST(arg); \ - if (_Py_IsImmortal(op)) { \ - _Py_DECREF_IMMORTAL_STAT_INC(); \ - break; \ - } \ - _Py_DECREF_STAT_INC(); \ - if (--op->ob_refcnt == 0) { \ - _PyReftracerTrack(op, PyRefTracer_DESTROY); \ - destructor dealloc = Py_TYPE(op)->tp_dealloc; \ - (*dealloc)(op); \ - } \ - } while (0) - -#undef _Py_DECREF_SPECIALIZED -#define _Py_DECREF_SPECIALIZED(arg, dealloc) \ - do { \ - PyObject *op = _PyObject_CAST(arg); \ - if (_Py_IsImmortal(op)) { \ - _Py_DECREF_IMMORTAL_STAT_INC(); \ - break; \ - } \ - _Py_DECREF_STAT_INC(); \ - if (--op->ob_refcnt == 0) { \ - _PyReftracerTrack(op, PyRefTracer_DESTROY); \ - destructor d = (destructor)(dealloc); \ - d(op); \ - } \ - } while (0) - -#else // Py_GIL_DISABLED - -#undef Py_DECREF -#define Py_DECREF(arg) \ - do { \ - PyObject *op = _PyObject_CAST(arg); \ - uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local); \ - if (local == _Py_IMMORTAL_REFCNT_LOCAL) { \ - _Py_DECREF_IMMORTAL_STAT_INC(); \ - break; \ - } \ - _Py_DECREF_STAT_INC(); \ - if (_Py_IsOwnedByCurrentThread(op)) { \ - local--; \ - _Py_atomic_store_uint32_relaxed(&op->ob_ref_local, local); \ - if (local == 0) { \ - _Py_MergeZeroLocalRefcount(op); \ - } \ - } \ - else { \ - _Py_DecRefShared(op); \ - } \ - } while (0) - -#undef _Py_DECREF_SPECIALIZED -#define _Py_DECREF_SPECIALIZED(arg, dealloc) Py_DECREF(arg) - -#endif -#endif - - -static void -check_invalid_reentrancy(void) -{ -#if defined(Py_DEBUG) && defined(Py_GIL_DISABLED) - // In the free-threaded build, the interpreter must not be re-entered if - // the world-is-stopped. If so, that's a bug somewhere (quite likely in - // the painfully complex typeobject code). - PyInterpreterState *interp = _PyInterpreterState_GET(); - assert(!interp->stoptheworld.world_stopped); -#endif -} - - -#ifdef Py_DEBUG -static void -dump_item(_PyStackRef item) -{ - if (PyStackRef_IsNull(item)) { - printf("<NULL>"); - return; - } - if (PyStackRef_IsTaggedInt(item)) { - printf("%" PRId64, (int64_t)PyStackRef_UntagInt(item)); - return; - } - PyObject *obj = PyStackRef_AsPyObjectBorrow(item); - if (obj == NULL) { - printf("<nil>"); - return; - } - // Don't call __repr__(), it might recurse into the interpreter. - printf("<%s at %p>", Py_TYPE(obj)->tp_name, (void *)obj); -} - -static void -dump_stack(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer) -{ - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef *locals_base = _PyFrame_GetLocalsArray(frame); - _PyStackRef *stack_base = _PyFrame_Stackbase(frame); - PyObject *exc = PyErr_GetRaisedException(); - printf(" locals=["); - for (_PyStackRef *ptr = locals_base; ptr < stack_base; ptr++) { - if (ptr != locals_base) { - printf(", "); - } - dump_item(*ptr); - } - printf("]\n"); - if (stack_pointer < stack_base) { - printf(" stack=%d\n", (int)(stack_pointer-stack_base)); - } - else { - printf(" stack=["); - for (_PyStackRef *ptr = stack_base; ptr < stack_pointer; ptr++) { - if (ptr != stack_base) { - printf(", "); - } - dump_item(*ptr); - } - printf("]\n"); - } - fflush(stdout); - PyErr_SetRaisedException(exc); - _PyFrame_GetStackPointer(frame); -} - -static void -lltrace_instruction(_PyInterpreterFrame *frame, - _PyStackRef *stack_pointer, - _Py_CODEUNIT *next_instr, - int opcode, - int oparg) -{ - int offset = 0; - if (frame->owner < FRAME_OWNED_BY_INTERPRETER) { - dump_stack(frame, stack_pointer); - offset = (int)(next_instr - _PyFrame_GetBytecode(frame)); - } - const char *opname = _PyOpcode_OpName[opcode]; - assert(opname != NULL); - if (OPCODE_HAS_ARG((int)_PyOpcode_Deopt[opcode])) { - printf("%d: %s %d\n", offset * 2, opname, oparg); - } - else { - printf("%d: %s\n", offset * 2, opname); - } - fflush(stdout); -} - -static void -lltrace_resume_frame(_PyInterpreterFrame *frame) -{ - PyObject *fobj = PyStackRef_AsPyObjectBorrow(frame->f_funcobj); - if (!PyStackRef_CodeCheck(frame->f_executable) || - fobj == NULL || - !PyFunction_Check(fobj) - ) { - printf("\nResuming frame.\n"); - return; - } - PyFunctionObject *f = (PyFunctionObject *)fobj; - PyObject *exc = PyErr_GetRaisedException(); - PyObject *name = f->func_qualname; - if (name == NULL) { - name = f->func_name; - } - printf("\nResuming frame"); - if (name) { - printf(" for "); - if (PyObject_Print(name, stdout, 0) < 0) { - PyErr_Clear(); - } - } - if (f->func_module) { - printf(" in module "); - if (PyObject_Print(f->func_module, stdout, 0) < 0) { - PyErr_Clear(); - } - } - printf("\n"); - fflush(stdout); - PyErr_SetRaisedException(exc); -} - -static int -maybe_lltrace_resume_frame(_PyInterpreterFrame *frame, PyObject *globals) -{ - if (globals == NULL) { - return 0; - } - if (frame->owner >= FRAME_OWNED_BY_INTERPRETER) { - return 0; - } - int r = PyDict_Contains(globals, &_Py_ID(__lltrace__)); - if (r < 0) { - return -1; - } - int lltrace = r * 5; // Levels 1-4 only trace uops - if (!lltrace) { - // Can also be controlled by environment variable - char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); - if (python_lltrace != NULL && *python_lltrace >= '0') { - lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that - } - } - if (lltrace >= 5) { - lltrace_resume_frame(frame); - } - return lltrace; -} - -#endif - -static void monitor_reraise(PyThreadState *tstate, - _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr); -static int monitor_stop_iteration(PyThreadState *tstate, - _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr, - PyObject *value); -static void monitor_unwind(PyThreadState *tstate, - _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr); -static int monitor_handled(PyThreadState *tstate, - _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr, PyObject *exc); -static void monitor_throw(PyThreadState *tstate, - _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr); - -static int get_exception_handler(PyCodeObject *, int, int*, int*, int*); -static _PyInterpreterFrame * -_PyEvalFramePushAndInit_Ex(PyThreadState *tstate, _PyStackRef func, - PyObject *locals, Py_ssize_t nargs, PyObject *callargs, PyObject *kwargs, _PyInterpreterFrame *previous); - -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif +#include "ceval.h" +#include "pycore_long.h" int Py_GetRecursionLimit(void) @@ -346,23 +30,23 @@ _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int margin_count) { uintptr_t here_addr = _Py_get_machine_stack_pointer(); _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; +#if _Py_STACK_GROWS_DOWN if (here_addr > _tstate->c_stack_soft_limit + margin_count * _PyOS_STACK_MARGIN_BYTES) { +#else + if (here_addr <= _tstate->c_stack_soft_limit - margin_count * _PyOS_STACK_MARGIN_BYTES) { +#endif return 0; } if (_tstate->c_stack_hard_limit == 0) { _Py_InitializeRecursionLimits(tstate); } - return here_addr <= _tstate->c_stack_soft_limit + margin_count * _PyOS_STACK_MARGIN_BYTES; -} - -void -_Py_EnterRecursiveCallUnchecked(PyThreadState *tstate) -{ - uintptr_t here_addr = _Py_get_machine_stack_pointer(); - _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; - if (here_addr < _tstate->c_stack_hard_limit) { - Py_FatalError("Unchecked stack overflow."); - } +#if _Py_STACK_GROWS_DOWN + return here_addr <= _tstate->c_stack_soft_limit + margin_count * _PyOS_STACK_MARGIN_BYTES && + here_addr >= _tstate->c_stack_soft_limit - 2 * _PyOS_STACK_MARGIN_BYTES; +#else + return here_addr > _tstate->c_stack_soft_limit - margin_count * _PyOS_STACK_MARGIN_BYTES && + here_addr <= _tstate->c_stack_soft_limit + 2 * _PyOS_STACK_MARGIN_BYTES; +#endif } #if defined(__s390x__) @@ -437,22 +121,28 @@ int pthread_attr_destroy(pthread_attr_t *a) #endif - -void -_Py_InitializeRecursionLimits(PyThreadState *tstate) +static void +hardware_stack_limits(uintptr_t *base, uintptr_t *top, uintptr_t sp) { - _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; #ifdef WIN32 ULONG_PTR low, high; GetCurrentThreadStackLimits(&low, &high); - _tstate->c_stack_top = (uintptr_t)high; + *top = (uintptr_t)high; ULONG guarantee = 0; SetThreadStackGuarantee(&guarantee); - _tstate->c_stack_hard_limit = ((uintptr_t)low) + guarantee + _PyOS_STACK_MARGIN_BYTES; - _tstate->c_stack_soft_limit = _tstate->c_stack_hard_limit + _PyOS_STACK_MARGIN_BYTES; + *base = (uintptr_t)low + guarantee; +#elif defined(__APPLE__) + pthread_t this_thread = pthread_self(); + void *stack_addr = pthread_get_stackaddr_np(this_thread); // top of the stack + size_t stack_size = pthread_get_stacksize_np(this_thread); + *top = (uintptr_t)stack_addr; + *base = ((uintptr_t)stack_addr) - stack_size; #else - uintptr_t here_addr = _Py_get_machine_stack_pointer(); -# if defined(HAVE_PTHREAD_GETATTR_NP) && !defined(_AIX) && !defined(__NetBSD__) + /// XXX musl supports HAVE_PTHRED_GETATTR_NP, but the resulting stack size + /// (on alpine at least) is much smaller than expected and imposes undue limits + /// compared to the old stack size estimation. (We assume musl is not glibc.) +# if defined(HAVE_PTHREAD_GETATTR_NP) && !defined(_AIX) && \ + !defined(__NetBSD__) && (defined(__GLIBC__) || !defined(__linux__)) size_t stack_size, guard_size; void *stack_addr; pthread_attr_t attr; @@ -463,28 +153,118 @@ _Py_InitializeRecursionLimits(PyThreadState *tstate) err |= pthread_attr_destroy(&attr); } if (err == 0) { - uintptr_t base = ((uintptr_t)stack_addr) + guard_size; - _tstate->c_stack_top = base + stack_size; + *base = ((uintptr_t)stack_addr) + guard_size; + *top = (uintptr_t)stack_addr + stack_size; + return; + } +# endif + // Add some space for caller function then round to minimum page size + // This is a guess at the top of the stack, but should be a reasonably + // good guess if called from _PyThreadState_Attach when creating a thread. + // If the thread is attached deep in a call stack, then the guess will be poor. +#if _Py_STACK_GROWS_DOWN + uintptr_t top_addr = _Py_SIZE_ROUND_UP(sp + 8*sizeof(void*), SYSTEM_PAGE_SIZE); + *top = top_addr; + *base = top_addr - Py_C_STACK_SIZE; +# else + uintptr_t base_addr = _Py_SIZE_ROUND_DOWN(sp - 8*sizeof(void*), SYSTEM_PAGE_SIZE); + *base = base_addr; + *top = base_addr + Py_C_STACK_SIZE; +#endif +#endif +} + +static void +tstate_set_stack(PyThreadState *tstate, + uintptr_t base, uintptr_t top) +{ + assert(base < top); + assert((top - base) >= _PyOS_MIN_STACK_SIZE); + #ifdef _Py_THREAD_SANITIZER - // Thread sanitizer crashes if we use a bit more than half the stack. - _tstate->c_stack_soft_limit = base + (stack_size / 2); + // Thread sanitizer crashes if we use more than half the stack. + uintptr_t stacksize = top - base; +# if _Py_STACK_GROWS_DOWN + base += stacksize/2; +# else + top -= stacksize/2; +# endif +#endif + _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; +#if _Py_STACK_GROWS_DOWN + _tstate->c_stack_top = top; + _tstate->c_stack_hard_limit = base + _PyOS_STACK_MARGIN_BYTES; + _tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2; +# ifndef NDEBUG + // Sanity checks + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate; + assert(ts->c_stack_hard_limit <= ts->c_stack_soft_limit); + assert(ts->c_stack_soft_limit < ts->c_stack_top); +# endif #else - _tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2; + _tstate->c_stack_top = base; + _tstate->c_stack_hard_limit = top - _PyOS_STACK_MARGIN_BYTES; + _tstate->c_stack_soft_limit = top - _PyOS_STACK_MARGIN_BYTES * 2; +# ifndef NDEBUG + // Sanity checks + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate; + assert(ts->c_stack_hard_limit >= ts->c_stack_soft_limit); + assert(ts->c_stack_soft_limit > ts->c_stack_top); +# endif #endif - _tstate->c_stack_hard_limit = base + _PyOS_STACK_MARGIN_BYTES; - assert(_tstate->c_stack_soft_limit < here_addr); - assert(here_addr < _tstate->c_stack_top); +} + + +void +_Py_InitializeRecursionLimits(PyThreadState *tstate) +{ + uintptr_t base, top; + uintptr_t here_addr = _Py_get_machine_stack_pointer(); + hardware_stack_limits(&base, &top, here_addr); + assert(top != 0); + + tstate_set_stack(tstate, base, top); + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate; + ts->c_stack_init_base = base; + ts->c_stack_init_top = top; +} + + +int +PyUnstable_ThreadState_SetStackProtection(PyThreadState *tstate, + void *stack_start_addr, size_t stack_size) +{ + if (stack_size < _PyOS_MIN_STACK_SIZE) { + PyErr_Format(PyExc_ValueError, + "stack_size must be at least %zu bytes", + _PyOS_MIN_STACK_SIZE); + return -1; + } + + uintptr_t base = (uintptr_t)stack_start_addr; + uintptr_t top = base + stack_size; + tstate_set_stack(tstate, base, top); + return 0; +} + + +void +PyUnstable_ThreadState_ResetStackProtection(PyThreadState *tstate) +{ + _PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate; + if (ts->c_stack_init_top != 0) { + tstate_set_stack(tstate, + ts->c_stack_init_base, + ts->c_stack_init_top); return; } -# endif - _tstate->c_stack_top = _Py_SIZE_ROUND_UP(here_addr, 4096); - _tstate->c_stack_soft_limit = _tstate->c_stack_top - Py_C_STACK_SIZE; - _tstate->c_stack_hard_limit = _tstate->c_stack_top - (Py_C_STACK_SIZE + _PyOS_STACK_MARGIN_BYTES); -#endif + + _Py_InitializeRecursionLimits(tstate); } + /* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall() - if the recursion_depth reaches recursion_limit. */ + if the stack pointer is beyond c_stack_soft_limit. */ int _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where) { @@ -492,9 +272,22 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where) uintptr_t here_addr = _Py_get_machine_stack_pointer(); assert(_tstate->c_stack_soft_limit != 0); assert(_tstate->c_stack_hard_limit != 0); +#if _Py_STACK_GROWS_DOWN if (here_addr < _tstate->c_stack_hard_limit) { - /* Overflowing while handling an overflow. Give up. */ + if (here_addr < _tstate->c_stack_hard_limit - _PyOS_STACK_MARGIN_BYTES) { + // Far out of bounds -- Assume stack switching has occurred + return 0; + } int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024; +#else + if (here_addr > _tstate->c_stack_hard_limit) { + if (here_addr > _tstate->c_stack_hard_limit + _PyOS_STACK_MARGIN_BYTES) { + // Far out of bounds -- Assume stack switching has occurred + return 0; + } + int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024; +#endif + /* Too much stack used to safely raise an exception. Give up. */ char buffer[80]; snprintf(buffer, 80, "Unrecoverable stack overflow (used %d kB)%s", kbytes_used, where); Py_FatalError(buffer); @@ -503,7 +296,11 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where) return 0; } else { +#if _Py_STACK_GROWS_DOWN int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024; +#else + int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024; +#endif tstate->recursion_headroom++; _PyErr_Format(tstate, PyExc_RecursionError, "Stack overflow (used %d kB)%s", @@ -631,13 +428,15 @@ _PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys) // - Atomically check for a key and get its value without error handling. // - Don't cause key creation or resizing in dict subclasses like // collections.defaultdict that define __missing__ (or similar). - _PyCStackRef cref; - _PyThreadState_PushCStackRef(tstate, &cref); - int meth_found = _PyObject_GetMethodStackRef(tstate, map, &_Py_ID(get), &cref.ref); - PyObject *get = PyStackRef_AsPyObjectBorrow(cref.ref); - if (get == NULL) { + _PyCStackRef self, method; + _PyThreadState_PushCStackRef(tstate, &self); + _PyThreadState_PushCStackRef(tstate, &method); + self.ref = PyStackRef_FromPyObjectBorrow(map); + int res = _PyObject_GetMethodStackRef(tstate, &self.ref, &_Py_ID(get), &method.ref); + if (res < 0) { goto fail; } + PyObject *get = PyStackRef_AsPyObjectBorrow(method.ref); seen = PySet_New(NULL); if (seen == NULL) { goto fail; @@ -661,9 +460,10 @@ _PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys) } goto fail; } - PyObject *args[] = { map, key, dummy }; + PyObject *self_obj = PyStackRef_AsPyObjectBorrow(self.ref); + PyObject *args[] = { self_obj, key, dummy }; PyObject *value = NULL; - if (meth_found) { + if (!PyStackRef_IsNull(self.ref)) { value = PyObject_Vectorcall(get, args, 3, NULL); } else { @@ -684,12 +484,14 @@ _PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys) } // Success: done: - _PyThreadState_PopCStackRef(tstate, &cref); + _PyThreadState_PopCStackRef(tstate, &method); + _PyThreadState_PopCStackRef(tstate, &self); Py_DECREF(seen); Py_DECREF(dummy); return values; fail: - _PyThreadState_PopCStackRef(tstate, &cref); + _PyThreadState_PopCStackRef(tstate, &method); + _PyThreadState_PopCStackRef(tstate, &self); Py_XDECREF(seen); Py_XDECREF(dummy); Py_XDECREF(values); @@ -704,15 +506,18 @@ match_class_attr(PyThreadState *tstate, PyObject *subject, PyObject *type, PyObject *name, PyObject *seen) { assert(PyUnicode_CheckExact(name)); - assert(PySet_CheckExact(seen)); - if (PySet_Contains(seen, name) || PySet_Add(seen, name)) { - if (!_PyErr_Occurred(tstate)) { - // Seen it before! - _PyErr_Format(tstate, PyExc_TypeError, - "%s() got multiple sub-patterns for attribute %R", - ((PyTypeObject*)type)->tp_name, name); + // Only check for duplicates if seen is not NULL. + if (seen != NULL) { + assert(PySet_CheckExact(seen)); + if (PySet_Contains(seen, name) || PySet_Add(seen, name)) { + if (!_PyErr_Occurred(tstate)) { + // Seen it before! + _PyErr_Format(tstate, PyExc_TypeError, + "%s() got multiple sub-patterns for attribute %R", + ((PyTypeObject*)type)->tp_name, name); + } + return NULL; } - return NULL; } PyObject *attr; (void)PyObject_GetOptionalAttr(subject, name, &attr); @@ -726,7 +531,7 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs) { if (!PyType_Check(type)) { - const char *e = "called match pattern must be a class"; + const char *e = "class pattern must refer to a class"; _PyErr_Format(tstate, PyExc_TypeError, e); return NULL; } @@ -735,14 +540,26 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, if (PyObject_IsInstance(subject, type) <= 0) { return NULL; } + // Short circuit if there aren't any arguments: + Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwargs); + Py_ssize_t nattrs = nargs + nkwargs; + if (!nattrs) { + return PyTuple_New(0); + } // So far so good: - PyObject *seen = PySet_New(NULL); - if (seen == NULL) { - return NULL; + PyObject *seen = NULL; + // Only check for duplicates if there is at least one positional attribute + // and two or more attributes in total. Duplicate keyword attributes are + // detected during the compile stage and raise a SyntaxError. + if (nargs > 0 && nattrs > 1) { + seen = PySet_New(NULL); + if (seen == NULL) { + return NULL; + } } - PyObject *attrs = PyList_New(0); + PyObject *attrs = PyTuple_New(nattrs); if (attrs == NULL) { - Py_DECREF(seen); + Py_XDECREF(seen); return NULL; } // NOTE: From this point on, goto fail on failure: @@ -776,16 +593,15 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, if (allowed < nargs) { const char *plural = (allowed == 1) ? "" : "s"; _PyErr_Format(tstate, PyExc_TypeError, - "%s() accepts %d positional sub-pattern%s (%d given)", + "%s() accepts %zd positional sub-pattern%s (%zd given)", ((PyTypeObject*)type)->tp_name, allowed, plural, nargs); goto fail; } if (match_self) { // Easy. Copy the subject itself, and move on to kwargs. - if (PyList_Append(attrs, subject) < 0) { - goto fail; - } + assert(PyTuple_GET_ITEM(attrs, 0) == NULL); + PyTuple_SET_ITEM(attrs, 0, Py_NewRef(subject)); } else { for (Py_ssize_t i = 0; i < nargs; i++) { @@ -801,36 +617,29 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, if (attr == NULL) { goto fail; } - if (PyList_Append(attrs, attr) < 0) { - Py_DECREF(attr); - goto fail; - } - Py_DECREF(attr); + assert(PyTuple_GET_ITEM(attrs, i) == NULL); + PyTuple_SET_ITEM(attrs, i, attr); } } Py_CLEAR(match_args); } // Finally, the keyword subpatterns: - for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwargs); i++) { + for (Py_ssize_t i = 0; i < nkwargs; i++) { PyObject *name = PyTuple_GET_ITEM(kwargs, i); PyObject *attr = match_class_attr(tstate, subject, type, name, seen); if (attr == NULL) { goto fail; } - if (PyList_Append(attrs, attr) < 0) { - Py_DECREF(attr); - goto fail; - } - Py_DECREF(attr); + assert(PyTuple_GET_ITEM(attrs, nargs + i) == NULL); + PyTuple_SET_ITEM(attrs, nargs + i, attr); } - Py_SETREF(attrs, PyList_AsTuple(attrs)); - Py_DECREF(seen); + Py_XDECREF(seen); return attrs; fail: // We really don't care whether an error was raised or not... that's our // caller's problem. All we know is that the match failed. Py_XDECREF(match_args); - Py_DECREF(seen); + Py_XDECREF(seen); Py_DECREF(attrs); return NULL; } @@ -890,6 +699,314 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) #include "ceval_macros.h" + +/* Helper functions to keep the size of the largest uops down */ + +PyObject * +_Py_VectorCall_StackRefSteal( + _PyStackRef callable, + _PyStackRef *arguments, + int total_args, + _PyStackRef kwnames) +{ + PyObject *res; + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + if (CONVERSION_FAILED(args_o)) { + res = NULL; + goto cleanup; + } + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); + int positional_args = total_args; + if (kwnames_o != NULL) { + positional_args -= (int)PyTuple_GET_SIZE(kwnames_o); + } + res = PyObject_Vectorcall( + callable_o, args_o, + positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + kwnames_o); + STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); + assert((res != NULL) ^ (PyErr_Occurred() != NULL)); +cleanup: + PyStackRef_XCLOSE(kwnames); + // arguments is a pointer into the GC visible stack, + // so we must NULL out values as we clear them. + for (int i = total_args-1; i >= 0; i--) { + _PyStackRef tmp = arguments[i]; + arguments[i] = PyStackRef_NULL; + PyStackRef_CLOSE(tmp); + } + PyStackRef_CLOSE(callable); + return res; +} + +PyObject* +_Py_VectorCallInstrumentation_StackRefSteal( + _PyStackRef callable, + _PyStackRef* arguments, + int total_args, + _PyStackRef kwnames, + bool call_instrumentation, + _PyInterpreterFrame* frame, + _Py_CODEUNIT* this_instr, + PyThreadState* tstate) +{ + PyObject* res; + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + if (CONVERSION_FAILED(args_o)) { + res = NULL; + goto cleanup; + } + PyObject* callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject* kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); + int positional_args = total_args; + if (kwnames_o != NULL) { + positional_args -= (int)PyTuple_GET_SIZE(kwnames_o); + } + res = PyObject_Vectorcall( + callable_o, args_o, + positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, + kwnames_o); + STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); + if (call_instrumentation) { + PyObject* arg = total_args == 0 ? + &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); + if (res == NULL) { + _Py_call_instrumentation_exc2( + tstate, PY_MONITORING_EVENT_C_RAISE, + frame, this_instr, callable_o, arg); + } + else { + int err = _Py_call_instrumentation_2args( + tstate, PY_MONITORING_EVENT_C_RETURN, + frame, this_instr, callable_o, arg); + if (err < 0) { + Py_CLEAR(res); + } + } + } + assert((res != NULL) ^ (PyErr_Occurred() != NULL)); +cleanup: + PyStackRef_XCLOSE(kwnames); + // arguments is a pointer into the GC visible stack, + // so we must NULL out values as we clear them. + for (int i = total_args - 1; i >= 0; i--) { + _PyStackRef tmp = arguments[i]; + arguments[i] = PyStackRef_NULL; + PyStackRef_CLOSE(tmp); + } + PyStackRef_CLOSE(callable); + return res; +} + +PyObject * +_Py_BuiltinCallFast_StackRef( + _PyStackRef callable, + _PyStackRef *arguments, + int total_args) +{ + PyObject *res; + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + if (CONVERSION_FAILED(args_o)) { + return NULL; + } + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); + res = _PyCFunctionFast_CAST(cfunc)( + PyCFunction_GET_SELF(callable_o), + args_o, + total_args + ); + STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); + assert((res != NULL) ^ (PyErr_Occurred() != NULL)); + return res; +} + +PyObject * +_Py_BuiltinCallFastWithKeywords_StackRef( + _PyStackRef callable, + _PyStackRef *arguments, + int total_args) +{ + PyObject *res; + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + if (CONVERSION_FAILED(args_o)) { + return NULL; + } + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyCFunctionFastWithKeywords cfunc = + _PyCFunctionFastWithKeywords_CAST(PyCFunction_GET_FUNCTION(callable_o)); + res = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL); + STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); + assert((res != NULL) ^ (PyErr_Occurred() != NULL)); + return res; +} + +PyObject * +_PyCallMethodDescriptorFast_StackRef( + _PyStackRef callable, + PyCFunctionFast cfunc, + PyObject *self, + _PyStackRef *arguments, + int total_args) +{ + PyObject *res; + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + if (CONVERSION_FAILED(args_o)) { + return NULL; + } + assert(self == PyStackRef_AsPyObjectBorrow(arguments[0])); + + res = cfunc(self, (args_o + 1), total_args - 1); + STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); + assert((res != NULL) ^ (PyErr_Occurred() != NULL)); + return res; +} + +PyObject * +_PyCallMethodDescriptorFastWithKeywords_StackRef( + _PyStackRef callable, + PyCFunctionFastWithKeywords cfunc, + PyObject *self, + _PyStackRef *arguments, + int total_args) +{ + PyObject *res; + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + if (CONVERSION_FAILED(args_o)) { + return NULL; + } + assert(self == PyStackRef_AsPyObjectBorrow(arguments[0])); + + res = cfunc(self, (args_o + 1), total_args-1, NULL); + STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); + assert((res != NULL) ^ (PyErr_Occurred() != NULL)); + return res; +} + +PyObject * +_Py_CallBuiltinClass_StackRef( + _PyStackRef callable, + _PyStackRef *arguments, + int total_args) +{ + PyObject *res; + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + if (CONVERSION_FAILED(args_o)) { + return NULL; + } + PyTypeObject *tp = (PyTypeObject *)PyStackRef_AsPyObjectBorrow(callable); + res = tp->tp_vectorcall((PyObject *)tp, args_o, total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); + assert((res != NULL) ^ (PyErr_Occurred() != NULL)); + return res; +} + +PyObject * +_Py_BuildString_StackRefSteal( + _PyStackRef *arguments, + int total_args) +{ + PyObject *res; + STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); + if (CONVERSION_FAILED(args_o)) { + res = NULL; + goto cleanup; + } + res = _PyUnicode_JoinArray(&_Py_STR(empty), args_o, total_args); + STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); + assert((res != NULL) ^ (PyErr_Occurred() != NULL)); +cleanup: + // arguments is a pointer into the GC visible stack, + // so we must NULL out values as we clear them. + for (int i = total_args-1; i >= 0; i--) { + _PyStackRef tmp = arguments[i]; + arguments[i] = PyStackRef_NULL; + PyStackRef_CLOSE(tmp); + } + return res; +} + +PyObject * +_Py_BuildMap_StackRefSteal( + _PyStackRef *arguments, + int half_args) +{ + PyObject *res; + STACKREFS_TO_PYOBJECTS(arguments, half_args*2, args_o); + if (CONVERSION_FAILED(args_o)) { + res = NULL; + goto cleanup; + } + res = _PyDict_FromItems( + args_o, 2, + args_o+1, 2, + half_args + ); + STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); + assert((res != NULL) ^ (PyErr_Occurred() != NULL)); +cleanup: + // arguments is a pointer into the GC visible stack, + // so we must NULL out values as we clear them. + for (int i = half_args*2-1; i >= 0; i--) { + _PyStackRef tmp = arguments[i]; + arguments[i] = PyStackRef_NULL; + PyStackRef_CLOSE(tmp); + } + return res; +} + +_PyStackRef +_Py_LoadAttr_StackRefSteal( + PyThreadState *tstate, _PyStackRef owner, + PyObject *name, _PyStackRef *self_or_null) +{ + // Use _PyCStackRefs to ensure that both method and self are visible to + // the GC. Even though self_or_null is on the evaluation stack, it may be + // after the stackpointer and therefore not visible to the GC. + _PyCStackRef method, self; + _PyThreadState_PushCStackRef(tstate, &method); + _PyThreadState_PushCStackRef(tstate, &self); + self.ref = owner; // steal reference to owner + // NOTE: method.ref is initialized to PyStackRef_NULL and remains null on + // error, so we don't need to explicitly use the return code from the call. + _PyObject_GetMethodStackRef(tstate, &self.ref, name, &method.ref); + *self_or_null = _PyThreadState_PopCStackRefSteal(tstate, &self); + return _PyThreadState_PopCStackRefSteal(tstate, &method); +} + +#ifdef Py_DEBUG +void +_Py_assert_within_stack_bounds( + _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, + const char *filename, int lineno +) { + if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { + return; + } + int level = (int)(stack_pointer - _PyFrame_Stackbase(frame)); + if (level < 0) { + printf("Stack underflow (depth = %d) at %s:%d\n", level, filename, lineno); + fflush(stdout); + abort(); + } + int size = _PyFrame_GetCode(frame)->co_stacksize; + if (level > size) { + printf("Stack overflow (depth = %d) at %s:%d\n", level, filename, lineno); + fflush(stdout); + abort(); + } +} +#ifdef _Py_JIT +void +_Py_jit_assert_within_stack_bounds( + _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int lineno +) { + _Py_assert_within_stack_bounds(frame, stack_pointer, "executor_cases.c.h", lineno); +} +#endif +#endif + int _Py_CheckRecursiveCallPy( PyThreadState *tstate) { @@ -918,27 +1035,17 @@ static const _Py_CODEUNIT _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS[] = { { .op.code = INTERPRETER_EXIT, .op.arg = 0 }, /* reached on return */ { .op.code = NOP, .op.arg = 0 }, { .op.code = INTERPRETER_EXIT, .op.arg = 0 }, /* reached on yield */ - { .op.code = RESUME, .op.arg = RESUME_OPARG_DEPTH1_MASK | RESUME_AT_FUNC_START } + { .op.code = RESUME, .op.arg = RESUME_OPARG_DEPTH1_MASK | RESUME_AT_FUNC_START }, + { .op.code = CACHE, .op.arg = 0 } /* RESUME's CACHE */ }; +const _Py_CODEUNIT *_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR = (_Py_CODEUNIT*)&_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS; + #ifdef Py_DEBUG extern void _PyUOpPrint(const _PyUOpInstruction *uop); #endif -/* Disable unused label warnings. They are handy for debugging, even - if computed gotos aren't used. */ - -/* TBD - what about other compilers? */ -#if defined(__GNUC__) || defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-label" -#elif defined(_MSC_VER) /* MS_WINDOWS */ -# pragma warning(push) -# pragma warning(disable:4102) -#endif - - PyObject ** _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject **scratch) { @@ -949,11 +1056,12 @@ _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject if (result == NULL) { return NULL; } - result++; } else { result = scratch; } + result++; + result[0] = NULL; /* Keep GCC happy */ for (int i = 0; i < nargs; i++) { result[i] = PyStackRef_AsPyObjectBorrow(input[i]); } @@ -968,6 +1076,20 @@ _PyObjectArray_Free(PyObject **array, PyObject **scratch) } } +#if _Py_TIER2 +// 0 for success, -1 for error. +static int +stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) +{ + int _is_sys_tracing = (tstate->c_tracefunc != NULL) || (tstate->c_profilefunc != NULL); + int err = 0; + if (!_PyErr_Occurred(tstate) && !_is_sys_tracing) { + err = _PyOptimizer_Optimize(frame, tstate); + } + _PyJit_FinalizeTracing(tstate, err); + return err; +} +#endif /* _PyEval_EvalFrameDefault is too large to optimize for speed with PGO on MSVC. */ @@ -982,11 +1104,47 @@ _PyObjectArray_Free(PyObject **array, PyObject **scratch) /* This setting is reversed below following _PyEval_EvalFrameDefault */ #endif -#if Py_TAIL_CALL_INTERP +#if _Py_TAIL_CALL_INTERP #include "opcode_targets.h" #include "generated_cases.c.h" #endif + +_PyStackRef +_PyEval_GetIter(_PyStackRef iterable, _PyStackRef *index_or_null, int yield_from) +{ + PyTypeObject *tp = PyStackRef_TYPE(iterable); + if (tp->_tp_iteritem != NULL) { + /* Leave iterable on stack and pushed tagged 0 */ + *index_or_null = PyStackRef_TagInt(0); + return iterable; + } + *index_or_null = PyStackRef_NULL; + if (tp->tp_iter == PyObject_SelfIter) { + return iterable; + } + if (yield_from && tp == &PyCoro_Type) { + assert(yield_from != GET_ITER_YIELD_FROM); + if (yield_from == GET_ITER_YIELD_FROM_CORO_CHECK) { + /* `iterable` is a coroutine and it is used in a 'yield from' + * expression of a regular generator. */ + PyErr_SetString(PyExc_TypeError, + "cannot 'yield from' a coroutine object " + "in a non-coroutine generator"); + PyStackRef_CLOSE(iterable); + return PyStackRef_ERROR; + } + return iterable; + } + /* Pop iterable, and push iterator then NULL */ + PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); + PyStackRef_CLOSE(iterable); + if (iter_o == NULL) { + return PyStackRef_ERROR; + } + return PyStackRef_FromPyObjectSteal(iter_o); +} + #if (defined(__GNUC__) && __GNUC__ >= 10 && !defined(__clang__)) && defined(__x86_64__) /* * gh-129987: The SLP autovectorizer can cause poor code generation for @@ -997,15 +1155,42 @@ _PyObjectArray_Free(PyObject **array, PyObject **scratch) * (prior to GCC 9, 40% performance drop), so we have to selectively disable * it. */ -#define DONT_SLP_VECTORIZE __attribute__((optimize ("no-tree-slp-vectorize"))) +#define DONT_SLP_VECTORIZE __attribute__((optimize ("no-tree-slp-vectorize", "no-omit-frame-pointer"))) #else #define DONT_SLP_VECTORIZE #endif -typedef struct { - _PyInterpreterFrame frame; - _PyStackRef stack[1]; -} _PyEntryFrame; +#ifdef WITH_DTRACE +static void +dtrace_function_entry(_PyInterpreterFrame *frame) +{ + const char *filename; + const char *funcname; + int lineno; + + PyCodeObject *code = _PyFrame_GetCode(frame); + filename = PyUnicode_AsUTF8(code->co_filename); + funcname = PyUnicode_AsUTF8(code->co_name); + lineno = PyUnstable_InterpreterFrame_GetLine(frame); + + PyDTrace_FUNCTION_ENTRY(filename, funcname, lineno); +} + +static void +dtrace_function_return(_PyInterpreterFrame *frame) +{ + const char *filename; + const char *funcname; + int lineno; + + PyCodeObject *code = _PyFrame_GetCode(frame); + filename = PyUnicode_AsUTF8(code->co_filename); + funcname = PyUnicode_AsUTF8(code->co_name); + lineno = PyUnstable_InterpreterFrame_GetLine(frame); + + PyDTrace_FUNCTION_RETURN(filename, funcname, lineno); +} +#endif PyObject* _Py_HOT_FUNCTION DONT_SLP_VECTORIZE _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) @@ -1014,18 +1199,23 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int check_invalid_reentrancy(); CALL_STAT_INC(pyeval_calls); -#if USE_COMPUTED_GOTOS && !Py_TAIL_CALL_INTERP +#if USE_COMPUTED_GOTOS && !_Py_TAIL_CALL_INTERP /* Import the static jump table */ #include "opcode_targets.h" + void **opcode_targets = opcode_targets_table; #endif #ifdef Py_STATS int lastopcode = 0; #endif -#if !Py_TAIL_CALL_INTERP +#if !_Py_TAIL_CALL_INTERP uint8_t opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ assert(tstate->current_frame == NULL || tstate->current_frame->stackpointer != NULL); +#if !USE_COMPUTED_GOTOS + uint8_t tracing_mode = 0; + uint8_t dispatch_code; +#endif #endif _PyEntryFrame entry; @@ -1094,27 +1284,22 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int next_instr = frame->instr_ptr; monitor_throw(tstate, frame, next_instr); stack_pointer = _PyFrame_GetStackPointer(frame); -#if Py_TAIL_CALL_INTERP +#if _Py_TAIL_CALL_INTERP # if Py_STATS - return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, 0, lastopcode); + return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, instruction_funcptr_handler_table, 0, lastopcode); # else - return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, 0); + return _TAIL_CALL_error(frame, stack_pointer, tstate, next_instr, instruction_funcptr_handler_table, 0); # endif #else goto error; #endif } -#if defined(_Py_TIER2) && !defined(_Py_JIT) - /* Tier 2 interpreter state */ - _PyExecutorObject *current_executor = NULL; - const _PyUOpInstruction *next_uop = NULL; -#endif -#if Py_TAIL_CALL_INTERP +#if _Py_TAIL_CALL_INTERP # if Py_STATS - return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, 0, lastopcode); + return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, instruction_funcptr_handler_table, 0, lastopcode); # else - return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, 0); + return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, instruction_funcptr_handler_table, 0); # endif #else goto start_frame; @@ -1122,14 +1307,51 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif +early_exit: + assert(_PyErr_Occurred(tstate)); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + frame->return_offset = 0; + assert(frame->owner == FRAME_OWNED_BY_INTERPRETER); + /* Restore previous frame and exit */ + tstate->current_frame = frame->previous; + return NULL; +} #ifdef _Py_TIER2 - -// Tier 2 is also here! -enter_tier_two: - #ifdef _Py_JIT - assert(0); +_PyJitEntryFuncPtr _Py_jit_entry = _PyJIT_Entry; #else +_PyJitEntryFuncPtr _Py_jit_entry = _PyTier2Interpreter; +#endif +#endif + +#if defined(_Py_TIER2) && !defined(_Py_JIT) + +_Py_CODEUNIT * +_PyTier2Interpreter( + _PyExecutorObject *current_executor, _PyInterpreterFrame *frame, + _PyStackRef *stack_pointer, PyThreadState *tstate +) { + const _PyUOpInstruction *next_uop; + int oparg; + /* Set up "jit" state after entry from tier 1. + * This mimics what the jit shim function does. */ + tstate->jit_exit = NULL; + _PyStackRef _tos_cache0 = PyStackRef_ZERO_BITS; + _PyStackRef _tos_cache1 = PyStackRef_ZERO_BITS; + _PyStackRef _tos_cache2 = PyStackRef_ZERO_BITS; + int current_cached_values = 0; + +tier2_start: + + next_uop = current_executor->trace; + assert(next_uop->opcode == _START_EXECUTOR_r00 + current_cached_values || + next_uop->opcode == _COLD_EXIT_r00 + current_cached_values || + next_uop->opcode == _COLD_DYNAMIC_EXIT_r00 + current_cached_values); #undef LOAD_IP #define LOAD_IP(UNUSED) (void)0 @@ -1144,24 +1366,30 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #undef ENABLE_SPECIALIZATION #define ENABLE_SPECIALIZATION 0 -#undef ENABLE_SPECIALIZATION_FT -#define ENABLE_SPECIALIZATION_FT 0 - ; // dummy statement after a label, before a declaration uint16_t uopcode; #ifdef Py_STATS int lastuop = 0; uint64_t trace_uop_execution_counter = 0; #endif - assert(next_uop->opcode == _START_EXECUTOR); + assert(next_uop->opcode == _START_EXECUTOR_r00 || + next_uop->opcode == _COLD_EXIT_r00 || + next_uop->opcode == _COLD_DYNAMIC_EXIT_r00); tier2_dispatch: for (;;) { uopcode = next_uop->opcode; #ifdef Py_DEBUG - if (frame->lltrace >= 3) { + if (frame->lltrace >= 4) { dump_stack(frame, stack_pointer); - if (next_uop->opcode == _START_EXECUTOR) { + printf(" cache=["); + dump_cache_item(_tos_cache0, 0, current_cached_values); + printf(", "); + dump_cache_item(_tos_cache1, 1, current_cached_values); + printf(", "); + dump_cache_item(_tos_cache2, 2, current_cached_values); + printf("]\n"); + if (next_uop->opcode == _START_EXECUTOR_r00) { printf("%4d uop: ", 0); } else { @@ -1169,6 +1397,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } _PyUOpPrint(next_uop); printf("\n"); + fflush(stdout); } #endif next_uop++; @@ -1207,6 +1436,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int printf(" @ %d -> %s]\n", (int)(next_uop - current_executor->trace - 1), _PyOpcode_OpName[frame->instr_ptr->op.code]); + fflush(stdout); } #endif assert(next_uop[-1].format == UOP_FORMAT_JUMP); @@ -1220,24 +1450,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int next_uop = current_executor->trace + target; goto tier2_dispatch; -#endif // _Py_JIT - +} #endif // _Py_TIER2 -early_exit: - assert(_PyErr_Occurred(tstate)); - _Py_LeaveRecursiveCallPy(tstate); - assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - // GH-99729: We need to unlink the frame *before* clearing it: - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - frame->return_offset = 0; - assert(frame->owner == FRAME_OWNED_BY_INTERPRETER); - /* Restore previous frame and exit */ - tstate->current_frame = frame->previous; - return NULL; -} #ifdef DO_NOT_OPTIMIZE_INTERP_LOOP # pragma optimize("", on) @@ -1303,7 +1518,7 @@ format_missing(PyThreadState *tstate, const char *kind, if (name_str == NULL) return; _PyErr_Format(tstate, PyExc_TypeError, - "%U() missing %i required %s argument%s: %U", + "%U() missing %zd required %s argument%s: %U", qualname, len, kind, @@ -1473,74 +1688,6 @@ positional_only_passed_as_keyword(PyThreadState *tstate, PyCodeObject *co, } - -static inline unsigned char * -scan_back_to_entry_start(unsigned char *p) { - for (; (p[0]&128) == 0; p--); - return p; -} - -static inline unsigned char * -skip_to_next_entry(unsigned char *p, unsigned char *end) { - while (p < end && ((p[0] & 128) == 0)) { - p++; - } - return p; -} - - -#define MAX_LINEAR_SEARCH 40 - -static Py_NO_INLINE int -get_exception_handler(PyCodeObject *code, int index, int *level, int *handler, int *lasti) -{ - unsigned char *start = (unsigned char *)PyBytes_AS_STRING(code->co_exceptiontable); - unsigned char *end = start + PyBytes_GET_SIZE(code->co_exceptiontable); - /* Invariants: - * start_table == end_table OR - * start_table points to a legal entry and end_table points - * beyond the table or to a legal entry that is after index. - */ - if (end - start > MAX_LINEAR_SEARCH) { - int offset; - parse_varint(start, &offset); - if (offset > index) { - return 0; - } - do { - unsigned char * mid = start + ((end-start)>>1); - mid = scan_back_to_entry_start(mid); - parse_varint(mid, &offset); - if (offset > index) { - end = mid; - } - else { - start = mid; - } - - } while (end - start > MAX_LINEAR_SEARCH); - } - unsigned char *scan = start; - while (scan < end) { - int start_offset, size; - scan = parse_varint(scan, &start_offset); - if (start_offset > index) { - break; - } - scan = parse_varint(scan, &size); - if (start_offset + size > index) { - scan = parse_varint(scan, handler); - int depth_and_lasti; - parse_varint(scan, &depth_and_lasti); - *level = depth_and_lasti >> 1; - *lasti = depth_and_lasti & 1; - return 1; - } - scan = skip_to_next_entry(scan, end); - } - return 0; -} - static int initialize_locals(PyThreadState *tstate, PyFunctionObject *func, _PyStackRef *localsplus, _PyStackRef const *args, @@ -1809,19 +1956,32 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) { assert(frame->owner == FRAME_OWNED_BY_GENERATOR); PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); - gen->gi_frame_state = FRAME_CLEARED; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_CLEARED); assert(tstate->exc_info == &gen->gi_exc_state); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); + frame->previous = NULL; _PyFrame_ClearExceptCode(frame); _PyErr_ClearExcState(&gen->gi_exc_state); - frame->previous = NULL; + // gh-143939: There must not be any escaping calls between setting + // the generator return kind and returning from _PyEval_EvalFrame. + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_RETURN; } void _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame) { + // Update last_profiled_frame for remote profiler frame caching. + // By this point, tstate->current_frame is already set to the parent frame. + // Only update if we're popping the exact frame that was last profiled. + // This avoids corrupting the cache when transient frames (called and returned + // between profiler samples) update last_profiled_frame to addresses the + // profiler never saw. + if (tstate->last_profiled_frame != NULL && tstate->last_profiled_frame == frame) { + tstate->last_profiled_frame = tstate->current_frame; + } + if (frame->owner == FRAME_OWNED_BY_THREAD) { clear_thread_frame(tstate, frame); } @@ -1870,7 +2030,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, _PyStackRef func, /* Same as _PyEvalFramePushAndInit but takes an args tuple and kwargs dict. Steals references to func, callargs and kwargs. */ -static _PyInterpreterFrame * +_PyInterpreterFrame * _PyEvalFramePushAndInit_Ex(PyThreadState *tstate, _PyStackRef func, PyObject *locals, Py_ssize_t nargs, PyObject *callargs, PyObject *kwargs, _PyInterpreterFrame *previous) { @@ -1878,18 +2038,23 @@ _PyEvalFramePushAndInit_Ex(PyThreadState *tstate, _PyStackRef func, PyObject *kwnames = NULL; _PyStackRef *newargs; PyObject *const *object_array = NULL; - _PyStackRef stack_array[8]; + _PyStackRef stack_array[8] = {0}; if (has_dict) { object_array = _PyStack_UnpackDict(tstate, _PyTuple_ITEMS(callargs), nargs, kwargs, &kwnames); if (object_array == NULL) { PyStackRef_CLOSE(func); goto error; } - size_t total_args = nargs + PyDict_GET_SIZE(kwargs); + size_t nkwargs = PyDict_GET_SIZE(kwargs); assert(sizeof(PyObject *) == sizeof(_PyStackRef)); newargs = (_PyStackRef *)object_array; - for (size_t i = 0; i < total_args; i++) { - newargs[i] = PyStackRef_FromPyObjectSteal(object_array[i]); + /* Positional args are borrowed from callargs tuple, need new reference */ + for (Py_ssize_t i = 0; i < nargs; i++) { + newargs[i] = PyStackRef_FromPyObjectNew(object_array[i]); + } + /* Keyword args are owned by _PyStack_UnpackDict, steal them */ + for (size_t i = 0; i < nkwargs; i++) { + newargs[nargs + i] = PyStackRef_FromPyObjectSteal(object_array[nargs + i]); } } else { @@ -1941,7 +2106,7 @@ _PyEval_Vector(PyThreadState *tstate, PyFunctionObject *func, if (kwnames) { total_args += PyTuple_GET_SIZE(kwnames); } - _PyStackRef stack_array[8]; + _PyStackRef stack_array[8] = {0}; _PyStackRef *arguments; if (total_args <= 8) { arguments = stack_array; @@ -1987,7 +2152,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, { PyThreadState *tstate = _PyThreadState_GET(); PyObject *res = NULL; - PyObject *defaults = _PyTuple_FromArray(defs, defcount); + PyObject *defaults = PyTuple_FromArray(defs, defcount); if (defaults == NULL) { return NULL; } @@ -2019,138 +2184,36 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, newargs[i] = args[i]; } for (int i = 0; i < kwcount; i++) { - PyTuple_SET_ITEM(kwnames, i, Py_NewRef(kws[2*i])); - newargs[argcount+i] = kws[2*i+1]; - } - allargs = newargs; - } - PyFrameConstructor constr = { - .fc_globals = globals, - .fc_builtins = builtins, - .fc_name = ((PyCodeObject *)_co)->co_name, - .fc_qualname = ((PyCodeObject *)_co)->co_name, - .fc_code = _co, - .fc_defaults = defaults, - .fc_kwdefaults = kwdefs, - .fc_closure = closure - }; - func = _PyFunction_FromConstructor(&constr); - if (func == NULL) { - goto fail; - } - EVAL_CALL_STAT_INC(EVAL_CALL_LEGACY); - res = _PyEval_Vector(tstate, func, locals, - allargs, argcount, - kwnames); -fail: - Py_XDECREF(func); - Py_XDECREF(kwnames); - PyMem_Free(newargs); - _Py_DECREF_BUILTINS(builtins); - Py_DECREF(defaults); - return res; -} - - -/* Logic for the raise statement (too complicated for inlining). - This *consumes* a reference count to each of its arguments. */ -static int -do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause) -{ - PyObject *type = NULL, *value = NULL; - - if (exc == NULL) { - /* Reraise */ - _PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate); - exc = exc_info->exc_value; - if (Py_IsNone(exc) || exc == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "No active exception to reraise"); - return 0; - } - Py_INCREF(exc); - assert(PyExceptionInstance_Check(exc)); - _PyErr_SetRaisedException(tstate, exc); - return 1; - } - - /* We support the following forms of raise: - raise - raise <instance> - raise <type> */ - - if (PyExceptionClass_Check(exc)) { - type = exc; - value = _PyObject_CallNoArgs(exc); - if (value == NULL) - goto raise_error; - if (!PyExceptionInstance_Check(value)) { - _PyErr_Format(tstate, PyExc_TypeError, - "calling %R should have returned an instance of " - "BaseException, not %R", - type, Py_TYPE(value)); - goto raise_error; - } - } - else if (PyExceptionInstance_Check(exc)) { - value = exc; - type = PyExceptionInstance_Class(exc); - Py_INCREF(type); - } - else { - /* Not something you can raise. You get an exception - anyway, just not what you specified :-) */ - Py_DECREF(exc); - _PyErr_SetString(tstate, PyExc_TypeError, - "exceptions must derive from BaseException"); - goto raise_error; - } - - assert(type != NULL); - assert(value != NULL); - - if (cause) { - PyObject *fixed_cause; - if (PyExceptionClass_Check(cause)) { - fixed_cause = _PyObject_CallNoArgs(cause); - if (fixed_cause == NULL) - goto raise_error; - if (!PyExceptionInstance_Check(fixed_cause)) { - _PyErr_Format(tstate, PyExc_TypeError, - "calling %R should have returned an instance of " - "BaseException, not %R", - cause, Py_TYPE(fixed_cause)); - goto raise_error; - } - Py_DECREF(cause); - } - else if (PyExceptionInstance_Check(cause)) { - fixed_cause = cause; - } - else if (Py_IsNone(cause)) { - Py_DECREF(cause); - fixed_cause = NULL; - } - else { - _PyErr_SetString(tstate, PyExc_TypeError, - "exception causes must derive from " - "BaseException"); - goto raise_error; + PyTuple_SET_ITEM(kwnames, i, Py_NewRef(kws[2*i])); + newargs[argcount+i] = kws[2*i+1]; } - PyException_SetCause(value, fixed_cause); + allargs = newargs; } - - _PyErr_SetObject(tstate, type, value); - /* _PyErr_SetObject incref's its arguments */ - Py_DECREF(value); - Py_DECREF(type); - return 0; - -raise_error: - Py_XDECREF(value); - Py_XDECREF(type); - Py_XDECREF(cause); - return 0; + PyFrameConstructor constr = { + .fc_globals = globals, + .fc_builtins = builtins, + .fc_name = ((PyCodeObject *)_co)->co_name, + .fc_qualname = ((PyCodeObject *)_co)->co_name, + .fc_code = _co, + .fc_defaults = defaults, + .fc_kwdefaults = kwdefs, + .fc_closure = closure + }; + func = _PyFunction_FromConstructor(&constr); + if (func == NULL) { + goto fail; + } + EVAL_CALL_STAT_INC(EVAL_CALL_LEGACY); + res = _PyEval_Vector(tstate, func, locals, + allargs, argcount, + kwnames); +fail: + Py_XDECREF(func); + Py_XDECREF(kwnames); + PyMem_Free(newargs); + _Py_DECREF_BUILTINS(builtins); + Py_DECREF(defaults); + return res; } /* Logic for matching an exception in an except* clause (too @@ -2186,14 +2249,19 @@ _PyEval_ExceptionGroupMatch(_PyInterpreterFrame *frame, PyObject* exc_value, return -1; } PyFrameObject *f = _PyFrame_GetFrameObject(frame); - if (f != NULL) { - PyObject *tb = _PyTraceBack_FromFrame(NULL, f); - if (tb == NULL) { - return -1; - } - PyException_SetTraceback(wrapped, tb); - Py_DECREF(tb); + if (f == NULL) { + Py_DECREF(wrapped); + return -1; + } + + PyObject *tb = _PyTraceBack_FromFrame(NULL, f); + if (tb == NULL) { + Py_DECREF(wrapped); + return -1; } + PyException_SetTraceback(wrapped, tb); + Py_DECREF(tb); + *match = wrapped; } *rest = Py_NewRef(Py_None); @@ -2350,116 +2418,24 @@ _PyEval_UnpackIterableStackRef(PyThreadState *tstate, PyObject *v, return 0; } -static int -do_monitor_exc(PyThreadState *tstate, _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr, int event) -{ - assert(event < _PY_MONITORING_UNGROUPED_EVENTS); - if (_PyFrame_GetCode(frame)->co_flags & CO_NO_MONITORING_EVENTS) { - return 0; - } - PyObject *exc = PyErr_GetRaisedException(); - assert(exc != NULL); - int err = _Py_call_instrumentation_arg(tstate, event, frame, instr, exc); - if (err == 0) { - PyErr_SetRaisedException(exc); - } - else { - assert(PyErr_Occurred()); - Py_DECREF(exc); - } - return err; -} - -static inline bool -no_tools_for_global_event(PyThreadState *tstate, int event) -{ - return tstate->interp->monitors.tools[event] == 0; -} -static inline bool -no_tools_for_local_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event) -{ - assert(event < _PY_MONITORING_LOCAL_EVENTS); - _PyCoMonitoringData *data = _PyFrame_GetCode(frame)->_co_monitoring; - if (data) { - return data->active_monitors.tools[event] == 0; - } - else { - return no_tools_for_global_event(tstate, event); - } -} void _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr) { - if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RAISE)) { + if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_RAISE)) { return; } do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RAISE); } -static void -monitor_reraise(PyThreadState *tstate, _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr) -{ - if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RERAISE)) { - return; - } - do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RERAISE); -} - -static int -monitor_stop_iteration(PyThreadState *tstate, _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr, PyObject *value) -{ - if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) { - return 0; - } - assert(!PyErr_Occurred()); - PyErr_SetObject(PyExc_StopIteration, value); - int res = do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_STOP_ITERATION); - if (res < 0) { - return res; - } - PyErr_SetRaisedException(NULL); - return 0; -} - -static void -monitor_unwind(PyThreadState *tstate, - _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr) -{ - if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_UNWIND)) { - return; - } - do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_UNWIND); -} - - -static int -monitor_handled(PyThreadState *tstate, - _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr, PyObject *exc) +bool +_PyEval_NoToolsForUnwind(PyThreadState *tstate, _PyInterpreterFrame *frame) { - if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) { - return 0; - } - return _Py_call_instrumentation_arg(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED, frame, instr, exc); + return no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_PY_UNWIND); } -static void -monitor_throw(PyThreadState *tstate, - _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr) -{ - if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_THROW)) { - return; - } - do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_THROW); -} void PyThreadState_EnterTracing(PyThreadState *tstate) @@ -2505,21 +2481,10 @@ PyEval_SetProfile(Py_tracefunc func, PyObject *arg) void PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *arg) { - PyThreadState *this_tstate = _PyThreadState_GET(); - PyInterpreterState* interp = this_tstate->interp; - - _PyRuntimeState *runtime = &_PyRuntime; - HEAD_LOCK(runtime); - PyThreadState* ts = PyInterpreterState_ThreadHead(interp); - HEAD_UNLOCK(runtime); - - while (ts) { - if (_PyEval_SetProfile(ts, func, arg) < 0) { - PyErr_FormatUnraisable("Exception ignored in PyEval_SetProfileAllThreads"); - } - HEAD_LOCK(runtime); - ts = PyThreadState_Next(ts); - HEAD_UNLOCK(runtime); + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_PyEval_SetProfileAllThreads(interp, func, arg) < 0) { + /* Log _PySys_Audit() error */ + PyErr_FormatUnraisable("Exception ignored in PyEval_SetProfileAllThreads"); } } @@ -2536,21 +2501,10 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg) void PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *arg) { - PyThreadState *this_tstate = _PyThreadState_GET(); - PyInterpreterState* interp = this_tstate->interp; - - _PyRuntimeState *runtime = &_PyRuntime; - HEAD_LOCK(runtime); - PyThreadState* ts = PyInterpreterState_ThreadHead(interp); - HEAD_UNLOCK(runtime); - - while (ts) { - if (_PyEval_SetTrace(ts, func, arg) < 0) { - PyErr_FormatUnraisable("Exception ignored in PyEval_SetTraceAllThreads"); - } - HEAD_LOCK(runtime); - ts = PyThreadState_Next(ts); - HEAD_UNLOCK(runtime); + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_PyEval_SetTraceAllThreads(interp, func, arg) < 0) { + /* Log _PySys_Audit() error */ + PyErr_FormatUnraisable("Exception ignored in PyEval_SetTraceAllThreads"); } } @@ -2663,12 +2617,6 @@ _PyEval_GetBuiltin(PyObject *name) return attr; } -PyObject * -_PyEval_GetBuiltinId(_Py_Identifier *name) -{ - return _PyEval_GetBuiltin(_PyUnicode_FromId(name)); -} - PyObject * PyEval_GetLocals(void) { @@ -2689,6 +2637,11 @@ PyEval_GetLocals(void) if (PyFrameLocalsProxy_Check(locals)) { PyFrameObject *f = _PyFrame_GetFrameObject(current_frame); + if (f == NULL) { + Py_DECREF(locals); + return NULL; + } + PyObject *ret = f->f_locals_cache; if (ret == NULL) { ret = PyDict_New(); @@ -2785,7 +2738,7 @@ static PyObject * get_globals_builtins(PyObject *globals) { PyObject *builtins = NULL; - if (PyDict_Check(globals)) { + if (PyAnyDict_Check(globals)) { if (PyDict_GetItemRef(globals, &_Py_ID(__builtins__), &builtins) < 0) { return NULL; } @@ -2810,6 +2763,10 @@ set_globals_builtins(PyObject *globals, PyObject *builtins) } else { if (PyObject_SetItem(globals, &_Py_ID(__builtins__), builtins) < 0) { + if (PyFrozenDict_Check(globals)) { + PyErr_SetString(PyExc_TypeError, + "cannot assign __builtins__ to frozendict globals"); + } return -1; } } @@ -2899,6 +2856,9 @@ PyEval_MergeCompilerFlags(PyCompilerFlags *cf) { PyThreadState *tstate = _PyThreadState_GET(); _PyInterpreterFrame *current_frame = tstate->current_frame; + if (current_frame == tstate->base_frame) { + current_frame = NULL; + } int result = cf->cf_flags != 0; if (current_frame != NULL) { @@ -2948,23 +2908,10 @@ PyEval_GetFuncDesc(PyObject *func) int _PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi) { - PyThreadState *tstate = _PyThreadState_GET(); - if (!Py_IsNone(v)) { - Py_ssize_t x; - if (_PyIndex_Check(v)) { - x = PyNumber_AsSsize_t(v, NULL); - if (x == -1 && _PyErr_Occurred(tstate)) - return 0; - } - else { - _PyErr_SetString(tstate, PyExc_TypeError, - "slice indices must be integers or " - "None or have an __index__ method"); - return 0; - } - *pi = x; + if (Py_IsNone(v)) { + return 1; } - return 1; + return _PyEval_SliceIndexNotNone(v, pi); } int @@ -2972,6 +2919,10 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi) { PyThreadState *tstate = _PyThreadState_GET(); Py_ssize_t x; + if (PyLong_CheckExact(v) && _PyLong_IsCompact((PyLongObject *)v)) { + *pi = _PyLong_CompactValue((PyLongObject *)v); + return 1; + } if (_PyIndex_Check(v)) { x = PyNumber_AsSsize_t(v, NULL); if (x == -1 && _PyErr_Occurred(tstate)) @@ -2987,12 +2938,34 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi) return 1; } +int +_PyEval_UnpackIndices(PyObject *start, PyObject *stop, + Py_ssize_t len, + Py_ssize_t *istart, Py_ssize_t *istop) +{ + if (len < 0) { + return 0; + } + *istart = 0; + *istop = PY_SSIZE_T_MAX; + if (!_PyEval_SliceIndex(start, istart)) { + return 0; + } + if (!_PyEval_SliceIndex(stop, istop)) { + return 0; + } + PySlice_AdjustIndices(len, istart, istop, 1); + return 1; +} + PyObject * -_PyEval_ImportName(PyThreadState *tstate, _PyInterpreterFrame *frame, - PyObject *name, PyObject *fromlist, PyObject *level) +_PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, + PyObject *globals, PyObject *locals, PyObject *name, + PyObject *fromlist, PyObject *level) { PyObject *import_func; - if (PyMapping_GetOptionalItem(frame->f_builtins, &_Py_ID(__import__), &import_func) < 0) { + if (PyMapping_GetOptionalItem(builtins, &_Py_ID(__import__), + &import_func) < 0) { return NULL; } if (import_func == NULL) { @@ -3000,29 +2973,153 @@ _PyEval_ImportName(PyThreadState *tstate, _PyInterpreterFrame *frame, return NULL; } - PyObject *locals = frame->f_locals; + PyObject *res = _PyEval_ImportNameWithImport( + tstate, import_func, globals, locals, name, fromlist, level); + Py_DECREF(import_func); + return res; +} + +PyObject * +_PyEval_ImportNameWithImport(PyThreadState *tstate, PyObject *import_func, + PyObject *globals, PyObject *locals, + PyObject *name, PyObject *fromlist, PyObject *level) +{ if (locals == NULL) { locals = Py_None; } /* Fast path for not overloaded __import__. */ if (_PyImport_IsDefaultImportFunc(tstate->interp, import_func)) { - Py_DECREF(import_func); int ilevel = PyLong_AsInt(level); if (ilevel == -1 && _PyErr_Occurred(tstate)) { return NULL; } return PyImport_ImportModuleLevelObject( name, - frame->f_globals, + globals, locals, fromlist, ilevel); } - PyObject* args[5] = {name, frame->f_globals, locals, fromlist, level}; + PyObject *args[5] = {name, globals, locals, fromlist, level}; PyObject *res = PyObject_Vectorcall(import_func, args, 5, NULL); - Py_DECREF(import_func); + return res; +} + +static int +check_lazy_import_compatibility(PyThreadState *tstate, PyObject *globals, + PyObject *name, PyObject *level) +{ + // Check if this module should be imported lazily due to + // the compatibility mode support via __lazy_modules__. + PyObject *lazy_modules = NULL; + PyObject *abs_name = NULL; + int res = -1; + + if (globals != NULL && + PyMapping_GetOptionalItem(globals, &_Py_ID(__lazy_modules__), + &lazy_modules) < 0) + { + return -1; + } + if (lazy_modules == NULL) { + assert(!PyErr_Occurred()); + return 0; + } + + int ilevel = PyLong_AsInt(level); + if (ilevel == -1 && _PyErr_Occurred(tstate)) { + goto error; + } + + abs_name = _PyImport_GetAbsName(tstate, name, globals, ilevel); + if (abs_name == NULL) { + goto error; + } + + res = PySequence_Contains(lazy_modules, abs_name); +error: + Py_XDECREF(abs_name); + Py_XDECREF(lazy_modules); + return res; +} + +static int +is_lazy_import_module_level(void) +{ + _PyInterpreterFrame *frame = _PyEval_GetFrame(); + return frame != NULL && frame->f_globals == frame->f_locals; +} + +PyObject * +_PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, + PyObject *globals, PyObject *locals, PyObject *name, + PyObject *fromlist, PyObject *level, int lazy) +{ + PyObject *res = NULL; + // Check if global policy overrides the local syntax + switch (PyImport_GetLazyImportsMode()) { + case PyImport_LAZY_ALL: + if (!lazy) { + lazy = is_lazy_import_module_level(); + } + break; + case PyImport_LAZY_NORMAL: + break; + } + + if (!lazy) { + // See if __lazy_modules__ forces this to be lazy. + // __lazy_modules__ only applies at module level; exec() inside + // functions or classes should remain eager. + if (is_lazy_import_module_level()) { + lazy = check_lazy_import_compatibility(tstate, globals, name, level); + if (lazy < 0) { + return NULL; + } + } + } + + if (!lazy) { + // Not a lazy import or lazy imports are disabled, fallback to the + // regular import. + return _PyEval_ImportName(tstate, builtins, globals, locals, + name, fromlist, level); + } + + PyObject *lazy_import_func; + if (PyMapping_GetOptionalItem(builtins, &_Py_ID(__lazy_import__), + &lazy_import_func) < 0) { + goto error; + } + if (lazy_import_func == NULL) { + assert(!PyErr_Occurred()); + _PyErr_SetString(tstate, PyExc_ImportError, + "__lazy_import__ not found"); + goto error; + } + + if (locals == NULL) { + locals = Py_None; + } + + if (_PyImport_IsDefaultLazyImportFunc(tstate->interp, lazy_import_func)) { + int ilevel = PyLong_AsInt(level); + if (ilevel == -1 && PyErr_Occurred()) { + goto error; + } + + res = _PyImport_LazyImportModuleLevelObject( + tstate, name, builtins, globals, locals, fromlist, ilevel + ); + goto error; + } + + PyObject *args[6] = {name, globals, locals, fromlist, level, builtins}; + res = PyObject_Vectorcall(lazy_import_func, args, 6, NULL); +error: + Py_XDECREF(lazy_import_func); return res; } @@ -3194,6 +3291,64 @@ _PyEval_ImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name) return NULL; } +PyObject * +_PyEval_LazyImportFrom(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *v, PyObject *name) +{ + assert(PyLazyImport_CheckExact(v)); + assert(name); + assert(PyUnicode_Check(name)); + PyObject *ret; + PyLazyImportObject *d = (PyLazyImportObject *)v; + PyObject *mod = PyImport_GetModule(d->lz_from); + if (mod != NULL) { + // Check if the module already has the attribute, if so, resolve it + // eagerly. + if (PyModule_Check(mod)) { + PyObject *mod_dict = PyModule_GetDict(mod); + if (mod_dict != NULL) { + if (PyDict_GetItemRef(mod_dict, name, &ret) < 0) { + Py_DECREF(mod); + return NULL; + } + if (ret != NULL) { + Py_DECREF(mod); + return ret; + } + } + } + Py_DECREF(mod); + } + + if (d->lz_attr != NULL) { + if (PyUnicode_Check(d->lz_attr)) { + PyObject *from = PyUnicode_FromFormat( + "%U.%U", d->lz_from, d->lz_attr); + if (from == NULL) { + return NULL; + } + ret = _PyLazyImport_New(frame, d->lz_builtins, from, name); + Py_DECREF(from); + return ret; + } + } + else { + Py_ssize_t dot = PyUnicode_FindChar( + d->lz_from, '.', 0, PyUnicode_GET_LENGTH(d->lz_from), 1 + ); + if (dot >= 0) { + PyObject *from = PyUnicode_Substring(d->lz_from, 0, dot); + if (from == NULL) { + return NULL; + } + ret = _PyLazyImport_New(frame, d->lz_builtins, from, name); + Py_DECREF(from); + return ret; + } + } + ret = _PyLazyImport_New(frame, d->lz_builtins, d->lz_from, name); + return ret; +} + #define CANNOT_CATCH_MSG "catching classes that do not inherit from "\ "BaseException is not allowed" @@ -3266,62 +3421,45 @@ int _Py_Check_ArgsIterable(PyThreadState *tstate, PyObject *func, PyObject *args) { if (Py_TYPE(args)->tp_iter == NULL && !PySequence_Check(args)) { - /* _Py_Check_ArgsIterable() may be called with a live exception: - * clear it to prevent calling _PyObject_FunctionStr() with an - * exception set. */ - _PyErr_Clear(tstate); - PyObject *funcstr = _PyObject_FunctionStr(func); - if (funcstr != NULL) { - _PyErr_Format(tstate, PyExc_TypeError, - "%U argument after * must be an iterable, not %.200s", - funcstr, Py_TYPE(args)->tp_name); - Py_DECREF(funcstr); - } + _PyErr_Format(tstate, PyExc_TypeError, + "Value after * must be an iterable, not %.200s", + Py_TYPE(args)->tp_name); return -1; } return 0; } void -_PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs) +_PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs, PyObject *dupkey) { - /* _PyDict_MergeEx raises attribute + if (dupkey != NULL) { + PyObject *funcstr = _PyObject_FunctionStr(func); + _PyErr_Format( + tstate, PyExc_TypeError, + "%V got multiple values for keyword argument '%S'", + funcstr, "function", dupkey); + Py_XDECREF(funcstr); + return; + } + /* _PyDict_MergeUniq raises attribute * error (percolated from an attempt * to get 'keys' attribute) instead of * a type error if its second argument * is not a mapping. */ if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) { - _PyErr_Clear(tstate); - PyObject *funcstr = _PyObject_FunctionStr(func); - if (funcstr != NULL) { + PyObject *exc = _PyErr_GetRaisedException(tstate); + int has_keys = PyObject_HasAttrWithError(kwargs, &_Py_ID(keys)); + if (has_keys == 0) { _PyErr_Format( tstate, PyExc_TypeError, - "%U argument after ** must be a mapping, not %.200s", - funcstr, Py_TYPE(kwargs)->tp_name); - Py_DECREF(funcstr); - } - } - else if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - PyObject *exc = _PyErr_GetRaisedException(tstate); - PyObject *args = PyException_GetArgs(exc); - if (PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1) { - _PyErr_Clear(tstate); - PyObject *funcstr = _PyObject_FunctionStr(func); - if (funcstr != NULL) { - PyObject *key = PyTuple_GET_ITEM(args, 0); - _PyErr_Format( - tstate, PyExc_TypeError, - "%U got multiple values for keyword argument '%S'", - funcstr, key); - Py_DECREF(funcstr); - } - Py_XDECREF(exc); + "Value after ** must be a mapping, not %T", + kwargs); + Py_DECREF(exc); } else { - _PyErr_SetRaisedException(tstate, exc); + _PyErr_ChainExceptions1Tstate(tstate, exc); } - Py_DECREF(args); } } @@ -3397,11 +3535,27 @@ PyUnstable_Eval_RequestCodeExtraIndex(freefunc free) PyInterpreterState *interp = _PyInterpreterState_GET(); Py_ssize_t new_index; - if (interp->co_extra_user_count == MAX_CO_EXTRA_USERS - 1) { +#ifdef Py_GIL_DISABLED + struct _py_code_state *state = &interp->code_state; + FT_MUTEX_LOCK(&state->mutex); +#endif + + if (interp->co_extra_user_count >= MAX_CO_EXTRA_USERS - 1) { +#ifdef Py_GIL_DISABLED + FT_MUTEX_UNLOCK(&state->mutex); +#endif return -1; } - new_index = interp->co_extra_user_count++; + + new_index = interp->co_extra_user_count; interp->co_extra_freefuncs[new_index] = free; + + // Publish freefuncs[new_index] before making the index visible. + FT_ATOMIC_STORE_SSIZE_RELEASE(interp->co_extra_user_count, new_index + 1); + +#ifdef Py_GIL_DISABLED + FT_MUTEX_UNLOCK(&state->mutex); +#endif return new_index; } @@ -3460,7 +3614,7 @@ _PyEval_GetANext(PyObject *aiter) void _PyEval_LoadGlobalStackRef(PyObject *globals, PyObject *builtins, PyObject *name, _PyStackRef *writeto) { - if (PyDict_CheckExact(globals) && PyDict_CheckExact(builtins)) { + if (PyAnyDict_CheckExact(globals) && PyAnyDict_CheckExact(builtins)) { _PyDict_LoadGlobalStackRef((PyDictObject *)globals, (PyDictObject *)builtins, name, writeto); @@ -3495,6 +3649,24 @@ _PyEval_LoadGlobalStackRef(PyObject *globals, PyObject *builtins, PyObject *name } *writeto = PyStackRef_FromPyObjectSteal(res); } + + PyObject *res_o = PyStackRef_AsPyObjectBorrow(*writeto); + if (res_o != NULL && PyLazyImport_CheckExact(res_o)) { + PyObject *l_v = _PyImport_LoadLazyImportTstate(PyThreadState_GET(), res_o); + PyStackRef_CLOSE(writeto[0]); + if (l_v == NULL) { + assert(PyErr_Occurred()); + *writeto = PyStackRef_NULL; + return; + } + int err = PyDict_SetItem(globals, name, l_v); + if (err < 0) { + Py_DECREF(l_v); + *writeto = PyStackRef_NULL; + return; + } + *writeto = PyStackRef_FromPyObjectSteal(l_v); + } } PyObject * @@ -3507,15 +3679,15 @@ _PyEval_GetAwaitable(PyObject *iterable, int oparg) Py_TYPE(iterable), oparg); } else if (PyCoro_CheckExact(iter)) { - PyObject *yf = _PyGen_yf((PyGenObject*)iter); - if (yf != NULL) { - /* `iter` is a coroutine object that is being - awaited, `yf` is a pointer to the current awaitable - being awaited on. */ - Py_DECREF(yf); + PyCoroObject *coro = (PyCoroObject *)iter; + int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(coro->cr_frame_state); + if (frame_state == FRAME_SUSPENDED_YIELD_FROM || + frame_state == FRAME_SUSPENDED_YIELD_FROM_LOCKED) + { + /* `iter` is a coroutine object that is being awaited. */ Py_CLEAR(iter); _PyErr_SetString(PyThreadState_GET(), PyExc_RuntimeError, - "coroutine is being awaited already"); + "coroutine is being awaited already"); } } return iter; @@ -3554,36 +3726,24 @@ _PyEval_LoadName(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject *na return value; } -static _PyStackRef -foriter_next(PyObject *seq, _PyStackRef index) -{ - assert(PyStackRef_IsTaggedInt(index)); - assert(PyTuple_CheckExact(seq) || PyList_CheckExact(seq)); - intptr_t i = PyStackRef_UntagInt(index); - if (PyTuple_CheckExact(seq)) { - size_t size = PyTuple_GET_SIZE(seq); - if ((size_t)i >= size) { - return PyStackRef_NULL; - } - return PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq, i)); - } - PyObject *item = _PyList_GetItemRef((PyListObject *)seq, i); - if (item == NULL) { - return PyStackRef_NULL; - } - return PyStackRef_FromPyObjectSteal(item); -} - _PyStackRef _PyForIter_VirtualIteratorNext(PyThreadState* tstate, _PyInterpreterFrame* frame, _PyStackRef iter, _PyStackRef* index_ptr) { PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); _PyStackRef index = *index_ptr; if (PyStackRef_IsTaggedInt(index)) { - *index_ptr = PyStackRef_IncrementTaggedIntNoOverflow(index); - return foriter_next(iter_o, index); - } - PyObject *next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o); - if (next_o == NULL) { + intptr_t i = PyStackRef_UntagInt(index); + assert(i >= 0); + _PyObjectIndexPair next_index = Py_TYPE(iter_o)->_tp_iteritem(iter_o, i); + i = next_index.index; + PyObject *next = next_index.object; + if (next == NULL) { + return i < 0 ? PyStackRef_ERROR : PyStackRef_NULL; + } + *index_ptr = PyStackRef_TagInt(i); + return PyStackRef_FromPyObjectSteal(next); + } + PyObject *next = (*Py_TYPE(iter_o)->tp_iternext)(iter_o); + if (next == NULL) { if (_PyErr_Occurred(tstate)) { if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { _PyEval_MonitorRaise(tstate, frame, frame->instr_ptr); @@ -3595,7 +3755,7 @@ _PyStackRef _PyForIter_VirtualIteratorNext(PyThreadState* tstate, _PyInterpreter } return PyStackRef_NULL; } - return PyStackRef_FromPyObjectSteal(next_o); + return PyStackRef_FromPyObjectSteal(next); } /* Check if a 'cls' provides the given special method. */ diff --git a/Python/ceval.h b/Python/ceval.h new file mode 100644 index 00000000000000..0437ab85c5a668 --- /dev/null +++ b/Python/ceval.h @@ -0,0 +1,626 @@ +#define _PY_INTERPRETER + +#include "Python.h" +#include "pycore_abstract.h" // _PyIndex_Check() +#include "pycore_audit.h" // _PySys_Audit() +#include "pycore_backoff.h" +#include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_cell.h" // PyCell_GetRef() +#include "pycore_ceval.h" // SPECIAL___ENTER__ +#include "pycore_code.h" +#include "pycore_dict.h" +#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS +#include "pycore_floatobject.h" // _PyFloat_ExactDealloc() +#include "pycore_frame.h" +#include "pycore_function.h" +#include "pycore_genobject.h" // _PyCoro_GetAwaitableIter() +#include "pycore_import.h" // _PyImport_IsDefaultImportFunc() +#include "pycore_instruments.h" +#include "pycore_interpframe.h" // _PyFrame_SetStackPointer() +#include "pycore_interpolation.h" // _PyInterpolation_Build() +#include "pycore_intrinsics.h" +#include "pycore_jit.h" +#include "pycore_lazyimportobject.h" +#include "pycore_list.h" // _PyList_GetItemRef() +#include "pycore_long.h" // _PyLong_GetZero() +#include "pycore_moduleobject.h" // PyModuleObject +#include "pycore_object.h" // _PyObject_GC_TRACK() +#include "pycore_opcode_metadata.h" // EXTRA_CASES +#include "pycore_opcode_utils.h" // MAKE_FUNCTION_* +#include "pycore_optimizer.h" // _PyUOpExecutor_Type +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_* +#include "pycore_pyerrors.h" // _PyErr_GetRaisedException() +#include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_range.h" // _PyRangeIterObject +#include "pycore_setobject.h" // _PySet_Update() +#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs +#include "pycore_sysmodule.h" // _PySys_GetOptionalAttrString() +#include "pycore_template.h" // _PyTemplate_Build() +#include "pycore_traceback.h" // _PyTraceBack_FromFrame +#include "pycore_tuple.h" // _PyTuple_ITEMS() +#include "pycore_uop_ids.h" // Uops + +#include "dictobject.h" +#include "frameobject.h" // _PyInterpreterFrame_GetLine +#include "opcode.h" +#include "pydtrace.h" +#include "setobject.h" +#include "pycore_stackref.h" + +#include <stdbool.h> // bool + +#if !defined(Py_BUILD_CORE) +# error "ceval.c must be build with Py_BUILD_CORE define for best performance" +#endif + +#if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) +// GH-89279: The MSVC compiler does not inline these static inline functions +// in PGO build in _PyEval_EvalFrameDefault(), because this function is over +// the limit of PGO, and that limit cannot be configured. +// Define them as macros to make sure that they are always inlined by the +// preprocessor. + +#undef Py_IS_TYPE +#define Py_IS_TYPE(ob, type) \ + (_PyObject_CAST(ob)->ob_type == (type)) + +#undef Py_XDECREF +#define Py_XDECREF(arg) \ + do { \ + PyObject *xop = _PyObject_CAST(arg); \ + if (xop != NULL) { \ + Py_DECREF(xop); \ + } \ + } while (0) + +#ifndef Py_GIL_DISABLED + +#undef Py_DECREF +#define Py_DECREF(arg) \ + do { \ + PyObject *op = _PyObject_CAST(arg); \ + if (_Py_IsImmortal(op)) { \ + _Py_DECREF_IMMORTAL_STAT_INC(); \ + break; \ + } \ + _Py_DECREF_STAT_INC(); \ + if (--op->ob_refcnt == 0) { \ + _PyReftracerTrack(op, PyRefTracer_DESTROY); \ + destructor dealloc = Py_TYPE(op)->tp_dealloc; \ + (*dealloc)(op); \ + } \ + } while (0) + +#undef _Py_DECREF_SPECIALIZED +#define _Py_DECREF_SPECIALIZED(arg, dealloc) \ + do { \ + PyObject *op = _PyObject_CAST(arg); \ + if (_Py_IsImmortal(op)) { \ + _Py_DECREF_IMMORTAL_STAT_INC(); \ + break; \ + } \ + _Py_DECREF_STAT_INC(); \ + if (--op->ob_refcnt == 0) { \ + _PyReftracerTrack(op, PyRefTracer_DESTROY); \ + destructor d = (destructor)(dealloc); \ + d(op); \ + } \ + } while (0) + +#else // Py_GIL_DISABLED + +#undef Py_DECREF +#define Py_DECREF(arg) \ + do { \ + PyObject *op = _PyObject_CAST(arg); \ + uint32_t local = _Py_atomic_load_uint32_relaxed(&op->ob_ref_local); \ + if (local == _Py_IMMORTAL_REFCNT_LOCAL) { \ + _Py_DECREF_IMMORTAL_STAT_INC(); \ + break; \ + } \ + _Py_DECREF_STAT_INC(); \ + if (_Py_IsOwnedByCurrentThread(op)) { \ + local--; \ + _Py_atomic_store_uint32_relaxed(&op->ob_ref_local, local); \ + if (local == 0) { \ + _Py_MergeZeroLocalRefcount(op); \ + } \ + } \ + else { \ + _Py_DecRefShared(op); \ + } \ + } while (0) + +#undef _Py_DECREF_SPECIALIZED +#define _Py_DECREF_SPECIALIZED(arg, dealloc) Py_DECREF(arg) + +#endif +#endif + +static void +check_invalid_reentrancy(void) +{ +#if defined(Py_DEBUG) && defined(Py_GIL_DISABLED) + // In the free-threaded build, the interpreter must not be re-entered if + // the world-is-stopped. If so, that's a bug somewhere (quite likely in + // the painfully complex typeobject code). + PyInterpreterState *interp = _PyInterpreterState_GET(); + assert(!interp->stoptheworld.world_stopped); +#endif +} + + +#ifdef Py_DEBUG +static void +dump_item(_PyStackRef item) +{ + if (PyStackRef_IsNull(item)) { + printf("<NULL>"); + return; + } + if (PyStackRef_IsMalformed(item)) { + printf("<INVALID>"); + return; + } + if (PyStackRef_IsTaggedInt(item)) { + printf("%" PRId64, (int64_t)PyStackRef_UntagInt(item)); + return; + } + PyObject *obj = PyStackRef_AsPyObjectBorrow(item); + if (obj == NULL) { + printf("<nil>"); + return; + } + // Don't call __repr__(), it might recurse into the interpreter. + printf("<%s at %p>", Py_TYPE(obj)->tp_name, (void *)obj); +} + +static void +dump_stack(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer) +{ + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef *locals_base = _PyFrame_GetLocalsArray(frame); + _PyStackRef *stack_base = _PyFrame_Stackbase(frame); + PyObject *exc = PyErr_GetRaisedException(); + printf(" locals=["); + for (_PyStackRef *ptr = locals_base; ptr < stack_base; ptr++) { + if (ptr != locals_base) { + printf(", "); + } + dump_item(*ptr); + } + printf("]\n"); + if (stack_pointer < stack_base) { + printf(" stack=%d\n", (int)(stack_pointer-stack_base)); + } + else { + printf(" stack=["); + for (_PyStackRef *ptr = stack_base; ptr < stack_pointer; ptr++) { + if (ptr != stack_base) { + printf(", "); + } + dump_item(*ptr); + } + printf("]\n"); + } + fflush(stdout); + PyErr_SetRaisedException(exc); + _PyFrame_GetStackPointer(frame); +} + +#if defined(_Py_TIER2) && !defined(_Py_JIT) && defined(Py_DEBUG) +static void +dump_cache_item(_PyStackRef cache, int position, int depth) +{ + if (position < depth) { + dump_item(cache); + } + else { + printf("---"); + } +} +#endif + +static void +lltrace_instruction(_PyInterpreterFrame *frame, + _PyStackRef *stack_pointer, + _Py_CODEUNIT *next_instr, + int opcode, + int oparg) +{ + int offset = 0; + if (frame->owner < FRAME_OWNED_BY_INTERPRETER) { + dump_stack(frame, stack_pointer); + offset = (int)(next_instr - _PyFrame_GetBytecode(frame)); + } + const char *opname = _PyOpcode_OpName[opcode]; + assert(opname != NULL); + if (OPCODE_HAS_ARG((int)_PyOpcode_Deopt[opcode])) { + printf("%d: %s %d\n", offset * 2, opname, oparg); + } + else { + printf("%d: %s\n", offset * 2, opname); + } + fflush(stdout); +} + +static void +lltrace_resume_frame(_PyInterpreterFrame *frame) +{ + PyObject *fobj = PyStackRef_AsPyObjectBorrow(frame->f_funcobj); + if (!PyStackRef_CodeCheck(frame->f_executable) || + fobj == NULL || + !PyFunction_Check(fobj) + ) { + printf("\nResuming frame.\n"); + return; + } + PyFunctionObject *f = (PyFunctionObject *)fobj; + PyObject *exc = PyErr_GetRaisedException(); + PyObject *name = f->func_qualname; + if (name == NULL) { + name = f->func_name; + } + printf("\nResuming frame"); + if (name) { + printf(" for "); + if (PyObject_Print(name, stdout, 0) < 0) { + PyErr_Clear(); + } + } + if (f->func_module) { + printf(" in module "); + if (PyObject_Print(f->func_module, stdout, 0) < 0) { + PyErr_Clear(); + } + } + printf("\n"); + fflush(stdout); + PyErr_SetRaisedException(exc); +} + +static int +maybe_lltrace_resume_frame(_PyInterpreterFrame *frame, PyObject *globals) +{ + if (globals == NULL) { + return 0; + } + if (frame->owner >= FRAME_OWNED_BY_INTERPRETER) { + return 0; + } + int r = PyDict_Contains(globals, &_Py_ID(__lltrace__)); + if (r < 0) { + PyErr_Clear(); + return 0; + } + int lltrace = r * 5; // Levels 1-4 only trace uops + if (!lltrace) { + // Can also be controlled by environment variable + char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); + if (python_lltrace != NULL && *python_lltrace >= '0') { + lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that + } + } + if (lltrace >= 5) { + lltrace_resume_frame(frame); + } + return lltrace; +} + +#endif + +static void monitor_reraise(PyThreadState *tstate, + _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr); +static int monitor_stop_iteration(PyThreadState *tstate, + _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr, + PyObject *value); +static void monitor_unwind(PyThreadState *tstate, + _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr); +static int monitor_handled(PyThreadState *tstate, + _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr, PyObject *exc); +static void monitor_throw(PyThreadState *tstate, + _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr); + +static int get_exception_handler(PyCodeObject *, int, int*, int*, int*); + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +typedef struct { + _PyInterpreterFrame frame; + _PyStackRef stack[1]; +} _PyEntryFrame; + +static int +do_monitor_exc(PyThreadState *tstate, _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr, int event) +{ + assert(event < _PY_MONITORING_UNGROUPED_EVENTS); + if (_PyFrame_GetCode(frame)->co_flags & CO_NO_MONITORING_EVENTS) { + return 0; + } + PyObject *exc = PyErr_GetRaisedException(); + assert(exc != NULL); + int err = _Py_call_instrumentation_arg(tstate, event, frame, instr, exc); + if (err == 0) { + PyErr_SetRaisedException(exc); + } + else { + assert(PyErr_Occurred()); + Py_DECREF(exc); + } + return err; +} + +static inline bool +no_tools_for_global_event(PyThreadState *tstate, int event) +{ + return tstate->interp->monitors.tools[event] == 0; +} + +static inline bool +no_tools_for_local_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event) +{ + assert(event < _PY_MONITORING_UNGROUPED_EVENTS); + _PyCoMonitoringData *data = _PyFrame_GetCode(frame)->_co_monitoring; + if (data) { + return data->active_monitors.tools[event] == 0; + } + else { + return no_tools_for_global_event(tstate, event); + } +} + +static int +monitor_handled(PyThreadState *tstate, + _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr, PyObject *exc) +{ + if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) { + return 0; + } + return _Py_call_instrumentation_arg(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED, frame, instr, exc); +} + +static void +monitor_throw(PyThreadState *tstate, + _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr) +{ + if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_PY_THROW)) { + return; + } + do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_THROW); +} + +static void +monitor_reraise(PyThreadState *tstate, _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr) +{ + if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_RERAISE)) { + return; + } + do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RERAISE); +} + +static int +monitor_stop_iteration(PyThreadState *tstate, _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr, PyObject *value) +{ + if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) { + return 0; + } + assert(!PyErr_Occurred()); + PyErr_SetObject(PyExc_StopIteration, value); + int res = do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_STOP_ITERATION); + if (res < 0) { + return res; + } + PyErr_SetRaisedException(NULL); + return 0; +} + +static void +monitor_unwind(PyThreadState *tstate, + _PyInterpreterFrame *frame, + _Py_CODEUNIT *instr) +{ + if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_PY_UNWIND)) { + return; + } + do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_UNWIND); +} + +static inline unsigned char * +scan_back_to_entry_start(unsigned char *p) { + for (; (p[0]&128) == 0; p--); + return p; +} + +static inline unsigned char * +skip_to_next_entry(unsigned char *p, unsigned char *end) { + while (p < end && ((p[0] & 128) == 0)) { + p++; + } + return p; +} + + +#define MAX_LINEAR_SEARCH 40 + +static Py_NO_INLINE int +get_exception_handler(PyCodeObject *code, int index, int *level, int *handler, int *lasti) +{ + unsigned char *start = (unsigned char *)PyBytes_AS_STRING(code->co_exceptiontable); + unsigned char *end = start + PyBytes_GET_SIZE(code->co_exceptiontable); + /* Invariants: + * start_table == end_table OR + * start_table points to a legal entry and end_table points + * beyond the table or to a legal entry that is after index. + */ + if (end - start > MAX_LINEAR_SEARCH) { + int offset; + parse_varint(start, &offset); + if (offset > index) { + return 0; + } + do { + unsigned char * mid = start + ((end-start)>>1); + mid = scan_back_to_entry_start(mid); + parse_varint(mid, &offset); + if (offset > index) { + end = mid; + } + else { + start = mid; + } + + } while (end - start > MAX_LINEAR_SEARCH); + } + unsigned char *scan = start; + while (scan < end) { + int start_offset, size; + scan = parse_varint(scan, &start_offset); + if (start_offset > index) { + break; + } + scan = parse_varint(scan, &size); + if (start_offset + size > index) { + scan = parse_varint(scan, handler); + int depth_and_lasti; + parse_varint(scan, &depth_and_lasti); + *level = depth_and_lasti >> 1; + *lasti = depth_and_lasti & 1; + return 1; + } + scan = skip_to_next_entry(scan, end); + } + return 0; +} + + +#ifdef Py_DEBUG +#define ASSERT_WITHIN_STACK_BOUNDS(F, L) _Py_assert_within_stack_bounds(frame, stack_pointer, (F), (L)) +#else +#define ASSERT_WITHIN_STACK_BOUNDS(F, L) (void)0 +#endif + +/* Logic for the raise statement (too complicated for inlining). + This *consumes* a reference count to each of its arguments. */ +static int +do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause) +{ + PyObject *type = NULL, *value = NULL; + + if (exc == NULL) { + /* Reraise */ + _PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate); + exc = exc_info->exc_value; + if (Py_IsNone(exc) || exc == NULL) { + _PyErr_SetString(tstate, PyExc_RuntimeError, + "No active exception to reraise"); + return 0; + } + Py_INCREF(exc); + assert(PyExceptionInstance_Check(exc)); + _PyErr_SetRaisedException(tstate, exc); + return 1; + } + + /* We support the following forms of raise: + raise + raise <instance> + raise <type> */ + + if (PyExceptionClass_Check(exc)) { + type = exc; + value = _PyObject_CallNoArgs(exc); + if (value == NULL) + goto raise_error; + if (!PyExceptionInstance_Check(value)) { + _PyErr_Format(tstate, PyExc_TypeError, + "calling %R should have returned an instance of " + "BaseException, not %R", + type, Py_TYPE(value)); + goto raise_error; + } + } + else if (PyExceptionInstance_Check(exc)) { + value = exc; + type = PyExceptionInstance_Class(exc); + Py_INCREF(type); + } + else { + /* Not something you can raise. You get an exception + anyway, just not what you specified :-) */ + Py_DECREF(exc); + _PyErr_SetString(tstate, PyExc_TypeError, + "exceptions must derive from BaseException"); + goto raise_error; + } + + assert(type != NULL); + assert(value != NULL); + + if (cause) { + PyObject *fixed_cause; + if (PyExceptionClass_Check(cause)) { + fixed_cause = _PyObject_CallNoArgs(cause); + if (fixed_cause == NULL) + goto raise_error; + if (!PyExceptionInstance_Check(fixed_cause)) { + _PyErr_Format(tstate, PyExc_TypeError, + "calling %R should have returned an instance of " + "BaseException, not %R", + cause, Py_TYPE(fixed_cause)); + Py_DECREF(fixed_cause); + goto raise_error; + } + Py_DECREF(cause); + } + else if (PyExceptionInstance_Check(cause)) { + fixed_cause = cause; + } + else if (Py_IsNone(cause)) { + Py_DECREF(cause); + fixed_cause = NULL; + } + else { + _PyErr_SetString(tstate, PyExc_TypeError, + "exception causes must derive from " + "BaseException"); + goto raise_error; + } + PyException_SetCause(value, fixed_cause); + } + + _PyErr_SetObject(tstate, type, value); + /* _PyErr_SetObject incref's its arguments */ + Py_DECREF(value); + Py_DECREF(type); + return 0; + +raise_error: + Py_XDECREF(value); + Py_XDECREF(type); + Py_XDECREF(cause); + return 0; +} + +/* Disable unused label warnings. They are handy for debugging, even + if computed gotos aren't used. */ + +/* TBD - what about other compilers? */ +#if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-label" +#elif defined(_MSC_VER) /* MS_WINDOWS */ +# pragma warning(push) +# pragma warning(disable:4102) +#endif diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index aa68371ac8febf..2425bc1b39f0dc 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -207,6 +207,7 @@ drop_gil_impl(PyThreadState *tstate, struct _gil_runtime_state *gil) _Py_atomic_store_int_relaxed(&gil->locked, 0); if (tstate != NULL) { tstate->holds_gil = 0; + tstate->gil_requested = 0; } COND_SIGNAL(gil->cond); MUTEX_UNLOCK(gil->mutex); @@ -320,6 +321,8 @@ take_gil(PyThreadState *tstate) MUTEX_LOCK(gil->mutex); + tstate->gil_requested = 1; + int drop_requested = 0; while (_Py_atomic_load_int_relaxed(&gil->locked)) { unsigned long saved_switchnum = gil->switch_number; @@ -407,6 +410,7 @@ take_gil(PyThreadState *tstate) } assert(_PyThreadState_CheckConsistency(tstate)); + tstate->gil_requested = 0; tstate->holds_gil = 1; _Py_unset_eval_breaker_bit(tstate, _PY_GIL_DROP_REQUEST_BIT); update_eval_breaker_for_thread(interp, tstate); @@ -907,13 +911,9 @@ unsignal_pending_calls(PyThreadState *tstate, PyInterpreterState *interp) static void clear_pending_handling_thread(struct _pending_calls *pending) { -#ifdef Py_GIL_DISABLED - PyMutex_Lock(&pending->mutex); + FT_MUTEX_LOCK(&pending->mutex); pending->handling_thread = NULL; - PyMutex_Unlock(&pending->mutex); -#else - pending->handling_thread = NULL; -#endif + FT_MUTEX_UNLOCK(&pending->mutex); } static int @@ -1397,13 +1397,19 @@ _Py_HandlePending(PyThreadState *tstate) if ((breaker & _PY_GC_SCHEDULED_BIT) != 0) { _Py_unset_eval_breaker_bit(tstate, _PY_GC_SCHEDULED_BIT); _Py_RunGC(tstate); +#ifdef _Py_TIER2 + _Py_ClearExecutorDeletionList(tstate->interp); +#endif } +#ifdef _Py_TIER2 if ((breaker & _PY_EVAL_JIT_INVALIDATE_COLD_BIT) != 0) { _Py_unset_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT); _Py_Executors_InvalidateCold(tstate->interp); - tstate->interp->trace_run_counter = JIT_CLEANUP_THRESHOLD; + tstate->interp->executor_creation_counter = JIT_CLEANUP_THRESHOLD; + _Py_ClearExecutorDeletionList(tstate->interp); } +#endif /* GIL drop request */ if ((breaker & _PY_GIL_DROP_REQUEST_BIT) != 0) { @@ -1417,11 +1423,7 @@ _Py_HandlePending(PyThreadState *tstate) /* Check for asynchronous exception. */ if ((breaker & _PY_ASYNC_EXCEPTION_BIT) != 0) { - _Py_unset_eval_breaker_bit(tstate, _PY_ASYNC_EXCEPTION_BIT); - PyObject *exc = _Py_atomic_exchange_ptr(&tstate->async_exc, NULL); - if (exc != NULL) { - _PyErr_SetNone(tstate, exc); - Py_DECREF(exc); + if (_PyEval_RaiseAsyncExc(tstate) < 0) { return -1; } } @@ -1432,3 +1434,18 @@ _Py_HandlePending(PyThreadState *tstate) return 0; } + +int +_PyEval_RaiseAsyncExc(PyThreadState *tstate) +{ + assert(tstate != NULL); + assert(tstate == _PyThreadState_GET()); + _Py_unset_eval_breaker_bit(tstate, _PY_ASYNC_EXCEPTION_BIT); + PyObject *exc = _Py_atomic_exchange_ptr(&tstate->async_exc, NULL); + if (exc != NULL) { + _PyErr_SetNone(tstate, exc); + Py_DECREF(exc); + return -1; + } + return 0; +} diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 187ec8fdd26584..37479c4cb4ebd4 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -62,8 +62,9 @@ #ifdef Py_STATS #define INSTRUCTION_STATS(op) \ do { \ + PyStats *s = _PyStats_GET(); \ OPCODE_EXE_INC(op); \ - if (_Py_stats) _Py_stats->opcode_stats[lastopcode].pair_count[op]++; \ + if (s) s->opcode_stats[lastopcode].pair_count[op]++; \ lastopcode = op; \ } while (0) #else @@ -71,23 +72,42 @@ #endif #ifdef Py_STATS -# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, int oparg, int lastopcode -# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, oparg, lastopcode +# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, const void *instruction_funcptr_table, int oparg, int lastopcode +# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, oparg, lastopcode #else -# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, int oparg -# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, oparg +# define TAIL_CALL_PARAMS _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, _Py_CODEUNIT *next_instr, const void *instruction_funcptr_table, int oparg +# define TAIL_CALL_ARGS frame, stack_pointer, tstate, next_instr, instruction_funcptr_table, oparg #endif -#if Py_TAIL_CALL_INTERP - // Note: [[clang::musttail]] works for GCC 15, but not __attribute__((musttail)) at the moment. -# define Py_MUSTTAIL [[clang::musttail]] -# define Py_PRESERVE_NONE_CC __attribute__((preserve_none)) - Py_PRESERVE_NONE_CC typedef PyObject* (*py_tail_call_funcptr)(TAIL_CALL_PARAMS); +#if _Py_TAIL_CALL_INTERP +# if defined(__clang__) || defined(__GNUC__) +# if !_Py__has_attribute(preserve_none) || !_Py__has_attribute(musttail) +# error "This compiler does not have support for efficient tail calling." +# endif +# elif defined(_MSC_VER) && (_MSC_VER < 1950) +# error "You need at least VS 2026 / PlatformToolset v145 for tail calling." +# endif +# if defined(_MSC_VER) && !defined(__clang__) +# define Py_MUSTTAIL [[msvc::musttail]] +# define Py_PRESERVE_NONE_CC __preserve_none +# else +# define Py_MUSTTAIL __attribute__((musttail)) +# define Py_PRESERVE_NONE_CC __attribute__((preserve_none)) +# endif + typedef PyObject *(Py_PRESERVE_NONE_CC *py_tail_call_funcptr)(TAIL_CALL_PARAMS); + +# define DISPATCH_TABLE_VAR instruction_funcptr_table +# define DISPATCH_TABLE instruction_funcptr_handler_table +# define TRACING_DISPATCH_TABLE instruction_funcptr_tracing_table +# define TARGET(op) Py_NO_INLINE PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_##op(TAIL_CALL_PARAMS) -# define TARGET(op) Py_PRESERVE_NONE_CC PyObject *_TAIL_CALL_##op(TAIL_CALL_PARAMS) # define DISPATCH_GOTO() \ do { \ - Py_MUSTTAIL return (INSTRUCTION_TABLE[opcode])(TAIL_CALL_ARGS); \ + Py_MUSTTAIL return (((py_tail_call_funcptr *)instruction_funcptr_table)[opcode])(TAIL_CALL_ARGS); \ + } while (0) +# define DISPATCH_GOTO_NON_TRACING() \ + do { \ + Py_MUSTTAIL return (((py_tail_call_funcptr *)DISPATCH_TABLE)[opcode])(TAIL_CALL_ARGS); \ } while (0) # define JUMP_TO_LABEL(name) \ do { \ @@ -96,29 +116,58 @@ # ifdef Py_STATS # define JUMP_TO_PREDICTED(name) \ do { \ - Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, oparg, lastopcode); \ + Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, instruction_funcptr_table, oparg, lastopcode); \ } while (0) # else # define JUMP_TO_PREDICTED(name) \ do { \ - Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, oparg); \ + Py_MUSTTAIL return (_TAIL_CALL_##name)(frame, stack_pointer, tstate, this_instr, instruction_funcptr_table, oparg); \ } while (0) # endif # define LABEL(name) TARGET(name) #elif USE_COMPUTED_GOTOS +# define DISPATCH_TABLE_VAR opcode_targets +# define DISPATCH_TABLE opcode_targets_table +# define TRACING_DISPATCH_TABLE opcode_tracing_targets_table # define TARGET(op) TARGET_##op: # define DISPATCH_GOTO() goto *opcode_targets[opcode] +# define DISPATCH_GOTO_NON_TRACING() goto *DISPATCH_TABLE[opcode]; # define JUMP_TO_LABEL(name) goto name; # define JUMP_TO_PREDICTED(name) goto PREDICTED_##name; # define LABEL(name) name: #else # define TARGET(op) case op: TARGET_##op: -# define DISPATCH_GOTO() goto dispatch_opcode +# define DISPATCH_GOTO() dispatch_code = opcode | tracing_mode ; goto dispatch_opcode +# define DISPATCH_GOTO_NON_TRACING() dispatch_code = opcode; goto dispatch_opcode # define JUMP_TO_LABEL(name) goto name; # define JUMP_TO_PREDICTED(name) goto PREDICTED_##name; # define LABEL(name) name: #endif +#if (_Py_TAIL_CALL_INTERP || USE_COMPUTED_GOTOS) && _Py_TIER2 +# define IS_JIT_TRACING() (DISPATCH_TABLE_VAR == TRACING_DISPATCH_TABLE) +# define ENTER_TRACING() \ + DISPATCH_TABLE_VAR = TRACING_DISPATCH_TABLE; +# define LEAVE_TRACING() \ + DISPATCH_TABLE_VAR = DISPATCH_TABLE; +#else +# define IS_JIT_TRACING() (tracing_mode != 0) +# define ENTER_TRACING() tracing_mode = 255 +# define LEAVE_TRACING() tracing_mode = 0 +#endif + +#if _Py_TIER2 +#define STOP_TRACING() \ + do { \ + if (IS_JIT_TRACING()) { \ + LEAVE_TRACING(); \ + _PyJit_FinalizeTracing(tstate, 0); \ + } \ + } while (0); +#else +#define STOP_TRACING() ((void)(0)); +#endif + /* PRE_DISPATCH_GOTO() does lltrace if enabled. Normally a no-op */ #ifdef Py_DEBUG #define PRE_DISPATCH_GOTO() if (frame->lltrace >= 5) { \ @@ -133,9 +182,6 @@ do { \ _PyFrame_SetStackPointer(frame, stack_pointer); \ int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); \ stack_pointer = _PyFrame_GetStackPointer(frame); \ - if (lltrace < 0) { \ - JUMP_TO_LABEL(exit_unwind); \ - } \ frame->lltrace = lltrace; \ } while (0) #else @@ -158,21 +204,29 @@ do { \ DISPATCH_GOTO(); \ } +#define DISPATCH_NON_TRACING() \ + { \ + assert(frame->stackpointer == NULL); \ + NEXTOPARG(); \ + PRE_DISPATCH_GOTO(); \ + DISPATCH_GOTO_NON_TRACING(); \ + } + #define DISPATCH_SAME_OPARG() \ { \ opcode = next_instr->op.code; \ PRE_DISPATCH_GOTO(); \ - DISPATCH_GOTO(); \ + DISPATCH_GOTO_NON_TRACING(); \ } -#define DISPATCH_INLINED(NEW_FRAME) \ - do { \ - assert(tstate->interp->eval_frame == NULL); \ - _PyFrame_SetStackPointer(frame, stack_pointer); \ - assert((NEW_FRAME)->previous == frame); \ - frame = tstate->current_frame = (NEW_FRAME); \ - CALL_STAT_INC(inlined_py_calls); \ - JUMP_TO_LABEL(start_frame); \ +#define DISPATCH_INLINED(NEW_FRAME) \ + do { \ + assert(!IS_PEP523_HOOKED(tstate)); \ + _PyFrame_SetStackPointer(frame, stack_pointer); \ + assert((NEW_FRAME)->previous == frame); \ + frame = tstate->current_frame = (NEW_FRAME); \ + CALL_STAT_INC(inlined_py_calls); \ + JUMP_TO_LABEL(start_frame); \ } while (0) /* Tuple access macros */ @@ -212,6 +266,14 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define WITHIN_STACK_BOUNDS() \ (frame->owner == FRAME_OWNED_BY_INTERPRETER || (STACK_LEVEL() >= 0 && STACK_LEVEL() <= STACK_SIZE())) +#if defined(Py_DEBUG) && !defined(_Py_JIT) +// This allows temporary stack "overflows", provided it's all in the cache at any point of time. +#define ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(F, L) \ + assert(frame->owner == FRAME_OWNED_BY_INTERPRETER || (STACK_LEVEL() >= 0 && (STACK_LEVEL()) <= STACK_SIZE())) +#else +#define ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE ASSERT_WITHIN_STACK_BOUNDS +#endif + /* Data access macros */ #define FRAME_CO_CONSTS (_PyFrame_GetCode(frame)->co_consts) #define FRAME_CO_NAMES (_PyFrame_GetCode(frame)->co_names) @@ -266,14 +328,28 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define CONSTS() _PyFrame_GetCode(frame)->co_consts #define NAMES() _PyFrame_GetCode(frame)->co_names +#if defined(WITH_DTRACE) && !defined(Py_BUILD_CORE_MODULE) +static void dtrace_function_entry(_PyInterpreterFrame *); +static void dtrace_function_return(_PyInterpreterFrame *); + #define DTRACE_FUNCTION_ENTRY() \ if (PyDTrace_FUNCTION_ENTRY_ENABLED()) { \ dtrace_function_entry(frame); \ } +#define DTRACE_FUNCTION_RETURN() \ + if (PyDTrace_FUNCTION_RETURN_ENABLED()) { \ + dtrace_function_return(frame); \ + } +#else +#define DTRACE_FUNCTION_ENTRY() ((void)0) +#define DTRACE_FUNCTION_RETURN() ((void)0) +#endif + /* This takes a uint16_t instead of a _Py_BackoffCounter, * because it is used directly on the cache entry in generated code, * which is always an integral type. */ +// Force re-specialization when tracing a side exit to get good side exits. #define ADAPTIVE_COUNTER_TRIGGERS(COUNTER) \ backoff_counter_triggers(forge_backoff_counter((COUNTER))) @@ -287,7 +363,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { (COUNTER) = pause_backoff_counter((COUNTER)); \ } while (0); -#ifdef ENABLE_SPECIALIZATION_FT +#ifdef ENABLE_SPECIALIZATION /* Multiple threads may execute these concurrently if thread-local bytecode is * disabled and they all execute the main copy of the bytecode. Specialization * is disabled in that case so the value is unused, but the RMW cycle should be @@ -354,57 +430,61 @@ _PyFrame_SetStackPointer(frame, stack_pointer) /* Tier-switching macros. */ -#ifdef _Py_JIT -#define GOTO_TIER_TWO(EXECUTOR) \ +#define TIER1_TO_TIER2(EXECUTOR) \ do { \ OPT_STAT_INC(traces_executed); \ - _PyExecutorObject *_executor = (EXECUTOR); \ - tstate->current_executor = (PyObject *)_executor; \ - jit_func jitted = _executor->jit_code; \ - /* Keep the shim frame alive via the executor: */ \ - Py_INCREF(_executor); \ - next_instr = jitted(frame, stack_pointer, tstate); \ - Py_DECREF(_executor); \ + next_instr = _Py_jit_entry((EXECUTOR), frame, stack_pointer, tstate); \ frame = tstate->current_frame; \ stack_pointer = _PyFrame_GetStackPointer(frame); \ + int keep_tracing_bit = (uintptr_t)next_instr & 1; \ + next_instr = (_Py_CODEUNIT *)(((uintptr_t)next_instr) & (~1)); \ if (next_instr == NULL) { \ - next_instr = frame->instr_ptr; \ + /* gh-140104: The exception handler expects frame->instr_ptr + to after this_instr, not this_instr! */ \ + next_instr = frame->instr_ptr + 1; \ JUMP_TO_LABEL(error); \ } \ + if (keep_tracing_bit) { \ + assert(uop_buffer_length(&((_PyThreadStateImpl *)tstate)->jit_tracer_state->code_buffer)); \ + ENTER_TRACING(); \ + DISPATCH_NON_TRACING(); \ + } \ DISPATCH(); \ } while (0) -#else -#define GOTO_TIER_TWO(EXECUTOR) \ -do { \ - OPT_STAT_INC(traces_executed); \ - _PyExecutorObject *_executor = (EXECUTOR); \ - tstate->current_executor = (PyObject *)_executor; \ - next_uop = _executor->trace; \ - assert(next_uop->opcode == _START_EXECUTOR); \ - goto enter_tier_two; \ + +#define TIER2_TO_TIER2(EXECUTOR) \ +do { \ + OPT_STAT_INC(traces_executed); \ + current_executor = (EXECUTOR); \ + goto tier2_start; \ } while (0) -#endif -#define GOTO_TIER_ONE(TARGET) \ - do \ - { \ - tstate->current_executor = NULL; \ - next_instr = (TARGET); \ - assert(tstate->current_executor == NULL); \ - OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); \ - _PyFrame_SetStackPointer(frame, stack_pointer); \ - stack_pointer = _PyFrame_GetStackPointer(frame); \ - if (next_instr == NULL) \ - { \ - next_instr = frame->instr_ptr; \ - goto error; \ - } \ - DISPATCH(); \ +#define GOTO_TIER_ONE_SETUP \ + tstate->current_executor = NULL; \ + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); \ + _PyFrame_SetStackPointer(frame, stack_pointer); + +#define GOTO_TIER_ONE(TARGET) \ + do \ + { \ + GOTO_TIER_ONE_SETUP \ + return (_Py_CODEUNIT *)(TARGET); \ + } while (0) + +#define GOTO_TIER_ONE_CONTINUE_TRACING(TARGET) \ + do \ + { \ + GOTO_TIER_ONE_SETUP \ + return (_Py_CODEUNIT *)(((uintptr_t)(TARGET))| 1); \ } while (0) #define CURRENT_OPARG() (next_uop[-1].oparg) -#define CURRENT_OPERAND0() (next_uop[-1].operand0) -#define CURRENT_OPERAND1() (next_uop[-1].operand1) +#define CURRENT_OPERAND0_64() (next_uop[-1].operand0) +#define CURRENT_OPERAND1_64() (next_uop[-1].operand1) +#define CURRENT_OPERAND0_32() (next_uop[-1].operand0) +#define CURRENT_OPERAND1_32() (next_uop[-1].operand1) +#define CURRENT_OPERAND0_16() (next_uop[-1].operand0) +#define CURRENT_OPERAND1_16() (next_uop[-1].operand1) #define CURRENT_TARGET() (next_uop[-1].target) #define JUMP_TO_JUMP_TARGET() goto jump_to_jump_target @@ -418,10 +498,148 @@ do { \ #define STACKREFS_TO_PYOBJECTS(ARGS, ARG_COUNT, NAME) \ /* +1 because vectorcall might use -1 to write self */ \ PyObject *NAME##_temp[MAX_STACKREF_SCRATCH+1]; \ - PyObject **NAME = _PyObjectArray_FromStackRefArray(ARGS, ARG_COUNT, NAME##_temp + 1); + PyObject **NAME = _PyObjectArray_FromStackRefArray(ARGS, ARG_COUNT, NAME##_temp); #define STACKREFS_TO_PYOBJECTS_CLEANUP(NAME) \ /* +1 because we +1 previously */ \ _PyObjectArray_Free(NAME - 1, NAME##_temp); #define CONVERSION_FAILED(NAME) ((NAME) == NULL) + +#if defined(Py_DEBUG) && !defined(_Py_JIT) +#define SET_CURRENT_CACHED_VALUES(N) current_cached_values = (N) +#define CHECK_CURRENT_CACHED_VALUES(N) assert(current_cached_values == (N)) +#else +#define SET_CURRENT_CACHED_VALUES(N) ((void)0) +#define CHECK_CURRENT_CACHED_VALUES(N) ((void)0) +#endif + +#define IS_PEP523_HOOKED(tstate) (tstate->interp->eval_frame != NULL) + +static inline int +check_periodics(PyThreadState *tstate) { + _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); + QSBR_QUIESCENT_STATE(tstate); + if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { + return _Py_HandlePending(tstate); + } + return 0; +} + +// Mark the generator as executing. Returns true if the state was changed, +// false if it was already executing or finished. +static inline bool +gen_try_set_executing(PyGenObject *gen) +{ +#ifdef Py_GIL_DISABLED + if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) { + int8_t frame_state = _Py_atomic_load_int8_relaxed(&gen->gi_frame_state); + while (frame_state < FRAME_SUSPENDED_YIELD_FROM_LOCKED) { + if (_Py_atomic_compare_exchange_int8(&gen->gi_frame_state, + &frame_state, + FRAME_EXECUTING)) { + return true; + } + } + // NB: We return false for FRAME_SUSPENDED_YIELD_FROM_LOCKED as well. + // That case is rare enough that we can just handle it in the deopt. + return false; + } +#endif + // Use faster non-atomic modifications in the GIL-enabled build and when + // the object is uniquely referenced in the free-threaded build. + if (gen->gi_frame_state < FRAME_EXECUTING) { + assert(gen->gi_frame_state != FRAME_SUSPENDED_YIELD_FROM_LOCKED); + gen->gi_frame_state = FRAME_EXECUTING; + return true; + } + return false; +} + +// Macro for inplace float binary ops (tier 2 only). +// Mutates the uniquely-referenced TARGET operand in place. +// TARGET must be either left or right. +#define FLOAT_INPLACE_OP(left, right, TARGET, OP) \ + do { \ + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); \ + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); \ + assert(PyFloat_CheckExact(left_o)); \ + assert(PyFloat_CheckExact(right_o)); \ + assert(_PyObject_IsUniquelyReferenced( \ + PyStackRef_AsPyObjectBorrow(TARGET))); \ + STAT_INC(BINARY_OP, hit); \ + double _dres = \ + ((PyFloatObject *)left_o)->ob_fval \ + OP ((PyFloatObject *)right_o)->ob_fval; \ + ((PyFloatObject *)PyStackRef_AsPyObjectBorrow(TARGET)) \ + ->ob_fval = _dres; \ + } while (0) + +// Inplace float true division. Sets _divop_err to 1 on zero division. +// Caller must check _divop_err and call ERROR_NO_POP() if set. +#define FLOAT_INPLACE_DIVOP(left, right, TARGET) \ + int _divop_err = 0; \ + do { \ + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); \ + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); \ + assert(PyFloat_CheckExact(left_o)); \ + assert(PyFloat_CheckExact(right_o)); \ + assert(_PyObject_IsUniquelyReferenced( \ + PyStackRef_AsPyObjectBorrow(TARGET))); \ + STAT_INC(BINARY_OP, hit); \ + double _divisor = ((PyFloatObject *)right_o)->ob_fval; \ + if (_divisor == 0.0) { \ + PyErr_SetString(PyExc_ZeroDivisionError, \ + "float division by zero"); \ + _divop_err = 1; \ + break; \ + } \ + double _dres = ((PyFloatObject *)left_o)->ob_fval / _divisor; \ + ((PyFloatObject *)PyStackRef_AsPyObjectBorrow(TARGET)) \ + ->ob_fval = _dres; \ + } while (0) + +// Inplace compact int operation. TARGET is expected to be uniquely +// referenced at the optimizer level, but at runtime it may be a +// cached small int singleton. We check _Py_IsImmortal on TARGET +// to decide whether inplace mutation is safe. +// +// After the macro, _int_inplace_res holds the result (may be NULL +// on allocation failure). On success, TARGET was mutated in place +// and _int_inplace_res is a DUP'd reference to it. On fallback +// (small int target, small int result, or overflow), _int_inplace_res +// is from FUNC (_PyCompactLong_Add etc.). +// FUNC is the fallback function (_PyCompactLong_Add etc.) +#define INT_INPLACE_OP(left, right, TARGET, OP, FUNC) \ + _PyStackRef _int_inplace_res = PyStackRef_NULL; \ + do { \ + PyObject *target_o = PyStackRef_AsPyObjectBorrow(TARGET); \ + if (_Py_IsImmortal(target_o)) { \ + break; \ + } \ + assert(_PyObject_IsUniquelyReferenced(target_o)); \ + Py_ssize_t left_val = _PyLong_CompactValue( \ + (PyLongObject *)PyStackRef_AsPyObjectBorrow(left)); \ + Py_ssize_t right_val = _PyLong_CompactValue( \ + (PyLongObject *)PyStackRef_AsPyObjectBorrow(right)); \ + Py_ssize_t result = left_val OP right_val; \ + if (!_PY_IS_SMALL_INT(result) \ + && ((twodigits)((stwodigits)result) + PyLong_MASK \ + < (twodigits)PyLong_MASK + PyLong_BASE)) \ + { \ + _PyLong_SetSignAndDigitCount( \ + (PyLongObject *)target_o, result < 0 ? -1 : 1, 1); \ + ((PyLongObject *)target_o)->long_value.ob_digit[0] = \ + (digit)(result < 0 ? -result : result); \ + _int_inplace_res = PyStackRef_DUP(TARGET); \ + break; \ + } \ + } while (0); \ + if (PyStackRef_IsNull(_int_inplace_res)) { \ + _int_inplace_res = FUNC( \ + (PyLongObject *)PyStackRef_AsPyObjectBorrow(left), \ + (PyLongObject *)PyStackRef_AsPyObjectBorrow(right)); \ + } + +#define CALL_TP_ITERITEM_NO_ESCAPE(ITER, INDEX) \ + Py_TYPE(ITER)->_tp_iteritem((ITER), (INDEX)) diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index c826a5724f769c..4a38e0df61708c 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -25,9 +25,10 @@ PyDoc_STRVAR(builtin___import____doc__, "empty list to emulate ``import name``.\n" "When importing a module from a package, note that __import__(\'A.B\', ...)\n" "returns package A when fromlist is empty, but its submodule B when\n" -"fromlist is not empty. The level argument is used to determine whether to\n" -"perform absolute or relative imports: 0 is absolute, while a positive number\n" -"is the number of parent directories to search relative to the current module."); +"fromlist is not empty. The level argument is used to determine whether\n" +"to perform absolute or relative imports: 0 is absolute, while a positive\n" +"number is the number of parent directories to search relative to the\n" +"current module."); #define BUILTIN___IMPORT___METHODDEF \ {"__import__", _PyCFunction_CAST(builtin___import__), METH_FASTCALL|METH_KEYWORDS, builtin___import____doc__}, @@ -113,8 +114,103 @@ builtin___import__(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py return return_value; } +PyDoc_STRVAR(builtin___lazy_import____doc__, +"__lazy_import__($module, /, name, globals=None, locals=None,\n" +" fromlist=(), level=0)\n" +"--\n" +"\n" +"Lazily imports a module.\n" +"\n" +"Returns either the module to be imported or a imp.lazy_module object\n" +"which indicates the module to be lazily imported."); + +#define BUILTIN___LAZY_IMPORT___METHODDEF \ + {"__lazy_import__", _PyCFunction_CAST(builtin___lazy_import__), METH_FASTCALL|METH_KEYWORDS, builtin___lazy_import____doc__}, + +static PyObject * +builtin___lazy_import___impl(PyObject *module, PyObject *name, + PyObject *globals, PyObject *locals, + PyObject *fromlist, int level); + +static PyObject * +builtin___lazy_import__(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 5 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(name), &_Py_ID(globals), &_Py_ID(locals), &_Py_ID(fromlist), &_Py_ID(level), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"name", "globals", "locals", "fromlist", "level", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "__lazy_import__", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[5]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *name; + PyObject *globals = NULL; + PyObject *locals = NULL; + PyObject *fromlist = NULL; + int level = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 5, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + name = args[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1]) { + globals = args[1]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[2]) { + locals = args[2]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[3]) { + fromlist = args[3]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + level = PyLong_AsInt(args[4]); + if (level == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_pos: + return_value = builtin___lazy_import___impl(module, name, globals, locals, fromlist, level); + +exit: + return return_value; +} + PyDoc_STRVAR(builtin_abs__doc__, -"abs($module, x, /)\n" +"abs($module, number, /)\n" "--\n" "\n" "Return the absolute value of the argument."); @@ -159,7 +255,7 @@ PyDoc_STRVAR(builtin_ascii__doc__, {"ascii", (PyCFunction)builtin_ascii, METH_O, builtin_ascii__doc__}, PyDoc_STRVAR(builtin_bin__doc__, -"bin($module, number, /)\n" +"bin($module, integer, /)\n" "--\n" "\n" "Return the binary representation of an integer.\n" @@ -238,21 +334,23 @@ PyDoc_STRVAR(builtin_chr__doc__, PyDoc_STRVAR(builtin_compile__doc__, "compile($module, /, source, filename, mode, flags=0,\n" -" dont_inherit=False, optimize=-1, *, _feature_version=-1)\n" +" dont_inherit=False, optimize=-1, *, module=None,\n" +" _feature_version=-1)\n" "--\n" "\n" "Compile source into a code object that can be executed by exec() or eval().\n" "\n" -"The source code may represent a Python module, statement or expression.\n" +"The source code may represent a Python module, statement or\n" +"expression.\n" "The filename will be used for run-time error messages.\n" "The mode must be \'exec\' to compile a module, \'single\' to compile a\n" "single (interactive) statement, or \'eval\' to compile an expression.\n" -"The flags argument, if present, controls which future statements influence\n" -"the compilation of the code.\n" +"The flags argument, if present, controls which future statements\n" +"influence the compilation of the code.\n" "The dont_inherit argument, if true, stops the compilation inheriting\n" "the effects of any future statements in effect in the code calling\n" -"compile; if absent or false these statements do influence the compilation,\n" -"in addition to any features explicitly specified."); +"compile; if absent or false these statements do influence the\n" +"compilation, in addition to any features explicitly specified."); #define BUILTIN_COMPILE_METHODDEF \ {"compile", _PyCFunction_CAST(builtin_compile), METH_FASTCALL|METH_KEYWORDS, builtin_compile__doc__}, @@ -260,7 +358,7 @@ PyDoc_STRVAR(builtin_compile__doc__, static PyObject * builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename, const char *mode, int flags, int dont_inherit, - int optimize, int feature_version); + int optimize, PyObject *modname, int feature_version); static PyObject * builtin_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -268,7 +366,7 @@ builtin_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 7 + #define NUM_KEYWORDS 8 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD @@ -277,7 +375,7 @@ builtin_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) .ob_hash = -1, - .ob_item = { &_Py_ID(source), &_Py_ID(filename), &_Py_ID(mode), &_Py_ID(flags), &_Py_ID(dont_inherit), &_Py_ID(optimize), &_Py_ID(_feature_version), }, + .ob_item = { &_Py_ID(source), &_Py_ID(filename), &_Py_ID(mode), &_Py_ID(flags), &_Py_ID(dont_inherit), &_Py_ID(optimize), &_Py_ID(module), &_Py_ID(_feature_version), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -286,21 +384,22 @@ builtin_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"source", "filename", "mode", "flags", "dont_inherit", "optimize", "_feature_version", NULL}; + static const char * const _keywords[] = {"source", "filename", "mode", "flags", "dont_inherit", "optimize", "module", "_feature_version", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "compile", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[7]; + PyObject *argsbuf[8]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 3; PyObject *source; - PyObject *filename; + PyObject *filename = NULL; const char *mode; int flags = 0; int dont_inherit = 0; int optimize = -1; + PyObject *modname = Py_None; int feature_version = -1; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, @@ -359,14 +458,23 @@ builtin_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj if (!noptargs) { goto skip_optional_kwonly; } - feature_version = PyLong_AsInt(args[6]); + if (args[6]) { + modname = args[6]; + if (!--noptargs) { + goto skip_optional_kwonly; + } + } + feature_version = PyLong_AsInt(args[7]); if (feature_version == -1 && PyErr_Occurred()) { goto exit; } skip_optional_kwonly: - return_value = builtin_compile_impl(module, source, filename, mode, flags, dont_inherit, optimize, feature_version); + return_value = builtin_compile_impl(module, source, filename, mode, flags, dont_inherit, optimize, modname, feature_version); exit: + /* Cleanup for filename */ + Py_XDECREF(filename); + return return_value; } @@ -577,8 +685,8 @@ PyDoc_STRVAR(builtin_globals__doc__, "\n" "Return the dictionary containing the current scope\'s global variables.\n" "\n" -"NOTE: Updates to this dictionary *will* affect name lookups in the current\n" -"global scope and vice-versa."); +"NOTE: Updates to this dictionary *will* affect name lookups in the\n" +"current global scope and vice-versa."); #define BUILTIN_GLOBALS_METHODDEF \ {"globals", (PyCFunction)builtin_globals, METH_NOARGS, builtin_globals__doc__}, @@ -720,16 +828,18 @@ PyDoc_STRVAR(builtin_hash__doc__, "hash($module, obj, /)\n" "--\n" "\n" -"Return the hash value for the given object.\n" +"Return the integer hash value for the given object.\n" "\n" -"Two objects that compare equal must also have the same hash value, but the\n" -"reverse is not necessarily true."); +"Two objects that compare equal must also have the same hash value, but\n" +"the reverse is not necessarily true. Hash values may differ between\n" +"Python processes. Not all objects are hashable; calling hash() on an\n" +"unhashable object raises TypeError."); #define BUILTIN_HASH_METHODDEF \ {"hash", (PyCFunction)builtin_hash, METH_O, builtin_hash__doc__}, PyDoc_STRVAR(builtin_hex__doc__, -"hex($module, number, /)\n" +"hex($module, integer, /)\n" "--\n" "\n" "Return the hexadecimal representation of an integer.\n" @@ -750,7 +860,7 @@ PyDoc_STRVAR(builtin_aiter__doc__, {"aiter", (PyCFunction)builtin_aiter, METH_O, builtin_aiter__doc__}, PyDoc_STRVAR(builtin_anext__doc__, -"anext($module, aiterator, default=<unrepresentable>, /)\n" +"anext($module, async_iterator, default=<unrepresentable>, /)\n" "--\n" "\n" "Return the next item from the async iterator.\n" @@ -802,9 +912,10 @@ PyDoc_STRVAR(builtin_locals__doc__, "\n" "Return a dictionary containing the current scope\'s local variables.\n" "\n" -"NOTE: Whether or not updates to this dictionary will affect name lookups in\n" -"the local scope and vice-versa is *implementation dependent* and not\n" -"covered by any backwards compatibility guarantees."); +"NOTE: Whether or not updates to this dictionary will affect name\n" +"lookups in the local scope and vice-versa is *implementation\n" +"dependent* and not covered by any backwards compatibility\n" +"guarantees."); #define BUILTIN_LOCALS_METHODDEF \ {"locals", (PyCFunction)builtin_locals, METH_NOARGS, builtin_locals__doc__}, @@ -819,7 +930,7 @@ builtin_locals(PyObject *module, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(builtin_oct__doc__, -"oct($module, number, /)\n" +"oct($module, integer, /)\n" "--\n" "\n" "Return the octal representation of an integer.\n" @@ -831,10 +942,16 @@ PyDoc_STRVAR(builtin_oct__doc__, {"oct", (PyCFunction)builtin_oct, METH_O, builtin_oct__doc__}, PyDoc_STRVAR(builtin_ord__doc__, -"ord($module, c, /)\n" +"ord($module, character, /)\n" "--\n" "\n" -"Return the Unicode code point for a one-character string."); +"Return the ordinal value of a character.\n" +"\n" +"If the argument is a one-character string, return the Unicode code\n" +"point of that character.\n" +"\n" +"If the argument is a bytes or bytearray object of length 1, return its\n" +"single byte value."); #define BUILTIN_ORD_METHODDEF \ {"ord", (PyCFunction)builtin_ord, METH_O, builtin_ord__doc__}, @@ -845,8 +962,8 @@ PyDoc_STRVAR(builtin_pow__doc__, "\n" "Equivalent to base**exp with 2 arguments or base**exp % mod with 3 arguments\n" "\n" -"Some types, such as ints, are able to use a more efficient algorithm when\n" -"invoked using the three argument form."); +"Some types, such as ints, are able to use a more efficient algorithm\n" +"when invoked using the three argument form."); #define BUILTIN_POW_METHODDEF \ {"pow", _PyCFunction_CAST(builtin_pow), METH_FASTCALL|METH_KEYWORDS, builtin_pow__doc__}, @@ -911,7 +1028,7 @@ builtin_pow(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject } PyDoc_STRVAR(builtin_print__doc__, -"print($module, /, *args, sep=\' \', end=\'\\n\', file=None, flush=False)\n" +"print($module, /, *objects, sep=\' \', end=\'\\n\', file=None, flush=False)\n" "--\n" "\n" "Prints the values to a stream, or to sys.stdout by default.\n" @@ -929,8 +1046,8 @@ PyDoc_STRVAR(builtin_print__doc__, {"print", _PyCFunction_CAST(builtin_print), METH_FASTCALL|METH_KEYWORDS, builtin_print__doc__}, static PyObject * -builtin_print_impl(PyObject *module, PyObject * const *args, - Py_ssize_t args_length, PyObject *sep, PyObject *end, +builtin_print_impl(PyObject *module, PyObject * const *objects, + Py_ssize_t objects_length, PyObject *sep, PyObject *end, PyObject *file, int flush); static PyObject * @@ -967,8 +1084,8 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *argsbuf[4]; PyObject * const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject * const *__clinic_args; - Py_ssize_t args_length; + PyObject * const *objects; + Py_ssize_t objects_length; PyObject *sep = Py_None; PyObject *end = Py_None; PyObject *file = Py_None; @@ -1005,9 +1122,9 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec goto exit; } skip_optional_kwonly: - __clinic_args = args; - args_length = nargs; - return_value = builtin_print_impl(module, __clinic_args, args_length, sep, end, file, flush); + objects = args; + objects_length = nargs; + return_value = builtin_print_impl(module, objects, objects_length, sep, end, file, flush); exit: return return_value; @@ -1022,7 +1139,8 @@ PyDoc_STRVAR(builtin_input__doc__, "The prompt string, if given, is printed to standard output without a\n" "trailing newline before reading input.\n" "\n" -"If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.\n" +"If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise\n" +"EOFError.\n" "On *nix systems, readline is used if available."); #define BUILTIN_INPUT_METHODDEF \ @@ -1068,8 +1186,9 @@ PyDoc_STRVAR(builtin_round__doc__, "\n" "Round a number to a given precision in decimal digits.\n" "\n" -"The return value is an integer if ndigits is omitted or None. Otherwise\n" -"the return value has the same type as the number. ndigits may be negative."); +"The return value is an integer if ndigits is omitted or None.\n" +"Otherwise the return value has the same type as the number. ndigits\n" +"may be negative."); #define BUILTIN_ROUND_METHODDEF \ {"round", _PyCFunction_CAST(builtin_round), METH_FASTCALL|METH_KEYWORDS, builtin_round__doc__}, @@ -1137,8 +1256,8 @@ PyDoc_STRVAR(builtin_sum__doc__, "Return the sum of a \'start\' value (default: 0) plus an iterable of numbers\n" "\n" "When the iterable is empty, return the start value.\n" -"This function is intended specifically for use with numeric values and may\n" -"reject non-numeric types."); +"This function is intended specifically for use with numeric values and\n" +"may reject non-numeric types."); #define BUILTIN_SUM_METHODDEF \ {"sum", _PyCFunction_CAST(builtin_sum), METH_FASTCALL|METH_KEYWORDS, builtin_sum__doc__}, @@ -1205,9 +1324,9 @@ PyDoc_STRVAR(builtin_isinstance__doc__, "\n" "Return whether an object is an instance of a class or of a subclass thereof.\n" "\n" -"A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to\n" -"check against. This is equivalent to ``isinstance(x, A) or isinstance(x, B)\n" -"or ...`` etc."); +"A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the\n" +"target to check against. This is equivalent to ``isinstance(x, A) or\n" +"isinstance(x, B) or ...`` etc."); #define BUILTIN_ISINSTANCE_METHODDEF \ {"isinstance", _PyCFunction_CAST(builtin_isinstance), METH_FASTCALL, builtin_isinstance__doc__}, @@ -1240,9 +1359,9 @@ PyDoc_STRVAR(builtin_issubclass__doc__, "\n" "Return whether \'cls\' is derived from another class or is the same class.\n" "\n" -"A tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the target to\n" -"check against. This is equivalent to ``issubclass(x, A) or issubclass(x, B)\n" -"or ...``."); +"A tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the\n" +"target to check against. This is equivalent to ``issubclass(x, A) or\n" +"issubclass(x, B) or ...``."); #define BUILTIN_ISSUBCLASS_METHODDEF \ {"issubclass", _PyCFunction_CAST(builtin_issubclass), METH_FASTCALL, builtin_issubclass__doc__}, @@ -1268,4 +1387,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=e7a5d0851d7f2cfb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=84efa9c5cc737ce5 input=a9049054013a1b77]*/ diff --git a/Python/clinic/context.c.h b/Python/clinic/context.c.h index 5ed74e6e6ddb6b..ece7341d65d5fb 100644 --- a/Python/clinic/context.c.h +++ b/Python/clinic/context.c.h @@ -10,8 +10,8 @@ PyDoc_STRVAR(_contextvars_Context_get__doc__, "\n" "Return the value for `key` if `key` has the value in the context object.\n" "\n" -"If `key` does not exist, return `default`. If `default` is not given,\n" -"return None."); +"If `key` does not exist, return `default`. If `default` is not\n" +"given, return None."); #define _CONTEXTVARS_CONTEXT_GET_METHODDEF \ {"get", _PyCFunction_CAST(_contextvars_Context_get), METH_FASTCALL, _contextvars_Context_get__doc__}, @@ -122,10 +122,12 @@ PyDoc_STRVAR(_contextvars_ContextVar_get__doc__, "\n" "Return a value for the context variable for the current context.\n" "\n" -"If there is no value for the variable in the current context, the method will:\n" -" * return the value of the default argument of the method, if provided; or\n" -" * return the default value for the context variable, if it was created\n" -" with one; or\n" +"If there is no value for the variable in the current context, the\n" +"method will:\n" +" * return the value of the default argument of the method, if\n" +" provided; or\n" +" * return the default value for the context variable, if it was\n" +" created with one; or\n" " * raise a LookupError."); #define _CONTEXTVARS_CONTEXTVAR_GET_METHODDEF \ @@ -160,10 +162,11 @@ PyDoc_STRVAR(_contextvars_ContextVar_set__doc__, "\n" "Call to set a new value for the context variable in the current context.\n" "\n" -"The required value argument is the new value for the context variable.\n" +"The required value argument is the new value for the context\n" +"variable.\n" "\n" -"Returns a Token object that can be used to restore the variable to its previous\n" -"value via the `ContextVar.reset()` method."); +"Returns a Token object that can be used to restore the variable to\n" +"its previous value via the `ContextVar.reset()` method."); #define _CONTEXTVARS_CONTEXTVAR_SET_METHODDEF \ {"set", (PyCFunction)_contextvars_ContextVar_set, METH_O, _contextvars_ContextVar_set__doc__}, @@ -187,8 +190,8 @@ PyDoc_STRVAR(_contextvars_ContextVar_reset__doc__, "\n" "Reset the context variable.\n" "\n" -"The variable is reset to the value it had before the `ContextVar.set()` that\n" -"created the token was used."); +"The variable is reset to the value it had before the\n" +"`ContextVar.set()` that created the token was used."); #define _CONTEXTVARS_CONTEXTVAR_RESET_METHODDEF \ {"reset", (PyCFunction)_contextvars_ContextVar_reset, METH_O, _contextvars_ContextVar_reset__doc__}, @@ -256,4 +259,4 @@ token_exit(PyObject *self, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=3a04b2fddf24c3e9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=90ec3e4375804e9b input=a9049054013a1b77]*/ diff --git a/Python/clinic/import.c.h b/Python/clinic/import.c.h index 9bbb13f7566517..2e4e178b308406 100644 --- a/Python/clinic/import.c.h +++ b/Python/clinic/import.c.h @@ -34,8 +34,9 @@ PyDoc_STRVAR(_imp_acquire_lock__doc__, "\n" "Acquires the interpreter\'s import lock for the current thread.\n" "\n" -"This lock should be used by import hooks to ensure thread-safety when importing\n" -"modules. On platforms without threads, this function does nothing."); +"This lock should be used by import hooks to ensure thread-safety when\n" +"importing modules. On platforms without threads, this function does\n" +"nothing."); #define _IMP_ACQUIRE_LOCK_METHODDEF \ {"acquire_lock", (PyCFunction)_imp_acquire_lock, METH_NOARGS, _imp_acquire_lock__doc__}, @@ -622,6 +623,41 @@ _imp_source_hash(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb return return_value; } +PyDoc_STRVAR(_imp__set_lazy_attributes__doc__, +"_set_lazy_attributes($module, modobj, name, /)\n" +"--\n" +"\n" +"Sets attributes to lazy submodules on the module, as side effects."); + +#define _IMP__SET_LAZY_ATTRIBUTES_METHODDEF \ + {"_set_lazy_attributes", _PyCFunction_CAST(_imp__set_lazy_attributes), METH_FASTCALL, _imp__set_lazy_attributes__doc__}, + +static PyObject * +_imp__set_lazy_attributes_impl(PyObject *module, PyObject *modobj, + PyObject *name); + +static PyObject * +_imp__set_lazy_attributes(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *modobj; + PyObject *name; + + if (!_PyArg_CheckPositional("_set_lazy_attributes", nargs, 2, 2)) { + goto exit; + } + modobj = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("_set_lazy_attributes", "argument 2", "str", args[1]); + goto exit; + } + name = args[1]; + return_value = _imp__set_lazy_attributes_impl(module, modobj, name); + +exit: + return return_value; +} + #ifndef _IMP_CREATE_DYNAMIC_METHODDEF #define _IMP_CREATE_DYNAMIC_METHODDEF #endif /* !defined(_IMP_CREATE_DYNAMIC_METHODDEF) */ @@ -629,4 +665,4 @@ _imp_source_hash(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb #ifndef _IMP_EXEC_DYNAMIC_METHODDEF #define _IMP_EXEC_DYNAMIC_METHODDEF #endif /* !defined(_IMP_EXEC_DYNAMIC_METHODDEF) */ -/*[clinic end generated code: output=24f597d6b0f3feed input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0974db098d601372 input=a9049054013a1b77]*/ diff --git a/Python/clinic/marshal.c.h b/Python/clinic/marshal.c.h index 6c00b2b31b007f..ec0d2eb8a2af54 100644 --- a/Python/clinic/marshal.c.h +++ b/Python/clinic/marshal.c.h @@ -195,8 +195,8 @@ PyDoc_STRVAR(marshal_dumps__doc__, " allow_code\n" " Allow to write code objects.\n" "\n" -"Raise a ValueError exception if value has (or contains an object that has) an\n" -"unsupported type."); +"Raise a ValueError exception if value has (or contains an object that\n" +"has) an unsupported type."); #define MARSHAL_DUMPS_METHODDEF \ {"dumps", _PyCFunction_CAST(marshal_dumps), METH_FASTCALL|METH_KEYWORDS, marshal_dumps__doc__}, @@ -280,8 +280,8 @@ PyDoc_STRVAR(marshal_loads__doc__, " allow_code\n" " Allow to load code objects.\n" "\n" -"If no valid value is found, raise EOFError, ValueError or TypeError. Extra\n" -"bytes in the input are ignored."); +"If no valid value is found, raise EOFError, ValueError or TypeError.\n" +"Extra bytes in the input are ignored."); #define MARSHAL_LOADS_METHODDEF \ {"loads", _PyCFunction_CAST(marshal_loads), METH_FASTCALL|METH_KEYWORDS, marshal_loads__doc__}, @@ -351,4 +351,4 @@ marshal_loads(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec return return_value; } -/*[clinic end generated code: output=3e4bfc070a3c78ac input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a574570c3717f60e input=a9049054013a1b77]*/ diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index a47e4d11b54441..0c72760f69a4c4 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -7,7 +7,6 @@ preserve # include "pycore_runtime.h" // _Py_ID() #endif #include "pycore_modsupport.h" // _PyArg_UnpackKeywords() -#include "pycore_tuple.h" // _PyTuple_FromArray() PyDoc_STRVAR(sys_addaudithook__doc__, "addaudithook($module, /, hook)\n" @@ -102,7 +101,7 @@ sys_audit(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyErr_SetString(PyExc_ValueError, "embedded null character"); goto exit; } - __clinic_args = _PyTuple_FromArray(args + 1, nargs - 1); + __clinic_args = PyTuple_FromArray(args + 1, nargs - 1); if (__clinic_args == NULL) { goto exit; } @@ -762,34 +761,6 @@ sys_getwindowsversion(PyObject *module, PyObject *Py_UNUSED(ignored)) #endif /* defined(MS_WINDOWS) */ -#if defined(MS_WINDOWS) - -PyDoc_STRVAR(sys__enablelegacywindowsfsencoding__doc__, -"_enablelegacywindowsfsencoding($module, /)\n" -"--\n" -"\n" -"Changes the default filesystem encoding to mbcs:replace.\n" -"\n" -"This is done for consistency with earlier versions of Python. See PEP\n" -"529 for more information.\n" -"\n" -"This is equivalent to defining the PYTHONLEGACYWINDOWSFSENCODING\n" -"environment variable before launching Python."); - -#define SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF \ - {"_enablelegacywindowsfsencoding", (PyCFunction)sys__enablelegacywindowsfsencoding, METH_NOARGS, sys__enablelegacywindowsfsencoding__doc__}, - -static PyObject * -sys__enablelegacywindowsfsencoding_impl(PyObject *module); - -static PyObject * -sys__enablelegacywindowsfsencoding(PyObject *module, PyObject *Py_UNUSED(ignored)) -{ - return sys__enablelegacywindowsfsencoding_impl(module); -} - -#endif /* defined(MS_WINDOWS) */ - #if defined(HAVE_DLOPEN) PyDoc_STRVAR(sys_setdlopenflags__doc__, @@ -1396,7 +1367,8 @@ PyDoc_STRVAR(sys__stats_dump__doc__, "\n" "Dump stats to file, and clears the stats.\n" "\n" -"Return False if no statistics were not dumped because stats gathering was off."); +"Return False if no statistics were not dumped because stats gathering\n" +"was off."); #define SYS__STATS_DUMP_METHODDEF \ {"_stats_dump", (PyCFunction)sys__stats_dump, METH_NOARGS, sys__stats_dump__doc__}, @@ -1544,16 +1516,16 @@ PyDoc_STRVAR(sys_remote_exec__doc__, "Executes a file containing Python code in a given remote Python process.\n" "\n" "This function returns immediately, and the code will be executed by the\n" -"target process\'s main thread at the next available opportunity, similarly\n" -"to how signals are handled. There is no interface to determine when the\n" -"code has been executed. The caller is responsible for making sure that\n" -"the file still exists whenever the remote process tries to read it and that\n" -"it hasn\'t been overwritten.\n" +"target process\'s main thread at the next available opportunity,\n" +"similarly to how signals are handled. There is no interface to\n" +"determine when the code has been executed. The caller is responsible\n" +"for making sure that the file still exists whenever the remote process\n" +"tries to read it and that it hasn\'t been overwritten.\n" "\n" -"The remote process must be running a CPython interpreter of the same major\n" -"and minor version as the local process. If either the local or remote\n" -"interpreter is pre-release (alpha, beta, or release candidate) then the\n" -"local and remote interpreters must be the same exact version.\n" +"The remote process must be running a CPython interpreter of the same\n" +"major and minor version as the local process. If either the local or\n" +"remote interpreter is pre-release (alpha, beta, or release candidate)\n" +"then the local and remote interpreters must be the same exact version.\n" "\n" "Args:\n" " pid (int): The process ID of the target Python process.\n" @@ -1821,6 +1793,179 @@ sys__is_gil_enabled(PyObject *module, PyObject *Py_UNUSED(ignored)) return return_value; } +PyDoc_STRVAR(sys_set_lazy_imports_filter__doc__, +"set_lazy_imports_filter($module, /, filter)\n" +"--\n" +"\n" +"Set the lazy imports filter callback.\n" +"\n" +"The filter is a callable which disables lazy imports when they\n" +"would otherwise be enabled. Returns True if the import is still enabled\n" +"or False to disable it. The callable is called with:\n" +"\n" +"(importing_module_name, resolved_imported_module_name, [fromlist])\n" +"\n" +"Pass None to clear the filter."); + +#define SYS_SET_LAZY_IMPORTS_FILTER_METHODDEF \ + {"set_lazy_imports_filter", _PyCFunction_CAST(sys_set_lazy_imports_filter), METH_FASTCALL|METH_KEYWORDS, sys_set_lazy_imports_filter__doc__}, + +static PyObject * +sys_set_lazy_imports_filter_impl(PyObject *module, PyObject *filter); + +static PyObject * +sys_set_lazy_imports_filter(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(filter), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"filter", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "set_lazy_imports_filter", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *filter; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + filter = args[0]; + return_value = sys_set_lazy_imports_filter_impl(module, filter); + +exit: + return return_value; +} + +PyDoc_STRVAR(sys_get_lazy_imports_filter__doc__, +"get_lazy_imports_filter($module, /)\n" +"--\n" +"\n" +"Get the current lazy imports filter callback.\n" +"\n" +"Returns the filter callable or None if no filter is set."); + +#define SYS_GET_LAZY_IMPORTS_FILTER_METHODDEF \ + {"get_lazy_imports_filter", (PyCFunction)sys_get_lazy_imports_filter, METH_NOARGS, sys_get_lazy_imports_filter__doc__}, + +static PyObject * +sys_get_lazy_imports_filter_impl(PyObject *module); + +static PyObject * +sys_get_lazy_imports_filter(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return sys_get_lazy_imports_filter_impl(module); +} + +PyDoc_STRVAR(sys_set_lazy_imports__doc__, +"set_lazy_imports($module, /, mode)\n" +"--\n" +"\n" +"Sets the global lazy imports mode.\n" +"\n" +"The mode parameter must be one of the following strings:\n" +"- \"all\": All top-level imports become potentially lazy\n" +"- \"normal\": Only explicitly marked imports (with \'lazy\' keyword) are\n" +" lazy\n" +"\n" +"In addition to the mode, lazy imports can be controlled via the filter\n" +"provided to sys.set_lazy_imports_filter"); + +#define SYS_SET_LAZY_IMPORTS_METHODDEF \ + {"set_lazy_imports", _PyCFunction_CAST(sys_set_lazy_imports), METH_FASTCALL|METH_KEYWORDS, sys_set_lazy_imports__doc__}, + +static PyObject * +sys_set_lazy_imports_impl(PyObject *module, PyObject *mode); + +static PyObject * +sys_set_lazy_imports(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + Py_hash_t ob_hash; + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_hash = -1, + .ob_item = { &_Py_ID(mode), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"mode", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "set_lazy_imports", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *mode; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, + /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf); + if (!args) { + goto exit; + } + mode = args[0]; + return_value = sys_set_lazy_imports_impl(module, mode); + +exit: + return return_value; +} + +PyDoc_STRVAR(sys_get_lazy_imports__doc__, +"get_lazy_imports($module, /)\n" +"--\n" +"\n" +"Gets the global lazy imports mode.\n" +"\n" +"Returns \"all\" if all top level imports are potentially lazy.\n" +"Returns \"normal\" if only explicitly marked imports are lazy."); + +#define SYS_GET_LAZY_IMPORTS_METHODDEF \ + {"get_lazy_imports", (PyCFunction)sys_get_lazy_imports, METH_NOARGS, sys_get_lazy_imports__doc__}, + +static PyObject * +sys_get_lazy_imports_impl(PyObject *module); + +static PyObject * +sys_get_lazy_imports(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return sys_get_lazy_imports_impl(module); +} + PyDoc_STRVAR(_jit_is_available__doc__, "is_available($module, /)\n" "--\n" @@ -1909,10 +2054,6 @@ _jit_is_active(PyObject *module, PyObject *Py_UNUSED(ignored)) #define SYS_GETWINDOWSVERSION_METHODDEF #endif /* !defined(SYS_GETWINDOWSVERSION_METHODDEF) */ -#ifndef SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF - #define SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF -#endif /* !defined(SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF) */ - #ifndef SYS_SETDLOPENFLAGS_METHODDEF #define SYS_SETDLOPENFLAGS_METHODDEF #endif /* !defined(SYS_SETDLOPENFLAGS_METHODDEF) */ @@ -1948,4 +2089,4 @@ _jit_is_active(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF #define SYS_GETANDROIDAPILEVEL_METHODDEF #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */ -/*[clinic end generated code: output=449d16326e69dcf6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8a4482f9c5c493e5 input=a9049054013a1b77]*/ diff --git a/Python/codecs.c b/Python/codecs.c index caf8d9d5f3c188..6d1ae651fa0005 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -10,13 +10,15 @@ Copyright (c) Corporation for National Research Initiatives. #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_codecs.h" // export _PyCodec_LookupTextEncoding() +#include "pycore_initconfig.h" // _Py_DumpPathConfig() #include "pycore_interp.h" // PyInterpreterState.codec_search_path #include "pycore_pyerrors.h" // _PyErr_FormatNote() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_runtime.h" // _Py_ID() #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI #include "pycore_unicodeobject.h" // _PyUnicode_InternMortal() - +#include "pycore_pyatomic_ft_wrappers.h" static const char *codecs_builtin_error_handlers[] = { "strict", "ignore", "replace", @@ -40,13 +42,10 @@ int PyCodec_Register(PyObject *search_function) PyErr_SetString(PyExc_TypeError, "argument must be callable"); goto onError; } -#ifdef Py_GIL_DISABLED - PyMutex_Lock(&interp->codecs.search_path_mutex); -#endif + FT_MUTEX_LOCK(&interp->codecs.search_path_mutex); int ret = PyList_Append(interp->codecs.search_path, search_function); -#ifdef Py_GIL_DISABLED - PyMutex_Unlock(&interp->codecs.search_path_mutex); -#endif + FT_MUTEX_UNLOCK(&interp->codecs.search_path_mutex); + return ret; onError: @@ -66,9 +65,7 @@ PyCodec_Unregister(PyObject *search_function) PyObject *codec_search_path = interp->codecs.search_path; assert(PyList_CheckExact(codec_search_path)); for (Py_ssize_t i = 0; i < PyList_GET_SIZE(codec_search_path); i++) { -#ifdef Py_GIL_DISABLED - PyMutex_Lock(&interp->codecs.search_path_mutex); -#endif + FT_MUTEX_LOCK(&interp->codecs.search_path_mutex); PyObject *item = PyList_GetItemRef(codec_search_path, i); int ret = 1; if (item == search_function) { @@ -76,9 +73,7 @@ PyCodec_Unregister(PyObject *search_function) // while we hold search_path_mutex. ret = PyList_SetSlice(codec_search_path, i, i+1, NULL); } -#ifdef Py_GIL_DISABLED - PyMutex_Unlock(&interp->codecs.search_path_mutex); -#endif + FT_MUTEX_UNLOCK(&interp->codecs.search_path_mutex); Py_DECREF(item); if (ret != 1) { assert(interp->codecs.search_cache != NULL); @@ -90,16 +85,15 @@ PyCodec_Unregister(PyObject *search_function) return 0; } -extern int _Py_normalize_encoding(const char *, char *, size_t); - -/* Convert a string to a normalized Python string(decoded from UTF-8): all characters are - converted to lower case, spaces and hyphens are replaced with underscores. */ +/* Convert a string to a normalized Python string: all ASCII letters are + converted to lower case, spaces are replaced with hyphens. */ -static -PyObject *normalizestring(const char *string) +static PyObject* +normalizestring(const char *string) { + size_t i; size_t len = strlen(string); - char *encoding; + char *p; PyObject *v; if (len > PY_SSIZE_T_MAX) { @@ -107,28 +101,30 @@ PyObject *normalizestring(const char *string) return NULL; } - encoding = PyMem_Malloc(len + 1); - if (encoding == NULL) + p = PyMem_Malloc(len + 1); + if (p == NULL) return PyErr_NoMemory(); - - if (!_Py_normalize_encoding(string, encoding, len + 1)) - { - PyErr_SetString(PyExc_RuntimeError, "_Py_normalize_encoding() failed"); - PyMem_Free(encoding); - return NULL; - } - - v = PyUnicode_FromString(encoding); - PyMem_Free(encoding); + for (i = 0; i < len; i++) { + char ch = string[i]; + if (ch == ' ') + ch = '-'; + else + ch = Py_TOLOWER(Py_CHARMASK(ch)); + p[i] = ch; + } + p[i] = '\0'; + v = PyUnicode_FromString(p); + PyMem_Free(p); return v; } /* Lookup the given encoding and return a tuple providing the codec facilities. - The encoding string is looked up converted to all lower-case - characters. This makes encodings looked up through this mechanism - effectively case-insensitive. + ASCII letters in the encoding string is looked up converted to all + lower case. This makes encodings looked up through this mechanism + effectively case-insensitive. Spaces are replaced with hyphens for + names like "US ASCII" and "ISO 8859-1". If no codec is found, a LookupError is set and NULL returned. @@ -149,8 +145,8 @@ PyObject *_PyCodec_Lookup(const char *encoding) assert(interp->codecs.initialized); /* Convert the encoding to a normalized Python string: all - characters are converted to lower case, spaces and hyphens are - replaced with underscores. */ + ASCII letters are converted to lower case, spaces are + replaced with hyphens. */ PyObject *v = normalizestring(encoding); if (v == NULL) { return NULL; @@ -1204,7 +1200,7 @@ get_standard_encoding_impl(const char *encoding, int *bytelength) } } } - else if (strcmp(encoding, "CP_UTF8") == 0) { + else if (strcmp(encoding, "cp65001") == 0) { *bytelength = 3; return ENC_UTF8; } @@ -1691,6 +1687,8 @@ _PyCodec_InitRegistry(PyInterpreterState *interp) // search functions, so this is done after everything else is initialized. PyObject *mod = PyImport_ImportModule("encodings"); if (mod == NULL) { + PyThreadState *tstate = _PyThreadState_GET(); + _Py_DumpPathConfig(tstate); return PyStatus_Error("Failed to import encodings module"); } Py_DECREF(mod); diff --git a/Python/codegen.c b/Python/codegen.c index 53388592330a5d..205c49cff1827c 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -29,6 +29,7 @@ #include "pycore_symtable.h" // PySTEntryObject #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString #include "pycore_ceval.h" // SPECIAL___ENTER__ +#include "pycore_template.h" // _PyTemplate_Type #define NEED_OPCODE_METADATA #include "pycore_opcode_metadata.h" // _PyOpcode_opcode_metadata, _PyOpcode_num_popped/pushed @@ -226,19 +227,25 @@ static int codegen_call_helper(compiler *c, location loc, static int codegen_try_except(compiler *, stmt_ty); static int codegen_try_star_except(compiler *, stmt_ty); +typedef enum { + ITERABLE_IN_LOCAL = 0, + ITERABLE_ON_STACK = 1, + ITERATOR_ON_STACK = 2, +} IterStackPosition; + static int codegen_sync_comprehension_generator( compiler *c, location loc, asdl_comprehension_seq *generators, int gen_index, int depth, expr_ty elt, expr_ty val, int type, - int iter_on_stack); + IterStackPosition iter_pos); static int codegen_async_comprehension_generator( compiler *c, location loc, asdl_comprehension_seq *generators, int gen_index, int depth, expr_ty elt, expr_ty val, int type, - int iter_on_stack); + IterStackPosition iter_pos); static int codegen_pattern(compiler *, pattern_ty, pattern_context *); static int codegen_match(compiler *, stmt_ty); @@ -350,8 +357,8 @@ codegen_addop_o(compiler *c, location loc, #define LOAD_ZERO_SUPER_METHOD -4 static int -codegen_addop_name(compiler *c, location loc, - int opcode, PyObject *dict, PyObject *o) +codegen_addop_name_custom(compiler *c, location loc, int opcode, + PyObject *dict, PyObject *o, int shift, int low) { PyObject *mangled = _PyCompile_MaybeMangle(c, o); if (!mangled) { @@ -362,40 +369,51 @@ codegen_addop_name(compiler *c, location loc, if (arg < 0) { return ERROR; } + ADDOP_I(c, loc, opcode, (arg << shift) | low); + return SUCCESS; +} + +static int +codegen_addop_name(compiler *c, location loc, + int opcode, PyObject *dict, PyObject *o) +{ + int shift = 0, low = 0; if (opcode == LOAD_ATTR) { - arg <<= 1; + shift = 1; } if (opcode == LOAD_METHOD) { opcode = LOAD_ATTR; - arg <<= 1; - arg |= 1; + shift = 1; + low = 1; } if (opcode == LOAD_SUPER_ATTR) { - arg <<= 2; - arg |= 2; + shift = 2; + low = 2; } if (opcode == LOAD_SUPER_METHOD) { opcode = LOAD_SUPER_ATTR; - arg <<= 2; - arg |= 3; + shift = 2; + low = 3; } if (opcode == LOAD_ZERO_SUPER_ATTR) { opcode = LOAD_SUPER_ATTR; - arg <<= 2; + shift = 2; } if (opcode == LOAD_ZERO_SUPER_METHOD) { opcode = LOAD_SUPER_ATTR; - arg <<= 2; - arg |= 1; + shift = 2; + low = 1; } - ADDOP_I(c, loc, opcode, arg); - return SUCCESS; + return codegen_addop_name_custom(c, loc, opcode, dict, o, shift, low); } #define ADDOP_NAME(C, LOC, OP, O, TYPE) \ RETURN_IF_ERROR(codegen_addop_name((C), (LOC), (OP), METADATA(C)->u_ ## TYPE, (O))) -static int +#define ADDOP_NAME_CUSTOM(C, LOC, OP, O, TYPE, SHIFT, LOW) \ + RETURN_IF_ERROR(codegen_addop_name_custom((C), (LOC), (OP), METADATA(C)->u_ ## TYPE, (O), SHIFT, LOW)) + + static int codegen_addop_j(instr_sequence *seq, location loc, int opcode, jump_target_label target) { @@ -588,6 +606,7 @@ codegen_unwind_fblock(compiler *c, location *ploc, RETURN_IF_ERROR(codegen_call_exit_with_nones(c, *ploc)); if (info->fb_type == COMPILE_FBLOCK_ASYNC_WITH) { ADDOP_I(c, *ploc, GET_AWAITABLE, 2); + ADDOP(c, *ploc, PUSH_NULL); ADDOP_LOAD_CONST(c, *ploc, Py_None); ADD_YIELD_FROM(c, *ploc, 1); } @@ -648,8 +667,8 @@ codegen_unwind_fblock_stack(compiler *c, location *ploc, _PyCompile_PopFBlock(c, top->fb_type, top->fb_block); RETURN_IF_ERROR(codegen_unwind_fblock(c, ploc, &copy, preserve_tos)); RETURN_IF_ERROR(codegen_unwind_fblock_stack(c, ploc, preserve_tos, loop)); - _PyCompile_PushFBlock(c, copy.fb_loc, copy.fb_type, copy.fb_block, - copy.fb_exit, copy.fb_datum); + RETURN_IF_ERROR(_PyCompile_PushFBlock(c, copy.fb_loc, copy.fb_type, copy.fb_block, + copy.fb_exit, copy.fb_datum)); return SUCCESS; } @@ -664,6 +683,18 @@ codegen_enter_scope(compiler *c, identifier name, int scope_type, if (scope_type == COMPILE_SCOPE_MODULE) { loc.lineno = 0; } + /* Add the generator prefix instructions. */ + + PySTEntryObject *ste = SYMTABLE_ENTRY(c); + if (ste->ste_coroutine || ste->ste_generator) { + /* Note that RETURN_GENERATOR + POP_TOP have a net stack effect + * of 0. This is because RETURN_GENERATOR pushes the generator + before returning. */ + location loc = LOCATION(lineno, lineno, -1, -1); + ADDOP(c, loc, RETURN_GENERATOR); + ADDOP(c, loc, POP_TOP); + } + ADDOP_I(c, loc, RESUME, RESUME_AT_FUNC_START); if (scope_type == COMPILE_SCOPE_MODULE) { ADDOP(c, loc, ANNOTATIONS_PLACEHOLDER); @@ -684,10 +715,14 @@ codegen_setup_annotations_scope(compiler *c, location loc, // if .format > VALUE_WITH_FAKE_GLOBALS: raise NotImplementedError PyObject *value_with_fake_globals = PyLong_FromLong(_Py_ANNOTATE_FORMAT_VALUE_WITH_FAKE_GLOBALS); + if (value_with_fake_globals == NULL) { + return ERROR; + } + assert(!SYMTABLE_ENTRY(c)->ste_has_docstring); _Py_DECLARE_STR(format, ".format"); ADDOP_I(c, loc, LOAD_FAST, 0); - ADDOP_LOAD_CONST(c, loc, value_with_fake_globals); + ADDOP_LOAD_CONST_NEW(c, loc, value_with_fake_globals); ADDOP_I(c, loc, COMPARE_OP, (Py_GT << 5) | compare_masks[Py_GT]); NEW_JUMP_TARGET_LABEL(c, body); ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, body); @@ -763,6 +798,9 @@ codegen_deferred_annotations_body(compiler *c, location loc, if (!mangled) { return ERROR; } + // NOTE: ref of mangled can be leaked on ADDOP* and VISIT macros due to early returns + // fixing would require an overhaul of these macros + PyObject *cond_index = PyList_GET_ITEM(conditional_annotation_indices, i); assert(PyLong_CheckExact(cond_index)); long idx = PyLong_AS_LONG(cond_index); @@ -1092,10 +1130,10 @@ codegen_annotations_in_scope(compiler *c, location loc, Py_ssize_t *annotations_len) { RETURN_IF_ERROR( - codegen_argannotations(c, args->args, annotations_len, loc)); + codegen_argannotations(c, args->posonlyargs, annotations_len, loc)); RETURN_IF_ERROR( - codegen_argannotations(c, args->posonlyargs, annotations_len, loc)); + codegen_argannotations(c, args->args, annotations_len, loc)); if (args->vararg && args->vararg->annotation) { RETURN_IF_ERROR( @@ -1186,10 +1224,20 @@ codegen_wrap_in_stopiteration_handler(compiler *c) { NEW_JUMP_TARGET_LABEL(c, handler); - /* Insert SETUP_CLEANUP at start */ + /* Insert SETUP_CLEANUP just after the initial RETURN_GENERATOR; POP_TOP */ + instr_sequence *seq = INSTR_SEQUENCE(c); + int resume = 0; + while (_PyInstructionSequence_GetInstruction(seq, resume).i_opcode != RETURN_GENERATOR) { + resume++; + assert(resume < seq->s_used); + } + resume++; + assert(_PyInstructionSequence_GetInstruction(seq, resume).i_opcode == POP_TOP); + resume++; + assert(resume < seq->s_used); RETURN_IF_ERROR( _PyInstructionSequence_InsertInstruction( - INSTR_SEQUENCE(c), 0, + seq, resume, SETUP_CLEANUP, handler.id, NO_LOCATION)); ADDOP_LOAD_CONST(c, NO_LOCATION, Py_None); @@ -1209,11 +1257,11 @@ codegen_type_param_bound_or_default(compiler *c, expr_ty e, ADDOP_LOAD_CONST_NEW(c, LOC(e), defaults); RETURN_IF_ERROR(codegen_setup_annotations_scope(c, LOC(e), key, name)); if (allow_starred && e->kind == Starred_kind) { - VISIT(c, expr, e->v.Starred.value); - ADDOP_I(c, LOC(e), UNPACK_SEQUENCE, (Py_ssize_t)1); + VISIT_IN_SCOPE(c, expr, e->v.Starred.value); + ADDOP_I_IN_SCOPE(c, LOC(e), UNPACK_SEQUENCE, (Py_ssize_t)1); } else { - VISIT(c, expr, e); + VISIT_IN_SCOPE(c, expr, e); } ADDOP_IN_SCOPE(c, LOC(e), RETURN_VALUE); PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 1); @@ -1387,7 +1435,6 @@ codegen_function_body(compiler *c, stmt_ty s, int is_async, Py_ssize_t funcflags PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 1); _PyCompile_ExitScope(c); if (co == NULL) { - Py_XDECREF(co); return ERROR; } int ret = codegen_make_closure(c, LOC(s), co, funcflags); @@ -2090,7 +2137,7 @@ codegen_for(compiler *c, stmt_ty s) VISIT(c, expr, s->v.For.iter); loc = LOC(s->v.For.iter); - ADDOP(c, loc, GET_ITER); + ADDOP_I(c, loc, GET_ITER, 0); USE_LABEL(c, start); ADDOP_JUMP(c, loc, FOR_ITER, cleanup); @@ -2109,7 +2156,7 @@ codegen_for(compiler *c, stmt_ty s) USE_LABEL(c, cleanup); /* It is important for instrumentation that the `END_FOR` comes first. * Iteration over a generator will jump to the first of these instructions, - * but a non-generator will jump to a later instruction. + * but a non-generator will jump to the second instruction. */ ADDOP(c, NO_LOCATION, END_FOR); ADDOP(c, NO_LOCATION, POP_ITER); @@ -2141,6 +2188,7 @@ codegen_async_for(compiler *c, stmt_ty s) /* SETUP_FINALLY to guard the __anext__ call */ ADDOP_JUMP(c, loc, SETUP_FINALLY, except); ADDOP(c, loc, GET_ANEXT); + ADDOP(c, loc, PUSH_NULL); ADDOP_LOAD_CONST(c, loc, Py_None); USE_LABEL(c, send); ADD_YIELD_FROM(c, loc, 1); @@ -2840,6 +2888,17 @@ codegen_import_as(compiler *c, location loc, return codegen_nameop(c, loc, asname, Store); } +static int +codegen_validate_lazy_import(compiler *c, location loc) +{ + if (_PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE) { + return _PyCompile_Error( + c, loc, "lazy imports only allowed in module scope"); + } + + return SUCCESS; +} + static int codegen_import(compiler *c, stmt_ty s) { @@ -2860,7 +2919,18 @@ codegen_import(compiler *c, stmt_ty s) ADDOP_LOAD_CONST(c, loc, zero); ADDOP_LOAD_CONST(c, loc, Py_None); - ADDOP_NAME(c, loc, IMPORT_NAME, alias->name, names); + if (s->v.Import.is_lazy) { + RETURN_IF_ERROR(codegen_validate_lazy_import(c, loc)); + ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 1); + } else { + if (_PyCompile_InExceptionHandler(c) || + _PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE) { + // force eager import in try/except block + ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 2); + } else { + ADDOP_NAME_CUSTOM(c, loc, IMPORT_NAME, alias->name, names, 2, 0); + } + } if (alias->asname) { r = codegen_import_as(c, loc, alias->name, alias->asname); @@ -2906,13 +2976,29 @@ codegen_from_import(compiler *c, stmt_ty s) ADDOP_LOAD_CONST_NEW(c, LOC(s), names); + identifier from = &_Py_STR(empty); if (s->v.ImportFrom.module) { - ADDOP_NAME(c, LOC(s), IMPORT_NAME, s->v.ImportFrom.module, names); + from = s->v.ImportFrom.module; } - else { - _Py_DECLARE_STR(empty, ""); - ADDOP_NAME(c, LOC(s), IMPORT_NAME, &_Py_STR(empty), names); + if (s->v.ImportFrom.is_lazy) { + alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, 0); + if (PyUnicode_READ_CHAR(alias->name, 0) == '*') { + return _PyCompile_Error(c, LOC(s), "cannot lazy import *"); + } + RETURN_IF_ERROR(codegen_validate_lazy_import(c, LOC(s))); + ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 1); + } else { + alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, 0); + if (_PyCompile_InExceptionHandler(c) || + _PyCompile_ScopeType(c) != COMPILE_SCOPE_MODULE || + PyUnicode_READ_CHAR(alias->name, 0) == '*') { + // forced non-lazy import due to try/except or import * + ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 2); + } else { + ADDOP_NAME_CUSTOM(c, LOC(s), IMPORT_NAME, from, names, 2, 0); + } } + for (Py_ssize_t i = 0; i < n; i++) { alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i); identifier store_name; @@ -3205,7 +3291,10 @@ codegen_nameop(compiler *c, location loc, } int scope = _PyST_GetScope(SYMTABLE_ENTRY(c), mangled); - RETURN_IF_ERROR(scope); + if (scope == -1) { + goto error; + } + _PyCompile_optype optype; Py_ssize_t arg = 0; if (_PyCompile_ResolveNameop(c, mangled, scope, &optype, &arg) < 0) { @@ -3617,10 +3706,11 @@ infer_type(expr_ty e) return &PyGen_Type; case Lambda_kind: return &PyFunction_Type; - case JoinedStr_kind: case TemplateStr_kind: - case FormattedValue_kind: case Interpolation_kind: + return &_PyTemplate_Type; + case JoinedStr_kind: + case FormattedValue_kind: return &PyUnicode_Type; case Constant_kind: return Py_TYPE(e->v.Constant.value); @@ -3674,6 +3764,8 @@ check_subscripter(compiler *c, expr_ty e) case Set_kind: case SetComp_kind: case GeneratorExp_kind: + case TemplateStr_kind: + case Interpolation_kind: case Lambda_kind: { location loc = LOC(e); return _PyCompile_Warn(c, loc, "'%.200s' object is not subscriptable; " @@ -3708,9 +3800,7 @@ check_index(compiler *c, expr_ty e, expr_ty s) case List_kind: case ListComp_kind: case JoinedStr_kind: - case TemplateStr_kind: - case FormattedValue_kind: - case Interpolation_kind: { + case FormattedValue_kind: { location loc = LOC(e); return _PyCompile_Warn(c, loc, "%.200s indices must be integers " "or slices, not %.200s; " @@ -3863,14 +3953,45 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) if (! (func->kind == Name_kind && asdl_seq_LEN(args) == 1 && - asdl_seq_LEN(kwds) == 0 && - asdl_seq_GET(args, 0)->kind == GeneratorExp_kind)) + asdl_seq_LEN(kwds) == 0)) { return 0; } location loc = LOC(func); + expr_ty arg_expr = asdl_seq_GET(args, 0); + + if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "frozenset") + && (arg_expr->kind == Set_kind || arg_expr->kind == SetComp_kind)) { + NEW_JUMP_TARGET_LABEL(c, skip_optimization); + + ADDOP_I(c, loc, COPY, 1); + ADDOP_I(c, loc, LOAD_COMMON_CONSTANT, CONSTANT_BUILTIN_FROZENSET); + ADDOP_COMPARE(c, loc, Is); + ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, skip_optimization); + ADDOP(c, loc, POP_TOP); + + VISIT(c, expr, arg_expr); + ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_BUILD_FROZENSET); + + ADDOP_JUMP(c, loc, JUMP, end); + + USE_LABEL(c, skip_optimization); + return 1; + } + + if (arg_expr->kind != GeneratorExp_kind) { + return 0; + } + + PySTEntryObject *generator_entry = _PySymtable_Lookup(SYMTABLE(c), (void *)arg_expr); + if (generator_entry->ste_coroutine) { + Py_DECREF(generator_entry); + return 0; + } + Py_DECREF(generator_entry); + int optimized = 0; NEW_JUMP_TARGET_LABEL(c, skip_optimization); @@ -3890,6 +4011,15 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) else if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "tuple")) { const_oparg = CONSTANT_BUILTIN_TUPLE; } + else if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "list")) { + const_oparg = CONSTANT_BUILTIN_LIST; + } + else if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "set")) { + const_oparg = CONSTANT_BUILTIN_SET; + } + else if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "frozenset")) { + const_oparg = CONSTANT_BUILTIN_FROZENSET; + } if (const_oparg != -1) { ADDOP_I(c, loc, COPY, 1); // the function ADDOP_I(c, loc, LOAD_COMMON_CONSTANT, const_oparg); @@ -3897,11 +4027,12 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, skip_optimization); ADDOP(c, loc, POP_TOP); - if (const_oparg == CONSTANT_BUILTIN_TUPLE) { + if (const_oparg == CONSTANT_BUILTIN_TUPLE || const_oparg == CONSTANT_BUILTIN_LIST) { ADDOP_I(c, loc, BUILD_LIST, 0); + } else if (const_oparg == CONSTANT_BUILTIN_SET || const_oparg == CONSTANT_BUILTIN_FROZENSET) { + ADDOP_I(c, loc, BUILD_SET, 0); } - expr_ty generator_exp = asdl_seq_GET(args, 0); - VISIT(c, expr, generator_exp); + VISIT(c, expr, arg_expr); NEW_JUMP_TARGET_LABEL(c, loop); NEW_JUMP_TARGET_LABEL(c, cleanup); @@ -3909,9 +4040,12 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) ADDOP(c, loc, PUSH_NULL); // Push NULL index for loop USE_LABEL(c, loop); ADDOP_JUMP(c, loc, FOR_ITER, cleanup); - if (const_oparg == CONSTANT_BUILTIN_TUPLE) { + if (const_oparg == CONSTANT_BUILTIN_TUPLE || const_oparg == CONSTANT_BUILTIN_LIST) { ADDOP_I(c, loc, LIST_APPEND, 3); ADDOP_JUMP(c, loc, JUMP, loop); + } else if (const_oparg == CONSTANT_BUILTIN_SET || const_oparg == CONSTANT_BUILTIN_FROZENSET) { + ADDOP_I(c, loc, SET_ADD, 3); + ADDOP_JUMP(c, loc, JUMP, loop); } else { ADDOP(c, loc, TO_BOOL); @@ -3919,7 +4053,10 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) } ADDOP(c, NO_LOCATION, POP_ITER); - if (const_oparg != CONSTANT_BUILTIN_TUPLE) { + if (const_oparg != CONSTANT_BUILTIN_TUPLE && + const_oparg != CONSTANT_BUILTIN_LIST && + const_oparg != CONSTANT_BUILTIN_SET && + const_oparg != CONSTANT_BUILTIN_FROZENSET) { ADDOP_LOAD_CONST(c, loc, initial_res == Py_True ? Py_False : Py_True); } ADDOP_JUMP(c, loc, JUMP, end); @@ -3929,6 +4066,13 @@ maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end) ADDOP(c, NO_LOCATION, POP_ITER); if (const_oparg == CONSTANT_BUILTIN_TUPLE) { ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_LIST_TO_TUPLE); + } else if (const_oparg == CONSTANT_BUILTIN_LIST) { + // result is already a list + } else if (const_oparg == CONSTANT_BUILTIN_SET) { + // result is already a set + } + else if (const_oparg == CONSTANT_BUILTIN_FROZENSET) { + ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_BUILD_FROZENSET); } else { ADDOP_LOAD_CONST(c, loc, initial_res); @@ -4382,18 +4526,18 @@ codegen_comprehension_generator(compiler *c, location loc, asdl_comprehension_seq *generators, int gen_index, int depth, expr_ty elt, expr_ty val, int type, - int iter_on_stack) + IterStackPosition iter_pos) { comprehension_ty gen; gen = (comprehension_ty)asdl_seq_GET(generators, gen_index); if (gen->is_async) { return codegen_async_comprehension_generator( c, loc, generators, gen_index, depth, elt, val, type, - iter_on_stack); + iter_pos); } else { return codegen_sync_comprehension_generator( c, loc, generators, gen_index, depth, elt, val, type, - iter_on_stack); + iter_pos); } } @@ -4402,7 +4546,7 @@ codegen_sync_comprehension_generator(compiler *c, location loc, asdl_comprehension_seq *generators, int gen_index, int depth, expr_ty elt, expr_ty val, int type, - int iter_on_stack) + IterStackPosition iter_pos) { /* generate code for the iterator, then each of the ifs, and then write to the element */ @@ -4414,7 +4558,7 @@ codegen_sync_comprehension_generator(compiler *c, location loc, comprehension_ty gen = (comprehension_ty)asdl_seq_GET(generators, gen_index); - if (!iter_on_stack) { + if (iter_pos == ITERABLE_IN_LOCAL) { if (gen_index == 0) { assert(METADATA(c)->u_argcount == 1); ADDOP_I(c, loc, LOAD_FAST, 0); @@ -4449,9 +4593,12 @@ codegen_sync_comprehension_generator(compiler *c, location loc, } if (IS_JUMP_TARGET_LABEL(start)) { - depth += 2; - ADDOP(c, LOC(gen->iter), GET_ITER); + if (iter_pos != ITERATOR_ON_STACK) { + ADDOP_I(c, LOC(gen->iter), GET_ITER, 0); + depth += 1; + } USE_LABEL(c, start); + depth += 1; ADDOP_JUMP(c, LOC(gen->iter), FOR_ITER, anchor); } VISIT(c, expr, gen->target); @@ -4467,7 +4614,7 @@ codegen_sync_comprehension_generator(compiler *c, location loc, RETURN_IF_ERROR( codegen_comprehension_generator(c, loc, generators, gen_index, depth, - elt, val, type, 0)); + elt, val, type, ITERABLE_IN_LOCAL)); } location elt_loc = LOC(elt); @@ -4477,28 +4624,63 @@ codegen_sync_comprehension_generator(compiler *c, location loc, /* comprehension specific code */ switch (type) { case COMP_GENEXP: - VISIT(c, expr, elt); - ADDOP_YIELD(c, elt_loc); - ADDOP(c, elt_loc, POP_TOP); + if (elt->kind == Starred_kind) { + NEW_JUMP_TARGET_LABEL(c, unpack_start); + NEW_JUMP_TARGET_LABEL(c, unpack_end); + VISIT(c, expr, elt->v.Starred.value); + ADDOP_I(c, elt_loc, GET_ITER, 0); + USE_LABEL(c, unpack_start); + ADDOP_JUMP(c, elt_loc, FOR_ITER, unpack_end); + ADDOP_YIELD(c, elt_loc); + ADDOP(c, elt_loc, POP_TOP); + ADDOP_JUMP(c, NO_LOCATION, JUMP, unpack_start); + USE_LABEL(c, unpack_end); + ADDOP(c, NO_LOCATION, END_FOR); + ADDOP(c, NO_LOCATION, POP_ITER); + } + else { + VISIT(c, expr, elt); + ADDOP_YIELD(c, elt_loc); + ADDOP(c, elt_loc, POP_TOP); + } break; case COMP_LISTCOMP: - VISIT(c, expr, elt); - ADDOP_I(c, elt_loc, LIST_APPEND, depth + 1); + if (elt->kind == Starred_kind) { + VISIT(c, expr, elt->v.Starred.value); + ADDOP_I(c, elt_loc, LIST_EXTEND, depth + 1); + } + else { + VISIT(c, expr, elt); + ADDOP_I(c, elt_loc, LIST_APPEND, depth + 1); + } break; case COMP_SETCOMP: - VISIT(c, expr, elt); - ADDOP_I(c, elt_loc, SET_ADD, depth + 1); + if (elt->kind == Starred_kind) { + VISIT(c, expr, elt->v.Starred.value); + ADDOP_I(c, elt_loc, SET_UPDATE, depth + 1); + } + else { + VISIT(c, expr, elt); + ADDOP_I(c, elt_loc, SET_ADD, depth + 1); + } break; case COMP_DICTCOMP: - /* With '{k: v}', k is evaluated before v, so we do - the same. */ - VISIT(c, expr, elt); - VISIT(c, expr, val); - elt_loc = LOCATION(elt->lineno, - val->end_lineno, - elt->col_offset, - val->end_col_offset); - ADDOP_I(c, elt_loc, MAP_ADD, depth + 1); + if (val == NULL) { + /* unpacking (**) case */ + VISIT(c, expr, elt); + ADDOP_I(c, elt_loc, DICT_UPDATE, depth+1); + } + else { + /* With '{k: v}', k is evaluated before v, so we do + the same. */ + VISIT(c, expr, elt); + VISIT(c, expr, val); + elt_loc = LOCATION(elt->lineno, + val->end_lineno, + elt->col_offset, + val->end_col_offset); + ADDOP_I(c, elt_loc, MAP_ADD, depth + 1); + } break; default: return ERROR; @@ -4526,7 +4708,7 @@ codegen_async_comprehension_generator(compiler *c, location loc, asdl_comprehension_seq *generators, int gen_index, int depth, expr_ty elt, expr_ty val, int type, - int iter_on_stack) + IterStackPosition iter_pos) { NEW_JUMP_TARGET_LABEL(c, start); NEW_JUMP_TARGET_LABEL(c, send); @@ -4536,7 +4718,7 @@ codegen_async_comprehension_generator(compiler *c, location loc, comprehension_ty gen = (comprehension_ty)asdl_seq_GET(generators, gen_index); - if (!iter_on_stack) { + if (iter_pos == ITERABLE_IN_LOCAL) { if (gen_index == 0) { assert(METADATA(c)->u_argcount == 1); ADDOP_I(c, loc, LOAD_FAST, 0); @@ -4546,7 +4728,9 @@ codegen_async_comprehension_generator(compiler *c, location loc, VISIT(c, expr, gen->iter); } } - ADDOP(c, LOC(gen->iter), GET_AITER); + if (iter_pos != ITERATOR_ON_STACK) { + ADDOP(c, LOC(gen->iter), GET_AITER); + } USE_LABEL(c, start); /* Runtime will push a block here, so we need to account for that */ @@ -4556,6 +4740,7 @@ codegen_async_comprehension_generator(compiler *c, location loc, ADDOP_JUMP(c, loc, SETUP_FINALLY, except); ADDOP(c, loc, GET_ANEXT); + ADDOP(c, loc, PUSH_NULL); ADDOP_LOAD_CONST(c, loc, Py_None); USE_LABEL(c, send); ADD_YIELD_FROM(c, loc, 1); @@ -4582,28 +4767,63 @@ codegen_async_comprehension_generator(compiler *c, location loc, /* comprehension specific code */ switch (type) { case COMP_GENEXP: - VISIT(c, expr, elt); - ADDOP_YIELD(c, elt_loc); - ADDOP(c, elt_loc, POP_TOP); + if (elt->kind == Starred_kind) { + NEW_JUMP_TARGET_LABEL(c, unpack_start); + NEW_JUMP_TARGET_LABEL(c, unpack_end); + VISIT(c, expr, elt->v.Starred.value); + ADDOP_I(c, elt_loc, GET_ITER, 0); + USE_LABEL(c, unpack_start); + ADDOP_JUMP(c, elt_loc, FOR_ITER, unpack_end); + ADDOP_YIELD(c, elt_loc); + ADDOP(c, elt_loc, POP_TOP); + ADDOP_JUMP(c, NO_LOCATION, JUMP, unpack_start); + USE_LABEL(c, unpack_end); + ADDOP(c, NO_LOCATION, END_FOR); + ADDOP(c, NO_LOCATION, POP_ITER); + } + else { + VISIT(c, expr, elt); + ADDOP_YIELD(c, elt_loc); + ADDOP(c, elt_loc, POP_TOP); + } break; case COMP_LISTCOMP: - VISIT(c, expr, elt); - ADDOP_I(c, elt_loc, LIST_APPEND, depth + 1); + if (elt->kind == Starred_kind) { + VISIT(c, expr, elt->v.Starred.value); + ADDOP_I(c, elt_loc, LIST_EXTEND, depth + 1); + } + else { + VISIT(c, expr, elt); + ADDOP_I(c, elt_loc, LIST_APPEND, depth + 1); + } break; case COMP_SETCOMP: - VISIT(c, expr, elt); - ADDOP_I(c, elt_loc, SET_ADD, depth + 1); + if (elt->kind == Starred_kind) { + VISIT(c, expr, elt->v.Starred.value); + ADDOP_I(c, elt_loc, SET_UPDATE, depth + 1); + } + else { + VISIT(c, expr, elt); + ADDOP_I(c, elt_loc, SET_ADD, depth + 1); + } break; case COMP_DICTCOMP: - /* With '{k: v}', k is evaluated before v, so we do - the same. */ - VISIT(c, expr, elt); - VISIT(c, expr, val); - elt_loc = LOCATION(elt->lineno, - val->end_lineno, - elt->col_offset, - val->end_col_offset); - ADDOP_I(c, elt_loc, MAP_ADD, depth + 1); + if (val == NULL) { + /* unpacking (**) case */ + VISIT(c, expr, elt); + ADDOP_I(c, elt_loc, DICT_UPDATE, depth+1); + } + else { + /* With '{k: v}', k is evaluated before v, so we do + the same. */ + VISIT(c, expr, elt); + VISIT(c, expr, val); + elt_loc = LOCATION(elt->lineno, + val->end_lineno, + elt->col_offset, + val->end_col_offset); + ADDOP_I(c, elt_loc, MAP_ADD, depth + 1); + } break; default: return ERROR; @@ -4776,11 +4996,13 @@ codegen_comprehension(compiler *c, expr_ty e, int type, location loc = LOC(e); outermost = (comprehension_ty) asdl_seq_GET(generators, 0); + IterStackPosition iter_state; if (is_inlined) { VISIT(c, expr, outermost->iter); if (push_inlined_comprehension_state(c, loc, entry, &inline_state)) { goto error; } + iter_state = ITERABLE_ON_STACK; } else { /* Receive outermost iter as an implicit argument */ @@ -4791,6 +5013,27 @@ codegen_comprehension(compiler *c, expr_ty e, int type, (void *)e, e->lineno, NULL, &umd) < 0) { goto error; } + if (type == COMP_GENEXP) { + /* Insert GET_ITER before RETURN_GENERATOR. + https://docs.python.org/3/reference/expressions.html#generator-expressions */ + RETURN_IF_ERROR( + _PyInstructionSequence_InsertInstruction( + INSTR_SEQUENCE(c), 0, + RESUME, RESUME_AT_GEN_EXPR_START, NO_LOCATION)); + RETURN_IF_ERROR( + _PyInstructionSequence_InsertInstruction( + INSTR_SEQUENCE(c), 1, + LOAD_FAST, 0, LOC(outermost->iter))); + RETURN_IF_ERROR( + _PyInstructionSequence_InsertInstruction( + INSTR_SEQUENCE(c), 2, + outermost->is_async ? GET_AITER : GET_ITER, + 0, LOC(outermost->iter))); + iter_state = ITERATOR_ON_STACK; + } + else { + iter_state = ITERABLE_IN_LOCAL; + } } Py_CLEAR(entry); @@ -4817,9 +5060,8 @@ codegen_comprehension(compiler *c, expr_ty e, int type, ADDOP_I(c, loc, SWAP, 2); } } - if (codegen_comprehension_generator(c, loc, generators, 0, 0, - elt, val, type, is_inlined) < 0) { + elt, val, type, iter_state) < 0) { goto error_in_scope; } @@ -4856,6 +5098,7 @@ codegen_comprehension(compiler *c, expr_ty e, int type, if (is_async_comprehension && type != COMP_GENEXP) { ADDOP_I(c, loc, GET_AWAITABLE, 0); + ADDOP(c, loc, PUSH_NULL); ADDOP_LOAD_CONST(c, loc, Py_None); ADD_YIELD_FROM(c, loc, 1); } @@ -4995,6 +5238,7 @@ codegen_async_with_inner(compiler *c, stmt_ty s, int pos) ADDOP_I(c, loc, LOAD_SPECIAL, SPECIAL___AENTER__); ADDOP_I(c, loc, CALL, 0); ADDOP_I(c, loc, GET_AWAITABLE, 1); + ADDOP(c, loc, PUSH_NULL); ADDOP_LOAD_CONST(c, loc, Py_None); ADD_YIELD_FROM(c, loc, 1); @@ -5031,6 +5275,7 @@ codegen_async_with_inner(compiler *c, stmt_ty s, int pos) */ RETURN_IF_ERROR(codegen_call_exit_with_nones(c, loc)); ADDOP_I(c, loc, GET_AWAITABLE, 2); + ADDOP(c, loc, PUSH_NULL); ADDOP_LOAD_CONST(c, loc, Py_None); ADD_YIELD_FROM(c, loc, 1); @@ -5045,6 +5290,7 @@ codegen_async_with_inner(compiler *c, stmt_ty s, int pos) ADDOP(c, loc, PUSH_EXC_INFO); ADDOP(c, loc, WITH_EXCEPT_START); ADDOP_I(c, loc, GET_AWAITABLE, 2); + ADDOP(c, loc, PUSH_NULL); ADDOP_LOAD_CONST(c, loc, Py_None); ADD_YIELD_FROM(c, loc, 1); RETURN_IF_ERROR(codegen_with_except_finish(c, cleanup)); @@ -5225,13 +5471,14 @@ codegen_visit_expr(compiler *c, expr_ty e) return _PyCompile_Error(c, loc, "'yield from' inside async function"); } VISIT(c, expr, e->v.YieldFrom.value); - ADDOP(c, loc, GET_YIELD_FROM_ITER); + ADDOP_I(c, loc, GET_ITER, GET_ITER_YIELD_FROM); ADDOP_LOAD_CONST(c, loc, Py_None); ADD_YIELD_FROM(c, loc, 0); break; case Await_kind: VISIT(c, expr, e->v.Await.value); ADDOP_I(c, loc, GET_AWAITABLE, 0); + ADDOP(c, loc, PUSH_NULL); ADDOP_LOAD_CONST(c, loc, Py_None); ADD_YIELD_FROM(c, loc, 1); break; @@ -5412,23 +5659,6 @@ codegen_check_ann_expr(compiler *c, expr_ty e) return SUCCESS; } -static int -codegen_check_annotation(compiler *c, stmt_ty s) -{ - /* Annotations of complex targets does not produce anything - under annotations future */ - if (FUTURE_FEATURES(c) & CO_FUTURE_ANNOTATIONS) { - return SUCCESS; - } - - /* Annotations are only evaluated in a module or class. */ - if (SCOPE_TYPE(c) == COMPILE_SCOPE_MODULE || - SCOPE_TYPE(c) == COMPILE_SCOPE_CLASS) { - return codegen_check_ann_expr(c, s->v.AnnAssign.annotation); - } - return SUCCESS; -} - static int codegen_check_ann_subscr(compiler *c, expr_ty e) { @@ -5492,10 +5722,12 @@ codegen_annassign(compiler *c, stmt_ty s) RETURN_IF_ERROR(_PyCompile_AddDeferredAnnotation( c, s, &conditional_annotation_index)); if (conditional_annotation_index != NULL) { - ADDOP_NAME( - c, loc, - SCOPE_TYPE(c) == COMPILE_SCOPE_CLASS ? LOAD_DEREF : LOAD_NAME, - &_Py_ID(__conditional_annotations__), cellvars); + if (SCOPE_TYPE(c) == COMPILE_SCOPE_CLASS) { + ADDOP_NAME(c, loc, LOAD_DEREF, &_Py_ID(__conditional_annotations__), cellvars); + } + else { + ADDOP_NAME(c, loc, LOAD_NAME, &_Py_ID(__conditional_annotations__), names); + } ADDOP_LOAD_CONST_NEW(c, loc, conditional_annotation_index); ADDOP_I(c, loc, SET_ADD, 1); ADDOP(c, loc, POP_TOP); @@ -5522,10 +5754,6 @@ codegen_annassign(compiler *c, stmt_ty s) targ->kind); return ERROR; } - /* Annotation is evaluated last. */ - if (future_annotations && !s->v.AnnAssign.simple && codegen_check_annotation(c, s) < 0) { - return ERROR; - } return SUCCESS; } diff --git a/Python/compile.c b/Python/compile.c index c04391e682f9ac..e223ef42a42e22 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -23,6 +23,7 @@ #include "pycore_runtime.h" // _Py_ID() #include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_stats.h" +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() #include "cpython/code.h" @@ -103,11 +104,14 @@ typedef struct _PyCompiler { bool c_save_nested_seqs; /* if true, construct recursive instruction sequences * (including instructions for nested code objects) */ + int c_disable_warning; + PyObject *c_module; } compiler; static int compiler_setup(compiler *c, mod_ty mod, PyObject *filename, - PyCompilerFlags *flags, int optimize, PyArena *arena) + PyCompilerFlags *flags, int optimize, PyArena *arena, + PyObject *module) { PyCompilerFlags local_flags = _PyCompilerFlags_INIT; @@ -125,6 +129,7 @@ compiler_setup(compiler *c, mod_ty mod, PyObject *filename, if (!_PyFuture_FromAST(mod, filename, &c->c_future)) { return ERROR; } + c->c_module = Py_XNewRef(module); if (!flags) { flags = &local_flags; } @@ -135,7 +140,9 @@ compiler_setup(compiler *c, mod_ty mod, PyObject *filename, c->c_optimize = (optimize == -1) ? _Py_GetConfig()->optimization_level : optimize; c->c_save_nested_seqs = false; - if (!_PyAST_Preprocess(mod, arena, filename, c->c_optimize, merged, 0)) { + if (!_PyAST_Preprocess(mod, arena, filename, c->c_optimize, merged, + 0, 1, module)) + { return ERROR; } c->c_st = _PySymtable_Build(mod, filename, &c->c_future); @@ -155,6 +162,7 @@ compiler_free(compiler *c) _PySymtable_Free(c->c_st); } Py_XDECREF(c->c_filename); + Py_XDECREF(c->c_module); Py_XDECREF(c->c_const_cache); Py_XDECREF(c->c_stack); PyMem_Free(c); @@ -162,13 +170,13 @@ compiler_free(compiler *c) static compiler* new_compiler(mod_ty mod, PyObject *filename, PyCompilerFlags *pflags, - int optimize, PyArena *arena) + int optimize, PyArena *arena, PyObject *module) { compiler *c = PyMem_Calloc(1, sizeof(compiler)); if (c == NULL) { return NULL; } - if (compiler_setup(c, mod, filename, pflags, optimize, arena) < 0) { + if (compiler_setup(c, mod, filename, pflags, optimize, arena, module) < 0) { compiler_free(c); return NULL; } @@ -289,6 +297,19 @@ compiler_set_qualname(compiler *c) base = Py_NewRef(parent->u_metadata.u_qualname); } } + if (u->u_ste->ste_function_name != NULL) { + PyObject *tmp = base; + base = PyUnicode_FromFormat("%U.%U", + base, + u->u_ste->ste_function_name); + Py_DECREF(tmp); + if (base == NULL) { + return ERROR; + } + } + } + else if (u->u_ste->ste_function_name != NULL) { + base = Py_NewRef(u->u_ste->ste_function_name); } if (base != NULL) { @@ -627,7 +648,7 @@ _PyCompile_EnterScope(compiler *c, identifier name, int scope_type, } } if (u->u_ste->ste_has_conditional_annotations) { - /* Cook up an implicit __conditional__annotations__ cell */ + /* Cook up an implicit __conditional_annotations__ cell */ Py_ssize_t res; assert(u->u_scope_type == COMPILE_SCOPE_CLASS || u->u_scope_type == COMPILE_SCOPE_MODULE); res = _PyCompile_DictAddObj(u->u_metadata.u_cellvars, &_Py_ID(__conditional_annotations__)); @@ -765,6 +786,9 @@ _PyCompile_PushFBlock(compiler *c, location loc, f->fb_loc = loc; f->fb_exit = exit; f->fb_datum = datum; + if (t == COMPILE_FBLOCK_FINALLY_END) { + c->c_disable_warning++; + } return SUCCESS; } @@ -776,6 +800,9 @@ _PyCompile_PopFBlock(compiler *c, fblocktype t, jump_target_label block_label) u->u_nfblocks--; assert(u->u_fblock[u->u_nfblocks].fb_type == t); assert(SAME_JUMP_TARGET_LABEL(u->u_fblock[u->u_nfblocks].fb_block, block_label)); + if (t == COMPILE_FBLOCK_FINALLY_END) { + c->c_disable_warning--; + } } fblockinfo * @@ -787,6 +814,26 @@ _PyCompile_TopFBlock(compiler *c) return &c->u->u_fblock[c->u->u_nfblocks - 1]; } +bool +_PyCompile_InExceptionHandler(compiler *c) +{ + for (Py_ssize_t i = 0; i < c->u->u_nfblocks; i++) { + fblockinfo *block = &c->u->u_fblock[i]; + switch (block->fb_type) { + case COMPILE_FBLOCK_TRY_EXCEPT: + case COMPILE_FBLOCK_FINALLY_TRY: + case COMPILE_FBLOCK_FINALLY_END: + case COMPILE_FBLOCK_EXCEPTION_HANDLER: + case COMPILE_FBLOCK_EXCEPTION_GROUP_HANDLER: + case COMPILE_FBLOCK_HANDLER_CLEANUP: + return true; + default: + break; + } + } + return false; +} + void _PyCompile_DeferredAnnotations(compiler *c, PyObject **deferred_annotations, @@ -846,12 +893,15 @@ compiler_mod(compiler *c, mod_ty mod) { PyCodeObject *co = NULL; int addNone = mod->kind != Expression_kind; + assert(c->u == NULL); if (compiler_codegen(c, mod) < 0) { goto finally; } co = _PyCompile_OptimizeAndAssemble(c, addNone); finally: - _PyCompile_ExitScope(c); + if (c->u != NULL) { + _PyCompile_ExitScope(c); + } return co; } @@ -1066,18 +1116,22 @@ _PyCompile_TweakInlinedComprehensionScopes(compiler *c, location loc, assert(orig == NULL || orig == Py_True || orig == Py_False); if (orig != Py_True) { if (PyDict_SetItem(c->u->u_metadata.u_fasthidden, k, Py_True) < 0) { + Py_XDECREF(orig); return ERROR; } if (state->fast_hidden == NULL) { state->fast_hidden = PySet_New(NULL); if (state->fast_hidden == NULL) { + Py_XDECREF(orig); return ERROR; } } if (PySet_Add(state->fast_hidden, k) < 0) { + Py_XDECREF(orig); return ERROR; } } + Py_XDECREF(orig); } } } @@ -1203,6 +1257,9 @@ _PyCompile_Error(compiler *c, location loc, const char *format, ...) int _PyCompile_Warn(compiler *c, location loc, const char *format, ...) { + if (c->c_disable_warning) { + return 0; + } va_list vargs; va_start(vargs, format); PyObject *msg = PyUnicode_FromFormatV(format, vargs); @@ -1211,7 +1268,8 @@ _PyCompile_Warn(compiler *c, location loc, const char *format, ...) return ERROR; } int ret = _PyErr_EmitSyntaxWarning(msg, c->c_filename, loc.lineno, loc.col_offset + 1, - loc.end_lineno, loc.end_col_offset + 1); + loc.end_lineno, loc.end_col_offset + 1, + c->c_module); Py_DECREF(msg); return ret; } @@ -1426,7 +1484,7 @@ optimize_and_assemble_code_unit(struct compiler_unit *u, PyObject *const_cache, int stackdepth; int nlocalsplus; - if (_PyCfg_OptimizedCfgToInstructionSequence(g, &u->u_metadata, code_flags, + if (_PyCfg_OptimizedCfgToInstructionSequence(g, &u->u_metadata, &stackdepth, &nlocalsplus, &optimized_instrs) < 0) { goto error; @@ -1466,10 +1524,10 @@ _PyCompile_OptimizeAndAssemble(compiler *c, int addNone) PyCodeObject * _PyAST_Compile(mod_ty mod, PyObject *filename, PyCompilerFlags *pflags, - int optimize, PyArena *arena) + int optimize, PyArena *arena, PyObject *module) { assert(!PyErr_Occurred()); - compiler *c = new_compiler(mod, filename, pflags, optimize, arena); + compiler *c = new_compiler(mod, filename, pflags, optimize, arena, module); if (c == NULL) { return NULL; } @@ -1482,7 +1540,8 @@ _PyAST_Compile(mod_ty mod, PyObject *filename, PyCompilerFlags *pflags, int _PyCompile_AstPreprocess(mod_ty mod, PyObject *filename, PyCompilerFlags *cf, - int optimize, PyArena *arena, int no_const_folding) + int optimize, PyArena *arena, int no_const_folding, + PyObject *module) { _PyFutureFeatures future; if (!_PyFuture_FromAST(mod, filename, &future)) { @@ -1492,7 +1551,9 @@ _PyCompile_AstPreprocess(mod_ty mod, PyObject *filename, PyCompilerFlags *cf, if (optimize == -1) { optimize = _Py_GetConfig()->optimization_level; } - if (!_PyAST_Preprocess(mod, arena, filename, optimize, flags, no_const_folding)) { + if (!_PyAST_Preprocess(mod, arena, filename, optimize, flags, + no_const_folding, 0, module)) + { return -1; } return 0; @@ -1600,6 +1661,7 @@ _PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags, { PyObject *res = NULL; PyObject *metadata = NULL; + PyObject *consts_list = NULL; if (!PyAST_Check(ast)) { PyErr_SetString(PyExc_TypeError, "expected an AST"); @@ -1617,7 +1679,7 @@ _PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags, return NULL; } - compiler *c = new_compiler(mod, filename, pflags, optimize, arena); + compiler *c = new_compiler(mod, filename, pflags, optimize, arena, NULL); if (c == NULL) { _PyArena_Free(arena); return NULL; @@ -1654,12 +1716,23 @@ _PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags, } if (_PyInstructionSequence_ApplyLabelMap(_PyCompile_InstrSequence(c)) < 0) { - return NULL; + goto finally; } + + /* After AddReturnAtEnd: co_consts indices match the final instruction stream. */ + consts_list = consts_dict_keys_inorder(umd->u_consts); + if (consts_list == NULL) { + goto finally; + } + if (PyDict_SetItemString(metadata, "consts", consts_list) < 0) { + goto finally; + } + /* Allocate a copy of the instruction sequence on the heap */ - res = PyTuple_Pack(2, _PyCompile_InstrSequence(c), metadata); + res = _PyTuple_FromPair((PyObject *)_PyCompile_InstrSequence(c), metadata); finally: + Py_XDECREF(consts_list); Py_XDECREF(metadata); _PyCompile_ExitScope(c); compiler_free(c); @@ -1698,7 +1771,7 @@ _PyCompile_Assemble(_PyCompile_CodeUnitMetadata *umd, PyObject *filename, int code_flags = 0; int stackdepth, nlocalsplus; - if (_PyCfg_OptimizedCfgToInstructionSequence(g, umd, code_flags, + if (_PyCfg_OptimizedCfgToInstructionSequence(g, umd, &stackdepth, &nlocalsplus, &optimized_instrs) < 0) { goto error; diff --git a/Python/context.c b/Python/context.c index 9927cab915cae7..593e6ef90037cf 100644 --- a/Python/context.c +++ b/Python/context.c @@ -190,21 +190,25 @@ context_switched(PyThreadState *ts) } -static int +int _PyContext_Enter(PyThreadState *ts, PyObject *octx) { ENSURE_Context(octx, -1) PyContext *ctx = (PyContext *)octx; +#ifdef Py_GIL_DISABLED + int already_entered = _Py_atomic_exchange_int(&ctx->ctx_entered, 1); +#else + int already_entered = ctx->ctx_entered; + ctx->ctx_entered = 1; +#endif - if (ctx->ctx_entered) { + if (already_entered) { _PyErr_Format(ts, PyExc_RuntimeError, "cannot enter context: %R is already entered", ctx); return -1; } ctx->ctx_prev = (PyContext *)ts->context; /* borrow */ - ctx->ctx_entered = 1; - ts->context = Py_NewRef(ctx); context_switched(ts); return 0; @@ -220,13 +224,14 @@ PyContext_Enter(PyObject *octx) } -static int +int _PyContext_Exit(PyThreadState *ts, PyObject *octx) { ENSURE_Context(octx, -1) PyContext *ctx = (PyContext *)octx; + int already_entered = FT_ATOMIC_LOAD_INT_RELAXED(ctx->ctx_entered); - if (!ctx->ctx_entered) { + if (!already_entered) { PyErr_Format(PyExc_RuntimeError, "cannot exit context: %R has not been entered", ctx); return -1; @@ -243,7 +248,7 @@ _PyContext_Exit(PyThreadState *ts, PyObject *octx) Py_SETREF(ts->context, (PyObject *)ctx->ctx_prev); ctx->ctx_prev = NULL; - ctx->ctx_entered = 0; + FT_ATOMIC_STORE_INT(ctx->ctx_entered, 0); context_switched(ts); return 0; } @@ -343,12 +348,6 @@ PyContextVar_Set(PyObject *ovar, PyObject *val) ENSURE_ContextVar(ovar, NULL) PyContextVar *var = (PyContextVar *)ovar; - if (!PyContextVar_CheckExact(var)) { - PyErr_SetString( - PyExc_TypeError, "an instance of ContextVar was expected"); - return NULL; - } - PyContext *ctx = context_get(); if (ctx == NULL) { return NULL; @@ -619,6 +618,7 @@ context_tp_contains(PyObject *op, PyObject *key) /*[clinic input] +@permit_long_summary _contextvars.Context.get key: object default: object = None @@ -626,14 +626,14 @@ _contextvars.Context.get Return the value for `key` if `key` has the value in the context object. -If `key` does not exist, return `default`. If `default` is not given, -return None. +If `key` does not exist, return `default`. If `default` is not +given, return None. [clinic start generated code]*/ static PyObject * _contextvars_Context_get_impl(PyContext *self, PyObject *key, PyObject *default_value) -/*[clinic end generated code: output=0c54aa7664268189 input=c8eeb81505023995]*/ +/*[clinic end generated code: output=0c54aa7664268189 input=d669a0d56fabb0a5]*/ { if (context_check_key_type(key)) { return NULL; @@ -1013,23 +1013,19 @@ _contextvars.ContextVar.get Return a value for the context variable for the current context. -If there is no value for the variable in the current context, the method will: - * return the value of the default argument of the method, if provided; or - * return the default value for the context variable, if it was created - with one; or +If there is no value for the variable in the current context, the +method will: + * return the value of the default argument of the method, if + provided; or + * return the default value for the context variable, if it was + created with one; or * raise a LookupError. [clinic start generated code]*/ static PyObject * _contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value) -/*[clinic end generated code: output=0746bd0aa2ced7bf input=30aa2ab9e433e401]*/ +/*[clinic end generated code: output=0746bd0aa2ced7bf input=83814c6aef4a9fe3]*/ { - if (!PyContextVar_CheckExact(self)) { - PyErr_SetString( - PyExc_TypeError, "an instance of ContextVar was expected"); - return NULL; - } - PyObject *val; if (PyContextVar_Get((PyObject *)self, default_value, &val) < 0) { return NULL; @@ -1044,21 +1040,23 @@ _contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value) } /*[clinic input] +@permit_long_summary _contextvars.ContextVar.set value: object / Call to set a new value for the context variable in the current context. -The required value argument is the new value for the context variable. +The required value argument is the new value for the context +variable. -Returns a Token object that can be used to restore the variable to its previous -value via the `ContextVar.reset()` method. +Returns a Token object that can be used to restore the variable to +its previous value via the `ContextVar.reset()` method. [clinic start generated code]*/ static PyObject * _contextvars_ContextVar_set_impl(PyContextVar *self, PyObject *value) -/*[clinic end generated code: output=1b562d35cc79c806 input=c0a6887154227453]*/ +/*[clinic end generated code: output=1b562d35cc79c806 input=04ef8dcd810f5be6]*/ { return PyContextVar_Set((PyObject *)self, value); } @@ -1070,13 +1068,13 @@ _contextvars.ContextVar.reset Reset the context variable. -The variable is reset to the value it had before the `ContextVar.set()` that -created the token was used. +The variable is reset to the value it had before the +`ContextVar.set()` that created the token was used. [clinic start generated code]*/ static PyObject * _contextvars_ContextVar_reset_impl(PyContextVar *self, PyObject *token) -/*[clinic end generated code: output=3205d2bdff568521 input=ebe2881e5af4ffda]*/ +/*[clinic end generated code: output=3205d2bdff568521 input=dd33cfcb18c00e37]*/ { if (!PyContextToken_CheckExact(token)) { PyErr_Format(PyExc_TypeError, @@ -1102,7 +1100,8 @@ static PyMethodDef PyContextVar_methods[] = { _CONTEXTVARS_CONTEXTVAR_SET_METHODDEF _CONTEXTVARS_CONTEXTVAR_RESET_METHODDEF {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, + PyDoc_STR("ContextVars are generic over the type of their contained values")}, {NULL, NULL} }; @@ -1268,7 +1267,8 @@ token_exit_impl(PyContextToken *self, PyObject *type, PyObject *val, static PyMethodDef PyContextTokenType_methods[] = { {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + METH_O|METH_CLASS, + PyDoc_STR("Tokens are generic over the same type as the ContextVar which created them.")}, TOKEN_ENTER_METHODDEF TOKEN_EXIT_METHODDEF {NULL} @@ -1357,11 +1357,8 @@ get_token_missing(void) PyStatus _PyContext_Init(PyInterpreterState *interp) { - if (!_Py_IsMainInterpreter(interp)) { - return _PyStatus_OK(); - } - PyObject *missing = get_token_missing(); + assert(PyUnstable_IsImmortal(missing)); if (PyDict_SetItemString( _PyType_GetDict(&PyContextToken_Type), "MISSING", missing)) { diff --git a/Python/critical_section.c b/Python/critical_section.c index e628ba2f6d19bc..98e23eda7cdd77 100644 --- a/Python/critical_section.c +++ b/Python/critical_section.c @@ -1,7 +1,8 @@ #include "Python.h" -#include "pycore_lock.h" #include "pycore_critical_section.h" +#include "pycore_interp.h" +#include "pycore_lock.h" #ifdef Py_GIL_DISABLED static_assert(_Alignof(PyCriticalSection) >= 4, @@ -17,15 +18,36 @@ untag_critical_section(uintptr_t tag) #endif void -_PyCriticalSection_BeginSlow(PyCriticalSection *c, PyMutex *m) +_PyCriticalSection_BeginSlow(PyThreadState *tstate, PyCriticalSection *c, PyMutex *m) { #ifdef Py_GIL_DISABLED - PyThreadState *tstate = _PyThreadState_GET(); // As an optimisation for locking the same object recursively, skip // locking if the mutex is currently locked by the top-most critical // section. - if (tstate->critical_section && - untag_critical_section(tstate->critical_section)->_cs_mutex == m) { + // If the top-most critical section is a two-mutex critical section, + // then locking is skipped if either mutex is m. + if (tstate->critical_section) { + PyCriticalSection *prev = untag_critical_section(tstate->critical_section); + if (prev->_cs_mutex == m) { + c->_cs_mutex = NULL; + c->_cs_prev = 0; + return; + } + if (tstate->critical_section & _Py_CRITICAL_SECTION_TWO_MUTEXES) { + PyCriticalSection2 *prev2 = (PyCriticalSection2 *) + untag_critical_section(tstate->critical_section); + if (prev2->_cs_mutex2 == m) { + c->_cs_mutex = NULL; + c->_cs_prev = 0; + return; + } + } + } + // If the world is stopped, we don't need to acquire the lock because + // there are no other threads that could be accessing the object. + // Without this check, acquiring a critical section while the world is + // stopped could lead to a deadlock. + if (tstate->interp->stoptheworld.world_stopped) { c->_cs_mutex = NULL; c->_cs_prev = 0; return; @@ -40,11 +62,16 @@ _PyCriticalSection_BeginSlow(PyCriticalSection *c, PyMutex *m) } void -_PyCriticalSection2_BeginSlow(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2, +_PyCriticalSection2_BeginSlow(PyThreadState *tstate, PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2, int is_m1_locked) { #ifdef Py_GIL_DISABLED - PyThreadState *tstate = _PyThreadState_GET(); + if (tstate->interp->stoptheworld.world_stopped) { + c->_cs_base._cs_mutex = NULL; + c->_cs_mutex2 = NULL; + c->_cs_base._cs_prev = 0; + return; + } c->_cs_base._cs_mutex = NULL; c->_cs_mutex2 = NULL; c->_cs_base._cs_prev = tstate->critical_section; @@ -126,7 +153,7 @@ void PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op) { #ifdef Py_GIL_DISABLED - _PyCriticalSection_Begin(c, op); + _PyCriticalSection_Begin(_PyThreadState_GET(), c, op); #endif } @@ -135,7 +162,7 @@ void PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m) { #ifdef Py_GIL_DISABLED - _PyCriticalSection_BeginMutex(c, m); + _PyCriticalSection_BeginMutex(_PyThreadState_GET(), c, m); #endif } @@ -144,7 +171,7 @@ void PyCriticalSection_End(PyCriticalSection *c) { #ifdef Py_GIL_DISABLED - _PyCriticalSection_End(c); + _PyCriticalSection_End(_PyThreadState_GET(), c); #endif } @@ -153,7 +180,7 @@ void PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b) { #ifdef Py_GIL_DISABLED - _PyCriticalSection2_Begin(c, a, b); + _PyCriticalSection2_Begin(_PyThreadState_GET(), c, a, b); #endif } @@ -162,7 +189,7 @@ void PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2) { #ifdef Py_GIL_DISABLED - _PyCriticalSection2_BeginMutex(c, m1, m2); + _PyCriticalSection2_BeginMutex(_PyThreadState_GET(), c, m1, m2); #endif } @@ -171,6 +198,6 @@ void PyCriticalSection2_End(PyCriticalSection2 *c) { #ifdef Py_GIL_DISABLED - _PyCriticalSection2_End(c); + _PyCriticalSection2_End(_PyThreadState_GET(), c); #endif } diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 16a23f0351cd26..6b489bf03f86ec 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -568,6 +568,48 @@ _PyObject_GetXIData(PyThreadState *tstate, /* pickle C-API */ +/* Per-interpreter cache for pickle.dumps and pickle.loads. + * + * Each interpreter has its own cache in _PyXI_state_t.pickle, preserving + * interpreter isolation. The cache is populated lazily on first use and + * cleared during interpreter finalization in _Py_xi_state_fini(). + * + * Note: the cached references are captured at first use and not invalidated + * on module reload. This matches the caching pattern used elsewhere in + * CPython (e.g. arraymodule.c, _decimal.c). */ + +static PyObject * +_get_pickle_dumps(PyThreadState *tstate) +{ + _PyXI_state_t *state = _PyXI_GET_STATE(tstate->interp); + PyObject *dumps = state->pickle.dumps; + if (dumps != NULL) { + return dumps; + } + dumps = PyImport_ImportModuleAttrString("pickle", "dumps"); + if (dumps == NULL) { + return NULL; + } + state->pickle.dumps = dumps; // owns the reference + return dumps; +} + +static PyObject * +_get_pickle_loads(PyThreadState *tstate) +{ + _PyXI_state_t *state = _PyXI_GET_STATE(tstate->interp); + PyObject *loads = state->pickle.loads; + if (loads != NULL) { + return loads; + } + loads = PyImport_ImportModuleAttrString("pickle", "loads"); + if (loads == NULL) { + return NULL; + } + state->pickle.loads = loads; // owns the reference + return loads; +} + struct _pickle_context { PyThreadState *tstate; }; @@ -575,13 +617,12 @@ struct _pickle_context { static PyObject * _PyPickle_Dumps(struct _pickle_context *ctx, PyObject *obj) { - PyObject *dumps = PyImport_ImportModuleAttrString("pickle", "dumps"); + PyObject *dumps = _get_pickle_dumps(ctx->tstate); if (dumps == NULL) { return NULL; } - PyObject *bytes = PyObject_CallOneArg(dumps, obj); - Py_DECREF(dumps); - return bytes; + // dumps is a borrowed reference from the cache. + return PyObject_CallOneArg(dumps, obj); } @@ -609,6 +650,7 @@ check_missing___main___attr(PyObject *exc) // Get the error message. PyObject *args = PyException_GetArgs(exc); if (args == NULL || args == Py_None || PyObject_Size(args) < 1) { + Py_XDECREF(args); assert(!PyErr_Occurred()); return 0; } @@ -635,7 +677,8 @@ _PyPickle_Loads(struct _unpickle_context *ctx, PyObject *pickled) PyThreadState *tstate = ctx->tstate; PyObject *exc = NULL; - PyObject *loads = PyImport_ImportModuleAttrString("pickle", "loads"); + // loads is a borrowed reference from the per-interpreter cache. + PyObject *loads = _get_pickle_loads(tstate); if (loads == NULL) { return NULL; } @@ -681,7 +724,6 @@ _PyPickle_Loads(struct _unpickle_context *ctx, PyObject *pickled) // It might make sense to chain it (__context__). _PyErr_SetRaisedException(tstate, exc); } - Py_DECREF(loads); return obj; } @@ -1038,7 +1080,7 @@ _PyXIData_ReleaseAndRawFree(_PyXIData_t *xidata) /* convenience utilities */ /*************************/ -static const char * +static char * _copy_string_obj_raw(PyObject *strobj, Py_ssize_t *p_size) { Py_ssize_t size = -1; @@ -1102,12 +1144,12 @@ _convert_exc_to_TracebackException(PyObject *exc, PyObject **p_tbexc) } PyObject *tbexc = PyObject_Call(create, args, kwargs); - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(create); if (tbexc == NULL) { goto error; } + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(create); *p_tbexc = tbexc; return 0; @@ -1139,11 +1181,16 @@ _format_TracebackException(PyObject *tbexc) } Py_ssize_t size = -1; - const char *formatted = _copy_string_obj_raw(formatted_obj, &size); + char *formatted = _copy_string_obj_raw(formatted_obj, &size); Py_DECREF(formatted_obj); - // We remove trailing the newline added by TracebackException.format(). - assert(formatted[size-1] == '\n'); - ((char *)formatted)[size-1] = '\0'; + if (formatted == NULL || size == 0) { + return formatted; + } + assert(formatted[size] == '\0'); + // Remove a trailing newline if needed. + if (formatted[size-1] == '\n') { + formatted[size-1] = '\0'; + } return formatted; } @@ -1153,8 +1200,8 @@ _release_xid_data(_PyXIData_t *xidata, int rawfree) { PyObject *exc = PyErr_GetRaisedException(); int res = rawfree - ? _PyXIData_Release(xidata) - : _PyXIData_ReleaseAndRawFree(xidata); + ? _PyXIData_ReleaseAndRawFree(xidata) + : _PyXIData_Release(xidata); if (res < 0) { /* The owning interpreter is already destroyed. */ _PyXIData_Clear(NULL, xidata); @@ -1491,7 +1538,7 @@ _PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype) PyObject *formatted = _PyXI_excinfo_format(info); PyErr_SetObject(exctype, formatted); - Py_DECREF(formatted); + Py_XDECREF(formatted); if (tbexc != NULL) { PyObject *exc = PyErr_GetRaisedException(); @@ -1805,6 +1852,15 @@ _PyXI_InitFailureUTF8(_PyXI_failure *failure, int _PyXI_InitFailure(_PyXI_failure *failure, _PyXI_errcode code, PyObject *obj) { + *failure = (_PyXI_failure){ + .code = code, + .msg = NULL, + .msg_owned = 0, + }; + if (obj == NULL) { + return 0; + } + PyObject *msgobj = PyObject_Str(obj); if (msgobj == NULL) { return -1; @@ -1813,7 +1869,7 @@ _PyXI_InitFailure(_PyXI_failure *failure, _PyXI_errcode code, PyObject *obj) // That happens automatically in _capture_current_exception(). const char *msg = _copy_string_obj_raw(msgobj, NULL); Py_DECREF(msgobj); - if (PyErr_Occurred()) { + if (msg == NULL) { return -1; } *failure = (_PyXI_failure){ @@ -2950,7 +3006,7 @@ _pop_preserved(_PyXI_session *session, *p_xidata = NULL; } else { - _PyXI_namespace *xidata = _create_sharedns(session->_preserved); + xidata = _create_sharedns(session->_preserved); if (xidata == NULL) { failure.code = _PyXI_ERR_PRESERVE_FAILURE; goto error; @@ -3079,6 +3135,10 @@ _Py_xi_state_init(_PyXI_state_t *state, PyInterpreterState *interp) assert(state != NULL); assert(interp == NULL || state == _PyXI_GET_STATE(interp)); + // Initialize pickle function cache (before any fallible ops). + state->pickle.dumps = NULL; + state->pickle.loads = NULL; + xid_lookup_init(&state->data_lookup); // Initialize exceptions. @@ -3101,6 +3161,11 @@ _Py_xi_state_fini(_PyXI_state_t *state, PyInterpreterState *interp) assert(state != NULL); assert(interp == NULL || state == _PyXI_GET_STATE(interp)); + // Clear pickle function cache first: the cached functions may hold + // references to modules cleaned up by later finalization steps. + Py_CLEAR(state->pickle.dumps); + Py_CLEAR(state->pickle.loads); + fini_heap_exctypes(&state->exceptions); if (interp != NULL) { fini_static_exctypes(&state->exceptions, interp); @@ -3176,7 +3241,7 @@ _PyXI_InitTypes(PyInterpreterState *interp) "failed to initialize the cross-interpreter exception types"); } // We would initialize heap types here too but that leads to ref leaks. - // Instead, we intialize them in _PyXI_Init(). + // Instead, we initialize them in _PyXI_Init(). return _PyStatus_OK(); } diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index c3c76ae8d9a289..54422ad2335cb6 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -455,7 +455,7 @@ _PyBytes_GetXIDataWrapped(PyThreadState *tstate, return NULL; } if (size < sizeof(_PyBytes_data_t)) { - PyErr_Format(PyExc_ValueError, "expected size >= %d, got %d", + PyErr_Format(PyExc_ValueError, "expected size >= %zu, got %zu", sizeof(_PyBytes_data_t), size); return NULL; } @@ -657,6 +657,7 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, xidata_fallback_t fallback, shared->items = (_PyXIData_t **) PyMem_Calloc(shared->len, sizeof(_PyXIData_t *)); if (shared->items == NULL) { PyErr_NoMemory(); + PyMem_RawFree(shared); return -1; } diff --git a/Python/dtoa.c b/Python/dtoa.c index 3de150351a4ef8..89fadd33391cb4 100644 --- a/Python/dtoa.c +++ b/Python/dtoa.c @@ -139,8 +139,7 @@ #ifdef DOUBLE_IS_LITTLE_ENDIAN_IEEE754 # define IEEE_8087 #endif -#if defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) || \ - defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754) +#if defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) # define IEEE_MC68k #endif #if defined(IEEE_8087) + defined(IEEE_MC68k) != 1 @@ -149,8 +148,7 @@ /* The code below assumes that the endianness of integers matches the endianness of the two 32-bit words of a double. Check this. */ -#if defined(WORDS_BIGENDIAN) && (defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) || \ - defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754)) +#if defined(WORDS_BIGENDIAN) && defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) #error "doubles and ints have incompatible endianness" #endif diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index 583c9b752dfd90..0ff88ad330fd09 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -38,13 +38,17 @@ const char *_PyImport_DynLoadFiletab[] = { #ifdef __CYGWIN__ + "." SOABI ".dll", ".dll", #else /* !__CYGWIN__ */ "." SOABI ".so", #ifdef ALT_SOABI "." ALT_SOABI ".so", #endif +#ifndef Py_GIL_DISABLED ".abi" PYTHON_ABI_STRING ".so", +#endif /* Py_GIL_DISABLED */ + ".abi" PYTHON_ABI_STRING "t.so", ".so", #endif /* __CYGWIN__ */ NULL, diff --git a/Python/dynload_win.c b/Python/dynload_win.c index de9b0a77817a63..1c2544e94160ac 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -151,11 +151,16 @@ static char *GetPythonImport (HINSTANCE hModule) to this python DLL is loaded, not a python3.dll that might be on the path by chance. Return whether the DLL was found. + On free-threaded builds, PY3_DLLNAME is undefined and this is a no-op. + _Py_CheckPython3t will check for python3t.dll in that case. */ extern HMODULE PyWin_DLLhModule; static int _Py_CheckPython3(void) { +#ifndef PY3_DLLNAME + return 1; +#else static int python3_checked = 0; static HANDLE hPython3; #define MAXPATHLEN 512 @@ -169,10 +174,11 @@ _Py_CheckPython3(void) use that DLL */ if (PyWin_DLLhModule && GetModuleFileNameW(PyWin_DLLhModule, py3path, MAXPATHLEN)) { wchar_t *p = wcsrchr(py3path, L'\\'); + if (p) { - wcscpy(p + 1, PY3_DLLNAME); + wcscpy(p + 1, PY3_DLLNAME L".dll"); hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); - if (hPython3 != NULL) { + if (hPython3) { return 1; } } @@ -180,7 +186,7 @@ _Py_CheckPython3(void) /* If we can locate python3.dll in our application dir, use that DLL */ - hPython3 = LoadLibraryExW(PY3_DLLNAME, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR); + hPython3 = LoadLibraryExW(PY3_DLLNAME L".dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR); if (hPython3 != NULL) { return 1; } @@ -192,13 +198,78 @@ _Py_CheckPython3(void) assert(config->prefix); if (config->prefix) { wcscpy_s(py3path, MAXPATHLEN, config->prefix); - if (py3path[0] && _Py_add_relfile(py3path, L"DLLs\\" PY3_DLLNAME, MAXPATHLEN) >= 0) { + if (py3path[0] && _Py_add_relfile(py3path, L"DLLs\\" PY3_DLLNAME L".dll", MAXPATHLEN) >= 0) { hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); } } return hPython3 != NULL; #undef MAXPATHLEN +#endif /* PY3_DLLNAME */ +} + +/* To support extensions that can load with both abi3 and abi3t, we also need to + * preload python3t.dll. Due to 3.15 still supporting intermingled layouts, the + * check is a bit more complicated on that version as we need to try loading + * from a subdirectory first in case the adjacent python3t.dll is meant for + * python315t.dll (and we are python315.dll). + */ +static int +_Py_CheckPython3t(void) +{ +#ifndef ABI3T_DLLNAME + return 1; +#else +#if defined(PY3_DLLNAME) && PY_MAJOR_VERSION==3 && PY_MINOR_VERSION==15 + /* GIL-enabled builds of 3.15 might have a python3t.dll adjacent that is for + a free-threaded build. So we first check in a subdirectory in that case. + If it's not there, we'll look adjacent. */ + #define ABI3T_COMPAT_DLLNAME L"abi3t-compat\\" ABI3T_DLLNAME L".dll" +#endif + static int python3t_checked = 0; + static HANDLE hPython3t; + #define MAXPATHLEN 512 + wchar_t py3path[MAXPATHLEN+1]; + if (python3t_checked) { + return hPython3t != NULL; + } + python3t_checked = 1; + + /* If there is a python3t.dll [in the abi3t-compat dir] next to the + python3y.dll, use that DLL */ + if (PyWin_DLLhModule && GetModuleFileNameW(PyWin_DLLhModule, py3path, MAXPATHLEN)) { + wchar_t *p = wcsrchr(py3path, L'\\'); + + if (p) { +#ifdef ABI3T_COMPAT_DLLNAME + wcscpy(p + 1, ABI3T_COMPAT_DLLNAME); + hPython3t = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + if (hPython3t == NULL) +#endif + { + wcscpy(p + 1, ABI3T_DLLNAME L".dll"); + hPython3t = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + } + if (hPython3t) { + return 1; + } + } + } + + /* If we can locate python3.dll in our application dir, + use that DLL */ +#ifdef ABI3T_COMPAT_DLLNAME + hPython3t = LoadLibraryExW(ABI3T_COMPAT_DLLNAME, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR); + #undef ABI3T_COMPAT_DLLNAME + if (hPython3t == NULL) +#endif + { + hPython3t = LoadLibraryExW(ABI3T_DLLNAME L".dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR); + } + return hPython3t != NULL; + #undef MAXPATHLEN +#endif /* ABI3T_DLLNAME */ } + #endif /* Py_ENABLE_SHARED */ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, @@ -210,6 +281,7 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, #ifdef Py_ENABLE_SHARED _Py_CheckPython3(); + _Py_CheckPython3t(); #endif /* Py_ENABLE_SHARED */ wchar_t *wpathname = PyUnicode_AsWideCharString(pathname, NULL); diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index 75b98a047234d8..1833311ca74d9d 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -4,73 +4,57 @@ #include <Python.h> #include "pycore_runtime.h" // _PyRuntime -typedef int (*CountArgsFunc)(PyCFunctionWithKeywords func); +// We use the _PyRuntime.emscripten_trampoline field to store a function pointer +// for a wasm-gc based trampoline if it works. Otherwise fall back to JS +// trampoline. The JS trampoline breaks stack switching but every runtime that +// supports stack switching also supports wasm-gc. +// +// We'd like to make the trampoline call into a direct call but currently we +// need to import the wasmTable to compile trampolineModule. emcc >= 4.0.19 +// defines the table in WebAssembly and exports it so we won't have access to it +// until after the main module is compiled. +// +// To fix this, one natural solution would be to pass a funcref to the +// trampoline instead of a table index. Several PRs would be needed to fix +// things in llvm and emscripten in order to make this possible. +// +// The performance costs of an extra call_indirect aren't that large anyways. +// The JIT should notice that the target is always the same and turn into a +// check +// +// if (call_target != expected) deoptimize; +// direct_call(call_target, args); -// Offset of emscripten_count_args_function in _PyRuntimeState. There's a couple -// of alternatives: -// 1. Just make emscripten_count_args_function a real C global variable instead -// of a field of _PyRuntimeState. This would violate our rule against mutable +// Offset of emscripten_trampoline in _PyRuntimeState. There's a couple of +// alternatives: +// +// 1. Just make emscripten_trampoline a real C global variable instead of a +// field of _PyRuntimeState. This would violate our rule against mutable // globals. +// // 2. #define a preprocessor constant equal to a hard coded number and make a -// _Static_assert(offsetof(_PyRuntimeState, emscripten_count_args_function) -// == OURCONSTANT) This has the disadvantage that we have to update the hard -// coded constant when _PyRuntimeState changes +// _Static_assert(offsetof(_PyRuntimeState, emscripten_trampoline) == OURCONSTANT) +// This has the disadvantage that we have to update the hard coded constant +// when _PyRuntimeState changes // // So putting the mutable constant in _PyRuntime and using a immutable global to // record the offset so we can access it from JS is probably the best way. -EMSCRIPTEN_KEEPALIVE const int _PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET = offsetof(_PyRuntimeState, emscripten_count_args_function); +EMSCRIPTEN_KEEPALIVE const int _PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET = offsetof(_PyRuntimeState, emscripten_trampoline); -EM_JS(CountArgsFunc, _PyEM_GetCountArgsPtr, (), { - return Module._PyEM_CountArgsPtr; // initialized below -} -// Binary module for the checks. It has to be done in web assembly because -// clang/llvm have no support yet for the reference types yet. In fact, the wasm -// binary toolkit doesn't yet support the ref.test instruction either. To -// convert the following textual wasm to a binary, you can build wabt from this -// branch: https://github.com/WebAssembly/wabt/pull/2529 and then use that -// wat2wasm binary. -// -// (module -// (type $type0 (func (param) (result i32))) -// (type $type1 (func (param i32) (result i32))) -// (type $type2 (func (param i32 i32) (result i32))) -// (type $type3 (func (param i32 i32 i32) (result i32))) -// (type $blocktype (func (param) (result))) -// (table $funcs (import "e" "t") 0 funcref) -// (export "f" (func $f)) -// (func $f (param $fptr i32) (result i32) -// (local $fref funcref) -// local.get $fptr -// table.get $funcs -// local.tee $fref -// ref.test $type3 -// if $blocktype -// i32.const 3 -// return -// end -// local.get $fref -// ref.test $type2 -// if $blocktype -// i32.const 2 -// return -// end -// local.get $fref -// ref.test $type1 -// if $blocktype -// i32.const 1 -// return -// end -// local.get $fref -// ref.test $type0 -// if $blocktype -// i32.const 0 -// return -// end -// i32.const -1 -// ) -// ) +typedef PyObject* (*TrampolineFunc)(int* success, + PyCFunctionWithKeywords func, + PyObject* self, + PyObject* args, + PyObject* kw); -function getPyEMCountArgsPtr() { +/** + * Backwards compatible trampoline works with all JS runtimes + */ +EM_JS(PyObject*, _PyEM_TrampolineCall_JS, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { + return wasmTable.get(func)(arg1, arg2, arg3); +} +// Try to compile wasm-gc trampoline if possible. +function getPyEMTrampolinePtr() { // Starting with iOS 18.3.1, WebKit on iOS has an issue with the garbage // collector that breaks the call trampoline. See #130418 and // https://bugs.webkit.org/show_bug.cgi?id=293113 for details. @@ -84,135 +68,51 @@ function getPyEMCountArgsPtr() { if (isIOS) { return 0; } - - // Try to initialize countArgsFunc - const code = new Uint8Array([ - 0x00, 0x61, 0x73, 0x6d, // \0asm magic number - 0x01, 0x00, 0x00, 0x00, // version 1 - 0x01, 0x1a, // Type section, body is 0x1a bytes - 0x05, // 6 entries - 0x60, 0x00, 0x01, 0x7f, // (type $type0 (func (param) (result i32))) - 0x60, 0x01, 0x7f, 0x01, 0x7f, // (type $type1 (func (param i32) (result i32))) - 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, // (type $type2 (func (param i32 i32) (result i32))) - 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, // (type $type3 (func (param i32 i32 i32) (result i32))) - 0x60, 0x00, 0x00, // (type $blocktype (func (param) (result))) - 0x02, 0x09, // Import section, 0x9 byte body - 0x01, // 1 import (table $funcs (import "e" "t") 0 funcref) - 0x01, 0x65, // "e" - 0x01, 0x74, // "t" - 0x01, // importing a table - 0x70, // of entry type funcref - 0x00, 0x00, // table limits: no max, min of 0 - 0x03, 0x02, // Function section - 0x01, 0x01, // We're going to define one function of type 1 (func (param i32) (result i32)) - 0x07, 0x05, // export section - 0x01, // 1 export - 0x01, 0x66, // called "f" - 0x00, // a function - 0x00, // at index 0 - - 0x0a, 56, // Code section, - 0x01, 54, // one entry of length 54 - 0x01, 0x01, 0x70, // one local of type funcref - // Body of the function - 0x20, 0x00, // local.get $fptr - 0x25, 0x00, // table.get $funcs - 0x22, 0x01, // local.tee $fref - 0xfb, 0x14, 0x03, // ref.test $type3 - 0x04, 0x04, // if (type $blocktype) - 0x41, 0x03, // i32.const 3 - 0x0f, // return - 0x0b, // end block - - 0x20, 0x01, // local.get $fref - 0xfb, 0x14, 0x02, // ref.test $type2 - 0x04, 0x04, // if (type $blocktype) - 0x41, 0x02, // i32.const 2 - 0x0f, // return - 0x0b, // end block - - 0x20, 0x01, // local.get $fref - 0xfb, 0x14, 0x01, // ref.test $type1 - 0x04, 0x04, // if (type $blocktype) - 0x41, 0x01, // i32.const 1 - 0x0f, // return - 0x0b, // end block - - 0x20, 0x01, // local.get $fref - 0xfb, 0x14, 0x00, // ref.test $type0 - 0x04, 0x04, // if (type $blocktype) - 0x41, 0x00, // i32.const 0 - 0x0f, // return - 0x0b, // end block - - 0x41, 0x7f, // i32.const -1 - 0x0b // end function - ]); + let trampolineModule; try { - const mod = new WebAssembly.Module(code); - const inst = new WebAssembly.Instance(mod, { e: { t: wasmTable } }); - return addFunction(inst.exports.f); + trampolineModule = getWasmTrampolineModule(); } catch (e) { - // If something goes wrong, we'll null out _PyEM_CountFuncParams and fall - // back to the JS trampoline. + // Compilation error due to missing wasm-gc support, fall back to JS + // trampoline return 0; } + const trampolineInstance = new WebAssembly.Instance(trampolineModule, { + env: { __indirect_function_table: wasmTable, memory: wasmMemory }, + }); + return addFunction(trampolineInstance.exports.trampoline_call); } - -addOnPreRun(() => { - const ptr = getPyEMCountArgsPtr(); - Module._PyEM_CountArgsPtr = ptr; - const offset = HEAP32[__PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET / 4]; +// We have to be careful to work correctly with memory snapshots -- the value of +// _PyRuntimeState.emscripten_trampoline needs to reflect whether wasm-gc is +// available in the current runtime, not in the runtime the snapshot was taken +// in. This writes the appropriate value to +// _PyRuntimeState.emscripten_trampoline from JS startup code that runs every +// time, whether we are restoring a snapshot or not. +addOnPreRun(function setEmscriptenTrampoline() { + const ptr = getPyEMTrampolinePtr(); + const offset = HEAP32[__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET / 4]; HEAP32[(__PyRuntime + offset) / 4] = ptr; }); ); -void -_Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime) -{ - runtime->emscripten_count_args_function = _PyEM_GetCountArgsPtr(); -} - -// We have to be careful to work correctly with memory snapshots. Even if we are -// loading a memory snapshot, we need to perform the JS initialization work. -// That means we can't call the initialization code from C. Instead, we export -// this function pointer to JS and then fill it in a preRun function which runs -// unconditionally. -/** - * Backwards compatible trampoline works with all JS runtimes - */ -EM_JS(PyObject*, _PyEM_TrampolineCall_JS, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { - return wasmTable.get(func)(arg1, arg2, arg3); -}); - -typedef PyObject* (*zero_arg)(void); -typedef PyObject* (*one_arg)(PyObject*); -typedef PyObject* (*two_arg)(PyObject*, PyObject*); -typedef PyObject* (*three_arg)(PyObject*, PyObject*, PyObject*); - PyObject* _PyEM_TrampolineCall(PyCFunctionWithKeywords func, PyObject* self, PyObject* args, PyObject* kw) { - CountArgsFunc count_args = _PyRuntime.emscripten_count_args_function; - if (count_args == 0) { + TrampolineFunc trampoline = _PyRuntime.emscripten_trampoline; + if (trampoline == 0) { return _PyEM_TrampolineCall_JS(func, self, args, kw); } - switch (count_args(func)) { - case 0: - return ((zero_arg)func)(); - case 1: - return ((one_arg)func)(self); - case 2: - return ((two_arg)func)(self, args); - case 3: - return ((three_arg)func)(self, args, kw); - default: - PyErr_SetString(PyExc_SystemError, "Handler takes too many arguments"); - return NULL; + int success = 1; + PyObject *result = trampoline(&success, func, self, args, kw); + if (!success) { + PyErr_SetString(PyExc_SystemError, "Handler takes too many arguments"); } + return result; } +#else +// This is exported so we need to define it even when it isn't used +__attribute__((used)) const int _PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET = 0; #endif diff --git a/Python/emscripten_trampoline_inner.c b/Python/emscripten_trampoline_inner.c new file mode 100644 index 00000000000000..a2bad4857ed089 --- /dev/null +++ b/Python/emscripten_trampoline_inner.c @@ -0,0 +1,38 @@ +// This file must be compiled with -mgc to enable the extra wasm-gc +// instructions. It has to be compiled separately because not enough JS runtimes +// support wasm-gc yet. If the JS runtime does not support wasm-gc (or has buggy +// support like iOS), we will use the JS trampoline fallback. + +// We can't import Python.h here because it is compiled/linked with -nostdlib. +// We don't need to know what's inside PyObject* anyways. We could just call it +// void* everywhere. There are two reasons to do this: +// 1. to improve readability +// 2. eventually when we are comfortable requiring wasm-gc, we can merge this +// into emscripten_trampoline.c without worrying about it. +typedef void PyObject; + +typedef PyObject* (*three_arg)(PyObject*, PyObject*, PyObject*); +typedef PyObject* (*two_arg)(PyObject*, PyObject*); +typedef PyObject* (*one_arg)(PyObject*); +typedef PyObject* (*zero_arg)(void); + +#define TRY_RETURN_CALL(ty, args...) \ + if (__builtin_wasm_test_function_pointer_signature((ty)func)) { \ + return ((ty)func)(args); \ + } + +__attribute__((export_name("trampoline_call"))) PyObject* +trampoline_call(int* success, + void* func, + PyObject* self, + PyObject* args, + PyObject* kw) +{ + *success = 1; + TRY_RETURN_CALL(three_arg, self, args, kw); + TRY_RETURN_CALL(two_arg, self, args); + TRY_RETURN_CALL(one_arg, self); + TRY_RETURN_CALL(zero_arg); + *success = 0; + return 0; +} diff --git a/Python/errors.c b/Python/errors.c index a3122f76bdd87d..48b03e5fd714b1 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -246,13 +246,23 @@ PyErr_SetObject(PyObject *exception, PyObject *value) _PyErr_SetObject(tstate, exception, value); } -/* Set a key error with the specified argument, wrapping it in a - * tuple automatically so that tuple keys are not unpacked as the - * exception arguments. */ +/* Set a key error with the specified argument. This function should be used to + * raise a KeyError with an argument instead of PyErr_SetObject(PyExc_KeyError, + * arg) which has a special behavior. PyErr_SetObject() unpacks arg if it's a + * tuple, and it uses arg instead of creating a new exception if arg is an + * exception. + * + * If an exception is already set, override the exception. */ void _PyErr_SetKeyError(PyObject *arg) { PyThreadState *tstate = _PyThreadState_GET(); + + // PyObject_CallOneArg() must not be called with an exception set, + // otherwise _Py_CheckFunctionResult() can fail if the function returned + // a result with an excception set. + _PyErr_Clear(tstate); + PyObject *exc = PyObject_CallOneArg(PyExc_KeyError, arg); if (!exc) { /* caller will expect error to be set anyway */ @@ -1444,12 +1454,16 @@ make_unraisable_hook_args(PyThreadState *tstate, PyObject *exc_type, It can be called to log the exception of a custom sys.unraisablehook. - Do nothing if sys.stderr attribute doesn't exist or is set to None. */ + This assumes 'file' is neither NULL nor None. + */ static int write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type, PyObject *exc_value, PyObject *exc_tb, PyObject *err_msg, PyObject *obj, PyObject *file) { + assert(file != NULL); + assert(!Py_IsNone(file)); + if (obj != NULL && obj != Py_None) { if (err_msg != NULL && err_msg != Py_None) { if (PyFile_WriteObject(err_msg, file, Py_PRINT_RAW) < 0) { @@ -1484,6 +1498,27 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type, } } + // Try printing the exception using the stdlib module. + // If this fails, then we have to use the C implementation. + PyObject *print_exception_fn = PyImport_ImportModuleAttrString("traceback", + "_print_exception_bltin"); + if (print_exception_fn != NULL && PyCallable_Check(print_exception_fn)) { + PyObject *args[2] = {exc_value, file}; + PyObject *result = PyObject_Vectorcall(print_exception_fn, args, 2, NULL); + int ok = (result != NULL); + Py_DECREF(print_exception_fn); + Py_XDECREF(result); + if (ok) { + // Nothing else to do + return 0; + } + } + else { + Py_XDECREF(print_exception_fn); + } + // traceback module failed, fall back to pure C + _PyErr_Clear(tstate); + if (exc_tb != NULL && exc_tb != Py_None) { if (PyTraceBack_Print(exc_tb, file) < 0) { /* continue even if writing the traceback failed */ @@ -1631,6 +1666,7 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) _Py_EnsureTstateNotNULL(tstate); PyObject *err_msg = NULL; + PyObject *hook = NULL; PyObject *exc_type, *exc_value, *exc_tb; _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); @@ -1675,7 +1711,6 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) goto error; } - PyObject *hook; if (PySys_GetOptionalAttr(&_Py_ID(unraisablehook), &hook) < 0) { Py_DECREF(hook_args); err_msg_str = NULL; @@ -1688,7 +1723,6 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) } if (_PySys_Audit(tstate, "sys.unraisablehook", "OO", hook, hook_args) < 0) { - Py_DECREF(hook); Py_DECREF(hook_args); err_msg_str = "Exception ignored in audit hook"; obj = NULL; @@ -1696,13 +1730,11 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) } if (hook == Py_None) { - Py_DECREF(hook); Py_DECREF(hook_args); goto default_hook; } PyObject *res = PyObject_CallOneArg(hook, hook_args); - Py_DECREF(hook); Py_DECREF(hook_args); if (res != NULL) { Py_DECREF(res); @@ -1732,6 +1764,7 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) Py_XDECREF(exc_value); Py_XDECREF(exc_tb); Py_XDECREF(err_msg); + Py_XDECREF(hook); _PyErr_Clear(tstate); /* Just in case */ } @@ -1935,10 +1968,11 @@ _PyErr_RaiseSyntaxError(PyObject *msg, PyObject *filename, int lineno, int col_o */ int _PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int col_offset, - int end_lineno, int end_col_offset) + int end_lineno, int end_col_offset, + PyObject *module) { - if (_PyErr_WarnExplicitObjectWithContext(PyExc_SyntaxWarning, msg, - filename, lineno) < 0) + if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, filename, lineno, + module, NULL) < 0) { if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) { /* Replace the SyntaxWarning exception with a SyntaxError diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index e152865e4ec9e8..7973c75e1a60ad 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -8,49 +8,217 @@ #endif #define TIER_TWO 2 - case _NOP: { + case _NOP_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_PERIODIC: { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { + case _NOP_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _NOP_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _NOP_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_PERIODIC_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _CHECK_PERIODIC_AT_END is not a viable micro-op for tier 2 because it is replaced */ + + case _CHECK_PERIODIC_IF_NOT_YIELD_FROM_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + oparg = CURRENT_OPARG(); + if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); + int err = check_periodics(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); if (err != 0) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_PERIODIC_IF_NOT_YIELD_FROM: { - oparg = CURRENT_OPARG(); - if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_ERROR(); - } - } + /* _LOAD_BYTECODE is not a viable micro-op for tier 2 because it is replaced */ + + case _RESUME_CHECK_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + uintptr_t version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + assert((version & _PY_EVAL_EVENTS_MASK) == 0); + if (eval_breaker != version) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #endif + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _QUICKEN_RESUME is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + case _RESUME_CHECK_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + uintptr_t version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + assert((version & _PY_EVAL_EVENTS_MASK) == 0); + if (eval_breaker != version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + #endif + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - /* _LOAD_BYTECODE is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + case _RESUME_CHECK_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + uintptr_t version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + assert((version & _PY_EVAL_EVENTS_MASK) == 0); + if (eval_breaker != version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + #endif + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - case _RESUME_CHECK: { + case _RESUME_CHECK_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; #if defined(__EMSCRIPTEN__) if (_Py_emscripten_signal_clock == 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; @@ -60,21 +228,36 @@ assert((version & _PY_EVAL_EVENTS_MASK) == 0); if (eval_breaker != version) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } #ifdef Py_GIL_DISABLED if (frame->tlbc_index != ((_PyThreadStateImpl *)tstate)->tlbc_index) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } #endif + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } /* _MONITOR_RESUME is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ - case _LOAD_FAST_CHECK: { + case _LOAD_FAST_CHECK_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; oparg = CURRENT_OPARG(); _PyStackRef value_s = GETLOCAL(oparg); @@ -85,4996 +268,20109 @@ PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) ); stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } value = PyStackRef_DUP(value_s); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_FAST_CHECK_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + _PyStackRef value_s = GETLOCAL(oparg); + if (PyStackRef_IsNull(value_s)) { + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + value = PyStackRef_DUP(value_s); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_FAST_CHECK_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + _PyStackRef value_s = GETLOCAL(oparg); + if (PyStackRef_IsNull(value_s)) { + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + value = PyStackRef_DUP(value_s); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_0: { + case _LOAD_FAST_0_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; oparg = 0; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); value = PyStackRef_DUP(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_1: { + case _LOAD_FAST_0_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 1; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 0; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); value = PyStackRef_DUP(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_2: { + case _LOAD_FAST_0_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 2; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 0; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); value = PyStackRef_DUP(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_3: { + case _LOAD_FAST_1_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 3; + oparg = 1; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); value = PyStackRef_DUP(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_4: { + case _LOAD_FAST_1_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 4; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 1; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); value = PyStackRef_DUP(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_5: { + case _LOAD_FAST_1_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 5; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 1; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); value = PyStackRef_DUP(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_6: { + case _LOAD_FAST_2_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 6; + oparg = 2; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); value = PyStackRef_DUP(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_7: { + case _LOAD_FAST_2_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 7; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 2; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); value = PyStackRef_DUP(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST: { + case _LOAD_FAST_2_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = CURRENT_OPARG(); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 2; + assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); value = PyStackRef_DUP(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_BORROW_0: { + case _LOAD_FAST_3_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 0; + oparg = 3; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); - value = PyStackRef_Borrow(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_BORROW_1: { + case _LOAD_FAST_3_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 1; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 3; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); - value = PyStackRef_Borrow(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_BORROW_2: { + case _LOAD_FAST_3_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 2; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 3; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); - value = PyStackRef_Borrow(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_BORROW_3: { + case _LOAD_FAST_4_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 3; + oparg = 4; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); - value = PyStackRef_Borrow(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_BORROW_4: { + case _LOAD_FAST_4_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = 4; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); - value = PyStackRef_Borrow(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_BORROW_5: { + case _LOAD_FAST_4_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 5; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 4; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); - value = PyStackRef_Borrow(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_BORROW_6: { + case _LOAD_FAST_5_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 6; + oparg = 5; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); - value = PyStackRef_Borrow(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_BORROW_7: { + case _LOAD_FAST_5_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 7; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 5; assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); - value = PyStackRef_Borrow(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_BORROW: { + case _LOAD_FAST_5_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = CURRENT_OPARG(); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 5; + assert(oparg == CURRENT_OPARG()); assert(!PyStackRef_IsNull(GETLOCAL(oparg))); - value = PyStackRef_Borrow(GETLOCAL(oparg)); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FAST_AND_CLEAR: { + case _LOAD_FAST_6_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = CURRENT_OPARG(); - value = GETLOCAL(oparg); - GETLOCAL(oparg) = PyStackRef_NULL; - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + oparg = 6; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_CONST: { + case _LOAD_FAST_6_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = CURRENT_OPARG(); - PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); - value = PyStackRef_FromPyObjectBorrow(obj); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_SMALL_INT_0: { + case _LOAD_FAST_6_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 0; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 6; assert(oparg == CURRENT_OPARG()); - assert(oparg < _PY_NSMALLPOSINTS); - PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; - value = PyStackRef_FromPyObjectBorrow(obj); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_SMALL_INT_1: { + case _LOAD_FAST_7_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 1; + oparg = 7; assert(oparg == CURRENT_OPARG()); - assert(oparg < _PY_NSMALLPOSINTS); - PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; - value = PyStackRef_FromPyObjectBorrow(obj); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_SMALL_INT_2: { + case _LOAD_FAST_7_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 2; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 7; assert(oparg == CURRENT_OPARG()); - assert(oparg < _PY_NSMALLPOSINTS); - PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; - value = PyStackRef_FromPyObjectBorrow(obj); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_SMALL_INT_3: { + case _LOAD_FAST_7_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 3; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 7; assert(oparg == CURRENT_OPARG()); - assert(oparg < _PY_NSMALLPOSINTS); - PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; - value = PyStackRef_FromPyObjectBorrow(obj); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_SMALL_INT: { + case _LOAD_FAST_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; oparg = CURRENT_OPARG(); - assert(oparg < _PY_NSMALLPOSINTS); - PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; - value = PyStackRef_FromPyObjectBorrow(obj); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_FAST_0: { + case _LOAD_FAST_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 0; - assert(oparg == CURRENT_OPARG()); - value = stack_pointer[-1]; - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_FAST_1: { + case _LOAD_FAST_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 1; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_DUP(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_FAST_BORROW_0_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + oparg = 0; assert(oparg == CURRENT_OPARG()); - value = stack_pointer[-1]; - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_FAST_2: { + case _LOAD_FAST_BORROW_0_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 2; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 0; assert(oparg == CURRENT_OPARG()); - value = stack_pointer[-1]; - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_FAST_3: { + case _LOAD_FAST_BORROW_0_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 3; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 0; assert(oparg == CURRENT_OPARG()); - value = stack_pointer[-1]; - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_FAST_4: { + case _LOAD_FAST_BORROW_1_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 4; + oparg = 1; assert(oparg == CURRENT_OPARG()); - value = stack_pointer[-1]; - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_FAST_5: { + case _LOAD_FAST_BORROW_1_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 5; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 1; assert(oparg == CURRENT_OPARG()); - value = stack_pointer[-1]; - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_FAST_6: { + case _LOAD_FAST_BORROW_1_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 6; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 1; assert(oparg == CURRENT_OPARG()); - value = stack_pointer[-1]; - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_FAST_7: { + case _LOAD_FAST_BORROW_2_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = 7; + oparg = 2; assert(oparg == CURRENT_OPARG()); - value = stack_pointer[-1]; - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_FAST: { + case _LOAD_FAST_BORROW_2_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - oparg = CURRENT_OPARG(); - value = stack_pointer[-1]; - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 2; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_TOP: { + case _LOAD_FAST_BORROW_2_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 2; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_TOP_NOP: { + case _LOAD_FAST_BORROW_3_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - assert(PyStackRef_IsNull(value) || (!PyStackRef_RefcountOnObject(value)) || - _Py_IsImmortal((PyStackRef_AsPyObjectBorrow(value)))); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + oparg = 3; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_TOP_INT: { + case _LOAD_FAST_BORROW_3_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); - PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 3; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_TOP_FLOAT: { + case _LOAD_FAST_BORROW_3_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); - PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 3; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_TOP_UNICODE: { + case _LOAD_FAST_BORROW_4_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); - PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + oparg = 4; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_TWO: { - _PyStackRef tos; - _PyStackRef nos; - tos = stack_pointer[-1]; - nos = stack_pointer[-2]; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(tos); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(nos); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _LOAD_FAST_BORROW_4_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _PUSH_NULL: { - _PyStackRef res; - res = PyStackRef_NULL; - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_FAST_BORROW_4_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _END_FOR: { + case _LOAD_FAST_BORROW_5_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); + oparg = 5; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_ITER: { - _PyStackRef index_or_null; - _PyStackRef iter; - index_or_null = stack_pointer[-1]; - iter = stack_pointer[-2]; - (void)index_or_null; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(iter); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _LOAD_FAST_BORROW_5_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _END_SEND: { + case _LOAD_FAST_BORROW_5_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef receiver; - _PyStackRef val; - value = stack_pointer[-1]; - receiver = stack_pointer[-2]; - val = value; - stack_pointer[-2] = val; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(receiver); - stack_pointer = _PyFrame_GetStackPointer(frame); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _UNARY_NEGATIVE: { + case _LOAD_FAST_BORROW_6_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef res; - value = stack_pointer[-1]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyNumber_Negative(PyStackRef_AsPyObjectBorrow(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + oparg = 6; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _UNARY_NOT: { + case _LOAD_FAST_BORROW_6_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef res; - value = stack_pointer[-1]; - assert(PyStackRef_BoolCheck(value)); - res = PyStackRef_IsFalse(value) - ? PyStackRef_True : PyStackRef_False; - stack_pointer[-1] = res; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _TO_BOOL: { + case _LOAD_FAST_BORROW_6_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef res; - value = stack_pointer[-1]; - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyObject_IsTrue(PyStackRef_AsPyObjectBorrow(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - JUMP_TO_ERROR(); - } - res = err ? PyStackRef_True : PyStackRef_False; - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _TO_BOOL_BOOL: { + case _LOAD_FAST_BORROW_7_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - if (!PyStackRef_BoolCheck(value)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - STAT_INC(TO_BOOL, hit); + oparg = 7; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _TO_BOOL_INT: { + case _LOAD_FAST_BORROW_7_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef res; - value = stack_pointer[-1]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (!PyLong_CheckExact(value_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - STAT_INC(TO_BOOL, hit); - if (_PyLong_IsZero((PyLongObject *)value_o)) { - assert(_Py_IsImmortal(value_o)); - res = PyStackRef_False; - } - else { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_True; - stack_pointer += 1; - } - stack_pointer[-1] = res; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_NOS_LIST: { - _PyStackRef nos; - nos = stack_pointer[-2]; - PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyList_CheckExact(o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _LOAD_FAST_BORROW_7_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_TOS_LIST: { - _PyStackRef tos; - tos = stack_pointer[-1]; - PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyList_CheckExact(o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _LOAD_FAST_BORROW_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + oparg = CURRENT_OPARG(); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_TOS_SLICE: { - _PyStackRef tos; - tos = stack_pointer[-1]; - PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PySlice_Check(o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _LOAD_FAST_BORROW_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _TO_BOOL_LIST: { + case _LOAD_FAST_BORROW_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef res; - value = stack_pointer[-1]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - assert(PyList_CheckExact(value_o)); - STAT_INC(TO_BOOL, hit); - res = PyList_GET_SIZE(value_o) ? PyStackRef_True : PyStackRef_False; - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = value; - value = res; - stack_pointer[-1] = value; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + assert(!PyStackRef_IsNull(GETLOCAL(oparg))); + value = PyStackRef_Borrow(GETLOCAL(oparg)); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _TO_BOOL_NONE: { + case _LOAD_FAST_AND_CLEAR_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef res; - value = stack_pointer[-1]; - if (!PyStackRef_IsNone(value)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - STAT_INC(TO_BOOL, hit); - res = PyStackRef_False; - stack_pointer[-1] = res; + oparg = CURRENT_OPARG(); + value = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_NULL; + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_NOS_UNICODE: { - _PyStackRef nos; - nos = stack_pointer[-2]; - PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyUnicode_CheckExact(o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _LOAD_FAST_AND_CLEAR_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + value = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_NULL; + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_TOS_UNICODE: { + case _LOAD_FAST_AND_CLEAR_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (!PyUnicode_CheckExact(value_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + value = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_NULL; + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _TO_BOOL_STR: { + case _LOAD_CONST_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef res; - value = stack_pointer[-1]; - STAT_INC(TO_BOOL, hit); - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (value_o == &_Py_STR(empty)) { - assert(_Py_IsImmortal(value_o)); - res = PyStackRef_False; - } - else { - assert(Py_SIZE(value_o)); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_True; - stack_pointer += 1; - } - stack_pointer[-1] = res; + oparg = CURRENT_OPARG(); + PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _REPLACE_WITH_TRUE: { + case _LOAD_CONST_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef res; - value = stack_pointer[-1]; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_True; - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _UNARY_INVERT: { + case _LOAD_CONST_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef res; - value = stack_pointer[-1]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyNumber_Invert(PyStackRef_AsPyObjectBorrow(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg); + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_NOS_INT: { - _PyStackRef left; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - if (!_PyLong_CheckExactAndCompact(left_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _LOAD_SMALL_INT_0_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + oparg = 0; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_TOS_INT: { + case _LOAD_SMALL_INT_0_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (!_PyLong_CheckExactAndCompact(value_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 0; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_NOS_OVERFLOWED: { - _PyStackRef left; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - assert(Py_TYPE(left_o) == &PyLong_Type); - if (!_PyLong_IsCompact((PyLongObject *)left_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _LOAD_SMALL_INT_0_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 0; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_TOS_OVERFLOWED: { + case _LOAD_SMALL_INT_1_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - assert(Py_TYPE(value_o) == &PyLong_Type); - if (!_PyLong_IsCompact((PyLongObject *)value_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + oparg = 1; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_MULTIPLY_INT: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyLong_CheckExact(left_o)); - assert(PyLong_CheckExact(right_o)); - assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); - STAT_INC(BINARY_OP, hit); - res = _PyCompactLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); - if (PyStackRef_IsNull(res)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_SMALL_INT_1_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 1; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_ADD_INT: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyLong_CheckExact(left_o)); - assert(PyLong_CheckExact(right_o)); - assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); - STAT_INC(BINARY_OP, hit); - res = _PyCompactLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); - if (PyStackRef_IsNull(res)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_SMALL_INT_1_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 1; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBTRACT_INT: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyLong_CheckExact(left_o)); - assert(PyLong_CheckExact(right_o)); - assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); - STAT_INC(BINARY_OP, hit); - res = _PyCompactLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); - if (PyStackRef_IsNull(res)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_SMALL_INT_2_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + oparg = 2; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_NOS_FLOAT: { - _PyStackRef left; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - if (!PyFloat_CheckExact(left_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _LOAD_SMALL_INT_2_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 2; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_TOS_FLOAT: { + case _LOAD_SMALL_INT_2_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - value = stack_pointer[-1]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (!PyFloat_CheckExact(value_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 2; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_MULTIPLY_FLOAT: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval * - ((PyFloatObject *)right_o)->ob_fval; - res = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (PyStackRef_IsNull(res)) { - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_SMALL_INT_3_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + oparg = 3; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_ADD_FLOAT: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval + - ((PyFloatObject *)right_o)->ob_fval; - res = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (PyStackRef_IsNull(res)) { - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_SMALL_INT_3_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 3; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBTRACT_FLOAT: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval - - ((PyFloatObject *)right_o)->ob_fval; - res = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (PyStackRef_IsNull(res)) { - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_SMALL_INT_3_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 3; + assert(oparg == CURRENT_OPARG()); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_MULTIPLY_FLOAT__NO_DECREF_INPUTS: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval * - ((PyFloatObject *)right_o)->ob_fval; - res = PyStackRef_FromPyObjectSteal(PyFloat_FromDouble(dres)); - if (PyStackRef_IsNull(res)) { - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_SMALL_INT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + oparg = CURRENT_OPARG(); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_ADD_FLOAT__NO_DECREF_INPUTS: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval + - ((PyFloatObject *)right_o)->ob_fval; - res = PyStackRef_FromPyObjectSteal(PyFloat_FromDouble(dres)); - if (PyStackRef_IsNull(res)) { - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_SMALL_INT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBTRACT_FLOAT__NO_DECREF_INPUTS: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval - - ((PyFloatObject *)right_o)->ob_fval; - res = PyStackRef_FromPyObjectSteal(PyFloat_FromDouble(dres)); - if (PyStackRef_IsNull(res)) { - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - stack_pointer[-2] = res; + case _LOAD_SMALL_INT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + assert(oparg < _PY_NSMALLPOSINTS); + PyObject *obj = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + oparg]; + value = PyStackRef_FromPyObjectBorrow(obj); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_0_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + oparg = 0; + assert(oparg == CURRENT_OPARG()); + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_ADD_UNICODE: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyUnicode_CheckExact(left_o)); - assert(PyUnicode_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - if (res_o == NULL) { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[-2] = res; + case _SWAP_FAST_0_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 0; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_0; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_0_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 0; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_1; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache1 = trash; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_0_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 0; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_2; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache2 = trash; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_1_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + oparg = 1; + assert(oparg == CURRENT_OPARG()); + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_INPLACE_ADD_UNICODE: { - _PyStackRef right; - _PyStackRef left; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - assert(PyUnicode_CheckExact(left_o)); + case _SWAP_FAST_1_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 1; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_0; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_1_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 1; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_1; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache1 = trash; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_1_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 1; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_2; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache2 = trash; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_2_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + oparg = 2; + assert(oparg == CURRENT_OPARG()); + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_2_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 2; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_0; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_2_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 2; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_1; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache1 = trash; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_2_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 2; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_2; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache2 = trash; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_3_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + oparg = 3; + assert(oparg == CURRENT_OPARG()); + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_3_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 3; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_0; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_3_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 3; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_1; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache1 = trash; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_3_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 3; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_2; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache2 = trash; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_4_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_4_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_0; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_4_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_1; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache1 = trash; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_4_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_2; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache2 = trash; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_5_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_5_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_0; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_5_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_1; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache1 = trash; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_5_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_2; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache2 = trash; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_6_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_6_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_0; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_6_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_1; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache1 = trash; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_6_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_2; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache2 = trash; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_7_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_7_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_0; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_7_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_1; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache1 = trash; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_7_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + value = _stack_item_2; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache2 = trash; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + oparg = CURRENT_OPARG(); + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + value = _stack_item_0; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache0 = trash; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + value = _stack_item_1; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache1 = trash; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SWAP_FAST_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef trash; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + value = _stack_item_2; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + _tos_cache2 = trash; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_NOP_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + assert(PyStackRef_IsNull(value) || (!PyStackRef_RefcountOnObject(value)) || + _Py_IsImmortal((PyStackRef_AsPyObjectBorrow(value)))); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_NOP_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + assert(PyStackRef_IsNull(value) || (!PyStackRef_RefcountOnObject(value)) || + _Py_IsImmortal((PyStackRef_AsPyObjectBorrow(value)))); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_NOP_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + assert(PyStackRef_IsNull(value) || (!PyStackRef_RefcountOnObject(value)) || + _Py_IsImmortal((PyStackRef_AsPyObjectBorrow(value)))); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_NOP_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + assert(PyStackRef_IsNull(value) || (!PyStackRef_RefcountOnObject(value)) || + _Py_IsImmortal((PyStackRef_AsPyObjectBorrow(value)))); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_INT_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_INT_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_INT_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_INT_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_FLOAT_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_FLOAT_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_FLOAT_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_FLOAT_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_UNICODE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_UNICODE_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_UNICODE_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_UNICODE_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_TOP_OPARG_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef_CloseStack(args, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _PUSH_NULL_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef res; + res = PyStackRef_NULL; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _PUSH_NULL_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + res = PyStackRef_NULL; + _tos_cache1 = res; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _PUSH_NULL_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + res = PyStackRef_NULL; + _tos_cache2 = res; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _END_FOR_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_ITER_r20: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef index_or_null; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + index_or_null = _stack_item_1; + iter = _stack_item_0; + (void)index_or_null; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(iter); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _END_SEND_r31: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef index_or_null; + _PyStackRef receiver; + _PyStackRef val; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + index_or_null = _stack_item_1; + receiver = _stack_item_0; + val = value; + (void)index_or_null; + stack_pointer[0] = val; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(receiver); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = val; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNARY_NEGATIVE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyNumber_Negative(PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + v = value; + _tos_cache1 = v; + _tos_cache0 = res; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNARY_NEGATIVE_FLOAT_INPLACE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + value = stack_pointer[-1]; + PyObject *val_o = PyStackRef_AsPyObjectBorrow(value); + assert(PyFloat_CheckExact(val_o)); + assert(_PyObject_IsUniquelyReferenced(val_o)); + STAT_INC(UNARY_NEGATIVE, hit); + double dres = -((PyFloatObject *)val_o)->ob_fval; + ((PyFloatObject *)val_o)->ob_fval = dres; + res = value; + v = PyStackRef_NULL; + _tos_cache1 = v; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNARY_NEGATIVE_FLOAT_INPLACE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + PyObject *val_o = PyStackRef_AsPyObjectBorrow(value); + assert(PyFloat_CheckExact(val_o)); + assert(_PyObject_IsUniquelyReferenced(val_o)); + STAT_INC(UNARY_NEGATIVE, hit); + double dres = -((PyFloatObject *)val_o)->ob_fval; + ((PyFloatObject *)val_o)->ob_fval = dres; + res = value; + v = PyStackRef_NULL; + _tos_cache1 = v; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNARY_NEGATIVE_FLOAT_INPLACE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + PyObject *val_o = PyStackRef_AsPyObjectBorrow(value); + assert(PyFloat_CheckExact(val_o)); + assert(_PyObject_IsUniquelyReferenced(val_o)); + STAT_INC(UNARY_NEGATIVE, hit); + double dres = -((PyFloatObject *)val_o)->ob_fval; + ((PyFloatObject *)val_o)->ob_fval = dres; + res = value; + v = PyStackRef_NULL; + _tos_cache2 = v; + _tos_cache1 = res; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNARY_NOT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + value = stack_pointer[-1]; + assert(PyStackRef_BoolCheck(value)); + res = PyStackRef_IsFalse(value) + ? PyStackRef_True : PyStackRef_False; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNARY_NOT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + assert(PyStackRef_BoolCheck(value)); + res = PyStackRef_IsFalse(value) + ? PyStackRef_True : PyStackRef_False; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNARY_NOT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + assert(PyStackRef_BoolCheck(value)); + res = PyStackRef_IsFalse(value) + ? PyStackRef_True : PyStackRef_False; + _tos_cache1 = res; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNARY_NOT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + assert(PyStackRef_BoolCheck(value)); + res = PyStackRef_IsFalse(value) + ? PyStackRef_True : PyStackRef_False; + _tos_cache2 = res; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyObject_IsTrue(PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = err ? PyStackRef_True : PyStackRef_False; + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_BOOL_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + if (!PyStackRef_BoolCheck(value)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(TO_BOOL, hit); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_BOOL_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + if (!PyStackRef_BoolCheck(value)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(TO_BOOL, hit); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_BOOL_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + if (!PyStackRef_BoolCheck(value)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(TO_BOOL, hit); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_BOOL_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + if (!PyStackRef_BoolCheck(value)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(TO_BOOL, hit); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_INT_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + value = stack_pointer[-1]; + STAT_INC(TO_BOOL, hit); + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + res = (_PyLong_IsZero((PyLongObject *)value_o)) ? PyStackRef_False : PyStackRef_True; + v = value; + _tos_cache1 = v; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_INT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + STAT_INC(TO_BOOL, hit); + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + res = (_PyLong_IsZero((PyLongObject *)value_o)) ? PyStackRef_False : PyStackRef_True; + v = value; + _tos_cache1 = v; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_INT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + STAT_INC(TO_BOOL, hit); + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + res = (_PyLong_IsZero((PyLongObject *)value_o)) ? PyStackRef_False : PyStackRef_True; + v = value; + _tos_cache2 = v; + _tos_cache1 = res; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_LIST_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyList_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_LIST_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + nos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyList_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_LIST_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + nos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyList_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_LIST_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + nos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyList_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_LIST_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyList_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_LIST_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + tos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyList_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_LIST_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + tos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyList_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_LIST_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + tos = _stack_item_2; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyList_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_SLICE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PySlice_Check(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_SLICE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + tos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PySlice_Check(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_SLICE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + tos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PySlice_Check(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_SLICE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + tos = _stack_item_2; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PySlice_Check(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_LIST_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + assert(PyList_CheckExact(value_o)); + STAT_INC(TO_BOOL, hit); + res = PyList_GET_SIZE(value_o) ? PyStackRef_True : PyStackRef_False; + v = value; + _tos_cache1 = v; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_LIST_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + assert(PyList_CheckExact(value_o)); + STAT_INC(TO_BOOL, hit); + res = PyList_GET_SIZE(value_o) ? PyStackRef_True : PyStackRef_False; + v = value; + _tos_cache1 = v; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_LIST_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + assert(PyList_CheckExact(value_o)); + STAT_INC(TO_BOOL, hit); + res = PyList_GET_SIZE(value_o) ? PyStackRef_True : PyStackRef_False; + v = value; + _tos_cache2 = v; + _tos_cache1 = res; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_NONE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + value = stack_pointer[-1]; + if (!PyStackRef_IsNone(value)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(TO_BOOL, hit); + res = PyStackRef_False; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_NONE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + if (!PyStackRef_IsNone(value)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(TO_BOOL, hit); + res = PyStackRef_False; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_NONE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + if (!PyStackRef_IsNone(value)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(TO_BOOL, hit); + res = PyStackRef_False; + _tos_cache1 = res; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_NONE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + if (!PyStackRef_IsNone(value)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(TO_BOOL, hit); + res = PyStackRef_False; + _tos_cache2 = res; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_COMPACT_ASCII_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + if (!PyUnicode_IS_COMPACT_ASCII(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_COMPACT_ASCII_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + nos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + if (!PyUnicode_IS_COMPACT_ASCII(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_COMPACT_ASCII_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + nos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + if (!PyUnicode_IS_COMPACT_ASCII(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_COMPACT_ASCII_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + nos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + if (!PyUnicode_IS_COMPACT_ASCII(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_UNICODE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_UNICODE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + nos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_UNICODE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + nos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_UNICODE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + nos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_UNICODE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyUnicode_CheckExact(value_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_UNICODE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyUnicode_CheckExact(value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_UNICODE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyUnicode_CheckExact(value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_UNICODE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyUnicode_CheckExact(value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_STR_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + value = stack_pointer[-1]; + STAT_INC(TO_BOOL, hit); + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + res = value_o == &_Py_STR(empty) ? PyStackRef_False : PyStackRef_True; + v = value; + _tos_cache1 = v; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_STR_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + STAT_INC(TO_BOOL, hit); + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + res = value_o == &_Py_STR(empty) ? PyStackRef_False : PyStackRef_True; + v = value; + _tos_cache1 = v; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _TO_BOOL_STR_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + STAT_INC(TO_BOOL, hit); + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + res = value_o == &_Py_STR(empty) ? PyStackRef_False : PyStackRef_True; + v = value; + _tos_cache2 = v; + _tos_cache1 = res; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _REPLACE_WITH_TRUE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + value = stack_pointer[-1]; + res = PyStackRef_True; + v = value; + _tos_cache1 = v; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _REPLACE_WITH_TRUE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + res = PyStackRef_True; + v = value; + _tos_cache1 = v; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _REPLACE_WITH_TRUE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + res = PyStackRef_True; + v = value; + _tos_cache2 = v; + _tos_cache1 = res; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNARY_INVERT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyNumber_Invert(PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + v = value; + _tos_cache1 = v; + _tos_cache0 = res; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_INT_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!_PyLong_CheckExactAndCompact(left_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_INT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!_PyLong_CheckExactAndCompact(left_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_INT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!_PyLong_CheckExactAndCompact(left_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_INT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + left = _stack_item_1; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!_PyLong_CheckExactAndCompact(left_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = left; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = left; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_INT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_INT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_INT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_INT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_OVERFLOWED_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + assert(Py_TYPE(left_o) == &PyLong_Type); + if (!_PyLong_IsCompact((PyLongObject *)left_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_OVERFLOWED_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + assert(Py_TYPE(left_o) == &PyLong_Type); + if (!_PyLong_IsCompact((PyLongObject *)left_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_OVERFLOWED_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + assert(Py_TYPE(left_o) == &PyLong_Type); + if (!_PyLong_IsCompact((PyLongObject *)left_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_OVERFLOWED_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + left = _stack_item_1; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + assert(Py_TYPE(left_o) == &PyLong_Type); + if (!_PyLong_IsCompact((PyLongObject *)left_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = left; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = left; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_OVERFLOWED_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + assert(Py_TYPE(value_o) == &PyLong_Type); + if (!_PyLong_IsCompact((PyLongObject *)value_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_OVERFLOWED_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + assert(Py_TYPE(value_o) == &PyLong_Type); + if (!_PyLong_IsCompact((PyLongObject *)value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_OVERFLOWED_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + assert(Py_TYPE(value_o) == &PyLong_Type); + if (!_PyLong_IsCompact((PyLongObject *)value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_OVERFLOWED_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + assert(Py_TYPE(value_o) == &PyLong_Type); + if (!_PyLong_IsCompact((PyLongObject *)value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = right; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = right; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = right; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyLong_CheckExact(left_o)); + assert(PyLong_CheckExact(right_o)); + assert(_PyLong_BothAreCompact((PyLongObject *)left_o, (PyLongObject *)right_o)); + STAT_INC(BINARY_OP, hit); + res = _PyCompactLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); + if (PyStackRef_IsNull(res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_INPLACE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + INT_INPLACE_OP(left, right, left, +, _PyCompactLong_Add); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_INPLACE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + INT_INPLACE_OP(left, right, left, +, _PyCompactLong_Add); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = right; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_INPLACE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + INT_INPLACE_OP(left, right, left, +, _PyCompactLong_Add); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_INPLACE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + INT_INPLACE_OP(left, right, left, -, _PyCompactLong_Subtract); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_INPLACE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + INT_INPLACE_OP(left, right, left, -, _PyCompactLong_Subtract); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = right; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_INPLACE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + INT_INPLACE_OP(left, right, left, -, _PyCompactLong_Subtract); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_INPLACE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + INT_INPLACE_OP(left, right, left, *, _PyCompactLong_Multiply); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_INPLACE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + INT_INPLACE_OP(left, right, left, *, _PyCompactLong_Multiply); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = right; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_INPLACE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + INT_INPLACE_OP(left, right, left, *, _PyCompactLong_Multiply); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_INPLACE_RIGHT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + INT_INPLACE_OP(left, right, right, +, _PyCompactLong_Add); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_INPLACE_RIGHT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + INT_INPLACE_OP(left, right, right, +, _PyCompactLong_Add); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = right; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_INPLACE_RIGHT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + INT_INPLACE_OP(left, right, right, +, _PyCompactLong_Add); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + INT_INPLACE_OP(left, right, right, -, _PyCompactLong_Subtract); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + INT_INPLACE_OP(left, right, right, -, _PyCompactLong_Subtract); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = right; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + INT_INPLACE_OP(left, right, right, -, _PyCompactLong_Subtract); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + INT_INPLACE_OP(left, right, right, *, _PyCompactLong_Multiply); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + INT_INPLACE_OP(left, right, right, *, _PyCompactLong_Multiply); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = right; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + INT_INPLACE_OP(left, right, right, *, _PyCompactLong_Multiply); + if (PyStackRef_IsNull(_int_inplace_res)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + res = _int_inplace_res; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_FLOAT_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!PyFloat_CheckExact(left_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_FLOAT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!PyFloat_CheckExact(left_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_FLOAT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!PyFloat_CheckExact(left_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_FLOAT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + left = _stack_item_1; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!PyFloat_CheckExact(left_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = left; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = left; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FLOAT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyFloat_CheckExact(value_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FLOAT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyFloat_CheckExact(value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FLOAT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyFloat_CheckExact(value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FLOAT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyFloat_CheckExact(value_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_FLOAT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval * + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_FLOAT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval * + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + stack_pointer[0] = right; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_FLOAT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval * + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_FLOAT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval + + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_FLOAT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval + + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + stack_pointer[0] = right; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_FLOAT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval + + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_FLOAT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval - + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_FLOAT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval - + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + stack_pointer[0] = right; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_FLOAT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval - + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_FLOAT_INPLACE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + FLOAT_INPLACE_OP(left, right, left, +); + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_FLOAT_INPLACE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + FLOAT_INPLACE_OP(left, right, left, +); + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_FLOAT_INPLACE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + FLOAT_INPLACE_OP(left, right, left, +); + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_FLOAT_INPLACE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + FLOAT_INPLACE_OP(left, right, left, -); + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_FLOAT_INPLACE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + FLOAT_INPLACE_OP(left, right, left, -); + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_FLOAT_INPLACE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + FLOAT_INPLACE_OP(left, right, left, -); + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_FLOAT_INPLACE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + FLOAT_INPLACE_OP(left, right, left, *); + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_FLOAT_INPLACE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + FLOAT_INPLACE_OP(left, right, left, *); + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_FLOAT_INPLACE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + FLOAT_INPLACE_OP(left, right, left, *); + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + FLOAT_INPLACE_OP(left, right, right, +); + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + FLOAT_INPLACE_OP(left, right, right, +); + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_FLOAT_INPLACE_RIGHT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + FLOAT_INPLACE_OP(left, right, right, +); + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + FLOAT_INPLACE_OP(left, right, right, *); + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + FLOAT_INPLACE_OP(left, right, right, *); + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + FLOAT_INPLACE_OP(left, right, right, *); + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + FLOAT_INPLACE_OP(left, right, right, -); + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + FLOAT_INPLACE_OP(left, right, right, -); + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + FLOAT_INPLACE_OP(left, right, right, -); + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_TRUEDIV_FLOAT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double divisor = ((PyFloatObject *)right_o)->ob_fval; + if (divisor == 0.0) { + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyErr_SetString(PyExc_ZeroDivisionError, + "float division by zero"); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + double dres = ((PyFloatObject *)left_o)->ob_fval / divisor; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + FLOAT_INPLACE_DIVOP(left, right, left); + if (_divop_err) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + FLOAT_INPLACE_DIVOP(left, right, left); + if (_divop_err) { + stack_pointer[0] = right; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + FLOAT_INPLACE_DIVOP(left, right, left); + if (_divop_err) { + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = left; + l = PyStackRef_NULL; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + FLOAT_INPLACE_DIVOP(left, right, right); + if (_divop_err) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + FLOAT_INPLACE_DIVOP(left, right, right); + if (_divop_err) { + stack_pointer[0] = right; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + FLOAT_INPLACE_DIVOP(left, right, right); + if (_divop_err) { + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = right; + l = left; + r = PyStackRef_NULL; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_UNICODE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + PyObject *res_o = PyUnicode_Concat(left_o, right_o); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_UNICODE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + PyObject *res_o = PyUnicode_Concat(left_o, right_o); + if (res_o == NULL) { + stack_pointer[0] = right; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_UNICODE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyUnicode_CheckExact(left_o)); + assert(PyUnicode_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + PyObject *res_o = PyUnicode_Concat(left_o, right_o); + if (res_o == NULL) { + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_INPLACE_ADD_UNICODE_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + assert(PyUnicode_CheckExact(left_o)); assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(right))); int next_oparg; #if TIER_ONE assert(next_instr->op.code == STORE_FAST); next_oparg = next_instr->op.arg; #else - next_oparg = CURRENT_OPERAND0(); + next_oparg = (int)CURRENT_OPERAND0_16(); + #endif + _PyStackRef *target_local = &GETLOCAL(next_oparg); + assert(PyUnicode_CheckExact(left_o)); + if (PyStackRef_AsPyObjectBorrow(*target_local) != left_o) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(BINARY_OP, hit); + assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left)); + PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); + PyObject *right_o = PyStackRef_AsPyObjectSteal(right); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyUnicode_Append(&temp, right_o); + _Py_DECREF_SPECIALIZED(right_o, _PyUnicode_ExactDealloc); + stack_pointer = _PyFrame_GetStackPointer(frame); + *target_local = PyStackRef_NULL; + if (temp == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(temp); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_EXTEND_LHS_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + left = stack_pointer[-2]; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard == NULL && d->lhs_type != NULL); + if (Py_TYPE(left_o) != d->lhs_type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_EXTEND_LHS_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + left = stack_pointer[-1]; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard == NULL && d->lhs_type != NULL); + if (Py_TYPE(left_o) != d->lhs_type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_EXTEND_LHS_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + left = _stack_item_0; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard == NULL && d->lhs_type != NULL); + if (Py_TYPE(left_o) != d->lhs_type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_EXTEND_LHS_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + left = _stack_item_1; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard == NULL && d->lhs_type != NULL); + if (Py_TYPE(left_o) != d->lhs_type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = left; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = left; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_EXTEND_RHS_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + right = stack_pointer[-1]; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard == NULL && d->rhs_type != NULL); + if (Py_TYPE(right_o) != d->rhs_type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = right; + _tos_cache0 = stack_pointer[-2]; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_EXTEND_RHS_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef _stack_item_0 = _tos_cache0; + right = _stack_item_0; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard == NULL && d->rhs_type != NULL); + if (Py_TYPE(right_o) != d->rhs_type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = right; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = right; + _tos_cache0 = stack_pointer[-1]; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_EXTEND_RHS_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard == NULL && d->rhs_type != NULL); + if (Py_TYPE(right_o) != d->rhs_type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = right; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_EXTEND_RHS_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + right = _stack_item_2; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d && d->guard == NULL && d->rhs_type != NULL); + if (Py_TYPE(right_o) != d->rhs_type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = right; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = right; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_EXTEND_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + assert(d != NULL); + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int match = (d->guard != NULL) + ? d->guard(left_o, right_o) + : (Py_TYPE(left_o) == d->lhs_type && Py_TYPE(right_o) == d->rhs_type); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (!match) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = right; + _tos_cache0 = left; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_EXTEND_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; + STAT_INC(BINARY_OP, hit); + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = d->action(left_o, right_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + assert(d->result_type == NULL || Py_TYPE(res_o) == d->result_type); + assert(!d->result_unique || Py_REFCNT(res_o) == 1 || _Py_IsImmortal(res_o)); + assert(!PyFloat_CheckExact(res_o) || Py_REFCNT(res_o) == 1); + res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_SLICE_r31: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef stop; + _PyStackRef start; + _PyStackRef container; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + stop = _stack_item_2; + start = _stack_item_1; + container = _stack_item_0; + PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); + PyObject *start_o = PyStackRef_AsPyObjectBorrow(start); + PyObject *stop_o = PyStackRef_AsPyObjectBorrow(stop); + PyObject *res_o; + if (PyList_CheckExact(container_o)) { + stack_pointer[0] = container; + stack_pointer[1] = start; + stack_pointer[2] = stop; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyList_BinarySlice(container_o, start_o, stop_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else if (PyTuple_CheckExact(container_o)) { + stack_pointer[0] = container; + stack_pointer[1] = start; + stack_pointer[2] = stop; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyTuple_BinarySlice(container_o, start_o, stop_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else if (PyUnicode_CheckExact(container_o)) { + stack_pointer[0] = container; + stack_pointer[1] = start; + stack_pointer[2] = stop; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyUnicode_BinarySlice(container_o, start_o, stop_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + PyObject *slice = PySlice_New(start_o, stop_o, NULL); + if (slice == NULL) { + res_o = NULL; + } + else { + stack_pointer[0] = container; + stack_pointer[1] = start; + stack_pointer[2] = stop; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyObject_GetItem(container_o, slice); + Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + } + stack_pointer += 3; + } + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = stop; + stop = PyStackRef_NULL; + stack_pointer[-3] = container; + stack_pointer[-2] = start; + stack_pointer[-1] = stop; + PyStackRef_CLOSE(tmp); + tmp = start; + start = PyStackRef_NULL; + stack_pointer[-2] = start; + PyStackRef_CLOSE(tmp); + tmp = container; + container = PyStackRef_NULL; + stack_pointer[-3] = container; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_SLICE_r30: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef stop; + _PyStackRef start; + _PyStackRef container; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + stop = _stack_item_2; + start = _stack_item_1; + container = _stack_item_0; + v = stack_pointer[-1]; + stack_pointer[0] = container; + stack_pointer[1] = start; + stack_pointer[2] = stop; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), + PyStackRef_AsPyObjectSteal(stop), + Py_None); + stack_pointer = _PyFrame_GetStackPointer(frame); + int err; + if (slice == NULL) { + err = 1; + } + else { + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), slice, PyStackRef_AsPyObjectBorrow(v)); + Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += 2; + } + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = container; + container = PyStackRef_NULL; + stack_pointer[-3] = container; + PyStackRef_CLOSE(tmp); + tmp = v; + v = PyStackRef_NULL; + stack_pointer[-4] = v; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -4; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (err) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_LIST_INT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef list_st; + _PyStackRef res; + _PyStackRef ls; + _PyStackRef ss; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + sub_st = _stack_item_1; + list_st = _stack_item_0; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); + assert(PyLong_CheckExact(sub)); + assert(PyList_CheckExact(list)); + Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub); + if (index < 0) { + index += PyList_GET_SIZE(list); + } + #ifdef Py_GIL_DISABLED + stack_pointer[0] = list_st; + stack_pointer[1] = sub_st; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyList_GetItemRef((PyListObject*)list, index); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = sub_st; + _tos_cache0 = list_st; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + #else + if (index < 0 || index >= PyList_GET_SIZE(list)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = sub_st; + _tos_cache0 = list_st; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + PyObject *res_o = PyList_GET_ITEM(list, index); + assert(res_o != NULL); + res = PyStackRef_FromPyObjectNew(res_o); + stack_pointer += 2; + #endif + STAT_INC(BINARY_OP, hit); + ls = list_st; + ss = sub_st; + _tos_cache2 = ss; + _tos_cache1 = ls; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_LIST_SLICE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef list_st; + _PyStackRef res; + _PyStackRef ls; + _PyStackRef ss; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + sub_st = _stack_item_1; + list_st = _stack_item_0; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); + assert(PySlice_Check(sub)); + assert(PyList_CheckExact(list)); + stack_pointer[0] = list_st; + stack_pointer[1] = sub_st; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyList_SliceSubscript(list, sub); + stack_pointer = _PyFrame_GetStackPointer(frame); + STAT_INC(BINARY_OP, hit); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + ls = list_st; + ss = sub_st; + _tos_cache2 = ss; + _tos_cache1 = ls; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_STR_INT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef str_st; + _PyStackRef res; + _PyStackRef s; + _PyStackRef i; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + sub_st = _stack_item_1; + str_st = _stack_item_0; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); + assert(PyLong_CheckExact(sub)); + assert(PyUnicode_CheckExact(str)); + if (!_PyLong_IsNonNegativeCompact((PyLongObject*)sub)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = sub_st; + _tos_cache0 = str_st; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + if (PyUnicode_GET_LENGTH(str) <= index) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = sub_st; + _tos_cache0 = str_st; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + uint8_t c = PyUnicode_1BYTE_DATA(str)[index]; + assert(c < 128); + STAT_INC(BINARY_OP, hit); + PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; + s = str_st; + i = sub_st; + res = PyStackRef_FromPyObjectBorrow(res_o); + _tos_cache2 = i; + _tos_cache1 = s; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_USTR_INT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef str_st; + _PyStackRef res; + _PyStackRef s; + _PyStackRef i; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + sub_st = _stack_item_1; + str_st = _stack_item_0; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); + assert(PyLong_CheckExact(sub)); + assert(PyUnicode_CheckExact(str)); + if (!_PyLong_IsNonNegativeCompact((PyLongObject*)sub)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = sub_st; + _tos_cache0 = str_st; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + if (PyUnicode_GET_LENGTH(str) <= index) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = sub_st; + _tos_cache0 = str_st; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + Py_UCS4 c = PyUnicode_READ_CHAR(str, index); + if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = sub_st; + _tos_cache0 = str_st; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(BINARY_OP, hit); + PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; + s = str_st; + i = sub_st; + res = PyStackRef_FromPyObjectBorrow(res_o); + _tos_cache2 = i; + _tos_cache1 = s; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_TUPLE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyTuple_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_TUPLE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + nos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyTuple_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_TUPLE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + nos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyTuple_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_TUPLE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + nos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyTuple_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_TUPLE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyTuple_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_TUPLE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + tos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyTuple_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_TUPLE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + tos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyTuple_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_TUPLE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + tos = _stack_item_2; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyTuple_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef tuple_st; + sub_st = stack_pointer[-1]; + tuple_st = stack_pointer[-2]; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); + if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + if (index >= PyTuple_GET_SIZE(tuple)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = sub_st; + _tos_cache0 = tuple_st; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef tuple_st; + _PyStackRef _stack_item_0 = _tos_cache0; + sub_st = _stack_item_0; + tuple_st = stack_pointer[-1]; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); + if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = sub_st; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + if (index >= PyTuple_GET_SIZE(tuple)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = sub_st; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = sub_st; + _tos_cache0 = tuple_st; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef tuple_st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + sub_st = _stack_item_1; + tuple_st = _stack_item_0; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); + if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = sub_st; + _tos_cache0 = tuple_st; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + if (index >= PyTuple_GET_SIZE(tuple)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = sub_st; + _tos_cache0 = tuple_st; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = sub_st; + _tos_cache0 = tuple_st; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef tuple_st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + sub_st = _stack_item_2; + tuple_st = _stack_item_1; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); + if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = sub_st; + _tos_cache1 = tuple_st; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + if (index >= PyTuple_GET_SIZE(tuple)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = sub_st; + _tos_cache1 = tuple_st; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = sub_st; + _tos_cache1 = tuple_st; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_TUPLE_INT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef tuple_st; + _PyStackRef res; + _PyStackRef ts; + _PyStackRef ss; + sub_st = stack_pointer[-1]; + tuple_st = stack_pointer[-2]; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); + STAT_INC(BINARY_OP, hit); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + PyObject *res_o = PyTuple_GET_ITEM(tuple, index); + assert(res_o != NULL); + res = PyStackRef_FromPyObjectNew(res_o); + ts = tuple_st; + ss = sub_st; + _tos_cache2 = ss; + _tos_cache1 = ts; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_TUPLE_INT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef tuple_st; + _PyStackRef res; + _PyStackRef ts; + _PyStackRef ss; + _PyStackRef _stack_item_0 = _tos_cache0; + sub_st = _stack_item_0; + tuple_st = stack_pointer[-1]; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); + STAT_INC(BINARY_OP, hit); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + PyObject *res_o = PyTuple_GET_ITEM(tuple, index); + assert(res_o != NULL); + res = PyStackRef_FromPyObjectNew(res_o); + ts = tuple_st; + ss = sub_st; + _tos_cache2 = ss; + _tos_cache1 = ts; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_TUPLE_INT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef tuple_st; + _PyStackRef res; + _PyStackRef ts; + _PyStackRef ss; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + sub_st = _stack_item_1; + tuple_st = _stack_item_0; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); + STAT_INC(BINARY_OP, hit); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + PyObject *res_o = PyTuple_GET_ITEM(tuple, index); + assert(res_o != NULL); + res = PyStackRef_FromPyObjectNew(res_o); + ts = tuple_st; + ss = sub_st; + _tos_cache2 = ss; + _tos_cache1 = ts; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_DICT_SUBSCRIPT_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!Py_TYPE(o)->tp_as_mapping) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_DICT_SUBSCRIPT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + nos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!Py_TYPE(o)->tp_as_mapping) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_DICT_SUBSCRIPT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + nos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!Py_TYPE(o)->tp_as_mapping) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_DICT_SUBSCRIPT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + nos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!Py_TYPE(o)->tp_as_mapping) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_DICT_STORE_SUBSCRIPT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!Py_TYPE(o)->tp_as_mapping) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = nos; + _tos_cache0 = stack_pointer[-3]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_DICT_STORE_SUBSCRIPT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + nos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!Py_TYPE(o)->tp_as_mapping) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_0; + _tos_cache1 = nos; + _tos_cache0 = stack_pointer[-2]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_DICT_STORE_SUBSCRIPT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + nos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!Py_TYPE(o)->tp_as_mapping) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_1; + _tos_cache1 = nos; + _tos_cache0 = stack_pointer[-1]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_DICT_STORE_SUBSCRIPT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + nos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!Py_TYPE(o)->tp_as_mapping) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_ANY_DICT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyAnyDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_ANY_DICT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + tos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyAnyDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_ANY_DICT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + tos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyAnyDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_ANY_DICT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + tos = _stack_item_2; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyAnyDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_DICT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_DICT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + tos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_DICT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + tos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_DICT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + tos = _stack_item_2; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FROZENDICT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyFrozenDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FROZENDICT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + tos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyFrozenDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FROZENDICT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + tos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyFrozenDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FROZENDICT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + tos = _stack_item_2; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyFrozenDict_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_DICT_KNOWN_HASH_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef dict_st; + _PyStackRef res; + _PyStackRef ds; + _PyStackRef ss; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + sub_st = _stack_item_1; + dict_st = _stack_item_0; + PyObject *hash = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(Py_TYPE(dict)->tp_as_mapping->mp_subscript == _PyDict_Subscript); + STAT_INC(BINARY_OP, hit); + stack_pointer[0] = dict_st; + stack_pointer[1] = sub_st; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyDict_SubscriptKnownHash(dict, sub, (Py_hash_t)hash); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + ds = dict_st; + ss = sub_st; + _tos_cache2 = ss; + _tos_cache1 = ds; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_DICT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef dict_st; + _PyStackRef res; + _PyStackRef ds; + _PyStackRef ss; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + sub_st = _stack_item_1; + dict_st = _stack_item_0; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(Py_TYPE(dict)->tp_as_mapping->mp_subscript == _PyDict_Subscript); + STAT_INC(BINARY_OP, hit); + stack_pointer[0] = dict_st; + stack_pointer[1] = sub_st; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyDict_Subscript(dict, sub); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + ds = dict_st; + ss = sub_st; + _tos_cache2 = ss; + _tos_cache1 = ds; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_CHECK_FUNC_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef container; + _PyStackRef getitem; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + container = _stack_item_0; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); + if (!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = container; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; + PyObject *getitem_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(ht->_spec_cache.getitem); + if (getitem_o == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = container; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + assert(PyFunction_Check(getitem_o)); + uint32_t cached_version = FT_ATOMIC_LOAD_UINT32_RELAXED(ht->_spec_cache.getitem_version); + if (((PyFunctionObject *)getitem_o)->func_version != cached_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = container; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem_o); + assert(code->co_argcount == 2); + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = container; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + getitem = PyStackRef_FromPyObjectNew(getitem_o); + _tos_cache2 = getitem; + _tos_cache1 = _stack_item_1; + _tos_cache0 = container; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_INIT_CALL_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef getitem; + _PyStackRef sub; + _PyStackRef container; + _PyStackRef new_frame; + getitem = stack_pointer[-1]; + sub = stack_pointer[-2]; + container = stack_pointer[-3]; + STAT_INC(BINARY_OP, hit); + _PyInterpreterFrame* pushed_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); + pushed_frame->localsplus[0] = container; + pushed_frame->localsplus[1] = sub; + frame->return_offset = 6u ; + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_INIT_CALL_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef getitem; + _PyStackRef sub; + _PyStackRef container; + _PyStackRef new_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + getitem = _stack_item_0; + sub = stack_pointer[-1]; + container = stack_pointer[-2]; + STAT_INC(BINARY_OP, hit); + _PyInterpreterFrame* pushed_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); + pushed_frame->localsplus[0] = container; + pushed_frame->localsplus[1] = sub; + frame->return_offset = 6u ; + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_INIT_CALL_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef getitem; + _PyStackRef sub; + _PyStackRef container; + _PyStackRef new_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + getitem = _stack_item_1; + sub = _stack_item_0; + container = stack_pointer[-1]; + STAT_INC(BINARY_OP, hit); + _PyInterpreterFrame* pushed_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); + pushed_frame->localsplus[0] = container; + pushed_frame->localsplus[1] = sub; + frame->return_offset = 6u ; + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_INIT_CALL_r31: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef getitem; + _PyStackRef sub; + _PyStackRef container; + _PyStackRef new_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + getitem = _stack_item_2; + sub = _stack_item_1; + container = _stack_item_0; + STAT_INC(BINARY_OP, hit); + _PyInterpreterFrame* pushed_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); + pushed_frame->localsplus[0] = container; + pushed_frame->localsplus[1] = sub; + frame->return_offset = 6u ; + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LIST_APPEND_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef v; + _PyStackRef list; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + v = _stack_item_0; + list = stack_pointer[-1 - (oparg-1)]; + int err = _PyList_AppendTakeRef((PyListObject *)PyStackRef_AsPyObjectBorrow(list), + PyStackRef_AsPyObjectSteal(v)); + if (err < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SET_ADD_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef v; + _PyStackRef set; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + v = _stack_item_0; + set = stack_pointer[-1 - (oparg-1)]; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PySet_AddTakeRef((PySetObject *)PyStackRef_AsPyObjectBorrow(set), + PyStackRef_AsPyObjectSteal(v)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_SUBSCR_r30: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub; + _PyStackRef container; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + sub = _stack_item_2; + container = _stack_item_1; + v = _stack_item_0; + stack_pointer[0] = v; + stack_pointer[1] = container; + stack_pointer[2] = sub; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), PyStackRef_AsPyObjectBorrow(sub), PyStackRef_AsPyObjectBorrow(v)); + _PyStackRef tmp = sub; + sub = PyStackRef_NULL; + stack_pointer[-1] = sub; + PyStackRef_CLOSE(tmp); + tmp = container; + container = PyStackRef_NULL; + stack_pointer[-2] = container; + PyStackRef_CLOSE(tmp); + tmp = v; + v = PyStackRef_NULL; + stack_pointer[-3] = v; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (err) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_SUBSCR_LIST_INT_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub_st; + _PyStackRef list_st; + _PyStackRef value; + _PyStackRef ls; + _PyStackRef ss; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + sub_st = _stack_item_2; + list_st = _stack_item_1; + value = _stack_item_0; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); + assert(PyLong_CheckExact(sub)); + assert(PyList_CheckExact(list)); + Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub); + if (!LOCK_OBJECT(list)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = sub_st; + _tos_cache1 = list_st; + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + Py_ssize_t len = PyList_GET_SIZE(list); + if (index < 0) { + index += len; + } + if (index < 0 || index >= len) { + UNLOCK_OBJECT(list); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = sub_st; + _tos_cache1 = list_st; + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + } + STAT_INC(STORE_SUBSCR, hit); + PyObject *old_value = PyList_GET_ITEM(list, index); + FT_ATOMIC_STORE_PTR_RELEASE(_PyList_ITEMS(list)[index], + PyStackRef_AsPyObjectSteal(value)); + assert(old_value != NULL); + UNLOCK_OBJECT(list); + ls = list_st; + ss = sub_st; + stack_pointer[0] = ls; + stack_pointer[1] = ss; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache1 = ss; + _tos_cache0 = ls; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_SUBSCR_DICT_r31: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub; + _PyStackRef dict_st; + _PyStackRef value; + _PyStackRef st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + sub = _stack_item_2; + dict_st = _stack_item_1; + value = _stack_item_0; + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(Py_TYPE(dict)->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript); + STAT_INC(STORE_SUBSCR, hit); + stack_pointer[0] = value; + stack_pointer[1] = dict_st; + stack_pointer[2] = sub; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyDict_SetItem_Take2((PyDictObject *)dict, + PyStackRef_AsPyObjectSteal(sub), + PyStackRef_AsPyObjectSteal(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(dict_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + st = dict_st; + _tos_cache0 = st; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_SUBSCR_DICT_KNOWN_HASH_r31: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub; + _PyStackRef dict_st; + _PyStackRef value; + _PyStackRef st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + sub = _stack_item_2; + dict_st = _stack_item_1; + value = _stack_item_0; + PyObject *hash = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(Py_TYPE(dict)->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript); + STAT_INC(STORE_SUBSCR, hit); + stack_pointer[0] = value; + stack_pointer[1] = dict_st; + stack_pointer[2] = sub; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyDict_SetItem_Take2_KnownHash((PyDictObject *)dict, + PyStackRef_AsPyObjectSteal(sub), + PyStackRef_AsPyObjectSteal(value), + (Py_hash_t)hash); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(dict_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + st = dict_st; + _tos_cache0 = st; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _DELETE_SUBSCR_r20: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef sub; + _PyStackRef container; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + sub = _stack_item_1; + container = _stack_item_0; + stack_pointer[0] = container; + stack_pointer[1] = sub; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyObject_DelItem(PyStackRef_AsPyObjectBorrow(container), + PyStackRef_AsPyObjectBorrow(sub)); + _PyStackRef tmp = sub; + sub = PyStackRef_NULL; + stack_pointer[-1] = sub; + PyStackRef_CLOSE(tmp); + tmp = container; + container = PyStackRef_NULL; + stack_pointer[-2] = container; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (err) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CALL_INTRINSIC_1_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + value = _stack_item_0; + assert(oparg <= MAX_INTRINSIC_1); + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + v = value; + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache1 = v; + _tos_cache0 = res; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CALL_INTRINSIC_2_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value1_st; + _PyStackRef value2_st; + _PyStackRef res; + _PyStackRef vs1; + _PyStackRef vs2; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + value1_st = _stack_item_1; + value2_st = _stack_item_0; + assert(oparg <= MAX_INTRINSIC_2); + PyObject *value1 = PyStackRef_AsPyObjectBorrow(value1_st); + PyObject *value2 = PyStackRef_AsPyObjectBorrow(value2_st); + stack_pointer[0] = value2_st; + stack_pointer[1] = value1_st; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyIntrinsics_BinaryFunctions[oparg].func(tstate, value2, value1); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + vs1 = value1_st; + vs2 = value2_st; + _tos_cache2 = vs2; + _tos_cache1 = vs1; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MAKE_HEAP_SAFE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + value = PyStackRef_MakeHeapSafe(value); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MAKE_HEAP_SAFE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + value = PyStackRef_MakeHeapSafe(value); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MAKE_HEAP_SAFE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + value = PyStackRef_MakeHeapSafe(value); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MAKE_HEAP_SAFE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + value = PyStackRef_MakeHeapSafe(value); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _RETURN_VALUE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef retval; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + retval = _stack_item_0; + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + _PyStackRef temp = retval; + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + stack_pointer = _PyFrame_GetStackPointer(frame); + LOAD_IP(frame->return_offset); + res = temp; + LLTRACE_RESUME_FRAME(); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GET_AITER_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef obj; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + obj = _stack_item_0; + unaryfunc getter = NULL; + PyObject *obj_o = PyStackRef_AsPyObjectBorrow(obj); + PyObject *iter_o; + PyTypeObject *type = Py_TYPE(obj_o); + if (type->tp_as_async != NULL) { + getter = type->tp_as_async->am_aiter; + } + if (getter == NULL) { + stack_pointer[0] = obj; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, + "'async for' requires an object with " + "__aiter__ method, got %.100s", + type->tp_name); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(obj); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + stack_pointer[0] = obj; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + iter_o = (*getter)(obj_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(obj); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (iter_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (Py_TYPE(iter_o)->tp_as_async == NULL || + Py_TYPE(iter_o)->tp_as_async->am_anext == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, + "'async for' received an object from __aiter__ " + "that does not implement __anext__: %.100s", + Py_TYPE(iter_o)->tp_name); + Py_DECREF(iter_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + iter = PyStackRef_FromPyObjectSteal(iter_o); + _tos_cache0 = iter; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GET_ANEXT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef aiter; + _PyStackRef awaitable; + _PyStackRef _stack_item_0 = _tos_cache0; + aiter = _stack_item_0; + stack_pointer[0] = aiter; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *awaitable_o = _PyEval_GetANext(PyStackRef_AsPyObjectBorrow(aiter)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (awaitable_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + awaitable = PyStackRef_FromPyObjectSteal(awaitable_o); + _tos_cache1 = awaitable; + _tos_cache0 = aiter; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GET_AWAITABLE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + iterable = _stack_item_0; + stack_pointer[0] = iterable; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *iter_o = _PyEval_GetAwaitable(PyStackRef_AsPyObjectBorrow(iterable), oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(iterable); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (iter_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + iter = PyStackRef_FromPyObjectSteal(iter_o); + _tos_cache0 = iter; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SEND_GEN_FRAME_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef v; + _PyStackRef receiver; + _PyStackRef gen_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + v = _stack_item_2; + receiver = _stack_item_0; + PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver); + if (Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = v; + _tos_cache1 = _stack_item_1; + _tos_cache0 = receiver; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + if (!gen_try_set_executing((PyGenObject *)gen)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = v; + _tos_cache1 = _stack_item_1; + _tos_cache0 = receiver; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(SEND, hit); + _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; + _PyFrame_StackPush(pushed_frame, PyStackRef_MakeHeapSafe(v)); + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert( 2u + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)( 2u + oparg); + pushed_frame->previous = frame; + gen_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache2 = gen_frame; + _tos_cache1 = _stack_item_1; + _tos_cache0 = receiver; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_IS_NONE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef val; + val = stack_pointer[-1]; + if (!PyStackRef_IsNone(val)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = val; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_IS_NONE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef val; + _PyStackRef _stack_item_0 = _tos_cache0; + val = _stack_item_0; + if (!PyStackRef_IsNone(val)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = val; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = val; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_IS_NONE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef val; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + val = _stack_item_1; + if (!PyStackRef_IsNone(val)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = val; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = val; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_IS_NONE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef val; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + val = _stack_item_2; + if (!PyStackRef_IsNone(val)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = val; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = val; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_NOT_NULL_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + nos = stack_pointer[-2]; + if (PyStackRef_IsNull(nos)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_NOT_NULL_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + nos = stack_pointer[-1]; + if (PyStackRef_IsNull(nos)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_NOT_NULL_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + nos = _stack_item_0; + if (PyStackRef_IsNull(nos)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_NOT_NULL_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + nos = _stack_item_1; + if (PyStackRef_IsNull(nos)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _SEND_VIRTUAL is not a viable micro-op for tier 2 because it is replaced */ + + case _SEND_VIRTUAL_TIER_TWO_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef none; + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + none = stack_pointer[-1]; + null_or_index = stack_pointer[-2]; + iter = stack_pointer[-3]; + assert(PyStackRef_IsNone(none)); + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyObjectIndexPair next_index = CALL_TP_ITERITEM_NO_ESCAPE(iter_o, index); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + next = none; + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + } + next = PyStackRef_FromPyObjectSteal(next_o); + null_or_index = PyStackRef_TagInt(index); + _tos_cache2 = next; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SEND_VIRTUAL_TIER_TWO_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef none; + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + none = _stack_item_0; + null_or_index = stack_pointer[-1]; + iter = stack_pointer[-2]; + assert(PyStackRef_IsNone(none)); + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyObjectIndexPair next_index = CALL_TP_ITERITEM_NO_ESCAPE(iter_o, index); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + stack_pointer[0] = none; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + next = none; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = stack_pointer[0]; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + next = PyStackRef_FromPyObjectSteal(next_o); + null_or_index = PyStackRef_TagInt(index); + _tos_cache2 = next; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SEND_VIRTUAL_TIER_TWO_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef none; + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + none = _stack_item_1; + null_or_index = _stack_item_0; + iter = stack_pointer[-1]; + assert(PyStackRef_IsNone(none)); + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyObjectIndexPair next_index = CALL_TP_ITERITEM_NO_ESCAPE(iter_o, index); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + stack_pointer[0] = null_or_index; + stack_pointer[1] = none; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + next = none; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = stack_pointer[1]; + _tos_cache0 = null_or_index; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + next = PyStackRef_FromPyObjectSteal(next_o); + null_or_index = PyStackRef_TagInt(index); + _tos_cache2 = next; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SEND_VIRTUAL_TIER_TWO_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef none; + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + none = _stack_item_2; + null_or_index = _stack_item_1; + iter = _stack_item_0; + assert(PyStackRef_IsNone(none)); + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyObjectIndexPair next_index = CALL_TP_ITERITEM_NO_ESCAPE(iter_o, index); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + stack_pointer[0] = iter; + stack_pointer[1] = null_or_index; + stack_pointer[2] = none; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + next = none; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = stack_pointer[2]; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + } + next = PyStackRef_FromPyObjectSteal(next_o); + null_or_index = PyStackRef_TagInt(index); + _tos_cache2 = next; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_3OS_ASYNC_GEN_ASEND_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + iter = stack_pointer[-3]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (!PyAsyncGenASend_CheckExact(iter_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_3OS_ASYNC_GEN_ASEND_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + iter = stack_pointer[-2]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (!PyAsyncGenASend_CheckExact(iter_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_3OS_ASYNC_GEN_ASEND_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + iter = stack_pointer[-1]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (!PyAsyncGenASend_CheckExact(iter_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_3OS_ASYNC_GEN_ASEND_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + iter = _stack_item_0; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (!PyAsyncGenASend_CheckExact(iter_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _SEND_ASYNC_GEN is not a viable micro-op for tier 2 because it is replaced */ + + case _SEND_ASYNC_GEN_TIER_TWO_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef v; + _PyStackRef null_in; + _PyStackRef iter; + _PyStackRef asend; + _PyStackRef null_out; + _PyStackRef retval; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + v = _stack_item_2; + null_in = _stack_item_1; + iter = _stack_item_0; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + assert(PyAsyncGenASend_CheckExact(iter_o)); + PyObject *val = PyStackRef_AsPyObjectBorrow(v); + PyObject *retval_o; + stack_pointer[0] = iter; + stack_pointer[1] = null_in; + stack_pointer[2] = v; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PySendResult what = _PyAsyncGenASend_Send(iter_o, val, &retval_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (what == PYGEN_ERROR) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + asend = iter; + null_out = null_in; + retval = PyStackRef_FromPyObjectSteal(retval_o); + if (what == PYGEN_RETURN) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = stack_pointer[-3]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = retval; + _tos_cache1 = null_out; + _tos_cache0 = asend; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _YIELD_VALUE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef retval; + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + retval = _stack_item_0; + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + frame->instr_ptr++; + PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); + assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); + assert(oparg == 0 || oparg == 1); + _PyStackRef temp = retval; + _PyFrame_SetStackPointer(frame, stack_pointer); + DTRACE_FUNCTION_RETURN(); + tstate->exc_info = gen->gi_exc_state.previous_item; + gen->gi_exc_state.previous_item = NULL; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; + frame = tstate->current_frame = frame->previous; + gen_frame->previous = NULL; + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); + assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + #if TIER_ONE && defined(Py_DEBUG) + if (!PyStackRef_IsNone(frame->f_executable)) { + Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); + assert(i >= 0 && i <= INT_MAX); + int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; + assert(opcode == SEND || opcode == FOR_ITER); + } + #endif + stack_pointer = _PyFrame_GetStackPointer(frame); + LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); + value = temp; + LLTRACE_RESUME_FRAME(); + _tos_cache0 = value; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _POP_EXCEPT_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef exc_value; + _PyStackRef _stack_item_0 = _tos_cache0; + exc_value = _stack_item_0; + _PyErr_StackItem *exc_info = tstate->exc_info; + stack_pointer[0] = exc_value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XSETREF(exc_info->exc_value, + PyStackRef_IsNone(exc_value) + ? NULL : PyStackRef_AsPyObjectSteal(exc_value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_COMMON_CONSTANT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + oparg = CURRENT_OPARG(); + assert(oparg < NUM_COMMON_CONSTANTS); + value = PyStackRef_DupImmortal(tstate->interp->common_consts[oparg]); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_COMMON_CONSTANT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + assert(oparg < NUM_COMMON_CONSTANTS); + value = PyStackRef_DupImmortal(tstate->interp->common_consts[oparg]); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_COMMON_CONSTANT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + assert(oparg < NUM_COMMON_CONSTANTS); + value = PyStackRef_DupImmortal(tstate->interp->common_consts[oparg]); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_BUILD_CLASS_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bc; + int err; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *bc_o = _PyMapping_GetOptionalItem2(BUILTINS(), &_Py_ID(__build_class__), &err); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (bc_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetString(tstate, PyExc_NameError, + "__build_class__ not found"); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + bc = PyStackRef_FromPyObjectSteal(bc_o); + _tos_cache0 = bc; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_NAME_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + v = _stack_item_0; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *ns = LOCALS(); + int err; + if (ns == NULL) { + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_SystemError, + "no locals found when storing %R", name); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (PyDict_CheckExact(ns)) { + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + err = PyDict_SetItem(ns, name, PyStackRef_AsPyObjectBorrow(v)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + err = PyObject_SetItem(ns, name, PyStackRef_AsPyObjectBorrow(v)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _DELETE_NAME_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + oparg = CURRENT_OPARG(); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *ns = LOCALS(); + int err; + if (ns == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_SystemError, + "no locals when deleting %R", name); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + err = PyObject_DelItem(ns, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, + name); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef *top; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + seq = _stack_item_0; + top = &stack_pointer[oparg]; + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = _PyEval_UnpackIterableStackRef(tstate, seq_o, oparg, -1, top); + Py_DECREF(seq_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res == 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_TWO_TUPLE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef val1; + _PyStackRef val0; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + seq = _stack_item_0; + assert(oparg == 2); + PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); + assert(PyTuple_CheckExact(seq_o)); + if (PyTuple_GET_SIZE(seq_o) != 2) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = seq; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(UNPACK_SEQUENCE, hit); + val0 = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq_o, 0)); + val1 = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq_o, 1)); + stack_pointer[0] = val1; + stack_pointer[1] = val0; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(seq); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache1 = val0; + _tos_cache0 = val1; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef val1; + _PyStackRef val0; + seq = stack_pointer[-1]; + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + STAT_INC(UNPACK_SEQUENCE, hit); + val0 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 0)); + val1 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 1)); + PyObject_GC_UnTrack(seq_o); + _PyStolenTuple_Free(seq_o); + _tos_cache1 = val0; + _tos_cache0 = val1; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef val1; + _PyStackRef val0; + _PyStackRef _stack_item_0 = _tos_cache0; + seq = _stack_item_0; + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + STAT_INC(UNPACK_SEQUENCE, hit); + val0 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 0)); + val1 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 1)); + PyObject_GC_UnTrack(seq_o); + _PyStolenTuple_Free(seq_o); + _tos_cache1 = val0; + _tos_cache0 = val1; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef val1; + _PyStackRef val0; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + seq = _stack_item_1; + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + STAT_INC(UNPACK_SEQUENCE, hit); + val0 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 0)); + val1 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 1)); + PyObject_GC_UnTrack(seq_o); + _PyStolenTuple_Free(seq_o); + _tos_cache2 = val0; + _tos_cache1 = val1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef val2; + _PyStackRef val1; + _PyStackRef val0; + seq = stack_pointer[-1]; + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + STAT_INC(UNPACK_SEQUENCE, hit); + val0 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 0)); + val1 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 1)); + val2 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 2)); + PyObject_GC_UnTrack(seq_o); + _PyStolenTuple_Free(seq_o); + _tos_cache2 = val0; + _tos_cache1 = val1; + _tos_cache0 = val2; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef val2; + _PyStackRef val1; + _PyStackRef val0; + _PyStackRef _stack_item_0 = _tos_cache0; + seq = _stack_item_0; + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + STAT_INC(UNPACK_SEQUENCE, hit); + val0 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 0)); + val1 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 1)); + val2 = PyStackRef_FromPyObjectSteal(PyTuple_GET_ITEM(seq_o, 2)); + PyObject_GC_UnTrack(seq_o); + _PyStolenTuple_Free(seq_o); + _tos_cache2 = val0; + _tos_cache1 = val1; + _tos_cache0 = val2; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_TUPLE_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef *values; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + seq = _stack_item_0; + values = &stack_pointer[0]; + PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); + assert(PyTuple_CheckExact(seq_o)); + if (PyTuple_GET_SIZE(seq_o) != oparg) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = seq; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(UNPACK_SEQUENCE, hit); + PyObject **items = _PyTuple_ITEMS(seq_o); + for (int i = oparg; --i >= 0; ) { + *values++ = PyStackRef_FromPyObjectNew(items[i]); + } + stack_pointer += oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(seq); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_UNIQUE_TUPLE_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef *values; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + seq = _stack_item_0; + values = &stack_pointer[0]; + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + assert(PyTuple_CheckExact(seq_o)); + assert(PyTuple_GET_SIZE(seq_o) == oparg); + assert(_PyObject_IsUniquelyReferenced(seq_o)); + STAT_INC(UNPACK_SEQUENCE, hit); + PyObject **items = _PyTuple_ITEMS(seq_o); + for (int i = oparg; --i >= 0; ) { + *values++ = PyStackRef_FromPyObjectSteal(items[i]); + } + PyObject_GC_UnTrack(seq_o); + _PyStolenTuple_Free(seq_o); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_LIST_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef *values; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + seq = _stack_item_0; + values = &stack_pointer[0]; + PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); + assert(PyList_CheckExact(seq_o)); + if (!LOCK_OBJECT(seq_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = seq; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + if (PyList_GET_SIZE(seq_o) != oparg) { + UNLOCK_OBJECT(seq_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = seq; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + STAT_INC(UNPACK_SEQUENCE, hit); + PyObject **items = _PyList_ITEMS(seq_o); + for (int i = oparg; --i >= 0; ) { + *values++ = PyStackRef_FromPyObjectNew(items[i]); + } + UNLOCK_OBJECT(seq_o); + stack_pointer += oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(seq); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _UNPACK_EX_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef seq; + _PyStackRef *top; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + seq = _stack_item_0; + top = &stack_pointer[1 + (oparg & 0xFF) + (oparg >> 8)]; + PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = _PyEval_UnpackIterableStackRef(tstate, seq_o, oparg & 0xFF, oparg >> 8, top); + Py_DECREF(seq_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res == 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += 1 + (oparg & 0xFF) + (oparg >> 8); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_ATTR_r20: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + owner = _stack_item_1; + v = _stack_item_0; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + stack_pointer[0] = v; + stack_pointer[1] = owner; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyObject_SetAttr(PyStackRef_AsPyObjectBorrow(owner), + name, PyStackRef_AsPyObjectBorrow(v)); + _PyStackRef tmp = owner; + owner = PyStackRef_NULL; + stack_pointer[-1] = owner; + PyStackRef_CLOSE(tmp); + tmp = v; + v = PyStackRef_NULL; + stack_pointer[-2] = v; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (err) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _DELETE_ATTR_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + owner = _stack_item_0; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + stack_pointer[0] = owner; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyObject_DelAttr(PyStackRef_AsPyObjectBorrow(owner), name); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_GLOBAL_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + v = _stack_item_0; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _DELETE_GLOBAL_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + oparg = CURRENT_OPARG(); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyDict_Pop(GLOBALS(), name, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (err == 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_LOCALS_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef locals; + PyObject *l = LOCALS(); + if (l == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetString(tstate, PyExc_SystemError, + "no locals found"); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + locals = PyStackRef_FromPyObjectNew(l); + _tos_cache0 = locals; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_LOCALS_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef locals; + _PyStackRef _stack_item_0 = _tos_cache0; + PyObject *l = LOCALS(); + if (l == NULL) { + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetString(tstate, PyExc_SystemError, + "no locals found"); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + locals = PyStackRef_FromPyObjectNew(l); + _tos_cache1 = locals; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_LOCALS_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef locals; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + PyObject *l = LOCALS(); + if (l == NULL) { + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_SetString(tstate, PyExc_SystemError, + "no locals found"); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + locals = PyStackRef_FromPyObjectNew(l); + _tos_cache2 = locals; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _LOAD_FROM_DICT_OR_GLOBALS is not a viable micro-op for tier 2 because it has both popping and not-popping errors */ + + case _LOAD_NAME_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef v; + oparg = CURRENT_OPARG(); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *v_o = _PyEval_LoadName(tstate, frame, name); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (v_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (PyLazyImport_CheckExact(v_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (l_v == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(v_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyDict_SetItem(GLOBALS(), name, l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(v_o); + Py_DECREF(l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_SETREF(v_o, l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + v = PyStackRef_FromPyObjectSteal(v_o); + _tos_cache0 = v; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_GLOBAL_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *res; + oparg = CURRENT_OPARG(); + res = &stack_pointer[0]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (PyStackRef_IsNull(*res)) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _PUSH_NULL_CONDITIONAL_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *null; + oparg = CURRENT_OPARG(); + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += (oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_GLOBALS_VERSION_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + uint16_t version = (uint16_t)CURRENT_OPERAND0_16(); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + if (!PyDict_CheckExact(dict)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_GLOBALS_VERSION_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + uint16_t version = (uint16_t)CURRENT_OPERAND0_16(); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + if (!PyDict_CheckExact(dict)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_GLOBALS_VERSION_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + uint16_t version = (uint16_t)CURRENT_OPERAND0_16(); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + if (!PyDict_CheckExact(dict)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_GLOBALS_VERSION_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + uint16_t version = (uint16_t)CURRENT_OPERAND0_16(); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + if (!PyDict_CheckExact(dict)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_GLOBAL_MODULE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef res; + uint16_t version = (uint16_t)CURRENT_OPERAND0_16(); + uint16_t index = (uint16_t)CURRENT_OPERAND1_16(); + PyDictObject *dict = (PyDictObject *)GLOBALS(); + if (!PyDict_CheckExact(dict)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); + assert(index < DK_SIZE(keys)); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + if (res_o == NULL) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #if Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res); + if (!increfed) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #else + res = PyStackRef_FromPyObjectNew(res_o); + #endif + STAT_INC(LOAD_GLOBAL, hit); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_GLOBAL_BUILTINS_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef res; + uint16_t version = (uint16_t)CURRENT_OPERAND0_16(); + uint16_t index = (uint16_t)CURRENT_OPERAND1_16(); + PyDictObject *dict = (PyDictObject *)BUILTINS(); + if (!PyDict_CheckExact(dict)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); + PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); + if (res_o == NULL) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #if Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res); + if (!increfed) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #else + res = PyStackRef_FromPyObjectNew(res_o); + #endif + STAT_INC(LOAD_GLOBAL, hit); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _DELETE_FAST_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + oparg = CURRENT_OPARG(); + _PyStackRef v = GETLOCAL(oparg); + if (PyStackRef_IsNull(v)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_NULL; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MAKE_CELL_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + oparg = CURRENT_OPARG(); + PyObject *initial = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + PyObject *cell = PyCell_New(initial); + if (cell == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyStackRef_FromPyObjectSteal(cell); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _DELETE_DEREF_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + oparg = CURRENT_OPARG(); + PyObject *cell = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + PyObject *oldobj = PyCell_SwapTakeRef((PyCellObject *)cell, NULL); + if (oldobj == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(oldobj); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_FROM_DICT_OR_DEREF_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef class_dict_st; + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + class_dict_st = _stack_item_0; + PyObject *name; + PyObject *class_dict = PyStackRef_AsPyObjectBorrow(class_dict_st); + assert(class_dict); + assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus); + name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); + int err; + stack_pointer[0] = class_dict_st; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject* value_o = _PyMapping_GetOptionalItem2(class_dict, name, &err); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (!value_o) { + PyCellObject *cell = (PyCellObject *)PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + value_o = PyCell_GetRef(cell); + if (value_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(class_dict_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + value = PyStackRef_FromPyObjectSteal(value_o); + _tos_cache0 = value; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_DEREF_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + oparg = CURRENT_OPARG(); + PyCellObject *cell = (PyCellObject *)PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + _PyFrame_SetStackPointer(frame, stack_pointer); + value = _PyCell_GetStackRef(cell); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (PyStackRef_IsNull(value)) { + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = value; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_DEREF_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef v; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + v = _stack_item_0; + PyCellObject *cell = (PyCellObject *)PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyCell_SetTakeRef(cell, PyStackRef_AsPyObjectSteal(v)); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COPY_FREE_VARS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + oparg = CURRENT_OPARG(); + PyCodeObject *co = _PyFrame_GetCode(frame); + assert(PyStackRef_FunctionCheck(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); + PyObject *closure = func->func_closure; + assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; + for (int i = 0; i < oparg; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + frame->localsplus[offset + i] = PyStackRef_FromPyObjectNew(o); + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COPY_FREE_VARS_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + PyCodeObject *co = _PyFrame_GetCode(frame); + assert(PyStackRef_FunctionCheck(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); + PyObject *closure = func->func_closure; + assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; + for (int i = 0; i < oparg; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + frame->localsplus[offset + i] = PyStackRef_FromPyObjectNew(o); + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COPY_FREE_VARS_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + PyCodeObject *co = _PyFrame_GetCode(frame); + assert(PyStackRef_FunctionCheck(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); + PyObject *closure = func->func_closure; + assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; + for (int i = 0; i < oparg; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + frame->localsplus[offset + i] = PyStackRef_FromPyObjectNew(o); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COPY_FREE_VARS_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + PyCodeObject *co = _PyFrame_GetCode(frame); + assert(PyStackRef_FunctionCheck(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); + PyObject *closure = func->func_closure; + assert(oparg == co->co_nfreevars); + int offset = co->co_nlocalsplus - oparg; + for (int i = 0; i < oparg; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + frame->localsplus[offset + i] = PyStackRef_FromPyObjectNew(o); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BUILD_STRING_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *pieces; + _PyStackRef str; + oparg = CURRENT_OPARG(); + pieces = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *str_o = _Py_BuildString_StackRefSteal(pieces, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (str_o == NULL) { + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + str = PyStackRef_FromPyObjectSteal(str_o); + _tos_cache0 = str; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BUILD_INTERPOLATION_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *format; + _PyStackRef str; + _PyStackRef value; + _PyStackRef interpolation; + oparg = CURRENT_OPARG(); + format = &stack_pointer[-(oparg & 1)]; + str = stack_pointer[-1 - (oparg & 1)]; + value = stack_pointer[-2 - (oparg & 1)]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + PyObject *str_o = PyStackRef_AsPyObjectBorrow(str); + int conversion = oparg >> 2; + PyObject *format_o; + if (oparg & 1) { + format_o = PyStackRef_AsPyObjectBorrow(format[0]); + } + else { + format_o = &_Py_STR(empty); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (oparg & 1) { + stack_pointer += -(oparg & 1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(format[0]); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + stack_pointer += -(oparg & 1); + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(str); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (interpolation_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + interpolation = PyStackRef_FromPyObjectSteal(interpolation_o); + _tos_cache0 = interpolation; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BUILD_TEMPLATE_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef interpolations; + _PyStackRef strings; + _PyStackRef template; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + interpolations = _stack_item_1; + strings = _stack_item_0; + PyObject *strings_o = PyStackRef_AsPyObjectBorrow(strings); + PyObject *interpolations_o = PyStackRef_AsPyObjectBorrow(interpolations); + stack_pointer[0] = strings; + stack_pointer[1] = interpolations; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(interpolations); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(strings); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (template_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + template = PyStackRef_FromPyObjectSteal(template_o); + _tos_cache0 = template; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BUILD_TUPLE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *values; + _PyStackRef tup; + oparg = CURRENT_OPARG(); + values = &stack_pointer[-oparg]; + PyObject *tup_o = _PyTuple_FromStackRefStealOnSuccess(values, oparg); + if (tup_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + tup = PyStackRef_FromPyObjectStealMortal(tup_o); + _tos_cache0 = tup; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BUILD_LIST_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *values; + _PyStackRef list; + oparg = CURRENT_OPARG(); + values = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *list_o = _PyList_FromStackRefStealOnSuccess(values, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (list_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + list = PyStackRef_FromPyObjectStealMortal(list_o); + _tos_cache0 = list; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LIST_EXTEND_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable_st; + _PyStackRef list_st; + _PyStackRef i; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + iterable_st = _stack_item_0; + list_st = stack_pointer[-1 - (oparg-1)]; + PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); + PyObject *iterable = PyStackRef_AsPyObjectBorrow(iterable_st); + stack_pointer[0] = iterable_st; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (none_val == NULL) { + int matches = _PyErr_ExceptionMatches(tstate, PyExc_TypeError); + if (matches && + (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) + { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Clear(tstate); + _PyErr_Format(tstate, PyExc_TypeError, + "Value after * must be an iterable, not %.200s", + Py_TYPE(iterable)->tp_name); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + assert(Py_IsNone(none_val)); + i = iterable_st; + _tos_cache0 = i; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SET_UPDATE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + _PyStackRef set; + _PyStackRef i; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + iterable = _stack_item_0; + set = stack_pointer[-1 - (oparg-1)]; + stack_pointer[0] = iterable; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PySet_Update(PyStackRef_AsPyObjectBorrow(set), + PyStackRef_AsPyObjectBorrow(iterable)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + i = iterable; + _tos_cache0 = i; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BUILD_SET_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *values; + _PyStackRef set; + oparg = CURRENT_OPARG(); + values = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *set_o = PySet_New(NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (set_o == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp; + for (int _i = oparg; --_i >= 0;) { + tmp = values[_i]; + values[_i] = PyStackRef_NULL; + PyStackRef_CLOSE(tmp); + } + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + int err = 0; + for (Py_ssize_t i = 0; i < oparg; i++) { + _PyStackRef value = values[i]; + values[i] = PyStackRef_NULL; + if (err == 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + err = _PySet_AddTakeRef((PySetObject *)set_o, PyStackRef_AsPyObjectSteal(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + if (err) { + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(set_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + set = PyStackRef_FromPyObjectStealMortal(set_o); + _tos_cache0 = set; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _BUILD_MAP_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *values; + _PyStackRef map; + oparg = CURRENT_OPARG(); + values = &stack_pointer[-oparg*2]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *map_o = _Py_BuildMap_StackRefSteal(values, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (map_o == NULL) { + stack_pointer += -oparg*2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + map = PyStackRef_FromPyObjectStealMortal(map_o); + _tos_cache0 = map; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -oparg*2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SETUP_ANNOTATIONS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + if (LOCALS() == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_SystemError, + "no locals found when setting up annotations"); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + int err; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject* ann_dict = _PyMapping_GetOptionalItem2(LOCALS(), &_Py_ID(__annotations__), &err); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (ann_dict == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + ann_dict = PyDict_New(); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (ann_dict == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), + ann_dict); + Py_DECREF(ann_dict); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(ann_dict); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _DICT_UPDATE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef update; + _PyStackRef dict; + _PyStackRef upd; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + update = _stack_item_0; + dict = stack_pointer[-1 - (oparg - 1)]; + PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); + PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); + stack_pointer[0] = update; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyDict_Update(dict_o, update_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + int matches = _PyErr_ExceptionMatches(tstate, PyExc_AttributeError); + if (matches) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *exc = _PyErr_GetRaisedException(tstate); + int has_keys = PyObject_HasAttrWithError(update_o, &_Py_ID(keys)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (has_keys == 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, + "'%T' object is not a mapping", + update_o); + Py_DECREF(exc); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_ChainExceptions1(exc); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + upd = update; + _tos_cache0 = upd; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _DICT_MERGE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef update; + _PyStackRef dict; + _PyStackRef callable; + _PyStackRef u; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + update = _stack_item_0; + dict = stack_pointer[-1 - (oparg - 1)]; + callable = stack_pointer[-4 - (oparg - 1)]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); + PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); + PyObject *dupkey = NULL; + stack_pointer[0] = update; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyDict_MergeUniq(dict_o, update_o, &dupkey); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatKwargsError(tstate, callable_o, update_o, dupkey); + Py_XDECREF(dupkey); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + u = update; + _tos_cache0 = u; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MAP_ADD_r20: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef key; + _PyStackRef dict_st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + value = _stack_item_1; + key = _stack_item_0; + dict_st = stack_pointer[-1 - (oparg - 1)]; + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(PyDict_CheckExact(dict)); + stack_pointer[0] = key; + stack_pointer[1] = value; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyDict_SetItem_Take2( + (PyDictObject *)dict, + PyStackRef_AsPyObjectSteal(key), + PyStackRef_AsPyObjectSteal(value) + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_SUPER_ATTR_ATTR_r31: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_st; + _PyStackRef class_st; + _PyStackRef global_super_st; + _PyStackRef attr_st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + self_st = _stack_item_2; + class_st = _stack_item_1; + global_super_st = _stack_item_0; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + assert(!(oparg & 1)); + if (global_super != (PyObject *)&PySuper_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = self_st; + _tos_cache1 = class_st; + _tos_cache0 = global_super_st; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + if (!PyType_Check(class)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = self_st; + _tos_cache1 = class_st; + _tos_cache0 = global_super_st; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(LOAD_SUPER_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + stack_pointer[0] = global_super_st; + stack_pointer[1] = class_st; + stack_pointer[2] = self_st; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *attr = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL); + _PyStackRef tmp = self_st; + self_st = PyStackRef_NULL; + stack_pointer[-1] = self_st; + PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-2] = class_st; + PyStackRef_CLOSE(tmp); + tmp = global_super_st; + global_super_st = PyStackRef_NULL; + stack_pointer[-3] = global_super_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (attr == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + attr_st = PyStackRef_FromPyObjectSteal(attr); + _tos_cache0 = attr_st; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_TYPE_VERSION_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + nos = stack_pointer[-2]; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyTypeObject *tp = (PyTypeObject *)PyStackRef_AsPyObjectBorrow(nos); + assert(type_version != 0); + if (!PyType_Check((PyObject *)tp)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_TYPE_VERSION_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + nos = stack_pointer[-1]; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyTypeObject *tp = (PyTypeObject *)PyStackRef_AsPyObjectBorrow(nos); + assert(type_version != 0); + if (!PyType_Check((PyObject *)tp)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_TYPE_VERSION_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + nos = _stack_item_0; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyTypeObject *tp = (PyTypeObject *)PyStackRef_AsPyObjectBorrow(nos); + assert(type_version != 0); + if (!PyType_Check((PyObject *)tp)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = nos; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_TYPE_VERSION_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef nos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + nos = _stack_item_1; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyTypeObject *tp = (PyTypeObject *)PyStackRef_AsPyObjectBorrow(nos); + assert(type_version != 0); + if (!PyType_Check((PyObject *)tp)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = nos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_LOAD_SUPER_ATTR_METHOD_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef class_st; + _PyStackRef global_super_st; + oparg = CURRENT_OPARG(); + class_st = stack_pointer[-2]; + global_super_st = stack_pointer[-3]; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + assert(oparg & 1); + if (global_super != (PyObject *)&PySuper_Type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + if (!PyType_Check(class)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = class_st; + _tos_cache0 = global_super_st; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_LOAD_SUPER_ATTR_METHOD_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef class_st; + _PyStackRef global_super_st; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + class_st = stack_pointer[-1]; + global_super_st = stack_pointer[-2]; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + assert(oparg & 1); + if (global_super != (PyObject *)&PySuper_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + if (!PyType_Check(class)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_0; + _tos_cache1 = class_st; + _tos_cache0 = global_super_st; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_LOAD_SUPER_ATTR_METHOD_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef class_st; + _PyStackRef global_super_st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + class_st = _stack_item_0; + global_super_st = stack_pointer[-1]; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + assert(oparg & 1); + if (global_super != (PyObject *)&PySuper_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = class_st; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + if (!PyType_Check(class)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = class_st; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_1; + _tos_cache1 = class_st; + _tos_cache0 = global_super_st; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_LOAD_SUPER_ATTR_METHOD_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef class_st; + _PyStackRef global_super_st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + class_st = _stack_item_1; + global_super_st = _stack_item_0; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + assert(oparg & 1); + if (global_super != (PyObject *)&PySuper_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = class_st; + _tos_cache0 = global_super_st; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + if (!PyType_Check(class)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = class_st; + _tos_cache0 = global_super_st; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = class_st; + _tos_cache0 = global_super_st; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_SUPER_ATTR_METHOD_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_st; + _PyStackRef class_st; + _PyStackRef global_super_st; + _PyStackRef attr; + _PyStackRef self_or_null; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + self_st = _stack_item_2; + class_st = _stack_item_1; + global_super_st = _stack_item_0; + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + STAT_INC(LOAD_SUPER_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + PyTypeObject *cls = (PyTypeObject *)class; + int method_found = 0; + PyObject *attr_o; + { + int *method_found_ptr = &method_found; + stack_pointer[0] = global_super_st; + stack_pointer[1] = class_st; + stack_pointer[2] = self_st; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + attr_o = _PySuper_Lookup(cls, self, name, + Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? method_found_ptr : NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + if (attr_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (method_found) { + self_or_null = self_st; + } else { + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(self_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + self_or_null = PyStackRef_NULL; + stack_pointer += 1; + } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = global_super_st; + global_super_st = self_or_null; + stack_pointer[-2] = global_super_st; + PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-1] = class_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + attr = PyStackRef_FromPyObjectSteal(attr_o); + _tos_cache1 = self_or_null; + _tos_cache0 = attr; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef *self_or_null; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + owner = _stack_item_0; + self_or_null = &stack_pointer[1]; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); + if (oparg & 1) { + stack_pointer[0] = owner; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + attr = _Py_LoadAttr_StackRefSteal(tstate, owner, name, self_or_null); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (PyStackRef_IsNull(attr)) { + stack_pointer[-1] = attr; + stack_pointer += (oparg&1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + } + else { + stack_pointer[0] = owner; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + attr = _PyObject_GetAttrStackRef(PyStackRef_AsPyObjectBorrow(owner), name); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer[-1] = attr; + stack_pointer += (oparg&1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(owner); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (PyStackRef_IsNull(attr)) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + stack_pointer += -(oparg&1); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[-1] = attr; + stack_pointer += (oparg&1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_VERSION_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + owner = stack_pointer[-1]; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_VERSION_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_VERSION_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_VERSION_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + owner = _stack_item_2; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_VERSION_LOCKED_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + owner = stack_pointer[-1]; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(type_version != 0); + PyTypeObject *tp = Py_TYPE(owner_o); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UNLOCK_OBJECT(owner_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_VERSION_LOCKED_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(type_version != 0); + PyTypeObject *tp = Py_TYPE(owner_o); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UNLOCK_OBJECT(owner_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_VERSION_LOCKED_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(type_version != 0); + PyTypeObject *tp = Py_TYPE(owner_o); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UNLOCK_OBJECT(owner_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_VERSION_LOCKED_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + owner = _stack_item_2; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(type_version != 0); + PyTypeObject *tp = Py_TYPE(owner_o); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UNLOCK_OBJECT(owner_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + owner = stack_pointer[-1]; + PyObject *type = (PyObject *)CURRENT_OPERAND0_64(); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + if (tp != (PyTypeObject *)type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + PyObject *type = (PyObject *)CURRENT_OPERAND0_64(); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + if (tp != (PyTypeObject *)type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + PyObject *type = (PyObject *)CURRENT_OPERAND0_64(); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + if (tp != (PyTypeObject *)type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + owner = _stack_item_2; + PyObject *type = (PyObject *)CURRENT_OPERAND0_64(); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + if (tp != (PyTypeObject *)type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_MANAGED_OBJECT_HAS_VALUES_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + owner = stack_pointer[-1]; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_dictoffset < 0); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_MANAGED_OBJECT_HAS_VALUES_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_dictoffset < 0); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_MANAGED_OBJECT_HAS_VALUES_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_dictoffset < 0); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_MANAGED_OBJECT_HAS_VALUES_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + owner = _stack_item_2; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_dictoffset < 0); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_INSTANCE_VALUE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + owner = stack_pointer[-1]; + uint16_t offset = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); + if (attr_o == NULL) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr); + if (!increfed) { + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + _tos_cache1 = o; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_INSTANCE_VALUE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + uint16_t offset = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); + if (attr_o == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr); + if (!increfed) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + _tos_cache1 = o; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_INSTANCE_VALUE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + uint16_t offset = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); + if (attr_o == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr); + if (!increfed) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); #endif - _PyStackRef *target_local = &GETLOCAL(next_oparg); - assert(PyUnicode_CheckExact(left_o)); - if (PyStackRef_AsPyObjectBorrow(*target_local) != left_o) { + STAT_INC(LOAD_ATTR, hit); + o = owner; + _tos_cache2 = o; + _tos_cache1 = attr; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_MODULE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + uint32_t dict_version = (uint32_t)CURRENT_OPERAND0_32(); + uint16_t index = (uint16_t)CURRENT_OPERAND1_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; + assert(dict != NULL); + PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); + if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + assert(keys->dk_kind == DICT_KEYS_UNICODE); + assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries)); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index; + PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); + if (attr_o == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); + if (!increfed) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + _tos_cache1 = o; + _tos_cache0 = attr; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_WITH_HINT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + owner = _stack_item_0; + uint16_t hint = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictObject *dict = _PyObject_GetManagedDict(owner_o); + if (dict == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + PyDictKeysObject *dk = FT_ATOMIC_LOAD_PTR(dict->ma_keys); + assert(PyDict_CheckExact((PyObject *)dict)); + #ifdef Py_GIL_DISABLED + if (!_Py_IsOwnedByCurrentThread((PyObject *)dict) && !_PyObject_GC_IS_SHARED(dict)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + #endif + PyObject *attr_o; + if (hint >= (size_t)FT_ATOMIC_LOAD_SSIZE_RELAXED(dk->dk_nentries)) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + if (dk->dk_kind != DICT_KEYS_UNICODE) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dk) + hint; + if (FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_key) != name) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + attr_o = FT_ATOMIC_LOAD_PTR(ep->me_value); + if (attr_o == NULL) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + STAT_INC(LOAD_ATTR, hit); + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); + if (!increfed) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + o = owner; + _tos_cache1 = o; + _tos_cache0 = attr; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_SLOT_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + owner = stack_pointer[-1]; + uint16_t index = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **addr = (PyObject **)((char *)owner_o + index); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); + if (attr_o == NULL) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); + if (!increfed) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + _tos_cache1 = o; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_SLOT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + uint16_t index = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **addr = (PyObject **)((char *)owner_o + index); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); + if (attr_o == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); + if (!increfed) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + _tos_cache1 = o; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_SLOT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef o; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + uint16_t index = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + PyObject **addr = (PyObject **)((char *)owner_o + index); + PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); + if (attr_o == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + #ifdef Py_GIL_DISABLED + int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); + if (!increfed) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + #else + attr = PyStackRef_FromPyObjectNew(attr_o); + #endif + STAT_INC(LOAD_ATTR, hit); + o = owner; + _tos_cache2 = o; + _tos_cache1 = attr; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_ATTR_CLASS_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + owner = stack_pointer[-1]; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (!PyType_Check(owner_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_ATTR_CLASS_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (!PyType_Check(owner_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_ATTR_CLASS_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (!PyType_Check(owner_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_ATTR_CLASS_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + owner = _stack_item_2; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (!PyType_Check(owner_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_CLASS_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + attr = PyStackRef_FromPyObjectNew(descr); + stack_pointer[0] = owner; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = owner; + owner = attr; + stack_pointer[-1] = owner; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = attr; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_PROPERTY_FRAME_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef new_frame; + oparg = CURRENT_OPARG(); + owner = stack_pointer[-1]; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *fget = (PyObject *)CURRENT_OPERAND1_64(); + assert((oparg & 1) == 0); + assert(Py_IS_TYPE(fget, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)fget; + if (f->func_version != func_version) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + PyCodeObject *code = (PyCodeObject *)f->func_code; + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(LOAD_ATTR, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(fget), 1, frame); + pushed_frame->localsplus[0] = owner; + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_PROPERTY_FRAME_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef new_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + owner = _stack_item_0; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *fget = (PyObject *)CURRENT_OPERAND1_64(); + assert((oparg & 1) == 0); + assert(Py_IS_TYPE(fget, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)fget; + if (f->func_version != func_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + PyCodeObject *code = (PyCodeObject *)f->func_code; + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(LOAD_ATTR, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(fget), 1, frame); + pushed_frame->localsplus[0] = owner; + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_PROPERTY_FRAME_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef new_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + owner = _stack_item_1; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *fget = (PyObject *)CURRENT_OPERAND1_64(); + assert((oparg & 1) == 0); + assert(Py_IS_TYPE(fget, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)fget; + if (f->func_version != func_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + PyCodeObject *code = (PyCodeObject *)f->func_code; + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(LOAD_ATTR, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(fget), 1, frame); + pushed_frame->localsplus[0] = owner; + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache1 = new_frame; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_PROPERTY_FRAME_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef new_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + owner = _stack_item_2; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *fget = (PyObject *)CURRENT_OPERAND1_64(); + assert((oparg & 1) == 0); + assert(Py_IS_TYPE(fget, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)fget; + if (f->func_version != func_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + PyCodeObject *code = (PyCodeObject *)f->func_code; + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(LOAD_ATTR, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(fget), 1, frame); + pushed_frame->localsplus[0] = owner; + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache2 = new_frame; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef new_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + owner = _stack_item_0; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *getattribute = (PyObject *)CURRENT_OPERAND1_64(); + assert((oparg & 1) == 0); + assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)getattribute; + assert(func_version != 0); + if (f->func_version != func_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + PyCodeObject *code = (PyCodeObject *)f->func_code; + assert(code->co_argcount == 2); + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(LOAD_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked( + tstate, PyStackRef_FromPyObjectNew(f), 2, frame); + pushed_frame->localsplus[0] = owner; + pushed_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name); + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_DORV_NO_DICT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + owner = stack_pointer[-1]; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_dictoffset < 0); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (_PyObject_GetManagedDict(owner_o) || + !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UNLOCK_OBJECT(owner_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_DORV_NO_DICT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_dictoffset < 0); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (_PyObject_GetManagedDict(owner_o) || + !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UNLOCK_OBJECT(owner_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_DORV_NO_DICT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_dictoffset < 0); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (_PyObject_GetManagedDict(owner_o) || + !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UNLOCK_OBJECT(owner_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_DORV_NO_DICT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + owner = _stack_item_2; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_dictoffset < 0); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + if (_PyObject_GetManagedDict(owner_o) || + !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + UNLOCK_OBJECT(owner_o); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_ATTR_INSTANCE_VALUE_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef value; + _PyStackRef o; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + value = _stack_item_0; + uint16_t offset = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + STAT_INC(STORE_ATTR, hit); + assert(_PyObject_GetManagedDict(owner_o) == NULL); + PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); + PyObject *old_value = *value_ptr; + FT_ATOMIC_STORE_PTR_RELEASE(*value_ptr, PyStackRef_AsPyObjectSteal(value)); + if (old_value == NULL) { + PyDictValues *values = _PyObject_InlineValues(owner_o); + Py_ssize_t index = value_ptr - values->values; + _PyDictValues_AddToInsertionOrder(values, index); + } + UNLOCK_OBJECT(owner_o); + o = owner; + stack_pointer[0] = o; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = o; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOCK_OBJECT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + value = stack_pointer[-1]; + if (!LOCK_OBJECT(PyStackRef_AsPyObjectBorrow(value))) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOCK_OBJECT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + if (!LOCK_OBJECT(PyStackRef_AsPyObjectBorrow(value))) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOCK_OBJECT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + value = _stack_item_1; + if (!LOCK_OBJECT(PyStackRef_AsPyObjectBorrow(value))) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOCK_OBJECT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + value = _stack_item_2; + if (!LOCK_OBJECT(PyStackRef_AsPyObjectBorrow(value))) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_ATTR_WITH_HINT_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef value; + _PyStackRef o; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + owner = _stack_item_1; + value = _stack_item_0; + uint16_t hint = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictObject *dict = _PyObject_GetManagedDict(owner_o); + if (dict == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + if (!LOCK_OBJECT(dict)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + assert(PyDict_CheckExact((PyObject *)dict)); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + if (hint >= (size_t)dict->ma_keys->dk_nentries || + dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) { + UNLOCK_OBJECT(dict); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; + if (ep->me_key != name) { + UNLOCK_OBJECT(dict); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + PyObject *old_value = ep->me_value; + if (old_value == NULL) { + UNLOCK_OBJECT(dict); + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + stack_pointer[0] = value; + stack_pointer[1] = owner; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); + UNLOCK_OBJECT(dict); + STAT_INC(STORE_ATTR, hit); + o = owner; + stack_pointer[-2] = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = o; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _STORE_ATTR_SLOT_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef value; + _PyStackRef o; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + value = _stack_item_0; + uint16_t index = (uint16_t)CURRENT_OPERAND0_16(); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (!LOCK_OBJECT(owner_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + char *addr = (char *)owner_o + index; + STAT_INC(STORE_ATTR, hit); + PyObject *old_value = *(PyObject **)addr; + FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); + UNLOCK_OBJECT(owner_o); + o = owner; + stack_pointer[0] = o; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_XDECREF(old_value); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = o; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COMPARE_OP_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert((oparg >> 5) <= Py_GE); + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyObject_RichCompare(left_o, right_o, oparg >> 5); + _PyStackRef tmp = right; + right = PyStackRef_NULL; + stack_pointer[-1] = right; + PyStackRef_CLOSE(tmp); + tmp = left; + left = PyStackRef_NULL; + stack_pointer[-2] = left; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (oparg & 16) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int res_bool = PyObject_IsTrue(res_o); + Py_DECREF(res_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_bool < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = res_bool ? PyStackRef_True : PyStackRef_False; + } + else { + res = PyStackRef_FromPyObjectSteal(res_o); + } + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COMPARE_OP_FLOAT_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + oparg = CURRENT_OPARG(); + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + STAT_INC(COMPARE_OP, hit); + double dleft = PyFloat_AS_DOUBLE(left_o); + double dright = PyFloat_AS_DOUBLE(right_o); + int sign_ish = COMPARISON_BIT(dleft, dright); + l = left; + r = right; + res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COMPARE_OP_FLOAT_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + right = _stack_item_0; + left = stack_pointer[-1]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + STAT_INC(COMPARE_OP, hit); + double dleft = PyFloat_AS_DOUBLE(left_o); + double dright = PyFloat_AS_DOUBLE(right_o); + int sign_ish = COMPARISON_BIT(dleft, dright); + l = left; + r = right; + res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COMPARE_OP_FLOAT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + STAT_INC(COMPARE_OP, hit); + double dleft = PyFloat_AS_DOUBLE(left_o); + double dright = PyFloat_AS_DOUBLE(right_o); + int sign_ish = COMPARISON_BIT(dleft, dright); + l = left; + r = right; + res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COMPARE_OP_INT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(_PyLong_IsCompact((PyLongObject *)left_o)); + assert(_PyLong_IsCompact((PyLongObject *)right_o)); + STAT_INC(COMPARE_OP, hit); + assert(_PyLong_DigitCount((PyLongObject *)left_o) <= 1 && + _PyLong_DigitCount((PyLongObject *)right_o) <= 1); + Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left_o); + Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o); + int sign_ish = COMPARISON_BIT(ileft, iright); + l = left; + r = right; + res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COMPARE_OP_STR_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + STAT_INC(COMPARE_OP, hit); + int eq = _PyUnicode_Equal(left_o, right_o); + assert((oparg >> 5) == Py_EQ || (oparg >> 5) == Py_NE); + l = left; + r = right; + assert(eq == 0 || eq == 1); + assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); + assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); + res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? PyStackRef_True : PyStackRef_False; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _IS_OP_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + oparg = CURRENT_OPARG(); + right = stack_pointer[-1]; + left = stack_pointer[-2]; + int res = Py_Is(PyStackRef_AsPyObjectBorrow(left), PyStackRef_AsPyObjectBorrow(right)) ^ oparg; + b = res ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = b; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _IS_OP_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + right = _stack_item_0; + left = stack_pointer[-1]; + int res = Py_Is(PyStackRef_AsPyObjectBorrow(left), PyStackRef_AsPyObjectBorrow(right)) ^ oparg; + b = res ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = b; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _IS_OP_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + right = _stack_item_1; + left = _stack_item_0; + int res = Py_Is(PyStackRef_AsPyObjectBorrow(left), PyStackRef_AsPyObjectBorrow(right)) ^ oparg; + b = res ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = b; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CONTAINS_OP_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = PySequence_Contains(right_o, left_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = b; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_ANY_SET_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyAnySet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_ANY_SET_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + tos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyAnySet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_ANY_SET_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + tos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyAnySet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_ANY_SET_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + tos = _stack_item_2; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyAnySet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_SET_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PySet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_SET_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + tos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PySet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_SET_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + tos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PySet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_SET_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + tos = _stack_item_2; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PySet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FROZENSET_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + tos = stack_pointer[-1]; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyFrozenSet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FROZENSET_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + tos = _stack_item_0; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyFrozenSet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = tos; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FROZENSET_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + tos = _stack_item_1; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyFrozenSet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = tos; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_FROZENSET_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef tos; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + tos = _stack_item_2; + PyObject *o = PyStackRef_AsPyObjectBorrow(tos); + if (!PyFrozenSet_CheckExact(o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = tos; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CONTAINS_OP_SET_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyAnySet_CheckExact(right_o)); + STAT_INC(CONTAINS_OP, hit); + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = _PySet_Contains((PySetObject *)right_o, left_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = b; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CONTAINS_OP_DICT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyAnyDict_CheckExact(right_o)); + STAT_INC(CONTAINS_OP, hit); + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = PyDict_Contains(right_o, left_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = b; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_EG_MATCH_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef match_type_st; + _PyStackRef exc_value_st; + _PyStackRef rest; + _PyStackRef match; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + match_type_st = _stack_item_1; + exc_value_st = _stack_item_0; + PyObject *exc_value = PyStackRef_AsPyObjectBorrow(exc_value_st); + PyObject *match_type = PyStackRef_AsPyObjectBorrow(match_type_st); + stack_pointer[0] = exc_value_st; + stack_pointer[1] = match_type_st; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyEval_CheckExceptStarTypeValid(tstate, match_type); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = match_type_st; + match_type_st = PyStackRef_NULL; + stack_pointer[-1] = match_type_st; + PyStackRef_CLOSE(tmp); + tmp = exc_value_st; + exc_value_st = PyStackRef_NULL; + stack_pointer[-2] = exc_value_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + PyObject *match_o = NULL; + PyObject *rest_o = NULL; + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = _PyEval_ExceptionGroupMatch(frame, exc_value, match_type, + &match_o, &rest_o); + _PyStackRef tmp = match_type_st; + match_type_st = PyStackRef_NULL; + stack_pointer[-1] = match_type_st; + PyStackRef_CLOSE(tmp); + tmp = exc_value_st; + exc_value_st = PyStackRef_NULL; + stack_pointer[-2] = exc_value_st; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (res < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + assert((match_o == NULL) == (rest_o == NULL)); + if (match_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (!Py_IsNone(match_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyErr_SetHandledException(match_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + rest = PyStackRef_FromPyObjectSteal(rest_o); + match = PyStackRef_FromPyObjectSteal(match_o); + _tos_cache1 = match; + _tos_cache0 = rest; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_EXC_MATCH_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef right; + _PyStackRef left; + _PyStackRef b; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + right = _stack_item_1; + left = _stack_item_0; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyExceptionInstance_Check(left_o)); + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PyEval_CheckExceptTypeValid(tstate, right_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + int res = PyErr_GivenExceptionMatches(left_o, right_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(right); + stack_pointer = _PyFrame_GetStackPointer(frame); + b = res ? PyStackRef_True : PyStackRef_False; + _tos_cache1 = b; + _tos_cache0 = left; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _IMPORT_NAME_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef fromlist; + _PyStackRef level; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + fromlist = _stack_item_1; + level = _stack_item_0; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + PyObject *res_o; + if (!(oparg & 0x02)) { + stack_pointer[0] = level; + stack_pointer[1] = fromlist; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_LazyImportName(tstate, BUILTINS(), GLOBALS(), + LOCALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level), + oparg & 0x01); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + stack_pointer[0] = level; + stack_pointer[1] = fromlist; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_ImportName(tstate, BUILTINS(), GLOBALS(), + LOCALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = fromlist; + fromlist = PyStackRef_NULL; + stack_pointer[-1] = fromlist; + PyStackRef_CLOSE(tmp); + tmp = level; + level = PyStackRef_NULL; + stack_pointer[-2] = level; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _IMPORT_FROM_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef from; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + from = _stack_item_0; + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *res_o; + if (PyLazyImport_CheckExact(PyStackRef_AsPyObjectBorrow(from))) { + stack_pointer[0] = from; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_LazyImportFrom( + tstate, frame, PyStackRef_AsPyObjectBorrow(from), name); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + stack_pointer[0] = from; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_ImportFrom( + tstate, PyStackRef_AsPyObjectBorrow(from), name); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + if (res_o == NULL) { + stack_pointer[-1] = from; + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache1 = res; + _tos_cache0 = from; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _POP_JUMP_IF_FALSE is not a viable micro-op for tier 2 because it is replaced */ + + /* _POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 because it is replaced */ + + case _IS_NONE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef b; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + if (PyStackRef_IsNone(value)) { + b = PyStackRef_True; + } + else { + b = PyStackRef_False; + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef tmp = value; + value = b; + stack_pointer[-1] = value; + PyStackRef_CLOSE(tmp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + } + _tos_cache0 = b; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _JUMP_BACKWARD_NO_INTERRUPT is not a viable micro-op for tier 2 because it is replaced */ + + case _GET_LEN_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef obj; + _PyStackRef len; + _PyStackRef _stack_item_0 = _tos_cache0; + obj = _stack_item_0; + stack_pointer[0] = obj; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_ssize_t len_i = PyObject_Length(PyStackRef_AsPyObjectBorrow(obj)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (len_i < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + PyObject *len_o = PyLong_FromSsize_t(len_i); + if (len_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + len = PyStackRef_FromPyObjectSteal(len_o); + _tos_cache1 = len; + _tos_cache0 = obj; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MATCH_CLASS_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef names; + _PyStackRef type; + _PyStackRef subject; + _PyStackRef attrs; + _PyStackRef s; + _PyStackRef tp; + _PyStackRef n; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + names = _stack_item_2; + type = _stack_item_1; + subject = _stack_item_0; + assert(PyTuple_CheckExact(PyStackRef_AsPyObjectBorrow(names))); + stack_pointer[0] = subject; + stack_pointer[1] = type; + stack_pointer[2] = names; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *attrs_o = _PyEval_MatchClass(tstate, + PyStackRef_AsPyObjectBorrow(subject), + PyStackRef_AsPyObjectBorrow(type), oparg, + PyStackRef_AsPyObjectBorrow(names)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (attrs_o) { + assert(PyTuple_CheckExact(attrs_o)); + attrs = PyStackRef_FromPyObjectSteal(attrs_o); + } + else { + if (_PyErr_Occurred(tstate)) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + attrs = PyStackRef_None; + } + s = subject; + tp = type; + n = names; + _tos_cache2 = n; + _tos_cache1 = tp; + _tos_cache0 = s; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer[-3] = attrs; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MATCH_MAPPING_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef subject; + _PyStackRef res; + subject = stack_pointer[-1]; + int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; + res = match ? PyStackRef_True : PyStackRef_False; + _tos_cache1 = res; + _tos_cache0 = subject; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MATCH_MAPPING_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef subject; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + subject = _stack_item_0; + int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; + res = match ? PyStackRef_True : PyStackRef_False; + _tos_cache1 = res; + _tos_cache0 = subject; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MATCH_MAPPING_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef subject; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + subject = _stack_item_1; + int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; + res = match ? PyStackRef_True : PyStackRef_False; + _tos_cache2 = res; + _tos_cache1 = subject; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MATCH_SEQUENCE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef subject; + _PyStackRef res; + subject = stack_pointer[-1]; + int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; + res = match ? PyStackRef_True : PyStackRef_False; + _tos_cache1 = res; + _tos_cache0 = subject; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MATCH_SEQUENCE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef subject; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + subject = _stack_item_0; + int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; + res = match ? PyStackRef_True : PyStackRef_False; + _tos_cache1 = res; + _tos_cache0 = subject; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MATCH_SEQUENCE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef subject; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + subject = _stack_item_1; + int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; + res = match ? PyStackRef_True : PyStackRef_False; + _tos_cache2 = res; + _tos_cache1 = subject; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MATCH_KEYS_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef keys; + _PyStackRef subject; + _PyStackRef values_or_none; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + keys = _stack_item_1; + subject = _stack_item_0; + stack_pointer[0] = subject; + stack_pointer[1] = keys; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *values_or_none_o = _PyEval_MatchKeys(tstate, + PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(keys)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (values_or_none_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + values_or_none = PyStackRef_FromPyObjectSteal(values_or_none_o); + _tos_cache2 = values_or_none; + _tos_cache1 = keys; + _tos_cache0 = subject; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GET_ITER_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + _PyStackRef iter; + _PyStackRef index_or_null; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + iterable = _stack_item_0; + stack_pointer[0] = iterable; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef result = _PyEval_GetIter(iterable, &index_or_null, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (PyStackRef_IsError(result)) { + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + iter = result; + _tos_cache1 = index_or_null; + _tos_cache0 = iter; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_ITERATOR_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + iterable = stack_pointer[-1]; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->tp_iter != PyObject_SelfIter) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(GET_ITER, hit); + _tos_cache0 = iterable; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_ITERATOR_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + _PyStackRef _stack_item_0 = _tos_cache0; + iterable = _stack_item_0; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->tp_iter != PyObject_SelfIter) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = iterable; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(GET_ITER, hit); + _tos_cache0 = iterable; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_ITERATOR_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + iterable = _stack_item_1; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->tp_iter != PyObject_SelfIter) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = iterable; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(GET_ITER, hit); + _tos_cache1 = iterable; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_ITERATOR_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + iterable = _stack_item_2; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->tp_iter != PyObject_SelfIter) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = iterable; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(GET_ITER, hit); + _tos_cache2 = iterable; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_ITER_VIRTUAL_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + iterable = stack_pointer[-1]; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->_tp_iteritem == NULL) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(GET_ITER, hit); + _tos_cache0 = iterable; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_ITER_VIRTUAL_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + _PyStackRef _stack_item_0 = _tos_cache0; + iterable = _stack_item_0; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->_tp_iteritem == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = iterable; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(GET_ITER, hit); + _tos_cache0 = iterable; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_ITER_VIRTUAL_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + iterable = _stack_item_1; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->_tp_iteritem == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = iterable; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(GET_ITER, hit); + _tos_cache1 = iterable; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_ITER_VIRTUAL_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + iterable = _stack_item_2; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->_tp_iteritem == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = iterable; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(GET_ITER, hit); + _tos_cache2 = iterable; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _PUSH_TAGGED_ZERO_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef zero; + zero = PyStackRef_TagInt(0); + _tos_cache0 = zero; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _PUSH_TAGGED_ZERO_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef zero; + _PyStackRef _stack_item_0 = _tos_cache0; + zero = PyStackRef_TagInt(0); + _tos_cache1 = zero; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _PUSH_TAGGED_ZERO_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef zero; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + zero = PyStackRef_TagInt(0); + _tos_cache2 = zero; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GET_ITER_TRAD_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iterable; + _PyStackRef iter; + _PyStackRef index_or_null; + _PyStackRef _stack_item_0 = _tos_cache0; + iterable = _stack_item_0; + stack_pointer[0] = iterable; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(iterable); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (iter_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + iter = PyStackRef_FromPyObjectSteal(iter_o); + index_or_null = PyStackRef_NULL; + _tos_cache1 = index_or_null; + _tos_cache0 = iter; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _FOR_ITER is not a viable micro-op for tier 2 because it is replaced */ + + case _FOR_ITER_TIER_TWO_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null_or_index = _stack_item_1; + iter = _stack_item_0; + stack_pointer[0] = iter; + stack_pointer[1] = null_or_index; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef item = _PyForIter_VirtualIteratorNext(tstate, frame, iter, &null_or_index); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (!PyStackRef_IsValid(item)) { + if (PyStackRef_IsError(item)) { + stack_pointer[-1] = null_or_index; + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); + } + } + next = item; + _tos_cache2 = next; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_ITER_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + iter = stack_pointer[-2]; + PyObject *expected_type = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != (PyTypeObject *)expected_type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_ITER_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + iter = stack_pointer[-1]; + PyObject *expected_type = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != (PyTypeObject *)expected_type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_ITER_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + iter = _stack_item_0; + PyObject *expected_type = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != (PyTypeObject *)expected_type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TYPE_ITER_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + iter = _stack_item_1; + PyObject *expected_type = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != (PyTypeObject *)expected_type) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } - STAT_INC(BINARY_OP, hit); - assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left)); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); - PyObject *right_o = PyStackRef_AsPyObjectSteal(right); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = _stack_item_2; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _ITER_NEXT_INLINE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + iter = _stack_item_0; + PyObject *iternext_fn = (PyObject *)CURRENT_OPERAND0_64(); + assert(sizeof(iternextfunc) == sizeof(uintptr_t)); + volatile iternextfunc iternext_v = (iternextfunc)iternext_fn; + stack_pointer[0] = iter; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyUnicode_Append(&temp, right_o); + PyObject *item = iternext_v(PyStackRef_AsPyObjectBorrow(iter)); stack_pointer = _PyFrame_GetStackPointer(frame); - *target_local = PyStackRef_FromPyObjectSteal(temp); + if (item == NULL) { + if (_PyErr_Occurred(tstate)) { + if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_MonitorRaise(tstate, frame, frame->instr_ptr); + _PyErr_Clear(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + } + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); + } + } + STAT_INC(FOR_ITER, hit); + next = PyStackRef_FromPyObjectSteal(item); + _tos_cache2 = next; + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_ITER_VIRTUAL_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + iter = stack_pointer[-2]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o)->_tp_iteritem == NULL) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_ITER_VIRTUAL_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + iter = stack_pointer[-1]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o)->_tp_iteritem == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_0; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_ITER_VIRTUAL_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + iter = _stack_item_0; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o)->_tp_iteritem == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_ITER_VIRTUAL_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + iter = _stack_item_1; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o)->_tp_iteritem == NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_NOT_NULL_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + null_or_index = stack_pointer[-1]; + if (PyStackRef_IsNull(null_or_index)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = null_or_index; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_NOT_NULL_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef _stack_item_0 = _tos_cache0; + null_or_index = _stack_item_0; + if (PyStackRef_IsNull(null_or_index)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = null_or_index; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = null_or_index; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_NOT_NULL_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null_or_index = _stack_item_1; + if (PyStackRef_IsNull(null_or_index)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = null_or_index; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = null_or_index; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_TOS_NOT_NULL_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + null_or_index = _stack_item_2; + if (PyStackRef_IsNull(null_or_index)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = null_or_index; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = null_or_index; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _FOR_ITER_VIRTUAL is not a viable micro-op for tier 2 because it is replaced */ + + case _FOR_ITER_VIRTUAL_TIER_TWO_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null_or_index = _stack_item_1; + iter = _stack_item_0; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + stack_pointer[0] = iter; + stack_pointer[1] = null_or_index; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(right_o); + _PyObjectIndexPair next_index = Py_TYPE(iter_o)->_tp_iteritem(iter_o, index); stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(*target_local)) { - JUMP_TO_ERROR(); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); + } } - #if TIER_ONE + next = PyStackRef_FromPyObjectSteal(next_o); + null_or_index = PyStackRef_TagInt(index); + _tos_cache2 = next; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - assert(next_instr->op.code == STORE_FAST); - SKIP_OVER(1); + /* _INSTRUMENTED_FOR_ITER is not a viable micro-op for tier 2 because it is instrumented */ + + case _ITER_CHECK_LIST_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + null_or_index = stack_pointer[-1]; + iter = stack_pointer[-2]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != &PyList_Type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + assert(PyStackRef_IsTaggedInt(null_or_index)); + #ifdef Py_GIL_DISABLED + if (!_Py_IsOwnedByCurrentThread(iter_o) && !_PyObject_GC_IS_SHARED(iter_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } #endif + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_BINARY_OP_EXTEND: { - _PyStackRef right; - _PyStackRef left; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *descr = (PyObject *)CURRENT_OPERAND0(); - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; - assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); - assert(d && d->guard); + case _ITER_CHECK_LIST_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + null_or_index = _stack_item_0; + iter = stack_pointer[-1]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != &PyList_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = null_or_index; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + assert(PyStackRef_IsTaggedInt(null_or_index)); + #ifdef Py_GIL_DISABLED + if (!_Py_IsOwnedByCurrentThread(iter_o) && !_PyObject_GC_IS_SHARED(iter_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = null_or_index; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + #endif + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _ITER_CHECK_LIST_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null_or_index = _stack_item_1; + iter = _stack_item_0; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != &PyList_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + assert(PyStackRef_IsTaggedInt(null_or_index)); + #ifdef Py_GIL_DISABLED + if (!_Py_IsOwnedByCurrentThread(iter_o) && !_PyObject_GC_IS_SHARED(iter_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + #endif + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _ITER_CHECK_LIST_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + null_or_index = _stack_item_2; + iter = _stack_item_1; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != &PyList_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = null_or_index; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + assert(PyStackRef_IsTaggedInt(null_or_index)); + #ifdef Py_GIL_DISABLED + if (!_Py_IsOwnedByCurrentThread(iter_o) && !_PyObject_GC_IS_SHARED(iter_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = null_or_index; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + #endif + _tos_cache2 = null_or_index; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _ITER_JUMP_LIST is not a viable micro-op for tier 2 because it is replaced */ + + case _GUARD_NOT_EXHAUSTED_LIST_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + null_or_index = stack_pointer[-1]; + iter = stack_pointer[-2]; + #ifndef Py_GIL_DISABLED + PyObject *list_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(list_o) == &PyList_Type); + if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyList_GET_SIZE(list_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + #endif + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOT_EXHAUSTED_LIST_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + null_or_index = _stack_item_0; + iter = stack_pointer[-1]; + #ifndef Py_GIL_DISABLED + PyObject *list_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(list_o) == &PyList_Type); + if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyList_GET_SIZE(list_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = null_or_index; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + #endif + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOT_EXHAUSTED_LIST_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null_or_index = _stack_item_1; + iter = _stack_item_0; + #ifndef Py_GIL_DISABLED + PyObject *list_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(list_o) == &PyList_Type); + if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyList_GET_SIZE(list_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + #endif + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOT_EXHAUSTED_LIST_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + null_or_index = _stack_item_2; + iter = _stack_item_1; + #ifndef Py_GIL_DISABLED + PyObject *list_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(list_o) == &PyList_Type); + if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyList_GET_SIZE(list_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = null_or_index; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + #endif + _tos_cache2 = null_or_index; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _ITER_NEXT_LIST is not a viable micro-op for tier 2 because it is replaced */ + + case _ITER_NEXT_LIST_TIER_TWO_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null_or_index = _stack_item_1; + iter = _stack_item_0; + PyObject *list_o = PyStackRef_AsPyObjectBorrow(iter); + assert(PyList_CheckExact(list_o)); + #ifdef Py_GIL_DISABLED + assert(_Py_IsOwnedByCurrentThread((PyObject *)list_o) || + _PyObject_GC_IS_SHARED(list_o)); + STAT_INC(FOR_ITER, hit); + stack_pointer[0] = iter; + stack_pointer[1] = null_or_index; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - int res = d->guard(left_o, right_o); + int result = _PyList_GetItemRefNoLock((PyListObject *)list_o, PyStackRef_UntagInt(null_or_index), &next); stack_pointer = _PyFrame_GetStackPointer(frame); - if (!res) { + if (result <= 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_JUMP_TARGET(); } + #else + assert(PyStackRef_UntagInt(null_or_index) < PyList_GET_SIZE(list_o)); + next = PyStackRef_FromPyObjectNew(PyList_GET_ITEM(list_o, PyStackRef_UntagInt(null_or_index))); + stack_pointer += 2; + #endif + null_or_index = PyStackRef_IncrementTaggedIntNoOverflow(null_or_index); + _tos_cache2 = next; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_EXTEND: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *descr = (PyObject *)CURRENT_OPERAND0(); - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); - _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; - STAT_INC(BINARY_OP, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = d->action(left_o, right_o); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _ITER_CHECK_TUPLE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + null_or_index = stack_pointer[-1]; + iter = stack_pointer[-2]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != &PyTuple_Type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + assert(PyStackRef_IsTaggedInt(null_or_index)); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_SLICE: { - _PyStackRef stop; - _PyStackRef start; - _PyStackRef container; - _PyStackRef res; - stop = stack_pointer[-1]; - start = stack_pointer[-2]; - container = stack_pointer[-3]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); - stack_pointer = _PyFrame_GetStackPointer(frame); - PyObject *res_o; - if (slice == NULL) { - res_o = NULL; - } - else { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); - Py_DECREF(slice); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += 2; - } - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(container); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_ERROR(); + case _ITER_CHECK_TUPLE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + null_or_index = _stack_item_0; + iter = stack_pointer[-1]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != &PyTuple_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = null_or_index; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + assert(PyStackRef_IsTaggedInt(null_or_index)); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_SLICE: { - _PyStackRef stop; - _PyStackRef start; - _PyStackRef container; - _PyStackRef v; - stop = stack_pointer[-1]; - start = stack_pointer[-2]; - container = stack_pointer[-3]; - v = stack_pointer[-4]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); - stack_pointer = _PyFrame_GetStackPointer(frame); - int err; - if (slice == NULL) { - err = 1; - } - else { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), slice, PyStackRef_AsPyObjectBorrow(v)); - Py_DECREF(slice); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += 2; - } - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = container; - container = PyStackRef_NULL; - stack_pointer[-3] = container; - PyStackRef_CLOSE(tmp); - tmp = v; - v = PyStackRef_NULL; - stack_pointer[-4] = v; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -4; - assert(WITHIN_STACK_BOUNDS()); - if (err) { - JUMP_TO_ERROR(); + case _ITER_CHECK_TUPLE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null_or_index = _stack_item_1; + iter = _stack_item_0; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != &PyTuple_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } + assert(PyStackRef_IsTaggedInt(null_or_index)); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBSCR_LIST_INT: { - _PyStackRef sub_st; - _PyStackRef list_st; - _PyStackRef res; - sub_st = stack_pointer[-1]; - list_st = stack_pointer[-2]; - PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); - PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); - assert(PyLong_CheckExact(sub)); - assert(PyList_CheckExact(list)); - if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + case _ITER_CHECK_TUPLE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + null_or_index = _stack_item_2; + iter = _stack_item_1; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(iter_o) != &PyTuple_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = null_or_index; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - #ifdef Py_GIL_DISABLED - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyList_GetItemRef((PyListObject*)list, index); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { + assert(PyStackRef_IsTaggedInt(null_or_index)); + _tos_cache2 = null_or_index; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _ITER_JUMP_TUPLE is not a viable micro-op for tier 2 because it is replaced */ + + case _GUARD_NOT_EXHAUSTED_TUPLE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + null_or_index = stack_pointer[-1]; + iter = stack_pointer[-2]; + PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(tuple_o) == &PyTuple_Type); + if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyTuple_GET_SIZE(tuple_o)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - STAT_INC(BINARY_OP, hit); - res = PyStackRef_FromPyObjectSteal(res_o); - #else - if (index >= PyList_GET_SIZE(list)) { + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOT_EXHAUSTED_TUPLE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + null_or_index = _stack_item_0; + iter = stack_pointer[-1]; + PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(tuple_o) == &PyTuple_Type); + if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyTuple_GET_SIZE(tuple_o)) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = null_or_index; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - STAT_INC(BINARY_OP, hit); - PyObject *res_o = PyList_GET_ITEM(list, index); - assert(res_o != NULL); - res = PyStackRef_FromPyObjectNew(res_o); - #endif - STAT_INC(BINARY_OP, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = list_st; - list_st = res; - stack_pointer[-2] = list_st; - PyStackRef_CLOSE(tmp); - tmp = sub_st; - sub_st = PyStackRef_NULL; - stack_pointer[-1] = sub_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBSCR_LIST_SLICE: { - _PyStackRef sub_st; - _PyStackRef list_st; - _PyStackRef res; - sub_st = stack_pointer[-1]; - list_st = stack_pointer[-2]; - PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); - PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); - assert(PySlice_Check(sub)); - assert(PyList_CheckExact(list)); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyList_SliceSubscript(list, sub); - stack_pointer = _PyFrame_GetStackPointer(frame); - STAT_INC(BINARY_OP, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = sub_st; - sub_st = PyStackRef_NULL; - stack_pointer[-1] = sub_st; - PyStackRef_CLOSE(tmp); - tmp = list_st; - list_st = PyStackRef_NULL; - stack_pointer[-2] = list_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_ERROR(); + case _GUARD_NOT_EXHAUSTED_TUPLE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null_or_index = _stack_item_1; + iter = _stack_item_0; + PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(tuple_o) == &PyTuple_Type); + if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyTuple_GET_SIZE(tuple_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBSCR_STR_INT: { - _PyStackRef sub_st; - _PyStackRef str_st; - _PyStackRef res; - sub_st = stack_pointer[-1]; - str_st = stack_pointer[-2]; - PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); - PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); - assert(PyLong_CheckExact(sub)); - assert(PyUnicode_CheckExact(str)); - if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + case _GUARD_NOT_EXHAUSTED_TUPLE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + null_or_index = _stack_item_2; + iter = _stack_item_1; + PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(tuple_o) == &PyTuple_Type); + if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyTuple_GET_SIZE(tuple_o)) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = null_or_index; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - if (PyUnicode_GET_LENGTH(str) <= index) { + _tos_cache2 = null_or_index; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _ITER_NEXT_TUPLE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + null_or_index = stack_pointer[-1]; + iter = stack_pointer[-2]; + PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(tuple_o) == &PyTuple_Type); + uintptr_t i = PyStackRef_UntagInt(null_or_index); + assert((size_t)i < (size_t)PyTuple_GET_SIZE(tuple_o)); + next = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(tuple_o, i)); + null_or_index = PyStackRef_IncrementTaggedIntNoOverflow(null_or_index); + _tos_cache2 = next; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _ITER_NEXT_TUPLE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + null_or_index = _stack_item_0; + iter = stack_pointer[-1]; + PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(tuple_o) == &PyTuple_Type); + uintptr_t i = PyStackRef_UntagInt(null_or_index); + assert((size_t)i < (size_t)PyTuple_GET_SIZE(tuple_o)); + next = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(tuple_o, i)); + null_or_index = PyStackRef_IncrementTaggedIntNoOverflow(null_or_index); + _tos_cache2 = next; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _ITER_NEXT_TUPLE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null_or_index = _stack_item_1; + iter = _stack_item_0; + PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(tuple_o) == &PyTuple_Type); + uintptr_t i = PyStackRef_UntagInt(null_or_index); + assert((size_t)i < (size_t)PyTuple_GET_SIZE(tuple_o)); + next = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(tuple_o, i)); + null_or_index = PyStackRef_IncrementTaggedIntNoOverflow(null_or_index); + _tos_cache2 = next; + _tos_cache1 = null_or_index; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _ITER_CHECK_RANGE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + iter = stack_pointer[-2]; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(r) != &PyRangeIter_Type) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - Py_UCS4 c = PyUnicode_READ_CHAR(str, index); - if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) { + #ifdef Py_GIL_DISABLED + if (!_PyObject_IsUniquelyReferenced((PyObject *)r)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - STAT_INC(BINARY_OP, hit); - PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); + #endif + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(str_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_FromPyObjectBorrow(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_NOS_TUPLE: { - _PyStackRef nos; - nos = stack_pointer[-2]; - PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyTuple_CheckExact(o)) { + case _ITER_CHECK_RANGE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + iter = stack_pointer[-1]; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(r) != &PyRangeIter_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - break; - } - - case _GUARD_TOS_TUPLE: { - _PyStackRef tos; - tos = stack_pointer[-1]; - PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyTuple_CheckExact(o)) { + #ifdef Py_GIL_DISABLED + if (!_PyObject_IsUniquelyReferenced((PyObject *)r)) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } + #endif + _tos_cache1 = _stack_item_0; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBSCR_TUPLE_INT: { - _PyStackRef sub_st; - _PyStackRef tuple_st; - _PyStackRef res; - sub_st = stack_pointer[-1]; - tuple_st = stack_pointer[-2]; - PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); - PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); - assert(PyLong_CheckExact(sub)); - assert(PyTuple_CheckExact(tuple)); - if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + case _ITER_CHECK_RANGE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + iter = _stack_item_0; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(r) != &PyRangeIter_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - if (index >= PyTuple_GET_SIZE(tuple)) { + #ifdef Py_GIL_DISABLED + if (!_PyObject_IsUniquelyReferenced((PyObject *)r)) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - STAT_INC(BINARY_OP, hit); - PyObject *res_o = PyTuple_GET_ITEM(tuple, index); - assert(res_o != NULL); - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - res = PyStackRef_FromPyObjectNew(res_o); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = tuple_st; - tuple_st = res; - stack_pointer[-1] = tuple_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + #endif + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_NOS_DICT: { - _PyStackRef nos; - nos = stack_pointer[-2]; - PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + case _ITER_CHECK_RANGE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + iter = _stack_item_1; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(r) != &PyRangeIter_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } - break; - } - - case _GUARD_TOS_DICT: { - _PyStackRef tos; - tos = stack_pointer[-1]; - PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyDict_CheckExact(o)) { + #ifdef Py_GIL_DISABLED + if (!_PyObject_IsUniquelyReferenced((PyObject *)r)) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } + #endif + _tos_cache2 = _stack_item_2; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBSCR_DICT: { - _PyStackRef sub_st; - _PyStackRef dict_st; - _PyStackRef res; - sub_st = stack_pointer[-1]; - dict_st = stack_pointer[-2]; - PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); - PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - assert(PyDict_CheckExact(dict)); - STAT_INC(BINARY_OP, hit); - PyObject *res_o; - _PyFrame_SetStackPointer(frame, stack_pointer); - int rc = PyDict_GetItemRef(dict, sub, &res_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (rc == 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_SetKeyError(sub); - stack_pointer = _PyFrame_GetStackPointer(frame); + /* _ITER_JUMP_RANGE is not a viable micro-op for tier 2 because it is replaced */ + + case _GUARD_NOT_EXHAUSTED_RANGE_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + iter = stack_pointer[-2]; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(r) == &PyRangeIter_Type); + if (r->len <= 0) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = sub_st; - sub_st = PyStackRef_NULL; - stack_pointer[-1] = sub_st; - PyStackRef_CLOSE(tmp); - tmp = dict_st; - dict_st = PyStackRef_NULL; - stack_pointer[-2] = dict_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (rc <= 0) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBSCR_CHECK_FUNC: { - _PyStackRef container; - _PyStackRef getitem; - container = stack_pointer[-2]; - PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(container)); - if (!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - PyHeapTypeObject *ht = (PyHeapTypeObject *)tp; - PyObject *getitem_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(ht->_spec_cache.getitem); - if (getitem_o == NULL) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - assert(PyFunction_Check(getitem_o)); - uint32_t cached_version = FT_ATOMIC_LOAD_UINT32_RELAXED(ht->_spec_cache.getitem_version); - if (((PyFunctionObject *)getitem_o)->func_version != cached_version) { + case _GUARD_NOT_EXHAUSTED_RANGE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + iter = stack_pointer[-1]; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(r) == &PyRangeIter_Type); + if (r->len <= 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - PyCodeObject *code = (PyCodeObject *)PyFunction_GET_CODE(getitem_o); - assert(code->co_argcount == 2); - if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + _tos_cache1 = _stack_item_0; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_NOT_EXHAUSTED_RANGE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + iter = _stack_item_0; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(r) == &PyRangeIter_Type); + if (r->len <= 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - getitem = PyStackRef_FromPyObjectNew(getitem_o); - STAT_INC(BINARY_OP, hit); - stack_pointer[0] = getitem; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBSCR_INIT_CALL: { - _PyStackRef getitem; - _PyStackRef sub; - _PyStackRef container; - _PyStackRef new_frame; - getitem = stack_pointer[-1]; - sub = stack_pointer[-2]; - container = stack_pointer[-3]; - _PyInterpreterFrame* pushed_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); - pushed_frame->localsplus[0] = container; - pushed_frame->localsplus[1] = sub; - frame->return_offset = 6 ; - new_frame = PyStackRef_Wrap(pushed_frame); - stack_pointer[-3] = new_frame; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_NOT_EXHAUSTED_RANGE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + iter = _stack_item_1; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(r) == &PyRangeIter_Type); + if (r->len <= 0) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = iter; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LIST_APPEND: { - _PyStackRef v; - _PyStackRef list; - oparg = CURRENT_OPARG(); - v = stack_pointer[-1]; - list = stack_pointer[-2 - (oparg-1)]; - int err = _PyList_AppendTakeRef((PyListObject *)PyStackRef_AsPyObjectBorrow(list), - PyStackRef_AsPyObjectSteal(v)); - if (err < 0) { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _ITER_NEXT_RANGE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef next; + iter = stack_pointer[-2]; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(r) == &PyRangeIter_Type); + #ifdef Py_GIL_DISABLED + assert(_PyObject_IsUniquelyReferenced((PyObject *)r)); + #endif + assert(r->len > 0); + long value = r->start; + r->start = value + r->step; + r->len--; + PyObject *res = PyLong_FromLong(value); + if (res == NULL) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + next = PyStackRef_FromPyObjectSteal(res); + _tos_cache2 = next; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _SET_ADD: { - _PyStackRef v; - _PyStackRef set; - oparg = CURRENT_OPARG(); - v = stack_pointer[-1]; - set = stack_pointer[-2 - (oparg-1)]; - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PySet_AddTakeRef((PySetObject *)PyStackRef_AsPyObjectBorrow(set), - PyStackRef_AsPyObjectSteal(v)); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _ITER_NEXT_RANGE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + iter = stack_pointer[-1]; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(r) == &PyRangeIter_Type); + #ifdef Py_GIL_DISABLED + assert(_PyObject_IsUniquelyReferenced((PyObject *)r)); + #endif + assert(r->len > 0); + long value = r->start; + r->start = value + r->step; + r->len--; + PyObject *res = PyLong_FromLong(value); + if (res == NULL) { + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } + next = PyStackRef_FromPyObjectSteal(res); + _tos_cache2 = next; + _tos_cache1 = _stack_item_0; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_SUBSCR: { - _PyStackRef sub; - _PyStackRef container; - _PyStackRef v; - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - v = stack_pointer[-3]; - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), PyStackRef_AsPyObjectBorrow(sub), PyStackRef_AsPyObjectBorrow(v)); - _PyStackRef tmp = sub; - sub = PyStackRef_NULL; - stack_pointer[-1] = sub; - PyStackRef_CLOSE(tmp); - tmp = container; - container = PyStackRef_NULL; - stack_pointer[-2] = container; - PyStackRef_CLOSE(tmp); - tmp = v; - v = PyStackRef_NULL; - stack_pointer[-3] = v; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - if (err) { + case _ITER_NEXT_RANGE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef next; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + iter = _stack_item_0; + _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(r) == &PyRangeIter_Type); + #ifdef Py_GIL_DISABLED + assert(_PyObject_IsUniquelyReferenced((PyObject *)r)); + #endif + assert(r->len > 0); + long value = r->start; + r->start = value + r->step; + r->len--; + PyObject *res = PyLong_FromLong(value); + if (res == NULL) { + stack_pointer[0] = iter; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } + next = PyStackRef_FromPyObjectSteal(res); + _tos_cache2 = next; + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_SUBSCR_LIST_INT: { - _PyStackRef sub_st; - _PyStackRef list_st; - _PyStackRef value; - sub_st = stack_pointer[-1]; - list_st = stack_pointer[-2]; - value = stack_pointer[-3]; - PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); - PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); - assert(PyLong_CheckExact(sub)); - assert(PyList_CheckExact(list)); - if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + case _FOR_ITER_GEN_FRAME_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef gen_frame; + oparg = CURRENT_OPARG(); + iter = stack_pointer[-2]; + PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(gen) != &PyGen_Type) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; - if (!LOCK_OBJECT(list)) { + if (!gen_try_set_executing((PyGenObject *)gen)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - if (index >= PyList_GET_SIZE(list)) { - UNLOCK_OBJECT(list); - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - } - STAT_INC(STORE_SUBSCR, hit); - PyObject *old_value = PyList_GET_ITEM(list, index); - FT_ATOMIC_STORE_PTR_RELEASE(_PyList_ITEMS(list)[index], - PyStackRef_AsPyObjectSteal(value)); - assert(old_value != NULL); - UNLOCK_OBJECT(list); - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(list_st); - Py_DECREF(old_value); - stack_pointer = _PyFrame_GetStackPointer(frame); + STAT_INC(FOR_ITER, hit); + _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; + _PyFrame_StackPush(pushed_frame, PyStackRef_None); + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + pushed_frame->previous = frame; + frame->return_offset = (uint16_t)( 2u + oparg); + gen_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache2 = gen_frame; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_SUBSCR_DICT: { - _PyStackRef sub; - _PyStackRef dict_st; - _PyStackRef value; - sub = stack_pointer[-1]; - dict_st = stack_pointer[-2]; - value = stack_pointer[-3]; - PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - assert(PyDict_CheckExact(dict)); - STAT_INC(STORE_SUBSCR, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PyDict_SetItem_Take2((PyDictObject *)dict, - PyStackRef_AsPyObjectSteal(sub), - PyStackRef_AsPyObjectSteal(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(dict_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) { - JUMP_TO_ERROR(); + case _FOR_ITER_GEN_FRAME_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef gen_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + iter = stack_pointer[-1]; + PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(gen) != &PyGen_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + if (!gen_try_set_executing((PyGenObject *)gen)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } + STAT_INC(FOR_ITER, hit); + _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; + _PyFrame_StackPush(pushed_frame, PyStackRef_None); + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + pushed_frame->previous = frame; + frame->return_offset = (uint16_t)( 2u + oparg); + gen_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache2 = gen_frame; + _tos_cache1 = _stack_item_0; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _DELETE_SUBSCR: { - _PyStackRef sub; - _PyStackRef container; - sub = stack_pointer[-1]; - container = stack_pointer[-2]; - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyObject_DelItem(PyStackRef_AsPyObjectBorrow(container), - PyStackRef_AsPyObjectBorrow(sub)); - _PyStackRef tmp = sub; - sub = PyStackRef_NULL; - stack_pointer[-1] = sub; - PyStackRef_CLOSE(tmp); - tmp = container; - container = PyStackRef_NULL; - stack_pointer[-2] = container; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (err) { - JUMP_TO_ERROR(); + case _FOR_ITER_GEN_FRAME_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef iter; + _PyStackRef gen_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + iter = _stack_item_0; + PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); + if (Py_TYPE(gen) != &PyGen_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + if (!gen_try_set_executing((PyGenObject *)gen)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } + STAT_INC(FOR_ITER, hit); + _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; + _PyFrame_StackPush(pushed_frame, PyStackRef_None); + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + pushed_frame->previous = frame; + frame->return_offset = (uint16_t)( 2u + oparg); + gen_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache2 = gen_frame; + _tos_cache1 = _stack_item_1; + _tos_cache0 = iter; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_INTRINSIC_1: { - _PyStackRef value; - _PyStackRef res; - oparg = CURRENT_OPARG(); - value = stack_pointer[-1]; - assert(oparg <= MAX_INTRINSIC_1); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, PyStackRef_AsPyObjectBorrow(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _INSERT_NULL_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self; + _PyStackRef *method_and_self; + _PyStackRef _stack_item_0 = _tos_cache0; + self = _stack_item_0; + method_and_self = &stack_pointer[0]; + method_and_self[1] = self; + method_and_self[0] = PyStackRef_NULL; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_INTRINSIC_2: { - _PyStackRef value1_st; - _PyStackRef value2_st; - _PyStackRef res; + case _LOAD_SPECIAL_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *method_and_self; oparg = CURRENT_OPARG(); - value1_st = stack_pointer[-1]; - value2_st = stack_pointer[-2]; - assert(oparg <= MAX_INTRINSIC_2); - PyObject *value1 = PyStackRef_AsPyObjectBorrow(value1_st); - PyObject *value2 = PyStackRef_AsPyObjectBorrow(value2_st); + method_and_self = &stack_pointer[-2]; + PyObject *name = _Py_SpecialMethods[oparg].name; _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyIntrinsics_BinaryFunctions[oparg].func(tstate, value2, value1); - _PyStackRef tmp = value1_st; - value1_st = PyStackRef_NULL; - stack_pointer[-1] = value1_st; - PyStackRef_CLOSE(tmp); - tmp = value2_st; - value2_st = PyStackRef_NULL; - stack_pointer[-2] = value2_st; - PyStackRef_CLOSE(tmp); + int err = _PyObject_LookupSpecialMethod(name, method_and_self); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { + if (err <= 0) { + if (err == 0) { + PyObject *owner = PyStackRef_AsPyObjectBorrow(method_and_self[1]); + _PyFrame_SetStackPointer(frame, stack_pointer); + const char *errfmt = _PyEval_SpecialMethodCanSuggest(owner, oparg) + ? _Py_SpecialMethods[oparg].error_suggestion + : _Py_SpecialMethods[oparg].error; + stack_pointer = _PyFrame_GetStackPointer(frame); + assert(!_PyErr_Occurred(tstate)); + assert(errfmt != NULL); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, errfmt, owner); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _RETURN_VALUE: { - _PyStackRef retval; + case _WITH_EXCEPT_START_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef val; + _PyStackRef lasti; + _PyStackRef exit_self; + _PyStackRef exit_func; _PyStackRef res; - retval = stack_pointer[-1]; - assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - _PyStackRef temp = PyStackRef_MakeHeapSafe(retval); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - assert(STACK_LEVEL() == 0); - _Py_LeaveRecursiveCallPy(tstate); - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - stack_pointer = _PyFrame_GetStackPointer(frame); - LOAD_IP(frame->return_offset); - res = temp; - LLTRACE_RESUME_FRAME(); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _GET_AITER: { - _PyStackRef obj; - _PyStackRef iter; - obj = stack_pointer[-1]; - unaryfunc getter = NULL; - PyObject *obj_o = PyStackRef_AsPyObjectBorrow(obj); - PyObject *iter_o; - PyTypeObject *type = Py_TYPE(obj_o); - if (type->tp_as_async != NULL) { - getter = type->tp_as_async->am_aiter; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + val = _stack_item_2; + lasti = _stack_item_0; + exit_self = stack_pointer[-1]; + exit_func = stack_pointer[-2]; + PyObject *exc, *tb; + PyObject *val_o = PyStackRef_AsPyObjectBorrow(val); + PyObject *exit_func_o = PyStackRef_AsPyObjectBorrow(exit_func); + assert(val_o && PyExceptionInstance_Check(val_o)); + exc = PyExceptionInstance_Class(val_o); + PyObject *original_tb = tb = PyException_GetTraceback(val_o); + if (tb == NULL) { + tb = Py_None; } - if (getter == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_Format(tstate, PyExc_TypeError, - "'async for' requires an object with " - "__aiter__ method, got %.100s", - type->tp_name); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + assert(PyStackRef_IsTaggedInt(lasti)); + (void)lasti; + PyObject* res_o; + { + PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb}; + int has_self = !PyStackRef_IsNull(exit_self); + stack_pointer[0] = lasti; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = val; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(obj); + res_o = PyObject_Vectorcall(exit_func_o, stack + 2 - has_self, + (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); } _PyFrame_SetStackPointer(frame, stack_pointer); - iter_o = (*getter)(obj_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(obj); + Py_XDECREF(original_tb); stack_pointer = _PyFrame_GetStackPointer(frame); - if (iter_o == NULL) { + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - if (Py_TYPE(iter_o)->tp_as_async == NULL || - Py_TYPE(iter_o)->tp_as_async->am_anext == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_Format(tstate, PyExc_TypeError, - "'async for' received an object from __aiter__ " - "that does not implement __anext__: %.100s", - Py_TYPE(iter_o)->tp_name); - Py_DECREF(iter_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache2 = res; + _tos_cache1 = val; + _tos_cache0 = _stack_item_1; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _PUSH_EXC_INFO_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef exc; + _PyStackRef prev_exc; + _PyStackRef new_exc; + exc = stack_pointer[-1]; + _PyErr_StackItem *exc_info = tstate->exc_info; + if (exc_info->exc_value != NULL) { + prev_exc = PyStackRef_FromPyObjectSteal(exc_info->exc_value); } - iter = PyStackRef_FromPyObjectSteal(iter_o); - stack_pointer[0] = iter; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + else { + prev_exc = PyStackRef_None; + } + assert(PyStackRef_ExceptionInstanceCheck(exc)); + exc_info->exc_value = PyStackRef_AsPyObjectNew(exc); + new_exc = exc; + _tos_cache1 = new_exc; + _tos_cache0 = prev_exc; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GET_ANEXT: { - _PyStackRef aiter; - _PyStackRef awaitable; - aiter = stack_pointer[-1]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *awaitable_o = _PyEval_GetANext(PyStackRef_AsPyObjectBorrow(aiter)); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (awaitable_o == NULL) { - JUMP_TO_ERROR(); + case _PUSH_EXC_INFO_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef exc; + _PyStackRef prev_exc; + _PyStackRef new_exc; + _PyStackRef _stack_item_0 = _tos_cache0; + exc = _stack_item_0; + _PyErr_StackItem *exc_info = tstate->exc_info; + if (exc_info->exc_value != NULL) { + prev_exc = PyStackRef_FromPyObjectSteal(exc_info->exc_value); } - awaitable = PyStackRef_FromPyObjectSteal(awaitable_o); - stack_pointer[0] = awaitable; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + else { + prev_exc = PyStackRef_None; + } + assert(PyStackRef_ExceptionInstanceCheck(exc)); + exc_info->exc_value = PyStackRef_AsPyObjectNew(exc); + new_exc = exc; + _tos_cache1 = new_exc; + _tos_cache0 = prev_exc; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GET_AWAITABLE: { - _PyStackRef iterable; - _PyStackRef iter; - oparg = CURRENT_OPARG(); - iterable = stack_pointer[-1]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *iter_o = _PyEval_GetAwaitable(PyStackRef_AsPyObjectBorrow(iterable), oparg); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(iterable); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (iter_o == NULL) { - JUMP_TO_ERROR(); + case _PUSH_EXC_INFO_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef exc; + _PyStackRef prev_exc; + _PyStackRef new_exc; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + exc = _stack_item_1; + _PyErr_StackItem *exc_info = tstate->exc_info; + if (exc_info->exc_value != NULL) { + prev_exc = PyStackRef_FromPyObjectSteal(exc_info->exc_value); } - iter = PyStackRef_FromPyObjectSteal(iter_o); - stack_pointer[0] = iter; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + else { + prev_exc = PyStackRef_None; + } + assert(PyStackRef_ExceptionInstanceCheck(exc)); + exc_info->exc_value = PyStackRef_AsPyObjectNew(exc); + new_exc = exc; + _tos_cache2 = new_exc; + _tos_cache1 = prev_exc; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _SEND is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + owner = stack_pointer[-1]; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + PyDictValues *ivs = _PyObject_InlineValues(owner_o); + if (!FT_ATOMIC_LOAD_UINT8(ivs->valid)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - case _SEND_GEN_FRAME: { - _PyStackRef v; - _PyStackRef receiver; - _PyStackRef gen_frame; - oparg = CURRENT_OPARG(); - v = stack_pointer[-1]; - receiver = stack_pointer[-2]; - PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver); - if (Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type) { + case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + PyDictValues *ivs = _PyObject_InlineValues(owner_o); + if (!FT_ATOMIC_LOAD_UINT8(ivs->valid)) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - if (gen->gi_frame_state >= FRAME_EXECUTING) { + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + PyDictValues *ivs = _PyObject_InlineValues(owner_o); + if (!FT_ATOMIC_LOAD_UINT8(ivs->valid)) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - STAT_INC(SEND, hit); - _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; - _PyFrame_StackPush(pushed_frame, PyStackRef_MakeHeapSafe(v)); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - assert( 2 + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)( 2 + oparg); - pushed_frame->previous = frame; - gen_frame = PyStackRef_Wrap(pushed_frame); - stack_pointer[-1] = gen_frame; + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _YIELD_VALUE: { - _PyStackRef retval; - _PyStackRef value; - oparg = CURRENT_OPARG(); - retval = stack_pointer[-1]; - assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - frame->instr_ptr++; - PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); - assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); - assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; - _PyStackRef temp = retval; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - tstate->exc_info = gen->gi_exc_state.previous_item; - gen->gi_exc_state.previous_item = NULL; - _Py_LeaveRecursiveCallPy(tstate); - _PyInterpreterFrame *gen_frame = frame; - frame = tstate->current_frame = frame->previous; - gen_frame->previous = NULL; - assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); - #if TIER_ONE - assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE || - frame->instr_ptr->op.code == INSTRUMENTED_INSTRUCTION || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == SEND || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == FOR_ITER || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == INTERPRETER_EXIT || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == ENTER_EXECUTOR); - #endif - stack_pointer = _PyFrame_GetStackPointer(frame); - LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); - value = PyStackRef_MakeHeapSafe(temp); - LLTRACE_RESUME_FRAME(); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + owner = _stack_item_2; + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); + PyDictValues *ivs = _PyObject_InlineValues(owner_o); + if (!FT_ATOMIC_LOAD_UINT8(ivs->valid)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_EXCEPT: { - _PyStackRef exc_value; - exc_value = stack_pointer[-1]; - _PyErr_StackItem *exc_info = tstate->exc_info; - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_XSETREF(exc_info->exc_value, - PyStackRef_IsNone(exc_value) - ? NULL : PyStackRef_AsPyObjectSteal(exc_value)); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _LOAD_ATTR_METHOD_WITH_VALUES_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; + oparg = CURRENT_OPARG(); + owner = stack_pointer[-1]; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg & 1); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + _tos_cache1 = self; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_COMMON_CONSTANT: { - _PyStackRef value; + case _LOAD_ATTR_METHOD_WITH_VALUES_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); - assert(oparg < NUM_COMMON_CONSTANTS); - value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + owner = _stack_item_0; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg & 1); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + _tos_cache1 = self; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_BUILD_CLASS: { - _PyStackRef bc; - PyObject *bc_o; - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - JUMP_TO_ERROR(); - } - if (bc_o == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_SetString(tstate, PyExc_NameError, - "__build_class__ not found"); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); - } - bc = PyStackRef_FromPyObjectSteal(bc_o); - stack_pointer[0] = bc; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_ATTR_METHOD_WITH_VALUES_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + owner = _stack_item_1; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg & 1); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + _tos_cache2 = self; + _tos_cache1 = attr; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_NAME: { - _PyStackRef v; + case _LOAD_ATTR_METHOD_NO_DICT_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; oparg = CURRENT_OPARG(); - v = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *ns = LOCALS(); - int err; - if (ns == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_Format(tstate, PyExc_SystemError, - "no locals found when storing %R", name); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(v); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); - } - if (PyDict_CheckExact(ns)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - err = PyDict_SetItem(ns, name, PyStackRef_AsPyObjectBorrow(v)); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - err = PyObject_SetItem(ns, name, PyStackRef_AsPyObjectBorrow(v)); - stack_pointer = _PyFrame_GetStackPointer(frame); - } + owner = stack_pointer[-1]; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg & 1); + assert(Py_TYPE(PyStackRef_AsPyObjectBorrow(owner))->tp_dictoffset == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + _tos_cache1 = self; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(v); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) { - JUMP_TO_ERROR(); - } + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _DELETE_NAME: { + case _LOAD_ATTR_METHOD_NO_DICT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *ns = LOCALS(); - int err; - if (ns == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_Format(tstate, PyExc_SystemError, - "no locals when deleting %R", name); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - err = PyObject_DelItem(ns, name); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, - name); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); - } + owner = _stack_item_0; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg & 1); + assert(Py_TYPE(PyStackRef_AsPyObjectBorrow(owner))->tp_dictoffset == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + _tos_cache1 = self; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_ATTR_METHOD_NO_DICT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + owner = _stack_item_1; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg & 1); + assert(Py_TYPE(PyStackRef_AsPyObjectBorrow(owner))->tp_dictoffset == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + _tos_cache2 = self; + _tos_cache1 = attr; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _UNPACK_SEQUENCE: { - _PyStackRef seq; - _PyStackRef *top; + case _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); - seq = stack_pointer[-1]; - top = &stack_pointer[-1 + oparg]; - PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + owner = _stack_item_0; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert((oparg & 1) == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); _PyFrame_SetStackPointer(frame, stack_pointer); - int res = _PyEval_UnpackIterableStackRef(tstate, seq_o, oparg, -1, top); - Py_DECREF(seq_o); + PyStackRef_CLOSE(owner); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res == 0) { - JUMP_TO_ERROR(); - } - stack_pointer += oparg; - assert(WITHIN_STACK_BOUNDS()); + attr = PyStackRef_FromPyObjectNew(descr); + _tos_cache0 = attr; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _UNPACK_SEQUENCE_TWO_TUPLE: { - _PyStackRef seq; - _PyStackRef val1; - _PyStackRef val0; + case _LOAD_ATTR_NONDESCRIPTOR_NO_DICT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); - seq = stack_pointer[-1]; - assert(oparg == 2); - PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); - assert(PyTuple_CheckExact(seq_o)); - if (PyTuple_GET_SIZE(seq_o) != 2) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - STAT_INC(UNPACK_SEQUENCE, hit); - val0 = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq_o, 0)); - val1 = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(seq_o, 1)); - stack_pointer[-1] = val1; - stack_pointer[0] = val0; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + owner = _stack_item_0; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert((oparg & 1) == 0); + assert(Py_TYPE(PyStackRef_AsPyObjectBorrow(owner))->tp_dictoffset == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(seq); + PyStackRef_CLOSE(owner); stack_pointer = _PyFrame_GetStackPointer(frame); + attr = PyStackRef_FromPyObjectNew(descr); + _tos_cache0 = attr; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _UNPACK_SEQUENCE_TUPLE: { - _PyStackRef seq; - _PyStackRef *values; - oparg = CURRENT_OPARG(); - seq = stack_pointer[-1]; - values = &stack_pointer[-1]; - PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); - assert(PyTuple_CheckExact(seq_o)); - if (PyTuple_GET_SIZE(seq_o) != oparg) { + case _CHECK_ATTR_METHOD_LAZY_DICT_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + owner = stack_pointer[-1]; + uint16_t dictoffset = (uint16_t)CURRENT_OPERAND0_16(); + char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); + if (dict != NULL) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - STAT_INC(UNPACK_SEQUENCE, hit); - PyObject **items = _PyTuple_ITEMS(seq_o); - for (int i = oparg; --i >= 0; ) { - *values++ = PyStackRef_FromPyObjectNew(items[i]); - } - stack_pointer += -1 + oparg; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(seq); - stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _UNPACK_SEQUENCE_LIST: { - _PyStackRef seq; - _PyStackRef *values; - oparg = CURRENT_OPARG(); - seq = stack_pointer[-1]; - values = &stack_pointer[-1]; - PyObject *seq_o = PyStackRef_AsPyObjectBorrow(seq); - assert(PyList_CheckExact(seq_o)); - if (!LOCK_OBJECT(seq_o)) { + case _CHECK_ATTR_METHOD_LAZY_DICT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + owner = _stack_item_0; + uint16_t dictoffset = (uint16_t)CURRENT_OPERAND0_16(); + char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); + if (dict != NULL) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - if (PyList_GET_SIZE(seq_o) != oparg) { - UNLOCK_OBJECT(seq_o); - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - } - STAT_INC(UNPACK_SEQUENCE, hit); - PyObject **items = _PyList_ITEMS(seq_o); - for (int i = oparg; --i >= 0; ) { - *values++ = PyStackRef_FromPyObjectNew(items[i]); - } - UNLOCK_OBJECT(seq_o); - stack_pointer += -1 + oparg; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(seq); - stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = owner; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _UNPACK_EX: { - _PyStackRef seq; - _PyStackRef *top; - oparg = CURRENT_OPARG(); - seq = stack_pointer[-1]; - top = &stack_pointer[(oparg & 0xFF) + (oparg >> 8)]; - PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int res = _PyEval_UnpackIterableStackRef(tstate, seq_o, oparg & 0xFF, oparg >> 8, top); - Py_DECREF(seq_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res == 0) { - JUMP_TO_ERROR(); + case _CHECK_ATTR_METHOD_LAZY_DICT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + owner = _stack_item_1; + uint16_t dictoffset = (uint16_t)CURRENT_OPERAND0_16(); + char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); + if (dict != NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - stack_pointer += 1 + (oparg & 0xFF) + (oparg >> 8); - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = owner; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_ATTR: { + case _CHECK_ATTR_METHOD_LAZY_DICT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef owner; - _PyStackRef v; - oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; - v = stack_pointer[-2]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyObject_SetAttr(PyStackRef_AsPyObjectBorrow(owner), - name, PyStackRef_AsPyObjectBorrow(v)); - _PyStackRef tmp = owner; - owner = PyStackRef_NULL; - stack_pointer[-1] = owner; - PyStackRef_CLOSE(tmp); - tmp = v; - v = PyStackRef_NULL; - stack_pointer[-2] = v; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (err) { - JUMP_TO_ERROR(); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + owner = _stack_item_2; + uint16_t dictoffset = (uint16_t)CURRENT_OPERAND0_16(); + char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; + PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); + if (dict != NULL) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); } + _tos_cache2 = owner; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _DELETE_ATTR: { + case _LOAD_ATTR_METHOD_LAZY_DICT_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; oparg = CURRENT_OPARG(); owner = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyObject_DelAttr(PyStackRef_AsPyObjectBorrow(owner), name); - stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg & 1); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + _tos_cache1 = self; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) { - JUMP_TO_ERROR(); - } + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_GLOBAL: { - _PyStackRef v; + case _LOAD_ATTR_METHOD_LAZY_DICT_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); - v = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(v); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) { - JUMP_TO_ERROR(); - } + owner = _stack_item_0; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg & 1); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + _tos_cache1 = self; + _tos_cache0 = attr; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _DELETE_GLOBAL: { + case _LOAD_ATTR_METHOD_LAZY_DICT_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef owner; + _PyStackRef attr; + _PyStackRef self; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; oparg = CURRENT_OPARG(); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyDict_Pop(GLOBALS(), name, NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - JUMP_TO_ERROR(); - } - if (err == 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); - } + owner = _stack_item_1; + PyObject *descr = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg & 1); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = PyStackRef_FromPyObjectNew(descr); + self = owner; + _tos_cache2 = self; + _tos_cache1 = attr; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_LOCALS: { - _PyStackRef locals; - PyObject *l = LOCALS(); - if (l == NULL) { + case _MAYBE_EXPAND_METHOD_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_or_null; + _PyStackRef callable; + oparg = CURRENT_OPARG(); + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *self = ((PyMethodObject *)callable_o)->im_self; + self_or_null = PyStackRef_FromPyObjectNew(self); + PyObject *method = ((PyMethodObject *)callable_o)->im_func; + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(method); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_SetString(tstate, PyExc_SystemError, - "no locals found"); + PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); } - locals = PyStackRef_FromPyObjectNew(l); - stack_pointer[0] = locals; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _LOAD_FROM_DICT_OR_GLOBALS is not a viable micro-op for tier 2 because it has both popping and not-popping errors */ + /* _DO_CALL is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ - case _LOAD_NAME: { - _PyStackRef v; - oparg = CURRENT_OPARG(); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *v_o = _PyEval_LoadName(tstate, frame, name); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (v_o == NULL) { - JUMP_TO_ERROR(); - } - v = PyStackRef_FromPyObjectSteal(v_o); - stack_pointer[0] = v; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; - } + /* _MONITOR_CALL is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ - case _LOAD_GLOBAL: { - _PyStackRef *res; + case _PY_FRAME_GENERAL_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef new_frame; oparg = CURRENT_OPARG(); - res = &stack_pointer[0]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + args--; + total_args++; + } + assert(Py_TYPE(callable_o) == &PyFunction_Type); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_LoadGlobalStackRef(GLOBALS(), BUILTINS(), name, res); + _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( + tstate, callable, locals, + args, total_args, NULL, frame + ); stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(*res)) { + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (temp == NULL) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + new_frame = PyStackRef_Wrap(temp); + _tos_cache0 = new_frame; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _PUSH_NULL_CONDITIONAL: { - _PyStackRef *null; + case _CHECK_FUNCTION_VERSION_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; oparg = CURRENT_OPARG(); - null = &stack_pointer[0]; - if (oparg & 1) { - null[0] = PyStackRef_NULL; - } - stack_pointer += (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _GUARD_GLOBALS_VERSION: { - uint16_t version = (uint16_t)CURRENT_OPERAND0(); - PyDictObject *dict = (PyDictObject *)GLOBALS(); - if (!PyDict_CheckExact(dict)) { + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyFunction_Check(callable_o)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - assert(DK_IS_UNICODE(keys)); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_GLOBAL_MODULE: { - _PyStackRef res; - uint16_t version = (uint16_t)CURRENT_OPERAND0(); - uint16_t index = (uint16_t)CURRENT_OPERAND1(); - PyDictObject *dict = (PyDictObject *)GLOBALS(); - if (!PyDict_CheckExact(dict)) { + case _CHECK_FUNCTION_VERSION_INLINE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *callable_o = (PyObject *)CURRENT_OPERAND1_64(); + assert(PyFunction_Check(callable_o)); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_FUNCTION_VERSION_INLINE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *callable_o = (PyObject *)CURRENT_OPERAND1_64(); + assert(PyFunction_Check(callable_o)); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - assert(DK_IS_UNICODE(keys)); - PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); - assert(index < DK_SIZE(keys)); - PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); - if (res_o == NULL) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_FUNCTION_VERSION_INLINE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *callable_o = (PyObject *)CURRENT_OPERAND1_64(); + assert(PyFunction_Check(callable_o)); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - #if Py_GIL_DISABLED - int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res); - if (!increfed) { + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_FUNCTION_VERSION_INLINE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *callable_o = (PyObject *)CURRENT_OPERAND1_64(); + assert(PyFunction_Check(callable_o)); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } - #else - res = PyStackRef_FromPyObjectNew(res_o); - #endif - STAT_INC(LOAD_GLOBAL, hit); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_GLOBAL_BUILTINS: { - _PyStackRef res; - uint16_t version = (uint16_t)CURRENT_OPERAND0(); - uint16_t index = (uint16_t)CURRENT_OPERAND1(); - PyDictObject *dict = (PyDictObject *)BUILTINS(); - if (!PyDict_CheckExact(dict)) { + case _CHECK_METHOD_VERSION_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + _PyStackRef callable; + oparg = CURRENT_OPARG(); + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (Py_TYPE(callable_o) != &PyMethod_Type) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != version) { + PyObject *func = ((PyMethodObject *)callable_o)->im_func; + if (!PyFunction_Check(func)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - assert(DK_IS_UNICODE(keys)); - PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); - PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); - if (res_o == NULL) { + if (((PyFunctionObject *)func)->func_version != func_version) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - #if Py_GIL_DISABLED - int increfed = _Py_TryIncrefCompareStackRef(&entries[index].me_value, res_o, &res); - if (!increfed) { + if (!PyStackRef_IsNull(null)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - #else - res = PyStackRef_FromPyObjectNew(res_o); - #endif - STAT_INC(LOAD_GLOBAL, hit); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _DELETE_FAST: { + case _EXPAND_METHOD_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_or_null; + _PyStackRef callable; oparg = CURRENT_OPARG(); - _PyStackRef v = GETLOCAL(oparg); - if (PyStackRef_IsNull(v)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError, - UNBOUNDLOCAL_ERROR_MSG, - PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) - ); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); - } - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = PyStackRef_NULL; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(PyStackRef_IsNull(self_or_null)); + assert(Py_TYPE(callable_o) == &PyMethod_Type); + self_or_null = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + assert(PyStackRef_FunctionCheck(callable)); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); + PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _MAKE_CELL: { + case _CHECK_IS_NOT_PY_CALLABLE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; oparg = CURRENT_OPARG(); - PyObject *initial = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); - PyObject *cell = PyCell_New(initial); - if (cell == NULL) { - JUMP_TO_ERROR(); + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (PyFunction_Check(callable_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = PyStackRef_FromPyObjectSteal(cell); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + if (Py_TYPE(callable_o) == &PyMethod_Type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _DELETE_DEREF: { + case _CALL_NON_PY_GENERAL_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef res; oparg = CURRENT_OPARG(); - PyObject *cell = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); - PyObject *oldobj = PyCell_SwapTakeRef((PyCellObject *)cell, NULL); - if (oldobj == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + #if TIER_ONE + assert(opcode != INSTRUMENTED_CALL); + #endif + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; } _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(oldobj); + PyObject *res_o = _Py_VectorCall_StackRefSteal( + callable, + arguments, + total_args, + PyStackRef_NULL); stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_FROM_DICT_OR_DEREF: { - _PyStackRef class_dict_st; - _PyStackRef value; + case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + _PyStackRef callable; oparg = CURRENT_OPARG(); - class_dict_st = stack_pointer[-1]; - PyObject *value_o; - PyObject *name; - PyObject *class_dict = PyStackRef_AsPyObjectBorrow(class_dict_st); - assert(class_dict); - assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus); - name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyMapping_GetOptionalItem(class_dict, name, &value_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - JUMP_TO_ERROR(); + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + if (!PyStackRef_IsNull(null)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - if (!value_o) { - PyCellObject *cell = (PyCellObject *)PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); - value_o = PyCell_GetRef(cell); - if (value_o == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); - } + if (Py_TYPE(PyStackRef_AsPyObjectBorrow(callable)) != &PyMethod_Type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(class_dict_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - value = PyStackRef_FromPyObjectSteal(value_o); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_DEREF: { - _PyStackRef value; + case _INIT_CALL_BOUND_METHOD_EXACT_ARGS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_or_null; + _PyStackRef callable; oparg = CURRENT_OPARG(); - PyCellObject *cell = (PyCellObject *)PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(PyStackRef_IsNull(self_or_null)); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + STAT_INC(CALL, hit); + self_or_null = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; _PyFrame_SetStackPointer(frame, stack_pointer); - value = _PyCell_GetStackRef(cell); + PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(value)) { - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_PEP_523_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + if (IS_PEP523_HOOKED(tstate)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_PEP_523_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + if (IS_PEP523_HOOKED(tstate)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_PEP_523_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + if (IS_PEP523_HOOKED(tstate)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_DEREF: { - _PyStackRef v; - oparg = CURRENT_OPARG(); - v = stack_pointer[-1]; - PyCellObject *cell = (PyCellObject *)PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg)); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyCell_SetTakeRef(cell, PyStackRef_AsPyObjectSteal(v)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _CHECK_PEP_523_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + if (IS_PEP523_HOOKED(tstate)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _COPY_FREE_VARS: { + case _CHECK_FUNCTION_EXACT_ARGS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_or_null; + _PyStackRef callable; oparg = CURRENT_OPARG(); - PyCodeObject *co = _PyFrame_GetCode(frame); - assert(PyStackRef_FunctionCheck(frame->f_funcobj)); - PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); - PyObject *closure = func->func_closure; - assert(oparg == co->co_nfreevars); - int offset = co->co_nlocalsplus - oparg; - for (int i = 0; i < oparg; ++i) { - PyObject *o = PyTuple_GET_ITEM(closure, i); - frame->localsplus[offset + i] = PyStackRef_FromPyObjectNew(o); + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(PyFunction_Check(callable_o)); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + PyCodeObject *code = (PyCodeObject *)func->func_code; + if (code->co_argcount != oparg + (!PyStackRef_IsNull(self_or_null))) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BUILD_STRING: { - _PyStackRef *pieces; - _PyStackRef str; + case _CHECK_STACK_SPACE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; oparg = CURRENT_OPARG(); - pieces = &stack_pointer[-oparg]; - STACKREFS_TO_PYOBJECTS(pieces, oparg, pieces_o); - if (CONVERSION_FAILED(pieces_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = pieces[_i]; - pieces[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - PyObject *str_o = _PyUnicode_JoinArray(&_Py_STR(empty), pieces_o, oparg); - STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = pieces[_i]; - pieces[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyFunctionObject *func = (PyFunctionObject *)callable_o; + PyCodeObject *code = (PyCodeObject *)func->func_code; + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); - if (str_o == NULL) { - JUMP_TO_ERROR(); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_RECURSION_REMAINING_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + if (tstate->py_recursion_remaining <= 1) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - str = PyStackRef_FromPyObjectSteal(str_o); - stack_pointer[0] = str; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BUILD_INTERPOLATION: { - _PyStackRef *format; - _PyStackRef str; - _PyStackRef value; - _PyStackRef interpolation; - oparg = CURRENT_OPARG(); - format = &stack_pointer[-(oparg & 1)]; - str = stack_pointer[-1 - (oparg & 1)]; - value = stack_pointer[-2 - (oparg & 1)]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - PyObject *str_o = PyStackRef_AsPyObjectBorrow(str); - int conversion = oparg >> 2; - PyObject *format_o; - if (oparg & 1) { - format_o = PyStackRef_AsPyObjectBorrow(format[0]); + case _CHECK_RECURSION_REMAINING_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + if (tstate->py_recursion_remaining <= 1) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } - else { - format_o = &_Py_STR(empty); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_RECURSION_REMAINING_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + if (tstate->py_recursion_remaining <= 1) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *interpolation_o = _PyInterpolation_Build(value_o, str_o, conversion, format_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (oparg & 1) { - stack_pointer += -(oparg & 1); - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(format[0]); - stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_RECURSION_REMAINING_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + if (tstate->py_recursion_remaining <= 1) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); } - else { - stack_pointer += -(oparg & 1); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _INIT_CALL_PY_EXACT_ARGS_0_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef new_frame; + oparg = 0; + assert(oparg == CURRENT_OPARG()); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int has_self = !PyStackRef_IsNull(self_or_null); + STAT_INC(CALL, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); + _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; + pushed_frame->localsplus[0] = self_or_null; + for (int i = 0; i < oparg; i++) { + first_non_self_local[i] = args[i]; } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(str); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (interpolation_o == NULL) { - JUMP_TO_ERROR(); + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _INIT_CALL_PY_EXACT_ARGS_1_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef new_frame; + oparg = 1; + assert(oparg == CURRENT_OPARG()); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int has_self = !PyStackRef_IsNull(self_or_null); + STAT_INC(CALL, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); + _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; + pushed_frame->localsplus[0] = self_or_null; + for (int i = 0; i < oparg; i++) { + first_non_self_local[i] = args[i]; } - interpolation = PyStackRef_FromPyObjectSteal(interpolation_o); - stack_pointer[0] = interpolation; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BUILD_TEMPLATE: { - _PyStackRef interpolations; - _PyStackRef strings; - _PyStackRef template; - interpolations = stack_pointer[-1]; - strings = stack_pointer[-2]; - PyObject *strings_o = PyStackRef_AsPyObjectBorrow(strings); - PyObject *interpolations_o = PyStackRef_AsPyObjectBorrow(interpolations); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(interpolations); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(strings); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (template_o == NULL) { - JUMP_TO_ERROR(); + case _INIT_CALL_PY_EXACT_ARGS_2_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef new_frame; + oparg = 2; + assert(oparg == CURRENT_OPARG()); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int has_self = !PyStackRef_IsNull(self_or_null); + STAT_INC(CALL, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); + _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; + pushed_frame->localsplus[0] = self_or_null; + for (int i = 0; i < oparg; i++) { + first_non_self_local[i] = args[i]; } - template = PyStackRef_FromPyObjectSteal(template_o); - stack_pointer[0] = template; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BUILD_TUPLE: { - _PyStackRef *values; - _PyStackRef tup; - oparg = CURRENT_OPARG(); - values = &stack_pointer[-oparg]; - PyObject *tup_o = _PyTuple_FromStackRefStealOnSuccess(values, oparg); - if (tup_o == NULL) { - JUMP_TO_ERROR(); + case _INIT_CALL_PY_EXACT_ARGS_3_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef new_frame; + oparg = 3; + assert(oparg == CURRENT_OPARG()); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int has_self = !PyStackRef_IsNull(self_or_null); + STAT_INC(CALL, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); + _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; + pushed_frame->localsplus[0] = self_or_null; + for (int i = 0; i < oparg; i++) { + first_non_self_local[i] = args[i]; } - tup = PyStackRef_FromPyObjectStealMortal(tup_o); - stack_pointer[-oparg] = tup; - stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BUILD_LIST: { - _PyStackRef *values; - _PyStackRef list; - oparg = CURRENT_OPARG(); - values = &stack_pointer[-oparg]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *list_o = _PyList_FromStackRefStealOnSuccess(values, oparg); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (list_o == NULL) { - JUMP_TO_ERROR(); + case _INIT_CALL_PY_EXACT_ARGS_4_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef new_frame; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int has_self = !PyStackRef_IsNull(self_or_null); + STAT_INC(CALL, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); + _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; + pushed_frame->localsplus[0] = self_or_null; + for (int i = 0; i < oparg; i++) { + first_non_self_local[i] = args[i]; } - list = PyStackRef_FromPyObjectStealMortal(list_o); - stack_pointer[-oparg] = list; - stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LIST_EXTEND: { - _PyStackRef iterable_st; - _PyStackRef list_st; + case _INIT_CALL_PY_EXACT_ARGS_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef new_frame; oparg = CURRENT_OPARG(); - iterable_st = stack_pointer[-1]; - list_st = stack_pointer[-2 - (oparg-1)]; - PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); - PyObject *iterable = PyStackRef_AsPyObjectBorrow(iterable_st); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (none_val == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int matches = _PyErr_ExceptionMatches(tstate, PyExc_TypeError); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (matches && - (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) - { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_Clear(tstate); - _PyErr_Format(tstate, PyExc_TypeError, - "Value after * must be an iterable, not %.200s", - Py_TYPE(iterable)->tp_name); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(iterable_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int has_self = !PyStackRef_IsNull(self_or_null); + STAT_INC(CALL, hit); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); + _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; + pushed_frame->localsplus[0] = self_or_null; + for (int i = 0; i < oparg; i++) { + first_non_self_local[i] = args[i]; } - assert(Py_IsNone(none_val)); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(iterable_st); - stack_pointer = _PyFrame_GetStackPointer(frame); + new_frame = PyStackRef_Wrap(pushed_frame); + _tos_cache0 = new_frame; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _SET_UPDATE: { - _PyStackRef iterable; - _PyStackRef set; - oparg = CURRENT_OPARG(); - iterable = stack_pointer[-1]; - set = stack_pointer[-2 - (oparg-1)]; - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PySet_Update(PyStackRef_AsPyObjectBorrow(set), - PyStackRef_AsPyObjectBorrow(iterable)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _PUSH_FRAME_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef new_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + new_frame = _stack_item_0; + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(iterable); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - JUMP_TO_ERROR(); - } + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BUILD_SET: { - _PyStackRef *values; - _PyStackRef set; - oparg = CURRENT_OPARG(); - values = &stack_pointer[-oparg]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *set_o = PySet_New(NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (set_o == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = values[_i]; - values[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - int err = 0; - for (Py_ssize_t i = 0; i < oparg; i++) { - _PyStackRef value = values[i]; - values[i] = PyStackRef_NULL; - if (err == 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - err = _PySet_AddTakeRef((PySetObject *)set_o, PyStackRef_AsPyObjectSteal(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - } - if (err) { - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(set_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); + case _GUARD_NOS_NULL_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + null = stack_pointer[-2]; + if (!PyStackRef_IsNull(null)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - set = PyStackRef_FromPyObjectStealMortal(set_o); - stack_pointer[-oparg] = set; - stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = null; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BUILD_MAP: { - _PyStackRef *values; - _PyStackRef map; - oparg = CURRENT_OPARG(); - values = &stack_pointer[-oparg*2]; - STACKREFS_TO_PYOBJECTS(values, oparg*2, values_o); - if (CONVERSION_FAILED(values_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg*2; --_i >= 0;) { - tmp = values[_i]; - values[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg*2; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *map_o = _PyDict_FromItems( - values_o, 2, - values_o+1, 2, - oparg); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(values_o); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg*2; --_i >= 0;) { - tmp = values[_i]; - values[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg*2; - assert(WITHIN_STACK_BOUNDS()); - if (map_o == NULL) { - JUMP_TO_ERROR(); + case _GUARD_NOS_NULL_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + _PyStackRef _stack_item_0 = _tos_cache0; + null = stack_pointer[-1]; + if (!PyStackRef_IsNull(null)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } - map = PyStackRef_FromPyObjectStealMortal(map_o); - stack_pointer[0] = map; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = _stack_item_0; + _tos_cache0 = null; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _SETUP_ANNOTATIONS: { - PyObject *ann_dict; - if (LOCALS() == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_Format(tstate, PyExc_SystemError, - "no locals found when setting up annotations"); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - JUMP_TO_ERROR(); - } - if (ann_dict == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - ann_dict = PyDict_New(); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (ann_dict == NULL) { - JUMP_TO_ERROR(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), - ann_dict); - Py_DECREF(ann_dict); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) { - JUMP_TO_ERROR(); - } - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(ann_dict); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _GUARD_NOS_NULL_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null = _stack_item_0; + if (!PyStackRef_IsNull(null)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = null; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } + _tos_cache1 = _stack_item_1; + _tos_cache0 = null; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _DICT_UPDATE: { - _PyStackRef update; - _PyStackRef dict; - oparg = CURRENT_OPARG(); - update = stack_pointer[-1]; - dict = stack_pointer[-2 - (oparg - 1)]; - PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); - PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyDict_Update(dict_o, update_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int matches = _PyErr_ExceptionMatches(tstate, PyExc_AttributeError); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (matches) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object is not a mapping", - Py_TYPE(update_o)->tp_name); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(update); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); + case _GUARD_NOS_NULL_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + null = _stack_item_1; + if (!PyStackRef_IsNull(null)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = null; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(update); - stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache2 = _stack_item_2; + _tos_cache1 = null; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _DICT_MERGE: { - _PyStackRef update; - _PyStackRef dict; - _PyStackRef callable; - oparg = CURRENT_OPARG(); - update = stack_pointer[-1]; - dict = stack_pointer[-2 - (oparg - 1)]; - callable = stack_pointer[-5 - (oparg - 1)]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); - PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PyDict_MergeEx(dict_o, update_o, 2); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_FormatKwargsError(tstate, callable_o, update_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(update); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); + case _GUARD_THIRD_NULL_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + null = stack_pointer[-3]; + if (!PyStackRef_IsNull(null)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(update); - stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = null; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _MAP_ADD: { - _PyStackRef value; - _PyStackRef key; - _PyStackRef dict_st; - oparg = CURRENT_OPARG(); - value = stack_pointer[-1]; - key = stack_pointer[-2]; - dict_st = stack_pointer[-3 - (oparg - 1)]; - PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - assert(PyDict_CheckExact(dict)); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PyDict_SetItem_Take2( - (PyDictObject *)dict, - PyStackRef_AsPyObjectSteal(key), - PyStackRef_AsPyObjectSteal(value) - ); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); + case _GUARD_THIRD_NULL_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + _PyStackRef _stack_item_0 = _tos_cache0; + null = stack_pointer[-2]; + if (!PyStackRef_IsNull(null)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = null; + SET_CURRENT_CACHED_VALUES(3); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_SUPER_ATTR_ATTR: { - _PyStackRef self_st; - _PyStackRef class_st; - _PyStackRef global_super_st; - _PyStackRef attr_st; - oparg = CURRENT_OPARG(); - self_st = stack_pointer[-1]; - class_st = stack_pointer[-2]; - global_super_st = stack_pointer[-3]; - PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); - PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); - PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); - assert(!(oparg & 1)); - if (global_super != (PyObject *)&PySuper_Type) { + case _GUARD_THIRD_NULL_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + null = stack_pointer[-1]; + if (!PyStackRef_IsNull(null)) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - if (!PyType_Check(class)) { + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = null; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_THIRD_NULL_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + null = _stack_item_0; + if (!PyStackRef_IsNull(null)) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = null; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } - STAT_INC(LOAD_SUPER_ATTR, hit); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *attr = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL); - _PyStackRef tmp = self_st; - self_st = PyStackRef_NULL; - stack_pointer[-1] = self_st; - PyStackRef_CLOSE(tmp); - tmp = class_st; - class_st = PyStackRef_NULL; - stack_pointer[-2] = class_st; - PyStackRef_CLOSE(tmp); - tmp = global_super_st; - global_super_st = PyStackRef_NULL; - stack_pointer[-3] = global_super_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - if (attr == NULL) { - JUMP_TO_ERROR(); - } - attr_st = PyStackRef_FromPyObjectSteal(attr); - stack_pointer[0] = attr_st; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = null; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_SUPER_ATTR_METHOD: { - _PyStackRef self_st; - _PyStackRef class_st; - _PyStackRef global_super_st; - _PyStackRef attr; - _PyStackRef self_or_null; - oparg = CURRENT_OPARG(); - self_st = stack_pointer[-1]; - class_st = stack_pointer[-2]; - global_super_st = stack_pointer[-3]; - PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); - PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); - PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); - assert(oparg & 1); - if (global_super != (PyObject *)&PySuper_Type) { + case _GUARD_CALLABLE_TYPE_1_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyType_Type) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - if (!PyType_Check(class)) { + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_TYPE_1_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + callable = stack_pointer[-2]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyType_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - STAT_INC(LOAD_SUPER_ATTR, hit); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); - PyTypeObject *cls = (PyTypeObject *)class; - int method_found = 0; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *attr_o = _PySuper_Lookup(cls, self, name, - Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (attr_o == NULL) { - JUMP_TO_ERROR(); - } - if (method_found) { - self_or_null = self_st; - } else { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(self_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - self_or_null = PyStackRef_NULL; - stack_pointer += 1; + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_TYPE_1_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + callable = stack_pointer[-1]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyType_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = global_super_st; - global_super_st = self_or_null; - stack_pointer[-2] = global_super_st; - PyStackRef_CLOSE(tmp); - tmp = class_st; - class_st = PyStackRef_NULL; - stack_pointer[-1] = class_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_TYPE_1_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + callable = _stack_item_0; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyType_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CALL_TYPE_1_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef res; + _PyStackRef a; + oparg = CURRENT_OPARG(); + arg = stack_pointer[-1]; + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + assert(oparg == 1); + STAT_INC(CALL, hit); + a = arg; + res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); + _tos_cache1 = a; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CALL_TYPE_1_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef res; + _PyStackRef a; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + arg = _stack_item_0; + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + assert(oparg == 1); + STAT_INC(CALL, hit); + a = arg; + res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); + _tos_cache1 = a; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - attr = PyStackRef_FromPyObjectSteal(attr_o); - stack_pointer[0] = attr; - stack_pointer[1] = self_or_null; - stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_ATTR: { - _PyStackRef owner; - _PyStackRef *attr; - _PyStackRef *self_or_null; + case _CALL_TYPE_1_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef res; + _PyStackRef a; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; - attr = &stack_pointer[-1]; - self_or_null = &stack_pointer[0]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); - if (oparg & 1) { - *attr = PyStackRef_NULL; - _PyFrame_SetStackPointer(frame, stack_pointer); - int is_meth = _PyObject_GetMethodStackRef(tstate, PyStackRef_AsPyObjectBorrow(owner), name, attr); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (is_meth) { - assert(!PyStackRef_IsNull(*attr)); - self_or_null[0] = owner; - } - else { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(*attr)) { - JUMP_TO_ERROR(); - } - self_or_null[0] = PyStackRef_NULL; - stack_pointer += 1; - } - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (attr_o == NULL) { - JUMP_TO_ERROR(); - } - *attr = PyStackRef_FromPyObjectSteal(attr_o); - stack_pointer += 1; - } - stack_pointer += (oparg&1); - assert(WITHIN_STACK_BOUNDS()); + arg = _stack_item_1; + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + assert(oparg == 1); + STAT_INC(CALL, hit); + a = arg; + res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); + _tos_cache1 = a; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_TYPE_VERSION: { - _PyStackRef owner; - owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); - PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - assert(type_version != 0); - if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + case _CALL_TYPE_1_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef res; + _PyStackRef a; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + arg = _stack_item_2; + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + assert(oparg == 1); + STAT_INC(CALL, hit); + a = arg; + res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); + _tos_cache1 = a; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_STR_1_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyUnicode_Type) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_TYPE_VERSION_AND_LOCK: { - _PyStackRef owner; - owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - assert(type_version != 0); - if (!LOCK_OBJECT(owner_o)) { + case _GUARD_CALLABLE_STR_1_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + callable = stack_pointer[-2]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyUnicode_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - PyTypeObject *tp = Py_TYPE(owner_o); - if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { - UNLOCK_OBJECT(owner_o); - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - } + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_MANAGED_OBJECT_HAS_VALUES: { - _PyStackRef owner; - owner = stack_pointer[-1]; - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - assert(Py_TYPE(owner_o)->tp_dictoffset < 0); - assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (!FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { + case _GUARD_CALLABLE_STR_1_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + callable = stack_pointer[-1]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyUnicode_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_ATTR_INSTANCE_VALUE: { - _PyStackRef owner; - _PyStackRef attr; - owner = stack_pointer[-1]; - uint16_t offset = (uint16_t)CURRENT_OPERAND0(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); - PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr); - if (attr_o == NULL) { + case _GUARD_CALLABLE_STR_1_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + callable = _stack_item_0; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyUnicode_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } - #ifdef Py_GIL_DISABLED - int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr); - if (!increfed) { - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - } - #else - attr = PyStackRef_FromPyObjectNew(attr_o); - #endif - STAT_INC(LOAD_ATTR, hit); - stack_pointer[-1] = attr; + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CALL_STR_1_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef res; + _PyStackRef a; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + arg = _stack_item_2; + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + assert(oparg == 1); + STAT_INC(CALL, hit); + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = arg; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); + PyObject *res_o = PyObject_Str(arg_o); stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + a = arg; + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache1 = a; + _tos_cache0 = res; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_ATTR_MODULE: { - _PyStackRef owner; - _PyStackRef attr; - owner = stack_pointer[-1]; - uint32_t dict_version = (uint32_t)CURRENT_OPERAND0(); - uint16_t index = (uint16_t)CURRENT_OPERAND1(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - if (Py_TYPE(owner_o)->tp_getattro != PyModule_Type.tp_getattro) { + case _GUARD_CALLABLE_TUPLE_1_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyTuple_Type) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner_o)->md_dict; - assert(dict != NULL); - PyDictKeysObject *keys = FT_ATOMIC_LOAD_PTR_ACQUIRE(dict->ma_keys); - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != dict_version) { + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_TUPLE_1_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + callable = stack_pointer[-2]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyTuple_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - assert(keys->dk_kind == DICT_KEYS_UNICODE); - assert(index < FT_ATOMIC_LOAD_SSIZE_RELAXED(keys->dk_nentries)); - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(keys) + index; - PyObject *attr_o = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_value); - if (attr_o == NULL) { + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_TUPLE_1_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + callable = stack_pointer[-1]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyTuple_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - #ifdef Py_GIL_DISABLED - int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); - if (!increfed) { - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_TUPLE_1_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + callable = _stack_item_0; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (callable_o != (PyObject *)&PyTuple_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); } - #else - attr = PyStackRef_FromPyObjectNew(attr_o); - #endif - STAT_INC(LOAD_ATTR, hit); - stack_pointer[-1] = attr; + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CALL_TUPLE_1_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef res; + _PyStackRef a; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + arg = _stack_item_2; + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + assert(oparg == 1); + STAT_INC(CALL, hit); + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = arg; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); + PyObject *res_o = PySequence_Tuple(arg_o); stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + a = arg; + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache1 = a; + _tos_cache0 = res; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_ATTR_WITH_HINT: { - _PyStackRef owner; - _PyStackRef attr; + case _CHECK_OBJECT_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_or_null; + _PyStackRef callable; oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; - uint16_t hint = (uint16_t)CURRENT_OPERAND0(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictObject *dict = _PyObject_GetManagedDict(owner_o); - if (dict == NULL) { + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + uint32_t type_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyStackRef_IsNull(self_or_null)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - PyDictKeysObject *dk = FT_ATOMIC_LOAD_PTR(dict->ma_keys); - assert(PyDict_CheckExact((PyObject *)dict)); - #ifdef Py_GIL_DISABLED - if (!_Py_IsOwnedByCurrentThread((PyObject *)dict) && !_PyObject_GC_IS_SHARED(dict)) { + if (!PyType_Check(callable_o)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - #endif - PyObject *attr_o; - if (hint >= (size_t)FT_ATOMIC_LOAD_SSIZE_RELAXED(dk->dk_nentries)) { - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - } - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); - if (dk->dk_kind != DICT_KEYS_UNICODE) { - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - } - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dk) + hint; - if (FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_key) != name) { - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - } - attr_o = FT_ATOMIC_LOAD_PTR(ep->me_value); - if (attr_o == NULL) { - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - } - STAT_INC(LOAD_ATTR, hit); - #ifdef Py_GIL_DISABLED - int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr); - if (!increfed) { - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + PyTypeObject *tp = (PyTypeObject *)callable_o; + if (FT_ATOMIC_LOAD_UINT32_RELAXED(tp->tp_version_tag) != type_version) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - #else - attr = PyStackRef_FromPyObjectNew(attr_o); - #endif - stack_pointer[-1] = attr; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); - stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_ATTR_SLOT: { - _PyStackRef owner; - _PyStackRef attr; - owner = stack_pointer[-1]; - uint16_t index = (uint16_t)CURRENT_OPERAND0(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - PyObject **addr = (PyObject **)((char *)owner_o + index); - PyObject *attr_o = FT_ATOMIC_LOAD_PTR(*addr); - if (attr_o == NULL) { + case _ALLOCATE_OBJECT_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_or_null; + _PyStackRef callable; + oparg = CURRENT_OPARG(); + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(PyStackRef_IsNull(self_or_null)); + assert(PyType_Check(callable_o)); + PyTypeObject *tp = (PyTypeObject *)callable_o; + assert(tp->tp_new == PyBaseObject_Type.tp_new); + assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); + assert(tp->tp_alloc == PyType_GenericAlloc); + PyHeapTypeObject *cls = (PyHeapTypeObject *)callable_o; + PyFunctionObject *init_func = (PyFunctionObject *)FT_ATOMIC_LOAD_PTR_ACQUIRE(cls->_spec_cache.init); + PyCodeObject *code = (PyCodeObject *)init_func->func_code; + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - #ifdef Py_GIL_DISABLED - int increfed = _Py_TryIncrefCompareStackRef(addr, attr_o, &attr); - if (!increfed) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *self_o = PyType_GenericAlloc(tp, 0); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (self_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } - #else - attr = PyStackRef_FromPyObjectNew(attr_o); - #endif - STAT_INC(LOAD_ATTR, hit); + self_or_null = PyStackRef_FromPyObjectSteal(self_o); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(init_func); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = owner; - owner = attr; - stack_pointer[-1] = owner; - PyStackRef_CLOSE(tmp); + PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_ATTR_CLASS: { - _PyStackRef owner; - owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - if (!PyType_Check(owner_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - assert(type_version != 0); - if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + case _CREATE_INIT_FRAME_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self; + _PyStackRef init; + _PyStackRef init_frame; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self = stack_pointer[-1 - oparg]; + init = stack_pointer[-2 - oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( + tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK); + assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE); + shim->localsplus[0] = PyStackRef_DUP(self); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( + tstate, init, NULL, args-1, oparg+1, NULL, shim); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (temp == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FrameClearAndPop(tstate, shim); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } + frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL; + tstate->py_recursion_remaining--; + init_frame = PyStackRef_Wrap(temp); + _tos_cache0 = init_frame; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_ATTR_CLASS: { - _PyStackRef owner; - _PyStackRef attr; - owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND0(); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - attr = PyStackRef_FromPyObjectNew(descr); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = owner; - owner = attr; - stack_pointer[-1] = owner; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _EXIT_INIT_CHECK_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef should_be_none; + _PyStackRef _stack_item_0 = _tos_cache0; + should_be_none = _stack_item_0; + if (!PyStackRef_IsNone(should_be_none)) { + stack_pointer[0] = should_be_none; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyErr_Format(PyExc_TypeError, + "__init__() should return None, not '%.200s'", + Py_TYPE(PyStackRef_AsPyObjectBorrow(should_be_none))->tp_name); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_ATTR_PROPERTY_FRAME: { - _PyStackRef owner; - _PyStackRef new_frame; + case _GUARD_CALLABLE_BUILTIN_CLASS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; - PyObject *fget = (PyObject *)CURRENT_OPERAND0(); - assert((oparg & 1) == 0); - assert(Py_IS_TYPE(fget, &PyFunction_Type)); - PyFunctionObject *f = (PyFunctionObject *)fget; - PyCodeObject *code = (PyCodeObject *)f->func_code; - if ((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - if (code->co_kwonlyargcount) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - if (code->co_argcount != 1) { + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyType_Check(callable_o)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + PyTypeObject *tp = (PyTypeObject *)callable_o; + if (tp->tp_vectorcall == NULL) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - STAT_INC(LOAD_ATTR, hit); - _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, PyStackRef_FromPyObjectNew(fget), 1, frame); - pushed_frame->localsplus[0] = owner; - new_frame = PyStackRef_Wrap(pushed_frame); - stack_pointer[-1] = new_frame; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 because it has too many cache entries */ - - case _GUARD_DORV_NO_DICT: { - _PyStackRef owner; - owner = stack_pointer[-1]; - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - assert(Py_TYPE(owner_o)->tp_dictoffset < 0); - assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - if (_PyObject_GetManagedDict(owner_o) || - !FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(owner_o)->valid)) { - UNLOCK_OBJECT(owner_o); - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _CALL_BUILTIN_CLASS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; } - break; - } - - case _STORE_ATTR_INSTANCE_VALUE: { - _PyStackRef owner; - _PyStackRef value; - owner = stack_pointer[-1]; - value = stack_pointer[-2]; - uint16_t offset = (uint16_t)CURRENT_OPERAND0(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - STAT_INC(STORE_ATTR, hit); - assert(_PyObject_GetManagedDict(owner_o) == NULL); - PyObject **value_ptr = (PyObject**)(((char *)owner_o) + offset); - PyObject *old_value = *value_ptr; - FT_ATOMIC_STORE_PTR_RELEASE(*value_ptr, PyStackRef_AsPyObjectSteal(value)); - if (old_value == NULL) { - PyDictValues *values = _PyObject_InlineValues(owner_o); - Py_ssize_t index = value_ptr - values->values; - _PyDictValues_AddToInsertionOrder(values, index); + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _Py_CallBuiltinClass_StackRef( + callable, + arguments, + total_args); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } - UNLOCK_OBJECT(owner_o); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); - Py_XDECREF(old_value); + PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _STORE_ATTR_WITH_HINT: { - _PyStackRef owner; - _PyStackRef value; + case _GUARD_CALLABLE_BUILTIN_O_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_or_null; + _PyStackRef callable; oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; - value = stack_pointer[-2]; - uint16_t hint = (uint16_t)CURRENT_OPERAND0(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictObject *dict = _PyObject_GetManagedDict(owner_o); - if (dict == NULL) { + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyCFunction_CheckExact(callable_o)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - if (!LOCK_OBJECT(dict)) { + if (PyCFunction_GET_FLAGS(callable_o) != METH_O) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - assert(PyDict_CheckExact((PyObject *)dict)); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - if (hint >= (size_t)dict->ma_keys->dk_nentries || - !DK_IS_UNICODE(dict->ma_keys)) { - UNLOCK_OBJECT(dict); - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - } - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; - if (ep->me_key != name) { - UNLOCK_OBJECT(dict); - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - } - PyObject *old_value = ep->me_value; - if (old_value == NULL) { - UNLOCK_OBJECT(dict); - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; } - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); - UNLOCK_OBJECT(dict); - STAT_INC(STORE_ATTR, hit); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); - Py_XDECREF(old_value); - stack_pointer = _PyFrame_GetStackPointer(frame); - break; - } - - case _STORE_ATTR_SLOT: { - _PyStackRef owner; - _PyStackRef value; - owner = stack_pointer[-1]; - value = stack_pointer[-2]; - uint16_t index = (uint16_t)CURRENT_OPERAND0(); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - if (!LOCK_OBJECT(owner_o)) { + if (total_args != 1) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - char *addr = (char *)owner_o + index; - STAT_INC(STORE_ATTR, hit); - PyObject *old_value = *(PyObject **)addr; - FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); - UNLOCK_OBJECT(owner_o); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); - Py_XDECREF(old_value); - stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _COMPARE_OP: { - _PyStackRef right; - _PyStackRef left; + case _CALL_BUILTIN_O_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; _PyStackRef res; + _PyStackRef c; + _PyStackRef s; oparg = CURRENT_OPARG(); - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert((oparg >> 5) <= Py_GE); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyStackRef_IsNull(self_or_null)) { + args--; + } + STAT_INC(CALL, hit); + PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); + _PyStackRef arg = args[0]; _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_RichCompare(left_o, right_o, oparg >> 5); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable_o), PyStackRef_AsPyObjectBorrow(arg)); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); if (res_o == NULL) { - JUMP_TO_ERROR(); - } - if (oparg & 16) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int res_bool = PyObject_IsTrue(res_o); - Py_DECREF(res_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_bool < 0) { - JUMP_TO_ERROR(); - } - res = res_bool ? PyStackRef_True : PyStackRef_False; - } - else { - res = PyStackRef_FromPyObjectSteal(res_o); - } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _COMPARE_OP_FLOAT: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - oparg = CURRENT_OPARG(); - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - STAT_INC(COMPARE_OP, hit); - double dleft = PyFloat_AS_DOUBLE(left_o); - double dright = PyFloat_AS_DOUBLE(right_o); - int sign_ish = COMPARISON_BIT(dleft, dright); - PyStackRef_CLOSE_SPECIALIZED(left, _PyFloat_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(right, _PyFloat_ExactDealloc); - res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _COMPARE_OP_INT: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - oparg = CURRENT_OPARG(); - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(_PyLong_IsCompact((PyLongObject *)left_o)); - assert(_PyLong_IsCompact((PyLongObject *)right_o)); - STAT_INC(COMPARE_OP, hit); - assert(_PyLong_DigitCount((PyLongObject *)left_o) <= 1 && - _PyLong_DigitCount((PyLongObject *)right_o) <= 1); - Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left_o); - Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o); - int sign_ish = COMPARISON_BIT(ileft, iright); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _COMPARE_OP_STR: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef res; - oparg = CURRENT_OPARG(); - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - STAT_INC(COMPARE_OP, hit); - int eq = _PyUnicode_Equal(left_o, right_o); - assert((oparg >> 5) == Py_EQ || (oparg >> 5) == Py_NE); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - assert(eq == 0 || eq == 1); - assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); - assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); - res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? PyStackRef_True : PyStackRef_False; - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + c = callable; + s = args[0]; + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _IS_OP: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef b; + case _GUARD_CALLABLE_BUILTIN_FAST_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; oparg = CURRENT_OPARG(); - right = stack_pointer[-1]; - left = stack_pointer[-2]; - int res = Py_Is(PyStackRef_AsPyObjectBorrow(left), PyStackRef_AsPyObjectBorrow(right)) ^ oparg; - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - b = res ? PyStackRef_True : PyStackRef_False; - stack_pointer[0] = b; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyCFunction_CheckExact(callable_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + if (PyCFunction_GET_FLAGS(callable_o) != METH_FASTCALL) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CONTAINS_OP: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef b; + case _CALL_BUILTIN_FAST_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; oparg = CURRENT_OPARG(); - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); - int res = PySequence_Contains(right_o, left_o); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); + PyObject *res_o = _Py_BuiltinCallFast_StackRef( + callable, + arguments, + total_args + ); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (res < 0) { + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; - stack_pointer[0] = b; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_TOS_ANY_SET: { - _PyStackRef tos; - tos = stack_pointer[-1]; - PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyAnySet_CheckExact(o)) { + case _GUARD_CALLABLE_BUILTIN_FAST_WITH_KEYWORDS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + oparg = CURRENT_OPARG(); + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyCFunction_CheckExact(callable_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + if (PyCFunction_GET_FLAGS(callable_o) != (METH_FASTCALL | METH_KEYWORDS)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CONTAINS_OP_SET: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef b; + case _CALL_BUILTIN_FAST_WITH_KEYWORDS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; oparg = CURRENT_OPARG(); - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyAnySet_CheckExact(right_o)); - STAT_INC(CONTAINS_OP, hit); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); - int res = _PySet_Contains((PySetObject *)right_o, left_o); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); + PyObject *res_o = _Py_BuiltinCallFastWithKeywords_StackRef(callable, arguments, total_args); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (res < 0) { + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; - stack_pointer[0] = b; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CONTAINS_OP_DICT: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef b; - oparg = CURRENT_OPARG(); - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyDict_CheckExact(right_o)); - STAT_INC(CONTAINS_OP, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - int res = PyDict_Contains(right_o, left_o); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _GUARD_CALLABLE_LEN_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.len) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_LEN_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + callable = stack_pointer[-2]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.len) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (res < 0) { - JUMP_TO_ERROR(); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_LEN_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + callable = stack_pointer[-1]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.len) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; - stack_pointer[0] = b; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_EG_MATCH: { - _PyStackRef match_type_st; - _PyStackRef exc_value_st; - _PyStackRef rest; - _PyStackRef match; - match_type_st = stack_pointer[-1]; - exc_value_st = stack_pointer[-2]; - PyObject *exc_value = PyStackRef_AsPyObjectBorrow(exc_value_st); - PyObject *match_type = PyStackRef_AsPyObjectBorrow(match_type_st); + case _GUARD_CALLABLE_LEN_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + callable = _stack_item_0; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.len) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CALL_LEN_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef callable; + _PyStackRef res; + _PyStackRef a; + _PyStackRef c; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + arg = _stack_item_2; + callable = _stack_item_0; + STAT_INC(CALL, hit); + PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); + stack_pointer[0] = callable; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = arg; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PyEval_CheckExceptStarTypeValid(tstate, match_type); + Py_ssize_t len_i = PyObject_Length(arg_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = match_type_st; - match_type_st = PyStackRef_NULL; - stack_pointer[-1] = match_type_st; - PyStackRef_CLOSE(tmp); - tmp = exc_value_st; - exc_value_st = PyStackRef_NULL; - stack_pointer[-2] = exc_value_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + if (len_i < 0) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - PyObject *match_o = NULL; - PyObject *rest_o = NULL; - _PyFrame_SetStackPointer(frame, stack_pointer); - int res = _PyEval_ExceptionGroupMatch(frame, exc_value, match_type, - &match_o, &rest_o); - _PyStackRef tmp = match_type_st; - match_type_st = PyStackRef_NULL; - stack_pointer[-1] = match_type_st; - PyStackRef_CLOSE(tmp); - tmp = exc_value_st; - exc_value_st = PyStackRef_NULL; - stack_pointer[-2] = exc_value_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (res < 0) { + PyObject *res_o = PyLong_FromSsize_t(len_i); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - assert((match_o == NULL) == (rest_o == NULL)); - if (match_o == NULL) { - JUMP_TO_ERROR(); + a = arg; + c = callable; + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache2 = c; + _tos_cache1 = a; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_ISINSTANCE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + callable = stack_pointer[-4]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.isinstance) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = stack_pointer[-3]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_ISINSTANCE_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.isinstance) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = stack_pointer[-2]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_ISINSTANCE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + callable = stack_pointer[-2]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.isinstance) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - if (!Py_IsNone(match_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - PyErr_SetHandledException(match_o); - stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = stack_pointer[-1]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_ISINSTANCE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + callable = stack_pointer[-1]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.isinstance) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); } - rest = PyStackRef_FromPyObjectSteal(rest_o); - match = PyStackRef_FromPyObjectSteal(match_o); - stack_pointer[0] = rest; - stack_pointer[1] = match; - stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_EXC_MATCH: { - _PyStackRef right; - _PyStackRef left; - _PyStackRef b; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyExceptionInstance_Check(left_o)); + case _CALL_ISINSTANCE_r31: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef cls; + _PyStackRef instance; + _PyStackRef null; + _PyStackRef callable; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + cls = _stack_item_2; + instance = _stack_item_1; + null = _stack_item_0; + callable = stack_pointer[-1]; + STAT_INC(CALL, hit); + PyObject *inst_o = PyStackRef_AsPyObjectBorrow(instance); + PyObject *cls_o = PyStackRef_AsPyObjectBorrow(cls); + stack_pointer[0] = null; + stack_pointer[1] = instance; + stack_pointer[2] = cls; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PyEval_CheckExceptTypeValid(tstate, right_o); + int retval = PyObject_IsInstance(inst_o, cls_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { + if (retval < 0) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } + (void)null; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - int res = PyErr_GivenExceptionMatches(left_o, right_o); + PyStackRef_CLOSE(cls); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(right); + PyStackRef_CLOSE(instance); stack_pointer = _PyFrame_GetStackPointer(frame); - b = res ? PyStackRef_True : PyStackRef_False; - stack_pointer[0] = b; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callable); + stack_pointer = _PyFrame_GetStackPointer(frame); + res = retval ? PyStackRef_True : PyStackRef_False; + assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _IMPORT_NAME: { - _PyStackRef fromlist; - _PyStackRef level; - _PyStackRef res; - oparg = CURRENT_OPARG(); - fromlist = stack_pointer[-1]; - level = stack_pointer[-2]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyEval_ImportName(tstate, frame, name, - PyStackRef_AsPyObjectBorrow(fromlist), - PyStackRef_AsPyObjectBorrow(level)); - _PyStackRef tmp = fromlist; - fromlist = PyStackRef_NULL; - stack_pointer[-1] = fromlist; - PyStackRef_CLOSE(tmp); - tmp = level; - level = PyStackRef_NULL; - stack_pointer[-2] = level; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _GUARD_CALLABLE_LIST_APPEND_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + callable = stack_pointer[-3]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.list_append) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_LIST_APPEND_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + callable = stack_pointer[-2]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.list_append) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_ERROR(); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_LIST_APPEND_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + callable = stack_pointer[-1]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.list_append) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _IMPORT_FROM: { - _PyStackRef from; - _PyStackRef res; + case _GUARD_CALLABLE_LIST_APPEND_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + callable = _stack_item_0; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyInterpreterState *interp = tstate->interp; + if (callable_o != interp->callable_cache.list_append) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CALL_LIST_APPEND_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef self; + _PyStackRef callable; + _PyStackRef none; + _PyStackRef c; + _PyStackRef s; oparg = CURRENT_OPARG(); - from = stack_pointer[-1]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyEval_ImportFrom(tstate, PyStackRef_AsPyObjectBorrow(from), name); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { + arg = stack_pointer[-1]; + self = stack_pointer[-2]; + callable = stack_pointer[-3]; + assert(oparg == 1); + PyObject *self_o = PyStackRef_AsPyObjectBorrow(self); + if (!LOCK_OBJECT(self_o)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(CALL, hit); + int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); + UNLOCK_OBJECT(self_o); + if (err) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + c = callable; + s = self; + none = PyStackRef_None; + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = none; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _POP_JUMP_IF_FALSE is not a viable micro-op for tier 2 because it is replaced */ - - /* _POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 because it is replaced */ + case _CALL_LIST_APPEND_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef self; + _PyStackRef callable; + _PyStackRef none; + _PyStackRef c; + _PyStackRef s; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + arg = _stack_item_0; + self = stack_pointer[-1]; + callable = stack_pointer[-2]; + assert(oparg == 1); + PyObject *self_o = PyStackRef_AsPyObjectBorrow(self); + if (!LOCK_OBJECT(self_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = arg; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(CALL, hit); + int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); + UNLOCK_OBJECT(self_o); + if (err) { + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + c = callable; + s = self; + none = PyStackRef_None; + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = none; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - case _IS_NONE: { - _PyStackRef value; - _PyStackRef b; - value = stack_pointer[-1]; - if (PyStackRef_IsNone(value)) { - b = PyStackRef_True; + case _CALL_LIST_APPEND_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef self; + _PyStackRef callable; + _PyStackRef none; + _PyStackRef c; + _PyStackRef s; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + arg = _stack_item_1; + self = _stack_item_0; + callable = stack_pointer[-1]; + assert(oparg == 1); + PyObject *self_o = PyStackRef_AsPyObjectBorrow(self); + if (!LOCK_OBJECT(self_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = arg; + _tos_cache0 = self; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - else { - b = PyStackRef_False; - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = value; - value = b; - stack_pointer[-1] = value; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + STAT_INC(CALL, hit); + int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); + UNLOCK_OBJECT(self_o); + if (err) { + stack_pointer[0] = self; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } - stack_pointer[-1] = b; + c = callable; + s = self; + none = PyStackRef_None; + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = none; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GET_LEN: { - _PyStackRef obj; - _PyStackRef len; - obj = stack_pointer[-1]; - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_ssize_t len_i = PyObject_Length(PyStackRef_AsPyObjectBorrow(obj)); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (len_i < 0) { + case _CALL_LIST_APPEND_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef arg; + _PyStackRef self; + _PyStackRef callable; + _PyStackRef none; + _PyStackRef c; + _PyStackRef s; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + arg = _stack_item_2; + self = _stack_item_1; + callable = _stack_item_0; + assert(oparg == 1); + PyObject *self_o = PyStackRef_AsPyObjectBorrow(self); + if (!LOCK_OBJECT(self_o)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = arg; + _tos_cache1 = self; + _tos_cache0 = callable; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + STAT_INC(CALL, hit); + int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); + UNLOCK_OBJECT(self_o); + if (err) { + stack_pointer[0] = callable; + stack_pointer[1] = self; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - PyObject *len_o = PyLong_FromSsize_t(len_i); - if (len_o == NULL) { - JUMP_TO_ERROR(); + c = callable; + s = self; + none = PyStackRef_None; + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = none; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_METHOD_DESCRIPTOR_O_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + if (method->d_method->ml_flags != METH_O) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args != 2) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - len = PyStackRef_FromPyObjectSteal(len_o); - stack_pointer[0] = len; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _MATCH_CLASS: { - _PyStackRef names; - _PyStackRef type; - _PyStackRef subject; - _PyStackRef attrs; + case _CALL_METHOD_DESCRIPTOR_O_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef a; oparg = CURRENT_OPARG(); - names = stack_pointer[-1]; - type = stack_pointer[-2]; - subject = stack_pointer[-3]; - assert(PyTuple_CheckExact(PyStackRef_AsPyObjectBorrow(names))); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + } + STAT_INC(CALL, hit); + PyCFunction cfunc = method->d_method->ml_meth; + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + PyObject *arg = PyStackRef_AsPyObjectBorrow(arguments[1]); _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *attrs_o = _PyEval_MatchClass(tstate, - PyStackRef_AsPyObjectBorrow(subject), - PyStackRef_AsPyObjectBorrow(type), oparg, - PyStackRef_AsPyObjectBorrow(names)); - _PyStackRef tmp = names; - names = PyStackRef_NULL; - stack_pointer[-1] = names; - PyStackRef_CLOSE(tmp); - tmp = type; - type = PyStackRef_NULL; - stack_pointer[-2] = type; - PyStackRef_CLOSE(tmp); - tmp = subject; - subject = PyStackRef_NULL; - stack_pointer[-3] = subject; - PyStackRef_CLOSE(tmp); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, self, arg); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - if (attrs_o) { - assert(PyTuple_CheckExact(attrs_o)); - attrs = PyStackRef_FromPyObjectSteal(attrs_o); - } - else { - if (_PyErr_Occurred(tstate)) { - JUMP_TO_ERROR(); - } - attrs = PyStackRef_None; + _Py_LeaveRecursiveCallTstate(tstate); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } - stack_pointer[0] = attrs; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _MATCH_MAPPING: { - _PyStackRef subject; - _PyStackRef res; - subject = stack_pointer[-1]; - int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; - res = match ? PyStackRef_True : PyStackRef_False; - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + c = callable; + s = arguments[0]; + a = arguments[1]; + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache2 = a; + _tos_cache1 = s; + _tos_cache0 = c; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _MATCH_SEQUENCE: { - _PyStackRef subject; - _PyStackRef res; - subject = stack_pointer[-1]; - int match = PyStackRef_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; - res = match ? PyStackRef_True : PyStackRef_False; - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _CHECK_RECURSION_LIMIT_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + if (_Py_ReachedRecursionLimit(tstate)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _MATCH_KEYS: { - _PyStackRef keys; - _PyStackRef subject; - _PyStackRef values_or_none; - keys = stack_pointer[-1]; - subject = stack_pointer[-2]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *values_or_none_o = _PyEval_MatchKeys(tstate, - PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(keys)); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (values_or_none_o == NULL) { - JUMP_TO_ERROR(); + case _CHECK_RECURSION_LIMIT_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + if (_Py_ReachedRecursionLimit(tstate)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } - values_or_none = PyStackRef_FromPyObjectSteal(values_or_none_o); - stack_pointer[0] = values_or_none; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GET_ITER: { - _PyStackRef iterable; - _PyStackRef iter; - _PyStackRef index_or_null; - iterable = stack_pointer[-1]; - #ifdef Py_STATS - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_GatherStats_GetIter(iterable); - stack_pointer = _PyFrame_GetStackPointer(frame); - #endif - - PyTypeObject *tp = PyStackRef_TYPE(iterable); - if (tp == &PyTuple_Type || tp == &PyList_Type) { - iter = iterable; - index_or_null = PyStackRef_TagInt(0); - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(iterable); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (iter_o == NULL) { - JUMP_TO_ERROR(); - } - iter = PyStackRef_FromPyObjectSteal(iter_o); - index_or_null = PyStackRef_NULL; - stack_pointer += 1; + case _CHECK_RECURSION_LIMIT_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + if (_Py_ReachedRecursionLimit(tstate)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - stack_pointer[-1] = iter; - stack_pointer[0] = index_or_null; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GET_YIELD_FROM_ITER: { - _PyStackRef iterable; - _PyStackRef iter; - iterable = stack_pointer[-1]; - PyObject *iterable_o = PyStackRef_AsPyObjectBorrow(iterable); - if (PyCoro_CheckExact(iterable_o)) { - if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_SetString(tstate, PyExc_TypeError, - "cannot 'yield from' a coroutine object " - "in a non-coroutine generator"); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); - } - iter = iterable; - } - else if (PyGen_CheckExact(iterable_o)) { - iter = iterable; - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *iter_o = PyObject_GetIter(iterable_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (iter_o == NULL) { - JUMP_TO_ERROR(); - } - iter = PyStackRef_FromPyObjectSteal(iter_o); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = iterable; - iterable = iter; - stack_pointer[-1] = iterable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _CHECK_RECURSION_LIMIT_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + if (_Py_ReachedRecursionLimit(tstate)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); } - stack_pointer[-1] = iter; + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _FOR_ITER is not a viable micro-op for tier 2 because it is replaced */ - - case _FOR_ITER_TIER_TWO: { - _PyStackRef null_or_index; - _PyStackRef iter; - _PyStackRef next; - null_or_index = stack_pointer[-1]; - iter = stack_pointer[-2]; + case _CALL_METHOD_DESCRIPTOR_O_INLINE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef callable; + _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef a; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + callable = stack_pointer[-1 - oparg]; + PyObject *cfunc = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg == 2); + STAT_INC(CALL, hit); + volatile PyCFunction cfunc_v = (PyCFunction)cfunc; + PyObject *self = PyStackRef_AsPyObjectBorrow(args[0]); + PyObject *arg = PyStackRef_AsPyObjectBorrow(args[1]); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef item = _PyForIter_VirtualIteratorNext(tstate, frame, iter, &null_or_index); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc_v, self, arg); stack_pointer = _PyFrame_GetStackPointer(frame); - if (!PyStackRef_IsValid(item)) { - if (PyStackRef_IsError(item)) { - JUMP_TO_ERROR(); - } - if (true) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + _Py_LeaveRecursiveCallTstate(tstate); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } - next = item; - stack_pointer[-1] = null_or_index; - stack_pointer[0] = next; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + c = callable; + s = args[0]; + a = args[1]; + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache2 = a; + _tos_cache1 = s; + _tos_cache0 = c; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer[-1 - oparg] = res; + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _INSTRUMENTED_FOR_ITER is not a viable micro-op for tier 2 because it is instrumented */ - - case _ITER_CHECK_LIST: { - _PyStackRef null_or_index; - _PyStackRef iter; - null_or_index = stack_pointer[-1]; - iter = stack_pointer[-2]; - PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); - if (Py_TYPE(iter_o) != &PyList_Type) { + case _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - assert(PyStackRef_IsTaggedInt(null_or_index)); - #ifdef Py_GIL_DISABLED - if (!_Py_IsOwnedByCurrentThread(iter_o) && !_PyObject_GC_IS_SHARED(iter_o)) { + if (method->d_method->ml_flags != (METH_FASTCALL|METH_KEYWORDS)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - #endif - break; - } - - /* _ITER_JUMP_LIST is not a viable micro-op for tier 2 because it is replaced */ - - case _GUARD_NOT_EXHAUSTED_LIST: { - _PyStackRef null_or_index; - _PyStackRef iter; - null_or_index = stack_pointer[-1]; - iter = stack_pointer[-2]; - #ifndef Py_GIL_DISABLED - PyObject *list_o = PyStackRef_AsPyObjectBorrow(iter); - assert(Py_TYPE(list_o) == &PyList_Type); - if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyList_GET_SIZE(list_o)) { + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + if (total_args == 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - #endif + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _ITER_NEXT_LIST is not a viable micro-op for tier 2 because it is replaced */ + case _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + assert(self != NULL); + STAT_INC(CALL, hit); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyCFunctionFastWithKeywords cfunc = _PyCFunctionFastWithKeywords_CAST(method->d_method->ml_meth); + PyObject *res_o = _PyCallMethodDescriptorFastWithKeywords_StackRef( + callable, + cfunc, + self, + arguments, + total_args + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - case _ITER_NEXT_LIST_TIER_TWO: { - _PyStackRef null_or_index; - _PyStackRef iter; - _PyStackRef next; - null_or_index = stack_pointer[-1]; - iter = stack_pointer[-2]; - PyObject *list_o = PyStackRef_AsPyObjectBorrow(iter); - assert(PyList_CheckExact(list_o)); - #ifdef Py_GIL_DISABLED - assert(_Py_IsOwnedByCurrentThread((PyObject *)list_o) || - _PyObject_GC_IS_SHARED(list_o)); - STAT_INC(FOR_ITER, hit); + case _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_st; + _PyStackRef callable; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self_st = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *cfunc = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); - int result = _PyList_GetItemRefNoLock((PyListObject *)list_o, PyStackRef_UntagInt(null_or_index), &next); + volatile PyCFunctionFastWithKeywords cfunc_v = _PyCFunctionFastWithKeywords_CAST(cfunc); + PyObject *res_o = _PyCallMethodDescriptorFastWithKeywords_StackRef( + callable, + cfunc_v, + self, + args - 1, + oparg + 1 + ); stack_pointer = _PyFrame_GetStackPointer(frame); - if (result <= 0) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } - #else - assert(PyStackRef_UntagInt(null_or_index) < PyList_GET_SIZE(list_o)); - next = PyStackRef_FromPyObjectNew(PyList_GET_ITEM(list_o, PyStackRef_UntagInt(null_or_index))); - #endif - null_or_index = PyStackRef_IncrementTaggedIntNoOverflow(null_or_index); - stack_pointer[-1] = null_or_index; - stack_pointer[0] = next; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _ITER_CHECK_TUPLE: { - _PyStackRef null_or_index; - _PyStackRef iter; - null_or_index = stack_pointer[-1]; - iter = stack_pointer[-2]; - PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); - if (Py_TYPE(iter_o) != &PyTuple_Type) { + case _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - assert(PyStackRef_IsTaggedInt(null_or_index)); - break; - } - - /* _ITER_JUMP_TUPLE is not a viable micro-op for tier 2 because it is replaced */ - - case _GUARD_NOT_EXHAUSTED_TUPLE: { - _PyStackRef null_or_index; - _PyStackRef iter; - null_or_index = stack_pointer[-1]; - iter = stack_pointer[-2]; - PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); - assert(Py_TYPE(tuple_o) == &PyTuple_Type); - if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyTuple_GET_SIZE(tuple_o)) { + if (method->d_method->ml_flags != METH_NOARGS) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - break; - } - - case _ITER_NEXT_TUPLE: { - _PyStackRef null_or_index; - _PyStackRef iter; - _PyStackRef next; - null_or_index = stack_pointer[-1]; - iter = stack_pointer[-2]; - PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); - assert(Py_TYPE(tuple_o) == &PyTuple_Type); - uintptr_t i = PyStackRef_UntagInt(null_or_index); - assert((size_t)i < (size_t)PyTuple_GET_SIZE(tuple_o)); - next = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(tuple_o, i)); - null_or_index = PyStackRef_IncrementTaggedIntNoOverflow(null_or_index); - stack_pointer[-1] = null_or_index; - stack_pointer[0] = next; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _ITER_CHECK_RANGE: { - _PyStackRef iter; - iter = stack_pointer[-2]; - _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); - if (Py_TYPE(r) != &PyRangeIter_Type) { + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args != 1) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - #ifdef Py_GIL_DISABLED - if (!_PyObject_IsUniquelyReferenced((PyObject *)r)) { + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - #endif + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _ITER_JUMP_RANGE is not a viable micro-op for tier 2 because it is replaced */ - - case _GUARD_NOT_EXHAUSTED_RANGE: { - _PyStackRef iter; - iter = stack_pointer[-2]; - _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); - assert(Py_TYPE(r) == &PyRangeIter_Type); - if (r->len <= 0) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + case _CALL_METHOD_DESCRIPTOR_NOARGS_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + assert(oparg == 1 || !PyStackRef_IsNull(self_or_null)); + if (!PyStackRef_IsNull(self_or_null)) { + args--; + } + _PyStackRef self_stackref = args[0]; + PyObject *self = PyStackRef_AsPyObjectBorrow(self_stackref); + STAT_INC(CALL, hit); + PyCFunction cfunc = method->d_method->ml_meth; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, self, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } + c = callable; + s = args[0]; + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _ITER_NEXT_RANGE: { - _PyStackRef iter; - _PyStackRef next; - iter = stack_pointer[-2]; - _PyRangeIterObject *r = (_PyRangeIterObject *)PyStackRef_AsPyObjectBorrow(iter); - assert(Py_TYPE(r) == &PyRangeIter_Type); - #ifdef Py_GIL_DISABLED - assert(_PyObject_IsUniquelyReferenced((PyObject *)r)); - #endif - assert(r->len > 0); - long value = r->start; - r->start = value + r->step; - r->len--; - PyObject *res = PyLong_FromLong(value); - if (res == NULL) { + case _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef callable; + _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + callable = stack_pointer[-1 - oparg]; + PyObject *cfunc = (PyObject *)CURRENT_OPERAND0_64(); + assert(oparg == 1); + _PyStackRef self_stackref = args[0]; + PyObject *self = PyStackRef_AsPyObjectBorrow(self_stackref); + STAT_INC(CALL, hit); + volatile PyCFunction cfunc_v = (PyCFunction)cfunc; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc_v, self, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + _Py_LeaveRecursiveCallTstate(tstate); + assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - next = PyStackRef_FromPyObjectSteal(res); - stack_pointer[0] = next; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + c = callable; + s = args[0]; + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _FOR_ITER_GEN_FRAME: { - _PyStackRef iter; - _PyStackRef gen_frame; + case _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; oparg = CURRENT_OPARG(); - iter = stack_pointer[-2]; - PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); - if (Py_TYPE(gen) != &PyGen_Type) { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - #ifdef Py_GIL_DISABLED - - if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) { + if (method->d_method->ml_flags != METH_FASTCALL) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - #endif - if (gen->gi_frame_state >= FRAME_EXECUTING) { + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args == 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - STAT_INC(FOR_ITER, hit); - _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; - _PyFrame_StackPush(pushed_frame, PyStackRef_None); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - pushed_frame->previous = frame; - frame->return_offset = (uint16_t)( 2 + oparg); - gen_frame = PyStackRef_Wrap(pushed_frame); - stack_pointer[0] = gen_frame; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _INSERT_NULL: { - _PyStackRef self; - _PyStackRef *method_and_self; - self = stack_pointer[-1]; - method_and_self = &stack_pointer[-1]; - method_and_self[1] = self; - method_and_self[0] = PyStackRef_NULL; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_SPECIAL: { - _PyStackRef *method_and_self; + case _CALL_METHOD_DESCRIPTOR_FAST_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; oparg = CURRENT_OPARG(); - method_and_self = &stack_pointer[-2]; - PyObject *name = _Py_SpecialMethods[oparg].name; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + assert(self != NULL); + STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PyObject_LookupSpecialMethod(name, method_and_self); + PyCFunctionFast cfunc = _PyCFunctionFast_CAST(method->d_method->ml_meth); + PyObject *res_o = _PyCallMethodDescriptorFast_StackRef( + callable, + cfunc, + self, + arguments, + total_args + ); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err <= 0) { - if (err == 0) { - PyObject *owner = PyStackRef_AsPyObjectBorrow(method_and_self[1]); - _PyFrame_SetStackPointer(frame, stack_pointer); - const char *errfmt = _PyEval_SpecialMethodCanSuggest(owner, oparg) - ? _Py_SpecialMethods[oparg].error_suggestion - : _Py_SpecialMethods[oparg].error; - stack_pointer = _PyFrame_GetStackPointer(frame); - assert(!_PyErr_Occurred(tstate)); - assert(errfmt != NULL); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_Format(tstate, PyExc_TypeError, errfmt, owner); - stack_pointer = _PyFrame_GetStackPointer(frame); - } + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _WITH_EXCEPT_START: { - _PyStackRef val; - _PyStackRef lasti; - _PyStackRef exit_self; - _PyStackRef exit_func; - _PyStackRef res; - val = stack_pointer[-1]; - lasti = stack_pointer[-3]; - exit_self = stack_pointer[-4]; - exit_func = stack_pointer[-5]; - PyObject *exc, *tb; - PyObject *val_o = PyStackRef_AsPyObjectBorrow(val); - PyObject *exit_func_o = PyStackRef_AsPyObjectBorrow(exit_func); - assert(val_o && PyExceptionInstance_Check(val_o)); - exc = PyExceptionInstance_Class(val_o); - PyObject *original_tb = tb = PyException_GetTraceback(val_o); - if (tb == NULL) { - tb = Py_None; - } - assert(PyStackRef_IsTaggedInt(lasti)); - (void)lasti; - PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb}; - int has_self = !PyStackRef_IsNull(exit_self); + case _CALL_METHOD_DESCRIPTOR_FAST_INLINE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef *args; + _PyStackRef self_st; + _PyStackRef callable; + oparg = CURRENT_OPARG(); + args = &stack_pointer[-oparg]; + self_st = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *cfunc = (PyObject *)CURRENT_OPERAND0_64(); + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + assert(self != NULL); + STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Vectorcall(exit_func_o, stack + 2 - has_self, - (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); - Py_XDECREF(original_tb); + volatile PyCFunctionFast cfunc_v = _PyCFunctionFast_CAST(cfunc); + PyObject *res_o = _PyCallMethodDescriptorFast_StackRef( + callable, + cfunc_v, + self, + args - 1, + oparg + 1 + ); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _PUSH_EXC_INFO: { - _PyStackRef exc; - _PyStackRef prev_exc; - _PyStackRef new_exc; - exc = stack_pointer[-1]; - _PyErr_StackItem *exc_info = tstate->exc_info; - if (exc_info->exc_value != NULL) { - prev_exc = PyStackRef_FromPyObjectSteal(exc_info->exc_value); + /* _MONITOR_CALL_KW is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + + case _MAYBE_EXPAND_METHOD_KW_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *self = ((PyMethodObject *)callable_o)->im_self; + self_or_null = PyStackRef_FromPyObjectNew(self); + PyObject *method = ((PyMethodObject *)callable_o)->im_func; + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectNew(method); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; } - else { - prev_exc = PyStackRef_None; + _tos_cache0 = _stack_item_0; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + /* _DO_CALL_KW is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + + case _PY_FRAME_KW_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef kwnames; + _PyStackRef *args; + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef new_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + kwnames = _stack_item_0; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; } - assert(PyStackRef_ExceptionInstanceCheck(exc)); - exc_info->exc_value = PyStackRef_AsPyObjectNew(exc); - new_exc = exc; - stack_pointer[-1] = prev_exc; - stack_pointer[0] = new_exc; + PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); + int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); + assert(Py_TYPE(callable_o) == &PyFunction_Type); + int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); + stack_pointer[0] = kwnames; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( + tstate, callable, locals, + arguments, positional_args, kwnames_o, frame + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (temp == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + new_frame = PyStackRef_Wrap(temp); + _tos_cache0 = new_frame; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT: { - _PyStackRef owner; - owner = stack_pointer[-1]; - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - assert(Py_TYPE(owner_o)->tp_flags & Py_TPFLAGS_INLINE_VALUES); - PyDictValues *ivs = _PyObject_InlineValues(owner_o); - if (!FT_ATOMIC_LOAD_UINT8(ivs->valid)) { + case _CHECK_FUNCTION_VERSION_KW_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyFunction_Check(callable_o)) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - break; - } - - case _GUARD_KEYS_VERSION: { - _PyStackRef owner; - owner = stack_pointer[-1]; - uint32_t keys_version = (uint32_t)CURRENT_OPERAND0(); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { + PyFunctionObject *func = (PyFunctionObject *)callable_o; + if (func->func_version != func_version) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_ATTR_METHOD_WITH_VALUES: { - _PyStackRef owner; - _PyStackRef attr; - _PyStackRef self; + case _CHECK_METHOD_VERSION_KW_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef null; + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND0(); - assert(oparg & 1); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - attr = PyStackRef_FromPyObjectNew(descr); - self = owner; - stack_pointer[-1] = attr; - stack_pointer[0] = self; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + uint32_t func_version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (Py_TYPE(callable_o) != &PyMethod_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + PyObject *func = ((PyMethodObject *)callable_o)->im_func; + if (!PyFunction_Check(func)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + if (((PyFunctionObject *)func)->func_version != func_version) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + if (!PyStackRef_IsNull(null)) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_ATTR_METHOD_NO_DICT: { - _PyStackRef owner; - _PyStackRef attr; - _PyStackRef self; + case _EXPAND_METHOD_KW_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef self_or_null; + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND0(); - assert(oparg & 1); - assert(Py_TYPE(PyStackRef_AsPyObjectBorrow(owner))->tp_dictoffset == 0); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - attr = PyStackRef_FromPyObjectNew(descr); - self = owner; - stack_pointer[-1] = attr; - stack_pointer[0] = self; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + assert(PyStackRef_IsNull(self_or_null)); + _PyStackRef callable_s = callable; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(Py_TYPE(callable_o) == &PyMethod_Type); + self_or_null = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); + callable = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); + assert(PyStackRef_FunctionCheck(callable)); + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; + stack_pointer[0] = _stack_item_0; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES: { - _PyStackRef owner; - _PyStackRef attr; - oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND0(); - assert((oparg & 1) == 0); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); + PyStackRef_CLOSE(callable_s); stack_pointer = _PyFrame_GetStackPointer(frame); - attr = PyStackRef_FromPyObjectNew(descr); - stack_pointer[0] = attr; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _LOAD_ATTR_NONDESCRIPTOR_NO_DICT: { - _PyStackRef owner; - _PyStackRef attr; - oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND0(); - assert((oparg & 1) == 0); - assert(Py_TYPE(PyStackRef_AsPyObjectBorrow(owner))->tp_dictoffset == 0); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); + _tos_cache0 = _stack_item_0; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); - stack_pointer = _PyFrame_GetStackPointer(frame); - attr = PyStackRef_FromPyObjectNew(descr); - stack_pointer[0] = attr; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_ATTR_METHOD_LAZY_DICT: { - _PyStackRef owner; - owner = stack_pointer[-1]; - uint16_t dictoffset = (uint16_t)CURRENT_OPERAND0(); - char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset; - PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr); - if (dict != NULL) { + case _CHECK_IS_NOT_PY_CALLABLE_KW_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callable; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (PyFunction_Check(callable_o)) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } + if (Py_TYPE(callable_o) == &PyMethod_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_ATTR_METHOD_LAZY_DICT: { - _PyStackRef owner; - _PyStackRef attr; - _PyStackRef self; - oparg = CURRENT_OPARG(); - owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)CURRENT_OPERAND0(); - assert(oparg & 1); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - attr = PyStackRef_FromPyObjectNew(descr); - self = owner; - stack_pointer[-1] = attr; - stack_pointer[0] = self; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _MAYBE_EXPAND_METHOD: { + case _CALL_KW_NON_PY_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef kwnames; + _PyStackRef *args; _PyStackRef self_or_null; _PyStackRef callable; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); + kwnames = _stack_item_0; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; - if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - PyObject *self = ((PyMethodObject *)callable_o)->im_self; - self_or_null = PyStackRef_FromPyObjectNew(self); - PyObject *method = ((PyMethodObject *)callable_o)->im_func; - _PyStackRef temp = callable; - callable = PyStackRef_FromPyObjectNew(method); - stack_pointer[-2 - oparg] = callable; - stack_pointer[-1 - oparg] = self_or_null; + #if TIER_ONE + assert(opcode != INSTRUMENTED_CALL); + #endif + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + stack_pointer[0] = kwnames; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _Py_VectorCall_StackRefSteal( + callable, + arguments, + total_args, + kwnames); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + stack_pointer += -3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MAKE_CALLARGS_A_TUPLE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef callargs; + _PyStackRef func; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + callargs = _stack_item_1; + func = stack_pointer[-1]; + PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); + if (!PyTuple_CheckExact(callargs_o)) { + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = callargs; + stack_pointer[2] = _stack_item_2; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *tuple_o = PySequence_Tuple(callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (tuple_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + _PyStackRef temp = callargs; + callargs = PyStackRef_FromPyObjectSteal(tuple_o); + stack_pointer[-2] = callargs; _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; } - stack_pointer[-2 - oparg] = callable; - stack_pointer[-1 - oparg] = self_or_null; + _tos_cache2 = _stack_item_2; + _tos_cache1 = callargs; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _DO_CALL is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + /* _DO_CALL_FUNCTION_EX is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ - /* _MONITOR_CALL is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + case _CHECK_IS_PY_CALLABLE_EX_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_st; + func_st = stack_pointer[-4]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) != &PyFunction_Type) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + if (((PyFunctionObject *)func)->vectorcall != _PyFunction_Vectorcall) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = stack_pointer[-3]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - case _PY_FRAME_GENERAL: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef new_frame; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - if (!PyStackRef_IsNull(self_or_null)) { - args--; - total_args++; + case _CHECK_IS_PY_CALLABLE_EX_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_st; + _PyStackRef _stack_item_0 = _tos_cache0; + func_st = stack_pointer[-3]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) != &PyFunction_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } - assert(Py_TYPE(callable_o) == &PyFunction_Type); - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( - tstate, callable, locals, - args, total_args, NULL, frame - ); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (temp == NULL) { - JUMP_TO_ERROR(); + if (((PyFunctionObject *)func)->vectorcall != _PyFunction_Vectorcall) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } - new_frame = PyStackRef_Wrap(temp); - stack_pointer[0] = new_frame; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = stack_pointer[-2]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_FUNCTION_VERSION: { - _PyStackRef callable; - oparg = CURRENT_OPARG(); - callable = stack_pointer[-2 - oparg]; - uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (!PyFunction_Check(callable_o)) { + case _CHECK_IS_PY_CALLABLE_EX_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + func_st = stack_pointer[-2]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) != &PyFunction_Type) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - PyFunctionObject *func = (PyFunctionObject *)callable_o; - if (func->func_version != func_version) { + if (((PyFunctionObject *)func)->vectorcall != _PyFunction_Vectorcall) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = stack_pointer[-1]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_FUNCTION_VERSION_INLINE: { - uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); - PyObject *callable_o = (PyObject *)CURRENT_OPERAND1(); - assert(PyFunction_Check(callable_o)); - PyFunctionObject *func = (PyFunctionObject *)callable_o; - if (func->func_version != func_version) { + case _CHECK_IS_PY_CALLABLE_EX_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + func_st = stack_pointer[-1]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) != &PyFunction_Type) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + if (((PyFunctionObject *)func)->vectorcall != _PyFunction_Vectorcall) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _PY_FRAME_EX_r31: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef kwargs_st; + _PyStackRef callargs_st; + _PyStackRef func_st; + _PyStackRef ex_frame; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + kwargs_st = _stack_item_2; + callargs_st = _stack_item_1; + func_st = stack_pointer[-1]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); + assert(PyTuple_CheckExact(callargs)); + assert(Py_TYPE(func) == &PyFunction_Type); + assert(((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall); + PyObject *kwargs = PyStackRef_IsNull(kwargs_st) ? NULL : PyStackRef_AsPyObjectSteal(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); + int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( + tstate, func_st, locals, + nargs, callargs, kwargs, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (new_frame == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + ex_frame = PyStackRef_Wrap(new_frame); + _tos_cache0 = ex_frame; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_METHOD_VERSION: { - _PyStackRef null; - _PyStackRef callable; - oparg = CURRENT_OPARG(); - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (Py_TYPE(callable_o) != &PyMethod_Type) { + case _CHECK_IS_NOT_PY_CALLABLE_EX_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_st; + func_st = stack_pointer[-4]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) == &PyFunction_Type && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - PyObject *func = ((PyMethodObject *)callable_o)->im_func; - if (!PyFunction_Check(func)) { + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = stack_pointer[-3]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_IS_NOT_PY_CALLABLE_EX_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_st; + _PyStackRef _stack_item_0 = _tos_cache0; + func_st = stack_pointer[-3]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) == &PyFunction_Type && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - if (((PyFunctionObject *)func)->func_version != func_version) { + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = stack_pointer[-2]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_IS_NOT_PY_CALLABLE_EX_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + func_st = stack_pointer[-2]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) == &PyFunction_Type && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - if (!PyStackRef_IsNull(null)) { + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = stack_pointer[-1]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_IS_NOT_PY_CALLABLE_EX_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_st; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + func_st = stack_pointer[-1]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) == &PyFunction_Type && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _EXPAND_METHOD: { - _PyStackRef self_or_null; - _PyStackRef callable; - oparg = CURRENT_OPARG(); - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - assert(PyStackRef_IsNull(self_or_null)); - assert(Py_TYPE(callable_o) == &PyMethod_Type); - self_or_null = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); - _PyStackRef temp = callable; - callable = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); - assert(PyStackRef_FunctionCheck(callable)); - stack_pointer[-2 - oparg] = callable; - stack_pointer[-1 - oparg] = self_or_null; + case _CALL_FUNCTION_EX_NON_PY_GENERAL_r31: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef kwargs_st; + _PyStackRef callargs_st; + _PyStackRef null; + _PyStackRef func_st; + _PyStackRef result; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + kwargs_st = _stack_item_2; + callargs_st = _stack_item_1; + null = _stack_item_0; + func_st = stack_pointer[-1]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + (void)null; + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + stack_pointer[0] = null; + stack_pointer[1] = callargs_st; + stack_pointer[2] = kwargs_st; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(temp); + PyObject *result_o = PyObject_Call(func, callargs, kwargs); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(kwargs_st); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(func_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + result = PyStackRef_FromPyObjectSteal(result_o); + _tos_cache0 = result; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_IS_NOT_PY_CALLABLE: { - _PyStackRef callable; - oparg = CURRENT_OPARG(); - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (PyFunction_Check(callable_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + case _MAKE_FUNCTION_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef codeobj_st; + _PyStackRef func; + _PyStackRef co; + _PyStackRef _stack_item_0 = _tos_cache0; + codeobj_st = _stack_item_0; + PyObject *codeobj = PyStackRef_AsPyObjectBorrow(codeobj_st); + stack_pointer[0] = codeobj_st; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyFunctionObject *func_obj = (PyFunctionObject *) + PyFunction_New(codeobj, GLOBALS()); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (func_obj == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } - if (Py_TYPE(callable_o) == &PyMethod_Type) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + co = codeobj_st; + _PyFunction_SetVersion( + func_obj, ((PyCodeObject *)codeobj)->co_version); + func = PyStackRef_FromPyObjectSteal((PyObject *)func_obj); + _tos_cache1 = co; + _tos_cache0 = func; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SET_FUNCTION_ATTRIBUTE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_in; + _PyStackRef attr_st; + _PyStackRef func_out; + oparg = CURRENT_OPARG(); + func_in = stack_pointer[-1]; + attr_st = stack_pointer[-2]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_in); + PyObject *attr = PyStackRef_AsPyObjectSteal(attr_st); + func_out = func_in; + assert(PyFunction_Check(func)); + size_t offset = _Py_FunctionAttributeOffsets[oparg]; + assert(offset != 0); + PyObject **ptr = (PyObject **)(((char *)func) + offset); + assert(*ptr == NULL); + *ptr = attr; + _tos_cache0 = func_out; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SET_FUNCTION_ATTRIBUTE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_in; + _PyStackRef attr_st; + _PyStackRef func_out; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + func_in = _stack_item_0; + attr_st = stack_pointer[-1]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_in); + PyObject *attr = PyStackRef_AsPyObjectSteal(attr_st); + func_out = func_in; + assert(PyFunction_Check(func)); + size_t offset = _Py_FunctionAttributeOffsets[oparg]; + assert(offset != 0); + PyObject **ptr = (PyObject **)(((char *)func) + offset); + assert(*ptr == NULL); + *ptr = attr; + _tos_cache0 = func_out; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SET_FUNCTION_ATTRIBUTE_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_in; + _PyStackRef attr_st; + _PyStackRef func_out; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + func_in = _stack_item_1; + attr_st = _stack_item_0; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_in); + PyObject *attr = PyStackRef_AsPyObjectSteal(attr_st); + func_out = func_in; + assert(PyFunction_Check(func)); + size_t offset = _Py_FunctionAttributeOffsets[oparg]; + assert(offset != 0); + PyObject **ptr = (PyObject **)(((char *)func) + offset); + assert(*ptr == NULL); + *ptr = attr; + _tos_cache0 = func_out; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SET_FUNCTION_ATTRIBUTE_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef func_in; + _PyStackRef attr_st; + _PyStackRef func_out; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + func_in = _stack_item_2; + attr_st = _stack_item_1; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_in); + PyObject *attr = PyStackRef_AsPyObjectSteal(attr_st); + func_out = func_in; + assert(PyFunction_Check(func)); + size_t offset = _Py_FunctionAttributeOffsets[oparg]; + assert(offset != 0); + PyObject **ptr = (PyObject **)(((char *)func) + offset); + assert(*ptr == NULL); + *ptr = attr; + _tos_cache1 = func_out; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _RETURN_GENERATOR_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef res; + assert(PyStackRef_FunctionCheck(frame->f_funcobj)); + PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (gen == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } + assert(STACK_LEVEL() <= 2); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *gen_frame = &gen->gi_iframe; + frame->instr_ptr++; + _PyFrame_Copy(frame, gen_frame); + assert(frame->frame_obj == NULL); + gen->gi_frame_state = FRAME_CREATED; + gen_frame->owner = FRAME_OWNED_BY_GENERATOR; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *prev = frame->previous; + _PyThreadState_PopFrame(tstate, frame); + frame = tstate->current_frame = prev; + LOAD_IP(frame->return_offset); + stack_pointer = _PyFrame_GetStackPointer(frame); + res = PyStackRef_FromPyObjectStealMortal((PyObject *)gen); + LLTRACE_RESUME_FRAME(); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_NON_PY_GENERAL: { + case _BUILD_SLICE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef res; + _PyStackRef slice; oparg = CURRENT_OPARG(); args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - #if TIER_ONE - assert(opcode != INSTRUMENTED_CALL); - #endif - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + PyObject *start_o = PyStackRef_AsPyObjectBorrow(args[0]); + PyObject *stop_o = PyStackRef_AsPyObjectBorrow(args[1]); + PyObject *step_o = oparg == 3 ? PyStackRef_AsPyObjectBorrow(args[2]) : NULL; + PyObject *slice_o = PySlice_New(start_o, stop_o, step_o); _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp; for (int _i = oparg; --_i >= 0;) { @@ -5082,2409 +20378,3951 @@ args[_i] = PyStackRef_NULL; PyStackRef_CLOSE(tmp); } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (slice_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + slice = PyStackRef_FromPyObjectStealMortal(slice_o); + _tos_cache0 = slice; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: { - _PyStackRef null; - _PyStackRef callable; + case _CONVERT_VALUE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef result; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); - null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - if (!PyStackRef_IsNull(null)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + value = _stack_item_0; + conversion_func conv_fn; + assert(oparg >= FVC_STR && oparg <= FVC_ASCII); + conv_fn = _PyEval_ConversionFuncs[oparg]; + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *result_o = conv_fn(PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } - if (Py_TYPE(PyStackRef_AsPyObjectBorrow(callable)) != &PyMethod_Type) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + result = PyStackRef_FromPyObjectSteal(result_o); + _tos_cache0 = result; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _FORMAT_SIMPLE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + value = _stack_item_0; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!PyUnicode_CheckExact(value_o)) { + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyObject_Format(value_o, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + } + else { + res = value; } + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _INIT_CALL_BOUND_METHOD_EXACT_ARGS: { - _PyStackRef self_or_null; - _PyStackRef callable; - oparg = CURRENT_OPARG(); - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(PyStackRef_IsNull(self_or_null)); - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - STAT_INC(CALL, hit); - self_or_null = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); - _PyStackRef temp = callable; - callable = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); - stack_pointer[-2 - oparg] = callable; - stack_pointer[-1 - oparg] = self_or_null; + case _FORMAT_WITH_SPEC_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef fmt_spec; + _PyStackRef value; + _PyStackRef res; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + fmt_spec = _stack_item_1; + value = _stack_item_0; + stack_pointer[0] = value; + stack_pointer[1] = fmt_spec; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(temp); + PyObject *res_o = PyObject_Format(PyStackRef_AsPyObjectBorrow(value), PyStackRef_AsPyObjectBorrow(fmt_spec)); + _PyStackRef tmp = fmt_spec; + fmt_spec = PyStackRef_NULL; + stack_pointer[-1] = fmt_spec; + PyStackRef_CLOSE(tmp); + tmp = value; + value = PyStackRef_NULL; + stack_pointer[-2] = value; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache0 = res; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_PEP_523: { - if (tstate->interp->eval_frame) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _COPY_1_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + bottom = stack_pointer[-1]; + top = PyStackRef_DUP(bottom); + _tos_cache1 = top; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_FUNCTION_EXACT_ARGS: { - _PyStackRef self_or_null; - _PyStackRef callable; - oparg = CURRENT_OPARG(); - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - assert(PyFunction_Check(callable_o)); - PyFunctionObject *func = (PyFunctionObject *)callable_o; - PyCodeObject *code = (PyCodeObject *)func->func_code; - if (code->co_argcount != oparg + (!PyStackRef_IsNull(self_or_null))) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _COPY_1_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + _PyStackRef _stack_item_0 = _tos_cache0; + bottom = _stack_item_0; + top = PyStackRef_DUP(bottom); + _tos_cache1 = top; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_STACK_SPACE: { - _PyStackRef callable; - oparg = CURRENT_OPARG(); - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - PyFunctionObject *func = (PyFunctionObject *)callable_o; - PyCodeObject *code = (PyCodeObject *)func->func_code; - if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _COPY_1_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + bottom = _stack_item_1; + top = PyStackRef_DUP(bottom); + _tos_cache2 = top; + _tos_cache1 = bottom; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_RECURSION_REMAINING: { - if (tstate->py_recursion_remaining <= 1) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _COPY_2_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + bottom = stack_pointer[-2]; + top = PyStackRef_DUP(bottom); + _tos_cache2 = top; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _INIT_CALL_PY_EXACT_ARGS_0: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef new_frame; - oparg = 0; - assert(oparg == CURRENT_OPARG()); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int has_self = !PyStackRef_IsNull(self_or_null); - STAT_INC(CALL, hit); - _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); - _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; - pushed_frame->localsplus[0] = self_or_null; - for (int i = 0; i < oparg; i++) { - first_non_self_local[i] = args[i]; - } - new_frame = PyStackRef_Wrap(pushed_frame); - stack_pointer[-2 - oparg] = new_frame; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + case _COPY_2_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + _PyStackRef _stack_item_0 = _tos_cache0; + bottom = stack_pointer[-1]; + top = PyStackRef_DUP(bottom); + _tos_cache2 = top; + _tos_cache1 = _stack_item_0; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _INIT_CALL_PY_EXACT_ARGS_1: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef new_frame; - oparg = 1; - assert(oparg == CURRENT_OPARG()); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int has_self = !PyStackRef_IsNull(self_or_null); - STAT_INC(CALL, hit); - _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); - _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; - pushed_frame->localsplus[0] = self_or_null; - for (int i = 0; i < oparg; i++) { - first_non_self_local[i] = args[i]; - } - new_frame = PyStackRef_Wrap(pushed_frame); - stack_pointer[-2 - oparg] = new_frame; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + case _COPY_2_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + bottom = _stack_item_0; + top = PyStackRef_DUP(bottom); + _tos_cache2 = top; + _tos_cache1 = _stack_item_1; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COPY_3_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + bottom = stack_pointer[-3]; + top = PyStackRef_DUP(bottom); + _tos_cache2 = top; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = stack_pointer[-2]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COPY_3_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + _PyStackRef _stack_item_0 = _tos_cache0; + bottom = stack_pointer[-2]; + top = PyStackRef_DUP(bottom); + _tos_cache2 = top; + _tos_cache1 = _stack_item_0; + _tos_cache0 = stack_pointer[-1]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COPY_3_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + bottom = stack_pointer[-1]; + top = PyStackRef_DUP(bottom); + _tos_cache2 = top; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COPY_3_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + bottom = _stack_item_0; + top = PyStackRef_DUP(bottom); + _tos_cache2 = top; + _tos_cache1 = _stack_item_2; + _tos_cache0 = _stack_item_1; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer[0] = bottom; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _COPY_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef bottom; + _PyStackRef top; + oparg = CURRENT_OPARG(); + bottom = stack_pointer[-1 - (oparg-1)]; + top = PyStackRef_DUP(bottom); + _tos_cache0 = top; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _INIT_CALL_PY_EXACT_ARGS_2: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef new_frame; - oparg = 2; - assert(oparg == CURRENT_OPARG()); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int has_self = !PyStackRef_IsNull(self_or_null); - STAT_INC(CALL, hit); - _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); - _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; - pushed_frame->localsplus[0] = self_or_null; - for (int i = 0; i < oparg; i++) { - first_non_self_local[i] = args[i]; + case _BINARY_OP_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef rhs; + _PyStackRef lhs; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + rhs = _stack_item_1; + lhs = _stack_item_0; + PyObject *lhs_o = PyStackRef_AsPyObjectBorrow(lhs); + PyObject *rhs_o = PyStackRef_AsPyObjectBorrow(rhs); + assert(_PyEval_BinaryOps[oparg]); + stack_pointer[0] = lhs; + stack_pointer[1] = rhs; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyEval_BinaryOps[oparg](lhs_o, rhs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); } - new_frame = PyStackRef_Wrap(pushed_frame); - stack_pointer[-2 - oparg] = new_frame; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + res = PyStackRef_FromPyObjectSteal(res_o); + l = lhs; + r = rhs; + _tos_cache2 = r; + _tos_cache1 = l; + _tos_cache0 = res; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _INIT_CALL_PY_EXACT_ARGS_3: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef new_frame; - oparg = 3; - assert(oparg == CURRENT_OPARG()); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int has_self = !PyStackRef_IsNull(self_or_null); - STAT_INC(CALL, hit); - _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); - _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; - pushed_frame->localsplus[0] = self_or_null; - for (int i = 0; i < oparg; i++) { - first_non_self_local[i] = args[i]; - } - new_frame = PyStackRef_Wrap(pushed_frame); - stack_pointer[-2 - oparg] = new_frame; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + case _SWAP_2_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef bottom; + top = stack_pointer[-1]; + bottom = stack_pointer[-2]; + _PyStackRef temp = bottom; + bottom = top; + top = temp; + _tos_cache1 = top; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _INIT_CALL_PY_EXACT_ARGS_4: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef new_frame; - oparg = 4; - assert(oparg == CURRENT_OPARG()); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int has_self = !PyStackRef_IsNull(self_or_null); - STAT_INC(CALL, hit); - _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); - _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; - pushed_frame->localsplus[0] = self_or_null; - for (int i = 0; i < oparg; i++) { - first_non_self_local[i] = args[i]; - } - new_frame = PyStackRef_Wrap(pushed_frame); - stack_pointer[-2 - oparg] = new_frame; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + case _SWAP_2_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef bottom; + _PyStackRef _stack_item_0 = _tos_cache0; + top = _stack_item_0; + bottom = stack_pointer[-1]; + _PyStackRef temp = bottom; + bottom = top; + top = temp; + _tos_cache1 = top; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _INIT_CALL_PY_EXACT_ARGS: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef new_frame; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - int has_self = !PyStackRef_IsNull(self_or_null); - STAT_INC(CALL, hit); - _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked(tstate, callable, oparg + has_self, frame); - _PyStackRef *first_non_self_local = pushed_frame->localsplus + has_self; - pushed_frame->localsplus[0] = self_or_null; - for (int i = 0; i < oparg; i++) { - first_non_self_local[i] = args[i]; - } - new_frame = PyStackRef_Wrap(pushed_frame); - stack_pointer[-2 - oparg] = new_frame; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + case _SWAP_2_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef bottom; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + top = _stack_item_1; + bottom = _stack_item_0; + _PyStackRef temp = bottom; + bottom = top; + top = temp; + _tos_cache1 = top; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _PUSH_FRAME: { - _PyStackRef new_frame; - new_frame = stack_pointer[-1]; - assert(tstate->interp->eval_frame == NULL); - _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - assert(temp->previous == frame || temp->previous->previous == frame); - CALL_STAT_INC(inlined_py_calls); - frame = tstate->current_frame = temp; - tstate->py_recursion_remaining--; - LOAD_SP(); - LOAD_IP(0); - LLTRACE_RESUME_FRAME(); + case _SWAP_2_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef bottom; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + top = _stack_item_2; + bottom = _stack_item_1; + _PyStackRef temp = bottom; + bottom = top; + top = temp; + _tos_cache2 = top; + _tos_cache1 = bottom; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_NOS_NULL: { - _PyStackRef null; - null = stack_pointer[-2]; - if (!PyStackRef_IsNull(null)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _SWAP_3_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef bottom; + top = stack_pointer[-1]; + bottom = stack_pointer[-3]; + _PyStackRef temp = bottom; + bottom = top; + top = temp; + _tos_cache2 = top; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_NOS_NOT_NULL: { - _PyStackRef nos; - nos = stack_pointer[-2]; - PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (o == NULL) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _SWAP_3_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef bottom; + _PyStackRef _stack_item_0 = _tos_cache0; + top = _stack_item_0; + bottom = stack_pointer[-2]; + _PyStackRef temp = bottom; + bottom = top; + top = temp; + _tos_cache2 = top; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_THIRD_NULL: { - _PyStackRef null; - null = stack_pointer[-3]; - if (!PyStackRef_IsNull(null)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _SWAP_3_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef bottom; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + top = _stack_item_1; + bottom = stack_pointer[-1]; + _PyStackRef temp = bottom; + bottom = top; + top = temp; + _tos_cache2 = top; + _tos_cache1 = _stack_item_0; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_CALLABLE_TYPE_1: { - _PyStackRef callable; - callable = stack_pointer[-3]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (callable_o != (PyObject *)&PyType_Type) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _SWAP_3_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef bottom; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + top = _stack_item_2; + bottom = _stack_item_0; + _PyStackRef temp = bottom; + bottom = top; + top = temp; + _tos_cache2 = top; + _tos_cache1 = _stack_item_1; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_TYPE_1: { - _PyStackRef arg; - _PyStackRef null; - _PyStackRef callable; - _PyStackRef res; + case _SWAP_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef bottom; + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); - arg = stack_pointer[-1]; - null = stack_pointer[-2]; - callable = stack_pointer[-3]; - PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); - assert(oparg == 1); - (void)callable; - (void)null; - STAT_INC(CALL, hit); - res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); - stack_pointer[-3] = res; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); - stack_pointer = _PyFrame_GetStackPointer(frame); + top = _stack_item_0; + bottom = stack_pointer[-1 - (oparg-2)]; + _PyStackRef temp = bottom; + bottom = top; + top = temp; + _tos_cache0 = top; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer[-1 - (oparg-2)] = bottom; + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_CALLABLE_STR_1: { - _PyStackRef callable; - callable = stack_pointer[-3]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (callable_o != (PyObject *)&PyUnicode_Type) { + /* _INSTRUMENTED_LINE is not a viable micro-op for tier 2 because it is instrumented */ + + /* _INSTRUMENTED_INSTRUCTION is not a viable micro-op for tier 2 because it is instrumented */ + + /* _INSTRUMENTED_JUMP_FORWARD is not a viable micro-op for tier 2 because it is instrumented */ + + /* _MONITOR_JUMP_BACKWARD is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + + /* _INSTRUMENTED_NOT_TAKEN is not a viable micro-op for tier 2 because it is instrumented */ + + /* _INSTRUMENTED_POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 because it is instrumented */ + + /* _INSTRUMENTED_POP_JUMP_IF_FALSE is not a viable micro-op for tier 2 because it is instrumented */ + + /* _INSTRUMENTED_POP_JUMP_IF_NONE is not a viable micro-op for tier 2 because it is instrumented */ + + /* _INSTRUMENTED_POP_JUMP_IF_NOT_NONE is not a viable micro-op for tier 2 because it is instrumented */ + + case _GUARD_IS_TRUE_POP_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + flag = stack_pointer[-1]; + int is_true = PyStackRef_IsTrue(flag); + if (!is_true) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_JUMP_TARGET(); } + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_STR_1: { - _PyStackRef arg; - _PyStackRef null; - _PyStackRef callable; - _PyStackRef res; - oparg = CURRENT_OPARG(); - arg = stack_pointer[-1]; - null = stack_pointer[-2]; - callable = stack_pointer[-3]; - PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); - assert(oparg == 1); - STAT_INC(CALL, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Str(arg_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - (void)callable; - (void)null; - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_ERROR(); + case _GUARD_IS_TRUE_POP_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + flag = _stack_item_0; + int is_true = PyStackRef_IsTrue(flag); + if (!is_true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_CALLABLE_TUPLE_1: { - _PyStackRef callable; - callable = stack_pointer[-3]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (callable_o != (PyObject *)&PyTuple_Type) { + case _GUARD_IS_TRUE_POP_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + flag = _stack_item_1; + int is_true = PyStackRef_IsTrue(flag); + if (!is_true) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_TUPLE_1: { - _PyStackRef arg; - _PyStackRef null; - _PyStackRef callable; - _PyStackRef res; - oparg = CURRENT_OPARG(); - arg = stack_pointer[-1]; - null = stack_pointer[-2]; - callable = stack_pointer[-3]; - PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); - assert(oparg == 1); - STAT_INC(CALL, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PySequence_Tuple(arg_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - (void)callable; - (void)null; - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_ERROR(); + case _GUARD_IS_TRUE_POP_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + flag = _stack_item_2; + int is_true = PyStackRef_IsTrue(flag); + if (!is_true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_AND_ALLOCATE_OBJECT: { - _PyStackRef self_or_null; - _PyStackRef callable; - oparg = CURRENT_OPARG(); - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - uint32_t type_version = (uint32_t)CURRENT_OPERAND0(); - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (!PyStackRef_IsNull(self_or_null)) { + case _GUARD_IS_FALSE_POP_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + flag = stack_pointer[-1]; + int is_false = PyStackRef_IsFalse(flag); + if (!is_false) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_JUMP_TARGET(); } - if (!PyType_Check(callable_o)) { + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_IS_FALSE_POP_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + flag = _stack_item_0; + int is_false = PyStackRef_IsFalse(flag); + if (!is_false) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - PyTypeObject *tp = (PyTypeObject *)callable_o; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(tp->tp_version_tag) != type_version) { + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_IS_FALSE_POP_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + flag = _stack_item_1; + int is_false = PyStackRef_IsFalse(flag); + if (!is_false) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - assert(tp->tp_new == PyBaseObject_Type.tp_new); - assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); - assert(tp->tp_alloc == PyType_GenericAlloc); - PyHeapTypeObject *cls = (PyHeapTypeObject *)callable_o; - PyFunctionObject *init_func = (PyFunctionObject *)FT_ATOMIC_LOAD_PTR_ACQUIRE(cls->_spec_cache.init); - PyCodeObject *code = (PyCodeObject *)init_func->func_code; - if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize + _Py_InitCleanup.co_framesize)) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_IS_FALSE_POP_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + flag = _stack_item_2; + int is_false = PyStackRef_IsFalse(flag); + if (!is_false) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - STAT_INC(CALL, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *self_o = PyType_GenericAlloc(tp, 0); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (self_o == NULL) { - JUMP_TO_ERROR(); - } - self_or_null = PyStackRef_FromPyObjectSteal(self_o); - _PyStackRef temp = callable; - callable = PyStackRef_FromPyObjectNew(init_func); - stack_pointer[-2 - oparg] = callable; - stack_pointer[-1 - oparg] = self_or_null; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(temp); - stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CREATE_INIT_FRAME: { - _PyStackRef *args; - _PyStackRef self; - _PyStackRef init; - _PyStackRef init_frame; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self = stack_pointer[-1 - oparg]; - init = stack_pointer[-2 - oparg]; - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( - tstate, (PyCodeObject *)&_Py_InitCleanup, 1, frame); - stack_pointer = _PyFrame_GetStackPointer(frame); - assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK); - assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE); - shim->localsplus[0] = PyStackRef_DUP(self); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( - tstate, init, NULL, args-1, oparg+1, NULL, shim); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (temp == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_FrameClearAndPop(tstate, shim); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); + case _GUARD_BIT_IS_SET_POP_4_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + flag = stack_pointer[-1]; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); } - frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL; - tstate->py_recursion_remaining--; - init_frame = PyStackRef_Wrap(temp); - stack_pointer[0] = init_frame; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _EXIT_INIT_CHECK: { - _PyStackRef should_be_none; - should_be_none = stack_pointer[-1]; - if (!PyStackRef_IsNone(should_be_none)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - PyErr_Format(PyExc_TypeError, - "__init__() should return None, not '%.200s'", - Py_TYPE(PyStackRef_AsPyObjectBorrow(should_be_none))->tp_name); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_ERROR(); + case _GUARD_BIT_IS_SET_POP_4_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_0; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_BUILTIN_CLASS: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef res; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (!PyType_Check(callable_o)) { + case _GUARD_BIT_IS_SET_POP_4_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_1; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - PyTypeObject *tp = (PyTypeObject *)callable_o; - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } - if (tp->tp_vectorcall == NULL) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_SET_POP_4_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_2; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - STAT_INC(CALL, hit); - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_BUILTIN_O: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef res; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - if (!PyStackRef_IsNull(self_or_null)) { - args--; - total_args++; + case _GUARD_BIT_IS_SET_POP_5_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + flag = stack_pointer[-1]; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); } - if (total_args != 1) { + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_SET_POP_5_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_0; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - if (!PyCFunction_CheckExact(callable_o)) { + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_SET_POP_5_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_1; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - if (PyCFunction_GET_FLAGS(callable_o) != METH_O) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_SET_POP_5_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_2; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - if (_Py_ReachedRecursionLimit(tstate)) { + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_SET_POP_6_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + flag = stack_pointer[-1]; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_JUMP_TARGET(); } - STAT_INC(CALL, hit); - PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); - _PyStackRef arg = args[0]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable_o), PyStackRef_AsPyObjectBorrow(arg)); - stack_pointer = _PyFrame_GetStackPointer(frame); - _Py_LeaveRecursiveCallTstate(tstate); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_BUILTIN_FAST: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef res; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } - if (!PyCFunction_CheckExact(callable_o)) { + case _GUARD_BIT_IS_SET_POP_6_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_0; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - if (PyCFunction_GET_FLAGS(callable_o) != METH_FASTCALL) { + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_SET_POP_6_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_1; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - STAT_INC(CALL, hit); - PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyCFunctionFast_CAST(cfunc)( - PyCFunction_GET_SELF(callable_o), - args_o, - total_args); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_BUILTIN_FAST_WITH_KEYWORDS: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef res; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } - if (!PyCFunction_CheckExact(callable_o)) { + case _GUARD_BIT_IS_SET_POP_6_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_2; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - if (PyCFunction_GET_FLAGS(callable_o) != (METH_FASTCALL | METH_KEYWORDS)) { + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_SET_POP_7_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + flag = stack_pointer[-1]; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_JUMP_TARGET(); } - STAT_INC(CALL, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyCFunctionFastWithKeywords cfunc = - _PyCFunctionFastWithKeywords_CAST(PyCFunction_GET_FUNCTION(callable_o)); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_CALLABLE_LEN: { - _PyStackRef callable; - callable = stack_pointer[-3]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - PyInterpreterState *interp = tstate->interp; - if (callable_o != interp->callable_cache.len) { + case _GUARD_BIT_IS_SET_POP_7_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_0; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_LEN: { - _PyStackRef arg; - _PyStackRef null; - _PyStackRef callable; - _PyStackRef res; - arg = stack_pointer[-1]; - null = stack_pointer[-2]; - callable = stack_pointer[-3]; - (void)null; - STAT_INC(CALL, hit); - PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_ssize_t len_i = PyObject_Length(arg_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (len_i < 0) { - JUMP_TO_ERROR(); - } - PyObject *res_o = PyLong_FromSsize_t(len_i); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - if (res_o == NULL) { - JUMP_TO_ERROR(); + case _GUARD_BIT_IS_SET_POP_7_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_1; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_CALLABLE_ISINSTANCE: { - _PyStackRef callable; - callable = stack_pointer[-4]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - PyInterpreterState *interp = tstate->interp; - if (callable_o != interp->callable_cache.isinstance) { + case _GUARD_BIT_IS_SET_POP_7_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_2; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_ISINSTANCE: { - _PyStackRef cls; - _PyStackRef instance; - _PyStackRef null; - _PyStackRef callable; - _PyStackRef res; - cls = stack_pointer[-1]; - instance = stack_pointer[-2]; - null = stack_pointer[-3]; - callable = stack_pointer[-4]; - STAT_INC(CALL, hit); - PyObject *inst_o = PyStackRef_AsPyObjectBorrow(instance); - PyObject *cls_o = PyStackRef_AsPyObjectBorrow(cls); - _PyFrame_SetStackPointer(frame, stack_pointer); - int retval = PyObject_IsInstance(inst_o, cls_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (retval < 0) { - JUMP_TO_ERROR(); + case _GUARD_BIT_IS_SET_POP_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + oparg = CURRENT_OPARG(); + flag = stack_pointer[-1]; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); } - (void)null; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(cls); - stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(instance); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); - res = retval ? PyStackRef_True : PyStackRef_False; - assert((!PyStackRef_IsNull(res)) ^ (_PyErr_Occurred(tstate) != NULL)); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_CALLABLE_LIST_APPEND: { - _PyStackRef callable; - callable = stack_pointer[-3]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - PyInterpreterState *interp = tstate->interp; - if (callable_o != interp->callable_cache.list_append) { + case _GUARD_BIT_IS_SET_POP_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + flag = _stack_item_0; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_LIST_APPEND: { - _PyStackRef arg; - _PyStackRef self; - _PyStackRef callable; + case _GUARD_BIT_IS_SET_POP_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; oparg = CURRENT_OPARG(); - arg = stack_pointer[-1]; - self = stack_pointer[-2]; - callable = stack_pointer[-3]; - assert(oparg == 1); - PyObject *self_o = PyStackRef_AsPyObjectBorrow(self); - if (!PyList_CheckExact(self_o)) { + flag = _stack_item_1; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - if (!LOCK_OBJECT(self_o)) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_SET_POP_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + flag = _stack_item_2; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set == 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - STAT_INC(CALL, hit); - int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); - UNLOCK_OBJECT(self_o); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(self); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) { - JUMP_TO_ERROR(); - } - #if TIER_ONE + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); + case _GUARD_BIT_IS_UNSET_POP_4_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + flag = stack_pointer[-1]; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); + } + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_METHOD_DESCRIPTOR_O: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef res; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - if (total_args != 2) { + case _GUARD_BIT_IS_UNSET_POP_4_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_0; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_4_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_1; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - PyMethodDef *meth = method->d_method; - if (meth->ml_flags != METH_O) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_4_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 4; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_2; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - if (_Py_ReachedRecursionLimit(tstate)) { + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_5_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + flag = stack_pointer[-1]; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_JUMP_TARGET(); } - _PyStackRef arg_stackref = arguments[1]; - _PyStackRef self_stackref = arguments[0]; - if (!Py_IS_TYPE(PyStackRef_AsPyObjectBorrow(self_stackref), - method->d_common.d_type)) { + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_5_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_0; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - STAT_INC(CALL, hit); - PyCFunction cfunc = meth->ml_meth; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, - PyStackRef_AsPyObjectBorrow(self_stackref), - PyStackRef_AsPyObjectBorrow(arg_stackref)); - stack_pointer = _PyFrame_GetStackPointer(frame); - _Py_LeaveRecursiveCallTstate(tstate); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef res; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } - if (total_args == 0) { + case _GUARD_BIT_IS_UNSET_POP_5_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_1; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_5_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 5; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_2; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - PyMethodDef *meth = method->d_method; - if (meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS)) { + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_6_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + flag = stack_pointer[-1]; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_JUMP_TARGET(); } - PyTypeObject *d_type = method->d_common.d_type; - PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); - assert(self != NULL); - if (!Py_IS_TYPE(self, d_type)) { + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_6_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_0; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - STAT_INC(CALL, hit); - int nargs = total_args - 1; - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyCFunctionFastWithKeywords cfunc = - _PyCFunctionFastWithKeywords_CAST(meth->ml_meth); - PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_METHOD_DESCRIPTOR_NOARGS: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef res; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - assert(oparg == 0 || oparg == 1); - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - if (!PyStackRef_IsNull(self_or_null)) { - args--; - total_args++; + case _GUARD_BIT_IS_UNSET_POP_6_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_1; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } - if (total_args != 1) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_6_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 6; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_2; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_7_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + flag = stack_pointer[-1]; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_JUMP_TARGET(); } - PyMethodDef *meth = method->d_method; - _PyStackRef self_stackref = args[0]; - PyObject *self = PyStackRef_AsPyObjectBorrow(self_stackref); - if (!Py_IS_TYPE(self, method->d_common.d_type)) { + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_7_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_0; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - if (meth->ml_flags != METH_NOARGS) { + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_7_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_1; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - if (_Py_ReachedRecursionLimit(tstate)) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_7_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = 7; + assert(oparg == CURRENT_OPARG()); + flag = _stack_item_2; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - STAT_INC(CALL, hit); - PyCFunction cfunc = meth->ml_meth; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, self, NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - _Py_LeaveRecursiveCallTstate(tstate); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(self_stackref); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_METHOD_DESCRIPTOR_FAST: { - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef res; + case _GUARD_BIT_IS_UNSET_POP_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; - callable = stack_pointer[-2 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } - if (total_args == 0) { + flag = stack_pointer[-1]; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_JUMP_TARGET(); } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + oparg = CURRENT_OPARG(); + flag = _stack_item_0; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - PyMethodDef *meth = method->d_method; - if (meth->ml_flags != METH_FASTCALL) { + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); + flag = _stack_item_1; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); - assert(self != NULL); - if (!Py_IS_TYPE(self, method->d_common.d_type)) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef flag; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + flag = _stack_item_2; + #ifdef Py_STACKREF_DEBUG + uintptr_t bits = flag.index; + #else + uintptr_t bits = flag.bits; + #endif + uintptr_t set = (1 << oparg) & bits; + if (set != 0) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - STAT_INC(CALL, hit); - int nargs = total_args - 1; - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_IS_NONE_POP_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef val; + val = stack_pointer[-1]; + int is_none = PyStackRef_IsNone(val); + if (!is_none) { + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); + PyStackRef_CLOSE(val); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyCFunctionFast cfunc = _PyCFunctionFast_CAST(meth->ml_meth); - PyObject *res_o = cfunc(self, (args_o + 1), nargs); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_ERROR(); + if (1) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _MONITOR_CALL_KW is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ - - case _MAYBE_EXPAND_METHOD_KW: { - _PyStackRef self_or_null; - _PyStackRef callable; - oparg = CURRENT_OPARG(); - self_or_null = stack_pointer[-2 - oparg]; - callable = stack_pointer[-3 - oparg]; - if (PyStackRef_TYPE(callable) == &PyMethod_Type && PyStackRef_IsNull(self_or_null)) { - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - PyObject *self = ((PyMethodObject *)callable_o)->im_self; - self_or_null = PyStackRef_FromPyObjectNew(self); - PyObject *method = ((PyMethodObject *)callable_o)->im_func; - _PyStackRef temp = callable; - callable = PyStackRef_FromPyObjectNew(method); - stack_pointer[-3 - oparg] = callable; - stack_pointer[-2 - oparg] = self_or_null; + case _GUARD_IS_NONE_POP_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef val; + _PyStackRef _stack_item_0 = _tos_cache0; + val = _stack_item_0; + int is_none = PyStackRef_IsNone(val); + if (!is_none) { _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(temp); + PyStackRef_CLOSE(val); stack_pointer = _PyFrame_GetStackPointer(frame); + if (1) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } } - stack_pointer[-3 - oparg] = callable; - stack_pointer[-2 - oparg] = self_or_null; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _DO_CALL_KW is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + case _GUARD_IS_NONE_POP_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef val; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + val = _stack_item_1; + int is_none = PyStackRef_IsNone(val); + if (!is_none) { + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(val); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (1) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - case _PY_FRAME_KW: { - _PyStackRef kwnames; - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef new_frame; - oparg = CURRENT_OPARG(); - kwnames = stack_pointer[-1]; - args = &stack_pointer[-1 - oparg]; - self_or_null = stack_pointer[-2 - oparg]; - callable = stack_pointer[-3 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; + case _GUARD_IS_NONE_POP_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef val; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + val = _stack_item_2; + int is_none = PyStackRef_IsNone(val); + if (!is_none) { + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(val); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (1) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); + } } - PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); - int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); - assert(Py_TYPE(callable_o) == &PyFunction_Type); - int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; - PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(callable_o)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyInterpreterFrame *temp = _PyEvalFramePushAndInit( - tstate, callable, locals, - arguments, positional_args, kwnames_o, frame - ); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_IS_NOT_NONE_POP_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef val; + _PyStackRef _stack_item_0 = _tos_cache0; + val = _stack_item_0; + int is_none = PyStackRef_IsNone(val); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(kwnames); + PyStackRef_CLOSE(val); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (temp == NULL) { - JUMP_TO_ERROR(); + if (is_none) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - new_frame = PyStackRef_Wrap(temp); - stack_pointer[0] = new_frame; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = PyStackRef_ZERO_BITS; + _tos_cache1 = PyStackRef_ZERO_BITS; + _tos_cache2 = PyStackRef_ZERO_BITS; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _JUMP_TO_TOP_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + JUMP_TO_JUMP_TARGET(); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SET_IP_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + PyObject *instr_ptr = (PyObject *)CURRENT_OPERAND0_64(); + frame->instr_ptr = (_Py_CODEUNIT *)instr_ptr; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SET_IP_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + PyObject *instr_ptr = (PyObject *)CURRENT_OPERAND0_64(); + frame->instr_ptr = (_Py_CODEUNIT *)instr_ptr; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_FUNCTION_VERSION_KW: { - _PyStackRef callable; - oparg = CURRENT_OPARG(); - callable = stack_pointer[-3 - oparg]; - uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (!PyFunction_Check(callable_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - PyFunctionObject *func = (PyFunctionObject *)callable_o; - if (func->func_version != func_version) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + case _SET_IP_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + PyObject *instr_ptr = (PyObject *)CURRENT_OPERAND0_64(); + frame->instr_ptr = (_Py_CODEUNIT *)instr_ptr; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_METHOD_VERSION_KW: { - _PyStackRef null; - _PyStackRef callable; - oparg = CURRENT_OPARG(); - null = stack_pointer[-2 - oparg]; - callable = stack_pointer[-3 - oparg]; - uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (Py_TYPE(callable_o) != &PyMethod_Type) { + case _SET_IP_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + PyObject *instr_ptr = (PyObject *)CURRENT_OPERAND0_64(); + frame->instr_ptr = (_Py_CODEUNIT *)instr_ptr; + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_STACK_SPACE_OPERAND_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + uint32_t framesize = (uint32_t)CURRENT_OPERAND0_32(); + assert(framesize <= INT_MAX); + if (!_PyThreadState_HasStackSpace(tstate, framesize)) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } - PyObject *func = ((PyMethodObject *)callable_o)->im_func; - if (!PyFunction_Check(func)) { + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_STACK_SPACE_OPERAND_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + uint32_t framesize = (uint32_t)CURRENT_OPERAND0_32(); + assert(framesize <= INT_MAX); + if (!_PyThreadState_HasStackSpace(tstate, framesize)) { UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); JUMP_TO_JUMP_TARGET(); } - if (((PyFunctionObject *)func)->func_version != func_version) { + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_STACK_SPACE_OPERAND_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + uint32_t framesize = (uint32_t)CURRENT_OPERAND0_32(); + assert(framesize <= INT_MAX); + if (!_PyThreadState_HasStackSpace(tstate, framesize)) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } - if (!PyStackRef_IsNull(null)) { + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _CHECK_STACK_SPACE_OPERAND_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + uint32_t framesize = (uint32_t)CURRENT_OPERAND0_32(); + assert(framesize <= INT_MAX); + if (!_PyThreadState_HasStackSpace(tstate, framesize)) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _EXPAND_METHOD_KW: { - _PyStackRef self_or_null; - _PyStackRef callable; + case _SAVE_RETURN_OFFSET_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); oparg = CURRENT_OPARG(); - self_or_null = stack_pointer[-2 - oparg]; - callable = stack_pointer[-3 - oparg]; - assert(PyStackRef_IsNull(self_or_null)); - _PyStackRef callable_s = callable; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - assert(Py_TYPE(callable_o) == &PyMethod_Type); - self_or_null = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_self); - callable = PyStackRef_FromPyObjectNew(((PyMethodObject *)callable_o)->im_func); - assert(PyStackRef_FunctionCheck(callable)); - stack_pointer[-3 - oparg] = callable; - stack_pointer[-2 - oparg] = self_or_null; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable_s); - stack_pointer = _PyFrame_GetStackPointer(frame); + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_IS_NOT_PY_CALLABLE_KW: { - _PyStackRef callable; + case _SAVE_RETURN_OFFSET_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); - callable = stack_pointer[-3 - oparg]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - if (PyFunction_Check(callable_o)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - if (Py_TYPE(callable_o) == &PyMethod_Type) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CALL_KW_NON_PY: { - _PyStackRef kwnames; - _PyStackRef *args; - _PyStackRef self_or_null; - _PyStackRef callable; - _PyStackRef res; + case _SAVE_RETURN_OFFSET_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; oparg = CURRENT_OPARG(); - kwnames = stack_pointer[-1]; - args = &stack_pointer[-1 - oparg]; - self_or_null = stack_pointer[-2 - oparg]; - callable = stack_pointer[-3 - oparg]; #if TIER_ONE - assert(opcode != INSTRUMENTED_CALL); + frame->return_offset = (uint16_t)(next_instr - this_instr); #endif - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; + #if TIER_TWO + frame->return_offset = oparg; + #endif + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SAVE_RETURN_OFFSET_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + oparg = CURRENT_OPARG(); + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _EXIT_TRACE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + PyObject *exit_p = (PyObject *)CURRENT_OPERAND0_64(); + _PyExitData *exit = (_PyExitData *)exit_p; + #if defined(Py_DEBUG) && !defined(_Py_JIT) + const _Py_CODEUNIT *target = ((frame->owner == FRAME_OWNED_BY_INTERPRETER) + ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame)) + + exit->target; + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + if (frame->lltrace >= 3) { + _PyFrame_SetStackPointer(frame, stack_pointer); + printf("SIDE EXIT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(", exit %tu, temp %d, target %d -> %s, is_control_flow %d]\n", + exit - current_executor->exits, exit->temperature.value_and_backoff, + (int)(target - _PyFrame_GetBytecode(frame)), + _PyOpcode_OpName[target->op.code], exit->is_control_flow); + stack_pointer = _PyFrame_GetStackPointer(frame); } - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { + #endif + tstate->jit_exit = exit; + SET_CURRENT_CACHED_VALUES(0); + TIER2_TO_TIER2(exit->executor); + } + + case _EXIT_TRACE_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + PyObject *exit_p = (PyObject *)CURRENT_OPERAND0_64(); + _PyExitData *exit = (_PyExitData *)exit_p; + #if defined(Py_DEBUG) && !defined(_Py_JIT) + const _Py_CODEUNIT *target = ((frame->owner == FRAME_OWNED_BY_INTERPRETER) + ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame)) + + exit->target; + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + if (frame->lltrace >= 3) { + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = kwnames; - kwnames = PyStackRef_NULL; - stack_pointer[-1] = kwnames; - PyStackRef_CLOSE(tmp); - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-2 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-3 - oparg] = callable; - PyStackRef_CLOSE(tmp); + printf("SIDE EXIT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(", exit %tu, temp %d, target %d -> %s, is_control_flow %d]\n", + exit - current_executor->exits, exit->temperature.value_and_backoff, + (int)(target - _PyFrame_GetBytecode(frame)), + _PyOpcode_OpName[target->op.code], exit->is_control_flow); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_ERROR(); + stack_pointer += -1; } - PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); - int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(kwnames); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); + #endif + tstate->jit_exit = exit; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + TIER2_TO_TIER2(exit->executor); + } + + case _EXIT_TRACE_r20: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + PyObject *exit_p = (PyObject *)CURRENT_OPERAND0_64(); + _PyExitData *exit = (_PyExitData *)exit_p; + #if defined(Py_DEBUG) && !defined(_Py_JIT) + const _Py_CODEUNIT *target = ((frame->owner == FRAME_OWNED_BY_INTERPRETER) + ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame)) + + exit->target; + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + if (frame->lltrace >= 3) { + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + printf("SIDE EXIT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(", exit %tu, temp %d, target %d -> %s, is_control_flow %d]\n", + exit - current_executor->exits, exit->temperature.value_and_backoff, + (int)(target - _PyFrame_GetBytecode(frame)), + _PyOpcode_OpName[target->op.code], exit->is_control_flow); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_ERROR(); + #endif + tstate->jit_exit = exit; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + TIER2_TO_TIER2(exit->executor); + } + + case _EXIT_TRACE_r30: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + PyObject *exit_p = (PyObject *)CURRENT_OPERAND0_64(); + _PyExitData *exit = (_PyExitData *)exit_p; + #if defined(Py_DEBUG) && !defined(_Py_JIT) + const _Py_CODEUNIT *target = ((frame->owner == FRAME_OWNED_BY_INTERPRETER) + ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame)) + + exit->target; + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + if (frame->lltrace >= 3) { + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = _stack_item_2; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + printf("SIDE EXIT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(", exit %tu, temp %d, target %d -> %s, is_control_flow %d]\n", + exit - current_executor->exits, exit->temperature.value_and_backoff, + (int)(target - _PyFrame_GetBytecode(frame)), + _PyOpcode_OpName[target->op.code], exit->is_control_flow); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; + #endif + tstate->jit_exit = exit; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = _stack_item_2; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + TIER2_TO_TIER2(exit->executor); + } + + case _DYNAMIC_EXIT_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + PyObject *exit_p = (PyObject *)CURRENT_OPERAND0_64(); + #if defined(Py_DEBUG) && !defined(_Py_JIT) + _PyExitData *exit = (_PyExitData *)exit_p; + _Py_CODEUNIT *target = frame->instr_ptr; + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + if (frame->lltrace >= 3) { + _PyFrame_SetStackPointer(frame, stack_pointer); + printf("DYNAMIC EXIT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(", exit %tu, temp %d, target %d -> %s]\n", + exit - current_executor->exits, exit->temperature.value_and_backoff, + (int)(target - _PyFrame_GetBytecode(frame)), + _PyOpcode_OpName[target->op.code]); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + #endif + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE(frame->instr_ptr); } - case _MAKE_CALLARGS_A_TUPLE: { - _PyStackRef callargs; - _PyStackRef func; - callargs = stack_pointer[-2]; - func = stack_pointer[-4]; - PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); - if (!PyTuple_CheckExact(callargs_o)) { + case _DYNAMIC_EXIT_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + PyObject *exit_p = (PyObject *)CURRENT_OPERAND0_64(); + #if defined(Py_DEBUG) && !defined(_Py_JIT) + _PyExitData *exit = (_PyExitData *)exit_p; + _Py_CODEUNIT *target = frame->instr_ptr; + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + if (frame->lltrace >= 3) { + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); + printf("DYNAMIC EXIT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(", exit %tu, temp %d, target %d -> %s]\n", + exit - current_executor->exits, exit->temperature.value_and_backoff, + (int)(target - _PyFrame_GetBytecode(frame)), + _PyOpcode_OpName[target->op.code]); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - JUMP_TO_ERROR(); - } + stack_pointer += -1; + } + #endif + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + GOTO_TIER_ONE(frame->instr_ptr); + } + + case _DYNAMIC_EXIT_r20: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + PyObject *exit_p = (PyObject *)CURRENT_OPERAND0_64(); + #if defined(Py_DEBUG) && !defined(_Py_JIT) + _PyExitData *exit = (_PyExitData *)exit_p; + _Py_CODEUNIT *target = frame->instr_ptr; + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + if (frame->lltrace >= 3) { + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *tuple_o = PySequence_Tuple(callargs_o); + printf("DYNAMIC EXIT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(", exit %tu, temp %d, target %d -> %s]\n", + exit - current_executor->exits, exit->temperature.value_and_backoff, + (int)(target - _PyFrame_GetBytecode(frame)), + _PyOpcode_OpName[target->op.code]); stack_pointer = _PyFrame_GetStackPointer(frame); - if (tuple_o == NULL) { - JUMP_TO_ERROR(); - } - _PyStackRef temp = callargs; - callargs = PyStackRef_FromPyObjectSteal(tuple_o); - stack_pointer[-2] = callargs; + stack_pointer += -2; + } + #endif + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + GOTO_TIER_ONE(frame->instr_ptr); + } + + case _DYNAMIC_EXIT_r30: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + PyObject *exit_p = (PyObject *)CURRENT_OPERAND0_64(); + #if defined(Py_DEBUG) && !defined(_Py_JIT) + _PyExitData *exit = (_PyExitData *)exit_p; + _Py_CODEUNIT *target = frame->instr_ptr; + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + if (frame->lltrace >= 3) { + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = _stack_item_2; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(temp); + printf("DYNAMIC EXIT: [UOp "); + _PyUOpPrint(&next_uop[-1]); + printf(", exit %tu, temp %d, target %d -> %s]\n", + exit - current_executor->exits, exit->temperature.value_and_backoff, + (int)(target - _PyFrame_GetBytecode(frame)), + _PyOpcode_OpName[target->op.code]); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + } + #endif + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = _stack_item_2; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + GOTO_TIER_ONE(frame->instr_ptr); + } + + case _CHECK_VALIDITY_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + if (!current_executor->vm_data.valid) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - stack_pointer[-2] = callargs; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _DO_CALL_FUNCTION_EX is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ - - case _MAKE_FUNCTION: { - _PyStackRef codeobj_st; - _PyStackRef func; - codeobj_st = stack_pointer[-1]; - PyObject *codeobj = PyStackRef_AsPyObjectBorrow(codeobj_st); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyFunctionObject *func_obj = (PyFunctionObject *) - PyFunction_New(codeobj, GLOBALS()); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(codeobj_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (func_obj == NULL) { - JUMP_TO_ERROR(); + case _CHECK_VALIDITY_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + if (!current_executor->vm_data.valid) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } - _PyFunction_SetVersion( - func_obj, ((PyCodeObject *)codeobj)->co_version); - func = PyStackRef_FromPyObjectSteal((PyObject *)func_obj); - stack_pointer[0] = func; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _SET_FUNCTION_ATTRIBUTE: { - _PyStackRef func_in; - _PyStackRef attr_st; - _PyStackRef func_out; - oparg = CURRENT_OPARG(); - func_in = stack_pointer[-1]; - attr_st = stack_pointer[-2]; - PyObject *func = PyStackRef_AsPyObjectBorrow(func_in); - PyObject *attr = PyStackRef_AsPyObjectSteal(attr_st); - func_out = func_in; - assert(PyFunction_Check(func)); - size_t offset = _Py_FunctionAttributeOffsets[oparg]; - assert(offset != 0); - PyObject **ptr = (PyObject **)(((char *)func) + offset); - assert(*ptr == NULL); - *ptr = attr; - stack_pointer[-2] = func_out; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _CHECK_VALIDITY_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + if (!current_executor->vm_data.valid) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _RETURN_GENERATOR: { - _PyStackRef res; - assert(PyStackRef_FunctionCheck(frame->f_funcobj)); - PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (gen == NULL) { - JUMP_TO_ERROR(); + case _CHECK_VALIDITY_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + if (!current_executor->vm_data.valid) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); } - assert(STACK_LEVEL() == 0); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyInterpreterFrame *gen_frame = &gen->gi_iframe; - frame->instr_ptr++; - _PyFrame_Copy(frame, gen_frame); - assert(frame->frame_obj == NULL); - gen->gi_frame_state = FRAME_CREATED; - gen_frame->owner = FRAME_OWNED_BY_GENERATOR; - _Py_LeaveRecursiveCallPy(tstate); - _PyInterpreterFrame *prev = frame->previous; - _PyThreadState_PopFrame(tstate, frame); - frame = tstate->current_frame = prev; - LOAD_IP(frame->return_offset); - stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_FromPyObjectStealMortal((PyObject *)gen); - LLTRACE_RESUME_FRAME(); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BUILD_SLICE: { - _PyStackRef *args; - _PyStackRef slice; - oparg = CURRENT_OPARG(); - args = &stack_pointer[-oparg]; - PyObject *start_o = PyStackRef_AsPyObjectBorrow(args[0]); - PyObject *stop_o = PyStackRef_AsPyObjectBorrow(args[1]); - PyObject *step_o = oparg == 3 ? PyStackRef_AsPyObjectBorrow(args[2]) : NULL; - PyObject *slice_o = PySlice_New(start_o, stop_o, step_o); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); - if (slice_o == NULL) { - JUMP_TO_ERROR(); - } - slice = PyStackRef_FromPyObjectStealMortal(slice_o); - stack_pointer[0] = slice; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _LOAD_CONST_INLINE_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0_64(); + value = PyStackRef_FromPyObjectNew(ptr); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CONVERT_VALUE: { + case _LOAD_CONST_INLINE_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef result; - oparg = CURRENT_OPARG(); - value = stack_pointer[-1]; - conversion_func conv_fn; - assert(oparg >= FVC_STR && oparg <= FVC_ASCII); - conv_fn = _PyEval_ConversionFuncs[oparg]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *result_o = conv_fn(PyStackRef_AsPyObjectBorrow(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (result_o == NULL) { - JUMP_TO_ERROR(); - } - result = PyStackRef_FromPyObjectSteal(result_o); - stack_pointer[0] = result; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef _stack_item_0 = _tos_cache0; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0_64(); + value = PyStackRef_FromPyObjectNew(ptr); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _FORMAT_SIMPLE: { + case _LOAD_CONST_INLINE_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef value; - _PyStackRef res; - value = stack_pointer[-1]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (!PyUnicode_CheckExact(value_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Format(value_o, NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0_64(); + value = PyStackRef_FromPyObjectNew(ptr); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_CONST_INLINE_BORROW_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0_64(); + value = PyStackRef_FromPyObjectBorrow(ptr); + _tos_cache0 = value; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_CONST_INLINE_BORROW_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0_64(); + value = PyStackRef_FromPyObjectBorrow(ptr); + _tos_cache1 = value; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _LOAD_CONST_INLINE_BORROW_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef value; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + PyObject *ptr = (PyObject *)CURRENT_OPERAND0_64(); + value = PyStackRef_FromPyObjectBorrow(ptr); + _tos_cache2 = value; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _RROT_3_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef middle; + _PyStackRef bottom; + top = stack_pointer[-1]; + middle = stack_pointer[-2]; + bottom = stack_pointer[-3]; + _PyStackRef temp = top; + top = middle; + middle = bottom; + bottom = temp; + _tos_cache2 = top; + _tos_cache1 = middle; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _RROT_3_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef middle; + _PyStackRef bottom; + _PyStackRef _stack_item_0 = _tos_cache0; + top = _stack_item_0; + middle = stack_pointer[-1]; + bottom = stack_pointer[-2]; + _PyStackRef temp = top; + top = middle; + middle = bottom; + bottom = temp; + _tos_cache2 = top; + _tos_cache1 = middle; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _RROT_3_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef middle; + _PyStackRef bottom; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + top = _stack_item_1; + middle = _stack_item_0; + bottom = stack_pointer[-1]; + _PyStackRef temp = top; + top = middle; + middle = bottom; + bottom = temp; + _tos_cache2 = top; + _tos_cache1 = middle; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _RROT_3_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef top; + _PyStackRef middle; + _PyStackRef bottom; + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + top = _stack_item_2; + middle = _stack_item_1; + bottom = _stack_item_0; + _PyStackRef temp = top; + top = middle; + middle = bottom; + bottom = temp; + _tos_cache2 = top; + _tos_cache1 = middle; + _tos_cache0 = bottom; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _START_EXECUTOR_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + PyObject *executor = (PyObject *)CURRENT_OPERAND0_64(); + #ifndef _Py_JIT + assert(current_executor == (_PyExecutorObject*)executor); + #endif + assert(tstate->jit_exit == NULL || tstate->jit_exit->executor == current_executor); + tstate->current_executor = (PyObject *)current_executor; + if (!current_executor->vm_data.valid) { + assert(tstate->jit_exit->executor == current_executor); + assert(tstate->current_executor == executor); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); + _PyExecutor_ClearExit(tstate->jit_exit); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_ERROR(); + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - res = PyStackRef_FromPyObjectSteal(res_o); - } - else { - res = value; - stack_pointer += -1; } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _FORMAT_WITH_SPEC: { - _PyStackRef fmt_spec; - _PyStackRef value; - _PyStackRef res; - fmt_spec = stack_pointer[-1]; - value = stack_pointer[-2]; + case _MAKE_WARM_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + current_executor->vm_data.cold = false; + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MAKE_WARM_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + current_executor->vm_data.cold = false; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MAKE_WARM_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + current_executor->vm_data.cold = false; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _MAKE_WARM_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + current_executor->vm_data.cold = false; + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _FATAL_ERROR_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + assert(0); + Py_FatalError("Fatal error uop executed."); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _FATAL_ERROR_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + assert(0); + Py_FatalError("Fatal error uop executed."); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _FATAL_ERROR_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + assert(0); + Py_FatalError("Fatal error uop executed."); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _FATAL_ERROR_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + assert(0); + Py_FatalError("Fatal error uop executed."); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _DEOPT_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE((frame->owner == FRAME_OWNED_BY_INTERPRETER) + ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + } + + case _DEOPT_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE((frame->owner == FRAME_OWNED_BY_INTERPRETER) + ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + } + + case _DEOPT_r20: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE((frame->owner == FRAME_OWNED_BY_INTERPRETER) + ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + } + + case _DEOPT_r30: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = _stack_item_2; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE((frame->owner == FRAME_OWNED_BY_INTERPRETER) + ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + } + + case _HANDLE_PENDING_AND_DEOPT_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Format(PyStackRef_AsPyObjectBorrow(value), PyStackRef_AsPyObjectBorrow(fmt_spec)); - _PyStackRef tmp = fmt_spec; - fmt_spec = PyStackRef_NULL; - stack_pointer[-1] = fmt_spec; - PyStackRef_CLOSE(tmp); - tmp = value; - value = PyStackRef_NULL; - stack_pointer[-2] = value; - PyStackRef_CLOSE(tmp); + int err = _Py_HandlePending(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE(err ? NULL : _PyFrame_GetBytecode(frame) + CURRENT_TARGET()); } - case _COPY_1: { - _PyStackRef bottom; - _PyStackRef top; - bottom = stack_pointer[-1]; - top = PyStackRef_DUP(bottom); - stack_pointer[0] = top; + case _HANDLE_PENDING_AND_DEOPT_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + stack_pointer[0] = _stack_item_0; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_HandlePending(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE(err ? NULL : _PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + } + + case _HANDLE_PENDING_AND_DEOPT_r20: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_HandlePending(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE(err ? NULL : _PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + } + + case _HANDLE_PENDING_AND_DEOPT_r30: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = _stack_item_2; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_HandlePending(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE(err ? NULL : _PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + } + + case _ERROR_POP_N_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + oparg = CURRENT_OPARG(); + uint32_t target = (uint32_t)CURRENT_OPERAND0_32(); + assert(oparg == 0); + frame->instr_ptr = _PyFrame_GetBytecode(frame) + target; + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE(NULL); + } + + case _SPILL_OR_RELOAD_r01: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _tos_cache0 = stack_pointer[-1]; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _COPY_2: { - _PyStackRef bottom; - _PyStackRef top; - bottom = stack_pointer[-2]; - top = PyStackRef_DUP(bottom); - stack_pointer[0] = top; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _SPILL_OR_RELOAD_r02: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = stack_pointer[-2]; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _COPY_3: { - _PyStackRef bottom; - _PyStackRef top; - bottom = stack_pointer[-3]; - top = PyStackRef_DUP(bottom); - stack_pointer[0] = top; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _SPILL_OR_RELOAD_r03: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _tos_cache2 = stack_pointer[-1]; + _tos_cache1 = stack_pointer[-2]; + _tos_cache0 = stack_pointer[-3]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _COPY: { - _PyStackRef bottom; - _PyStackRef top; - oparg = CURRENT_OPARG(); - bottom = stack_pointer[-1 - (oparg-1)]; - top = PyStackRef_DUP(bottom); - stack_pointer[0] = top; + case _SPILL_OR_RELOAD_r10: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[0] = _stack_item_0; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _BINARY_OP: { - _PyStackRef rhs; - _PyStackRef lhs; - _PyStackRef res; - oparg = CURRENT_OPARG(); - rhs = stack_pointer[-1]; - lhs = stack_pointer[-2]; - PyObject *lhs_o = PyStackRef_AsPyObjectBorrow(lhs); - PyObject *rhs_o = PyStackRef_AsPyObjectBorrow(rhs); - assert(_PyEval_BinaryOps[oparg]); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyEval_BinaryOps[oparg](lhs_o, rhs_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_ERROR(); - } - res = PyStackRef_FromPyObjectSteal(res_o); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = lhs; - lhs = res; - stack_pointer[-2] = lhs; - PyStackRef_CLOSE(tmp); - tmp = rhs; - rhs = PyStackRef_NULL; - stack_pointer[-1] = rhs; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _SPILL_OR_RELOAD_r12: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _tos_cache1 = _stack_item_0; + _tos_cache0 = stack_pointer[-1]; + SET_CURRENT_CACHED_VALUES(2); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _SWAP_2: { - _PyStackRef top; - _PyStackRef bottom; - top = stack_pointer[-1]; - bottom = stack_pointer[-2]; - _PyStackRef temp = bottom; - bottom = top; - top = temp; - stack_pointer[-2] = bottom; - stack_pointer[-1] = top; + case _SPILL_OR_RELOAD_r13: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _tos_cache2 = _stack_item_0; + _tos_cache1 = stack_pointer[-1]; + _tos_cache0 = stack_pointer[-2]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _SWAP_3: { - _PyStackRef top; - _PyStackRef bottom; - top = stack_pointer[-1]; - bottom = stack_pointer[-3]; - _PyStackRef temp = bottom; - bottom = top; - top = temp; - stack_pointer[-3] = bottom; - stack_pointer[-1] = top; + case _SPILL_OR_RELOAD_r20: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _SWAP: { - _PyStackRef top; - _PyStackRef bottom; - oparg = CURRENT_OPARG(); - top = stack_pointer[-1]; - bottom = stack_pointer[-2 - (oparg-2)]; - _PyStackRef temp = bottom; - bottom = top; - top = temp; - stack_pointer[-2 - (oparg-2)] = bottom; - stack_pointer[-1] = top; + case _SPILL_OR_RELOAD_r21: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _tos_cache0 = _stack_item_1; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - /* _INSTRUMENTED_LINE is not a viable micro-op for tier 2 because it is instrumented */ - - /* _INSTRUMENTED_INSTRUCTION is not a viable micro-op for tier 2 because it is instrumented */ - - /* _INSTRUMENTED_JUMP_FORWARD is not a viable micro-op for tier 2 because it is instrumented */ - - /* _MONITOR_JUMP_BACKWARD is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ - - /* _INSTRUMENTED_NOT_TAKEN is not a viable micro-op for tier 2 because it is instrumented */ - - /* _INSTRUMENTED_POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 because it is instrumented */ + case _SPILL_OR_RELOAD_r23: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _tos_cache2 = _stack_item_1; + _tos_cache1 = _stack_item_0; + _tos_cache0 = stack_pointer[-1]; + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SPILL_OR_RELOAD_r30: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + SET_CURRENT_CACHED_VALUES(0); + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer[2] = _stack_item_2; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _SPILL_OR_RELOAD_r31: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + _tos_cache0 = _stack_item_2; + SET_CURRENT_CACHED_VALUES(1); + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = _stack_item_1; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - /* _INSTRUMENTED_POP_JUMP_IF_FALSE is not a viable micro-op for tier 2 because it is instrumented */ + case _SPILL_OR_RELOAD_r32: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + _tos_cache1 = _stack_item_2; + _tos_cache0 = _stack_item_1; + SET_CURRENT_CACHED_VALUES(2); + stack_pointer[0] = _stack_item_0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - /* _INSTRUMENTED_POP_JUMP_IF_NONE is not a viable micro-op for tier 2 because it is instrumented */ + case _TIER2_RESUME_CHECK_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t iversion = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + if (eval_breaker != iversion) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - /* _INSTRUMENTED_POP_JUMP_IF_NOT_NONE is not a viable micro-op for tier 2 because it is instrumented */ + case _TIER2_RESUME_CHECK_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t iversion = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + if (eval_breaker != iversion) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } - case _GUARD_IS_TRUE_POP: { - _PyStackRef flag; - flag = stack_pointer[-1]; - int is_true = PyStackRef_IsTrue(flag); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - if (!is_true) { + case _TIER2_RESUME_CHECK_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t iversion = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + if (eval_breaker != iversion) { UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); JUMP_TO_JUMP_TARGET(); } + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_IS_FALSE_POP: { - _PyStackRef flag; - flag = stack_pointer[-1]; - int is_false = PyStackRef_IsFalse(flag); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - if (!is_false) { + case _TIER2_RESUME_CHECK_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t iversion = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + if (eval_breaker != iversion) { UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); JUMP_TO_JUMP_TARGET(); } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_IS_NONE_POP: { - _PyStackRef val; - val = stack_pointer[-1]; - int is_none = PyStackRef_IsNone(val); - if (!is_none) { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(val); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (1) { + case _COLD_EXIT_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyExitData *exit = tstate->jit_exit; + assert(exit != NULL); + assert(frame->owner < FRAME_OWNED_BY_INTERPRETER); + _Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target; + _Py_BackoffCounter temperature = exit->temperature; + _PyExecutorObject *executor; + if (target->op.code == ENTER_EXECUTOR) { + PyCodeObject *code = _PyFrame_GetCode(frame); + executor = code->co_executors->executors[target->op.arg]; + Py_INCREF(executor); + assert(tstate->jit_exit == exit); + exit->executor = executor; + SET_CURRENT_CACHED_VALUES(0); + TIER2_TO_TIER2(exit->executor); + } + else { + if (!backoff_counter_triggers(temperature)) { + exit->temperature = advance_backoff_counter(temperature); + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE(target); + } + _PyExecutorObject *previous_executor = _PyExecutor_FromExit(exit); + assert(tstate->current_executor == (PyObject *)previous_executor); + int chain_depth = previous_executor->vm_data.chain_depth + !exit->is_control_flow; + int succ = _PyJit_TryInitializeTracing(tstate, frame, target, target, target, stack_pointer, chain_depth, exit, target->op.arg, previous_executor); + exit->temperature = restart_backoff_counter(exit->temperature); + if (succ) { + GOTO_TIER_ONE_CONTINUE_TRACING(target); + } + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE(target); + } + } + + case _COLD_DYNAMIC_EXIT_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _Py_CODEUNIT *target = frame->instr_ptr; + SET_CURRENT_CACHED_VALUES(0); + GOTO_TIER_ONE(target); + } + + case _GUARD_CODE_VERSION__PUSH_FRAME_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + if (true) { UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); JUMP_TO_JUMP_TARGET(); } } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _GUARD_IS_NOT_NONE_POP: { - _PyStackRef val; - val = stack_pointer[-1]; - int is_none = PyStackRef_IsNone(val); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(val); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (is_none) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + case _GUARD_CODE_VERSION__PUSH_FRAME_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _JUMP_TO_TOP: { - JUMP_TO_JUMP_TARGET(); + case _GUARD_CODE_VERSION__PUSH_FRAME_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _SET_IP: { - PyObject *instr_ptr = (PyObject *)CURRENT_OPERAND0(); - frame->instr_ptr = (_Py_CODEUNIT *)instr_ptr; + case _GUARD_CODE_VERSION__PUSH_FRAME_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_STACK_SPACE_OPERAND: { - uint32_t framesize = (uint32_t)CURRENT_OPERAND0(); - assert(framesize <= INT_MAX); - if (!_PyThreadState_HasStackSpace(tstate, framesize)) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - if (tstate->py_recursion_remaining <= 1) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + case _GUARD_CODE_VERSION_YIELD_VALUE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _SAVE_RETURN_OFFSET: { - oparg = CURRENT_OPARG(); - #if TIER_ONE - frame->return_offset = (uint16_t)(next_instr - this_instr); - #endif - #if TIER_TWO - frame->return_offset = oparg; - #endif + case _GUARD_CODE_VERSION_YIELD_VALUE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _EXIT_TRACE: { - PyObject *exit_p = (PyObject *)CURRENT_OPERAND0(); - _PyExitData *exit = (_PyExitData *)exit_p; - PyCodeObject *code = _PyFrame_GetCode(frame); - _Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target; - #if defined(Py_DEBUG) && !defined(_Py_JIT) - OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - if (frame->lltrace >= 2) { - _PyFrame_SetStackPointer(frame, stack_pointer); - printf("SIDE EXIT: [UOp "); - _PyUOpPrint(&next_uop[-1]); - printf(", exit %lu, temp %d, target %d -> %s]\n", - exit - current_executor->exits, exit->temperature.value_and_backoff, - (int)(target - _PyFrame_GetBytecode(frame)), - _PyOpcode_OpName[target->op.code]); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _GUARD_CODE_VERSION_YIELD_VALUE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } } - #endif - if (exit->executor && !exit->executor->vm_data.valid) { - exit->temperature = initial_temperature_backoff_counter(); - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_CLEAR(exit->executor); - stack_pointer = _PyFrame_GetStackPointer(frame); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CODE_VERSION_YIELD_VALUE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } } - if (exit->executor == NULL) { - _Py_BackoffCounter temperature = exit->temperature; - if (!backoff_counter_triggers(temperature)) { - exit->temperature = advance_backoff_counter(temperature); - GOTO_TIER_ONE(target); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CODE_VERSION_RETURN_VALUE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); } - _PyExecutorObject *executor; - if (target->op.code == ENTER_EXECUTOR) { - executor = code->co_executors->executors[target->op.arg]; - Py_INCREF(executor); + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CODE_VERSION_RETURN_VALUE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); } - else { - int chain_depth = current_executor->vm_data.chain_depth + 1; - _PyFrame_SetStackPointer(frame, stack_pointer); - int optimized = _PyOptimizer_Optimize(frame, target, &executor, chain_depth); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (optimized <= 0) { - exit->temperature = restart_backoff_counter(temperature); - GOTO_TIER_ONE(optimized < 0 ? NULL : target); - } - exit->temperature = initial_temperature_backoff_counter(); + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + break; + } + + case _GUARD_CODE_VERSION_RETURN_VALUE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); } - exit->executor = executor; } - GOTO_TIER_TWO(exit->executor); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_VALIDITY: { - if (!current_executor->vm_data.valid) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + case _GUARD_CODE_VERSION_RETURN_VALUE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_CONST_INLINE: { - _PyStackRef value; - PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); - value = PyStackRef_FromPyObjectNew(ptr); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_CODE_VERSION_RETURN_GENERATOR_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_TOP_LOAD_CONST_INLINE: { - _PyStackRef pop; - _PyStackRef value; - pop = stack_pointer[-1]; - PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(pop); - stack_pointer = _PyFrame_GetStackPointer(frame); - value = PyStackRef_FromPyObjectNew(ptr); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_CODE_VERSION_RETURN_GENERATOR_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_CONST_INLINE_BORROW: { - _PyStackRef value; - PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); - value = PyStackRef_FromPyObjectBorrow(ptr); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_CODE_VERSION_RETURN_GENERATOR_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_CALL: { - _PyStackRef null; - _PyStackRef callable; - null = stack_pointer[-1]; - callable = stack_pointer[-2]; - (void)null; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _GUARD_CODE_VERSION_RETURN_GENERATOR_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + assert(PyCode_Check(code)); + if (((PyCodeObject *)code)->co_version != version) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_CALL_ONE: { - _PyStackRef pop; - _PyStackRef null; - _PyStackRef callable; - pop = stack_pointer[-1]; - null = stack_pointer[-2]; - callable = stack_pointer[-3]; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(pop); - stack_pointer = _PyFrame_GetStackPointer(frame); - (void)null; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _GUARD_IP__PUSH_FRAME_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr; + if (target != (_Py_CODEUNIT *)ip) { + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_CALL_TWO: { - _PyStackRef pop2; - _PyStackRef pop1; - _PyStackRef null; - _PyStackRef callable; - pop2 = stack_pointer[-1]; - pop1 = stack_pointer[-2]; - null = stack_pointer[-3]; - callable = stack_pointer[-4]; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(pop2); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(pop1); - stack_pointer = _PyFrame_GetStackPointer(frame); - (void)null; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); + case _GUARD_IP__PUSH_FRAME_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr; + if (target != (_Py_CODEUNIT *)ip) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_TOP_LOAD_CONST_INLINE_BORROW: { - _PyStackRef pop; - _PyStackRef value; - pop = stack_pointer[-1]; - PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(pop); - stack_pointer = _PyFrame_GetStackPointer(frame); - value = PyStackRef_FromPyObjectBorrow(ptr); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_IP__PUSH_FRAME_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr; + if (target != (_Py_CODEUNIT *)ip) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_TWO_LOAD_CONST_INLINE_BORROW: { - _PyStackRef pop2; - _PyStackRef pop1; - _PyStackRef value; - pop2 = stack_pointer[-1]; - pop1 = stack_pointer[-2]; - PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(pop2); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(pop1); - stack_pointer = _PyFrame_GetStackPointer(frame); - value = PyStackRef_FromPyObjectBorrow(ptr); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_IP__PUSH_FRAME_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr; + if (target != (_Py_CODEUNIT *)ip) { + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_CALL_LOAD_CONST_INLINE_BORROW: { - _PyStackRef null; - _PyStackRef callable; - _PyStackRef value; - null = stack_pointer[-1]; - callable = stack_pointer[-2]; - PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); - (void)null; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); - value = PyStackRef_FromPyObjectBorrow(ptr); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_IP_YIELD_VALUE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + 1 + INLINE_CACHE_ENTRIES_SEND; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW: { - _PyStackRef pop; - _PyStackRef null; - _PyStackRef callable; - _PyStackRef value; - pop = stack_pointer[-1]; - null = stack_pointer[-2]; - callable = stack_pointer[-3]; - PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(pop); - stack_pointer = _PyFrame_GetStackPointer(frame); - (void)null; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); - value = PyStackRef_FromPyObjectBorrow(ptr); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_IP_YIELD_VALUE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + 1 + INLINE_CACHE_ENTRIES_SEND; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW: { - _PyStackRef pop2; - _PyStackRef pop1; - _PyStackRef null; - _PyStackRef callable; - _PyStackRef value; - pop2 = stack_pointer[-1]; - pop1 = stack_pointer[-2]; - null = stack_pointer[-3]; - callable = stack_pointer[-4]; - PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(pop2); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(pop1); - stack_pointer = _PyFrame_GetStackPointer(frame); - (void)null; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); - value = PyStackRef_FromPyObjectBorrow(ptr); - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_IP_YIELD_VALUE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + 1 + INLINE_CACHE_ENTRIES_SEND; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_CONST_UNDER_INLINE: { - _PyStackRef old; - _PyStackRef value; - _PyStackRef new; - old = stack_pointer[-1]; - PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); - new = old; - value = PyStackRef_FromPyObjectNew(ptr); - stack_pointer[-1] = value; - stack_pointer[0] = new; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_IP_YIELD_VALUE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + 1 + INLINE_CACHE_ENTRIES_SEND; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _LOAD_CONST_UNDER_INLINE_BORROW: { - _PyStackRef old; - _PyStackRef value; - _PyStackRef new; - old = stack_pointer[-1]; - PyObject *ptr = (PyObject *)CURRENT_OPERAND0(); - new = old; - value = PyStackRef_FromPyObjectBorrow(ptr); - stack_pointer[-1] = value; - stack_pointer[0] = new; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_IP_RETURN_VALUE_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + frame->return_offset; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _CHECK_FUNCTION: { - uint32_t func_version = (uint32_t)CURRENT_OPERAND0(); - assert(PyStackRef_FunctionCheck(frame->f_funcobj)); - PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); - if (func->func_version != func_version) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + case _GUARD_IP_RETURN_VALUE_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + frame->return_offset; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _START_EXECUTOR: { - PyObject *executor = (PyObject *)CURRENT_OPERAND0(); - #ifndef _Py_JIT - current_executor = (_PyExecutorObject*)executor; - #endif - assert(((_PyExecutorObject *)executor)->vm_data.valid); + case _GUARD_IP_RETURN_VALUE_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + frame->return_offset; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _MAKE_WARM: { - current_executor->vm_data.warm = true; - if (--tstate->interp->trace_run_counter == 0) { - _Py_set_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT); + case _GUARD_IP_RETURN_VALUE_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + frame->return_offset; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } } + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _FATAL_ERROR: { - assert(0); - Py_FatalError("Fatal error uop executed."); + case _GUARD_IP_RETURN_GENERATOR_r00: { + CHECK_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + frame->return_offset; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_JUMP_TARGET(); + } + } + SET_CURRENT_CACHED_VALUES(0); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _DEOPT: { - GOTO_TIER_ONE(_PyFrame_GetBytecode(frame) + CURRENT_TARGET()); + case _GUARD_IP_RETURN_GENERATOR_r11: { + CHECK_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + frame->return_offset; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(1); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _ERROR_POP_N: { - oparg = CURRENT_OPARG(); - uint32_t target = (uint32_t)CURRENT_OPERAND0(); - assert(oparg == 0); - frame->instr_ptr = _PyFrame_GetBytecode(frame) + target; - GOTO_TIER_ONE(NULL); + case _GUARD_IP_RETURN_GENERATOR_r22: { + CHECK_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + frame->return_offset; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + } + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(2); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } - case _TIER2_RESUME_CHECK: { - #if defined(__EMSCRIPTEN__) - if (_Py_emscripten_signal_clock == 0) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); - } - _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; - #endif - uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); - if (eval_breaker & _PY_EVAL_EVENTS_MASK) { - UOP_STAT_INC(uopcode, miss); - JUMP_TO_JUMP_TARGET(); + case _GUARD_IP_RETURN_GENERATOR_r33: { + CHECK_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef _stack_item_0 = _tos_cache0; + _PyStackRef _stack_item_1 = _tos_cache1; + _PyStackRef _stack_item_2 = _tos_cache2; + PyObject *ip = (PyObject *)CURRENT_OPERAND0_64(); + _Py_CODEUNIT *target = frame->instr_ptr + frame->return_offset; + if (target != (_Py_CODEUNIT *)ip) { + frame->instr_ptr += frame->return_offset; + if (true) { + UOP_STAT_INC(uopcode, miss); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + JUMP_TO_JUMP_TARGET(); + } } - assert(tstate->tracing || eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version)); + _tos_cache2 = _stack_item_2; + _tos_cache1 = _stack_item_1; + _tos_cache0 = _stack_item_0; + SET_CURRENT_CACHED_VALUES(3); + ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); break; } + /* _TRACE_RECORD is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ + + #undef TIER_TWO diff --git a/Python/fileutils.c b/Python/fileutils.c index 2a3f12d4e872f8..0c1766b8804500 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2,6 +2,7 @@ #include "pycore_fileutils.h" // fileutils definitions #include "pycore_runtime.h" // _PyRuntime #include "pycore_pystate.h" // _Py_AssertHoldsTstate() +#include "pycore_unicodeobject.h" // _Py_MAX_UNICODE #include "osdefs.h" // SEP #include <stdlib.h> // mbstowcs() @@ -50,9 +51,6 @@ extern int winerror_to_errno(int); int _Py_open_cloexec_works = -1; #endif -// The value must be the same in unicodeobject.c. -#define MAX_UNICODE 0x10ffff - // mbstowcs() and mbrtowc() errors static const size_t DECODE_ERROR = ((size_t)-1); #ifdef HAVE_MBRTOWC @@ -123,7 +121,7 @@ is_valid_wide_char(wchar_t ch) { #ifdef HAVE_NON_UNICODE_WCHAR_T_REPRESENTATION /* Oracle Solaris doesn't use Unicode code points as wchar_t encoding - for non-Unicode locales, which makes values higher than MAX_UNICODE + for non-Unicode locales, which makes values higher than _Py_MAX_UNICODE possibly valid. */ return 1; #endif @@ -132,7 +130,7 @@ is_valid_wide_char(wchar_t ch) return 0; } #if SIZEOF_WCHAR_T > 2 - if (ch > MAX_UNICODE) { + if (ch > _Py_MAX_UNICODE) { // bpo-35883: Reject characters outside [U+0000; U+10ffff] range. // The glibc mbstowcs() UTF-8 decoder does not respect the RFC 3629, // it creates characters outside the [U+0000; U+10ffff] range: @@ -180,7 +178,7 @@ _Py_mbrtowc(wchar_t *pwc, const char *str, size_t len, mbstate_t *pmbs) #define USE_FORCE_ASCII -extern int _Py_normalize_encoding(const char *, char *, size_t); +extern int _Py_normalize_encoding(const char *, char *, size_t, int); /* Workaround FreeBSD and OpenIndiana locale encoding issue with the C locale and POSIX locale. nl_langinfo(CODESET) announces an alias of the @@ -231,7 +229,7 @@ check_force_ascii(void) } char encoding[20]; /* longest name: "iso_646.irv_1991\0" */ - if (!_Py_normalize_encoding(codeset, encoding, sizeof(encoding))) { + if (!_Py_normalize_encoding(codeset, encoding, sizeof(encoding), 1)) { goto error; } @@ -2120,7 +2118,6 @@ _Py_wrealpath(const wchar_t *path, wchar_t *resolved_path, size_t resolved_path_len) { char *cpath; - char cresolved_path[MAXPATHLEN]; wchar_t *wresolved_path; char *res; size_t r; @@ -2129,12 +2126,14 @@ _Py_wrealpath(const wchar_t *path, errno = EINVAL; return NULL; } - res = realpath(cpath, cresolved_path); + res = realpath(cpath, NULL); PyMem_RawFree(cpath); if (res == NULL) return NULL; - wresolved_path = Py_DecodeLocale(cresolved_path, &r); + wresolved_path = Py_DecodeLocale(res, &r); + free(res); + if (wresolved_path == NULL) { errno = EINVAL; return NULL; diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 1cb6f03169e3b5..f135f243c74e2f 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -6,9 +6,12 @@ #include "pycore_intrinsics.h" #include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_long.h" // _PY_IS_SMALL_INT() +#include "pycore_hashtable.h" // _Py_hashtable_t #include "pycore_opcode_utils.h" #include "pycore_opcode_metadata.h" // OPCODE_HAS_ARG, etc +#include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_stackref.h" // PyStackRef_AsPyObjectBorrow() #include <stdbool.h> @@ -199,8 +202,10 @@ basicblock_addop(basicblock *b, int opcode, int oparg, location loc) cfg_instr *i = &b->b_instr[off]; i->i_opcode = opcode; i->i_oparg = oparg; - i->i_target = NULL; i->i_loc = loc; + // memory is already zero initialized + assert(i->i_target == NULL); + assert(i->i_except == NULL); return SUCCESS; } @@ -399,7 +404,6 @@ cfg_builder_maybe_start_new_block(cfg_builder *g) static bool cfg_builder_check(cfg_builder *g) { - assert(g->g_entryblock->b_iused > 0); for (basicblock *block = g->g_block_list; block != NULL; block = block->b_list) { assert(!_PyMem_IsPtrFreed(block)); if (block->b_instr != NULL) { @@ -848,7 +852,7 @@ calculate_stackdepth(cfg_builder *g) int new_depth = depth + effects.net; if (new_depth < 0) { PyErr_Format(PyExc_ValueError, - "Invalid CFG, stack underflow"); + "Invalid CFG, stack underflow at line %d", instr->i_loc.lineno); goto error; } maxdepth = Py_MAX(maxdepth, depth); @@ -961,7 +965,7 @@ label_exception_targets(basicblock *entryblock) { } else if (instr->i_opcode == RESUME) { instr->i_except = handler; - if (instr->i_oparg != RESUME_AT_FUNC_START) { + if (instr->i_oparg != RESUME_AT_FUNC_START && instr->i_oparg != RESUME_AT_GEN_EXPR_START) { assert(last_yield_except_depth >= 0); if (last_yield_except_depth == 1) { instr->i_oparg |= RESUME_OPARG_DEPTH1_MASK; @@ -969,6 +973,9 @@ label_exception_targets(basicblock *entryblock) { last_yield_except_depth = -1; } } + else if (instr->i_opcode == RETURN_GENERATOR) { + instr->i_except = NULL; + } else { instr->i_except = handler; } @@ -1104,6 +1111,7 @@ basicblock_remove_redundant_nops(basicblock *bb) { assert(dest <= bb->b_iused); int num_removed = bb->b_iused - dest; bb->b_iused = dest; + memset(&bb->b_instr[dest], 0, sizeof(cfg_instr) * num_removed); return num_removed; } @@ -1118,6 +1126,8 @@ remove_redundant_nops(cfg_builder *g) { return changes; } +static int loads_const(int opcode); + static int remove_redundant_nops_and_pairs(basicblock *entryblock) { @@ -1141,7 +1151,7 @@ remove_redundant_nops_and_pairs(basicblock *entryblock) int opcode = instr->i_opcode; bool is_redundant_pair = false; if (opcode == POP_TOP) { - if (prev_opcode == LOAD_CONST || prev_opcode == LOAD_SMALL_INT) { + if (loads_const(prev_opcode)) { is_redundant_pair = true; } else if (prev_opcode == COPY && prev_oparg == 1) { @@ -1293,7 +1303,9 @@ jump_thread(basicblock *bb, cfg_instr *inst, cfg_instr *target, int opcode) static int loads_const(int opcode) { - return OPCODE_HAS_CONST(opcode) || opcode == LOAD_SMALL_INT; + return OPCODE_HAS_CONST(opcode) + || opcode == LOAD_SMALL_INT + || opcode == LOAD_COMMON_CONSTANT; } /* Returns new reference */ @@ -1303,11 +1315,24 @@ get_const_value(int opcode, int oparg, PyObject *co_consts) PyObject *constant = NULL; assert(loads_const(opcode)); if (opcode == LOAD_CONST) { + assert(PyList_Check(co_consts)); + Py_ssize_t n = PyList_GET_SIZE(co_consts); + if (oparg < 0 || oparg >= n) { + PyErr_Format(PyExc_ValueError, + "LOAD_CONST index %d is out of range for consts (len=%zd)", + oparg, n); + return NULL; + } constant = PyList_GET_ITEM(co_consts, oparg); } if (opcode == LOAD_SMALL_INT) { return PyLong_FromLong(oparg); } + if (opcode == LOAD_COMMON_CONSTANT) { + assert(oparg < NUM_COMMON_CONSTANTS); + return PyStackRef_AsPyObjectBorrow( + _PyInterpreterState_GET()->common_consts[oparg]); + } if (constant == NULL) { PyErr_SetString(PyExc_SystemError, @@ -1319,30 +1344,38 @@ get_const_value(int opcode, int oparg, PyObject *co_consts) // Steals a reference to newconst. static int -add_const(PyObject *newconst, PyObject *consts, PyObject *const_cache) +add_const(PyObject *newconst, PyObject *consts, PyObject *const_cache, + _Py_hashtable_t *consts_index) { if (_PyCompile_ConstCacheMergeOne(const_cache, &newconst) < 0) { Py_DECREF(newconst); return -1; } - Py_ssize_t index; - for (index = 0; index < PyList_GET_SIZE(consts); index++) { - if (PyList_GET_ITEM(consts, index) == newconst) { - break; - } + _Py_hashtable_entry_t *entry = _Py_hashtable_get_entry(consts_index, (void *)newconst); + if (entry != NULL) { + Py_DECREF(newconst); + return (int)(uintptr_t)entry->value; } - if (index == PyList_GET_SIZE(consts)) { - if ((size_t)index >= (size_t)INT_MAX - 1) { - PyErr_SetString(PyExc_OverflowError, "too many constants"); - Py_DECREF(newconst); - return -1; - } - if (PyList_Append(consts, newconst)) { - Py_DECREF(newconst); - return -1; - } + + Py_ssize_t index = PyList_GET_SIZE(consts); + if ((size_t)index >= (size_t)INT_MAX - 1) { + PyErr_SetString(PyExc_OverflowError, "too many constants"); + Py_DECREF(newconst); + return -1; + } + if (PyList_Append(consts, newconst)) { + Py_DECREF(newconst); + return -1; + } + + if (_Py_hashtable_set(consts_index, (void *)newconst, (void *)(uintptr_t)index) < 0) { + PyList_SetSlice(consts, index, index + 1, NULL); + Py_DECREF(newconst); + PyErr_NoMemory(); + return -1; } + Py_DECREF(newconst); return (int)index; } @@ -1405,7 +1438,7 @@ maybe_instr_make_load_smallint(cfg_instr *instr, PyObject *newconst, if (val == -1 && PyErr_Occurred()) { return -1; } - if (!overflow && _PY_IS_SMALL_INT(val)) { + if (!overflow && _PY_IS_SMALL_INT(val) && 0 <= val && val <= 255) { assert(_Py_IsImmortal(newconst)); INSTR_SET_OP1(instr, LOAD_SMALL_INT, (int)val); return 1; @@ -1414,11 +1447,56 @@ maybe_instr_make_load_smallint(cfg_instr *instr, PyObject *newconst, return 0; } +/* Does not steal reference to "newconst". + Return 1 if changed instruction to LOAD_COMMON_CONSTANT. + Return 0 if could not change instruction to LOAD_COMMON_CONSTANT. + Return -1 on error. +*/ +static int +maybe_instr_make_load_common_const(cfg_instr *instr, PyObject *newconst) +{ + int oparg; + if (newconst == Py_None) { + oparg = CONSTANT_NONE; + } + else if (newconst == Py_True) { + oparg = CONSTANT_TRUE; + } + else if (newconst == Py_False) { + oparg = CONSTANT_FALSE; + } + else if (PyUnicode_CheckExact(newconst) + && PyUnicode_GET_LENGTH(newconst) == 0) { + oparg = CONSTANT_EMPTY_STR; + } + else if (PyTuple_CheckExact(newconst) + && PyTuple_GET_SIZE(newconst) == 0) { + oparg = CONSTANT_EMPTY_TUPLE; + } + else if (PyLong_CheckExact(newconst)) { + int overflow; + long val = PyLong_AsLongAndOverflow(newconst, &overflow); + if (val == -1 && PyErr_Occurred()) { + return -1; + } + if (overflow || val != -1) { + return 0; + } + oparg = CONSTANT_MINUS_ONE; + } + else { + return 0; + } + assert(_Py_IsImmortal(newconst)); + INSTR_SET_OP1(instr, LOAD_COMMON_CONSTANT, oparg); + return 1; +} /* Steals reference to "newconst" */ static int instr_make_load_const(cfg_instr *instr, PyObject *newconst, - PyObject *consts, PyObject *const_cache) + PyObject *consts, PyObject *const_cache, + _Py_hashtable_t *consts_index) { int res = maybe_instr_make_load_smallint(instr, newconst, consts, const_cache); if (res < 0) { @@ -1428,7 +1506,15 @@ instr_make_load_const(cfg_instr *instr, PyObject *newconst, if (res > 0) { return SUCCESS; } - int oparg = add_const(newconst, consts, const_cache); + res = maybe_instr_make_load_common_const(instr, newconst); + if (res < 0) { + Py_DECREF(newconst); + return ERROR; + } + if (res > 0) { + return SUCCESS; + } + int oparg = add_const(newconst, consts, const_cache, consts_index); RETURN_IF_ERROR(oparg); INSTR_SET_OP1(instr, LOAD_CONST, oparg); return SUCCESS; @@ -1441,7 +1527,8 @@ instr_make_load_const(cfg_instr *instr, PyObject *newconst, Called with codestr pointing to the first LOAD_CONST. */ static int -fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, PyObject *const_cache) +fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, + PyObject *const_cache, _Py_hashtable_t *consts_index) { /* Pre-conditions */ assert(PyDict_CheckExact(const_cache)); @@ -1478,7 +1565,7 @@ fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, PyObject *const } nop_out(const_instrs, seq_size); - return instr_make_load_const(instr, const_tuple, consts, const_cache); + return instr_make_load_const(instr, const_tuple, consts, const_cache, consts_index); } /* Replace: @@ -1496,7 +1583,8 @@ fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, PyObject *const */ static int fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, - PyObject *consts, PyObject *const_cache) + PyObject *consts, PyObject *const_cache, + _Py_hashtable_t *consts_index) { assert(PyDict_CheckExact(const_cache)); assert(PyList_CheckExact(consts)); @@ -1548,7 +1636,7 @@ fold_constant_intrinsic_list_to_tuple(basicblock *bb, int i, nop_out(&instr, 1); } assert(consts_found == 0); - return instr_make_load_const(intrinsic, newconst, consts, const_cache); + return instr_make_load_const(intrinsic, newconst, consts, const_cache, consts_index); } if (expect_append) { @@ -1584,7 +1672,8 @@ Optimize lists and sets for: */ static int optimize_lists_and_sets(basicblock *bb, int i, int nextop, - PyObject *consts, PyObject *const_cache) + PyObject *consts, PyObject *const_cache, + _Py_hashtable_t *consts_index) { assert(PyDict_CheckExact(const_cache)); assert(PyList_CheckExact(consts)); @@ -1634,7 +1723,7 @@ optimize_lists_and_sets(basicblock *bb, int i, int nextop, Py_SETREF(const_result, frozenset); } - int index = add_const(const_result, consts, const_cache); + int index = add_const(const_result, consts, const_cache, consts_index); RETURN_IF_ERROR(index); nop_out(const_instrs, seq_size); @@ -1831,7 +1920,8 @@ eval_const_binop(PyObject *left, int op, PyObject *right) } static int -fold_const_binop(basicblock *bb, int i, PyObject *consts, PyObject *const_cache) +fold_const_binop(basicblock *bb, int i, PyObject *consts, + PyObject *const_cache, _Py_hashtable_t *consts_index) { #define BINOP_OPERAND_COUNT 2 assert(PyDict_CheckExact(const_cache)); @@ -1873,7 +1963,7 @@ fold_const_binop(basicblock *bb, int i, PyObject *consts, PyObject *const_cache) } nop_out(operands_instrs, BINOP_OPERAND_COUNT); - return instr_make_load_const(binop, newconst, consts, const_cache); + return instr_make_load_const(binop, newconst, consts, const_cache, consts_index); } static PyObject * @@ -1919,7 +2009,8 @@ eval_const_unaryop(PyObject *operand, int opcode, int oparg) } static int -fold_const_unaryop(basicblock *bb, int i, PyObject *consts, PyObject *const_cache) +fold_const_unaryop(basicblock *bb, int i, PyObject *consts, + PyObject *const_cache, _Py_hashtable_t *consts_index) { #define UNARYOP_OPERAND_COUNT 1 assert(PyDict_CheckExact(const_cache)); @@ -1956,7 +2047,7 @@ fold_const_unaryop(basicblock *bb, int i, PyObject *consts, PyObject *const_cach assert(PyBool_Check(newconst)); } nop_out(&operand_instr, UNARYOP_OPERAND_COUNT); - return instr_make_load_const(unaryop, newconst, consts, const_cache); + return instr_make_load_const(unaryop, newconst, consts, const_cache, consts_index); } #define VISITED (-1) @@ -2151,7 +2242,8 @@ apply_static_swaps(basicblock *block, int i) } static int -basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject *consts) +basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, + PyObject *consts, _Py_hashtable_t *consts_index) { assert(PyDict_CheckExact(const_cache)); assert(PyList_CheckExact(consts)); @@ -2161,6 +2253,9 @@ basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject * cfg_instr *inst = &bb->b_instr[i]; if (inst->i_opcode == LOAD_CONST) { PyObject *constant = get_const_value(inst->i_opcode, inst->i_oparg, consts); + if (constant == NULL) { + return ERROR; + } int res = maybe_instr_make_load_smallint(inst, constant, consts, const_cache); Py_DECREF(constant); if (res < 0) { @@ -2175,7 +2270,7 @@ basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject * oparg = inst->i_oparg; } assert(!IS_ASSEMBLER_OPCODE(opcode)); - if (opcode != LOAD_CONST && opcode != LOAD_SMALL_INT) { + if (!loads_const(opcode)) { continue; } int nextop = i+1 < bb->b_iused ? bb->b_instr[i+1].i_opcode : 0; @@ -2266,7 +2361,7 @@ basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject * return ERROR; } cnt = PyBool_FromLong(is_true); - int index = add_const(cnt, consts, const_cache); + int index = add_const(cnt, consts, const_cache, consts_index); if (index < 0) { return ERROR; } @@ -2275,20 +2370,33 @@ basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject * break; } } + if (inst->i_opcode == LOAD_CONST) { + PyObject *constant = get_const_value(inst->i_opcode, inst->i_oparg, consts); + if (constant == NULL) { + return ERROR; + } + int res = maybe_instr_make_load_common_const(inst, constant); + Py_DECREF(constant); + if (res < 0) { + return ERROR; + } + } } return SUCCESS; } static int -optimize_load_const(PyObject *const_cache, cfg_builder *g, PyObject *consts) { +optimize_load_const(PyObject *const_cache, cfg_builder *g, PyObject *consts, + _Py_hashtable_t *consts_index) { for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - RETURN_IF_ERROR(basicblock_optimize_load_const(const_cache, b, consts)); + RETURN_IF_ERROR(basicblock_optimize_load_const(const_cache, b, consts, consts_index)); } return SUCCESS; } static int -optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) +optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts, + _Py_hashtable_t *consts_index) { assert(PyDict_CheckExact(const_cache)); assert(PyList_CheckExact(consts)); @@ -2328,11 +2436,11 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) continue; } } - RETURN_IF_ERROR(fold_tuple_of_constants(bb, i, consts, const_cache)); + RETURN_IF_ERROR(fold_tuple_of_constants(bb, i, consts, const_cache, consts_index)); break; case BUILD_LIST: case BUILD_SET: - RETURN_IF_ERROR(optimize_lists_and_sets(bb, i, nextop, consts, const_cache)); + RETURN_IF_ERROR(optimize_lists_and_sets(bb, i, nextop, consts, const_cache, consts_index)); break; case POP_JUMP_IF_NOT_NONE: case POP_JUMP_IF_NONE: @@ -2467,7 +2575,7 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) _Py_FALLTHROUGH; case UNARY_INVERT: case UNARY_NEGATIVE: - RETURN_IF_ERROR(fold_const_unaryop(bb, i, consts, const_cache)); + RETURN_IF_ERROR(fold_const_unaryop(bb, i, consts, const_cache, consts_index)); break; case CALL_INTRINSIC_1: if (oparg == INTRINSIC_LIST_TO_TUPLE) { @@ -2475,15 +2583,15 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) INSTR_SET_OP0(inst, NOP); } else { - RETURN_IF_ERROR(fold_constant_intrinsic_list_to_tuple(bb, i, consts, const_cache)); + RETURN_IF_ERROR(fold_constant_intrinsic_list_to_tuple(bb, i, consts, const_cache, consts_index)); } } else if (oparg == INTRINSIC_UNARY_POSITIVE) { - RETURN_IF_ERROR(fold_const_unaryop(bb, i, consts, const_cache)); + RETURN_IF_ERROR(fold_const_unaryop(bb, i, consts, const_cache, consts_index)); } break; case BINARY_OP: - RETURN_IF_ERROR(fold_const_binop(bb, i, consts, const_cache)); + RETURN_IF_ERROR(fold_const_binop(bb, i, consts, const_cache, consts_index)); break; } } @@ -2528,16 +2636,17 @@ remove_redundant_nops_and_jumps(cfg_builder *g) NOPs. Later those NOPs are removed. */ static int -optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache, int firstlineno) +optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache, + _Py_hashtable_t *consts_index, int firstlineno) { assert(PyDict_CheckExact(const_cache)); RETURN_IF_ERROR(check_cfg(g)); RETURN_IF_ERROR(inline_small_or_no_lineno_blocks(g->g_entryblock)); RETURN_IF_ERROR(remove_unreachable(g->g_entryblock)); RETURN_IF_ERROR(resolve_line_numbers(g, firstlineno)); - RETURN_IF_ERROR(optimize_load_const(const_cache, g, consts)); + RETURN_IF_ERROR(optimize_load_const(const_cache, g, consts, consts_index)); for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { - RETURN_IF_ERROR(optimize_basic_block(const_cache, b, consts)); + RETURN_IF_ERROR(optimize_basic_block(const_cache, b, consts, consts_index)); } RETURN_IF_ERROR(remove_redundant_nops_and_pairs(g->g_entryblock)); RETURN_IF_ERROR(remove_unreachable(g->g_entryblock)); @@ -2878,7 +2987,6 @@ optimize_load_fast(cfg_builder *g) case GET_ANEXT: case GET_ITER: case GET_LEN: - case GET_YIELD_FROM_ITER: case IMPORT_FROM: case MATCH_KEYS: case MATCH_MAPPING: @@ -2888,7 +2996,7 @@ optimize_load_fast(cfg_builder *g) int num_pushed = _PyOpcode_num_pushed(opcode, oparg); int net_pushed = num_pushed - num_popped; assert(net_pushed >= 0); - for (int i = 0; i < net_pushed; i++) { + for (int j = 0; j < net_pushed; j++) { PUSH_REF(i, NOT_LOCAL); } break; @@ -2913,7 +3021,16 @@ optimize_load_fast(cfg_builder *g) break; } - case END_SEND: + case END_SEND: { + assert(_PyOpcode_num_popped(opcode, oparg) == 3); + assert(_PyOpcode_num_pushed(opcode, oparg) == 1); + ref tos = ref_stack_pop(&refs); + ref_stack_pop(&refs); + ref_stack_pop(&refs); + PUSH_REF(tos.instr, tos.local); + break; + } + case SET_FUNCTION_ATTRIBUTE: { assert(_PyOpcode_num_popped(opcode, oparg) == 2); assert(_PyOpcode_num_pushed(opcode, oparg) == 1); @@ -2990,11 +3107,8 @@ optimize_load_fast(cfg_builder *g) } // Push fallthrough block - cfg_instr *term = basicblock_last_instr(block); - if (term != NULL && block->b_next != NULL && - !(IS_UNCONDITIONAL_JUMP_OPCODE(term->i_opcode) || - IS_SCOPE_EXIT_OPCODE(term->i_opcode))) { - assert(BB_HAS_FALLTHROUGH(block)); + if (BB_HAS_FALLTHROUGH(block)) { + assert(block->b_next != NULL); load_fast_push_block(&sp, block->b_next, refs.size); } @@ -3164,6 +3278,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) index_map = PyMem_Malloc(nconsts * sizeof(Py_ssize_t)); if (index_map == NULL) { + PyErr_NoMemory(); goto end; } for (Py_ssize_t i = 1; i < nconsts; i++) { @@ -3216,6 +3331,7 @@ remove_unused_consts(basicblock *entryblock, PyObject *consts) /* adjust const indices in the bytecode */ reverse_index_map = PyMem_Malloc(nconsts * sizeof(Py_ssize_t)); if (reverse_index_map == NULL) { + PyErr_NoMemory(); goto end; } for (Py_ssize_t i = 0; i < nconsts; i++) { @@ -3472,11 +3588,13 @@ convert_pseudo_conditional_jumps(cfg_builder *g) instr->i_opcode = instr->i_opcode == JUMP_IF_FALSE ? POP_JUMP_IF_FALSE : POP_JUMP_IF_TRUE; location loc = instr->i_loc; + basicblock *except = instr->i_except; cfg_instr copy = { .i_opcode = COPY, .i_oparg = 1, .i_loc = loc, .i_target = NULL, + .i_except = except, }; RETURN_IF_ERROR(basicblock_insert_instruction(b, i++, &copy)); cfg_instr to_bool = { @@ -3484,6 +3602,7 @@ convert_pseudo_conditional_jumps(cfg_builder *g) .i_oparg = 0, .i_loc = loc, .i_target = NULL, + .i_except = except, }; RETURN_IF_ERROR(basicblock_insert_instruction(b, i++, &to_bool)); } @@ -3588,8 +3707,19 @@ duplicate_exits_without_lineno(cfg_builder *g) * Also reduces the size of the line number table, * but has no impact on the generated line number events. */ + +static inline void +maybe_propagate_location(basicblock *b, int i, location loc) +{ + assert(b->b_iused > i); + if (b->b_instr[i].i_loc.lineno == NO_LOCATION.lineno) { + b->b_instr[i].i_loc = loc; + } +} + static void -propagate_line_numbers(basicblock *entryblock) { +propagate_line_numbers(basicblock *entryblock) +{ for (basicblock *b = entryblock; b != NULL; b = b->b_next) { cfg_instr *last = basicblock_last_instr(b); if (last == NULL) { @@ -3598,26 +3728,21 @@ propagate_line_numbers(basicblock *entryblock) { location prev_location = NO_LOCATION; for (int i = 0; i < b->b_iused; i++) { - if (b->b_instr[i].i_loc.lineno == NO_LOCATION.lineno) { - b->b_instr[i].i_loc = prev_location; - } - else { - prev_location = b->b_instr[i].i_loc; - } + maybe_propagate_location(b, i, prev_location); + prev_location = b->b_instr[i].i_loc; } if (BB_HAS_FALLTHROUGH(b) && b->b_next->b_predecessors == 1) { if (b->b_next->b_iused > 0) { - if (b->b_next->b_instr[0].i_loc.lineno == NO_LOCATION.lineno) { - b->b_next->b_instr[0].i_loc = prev_location; - } + maybe_propagate_location(b->b_next, 0, prev_location); } } if (is_jump(last)) { basicblock *target = last->i_target; + while (target->b_iused == 0 && target->b_predecessors == 1) { + target = target->b_next; + } if (target->b_predecessors == 1) { - if (target->b_instr[0].i_loc.lineno == NO_LOCATION.lineno) { - target->b_instr[0].i_loc = prev_location; - } + maybe_propagate_location(target, 0, prev_location); } } } @@ -3636,6 +3761,7 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache, int nlocals, int nparams, int firstlineno) { assert(cfg_builder_check(g)); + assert(g->g_entryblock->b_iused > 0); /** Preprocessing **/ /* Map labels to targets and mark exception handlers */ RETURN_IF_ERROR(translate_jump_labels_to_targets(g->g_entryblock)); @@ -3643,7 +3769,33 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache, RETURN_IF_ERROR(label_exception_targets(g->g_entryblock)); /** Optimization **/ - RETURN_IF_ERROR(optimize_cfg(g, consts, const_cache, firstlineno)); + + _Py_hashtable_t *consts_index = _Py_hashtable_new( + _Py_hashtable_hash_ptr, _Py_hashtable_compare_direct); + if (consts_index == NULL) { + PyErr_NoMemory(); + return ERROR; + } + + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(consts); i++) { + PyObject *item = PyList_GET_ITEM(consts, i); + if (_Py_hashtable_get_entry(consts_index, (void *)item) != NULL) { + continue; + } + if (_Py_hashtable_set(consts_index, (void *)item, + (void *)(uintptr_t)i) < 0) { + _Py_hashtable_destroy(consts_index); + PyErr_NoMemory(); + return ERROR; + } + } + + int ret = optimize_cfg(g, consts, const_cache, consts_index, firstlineno); + + _Py_hashtable_destroy(consts_index); + + RETURN_IF_ERROR(ret); + RETURN_IF_ERROR(remove_unused_consts(g->g_entryblock, consts)); RETURN_IF_ERROR( add_checks_for_loads_of_uninitialized_variables( @@ -3709,34 +3861,10 @@ build_cellfixedoffsets(_PyCompile_CodeUnitMetadata *umd) static int insert_prefix_instructions(_PyCompile_CodeUnitMetadata *umd, basicblock *entryblock, - int *fixed, int nfreevars, int code_flags) + int *fixed, int nfreevars) { assert(umd->u_firstlineno > 0); - /* Add the generator prefix instructions. */ - if (IS_GENERATOR(code_flags)) { - /* Note that RETURN_GENERATOR + POP_TOP have a net stack effect - * of 0. This is because RETURN_GENERATOR pushes an element - * with _PyFrame_StackPush before switching stacks. - */ - - location loc = LOCATION(umd->u_firstlineno, umd->u_firstlineno, -1, -1); - cfg_instr make_gen = { - .i_opcode = RETURN_GENERATOR, - .i_oparg = 0, - .i_loc = loc, - .i_target = NULL, - }; - RETURN_IF_ERROR(basicblock_insert_instruction(entryblock, 0, &make_gen)); - cfg_instr pop_top = { - .i_opcode = POP_TOP, - .i_oparg = 0, - .i_loc = loc, - .i_target = NULL, - }; - RETURN_IF_ERROR(basicblock_insert_instruction(entryblock, 1, &pop_top)); - } - /* Set up cells for any variable that escapes, to be put in a closure. */ const int ncellvars = (int)PyDict_GET_SIZE(umd->u_cellvars); if (ncellvars) { @@ -3763,6 +3891,7 @@ insert_prefix_instructions(_PyCompile_CodeUnitMetadata *umd, basicblock *entrybl .i_oparg = oldindex, .i_loc = NO_LOCATION, .i_target = NULL, + .i_except = NULL, }; if (basicblock_insert_instruction(entryblock, ncellsused, &make_cell) < 0) { PyMem_RawFree(sorted); @@ -3779,6 +3908,7 @@ insert_prefix_instructions(_PyCompile_CodeUnitMetadata *umd, basicblock *entrybl .i_oparg = nfreevars, .i_loc = NO_LOCATION, .i_target = NULL, + .i_except = NULL, }; RETURN_IF_ERROR(basicblock_insert_instruction(entryblock, 0, &copy_frees)); } @@ -3832,7 +3962,7 @@ fix_cell_offsets(_PyCompile_CodeUnitMetadata *umd, basicblock *entryblock, int * } static int -prepare_localsplus(_PyCompile_CodeUnitMetadata *umd, cfg_builder *g, int code_flags) +prepare_localsplus(_PyCompile_CodeUnitMetadata *umd, cfg_builder *g) { assert(PyDict_GET_SIZE(umd->u_varnames) < INT_MAX); assert(PyDict_GET_SIZE(umd->u_cellvars) < INT_MAX); @@ -3849,7 +3979,7 @@ prepare_localsplus(_PyCompile_CodeUnitMetadata *umd, cfg_builder *g, int code_fl } // This must be called before fix_cell_offsets(). - if (insert_prefix_instructions(umd, g->g_entryblock, cellfixedoffsets, nfreevars, code_flags)) { + if (insert_prefix_instructions(umd, g->g_entryblock, cellfixedoffsets, nfreevars)) { PyMem_Free(cellfixedoffsets); return ERROR; } @@ -3970,7 +4100,7 @@ _PyCfg_ToInstructionSequence(cfg_builder *g, _PyInstructionSequence *seq) int _PyCfg_OptimizedCfgToInstructionSequence(cfg_builder *g, - _PyCompile_CodeUnitMetadata *umd, int code_flags, + _PyCompile_CodeUnitMetadata *umd, int *stackdepth, int *nlocalsplus, _PyInstructionSequence *seq) { @@ -3981,16 +4111,7 @@ _PyCfg_OptimizedCfgToInstructionSequence(cfg_builder *g, return ERROR; } - /* prepare_localsplus adds instructions for generators that push - * and pop an item on the stack. This assertion makes sure there - * is space on the stack for that. - * It should always be true, because a generator must have at - * least one expression or call to INTRINSIC_STOPITERATION_ERROR, - * which requires stackspace. - */ - assert(!(IS_GENERATOR(code_flags) && *stackdepth == 0)); - - *nlocalsplus = prepare_localsplus(umd, g, code_flags); + *nlocalsplus = prepare_localsplus(umd, g); if (*nlocalsplus < 0) { return ERROR; } @@ -4075,6 +4196,10 @@ _PyCompile_OptimizeCfg(PyObject *seq, PyObject *consts, int nlocals) PyErr_SetString(PyExc_ValueError, "expected an instruction sequence"); return NULL; } + if (!PyList_Check(consts)) { + PyErr_SetString(PyExc_TypeError, "consts must be a list"); + return NULL; + } PyObject *const_cache = PyDict_New(); if (const_cache == NULL) { return NULL; diff --git a/Python/formatter_unicode.c b/Python/formatter_unicode.c deleted file mode 100644 index 30807f428c7d71..00000000000000 --- a/Python/formatter_unicode.c +++ /dev/null @@ -1,1726 +0,0 @@ -/* implements the unicode (as opposed to string) version of the - built-in formatters for string, int, float. that is, the versions - of int.__float__, etc., that take and return unicode objects */ - -#include "Python.h" -#include "pycore_fileutils.h" // _Py_GetLocaleconvNumeric() -#include "pycore_long.h" // _PyLong_FormatWriter() -#include "pycore_unicodeobject.h" // PyUnicode_MAX_CHAR_VALUE() -#include <locale.h> - -/* Raises an exception about an unknown presentation type for this - * type. */ - -static void -unknown_presentation_type(Py_UCS4 presentation_type, - const char* type_name) -{ - /* %c might be out-of-range, hence the two cases. */ - if (presentation_type > 32 && presentation_type < 128) - PyErr_Format(PyExc_ValueError, - "Unknown format code '%c' " - "for object of type '%.200s'", - (char)presentation_type, - type_name); - else - PyErr_Format(PyExc_ValueError, - "Unknown format code '\\x%x' " - "for object of type '%.200s'", - (unsigned int)presentation_type, - type_name); -} - -static void -invalid_thousands_separator_type(char specifier, Py_UCS4 presentation_type) -{ - assert(specifier == ',' || specifier == '_'); - if (presentation_type > 32 && presentation_type < 128) - PyErr_Format(PyExc_ValueError, - "Cannot specify '%c' with '%c'.", - specifier, (char)presentation_type); - else - PyErr_Format(PyExc_ValueError, - "Cannot specify '%c' with '\\x%x'.", - specifier, (unsigned int)presentation_type); -} - -static void -invalid_comma_and_underscore(void) -{ - PyErr_Format(PyExc_ValueError, "Cannot specify both ',' and '_'."); -} - -/* - get_integer consumes 0 or more decimal digit characters from an - input string, updates *result with the corresponding positive - integer, and returns the number of digits consumed. - - returns -1 on error. -*/ -static int -get_integer(PyObject *str, Py_ssize_t *ppos, Py_ssize_t end, - Py_ssize_t *result) -{ - Py_ssize_t accumulator, digitval, pos = *ppos; - int numdigits; - int kind = PyUnicode_KIND(str); - const void *data = PyUnicode_DATA(str); - - accumulator = numdigits = 0; - for (; pos < end; pos++, numdigits++) { - digitval = Py_UNICODE_TODECIMAL(PyUnicode_READ(kind, data, pos)); - if (digitval < 0) - break; - /* - Detect possible overflow before it happens: - - accumulator * 10 + digitval > PY_SSIZE_T_MAX if and only if - accumulator > (PY_SSIZE_T_MAX - digitval) / 10. - */ - if (accumulator > (PY_SSIZE_T_MAX - digitval) / 10) { - PyErr_Format(PyExc_ValueError, - "Too many decimal digits in format string"); - *ppos = pos; - return -1; - } - accumulator = accumulator * 10 + digitval; - } - *ppos = pos; - *result = accumulator; - return numdigits; -} - -/************************************************************************/ -/*********** standard format specifier parsing **************************/ -/************************************************************************/ - -/* returns true if this character is a specifier alignment token */ -Py_LOCAL_INLINE(int) -is_alignment_token(Py_UCS4 c) -{ - switch (c) { - case '<': case '>': case '=': case '^': - return 1; - default: - return 0; - } -} - -/* returns true if this character is a sign element */ -Py_LOCAL_INLINE(int) -is_sign_element(Py_UCS4 c) -{ - switch (c) { - case ' ': case '+': case '-': - return 1; - default: - return 0; - } -} - -/* Locale type codes. LT_NO_LOCALE must be zero. */ -enum LocaleType { - LT_NO_LOCALE = 0, - LT_DEFAULT_LOCALE = ',', - LT_UNDERSCORE_LOCALE = '_', - LT_UNDER_FOUR_LOCALE, - LT_CURRENT_LOCALE -}; - -typedef struct { - Py_UCS4 fill_char; - Py_UCS4 align; - int alternate; - int no_neg_0; - Py_UCS4 sign; - Py_ssize_t width; - enum LocaleType thousands_separators; - Py_ssize_t precision; - enum LocaleType frac_thousands_separator; - Py_UCS4 type; -} InternalFormatSpec; - - -/* - ptr points to the start of the format_spec, end points just past its end. - fills in format with the parsed information. - returns 1 on success, 0 on failure. - if failure, sets the exception -*/ -static int -parse_internal_render_format_spec(PyObject *obj, - PyObject *format_spec, - Py_ssize_t start, Py_ssize_t end, - InternalFormatSpec *format, - char default_type, - char default_align) -{ - Py_ssize_t pos = start; - int kind = PyUnicode_KIND(format_spec); - const void *data = PyUnicode_DATA(format_spec); - /* end-pos is used throughout this code to specify the length of - the input string */ -#define READ_spec(index) PyUnicode_READ(kind, data, index) - - Py_ssize_t consumed; - int align_specified = 0; - int fill_char_specified = 0; - - format->fill_char = ' '; - format->align = default_align; - format->alternate = 0; - format->no_neg_0 = 0; - format->sign = '\0'; - format->width = -1; - format->thousands_separators = LT_NO_LOCALE; - format->frac_thousands_separator = LT_NO_LOCALE; - format->precision = -1; - format->type = default_type; - - /* If the second char is an alignment token, - then parse the fill char */ - if (end-pos >= 2 && is_alignment_token(READ_spec(pos+1))) { - format->align = READ_spec(pos+1); - format->fill_char = READ_spec(pos); - fill_char_specified = 1; - align_specified = 1; - pos += 2; - } - else if (end-pos >= 1 && is_alignment_token(READ_spec(pos))) { - format->align = READ_spec(pos); - align_specified = 1; - ++pos; - } - - /* Parse the various sign options */ - if (end-pos >= 1 && is_sign_element(READ_spec(pos))) { - format->sign = READ_spec(pos); - ++pos; - } - - /* If the next character is z, request coercion of negative 0. - Applies only to floats. */ - if (end-pos >= 1 && READ_spec(pos) == 'z') { - format->no_neg_0 = 1; - ++pos; - } - - /* If the next character is #, we're in alternate mode. This only - applies to integers. */ - if (end-pos >= 1 && READ_spec(pos) == '#') { - format->alternate = 1; - ++pos; - } - - /* The special case for 0-padding (backwards compat) */ - if (!fill_char_specified && end-pos >= 1 && READ_spec(pos) == '0') { - format->fill_char = '0'; - if (!align_specified && default_align == '>') { - format->align = '='; - } - ++pos; - } - - consumed = get_integer(format_spec, &pos, end, &format->width); - if (consumed == -1) - /* Overflow error. Exception already set. */ - return 0; - - /* If consumed is 0, we didn't consume any characters for the - width. In that case, reset the width to -1, because - get_integer() will have set it to zero. -1 is how we record - that the width wasn't specified. */ - if (consumed == 0) - format->width = -1; - - /* Comma signifies add thousands separators */ - if (end-pos && READ_spec(pos) == ',') { - format->thousands_separators = LT_DEFAULT_LOCALE; - ++pos; - } - /* Underscore signifies add thousands separators */ - if (end-pos && READ_spec(pos) == '_') { - if (format->thousands_separators != LT_NO_LOCALE) { - invalid_comma_and_underscore(); - return 0; - } - format->thousands_separators = LT_UNDERSCORE_LOCALE; - ++pos; - } - if (end-pos && READ_spec(pos) == ',') { - if (format->thousands_separators == LT_UNDERSCORE_LOCALE) { - invalid_comma_and_underscore(); - return 0; - } - } - - /* Parse field precision */ - if (end-pos && READ_spec(pos) == '.') { - ++pos; - - consumed = get_integer(format_spec, &pos, end, &format->precision); - if (consumed == -1) - /* Overflow error. Exception already set. */ - return 0; - - if (end-pos && READ_spec(pos) == ',') { - if (consumed == 0) { - format->precision = -1; - } - format->frac_thousands_separator = LT_DEFAULT_LOCALE; - ++pos; - ++consumed; - } - if (end-pos && READ_spec(pos) == '_') { - if (format->frac_thousands_separator != LT_NO_LOCALE) { - invalid_comma_and_underscore(); - return 0; - } - if (consumed == 0) { - format->precision = -1; - } - format->frac_thousands_separator = LT_UNDERSCORE_LOCALE; - ++pos; - ++consumed; - } - if (end-pos && READ_spec(pos) == ',') { - if (format->frac_thousands_separator == LT_UNDERSCORE_LOCALE) { - invalid_comma_and_underscore(); - return 0; - } - } - - /* Not having a precision or underscore/comma after a dot - is an error. */ - if (consumed == 0) { - PyErr_Format(PyExc_ValueError, - "Format specifier missing precision"); - return 0; - } - - } - - /* Finally, parse the type field. */ - - if (end-pos > 1) { - /* More than one char remains, so this is an invalid format - specifier. */ - /* Create a temporary object that contains the format spec we're - operating on. It's format_spec[start:end] (in Python syntax). */ - PyObject* actual_format_spec = PyUnicode_FromKindAndData(kind, - (char*)data + kind*start, - end-start); - if (actual_format_spec != NULL) { - PyErr_Format(PyExc_ValueError, - "Invalid format specifier '%U' for object of type '%.200s'", - actual_format_spec, Py_TYPE(obj)->tp_name); - Py_DECREF(actual_format_spec); - } - return 0; - } - - if (end-pos == 1) { - format->type = READ_spec(pos); - ++pos; - } - - /* Do as much validating as we can, just by looking at the format - specifier. Do not take into account what type of formatting - we're doing (int, float, string). */ - - if (format->thousands_separators) { - switch (format->type) { - case 'd': - case 'e': - case 'f': - case 'g': - case 'E': - case 'G': - case '%': - case 'F': - case '\0': - /* These are allowed. See PEP 378.*/ - break; - case 'b': - case 'o': - case 'x': - case 'X': - /* Underscores are allowed in bin/oct/hex. See PEP 515. */ - if (format->thousands_separators == LT_UNDERSCORE_LOCALE) { - /* Every four digits, not every three, in bin/oct/hex. */ - format->thousands_separators = LT_UNDER_FOUR_LOCALE; - break; - } - _Py_FALLTHROUGH; - default: - invalid_thousands_separator_type(format->thousands_separators, format->type); - return 0; - } - } - - if (format->type == 'n' - && format->frac_thousands_separator != LT_NO_LOCALE) - { - invalid_thousands_separator_type(format->frac_thousands_separator, - format->type); - return 0; - } - - assert (format->align <= 127); - assert (format->sign <= 127); - return 1; -} - -/* Calculate the padding needed. */ -static void -calc_padding(Py_ssize_t nchars, Py_ssize_t width, Py_UCS4 align, - Py_ssize_t *n_lpadding, Py_ssize_t *n_rpadding, - Py_ssize_t *n_total) -{ - if (width >= 0) { - if (nchars > width) - *n_total = nchars; - else - *n_total = width; - } - else { - /* not specified, use all of the chars and no more */ - *n_total = nchars; - } - - /* Figure out how much leading space we need, based on the - aligning */ - if (align == '>') - *n_lpadding = *n_total - nchars; - else if (align == '^') - *n_lpadding = (*n_total - nchars) / 2; - else if (align == '<' || align == '=') - *n_lpadding = 0; - else { - /* We should never have an unspecified alignment. */ - Py_UNREACHABLE(); - } - - *n_rpadding = *n_total - nchars - *n_lpadding; -} - -/* Do the padding, and return a pointer to where the caller-supplied - content goes. */ -static int -fill_padding(_PyUnicodeWriter *writer, - Py_ssize_t nchars, - Py_UCS4 fill_char, Py_ssize_t n_lpadding, - Py_ssize_t n_rpadding) -{ - Py_ssize_t pos; - - /* Pad on left. */ - if (n_lpadding) { - pos = writer->pos; - _PyUnicode_FastFill(writer->buffer, pos, n_lpadding, fill_char); - } - - /* Pad on right. */ - if (n_rpadding) { - pos = writer->pos + nchars + n_lpadding; - _PyUnicode_FastFill(writer->buffer, pos, n_rpadding, fill_char); - } - - /* Pointer to the user content. */ - writer->pos += n_lpadding; - return 0; -} - -/************************************************************************/ -/*********** common routines for numeric formatting *********************/ -/************************************************************************/ - -/* Locale info needed for formatting integers and the part of floats - before and including the decimal. Note that locales only support - 8-bit chars, not unicode. */ -typedef struct { - PyObject *decimal_point; - PyObject *thousands_sep; - PyObject *frac_thousands_sep; - const char *grouping; - char *grouping_buffer; -} LocaleInfo; - -#define LocaleInfo_STATIC_INIT {0, 0, 0, 0} - -/* describes the layout for an integer, see the comment in - calc_number_widths() for details */ -typedef struct { - Py_ssize_t n_lpadding; - Py_ssize_t n_prefix; - Py_ssize_t n_spadding; - Py_ssize_t n_rpadding; - char sign; - Py_ssize_t n_sign; /* number of digits needed for sign (0/1) */ - Py_ssize_t n_grouped_digits; /* Space taken up by the digits, including - any grouping chars. */ - Py_ssize_t n_decimal; /* 0 if only an integer */ - Py_ssize_t n_remainder; /* Digits in decimal and/or exponent part, - excluding the decimal itself, if - present. */ - Py_ssize_t n_frac; - Py_ssize_t n_grouped_frac_digits; - - /* These 2 are not the widths of fields, but are needed by - STRINGLIB_GROUPING. */ - Py_ssize_t n_digits; /* The number of digits before a decimal - or exponent. */ - Py_ssize_t n_min_width; /* The min_width we used when we computed - the n_grouped_digits width. */ -} NumberFieldWidths; - - -/* Given a number of the form: - digits[remainder] - where ptr points to the start and end points to the end, find where - the integer part ends. This could be a decimal, an exponent, both, - or neither. - If a decimal point is present, set *has_decimal and increment - remainder beyond it. - Results are undefined (but shouldn't crash) for improperly - formatted strings. -*/ -static void -parse_number(PyObject *s, Py_ssize_t pos, Py_ssize_t end, - Py_ssize_t *n_remainder, Py_ssize_t *n_frac, int *has_decimal) -{ - Py_ssize_t frac; - int kind = PyUnicode_KIND(s); - const void *data = PyUnicode_DATA(s); - - while (pos<end && Py_ISDIGIT(PyUnicode_READ(kind, data, pos))) { - ++pos; - } - frac = pos; - - /* Does remainder start with a decimal point? */ - *has_decimal = pos<end && PyUnicode_READ(kind, data, frac) == '.'; - - /* Skip the decimal point. */ - if (*has_decimal) { - frac++; - pos++; - } - - while (pos<end && Py_ISDIGIT(PyUnicode_READ(kind, data, pos))) { - ++pos; - } - - *n_frac = pos - frac; - *n_remainder = end - pos; -} - -/* not all fields of format are used. for example, precision is - unused. should this take discrete params in order to be more clear - about what it does? or is passing a single format parameter easier - and more efficient enough to justify a little obfuscation? - Return -1 on error. */ -static Py_ssize_t -calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix, - Py_UCS4 sign_char, Py_ssize_t n_start, - Py_ssize_t n_end, Py_ssize_t n_remainder, Py_ssize_t n_frac, - int has_decimal, const LocaleInfo *locale, - const InternalFormatSpec *format, Py_UCS4 *maxchar) -{ - Py_ssize_t n_non_digit_non_padding; - Py_ssize_t n_padding; - - spec->n_digits = n_end - n_start - n_frac - n_remainder - (has_decimal?1:0); - spec->n_lpadding = 0; - spec->n_prefix = n_prefix; - spec->n_decimal = has_decimal ? PyUnicode_GET_LENGTH(locale->decimal_point) : 0; - spec->n_remainder = n_remainder; - spec->n_frac = n_frac; - spec->n_spadding = 0; - spec->n_rpadding = 0; - spec->sign = '\0'; - spec->n_sign = 0; - - /* the output will look like: - | | - | <lpadding> <sign> <prefix> <spadding> <grouped_digits> <decimal> <remainder> <rpadding> | - | | - - sign is computed from format->sign and the actual - sign of the number - - prefix is given (it's for the '0x' prefix) - - digits is already known - - the total width is either given, or computed from the - actual digits - - only one of lpadding, spadding, and rpadding can be non-zero, - and it's calculated from the width and other fields - */ - - /* compute the various parts we're going to write */ - switch (format->sign) { - case '+': - /* always put a + or - */ - spec->n_sign = 1; - spec->sign = (sign_char == '-' ? '-' : '+'); - break; - case ' ': - spec->n_sign = 1; - spec->sign = (sign_char == '-' ? '-' : ' '); - break; - default: - /* Not specified, or the default (-) */ - if (sign_char == '-') { - spec->n_sign = 1; - spec->sign = '-'; - } - } - - if (spec->n_frac == 0) { - spec->n_grouped_frac_digits = 0; - } - else { - Py_UCS4 grouping_maxchar; - spec->n_grouped_frac_digits = _PyUnicode_InsertThousandsGrouping( - NULL, 0, - NULL, 0, spec->n_frac, - spec->n_frac, - locale->grouping, locale->frac_thousands_sep, &grouping_maxchar, 1); - if (spec->n_grouped_frac_digits == -1) { - return -1; - } - *maxchar = Py_MAX(*maxchar, grouping_maxchar); - } - - /* The number of chars used for non-digits and non-padding. */ - n_non_digit_non_padding = spec->n_sign + spec->n_prefix + spec->n_decimal + - + spec->n_frac + spec->n_remainder; - - /* min_width can go negative, that's okay. format->width == -1 means - we don't care. */ - if (format->fill_char == '0' && format->align == '=') - spec->n_min_width = (format->width - n_non_digit_non_padding - + spec->n_frac - spec->n_grouped_frac_digits); - else - spec->n_min_width = 0; - - if (spec->n_digits == 0) - /* This case only occurs when using 'c' formatting, we need - to special case it because the grouping code always wants - to have at least one character. */ - spec->n_grouped_digits = 0; - else { - Py_UCS4 grouping_maxchar; - spec->n_grouped_digits = _PyUnicode_InsertThousandsGrouping( - NULL, 0, - NULL, 0, spec->n_digits, - spec->n_min_width, - locale->grouping, locale->thousands_sep, &grouping_maxchar, 0); - if (spec->n_grouped_digits == -1) { - return -1; - } - *maxchar = Py_MAX(*maxchar, grouping_maxchar); - } - - /* Given the desired width and the total of digit and non-digit - space we consume, see if we need any padding. format->width can - be negative (meaning no padding), but this code still works in - that case. */ - n_padding = format->width - - (n_non_digit_non_padding + spec->n_grouped_digits - + spec->n_grouped_frac_digits - spec->n_frac); - if (n_padding > 0) { - /* Some padding is needed. Determine if it's left, space, or right. */ - switch (format->align) { - case '<': - spec->n_rpadding = n_padding; - break; - case '^': - spec->n_lpadding = n_padding / 2; - spec->n_rpadding = n_padding - spec->n_lpadding; - break; - case '=': - spec->n_spadding = n_padding; - break; - case '>': - spec->n_lpadding = n_padding; - break; - default: - /* Shouldn't get here */ - Py_UNREACHABLE(); - } - } - - if (spec->n_lpadding || spec->n_spadding || spec->n_rpadding) - *maxchar = Py_MAX(*maxchar, format->fill_char); - - if (spec->n_decimal) - *maxchar = Py_MAX(*maxchar, PyUnicode_MAX_CHAR_VALUE(locale->decimal_point)); - - return spec->n_lpadding + spec->n_sign + spec->n_prefix + - spec->n_spadding + spec->n_grouped_digits + spec->n_decimal + - spec->n_grouped_frac_digits + spec->n_remainder + spec->n_rpadding; -} - -/* Fill in the digit parts of a number's string representation, - as determined in calc_number_widths(). - Return -1 on error, or 0 on success. */ -static int -fill_number(_PyUnicodeWriter *writer, const NumberFieldWidths *spec, - PyObject *digits, Py_ssize_t d_start, - PyObject *prefix, Py_ssize_t p_start, - Py_UCS4 fill_char, - LocaleInfo *locale, int toupper) -{ - /* Used to keep track of digits, decimal, and remainder. */ - Py_ssize_t d_pos = d_start; - const int kind = writer->kind; - const void *data = writer->data; - Py_ssize_t r; - - if (spec->n_lpadding) { - _PyUnicode_FastFill(writer->buffer, - writer->pos, spec->n_lpadding, fill_char); - writer->pos += spec->n_lpadding; - } - if (spec->n_sign == 1) { - PyUnicode_WRITE(kind, data, writer->pos, spec->sign); - writer->pos++; - } - if (spec->n_prefix) { - _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, - prefix, p_start, - spec->n_prefix); - if (toupper) { - Py_ssize_t t; - for (t = 0; t < spec->n_prefix; t++) { - Py_UCS4 c = PyUnicode_READ(kind, data, writer->pos + t); - c = Py_TOUPPER(c); - assert (c <= 127); - PyUnicode_WRITE(kind, data, writer->pos + t, c); - } - } - writer->pos += spec->n_prefix; - } - if (spec->n_spadding) { - _PyUnicode_FastFill(writer->buffer, - writer->pos, spec->n_spadding, fill_char); - writer->pos += spec->n_spadding; - } - - /* Only for type 'c' special case, it has no digits. */ - if (spec->n_digits != 0) { - /* Fill the digits with InsertThousandsGrouping. */ - r = _PyUnicode_InsertThousandsGrouping( - writer, spec->n_grouped_digits, - digits, d_pos, spec->n_digits, - spec->n_min_width, - locale->grouping, locale->thousands_sep, NULL, 0); - if (r == -1) - return -1; - assert(r == spec->n_grouped_digits); - d_pos += spec->n_digits; - } - if (toupper) { - Py_ssize_t t; - for (t = 0; t < spec->n_grouped_digits; t++) { - Py_UCS4 c = PyUnicode_READ(kind, data, writer->pos + t); - c = Py_TOUPPER(c); - if (c > 127) { - PyErr_SetString(PyExc_SystemError, "non-ascii grouped digit"); - return -1; - } - PyUnicode_WRITE(kind, data, writer->pos + t, c); - } - } - writer->pos += spec->n_grouped_digits; - - if (spec->n_decimal) { - _PyUnicode_FastCopyCharacters( - writer->buffer, writer->pos, - locale->decimal_point, 0, spec->n_decimal); - writer->pos += spec->n_decimal; - d_pos += 1; - } - - if (spec->n_frac) { - r = _PyUnicode_InsertThousandsGrouping( - writer, spec->n_grouped_frac_digits, - digits, d_pos, spec->n_frac, spec->n_frac, - locale->grouping, locale->frac_thousands_sep, NULL, 1); - if (r == -1) { - return -1; - } - assert(r == spec->n_grouped_frac_digits); - d_pos += spec->n_frac; - writer->pos += spec->n_grouped_frac_digits; - } - - if (spec->n_remainder) { - _PyUnicode_FastCopyCharacters( - writer->buffer, writer->pos, - digits, d_pos, spec->n_remainder); - writer->pos += spec->n_remainder; - /* d_pos += spec->n_remainder; */ - } - - if (spec->n_rpadding) { - _PyUnicode_FastFill(writer->buffer, - writer->pos, spec->n_rpadding, - fill_char); - writer->pos += spec->n_rpadding; - } - return 0; -} - -static const char no_grouping[1] = {CHAR_MAX}; - -/* Find the decimal point character(s?), thousands_separator(s?), and - grouping description, either for the current locale if type is - LT_CURRENT_LOCALE, a hard-coded locale if LT_DEFAULT_LOCALE or - LT_UNDERSCORE_LOCALE/LT_UNDER_FOUR_LOCALE, or none if LT_NO_LOCALE. */ -static int -get_locale_info(enum LocaleType type, enum LocaleType frac_type, - LocaleInfo *locale_info) -{ - switch (type) { - case LT_CURRENT_LOCALE: { - struct lconv *lc = localeconv(); - if (_Py_GetLocaleconvNumeric(lc, - &locale_info->decimal_point, - &locale_info->thousands_sep) < 0) { - return -1; - } - - /* localeconv() grouping can become a dangling pointer or point - to a different string if another thread calls localeconv() during - the string formatting. Copy the string to avoid this risk. */ - locale_info->grouping_buffer = _PyMem_Strdup(lc->grouping); - if (locale_info->grouping_buffer == NULL) { - PyErr_NoMemory(); - return -1; - } - locale_info->grouping = locale_info->grouping_buffer; - break; - } - case LT_DEFAULT_LOCALE: - case LT_UNDERSCORE_LOCALE: - case LT_UNDER_FOUR_LOCALE: - locale_info->decimal_point = PyUnicode_FromOrdinal('.'); - locale_info->thousands_sep = PyUnicode_FromOrdinal( - type == LT_DEFAULT_LOCALE ? ',' : '_'); - if (!locale_info->decimal_point || !locale_info->thousands_sep) - return -1; - if (type != LT_UNDER_FOUR_LOCALE) - locale_info->grouping = "\3"; /* Group every 3 characters. The - (implicit) trailing 0 means repeat - infinitely. */ - else - locale_info->grouping = "\4"; /* Bin/oct/hex group every four. */ - break; - case LT_NO_LOCALE: - locale_info->decimal_point = PyUnicode_FromOrdinal('.'); - locale_info->thousands_sep = Py_GetConstant(Py_CONSTANT_EMPTY_STR); - if (!locale_info->decimal_point || !locale_info->thousands_sep) - return -1; - locale_info->grouping = no_grouping; - break; - } - if (frac_type != LT_NO_LOCALE) { - locale_info->frac_thousands_sep = PyUnicode_FromOrdinal( - frac_type == LT_DEFAULT_LOCALE ? ',' : '_'); - if (!locale_info->frac_thousands_sep) { - return -1; - } - if (locale_info->grouping == no_grouping) { - locale_info->grouping = "\3"; - } - } - else { - locale_info->frac_thousands_sep = Py_GetConstant(Py_CONSTANT_EMPTY_STR); - } - return 0; -} - -static void -free_locale_info(LocaleInfo *locale_info) -{ - Py_XDECREF(locale_info->decimal_point); - Py_XDECREF(locale_info->thousands_sep); - Py_XDECREF(locale_info->frac_thousands_sep); - PyMem_Free(locale_info->grouping_buffer); -} - -/************************************************************************/ -/*********** string formatting ******************************************/ -/************************************************************************/ - -static int -format_string_internal(PyObject *value, const InternalFormatSpec *format, - _PyUnicodeWriter *writer) -{ - Py_ssize_t lpad; - Py_ssize_t rpad; - Py_ssize_t total; - Py_ssize_t len; - int result = -1; - Py_UCS4 maxchar; - - len = PyUnicode_GET_LENGTH(value); - - /* sign is not allowed on strings */ - if (format->sign != '\0') { - if (format->sign == ' ') { - PyErr_SetString(PyExc_ValueError, - "Space not allowed in string format specifier"); - } - else { - PyErr_SetString(PyExc_ValueError, - "Sign not allowed in string format specifier"); - } - goto done; - } - - /* negative 0 coercion is not allowed on strings */ - if (format->no_neg_0) { - PyErr_SetString(PyExc_ValueError, - "Negative zero coercion (z) not allowed in string format " - "specifier"); - goto done; - } - - /* alternate is not allowed on strings */ - if (format->alternate) { - PyErr_SetString(PyExc_ValueError, - "Alternate form (#) not allowed in string format " - "specifier"); - goto done; - } - - /* '=' alignment not allowed on strings */ - if (format->align == '=') { - PyErr_SetString(PyExc_ValueError, - "'=' alignment not allowed " - "in string format specifier"); - goto done; - } - - if ((format->width == -1 || format->width <= len) - && (format->precision == -1 || format->precision >= len)) { - /* Fast path */ - return _PyUnicodeWriter_WriteStr(writer, value); - } - - /* if precision is specified, output no more that format.precision - characters */ - if (format->precision >= 0 && len >= format->precision) { - len = format->precision; - } - - calc_padding(len, format->width, format->align, &lpad, &rpad, &total); - - maxchar = writer->maxchar; - if (lpad != 0 || rpad != 0) - maxchar = Py_MAX(maxchar, format->fill_char); - if (PyUnicode_MAX_CHAR_VALUE(value) > maxchar) { - Py_UCS4 valmaxchar = _PyUnicode_FindMaxChar(value, 0, len); - maxchar = Py_MAX(maxchar, valmaxchar); - } - - /* allocate the resulting string */ - if (_PyUnicodeWriter_Prepare(writer, total, maxchar) == -1) - goto done; - - /* Write into that space. First the padding. */ - result = fill_padding(writer, len, format->fill_char, lpad, rpad); - if (result == -1) - goto done; - - /* Then the source string. */ - if (len) { - _PyUnicode_FastCopyCharacters(writer->buffer, writer->pos, - value, 0, len); - } - writer->pos += (len + rpad); - result = 0; - -done: - return result; -} - - -/************************************************************************/ -/*********** long formatting ********************************************/ -/************************************************************************/ - -static int -format_long_internal(PyObject *value, const InternalFormatSpec *format, - _PyUnicodeWriter *writer) -{ - int result = -1; - Py_UCS4 maxchar = 127; - PyObject *tmp = NULL; - Py_ssize_t inumeric_chars; - Py_UCS4 sign_char = '\0'; - Py_ssize_t n_digits; /* count of digits need from the computed - string */ - Py_ssize_t n_remainder = 0; /* Used only for 'c' formatting, which - produces non-digits */ - Py_ssize_t n_prefix = 0; /* Count of prefix chars, (e.g., '0x') */ - Py_ssize_t n_total; - Py_ssize_t prefix = 0; - NumberFieldWidths spec; - long x; - - /* Locale settings, either from the actual locale or - from a hard-code pseudo-locale */ - LocaleInfo locale = LocaleInfo_STATIC_INIT; - - /* no precision allowed on integers */ - if (format->precision != -1) { - PyErr_SetString(PyExc_ValueError, - "Precision not allowed in integer format specifier"); - goto done; - } - /* no negative zero coercion on integers */ - if (format->no_neg_0) { - PyErr_SetString(PyExc_ValueError, - "Negative zero coercion (z) not allowed in integer" - " format specifier"); - goto done; - } - - /* special case for character formatting */ - if (format->type == 'c') { - /* error to specify a sign */ - if (format->sign != '\0') { - PyErr_SetString(PyExc_ValueError, - "Sign not allowed with integer" - " format specifier 'c'"); - goto done; - } - /* error to request alternate format */ - if (format->alternate) { - PyErr_SetString(PyExc_ValueError, - "Alternate form (#) not allowed with integer" - " format specifier 'c'"); - goto done; - } - - /* taken from unicodeobject.c formatchar() */ - /* Integer input truncated to a character */ - x = PyLong_AsLong(value); - if (x == -1 && PyErr_Occurred()) - goto done; - if (x < 0 || x > 0x10ffff) { - PyErr_SetString(PyExc_OverflowError, - "%c arg not in range(0x110000)"); - goto done; - } - tmp = PyUnicode_FromOrdinal(x); - inumeric_chars = 0; - n_digits = 1; - maxchar = Py_MAX(maxchar, (Py_UCS4)x); - - /* As a sort-of hack, we tell calc_number_widths that we only - have "remainder" characters. calc_number_widths thinks - these are characters that don't get formatted, only copied - into the output string. We do this for 'c' formatting, - because the characters are likely to be non-digits. */ - n_remainder = 1; - } - else { - int base; - int leading_chars_to_skip = 0; /* Number of characters added by - PyNumber_ToBase that we want to - skip over. */ - - /* Compute the base and how many characters will be added by - PyNumber_ToBase */ - switch (format->type) { - case 'b': - base = 2; - leading_chars_to_skip = 2; /* 0b */ - break; - case 'o': - base = 8; - leading_chars_to_skip = 2; /* 0o */ - break; - case 'x': - case 'X': - base = 16; - leading_chars_to_skip = 2; /* 0x */ - break; - default: /* shouldn't be needed, but stops a compiler warning */ - case 'd': - case 'n': - base = 10; - break; - } - - if (format->sign != '+' && format->sign != ' ' - && format->width == -1 - && format->type != 'X' && format->type != 'n' - && !format->thousands_separators - && PyLong_CheckExact(value)) - { - /* Fast path */ - return _PyLong_FormatWriter(writer, value, base, format->alternate); - } - - /* The number of prefix chars is the same as the leading - chars to skip */ - if (format->alternate) - n_prefix = leading_chars_to_skip; - - /* Do the hard part, converting to a string in a given base */ - tmp = _PyLong_Format(value, base); - if (tmp == NULL) - goto done; - - inumeric_chars = 0; - n_digits = PyUnicode_GET_LENGTH(tmp); - - prefix = inumeric_chars; - - /* Is a sign character present in the output? If so, remember it - and skip it */ - if (PyUnicode_READ_CHAR(tmp, inumeric_chars) == '-') { - sign_char = '-'; - ++prefix; - ++leading_chars_to_skip; - } - - /* Skip over the leading chars (0x, 0b, etc.) */ - n_digits -= leading_chars_to_skip; - inumeric_chars += leading_chars_to_skip; - } - - /* Determine the grouping, separator, and decimal point, if any. */ - if (get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : - format->thousands_separators, 0, - &locale) == -1) - goto done; - - /* Calculate how much memory we'll need. */ - n_total = calc_number_widths(&spec, n_prefix, sign_char, inumeric_chars, - inumeric_chars + n_digits, n_remainder, 0, 0, - &locale, format, &maxchar); - if (n_total == -1) { - goto done; - } - - /* Allocate the memory. */ - if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1) - goto done; - - /* Populate the memory. */ - result = fill_number(writer, &spec, - tmp, inumeric_chars, - tmp, prefix, format->fill_char, - &locale, format->type == 'X'); - -done: - Py_XDECREF(tmp); - free_locale_info(&locale); - return result; -} - -/************************************************************************/ -/*********** float formatting *******************************************/ -/************************************************************************/ - -/* much of this is taken from unicodeobject.c */ -static int -format_float_internal(PyObject *value, - const InternalFormatSpec *format, - _PyUnicodeWriter *writer) -{ - char *buf = NULL; /* buffer returned from PyOS_double_to_string */ - Py_ssize_t n_digits; - Py_ssize_t n_remainder; - Py_ssize_t n_frac; - Py_ssize_t n_total; - int has_decimal; - double val; - int precision, default_precision = 6; - Py_UCS4 type = format->type; - int add_pct = 0; - Py_ssize_t index; - NumberFieldWidths spec; - int flags = 0; - int result = -1; - Py_UCS4 maxchar = 127; - Py_UCS4 sign_char = '\0'; - int float_type; /* Used to see if we have a nan, inf, or regular float. */ - PyObject *unicode_tmp = NULL; - - /* Locale settings, either from the actual locale or - from a hard-code pseudo-locale */ - LocaleInfo locale = LocaleInfo_STATIC_INIT; - - if (format->precision > INT_MAX) { - PyErr_SetString(PyExc_ValueError, "precision too big"); - goto done; - } - precision = (int)format->precision; - - if (format->alternate) - flags |= Py_DTSF_ALT; - if (format->no_neg_0) - flags |= Py_DTSF_NO_NEG_0; - - if (type == '\0') { - /* Omitted type specifier. Behaves in the same way as repr(x) - and str(x) if no precision is given, else like 'g', but with - at least one digit after the decimal point. */ - flags |= Py_DTSF_ADD_DOT_0; - type = 'r'; - default_precision = 0; - } - - if (type == 'n') - /* 'n' is the same as 'g', except for the locale used to - format the result. We take care of that later. */ - type = 'g'; - - val = PyFloat_AsDouble(value); - if (val == -1.0 && PyErr_Occurred()) - goto done; - - if (type == '%') { - type = 'f'; - val *= 100; - add_pct = 1; - } - - if (precision < 0) - precision = default_precision; - else if (type == 'r') - type = 'g'; - - /* Cast "type", because if we're in unicode we need to pass an - 8-bit char. This is safe, because we've restricted what "type" - can be. */ - buf = PyOS_double_to_string(val, (char)type, precision, flags, - &float_type); - if (buf == NULL) - goto done; - n_digits = strlen(buf); - - if (add_pct) { - /* We know that buf has a trailing zero (since we just called - strlen() on it), and we don't use that fact any more. So we - can just write over the trailing zero. */ - buf[n_digits] = '%'; - n_digits += 1; - } - - if (format->sign != '+' && format->sign != ' ' - && format->width == -1 - && format->type != 'n' - && !format->thousands_separators - && !format->frac_thousands_separator) - { - /* Fast path */ - result = _PyUnicodeWriter_WriteASCIIString(writer, buf, n_digits); - PyMem_Free(buf); - return result; - } - - /* Since there is no unicode version of PyOS_double_to_string, - just use the 8 bit version and then convert to unicode. */ - unicode_tmp = _PyUnicode_FromASCII(buf, n_digits); - PyMem_Free(buf); - if (unicode_tmp == NULL) - goto done; - - /* Is a sign character present in the output? If so, remember it - and skip it */ - index = 0; - if (PyUnicode_READ_CHAR(unicode_tmp, index) == '-') { - sign_char = '-'; - ++index; - --n_digits; - } - - /* Determine if we have any "remainder" (after the digits, might include - decimal or exponent or both (or neither)) */ - parse_number(unicode_tmp, index, index + n_digits, - &n_remainder, &n_frac, &has_decimal); - - /* Determine the grouping, separator, and decimal point, if any. */ - if (get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : - format->thousands_separators, - format->frac_thousands_separator, - &locale) == -1) - goto done; - - /* Calculate how much memory we'll need. */ - n_total = calc_number_widths(&spec, 0, sign_char, index, - index + n_digits, n_remainder, n_frac, - has_decimal, &locale, format, &maxchar); - if (n_total == -1) { - goto done; - } - - /* Allocate the memory. */ - if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1) - goto done; - - /* Populate the memory. */ - result = fill_number(writer, &spec, - unicode_tmp, index, - NULL, 0, format->fill_char, - &locale, 0); - -done: - Py_XDECREF(unicode_tmp); - free_locale_info(&locale); - return result; -} - -/************************************************************************/ -/*********** complex formatting *****************************************/ -/************************************************************************/ - -static int -format_complex_internal(PyObject *value, - const InternalFormatSpec *format, - _PyUnicodeWriter *writer) -{ - double re; - double im; - char *re_buf = NULL; /* buffer returned from PyOS_double_to_string */ - char *im_buf = NULL; /* buffer returned from PyOS_double_to_string */ - - InternalFormatSpec tmp_format = *format; - Py_ssize_t n_re_digits; - Py_ssize_t n_im_digits; - Py_ssize_t n_re_remainder; - Py_ssize_t n_im_remainder; - Py_ssize_t n_re_frac; - Py_ssize_t n_im_frac; - Py_ssize_t n_re_total; - Py_ssize_t n_im_total; - int re_has_decimal; - int im_has_decimal; - int precision, default_precision = 6; - Py_UCS4 type = format->type; - Py_ssize_t i_re; - Py_ssize_t i_im; - NumberFieldWidths re_spec; - NumberFieldWidths im_spec; - int flags = 0; - int result = -1; - Py_UCS4 maxchar = 127; - int rkind; - void *rdata; - Py_UCS4 re_sign_char = '\0'; - Py_UCS4 im_sign_char = '\0'; - int re_float_type; /* Used to see if we have a nan, inf, or regular float. */ - int im_float_type; - int add_parens = 0; - int skip_re = 0; - Py_ssize_t lpad; - Py_ssize_t rpad; - Py_ssize_t total; - PyObject *re_unicode_tmp = NULL; - PyObject *im_unicode_tmp = NULL; - - /* Locale settings, either from the actual locale or - from a hard-code pseudo-locale */ - LocaleInfo locale = LocaleInfo_STATIC_INIT; - - if (format->precision > INT_MAX) { - PyErr_SetString(PyExc_ValueError, "precision too big"); - goto done; - } - precision = (int)format->precision; - - /* Zero padding is not allowed. */ - if (format->fill_char == '0') { - PyErr_SetString(PyExc_ValueError, - "Zero padding is not allowed in complex format " - "specifier"); - goto done; - } - - /* Neither is '=' alignment . */ - if (format->align == '=') { - PyErr_SetString(PyExc_ValueError, - "'=' alignment flag is not allowed in complex format " - "specifier"); - goto done; - } - - re = PyComplex_RealAsDouble(value); - if (re == -1.0 && PyErr_Occurred()) - goto done; - im = PyComplex_ImagAsDouble(value); - if (im == -1.0 && PyErr_Occurred()) - goto done; - - if (format->alternate) - flags |= Py_DTSF_ALT; - if (format->no_neg_0) - flags |= Py_DTSF_NO_NEG_0; - - if (type == '\0') { - /* Omitted type specifier. Should be like str(self). */ - type = 'r'; - default_precision = 0; - if (re == 0.0 && copysign(1.0, re) == 1.0) - skip_re = 1; - else - add_parens = 1; - } - - if (type == 'n') - /* 'n' is the same as 'g', except for the locale used to - format the result. We take care of that later. */ - type = 'g'; - - if (precision < 0) - precision = default_precision; - else if (type == 'r') - type = 'g'; - - /* Cast "type", because if we're in unicode we need to pass an - 8-bit char. This is safe, because we've restricted what "type" - can be. */ - re_buf = PyOS_double_to_string(re, (char)type, precision, flags, - &re_float_type); - if (re_buf == NULL) - goto done; - im_buf = PyOS_double_to_string(im, (char)type, precision, flags, - &im_float_type); - if (im_buf == NULL) - goto done; - - n_re_digits = strlen(re_buf); - n_im_digits = strlen(im_buf); - - /* Since there is no unicode version of PyOS_double_to_string, - just use the 8 bit version and then convert to unicode. */ - re_unicode_tmp = _PyUnicode_FromASCII(re_buf, n_re_digits); - if (re_unicode_tmp == NULL) - goto done; - i_re = 0; - - im_unicode_tmp = _PyUnicode_FromASCII(im_buf, n_im_digits); - if (im_unicode_tmp == NULL) - goto done; - i_im = 0; - - /* Is a sign character present in the output? If so, remember it - and skip it */ - if (PyUnicode_READ_CHAR(re_unicode_tmp, i_re) == '-') { - re_sign_char = '-'; - ++i_re; - --n_re_digits; - } - if (PyUnicode_READ_CHAR(im_unicode_tmp, i_im) == '-') { - im_sign_char = '-'; - ++i_im; - --n_im_digits; - } - - /* Determine if we have any "remainder" (after the digits, might include - decimal or exponent or both (or neither)) */ - parse_number(re_unicode_tmp, i_re, i_re + n_re_digits, - &n_re_remainder, &n_re_frac, &re_has_decimal); - parse_number(im_unicode_tmp, i_im, i_im + n_im_digits, - &n_im_remainder, &n_im_frac, &im_has_decimal); - - /* Determine the grouping, separator, and decimal point, if any. */ - if (get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : - format->thousands_separators, - format->frac_thousands_separator, - &locale) == -1) - goto done; - - /* Turn off any padding. We'll do it later after we've composed - the numbers without padding. */ - tmp_format.fill_char = '\0'; - tmp_format.align = '<'; - tmp_format.width = -1; - - /* Calculate how much memory we'll need. */ - n_re_total = calc_number_widths(&re_spec, 0, re_sign_char, - i_re, i_re + n_re_digits, n_re_remainder, - n_re_frac, re_has_decimal, &locale, - &tmp_format, &maxchar); - if (n_re_total == -1) { - goto done; - } - - /* Same formatting, but always include a sign, unless the real part is - * going to be omitted, in which case we use whatever sign convention was - * requested by the original format. */ - if (!skip_re) - tmp_format.sign = '+'; - n_im_total = calc_number_widths(&im_spec, 0, im_sign_char, - i_im, i_im + n_im_digits, n_im_remainder, - n_im_frac, im_has_decimal, &locale, - &tmp_format, &maxchar); - if (n_im_total == -1) { - goto done; - } - - if (skip_re) - n_re_total = 0; - - /* Add 1 for the 'j', and optionally 2 for parens. */ - calc_padding(n_re_total + n_im_total + 1 + add_parens * 2, - format->width, format->align, &lpad, &rpad, &total); - - if (lpad || rpad) - maxchar = Py_MAX(maxchar, format->fill_char); - - if (_PyUnicodeWriter_Prepare(writer, total, maxchar) == -1) - goto done; - rkind = writer->kind; - rdata = writer->data; - - /* Populate the memory. First, the padding. */ - result = fill_padding(writer, - n_re_total + n_im_total + 1 + add_parens * 2, - format->fill_char, lpad, rpad); - if (result == -1) - goto done; - - if (add_parens) { - PyUnicode_WRITE(rkind, rdata, writer->pos, '('); - writer->pos++; - } - - if (!skip_re) { - result = fill_number(writer, &re_spec, - re_unicode_tmp, i_re, - NULL, 0, - 0, - &locale, 0); - if (result == -1) - goto done; - } - result = fill_number(writer, &im_spec, - im_unicode_tmp, i_im, - NULL, 0, - 0, - &locale, 0); - if (result == -1) - goto done; - PyUnicode_WRITE(rkind, rdata, writer->pos, 'j'); - writer->pos++; - - if (add_parens) { - PyUnicode_WRITE(rkind, rdata, writer->pos, ')'); - writer->pos++; - } - - writer->pos += rpad; - -done: - PyMem_Free(re_buf); - PyMem_Free(im_buf); - Py_XDECREF(re_unicode_tmp); - Py_XDECREF(im_unicode_tmp); - free_locale_info(&locale); - return result; -} - -/************************************************************************/ -/*********** built in formatters ****************************************/ -/************************************************************************/ -static int -format_obj(PyObject *obj, _PyUnicodeWriter *writer) -{ - PyObject *str; - int err; - - str = PyObject_Str(obj); - if (str == NULL) - return -1; - err = _PyUnicodeWriter_WriteStr(writer, str); - Py_DECREF(str); - return err; -} - -int -_PyUnicode_FormatAdvancedWriter(_PyUnicodeWriter *writer, - PyObject *obj, - PyObject *format_spec, - Py_ssize_t start, Py_ssize_t end) -{ - InternalFormatSpec format; - - assert(PyUnicode_Check(obj)); - - /* check for the special case of zero length format spec, make - it equivalent to str(obj) */ - if (start == end) { - if (PyUnicode_CheckExact(obj)) - return _PyUnicodeWriter_WriteStr(writer, obj); - else - return format_obj(obj, writer); - } - - /* parse the format_spec */ - if (!parse_internal_render_format_spec(obj, format_spec, start, end, - &format, 's', '<')) - return -1; - - /* type conversion? */ - switch (format.type) { - case 's': - /* no type conversion needed, already a string. do the formatting */ - return format_string_internal(obj, &format, writer); - default: - /* unknown */ - unknown_presentation_type(format.type, Py_TYPE(obj)->tp_name); - return -1; - } -} - -int -_PyLong_FormatAdvancedWriter(_PyUnicodeWriter *writer, - PyObject *obj, - PyObject *format_spec, - Py_ssize_t start, Py_ssize_t end) -{ - PyObject *tmp = NULL; - InternalFormatSpec format; - int result = -1; - - /* check for the special case of zero length format spec, make - it equivalent to str(obj) */ - if (start == end) { - if (PyLong_CheckExact(obj)) - return _PyLong_FormatWriter(writer, obj, 10, 0); - else - return format_obj(obj, writer); - } - - /* parse the format_spec */ - if (!parse_internal_render_format_spec(obj, format_spec, start, end, - &format, 'd', '>')) - goto done; - - /* type conversion? */ - switch (format.type) { - case 'b': - case 'c': - case 'd': - case 'o': - case 'x': - case 'X': - case 'n': - /* no type conversion needed, already an int. do the formatting */ - result = format_long_internal(obj, &format, writer); - break; - - case 'e': - case 'E': - case 'f': - case 'F': - case 'g': - case 'G': - case '%': - /* convert to float */ - tmp = PyNumber_Float(obj); - if (tmp == NULL) - goto done; - result = format_float_internal(tmp, &format, writer); - break; - - default: - /* unknown */ - unknown_presentation_type(format.type, Py_TYPE(obj)->tp_name); - goto done; - } - -done: - Py_XDECREF(tmp); - return result; -} - -int -_PyFloat_FormatAdvancedWriter(_PyUnicodeWriter *writer, - PyObject *obj, - PyObject *format_spec, - Py_ssize_t start, Py_ssize_t end) -{ - InternalFormatSpec format; - - /* check for the special case of zero length format spec, make - it equivalent to str(obj) */ - if (start == end) - return format_obj(obj, writer); - - /* parse the format_spec */ - if (!parse_internal_render_format_spec(obj, format_spec, start, end, - &format, '\0', '>')) - return -1; - - /* type conversion? */ - switch (format.type) { - case '\0': /* No format code: like 'g', but with at least one decimal. */ - case 'e': - case 'E': - case 'f': - case 'F': - case 'g': - case 'G': - case 'n': - case '%': - /* no conversion, already a float. do the formatting */ - return format_float_internal(obj, &format, writer); - - default: - /* unknown */ - unknown_presentation_type(format.type, Py_TYPE(obj)->tp_name); - return -1; - } -} - -int -_PyComplex_FormatAdvancedWriter(_PyUnicodeWriter *writer, - PyObject *obj, - PyObject *format_spec, - Py_ssize_t start, Py_ssize_t end) -{ - InternalFormatSpec format; - - /* check for the special case of zero length format spec, make - it equivalent to str(obj) */ - if (start == end) - return format_obj(obj, writer); - - /* parse the format_spec */ - if (!parse_internal_render_format_spec(obj, format_spec, start, end, - &format, '\0', '>')) - return -1; - - /* type conversion? */ - switch (format.type) { - case '\0': /* No format code: like 'g', but with at least one decimal. */ - case 'e': - case 'E': - case 'f': - case 'F': - case 'g': - case 'G': - case 'n': - /* no conversion, already a complex. do the formatting */ - return format_complex_internal(obj, &format, writer); - - default: - /* unknown */ - unknown_presentation_type(format.type, Py_TYPE(obj)->tp_name); - return -1; - } -} diff --git a/Python/frame.c b/Python/frame.c index ce216797e47cda..ff81eb0b3020c7 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -54,7 +54,7 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) _PyFrame_Copy(frame, new_frame); // _PyFrame_Copy takes the reference to the executable, // so we need to restore it. - frame->f_executable = PyStackRef_DUP(new_frame->f_executable); + new_frame->f_executable = PyStackRef_DUP(new_frame->f_executable); f->f_frame = new_frame; new_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; if (_PyFrame_IsIncomplete(new_frame)) { @@ -109,7 +109,7 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame) /* It is the responsibility of the owning generator/coroutine * to have cleared the enclosing generator, if any. */ assert(frame->owner != FRAME_OWNED_BY_GENERATOR || - _PyGen_GetGeneratorFromFrame(frame)->gi_frame_state == FRAME_CLEARED); + FT_ATOMIC_LOAD_INT8_RELAXED(_PyGen_GetGeneratorFromFrame(frame)->gi_frame_state) == FRAME_CLEARED); // GH-99729: Clearing this frame can expose the stack (via finalizers). It's // crucial that this frame has been unlinked, and is no longer visible: assert(_PyThreadState_GET()->current_frame != frame); @@ -135,14 +135,14 @@ PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame) return PyStackRef_AsPyObjectNew(frame->f_executable); } -int +// NOTE: We allow racy accesses to the instruction pointer from other threads +// for sys._current_frames() and similar APIs. +int _Py_NO_SANITIZE_THREAD PyUnstable_InterpreterFrame_GetLasti(struct _PyInterpreterFrame *frame) { return _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT); } -// NOTE: We allow racy accesses to the instruction pointer from other threads -// for sys._current_frames() and similar APIs. int _Py_NO_SANITIZE_THREAD PyUnstable_InterpreterFrame_GetLine(_PyInterpreterFrame *frame) { diff --git a/Python/frozen.c b/Python/frozen.c index 15d256b6743e0a..1fae26f8dbccb0 100644 --- a/Python/frozen.c +++ b/Python/frozen.c @@ -46,6 +46,10 @@ #include "frozen_modules/zipimport.h" #include "frozen_modules/abc.h" #include "frozen_modules/codecs.h" +#include "frozen_modules/encodings.h" +#include "frozen_modules/encodings.aliases.h" +#include "frozen_modules/encodings.utf_8.h" +#include "frozen_modules/encodings._win_cp_codecs.h" #include "frozen_modules/io.h" #include "frozen_modules/_collections_abc.h" #include "frozen_modules/_sitebuiltins.h" @@ -55,6 +59,7 @@ #include "frozen_modules/os.h" #include "frozen_modules/site.h" #include "frozen_modules/stat.h" +#include "frozen_modules/linecache.h" #include "frozen_modules/importlib.util.h" #include "frozen_modules/importlib.machinery.h" #include "frozen_modules/runpy.h" @@ -76,6 +81,10 @@ static const struct _frozen stdlib_modules[] = { /* stdlib - startup, without site (python -S) */ {"abc", _Py_M__abc, (int)sizeof(_Py_M__abc), false}, {"codecs", _Py_M__codecs, (int)sizeof(_Py_M__codecs), false}, + {"encodings", _Py_M__encodings, (int)sizeof(_Py_M__encodings), true}, + {"encodings.aliases", _Py_M__encodings_aliases, (int)sizeof(_Py_M__encodings_aliases), false}, + {"encodings.utf_8", _Py_M__encodings_utf_8, (int)sizeof(_Py_M__encodings_utf_8), false}, + {"encodings._win_cp_codecs", _Py_M__encodings__win_cp_codecs, (int)sizeof(_Py_M__encodings__win_cp_codecs), false}, {"io", _Py_M__io, (int)sizeof(_Py_M__io), false}, /* stdlib - startup, with site */ @@ -88,6 +97,9 @@ static const struct _frozen stdlib_modules[] = { {"site", _Py_M__site, (int)sizeof(_Py_M__site), false}, {"stat", _Py_M__stat, (int)sizeof(_Py_M__stat), false}, + /* pythonrun - interactive */ + {"linecache", _Py_M__linecache, (int)sizeof(_Py_M__linecache), false}, + /* runpy - run module with -m */ {"importlib.util", _Py_M__importlib_util, (int)sizeof(_Py_M__importlib_util), false}, {"importlib.machinery", _Py_M__importlib_machinery, (int)sizeof(_Py_M__importlib_machinery), false}, diff --git a/Python/frozenmain.c b/Python/frozenmain.c index ec4566bd4f84bc..3de587c0423226 100644 --- a/Python/frozenmain.c +++ b/Python/frozenmain.c @@ -1,8 +1,7 @@ /* Python interpreter main program for frozen scripts */ #include "Python.h" -#include "pycore_pystate.h" // _Py_GetConfig() -#include "pycore_runtime.h" // _PyRuntime_Initialize() +#include "pycore_pystate.h" // _PyInterpreterState_SetRunningMain() #ifdef HAVE_UNISTD_H # include <unistd.h> // isatty() @@ -20,11 +19,6 @@ extern int PyInitFrozenExtensions(void); int Py_FrozenMain(int argc, char **argv) { - PyStatus status = _PyRuntime_Initialize(); - if (PyStatus_Exception(status)) { - Py_ExitStatusException(status); - } - PyConfig config; PyConfig_InitPythonConfig(&config); // Suppress errors from getpath.c @@ -32,7 +26,7 @@ Py_FrozenMain(int argc, char **argv) // Don't parse command line options like -E config.parse_argv = 0; - status = PyConfig_SetBytesArgv(&config, argc, argv); + PyStatus status = PyConfig_SetBytesArgv(&config, argc, argv); if (PyStatus_Exception(status)) { PyConfig_Clear(&config); Py_ExitStatusException(status); @@ -64,7 +58,12 @@ Py_FrozenMain(int argc, char **argv) PyWinFreeze_ExeInit(); #endif - if (_Py_GetConfig()->verbose) { + int verbose; + if (PyConfig_GetInt("verbose", &verbose) < 0) { + verbose = 0; + PyErr_Clear(); + } + if (verbose) { fprintf(stderr, "Python %s\n%s\n", Py_GetVersion(), Py_GetCopyright()); } diff --git a/Python/gc.c b/Python/gc.c index 25536270523305..201c621bcc3cb9 100644 --- a/Python/gc.c +++ b/Python/gc.c @@ -6,17 +6,18 @@ #include "pycore_ceval.h" // _Py_set_eval_breaker_bit() #include "pycore_dict.h" // _PyInlineValuesSize() #include "pycore_initconfig.h" // _PyStatus_OK() +#include "pycore_context.h" #include "pycore_interp.h" // PyInterpreterState.gc #include "pycore_interpframe.h" // _PyFrame_GetLocalsArray() +#include "pycore_object.h" #include "pycore_object_alloc.h" // _PyObject_MallocWithType() +#include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_tuple.h" // _PyTuple_MaybeUntrack() #include "pycore_weakref.h" // _PyWeakref_ClearRef() - #include "pydtrace.h" - -#ifndef Py_GIL_DISABLED +#if !defined(Py_GIL_DISABLED) typedef struct _gc_runtime_state GCState; @@ -24,10 +25,6 @@ typedef struct _gc_runtime_state GCState; # define GC_DEBUG #endif -// Define this when debugging the GC -// #define GC_EXTRA_DEBUG - - #define GC_NEXT _PyGCHead_NEXT #define GC_PREV _PyGCHead_PREV @@ -50,7 +47,7 @@ typedef struct _gc_runtime_state GCState; // move_legacy_finalizers() removes this flag instead. // Between them, unreachable list is not normal list and we can not use // most gc_list_* functions for it. -#define NEXT_MASK_UNREACHABLE 2 +#define NEXT_MASK_UNREACHABLE (1) #define AS_GC(op) _Py_AS_GC(op) #define FROM_GC(gc) _Py_FROM_GC(gc) @@ -100,48 +97,9 @@ gc_decref(PyGC_Head *g) g->_gc_prev -= 1 << _PyGC_PREV_SHIFT; } -static inline int -gc_old_space(PyGC_Head *g) -{ - return g->_gc_next & _PyGC_NEXT_MASK_OLD_SPACE_1; -} - -static inline int -other_space(int space) -{ - assert(space == 0 || space == 1); - return space ^ _PyGC_NEXT_MASK_OLD_SPACE_1; -} - -static inline void -gc_flip_old_space(PyGC_Head *g) -{ - g->_gc_next ^= _PyGC_NEXT_MASK_OLD_SPACE_1; -} -static inline void -gc_set_old_space(PyGC_Head *g, int space) -{ - assert(space == 0 || space == _PyGC_NEXT_MASK_OLD_SPACE_1); - g->_gc_next &= ~_PyGC_NEXT_MASK_OLD_SPACE_1; - g->_gc_next |= space; -} +#define GEN_HEAD(gcstate, n) (&(gcstate)->generations[n].head) -static PyGC_Head * -GEN_HEAD(GCState *gcstate, int n) -{ - assert((gcstate->visited_space & (~1)) == 0); - switch(n) { - case 0: - return &gcstate->young.head; - case 1: - return &gcstate->old[gcstate->visited_space].head; - case 2: - return &gcstate->old[gcstate->visited_space^1].head; - default: - Py_UNREACHABLE(); - } -} static GCState * get_gc_state(void) @@ -160,12 +118,11 @@ _PyGC_InitState(GCState *gcstate) GEN.head._gc_prev = (uintptr_t)&GEN.head; \ } while (0) - assert(gcstate->young.count == 0); - assert(gcstate->old[0].count == 0); - assert(gcstate->old[1].count == 0); - INIT_HEAD(gcstate->young); - INIT_HEAD(gcstate->old[0]); - INIT_HEAD(gcstate->old[1]); + for (int i = 0; i < NUM_GENERATIONS; i++) { + assert(gcstate->generations[i].count == 0); + INIT_HEAD(gcstate->generations[i]); + }; + gcstate->generation0 = GEN_HEAD(gcstate, 0); INIT_HEAD(gcstate->permanent_generation); #undef INIT_HEAD @@ -177,6 +134,11 @@ _PyGC_Init(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; + gcstate->generation_stats = PyMem_RawCalloc(1, sizeof(struct gc_stats)); + if (gcstate->generation_stats == NULL) { + return _PyStatus_NO_MEMORY(); + } + gcstate->garbage = PyList_New(0); if (gcstate->garbage == NULL) { return _PyStatus_NO_MEMORY(); @@ -186,7 +148,6 @@ _PyGC_Init(PyInterpreterState *interp) if (gcstate->callbacks == NULL) { return _PyStatus_NO_MEMORY(); } - gcstate->heap_size = 0; return _PyStatus_OK(); } @@ -264,7 +225,6 @@ gc_list_is_empty(PyGC_Head *list) static inline void gc_list_append(PyGC_Head *node, PyGC_Head *list) { - assert((list->_gc_prev & ~_PyGC_PREV_MASK) == 0); PyGC_Head *last = (PyGC_Head *)list->_gc_prev; // last <-> node @@ -322,8 +282,6 @@ gc_list_merge(PyGC_Head *from, PyGC_Head *to) PyGC_Head *from_tail = GC_PREV(from); assert(from_head != from); assert(from_tail != from); - assert(gc_list_is_empty(to) || - gc_old_space(to_tail) == gc_old_space(from_tail)); _PyGCHead_SET_NEXT(to_tail, from_head); _PyGCHead_SET_PREV(from_head, to_tail); @@ -392,8 +350,8 @@ enum flagstates {collecting_clear_unreachable_clear, static void validate_list(PyGC_Head *head, enum flagstates flags) { - assert((head->_gc_prev & ~_PyGC_PREV_MASK) == 0); - assert((head->_gc_next & ~_PyGC_PREV_MASK) == 0); + assert((head->_gc_prev & PREV_MASK_COLLECTING) == 0); + assert((head->_gc_next & NEXT_MASK_UNREACHABLE) == 0); uintptr_t prev_value = 0, next_value = 0; switch (flags) { case collecting_clear_unreachable_clear: @@ -415,7 +373,7 @@ validate_list(PyGC_Head *head, enum flagstates flags) PyGC_Head *gc = GC_NEXT(head); while (gc != head) { PyGC_Head *trueprev = GC_PREV(gc); - PyGC_Head *truenext = GC_NEXT(gc); + PyGC_Head *truenext = (PyGC_Head *)(gc->_gc_next & ~NEXT_MASK_UNREACHABLE); assert(truenext != NULL); assert(trueprev == prev); assert((gc->_gc_prev & PREV_MASK_COLLECTING) == prev_value); @@ -425,69 +383,22 @@ validate_list(PyGC_Head *head, enum flagstates flags) } assert(prev == GC_PREV(head)); } - #else #define validate_list(x, y) do{}while(0) #endif -#ifdef GC_EXTRA_DEBUG - - -static void -gc_list_validate_space(PyGC_Head *head, int space) { - PyGC_Head *gc = GC_NEXT(head); - while (gc != head) { - assert(gc_old_space(gc) == space); - gc = GC_NEXT(gc); - } -} - -static void -validate_spaces(GCState *gcstate) -{ - int visited = gcstate->visited_space; - int not_visited = other_space(visited); - gc_list_validate_space(&gcstate->young.head, not_visited); - for (int space = 0; space < 2; space++) { - gc_list_validate_space(&gcstate->old[space].head, space); - } - gc_list_validate_space(&gcstate->permanent_generation.head, visited); -} - -static void -validate_consistent_old_space(PyGC_Head *head) -{ - PyGC_Head *gc = GC_NEXT(head); - if (gc == head) { - return; - } - int old_space = gc_old_space(gc); - while (gc != head) { - PyGC_Head *truenext = GC_NEXT(gc); - assert(truenext != NULL); - assert(gc_old_space(gc) == old_space); - gc = truenext; - } -} - - -#else -#define validate_spaces(g) do{}while(0) -#define validate_consistent_old_space(l) do{}while(0) -#define gc_list_validate_space(l, s) do{}while(0) -#endif - /*** end of list stuff ***/ /* Set all gc_refs = ob_refcnt. After this, gc_refs is > 0 and * PREV_MASK_COLLECTING bit is set for all objects in containers. */ -static void +static Py_ssize_t update_refs(PyGC_Head *containers) { PyGC_Head *next; PyGC_Head *gc = GC_NEXT(containers); + Py_ssize_t candidates = 0; while (gc != containers) { next = GC_NEXT(gc); @@ -495,8 +406,8 @@ update_refs(PyGC_Head *containers) if (_Py_IsImmortal(op)) { assert(!_Py_IsStaticImmortal(op)); _PyObject_GC_UNTRACK(op); - gc = next; - continue; + gc = next; + continue; } gc_reset_refs(gc, Py_REFCNT(op)); /* Python's cyclic gc should never see an incoming refcount @@ -519,7 +430,9 @@ update_refs(PyGC_Head *containers) */ _PyObject_ASSERT(op, gc_get_refs(gc) != 0); gc = next; + candidates++; } + return candidates; } /* A traversal callback for subtract_refs. */ @@ -570,8 +483,7 @@ _PyGC_VisitFrameStack(_PyInterpreterFrame *frame, visitproc visit, void *arg) } /* Subtract internal references from gc_refs. After this, gc_refs is >= 0 - * for all objects in containers, and is GC_REACHABLE for all tracked gc - * objects not in containers. The ones with gc_refs > 0 are directly + * for all objects in containers. The ones with gc_refs > 0 are directly * reachable from outside containers, and so can't be collected. */ static void @@ -622,13 +534,12 @@ visit_reachable(PyObject *op, void *arg) // Manually unlink gc from unreachable list because the list functions // don't work right in the presence of NEXT_MASK_UNREACHABLE flags. PyGC_Head *prev = GC_PREV(gc); - PyGC_Head *next = GC_NEXT(gc); + PyGC_Head *next = (PyGC_Head*)(gc->_gc_next & ~NEXT_MASK_UNREACHABLE); _PyObject_ASSERT(FROM_GC(prev), prev->_gc_next & NEXT_MASK_UNREACHABLE); _PyObject_ASSERT(FROM_GC(next), next->_gc_next & NEXT_MASK_UNREACHABLE); - prev->_gc_next = gc->_gc_next; // copy flag bits - gc->_gc_next &= ~NEXT_MASK_UNREACHABLE; + prev->_gc_next = gc->_gc_next; // copy NEXT_MASK_UNREACHABLE _PyGCHead_SET_PREV(next, prev); gc_list_append(gc, reachable); @@ -680,9 +591,6 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable) * or to the right have been scanned yet. */ - validate_consistent_old_space(young); - /* Record which old space we are in, and set NEXT_MASK_UNREACHABLE bit for convenience */ - uintptr_t flags = NEXT_MASK_UNREACHABLE | (gc->_gc_next & _PyGC_NEXT_MASK_OLD_SPACE_1); while (gc != young) { if (gc_get_refs(gc)) { /* gc is definitely reachable from outside the @@ -728,18 +636,17 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable) // But this may pollute the unreachable list head's 'next' pointer // too. That's semantically senseless but expedient here - the // damage is repaired when this function ends. - last->_gc_next = flags | (uintptr_t)gc; + last->_gc_next = (NEXT_MASK_UNREACHABLE | (uintptr_t)gc); _PyGCHead_SET_PREV(gc, last); - gc->_gc_next = flags | (uintptr_t)unreachable; + gc->_gc_next = (NEXT_MASK_UNREACHABLE | (uintptr_t)unreachable); unreachable->_gc_prev = (uintptr_t)gc; } - gc = _PyGCHead_NEXT(prev); + gc = (PyGC_Head*)prev->_gc_next; } // young->_gc_prev must be last element remained in the list. young->_gc_prev = (uintptr_t)prev; - young->_gc_next &= _PyGC_PREV_MASK; // don't let the pollution of the list head's next pointer leak - unreachable->_gc_next &= _PyGC_PREV_MASK; + unreachable->_gc_next &= ~NEXT_MASK_UNREACHABLE; } /* In theory, all tuples should be younger than the @@ -795,8 +702,8 @@ move_legacy_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers) PyObject *op = FROM_GC(gc); _PyObject_ASSERT(op, gc->_gc_next & NEXT_MASK_UNREACHABLE); - next = GC_NEXT(gc); gc->_gc_next &= ~NEXT_MASK_UNREACHABLE; + next = (PyGC_Head*)gc->_gc_next; if (has_legacy_finalizer(op)) { gc_clear_collecting(gc); @@ -819,8 +726,8 @@ clear_unreachable_mask(PyGC_Head *unreachable) PyGC_Head *gc, *next; for (gc = GC_NEXT(unreachable); gc != unreachable; gc = next) { _PyObject_ASSERT((PyObject*)FROM_GC(gc), gc->_gc_next & NEXT_MASK_UNREACHABLE); - next = GC_NEXT(gc); gc->_gc_next &= ~NEXT_MASK_UNREACHABLE; + next = (PyGC_Head*)gc->_gc_next; } validate_list(unreachable, collecting_set_unreachable_clear); } @@ -858,60 +765,65 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers) } } -/* Clear all weakrefs to unreachable objects, and if such a weakref has a - * callback, invoke it if necessary. Note that it's possible for such - * weakrefs to be outside the unreachable set -- indeed, those are precisely - * the weakrefs whose callbacks must be invoked. See gc_weakref.txt for - * overview & some details. Some weakrefs with callbacks may be reclaimed - * directly by this routine; the number reclaimed is the return value. Other - * weakrefs with callbacks may be moved into the `old` generation. Objects - * moved into `old` have gc_refs set to GC_REACHABLE; the objects remaining in - * unreachable are left at GC_TENTATIVELY_UNREACHABLE. When this returns, - * no object in `unreachable` is weakly referenced anymore. +/* Handle weakref callbacks. Note that it's possible for such weakrefs to be + * outside the unreachable set -- indeed, those are precisely the weakrefs + * whose callbacks must be invoked. See gc_weakref.txt for overview & some + * details. + * + * The clearing of weakrefs is suble and must be done carefully, as there was + * previous bugs related to this. First, weakrefs to the unreachable set of + * objects must be cleared before we start calling `tp_clear`. If we don't, + * those weakrefs can reveal unreachable objects to Python-level code and that + * is not safe. Many objects are not usable after `tp_clear` has been called + * and could even cause crashes if accessed (see bpo-38006 and gh-91636 as + * example bugs). + * + * Weakrefs with callbacks always need to be cleared before executing the + * callback. That's because sometimes a callback will call the ref object, + * to check if the reference is actually dead (KeyedRef does this, for + * example). We want to indicate that it is dead, even though it is possible + * a finalizer might resurrect it. Clearing also prevents the callback from + * executing more than once. + * + * Since Python 2.3, all weakrefs to cyclic garbage have been cleared *before* + * calling finalizers. However, since tp_subclasses started being necessary + * to invalidate caches (e.g. by PyType_Modified), that clearing has created + * a bug. If the weakref to the subclass is cleared before a finalizer is + * called, the cache may not be correctly invalidated. That can lead to + * segfaults since the caches can refer to deallocated objects (GH-135552 + * is an example). Now, we delay the clear of weakrefs without callbacks + * until *after* finalizers have been executed. That means weakrefs without + * callbacks are still usable while finalizers are being executed. + * + * The weakrefs that are inside the unreachable set must also be cleared. + * The reason this is required is not immediately obvious. If the weakref + * refers to an object outside of the unreachable set, that object might go + * away when we start clearing objects. Normally, the object should also be + * part of the unreachable set but that's not true in the case of incomplete + * or missing `tp_traverse` methods. When that object goes away, the callback + * for weakref can be executed and that could reveal unreachable objects to + * Python-level code. See bpo-38006 as an example bug. */ static int -handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks) +handle_weakref_callbacks(PyGC_Head *unreachable, PyGC_Head *old) { PyGC_Head *gc; - PyObject *op; /* generally FROM_GC(gc) */ - PyWeakReference *wr; /* generally a cast of op */ PyGC_Head wrcb_to_call; /* weakrefs with callbacks to call */ PyGC_Head *next; int num_freed = 0; - if (allow_callbacks) { - gc_list_init(&wrcb_to_call); - } + gc_list_init(&wrcb_to_call); - /* Clear all weakrefs to the objects in unreachable. If such a weakref - * also has a callback, move it into `wrcb_to_call` if the callback - * needs to be invoked. Note that we cannot invoke any callbacks until - * all weakrefs to unreachable objects are cleared, lest the callback - * resurrect an unreachable object via a still-active weakref. We - * make another pass over wrcb_to_call, invoking callbacks, after this - * pass completes. + /* Find all weakrefs with callbacks and move into `wrcb_to_call` if the + * callback needs to be invoked. We make another pass over wrcb_to_call, + * invoking callbacks, after this pass completes. */ for (gc = GC_NEXT(unreachable); gc != unreachable; gc = next) { PyWeakReference **wrlist; - op = FROM_GC(gc); + PyObject *op = FROM_GC(gc); next = GC_NEXT(gc); - if (PyWeakref_Check(op)) { - /* A weakref inside the unreachable set must be cleared. If we - * allow its callback to execute inside delete_garbage(), it - * could expose objects that have tp_clear already called on - * them. Or, it could resurrect unreachable objects. One way - * this can happen is if some container objects do not implement - * tp_traverse. Then, wr_object can be outside the unreachable - * set but can be deallocated as a result of breaking the - * reference cycle. If we don't clear the weakref, the callback - * will run and potentially cause a crash. See bpo-38006 for - * one example. - */ - _PyWeakref_ClearRef((PyWeakReference *)op); - } - if (! _PyType_SUPPORTS_WEAKREFS(Py_TYPE(op))) { continue; } @@ -923,30 +835,29 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks) */ wrlist = _PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(op); - /* `op` may have some weakrefs. March over the list, clear - * all the weakrefs, and move the weakrefs with callbacks - * that must be called into wrcb_to_call. + /* `op` may have some weakrefs. March over the list and move the + * weakrefs with callbacks that must be called into wrcb_to_call. */ - for (wr = *wrlist; wr != NULL; wr = *wrlist) { - PyGC_Head *wrasgc; /* AS_GC(wr) */ - - /* _PyWeakref_ClearRef clears the weakref but leaves - * the callback pointer intact. Obscure: it also - * changes *wrlist. - */ - _PyObject_ASSERT((PyObject *)wr, wr->wr_object == op); - _PyWeakref_ClearRef(wr); - _PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None); - - if (!allow_callbacks) { - continue; - } + PyWeakReference *next_wr; + for (PyWeakReference *wr = *wrlist; wr != NULL; wr = next_wr) { + // Get the next list element to get iterator progress if we omit + // clearing of the weakref (because _PyWeakref_ClearRef changes + // next pointer in the wrlist). + next_wr = wr->wr_next; if (wr->wr_callback == NULL) { /* no callback */ continue; } + // Clear the weakref. See the comments above this function for + // reasons why we need to clear weakrefs that have callbacks. + // Note that _PyWeakref_ClearRef clears the weakref but leaves the + // callback pointer intact. Obscure: it also changes *wrlist. + _PyObject_ASSERT((PyObject *)wr, wr->wr_object == op); + _PyWeakref_ClearRef(wr); + _PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None); + /* Headache time. `op` is going away, and is weakly referenced by * `wr`, which has a callback. Should the callback be invoked? If wr * is also trash, no: @@ -962,10 +873,10 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks) * outside the current generation, CT may be reachable from the * callback. Then the callback could resurrect insane objects. * - * Since the callback is never needed and may be unsafe in this case, - * wr is simply left in the unreachable set. Note that because we - * already called _PyWeakref_ClearRef(wr), its callback will never - * trigger. + * Since the callback is never needed and may be unsafe in this + * case, wr is simply left in the unreachable set. Note that + * clear_weakrefs() will ensure its callback will not trigger + * inside delete_garbage(). * * OTOH, if wr isn't part of CT, we should invoke the callback: the * weakref outlived the trash. Note that since wr isn't CT in this @@ -976,8 +887,6 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks) * is moved to wrcb_to_call in this case. */ if (gc_is_collecting(AS_GC((PyObject *)wr))) { - /* it should already have been cleared above */ - _PyObject_ASSERT((PyObject*)wr, wr->wr_object == Py_None); continue; } @@ -987,29 +896,24 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks) Py_INCREF(wr); /* Move wr to wrcb_to_call, for the next pass. */ - wrasgc = AS_GC((PyObject *)wr); + PyGC_Head *wrasgc = AS_GC((PyObject *)wr); // wrasgc is reachable, but next isn't, so they can't be the same _PyObject_ASSERT((PyObject *)wr, wrasgc != next); gc_list_move(wrasgc, &wrcb_to_call); } } - if (!allow_callbacks) { - return 0; - } - /* Invoke the callbacks we decided to honor. It's safe to invoke them * because they can't reference unreachable objects. */ - int visited_space = get_gc_state()->visited_space; while (! gc_list_is_empty(&wrcb_to_call)) { PyObject *temp; PyObject *callback; gc = (PyGC_Head*)wrcb_to_call._gc_next; - op = FROM_GC(gc); + PyObject *op = FROM_GC(gc); _PyObject_ASSERT(op, PyWeakref_Check(op)); - wr = (PyWeakReference *)op; + PyWeakReference *wr = (PyWeakReference *)op; callback = wr->wr_callback; _PyObject_ASSERT(op, callback != NULL); @@ -1037,7 +941,6 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks) Py_DECREF(op); if (wrcb_to_call._gc_next == (uintptr_t)gc) { /* object is still alive -- move it */ - gc_set_old_space(gc, visited_space); gc_list_move(gc, old); } else { @@ -1048,6 +951,56 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks) return num_freed; } +/* Clear all weakrefs to unreachable objects. When this returns, no object in + * `unreachable` is weakly referenced anymore. See the comments above + * handle_weakref_callbacks() for why these weakrefs need to be cleared. + */ +static void +clear_weakrefs(PyGC_Head *unreachable) +{ + PyGC_Head *gc; + PyGC_Head *next; + + for (gc = GC_NEXT(unreachable); gc != unreachable; gc = next) { + PyWeakReference **wrlist; + + PyObject *op = FROM_GC(gc); + next = GC_NEXT(gc); + + if (PyWeakref_Check(op)) { + /* A weakref inside the unreachable set is always cleared. See + * the comments above handle_weakref_callbacks() for why these + * must be cleared. + */ + _PyWeakref_ClearRef((PyWeakReference *)op); + } + + if (! _PyType_SUPPORTS_WEAKREFS(Py_TYPE(op))) { + continue; + } + + /* It supports weakrefs. Does it have any? + * + * This is never triggered for static types so we can avoid the + * (slightly) more costly _PyObject_GET_WEAKREFS_LISTPTR(). + */ + wrlist = _PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(op); + + /* `op` may have some weakrefs. March over the list, clear + * all the weakrefs. + */ + for (PyWeakReference *wr = *wrlist; wr != NULL; wr = *wrlist) { + /* _PyWeakref_ClearRef clears the weakref but leaves + * the callback pointer intact. Obscure: it also + * changes *wrlist. + */ + _PyObject_ASSERT((PyObject *)wr, wr->wr_object == op); + _PyWeakref_ClearRef(wr); + _PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None); + } + } +} + static void debug_cycle(const char *msg, PyObject *op) { @@ -1166,6 +1119,25 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate, } +// Show stats for objects in each generations +static void +show_stats_each_generations(GCState *gcstate) +{ + char buf[100]; + size_t pos = 0; + + for (int i = 0; i < NUM_GENERATIONS && pos < sizeof(buf); i++) { + pos += PyOS_snprintf(buf+pos, sizeof(buf)-pos, + " %zd", + gc_list_size(GEN_HEAD(gcstate, i))); + } + + PySys_FormatStderr( + "gc: objects in each generation:%s\n" + "gc: objects in permanent generation: %zd\n", + buf, gc_list_size(&gcstate->permanent_generation.head)); +} + /* Deduce which objects among "base" are unreachable from outside the list and move them to 'unreachable'. The process consist in the following steps: @@ -1193,7 +1165,7 @@ flag set but it does not clear it to skip unnecessary iteration. Before the flag is cleared (for example, by using 'clear_unreachable_mask' function or by a call to 'move_legacy_finalizers'), the 'unreachable' list is not a normal list and we can not use most gc_list_* functions for it. */ -static inline void +static inline Py_ssize_t deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) { validate_list(base, collecting_clear_unreachable_clear); /* Using ob_refcnt and gc_refs, calculate which objects in the @@ -1201,7 +1173,7 @@ deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) { * refcount greater than 0 when all the references within the * set are taken into account). */ - update_refs(base); // gc_prev is used for gc_refs + Py_ssize_t candidates = update_refs(base); // gc_prev is used for gc_refs subtract_refs(base); /* Leave everything reachable from outside base in base, and move @@ -1239,9 +1211,11 @@ deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) { * the reachable objects instead. But this is a one-time cost, probably not * worth complicating the code to speed just a little. */ + gc_list_init(unreachable); move_unreachable(base, unreachable); // gc_prev is pointer again validate_list(base, collecting_clear_unreachable_clear); validate_list(unreachable, collecting_set_unreachable_set); + return candidates; } /* Handle objects that may have resurrected after a call to 'finalize_garbage', moving @@ -1276,446 +1250,283 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable, gc_list_merge(resurrected, old_generation); } -static void -gc_collect_region(PyThreadState *tstate, - PyGC_Head *from, - PyGC_Head *to, - struct gc_collection_stats *stats); - -static inline Py_ssize_t -gc_list_set_space(PyGC_Head *list, int space) -{ - Py_ssize_t size = 0; - PyGC_Head *gc; - for (gc = GC_NEXT(list); gc != list; gc = GC_NEXT(gc)) { - gc_set_old_space(gc, space); - size++; - } - return size; -} -/* Making progress in the incremental collector - * In order to eventually collect all cycles - * the incremental collector must progress through the old - * space faster than objects are added to the old space. - * - * Each young or incremental collection adds a number of - * objects, S (for survivors) to the old space, and - * incremental collectors scan I objects from the old space. - * I > S must be true. We also want I > S * N to be where - * N > 1. Higher values of N mean that the old space is - * scanned more rapidly. - * The default incremental threshold of 10 translates to - * N == 1.4 (1 + 4/threshold) +/* Invoke progress callbacks to notify clients that garbage collection + * is starting or stopping */ - -/* Divide by 10, so that the default incremental threshold of 10 - * scans objects at 1% of the heap size */ -#define SCAN_RATE_DIVISOR 10 - static void -add_stats(GCState *gcstate, int gen, struct gc_collection_stats *stats) +invoke_gc_callback(PyThreadState *tstate, const char *phase, + int generation, struct gc_generation_stats *stats) { - gcstate->generation_stats[gen].collected += stats->collected; - gcstate->generation_stats[gen].uncollectable += stats->uncollectable; - gcstate->generation_stats[gen].collections += 1; -} + assert(!_PyErr_Occurred(tstate)); -static void -gc_collect_young(PyThreadState *tstate, - struct gc_collection_stats *stats) -{ + /* we may get called very early */ GCState *gcstate = &tstate->interp->gc; - validate_spaces(gcstate); - PyGC_Head *young = &gcstate->young.head; - PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; - untrack_tuples(young); - GC_STAT_ADD(0, collections, 1); - - PyGC_Head survivors; - gc_list_init(&survivors); - gc_list_set_space(young, gcstate->visited_space); - gc_collect_region(tstate, young, &survivors, stats); - gc_list_merge(&survivors, visited); - validate_spaces(gcstate); - gcstate->young.count = 0; - gcstate->old[gcstate->visited_space].count++; - add_stats(gcstate, 0, stats); - validate_spaces(gcstate); -} - -#ifndef NDEBUG -static inline int -IS_IN_VISITED(PyGC_Head *gc, int visited_space) -{ - assert(visited_space == 0 || other_space(visited_space) == 0); - return gc_old_space(gc) == visited_space; -} -#endif - -struct container_and_flag { - PyGC_Head *container; - int visited_space; - intptr_t size; -}; - -/* A traversal callback for adding to container) */ -static int -visit_add_to_container(PyObject *op, void *arg) -{ - OBJECT_STAT_INC(object_visits); - struct container_and_flag *cf = (struct container_and_flag *)arg; - int visited = cf->visited_space; - assert(visited == get_gc_state()->visited_space); - if (!_Py_IsImmortal(op) && _PyObject_IS_GC(op)) { - PyGC_Head *gc = AS_GC(op); - if (_PyObject_GC_IS_TRACKED(op) && - gc_old_space(gc) != visited) { - gc_flip_old_space(gc); - gc_list_move(gc, cf->container); - cf->size++; - } + if (gcstate->callbacks == NULL) { + return; } - return 0; -} -static intptr_t -expand_region_transitively_reachable(PyGC_Head *container, PyGC_Head *gc, GCState *gcstate) -{ - struct container_and_flag arg = { - .container = container, - .visited_space = gcstate->visited_space, - .size = 0 - }; - assert(GC_NEXT(gc) == container); - while (gc != container) { - /* Survivors will be moved to visited space, so they should - * have been marked as visited */ - assert(IS_IN_VISITED(gc, gcstate->visited_space)); - PyObject *op = FROM_GC(gc); - assert(_PyObject_GC_IS_TRACKED(op)); - if (_Py_IsImmortal(op)) { - PyGC_Head *next = GC_NEXT(gc); - gc_list_move(gc, &get_gc_state()->permanent_generation.head); - gc = next; - continue; + /* The local variable cannot be rebound, check it for sanity */ + assert(PyList_CheckExact(gcstate->callbacks)); + PyObject *info = NULL; + if (PyList_GET_SIZE(gcstate->callbacks) != 0) { + info = Py_BuildValue("{sisnsnsnsd}", + "generation", generation, + "collected", stats->collected, + "uncollectable", stats->uncollectable, + "candidates", stats->candidates, + "duration", stats->duration); + if (info == NULL) { + PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); + return; } - traverseproc traverse = Py_TYPE(op)->tp_traverse; - (void) traverse(op, - visit_add_to_container, - &arg); - gc = GC_NEXT(gc); } - return arg.size; -} -/* Do bookkeeping for a completed GC cycle */ -static void -completed_scavenge(GCState *gcstate) -{ - /* We must observe two invariants: - * 1. Members of the permanent generation must be marked visited. - * 2. We cannot touch members of the permanent generation. */ - int visited; - if (gc_list_is_empty(&gcstate->permanent_generation.head)) { - /* Permanent generation is empty so we can flip spaces bit */ - int not_visited = gcstate->visited_space; - visited = other_space(not_visited); - gcstate->visited_space = visited; - /* Make sure all objects have visited bit set correctly */ - gc_list_set_space(&gcstate->young.head, not_visited); - } - else { - /* We must move the objects from visited to pending space. */ - visited = gcstate->visited_space; - int not_visited = other_space(visited); - assert(gc_list_is_empty(&gcstate->old[not_visited].head)); - gc_list_merge(&gcstate->old[visited].head, &gcstate->old[not_visited].head); - gc_list_set_space(&gcstate->old[not_visited].head, not_visited); + PyObject *phase_obj = PyUnicode_FromString(phase); + if (phase_obj == NULL) { + Py_XDECREF(info); + PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); + return; } - assert(gc_list_is_empty(&gcstate->old[visited].head)); - gcstate->work_to_do = 0; - gcstate->phase = GC_PHASE_MARK; -} -static intptr_t -move_to_reachable(PyObject *op, PyGC_Head *reachable, int visited_space) -{ - if (op != NULL && !_Py_IsImmortal(op) && _PyObject_IS_GC(op)) { - PyGC_Head *gc = AS_GC(op); - if (_PyObject_GC_IS_TRACKED(op) && - gc_old_space(gc) != visited_space) { - gc_flip_old_space(gc); - gc_list_move(gc, reachable); - return 1; + PyObject *stack[] = {phase_obj, info}; + for (Py_ssize_t i=0; i<PyList_GET_SIZE(gcstate->callbacks); i++) { + PyObject *r, *cb = PyList_GET_ITEM(gcstate->callbacks, i); + Py_INCREF(cb); /* make sure cb doesn't go away */ + r = PyObject_Vectorcall(cb, stack, 2, NULL); + if (r == NULL) { + PyErr_FormatUnraisable("Exception ignored while " + "calling GC callback %R", cb); + } + else { + Py_DECREF(r); } + Py_DECREF(cb); } - return 0; + Py_DECREF(phase_obj); + Py_XDECREF(info); + assert(!_PyErr_Occurred(tstate)); } -static intptr_t -mark_all_reachable(PyGC_Head *reachable, PyGC_Head *visited, int visited_space) -{ - // Transitively traverse all objects from reachable, until empty - struct container_and_flag arg = { - .container = reachable, - .visited_space = visited_space, - .size = 0 - }; - while (!gc_list_is_empty(reachable)) { - PyGC_Head *gc = _PyGCHead_NEXT(reachable); - assert(gc_old_space(gc) == visited_space); - gc_list_move(gc, visited); - PyObject *op = FROM_GC(gc); - traverseproc traverse = Py_TYPE(op)->tp_traverse; - (void) traverse(op, - visit_add_to_container, - &arg); - } - gc_list_validate_space(visited, visited_space); - return arg.size; -} - -static intptr_t -mark_stacks(PyInterpreterState *interp, PyGC_Head *visited, int visited_space, bool start) -{ - PyGC_Head reachable; - gc_list_init(&reachable); - Py_ssize_t objects_marked = 0; - // Move all objects on stacks to reachable - _PyRuntimeState *runtime = &_PyRuntime; - HEAD_LOCK(runtime); - PyThreadState* ts = PyInterpreterState_ThreadHead(interp); - HEAD_UNLOCK(runtime); - while (ts) { - _PyInterpreterFrame *frame = ts->current_frame; - while (frame) { - if (frame->owner >= FRAME_OWNED_BY_INTERPRETER) { - frame = frame->previous; + +/* Find the oldest generation (highest numbered) where the count + * exceeds the threshold. Objects in the that generation and + * generations younger than it will be collected. */ +static int +gc_select_generation(GCState *gcstate) +{ + for (int i = NUM_GENERATIONS-1; i >= 0; i--) { + if (gcstate->generations[i].count > gcstate->generations[i].threshold) { + /* Avoid quadratic performance degradation in number + of tracked objects (see also issue #4074): + + To limit the cost of garbage collection, there are two strategies; + - make each collection faster, e.g. by scanning fewer objects + - do less collections + This heuristic is about the latter strategy. + + In addition to the various configurable thresholds, we only trigger a + full collection if the ratio + + long_lived_pending / long_lived_total + + is above a given value (hardwired to 25%). + + The reason is that, while "non-full" collections (i.e., collections of + the young and middle generations) will always examine roughly the same + number of objects -- determined by the aforementioned thresholds --, + the cost of a full collection is proportional to the total number of + long-lived objects, which is virtually unbounded. + + Indeed, it has been remarked that doing a full collection every + <constant number> of object creations entails a dramatic performance + degradation in workloads which consist in creating and storing lots of + long-lived objects (e.g. building a large list of GC-tracked objects would + show quadratic performance, instead of linear as expected: see issue #4074). + + Using the above ratio, instead, yields amortized linear performance in + the total number of objects (the effect of which can be summarized + thusly: "each full garbage collection is more and more costly as the + number of objects grows, but we do fewer and fewer of them"). + + This heuristic was suggested by Martin von Löwis on python-dev in + June 2008. His original analysis and proposal can be found at: + http://mail.python.org/pipermail/python-dev/2008-June/080579.html + */ + if (i == NUM_GENERATIONS - 1 + && gcstate->long_lived_pending < gcstate->long_lived_total / 4) + { continue; } - _PyStackRef *locals = frame->localsplus; - _PyStackRef *sp = frame->stackpointer; - objects_marked += move_to_reachable(frame->f_locals, &reachable, visited_space); - PyObject *func = PyStackRef_AsPyObjectBorrow(frame->f_funcobj); - objects_marked += move_to_reachable(func, &reachable, visited_space); - while (sp > locals) { - sp--; - if (PyStackRef_IsNullOrInt(*sp)) { - continue; - } - PyObject *op = PyStackRef_AsPyObjectBorrow(*sp); - if (_Py_IsImmortal(op)) { - continue; - } - if (_PyObject_IS_GC(op)) { - PyGC_Head *gc = AS_GC(op); - if (_PyObject_GC_IS_TRACKED(op) && - gc_old_space(gc) != visited_space) { - gc_flip_old_space(gc); - objects_marked++; - gc_list_move(gc, &reachable); - } - } - } - if (!start && frame->visited) { - // If this frame has already been visited, then the lower frames - // will have already been visited and will not have changed - break; - } - frame->visited = 1; - frame = frame->previous; + return i; } - HEAD_LOCK(runtime); - ts = PyThreadState_Next(ts); - HEAD_UNLOCK(runtime); } - objects_marked += mark_all_reachable(&reachable, visited, visited_space); - assert(gc_list_is_empty(&reachable)); - return objects_marked; + return -1; } -static intptr_t -mark_global_roots(PyInterpreterState *interp, PyGC_Head *visited, int visited_space) +static struct gc_generation_stats * +gc_get_stats(GCState *gcstate, int gen) { - PyGC_Head reachable; - gc_list_init(&reachable); - Py_ssize_t objects_marked = 0; - objects_marked += move_to_reachable(interp->sysdict, &reachable, visited_space); - objects_marked += move_to_reachable(interp->builtins, &reachable, visited_space); - objects_marked += move_to_reachable(interp->dict, &reachable, visited_space); - struct types_state *types = &interp->types; - for (int i = 0; i < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES; i++) { - objects_marked += move_to_reachable(types->builtins.initialized[i].tp_dict, &reachable, visited_space); - objects_marked += move_to_reachable(types->builtins.initialized[i].tp_subclasses, &reachable, visited_space); + if (gen == 0) { + struct gc_young_stats_buffer *buffer = &gcstate->generation_stats->young; + buffer->index = (buffer->index + 1) % GC_YOUNG_STATS_SIZE; + struct gc_generation_stats *stats = &buffer->items[buffer->index]; + return stats; } - for (int i = 0; i < _Py_MAX_MANAGED_STATIC_EXT_TYPES; i++) { - objects_marked += move_to_reachable(types->for_extensions.initialized[i].tp_dict, &reachable, visited_space); - objects_marked += move_to_reachable(types->for_extensions.initialized[i].tp_subclasses, &reachable, visited_space); + else { + struct gc_old_stats_buffer *buffer = &gcstate->generation_stats->old[gen - 1]; + buffer->index = (buffer->index + 1) % GC_OLD_STATS_SIZE; + struct gc_generation_stats *stats = &buffer->items[buffer->index]; + return stats; } - objects_marked += mark_all_reachable(&reachable, visited, visited_space); - assert(gc_list_is_empty(&reachable)); - return objects_marked; } -static intptr_t -mark_at_start(PyThreadState *tstate) +static struct gc_generation_stats * +gc_get_prev_stats(GCState *gcstate, int gen) { - // TO DO -- Make this incremental - GCState *gcstate = &tstate->interp->gc; - PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; - Py_ssize_t objects_marked = mark_global_roots(tstate->interp, visited, gcstate->visited_space); - objects_marked += mark_stacks(tstate->interp, visited, gcstate->visited_space, true); - gcstate->work_to_do -= objects_marked; - gcstate->phase = GC_PHASE_COLLECT; - validate_spaces(gcstate); - return objects_marked; -} - -static intptr_t -assess_work_to_do(GCState *gcstate) -{ - /* The amount of work we want to do depends on three things. - * 1. The number of new objects created - * 2. The growth in heap size since the last collection - * 3. The heap size (up to the number of new objects, to avoid quadratic effects) - * - * For a steady state heap, the amount of work to do is three times the number - * of new objects added to the heap. This ensures that we stay ahead in the - * worst case of all new objects being garbage. - * - * This could be improved by tracking survival rates, but it is still a - * large improvement on the non-marking approach. - */ - intptr_t scale_factor = gcstate->old[0].threshold; - if (scale_factor < 2) { - scale_factor = 2; + if (gen == 0) { + struct gc_young_stats_buffer *buffer = &gcstate->generation_stats->young; + struct gc_generation_stats *stats = &buffer->items[buffer->index]; + return stats; } - intptr_t new_objects = gcstate->young.count; - intptr_t max_heap_fraction = new_objects*3/2; - intptr_t heap_fraction = gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor; - if (heap_fraction > max_heap_fraction) { - heap_fraction = max_heap_fraction; + else { + struct gc_old_stats_buffer *buffer = &gcstate->generation_stats->old[gen - 1]; + struct gc_generation_stats *stats = &buffer->items[buffer->index]; + return stats; } - gcstate->young.count = 0; - return new_objects + heap_fraction; } static void -gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats) +add_stats(GCState *gcstate, int gen, struct gc_generation_stats *stats) { - GC_STAT_ADD(1, collections, 1); - GCState *gcstate = &tstate->interp->gc; - gcstate->work_to_do += assess_work_to_do(gcstate); - untrack_tuples(&gcstate->young.head); - if (gcstate->phase == GC_PHASE_MARK) { - Py_ssize_t objects_marked = mark_at_start(tstate); - GC_STAT_ADD(1, objects_transitively_reachable, objects_marked); - gcstate->work_to_do -= objects_marked; - validate_spaces(gcstate); - return; - } - PyGC_Head *not_visited = &gcstate->old[gcstate->visited_space^1].head; - PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; - PyGC_Head increment; - gc_list_init(&increment); - int scale_factor = gcstate->old[0].threshold; - if (scale_factor < 2) { - scale_factor = 2; - } - intptr_t objects_marked = mark_stacks(tstate->interp, visited, gcstate->visited_space, false); - GC_STAT_ADD(1, objects_transitively_reachable, objects_marked); - gcstate->work_to_do -= objects_marked; - gc_list_set_space(&gcstate->young.head, gcstate->visited_space); - gc_list_merge(&gcstate->young.head, &increment); - gc_list_validate_space(&increment, gcstate->visited_space); - Py_ssize_t increment_size = gc_list_size(&increment); - while (increment_size < gcstate->work_to_do) { - if (gc_list_is_empty(not_visited)) { - break; - } - PyGC_Head *gc = _PyGCHead_NEXT(not_visited); - gc_list_move(gc, &increment); - increment_size++; - assert(!_Py_IsImmortal(FROM_GC(gc))); - gc_set_old_space(gc, gcstate->visited_space); - increment_size += expand_region_transitively_reachable(&increment, gc, gcstate); - } - GC_STAT_ADD(1, objects_not_transitively_reachable, increment_size); - validate_list(&increment, collecting_clear_unreachable_clear); - gc_list_validate_space(&increment, gcstate->visited_space); - PyGC_Head survivors; - gc_list_init(&survivors); - gc_collect_region(tstate, &increment, &survivors, stats); - gc_list_merge(&survivors, visited); - assert(gc_list_is_empty(&increment)); - gcstate->work_to_do += gcstate->heap_size / SCAN_RATE_DIVISOR / scale_factor; - gcstate->work_to_do -= increment_size; - - add_stats(gcstate, 1, stats); - if (gc_list_is_empty(not_visited)) { - completed_scavenge(gcstate); - } - validate_spaces(gcstate); + struct gc_generation_stats *prev_stats = gc_get_prev_stats(gcstate, gen); + struct gc_generation_stats *cur_stats = gc_get_stats(gcstate, gen); + + memcpy(cur_stats, prev_stats, sizeof(struct gc_generation_stats)); + + cur_stats->ts_start = stats->ts_start; + cur_stats->collections += 1; + cur_stats->collected += stats->collected; + cur_stats->uncollectable += stats->uncollectable; + cur_stats->candidates += stats->candidates; + + cur_stats->duration += stats->duration; + cur_stats->heap_size = stats->heap_size; + /* Publish ts_stop last so remote readers do not select a partially + updated stats record as the latest collection. */ + cur_stats->ts_stop = stats->ts_stop; } -static void -gc_collect_full(PyThreadState *tstate, - struct gc_collection_stats *stats) -{ - GC_STAT_ADD(2, collections, 1); - GCState *gcstate = &tstate->interp->gc; - validate_spaces(gcstate); - PyGC_Head *young = &gcstate->young.head; - PyGC_Head *pending = &gcstate->old[gcstate->visited_space^1].head; - PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head; - untrack_tuples(young); - /* merge all generations into visited */ - gc_list_merge(young, pending); - gc_list_validate_space(pending, 1-gcstate->visited_space); - gc_list_set_space(pending, gcstate->visited_space); - gcstate->young.count = 0; - gc_list_merge(pending, visited); - validate_spaces(gcstate); - - gc_collect_region(tstate, visited, visited, - stats); - validate_spaces(gcstate); - gcstate->young.count = 0; - gcstate->old[0].count = 0; - gcstate->old[1].count = 0; - completed_scavenge(gcstate); - _PyGC_ClearAllFreeLists(tstate->interp); - validate_spaces(gcstate); - add_stats(gcstate, 2, stats); -} - -/* This is the main function. Read this to understand how the +/* This is the main function. Read this to understand how the * collection process works. */ -static void -gc_collect_region(PyThreadState *tstate, - PyGC_Head *from, - PyGC_Head *to, - struct gc_collection_stats *stats) +static Py_ssize_t +gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) { + int i; + PyGC_Head *young; /* the generation we are examining */ + PyGC_Head *old; /* next older generation */ PyGC_Head unreachable; /* non-problematic unreachable trash */ PyGC_Head finalizers; /* objects with, & reachable from, __del__ */ - PyGC_Head *gc; /* initialize to prevent a compiler warning */ + PyGC_Head *gc; GCState *gcstate = &tstate->interp->gc; + // gc_collect_main() must not be called before _PyGC_Init + // or after _PyGC_Fini() assert(gcstate->garbage != NULL); assert(!_PyErr_Occurred(tstate)); - gc_list_init(&unreachable); - deduce_unreachable(from, &unreachable); - validate_consistent_old_space(from); - untrack_tuples(from); - validate_consistent_old_space(to); - if (from != to) { - gc_list_merge(from, to); + int expected = 0; + if (!_Py_atomic_compare_exchange_int(&gcstate->collecting, &expected, 1)) { + // Don't start a garbage collection if one is already in progress. + return 0; + } + gcstate->frame = tstate->current_frame; + + if (generation == GENERATION_AUTO) { + // Select the oldest generation that needs collecting. We will collect + // objects from that generation and all generations younger than it. + generation = gc_select_generation(gcstate); + if (generation < 0) { + // No generation needs to be collected. + _Py_atomic_store_int(&gcstate->collecting, 0); + return 0; + } } - validate_consistent_old_space(to); + + assert(generation >= 0 && generation < NUM_GENERATIONS); + +#ifdef Py_STATS + { + PyStats *s = _PyStats_GET(); + if (s) { + s->object_stats.object_visits = 0; + } + } +#endif + + GC_STAT_ADD(generation, collections, 1); + + struct gc_generation_stats stats = { 0 }; + if (reason != _Py_GC_REASON_SHUTDOWN) { + invoke_gc_callback(tstate, "start", generation, &stats); + } + + stats.heap_size = gcstate->heap_size; + // ignore error: don't interrupt the GC if reading the clock fails + (void)PyTime_PerfCounterRaw(&stats.ts_start); + if (gcstate->debug & _PyGC_DEBUG_STATS) { + PySys_WriteStderr("gc: collecting generation %d...\n", generation); + show_stats_each_generations(gcstate); + } + + if (PyDTrace_GC_START_ENABLED()) { + PyDTrace_GC_START(generation); + } + + /* update collection and allocation counters */ + if (generation+1 < NUM_GENERATIONS) { + gcstate->generations[generation+1].count += 1; + } + for (i = 0; i <= generation; i++) { + gcstate->generations[i].count = 0; + } + + /* merge younger generations with one we are currently collecting */ + for (i = 0; i < generation; i++) { + gc_list_merge(GEN_HEAD(gcstate, i), GEN_HEAD(gcstate, generation)); + } + + /* handy references */ + young = GEN_HEAD(gcstate, generation); + if (generation < NUM_GENERATIONS-1) { + old = GEN_HEAD(gcstate, generation+1); + } + else { + old = young; + } + validate_list(old, collecting_clear_unreachable_clear); + + stats.candidates = deduce_unreachable(young, &unreachable); + + untrack_tuples(young); /* Move reachable objects to next generation. */ + if (young != old) { + if (generation == NUM_GENERATIONS - 2) { + gcstate->long_lived_pending += gc_list_size(young); + } + gc_list_merge(young, old); + } + else { + // In Python <= 3.13, we called untrack_dicts(young) here to untrack + // atomic-only dicts (see issue #14775). Python 3.14 removed the lazy + // dict tracking machinery entirely (GH-127010) -- dicts are always + // tracked from creation and never untracked by GC. That way, we don't + // have to restore MAINTAIN_TRACKING across every PyDict_SetItem call + // site; the cost is slightly more work for full collections on dicts + // with only atomic values. + gcstate->long_lived_pending = 0; + gcstate->long_lived_total = gc_list_size(young); + } /* All objects in unreachable are trash, but objects reachable from * legacy finalizers (e.g. tp_del) can't safely be deleted. @@ -1729,8 +1540,10 @@ gc_collect_region(PyThreadState *tstate, * and we move those into the finalizers list too. */ move_legacy_finalizer_reachable(&finalizers); + validate_list(&finalizers, collecting_clear_unreachable_clear); validate_list(&unreachable, collecting_set_unreachable_clear); + /* Print debugging information. */ if (gcstate->debug & _PyGC_DEBUG_COLLECTABLE) { for (gc = GC_NEXT(&unreachable); gc != &unreachable; gc = GC_NEXT(gc)) { @@ -1739,19 +1552,18 @@ gc_collect_region(PyThreadState *tstate, } /* Clear weakrefs and invoke callbacks as necessary. */ - stats->collected += handle_weakrefs(&unreachable, to, true); - gc_list_validate_space(to, gcstate->visited_space); - validate_list(to, collecting_clear_unreachable_clear); + stats.collected += handle_weakref_callbacks(&unreachable, old); + validate_list(old, collecting_clear_unreachable_clear); validate_list(&unreachable, collecting_set_unreachable_clear); /* Call tp_finalize on objects which have one. */ finalize_garbage(tstate, &unreachable); + /* Handle any objects that may have resurrected after the call * to 'finalize_garbage' and continue the collection with the * objects that are still unreachable */ PyGC_Head final_unreachable; - gc_list_init(&final_unreachable); - handle_resurrected_objects(&unreachable, &final_unreachable, to); + handle_resurrected_objects(&unreachable, &final_unreachable, old); /* Clear weakrefs to objects in the unreachable set. No Python-level * code must be allowed to access those unreachable objects. During @@ -1759,14 +1571,14 @@ gc_collect_region(PyThreadState *tstate, * and create new weakrefs. If those weakrefs were not cleared, they * could reveal unreachable objects. Callbacks are not executed. */ - handle_weakrefs(&final_unreachable, NULL, false); + clear_weakrefs(&final_unreachable); /* Call tp_clear on objects in the final_unreachable set. This will cause * the reference cycles to be broken. It may also cause some objects * in finalizers to be freed. */ - stats->collected += gc_list_size(&final_unreachable); - delete_garbage(tstate, gcstate, &final_unreachable, to); + stats.collected += gc_list_size(&final_unreachable); + delete_garbage(tstate, gcstate, &final_unreachable, old); /* Collect statistics on uncollectable objects found and print * debugging information. */ @@ -1776,73 +1588,65 @@ gc_collect_region(PyThreadState *tstate, if (gcstate->debug & _PyGC_DEBUG_UNCOLLECTABLE) debug_cycle("uncollectable", FROM_GC(gc)); } - stats->uncollectable = n; + stats.uncollectable = n; + (void)PyTime_PerfCounterRaw(&stats.ts_stop); + stats.duration = PyTime_AsSecondsDouble(stats.ts_stop - stats.ts_start); + if (gcstate->debug & _PyGC_DEBUG_STATS) { + PySys_WriteStderr( + "gc: done, %zd unreachable, %zd uncollectable, %.4fs elapsed\n", + stats.uncollectable+stats.collected, stats.uncollectable, + stats.duration); + } + /* Append instances in the uncollectable set to a Python * reachable list of garbage. The programmer has to deal with * this if they insist on creating this type of structure. */ - handle_legacy_finalizers(tstate, gcstate, &finalizers, to); - gc_list_validate_space(to, gcstate->visited_space); - validate_list(to, collecting_clear_unreachable_clear); -} + handle_legacy_finalizers(tstate, gcstate, &finalizers, old); + validate_list(old, collecting_clear_unreachable_clear); -/* Invoke progress callbacks to notify clients that garbage collection - * is starting or stopping - */ -static void -do_gc_callback(GCState *gcstate, const char *phase, - int generation, struct gc_collection_stats *stats) -{ - assert(!PyErr_Occurred()); + /* Clear free list only during the collection of the highest + * generation */ + if (generation == NUM_GENERATIONS-1) { + _PyGC_ClearAllFreeLists(tstate->interp); + } - /* The local variable cannot be rebound, check it for sanity */ - assert(PyList_CheckExact(gcstate->callbacks)); - PyObject *info = NULL; - if (PyList_GET_SIZE(gcstate->callbacks) != 0) { - info = Py_BuildValue("{sisnsn}", - "generation", generation, - "collected", stats->collected, - "uncollectable", stats->uncollectable); - if (info == NULL) { - PyErr_FormatUnraisable("Exception ignored while invoking gc callbacks"); - return; + if (_PyErr_Occurred(tstate)) { + if (reason == _Py_GC_REASON_SHUTDOWN) { + _PyErr_Clear(tstate); + } + else { + PyErr_FormatUnraisable("Exception ignored in garbage collection"); } } - PyObject *phase_obj = PyUnicode_FromString(phase); - if (phase_obj == NULL) { - Py_XDECREF(info); - PyErr_FormatUnraisable("Exception ignored while invoking gc callbacks"); - return; - } + /* Update stats */ + add_stats(gcstate, generation, &stats); + GC_STAT_ADD(generation, objects_collected, stats.collected); - PyObject *stack[] = {phase_obj, info}; - for (Py_ssize_t i=0; i<PyList_GET_SIZE(gcstate->callbacks); i++) { - PyObject *r, *cb = PyList_GET_ITEM(gcstate->callbacks, i); - Py_INCREF(cb); /* make sure cb doesn't go away */ - r = PyObject_Vectorcall(cb, stack, 2, NULL); - if (r == NULL) { - PyErr_FormatUnraisable("Exception ignored while " - "calling GC callback %R", cb); - } - else { - Py_DECREF(r); +#ifdef Py_STATS + { + PyStats *s = _PyStats_GET(); + if (s) { + GC_STAT_ADD(generation, object_visits, + s->object_stats.object_visits); + s->object_stats.object_visits = 0; } - Py_DECREF(cb); } - Py_DECREF(phase_obj); - Py_XDECREF(info); - assert(!PyErr_Occurred()); -} +#endif -static void -invoke_gc_callback(GCState *gcstate, const char *phase, - int generation, struct gc_collection_stats *stats) -{ - if (gcstate->callbacks == NULL) { - return; + if (PyDTrace_GC_DONE_ENABLED()) { + PyDTrace_GC_DONE(stats.uncollectable + stats.collected); + } + + if (reason != _Py_GC_REASON_SHUTDOWN) { + invoke_gc_callback(tstate, "stop", generation, &stats); } - do_gc_callback(gcstate, phase, generation, stats); + + assert(!_PyErr_Occurred(tstate)); + gcstate->frame = NULL; + _Py_atomic_store_int(&gcstate->collecting, 0); + return stats.uncollectable + stats.collected; } static int @@ -1904,25 +1708,20 @@ _PyGC_GetObjects(PyInterpreterState *interp, int generation) GCState *gcstate = &interp->gc; PyObject *result = PyList_New(0); - /* Generation: - * -1: Return all objects - * 0: All young objects - * 1: No objects - * 2: All old objects - */ - if (result == NULL || generation == 1) { - return result; + if (result == NULL) { + return NULL; } - if (generation <= 0) { - if (append_objects(result, &gcstate->young.head)) { - goto error; + + if (generation == -1) { + /* If generation is -1, get all objects from all generations */ + for (int i = 0; i < NUM_GENERATIONS; i++) { + if (append_objects(result, GEN_HEAD(gcstate, i))) { + goto error; + } } } - if (generation != 0) { - if (append_objects(result, &gcstate->old[0].head)) { - goto error; - } - if (append_objects(result, &gcstate->old[1].head)) { + else { + if (append_objects(result, GEN_HEAD(gcstate, generation))) { goto error; } } @@ -1937,23 +1736,10 @@ void _PyGC_Freeze(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; - /* The permanent_generation must be visited */ - gc_list_set_space(&gcstate->young.head, gcstate->visited_space); - gc_list_merge(&gcstate->young.head, &gcstate->permanent_generation.head); - gcstate->young.count = 0; - PyGC_Head*old0 = &gcstate->old[0].head; - PyGC_Head*old1 = &gcstate->old[1].head; - if (gcstate->visited_space) { - gc_list_set_space(old0, 1); - } - else { - gc_list_set_space(old1, 0); + for (int i = 0; i < NUM_GENERATIONS; ++i) { + gc_list_merge(GEN_HEAD(gcstate, i), &gcstate->permanent_generation.head); + gcstate->generations[i].count = 0; } - gc_list_merge(old0, &gcstate->permanent_generation.head); - gcstate->old[0].count = 0; - gc_list_merge(old1, &gcstate->permanent_generation.head); - gcstate->old[1].count = 0; - validate_spaces(gcstate); } void @@ -1961,8 +1747,7 @@ _PyGC_Unfreeze(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; gc_list_merge(&gcstate->permanent_generation.head, - &gcstate->old[gcstate->visited_space].head); - validate_spaces(gcstate); + GEN_HEAD(gcstate, NUM_GENERATIONS-1)); } Py_ssize_t @@ -1998,86 +1783,29 @@ PyGC_IsEnabled(void) return gcstate->enabled; } -// Show stats for objects in each generations -static void -show_stats_each_generations(GCState *gcstate) -{ - char buf[100]; - size_t pos = 0; - - for (int i = 0; i < NUM_GENERATIONS && pos < sizeof(buf); i++) { - pos += PyOS_snprintf(buf+pos, sizeof(buf)-pos, - " %zd", - gc_list_size(GEN_HEAD(gcstate, i))); - } - PySys_FormatStderr( - "gc: objects in each generation:%s\n" - "gc: objects in permanent generation: %zd\n", - buf, gc_list_size(&gcstate->permanent_generation.head)); -} - +/* Public API to invoke gc.collect() from C */ Py_ssize_t -_PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason) +PyGC_Collect(void) { + PyThreadState *tstate = _PyThreadState_GET(); GCState *gcstate = &tstate->interp->gc; - assert(tstate->current_frame == NULL || tstate->current_frame->stackpointer != NULL); - int expected = 0; - if (!_Py_atomic_compare_exchange_int(&gcstate->collecting, &expected, 1)) { - // Don't start a garbage collection if one is already in progress. + if (!gcstate->enabled) { return 0; } - struct gc_collection_stats stats = { 0 }; - if (reason != _Py_GC_REASON_SHUTDOWN) { - invoke_gc_callback(gcstate, "start", generation, &stats); - } - if (gcstate->debug & _PyGC_DEBUG_STATS) { - PySys_WriteStderr("gc: collecting generation %d...\n", generation); - show_stats_each_generations(gcstate); - } - if (PyDTrace_GC_START_ENABLED()) { - PyDTrace_GC_START(generation); - } + Py_ssize_t n; PyObject *exc = _PyErr_GetRaisedException(tstate); - switch(generation) { - case 0: - gc_collect_young(tstate, &stats); - break; - case 1: - gc_collect_increment(tstate, &stats); - break; - case 2: - gc_collect_full(tstate, &stats); - break; - default: - Py_UNREACHABLE(); - } - if (PyDTrace_GC_DONE_ENABLED()) { - PyDTrace_GC_DONE(stats.uncollectable + stats.collected); - } - if (reason != _Py_GC_REASON_SHUTDOWN) { - invoke_gc_callback(gcstate, "stop", generation, &stats); - } + n = gc_collect_main(tstate, NUM_GENERATIONS - 1, _Py_GC_REASON_MANUAL); _PyErr_SetRaisedException(tstate, exc); - GC_STAT_ADD(generation, objects_collected, stats.collected); -#ifdef Py_STATS - if (_Py_stats) { - GC_STAT_ADD(generation, object_visits, - _Py_stats->object_stats.object_visits); - _Py_stats->object_stats.object_visits = 0; - } -#endif - validate_spaces(gcstate); - _Py_atomic_store_int(&gcstate->collecting, 0); - return stats.uncollectable + stats.collected; + + return n; } -/* Public API to invoke gc.collect() from C */ Py_ssize_t -PyGC_Collect(void) +_PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason) { - return _PyGC_Collect(_PyThreadState_GET(), 2, _Py_GC_REASON_MANUAL); + return gc_collect_main(tstate, generation, reason); } void @@ -2089,7 +1817,7 @@ _PyGC_CollectNoFail(PyThreadState *tstate) during interpreter shutdown (and then never finish it). See http://bugs.python.org/issue8713#msg195178 for an example. */ - _PyGC_Collect(_PyThreadState_GET(), 2, _Py_GC_REASON_SHUTDOWN); + gc_collect_main(tstate, NUM_GENERATIONS - 1, _Py_GC_REASON_SHUTDOWN); } void @@ -2148,6 +1876,8 @@ _PyGC_Fini(PyInterpreterState *interp) GCState *gcstate = &interp->gc; Py_CLEAR(gcstate->garbage); Py_CLEAR(gcstate->callbacks); + PyMem_RawFree(gcstate->generation_stats); + gcstate->generation_stats = NULL; /* Prevent a subtle bug that affects sub-interpreters that use basic * single-phase init extensions (m_size == -1). Those extensions cause objects @@ -2164,9 +1894,9 @@ _PyGC_Fini(PyInterpreterState *interp) * This bug was originally fixed when reported as gh-90228. The bug was * re-introduced in gh-94673. */ - finalize_unlink_gc_head(&gcstate->young.head); - finalize_unlink_gc_head(&gcstate->old[0].head); - finalize_unlink_gc_head(&gcstate->old[1].head); + for (int i = 0; i < NUM_GENERATIONS; i++) { + finalize_unlink_gc_head(&gcstate->generations[i].head); + } finalize_unlink_gc_head(&gcstate->permanent_generation.head); } @@ -2174,7 +1904,7 @@ _PyGC_Fini(PyInterpreterState *interp) void _PyGC_Dump(PyGC_Head *g) { - _PyObject_Dump(FROM_GC(g)); + PyObject_Dump(FROM_GC(g)); } @@ -2251,11 +1981,10 @@ _PyObject_GC_Link(PyObject *op) GCState *gcstate = &tstate->interp->gc; gc->_gc_next = 0; gc->_gc_prev = 0; - gcstate->young.count++; /* number of allocated GC objects */ - gcstate->heap_size++; - if (gcstate->young.count > gcstate->young.threshold && + gcstate->generations[0].count++; /* number of allocated GC objects */ + if (gcstate->generations[0].count > gcstate->generations[0].threshold && gcstate->enabled && - gcstate->young.threshold && + gcstate->generations[0].threshold && !_Py_atomic_load_int_relaxed(&gcstate->collecting) && !_PyErr_Occurred(tstate)) { @@ -2266,9 +1995,11 @@ _PyObject_GC_Link(PyObject *op) void _Py_RunGC(PyThreadState *tstate) { - if (tstate->interp->gc.enabled) { - _PyGC_Collect(tstate, 1, _Py_GC_REASON_HEAP); + GCState *gcstate = get_gc_state(); + if (!gcstate->enabled) { + return; } + gc_collect_main(tstate, GENERATION_AUTO, _Py_GC_REASON_HEAP); } static PyObject * @@ -2369,6 +2100,8 @@ PyObject_GC_Del(void *op) PyGC_Head *g = AS_GC(op); if (_PyObject_GC_IS_TRACKED(op)) { gc_list_remove(g); + GCState *gcstate = get_gc_state(); + gcstate->heap_size--; #ifdef Py_DEBUG PyObject *exc = PyErr_GetRaisedException(); if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0, @@ -2383,10 +2116,9 @@ PyObject_GC_Del(void *op) #endif } GCState *gcstate = get_gc_state(); - if (gcstate->young.count > 0) { - gcstate->young.count--; + if (gcstate->generations[0].count > 0) { + gcstate->generations[0].count--; } - gcstate->heap_size--; PyObject_Free(((char *)op)-presize); } @@ -2431,18 +2163,14 @@ PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) GCState *gcstate = get_gc_state(); int original_state = gcstate->enabled; gcstate->enabled = 0; - if (visit_generation(callback, arg, &gcstate->young) < 0) { - goto done; - } - if (visit_generation(callback, arg, &gcstate->old[0]) < 0) { - goto done; - } - if (visit_generation(callback, arg, &gcstate->old[1]) < 0) { - goto done; + for (size_t i = 0; i < NUM_GENERATIONS; i++) { + if (visit_generation(callback, arg, &gcstate->generations[i]) < 0) { + goto done; + } } visit_generation(callback, arg, &gcstate->permanent_generation); done: gcstate->enabled = original_state; } -#endif // Py_GIL_DISABLED +#endif // !Py_GIL_DISABLED diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 0b0ddf227e4952..4e36189580bbf8 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -17,30 +17,6 @@ #include "pydtrace.h" -// Platform-specific includes for get_process_mem_usage(). -#ifdef _WIN32 - #include <windows.h> - #include <psapi.h> // For GetProcessMemoryInfo -#elif defined(__linux__) - #include <unistd.h> // For sysconf, getpid -#elif defined(__APPLE__) - #include <mach/mach.h> - #include <mach/task.h> // Required for TASK_VM_INFO - #include <unistd.h> // For sysconf, getpid -#elif defined(__FreeBSD__) - #include <sys/types.h> - #include <sys/sysctl.h> - #include <sys/user.h> // Requires sys/user.h for kinfo_proc definition - #include <kvm.h> - #include <unistd.h> // For sysconf, getpid - #include <fcntl.h> // For O_RDONLY - #include <limits.h> // For _POSIX2_LINE_MAX -#elif defined(__OpenBSD__) - #include <sys/types.h> - #include <sys/sysctl.h> - #include <sys/user.h> // For kinfo_proc - #include <unistd.h> // For sysconf, getpid -#endif // enable the "mark alive" pass of GC #define GC_ENABLE_MARK_ALIVE 1 @@ -100,6 +76,7 @@ struct collection_state { int skip_deferred_objects; Py_ssize_t collected; Py_ssize_t uncollectable; + Py_ssize_t candidates; Py_ssize_t long_lived_total; struct worklist unreachable; struct worklist legacy_finalizers; @@ -289,7 +266,7 @@ frame_disable_deferred_refcounting(_PyInterpreterFrame *frame) frame->f_funcobj = PyStackRef_AsStrongReference(frame->f_funcobj); for (_PyStackRef *ref = frame->localsplus; ref < frame->stackpointer; ref++) { - if (!PyStackRef_IsNullOrInt(*ref) && PyStackRef_IsDeferred(*ref)) { + if (!PyStackRef_IsNullOrInt(*ref) && !PyStackRef_RefcountOnObject(*ref)) { *ref = PyStackRef_AsStrongReference(*ref); } } @@ -374,6 +351,19 @@ op_from_block(void *block, void *arg, bool include_frozen) return op; } +// As above but returns untracked and frozen objects as well. +static PyObject * +op_from_block_all_gc(void *block, void *arg) +{ + struct visitor_args *a = arg; + if (block == NULL) { + return NULL; + } + PyObject *op = (PyObject *)((char*)block + a->offset); + assert(PyObject_IS_GC(op)); + return op; +} + static int gc_visit_heaps_lock_held(PyInterpreterState *interp, mi_block_visit_fun *visitor, struct visitor_args *arg) @@ -444,7 +434,7 @@ gc_visit_heaps(PyInterpreterState *interp, mi_block_visit_fun *visitor, static inline void gc_visit_stackref(_PyStackRef stackref) { - if (PyStackRef_IsDeferred(stackref) && !PyStackRef_IsNullOrInt(stackref)) { + if (!PyStackRef_IsNullOrInt(stackref) && !PyStackRef_RefcountOnObject(stackref)) { PyObject *obj = PyStackRef_AsPyObjectBorrow(stackref); if (_PyObject_GC_IS_TRACKED(obj) && !gc_is_frozen(obj)) { gc_add_refs(obj, 1); @@ -492,6 +482,10 @@ gc_visit_thread_stacks(PyInterpreterState *interp, struct collection_state *stat static bool gc_maybe_untrack(PyObject *op) { + if (_PyObject_HasDeferredRefcount(op)) { + // deferred refcounting only works if the object is tracked + return false; + } // Currently we only check for tuples containing only non-GC objects. In // theory we could check other immutable objects that contain references // to non-GC objects. @@ -675,10 +669,11 @@ gc_mark_span_push(gc_span_stack_t *ss, PyObject **start, PyObject **end) else { ss->capacity *= 2; } - ss->stack = (gc_span_t *)PyMem_Realloc(ss->stack, ss->capacity * sizeof(gc_span_t)); - if (ss->stack == NULL) { + gc_span_t *new_stack = (gc_span_t *)PyMem_Realloc(ss->stack, ss->capacity * sizeof(gc_span_t)); + if (new_stack == NULL) { return -1; } + ss->stack = new_stack; } assert(end > start); ss->stack[ss->size].start = start; @@ -891,7 +886,11 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, gc_mark_args_t *ar static void queue_untracked_obj_decref(PyObject *op, struct collection_state *state) { - if (!_PyObject_GC_IS_TRACKED(op)) { + assert(Py_REFCNT(op) == 0); + // gh-142975: We have to treat frozen objects as untracked in this function + // or else they might be picked up in a future collection, which breaks the + // assumption that all incoming objects have a non-zero reference count. + if (!_PyObject_GC_IS_TRACKED(op) || gc_is_frozen(op)) { // GC objects with zero refcount are handled subsequently by the // GC as if they were cyclic trash, but we have to handle dead // non-GC objects here. Add one to the refcount so that we can @@ -974,15 +973,12 @@ static bool update_refs(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { + struct collection_state *state = (struct collection_state *)args; PyObject *op = op_from_block(block, args, false); if (op == NULL) { return true; } - if (gc_is_alive(op)) { - return true; - } - // Exclude immortal objects from garbage collection if (_Py_IsImmortal(op)) { op->ob_tid = 0; @@ -990,6 +986,11 @@ update_refs(const mi_heap_t *heap, const mi_heap_area_t *area, gc_clear_unreachable(op); return true; } + // Marked objects count as candidates, immortals don't: + state->candidates++; + if (gc_is_alive(op)) { + return true; + } Py_ssize_t refcount = Py_REFCNT(op); if (_PyObject_HasDeferredRefcount(op)) { @@ -997,7 +998,7 @@ update_refs(const mi_heap_t *heap, const mi_heap_area_t *area, } _PyObject_ASSERT(op, refcount >= 0); - if (refcount > 0 && !_PyObject_HasDeferredRefcount(op)) { + if (refcount > 0) { if (gc_maybe_untrack(op)) { gc_restore_refs(op); return true; @@ -1182,12 +1183,20 @@ static bool scan_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { - PyObject *op = op_from_block(block, args, false); + PyObject *op = op_from_block_all_gc(block, args); if (op == NULL) { return true; } - struct collection_state *state = (struct collection_state *)args; + // The free-threaded GC cost is proportional to the number of objects in + // the mimalloc GC heap and so we should include the counts for untracked + // and frozen objects as well. This is especially important if many + // tuples have been untracked. + state->long_lived_total++; + if (!_PyObject_GC_IS_TRACKED(op) || gc_is_frozen(op)) { + return true; + } + if (gc_is_unreachable(op)) { // Disable deferred refcounting for unreachable objects so that they // are collected immediately after finalization. @@ -1205,6 +1214,9 @@ scan_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area, else { worklist_push(&state->unreachable, op); } + // It is possible this object will be resurrected but + // for now we assume it will be deallocated. + state->long_lived_total--; return true; } @@ -1218,7 +1230,6 @@ scan_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area, // object is reachable, restore `ob_tid`; we're done with these objects gc_restore_tid(op); gc_clear_alive(op); - state->long_lived_total++; return true; } @@ -1492,22 +1503,49 @@ move_legacy_finalizer_reachable(struct collection_state *state) return 0; } -// Clear all weakrefs to unreachable objects. Weakrefs with callbacks are -// optionally enqueued in `wrcb_to_call`, but not invoked yet. +// Weakrefs with callbacks are enqueued in `wrcb_to_call`, but not invoked +// yet. Note that it's possible for such weakrefs to be outside the +// unreachable set -- indeed, those are precisely the weakrefs whose callbacks +// must be invoked. See gc_weakref.txt for overview & some details. +// +// The clearing of weakrefs is suble and must be done carefully, as there was +// previous bugs related to this. First, weakrefs to the unreachable set of +// objects must be cleared before we start calling `tp_clear`. If we don't, +// those weakrefs can reveal unreachable objects to Python-level code and that +// is not safe. Many objects are not usable after `tp_clear` has been called +// and could even cause crashes if accessed (see bpo-38006 and gh-91636 as +// example bugs). +// +// Weakrefs with callbacks always need to be cleared before executing the +// callback. That's because sometimes a callback will call the ref object, +// to check if the reference is actually dead (KeyedRef does this, for +// example). We want to indicate that it is dead, even though it is possible +// a finalizer might resurrect it. Clearing also prevents the callback from +// executing more than once. +// +// Since Python 2.3, all weakrefs to cyclic garbage have been cleared *before* +// calling finalizers. However, since tp_subclasses started being necessary +// to invalidate caches (e.g. by PyType_Modified), that clearing has created +// a bug. If the weakref to the subclass is cleared before a finalizer is +// called, the cache may not be correctly invalidated. That can lead to +// segfaults since the caches can refer to deallocated objects (GH-91636 +// is an example). Now, we delay the clear of weakrefs without callbacks +// until *after* finalizers have been executed. That means weakrefs without +// callbacks are still usable while finalizers are being executed. +// +// The weakrefs that are inside the unreachable set must also be cleared. +// The reason this is required is not immediately obvious. If the weakref +// refers to an object outside of the unreachable set, that object might go +// away when we start clearing objects. Normally, the object should also be +// part of the unreachable set but that's not true in the case of incomplete +// or missing `tp_traverse` methods. When that object goes away, the callback +// for weakref can be executed and that could reveal unreachable objects to +// Python-level code. See bpo-38006 as an example bug. static void -clear_weakrefs(struct collection_state *state, bool enqueue_callbacks) +find_weakref_callbacks(struct collection_state *state) { PyObject *op; WORKSTACK_FOR_EACH(&state->unreachable, op) { - if (PyWeakref_Check(op)) { - // Clear weakrefs that are themselves unreachable to ensure their - // callbacks will not be executed later from a `tp_clear()` - // inside delete_garbage(). That would be unsafe: it could - // resurrect a dead object or access a an already cleared object. - // See bpo-38006 for one example. - _PyWeakref_ClearRef((PyWeakReference *)op); - } - if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(op))) { continue; } @@ -1519,16 +1557,21 @@ clear_weakrefs(struct collection_state *state, bool enqueue_callbacks) // `op` may have some weakrefs. March over the list, clear // all the weakrefs, and enqueue the weakrefs with callbacks // that must be called into wrcb_to_call. - for (PyWeakReference *wr = *wrlist; wr != NULL; wr = *wrlist) { - // _PyWeakref_ClearRef clears the weakref but leaves - // the callback pointer intact. Obscure: it also - // changes *wrlist. - _PyObject_ASSERT((PyObject *)wr, wr->wr_object == op); - _PyWeakref_ClearRef(wr); - _PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None); - - if (!enqueue_callbacks) { - continue; + PyWeakReference *next_wr; + for (PyWeakReference *wr = *wrlist; wr != NULL; wr = next_wr) { + // Get the next list element to get iterator progress if we omit + // clearing of the weakref (because _PyWeakref_ClearRef changes + // next pointer in the wrlist). + next_wr = wr->wr_next; + + // Weakrefs with callbacks always need to be cleared before + // executing the callback. + if (wr->wr_callback != NULL) { + // _PyWeakref_ClearRef clears the weakref but leaves the + // callback pointer intact. Obscure: it also changes *wrlist. + _PyObject_ASSERT((PyObject *)wr, wr->wr_object == op); + _PyWeakref_ClearRef(wr); + _PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None); } // We do not invoke callbacks for weakrefs that are themselves @@ -1550,6 +1593,39 @@ clear_weakrefs(struct collection_state *state, bool enqueue_callbacks) } } +// Clear weakrefs to objects in the unreachable set. See comments +// above find_weakref_callbacks() for why this clearing is required. +static void +clear_weakrefs(struct collection_state *state) +{ + PyObject *op; + WORKSTACK_FOR_EACH(&state->unreachable, op) { + if (PyWeakref_Check(op)) { + // Clear weakrefs that are themselves unreachable. + _PyWeakref_ClearRef((PyWeakReference *)op); + } + + if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(op))) { + continue; + } + + // NOTE: This is never triggered for static types so we can avoid the + // (slightly) more costly _PyObject_GET_WEAKREFS_LISTPTR(). + PyWeakReference **wrlist = _PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(op); + + // `op` may have some weakrefs. March over the list, clear + // all the weakrefs. + for (PyWeakReference *wr = *wrlist; wr != NULL; wr = *wrlist) { + // _PyWeakref_ClearRef clears the weakref but leaves + // the callback pointer intact. Obscure: it also + // changes *wrlist. + _PyObject_ASSERT((PyObject *)wr, wr->wr_object == op); + _PyWeakref_ClearRef(wr); + _PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None); + } + } +} + static void call_weakref_callbacks(struct collection_state *state) { @@ -1598,6 +1674,11 @@ _PyGC_Init(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; + gcstate->generation_stats = PyMem_RawCalloc(1, sizeof(struct gc_stats)); + if (gcstate->generation_stats == NULL) { + return _PyStatus_NO_MEMORY(); + } + gcstate->garbage = PyList_New(0); if (gcstate->garbage == NULL) { return _PyStatus_NO_MEMORY(); @@ -1736,7 +1817,7 @@ _PyGC_VisitStackRef(_PyStackRef *ref, visitproc visit, void *arg) // computing the incoming references, but otherwise treat them like // regular references. assert(!PyStackRef_IsTaggedInt(*ref)); - if (!PyStackRef_IsDeferred(*ref) || + if (PyStackRef_RefcountOnObject(*ref) || (visit != visit_decref && visit != visit_decref_unreachable)) { Py_VISIT(PyStackRef_AsPyObjectBorrow(*ref)); @@ -1822,6 +1903,7 @@ handle_resurrected_objects(struct collection_state *state) _PyObject_ASSERT(op, Py_REFCNT(op) > 1); worklist_remove(&iter); merge_refcount(op, -1); // remove worklist reference + state->long_lived_total++; } } } @@ -1845,7 +1927,8 @@ handle_resurrected_objects(struct collection_state *state) static void invoke_gc_callback(PyThreadState *tstate, const char *phase, int generation, Py_ssize_t collected, - Py_ssize_t uncollectable) + Py_ssize_t uncollectable, Py_ssize_t candidates, + double duration) { assert(!_PyErr_Occurred(tstate)); @@ -1859,10 +1942,12 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase, assert(PyList_CheckExact(gcstate->callbacks)); PyObject *info = NULL; if (PyList_GET_SIZE(gcstate->callbacks) != 0) { - info = Py_BuildValue("{sisnsn}", + info = Py_BuildValue("{sisnsnsnsd}", "generation", generation, "collected", collected, - "uncollectable", uncollectable); + "uncollectable", uncollectable, + "candidates", candidates, + "duration", duration); if (info == NULL) { PyErr_FormatUnraisable("Exception ignored while " "invoking gc callbacks"); @@ -1907,185 +1992,6 @@ cleanup_worklist(struct worklist *worklist) } } -// Return the memory usage (typically RSS + swap) of the process, in units of -// KB. Returns -1 if this operation is not supported or on failure. -static Py_ssize_t -get_process_mem_usage(void) -{ -#ifdef _WIN32 - // Windows implementation using GetProcessMemoryInfo - // Returns WorkingSetSize + PagefileUsage - PROCESS_MEMORY_COUNTERS pmc; - HANDLE hProcess = GetCurrentProcess(); - if (NULL == hProcess) { - // Should not happen for the current process - return -1; - } - - // GetProcessMemoryInfo returns non-zero on success - if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) { - // Values are in bytes, convert to KB. - return (Py_ssize_t)((pmc.WorkingSetSize + pmc.PagefileUsage) / 1024); - } - else { - return -1; - } - -#elif __linux__ - FILE* fp = fopen("/proc/self/status", "r"); - if (fp == NULL) { - return -1; - } - - char line_buffer[256]; - long long rss_kb = -1; - long long swap_kb = -1; - - while (fgets(line_buffer, sizeof(line_buffer), fp) != NULL) { - if (rss_kb == -1 && strncmp(line_buffer, "VmRSS:", 6) == 0) { - sscanf(line_buffer + 6, "%lld", &rss_kb); - } - else if (swap_kb == -1 && strncmp(line_buffer, "VmSwap:", 7) == 0) { - sscanf(line_buffer + 7, "%lld", &swap_kb); - } - if (rss_kb != -1 && swap_kb != -1) { - break; // Found both - } - } - fclose(fp); - - if (rss_kb != -1 && swap_kb != -1) { - return (Py_ssize_t)(rss_kb + swap_kb); - } - return -1; - -#elif defined(__APPLE__) - // --- MacOS (Darwin) --- - // Returns phys_footprint (RAM + compressed memory) - task_vm_info_data_t vm_info; - mach_msg_type_number_t count = TASK_VM_INFO_COUNT; - kern_return_t kerr; - - kerr = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&vm_info, &count); - if (kerr != KERN_SUCCESS) { - return -1; - } - // phys_footprint is in bytes. Convert to KB. - return (Py_ssize_t)(vm_info.phys_footprint / 1024); - -#elif defined(__FreeBSD__) - // NOTE: Returns RSS only. Per-process swap usage isn't readily available - long page_size_kb = sysconf(_SC_PAGESIZE) / 1024; - if (page_size_kb <= 0) { - return -1; - } - - // Using /dev/null for vmcore avoids needing dump file. - // NULL for kernel file uses running kernel. - char errbuf[_POSIX2_LINE_MAX]; // For kvm error messages - kvm_t *kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf); - if (kd == NULL) { - return -1; - } - - // KERN_PROC_PID filters for the specific process ID - // n_procs will contain the number of processes returned (should be 1 or 0) - pid_t pid = getpid(); - int n_procs; - struct kinfo_proc *kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &n_procs); - if (kp == NULL) { - kvm_close(kd); - return -1; - } - - Py_ssize_t rss_kb = -1; - if (n_procs > 0) { - // kp[0] contains the info for our process - // ki_rssize is in pages. Convert to KB. - rss_kb = (Py_ssize_t)kp->ki_rssize * page_size_kb; - } - else { - // Process with PID not found, shouldn't happen for self. - rss_kb = -1; - } - - kvm_close(kd); - return rss_kb; - -#elif defined(__OpenBSD__) - // NOTE: Returns RSS only. Per-process swap usage isn't readily available - long page_size_kb = sysconf(_SC_PAGESIZE) / 1024; - if (page_size_kb <= 0) { - return -1; - } - - struct kinfo_proc kp; - pid_t pid = getpid(); - int mib[6]; - size_t len = sizeof(kp); - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = pid; - mib[4] = sizeof(struct kinfo_proc); // size of the structure we want - mib[5] = 1; // want 1 structure back - if (sysctl(mib, 6, &kp, &len, NULL, 0) == -1) { - return -1; - } - - if (len > 0) { - // p_vm_rssize is in pages on OpenBSD. Convert to KB. - return (Py_ssize_t)kp.p_vm_rssize * page_size_kb; - } - else { - // Process info not returned - return -1; - } -#else - // Unsupported platform - return -1; -#endif -} - -static bool -gc_should_collect_mem_usage(GCState *gcstate) -{ - Py_ssize_t mem = get_process_mem_usage(); - if (mem < 0) { - // Reading process memory usage is not support or failed. - return true; - } - int threshold = gcstate->young.threshold; - Py_ssize_t deferred = _Py_atomic_load_ssize_relaxed(&gcstate->deferred_count); - if (deferred > threshold * 40) { - // Too many new container objects since last GC, even though memory use - // might not have increased much. This is intended to avoid resource - // exhaustion if some objects consume resources but don't result in a - // memory usage increase. We use 40x as the factor here because older - // versions of Python would do full collections after roughly every - // 70,000 new container objects. - return true; - } - Py_ssize_t last_mem = _Py_atomic_load_ssize_relaxed(&gcstate->last_mem); - Py_ssize_t mem_threshold = Py_MAX(last_mem / 10, 128); - if ((mem - last_mem) > mem_threshold) { - // The process memory usage has increased too much, do a collection. - return true; - } - else { - // The memory usage has not increased enough, defer the collection and - // clear the young object count so we don't check memory usage again - // on the next call to gc_should_collect(). - PyMutex_Lock(&gcstate->mutex); - int young_count = _Py_atomic_exchange_int(&gcstate->young.count, 0); - _Py_atomic_store_ssize_relaxed(&gcstate->deferred_count, - gcstate->deferred_count + young_count); - PyMutex_Unlock(&gcstate->mutex); - return false; - } -} - static bool gc_should_collect(GCState *gcstate) { @@ -2105,7 +2011,7 @@ gc_should_collect(GCState *gcstate) // objects. return false; } - return gc_should_collect_mem_usage(gcstate); + return true; } static void @@ -2138,7 +2044,19 @@ record_deallocation(PyThreadState *tstate) gc->alloc_count--; if (gc->alloc_count <= -LOCAL_ALLOC_COUNT_THRESHOLD) { GCState *gcstate = &tstate->interp->gc; - _Py_atomic_add_int(&gcstate->young.count, (int)gc->alloc_count); + int count = _Py_atomic_load_int_relaxed(&gcstate->young.count); + int new_count; + do { + if (count == 0) { + break; + } + new_count = count + (int)gc->alloc_count; + if (new_count < 0) { + new_count = 0; + } + } while (!_Py_atomic_compare_exchange_int(&gcstate->young.count, + &count, + new_count)); gc->alloc_count = 0; } } @@ -2154,7 +2072,6 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, } state->gcstate->young.count = 0; - state->gcstate->deferred_count = 0; for (int i = 1; i <= generation; ++i) { state->gcstate->old[i-1].count = 0; } @@ -2219,11 +2136,8 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, } } - // Record the number of live GC objects - interp->gc.long_lived_total = state->long_lived_total; - - // Clear weakrefs and enqueue callbacks (but do not call them). - clear_weakrefs(state, true); + // Find weakref callbacks we will honor (but do not call them). + find_weakref_callbacks(state); _PyEval_StartTheWorld(interp); // Deallocate any object from the refcount merge step @@ -2240,15 +2154,13 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, // Clear free lists in all threads _PyGC_ClearAllFreeLists(interp); if (err == 0) { - // Clear weakrefs to objects in the unreachable set. No Python-level - // code must be allowed to access those unreachable objects. During - // delete_garbage(), finalizers outside the unreachable set might - // run and create new weakrefs. If those weakrefs were not cleared, - // they could reveal unreachable objects. - clear_weakrefs(state, false); + clear_weakrefs(state); } + // Record the number of live GC objects + interp->gc.long_lived_total = state->long_lived_total; _PyEval_StartTheWorld(interp); + if (err < 0) { cleanup_worklist(&state->unreachable); cleanup_worklist(&state->legacy_finalizers); @@ -2263,15 +2175,25 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, // to be freed. delete_garbage(state); - // Store the current memory usage, can be smaller now if breaking cycles - // freed some memory. - Py_ssize_t last_mem = get_process_mem_usage(); - _Py_atomic_store_ssize_relaxed(&state->gcstate->last_mem, last_mem); - // Append objects with legacy finalizers to the "gc.garbage" list. handle_legacy_finalizers(state); } +static struct gc_generation_stats * +get_stats(GCState *gcstate, int gen) +{ + if (gen == 0) { + struct gc_young_stats_buffer *buffer = &gcstate->generation_stats->young; + struct gc_generation_stats *stats = &buffer->items[buffer->index]; + return stats; + } + else { + struct gc_old_stats_buffer *buffer = &gcstate->generation_stats->old[gen - 1]; + struct gc_generation_stats *stats = &buffer->items[buffer->index]; + return stats; + } +} + /* This is the main function. Read this to understand how the * collection process works. */ static Py_ssize_t @@ -2279,7 +2201,6 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) { Py_ssize_t m = 0; /* # objects collected */ Py_ssize_t n = 0; /* # unreachable objects that couldn't be collected */ - PyTime_t t1 = 0; /* initialize to prevent a compiler warning */ GCState *gcstate = &tstate->interp->gc; // gc_collect_main() must not be called before _PyGC_Init @@ -2298,30 +2219,32 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) _Py_atomic_store_int(&gcstate->collecting, 0); return 0; } + gcstate->frame = tstate->current_frame; assert(generation >= 0 && generation < NUM_GENERATIONS); #ifdef Py_STATS - if (_Py_stats) { - _Py_stats->object_stats.object_visits = 0; + PyStats *s = _PyStats_GET(); + if (s) { + s->object_stats.object_visits = 0; } #endif GC_STAT_ADD(generation, collections, 1); if (reason != _Py_GC_REASON_SHUTDOWN) { - invoke_gc_callback(tstate, "start", generation, 0, 0); + invoke_gc_callback(tstate, "start", generation, 0, 0, 0, 0.0); } if (gcstate->debug & _PyGC_DEBUG_STATS) { PySys_WriteStderr("gc: collecting generation %d...\n", generation); show_stats_each_generations(gcstate); - // ignore error: don't interrupt the GC if reading the clock fails - (void)PyTime_PerfCounterRaw(&t1); } if (PyDTrace_GC_START_ENABLED()) { PyDTrace_GC_START(generation); } + PyTime_t start, stop; + (void)PyTime_PerfCounterRaw(&start); PyInterpreterState *interp = tstate->interp; @@ -2336,13 +2259,13 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) m = state.collected; n = state.uncollectable; + (void)PyTime_PerfCounterRaw(&stop); + double duration = PyTime_AsSecondsDouble(stop - start); + if (gcstate->debug & _PyGC_DEBUG_STATS) { - PyTime_t t2; - (void)PyTime_PerfCounterRaw(&t2); - double d = PyTime_AsSecondsDouble(t2 - t1); PySys_WriteStderr( "gc: done, %zd unreachable, %zd uncollectable, %.4fs elapsed\n", - n+m, n, d); + n+m, n, duration); } // Clear the current thread's free-list again. @@ -2359,17 +2282,24 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) } /* Update stats */ - struct gc_generation_stats *stats = &gcstate->generation_stats[generation]; + struct gc_generation_stats *stats = get_stats(gcstate, generation); + stats->ts_start = start; + stats->ts_stop = stop; stats->collections++; stats->collected += m; stats->uncollectable += n; + stats->duration += duration; + stats->candidates += state.candidates; GC_STAT_ADD(generation, objects_collected, m); #ifdef Py_STATS - if (_Py_stats) { - GC_STAT_ADD(generation, object_visits, - _Py_stats->object_stats.object_visits); - _Py_stats->object_stats.object_visits = 0; + { + PyStats *s = _PyStats_GET(); + if (s) { + GC_STAT_ADD(generation, object_visits, + s->object_stats.object_visits); + s->object_stats.object_visits = 0; + } } #endif @@ -2378,10 +2308,11 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) } if (reason != _Py_GC_REASON_SHUTDOWN) { - invoke_gc_callback(tstate, "stop", generation, m, n); + invoke_gc_callback(tstate, "stop", generation, m, n, state.candidates, duration); } assert(!_PyErr_Occurred(tstate)); + gcstate->frame = NULL; _Py_atomic_store_int(&gcstate->collecting, 0); return n + m; } @@ -2698,6 +2629,8 @@ _PyGC_Fini(PyInterpreterState *interp) GCState *gcstate = &interp->gc; Py_CLEAR(gcstate->garbage); Py_CLEAR(gcstate->callbacks); + PyMem_RawFree(gcstate->generation_stats); + gcstate->generation_stats = NULL; /* We expect that none of this interpreters objects are shared with other interpreters. diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index aa1eb373b7ba4b..5adcdcb4521baf 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8,18 +8,18 @@ #endif #define TIER_ONE 1 -#if !Py_TAIL_CALL_INTERP +#if !_Py_TAIL_CALL_INTERP #if !USE_COMPUTED_GOTOS dispatch_opcode: - switch (opcode) + switch (dispatch_code) #endif { -#endif /* Py_TAIL_CALL_INTERP */ +#endif /* _Py_TAIL_CALL_INTERP */ /* BEGIN INSTRUCTIONS */ TARGET(BINARY_OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP; (void)(opcode); #endif @@ -32,13 +32,16 @@ _PyStackRef lhs; _PyStackRef rhs; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; // _SPECIALIZE_BINARY_OP { rhs = stack_pointer[-1]; lhs = stack_pointer[-2]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -48,7 +51,7 @@ } OPCODE_DEFERRED_INC(BINARY_OP); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ assert(NB_ADD <= oparg); assert(oparg <= NB_OPARG_LAST); } @@ -65,24 +68,32 @@ JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); + l = lhs; + r = rhs; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = res; + stack_pointer[-1] = l; _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = lhs; - lhs = res; - stack_pointer[-2] = lhs; - PyStackRef_CLOSE(tmp); - tmp = rhs; - rhs = PyStackRef_NULL; - stack_pointer[-1] = rhs; - PyStackRef_CLOSE(tmp); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } DISPATCH(); } TARGET(BINARY_OP_ADD_FLOAT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_ADD_FLOAT; (void)(opcode); #endif @@ -96,6 +107,8 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; // _GUARD_TOS_FLOAT { value = stack_pointer[-1]; @@ -128,19 +141,34 @@ double dres = ((PyFloatObject *)left_o)->ob_fval + ((PyFloatObject *)right_o)->ob_fval; - res = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (PyStackRef_IsNull(res)) { - JUMP_TO_LABEL(pop_2_error); + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + JUMP_TO_LABEL(error); } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + } + // _POP_TOP_FLOAT + { + value = r; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + // _POP_TOP_FLOAT + { + value = l; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BINARY_OP_ADD_INT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_ADD_INT; (void)(opcode); #endif @@ -154,6 +182,8 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; // _GUARD_TOS_INT { value = stack_pointer[-1]; @@ -190,17 +220,29 @@ assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + l = left; + r = right; + } + // _POP_TOP_INT + { + value = r; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_INT + { + value = l; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BINARY_OP_ADD_UNICODE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_ADD_UNICODE; (void)(opcode); #endif @@ -215,6 +257,8 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; // _GUARD_TOS_UNICODE { value = stack_pointer[-1]; @@ -246,21 +290,33 @@ assert(PyUnicode_CheckExact(right_o)); STAT_INC(BINARY_OP, hit); PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); if (res_o == NULL) { - JUMP_TO_LABEL(pop_2_error); + JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + } + // _POP_TOP_UNICODE + { + value = r; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + // _POP_TOP_UNICODE + { + value = l; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BINARY_OP_EXTEND) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_EXTEND; (void)(opcode); #endif @@ -273,6 +329,9 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; /* Skip 1 cache entry */ // _GUARD_BINARY_OP_EXTEND { @@ -283,11 +342,13 @@ PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr*)descr; assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5); - assert(d && d->guard); + assert(d != NULL); _PyFrame_SetStackPointer(frame, stack_pointer); - int res = d->guard(left_o, right_o); + int match = (d->guard != NULL) + ? d->guard(left_o, right_o) + : (Py_TYPE(left_o) == d->lhs_type && Py_TYPE(right_o) == d->rhs_type); stack_pointer = _PyFrame_GetStackPointer(frame); - if (!res) { + if (!match) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); @@ -304,27 +365,40 @@ STAT_INC(BINARY_OP, hit); _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = d->action(left_o, right_o); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + assert(d->result_type == NULL || Py_TYPE(res_o) == d->result_type); + assert(!d->result_unique || Py_REFCNT(res_o) == 1 || _Py_IsImmortal(res_o)); + assert(!PyFloat_CheckExact(res_o) || Py_REFCNT(res_o) == 1); res = PyStackRef_FromPyObjectSteal(res_o); + l = left; + r = right; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = res; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_INPLACE_ADD_UNICODE; (void)(opcode); #endif @@ -338,6 +412,7 @@ _PyStackRef nos; _PyStackRef left; _PyStackRef right; + _PyStackRef res; // _GUARD_TOS_UNICODE { value = stack_pointer[-1]; @@ -371,7 +446,7 @@ assert(next_instr->op.code == STORE_FAST); next_oparg = next_instr->op.arg; #else - next_oparg = CURRENT_OPERAND0(); + next_oparg = (int)CURRENT_OPERAND0_16(); #endif _PyStackRef *target_local = &GETLOCAL(next_oparg); assert(PyUnicode_CheckExact(left_o)); @@ -382,32 +457,29 @@ } STAT_INC(BINARY_OP, hit); assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left)); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); PyObject *right_o = PyStackRef_AsPyObjectSteal(right); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyUnicode_Append(&temp, right_o); + _Py_DECREF_SPECIALIZED(right_o, _PyUnicode_ExactDealloc); stack_pointer = _PyFrame_GetStackPointer(frame); - *target_local = PyStackRef_FromPyObjectSteal(temp); - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(right_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(*target_local)) { + *target_local = PyStackRef_NULL; + if (temp == NULL) { JUMP_TO_LABEL(error); } - #if TIER_ONE - - assert(next_instr->op.code == STORE_FAST); - SKIP_OVER(1); - #endif + res = PyStackRef_FromPyObjectSteal(temp); } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BINARY_OP_MULTIPLY_FLOAT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_MULTIPLY_FLOAT; (void)(opcode); #endif @@ -421,6 +493,8 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; // _GUARD_TOS_FLOAT { value = stack_pointer[-1]; @@ -453,19 +527,34 @@ double dres = ((PyFloatObject *)left_o)->ob_fval * ((PyFloatObject *)right_o)->ob_fval; - res = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (PyStackRef_IsNull(res)) { - JUMP_TO_LABEL(pop_2_error); + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + JUMP_TO_LABEL(error); } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + } + // _POP_TOP_FLOAT + { + value = r; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + // _POP_TOP_FLOAT + { + value = l; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BINARY_OP_MULTIPLY_INT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_MULTIPLY_INT; (void)(opcode); #endif @@ -479,6 +568,8 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; // _GUARD_TOS_INT { value = stack_pointer[-1]; @@ -515,17 +606,29 @@ assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + l = left; + r = right; + } + // _POP_TOP_INT + { + value = r; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_INT + { + value = l; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BINARY_OP_SUBSCR_DICT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_SUBSCR_DICT; (void)(opcode); #endif @@ -539,11 +642,19 @@ _PyStackRef dict_st; _PyStackRef sub_st; _PyStackRef res; - // _GUARD_NOS_DICT + _PyStackRef ds; + _PyStackRef ss; + _PyStackRef value; + // _GUARD_NOS_DICT_SUBSCRIPT { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + if (!Py_TYPE(o)->tp_as_mapping) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); @@ -556,42 +667,41 @@ dict_st = nos; PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - assert(PyDict_CheckExact(dict)); + assert(Py_TYPE(dict)->tp_as_mapping->mp_subscript == _PyDict_Subscript); STAT_INC(BINARY_OP, hit); - PyObject *res_o; - _PyFrame_SetStackPointer(frame, stack_pointer); - int rc = PyDict_GetItemRef(dict, sub, &res_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (rc == 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_SetKeyError(sub); - stack_pointer = _PyFrame_GetStackPointer(frame); - } _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = sub_st; - sub_st = PyStackRef_NULL; - stack_pointer[-1] = sub_st; - PyStackRef_CLOSE(tmp); - tmp = dict_st; - dict_st = PyStackRef_NULL; - stack_pointer[-2] = dict_st; - PyStackRef_CLOSE(tmp); + PyObject *res_o = _PyDict_Subscript(dict, sub); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (rc <= 0) { + if (res_o == NULL) { JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); + ds = dict_st; + ss = sub_st; + } + // _POP_TOP + { + value = ss; + stack_pointer[-2] = res; + stack_pointer[-1] = ds; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = ds; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(BINARY_OP_SUBSCR_GETITEM) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_SUBSCR_GETITEM; (void)(opcode); #endif @@ -608,7 +718,7 @@ /* Skip 5 cache entries */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); @@ -645,23 +755,23 @@ JUMP_TO_PREDICTED(BINARY_OP); } getitem = PyStackRef_FromPyObjectNew(getitem_o); - STAT_INC(BINARY_OP, hit); } // _BINARY_OP_SUBSCR_INIT_CALL { sub = stack_pointer[-1]; + STAT_INC(BINARY_OP, hit); _PyInterpreterFrame* pushed_frame = _PyFrame_PushUnchecked(tstate, getitem, 2, frame); pushed_frame->localsplus[0] = container; pushed_frame->localsplus[1] = sub; - frame->return_offset = 6 ; + frame->return_offset = 6u ; new_frame = PyStackRef_Wrap(pushed_frame); } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); CALL_STAT_INC(inlined_py_calls); @@ -669,13 +779,14 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } TARGET(BINARY_OP_SUBSCR_LIST_INT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_SUBSCR_LIST_INT; (void)(opcode); #endif @@ -690,6 +801,8 @@ _PyStackRef list_st; _PyStackRef sub_st; _PyStackRef res; + _PyStackRef ls; + _PyStackRef ss; // _GUARD_TOS_INT { value = stack_pointer[-1]; @@ -719,12 +832,10 @@ PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); assert(PyLong_CheckExact(sub)); assert(PyList_CheckExact(list)); - if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { - UPDATE_MISS_STATS(BINARY_OP); - assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); - JUMP_TO_PREDICTED(BINARY_OP); + Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub); + if (index < 0) { + index += PyList_GET_SIZE(list); } - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; #ifdef Py_GIL_DISABLED _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = _PyList_GetItemRef((PyListObject*)list, index); @@ -734,38 +845,42 @@ assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } - STAT_INC(BINARY_OP, hit); res = PyStackRef_FromPyObjectSteal(res_o); #else - if (index >= PyList_GET_SIZE(list)) { + if (index < 0 || index >= PyList_GET_SIZE(list)) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } - STAT_INC(BINARY_OP, hit); PyObject *res_o = PyList_GET_ITEM(list, index); assert(res_o != NULL); res = PyStackRef_FromPyObjectNew(res_o); #endif STAT_INC(BINARY_OP, hit); + ls = list_st; + ss = sub_st; + } + // _POP_TOP_INT + { + value = ss; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP + { + value = ls; + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = list_st; - list_st = res; - stack_pointer[-2] = list_st; - PyStackRef_CLOSE(tmp); - tmp = sub_st; - sub_st = PyStackRef_NULL; - stack_pointer[-1] = sub_st; - PyStackRef_CLOSE(tmp); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); } DISPATCH(); } TARGET(BINARY_OP_SUBSCR_LIST_SLICE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_SUBSCR_LIST_SLICE; (void)(opcode); #endif @@ -780,6 +895,9 @@ _PyStackRef list_st; _PyStackRef sub_st; _PyStackRef res; + _PyStackRef ls; + _PyStackRef ss; + _PyStackRef value; // _GUARD_TOS_SLICE { tos = stack_pointer[-1]; @@ -813,31 +931,36 @@ PyObject *res_o = _PyList_SliceSubscript(list, sub); stack_pointer = _PyFrame_GetStackPointer(frame); STAT_INC(BINARY_OP, hit); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = sub_st; - sub_st = PyStackRef_NULL; - stack_pointer[-1] = sub_st; - PyStackRef_CLOSE(tmp); - tmp = list_st; - list_st = PyStackRef_NULL; - stack_pointer[-2] = list_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); if (res_o == NULL) { JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); + ls = list_st; + ss = sub_st; + } + // _POP_TOP + { + value = ss; + stack_pointer[-2] = res; + stack_pointer[-1] = ls; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = ls; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(BINARY_OP_SUBSCR_STR_INT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_SUBSCR_STR_INT; (void)(opcode); #endif @@ -852,6 +975,8 @@ _PyStackRef str_st; _PyStackRef sub_st; _PyStackRef res; + _PyStackRef s; + _PyStackRef i; // _GUARD_TOS_INT { value = stack_pointer[-1]; @@ -862,7 +987,7 @@ JUMP_TO_PREDICTED(BINARY_OP); } } - // _GUARD_NOS_UNICODE + // _GUARD_NOS_COMPACT_ASCII { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); @@ -871,6 +996,11 @@ assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } + if (!PyUnicode_IS_COMPACT_ASCII(o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } } /* Skip 5 cache entries */ // _BINARY_OP_SUBSCR_STR_INT @@ -881,7 +1011,7 @@ PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); assert(PyLong_CheckExact(sub)); assert(PyUnicode_CheckExact(str)); - if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { + if (!_PyLong_IsNonNegativeCompact((PyLongObject*)sub)) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); @@ -892,30 +1022,34 @@ assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } - Py_UCS4 c = PyUnicode_READ_CHAR(str, index); - if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) { - UPDATE_MISS_STATS(BINARY_OP); - assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); - JUMP_TO_PREDICTED(BINARY_OP); - } + uint8_t c = PyUnicode_1BYTE_DATA(str)[index]; + assert(c < 128); STAT_INC(BINARY_OP, hit); PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(str_st); - stack_pointer = _PyFrame_GetStackPointer(frame); + s = str_st; + i = sub_st; res = PyStackRef_FromPyObjectBorrow(res_o); } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + // _POP_TOP_INT + { + value = i; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_UNICODE + { + value = s; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BINARY_OP_SUBSCR_TUPLE_INT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_OP_SUBSCR_TUPLE_INT; (void)(opcode); #endif @@ -930,6 +1064,8 @@ _PyStackRef tuple_st; _PyStackRef sub_st; _PyStackRef res; + _PyStackRef ts; + _PyStackRef ss; // _GUARD_TOS_INT { value = stack_pointer[-1]; @@ -950,8 +1086,7 @@ JUMP_TO_PREDICTED(BINARY_OP); } } - /* Skip 5 cache entries */ - // _BINARY_OP_SUBSCR_TUPLE_INT + // _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS { sub_st = value; tuple_st = nos; @@ -970,118 +1105,243 @@ assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBSCR_TUPLE_INT + { + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *tuple = PyStackRef_AsPyObjectBorrow(tuple_st); + assert(PyLong_CheckExact(sub)); + assert(PyTuple_CheckExact(tuple)); STAT_INC(BINARY_OP, hit); + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; PyObject *res_o = PyTuple_GET_ITEM(tuple, index); assert(res_o != NULL); - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); res = PyStackRef_FromPyObjectNew(res_o); + ts = tuple_st; + ss = sub_st; + } + // _POP_TOP_INT + { + value = ss; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP + { + value = ts; + stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = tuple_st; - tuple_st = res; - stack_pointer[-1] = tuple_st; - PyStackRef_CLOSE(tmp); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); } DISPATCH(); } - TARGET(BINARY_OP_SUBTRACT_FLOAT) { - #if Py_TAIL_CALL_INTERP - int opcode = BINARY_OP_SUBTRACT_FLOAT; + TARGET(BINARY_OP_SUBSCR_USTR_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBSCR_USTR_INT; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; - INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); + INSTRUCTION_STATS(BINARY_OP_SUBSCR_USTR_INT); static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef value; - _PyStackRef left; - _PyStackRef right; + _PyStackRef nos; + _PyStackRef str_st; + _PyStackRef sub_st; _PyStackRef res; - // _GUARD_TOS_FLOAT + _PyStackRef s; + _PyStackRef i; + // _GUARD_TOS_INT { value = stack_pointer[-1]; PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (!PyFloat_CheckExact(value_o)) { + if (!_PyLong_CheckExactAndCompact(value_o)) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } } - // _GUARD_NOS_FLOAT + // _GUARD_NOS_UNICODE { - left = stack_pointer[-2]; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - if (!PyFloat_CheckExact(left_o)) { + nos = stack_pointer[-2]; + PyObject *o = PyStackRef_AsPyObjectBorrow(nos); + if (!PyUnicode_CheckExact(o)) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } } /* Skip 5 cache entries */ - // _BINARY_OP_SUBTRACT_FLOAT + // _BINARY_OP_SUBSCR_USTR_INT { - right = value; - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval - - ((PyFloatObject *)right_o)->ob_fval; - res = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (PyStackRef_IsNull(res)) { - JUMP_TO_LABEL(pop_2_error); + sub_st = value; + str_st = nos; + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *str = PyStackRef_AsPyObjectBorrow(str_st); + assert(PyLong_CheckExact(sub)); + assert(PyUnicode_CheckExact(str)); + if (!_PyLong_IsNonNegativeCompact((PyLongObject*)sub)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + if (PyUnicode_GET_LENGTH(str) <= index) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + Py_UCS4 c = PyUnicode_READ_CHAR(str, index); + if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); } + STAT_INC(BINARY_OP, hit); + PyObject *res_o = (PyObject*)&_Py_SINGLETON(strings).ascii[c]; + s = str_st; + i = sub_st; + res = PyStackRef_FromPyObjectBorrow(res_o); + } + // _POP_TOP_INT + { + value = i; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_UNICODE + { + value = s; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } - TARGET(BINARY_OP_SUBTRACT_INT) { - #if Py_TAIL_CALL_INTERP - int opcode = BINARY_OP_SUBTRACT_INT; + TARGET(BINARY_OP_SUBTRACT_FLOAT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBTRACT_FLOAT; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; next_instr += 6; - INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); + INSTRUCTION_STATS(BINARY_OP_SUBTRACT_FLOAT); static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); _PyStackRef value; _PyStackRef left; _PyStackRef right; _PyStackRef res; - // _GUARD_TOS_INT + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_FLOAT { value = stack_pointer[-1]; PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (!_PyLong_CheckExactAndCompact(value_o)) { + if (!PyFloat_CheckExact(value_o)) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } } - // _GUARD_NOS_INT + // _GUARD_NOS_FLOAT { left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - if (!_PyLong_CheckExactAndCompact(left_o)) { + if (!PyFloat_CheckExact(left_o)) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } } /* Skip 5 cache entries */ - // _BINARY_OP_SUBTRACT_INT + // _BINARY_OP_SUBTRACT_FLOAT + { + right = value; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyFloat_CheckExact(left_o)); + assert(PyFloat_CheckExact(right_o)); + STAT_INC(BINARY_OP, hit); + double dres = + ((PyFloatObject *)left_o)->ob_fval - + ((PyFloatObject *)right_o)->ob_fval; + PyObject *d = PyFloat_FromDouble(dres); + if (d == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(d); + l = left; + r = right; + } + // _POP_TOP_FLOAT + { + value = r; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + // _POP_TOP_FLOAT + { + value = l; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(BINARY_OP_SUBTRACT_INT) { + #if _Py_TAIL_CALL_INTERP + int opcode = BINARY_OP_SUBTRACT_INT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 6; + INSTRUCTION_STATS(BINARY_OP_SUBTRACT_INT); + static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5, "incorrect cache size"); + _PyStackRef value; + _PyStackRef left; + _PyStackRef right; + _PyStackRef res; + _PyStackRef l; + _PyStackRef r; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + // _GUARD_NOS_INT + { + left = stack_pointer[-2]; + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + if (!_PyLong_CheckExactAndCompact(left_o)) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + } + /* Skip 5 cache entries */ + // _BINARY_OP_SUBTRACT_INT { right = value; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -1096,17 +1356,29 @@ assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); JUMP_TO_PREDICTED(BINARY_OP); } - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + l = left; + r = right; + } + // _POP_TOP_INT + { + value = r; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_INT + { + value = l; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BINARY_SLICE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BINARY_SLICE; (void)(opcode); #endif @@ -1128,28 +1400,53 @@ stop = stack_pointer[-1]; start = stack_pointer[-2]; container = stack_pointer[-3]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); - stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *container_o = PyStackRef_AsPyObjectBorrow(container); + PyObject *start_o = PyStackRef_AsPyObjectBorrow(start); + PyObject *stop_o = PyStackRef_AsPyObjectBorrow(stop); PyObject *res_o; - if (slice == NULL) { - res_o = NULL; + if (PyList_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyList_BinarySlice(container_o, start_o, stop_o); + stack_pointer = _PyFrame_GetStackPointer(frame); } - else { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + else if (PyTuple_CheckExact(container_o)) { _PyFrame_SetStackPointer(frame, stack_pointer); - res_o = PyObject_GetItem(PyStackRef_AsPyObjectBorrow(container), slice); - Py_DECREF(slice); + res_o = _PyTuple_BinarySlice(container_o, start_o, stop_o); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += 2; } - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + else if (PyUnicode_CheckExact(container_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyUnicode_BinarySlice(container_o, start_o, stop_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + PyObject *slice = PySlice_New(start_o, stop_o, NULL); + if (slice == NULL) { + res_o = NULL; + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyObject_GetItem(container_o, slice); + Py_DECREF(slice); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(container); + _PyStackRef tmp = stop; + stop = PyStackRef_NULL; + stack_pointer[-1] = stop; + PyStackRef_CLOSE(tmp); + tmp = start; + start = PyStackRef_NULL; + stack_pointer[-2] = start; + PyStackRef_CLOSE(tmp); + tmp = container; + container = PyStackRef_NULL; + stack_pointer[-3] = container; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (res_o == NULL) { JUMP_TO_LABEL(error); } @@ -1157,12 +1454,12 @@ } stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BUILD_INTERPOLATION) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BUILD_INTERPOLATION; (void)(opcode); #endif @@ -1191,7 +1488,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); if (oparg & 1) { stack_pointer += -(oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(format[0]); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -1200,12 +1497,12 @@ stack_pointer += -(oparg & 1); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(str); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -1215,12 +1512,12 @@ interpolation = PyStackRef_FromPyObjectSteal(interpolation_o); stack_pointer[0] = interpolation; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BUILD_LIST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BUILD_LIST; (void)(opcode); #endif @@ -1239,12 +1536,12 @@ list = PyStackRef_FromPyObjectStealMortal(list_o); stack_pointer[-oparg] = list; stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BUILD_MAP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BUILD_MAP; (void)(opcode); #endif @@ -1254,49 +1551,23 @@ _PyStackRef *values; _PyStackRef map; values = &stack_pointer[-oparg*2]; - STACKREFS_TO_PYOBJECTS(values, oparg*2, values_o); - if (CONVERSION_FAILED(values_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg*2; --_i >= 0;) { - tmp = values[_i]; - values[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg*2; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_LABEL(error); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *map_o = _PyDict_FromItems( - values_o, 2, - values_o+1, 2, - oparg); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(values_o); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg*2; --_i >= 0;) { - tmp = values[_i]; - values[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } + PyObject *map_o = _Py_BuildMap_StackRefSteal(values, oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg*2; - assert(WITHIN_STACK_BOUNDS()); if (map_o == NULL) { + stack_pointer += -oparg*2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } map = PyStackRef_FromPyObjectStealMortal(map_o); - stack_pointer[0] = map; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-oparg*2] = map; + stack_pointer += 1 - oparg*2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BUILD_SET) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BUILD_SET; (void)(opcode); #endif @@ -1319,7 +1590,7 @@ } stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } int err = 0; @@ -1339,7 +1610,7 @@ } if (err) { stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(set_o); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -1348,12 +1619,12 @@ set = PyStackRef_FromPyObjectStealMortal(set_o); stack_pointer[-oparg] = set; stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BUILD_SLICE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BUILD_SLICE; (void)(opcode); #endif @@ -1376,19 +1647,19 @@ } stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (slice_o == NULL) { JUMP_TO_LABEL(error); } slice = PyStackRef_FromPyObjectStealMortal(slice_o); stack_pointer[0] = slice; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BUILD_STRING) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BUILD_STRING; (void)(opcode); #endif @@ -1398,44 +1669,23 @@ _PyStackRef *pieces; _PyStackRef str; pieces = &stack_pointer[-oparg]; - STACKREFS_TO_PYOBJECTS(pieces, oparg, pieces_o); - if (CONVERSION_FAILED(pieces_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = pieces[_i]; - pieces[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_LABEL(error); - } - PyObject *str_o = _PyUnicode_JoinArray(&_Py_STR(empty), pieces_o, oparg); - STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = pieces[_i]; - pieces[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } + PyObject *str_o = _Py_BuildString_StackRefSteal(pieces, oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); if (str_o == NULL) { + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } str = PyStackRef_FromPyObjectSteal(str_o); - stack_pointer[0] = str; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-oparg] = str; + stack_pointer += 1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BUILD_TEMPLATE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BUILD_TEMPLATE; (void)(opcode); #endif @@ -1453,12 +1703,12 @@ PyObject *template_o = _PyTemplate_Build(strings_o, interpolations_o); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(interpolations); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(strings); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -1468,12 +1718,12 @@ template = PyStackRef_FromPyObjectSteal(template_o); stack_pointer[0] = template; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(BUILD_TUPLE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = BUILD_TUPLE; (void)(opcode); #endif @@ -1490,12 +1740,12 @@ tup = PyStackRef_FromPyObjectStealMortal(tup_o); stack_pointer[-oparg] = tup; stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(CACHE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CACHE; (void)(opcode); #endif @@ -1508,7 +1758,7 @@ } TARGET(CALL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL; (void)(opcode); #endif @@ -1529,17 +1779,17 @@ callable = stack_pointer[-2 - oparg]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_Specialize_Call(callable, next_instr, oparg + !PyStackRef_IsNull(self_or_null)); + _Py_Specialize_Call(callable, self_or_null, next_instr, oparg + !PyStackRef_IsNull(self_or_null)); stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH_SAME_OPARG(); } OPCODE_DEFERRED_INC(CALL); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } /* Skip 2 cache entries */ // _MAYBE_EXPAND_METHOD @@ -1569,7 +1819,7 @@ total_args++; } if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -1583,118 +1833,50 @@ ); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (new_frame == NULL) { JUMP_TO_LABEL(error); } - frame->return_offset = 4 ; + frame->return_offset = 4u ; DISPATCH_INLINED(new_frame); } - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_LABEL(error); - } stack_pointer[-2 - oparg] = callable; stack_pointer[-1 - oparg] = self_or_null; _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - if (opcode == INSTRUMENTED_CALL) { - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); - if (res_o == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, callable_o, arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, callable_o, arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_CLEAR(res_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - } - } - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); + PyObject* res_o = _Py_VectorCallInstrumentation_StackRefSteal( + callable, + arguments, + total_args, + PyStackRef_NULL, + opcode == INSTRUMENTED_CALL, + frame, + this_instr, + tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); if (res_o == NULL) { + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _CHECK_PERIODIC_AT_END { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_ALLOC_AND_ENTER_INIT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_ALLOC_AND_ENTER_INIT; (void)(opcode); #endif @@ -1714,13 +1896,13 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } } - // _CHECK_AND_ALLOCATE_OBJECT + // _CHECK_OBJECT { self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; @@ -1742,6 +1924,21 @@ assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + } + // _CHECK_RECURSION_REMAINING + { + if (tstate->py_recursion_remaining <= 1) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _ALLOCATE_OBJECT + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + assert(PyStackRef_IsNull(self_or_null)); + assert(PyType_Check(callable_o)); + PyTypeObject *tp = (PyTypeObject *)callable_o; assert(tp->tp_new == PyBaseObject_Type.tp_new); assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); assert(tp->tp_alloc == PyType_GenericAlloc); @@ -1786,7 +1983,7 @@ tstate, init, NULL, args-1, oparg+1, NULL, shim); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (temp == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_FrameClearAndPop(tstate, shim); @@ -1800,7 +1997,7 @@ // _PUSH_FRAME { new_frame = init_frame; - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -1809,13 +2006,14 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } TARGET(CALL_BOUND_METHOD_EXACT_ARGS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_BOUND_METHOD_EXACT_ARGS; (void)(opcode); #endif @@ -1833,7 +2031,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -1941,10 +2139,10 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); CALL_STAT_INC(inlined_py_calls); @@ -1952,13 +2150,14 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } TARGET(CALL_BOUND_METHOD_GENERAL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_BOUND_METHOD_GENERAL; (void)(opcode); #endif @@ -1976,7 +2175,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -2054,7 +2253,7 @@ ); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (temp == NULL) { JUMP_TO_LABEL(error); } @@ -2071,7 +2270,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -2080,13 +2279,14 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } TARGET(CALL_BUILTIN_CLASS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_BUILTIN_CLASS; (void)(opcode); #endif @@ -2099,13 +2299,11 @@ _PyStackRef callable; _PyStackRef self_or_null; _PyStackRef *args; - _PyStackRef res; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ - // _CALL_BUILTIN_CLASS + // _GUARD_CALLABLE_BUILTIN_CLASS { - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); if (!PyType_Check(callable_o)) { @@ -2114,92 +2312,69 @@ JUMP_TO_PREDICTED(CALL); } PyTypeObject *tp = (PyTypeObject *)callable_o; + if (tp->tp_vectorcall == NULL) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + } + // _CALL_BUILTIN_CLASS + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { arguments--; total_args++; } - if (tp->tp_vectorcall == NULL) { - UPDATE_MISS_STATS(CALL); - assert(_PyOpcode_Deopt[opcode] == (CALL)); - JUMP_TO_PREDICTED(CALL); - } STAT_INC(CALL, hit); - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_LABEL(error); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); + PyObject *res_o = _Py_CallBuiltinClass_StackRef( + callable, + arguments, + total_args); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); if (res_o == NULL) { JUMP_TO_LABEL(error); } - res = PyStackRef_FromPyObjectSteal(res_o); - } - // _CHECK_PERIODIC + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP_OPARG { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + args = &stack_pointer[-oparg]; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyStackRef_CloseStack(args, oparg); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = self_or_null; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_BUILTIN_FAST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_BUILTIN_FAST; (void)(opcode); #endif @@ -2212,21 +2387,13 @@ _PyStackRef callable; _PyStackRef self_or_null; _PyStackRef *args; - _PyStackRef res; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ - // _CALL_BUILTIN_FAST + // _GUARD_CALLABLE_BUILTIN_FAST { - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } if (!PyCFunction_CheckExact(callable_o)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); @@ -2237,86 +2404,65 @@ assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + } + // _CALL_BUILTIN_FAST + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } STAT_INC(CALL, hit); - PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _Py_BuiltinCallFast_StackRef( + callable, + arguments, + total_args + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { JUMP_TO_LABEL(error); } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyCFunctionFast_CAST(cfunc)( - PyCFunction_GET_SELF(callable_o), - args_o, - total_args); + PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + } + // _POP_TOP_OPARG + { + args = &stack_pointer[-oparg]; _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); + _PyStackRef_CloseStack(args, oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_LABEL(error); - } - res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _POP_TOP { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + value = self_or_null; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_BUILTIN_FAST_WITH_KEYWORDS; (void)(opcode); #endif @@ -2329,21 +2475,13 @@ _PyStackRef callable; _PyStackRef self_or_null; _PyStackRef *args; - _PyStackRef res; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ - // _CALL_BUILTIN_FAST_WITH_KEYWORDS + // _GUARD_CALLABLE_BUILTIN_FAST_WITH_KEYWORDS { - args = &stack_pointer[-oparg]; - self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } if (!PyCFunction_CheckExact(callable_o)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); @@ -2354,86 +2492,61 @@ assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + } + // _CALL_BUILTIN_FAST_WITH_KEYWORDS + { + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); - PyCFunctionFastWithKeywords cfunc = - _PyCFunctionFastWithKeywords_CAST(PyCFunction_GET_FUNCTION(callable_o)); + PyObject *res_o = _Py_BuiltinCallFastWithKeywords_StackRef(callable, arguments, total_args); stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + if (res_o == NULL) { JUMP_TO_LABEL(error); } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL); + PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + } + // _POP_TOP_OPARG + { + args = &stack_pointer[-oparg]; _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); + _PyStackRef_CloseStack(args, oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_LABEL(error); - } - res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _POP_TOP { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + value = self_or_null; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_BUILTIN_O) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_BUILTIN_O; (void)(opcode); #endif @@ -2447,39 +2560,51 @@ _PyStackRef self_or_null; _PyStackRef *args; _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ - // _CALL_BUILTIN_O + // _GUARD_CALLABLE_BUILTIN_O { - args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - if (!PyStackRef_IsNull(self_or_null)) { - args--; - total_args++; - } - if (total_args != 1) { + if (!PyCFunction_CheckExact(callable_o)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - if (!PyCFunction_CheckExact(callable_o)) { + if (PyCFunction_GET_FLAGS(callable_o) != METH_O) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - if (PyCFunction_GET_FLAGS(callable_o) != METH_O) { + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args != 1) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + } + // _CHECK_RECURSION_LIMIT + { if (_Py_ReachedRecursionLimit(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + } + // _CALL_BUILTIN_O + { + args = &stack_pointer[-oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (!PyStackRef_IsNull(self_or_null)) { + args--; + } STAT_INC(CALL, hit); PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o); _PyStackRef arg = args[0]; @@ -2488,52 +2613,285 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + c = callable; + s = args[0]; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = s; + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + } + // _POP_TOP + { + value = c; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { JUMP_TO_LABEL(error); } - res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + DISPATCH(); + } + + TARGET(CALL_EX_NON_PY_GENERAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_EX_NON_PY_GENERAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(CALL_EX_NON_PY_GENERAL); + static_assert(INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX == 1, "incorrect cache size"); + _PyStackRef func_st; + _PyStackRef func; + _PyStackRef callargs; + _PyStackRef null; + _PyStackRef callargs_st; + _PyStackRef kwargs_st; + _PyStackRef result; + /* Skip 1 cache entry */ + // _CHECK_IS_NOT_PY_CALLABLE_EX { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + func_st = stack_pointer[-4]; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) == &PyFunction_Type && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { + UPDATE_MISS_STATS(CALL_FUNCTION_EX); + assert(_PyOpcode_Deopt[opcode] == (CALL_FUNCTION_EX)); + JUMP_TO_PREDICTED(CALL_FUNCTION_EX); + } + } + // _MAKE_CALLARGS_A_TUPLE + { + callargs = stack_pointer[-2]; + func = func_st; + PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); + if (!PyTuple_CheckExact(callargs_o)) { _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); + int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { + if (err < 0) { JUMP_TO_LABEL(error); } - stack_pointer += -1; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *tuple_o = PySequence_Tuple(callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (tuple_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callargs; + callargs = PyStackRef_FromPyObjectSteal(tuple_o); + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _CALL_FUNCTION_EX_NON_PY_GENERAL + { + kwargs_st = stack_pointer[-1]; + callargs_st = callargs; + null = stack_pointer[-3]; + func_st = func; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); + (void)null; + assert(PyTuple_CheckExact(callargs)); + PyObject *kwargs = PyStackRef_AsPyObjectBorrow(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + stack_pointer[-2] = callargs_st; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *result_o = PyObject_Call(func, callargs, kwargs); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(kwargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(callargs_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(func_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (result_o == NULL) { + JUMP_TO_LABEL(error); + } + result = PyStackRef_FromPyObjectSteal(result_o); + } + // _CHECK_PERIODIC_AT_END + { + stack_pointer[0] = result; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } - TARGET(CALL_FUNCTION_EX) { - #if Py_TAIL_CALL_INTERP - int opcode = CALL_FUNCTION_EX; + TARGET(CALL_EX_PY) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_EX_PY; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; - next_instr += 1; + next_instr += 2; + INSTRUCTION_STATS(CALL_EX_PY); + static_assert(INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX == 1, "incorrect cache size"); + _PyStackRef func; + _PyStackRef callargs; + _PyStackRef func_st; + _PyStackRef callargs_st; + _PyStackRef kwargs_st; + _PyStackRef ex_frame; + _PyStackRef new_frame; + /* Skip 1 cache entry */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(CALL_FUNCTION_EX); + assert(_PyOpcode_Deopt[opcode] == (CALL_FUNCTION_EX)); + JUMP_TO_PREDICTED(CALL_FUNCTION_EX); + } + } + // _MAKE_CALLARGS_A_TUPLE + { + callargs = stack_pointer[-2]; + func = stack_pointer[-4]; + PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); + if (!PyTuple_CheckExact(callargs_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _Py_Check_ArgsIterable(tstate, PyStackRef_AsPyObjectBorrow(func), callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *tuple_o = PySequence_Tuple(callargs_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (tuple_o == NULL) { + JUMP_TO_LABEL(error); + } + _PyStackRef temp = callargs; + callargs = PyStackRef_FromPyObjectSteal(tuple_o); + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(temp); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + // _CHECK_IS_PY_CALLABLE_EX + { + func_st = func; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + if (Py_TYPE(func) != &PyFunction_Type) { + UPDATE_MISS_STATS(CALL_FUNCTION_EX); + assert(_PyOpcode_Deopt[opcode] == (CALL_FUNCTION_EX)); + JUMP_TO_PREDICTED(CALL_FUNCTION_EX); + } + if (((PyFunctionObject *)func)->vectorcall != _PyFunction_Vectorcall) { + UPDATE_MISS_STATS(CALL_FUNCTION_EX); + assert(_PyOpcode_Deopt[opcode] == (CALL_FUNCTION_EX)); + JUMP_TO_PREDICTED(CALL_FUNCTION_EX); + } + } + // _PY_FRAME_EX + { + kwargs_st = stack_pointer[-1]; + callargs_st = callargs; + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); + assert(PyTuple_CheckExact(callargs)); + assert(Py_TYPE(func) == &PyFunction_Type); + assert(((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall); + PyObject *kwargs = PyStackRef_IsNull(kwargs_st) ? NULL : PyStackRef_AsPyObjectSteal(kwargs_st); + assert(kwargs == NULL || PyDict_CheckExact(kwargs)); + Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); + int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; + PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( + tstate, func_st, locals, + nargs, callargs, kwargs, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (new_frame == NULL) { + JUMP_TO_LABEL(error); + } + ex_frame = PyStackRef_Wrap(new_frame); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + new_frame = ex_frame; + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); + } + + TARGET(CALL_FUNCTION_EX) { + #if _Py_TAIL_CALL_INTERP + int opcode = CALL_FUNCTION_EX; + (void)(opcode); + #endif + frame->instr_ptr = next_instr; + next_instr += 2; INSTRUCTION_STATS(CALL_FUNCTION_EX); + PREDICTED_CALL_FUNCTION_EX:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; opcode = CALL_FUNCTION_EX; _PyStackRef func; _PyStackRef callargs; @@ -2542,10 +2900,26 @@ _PyStackRef callargs_st; _PyStackRef kwargs_st; _PyStackRef result; + // _SPECIALIZE_CALL_FUNCTION_EX + { + func = stack_pointer[-4]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_CallFunctionEx(func, next_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(CALL_FUNCTION_EX); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ + } // _MAKE_CALLARGS_A_TUPLE { callargs = stack_pointer[-2]; - func = stack_pointer[-4]; PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); if (!PyTuple_CheckExact(callargs_o)) { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -2622,7 +2996,7 @@ } else { if (Py_TYPE(func) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); assert(PyTuple_CheckExact(callargs)); @@ -2632,19 +3006,19 @@ int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( tstate, func_st, locals, nargs, callargs, kwargs, frame); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (new_frame == NULL) { JUMP_TO_LABEL(error); } - assert( 1 == 1); - frame->return_offset = 1; + assert( 2u == 1 + INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX); + frame->return_offset = 2u ; DISPATCH_INLINED(new_frame); } PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); @@ -2657,17 +3031,17 @@ stack_pointer = _PyFrame_GetStackPointer(frame); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_XCLOSE(kwargs_st); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callargs_st); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(func_st); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -2676,31 +3050,23 @@ } result = PyStackRef_FromPyObjectSteal(result_o); } - // _CHECK_PERIODIC + // _CHECK_PERIODIC_AT_END { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = result; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + stack_pointer[0] = result; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = result; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_INTRINSIC_1) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_INTRINSIC_1; (void)(opcode); #endif @@ -2709,67 +3075,85 @@ INSTRUCTION_STATS(CALL_INTRINSIC_1); _PyStackRef value; _PyStackRef res; - value = stack_pointer[-1]; - assert(oparg <= MAX_INTRINSIC_1); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, PyStackRef_AsPyObjectBorrow(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_LABEL(error); + _PyStackRef v; + // _CALL_INTRINSIC_1 + { + value = stack_pointer[-1]; + assert(oparg <= MAX_INTRINSIC_1); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + v = value; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = v; + stack_pointer[-1] = res; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_INTRINSIC_2) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_INTRINSIC_2; (void)(opcode); #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(CALL_INTRINSIC_2); - _PyStackRef value2_st; - _PyStackRef value1_st; - _PyStackRef res; - value1_st = stack_pointer[-1]; - value2_st = stack_pointer[-2]; - assert(oparg <= MAX_INTRINSIC_2); - PyObject *value1 = PyStackRef_AsPyObjectBorrow(value1_st); - PyObject *value2 = PyStackRef_AsPyObjectBorrow(value2_st); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyIntrinsics_BinaryFunctions[oparg].func(tstate, value2, value1); - _PyStackRef tmp = value1_st; - value1_st = PyStackRef_NULL; - stack_pointer[-1] = value1_st; - PyStackRef_CLOSE(tmp); - tmp = value2_st; - value2_st = PyStackRef_NULL; - stack_pointer[-2] = value2_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_LABEL(error); + _PyStackRef value2_st; + _PyStackRef value1_st; + _PyStackRef res; + _PyStackRef vs1; + _PyStackRef vs2; + _PyStackRef value; + // _CALL_INTRINSIC_2 + { + value1_st = stack_pointer[-1]; + value2_st = stack_pointer[-2]; + assert(oparg <= MAX_INTRINSIC_2); + PyObject *value1 = PyStackRef_AsPyObjectBorrow(value1_st); + PyObject *value2 = PyStackRef_AsPyObjectBorrow(value2_st); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = _PyIntrinsics_BinaryFunctions[oparg].func(tstate, value2, value1); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + vs1 = value1_st; + vs2 = value2_st; + } + // _POP_TOP + { + value = vs2; + stack_pointer[-2] = res; + stack_pointer[-1] = vs1; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = vs1; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_ISINSTANCE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_ISINSTANCE; (void)(opcode); #endif @@ -2821,17 +3205,17 @@ } (void)null; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(cls); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(instance); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callable); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -2840,12 +3224,12 @@ } stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(CALL_KW) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_KW; (void)(opcode); #endif @@ -2867,7 +3251,7 @@ callable = stack_pointer[-3 - oparg]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -2877,7 +3261,7 @@ } OPCODE_DEFERRED_INC(CALL_KW); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } /* Skip 2 cache entries */ // _MAYBE_EXPAND_METHOD_KW @@ -2910,7 +3294,7 @@ } int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -2924,110 +3308,45 @@ ); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -3 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); stack_pointer = _PyFrame_GetStackPointer(frame); if (new_frame == NULL) { JUMP_TO_LABEL(error); } - assert( 4 == 1 + INLINE_CACHE_ENTRIES_CALL_KW); - frame->return_offset = 4 ; + assert( 4u == 1 + INLINE_CACHE_ENTRIES_CALL_KW); + frame->return_offset = 4u ; DISPATCH_INLINED(new_frame); } - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = kwnames; - kwnames = PyStackRef_NULL; - stack_pointer[-3 - oparg] = callable; - stack_pointer[-2 - oparg] = self_or_null; - stack_pointer[-1] = kwnames; - PyStackRef_CLOSE(tmp); - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-2 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-3 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_LABEL(error); - } stack_pointer[-3 - oparg] = callable; stack_pointer[-2 - oparg] = self_or_null; _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - if (opcode == INSTRUMENTED_CALL_KW) { - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); - if (res_o == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, callable_o, arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, callable_o, arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_CLEAR(res_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - } - } - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = kwnames; - kwnames = PyStackRef_NULL; - stack_pointer[-1] = kwnames; - PyStackRef_CLOSE(tmp); - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-2 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-3 - oparg] = callable; - PyStackRef_CLOSE(tmp); + PyObject* res_o = _Py_VectorCallInstrumentation_StackRefSteal( + callable, + arguments, + total_args, + kwnames, + opcode == INSTRUMENTED_CALL_KW, + frame, + this_instr, + tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3 - oparg; - assert(WITHIN_STACK_BOUNDS()); if (res_o == NULL) { + stack_pointer += -3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-3 - oparg] = res; + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(CALL_KW_BOUND_METHOD) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_KW_BOUND_METHOD; (void)(opcode); #endif @@ -3046,7 +3365,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL_KW); assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); JUMP_TO_PREDICTED(CALL_KW); @@ -3120,12 +3439,12 @@ ); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (temp == NULL) { JUMP_TO_LABEL(error); } @@ -3142,7 +3461,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -3151,13 +3470,14 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } TARGET(CALL_KW_NON_PY) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_KW_NON_PY; (void)(opcode); #endif @@ -3198,101 +3518,43 @@ #if TIER_ONE assert(opcode != INSTRUMENTED_CALL); #endif - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { arguments--; total_args++; } - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = kwnames; - kwnames = PyStackRef_NULL; - stack_pointer[-1] = kwnames; - PyStackRef_CLOSE(tmp); - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-2 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-3 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_LABEL(error); - } - PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames); - int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(kwnames); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); + PyObject *res_o = _Py_VectorCall_StackRefSteal( + callable, + arguments, + total_args, + kwnames); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); if (res_o == NULL) { + stack_pointer += -3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _CHECK_PERIODIC_AT_END { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + stack_pointer[-3 - oparg] = res; + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_KW_PY) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_KW_PY; (void)(opcode); #endif @@ -3310,7 +3572,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL_KW); assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); JUMP_TO_PREDICTED(CALL_KW); @@ -3333,6 +3595,14 @@ JUMP_TO_PREDICTED(CALL_KW); } } + // _CHECK_RECURSION_REMAINING + { + if (tstate->py_recursion_remaining <= 1) { + UPDATE_MISS_STATS(CALL_KW); + assert(_PyOpcode_Deopt[opcode] == (CALL_KW)); + JUMP_TO_PREDICTED(CALL_KW); + } + } // _PY_FRAME_KW { kwnames = stack_pointer[-1]; @@ -3357,12 +3627,12 @@ ); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (temp == NULL) { JUMP_TO_LABEL(error); } @@ -3379,7 +3649,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -3388,13 +3658,14 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } TARGET(CALL_LEN) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_LEN; (void)(opcode); #endif @@ -3408,6 +3679,9 @@ _PyStackRef callable; _PyStackRef arg; _PyStackRef res; + _PyStackRef a; + _PyStackRef c; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ // _GUARD_NOS_NULL @@ -3433,7 +3707,6 @@ // _CALL_LEN { arg = stack_pointer[-1]; - (void)null; STAT_INC(CALL, hit); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -3447,26 +3720,35 @@ if (res_o == NULL) { JUMP_TO_LABEL(error); } + a = arg; + c = callable; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = c; + stack_pointer[-3] = res; + stack_pointer[-2] = a; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + } + // _POP_TOP + { + value = a; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_FromPyObjectSteal(res_o); } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_LIST_APPEND) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_LIST_APPEND; (void)(opcode); #endif @@ -3480,6 +3762,10 @@ _PyStackRef nos; _PyStackRef self; _PyStackRef arg; + _PyStackRef none; + _PyStackRef c; + _PyStackRef s; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ // _GUARD_CALLABLE_LIST_APPEND @@ -3496,8 +3782,7 @@ // _GUARD_NOS_NOT_NULL { nos = stack_pointer[-2]; - PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (o == NULL) { + if (PyStackRef_IsNull(nos)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -3518,11 +3803,6 @@ self = nos; assert(oparg == 1); PyObject *self_o = PyStackRef_AsPyObjectBorrow(self); - if (!PyList_CheckExact(self_o)) { - UPDATE_MISS_STATS(CALL); - assert(_PyOpcode_Deopt[opcode] == (CALL)); - JUMP_TO_PREDICTED(CALL); - } if (!LOCK_OBJECT(self_o)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); @@ -3531,30 +3811,38 @@ STAT_INC(CALL, hit); int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg)); UNLOCK_OBJECT(self_o); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + if (err) { + JUMP_TO_LABEL(error); + } + c = callable; + s = self; + none = PyStackRef_None; + } + // _POP_TOP + { + value = s; + stack_pointer[-3] = none; + stack_pointer[-2] = c; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(self); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = c; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err) { - JUMP_TO_LABEL(error); - } - #if TIER_ONE - - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); - #endif } DISPATCH(); } TARGET(CALL_METHOD_DESCRIPTOR_FAST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_METHOD_DESCRIPTOR_FAST; (void)(opcode); #endif @@ -3567,123 +3855,105 @@ _PyStackRef callable; _PyStackRef self_or_null; _PyStackRef *args; - _PyStackRef res; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ - // _CALL_METHOD_DESCRIPTOR_FAST + // _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST { args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } - if (total_args == 0) { + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + if (method->d_method->ml_flags != METH_FASTCALL) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - PyMethodDef *meth = method->d_method; - if (meth->ml_flags != METH_FASTCALL) { + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args == 0) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); - assert(self != NULL); + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); if (!Py_IS_TYPE(self, method->d_common.d_type)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + } + // _CALL_METHOD_DESCRIPTOR_FAST + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; + } + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + assert(self != NULL); STAT_INC(CALL, hit); - int nargs = total_args - 1; - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyCFunctionFast cfunc = _PyCFunctionFast_CAST(method->d_method->ml_meth); + PyObject *res_o = _PyCallMethodDescriptorFast_StackRef( + callable, + cfunc, + self, + arguments, + total_args + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { JUMP_TO_LABEL(error); } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; _PyFrame_SetStackPointer(frame, stack_pointer); - PyCFunctionFast cfunc = _PyCFunctionFast_CAST(meth->ml_meth); - PyObject *res_o = cfunc(self, (args_o + 1), nargs); + PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + } + // _POP_TOP_OPARG + { _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); + _PyStackRef_CloseStack(args, oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_LABEL(error); - } - res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _POP_TOP { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + value = self_or_null; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS; (void)(opcode); #endif @@ -3696,15 +3966,26 @@ _PyStackRef callable; _PyStackRef self_or_null; _PyStackRef *args; - _PyStackRef res; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ - // _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS + // _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS { args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } + if (method->d_method->ml_flags != (METH_FASTCALL|METH_KEYWORDS)) { + UPDATE_MISS_STATS(CALL); + assert(_PyOpcode_Deopt[opcode] == (CALL)); + JUMP_TO_PREDICTED(CALL); + } int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { @@ -3716,105 +3997,75 @@ assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - PyMethodDef *meth = method->d_method; - if (meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS)) { - UPDATE_MISS_STATS(CALL); - assert(_PyOpcode_Deopt[opcode] == (CALL)); - JUMP_TO_PREDICTED(CALL); + } + // _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + int total_args = oparg; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + total_args++; } - PyTypeObject *d_type = method->d_common.d_type; PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); assert(self != NULL); - if (!Py_IS_TYPE(self, d_type)) { - UPDATE_MISS_STATS(CALL); - assert(_PyOpcode_Deopt[opcode] == (CALL)); - JUMP_TO_PREDICTED(CALL); - } STAT_INC(CALL, hit); - int nargs = total_args - 1; - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyCFunctionFastWithKeywords cfunc = _PyCFunctionFastWithKeywords_CAST(method->d_method->ml_meth); + PyObject *res_o = _PyCallMethodDescriptorFastWithKeywords_StackRef( + callable, + cfunc, + self, + arguments, + total_args + ); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { JUMP_TO_LABEL(error); } + _PyStackRef temp = callable; + callable = PyStackRef_FromPyObjectSteal(res_o); + stack_pointer[-2 - oparg] = callable; _PyFrame_SetStackPointer(frame, stack_pointer); - PyCFunctionFastWithKeywords cfunc = - _PyCFunctionFastWithKeywords_CAST(meth->ml_meth); - PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL); + PyStackRef_CLOSE(temp); stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + } + // _POP_TOP_OPARG + { _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); + _PyStackRef_CloseStack(args, oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - if (res_o == NULL) { - JUMP_TO_LABEL(error); - } - res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _POP_TOP { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + value = self_or_null; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_METHOD_DESCRIPTOR_NOARGS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_METHOD_DESCRIPTOR_NOARGS; (void)(opcode); #endif @@ -3828,94 +4079,111 @@ _PyStackRef self_or_null; _PyStackRef *args; _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ - // _CALL_METHOD_DESCRIPTOR_NOARGS + // _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS { args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; - assert(oparg == 0 || oparg == 1); PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - if (!PyStackRef_IsNull(self_or_null)) { - args--; - total_args++; - } - if (total_args != 1) { + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + if (method->d_method->ml_flags != METH_NOARGS) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - PyMethodDef *meth = method->d_method; - _PyStackRef self_stackref = args[0]; - PyObject *self = PyStackRef_AsPyObjectBorrow(self_stackref); - if (!Py_IS_TYPE(self, method->d_common.d_type)) { + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args != 1) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - if (meth->ml_flags != METH_NOARGS) { + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + } + // _CHECK_RECURSION_LIMIT + { if (_Py_ReachedRecursionLimit(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + } + // _CALL_METHOD_DESCRIPTOR_NOARGS + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + assert(oparg == 1 || !PyStackRef_IsNull(self_or_null)); + if (!PyStackRef_IsNull(self_or_null)) { + args--; + } + _PyStackRef self_stackref = args[0]; + PyObject *self = PyStackRef_AsPyObjectBorrow(self_stackref); STAT_INC(CALL, hit); - PyCFunction cfunc = meth->ml_meth; + PyCFunction cfunc = method->d_method->ml_meth; _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, self, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(self_stackref); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { JUMP_TO_LABEL(error); } + c = callable; + s = args[0]; res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _POP_TOP { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + value = s; + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = c; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_METHOD_DESCRIPTOR_O) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_METHOD_DESCRIPTOR_O; (void)(opcode); #endif @@ -3929,107 +4197,123 @@ _PyStackRef self_or_null; _PyStackRef *args; _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef a; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ - // _CALL_METHOD_DESCRIPTOR_O + // _GUARD_CALLABLE_METHOD_DESCRIPTOR_O { args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - int total_args = oparg; - _PyStackRef *arguments = args; - if (!PyStackRef_IsNull(self_or_null)) { - arguments--; - total_args++; - } PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; - if (total_args != 2) { + if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) { + if (method->d_method->ml_flags != METH_O) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - PyMethodDef *meth = method->d_method; - if (meth->ml_flags != METH_O) { + int total_args = oparg; + if (!PyStackRef_IsNull(self_or_null)) { + total_args++; + } + if (total_args != 2) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - if (_Py_ReachedRecursionLimit(tstate)) { + PyObject *self = PyStackRef_AsPyObjectBorrow( + PyStackRef_IsNull(self_or_null) ? args[0] : self_or_null); + if (!Py_IS_TYPE(self, method->d_common.d_type)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } - _PyStackRef arg_stackref = arguments[1]; - _PyStackRef self_stackref = arguments[0]; - if (!Py_IS_TYPE(PyStackRef_AsPyObjectBorrow(self_stackref), - method->d_common.d_type)) { + } + // _CHECK_RECURSION_LIMIT + { + if (_Py_ReachedRecursionLimit(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); } + } + // _CALL_METHOD_DESCRIPTOR_O + { + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + _PyStackRef *arguments = args; + if (!PyStackRef_IsNull(self_or_null)) { + arguments--; + } STAT_INC(CALL, hit); - PyCFunction cfunc = meth->ml_meth; + PyCFunction cfunc = method->d_method->ml_meth; + PyObject *self = PyStackRef_AsPyObjectBorrow(arguments[0]); + PyObject *arg = PyStackRef_AsPyObjectBorrow(arguments[1]); _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, - PyStackRef_AsPyObjectBorrow(self_stackref), - PyStackRef_AsPyObjectBorrow(arg_stackref)); + PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, self, arg); stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); if (res_o == NULL) { JUMP_TO_LABEL(error); } + c = callable; + s = arguments[0]; + a = arguments[1]; res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _POP_TOP { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + value = a; + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer[-oparg] = s; + stack_pointer += 1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = s; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = c; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_NON_PY_GENERAL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_NON_PY_GENERAL; (void)(opcode); #endif @@ -4068,91 +4352,43 @@ #if TIER_ONE assert(opcode != INSTRUMENTED_CALL); #endif - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); int total_args = oparg; _PyStackRef *arguments = args; if (!PyStackRef_IsNull(self_or_null)) { arguments--; total_args++; } - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_LABEL(error); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); + PyObject *res_o = _Py_VectorCall_StackRefSteal( + callable, + arguments, + total_args, + PyStackRef_NULL); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); if (res_o == NULL) { + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _CHECK_PERIODIC_AT_END { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_PY_EXACT_ARGS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_PY_EXACT_ARGS; (void)(opcode); #endif @@ -4169,7 +4405,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -4248,10 +4484,10 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); CALL_STAT_INC(inlined_py_calls); @@ -4259,13 +4495,14 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } TARGET(CALL_PY_GENERAL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_PY_GENERAL; (void)(opcode); #endif @@ -4282,7 +4519,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(CALL); assert(_PyOpcode_Deopt[opcode] == (CALL)); JUMP_TO_PREDICTED(CALL); @@ -4333,7 +4570,7 @@ ); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (temp == NULL) { JUMP_TO_LABEL(error); } @@ -4350,7 +4587,7 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -4359,13 +4596,14 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } TARGET(CALL_STR_1) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_STR_1; (void)(opcode); #endif @@ -4379,6 +4617,8 @@ _PyStackRef callable; _PyStackRef arg; _PyStackRef res; + _PyStackRef a; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ // _GUARD_NOS_NULL @@ -4407,45 +4647,38 @@ assert(oparg == 1); STAT_INC(CALL, hit); _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Str(arg_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - (void)callable; - (void)null; - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); + PyObject *res_o = PyObject_Str(arg_o); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { JUMP_TO_LABEL(error); } + a = arg; res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _POP_TOP { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + value = a; + stack_pointer[-3] = res; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_TUPLE_1) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_TUPLE_1; (void)(opcode); #endif @@ -4459,6 +4692,8 @@ _PyStackRef callable; _PyStackRef arg; _PyStackRef res; + _PyStackRef a; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ // _GUARD_NOS_NULL @@ -4489,43 +4724,36 @@ _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *res_o = PySequence_Tuple(arg_o); stack_pointer = _PyFrame_GetStackPointer(frame); - (void)callable; - (void)null; - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); - stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { JUMP_TO_LABEL(error); } + a = arg; res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _POP_TOP { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + value = a; + stack_pointer[-3] = res; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _CHECK_PERIODIC_AT_END + { + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CALL_TYPE_1) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CALL_TYPE_1; (void)(opcode); #endif @@ -4539,6 +4767,8 @@ _PyStackRef callable; _PyStackRef arg; _PyStackRef res; + _PyStackRef a; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ // _GUARD_NOS_NULL @@ -4565,22 +4795,25 @@ arg = stack_pointer[-1]; PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); assert(oparg == 1); - (void)callable; - (void)null; STAT_INC(CALL, hit); + a = arg; res = PyStackRef_FromPyObjectNew(Py_TYPE(arg_o)); + } + // _POP_TOP + { + value = a; stack_pointer[-3] = res; stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); } DISPATCH(); } TARGET(CHECK_EG_MATCH) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CHECK_EG_MATCH; (void)(opcode); #endif @@ -4610,7 +4843,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } PyObject *match_o = NULL; @@ -4628,7 +4861,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (res < 0) { JUMP_TO_LABEL(error); } @@ -4646,12 +4879,12 @@ stack_pointer[0] = rest; stack_pointer[1] = match; stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(CHECK_EXC_MATCH) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CHECK_EXC_MATCH; (void)(opcode); #endif @@ -4676,19 +4909,19 @@ int res = PyErr_GivenExceptionMatches(left_o, right_o); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(right); stack_pointer = _PyFrame_GetStackPointer(frame); b = res ? PyStackRef_True : PyStackRef_False; stack_pointer[0] = b; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(CLEANUP_THROW) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CLEANUP_THROW; (void)(opcode); #endif @@ -4698,15 +4931,18 @@ next_instr += 1; INSTRUCTION_STATS(CLEANUP_THROW); _PyStackRef sub_iter; + _PyStackRef null_in; _PyStackRef last_sent_val; _PyStackRef exc_value_st; _PyStackRef none; + _PyStackRef null_out; _PyStackRef value; exc_value_st = stack_pointer[-1]; last_sent_val = stack_pointer[-2]; - sub_iter = stack_pointer[-3]; + null_in = stack_pointer[-3]; + sub_iter = stack_pointer[-4]; PyObject *exc_value = PyStackRef_AsPyObjectBorrow(exc_value_st); - #if !Py_TAIL_CALL_INTERP + #if !_Py_TAIL_CALL_INTERP assert(throwflag); #endif assert(exc_value && PyExceptionInstance_Check(exc_value)); @@ -4718,7 +4954,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); _PyStackRef tmp = sub_iter; sub_iter = value; - stack_pointer[-3] = sub_iter; + stack_pointer[-4] = sub_iter; PyStackRef_CLOSE(tmp); tmp = exc_value_st; exc_value_st = PyStackRef_NULL; @@ -4728,9 +4964,14 @@ last_sent_val = PyStackRef_NULL; stack_pointer[-2] = last_sent_val; PyStackRef_CLOSE(tmp); + tmp = null_in; + null_in = PyStackRef_NULL; + stack_pointer[-3] = null_in; + PyStackRef_XCLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer += -4; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + null_out = null_in; none = PyStackRef_None; } else { @@ -4740,14 +4981,15 @@ JUMP_TO_LABEL(exception_unwind); } stack_pointer[0] = none; - stack_pointer[1] = value; - stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[1] = null_out; + stack_pointer[2] = value; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(COMPARE_OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = COMPARE_OP; (void)(opcode); #endif @@ -4766,7 +5008,7 @@ left = stack_pointer[-2]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -4776,7 +5018,7 @@ } OPCODE_DEFERRED_INC(COMPARE_OP); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } // _COMPARE_OP { @@ -4795,7 +5037,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (res_o == NULL) { JUMP_TO_LABEL(error); } @@ -4815,12 +5057,12 @@ } stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(COMPARE_OP_FLOAT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = COMPARE_OP_FLOAT; (void)(opcode); #endif @@ -4834,6 +5076,8 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; // _GUARD_TOS_FLOAT { value = stack_pointer[-1]; @@ -4864,18 +5108,30 @@ double dleft = PyFloat_AS_DOUBLE(left_o); double dright = PyFloat_AS_DOUBLE(right_o); int sign_ish = COMPARISON_BIT(dleft, dright); - PyStackRef_CLOSE_SPECIALIZED(left, _PyFloat_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(right, _PyFloat_ExactDealloc); + l = left; + r = right; res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; } + // _POP_TOP_FLOAT + { + value = r; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } + // _POP_TOP_FLOAT + { + value = l; + assert(PyFloat_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyFloat_ExactDealloc); + } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(COMPARE_OP_INT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = COMPARE_OP_INT; (void)(opcode); #endif @@ -4889,6 +5145,8 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; // _GUARD_TOS_INT { value = stack_pointer[-1]; @@ -4923,18 +5181,30 @@ Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left_o); Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o); int sign_ish = COMPARISON_BIT(ileft, iright); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); + l = left; + r = right; res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; } + // _POP_TOP_INT + { + value = r; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP_INT + { + value = l; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(COMPARE_OP_STR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = COMPARE_OP_STR; (void)(opcode); #endif @@ -4949,6 +5219,8 @@ _PyStackRef left; _PyStackRef right; _PyStackRef res; + _PyStackRef l; + _PyStackRef r; // _GUARD_TOS_UNICODE { value = stack_pointer[-1]; @@ -4979,21 +5251,33 @@ STAT_INC(COMPARE_OP, hit); int eq = _PyUnicode_Equal(left_o, right_o); assert((oparg >> 5) == Py_EQ || (oparg >> 5) == Py_NE); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); + l = left; + r = right; assert(eq == 0 || eq == 1); assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? PyStackRef_True : PyStackRef_False; } + // _POP_TOP_UNICODE + { + value = r; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } + // _POP_TOP_UNICODE + { + value = l; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); + } stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(CONTAINS_OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CONTAINS_OP; (void)(opcode); #endif @@ -5006,12 +5290,15 @@ _PyStackRef right; _PyStackRef left; _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; // _SPECIALIZE_CONTAINS_OP { right = stack_pointer[-1]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5021,7 +5308,7 @@ } OPCODE_DEFERRED_INC(CONTAINS_OP); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } // _CONTAINS_OP { @@ -5030,30 +5317,37 @@ PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); _PyFrame_SetStackPointer(frame, stack_pointer); int res = PySequence_Contains(right_o, left_o); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); if (res < 0) { JUMP_TO_LABEL(error); } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = b; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - stack_pointer[0] = b; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CONTAINS_OP_DICT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CONTAINS_OP_DICT; (void)(opcode); #endif @@ -5067,11 +5361,14 @@ _PyStackRef left; _PyStackRef right; _PyStackRef b; - // _GUARD_TOS_DICT + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; + // _GUARD_TOS_ANY_DICT { tos = stack_pointer[-1]; PyObject *o = PyStackRef_AsPyObjectBorrow(tos); - if (!PyDict_CheckExact(o)) { + if (!PyAnyDict_CheckExact(o)) { UPDATE_MISS_STATS(CONTAINS_OP); assert(_PyOpcode_Deopt[opcode] == (CONTAINS_OP)); JUMP_TO_PREDICTED(CONTAINS_OP); @@ -5084,34 +5381,41 @@ left = stack_pointer[-2]; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyDict_CheckExact(right_o)); + assert(PyAnyDict_CheckExact(right_o)); STAT_INC(CONTAINS_OP, hit); _PyFrame_SetStackPointer(frame, stack_pointer); int res = PyDict_Contains(right_o, left_o); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); if (res < 0) { JUMP_TO_LABEL(error); } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = b; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - stack_pointer[0] = b; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CONTAINS_OP_SET) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CONTAINS_OP_SET; (void)(opcode); #endif @@ -5125,6 +5429,9 @@ _PyStackRef left; _PyStackRef right; _PyStackRef b; + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; // _GUARD_TOS_ANY_SET { tos = stack_pointer[-1]; @@ -5146,30 +5453,37 @@ STAT_INC(CONTAINS_OP, hit); _PyFrame_SetStackPointer(frame, stack_pointer); int res = _PySet_Contains((PySetObject *)right_o, left_o); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); if (res < 0) { JUMP_TO_LABEL(error); } b = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = b; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - stack_pointer[0] = b; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(CONVERT_VALUE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = CONVERT_VALUE; (void)(opcode); #endif @@ -5186,7 +5500,7 @@ PyObject *result_o = conv_fn(PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -5196,12 +5510,12 @@ result = PyStackRef_FromPyObjectSteal(result_o); stack_pointer[0] = result; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(COPY) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = COPY; (void)(opcode); #endif @@ -5214,12 +5528,12 @@ top = PyStackRef_DUP(bottom); stack_pointer[0] = top; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(COPY_FREE_VARS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = COPY_FREE_VARS; (void)(opcode); #endif @@ -5240,7 +5554,7 @@ } TARGET(DELETE_ATTR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = DELETE_ATTR; (void)(opcode); #endif @@ -5254,7 +5568,7 @@ int err = PyObject_DelAttr(PyStackRef_AsPyObjectBorrow(owner), name); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(owner); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -5265,7 +5579,7 @@ } TARGET(DELETE_DEREF) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = DELETE_DEREF; (void)(opcode); #endif @@ -5287,7 +5601,7 @@ } TARGET(DELETE_FAST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = DELETE_FAST; (void)(opcode); #endif @@ -5313,7 +5627,7 @@ } TARGET(DELETE_GLOBAL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = DELETE_GLOBAL; (void)(opcode); #endif @@ -5338,7 +5652,7 @@ } TARGET(DELETE_NAME) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = DELETE_NAME; (void)(opcode); #endif @@ -5370,7 +5684,7 @@ } TARGET(DELETE_SUBSCR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = DELETE_SUBSCR; (void)(opcode); #endif @@ -5394,7 +5708,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (err) { JUMP_TO_LABEL(error); } @@ -5402,7 +5716,7 @@ } TARGET(DICT_MERGE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = DICT_MERGE; (void)(opcode); #endif @@ -5412,36 +5726,43 @@ _PyStackRef callable; _PyStackRef dict; _PyStackRef update; - update = stack_pointer[-1]; - dict = stack_pointer[-2 - (oparg - 1)]; - callable = stack_pointer[-5 - (oparg - 1)]; - PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); - PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); - PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PyDict_MergeEx(dict_o, update_o, 2); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { + _PyStackRef u; + _PyStackRef value; + // _DICT_MERGE + { + update = stack_pointer[-1]; + dict = stack_pointer[-2 - (oparg - 1)]; + callable = stack_pointer[-5 - (oparg - 1)]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); + PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); + PyObject *dupkey = NULL; _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_FormatKwargsError(tstate, callable_o, update_o); + int err = _PyDict_MergeUniq(dict_o, update_o, &dupkey); stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyEval_FormatKwargsError(tstate, callable_o, update_o, dupkey); + Py_XDECREF(dupkey); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + u = update; + } + // _POP_TOP + { + value = u; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(update); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_LABEL(error); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(update); - stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } TARGET(DICT_UPDATE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = DICT_UPDATE; (void)(opcode); #endif @@ -5450,41 +5771,56 @@ INSTRUCTION_STATS(DICT_UPDATE); _PyStackRef dict; _PyStackRef update; - update = stack_pointer[-1]; - dict = stack_pointer[-2 - (oparg - 1)]; - PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); - PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyDict_Update(dict_o, update_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { + _PyStackRef upd; + _PyStackRef value; + // _DICT_UPDATE + { + update = stack_pointer[-1]; + dict = stack_pointer[-2 - (oparg - 1)]; + PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict); + PyObject *update_o = PyStackRef_AsPyObjectBorrow(update); _PyFrame_SetStackPointer(frame, stack_pointer); - int matches = _PyErr_ExceptionMatches(tstate, PyExc_AttributeError); + int err = PyDict_Update(dict_o, update_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (matches) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_Format(tstate, PyExc_TypeError, - "'%.200s' object is not a mapping", - Py_TYPE(update_o)->tp_name); - stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + int matches = _PyErr_ExceptionMatches(tstate, PyExc_AttributeError); + if (matches) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *exc = _PyErr_GetRaisedException(tstate); + int has_keys = PyObject_HasAttrWithError(update_o, &_Py_ID(keys)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (has_keys == 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Format(tstate, PyExc_TypeError, + "'%T' object is not a mapping", + update_o); + Py_DECREF(exc); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_ChainExceptions1(exc); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + } + JUMP_TO_LABEL(error); } + upd = update; + } + // _POP_TOP + { + value = upd; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(update); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_LABEL(error); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(update); - stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } TARGET(END_ASYNC_FOR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = END_ASYNC_FOR; (void)(opcode); #endif @@ -5516,7 +5852,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); } else { Py_INCREF(exc); @@ -5529,7 +5865,7 @@ } TARGET(END_FOR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = END_FOR; (void)(opcode); #endif @@ -5538,7 +5874,7 @@ _PyStackRef value; value = stack_pointer[-1]; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -5546,7 +5882,7 @@ } TARGET(END_SEND) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = END_SEND; (void)(opcode); #endif @@ -5554,14 +5890,17 @@ next_instr += 1; INSTRUCTION_STATS(END_SEND); _PyStackRef receiver; + _PyStackRef index_or_null; _PyStackRef value; _PyStackRef val; value = stack_pointer[-1]; - receiver = stack_pointer[-2]; + index_or_null = stack_pointer[-2]; + receiver = stack_pointer[-3]; val = value; - stack_pointer[-2] = val; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + (void)index_or_null; + stack_pointer[-3] = val; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(receiver); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -5569,7 +5908,7 @@ } TARGET(ENTER_EXECUTOR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = ENTER_EXECUTOR; (void)(opcode); #endif @@ -5582,11 +5921,26 @@ #ifdef _Py_TIER2 PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; + if (IS_JIT_TRACING()) { + int og_opcode = executor->vm_data.opcode; + int og_oparg = (oparg & ~255) | executor->vm_data.oparg; + next_instr = this_instr; + if (_PyJit_EnterExecutorShouldStopTracing(og_opcode)) { + if (_PyOpcode_Caches[_PyOpcode_Deopt[og_opcode]]) { + PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter); + } + opcode = og_opcode; + oparg = og_oparg; + DISPATCH_GOTO_NON_TRACING(); + } + JUMP_TO_LABEL(stop_tracing); + } assert(executor->vm_data.index == INSTR_OFFSET() - 1); assert(executor->vm_data.code == code); assert(executor->vm_data.valid); assert(tstate->current_executor == NULL); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { + uintptr_t iversion = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(code->_co_instrumentation_version); + if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) != iversion) { opcode = executor->vm_data.opcode; oparg = (oparg & ~255) | executor->vm_data.oparg; next_instr = this_instr; @@ -5595,15 +5949,16 @@ } DISPATCH_GOTO(); } - GOTO_TIER_TWO(executor); + assert(executor != tstate->interp->cold_executor); + tstate->jit_exit = NULL; + TIER1_TO_TIER2(executor); #else Py_FatalError("ENTER_EXECUTOR is not supported in this build"); #endif /* _Py_TIER2 */ - DISPATCH(); } TARGET(EXIT_INIT_CHECK) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = EXIT_INIT_CHECK; (void)(opcode); #endif @@ -5621,12 +5976,12 @@ JUMP_TO_LABEL(error); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(EXTENDED_ARG) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = EXTENDED_ARG; (void)(opcode); #endif @@ -5642,7 +5997,7 @@ } TARGET(FORMAT_SIMPLE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = FORMAT_SIMPLE; (void)(opcode); #endif @@ -5658,7 +6013,7 @@ PyObject *res_o = PyObject_Format(value_o, NULL); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -5673,12 +6028,12 @@ } stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(FORMAT_WITH_SPEC) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = FORMAT_WITH_SPEC; (void)(opcode); #endif @@ -5702,19 +6057,19 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (res_o == NULL) { JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(FOR_ITER) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = FOR_ITER; (void)(opcode); #endif @@ -5733,7 +6088,7 @@ iter = stack_pointer[-2]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -5743,7 +6098,7 @@ } OPCODE_DEFERRED_INC(FOR_ITER); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } // _FOR_ITER { @@ -5763,12 +6118,12 @@ stack_pointer[-1] = null_or_index; stack_pointer[0] = next; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(FOR_ITER_GEN) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = FOR_ITER_GEN; (void)(opcode); #endif @@ -5784,7 +6139,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(FOR_ITER); assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); JUMP_TO_PREDICTED(FOR_ITER); @@ -5799,14 +6154,7 @@ assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); JUMP_TO_PREDICTED(FOR_ITER); } - #ifdef Py_GIL_DISABLED - if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) { - UPDATE_MISS_STATS(FOR_ITER); - assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); - JUMP_TO_PREDICTED(FOR_ITER); - } - #endif - if (gen->gi_frame_state >= FRAME_EXECUTING) { + if (!gen_try_set_executing((PyGenObject *)gen)) { UPDATE_MISS_STATS(FOR_ITER); assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); JUMP_TO_PREDICTED(FOR_ITER); @@ -5814,17 +6162,16 @@ STAT_INC(FOR_ITER, hit); _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; _PyFrame_StackPush(pushed_frame, PyStackRef_None); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; pushed_frame->previous = frame; - frame->return_offset = (uint16_t)( 2 + oparg); + frame->return_offset = (uint16_t)( 2u + oparg); gen_frame = PyStackRef_Wrap(pushed_frame); } // _PUSH_FRAME { new_frame = gen_frame; - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); @@ -5833,13 +6180,14 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } TARGET(FOR_ITER_LIST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = FOR_ITER_LIST; (void)(opcode); #endif @@ -5918,12 +6266,12 @@ stack_pointer[-1] = null_or_index; stack_pointer[0] = next; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(FOR_ITER_RANGE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = FOR_ITER_RANGE; (void)(opcode); #endif @@ -5985,12 +6333,12 @@ } stack_pointer[0] = next; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(FOR_ITER_TUPLE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = FOR_ITER_TUPLE; (void)(opcode); #endif @@ -6029,24 +6377,75 @@ DISPATCH(); } } - // _ITER_NEXT_TUPLE + // _ITER_NEXT_TUPLE + { + PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); + assert(Py_TYPE(tuple_o) == &PyTuple_Type); + uintptr_t i = PyStackRef_UntagInt(null_or_index); + assert((size_t)i < (size_t)PyTuple_GET_SIZE(tuple_o)); + next = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(tuple_o, i)); + null_or_index = PyStackRef_IncrementTaggedIntNoOverflow(null_or_index); + } + stack_pointer[-1] = null_or_index; + stack_pointer[0] = next; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(FOR_ITER_VIRTUAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = FOR_ITER_VIRTUAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(FOR_ITER_VIRTUAL); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); + _PyStackRef null_or_index; + _PyStackRef iter; + _PyStackRef next; + /* Skip 1 cache entry */ + // _GUARD_TOS_NOT_NULL + { + null_or_index = stack_pointer[-1]; + if (PyStackRef_IsNull(null_or_index)) { + UPDATE_MISS_STATS(FOR_ITER); + assert(_PyOpcode_Deopt[opcode] == (FOR_ITER)); + JUMP_TO_PREDICTED(FOR_ITER); + } + } + // _FOR_ITER_VIRTUAL { - PyObject *tuple_o = PyStackRef_AsPyObjectBorrow(iter); - assert(Py_TYPE(tuple_o) == &PyTuple_Type); - uintptr_t i = PyStackRef_UntagInt(null_or_index); - assert((size_t)i < (size_t)PyTuple_GET_SIZE(tuple_o)); - next = PyStackRef_FromPyObjectNew(PyTuple_GET_ITEM(tuple_o, i)); - null_or_index = PyStackRef_IncrementTaggedIntNoOverflow(null_or_index); + iter = stack_pointer[-2]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyObjectIndexPair next_index = Py_TYPE(iter_o)->_tp_iteritem(iter_o, index); + stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + JUMP_TO_LABEL(error); + } + JUMPBY(oparg + 1); + DISPATCH(); + } + null_or_index = PyStackRef_TagInt(index); + next = PyStackRef_FromPyObjectSteal(next_o); } stack_pointer[-1] = null_or_index; stack_pointer[0] = next; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(GET_AITER) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = GET_AITER; (void)(opcode); #endif @@ -6071,7 +6470,7 @@ type->tp_name); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(obj); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -6081,7 +6480,7 @@ iter_o = (*getter)(obj_o); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(obj); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -6102,12 +6501,12 @@ iter = PyStackRef_FromPyObjectSteal(iter_o); stack_pointer[0] = iter; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(GET_ANEXT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = GET_ANEXT; (void)(opcode); #endif @@ -6126,12 +6525,12 @@ awaitable = PyStackRef_FromPyObjectSteal(awaitable_o); stack_pointer[0] = awaitable; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(GET_AWAITABLE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = GET_AWAITABLE; (void)(opcode); #endif @@ -6145,7 +6544,7 @@ PyObject *iter_o = _PyEval_GetAwaitable(PyStackRef_AsPyObjectBorrow(iterable), oparg); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(iterable); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -6155,58 +6554,130 @@ iter = PyStackRef_FromPyObjectSteal(iter_o); stack_pointer[0] = iter; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(GET_ITER) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = GET_ITER; (void)(opcode); #endif frame->instr_ptr = next_instr; - next_instr += 1; + next_instr += 2; INSTRUCTION_STATS(GET_ITER); + PREDICTED_GET_ITER:; + _Py_CODEUNIT* const this_instr = next_instr - 2; + (void)this_instr; _PyStackRef iterable; _PyStackRef iter; _PyStackRef index_or_null; - iterable = stack_pointer[-1]; - #ifdef Py_STATS - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_GatherStats_GetIter(iterable); - stack_pointer = _PyFrame_GetStackPointer(frame); - #endif - - PyTypeObject *tp = PyStackRef_TYPE(iterable); - if (tp == &PyTuple_Type || tp == &PyList_Type) { - iter = iterable; - index_or_null = PyStackRef_TagInt(0); + // _SPECIALIZE_GET_ITER + { + iterable = stack_pointer[-1]; + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + #if ENABLE_SPECIALIZATION + if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { + next_instr = this_instr; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_GetIter(iterable, next_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + DISPATCH_SAME_OPARG(); + } + OPCODE_DEFERRED_INC(GET_ITER); + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + #endif /* ENABLE_SPECIALIZATION */ } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + // _GET_ITER + { _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(iterable); + _PyStackRef result = _PyEval_GetIter(iterable, &index_or_null, oparg); stack_pointer = _PyFrame_GetStackPointer(frame); - if (iter_o == NULL) { - JUMP_TO_LABEL(error); + if (PyStackRef_IsError(result)) { + JUMP_TO_LABEL(pop_1_error); } - iter = PyStackRef_FromPyObjectSteal(iter_o); - index_or_null = PyStackRef_NULL; - stack_pointer += 1; + iter = result; } stack_pointer[-1] = iter; stack_pointer[0] = index_or_null; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(GET_ITER_SELF) { + #if _Py_TAIL_CALL_INTERP + int opcode = GET_ITER_SELF; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(GET_ITER_SELF); + static_assert(INLINE_CACHE_ENTRIES_GET_ITER == 1, "incorrect cache size"); + _PyStackRef iterable; + _PyStackRef res; + /* Skip 1 cache entry */ + // _GUARD_ITERATOR + { + iterable = stack_pointer[-1]; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->tp_iter != PyObject_SelfIter) { + UPDATE_MISS_STATS(GET_ITER); + assert(_PyOpcode_Deopt[opcode] == (GET_ITER)); + JUMP_TO_PREDICTED(GET_ITER); + } + STAT_INC(GET_ITER, hit); + } + // _PUSH_NULL + { + res = PyStackRef_NULL; + } + stack_pointer[0] = res; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + DISPATCH(); + } + + TARGET(GET_ITER_VIRTUAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = GET_ITER_VIRTUAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(GET_ITER_VIRTUAL); + static_assert(INLINE_CACHE_ENTRIES_GET_ITER == 1, "incorrect cache size"); + _PyStackRef iterable; + _PyStackRef zero; + /* Skip 1 cache entry */ + // _GUARD_ITER_VIRTUAL + { + iterable = stack_pointer[-1]; + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(iterable)); + if (tp->_tp_iteritem == NULL) { + UPDATE_MISS_STATS(GET_ITER); + assert(_PyOpcode_Deopt[opcode] == (GET_ITER)); + JUMP_TO_PREDICTED(GET_ITER); + } + STAT_INC(GET_ITER, hit); + } + // _PUSH_TAGGED_ZERO + { + zero = PyStackRef_TagInt(0); + } + stack_pointer[0] = zero; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(GET_LEN) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = GET_LEN; (void)(opcode); #endif @@ -6229,57 +6700,12 @@ len = PyStackRef_FromPyObjectSteal(len_o); stack_pointer[0] = len; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - DISPATCH(); - } - - TARGET(GET_YIELD_FROM_ITER) { - #if Py_TAIL_CALL_INTERP - int opcode = GET_YIELD_FROM_ITER; - (void)(opcode); - #endif - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(GET_YIELD_FROM_ITER); - _PyStackRef iterable; - _PyStackRef iter; - iterable = stack_pointer[-1]; - PyObject *iterable_o = PyStackRef_AsPyObjectBorrow(iterable); - if (PyCoro_CheckExact(iterable_o)) { - if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_SetString(tstate, PyExc_TypeError, - "cannot 'yield from' a coroutine object " - "in a non-coroutine generator"); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_LABEL(error); - } - iter = iterable; - } - else if (PyGen_CheckExact(iterable_o)) { - iter = iterable; - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *iter_o = PyObject_GetIter(iterable_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (iter_o == NULL) { - JUMP_TO_LABEL(error); - } - iter = PyStackRef_FromPyObjectSteal(iter_o); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = iterable; - iterable = iter; - stack_pointer[-1] = iterable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - stack_pointer[-1] = iter; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(IMPORT_FROM) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = IMPORT_FROM; (void)(opcode); #endif @@ -6290,21 +6716,31 @@ _PyStackRef res; from = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyEval_ImportFrom(tstate, PyStackRef_AsPyObjectBorrow(from), name); - stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *res_o; + if (PyLazyImport_CheckExact(PyStackRef_AsPyObjectBorrow(from))) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_LazyImportFrom( + tstate, frame, PyStackRef_AsPyObjectBorrow(from), name); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_ImportFrom( + tstate, PyStackRef_AsPyObjectBorrow(from), name); + stack_pointer = _PyFrame_GetStackPointer(frame); + } if (res_o == NULL) { JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(IMPORT_NAME) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = IMPORT_NAME; (void)(opcode); #endif @@ -6316,11 +6752,26 @@ _PyStackRef res; fromlist = stack_pointer[-1]; level = stack_pointer[-2]; - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + PyObject *res_o; + if (!(oparg & 0x02)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_LazyImportName(tstate, BUILTINS(), GLOBALS(), + LOCALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level), + oparg & 0x01); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + else { + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = _PyEval_ImportName(tstate, BUILTINS(), GLOBALS(), + LOCALS(), name, + PyStackRef_AsPyObjectBorrow(fromlist), + PyStackRef_AsPyObjectBorrow(level)); + stack_pointer = _PyFrame_GetStackPointer(frame); + } _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = _PyEval_ImportName(tstate, frame, name, - PyStackRef_AsPyObjectBorrow(fromlist), - PyStackRef_AsPyObjectBorrow(level)); _PyStackRef tmp = fromlist; fromlist = PyStackRef_NULL; stack_pointer[-1] = fromlist; @@ -6331,19 +6782,19 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (res_o == NULL) { JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(INSTRUMENTED_CALL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_CALL; (void)(opcode); #endif @@ -6420,7 +6871,7 @@ total_args++; } if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -6432,121 +6883,55 @@ ); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (new_frame == NULL) { JUMP_TO_LABEL(error); } - frame->return_offset = 4 ; + frame->return_offset = 4u ; DISPATCH_INLINED(new_frame); } - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_LABEL(error); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - total_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - if (opcode == INSTRUMENTED_CALL) { - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); - if (res_o == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, callable_o, arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, callable_o, arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_CLEAR(res_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - } - } - assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp; - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-1 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-2 - oparg] = callable; - PyStackRef_CLOSE(tmp); + PyObject* res_o = _Py_VectorCallInstrumentation_StackRefSteal( + callable, + arguments, + total_args, + PyStackRef_NULL, + opcode == INSTRUMENTED_CALL, + frame, + this_instr, + tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); if (res_o == NULL) { + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } - // _CHECK_PERIODIC + // _CHECK_PERIODIC_AT_END { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + stack_pointer[-2 - oparg] = res; + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(INSTRUMENTED_CALL_FUNCTION_EX) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_CALL_FUNCTION_EX; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; - next_instr += 1; + next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_CALL_FUNCTION_EX); opcode = INSTRUMENTED_CALL_FUNCTION_EX; _PyStackRef func; @@ -6556,6 +6941,7 @@ _PyStackRef callargs_st; _PyStackRef kwargs_st; _PyStackRef result; + /* Skip 1 cache entry */ // _MAKE_CALLARGS_A_TUPLE { callargs = stack_pointer[-2]; @@ -6636,7 +7022,7 @@ } else { if (Py_TYPE(func) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); assert(PyTuple_CheckExact(callargs)); @@ -6646,19 +7032,19 @@ int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags; PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func)); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex( tstate, func_st, locals, nargs, callargs, kwargs, frame); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (new_frame == NULL) { JUMP_TO_LABEL(error); } - assert( 1 == 1); - frame->return_offset = 1; + assert( 2u == 1 + INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX); + frame->return_offset = 2u ; DISPATCH_INLINED(new_frame); } PyObject *callargs = PyStackRef_AsPyObjectBorrow(callargs_st); @@ -6671,17 +7057,17 @@ stack_pointer = _PyFrame_GetStackPointer(frame); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_XCLOSE(kwargs_st); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(callargs_st); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(func_st); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -6690,31 +7076,23 @@ } result = PyStackRef_FromPyObjectSteal(result_o); } - // _CHECK_PERIODIC + // _CHECK_PERIODIC_AT_END { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - stack_pointer[0] = result; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } - stack_pointer += -1; + stack_pointer[0] = result; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } - stack_pointer[0] = result; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(INSTRUMENTED_CALL_KW) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_CALL_KW; (void)(opcode); #endif @@ -6789,7 +7167,7 @@ } int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o); if (Py_TYPE(callable_o) == &PyFunction_Type && - tstate->interp->eval_frame == NULL && + !IS_PEP523_HOOKED(tstate) && ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall) { int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(callable_o))->co_flags; @@ -6801,106 +7179,43 @@ ); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -3 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(kwnames); stack_pointer = _PyFrame_GetStackPointer(frame); if (new_frame == NULL) { JUMP_TO_LABEL(error); } - assert( 4 == 1 + INLINE_CACHE_ENTRIES_CALL_KW); - frame->return_offset = 4 ; + assert( 4u == 1 + INLINE_CACHE_ENTRIES_CALL_KW); + frame->return_offset = 4u ; DISPATCH_INLINED(new_frame); } - STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o); - if (CONVERSION_FAILED(args_o)) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = kwnames; - kwnames = PyStackRef_NULL; - stack_pointer[-1] = kwnames; - PyStackRef_CLOSE(tmp); - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-2 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-3 - oparg] = callable; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3 - oparg; - assert(WITHIN_STACK_BOUNDS()); - JUMP_TO_LABEL(error); - } - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Vectorcall( - callable_o, args_o, - positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - kwnames_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - STACKREFS_TO_PYOBJECTS_CLEANUP(args_o); - if (opcode == INSTRUMENTED_CALL_KW) { - PyObject *arg = total_args == 0 ? - &_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]); - if (res_o == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _Py_call_instrumentation_exc2( - tstate, PY_MONITORING_EVENT_C_RAISE, - frame, this_instr, callable_o, arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - else { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_call_instrumentation_2args( - tstate, PY_MONITORING_EVENT_C_RETURN, - frame, this_instr, callable_o, arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_CLEAR(res_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - } - } _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = kwnames; - kwnames = PyStackRef_NULL; - stack_pointer[-1] = kwnames; - PyStackRef_CLOSE(tmp); - for (int _i = oparg; --_i >= 0;) { - tmp = args[_i]; - args[_i] = PyStackRef_NULL; - PyStackRef_CLOSE(tmp); - } - tmp = self_or_null; - self_or_null = PyStackRef_NULL; - stack_pointer[-2 - oparg] = self_or_null; - PyStackRef_XCLOSE(tmp); - tmp = callable; - callable = PyStackRef_NULL; - stack_pointer[-3 - oparg] = callable; - PyStackRef_CLOSE(tmp); + PyObject* res_o = _Py_VectorCallInstrumentation_StackRefSteal( + callable, + arguments, + total_args, + kwnames, + opcode == INSTRUMENTED_CALL_KW, + frame, + this_instr, + tstate); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3 - oparg; - assert(WITHIN_STACK_BOUNDS()); if (res_o == NULL) { + stack_pointer += -3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } res = PyStackRef_FromPyObjectSteal(res_o); } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-3 - oparg] = res; + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(INSTRUMENTED_END_ASYNC_FOR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_END_ASYNC_FOR; (void)(opcode); #endif @@ -6939,7 +7254,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); } else { Py_INCREF(exc); @@ -6953,7 +7268,7 @@ } TARGET(INSTRUMENTED_END_FOR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_END_FOR; (void)(opcode); #endif @@ -6974,7 +7289,7 @@ } } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -6982,7 +7297,7 @@ } TARGET(INSTRUMENTED_END_SEND) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_END_SEND; (void)(opcode); #endif @@ -6992,10 +7307,12 @@ next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_END_SEND); _PyStackRef receiver; + _PyStackRef index_or_null; _PyStackRef value; _PyStackRef val; value = stack_pointer[-1]; - receiver = stack_pointer[-2]; + index_or_null = stack_pointer[-2]; + receiver = stack_pointer[-3]; PyObject *receiver_o = PyStackRef_AsPyObjectBorrow(receiver); if (PyGen_Check(receiver_o) || PyCoro_CheckExact(receiver_o)) { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -7005,10 +7322,11 @@ JUMP_TO_LABEL(error); } } - val = value; - stack_pointer[-2] = val; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + val = value; + (void)index_or_null; + stack_pointer[-3] = val; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(receiver); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -7016,7 +7334,7 @@ } TARGET(INSTRUMENTED_FOR_ITER) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_FOR_ITER; (void)(opcode); #endif @@ -7047,12 +7365,12 @@ stack_pointer[-1] = null_or_index; stack_pointer[0] = next; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(INSTRUMENTED_INSTRUCTION) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_INSTRUCTION; (void)(opcode); #endif @@ -7079,7 +7397,7 @@ } TARGET(INSTRUMENTED_JUMP_BACKWARD) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_JUMP_BACKWARD; (void)(opcode); #endif @@ -7091,15 +7409,11 @@ /* Skip 1 cache entry */ // _CHECK_PERIODIC { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } // _MONITOR_JUMP_BACKWARD @@ -7110,7 +7424,7 @@ } TARGET(INSTRUMENTED_JUMP_FORWARD) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_JUMP_FORWARD; (void)(opcode); #endif @@ -7124,7 +7438,7 @@ } TARGET(INSTRUMENTED_LINE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_LINE; (void)(opcode); #endif @@ -7164,7 +7478,7 @@ } TARGET(INSTRUMENTED_LOAD_SUPER_ATTR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_LOAD_SUPER_ATTR; (void)(opcode); #endif @@ -7211,14 +7525,17 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } } - PyObject *stack[] = {class, self}; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *super; + { + PyObject *stack[] = {class, self}; + _PyFrame_SetStackPointer(frame, stack_pointer); + super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + } if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; if (super == NULL) { @@ -7256,7 +7573,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (super == NULL) { JUMP_TO_LABEL(error); } @@ -7279,12 +7596,12 @@ } stack_pointer[0] = attr; stack_pointer += 1 + (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(INSTRUMENTED_NOT_TAKEN) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_NOT_TAKEN; (void)(opcode); #endif @@ -7300,7 +7617,7 @@ } TARGET(INSTRUMENTED_POP_ITER) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_POP_ITER; (void)(opcode); #endif @@ -7317,7 +7634,7 @@ (void)index_or_null; INSTRUMENTED_JUMP(prev_instr, this_instr+1, PY_MONITORING_EVENT_BRANCH_RIGHT); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(iter); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -7325,7 +7642,7 @@ } TARGET(INSTRUMENTED_POP_JUMP_IF_FALSE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_POP_JUMP_IF_FALSE; (void)(opcode); #endif @@ -7344,12 +7661,12 @@ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(INSTRUMENTED_POP_JUMP_IF_NONE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_POP_JUMP_IF_NONE; (void)(opcode); #endif @@ -7368,19 +7685,19 @@ } else { stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += 1; } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(INSTRUMENTED_POP_JUMP_IF_NOT_NONE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_POP_JUMP_IF_NOT_NONE; (void)(opcode); #endif @@ -7396,7 +7713,7 @@ RECORD_BRANCH_TAKEN(this_instr[1].cache, jump); if (jump) { stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -7409,7 +7726,7 @@ } TARGET(INSTRUMENTED_POP_JUMP_IF_TRUE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_POP_JUMP_IF_TRUE; (void)(opcode); #endif @@ -7428,20 +7745,21 @@ INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(INSTRUMENTED_RESUME) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_RESUME; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; - next_instr += 1; + next_instr += 2; INSTRUCTION_STATS(INSTRUMENTED_RESUME); + /* Skip 1 cache entry */ // _LOAD_BYTECODE { #ifdef Py_GIL_DISABLED @@ -7464,7 +7782,13 @@ } // _MAYBE_INSTRUMENT { - if (tstate->tracing == 0) { + #ifdef Py_GIL_DISABLED + + int check_instrumentation = 1; + #else + int check_instrumentation = (tstate->tracing == 0); + #endif + if (check_instrumentation) { uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & ~_PY_EVAL_EVENTS_MASK; uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); if (code_version != global_version) { @@ -7482,15 +7806,11 @@ // _CHECK_PERIODIC_IF_NOT_YIELD_FROM { if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } } @@ -7498,7 +7818,7 @@ { _PyFrame_SetStackPointer(frame, stack_pointer); int err = _Py_call_instrumentation( - tstate, oparg > 0, frame, this_instr); + tstate, oparg == 0 ? PY_MONITORING_EVENT_PY_START : PY_MONITORING_EVENT_PY_RESUME, frame, this_instr); stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { JUMP_TO_LABEL(error); @@ -7511,7 +7831,7 @@ } TARGET(INSTRUMENTED_RETURN_VALUE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_RETURN_VALUE; (void)(opcode); #endif @@ -7521,6 +7841,7 @@ next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_RETURN_VALUE); _PyStackRef val; + _PyStackRef value; _PyStackRef retval; _PyStackRef res; // _RETURN_VALUE_EVENT @@ -7535,15 +7856,21 @@ JUMP_TO_LABEL(error); } } + // _MAKE_HEAP_SAFE + { + value = val; + value = PyStackRef_MakeHeapSafe(value); + } // _RETURN_VALUE { - retval = val; + retval = value; assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - _PyStackRef temp = PyStackRef_MakeHeapSafe(retval); + _PyStackRef temp = retval; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *dying = frame; frame = tstate->current_frame = dying->previous; @@ -7555,12 +7882,12 @@ } stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(INSTRUMENTED_YIELD_VALUE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INSTRUMENTED_YIELD_VALUE; (void)(opcode); #endif @@ -7569,9 +7896,10 @@ frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(INSTRUMENTED_YIELD_VALUE); + opcode = INSTRUMENTED_YIELD_VALUE; _PyStackRef val; - _PyStackRef retval; _PyStackRef value; + _PyStackRef retval; // _YIELD_VALUE_EVENT { val = stack_pointer[-1]; @@ -7588,47 +7916,54 @@ DISPATCH(); } } + // _MAKE_HEAP_SAFE + { + value = val; + value = PyStackRef_MakeHeapSafe(value); + } // _YIELD_VALUE { - retval = val; + retval = value; assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); frame->instr_ptr++; PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; _PyStackRef temp = retval; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); + DTRACE_FUNCTION_RETURN(); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; frame = tstate->current_frame = frame->previous; gen_frame->previous = NULL; + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); - #if TIER_ONE - assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE || - frame->instr_ptr->op.code == INSTRUMENTED_INSTRUCTION || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == SEND || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == FOR_ITER || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == INTERPRETER_EXIT || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == ENTER_EXECUTOR); + #if TIER_ONE && defined(Py_DEBUG) + if (!PyStackRef_IsNone(frame->f_executable)) { + Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); + assert(i >= 0 && i <= INT_MAX); + int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; + assert(opcode == SEND || opcode == FOR_ITER); + } #endif stack_pointer = _PyFrame_GetStackPointer(frame); LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); - value = PyStackRef_MakeHeapSafe(temp); + value = temp; LLTRACE_RESUME_FRAME(); } stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(INTERPRETER_EXIT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = INTERPRETER_EXIT; (void)(opcode); #endif @@ -7642,7 +7977,7 @@ tstate->current_frame = frame->previous; assert(!_PyErr_Occurred(tstate)); PyObject *result = PyStackRef_AsPyObjectSteal(retval); - #if !Py_TAIL_CALL_INTERP + #if !_Py_TAIL_CALL_INTERP assert(frame == &entry.frame); #endif #ifdef _Py_TIER2 @@ -7651,7 +7986,7 @@ if (!PyStackRef_IsNull(executor)) { tstate->current_executor = PyStackRef_AsPyObjectBorrow(executor); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(executor); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -7663,7 +7998,7 @@ } TARGET(IS_OP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = IS_OP; (void)(opcode); #endif @@ -7673,30 +8008,41 @@ _PyStackRef left; _PyStackRef right; _PyStackRef b; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - int res = Py_Is(PyStackRef_AsPyObjectBorrow(left), PyStackRef_AsPyObjectBorrow(right)) ^ oparg; - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = right; - right = PyStackRef_NULL; - stack_pointer[-1] = right; - PyStackRef_CLOSE(tmp); - tmp = left; - left = PyStackRef_NULL; - stack_pointer[-2] = left; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - b = res ? PyStackRef_True : PyStackRef_False; - stack_pointer[0] = b; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + _PyStackRef l; + _PyStackRef r; + _PyStackRef value; + // _IS_OP + { + right = stack_pointer[-1]; + left = stack_pointer[-2]; + int res = Py_Is(PyStackRef_AsPyObjectBorrow(left), PyStackRef_AsPyObjectBorrow(right)) ^ oparg; + b = res ? PyStackRef_True : PyStackRef_False; + l = left; + r = right; + } + // _POP_TOP + { + value = r; + stack_pointer[-2] = b; + stack_pointer[-1] = l; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = l; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } DISPATCH(); } TARGET(JUMP_BACKWARD) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = JUMP_BACKWARD; (void)(opcode); #endif @@ -7711,7 +8057,8 @@ { #if ENABLE_SPECIALIZATION if (this_instr->op.code == JUMP_BACKWARD) { - this_instr->op.code = tstate->interp->jit ? JUMP_BACKWARD_JIT : JUMP_BACKWARD_NO_JIT; + uint8_t desired = tstate->interp->jit ? JUMP_BACKWARD_JIT : JUMP_BACKWARD_NO_JIT; + FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, desired); next_instr = this_instr; DISPATCH_SAME_OPARG(); } @@ -7719,15 +8066,11 @@ } // _CHECK_PERIODIC { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } // _JUMP_BACKWARD_NO_INTERRUPT @@ -7739,7 +8082,7 @@ } TARGET(JUMP_BACKWARD_JIT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = JUMP_BACKWARD_JIT; (void)(opcode); #endif @@ -7752,15 +8095,11 @@ /* Skip 1 cache entry */ // _CHECK_PERIODIC { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } // _JUMP_BACKWARD_NO_INTERRUPT @@ -7771,29 +8110,24 @@ // _JIT { #ifdef _Py_TIER2 + bool is_resume = this_instr->op.code == RESUME_CHECK_JIT; _Py_BackoffCounter counter = this_instr[1].counter; - if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD_JIT) { - _Py_CODEUNIT *start = this_instr; + if ((backoff_counter_triggers(counter) && + !IS_JIT_TRACING() && + (this_instr->op.code == JUMP_BACKWARD_JIT || is_resume)) && + next_instr->op.code != ENTER_EXECUTOR) { + _Py_CODEUNIT *insert_exec_at = this_instr; while (oparg > 255) { oparg >>= 8; - start--; + insert_exec_at--; } - _PyExecutorObject *executor; - _PyFrame_SetStackPointer(frame, stack_pointer); - int optimized = _PyOptimizer_Optimize(frame, start, &executor, 0); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (optimized <= 0) { - this_instr[1].counter = restart_backoff_counter(counter); - if (optimized < 0) { - JUMP_TO_LABEL(error); - } + int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, + is_resume ? insert_exec_at : next_instr, stack_pointer, 0, NULL, oparg, NULL); + if (succ) { + ENTER_TRACING(); } else { - _PyFrame_SetStackPointer(frame, stack_pointer); - this_instr[1].counter = initial_jump_backoff_counter(); - stack_pointer = _PyFrame_GetStackPointer(frame); - assert(tstate->current_executor == NULL); - GOTO_TIER_TWO(executor); + this_instr[1].counter = restart_backoff_counter(counter); } } else { @@ -7805,7 +8139,7 @@ } TARGET(JUMP_BACKWARD_NO_INTERRUPT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = JUMP_BACKWARD_NO_INTERRUPT; (void)(opcode); #endif @@ -7818,7 +8152,7 @@ } TARGET(JUMP_BACKWARD_NO_JIT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = JUMP_BACKWARD_NO_JIT; (void)(opcode); #endif @@ -7829,15 +8163,11 @@ /* Skip 1 cache entry */ // _CHECK_PERIODIC { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } // _JUMP_BACKWARD_NO_INTERRUPT @@ -7849,7 +8179,7 @@ } TARGET(JUMP_FORWARD) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = JUMP_FORWARD; (void)(opcode); #endif @@ -7861,7 +8191,7 @@ } TARGET(LIST_APPEND) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LIST_APPEND; (void)(opcode); #endif @@ -7878,12 +8208,12 @@ JUMP_TO_LABEL(pop_1_error); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LIST_EXTEND) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LIST_EXTEND; (void)(opcode); #endif @@ -7892,45 +8222,48 @@ INSTRUCTION_STATS(LIST_EXTEND); _PyStackRef list_st; _PyStackRef iterable_st; - iterable_st = stack_pointer[-1]; - list_st = stack_pointer[-2 - (oparg-1)]; - PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); - PyObject *iterable = PyStackRef_AsPyObjectBorrow(iterable_st); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (none_val == NULL) { + _PyStackRef i; + _PyStackRef value; + // _LIST_EXTEND + { + iterable_st = stack_pointer[-1]; + list_st = stack_pointer[-2 - (oparg-1)]; + PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); + PyObject *iterable = PyStackRef_AsPyObjectBorrow(iterable_st); _PyFrame_SetStackPointer(frame, stack_pointer); - int matches = _PyErr_ExceptionMatches(tstate, PyExc_TypeError); + PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable); stack_pointer = _PyFrame_GetStackPointer(frame); - if (matches && - (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) - { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyErr_Clear(tstate); - _PyErr_Format(tstate, PyExc_TypeError, + if (none_val == NULL) { + int matches = _PyErr_ExceptionMatches(tstate, PyExc_TypeError); + if (matches && + (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable))) + { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyErr_Clear(tstate); + _PyErr_Format(tstate, PyExc_TypeError, "Value after * must be an iterable, not %.200s", Py_TYPE(iterable)->tp_name); - stack_pointer = _PyFrame_GetStackPointer(frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + JUMP_TO_LABEL(error); } + assert(Py_IsNone(none_val)); + i = iterable_st; + } + // _POP_TOP + { + value = i; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(iterable_st); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_LABEL(error); } - assert(Py_IsNone(none_val)); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(iterable_st); - stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } TARGET(LOAD_ATTR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR; (void)(opcode); #endif @@ -7941,14 +8274,14 @@ _Py_CODEUNIT* const this_instr = next_instr - 10; (void)this_instr; _PyStackRef owner; - _PyStackRef *attr; + _PyStackRef attr; _PyStackRef *self_or_null; // _SPECIALIZE_LOAD_ATTR { owner = stack_pointer[-1]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); next_instr = this_instr; @@ -7959,59 +8292,45 @@ } OPCODE_DEFERRED_INC(LOAD_ATTR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } /* Skip 8 cache entries */ // _LOAD_ATTR { - attr = &stack_pointer[-1]; self_or_null = &stack_pointer[0]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); if (oparg & 1) { - *attr = PyStackRef_NULL; _PyFrame_SetStackPointer(frame, stack_pointer); - int is_meth = _PyObject_GetMethodStackRef(tstate, PyStackRef_AsPyObjectBorrow(owner), name, attr); + attr = _Py_LoadAttr_StackRefSteal(tstate, owner, name, self_or_null); stack_pointer = _PyFrame_GetStackPointer(frame); - if (is_meth) { - assert(!PyStackRef_IsNull(*attr)); - self_or_null[0] = owner; - } - else { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(*attr)) { - JUMP_TO_LABEL(error); - } - self_or_null[0] = PyStackRef_NULL; - stack_pointer += 1; + if (PyStackRef_IsNull(attr)) { + JUMP_TO_LABEL(pop_1_error); } } else { _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name); + attr = _PyObject_GetAttrStackRef(PyStackRef_AsPyObjectBorrow(owner), name); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = attr; + stack_pointer += (oparg&1); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(owner); stack_pointer = _PyFrame_GetStackPointer(frame); - if (attr_o == NULL) { + if (PyStackRef_IsNull(attr)) { JUMP_TO_LABEL(error); } - *attr = PyStackRef_FromPyObjectSteal(attr_o); - stack_pointer += 1; + stack_pointer += -(oparg&1); } } + stack_pointer[-1] = attr; stack_pointer += (oparg&1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_CLASS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_CLASS; (void)(opcode); #endif @@ -8064,12 +8383,12 @@ } } stack_pointer += (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_CLASS_WITH_METACLASS_CHECK) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_CLASS_WITH_METACLASS_CHECK; (void)(opcode); #endif @@ -8083,29 +8402,29 @@ _PyStackRef attr; _PyStackRef *null; /* Skip 1 cache entry */ - // _CHECK_ATTR_CLASS + // _GUARD_TYPE_VERSION { owner = stack_pointer[-1]; uint32_t type_version = read_u32(&this_instr[2].cache); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - if (!PyType_Check(owner_o)) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); assert(type_version != 0); - if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); } } - // _GUARD_TYPE_VERSION + // _CHECK_ATTR_CLASS { uint32_t type_version = read_u32(&this_instr[4].cache); - PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + if (!PyType_Check(owner_o)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } assert(type_version != 0); - if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + if (FT_ATOMIC_LOAD_UINT_RELAXED(((PyTypeObject *)owner_o)->tp_version_tag) != type_version) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); @@ -8132,12 +8451,12 @@ } } stack_pointer += (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN; (void)(opcode); #endif @@ -8148,54 +8467,86 @@ INSTRUCTION_STATS(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN); static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; + _PyStackRef new_frame; /* Skip 1 cache entry */ - owner = stack_pointer[-1]; - uint32_t type_version = read_u32(&this_instr[2].cache); - uint32_t func_version = read_u32(&this_instr[4].cache); - PyObject *getattribute = read_obj(&this_instr[6].cache); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - assert((oparg & 1) == 0); - if (tstate->interp->eval_frame) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - PyTypeObject *cls = Py_TYPE(owner_o); - assert(type_version != 0); - if (FT_ATOMIC_LOAD_UINT_RELAXED(cls->tp_version_tag) != type_version) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); - PyFunctionObject *f = (PyFunctionObject *)getattribute; - assert(func_version != 0); - if (f->func_version != func_version) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - PyCodeObject *code = (PyCodeObject *)f->func_code; - assert(code->co_argcount == 2); - if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - STAT_INC(LOAD_ATTR, hit); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked( - tstate, PyStackRef_FromPyObjectNew(f), 2, frame); - new_frame->localsplus[0] = owner; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - new_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name); - frame->return_offset = 10 ; - DISPATCH_INLINED(new_frame); + // _GUARD_TYPE_VERSION + { + owner = stack_pointer[-1]; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); + assert(type_version != 0); + if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } + // _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME + { + uint32_t func_version = read_u32(&this_instr[4].cache); + PyObject *getattribute = read_obj(&this_instr[6].cache); + assert((oparg & 1) == 0); + assert(Py_IS_TYPE(getattribute, &PyFunction_Type)); + PyFunctionObject *f = (PyFunctionObject *)getattribute; + assert(func_version != 0); + if (f->func_version != func_version) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + PyCodeObject *code = (PyCodeObject *)f->func_code; + assert(code->co_argcount == 2); + if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + STAT_INC(LOAD_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1); + _PyInterpreterFrame *pushed_frame = _PyFrame_PushUnchecked( + tstate, PyStackRef_FromPyObjectNew(f), 2, frame); + pushed_frame->localsplus[0] = owner; + pushed_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name); + new_frame = PyStackRef_Wrap(pushed_frame); + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE + frame->return_offset = (uint16_t)(next_instr - this_instr); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + { + assert(!IS_PEP523_HOOKED(tstate)); + _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(temp->previous == frame || temp->previous->previous == frame); + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = temp; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); + LLTRACE_RESUME_FRAME(); + } + DISPATCH(); } TARGET(LOAD_ATTR_INSTANCE_VALUE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_INSTANCE_VALUE; (void)(opcode); #endif @@ -8207,6 +8558,8 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; + _PyStackRef o; + _PyStackRef value; _PyStackRef *null; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION @@ -8256,9 +8609,14 @@ attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); + o = owner; + } + // _POP_TOP + { + value = o; stack_pointer[-1] = attr; _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); } /* Skip 5 cache entries */ @@ -8270,12 +8628,12 @@ } } stack_pointer += (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_METHOD_LAZY_DICT; (void)(opcode); #endif @@ -8326,12 +8684,12 @@ stack_pointer[-1] = attr; stack_pointer[0] = self; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_METHOD_NO_DICT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_METHOD_NO_DICT; (void)(opcode); #endif @@ -8372,12 +8730,12 @@ stack_pointer[-1] = attr; stack_pointer[0] = self; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_METHOD_WITH_VALUES; (void)(opcode); #endif @@ -8414,18 +8772,7 @@ JUMP_TO_PREDICTED(LOAD_ATTR); } } - // _GUARD_KEYS_VERSION - { - uint32_t keys_version = read_u32(&this_instr[4].cache); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - } + /* Skip 2 cache entries */ // _LOAD_ATTR_METHOD_WITH_VALUES { PyObject *descr = read_obj(&this_instr[6].cache); @@ -8439,12 +8786,12 @@ stack_pointer[-1] = attr; stack_pointer[0] = self; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_MODULE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_MODULE; (void)(opcode); #endif @@ -8456,6 +8803,8 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; + _PyStackRef o; + _PyStackRef value; _PyStackRef *null; /* Skip 1 cache entry */ // _LOAD_ATTR_MODULE @@ -8499,9 +8848,14 @@ attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); + o = owner; + } + // _POP_TOP + { + value = o; stack_pointer[-1] = attr; _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); } /* Skip 5 cache entries */ @@ -8513,12 +8867,12 @@ } } stack_pointer += (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_NONDESCRIPTOR_NO_DICT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_NONDESCRIPTOR_NO_DICT; (void)(opcode); #endif @@ -8552,7 +8906,7 @@ STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(owner); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -8560,12 +8914,12 @@ } stack_pointer[0] = attr; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES; (void)(opcode); #endif @@ -8601,18 +8955,7 @@ JUMP_TO_PREDICTED(LOAD_ATTR); } } - // _GUARD_KEYS_VERSION - { - uint32_t keys_version = read_u32(&this_instr[4].cache); - PyTypeObject *owner_cls = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - PyDictKeysObject *keys = owner_heap_type->ht_cached_keys; - if (FT_ATOMIC_LOAD_UINT32_RELAXED(keys->dk_version) != keys_version) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - } + /* Skip 2 cache entries */ // _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES { PyObject *descr = read_obj(&this_instr[6].cache); @@ -8620,7 +8963,7 @@ STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(owner); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -8628,12 +8971,12 @@ } stack_pointer[0] = attr; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_PROPERTY) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_PROPERTY; (void)(opcode); #endif @@ -8646,14 +8989,6 @@ _PyStackRef owner; _PyStackRef new_frame; /* Skip 1 cache entry */ - // _CHECK_PEP_523 - { - if (tstate->interp->eval_frame) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - } // _GUARD_TYPE_VERSION { owner = stack_pointer[-1]; @@ -8666,29 +9001,27 @@ JUMP_TO_PREDICTED(LOAD_ATTR); } } - /* Skip 2 cache entries */ + // _CHECK_PEP_523 + { + if (IS_PEP523_HOOKED(tstate)) { + UPDATE_MISS_STATS(LOAD_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); + JUMP_TO_PREDICTED(LOAD_ATTR); + } + } // _LOAD_ATTR_PROPERTY_FRAME { + uint32_t func_version = read_u32(&this_instr[4].cache); PyObject *fget = read_obj(&this_instr[6].cache); assert((oparg & 1) == 0); assert(Py_IS_TYPE(fget, &PyFunction_Type)); PyFunctionObject *f = (PyFunctionObject *)fget; - PyCodeObject *code = (PyCodeObject *)f->func_code; - if ((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - if (code->co_kwonlyargcount) { - UPDATE_MISS_STATS(LOAD_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); - JUMP_TO_PREDICTED(LOAD_ATTR); - } - if (code->co_argcount != 1) { + if (f->func_version != func_version) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); JUMP_TO_PREDICTED(LOAD_ATTR); } + PyCodeObject *code = (PyCodeObject *)f->func_code; if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { UPDATE_MISS_STATS(LOAD_ATTR); assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR)); @@ -8710,10 +9043,10 @@ } // _PUSH_FRAME { - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); CALL_STAT_INC(inlined_py_calls); @@ -8721,13 +9054,14 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } TARGET(LOAD_ATTR_SLOT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_SLOT; (void)(opcode); #endif @@ -8739,6 +9073,8 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; + _PyStackRef o; + _PyStackRef value; _PyStackRef *null; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION @@ -8775,11 +9111,14 @@ attr = PyStackRef_FromPyObjectNew(attr_o); #endif STAT_INC(LOAD_ATTR, hit); + o = owner; + } + // _POP_TOP + { + value = o; + stack_pointer[-1] = attr; _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = owner; - owner = attr; - stack_pointer[-1] = owner; - PyStackRef_CLOSE(tmp); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); } /* Skip 5 cache entries */ @@ -8791,12 +9130,12 @@ } } stack_pointer += (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_ATTR_WITH_HINT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_ATTR_WITH_HINT; (void)(opcode); #endif @@ -8808,6 +9147,8 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; + _PyStackRef o; + _PyStackRef value; _PyStackRef *null; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION @@ -8887,9 +9228,14 @@ #else attr = PyStackRef_FromPyObjectNew(attr_o); #endif + o = owner; + } + // _POP_TOP + { + value = o; stack_pointer[-1] = attr; _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); } /* Skip 5 cache entries */ @@ -8901,12 +9247,12 @@ } } stack_pointer += (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_BUILD_CLASS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_BUILD_CLASS; (void)(opcode); #endif @@ -8914,9 +9260,9 @@ next_instr += 1; INSTRUCTION_STATS(LOAD_BUILD_CLASS); _PyStackRef bc; - PyObject *bc_o; + int err; _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc_o); + PyObject *bc_o = _PyMapping_GetOptionalItem2(BUILTINS(), &_Py_ID(__build_class__), &err); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { JUMP_TO_LABEL(error); @@ -8931,12 +9277,12 @@ bc = PyStackRef_FromPyObjectSteal(bc_o); stack_pointer[0] = bc; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_COMMON_CONSTANT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_COMMON_CONSTANT; (void)(opcode); #endif @@ -8945,15 +9291,15 @@ INSTRUCTION_STATS(LOAD_COMMON_CONSTANT); _PyStackRef value; assert(oparg < NUM_COMMON_CONSTANTS); - value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); + value = PyStackRef_DupImmortal(tstate->interp->common_consts[oparg]); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_CONST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_CONST; (void)(opcode); #endif @@ -8965,12 +9311,12 @@ value = PyStackRef_FromPyObjectBorrow(obj); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_DEREF) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_DEREF; (void)(opcode); #endif @@ -8985,7 +9331,7 @@ if (PyStackRef_IsNull(value)) { stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -8993,12 +9339,12 @@ } stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_FAST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_FAST; (void)(opcode); #endif @@ -9010,12 +9356,12 @@ value = PyStackRef_DUP(GETLOCAL(oparg)); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_FAST_AND_CLEAR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_FAST_AND_CLEAR; (void)(opcode); #endif @@ -9027,12 +9373,12 @@ GETLOCAL(oparg) = PyStackRef_NULL; stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_FAST_BORROW) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_FAST_BORROW; (void)(opcode); #endif @@ -9044,12 +9390,12 @@ value = PyStackRef_Borrow(GETLOCAL(oparg)); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_FAST_BORROW_LOAD_FAST_BORROW) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_FAST_BORROW_LOAD_FAST_BORROW; (void)(opcode); #endif @@ -9065,12 +9411,12 @@ stack_pointer[0] = value1; stack_pointer[1] = value2; stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_FAST_CHECK) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_FAST_CHECK; (void)(opcode); #endif @@ -9091,12 +9437,12 @@ value = PyStackRef_DUP(value_s); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_FAST_LOAD_FAST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_FAST_LOAD_FAST; (void)(opcode); #endif @@ -9112,12 +9458,12 @@ stack_pointer[0] = value1; stack_pointer[1] = value2; stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_FROM_DICT_OR_DEREF) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_FROM_DICT_OR_DEREF; (void)(opcode); #endif @@ -9127,14 +9473,14 @@ _PyStackRef class_dict_st; _PyStackRef value; class_dict_st = stack_pointer[-1]; - PyObject *value_o; PyObject *name; PyObject *class_dict = PyStackRef_AsPyObjectBorrow(class_dict_st); assert(class_dict); assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus); name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); + int err; _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyMapping_GetOptionalItem(class_dict, name, &value_o); + PyObject* value_o = _PyMapping_GetOptionalItem2(class_dict, name, &err); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { JUMP_TO_LABEL(error); @@ -9150,19 +9496,19 @@ } } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(class_dict_st); stack_pointer = _PyFrame_GetStackPointer(frame); value = PyStackRef_FromPyObjectSteal(value_o); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_FROM_DICT_OR_GLOBALS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_FROM_DICT_OR_GLOBALS; (void)(opcode); #endif @@ -9173,12 +9519,12 @@ _PyStackRef v; mod_or_class_dict = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *v_o; + int err; _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyMapping_GetOptionalItem(PyStackRef_AsPyObjectBorrow(mod_or_class_dict), name, &v_o); + PyObject *v_o = _PyMapping_GetOptionalItem2(PyStackRef_AsPyObjectBorrow(mod_or_class_dict), name, &err); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(mod_or_class_dict); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -9203,17 +9549,26 @@ } JUMP_TO_LABEL(error); } + if (PyLazyImport_CheckExact(v_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + Py_SETREF(v_o, l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (v_o == NULL) { + JUMP_TO_LABEL(error); + } + } } else { _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyMapping_GetOptionalItem(GLOBALS(), name, &v_o); + v_o = _PyMapping_GetOptionalItem2(GLOBALS(), name, &err); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { JUMP_TO_LABEL(error); } if (v_o == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyMapping_GetOptionalItem(BUILTINS(), name, &v_o); + v_o = _PyMapping_GetOptionalItem2(BUILTINS(), name, &err); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { JUMP_TO_LABEL(error); @@ -9227,17 +9582,26 @@ JUMP_TO_LABEL(error); } } + if (PyLazyImport_CheckExact(v_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + Py_SETREF(v_o, l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (v_o == NULL) { + JUMP_TO_LABEL(error); + } + } } } v = PyStackRef_FromPyObjectSteal(v_o); stack_pointer[0] = v; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_GLOBAL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_GLOBAL; (void)(opcode); #endif @@ -9253,7 +9617,7 @@ { uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg>>1); next_instr = this_instr; @@ -9264,7 +9628,7 @@ } OPCODE_DEFERRED_INC(LOAD_GLOBAL); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } /* Skip 1 cache entry */ /* Skip 1 cache entry */ @@ -9288,12 +9652,12 @@ } } stack_pointer += 1 + (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_GLOBAL_BUILTIN) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_GLOBAL_BUILTIN; (void)(opcode); #endif @@ -9321,7 +9685,7 @@ assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); JUMP_TO_PREDICTED(LOAD_GLOBAL); } - assert(DK_IS_UNICODE(keys)); + assert(keys->dk_kind == DICT_KEYS_UNICODE); } // _LOAD_GLOBAL_BUILTINS { @@ -9339,7 +9703,7 @@ assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); JUMP_TO_PREDICTED(LOAD_GLOBAL); } - assert(DK_IS_UNICODE(keys)); + assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); if (res_o == NULL) { @@ -9368,12 +9732,12 @@ } stack_pointer[0] = res; stack_pointer += 1 + (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_GLOBAL_MODULE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_GLOBAL_MODULE; (void)(opcode); #endif @@ -9405,7 +9769,7 @@ assert(_PyOpcode_Deopt[opcode] == (LOAD_GLOBAL)); JUMP_TO_PREDICTED(LOAD_GLOBAL); } - assert(DK_IS_UNICODE(keys)); + assert(keys->dk_kind == DICT_KEYS_UNICODE); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys); assert(index < DK_SIZE(keys)); PyObject *res_o = FT_ATOMIC_LOAD_PTR_RELAXED(entries[index].me_value); @@ -9435,12 +9799,12 @@ } stack_pointer[0] = res; stack_pointer += 1 + (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_LOCALS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_LOCALS; (void)(opcode); #endif @@ -9459,12 +9823,12 @@ locals = PyStackRef_FromPyObjectNew(l); stack_pointer[0] = locals; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_NAME) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_NAME; (void)(opcode); #endif @@ -9479,15 +9843,39 @@ if (v_o == NULL) { JUMP_TO_LABEL(error); } + if (PyLazyImport_CheckExact(v_o)) { + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *l_v = _PyImport_LoadLazyImportTstate(tstate, v_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (l_v == NULL) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(v_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = PyDict_SetItem(GLOBALS(), name, l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(v_o); + Py_DECREF(l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + JUMP_TO_LABEL(error); + } + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_SETREF(v_o, l_v); + stack_pointer = _PyFrame_GetStackPointer(frame); + } v = PyStackRef_FromPyObjectSteal(v_o); stack_pointer[0] = v; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_SMALL_INT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_SMALL_INT; (void)(opcode); #endif @@ -9500,12 +9888,12 @@ value = PyStackRef_FromPyObjectBorrow(obj); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_SPECIAL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_SPECIAL; (void)(opcode); #endif @@ -9526,7 +9914,7 @@ method_and_self = &stack_pointer[-1]; PyObject *name = _Py_SpecialMethods[oparg].name; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); int err = _PyObject_LookupSpecialMethod(name, method_and_self); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -9551,7 +9939,7 @@ } TARGET(LOAD_SUPER_ATTR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_SUPER_ATTR; (void)(opcode); #endif @@ -9573,7 +9961,7 @@ global_super_st = stack_pointer[-3]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION int load_method = oparg & 1; if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; @@ -9584,7 +9972,7 @@ } OPCODE_DEFERRED_INC(LOAD_SUPER_ATTR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } // _LOAD_SUPER_ATTR { @@ -9615,14 +10003,17 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_LABEL(error); } } - PyObject *stack[] = {class, self}; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *super; + { + PyObject *stack[] = {class, self}; + _PyFrame_SetStackPointer(frame, stack_pointer); + super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + } if (opcode == INSTRUMENTED_LOAD_SUPER_ATTR) { PyObject *arg = oparg & 2 ? class : &_PyInstrumentation_MISSING; if (super == NULL) { @@ -9660,7 +10051,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (super == NULL) { JUMP_TO_LABEL(error); } @@ -9683,12 +10074,12 @@ } stack_pointer[0] = attr; stack_pointer += 1 + (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_SUPER_ATTR_ATTR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_SUPER_ATTR_ATTR; (void)(opcode); #endif @@ -9738,19 +10129,19 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (attr == NULL) { JUMP_TO_LABEL(error); } attr_st = PyStackRef_FromPyObjectSteal(attr); stack_pointer[0] = attr_st; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(LOAD_SUPER_ATTR_METHOD) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = LOAD_SUPER_ATTR_METHOD; (void)(opcode); #endif @@ -9766,69 +10157,80 @@ _PyStackRef attr; _PyStackRef self_or_null; /* Skip 1 cache entry */ - self_st = stack_pointer[-1]; - class_st = stack_pointer[-2]; - global_super_st = stack_pointer[-3]; - PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); - PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); - PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); - assert(oparg & 1); - if (global_super != (PyObject *)&PySuper_Type) { - UPDATE_MISS_STATS(LOAD_SUPER_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); - JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); - } - if (!PyType_Check(class)) { - UPDATE_MISS_STATS(LOAD_SUPER_ATTR); - assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); - JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); - } - STAT_INC(LOAD_SUPER_ATTR, hit); - PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); - PyTypeObject *cls = (PyTypeObject *)class; - int method_found = 0; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *attr_o = _PySuper_Lookup(cls, self, name, - Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (attr_o == NULL) { - JUMP_TO_LABEL(error); + // _GUARD_LOAD_SUPER_ATTR_METHOD + { + class_st = stack_pointer[-2]; + global_super_st = stack_pointer[-3]; + PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + assert(oparg & 1); + if (global_super != (PyObject *)&PySuper_Type) { + UPDATE_MISS_STATS(LOAD_SUPER_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); + JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); + } + if (!PyType_Check(class)) { + UPDATE_MISS_STATS(LOAD_SUPER_ATTR); + assert(_PyOpcode_Deopt[opcode] == (LOAD_SUPER_ATTR)); + JUMP_TO_PREDICTED(LOAD_SUPER_ATTR); + } } - if (method_found) { - self_or_null = self_st; - } else { + // _LOAD_SUPER_ATTR_METHOD + { + self_st = stack_pointer[-1]; + PyObject *class = PyStackRef_AsPyObjectBorrow(class_st); + PyObject *self = PyStackRef_AsPyObjectBorrow(self_st); + STAT_INC(LOAD_SUPER_ATTR, hit); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2); + PyTypeObject *cls = (PyTypeObject *)class; + int method_found = 0; + PyObject *attr_o; + { + int *method_found_ptr = &method_found; + _PyFrame_SetStackPointer(frame, stack_pointer); + attr_o = _PySuper_Lookup(cls, self, name, + Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? method_found_ptr : NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + if (attr_o == NULL) { + JUMP_TO_LABEL(error); + } + if (method_found) { + self_or_null = self_st; + } else { + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(self_st); + stack_pointer = _PyFrame_GetStackPointer(frame); + self_or_null = PyStackRef_NULL; + stack_pointer += 1; + } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(self_st); + _PyStackRef tmp = global_super_st; + global_super_st = self_or_null; + stack_pointer[-2] = global_super_st; + PyStackRef_CLOSE(tmp); + tmp = class_st; + class_st = PyStackRef_NULL; + stack_pointer[-1] = class_st; + PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); - self_or_null = PyStackRef_NULL; - stack_pointer += 1; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + attr = PyStackRef_FromPyObjectSteal(attr_o); } - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = global_super_st; - global_super_st = self_or_null; - stack_pointer[-2] = global_super_st; - PyStackRef_CLOSE(tmp); - tmp = class_st; - class_st = PyStackRef_NULL; - stack_pointer[-1] = class_st; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); - attr = PyStackRef_FromPyObjectSteal(attr_o); stack_pointer[0] = attr; stack_pointer[1] = self_or_null; stack_pointer += 2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(MAKE_CELL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = MAKE_CELL; (void)(opcode); #endif @@ -9849,7 +10251,7 @@ } TARGET(MAKE_FUNCTION) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = MAKE_FUNCTION; (void)(opcode); #endif @@ -9858,31 +10260,37 @@ INSTRUCTION_STATS(MAKE_FUNCTION); _PyStackRef codeobj_st; _PyStackRef func; - codeobj_st = stack_pointer[-1]; - PyObject *codeobj = PyStackRef_AsPyObjectBorrow(codeobj_st); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyFunctionObject *func_obj = (PyFunctionObject *) - PyFunction_New(codeobj, GLOBALS()); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(codeobj_st); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (func_obj == NULL) { - JUMP_TO_LABEL(error); - } - _PyFunction_SetVersion( + _PyStackRef co; + _PyStackRef value; + // _MAKE_FUNCTION + { + codeobj_st = stack_pointer[-1]; + PyObject *codeobj = PyStackRef_AsPyObjectBorrow(codeobj_st); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyFunctionObject *func_obj = (PyFunctionObject *) + PyFunction_New(codeobj, GLOBALS()); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (func_obj == NULL) { + JUMP_TO_LABEL(error); + } + co = codeobj_st; + _PyFunction_SetVersion( func_obj, ((PyCodeObject *)codeobj)->co_version); - func = PyStackRef_FromPyObjectSteal((PyObject *)func_obj); - stack_pointer[0] = func; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + func = PyStackRef_FromPyObjectSteal((PyObject *)func_obj); + } + // _POP_TOP + { + value = co; + stack_pointer[-1] = func; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } DISPATCH(); } TARGET(MAP_ADD) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = MAP_ADD; (void)(opcode); #endif @@ -9908,12 +10316,12 @@ JUMP_TO_LABEL(pop_2_error); } stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(MATCH_CLASS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = MATCH_CLASS; (void)(opcode); #endif @@ -9924,48 +10332,69 @@ _PyStackRef type; _PyStackRef names; _PyStackRef attrs; - names = stack_pointer[-1]; - type = stack_pointer[-2]; - subject = stack_pointer[-3]; - assert(PyTuple_CheckExact(PyStackRef_AsPyObjectBorrow(names))); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *attrs_o = _PyEval_MatchClass(tstate, - PyStackRef_AsPyObjectBorrow(subject), - PyStackRef_AsPyObjectBorrow(type), oparg, - PyStackRef_AsPyObjectBorrow(names)); - _PyStackRef tmp = names; - names = PyStackRef_NULL; - stack_pointer[-1] = names; - PyStackRef_CLOSE(tmp); - tmp = type; - type = PyStackRef_NULL; - stack_pointer[-2] = type; - PyStackRef_CLOSE(tmp); - tmp = subject; - subject = PyStackRef_NULL; - stack_pointer[-3] = subject; - PyStackRef_CLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - if (attrs_o) { - assert(PyTuple_CheckExact(attrs_o)); - attrs = PyStackRef_FromPyObjectSteal(attrs_o); - } - else { - if (_PyErr_Occurred(tstate)) { - JUMP_TO_LABEL(error); + _PyStackRef s; + _PyStackRef tp; + _PyStackRef n; + _PyStackRef value; + // _MATCH_CLASS + { + names = stack_pointer[-1]; + type = stack_pointer[-2]; + subject = stack_pointer[-3]; + assert(PyTuple_CheckExact(PyStackRef_AsPyObjectBorrow(names))); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *attrs_o = _PyEval_MatchClass(tstate, + PyStackRef_AsPyObjectBorrow(subject), + PyStackRef_AsPyObjectBorrow(type), oparg, + PyStackRef_AsPyObjectBorrow(names)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (attrs_o) { + assert(PyTuple_CheckExact(attrs_o)); + attrs = PyStackRef_FromPyObjectSteal(attrs_o); + } + else { + if (_PyErr_Occurred(tstate)) { + JUMP_TO_LABEL(error); + } + attrs = PyStackRef_None; } - attrs = PyStackRef_None; + s = subject; + tp = type; + n = names; + } + // _POP_TOP + { + value = n; + stack_pointer[-3] = attrs; + stack_pointer[-2] = s; + stack_pointer[-1] = tp; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = tp; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + // _POP_TOP + { + value = s; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - stack_pointer[0] = attrs; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(MATCH_KEYS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = MATCH_KEYS; (void)(opcode); #endif @@ -9987,12 +10416,12 @@ values_or_none = PyStackRef_FromPyObjectSteal(values_or_none_o); stack_pointer[0] = values_or_none; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(MATCH_MAPPING) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = MATCH_MAPPING; (void)(opcode); #endif @@ -10006,12 +10435,12 @@ res = match ? PyStackRef_True : PyStackRef_False; stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(MATCH_SEQUENCE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = MATCH_SEQUENCE; (void)(opcode); #endif @@ -10025,12 +10454,12 @@ res = match ? PyStackRef_True : PyStackRef_False; stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(NOP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = NOP; (void)(opcode); #endif @@ -10041,7 +10470,7 @@ } TARGET(NOT_TAKEN) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = NOT_TAKEN; (void)(opcode); #endif @@ -10052,7 +10481,7 @@ } TARGET(POP_EXCEPT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = POP_EXCEPT; (void)(opcode); #endif @@ -10068,12 +10497,12 @@ ? NULL : PyStackRef_AsPyObjectSteal(exc_value)); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(POP_ITER) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = POP_ITER; (void)(opcode); #endif @@ -10086,7 +10515,7 @@ iter = stack_pointer[-2]; (void)index_or_null; stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(iter); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -10094,7 +10523,7 @@ } TARGET(POP_JUMP_IF_FALSE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = POP_JUMP_IF_FALSE; (void)(opcode); #endif @@ -10111,12 +10540,12 @@ RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(POP_JUMP_IF_NONE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = POP_JUMP_IF_NONE; (void)(opcode); #endif @@ -10154,12 +10583,12 @@ JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(POP_JUMP_IF_NOT_NONE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = POP_JUMP_IF_NOT_NONE; (void)(opcode); #endif @@ -10197,12 +10626,12 @@ JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(POP_JUMP_IF_TRUE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = POP_JUMP_IF_TRUE; (void)(opcode); #endif @@ -10219,12 +10648,12 @@ RECORD_BRANCH_TAKEN(this_instr[1].cache, flag); JUMPBY(flag ? oparg : next_instr->op.code == NOT_TAKEN); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(POP_TOP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = POP_TOP; (void)(opcode); #endif @@ -10234,7 +10663,7 @@ _PyStackRef value; value = stack_pointer[-1]; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -10242,7 +10671,7 @@ } TARGET(PUSH_EXC_INFO) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = PUSH_EXC_INFO; (void)(opcode); #endif @@ -10266,12 +10695,12 @@ stack_pointer[-1] = prev_exc; stack_pointer[0] = new_exc; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(PUSH_NULL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = PUSH_NULL; (void)(opcode); #endif @@ -10282,12 +10711,12 @@ res = PyStackRef_NULL; stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(RAISE_VARARGS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = RAISE_VARARGS; (void)(opcode); #endif @@ -10302,7 +10731,7 @@ PyObject *cause = oparg == 2 ? PyStackRef_AsPyObjectSteal(args[1]) : NULL; PyObject *exc = oparg > 0 ? PyStackRef_AsPyObjectSteal(args[0]) : NULL; stack_pointer += -oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); int err = do_raise(tstate, exc, cause); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -10316,7 +10745,7 @@ } TARGET(RERAISE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = RERAISE; (void)(opcode); #endif @@ -10336,7 +10765,7 @@ } assert(exc && PyExceptionInstance_Check(exc)); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); _PyErr_SetRaisedException(tstate, exc); monitor_reraise(tstate, frame, this_instr); @@ -10344,7 +10773,7 @@ } TARGET(RESERVED) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = RESERVED; (void)(opcode); #endif @@ -10357,15 +10786,15 @@ } TARGET(RESUME) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = RESUME; (void)(opcode); #endif frame->instr_ptr = next_instr; - next_instr += 1; + next_instr += 2; INSTRUCTION_STATS(RESUME); PREDICTED_RESUME:; - _Py_CODEUNIT* const this_instr = next_instr - 1; + _Py_CODEUNIT* const this_instr = next_instr - 2; (void)this_instr; // _LOAD_BYTECODE { @@ -10389,7 +10818,13 @@ } // _MAYBE_INSTRUMENT { - if (tstate->tracing == 0) { + #ifdef Py_GIL_DISABLED + + int check_instrumentation = 1; + #else + int check_instrumentation = (tstate->tracing == 0); + #endif + if (check_instrumentation) { uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & ~_PY_EVAL_EVENTS_MASK; uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); if (code_version != global_version) { @@ -10406,24 +10841,20 @@ } // _QUICKEN_RESUME { - #if ENABLE_SPECIALIZATION_FT - if (tstate->tracing == 0 && this_instr->op.code == RESUME) { - FT_ATOMIC_STORE_UINT8_RELAXED(this_instr->op.code, RESUME_CHECK); - } - #endif /* ENABLE_SPECIALIZATION_FT */ + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_Specialize_Resume(this_instr, tstate, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); } // _CHECK_PERIODIC_IF_NOT_YIELD_FROM { if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) { - _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); - QSBR_QUIESCENT_STATE(tstate); - if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _Py_HandlePending(tstate); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err != 0) { - JUMP_TO_LABEL(error); - } + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = check_periodics(tstate); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err != 0) { + JUMP_TO_LABEL(error); } } } @@ -10431,16 +10862,17 @@ } TARGET(RESUME_CHECK) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = RESUME_CHECK; (void)(opcode); #endif _Py_CODEUNIT* const this_instr = next_instr; (void)this_instr; frame->instr_ptr = next_instr; - next_instr += 1; + next_instr += 2; INSTRUCTION_STATS(RESUME_CHECK); - static_assert(0 == 0, "incorrect cache size"); + static_assert(1 == 1, "incorrect cache size"); + /* Skip 1 cache entry */ #if defined(__EMSCRIPTEN__) if (_Py_emscripten_signal_clock == 0) { UPDATE_MISS_STATS(RESUME); @@ -10468,8 +10900,78 @@ DISPATCH(); } + TARGET(RESUME_CHECK_JIT) { + #if _Py_TAIL_CALL_INTERP + int opcode = RESUME_CHECK_JIT; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(RESUME_CHECK_JIT); + static_assert(1 == 1, "incorrect cache size"); + /* Skip 1 cache entry */ + // _RESUME_CHECK + { + #if defined(__EMSCRIPTEN__) + if (_Py_emscripten_signal_clock == 0) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } + _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; + #endif + uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker); + uintptr_t version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version); + assert((version & _PY_EVAL_EVENTS_MASK) == 0); + if (eval_breaker != version) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } + #ifdef Py_GIL_DISABLED + if (frame->tlbc_index != + ((_PyThreadStateImpl *)tstate)->tlbc_index) { + UPDATE_MISS_STATS(RESUME); + assert(_PyOpcode_Deopt[opcode] == (RESUME)); + JUMP_TO_PREDICTED(RESUME); + } + #endif + } + // _JIT + { + #ifdef _Py_TIER2 + bool is_resume = this_instr->op.code == RESUME_CHECK_JIT; + _Py_BackoffCounter counter = this_instr[1].counter; + if ((backoff_counter_triggers(counter) && + !IS_JIT_TRACING() && + (this_instr->op.code == JUMP_BACKWARD_JIT || is_resume)) && + next_instr->op.code != ENTER_EXECUTOR) { + _Py_CODEUNIT *insert_exec_at = this_instr; + while (oparg > 255) { + oparg >>= 8; + insert_exec_at--; + } + int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, + is_resume ? insert_exec_at : next_instr, stack_pointer, 0, NULL, oparg, NULL); + if (succ) { + ENTER_TRACING(); + } + else { + this_instr[1].counter = restart_backoff_counter(counter); + } + } + else { + ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); + } + #endif + } + DISPATCH(); + } + TARGET(RETURN_GENERATOR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = RETURN_GENERATOR; (void)(opcode); #endif @@ -10485,7 +10987,7 @@ if (gen == NULL) { JUMP_TO_LABEL(error); } - assert(STACK_LEVEL() == 0); + assert(STACK_LEVEL() <= 2); _PyFrame_SetStackPointer(frame, stack_pointer); _PyInterpreterFrame *gen_frame = &gen->gi_iframe; frame->instr_ptr++; @@ -10503,43 +11005,53 @@ LLTRACE_RESUME_FRAME(); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(RETURN_VALUE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = RETURN_VALUE; (void)(opcode); #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(RETURN_VALUE); + _PyStackRef value; _PyStackRef retval; _PyStackRef res; - retval = stack_pointer[-1]; - assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - _PyStackRef temp = PyStackRef_MakeHeapSafe(retval); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - assert(STACK_LEVEL() == 0); - _Py_LeaveRecursiveCallPy(tstate); - _PyInterpreterFrame *dying = frame; - frame = tstate->current_frame = dying->previous; - _PyEval_FrameClearAndPop(tstate, dying); - stack_pointer = _PyFrame_GetStackPointer(frame); - LOAD_IP(frame->return_offset); - res = temp; - LLTRACE_RESUME_FRAME(); + // _MAKE_HEAP_SAFE + { + value = stack_pointer[-1]; + value = PyStackRef_MakeHeapSafe(value); + } + // _RETURN_VALUE + { + retval = value; + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + _PyStackRef temp = retval; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(STACK_LEVEL() == 0); + DTRACE_FUNCTION_RETURN(); + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + stack_pointer = _PyFrame_GetStackPointer(frame); + LOAD_IP(frame->return_offset); + res = temp; + LLTRACE_RESUME_FRAME(); + } stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(SEND) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = SEND; (void)(opcode); #endif @@ -10550,14 +11062,15 @@ _Py_CODEUNIT* const this_instr = next_instr - 2; (void)this_instr; _PyStackRef receiver; + _PyStackRef null_or_index; _PyStackRef v; _PyStackRef retval; // _SPECIALIZE_SEND { - receiver = stack_pointer[-2]; + receiver = stack_pointer[-3]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -10567,84 +11080,134 @@ } OPCODE_DEFERRED_INC(SEND); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } // _SEND { v = stack_pointer[-1]; + null_or_index = stack_pointer[-2]; PyObject *receiver_o = PyStackRef_AsPyObjectBorrow(receiver); - PyObject *retval_o; assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - if ((tstate->interp->eval_frame == NULL) && + if (!IS_PEP523_HOOKED(tstate) && (Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) && - ((PyGenObject *)receiver_o)->gi_frame_state < FRAME_EXECUTING) + gen_try_set_executing((PyGenObject *)receiver_o)) { PyGenObject *gen = (PyGenObject *)receiver_o; _PyInterpreterFrame *gen_frame = &gen->gi_iframe; _PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v)); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - gen->gi_frame_state = FRAME_EXECUTING; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; - assert( 2 + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)( 2 + oparg); + assert( 2u + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)( 2u + oparg); assert(gen_frame->previous == NULL); gen_frame->previous = frame; DISPATCH_INLINED(gen_frame); } - if (PyStackRef_IsNone(v) && PyIter_Check(receiver_o)) { + if (!PyStackRef_IsNull(null_or_index) && PyStackRef_IsNone(v)) { _PyFrame_SetStackPointer(frame, stack_pointer); - retval_o = Py_TYPE(receiver_o)->tp_iternext(receiver_o); + _PyStackRef item = _PyForIter_VirtualIteratorNext(tstate, frame, receiver, &null_or_index); stack_pointer = _PyFrame_GetStackPointer(frame); + if (!PyStackRef_IsValid(item)) { + if (PyStackRef_IsError(item)) { + JUMP_TO_LABEL(error); + } + JUMPBY(oparg); + stack_pointer[-2] = null_or_index; + DISPATCH(); + } + retval = item; } else { + PyObject *v_o = PyStackRef_AsPyObjectBorrow(v); _PyFrame_SetStackPointer(frame, stack_pointer); - retval_o = PyObject_CallMethodOneArg(receiver_o, - &_Py_ID(send), - PyStackRef_AsPyObjectBorrow(v)); - stack_pointer = _PyFrame_GetStackPointer(frame); - } - if (retval_o == NULL) { - _PyFrame_SetStackPointer(frame, stack_pointer); - int matches = _PyErr_ExceptionMatches(tstate, PyExc_StopIteration); + PySendResultPair res = _PyIter_Send(receiver_o, v_o); stack_pointer = _PyFrame_GetStackPointer(frame); - if (matches) { - _PyFrame_SetStackPointer(frame, stack_pointer); - _PyEval_MonitorRaise(tstate, frame, this_instr); - stack_pointer = _PyFrame_GetStackPointer(frame); + if (res.kind == PYGEN_ERROR) { + JUMP_TO_LABEL(error); } + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PyGen_FetchStopIterationValue(&retval_o); + PyStackRef_CLOSE(v); stack_pointer = _PyFrame_GetStackPointer(frame); - if (err == 0) { - assert(retval_o != NULL); + retval = PyStackRef_FromPyObjectSteal(res.object); + if (res.kind == PYGEN_RETURN) { JUMPBY(oparg); } - else { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(v); - stack_pointer = _PyFrame_GetStackPointer(frame); - JUMP_TO_LABEL(error); - } + stack_pointer += 1; + } + } + stack_pointer[-2] = null_or_index; + stack_pointer[-1] = retval; + DISPATCH(); + } + + TARGET(SEND_ASYNC_GEN) { + #if _Py_TAIL_CALL_INTERP + int opcode = SEND_ASYNC_GEN; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(SEND_ASYNC_GEN); + static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); + _PyStackRef iter; + _PyStackRef null_in; + _PyStackRef v; + _PyStackRef asend; + _PyStackRef null_out; + _PyStackRef retval; + /* Skip 1 cache entry */ + // _GUARD_3OS_ASYNC_GEN_ASEND + { + iter = stack_pointer[-3]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + if (!PyAsyncGenASend_CheckExact(iter_o)) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } + } + // _SEND_ASYNC_GEN + { + v = stack_pointer[-1]; + null_in = stack_pointer[-2]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + assert(PyAsyncGenASend_CheckExact(iter_o)); + PyObject *val = PyStackRef_AsPyObjectBorrow(v); + PyObject *retval_o; + _PyFrame_SetStackPointer(frame, stack_pointer); + PySendResult what = _PyAsyncGenASend_Send(iter_o, val, &retval_o); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (what == PYGEN_ERROR) { + JUMP_TO_LABEL(error); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(v); stack_pointer = _PyFrame_GetStackPointer(frame); + asend = iter; + null_out = null_in; retval = PyStackRef_FromPyObjectSteal(retval_o); + if (what == PYGEN_RETURN) { + JUMPBY(oparg); + } } + stack_pointer[-2] = asend; + stack_pointer[-1] = null_out; stack_pointer[0] = retval; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(SEND_GEN) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = SEND_GEN; (void)(opcode); #endif @@ -10661,7 +11224,7 @@ /* Skip 1 cache entry */ // _CHECK_PEP_523 { - if (tstate->interp->eval_frame) { + if (IS_PEP523_HOOKED(tstate)) { UPDATE_MISS_STATS(SEND); assert(_PyOpcode_Deopt[opcode] == (SEND)); JUMP_TO_PREDICTED(SEND); @@ -10670,14 +11233,14 @@ // _SEND_GEN_FRAME { v = stack_pointer[-1]; - receiver = stack_pointer[-2]; + receiver = stack_pointer[-3]; PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver); if (Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type) { UPDATE_MISS_STATS(SEND); assert(_PyOpcode_Deopt[opcode] == (SEND)); JUMP_TO_PREDICTED(SEND); } - if (gen->gi_frame_state >= FRAME_EXECUTING) { + if (!gen_try_set_executing((PyGenObject *)gen)) { UPDATE_MISS_STATS(SEND); assert(_PyOpcode_Deopt[opcode] == (SEND)); JUMP_TO_PREDICTED(SEND); @@ -10685,21 +11248,20 @@ STAT_INC(SEND, hit); _PyInterpreterFrame *pushed_frame = &gen->gi_iframe; _PyFrame_StackPush(pushed_frame, PyStackRef_MakeHeapSafe(v)); - gen->gi_frame_state = FRAME_EXECUTING; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; - assert( 2 + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)( 2 + oparg); + assert( 2u + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)( 2u + oparg); pushed_frame->previous = frame; gen_frame = PyStackRef_Wrap(pushed_frame); } // _PUSH_FRAME { new_frame = gen_frame; - assert(tstate->interp->eval_frame == NULL); + assert(!IS_PEP523_HOOKED(tstate)); _PyInterpreterFrame *temp = PyStackRef_Unwrap(new_frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); assert(temp->previous == frame || temp->previous->previous == frame); CALL_STAT_INC(inlined_py_calls); @@ -10707,20 +11269,84 @@ tstate->py_recursion_remaining--; LOAD_SP(); LOAD_IP(0); + DTRACE_FUNCTION_ENTRY(); LLTRACE_RESUME_FRAME(); } DISPATCH(); } + TARGET(SEND_VIRTUAL) { + #if _Py_TAIL_CALL_INTERP + int opcode = SEND_VIRTUAL; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 2; + INSTRUCTION_STATS(SEND_VIRTUAL); + static_assert(INLINE_CACHE_ENTRIES_SEND == 1, "incorrect cache size"); + _PyStackRef val; + _PyStackRef nos; + _PyStackRef iter; + _PyStackRef null_or_index; + _PyStackRef none; + _PyStackRef next; + /* Skip 1 cache entry */ + // _GUARD_TOS_IS_NONE + { + val = stack_pointer[-1]; + if (!PyStackRef_IsNone(val)) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } + } + // _GUARD_NOS_NOT_NULL + { + nos = stack_pointer[-2]; + if (PyStackRef_IsNull(nos)) { + UPDATE_MISS_STATS(SEND); + assert(_PyOpcode_Deopt[opcode] == (SEND)); + JUMP_TO_PREDICTED(SEND); + } + } + // _SEND_VIRTUAL + { + none = val; + null_or_index = nos; + iter = stack_pointer[-3]; + PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); + Py_ssize_t index = PyStackRef_UntagInt(null_or_index); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyObjectIndexPair next_index = Py_TYPE(iter_o)->_tp_iteritem(iter_o, index); + stack_pointer = _PyFrame_GetStackPointer(frame); + PyObject *next_o = next_index.object; + index = next_index.index; + if (next_o == NULL) { + if (index < 0) { + JUMP_TO_LABEL(error); + } + next = none; + JUMPBY(oparg); + DISPATCH(); + } + next = PyStackRef_FromPyObjectSteal(next_o); + null_or_index = PyStackRef_TagInt(index); + } + stack_pointer[-2] = null_or_index; + stack_pointer[-1] = next; + DISPATCH(); + } + TARGET(SETUP_ANNOTATIONS) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = SETUP_ANNOTATIONS; (void)(opcode); #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(SETUP_ANNOTATIONS); - PyObject *ann_dict; if (LOCALS() == NULL) { _PyFrame_SetStackPointer(frame, stack_pointer); _PyErr_Format(tstate, PyExc_SystemError, @@ -10728,8 +11354,9 @@ stack_pointer = _PyFrame_GetStackPointer(frame); JUMP_TO_LABEL(error); } + int err; _PyFrame_SetStackPointer(frame, stack_pointer); - int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict); + PyObject* ann_dict = _PyMapping_GetOptionalItem2(LOCALS(), &_Py_ID(__annotations__), &err); stack_pointer = _PyFrame_GetStackPointer(frame); if (err < 0) { JUMP_TO_LABEL(error); @@ -10759,7 +11386,7 @@ } TARGET(SET_ADD) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = SET_ADD; (void)(opcode); #endif @@ -10778,12 +11405,12 @@ JUMP_TO_LABEL(pop_1_error); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(SET_FUNCTION_ATTRIBUTE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = SET_FUNCTION_ATTRIBUTE; (void)(opcode); #endif @@ -10806,12 +11433,12 @@ *ptr = attr; stack_pointer[-2] = func_out; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(SET_UPDATE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = SET_UPDATE; (void)(opcode); #endif @@ -10820,25 +11447,35 @@ INSTRUCTION_STATS(SET_UPDATE); _PyStackRef set; _PyStackRef iterable; - iterable = stack_pointer[-1]; - set = stack_pointer[-2 - (oparg-1)]; - _PyFrame_SetStackPointer(frame, stack_pointer); - int err = _PySet_Update(PyStackRef_AsPyObjectBorrow(set), + _PyStackRef i; + _PyStackRef value; + // _SET_UPDATE + { + iterable = stack_pointer[-1]; + set = stack_pointer[-2 - (oparg-1)]; + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = _PySet_Update(PyStackRef_AsPyObjectBorrow(set), PyStackRef_AsPyObjectBorrow(iterable)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(iterable); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (err < 0) { - JUMP_TO_LABEL(error); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + i = iterable; + } + // _POP_TOP + { + value = i; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } DISPATCH(); } TARGET(STORE_ATTR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_ATTR; (void)(opcode); #endif @@ -10855,7 +11492,7 @@ owner = stack_pointer[-1]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); next_instr = this_instr; @@ -10866,7 +11503,7 @@ } OPCODE_DEFERRED_INC(STORE_ATTR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } /* Skip 3 cache entries */ // _STORE_ATTR @@ -10886,7 +11523,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (err) { JUMP_TO_LABEL(error); } @@ -10895,7 +11532,7 @@ } TARGET(STORE_ATTR_INSTANCE_VALUE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_ATTR_INSTANCE_VALUE; (void)(opcode); #endif @@ -10905,20 +11542,25 @@ next_instr += 5; INSTRUCTION_STATS(STORE_ATTR_INSTANCE_VALUE); static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); - _PyStackRef owner; _PyStackRef value; + _PyStackRef owner; + _PyStackRef o; /* Skip 1 cache entry */ - // _GUARD_TYPE_VERSION_AND_LOCK + // _LOCK_OBJECT { - owner = stack_pointer[-1]; - uint32_t type_version = read_u32(&this_instr[2].cache); - PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); - assert(type_version != 0); - if (!LOCK_OBJECT(owner_o)) { + value = stack_pointer[-1]; + if (!LOCK_OBJECT(PyStackRef_AsPyObjectBorrow(value))) { UPDATE_MISS_STATS(STORE_ATTR); assert(_PyOpcode_Deopt[opcode] == (STORE_ATTR)); JUMP_TO_PREDICTED(STORE_ATTR); } + } + // _GUARD_TYPE_VERSION_LOCKED + { + owner = value; + uint32_t type_version = read_u32(&this_instr[2].cache); + PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); + assert(type_version != 0); PyTypeObject *tp = Py_TYPE(owner_o); if (FT_ATOMIC_LOAD_UINT_RELAXED(tp->tp_version_tag) != type_version) { UNLOCK_OBJECT(owner_o); @@ -10960,18 +11602,28 @@ _PyDictValues_AddToInsertionOrder(values, index); } UNLOCK_OBJECT(owner_o); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + o = owner; + stack_pointer[-2] = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); } + // _POP_TOP + { + value = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } DISPATCH(); } TARGET(STORE_ATTR_SLOT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_ATTR_SLOT; (void)(opcode); #endif @@ -10983,6 +11635,7 @@ static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); _PyStackRef owner; _PyStackRef value; + _PyStackRef o; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION { @@ -11011,18 +11664,28 @@ PyObject *old_value = *(PyObject **)addr; FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(owner_o); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + o = owner; + stack_pointer[-2] = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); } + // _POP_TOP + { + value = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } DISPATCH(); } TARGET(STORE_ATTR_WITH_HINT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_ATTR_WITH_HINT; (void)(opcode); #endif @@ -11034,6 +11697,7 @@ static_assert(INLINE_CACHE_ENTRIES_STORE_ATTR == 4, "incorrect cache size"); _PyStackRef owner; _PyStackRef value; + _PyStackRef o; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION { @@ -11067,7 +11731,7 @@ assert(PyDict_CheckExact((PyObject *)dict)); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); if (hint >= (size_t)dict->ma_keys->dk_nentries || - !DK_IS_UNICODE(dict->ma_keys)) { + dict->ma_keys->dk_kind != DICT_KEYS_UNICODE) { UNLOCK_OBJECT(dict); if (true) { UPDATE_MISS_STATS(STORE_ATTR); @@ -11094,23 +11758,33 @@ } } _PyFrame_SetStackPointer(frame, stack_pointer); - _PyDict_NotifyEvent(tstate->interp, PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); + _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value)); UNLOCK_OBJECT(dict); STAT_INC(STORE_ATTR, hit); - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + o = owner; + stack_pointer[-2] = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(owner); Py_XDECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); } + // _POP_TOP + { + value = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } DISPATCH(); } TARGET(STORE_DEREF) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_DEREF; (void)(opcode); #endif @@ -11124,32 +11798,41 @@ PyCell_SetTakeRef(cell, PyStackRef_AsPyObjectSteal(v)); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(STORE_FAST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_FAST; (void)(opcode); #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(STORE_FAST); - _PyStackRef value; - value = stack_pointer[-1]; - _PyStackRef tmp = GETLOCAL(oparg); - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_XCLOSE(tmp); - stack_pointer = _PyFrame_GetStackPointer(frame); + _PyStackRef value; + _PyStackRef trash; + // _SWAP_FAST + { + value = stack_pointer[-1]; + _PyStackRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = value; + trash = tmp; + } + // _POP_TOP + { + value = trash; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } DISPATCH(); } TARGET(STORE_FAST_LOAD_FAST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_FAST_LOAD_FAST; (void)(opcode); #endif @@ -11172,7 +11855,7 @@ } TARGET(STORE_FAST_STORE_FAST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_FAST_STORE_FAST; (void)(opcode); #endif @@ -11188,14 +11871,14 @@ _PyStackRef tmp = GETLOCAL(oparg1); GETLOCAL(oparg1) = value1; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_XCLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); tmp = GETLOCAL(oparg2); GETLOCAL(oparg2) = value2; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_XCLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -11203,7 +11886,7 @@ } TARGET(STORE_GLOBAL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_GLOBAL; (void)(opcode); #endif @@ -11217,7 +11900,7 @@ int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(v); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -11228,7 +11911,7 @@ } TARGET(STORE_NAME) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_NAME; (void)(opcode); #endif @@ -11246,7 +11929,7 @@ "no locals found when storing %R", name); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(v); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -11263,7 +11946,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); } stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(v); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -11274,7 +11957,7 @@ } TARGET(STORE_SLICE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_SLICE; (void)(opcode); #endif @@ -11299,7 +11982,8 @@ v = stack_pointer[-4]; _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *slice = _PyBuildSlice_ConsumeRefs(PyStackRef_AsPyObjectSteal(start), - PyStackRef_AsPyObjectSteal(stop)); + PyStackRef_AsPyObjectSteal(stop), + Py_None); stack_pointer = _PyFrame_GetStackPointer(frame); int err; if (slice == NULL) { @@ -11307,7 +11991,7 @@ } else { stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), slice, PyStackRef_AsPyObjectBorrow(v)); Py_DECREF(slice); @@ -11325,7 +12009,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -4; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (err) { JUMP_TO_LABEL(error); } @@ -11334,7 +12018,7 @@ } TARGET(STORE_SUBSCR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_SUBSCR; (void)(opcode); #endif @@ -11353,7 +12037,7 @@ container = stack_pointer[-2]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -11363,7 +12047,7 @@ } OPCODE_DEFERRED_INC(STORE_SUBSCR); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } // _STORE_SUBSCR { @@ -11384,7 +12068,7 @@ PyStackRef_CLOSE(tmp); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); if (err) { JUMP_TO_LABEL(error); } @@ -11393,7 +12077,7 @@ } TARGET(STORE_SUBSCR_DICT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_SUBSCR_DICT; (void)(opcode); #endif @@ -11407,11 +12091,17 @@ _PyStackRef value; _PyStackRef dict_st; _PyStackRef sub; - // _GUARD_NOS_DICT + _PyStackRef st; + // _GUARD_NOS_DICT_STORE_SUBSCRIPT { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!PyDict_CheckExact(o)) { + if (!Py_TYPE(o)->tp_as_mapping) { + UPDATE_MISS_STATS(STORE_SUBSCR); + assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); + JUMP_TO_PREDICTED(STORE_SUBSCR); + } + if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { UPDATE_MISS_STATS(STORE_SUBSCR); assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); JUMP_TO_PREDICTED(STORE_SUBSCR); @@ -11424,27 +12114,37 @@ dict_st = nos; value = stack_pointer[-3]; PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); - assert(PyDict_CheckExact(dict)); + assert(Py_TYPE(dict)->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript); STAT_INC(STORE_SUBSCR, hit); _PyFrame_SetStackPointer(frame, stack_pointer); int err = _PyDict_SetItem_Take2((PyDictObject *)dict, PyStackRef_AsPyObjectSteal(sub), PyStackRef_AsPyObjectSteal(value)); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(dict_st); - stack_pointer = _PyFrame_GetStackPointer(frame); if (err) { + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_CLOSE(dict_st); + stack_pointer = _PyFrame_GetStackPointer(frame); JUMP_TO_LABEL(error); } + st = dict_st; + } + // _POP_TOP + { + value = st; + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } DISPATCH(); } TARGET(STORE_SUBSCR_LIST_INT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = STORE_SUBSCR_LIST_INT; (void)(opcode); #endif @@ -11458,6 +12158,8 @@ _PyStackRef nos; _PyStackRef list_st; _PyStackRef sub_st; + _PyStackRef ls; + _PyStackRef ss; // _GUARD_TOS_INT { value = stack_pointer[-1]; @@ -11488,18 +12190,17 @@ PyObject *list = PyStackRef_AsPyObjectBorrow(list_st); assert(PyLong_CheckExact(sub)); assert(PyList_CheckExact(list)); - if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) { - UPDATE_MISS_STATS(STORE_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); - JUMP_TO_PREDICTED(STORE_SUBSCR); - } - Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0]; + Py_ssize_t index = _PyLong_CompactValue((PyLongObject *)sub); if (!LOCK_OBJECT(list)) { UPDATE_MISS_STATS(STORE_SUBSCR); assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); JUMP_TO_PREDICTED(STORE_SUBSCR); } - if (index >= PyList_GET_SIZE(list)) { + Py_ssize_t len = PyList_GET_SIZE(list); + if (index < 0) { + index += len; + } + if (index < 0 || index >= len) { UNLOCK_OBJECT(list); if (true) { UPDATE_MISS_STATS(STORE_SUBSCR); @@ -11513,19 +12214,36 @@ PyStackRef_AsPyObjectSteal(value)); assert(old_value != NULL); UNLOCK_OBJECT(list); - PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc); - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + ls = list_st; + ss = sub_st; + stack_pointer[-3] = ls; + stack_pointer[-2] = ss; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(list_st); Py_DECREF(old_value); stack_pointer = _PyFrame_GetStackPointer(frame); } + // _POP_TOP_INT + { + value = ss; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); + } + // _POP_TOP + { + value = ls; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); + } DISPATCH(); } TARGET(SWAP) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = SWAP; (void)(opcode); #endif @@ -11545,7 +12263,7 @@ } TARGET(TO_BOOL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TO_BOOL; (void)(opcode); #endif @@ -11562,7 +12280,7 @@ value = stack_pointer[-1]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -11572,7 +12290,7 @@ } OPCODE_DEFERRED_INC(TO_BOOL); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ } /* Skip 2 cache entries */ // _TO_BOOL @@ -11581,7 +12299,7 @@ int err = PyObject_IsTrue(PyStackRef_AsPyObjectBorrow(value)); stack_pointer = _PyFrame_GetStackPointer(frame); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -11592,12 +12310,12 @@ } stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(TO_BOOL_ALWAYS_TRUE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TO_BOOL_ALWAYS_TRUE; (void)(opcode); #endif @@ -11610,6 +12328,7 @@ _PyStackRef owner; _PyStackRef value; _PyStackRef res; + _PyStackRef v; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION { @@ -11626,21 +12345,22 @@ // _REPLACE_WITH_TRUE { value = owner; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + res = PyStackRef_True; + v = value; + } + // _POP_TOP + { + value = v; + stack_pointer[-1] = res; _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_True; } - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(TO_BOOL_BOOL) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TO_BOOL_BOOL; (void)(opcode); #endif @@ -11664,7 +12384,7 @@ } TARGET(TO_BOOL_INT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TO_BOOL_INT; (void)(opcode); #endif @@ -11676,35 +12396,38 @@ static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); _PyStackRef value; _PyStackRef res; + _PyStackRef v; + // _GUARD_TOS_INT + { + value = stack_pointer[-1]; + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + if (!_PyLong_CheckExactAndCompact(value_o)) { + UPDATE_MISS_STATS(TO_BOOL); + assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); + JUMP_TO_PREDICTED(TO_BOOL); + } + } /* Skip 1 cache entry */ /* Skip 2 cache entries */ - value = stack_pointer[-1]; - PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (!PyLong_CheckExact(value_o)) { - UPDATE_MISS_STATS(TO_BOOL); - assert(_PyOpcode_Deopt[opcode] == (TO_BOOL)); - JUMP_TO_PREDICTED(TO_BOOL); - } - STAT_INC(TO_BOOL, hit); - if (_PyLong_IsZero((PyLongObject *)value_o)) { - assert(_Py_IsImmortal(value_o)); - res = PyStackRef_False; + // _TO_BOOL_INT + { + STAT_INC(TO_BOOL, hit); + PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); + res = (_PyLong_IsZero((PyLongObject *)value_o)) ? PyStackRef_False : PyStackRef_True; + v = value; } - else { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_True; - stack_pointer += 1; + // _POP_TOP_INT + { + value = v; + assert(PyLong_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyLong_ExactDealloc); } stack_pointer[-1] = res; DISPATCH(); } TARGET(TO_BOOL_LIST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TO_BOOL_LIST; (void)(opcode); #endif @@ -11717,6 +12440,7 @@ _PyStackRef tos; _PyStackRef value; _PyStackRef res; + _PyStackRef v; // _GUARD_TOS_LIST { tos = stack_pointer[-1]; @@ -11736,18 +12460,21 @@ assert(PyList_CheckExact(value_o)); STAT_INC(TO_BOOL, hit); res = PyList_GET_SIZE(value_o) ? PyStackRef_True : PyStackRef_False; + v = value; + } + // _POP_TOP + { + value = v; + stack_pointer[-1] = res; _PyFrame_SetStackPointer(frame, stack_pointer); - _PyStackRef tmp = value; - value = res; - stack_pointer[-1] = value; - PyStackRef_CLOSE(tmp); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); } DISPATCH(); } TARGET(TO_BOOL_NONE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TO_BOOL_NONE; (void)(opcode); #endif @@ -11774,7 +12501,7 @@ } TARGET(TO_BOOL_STR) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = TO_BOOL_STR; (void)(opcode); #endif @@ -11786,6 +12513,7 @@ static_assert(INLINE_CACHE_ENTRIES_TO_BOOL == 3, "incorrect cache size"); _PyStackRef value; _PyStackRef res; + _PyStackRef v; // _GUARD_TOS_UNICODE { value = stack_pointer[-1]; @@ -11802,27 +12530,97 @@ { STAT_INC(TO_BOOL, hit); PyObject *value_o = PyStackRef_AsPyObjectBorrow(value); - if (value_o == &_Py_STR(empty)) { - assert(_Py_IsImmortal(value_o)); - res = PyStackRef_False; - } - else { - assert(Py_SIZE(value_o)); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_True; - stack_pointer += 1; - } + res = value_o == &_Py_STR(empty) ? PyStackRef_False : PyStackRef_True; + v = value; + } + // _POP_TOP_UNICODE + { + value = v; + assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(value))); + PyStackRef_CLOSE_SPECIALIZED(value, _PyUnicode_ExactDealloc); } stack_pointer[-1] = res; DISPATCH(); } + TARGET(TRACE_RECORD) { + #if _Py_TAIL_CALL_INTERP + int opcode = TRACE_RECORD; + (void)(opcode); + #endif + _Py_CODEUNIT* const prev_instr = frame->instr_ptr; + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 1; + INSTRUCTION_STATS(TRACE_RECORD); + opcode = TRACE_RECORD; + #if _Py_TIER2 + assert(IS_JIT_TRACING()); + next_instr = this_instr; + frame->instr_ptr = prev_instr; + opcode = next_instr->op.code; + bool stop_tracing = ( + opcode == WITH_EXCEPT_START || + opcode == RERAISE || + opcode == CLEANUP_THROW || + opcode == PUSH_EXC_INFO || + opcode == INTERPRETER_EXIT || + (opcode >= MIN_INSTRUMENTED_OPCODE && opcode != ENTER_EXECUTOR) + ); + _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; + _PyJitTracerState *tracer = _tstate->jit_tracer_state; + assert(tracer != NULL); + _PyFrame_SetStackPointer(frame, stack_pointer); + int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing ? _DEOPT : 0); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (full) { + LEAVE_TRACING(); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = stop_tracing_and_jit(tstate, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + DISPATCH(); + } + for (int i = 0; i < tracer->prev_state.recorded_count; i++) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_CLEAR(tracer->prev_state.recorded_values[i]); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + tracer->prev_state.recorded_count = 0; + tracer->prev_state.instr = next_instr; + PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + if (tracer->prev_state.instr_code != (PyCodeObject *)prev_code) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_SETREF(tracer->prev_state.instr_code, (PyCodeObject*)Py_NewRef((prev_code))); + stack_pointer = _PyFrame_GetStackPointer(frame); + } + tracer->prev_state.instr_frame = frame; + tracer->prev_state.instr_oparg = oparg; + tracer->prev_state.instr_stacklevel = PyStackRef_IsNone(frame->f_executable) ? 2 : STACK_LEVEL(); + if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] + // Branch opcodes use the cache for branch history, not + // specialization counters. Don't reset it. + && !IS_CONDITIONAL_JUMP_OPCODE(opcode)) { + (&next_instr[1])->counter = trigger_backoff_counter(); + } + const _PyOpcodeRecordEntry *record_entry = &_PyOpcode_RecordEntries[opcode]; + for (int i = 0; i < record_entry->count; i++) { + _Py_RecordFuncPtr doesnt_escape = _PyOpcode_RecordFunctions[record_entry->indices[i]]; + doesnt_escape(frame, stack_pointer, oparg, &tracer->prev_state.recorded_values[i]); + } + tracer->prev_state.recorded_count = record_entry->count; + DISPATCH_GOTO_NON_TRACING(); + #else + (void)prev_instr; + Py_FatalError("JIT instruction executed in non-jit build."); + #endif + } + TARGET(UNARY_INVERT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = UNARY_INVERT; (void)(opcode); #endif @@ -11831,27 +12629,32 @@ INSTRUCTION_STATS(UNARY_INVERT); _PyStackRef value; _PyStackRef res; - value = stack_pointer[-1]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyNumber_Invert(PyStackRef_AsPyObjectBorrow(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_LABEL(error); + _PyStackRef v; + // _UNARY_INVERT + { + value = stack_pointer[-1]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyNumber_Invert(PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + v = value; + } + // _POP_TOP + { + value = v; + stack_pointer[-1] = res; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(UNARY_NEGATIVE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = UNARY_NEGATIVE; (void)(opcode); #endif @@ -11860,27 +12663,32 @@ INSTRUCTION_STATS(UNARY_NEGATIVE); _PyStackRef value; _PyStackRef res; - value = stack_pointer[-1]; - _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyNumber_Negative(PyStackRef_AsPyObjectBorrow(value)); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(value); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_LABEL(error); + _PyStackRef v; + // _UNARY_NEGATIVE + { + value = stack_pointer[-1]; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyObject *res_o = PyNumber_Negative(PyStackRef_AsPyObjectBorrow(value)); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res = PyStackRef_FromPyObjectSteal(res_o); + v = value; + } + // _POP_TOP + { + value = v; + stack_pointer[-1] = res; + _PyFrame_SetStackPointer(frame, stack_pointer); + PyStackRef_XCLOSE(value); + stack_pointer = _PyFrame_GetStackPointer(frame); } - res = PyStackRef_FromPyObjectSteal(res_o); - stack_pointer[0] = res; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } TARGET(UNARY_NOT) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = UNARY_NOT; (void)(opcode); #endif @@ -11898,7 +12706,7 @@ } TARGET(UNPACK_EX) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = UNPACK_EX; (void)(opcode); #endif @@ -11911,7 +12719,7 @@ top = &stack_pointer[(oparg & 0xFF) + (oparg >> 8)]; PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); int res = _PyEval_UnpackIterableStackRef(tstate, seq_o, oparg & 0xFF, oparg >> 8, top); Py_DECREF(seq_o); @@ -11920,12 +12728,12 @@ JUMP_TO_LABEL(error); } stack_pointer += 1 + (oparg & 0xFF) + (oparg >> 8); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(UNPACK_SEQUENCE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = UNPACK_SEQUENCE; (void)(opcode); #endif @@ -11942,7 +12750,7 @@ seq = stack_pointer[-1]; uint16_t counter = read_u16(&this_instr[1].cache); (void)counter; - #if ENABLE_SPECIALIZATION_FT + #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_TRIGGERS(counter)) { next_instr = this_instr; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -11952,7 +12760,7 @@ } OPCODE_DEFERRED_INC(UNPACK_SEQUENCE); ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); - #endif /* ENABLE_SPECIALIZATION_FT */ + #endif /* ENABLE_SPECIALIZATION */ (void)seq; (void)counter; } @@ -11961,7 +12769,7 @@ top = &stack_pointer[-1 + oparg]; PyObject *seq_o = PyStackRef_AsPyObjectSteal(seq); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); int res = _PyEval_UnpackIterableStackRef(tstate, seq_o, oparg, -1, top); Py_DECREF(seq_o); @@ -11971,12 +12779,12 @@ } } stack_pointer += oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(UNPACK_SEQUENCE_LIST) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = UNPACK_SEQUENCE_LIST; (void)(opcode); #endif @@ -12026,7 +12834,7 @@ } UNLOCK_OBJECT(seq_o); stack_pointer += -1 + oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(seq); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -12035,7 +12843,7 @@ } TARGET(UNPACK_SEQUENCE_TUPLE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = UNPACK_SEQUENCE_TUPLE; (void)(opcode); #endif @@ -12076,7 +12884,7 @@ *values++ = PyStackRef_FromPyObjectNew(items[i]); } stack_pointer += -1 + oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(seq); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -12085,7 +12893,7 @@ } TARGET(UNPACK_SEQUENCE_TWO_TUPLE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = UNPACK_SEQUENCE_TWO_TUPLE; (void)(opcode); #endif @@ -12127,7 +12935,7 @@ stack_pointer[-1] = val1; stack_pointer[0] = val0; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_CLOSE(seq); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -12136,7 +12944,7 @@ } TARGET(WITH_EXCEPT_START) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = WITH_EXCEPT_START; (void)(opcode); #endif @@ -12163,11 +12971,16 @@ } assert(PyStackRef_IsTaggedInt(lasti)); (void)lasti; - PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb}; - int has_self = !PyStackRef_IsNull(exit_self); + PyObject* res_o; + { + PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb}; + int has_self = !PyStackRef_IsNull(exit_self); + _PyFrame_SetStackPointer(frame, stack_pointer); + res_o = PyObject_Vectorcall(exit_func_o, stack + 2 - has_self, + (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); + stack_pointer = _PyFrame_GetStackPointer(frame); + } _PyFrame_SetStackPointer(frame, stack_pointer); - PyObject *res_o = PyObject_Vectorcall(exit_func_o, stack + 2 - has_self, - (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); Py_XDECREF(original_tb); stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { @@ -12176,58 +12989,69 @@ res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } TARGET(YIELD_VALUE) { - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode = YIELD_VALUE; (void)(opcode); #endif frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(YIELD_VALUE); - _PyStackRef retval; + opcode = YIELD_VALUE; _PyStackRef value; - retval = stack_pointer[-1]; - assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - frame->instr_ptr++; - PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); - assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); - assert(oparg == 0 || oparg == 1); - gen->gi_frame_state = FRAME_SUSPENDED + oparg; - _PyStackRef temp = retval; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - _PyFrame_SetStackPointer(frame, stack_pointer); - tstate->exc_info = gen->gi_exc_state.previous_item; - gen->gi_exc_state.previous_item = NULL; - _Py_LeaveRecursiveCallPy(tstate); - _PyInterpreterFrame *gen_frame = frame; - frame = tstate->current_frame = frame->previous; - gen_frame->previous = NULL; - assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); - #if TIER_ONE - assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE || - frame->instr_ptr->op.code == INSTRUMENTED_INSTRUCTION || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == SEND || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == FOR_ITER || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == INTERPRETER_EXIT || - _PyOpcode_Deopt[frame->instr_ptr->op.code] == ENTER_EXECUTOR); - #endif - stack_pointer = _PyFrame_GetStackPointer(frame); - LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); - value = PyStackRef_MakeHeapSafe(temp); - LLTRACE_RESUME_FRAME(); + _PyStackRef retval; + // _MAKE_HEAP_SAFE + { + value = stack_pointer[-1]; + value = PyStackRef_MakeHeapSafe(value); + } + // _YIELD_VALUE + { + retval = value; + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + frame->instr_ptr++; + PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); + assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); + assert(oparg == 0 || oparg == 1); + _PyStackRef temp = retval; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + DTRACE_FUNCTION_RETURN(); + tstate->exc_info = gen->gi_exc_state.previous_item; + gen->gi_exc_state.previous_item = NULL; + _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; + frame = tstate->current_frame = frame->previous; + gen_frame->previous = NULL; + ((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD; + FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); + assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); + #if TIER_ONE && defined(Py_DEBUG) + if (!PyStackRef_IsNone(frame->f_executable)) { + Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); + assert(i >= 0 && i <= INT_MAX); + int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; + assert(opcode == SEND || opcode == FOR_ITER); + } + #endif + stack_pointer = _PyFrame_GetStackPointer(frame); + LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); + value = temp; + LLTRACE_RESUME_FRAME(); + } stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } /* END INSTRUCTIONS */ -#if !Py_TAIL_CALL_INTERP +#if !_Py_TAIL_CALL_INTERP #if USE_COMPUTED_GOTOS _unknown_opcode: #else @@ -12249,7 +13073,7 @@ JUMP_TO_LABEL(error); /* This should never be reached. Every opcode should end with DISPATCH() or goto error. */ Py_UNREACHABLE(); -#endif /* Py_TAIL_CALL_INTERP */ +#endif /* _Py_TAIL_CALL_INTERP */ /* BEGIN LABELS */ LABEL(pop_2_error) @@ -12278,7 +13102,9 @@ JUMP_TO_LABEL(error); #else assert(_PyErr_Occurred(tstate)); #endif - + _PyFrame_SetStackPointer(frame, stack_pointer); + STOP_TRACING(); + stack_pointer = _PyFrame_GetStackPointer(frame); assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); if (!_PyFrame_IsIncomplete(frame)) { _PyFrame_SetStackPointer(frame, stack_pointer); @@ -12297,6 +13123,7 @@ JUMP_TO_LABEL(error); LABEL(exception_unwind) { + STOP_TRACING(); int offset = INSTR_OFFSET()-1; int level, handler, lasti; int handled = get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti); @@ -12335,13 +13162,20 @@ JUMP_TO_LABEL(error); } #endif stack_pointer = _PyFrame_GetStackPointer(frame); - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode; #endif DISPATCH(); } LABEL(exit_unwind) + { + assert(_PyErr_Occurred(tstate)); + DTRACE_FUNCTION_RETURN(); + JUMP_TO_LABEL(exit_unwind_notrace); + } + + LABEL(exit_unwind_notrace) { assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallPy(tstate); @@ -12352,7 +13186,7 @@ JUMP_TO_LABEL(error); frame->return_offset = 0; if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { tstate->current_frame = frame->previous; - #if !Py_TAIL_CALL_INTERP + #if !_Py_TAIL_CALL_INTERP assert(frame == &entry.frame); #endif #ifdef _Py_TIER2 @@ -12374,8 +13208,9 @@ JUMP_TO_LABEL(error); { int too_deep = _Py_EnterRecursivePy(tstate); if (too_deep) { - JUMP_TO_LABEL(exit_unwind); + JUMP_TO_LABEL(exit_unwind_notrace); } + DTRACE_FUNCTION_ENTRY(); next_instr = frame->instr_ptr; #ifdef Py_DEBUG int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); @@ -12386,11 +13221,35 @@ JUMP_TO_LABEL(error); assert(!_PyErr_Occurred(tstate)); #endif stack_pointer = _PyFrame_GetStackPointer(frame); - #if Py_TAIL_CALL_INTERP + #if _Py_TAIL_CALL_INTERP int opcode; #endif DISPATCH(); } + #if _Py_TAIL_CALL_INTERP && !defined(_Py_TIER2) + Py_GCC_ATTRIBUTE((unused)) + #endif + LABEL(stop_tracing) + { + #if _Py_TIER2 + assert(IS_JIT_TRACING()); + int opcode = next_instr->op.code; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, _EXIT_TRACE); + stack_pointer = _PyFrame_GetStackPointer(frame); + LEAVE_TRACING(); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = stop_tracing_and_jit(tstate, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + DISPATCH_GOTO_NON_TRACING(); + #else + Py_FatalError("JIT label executed in non-jit build."); + #endif + } + /* END LABELS */ #undef TIER_ONE diff --git a/Python/getargs.c b/Python/getargs.c index c119ca5c35398b..3f423266bff7f4 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -57,8 +57,15 @@ static const char *convertsimple(PyObject *, const char **, va_list *, int, static Py_ssize_t convertbuffer(PyObject *, const void **p, const char **); static int getbuffer(PyObject *, Py_buffer *, const char**); -static int vgetargskeywords(PyObject *, PyObject *, - const char *, const char * const *, va_list *, int); +static int +vgetargskeywords(PyObject *args, PyObject *kwargs, + const char *format, const char * const *kwlist, + va_list *p_va, int flags); +static int +vgetargskeywords_impl(PyObject *const *args, Py_ssize_t nargs, + PyObject *kwargs, PyObject *kwnames, + const char *format, const char * const *kwlist, + va_list *p_va, int flags); static int vgetargskeywordsfast(PyObject *, PyObject *, struct _PyArg_Parser *, va_list *, int); static int vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, @@ -129,6 +136,40 @@ _PyArg_ParseStack(PyObject *const *args, Py_ssize_t nargs, const char *format, . return retval; } +int +PyArg_ParseArray(PyObject *const *args, Py_ssize_t nargs, const char *format, ...) +{ + va_list va; + va_start(va, format); + int retval = vgetargs1_impl(NULL, args, nargs, format, &va, 0); + va_end(va); + return retval; +} + +int +PyArg_ParseArrayAndKeywords(PyObject *const *args, Py_ssize_t nargs, + PyObject *kwnames, + const char *format, + const char * const *kwlist, ...) +{ + if ((args == NULL && nargs != 0) || + (kwnames != NULL && !PyTuple_Check(kwnames)) || + format == NULL || + kwlist == NULL) + { + PyErr_BadInternalCall(); + return 0; + } + + va_list va; + va_start(va, kwlist); + int retval = vgetargskeywords_impl(args, nargs, NULL, kwnames, format, + kwlist, &va, 0); + va_end(va); + return retval; +} + + int PyArg_VaParse(PyObject *args, const char *format, va_list va) { @@ -1612,11 +1653,27 @@ PyArg_ValidateKeywordArguments(PyObject *kwargs) static PyObject * new_kwtuple(const char * const *keywords, int total, int pos); +static PyObject* +find_keyword_str(PyObject *kwnames, PyObject *const *kwstack, const char *key) +{ + Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwnames); + for (Py_ssize_t i = 0; i < nkwargs; i++) { + PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); + assert(PyUnicode_Check(kwname)); + if (PyUnicode_EqualToUTF8(kwname, key)) { + return Py_NewRef(kwstack[i]); + } + } + return NULL; +} + #define IS_END_OF_FORMAT(c) (c == '\0' || c == ';' || c == ':') static int -vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, - const char * const *kwlist, va_list *p_va, int flags) +vgetargskeywords_impl(PyObject *const *args, Py_ssize_t nargs, + PyObject *kwargs, PyObject *kwnames, + const char *format, const char * const *kwlist, + va_list *p_va, int flags) { char msgbuf[512]; int levels[32]; @@ -1625,16 +1682,18 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, int max = INT_MAX; int i, pos, len; int skip = 0; - Py_ssize_t nargs, nkwargs; + Py_ssize_t nkwargs; freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; freelist_t freelist; + PyObject * const *kwstack = NULL; freelist.entries = static_entries; freelist.first_available = 0; freelist.entries_malloced = 0; - assert(args != NULL && PyTuple_Check(args)); + assert(args != NULL || nargs == 0); assert(kwargs == NULL || PyDict_Check(kwargs)); + assert(kwnames == NULL || PyTuple_Check(kwnames)); assert(format != NULL); assert(kwlist != NULL); assert(p_va != NULL); @@ -1672,8 +1731,16 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, freelist.entries_malloced = 1; } - nargs = PyTuple_GET_SIZE(args); - nkwargs = (kwargs == NULL) ? 0 : PyDict_GET_SIZE(kwargs); + if (kwargs != NULL) { + nkwargs = PyDict_GET_SIZE(kwargs); + } + else if (kwnames != NULL) { + nkwargs = PyTuple_GET_SIZE(kwnames); + kwstack = args + nargs; + } + else { + nkwargs = 0; + } if (nargs + nkwargs > len) { /* Adding "keyword" (when nargs == 0) prevents producing wrong error messages in some special cases (see bpo-31229). */ @@ -1757,11 +1824,16 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, if (!skip) { PyObject *current_arg; if (i < nargs) { - current_arg = Py_NewRef(PyTuple_GET_ITEM(args, i)); + current_arg = Py_NewRef(args[i]); } else if (nkwargs && i >= pos) { - if (PyDict_GetItemStringRef(kwargs, kwlist[i], &current_arg) < 0) { - return cleanreturn(0, &freelist); + if (kwargs != NULL) { + if (PyDict_GetItemStringRef(kwargs, kwlist[i], &current_arg) < 0) { + return cleanreturn(0, &freelist); + } + } + else { + current_arg = find_keyword_str(kwnames, kwstack, kwlist[i]); } if (current_arg) { --nkwargs; @@ -1846,8 +1918,13 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, /* make sure there are no arguments given by name and position */ for (i = pos; i < nargs; i++) { PyObject *current_arg; - if (PyDict_GetItemStringRef(kwargs, kwlist[i], &current_arg) < 0) { - return cleanreturn(0, &freelist); + if (kwargs != NULL) { + if (PyDict_GetItemStringRef(kwargs, kwlist[i], &current_arg) < 0) { + return cleanreturn(0, &freelist); + } + } + else { + current_arg = find_keyword_str(kwnames, kwstack, kwlist[i]); } if (current_arg) { Py_DECREF(current_arg); @@ -1863,7 +1940,20 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, } /* make sure there are no extraneous keyword arguments */ j = 0; - while (PyDict_Next(kwargs, &j, &key, NULL)) { + while (1) { + if (kwargs != NULL) { + if (!PyDict_Next(kwargs, &j, &key, NULL)) { + break; + } + } + else { + if (j >= nkwargs) { + break; + } + key = PyTuple_GET_ITEM(kwnames, j); + j++; + } + int match = 0; if (!PyUnicode_Check(key)) { PyErr_SetString(PyExc_TypeError, @@ -1921,6 +2011,16 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, return cleanreturn(1, &freelist); } +static int +vgetargskeywords(PyObject *argstuple, PyObject *kwargs, + const char *format, const char * const *kwlist, + va_list *p_va, int flags) +{ + PyObject *const *args = _PyTuple_ITEMS(argstuple); + Py_ssize_t nargs = PyTuple_GET_SIZE(argstuple); + return vgetargskeywords_impl(args, nargs, kwargs, NULL, + format, kwlist, p_va, flags); +} static int scan_keywords(const char * const *keywords, int *ptotal, int *pposonly) @@ -2321,7 +2421,7 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, if (i < parser->min) { /* Less arguments than required */ if (i < pos) { - Py_ssize_t min = Py_MIN(pos, parser->min); + int min = Py_MIN(pos, parser->min); PyErr_Format(PyExc_TypeError, "%.200s%s takes %s %d positional argument%s" " (%zd given)", @@ -2335,7 +2435,7 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, else { keyword = PyTuple_GET_ITEM(kwtuple, i - pos); PyErr_Format(PyExc_TypeError, "%.200s%s missing required " - "argument '%U' (pos %d)", + "argument '%U' (pos %zd)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", keyword, i+1); @@ -2376,7 +2476,7 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, /* arg present in tuple and in dict */ PyErr_Format(PyExc_TypeError, "argument for %.200s%s given by name ('%U') " - "and position (%d)", + "and position (%zd)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", keyword, i+1); diff --git a/Python/getcompiler.c b/Python/getcompiler.c index a5d26239e8772e..cc56ad8c895551 100644 --- a/Python/getcompiler.c +++ b/Python/getcompiler.c @@ -3,6 +3,10 @@ #include "Python.h" +#ifdef _Py_COMPILER +# define COMPILER _Py_COMPILER +#endif + #ifndef COMPILER // Note the __clang__ conditional has to come before the __GNUC__ one because diff --git a/Python/hamt.c b/Python/hamt.c index 906149cc6cdbdc..95998ae5062ac7 100644 --- a/Python/hamt.c +++ b/Python/hamt.c @@ -4,6 +4,7 @@ #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_long.h" // _PyLong_Format() #include "pycore_object.h" // _PyObject_GC_TRACK() +#include "pycore_tuple.h" // _PyTuple_FromPair #include <stddef.h> // offsetof() @@ -256,9 +257,9 @@ Debug ===== The HAMT datatype is accessible for testing purposes under the -`_testcapi` module: +`_testinternalcapi` module: - >>> from _testcapi import hamt + >>> from _testinternalcapi import hamt >>> h = hamt() >>> h2 = h.set('a', 2) >>> h3 = h2.set('b', 3) @@ -701,6 +702,7 @@ hamt_node_bitmap_assoc(PyHamtNode_Bitmap *self, PyHamtNode_Bitmap *ret = hamt_node_bitmap_clone(self); if (ret == NULL) { + Py_DECREF(sub_node); return NULL; } Py_SETREF(ret->b_array[val_idx], (PyObject*)sub_node); @@ -993,6 +995,7 @@ hamt_node_bitmap_without(PyHamtNode_Bitmap *self, PyHamtNode_Bitmap *clone = hamt_node_bitmap_clone(self); if (clone == NULL) { + Py_DECREF(sub_node); return W_ERROR; } @@ -2328,6 +2331,10 @@ _PyHamt_Eq(PyHamtObject *v, PyHamtObject *w) return 0; } + Py_INCREF(v); + Py_INCREF(w); + + int res = 1; PyHamtIteratorState iter; hamt_iter_t iter_res; hamt_find_t find_res; @@ -2343,25 +2350,38 @@ _PyHamt_Eq(PyHamtObject *v, PyHamtObject *w) find_res = hamt_find(w, v_key, &w_val); switch (find_res) { case F_ERROR: - return -1; + res = -1; + goto done; case F_NOT_FOUND: - return 0; + res = 0; + goto done; case F_FOUND: { + Py_INCREF(v_key); + Py_INCREF(v_val); + Py_INCREF(w_val); int cmp = PyObject_RichCompareBool(v_val, w_val, Py_EQ); + Py_DECREF(v_key); + Py_DECREF(v_val); + Py_DECREF(w_val); if (cmp < 0) { - return -1; + res = -1; + goto done; } if (cmp == 0) { - return 0; + res = 0; + goto done; } } } } } while (iter_res != I_END); - return 1; +done: + Py_DECREF(v); + Py_DECREF(w); + return res; } Py_ssize_t @@ -2525,7 +2545,7 @@ PyTypeObject _PyHamtItems_Type = { static PyObject * hamt_iter_yield_items(PyObject *key, PyObject *val) { - return PyTuple_Pack(2, key, val); + return _PyTuple_FromPair(key, val); } PyObject * diff --git a/Python/import.c b/Python/import.c index 73b94d0dd2a1b1..42bfe15121f84f 100644 --- a/Python/import.c +++ b/Python/import.c @@ -3,21 +3,29 @@ #include "Python.h" #include "pycore_audit.h" // _PySys_Audit() #include "pycore_ceval.h" +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() +#include "pycore_dict.h" // _PyDict_Contains_KnownHash() #include "pycore_hashtable.h" // _Py_hashtable_new_full() #include "pycore_import.h" // _PyImport_BootstrapImp() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_interp.h" // struct _import_runtime_state +#include "pycore_interpframe.h" +#include "pycore_lazyimportobject.h" +#include "pycore_long.h" // _PyLong_GetZero #include "pycore_magic_number.h" // PYC_MAGIC_NUMBER_TOKEN #include "pycore_moduleobject.h" // _PyModule_GetDef() #include "pycore_namespace.h" // _PyNamespace_Type #include "pycore_object.h" // _Py_SetImmortal() +#include "pycore_pyatomic_ft_wrappers.h" #include "pycore_pyerrors.h" // _PyErr_SetString() #include "pycore_pyhash.h" // _Py_KeyedHash() #include "pycore_pylifecycle.h" #include "pycore_pymem.h" // _PyMem_DefaultRawFree() #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_sysmodule.h" // _PySys_ClearAttrString() #include "pycore_time.h" // _PyTime_AsMicroseconds() +#include "pycore_traceback.h" #include "pycore_unicodeobject.h" // _PyUnicode_AsUTF8NoNUL() #include "pycore_weakref.h" // _PyWeakref_GET_REF() @@ -84,6 +92,10 @@ static struct _inittab *inittab_copy = NULL; (interp)->imports.modules #define MODULES_BY_INDEX(interp) \ (interp)->imports.modules_by_index +#define LAZY_MODULES(interp) \ + (interp)->imports.lazy_modules +#define LAZY_PENDING_SUBMODULES(interp) \ + (interp)->imports.lazy_pending_submodules #define IMPORTLIB(interp) \ (interp)->imports.importlib #define OVERRIDE_MULTI_INTERP_EXTENSIONS_CHECK(interp) \ @@ -97,12 +109,30 @@ static struct _inittab *inittab_copy = NULL; #define IMPORT_FUNC(interp) \ (interp)->imports.import_func +#define LAZY_IMPORT_FUNC(interp) \ + (interp)->imports.lazy_import_func + #define IMPORT_LOCK(interp) \ (interp)->imports.lock #define FIND_AND_LOAD(interp) \ (interp)->imports.find_and_load +#define LAZY_IMPORTS_MODE(interp) \ + (interp)->imports.lazy_imports_mode + +#define LAZY_IMPORTS_FILTER(interp) \ + (interp)->imports.lazy_imports_filter + +#ifdef Py_GIL_DISABLED +#define LAZY_IMPORTS_LOCK(interp) PyMutex_Lock(&(interp)->imports.lazy_mutex) +#define LAZY_IMPORTS_UNLOCK(interp) PyMutex_Unlock(&(interp)->imports.lazy_mutex) +#else +#define LAZY_IMPORTS_LOCK(interp) +#define LAZY_IMPORTS_UNLOCK(interp) +#endif + + #define _IMPORT_TIME_HEADER(interp) \ do { \ if (FIND_AND_LOAD((interp)).header) { \ @@ -240,6 +270,24 @@ import_get_module(PyThreadState *tstate, PyObject *name) return m; } +PyObject * +_PyImport_InitLazyModules(PyInterpreterState *interp) +{ + assert(LAZY_MODULES(interp) == NULL && + LAZY_PENDING_SUBMODULES(interp) == NULL); + + LAZY_PENDING_SUBMODULES(interp) = PyDict_New(); + LAZY_MODULES(interp) = PySet_New(0); + return LAZY_MODULES(interp); +} + +void +_PyImport_ClearLazyModules(PyInterpreterState *interp) +{ + Py_CLEAR(LAZY_MODULES(interp)); + Py_CLEAR(LAZY_PENDING_SUBMODULES(interp)); +} + static int import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *name) { @@ -296,12 +344,32 @@ PyImport_GetModule(PyObject *name) mod = import_get_module(tstate, name); if (mod != NULL && mod != Py_None) { if (import_ensure_initialized(tstate->interp, mod, name) < 0) { + goto error; + } + /* Verify the module is still in sys.modules. Another thread may have + removed it (due to import failure) between our import_get_module() + call and the _initializing check in import_ensure_initialized(). */ + PyObject *mod_check = import_get_module(tstate, name); + if (mod_check != mod) { + Py_XDECREF(mod_check); + if (_PyErr_Occurred(tstate)) { + goto error; + } + /* The module was removed or replaced. Return NULL to report + "not found" rather than trying to keep up with racing + modifications to sys.modules; returning the new value would + require looping to redo the ensure_initialized check. */ Py_DECREF(mod); - remove_importlib_frames(tstate); return NULL; } + Py_DECREF(mod_check); } return mod; + +error: + Py_DECREF(mod); + remove_importlib_frames(tstate); + return NULL; } /* Get the module object corresponding to a module name. @@ -309,13 +377,8 @@ PyImport_GetModule(PyObject *name) if not, create a new one and insert it in the modules dictionary. */ static PyObject * -import_add_module(PyThreadState *tstate, PyObject *name) +import_add_module_lock_held(PyObject *modules, PyObject *name) { - PyObject *modules = get_modules_dict(tstate, false); - if (modules == NULL) { - return NULL; - } - PyObject *m; if (PyMapping_GetOptionalItem(modules, name, &m) < 0) { return NULL; @@ -335,6 +398,21 @@ import_add_module(PyThreadState *tstate, PyObject *name) return m; } +static PyObject * +import_add_module(PyThreadState *tstate, PyObject *name) +{ + PyObject *modules = get_modules_dict(tstate, false); + if (modules == NULL) { + return NULL; + } + + PyObject *m; + Py_BEGIN_CRITICAL_SECTION(modules); + m = import_add_module_lock_held(modules, name); + Py_END_CRITICAL_SECTION(); + return m; +} + PyObject * PyImport_AddModuleRef(const char *name) { @@ -672,8 +750,8 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) (6). first time (not found in _PyRuntime.imports.extensions): A. _imp_create_dynamic_impl() -> import_find_extension() - B. _imp_create_dynamic_impl() -> _PyImport_GetModInitFunc() - C. _PyImport_GetModInitFunc(): load <module init func> + B. _imp_create_dynamic_impl() -> _PyImport_GetModuleExportHooks() + C. _PyImport_GetModuleExportHooks(): load <module init func> D. _imp_create_dynamic_impl() -> import_run_extension() E. import_run_extension() -> _PyImport_RunModInitFunc() F. _PyImport_RunModInitFunc(): call <module init func> @@ -743,16 +821,19 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) A. noop - ...for multi-phase init modules: + ...for multi-phase init modules from PyModInit_* (PyModuleDef): (6). every time: A. _imp_create_dynamic_impl() -> import_find_extension() (not found) - B. _imp_create_dynamic_impl() -> _PyImport_GetModInitFunc() - C. _PyImport_GetModInitFunc(): load <module init func> + B. _imp_create_dynamic_impl() -> _PyImport_GetModuleExportHooks() + C. _PyImport_GetModuleExportHooks(): load <module init func> D. _imp_create_dynamic_impl() -> import_run_extension() E. import_run_extension() -> _PyImport_RunModInitFunc() F. _PyImport_RunModInitFunc(): call <module init func> G. import_run_extension() -> PyModule_FromDefAndSpec() + + PyModule_FromDefAndSpec(): + H. PyModule_FromDefAndSpec(): gather/check moduledef slots I. if there's a Py_mod_create slot: 1. PyModule_FromDefAndSpec(): call its function @@ -765,10 +846,29 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) (10). every time: A. _imp_exec_dynamic_impl() -> exec_builtin_or_dynamic() B. if mod->md_state == NULL (including if m_size == 0): - 1. exec_builtin_or_dynamic() -> PyModule_ExecDef() - 2. PyModule_ExecDef(): allocate mod->md_state + 1. exec_builtin_or_dynamic() -> PyModule_Exec() + 2. PyModule_Exec(): allocate mod->md_state 3. if there's a Py_mod_exec slot: - 1. PyModule_ExecDef(): call its function + 1. PyModule_Exec(): call its function + + + ...for multi-phase init modules from PyModExport_* (slots array): + + (6). every time: + + A. _imp_create_dynamic_impl() -> import_find_extension() (not found) + B. _imp_create_dynamic_impl() -> _PyImport_GetModuleExportHooks() + C. _PyImport_GetModuleExportHooks(): load <module export func> + D. _imp_create_dynamic_impl() -> import_run_modexport() + E. import_run_modexport(): call <module init func> + F. import_run_modexport() -> PyModule_FromSlotsAndSpec() + G. PyModule_FromSlotsAndSpec(): create temporary PyModuleDef-like + H. PyModule_FromSlotsAndSpec() -> PyModule_FromDefAndSpec() + + (PyModule_FromDefAndSpec behaves as for PyModInit_*, above) + + (10). every time: as for PyModInit_*, above + */ @@ -782,18 +882,13 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) substitute this (if the name actually matches). */ -#ifdef HAVE_THREAD_LOCAL -_Py_thread_local const char *pkgcontext = NULL; +static _Py_thread_local const char *pkgcontext = NULL; # undef PKGCONTEXT # define PKGCONTEXT pkgcontext -#endif const char * _PyImport_ResolveNameWithPackageContext(const char *name) { -#ifndef HAVE_THREAD_LOCAL - PyMutex_Lock(&EXTENSIONS.mutex); -#endif if (PKGCONTEXT != NULL) { const char *p = strrchr(PKGCONTEXT, '.'); if (p != NULL && strcmp(name, p+1) == 0) { @@ -801,23 +896,14 @@ _PyImport_ResolveNameWithPackageContext(const char *name) PKGCONTEXT = NULL; } } -#ifndef HAVE_THREAD_LOCAL - PyMutex_Unlock(&EXTENSIONS.mutex); -#endif return name; } const char * _PyImport_SwapPackageContext(const char *newcontext) { -#ifndef HAVE_THREAD_LOCAL - PyMutex_Lock(&EXTENSIONS.mutex); -#endif const char *oldcontext = PKGCONTEXT; PKGCONTEXT = newcontext; -#ifndef HAVE_THREAD_LOCAL - PyMutex_Unlock(&EXTENSIONS.mutex); -#endif return oldcontext; } @@ -839,25 +925,19 @@ _PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val) /* Common implementation for _imp.exec_dynamic and _imp.exec_builtin */ static int exec_builtin_or_dynamic(PyObject *mod) { - PyModuleDef *def; void *state; if (!PyModule_Check(mod)) { return 0; } - def = PyModule_GetDef(mod); - if (def == NULL) { - return 0; - } - state = PyModule_GetState(mod); if (state) { /* Already initialized; skip reload */ return 0; } - return PyModule_ExecDef(mod, def); + return PyModule_Exec(mod); } @@ -1015,9 +1095,10 @@ struct extensions_cache_value { _Py_ext_module_origin origin; #ifdef Py_GIL_DISABLED - /* The module's md_gil slot, for legacy modules that are reinitialized from - m_dict rather than calling their initialization function again. */ - void *md_gil; + /* The module's md_requires_gil member, for legacy modules that are + * reinitialized from m_dict rather than calling their initialization + * function again. */ + bool md_requires_gil; #endif }; @@ -1348,7 +1429,7 @@ static struct extensions_cache_value * _extensions_cache_set(PyObject *path, PyObject *name, PyModuleDef *def, PyModInitFunction m_init, Py_ssize_t m_index, PyObject *m_dict, - _Py_ext_module_origin origin, void *md_gil) + _Py_ext_module_origin origin, bool requires_gil) { struct extensions_cache_value *value = NULL; void *key = NULL; @@ -1403,11 +1484,11 @@ _extensions_cache_set(PyObject *path, PyObject *name, /* m_dict is set by set_cached_m_dict(). */ .origin=origin, #ifdef Py_GIL_DISABLED - .md_gil=md_gil, + .md_requires_gil=requires_gil, #endif }; #ifndef Py_GIL_DISABLED - (void)md_gil; + (void)requires_gil; #endif if (init_cached_m_dict(newvalue, m_dict) < 0) { goto finally; @@ -1545,26 +1626,13 @@ _PyImport_CheckGILForModule(PyObject* module, PyObject *module_name) } if (!PyModule_Check(module) || - ((PyModuleObject *)module)->md_gil == Py_MOD_GIL_USED) { - if (_PyEval_EnableGILPermanent(tstate)) { - int warn_result = PyErr_WarnFormat( - PyExc_RuntimeWarning, - 1, - "The global interpreter lock (GIL) has been enabled to load " - "module '%U', which has not declared that it can run safely " - "without the GIL. To override this behavior and keep the GIL " - "disabled (at your own risk), run with PYTHON_GIL=0 or -Xgil=0.", - module_name - ); - if (warn_result < 0) { - return warn_result; - } + ((PyModuleObject *)module)->md_requires_gil) + { + if (PyModule_Check(module)) { + assert(((PyModuleObject *)module)->md_token_is_def); } - - const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp); - if (config->enable_gil == _PyConfig_GIL_DEFAULT && config->verbose) { - PySys_FormatStderr("# loading module '%U', which requires the GIL\n", - module_name); + if (_PyImport_EnableGILAndWarn(tstate, module_name) < 0) { + return -1; } } else { @@ -1573,6 +1641,28 @@ _PyImport_CheckGILForModule(PyObject* module, PyObject *module_name) return 0; } + +int +_PyImport_EnableGILAndWarn(PyThreadState *tstate, PyObject *module_name) +{ + if (_PyEval_EnableGILPermanent(tstate)) { + return PyErr_WarnFormat( + PyExc_RuntimeWarning, + 1, + "The global interpreter lock (GIL) has been enabled to load " + "module '%U', which has not declared that it can run safely " + "without the GIL. To override this behavior and keep the GIL " + "disabled (at your own risk), run with PYTHON_GIL=0 or -Xgil=0.", + module_name + ); + } + const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp); + if (config->enable_gil == _PyConfig_GIL_DEFAULT && config->verbose) { + PySys_FormatStderr("# loading module '%U', which requires the GIL\n", + module_name); + } + return 0; +} #endif static PyThreadState * @@ -1723,7 +1813,7 @@ struct singlephase_global_update { Py_ssize_t m_index; PyObject *m_dict; _Py_ext_module_origin origin; - void *md_gil; + bool md_requires_gil; }; static struct extensions_cache_value * @@ -1782,7 +1872,7 @@ update_global_state_for_extension(PyThreadState *tstate, #endif cached = _extensions_cache_set( path, name, def, m_init, singlephase->m_index, m_dict, - singlephase->origin, singlephase->md_gil); + singlephase->origin, singlephase->md_requires_gil); if (cached == NULL) { // XXX Ignore this error? Doing so would effectively // mark the module as not loadable. @@ -1801,7 +1891,7 @@ finish_singlephase_extension(PyThreadState *tstate, PyObject *mod, PyObject *name, PyObject *modules) { assert(mod != NULL && PyModule_Check(mod)); - assert(cached->def == _PyModule_GetDef(mod)); + assert(cached->def == _PyModule_GetDefOrNull(mod)); Py_ssize_t index = _get_cached_module_index(cached); if (_modules_by_index_set(tstate->interp, index, mod) < 0) { @@ -1871,7 +1961,7 @@ reload_singlephase_extension(PyThreadState *tstate, if (def->m_base.m_copy != NULL) { // For non-core modules, fetch the GIL slot that was stored by // import_run_extension(). - ((PyModuleObject *)mod)->md_gil = cached->md_gil; + ((PyModuleObject *)mod)->md_requires_gil = cached->md_requires_gil; } #endif /* We can't set mod->md_def if it's missing, @@ -1879,8 +1969,8 @@ reload_singlephase_extension(PyThreadState *tstate, * due to violating interpreter isolation. * See the note in set_cached_m_dict(). * Until that is solved, we leave md_def set to NULL. */ - assert(_PyModule_GetDef(mod) == NULL - || _PyModule_GetDef(mod) == def); + assert(_PyModule_GetDefOrNull(mod) == NULL + || _PyModule_GetDefOrNull(mod) == def); } else { assert(cached->m_dict == NULL); @@ -1967,6 +2057,43 @@ import_find_extension(PyThreadState *tstate, return mod; } +static PyObject * +import_run_modexport(PyThreadState *tstate, PyModExportFunction ex0, + struct _Py_ext_module_loader_info *info, + PyObject *spec) +{ + /* This is like import_run_extension, but avoids interpreter switching + * and code for for single-phase modules. + */ + PySlot *slots = ex0(); + if (!slots) { + if (!PyErr_Occurred()) { + PyErr_Format( + PyExc_SystemError, + "module export hook for module %R failed without setting an exception", + info->name); + } + return NULL; + } + if (PyErr_Occurred()) { + PyErr_Format( + PyExc_SystemError, + "module export hook for module %R raised unreported exception", + info->name); + } + PyObject *result = PyModule_FromSlotsAndSpec(slots, spec); + if (!result) { + return NULL; + } + if (PyModule_Check(result)) { + PyModuleObject *mod = (PyModuleObject *)result; + if (mod && !mod->md_token) { + mod->md_token = slots; + } + } + return result; +} + static PyObject * import_run_extension(PyThreadState *tstate, PyModInitFunction p0, struct _Py_ext_module_loader_info *info, @@ -2041,6 +2168,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0, struct _Py_ext_module_loader_result res; int rc = _PyImport_RunModInitFunc(p0, info, &res); + bool main_error = false; if (rc < 0) { /* We discard res.def. */ assert(res.module == NULL); @@ -2065,9 +2193,11 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0, // obmalloc, so we create a copy here. filename = _PyUnicode_Copy(info->filename); if (filename == NULL) { - return NULL; + main_error = true; + goto main_finally; } - } else { + } + else { filename = Py_NewRef(info->filename); } // XXX There's a refleak somewhere with the filename. @@ -2089,7 +2219,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0, .m_index=def->m_base.m_index, .origin=info->origin, #ifdef Py_GIL_DISABLED - .md_gil=((PyModuleObject *)mod)->md_gil, + .md_requires_gil=((PyModuleObject *)mod)->md_requires_gil, #endif }; // gh-88216: Extensions and def->m_base.m_copy can be updated @@ -2110,19 +2240,36 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0, main_tstate, info->path, info->name, def, &singlephase); if (cached == NULL) { assert(PyErr_Occurred()); + main_error = true; goto main_finally; } } } main_finally: + if (rc < 0) { + _Py_ext_module_loader_result_apply_error(&res, name_buf); + } + /* Switch back to the subinterpreter. */ if (switched) { + // gh-144601: The exception object can't be transferred across + // interpreters. Instead, we print out an unraisable exception, and + // then raise a different exception for the calling interpreter. + if (rc < 0 || main_error) { + assert(PyErr_Occurred()); + PyErr_FormatUnraisable("Exception while importing from subinterpreter"); + } assert(main_tstate != tstate); switch_back_from_main_interpreter(tstate, main_tstate, mod); /* Any module we got from the init function will have to be * reloaded in the subinterpreter. */ mod = NULL; + if (rc < 0 || main_error) { + PyErr_SetString(PyExc_ImportError, + "failed to import from subinterpreter due to exception"); + goto error; + } } /*****************************************************************/ @@ -2131,7 +2278,9 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0, /* Finally we handle the error return from _PyImport_RunModInitFunc(). */ if (rc < 0) { - _Py_ext_module_loader_result_apply_error(&res, name_buf); + goto error; + } + if (main_error) { goto error; } @@ -2139,7 +2288,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0, assert_multiphase_def(def); assert(mod == NULL); /* Note that we cheat a little by not repeating the calls - * to _PyImport_GetModInitFunc() and _PyImport_RunModInitFunc(). */ + * to _PyImport_GetModuleExportHooks() and _PyImport_RunModInitFunc(). */ mod = PyModule_FromDefAndSpec(def, spec); if (mod == NULL) { goto error; @@ -2253,8 +2402,9 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name, return -1; } - PyModuleDef *def = PyModule_GetDef(mod); + PyModuleDef *def = _PyModule_GetDefOrNull(mod); if (def == NULL) { + assert(!PyErr_Occurred()); PyErr_BadInternalCall(); goto finally; } @@ -2283,7 +2433,7 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name, .origin=_Py_ext_module_origin_CORE, #ifdef Py_GIL_DISABLED /* Unused when m_dict == NULL. */ - .md_gil=NULL, + .md_requires_gil=false, #endif }; cached = update_global_state_for_extension( @@ -2322,8 +2472,23 @@ is_builtin(PyObject *name) return 0; } +static struct _inittab* +lookup_inittab_entry(const struct _Py_ext_module_loader_info* info) +{ + for (struct _inittab *p = INITTAB; p->name != NULL; p++) { + if (_PyUnicode_EqualToASCIIString(info->name, p->name)) { + return p; + } + } + // not found + return NULL; +} + static PyObject* -create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) +create_builtin( + PyThreadState *tstate, PyObject *name, + PyObject *spec, + PyModInitFunction initfunc) { struct _Py_ext_module_loader_info info; if (_Py_ext_module_loader_info_init_for_builtin(&info, name) < 0) { @@ -2336,8 +2501,8 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) assert(!_PyErr_Occurred(tstate)); assert(cached != NULL); /* The module might not have md_def set in certain reload cases. */ - assert(_PyModule_GetDef(mod) == NULL - || cached->def == _PyModule_GetDef(mod)); + assert(_PyModule_GetDefOrNull(mod) == NULL + || cached->def == _PyModule_GetDefOrNull(mod)); assert_singlephase(cached); goto finally; } @@ -2354,19 +2519,21 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) _extensions_cache_delete(info.path, info.name); } - struct _inittab *found = NULL; - for (struct _inittab *p = INITTAB; p->name != NULL; p++) { - if (_PyUnicode_EqualToASCIIString(info.name, p->name)) { - found = p; + PyModInitFunction p0 = NULL; + if (initfunc == NULL) { + struct _inittab *entry = lookup_inittab_entry(&info); + if (entry == NULL) { + mod = NULL; + _PyErr_SetModuleNotFoundError(name); + goto finally; } + + p0 = (PyModInitFunction)entry->initfunc; } - if (found == NULL) { - // not found - mod = Py_NewRef(Py_None); - goto finally; + else { + p0 = initfunc; } - PyModInitFunction p0 = (PyModInitFunction)found->initfunc; if (p0 == NULL) { /* Cannot re-init internal module ("sys" or "builtins") */ assert(is_core_module(tstate->interp, info.name, info.path)); @@ -2374,6 +2541,7 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) goto finally; } + #ifdef Py_GIL_DISABLED // This call (and the corresponding call to _PyImport_CheckGILForModule()) // would ideally be inside import_run_extension(). They are kept in the @@ -2397,6 +2565,33 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) return mod; } +PyObject* +PyImport_CreateModuleFromInitfunc( + PyObject *spec, PyObject *(*initfunc)(void)) +{ + if (initfunc == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + + PyThreadState *tstate = _PyThreadState_GET(); + + PyObject *name = PyObject_GetAttr(spec, &_Py_ID(name)); + if (name == NULL) { + return NULL; + } + + if (!PyUnicode_Check(name)) { + PyErr_Format(PyExc_TypeError, + "spec name must be string, not %T", name); + Py_DECREF(name); + return NULL; + } + + PyObject *mod = create_builtin(tstate, name, spec, initfunc); + Py_DECREF(name); + return mod; +} /*****************************/ /* the builtin modules table */ @@ -2979,7 +3174,7 @@ find_frozen(PyObject *nameobj, struct frozen_info *info) if (nameobj == NULL || nameobj == Py_None) { return FROZEN_BAD_NAME; } - const char *name = PyUnicode_AsUTF8(nameobj); + const char *name = _PyUnicode_AsUTF8NoNUL(nameobj); if (name == NULL) { // Note that this function previously used // _PyUnicode_EqualToASCIIString(). We clear the error here @@ -3166,7 +3361,7 @@ bootstrap_imp(PyThreadState *tstate) } // Create the _imp module from its definition. - PyObject *mod = create_builtin(tstate, name, spec); + PyObject *mod = create_builtin(tstate, name, spec, NULL); Py_CLEAR(name); Py_DECREF(spec); if (mod == NULL) { @@ -3398,6 +3593,13 @@ _PyImport_InitDefaultImportFunc(PyInterpreterState *interp) return -1; } IMPORT_FUNC(interp) = import_func; + + // Get the __lazy_import__ function + if (PyDict_GetItemStringRef(interp->builtins, "__lazy_import__", + &import_func) <= 0) { + return -1; + } + LAZY_IMPORT_FUNC(interp) = import_func; return 0; } @@ -3407,6 +3609,11 @@ _PyImport_IsDefaultImportFunc(PyInterpreterState *interp, PyObject *func) return func == IMPORT_FUNC(interp); } +int +_PyImport_IsDefaultLazyImportFunc(PyInterpreterState *interp, PyObject *func) +{ + return func == LAZY_IMPORT_FUNC(interp); +} /* Import a module, either built-in, frozen, or external, and return its module object WITH INCREMENTED REFERENCE COUNT */ @@ -3533,8 +3740,9 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level _PyErr_SetString(tstate, PyExc_KeyError, "'__name__' not in globals"); goto error; } - if (!PyDict_Check(globals)) { - _PyErr_SetString(tstate, PyExc_TypeError, "globals must be a dict"); + if (!PyAnyDict_Check(globals)) { + _PyErr_SetString(tstate, PyExc_TypeError, + "globals must be a dict or a frozendict"); goto error; } if (PyDict_GetItemRef(globals, &_Py_ID(__package__), &package) < 0) { @@ -3670,43 +3878,243 @@ resolve_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level return NULL; } -static PyObject * -import_find_and_load(PyThreadState *tstate, PyObject *abs_name) +PyObject * +_PyImport_ResolveName(PyThreadState *tstate, PyObject *name, + PyObject *globals, int level) { - PyObject *mod = NULL; + return resolve_name(tstate, name, globals, level); +} + +PyObject * +_PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import) +{ + PyObject *obj = NULL; + PyObject *fromlist = Py_None; + PyObject *import_func = NULL; + assert(lazy_import != NULL); + assert(PyLazyImport_CheckExact(lazy_import)); + + PyLazyImportObject *lz = (PyLazyImportObject *)lazy_import; PyInterpreterState *interp = tstate->interp; - int import_time = _PyInterpreterState_GetConfig(interp)->import_time; -#define import_level FIND_AND_LOAD(interp).import_level -#define accumulated FIND_AND_LOAD(interp).accumulated - PyTime_t t1 = 0, accumulated_copy = accumulated; + // Acquire the global import lock to serialize reification + _PyImport_AcquireLock(interp); - PyObject *sys_path, *sys_meta_path, *sys_path_hooks; - if (PySys_GetOptionalAttrString("path", &sys_path) < 0) { - return NULL; + // Check if we are already importing this module, if so, then we want to + // return an error that indicates we've hit a cycle which will indicate + // the value isn't yet available. + PyObject *importing = interp->imports.lazy_importing_modules; + if (importing == NULL) { + importing = interp->imports.lazy_importing_modules = PySet_New(NULL); + if (importing == NULL) { + _PyImport_ReleaseLock(interp); + return NULL; + } } - if (PySys_GetOptionalAttrString("meta_path", &sys_meta_path) < 0) { - Py_XDECREF(sys_path); + + assert(PyAnySet_CheckExact(importing)); + int is_loading = _PySet_Contains((PySetObject *)importing, lazy_import); + if (is_loading < 0) { + _PyImport_ReleaseLock(interp); return NULL; } - if (PySys_GetOptionalAttrString("path_hooks", &sys_path_hooks) < 0) { - Py_XDECREF(sys_meta_path); - Py_XDECREF(sys_path); + else if (is_loading == 1) { + PyObject *name = _PyLazyImport_GetName(lazy_import); + if (name == NULL) { + _PyImport_ReleaseLock(interp); + return NULL; + } + PyObject *errmsg = PyUnicode_FromFormat( + "cannot import name %R (most likely due to a circular import)", + name); + if (errmsg == NULL) { + Py_DECREF(name); + _PyImport_ReleaseLock(interp); + return NULL; + } + PyErr_SetImportErrorSubclass(PyExc_ImportCycleError, errmsg, + lz->lz_from, NULL); + Py_DECREF(errmsg); + Py_DECREF(name); + _PyImport_ReleaseLock(interp); return NULL; } - if (_PySys_Audit(tstate, "import", "OOOOO", - abs_name, Py_None, sys_path ? sys_path : Py_None, - sys_meta_path ? sys_meta_path : Py_None, - sys_path_hooks ? sys_path_hooks : Py_None) < 0) { - Py_XDECREF(sys_path_hooks); - Py_XDECREF(sys_meta_path); - Py_XDECREF(sys_path); - return NULL; + else if (PySet_Add(importing, lazy_import) < 0) { + goto error; + } + + Py_ssize_t dot = -1; + int full = 0; + if (lz->lz_attr != NULL) { + full = 1; + } + if (!full) { + dot = PyUnicode_FindChar(lz->lz_from, '.', 0, + PyUnicode_GET_LENGTH(lz->lz_from), 1); + } + if (dot < 0) { + full = 1; + } + + if (lz->lz_attr != NULL) { + if (PyUnicode_Check(lz->lz_attr)) { + fromlist = PyTuple_New(1); + if (fromlist == NULL) { + goto error; + } + Py_INCREF(lz->lz_attr); + PyTuple_SET_ITEM(fromlist, 0, lz->lz_attr); + } + else { + Py_INCREF(lz->lz_attr); + fromlist = lz->lz_attr; + } + } + + PyObject *globals = PyEval_GetGlobals(); + + if (PyMapping_GetOptionalItem(lz->lz_builtins, &_Py_ID(__import__), + &import_func) < 0) { + goto error; + } + if (import_func == NULL) { + PyErr_SetString(PyExc_ImportError, "__import__ not found"); + goto error; + } + if (full) { + obj = _PyEval_ImportNameWithImport( + tstate, import_func, globals, globals, + lz->lz_from, fromlist, _PyLong_GetZero() + ); } - Py_XDECREF(sys_path_hooks); - Py_XDECREF(sys_meta_path); - Py_XDECREF(sys_path); + else { + PyObject *name = PyUnicode_Substring(lz->lz_from, 0, dot); + if (name == NULL) { + goto error; + } + obj = _PyEval_ImportNameWithImport( + tstate, import_func, globals, globals, + name, fromlist, _PyLong_GetZero() + ); + Py_DECREF(name); + } + if (obj == NULL) { + goto error; + } + + if (lz->lz_attr != NULL && PyUnicode_Check(lz->lz_attr)) { + PyObject *from = obj; + obj = _PyEval_ImportFrom(tstate, from, lz->lz_attr); + Py_DECREF(from); + if (obj == NULL) { + goto error; + } + } + + assert(!PyLazyImport_CheckExact(obj)); + + goto ok; + +error: + Py_CLEAR(obj); + + // If an error occurred and we have frame information, add it to the + // exception. + if (PyErr_Occurred() && lz->lz_code != NULL && lz->lz_instr_offset >= 0) { + // Get the current exception - this already has the full traceback + // from the access point. + PyObject *exc = _PyErr_GetRaisedException(tstate); + + // Get import name - this can fail and set an exception. + PyObject *import_name = _PyLazyImport_GetName(lazy_import); + if (!import_name) { + // Failed to get import name, just restore original exception. + _PyErr_SetRaisedException(tstate, exc); + goto ok; + } + + // Resolve line number from instruction offset on demand. + int lineno = PyCode_Addr2Line((PyCodeObject *)lz->lz_code, + lz->lz_instr_offset*2); + + // Get strings - these can return NULL on encoding errors. + const char *filename_str = PyUnicode_AsUTF8(lz->lz_code->co_filename); + if (!filename_str) { + // Unicode conversion failed - clear error and restore original + // exception. + PyErr_Clear(); + Py_DECREF(import_name); + _PyErr_SetRaisedException(tstate, exc); + goto ok; + } + + const char *funcname_str = PyUnicode_AsUTF8(lz->lz_code->co_name); + if (!funcname_str) { + // Unicode conversion failed - clear error and restore original + // exception. + PyErr_Clear(); + Py_DECREF(import_name); + _PyErr_SetRaisedException(tstate, exc); + goto ok; + } + + // Create a cause exception showing where the lazy import was declared. + PyObject *msg = PyUnicode_FromFormat( + "lazy import of '%U' raised an exception during resolution", + import_name + ); + Py_DECREF(import_name); // Done with import_name. + + if (!msg) { + // Failed to create message - restore original exception. + _PyErr_SetRaisedException(tstate, exc); + goto ok; + } + + PyObject *cause_exc = PyObject_CallOneArg(PyExc_ImportError, msg); + Py_DECREF(msg); // Done with msg. + + if (!cause_exc) { + // Failed to create exception - restore original. + _PyErr_SetRaisedException(tstate, exc); + goto ok; + } + + // Add traceback entry for the lazy import declaration. + _PyErr_SetRaisedException(tstate, cause_exc); + _PyTraceback_Add(funcname_str, filename_str, lineno); + PyObject *cause_with_tb = _PyErr_GetRaisedException(tstate); + // Set the cause on the original exception. + PyException_SetCause(exc, cause_with_tb); // Steals ref to cause_with_tb. + + // Restore the original exception with its full traceback. + _PyErr_SetRaisedException(tstate, exc); + } + +ok: + if (PySet_Discard(importing, lazy_import) < 0) { + Py_CLEAR(obj); + } + + // Release the global import lock. + _PyImport_ReleaseLock(interp); + + Py_XDECREF(fromlist); + Py_XDECREF(import_func); + return obj; +} + +static PyObject * +import_find_and_load(PyThreadState *tstate, PyObject *abs_name) +{ + PyObject *mod = NULL; + PyInterpreterState *interp = tstate->interp; + int import_time = _PyInterpreterState_GetConfig(interp)->import_time; +#define import_level FIND_AND_LOAD(interp).import_level +#define accumulated FIND_AND_LOAD(interp).accumulated + + PyTime_t t1 = 0, accumulated_copy = accumulated; /* XOptions is initialized after first some imports. * So we can't have negative cache before completed initialization. @@ -3751,6 +4159,28 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name) #undef accumulated } +static PyObject * +get_abs_name(PyThreadState *tstate, PyObject *name, PyObject *globals, + int level) +{ + if (level > 0) { + return resolve_name(tstate, name, globals, level); + } + if (PyUnicode_GET_LENGTH(name) == 0) { + _PyErr_SetString(tstate, PyExc_ValueError, "Empty module name"); + return NULL; + } + return Py_NewRef(name); +} + +PyObject * +_PyImport_GetAbsName(PyThreadState *tstate, PyObject *name, + PyObject *globals, int level) +{ + return get_abs_name(tstate, name, globals, level); +} + + PyObject * PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, PyObject *locals, PyObject *fromlist, @@ -3760,7 +4190,6 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, PyObject *abs_name = NULL; PyObject *final_mod = NULL; PyObject *mod = NULL; - PyObject *package = NULL; PyInterpreterState *interp = tstate->interp; int has_from; @@ -3782,17 +4211,9 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, goto error; } - if (level > 0) { - abs_name = resolve_name(tstate, name, globals, level); - if (abs_name == NULL) - goto error; - } - else { /* level == 0 */ - if (PyUnicode_GET_LENGTH(name) == 0) { - _PyErr_SetString(tstate, PyExc_ValueError, "Empty module name"); - goto error; - } - abs_name = Py_NewRef(name); + abs_name = get_abs_name(tstate, name, globals, level); + if (abs_name == NULL) { + goto error; } mod = import_get_module(tstate, abs_name); @@ -3804,6 +4225,27 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, if (import_ensure_initialized(tstate->interp, mod, abs_name) < 0) { goto error; } + /* Verify the module is still in sys.modules. Another thread may have + removed it (due to import failure) between our import_get_module() + call and the _initializing check in import_ensure_initialized(). + If removed, we retry the import to preserve normal semantics: the + caller gets the exception from the actual import failure rather + than a synthetic error. */ + PyObject *mod_check = import_get_module(tstate, abs_name); + if (mod_check != mod) { + Py_XDECREF(mod_check); + if (_PyErr_Occurred(tstate)) { + goto error; + } + Py_DECREF(mod); + mod = import_find_and_load(tstate, abs_name); + if (mod == NULL) { + goto error; + } + } + else { + Py_DECREF(mod_check); + } } else { Py_XDECREF(mod); @@ -3889,13 +4331,285 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, error: Py_XDECREF(abs_name); Py_XDECREF(mod); - Py_XDECREF(package); if (final_mod == NULL) { remove_importlib_frames(tstate); } return final_mod; } +// ensure we have the set for the parent module name in sys.lazy_modules. +// Returns a new reference. +static PyObject * +ensure_lazy_pending_submodules(PyDictObject *lazy_modules, PyObject *parent) +{ + PyObject *lazy_submodules; + Py_BEGIN_CRITICAL_SECTION(lazy_modules); + int err = _PyDict_GetItemRef_Unicode_LockHeld(lazy_modules, parent, + &lazy_submodules); + if (err == 0) { + // value isn't present + lazy_submodules = PySet_New(NULL); + if (lazy_submodules != NULL && + _PyDict_SetItem_LockHeld(lazy_modules, parent, + lazy_submodules) < 0) { + Py_CLEAR(lazy_submodules); + } + } + Py_END_CRITICAL_SECTION(); + return lazy_submodules; +} + +// Records all parent-child relationships in lazy_pending_submodules +// for a lazily imported module name. When a parent module's attribute +// is accessed, _Py_module_getattro_impl will check lazy_pending_submodules +// and trigger the import. +static int +register_lazy_on_parent(PyThreadState *tstate, PyObject *name) +{ + int ret = -1; + PyObject *parent = NULL; + PyObject *child = NULL; + + PyInterpreterState *interp = tstate->interp; + PyObject *lazy_pending_submodules = LAZY_PENDING_SUBMODULES(interp); + assert(lazy_pending_submodules != NULL); + + Py_INCREF(name); + while (true) { + Py_ssize_t dot = PyUnicode_FindChar(name, '.', 0, + PyUnicode_GET_LENGTH(name), -1); + if (dot < 0) { + PyObject *lazy_submodules = ensure_lazy_pending_submodules( + (PyDictObject *)lazy_pending_submodules, name); + if (lazy_submodules == NULL) { + goto done; + } + Py_DECREF(lazy_submodules); + ret = 0; + goto done; + } + parent = PyUnicode_Substring(name, 0, dot); + if (parent == NULL) { + goto done; + } + Py_XDECREF(child); + child = PyUnicode_Substring(name, dot + 1, PyUnicode_GET_LENGTH(name)); + if (child == NULL) { + goto done; + } + + PyObject *lazy_submodules = ensure_lazy_pending_submodules( + (PyDictObject *)lazy_pending_submodules, parent); + if (lazy_submodules == NULL) { + goto done; + } + + if (PySet_Add(lazy_submodules, child) < 0) { + Py_DECREF(lazy_submodules); + goto done; + } + Py_DECREF(lazy_submodules); + + Py_SETREF(name, parent); + parent = NULL; + } + +done: + Py_XDECREF(child); + Py_XDECREF(parent); + Py_XDECREF(name); + return ret; +} + +static int +register_from_lazy_on_parent(PyThreadState *tstate, PyObject *abs_name, + PyObject *from) +{ + PyObject *fromname = PyUnicode_FromFormat("%U.%U", abs_name, from); + if (fromname == NULL) { + return -1; + } + + // Add the module name to sys.lazy_modules set (PEP 810). + PyObject *lazy_modules = LAZY_MODULES(tstate->interp); + if (PySet_Add(lazy_modules, fromname) < 0) { + Py_DECREF(fromname); + return -1; + } + + int res = register_lazy_on_parent(tstate, fromname); + Py_DECREF(fromname); + return res; +} + +PyObject * +_PyImport_TryLoadLazySubmodule(PyObject *mod_name, PyObject *attr_name) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyObject *lazy_pending = LAZY_PENDING_SUBMODULES(interp); + if (lazy_pending == NULL) { + return NULL; + } + + PyObject *pending_set; + int rc = PyDict_GetItemRef(lazy_pending, mod_name, &pending_set); + if (rc <= 0) { + return NULL; + } + + int contains = PySet_Contains(pending_set, attr_name); + if (contains <= 0) { + Py_DECREF(pending_set); + return NULL; + } + + PyObject *full_name = PyUnicode_FromFormat("%U.%U", mod_name, attr_name); + if (full_name == NULL) { + Py_DECREF(pending_set); + return NULL; + } + + PyObject *mod = PyImport_ImportModuleLevelObject( + full_name, NULL, NULL, NULL, 0); + if (mod == NULL) { + Py_DECREF(pending_set); + Py_DECREF(full_name); + return NULL; + } + Py_DECREF(mod); + + if (PySet_Discard(pending_set, attr_name) < 0) { + Py_DECREF(pending_set); + Py_DECREF(full_name); + return NULL; + } + Py_DECREF(pending_set); + + PyObject *submod = PyImport_GetModule(full_name); + Py_DECREF(full_name); + return submod; +} + +PyObject * +_PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, + PyObject *name, PyObject *builtins, + PyObject *globals, PyObject *locals, + PyObject *fromlist, int level) +{ + assert(name != NULL); + if (!PyUnicode_Check(name)) { + _PyErr_Format(tstate, PyExc_TypeError, + "module name must be a string, got %T", name); + return NULL; + } + if (level < 0) { + _PyErr_SetString(tstate, PyExc_ValueError, "level must be >= 0"); + return NULL; + } + + PyObject *abs_name = get_abs_name(tstate, name, globals, level); + if (abs_name == NULL) { + return NULL; + } + + PyInterpreterState *interp = tstate->interp; + _PyInterpreterFrame *frame = _PyEval_GetFrame(); + if (frame == NULL || frame->f_globals != frame->f_locals) { + Py_DECREF(abs_name); + PyErr_SetString(PyExc_SyntaxError, + "'lazy import' is only allowed at module level"); + return NULL; + } + + // Check if the filter disables the lazy import. + // We must hold a reference to the filter while calling it to prevent + // use-after-free if another thread replaces it via + // PyImport_SetLazyImportsFilter. + LAZY_IMPORTS_LOCK(interp); + PyObject *filter = Py_XNewRef(LAZY_IMPORTS_FILTER(interp)); + LAZY_IMPORTS_UNLOCK(interp); + + if (filter != NULL) { + PyObject *modname; + if (PyDict_GetItemRef(globals, &_Py_ID(__name__), &modname) < 0) { + Py_DECREF(filter); + Py_DECREF(abs_name); + return NULL; + } + if (modname == NULL) { + assert(!PyErr_Occurred()); + modname = Py_NewRef(Py_None); + } + if (fromlist == NULL) { + assert(!PyErr_Occurred()); + fromlist = Py_NewRef(Py_None); + } + PyObject *args[] = {modname, abs_name, fromlist}; + PyObject *res = PyObject_Vectorcall(filter, args, 3, NULL); + + Py_DECREF(modname); + Py_DECREF(filter); + + if (res == NULL) { + Py_DECREF(abs_name); + return NULL; + } + + int is_true = PyObject_IsTrue(res); + Py_DECREF(res); + + if (is_true < 0) { + Py_DECREF(abs_name); + return NULL; + } + if (!is_true) { + Py_DECREF(abs_name); + return PyImport_ImportModuleLevelObject( + name, globals, locals, fromlist, level + ); + } + } + + // here, 'filter' is either NULL or is equivalent to a borrowed reference + PyObject *res = _PyLazyImport_New(frame, builtins, abs_name, fromlist); + if (res == NULL) { + Py_DECREF(abs_name); + return NULL; + } + + // Add the module name to sys.lazy_modules set (PEP 810). + PyObject *lazy_modules = LAZY_MODULES(tstate->interp); + if (PySet_Add(lazy_modules, abs_name) < 0) { + goto error; + } + + if (fromlist && PyUnicode_Check(fromlist)) { + if (register_from_lazy_on_parent(tstate, abs_name, fromlist) < 0) { + goto error; + } + } + else if (fromlist && PyTuple_Check(fromlist) && + PyTuple_GET_SIZE(fromlist)) { + for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(fromlist); i++) { + if (register_from_lazy_on_parent(tstate, abs_name, + PyTuple_GET_ITEM(fromlist, i)) < 0) + { + goto error; + } + } + } + else if (register_lazy_on_parent(tstate, abs_name) < 0) { + goto error; + } + + Py_DECREF(abs_name); + return res; +error: + Py_DECREF(abs_name); + Py_DECREF(res); + return NULL; +} + PyObject * PyImport_ImportModuleLevel(const char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) @@ -4102,6 +4816,11 @@ _PyImport_ClearCore(PyInterpreterState *interp) Py_CLEAR(MODULES_BY_INDEX(interp)); Py_CLEAR(IMPORTLIB(interp)); Py_CLEAR(IMPORT_FUNC(interp)); + Py_CLEAR(LAZY_IMPORT_FUNC(interp)); + Py_CLEAR(interp->imports.lazy_pending_submodules); + Py_CLEAR(interp->imports.lazy_modules); + Py_CLEAR(interp->imports.lazy_importing_modules); + Py_CLEAR(interp->imports.lazy_imports_filter); } void @@ -4237,6 +4956,58 @@ PyImport_ImportModuleAttrString(const char *modname, const char *attrname) return result; } +int +PyImport_SetLazyImportsFilter(PyObject *filter) +{ + if (filter == Py_None) { + filter = NULL; + } + if (filter != NULL && !PyCallable_Check(filter)) { + PyErr_SetString(PyExc_ValueError, + "filter provided but is not callable"); + return -1; + } + + PyInterpreterState *interp = _PyInterpreterState_GET(); + // Exchange the filter w/ the lock held. We can't use Py_XSETREF + // because we need to release the lock before the decref. + LAZY_IMPORTS_LOCK(interp); + PyObject *old = LAZY_IMPORTS_FILTER(interp); + LAZY_IMPORTS_FILTER(interp) = Py_XNewRef(filter); + LAZY_IMPORTS_UNLOCK(interp); + Py_XDECREF(old); + return 0; +} + +/* Return a strong reference to the current lazy imports filter + * or NULL if none exists. This function always succeeds. + */ +PyObject * +PyImport_GetLazyImportsFilter(void) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + LAZY_IMPORTS_LOCK(interp); + PyObject *res = Py_XNewRef(LAZY_IMPORTS_FILTER(interp)); + LAZY_IMPORTS_UNLOCK(interp); + return res; +} + +int +PyImport_SetLazyImportsMode(PyImport_LazyImportsMode mode) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + FT_ATOMIC_STORE_INT_RELAXED(LAZY_IMPORTS_MODE(interp), mode); + return 0; +} + +/* Checks if lazy imports is globally enabled or disabled. Return 1 when + * globally forced on, 0 when globally forced off, or -1 when not set.*/ +PyImport_LazyImportsMode +PyImport_GetLazyImportsMode(void) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + return FT_ATOMIC_LOAD_INT_RELAXED(LAZY_IMPORTS_MODE(interp)); +} /**************/ /* the module */ @@ -4263,13 +5034,14 @@ _imp.acquire_lock Acquires the interpreter's import lock for the current thread. -This lock should be used by import hooks to ensure thread-safety when importing -modules. On platforms without threads, this function does nothing. +This lock should be used by import hooks to ensure thread-safety when +importing modules. On platforms without threads, this function does +nothing. [clinic start generated code]*/ static PyObject * _imp_acquire_lock_impl(PyObject *module) -/*[clinic end generated code: output=1aff58cb0ee1b026 input=4a2d4381866d5fdc]*/ +/*[clinic end generated code: output=1aff58cb0ee1b026 input=60e9c1b4ab471ead]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); _PyImport_AcquireLock(interp); @@ -4352,7 +5124,13 @@ _imp_create_builtin(PyObject *module, PyObject *spec) return NULL; } - PyObject *mod = create_builtin(tstate, name, spec); + if (PyUnicode_GetLength(name) == 0) { + PyErr_Format(PyExc_ValueError, "name must not be empty"); + Py_DECREF(name); + return NULL; + } + + PyObject *mod = create_builtin(tstate, name, spec, NULL); Py_DECREF(name); return mod; } @@ -4421,6 +5199,7 @@ _imp_init_frozen_impl(PyObject *module, PyObject *name) } /*[clinic input] +@permit_long_summary _imp.find_frozen name: unicode @@ -4441,7 +5220,7 @@ The returned info (a 2-tuple): static PyObject * _imp_find_frozen_impl(PyObject *module, PyObject *name, int withdata) -/*[clinic end generated code: output=8c1c3c7f925397a5 input=22a8847c201542fd]*/ +/*[clinic end generated code: output=8c1c3c7f925397a5 input=30a7a50da49eca97]*/ { struct frozen_info info; frozen_status status = find_frozen(name, &info); @@ -4628,6 +5407,7 @@ _imp__override_frozen_modules_for_tests_impl(PyObject *module, int override) } /*[clinic input] +@permit_long_summary _imp._override_multi_interp_extensions_check override: int @@ -4641,7 +5421,7 @@ _imp._override_multi_interp_extensions_check static PyObject * _imp__override_multi_interp_extensions_check_impl(PyObject *module, int override) -/*[clinic end generated code: output=3ff043af52bbf280 input=e086a2ea181f92ae]*/ +/*[clinic end generated code: output=3ff043af52bbf280 input=24f23f8510a7f6e7]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); if (_Py_IsMainInterpreter(interp)) { @@ -4678,6 +5458,7 @@ static PyObject * _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) /*[clinic end generated code: output=83249b827a4fde77 input=c31b954f4cf4e09d]*/ { + FILE *fp = NULL; PyObject *mod = NULL; PyThreadState *tstate = _PyThreadState_GET(); @@ -4692,8 +5473,8 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) assert(!_PyErr_Occurred(tstate)); assert(cached != NULL); /* The module might not have md_def set in certain reload cases. */ - assert(_PyModule_GetDef(mod) == NULL - || cached->def == _PyModule_GetDef(mod)); + assert(_PyModule_GetDefOrNull(mod) == NULL + || cached->def == _PyModule_GetDefOrNull(mod)); assert_singlephase(cached); goto finally; } @@ -4718,20 +5499,24 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) } /* We would move this (and the fclose() below) into - * _PyImport_GetModInitFunc(), but it isn't clear if the intervening + * _PyImport_GetModuleExportHooks(), but it isn't clear if the intervening * code relies on fp still being open. */ - FILE *fp; if (file != NULL) { fp = Py_fopen(info.filename, "r"); if (fp == NULL) { goto finally; } } - else { - fp = NULL; - } - PyModInitFunction p0 = _PyImport_GetModInitFunc(&info, fp); + PyModInitFunction p0 = NULL; + PyModExportFunction ex0 = NULL; + _PyImport_GetModuleExportHooks(&info, fp, &p0, &ex0); + if (ex0) { + mod = import_run_modexport(tstate, ex0, &info, spec); + // Modules created from slots handle GIL enablement (Py_mod_gil slot) + // when they're created. + goto finally; + } if (p0 == NULL) { goto finally; } @@ -4753,12 +5538,10 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) } #endif - // XXX Shouldn't this happen in the error cases too (i.e. in "finally")? - if (fp) { +finally: + if (fp != NULL) { fclose(fp); } - -finally: _Py_ext_module_loader_info_clear(&info); return mod; } @@ -4826,6 +5609,25 @@ _imp_source_hash_impl(PyObject *module, long key, Py_buffer *source) return PyBytes_FromStringAndSize(hash.data, sizeof(hash.data)); } +/*[clinic input] +_imp._set_lazy_attributes + modobj: object + name: unicode + / +Sets attributes to lazy submodules on the module, as side effects. +[clinic start generated code]*/ + +static PyObject * +_imp__set_lazy_attributes_impl(PyObject *module, PyObject *modobj, + PyObject *name) +/*[clinic end generated code: output=3369bb3242b1f043 input=38ea6f30956dd7d6]*/ +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (PySet_Discard(LAZY_MODULES(interp), name) < 0) { + return NULL; + } + Py_RETURN_NONE; +} PyDoc_STRVAR(doc_imp, "(Extremely) low-level import machinery bits as used by importlib."); @@ -4850,6 +5652,7 @@ static PyMethodDef imp_methods[] = { _IMP_EXEC_BUILTIN_METHODDEF _IMP__FIX_CO_FILENAME_METHODDEF _IMP_SOURCE_HASH_METHODDEF + _IMP__SET_LAZY_ATTRIBUTES_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -4874,6 +5677,7 @@ imp_module_exec(PyObject *module) static PyModuleDef_Slot imp_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, imp_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Python/importdl.c b/Python/importdl.c index 802843fe7b9dce..537e8d869dc93c 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -5,38 +5,19 @@ #include "pycore_call.h" // _PyObject_CallMethod() #include "pycore_import.h" // _PyImport_SwapPackageContext() #include "pycore_importdl.h" -#include "pycore_moduleobject.h" // _PyModule_GetDef() +#include "pycore_moduleobject.h" // _PyModule_GetDefOrNull() #include "pycore_pyerrors.h" // _PyErr_FormatFromCause() #include "pycore_runtime.h" // _Py_ID() -/* ./configure sets HAVE_DYNAMIC_LOADING if dynamic loading of modules is - supported on this platform. configure will then compile and link in one - of the dynload_*.c files, as appropriate. We will call a function in - those modules to get a function pointer to the module's init function. -*/ -#ifdef HAVE_DYNAMIC_LOADING - -#ifdef MS_WINDOWS -extern dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, - const char *shortname, - PyObject *pathname, - FILE *fp); -#else -extern dl_funcptr _PyImport_FindSharedFuncptr(const char *prefix, - const char *shortname, - const char *pathname, FILE *fp); -#endif - -#endif /* HAVE_DYNAMIC_LOADING */ - - /***********************************/ /* module info to use when loading */ /***********************************/ -static const char * const ascii_only_prefix = "PyInit"; -static const char * const nonascii_prefix = "PyInitU"; +static const struct hook_prefixes ascii_only_prefixes = { + "PyInit", "PyModExport"}; +static const struct hook_prefixes nonascii_prefixes = { + "PyInitU", "PyModExportU"}; /* Get the variable part of a module's export symbol name. * Returns a bytes instance. For non-ASCII-named modules, the name is @@ -45,7 +26,7 @@ static const char * const nonascii_prefix = "PyInitU"; * nonascii_prefix, as appropriate. */ static PyObject * -get_encoded_name(PyObject *name, const char **hook_prefix) { +get_encoded_name(PyObject *name, const struct hook_prefixes **hook_prefixes) { PyObject *tmp; PyObject *encoded = NULL; PyObject *modname = NULL; @@ -72,7 +53,7 @@ get_encoded_name(PyObject *name, const char **hook_prefix) { /* Encode to ASCII or Punycode, as needed */ encoded = PyUnicode_AsEncodedString(name, "ascii", NULL); if (encoded != NULL) { - *hook_prefix = ascii_only_prefix; + *hook_prefixes = &ascii_only_prefixes; } else { if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { PyErr_Clear(); @@ -80,7 +61,7 @@ get_encoded_name(PyObject *name, const char **hook_prefix) { if (encoded == NULL) { goto error; } - *hook_prefix = nonascii_prefix; + *hook_prefixes = &nonascii_prefixes; } else { goto error; } @@ -130,7 +111,7 @@ _Py_ext_module_loader_info_init(struct _Py_ext_module_loader_info *p_info, assert(PyUnicode_GetLength(name) > 0); info.name = Py_NewRef(name); - info.name_encoded = get_encoded_name(info.name, &info.hook_prefix); + info.name_encoded = get_encoded_name(info.name, &info.hook_prefixes); if (info.name_encoded == NULL) { _Py_ext_module_loader_info_clear(&info); return -1; @@ -175,7 +156,6 @@ _Py_ext_module_loader_info_init_for_builtin( PyObject *name) { assert(PyUnicode_Check(name)); - assert(PyUnicode_FindChar(name, '.', 0, PyUnicode_GetLength(name), -1) == -1); assert(PyUnicode_GetLength(name) > 0); PyObject *name_encoded = PyUnicode_AsEncodedString(name, "ascii", NULL); @@ -189,7 +169,7 @@ _Py_ext_module_loader_info_init_for_builtin( /* We won't need filename. */ .path=name, .origin=_Py_ext_module_origin_BUILTIN, - .hook_prefix=ascii_only_prefix, + .hook_prefixes=&ascii_only_prefixes, .newcontext=NULL, }; return 0; @@ -377,39 +357,66 @@ _Py_ext_module_loader_result_apply_error( /********************************************/ #ifdef HAVE_DYNAMIC_LOADING -PyModInitFunction -_PyImport_GetModInitFunc(struct _Py_ext_module_loader_info *info, - FILE *fp) +static dl_funcptr +findfuncptr(const char *prefix, const char *name_buf, + struct _Py_ext_module_loader_info *info, + FILE *fp) { - const char *name_buf = PyBytes_AS_STRING(info->name_encoded); - dl_funcptr exportfunc; #ifdef MS_WINDOWS - exportfunc = _PyImport_FindSharedFuncptrWindows( - info->hook_prefix, name_buf, info->filename, fp); + return _PyImport_FindSharedFuncptrWindows( + prefix, name_buf, info->filename, fp); #else - { - const char *path_buf = PyBytes_AS_STRING(info->filename_encoded); - exportfunc = _PyImport_FindSharedFuncptr( - info->hook_prefix, name_buf, path_buf, fp); - } + const char *path_buf = PyBytes_AS_STRING(info->filename_encoded); + return _PyImport_FindSharedFuncptr( + prefix, name_buf, path_buf, fp); #endif +} - if (exportfunc == NULL) { - if (!PyErr_Occurred()) { - PyObject *msg; - msg = PyUnicode_FromFormat( - "dynamic module does not define " - "module export function (%s_%s)", - info->hook_prefix, name_buf); - if (msg != NULL) { - PyErr_SetImportError(msg, info->name, info->filename); - Py_DECREF(msg); - } - } - return NULL; +int +_PyImport_GetModuleExportHooks( + struct _Py_ext_module_loader_info *info, + FILE *fp, + PyModInitFunction *modinit, + PyModExportFunction *modexport) +{ + *modinit = NULL; + *modexport = NULL; + + const char *name_buf = PyBytes_AS_STRING(info->name_encoded); + dl_funcptr exportfunc; + + exportfunc = findfuncptr( + info->hook_prefixes->export_prefix, + name_buf, info, fp); + if (exportfunc) { + *modexport = (PyModExportFunction)exportfunc; + return 2; + } + if (PyErr_Occurred()) { + return -1; + } + + exportfunc = findfuncptr( + info->hook_prefixes->init_prefix, + name_buf, info, fp); + if (exportfunc) { + *modinit = (PyModInitFunction)exportfunc; + return 1; } - return (PyModInitFunction)exportfunc; + if (!PyErr_Occurred()) { + PyObject *msg; + msg = PyUnicode_FromFormat( + "dynamic module does not define " + "module export function (%s_%s or %s_%s)", + info->hook_prefixes->export_prefix, name_buf, + info->hook_prefixes->init_prefix, name_buf); + if (msg != NULL) { + PyErr_SetImportError(msg, info->name, info->filename); + Py_DECREF(msg); + } + } + return -1; } #endif /* HAVE_DYNAMIC_LOADING */ @@ -477,7 +484,7 @@ _PyImport_RunModInitFunc(PyModInitFunction p0, res.def = (PyModuleDef *)m; /* Run PyModule_FromDefAndSpec() to finish loading the module. */ } - else if (info->hook_prefix == nonascii_prefix) { + else if (info->hook_prefixes == &nonascii_prefixes) { /* Non-ASCII is only supported for multi-phase init. */ res.kind = _Py_ext_module_kind_MULTIPHASE; /* Don't allow legacy init for non-ASCII module names. */ @@ -496,7 +503,7 @@ _PyImport_RunModInitFunc(PyModInitFunction p0, goto error; } - res.def = _PyModule_GetDef(m); + res.def = _PyModule_GetDefOrNull(m); if (res.def == NULL) { PyErr_Clear(); _Py_ext_module_loader_result_set_error( diff --git a/Python/initconfig.c b/Python/initconfig.c index cc0db19d416058..eb0a5ef7aa0136 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -111,6 +111,7 @@ static const PyConfigSpec PYCONFIG_SPEC[] = { SPEC(base_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_prefix")), SPEC(bytes_warning, UINT, PUBLIC, SYS_FLAG(9)), SPEC(cpu_count, INT, PUBLIC, NO_SYS), + SPEC(lazy_imports, INT, PUBLIC, NO_SYS), SPEC(exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("exec_prefix")), SPEC(executable, WSTR_OPT, PUBLIC, SYS_ATTR("executable")), SPEC(inspect, BOOL, PUBLIC, SYS_FLAG(1)), @@ -160,6 +161,7 @@ static const PyConfigSpec PYCONFIG_SPEC[] = { SPEC(legacy_windows_stdio, BOOL, READ_ONLY, NO_SYS), #endif SPEC(malloc_stats, BOOL, READ_ONLY, NO_SYS), + SPEC(pymalloc_hugepages, BOOL, READ_ONLY, NO_SYS), SPEC(orig_argv, WSTR_LIST, READ_ONLY, SYS_ATTR("orig_argv")), SPEC(parse_argv, BOOL, READ_ONLY, NO_SYS), SPEC(pathconfig_warnings, BOOL, READ_ONLY, NO_SYS), @@ -239,223 +241,349 @@ static const PyConfigSpec PYPRECONFIG_SPEC[] = { // Forward declarations -static PyObject* -config_get(const PyConfig *config, const PyConfigSpec *spec, - int use_sys); +static PyObject* config_get(const PyConfig *config, const PyConfigSpec *spec, + int use_sys); +static void initconfig_free_wstr(wchar_t *member); +static void initconfig_free_wstr_list(PyWideStringList *list); +static void initconfig_free_config(const PyConfig *config); /* --- Command line options --------------------------------------- */ -/* Short usage message (with %s for argv0) */ +/* + * Help text markup (matching Lib/_colorize.py Argparse theme). + * + * Color spans, #X{...} where "}" resets to default color: + * #b{...} label bold yellow + * #B{...} summary label yellow + * #E{...} env var (primary) bold cyan + * #e{...} env var reference cyan + * #h{...} heading bold blue + * #L{...} long option bold cyan + * #s{...} short option bold green + * #S{...} summary short opt green + * + * Runtime substitutions (no "{" follows): + * #P program name (bold magenta) + * #D path separator (DELIM) + * #H PYTHONHOMEHELP default search path + * + * fprint_help() walks the string, expanding color codes only when colorize=1 + * and substituting runtime values regardless. + */ + +#if defined(MS_WINDOWS) +# define PYTHONHOMEHELP "<prefix>\\python{major}{minor}" +#else +# define PYTHONHOMEHELP "<prefix>/lib/pythonX.X" +#endif + +/* Determine if we can emit ANSI color codes on the given stream. + * Logic mirrors Lib/_colorize.py:can_colorize(). */ +static int +_Py_can_colorize(FILE *f) +{ + const char *env; + + env = Py_GETENV("PYTHON_COLORS"); + if (env) { + if (strcmp(env, "0") == 0) { + return 0; + } + if (strcmp(env, "1") == 0) { + return 1; + } + } + if (getenv("NO_COLOR")) { + return 0; + } + if (getenv("FORCE_COLOR")) { + return 1; + } + env = getenv("TERM"); + if (env && strcmp(env, "dumb") == 0) { + return 0; + } +#if defined(MS_WINDOWS) && defined(HAVE_WINDOWS_CONSOLE_IO) + { + DWORD mode = 0; + DWORD nStdHandle = (f == stderr) ? STD_ERROR_HANDLE + : STD_OUTPUT_HANDLE; + HANDLE handle = GetStdHandle(nStdHandle); + if (!GetConsoleMode(handle, &mode) + || !(mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + { + return 0; + } + } +#endif + return isatty(fileno(f)); +} + +/* Walk help text, expanding markup: + * #X{...} color span (only emitted when colorize=1; '}' resets). + * #X runtime substitution (program name, DELIM, PYTHONHOMEHELP). + * See the markup table above the macro/comment block. */ +static void +fprint_help(FILE *f, const char *text, int colorize, const wchar_t *program) +{ + for (const char *p = text; *p; ) { + if (*p == '#' && p[1]) { + char code = p[1]; + if (p[2] == '{') { + /* Color span open */ + const char *seq = NULL; + switch (code) { + case 'h': seq = "\x1b[1;34m"; break; // heading + case 'E': seq = "\x1b[1;36m"; break; // env var primary + case 'e': seq = "\x1b[36m"; break; // env var reference + case 'L': seq = "\x1b[1;36m"; break; // long option + case 'b': seq = "\x1b[1;33m"; break; // label + case 'B': seq = "\x1b[33m"; break; // summary label + case 's': seq = "\x1b[1;32m"; break; // short option + case 'S': seq = "\x1b[32m"; break; // summary short option + } + if (colorize && seq) fputs(seq, f); + p += 3; // skip "#X{" + continue; + } + /* Runtime substitution */ + switch (code) { + case 'P': // program name with bold magenta + if (colorize) fputs("\x1b[1;35m", f); + if (program) fprintf(f, "%ls", program); + if (colorize) fputs("\x1b[0m", f); + break; + case 'D': + fputc((char)DELIM, f); + break; + case 'H': + fputs(PYTHONHOMEHELP, f); + break; + default: // unknown: emit literally + fputc('#', f); + fputc(code, f); + break; + } + p += 2; // skip "#X" + continue; + } + if (*p == '}') { + if (colorize) fputs("\x1b[0m", f); + p++; + continue; + } + fputc(*p++, f); + } +} + +/* Short usage message */ static const char usage_line[] = -"usage: %ls [option] ... [-c cmd | -m mod | file | -] [arg] ...\n"; +"#h{usage:} #P [#S{option}] #S{...} " +"[#S{-c} #B{cmd} | #S{-m} #B{mod} | #S{file} | #S{-}] " +"[#S{arg}] #S{...}\n" +; /* Long help message */ /* Lines sorted by option name; keep in sync with usage_envvars* below */ -static const char usage_help[] = "\ -Options (and corresponding environment variables):\n\ --b : issue warnings about converting bytes/bytearray to str and comparing\n\ - bytes/bytearray with str or bytes with int. (-bb: issue errors)\n\ --B : don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x\n\ --c cmd : program passed in as string (terminates option list)\n\ --d : turn on parser debugging output (for experts only, only works on\n\ - debug builds); also PYTHONDEBUG=x\n\ --E : ignore PYTHON* environment variables (such as PYTHONPATH)\n\ --h : print this help message and exit (also -? or --help)\n\ --i : inspect interactively after running script; forces a prompt even\n\ - if stdin does not appear to be a terminal; also PYTHONINSPECT=x\n\ --I : isolate Python from the user's environment (implies -E, -P and -s)\n\ --m mod : run library module as a script (terminates option list)\n\ --O : remove assert and __debug__-dependent statements; add .opt-1 before\n\ - .pyc extension; also PYTHONOPTIMIZE=x\n\ --OO : do -O changes and also discard docstrings; add .opt-2 before\n\ - .pyc extension\n\ --P : don't prepend a potentially unsafe path to sys.path; also\n\ - PYTHONSAFEPATH\n\ --q : don't print version and copyright messages on interactive startup\n\ --s : don't add user site directory to sys.path; also PYTHONNOUSERSITE=x\n\ --S : don't imply 'import site' on initialization\n\ --u : force the stdout and stderr streams to be unbuffered;\n\ - this option has no effect on stdin; also PYTHONUNBUFFERED=x\n\ --v : verbose (trace import statements); also PYTHONVERBOSE=x\n\ - can be supplied multiple times to increase verbosity\n\ --V : print the Python version number and exit (also --version)\n\ - when given twice, print more information about the build\n\ --W arg : warning control; arg is action:message:category:module:lineno\n\ - also PYTHONWARNINGS=arg\n\ --x : skip first line of source, allowing use of non-Unix forms of #!cmd\n\ --X opt : set implementation-specific option\n\ ---check-hash-based-pycs always|default|never:\n\ - control how Python invalidates hash-based .pyc files\n\ ---help-env: print help about Python environment variables and exit\n\ ---help-xoptions: print help about implementation-specific -X options and exit\n\ ---help-all: print complete help information and exit\n\ -\n\ -Arguments:\n\ -file : program read from script file\n\ -- : program read from stdin (default; interactive mode if a tty)\n\ -arg ...: arguments passed to program in sys.argv[1:]\n\ -"; - -static const char usage_xoptions[] = "\ -The following implementation-specific options are available:\n\ --X cpu_count=N: override the return value of os.cpu_count();\n\ - -X cpu_count=default cancels overriding; also PYTHON_CPU_COUNT\n\ --X dev : enable Python Development Mode; also PYTHONDEVMODE\n\ --X faulthandler: dump the Python traceback on fatal errors;\n\ - also PYTHONFAULTHANDLER\n\ --X frozen_modules=[on|off]: whether to use frozen modules; the default is \"on\"\n\ - for installed Python and \"off\" for a local build;\n\ - also PYTHON_FROZEN_MODULES\n\ -" +static const char usage_help[] = +"#h{Options (and corresponding environment variables):}\n" +"#s{-b} : issue warnings about converting bytes/bytearray to str and comparing\n" +" bytes/bytearray with str or bytes with int. (#S{-bb}: issue errors)\n" +" deprecated since 3.15 and will become no-op in 3.17.\n" +"#s{-B} : don't write .pyc files on import; also #e{PYTHONDONTWRITEBYTECODE}#B{=x}\n" +"#s{-c} #b{cmd} : program passed in as string (terminates option list)\n" +"#s{-d} : turn on parser debugging output (for experts only, only works on\n" +" debug builds); also #e{PYTHONDEBUG}#B{=x}\n" +"#s{-E} : ignore #e{PYTHON*} environment variables (such as #e{PYTHONPATH})\n" +"#s{-h} : print this help message and exit (also #S{-?} or #e{--help})\n" +"#s{-i} : inspect interactively after running script; forces a prompt even\n" +" if stdin does not appear to be a terminal; also #e{PYTHONINSPECT}#B{=x}\n" +"#s{-I} : isolate Python from the user's environment (implies #S{-E}, #S{-P} and #S{-s})\n" +"#s{-m} #b{mod} : run library module as a script (terminates option list)\n" +"#s{-O} : remove assert and __debug__-dependent statements; add .opt-1 before\n" +" .pyc extension; also #e{PYTHONOPTIMIZE}#B{=x}\n" +"#s{-OO} : do #S{-O} changes and also discard docstrings; add .opt-2 before\n" +" .pyc extension\n" +"#s{-P} : don't prepend a potentially unsafe path to sys.path; also\n" +" #e{PYTHONSAFEPATH}\n" +"#s{-q} : don't print version and copyright messages on interactive startup\n" +"#s{-s} : don't add user site directory to sys.path; also #e{PYTHONNOUSERSITE}#B{=x}\n" +"#s{-S} : don't imply 'import site' on initialization\n" +"#s{-u} : force the stdout and stderr streams to be unbuffered;\n" +" this option has no effect on stdin; also #e{PYTHONUNBUFFERED}#B{=x}\n" +"#s{-v} : verbose (trace import statements); also #e{PYTHONVERBOSE}#B{=x}\n" +" can be supplied multiple times to increase verbosity\n" +"#s{-V} : print the Python version number and exit (also #e{--version})\n" +" when given twice, print more information about the build\n" +"#s{-W} #b{arg} : warning control; #B{arg} is action:message:category:module:lineno\n" +" also #e{PYTHONWARNINGS}#B{=arg}\n" +"#s{-x} : skip first line of source, allowing use of non-Unix forms of #!cmd\n" +"#s{-X} #b{opt} : set implementation-specific option\n" +"#L{--check-hash-based-pycs} #b{always|default|never}:\n" +" control how Python invalidates hash-based .pyc files\n" +"#L{--help-env}: print help about Python environment variables and exit\n" +"#L{--help-xoptions}: print help about implementation-specific #S{-X} options and exit\n" +"#L{--help-all}: print complete help information and exit\n" +"\n" +"#h{Arguments:}\n" +"#s{file} : program read from script file\n" +"#s{-} : program read from stdin (default; interactive mode if a tty)\n" +"#s{arg} #b{...}: arguments passed to program in sys.argv[1:]\n" +; + +static const char usage_xoptions[] = +"#h{The following implementation-specific options are available:}\n" +"#s{-X} #L{context_aware_warnings}#b{=[0|1]}: if true (#B{1}) then the warnings module will\n" +" use a context variables; if false (#B{0}) then the warnings module will\n" +" use module globals, which is not concurrent-safe; set to true for\n" +" free-threaded builds and false otherwise; also\n" +" #e{PYTHON_CONTEXT_AWARE_WARNINGS}\n" +"#s{-X} #L{cpu_count}#b{=N}: override the return value of os.cpu_count();\n" +" #S{-X} #e{cpu_count}#B{=default} cancels overriding; also #e{PYTHON_CPU_COUNT}\n" +"#s{-X} #L{dev} : enable Python Development Mode; also #e{PYTHONDEVMODE}\n" +"#s{-X} #L{disable-remote-debug}: disable remote debugging; also #e{PYTHON_DISABLE_REMOTE_DEBUG}\n" +"#s{-X} #L{faulthandler}: dump the Python traceback on fatal errors;\n" +" also #e{PYTHONFAULTHANDLER}\n" +"#s{-X} #L{frozen_modules}#b{=[on|off]}: whether to use frozen modules; the default is \"#B{on}\"\n" +" for installed Python and \"#B{off}\" for a local build;\n" +" also #e{PYTHON_FROZEN_MODULES}\n" #ifdef Py_GIL_DISABLED -"-X gil=[0|1]: enable (1) or disable (0) the GIL; also PYTHON_GIL\n" +"#s{-X} #L{gil}#b{=[0|1]}: enable (#B{1}) or disable (#B{0}) the GIL; also #e{PYTHON_GIL}\n" #endif -"\ --X importtime[=2]: show how long each import takes; use -X importtime=2 to\n\ - log imports of already-loaded modules; also PYTHONPROFILEIMPORTTIME\n\ --X int_max_str_digits=N: limit the size of int<->str conversions;\n\ - 0 disables the limit; also PYTHONINTMAXSTRDIGITS\n\ --X no_debug_ranges: don't include extra location information in code objects;\n\ - also PYTHONNODEBUGRANGES\n\ --X perf: support the Linux \"perf\" profiler; also PYTHONPERFSUPPORT=1\n\ --X perf_jit: support the Linux \"perf\" profiler with DWARF support;\n\ - also PYTHON_PERF_JIT_SUPPORT=1\n\ --X disable-remote-debug: disable remote debugging; also PYTHON_DISABLE_REMOTE_DEBUG\n\ -" +"#s{-X} #L{importtime}#b{[=2]}: show how long each import takes; use #S{-X} #e{importtime}#B{=2} to\n" +" log imports of already-loaded modules; also #e{PYTHONPROFILEIMPORTTIME}\n" +"#s{-X} #L{int_max_str_digits}#b{=N}: limit the size of int<->str conversions;\n" +" 0 disables the limit; also #e{PYTHONINTMAXSTRDIGITS}\n" +"#s{-X} #L{lazy_imports}#b{=[all|normal]}: control global lazy imports;\n" +" default is #B{normal}; also #e{PYTHON_LAZY_IMPORTS}\n" +"#s{-X} #L{no_debug_ranges}: don't include extra location information in code objects;\n" +" also #e{PYTHONNODEBUGRANGES}\n" +"#s{-X} #L{pathconfig_warnings}#b{=[0|1]}: if true (#B{1}) then path configuration is allowed\n" +" to log warnings into stderr; if false (#B{0}) suppress these warnings;\n" +" set to true by default; also #e{PYTHON_PATHCONFIG_WARNINGS}\n" +"#s{-X} #L{perf}: support the Linux \"perf\" profiler; also #e{PYTHONPERFSUPPORT}#B{=1}\n" +"#s{-X} #L{perf_jit}: support the Linux \"perf\" profiler with DWARF support;\n" +" also #e{PYTHON_PERF_JIT_SUPPORT}#B{=1}\n" #ifdef Py_DEBUG -"-X presite=MOD: import this module before site; also PYTHON_PRESITE\n" +"#s{-X} #L{presite}#b{=MOD}: import this module before site; also #e{PYTHON_PRESITE}\n" #endif -"\ --X pycache_prefix=PATH: write .pyc files to a parallel tree instead of to the\n\ - code tree; also PYTHONPYCACHEPREFIX\n\ -" +"#s{-X} #L{pycache_prefix}#b{=PATH}: write .pyc files to a parallel tree instead of to the\n" +" code tree; also #e{PYTHONPYCACHEPREFIX}\n" #ifdef Py_STATS -"-X pystats: enable pystats collection at startup; also PYTHONSTATS\n" +"#s{-X} #L{pystats}: enable pystats collection at startup; also #e{PYTHONSTATS}\n" #endif -"\ --X showrefcount: output the total reference count and number of used\n\ - memory blocks when the program finishes or after each statement in\n\ - the interactive interpreter; only works on debug builds\n" +"#s{-X} #L{showrefcount}: output the total reference count and number of used\n" +" memory blocks when the program finishes or after each statement in\n" +" the interactive interpreter; only works on debug builds\n" +"#s{-X} #L{thread_inherit_context}#b{=[0|1]}: enable (#B{1}) or disable (#B{0}) threads inheriting\n" +" context vars by default; enabled by default in the free-threaded\n" +" build and disabled otherwise; also #e{PYTHON_THREAD_INHERIT_CONTEXT}\n" #ifdef Py_GIL_DISABLED -"-X tlbc=[0|1]: enable (1) or disable (0) thread-local bytecode. Also\n\ - PYTHON_TLBC\n" +"#s{-X} #L{tlbc}#b{=[0|1]}: enable (#B{1}) or disable (#B{0}) thread-local bytecode. Also\n" +" #e{PYTHON_TLBC}\n" #endif -"\ --X thread_inherit_context=[0|1]: enable (1) or disable (0) threads inheriting\n\ - context vars by default; enabled by default in the free-threaded\n\ - build and disabled otherwise; also PYTHON_THREAD_INHERIT_CONTEXT\n\ --X context_aware_warnings=[0|1]: if true (1) then the warnings module will\n\ - use a context variables; if false (0) then the warnings module will\n\ - use module globals, which is not concurrent-safe; set to true for\n\ - free-threaded builds and false otherwise; also\n\ - PYTHON_CONTEXT_AWARE_WARNINGS\n\ --X tracemalloc[=N]: trace Python memory allocations; N sets a traceback limit\n \ - of N frames (default: 1); also PYTHONTRACEMALLOC=N\n\ --X utf8[=0|1]: enable (1) or disable (0) UTF-8 mode; also PYTHONUTF8\n\ --X warn_default_encoding: enable opt-in EncodingWarning for 'encoding=None';\n\ - also PYTHONWARNDEFAULTENCODING\ -"; +"#s{-X} #L{tracemalloc}#b{[=N]}: trace Python memory allocations; N sets a traceback limit\n" +" of #B{N} frames (default: #B{1}); also #e{PYTHONTRACEMALLOC}#B{=N}\n" +"#s{-X} #L{utf8}#b{[=0|1]}: enable (#B{1}) or disable (#B{0}) UTF-8 mode; also #e{PYTHONUTF8}\n" +"#s{-X} #L{warn_default_encoding}: enable opt-in EncodingWarning for 'encoding=None';\n" +" also #e{PYTHONWARNDEFAULTENCODING}\n" +; /* Envvars that don't have equivalent command-line options are listed first */ static const char usage_envvars[] = -"Environment variables that change behavior:\n" -"PYTHONSTARTUP : file executed on interactive startup (no default)\n" -"PYTHONPATH : '%lc'-separated list of directories prefixed to the\n" -" default module search path. The result is sys.path.\n" -"PYTHONHOME : alternate <prefix> directory (or <prefix>%lc<exec_prefix>).\n" -" The default module search path uses %s.\n" -"PYTHONPLATLIBDIR: override sys.platlibdir\n" -"PYTHONCASEOK : ignore case in 'import' statements (Windows)\n" -"PYTHONIOENCODING: encoding[:errors] used for stdin/stdout/stderr\n" -"PYTHONHASHSEED : if this variable is set to 'random', a random value is used\n" -" to seed the hashes of str and bytes objects. It can also be\n" -" set to an integer in the range [0,4294967295] to get hash\n" -" values with a predictable seed.\n" -"PYTHONMALLOC : set the Python memory allocators and/or install debug hooks\n" -" on Python memory allocators. Use PYTHONMALLOC=debug to\n" -" install debug hooks.\n" -"PYTHONMALLOCSTATS: print memory allocator statistics\n" -"PYTHONCOERCECLOCALE: if this variable is set to 0, it disables the locale\n" -" coercion behavior. Use PYTHONCOERCECLOCALE=warn to request\n" -" display of locale coercion and locale compatibility warnings\n" -" on stderr.\n" -"PYTHONBREAKPOINT: if this variable is set to 0, it disables the default\n" +"#h{Environment variables that change behavior:}\n" +"#E{PYTHONASYNCIODEBUG}: enable asyncio debug mode\n" +"#E{PYTHON_BASIC_REPL}: use the traditional parser-based REPL\n" +"#E{PYTHONBREAKPOINT}: if this variable is set to #B{0}, it disables the default\n" " debugger. It can be set to the callable of your debugger of\n" " choice.\n" -"PYTHON_COLORS : if this variable is set to 1, the interpreter will colorize\n" -" various kinds of output. Setting it to 0 deactivates\n" +"#E{PYTHONCASEOK} : ignore case in 'import' statements (Windows)\n" +"#E{PYTHONCOERCECLOCALE}: if this variable is set to #B{0}, it disables the locale\n" +" coercion behavior. Use #e{PYTHONCOERCECLOCALE}#B{=warn} to request\n" +" display of locale coercion and locale compatibility warnings\n" +" on stderr.\n" +"#E{PYTHON_COLORS} : if this variable is set to #B{1}, the interpreter will colorize\n" +" various kinds of output. Setting it to #B{0} deactivates\n" " this behavior.\n" -"PYTHON_HISTORY : the location of a .python_history file.\n" -"PYTHONASYNCIODEBUG: enable asyncio debug mode\n" #ifdef Py_TRACE_REFS -"PYTHONDUMPREFS : dump objects and reference counts still alive after shutdown\n" -"PYTHONDUMPREFSFILE: dump objects and reference counts to the specified file\n" +"#E{PYTHONDUMPREFS} : dump objects and reference counts still alive after shutdown\n" +"#E{PYTHONDUMPREFSFILE}: dump objects and reference counts to the specified file\n" #endif #ifdef __APPLE__ -"PYTHONEXECUTABLE: set sys.argv[0] to this value (macOS only)\n" +"#E{PYTHONEXECUTABLE}: set sys.argv[0] to this value (macOS only)\n" #endif +"#E{PYTHONHASHSEED} : if this variable is set to 'random', a random value is used\n" +" to seed the hashes of str and bytes objects. It can also be\n" +" set to an integer in the range [0,4294967295] to get hash\n" +" values with a predictable seed.\n" +"#E{PYTHON_HISTORY} : the location of a .python_history file.\n" +"#E{PYTHONHOME} : alternate <prefix> directory (or <prefix>#D<exec_prefix>).\n" +" The default module search path uses #H.\n" +"#E{PYTHONIOENCODING}: encoding[:errors] used for stdin/stdout/stderr\n" #ifdef MS_WINDOWS -"PYTHONLEGACYWINDOWSFSENCODING: use legacy \"mbcs\" encoding for file system\n" -"PYTHONLEGACYWINDOWSSTDIO: use legacy Windows stdio\n" +"#E{PYTHONLEGACYWINDOWSFSENCODING}: use legacy \"mbcs\" encoding for file system\n" +"#E{PYTHONLEGACYWINDOWSSTDIO}: use legacy Windows stdio\n" #endif -"PYTHONUSERBASE : defines the user base directory (site.USER_BASE)\n" -"PYTHON_BASIC_REPL: use the traditional parser-based REPL\n" +"#E{PYTHONMALLOC} : set the Python memory allocators and/or install debug hooks\n" +" on Python memory allocators. Use #e{PYTHONMALLOC}#B{=debug} to\n" +" install debug hooks.\n" +"#E{PYTHONMALLOCSTATS}: print memory allocator statistics\n" +"#E{PYTHONPATH} : '#D'-separated list of directories prefixed to the\n" +" default module search path. The result is sys.path.\n" +"#E{PYTHONPLATLIBDIR}: override sys.platlibdir\n" +"#E{PYTHONSTARTUP} : file executed on interactive startup (no default)\n" +"#E{PYTHONUSERBASE} : defines the user base directory (site.USER_BASE)\n" "\n" -"These variables have equivalent command-line options (see --help for details):\n" -"PYTHON_CPU_COUNT: override the return value of os.cpu_count() (-X cpu_count)\n" -"PYTHONDEBUG : enable parser debug mode (-d)\n" -"PYTHONDEVMODE : enable Python Development Mode (-X dev)\n" -"PYTHONDONTWRITEBYTECODE: don't write .pyc files (-B)\n" -"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors (-X faulthandler)\n" -"PYTHON_FROZEN_MODULES: whether to use frozen modules; the default is \"on\"\n" -" for installed Python and \"off\" for a local build\n" -" (-X frozen_modules)\n" +"#h{These variables have equivalent command-line options (see }#e{--help} for details):\n" +"#E{PYTHON_CONTEXT_AWARE_WARNINGS}: if true (#B{1}), enable thread-safe warnings\n" +" module behaviour (#S{-X} #e{context_aware_warnings})\n" +"#E{PYTHON_CPU_COUNT}: override the return value of os.cpu_count() (#S{-X} #e{cpu_count})\n" +"#E{PYTHONDEBUG} : enable parser debug mode (#S{-d})\n" +"#E{PYTHONDEVMODE} : enable Python Development Mode (#S{-X} #e{dev})\n" +"#E{PYTHONDONTWRITEBYTECODE}: don't write .pyc files (#S{-B})\n" +"#E{PYTHONFAULTHANDLER}: dump the Python traceback on fatal errors (#S{-X} #e{faulthandler})\n" +"#E{PYTHON_FROZEN_MODULES}: whether to use frozen modules; the default is \"#B{on}\"\n" +" for installed Python and \"#B{off}\" for a local build\n" +" (#S{-X} #e{frozen_modules})\n" #ifdef Py_GIL_DISABLED -"PYTHON_GIL : when set to 0, disables the GIL (-X gil)\n" +"#E{PYTHON_GIL} : when set to #B{0}, disables the GIL (#S{-X} #e{gil})\n" #endif -"PYTHONINSPECT : inspect interactively after running script (-i)\n" -"PYTHONINTMAXSTRDIGITS: limit the size of int<->str conversions;\n" -" 0 disables the limit (-X int_max_str_digits=N)\n" -"PYTHONNODEBUGRANGES: don't include extra location information in code objects\n" -" (-X no_debug_ranges)\n" -"PYTHONNOUSERSITE: disable user site directory (-s)\n" -"PYTHONOPTIMIZE : enable level 1 optimizations (-O)\n" -"PYTHONPERFSUPPORT: support the Linux \"perf\" profiler (-X perf)\n" -"PYTHON_PERF_JIT_SUPPORT: enable Linux \"perf\" profiler support with JIT\n" -" (-X perf_jit)\n" +"#E{PYTHONINSPECT} : inspect interactively after running script (#S{-i})\n" +"#E{PYTHONINTMAXSTRDIGITS}: limit the size of int<->str conversions;\n" +" 0 disables the limit (#S{-X} #e{int_max_str_digits}#B{=N})\n" +"#E{PYTHON_LAZY_IMPORTS}: control global lazy imports (#S{-X} #e{lazy_imports})\n" +"#E{PYTHONNODEBUGRANGES}: don't include extra location information in code objects\n" +" (#S{-X} #e{no_debug_ranges})\n" +"#E{PYTHONNOUSERSITE}: disable user site directory (#S{-s})\n" +"#E{PYTHONOPTIMIZE} : enable level 1 optimizations (#S{-O})\n" +"#E{PYTHON_PERF_JIT_SUPPORT}: enable Linux \"perf\" profiler support with JIT\n" +" (#S{-X} #e{perf_jit})\n" +"#E{PYTHONPERFSUPPORT}: support the Linux \"perf\" profiler (#S{-X} #e{perf})\n" #ifdef Py_DEBUG -"PYTHON_PRESITE: import this module before site (-X presite)\n" +"#E{PYTHON_PRESITE}: import this module before site (#S{-X} #e{presite})\n" #endif -"PYTHONPROFILEIMPORTTIME: show how long each import takes (-X importtime)\n" -"PYTHONPYCACHEPREFIX: root directory for bytecode cache (pyc) files\n" -" (-X pycache_prefix)\n" -"PYTHONSAFEPATH : don't prepend a potentially unsafe path to sys.path.\n" +"#E{PYTHONPROFILEIMPORTTIME}: show how long each import takes (#S{-X} #e{importtime})\n" +"#E{PYTHONPYCACHEPREFIX}: root directory for bytecode cache (pyc) files\n" +" (#S{-X} #e{pycache_prefix})\n" +"#E{PYTHONSAFEPATH} : don't prepend a potentially unsafe path to sys.path.\n" #ifdef Py_STATS -"PYTHONSTATS : turns on statistics gathering (-X pystats)\n" +"#E{PYTHONSTATS} : turns on statistics gathering (#S{-X} #e{pystats})\n" #endif +"#E{PYTHON_THREAD_INHERIT_CONTEXT}: if true (#B{1}), threads inherit context vars\n" +" (#S{-X} #e{thread_inherit_context})\n" #ifdef Py_GIL_DISABLED -"PYTHON_TLBC : when set to 0, disables thread-local bytecode (-X tlbc)\n" +"#E{PYTHON_TLBC} : when set to #B{0}, disables thread-local bytecode (#S{-X} #e{tlbc})\n" #endif -"PYTHON_THREAD_INHERIT_CONTEXT: if true (1), threads inherit context vars\n" -" (-X thread_inherit_context)\n" -"PYTHON_CONTEXT_AWARE_WARNINGS: if true (1), enable thread-safe warnings module\n" -" behaviour (-X context_aware_warnings)\n" -"PYTHONTRACEMALLOC: trace Python memory allocations (-X tracemalloc)\n" -"PYTHONUNBUFFERED: disable stdout/stderr buffering (-u)\n" -"PYTHONUTF8 : control the UTF-8 mode (-X utf8)\n" -"PYTHONVERBOSE : trace import statements (-v)\n" -"PYTHONWARNDEFAULTENCODING: enable opt-in EncodingWarning for 'encoding=None'\n" -" (-X warn_default_encoding)\n" -"PYTHONWARNINGS : warning control (-W)\n" +"#E{PYTHONTRACEMALLOC}: trace Python memory allocations (#S{-X} #e{tracemalloc})\n" +"#E{PYTHONUNBUFFERED}: disable stdout/stderr buffering (#S{-u})\n" +"#E{PYTHONUTF8} : control the UTF-8 mode (#S{-X} #e{utf8})\n" +"#E{PYTHONVERBOSE} : trace import statements (#S{-v})\n" +"#E{PYTHONWARNDEFAULTENCODING}: enable opt-in EncodingWarning for 'encoding=None'\n" +" (#S{-X} #e{warn_default_encoding})\n" +"#E{PYTHONWARNINGS} : warning control (#S{-W})\n" ; -#if defined(MS_WINDOWS) -# define PYTHONHOMEHELP "<prefix>\\python{major}{minor}" -#else -# define PYTHONHOMEHELP "<prefix>/lib/pythonX.X" -#endif - /* --- Global configuration variables ----------------------------- */ @@ -499,7 +627,7 @@ _Py_COMP_DIAG_IGNORE_DEPR_DECLS do { \ obj = (EXPR); \ if (obj == NULL) { \ - return NULL; \ + goto fail; \ } \ int res = PyDict_SetItemString(dict, (KEY), obj); \ Py_DECREF(obj); \ @@ -897,6 +1025,7 @@ config_check_consistency(const PyConfig *config) assert(config->show_ref_count >= 0); assert(config->dump_refs >= 0); assert(config->malloc_stats >= 0); + assert(config->pymalloc_hugepages >= 0); assert(config->site_import >= 0); assert(config->bytes_warning >= 0); assert(config->warn_default_encoding >= 0); @@ -936,6 +1065,9 @@ config_check_consistency(const PyConfig *config) assert(config->int_max_str_digits >= 0); // cpu_count can be -1 if the user doesn't override it. assert(config->cpu_count != 0); + // lazy_imports can be -1 (default) or 1 (on). 0 is rejected later + // for embedders with an error message. + assert(config->lazy_imports >= -1 && config->lazy_imports <= 1); // config->use_frozen_modules is initialized later // by _PyConfig_InitImportConfig(). assert(config->thread_inherit_context >= 0); @@ -1047,6 +1179,7 @@ _PyConfig_InitCompatConfig(PyConfig *config) config->_is_python_build = 0; config->code_debug_ranges = 1; config->cpu_count = -1; + config->lazy_imports = -1; #ifdef Py_GIL_DISABLED config->thread_inherit_context = 1; config->context_aware_warnings = 1; @@ -1843,7 +1976,9 @@ config_read_env_vars(PyConfig *config) _Py_get_env_flag(use_env, &config->parser_debug, "PYTHONDEBUG"); _Py_get_env_flag(use_env, &config->verbose, "PYTHONVERBOSE"); _Py_get_env_flag(use_env, &config->optimization_level, "PYTHONOPTIMIZE"); - _Py_get_env_flag(use_env, &config->inspect, "PYTHONINSPECT"); + if (!config->inspect && _Py_GetEnv(use_env, "PYTHONINSPECT")) { + config->inspect = 1; + } int dont_write_bytecode = 0; _Py_get_env_flag(use_env, &dont_write_bytecode, "PYTHONDONTWRITEBYTECODE"); @@ -1874,6 +2009,18 @@ config_read_env_vars(PyConfig *config) if (config_get_env(config, "PYTHONMALLOCSTATS")) { config->malloc_stats = 1; } + { + const char *env = _Py_GetEnv(use_env, "PYTHON_PYMALLOC_HUGEPAGES"); + if (env) { + int value; + if (_Py_str_to_int(env, &value) < 0 || value < 0) { + /* PYTHON_PYMALLOC_HUGEPAGES=text or negative + behaves as PYTHON_PYMALLOC_HUGEPAGES=1 */ + value = 1; + } + config->pymalloc_hugepages = (value > 0); + } + } if (config->dump_refs_file == NULL) { status = CONFIG_GET_ENV_DUP(config, &config->dump_refs_file, @@ -2281,6 +2428,69 @@ config_init_import_time(PyConfig *config) return _PyStatus_OK(); } +static PyStatus +config_init_lazy_imports(PyConfig *config) +{ + int lazy_imports = -1; + + const char *env = config_get_env(config, "PYTHON_LAZY_IMPORTS"); + if (env) { + if (strcmp(env, "all") == 0) { + lazy_imports = 1; + } + else if (strcmp(env, "normal") == 0) { + lazy_imports = -1; + } + else { + return _PyStatus_ERR("PYTHON_LAZY_IMPORTS: invalid value; " + "expected 'all' or 'normal'"); + } + config->lazy_imports = lazy_imports; + } + + const wchar_t *x_value = config_get_xoption_value(config, L"lazy_imports"); + if (x_value) { + if (wcscmp(x_value, L"all") == 0) { + lazy_imports = 1; + } + else if (wcscmp(x_value, L"normal") == 0) { + lazy_imports = -1; + } + else { + return _PyStatus_ERR("-X lazy_imports: invalid value; " + "expected 'all' or 'normal'"); + } + config->lazy_imports = lazy_imports; + } + return _PyStatus_OK(); +} + +static PyStatus +config_init_pathconfig_warnings(PyConfig *config) +{ + const char *env = config_get_env(config, "PYTHON_PATHCONFIG_WARNINGS"); + if (env) { + int enabled; + if (_Py_str_to_int(env, &enabled) < 0 || (enabled < 0) || (enabled > 1)) { + return _PyStatus_ERR( + "PYTHON_PATHCONFIG_WARNINGS=N: N is missing or invalid"); + } + config->pathconfig_warnings = enabled; + } + + const wchar_t *xoption = config_get_xoption(config, L"pathconfig_warnings"); + if (xoption) { + int enabled; + const wchar_t *sep = wcschr(xoption, L'='); + if (!sep || (config_wstr_to_int(sep + 1, &enabled) < 0) || (enabled < 0) || (enabled > 1)) { + return _PyStatus_ERR( + "-X pathconfig_warnings=n: n is missing or invalid"); + } + config->pathconfig_warnings = enabled; + } + return _PyStatus_OK(); +} + static PyStatus config_read_complex_options(PyConfig *config) { @@ -2304,6 +2514,13 @@ config_read_complex_options(PyConfig *config) } } + if (config->lazy_imports < 0) { + status = config_init_lazy_imports(config); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + } + if (config->tracemalloc < 0) { status = config_init_tracemalloc(config); if (_PyStatus_EXCEPTION(status)) { @@ -2370,6 +2587,11 @@ config_read_complex_options(PyConfig *config) return status; } + status = config_init_pathconfig_warnings(config); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + return _PyStatus_OK(); } @@ -2693,6 +2915,9 @@ config_read(PyConfig *config, int compute_path_config) if (config->tracemalloc < 0) { config->tracemalloc = 0; } + if (config->lazy_imports < 0) { + config->lazy_imports = -1; // Default is auto/unset + } if (config->perf_profiling < 0) { config->perf_profiling = 0; } @@ -2807,10 +3032,8 @@ _PyConfig_Write(const PyConfig *config, _PyRuntimeState *runtime) return _PyStatus_NO_MEMORY(); } -#ifdef Py_STATS - if (config->_pystats) { - _Py_StatsOn(); - } +#ifdef PYMALLOC_USE_HUGEPAGES + runtime->allocators.use_hugepages = config->pymalloc_hugepages; #endif return _PyStatus_OK(); @@ -2823,25 +3046,29 @@ static void config_usage(int error, const wchar_t* program) { FILE *f = error ? stderr : stdout; + int colorize = _Py_can_colorize(f); - fprintf(f, usage_line, program); - if (error) + fprint_help(f, usage_line, colorize, program); + if (error) { fprintf(f, "Try `python -h' for more information.\n"); + } else { - fputs(usage_help, f); + fprint_help(f, usage_help, colorize, NULL); } } static void config_envvars_usage(void) { - printf(usage_envvars, (wint_t)DELIM, (wint_t)DELIM, PYTHONHOMEHELP); + int colorize = _Py_can_colorize(stdout); + fprint_help(stdout, usage_envvars, colorize, NULL); } static void config_xoptions_usage(void) { - puts(usage_xoptions); + int colorize = _Py_can_colorize(stdout); + fprint_help(stdout, usage_xoptions, colorize, NULL); } static void @@ -3456,6 +3683,44 @@ PyConfig_SetWideStringList(PyConfig *config, PyWideStringList *list, } +#ifdef __CYGWIN__ +// Cygwin strips ".exe" suffix from argv[0]. +// Add again the ".exe" suffix. +static PyStatus +config_argv0_add_exe(PyConfig *config) +{ + if (config->argv.length < 1) { + return _PyStatus_OK(); + } + const wchar_t *argv0 = config->argv.items[0]; + size_t len = wcslen(argv0); + if (len >= 5 && wcscmp(argv0 + len - 4, L".exe") == 0) { + return _PyStatus_OK(); + } + + wchar_t *exe = PyMem_RawMalloc((len + 4 + 1) * sizeof(wchar_t)); + if (exe == NULL) { + return _PyStatus_NO_MEMORY(); + } + wcscpy(exe, argv0); + wcscat(exe, L".exe"); + + FILE *fp = _Py_wfopen(exe, L"rb"); + if (fp != NULL) { + fclose(fp); + + PyMem_RawFree(config->argv.items[0]); + config->argv.items[0] = exe; + } + else { + PyMem_RawFree(exe); + } + + return _PyStatus_OK(); +} +#endif + + /* Read the configuration into PyConfig from: * Command line arguments @@ -3475,6 +3740,13 @@ _PyConfig_Read(PyConfig *config, int compute_path_config) config_get_global_vars(config); +#ifdef __CYGWIN__ + status = config_argv0_add_exe(config); + if (_PyStatus_EXCEPTION(status)) { + return status; + } +#endif + if (config->orig_argv.length == 0 && !(config->argv.length == 1 && wcscmp(config->argv.items[0], L"") == 0)) @@ -3725,6 +3997,9 @@ PyInitConfig_Free(PyInitConfig *config) if (config == NULL) { return; } + + initconfig_free_config(&config->config); + PyMem_RawFree(config->inittab); free(config->err_msg); free(config); } @@ -4093,13 +4368,51 @@ PyInitConfig_SetStr(PyInitConfig *config, const char *name, const char* value) } +static void +initconfig_free_wstr(wchar_t *member) +{ + if (member) { + free(member); + } +} + + +static void +initconfig_free_wstr_list(PyWideStringList *list) +{ + for (Py_ssize_t i = 0; i < list->length; i++) { + free(list->items[i]); + } + free(list->items); +} + + +static void +initconfig_free_config(const PyConfig *config) +{ + const PyConfigSpec *spec = PYCONFIG_SPEC; + for (; spec->name != NULL; spec++) { + void *member = config_get_spec_member(config, spec); + if (spec->type == PyConfig_MEMBER_WSTR + || spec->type == PyConfig_MEMBER_WSTR_OPT) + { + wchar_t *wstr = *(wchar_t **)member; + initconfig_free_wstr(wstr); + } + else if (spec->type == PyConfig_MEMBER_WSTR_LIST) { + initconfig_free_wstr_list(member); + } + } +} + + static int -_PyWideStringList_FromUTF8(PyInitConfig *config, PyWideStringList *list, - Py_ssize_t length, char * const *items) +initconfig_set_str_list(PyInitConfig *config, PyWideStringList *list, + Py_ssize_t length, char * const *items) { PyWideStringList wlist = _PyWideStringList_INIT; size_t size = sizeof(wchar_t*) * length; - wlist.items = (wchar_t **)PyMem_RawMalloc(size); + wlist.items = (wchar_t **)malloc(size); if (wlist.items == NULL) { config->status = _PyStatus_NO_MEMORY(); return -1; @@ -4108,14 +4421,14 @@ _PyWideStringList_FromUTF8(PyInitConfig *config, PyWideStringList *list, for (Py_ssize_t i = 0; i < length; i++) { wchar_t *arg = utf8_to_wstr(config, items[i]); if (arg == NULL) { - _PyWideStringList_Clear(&wlist); + initconfig_free_wstr_list(&wlist); return -1; } wlist.items[i] = arg; wlist.length++; } - _PyWideStringList_Clear(list); + initconfig_free_wstr_list(list); *list = wlist; return 0; } @@ -4136,7 +4449,7 @@ PyInitConfig_SetStrList(PyInitConfig *config, const char *name, return -1; } PyWideStringList *list = raw_member; - if (_PyWideStringList_FromUTF8(config, list, length, items) < 0) { + if (initconfig_set_str_list(config, list, length, items) < 0) { return -1; } diff --git a/Python/instruction_sequence.c b/Python/instruction_sequence.c index e2db46b48930d6..42cb148ac97c01 100644 --- a/Python/instruction_sequence.c +++ b/Python/instruction_sequence.c @@ -154,6 +154,13 @@ _PyInstructionSequence_InsertInstruction(instr_sequence *seq, int pos, return SUCCESS; } +_PyInstruction +_PyInstructionSequence_GetInstruction(instr_sequence *seq, int pos) +{ + assert(pos >= 0 && pos < seq->s_used); + return seq->s_instrs[pos]; +} + int _PyInstructionSequence_SetAnnotationsCode(instr_sequence *seq, instr_sequence *annotations) diff --git a/Python/instrumentation.c b/Python/instrumentation.c index f9913cff402ed1..0af2070b5cd983 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -185,19 +185,25 @@ opcode_has_event(int opcode) ); } +uint8_t +_PyCode_Deinstrument(uint8_t opcode) +{ + return DE_INSTRUMENT[opcode]; +} + static inline bool is_instrumented(int opcode) { assert(opcode != 0); assert(opcode != RESERVED); - return opcode != ENTER_EXECUTOR && opcode >= MIN_INSTRUMENTED_OPCODE; + return opcode < ENTER_EXECUTOR && opcode >= MIN_INSTRUMENTED_OPCODE; } #ifndef NDEBUG static inline bool monitors_equals(_Py_LocalMonitors a, _Py_LocalMonitors b) { - for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) { + for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) { if (a.tools[i] != b.tools[i]) { return false; } @@ -210,7 +216,7 @@ static inline _Py_LocalMonitors monitors_sub(_Py_LocalMonitors a, _Py_LocalMonitors b) { _Py_LocalMonitors res; - for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) { + for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) { res.tools[i] = a.tools[i] & ~b.tools[i]; } return res; @@ -221,7 +227,7 @@ static inline _Py_LocalMonitors monitors_and(_Py_LocalMonitors a, _Py_LocalMonitors b) { _Py_LocalMonitors res; - for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) { + for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) { res.tools[i] = a.tools[i] & b.tools[i]; } return res; @@ -237,7 +243,7 @@ static inline _Py_LocalMonitors local_union(_Py_GlobalMonitors a, _Py_LocalMonitors b) { _Py_LocalMonitors res; - for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) { + for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) { res.tools[i] = a.tools[i] | b.tools[i]; } return res; @@ -246,7 +252,7 @@ local_union(_Py_GlobalMonitors a, _Py_LocalMonitors b) static inline bool monitors_are_empty(_Py_LocalMonitors m) { - for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) { + for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) { if (m.tools[i]) { return false; } @@ -257,7 +263,7 @@ monitors_are_empty(_Py_LocalMonitors m) static inline bool multiple_tools(_Py_LocalMonitors *m) { - for (int i = 0; i < _PY_MONITORING_LOCAL_EVENTS; i++) { + for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) { if (_Py_popcount32(m->tools[i]) > 1) { return true; } @@ -269,7 +275,7 @@ static inline _PyMonitoringEventSet get_local_events(_Py_LocalMonitors *m, int tool_id) { _PyMonitoringEventSet result = 0; - for (int e = 0; e < _PY_MONITORING_LOCAL_EVENTS; e++) { + for (int e = 0; e < _PY_MONITORING_UNGROUPED_EVENTS; e++) { if ((m->tools[e] >> tool_id) & 1) { result |= (1 << e); } @@ -330,12 +336,6 @@ _PyInstruction_GetLength(PyCodeObject *code, int offset) return 1 + _PyOpcode_Caches[inst.op.code]; } -static inline uint8_t -get_original_opcode(_PyCoLineInstrumentationData *line_data, int index) -{ - return line_data->data[index*line_data->bytes_per_entry]; -} - static inline uint8_t * get_original_opcode_ptr(_PyCoLineInstrumentationData *line_data, int index) { @@ -401,7 +401,7 @@ dump_instrumentation_data_lines(PyCodeObject *code, _PyCoLineInstrumentationData fprintf(out, ", lines = NULL"); } else { - int opcode = get_original_opcode(lines, i); + int opcode = _PyCode_GetOriginalOpcode(lines, i); int line_delta = get_line_delta(lines, i); if (opcode == 0) { fprintf(out, ", lines = {original_opcode = No LINE (0), line_delta = %d)", line_delta); @@ -453,7 +453,7 @@ static void dump_local_monitors(const char *prefix, _Py_LocalMonitors monitors, FILE*out) { fprintf(out, "%s monitors:\n", prefix); - for (int event = 0; event < _PY_MONITORING_LOCAL_EVENTS; event++) { + for (int event = 0; event < _PY_MONITORING_UNGROUPED_EVENTS; event++) { fprintf(out, " Event %d: Tools %x\n", event, monitors.tools[event]); } } @@ -525,7 +525,7 @@ valid_opcode(int opcode) if (IS_VALID_OPCODE(opcode) && opcode != CACHE && opcode != RESERVED && - opcode < 255) + opcode < 254) { return true; } @@ -571,11 +571,12 @@ sanity_check_instrumentation(PyCodeObject *code) } if (opcode == INSTRUMENTED_LINE) { CHECK(data->lines); - opcode = get_original_opcode(data->lines, i); + opcode = _PyCode_GetOriginalOpcode(data->lines, i); CHECK(valid_opcode(opcode)); CHECK(opcode != END_FOR); CHECK(opcode != RESUME); CHECK(opcode != RESUME_CHECK); + CHECK(opcode != RESUME_CHECK_JIT); CHECK(opcode != INSTRUMENTED_RESUME); if (!is_instrumented(opcode)) { CHECK(_PyOpcode_Deopt[opcode] == opcode); @@ -587,7 +588,7 @@ sanity_check_instrumentation(PyCodeObject *code) * *and* we are executing a INSTRUMENTED_LINE instruction * that has de-instrumented itself, then we will execute * an invalid INSTRUMENTED_INSTRUCTION */ - CHECK(get_original_opcode(data->lines, i) != INSTRUMENTED_INSTRUCTION); + CHECK(_PyCode_GetOriginalOpcode(data->lines, i) != INSTRUMENTED_INSTRUCTION); } if (opcode == INSTRUMENTED_INSTRUCTION) { CHECK(data->per_instruction_opcodes[i] != 0); @@ -602,7 +603,7 @@ sanity_check_instrumentation(PyCodeObject *code) } CHECK(active_monitors.tools[event] != 0); } - if (data->lines && get_original_opcode(data->lines, i)) { + if (data->lines && _PyCode_GetOriginalOpcode(data->lines, i)) { int line1 = compute_line(code, get_line_delta(data->lines, i)); int line2 = _PyCode_CheckLineNumber(i*sizeof(_Py_CODEUNIT), &range); CHECK(line1 == line2); @@ -654,7 +655,7 @@ _Py_GetBaseCodeUnit(PyCodeObject *code, int i) return inst; } if (opcode == INSTRUMENTED_LINE) { - opcode = get_original_opcode(code->_co_monitoring->lines, i); + opcode = _PyCode_GetOriginalOpcode(code->_co_monitoring->lines, i); } if (opcode == INSTRUMENTED_INSTRUCTION) { opcode = code->_co_monitoring->per_instruction_opcodes[i]; @@ -713,7 +714,7 @@ de_instrument_line(PyCodeObject *code, _Py_CODEUNIT *bytecode, _PyCoMonitoringDa return; } _PyCoLineInstrumentationData *lines = monitoring->lines; - int original_opcode = get_original_opcode(lines, i); + int original_opcode = _PyCode_GetOriginalOpcode(lines, i); if (original_opcode == INSTRUMENTED_INSTRUCTION) { set_original_opcode(lines, i, monitoring->per_instruction_opcodes[i]); } @@ -1040,6 +1041,8 @@ set_version_raw(uintptr_t *ptr, uint32_t version) static void set_global_version(PyThreadState *tstate, uint32_t version) { + ASSERT_WORLD_STOPPED(); + assert((version & _PY_EVAL_EVENTS_MASK) == 0); PyInterpreterState *interp = tstate->interp; set_version_raw(&interp->ceval.instrumentation_version, version); @@ -1099,8 +1102,10 @@ get_tools_for_instruction(PyCodeObject *code, PyInterpreterState *interp, int i, event == PY_MONITORING_EVENT_C_RETURN); event = PY_MONITORING_EVENT_CALL; } + assert(_PY_MONITORING_IS_UNGROUPED_EVENT(event)); + CHECK(debug_check_sanity(interp, code)); if (PY_MONITORING_IS_INSTRUMENTED_EVENT(event)) { - CHECK(debug_check_sanity(interp, code)); + /* Instrumented events use per-instruction tool bitmaps. */ if (code->_co_monitoring->tools) { tools = code->_co_monitoring->tools[i]; } @@ -1109,7 +1114,9 @@ get_tools_for_instruction(PyCodeObject *code, PyInterpreterState *interp, int i, } } else { - tools = interp->monitors.tools[event]; + /* Other (non-instrumented) events are not tied to specific instructions; + * use the code-object-level active_monitors bitmap instead. */ + tools = code->_co_monitoring->active_monitors.tools[event]; } return tools; } @@ -1136,6 +1143,25 @@ static const char *const event_names [] = { [PY_MONITORING_EVENT_STOP_ITERATION] = "STOP_ITERATION", }; +/* Disable an "other" (non-instrumented) event (e.g. PY_UNWIND) for a single + * tool on this code object. Must be called with the world stopped or the + * code lock held. */ +static void +remove_local_tool(PyCodeObject *code, PyInterpreterState *interp, + int event, int tool) +{ + ASSERT_WORLD_STOPPED_OR_LOCKED(code); + assert(_PY_MONITORING_IS_UNGROUPED_EVENT(event)); + assert(!PY_MONITORING_IS_INSTRUMENTED_EVENT(event)); + assert(code->_co_monitoring); + code->_co_monitoring->local_monitors.tools[event] &= ~(1 << tool); + /* Recompute active_monitors for this event as the union of global and + * (now updated) local monitors. */ + code->_co_monitoring->active_monitors.tools[event] = + interp->monitors.tools[event] | + code->_co_monitoring->local_monitors.tools[event]; +} + static int call_instrumentation_vector( _Py_CODEUNIT *instr, PyThreadState *tstate, int event, @@ -1180,7 +1206,18 @@ call_instrumentation_vector( } else { /* DISABLE */ - if (!PY_MONITORING_IS_INSTRUMENTED_EVENT(event)) { + if (PY_MONITORING_IS_INSTRUMENTED_EVENT(event)) { + _PyEval_StopTheWorld(interp); + remove_tools(code, offset, event, 1 << tool); + _PyEval_StartTheWorld(interp); + } + else if (_PY_MONITORING_IS_UNGROUPED_EVENT(event)) { + /* Other (non-instrumented) event: disable for this code object. */ + _PyEval_StopTheWorld(interp); + remove_local_tool(code, interp, event, tool); + _PyEval_StartTheWorld(interp); + } + else { PyErr_Format(PyExc_ValueError, "Cannot disable %s events. Callback removed.", event_names[event]); @@ -1189,12 +1226,6 @@ call_instrumentation_vector( err = -1; break; } - else { - PyInterpreterState *interp = tstate->interp; - _PyEval_StopTheWorld(interp); - remove_tools(code, offset, event, 1 << tool); - _PyEval_StartTheWorld(interp); - } } } Py_DECREF(arg2_obj); @@ -1283,13 +1314,10 @@ _Py_call_instrumentation_exc2( } int -_Py_Instrumentation_GetLine(PyCodeObject *code, int index) +_Py_Instrumentation_GetLine(PyCodeObject *code, _PyCoLineInstrumentationData *line_data, int index) { - _PyCoMonitoringData *monitoring = code->_co_monitoring; - assert(monitoring != NULL); - assert(monitoring->lines != NULL); + assert(line_data != NULL); assert(index < Py_SIZE(code)); - _PyCoLineInstrumentationData *line_data = monitoring->lines; int line_delta = get_line_delta(line_data, index); int line = compute_line(code, line_delta); return line; @@ -1307,11 +1335,11 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, _PyCoMonitoringData *monitoring = code->_co_monitoring; _PyCoLineInstrumentationData *line_data = monitoring->lines; PyInterpreterState *interp = tstate->interp; - int line = _Py_Instrumentation_GetLine(code, i); + int line = _Py_Instrumentation_GetLine(code, line_data, i); assert(line >= 0); assert(prev != NULL); int prev_index = (int)(prev - bytecode); - int prev_line = _Py_Instrumentation_GetLine(code, prev_index); + int prev_line = _Py_Instrumentation_GetLine(code, line_data, prev_index); if (prev_line == line) { int prev_opcode = bytecode[prev_index].op.code; /* RESUME and INSTRUMENTED_RESUME are needed for the operation of @@ -1391,7 +1419,7 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, Py_DECREF(line_obj); uint8_t original_opcode; done: - original_opcode = get_original_opcode(line_data, i); + original_opcode = _PyCode_GetOriginalOpcode(line_data, i); assert(original_opcode != 0); assert(original_opcode != INSTRUMENTED_LINE); assert(_PyOpcode_Deopt[original_opcode] == original_opcode); @@ -1464,7 +1492,7 @@ initialize_tools(PyCodeObject *code) int opcode = instr->op.code; assert(opcode != ENTER_EXECUTOR); if (opcode == INSTRUMENTED_LINE) { - opcode = get_original_opcode(code->_co_monitoring->lines, i); + opcode = _PyCode_GetOriginalOpcode(code->_co_monitoring->lines, i); } if (opcode == INSTRUMENTED_INSTRUCTION) { opcode = code->_co_monitoring->per_instruction_opcodes[i]; @@ -1508,11 +1536,9 @@ initialize_tools(PyCodeObject *code) } static void -initialize_lines(PyCodeObject *code, int bytes_per_entry) +initialize_lines(_PyCoLineInstrumentationData *line_data, PyCodeObject *code, int bytes_per_entry) { ASSERT_WORLD_STOPPED_OR_LOCKED(code); - _PyCoLineInstrumentationData *line_data = code->_co_monitoring->lines; - assert(line_data != NULL); line_data->bytes_per_entry = bytes_per_entry; int code_len = (int)Py_SIZE(code); @@ -1653,18 +1679,19 @@ allocate_instrumentation_data(PyCodeObject *code) ASSERT_WORLD_STOPPED_OR_LOCKED(code); if (code->_co_monitoring == NULL) { - code->_co_monitoring = PyMem_Malloc(sizeof(_PyCoMonitoringData)); - if (code->_co_monitoring == NULL) { + _PyCoMonitoringData *monitoring = PyMem_Malloc(sizeof(_PyCoMonitoringData)); + if (monitoring == NULL) { PyErr_NoMemory(); return -1; } - code->_co_monitoring->local_monitors = (_Py_LocalMonitors){ 0 }; - code->_co_monitoring->active_monitors = (_Py_LocalMonitors){ 0 }; - code->_co_monitoring->tools = NULL; - code->_co_monitoring->lines = NULL; - code->_co_monitoring->line_tools = NULL; - code->_co_monitoring->per_instruction_opcodes = NULL; - code->_co_monitoring->per_instruction_tools = NULL; + monitoring->local_monitors = (_Py_LocalMonitors){ 0 }; + monitoring->active_monitors = (_Py_LocalMonitors){ 0 }; + monitoring->tools = NULL; + monitoring->lines = NULL; + monitoring->line_tools = NULL; + monitoring->per_instruction_opcodes = NULL; + monitoring->per_instruction_tools = NULL; + _Py_atomic_store_ptr_release(&code->_co_monitoring, monitoring); } return 0; } @@ -1682,7 +1709,7 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp) _Py_LocalMonitors *local_monitors = &code->_co_monitoring->local_monitors; for (int i = 0; i < PY_MONITORING_TOOL_IDS; i++) { if (code->_co_monitoring->tool_versions[i] != interp->monitoring_tool_versions[i]) { - for (int j = 0; j < _PY_MONITORING_LOCAL_EVENTS; j++) { + for (int j = 0; j < _PY_MONITORING_UNGROUPED_EVENTS; j++) { local_monitors->tools[j] &= ~(1 << i); } } @@ -1729,12 +1756,13 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp) else { bytes_per_entry = 5; } - code->_co_monitoring->lines = PyMem_Malloc(1 + code_len * bytes_per_entry); - if (code->_co_monitoring->lines == NULL) { + _PyCoLineInstrumentationData *lines = PyMem_Malloc(1 + code_len * bytes_per_entry); + if (lines == NULL) { PyErr_NoMemory(); return -1; } - initialize_lines(code, bytes_per_entry); + initialize_lines(lines, code, bytes_per_entry); + _Py_atomic_store_ptr_release(&code->_co_monitoring->lines, lines); } if (multitools && code->_co_monitoring->line_tools == NULL) { code->_co_monitoring->line_tools = PyMem_Malloc(code_len); @@ -1849,7 +1877,7 @@ force_instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp) if (removed_line_tools) { _PyCoLineInstrumentationData *line_data = code->_co_monitoring->lines; for (int i = code->_co_firsttraceable; i < code_len;) { - if (get_original_opcode(line_data, i)) { + if (_PyCode_GetOriginalOpcode(line_data, i)) { remove_line_tools(code, i, removed_line_tools); } i += _PyInstruction_GetLength(code, i); @@ -1876,7 +1904,7 @@ force_instrument_lock_held(PyCodeObject *code, PyInterpreterState *interp) if (new_line_tools) { _PyCoLineInstrumentationData *line_data = code->_co_monitoring->lines; for (int i = code->_co_firsttraceable; i < code_len;) { - if (get_original_opcode(line_data, i)) { + if (_PyCode_GetOriginalOpcode(line_data, i)) { add_line_tools(code, i, new_line_tools); } i += _PyInstruction_GetLength(code, i); @@ -1939,28 +1967,26 @@ _Py_Instrument(PyCodeObject *code, PyInterpreterState *interp) static int -instrument_all_executing_code_objects(PyInterpreterState *interp) { +instrument_all_executing_code_objects(PyInterpreterState *interp) +{ ASSERT_WORLD_STOPPED(); - _PyRuntimeState *runtime = &_PyRuntime; - HEAD_LOCK(runtime); - PyThreadState* ts = PyInterpreterState_ThreadHead(interp); - HEAD_UNLOCK(runtime); - while (ts) { + int err = 0; + _Py_FOR_EACH_TSTATE_BEGIN(interp, ts) { _PyInterpreterFrame *frame = ts->current_frame; while (frame) { if (frame->owner < FRAME_OWNED_BY_INTERPRETER) { - if (instrument_lock_held(_PyFrame_GetCode(frame), interp)) { - return -1; + err = instrument_lock_held(_PyFrame_GetCode(frame), interp); + if (err) { + goto done; } } frame = frame->previous; } - HEAD_LOCK(runtime); - ts = PyThreadState_Next(ts); - HEAD_UNLOCK(runtime); } - return 0; +done: + _Py_FOR_EACH_TSTATE_END(interp); + return err; } static void @@ -1979,7 +2005,7 @@ static void set_local_events(_Py_LocalMonitors *m, int tool_id, _PyMonitoringEventSet events) { assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS); - for (int e = 0; e < _PY_MONITORING_LOCAL_EVENTS; e++) { + for (int e = 0; e < _PY_MONITORING_UNGROUPED_EVENTS; e++) { uint8_t *tools = &m->tools[e]; int val = (events >> e) & 1; *tools &= ~(1 << tool_id); @@ -2006,6 +2032,7 @@ check_tool(PyInterpreterState *interp, int tool_id) int _PyMonitoring_SetEvents(int tool_id, _PyMonitoringEventSet events) { + ASSERT_WORLD_STOPPED(); assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS); PyThreadState *tstate = _PyThreadState_GET(); PyInterpreterState *interp = tstate->interp; @@ -2014,36 +2041,31 @@ _PyMonitoring_SetEvents(int tool_id, _PyMonitoringEventSet events) return -1; } - int res; - _PyEval_StopTheWorld(interp); uint32_t existing_events = get_events(&interp->monitors, tool_id); if (existing_events == events) { - res = 0; - goto done; + return 0; } - set_events(&interp->monitors, tool_id, events); uint32_t new_version = global_version(interp) + MONITORING_VERSION_INCREMENT; if (new_version == 0) { PyErr_Format(PyExc_OverflowError, "events set too many times"); - res = -1; - goto done; + return -1; } + set_events(&interp->monitors, tool_id, events); set_global_version(tstate, new_version); #ifdef _Py_TIER2 _Py_Executors_InvalidateAll(interp, 1); #endif - res = instrument_all_executing_code_objects(interp); -done: - _PyEval_StartTheWorld(interp); - return res; + return instrument_all_executing_code_objects(interp); } int _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEventSet events) { + ASSERT_WORLD_STOPPED(); + assert(0 <= tool_id && tool_id < PY_MONITORING_TOOL_IDS); PyInterpreterState *interp = _PyInterpreterState_GET(); - assert(events < (1 << _PY_MONITORING_LOCAL_EVENTS)); + assert(events < (1 << _PY_MONITORING_UNGROUPED_EVENTS)); if (code->_co_firsttraceable >= Py_SIZE(code)) { PyErr_Format(PyExc_SystemError, "cannot instrument shim code object '%U'", code->co_name); return -1; @@ -2052,11 +2074,8 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent return -1; } - int res; - _PyEval_StopTheWorld(interp); if (allocate_instrumentation_data(code)) { - res = -1; - goto done; + return -1; } code->_co_monitoring->tool_versions[tool_id] = interp->monitoring_tool_versions[tool_id]; @@ -2064,16 +2083,11 @@ _PyMonitoring_SetLocalEvents(PyCodeObject *code, int tool_id, _PyMonitoringEvent _Py_LocalMonitors *local = &code->_co_monitoring->local_monitors; uint32_t existing_events = get_local_events(local, tool_id); if (existing_events == events) { - res = 0; - goto done; + return 0; } set_local_events(local, tool_id, events); - res = force_instrument_lock_held(code, interp); - -done: - _PyEval_StartTheWorld(interp); - return res; + return force_instrument_lock_held(code, interp); } int @@ -2105,11 +2119,12 @@ int _PyMonitoring_ClearToolId(int tool_id) } } + _PyEval_StopTheWorld(interp); if (_PyMonitoring_SetEvents(tool_id, 0) < 0) { + _PyEval_StartTheWorld(interp); return -1; } - _PyEval_StopTheWorld(interp); uint32_t version = global_version(interp) + MONITORING_VERSION_INCREMENT; if (version == 0) { PyErr_Format(PyExc_OverflowError, "events set too many times"); @@ -2125,6 +2140,9 @@ int _PyMonitoring_ClearToolId(int tool_id) // Set the new global version so all the code objects can refresh the // instrumentation. set_global_version(_PyThreadState_GET(), version); +#ifdef _Py_TIER2 + _Py_Executors_InvalidateAll(interp, 1); +#endif int res = instrument_all_executing_code_objects(interp); _PyEval_StartTheWorld(interp); return res; @@ -2346,7 +2364,11 @@ monitoring_set_events_impl(PyObject *module, int tool_id, int event_set) event_set &= ~(1 << PY_MONITORING_EVENT_BRANCH); event_set |= (1 << PY_MONITORING_EVENT_BRANCH_RIGHT) | (1 << PY_MONITORING_EVENT_BRANCH_LEFT); } - if (_PyMonitoring_SetEvents(tool_id, event_set)) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); + int err = _PyMonitoring_SetEvents(tool_id, event_set); + _PyEval_StartTheWorld(interp); + if (err) { return NULL; } Py_RETURN_NONE; @@ -2379,7 +2401,7 @@ monitoring_get_local_events_impl(PyObject *module, int tool_id, _PyMonitoringEventSet event_set = 0; _PyCoMonitoringData *data = ((PyCodeObject *)code)->_co_monitoring; if (data != NULL) { - for (int e = 0; e < _PY_MONITORING_LOCAL_EVENTS; e++) { + for (int e = 0; e < _PY_MONITORING_UNGROUPED_EVENTS; e++) { if ((data->local_monitors.tools[e] >> tool_id) & 1) { event_set |= (1 << e); } @@ -2422,12 +2444,16 @@ monitoring_set_local_events_impl(PyObject *module, int tool_id, event_set &= ~(1 << PY_MONITORING_EVENT_BRANCH); event_set |= (1 << PY_MONITORING_EVENT_BRANCH_RIGHT) | (1 << PY_MONITORING_EVENT_BRANCH_LEFT); } - if (event_set < 0 || event_set >= (1 << _PY_MONITORING_LOCAL_EVENTS)) { + if (event_set < 0 || event_set >= (1 << _PY_MONITORING_UNGROUPED_EVENTS)) { PyErr_Format(PyExc_ValueError, "invalid local event set 0x%x", event_set); return NULL; } - if (_PyMonitoring_SetLocalEvents((PyCodeObject*)code, tool_id, event_set)) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); + int err = _PyMonitoring_SetLocalEvents((PyCodeObject*)code, tool_id, event_set); + _PyEval_StartTheWorld(interp); + if (err) { return NULL; } Py_RETURN_NONE; @@ -2459,6 +2485,9 @@ monitoring_restart_events_impl(PyObject *module) } interp->last_restart_version = restart_version; set_global_version(tstate, new_version); +#ifdef _Py_TIER2 + _Py_Executors_InvalidateAll(interp, 1); +#endif int res = instrument_all_executing_code_objects(interp); _PyEval_StartTheWorld(interp); @@ -2601,8 +2630,9 @@ capi_call_instrumentation(PyMonitoringState *state, PyObject *codelike, int32_t PyErr_SetString(PyExc_ValueError, "offset must be non-negative"); return -1; } + PyObject *offset_obj = NULL; if (event != PY_MONITORING_EVENT_LINE) { - PyObject *offset_obj = PyLong_FromLong(offset); + offset_obj = PyLong_FromLong(offset); if (offset_obj == NULL) { return -1; } @@ -2643,6 +2673,7 @@ capi_call_instrumentation(PyMonitoringState *state, PyObject *codelike, int32_t } } } + Py_XDECREF(offset_obj); return err; } diff --git a/Python/interpconfig.c b/Python/interpconfig.c index 1add8a81425b9a..a37bd3f5b23a01 100644 --- a/Python/interpconfig.c +++ b/Python/interpconfig.c @@ -208,7 +208,7 @@ interp_config_from_dict(PyObject *origdict, PyInterpreterConfig *config, } else if (unused > 0) { PyErr_Format(PyExc_ValueError, - "config dict has %d extra items (%R)", unused, dict); + "config dict has %zd extra items (%R)", unused, dict); goto error; } diff --git a/Python/intrinsics.c b/Python/intrinsics.c index 8ea920e690cd0d..f081f33cc83b88 100644 --- a/Python/intrinsics.c +++ b/Python/intrinsics.c @@ -7,9 +7,11 @@ #include "pycore_genobject.h" // _PyAsyncGenValueWrapperNew #include "pycore_interpframe.h" // _PyFrame_GetLocals() #include "pycore_intrinsics.h" // INTRINSIC_PRINT +#include "pycore_list.h" // _PyList_AsTupleAndClear() +#include "pycore_object.h" // _PyObject_IsUniquelyReferenced() +#include "pycore_setobject.h" // _PySet_Freeze() #include "pycore_pyerrors.h" // _PyErr_SetString() #include "pycore_runtime.h" // _Py_ID() -#include "pycore_tuple.h" // _PyTuple_FromArray() #include "pycore_typevarobject.h" // _Py_make_typevar() #include "pycore_unicodeobject.h" // _PyUnicode_FromASCII() @@ -191,8 +193,12 @@ unary_pos(PyThreadState* unused, PyObject *value) static PyObject * list_to_tuple(PyThreadState* unused, PyObject *v) { - assert(PyList_Check(v)); - return _PyTuple_FromArray(((PyListObject *)v)->ob_item, Py_SIZE(v)); + /* INTRINSIC_LIST_TO_TUPLE is only emitted by the compiler for a + freshly-built, uniquely-referenced temporary list, so steal its items + into the tuple instead of copying them. */ + assert(PyList_CheckExact(v)); + assert(_PyObject_IsUniquelyReferenced(v)); + return _PyList_AsTupleAndClear((PyListObject *)v); } static PyObject * @@ -202,6 +208,14 @@ make_typevar(PyThreadState* Py_UNUSED(ignored), PyObject *v) return _Py_make_typevar(v, NULL, NULL); } +static PyObject * +make_frozenset(PyThreadState* Py_UNUSED(ignored), PyObject *set) +{ + assert(PySet_CheckExact(set)); + assert(_PyObject_IsUniquelyReferenced(set)); + return _PySet_Freeze(set); +} + #define INTRINSIC_FUNC_ENTRY(N, F) \ [N] = {F, #N}, @@ -220,6 +234,7 @@ _PyIntrinsics_UnaryFunctions[] = { INTRINSIC_FUNC_ENTRY(INTRINSIC_TYPEVARTUPLE, _Py_make_typevartuple) INTRINSIC_FUNC_ENTRY(INTRINSIC_SUBSCRIPT_GENERIC, _Py_subscript_generic) INTRINSIC_FUNC_ENTRY(INTRINSIC_TYPEALIAS, _Py_make_typealias) + INTRINSIC_FUNC_ENTRY(INTRINSIC_BUILD_FROZENSET, make_frozenset) }; diff --git a/Python/jit.c b/Python/jit.c index 01bc0076497c6d..50e606a7a0963f 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -11,11 +11,16 @@ #include "pycore_floatobject.h" #include "pycore_frame.h" #include "pycore_function.h" +#include "pycore_genobject.h" +#include "pycore_import.h" #include "pycore_interpframe.h" #include "pycore_interpolation.h" #include "pycore_intrinsics.h" +#include "pycore_jit_publish.h" +#include "pycore_lazyimportobject.h" #include "pycore_list.h" #include "pycore_long.h" +#include "pycore_mmap.h" #include "pycore_opcode_metadata.h" #include "pycore_opcode_utils.h" #include "pycore_optimizer.h" @@ -28,7 +33,7 @@ #include "pycore_jit.h" -// Memory management stuff: //////////////////////////////////////////////////// +// Memory management stuff: /////////////////////////////////////////////////// #ifndef MS_WINDOWS #include <sys/mman.h> @@ -57,9 +62,64 @@ jit_error(const char *message) PyErr_Format(PyExc_RuntimeWarning, "JIT %s (%d)", message, hint); } +static int +address_in_executor_array(_PyExecutorObject **ptrs, size_t count, uintptr_t addr) +{ + for (size_t i = 0; i < count; i++) { + _PyExecutorObject *exec = ptrs[i]; + if (exec->jit_code == NULL || exec->jit_size == 0) { + continue; + } + uintptr_t start = (uintptr_t)exec->jit_code; + uintptr_t end = start + exec->jit_size; + if (addr >= start && addr < end) { + return 1; + } + } + return 0; +} + +static int +address_in_executor_list(_PyExecutorObject *head, uintptr_t addr) +{ + for (_PyExecutorObject *exec = head; + exec != NULL; + exec = exec->vm_data.links.next) + { + if (exec->jit_code == NULL || exec->jit_size == 0) { + continue; + } + uintptr_t start = (uintptr_t)exec->jit_code; + uintptr_t end = start + exec->jit_size; + if (addr >= start && addr < end) { + return 1; + } + } + return 0; +} + +PyAPI_FUNC(int) +_PyJIT_AddressInJitCode(PyInterpreterState *interp, uintptr_t addr) +{ + if (interp == NULL) { + return 0; + } + if (address_in_executor_array(interp->executor_ptrs, interp->executor_count, addr)) { + return 1; + } + if (address_in_executor_list(interp->executor_deletion_list_head, addr)) { + return 1; + } + return 0; +} + static unsigned char * jit_alloc(size_t size) { + if (size > PY_MAX_JIT_CODE_SIZE) { + jit_error("code too big; refactor bytecodes.c to keep uop size down, or reduce maximum trace length."); + return NULL; + } assert(size); assert(size % get_page_size() == 0); #ifdef MS_WINDOWS @@ -69,12 +129,11 @@ jit_alloc(size_t size) #else int flags = MAP_ANONYMOUS | MAP_PRIVATE; int prot = PROT_READ | PROT_WRITE; -# ifdef MAP_JIT - flags |= MAP_JIT; - prot |= PROT_EXEC; -# endif unsigned char *memory = mmap(NULL, size, prot, flags, -1, 0); int failed = memory == MAP_FAILED; + if (!failed) { + (void)_PyAnnotateMemoryMap(memory, size, "cpython:jit"); + } #endif if (failed) { jit_error("unable to allocate memory"); @@ -115,14 +174,11 @@ mark_executable(unsigned char *memory, size_t size) jit_error("unable to flush instruction cache"); return -1; } - int old; + DWORD old; int failed = !VirtualProtect(memory, size, PAGE_EXECUTE_READ, &old); #else - int failed = 0; __builtin___clear_cache((char *)memory, (char *)memory + size); -#ifndef MAP_JIT - failed = mprotect(memory, size, PROT_EXEC | PROT_READ); -#endif + int failed = mprotect(memory, size, PROT_EXEC | PROT_READ); #endif if (failed) { jit_error("unable to protect executable memory"); @@ -133,7 +189,8 @@ mark_executable(unsigned char *memory, size_t size) // JIT compiler stuff: ///////////////////////////////////////////////////////// -#define SYMBOL_MASK_WORDS 4 +#define GOT_SLOT_SIZE sizeof(uintptr_t) +#define SYMBOL_MASK_WORDS 8 typedef uint32_t symbol_mask[SYMBOL_MASK_WORDS]; @@ -141,10 +198,11 @@ typedef struct { unsigned char *mem; symbol_mask mask; size_t size; -} trampoline_state; +} symbol_state; typedef struct { - trampoline_state trampolines; + symbol_state trampolines; + symbol_state got_symbols; uintptr_t instruction_starts[UOP_MAX_TRACE_LENGTH]; } jit_state; @@ -164,21 +222,30 @@ set_bits(uint32_t *loc, uint8_t loc_start, uint64_t value, uint8_t value_start, uint8_t width) { assert(loc_start + width <= 32); + uint32_t temp_val; + // Use memcpy to safely read the value, avoiding potential alignment + // issues and strict aliasing violations. + memcpy(&temp_val, loc, sizeof(temp_val)); // Clear the bits we're about to patch: - *loc &= ~(((1ULL << width) - 1) << loc_start); - assert(get_bits(*loc, loc_start, width) == 0); + temp_val &= ~(((1ULL << width) - 1) << loc_start); + assert(get_bits(temp_val, loc_start, width) == 0); // Patch the bits: - *loc |= get_bits(value, value_start, width) << loc_start; - assert(get_bits(*loc, loc_start, width) == get_bits(value, value_start, width)); + temp_val |= get_bits(value, value_start, width) << loc_start; + assert(get_bits(temp_val, loc_start, width) == get_bits(value, value_start, width)); + // Safely write the modified value back to memory. + memcpy(loc, &temp_val, sizeof(temp_val)); } // See https://developer.arm.com/documentation/ddi0602/2023-09/Base-Instructions // for instruction encodings: -#define IS_AARCH64_ADD_OR_SUB(I) (((I) & 0x11C00000) == 0x11000000) -#define IS_AARCH64_ADRP(I) (((I) & 0x9F000000) == 0x90000000) -#define IS_AARCH64_BRANCH(I) (((I) & 0x7C000000) == 0x14000000) -#define IS_AARCH64_LDR_OR_STR(I) (((I) & 0x3B000000) == 0x39000000) -#define IS_AARCH64_MOV(I) (((I) & 0x9F800000) == 0x92800000) +#define IS_AARCH64_ADD_OR_SUB(I) (((I) & 0x11C00000) == 0x11000000) +#define IS_AARCH64_ADRP(I) (((I) & 0x9F000000) == 0x90000000) +#define IS_AARCH64_BRANCH(I) (((I) & 0x7C000000) == 0x14000000) +#define IS_AARCH64_BRANCH_COND(I) (((I) & 0x7C000000) == 0x54000000) +#define IS_AARCH64_BRANCH_ZERO(I) (((I) & 0x7E000000) == 0x34000000) +#define IS_AARCH64_TEST_AND_BRANCH(I) (((I) & 0x7E000000) == 0x36000000) +#define IS_AARCH64_LDR_OR_STR(I) (((I) & 0x3B000000) == 0x39000000) +#define IS_AARCH64_MOV(I) (((I) & 0x9F800000) == 0x92800000) // LLD is a great reference for performing relocations... just keep in // mind that Tools/jit/build.py does filtering and preprocessing for us! @@ -200,6 +267,33 @@ set_bits(uint32_t *loc, uint8_t loc_start, uint64_t value, uint8_t value_start, // - x86_64-unknown-linux-gnu: // - https://github.com/llvm/llvm-project/blob/main/lld/ELF/Arch/X86_64.cpp + +// Get the symbol slot memory location for a given symbol ordinal. +static unsigned char * +get_symbol_slot(int ordinal, symbol_state *state, int size) +{ + const uint32_t symbol_mask = 1U << (ordinal % 32); + const uint32_t state_mask = state->mask[ordinal / 32]; + assert(symbol_mask & state_mask); + + // Count the number of set bits in the symbol mask lower than ordinal + size_t index = _Py_popcount32(state_mask & (symbol_mask - 1)); + for (int i = 0; i < ordinal / 32; i++) { + index += _Py_popcount32(state->mask[i]); + } + + unsigned char *slot = state->mem + index * size; + assert((size_t)(index + 1) * size <= state->size); + return slot; +} + +// Return the address of the GOT slot for the requested symbol ordinal. +static uintptr_t +got_symbol_address(int ordinal, jit_state *state) +{ + return (uintptr_t)get_symbol_slot(ordinal, &state->got_symbols, GOT_SLOT_SIZE); +} + // Many of these patches are "relaxing", meaning that they can rewrite the // code they're patching to be more efficient (like turning a 64-bit memory // load into a 32-bit immediate load). These patches have an "x" in their name. @@ -209,30 +303,29 @@ set_bits(uint32_t *loc, uint8_t loc_start, uint64_t value, uint8_t value_start, void patch_32(unsigned char *location, uint64_t value) { - uint32_t *loc32 = (uint32_t *)location; // Check that we're not out of range of 32 unsigned bits: assert(value < (1ULL << 32)); - *loc32 = (uint32_t)value; + uint32_t final_value = (uint32_t)value; + memcpy(location, &final_value, sizeof(final_value)); } // 32-bit relative address. void patch_32r(unsigned char *location, uint64_t value) { - uint32_t *loc32 = (uint32_t *)location; value -= (uintptr_t)location; // Check that we're not out of range of 32 signed bits: assert((int64_t)value >= -(1LL << 31)); assert((int64_t)value < (1LL << 31)); - *loc32 = (uint32_t)value; + uint32_t final_value = (uint32_t)value; + memcpy(location, &final_value, sizeof(final_value)); } // 64-bit absolute address. void patch_64(unsigned char *location, uint64_t value) { - uint64_t *loc64 = (uint64_t *)location; - *loc64 = value; + memcpy(location, &value, sizeof(value)); } // 12-bit low part of an absolute address. Pairs nicely with patch_aarch64_21r @@ -255,15 +348,11 @@ patch_aarch64_12(unsigned char *location, uint64_t value) set_bits(loc32, 10, value, shift, 12); } -// Relaxable 12-bit low part of an absolute address. Pairs nicely with -// patch_aarch64_21rx (below). +// Relaxable 12-bit low part of an absolute address. +// Usually paired with patch_aarch64_21rx (below). void patch_aarch64_12x(unsigned char *location, uint64_t value) { - // This can *only* be relaxed if it occurs immediately before a matching - // patch_aarch64_21rx. If that happens, the JIT build step will replace both - // calls with a single call to patch_aarch64_33rx. Otherwise, we end up - // here, and the instruction is patched normally: patch_aarch64_12(location, value); } @@ -328,17 +417,28 @@ patch_aarch64_21r(unsigned char *location, uint64_t value) } // Relaxable 21-bit count of pages between this page and an absolute address's -// page. Pairs nicely with patch_aarch64_12x (above). +// page. Usually paired with patch_aarch64_12x (above). void patch_aarch64_21rx(unsigned char *location, uint64_t value) { - // This can *only* be relaxed if it occurs immediately before a matching - // patch_aarch64_12x. If that happens, the JIT build step will replace both - // calls with a single call to patch_aarch64_33rx. Otherwise, we end up - // here, and the instruction is patched normally: patch_aarch64_21r(location, value); } +// 21-bit relative branch. +void +patch_aarch64_19r(unsigned char *location, uint64_t value) +{ + uint32_t *loc32 = (uint32_t *)location; + assert(IS_AARCH64_BRANCH_COND(*loc32) || IS_AARCH64_BRANCH_ZERO(*loc32)); + value -= (uintptr_t)location; + // Check that we're not out of range of 21 signed bits: + assert((int64_t)value >= -(1 << 20)); + assert((int64_t)value < (1 << 20)); + // Since instructions are 4-byte aligned, only use 19 bits: + assert(get_bits(value, 0, 2) == 0); + set_bits(loc32, 5, value, 2, 19); +} + // 28-bit relative branch. void patch_aarch64_26r(unsigned char *location, uint64_t value) @@ -356,42 +456,52 @@ patch_aarch64_26r(unsigned char *location, uint64_t value) // A pair of patch_aarch64_21rx and patch_aarch64_12x. void -patch_aarch64_33rx(unsigned char *location, uint64_t value) +patch_aarch64_33rx(unsigned char *location_a, unsigned char *location_b, uint64_t value) { - uint32_t *loc32 = (uint32_t *)location; + uint32_t *loc32_a = (uint32_t *)location_a; + uint32_t *loc32_b = (uint32_t *)location_b; // Try to relax the pair of GOT loads into an immediate value: - assert(IS_AARCH64_ADRP(*loc32)); - unsigned char reg = get_bits(loc32[0], 0, 5); - assert(IS_AARCH64_LDR_OR_STR(loc32[1])); + assert(IS_AARCH64_ADRP(*loc32_a)); + assert(IS_AARCH64_LDR_OR_STR(*loc32_b)); + unsigned char reg = get_bits(*loc32_a, 0, 5); // There should be only one register involved: - assert(reg == get_bits(loc32[1], 0, 5)); // ldr's output register. - assert(reg == get_bits(loc32[1], 5, 5)); // ldr's input register. + assert(reg == get_bits(*loc32_a, 0, 5)); // ldr's output register. + assert(reg == get_bits(*loc32_b, 5, 5)); // ldr's input register. uint64_t relaxed = *(uint64_t *)value; if (relaxed < (1UL << 16)) { // adrp reg, AAA; ldr reg, [reg + BBB] -> movz reg, XXX; nop - loc32[0] = 0xD2800000 | (get_bits(relaxed, 0, 16) << 5) | reg; - loc32[1] = 0xD503201F; + *loc32_a = 0xD2800000 | (get_bits(relaxed, 0, 16) << 5) | reg; + *loc32_b = 0xD503201F; return; } if (relaxed < (1ULL << 32)) { // adrp reg, AAA; ldr reg, [reg + BBB] -> movz reg, XXX; movk reg, YYY - loc32[0] = 0xD2800000 | (get_bits(relaxed, 0, 16) << 5) | reg; - loc32[1] = 0xF2A00000 | (get_bits(relaxed, 16, 16) << 5) | reg; + *loc32_a = 0xD2800000 | (get_bits(relaxed, 0, 16) << 5) | reg; + *loc32_b = 0xF2A00000 | (get_bits(relaxed, 16, 16) << 5) | reg; return; } - relaxed = value - (uintptr_t)location; + int64_t page_delta = (relaxed >> 12) - ((uintptr_t)location_a >> 12); + if (page_delta >= -(1L << 20) && + page_delta < (1L << 20)) + { + // adrp reg, AAA; ldr reg, [reg + BBB] -> adrp reg, AAA; add reg, reg, BBB + patch_aarch64_21rx(location_a, relaxed); + *loc32_b = 0x91000000 | get_bits(relaxed, 0, 12) << 10 | reg << 5 | reg; + return; + } + relaxed = value - (uintptr_t)location_a; if ((relaxed & 0x3) == 0 && (int64_t)relaxed >= -(1L << 19) && (int64_t)relaxed < (1L << 19)) { // adrp reg, AAA; ldr reg, [reg + BBB] -> ldr reg, XXX; nop - loc32[0] = 0x58000000 | (get_bits(relaxed, 2, 19) << 5) | reg; - loc32[1] = 0xD503201F; + *loc32_a = 0x58000000 | (get_bits(relaxed, 2, 19) << 5) | reg; + *loc32_b = 0xD503201F; return; } // Couldn't do it. Just patch the two instructions normally: - patch_aarch64_21rx(location, value); - patch_aarch64_12x(location + 4, value); + patch_aarch64_21rx(location_a, value); + patch_aarch64_12x(location_b, value); } // Relaxable 32-bit relative address. @@ -400,7 +510,10 @@ patch_x86_64_32rx(unsigned char *location, uint64_t value) { uint8_t *loc8 = (uint8_t *)location; // Try to relax the GOT load into an immediate value: - uint64_t relaxed = *(uint64_t *)(value + 4) - 4; + uint64_t relaxed; + memcpy(&relaxed, (void *)(value + 4), sizeof(relaxed)); + relaxed -= 4; + if ((int64_t)relaxed - (int64_t)location >= -(1LL << 31) && (int64_t)relaxed - (int64_t)location + 1 < (1LL << 31)) { @@ -425,18 +538,34 @@ patch_x86_64_32rx(unsigned char *location, uint64_t value) patch_32r(location, value); } +void patch_got_symbol(jit_state *state, int ordinal); void patch_aarch64_trampoline(unsigned char *location, int ordinal, jit_state *state); +void patch_x86_64_trampoline(unsigned char *location, int ordinal, jit_state *state); #include "jit_stencils.h" #if defined(__aarch64__) || defined(_M_ARM64) #define TRAMPOLINE_SIZE 16 #define DATA_ALIGN 8 +#elif defined(__x86_64__) && defined(__APPLE__) + // LLVM 20 on macOS x86_64 debug builds: GOT entries may exceed ±2GB PC-relative + // range. + #define TRAMPOLINE_SIZE 16 // 14 bytes + 2 bytes padding for alignment + #define DATA_ALIGN 8 #else #define TRAMPOLINE_SIZE 0 #define DATA_ALIGN 1 #endif +// Populate the GOT entry for the given symbol ordinal with its resolved address. +void +patch_got_symbol(jit_state *state, int ordinal) +{ + uint64_t value = (uintptr_t)symbols_map[ordinal]; + unsigned char *location = (unsigned char *)get_symbol_slot(ordinal, &state->got_symbols, GOT_SLOT_SIZE); + patch_64(location, value); +} + // Generate and patch AArch64 trampolines. The symbols to jump to are stored // in the jit_stencils.h in the symbols_map. void @@ -453,21 +582,8 @@ patch_aarch64_trampoline(unsigned char *location, int ordinal, jit_state *state) return; } - // Masking is done modulo 32 as the mask is stored as an array of uint32_t - const uint32_t symbol_mask = 1 << (ordinal % 32); - const uint32_t trampoline_mask = state->trampolines.mask[ordinal / 32]; - assert(symbol_mask & trampoline_mask); - - // Count the number of set bits in the trampoline mask lower than ordinal, - // this gives the index into the array of trampolines. - int index = _Py_popcount32(trampoline_mask & (symbol_mask - 1)); - for (int i = 0; i < ordinal / 32; i++) { - index += _Py_popcount32(state->trampolines.mask[i]); - } - - uint32_t *p = (uint32_t*)(state->trampolines.mem + index * TRAMPOLINE_SIZE); - assert((size_t)(index + 1) * TRAMPOLINE_SIZE <= state->trampolines.size); - + // Out of range - need a trampoline + uint32_t *p = (uint32_t *)get_symbol_slot(ordinal, &state->trampolines, TRAMPOLINE_SIZE); /* Generate the trampoline 0: 58000048 ldr x8, 8 @@ -483,6 +599,37 @@ patch_aarch64_trampoline(unsigned char *location, int ordinal, jit_state *state) patch_aarch64_26r(location, (uintptr_t)p); } +// Generate and patch x86_64 trampolines. +void +patch_x86_64_trampoline(unsigned char *location, int ordinal, jit_state *state) +{ + uint64_t value = (uintptr_t)symbols_map[ordinal]; + int64_t range = (int64_t)value - 4 - (int64_t)location; + + // If we are in range of 32 signed bits, we can patch directly + if (range >= -(1LL << 31) && range < (1LL << 31)) { + patch_32r(location, value - 4); + return; + } + + // Out of range - need a trampoline + unsigned char *trampoline = get_symbol_slot(ordinal, &state->trampolines, TRAMPOLINE_SIZE); + + /* Generate the trampoline (14 bytes, padded to 16): + 0: ff 25 00 00 00 00 jmp *(%rip) + 6: XX XX XX XX XX XX XX XX (64-bit target address) + + Reference: https://wiki.osdev.org/X86-64_Instruction_Encoding#FF (JMP r/m64) + */ + trampoline[0] = 0xFF; + trampoline[1] = 0x25; + memset(trampoline + 2, 0, 4); + memcpy(trampoline + 6, &value, 8); + + // Patch the call site to call the trampoline instead + patch_32r(location, (uintptr_t)trampoline - 4); +} + static void combine_symbol_mask(const symbol_mask src, symbol_mask dest) { @@ -501,10 +648,6 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz size_t code_size = 0; size_t data_size = 0; jit_state state = {0}; - group = &shim; - code_size += group->code_size; - data_size += group->data_size; - combine_symbol_mask(group->trampoline_mask, state.trampolines.mask); for (size_t i = 0; i < length; i++) { const _PyUOpInstruction *instruction = &trace[i]; group = &stencil_groups[instruction->opcode]; @@ -512,33 +655,36 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz code_size += group->code_size; data_size += group->data_size; combine_symbol_mask(group->trampoline_mask, state.trampolines.mask); + combine_symbol_mask(group->got_mask, state.got_symbols.mask); } - group = &stencil_groups[_FATAL_ERROR]; + group = &stencil_groups[_FATAL_ERROR_r00]; code_size += group->code_size; data_size += group->data_size; combine_symbol_mask(group->trampoline_mask, state.trampolines.mask); + combine_symbol_mask(group->got_mask, state.got_symbols.mask); // Calculate the size of the trampolines required by the whole trace for (size_t i = 0; i < Py_ARRAY_LENGTH(state.trampolines.mask); i++) { state.trampolines.size += _Py_popcount32(state.trampolines.mask[i]) * TRAMPOLINE_SIZE; } + for (size_t i = 0; i < Py_ARRAY_LENGTH(state.got_symbols.mask); i++) { + state.got_symbols.size += _Py_popcount32(state.got_symbols.mask[i]) * GOT_SLOT_SIZE; + } // Round up to the nearest page: size_t page_size = get_page_size(); assert((page_size & (page_size - 1)) == 0); size_t code_padding = DATA_ALIGN - ((code_size + state.trampolines.size) & (DATA_ALIGN - 1)); - size_t padding = page_size - ((code_size + state.trampolines.size + code_padding + data_size) & (page_size - 1)); - size_t total_size = code_size + state.trampolines.size + code_padding + data_size + padding; + size_t padding = page_size - ((code_size + state.trampolines.size + code_padding + data_size + state.got_symbols.size) & (page_size - 1)); + size_t total_size = code_size + state.trampolines.size + code_padding + data_size + state.got_symbols.size + padding; unsigned char *memory = jit_alloc(total_size); if (memory == NULL) { return -1; } -#ifdef MAP_JIT - pthread_jit_write_protect_np(0); -#endif // Collect memory stats OPT_STAT_ADD(jit_total_memory_size, total_size); OPT_STAT_ADD(jit_code_size, code_size); OPT_STAT_ADD(jit_trampoline_size, state.trampolines.size); OPT_STAT_ADD(jit_data_size, data_size); + OPT_STAT_ADD(jit_got_size, state.got_symbols.size); OPT_STAT_ADD(jit_padding_size, padding); OPT_HIST(total_size, trace_total_memory_hist); // Update the offsets of each instruction: @@ -549,14 +695,8 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz unsigned char *code = memory; state.trampolines.mem = memory + code_size; unsigned char *data = memory + code_size + state.trampolines.size + code_padding; - // Compile the shim, which handles converting between the native - // calling convention and the calling convention used by jitted code - // (which may be different for efficiency reasons). - group = &shim; - group->emit(code, data, executor, NULL, &state); - code += group->code_size; - data += group->data_size; - assert(trace[0].opcode == _START_EXECUTOR); + assert(trace[0].opcode == _START_EXECUTOR_r00 || trace[0].opcode == _COLD_EXIT_r00 || trace[0].opcode == _COLD_DYNAMIC_EXIT_r00); + state.got_symbols.mem = data + data_size; for (size_t i = 0; i < length; i++) { const _PyUOpInstruction *instruction = &trace[i]; group = &stencil_groups[instruction->opcode]; @@ -565,25 +705,27 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz data += group->data_size; } // Protect against accidental buffer overrun into data: - group = &stencil_groups[_FATAL_ERROR]; + group = &stencil_groups[_FATAL_ERROR_r00]; group->emit(code, data, executor, NULL, &state); code += group->code_size; data += group->data_size; assert(code == memory + code_size); assert(data == memory + code_size + state.trampolines.size + code_padding + data_size); -#ifdef MAP_JIT - pthread_jit_write_protect_np(1); -#endif if (mark_executable(memory, total_size)) { jit_free(memory, total_size); return -1; } executor->jit_code = memory; - executor->jit_side_entry = memory + shim.code_size; executor->jit_size = total_size; + executor->jit_registration = _PyJit_RegisterCode( + memory, + code_size + state.trampolines.size, + "jit", + "executor"); return 0; } +// Free executor's memory allocated with _PyJIT_Compile void _PyJIT_Free(_PyExecutorObject *executor) { @@ -591,8 +733,9 @@ _PyJIT_Free(_PyExecutorObject *executor) size_t size = executor->jit_size; if (memory) { executor->jit_code = NULL; - executor->jit_side_entry = NULL; executor->jit_size = 0; + _PyJit_UnregisterCode(executor->jit_registration); + executor->jit_registration = NULL; if (jit_free(memory, size)) { PyErr_FormatUnraisable("Exception ignored while " "freeing JIT memory"); @@ -600,4 +743,14 @@ _PyJIT_Free(_PyExecutorObject *executor) } } +// Avoid excessive bloat due to asserts in stencils +int +_Py_jit_assertion_failure(int line) +{ + printf("Assertion failure at line %d of executor_cases.c.h", line); + fflush(stdout); + abort(); + return 0; +} + #endif // _Py_JIT diff --git a/Python/jit_publish.c b/Python/jit_publish.c new file mode 100644 index 00000000000000..fd068076a7709e --- /dev/null +++ b/Python/jit_publish.c @@ -0,0 +1,137 @@ +#include "Python.h" + +#include "pycore_ceval.h" +#include "pycore_jit_publish.h" +#include "pycore_jit_unwind.h" + +#ifdef _Py_JIT + +#if defined(PY_HAVE_JIT_GDB_UNWIND) \ + || defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND) +struct _PyJitCodeRegistration { + void *gdb_handle; + void *gnu_backtrace_handle; +}; +#endif + +static void +jit_register_perf_code(const void *code_addr, size_t code_size, + const char *entry, const char *filename) +{ +#ifdef PY_HAVE_PERF_TRAMPOLINE + _PyPerf_Callbacks callbacks; + _PyPerfTrampoline_GetCallbacks(&callbacks); + if (callbacks.write_state == _Py_perfmap_jit_callbacks.write_state) { + _PyPerfJit_WriteNamedCode( + code_addr, code_size, entry, filename); + } +#else + (void)code_addr; + (void)code_size; + (void)entry; + (void)filename; +#endif +} + +#if defined(PY_HAVE_JIT_GDB_UNWIND) +static void +jit_register_gdb_code(_PyJitCodeRegistration *registration, + const void *code_addr, size_t code_size, + const char *entry, const char *filename) +{ + registration->gdb_handle = _PyJitUnwind_GdbRegisterCode( + code_addr, code_size, entry, filename); +} + +static void +jit_unregister_gdb_code(_PyJitCodeRegistration *registration) +{ + if (registration->gdb_handle != NULL) { + _PyJitUnwind_GdbUnregisterCode(registration->gdb_handle); + registration->gdb_handle = NULL; + } +} +#endif + +#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND) +static void +jit_register_gnu_backtrace_code(_PyJitCodeRegistration *registration, + const void *code_addr, size_t code_size) +{ + registration->gnu_backtrace_handle = + _PyJitUnwind_GnuBacktraceRegisterCode(code_addr, code_size); +} + +static void +jit_unregister_gnu_backtrace_code(_PyJitCodeRegistration *registration) +{ + if (registration->gnu_backtrace_handle != NULL) { + _PyJitUnwind_GnuBacktraceUnregisterCode( + registration->gnu_backtrace_handle); + registration->gnu_backtrace_handle = NULL; + } +} +#endif + +_PyJitCodeRegistration * +_PyJit_RegisterCode(const void *code_addr, size_t code_size, + const char *entry, const char *filename) +{ + jit_register_perf_code(code_addr, code_size, entry, filename); + // Perf publication has no teardown handle, so it is intentionally + // not counted below. + +#if !defined(PY_HAVE_JIT_GDB_UNWIND) \ + && !defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND) + return NULL; +#else + _PyJitCodeRegistration *registration = PyMem_RawCalloc( + 1, sizeof(*registration)); + if (registration == NULL) { + return NULL; + } + + // Partial failures are non-fatal: the JIT code can still execute, but + // unavailable tooling may not be able to unwind it. + int any_registered = 0; +# if defined(PY_HAVE_JIT_GDB_UNWIND) + jit_register_gdb_code( + registration, code_addr, code_size, entry, filename); + any_registered |= registration->gdb_handle != NULL; +# endif +# if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND) + jit_register_gnu_backtrace_code( + registration, code_addr, code_size); + any_registered |= registration->gnu_backtrace_handle != NULL; +# endif + if (!any_registered) { + PyMem_RawFree(registration); + return NULL; + } + return registration; +#endif +} + +void +_PyJit_UnregisterCode(_PyJitCodeRegistration *registration) +{ +#if !defined(PY_HAVE_JIT_GDB_UNWIND) \ + && !defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND) + assert(registration == NULL); + (void)registration; +#else + if (registration == NULL) { + return; + } + +# if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND) + jit_unregister_gnu_backtrace_code(registration); +# endif +# if defined(PY_HAVE_JIT_GDB_UNWIND) + jit_unregister_gdb_code(registration); +# endif + PyMem_RawFree(registration); +#endif +} + +#endif // _Py_JIT diff --git a/Python/jit_unwind.c b/Python/jit_unwind.c new file mode 100644 index 00000000000000..0941ed593ff7d1 --- /dev/null +++ b/Python/jit_unwind.c @@ -0,0 +1,1062 @@ +/* + * Python JIT - DWARF .eh_frame builder + * + * This file contains the DWARF CFI generator used to build .eh_frame + * data for JIT code (perf jitdump and other unwinders). + */ + +#include "Python.h" +#include "pycore_jit_unwind.h" +#include "pycore_lock.h" + +#if defined(PY_HAVE_JIT_GDB_UNWIND) +# include "jit_unwind_info.h" +# if !JIT_UNWIND_INFO_SUPPORTED +# error "JIT unwind info was not generated for this target" +# endif +#endif + +#if defined(PY_HAVE_PERF_TRAMPOLINE) \ + || defined(PY_HAVE_JIT_GDB_UNWIND) \ + || defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND) + +#if defined(PY_HAVE_JIT_GDB_UNWIND) +# include <elf.h> +#endif +#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND) +/* + * libgcc exposes frame registration entry points, but GCC's public headers + * on some distributions do not declare them even though the symbols are + * available in libgcc_s. + */ +void __register_frame(const void *); +void __deregister_frame(const void *); +#endif +#include <stdio.h> +#include <string.h> + +// ============================================================================= +// DWARF CONSTANTS +// ============================================================================= + +/* + * DWARF (Debug With Arbitrary Record Formats) constants + * + * DWARF is a debugging data format used to provide stack unwinding information. + * These constants define the various encoding types and opcodes used in + * DWARF Call Frame Information (CFI) records. + */ + +/* DWARF Call Frame Information version */ +#define DWRF_CIE_VERSION 1 + +/* DWARF CFA (Call Frame Address) opcodes */ +enum { + DWRF_CFA_nop = 0x0, // No operation + DWRF_CFA_offset_extended = 0x5, // Extended offset instruction + DWRF_CFA_def_cfa = 0xc, // Define CFA rule + DWRF_CFA_def_cfa_register = 0xd, // Define CFA register + DWRF_CFA_def_cfa_offset = 0xe, // Define CFA offset + DWRF_CFA_offset_extended_sf = 0x11, // Extended signed offset + DWRF_CFA_advance_loc = 0x40, // Advance location counter + DWRF_CFA_offset = 0x80, // Simple offset instruction +#if defined(__aarch64__) + DWRF_CFA_AARCH64_negate_ra_state = 0x2d, // Toggle return address signing state +#endif + DWRF_CFA_restore = 0xc0 // Restore register +}; + +/* + * Architecture-specific DWARF register numbers + * + * These constants define the register numbering scheme used by DWARF + * for each supported architecture. The numbers must match the ABI + * specification for proper stack unwinding. + */ +enum { +#ifdef __x86_64__ + /* x86_64 register numbering (note: order is defined by x86_64 ABI) */ + DWRF_REG_AX, // RAX + DWRF_REG_DX, // RDX + DWRF_REG_CX, // RCX + DWRF_REG_BX, // RBX + DWRF_REG_SI, // RSI + DWRF_REG_DI, // RDI + DWRF_REG_BP, // RBP + DWRF_REG_SP, // RSP + DWRF_REG_8, // R8 + DWRF_REG_9, // R9 + DWRF_REG_10, // R10 + DWRF_REG_11, // R11 + DWRF_REG_12, // R12 + DWRF_REG_13, // R13 + DWRF_REG_14, // R14 + DWRF_REG_15, // R15 + DWRF_REG_RA, // Return address (RIP) +#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) + /* AArch64 register numbering */ + DWRF_REG_FP = 29, // Frame Pointer + DWRF_REG_RA = 30, // Link register (return address) + DWRF_REG_SP = 31, // Stack pointer +#else +# error "Unsupported target architecture" +#endif +}; + +// ============================================================================= +// ELF OBJECT CONTEXT +// ============================================================================= + +/* + * Context for building ELF/DWARF structures + * + * This structure maintains state while constructing DWARF unwind information. + * It acts as a simple buffer manager with pointers to track current position + * and important landmarks within the buffer. + */ +typedef struct ELFObjectContext { + uint8_t* p; // Current write position in buffer + uint8_t* startp; // Start of buffer (for offset calculations) + uint8_t* fde_p; // Start of FDE data (for PC-relative calculations) + uintptr_t code_addr; // Address of the code section + size_t code_size; // Size of the code section +} ELFObjectContext; + +// ============================================================================= +// DWARF GENERATION UTILITIES +// ============================================================================= + +/* + * Append a null-terminated string to the ELF context buffer. + * + * Args: + * ctx: ELF object context + * str: String to append (must be null-terminated) + * + * Returns: Offset from start of buffer where string was written + */ +static uint32_t elfctx_append_string(ELFObjectContext* ctx, const char* str) { + uint8_t* p = ctx->p; + uint32_t ofs = (uint32_t)(p - ctx->startp); + + /* Copy string including null terminator */ + do { + *p++ = (uint8_t)*str; + } while (*str++); + + ctx->p = p; + return ofs; +} + +/* + * Append a SLEB128 (Signed Little Endian Base 128) value + * + * SLEB128 is a variable-length encoding used extensively in DWARF. + * It efficiently encodes small numbers in fewer bytes. + * + * Args: + * ctx: ELF object context + * v: Signed value to encode + */ +static void elfctx_append_sleb128(ELFObjectContext* ctx, int32_t v) { + uint8_t* p = ctx->p; + + /* Encode 7 bits at a time, with continuation bit in MSB */ + for (; (uint32_t)(v + 0x40) >= 0x80; v >>= 7) { + *p++ = (uint8_t)((v & 0x7f) | 0x80); // Set continuation bit + } + *p++ = (uint8_t)(v & 0x7f); // Final byte without continuation bit + + ctx->p = p; +} + +/* + * Append a ULEB128 (Unsigned Little Endian Base 128) value + * + * Similar to SLEB128 but for unsigned values. + * + * Args: + * ctx: ELF object context + * v: Unsigned value to encode + */ +static void elfctx_append_uleb128(ELFObjectContext* ctx, uint32_t v) { + uint8_t* p = ctx->p; + + /* Encode 7 bits at a time, with continuation bit in MSB */ + for (; v >= 0x80; v >>= 7) { + *p++ = (char)((v & 0x7f) | 0x80); // Set continuation bit + } + *p++ = (char)v; // Final byte without continuation bit + + ctx->p = p; +} + +/* + * Macros for generating DWARF structures + * + * These macros provide a convenient way to write various data types + * to the DWARF buffer while automatically advancing the pointer. + */ +#define DWRF_U8(x) (*p++ = (x)) // Write unsigned 8-bit +#define DWRF_I8(x) (*(int8_t*)p = (x), p++) // Write signed 8-bit +#define DWRF_U16(x) (*(uint16_t*)p = (x), p += 2) // Write unsigned 16-bit +#define DWRF_U32(x) (*(uint32_t*)p = (x), p += 4) // Write unsigned 32-bit +#define DWRF_ADDR(x) (*(uintptr_t*)p = (x), p += sizeof(uintptr_t)) // Write address +#define DWRF_UV(x) (ctx->p = p, elfctx_append_uleb128(ctx, (x)), p = ctx->p) // Write ULEB128 +#define DWRF_SV(x) (ctx->p = p, elfctx_append_sleb128(ctx, (x)), p = ctx->p) // Write SLEB128 +#define DWRF_STR(str) (ctx->p = p, elfctx_append_string(ctx, (str)), p = ctx->p) // Write string + +/* Align to specified boundary with NOP instructions */ +#define DWRF_ALIGNNOP(s) \ + while ((uintptr_t)p & ((s)-1)) { \ + *p++ = DWRF_CFA_nop; \ + } + +/* Write a DWARF section with automatic size calculation */ +#define DWRF_SECTION(name, stmt) \ + { \ + uint32_t* szp_##name = (uint32_t*)p; \ + p += 4; \ + stmt; \ + *szp_##name = (uint32_t)((p - (uint8_t*)szp_##name) - 4); \ + } + +// ============================================================================= +// DWARF EH FRAME GENERATION +// ============================================================================= + +static void elf_init_ehframe_perf(ELFObjectContext* ctx); +#if defined(PY_HAVE_JIT_GDB_UNWIND) +static void elf_init_ehframe_gdb(ELFObjectContext* ctx); +#endif + +static inline void elf_init_ehframe(ELFObjectContext* ctx, int absolute_addr) { + if (absolute_addr) { +#if defined(PY_HAVE_JIT_GDB_UNWIND) + elf_init_ehframe_gdb(ctx); +#else + Py_UNREACHABLE(); +#endif + } + else { + elf_init_ehframe_perf(ctx); + } +} + +size_t +_PyJitUnwind_EhFrameSize(int absolute_addr) +{ + /* The .eh_frame we emit is small and bounded; keep a generous buffer. */ + uint8_t scratch[512]; + _Static_assert(sizeof(scratch) >= 256, + "scratch buffer may be too small for elf_init_ehframe"); + ELFObjectContext ctx; + ctx.code_size = 1; + ctx.code_addr = 0; + ctx.startp = ctx.p = scratch; + ctx.fde_p = NULL; + /* Generate once into scratch to learn the required size. */ + elf_init_ehframe(&ctx, absolute_addr); + ptrdiff_t size = ctx.p - ctx.startp; + assert(size <= (ptrdiff_t)sizeof(scratch)); + return (size_t)size; +} + +size_t +_PyJitUnwind_BuildEhFrame(uint8_t *buffer, size_t buffer_size, + const void *code_addr, size_t code_size, + int absolute_addr) +{ + if (buffer == NULL || code_addr == NULL || code_size == 0) { + return 0; + } + /* Generate the frame twice: once to size-check, once to write. */ + size_t required = _PyJitUnwind_EhFrameSize(absolute_addr); + if (required == 0 || required > buffer_size) { + return 0; + } + ELFObjectContext ctx; + ctx.code_size = code_size; + ctx.code_addr = (uintptr_t)code_addr; + ctx.startp = ctx.p = buffer; + ctx.fde_p = NULL; + elf_init_ehframe(&ctx, absolute_addr); + size_t written = (size_t)(ctx.p - ctx.startp); + /* The frame size is independent of code_addr/code_size (fixed-width fields). */ + assert(written == required); + return written; +} + +/* + * Generate a minimal .eh_frame for a single JIT code region. + * + * The .eh_frame section contains Call Frame Information (CFI) that describes + * how to unwind the stack at any point in the code. This is essential for + * unwinding through JIT-generated code. + * + * The generated data contains: + * 1. A CIE (Common Information Entry) describing the calling convention. + * 2. An FDE (Frame Description Entry) describing how to unwind the JIT frame. + * + * Two flavors are emitted, dispatched on the absolute_addr flag: + * + * - absolute_addr == 0 (elf_init_ehframe_perf): PC-relative FDE address + * encoding for perf's synthesized DSO layout. The CIE describes the + * trampoline's entry state and the FDE walks through the prologue and + * epilogue with advance_loc instructions. This matches the pre-existing + * perf_jit_trampoline behavior byte-for-byte. + * + * - absolute_addr == 1 (elf_init_ehframe_gdb): absolute FDE address + * encoding for the GDB JIT in-memory ELF. The CIE describes the + * steady-state frame layout (CFA = %rbp+16 / x29+16, with saved fp and + * return-address column at fixed offsets) and the FDE emits no further + * CFI. The same rule applies at every PC in the registered region, + * which is correct for executor stencils (they pin the frame pointer + * across the region). This is the GDB-side fix; see elf_init_ehframe_gdb + * for details. + */ +static void elf_init_ehframe_perf(ELFObjectContext* ctx) { + int fde_ptr_enc = DWRF_EH_PE_pcrel | DWRF_EH_PE_sdata4; + uint8_t* p = ctx->p; + uint8_t* framep = p; // Remember start of frame data + + /* + * DWARF Unwind Table for Trampoline Function + * + * This section defines DWARF Call Frame Information (CFI) using encoded macros + * like `DWRF_U8`, `DWRF_UV`, and `DWRF_SECTION` to describe how the trampoline function + * preserves and restores registers. This is used by profiling tools (e.g., `perf`) + * and debuggers for stack unwinding in JIT-compiled code. + * + * ------------------------------------------------- + * TO REGENERATE THIS TABLE FROM GCC OBJECTS: + * ------------------------------------------------- + * + * 1. Create a trampoline source file (e.g., `trampoline.c`): + * + * #include <Python.h> + * typedef PyObject* (*py_evaluator)(void*, void*, int); + * PyObject* trampoline(void *ts, void *f, int throwflag, py_evaluator evaluator) { + * return evaluator(ts, f, throwflag); + * } + * + * 2. Compile to an object file with frame pointer preservation: + * + * gcc trampoline.c -I. -I./Include -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -c + * + * 3. Extract DWARF unwind info from the object file: + * + * readelf -w trampoline.o + * + * Example output from `.eh_frame`: + * + * 00000000 CIE + * Version: 1 + * Augmentation: "zR" + * Code alignment factor: 4 + * Data alignment factor: -8 + * Return address column: 30 + * DW_CFA_def_cfa: r31 (sp) ofs 0 + * + * 00000014 FDE cie=00000000 pc=0..14 + * DW_CFA_advance_loc: 4 + * DW_CFA_def_cfa_offset: 16 + * DW_CFA_offset: r29 at cfa-16 + * DW_CFA_offset: r30 at cfa-8 + * DW_CFA_advance_loc: 12 + * DW_CFA_restore: r30 + * DW_CFA_restore: r29 + * DW_CFA_def_cfa_offset: 0 + * + * -- These values can be verified by comparing with `readelf -w` or `llvm-dwarfdump --eh-frame`. + * + * ---------------------------------- + * HOW TO TRANSLATE TO DWRF_* MACROS: + * ---------------------------------- + * + * After compiling your trampoline with: + * + * gcc trampoline.c -I. -I./Include -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -c + * + * run: + * + * readelf -w trampoline.o + * + * to inspect the generated `.eh_frame` data. You will see two main components: + * + * 1. A CIE (Common Information Entry): shared configuration used by all FDEs. + * 2. An FDE (Frame Description Entry): function-specific unwind instructions. + * + * --------------------- + * Translating the CIE: + * --------------------- + * From `readelf -w`, you might see: + * + * 00000000 0000000000000010 00000000 CIE + * Version: 1 + * Augmentation: "zR" + * Code alignment factor: 4 + * Data alignment factor: -8 + * Return address column: 30 + * Augmentation data: 1b + * DW_CFA_def_cfa: r31 (sp) ofs 0 + * + * Map this to: + * + * DWRF_SECTION(CIE, + * DWRF_U32(0); // CIE ID (always 0 for CIEs) + * DWRF_U8(DWRF_CIE_VERSION); // Version: 1 + * DWRF_STR("zR"); // Augmentation string "zR" + * DWRF_UV(4); // Code alignment factor = 4 + * DWRF_SV(-8); // Data alignment factor = -8 + * DWRF_U8(DWRF_REG_RA); // Return address register (e.g., x30 = 30) + * DWRF_UV(1); // Augmentation data length = 1 + * DWRF_U8(DWRF_EH_PE_pcrel | DWRF_EH_PE_sdata4); // Encoding for FDE pointers + * + * DWRF_U8(DWRF_CFA_def_cfa); // DW_CFA_def_cfa + * DWRF_UV(DWRF_REG_SP); // Register: SP (r31) + * DWRF_UV(0); // Offset = 0 + * + * DWRF_ALIGNNOP(sizeof(uintptr_t)); // Align to pointer size boundary + * ) + * + * Notes: + * - Use `DWRF_UV` for unsigned LEB128, `DWRF_SV` for signed LEB128. + * - `DWRF_REG_RA` and `DWRF_REG_SP` are architecture-defined constants. + * + * --------------------- + * Translating the FDE: + * --------------------- + * From `readelf -w`: + * + * 00000014 0000000000000020 00000018 FDE cie=00000000 pc=0000000000000000..0000000000000014 + * DW_CFA_advance_loc: 4 + * DW_CFA_def_cfa_offset: 16 + * DW_CFA_offset: r29 at cfa-16 + * DW_CFA_offset: r30 at cfa-8 + * DW_CFA_advance_loc: 12 + * DW_CFA_restore: r30 + * DW_CFA_restore: r29 + * DW_CFA_def_cfa_offset: 0 + * + * Map the FDE header and instructions to: + * + * DWRF_SECTION(FDE, + * DWRF_U32((uint32_t)(p - framep)); // Offset to CIE (relative from here) + * DWRF_U32(pc_relative_offset); // PC-relative location of the code (calculated dynamically) + * DWRF_U32(ctx->code_size); // Code range covered by this FDE + * DWRF_U8(0); // Augmentation data length (none) + * + * DWRF_U8(DWRF_CFA_advance_loc | 1); // Advance location by 1 unit (1 * 4 = 4 bytes) + * DWRF_U8(DWRF_CFA_def_cfa_offset); // CFA = SP + 16 + * DWRF_UV(16); + * + * DWRF_U8(DWRF_CFA_offset | DWRF_REG_FP); // Save x29 (frame pointer) + * DWRF_UV(2); // At offset 2 * 8 = 16 bytes + * + * DWRF_U8(DWRF_CFA_offset | DWRF_REG_RA); // Save x30 (return address) + * DWRF_UV(1); // At offset 1 * 8 = 8 bytes + * + * DWRF_U8(DWRF_CFA_advance_loc | 3); // Advance location by 3 units (3 * 4 = 12 bytes) + * + * DWRF_U8(DWRF_CFA_offset | DWRF_REG_RA); // Restore x30 + * DWRF_U8(DWRF_CFA_offset | DWRF_REG_FP); // Restore x29 + * + * DWRF_U8(DWRF_CFA_def_cfa_offset); // CFA = SP + * DWRF_UV(0); + * ) + * + * To regenerate: + * 1. Get the `code alignment factor`, `data alignment factor`, and `RA column` from the CIE. + * 2. Note the range of the function from the FDE's `pc=...` line and map it to the JIT code as + * the code is in a different address space every time. + * 3. For each `DW_CFA_*` entry, use the corresponding `DWRF_*` macro: + * - `DW_CFA_def_cfa_offset` → DWRF_U8(DWRF_CFA_def_cfa_offset), DWRF_UV(value) + * - `DW_CFA_offset: rX` → DWRF_U8(DWRF_CFA_offset | reg), DWRF_UV(offset) + * - `DW_CFA_restore: rX` → DWRF_U8(DWRF_CFA_offset | reg) // restore is same as reusing offset + * - `DW_CFA_advance_loc: N` → DWRF_U8(DWRF_CFA_advance_loc | (N / code_alignment_factor)) + * 4. Use `DWRF_REG_FP`, `DWRF_REG_RA`, etc., for register numbers. + * 5. Use `sizeof(uintptr_t)` (typically 8) for pointer size calculations and alignment. + */ + + /* + * Emit DWARF EH CIE (Common Information Entry) + * + * The CIE describes the calling conventions and basic unwinding rules + * that apply to all functions in this compilation unit. + */ + DWRF_SECTION(CIE, + DWRF_U32(0); // CIE ID (0 indicates this is a CIE) + DWRF_U8(DWRF_CIE_VERSION); // CIE version (1) + DWRF_STR("zR"); // Augmentation string ("zR" = has LSDA) +#ifdef __x86_64__ + DWRF_UV(1); // Code alignment factor (x86_64: 1 byte) +#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) + DWRF_UV(4); // Code alignment factor (AArch64: 4 bytes per instruction) +#endif + DWRF_SV(-(int64_t)sizeof(uintptr_t)); // Data alignment factor (negative) + DWRF_U8(DWRF_REG_RA); // Return address register number + DWRF_UV(1); // Augmentation data length + DWRF_U8(fde_ptr_enc); // FDE pointer encoding + + /* Initial CFI instructions - describe default calling convention */ +#ifdef __x86_64__ + /* x86_64 initial CFI state */ + DWRF_U8(DWRF_CFA_def_cfa); // Define CFA (Call Frame Address) + DWRF_UV(DWRF_REG_SP); // CFA = SP register + DWRF_UV(sizeof(uintptr_t)); // CFA = SP + pointer_size + DWRF_U8(DWRF_CFA_offset|DWRF_REG_RA); // Return address is saved + DWRF_UV(1); // At offset 1 from CFA +#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) + /* AArch64 initial CFI state */ + DWRF_U8(DWRF_CFA_def_cfa); // Define CFA (Call Frame Address) + DWRF_UV(DWRF_REG_SP); // CFA = SP register + DWRF_UV(0); // CFA = SP + 0 (AArch64 starts with offset 0) + // No initial register saves in AArch64 CIE +#endif + DWRF_ALIGNNOP(sizeof(uintptr_t)); // Align to pointer boundary + ) + + /* + * Emit DWARF EH FDE (Frame Description Entry) + * + * The FDE describes unwinding information specific to this function. + * It references the CIE and provides function-specific CFI instructions. + * + * The PC-relative offset is calculated after the entire EH frame is built + * to ensure accurate positioning relative to the synthesized DSO layout. + */ + DWRF_SECTION(FDE, + DWRF_U32((uint32_t)(p - framep)); // Offset to CIE (backwards reference) + /* + * In perf jitdump mode the FDE PC field is encoded PC-relative and + * points back to code_start. Record where that field lives so we can + * patch in the final offset after the rest of the synthetic DSO + * layout is known. + */ + ctx->fde_p = p; // Remember where PC offset field is located for later calculation + DWRF_U32(0); // Placeholder for PC-relative offset (calculated below) + DWRF_U32(ctx->code_size); // Address range covered by this FDE (code length) + DWRF_U8(0); // Augmentation data length (none) + + /* + * Architecture-specific CFI instructions + * + * These instructions describe how registers are saved and restored + * during function calls. Each architecture has different calling + * conventions and register usage patterns. + */ +#ifdef __x86_64__ + /* x86_64 calling convention unwinding rules */ +# if defined(__CET__) && (__CET__ & 1) + DWRF_U8(DWRF_CFA_advance_loc | 4); // Advance past endbr64 (4 bytes) +# endif + DWRF_U8(DWRF_CFA_advance_loc | 1); // Advance past push %rbp (1 byte) + DWRF_U8(DWRF_CFA_def_cfa_offset); // def_cfa_offset 16 + DWRF_UV(16); // New offset: SP + 16 + DWRF_U8(DWRF_CFA_offset | DWRF_REG_BP); // offset r6 at cfa-16 + DWRF_UV(2); // Offset factor: 2 * 8 = 16 bytes + DWRF_U8(DWRF_CFA_advance_loc | 3); // Advance past mov %rsp,%rbp (3 bytes) + DWRF_U8(DWRF_CFA_def_cfa_register); // def_cfa_register r6 + DWRF_UV(DWRF_REG_BP); // Use base pointer register + DWRF_U8(DWRF_CFA_advance_loc | 3); // Advance past call *%rcx (2 bytes) + pop %rbp (1 byte) = 3 + DWRF_U8(DWRF_CFA_def_cfa); // def_cfa r7 ofs 8 + DWRF_UV(DWRF_REG_SP); // Use stack pointer register + DWRF_UV(8); // New offset: SP + 8 +#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) + /* AArch64 calling convention unwinding rules */ +#if defined(__ARM_FEATURE_PAC_DEFAULT) || \ + (defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT == 1) + DWRF_U8(DWRF_CFA_advance_loc | 1); // Advance past SIGN_LR (4 bytes) +#endif +#if defined(__ARM_FEATURE_PAC_DEFAULT) + DWRF_U8(DWRF_CFA_AARCH64_negate_ra_state); // Saved LR is PAC-signed from here +#endif + DWRF_U8(DWRF_CFA_advance_loc | 1); // Advance by 1 instruction (4 bytes) + DWRF_U8(DWRF_CFA_def_cfa_offset); // CFA = SP + 16 + DWRF_UV(16); // Stack pointer moved by 16 bytes + DWRF_U8(DWRF_CFA_offset | DWRF_REG_FP); // x29 (frame pointer) saved + DWRF_UV(2); // At CFA-16 (2 * 8 = 16 bytes from CFA) + DWRF_U8(DWRF_CFA_offset | DWRF_REG_RA); // x30 (link register) saved + DWRF_UV(1); // At CFA-8 (1 * 8 = 8 bytes from CFA) + DWRF_U8(DWRF_CFA_advance_loc | 3); // Advance by 3 instructions (12 bytes) +#if defined(__ARM_FEATURE_PAC_DEFAULT) + DWRF_U8(DWRF_CFA_AARCH64_negate_ra_state); // LR is authenticated, no longer PAC-signed +#endif + DWRF_U8(DWRF_CFA_def_cfa_register); // CFA = FP (x29) + 16 + DWRF_UV(DWRF_REG_FP); + DWRF_U8(DWRF_CFA_restore | DWRF_REG_RA); // Restore x30 - NO DWRF_UV() after this! + DWRF_U8(DWRF_CFA_restore | DWRF_REG_FP); // Restore x29 - NO DWRF_UV() after this! + DWRF_U8(DWRF_CFA_def_cfa); // CFA = SP + 0 (stack restored) + DWRF_UV(DWRF_REG_SP); + DWRF_UV(0); + +#else +# error "Unsupported target architecture" +#endif + + DWRF_ALIGNNOP(sizeof(uintptr_t)); // Align to pointer boundary + ) + + ctx->p = p; // Update context pointer to end of generated data + + /* Calculate and update the PC-relative offset in the FDE + * + * When perf processes the jitdump, it creates a synthesized DSO with this layout: + * + * Synthesized DSO Memory Layout: + * ┌─────────────────────────────────────────────────────────────┐ < code_start + * │ Code Section │ + * │ (round_up(code_size, 8) bytes) │ + * ├─────────────────────────────────────────────────────────────┤ < start of EH frame data + * │ EH Frame Data │ + * │ ┌─────────────────────────────────────────────────────┐ │ + * │ │ CIE data │ │ + * │ └─────────────────────────────────────────────────────┘ │ + * │ ┌─────────────────────────────────────────────────────┐ │ + * │ │ FDE Header: │ │ + * │ │ - CIE offset (4 bytes) │ │ + * │ │ - PC offset (4 bytes) <─ fde_offset_in_frame ─────┼────┼─> points to code_start + * │ │ - address range (4 bytes) │ │ (this specific field) + * │ │ CFI Instructions... │ │ + * │ └─────────────────────────────────────────────────────┘ │ + * ├─────────────────────────────────────────────────────────────┤ < reference_point + * │ EhFrameHeader │ + * │ (navigation metadata) │ + * └─────────────────────────────────────────────────────────────┘ + * + * The PC offset field in the FDE must contain the distance from itself to code_start: + * + * distance = code_start - fde_pc_field + * + * Where: + * fde_pc_field_location = reference_point - eh_frame_size + fde_offset_in_frame + * code_start_location = reference_point - eh_frame_size - round_up(code_size, 8) + * + * Therefore: + * distance = code_start_location - fde_pc_field_location + * = (ref - eh_frame_size - rounded_code_size) - (ref - eh_frame_size + fde_offset_in_frame) + * = -rounded_code_size - fde_offset_in_frame + * = -(round_up(code_size, 8) + fde_offset_in_frame) + * + * Note: fde_offset_in_frame is the offset from EH frame start to the PC offset field. + * + */ + int32_t rounded_code_size = + (int32_t)_Py_SIZE_ROUND_UP(ctx->code_size, 8); + int32_t fde_offset_in_frame = (int32_t)(ctx->fde_p - framep); + *(int32_t *)ctx->fde_p = -(rounded_code_size + fde_offset_in_frame); +} + +/* + * Build .eh_frame data for the GDB JIT interface. + * + * The executor runs inside the frame established by _PyJIT_Entry, but the + * synthetic executor FDE collapses that state into a single logical JIT frame + * that unwinds directly into _PyEval_*. Executor stencils never touch the + * frame pointer - enforced by Tools/jit/_optimizers.py _validate() and + * -mframe-pointer=reserved - so the steady-state rule is valid at every PC + * and the FDE body is empty. Tools/jit/_targets.py derives the initial CFI + * rules from the row active at the executor call in the compiled shim object. + */ +#if defined(PY_HAVE_JIT_GDB_UNWIND) +static void elf_init_ehframe_gdb(ELFObjectContext* ctx) { + int fde_ptr_enc = DWRF_EH_PE_absptr; + uint8_t* p = ctx->p; + uint8_t* framep = p; + + DWRF_SECTION(CIE, + DWRF_U32(0); // CIE ID + DWRF_U8(DWRF_CIE_VERSION); + DWRF_STR("zR"); // aug data length + FDE ptr encoding follow + DWRF_UV(JIT_UNWIND_CODE_ALIGNMENT_FACTOR); + DWRF_SV(JIT_UNWIND_DATA_ALIGNMENT_FACTOR); + DWRF_U8(JIT_UNWIND_RA_REG); + DWRF_UV(1); // Augmentation data length + DWRF_U8(fde_ptr_enc); // FDE pointer encoding + + /* Executor steady-state rule (our invariant, not the compiler's). */ + DWRF_U8(DWRF_CFA_def_cfa); + DWRF_UV(JIT_UNWIND_CFA_REG); + DWRF_UV(JIT_UNWIND_CFA_OFFSET); + DWRF_U8(DWRF_CFA_offset | JIT_UNWIND_FP_REG); + DWRF_UV(JIT_UNWIND_FP_OFFSET); + DWRF_U8(DWRF_CFA_offset | JIT_UNWIND_RA_REG); + DWRF_UV(JIT_UNWIND_RA_OFFSET); + DWRF_ALIGNNOP(sizeof(uintptr_t)); + ) + + DWRF_SECTION(FDE, + DWRF_U32((uint32_t)(p - framep)); // Offset to CIE (backwards reference) + DWRF_ADDR(ctx->code_addr); // Absolute code start + DWRF_ADDR((uintptr_t)ctx->code_size); // Code range covered + DWRF_U8(0); // Augmentation data length (none) + DWRF_ALIGNNOP(sizeof(uintptr_t)); + ) + + ctx->p = p; +} +#endif + +#if defined(PY_HAVE_JIT_GDB_UNWIND) +enum { + JIT_NOACTION = 0, + JIT_REGISTER_FN = 1, + JIT_UNREGISTER_FN = 2, +}; + +struct jit_code_entry { + struct jit_code_entry *next; + struct jit_code_entry *prev; + const char *symfile_addr; + uint64_t symfile_size; + const void *code_addr; +}; + +struct jit_descriptor { + uint32_t version; + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; +}; + +PyMutex _Py_jit_debug_mutex = {0}; + +Py_EXPORTED_SYMBOL volatile struct jit_descriptor __jit_debug_descriptor = { + 1, JIT_NOACTION, NULL, NULL +}; + +Py_EXPORTED_SYMBOL void __attribute__((noinline)) +__jit_debug_register_code(void) +{ + /* Keep this call visible to debuggers and not optimized away. */ + (void)__jit_debug_descriptor.action_flag; +#if defined(__GNUC__) || defined(__clang__) + __asm__ __volatile__("" ::: "memory"); +#endif +} + +static uint16_t +gdb_jit_machine_id(void) +{ + /* Map the current target to ELF e_machine; return 0 to skip registration. */ +#if defined(__x86_64__) || defined(_M_X64) + return EM_X86_64; +#elif defined(__aarch64__) && !defined(__ILP32__) + return EM_AARCH64; +#else + return 0; +#endif +} + +static struct jit_code_entry * +gdb_jit_register_code( + const void *code_addr, + size_t code_size, + const char *symname, + const uint8_t *eh_frame, + size_t eh_frame_size +) +{ + /* + * Build a minimal in-memory ELF for GDB's JIT interface and link it into + * __jit_debug_descriptor so debuggers can resolve JIT code. + */ + if (code_addr == NULL || code_size == 0 || symname == NULL) { + return NULL; + } + + const uint16_t machine = gdb_jit_machine_id(); + if (machine == 0) { + return NULL; + } + + enum { + SH_NULL = 0, + SH_TEXT, + SH_EH_FRAME, + SH_SHSTRTAB, + SH_STRTAB, + SH_SYMTAB, + SH_NUM, + }; + static const char shstrtab[] = + "\0.text\0.eh_frame\0.shstrtab\0.strtab\0.symtab"; + _Static_assert(sizeof(shstrtab) == + 1 + sizeof(".text") + sizeof(".eh_frame") + + sizeof(".shstrtab") + sizeof(".strtab") + sizeof(".symtab"), + "shstrtab size mismatch"); + const size_t shstrtab_size = sizeof(shstrtab); + const size_t sh_text = 1; + const size_t sh_eh_frame = sh_text + sizeof(".text"); + const size_t sh_shstrtab = sh_eh_frame + sizeof(".eh_frame"); + const size_t sh_strtab = sh_shstrtab + sizeof(".shstrtab"); + const size_t sh_symtab = sh_strtab + sizeof(".strtab"); + const size_t text_size = code_size; + const size_t text_padded = _Py_SIZE_ROUND_UP(text_size, 8); + const size_t strtab_size = 1 + strlen(symname) + 1; + const size_t symtab_size = 3 * sizeof(Elf64_Sym); + + size_t offset = sizeof(Elf64_Ehdr); + offset = _Py_SIZE_ROUND_UP(offset, 16); + const size_t text_off = offset; + const size_t eh_off = text_off + text_padded; + offset = eh_off + eh_frame_size; + const size_t shstr_off = offset; + offset += shstrtab_size; + const size_t str_off = offset; + offset += strtab_size; + /* Elf64_Sym requires 8-byte alignment for st_value/st_size. */ + offset = _Py_SIZE_ROUND_UP(offset, 8); + const size_t sym_off = offset; + offset += symtab_size; + offset = _Py_SIZE_ROUND_UP(offset, sizeof(Elf64_Shdr)); + const size_t sh_off = offset; + + const size_t shnum = SH_NUM; + const size_t total_size = sh_off + shnum * sizeof(Elf64_Shdr); + uint8_t *buf = (uint8_t *)PyMem_RawMalloc(total_size); + if (buf == NULL) { + return NULL; + } + memset(buf, 0, total_size); + + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)buf; + memcpy(ehdr->e_ident, ELFMAG, SELFMAG); + ehdr->e_ident[EI_CLASS] = ELFCLASS64; + ehdr->e_ident[EI_DATA] = ELFDATA2LSB; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; + ehdr->e_type = ET_DYN; + ehdr->e_machine = machine; + ehdr->e_version = EV_CURRENT; + ehdr->e_entry = 0; + ehdr->e_phoff = 0; + ehdr->e_shoff = sh_off; + ehdr->e_ehsize = sizeof(Elf64_Ehdr); + ehdr->e_shentsize = sizeof(Elf64_Shdr); + ehdr->e_shnum = shnum; + ehdr->e_shstrndx = SH_SHSTRTAB; + + memcpy(buf + text_off, code_addr, text_size); + memcpy(buf + eh_off, eh_frame, eh_frame_size); + + char *shstr = (char *)(buf + shstr_off); + memcpy(shstr, shstrtab, shstrtab_size); + + char *strtab = (char *)(buf + str_off); + strtab[0] = '\0'; + memcpy(strtab + 1, symname, strlen(symname)); + strtab[strtab_size - 1] = '\0'; + + Elf64_Sym *syms = (Elf64_Sym *)(buf + sym_off); + memset(syms, 0, symtab_size); + /* Section symbol for .text (local) */ + syms[1].st_info = ELF64_ST_INFO(STB_LOCAL, STT_SECTION); + syms[1].st_shndx = 1; + /* Function symbol */ + syms[2].st_name = 1; + syms[2].st_info = ELF64_ST_INFO(STB_GLOBAL, STT_FUNC); + syms[2].st_other = STV_DEFAULT; + syms[2].st_shndx = 1; + /* For ET_DYN/ET_EXEC, st_value is the absolute virtual address. */ + syms[2].st_value = (Elf64_Addr)(uintptr_t)code_addr; + syms[2].st_size = code_size; + + Elf64_Shdr *shdrs = (Elf64_Shdr *)(buf + sh_off); + memset(shdrs, 0, shnum * sizeof(Elf64_Shdr)); + + shdrs[SH_TEXT].sh_name = sh_text; + shdrs[SH_TEXT].sh_type = SHT_PROGBITS; + shdrs[SH_TEXT].sh_flags = SHF_ALLOC | SHF_EXECINSTR; + shdrs[SH_TEXT].sh_addr = (Elf64_Addr)(uintptr_t)code_addr; + shdrs[SH_TEXT].sh_offset = text_off; + shdrs[SH_TEXT].sh_size = text_size; + shdrs[SH_TEXT].sh_addralign = 16; + + shdrs[SH_EH_FRAME].sh_name = sh_eh_frame; + shdrs[SH_EH_FRAME].sh_type = SHT_PROGBITS; + shdrs[SH_EH_FRAME].sh_flags = SHF_ALLOC; + shdrs[SH_EH_FRAME].sh_addr = + (Elf64_Addr)((uintptr_t)code_addr + text_padded); + shdrs[SH_EH_FRAME].sh_offset = eh_off; + shdrs[SH_EH_FRAME].sh_size = eh_frame_size; + shdrs[SH_EH_FRAME].sh_addralign = 8; + + shdrs[SH_SHSTRTAB].sh_name = sh_shstrtab; + shdrs[SH_SHSTRTAB].sh_type = SHT_STRTAB; + shdrs[SH_SHSTRTAB].sh_offset = shstr_off; + shdrs[SH_SHSTRTAB].sh_size = shstrtab_size; + shdrs[SH_SHSTRTAB].sh_addralign = 1; + + shdrs[SH_STRTAB].sh_name = sh_strtab; + shdrs[SH_STRTAB].sh_type = SHT_STRTAB; + shdrs[SH_STRTAB].sh_offset = str_off; + shdrs[SH_STRTAB].sh_size = strtab_size; + shdrs[SH_STRTAB].sh_addralign = 1; + + shdrs[SH_SYMTAB].sh_name = sh_symtab; + shdrs[SH_SYMTAB].sh_type = SHT_SYMTAB; + shdrs[SH_SYMTAB].sh_offset = sym_off; + shdrs[SH_SYMTAB].sh_size = symtab_size; + shdrs[SH_SYMTAB].sh_link = SH_STRTAB; + shdrs[SH_SYMTAB].sh_info = 2; + shdrs[SH_SYMTAB].sh_addralign = 8; + shdrs[SH_SYMTAB].sh_entsize = sizeof(Elf64_Sym); + + struct jit_code_entry *entry = PyMem_RawMalloc(sizeof(*entry)); + if (entry == NULL) { + PyMem_RawFree(buf); + return NULL; + } + entry->symfile_addr = (const char *)buf; + entry->symfile_size = total_size; + entry->code_addr = code_addr; + + PyMutex_Lock(&_Py_jit_debug_mutex); + entry->prev = NULL; + entry->next = __jit_debug_descriptor.first_entry; + if (entry->next != NULL) { + entry->next->prev = entry; + } + __jit_debug_descriptor.first_entry = entry; + __jit_debug_descriptor.relevant_entry = entry; + __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; + __jit_debug_register_code(); + __jit_debug_descriptor.action_flag = JIT_NOACTION; + __jit_debug_descriptor.relevant_entry = NULL; + PyMutex_Unlock(&_Py_jit_debug_mutex); + return entry; +} +#endif // defined(PY_HAVE_JIT_GDB_UNWIND) + +void * +_PyJitUnwind_GdbRegisterCode(const void *code_addr, + size_t code_size, + const char *entry, + const char *filename) +{ +#if defined(PY_HAVE_JIT_GDB_UNWIND) + /* GDB expects a stable symbol name and absolute addresses in .eh_frame. */ + if (entry == NULL) { + entry = ""; + } + if (filename == NULL) { + filename = ""; + } + size_t name_size = snprintf(NULL, 0, "py::%s:%s", entry, filename) + 1; + char *name = (char *)PyMem_RawMalloc(name_size); + if (name == NULL) { + return NULL; + } + snprintf(name, name_size, "py::%s:%s", entry, filename); + + uint8_t buffer[1024]; + size_t eh_frame_size = _PyJitUnwind_BuildEhFrame( + buffer, sizeof(buffer), code_addr, code_size, 1); + if (eh_frame_size == 0) { + PyMem_RawFree(name); + return NULL; + } + + void *handle = gdb_jit_register_code(code_addr, code_size, name, + buffer, eh_frame_size); + PyMem_RawFree(name); + return handle; +#else + (void)code_addr; + (void)code_size; + (void)entry; + (void)filename; + return NULL; +#endif +} + +void +_PyJitUnwind_GdbUnregisterCode(void *handle) +{ +#if defined(PY_HAVE_JIT_GDB_UNWIND) + struct jit_code_entry *entry = (struct jit_code_entry *)handle; + if (entry == NULL) { + return; + } + + PyMutex_Lock(&_Py_jit_debug_mutex); + if (entry->prev != NULL) { + entry->prev->next = entry->next; + } + else { + __jit_debug_descriptor.first_entry = entry->next; + } + if (entry->next != NULL) { + entry->next->prev = entry->prev; + } + + __jit_debug_descriptor.relevant_entry = entry; + __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN; + __jit_debug_register_code(); + __jit_debug_descriptor.action_flag = JIT_NOACTION; + __jit_debug_descriptor.relevant_entry = NULL; + + PyMutex_Unlock(&_Py_jit_debug_mutex); + + PyMem_RawFree((void *)entry->symfile_addr); + PyMem_RawFree(entry); +#else + (void)handle; +#endif +} + +#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND) +void * +_PyJitUnwind_GnuBacktraceRegisterCode(const void *code_addr, size_t code_size) +{ + if (code_addr == NULL || code_size == 0) { + return NULL; + } + + size_t eh_frame_size = _PyJitUnwind_EhFrameSize(1); + if (eh_frame_size == 0) { + return NULL; + } + size_t total_size = eh_frame_size + sizeof(uint32_t); + if (total_size < eh_frame_size) { + return NULL; + } + + /* + * libgcc's __register_frame walks a .eh_frame section until it finds a + * zero-length terminator entry, so keep an extra zeroed word after the + * generated CIE/FDE pair. + * + * See GCC's libgcc/unwind-dw2-fde.c (__register_frame) and + * libgcc/unwind-dw2-fde.h (last_fde/next_fde): + * https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-dw2-fde.c + * https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-dw2-fde.h + */ + uint8_t *eh_frame = PyMem_RawCalloc(1, total_size); + if (eh_frame == NULL) { + return NULL; + } + if (_PyJitUnwind_BuildEhFrame( + eh_frame, eh_frame_size, code_addr, code_size, 1) == 0) { + PyMem_RawFree(eh_frame); + return NULL; + } + + __register_frame(eh_frame); + return eh_frame; +} + +void +_PyJitUnwind_GnuBacktraceUnregisterCode(void *handle) +{ + if (handle == NULL) { + return; + } + __deregister_frame(handle); + PyMem_RawFree(handle); +} +#endif // defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND) + +#endif // JIT unwind support diff --git a/Python/legacy_tracing.c b/Python/legacy_tracing.c index dbd19d7755c237..bf65a904de4d21 100644 --- a/Python/legacy_tracing.c +++ b/Python/legacy_tracing.c @@ -20,13 +20,6 @@ typedef struct _PyLegacyEventHandler { #define _PyLegacyEventHandler_CAST(op) ((_PyLegacyEventHandler *)(op)) -#ifdef Py_GIL_DISABLED -#define LOCK_SETUP() PyMutex_Lock(&_PyRuntime.ceval.sys_trace_profile_mutex); -#define UNLOCK_SETUP() PyMutex_Unlock(&_PyRuntime.ceval.sys_trace_profile_mutex); -#else -#define LOCK_SETUP() -#define UNLOCK_SETUP() -#endif /* The Py_tracefunc function expects the following arguments: * obj: the trace object (PyObject *) * frame: the current frame (PyFrameObject *) @@ -133,16 +126,10 @@ sys_profile_call_or_return( Py_RETURN_NONE; } -int -_PyEval_SetOpcodeTrace( - PyFrameObject *frame, - bool enable -) { - assert(frame != NULL); - - PyCodeObject *code = _PyFrame_GetCode(frame->f_frame); +static int +set_opcode_trace_world_stopped(PyCodeObject *code, bool enable) +{ _PyMonitoringEventSet events = 0; - if (_PyMonitoring_GetLocalEvents(code, PY_MONITORING_SYS_TRACE_ID, &events) < 0) { return -1; } @@ -161,6 +148,32 @@ _PyEval_SetOpcodeTrace( return _PyMonitoring_SetLocalEvents(code, PY_MONITORING_SYS_TRACE_ID, events); } +int +_PyEval_SetOpcodeTrace(PyFrameObject *frame, bool enable) +{ + assert(frame != NULL); + + PyCodeObject *code = _PyFrame_GetCode(frame->f_frame); + +#ifdef Py_GIL_DISABLED + // First check if a change is necessary outside of the stop-the-world pause + _PyMonitoringEventSet events = 0; + if (_PyMonitoring_GetLocalEvents(code, PY_MONITORING_SYS_TRACE_ID, &events) < 0) { + return -1; + } + int is_enabled = (events & (1 << PY_MONITORING_EVENT_INSTRUCTION)) != 0; + if (is_enabled == enable) { + return 0; // No change needed + } +#endif + + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); + int res = set_opcode_trace_world_stopped(code, enable); + _PyEval_StartTheWorld(interp); + return res; +} + static PyObject * call_trace_func(_PyLegacyEventHandler *self, PyObject *arg) { @@ -378,8 +391,8 @@ sys_trace_jump_func( assert(PyCode_Check(code)); /* We can call _Py_Instrumentation_GetLine because we always set * line events for tracing */ - int to_line = _Py_Instrumentation_GetLine(code, to); - int from_line = _Py_Instrumentation_GetLine(code, from); + int to_line = _Py_Instrumentation_GetLine(code, code->_co_monitoring->lines, to); + int from_line = _Py_Instrumentation_GetLine(code, code->_co_monitoring->lines, from); if (to_line != from_line) { /* Will be handled by target INSTRUMENTED_LINE */ return &_PyInstrumentation_DISABLE; @@ -438,59 +451,74 @@ is_tstate_valid(PyThreadState *tstate) } #endif -static Py_ssize_t -setup_profile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg, PyObject **old_profileobj) +static int +setup_profile_callbacks(void *Py_UNUSED(arg)) { - *old_profileobj = NULL; /* Setup PEP 669 monitoring callbacks and events. */ - if (!tstate->interp->sys_profile_initialized) { - tstate->interp->sys_profile_initialized = true; - if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, - sys_profile_start, PyTrace_CALL, - PY_MONITORING_EVENT_PY_START, - PY_MONITORING_EVENT_PY_RESUME)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, - sys_profile_throw, PyTrace_CALL, - PY_MONITORING_EVENT_PY_THROW, -1)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, - sys_profile_return, PyTrace_RETURN, - PY_MONITORING_EVENT_PY_RETURN, - PY_MONITORING_EVENT_PY_YIELD)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, - sys_profile_unwind, PyTrace_RETURN, - PY_MONITORING_EVENT_PY_UNWIND, -1)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, - sys_profile_call_or_return, PyTrace_C_CALL, - PY_MONITORING_EVENT_CALL, -1)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, - sys_profile_call_or_return, PyTrace_C_RETURN, - PY_MONITORING_EVENT_C_RETURN, -1)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, - sys_profile_call_or_return, PyTrace_C_EXCEPTION, - PY_MONITORING_EVENT_C_RAISE, -1)) { - return -1; - } + if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, + sys_profile_start, PyTrace_CALL, + PY_MONITORING_EVENT_PY_START, + PY_MONITORING_EVENT_PY_RESUME)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, + sys_profile_throw, PyTrace_CALL, + PY_MONITORING_EVENT_PY_THROW, -1)) { + return -1; } + if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, + sys_profile_return, PyTrace_RETURN, + PY_MONITORING_EVENT_PY_RETURN, + PY_MONITORING_EVENT_PY_YIELD)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, + sys_profile_unwind, PyTrace_RETURN, + PY_MONITORING_EVENT_PY_UNWIND, -1)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, + sys_profile_call_or_return, PyTrace_C_CALL, + PY_MONITORING_EVENT_CALL, -1)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, + sys_profile_call_or_return, PyTrace_C_RETURN, + PY_MONITORING_EVENT_C_RETURN, -1)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_PROFILE_ID, + sys_profile_call_or_return, PyTrace_C_EXCEPTION, + PY_MONITORING_EVENT_C_RAISE, -1)) { + return -1; + } + return 0; +} +static PyObject * +swap_profile_func_arg(PyThreadState *tstate, Py_tracefunc func, PyObject *arg) +{ int delta = (func != NULL) - (tstate->c_profilefunc != NULL); tstate->c_profilefunc = func; - *old_profileobj = tstate->c_profileobj; + PyObject *old_profileobj = tstate->c_profileobj; tstate->c_profileobj = Py_XNewRef(arg); tstate->interp->sys_profiling_threads += delta; assert(tstate->interp->sys_profiling_threads >= 0); - return tstate->interp->sys_profiling_threads; + return old_profileobj; +} + +static int +set_monitoring_profile_events(PyInterpreterState *interp) +{ + uint32_t events = 0; + if (interp->sys_profiling_threads) { + events = + (1 << PY_MONITORING_EVENT_PY_START) | (1 << PY_MONITORING_EVENT_PY_RESUME) | + (1 << PY_MONITORING_EVENT_PY_RETURN) | (1 << PY_MONITORING_EVENT_PY_YIELD) | + (1 << PY_MONITORING_EVENT_CALL) | (1 << PY_MONITORING_EVENT_PY_UNWIND) | + (1 << PY_MONITORING_EVENT_PY_THROW); + } + return _PyMonitoring_SetEvents(PY_MONITORING_SYS_PROFILE_ID, events); } int @@ -507,87 +535,155 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg) return -1; } - // needs to be decref'd outside of the lock - PyObject *old_profileobj; - LOCK_SETUP(); - Py_ssize_t profiling_threads = setup_profile(tstate, func, arg, &old_profileobj); - UNLOCK_SETUP(); - Py_XDECREF(old_profileobj); + PyInterpreterState *interp = tstate->interp; + if (_PyOnceFlag_CallOnce(&interp->sys_profile_once_flag, + setup_profile_callbacks, NULL) < 0) { + return -1; + } - uint32_t events = 0; - if (profiling_threads) { - events = - (1 << PY_MONITORING_EVENT_PY_START) | (1 << PY_MONITORING_EVENT_PY_RESUME) | - (1 << PY_MONITORING_EVENT_PY_RETURN) | (1 << PY_MONITORING_EVENT_PY_YIELD) | - (1 << PY_MONITORING_EVENT_CALL) | (1 << PY_MONITORING_EVENT_PY_UNWIND) | - (1 << PY_MONITORING_EVENT_PY_THROW); + _PyEval_StopTheWorld(interp); + PyObject *old_profileobj = swap_profile_func_arg(tstate, func, arg); + int ret = set_monitoring_profile_events(interp); + _PyEval_StartTheWorld(interp); + Py_XDECREF(old_profileobj); // needs to be decref'd outside of stop-the-world + return ret; +} + +int +_PyEval_SetProfileAllThreads(PyInterpreterState *interp, Py_tracefunc func, PyObject *arg) +{ + PyThreadState *current_tstate = _PyThreadState_GET(); + assert(is_tstate_valid(current_tstate)); + assert(current_tstate->interp == interp); + + if (_PySys_Audit(current_tstate, "sys.setprofile", NULL) < 0) { + return -1; } - return _PyMonitoring_SetEvents(PY_MONITORING_SYS_PROFILE_ID, events); + + if (_PyOnceFlag_CallOnce(&interp->sys_profile_once_flag, + setup_profile_callbacks, NULL) < 0) { + return -1; + } + + PyObject *old_profileobjs = NULL; + _PyEval_StopTheWorld(interp); + HEAD_LOCK(&_PyRuntime); + Py_ssize_t num_thread_states = 0; + _Py_FOR_EACH_TSTATE_UNLOCKED(interp, p) { + num_thread_states++; + } + old_profileobjs = PyTuple_New(num_thread_states); + if (old_profileobjs == NULL) { + HEAD_UNLOCK(&_PyRuntime); + _PyEval_StartTheWorld(interp); + return -1; + } + _Py_FOR_EACH_TSTATE_UNLOCKED(interp, tstate) { + PyObject *old = swap_profile_func_arg(tstate, func, arg); + PyTuple_SET_ITEM(old_profileobjs, --num_thread_states, old); + } + HEAD_UNLOCK(&_PyRuntime); + int ret = set_monitoring_profile_events(interp); + _PyEval_StartTheWorld(interp); + Py_XDECREF(old_profileobjs); // needs to be decref'd outside of stop-the-world + return ret; } -static Py_ssize_t -setup_tracing(PyThreadState *tstate, Py_tracefunc func, PyObject *arg, PyObject **old_traceobj) +static int +setup_trace_callbacks(void *Py_UNUSED(arg)) { - *old_traceobj = NULL; /* Setup PEP 669 monitoring callbacks and events. */ - if (!tstate->interp->sys_trace_initialized) { - tstate->interp->sys_trace_initialized = true; - if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, - sys_trace_start, PyTrace_CALL, - PY_MONITORING_EVENT_PY_START, - PY_MONITORING_EVENT_PY_RESUME)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, - sys_trace_throw, PyTrace_CALL, - PY_MONITORING_EVENT_PY_THROW, -1)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, - sys_trace_return, PyTrace_RETURN, - PY_MONITORING_EVENT_PY_RETURN, -1)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, - sys_trace_yield, PyTrace_RETURN, - PY_MONITORING_EVENT_PY_YIELD, -1)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, - sys_trace_exception_func, PyTrace_EXCEPTION, - PY_MONITORING_EVENT_RAISE, - PY_MONITORING_EVENT_STOP_ITERATION)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, - sys_trace_line_func, PyTrace_LINE, - PY_MONITORING_EVENT_LINE, -1)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, - sys_trace_unwind, PyTrace_RETURN, - PY_MONITORING_EVENT_PY_UNWIND, -1)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, - sys_trace_jump_func, PyTrace_LINE, - PY_MONITORING_EVENT_JUMP, -1)) { - return -1; - } - if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, - sys_trace_instruction_func, PyTrace_OPCODE, - PY_MONITORING_EVENT_INSTRUCTION, -1)) { - return -1; - } + if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, + sys_trace_start, PyTrace_CALL, + PY_MONITORING_EVENT_PY_START, + PY_MONITORING_EVENT_PY_RESUME)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, + sys_trace_throw, PyTrace_CALL, + PY_MONITORING_EVENT_PY_THROW, -1)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, + sys_trace_return, PyTrace_RETURN, + PY_MONITORING_EVENT_PY_RETURN, -1)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, + sys_trace_yield, PyTrace_RETURN, + PY_MONITORING_EVENT_PY_YIELD, -1)) { + return -1; } + if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, + sys_trace_exception_func, PyTrace_EXCEPTION, + PY_MONITORING_EVENT_RAISE, + PY_MONITORING_EVENT_STOP_ITERATION)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, + sys_trace_line_func, PyTrace_LINE, + PY_MONITORING_EVENT_LINE, -1)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, + sys_trace_unwind, PyTrace_RETURN, + PY_MONITORING_EVENT_PY_UNWIND, -1)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, + sys_trace_jump_func, PyTrace_LINE, + PY_MONITORING_EVENT_JUMP, -1)) { + return -1; + } + if (set_callbacks(PY_MONITORING_SYS_TRACE_ID, + sys_trace_instruction_func, PyTrace_OPCODE, + PY_MONITORING_EVENT_INSTRUCTION, -1)) { + return -1; + } + return 0; +} +static PyObject * +swap_trace_func_arg(PyThreadState *tstate, Py_tracefunc func, PyObject *arg) +{ int delta = (func != NULL) - (tstate->c_tracefunc != NULL); tstate->c_tracefunc = func; - *old_traceobj = tstate->c_traceobj; + PyObject *old_traceobj = tstate->c_traceobj; tstate->c_traceobj = Py_XNewRef(arg); tstate->interp->sys_tracing_threads += delta; assert(tstate->interp->sys_tracing_threads >= 0); - return tstate->interp->sys_tracing_threads; + return old_traceobj; +} + +static int +set_monitoring_trace_events(PyInterpreterState *interp) +{ + uint32_t events = 0; + if (interp->sys_tracing_threads) { + events = + (1 << PY_MONITORING_EVENT_PY_START) | (1 << PY_MONITORING_EVENT_PY_RESUME) | + (1 << PY_MONITORING_EVENT_PY_RETURN) | (1 << PY_MONITORING_EVENT_PY_YIELD) | + (1 << PY_MONITORING_EVENT_RAISE) | (1 << PY_MONITORING_EVENT_LINE) | + (1 << PY_MONITORING_EVENT_JUMP) | + (1 << PY_MONITORING_EVENT_PY_UNWIND) | (1 << PY_MONITORING_EVENT_PY_THROW) | + (1 << PY_MONITORING_EVENT_STOP_ITERATION); + } + return _PyMonitoring_SetEvents(PY_MONITORING_SYS_TRACE_ID, events); +} + +// Enable opcode tracing for the thread's current frame if needed. +static int +maybe_set_opcode_trace(PyThreadState *tstate) +{ + _PyInterpreterFrame *iframe = tstate->current_frame; + if (iframe == NULL) { + return 0; + } + PyFrameObject *frame = iframe->frame_obj; + if (frame == NULL || !frame->f_trace_opcodes) { + return 0; + } + return set_opcode_trace_world_stopped(_PyFrame_GetCode(iframe), true); } int @@ -603,35 +699,76 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg) if (_PySys_Audit(current_tstate, "sys.settrace", NULL) < 0) { return -1; } - // needs to be decref'd outside of the lock - PyObject *old_traceobj; - LOCK_SETUP(); - assert(tstate->interp->sys_tracing_threads >= 0); - Py_ssize_t tracing_threads = setup_tracing(tstate, func, arg, &old_traceobj); - UNLOCK_SETUP(); - Py_XDECREF(old_traceobj); - if (tracing_threads < 0) { + + PyInterpreterState *interp = tstate->interp; + if (_PyOnceFlag_CallOnce(&interp->sys_trace_once_flag, + setup_trace_callbacks, NULL) < 0) { return -1; } - uint32_t events = 0; - if (tracing_threads) { - events = - (1 << PY_MONITORING_EVENT_PY_START) | (1 << PY_MONITORING_EVENT_PY_RESUME) | - (1 << PY_MONITORING_EVENT_PY_RETURN) | (1 << PY_MONITORING_EVENT_PY_YIELD) | - (1 << PY_MONITORING_EVENT_RAISE) | (1 << PY_MONITORING_EVENT_LINE) | - (1 << PY_MONITORING_EVENT_JUMP) | - (1 << PY_MONITORING_EVENT_PY_UNWIND) | (1 << PY_MONITORING_EVENT_PY_THROW) | - (1 << PY_MONITORING_EVENT_STOP_ITERATION); + int err = 0; + _PyEval_StopTheWorld(interp); + PyObject *old_traceobj = swap_trace_func_arg(tstate, func, arg); + err = set_monitoring_trace_events(interp); + if (err != 0) { + goto done; + } + if (interp->sys_tracing_threads) { + err = maybe_set_opcode_trace(tstate); + } +done: + _PyEval_StartTheWorld(interp); + Py_XDECREF(old_traceobj); // needs to be decref'd outside stop-the-world + return err; +} - PyFrameObject* frame = PyEval_GetFrame(); - if (frame && frame->f_trace_opcodes) { - int ret = _PyEval_SetOpcodeTrace(frame, true); - if (ret != 0) { - return ret; +int +_PyEval_SetTraceAllThreads(PyInterpreterState *interp, Py_tracefunc func, PyObject *arg) +{ + PyThreadState *current_tstate = _PyThreadState_GET(); + assert(is_tstate_valid(current_tstate)); + assert(current_tstate->interp == interp); + + if (_PySys_Audit(current_tstate, "sys.settrace", NULL) < 0) { + return -1; + } + + if (_PyOnceFlag_CallOnce(&interp->sys_trace_once_flag, + setup_trace_callbacks, NULL) < 0) { + return -1; + } + + PyObject *old_trace_objs = NULL; + _PyEval_StopTheWorld(interp); + HEAD_LOCK(&_PyRuntime); + Py_ssize_t num_thread_states = 0; + _Py_FOR_EACH_TSTATE_UNLOCKED(interp, p) { + num_thread_states++; + } + old_trace_objs = PyTuple_New(num_thread_states); + if (old_trace_objs == NULL) { + HEAD_UNLOCK(&_PyRuntime); + _PyEval_StartTheWorld(interp); + return -1; + } + _Py_FOR_EACH_TSTATE_UNLOCKED(interp, tstate) { + PyObject *old = swap_trace_func_arg(tstate, func, arg); + PyTuple_SET_ITEM(old_trace_objs, --num_thread_states, old); + } + if (interp->sys_tracing_threads) { + _Py_FOR_EACH_TSTATE_UNLOCKED(interp, tstate) { + int err = maybe_set_opcode_trace(tstate); + if (err != 0) { + HEAD_UNLOCK(&_PyRuntime); + _PyEval_StartTheWorld(interp); + Py_XDECREF(old_trace_objs); + return -1; } } } - - return _PyMonitoring_SetEvents(PY_MONITORING_SYS_TRACE_ID, events); + HEAD_UNLOCK(&_PyRuntime); + int err = set_monitoring_trace_events(interp); + _PyEval_StartTheWorld(interp); + Py_XDECREF(old_trace_objs); // needs to be decref'd outside of stop-the-world + return err; } diff --git a/Python/lock.c b/Python/lock.c index a49d587a1686d9..af136fefd299d3 100644 --- a/Python/lock.c +++ b/Python/lock.c @@ -6,6 +6,7 @@ #include "pycore_parking_lot.h" #include "pycore_semaphore.h" #include "pycore_time.h" // _PyTime_Add() +#include "pycore_stats.h" // FT_STAT_MUTEX_SLEEP_INC() #ifdef MS_WINDOWS # ifndef WIN32_LEAN_AND_MEAN @@ -26,8 +27,10 @@ static const PyTime_t TIME_TO_BE_FAIR_NS = 1000*1000; // enabled. #if Py_GIL_DISABLED static const int MAX_SPIN_COUNT = 40; +static const int RELOAD_SPIN_MASK = 3; #else static const int MAX_SPIN_COUNT = 0; +static const int RELOAD_SPIN_MASK = 1; #endif struct mutex_entry { @@ -39,7 +42,7 @@ struct mutex_entry { int handed_off; }; -static void +void _Py_yield(void) { #ifdef MS_WINDOWS @@ -62,6 +65,8 @@ _PyMutex_LockTimed(PyMutex *m, PyTime_t timeout, _PyLockFlags flags) return PY_LOCK_FAILURE; } + FT_STAT_MUTEX_SLEEP_INC(); + PyTime_t now; // silently ignore error: cannot report error to the caller (void)PyTime_MonotonicRaw(&now); @@ -76,6 +81,16 @@ _PyMutex_LockTimed(PyMutex *m, PyTime_t timeout, _PyLockFlags flags) }; Py_ssize_t spin_count = 0; +#ifdef Py_GIL_DISABLED + // Using thread-id as a way of reducing contention further in the reload below. + // It adds a pseudo-random starting offset to the recurrence, so that threads + // are less likely to try and run compare-exchange at the same time. + // The lower bits of platform thread ids are likely to not be random, + // hence the right shift. + const Py_ssize_t tid = (Py_ssize_t)(_Py_ThreadId() >> 12); +#else + const Py_ssize_t tid = 0; +#endif for (;;) { if ((v & _Py_LOCKED) == 0) { // The lock is unlocked. Try to grab it. @@ -89,6 +104,9 @@ _PyMutex_LockTimed(PyMutex *m, PyTime_t timeout, _PyLockFlags flags) // Spin for a bit. _Py_yield(); spin_count++; + if (((spin_count + tid) & RELOAD_SPIN_MASK) == 0) { + v = _Py_atomic_load_uint8_relaxed(&m->_bits); + } continue; } @@ -121,8 +139,11 @@ _PyMutex_LockTimed(PyMutex *m, PyTime_t timeout, _PyLockFlags flags) &entry, (flags & _PY_LOCK_DETACH) != 0); if (ret == Py_PARK_OK) { if (entry.handed_off) { - // We own the lock now. - assert(_Py_atomic_load_uint8_relaxed(&m->_bits) & _Py_LOCKED); + // We own the lock now. thread.Lock allows other threads + // to concurrently release the lock so we cannot assert that + // it is locked if _PY_LOCK_PYTHONLOCK is set. + assert(_Py_atomic_load_uint8_relaxed(&m->_bits) & _Py_LOCKED || + (flags & _PY_LOCK_PYTHONLOCK) != 0); return PY_LOCK_ACQUIRED; } } @@ -227,7 +248,16 @@ _PyRawMutex_LockSlow(_PyRawMutex *m) // Wait for us to be woken up. Note that we still have to lock the // mutex ourselves: it is NOT handed off to us. - _PySemaphore_Wait(&waiter.sema, -1, /*detach=*/0); + // + // Loop until we observe an actual wakeup. A return of Py_PARK_INTR + // could otherwise let us exit _PySemaphore_Wait and destroy + // `waiter.sema` while _PyRawMutex_UnlockSlow's matching + // _PySemaphore_Wakeup is still pending, since the unlocker has + // already CAS-removed us from the waiter list without any handshake. + int res; + do { + res = _PySemaphore_Wait(&waiter.sema, -1); + } while (res != Py_PARK_OK); } _PySemaphore_Destroy(&waiter.sema); diff --git a/Python/marshal.c b/Python/marshal.c index 15dd25d6268df4..9688d426419c2f 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -11,8 +11,10 @@ #include "pycore_code.h" // _PyCode_New() #include "pycore_hashtable.h" // _Py_hashtable_t #include "pycore_long.h" // _PyLong_IsZero() +#include "pycore_object.h" // _PyObject_IsUniquelyReferenced #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_setobject.h" // _PySet_NextEntryRef() +#include "pycore_tuple.h" // _PyTuple_FromPairSteal #include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal() #include "marshal.h" // Py_MARSHAL_VERSION @@ -66,6 +68,7 @@ module marshal #define TYPE_TUPLE '(' // See also TYPE_SMALL_TUPLE. #define TYPE_LIST '[' #define TYPE_DICT '{' +#define TYPE_FROZENDICT '}' #define TYPE_CODE 'c' #define TYPE_UNICODE 'u' #define TYPE_UNKNOWN '?' @@ -309,7 +312,7 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p) } if (!long_export.digits) { int8_t sign = long_export.value < 0 ? -1 : 1; - uint64_t abs_value = Py_ABS(long_export.value); + uint64_t abs_value = _Py_ABS_CAST(uint64_t, long_export.value); uint64_t d = abs_value; long l = 0; @@ -379,7 +382,6 @@ static int w_ref(PyObject *v, char *flag, WFILE *p) { _Py_hashtable_entry_t *entry; - int w; if (p->version < 3 || p->hashtable == NULL) return 0; /* not writing object references */ @@ -388,7 +390,7 @@ w_ref(PyObject *v, char *flag, WFILE *p) * But we use TYPE_REF always for interned string, to PYC file stable * as possible. */ - if (Py_REFCNT(v) == 1 && + if (_PyObject_IsUniquelyReferenced(v) && !(PyUnicode_CheckExact(v) && PyUnicode_CHECK_INTERNED(v))) { return 0; } @@ -396,20 +398,28 @@ w_ref(PyObject *v, char *flag, WFILE *p) entry = _Py_hashtable_get_entry(p->hashtable, v); if (entry != NULL) { /* write the reference index to the stream */ - w = (int)(uintptr_t)entry->value; + uintptr_t w = (uintptr_t)entry->value; + if (w & 0x80000000LU) { + PyErr_Format(PyExc_ValueError, "cannot marshal recursion %T objects", v); + goto err; + } /* we don't store "long" indices in the dict */ - assert(0 <= w && w <= 0x7fffffff); + assert(w <= 0x7fffffff); w_byte(TYPE_REF, p); - w_long(w, p); + w_long((int)w, p); return 1; } else { - size_t s = p->hashtable->nentries; + size_t w = p->hashtable->nentries; /* we don't support long indices */ - if (s >= 0x7fffffff) { + if (w >= 0x7fffffff) { PyErr_SetString(PyExc_ValueError, "too many objects"); goto err; } - w = (int)s; + // Corresponding code should call w_complete() after + // writing the object. + if (PyCode_Check(v) || PySlice_Check(v) || PyFrozenDict_CheckExact(v)) { + w |= 0x80000000LU; + } if (_Py_hashtable_set(p->hashtable, Py_NewRef(v), (void *)(uintptr_t)w) < 0) { Py_DECREF(v); @@ -423,6 +433,27 @@ w_ref(PyObject *v, char *flag, WFILE *p) return 1; } +static void +w_complete(PyObject *v, WFILE *p) +{ + if (p->version < 3 || p->hashtable == NULL) { + return; + } + if (_PyObject_IsUniquelyReferenced(v)) { + return; + } + + _Py_hashtable_entry_t *entry = _Py_hashtable_get_entry(p->hashtable, v); + if (entry == NULL) { + return; + } + assert(entry != NULL); + uintptr_t w = (uintptr_t)entry->value; + assert(w & 0x80000000LU); + w &= ~0x80000000LU; + entry->value = (void *)(uintptr_t)w; +} + static void w_complex_object(PyObject *v, char flag, WFILE *p); @@ -431,6 +462,10 @@ w_object(PyObject *v, WFILE *p) { char flag = '\0'; + if (p->error != WFERR_OK) { + return; + } + p->depth++; if (p->depth > MAX_MARSHAL_STACK_DEPTH) { @@ -570,10 +605,21 @@ w_complex_object(PyObject *v, char flag, WFILE *p) w_object(PyList_GET_ITEM(v, i), p); } } - else if (PyDict_CheckExact(v)) { + else if (PyAnyDict_CheckExact(v)) { Py_ssize_t pos; PyObject *key, *value; - W_TYPE(TYPE_DICT, p); + if (PyFrozenDict_CheckExact(v)) { + if (p->version < 6) { + w_byte(TYPE_UNKNOWN, p); + p->error = WFERR_UNMARSHALLABLE; + return; + } + + W_TYPE(TYPE_FROZENDICT, p); + } + else { + W_TYPE(TYPE_DICT, p); + } /* This one is NULL object terminated! */ pos = 0; while (PyDict_Next(v, &pos, &key, &value)) { @@ -581,6 +627,9 @@ w_complex_object(PyObject *v, char flag, WFILE *p) w_object(value, p); } w_object((PyObject *)NULL, p); + if (PyFrozenDict_CheckExact(v)) { + w_complete(v, p); + } } else if (PyAnySet_CheckExact(v)) { PyObject *value; @@ -612,9 +661,7 @@ w_complex_object(PyObject *v, char flag, WFILE *p) Py_DECREF(value); break; } - PyObject *pair = PyTuple_Pack(2, dump, value); - Py_DECREF(dump); - Py_DECREF(value); + PyObject *pair = _PyTuple_FromPairSteal(dump, value); if (pair == NULL) { p->error = WFERR_NOMEMORY; break; @@ -668,6 +715,7 @@ w_complex_object(PyObject *v, char flag, WFILE *p) w_object(co->co_linetable, p); w_object(co->co_exceptiontable, p); Py_DECREF(co_code); + w_complete(v, p); } else if (PyObject_CheckBuffer(v)) { /* Write unknown bytes-like objects as a bytes object */ @@ -693,6 +741,7 @@ w_complex_object(PyObject *v, char flag, WFILE *p) w_object(slice->start, p); w_object(slice->stop, p); w_object(slice->step, p); + w_complete(v, p); } else { W_TYPE(TYPE_UNKNOWN, p); @@ -1415,10 +1464,21 @@ r_object(RFILE *p) break; case TYPE_DICT: + case TYPE_FROZENDICT: v = PyDict_New(); - R_REF(v); - if (v == NULL) + if (v == NULL) { break; + } + if (type == TYPE_DICT) { + R_REF(v); + } + else { + idx = r_ref_reserve(flag, p); + if (idx < 0) { + Py_CLEAR(v); + break; + } + } for (;;) { PyObject *key, *val; key = r_object(p); @@ -1438,7 +1498,10 @@ r_object(RFILE *p) Py_DECREF(val); } if (PyErr_Occurred()) { - Py_SETREF(v, NULL); + Py_CLEAR(v); + } + if (type == TYPE_FROZENDICT && v != NULL) { + Py_SETREF(v, PyFrozenDict_New(v)); } retval = v; break; @@ -1573,7 +1636,7 @@ r_object(RFILE *p) goto code_error; firstlineno = (int)r_long(p); if (firstlineno == -1 && PyErr_Occurred()) - break; + goto code_error; linetable = r_object(p); if (linetable == NULL) goto code_error; @@ -1997,6 +2060,7 @@ marshal_load_impl(PyObject *module, PyObject *file, int allow_code) } /*[clinic input] +@permit_long_summary marshal.dumps value: object @@ -2010,14 +2074,14 @@ marshal.dumps Return the bytes object that would be written to a file by dump(value, file). -Raise a ValueError exception if value has (or contains an object that has) an -unsupported type. +Raise a ValueError exception if value has (or contains an object that +has) an unsupported type. [clinic start generated code]*/ static PyObject * marshal_dumps_impl(PyObject *module, PyObject *value, int version, int allow_code) -/*[clinic end generated code: output=115f90da518d1d49 input=167eaecceb63f0a8]*/ +/*[clinic end generated code: output=115f90da518d1d49 input=dc1edcafd43124c5]*/ { return _PyMarshal_WriteObjectToString(value, version, allow_code); } @@ -2033,13 +2097,13 @@ marshal.loads Convert the bytes-like object to a value. -If no valid value is found, raise EOFError, ValueError or TypeError. Extra -bytes in the input are ignored. +If no valid value is found, raise EOFError, ValueError or TypeError. +Extra bytes in the input are ignored. [clinic start generated code]*/ static PyObject * marshal_loads_impl(PyObject *module, Py_buffer *bytes, int allow_code) -/*[clinic end generated code: output=62c0c538d3edc31f input=14de68965b45aaa7]*/ +/*[clinic end generated code: output=62c0c538d3edc31f input=286f1dbd6811d2ad]*/ { RFILE rf; char *s = bytes->buf; @@ -2107,6 +2171,7 @@ marshal_module_exec(PyObject *mod) } static PyModuleDef_Slot marshalmodule_slots[] = { + _Py_ABI_SLOT, {Py_mod_exec, marshal_module_exec}, {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, {Py_mod_gil, Py_MOD_GIL_NOT_USED}, diff --git a/Python/modsupport.c b/Python/modsupport.c index 437ad412027e28..bab21d1b2be5b5 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -4,6 +4,7 @@ #include "Python.h" #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_object.h" // _PyType_IsReady() +#include "pycore_unicodeobject.h" // _PyUnicodeWriter_FormatV() typedef double va_double; @@ -33,6 +34,19 @@ _Py_convert_optional_to_ssize_t(PyObject *obj, void *result) return 1; } +int +_Py_convert_optional_to_non_negative_ssize_t(PyObject *obj, void *result) +{ + if (!_Py_convert_optional_to_ssize_t(obj, result)) { + return 0; + } + if (obj != Py_None && *((Py_ssize_t *)result) < 0) { + PyErr_SetString(PyExc_ValueError, "argument cannot be negative"); + return 0; + } + return 1; +} + /* Helper for mkvalue() to scan the length of a format */ @@ -655,6 +669,119 @@ PyModule_AddType(PyObject *module, PyTypeObject *type) return PyModule_AddObjectRef(module, name, (PyObject *)type); } +static int _abiinfo_raise(const char *module_name, const char *format, ...) +{ + PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); + if (!writer) { + return -1; + } + if (module_name) { + if (PyUnicodeWriter_WriteUTF8(writer, module_name, -1) < 0) { + PyUnicodeWriter_Discard(writer); + return -1; + } + if (PyUnicodeWriter_WriteASCII(writer, ": ", 2) < 0) { + PyUnicodeWriter_Discard(writer); + return -1; + } + } + va_list vargs; + va_start(vargs, format); + if (_PyUnicodeWriter_FormatV(writer, format, vargs) < 0) { + va_end(vargs); + PyUnicodeWriter_Discard(writer); + return -1; + } + + va_end(vargs); + PyObject *message = PyUnicodeWriter_Finish(writer); + if (!message) { + return -1; + } + PyErr_SetObject(PyExc_ImportError, message); + Py_DECREF(message); + return -1; +} + +int PyABIInfo_Check(PyABIInfo *info, const char *module_name) +{ + if (!info) { + return _abiinfo_raise(module_name, "NULL PyABIInfo"); + } + + /* abiinfo_major_version */ + if (info->abiinfo_major_version == 0) { + return 0; + } + if (info->abiinfo_major_version > 1) { + return _abiinfo_raise(module_name, "PyABIInfo version too high"); + } + + /* Internal ABI */ + if (info->flags & PyABIInfo_INTERNAL) { + if (info->abi_version && (info->abi_version != PY_VERSION_HEX)) { + return _abiinfo_raise( + module_name, + "incompatible internal ABI (0x%x != 0x%x)", + info->abi_version, PY_VERSION_HEX); + } + } + +#define XY_MASK 0xffff0000 + if (info->flags & PyABIInfo_STABLE) { + /* Greater-than major.minor version check */ + if (info->abi_version) { + if ((info->abi_version & XY_MASK) > (PY_VERSION_HEX & XY_MASK)) { + return _abiinfo_raise( + module_name, + "incompatible future stable ABI version (%d.%d)", + ((info->abi_version) >> 24) & 0xff, + ((info->abi_version) >> 16) & 0xff); + } + if (info->abi_version < Py_PACK_VERSION(3, 2)) { + return _abiinfo_raise( + module_name, + "invalid stable ABI version (%d.%d)", + ((info->abi_version) >> 24) & 0xff, + ((info->abi_version) >> 16) & 0xff); + } + } + if (info->flags & PyABIInfo_INTERNAL) { + return _abiinfo_raise(module_name, + "cannot use both internal and stable ABI"); + } + } + else { + /* Exact major.minor version check */ + if (info->abi_version) { + if ((info->abi_version & XY_MASK) != (PY_VERSION_HEX & XY_MASK)) { + return _abiinfo_raise( + module_name, + "incompatible ABI version (%d.%d)", + ((info->abi_version) >> 24) & 0xff, + ((info->abi_version) >> 16) & 0xff); + } + } + } +#undef XY_MASK + + /* Free-threading/GIL */ + uint16_t gilflags = info->flags & (PyABIInfo_GIL | PyABIInfo_FREETHREADED); +#if Py_GIL_DISABLED + if (gilflags == PyABIInfo_GIL) { + return _abiinfo_raise(module_name, + "incompatible with free-threaded CPython"); + } +#else + if (gilflags == PyABIInfo_FREETHREADED) { + return _abiinfo_raise(module_name, + "only compatible with free-threaded CPython"); + } +#endif + + return 0; +} + /* Exported functions for version helper macros */ diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 1d6dcddab4b12d..1a7eb9169fc837 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -1,5 +1,5 @@ -#if !Py_TAIL_CALL_INTERP -static void *opcode_targets[256] = { +#if !_Py_TAIL_CALL_INTERP +static void *opcode_targets_table[256] = { &&TARGET_CACHE, &&TARGET_BINARY_SLICE, &&TARGET_BUILD_TEMPLATE, @@ -16,10 +16,8 @@ static void *opcode_targets[256] = { &&TARGET_FORMAT_WITH_SPEC, &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, - &&TARGET_GET_ITER, - &&TARGET_RESERVED, &&TARGET_GET_LEN, - &&TARGET_GET_YIELD_FROM_ITER, + &&TARGET_RESERVED, &&TARGET_INTERPRETER_EXIT, &&TARGET_LOAD_BUILD_CLASS, &&TARGET_LOAD_LOCALS, @@ -72,6 +70,7 @@ static void *opcode_targets[256] = { &&TARGET_EXTENDED_ARG, &&TARGET_FOR_ITER, &&TARGET_GET_AWAITABLE, + &&TARGET_GET_ITER, &&TARGET_IMPORT_FROM, &&TARGET_IMPORT_NAME, &&TARGET_IS_OP, @@ -128,6 +127,7 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, + &&_unknown_opcode, &&TARGET_RESUME, &&TARGET_BINARY_OP_ADD_FLOAT, &&TARGET_BINARY_OP_ADD_INT, @@ -141,6 +141,7 @@ static void *opcode_targets[256] = { &&TARGET_BINARY_OP_SUBSCR_LIST_SLICE, &&TARGET_BINARY_OP_SUBSCR_STR_INT, &&TARGET_BINARY_OP_SUBSCR_TUPLE_INT, + &&TARGET_BINARY_OP_SUBSCR_USTR_INT, &&TARGET_BINARY_OP_SUBTRACT_FLOAT, &&TARGET_BINARY_OP_SUBTRACT_INT, &&TARGET_CALL_ALLOC_AND_ENTER_INIT, @@ -150,6 +151,8 @@ static void *opcode_targets[256] = { &&TARGET_CALL_BUILTIN_FAST, &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS, &&TARGET_CALL_BUILTIN_O, + &&TARGET_CALL_EX_NON_PY_GENERAL, + &&TARGET_CALL_EX_PY, &&TARGET_CALL_ISINSTANCE, &&TARGET_CALL_KW_BOUND_METHOD, &&TARGET_CALL_KW_NON_PY, @@ -175,6 +178,9 @@ static void *opcode_targets[256] = { &&TARGET_FOR_ITER_LIST, &&TARGET_FOR_ITER_RANGE, &&TARGET_FOR_ITER_TUPLE, + &&TARGET_FOR_ITER_VIRTUAL, + &&TARGET_GET_ITER_SELF, + &&TARGET_GET_ITER_VIRTUAL, &&TARGET_JUMP_BACKWARD_JIT, &&TARGET_JUMP_BACKWARD_NO_JIT, &&TARGET_LOAD_ATTR_CLASS, @@ -195,7 +201,10 @@ static void *opcode_targets[256] = { &&TARGET_LOAD_SUPER_ATTR_ATTR, &&TARGET_LOAD_SUPER_ATTR_METHOD, &&TARGET_RESUME_CHECK, + &&TARGET_RESUME_CHECK_JIT, + &&TARGET_SEND_ASYNC_GEN, &&TARGET_SEND_GEN, + &&TARGET_SEND_VIRTUAL, &&TARGET_STORE_ATTR_INSTANCE_VALUE, &&TARGET_STORE_ATTR_SLOT, &&TARGET_STORE_ATTR_WITH_HINT, @@ -224,16 +233,6 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_INSTRUMENTED_END_FOR, &&TARGET_INSTRUMENTED_POP_ITER, &&TARGET_INSTRUMENTED_END_SEND, @@ -256,244 +255,518 @@ static void *opcode_targets[256] = { &&TARGET_INSTRUMENTED_JUMP_BACKWARD, &&TARGET_INSTRUMENTED_LINE, &&TARGET_ENTER_EXECUTOR, + &&TARGET_TRACE_RECORD, }; -#else /* Py_TAIL_CALL_INTERP */ -static py_tail_call_funcptr INSTRUCTION_TABLE[256]; +#if _Py_TIER2 +static void *opcode_tracing_targets_table[256] = { + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, + &&TARGET_TRACE_RECORD, +}; +#endif +#else /* _Py_TAIL_CALL_INTERP */ +static py_tail_call_funcptr instruction_funcptr_handler_table[256]; + +static py_tail_call_funcptr instruction_funcptr_tracing_table[256]; -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_2_error(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_pop_1_error(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_exception_unwind(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_exit_unwind(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_start_frame(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_pop_2_error(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_pop_1_error(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_error(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_exception_unwind(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_exit_unwind(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_exit_unwind_notrace(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_start_frame(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_stop_tracing(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_ADD_FLOAT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_ADD_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_ADD_UNICODE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_EXTEND(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_INPLACE_ADD_UNICODE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_MULTIPLY_FLOAT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_MULTIPLY_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_DICT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_GETITEM(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_LIST_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_LIST_SLICE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_STR_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBSCR_TUPLE_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_OP_SUBTRACT_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BINARY_SLICE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_INTERPOLATION(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_LIST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_MAP(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_SET(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_SLICE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_STRING(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_TEMPLATE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_BUILD_TUPLE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CACHE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_ALLOC_AND_ENTER_INIT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BOUND_METHOD_EXACT_ARGS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BOUND_METHOD_GENERAL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BUILTIN_CLASS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BUILTIN_FAST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BUILTIN_FAST_WITH_KEYWORDS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_BUILTIN_O(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_FUNCTION_EX(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_INTRINSIC_1(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_INTRINSIC_2(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_ISINSTANCE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_KW(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_KW_BOUND_METHOD(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_KW_NON_PY(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_KW_PY(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_LEN(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_LIST_APPEND(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_METHOD_DESCRIPTOR_NOARGS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_METHOD_DESCRIPTOR_O(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_NON_PY_GENERAL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_PY_EXACT_ARGS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_PY_GENERAL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_STR_1(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_TUPLE_1(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CALL_TYPE_1(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CHECK_EG_MATCH(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CHECK_EXC_MATCH(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CLEANUP_THROW(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COMPARE_OP(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COMPARE_OP_FLOAT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COMPARE_OP_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COMPARE_OP_STR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CONTAINS_OP(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CONTAINS_OP_DICT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CONTAINS_OP_SET(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_CONVERT_VALUE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COPY(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_COPY_FREE_VARS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_ATTR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_DEREF(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_FAST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_GLOBAL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_NAME(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DELETE_SUBSCR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DICT_MERGE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_DICT_UPDATE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_END_ASYNC_FOR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_END_FOR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_END_SEND(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_ENTER_EXECUTOR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_EXIT_INIT_CHECK(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_EXTENDED_ARG(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FORMAT_SIMPLE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FORMAT_WITH_SPEC(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FOR_ITER(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FOR_ITER_GEN(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FOR_ITER_LIST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FOR_ITER_RANGE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_FOR_ITER_TUPLE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_AITER(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_ANEXT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_AWAITABLE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_ITER(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_LEN(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_GET_YIELD_FROM_ITER(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_IMPORT_FROM(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_IMPORT_NAME(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_CALL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_CALL_FUNCTION_EX(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_CALL_KW(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_END_ASYNC_FOR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_END_FOR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_END_SEND(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_FOR_ITER(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_INSTRUCTION(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_JUMP_BACKWARD(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_JUMP_FORWARD(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_LINE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_LOAD_SUPER_ATTR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_NOT_TAKEN(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_POP_ITER(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_FALSE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NONE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NOT_NONE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_TRUE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_RESUME(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_RETURN_VALUE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INSTRUMENTED_YIELD_VALUE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_INTERPRETER_EXIT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_IS_OP(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_JUMP_BACKWARD(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_JUMP_BACKWARD_JIT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_JUMP_BACKWARD_NO_INTERRUPT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_JUMP_BACKWARD_NO_JIT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_JUMP_FORWARD(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LIST_APPEND(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LIST_EXTEND(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_CLASS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_CLASS_WITH_METACLASS_CHECK(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_INSTANCE_VALUE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_METHOD_LAZY_DICT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_METHOD_NO_DICT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_METHOD_WITH_VALUES(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_MODULE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_NO_DICT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_PROPERTY(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_SLOT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_ATTR_WITH_HINT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_BUILD_CLASS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_COMMON_CONSTANT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_CONST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_DEREF(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FAST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FAST_AND_CLEAR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FAST_BORROW(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FAST_BORROW_LOAD_FAST_BORROW(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FAST_CHECK(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FAST_LOAD_FAST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FROM_DICT_OR_DEREF(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_FROM_DICT_OR_GLOBALS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_GLOBAL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_GLOBAL_BUILTIN(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_GLOBAL_MODULE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_LOCALS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_NAME(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_SMALL_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_SPECIAL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_SUPER_ATTR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_SUPER_ATTR_ATTR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_LOAD_SUPER_ATTR_METHOD(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MAKE_CELL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MAKE_FUNCTION(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MAP_ADD(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MATCH_CLASS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MATCH_KEYS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MATCH_MAPPING(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_MATCH_SEQUENCE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_NOP(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_NOT_TAKEN(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_EXCEPT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_ITER(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_JUMP_IF_FALSE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_JUMP_IF_NONE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_JUMP_IF_NOT_NONE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_JUMP_IF_TRUE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_POP_TOP(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_PUSH_EXC_INFO(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_PUSH_NULL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RAISE_VARARGS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RERAISE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RESERVED(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RESUME(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RESUME_CHECK(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RETURN_GENERATOR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_RETURN_VALUE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SEND(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SEND_GEN(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SETUP_ANNOTATIONS(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SET_ADD(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SET_FUNCTION_ATTRIBUTE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SET_UPDATE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_ATTR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_ATTR_INSTANCE_VALUE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_ATTR_SLOT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_ATTR_WITH_HINT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_DEREF(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_FAST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_FAST_LOAD_FAST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_FAST_STORE_FAST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_GLOBAL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_NAME(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_SLICE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_SUBSCR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_SUBSCR_DICT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_STORE_SUBSCR_LIST_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_SWAP(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_ALWAYS_TRUE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_BOOL(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_INT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_LIST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_NONE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_TO_BOOL_STR(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNARY_INVERT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNARY_NEGATIVE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNARY_NOT(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNPACK_EX(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNPACK_SEQUENCE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNPACK_SEQUENCE_LIST(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNPACK_SEQUENCE_TUPLE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNPACK_SEQUENCE_TWO_TUPLE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_WITH_EXCEPT_START(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_YIELD_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_ADD_FLOAT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_ADD_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_ADD_UNICODE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_EXTEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_INPLACE_ADD_UNICODE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_MULTIPLY_FLOAT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_MULTIPLY_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_GETITEM(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_LIST_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_LIST_SLICE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_STR_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_TUPLE_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBSCR_USTR_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_OP_SUBTRACT_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BINARY_SLICE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_INTERPOLATION(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_LIST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_MAP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_SET(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_SLICE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_STRING(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_TEMPLATE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_BUILD_TUPLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CACHE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_ALLOC_AND_ENTER_INIT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BOUND_METHOD_EXACT_ARGS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BOUND_METHOD_GENERAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BUILTIN_CLASS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BUILTIN_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BUILTIN_FAST_WITH_KEYWORDS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_BUILTIN_O(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_EX_NON_PY_GENERAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_EX_PY(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_FUNCTION_EX(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_INTRINSIC_1(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_INTRINSIC_2(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_ISINSTANCE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_KW(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_KW_BOUND_METHOD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_KW_NON_PY(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_KW_PY(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_LEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_LIST_APPEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_METHOD_DESCRIPTOR_NOARGS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_METHOD_DESCRIPTOR_O(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_NON_PY_GENERAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_PY_EXACT_ARGS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_PY_GENERAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_STR_1(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_TUPLE_1(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CALL_TYPE_1(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CHECK_EG_MATCH(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CHECK_EXC_MATCH(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CLEANUP_THROW(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COMPARE_OP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COMPARE_OP_FLOAT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COMPARE_OP_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COMPARE_OP_STR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CONTAINS_OP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CONTAINS_OP_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CONTAINS_OP_SET(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_CONVERT_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COPY(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_COPY_FREE_VARS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_DEREF(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_GLOBAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_NAME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DELETE_SUBSCR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DICT_MERGE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_DICT_UPDATE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_END_ASYNC_FOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_END_FOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_END_SEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_ENTER_EXECUTOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_EXIT_INIT_CHECK(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_EXTENDED_ARG(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FORMAT_SIMPLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FORMAT_WITH_SPEC(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER_GEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER_LIST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER_RANGE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER_TUPLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_FOR_ITER_VIRTUAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_AITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_ANEXT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_AWAITABLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_ITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_ITER_SELF(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_ITER_VIRTUAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_GET_LEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_IMPORT_FROM(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_IMPORT_NAME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_CALL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_CALL_FUNCTION_EX(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_CALL_KW(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_END_ASYNC_FOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_END_FOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_END_SEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_FOR_ITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_INSTRUCTION(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_JUMP_BACKWARD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_JUMP_FORWARD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_LINE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_LOAD_SUPER_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_NOT_TAKEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_POP_ITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_FALSE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NONE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_NOT_NONE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_POP_JUMP_IF_TRUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_RESUME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_RETURN_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INSTRUMENTED_YIELD_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_INTERPRETER_EXIT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_IS_OP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_JUMP_BACKWARD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_JUMP_BACKWARD_JIT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_JUMP_BACKWARD_NO_INTERRUPT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_JUMP_BACKWARD_NO_JIT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_JUMP_FORWARD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LIST_APPEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LIST_EXTEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_CLASS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_CLASS_WITH_METACLASS_CHECK(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_INSTANCE_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_METHOD_LAZY_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_METHOD_NO_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_METHOD_WITH_VALUES(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_MODULE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_NO_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_PROPERTY(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_SLOT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_ATTR_WITH_HINT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_BUILD_CLASS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_COMMON_CONSTANT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_CONST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_DEREF(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST_AND_CLEAR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST_BORROW(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST_BORROW_LOAD_FAST_BORROW(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST_CHECK(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FAST_LOAD_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FROM_DICT_OR_DEREF(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_FROM_DICT_OR_GLOBALS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_GLOBAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_GLOBAL_BUILTIN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_GLOBAL_MODULE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_LOCALS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_NAME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_SMALL_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_SPECIAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_SUPER_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_SUPER_ATTR_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_LOAD_SUPER_ATTR_METHOD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MAKE_CELL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MAKE_FUNCTION(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MAP_ADD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MATCH_CLASS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MATCH_KEYS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MATCH_MAPPING(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_MATCH_SEQUENCE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_NOP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_NOT_TAKEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_EXCEPT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_ITER(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_JUMP_IF_FALSE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_JUMP_IF_NONE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_JUMP_IF_NOT_NONE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_JUMP_IF_TRUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_POP_TOP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_PUSH_EXC_INFO(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_PUSH_NULL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RAISE_VARARGS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RERAISE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RESERVED(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RESUME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RESUME_CHECK(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RESUME_CHECK_JIT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RETURN_GENERATOR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_RETURN_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SEND(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SEND_ASYNC_GEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SEND_GEN(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SEND_VIRTUAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SETUP_ANNOTATIONS(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SET_ADD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SET_FUNCTION_ATTRIBUTE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SET_UPDATE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_ATTR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_ATTR_INSTANCE_VALUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_ATTR_SLOT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_ATTR_WITH_HINT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_DEREF(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_FAST_LOAD_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_FAST_STORE_FAST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_GLOBAL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_NAME(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_SLICE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_SUBSCR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_SUBSCR_DICT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_STORE_SUBSCR_LIST_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_SWAP(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_ALWAYS_TRUE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_BOOL(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_INT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_LIST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_NONE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TO_BOOL_STR(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_TRACE_RECORD(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNARY_INVERT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNARY_NEGATIVE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNARY_NOT(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNPACK_EX(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNPACK_SEQUENCE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNPACK_SEQUENCE_LIST(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNPACK_SEQUENCE_TUPLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNPACK_SEQUENCE_TWO_TUPLE(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_WITH_EXCEPT_START(TAIL_CALL_PARAMS); +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_YIELD_VALUE(TAIL_CALL_PARAMS); -Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNKNOWN_OPCODE(TAIL_CALL_PARAMS) { +static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_UNKNOWN_OPCODE(TAIL_CALL_PARAMS) { int opcode = next_instr->op.code; _PyErr_Format(tstate, PyExc_SystemError, "%U:%d: unknown opcode %d", @@ -503,7 +776,7 @@ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_UNKNOWN_OPCODE(TAIL_CALL_PARAMS) JUMP_TO_LABEL(error); } -static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { +static py_tail_call_funcptr instruction_funcptr_handler_table[256] = { [BINARY_OP] = _TAIL_CALL_BINARY_OP, [BINARY_OP_ADD_FLOAT] = _TAIL_CALL_BINARY_OP_ADD_FLOAT, [BINARY_OP_ADD_INT] = _TAIL_CALL_BINARY_OP_ADD_INT, @@ -518,6 +791,7 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { [BINARY_OP_SUBSCR_LIST_SLICE] = _TAIL_CALL_BINARY_OP_SUBSCR_LIST_SLICE, [BINARY_OP_SUBSCR_STR_INT] = _TAIL_CALL_BINARY_OP_SUBSCR_STR_INT, [BINARY_OP_SUBSCR_TUPLE_INT] = _TAIL_CALL_BINARY_OP_SUBSCR_TUPLE_INT, + [BINARY_OP_SUBSCR_USTR_INT] = _TAIL_CALL_BINARY_OP_SUBSCR_USTR_INT, [BINARY_OP_SUBTRACT_FLOAT] = _TAIL_CALL_BINARY_OP_SUBTRACT_FLOAT, [BINARY_OP_SUBTRACT_INT] = _TAIL_CALL_BINARY_OP_SUBTRACT_INT, [BINARY_SLICE] = _TAIL_CALL_BINARY_SLICE, @@ -538,6 +812,8 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { [CALL_BUILTIN_FAST] = _TAIL_CALL_CALL_BUILTIN_FAST, [CALL_BUILTIN_FAST_WITH_KEYWORDS] = _TAIL_CALL_CALL_BUILTIN_FAST_WITH_KEYWORDS, [CALL_BUILTIN_O] = _TAIL_CALL_CALL_BUILTIN_O, + [CALL_EX_NON_PY_GENERAL] = _TAIL_CALL_CALL_EX_NON_PY_GENERAL, + [CALL_EX_PY] = _TAIL_CALL_CALL_EX_PY, [CALL_FUNCTION_EX] = _TAIL_CALL_CALL_FUNCTION_EX, [CALL_INTRINSIC_1] = _TAIL_CALL_CALL_INTRINSIC_1, [CALL_INTRINSIC_2] = _TAIL_CALL_CALL_INTRINSIC_2, @@ -592,12 +868,14 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { [FOR_ITER_LIST] = _TAIL_CALL_FOR_ITER_LIST, [FOR_ITER_RANGE] = _TAIL_CALL_FOR_ITER_RANGE, [FOR_ITER_TUPLE] = _TAIL_CALL_FOR_ITER_TUPLE, + [FOR_ITER_VIRTUAL] = _TAIL_CALL_FOR_ITER_VIRTUAL, [GET_AITER] = _TAIL_CALL_GET_AITER, [GET_ANEXT] = _TAIL_CALL_GET_ANEXT, [GET_AWAITABLE] = _TAIL_CALL_GET_AWAITABLE, [GET_ITER] = _TAIL_CALL_GET_ITER, + [GET_ITER_SELF] = _TAIL_CALL_GET_ITER_SELF, + [GET_ITER_VIRTUAL] = _TAIL_CALL_GET_ITER_VIRTUAL, [GET_LEN] = _TAIL_CALL_GET_LEN, - [GET_YIELD_FROM_ITER] = _TAIL_CALL_GET_YIELD_FROM_ITER, [IMPORT_FROM] = _TAIL_CALL_IMPORT_FROM, [IMPORT_NAME] = _TAIL_CALL_IMPORT_NAME, [INSTRUMENTED_CALL] = _TAIL_CALL_INSTRUMENTED_CALL, @@ -689,10 +967,13 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { [RESERVED] = _TAIL_CALL_RESERVED, [RESUME] = _TAIL_CALL_RESUME, [RESUME_CHECK] = _TAIL_CALL_RESUME_CHECK, + [RESUME_CHECK_JIT] = _TAIL_CALL_RESUME_CHECK_JIT, [RETURN_GENERATOR] = _TAIL_CALL_RETURN_GENERATOR, [RETURN_VALUE] = _TAIL_CALL_RETURN_VALUE, [SEND] = _TAIL_CALL_SEND, + [SEND_ASYNC_GEN] = _TAIL_CALL_SEND_ASYNC_GEN, [SEND_GEN] = _TAIL_CALL_SEND_GEN, + [SEND_VIRTUAL] = _TAIL_CALL_SEND_VIRTUAL, [SETUP_ANNOTATIONS] = _TAIL_CALL_SETUP_ANNOTATIONS, [SET_ADD] = _TAIL_CALL_SET_ADD, [SET_FUNCTION_ATTRIBUTE] = _TAIL_CALL_SET_FUNCTION_ATTRIBUTE, @@ -719,6 +1000,7 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { [TO_BOOL_LIST] = _TAIL_CALL_TO_BOOL_LIST, [TO_BOOL_NONE] = _TAIL_CALL_TO_BOOL_NONE, [TO_BOOL_STR] = _TAIL_CALL_TO_BOOL_STR, + [TRACE_RECORD] = _TAIL_CALL_TRACE_RECORD, [UNARY_INVERT] = _TAIL_CALL_UNARY_INVERT, [UNARY_NEGATIVE] = _TAIL_CALL_UNARY_NEGATIVE, [UNARY_NOT] = _TAIL_CALL_UNARY_NOT, @@ -729,6 +1011,265 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { [UNPACK_SEQUENCE_TWO_TUPLE] = _TAIL_CALL_UNPACK_SEQUENCE_TWO_TUPLE, [WITH_EXCEPT_START] = _TAIL_CALL_WITH_EXCEPT_START, [YIELD_VALUE] = _TAIL_CALL_YIELD_VALUE, + [120] = _TAIL_CALL_UNKNOWN_OPCODE, + [121] = _TAIL_CALL_UNKNOWN_OPCODE, + [122] = _TAIL_CALL_UNKNOWN_OPCODE, + [123] = _TAIL_CALL_UNKNOWN_OPCODE, + [124] = _TAIL_CALL_UNKNOWN_OPCODE, + [125] = _TAIL_CALL_UNKNOWN_OPCODE, + [126] = _TAIL_CALL_UNKNOWN_OPCODE, + [127] = _TAIL_CALL_UNKNOWN_OPCODE, + [219] = _TAIL_CALL_UNKNOWN_OPCODE, + [220] = _TAIL_CALL_UNKNOWN_OPCODE, + [221] = _TAIL_CALL_UNKNOWN_OPCODE, + [222] = _TAIL_CALL_UNKNOWN_OPCODE, + [223] = _TAIL_CALL_UNKNOWN_OPCODE, + [224] = _TAIL_CALL_UNKNOWN_OPCODE, + [225] = _TAIL_CALL_UNKNOWN_OPCODE, + [226] = _TAIL_CALL_UNKNOWN_OPCODE, + [227] = _TAIL_CALL_UNKNOWN_OPCODE, + [228] = _TAIL_CALL_UNKNOWN_OPCODE, + [229] = _TAIL_CALL_UNKNOWN_OPCODE, + [230] = _TAIL_CALL_UNKNOWN_OPCODE, + [231] = _TAIL_CALL_UNKNOWN_OPCODE, + [232] = _TAIL_CALL_UNKNOWN_OPCODE, +}; +static py_tail_call_funcptr instruction_funcptr_tracing_table[256] = { + [BINARY_OP] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_ADD_FLOAT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_ADD_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_ADD_UNICODE] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_EXTEND] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_INPLACE_ADD_UNICODE] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_MULTIPLY_FLOAT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_MULTIPLY_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_DICT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_GETITEM] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_LIST_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_LIST_SLICE] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_STR_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_TUPLE_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBSCR_USTR_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBTRACT_FLOAT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_OP_SUBTRACT_INT] = _TAIL_CALL_TRACE_RECORD, + [BINARY_SLICE] = _TAIL_CALL_TRACE_RECORD, + [BUILD_INTERPOLATION] = _TAIL_CALL_TRACE_RECORD, + [BUILD_LIST] = _TAIL_CALL_TRACE_RECORD, + [BUILD_MAP] = _TAIL_CALL_TRACE_RECORD, + [BUILD_SET] = _TAIL_CALL_TRACE_RECORD, + [BUILD_SLICE] = _TAIL_CALL_TRACE_RECORD, + [BUILD_STRING] = _TAIL_CALL_TRACE_RECORD, + [BUILD_TEMPLATE] = _TAIL_CALL_TRACE_RECORD, + [BUILD_TUPLE] = _TAIL_CALL_TRACE_RECORD, + [CACHE] = _TAIL_CALL_TRACE_RECORD, + [CALL] = _TAIL_CALL_TRACE_RECORD, + [CALL_ALLOC_AND_ENTER_INIT] = _TAIL_CALL_TRACE_RECORD, + [CALL_BOUND_METHOD_EXACT_ARGS] = _TAIL_CALL_TRACE_RECORD, + [CALL_BOUND_METHOD_GENERAL] = _TAIL_CALL_TRACE_RECORD, + [CALL_BUILTIN_CLASS] = _TAIL_CALL_TRACE_RECORD, + [CALL_BUILTIN_FAST] = _TAIL_CALL_TRACE_RECORD, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = _TAIL_CALL_TRACE_RECORD, + [CALL_BUILTIN_O] = _TAIL_CALL_TRACE_RECORD, + [CALL_EX_NON_PY_GENERAL] = _TAIL_CALL_TRACE_RECORD, + [CALL_EX_PY] = _TAIL_CALL_TRACE_RECORD, + [CALL_FUNCTION_EX] = _TAIL_CALL_TRACE_RECORD, + [CALL_INTRINSIC_1] = _TAIL_CALL_TRACE_RECORD, + [CALL_INTRINSIC_2] = _TAIL_CALL_TRACE_RECORD, + [CALL_ISINSTANCE] = _TAIL_CALL_TRACE_RECORD, + [CALL_KW] = _TAIL_CALL_TRACE_RECORD, + [CALL_KW_BOUND_METHOD] = _TAIL_CALL_TRACE_RECORD, + [CALL_KW_NON_PY] = _TAIL_CALL_TRACE_RECORD, + [CALL_KW_PY] = _TAIL_CALL_TRACE_RECORD, + [CALL_LEN] = _TAIL_CALL_TRACE_RECORD, + [CALL_LIST_APPEND] = _TAIL_CALL_TRACE_RECORD, + [CALL_METHOD_DESCRIPTOR_FAST] = _TAIL_CALL_TRACE_RECORD, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = _TAIL_CALL_TRACE_RECORD, + [CALL_METHOD_DESCRIPTOR_NOARGS] = _TAIL_CALL_TRACE_RECORD, + [CALL_METHOD_DESCRIPTOR_O] = _TAIL_CALL_TRACE_RECORD, + [CALL_NON_PY_GENERAL] = _TAIL_CALL_TRACE_RECORD, + [CALL_PY_EXACT_ARGS] = _TAIL_CALL_TRACE_RECORD, + [CALL_PY_GENERAL] = _TAIL_CALL_TRACE_RECORD, + [CALL_STR_1] = _TAIL_CALL_TRACE_RECORD, + [CALL_TUPLE_1] = _TAIL_CALL_TRACE_RECORD, + [CALL_TYPE_1] = _TAIL_CALL_TRACE_RECORD, + [CHECK_EG_MATCH] = _TAIL_CALL_TRACE_RECORD, + [CHECK_EXC_MATCH] = _TAIL_CALL_TRACE_RECORD, + [CLEANUP_THROW] = _TAIL_CALL_TRACE_RECORD, + [COMPARE_OP] = _TAIL_CALL_TRACE_RECORD, + [COMPARE_OP_FLOAT] = _TAIL_CALL_TRACE_RECORD, + [COMPARE_OP_INT] = _TAIL_CALL_TRACE_RECORD, + [COMPARE_OP_STR] = _TAIL_CALL_TRACE_RECORD, + [CONTAINS_OP] = _TAIL_CALL_TRACE_RECORD, + [CONTAINS_OP_DICT] = _TAIL_CALL_TRACE_RECORD, + [CONTAINS_OP_SET] = _TAIL_CALL_TRACE_RECORD, + [CONVERT_VALUE] = _TAIL_CALL_TRACE_RECORD, + [COPY] = _TAIL_CALL_TRACE_RECORD, + [COPY_FREE_VARS] = _TAIL_CALL_TRACE_RECORD, + [DELETE_ATTR] = _TAIL_CALL_TRACE_RECORD, + [DELETE_DEREF] = _TAIL_CALL_TRACE_RECORD, + [DELETE_FAST] = _TAIL_CALL_TRACE_RECORD, + [DELETE_GLOBAL] = _TAIL_CALL_TRACE_RECORD, + [DELETE_NAME] = _TAIL_CALL_TRACE_RECORD, + [DELETE_SUBSCR] = _TAIL_CALL_TRACE_RECORD, + [DICT_MERGE] = _TAIL_CALL_TRACE_RECORD, + [DICT_UPDATE] = _TAIL_CALL_TRACE_RECORD, + [END_ASYNC_FOR] = _TAIL_CALL_TRACE_RECORD, + [END_FOR] = _TAIL_CALL_TRACE_RECORD, + [END_SEND] = _TAIL_CALL_TRACE_RECORD, + [ENTER_EXECUTOR] = _TAIL_CALL_TRACE_RECORD, + [EXIT_INIT_CHECK] = _TAIL_CALL_TRACE_RECORD, + [EXTENDED_ARG] = _TAIL_CALL_TRACE_RECORD, + [FORMAT_SIMPLE] = _TAIL_CALL_TRACE_RECORD, + [FORMAT_WITH_SPEC] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER_GEN] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER_LIST] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER_RANGE] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER_TUPLE] = _TAIL_CALL_TRACE_RECORD, + [FOR_ITER_VIRTUAL] = _TAIL_CALL_TRACE_RECORD, + [GET_AITER] = _TAIL_CALL_TRACE_RECORD, + [GET_ANEXT] = _TAIL_CALL_TRACE_RECORD, + [GET_AWAITABLE] = _TAIL_CALL_TRACE_RECORD, + [GET_ITER] = _TAIL_CALL_TRACE_RECORD, + [GET_ITER_SELF] = _TAIL_CALL_TRACE_RECORD, + [GET_ITER_VIRTUAL] = _TAIL_CALL_TRACE_RECORD, + [GET_LEN] = _TAIL_CALL_TRACE_RECORD, + [IMPORT_FROM] = _TAIL_CALL_TRACE_RECORD, + [IMPORT_NAME] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_CALL] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_CALL_FUNCTION_EX] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_CALL_KW] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_END_ASYNC_FOR] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_END_FOR] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_END_SEND] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_FOR_ITER] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_INSTRUCTION] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_JUMP_BACKWARD] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_JUMP_FORWARD] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_LINE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_LOAD_SUPER_ATTR] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_NOT_TAKEN] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_POP_ITER] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_POP_JUMP_IF_FALSE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_POP_JUMP_IF_NONE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_POP_JUMP_IF_TRUE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_RESUME] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_RETURN_VALUE] = _TAIL_CALL_TRACE_RECORD, + [INSTRUMENTED_YIELD_VALUE] = _TAIL_CALL_TRACE_RECORD, + [INTERPRETER_EXIT] = _TAIL_CALL_TRACE_RECORD, + [IS_OP] = _TAIL_CALL_TRACE_RECORD, + [JUMP_BACKWARD] = _TAIL_CALL_TRACE_RECORD, + [JUMP_BACKWARD_JIT] = _TAIL_CALL_TRACE_RECORD, + [JUMP_BACKWARD_NO_INTERRUPT] = _TAIL_CALL_TRACE_RECORD, + [JUMP_BACKWARD_NO_JIT] = _TAIL_CALL_TRACE_RECORD, + [JUMP_FORWARD] = _TAIL_CALL_TRACE_RECORD, + [LIST_APPEND] = _TAIL_CALL_TRACE_RECORD, + [LIST_EXTEND] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_CLASS] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_INSTANCE_VALUE] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_METHOD_LAZY_DICT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_METHOD_NO_DICT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_METHOD_WITH_VALUES] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_MODULE] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_PROPERTY] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_SLOT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_ATTR_WITH_HINT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_BUILD_CLASS] = _TAIL_CALL_TRACE_RECORD, + [LOAD_COMMON_CONSTANT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_CONST] = _TAIL_CALL_TRACE_RECORD, + [LOAD_DEREF] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST_AND_CLEAR] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST_BORROW] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST_BORROW_LOAD_FAST_BORROW] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST_CHECK] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FAST_LOAD_FAST] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FROM_DICT_OR_DEREF] = _TAIL_CALL_TRACE_RECORD, + [LOAD_FROM_DICT_OR_GLOBALS] = _TAIL_CALL_TRACE_RECORD, + [LOAD_GLOBAL] = _TAIL_CALL_TRACE_RECORD, + [LOAD_GLOBAL_BUILTIN] = _TAIL_CALL_TRACE_RECORD, + [LOAD_GLOBAL_MODULE] = _TAIL_CALL_TRACE_RECORD, + [LOAD_LOCALS] = _TAIL_CALL_TRACE_RECORD, + [LOAD_NAME] = _TAIL_CALL_TRACE_RECORD, + [LOAD_SMALL_INT] = _TAIL_CALL_TRACE_RECORD, + [LOAD_SPECIAL] = _TAIL_CALL_TRACE_RECORD, + [LOAD_SUPER_ATTR] = _TAIL_CALL_TRACE_RECORD, + [LOAD_SUPER_ATTR_ATTR] = _TAIL_CALL_TRACE_RECORD, + [LOAD_SUPER_ATTR_METHOD] = _TAIL_CALL_TRACE_RECORD, + [MAKE_CELL] = _TAIL_CALL_TRACE_RECORD, + [MAKE_FUNCTION] = _TAIL_CALL_TRACE_RECORD, + [MAP_ADD] = _TAIL_CALL_TRACE_RECORD, + [MATCH_CLASS] = _TAIL_CALL_TRACE_RECORD, + [MATCH_KEYS] = _TAIL_CALL_TRACE_RECORD, + [MATCH_MAPPING] = _TAIL_CALL_TRACE_RECORD, + [MATCH_SEQUENCE] = _TAIL_CALL_TRACE_RECORD, + [NOP] = _TAIL_CALL_TRACE_RECORD, + [NOT_TAKEN] = _TAIL_CALL_TRACE_RECORD, + [POP_EXCEPT] = _TAIL_CALL_TRACE_RECORD, + [POP_ITER] = _TAIL_CALL_TRACE_RECORD, + [POP_JUMP_IF_FALSE] = _TAIL_CALL_TRACE_RECORD, + [POP_JUMP_IF_NONE] = _TAIL_CALL_TRACE_RECORD, + [POP_JUMP_IF_NOT_NONE] = _TAIL_CALL_TRACE_RECORD, + [POP_JUMP_IF_TRUE] = _TAIL_CALL_TRACE_RECORD, + [POP_TOP] = _TAIL_CALL_TRACE_RECORD, + [PUSH_EXC_INFO] = _TAIL_CALL_TRACE_RECORD, + [PUSH_NULL] = _TAIL_CALL_TRACE_RECORD, + [RAISE_VARARGS] = _TAIL_CALL_TRACE_RECORD, + [RERAISE] = _TAIL_CALL_TRACE_RECORD, + [RESERVED] = _TAIL_CALL_TRACE_RECORD, + [RESUME] = _TAIL_CALL_TRACE_RECORD, + [RESUME_CHECK] = _TAIL_CALL_TRACE_RECORD, + [RESUME_CHECK_JIT] = _TAIL_CALL_TRACE_RECORD, + [RETURN_GENERATOR] = _TAIL_CALL_TRACE_RECORD, + [RETURN_VALUE] = _TAIL_CALL_TRACE_RECORD, + [SEND] = _TAIL_CALL_TRACE_RECORD, + [SEND_ASYNC_GEN] = _TAIL_CALL_TRACE_RECORD, + [SEND_GEN] = _TAIL_CALL_TRACE_RECORD, + [SEND_VIRTUAL] = _TAIL_CALL_TRACE_RECORD, + [SETUP_ANNOTATIONS] = _TAIL_CALL_TRACE_RECORD, + [SET_ADD] = _TAIL_CALL_TRACE_RECORD, + [SET_FUNCTION_ATTRIBUTE] = _TAIL_CALL_TRACE_RECORD, + [SET_UPDATE] = _TAIL_CALL_TRACE_RECORD, + [STORE_ATTR] = _TAIL_CALL_TRACE_RECORD, + [STORE_ATTR_INSTANCE_VALUE] = _TAIL_CALL_TRACE_RECORD, + [STORE_ATTR_SLOT] = _TAIL_CALL_TRACE_RECORD, + [STORE_ATTR_WITH_HINT] = _TAIL_CALL_TRACE_RECORD, + [STORE_DEREF] = _TAIL_CALL_TRACE_RECORD, + [STORE_FAST] = _TAIL_CALL_TRACE_RECORD, + [STORE_FAST_LOAD_FAST] = _TAIL_CALL_TRACE_RECORD, + [STORE_FAST_STORE_FAST] = _TAIL_CALL_TRACE_RECORD, + [STORE_GLOBAL] = _TAIL_CALL_TRACE_RECORD, + [STORE_NAME] = _TAIL_CALL_TRACE_RECORD, + [STORE_SLICE] = _TAIL_CALL_TRACE_RECORD, + [STORE_SUBSCR] = _TAIL_CALL_TRACE_RECORD, + [STORE_SUBSCR_DICT] = _TAIL_CALL_TRACE_RECORD, + [STORE_SUBSCR_LIST_INT] = _TAIL_CALL_TRACE_RECORD, + [SWAP] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_ALWAYS_TRUE] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_BOOL] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_INT] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_LIST] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_NONE] = _TAIL_CALL_TRACE_RECORD, + [TO_BOOL_STR] = _TAIL_CALL_TRACE_RECORD, + [TRACE_RECORD] = _TAIL_CALL_TRACE_RECORD, + [UNARY_INVERT] = _TAIL_CALL_TRACE_RECORD, + [UNARY_NEGATIVE] = _TAIL_CALL_TRACE_RECORD, + [UNARY_NOT] = _TAIL_CALL_TRACE_RECORD, + [UNPACK_EX] = _TAIL_CALL_TRACE_RECORD, + [UNPACK_SEQUENCE] = _TAIL_CALL_TRACE_RECORD, + [UNPACK_SEQUENCE_LIST] = _TAIL_CALL_TRACE_RECORD, + [UNPACK_SEQUENCE_TUPLE] = _TAIL_CALL_TRACE_RECORD, + [UNPACK_SEQUENCE_TWO_TUPLE] = _TAIL_CALL_TRACE_RECORD, + [WITH_EXCEPT_START] = _TAIL_CALL_TRACE_RECORD, + [YIELD_VALUE] = _TAIL_CALL_TRACE_RECORD, + [120] = _TAIL_CALL_UNKNOWN_OPCODE, [121] = _TAIL_CALL_UNKNOWN_OPCODE, [122] = _TAIL_CALL_UNKNOWN_OPCODE, [123] = _TAIL_CALL_UNKNOWN_OPCODE, @@ -736,15 +1277,6 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { [125] = _TAIL_CALL_UNKNOWN_OPCODE, [126] = _TAIL_CALL_UNKNOWN_OPCODE, [127] = _TAIL_CALL_UNKNOWN_OPCODE, - [210] = _TAIL_CALL_UNKNOWN_OPCODE, - [211] = _TAIL_CALL_UNKNOWN_OPCODE, - [212] = _TAIL_CALL_UNKNOWN_OPCODE, - [213] = _TAIL_CALL_UNKNOWN_OPCODE, - [214] = _TAIL_CALL_UNKNOWN_OPCODE, - [215] = _TAIL_CALL_UNKNOWN_OPCODE, - [216] = _TAIL_CALL_UNKNOWN_OPCODE, - [217] = _TAIL_CALL_UNKNOWN_OPCODE, - [218] = _TAIL_CALL_UNKNOWN_OPCODE, [219] = _TAIL_CALL_UNKNOWN_OPCODE, [220] = _TAIL_CALL_UNKNOWN_OPCODE, [221] = _TAIL_CALL_UNKNOWN_OPCODE, @@ -759,6 +1291,5 @@ static py_tail_call_funcptr INSTRUCTION_TABLE[256] = { [230] = _TAIL_CALL_UNKNOWN_OPCODE, [231] = _TAIL_CALL_UNKNOWN_OPCODE, [232] = _TAIL_CALL_UNKNOWN_OPCODE, - [233] = _TAIL_CALL_UNKNOWN_OPCODE, }; -#endif /* Py_TAIL_CALL_INTERP */ +#endif /* _Py_TAIL_CALL_INTERP */ diff --git a/Python/optimizer.c b/Python/optimizer.c index 8d01d605ef4a2a..e95e4b5e24b2c5 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -6,15 +6,15 @@ #include "pycore_interp.h" #include "pycore_backoff.h" #include "pycore_bitutils.h" // _Py_popcount32() +#include "pycore_ceval.h" // _Py_set_eval_breaker_bit #include "pycore_code.h" // _Py_GetBaseCodeUnit -#include "pycore_function.h" // _PyFunction_LookupByVersion() #include "pycore_interpframe.h" #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_opcode_metadata.h" // _PyOpcode_OpName[] #include "pycore_opcode_utils.h" // MAX_REAL_OPCODE #include "pycore_optimizer.h" // _Py_uop_analyze_and_optimize() #include "pycore_pystate.h" // _PyInterpreterState_GET() -#include "pycore_tuple.h" // _PyTuple_FromArraySteal +#include "pycore_tuple.h" // _PyTuple_FromArraySteal #include "pycore_unicodeobject.h" // _PyUnicode_FromASCII #include "pycore_uop_ids.h" #include "pycore_jit.h" @@ -28,11 +28,25 @@ #define MAX_EXECUTORS_SIZE 256 +// Trace too short, no progress: +// _START_EXECUTOR +// _MAKE_WARM +// _CHECK_VALIDITY +// _SET_IP +// is 4-5 instructions. +#define CODE_SIZE_NO_PROGRESS 5 +// We start with _START_EXECUTOR, _MAKE_WARM +#define CODE_SIZE_EMPTY 2 + #define _PyExecutorObject_CAST(op) ((_PyExecutorObject *)(op)) +#ifndef Py_GIL_DISABLED static bool has_space_for_executor(PyCodeObject *code, _Py_CODEUNIT *instr) { + if (code == (PyCodeObject *)&_Py_InitCleanup) { + return false; + } if (instr->op.code == ENTER_EXECUTOR) { return true; } @@ -97,41 +111,64 @@ insert_executor(PyCodeObject *code, _Py_CODEUNIT *instr, int index, _PyExecutorO instr->op.code = ENTER_EXECUTOR; instr->op.arg = index; } +#endif // Py_GIL_DISABLED static _PyExecutorObject * -make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies); +make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies); static int -uop_optimize(_PyInterpreterFrame *frame, _Py_CODEUNIT *instr, - _PyExecutorObject **exec_ptr, int curr_stackentries, +uop_optimize(_PyInterpreterFrame *frame, PyThreadState *tstate, + _PyExecutorObject **exec_ptr, bool progress_needed); /* Returns 1 if optimized, 0 if not optimized, and -1 for an error. * If optimized, *executor_ptr contains a new reference to the executor */ -int +// gh-137573: inlining this function causes stack overflows +Py_NO_INLINE int _PyOptimizer_Optimize( - _PyInterpreterFrame *frame, _Py_CODEUNIT *start, - _PyExecutorObject **executor_ptr, int chain_depth) + _PyInterpreterFrame *frame, PyThreadState *tstate) { - _PyStackRef *stack_pointer = frame->stackpointer; - assert(_PyInterpreterState_GET()->jit); + _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (!interp->jit) { + // gh-140936: It is possible that interp->jit will become false during + // interpreter finalization. However, the specialized JUMP_BACKWARD_JIT + // instruction may still be present. In this case, we should + // return immediately without optimization. + return 0; + } + _PyExecutorObject *prev_executor = _tstate->jit_tracer_state->initial_state.executor; + if (prev_executor != NULL && !prev_executor->vm_data.valid) { + // gh-143604: If we are a side exit executor and the original executor is no + // longer valid, don't compile to prevent a reference leak. + return 0; + } + assert(!interp->compiling); + assert(_tstate->jit_tracer_state->initial_state.stack_depth >= 0); +#ifndef Py_GIL_DISABLED + assert(_tstate->jit_tracer_state->initial_state.func != NULL); + interp->compiling = true; // The first executor in a chain and the MAX_CHAIN_DEPTH'th executor *must* // make progress in order to avoid infinite loops or excessively-long // side-exit chains. We can only insert the executor into the bytecode if // this is true, since a deopt won't infinitely re-enter the executor: + int chain_depth = _tstate->jit_tracer_state->initial_state.chain_depth; chain_depth %= MAX_CHAIN_DEPTH; bool progress_needed = chain_depth == 0; - PyCodeObject *code = _PyFrame_GetCode(frame); - assert(PyCode_Check(code)); + PyCodeObject *code = (PyCodeObject *)_tstate->jit_tracer_state->initial_state.code; + _Py_CODEUNIT *start = _tstate->jit_tracer_state->initial_state.start_instr; if (progress_needed && !has_space_for_executor(code, start)) { + interp->compiling = false; return 0; } - int err = uop_optimize(frame, start, executor_ptr, (int)(stack_pointer - _PyFrame_Stackbase(frame)), progress_needed); + _PyExecutorObject *executor; + int err = uop_optimize(frame, tstate, &executor, progress_needed); if (err <= 0) { + interp->compiling = false; return err; } - assert(*executor_ptr != NULL); + assert(executor != NULL); if (progress_needed) { int index = get_index_for_executor(code, start); if (index < 0) { @@ -141,17 +178,28 @@ _PyOptimizer_Optimize( * If an optimizer has already produced an executor, * it might get confused by the executor disappearing, * but there is not much we can do about that here. */ - Py_DECREF(*executor_ptr); + Py_DECREF(executor); + interp->compiling = false; return 0; } - insert_executor(code, start, index, *executor_ptr); + insert_executor(code, start, index, executor); + } + executor->vm_data.chain_depth = chain_depth; + assert(executor->vm_data.valid); + _PyExitData *exit = _tstate->jit_tracer_state->initial_state.exit; + if (exit != NULL && !progress_needed) { + exit->executor = executor; } else { - (*executor_ptr)->vm_data.code = NULL; + // An executor inserted into the code object now has a strong reference + // to it from the code object. Thus, we don't need this reference anymore. + Py_DECREF(executor); } - (*executor_ptr)->vm_data.chain_depth = chain_depth; - assert((*executor_ptr)->vm_data.valid); + interp->compiling = false; return 1; +#else + return 0; +#endif } static _PyExecutorObject * @@ -202,11 +250,9 @@ get_oparg(PyObject *self, PyObject *Py_UNUSED(ignored)) ///////////////////// Experimental UOp Optimizer ///////////////////// static int executor_clear(PyObject *executor); -static void unlink_executor(_PyExecutorObject *executor); - -static void -free_executor(_PyExecutorObject *self) +void +_PyExecutor_Free(_PyExecutorObject *self) { #ifdef _Py_JIT _PyJIT_Free(self); @@ -214,70 +260,87 @@ free_executor(_PyExecutorObject *self) PyObject_GC_Del(self); } +static void executor_invalidate(PyObject *op); + +static void +executor_clear_exits(_PyExecutorObject *executor) +{ + _PyExecutorObject *cold = _PyExecutor_GetColdExecutor(); + _PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor(); + for (uint32_t i = 0; i < executor->exit_count; i++) { + _PyExitData *exit = &executor->exits[i]; + exit->temperature = initial_unreachable_backoff_counter(); + _PyExecutorObject *old = executor->exits[i].executor; + exit->executor = exit->is_dynamic ? cold_dynamic : cold; + Py_DECREF(old); + } +} + + void _Py_ClearExecutorDeletionList(PyInterpreterState *interp) { + if (interp->executor_deletion_list_head == NULL) { + return; + } _PyRuntimeState *runtime = &_PyRuntime; HEAD_LOCK(runtime); PyThreadState* ts = PyInterpreterState_ThreadHead(interp); + while (ts) { + _PyExecutorObject *current = (_PyExecutorObject *)ts->current_executor; + Py_XINCREF(current); + ts = ts->next; + } HEAD_UNLOCK(runtime); + _PyExecutorObject *keep_list = NULL; + do { + _PyExecutorObject *exec = interp->executor_deletion_list_head; + interp->executor_deletion_list_head = exec->vm_data.links.next; + if (Py_REFCNT(exec) == 0) { + _PyExecutor_Free(exec); + } else { + exec->vm_data.links.next = keep_list; + keep_list = exec; + } + } while (interp->executor_deletion_list_head != NULL); + interp->executor_deletion_list_head = keep_list; + HEAD_LOCK(runtime); + ts = PyInterpreterState_ThreadHead(interp); while (ts) { _PyExecutorObject *current = (_PyExecutorObject *)ts->current_executor; if (current != NULL) { - /* Anything in this list will be unlinked, so we can reuse the - * linked field as a reachability marker. */ - current->vm_data.linked = 1; - } - HEAD_LOCK(runtime); - ts = PyThreadState_Next(ts); - HEAD_UNLOCK(runtime); - } - _PyExecutorObject **prev_to_next_ptr = &interp->executor_deletion_list_head; - _PyExecutorObject *exec = *prev_to_next_ptr; - while (exec != NULL) { - if (exec->vm_data.linked) { - // This executor is currently executing - exec->vm_data.linked = 0; - prev_to_next_ptr = &exec->vm_data.links.next; + Py_DECREF((PyObject *)current); } - else { - *prev_to_next_ptr = exec->vm_data.links.next; - free_executor(exec); - } - exec = *prev_to_next_ptr; + ts = ts->next; } - interp->executor_deletion_list_remaining_capacity = EXECUTOR_DELETE_LIST_MAX; + HEAD_UNLOCK(runtime); } static void add_to_pending_deletion_list(_PyExecutorObject *self) { + if (self->vm_data.pending_deletion) { + return; + } + self->vm_data.pending_deletion = 1; PyInterpreterState *interp = PyInterpreterState_Get(); + self->vm_data.links.previous = NULL; self->vm_data.links.next = interp->executor_deletion_list_head; interp->executor_deletion_list_head = self; - if (interp->executor_deletion_list_remaining_capacity > 0) { - interp->executor_deletion_list_remaining_capacity--; - } - else { - _Py_ClearExecutorDeletionList(interp); - } } static void uop_dealloc(PyObject *op) { _PyExecutorObject *self = _PyExecutorObject_CAST(op); - _PyObject_GC_UNTRACK(self); + executor_invalidate(op); assert(self->vm_data.code == NULL); - unlink_executor(self); - // Once unlinked it becomes impossible to invalidate an executor, so do it here. - self->vm_data.valid = 0; add_to_pending_deletion_list(self); } const char * _PyUOpName(int index) { - if (index < 0 || index > MAX_UOP_ID) { + if (index < 0 || index > MAX_UOP_REGS_ID) { return NULL; } return _PyOpcode_uop_name[index]; @@ -312,7 +375,7 @@ _PyUOpPrint(const _PyUOpInstruction *uop) default: printf(" (%d, Unknown format)", uop->oparg); } - if (_PyUop_Flags[uop->opcode] & HAS_ERROR_FLAG) { + if (_PyUop_Flags[_PyUop_Uncached[uop->opcode]] & HAS_ERROR_FLAG) { printf(", error_target=%d", uop->error_target); } @@ -336,7 +399,9 @@ uop_item(PyObject *op, Py_ssize_t index) PyErr_SetNone(PyExc_IndexError); return NULL; } - const char *name = _PyUOpName(self->trace[index].opcode); + int opcode = self->trace[index].opcode; + int base_opcode = _PyUop_Uncached[opcode]; + const char *name = _PyUOpName(base_opcode); if (name == NULL) { name = "<nil>"; } @@ -350,7 +415,7 @@ uop_item(PyObject *op, Py_ssize_t index) return NULL; } PyObject *target = PyLong_FromUnsignedLong(self->trace[index].target); - if (oparg == NULL) { + if (target == NULL) { Py_DECREF(oparg); Py_DECREF(oname); return NULL; @@ -431,7 +496,12 @@ _PyUOp_Replacements[MAX_UOP_ID + 1] = { [_ITER_JUMP_LIST] = _GUARD_NOT_EXHAUSTED_LIST, [_ITER_JUMP_TUPLE] = _GUARD_NOT_EXHAUSTED_TUPLE, [_FOR_ITER] = _FOR_ITER_TIER_TWO, + [_FOR_ITER_VIRTUAL] = _FOR_ITER_VIRTUAL_TIER_TWO, [_ITER_NEXT_LIST] = _ITER_NEXT_LIST_TIER_TWO, + [_CHECK_PERIODIC_AT_END] = _TIER2_RESUME_CHECK, + [_LOAD_BYTECODE] = _NOP, + [_SEND_VIRTUAL] = _SEND_VIRTUAL_TIER_TWO, + [_SEND_ASYNC_GEN] = _SEND_ASYNC_GEN_TIER_TWO, }; static const uint8_t @@ -440,6 +510,7 @@ is_for_iter_test[MAX_UOP_ID + 1] = { [_GUARD_NOT_EXHAUSTED_LIST] = 1, [_GUARD_NOT_EXHAUSTED_TUPLE] = 1, [_FOR_ITER_TIER_TWO] = 1, + [_ITER_NEXT_INLINE] = 1, }; static const uint16_t @@ -454,9 +525,35 @@ BRANCH_TO_GUARD[4][2] = { [POP_JUMP_IF_NOT_NONE - POP_JUMP_IF_FALSE][1] = _GUARD_IS_NOT_NONE_POP, }; +static const uint16_t +guard_ip_uop[MAX_UOP_ID + 1] = { + [_PUSH_FRAME] = _GUARD_IP__PUSH_FRAME, + [_RETURN_GENERATOR] = _GUARD_IP_RETURN_GENERATOR, + [_RETURN_VALUE] = _GUARD_IP_RETURN_VALUE, + [_YIELD_VALUE] = _GUARD_IP_YIELD_VALUE, +}; + +static const uint16_t +guard_code_version_uop[MAX_UOP_ID + 1] = { + [_PUSH_FRAME] = _GUARD_CODE_VERSION__PUSH_FRAME, + [_RETURN_GENERATOR] = _GUARD_CODE_VERSION_RETURN_GENERATOR, + [_RETURN_VALUE] = _GUARD_CODE_VERSION_RETURN_VALUE, + [_YIELD_VALUE] = _GUARD_CODE_VERSION_YIELD_VALUE, +}; + +static const uint16_t +dynamic_exit_uop[MAX_UOP_ID + 1] = { + [_GUARD_IP__PUSH_FRAME] = 1, + [_GUARD_IP_RETURN_GENERATOR] = 1, + [_GUARD_IP_RETURN_VALUE] = 1, + [_GUARD_IP_YIELD_VALUE] = 1, + [_GUARD_CODE_VERSION__PUSH_FRAME] = 1, + [_GUARD_CODE_VERSION_RETURN_GENERATOR] = 1, + [_GUARD_CODE_VERSION_RETURN_VALUE] = 1, + [_GUARD_CODE_VERSION_YIELD_VALUE] = 1, +}; + -#define CONFIDENCE_RANGE 1000 -#define CONFIDENCE_CUTOFF 333 #ifdef Py_DEBUG #define DPRINTF(level, ...) \ @@ -466,108 +563,116 @@ BRANCH_TO_GUARD[4][2] = { #endif -static inline int +static inline void add_to_trace( - _PyUOpInstruction *trace, - int trace_length, + _PyJitTracerState *tracer, uint16_t opcode, uint16_t oparg, uint64_t operand, uint32_t target) { - trace[trace_length].opcode = opcode; - trace[trace_length].format = UOP_FORMAT_TARGET; - trace[trace_length].target = target; - trace[trace_length].oparg = oparg; - trace[trace_length].operand0 = operand; + _PyJitUopBuffer *trace = &tracer->code_buffer; + _PyUOpInstruction *inst = trace->next; + inst->opcode = opcode; + inst->format = UOP_FORMAT_TARGET; + inst->target = target; + inst->oparg = oparg; + inst->operand0 = operand; #ifdef Py_STATS - trace[trace_length].execution_count = 0; + inst->execution_count = 0; + inst->fitness = tracer->translator_state.fitness; #endif - return trace_length + 1; + trace->next++; } + #ifdef Py_DEBUG #define ADD_TO_TRACE(OPCODE, OPARG, OPERAND, TARGET) \ - assert(trace_length < max_length); \ - trace_length = add_to_trace(trace, trace_length, (OPCODE), (OPARG), (OPERAND), (TARGET)); \ + add_to_trace(tracer, (OPCODE), (OPARG), (OPERAND), (TARGET)); \ if (lltrace >= 2) { \ - printf("%4d ADD_TO_TRACE: ", trace_length); \ - _PyUOpPrint(&trace[trace_length-1]); \ + printf("%4d ADD_TO_TRACE: ", uop_buffer_length(trace)); \ + _PyUOpPrint(uop_buffer_last(trace)); \ printf("\n"); \ } #else #define ADD_TO_TRACE(OPCODE, OPARG, OPERAND, TARGET) \ - assert(trace_length < max_length); \ - trace_length = add_to_trace(trace, trace_length, (OPCODE), (OPARG), (OPERAND), (TARGET)); + add_to_trace(tracer, (OPCODE), (OPARG), (OPERAND), (TARGET)) #endif #define INSTR_IP(INSTR, CODE) \ ((uint32_t)((INSTR) - ((_Py_CODEUNIT *)(CODE)->co_code_adaptive))) -// Reserve space for n uops -#define RESERVE_RAW(n, opname) \ - if (trace_length + (n) > max_length) { \ - DPRINTF(2, "No room for %s (need %d, got %d)\n", \ - (opname), (n), max_length - trace_length); \ - OPT_STAT_INC(trace_too_long); \ - goto done; \ - } - -// Reserve space for N uops, plus 3 for _SET_IP, _CHECK_VALIDITY and _EXIT_TRACE -#define RESERVE(needed) RESERVE_RAW((needed) + 3, _PyUOpName(opcode)) - -// Trace stack operations (used by _PUSH_FRAME, _RETURN_VALUE) -#define TRACE_STACK_PUSH() \ - if (trace_stack_depth >= TRACE_STACK_SIZE) { \ - DPRINTF(2, "Trace stack overflow\n"); \ - OPT_STAT_INC(trace_stack_overflow); \ - return 0; \ - } \ - assert(func == NULL || func->func_code == (PyObject *)code); \ - trace_stack[trace_stack_depth].func = func; \ - trace_stack[trace_stack_depth].code = code; \ - trace_stack[trace_stack_depth].instr = instr; \ - trace_stack_depth++; -#define TRACE_STACK_POP() \ - if (trace_stack_depth <= 0) { \ - Py_FatalError("Trace stack underflow\n"); \ - } \ - trace_stack_depth--; \ - func = trace_stack[trace_stack_depth].func; \ - code = trace_stack[trace_stack_depth].code; \ - assert(func == NULL || func->func_code == (PyObject *)code); \ - instr = trace_stack[trace_stack_depth].instr; - -/* Returns the length of the trace on success, - * 0 if it failed to produce a worthwhile trace, - * and -1 on an error. + +/* Branch penalty: 0 for a fully biased branch and FITNESS_BRANCH_BALANCED for + * a balanced or fully off-trace branch. This keeps any single branch from + * consuming more than one balanced-branch cost. */ +static inline int +compute_branch_penalty(uint16_t history) +{ + bool branch_taken = history & 1; + int taken_count = _Py_popcount32((uint32_t)history); + int on_trace_count = branch_taken ? taken_count : 16 - taken_count; + int off_trace = 16 - on_trace_count; + int penalty = off_trace * FITNESS_BRANCH_BALANCED / 8; + if (penalty > FITNESS_BRANCH_BALANCED) { + penalty = FITNESS_BRANCH_BALANCED; + } + return penalty; +} + +/* Compute exit quality for the current trace position. + * Higher values mean better places to stop the trace. */ +static inline int32_t +compute_exit_quality(_Py_CODEUNIT *target_instr, int opcode, + const _PyJitTracerState *tracer) +{ + if (target_instr == tracer->initial_state.close_loop_instr) { + return EXIT_QUALITY_CLOSE_LOOP; + } + else if (target_instr->op.code == ENTER_EXECUTOR) { + return EXIT_QUALITY_ENTER_EXECUTOR; + } + else if (opcode == JUMP_BACKWARD_JIT || + opcode == JUMP_BACKWARD || + opcode == JUMP_BACKWARD_NO_INTERRUPT) { + return EXIT_QUALITY_BACKWARD_EDGE; + } + else if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] > 0) { + return EXIT_QUALITY_SPECIALIZABLE; + } + return EXIT_QUALITY_DEFAULT; +} + +/* Frame penalty: (MAX_ABSTRACT_FRAME_DEPTH-1) pushes exhaust fitness. */ +static inline int32_t +compute_frame_penalty(uint16_t fitness_initial) +{ + return (int32_t)fitness_initial / (MAX_ABSTRACT_FRAME_DEPTH - 1) + 1; +} + static int -translate_bytecode_to_trace( +is_terminator(const _PyUOpInstruction *uop) +{ + int opcode = _PyUop_Uncached[uop->opcode]; + return ( + opcode == _EXIT_TRACE || + opcode == _DEOPT || + opcode == _JUMP_TO_TOP || + opcode == _DYNAMIC_EXIT + ); +} + +/* Returns 1 on success (added to trace), 0 on trace end. + */ +// gh-142543: inlining this function causes stack overflows +Py_NO_INLINE int +_PyJit_translate_single_bytecode_to_trace( + PyThreadState *tstate, _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr, - _PyUOpInstruction *trace, - int buffer_size, - _PyBloomFilter *dependencies, bool progress_needed) + _Py_CODEUNIT *next_instr, + int stop_tracing_opcode) { - bool first = true; - PyCodeObject *code = _PyFrame_GetCode(frame); - PyFunctionObject *func = _PyFrame_GetFunction(frame); - assert(PyFunction_Check(func)); - PyCodeObject *initial_code = code; - _Py_BloomFilter_Add(dependencies, initial_code); - _Py_CODEUNIT *initial_instr = instr; - int trace_length = 0; - // Leave space for possible trailing _EXIT_TRACE - int max_length = buffer_size-2; - struct { - PyFunctionObject *func; - PyCodeObject *code; - _Py_CODEUNIT *instr; - } trace_stack[TRACE_STACK_SIZE]; - int trace_stack_depth = 0; - int confidence = CONFIDENCE_RANGE; // Adjusted by branch instructions - bool jump_seen = false; #ifdef Py_DEBUG char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); @@ -576,410 +681,592 @@ translate_bytecode_to_trace( lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that } #endif - - DPRINTF(2, - "Optimizing %s (%s:%d) at byte offset %d\n", - PyUnicode_AsUTF8(code->co_qualname), - PyUnicode_AsUTF8(code->co_filename), - code->co_firstlineno, - 2 * INSTR_IP(initial_instr, code)); - ADD_TO_TRACE(_START_EXECUTOR, 0, (uintptr_t)instr, INSTR_IP(instr, code)); - ADD_TO_TRACE(_MAKE_WARM, 0, 0, 0); + _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; + _PyJitTracerState *tracer = _tstate->jit_tracer_state; + PyCodeObject *old_code = tracer->prev_state.instr_code; + bool progress_needed = (tracer->initial_state.chain_depth % MAX_CHAIN_DEPTH) == 0; + _PyJitUopBuffer *trace = &tracer->code_buffer; + + _Py_CODEUNIT *this_instr = tracer->prev_state.instr; + _Py_CODEUNIT *target_instr = this_instr; uint32_t target = 0; - for (;;) { - target = INSTR_IP(instr, code); - // Need space for _DEOPT - max_length--; + target = Py_IsNone((PyObject *)old_code) + ? (uint32_t)(target_instr - _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR) + : INSTR_IP(target_instr, old_code); + + // Rewind EXTENDED_ARG so that we see the whole thing. + // We must point to the first EXTENDED_ARG when deopting. + int oparg = tracer->prev_state.instr_oparg; + int opcode = this_instr->op.code; + int rewind_oparg = oparg; + while (rewind_oparg > 255) { + rewind_oparg >>= 8; + target--; + } + + if (opcode == ENTER_EXECUTOR) { + _PyExecutorObject *executor = old_code->co_executors->executors[oparg & 255]; + opcode = executor->vm_data.opcode; + oparg = (oparg & ~255) | executor->vm_data.oparg; + } + + if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] > 0) { + uint16_t backoff = (this_instr + 1)->counter.value_and_backoff; + // adaptive_counter_cooldown is a fresh specialization. + // trigger_backoff_counter is what we set during tracing. + // All tracing backoffs should be freshly specialized or untouched. + // If not, that indicates a deopt during tracing, and + // thus the "actual" instruction executed is not the one that is + // in the instruction stream, but rather the deopt. + // It's important we check for this, as some specializations might make + // no progress (they can immediately deopt after specializing). + // We do this to improve performance, as otherwise a compiled trace + // will just deopt immediately. + if (backoff != adaptive_counter_cooldown().value_and_backoff && + backoff != trigger_backoff_counter().value_and_backoff) { + OPT_STAT_INC(trace_immediately_deopts); + opcode = _PyOpcode_Deopt[opcode]; + } + } - uint32_t opcode = instr->op.code; - uint32_t oparg = instr->op.arg; + // Strange control-flow + bool has_dynamic_jump_taken = OPCODE_HAS_UNPREDICTABLE_JUMP(opcode) && + (next_instr != this_instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]); - if (!first && instr == initial_instr) { - // We have looped around to the start: - RESERVE(1); - ADD_TO_TRACE(_JUMP_TO_TOP, 0, 0, 0); - goto done; + /* Special case the first instruction, + * so that we can guarantee forward progress */ + if (progress_needed && uop_buffer_length(&tracer->code_buffer) < CODE_SIZE_NO_PROGRESS) { + if (OPCODE_HAS_EXIT(opcode) || OPCODE_HAS_DEOPT(opcode)) { + opcode = _PyOpcode_Deopt[opcode]; } + assert(!OPCODE_HAS_EXIT(opcode)); + assert(!OPCODE_HAS_DEOPT(opcode)); + } - DPRINTF(2, "%d: %s(%d)\n", target, _PyOpcode_OpName[opcode], oparg); + bool needs_guard_ip = OPCODE_HAS_NEEDS_GUARD_IP(opcode); + if (has_dynamic_jump_taken && !needs_guard_ip) { + DPRINTF(2, "Unsupported: dynamic jump taken %s\n", _PyOpcode_OpName[opcode]); + goto unsupported; + } - if (opcode == EXTENDED_ARG) { - instr++; - opcode = instr->op.code; - oparg = (oparg << 8) | instr->op.arg; - if (opcode == EXTENDED_ARG) { - instr--; - goto done; + int is_sys_tracing = (tstate->c_tracefunc != NULL) || (tstate->c_profilefunc != NULL); + if (is_sys_tracing) { + goto done; + } + + if (stop_tracing_opcode == _DEOPT) { + // gh-143183: It's important we rewind to the last known proper target. + // The current target might be garbage as stop tracing usually indicates + // we are in something that we can't trace. + DPRINTF(2, "Told to stop tracing\n"); + goto unsupported; + } + else if (stop_tracing_opcode != 0) { + assert(stop_tracing_opcode == _EXIT_TRACE); + ADD_TO_TRACE(stop_tracing_opcode, 0, 0, target); + goto done; + } + + DPRINTF(2, "%p %d: %s(%d) %d\n", old_code, target, _PyOpcode_OpName[opcode], oparg, needs_guard_ip); + +#ifdef Py_DEBUG + if (oparg > 255) { + assert(_Py_GetBaseCodeUnit(old_code, target).op.code == EXTENDED_ARG); + } +#endif + + // This happens when a recursive call happens that we can't trace. Such as Python -> C -> Python calls + // If we haven't guarded the IP, then it's untraceable. + if (frame != tracer->prev_state.instr_frame && !needs_guard_ip) { + DPRINTF(2, "Unsupported: unguardable jump taken\n"); + goto unsupported; + } + + if (oparg > 0xFFFF) { + DPRINTF(2, "Unsupported: oparg too large\n"); + unsupported: + { + _PyUOpInstruction *curr = uop_buffer_last(trace); + while (curr->opcode != _SET_IP && uop_buffer_length(trace) > 2) { + trace->next--; + curr = uop_buffer_last(trace); + } + if (curr->opcode == _SET_IP) { + int32_t old_target = (int32_t)uop_get_target(curr); + curr->opcode = _DEOPT; + curr->format = UOP_FORMAT_TARGET; + curr->target = old_target; } - } - if (opcode == ENTER_EXECUTOR) { - // We have a couple of options here. We *could* peek "underneath" - // this executor and continue tracing, which could give us a longer, - // more optimizeable trace (at the expense of lots of duplicated - // tier two code). Instead, we choose to just end here and stitch to - // the other trace, which allows a side-exit traces to rejoin the - // "main" trace periodically (and also helps protect us against - // pathological behavior where the amount of tier two code explodes - // for a medium-length, branchy code path). This seems to work - // better in practice, but in the future we could be smarter about - // what we do here: goto done; } - assert(opcode != ENTER_EXECUTOR && opcode != EXTENDED_ARG); - RESERVE_RAW(2, "_CHECK_VALIDITY"); - ADD_TO_TRACE(_CHECK_VALIDITY, 0, 0, target); - if (!OPCODE_HAS_NO_SAVE_IP(opcode)) { - RESERVE_RAW(2, "_SET_IP"); - ADD_TO_TRACE(_SET_IP, 0, (uintptr_t)instr, target); - } + } - /* Special case the first instruction, - * so that we can guarantee forward progress */ - if (first && progress_needed) { - assert(first); - if (OPCODE_HAS_EXIT(opcode) || OPCODE_HAS_DEOPT(opcode)) { - opcode = _PyOpcode_Deopt[opcode]; - } - assert(!OPCODE_HAS_EXIT(opcode)); - assert(!OPCODE_HAS_DEOPT(opcode)); - } + if (opcode == NOP) { + return 1; + } - if (OPCODE_HAS_EXIT(opcode)) { - // Make space for side exit and final _EXIT_TRACE: - RESERVE_RAW(2, "_EXIT_TRACE"); - max_length--; - } - if (OPCODE_HAS_ERROR(opcode)) { - // Make space for error stub and final _EXIT_TRACE: - RESERVE_RAW(2, "_ERROR_POP_N"); - max_length--; - } - switch (opcode) { - case POP_JUMP_IF_NONE: - case POP_JUMP_IF_NOT_NONE: - case POP_JUMP_IF_FALSE: - case POP_JUMP_IF_TRUE: - { - RESERVE(1); - int counter = instr[1].cache; - int bitcount = _Py_popcount32(counter); - int jump_likely = bitcount > 8; - /* If bitcount is 8 (half the jumps were taken), adjust confidence by 50%. - For values in between, adjust proportionally. */ - if (jump_likely) { - confidence = confidence * bitcount / 16; - } - else { - confidence = confidence * (16 - bitcount) / 16; - } - uint32_t uopcode = BRANCH_TO_GUARD[opcode - POP_JUMP_IF_FALSE][jump_likely]; - DPRINTF(2, "%d: %s(%d): counter=%04x, bitcount=%d, likely=%d, confidence=%d, uopcode=%s\n", - target, _PyOpcode_OpName[opcode], oparg, - counter, bitcount, jump_likely, confidence, _PyUOpName(uopcode)); - if (confidence < CONFIDENCE_CUTOFF) { - DPRINTF(2, "Confidence too low (%d < %d)\n", confidence, CONFIDENCE_CUTOFF); - OPT_STAT_INC(low_confidence); - goto done; - } - _Py_CODEUNIT *next_instr = instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; - _Py_CODEUNIT *target_instr = next_instr + oparg; - if (jump_likely) { - DPRINTF(2, "Jump likely (%04x = %d bits), continue at byte offset %d\n", - instr[1].cache, bitcount, 2 * INSTR_IP(target_instr, code)); - instr = target_instr; - ADD_TO_TRACE(uopcode, 0, 0, INSTR_IP(next_instr, code)); - goto top; - } - ADD_TO_TRACE(uopcode, 0, 0, INSTR_IP(target_instr, code)); - break; - } + if (opcode == JUMP_FORWARD) { + return 1; + } - case JUMP_BACKWARD: - case JUMP_BACKWARD_JIT: - ADD_TO_TRACE(_CHECK_PERIODIC, 0, 0, target); - _Py_FALLTHROUGH; - case JUMP_BACKWARD_NO_INTERRUPT: - { - instr += 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]] - (int)oparg; - if (jump_seen) { - OPT_STAT_INC(inner_loop); - DPRINTF(2, "JUMP_BACKWARD not to top ends trace\n"); - goto done; - } - jump_seen = true; - goto top; - } + if (opcode == EXTENDED_ARG) { + return 1; + } - case JUMP_FORWARD: - { - RESERVE(0); - // This will emit two _SET_IP instructions; leave it to the optimizer - instr += oparg; - break; - } + // Stop the trace if fitness has dropped below the exit quality threshold. + _PyJitTracerTranslatorState *ts = &tracer->translator_state; + int32_t eq = compute_exit_quality(target_instr, opcode, tracer); + DPRINTF(3, "Fitness check: %s(%d) fitness=%d, exit_quality=%d, depth=%d\n", + _PyOpcode_OpName[opcode], oparg, ts->fitness, eq, ts->frame_depth); - case RESUME: - /* Use a special tier 2 version of RESUME_CHECK to allow traces to - * start with RESUME_CHECK */ - ADD_TO_TRACE(_TIER2_RESUME_CHECK, 0, 0, target); - break; + if (ts->fitness < eq) { + // Heuristic exit: leave operand1=0 so the side exit increments chain_depth. + ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target); + OPT_STAT_INC(fitness_terminated_traces); + DPRINTF(2, "Fitness terminated: %s(%d) fitness=%d < exit_quality=%d\n", + _PyOpcode_OpName[opcode], oparg, ts->fitness, eq); + goto done; + } - default: - { - const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode]; - if (expansion->nuops > 0) { - // Reserve space for nuops (+ _SET_IP + _EXIT_TRACE) - int nuops = expansion->nuops; - RESERVE(nuops + 1); /* One extra for exit */ - int16_t last_op = expansion->uops[nuops-1].uop; - if (last_op == _RETURN_VALUE || last_op == _RETURN_GENERATOR || last_op == _YIELD_VALUE) { - // Check for trace stack underflow now: - // We can't bail e.g. in the middle of - // LOAD_CONST + _RETURN_VALUE. - if (trace_stack_depth == 0) { - DPRINTF(2, "Trace stack underflow\n"); - OPT_STAT_INC(trace_stack_underflow); - return 0; - } - } - uint32_t orig_oparg = oparg; // For OPARG_TOP/BOTTOM - for (int i = 0; i < nuops; i++) { - oparg = orig_oparg; - uint32_t uop = expansion->uops[i].uop; - uint64_t operand = 0; - // Add one to account for the actual opcode/oparg pair: - int offset = expansion->uops[i].offset + 1; - switch (expansion->uops[i].size) { - case OPARG_SIMPLE: - assert(opcode != JUMP_BACKWARD_NO_INTERRUPT && opcode != JUMP_BACKWARD); - break; - case OPARG_CACHE_1: - operand = read_u16(&instr[offset].cache); - break; - case OPARG_CACHE_2: - operand = read_u32(&instr[offset].cache); - break; - case OPARG_CACHE_4: - operand = read_u64(&instr[offset].cache); - break; - case OPARG_TOP: // First half of super-instr - oparg = orig_oparg >> 4; - break; - case OPARG_BOTTOM: // Second half of super-instr - oparg = orig_oparg & 0xF; - break; - case OPARG_SAVE_RETURN_OFFSET: // op=_SAVE_RETURN_OFFSET; oparg=return_offset - oparg = offset; - assert(uop == _SAVE_RETURN_OFFSET); - break; - case OPARG_REPLACED: - uop = _PyUOp_Replacements[uop]; - assert(uop != 0); -#ifdef Py_DEBUG - { - uint32_t next_inst = target + 1 + INLINE_CACHE_ENTRIES_FOR_ITER + (oparg > 255); - uint32_t jump_target = next_inst + oparg; - assert(_Py_GetBaseCodeUnit(code, jump_target).op.code == END_FOR); - assert(_Py_GetBaseCodeUnit(code, jump_target+1).op.code == POP_ITER); - } + // Snapshot remaining space so the later fitness charge reflects all buffer + // space this bytecode consumed, including reserved tail slots. + int32_t remaining_before = uop_buffer_remaining_space(trace); + + // One for possible _DEOPT, one because _CHECK_VALIDITY itself might _DEOPT + trace->end -= 2; + + const _PyOpcodeRecordSlotMap *record_slot_map = &_PyOpcode_RecordSlotMaps[opcode]; + + assert(opcode != ENTER_EXECUTOR && opcode != EXTENDED_ARG); + assert(!_PyErr_Occurred(tstate)); + + + if (OPCODE_HAS_EXIT(opcode)) { + // Make space for side exit + trace->end--; + } + if (OPCODE_HAS_ERROR(opcode)) { + // Make space for error stub + trace->end--; + } + if (OPCODE_HAS_DEOPT(opcode)) { + // Make space for side exit + trace->end--; + } + + // _GUARD_IP leads to an exit. + trace->end -= needs_guard_ip; + +#if Py_DEBUG + const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode]; + int space_needed = expansion->nuops + needs_guard_ip + 2 + (!OPCODE_HAS_NO_SAVE_IP(opcode)); + assert(uop_buffer_remaining_space(trace) > space_needed); #endif - break; - case OPERAND1_1: - assert(trace[trace_length-1].opcode == uop); - operand = read_u16(&instr[offset].cache); - trace[trace_length-1].operand1 = operand; - continue; - case OPERAND1_2: - assert(trace[trace_length-1].opcode == uop); - operand = read_u32(&instr[offset].cache); - trace[trace_length-1].operand1 = operand; - continue; - case OPERAND1_4: - assert(trace[trace_length-1].opcode == uop); - operand = read_u64(&instr[offset].cache); - trace[trace_length-1].operand1 = operand; - continue; - default: - fprintf(stderr, - "opcode=%d, oparg=%d; nuops=%d, i=%d; size=%d, offset=%d\n", - opcode, oparg, nuops, i, - expansion->uops[i].size, - expansion->uops[i].offset); - Py_FatalError("garbled expansion"); - } - if (uop == _RETURN_VALUE || uop == _RETURN_GENERATOR || uop == _YIELD_VALUE) { - TRACE_STACK_POP(); - /* Set the operand to the function or code object returned to, - * to assist optimization passes. (See _PUSH_FRAME below.) - */ - if (func != NULL) { - operand = (uintptr_t)func; - } - else if (code != NULL) { - operand = (uintptr_t)code | 1; - } - else { - operand = 0; - } - ADD_TO_TRACE(uop, oparg, operand, target); - DPRINTF(2, - "Returning to %s (%s:%d) at byte offset %d\n", - PyUnicode_AsUTF8(code->co_qualname), - PyUnicode_AsUTF8(code->co_filename), - code->co_firstlineno, - 2 * INSTR_IP(instr, code)); - goto top; - } + ADD_TO_TRACE(_CHECK_VALIDITY, 0, 0, target); - if (uop == _PUSH_FRAME) { - assert(i + 1 == nuops); - if (opcode == FOR_ITER_GEN || - opcode == LOAD_ATTR_PROPERTY || - opcode == BINARY_OP_SUBSCR_GETITEM || - opcode == SEND_GEN) - { - DPRINTF(2, "Bailing due to dynamic target\n"); - OPT_STAT_INC(unknown_callee); - return 0; - } - assert(_PyOpcode_Deopt[opcode] == CALL || _PyOpcode_Deopt[opcode] == CALL_KW); - int func_version_offset = - offsetof(_PyCallCache, func_version)/sizeof(_Py_CODEUNIT) - // Add one to account for the actual opcode/oparg pair: - + 1; - uint32_t func_version = read_u32(&instr[func_version_offset].cache); - PyCodeObject *new_code = NULL; - PyFunctionObject *new_func = - _PyFunction_LookupByVersion(func_version, (PyObject **) &new_code); - DPRINTF(2, "Function: version=%#x; new_func=%p, new_code=%p\n", - (int)func_version, new_func, new_code); - if (new_code != NULL) { - if (new_code == code) { - // Recursive call, bail (we could be here forever). - DPRINTF(2, "Bailing on recursive call to %s (%s:%d)\n", - PyUnicode_AsUTF8(new_code->co_qualname), - PyUnicode_AsUTF8(new_code->co_filename), - new_code->co_firstlineno); - OPT_STAT_INC(recursive_call); - ADD_TO_TRACE(uop, oparg, 0, target); - ADD_TO_TRACE(_EXIT_TRACE, 0, 0, 0); - goto done; - } - if (new_code->co_version != func_version) { - // func.__code__ was updated. - // Perhaps it may happen again, so don't bother tracing. - // TODO: Reason about this -- is it better to bail or not? - DPRINTF(2, "Bailing because co_version != func_version\n"); - ADD_TO_TRACE(uop, oparg, 0, target); - ADD_TO_TRACE(_EXIT_TRACE, 0, 0, 0); - goto done; - } - // Increment IP to the return address - instr += _PyOpcode_Caches[_PyOpcode_Deopt[opcode]] + 1; - TRACE_STACK_PUSH(); - _Py_BloomFilter_Add(dependencies, new_code); - /* Set the operand to the callee's function or code object, - * to assist optimization passes. - * We prefer setting it to the function (for remove_globals()) - * but if that's not available but the code is available, - * use the code, setting the low bit so the optimizer knows. - */ - if (new_func != NULL) { - operand = (uintptr_t)new_func; - } - else if (new_code != NULL) { - operand = (uintptr_t)new_code | 1; - } - else { - operand = 0; - } - ADD_TO_TRACE(uop, oparg, operand, target); - code = new_code; - func = new_func; - instr = _PyCode_CODE(code); - DPRINTF(2, - "Continuing in %s (%s:%d) at byte offset %d\n", - PyUnicode_AsUTF8(code->co_qualname), - PyUnicode_AsUTF8(code->co_filename), - code->co_firstlineno, - 2 * INSTR_IP(instr, code)); - goto top; - } - DPRINTF(2, "Bail, new_code == NULL\n"); - OPT_STAT_INC(unknown_callee); - return 0; - } + if (!OPCODE_HAS_NO_SAVE_IP(opcode)) { + ADD_TO_TRACE(_SET_IP, 0, (uintptr_t)target_instr, target); + } - if (uop == _BINARY_OP_INPLACE_ADD_UNICODE) { - assert(i + 1 == nuops); - _Py_CODEUNIT *next_instr = instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; - assert(next_instr->op.code == STORE_FAST); - operand = next_instr->op.arg; - // Skip the STORE_FAST: - instr++; - } + switch (opcode) { + case POP_JUMP_IF_NONE: + case POP_JUMP_IF_NOT_NONE: + case POP_JUMP_IF_FALSE: + case POP_JUMP_IF_TRUE: + { + _Py_CODEUNIT *computed_next_instr_without_modifiers = target_instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; + _Py_CODEUNIT *computed_next_instr = computed_next_instr_without_modifiers + (computed_next_instr_without_modifiers->op.code == NOT_TAKEN); + _Py_CODEUNIT *computed_jump_instr = computed_next_instr_without_modifiers + oparg; + assert(next_instr == computed_next_instr || next_instr == computed_jump_instr); + int jump_happened = target_instr[1].cache & 1; + assert(jump_happened ? (next_instr == computed_jump_instr) : (next_instr == computed_next_instr)); + uint32_t uopcode = BRANCH_TO_GUARD[opcode - POP_JUMP_IF_FALSE][jump_happened]; + ADD_TO_TRACE(uopcode, 0, 0, INSTR_IP(jump_happened ? computed_next_instr : computed_jump_instr, old_code)); + int bp = compute_branch_penalty(target_instr[1].cache); + tracer->translator_state.fitness -= bp; + DPRINTF(3, " branch penalty: -%d (history=0x%04x, taken=%d) -> fitness=%d\n", + bp, target_instr[1].cache, jump_happened, + tracer->translator_state.fitness); - // All other instructions - ADD_TO_TRACE(uop, oparg, operand, target); + break; + } + case JUMP_BACKWARD_JIT: + // This is possible as the JIT might have re-activated after it was disabled + case JUMP_BACKWARD_NO_JIT: + case JUMP_BACKWARD: + ADD_TO_TRACE(_CHECK_PERIODIC, 0, 0, target); + break; + case JUMP_BACKWARD_NO_INTERRUPT: + break; + + case RESUME: + case RESUME_CHECK: + case RESUME_CHECK_JIT: + /* Use a special tier 2 version of RESUME_CHECK to allow traces to + * start with RESUME_CHECK */ + ADD_TO_TRACE(_TIER2_RESUME_CHECK, 0, 0, target); + break; + default: + { + const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode]; + // Reserve space for nuops (+ _SET_IP + _EXIT_TRACE) + int nuops = expansion->nuops; + if (nuops == 0) { + DPRINTF(2, "Unsupported opcode %s\n", _PyOpcode_OpName[opcode]); + goto unsupported; + } + assert(nuops > 0); + uint32_t orig_oparg = oparg; // For OPARG_TOP/BOTTOM + uint32_t orig_target = target; + int record_idx = 0; + for (int i = 0; i < nuops; i++) { + oparg = orig_oparg; + target = orig_target; + uint32_t uop = expansion->uops[i].uop; + uint64_t operand = 0; + // Add one to account for the actual opcode/oparg pair: + int offset = expansion->uops[i].offset + 1; + switch (expansion->uops[i].size) { + case OPARG_SIMPLE: + assert(opcode != _JUMP_BACKWARD_NO_INTERRUPT && opcode != JUMP_BACKWARD); + break; + case OPARG_CACHE_1: + operand = read_u16(&this_instr[offset].cache); + break; + case OPARG_CACHE_2: + operand = read_u32(&this_instr[offset].cache); + break; + case OPARG_CACHE_4: + operand = read_u64(&this_instr[offset].cache); + break; + case OPARG_TOP: // First half of super-instr + assert(orig_oparg <= 255); + oparg = orig_oparg >> 4; + break; + case OPARG_BOTTOM: // Second half of super-instr + assert(orig_oparg <= 255); + oparg = orig_oparg & 0xF; + break; + case OPARG_SAVE_RETURN_OFFSET: // op=_SAVE_RETURN_OFFSET; oparg=return_offset + oparg = offset; + assert(uop == _SAVE_RETURN_OFFSET); + break; + case OPARG_REPLACED: + uop = _PyUOp_Replacements[uop]; + assert(uop != 0); + + uint32_t next_inst = target + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; + if (uop == _TIER2_RESUME_CHECK) { + target = next_inst; + } + else { + int extended_arg = orig_oparg > 255; + uint32_t jump_target = next_inst + orig_oparg + extended_arg; + /* Jump must be to an "END" either END_FOR or END_SEND */ + assert(( + _Py_GetBaseCodeUnit(old_code, jump_target).op.code == END_FOR && + _Py_GetBaseCodeUnit(old_code, jump_target+1).op.code == POP_ITER + ) + || + _Py_GetBaseCodeUnit(old_code, jump_target).op.code == END_SEND + ); + if (is_for_iter_test[uop]) { + target = jump_target + 1; + } + } + break; + case OPERAND1_1: + assert(uop_buffer_last(trace)->opcode == uop); + operand = read_u16(&this_instr[offset].cache); + uop_buffer_last(trace)->operand1 = operand; + continue; + case OPERAND1_2: + assert(uop_buffer_last(trace)->opcode == uop); + operand = read_u32(&this_instr[offset].cache); + uop_buffer_last(trace)->operand1 = operand; + continue; + case OPERAND1_4: + assert(uop_buffer_last(trace)->opcode == uop); + operand = read_u64(&this_instr[offset].cache); + uop_buffer_last(trace)->operand1 = operand; + continue; + default: + fprintf(stderr, + "opcode=%d, oparg=%d; nuops=%d, i=%d; size=%d, offset=%d\n", + opcode, oparg, nuops, i, + expansion->uops[i].size, + expansion->uops[i].offset); + Py_FatalError("garbled expansion"); + } + if (uop == _BINARY_OP_INPLACE_ADD_UNICODE) { + assert(i + 1 == nuops); + _Py_CODEUNIT *next = target_instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; + assert(next->op.code == STORE_FAST); + operand = next->op.arg; + } + else if (uop == _PUSH_FRAME) { + _PyJitTracerTranslatorState *ts_depth = &tracer->translator_state; + ts_depth->frame_depth++; + assert(ts_depth->frame_depth < MAX_ABSTRACT_FRAME_DEPTH); + int32_t frame_penalty = compute_frame_penalty(tstate->interp->opt_config.fitness_initial); + ts_depth->fitness -= frame_penalty; + DPRINTF(3, " _PUSH_FRAME: depth=%d, penalty=-%d -> fitness=%d\n", + ts_depth->frame_depth, frame_penalty, + ts_depth->fitness); + } + else if (uop == _RETURN_VALUE || uop == _RETURN_GENERATOR || uop == _YIELD_VALUE) { + _PyJitTracerTranslatorState *ts_depth = &tracer->translator_state; + int32_t frame_penalty = compute_frame_penalty(tstate->interp->opt_config.fitness_initial); + if (ts_depth->frame_depth <= 0) { + // Returning past the traced root is normal for guarded + // caller continuation. Charge a small penalty so these + // paths still terminate. + int32_t underflow_penalty = frame_penalty / 4; + ts_depth->fitness -= underflow_penalty; + DPRINTF(3, " %s: underflow penalty=-%d -> fitness=%d\n", + _PyOpcode_uop_name[uop], underflow_penalty, + ts_depth->fitness); + } + else { + // Symmetric with push: net-zero frame impact. + ts_depth->fitness += frame_penalty; + ts_depth->frame_depth--; + DPRINTF(3, " %s: return reward=+%d, depth=%d -> fitness=%d\n", + _PyOpcode_uop_name[uop], frame_penalty, + ts_depth->frame_depth, + ts_depth->fitness); } - break; } - DPRINTF(2, "Unsupported opcode %s\n", _PyOpcode_OpName[opcode]); - OPT_UNSUPPORTED_OPCODE(opcode); - goto done; // Break out of loop - } // End default - - } // End switch (opcode) + else if (_PyUop_Flags[uop] & HAS_RECORDS_VALUE_FLAG) { + assert(record_idx < record_slot_map->count); + uint8_t record_slot = record_slot_map->slots[record_idx]; + assert(record_slot < tracer->prev_state.recorded_count); + PyObject *recorded_value = tracer->prev_state.recorded_values[record_slot]; + tracer->prev_state.recorded_values[record_slot] = NULL; + if ((record_slot_map->transform_mask & (1u << record_idx)) && + recorded_value != NULL) { + recorded_value = _PyOpcode_RecordTransformValue(uop, recorded_value); + } + record_idx++; + operand = (uintptr_t)recorded_value; + } + // All other instructions + ADD_TO_TRACE(uop, oparg, operand, target); + } + break; + } // End default - instr++; - // Add cache size for opcode - instr += _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; + } // End switch (opcode) - if (opcode == CALL_LIST_APPEND) { - assert(instr->op.code == POP_TOP); - instr++; + if (needs_guard_ip) { + int last_opcode = uop_buffer_last(trace)->opcode; + uint16_t guard_ip = guard_ip_uop[last_opcode]; + if (guard_ip == 0) { + DPRINTF(1, "Unknown uop needing guard ip %s\n", _PyOpcode_uop_name[last_opcode]); + Py_UNREACHABLE(); } - top: - // Jump here after _PUSH_FRAME or likely branches. - first = false; - } // End for (;;) - + PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + Py_INCREF(code); + ADD_TO_TRACE(_RECORD_CODE, 0, (uintptr_t)code, 0); + ADD_TO_TRACE(guard_ip, 0, (uintptr_t)next_instr, 0); + if (PyCode_Check(code)) { + /* Record stack depth, in operand1 */ + int stack_depth = (int)(frame->stackpointer - _PyFrame_Stackbase(frame)); + uop_buffer_last(trace)->operand1 = stack_depth; + ADD_TO_TRACE(guard_code_version_uop[last_opcode], 0, ((PyCodeObject *)code)->co_version, 0); + } + } + // Loop back to the start + int is_first_instr = tracer->initial_state.close_loop_instr == next_instr || + tracer->initial_state.start_instr == next_instr; + if (is_first_instr && uop_buffer_length(trace) > CODE_SIZE_NO_PROGRESS) { + if (needs_guard_ip) { + ADD_TO_TRACE(_SET_IP, 0, (uintptr_t)next_instr, 0); + } + ADD_TO_TRACE(_JUMP_TO_TOP, 0, 0, 0); + goto done; + } + // Charge fitness by trace-buffer capacity consumed for this bytecode, + // including both emitted uops and tail reservations. + { + int32_t slots_used = remaining_before - uop_buffer_remaining_space(trace); + tracer->translator_state.fitness -= slots_used; + DPRINTF(3, " per-insn cost: -%d -> fitness=%d\n", slots_used, + tracer->translator_state.fitness); + } + DPRINTF(2, "Trace continuing (fitness=%d)\n", tracer->translator_state.fitness); + return 1; done: - while (trace_stack_depth > 0) { - TRACE_STACK_POP(); - } - assert(code == initial_code); - // Skip short traces where we can't even translate a single instruction: - if (first) { - OPT_STAT_INC(trace_too_short); - DPRINTF(2, - "No trace for %s (%s:%d) at byte offset %d (no progress)\n", - PyUnicode_AsUTF8(code->co_qualname), - PyUnicode_AsUTF8(code->co_filename), - code->co_firstlineno, - 2 * INSTR_IP(initial_instr, code)); + DPRINTF(2, "Trace done\n"); + if (!is_terminator(uop_buffer_last(trace))) { + ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target); + } + return 0; +} + +// Returns 0 for do not enter tracing, 1 on enter tracing. +// gh-142543: inlining this function causes stack overflows +Py_NO_INLINE int +_PyJit_TryInitializeTracing( + PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *curr_instr, + _Py_CODEUNIT *start_instr, _Py_CODEUNIT *close_loop_instr, _PyStackRef *stack_pointer, int chain_depth, + _PyExitData *exit, int oparg, _PyExecutorObject *current_executor) +{ + _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; + if (_tstate->jit_tracer_state == NULL) { + _tstate->jit_tracer_state = (_PyJitTracerState *)_PyObject_VirtualAlloc(sizeof(_PyJitTracerState)); + if (_tstate->jit_tracer_state == NULL) { + // Don't error, just go to next instruction. + return 0; + } + _tstate->jit_tracer_state->is_tracing = false; + } + _PyJitTracerState *tracer = _tstate->jit_tracer_state; + // A recursive trace. + if (tracer->is_tracing) { return 0; } - if (!is_terminator(&trace[trace_length-1])) { - /* Allow space for _EXIT_TRACE */ - max_length += 2; - ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target); + if (oparg > 0xFFFF) { + return 0; + } + PyObject *func = PyStackRef_AsPyObjectBorrow(frame->f_funcobj); + if (func == NULL || !PyFunction_Check(func)) { + return 0; + } + PyCodeObject *code = _PyFrame_GetCode(frame); +#ifdef Py_DEBUG + char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); + int lltrace = 0; + if (python_lltrace != NULL && *python_lltrace >= '0') { + lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that + } + DPRINTF(2, + "Tracing %s (%s:%d) at byte offset %d at chain depth %d\n", + PyUnicode_AsUTF8(code->co_qualname), + PyUnicode_AsUTF8(code->co_filename), + code->co_firstlineno, + 2 * INSTR_IP(close_loop_instr, code), + chain_depth); +#endif + /* Set up tracing buffer*/ + _PyJitUopBuffer *trace = &tracer->code_buffer; + uop_buffer_init(trace, &tracer->uop_array[0], UOP_MAX_TRACE_LENGTH); + _PyJitTracerTranslatorState *ts = &tracer->translator_state; + ts->fitness = tstate->interp->opt_config.fitness_initial; + ts->frame_depth = 0; + ADD_TO_TRACE(_START_EXECUTOR, 0, (uintptr_t)start_instr, INSTR_IP(start_instr, code)); + ADD_TO_TRACE(_MAKE_WARM, 0, 0, 0); + + tracer->initial_state.start_instr = start_instr; + tracer->initial_state.close_loop_instr = close_loop_instr; + tracer->initial_state.code = (PyCodeObject *)Py_NewRef(code); + tracer->initial_state.func = (PyFunctionObject *)Py_NewRef(func); + tracer->initial_state.executor = (_PyExecutorObject *)Py_XNewRef(current_executor); + tracer->initial_state.exit = exit; + tracer->initial_state.stack_depth = (int)(stack_pointer - _PyFrame_Stackbase(frame)); + tracer->initial_state.chain_depth = chain_depth; + tracer->prev_state.instr_code = (PyCodeObject *)Py_NewRef(_PyFrame_GetCode(frame)); + tracer->prev_state.instr = curr_instr; + tracer->prev_state.instr_frame = frame; + tracer->prev_state.instr_oparg = oparg; + tracer->prev_state.instr_stacklevel = tracer->initial_state.stack_depth; + tracer->prev_state.recorded_count = 0; + for (int i = 0; i < MAX_RECORDED_VALUES; i++) { + tracer->prev_state.recorded_values[i] = NULL; + } + const _PyOpcodeRecordEntry *record_entry = &_PyOpcode_RecordEntries[curr_instr->op.code]; + for (int i = 0; i < record_entry->count; i++) { + _Py_RecordFuncPtr record_func = _PyOpcode_RecordFunctions[record_entry->indices[i]]; + record_func(frame, stack_pointer, oparg, &tracer->prev_state.recorded_values[i]); + } + tracer->prev_state.recorded_count = record_entry->count; + assert(curr_instr->op.code == JUMP_BACKWARD_JIT || curr_instr->op.code == RESUME_CHECK_JIT || (exit != NULL)); + tracer->initial_state.jump_backward_instr = curr_instr; + + DPRINTF(3, "Fitness init: chain_depth=%d, fitness=%d\n", + chain_depth, ts->fitness); + + tracer->is_tracing = true; + return 1; +} + +Py_NO_INLINE void +_PyJit_FinalizeTracing(PyThreadState *tstate, int err) +{ + _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; + _PyJitTracerState *tracer = _tstate->jit_tracer_state; + // Deal with backoffs + assert(tracer != NULL); + _PyExitData *exit = tracer->initial_state.exit; + if (exit == NULL) { + // We hold a strong reference to the code object, so the instruction won't be freed. + if (err <= 0) { + _Py_BackoffCounter counter = tracer->initial_state.jump_backward_instr[1].counter; + tracer->initial_state.jump_backward_instr[1].counter = restart_backoff_counter(counter); + } + else { + if (tracer->initial_state.jump_backward_instr[0].op.code == JUMP_BACKWARD_JIT) { + tracer->initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter(&tstate->interp->opt_config); + } + else { + tracer->initial_state.jump_backward_instr[1].counter = initial_resume_backoff_counter(&tstate->interp->opt_config); + } + } + } + else if (tracer->initial_state.executor->vm_data.valid) { + // Likewise, we hold a strong reference to the executor containing this exit, so the exit is guaranteed + // to be valid to access. + if (err <= 0) { + exit->temperature = restart_backoff_counter(exit->temperature); + } + else { + exit->temperature = initial_temperature_backoff_counter(&tstate->interp->opt_config); + } + } + // Clear all recorded values + _PyJitUopBuffer *buffer = &tracer->code_buffer; + for (_PyUOpInstruction *inst = buffer->start; inst < buffer->next; inst++) { + if (_PyUop_Flags[inst->opcode] & HAS_RECORDS_VALUE_FLAG) { + Py_XDECREF((PyObject *)(uintptr_t)inst->operand0); + } + } + Py_CLEAR(tracer->initial_state.code); + Py_CLEAR(tracer->initial_state.func); + Py_CLEAR(tracer->initial_state.executor); + Py_CLEAR(tracer->prev_state.instr_code); + for (int i = 0; i < MAX_RECORDED_VALUES; i++) { + Py_CLEAR(tracer->prev_state.recorded_values[i]); + } + tracer->prev_state.recorded_count = 0; + uop_buffer_init(buffer, &tracer->uop_array[0], UOP_MAX_TRACE_LENGTH); + tracer->is_tracing = false; +} + +bool +_PyJit_EnterExecutorShouldStopTracing(int og_opcode) +{ + // Continue tracing (skip over the executor). If it's a RESUME + // trace to form longer, more optimizeable traces. + // We want to trace over RESUME traces. Otherwise, functions with lots of RESUME + // end up with many fragmented traces which perform badly. + // See for example, the richards benchmark in pyperformance. + // For consideration: We may want to consider tracing over side traces + // inserted into bytecode as well in the future. + return og_opcode == RESUME_CHECK_JIT; +} + +void +_PyJit_TracerFree(_PyThreadStateImpl *_tstate) +{ + if (_tstate->jit_tracer_state != NULL) { + _PyObject_VirtualFree(_tstate->jit_tracer_state, sizeof(_PyJitTracerState)); + _tstate->jit_tracer_state = NULL; } - DPRINTF(1, - "Created a proto-trace for %s (%s:%d) at byte offset %d -- length %d\n", - PyUnicode_AsUTF8(code->co_qualname), - PyUnicode_AsUTF8(code->co_filename), - code->co_firstlineno, - 2 * INSTR_IP(initial_instr, code), - trace_length); - OPT_HIST(trace_length, trace_length_hist); - return trace_length; } #undef RESERVE -#undef RESERVE_RAW #undef INSTR_IP #undef ADD_TO_TRACE #undef DPRINTF @@ -995,22 +1282,44 @@ count_exits(_PyUOpInstruction *buffer, int length) { int exit_count = 0; for (int i = 0; i < length; i++) { - int opcode = buffer[i].opcode; - if (opcode == _EXIT_TRACE) { + uint16_t base_opcode = _PyUop_Uncached[buffer[i].opcode]; + if (base_opcode == _EXIT_TRACE || base_opcode == _DYNAMIC_EXIT) { exit_count++; } } return exit_count; } -static void make_exit(_PyUOpInstruction *inst, int opcode, int target) +/* The number of cached registers at any exit (`EXIT_IF` or `DEOPT_IF`) + * This is the number of cached at entries at start, unless the uop is + * marked as `exit_depth_is_output` in which case it is the number of + * cached entries at the end */ +static int +get_cached_entries_for_side_exit(_PyUOpInstruction *inst) +{ + // Maybe add another generated table for this? + int base_opcode = _PyUop_Uncached[inst->opcode]; + assert(base_opcode != 0); + for (int i = 0; i <= MAX_CACHED_REGISTER; i++) { + const _PyUopTOSentry *entry = &_PyUop_Caching[base_opcode].entries[i]; + if (entry->opcode == inst->opcode) { + return entry->exit; + } + } + Py_UNREACHABLE(); +} + +static void make_exit(_PyUOpInstruction *inst, int opcode, int target, bool is_control_flow) { + assert(opcode > MAX_UOP_ID && opcode <= MAX_UOP_REGS_ID); inst->opcode = opcode; inst->oparg = 0; inst->operand0 = 0; inst->format = UOP_FORMAT_TARGET; inst->target = target; + inst->operand1 = is_control_flow; #ifdef Py_STATS + inst->fitness = 0; inst->execution_count = 0; #endif } @@ -1041,21 +1350,28 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) int next_spare = length; for (int i = 0; i < length; i++) { _PyUOpInstruction *inst = &buffer[i]; - int opcode = inst->opcode; + int base_opcode = _PyUop_Uncached[inst->opcode]; + assert(inst->opcode != _NOP); int32_t target = (int32_t)uop_get_target(inst); - if (_PyUop_Flags[opcode] & (HAS_EXIT_FLAG | HAS_DEOPT_FLAG)) { - uint16_t exit_op = (_PyUop_Flags[opcode] & HAS_EXIT_FLAG) ? - _EXIT_TRACE : _DEOPT; + uint16_t exit_flags = _PyUop_Flags[base_opcode] & (HAS_EXIT_FLAG | HAS_DEOPT_FLAG | HAS_PERIODIC_FLAG); + if (exit_flags) { + uint16_t base_exit_op = _EXIT_TRACE; + if (exit_flags & HAS_DEOPT_FLAG) { + base_exit_op = _DEOPT; + } + else if (exit_flags & HAS_PERIODIC_FLAG) { + base_exit_op = _HANDLE_PENDING_AND_DEOPT; + } int32_t jump_target = target; - if (is_for_iter_test[opcode]) { - /* Target the POP_TOP immediately after the END_FOR, - * leaving only the iterator on the stack. */ - int extended_arg = inst->oparg > 255; - int32_t next_inst = target + 1 + INLINE_CACHE_ENTRIES_FOR_ITER + extended_arg; - jump_target = next_inst + inst->oparg + 1; + if (dynamic_exit_uop[base_opcode]) { + base_exit_op = _DYNAMIC_EXIT; } + int exit_depth = get_cached_entries_for_side_exit(inst); + assert(_PyUop_Caching[base_exit_op].entries[exit_depth].opcode > 0); + int16_t exit_op = _PyUop_Caching[base_exit_op].entries[exit_depth].opcode; + bool is_control_flow = (base_opcode == _GUARD_IS_FALSE_POP || base_opcode == _GUARD_IS_TRUE_POP || is_for_iter_test[base_opcode]); if (jump_target != current_jump_target || current_exit_op != exit_op) { - make_exit(&buffer[next_spare], exit_op, jump_target); + make_exit(&buffer[next_spare], exit_op, jump_target, is_control_flow); current_exit_op = exit_op; current_jump_target = jump_target; current_jump = next_spare; @@ -1064,14 +1380,14 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) buffer[i].jump_target = current_jump; buffer[i].format = UOP_FORMAT_JUMP; } - if (_PyUop_Flags[opcode] & HAS_ERROR_FLAG) { - int popped = (_PyUop_Flags[opcode] & HAS_ERROR_NO_POP_FLAG) ? - 0 : _PyUop_num_popped(opcode, inst->oparg); + if (_PyUop_Flags[base_opcode] & HAS_ERROR_FLAG) { + int popped = (_PyUop_Flags[base_opcode] & HAS_ERROR_NO_POP_FLAG) ? + 0 : _PyUop_num_popped(base_opcode, inst->oparg); if (target != current_error_target || popped != current_popped) { current_popped = popped; current_error = next_spare; current_error_target = target; - make_exit(&buffer[next_spare], _ERROR_POP_N, 0); + make_exit(&buffer[next_spare], _ERROR_POP_N_r00, 0, false); buffer[next_spare].operand0 = target; next_spare++; } @@ -1081,8 +1397,8 @@ prepare_for_execution(_PyUOpInstruction *buffer, int length) buffer[i].jump_target = 0; } } - if (opcode == _JUMP_TO_TOP) { - assert(buffer[0].opcode == _START_EXECUTOR); + if (base_opcode == _JUMP_TO_TOP) { + assert(_PyUop_Uncached[buffer[0].opcode] == _START_EXECUTOR); buffer[i].format = UOP_FORMAT_JUMP; buffer[i].jump_target = 1; } @@ -1103,6 +1419,7 @@ allocate_executor(int exit_count, int length) res->trace = (_PyUOpInstruction *)(res->exits + exit_count); res->code_size = length; res->exit_count = exit_count; + res->jit_registration = NULL; return res; } @@ -1129,21 +1446,26 @@ sanity_check(_PyExecutorObject *executor) } bool ended = false; uint32_t i = 0; - CHECK(executor->trace[0].opcode == _START_EXECUTOR); + CHECK(_PyUop_Uncached[executor->trace[0].opcode] == _START_EXECUTOR || + _PyUop_Uncached[executor->trace[0].opcode] == _COLD_EXIT || + _PyUop_Uncached[executor->trace[0].opcode] == _COLD_DYNAMIC_EXIT); for (; i < executor->code_size; i++) { const _PyUOpInstruction *inst = &executor->trace[i]; uint16_t opcode = inst->opcode; - CHECK(opcode <= MAX_UOP_ID); - CHECK(_PyOpcode_uop_name[opcode] != NULL); + uint16_t base_opcode = _PyUop_Uncached[opcode]; + CHECK(opcode > MAX_UOP_ID); + CHECK(opcode <= MAX_UOP_REGS_ID); + CHECK(base_opcode <= MAX_UOP_ID); + CHECK(base_opcode != 0); switch(inst->format) { case UOP_FORMAT_TARGET: - CHECK(target_unused(opcode)); + CHECK(target_unused(base_opcode)); break; case UOP_FORMAT_JUMP: CHECK(inst->jump_target < executor->code_size); break; } - if (_PyUop_Flags[opcode] & HAS_ERROR_FLAG) { + if (_PyUop_Flags[base_opcode] & HAS_ERROR_FLAG) { CHECK(inst->format == UOP_FORMAT_JUMP); CHECK(inst->error_target < executor->code_size); } @@ -1156,11 +1478,13 @@ sanity_check(_PyExecutorObject *executor) CHECK(ended); for (; i < executor->code_size; i++) { const _PyUOpInstruction *inst = &executor->trace[i]; - uint16_t opcode = inst->opcode; + uint16_t base_opcode = _PyUop_Uncached[inst->opcode]; CHECK( - opcode == _DEOPT || - opcode == _EXIT_TRACE || - opcode == _ERROR_POP_N); + base_opcode == _DEOPT || + base_opcode == _HANDLE_PENDING_AND_DEOPT || + base_opcode == _EXIT_TRACE || + base_opcode == _ERROR_POP_N || + base_opcode == _DYNAMIC_EXIT); } } @@ -1173,7 +1497,7 @@ sanity_check(_PyExecutorObject *executor) * and not a NOP. */ static _PyExecutorObject * -make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies) +make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies) { int exit_count = count_exits(buffer, length); _PyExecutorObject *executor = allocate_executor(exit_count, length); @@ -1182,30 +1506,44 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil } /* Initialize exits */ + int chain_depth = tstate->jit_tracer_state->initial_state.chain_depth; + _PyExecutorObject *cold = _PyExecutor_GetColdExecutor(); + _PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor(); + cold->vm_data.chain_depth = chain_depth; + PyInterpreterState *interp = tstate->base.interp; for (int i = 0; i < exit_count; i++) { - executor->exits[i].executor = NULL; - executor->exits[i].temperature = initial_temperature_backoff_counter(); + executor->exits[i].index = i; + executor->exits[i].temperature = initial_temperature_backoff_counter(&interp->opt_config); } int next_exit = exit_count-1; _PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length]; - assert(buffer[0].opcode == _START_EXECUTOR); + assert(_PyUop_Uncached[buffer[0].opcode] == _START_EXECUTOR); buffer[0].operand0 = (uint64_t)executor; for (int i = length-1; i >= 0; i--) { - int opcode = buffer[i].opcode; + uint16_t base_opcode = _PyUop_Uncached[buffer[i].opcode]; dest--; *dest = buffer[i]; - assert(opcode != _POP_JUMP_IF_FALSE && opcode != _POP_JUMP_IF_TRUE); - if (opcode == _EXIT_TRACE) { + if (base_opcode == _EXIT_TRACE || base_opcode == _DYNAMIC_EXIT) { _PyExitData *exit = &executor->exits[next_exit]; exit->target = buffer[i].target; dest->operand0 = (uint64_t)exit; + exit->executor = base_opcode == _EXIT_TRACE ? cold : cold_dynamic; + exit->is_dynamic = (char)(base_opcode == _DYNAMIC_EXIT); + exit->is_control_flow = (char)buffer[i].operand1; next_exit--; } } assert(next_exit == -1); assert(dest == executor->trace); - assert(dest->opcode == _START_EXECUTOR); - _Py_ExecutorInit(executor, dependencies); + assert(_PyUop_Uncached[dest->opcode] == _START_EXECUTOR); + // Note: we MUST track it here before any Py_DECREF(executor) or + // linking of executor. Otherwise, the GC tries to untrack a + // still untracked object during dealloc. + _PyObject_GC_TRACK(executor); + if (_Py_ExecutorInit(executor, dependencies) < 0) { + Py_DECREF(executor); + return NULL; + } #ifdef Py_DEBUG char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); int lltrace = 0; @@ -1224,17 +1562,15 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil #endif #ifdef _Py_JIT executor->jit_code = NULL; - executor->jit_side_entry = NULL; executor->jit_size = 0; - // This is initialized to true so we can prevent the executor + // This is initialized to false so we can prevent the executor // from being immediately detected as cold and invalidated. - executor->vm_data.warm = true; + executor->vm_data.cold = false; if (_PyJIT_Compile(executor, executor->trace, length)) { Py_DECREF(executor); return NULL; } #endif - _PyObject_GC_TRACK(executor); return executor; } @@ -1258,33 +1594,84 @@ int effective_trace_length(_PyUOpInstruction *buffer, int length) } #endif + +static int +stack_allocate(_PyUOpInstruction *buffer, _PyUOpInstruction *output, int length) +{ + assert(buffer[0].opcode == _START_EXECUTOR); + /* The input buffer and output buffers will overlap. + Make sure that we can move instructions to the output + without overwriting the input. */ + if (buffer == output) { + // This can only happen if optimizer has not been run + for (int i = 0; i < length; i++) { + buffer[i + UOP_MAX_TRACE_LENGTH] = buffer[i]; + } + buffer += UOP_MAX_TRACE_LENGTH; + } + else { + assert(output + UOP_MAX_TRACE_LENGTH == buffer); + } + int depth = 0; + _PyUOpInstruction *write = output; + for (int i = 0; i < length; i++) { + int uop = buffer[i].opcode; + if (uop == _NOP) { + continue; + } + int new_depth = _PyUop_Caching[uop].best[depth]; + if (new_depth != depth) { + write->opcode = _PyUop_SpillsAndReloads[depth][new_depth]; + assert(write->opcode != 0); + write->format = UOP_FORMAT_TARGET; + write->oparg = 0; + write->target = 0; + write++; + depth = new_depth; + } + *write = buffer[i]; + uint16_t new_opcode = _PyUop_Caching[uop].entries[depth].opcode; + assert(new_opcode != 0); + write->opcode = new_opcode; + write++; + depth = _PyUop_Caching[uop].entries[depth].output; + } + return (int)(write - output); +} + static int uop_optimize( _PyInterpreterFrame *frame, - _Py_CODEUNIT *instr, + PyThreadState *tstate, _PyExecutorObject **exec_ptr, - int curr_stackentries, bool progress_needed) { - _PyBloomFilter dependencies; - _Py_BloomFilter_Init(&dependencies); - _PyUOpInstruction buffer[UOP_MAX_TRACE_LENGTH]; + _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; + assert(_tstate->jit_tracer_state != NULL); + _PyUOpInstruction *buffer = _tstate->jit_tracer_state->code_buffer.start; OPT_STAT_INC(attempts); - int length = translate_bytecode_to_trace(frame, instr, buffer, UOP_MAX_TRACE_LENGTH, &dependencies, progress_needed); - if (length <= 0) { - // Error or nothing translated - return length; + bool is_noopt = !tstate->interp->opt_config.uops_optimize_enabled; + int curr_stackentries = _tstate->jit_tracer_state->initial_state.stack_depth; + int length = uop_buffer_length(&_tstate->jit_tracer_state->code_buffer); + if (length <= CODE_SIZE_NO_PROGRESS) { + return 0; } + assert(length > 0); assert(length < UOP_MAX_TRACE_LENGTH); OPT_STAT_INC(traces_created); - char *env_var = Py_GETENV("PYTHON_UOPS_OPTIMIZE"); - if (env_var == NULL || *env_var == '\0' || *env_var > '0') { - length = _Py_uop_analyze_and_optimize(frame, buffer, - length, - curr_stackentries, &dependencies); + + _PyBloomFilter dependencies; + _Py_BloomFilter_Init(&dependencies); + if (!is_noopt) { + _PyUOpInstruction *output = &_tstate->jit_tracer_state->uop_array[UOP_MAX_TRACE_LENGTH]; + length = _Py_uop_analyze_and_optimize( + _tstate, buffer, length, curr_stackentries, + output, &dependencies); + if (length <= 0) { return length; } + buffer = output; } assert(length < UOP_MAX_TRACE_LENGTH); assert(length >= 1); @@ -1296,19 +1683,39 @@ uop_optimize( buffer[pc].opcode = opcode + oparg + 1 - _PyUop_Replication[opcode].start; assert(strncmp(_PyOpcode_uop_name[buffer[pc].opcode], _PyOpcode_uop_name[opcode], strlen(_PyOpcode_uop_name[opcode])) == 0); } + else if (_PyUop_Flags[opcode] & HAS_RECORDS_VALUE_FLAG) { + Py_XDECREF((PyObject *)(uintptr_t)buffer[pc].operand0); + buffer[pc].opcode = _NOP; + } else if (is_terminator(&buffer[pc])) { break; } assert(_PyOpcode_uop_name[buffer[pc].opcode]); } + // We've cleaned up the references in the buffer, so discard the code buffer + // to avoid doing it again during tracer cleanup + _PyJitUopBuffer *code_buffer = &_tstate->jit_tracer_state->code_buffer; + code_buffer->next = code_buffer->start; + OPT_HIST(effective_trace_length(buffer, length), optimized_trace_length_hist); + _PyUOpInstruction *output = &_tstate->jit_tracer_state->uop_array[0]; + length = stack_allocate(buffer, output, length); + buffer = output; length = prepare_for_execution(buffer, length); assert(length <= UOP_MAX_TRACE_LENGTH); - _PyExecutorObject *executor = make_executor_from_uops(buffer, length, &dependencies); + _PyExecutorObject *executor = make_executor_from_uops( + _tstate, buffer, length, &dependencies); if (executor == NULL) { return -1; } assert(length <= UOP_MAX_TRACE_LENGTH); + + // Check executor coldness + // It's okay if this ends up going negative. + if (--tstate->interp->executor_creation_counter == 0) { + _Py_set_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT); + } + *exec_ptr = executor; return 1; } @@ -1318,148 +1725,122 @@ uop_optimize( * Executor management ****************************************/ -/* We use a bloomfilter with k = 6, m = 256 - * The choice of k and the following constants - * could do with a more rigorous analysis, - * but here is a simple analysis: - * - * We want to keep the false positive rate low. - * For n = 5 (a trace depends on 5 objects), - * we expect 30 bits set, giving a false positive - * rate of (30/256)**6 == 2.5e-6 which is plenty - * good enough. - * - * However with n = 10 we expect 60 bits set (worst case), - * giving a false positive of (60/256)**6 == 0.0001 - * - * We choose k = 6, rather than a higher number as - * it means the false positive rate grows slower for high n. - * - * n = 5, k = 6 => fp = 2.6e-6 - * n = 5, k = 8 => fp = 3.5e-7 - * n = 10, k = 6 => fp = 1.6e-4 - * n = 10, k = 8 => fp = 0.9e-4 - * n = 15, k = 6 => fp = 0.18% - * n = 15, k = 8 => fp = 0.23% - * n = 20, k = 6 => fp = 1.1% - * n = 20, k = 8 => fp = 2.3% - * - * The above analysis assumes perfect hash functions, - * but those don't exist, so the real false positive - * rates may be worse. - */ - -#define K 6 - -#define SEED 20221211 +static int +link_executor(_PyExecutorObject *executor, const _PyBloomFilter *bloom) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (interp->executor_count == interp->executor_capacity) { + size_t new_cap = interp->executor_capacity ? interp->executor_capacity * 2 : 64; + _PyBloomFilter *new_blooms = PyMem_Realloc( + interp->executor_blooms, new_cap * sizeof(_PyBloomFilter)); + if (new_blooms == NULL) { + return -1; + } + _PyExecutorObject **new_ptrs = PyMem_Realloc( + interp->executor_ptrs, new_cap * sizeof(_PyExecutorObject *)); + if (new_ptrs == NULL) { + /* Revert blooms realloc — the old pointer may have been freed by + * a successful realloc, but new_blooms is the valid pointer. */ + interp->executor_blooms = new_blooms; + return -1; + } + interp->executor_blooms = new_blooms; + interp->executor_ptrs = new_ptrs; + interp->executor_capacity = new_cap; + } + size_t idx = interp->executor_count++; + interp->executor_blooms[idx] = *bloom; + interp->executor_ptrs[idx] = executor; + executor->vm_data.bloom_array_idx = (int32_t)idx; + return 0; +} -/* TO DO -- Use more modern hash functions with better distribution of bits */ -static uint64_t -address_to_hash(void *ptr) { - assert(ptr != NULL); - uint64_t uhash = SEED; - uintptr_t addr = (uintptr_t)ptr; - for (int i = 0; i < SIZEOF_VOID_P; i++) { - uhash ^= addr & 255; - uhash *= (uint64_t)PyHASH_MULTIPLIER; - addr >>= 8; - } - return uhash; +static void +unlink_executor(_PyExecutorObject *executor) +{ + PyInterpreterState *interp = PyInterpreterState_Get(); + int32_t idx = executor->vm_data.bloom_array_idx; + assert(idx >= 0 && (size_t)idx < interp->executor_count); + size_t last = --interp->executor_count; + if ((size_t)idx != last) { + /* Swap-remove: move the last element into the vacated slot */ + interp->executor_blooms[idx] = interp->executor_blooms[last]; + interp->executor_ptrs[idx] = interp->executor_ptrs[last]; + interp->executor_ptrs[idx]->vm_data.bloom_array_idx = idx; + } + executor->vm_data.bloom_array_idx = -1; } -void -_Py_BloomFilter_Init(_PyBloomFilter *bloom) +/* This must be called by optimizers before using the executor */ +int +_Py_ExecutorInit(_PyExecutorObject *executor, const _PyBloomFilter *dependency_set) { - for (int i = 0; i < _Py_BLOOM_FILTER_WORDS; i++) { - bloom->bits[i] = 0; + executor->vm_data.valid = true; + executor->vm_data.pending_deletion = 0; + executor->vm_data.code = NULL; + if (link_executor(executor, dependency_set) < 0) { + return -1; } + return 0; } -/* We want K hash functions that each set 1 bit. - * A hash function that sets 1 bit in M bits can be trivially - * derived from a log2(M) bit hash function. - * So we extract 8 (log2(256)) bits at a time from - * the 64bit hash. */ -void -_Py_BloomFilter_Add(_PyBloomFilter *bloom, void *ptr) +static _PyExecutorObject * +make_cold_executor(uint16_t opcode) { - uint64_t hash = address_to_hash(ptr); - assert(K <= 8); - for (int i = 0; i < K; i++) { - uint8_t bits = hash & 255; - bloom->bits[bits >> 5] |= (1 << (bits&31)); - hash >>= 8; + _PyExecutorObject *cold = allocate_executor(0, 1); + if (cold == NULL) { + Py_FatalError("Cannot allocate core JIT code"); } + ((_PyUOpInstruction *)cold->trace)->opcode = opcode; + // This is initialized to false so we can prevent the executor + // from being immediately detected as cold and invalidated. + cold->vm_data.cold = false; +#ifdef _Py_JIT + cold->jit_code = NULL; + cold->jit_size = 0; + if (_PyJIT_Compile(cold, cold->trace, 1)) { + Py_DECREF(cold); + Py_FatalError("Cannot allocate core JIT code"); + } +#endif + _Py_SetImmortal((PyObject *)cold); + return cold; } -static bool -bloom_filter_may_contain(_PyBloomFilter *bloom, _PyBloomFilter *hashes) +_PyExecutorObject * +_PyExecutor_GetColdExecutor(void) { - for (int i = 0; i < _Py_BLOOM_FILTER_WORDS; i++) { - if ((bloom->bits[i] & hashes->bits[i]) != hashes->bits[i]) { - return false; - } + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (interp->cold_executor == NULL) { + return interp->cold_executor = make_cold_executor(_COLD_EXIT_r00);; } - return true; + return interp->cold_executor; } -static void -link_executor(_PyExecutorObject *executor) +_PyExecutorObject * +_PyExecutor_GetColdDynamicExecutor(void) { PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyExecutorLinkListNode *links = &executor->vm_data.links; - _PyExecutorObject *head = interp->executor_list_head; - if (head == NULL) { - interp->executor_list_head = executor; - links->previous = NULL; - links->next = NULL; + if (interp->cold_dynamic_executor == NULL) { + interp->cold_dynamic_executor = make_cold_executor(_COLD_DYNAMIC_EXIT_r00); } - else { - assert(head->vm_data.links.previous == NULL); - links->previous = NULL; - links->next = head; - head->vm_data.links.previous = executor; - interp->executor_list_head = executor; - } - executor->vm_data.linked = true; - /* executor_list_head must be first in list */ - assert(interp->executor_list_head->vm_data.links.previous == NULL); + return interp->cold_dynamic_executor; } -static void -unlink_executor(_PyExecutorObject *executor) +void +_PyExecutor_ClearExit(_PyExitData *exit) { - if (!executor->vm_data.linked) { + if (exit == NULL) { return; } - _PyExecutorLinkListNode *links = &executor->vm_data.links; - assert(executor->vm_data.valid); - _PyExecutorObject *next = links->next; - _PyExecutorObject *prev = links->previous; - if (next != NULL) { - next->vm_data.links.previous = prev; - } - if (prev != NULL) { - prev->vm_data.links.next = next; + _PyExecutorObject *old = exit->executor; + if (exit->is_dynamic) { + exit->executor = _PyExecutor_GetColdDynamicExecutor(); } else { - // prev == NULL implies that executor is the list head - PyInterpreterState *interp = PyInterpreterState_Get(); - assert(interp->executor_list_head == executor); - interp->executor_list_head = next; - } - executor->vm_data.linked = false; -} - -/* This must be called by optimizers before using the executor */ -void -_Py_ExecutorInit(_PyExecutorObject *executor, const _PyBloomFilter *dependency_set) -{ - executor->vm_data.valid = true; - for (int i = 0; i < _Py_BLOOM_FILTER_WORDS; i++) { - executor->vm_data.bloom.bits[i] = dependency_set->bits[i]; + exit->executor = _PyExecutor_GetColdExecutor(); } - link_executor(executor); + Py_DECREF(old); } /* Detaches the executor from the code object (if any) that @@ -1475,34 +1856,35 @@ _Py_ExecutorDetach(_PyExecutorObject *executor) assert(instruction->op.code == ENTER_EXECUTOR); int index = instruction->op.arg; assert(code->co_executors->executors[index] == executor); - instruction->op.code = executor->vm_data.opcode; + instruction->op.code = _PyOpcode_Deopt[executor->vm_data.opcode]; instruction->op.arg = executor->vm_data.oparg; executor->vm_data.code = NULL; code->co_executors->executors[index] = NULL; Py_DECREF(executor); } -static int -executor_clear(PyObject *op) +/* Executors can be invalidated at any time, + even with a stop-the-world lock held. + Consequently it must not run arbitrary code, + including Py_DECREF with a non-executor. */ +static void +executor_invalidate(PyObject *op) { _PyExecutorObject *executor = _PyExecutorObject_CAST(op); if (!executor->vm_data.valid) { - return 0; + return; } - assert(executor->vm_data.valid == 1); - unlink_executor(executor); executor->vm_data.valid = 0; - /* It is possible for an executor to form a reference - * cycle with itself, so decref'ing a side exit could - * free the executor unless we hold a strong reference to it - */ - Py_INCREF(executor); - for (uint32_t i = 0; i < executor->exit_count; i++) { - executor->exits[i].temperature = initial_unreachable_backoff_counter(); - Py_CLEAR(executor->exits[i].executor); - } + unlink_executor(executor); + executor_clear_exits(executor); _Py_ExecutorDetach(executor); - Py_DECREF(executor); + _PyObject_GC_UNTRACK(op); +} + +static int +executor_clear(PyObject *op) +{ + executor_invalidate(op); return 0; } @@ -1510,11 +1892,15 @@ void _Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj) { assert(executor->vm_data.valid); - _Py_BloomFilter_Add(&executor->vm_data.bloom, obj); + PyInterpreterState *interp = _PyInterpreterState_GET(); + int32_t idx = executor->vm_data.bloom_array_idx; + assert(idx >= 0 && (size_t)idx < interp->executor_count); + _Py_BloomFilter_Add(&interp->executor_blooms[idx], obj); } /* Invalidate all executors that depend on `obj` - * May cause other executors to be invalidated as well + * May cause other executors to be invalidated as well. + * Uses contiguous bloom filter array for cache-friendly scanning. */ void _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation) @@ -1522,27 +1908,24 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is _PyBloomFilter obj_filter; _Py_BloomFilter_Init(&obj_filter); _Py_BloomFilter_Add(&obj_filter, obj); - /* Walk the list of executors */ - /* TO DO -- Use a tree to avoid traversing as many objects */ + /* Scan contiguous bloom filter array */ PyObject *invalidate = PyList_New(0); if (invalidate == NULL) { goto error; } - /* Clearing an executor can deallocate others, so we need to make a list of + /* Clearing an executor can clear others, so we need to make a list of * executors to invalidate first */ - for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) { - assert(exec->vm_data.valid); - _PyExecutorObject *next = exec->vm_data.links.next; - if (bloom_filter_may_contain(&exec->vm_data.bloom, &obj_filter) && - PyList_Append(invalidate, (PyObject *)exec)) + for (size_t i = 0; i < interp->executor_count; i++) { + assert(interp->executor_ptrs[i]->vm_data.valid); + if (bloom_filter_may_contain(&interp->executor_blooms[i], &obj_filter) && + PyList_Append(invalidate, (PyObject *)interp->executor_ptrs[i])) { goto error; } - exec = next; } for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) { PyObject *exec = PyList_GET_ITEM(invalidate, i); - executor_clear(exec); + executor_invalidate(exec); if (is_invalidation) { OPT_STAT_INC(executors_invalidated); } @@ -1560,15 +1943,16 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is void _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation) { - while (interp->executor_list_head) { - _PyExecutorObject *executor = interp->executor_list_head; - assert(executor->vm_data.valid == 1 && executor->vm_data.linked == 1); + while (interp->executor_count > 0) { + /* Invalidate from the end to avoid repeated swap-remove shifts */ + _PyExecutorObject *executor = interp->executor_ptrs[interp->executor_count - 1]; + assert(executor->vm_data.valid); if (executor->vm_data.code) { // Clear the entire code object so its co_executors array be freed: _PyCode_Clear_Executors(executor->vm_data.code); } else { - executor_clear((PyObject *)executor); + executor_invalidate((PyObject *)executor); } if (is_invalidation) { OPT_STAT_INC(executors_invalidated); @@ -1579,8 +1963,7 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation) void _Py_Executors_InvalidateCold(PyInterpreterState *interp) { - /* Walk the list of executors */ - /* TO DO -- Use a tree to avoid traversing as many objects */ + /* Scan contiguous executor array */ PyObject *invalidate = PyList_New(0); if (invalidate == NULL) { goto error; @@ -1588,22 +1971,20 @@ _Py_Executors_InvalidateCold(PyInterpreterState *interp) /* Clearing an executor can deallocate others, so we need to make a list of * executors to invalidate first */ - for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) { + for (size_t i = 0; i < interp->executor_count; i++) { + _PyExecutorObject *exec = interp->executor_ptrs[i]; assert(exec->vm_data.valid); - _PyExecutorObject *next = exec->vm_data.links.next; - if (!exec->vm_data.warm && PyList_Append(invalidate, (PyObject *)exec) < 0) { + if (exec->vm_data.cold && PyList_Append(invalidate, (PyObject *)exec) < 0) { goto error; } else { - exec->vm_data.warm = false; + exec->vm_data.cold = true; } - - exec = next; } for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) { PyObject *exec = PyList_GET_ITEM(invalidate, i); - executor_clear(exec); + executor_invalidate(exec); } Py_DECREF(invalidate); return; @@ -1614,6 +1995,26 @@ _Py_Executors_InvalidateCold(PyInterpreterState *interp) _Py_Executors_InvalidateAll(interp, 0); } +#include "record_functions.c.h" + +static int +escape_angles(const char *input, Py_ssize_t size, char *buffer) { + int written = 0; + for (Py_ssize_t i = 0; i < size; i++) { + char c = input[i]; + if (c == '<' || c == '>') { + buffer[written++] = '&'; + buffer[written++] = c == '>' ? 'g' : 'l'; + buffer[written++] = 't'; + buffer[written++] = ';'; + } + else { + buffer[written++] = c; + } + } + return written; +} + static void write_str(PyObject *str, FILE *out) { @@ -1625,7 +2026,17 @@ write_str(PyObject *str, FILE *out) } const char *encoded_str = PyBytes_AsString(encoded_obj); Py_ssize_t encoded_size = PyBytes_Size(encoded_obj); - fwrite(encoded_str, 1, encoded_size, out); + char buffer[120]; + bool truncated = false; + if (encoded_size > 24) { + encoded_size = 24; + truncated = true; + } + int size = escape_angles(encoded_str, encoded_size, buffer); + fwrite(buffer, 1, size, out); + if (truncated) { + fwrite("...", 1, 3, out); + } Py_DECREF(encoded_obj); } @@ -1647,6 +2058,85 @@ find_line_number(PyCodeObject *code, _PyExecutorObject *executor) return -1; } +#define RED "#ff0000" +#define WHITE "#ffffff" +#define BLUE "#0000ff" +#define BLACK "#000000" +#define LOOP "#00c000" + +#ifdef Py_STATS + +static const char *COLORS[10] = { + "9", + "8", + "7", + "6", + "5", + "4", + "3", + "2", + "1", + WHITE, +}; +const char * +get_background_color(_PyUOpInstruction const *inst, uint64_t max_hotness) +{ + uint64_t hotness = inst->execution_count; + int index = (hotness * 10)/max_hotness; + if (index > 9) { + index = 9; + } + if (index < 0) { + index = 0; + } + return COLORS[index]; +} + +const char * +get_foreground_color(_PyUOpInstruction const *inst, uint64_t max_hotness) +{ + if(_PyUop_Uncached[inst->opcode] == _DEOPT) { + return RED; + } + uint64_t hotness = inst->execution_count; + int index = (hotness * 10)/max_hotness; + if (index > 3) { + return BLACK; + } + return WHITE; +} +#endif + +static void +write_row_for_uop(_PyExecutorObject *executor, uint32_t i, FILE *out) +{ + /* Write row for uop. + * The `port` is a marker so that outgoing edges can + * be placed correctly. If a row is marked `port=17`, + * then the outgoing edge is `{EXEC_NAME}:17 -> {TARGET}` + * https://graphviz.readthedocs.io/en/stable/manual.html#node-ports-compass + */ + _PyUOpInstruction const *inst = &executor->trace[i]; + const char *opname = _PyOpcode_uop_name[inst->opcode]; +#ifdef Py_STATS + const char *bg_color = get_background_color(inst, executor->trace[0].execution_count); + const char *color = get_foreground_color(inst, executor->trace[0].execution_count); + fprintf(out, " <tr><td port=\"i%d\" border=\"1\" color=\"%s\" bgcolor=\"%s\" ><font color=\"%s\"> %s [%d]&nbsp;--&nbsp; %" PRIu64 "</font></td></tr>\n", + i, color, bg_color, color, opname, inst->fitness, inst->execution_count); +#else + const char *color = (_PyUop_Uncached[inst->opcode] == _DEOPT) ? RED : BLACK; + fprintf(out, " <tr><td port=\"i%d\" border=\"1\" color=\"%s\" >%s op0=%" PRIu64 "</td></tr>\n", i, color, opname, inst->operand0); +#endif +} + +static bool +is_stop(_PyUOpInstruction const *inst) +{ + uint16_t base_opcode = _PyUop_Uncached[inst->opcode]; + return (base_opcode == _EXIT_TRACE || base_opcode == _DEOPT || base_opcode == _JUMP_TO_TOP); +} + + /* Writes the node and outgoing edges for a single tracelet in graphviz format. * Each tracelet is presented as a table of the uops it contains. * If Py_STATS is enabled, execution counts are included. @@ -1674,20 +2164,8 @@ executor_to_gv(_PyExecutorObject *executor, FILE *out) fprintf(out, ": %d</td></tr>\n", line); } for (uint32_t i = 0; i < executor->code_size; i++) { - /* Write row for uop. - * The `port` is a marker so that outgoing edges can - * be placed correctly. If a row is marked `port=17`, - * then the outgoing edge is `{EXEC_NAME}:17 -> {TARGET}` - * https://graphviz.readthedocs.io/en/stable/manual.html#node-ports-compass - */ - _PyUOpInstruction const *inst = &executor->trace[i]; - const char *opname = _PyOpcode_uop_name[inst->opcode]; -#ifdef Py_STATS - fprintf(out, " <tr><td port=\"i%d\" border=\"1\" >%s -- %" PRIu64 "</td></tr>\n", i, opname, inst->execution_count); -#else - fprintf(out, " <tr><td port=\"i%d\" border=\"1\" >%s</td></tr>\n", i, opname); -#endif - if (inst->opcode == _EXIT_TRACE || inst->opcode == _JUMP_TO_TOP) { + write_row_for_uop(executor, i, out); + if (is_stop(&executor->trace[i])) { break; } } @@ -1695,23 +2173,44 @@ executor_to_gv(_PyExecutorObject *executor, FILE *out) fprintf(out, "]\n\n"); /* Write all the outgoing edges */ + _PyExecutorObject *cold = _PyExecutor_GetColdExecutor(); + _PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor(); for (uint32_t i = 0; i < executor->code_size; i++) { _PyUOpInstruction const *inst = &executor->trace[i]; - uint16_t flags = _PyUop_Flags[inst->opcode]; + uint16_t base_opcode = _PyUop_Uncached[inst->opcode]; + uint16_t flags = _PyUop_Flags[base_opcode]; _PyExitData *exit = NULL; - if (inst->opcode == _EXIT_TRACE) { + if (base_opcode == _JUMP_TO_TOP) { + fprintf(out, "executor_%p:i%d -> executor_%p:i%d [color = \"" LOOP "\"]\n", executor, i, executor, inst->jump_target); + break; + } + if (base_opcode == _EXIT_TRACE) { exit = (_PyExitData *)inst->operand0; } else if (flags & HAS_EXIT_FLAG) { assert(inst->format == UOP_FORMAT_JUMP); _PyUOpInstruction const *exit_inst = &executor->trace[inst->jump_target]; - assert(exit_inst->opcode == _EXIT_TRACE); + uint16_t base_exit_opcode = _PyUop_Uncached[exit_inst->opcode]; + (void)base_exit_opcode; + assert(base_exit_opcode == _EXIT_TRACE || base_exit_opcode == _DYNAMIC_EXIT); exit = (_PyExitData *)exit_inst->operand0; } - if (exit != NULL && exit->executor != NULL) { - fprintf(out, "executor_%p:i%d -> executor_%p:start\n", executor, i, exit->executor); + if (exit != NULL) { + if (exit->executor == cold || exit->executor == cold_dynamic) { +#ifdef Py_STATS + /* Only mark as have cold exit if it has actually exited */ + uint64_t diff = inst->execution_count - executor->trace[i+1].execution_count; + if (diff) { + fprintf(out, "cold_%p%d [ label = \"%" PRIu64 "\" shape = ellipse color=\"" BLUE "\" ]\n", executor, i, diff); + fprintf(out, "executor_%p:i%d -> cold_%p%d\n", executor, i, executor, i); + } +#endif + } + else { + fprintf(out, "executor_%p:i%d -> executor_%p:start\n", executor, i, exit->executor); + } } - if (inst->opcode == _EXIT_TRACE || inst->opcode == _JUMP_TO_TOP) { + if (is_stop(inst)) { break; } } @@ -1723,10 +2222,10 @@ _PyDumpExecutors(FILE *out) { fprintf(out, "digraph ideal {\n\n"); fprintf(out, " rankdir = \"LR\"\n\n"); + fprintf(out, " node [colorscheme=greys9]\n"); PyInterpreterState *interp = PyInterpreterState_Get(); - for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) { - executor_to_gv(exec, out); - exec = exec->vm_data.links.next; + for (size_t i = 0; i < interp->executor_count; i++) { + executor_to_gv(interp->executor_ptrs[i], out); } fprintf(out, "}\n\n"); return 0; @@ -1741,4 +2240,11 @@ _PyDumpExecutors(FILE *out) return -1; } +void +_PyExecutor_Free(struct _PyExecutorObject *self) +{ + /* This should never be called */ + Py_UNREACHABLE(); +} + #endif /* _Py_TIER2 */ diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index fab6fef5ccda10..e726dc0e6fd111 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -18,6 +18,8 @@ #include "pycore_opcode_metadata.h" #include "pycore_opcode_utils.h" #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_* +#include "pycore_tstate.h" // _PyThreadStateImpl #include "pycore_uop_metadata.h" #include "pycore_long.h" #include "pycore_interpframe.h" // _PyFrame_GetCode @@ -28,6 +30,9 @@ #include "pycore_range.h" #include "pycore_unicodeobject.h" #include "pycore_ceval.h" +#include "pycore_floatobject.h" +#include "pycore_setobject.h" +#include "pycore_typeobject.h" #include <stdarg.h> #include <stdbool.h> @@ -37,6 +42,7 @@ #ifdef Py_DEBUG extern const char *_PyUOpName(int index); extern void _PyUOpPrint(const _PyUOpInstruction *uop); + extern void _PyUOpSymPrint(JitOptRef ref); static const char *const DEBUG_ENV = "PYTHON_OPT_DEBUG"; static inline int get_lltrace(void) { char *uop_debug = Py_GETENV(DEBUG_ENV); @@ -48,22 +54,81 @@ } #define DPRINTF(level, ...) \ if (get_lltrace() >= (level)) { printf(__VA_ARGS__); } + +static void +dump_abstract_stack(_Py_UOpsAbstractFrame *frame, JitOptRef *stack_pointer) +{ + printf(" locals=["); + for (int i = 0 ; i < frame->locals_len; i++) { + if (i > 0) { + printf(", "); + } + _PyUOpSymPrint(frame->locals[i]); + } + printf("]\n"); + if (stack_pointer < frame->stack) { + printf(" stack=%d\n", (int)(stack_pointer - frame->stack)); + } + else { + printf(" stack=["); + for (JitOptRef *ptr = frame->stack; ptr < stack_pointer; ptr++) { + if (ptr != frame->stack) { + printf(", "); + } + _PyUOpSymPrint(*ptr); + } + printf("]\n"); + } + fflush(stdout); +} + +static void +dump_uop(JitOptContext *ctx, const char *label, int index, + const _PyUOpInstruction *instr, JitOptRef *stack_pointer) +{ + if (get_lltrace() >= 3) { + printf("%4d %s: ", index, label); + _PyUOpPrint(instr); + printf("\n"); + if (get_lltrace() >= 5 && ctx->frame->code != ((PyCodeObject *)&_Py_InitCleanup)) { + dump_abstract_stack(ctx->frame, stack_pointer); + } + } +} + +static void +dump_uops(JitOptContext *ctx, const char *label, + _PyUOpInstruction *start, JitOptRef *stack_pointer) +{ + int current_len = uop_buffer_length(&ctx->out_buffer); + int added_count = (int)(ctx->out_buffer.next - start); + for (int j = 0; j < added_count; j++) { + dump_uop(ctx, label, current_len - added_count + j, &start[j], stack_pointer); + } +} + +#define DUMP_UOP dump_uop +#define DUMP_UOPS dump_uops + #else #define DPRINTF(level, ...) + #define DUMP_UOP(ctx, label, index, instr, stack_pointer) + #define DUMP_UOPS(ctx, label, start, stack_pointer) #endif static int get_mutations(PyObject* dict) { assert(PyDict_CheckExact(dict)); PyDictObject *d = (PyDictObject *)dict; - return (d->_ma_watcher_tag >> DICT_MAX_WATCHERS) & ((1 << DICT_WATCHED_MUTATION_BITS)-1); + uint64_t tag = FT_ATOMIC_LOAD_UINT64_RELAXED(d->_ma_watcher_tag); + return (tag >> DICT_MAX_WATCHERS) & ((1 << DICT_WATCHED_MUTATION_BITS) - 1); } static void increment_mutations(PyObject* dict) { assert(PyDict_CheckExact(dict)); PyDictObject *d = (PyDictObject *)dict; - d->_ma_watcher_tag += (1 << DICT_MAX_WATCHERS); + FT_ATOMIC_ADD_UINT64(d->_ma_watcher_tag, 1ULL << DICT_MAX_WATCHERS); } /* The first two dict watcher IDs are reserved for CPython, @@ -92,8 +157,29 @@ type_watcher_callback(PyTypeObject* type) return 0; } +static int +_setup_optimizer_watchers(void *Py_UNUSED(arg)) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + FT_ATOMIC_STORE_PTR_RELEASE( + interp->dict_state.watchers[GLOBALS_WATCHER_ID], + globals_watcher_callback); + interp->type_watchers[TYPE_WATCHER_ID] = type_watcher_callback; + return 0; +} + +static void +watch_type(PyTypeObject *type, _PyBloomFilter *filter) +{ + if (_Py_IsImmortal(type) && (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE)) { + return; + } + PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type); + _Py_BloomFilter_Add(filter, type); +} + static PyObject * -convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop) +convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj) { assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE); assert(PyDict_CheckExact(obj)); @@ -114,216 +200,74 @@ convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop) return NULL; } if (_Py_IsImmortal(res)) { - inst->opcode = pop ? _POP_TOP_LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE_BORROW; - } - else { - inst->opcode = pop ? _POP_TOP_LOAD_CONST_INLINE : _LOAD_CONST_INLINE; - } - if (inst->oparg & 1) { - assert(inst[1].opcode == _PUSH_NULL_CONDITIONAL); - assert(inst[1].oparg & 1); + inst->opcode = _LOAD_CONST_INLINE_BORROW; + } else { + inst->opcode = _LOAD_CONST_INLINE; } inst->operand0 = (uint64_t)res; return res; } -static int -incorrect_keys(_PyUOpInstruction *inst, PyObject *obj) +static bool +incorrect_keys(PyObject *obj, uint32_t version) { if (!PyDict_CheckExact(obj)) { - return 1; + return true; } PyDictObject *dict = (PyDictObject *)obj; - if (dict->ma_keys->dk_version != inst->operand0) { - return 1; - } - return 0; -} - -/* Returns 1 if successfully optimized - * 0 if the trace is not suitable for optimization (yet) - * -1 if there was an error. */ -static int -remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, - int buffer_size, _PyBloomFilter *dependencies) -{ - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *builtins = frame->f_builtins; - if (builtins != interp->builtins) { - OPT_STAT_INC(remove_globals_builtins_changed); - return 1; - } - PyObject *globals = frame->f_globals; - PyFunctionObject *function = _PyFrame_GetFunction(frame); - assert(PyFunction_Check(function)); - assert(function->func_builtins == builtins); - assert(function->func_globals == globals); - uint32_t function_version = _PyFunction_GetVersionForCurrentState(function); - /* In order to treat globals as constants, we need to - * know that the globals dict is the one we expected, and - * that it hasn't changed - * In order to treat builtins as constants, we need to - * know that the builtins dict is the one we expected, and - * that it hasn't changed and that the global dictionary's - * keys have not changed */ - - /* These values represent stacks of booleans (one bool per bit). - * Pushing a frame shifts left, popping a frame shifts right. */ - uint32_t function_checked = 0; - uint32_t builtins_watched = 0; - uint32_t globals_watched = 0; - uint32_t prechecked_function_version = 0; - if (interp->dict_state.watchers[GLOBALS_WATCHER_ID] == NULL) { - interp->dict_state.watchers[GLOBALS_WATCHER_ID] = globals_watcher_callback; - } - if (interp->type_watchers[TYPE_WATCHER_ID] == NULL) { - interp->type_watchers[TYPE_WATCHER_ID] = type_watcher_callback; - } - for (int pc = 0; pc < buffer_size; pc++) { - _PyUOpInstruction *inst = &buffer[pc]; - int opcode = inst->opcode; - switch(opcode) { - case _GUARD_GLOBALS_VERSION: - if (incorrect_keys(inst, globals)) { - OPT_STAT_INC(remove_globals_incorrect_keys); - return 0; - } - if (get_mutations(globals) >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { - continue; - } - if ((globals_watched & 1) == 0) { - PyDict_Watch(GLOBALS_WATCHER_ID, globals); - _Py_BloomFilter_Add(dependencies, globals); - globals_watched |= 1; - } - if (function_checked & 1) { - buffer[pc].opcode = NOP; - } - else { - buffer[pc].opcode = _CHECK_FUNCTION; - buffer[pc].operand0 = function_version; - function_checked |= 1; - } - break; - case _LOAD_GLOBAL_BUILTINS: - if (incorrect_keys(inst, builtins)) { - OPT_STAT_INC(remove_globals_incorrect_keys); - return 0; - } - if (interp->rare_events.builtin_dict >= _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) { - continue; - } - if ((builtins_watched & 1) == 0) { - PyDict_Watch(BUILTINS_WATCHER_ID, builtins); - builtins_watched |= 1; - } - if (function_checked & globals_watched & 1) { - convert_global_to_const(inst, builtins, false); - } - break; - case _LOAD_GLOBAL_MODULE: - if (incorrect_keys(inst, globals)) { - OPT_STAT_INC(remove_globals_incorrect_keys); - return 0; - } - if (get_mutations(globals) >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { - continue; - } - if ((globals_watched & 1) == 0) { - PyDict_Watch(GLOBALS_WATCHER_ID, globals); - _Py_BloomFilter_Add(dependencies, globals); - globals_watched |= 1; - } - if ((function_checked & 1) == 0 && buffer[pc-1].opcode == _NOP) { - buffer[pc-1].opcode = _CHECK_FUNCTION; - buffer[pc-1].operand0 = function_version; - function_checked |= 1; - } - if (function_checked & 1) { - convert_global_to_const(inst, globals, false); - } - break; - case _PUSH_FRAME: - { - builtins_watched <<= 1; - globals_watched <<= 1; - function_checked <<= 1; - uint64_t operand = buffer[pc].operand0; - if (operand == 0 || (operand & 1)) { - // It's either a code object or NULL, so bail - return 1; - } - PyFunctionObject *func = (PyFunctionObject *)operand; - if (func == NULL) { - return 1; - } - assert(PyFunction_Check(func)); - function_version = func->func_version; - if (prechecked_function_version == function_version) { - function_checked |= 1; - } - prechecked_function_version = 0; - globals = func->func_globals; - builtins = func->func_builtins; - if (builtins != interp->builtins) { - OPT_STAT_INC(remove_globals_builtins_changed); - return 1; - } - break; - } - case _RETURN_VALUE: - { - builtins_watched >>= 1; - globals_watched >>= 1; - function_checked >>= 1; - uint64_t operand = buffer[pc].operand0; - if (operand == 0 || (operand & 1)) { - // It's either a code object or NULL, so bail - return 1; - } - PyFunctionObject *func = (PyFunctionObject *)operand; - if (func == NULL) { - return 1; - } - assert(PyFunction_Check(func)); - function_version = func->func_version; - globals = func->func_globals; - builtins = func->func_builtins; - break; - } - case _CHECK_FUNCTION_EXACT_ARGS: - prechecked_function_version = (uint32_t)buffer[pc].operand0; - break; - default: - if (is_terminator(inst)) { - return 1; - } - break; - } - } - return 0; + return dict->ma_keys->dk_version != version; } - #define STACK_LEVEL() ((int)(stack_pointer - ctx->frame->stack)) #define STACK_SIZE() ((int)(ctx->frame->stack_len)) -#define WITHIN_STACK_BOUNDS() \ - (STACK_LEVEL() >= 0 && STACK_LEVEL() <= STACK_SIZE()) +static inline int +is_terminator_uop(const _PyUOpInstruction *uop) +{ + int opcode = uop->opcode; + return ( + opcode == _EXIT_TRACE || + opcode == _JUMP_TO_TOP || + opcode == _DYNAMIC_EXIT || + opcode == _DEOPT + ); +} +#define CURRENT_FRAME_IS_INIT_SHIM() (ctx->frame->code == ((PyCodeObject *)&_Py_InitCleanup)) #define GETLOCAL(idx) ((ctx->frame->locals[idx])) #define REPLACE_OP(INST, OP, ARG, OPERAND) \ - INST->opcode = OP; \ - INST->oparg = ARG; \ - INST->operand0 = OPERAND; + (INST)->opcode = OP; \ + (INST)->oparg = ARG; \ + (INST)->operand0 = OPERAND; + +#define ADD_OP(OP, ARG, OPERAND) add_op(ctx, this_instr, (OP), (ARG), (OPERAND)) + +static inline void +add_op(JitOptContext *ctx, _PyUOpInstruction *this_instr, + uint16_t opcode, uint16_t oparg, uintptr_t operand0) +{ + _PyUOpInstruction *out = ctx->out_buffer.next; + assert(out < ctx->out_buffer.end); + out->opcode = (opcode); + out->format = this_instr->format; + out->oparg = (oparg); + out->target = this_instr->target; + out->operand0 = (operand0); + out->operand1 = this_instr->operand1; +#ifdef Py_STATS + out->fitness = this_instr->fitness; +#endif + ctx->out_buffer.next++; +} /* Shortened forms for convenience, used in optimizer_bytecodes.c */ #define sym_is_not_null _Py_uop_sym_is_not_null #define sym_is_const _Py_uop_sym_is_const #define sym_is_safe_const _Py_uop_sym_is_safe_const +#define sym_is_not_container _Py_uop_sym_is_not_container #define sym_get_const _Py_uop_sym_get_const #define sym_new_const_steal _Py_uop_sym_new_const_steal #define sym_get_const_as_stackref _Py_uop_sym_get_const_as_stackref @@ -335,8 +279,10 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, #define sym_new_null _Py_uop_sym_new_null #define sym_has_type _Py_uop_sym_has_type #define sym_get_type _Py_uop_sym_get_type +#define sym_get_probable_type _Py_uop_sym_get_probable_type #define sym_matches_type _Py_uop_sym_matches_type #define sym_matches_type_version _Py_uop_sym_matches_type_version +#define sym_get_type_version _Py_uop_sym_get_type_version #define sym_set_null(SYM) _Py_uop_sym_set_null(ctx, SYM) #define sym_set_non_null(SYM) _Py_uop_sym_set_non_null(ctx, SYM) #define sym_set_type(SYM, TYPE) _Py_uop_sym_set_type(ctx, SYM, TYPE) @@ -346,6 +292,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, #define sym_is_bottom _Py_uop_sym_is_bottom #define sym_truthiness _Py_uop_sym_truthiness #define frame_new _Py_uop_frame_new +#define frame_new_from_symbol _Py_uop_frame_new_from_symbol #define frame_pop _Py_uop_frame_pop #define sym_new_tuple _Py_uop_sym_new_tuple #define sym_tuple_getitem _Py_uop_sym_tuple_getitem @@ -354,25 +301,66 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer, #define sym_is_compact_int _Py_uop_sym_is_compact_int #define sym_new_compact_int _Py_uop_sym_new_compact_int #define sym_new_truthiness _Py_uop_sym_new_truthiness +#define sym_new_predicate _Py_uop_sym_new_predicate +#define sym_apply_predicate_narrowing _Py_uop_sym_apply_predicate_narrowing +#define sym_set_recorded_type(SYM, TYPE) _Py_uop_sym_set_recorded_type(ctx, SYM, TYPE) +#define sym_set_recorded_value(SYM, VAL) _Py_uop_sym_set_recorded_value(ctx, SYM, VAL) +#define sym_set_recorded_gen_func(SYM, VAL) _Py_uop_sym_set_recorded_gen_func(ctx, SYM, VAL) +#define sym_get_probable_func_code _Py_uop_sym_get_probable_func_code +#define sym_get_probable_value _Py_uop_sym_get_probable_value +#define sym_set_stack_depth(DEPTH, SP) _Py_uop_sym_set_stack_depth(ctx, DEPTH, SP) + +/* Comparison oparg masks */ +#define COMPARE_LT_MASK 2 +#define COMPARE_GT_MASK 4 +#define COMPARE_EQ_MASK 8 #define JUMP_TO_LABEL(label) goto label; +static int +check_stack_bounds(JitOptContext *ctx, JitOptRef *stack_pointer, int offset, int opcode) +{ + int stack_level = (int)(stack_pointer + (offset) - ctx->frame->stack); + int should_check = !CURRENT_FRAME_IS_INIT_SHIM() || + (opcode == _RETURN_VALUE) || + (opcode == _RETURN_GENERATOR) || + (opcode == _YIELD_VALUE); + if (should_check && (stack_level < 0 || stack_level > STACK_SIZE() + MAX_CACHED_REGISTER)) { + ctx->contradiction = true; + ctx->done = true; + return 1; + } + return 0; +} + +#define CHECK_STACK_BOUNDS(offset) \ + if (check_stack_bounds(ctx, stack_pointer, offset, opcode)) { \ + break; \ + } \ + static int optimize_to_bool( _PyUOpInstruction *this_instr, JitOptContext *ctx, JitOptRef value, - JitOptRef *result_ptr) + JitOptRef *result_ptr, + uint16_t prefix, uint16_t suffix) { if (sym_matches_type(value, &PyBool_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); *result_ptr = value; return 1; } int truthiness = sym_truthiness(ctx, value); if (truthiness >= 0) { PyObject *load = truthiness ? Py_True : Py_False; - REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load); + if (prefix != _NOP) { + ADD_OP(prefix, 0, 0); + } + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load); + if (suffix != _NOP) { + ADD_OP(suffix, 2, 0); + } *result_ptr = sym_new_const(ctx, load); return 1; } @@ -380,9 +368,35 @@ optimize_to_bool( } static void -eliminate_pop_guard(_PyUOpInstruction *this_instr, bool exit) +optimize_dict_known_hash( + JitOptContext *ctx, _PyBloomFilter *dependencies, _PyUOpInstruction *this_instr, + PyObject *sub, uint16_t opcode) +{ + if (PyUnicode_CheckExact(sub) || PyLong_CheckExact(sub) || PyBytes_CheckExact(sub) + || PyFloat_CheckExact(sub) || PyComplex_CheckExact(sub)) { + // PyObject_Hash can't fail on these types + ADD_OP(opcode, 0, PyObject_Hash(sub)); + } + else if (PyTuple_CheckExact(sub)) { + // only use known hash variant when hash of tuple is already computed + // since computing it can call arbitrary code + Py_hash_t hash = ((PyTupleObject *)sub)->ob_hash; + if (hash != -1) { + ADD_OP(opcode, 0, hash); + } + } + else if (Py_TYPE(sub)->tp_hash == PyBaseObject_Type.tp_hash) { + // for user-defined objects which don't override tp_hash + Py_hash_t hash = PyObject_Hash(sub); + ADD_OP(opcode, 0, hash); + watch_type(Py_TYPE(sub), dependencies); + } +} + +static void +eliminate_pop_guard(_PyUOpInstruction *this_instr, JitOptContext *ctx, bool exit) { - REPLACE_OP(this_instr, _POP_TOP, 0, 0); + ADD_OP(_POP_TOP, 0, 0); if (exit) { REPLACE_OP((this_instr+1), _EXIT_TRACE, 0, 0); this_instr[1].target = this_instr->target; @@ -390,123 +404,232 @@ eliminate_pop_guard(_PyUOpInstruction *this_instr, bool exit) } static JitOptRef -lookup_attr(JitOptContext *ctx, _PyUOpInstruction *this_instr, - PyTypeObject *type, PyObject *name, uint16_t immortal, - uint16_t mortal) +lookup_attr(JitOptContext *ctx, _PyBloomFilter *dependencies, _PyUOpInstruction *this_instr, + PyTypeObject *type, PyObject *name, + uint16_t prefix, uint16_t suffix) { // The cached value may be dead, so we need to do the lookup again... :( if (type && PyType_Check(type)) { PyObject *lookup = _PyType_Lookup(type, name); if (lookup) { - int opcode = _Py_IsImmortal(lookup) ? immortal : mortal; - REPLACE_OP(this_instr, opcode, 0, (uintptr_t)lookup); + bool immortal = _Py_IsImmortal(lookup) || (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE); + if (prefix != _NOP) { + ADD_OP(prefix, 0, 0); + } + ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE, + 0, (uintptr_t)lookup); + if (suffix != _NOP) { + ADD_OP(suffix, 2, 0); + } + if ((type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0) { + watch_type(type, dependencies); + } return sym_new_const(ctx, lookup); } } return sym_new_not_null(ctx); } -/* _PUSH_FRAME/_RETURN_VALUE's operand can be 0, a PyFunctionObject *, or a - * PyCodeObject *. Retrieve the code object if possible. - */ -static PyCodeObject * -get_code(_PyUOpInstruction *op) +static void +optimize_pop_top(JitOptContext *ctx, _PyUOpInstruction *this_instr, JitOptRef value) { - assert(op->opcode == _PUSH_FRAME || op->opcode == _RETURN_VALUE || op->opcode == _RETURN_GENERATOR); - PyCodeObject *co = NULL; - uint64_t operand = op->operand0; - if (operand == 0) { - return NULL; + PyTypeObject *typ = sym_get_type(value); + if (PyJitRef_IsBorrowed(value) || + sym_is_immortal(PyJitRef_Unwrap(value)) || + sym_is_null(value)) { + ADD_OP(_POP_TOP_NOP, 0, 0); + } + else if (typ == &PyLong_Type) { + ADD_OP(_POP_TOP_INT, 0, 0); } - if (operand & 1) { - co = (PyCodeObject *)(operand & ~1); + else if (typ == &PyFloat_Type) { + ADD_OP(_POP_TOP_FLOAT, 0, 0); + } + else if (typ == &PyUnicode_Type) { + ADD_OP(_POP_TOP_UNICODE, 0, 0); } else { - PyFunctionObject *func = (PyFunctionObject *)operand; - assert(PyFunction_Check(func)); - co = (PyCodeObject *)func->func_code; + ADD_OP(_POP_TOP, 0, 0); } - assert(PyCode_Check(co)); - return co; } -static PyCodeObject * -get_code_with_logging(_PyUOpInstruction *op) +/* Look up name via super (normal case from supercheck where + su_obj_type = Py_TYPE(obj)). */ +static JitOptRef +lookup_super_attr(JitOptContext *ctx, _PyBloomFilter *dependencies, + _PyUOpInstruction *this_instr, + PyTypeObject *su_type, PyTypeObject *obj_type, + PyObject *name, + uint16_t immortal, uint16_t mortal, uint16_t suffix) { - PyCodeObject *co = NULL; - uint64_t push_operand = op->operand0; - if (push_operand & 1) { - co = (PyCodeObject *)(push_operand & ~1); - DPRINTF(3, "code=%p ", co); - assert(PyCode_Check(co)); + if (su_type == NULL || obj_type == NULL) { + return sym_new_not_null(ctx); } - else { - PyFunctionObject *func = (PyFunctionObject *)push_operand; - DPRINTF(3, "func=%p ", func); - if (func == NULL) { - DPRINTF(3, "\n"); - DPRINTF(1, "Missing function\n"); - return NULL; + /* Normal case: obj_type must be a subtype of su_type */ + if (!PyType_IsSubtype(obj_type, su_type)) { + return sym_new_not_null(ctx); + } + PyObject *lookup = _PySuper_LookupDescr(su_type, obj_type, name); + if (lookup == NULL) { + if (PyErr_Occurred()) { + PyErr_Clear(); } - co = (PyCodeObject *)func->func_code; - DPRINTF(3, "code=%p ", co); + return sym_new_not_null(ctx); + } + if ((Py_TYPE(lookup)->tp_flags & Py_TPFLAGS_METHOD_DESCRIPTOR) == 0) { + Py_DECREF(lookup); + return sym_new_not_null(ctx); + } + int opcode = mortal; + if (_Py_IsImmortal(lookup) || (obj_type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE)) { + opcode = immortal; } - return co; + ADD_OP(_SWAP, 3, 0); + ADD_OP(_POP_TOP, 0, 0); + ADD_OP(_POP_TOP, 0, 0); + ADD_OP(opcode, 0, (uintptr_t)lookup); + if (suffix != _NOP) { + ADD_OP(suffix, 2, 0); + } + // if obj_type is immutable, then all its superclasses are immutable + if ((obj_type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0) { + watch_type(su_type, dependencies); + watch_type(obj_type, dependencies); + } + return sym_new_const_steal(ctx, lookup); } -// TODO (gh-134584) generate most of this table automatically -const uint16_t op_without_decref_inputs[MAX_UOP_ID + 1] = { - [_BINARY_OP_MULTIPLY_FLOAT] = _BINARY_OP_MULTIPLY_FLOAT__NO_DECREF_INPUTS, - [_BINARY_OP_ADD_FLOAT] = _BINARY_OP_ADD_FLOAT__NO_DECREF_INPUTS, - [_BINARY_OP_SUBTRACT_FLOAT] = _BINARY_OP_SUBTRACT_FLOAT__NO_DECREF_INPUTS, -}; +static +PyCodeObject * +get_current_code_object(JitOptContext *ctx) +{ + return (PyCodeObject *)ctx->frame->code; +} + +static PyObject * +get_co_name(JitOptContext *ctx, int index) +{ + return PyTuple_GET_ITEM(get_current_code_object(ctx)->co_names, index); +} + +static int +get_test_bit_for_bools(void) { +#ifdef Py_STACKREF_DEBUG + uintptr_t false_bits = _Py_STACKREF_FALSE_INDEX; + uintptr_t true_bits = _Py_STACKREF_TRUE_INDEX; +#else + uintptr_t false_bits = (uintptr_t)&_Py_FalseStruct; + uintptr_t true_bits = (uintptr_t)&_Py_TrueStruct; +#endif + for (int i = 4; i < 8; i++) { + if ((true_bits ^ false_bits) & (uintptr_t)(1 << i)) { + return i; + } + } + return 0; +} + +static int +test_bit_set_in_true(int bit) { +#ifdef Py_STACKREF_DEBUG + uintptr_t true_bits = _Py_STACKREF_TRUE_INDEX; +#else + uintptr_t true_bits = (uintptr_t)&_Py_TrueStruct; +#endif + assert((true_bits ^ ((uintptr_t)&_Py_FalseStruct)) & (uintptr_t)(1 << bit)); + return true_bits & (uintptr_t)(1 << bit); +} + +#ifdef Py_DEBUG +void +_Py_opt_assert_within_stack_bounds( + _Py_UOpsAbstractFrame *frame, JitOptRef *stack_pointer, + const char *filename, int lineno +) { + if (frame->code == ((PyCodeObject *)&_Py_InitCleanup)) { + return; + } + int level = (int)(stack_pointer - frame->stack); + if (level < 0) { + printf("Stack underflow (depth = %d) at %s:%d\n", level, filename, lineno); + fflush(stdout); + abort(); + } + int size = (int)(frame->stack_len) + MAX_CACHED_REGISTER; + if (level > size) { + printf("Stack overflow (depth = %d) at %s:%d\n", level, filename, lineno); + fflush(stdout); + abort(); + } +} +#endif + +#ifdef Py_DEBUG +#define ASSERT_WITHIN_STACK_BOUNDS(F, L) _Py_opt_assert_within_stack_bounds(ctx->frame, stack_pointer, (F), (L)) +#else +#define ASSERT_WITHIN_STACK_BOUNDS(F, L) (void)0 +#endif -/* 1 for success, 0 for not ready, cannot error at the moment. */ +/* >0 (length) for success, 0 for not ready, clears all possible errors. */ static int optimize_uops( - PyCodeObject *co, + _PyThreadStateImpl *tstate, _PyUOpInstruction *trace, int trace_len, int curr_stacklen, + _PyUOpInstruction *output, _PyBloomFilter *dependencies ) { + assert(!PyErr_Occurred()); + assert(tstate->jit_tracer_state != NULL); + PyFunctionObject *func = tstate->jit_tracer_state->initial_state.func; - JitOptContext context; - JitOptContext *ctx = &context; + JitOptContext *ctx = &tstate->jit_tracer_state->opt_context; uint32_t opcode = UINT16_MAX; - int curr_space = 0; - int max_space = 0; - _PyUOpInstruction *first_valid_check_stack = NULL; - _PyUOpInstruction *corresponding_check_stack = NULL; - _Py_uop_abstractcontext_init(ctx); - _Py_UOpsAbstractFrame *frame = _Py_uop_frame_new(ctx, co, curr_stacklen, NULL, 0); + uop_buffer_init(&ctx->out_buffer, output, UOP_MAX_TRACE_LENGTH); + + // Make sure that watchers are set up + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyOnceFlag_CallOnce(&interp->dict_state.watcher_setup_once, + _setup_optimizer_watchers, NULL); + + _Py_uop_abstractcontext_init(ctx, dependencies); + _Py_UOpsAbstractFrame *frame = _Py_uop_frame_new(ctx, (PyCodeObject *)func->func_code, NULL, 0); if (frame == NULL) { - return -1; + return 0; } + frame->func = func; ctx->curr_frame_depth++; ctx->frame = frame; - ctx->done = false; - ctx->out_of_space = false; - ctx->contradiction = false; + _Py_uop_sym_set_stack_depth(ctx, curr_stacklen, frame->stack_pointer); _PyUOpInstruction *this_instr = NULL; - for (int i = 0; !ctx->done; i++) { - assert(i < trace_len); + JitOptRef *stack_pointer = ctx->frame->stack_pointer; + + for (int i = 0; i < trace_len; i++) { this_instr = &trace[i]; + if (ctx->done) { + // Don't do any more optimization, but + // we still need to reach a terminator for corrctness. + *(ctx->out_buffer.next++) = *this_instr; + if (is_terminator_uop(this_instr)) { + break; + } + continue; + } int oparg = this_instr->oparg; opcode = this_instr->opcode; - JitOptRef *stack_pointer = ctx->frame->stack_pointer; -#ifdef Py_DEBUG - if (get_lltrace() >= 3) { - printf("%4d abs: ", (int)(this_instr - trace)); - _PyUOpPrint(this_instr); - printf(" "); + if (!CURRENT_FRAME_IS_INIT_SHIM()) { + stack_pointer = ctx->frame->stack_pointer; } -#endif + + DUMP_UOP(ctx, "abs", (int)(this_instr - trace), this_instr, stack_pointer); + + _PyUOpInstruction *out_ptr = ctx->out_buffer.next; switch (opcode) { @@ -516,10 +639,17 @@ optimize_uops( DPRINTF(1, "\nUnknown opcode in abstract interpreter\n"); Py_UNREACHABLE(); } + // If no ADD_OP was called during this iteration, copy the original instruction + if (ctx->out_buffer.next == out_ptr) { + *(ctx->out_buffer.next++) = *this_instr; + } assert(ctx->frame != NULL); - DPRINTF(3, " stack_level %d\n", STACK_LEVEL()); - ctx->frame->stack_pointer = stack_pointer; - assert(STACK_LEVEL() >= 0); + DUMP_UOPS(ctx, "out", out_ptr, stack_pointer); + if (!CURRENT_FRAME_IS_INIT_SHIM() && !ctx->done) { + DPRINTF(3, " stack_level %d\n", STACK_LEVEL()); + ctx->frame->stack_pointer = stack_pointer; + assert(STACK_LEVEL() >= 0); + } } if (ctx->out_of_space) { DPRINTF(3, "\n"); @@ -527,28 +657,27 @@ optimize_uops( } if (ctx->contradiction) { // Attempted to push a "bottom" (contradiction) symbol onto the stack. - // This means that the abstract interpreter has hit unreachable code. + // This means that the abstract interpreter has optimized to trace + // to an unreachable estate. // We *could* generate an _EXIT_TRACE or _FATAL_ERROR here, but hitting - // bottom indicates type instability, so we are probably better off + // bottom usually indicates an optimizer bug, so we are probably better off // retrying later. DPRINTF(3, "\n"); DPRINTF(1, "Hit bottom in abstract interpreter\n"); _Py_uop_abstractcontext_fini(ctx); + OPT_STAT_INC(optimizer_contradiction); return 0; } /* Either reached the end or cannot optimize further, but there * would be no benefit in retrying later */ _Py_uop_abstractcontext_fini(ctx); - if (first_valid_check_stack != NULL) { - assert(first_valid_check_stack->opcode == _CHECK_STACK_SPACE); - assert(max_space > 0); - assert(max_space <= INT_MAX); - assert(max_space <= INT32_MAX); - first_valid_check_stack->opcode = _CHECK_STACK_SPACE_OPERAND; - first_valid_check_stack->operand0 = max_space; + // Check that the trace ends with a proper terminator + if (uop_buffer_length(&ctx->out_buffer) > 0) { + assert(is_terminator_uop(uop_buffer_last(&ctx->out_buffer))); } - return trace_len; + + return uop_buffer_length(&ctx->out_buffer); error: DPRINTF(3, "\n"); @@ -557,7 +686,11 @@ optimize_uops( OPT_ERROR_IN_OPCODE(opcode); } _Py_uop_abstractcontext_fini(ctx); - return -1; + + assert(PyErr_Occurred()); + PyErr_Clear(); + + return 0; } @@ -565,15 +698,10 @@ const uint16_t op_without_push[MAX_UOP_ID + 1] = { [_COPY] = _NOP, [_LOAD_CONST_INLINE] = _NOP, [_LOAD_CONST_INLINE_BORROW] = _NOP, - [_LOAD_CONST_UNDER_INLINE] = _POP_TOP_LOAD_CONST_INLINE, - [_LOAD_CONST_UNDER_INLINE_BORROW] = _POP_TOP_LOAD_CONST_INLINE_BORROW, [_LOAD_FAST] = _NOP, [_LOAD_FAST_BORROW] = _NOP, [_LOAD_SMALL_INT] = _NOP, - [_POP_TOP_LOAD_CONST_INLINE] = _POP_TOP, - [_POP_TOP_LOAD_CONST_INLINE_BORROW] = _POP_TOP, - [_POP_TWO_LOAD_CONST_INLINE_BORROW] = _POP_TWO, - [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = _POP_CALL_TWO, + [_PUSH_NULL] = _NOP, }; const bool op_skip[MAX_UOP_ID + 1] = { @@ -585,19 +713,10 @@ const bool op_skip[MAX_UOP_ID + 1] = { const uint16_t op_without_pop[MAX_UOP_ID + 1] = { [_POP_TOP] = _NOP, - [_POP_TOP_LOAD_CONST_INLINE] = _LOAD_CONST_INLINE, - [_POP_TOP_LOAD_CONST_INLINE_BORROW] = _LOAD_CONST_INLINE_BORROW, - [_POP_TWO] = _POP_TOP, - [_POP_TWO_LOAD_CONST_INLINE_BORROW] = _POP_TOP_LOAD_CONST_INLINE_BORROW, - [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, - [_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW] = _POP_CALL_LOAD_CONST_INLINE_BORROW, - [_POP_CALL_TWO] = _POP_CALL_ONE, - [_POP_CALL_ONE] = _POP_CALL, -}; - -const uint16_t op_without_pop_null[MAX_UOP_ID + 1] = { - [_POP_CALL] = _POP_TOP, - [_POP_CALL_LOAD_CONST_INLINE_BORROW] = _POP_TOP_LOAD_CONST_INLINE_BORROW, + [_POP_TOP_NOP] = _NOP, + [_POP_TOP_INT] = _NOP, + [_POP_TOP_FLOAT] = _NOP, + [_POP_TOP_UNICODE] = _NOP, }; @@ -628,13 +747,14 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size) buffer[pc].opcode = _NOP; } break; + case _EXIT_TRACE: default: { // Cancel out pushes and pops, repeatedly. So: - // _LOAD_FAST + _POP_TWO_LOAD_CONST_INLINE_BORROW + _POP_TOP + // _LOAD_FAST + _POP_TOP + _POP_TOP + _LOAD_CONST_INLINE_BORROW + _POP_TOP // ...becomes: - // _NOP + _POP_TOP + _NOP - while (op_without_pop[opcode] || op_without_pop_null[opcode]) { + // _NOP + _NOP + _POP_TOP + _NOP + _NOP + while (op_without_pop[opcode]) { _PyUOpInstruction *last = &buffer[pc - 1]; while (op_skip[last->opcode]) { last--; @@ -644,24 +764,16 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size) opcode = buffer[pc].opcode = op_without_pop[opcode]; if (op_without_pop[last->opcode]) { opcode = last->opcode; - pc = last - buffer; + pc = (int)(last - buffer); } } - else if (last->opcode == _PUSH_NULL) { - // Handle _POP_CALL and _POP_CALL_LOAD_CONST_INLINE_BORROW separately. - // This looks for a preceding _PUSH_NULL instruction and - // simplifies to _POP_TOP(_LOAD_CONST_INLINE_BORROW). - last->opcode = _NOP; - opcode = buffer[pc].opcode = op_without_pop_null[opcode]; - assert(opcode); - } else { break; } } /* _PUSH_FRAME doesn't escape or error, but it * does need the IP for the return address */ - bool needs_ip = opcode == _PUSH_FRAME; + bool needs_ip = (opcode == _PUSH_FRAME || opcode == _YIELD_VALUE || opcode == _DYNAMIC_EXIT || opcode == _EXIT_TRACE); if (_PyUop_Flags[opcode] & HAS_ESCAPES_FLAG) { needs_ip = true; may_have_escaped = true; @@ -671,10 +783,14 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size) buffer[last_set_ip].opcode = _SET_IP; last_set_ip = -1; } + if (opcode == _EXIT_TRACE) { + return pc + 1; + } break; } case _JUMP_TO_TOP: - case _EXIT_TRACE: + case _DYNAMIC_EXIT: + case _DEOPT: return pc + 1; } } @@ -686,29 +802,26 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size) // > 0 - length of optimized trace int _Py_uop_analyze_and_optimize( - _PyInterpreterFrame *frame, + _PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, int length, int curr_stacklen, + _PyUOpInstruction *output, _PyBloomFilter *dependencies ) { OPT_STAT_INC(optimizer_attempts); - int err = remove_globals(frame, buffer, length, dependencies); - if (err <= 0) { - return err; - } - length = optimize_uops( - _PyFrame_GetCode(frame), buffer, - length, curr_stacklen, dependencies); + tstate, buffer, length, curr_stacklen, output, dependencies); - if (length <= 0) { + if (length == 0) { return length; } - length = remove_unneeded_uops(buffer, length); + assert(length > 0); + + length = remove_unneeded_uops(output, length); assert(length > 0); OPT_STAT_INC(optimizer_successes); diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index aeff76affd8ace..edb4c644bccbf6 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -1,5 +1,9 @@ #include "Python.h" +#include "pycore_long.h" +#include "pycore_opcode_utils.h" #include "pycore_optimizer.h" +#include "pycore_stackref.h" +#include "pycore_typeobject.h" #include "pycore_uops.h" #include "pycore_uop_ids.h" #include "internal/pycore_moduleobject.h" @@ -20,6 +24,7 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame; #define sym_new_null _Py_uop_sym_new_null #define sym_matches_type _Py_uop_sym_matches_type #define sym_matches_type_version _Py_uop_sym_matches_type_version +#define sym_get_type_version _Py_uop_sym_get_type_version #define sym_get_type _Py_uop_sym_get_type #define sym_has_type _Py_uop_sym_has_type #define sym_set_null(SYM) _Py_uop_sym_set_null(ctx, SYM) @@ -30,6 +35,7 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame; #define sym_set_compact_int(SYM) _Py_uop_sym_set_compact_int(ctx, SYM) #define sym_is_bottom _Py_uop_sym_is_bottom #define frame_new _Py_uop_frame_new +#define frame_new_from_symbol _Py_uop_frame_new_from_symbol #define frame_pop _Py_uop_frame_pop #define sym_new_tuple _Py_uop_sym_new_tuple #define sym_tuple_getitem _Py_uop_sym_tuple_getitem @@ -38,16 +44,26 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame; #define sym_new_compact_int _Py_uop_sym_new_compact_int #define sym_is_compact_int _Py_uop_sym_is_compact_int #define sym_new_truthiness _Py_uop_sym_new_truthiness +#define sym_new_predicate _Py_uop_sym_new_predicate +#define sym_apply_predicate_narrowing _Py_uop_sym_apply_predicate_narrowing +#define sym_set_recorded_type(SYM, TYPE) _Py_uop_sym_set_recorded_type(ctx, SYM, TYPE) +#define sym_set_recorded_value(SYM, VAL) _Py_uop_sym_set_recorded_value(ctx, SYM, VAL) +#define sym_set_recorded_gen_func(SYM, VAL) _Py_uop_sym_set_recorded_gen_func(ctx, SYM, VAL) +#define sym_get_probable_func_code _Py_uop_sym_get_probable_func_code +#define sym_get_probable_value _Py_uop_sym_get_probable_value +#define sym_get_probable_type _Py_uop_sym_get_probable_type +#define sym_set_stack_depth(DEPTH, SP) _Py_uop_sym_set_stack_depth(ctx, DEPTH, SP) extern int optimize_to_bool( _PyUOpInstruction *this_instr, JitOptContext *ctx, JitOptSymbol *value, - JitOptSymbol **result_ptr); + JitOptSymbol **result_ptr, + uint16_t prefix, uint16_t suffix); extern void -eliminate_pop_guard(_PyUOpInstruction *this_instr, bool exit); +eliminate_pop_guard(_PyUOpInstruction *this_instr, JitOptContext *ctx, bool exit); extern PyCodeObject *get_code(_PyUOpInstruction *op); @@ -77,43 +93,125 @@ dummy_func(void) { // BEGIN BYTECODES // + op(_MAKE_HEAP_SAFE, (value -- value)) { + // eliminate _MAKE_HEAP_SAFE when we *know* the value is immortal + if (sym_is_immortal(PyJitRef_Unwrap(value))) { + ADD_OP(_NOP, 0, 0); + } + value = PyJitRef_StripBorrowInfo(value); + } + + op(_COPY_FREE_VARS, (--)) { + PyCodeObject *co = get_current_code_object(ctx); + if (co == NULL) { + ctx->done = true; + break; + } + int offset = co->co_nlocalsplus - oparg; + for (int i = 0; i < oparg; ++i) { + ctx->frame->locals[offset + i] = sym_new_not_null(ctx); + } + } + op(_LOAD_FAST_CHECK, (-- value)) { value = GETLOCAL(oparg); // We guarantee this will error - just bail and don't optimize it. if (sym_is_null(value)) { ctx->done = true; } + assert(!PyJitRef_IsUnique(value)); } op(_LOAD_FAST, (-- value)) { value = GETLOCAL(oparg); + assert(!PyJitRef_IsUnique(value)); } op(_LOAD_FAST_BORROW, (-- value)) { value = PyJitRef_Borrow(GETLOCAL(oparg)); + assert(!PyJitRef_IsUnique(value)); } op(_LOAD_FAST_AND_CLEAR, (-- value)) { value = GETLOCAL(oparg); JitOptRef temp = sym_new_null(ctx); GETLOCAL(oparg) = temp; + assert(!PyJitRef_IsUnique(value)); + } + + op(_GUARD_TYPE_VERSION_LOCKED, (type_version/2, owner -- owner)) { + assert(type_version); + if (sym_matches_type_version(owner, type_version)) { + ADD_OP(_NOP, 0, 0); + } + else { + PyTypeObject *probable_type = sym_get_probable_type(owner); + if (probable_type != NULL && + probable_type->tp_version_tag == type_version) { + // Promote the probable type version to a known one. + sym_set_type(owner, probable_type); + sym_set_type_version(owner, type_version); + watch_type(probable_type, dependencies); + } + else { + ctx->contradiction = true; + ctx->done = true; + break; + } + } + } + + op(_STORE_ATTR_INSTANCE_VALUE, (offset/1, value, owner -- o)) { + (void)offset; + (void)value; + o = owner; + } + + op(_STORE_ATTR_WITH_HINT, (hint/1, value, owner -- o)) { + (void)hint; + (void)value; + o = owner; + } + + op(_SWAP_FAST, (value -- trash)) { + JitOptRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyJitRef_RemoveUnique(value); + trash = tmp; + } + + op(_STORE_SUBSCR_LIST_INT, (value, list_st, sub_st -- ls, ss)) { + (void)value; + ls = list_st; + ss = sub_st; + } + + op(_STORE_ATTR_SLOT, (index/1, value, owner -- o)) { + (void)index; + (void)value; + o = owner; } - op(_STORE_FAST, (value --)) { - GETLOCAL(oparg) = value; + op(_STORE_SUBSCR_DICT, (value, dict_st, sub -- st)) { + PyObject *sub_o = sym_get_const(ctx, sub); + if (sub_o != NULL) { + optimize_dict_known_hash(ctx, dependencies, this_instr, + sub_o, _STORE_SUBSCR_DICT_KNOWN_HASH); + } + (void)value; + st = dict_st; } op(_PUSH_NULL, (-- res)) { - res = sym_new_null(ctx); + res = PyJitRef_Borrow(sym_new_null(ctx)); } op(_GUARD_TOS_INT, (value -- value)) { if (sym_is_compact_int(value)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } else { if (sym_get_type(value) == &PyLong_Type) { - REPLACE_OP(this_instr, _GUARD_TOS_OVERFLOWED, 0, 0); + ADD_OP(_GUARD_TOS_OVERFLOWED, 0, 0); } sym_set_compact_int(value); } @@ -121,72 +219,124 @@ dummy_func(void) { op(_GUARD_NOS_INT, (left, unused -- left, unused)) { if (sym_is_compact_int(left)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } else { if (sym_get_type(left) == &PyLong_Type) { - REPLACE_OP(this_instr, _GUARD_NOS_OVERFLOWED, 0, 0); + ADD_OP(_GUARD_NOS_OVERFLOWED, 0, 0); } sym_set_compact_int(left); } } op(_CHECK_ATTR_CLASS, (type_version/2, owner -- owner)) { - PyObject *type = (PyObject *)_PyType_LookupByVersion(type_version); - if (type) { + PyObject *type = sym_get_probable_value(owner); + if (type != NULL && ((PyTypeObject *)type)->tp_version_tag == type_version) { if (type == sym_get_const(ctx, owner)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } else { sym_set_const(owner, type); + watch_type((PyTypeObject *)type, dependencies); } } } op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner)) { assert(type_version); + assert(this_instr[-1].opcode == _RECORD_TOS_TYPE || this_instr[-1].opcode == _RECORD_TOS); if (sym_matches_type_version(owner, type_version)) { - REPLACE_OP(this_instr, _NOP, 0, 0); - } else { - // add watcher so that whenever the type changes we invalidate this - PyTypeObject *type = _PyType_LookupByVersion(type_version); - // if the type is null, it was not found in the cache (there was a conflict) - // with the key, in which case we can't trust the version - if (type) { - // if the type version was set properly, then add a watcher - // if it wasn't this means that the type version was previously set to something else - // and we set the owner to bottom, so we don't need to add a watcher because we must have - // already added one earlier. - if (sym_set_type_version(owner, type_version)) { - PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type); - _Py_BloomFilter_Add(dependencies, type); - } + ADD_OP(_NOP, 0, 0); + } + else { + PyTypeObject *probable_type = sym_get_probable_type(owner); + if (probable_type != NULL && + probable_type->tp_version_tag == type_version) { + sym_set_type(owner, probable_type); + sym_set_type_version(owner, type_version); + watch_type(probable_type, dependencies); + } + else { + ctx->contradiction = true; + ctx->done = true; + break; } - } } op(_GUARD_TOS_FLOAT, (value -- value)) { if (sym_matches_type(value, &PyFloat_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } sym_set_type(value, &PyFloat_Type); } op(_GUARD_NOS_FLOAT, (left, unused -- left, unused)) { if (sym_matches_type(left, &PyFloat_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } sym_set_type(left, &PyFloat_Type); } - op(_BINARY_OP, (lhs, rhs -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(lhs, rhs); + op(_BINARY_OP, (lhs, rhs -- res, l, r)) { + l = lhs; + r = rhs; + REPLACE_OPCODE_IF_EVALUATES_PURE(lhs, rhs, res); bool lhs_int = sym_matches_type(lhs, &PyLong_Type); bool rhs_int = sym_matches_type(rhs, &PyLong_Type); bool lhs_float = sym_matches_type(lhs, &PyFloat_Type); bool rhs_float = sym_matches_type(rhs, &PyFloat_Type); - if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) { + bool is_truediv = (oparg == NB_TRUE_DIVIDE + || oparg == NB_INPLACE_TRUE_DIVIDE); + bool is_remainder = (oparg == NB_REMAINDER + || oparg == NB_INPLACE_REMAINDER); + int emit_op = _BINARY_OP; + // Promote probable-float operands to known floats via speculative + // guards. _RECORD_TOS_TYPE / _RECORD_NOS_TYPE in the BINARY_OP macro + // record the observed operand type during tracing, which + // sym_get_probable_type reads here. Applied only to ops where + // narrowing unlocks a meaningful downstream win: + // - NB_TRUE_DIVIDE: enables the specialized float path below. + // - NB_REMAINDER: lets the float result type propagate. + // NB_POWER is excluded: speculative guards there regressed + // test_power_type_depends_on_input_values (GH-127844). + if (is_truediv || is_remainder) { + if (!sym_has_type(rhs) + && sym_get_probable_type(rhs) == &PyFloat_Type) { + ADD_OP(_GUARD_TOS_FLOAT, 0, 0); + sym_set_type(rhs, &PyFloat_Type); + rhs_float = true; + } + if (!sym_has_type(lhs) + && sym_get_probable_type(lhs) == &PyFloat_Type) { + ADD_OP(_GUARD_NOS_FLOAT, 0, 0); + sym_set_type(lhs, &PyFloat_Type); + lhs_float = true; + } + } + if (is_truediv && lhs_float && rhs_float) { + if (PyJitRef_IsUnique(lhs)) { + emit_op = _BINARY_OP_TRUEDIV_FLOAT_INPLACE; + l = sym_new_null(ctx); + r = rhs; + } + else if (PyJitRef_IsUnique(rhs)) { + emit_op = _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT; + l = lhs; + r = sym_new_null(ctx); + } + else { + emit_op = _BINARY_OP_TRUEDIV_FLOAT; + l = lhs; + r = rhs; + } + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); + } + else if (is_truediv + && (lhs_int || lhs_float) && (rhs_int || rhs_float)) { + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); + } + else if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) { // There's something other than an int or float involved: res = sym_new_unknown(ctx); } @@ -209,7 +359,7 @@ dummy_func(void) { } else if (lhs_float) { // Case C: - res = sym_new_type(ctx, &PyFloat_Type); + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); } else if (!sym_is_const(ctx, rhs)) { // Case A or B... can't know without the sign of the RHS: @@ -217,73 +367,179 @@ dummy_func(void) { } else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(ctx, rhs))) { // Case B: - res = sym_new_type(ctx, &PyFloat_Type); + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); } else { // Case A: res = sym_new_type(ctx, &PyLong_Type); } } - else if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) { - res = sym_new_type(ctx, &PyFloat_Type); - } else if (lhs_int && rhs_int) { res = sym_new_type(ctx, &PyLong_Type); } else { - res = sym_new_type(ctx, &PyFloat_Type); + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); } + ADD_OP(emit_op, oparg, 0); } - op(_BINARY_OP_ADD_INT, (left, right -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(left, right); - res = sym_new_compact_int(ctx); + op(_BINARY_OP_ADD_INT, (left, right -- res, l, r)) { + if (PyJitRef_IsUnique(left)) { + REPLACE_OP(this_instr, _BINARY_OP_ADD_INT_INPLACE, 0, 0); + } + else if (PyJitRef_IsUnique(right)) { + REPLACE_OP(this_instr, _BINARY_OP_ADD_INT_INPLACE_RIGHT, 0, 0); + } + // Result may be a unique compact int or a cached small int + // at runtime. Mark as unique; inplace ops verify at runtime. + res = PyJitRef_MakeUnique(sym_new_compact_int(ctx)); + l = left; + r = right; + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res); } - op(_BINARY_OP_SUBTRACT_INT, (left, right -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(left, right); - res = sym_new_compact_int(ctx); + op(_BINARY_OP_SUBTRACT_INT, (left, right -- res, l, r)) { + if (PyJitRef_IsUnique(left)) { + REPLACE_OP(this_instr, _BINARY_OP_SUBTRACT_INT_INPLACE, 0, 0); + } + else if (PyJitRef_IsUnique(right)) { + REPLACE_OP(this_instr, _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT, 0, 0); + } + res = PyJitRef_MakeUnique(sym_new_compact_int(ctx)); + l = left; + r = right; + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res); } - op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(left, right); - res = sym_new_compact_int(ctx); + op(_BINARY_OP_MULTIPLY_INT, (left, right -- res, l, r)) { + if (PyJitRef_IsUnique(left)) { + REPLACE_OP(this_instr, _BINARY_OP_MULTIPLY_INT_INPLACE, 0, 0); + } + else if (PyJitRef_IsUnique(right)) { + REPLACE_OP(this_instr, _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT, 0, 0); + } + res = PyJitRef_MakeUnique(sym_new_compact_int(ctx)); + l = left; + r = right; + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res); } - op(_BINARY_OP_ADD_FLOAT, (left, right -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(left, right); - res = sym_new_type(ctx, &PyFloat_Type); - // TODO (gh-134584): Refactor this to use another uop - if (PyJitRef_IsBorrowed(left) && PyJitRef_IsBorrowed(right)) { - REPLACE_OP(this_instr, op_without_decref_inputs[opcode], oparg, 0); + op(_BINARY_OP_ADD_FLOAT, (left, right -- res, l, r)) { + if (PyJitRef_IsUnique(left)) { + ADD_OP(_BINARY_OP_ADD_FLOAT_INPLACE, 0, 0); + l = PyJitRef_Borrow(sym_new_null(ctx)); + r = right; } + else if (PyJitRef_IsUnique(right)) { + ADD_OP(_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT, 0, 0); + l = left; + r = PyJitRef_Borrow(sym_new_null(ctx)); + } + else { + l = left; + r = right; + } + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); } - op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(left, right); - res = sym_new_type(ctx, &PyFloat_Type); - // TODO (gh-134584): Refactor this to use another uop - if (PyJitRef_IsBorrowed(left) && PyJitRef_IsBorrowed(right)) { - REPLACE_OP(this_instr, op_without_decref_inputs[opcode], oparg, 0); + op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res, l, r)) { + if (PyJitRef_IsUnique(left)) { + ADD_OP(_BINARY_OP_SUBTRACT_FLOAT_INPLACE, 0, 0); + l = PyJitRef_Borrow(sym_new_null(ctx)); + r = right; + } + else if (PyJitRef_IsUnique(right)) { + ADD_OP(_BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT, 0, 0); + l = left; + r = PyJitRef_Borrow(sym_new_null(ctx)); } + else { + l = left; + r = right; + } + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); } - op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(left, right); - res = sym_new_type(ctx, &PyFloat_Type); - // TODO (gh-134584): Refactor this to use another uop - if (PyJitRef_IsBorrowed(left) && PyJitRef_IsBorrowed(right)) { - REPLACE_OP(this_instr, op_without_decref_inputs[opcode], oparg, 0); + op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res, l, r)) { + if (PyJitRef_IsUnique(left)) { + ADD_OP(_BINARY_OP_MULTIPLY_FLOAT_INPLACE, 0, 0); + l = PyJitRef_Borrow(sym_new_null(ctx)); + r = right; + } + else if (PyJitRef_IsUnique(right)) { + ADD_OP(_BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT, 0, 0); + l = left; + r = PyJitRef_Borrow(sym_new_null(ctx)); + } + else { + l = left; + r = right; } + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); } - op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(left, right); + op(_BINARY_OP_ADD_UNICODE, (left, right -- res, l, r)) { res = sym_new_type(ctx, &PyUnicode_Type); + l = left; + r = right; + } + + op(_GUARD_BINARY_OP_EXTEND_LHS, (descr/4, left, right -- left, right)) { + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr; + assert(d != NULL && d->guard == NULL && d->lhs_type != NULL); + if (sym_matches_type(left, d->lhs_type)) { + ADD_OP(_NOP, 0, 0); + } + sym_set_type(left, d->lhs_type); + } + + op(_GUARD_BINARY_OP_EXTEND_RHS, (descr/4, left, right -- left, right)) { + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr; + assert(d != NULL && d->guard == NULL && d->rhs_type != NULL); + if (sym_matches_type(right, d->rhs_type)) { + ADD_OP(_NOP, 0, 0); + } + sym_set_type(right, d->rhs_type); + } + + op(_GUARD_BINARY_OP_EXTEND, (descr/4, left, right -- left, right)) { + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr; + if (d != NULL && d->guard == NULL) { + /* guard == NULL means the check is purely a type test against + lhs_type/rhs_type, so eliminate it when types are already known. */ + assert(d->lhs_type != NULL && d->rhs_type != NULL); + bool lhs_known = sym_matches_type(left, d->lhs_type); + bool rhs_known = sym_matches_type(right, d->rhs_type); + if (lhs_known && rhs_known) { + ADD_OP(_NOP, 0, 0); + } + else if (lhs_known) { + ADD_OP(_GUARD_BINARY_OP_EXTEND_RHS, 0, (uintptr_t)d); + sym_set_type(right, d->rhs_type); + } + else if (rhs_known) { + ADD_OP(_GUARD_BINARY_OP_EXTEND_LHS, 0, (uintptr_t)d); + sym_set_type(left, d->lhs_type); + } + } + } + + op(_BINARY_OP_EXTEND, (descr/4, left, right -- res, l, r)) { + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr; + if (d != NULL && d->result_type != NULL) { + res = sym_new_type(ctx, d->result_type); + if (d->result_unique) { + res = PyJitRef_MakeUnique(res); + } + } + else { + res = sym_new_not_null(ctx); + } + l = left; + r = right; } - op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right -- )) { - JitOptRef res; + op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right -- res)) { if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) { assert(PyUnicode_CheckExact(sym_get_const(ctx, left))); assert(PyUnicode_CheckExact(sym_get_const(ctx, right))); @@ -297,26 +553,66 @@ dummy_func(void) { else { res = sym_new_type(ctx, &PyUnicode_Type); } - // _STORE_FAST: - GETLOCAL(this_instr->operand0) = res; + GETLOCAL(this_instr->operand0) = sym_new_null(ctx); } - op(_BINARY_OP_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame)) { - new_frame = PyJitRef_NULL; - ctx->done = true; + op(_BINARY_OP_SUBSCR_CHECK_FUNC, (container, unused -- container, unused, getitem)) { + getitem = sym_new_not_null(ctx); + PyTypeObject *tp = sym_get_type(container); + if (tp == NULL) { + PyObject *c = sym_get_probable_value(container); + if (c != NULL) { + tp = Py_TYPE(c); + } + } + if (tp != NULL) { + PyObject *getitem_o = ((PyHeapTypeObject *)tp)->_spec_cache.getitem; + sym_set_recorded_value(getitem, getitem_o); + } } - op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res)) { + op(_BINARY_OP_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame)) { + _Py_UOpsAbstractFrame *f = frame_new_from_symbol(ctx, getitem, NULL, 0); + if (f == NULL) { + break; + } + f->locals[0] = container; + f->locals[1] = sub; + new_frame = PyJitRef_WrapInvalid(f); + } + + op(_BINARY_OP_SUBSCR_STR_INT, (str_st, sub_st -- res, s, i)) { res = sym_new_type(ctx, &PyUnicode_Type); + s = str_st; + i = sub_st; + } + + op(_BINARY_OP_SUBSCR_USTR_INT, (str_st, sub_st -- res, s, i)) { + res = sym_new_type(ctx, &PyUnicode_Type); + s = str_st; + i = sub_st; + } + + op(_GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS, (tuple_st, sub_st -- tuple_st, sub_st)) { + assert(sym_matches_type(tuple_st, &PyTuple_Type)); + if (sym_is_const(ctx, sub_st)) { + assert(PyLong_CheckExact(sym_get_const(ctx, sub_st))); + long index = PyLong_AsLong(sym_get_const(ctx, sub_st)); + assert(index >= 0); + Py_ssize_t tuple_length = sym_tuple_length(tuple_st); + if (tuple_length != -1 && index < tuple_length) { + ADD_OP(_NOP, 0, 0); + } + } } - op(_BINARY_OP_SUBSCR_TUPLE_INT, (tuple_st, sub_st -- res)) { + op(_BINARY_OP_SUBSCR_TUPLE_INT, (tuple_st, sub_st -- res, ts, ss)) { assert(sym_matches_type(tuple_st, &PyTuple_Type)); if (sym_is_const(ctx, sub_st)) { assert(PyLong_CheckExact(sym_get_const(ctx, sub_st))); long index = PyLong_AsLong(sym_get_const(ctx, sub_st)); assert(index >= 0); - int tuple_length = sym_tuple_length(tuple_st); + Py_ssize_t tuple_length = sym_tuple_length(tuple_st); if (tuple_length == -1) { // Unknown length res = sym_new_not_null(ctx); @@ -329,40 +625,69 @@ dummy_func(void) { else { res = sym_new_not_null(ctx); } + ts = tuple_st; + ss = sub_st; + } + + op(_BINARY_OP_SUBSCR_DICT, (dict_st, sub_st -- res, ds, ss)) { + res = sym_new_not_null(ctx); + ds = dict_st; + ss = sub_st; + PyObject *sub = sym_get_const(ctx, sub_st); + if (sym_is_not_container(sub_st) && + sym_matches_type(dict_st, &PyFrozenDict_Type)) { + REPLACE_OPCODE_IF_EVALUATES_PURE(dict_st, sub_st, res); + } + else if (sub != NULL) { + optimize_dict_known_hash(ctx, dependencies, this_instr, + sub, _BINARY_OP_SUBSCR_DICT_KNOWN_HASH); + } + } + + op(_BINARY_OP_SUBSCR_LIST_SLICE, (list_st, sub_st -- res, ls, ss)) { + res = sym_new_type(ctx, &PyList_Type); + ls = list_st; + ss = sub_st; } op(_TO_BOOL, (value -- res)) { - int already_bool = optimize_to_bool(this_instr, ctx, value, &res); + int already_bool = optimize_to_bool(this_instr, ctx, value, &res, + _POP_TOP, _NOP); if (!already_bool) { res = sym_new_truthiness(ctx, value, true); } } op(_TO_BOOL_BOOL, (value -- value)) { - int already_bool = optimize_to_bool(this_instr, ctx, value, &value); + int already_bool = optimize_to_bool(this_instr, ctx, value, &value, + _POP_TOP, _NOP); if (!already_bool) { sym_set_type(value, &PyBool_Type); - value = sym_new_truthiness(ctx, value, true); } } - op(_TO_BOOL_INT, (value -- res)) { - int already_bool = optimize_to_bool(this_instr, ctx, value, &res); + op(_TO_BOOL_INT, (value -- res, v)) { + int already_bool = optimize_to_bool(this_instr, ctx, value, &res, + _NOP, _SWAP); if (!already_bool) { sym_set_type(value, &PyLong_Type); res = sym_new_truthiness(ctx, value, true); } + v = value; } - op(_TO_BOOL_LIST, (value -- res)) { - int already_bool = optimize_to_bool(this_instr, ctx, value, &res); + op(_TO_BOOL_LIST, (value -- res, v)) { + int already_bool = optimize_to_bool(this_instr, ctx, value, &res, + _NOP, _SWAP); if (!already_bool) { res = sym_new_type(ctx, &PyBool_Type); } + v = value; } op(_TO_BOOL_NONE, (value -- res)) { - int already_bool = optimize_to_bool(this_instr, ctx, value, &res); + int already_bool = optimize_to_bool(this_instr, ctx, value, &res, + _POP_TOP, _NOP); if (!already_bool) { sym_set_const(value, Py_None); res = sym_new_const(ctx, Py_False); @@ -371,33 +696,46 @@ dummy_func(void) { op(_GUARD_NOS_UNICODE, (nos, unused -- nos, unused)) { if (sym_matches_type(nos, &PyUnicode_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } sym_set_type(nos, &PyUnicode_Type); } + op(_GUARD_NOS_COMPACT_ASCII, (nos, unused -- nos, unused)) { + sym_set_type(nos, &PyUnicode_Type); + } + op(_GUARD_TOS_UNICODE, (value -- value)) { if (sym_matches_type(value, &PyUnicode_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } sym_set_type(value, &PyUnicode_Type); } - op(_TO_BOOL_STR, (value -- res)) { - int already_bool = optimize_to_bool(this_instr, ctx, value, &res); + op(_TO_BOOL_STR, (value -- res, v)) { + int already_bool = optimize_to_bool(this_instr, ctx, value, &res, + _NOP, _SWAP); + v = value; if (!already_bool) { res = sym_new_truthiness(ctx, value, true); } } op(_UNARY_NOT, (value -- res)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(value); + REPLACE_OPCODE_IF_EVALUATES_PURE(value, res); sym_set_type(value, &PyBool_Type); res = sym_new_truthiness(ctx, value, false); } - op(_UNARY_NEGATIVE, (value -- res)) { - if (sym_is_compact_int(value)) { + op(_UNARY_NEGATIVE, (value -- res, v)) { + v = value; + REPLACE_OPCODE_IF_EVALUATES_PURE(value, res); + if (sym_matches_type(value, &PyFloat_Type) && PyJitRef_IsUnique(value)) { + ADD_OP(_UNARY_NEGATIVE_FLOAT_INPLACE, 0, 0); + v = PyJitRef_Borrow(sym_new_null(ctx)); + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); + } + else if (sym_is_compact_int(value)) { res = sym_new_compact_int(ctx); } else { @@ -411,7 +749,12 @@ dummy_func(void) { } } - op(_UNARY_INVERT, (value -- res)) { + op(_UNARY_INVERT, (value -- res, v)) { + v = value; + // Required to avoid a warning due to the deprecation of bitwise inversion of bools + if (!sym_matches_type(value, &PyBool_Type)) { + REPLACE_OPCODE_IF_EVALUATES_PURE(value, res); + } if (sym_matches_type(value, &PyLong_Type)) { res = sym_new_type(ctx, &PyLong_Type); } @@ -421,6 +764,9 @@ dummy_func(void) { } op(_COMPARE_OP, (left, right -- res)) { + // Comparison between bytes and str or int is not impacted by this optimization as bytes + // is not a safe type (due to its ability to raise a warning during comparisons). + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res); if (oparg & 16) { res = sym_new_type(ctx, &PyBool_Type); } @@ -429,54 +775,106 @@ dummy_func(void) { } } - op(_COMPARE_OP_INT, (left, right -- res)) { - if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) { - assert(PyLong_CheckExact(sym_get_const(ctx, left))); - assert(PyLong_CheckExact(sym_get_const(ctx, right))); - PyObject *tmp = PyObject_RichCompare(sym_get_const(ctx, left), - sym_get_const(ctx, right), - oparg >> 5); - if (tmp == NULL) { - goto error; - } - assert(PyBool_Check(tmp)); - assert(_Py_IsImmortal(tmp)); - REPLACE_OP(this_instr, _POP_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)tmp); - res = sym_new_const(ctx, tmp); - Py_DECREF(tmp); + op(_COMPARE_OP_INT, (left, right -- res, l, r)) { + int cmp_mask = oparg & (COMPARE_LT_MASK | COMPARE_GT_MASK | COMPARE_EQ_MASK); + + if (cmp_mask == COMPARE_EQ_MASK) { + res = sym_new_predicate(ctx, left, right, JIT_PRED_EQ); + } + else if (cmp_mask == (COMPARE_LT_MASK | COMPARE_GT_MASK)) { + res = sym_new_predicate(ctx, left, right, JIT_PRED_NE); } else { res = sym_new_type(ctx, &PyBool_Type); } + l = left; + r = right; + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res); } - op(_COMPARE_OP_FLOAT, (left, right -- res)) { - res = sym_new_type(ctx, &PyBool_Type); + op(_COMPARE_OP_FLOAT, (left, right -- res, l, r)) { + int cmp_mask = oparg & (COMPARE_LT_MASK | COMPARE_GT_MASK | COMPARE_EQ_MASK); + + if (cmp_mask == COMPARE_EQ_MASK) { + res = sym_new_predicate(ctx, left, right, JIT_PRED_EQ); + } + else if (cmp_mask == (COMPARE_LT_MASK | COMPARE_GT_MASK)) { + res = sym_new_predicate(ctx, left, right, JIT_PRED_NE); + } + else { + res = sym_new_type(ctx, &PyBool_Type); + } + l = left; + r = right; + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res); } - op(_COMPARE_OP_STR, (left, right -- res)) { + op(_COMPARE_OP_STR, (left, right -- res, l, r)) { + /* Cannot use predicate optimization here, as `a == b` + * does not imply that `a` is equivalent to `b`. `a` may be + * mortal, while `b` is immortal */ res = sym_new_type(ctx, &PyBool_Type); + l = left; + r = right; + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, res); } - op(_IS_OP, (left, right -- b)) { - b = sym_new_type(ctx, &PyBool_Type); + op(_IS_OP, (left, right -- b, l, r)) { + b = sym_new_predicate(ctx, left, right, (oparg ? JIT_PRED_IS_NOT : JIT_PRED_IS)); + l = left; + r = right; + } + + op(_IS_NONE, (value -- b)) { + if (sym_is_const(ctx, value)) { + PyObject *value_o = sym_get_const(ctx, value); + b = sym_new_const(ctx, Py_IsNone(value_o) ? Py_True : Py_False); + } + else { + b = sym_new_type(ctx, &PyBool_Type); + } } - op(_CONTAINS_OP, (left, right -- b)) { + op(_CONTAINS_OP, (left, right -- b, l, r)) { b = sym_new_type(ctx, &PyBool_Type); + l = left; + r = right; + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, b); } - op(_CONTAINS_OP_SET, (left, right -- b)) { + op(_CONTAINS_OP_SET, (left, right -- b, l, r)) { b = sym_new_type(ctx, &PyBool_Type); + l = left; + r = right; + if (sym_is_not_container(left) && + sym_matches_type(right, &PyFrozenSet_Type)) { + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, b); + } } - op(_CONTAINS_OP_DICT, (left, right -- b)) { + op(_CONTAINS_OP_DICT, (left, right -- b, l, r)) { b = sym_new_type(ctx, &PyBool_Type); + l = left; + r = right; + if (sym_is_not_container(left) && + sym_matches_type(right, &PyFrozenDict_Type)) { + REPLACE_OPCODE_IF_EVALUATES_PURE(left, right, b); + } } op(_LOAD_CONST, (-- value)) { + PyCodeObject *co = get_current_code_object(ctx); PyObject *val = PyTuple_GET_ITEM(co->co_consts, oparg); - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); + value = PyJitRef_Borrow(sym_new_const(ctx, val)); + } + + op(_LOAD_COMMON_CONSTANT, (-- value)) { + assert(oparg < NUM_COMMON_CONSTANTS); + PyObject *val = PyStackRef_AsPyObjectBorrow( + _PyInterpreterState_GET()->common_consts[oparg]); + assert(_Py_IsImmortal(val)); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); value = PyJitRef_Borrow(sym_new_const(ctx, val)); } @@ -484,7 +882,7 @@ dummy_func(void) { PyObject *val = PyLong_FromLong(oparg); assert(val); assert(_Py_IsImmortal(val)); - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); value = PyJitRef_Borrow(sym_new_const(ctx, val)); } @@ -496,46 +894,37 @@ dummy_func(void) { value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); } - op(_POP_TOP_LOAD_CONST_INLINE, (ptr/4, pop -- value)) { - value = sym_new_const(ctx, ptr); - } - - op(_POP_TOP_LOAD_CONST_INLINE_BORROW, (ptr/4, pop -- value)) { - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); + op(_POP_TOP_OPARG, (args[oparg] --)) { + for (int i = oparg-1; i >= 0; i--) { + optimize_pop_top(ctx, this_instr, args[i]); + } } - op(_POP_CALL_LOAD_CONST_INLINE_BORROW, (ptr/4, unused, unused -- value)) { - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); + op(_POP_TOP, (value -- )) { + optimize_pop_top(ctx, this_instr, value); } - op(_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, (ptr/4, unused, unused, unused -- value)) { - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); + op(_POP_TOP_INT, (value --)) { + if (PyJitRef_IsBorrowed(value)) { + ADD_OP(_POP_TOP_NOP, 0, 0); + } } - op(_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, (ptr/4, unused, unused, unused, unused -- value)) { - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); + op(_POP_TOP_FLOAT, (value --)) { + if (PyJitRef_IsBorrowed(value)) { + ADD_OP(_POP_TOP_NOP, 0, 0); + } } - op(_POP_TOP, (value -- )) { - PyTypeObject *typ = sym_get_type(value); - if (PyJitRef_IsBorrowed(value) || - sym_is_immortal(PyJitRef_Unwrap(value)) || - sym_is_null(value)) { - REPLACE_OP(this_instr, _POP_TOP_NOP, 0, 0); - } - else if (typ == &PyLong_Type) { - REPLACE_OP(this_instr, _POP_TOP_INT, 0, 0); - } - else if (typ == &PyFloat_Type) { - REPLACE_OP(this_instr, _POP_TOP_FLOAT, 0, 0); - } - else if (typ == &PyUnicode_Type) { - REPLACE_OP(this_instr, _POP_TOP_UNICODE, 0, 0); + op(_POP_TOP_UNICODE, (value --)) { + if (PyJitRef_IsBorrowed(value)) { + ADD_OP(_POP_TOP_NOP, 0, 0); } } op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { assert(oparg > 0); + bottom = PyJitRef_RemoveUnique(bottom); top = bottom; } @@ -546,12 +935,20 @@ dummy_func(void) { assert(oparg >= 2); } - op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr)) { + op(_RROT_3, (bottom, middle, top -- bottom, middle, top)) { + JitOptRef temp = top; + top = middle; + middle = bottom; + bottom = temp; + } + + op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr, o)) { attr = sym_new_not_null(ctx); (void)offset; + o = owner; } - op(_LOAD_ATTR_MODULE, (dict_version/2, index/1, owner -- attr)) { + op(_LOAD_ATTR_MODULE, (dict_version/2, index/1, owner -- attr, o)) { (void)dict_version; (void)index; attr = PyJitRef_NULL; @@ -563,11 +960,15 @@ dummy_func(void) { if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { PyDict_Watch(GLOBALS_WATCHER_ID, dict); _Py_BloomFilter_Add(dependencies, dict); - PyObject *res = convert_global_to_const(this_instr, dict, true); + PyObject *res = convert_global_to_const(this_instr, dict); if (res == NULL) { attr = sym_new_not_null(ctx); } else { + bool immortal = _Py_IsImmortal(res); + ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE, + 0, (uintptr_t)res); + ADD_OP(_SWAP, 2, 0); attr = sym_new_const(ctx, res); } @@ -578,119 +979,239 @@ dummy_func(void) { /* No conversion made. We don't know what `attr` is. */ attr = sym_new_not_null(ctx); } + o = owner; } op (_PUSH_NULL_CONDITIONAL, ( -- null[oparg & 1])) { if (oparg & 1) { - REPLACE_OP(this_instr, _PUSH_NULL, 0, 0); + ADD_OP(_PUSH_NULL, 0, 0); null[0] = sym_new_null(ctx); } else { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } } - op(_LOAD_ATTR, (owner -- attr[1], self_or_null[oparg&1])) { + op(_LOAD_ATTR, (owner -- attr, self_or_null[oparg&1])) { (void)owner; - *attr = sym_new_not_null(ctx); + attr = sym_new_not_null(ctx); if (oparg & 1) { self_or_null[0] = sym_new_unknown(ctx); } } - op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr)) { + op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr, o)) { attr = sym_new_not_null(ctx); (void)hint; + o = owner; } - op(_LOAD_ATTR_SLOT, (index/1, owner -- attr)) { + op(_LOAD_ATTR_SLOT, (index/1, owner -- attr, o)) { attr = sym_new_not_null(ctx); (void)index; + o = owner; } op(_LOAD_ATTR_CLASS, (descr/4, owner -- attr)) { (void)descr; PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _POP_TOP, _NOP); } op(_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, (descr/4, owner -- attr)) { (void)descr; PyTypeObject *type = sym_get_type(owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _POP_TOP, _NOP); } op(_LOAD_ATTR_NONDESCRIPTOR_NO_DICT, (descr/4, owner -- attr)) { (void)descr; PyTypeObject *type = sym_get_type(owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _POP_TOP, _NOP); } op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self)) { (void)descr; PyTypeObject *type = sym_get_type(owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _LOAD_CONST_UNDER_INLINE_BORROW, - _LOAD_CONST_UNDER_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _NOP, _SWAP); self = owner; } op(_LOAD_ATTR_METHOD_NO_DICT, (descr/4, owner -- attr, self)) { (void)descr; PyTypeObject *type = sym_get_type(owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _LOAD_CONST_UNDER_INLINE_BORROW, - _LOAD_CONST_UNDER_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _NOP, _SWAP); self = owner; } op(_LOAD_ATTR_METHOD_LAZY_DICT, (descr/4, owner -- attr, self)) { (void)descr; PyTypeObject *type = sym_get_type(owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _LOAD_CONST_UNDER_INLINE_BORROW, - _LOAD_CONST_UNDER_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _NOP, _SWAP); self = owner; } - op(_LOAD_ATTR_PROPERTY_FRAME, (fget/4, owner -- new_frame)) { - (void)fget; - new_frame = PyJitRef_NULL; - ctx->done = true; + op(_GUARD_LOAD_SUPER_ATTR_METHOD, (global_super_st, class_st, unused -- global_super_st, class_st, unused)) { + if (sym_get_const(ctx, global_super_st) == (PyObject *)&PySuper_Type) { + PyTypeObject *probable = (PyTypeObject *)sym_get_probable_value(class_st); + PyTypeObject *known = (PyTypeObject *)sym_get_const(ctx, class_st); + // not known, but has a probable type, promote the probable type + if (known == NULL && probable != NULL && PyType_Check(probable)) { + ADD_OP(_GUARD_NOS_TYPE_VERSION, 0, probable->tp_version_tag); + known = probable; + } + sym_set_const(class_st, (PyObject *)known); + } + else { + sym_set_const(global_super_st, (PyObject *)&PySuper_Type); + sym_set_type(class_st, &PyType_Type); + } + } + + op(_LOAD_SUPER_ATTR_METHOD, (global_super_st, class_st, self_st -- attr, self_or_null)) { + self_or_null = self_st; + PyTypeObject *su_type = (PyTypeObject *)sym_get_const(ctx, class_st); + PyTypeObject *obj_type = sym_get_type(self_st); + PyObject *name = get_co_name(ctx, oparg >> 2); + attr = lookup_super_attr(ctx, dependencies, this_instr, + su_type, obj_type, name, + _LOAD_CONST_INLINE_BORROW, + _LOAD_CONST_INLINE, _SWAP); + } + + op(_LOAD_ATTR_PROPERTY_FRAME, (func_version/2, fget/4, owner -- new_frame)) { + PyFunctionObject *func = (PyFunctionObject *)fget; + if (sym_get_type_version(owner) == 0 || + func->func_version != func_version) { + ctx->contradiction = true; + ctx->done = true; + break; + } + _Py_BloomFilter_Add(dependencies, fget); + PyCodeObject *co = (PyCodeObject *)func->func_code; + _Py_UOpsAbstractFrame *f = frame_new(ctx, co, NULL, 0); + if (f == NULL) { + break; + } + f->locals[0] = owner; + f->func = func; + new_frame = PyJitRef_WrapInvalid(f); + } + + op(_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME, (func_version/2, getattribute/4, owner -- new_frame)) { + PyFunctionObject *func = (PyFunctionObject *)getattribute; + if (sym_get_type_version(owner) == 0 || + func->func_version != func_version) { + ctx->contradiction = true; + ctx->done = true; + break; + } + _Py_BloomFilter_Add(dependencies, func); + PyCodeObject *co = (PyCodeObject *)func->func_code; + _Py_UOpsAbstractFrame *f = frame_new(ctx, co, NULL, 0); + if (f == NULL) { + break; + } + PyObject *name = get_co_name(ctx, oparg >> 1); + f->locals[0] = owner; + f->locals[1] = sym_new_const(ctx, name); + f->func = func; + new_frame = PyJitRef_WrapInvalid(f); } op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { - callable = sym_new_not_null(ctx); - self_or_null = sym_new_not_null(ctx); + PyObject *bound_method = sym_get_probable_value(callable); + if (bound_method != NULL && Py_TYPE(bound_method) == &PyMethod_Type) { + PyMethodObject *method = (PyMethodObject *)bound_method; + callable = sym_new_not_null(ctx); + sym_set_recorded_value(callable, method->im_func); + self_or_null = sym_new_not_null(ctx); + sym_set_recorded_value(self_or_null, method->im_self); + } + else { + callable = sym_new_not_null(ctx); + self_or_null = sym_new_not_null(ctx); + } } op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { - if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyFunction_Type)) { - assert(PyFunction_Check(sym_get_const(ctx, callable))); - REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); - this_instr->operand1 = (uintptr_t)sym_get_const(ctx, callable); + PyObject *func = sym_get_probable_value(callable); + if (func == NULL || !PyFunction_Check(func) || ((PyFunctionObject *)func)->func_version != func_version) { + ctx->contradiction = true; + ctx->done = true; + break; + } + // Guarded on this, so it can be promoted. + sym_set_const(callable, func); + _Py_BloomFilter_Add(dependencies, func); + } + + op(_CHECK_FUNCTION_VERSION_KW, (func_version/2, callable, unused, unused[oparg], unused -- callable, unused, unused[oparg], unused)) { + PyObject *func = sym_get_probable_value(callable); + if (func == NULL || !PyFunction_Check(func) || ((PyFunctionObject *)func)->func_version != func_version) { + ctx->contradiction = true; + ctx->done = true; + break; } - sym_set_type(callable, &PyFunction_Type); + // Guarded on this, so it can be promoted. + sym_set_const(callable, func); + _Py_BloomFilter_Add(dependencies, func); } op(_CHECK_METHOD_VERSION, (func_version/2, callable, null, unused[oparg] -- callable, null, unused[oparg])) { if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); assert(PyMethod_Check(method)); - REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); - this_instr->operand1 = (uintptr_t)method->im_func; + ADD_OP(_CHECK_FUNCTION_VERSION_INLINE, 0, func_version); + uop_buffer_last(&ctx->out_buffer)->operand1 = (uintptr_t)method->im_func; + } + else { + // Guarding on the bound method, safe to promote. + PyObject *bound_method = sym_get_probable_value(callable); + if (bound_method != NULL && Py_TYPE(bound_method) == &PyMethod_Type) { + PyMethodObject *method = (PyMethodObject *)bound_method; + PyObject *func = method->im_func; + if (PyFunction_Check(func) && + ((PyFunctionObject *)func)->func_version == func_version) { + _Py_BloomFilter_Add(dependencies, func); + sym_set_const(callable, bound_method); + } + } + } + sym_set_type(callable, &PyMethod_Type); + } + + op(_CHECK_METHOD_VERSION_KW, (func_version/2, callable, null, unused[oparg], unused -- callable, null, unused[oparg], unused)) { + if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { + PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); + assert(PyMethod_Check(method)); + ADD_OP(_CHECK_FUNCTION_VERSION_INLINE, 0, func_version); + uop_buffer_last(&ctx->out_buffer)->operand1 = (uintptr_t)method->im_func; + } + else { + // Guarding on the bound method, safe to promote. + PyObject *bound_method = sym_get_probable_value(callable); + if (bound_method != NULL && Py_TYPE(bound_method) == &PyMethod_Type) { + PyMethodObject *method = (PyMethodObject *)bound_method; + PyObject *func = method->im_func; + if (PyFunction_Check(func) && + ((PyFunctionObject *)func)->func_version == func_version) { + _Py_BloomFilter_Add(dependencies, func); + sym_set_const(callable, bound_method); + } + } } sym_set_type(callable, &PyMethod_Type); } @@ -701,8 +1222,8 @@ dummy_func(void) { if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) { PyFunctionObject *func = (PyFunctionObject *)sym_get_const(ctx, callable); PyCodeObject *co = (PyCodeObject *)func->func_code; - if (co->co_argcount == oparg + !sym_is_null(self_or_null)) { - REPLACE_OP(this_instr, _NOP, 0 ,0); + if (co->co_argcount == oparg + sym_is_not_null(self_or_null)) { + ADD_OP(_NOP, 0 ,0); } } } @@ -715,16 +1236,6 @@ dummy_func(void) { op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame)) { int argcount = oparg; - - PyCodeObject *co = NULL; - assert((this_instr + 2)->opcode == _PUSH_FRAME); - co = get_code_with_logging((this_instr + 2)); - if (co == NULL) { - ctx->done = true; - break; - } - - assert(!PyJitRef_IsNull(self_or_null)); assert(args != NULL); if (sym_is_not_null(self_or_null)) { @@ -734,9 +1245,33 @@ dummy_func(void) { } if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) { - new_frame = PyJitRef_Wrap((JitOptSymbol *)frame_new(ctx, co, 0, args, argcount)); + new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, args, argcount)); } else { - new_frame = PyJitRef_Wrap((JitOptSymbol *)frame_new(ctx, co, 0, NULL, 0)); + new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, NULL, 0)); + } + } + + op(_EXPAND_METHOD, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { + if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { + PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); + callable = sym_new_const(ctx, method->im_func); + self_or_null = sym_new_const(ctx, method->im_self); + } + else { + callable = sym_new_not_null(ctx); + self_or_null = sym_new_not_null(ctx); + } + } + + op(_EXPAND_METHOD_KW, (callable, self_or_null, unused[oparg], unused -- callable, self_or_null, unused[oparg], unused)) { + if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { + PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); + callable = sym_new_const(ctx, method->im_func); + self_or_null = sym_new_const(ctx, method->im_self); + } + else { + callable = sym_new_not_null(ctx); + self_or_null = sym_new_not_null(ctx); } } @@ -747,57 +1282,85 @@ dummy_func(void) { } op(_PY_FRAME_GENERAL, (callable, self_or_null, args[oparg] -- new_frame)) { - PyCodeObject *co = NULL; - assert((this_instr + 2)->opcode == _PUSH_FRAME); - co = get_code_with_logging((this_instr + 2)); - if (co == NULL) { - ctx->done = true; - break; - } - - new_frame = PyJitRef_Wrap((JitOptSymbol *)frame_new(ctx, co, 0, NULL, 0)); + new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, NULL, 0)); } op(_PY_FRAME_KW, (callable, self_or_null, args[oparg], kwnames -- new_frame)) { - new_frame = PyJitRef_NULL; - ctx->done = true; + new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, NULL, 0)); + } + + op(_PY_FRAME_EX, (func_st, null, callargs_st, kwargs_st -- ex_frame)) { + ex_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, func_st, NULL, 0)); + } + + op(_CHECK_OBJECT, (type_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { + PyObject *probable_callable = sym_get_probable_value(callable); + assert(probable_callable != NULL); + PyObject *const_callable = sym_get_const(ctx, callable); + bool is_probable = const_callable == NULL && probable_callable != NULL; + PyObject *callable_o = const_callable != NULL ? const_callable : probable_callable; + if (sym_is_null(self_or_null) && + callable_o != NULL && + PyType_Check(callable_o) && + ((PyTypeObject *)callable_o)->tp_version_tag == type_version) { + // Probable types need the guard. + if (!is_probable) { + ADD_OP(_NOP, 0, 0); + } + else { + // Promote the probable type, as we have + // guarded on it. + sym_set_const(callable, callable_o); + } + PyHeapTypeObject *cls = (PyHeapTypeObject *)callable_o; + PyObject *init = cls->_spec_cache.init; + assert(init != NULL); + assert(PyFunction_Check(init)); + callable = sym_new_const(ctx, init); + watch_type((PyTypeObject *)callable_o, dependencies); + } + else { + callable = sym_new_not_null(ctx); + } } - op(_CHECK_AND_ALLOCATE_OBJECT, (type_version/2, callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { - (void)type_version; - (void)args; - callable = sym_new_not_null(ctx); + op(_ALLOCATE_OBJECT, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { self_or_null = sym_new_not_null(ctx); } op(_CREATE_INIT_FRAME, (init, self, args[oparg] -- init_frame)) { - init_frame = PyJitRef_NULL; - ctx->done = true; + ctx->frame->stack_pointer = stack_pointer - oparg - 2; + _Py_UOpsAbstractFrame *shim = frame_new(ctx, (PyCodeObject *)&_Py_InitCleanup, NULL, 0); + if (shim == NULL) { + break; + } + /* Push self onto stack of shim */ + shim->stack_pointer[0] = self; + shim->stack_pointer++; + assert((int)(shim->stack_pointer - shim->stack) == 1); + ctx->frame = shim; + ctx->curr_frame_depth++; + assert((this_instr + 1)->opcode == _PUSH_FRAME); + init_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, init, args-1, oparg+1)); } op(_RETURN_VALUE, (retval -- res)) { - // We wrap and unwrap the value to mimic PyStackRef_MakeHeapSafe - // in bytecodes.c - JitOptRef temp = PyJitRef_Wrap(PyJitRef_Unwrap(retval)); + JitOptRef temp = retval; DEAD(retval); SAVE_STACK(); ctx->frame->stack_pointer = stack_pointer; - frame_pop(ctx); - stack_pointer = ctx->frame->stack_pointer; - - /* Stack space handling */ - assert(corresponding_check_stack == NULL); - assert(co != NULL); - int framesize = co->co_framesize; - assert(framesize > 0); - assert(framesize <= curr_space); - curr_space -= framesize; - - co = get_code(this_instr); - if (co == NULL) { - // might be impossible, but bailing is still safe + assert(this_instr[1].opcode == _RECORD_CODE); + PyCodeObject *returning_code = (PyCodeObject *)this_instr[1].operand0; + assert(PyCode_Check(returning_code)); + if (returning_code == NULL) { ctx->done = true; + break; + } + if (frame_pop(ctx, returning_code)) { + break; } + stack_pointer = ctx->frame->stack_pointer; + RELOAD_STACK(); res = temp; } @@ -805,33 +1368,81 @@ dummy_func(void) { op(_RETURN_GENERATOR, ( -- res)) { SYNC_SP(); ctx->frame->stack_pointer = stack_pointer; - frame_pop(ctx); + assert(this_instr[1].opcode == _RECORD_CODE); + PyCodeObject *returning_code = (PyCodeObject *)this_instr[1].operand0; + if (returning_code == NULL) { + ctx->done = true; + break; + } + assert(PyCode_Check(returning_code)); + if (frame_pop(ctx, returning_code)) { + break; + } stack_pointer = ctx->frame->stack_pointer; res = sym_new_unknown(ctx); + } - /* Stack space handling */ - assert(corresponding_check_stack == NULL); - assert(co != NULL); - int framesize = co->co_framesize; - assert(framesize > 0); - assert(framesize <= curr_space); - curr_space -= framesize; - - co = get_code(this_instr); - if (co == NULL) { - // might be impossible, but bailing is still safe + op(_YIELD_VALUE, (retval -- value)) { + JitOptRef temp = retval; + DEAD(retval); + SAVE_STACK(); + ctx->frame->stack_pointer = stack_pointer; + assert(this_instr[1].opcode == _RECORD_CODE); + PyCodeObject *returning_code = (PyCodeObject *)this_instr[1].operand0; + if (returning_code == NULL) { ctx->done = true; + break; } - } - - op(_YIELD_VALUE, (unused -- value)) { - value = sym_new_unknown(ctx); + assert(PyCode_Check(returning_code)); + if (frame_pop(ctx, returning_code)) { + break; + } + stack_pointer = ctx->frame->stack_pointer; + RELOAD_STACK(); + value = temp; } op(_GET_ITER, (iterable -- iter, index_or_null)) { - if (sym_matches_type(iterable, &PyTuple_Type) || sym_matches_type(iterable, &PyList_Type)) { + bool is_coro = false; + bool is_trad = false; // has `tp_iter` slot + bool definite = true; + PyTypeObject *tp = sym_get_type(iterable); + if (tp == NULL) { + definite = false; + tp = sym_get_probable_type(iterable); + } + if (oparg == GET_ITER_YIELD_FROM_NO_CHECK) { + if (tp == &PyCoro_Type) { + if (!definite) { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(iterable, tp); + } + ADD_OP(_PUSH_NULL, 0, 0); + is_coro = true; + } + } + if (tp != NULL && + tp->_tp_iteritem == NULL && + tp->tp_iter != NULL && + tp->tp_iter != PyObject_SelfIter && + tp->tp_flags & Py_TPFLAGS_IMMUTABLETYPE + ) { + assert(tp != &PyCoro_Type); + is_trad = true; + if (!definite) { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(iterable, tp); + } + ADD_OP(_GET_ITER_TRAD, 0, 0); + } + if (is_coro) { + assert(!is_trad); iter = iterable; - index_or_null = sym_new_not_null(ctx); + index_or_null = sym_new_null(ctx); + } + else if (is_trad) { + iter = sym_new_not_null(ctx); + index_or_null = sym_new_null(ctx); } else { iter = sym_new_not_null(ctx); @@ -839,61 +1450,131 @@ dummy_func(void) { } } - op(_FOR_ITER_GEN_FRAME, (unused, unused -- unused, unused, gen_frame)) { - gen_frame = PyJitRef_NULL; - /* We are about to hit the end of the trace */ - ctx->done = true; + op(_FOR_ITER_TIER_TWO, (iter, null_or_index -- iter, null_or_index, next)) { + bool definite = true; + PyTypeObject *type = sym_get_type(iter); + if (type == NULL) { + type = sym_get_probable_type(iter); + definite = false; + } + if (type != NULL && type != &PyGen_Type && type->tp_iternext != NULL + && !_PyType_HasSlotTpIternext(type)) { + PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type); + _Py_BloomFilter_Add(dependencies, type); + if (!definite) { + sym_set_type(iter, type); + assert((this_instr - 1)->opcode == _RECORD_NOS_TYPE); + int32_t orig_target = (this_instr - 1)->target; + ADD_OP(_GUARD_TYPE_ITER, 0, (uintptr_t)type); + uop_buffer_last(&ctx->out_buffer)->target = orig_target; + } + ADD_OP(_ITER_NEXT_INLINE, 0, (uintptr_t)type->tp_iternext); + } + next = sym_new_not_null(ctx); } - op(_SEND_GEN_FRAME, (unused, unused -- unused, gen_frame)) { - gen_frame = PyJitRef_NULL; - // We are about to hit the end of the trace: - ctx->done = true; + op(_GUARD_ITERATOR, (iterable -- iterable)) { + bool definite = true; + PyTypeObject *tp = sym_get_type(iterable); + if (tp == NULL) { + definite = false; + tp = sym_get_probable_type(iterable); + } + if (tp != NULL && tp->tp_iter == PyObject_SelfIter) { + if (definite) { + ADD_OP(_NOP, 0, 0); + } + else { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(iterable, tp); + } + } } - op(_CHECK_STACK_SPACE, (unused, unused, unused[oparg] -- unused, unused, unused[oparg])) { - assert(corresponding_check_stack == NULL); - corresponding_check_stack = this_instr; + op(_GUARD_ITER_VIRTUAL, (iterable -- iterable)) { + bool definite = true; + PyTypeObject *tp = sym_get_type(iterable); + if (tp == NULL) { + definite = false; + tp = sym_get_probable_type(iterable); + } + if (tp != NULL && tp->_tp_iteritem != NULL) { + if (definite) { + ADD_OP(_NOP, 0, 0); + } + else { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(iterable, tp); + } + } } - op (_CHECK_STACK_SPACE_OPERAND, (framesize/2 -- )) { - (void)framesize; - /* We should never see _CHECK_STACK_SPACE_OPERANDs. - * They are only created at the end of this pass. */ - Py_UNREACHABLE(); + op(_FOR_ITER_GEN_FRAME, (iter, unused -- iter, unused, gen_frame)) { + _Py_UOpsAbstractFrame *new_frame = frame_new_from_symbol(ctx, iter, NULL, 0); + if (new_frame == NULL) { + ctx->done = true; + break; + } + new_frame->stack_pointer[0] = sym_new_const(ctx, Py_None); + new_frame->stack_pointer++; + gen_frame = PyJitRef_WrapInvalid(new_frame); } - op(_PUSH_FRAME, (new_frame -- )) { - SYNC_SP(); - ctx->frame->stack_pointer = stack_pointer; - ctx->frame = (_Py_UOpsAbstractFrame *)PyJitRef_Unwrap(new_frame); - ctx->curr_frame_depth++; - stack_pointer = ctx->frame->stack_pointer; - co = get_code(this_instr); - if (co == NULL) { - // should be about to _EXIT_TRACE anyway + op(_SEND_GEN_FRAME, (receiver, null, v -- receiver, null, gen_frame)) { + _Py_UOpsAbstractFrame *new_frame = frame_new_from_symbol(ctx, receiver, NULL, 0); + if (new_frame == NULL) { ctx->done = true; break; } + new_frame->stack_pointer[0] = PyJitRef_StripReferenceInfo(v); + new_frame->stack_pointer++; + gen_frame = PyJitRef_WrapInvalid(new_frame); + } - /* Stack space handling */ - int framesize = co->co_framesize; - assert(framesize > 0); - curr_space += framesize; - if (curr_space < 0 || curr_space > INT32_MAX) { - // won't fit in signed 32-bit int + op(_CHECK_STACK_SPACE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { + PyCodeObject *co = sym_get_probable_func_code(callable); + if (co == NULL) { ctx->done = true; break; } - max_space = curr_space > max_space ? curr_space : max_space; - if (first_valid_check_stack == NULL) { - first_valid_check_stack = corresponding_check_stack; + ADD_OP(_CHECK_STACK_SPACE_OPERAND, 0, co->co_framesize); + } + + op (_CHECK_STACK_SPACE_OPERAND, (framesize/2 -- )) { + (void)framesize; + } + + op(_CHECK_IS_NOT_PY_CALLABLE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { + PyTypeObject *type = sym_get_type(callable); + if (type && type != &PyFunction_Type && type != &PyMethod_Type) { + ADD_OP(_NOP, 0, 0); } - else if (corresponding_check_stack) { - // delete all but the first valid _CHECK_STACK_SPACE - corresponding_check_stack->opcode = _NOP; + } + + op(_CHECK_IS_NOT_PY_CALLABLE_EX, (func_st, unused, unused, unused -- func_st, unused, unused, unused)) { + PyTypeObject *type = sym_get_type(func_st); + if (type && type != &PyFunction_Type) { + ADD_OP(_NOP, 0, 0); } - corresponding_check_stack = NULL; + } + + op(_CHECK_IS_NOT_PY_CALLABLE_KW, (callable, unused, unused[oparg], unused -- callable, unused, unused[oparg], unused)) { + PyTypeObject *type = sym_get_type(callable); + if (type && type != &PyFunction_Type && type != &PyMethod_Type) { + ADD_OP(_NOP, 0, 0); + } + } + + op(_PUSH_FRAME, (new_frame -- )) { + SYNC_SP(); + if (!CURRENT_FRAME_IS_INIT_SHIM()) { + ctx->frame->stack_pointer = stack_pointer; + } + ctx->frame->caller = true; + ctx->frame = (_Py_UOpsAbstractFrame *)PyJitRef_Unwrap(new_frame); + ctx->curr_frame_depth++; + stack_pointer = ctx->frame->stack_pointer; + assert(ctx->frame->locals != NULL); } op(_UNPACK_SEQUENCE, (seq -- values[oparg], top[0])) { @@ -915,38 +1596,63 @@ dummy_func(void) { op(_ITER_CHECK_TUPLE, (iter, null_or_index -- iter, null_or_index)) { if (sym_matches_type(iter, &PyTuple_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } sym_set_type(iter, &PyTuple_Type); } + op(_ITER_CHECK_LIST, (iter, null_or_index -- iter, null_or_index)) { + if (sym_matches_type(iter, &PyList_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(iter, &PyList_Type); + } + } + + op(_ITER_CHECK_RANGE, (iter, null_or_index -- iter, null_or_index)) { + if (sym_matches_type(iter, &PyRangeIter_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(iter, &PyRangeIter_Type); + } + } + op(_ITER_NEXT_RANGE, (iter, null_or_index -- iter, null_or_index, next)) { next = sym_new_type(ctx, &PyLong_Type); } - op(_CALL_TYPE_1, (unused, unused, arg -- res)) { + op(_CALL_TYPE_1, (callable, null, arg -- res, a)) { PyObject* type = (PyObject *)sym_get_type(arg); if (type) { res = sym_new_const(ctx, type); - REPLACE_OP(this_instr, _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, 0, - (uintptr_t)type); + ADD_OP(_SWAP, 3, 0); + optimize_pop_top(ctx, this_instr, callable); + optimize_pop_top(ctx, this_instr, null); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)type); + ADD_OP(_SWAP, 2, 0); } else { res = sym_new_not_null(ctx); } + a = arg; } - op(_CALL_STR_1, (unused, unused, arg -- res)) { + op(_CALL_STR_1, (unused, unused, arg -- res, a)) { if (sym_matches_type(arg, &PyUnicode_Type)) { // e.g. str('foo') or str(foo) where foo is known to be a string - res = arg; + // Note: we must strip the reference information because it goes + // through str() which strips the reference information from it. + res = PyJitRef_StripReferenceInfo(arg); } else { res = sym_new_type(ctx, &PyUnicode_Type); } + a = arg; } - op(_CALL_ISINSTANCE, (unused, unused, instance, cls -- res)) { + op(_CALL_ISINSTANCE, (callable, null, instance, cls -- res)) { // the result is always a bool, but sometimes we can // narrow it down to True or False res = sym_new_type(ctx, &PyBool_Type); @@ -962,24 +1668,329 @@ dummy_func(void) { out = Py_True; } sym_set_const(res, out); - REPLACE_OP(this_instr, _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)out); + optimize_pop_top(ctx, this_instr, cls); + optimize_pop_top(ctx, this_instr, instance); + optimize_pop_top(ctx, this_instr, null); + optimize_pop_top(ctx, this_instr, callable); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)out); + } + } + + op(_CALL_LIST_APPEND, (callable, self, arg -- none, c, s)) { + (void)(arg); + c = callable; + s = self; + none = sym_new_const(ctx, Py_None); + } + + op(_GUARD_CALLABLE_BUILTIN_CLASS, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyType_Type)) { + PyTypeObject *tp = (PyTypeObject *)callable_o; + if (tp->tp_vectorcall != NULL) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyType_Type); } } + op(_GUARD_CALLABLE_BUILTIN_O, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyCFunction_Type) && + (sym_is_not_null(self_or_null) || sym_is_null(self_or_null))) { + int total_args = oparg; + if (sym_is_not_null(self_or_null)) { + total_args++; + } + if (total_args == 1 && PyCFunction_GET_FLAGS(callable_o) == METH_O) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyCFunction_Type); + } + } + + op(_GUARD_CALLABLE_BUILTIN_FAST, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyCFunction_Type)) { + if (PyCFunction_GET_FLAGS(callable_o) == METH_FASTCALL) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyCFunction_Type); + } + } + + op(_GUARD_CALLABLE_BUILTIN_FAST_WITH_KEYWORDS, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyCFunction_Type)) { + if (PyCFunction_GET_FLAGS(callable_o) == (METH_FASTCALL | METH_KEYWORDS)) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyCFunction_Type); + } + } + + op(_CALL_BUILTIN_FAST_WITH_KEYWORDS, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + callable = sym_new_not_null(ctx); + } + + op(_CALL_BUILTIN_O, (callable, self_or_null, args[oparg] -- res, c, s)) { + res = sym_new_not_null(ctx); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + } + else if (sym_is_null(self_or_null)) { + s = args[0]; + } + else { + s = sym_new_unknown(ctx); + } + } + + op(_CALL_BUILTIN_FAST, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + callable = sym_new_not_null(ctx); + } + + op(_CALL_BUILTIN_CLASS, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + callable = sym_new_not_null(ctx); + } + + op(_GUARD_CALLABLE_METHOD_DESCRIPTOR_O, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyMethodDescr_Type) && + (sym_is_not_null(self_or_null) || sym_is_null(self_or_null))) { + int total_args = oparg; + if (sym_is_not_null(self_or_null)) { + total_args++; + } + PyTypeObject *self_type = NULL; + if (sym_is_not_null(self_or_null)) { + self_type = sym_get_type(self_or_null); + } + else { + self_type = sym_get_type(args[0]); + } + PyTypeObject *d_type = ((PyMethodDescrObject *)callable_o)->d_common.d_type; + if (total_args == 2 && + ((PyMethodDescrObject *)callable_o)->d_method->ml_flags == METH_O && + self_type == d_type) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyMethodDescr_Type); + } + } + + op(_GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyMethodDescr_Type) && + (sym_is_not_null(self_or_null) || sym_is_null(self_or_null))) { + int total_args = oparg; + if (sym_is_not_null(self_or_null)) { + total_args++; + } + PyTypeObject *self_type = NULL; + if (sym_is_not_null(self_or_null)) { + self_type = sym_get_type(self_or_null); + } + else { + self_type = sym_get_type(args[0]); + } + PyTypeObject *d_type = ((PyMethodDescrObject *)callable_o)->d_common.d_type; + if (total_args != 0 && + ((PyMethodDescrObject *)callable_o)->d_method->ml_flags == (METH_FASTCALL|METH_KEYWORDS) && + self_type == d_type) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyMethodDescr_Type); + } + } + + op(_GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyMethodDescr_Type) && + (sym_is_not_null(self_or_null) || sym_is_null(self_or_null))) { + int total_args = oparg; + if (sym_is_not_null(self_or_null)) { + total_args++; + } + PyTypeObject *self_type = NULL; + if (sym_is_not_null(self_or_null)) { + self_type = sym_get_type(self_or_null); + } + else { + self_type = sym_get_type(args[0]); + } + PyTypeObject *d_type = ((PyMethodDescrObject *)callable_o)->d_common.d_type; + if (total_args == 1 && + ((PyMethodDescrObject *)callable_o)->d_method->ml_flags == METH_NOARGS && + self_type == d_type) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyMethodDescr_Type); + } + } + + op(_CHECK_RECURSION_LIMIT, ( -- )) { + if (ctx->frame->is_c_recursion_checked) { + ADD_OP(_NOP, 0, 0); + } + ctx->frame->is_c_recursion_checked = true; + } + + op(_CALL_METHOD_DESCRIPTOR_NOARGS, (callable, self_or_null, args[oparg] -- res, c, s)) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && Py_IS_TYPE(callable_o, &PyMethodDescr_Type) + && sym_is_not_null(self_or_null)) { + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + PyCFunction cfunc = method->d_method->ml_meth; + ADD_OP(_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE, oparg + 1, (uintptr_t)cfunc); + } + res = sym_new_not_null(ctx); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + } + else if (sym_is_null(self_or_null)) { + s = args[0]; + } + else { + s = sym_new_unknown(ctx); + } + } + + op(_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && Py_IS_TYPE(callable_o, &PyMethodDescr_Type) + && sym_is_not_null(self_or_null)) { + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + PyCFunction cfunc = method->d_method->ml_meth; + ADD_OP(_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE, oparg, (uintptr_t)cfunc); + } + callable = sym_new_not_null(ctx); + } + + op(_CALL_METHOD_DESCRIPTOR_FAST, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && Py_IS_TYPE(callable_o, &PyMethodDescr_Type) + && sym_is_not_null(self_or_null)) { + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + PyCFunction cfunc = method->d_method->ml_meth; + ADD_OP(_CALL_METHOD_DESCRIPTOR_FAST_INLINE, oparg, (uintptr_t)cfunc); + } + callable = sym_new_not_null(ctx); + } + + op(_GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyMethodDescr_Type) && + (sym_is_not_null(self_or_null) || sym_is_null(self_or_null))) { + int total_args = oparg; + if (sym_is_not_null(self_or_null)) { + total_args++; + } + PyTypeObject *self_type = NULL; + if (sym_is_not_null(self_or_null)) { + self_type = sym_get_type(self_or_null); + } + else { + self_type = sym_get_type(args[0]); + } + PyTypeObject *d_type = ((PyMethodDescrObject *)callable_o)->d_common.d_type; + if (total_args != 0 && + ((PyMethodDescrObject *)callable_o)->d_method->ml_flags == METH_FASTCALL && + self_type == d_type) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyMethodDescr_Type); + } + } + + op(_CALL_METHOD_DESCRIPTOR_O, (callable, self_or_null, args[oparg] -- res, c, s, a)) { + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && Py_IS_TYPE(callable_o, &PyMethodDescr_Type) + && sym_is_not_null(self_or_null)) { + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + PyCFunction cfunc = method->d_method->ml_meth; + ADD_OP(_CALL_METHOD_DESCRIPTOR_O_INLINE, oparg + 1, (uintptr_t)cfunc); + } + res = sym_new_not_null(ctx); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + a = args[1]; + } + else { + s = sym_new_unknown(ctx); + a = sym_new_unknown(ctx); + } + } + + op(_CALL_INTRINSIC_1, (value -- res, v)) { + res = sym_new_not_null(ctx); + v = value; + } + + op(_CALL_INTRINSIC_2, (value2_st, value1_st -- res, vs1, vs2)) { + res = sym_new_not_null(ctx); + vs1 = value1_st; + vs2 = value2_st; + } + op(_GUARD_IS_TRUE_POP, (flag -- )) { + sym_apply_predicate_narrowing(ctx, flag, true); + if (sym_is_const(ctx, flag)) { PyObject *value = sym_get_const(ctx, flag); assert(value != NULL); - eliminate_pop_guard(this_instr, value != Py_True); + eliminate_pop_guard(this_instr, ctx, value != Py_True); + } + else { + int bit = get_test_bit_for_bools(); + if (bit) { + REPLACE_OP(this_instr, + test_bit_set_in_true(bit) ? + _GUARD_BIT_IS_SET_POP : + _GUARD_BIT_IS_UNSET_POP, bit, 0); + } } sym_set_const(flag, Py_True); } op(_GUARD_IS_FALSE_POP, (flag -- )) { + sym_apply_predicate_narrowing(ctx, flag, false); + if (sym_is_const(ctx, flag)) { PyObject *value = sym_get_const(ctx, flag); assert(value != NULL); - eliminate_pop_guard(this_instr, value != Py_False); + eliminate_pop_guard(this_instr, ctx, value != Py_False); + } + else { + int bit = get_test_bit_for_bools(); + if (bit) { + REPLACE_OP(this_instr, + test_bit_set_in_true(bit) ? + _GUARD_BIT_IS_UNSET_POP : + _GUARD_BIT_IS_SET_POP, bit, 0); + } } sym_set_const(flag, Py_False); } @@ -988,11 +1999,11 @@ dummy_func(void) { if (sym_is_const(ctx, val)) { PyObject *value = sym_get_const(ctx, val); assert(value != NULL); - eliminate_pop_guard(this_instr, !Py_IsNone(value)); + eliminate_pop_guard(this_instr, ctx, !Py_IsNone(value)); } else if (sym_has_type(val)) { assert(!sym_matches_type(val, &_PyNone_Type)); - eliminate_pop_guard(this_instr, true); + eliminate_pop_guard(this_instr, ctx, true); } sym_set_const(val, Py_None); } @@ -1001,11 +2012,11 @@ dummy_func(void) { if (sym_is_const(ctx, val)) { PyObject *value = sym_get_const(ctx, val); assert(value != NULL); - eliminate_pop_guard(this_instr, Py_IsNone(value)); + eliminate_pop_guard(this_instr, ctx, Py_IsNone(value)); } else if (sym_has_type(val)) { assert(!sym_matches_type(val, &_PyNone_Type)); - eliminate_pop_guard(this_instr, false); + eliminate_pop_guard(this_instr, ctx, false); } } @@ -1013,7 +2024,7 @@ dummy_func(void) { /* Setting the eval frame function invalidates * all executors, so no need to check dynamically */ if (_PyInterpreterState_GET()->eval_frame == NULL) { - REPLACE_OP(this_instr, _NOP, 0 ,0); + ADD_OP(_NOP, 0 ,0); } } @@ -1023,8 +2034,36 @@ dummy_func(void) { } op(_LOAD_SPECIAL, (method_and_self[2] -- method_and_self[2])) { - method_and_self[0] = sym_new_not_null(ctx); - method_and_self[1] = sym_new_unknown(ctx); + bool optimized = false; + PyTypeObject *type = sym_get_probable_type(method_and_self[1]); + if (type != NULL) { + PyObject *name = _Py_SpecialMethods[oparg].name; + PyObject *descr = _PyType_Lookup(type, name); + if (descr != NULL && (Py_TYPE(descr)->tp_flags & Py_TPFLAGS_METHOD_DESCRIPTOR)) { + /* LOAD_SPECIAL expands to _RECORD_TOS_TYPE + _INSERT_NULL + + * _LOAD_SPECIAL. Insert _GUARD_TYPE_VERSION before the + * already-emitted _INSERT_NULL so deopt sees the original + * stack shape.*/ + _PyUOpInstruction *insert_null = uop_buffer_last(&ctx->out_buffer); + assert(insert_null->opcode == _INSERT_NULL); + assert(insert_null->target == this_instr->target); + REPLACE_OP(insert_null, _GUARD_TYPE_VERSION, 0, type->tp_version_tag); + ADD_OP(_INSERT_NULL, 0, 0); + + bool immortal = _Py_IsImmortal(descr) || (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE); + ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE, + 0, (uintptr_t)descr); + ADD_OP(_SWAP, 3, 0); + optimize_pop_top(ctx, this_instr, method_and_self[0]); + watch_type(type, dependencies); + method_and_self[0] = sym_new_const(ctx, descr); + optimized = true; + } + } + if (!optimized) { + method_and_self[0] = sym_new_not_null(ctx); + method_and_self[1] = sym_new_unknown(ctx); + } } op(_JUMP_TO_TOP, (--)) { @@ -1036,13 +2075,20 @@ dummy_func(void) { ctx->done = true; } - op(_REPLACE_WITH_TRUE, (value -- res)) { - REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)Py_True); + op(_DEOPT, (--)) { + ctx->done = true; + } + + op(_REPLACE_WITH_TRUE, (value -- res, v)) { + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)Py_True); + ADD_OP(_SWAP, 2, 0); res = sym_new_const(ctx, Py_True); + v = value; } op(_BUILD_TUPLE, (values[oparg] -- tup)) { tup = sym_new_tuple(ctx, oparg, values); + tup = PyJitRef_MakeUnique(tup); } op(_BUILD_LIST, (values[oparg] -- list)) { @@ -1065,149 +2111,320 @@ dummy_func(void) { set = sym_new_type(ctx, &PySet_Type); } + op(_SET_UPDATE, (set, unused[oparg-1], iterable -- set, unused[oparg-1], i)) { + (void)set; + i = iterable; + } + + op(_LIST_EXTEND, (list_st, unused[oparg-1], iterable_st -- list_st, unused[oparg-1], i)) { + (void)list_st; + i = iterable_st; + } + + op(_DICT_MERGE, (callable, unused, unused, dict, unused[oparg - 1], update -- callable, unused, unused, dict, unused[oparg - 1], u)) { + (void)callable; + (void)dict; + u = update; + } + op(_UNPACK_SEQUENCE_TWO_TUPLE, (seq -- val1, val0)) { + if (PyJitRef_IsUnique(seq) && sym_tuple_length(seq) == 2) { + ADD_OP(_UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE, oparg, 0); + } val0 = sym_tuple_getitem(ctx, seq, 0); val1 = sym_tuple_getitem(ctx, seq, 1); } op(_UNPACK_SEQUENCE_TUPLE, (seq -- values[oparg])) { + if (PyJitRef_IsUnique(seq) && sym_tuple_length(seq) == 3) { + ADD_OP(_UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE, oparg, 0); + } + else if (PyJitRef_IsUnique(seq) && sym_tuple_length(seq) == oparg) { + ADD_OP(_UNPACK_SEQUENCE_UNIQUE_TUPLE, oparg, 0); + } for (int i = 0; i < oparg; i++) { values[i] = sym_tuple_getitem(ctx, seq, oparg - i - 1); } } - op(_CALL_TUPLE_1, (callable, null, arg -- res)) { + op(_CALL_TUPLE_1, (callable, null, arg -- res, a)) { if (sym_matches_type(arg, &PyTuple_Type)) { // e.g. tuple((1, 2)) or tuple(foo) where foo is known to be a tuple - res = arg; + // Note: we must strip the reference information because it goes + // through tuple() which strips the reference information from it. + res = PyJitRef_StripReferenceInfo(arg); } else { res = sym_new_type(ctx, &PyTuple_Type); } + a = arg; } op(_GUARD_TOS_LIST, (tos -- tos)) { if (sym_matches_type(tos, &PyList_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PyList_Type); } - sym_set_type(tos, &PyList_Type); } op(_GUARD_NOS_LIST, (nos, unused -- nos, unused)) { if (sym_matches_type(nos, &PyList_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(nos, &PyList_Type); } - sym_set_type(nos, &PyList_Type); } op(_GUARD_TOS_TUPLE, (tos -- tos)) { if (sym_matches_type(tos, &PyTuple_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PyTuple_Type); } - sym_set_type(tos, &PyTuple_Type); } op(_GUARD_NOS_TUPLE, (nos, unused -- nos, unused)) { if (sym_matches_type(nos, &PyTuple_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(nos, &PyTuple_Type); + } + } + + op(_GUARD_NOS_DICT_SUBSCRIPT, (nos, unused -- nos, unused)) { + PyTypeObject *tp = sym_get_type(nos); + bool definite = true; + if (!tp) { + tp = sym_get_probable_type(nos); + definite = false; + } + if (tp && tp->tp_as_mapping && + tp->tp_as_mapping->mp_subscript == _PyDict_Subscript) { + if (definite) { + ADD_OP(_NOP, 0, 0); + } + else { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(nos, tp); + } + PyType_Watch(TYPE_WATCHER_ID, (PyObject *)tp); + _Py_BloomFilter_Add(dependencies, tp); + } + } + + op(_GUARD_NOS_DICT_STORE_SUBSCRIPT, (unused, nos, unused -- unused, nos, unused)) { + PyTypeObject *tp = sym_get_type(nos); + bool definite = true; + if (!tp) { + tp = sym_get_probable_type(nos); + definite = false; + } + if (tp && tp->tp_as_mapping && + tp->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript) { + if (definite) { + ADD_OP(_NOP, 0, 0); + } + else { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(nos, tp); + } + PyType_Watch(TYPE_WATCHER_ID, (PyObject *)tp); + _Py_BloomFilter_Add(dependencies, tp); + } + } + + op(_GUARD_TOS_ANY_DICT, (tos -- tos)) { + PyTypeObject *tp = sym_get_type(tos); + if (tp == &PyDict_Type || tp == &PyFrozenDict_Type) { + ADD_OP(_NOP, 0, 0); + } + else { + // Narrowing the guard based on the probable type. + tp = sym_get_probable_type(tos); + if (tp == &PyDict_Type) { + ADD_OP(_GUARD_TOS_DICT, 0, 0); + } + else if (tp == &PyFrozenDict_Type) { + ADD_OP(_GUARD_TOS_FROZENDICT, 0, 0); + } } - sym_set_type(nos, &PyTuple_Type); } op(_GUARD_TOS_DICT, (tos -- tos)) { if (sym_matches_type(tos, &PyDict_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PyDict_Type); } - sym_set_type(tos, &PyDict_Type); } - op(_GUARD_NOS_DICT, (nos, unused -- nos, unused)) { - if (sym_matches_type(nos, &PyDict_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + op(_GUARD_TOS_FROZENDICT, (tos -- tos)) { + if (sym_matches_type(tos, &PyFrozenDict_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PyFrozenDict_Type); } - sym_set_type(nos, &PyDict_Type); } op(_GUARD_TOS_ANY_SET, (tos -- tos)) { - if (sym_matches_type(tos, &PySet_Type) || - sym_matches_type(tos, &PyFrozenSet_Type)) - { - REPLACE_OP(this_instr, _NOP, 0, 0); + PyTypeObject *tp = sym_get_type(tos); + if (tp == &PySet_Type || tp == &PyFrozenSet_Type) { + ADD_OP(_NOP, 0, 0); + } + else { + // Narrowing the guard based on the probable type. + tp = sym_get_probable_type(tos); + if (tp == &PySet_Type) { + ADD_OP(_GUARD_TOS_SET, 0, 0); + } + else if (tp == &PyFrozenSet_Type) { + ADD_OP(_GUARD_TOS_FROZENSET, 0, 0); + } + } + } + + op(_GUARD_TOS_SET, (tos -- tos)) { + if (sym_matches_type(tos, &PySet_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PySet_Type); + } + } + + op(_GUARD_TOS_FROZENSET, (tos -- tos)) { + if (sym_matches_type(tos, &PyFrozenSet_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PyFrozenSet_Type); + } + } + + op(_GUARD_TOS_SLICE, (tos -- tos)) { + if (sym_matches_type(tos, &PySlice_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PySlice_Type); } } op(_GUARD_NOS_NULL, (null, unused -- null, unused)) { if (sym_is_null(null)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_null(null); } - sym_set_null(null); } op(_GUARD_NOS_NOT_NULL, (nos, unused -- nos, unused)) { if (sym_is_not_null(nos)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_non_null(nos); } - sym_set_non_null(nos); } op(_GUARD_THIRD_NULL, (null, unused, unused -- null, unused, unused)) { if (sym_is_null(null)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_null(null); } - sym_set_null(null); } op(_GUARD_CALLABLE_TYPE_1, (callable, unused, unused -- callable, unused, unused)) { if (sym_get_const(ctx, callable) == (PyObject *)&PyType_Type) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, (PyObject *)&PyType_Type); } - sym_set_const(callable, (PyObject *)&PyType_Type); } op(_GUARD_CALLABLE_TUPLE_1, (callable, unused, unused -- callable, unused, unused)) { if (sym_get_const(ctx, callable) == (PyObject *)&PyTuple_Type) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, (PyObject *)&PyTuple_Type); } - sym_set_const(callable, (PyObject *)&PyTuple_Type); } op(_GUARD_CALLABLE_STR_1, (callable, unused, unused -- callable, unused, unused)) { if (sym_get_const(ctx, callable) == (PyObject *)&PyUnicode_Type) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, (PyObject *)&PyUnicode_Type); } - sym_set_const(callable, (PyObject *)&PyUnicode_Type); } - op(_CALL_LEN, (callable, null, arg -- res)) { + op(_CALL_LEN, (callable, null, arg -- res, a, c)) { res = sym_new_type(ctx, &PyLong_Type); - int tuple_length = sym_tuple_length(arg); - if (tuple_length >= 0) { - PyObject *temp = PyLong_FromLong(tuple_length); + Py_ssize_t length = sym_tuple_length(arg); + + // Not a tuple, check if it's another immutable const with known length + if (length < 0 && sym_is_const(ctx, arg)) { + PyObject *const_val = sym_get_const(ctx, arg); + if (const_val != NULL) { + if (PyUnicode_CheckExact(const_val)) { + length = PyUnicode_GET_LENGTH(const_val); + } + else if (PyBytes_CheckExact(const_val)) { + length = PyBytes_GET_SIZE(const_val); + } + else if (PyFrozenDict_CheckExact(const_val)) { + length = PyDict_GET_SIZE(const_val); + } + else if (PyFrozenSet_CheckExact(const_val)) { + length = PySet_GET_SIZE(const_val); + } + } + } + + if (length >= 0) { + PyObject *temp = PyLong_FromSsize_t(length); if (temp == NULL) { goto error; } if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, - 0, (uintptr_t)temp); + ADD_OP(_SWAP, 2, 0); + optimize_pop_top(ctx, this_instr, null); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + ADD_OP(_SWAP, 3, 0); } res = sym_new_const(ctx, temp); Py_DECREF(temp); } + a = arg; + c = callable; } op(_GET_LEN, (obj -- obj, len)) { - int tuple_length = sym_tuple_length(obj); + Py_ssize_t tuple_length = sym_tuple_length(obj); if (tuple_length == -1) { len = sym_new_type(ctx, &PyLong_Type); } else { assert(tuple_length >= 0); - PyObject *temp = PyLong_FromLong(tuple_length); + PyObject *temp = PyLong_FromSsize_t(tuple_length); if (temp == NULL) { goto error; } if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); } len = sym_new_const(ctx, temp); Py_DECREF(temp); @@ -1217,25 +2434,31 @@ dummy_func(void) { op(_GUARD_CALLABLE_LEN, (callable, unused, unused -- callable, unused, unused)) { PyObject *len = _PyInterpreterState_GET()->callable_cache.len; if (sym_get_const(ctx, callable) == len) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, len); } - sym_set_const(callable, len); } op(_GUARD_CALLABLE_ISINSTANCE, (callable, unused, unused, unused -- callable, unused, unused, unused)) { PyObject *isinstance = _PyInterpreterState_GET()->callable_cache.isinstance; if (sym_get_const(ctx, callable) == isinstance) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, isinstance); } - sym_set_const(callable, isinstance); } op(_GUARD_CALLABLE_LIST_APPEND, (callable, unused, unused -- callable, unused, unused)) { PyObject *list_append = _PyInterpreterState_GET()->callable_cache.list_append; if (sym_get_const(ctx, callable) == list_append) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, list_append); } - sym_set_const(callable, list_append); } op(_BINARY_SLICE, (container, start, stop -- res)) { @@ -1252,6 +2475,273 @@ dummy_func(void) { } } + op(_GUARD_GLOBALS_VERSION, (version/1 --)) { + if (ctx->frame->func != NULL) { + PyObject *globals = ctx->frame->func->func_globals; + if (incorrect_keys(globals, version)) { + OPT_STAT_INC(remove_globals_incorrect_keys); + ctx->done = true; + } + else if (get_mutations(globals) >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { + /* Do nothing */ + } + else { + if (!ctx->frame->globals_watched) { + PyDict_Watch(GLOBALS_WATCHER_ID, globals); + _Py_BloomFilter_Add(dependencies, globals); + ctx->frame->globals_watched = true; + } + if (ctx->frame->globals_checked_version == version) { + ADD_OP(_NOP, 0, 0); + } + } + } + ctx->frame->globals_checked_version = version; + } + + op(_LOAD_GLOBAL_BUILTINS, (version/1, index/1 -- res)) { + (void)version; + (void)index; + PyObject *cnst = NULL; + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyObject *builtins = interp->builtins; + if (incorrect_keys(builtins, version)) { + OPT_STAT_INC(remove_globals_incorrect_keys); + ctx->done = true; + } + else if (interp->rare_events.builtin_dict >= _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) { + /* Do nothing */ + } + else { + if (!ctx->builtins_watched) { + PyDict_Watch(BUILTINS_WATCHER_ID, builtins); + ctx->builtins_watched = true; + } + if (ctx->frame->globals_checked_version != 0 && ctx->frame->globals_watched) { + cnst = convert_global_to_const(this_instr, builtins); + } + } + if (cnst == NULL) { + res = sym_new_not_null(ctx); + } + else { + if (_Py_IsImmortal(cnst)) { + res = PyJitRef_Borrow(sym_new_const(ctx, cnst)); + } + else { + res = sym_new_const(ctx, cnst); + } + } + } + + op(_LOAD_GLOBAL_MODULE, (version/1, unused/1, index/1 -- res)) { + (void)index; + PyObject *cnst = NULL; + if (ctx->frame->func != NULL) { + PyObject *globals = ctx->frame->func->func_globals; + if (incorrect_keys(globals, version)) { + OPT_STAT_INC(remove_globals_incorrect_keys); + ctx->done = true; + } + else if (get_mutations(globals) >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { + /* Do nothing */ + } + else { + if (!ctx->frame->globals_watched) { + PyDict_Watch(GLOBALS_WATCHER_ID, globals); + _Py_BloomFilter_Add(dependencies, globals); + ctx->frame->globals_watched = true; + } + if (ctx->frame->globals_checked_version != version && this_instr[-1].opcode == _NOP) { + REPLACE_OP(uop_buffer_last(&ctx->out_buffer), _GUARD_GLOBALS_VERSION, 0, version); + ctx->frame->globals_checked_version = version; + } + if (ctx->frame->globals_checked_version == version) { + cnst = convert_global_to_const(this_instr, globals); + } + } + } + if (cnst == NULL) { + res = sym_new_not_null(ctx); + } + else { + if (_Py_IsImmortal(cnst)) { + res = PyJitRef_Borrow(sym_new_const(ctx, cnst)); + } + else { + res = sym_new_const(ctx, cnst); + } + } + } + + op(_BINARY_OP_SUBSCR_LIST_INT, (list_st, sub_st -- res, ls, ss)) { + res = sym_new_unknown(ctx); + ls = list_st; + ss = sub_st; + } + + op(_MAKE_FUNCTION, (codeobj_st -- func, co)) { + func = sym_new_type(ctx, &PyFunction_Type); + co = codeobj_st; + } + + op(_MATCH_CLASS, (subject, type, names -- attrs, s, tp, n)) { + attrs = sym_new_not_null(ctx); + s = subject; + tp = type; + n = names; + } + + op(_DICT_UPDATE, (dict, unused[oparg - 1], update -- dict, unused[oparg - 1], upd)) { + (void)dict; + upd = update; + } + + op(_RECORD_TOS, (tos -- tos)) { + sym_set_recorded_value(tos, (PyObject *)this_instr->operand0); + } + + op(_RECORD_TOS_TYPE, (tos -- tos)) { + PyTypeObject *tp = (PyTypeObject *)this_instr->operand0; + sym_set_recorded_type(tos, tp); + } + + op(_RECORD_NOS, (nos, tos -- nos, tos)) { + sym_set_recorded_value(nos, (PyObject *)this_instr->operand0); + } + + op(_RECORD_NOS_TYPE, (nos, tos -- nos, tos)) { + PyTypeObject *tp = (PyTypeObject *)this_instr->operand0; + sym_set_recorded_type(nos, tp); + } + + op(_RECORD_4OS, (value, _3os, nos, tos -- value, _3os, nos, tos)) { + sym_set_recorded_value(value, (PyObject *)this_instr->operand0); + } + + op(_RECORD_CALLABLE, (func, self, args[oparg] -- func, self, args[oparg])) { + sym_set_recorded_value(func, (PyObject *)this_instr->operand0); + } + + op(_RECORD_CALLABLE_KW, (func, self, args[oparg], kwnames -- func, self, args[oparg], kwnames)) { + sym_set_recorded_value(func, (PyObject *)this_instr->operand0); + } + + op(_RECORD_BOUND_METHOD, (callable, self, args[oparg] -- callable, self, args[oparg])) { + sym_set_recorded_value(callable, (PyObject *)this_instr->operand0); + } + + op(_RECORD_NOS_GEN_FUNC, (nos, tos -- nos, tos)) { + PyFunctionObject *func = (PyFunctionObject *)this_instr->operand0; + assert(func == NULL || PyFunction_Check(func)); + sym_set_recorded_gen_func(nos, func); + } + + op(_RECORD_3OS_GEN_FUNC, (gen, nos, tos -- gen, nos, tos)) { + PyFunctionObject *func = (PyFunctionObject *)this_instr->operand0; + assert(func == NULL || PyFunction_Check(func)); + sym_set_recorded_gen_func(gen, func); + } + + op(_GUARD_CODE_VERSION__PUSH_FRAME, (version/2 -- )) { + PyCodeObject *co = get_current_code_object(ctx); + if (co->co_version == version) { + _Py_BloomFilter_Add(dependencies, co); + // Functions derive their version from code objects. + PyFunctionObject *func = (PyFunctionObject *)sym_get_const(ctx, ctx->frame->callable); + if (func != NULL && func->func_version == version) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + } + else { + ctx->done = true; + } + } + + op(_GUARD_CODE_VERSION_RETURN_VALUE, (version/2 -- )) { + (void)version; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + } + + op(_GUARD_CODE_VERSION_YIELD_VALUE, (version/2 -- )) { + (void)version; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + } + + op(_GUARD_CODE_VERSION_RETURN_GENERATOR, (version/2 -- )) { + (void)version; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + } + + op(_GUARD_IP__PUSH_FRAME, (ip/4 --)) { + (void)ip; + stack_pointer = sym_set_stack_depth((int)this_instr->operand1, stack_pointer); + PyFunctionObject *func = (PyFunctionObject *)sym_get_const(ctx, ctx->frame->callable); + if (func != NULL && func->func_version != 0 && + // We can remove this guard for simple function call targets. + (((PyCodeObject *)ctx->frame->func->func_code)->co_flags & + (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) == 0) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + } + + op(_GUARD_IP_YIELD_VALUE, (ip/4 --)) { + (void)ip; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + stack_pointer = sym_set_stack_depth((int)this_instr->operand1, stack_pointer); + } + + op(_GUARD_IP_RETURN_VALUE, (ip/4 --)) { + (void)ip; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + stack_pointer = sym_set_stack_depth((int)this_instr->operand1, stack_pointer); + } + + op(_GUARD_IP_RETURN_GENERATOR, (ip/4 --)) { + (void)ip; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + stack_pointer = sym_set_stack_depth((int)this_instr->operand1, stack_pointer); + } + + op(_GUARD_TOS_NOT_NULL, (null_or_index -- null_or_index)) { + if (sym_is_not_null(null_or_index)) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + else { + sym_set_non_null(null_or_index); + } + } + + op(_GUARD_3OS_ASYNC_GEN_ASEND, (iter, null_or_index, value -- iter, null_or_index, value)) { + if (sym_matches_type(iter, &_PyAsyncGenASend_Type)) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + else { + sym_set_type(iter, &_PyAsyncGenASend_Type); + } + } + + op(_GET_ANEXT, (aiter -- aiter, awaitable)) { + if (sym_matches_type(aiter, &PyAsyncGen_Type)) { + awaitable = sym_new_type(ctx, &_PyAsyncGenASend_Type); + } + else { + awaitable = sym_new_not_null(ctx); + } + } + // END BYTECODES // } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 41402200c1683e..102b9527951414 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -11,12 +11,12 @@ break; } + /* _CHECK_PERIODIC_AT_END is not a viable micro-op for tier 2 */ + case _CHECK_PERIODIC_IF_NOT_YIELD_FROM: { break; } - /* _QUICKEN_RESUME is not a viable micro-op for tier 2 */ - /* _LOAD_BYTECODE is not a viable micro-op for tier 2 */ case _RESUME_CHECK: { @@ -31,27 +31,33 @@ if (sym_is_null(value)) { ctx->done = true; } + assert(!PyJitRef_IsUnique(value)); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_FAST: { JitOptRef value; value = GETLOCAL(oparg); + assert(!PyJitRef_IsUnique(value)); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_FAST_BORROW: { JitOptRef value; value = PyJitRef_Borrow(GETLOCAL(oparg)); + assert(!PyJitRef_IsUnique(value)); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -60,20 +66,24 @@ value = GETLOCAL(oparg); JitOptRef temp = sym_new_null(ctx); GETLOCAL(oparg) = temp; + assert(!PyJitRef_IsUnique(value)); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_CONST: { JitOptRef value; + PyCodeObject *co = get_current_code_object(ctx); PyObject *val = PyTuple_GET_ITEM(co->co_consts, oparg); - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); value = PyJitRef_Borrow(sym_new_const(ctx, val)); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -82,111 +92,169 @@ PyObject *val = PyLong_FromLong(oparg); assert(val); assert(_Py_IsImmortal(val)); - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); value = PyJitRef_Borrow(sym_new_const(ctx, val)); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _STORE_FAST: { + case _SWAP_FAST: { JitOptRef value; + JitOptRef trash; value = stack_pointer[-1]; - GETLOCAL(oparg) = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef tmp = GETLOCAL(oparg); + GETLOCAL(oparg) = PyJitRef_RemoveUnique(value); + trash = tmp; + stack_pointer[-1] = trash; break; } case _POP_TOP: { JitOptRef value; value = stack_pointer[-1]; - PyTypeObject *typ = sym_get_type(value); - if (PyJitRef_IsBorrowed(value) || - sym_is_immortal(PyJitRef_Unwrap(value)) || - sym_is_null(value)) { - REPLACE_OP(this_instr, _POP_TOP_NOP, 0, 0); - } - else if (typ == &PyLong_Type) { - REPLACE_OP(this_instr, _POP_TOP_INT, 0, 0); - } - else if (typ == &PyFloat_Type) { - REPLACE_OP(this_instr, _POP_TOP_FLOAT, 0, 0); - } - else if (typ == &PyUnicode_Type) { - REPLACE_OP(this_instr, _POP_TOP_UNICODE, 0, 0); - } + optimize_pop_top(ctx, this_instr, value); + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _POP_TOP_NOP: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _POP_TOP_INT: { + JitOptRef value; + value = stack_pointer[-1]; + if (PyJitRef_IsBorrowed(value)) { + ADD_OP(_POP_TOP_NOP, 0, 0); + } + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _POP_TOP_FLOAT: { + JitOptRef value; + value = stack_pointer[-1]; + if (PyJitRef_IsBorrowed(value)) { + ADD_OP(_POP_TOP_NOP, 0, 0); + } + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _POP_TOP_UNICODE: { + JitOptRef value; + value = stack_pointer[-1]; + if (PyJitRef_IsBorrowed(value)) { + ADD_OP(_POP_TOP_NOP, 0, 0); + } + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _POP_TWO: { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + case _POP_TOP_OPARG: { + JitOptRef *args; + args = &stack_pointer[-oparg]; + for (int i = oparg-1; i >= 0; i--) { + optimize_pop_top(ctx, this_instr, args[i]); + } + CHECK_STACK_BOUNDS(-oparg); + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _PUSH_NULL: { JitOptRef res; - res = sym_new_null(ctx); + res = PyJitRef_Borrow(sym_new_null(ctx)); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _END_FOR: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _POP_ITER: { + CHECK_STACK_BOUNDS(-2); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _END_SEND: { JitOptRef val; val = sym_new_not_null(ctx); - stack_pointer[-2] = val; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + CHECK_STACK_BOUNDS(-2); + stack_pointer[-3] = val; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _UNARY_NEGATIVE: { JitOptRef value; JitOptRef res; + JitOptRef v; value = stack_pointer[-1]; - if (sym_is_compact_int(value)) { + v = value; + if ( + sym_is_safe_const(ctx, value) + ) { + JitOptRef value_sym = value; + _PyStackRef value = sym_get_const_as_stackref(ctx, value_sym); + _PyStackRef res_stackref; + _PyStackRef v_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + PyObject *res_o = PyNumber_Negative(PyStackRef_AsPyObjectBorrow(value)); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res_stackref = PyStackRef_FromPyObjectSteal(res_o); + v_stackref = value; + /* End of uop copied from bytecodes for constant evaluation */ + (void)v_stackref; + res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _SWAP since we have one input and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_SWAP, 2, 0); + } + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + if (sym_matches_type(value, &PyFloat_Type) && PyJitRef_IsUnique(value)) { + ADD_OP(_UNARY_NEGATIVE_FLOAT_INPLACE, 0, 0); + v = PyJitRef_Borrow(sym_new_null(ctx)); + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); + } + else if (sym_is_compact_int(value)) { res = sym_new_compact_int(ctx); } else { @@ -198,7 +266,24 @@ res = sym_new_not_null(ctx); } } + CHECK_STACK_BOUNDS(1); + stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _UNARY_NEGATIVE_FLOAT_INPLACE: { + JitOptRef res; + JitOptRef v; + res = sym_new_not_null(ctx); + v = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -218,6 +303,14 @@ ? PyStackRef_True : PyStackRef_False; /* End of uop copied from bytecodes for constant evaluation */ res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _POP_TOP + _LOAD_CONST_INLINE_BORROW since we have one input and an immortal result + ADD_OP(_POP_TOP, 0, 0); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + } + } stack_pointer[-1] = res; break; } @@ -231,7 +324,8 @@ JitOptRef value; JitOptRef res; value = stack_pointer[-1]; - int already_bool = optimize_to_bool(this_instr, ctx, value, &res); + int already_bool = optimize_to_bool(this_instr, ctx, value, &res, + _POP_TOP, _NOP); if (!already_bool) { res = sym_new_truthiness(ctx, value, true); } @@ -242,10 +336,10 @@ case _TO_BOOL_BOOL: { JitOptRef value; value = stack_pointer[-1]; - int already_bool = optimize_to_bool(this_instr, ctx, value, &value); + int already_bool = optimize_to_bool(this_instr, ctx, value, &value, + _POP_TOP, _NOP); if (!already_bool) { sym_set_type(value, &PyBool_Type); - value = sym_new_truthiness(ctx, value, true); } stack_pointer[-1] = value; break; @@ -254,13 +348,20 @@ case _TO_BOOL_INT: { JitOptRef value; JitOptRef res; + JitOptRef v; value = stack_pointer[-1]; - int already_bool = optimize_to_bool(this_instr, ctx, value, &res); + int already_bool = optimize_to_bool(this_instr, ctx, value, &res, + _NOP, _SWAP); if (!already_bool) { sym_set_type(value, &PyLong_Type); res = sym_new_truthiness(ctx, value, true); } + v = value; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -268,9 +369,11 @@ JitOptRef nos; nos = stack_pointer[-2]; if (sym_matches_type(nos, &PyList_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(nos, &PyList_Type); } - sym_set_type(nos, &PyList_Type); break; } @@ -278,25 +381,42 @@ JitOptRef tos; tos = stack_pointer[-1]; if (sym_matches_type(tos, &PyList_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PyList_Type); } - sym_set_type(tos, &PyList_Type); break; } case _GUARD_TOS_SLICE: { + JitOptRef tos; + tos = stack_pointer[-1]; + if (sym_matches_type(tos, &PySlice_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PySlice_Type); + } break; } case _TO_BOOL_LIST: { JitOptRef value; JitOptRef res; + JitOptRef v; value = stack_pointer[-1]; - int already_bool = optimize_to_bool(this_instr, ctx, value, &res); + int already_bool = optimize_to_bool(this_instr, ctx, value, &res, + _NOP, _SWAP); if (!already_bool) { res = sym_new_type(ctx, &PyBool_Type); } + v = value; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -304,7 +424,8 @@ JitOptRef value; JitOptRef res; value = stack_pointer[-1]; - int already_bool = optimize_to_bool(this_instr, ctx, value, &res); + int already_bool = optimize_to_bool(this_instr, ctx, value, &res, + _POP_TOP, _NOP); if (!already_bool) { sym_set_const(value, Py_None); res = sym_new_const(ctx, Py_False); @@ -313,11 +434,18 @@ break; } + case _GUARD_NOS_COMPACT_ASCII: { + JitOptRef nos; + nos = stack_pointer[-2]; + sym_set_type(nos, &PyUnicode_Type); + break; + } + case _GUARD_NOS_UNICODE: { JitOptRef nos; nos = stack_pointer[-2]; if (sym_matches_type(nos, &PyUnicode_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } sym_set_type(nos, &PyUnicode_Type); break; @@ -327,7 +455,7 @@ JitOptRef value; value = stack_pointer[-1]; if (sym_matches_type(value, &PyUnicode_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } sym_set_type(value, &PyUnicode_Type); break; @@ -336,34 +464,90 @@ case _TO_BOOL_STR: { JitOptRef value; JitOptRef res; + JitOptRef v; value = stack_pointer[-1]; - int already_bool = optimize_to_bool(this_instr, ctx, value, &res); + int already_bool = optimize_to_bool(this_instr, ctx, value, &res, + _NOP, _SWAP); + v = value; if (!already_bool) { res = sym_new_truthiness(ctx, value, true); } + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _REPLACE_WITH_TRUE: { + JitOptRef value; JitOptRef res; - REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)Py_True); + JitOptRef v; + value = stack_pointer[-1]; + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)Py_True); + ADD_OP(_SWAP, 2, 0); res = sym_new_const(ctx, Py_True); + v = value; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _UNARY_INVERT: { JitOptRef value; JitOptRef res; + JitOptRef v; value = stack_pointer[-1]; + v = value; + if (!sym_matches_type(value, &PyBool_Type)) { + if ( + sym_is_safe_const(ctx, value) + ) { + JitOptRef value_sym = value; + _PyStackRef value = sym_get_const_as_stackref(ctx, value_sym); + _PyStackRef res_stackref; + _PyStackRef v_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + PyObject *res_o = PyNumber_Invert(PyStackRef_AsPyObjectBorrow(value)); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res_stackref = PyStackRef_FromPyObjectSteal(res_o); + v_stackref = value; + /* End of uop copied from bytecodes for constant evaluation */ + (void)v_stackref; + res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _SWAP since we have one input and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_SWAP, 2, 0); + } + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + } if (sym_matches_type(value, &PyLong_Type)) { res = sym_new_type(ctx, &PyLong_Type); } else { res = sym_new_not_null(ctx); } + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -371,11 +555,11 @@ JitOptRef left; left = stack_pointer[-2]; if (sym_is_compact_int(left)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } else { if (sym_get_type(left) == &PyLong_Type) { - REPLACE_OP(this_instr, _GUARD_NOS_OVERFLOWED, 0, 0); + ADD_OP(_GUARD_NOS_OVERFLOWED, 0, 0); } sym_set_compact_int(left); } @@ -386,11 +570,11 @@ JitOptRef value; value = stack_pointer[-1]; if (sym_is_compact_int(value)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } else { if (sym_get_type(value) == &PyLong_Type) { - REPLACE_OP(this_instr, _GUARD_TOS_OVERFLOWED, 0, 0); + ADD_OP(_GUARD_TOS_OVERFLOWED, 0, 0); } sym_set_compact_int(value); } @@ -409,8 +593,19 @@ JitOptRef right; JitOptRef left; JitOptRef res; + JitOptRef l; + JitOptRef r; right = stack_pointer[-1]; left = stack_pointer[-2]; + if (PyJitRef_IsUnique(left)) { + REPLACE_OP(this_instr, _BINARY_OP_MULTIPLY_INT_INPLACE, 0, 0); + } + else if (PyJitRef_IsUnique(right)) { + REPLACE_OP(this_instr, _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT, 0, 0); + } + res = PyJitRef_MakeUnique(sym_new_compact_int(ctx)); + l = left; + r = right; if ( sym_is_safe_const(ctx, left) && sym_is_safe_const(ctx, right) @@ -420,6 +615,8 @@ _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); _PyStackRef res_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; /* Start of uop copied from bytecodes for constant evaluation */ PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); @@ -432,19 +629,34 @@ ctx->done = true; break; } - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + l_stackref = left; + r_stackref = right; /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } + } + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - res = sym_new_compact_int(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -452,8 +664,19 @@ JitOptRef right; JitOptRef left; JitOptRef res; + JitOptRef l; + JitOptRef r; right = stack_pointer[-1]; left = stack_pointer[-2]; + if (PyJitRef_IsUnique(left)) { + REPLACE_OP(this_instr, _BINARY_OP_ADD_INT_INPLACE, 0, 0); + } + else if (PyJitRef_IsUnique(right)) { + REPLACE_OP(this_instr, _BINARY_OP_ADD_INT_INPLACE_RIGHT, 0, 0); + } + res = PyJitRef_MakeUnique(sym_new_compact_int(ctx)); + l = left; + r = right; if ( sym_is_safe_const(ctx, left) && sym_is_safe_const(ctx, right) @@ -463,6 +686,8 @@ _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); _PyStackRef res_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; /* Start of uop copied from bytecodes for constant evaluation */ PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); @@ -475,19 +700,34 @@ ctx->done = true; break; } - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + l_stackref = left; + r_stackref = right; /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } + } + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - res = sym_new_compact_int(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -495,8 +735,19 @@ JitOptRef right; JitOptRef left; JitOptRef res; + JitOptRef l; + JitOptRef r; right = stack_pointer[-1]; left = stack_pointer[-2]; + if (PyJitRef_IsUnique(left)) { + REPLACE_OP(this_instr, _BINARY_OP_SUBTRACT_INT_INPLACE, 0, 0); + } + else if (PyJitRef_IsUnique(right)) { + REPLACE_OP(this_instr, _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT, 0, 0); + } + res = PyJitRef_MakeUnique(sym_new_compact_int(ctx)); + l = left; + r = right; if ( sym_is_safe_const(ctx, left) && sym_is_safe_const(ctx, right) @@ -506,6 +757,8 @@ _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); _PyStackRef res_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; /* Start of uop copied from bytecodes for constant evaluation */ PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); @@ -518,19 +771,130 @@ ctx->done = true; break; } - PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc); + l_stackref = left; + r_stackref = right; /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } + } + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - res = sym_new_compact_int(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_INPLACE: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_INPLACE: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_INPLACE: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_INT_INPLACE_RIGHT: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_INT_INPLACE_RIGHT: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_INT_INPLACE_RIGHT: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -538,7 +902,7 @@ JitOptRef left; left = stack_pointer[-2]; if (sym_matches_type(left, &PyFloat_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } sym_set_type(left, &PyFloat_Type); break; @@ -548,7 +912,7 @@ JitOptRef value; value = stack_pointer[-1]; if (sym_matches_type(value, &PyFloat_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } sym_set_type(value, &PyFloat_Type); break; @@ -558,44 +922,31 @@ JitOptRef right; JitOptRef left; JitOptRef res; + JitOptRef l; + JitOptRef r; right = stack_pointer[-1]; left = stack_pointer[-2]; - if ( - sym_is_safe_const(ctx, left) && - sym_is_safe_const(ctx, right) - ) { - JitOptRef left_sym = left; - JitOptRef right_sym = right; - _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); - _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); - _PyStackRef res_stackref; - /* Start of uop copied from bytecodes for constant evaluation */ - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval * - ((PyFloatObject *)right_o)->ob_fval; - res_stackref = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (PyStackRef_IsNull(res_stackref )) { - goto error; - } - /* End of uop copied from bytecodes for constant evaluation */ - res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - break; + if (PyJitRef_IsUnique(left)) { + ADD_OP(_BINARY_OP_MULTIPLY_FLOAT_INPLACE, 0, 0); + l = PyJitRef_Borrow(sym_new_null(ctx)); + r = right; + } + else if (PyJitRef_IsUnique(right)) { + ADD_OP(_BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT, 0, 0); + l = left; + r = PyJitRef_Borrow(sym_new_null(ctx)); } - res = sym_new_type(ctx, &PyFloat_Type); - if (PyJitRef_IsBorrowed(left) && PyJitRef_IsBorrowed(right)) { - REPLACE_OP(this_instr, op_without_decref_inputs[opcode], oparg, 0); + else { + l = left; + r = right; } + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -603,44 +954,31 @@ JitOptRef right; JitOptRef left; JitOptRef res; + JitOptRef l; + JitOptRef r; right = stack_pointer[-1]; left = stack_pointer[-2]; - if ( - sym_is_safe_const(ctx, left) && - sym_is_safe_const(ctx, right) - ) { - JitOptRef left_sym = left; - JitOptRef right_sym = right; - _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); - _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); - _PyStackRef res_stackref; - /* Start of uop copied from bytecodes for constant evaluation */ - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval + - ((PyFloatObject *)right_o)->ob_fval; - res_stackref = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (PyStackRef_IsNull(res_stackref )) { - goto error; - } - /* End of uop copied from bytecodes for constant evaluation */ - res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - break; + if (PyJitRef_IsUnique(left)) { + ADD_OP(_BINARY_OP_ADD_FLOAT_INPLACE, 0, 0); + l = PyJitRef_Borrow(sym_new_null(ctx)); + r = right; } - res = sym_new_type(ctx, &PyFloat_Type); - if (PyJitRef_IsBorrowed(left) && PyJitRef_IsBorrowed(right)) { - REPLACE_OP(this_instr, op_without_decref_inputs[opcode], oparg, 0); + else if (PyJitRef_IsUnique(right)) { + ADD_OP(_BINARY_OP_ADD_FLOAT_INPLACE_RIGHT, 0, 0); + l = left; + r = PyJitRef_Borrow(sym_new_null(ctx)); } + else { + l = left; + r = right; + } + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -648,122 +986,204 @@ JitOptRef right; JitOptRef left; JitOptRef res; + JitOptRef l; + JitOptRef r; right = stack_pointer[-1]; left = stack_pointer[-2]; - if ( - sym_is_safe_const(ctx, left) && - sym_is_safe_const(ctx, right) - ) { - JitOptRef left_sym = left; - JitOptRef right_sym = right; - _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); - _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); - _PyStackRef res_stackref; - /* Start of uop copied from bytecodes for constant evaluation */ - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyFloat_CheckExact(left_o)); - assert(PyFloat_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - double dres = - ((PyFloatObject *)left_o)->ob_fval - - ((PyFloatObject *)right_o)->ob_fval; - res_stackref = _PyFloat_FromDouble_ConsumeInputs(left, right, dres); - if (PyStackRef_IsNull(res_stackref )) { - goto error; - } - /* End of uop copied from bytecodes for constant evaluation */ - res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - break; + if (PyJitRef_IsUnique(left)) { + ADD_OP(_BINARY_OP_SUBTRACT_FLOAT_INPLACE, 0, 0); + l = PyJitRef_Borrow(sym_new_null(ctx)); + r = right; + } + else if (PyJitRef_IsUnique(right)) { + ADD_OP(_BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT, 0, 0); + l = left; + r = PyJitRef_Borrow(sym_new_null(ctx)); } - res = sym_new_type(ctx, &PyFloat_Type); - if (PyJitRef_IsBorrowed(left) && PyJitRef_IsBorrowed(right)) { - REPLACE_OP(this_instr, op_without_decref_inputs[opcode], oparg, 0); + else { + l = left; + r = right; } + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _BINARY_OP_MULTIPLY_FLOAT__NO_DECREF_INPUTS: { + case _BINARY_OP_ADD_FLOAT_INPLACE: { JitOptRef res; + JitOptRef l; + JitOptRef r; res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _BINARY_OP_ADD_FLOAT__NO_DECREF_INPUTS: { + case _BINARY_OP_SUBTRACT_FLOAT_INPLACE: { JitOptRef res; + JitOptRef l; + JitOptRef r; res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _BINARY_OP_SUBTRACT_FLOAT__NO_DECREF_INPUTS: { + case _BINARY_OP_MULTIPLY_FLOAT_INPLACE: { JitOptRef res; + JitOptRef l; + JitOptRef r; res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _BINARY_OP_ADD_UNICODE: { - JitOptRef right; - JitOptRef left; + case _BINARY_OP_ADD_FLOAT_INPLACE_RIGHT: { JitOptRef res; - right = stack_pointer[-1]; - left = stack_pointer[-2]; - if ( - sym_is_safe_const(ctx, left) && - sym_is_safe_const(ctx, right) - ) { - JitOptRef left_sym = left; - JitOptRef right_sym = right; - _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); - _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); - _PyStackRef res_stackref; - /* Start of uop copied from bytecodes for constant evaluation */ - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyUnicode_CheckExact(left_o)); - assert(PyUnicode_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - PyObject *res_o = PyUnicode_Concat(left_o, right_o); - PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - if (res_o == NULL) { - goto error; - } - res_stackref = PyStackRef_FromPyObjectSteal(res_o); - /* End of uop copied from bytecodes for constant evaluation */ - res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); - stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - break; - } + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_MULTIPLY_FLOAT_INPLACE_RIGHT: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBTRACT_FLOAT_INPLACE_RIGHT: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_TRUEDIV_FLOAT: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_TRUEDIV_FLOAT_INPLACE: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT: { + JitOptRef res; + JitOptRef l; + JitOptRef r; + res = sym_new_not_null(ctx); + l = sym_new_not_null(ctx); + r = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_ADD_UNICODE: { + JitOptRef right; + JitOptRef left; + JitOptRef res; + JitOptRef l; + JitOptRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; res = sym_new_type(ctx, &PyUnicode_Type); + l = left; + r = right; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BINARY_OP_INPLACE_ADD_UNICODE: { JitOptRef right; JitOptRef left; + JitOptRef res; right = stack_pointer[-1]; left = stack_pointer[-2]; - JitOptRef res; if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) { assert(PyUnicode_CheckExact(sym_get_const(ctx, left))); assert(PyUnicode_CheckExact(sym_get_const(ctx, right))); @@ -772,27 +1192,100 @@ goto error; } res = sym_new_const(ctx, temp); + CHECK_STACK_BOUNDS(-1); + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyUnicode_Type); + stack_pointer += -1; } - GETLOCAL(this_instr->operand0) = res; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + GETLOCAL(this_instr->operand0) = sym_new_null(ctx); + stack_pointer[-1] = res; + break; + } + + case _GUARD_BINARY_OP_EXTEND_LHS: { + JitOptRef left; + left = stack_pointer[-2]; + PyObject *descr = (PyObject *)this_instr->operand0; + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr; + assert(d != NULL && d->guard == NULL && d->lhs_type != NULL); + if (sym_matches_type(left, d->lhs_type)) { + ADD_OP(_NOP, 0, 0); + } + sym_set_type(left, d->lhs_type); + break; + } + + case _GUARD_BINARY_OP_EXTEND_RHS: { + JitOptRef right; + right = stack_pointer[-1]; + PyObject *descr = (PyObject *)this_instr->operand0; + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr; + assert(d != NULL && d->guard == NULL && d->rhs_type != NULL); + if (sym_matches_type(right, d->rhs_type)) { + ADD_OP(_NOP, 0, 0); + } + sym_set_type(right, d->rhs_type); break; } case _GUARD_BINARY_OP_EXTEND: { + JitOptRef right; + JitOptRef left; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *descr = (PyObject *)this_instr->operand0; + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr; + if (d != NULL && d->guard == NULL) { + assert(d->lhs_type != NULL && d->rhs_type != NULL); + bool lhs_known = sym_matches_type(left, d->lhs_type); + bool rhs_known = sym_matches_type(right, d->rhs_type); + if (lhs_known && rhs_known) { + ADD_OP(_NOP, 0, 0); + } + else if (lhs_known) { + ADD_OP(_GUARD_BINARY_OP_EXTEND_RHS, 0, (uintptr_t)d); + sym_set_type(right, d->rhs_type); + } + else if (rhs_known) { + ADD_OP(_GUARD_BINARY_OP_EXTEND_LHS, 0, (uintptr_t)d); + sym_set_type(left, d->lhs_type); + } + } break; } case _BINARY_OP_EXTEND: { + JitOptRef right; + JitOptRef left; JitOptRef res; - res = sym_new_not_null(ctx); + JitOptRef l; + JitOptRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + PyObject *descr = (PyObject *)this_instr->operand0; + _PyBinaryOpSpecializationDescr *d = (_PyBinaryOpSpecializationDescr *)descr; + if (d != NULL && d->result_type != NULL) { + res = sym_new_type(ctx, d->result_type); + if (d->result_unique) { + res = PyJitRef_MakeUnique(res); + } + } + else { + res = sym_new_not_null(ctx); + } + l = left; + r = right; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -810,42 +1303,97 @@ else { res = sym_new_not_null(ctx); } + CHECK_STACK_BOUNDS(-2); stack_pointer[-3] = res; stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _STORE_SLICE: { + CHECK_STACK_BOUNDS(-4); stack_pointer += -4; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BINARY_OP_SUBSCR_LIST_INT: { + JitOptRef sub_st; + JitOptRef list_st; JitOptRef res; - res = sym_new_not_null(ctx); + JitOptRef ls; + JitOptRef ss; + sub_st = stack_pointer[-1]; + list_st = stack_pointer[-2]; + res = sym_new_unknown(ctx); + ls = list_st; + ss = sub_st; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = ls; + stack_pointer[0] = ss; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BINARY_OP_SUBSCR_LIST_SLICE: { + JitOptRef sub_st; + JitOptRef list_st; JitOptRef res; - res = sym_new_not_null(ctx); + JitOptRef ls; + JitOptRef ss; + sub_st = stack_pointer[-1]; + list_st = stack_pointer[-2]; + res = sym_new_type(ctx, &PyList_Type); + ls = list_st; + ss = sub_st; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = ls; + stack_pointer[0] = ss; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BINARY_OP_SUBSCR_STR_INT: { + JitOptRef sub_st; + JitOptRef str_st; + JitOptRef res; + JitOptRef s; + JitOptRef i; + sub_st = stack_pointer[-1]; + str_st = stack_pointer[-2]; + res = sym_new_type(ctx, &PyUnicode_Type); + s = str_st; + i = sub_st; + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = s; + stack_pointer[0] = i; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _BINARY_OP_SUBSCR_USTR_INT: { + JitOptRef sub_st; + JitOptRef str_st; JitOptRef res; + JitOptRef s; + JitOptRef i; + sub_st = stack_pointer[-1]; + str_st = stack_pointer[-2]; res = sym_new_type(ctx, &PyUnicode_Type); + s = str_st; + i = sub_st; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = s; + stack_pointer[0] = i; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -853,9 +1401,11 @@ JitOptRef nos; nos = stack_pointer[-2]; if (sym_matches_type(nos, &PyTuple_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(nos, &PyTuple_Type); } - sym_set_type(nos, &PyTuple_Type); break; } @@ -863,9 +1413,29 @@ JitOptRef tos; tos = stack_pointer[-1]; if (sym_matches_type(tos, &PyTuple_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PyTuple_Type); + } + break; + } + + case _GUARD_BINARY_OP_SUBSCR_TUPLE_INT_BOUNDS: { + JitOptRef sub_st; + JitOptRef tuple_st; + sub_st = stack_pointer[-1]; + tuple_st = stack_pointer[-2]; + assert(sym_matches_type(tuple_st, &PyTuple_Type)); + if (sym_is_const(ctx, sub_st)) { + assert(PyLong_CheckExact(sym_get_const(ctx, sub_st))); + long index = PyLong_AsLong(sym_get_const(ctx, sub_st)); + assert(index >= 0); + Py_ssize_t tuple_length = sym_tuple_length(tuple_st); + if (tuple_length != -1 && index < tuple_length) { + ADD_OP(_NOP, 0, 0); + } } - sym_set_type(tos, &PyTuple_Type); break; } @@ -873,6 +1443,8 @@ JitOptRef sub_st; JitOptRef tuple_st; JitOptRef res; + JitOptRef ts; + JitOptRef ss; sub_st = stack_pointer[-1]; tuple_st = stack_pointer[-2]; assert(sym_matches_type(tuple_st, &PyTuple_Type)); @@ -880,7 +1452,7 @@ assert(PyLong_CheckExact(sym_get_const(ctx, sub_st))); long index = PyLong_AsLong(sym_get_const(ctx, sub_st)); assert(index >= 0); - int tuple_length = sym_tuple_length(tuple_st); + Py_ssize_t tuple_length = sym_tuple_length(tuple_st); if (tuple_length == -1) { res = sym_new_not_null(ctx); } @@ -892,19 +1464,81 @@ else { res = sym_new_not_null(ctx); } + ts = tuple_st; + ss = sub_st; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = ts; + stack_pointer[0] = ss; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _GUARD_NOS_DICT: { + case _GUARD_NOS_DICT_SUBSCRIPT: { JitOptRef nos; nos = stack_pointer[-2]; - if (sym_matches_type(nos, &PyDict_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + PyTypeObject *tp = sym_get_type(nos); + bool definite = true; + if (!tp) { + tp = sym_get_probable_type(nos); + definite = false; + } + if (tp && tp->tp_as_mapping && + tp->tp_as_mapping->mp_subscript == _PyDict_Subscript) { + if (definite) { + ADD_OP(_NOP, 0, 0); + } + else { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(nos, tp); + } + PyType_Watch(TYPE_WATCHER_ID, (PyObject *)tp); + _Py_BloomFilter_Add(dependencies, tp); + } + break; + } + + case _GUARD_NOS_DICT_STORE_SUBSCRIPT: { + JitOptRef nos; + nos = stack_pointer[-2]; + PyTypeObject *tp = sym_get_type(nos); + bool definite = true; + if (!tp) { + tp = sym_get_probable_type(nos); + definite = false; + } + if (tp && tp->tp_as_mapping && + tp->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript) { + if (definite) { + ADD_OP(_NOP, 0, 0); + } + else { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(nos, tp); + } + PyType_Watch(TYPE_WATCHER_ID, (PyObject *)tp); + _Py_BloomFilter_Add(dependencies, tp); + } + break; + } + + case _GUARD_TOS_ANY_DICT: { + JitOptRef tos; + tos = stack_pointer[-1]; + PyTypeObject *tp = sym_get_type(tos); + if (tp == &PyDict_Type || tp == &PyFrozenDict_Type) { + ADD_OP(_NOP, 0, 0); + } + else { + tp = sym_get_probable_type(tos); + if (tp == &PyDict_Type) { + ADD_OP(_GUARD_TOS_DICT, 0, 0); + } + else if (tp == &PyFrozenDict_Type) { + ADD_OP(_GUARD_TOS_FROZENDICT, 0, 0); + } } - sym_set_type(nos, &PyDict_Type); break; } @@ -912,89 +1546,281 @@ JitOptRef tos; tos = stack_pointer[-1]; if (sym_matches_type(tos, &PyDict_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PyDict_Type); + } + break; + } + + case _GUARD_TOS_FROZENDICT: { + JitOptRef tos; + tos = stack_pointer[-1]; + if (sym_matches_type(tos, &PyFrozenDict_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PyFrozenDict_Type); } - sym_set_type(tos, &PyDict_Type); + break; + } + + case _BINARY_OP_SUBSCR_DICT_KNOWN_HASH: { + JitOptRef res; + JitOptRef ds; + JitOptRef ss; + res = sym_new_not_null(ctx); + ds = sym_new_not_null(ctx); + ss = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = ds; + stack_pointer[0] = ss; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BINARY_OP_SUBSCR_DICT: { + JitOptRef sub_st; + JitOptRef dict_st; JitOptRef res; + JitOptRef ds; + JitOptRef ss; + sub_st = stack_pointer[-1]; + dict_st = stack_pointer[-2]; res = sym_new_not_null(ctx); + ds = dict_st; + ss = sub_st; + PyObject *sub = sym_get_const(ctx, sub_st); + if (sym_is_not_container(sub_st) && + sym_matches_type(dict_st, &PyFrozenDict_Type)) { + if ( + sym_is_safe_const(ctx, dict_st) && + sym_is_safe_const(ctx, sub_st) + ) { + JitOptRef dict_st_sym = dict_st; + JitOptRef sub_st_sym = sub_st; + _PyStackRef dict_st = sym_get_const_as_stackref(ctx, dict_st_sym); + _PyStackRef sub_st = sym_get_const_as_stackref(ctx, sub_st_sym); + _PyStackRef res_stackref; + _PyStackRef ds_stackref; + _PyStackRef ss_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); + PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st); + assert(Py_TYPE(dict)->tp_as_mapping->mp_subscript == _PyDict_Subscript); + STAT_INC(BINARY_OP, hit); + PyObject *res_o = _PyDict_Subscript(dict, sub); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + res_stackref = PyStackRef_FromPyObjectSteal(res_o); + ds_stackref = dict_st; + ss_stackref = sub_st; + /* End of uop copied from bytecodes for constant evaluation */ + (void)ds_stackref; + (void)ss_stackref; + res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = ds; + stack_pointer[0] = ss; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + } + else if (sub != NULL) { + optimize_dict_known_hash(ctx, dependencies, this_instr, + sub, _BINARY_OP_SUBSCR_DICT_KNOWN_HASH); + } + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = ds; + stack_pointer[0] = ss; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BINARY_OP_SUBSCR_CHECK_FUNC: { + JitOptRef container; JitOptRef getitem; + container = stack_pointer[-2]; getitem = sym_new_not_null(ctx); + PyTypeObject *tp = sym_get_type(container); + if (tp == NULL) { + PyObject *c = sym_get_probable_value(container); + if (c != NULL) { + tp = Py_TYPE(c); + } + } + if (tp != NULL) { + PyObject *getitem_o = ((PyHeapTypeObject *)tp)->_spec_cache.getitem; + sym_set_recorded_value(getitem, getitem_o); + } + CHECK_STACK_BOUNDS(1); stack_pointer[0] = getitem; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BINARY_OP_SUBSCR_INIT_CALL: { + JitOptRef getitem; + JitOptRef sub; + JitOptRef container; JitOptRef new_frame; - new_frame = PyJitRef_NULL; - ctx->done = true; + getitem = stack_pointer[-1]; + sub = stack_pointer[-2]; + container = stack_pointer[-3]; + _Py_UOpsAbstractFrame *f = frame_new_from_symbol(ctx, getitem, NULL, 0); + if (f == NULL) { + break; + } + f->locals[0] = container; + f->locals[1] = sub; + new_frame = PyJitRef_WrapInvalid(f); + CHECK_STACK_BOUNDS(-2); stack_pointer[-3] = new_frame; stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LIST_APPEND: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _SET_ADD: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _STORE_SUBSCR: { + CHECK_STACK_BOUNDS(-3); stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _STORE_SUBSCR_LIST_INT: { - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef sub_st; + JitOptRef list_st; + JitOptRef value; + JitOptRef ls; + JitOptRef ss; + sub_st = stack_pointer[-1]; + list_st = stack_pointer[-2]; + value = stack_pointer[-3]; + (void)value; + ls = list_st; + ss = sub_st; + CHECK_STACK_BOUNDS(-1); + stack_pointer[-3] = ls; + stack_pointer[-2] = ss; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _STORE_SUBSCR_DICT: { - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef sub; + JitOptRef dict_st; + JitOptRef value; + JitOptRef st; + sub = stack_pointer[-1]; + dict_st = stack_pointer[-2]; + value = stack_pointer[-3]; + PyObject *sub_o = sym_get_const(ctx, sub); + if (sub_o != NULL) { + optimize_dict_known_hash(ctx, dependencies, this_instr, + sub_o, _STORE_SUBSCR_DICT_KNOWN_HASH); + } + (void)value; + st = dict_st; + CHECK_STACK_BOUNDS(-2); + stack_pointer[-3] = st; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _STORE_SUBSCR_DICT_KNOWN_HASH: { + JitOptRef st; + st = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(-2); + stack_pointer[-3] = st; + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _DELETE_SUBSCR: { + CHECK_STACK_BOUNDS(-2); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _CALL_INTRINSIC_1: { + JitOptRef value; JitOptRef res; + JitOptRef v; + value = stack_pointer[-1]; res = sym_new_not_null(ctx); + v = value; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = res; + stack_pointer[0] = v; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _CALL_INTRINSIC_2: { + JitOptRef value1_st; + JitOptRef value2_st; JitOptRef res; + JitOptRef vs1; + JitOptRef vs2; + value1_st = stack_pointer[-1]; + value2_st = stack_pointer[-2]; res = sym_new_not_null(ctx); + vs1 = value1_st; + vs2 = value2_st; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = vs1; + stack_pointer[0] = vs2; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _MAKE_HEAP_SAFE: { + JitOptRef value; + value = stack_pointer[-1]; + if (sym_is_immortal(PyJitRef_Unwrap(value))) { + ADD_OP(_NOP, 0, 0); + } + value = PyJitRef_StripBorrowInfo(value); + stack_pointer[-1] = value; break; } @@ -1002,26 +1828,27 @@ JitOptRef retval; JitOptRef res; retval = stack_pointer[-1]; - JitOptRef temp = PyJitRef_Wrap(PyJitRef_Unwrap(retval)); + JitOptRef temp = retval; + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); ctx->frame->stack_pointer = stack_pointer; - frame_pop(ctx); - stack_pointer = ctx->frame->stack_pointer; - assert(corresponding_check_stack == NULL); - assert(co != NULL); - int framesize = co->co_framesize; - assert(framesize > 0); - assert(framesize <= curr_space); - curr_space -= framesize; - co = get_code(this_instr); - if (co == NULL) { + assert(this_instr[1].opcode == _RECORD_CODE); + PyCodeObject *returning_code = (PyCodeObject *)this_instr[1].operand0; + assert(PyCode_Check(returning_code)); + if (returning_code == NULL) { ctx->done = true; + break; } - res = temp; + if (frame_pop(ctx, returning_code)) { + break; + } + stack_pointer = ctx->frame->stack_pointer; + res = temp; + CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1033,11 +1860,19 @@ } case _GET_ANEXT: { + JitOptRef aiter; JitOptRef awaitable; - awaitable = sym_new_not_null(ctx); + aiter = stack_pointer[-1]; + if (sym_matches_type(aiter, &PyAsyncGen_Type)) { + awaitable = sym_new_type(ctx, &_PyAsyncGenASend_Type); + } + else { + awaitable = sym_new_not_null(ctx); + } + CHECK_STACK_BOUNDS(1); stack_pointer[0] = awaitable; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1048,50 +1883,140 @@ break; } - /* _SEND is not a viable micro-op for tier 2 */ - case _SEND_GEN_FRAME: { + JitOptRef v; + JitOptRef receiver; JitOptRef gen_frame; - gen_frame = PyJitRef_NULL; - ctx->done = true; + v = stack_pointer[-1]; + receiver = stack_pointer[-3]; + _Py_UOpsAbstractFrame *new_frame = frame_new_from_symbol(ctx, receiver, NULL, 0); + if (new_frame == NULL) { + ctx->done = true; + break; + } + new_frame->stack_pointer[0] = PyJitRef_StripReferenceInfo(v); + new_frame->stack_pointer++; + gen_frame = PyJitRef_WrapInvalid(new_frame); stack_pointer[-1] = gen_frame; break; } + case _GUARD_TOS_IS_NONE: { + break; + } + + case _GUARD_NOS_NOT_NULL: { + JitOptRef nos; + nos = stack_pointer[-2]; + if (sym_is_not_null(nos)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_non_null(nos); + } + break; + } + + /* _SEND_VIRTUAL is not a viable micro-op for tier 2 */ + + case _SEND_VIRTUAL_TIER_TWO: { + JitOptRef next; + next = sym_new_not_null(ctx); + stack_pointer[-1] = next; + break; + } + + case _GUARD_3OS_ASYNC_GEN_ASEND: { + JitOptRef iter; + iter = stack_pointer[-3]; + if (sym_matches_type(iter, &_PyAsyncGenASend_Type)) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + else { + sym_set_type(iter, &_PyAsyncGenASend_Type); + } + break; + } + + /* _SEND_ASYNC_GEN is not a viable micro-op for tier 2 */ + + case _SEND_ASYNC_GEN_TIER_TWO: { + JitOptRef asend; + JitOptRef null_out; + JitOptRef retval; + asend = sym_new_not_null(ctx); + null_out = sym_new_not_null(ctx); + retval = sym_new_not_null(ctx); + stack_pointer[-3] = asend; + stack_pointer[-2] = null_out; + stack_pointer[-1] = retval; + break; + } + case _YIELD_VALUE: { + JitOptRef retval; JitOptRef value; - value = sym_new_unknown(ctx); - stack_pointer[-1] = value; + retval = stack_pointer[-1]; + JitOptRef temp = retval; + CHECK_STACK_BOUNDS(-1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + ctx->frame->stack_pointer = stack_pointer; + assert(this_instr[1].opcode == _RECORD_CODE); + PyCodeObject *returning_code = (PyCodeObject *)this_instr[1].operand0; + if (returning_code == NULL) { + ctx->done = true; + break; + } + assert(PyCode_Check(returning_code)); + if (frame_pop(ctx, returning_code)) { + break; + } + stack_pointer = ctx->frame->stack_pointer; + value = temp; + CHECK_STACK_BOUNDS(1); + stack_pointer[0] = value; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _POP_EXCEPT: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_COMMON_CONSTANT: { JitOptRef value; - value = sym_new_not_null(ctx); + assert(oparg < NUM_COMMON_CONSTANTS); + PyObject *val = PyStackRef_AsPyObjectBorrow( + _PyInterpreterState_GET()->common_consts[oparg]); + assert(_Py_IsImmortal(val)); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); + value = PyJitRef_Borrow(sym_new_const(ctx, val)); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_BUILD_CLASS: { JitOptRef bc; bc = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = bc; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _STORE_NAME: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1108,8 +2033,9 @@ for (int i = 0; i < oparg; i++) { values[i] = sym_new_unknown(ctx); } + CHECK_STACK_BOUNDS(-1 + oparg); stack_pointer += -1 + oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1118,12 +2044,45 @@ JitOptRef val1; JitOptRef val0; seq = stack_pointer[-1]; + if (PyJitRef_IsUnique(seq) && sym_tuple_length(seq) == 2) { + ADD_OP(_UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE, oparg, 0); + } val0 = sym_tuple_getitem(ctx, seq, 0); val1 = sym_tuple_getitem(ctx, seq, 1); + CHECK_STACK_BOUNDS(1); + stack_pointer[-1] = val1; + stack_pointer[0] = val0; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_UNIQUE_TWO_TUPLE: { + JitOptRef val1; + JitOptRef val0; + val1 = sym_new_not_null(ctx); + val0 = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = val1; stack_pointer[0] = val0; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE: { + JitOptRef val2; + JitOptRef val1; + JitOptRef val0; + val2 = sym_new_not_null(ctx); + val1 = sym_new_not_null(ctx); + val0 = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(2); + stack_pointer[-1] = val2; + stack_pointer[0] = val1; + stack_pointer[1] = val0; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1132,11 +2091,30 @@ JitOptRef *values; seq = stack_pointer[-1]; values = &stack_pointer[-1]; + if (PyJitRef_IsUnique(seq) && sym_tuple_length(seq) == 3) { + ADD_OP(_UNPACK_SEQUENCE_UNIQUE_THREE_TUPLE, oparg, 0); + } + else if (PyJitRef_IsUnique(seq) && sym_tuple_length(seq) == oparg) { + ADD_OP(_UNPACK_SEQUENCE_UNIQUE_TUPLE, oparg, 0); + } for (int i = 0; i < oparg; i++) { values[i] = sym_tuple_getitem(ctx, seq, oparg - i - 1); } + CHECK_STACK_BOUNDS(-1 + oparg); + stack_pointer += -1 + oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _UNPACK_SEQUENCE_UNIQUE_TUPLE: { + JitOptRef *values; + values = &stack_pointer[-1]; + for (int _i = oparg; --_i >= 0;) { + values[_i] = sym_new_not_null(ctx); + } + CHECK_STACK_BOUNDS(-1 + oparg); stack_pointer += -1 + oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1146,8 +2124,9 @@ for (int _i = oparg; --_i >= 0;) { values[_i] = sym_new_not_null(ctx); } + CHECK_STACK_BOUNDS(-1 + oparg); stack_pointer += -1 + oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1161,26 +2140,30 @@ for (int i = 0; i < totalargs; i++) { values[i] = sym_new_unknown(ctx); } + CHECK_STACK_BOUNDS((oparg & 0xFF) + (oparg >> 8)); stack_pointer += (oparg & 0xFF) + (oparg >> 8); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _STORE_ATTR: { + CHECK_STACK_BOUNDS(-2); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _DELETE_ATTR: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _STORE_GLOBAL: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1191,9 +2174,10 @@ case _LOAD_LOCALS: { JitOptRef locals; locals = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = locals; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1202,9 +2186,10 @@ case _LOAD_NAME: { JitOptRef v; v = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = v; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1212,8 +2197,9 @@ JitOptRef *res; res = &stack_pointer[0]; res[0] = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1221,36 +2207,129 @@ JitOptRef *null; null = &stack_pointer[0]; if (oparg & 1) { - REPLACE_OP(this_instr, _PUSH_NULL, 0, 0); + ADD_OP(_PUSH_NULL, 0, 0); null[0] = sym_new_null(ctx); } else { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } + CHECK_STACK_BOUNDS((oparg & 1)); stack_pointer += (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _GUARD_GLOBALS_VERSION: { + uint16_t version = (uint16_t)this_instr->operand0; + if (ctx->frame->func != NULL) { + PyObject *globals = ctx->frame->func->func_globals; + if (incorrect_keys(globals, version)) { + OPT_STAT_INC(remove_globals_incorrect_keys); + ctx->done = true; + } + else if (get_mutations(globals) >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { + } + else { + if (!ctx->frame->globals_watched) { + PyDict_Watch(GLOBALS_WATCHER_ID, globals); + _Py_BloomFilter_Add(dependencies, globals); + ctx->frame->globals_watched = true; + } + if (ctx->frame->globals_checked_version == version) { + ADD_OP(_NOP, 0, 0); + } + } + } + ctx->frame->globals_checked_version = version; break; } case _LOAD_GLOBAL_MODULE: { JitOptRef res; - res = sym_new_not_null(ctx); + uint16_t version = (uint16_t)this_instr->operand0; + uint16_t index = (uint16_t)this_instr->operand1; + (void)index; + PyObject *cnst = NULL; + if (ctx->frame->func != NULL) { + PyObject *globals = ctx->frame->func->func_globals; + if (incorrect_keys(globals, version)) { + OPT_STAT_INC(remove_globals_incorrect_keys); + ctx->done = true; + } + else if (get_mutations(globals) >= _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { + } + else { + if (!ctx->frame->globals_watched) { + PyDict_Watch(GLOBALS_WATCHER_ID, globals); + _Py_BloomFilter_Add(dependencies, globals); + ctx->frame->globals_watched = true; + } + if (ctx->frame->globals_checked_version != version && this_instr[-1].opcode == _NOP) { + REPLACE_OP(uop_buffer_last(&ctx->out_buffer), _GUARD_GLOBALS_VERSION, 0, version); + ctx->frame->globals_checked_version = version; + } + if (ctx->frame->globals_checked_version == version) { + cnst = convert_global_to_const(this_instr, globals); + } + } + } + if (cnst == NULL) { + res = sym_new_not_null(ctx); + } + else { + if (_Py_IsImmortal(cnst)) { + res = PyJitRef_Borrow(sym_new_const(ctx, cnst)); + } + else { + res = sym_new_const(ctx, cnst); + } + } + CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_GLOBAL_BUILTINS: { JitOptRef res; - res = sym_new_not_null(ctx); + uint16_t version = (uint16_t)this_instr->operand0; + uint16_t index = (uint16_t)this_instr->operand1; + (void)version; + (void)index; + PyObject *cnst = NULL; + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyObject *builtins = interp->builtins; + if (incorrect_keys(builtins, version)) { + OPT_STAT_INC(remove_globals_incorrect_keys); + ctx->done = true; + } + else if (interp->rare_events.builtin_dict >= _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) { + } + else { + if (!ctx->builtins_watched) { + PyDict_Watch(BUILTINS_WATCHER_ID, builtins); + ctx->builtins_watched = true; + } + if (ctx->frame->globals_checked_version != 0 && ctx->frame->globals_watched) { + cnst = convert_global_to_const(this_instr, builtins); + } + } + if (cnst == NULL) { + res = sym_new_not_null(ctx); + } + else { + if (_Py_IsImmortal(cnst)) { + res = PyJitRef_Borrow(sym_new_const(ctx, cnst)); + } + else { + res = sym_new_const(ctx, cnst); + } + } + CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1276,46 +2355,60 @@ case _LOAD_DEREF: { JitOptRef value; value = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _STORE_DEREF: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _COPY_FREE_VARS: { + PyCodeObject *co = get_current_code_object(ctx); + if (co == NULL) { + ctx->done = true; + break; + } + int offset = co->co_nlocalsplus - oparg; + for (int i = 0; i < oparg; ++i) { + ctx->frame->locals[offset + i] = sym_new_not_null(ctx); + } break; } case _BUILD_STRING: { JitOptRef str; str = sym_new_type(ctx, &PyUnicode_Type); + CHECK_STACK_BOUNDS(1 - oparg); stack_pointer[-oparg] = str; stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BUILD_INTERPOLATION: { JitOptRef interpolation; interpolation = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(-1 - (oparg & 1)); stack_pointer[-2 - (oparg & 1)] = interpolation; stack_pointer += -1 - (oparg & 1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BUILD_TEMPLATE: { JitOptRef template; template = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(-1); stack_pointer[-2] = template; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1324,48 +2417,65 @@ JitOptRef tup; values = &stack_pointer[-oparg]; tup = sym_new_tuple(ctx, oparg, values); + tup = PyJitRef_MakeUnique(tup); + CHECK_STACK_BOUNDS(1 - oparg); stack_pointer[-oparg] = tup; stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BUILD_LIST: { JitOptRef list; list = sym_new_type(ctx, &PyList_Type); + CHECK_STACK_BOUNDS(1 - oparg); stack_pointer[-oparg] = list; stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LIST_EXTEND: { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef iterable_st; + JitOptRef list_st; + JitOptRef i; + iterable_st = stack_pointer[-1]; + list_st = stack_pointer[-2 - (oparg-1)]; + (void)list_st; + i = iterable_st; + stack_pointer[-1] = i; break; } case _SET_UPDATE: { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef iterable; + JitOptRef set; + JitOptRef i; + iterable = stack_pointer[-1]; + set = stack_pointer[-2 - (oparg-1)]; + (void)set; + i = iterable; + stack_pointer[-1] = i; break; } case _BUILD_SET: { JitOptRef set; set = sym_new_type(ctx, &PySet_Type); + CHECK_STACK_BOUNDS(1 - oparg); stack_pointer[-oparg] = set; stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BUILD_MAP: { JitOptRef map; map = sym_new_type(ctx, &PyDict_Type); + CHECK_STACK_BOUNDS(1 - oparg*2); stack_pointer[-oparg*2] = map; stack_pointer += 1 - oparg*2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1374,58 +2484,115 @@ } case _DICT_UPDATE: { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef update; + JitOptRef dict; + JitOptRef upd; + update = stack_pointer[-1]; + dict = stack_pointer[-2 - (oparg - 1)]; + (void)dict; + upd = update; + stack_pointer[-1] = upd; break; } case _DICT_MERGE: { - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef update; + JitOptRef dict; + JitOptRef callable; + JitOptRef u; + update = stack_pointer[-1]; + dict = stack_pointer[-2 - (oparg - 1)]; + callable = stack_pointer[-5 - (oparg - 1)]; + (void)callable; + (void)dict; + u = update; + stack_pointer[-1] = u; break; } case _MAP_ADD: { + CHECK_STACK_BOUNDS(-2); stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_SUPER_ATTR_ATTR: { JitOptRef attr_st; attr_st = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(-2); stack_pointer[-3] = attr_st; stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_TYPE_VERSION: { + break; + } + + case _GUARD_LOAD_SUPER_ATTR_METHOD: { + JitOptRef class_st; + JitOptRef global_super_st; + class_st = stack_pointer[-2]; + global_super_st = stack_pointer[-3]; + if (sym_get_const(ctx, global_super_st) == (PyObject *)&PySuper_Type) { + PyTypeObject *probable = (PyTypeObject *)sym_get_probable_value(class_st); + PyTypeObject *known = (PyTypeObject *)sym_get_const(ctx, class_st); + if (known == NULL && probable != NULL && PyType_Check(probable)) { + ADD_OP(_GUARD_NOS_TYPE_VERSION, 0, probable->tp_version_tag); + known = probable; + } + sym_set_const(class_st, (PyObject *)known); + } + else { + sym_set_const(global_super_st, (PyObject *)&PySuper_Type); + sym_set_type(class_st, &PyType_Type); + } break; } case _LOAD_SUPER_ATTR_METHOD: { + JitOptRef self_st; + JitOptRef class_st; JitOptRef attr; JitOptRef self_or_null; - attr = sym_new_not_null(ctx); - self_or_null = sym_new_not_null(ctx); - stack_pointer[-3] = attr; - stack_pointer[-2] = self_or_null; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + self_st = stack_pointer[-1]; + class_st = stack_pointer[-2]; + self_or_null = self_st; + PyTypeObject *su_type = (PyTypeObject *)sym_get_const(ctx, class_st); + PyTypeObject *obj_type = sym_get_type(self_st); + CHECK_STACK_BOUNDS(-3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + PyObject *name = get_co_name(ctx, oparg >> 2); + attr = lookup_super_attr(ctx, dependencies, this_instr, + su_type, obj_type, name, + _LOAD_CONST_INLINE_BORROW, + _LOAD_CONST_INLINE, _SWAP); + CHECK_STACK_BOUNDS(2); + stack_pointer[0] = attr; + stack_pointer[1] = self_or_null; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_ATTR: { JitOptRef owner; - JitOptRef *attr; + JitOptRef attr; JitOptRef *self_or_null; owner = stack_pointer[-1]; - attr = &stack_pointer[-1]; self_or_null = &stack_pointer[0]; (void)owner; - *attr = sym_new_not_null(ctx); + attr = sym_new_not_null(ctx); if (oparg & 1) { self_or_null[0] = sym_new_unknown(ctx); } + CHECK_STACK_BOUNDS((oparg&1)); + stack_pointer[-1] = attr; stack_pointer += (oparg&1); - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1434,21 +2601,53 @@ owner = stack_pointer[-1]; uint32_t type_version = (uint32_t)this_instr->operand0; assert(type_version); + assert(this_instr[-1].opcode == _RECORD_TOS_TYPE || this_instr[-1].opcode == _RECORD_TOS); if (sym_matches_type_version(owner, type_version)) { - REPLACE_OP(this_instr, _NOP, 0, 0); - } else { - PyTypeObject *type = _PyType_LookupByVersion(type_version); - if (type) { - if (sym_set_type_version(owner, type_version)) { - PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type); - _Py_BloomFilter_Add(dependencies, type); - } + ADD_OP(_NOP, 0, 0); + } + else { + PyTypeObject *probable_type = sym_get_probable_type(owner); + if (probable_type != NULL && + probable_type->tp_version_tag == type_version) { + sym_set_type(owner, probable_type); + sym_set_type_version(owner, type_version); + watch_type(probable_type, dependencies); + } + else { + ctx->contradiction = true; + ctx->done = true; + break; + } + } + break; + } + + case _GUARD_TYPE_VERSION_LOCKED: { + JitOptRef owner; + owner = stack_pointer[-1]; + uint32_t type_version = (uint32_t)this_instr->operand0; + assert(type_version); + if (sym_matches_type_version(owner, type_version)) { + ADD_OP(_NOP, 0, 0); + } + else { + PyTypeObject *probable_type = sym_get_probable_type(owner); + if (probable_type != NULL && + probable_type->tp_version_tag == type_version) { + sym_set_type(owner, probable_type); + sym_set_type_version(owner, type_version); + watch_type(probable_type, dependencies); + } + else { + ctx->contradiction = true; + ctx->done = true; + break; } } break; } - case _GUARD_TYPE_VERSION_AND_LOCK: { + case _GUARD_TYPE: { break; } @@ -1457,20 +2656,29 @@ } case _LOAD_ATTR_INSTANCE_VALUE: { + JitOptRef owner; JitOptRef attr; + JitOptRef o; + owner = stack_pointer[-1]; uint16_t offset = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); (void)offset; + o = owner; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; + stack_pointer[0] = o; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_ATTR_MODULE: { JitOptRef owner; JitOptRef attr; + JitOptRef o; owner = stack_pointer[-1]; uint32_t dict_version = (uint32_t)this_instr->operand0; - uint16_t index = (uint16_t)this_instr->operand0; + uint16_t index = (uint16_t)this_instr->operand1; (void)dict_version; (void)index; attr = PyJitRef_NULL; @@ -1483,11 +2691,15 @@ if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { PyDict_Watch(GLOBALS_WATCHER_ID, dict); _Py_BloomFilter_Add(dependencies, dict); - PyObject *res = convert_global_to_const(this_instr, dict, true); + PyObject *res = convert_global_to_const(this_instr, dict); if (res == NULL) { attr = sym_new_not_null(ctx); } else { + bool immortal = _Py_IsImmortal(res); + ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE, + 0, (uintptr_t)res); + ADD_OP(_SWAP, 2, 0); attr = sym_new_const(ctx, res); } } @@ -1496,25 +2708,46 @@ if (PyJitRef_IsNull(attr)) { attr = sym_new_not_null(ctx); } + o = owner; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; + stack_pointer[0] = o; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_ATTR_WITH_HINT: { + JitOptRef owner; JitOptRef attr; + JitOptRef o; + owner = stack_pointer[-1]; uint16_t hint = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); (void)hint; + o = owner; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; + stack_pointer[0] = o; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_ATTR_SLOT: { + JitOptRef owner; JitOptRef attr; + JitOptRef o; + owner = stack_pointer[-1]; uint16_t index = (uint16_t)this_instr->operand0; attr = sym_new_not_null(ctx); (void)index; + o = owner; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; + stack_pointer[0] = o; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1522,13 +2755,14 @@ JitOptRef owner; owner = stack_pointer[-1]; uint32_t type_version = (uint32_t)this_instr->operand0; - PyObject *type = (PyObject *)_PyType_LookupByVersion(type_version); - if (type) { + PyObject *type = sym_get_probable_value(owner); + if (type != NULL && ((PyTypeObject *)type)->tp_version_tag == type_version) { if (type == sym_get_const(ctx, owner)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } else { sym_set_const(owner, type); + watch_type((PyTypeObject *)type, dependencies); } } break; @@ -1541,156 +2775,650 @@ PyObject *descr = (PyObject *)this_instr->operand0; (void)descr; PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _POP_TOP, _NOP); stack_pointer[-1] = attr; break; } case _LOAD_ATTR_PROPERTY_FRAME: { + JitOptRef owner; JitOptRef new_frame; - PyObject *fget = (PyObject *)this_instr->operand0; - (void)fget; - new_frame = PyJitRef_NULL; - ctx->done = true; + owner = stack_pointer[-1]; + uint32_t func_version = (uint32_t)this_instr->operand0; + PyObject *fget = (PyObject *)this_instr->operand1; + PyFunctionObject *func = (PyFunctionObject *)fget; + if (sym_get_type_version(owner) == 0 || + func->func_version != func_version) { + ctx->contradiction = true; + ctx->done = true; + break; + } + _Py_BloomFilter_Add(dependencies, fget); + PyCodeObject *co = (PyCodeObject *)func->func_code; + _Py_UOpsAbstractFrame *f = frame_new(ctx, co, NULL, 0); + if (f == NULL) { + break; + } + f->locals[0] = owner; + f->func = func; + new_frame = PyJitRef_WrapInvalid(f); stack_pointer[-1] = new_frame; break; } - /* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 */ + case _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME: { + JitOptRef owner; + JitOptRef new_frame; + owner = stack_pointer[-1]; + uint32_t func_version = (uint32_t)this_instr->operand0; + PyObject *getattribute = (PyObject *)this_instr->operand1; + PyFunctionObject *func = (PyFunctionObject *)getattribute; + if (sym_get_type_version(owner) == 0 || + func->func_version != func_version) { + ctx->contradiction = true; + ctx->done = true; + break; + } + _Py_BloomFilter_Add(dependencies, func); + PyCodeObject *co = (PyCodeObject *)func->func_code; + _Py_UOpsAbstractFrame *f = frame_new(ctx, co, NULL, 0); + if (f == NULL) { + break; + } + PyObject *name = get_co_name(ctx, oparg >> 1); + f->locals[0] = owner; + f->locals[1] = sym_new_const(ctx, name); + f->func = func; + new_frame = PyJitRef_WrapInvalid(f); + stack_pointer[-1] = new_frame; + break; + } case _GUARD_DORV_NO_DICT: { break; } case _STORE_ATTR_INSTANCE_VALUE: { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef owner; + JitOptRef value; + JitOptRef o; + owner = stack_pointer[-1]; + value = stack_pointer[-2]; + uint16_t offset = (uint16_t)this_instr->operand0; + (void)offset; + (void)value; + o = owner; + CHECK_STACK_BOUNDS(-1); + stack_pointer[-2] = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _STORE_ATTR_WITH_HINT: { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + case _LOCK_OBJECT: { break; } - case _STORE_ATTR_SLOT: { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + case _STORE_ATTR_WITH_HINT: { + JitOptRef owner; + JitOptRef value; + JitOptRef o; + owner = stack_pointer[-1]; + value = stack_pointer[-2]; + uint16_t hint = (uint16_t)this_instr->operand0; + (void)hint; + (void)value; + o = owner; + CHECK_STACK_BOUNDS(-1); + stack_pointer[-2] = o; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _COMPARE_OP: { - JitOptRef res; - if (oparg & 16) { - res = sym_new_type(ctx, &PyBool_Type); - } - else { - res = _Py_uop_sym_new_not_null(ctx); - } - stack_pointer[-2] = res; + case _STORE_ATTR_SLOT: { + JitOptRef owner; + JitOptRef value; + JitOptRef o; + owner = stack_pointer[-1]; + value = stack_pointer[-2]; + uint16_t index = (uint16_t)this_instr->operand0; + (void)index; + (void)value; + o = owner; + CHECK_STACK_BOUNDS(-1); + stack_pointer[-2] = o; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _COMPARE_OP_FLOAT: { + case _COMPARE_OP: { + JitOptRef right; + JitOptRef left; JitOptRef res; - res = sym_new_type(ctx, &PyBool_Type); + right = stack_pointer[-1]; + left = stack_pointer[-2]; + if ( + sym_is_safe_const(ctx, left) && + sym_is_safe_const(ctx, right) + ) { + JitOptRef left_sym = left; + JitOptRef right_sym = right; + _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); + _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); + _PyStackRef res_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert((oparg >> 5) <= Py_GE); + PyObject *res_o = PyObject_RichCompare(left_o, right_o, oparg >> 5); + if (res_o == NULL) { + goto error; + } + if (oparg & 16) { + int res_bool = PyObject_IsTrue(res_o); + Py_DECREF(res_o); + if (res_bool < 0) { + goto error; + } + res_stackref = res_bool ? PyStackRef_True : PyStackRef_False; + } + else { + res_stackref = PyStackRef_FromPyObjectSteal(res_o); + } + /* End of uop copied from bytecodes for constant evaluation */ + res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _POP_TOP + _POP_TOP + _LOAD_CONST_INLINE_BORROW since we have two inputs and an immortal result + ADD_OP(_POP_TOP, 0, 0); + ADD_OP(_POP_TOP, 0, 0); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + } + } + CHECK_STACK_BOUNDS(-1); + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + if (oparg & 16) { + res = sym_new_type(ctx, &PyBool_Type); + } + else { + res = _Py_uop_sym_new_not_null(ctx); + } + CHECK_STACK_BOUNDS(-1); stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _COMPARE_OP_INT: { + case _COMPARE_OP_FLOAT: { JitOptRef right; JitOptRef left; JitOptRef res; + JitOptRef l; + JitOptRef r; right = stack_pointer[-1]; left = stack_pointer[-2]; - if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) { - assert(PyLong_CheckExact(sym_get_const(ctx, left))); - assert(PyLong_CheckExact(sym_get_const(ctx, right))); - PyObject *tmp = PyObject_RichCompare(sym_get_const(ctx, left), - sym_get_const(ctx, right), - oparg >> 5); - if (tmp == NULL) { - goto error; + int cmp_mask = oparg & (COMPARE_LT_MASK | COMPARE_GT_MASK | COMPARE_EQ_MASK); + if (cmp_mask == COMPARE_EQ_MASK) { + res = sym_new_predicate(ctx, left, right, JIT_PRED_EQ); + } + else if (cmp_mask == (COMPARE_LT_MASK | COMPARE_GT_MASK)) { + res = sym_new_predicate(ctx, left, right, JIT_PRED_NE); + } + else { + res = sym_new_type(ctx, &PyBool_Type); + } + l = left; + r = right; + if ( + sym_is_safe_const(ctx, left) && + sym_is_safe_const(ctx, right) + ) { + JitOptRef left_sym = left; + JitOptRef right_sym = right; + _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); + _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); + _PyStackRef res_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + STAT_INC(COMPARE_OP, hit); + double dleft = PyFloat_AS_DOUBLE(left_o); + double dright = PyFloat_AS_DOUBLE(right_o); + int sign_ish = COMPARISON_BIT(dleft, dright); + l_stackref = left; + r_stackref = right; + res_stackref = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; + /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; + res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } } - assert(PyBool_Check(tmp)); - assert(_Py_IsImmortal(tmp)); - REPLACE_OP(this_instr, _POP_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)tmp); - res = sym_new_const(ctx, tmp); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - Py_DECREF(tmp); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _COMPARE_OP_INT: { + JitOptRef right; + JitOptRef left; + JitOptRef res; + JitOptRef l; + JitOptRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + int cmp_mask = oparg & (COMPARE_LT_MASK | COMPARE_GT_MASK | COMPARE_EQ_MASK); + if (cmp_mask == COMPARE_EQ_MASK) { + res = sym_new_predicate(ctx, left, right, JIT_PRED_EQ); + } + else if (cmp_mask == (COMPARE_LT_MASK | COMPARE_GT_MASK)) { + res = sym_new_predicate(ctx, left, right, JIT_PRED_NE); } else { res = sym_new_type(ctx, &PyBool_Type); - stack_pointer += -1; } - stack_pointer[-1] = res; + l = left; + r = right; + if ( + sym_is_safe_const(ctx, left) && + sym_is_safe_const(ctx, right) + ) { + JitOptRef left_sym = left; + JitOptRef right_sym = right; + _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); + _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); + _PyStackRef res_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(_PyLong_IsCompact((PyLongObject *)left_o)); + assert(_PyLong_IsCompact((PyLongObject *)right_o)); + STAT_INC(COMPARE_OP, hit); + assert(_PyLong_DigitCount((PyLongObject *)left_o) <= 1 && + _PyLong_DigitCount((PyLongObject *)right_o) <= 1); + Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left_o); + Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o); + int sign_ish = COMPARISON_BIT(ileft, iright); + l_stackref = left; + r_stackref = right; + res_stackref = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False; + /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; + res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _COMPARE_OP_STR: { + JitOptRef right; + JitOptRef left; JitOptRef res; + JitOptRef l; + JitOptRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; res = sym_new_type(ctx, &PyBool_Type); + l = left; + r = right; + if ( + sym_is_safe_const(ctx, left) && + sym_is_safe_const(ctx, right) + ) { + JitOptRef left_sym = left; + JitOptRef right_sym = right; + _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); + _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); + _PyStackRef res_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + STAT_INC(COMPARE_OP, hit); + int eq = _PyUnicode_Equal(left_o, right_o); + assert((oparg >> 5) == Py_EQ || (oparg >> 5) == Py_NE); + l_stackref = left; + r_stackref = right; + assert(eq == 0 || eq == 1); + assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS); + assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS); + res_stackref = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? PyStackRef_True : PyStackRef_False; + /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; + res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = res; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _IS_OP: { + JitOptRef right; + JitOptRef left; JitOptRef b; - b = sym_new_type(ctx, &PyBool_Type); + JitOptRef l; + JitOptRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; + b = sym_new_predicate(ctx, left, right, (oparg ? JIT_PRED_IS_NOT : JIT_PRED_IS)); + l = left; + r = right; + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = b; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _CONTAINS_OP: { + JitOptRef right; + JitOptRef left; JitOptRef b; + JitOptRef l; + JitOptRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; b = sym_new_type(ctx, &PyBool_Type); + l = left; + r = right; + if ( + sym_is_safe_const(ctx, left) && + sym_is_safe_const(ctx, right) + ) { + JitOptRef left_sym = left; + JitOptRef right_sym = right; + _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); + _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); + _PyStackRef b_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + int res = PySequence_Contains(right_o, left_o); + if (res < 0) { + JUMP_TO_LABEL(error); + } + b_stackref = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l_stackref = left; + r_stackref = right; + /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; + b = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(b_stackref)); + if (sym_is_const(ctx, b)) { + PyObject *result = sym_get_const(ctx, b); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = b; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = b; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _GUARD_TOS_ANY_SET: { JitOptRef tos; tos = stack_pointer[-1]; - if (sym_matches_type(tos, &PySet_Type) || - sym_matches_type(tos, &PyFrozenSet_Type)) - { - REPLACE_OP(this_instr, _NOP, 0, 0); + PyTypeObject *tp = sym_get_type(tos); + if (tp == &PySet_Type || tp == &PyFrozenSet_Type) { + ADD_OP(_NOP, 0, 0); + } + else { + tp = sym_get_probable_type(tos); + if (tp == &PySet_Type) { + ADD_OP(_GUARD_TOS_SET, 0, 0); + } + else if (tp == &PyFrozenSet_Type) { + ADD_OP(_GUARD_TOS_FROZENSET, 0, 0); + } + } + break; + } + + case _GUARD_TOS_SET: { + JitOptRef tos; + tos = stack_pointer[-1]; + if (sym_matches_type(tos, &PySet_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PySet_Type); + } + break; + } + + case _GUARD_TOS_FROZENSET: { + JitOptRef tos; + tos = stack_pointer[-1]; + if (sym_matches_type(tos, &PyFrozenSet_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(tos, &PyFrozenSet_Type); } break; } case _CONTAINS_OP_SET: { + JitOptRef right; + JitOptRef left; JitOptRef b; + JitOptRef l; + JitOptRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; b = sym_new_type(ctx, &PyBool_Type); + l = left; + r = right; + if (sym_is_not_container(left) && + sym_matches_type(right, &PyFrozenSet_Type)) { + if ( + sym_is_safe_const(ctx, left) && + sym_is_safe_const(ctx, right) + ) { + JitOptRef left_sym = left; + JitOptRef right_sym = right; + _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); + _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); + _PyStackRef b_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyAnySet_CheckExact(right_o)); + STAT_INC(CONTAINS_OP, hit); + int res = _PySet_Contains((PySetObject *)right_o, left_o); + if (res < 0) { + JUMP_TO_LABEL(error); + } + b_stackref = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l_stackref = left; + r_stackref = right; + /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; + b = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(b_stackref)); + if (sym_is_const(ctx, b)) { + PyObject *result = sym_get_const(ctx, b); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = b; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + } + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = b; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _CONTAINS_OP_DICT: { + JitOptRef right; + JitOptRef left; JitOptRef b; + JitOptRef l; + JitOptRef r; + right = stack_pointer[-1]; + left = stack_pointer[-2]; b = sym_new_type(ctx, &PyBool_Type); + l = left; + r = right; + if (sym_is_not_container(left) && + sym_matches_type(right, &PyFrozenDict_Type)) { + if ( + sym_is_safe_const(ctx, left) && + sym_is_safe_const(ctx, right) + ) { + JitOptRef left_sym = left; + JitOptRef right_sym = right; + _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); + _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); + _PyStackRef b_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; + /* Start of uop copied from bytecodes for constant evaluation */ + PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + assert(PyAnyDict_CheckExact(right_o)); + STAT_INC(CONTAINS_OP, hit); + int res = PyDict_Contains(right_o, left_o); + if (res < 0) { + JUMP_TO_LABEL(error); + } + b_stackref = (res ^ oparg) ? PyStackRef_True : PyStackRef_False; + l_stackref = left; + r_stackref = right; + /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; + b = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(b_stackref)); + if (sym_is_const(ctx, b)) { + PyObject *result = sym_get_const(ctx, b); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } + } + CHECK_STACK_BOUNDS(1); + stack_pointer[-2] = b; + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + } + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = b; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1714,18 +3442,20 @@ case _IMPORT_NAME: { JitOptRef res; res = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(-1); stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _IMPORT_FROM: { JitOptRef res; res = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1734,75 +3464,106 @@ /* _POP_JUMP_IF_TRUE is not a viable micro-op for tier 2 */ case _IS_NONE: { + JitOptRef value; JitOptRef b; - b = sym_new_not_null(ctx); + value = stack_pointer[-1]; + if (sym_is_const(ctx, value)) { + PyObject *value_o = sym_get_const(ctx, value); + b = sym_new_const(ctx, Py_IsNone(value_o) ? Py_True : Py_False); + } + else { + b = sym_new_type(ctx, &PyBool_Type); + } stack_pointer[-1] = b; break; } + /* _JUMP_BACKWARD_NO_INTERRUPT is not a viable micro-op for tier 2 */ + case _GET_LEN: { JitOptRef obj; JitOptRef len; obj = stack_pointer[-1]; - int tuple_length = sym_tuple_length(obj); + Py_ssize_t tuple_length = sym_tuple_length(obj); if (tuple_length == -1) { len = sym_new_type(ctx, &PyLong_Type); } else { assert(tuple_length >= 0); - PyObject *temp = PyLong_FromLong(tuple_length); + PyObject *temp = PyLong_FromSsize_t(tuple_length); if (temp == NULL) { goto error; } if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); } len = sym_new_const(ctx, temp); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = len; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); Py_DECREF(temp); stack_pointer += -1; } + CHECK_STACK_BOUNDS(1); stack_pointer[0] = len; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _MATCH_CLASS: { + JitOptRef names; + JitOptRef type; + JitOptRef subject; JitOptRef attrs; + JitOptRef s; + JitOptRef tp; + JitOptRef n; + names = stack_pointer[-1]; + type = stack_pointer[-2]; + subject = stack_pointer[-3]; attrs = sym_new_not_null(ctx); + s = subject; + tp = type; + n = names; + CHECK_STACK_BOUNDS(1); stack_pointer[-3] = attrs; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-2] = s; + stack_pointer[-1] = tp; + stack_pointer[0] = n; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _MATCH_MAPPING: { JitOptRef res; res = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _MATCH_SEQUENCE: { JitOptRef res; res = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _MATCH_KEYS: { JitOptRef values_or_none; values_or_none = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = values_or_none; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1811,42 +3572,210 @@ JitOptRef iter; JitOptRef index_or_null; iterable = stack_pointer[-1]; - if (sym_matches_type(iterable, &PyTuple_Type) || sym_matches_type(iterable, &PyList_Type)) { + bool is_coro = false; + bool is_trad = false; + bool definite = true; + PyTypeObject *tp = sym_get_type(iterable); + if (tp == NULL) { + definite = false; + tp = sym_get_probable_type(iterable); + } + if (oparg == GET_ITER_YIELD_FROM_NO_CHECK) { + if (tp == &PyCoro_Type) { + if (!definite) { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(iterable, tp); + } + ADD_OP(_PUSH_NULL, 0, 0); + is_coro = true; + } + } + if (tp != NULL && + tp->_tp_iteritem == NULL && + tp->tp_iter != NULL && + tp->tp_iter != PyObject_SelfIter && + tp->tp_flags & Py_TPFLAGS_IMMUTABLETYPE + ) { + assert(tp != &PyCoro_Type); + is_trad = true; + if (!definite) { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(iterable, tp); + } + ADD_OP(_GET_ITER_TRAD, 0, 0); + } + if (is_coro) { + assert(!is_trad); iter = iterable; - index_or_null = sym_new_not_null(ctx); + index_or_null = sym_new_null(ctx); + } + else if (is_trad) { + iter = sym_new_not_null(ctx); + index_or_null = sym_new_null(ctx); } else { iter = sym_new_not_null(ctx); index_or_null = sym_new_unknown(ctx); } + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = iter; stack_pointer[0] = index_or_null; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _GUARD_ITERATOR: { + JitOptRef iterable; + iterable = stack_pointer[-1]; + bool definite = true; + PyTypeObject *tp = sym_get_type(iterable); + if (tp == NULL) { + definite = false; + tp = sym_get_probable_type(iterable); + } + if (tp != NULL && tp->tp_iter == PyObject_SelfIter) { + if (definite) { + ADD_OP(_NOP, 0, 0); + } + else { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(iterable, tp); + } + } + break; + } + + case _GUARD_ITER_VIRTUAL: { + JitOptRef iterable; + iterable = stack_pointer[-1]; + bool definite = true; + PyTypeObject *tp = sym_get_type(iterable); + if (tp == NULL) { + definite = false; + tp = sym_get_probable_type(iterable); + } + if (tp != NULL && tp->_tp_iteritem != NULL) { + if (definite) { + ADD_OP(_NOP, 0, 0); + } + else { + ADD_OP(_GUARD_TYPE, 0, (uintptr_t)tp); + sym_set_type(iterable, tp); + } + } + break; + } + + case _PUSH_TAGGED_ZERO: { + JitOptRef zero; + zero = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[0] = zero; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _GET_YIELD_FROM_ITER: { + case _GET_ITER_TRAD: { JitOptRef iter; + JitOptRef index_or_null; iter = sym_new_not_null(ctx); + index_or_null = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = iter; + stack_pointer[0] = index_or_null; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } /* _FOR_ITER is not a viable micro-op for tier 2 */ case _FOR_ITER_TIER_TWO: { + JitOptRef iter; JitOptRef next; + iter = stack_pointer[-2]; + bool definite = true; + PyTypeObject *type = sym_get_type(iter); + if (type == NULL) { + type = sym_get_probable_type(iter); + definite = false; + } + if (type != NULL && type != &PyGen_Type && type->tp_iternext != NULL + && !_PyType_HasSlotTpIternext(type)) { + PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type); + _Py_BloomFilter_Add(dependencies, type); + if (!definite) { + sym_set_type(iter, type); + assert((this_instr - 1)->opcode == _RECORD_NOS_TYPE); + int32_t orig_target = (this_instr - 1)->target; + ADD_OP(_GUARD_TYPE_ITER, 0, (uintptr_t)type); + uop_buffer_last(&ctx->out_buffer)->target = orig_target; + } + ADD_OP(_ITER_NEXT_INLINE, 0, (uintptr_t)type->tp_iternext); + } next = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = next; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - /* _INSTRUMENTED_FOR_ITER is not a viable micro-op for tier 2 */ - - case _ITER_CHECK_LIST: { + case _GUARD_TYPE_ITER: { + break; + } + + case _ITER_NEXT_INLINE: { + JitOptRef next; + next = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[0] = next; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _GUARD_NOS_ITER_VIRTUAL: { + break; + } + + case _GUARD_TOS_NOT_NULL: { + JitOptRef null_or_index; + null_or_index = stack_pointer[-1]; + if (sym_is_not_null(null_or_index)) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + else { + sym_set_non_null(null_or_index); + } + break; + } + + /* _FOR_ITER_VIRTUAL is not a viable micro-op for tier 2 */ + + case _FOR_ITER_VIRTUAL_TIER_TWO: { + JitOptRef next; + next = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); + stack_pointer[0] = next; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + /* _INSTRUMENTED_FOR_ITER is not a viable micro-op for tier 2 */ + + case _ITER_CHECK_LIST: { + JitOptRef iter; + iter = stack_pointer[-2]; + if (sym_matches_type(iter, &PyList_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(iter, &PyList_Type); + } break; } @@ -1861,9 +3790,10 @@ case _ITER_NEXT_LIST_TIER_TWO: { JitOptRef next; next = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = next; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1871,7 +3801,7 @@ JitOptRef iter; iter = stack_pointer[-2]; if (sym_matches_type(iter, &PyTuple_Type)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } sym_set_type(iter, &PyTuple_Type); break; @@ -1886,13 +3816,22 @@ case _ITER_NEXT_TUPLE: { JitOptRef next; next = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = next; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _ITER_CHECK_RANGE: { + JitOptRef iter; + iter = stack_pointer[-2]; + if (sym_matches_type(iter, &PyRangeIter_Type)) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_type(iter, &PyRangeIter_Type); + } break; } @@ -1905,19 +3844,29 @@ case _ITER_NEXT_RANGE: { JitOptRef next; next = sym_new_type(ctx, &PyLong_Type); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = next; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _FOR_ITER_GEN_FRAME: { + JitOptRef iter; JitOptRef gen_frame; - gen_frame = PyJitRef_NULL; - ctx->done = true; + iter = stack_pointer[-2]; + _Py_UOpsAbstractFrame *new_frame = frame_new_from_symbol(ctx, iter, NULL, 0); + if (new_frame == NULL) { + ctx->done = true; + break; + } + new_frame->stack_pointer[0] = sym_new_const(ctx, Py_None); + new_frame->stack_pointer++; + gen_frame = PyJitRef_WrapInvalid(new_frame); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = gen_frame; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1928,25 +3877,50 @@ method_and_self = &stack_pointer[-1]; method_and_self[0] = sym_new_null(ctx); method_and_self[1] = self; + CHECK_STACK_BOUNDS(1); stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _LOAD_SPECIAL: { JitOptRef *method_and_self; method_and_self = &stack_pointer[-2]; - method_and_self[0] = sym_new_not_null(ctx); - method_and_self[1] = sym_new_unknown(ctx); + bool optimized = false; + PyTypeObject *type = sym_get_probable_type(method_and_self[1]); + if (type != NULL) { + PyObject *name = _Py_SpecialMethods[oparg].name; + PyObject *descr = _PyType_Lookup(type, name); + if (descr != NULL && (Py_TYPE(descr)->tp_flags & Py_TPFLAGS_METHOD_DESCRIPTOR)) { + _PyUOpInstruction *insert_null = uop_buffer_last(&ctx->out_buffer); + assert(insert_null->opcode == _INSERT_NULL); + assert(insert_null->target == this_instr->target); + REPLACE_OP(insert_null, _GUARD_TYPE_VERSION, 0, type->tp_version_tag); + ADD_OP(_INSERT_NULL, 0, 0); + bool immortal = _Py_IsImmortal(descr) || (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE); + ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE, + 0, (uintptr_t)descr); + ADD_OP(_SWAP, 3, 0); + optimize_pop_top(ctx, this_instr, method_and_self[0]); + watch_type(type, dependencies); + method_and_self[0] = sym_new_const(ctx, descr); + optimized = true; + } + } + if (!optimized) { + method_and_self[0] = sym_new_not_null(ctx); + method_and_self[1] = sym_new_unknown(ctx); + } break; } case _WITH_EXCEPT_START: { JitOptRef res; res = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1955,10 +3929,11 @@ JitOptRef new_exc; prev_exc = sym_new_not_null(ctx); new_exc = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = prev_exc; stack_pointer[0] = new_exc; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1966,10 +3941,6 @@ break; } - case _GUARD_KEYS_VERSION: { - break; - } - case _LOAD_ATTR_METHOD_WITH_VALUES: { JitOptRef owner; JitOptRef attr; @@ -1978,15 +3949,15 @@ PyObject *descr = (PyObject *)this_instr->operand0; (void)descr; PyTypeObject *type = sym_get_type(owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _LOAD_CONST_UNDER_INLINE_BORROW, - _LOAD_CONST_UNDER_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _NOP, _SWAP); self = owner; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; stack_pointer[0] = self; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -1998,15 +3969,15 @@ PyObject *descr = (PyObject *)this_instr->operand0; (void)descr; PyTypeObject *type = sym_get_type(owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _LOAD_CONST_UNDER_INLINE_BORROW, - _LOAD_CONST_UNDER_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _NOP, _SWAP); self = owner; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; stack_pointer[0] = self; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2017,10 +3988,9 @@ PyObject *descr = (PyObject *)this_instr->operand0; (void)descr; PyTypeObject *type = sym_get_type(owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _POP_TOP, _NOP); stack_pointer[-1] = attr; break; } @@ -2032,10 +4002,9 @@ PyObject *descr = (PyObject *)this_instr->operand0; (void)descr; PyTypeObject *type = sym_get_type(owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _POP_TOP, _NOP); stack_pointer[-1] = attr; break; } @@ -2052,15 +4021,15 @@ PyObject *descr = (PyObject *)this_instr->operand0; (void)descr; PyTypeObject *type = sym_get_type(owner); - PyObject *name = PyTuple_GET_ITEM(co->co_names, oparg >> 1); - attr = lookup_attr(ctx, this_instr, type, name, - _LOAD_CONST_UNDER_INLINE_BORROW, - _LOAD_CONST_UNDER_INLINE); + PyObject *name = get_co_name(ctx, oparg >> 1); + attr = lookup_attr(ctx, dependencies, this_instr, type, name, + _NOP, _SWAP); self = owner; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; stack_pointer[0] = self; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2084,18 +4053,14 @@ /* _MONITOR_CALL is not a viable micro-op for tier 2 */ case _PY_FRAME_GENERAL: { + JitOptRef callable; JitOptRef new_frame; - PyCodeObject *co = NULL; - assert((this_instr + 2)->opcode == _PUSH_FRAME); - co = get_code_with_logging((this_instr + 2)); - if (co == NULL) { - ctx->done = true; - break; - } - new_frame = PyJitRef_Wrap((JitOptSymbol *)frame_new(ctx, co, 0, NULL, 0)); + callable = stack_pointer[-2 - oparg]; + new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, NULL, 0)); + CHECK_STACK_BOUNDS(-1 - oparg); stack_pointer[-2 - oparg] = new_frame; stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2103,12 +4068,14 @@ JitOptRef callable; callable = stack_pointer[-2 - oparg]; uint32_t func_version = (uint32_t)this_instr->operand0; - if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyFunction_Type)) { - assert(PyFunction_Check(sym_get_const(ctx, callable))); - REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); - this_instr->operand1 = (uintptr_t)sym_get_const(ctx, callable); + PyObject *func = sym_get_probable_value(callable); + if (func == NULL || !PyFunction_Check(func) || ((PyFunctionObject *)func)->func_version != func_version) { + ctx->contradiction = true; + ctx->done = true; + break; } - sym_set_type(callable, &PyFunction_Type); + sym_set_const(callable, func); + _Py_BloomFilter_Add(dependencies, func); break; } @@ -2123,27 +4090,61 @@ if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); assert(PyMethod_Check(method)); - REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version); - this_instr->operand1 = (uintptr_t)method->im_func; + ADD_OP(_CHECK_FUNCTION_VERSION_INLINE, 0, func_version); + uop_buffer_last(&ctx->out_buffer)->operand1 = (uintptr_t)method->im_func; + } + else { + PyObject *bound_method = sym_get_probable_value(callable); + if (bound_method != NULL && Py_TYPE(bound_method) == &PyMethod_Type) { + PyMethodObject *method = (PyMethodObject *)bound_method; + PyObject *func = method->im_func; + if (PyFunction_Check(func) && + ((PyFunctionObject *)func)->func_version == func_version) { + _Py_BloomFilter_Add(dependencies, func); + sym_set_const(callable, bound_method); + } + } } sym_set_type(callable, &PyMethod_Type); break; } case _EXPAND_METHOD: { + JitOptRef self_or_null; + JitOptRef callable; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { + PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); + callable = sym_new_const(ctx, method->im_func); + self_or_null = sym_new_const(ctx, method->im_self); + } + else { + callable = sym_new_not_null(ctx); + self_or_null = sym_new_not_null(ctx); + } + stack_pointer[-2 - oparg] = callable; + stack_pointer[-1 - oparg] = self_or_null; break; } case _CHECK_IS_NOT_PY_CALLABLE: { + JitOptRef callable; + callable = stack_pointer[-2 - oparg]; + PyTypeObject *type = sym_get_type(callable); + if (type && type != &PyFunction_Type && type != &PyMethod_Type) { + ADD_OP(_NOP, 0, 0); + } break; } case _CALL_NON_PY_GENERAL: { JitOptRef res; res = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(-1 - oparg); stack_pointer[-2 - oparg] = res; stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2162,8 +4163,18 @@ JitOptRef callable; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; - callable = sym_new_not_null(ctx); - self_or_null = sym_new_not_null(ctx); + PyObject *bound_method = sym_get_probable_value(callable); + if (bound_method != NULL && Py_TYPE(bound_method) == &PyMethod_Type) { + PyMethodObject *method = (PyMethodObject *)bound_method; + callable = sym_new_not_null(ctx); + sym_set_recorded_value(callable, method->im_func); + self_or_null = sym_new_not_null(ctx); + sym_set_recorded_value(self_or_null, method->im_self); + } + else { + callable = sym_new_not_null(ctx); + self_or_null = sym_new_not_null(ctx); + } stack_pointer[-2 - oparg] = callable; stack_pointer[-1 - oparg] = self_or_null; break; @@ -2171,7 +4182,7 @@ case _CHECK_PEP_523: { if (_PyInterpreterState_GET()->eval_frame == NULL) { - REPLACE_OP(this_instr, _NOP, 0 ,0); + ADD_OP(_NOP, 0 ,0); } break; } @@ -2186,8 +4197,8 @@ if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) { PyFunctionObject *func = (PyFunctionObject *)sym_get_const(ctx, callable); PyCodeObject *co = (PyCodeObject *)func->func_code; - if (co->co_argcount == oparg + !sym_is_null(self_or_null)) { - REPLACE_OP(this_instr, _NOP, 0 ,0); + if (co->co_argcount == oparg + sym_is_not_null(self_or_null)) { + ADD_OP(_NOP, 0 ,0); } } } @@ -2195,8 +4206,14 @@ } case _CHECK_STACK_SPACE: { - assert(corresponding_check_stack == NULL); - corresponding_check_stack = this_instr; + JitOptRef callable; + callable = stack_pointer[-2 - oparg]; + PyCodeObject *co = sym_get_probable_func_code(callable); + if (co == NULL) { + ctx->done = true; + break; + } + ADD_OP(_CHECK_STACK_SPACE_OPERAND, 0, co->co_framesize); break; } @@ -2207,17 +4224,12 @@ case _INIT_CALL_PY_EXACT_ARGS: { JitOptRef *args; JitOptRef self_or_null; + JitOptRef callable; JitOptRef new_frame; args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; int argcount = oparg; - PyCodeObject *co = NULL; - assert((this_instr + 2)->opcode == _PUSH_FRAME); - co = get_code_with_logging((this_instr + 2)); - if (co == NULL) { - ctx->done = true; - break; - } assert(!PyJitRef_IsNull(self_or_null)); assert(args != NULL); if (sym_is_not_null(self_or_null)) { @@ -2225,45 +4237,31 @@ argcount++; } if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) { - new_frame = PyJitRef_Wrap((JitOptSymbol *)frame_new(ctx, co, 0, args, argcount)); + new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, args, argcount)); } else { - new_frame = PyJitRef_Wrap((JitOptSymbol *)frame_new(ctx, co, 0, NULL, 0)); + new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, NULL, 0)); } + CHECK_STACK_BOUNDS(-1 - oparg); stack_pointer[-2 - oparg] = new_frame; stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _PUSH_FRAME: { JitOptRef new_frame; new_frame = stack_pointer[-1]; + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); - ctx->frame->stack_pointer = stack_pointer; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + if (!CURRENT_FRAME_IS_INIT_SHIM()) { + ctx->frame->stack_pointer = stack_pointer; + } + ctx->frame->caller = true; ctx->frame = (_Py_UOpsAbstractFrame *)PyJitRef_Unwrap(new_frame); ctx->curr_frame_depth++; stack_pointer = ctx->frame->stack_pointer; - co = get_code(this_instr); - if (co == NULL) { - ctx->done = true; - break; - } - int framesize = co->co_framesize; - assert(framesize > 0); - curr_space += framesize; - if (curr_space < 0 || curr_space > INT32_MAX) { - ctx->done = true; - break; - } - max_space = curr_space > max_space ? curr_space : max_space; - if (first_valid_check_stack == NULL) { - first_valid_check_stack = corresponding_check_stack; - } - else if (corresponding_check_stack) { - corresponding_check_stack->opcode = _NOP; - } - corresponding_check_stack = NULL; + assert(ctx->frame->locals != NULL); break; } @@ -2271,19 +4269,11 @@ JitOptRef null; null = stack_pointer[-2]; if (sym_is_null(null)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); } - sym_set_null(null); - break; - } - - case _GUARD_NOS_NOT_NULL: { - JitOptRef nos; - nos = stack_pointer[-2]; - if (sym_is_not_null(nos)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + else { + sym_set_null(null); } - sym_set_non_null(nos); break; } @@ -2291,9 +4281,11 @@ JitOptRef null; null = stack_pointer[-3]; if (sym_is_null(null)) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_null(null); } - sym_set_null(null); break; } @@ -2301,28 +4293,41 @@ JitOptRef callable; callable = stack_pointer[-3]; if (sym_get_const(ctx, callable) == (PyObject *)&PyType_Type) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, (PyObject *)&PyType_Type); } - sym_set_const(callable, (PyObject *)&PyType_Type); break; } case _CALL_TYPE_1: { JitOptRef arg; + JitOptRef null; + JitOptRef callable; JitOptRef res; + JitOptRef a; arg = stack_pointer[-1]; + null = stack_pointer[-2]; + callable = stack_pointer[-3]; PyObject* type = (PyObject *)sym_get_type(arg); if (type) { res = sym_new_const(ctx, type); - REPLACE_OP(this_instr, _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, 0, - (uintptr_t)type); + ADD_OP(_SWAP, 3, 0); + optimize_pop_top(ctx, this_instr, callable); + optimize_pop_top(ctx, this_instr, null); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)type); + ADD_OP(_SWAP, 2, 0); } else { res = sym_new_not_null(ctx); } + a = arg; + CHECK_STACK_BOUNDS(-1); stack_pointer[-3] = res; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-2] = a; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2330,25 +4335,31 @@ JitOptRef callable; callable = stack_pointer[-3]; if (sym_get_const(ctx, callable) == (PyObject *)&PyUnicode_Type) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, (PyObject *)&PyUnicode_Type); } - sym_set_const(callable, (PyObject *)&PyUnicode_Type); break; } case _CALL_STR_1: { JitOptRef arg; JitOptRef res; + JitOptRef a; arg = stack_pointer[-1]; if (sym_matches_type(arg, &PyUnicode_Type)) { - res = arg; + res = PyJitRef_StripReferenceInfo(arg); } else { res = sym_new_type(ctx, &PyUnicode_Type); } + a = arg; + CHECK_STACK_BOUNDS(-1); stack_pointer[-3] = res; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-2] = a; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2356,94 +4367,232 @@ JitOptRef callable; callable = stack_pointer[-3]; if (sym_get_const(ctx, callable) == (PyObject *)&PyTuple_Type) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, (PyObject *)&PyTuple_Type); } - sym_set_const(callable, (PyObject *)&PyTuple_Type); break; } case _CALL_TUPLE_1: { JitOptRef arg; JitOptRef res; + JitOptRef a; arg = stack_pointer[-1]; if (sym_matches_type(arg, &PyTuple_Type)) { - res = arg; + res = PyJitRef_StripReferenceInfo(arg); } else { res = sym_new_type(ctx, &PyTuple_Type); } + a = arg; + CHECK_STACK_BOUNDS(-1); stack_pointer[-3] = res; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-2] = a; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _CHECK_AND_ALLOCATE_OBJECT: { - JitOptRef *args; + case _CHECK_OBJECT: { JitOptRef self_or_null; JitOptRef callable; - args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; uint32_t type_version = (uint32_t)this_instr->operand0; - (void)type_version; - (void)args; - callable = sym_new_not_null(ctx); - self_or_null = sym_new_not_null(ctx); + PyObject *probable_callable = sym_get_probable_value(callable); + assert(probable_callable != NULL); + PyObject *const_callable = sym_get_const(ctx, callable); + bool is_probable = const_callable == NULL && probable_callable != NULL; + PyObject *callable_o = const_callable != NULL ? const_callable : probable_callable; + if (sym_is_null(self_or_null) && + callable_o != NULL && + PyType_Check(callable_o) && + ((PyTypeObject *)callable_o)->tp_version_tag == type_version) { + if (!is_probable) { + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, callable_o); + } + PyHeapTypeObject *cls = (PyHeapTypeObject *)callable_o; + PyObject *init = cls->_spec_cache.init; + assert(init != NULL); + assert(PyFunction_Check(init)); + callable = sym_new_const(ctx, init); + stack_pointer[-2 - oparg] = callable; + watch_type((PyTypeObject *)callable_o, dependencies); + } + else { + callable = sym_new_not_null(ctx); + } stack_pointer[-2 - oparg] = callable; + break; + } + + case _ALLOCATE_OBJECT: { + JitOptRef self_or_null; + self_or_null = stack_pointer[-1 - oparg]; + self_or_null = sym_new_not_null(ctx); stack_pointer[-1 - oparg] = self_or_null; break; } case _CREATE_INIT_FRAME: { + JitOptRef *args; + JitOptRef self; + JitOptRef init; JitOptRef init_frame; - init_frame = PyJitRef_NULL; - ctx->done = true; + args = &stack_pointer[-oparg]; + self = stack_pointer[-1 - oparg]; + init = stack_pointer[-2 - oparg]; + ctx->frame->stack_pointer = stack_pointer - oparg - 2; + _Py_UOpsAbstractFrame *shim = frame_new(ctx, (PyCodeObject *)&_Py_InitCleanup, NULL, 0); + if (shim == NULL) { + break; + } + shim->stack_pointer[0] = self; + shim->stack_pointer++; + assert((int)(shim->stack_pointer - shim->stack) == 1); + ctx->frame = shim; + ctx->curr_frame_depth++; + assert((this_instr + 1)->opcode == _PUSH_FRAME); + init_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, init, args-1, oparg+1)); + CHECK_STACK_BOUNDS(-1 - oparg); stack_pointer[-2 - oparg] = init_frame; stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _EXIT_INIT_CHECK: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_BUILTIN_CLASS: { + JitOptRef callable; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyType_Type)) { + PyTypeObject *tp = (PyTypeObject *)callable_o; + if (tp->tp_vectorcall != NULL) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyType_Type); + } break; } case _CALL_BUILTIN_CLASS: { - JitOptRef res; - res = sym_new_not_null(ctx); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef callable; + callable = stack_pointer[-2 - oparg]; + callable = sym_new_not_null(ctx); + stack_pointer[-2 - oparg] = callable; + break; + } + + case _GUARD_CALLABLE_BUILTIN_O: { + JitOptRef self_or_null; + JitOptRef callable; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyCFunction_Type) && + (sym_is_not_null(self_or_null) || sym_is_null(self_or_null))) { + int total_args = oparg; + if (sym_is_not_null(self_or_null)) { + total_args++; + } + if (total_args == 1 && PyCFunction_GET_FLAGS(callable_o) == METH_O) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyCFunction_Type); + } break; } case _CALL_BUILTIN_O: { + JitOptRef *args; + JitOptRef self_or_null; + JitOptRef callable; JitOptRef res; + JitOptRef c; + JitOptRef s; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; res = sym_new_not_null(ctx); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + } + else if (sym_is_null(self_or_null)) { + s = args[0]; + } + else { + s = sym_new_unknown(ctx); + } + CHECK_STACK_BOUNDS(1 - oparg); stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1 - oparg] = c; + stack_pointer[-oparg] = s; + stack_pointer += 1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_BUILTIN_FAST: { + JitOptRef callable; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyCFunction_Type)) { + if (PyCFunction_GET_FLAGS(callable_o) == METH_FASTCALL) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyCFunction_Type); + } break; } case _CALL_BUILTIN_FAST: { - JitOptRef res; - res = sym_new_not_null(ctx); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef callable; + callable = stack_pointer[-2 - oparg]; + callable = sym_new_not_null(ctx); + stack_pointer[-2 - oparg] = callable; + break; + } + + case _GUARD_CALLABLE_BUILTIN_FAST_WITH_KEYWORDS: { + JitOptRef callable; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyCFunction_Type)) { + if (PyCFunction_GET_FLAGS(callable_o) == (METH_FASTCALL | METH_KEYWORDS)) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyCFunction_Type); + } break; } case _CALL_BUILTIN_FAST_WITH_KEYWORDS: { - JitOptRef res; - res = sym_new_not_null(ctx); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef callable; + callable = stack_pointer[-2 - oparg]; + callable = sym_new_not_null(ctx); + stack_pointer[-2 - oparg] = callable; break; } @@ -2452,37 +4601,67 @@ callable = stack_pointer[-3]; PyObject *len = _PyInterpreterState_GET()->callable_cache.len; if (sym_get_const(ctx, callable) == len) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, len); } - sym_set_const(callable, len); break; } case _CALL_LEN: { JitOptRef arg; + JitOptRef null; + JitOptRef callable; JitOptRef res; + JitOptRef a; + JitOptRef c; arg = stack_pointer[-1]; + null = stack_pointer[-2]; + callable = stack_pointer[-3]; res = sym_new_type(ctx, &PyLong_Type); - int tuple_length = sym_tuple_length(arg); - if (tuple_length >= 0) { - PyObject *temp = PyLong_FromLong(tuple_length); + Py_ssize_t length = sym_tuple_length(arg); + if (length < 0 && sym_is_const(ctx, arg)) { + PyObject *const_val = sym_get_const(ctx, arg); + if (const_val != NULL) { + if (PyUnicode_CheckExact(const_val)) { + length = PyUnicode_GET_LENGTH(const_val); + } + else if (PyBytes_CheckExact(const_val)) { + length = PyBytes_GET_SIZE(const_val); + } + else if (PyFrozenDict_CheckExact(const_val)) { + length = PyDict_GET_SIZE(const_val); + } + else if (PyFrozenSet_CheckExact(const_val)) { + length = PySet_GET_SIZE(const_val); + } + } + } + if (length >= 0) { + PyObject *temp = PyLong_FromSsize_t(length); if (temp == NULL) { goto error; } if (_Py_IsImmortal(temp)) { - REPLACE_OP(this_instr, _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, - 0, (uintptr_t)temp); + ADD_OP(_SWAP, 2, 0); + optimize_pop_top(ctx, this_instr, null); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp); + ADD_OP(_SWAP, 3, 0); } res = sym_new_const(ctx, temp); + CHECK_STACK_BOUNDS(-2); stack_pointer[-3] = res; stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); Py_DECREF(temp); stack_pointer += 2; } + a = arg; + c = callable; stack_pointer[-3] = res; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-2] = a; + stack_pointer[-1] = c; break; } @@ -2491,18 +4670,24 @@ callable = stack_pointer[-4]; PyObject *isinstance = _PyInterpreterState_GET()->callable_cache.isinstance; if (sym_get_const(ctx, callable) == isinstance) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, isinstance); } - sym_set_const(callable, isinstance); break; } case _CALL_ISINSTANCE: { JitOptRef cls; JitOptRef instance; + JitOptRef null; + JitOptRef callable; JitOptRef res; cls = stack_pointer[-1]; instance = stack_pointer[-2]; + null = stack_pointer[-3]; + callable = stack_pointer[-4]; res = sym_new_type(ctx, &PyBool_Type); PyTypeObject *inst_type = sym_get_type(instance); PyTypeObject *cls_o = (PyTypeObject *)sym_get_const(ctx, cls); @@ -2512,11 +4697,16 @@ out = Py_True; } sym_set_const(res, out); - REPLACE_OP(this_instr, _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)out); + optimize_pop_top(ctx, this_instr, cls); + optimize_pop_top(ctx, this_instr, instance); + optimize_pop_top(ctx, this_instr, null); + optimize_pop_top(ctx, this_instr, callable); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)out); } + CHECK_STACK_BOUNDS(-3); stack_pointer[-4] = res; stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2525,51 +4715,329 @@ callable = stack_pointer[-3]; PyObject *list_append = _PyInterpreterState_GET()->callable_cache.list_append; if (sym_get_const(ctx, callable) == list_append) { - REPLACE_OP(this_instr, _NOP, 0, 0); + ADD_OP(_NOP, 0, 0); + } + else { + sym_set_const(callable, list_append); } - sym_set_const(callable, list_append); break; } case _CALL_LIST_APPEND: { - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + JitOptRef arg; + JitOptRef self; + JitOptRef callable; + JitOptRef none; + JitOptRef c; + JitOptRef s; + arg = stack_pointer[-1]; + self = stack_pointer[-2]; + callable = stack_pointer[-3]; + (void)(arg); + c = callable; + s = self; + none = sym_new_const(ctx, Py_None); + stack_pointer[-3] = none; + stack_pointer[-2] = c; + stack_pointer[-1] = s; + break; + } + + case _GUARD_CALLABLE_METHOD_DESCRIPTOR_O: { + JitOptRef *args; + JitOptRef self_or_null; + JitOptRef callable; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyMethodDescr_Type) && + (sym_is_not_null(self_or_null) || sym_is_null(self_or_null))) { + int total_args = oparg; + if (sym_is_not_null(self_or_null)) { + total_args++; + } + PyTypeObject *self_type = NULL; + if (sym_is_not_null(self_or_null)) { + self_type = sym_get_type(self_or_null); + } + else { + self_type = sym_get_type(args[0]); + } + PyTypeObject *d_type = ((PyMethodDescrObject *)callable_o)->d_common.d_type; + if (total_args == 2 && + ((PyMethodDescrObject *)callable_o)->d_method->ml_flags == METH_O && + self_type == d_type) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyMethodDescr_Type); + } + break; + } + + case _CALL_METHOD_DESCRIPTOR_O: { + JitOptRef *args; + JitOptRef self_or_null; + JitOptRef callable; + JitOptRef res; + JitOptRef c; + JitOptRef s; + JitOptRef a; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && Py_IS_TYPE(callable_o, &PyMethodDescr_Type) + && sym_is_not_null(self_or_null)) { + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + PyCFunction cfunc = method->d_method->ml_meth; + ADD_OP(_CALL_METHOD_DESCRIPTOR_O_INLINE, oparg + 1, (uintptr_t)cfunc); + } + res = sym_new_not_null(ctx); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + a = args[1]; + } + else { + s = sym_new_unknown(ctx); + a = sym_new_unknown(ctx); + } + CHECK_STACK_BOUNDS(2 - oparg); + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer[-oparg] = s; + stack_pointer[1 - oparg] = a; + stack_pointer += 2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _CHECK_RECURSION_LIMIT: { + if (ctx->frame->is_c_recursion_checked) { + ADD_OP(_NOP, 0, 0); + } + ctx->frame->is_c_recursion_checked = true; + break; + } + + case _CALL_METHOD_DESCRIPTOR_O_INLINE: { + JitOptRef res; + JitOptRef c; + JitOptRef s; + JitOptRef a; + res = sym_new_not_null(ctx); + c = sym_new_not_null(ctx); + s = sym_new_not_null(ctx); + a = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(3 - oparg); + stack_pointer[-1 - oparg] = res; + stack_pointer[-oparg] = c; + stack_pointer[1 - oparg] = s; + stack_pointer[2 - oparg] = a; + stack_pointer += 3 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: { + JitOptRef *args; + JitOptRef self_or_null; + JitOptRef callable; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyMethodDescr_Type) && + (sym_is_not_null(self_or_null) || sym_is_null(self_or_null))) { + int total_args = oparg; + if (sym_is_not_null(self_or_null)) { + total_args++; + } + PyTypeObject *self_type = NULL; + if (sym_is_not_null(self_or_null)) { + self_type = sym_get_type(self_or_null); + } + else { + self_type = sym_get_type(args[0]); + } + PyTypeObject *d_type = ((PyMethodDescrObject *)callable_o)->d_common.d_type; + if (total_args != 0 && + ((PyMethodDescrObject *)callable_o)->d_method->ml_flags == (METH_FASTCALL|METH_KEYWORDS) && + self_type == d_type) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyMethodDescr_Type); + } + break; + } + + case _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: { + JitOptRef self_or_null; + JitOptRef callable; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && Py_IS_TYPE(callable_o, &PyMethodDescr_Type) + && sym_is_not_null(self_or_null)) { + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + PyCFunction cfunc = method->d_method->ml_meth; + ADD_OP(_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE, oparg, (uintptr_t)cfunc); + } + callable = sym_new_not_null(ctx); + stack_pointer[-2 - oparg] = callable; + break; + } + + case _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE: { + break; + } + + case _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS: { + JitOptRef *args; + JitOptRef self_or_null; + JitOptRef callable; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyMethodDescr_Type) && + (sym_is_not_null(self_or_null) || sym_is_null(self_or_null))) { + int total_args = oparg; + if (sym_is_not_null(self_or_null)) { + total_args++; + } + PyTypeObject *self_type = NULL; + if (sym_is_not_null(self_or_null)) { + self_type = sym_get_type(self_or_null); + } + else { + self_type = sym_get_type(args[0]); + } + PyTypeObject *d_type = ((PyMethodDescrObject *)callable_o)->d_common.d_type; + if (total_args == 1 && + ((PyMethodDescrObject *)callable_o)->d_method->ml_flags == METH_NOARGS && + self_type == d_type) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyMethodDescr_Type); + } + break; + } + + case _CALL_METHOD_DESCRIPTOR_NOARGS: { + JitOptRef *args; + JitOptRef self_or_null; + JitOptRef callable; + JitOptRef res; + JitOptRef c; + JitOptRef s; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && Py_IS_TYPE(callable_o, &PyMethodDescr_Type) + && sym_is_not_null(self_or_null)) { + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + PyCFunction cfunc = method->d_method->ml_meth; + ADD_OP(_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE, oparg + 1, (uintptr_t)cfunc); + } + res = sym_new_not_null(ctx); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + } + else if (sym_is_null(self_or_null)) { + s = args[0]; + } + else { + s = sym_new_unknown(ctx); + } + CHECK_STACK_BOUNDS(1 - oparg); + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer[-oparg] = s; + stack_pointer += 1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _CALL_METHOD_DESCRIPTOR_O: { + case _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE: { JitOptRef res; + JitOptRef c; + JitOptRef s; res = sym_new_not_null(ctx); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + c = sym_new_not_null(ctx); + s = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(2 - oparg); + stack_pointer[-1 - oparg] = res; + stack_pointer[-oparg] = c; + stack_pointer[1 - oparg] = s; + stack_pointer += 2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: { - JitOptRef res; - res = sym_new_not_null(ctx); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + case _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST: { + JitOptRef *args; + JitOptRef self_or_null; + JitOptRef callable; + args = &stack_pointer[-oparg]; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && sym_matches_type(callable, &PyMethodDescr_Type) && + (sym_is_not_null(self_or_null) || sym_is_null(self_or_null))) { + int total_args = oparg; + if (sym_is_not_null(self_or_null)) { + total_args++; + } + PyTypeObject *self_type = NULL; + if (sym_is_not_null(self_or_null)) { + self_type = sym_get_type(self_or_null); + } + else { + self_type = sym_get_type(args[0]); + } + PyTypeObject *d_type = ((PyMethodDescrObject *)callable_o)->d_common.d_type; + if (total_args != 0 && + ((PyMethodDescrObject *)callable_o)->d_method->ml_flags == METH_FASTCALL && + self_type == d_type) { + ADD_OP(_NOP, 0, 0); + } + } + else { + sym_set_type(callable, &PyMethodDescr_Type); + } break; } - case _CALL_METHOD_DESCRIPTOR_NOARGS: { - JitOptRef res; - res = sym_new_not_null(ctx); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + case _CALL_METHOD_DESCRIPTOR_FAST: { + JitOptRef self_or_null; + JitOptRef callable; + self_or_null = stack_pointer[-1 - oparg]; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = sym_get_const(ctx, callable); + if (callable_o && Py_IS_TYPE(callable_o, &PyMethodDescr_Type) + && sym_is_not_null(self_or_null)) { + PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; + PyCFunction cfunc = method->d_method->ml_meth; + ADD_OP(_CALL_METHOD_DESCRIPTOR_FAST_INLINE, oparg, (uintptr_t)cfunc); + } + callable = sym_new_not_null(ctx); + stack_pointer[-2 - oparg] = callable; break; } - case _CALL_METHOD_DESCRIPTOR_FAST: { - JitOptRef res; - res = sym_new_not_null(ctx); - stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + case _CALL_METHOD_DESCRIPTOR_FAST_INLINE: { break; } @@ -2582,37 +5050,94 @@ /* _DO_CALL_KW is not a viable micro-op for tier 2 */ case _PY_FRAME_KW: { + JitOptRef callable; JitOptRef new_frame; - new_frame = PyJitRef_NULL; - ctx->done = true; + callable = stack_pointer[-3 - oparg]; + new_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, callable, NULL, 0)); + CHECK_STACK_BOUNDS(-2 - oparg); stack_pointer[-3 - oparg] = new_frame; stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _CHECK_FUNCTION_VERSION_KW: { + JitOptRef callable; + callable = stack_pointer[-3 - oparg]; + uint32_t func_version = (uint32_t)this_instr->operand0; + PyObject *func = sym_get_probable_value(callable); + if (func == NULL || !PyFunction_Check(func) || ((PyFunctionObject *)func)->func_version != func_version) { + ctx->contradiction = true; + ctx->done = true; + break; + } + sym_set_const(callable, func); + _Py_BloomFilter_Add(dependencies, func); break; } case _CHECK_METHOD_VERSION_KW: { + JitOptRef callable; + callable = stack_pointer[-3 - oparg]; + uint32_t func_version = (uint32_t)this_instr->operand0; + if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { + PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); + assert(PyMethod_Check(method)); + ADD_OP(_CHECK_FUNCTION_VERSION_INLINE, 0, func_version); + uop_buffer_last(&ctx->out_buffer)->operand1 = (uintptr_t)method->im_func; + } + else { + PyObject *bound_method = sym_get_probable_value(callable); + if (bound_method != NULL && Py_TYPE(bound_method) == &PyMethod_Type) { + PyMethodObject *method = (PyMethodObject *)bound_method; + PyObject *func = method->im_func; + if (PyFunction_Check(func) && + ((PyFunctionObject *)func)->func_version == func_version) { + _Py_BloomFilter_Add(dependencies, func); + sym_set_const(callable, bound_method); + } + } + } + sym_set_type(callable, &PyMethod_Type); break; } case _EXPAND_METHOD_KW: { + JitOptRef self_or_null; + JitOptRef callable; + self_or_null = stack_pointer[-2 - oparg]; + callable = stack_pointer[-3 - oparg]; + if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyMethod_Type)) { + PyMethodObject *method = (PyMethodObject *)sym_get_const(ctx, callable); + callable = sym_new_const(ctx, method->im_func); + self_or_null = sym_new_const(ctx, method->im_self); + } + else { + callable = sym_new_not_null(ctx); + self_or_null = sym_new_not_null(ctx); + } + stack_pointer[-3 - oparg] = callable; + stack_pointer[-2 - oparg] = self_or_null; break; } case _CHECK_IS_NOT_PY_CALLABLE_KW: { + JitOptRef callable; + callable = stack_pointer[-3 - oparg]; + PyTypeObject *type = sym_get_type(callable); + if (type && type != &PyFunction_Type && type != &PyMethod_Type) { + ADD_OP(_NOP, 0, 0); + } break; } case _CALL_KW_NON_PY: { JitOptRef res; res = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(-2 - oparg); stack_pointer[-3 - oparg] = res; stack_pointer += -2 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2622,50 +5147,96 @@ /* _DO_CALL_FUNCTION_EX is not a viable micro-op for tier 2 */ + case _CHECK_IS_PY_CALLABLE_EX: { + break; + } + + case _PY_FRAME_EX: { + JitOptRef func_st; + JitOptRef ex_frame; + func_st = stack_pointer[-4]; + ex_frame = PyJitRef_WrapInvalid(frame_new_from_symbol(ctx, func_st, NULL, 0)); + CHECK_STACK_BOUNDS(-3); + stack_pointer[-4] = ex_frame; + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _CHECK_IS_NOT_PY_CALLABLE_EX: { + JitOptRef func_st; + func_st = stack_pointer[-4]; + PyTypeObject *type = sym_get_type(func_st); + if (type && type != &PyFunction_Type) { + ADD_OP(_NOP, 0, 0); + } + break; + } + + case _CALL_FUNCTION_EX_NON_PY_GENERAL: { + JitOptRef result; + result = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(-3); + stack_pointer[-4] = result; + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + case _MAKE_FUNCTION: { + JitOptRef codeobj_st; JitOptRef func; - func = sym_new_not_null(ctx); + JitOptRef co; + codeobj_st = stack_pointer[-1]; + func = sym_new_type(ctx, &PyFunction_Type); + co = codeobj_st; + CHECK_STACK_BOUNDS(1); stack_pointer[-1] = func; + stack_pointer[0] = co; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _SET_FUNCTION_ATTRIBUTE: { JitOptRef func_out; func_out = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(-1); stack_pointer[-2] = func_out; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _RETURN_GENERATOR: { JitOptRef res; ctx->frame->stack_pointer = stack_pointer; - frame_pop(ctx); + assert(this_instr[1].opcode == _RECORD_CODE); + PyCodeObject *returning_code = (PyCodeObject *)this_instr[1].operand0; + if (returning_code == NULL) { + ctx->done = true; + break; + } + assert(PyCode_Check(returning_code)); + if (frame_pop(ctx, returning_code)) { + break; + } stack_pointer = ctx->frame->stack_pointer; res = sym_new_unknown(ctx); - assert(corresponding_check_stack == NULL); - assert(co != NULL); - int framesize = co->co_framesize; - assert(framesize > 0); - assert(framesize <= curr_space); - curr_space -= framesize; + CHECK_STACK_BOUNDS(1); stack_pointer[0] = res; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - co = get_code(this_instr); - if (co == NULL) { - ctx->done = true; - } + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _BUILD_SLICE: { JitOptRef slice; slice = sym_new_type(ctx, &PySlice_Type); + CHECK_STACK_BOUNDS(1 - oparg); stack_pointer[-oparg] = slice; stack_pointer += 1 - oparg; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2686,9 +5257,10 @@ case _FORMAT_WITH_SPEC: { JitOptRef res; res = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(-1); stack_pointer[-2] = res; stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2697,10 +5269,13 @@ JitOptRef top; bottom = stack_pointer[-1 - (oparg-1)]; assert(oparg > 0); + bottom = PyJitRef_RemoveUnique(bottom); top = bottom; + CHECK_STACK_BOUNDS(1); + stack_pointer[-1 - (oparg-1)] = bottom; stack_pointer[0] = top; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2708,8 +5283,12 @@ JitOptRef rhs; JitOptRef lhs; JitOptRef res; + JitOptRef l; + JitOptRef r; rhs = stack_pointer[-1]; lhs = stack_pointer[-2]; + l = lhs; + r = rhs; if ( sym_is_safe_const(ctx, lhs) && sym_is_safe_const(ctx, rhs) @@ -2719,6 +5298,8 @@ _PyStackRef lhs = sym_get_const_as_stackref(ctx, lhs_sym); _PyStackRef rhs = sym_get_const_as_stackref(ctx, rhs_sym); _PyStackRef res_stackref; + _PyStackRef l_stackref; + _PyStackRef r_stackref; /* Start of uop copied from bytecodes for constant evaluation */ PyObject *lhs_o = PyStackRef_AsPyObjectBorrow(lhs); PyObject *rhs_o = PyStackRef_AsPyObjectBorrow(rhs); @@ -2728,18 +5309,74 @@ JUMP_TO_LABEL(error); } res_stackref = PyStackRef_FromPyObjectSteal(res_o); + l_stackref = lhs; + r_stackref = rhs; /* End of uop copied from bytecodes for constant evaluation */ + (void)l_stackref; + (void)r_stackref; res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); + if (sym_is_const(ctx, res)) { + PyObject *result = sym_get_const(ctx, res); + if (_Py_IsImmortal(result)) { + // Replace with _LOAD_CONST_INLINE_BORROW + _RROT_3 since we have two inputs and an immortal result + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + ADD_OP(_RROT_3, 0, 0); + } + } + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } bool lhs_int = sym_matches_type(lhs, &PyLong_Type); bool rhs_int = sym_matches_type(rhs, &PyLong_Type); bool lhs_float = sym_matches_type(lhs, &PyFloat_Type); bool rhs_float = sym_matches_type(rhs, &PyFloat_Type); - if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) { + bool is_truediv = (oparg == NB_TRUE_DIVIDE + || oparg == NB_INPLACE_TRUE_DIVIDE); + bool is_remainder = (oparg == NB_REMAINDER + || oparg == NB_INPLACE_REMAINDER); + int emit_op = _BINARY_OP; + if (is_truediv || is_remainder) { + if (!sym_has_type(rhs) + && sym_get_probable_type(rhs) == &PyFloat_Type) { + ADD_OP(_GUARD_TOS_FLOAT, 0, 0); + sym_set_type(rhs, &PyFloat_Type); + rhs_float = true; + } + if (!sym_has_type(lhs) + && sym_get_probable_type(lhs) == &PyFloat_Type) { + ADD_OP(_GUARD_NOS_FLOAT, 0, 0); + sym_set_type(lhs, &PyFloat_Type); + lhs_float = true; + } + } + if (is_truediv && lhs_float && rhs_float) { + if (PyJitRef_IsUnique(lhs)) { + emit_op = _BINARY_OP_TRUEDIV_FLOAT_INPLACE; + l = sym_new_null(ctx); + r = rhs; + } + else if (PyJitRef_IsUnique(rhs)) { + emit_op = _BINARY_OP_TRUEDIV_FLOAT_INPLACE_RIGHT; + l = lhs; + r = sym_new_null(ctx); + } + else { + emit_op = _BINARY_OP_TRUEDIV_FLOAT; + l = lhs; + r = rhs; + } + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); + } + else if (is_truediv + && (lhs_int || lhs_float) && (rhs_int || rhs_float)) { + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); + } + else if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) { res = sym_new_unknown(ctx); } else if (oparg == NB_POWER || oparg == NB_INPLACE_POWER) { @@ -2747,30 +5384,31 @@ res = sym_new_unknown(ctx); } else if (lhs_float) { - res = sym_new_type(ctx, &PyFloat_Type); + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); } else if (!sym_is_const(ctx, rhs)) { res = sym_new_unknown(ctx); } else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(ctx, rhs))) { - res = sym_new_type(ctx, &PyFloat_Type); + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); } else { res = sym_new_type(ctx, &PyLong_Type); } } - else if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) { - res = sym_new_type(ctx, &PyFloat_Type); - } else if (lhs_int && rhs_int) { res = sym_new_type(ctx, &PyLong_Type); } else { - res = sym_new_type(ctx, &PyFloat_Type); + res = PyJitRef_MakeUnique(sym_new_type(ctx, &PyFloat_Type)); } + ADD_OP(emit_op, oparg, 0); + CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + stack_pointer[-1] = l; + stack_pointer[0] = r; + stack_pointer += 1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2809,28 +5447,64 @@ case _GUARD_IS_TRUE_POP: { JitOptRef flag; flag = stack_pointer[-1]; + sym_apply_predicate_narrowing(ctx, flag, true); if (sym_is_const(ctx, flag)) { PyObject *value = sym_get_const(ctx, flag); assert(value != NULL); - eliminate_pop_guard(this_instr, value != Py_True); + eliminate_pop_guard(this_instr, ctx, value != Py_True); + } + else { + int bit = get_test_bit_for_bools(); + if (bit) { + REPLACE_OP(this_instr, + test_bit_set_in_true(bit) ? + _GUARD_BIT_IS_SET_POP : + _GUARD_BIT_IS_UNSET_POP, bit, 0); + } } sym_set_const(flag, Py_True); + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _GUARD_IS_FALSE_POP: { JitOptRef flag; flag = stack_pointer[-1]; + sym_apply_predicate_narrowing(ctx, flag, false); if (sym_is_const(ctx, flag)) { PyObject *value = sym_get_const(ctx, flag); assert(value != NULL); - eliminate_pop_guard(this_instr, value != Py_False); + eliminate_pop_guard(this_instr, ctx, value != Py_False); + } + else { + int bit = get_test_bit_for_bools(); + if (bit) { + REPLACE_OP(this_instr, + test_bit_set_in_true(bit) ? + _GUARD_BIT_IS_UNSET_POP : + _GUARD_BIT_IS_SET_POP, bit, 0); + } } sym_set_const(flag, Py_False); + CHECK_STACK_BOUNDS(-1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_SET_POP: { + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + break; + } + + case _GUARD_BIT_IS_UNSET_POP: { + CHECK_STACK_BOUNDS(-1); + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2840,15 +5514,16 @@ if (sym_is_const(ctx, val)) { PyObject *value = sym_get_const(ctx, val); assert(value != NULL); - eliminate_pop_guard(this_instr, !Py_IsNone(value)); + eliminate_pop_guard(this_instr, ctx, !Py_IsNone(value)); } else if (sym_has_type(val)) { assert(!sym_matches_type(val, &_PyNone_Type)); - eliminate_pop_guard(this_instr, true); + eliminate_pop_guard(this_instr, ctx, true); } sym_set_const(val, Py_None); + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2858,14 +5533,15 @@ if (sym_is_const(ctx, val)) { PyObject *value = sym_get_const(ctx, val); assert(value != NULL); - eliminate_pop_guard(this_instr, Py_IsNone(value)); + eliminate_pop_guard(this_instr, ctx, Py_IsNone(value)); } else if (sym_has_type(val)) { assert(!sym_matches_type(val, &_PyNone_Type)); - eliminate_pop_guard(this_instr, false); + eliminate_pop_guard(this_instr, ctx, false); } + CHECK_STACK_BOUNDS(-1); stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2881,7 +5557,6 @@ case _CHECK_STACK_SPACE_OPERAND: { uint32_t framesize = (uint32_t)this_instr->operand0; (void)framesize; - Py_UNREACHABLE(); break; } @@ -2896,6 +5571,10 @@ break; } + case _DYNAMIC_EXIT: { + break; + } + case _CHECK_VALIDITY: { break; } @@ -2904,17 +5583,10 @@ JitOptRef value; PyObject *ptr = (PyObject *)this_instr->operand0; value = sym_new_const(ctx, ptr); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); - break; - } - - case _POP_TOP_LOAD_CONST_INLINE: { - JitOptRef value; - PyObject *ptr = (PyObject *)this_instr->operand0; - value = sym_new_const(ctx, ptr); - stack_pointer[-1] = value; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -2922,126 +5594,237 @@ JitOptRef value; PyObject *ptr = (PyObject *)this_instr->operand0; value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); + CHECK_STACK_BOUNDS(1); stack_pointer[0] = value; stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } - case _POP_CALL: { - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + case _RROT_3: { + JitOptRef top; + JitOptRef middle; + JitOptRef bottom; + top = stack_pointer[-1]; + middle = stack_pointer[-2]; + bottom = stack_pointer[-3]; + JitOptRef temp = top; + top = middle; + middle = bottom; + bottom = temp; + stack_pointer[-3] = bottom; + stack_pointer[-2] = middle; + stack_pointer[-1] = top; break; } - case _POP_CALL_ONE: { - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + case _START_EXECUTOR: { break; } - case _POP_CALL_TWO: { - stack_pointer += -4; - assert(WITHIN_STACK_BOUNDS()); + case _MAKE_WARM: { break; } - case _POP_TOP_LOAD_CONST_INLINE_BORROW: { - JitOptRef value; - PyObject *ptr = (PyObject *)this_instr->operand0; - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); - stack_pointer[-1] = value; + case _FATAL_ERROR: { break; } - case _POP_TWO_LOAD_CONST_INLINE_BORROW: { - JitOptRef value; - value = sym_new_not_null(ctx); - stack_pointer[-2] = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _DEOPT: { + ctx->done = true; break; } - case _POP_CALL_LOAD_CONST_INLINE_BORROW: { - JitOptRef value; - PyObject *ptr = (PyObject *)this_instr->operand0; - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); - stack_pointer[-2] = value; - stack_pointer += -1; - assert(WITHIN_STACK_BOUNDS()); + case _HANDLE_PENDING_AND_DEOPT: { break; } - case _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW: { - JitOptRef value; - PyObject *ptr = (PyObject *)this_instr->operand0; - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); - stack_pointer[-3] = value; - stack_pointer += -2; - assert(WITHIN_STACK_BOUNDS()); + case _ERROR_POP_N: { break; } - case _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW: { - JitOptRef value; - PyObject *ptr = (PyObject *)this_instr->operand0; - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); - stack_pointer[-4] = value; - stack_pointer += -3; - assert(WITHIN_STACK_BOUNDS()); + case _SPILL_OR_RELOAD: { break; } - case _LOAD_CONST_UNDER_INLINE: { - JitOptRef value; - JitOptRef new; - value = sym_new_not_null(ctx); - new = sym_new_not_null(ctx); - stack_pointer[-1] = value; - stack_pointer[0] = new; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _TIER2_RESUME_CHECK: { break; } - case _LOAD_CONST_UNDER_INLINE_BORROW: { - JitOptRef value; - JitOptRef new; - value = sym_new_not_null(ctx); - new = sym_new_not_null(ctx); - stack_pointer[-1] = value; - stack_pointer[0] = new; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); + case _COLD_EXIT: { break; } - case _CHECK_FUNCTION: { + case _COLD_DYNAMIC_EXIT: { break; } - case _START_EXECUTOR: { + case _GUARD_CODE_VERSION__PUSH_FRAME: { + uint32_t version = (uint32_t)this_instr->operand0; + PyCodeObject *co = get_current_code_object(ctx); + if (co->co_version == version) { + _Py_BloomFilter_Add(dependencies, co); + PyFunctionObject *func = (PyFunctionObject *)sym_get_const(ctx, ctx->frame->callable); + if (func != NULL && func->func_version == version) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + } + else { + ctx->done = true; + } break; } - case _MAKE_WARM: { + case _GUARD_CODE_VERSION_YIELD_VALUE: { + uint32_t version = (uint32_t)this_instr->operand0; + (void)version; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } break; } - case _FATAL_ERROR: { + case _GUARD_CODE_VERSION_RETURN_VALUE: { + uint32_t version = (uint32_t)this_instr->operand0; + (void)version; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } break; } - case _DEOPT: { + case _GUARD_CODE_VERSION_RETURN_GENERATOR: { + uint32_t version = (uint32_t)this_instr->operand0; + (void)version; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } break; } - case _ERROR_POP_N: { + case _GUARD_IP__PUSH_FRAME: { + PyObject *ip = (PyObject *)this_instr->operand0; + (void)ip; + stack_pointer = sym_set_stack_depth((int)this_instr->operand1, stack_pointer); + PyFunctionObject *func = (PyFunctionObject *)sym_get_const(ctx, ctx->frame->callable); + if (func != NULL && func->func_version != 0 && + // We can remove this guard for simple function call targets. + (((PyCodeObject *)ctx->frame->func->func_code)->co_flags & + (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) == 0) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } break; } - case _TIER2_RESUME_CHECK: { + case _GUARD_IP_YIELD_VALUE: { + PyObject *ip = (PyObject *)this_instr->operand0; + (void)ip; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + stack_pointer = sym_set_stack_depth((int)this_instr->operand1, stack_pointer); + break; + } + + case _GUARD_IP_RETURN_VALUE: { + PyObject *ip = (PyObject *)this_instr->operand0; + (void)ip; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + stack_pointer = sym_set_stack_depth((int)this_instr->operand1, stack_pointer); + break; + } + + case _GUARD_IP_RETURN_GENERATOR: { + PyObject *ip = (PyObject *)this_instr->operand0; + (void)ip; + if (ctx->frame->caller) { + REPLACE_OP(this_instr, _NOP, 0, 0); + } + stack_pointer = sym_set_stack_depth((int)this_instr->operand1, stack_pointer); + break; + } + + case _RECORD_TOS: { + JitOptRef tos; + tos = stack_pointer[-1]; + sym_set_recorded_value(tos, (PyObject *)this_instr->operand0); + break; + } + + case _RECORD_TOS_TYPE: { + JitOptRef tos; + tos = stack_pointer[-1]; + PyTypeObject *tp = (PyTypeObject *)this_instr->operand0; + sym_set_recorded_type(tos, tp); + break; + } + + case _RECORD_NOS: { + JitOptRef nos; + nos = stack_pointer[-2]; + sym_set_recorded_value(nos, (PyObject *)this_instr->operand0); + break; + } + + case _RECORD_NOS_TYPE: { + JitOptRef nos; + nos = stack_pointer[-2]; + PyTypeObject *tp = (PyTypeObject *)this_instr->operand0; + sym_set_recorded_type(nos, tp); + break; + } + + case _RECORD_NOS_GEN_FUNC: { + JitOptRef nos; + nos = stack_pointer[-2]; + PyFunctionObject *func = (PyFunctionObject *)this_instr->operand0; + assert(func == NULL || PyFunction_Check(func)); + sym_set_recorded_gen_func(nos, func); + break; + } + + case _RECORD_3OS_GEN_FUNC: { + JitOptRef gen; + gen = stack_pointer[-3]; + PyFunctionObject *func = (PyFunctionObject *)this_instr->operand0; + assert(func == NULL || PyFunction_Check(func)); + sym_set_recorded_gen_func(gen, func); + break; + } + + case _RECORD_4OS: { + JitOptRef value; + value = stack_pointer[-4]; + sym_set_recorded_value(value, (PyObject *)this_instr->operand0); + break; + } + + case _RECORD_CALLABLE: { + JitOptRef func; + func = stack_pointer[-2 - oparg]; + sym_set_recorded_value(func, (PyObject *)this_instr->operand0); + break; + } + + case _RECORD_CALLABLE_KW: { + JitOptRef func; + func = stack_pointer[-3 - oparg]; + sym_set_recorded_value(func, (PyObject *)this_instr->operand0); + break; + } + + case _RECORD_BOUND_METHOD: { + JitOptRef callable; + callable = stack_pointer[-2 - oparg]; + sym_set_recorded_value(callable, (PyObject *)this_instr->operand0); break; } + case _RECORD_CODE: { + break; + } + + /* _TRACE_RECORD is not a viable micro-op for tier 2 */ + diff --git a/Python/optimizer_symbols.c b/Python/optimizer_symbols.c index 8a3df236c80626..79f81482d247e3 100644 --- a/Python/optimizer_symbols.c +++ b/Python/optimizer_symbols.c @@ -4,10 +4,10 @@ #include "pycore_code.h" #include "pycore_frame.h" +#include "pycore_interpframe.h" #include "pycore_long.h" #include "pycore_optimizer.h" #include "pycore_stats.h" -#include "pycore_tuple.h" // _PyTuple_FromArray() #include <stdbool.h> #include <stdint.h> @@ -26,26 +26,27 @@ state represents no information, and the BOTTOM state represents contradictory information. Though symbols logically progress through all intermediate nodes, we often skip in-between states for convenience: - UNKNOWN - | | -NULL | -| | <- Anything below this level is an object. -| NON_NULL-+ -| | | <- Anything below this level has a known type version. -| TYPE_VERSION | -| | | <- Anything below this level has a known type. -| KNOWN_CLASS | -| | | | | | -| | | INT* | | -| | | | | | <- Anything below this level has a known truthiness. -| | | | | TRUTHINESS -| | | | | | -| TUPLE | | | | -| | | | | | <- Anything below this level is a known constant. -| KNOWN_VALUE--+ -| | <- Anything below this level is unreachable. + UNKNOWN---------------------+------+ + | | | | +NULL | | RECORDED_VALUE* +| | | | <- Anything below this level is an object. +| NON_NULL---------+ | | +| | | | | <- Anything below this level has a known type version. +| TYPE_VERSION | | | +| | | | | <- Anything below this level has a known type. +| KNOWN_CLASS--+ | | | +| | | | | PREDICATE RECORDED_VALUE(known type) +| | | INT* | | | +| | | | | | | <- Anything below this level has a known truthiness. +| | | | | | | +| TUPLE | | TRUTHINESS | | +| | | | | | | <- Anything below this level is a known constant. +| KNOWN_VALUE--+-------+----+------+ +| | <- Anything below this level is unreachable. BOTTOM + + For example, after guarding that the type of an UNKNOWN local is int, we can narrow the symbol to KNOWN_CLASS (logically progressing though NON_NULL and TYPE_VERSION to get there). Later, we may learn that it is falsey based on the @@ -55,6 +56,7 @@ the same symbol, that would be a contradiction, and the symbol would be set to BOTTOM (indicating that the code is unreachable). INT* is a limited range int, currently a "compact" int. +RECORDED_VALUE* includes RECORDED_TYPE and RECORDED_GEN_FUNC */ @@ -69,6 +71,70 @@ static inline int get_lltrace(void) { } #define DPRINTF(level, ...) \ if (get_lltrace() >= (level)) { printf(__VA_ARGS__); } + +void +_PyUOpSymPrint(JitOptRef ref) +{ + if (PyJitRef_IsNull(ref)) { + printf("<JitRef NULL>"); + return; + } + if (PyJitRef_IsInvalid(ref)) { + printf("<INVALID frame at %p>", (void *)PyJitRef_Unwrap(ref)); + return; + } + JitOptSymbol *sym = PyJitRef_Unwrap(ref); + JitSymType tag = sym->tag; + switch (tag) { + case JIT_SYM_UNKNOWN_TAG: + printf("<? at %p>", (void *)sym); + break; + case JIT_SYM_NULL_TAG: + printf("<NULL at %p>", (void *)sym); + break; + case JIT_SYM_NON_NULL_TAG: + printf("<!NULL at %p>", (void *)sym); + break; + case JIT_SYM_BOTTOM_TAG: + printf("<BOTTOM at %p>", (void *)sym); + break; + case JIT_SYM_TYPE_VERSION_TAG: + printf("<v%u at %p>", sym->version.version, (void *)sym); + break; + case JIT_SYM_KNOWN_CLASS_TAG: + printf("<%s at %p>", sym->cls.type->tp_name, (void *)sym); + break; + case JIT_SYM_KNOWN_VALUE_TAG: + printf("<%s val=%p at %p>", Py_TYPE(sym->value.value)->tp_name, + (void *)sym->value.value, (void *)sym); + break; + case JIT_SYM_TUPLE_TAG: + printf("<tuple[%d] at %p>", sym->tuple.length, (void *)sym); + break; + case JIT_SYM_TRUTHINESS_TAG: + printf("<truthiness%s at %p>", sym->truthiness.invert ? "!" : "", (void *)sym); + break; + case JIT_SYM_COMPACT_INT: + printf("<compact_int at %p>", (void *)sym); + break; + case JIT_SYM_PREDICATE_TAG: + printf("<predicate at %p>", (void *)sym); + break; + case JIT_SYM_RECORDED_VALUE_TAG: + printf("<recorded value %p>", sym->recorded_value.value); + break; + case JIT_SYM_RECORDED_TYPE_TAG: + printf("<recorded type %s>", sym->recorded_type.type->tp_name); + break; + case JIT_SYM_RECORDED_GEN_FUNC_TAG: + printf("<recorded gen func at %p>", (void *)sym); + break; + default: + printf("<tag=%d at %p>", tag, (void *)sym); + break; + } +} + #else #define DPRINTF(level, ...) #endif @@ -213,6 +279,22 @@ _Py_uop_sym_is_safe_const(JitOptContext *ctx, JitOptRef sym) return (typ == &PyUnicode_Type) || (typ == &PyFloat_Type) || (typ == &_PyNone_Type) || + (typ == &PyBool_Type) || + (typ == &PyFrozenDict_Type) || + (typ == &PyFrozenSet_Type); +} + +bool +_Py_uop_sym_is_not_container(JitOptRef sym) +{ + PyTypeObject *typ = _Py_uop_sym_get_type(sym); + if (typ == NULL) { + return false; + } + return (typ == &PyLong_Type) || + (typ == &PyFloat_Type) || + (typ == &PyUnicode_Type) || + (typ == &_PyNone_Type) || (typ == &PyBool_Type); } @@ -251,14 +333,38 @@ _Py_uop_sym_set_type(JitOptContext *ctx, JitOptRef ref, PyTypeObject *typ) sym_set_bottom(ctx, sym); } return; + case JIT_SYM_RECORDED_GEN_FUNC_TAG: + if (typ != &PyGen_Type) { + sym_set_bottom(ctx, sym); + } + return; case JIT_SYM_BOTTOM_TAG: return; + case JIT_SYM_RECORDED_VALUE_TAG: + if (Py_TYPE(sym->recorded_value.value) == typ) { + sym->recorded_value.known_type = true; + } + else { + sym_set_bottom(ctx, sym); + } + return; + case JIT_SYM_RECORDED_TYPE_TAG: + if (sym->recorded_type.type == typ) { + sym->tag = JIT_SYM_KNOWN_CLASS_TAG; + sym->cls.version = 0; + sym->cls.type = typ; + } + else { + sym_set_bottom(ctx, sym); + } + return; case JIT_SYM_NON_NULL_TAG: case JIT_SYM_UNKNOWN_TAG: sym->tag = JIT_SYM_KNOWN_CLASS_TAG; sym->cls.version = 0; sym->cls.type = typ; return; + case JIT_SYM_PREDICATE_TAG: case JIT_SYM_TRUTHINESS_TAG: if (typ != &PyBool_Type) { sym_set_bottom(ctx, sym); @@ -307,6 +413,12 @@ _Py_uop_sym_set_type_version(JitOptContext *ctx, JitOptRef ref, unsigned int ver return false; }; return true; + case JIT_SYM_RECORDED_GEN_FUNC_TAG: + if (PyGen_Type.tp_version_tag != version) { + sym_set_bottom(ctx, sym); + return false; + } + return true; case JIT_SYM_TYPE_VERSION_TAG: if (sym->version.version != version) { sym_set_bottom(ctx, sym); @@ -320,6 +432,7 @@ _Py_uop_sym_set_type_version(JitOptContext *ctx, JitOptRef ref, unsigned int ver sym->tag = JIT_SYM_TYPE_VERSION_TAG; sym->version.version = version; return true; + case JIT_SYM_PREDICATE_TAG: case JIT_SYM_TRUTHINESS_TAG: if (version != PyBool_Type.tp_version_tag) { sym_set_bottom(ctx, sym); @@ -332,6 +445,29 @@ _Py_uop_sym_set_type_version(JitOptContext *ctx, JitOptRef ref, unsigned int ver return false; } return true; + case JIT_SYM_RECORDED_VALUE_TAG: + if (Py_TYPE(sym->recorded_value.value)->tp_version_tag == version) { + sym->recorded_value.known_type = true; + sym->tag = JIT_SYM_KNOWN_CLASS_TAG; + sym->cls.type = Py_TYPE(sym->recorded_value.value); + sym->cls.version = version; + return true; + } + else { + sym_set_bottom(ctx, sym); + return false; + } + case JIT_SYM_RECORDED_TYPE_TAG: + if (sym->recorded_type.type->tp_version_tag == version) { + sym->tag = JIT_SYM_KNOWN_CLASS_TAG; + sym->cls.type = sym->recorded_type.type; + sym->cls.version = version; + return true; + } + else { + sym_set_bottom(ctx, sym); + return false; + } } Py_UNREACHABLE(); } @@ -343,6 +479,7 @@ _Py_uop_sym_set_const(JitOptContext *ctx, JitOptRef ref, PyObject *const_val) JitSymType tag = sym->tag; switch(tag) { case JIT_SYM_NULL_TAG: + case JIT_SYM_RECORDED_GEN_FUNC_TAG: sym_set_bottom(ctx, sym); return; case JIT_SYM_KNOWN_CLASS_TAG: @@ -382,10 +519,22 @@ _Py_uop_sym_set_const(JitOptContext *ctx, JitOptRef ref, PyObject *const_val) return; case JIT_SYM_BOTTOM_TAG: return; + case JIT_SYM_RECORDED_VALUE_TAG: + case JIT_SYM_RECORDED_TYPE_TAG: + /* The given value might contradict the recorded one, + * in which case we could return bottom. + * Just discard the recorded value for now */ case JIT_SYM_NON_NULL_TAG: case JIT_SYM_UNKNOWN_TAG: make_const(sym, const_val); return; + case JIT_SYM_PREDICATE_TAG: + if (!PyBool_Check(const_val)) { + sym_set_bottom(ctx, sym); + return; + } + make_const(sym, const_val); + return; case JIT_SYM_TRUTHINESS_TAG: if (!PyBool_Check(const_val) || (_Py_uop_sym_is_const(ctx, ref) && @@ -530,6 +679,13 @@ _Py_uop_sym_get_type(JitOptRef ref) case JIT_SYM_BOTTOM_TAG: case JIT_SYM_NON_NULL_TAG: case JIT_SYM_UNKNOWN_TAG: + case JIT_SYM_RECORDED_TYPE_TAG: + case JIT_SYM_RECORDED_GEN_FUNC_TAG: + return NULL; + case JIT_SYM_RECORDED_VALUE_TAG: + if (sym->recorded_value.known_type) { + return Py_TYPE(sym->recorded_value.value); + } return NULL; case JIT_SYM_KNOWN_CLASS_TAG: return sym->cls.type; @@ -539,11 +695,39 @@ _Py_uop_sym_get_type(JitOptRef ref) return _PyType_LookupByVersion(sym->version.version); case JIT_SYM_TUPLE_TAG: return &PyTuple_Type; + case JIT_SYM_PREDICATE_TAG: case JIT_SYM_TRUTHINESS_TAG: return &PyBool_Type; case JIT_SYM_COMPACT_INT: return &PyLong_Type; + } + Py_UNREACHABLE(); +} +PyTypeObject * +_Py_uop_sym_get_probable_type(JitOptRef ref) +{ + JitOptSymbol *sym = PyJitRef_Unwrap(ref); + JitSymType tag = sym->tag; + switch(tag) { + case JIT_SYM_NULL_TAG: + case JIT_SYM_BOTTOM_TAG: + case JIT_SYM_NON_NULL_TAG: + case JIT_SYM_UNKNOWN_TAG: + case JIT_SYM_TYPE_VERSION_TAG: + case JIT_SYM_TUPLE_TAG: + case JIT_SYM_PREDICATE_TAG: + case JIT_SYM_TRUTHINESS_TAG: + case JIT_SYM_COMPACT_INT: + case JIT_SYM_KNOWN_CLASS_TAG: + case JIT_SYM_KNOWN_VALUE_TAG: + return _Py_uop_sym_get_type(ref); + case JIT_SYM_RECORDED_GEN_FUNC_TAG: + return &PyGen_Type; + case JIT_SYM_RECORDED_VALUE_TAG: + return Py_TYPE(sym->recorded_value.value); + case JIT_SYM_RECORDED_TYPE_TAG: + return sym->recorded_type.type; } Py_UNREACHABLE(); } @@ -558,6 +742,8 @@ _Py_uop_sym_get_type_version(JitOptRef ref) case JIT_SYM_BOTTOM_TAG: case JIT_SYM_NON_NULL_TAG: case JIT_SYM_UNKNOWN_TAG: + case JIT_SYM_RECORDED_VALUE_TAG: + case JIT_SYM_RECORDED_TYPE_TAG: return 0; case JIT_SYM_TYPE_VERSION_TAG: return sym->version.version; @@ -567,14 +753,18 @@ _Py_uop_sym_get_type_version(JitOptRef ref) return Py_TYPE(sym->value.value)->tp_version_tag; case JIT_SYM_TUPLE_TAG: return PyTuple_Type.tp_version_tag; + case JIT_SYM_PREDICATE_TAG: case JIT_SYM_TRUTHINESS_TAG: return PyBool_Type.tp_version_tag; case JIT_SYM_COMPACT_INT: return PyLong_Type.tp_version_tag; + case JIT_SYM_RECORDED_GEN_FUNC_TAG: + return PyGen_Type.tp_version_tag; } Py_UNREACHABLE(); } + bool _Py_uop_sym_has_type(JitOptRef sym) { @@ -594,17 +784,79 @@ _Py_uop_sym_matches_type_version(JitOptRef sym, unsigned int version) return _Py_uop_sym_get_type_version(sym) == version; } +PyObject * +_Py_uop_sym_get_probable_value(JitOptRef ref) +{ + JitOptSymbol *sym = PyJitRef_Unwrap(ref); + JitSymType tag = sym->tag; + switch(tag) { + case JIT_SYM_NULL_TAG: + case JIT_SYM_BOTTOM_TAG: + case JIT_SYM_NON_NULL_TAG: + case JIT_SYM_UNKNOWN_TAG: + case JIT_SYM_RECORDED_TYPE_TAG: + case JIT_SYM_TYPE_VERSION_TAG: + case JIT_SYM_TUPLE_TAG: + case JIT_SYM_PREDICATE_TAG: + case JIT_SYM_TRUTHINESS_TAG: + case JIT_SYM_COMPACT_INT: + case JIT_SYM_KNOWN_CLASS_TAG: + case JIT_SYM_RECORDED_GEN_FUNC_TAG: + return NULL; + case JIT_SYM_RECORDED_VALUE_TAG: + return sym->recorded_value.value; + case JIT_SYM_KNOWN_VALUE_TAG: + return sym->value.value; + } + Py_UNREACHABLE(); +} + +PyCodeObject * +_Py_uop_sym_get_probable_func_code(JitOptRef ref) +{ + JitOptSymbol *sym = PyJitRef_Unwrap(ref); + if (sym->tag == JIT_SYM_RECORDED_GEN_FUNC_TAG) { + return (PyCodeObject *)PyFunction_GET_CODE(sym->recorded_gen_func.func); + } + PyObject *obj = _Py_uop_sym_get_probable_value(ref); + if (obj != NULL) { + if (PyFunction_Check(obj)) { + return (PyCodeObject *)PyFunction_GET_CODE(obj); + } + } + return NULL; +} + +PyFunctionObject * +_Py_uop_sym_get_probable_function(JitOptRef ref) +{ + JitOptSymbol *sym = PyJitRef_Unwrap(ref); + if (sym->tag == JIT_SYM_RECORDED_GEN_FUNC_TAG) { + return sym->recorded_gen_func.func; + } + PyObject *obj = _Py_uop_sym_get_probable_value(ref); + if (obj != NULL && PyFunction_Check(obj)) { + return (PyFunctionObject *)obj; + } + return NULL; +} + int _Py_uop_sym_truthiness(JitOptContext *ctx, JitOptRef ref) { JitOptSymbol *sym = PyJitRef_Unwrap(ref); - switch(sym->tag) { + JitSymType tag = sym->tag; + switch (tag) { case JIT_SYM_NULL_TAG: case JIT_SYM_TYPE_VERSION_TAG: case JIT_SYM_BOTTOM_TAG: case JIT_SYM_NON_NULL_TAG: case JIT_SYM_UNKNOWN_TAG: case JIT_SYM_COMPACT_INT: + case JIT_SYM_PREDICATE_TAG: + case JIT_SYM_RECORDED_VALUE_TAG: + case JIT_SYM_RECORDED_TYPE_TAG: + case JIT_SYM_RECORDED_GEN_FUNC_TAG: return -1; case JIT_SYM_KNOWN_CLASS_TAG: /* TODO : @@ -616,7 +868,7 @@ _Py_uop_sym_truthiness(JitOptContext *ctx, JitOptRef ref) case JIT_SYM_TUPLE_TAG: return sym->tuple.length != 0; case JIT_SYM_TRUTHINESS_TAG: - ; + { JitOptSymbol *value = allocation_base(ctx) + sym->truthiness.value; int truthiness = _Py_uop_sym_truthiness(ctx, PyJitRef_Wrap(value)); @@ -626,6 +878,7 @@ _Py_uop_sym_truthiness(JitOptContext *ctx, JitOptRef ref) truthiness ^= sym->truthiness.invert; make_const(sym, truthiness ? Py_True : Py_False); return truthiness; + } } PyObject *value = sym->value.value; /* Only handle a few known safe types */ @@ -667,7 +920,7 @@ _Py_uop_sym_new_tuple(JitOptContext *ctx, int size, JitOptRef *args) } JitOptRef -_Py_uop_sym_tuple_getitem(JitOptContext *ctx, JitOptRef ref, int item) +_Py_uop_sym_tuple_getitem(JitOptContext *ctx, JitOptRef ref, Py_ssize_t item) { JitOptSymbol *sym = PyJitRef_Unwrap(ref); assert(item >= 0); @@ -683,7 +936,7 @@ _Py_uop_sym_tuple_getitem(JitOptContext *ctx, JitOptRef ref, int item) return _Py_uop_sym_new_not_null(ctx); } -int +Py_ssize_t _Py_uop_sym_tuple_length(JitOptRef ref) { JitOptSymbol *sym = PyJitRef_Unwrap(ref); @@ -736,6 +989,7 @@ _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef ref) JitSymType tag = sym->tag; switch(tag) { case JIT_SYM_NULL_TAG: + case JIT_SYM_RECORDED_GEN_FUNC_TAG: sym_set_bottom(ctx, sym); return; case JIT_SYM_KNOWN_CLASS_TAG: @@ -760,12 +1014,18 @@ _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef ref) } return; case JIT_SYM_TUPLE_TAG: + case JIT_SYM_PREDICATE_TAG: case JIT_SYM_TRUTHINESS_TAG: sym_set_bottom(ctx, sym); return; case JIT_SYM_BOTTOM_TAG: case JIT_SYM_COMPACT_INT: return; + case JIT_SYM_RECORDED_VALUE_TAG: + case JIT_SYM_RECORDED_TYPE_TAG: + /* The given value might contradict the recorded one, + * in which case we could return bottom. + * Just discard the recorded value for now */ case JIT_SYM_NON_NULL_TAG: case JIT_SYM_UNKNOWN_TAG: sym->tag = JIT_SYM_COMPACT_INT; @@ -773,6 +1033,72 @@ _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef ref) } } +JitOptRef +_Py_uop_sym_new_predicate(JitOptContext *ctx, JitOptRef lhs_ref, JitOptRef rhs_ref, JitOptPredicateKind kind) +{ + JitOptSymbol *lhs = PyJitRef_Unwrap(lhs_ref); + JitOptSymbol *rhs = PyJitRef_Unwrap(rhs_ref); + + JitOptSymbol *res = sym_new(ctx); + if (res == NULL) { + return out_of_space_ref(ctx); + } + + res->tag = JIT_SYM_PREDICATE_TAG; + res->predicate.kind = kind; + res->predicate.lhs = (uint16_t)(lhs - allocation_base(ctx)); + res->predicate.rhs = (uint16_t)(rhs - allocation_base(ctx)); + + return PyJitRef_Wrap(res); +} + +void +_Py_uop_sym_apply_predicate_narrowing(JitOptContext *ctx, JitOptRef ref, bool branch_is_true) +{ + JitOptSymbol *sym = PyJitRef_Unwrap(ref); + if (sym->tag != JIT_SYM_PREDICATE_TAG) { + return; + } + + JitOptPredicate pred = sym->predicate; + + JitOptRef lhs_ref = PyJitRef_Wrap(allocation_base(ctx) + pred.lhs); + JitOptRef rhs_ref = PyJitRef_Wrap(allocation_base(ctx) + pred.rhs); + + bool lhs_is_const = _Py_uop_sym_is_const(ctx, lhs_ref); + bool rhs_is_const = _Py_uop_sym_is_const(ctx, rhs_ref); + if (!lhs_is_const && !rhs_is_const) { + return; + } + + bool narrow = false; + switch(pred.kind) { + case JIT_PRED_EQ: + case JIT_PRED_IS: + narrow = branch_is_true; + break; + case JIT_PRED_NE: + case JIT_PRED_IS_NOT: + narrow = !branch_is_true; + break; + default: + return; + } + if (!narrow) { + return; + } + + JitOptRef subject_ref = lhs_is_const ? rhs_ref : lhs_ref; + JitOptRef const_ref = lhs_is_const ? lhs_ref : rhs_ref; + + PyObject *const_val = _Py_uop_sym_get_const(ctx, const_ref); + if (const_val == NULL) { + return; + } + _Py_uop_sym_set_const(ctx, subject_ref, const_val); + assert(_Py_uop_sym_is_const(ctx, subject_ref)); +} + JitOptRef _Py_uop_sym_new_truthiness(JitOptContext *ctx, JitOptRef ref, bool truthy) { @@ -809,26 +1135,253 @@ _Py_uop_sym_new_compact_int(JitOptContext *ctx) return PyJitRef_Wrap(sym); } +void +_Py_uop_sym_set_recorded_value(JitOptContext *ctx, JitOptRef ref, PyObject *value) +{ + // It is possible for value to be NULL due to respecialization + // during execution of the traced instruction. + if (value == NULL) { + return; + } + JitOptSymbol *sym = PyJitRef_Unwrap(ref); + JitSymType tag = sym->tag; + switch(tag) { + case JIT_SYM_NULL_TAG: + sym_set_bottom(ctx, sym); + return; + case JIT_SYM_BOTTOM_TAG: + return; + case JIT_SYM_NON_NULL_TAG: + case JIT_SYM_UNKNOWN_TAG: + sym->tag = JIT_SYM_RECORDED_VALUE_TAG; + sym->recorded_value.known_type = false; + sym->recorded_value.value = value; + return; + case JIT_SYM_RECORDED_VALUE_TAG: + if (sym->recorded_value.value != value) { + sym_set_bottom(ctx, sym); + } + return; + case JIT_SYM_RECORDED_TYPE_TAG: + if (sym->recorded_type.type == Py_TYPE(value)) { + sym->tag = JIT_SYM_RECORDED_VALUE_TAG; + sym->recorded_value.known_type = false; + sym->recorded_value.value = value; + } + else { + sym_set_bottom(ctx, sym); + } + return; + case JIT_SYM_KNOWN_CLASS_TAG: + if (sym->cls.type == Py_TYPE(value)) { + sym->tag = JIT_SYM_RECORDED_VALUE_TAG; + sym->recorded_value.known_type = true; + sym->recorded_value.value = value; + } + else { + sym_set_bottom(ctx, sym); + } + return; + case JIT_SYM_KNOWN_VALUE_TAG: + return; + case JIT_SYM_TYPE_VERSION_TAG: + if (sym->version.version == Py_TYPE(value)->tp_version_tag) { + sym->tag = JIT_SYM_RECORDED_VALUE_TAG; + sym->recorded_value.known_type = true; + sym->recorded_value.value = value; + } + else { + sym_set_bottom(ctx, sym); + } + return; + // In these cases the original information is more valuable + case JIT_SYM_RECORDED_GEN_FUNC_TAG: + case JIT_SYM_TUPLE_TAG: + case JIT_SYM_PREDICATE_TAG: + case JIT_SYM_TRUTHINESS_TAG: + case JIT_SYM_COMPACT_INT: + return; + } + Py_UNREACHABLE(); +} +void +_Py_uop_sym_set_recorded_gen_func(JitOptContext *ctx, JitOptRef ref, PyFunctionObject *value) +{ + // It is possible for value to be NULL due to respecialization + // during execution of the traced instruction. + if (value == NULL) { + return; + } + assert(!PyJitRef_IsNull(ref)); + JitOptSymbol *sym = PyJitRef_Unwrap(ref); + JitSymType tag = sym->tag; + switch(tag) { + case JIT_SYM_NULL_TAG: + case JIT_SYM_RECORDED_VALUE_TAG: + case JIT_SYM_KNOWN_VALUE_TAG: + case JIT_SYM_TUPLE_TAG: + case JIT_SYM_PREDICATE_TAG: + case JIT_SYM_TRUTHINESS_TAG: + case JIT_SYM_COMPACT_INT: + case JIT_SYM_BOTTOM_TAG: + return; + case JIT_SYM_NON_NULL_TAG: + case JIT_SYM_UNKNOWN_TAG: + sym->tag = JIT_SYM_RECORDED_GEN_FUNC_TAG; + sym->recorded_gen_func.func = value; + return; + case JIT_SYM_RECORDED_TYPE_TAG: + if (sym->recorded_type.type == &PyGen_Type) { + sym->tag = JIT_SYM_RECORDED_GEN_FUNC_TAG; + sym->recorded_gen_func.func = value; + } + else { + sym_set_bottom(ctx, sym); + } + return; + case JIT_SYM_KNOWN_CLASS_TAG: + if (sym->cls.type == &PyGen_Type) { + sym->tag = JIT_SYM_RECORDED_GEN_FUNC_TAG; + sym->recorded_gen_func.func = value; + } + else { + sym_set_bottom(ctx, sym); + } + return; + case JIT_SYM_TYPE_VERSION_TAG: + if (sym->version.version == PyGen_Type.tp_version_tag) { + sym->tag = JIT_SYM_RECORDED_GEN_FUNC_TAG; + sym->recorded_gen_func.func = value; + } + else { + sym_set_bottom(ctx, sym); + } + return; + case JIT_SYM_RECORDED_GEN_FUNC_TAG: + if (sym->recorded_gen_func.func != value) { + sym_set_bottom(ctx, sym); + } + return; + } + Py_UNREACHABLE(); +} + +void +_Py_uop_sym_set_recorded_type(JitOptContext *ctx, JitOptRef ref, PyTypeObject *type) +{ + // It is possible for type to be NULL due to respecialization + // during execution of the traced instruction. + if (type == NULL) { + return; + } + assert(PyType_Check((PyObject *)type)); + JitOptSymbol *sym = PyJitRef_Unwrap(ref); + JitSymType tag = sym->tag; + switch(tag) { + case JIT_SYM_NULL_TAG: + sym_set_bottom(ctx, sym); + return; + case JIT_SYM_BOTTOM_TAG: + return; + case JIT_SYM_NON_NULL_TAG: + case JIT_SYM_UNKNOWN_TAG: + sym->tag = JIT_SYM_RECORDED_TYPE_TAG; + sym->recorded_type.type = type; + return; + case JIT_SYM_RECORDED_VALUE_TAG: + if (Py_TYPE(sym->recorded_value.value) != type) { + sym_set_bottom(ctx, sym); + } + return; + case JIT_SYM_RECORDED_TYPE_TAG: + if (sym->recorded_type.type != type) { + sym_set_bottom(ctx, sym); + } + return; + case JIT_SYM_KNOWN_CLASS_TAG: + return; + case JIT_SYM_KNOWN_VALUE_TAG: + return; + case JIT_SYM_TYPE_VERSION_TAG: + if (sym->version.version == type->tp_version_tag) { + sym->tag = JIT_SYM_KNOWN_CLASS_TAG; + sym->cls.type = type; + } + else { + sym_set_bottom(ctx, sym); + } + return; + // In these cases the original information is more valuable + case JIT_SYM_TUPLE_TAG: + case JIT_SYM_PREDICATE_TAG: + case JIT_SYM_TRUTHINESS_TAG: + case JIT_SYM_COMPACT_INT: + case JIT_SYM_RECORDED_GEN_FUNC_TAG: + return; + } + Py_UNREACHABLE(); +} + +// 0 on success, -1 on error. +_Py_UOpsAbstractFrame * +_Py_uop_frame_new_from_symbol( + JitOptContext *ctx, + JitOptRef callable, + JitOptRef *args, + int arg_len) +{ + PyCodeObject *co = _Py_uop_sym_get_probable_func_code(callable); + if (co == NULL) { + ctx->done = true; + return NULL; + } + _Py_UOpsAbstractFrame *frame = _Py_uop_frame_new(ctx, co, args, arg_len); + if (frame == NULL) { + return NULL; + } + PyFunctionObject *func = _Py_uop_sym_get_probable_function(callable); + if (func != NULL) { + assert(PyFunction_Check(func)); + frame->func = func; + } + assert(frame->stack_pointer != NULL); + frame->callable = callable; + return frame; +} + // 0 on success, -1 on error. _Py_UOpsAbstractFrame * _Py_uop_frame_new( JitOptContext *ctx, PyCodeObject *co, - int curr_stackentries, JitOptRef *args, int arg_len) { - assert(ctx->curr_frame_depth < MAX_ABSTRACT_FRAME_DEPTH); + assert(co != NULL); + if (ctx->curr_frame_depth >= MAX_ABSTRACT_FRAME_DEPTH) { + ctx->done = true; + ctx->out_of_space = true; + OPT_STAT_INC(optimizer_frame_overflow); + return NULL; + } _Py_UOpsAbstractFrame *frame = &ctx->frames[ctx->curr_frame_depth]; + frame->code = co; - frame->stack_len = co->co_stacksize; + frame->locals = ctx->locals.used; + ctx->locals.used += co->co_nlocalsplus; frame->locals_len = co->co_nlocalsplus; - frame->locals = ctx->n_consumed; - frame->stack = frame->locals + co->co_nlocalsplus; - frame->stack_pointer = frame->stack + curr_stackentries; - ctx->n_consumed = ctx->n_consumed + (co->co_nlocalsplus + co->co_stacksize); - if (ctx->n_consumed >= ctx->limit) { + frame->stack = ctx->stack.used; + ctx->stack.used += co->co_stacksize; + frame->stack_len = co->co_stacksize; + + frame->stack_pointer = frame->stack; + frame->globals_checked_version = 0; + frame->globals_watched = false; + frame->func = NULL; + frame->caller = false; + frame->is_c_recursion_checked = false; + if (ctx->locals.used > ctx->locals.end || ctx->stack.used > ctx->stack.end) { ctx->done = true; ctx->out_of_space = true; return NULL; @@ -836,24 +1389,59 @@ _Py_uop_frame_new( // Initialize with the initial state of all local variables for (int i = 0; i < arg_len; i++) { - frame->locals[i] = args[i]; + frame->locals[i] = PyJitRef_RemoveUnique(args[i]); } + // If the args are known, then it's safe to just initialize + // every other non-set local to null symbol. + bool default_null = args != NULL; + for (int i = arg_len; i < co->co_nlocalsplus; i++) { - JitOptRef local = _Py_uop_sym_new_unknown(ctx); + JitOptRef local = default_null ? _Py_uop_sym_new_null(ctx) : _Py_uop_sym_new_unknown(ctx); frame->locals[i] = local; } + frame->callable = _Py_uop_sym_new_not_null(ctx); - // Initialize the stack as well - for (int i = 0; i < curr_stackentries; i++) { - JitOptRef stackvar = _Py_uop_sym_new_unknown(ctx); - frame->stack[i] = stackvar; - } + /* Most optimizations rely on code objects being immutable (including sys._getframe modifications), + * and up to date for instrumentation. */ + _Py_BloomFilter_Add(ctx->dependencies, co); + assert(frame->locals != NULL); return frame; } +JitOptRef * +_Py_uop_sym_set_stack_depth(JitOptContext *ctx, int stack_depth, JitOptRef *current_sp) { + _Py_UOpsAbstractFrame *frame = ctx->frame; + assert(frame->stack != NULL); + JitOptRef *new_stack_pointer = frame->stack + stack_depth; + if (current_sp > new_stack_pointer) { + ctx->done = true; + ctx->contradiction = true; + return NULL; + } + if (new_stack_pointer > ctx->stack.end) { + ctx->done = true; + ctx->out_of_space = true; + return NULL; + } + int delta = (int)(new_stack_pointer - current_sp); + assert(delta >= 0); + if (delta) { + /* Shift existing stack elements up */ + for (JitOptRef *p = current_sp-1; p >= frame->stack; p--) { + p[delta] = *p; + } + /* Fill rest of stack with unknowns */ + for (int i = 0; i < delta; i++) { + frame->stack[i] = _Py_uop_sym_new_unknown(ctx); + } + } + return frame->stack_pointer = new_stack_pointer; +} + + void _Py_uop_abstractcontext_fini(JitOptContext *ctx) { @@ -870,15 +1458,24 @@ _Py_uop_abstractcontext_fini(JitOptContext *ctx) } } +// Leave a bit of space to push values before checking that there is space for a new frame +#define STACK_HEADROOM 2 + void -_Py_uop_abstractcontext_init(JitOptContext *ctx) +_Py_uop_abstractcontext_init(JitOptContext *ctx, _PyBloomFilter *dependencies) { static_assert(sizeof(JitOptSymbol) <= 3 * sizeof(uint64_t), "JitOptSymbol has grown"); - ctx->limit = ctx->locals_and_stack + MAX_ABSTRACT_INTERP_SIZE; - ctx->n_consumed = ctx->locals_and_stack; + + ctx->stack.used = ctx->stack_array; + ctx->stack.end = &ctx->stack_array[ABSTRACT_INTERP_STACK_SIZE-STACK_HEADROOM]; + ctx->locals.used = ctx->locals_array; + ctx->locals.end = &ctx->locals_array[ABSTRACT_INTERP_LOCALS_SIZE-STACK_HEADROOM]; #ifdef Py_DEBUG // Aids debugging a little. There should never be NULL in the abstract interpreter. - for (int i = 0 ; i < MAX_ABSTRACT_INTERP_SIZE; i++) { - ctx->locals_and_stack[i] = PyJitRef_NULL; + for (int i = 0 ; i < ABSTRACT_INTERP_STACK_SIZE; i++) { + ctx->stack_array[i] = PyJitRef_NULL; + } + for (int i = 0 ; i < ABSTRACT_INTERP_LOCALS_SIZE; i++) { + ctx->locals_array[i] = PyJitRef_NULL; } #endif @@ -888,16 +1485,55 @@ _Py_uop_abstractcontext_init(JitOptContext *ctx) // Frame setup ctx->curr_frame_depth = 0; + + // Ctx signals. + // Note: this must happen before frame_new, as it might override + // the result should frame_new set things to bottom. + ctx->done = false; + ctx->out_of_space = false; + ctx->contradiction = false; + ctx->builtins_watched = false; + ctx->dependencies = dependencies; } int -_Py_uop_frame_pop(JitOptContext *ctx) +_Py_uop_frame_pop(JitOptContext *ctx, PyCodeObject *co) { _Py_UOpsAbstractFrame *frame = ctx->frame; - ctx->n_consumed = frame->locals; + ctx->stack.used = frame->stack; + ctx->locals.used = frame->locals; + ctx->curr_frame_depth--; - assert(ctx->curr_frame_depth >= 1); - ctx->frame = &ctx->frames[ctx->curr_frame_depth - 1]; + + if (ctx->curr_frame_depth >= 1) { + ctx->frame = &ctx->frames[ctx->curr_frame_depth - 1]; + assert(ctx->frame->locals != NULL); + + // We returned to the correct code. Nothing to do here. + if (co == ctx->frame->code) { + return 0; + } + // Else: the code we recorded doesn't match the code we *think* we're + // returning to. We could trace anything, we can't just return to the + // old frame. We have to restore what the tracer recorded + // as the traced next frame. + // Remove the current frame, and later swap it out with the right one. + else { + ctx->curr_frame_depth--; + } + } + // Else: trace stack underflow. + + // This handles swapping out frames. + _Py_UOpsAbstractFrame *new_frame = _Py_uop_frame_new(ctx, co, NULL, 0); + if (new_frame == NULL) { + ctx->done = true; + return 1; + } + + ctx->curr_frame_depth++; + ctx->frame = new_frame; + assert(ctx->frame->locals != NULL); return 0; } @@ -916,6 +1552,9 @@ static JitOptSymbol * make_bottom(JitOptContext *ctx) { JitOptSymbol *sym = sym_new(ctx); + if (sym == NULL) { + return out_of_space(ctx); + } sym->tag = JIT_SYM_BOTTOM_TAG; return sym; } @@ -925,11 +1564,12 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) { JitOptContext context; JitOptContext *ctx = &context; - _Py_uop_abstractcontext_init(ctx); + _Py_uop_abstractcontext_init(ctx, NULL); PyObject *val_42 = NULL; PyObject *val_43 = NULL; PyObject *val_big = NULL; PyObject *tuple = NULL; + PyFunctionObject *func = NULL; // Use a single 'sym' variable so copy-pasting tests is easier. JitOptRef ref = _Py_uop_sym_new_unknown(ctx); @@ -1033,7 +1673,7 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) "tuple item does not match value used to create tuple" ); PyObject *pair[2] = { val_42, val_43 }; - tuple = _PyTuple_FromArray(pair, 2); + tuple = PyTuple_FromArray(pair, 2); ref = _Py_uop_sym_new_const(ctx, tuple); TEST_PREDICATE( _Py_uop_sym_get_const(ctx, _Py_uop_sym_tuple_getitem(ctx, ref, 1)) == val_43, @@ -1060,6 +1700,259 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) TEST_PREDICATE(_Py_uop_sym_is_const(ctx, value) == true, "value is not constant"); TEST_PREDICATE(_Py_uop_sym_get_const(ctx, value) == Py_True, "value is not True"); + // Resolving predicate result to True should narrow subject to True + JitOptRef subject = _Py_uop_sym_new_unknown(ctx); + JitOptRef const_true = _Py_uop_sym_new_const(ctx, Py_True); + if (PyJitRef_IsNull(subject) || PyJitRef_IsNull(const_true)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_true, JIT_PRED_IS); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, true); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, subject), "predicate narrowing did not const-narrow subject"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, subject) == Py_True, "predicate narrowing did not narrow subject to True"); + + // Resolving predicate result to False should not narrow subject + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_true, JIT_PRED_IS); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, false); + TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, subject), "predicate narrowing incorrectly narrowed subject"); + + // Resolving inverted predicate to False should narrow subject to True + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_true, JIT_PRED_IS_NOT); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, false); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, subject), "predicate narrowing (inverted) did not const-narrow subject"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, subject) == Py_True, "predicate narrowing (inverted) did not narrow subject to True"); + + // Resolving inverted predicate to True should not narrow subject + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_true, JIT_PRED_IS_NOT); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, true); + TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, subject), "predicate narrowing incorrectly narrowed subject (inverted/true)"); + + // Test narrowing subject to None + subject = _Py_uop_sym_new_unknown(ctx); + JitOptRef const_none = _Py_uop_sym_new_const(ctx, Py_None); + if (PyJitRef_IsNull(subject) || PyJitRef_IsNull(const_none)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_none, JIT_PRED_IS); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, true); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, subject), "predicate narrowing did not const-narrow subject (None)"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, subject) == Py_None, "predicate narrowing did not narrow subject to None"); + + // Test narrowing subject to numerical constant from is comparison + subject = _Py_uop_sym_new_unknown(ctx); + PyObject *one_obj = PyLong_FromLong(1); + JitOptRef const_one = _Py_uop_sym_new_const(ctx, one_obj); + if (PyJitRef_IsNull(subject) || one_obj == NULL || PyJitRef_IsNull(const_one)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_one, JIT_PRED_IS); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, true); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, subject), "predicate narrowing did not const-narrow subject (1)"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, subject) == one_obj, "predicate narrowing did not narrow subject to 1"); + + // Test narrowing subject to constant from EQ predicate for int + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_one, JIT_PRED_EQ); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, true); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, subject), "predicate narrowing did not const-narrow subject (1)"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, subject) == one_obj, "predicate narrowing did not narrow subject to 1"); + + // Resolving EQ predicate to False should not narrow subject for int + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_one, JIT_PRED_EQ); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, false); + TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, subject), "predicate narrowing incorrectly narrowed subject (inverted/true)"); + + // Test narrowing subject to constant from NE predicate for int + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_one, JIT_PRED_NE); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, false); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, subject), "predicate narrowing did not const-narrow subject (1)"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, subject) == one_obj, "predicate narrowing did not narrow subject to 1"); + + // Resolving NE predicate to true should not narrow subject for int + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_one, JIT_PRED_NE); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, true); + TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, subject), "predicate narrowing incorrectly narrowed subject (inverted/true)"); + + // Test narrowing subject to constant from EQ predicate for float + subject = _Py_uop_sym_new_unknown(ctx); + PyObject *float_tenth_obj = PyFloat_FromDouble(0.1); + JitOptRef const_float_tenth = _Py_uop_sym_new_const(ctx, float_tenth_obj); + if (PyJitRef_IsNull(subject) || float_tenth_obj == NULL || PyJitRef_IsNull(const_float_tenth)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_float_tenth, JIT_PRED_EQ); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, true); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, subject), "predicate narrowing did not const-narrow subject (float)"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, subject) == float_tenth_obj, "predicate narrowing did not narrow subject to 0.1"); + + // Resolving EQ predicate to False should not narrow subject for float + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_float_tenth, JIT_PRED_EQ); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, false); + TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, subject), "predicate narrowing incorrectly narrowed subject (inverted/true)"); + + // Test narrowing subject to constant from NE predicate for float + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_float_tenth, JIT_PRED_NE); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, false); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, subject), "predicate narrowing did not const-narrow subject (float)"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, subject) == float_tenth_obj, "predicate narrowing did not narrow subject to 0.1"); + + // Resolving NE predicate to true should not narrow subject for float + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_float_tenth, JIT_PRED_NE); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, true); + TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, subject), "predicate narrowing incorrectly narrowed subject (inverted/true)"); + + // Test narrowing subject to constant from EQ predicate for str + subject = _Py_uop_sym_new_unknown(ctx); + PyObject *str_hello_obj = PyUnicode_FromString("hello"); + JitOptRef const_str_hello = _Py_uop_sym_new_const(ctx, str_hello_obj); + if (PyJitRef_IsNull(subject) || str_hello_obj == NULL || PyJitRef_IsNull(const_str_hello)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_str_hello, JIT_PRED_EQ); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, true); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, subject), "predicate narrowing did not const-narrow subject (str)"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, subject) == str_hello_obj, "predicate narrowing did not narrow subject to hello"); + + // Resolving EQ predicate to False should not narrow subject for str + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_str_hello, JIT_PRED_EQ); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, false); + TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, subject), "predicate narrowing incorrectly narrowed subject (inverted/true)"); + + // Test narrowing subject to constant from NE predicate for str + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_str_hello, JIT_PRED_NE); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, false); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, subject), "predicate narrowing did not const-narrow subject (str)"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, subject) == str_hello_obj, "predicate narrowing did not narrow subject to hello"); + + // Resolving NE predicate to true should not narrow subject for str + subject = _Py_uop_sym_new_unknown(ctx); + if (PyJitRef_IsNull(subject)) { + goto fail; + } + ref = _Py_uop_sym_new_predicate(ctx, subject, const_str_hello, JIT_PRED_NE); + if (PyJitRef_IsNull(ref)) { + goto fail; + } + _Py_uop_sym_apply_predicate_narrowing(ctx, ref, true); + TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, subject), "predicate narrowing incorrectly narrowed subject (inverted/true)"); + + subject = _Py_uop_sym_new_unknown(ctx); + value = _Py_uop_sym_new_const(ctx, one_obj); + ref = _Py_uop_sym_new_predicate(ctx, subject, value, JIT_PRED_IS); + if (PyJitRef_IsNull(subject) || PyJitRef_IsNull(value) || PyJitRef_IsNull(ref)) { + goto fail; + } + TEST_PREDICATE(_Py_uop_sym_matches_type(ref, &PyBool_Type), "predicate is not boolean"); + TEST_PREDICATE(_Py_uop_sym_truthiness(ctx, ref) == -1, "predicate is not unknown"); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, ref) == false, "predicate is constant"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, ref) == NULL, "predicate is not NULL"); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, value) == true, "value is not constant"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, value) == one_obj, "value is not 1"); + _Py_uop_sym_set_const(ctx, ref, Py_False); + TEST_PREDICATE(_Py_uop_sym_matches_type(ref, &PyBool_Type), "predicate is not boolean"); + TEST_PREDICATE(_Py_uop_sym_truthiness(ctx, ref) == 0, "predicate is not False"); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, ref) == true, "predicate is not constant"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, ref) == Py_False, "predicate is not False"); + TEST_PREDICATE(_Py_uop_sym_is_const(ctx, value) == true, "value is not constant"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, value) == one_obj, "value is not 1"); val_big = PyNumber_Lshift(_PyLong_GetOne(), PyLong_FromLong(66)); if (val_big == NULL) { @@ -1087,11 +1980,121 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) TEST_PREDICATE(_Py_uop_sym_matches_type(ref_int, &PyLong_Type), "43 is not an int"); TEST_PREDICATE(_Py_uop_sym_get_const(ctx, ref_int) == val_43, "43 isn't 43"); + // Test recorded values + + /* Test that recorded values aren't treated as known values*/ + JitOptRef rv1 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_value(ctx, rv1, val_42); + TEST_PREDICATE(!_Py_uop_sym_matches_type(rv1, &PyLong_Type), "recorded value is treated as known"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rv1) == NULL, "recorded value is treated as known"); + TEST_PREDICATE(!_Py_uop_sym_is_compact_int(rv1), "recorded value is treated as known"); + + /* Test that setting type or value narrows correctly */ + JitOptRef rv2 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_value(ctx, rv2, val_42); + _Py_uop_sym_set_const(ctx, rv2, val_42); + TEST_PREDICATE(_Py_uop_sym_matches_type(rv2, &PyLong_Type), "recorded value doesn't narrow"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rv2) == val_42, "recorded value doesn't narrow"); + + JitOptRef rv3 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_value(ctx, rv3, val_42); + _Py_uop_sym_set_type(ctx, rv3, &PyLong_Type); + TEST_PREDICATE(_Py_uop_sym_matches_type(rv3, &PyLong_Type), "recorded value doesn't narrow"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rv3) == NULL, "recorded value with type is treated as known"); + + JitOptRef rv4 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_value(ctx, rv4, val_42); + _Py_uop_sym_set_type_version(ctx, rv4, PyLong_Type.tp_version_tag); + TEST_PREDICATE(_Py_uop_sym_matches_type(rv4, &PyLong_Type), "recorded value doesn't narrow"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rv4) == NULL, "recorded value with type is treated as known"); + + // test recorded types + + /* Test that recorded type aren't treated as known values*/ + JitOptRef rt1 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_type(ctx, rt1, &PyLong_Type); + TEST_PREDICATE(!_Py_uop_sym_matches_type(rt1, &PyLong_Type), "recorded type is treated as known"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rt1) == NULL, "recorded type is treated as known value"); + + /* Test that setting type or value narrows correctly */ + JitOptRef rt2 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_type(ctx, rt2, &PyLong_Type); + _Py_uop_sym_set_const(ctx, rt2, val_42); + TEST_PREDICATE(_Py_uop_sym_matches_type(rt2, &PyLong_Type), "recorded value doesn't narrow"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rt2) == val_42, "recorded value doesn't narrow"); + + JitOptRef rt3 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_type(ctx, rt3, &PyLong_Type); + _Py_uop_sym_set_type(ctx, rt3, &PyLong_Type); + TEST_PREDICATE(_Py_uop_sym_matches_type(rt3, &PyLong_Type), "recorded value doesn't narrow"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rt3) == NULL, "known type is treated as known value"); + + JitOptRef rt4 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_type(ctx, rt4, &PyLong_Type); + _Py_uop_sym_set_type_version(ctx, rt4, PyLong_Type.tp_version_tag); + TEST_PREDICATE(_Py_uop_sym_matches_type(rt4, &PyLong_Type), "recorded value doesn't narrow"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rt4) == NULL, "recorded value with type is treated as known"); + + // test recorded gen function + + PyObject *dict = PyDict_New(); + if (dict == NULL) { + goto fail; + } + PyCodeObject *code = PyCode_NewEmpty(__FILE__, "uop_symbols_test", __LINE__); + if (code == NULL) { + goto fail; + } + func = (PyFunctionObject *)PyFunction_New((PyObject *)code, dict); + if (func == NULL) { + goto fail; + } + + /* Test that recorded type aren't treated as known values*/ + JitOptRef rg1 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_gen_func(ctx, rg1, func); + TEST_PREDICATE(!_Py_uop_sym_matches_type(rg1, &PyGen_Type), "recorded gen func treated as generator"); + TEST_PREDICATE(_Py_uop_sym_get_probable_type(rg1) == &PyGen_Type, "recorded gen func not treated as generator"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rg1) == NULL, "recorded gen func is treated as known value"); + + /* Test that setting type narrows correctly */ + + JitOptRef rg2 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_gen_func(ctx, rg2, func); + _Py_uop_sym_set_type(ctx, rg2, &PyGen_Type); + TEST_PREDICATE(!_Py_uop_sym_matches_type(rg2, &PyGen_Type), "recorded gen func treated as generator"); + TEST_PREDICATE(_Py_uop_sym_get_probable_type(rg2) == &PyGen_Type, "recorded gen func not treated as generator"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rg2) == NULL, "known type is treated as known value"); + + JitOptRef rg3 = _Py_uop_sym_new_unknown(ctx); + _Py_uop_sym_set_recorded_gen_func(ctx, rg3, func); + _Py_uop_sym_set_type_version(ctx, rg3, PyGen_Type.tp_version_tag); + TEST_PREDICATE(!_Py_uop_sym_matches_type(rg3, &PyGen_Type), "recorded gen func treated as generator"); + TEST_PREDICATE(_Py_uop_sym_get_probable_type(rg3) == &PyGen_Type, "recorded gen func not treated as generator"); + TEST_PREDICATE(_Py_uop_sym_get_const(ctx, rg3) == NULL, "recorded value with type is treated as known"); + + /* Test contradictions */ + _Py_uop_sym_set_type(ctx, rv1, &PyFloat_Type); + TEST_PREDICATE(_Py_uop_sym_is_bottom(rv1), "recorded value cast to other type isn't bottom"); + _Py_uop_sym_set_type_version(ctx, rv2, PyFloat_Type.tp_version_tag); + TEST_PREDICATE(_Py_uop_sym_is_bottom(rv2), "recorded value cast to other type version isn't bottom"); + + _Py_uop_sym_set_type(ctx, rt1, &PyFloat_Type); + TEST_PREDICATE(_Py_uop_sym_is_bottom(rv1), "recorded type cast to other type isn't bottom"); + _Py_uop_sym_set_type_version(ctx, rt2, PyFloat_Type.tp_version_tag); + TEST_PREDICATE(_Py_uop_sym_is_bottom(rv2), "recorded type cast to other type version isn't bottom"); + + _Py_uop_sym_set_type(ctx, rg1, &PyFloat_Type); + TEST_PREDICATE(_Py_uop_sym_is_bottom(rg1), "recorded gen func cast to other type isn't bottom"); + _Py_uop_sym_set_type_version(ctx, rg2, PyFloat_Type.tp_version_tag); + TEST_PREDICATE(_Py_uop_sym_is_bottom(rg2), "recorded gen func cast to other type version isn't bottom"); + _Py_uop_abstractcontext_fini(ctx); Py_DECREF(val_42); Py_DECREF(val_43); Py_DECREF(val_big); Py_DECREF(tuple); + Py_DECREF(func); Py_RETURN_NONE; fail: @@ -1099,7 +2102,8 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) Py_XDECREF(val_42); Py_XDECREF(val_43); Py_XDECREF(val_big); - Py_DECREF(tuple); + Py_XDECREF(tuple); + Py_XDECREF(func); return NULL; } diff --git a/Python/parking_lot.c b/Python/parking_lot.c index e896dea02712f2..8823d77719cb9a 100644 --- a/Python/parking_lot.c +++ b/Python/parking_lot.c @@ -61,7 +61,9 @@ _PySemaphore_Init(_PySemaphore *sema) NULL // unnamed ); if (!sema->platform_sem) { - Py_FatalError("parking_lot: CreateSemaphore failed"); + _Py_FatalErrorFormat(__func__, + "parking_lot: CreateSemaphore failed (error: %u)", + GetLastError()); } #elif defined(_Py_USE_SEMAPHORES) if (sem_init(&sema->platform_sem, /*pshared=*/0, /*value=*/0) < 0) { @@ -91,8 +93,8 @@ _PySemaphore_Destroy(_PySemaphore *sema) #endif } -static int -_PySemaphore_PlatformWait(_PySemaphore *sema, PyTime_t timeout) +int +_PySemaphore_Wait(_PySemaphore *sema, PyTime_t timeout) { int res; #if defined(MS_WINDOWS) @@ -141,8 +143,8 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, PyTime_t timeout) } else { _Py_FatalErrorFormat(__func__, - "unexpected error from semaphore: %u (error: %u)", - wait, GetLastError()); + "unexpected error from semaphore: %u (error: %u, handle: %p)", + wait, GetLastError(), sema->platform_sem); } #elif defined(_Py_USE_SEMAPHORES) int err; @@ -225,33 +227,14 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, PyTime_t timeout) return res; } -int -_PySemaphore_Wait(_PySemaphore *sema, PyTime_t timeout, int detach) -{ - PyThreadState *tstate = NULL; - if (detach) { - tstate = _PyThreadState_GET(); - if (tstate && _PyThreadState_IsAttached(tstate)) { - // Only detach if we are attached - PyEval_ReleaseThread(tstate); - } - else { - tstate = NULL; - } - } - int res = _PySemaphore_PlatformWait(sema, timeout); - if (tstate) { - PyEval_AcquireThread(tstate); - } - return res; -} - void _PySemaphore_Wakeup(_PySemaphore *sema) { #if defined(MS_WINDOWS) if (!ReleaseSemaphore(sema->platform_sem, 1, NULL)) { - Py_FatalError("parking_lot: ReleaseSemaphore failed"); + _Py_FatalErrorFormat(__func__, + "parking_lot: ReleaseSemaphore failed (error: %u, handle: %p)", + GetLastError(), sema->platform_sem); } #elif defined(_Py_USE_SEMAPHORES) int err = sem_post(&sema->platform_sem); @@ -342,7 +325,19 @@ _PyParkingLot_Park(const void *addr, const void *expected, size_t size, enqueue(bucket, addr, &wait); _PyRawMutex_Unlock(&bucket->mutex); - int res = _PySemaphore_Wait(&wait.sema, timeout_ns, detach); + PyThreadState *tstate = NULL; + if (detach) { + tstate = _PyThreadState_GET(); + if (tstate && _PyThreadState_IsAttached(tstate)) { + // Only detach if we are attached + PyEval_ReleaseThread(tstate); + } + else { + tstate = NULL; + } + } + + int res = _PySemaphore_Wait(&wait.sema, timeout_ns); if (res == Py_PARK_OK) { goto done; } @@ -354,7 +349,7 @@ _PyParkingLot_Park(const void *addr, const void *expected, size_t size, // Another thread has started to unpark us. Wait until we process the // wakeup signal. do { - res = _PySemaphore_Wait(&wait.sema, -1, detach); + res = _PySemaphore_Wait(&wait.sema, -1); } while (res != Py_PARK_OK); goto done; } @@ -366,6 +361,9 @@ _PyParkingLot_Park(const void *addr, const void *expected, size_t size, done: _PySemaphore_Destroy(&wait.sema); + if (tstate) { + PyEval_AcquireThread(tstate); + } return res; } diff --git a/Python/perf_jit_trampoline.c b/Python/perf_jit_trampoline.c index 8732be973616d4..32b147199544cf 100644 --- a/Python/perf_jit_trampoline.c +++ b/Python/perf_jit_trampoline.c @@ -61,6 +61,8 @@ #include "pycore_ceval.h" // _PyPerf_Callbacks #include "pycore_frame.h" #include "pycore_interp.h" +#include "pycore_mmap.h" // _PyAnnotateMemoryMap() +#include "pycore_jit_unwind.h" #include "pycore_runtime.h" // _PyRuntime #ifdef PY_HAVE_PERF_TRAMPOLINE @@ -72,6 +74,7 @@ #include <fcntl.h> // File control operations #include <stdio.h> // Standard I/O operations #include <stdlib.h> // Standard library functions +#include <string.h> // memcpy, strlen #include <sys/mman.h> // Memory mapping functions (mmap) #include <sys/types.h> // System data types #include <unistd.h> // System calls (sysconf, getpid) @@ -79,6 +82,9 @@ #if defined(__linux__) # include <sys/syscall.h> // System call interface #endif +#if defined(__APPLE__) +# include <mach/mach_time.h> // mach_absolute_time, mach_timebase_info +#endif // ============================================================================= // CONSTANTS AND CONFIGURATION @@ -214,11 +220,7 @@ struct BaseEvent { typedef struct { struct BaseEvent base; // Common event header uint32_t process_id; // Process ID where code was generated -#if defined(__APPLE__) - uint64_t thread_id; // Thread ID where code was generated -#else uint32_t thread_id; // Thread ID where code was generated -#endif uint64_t vma; // Virtual memory address where code is loaded uint64_t code_address; // Address of the actual machine code uint64_t code_size; // Size of the machine code in bytes @@ -245,6 +247,25 @@ typedef struct { */ } CodeUnwindingInfoEvent; +/* + * EH Frame Header structure for DWARF unwinding + * + * This header provides metadata about the .eh_frame data that follows. + * It uses PC-relative and data-relative encodings to keep the synthesized + * DSO self-contained when perf injects it. + */ +typedef struct __attribute__((packed)) { + uint8_t version; + uint8_t eh_frame_ptr_enc; + uint8_t fde_count_enc; + uint8_t table_enc; + int32_t eh_frame_ptr; + uint32_t eh_fde_count; + int32_t from; + int32_t to; +} EhFrameHeader; +_Static_assert(sizeof(EhFrameHeader) == 20, "EhFrameHeader layout mismatch"); + // ============================================================================= // GLOBAL STATE MANAGEMENT // ============================================================================= @@ -258,10 +279,11 @@ typedef struct { */ typedef struct { FILE* perf_map; // File handle for the jitdump file - PyThread_type_lock map_lock; // Thread synchronization lock + PyMutex map_lock; // Thread synchronization lock void* mapped_buffer; // Memory-mapped region (signals perf we're active) size_t mapped_size; // Size of the mapped region - int code_id; // Counter for unique code region identifiers + uint32_t code_id; // Counter for unique code region identifiers + uint64_t build_id_salt; // Per-process salt for unique synthetic DSOs } PerfMapJitState; /* Global singleton instance */ @@ -272,7 +294,9 @@ static PerfMapJitState perf_jit_map_state; // ============================================================================= /* Time conversion constant */ +#if !defined(__APPLE__) static const intptr_t nanoseconds_per_second = 1000000000; +#endif /* * Get current monotonic time in nanoseconds @@ -284,6 +308,18 @@ static const intptr_t nanoseconds_per_second = 1000000000; * Returns: Current monotonic time in nanoseconds since an arbitrary epoch */ static int64_t get_current_monotonic_ticks(void) { +#if defined(__APPLE__) + // On macOS the jitdump file is consumed by profilers (such as samply) that + // timestamp their samples using mach_absolute_time(). The jitdump event + // timestamps must use the same clock domain, otherwise the JIT code + // mappings cannot be lined up with the samples. + static mach_timebase_info_data_t timebase = {0, 0}; + if (timebase.denom == 0) { + (void)mach_timebase_info(&timebase); + } + uint64_t ticks = mach_absolute_time(); + return (int64_t)(ticks * timebase.numer / timebase.denom); +#else struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { Py_UNREACHABLE(); // Should never fail on supported systems @@ -295,6 +331,7 @@ static int64_t get_current_monotonic_ticks(void) { result *= nanoseconds_per_second; result += ts.tv_nsec; return result; +#endif } /* @@ -315,40 +352,6 @@ static int64_t get_current_time_microseconds(void) { return ((int64_t)(tv.tv_sec) * 1000000) + tv.tv_usec; } -// ============================================================================= -// UTILITY FUNCTIONS -// ============================================================================= - -/* - * Round up a value to the next multiple of a given number - * - * This is essential for maintaining proper alignment requirements in the - * jitdump format. Many structures need to be aligned to specific boundaries - * (typically 8 or 16 bytes) for efficient processing by perf. - * - * Args: - * value: The value to round up - * multiple: The multiple to round up to - * - * Returns: The smallest value >= input that is a multiple of 'multiple' - */ -static size_t round_up(int64_t value, int64_t multiple) { - if (multiple == 0) { - return value; // Avoid division by zero - } - - int64_t remainder = value % multiple; - if (remainder == 0) { - return value; // Already aligned - } - - /* Calculate how much to add to reach the next multiple */ - int64_t difference = multiple - remainder; - int64_t rounded_up_value = value + difference; - - return rounded_up_value; -} - // ============================================================================= // FILE I/O UTILITIES // ============================================================================= @@ -397,6 +400,7 @@ static void perf_map_jit_write_header(int pid, FILE* out_file) { header.version = 1; // Current jitdump version header.size = sizeof(Header); // Header size for validation header.elf_mach_target = GetElfMachineArchitecture(); // Target architecture + header.reserved = 0; // padding reserved for future use header.process_id = pid; // Process identifier header.time_stamp = get_current_time_microseconds(); // Creation time header.flags = 0; // No special flags currently used @@ -404,623 +408,6 @@ static void perf_map_jit_write_header(int pid, FILE* out_file) { perf_map_jit_write_fully(&header, sizeof(header)); } -// ============================================================================= -// DWARF CONSTANTS AND UTILITIES -// ============================================================================= - -/* - * DWARF (Debug With Arbitrary Record Formats) constants - * - * DWARF is a debugging data format used to provide stack unwinding information. - * These constants define the various encoding types and opcodes used in - * DWARF Call Frame Information (CFI) records. - */ - -/* DWARF Call Frame Information version */ -#define DWRF_CIE_VERSION 1 - -/* DWARF CFA (Call Frame Address) opcodes */ -enum { - DWRF_CFA_nop = 0x0, // No operation - DWRF_CFA_offset_extended = 0x5, // Extended offset instruction - DWRF_CFA_def_cfa = 0xc, // Define CFA rule - DWRF_CFA_def_cfa_register = 0xd, // Define CFA register - DWRF_CFA_def_cfa_offset = 0xe, // Define CFA offset - DWRF_CFA_offset_extended_sf = 0x11, // Extended signed offset - DWRF_CFA_advance_loc = 0x40, // Advance location counter - DWRF_CFA_offset = 0x80, // Simple offset instruction - DWRF_CFA_restore = 0xc0 // Restore register -}; - -/* DWARF Exception Handling pointer encodings */ -enum { - DWRF_EH_PE_absptr = 0x00, // Absolute pointer - DWRF_EH_PE_omit = 0xff, // Omitted value - - /* Data type encodings */ - DWRF_EH_PE_uleb128 = 0x01, // Unsigned LEB128 - DWRF_EH_PE_udata2 = 0x02, // Unsigned 2-byte - DWRF_EH_PE_udata4 = 0x03, // Unsigned 4-byte - DWRF_EH_PE_udata8 = 0x04, // Unsigned 8-byte - DWRF_EH_PE_sleb128 = 0x09, // Signed LEB128 - DWRF_EH_PE_sdata2 = 0x0a, // Signed 2-byte - DWRF_EH_PE_sdata4 = 0x0b, // Signed 4-byte - DWRF_EH_PE_sdata8 = 0x0c, // Signed 8-byte - DWRF_EH_PE_signed = 0x08, // Signed flag - - /* Reference type encodings */ - DWRF_EH_PE_pcrel = 0x10, // PC-relative - DWRF_EH_PE_textrel = 0x20, // Text-relative - DWRF_EH_PE_datarel = 0x30, // Data-relative - DWRF_EH_PE_funcrel = 0x40, // Function-relative - DWRF_EH_PE_aligned = 0x50, // Aligned - DWRF_EH_PE_indirect = 0x80 // Indirect -}; - -/* Additional DWARF constants for debug information */ -enum { DWRF_TAG_compile_unit = 0x11 }; -enum { DWRF_children_no = 0, DWRF_children_yes = 1 }; -enum { - DWRF_AT_name = 0x03, // Name attribute - DWRF_AT_stmt_list = 0x10, // Statement list - DWRF_AT_low_pc = 0x11, // Low PC address - DWRF_AT_high_pc = 0x12 // High PC address -}; -enum { - DWRF_FORM_addr = 0x01, // Address form - DWRF_FORM_data4 = 0x06, // 4-byte data - DWRF_FORM_string = 0x08 // String form -}; - -/* Line number program opcodes */ -enum { - DWRF_LNS_extended_op = 0, // Extended opcode - DWRF_LNS_copy = 1, // Copy operation - DWRF_LNS_advance_pc = 2, // Advance program counter - DWRF_LNS_advance_line = 3 // Advance line number -}; - -/* Line number extended opcodes */ -enum { - DWRF_LNE_end_sequence = 1, // End of sequence - DWRF_LNE_set_address = 2 // Set address -}; - -/* - * Architecture-specific DWARF register numbers - * - * These constants define the register numbering scheme used by DWARF - * for each supported architecture. The numbers must match the ABI - * specification for proper stack unwinding. - */ -enum { -#ifdef __x86_64__ - /* x86_64 register numbering (note: order is defined by x86_64 ABI) */ - DWRF_REG_AX, // RAX - DWRF_REG_DX, // RDX - DWRF_REG_CX, // RCX - DWRF_REG_BX, // RBX - DWRF_REG_SI, // RSI - DWRF_REG_DI, // RDI - DWRF_REG_BP, // RBP - DWRF_REG_SP, // RSP - DWRF_REG_8, // R8 - DWRF_REG_9, // R9 - DWRF_REG_10, // R10 - DWRF_REG_11, // R11 - DWRF_REG_12, // R12 - DWRF_REG_13, // R13 - DWRF_REG_14, // R14 - DWRF_REG_15, // R15 - DWRF_REG_RA, // Return address (RIP) -#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) - /* AArch64 register numbering */ - DWRF_REG_FP = 29, // Frame Pointer - DWRF_REG_RA = 30, // Link register (return address) - DWRF_REG_SP = 31, // Stack pointer -#else -# error "Unsupported target architecture" -#endif -}; - -/* DWARF encoding constants used in EH frame headers */ -static const uint8_t DwarfUData4 = 0x03; // Unsigned 4-byte data -static const uint8_t DwarfSData4 = 0x0b; // Signed 4-byte data -static const uint8_t DwarfPcRel = 0x10; // PC-relative encoding -static const uint8_t DwarfDataRel = 0x30; // Data-relative encoding - -// ============================================================================= -// ELF OBJECT CONTEXT -// ============================================================================= - -/* - * Context for building ELF/DWARF structures - * - * This structure maintains state while constructing DWARF unwind information. - * It acts as a simple buffer manager with pointers to track current position - * and important landmarks within the buffer. - */ -typedef struct ELFObjectContext { - uint8_t* p; // Current write position in buffer - uint8_t* startp; // Start of buffer (for offset calculations) - uint8_t* eh_frame_p; // Start of EH frame data (for relative offsets) - uint8_t* fde_p; // Start of FDE data (for PC-relative calculations) - uint32_t code_size; // Size of the code being described -} ELFObjectContext; - -/* - * EH Frame Header structure for DWARF unwinding - * - * This structure provides metadata about the DWARF unwinding information - * that follows. It's required by the perf jitdump format to enable proper - * stack unwinding during profiling. - */ -typedef struct { - unsigned char version; // EH frame version (always 1) - unsigned char eh_frame_ptr_enc; // Encoding of EH frame pointer - unsigned char fde_count_enc; // Encoding of FDE count - unsigned char table_enc; // Encoding of table entries - int32_t eh_frame_ptr; // Pointer to EH frame data - int32_t eh_fde_count; // Number of FDEs (Frame Description Entries) - int32_t from; // Start address of code range - int32_t to; // End address of code range -} EhFrameHeader; - -// ============================================================================= -// DWARF GENERATION UTILITIES -// ============================================================================= - -/* - * Append a null-terminated string to the ELF context buffer - * - * Args: - * ctx: ELF object context - * str: String to append (must be null-terminated) - * - * Returns: Offset from start of buffer where string was written - */ -static uint32_t elfctx_append_string(ELFObjectContext* ctx, const char* str) { - uint8_t* p = ctx->p; - uint32_t ofs = (uint32_t)(p - ctx->startp); - - /* Copy string including null terminator */ - do { - *p++ = (uint8_t)*str; - } while (*str++); - - ctx->p = p; - return ofs; -} - -/* - * Append a SLEB128 (Signed Little Endian Base 128) value - * - * SLEB128 is a variable-length encoding used extensively in DWARF. - * It efficiently encodes small numbers in fewer bytes. - * - * Args: - * ctx: ELF object context - * v: Signed value to encode - */ -static void elfctx_append_sleb128(ELFObjectContext* ctx, int32_t v) { - uint8_t* p = ctx->p; - - /* Encode 7 bits at a time, with continuation bit in MSB */ - for (; (uint32_t)(v + 0x40) >= 0x80; v >>= 7) { - *p++ = (uint8_t)((v & 0x7f) | 0x80); // Set continuation bit - } - *p++ = (uint8_t)(v & 0x7f); // Final byte without continuation bit - - ctx->p = p; -} - -/* - * Append a ULEB128 (Unsigned Little Endian Base 128) value - * - * Similar to SLEB128 but for unsigned values. - * - * Args: - * ctx: ELF object context - * v: Unsigned value to encode - */ -static void elfctx_append_uleb128(ELFObjectContext* ctx, uint32_t v) { - uint8_t* p = ctx->p; - - /* Encode 7 bits at a time, with continuation bit in MSB */ - for (; v >= 0x80; v >>= 7) { - *p++ = (char)((v & 0x7f) | 0x80); // Set continuation bit - } - *p++ = (char)v; // Final byte without continuation bit - - ctx->p = p; -} - -/* - * Macros for generating DWARF structures - * - * These macros provide a convenient way to write various data types - * to the DWARF buffer while automatically advancing the pointer. - */ -#define DWRF_U8(x) (*p++ = (x)) // Write unsigned 8-bit -#define DWRF_I8(x) (*(int8_t*)p = (x), p++) // Write signed 8-bit -#define DWRF_U16(x) (*(uint16_t*)p = (x), p += 2) // Write unsigned 16-bit -#define DWRF_U32(x) (*(uint32_t*)p = (x), p += 4) // Write unsigned 32-bit -#define DWRF_ADDR(x) (*(uintptr_t*)p = (x), p += sizeof(uintptr_t)) // Write address -#define DWRF_UV(x) (ctx->p = p, elfctx_append_uleb128(ctx, (x)), p = ctx->p) // Write ULEB128 -#define DWRF_SV(x) (ctx->p = p, elfctx_append_sleb128(ctx, (x)), p = ctx->p) // Write SLEB128 -#define DWRF_STR(str) (ctx->p = p, elfctx_append_string(ctx, (str)), p = ctx->p) // Write string - -/* Align to specified boundary with NOP instructions */ -#define DWRF_ALIGNNOP(s) \ - while ((uintptr_t)p & ((s)-1)) { \ - *p++ = DWRF_CFA_nop; \ - } - -/* Write a DWARF section with automatic size calculation */ -#define DWRF_SECTION(name, stmt) \ - { \ - uint32_t* szp_##name = (uint32_t*)p; \ - p += 4; \ - stmt; \ - *szp_##name = (uint32_t)((p - (uint8_t*)szp_##name) - 4); \ - } - -// ============================================================================= -// DWARF EH FRAME GENERATION -// ============================================================================= - -static void elf_init_ehframe(ELFObjectContext* ctx); - -/* - * Initialize DWARF .eh_frame section for a code region - * - * The .eh_frame section contains Call Frame Information (CFI) that describes - * how to unwind the stack at any point in the code. This is essential for - * proper profiling as it allows perf to generate accurate call graphs. - * - * The function generates two main components: - * 1. CIE (Common Information Entry) - describes calling conventions - * 2. FDE (Frame Description Entry) - describes specific function unwinding - * - * Args: - * ctx: ELF object context containing code size and buffer pointers - */ -static size_t calculate_eh_frame_size(void) { - /* Calculate the EH frame size for the trampoline function */ - extern void *_Py_trampoline_func_start; - extern void *_Py_trampoline_func_end; - - size_t code_size = (char*)&_Py_trampoline_func_end - (char*)&_Py_trampoline_func_start; - - ELFObjectContext ctx; - char buffer[1024]; // Buffer for DWARF data (1KB should be sufficient) - ctx.code_size = code_size; - ctx.startp = ctx.p = (uint8_t*)buffer; - ctx.fde_p = NULL; - - elf_init_ehframe(&ctx); - return ctx.p - ctx.startp; -} - -static void elf_init_ehframe(ELFObjectContext* ctx) { - uint8_t* p = ctx->p; - uint8_t* framep = p; // Remember start of frame data - - /* - * DWARF Unwind Table for Trampoline Function - * - * This section defines DWARF Call Frame Information (CFI) using encoded macros - * like `DWRF_U8`, `DWRF_UV`, and `DWRF_SECTION` to describe how the trampoline function - * preserves and restores registers. This is used by profiling tools (e.g., `perf`) - * and debuggers for stack unwinding in JIT-compiled code. - * - * ------------------------------------------------- - * TO REGENERATE THIS TABLE FROM GCC OBJECTS: - * ------------------------------------------------- - * - * 1. Create a trampoline source file (e.g., `trampoline.c`): - * - * #include <Python.h> - * typedef PyObject* (*py_evaluator)(void*, void*, int); - * PyObject* trampoline(void *ts, void *f, int throwflag, py_evaluator evaluator) { - * return evaluator(ts, f, throwflag); - * } - * - * 2. Compile to an object file with frame pointer preservation: - * - * gcc trampoline.c -I. -I./Include -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -c - * - * 3. Extract DWARF unwind info from the object file: - * - * readelf -w trampoline.o - * - * Example output from `.eh_frame`: - * - * 00000000 CIE - * Version: 1 - * Augmentation: "zR" - * Code alignment factor: 4 - * Data alignment factor: -8 - * Return address column: 30 - * DW_CFA_def_cfa: r31 (sp) ofs 0 - * - * 00000014 FDE cie=00000000 pc=0..14 - * DW_CFA_advance_loc: 4 - * DW_CFA_def_cfa_offset: 16 - * DW_CFA_offset: r29 at cfa-16 - * DW_CFA_offset: r30 at cfa-8 - * DW_CFA_advance_loc: 12 - * DW_CFA_restore: r30 - * DW_CFA_restore: r29 - * DW_CFA_def_cfa_offset: 0 - * - * -- These values can be verified by comparing with `readelf -w` or `llvm-dwarfdump --eh-frame`. - * - * ---------------------------------- - * HOW TO TRANSLATE TO DWRF_* MACROS: - * ---------------------------------- - * - * After compiling your trampoline with: - * - * gcc trampoline.c -I. -I./Include -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -c - * - * run: - * - * readelf -w trampoline.o - * - * to inspect the generated `.eh_frame` data. You will see two main components: - * - * 1. A CIE (Common Information Entry): shared configuration used by all FDEs. - * 2. An FDE (Frame Description Entry): function-specific unwind instructions. - * - * --------------------- - * Translating the CIE: - * --------------------- - * From `readelf -w`, you might see: - * - * 00000000 0000000000000010 00000000 CIE - * Version: 1 - * Augmentation: "zR" - * Code alignment factor: 4 - * Data alignment factor: -8 - * Return address column: 30 - * Augmentation data: 1b - * DW_CFA_def_cfa: r31 (sp) ofs 0 - * - * Map this to: - * - * DWRF_SECTION(CIE, - * DWRF_U32(0); // CIE ID (always 0 for CIEs) - * DWRF_U8(DWRF_CIE_VERSION); // Version: 1 - * DWRF_STR("zR"); // Augmentation string "zR" - * DWRF_UV(4); // Code alignment factor = 4 - * DWRF_SV(-8); // Data alignment factor = -8 - * DWRF_U8(DWRF_REG_RA); // Return address register (e.g., x30 = 30) - * DWRF_UV(1); // Augmentation data length = 1 - * DWRF_U8(DWRF_EH_PE_pcrel | DWRF_EH_PE_sdata4); // Encoding for FDE pointers - * - * DWRF_U8(DWRF_CFA_def_cfa); // DW_CFA_def_cfa - * DWRF_UV(DWRF_REG_SP); // Register: SP (r31) - * DWRF_UV(0); // Offset = 0 - * - * DWRF_ALIGNNOP(sizeof(uintptr_t)); // Align to pointer size boundary - * ) - * - * Notes: - * - Use `DWRF_UV` for unsigned LEB128, `DWRF_SV` for signed LEB128. - * - `DWRF_REG_RA` and `DWRF_REG_SP` are architecture-defined constants. - * - * --------------------- - * Translating the FDE: - * --------------------- - * From `readelf -w`: - * - * 00000014 0000000000000020 00000018 FDE cie=00000000 pc=0000000000000000..0000000000000014 - * DW_CFA_advance_loc: 4 - * DW_CFA_def_cfa_offset: 16 - * DW_CFA_offset: r29 at cfa-16 - * DW_CFA_offset: r30 at cfa-8 - * DW_CFA_advance_loc: 12 - * DW_CFA_restore: r30 - * DW_CFA_restore: r29 - * DW_CFA_def_cfa_offset: 0 - * - * Map the FDE header and instructions to: - * - * DWRF_SECTION(FDE, - * DWRF_U32((uint32_t)(p - framep)); // Offset to CIE (relative from here) - * DWRF_U32(pc_relative_offset); // PC-relative location of the code (calculated dynamically) - * DWRF_U32(ctx->code_size); // Code range covered by this FDE - * DWRF_U8(0); // Augmentation data length (none) - * - * DWRF_U8(DWRF_CFA_advance_loc | 1); // Advance location by 1 unit (1 * 4 = 4 bytes) - * DWRF_U8(DWRF_CFA_def_cfa_offset); // CFA = SP + 16 - * DWRF_UV(16); - * - * DWRF_U8(DWRF_CFA_offset | DWRF_REG_FP); // Save x29 (frame pointer) - * DWRF_UV(2); // At offset 2 * 8 = 16 bytes - * - * DWRF_U8(DWRF_CFA_offset | DWRF_REG_RA); // Save x30 (return address) - * DWRF_UV(1); // At offset 1 * 8 = 8 bytes - * - * DWRF_U8(DWRF_CFA_advance_loc | 3); // Advance location by 3 units (3 * 4 = 12 bytes) - * - * DWRF_U8(DWRF_CFA_offset | DWRF_REG_RA); // Restore x30 - * DWRF_U8(DWRF_CFA_offset | DWRF_REG_FP); // Restore x29 - * - * DWRF_U8(DWRF_CFA_def_cfa_offset); // CFA = SP - * DWRF_UV(0); - * ) - * - * To regenerate: - * 1. Get the `code alignment factor`, `data alignment factor`, and `RA column` from the CIE. - * 2. Note the range of the function from the FDE's `pc=...` line and map it to the JIT code as - * the code is in a different address space every time. - * 3. For each `DW_CFA_*` entry, use the corresponding `DWRF_*` macro: - * - `DW_CFA_def_cfa_offset` → DWRF_U8(DWRF_CFA_def_cfa_offset), DWRF_UV(value) - * - `DW_CFA_offset: rX` → DWRF_U8(DWRF_CFA_offset | reg), DWRF_UV(offset) - * - `DW_CFA_restore: rX` → DWRF_U8(DWRF_CFA_offset | reg) // restore is same as reusing offset - * - `DW_CFA_advance_loc: N` → DWRF_U8(DWRF_CFA_advance_loc | (N / code_alignment_factor)) - * 4. Use `DWRF_REG_FP`, `DWRF_REG_RA`, etc., for register numbers. - * 5. Use `sizeof(uintptr_t)` (typically 8) for pointer size calculations and alignment. - */ - - /* - * Emit DWARF EH CIE (Common Information Entry) - * - * The CIE describes the calling conventions and basic unwinding rules - * that apply to all functions in this compilation unit. - */ - DWRF_SECTION(CIE, - DWRF_U32(0); // CIE ID (0 indicates this is a CIE) - DWRF_U8(DWRF_CIE_VERSION); // CIE version (1) - DWRF_STR("zR"); // Augmentation string ("zR" = has LSDA) -#ifdef __x86_64__ - DWRF_UV(1); // Code alignment factor (x86_64: 1 byte) -#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) - DWRF_UV(4); // Code alignment factor (AArch64: 4 bytes per instruction) -#endif - DWRF_SV(-(int64_t)sizeof(uintptr_t)); // Data alignment factor (negative) - DWRF_U8(DWRF_REG_RA); // Return address register number - DWRF_UV(1); // Augmentation data length - DWRF_U8(DWRF_EH_PE_pcrel | DWRF_EH_PE_sdata4); // FDE pointer encoding - - /* Initial CFI instructions - describe default calling convention */ -#ifdef __x86_64__ - /* x86_64 initial CFI state */ - DWRF_U8(DWRF_CFA_def_cfa); // Define CFA (Call Frame Address) - DWRF_UV(DWRF_REG_SP); // CFA = SP register - DWRF_UV(sizeof(uintptr_t)); // CFA = SP + pointer_size - DWRF_U8(DWRF_CFA_offset|DWRF_REG_RA); // Return address is saved - DWRF_UV(1); // At offset 1 from CFA -#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) - /* AArch64 initial CFI state */ - DWRF_U8(DWRF_CFA_def_cfa); // Define CFA (Call Frame Address) - DWRF_UV(DWRF_REG_SP); // CFA = SP register - DWRF_UV(0); // CFA = SP + 0 (AArch64 starts with offset 0) - // No initial register saves in AArch64 CIE -#endif - DWRF_ALIGNNOP(sizeof(uintptr_t)); // Align to pointer boundary - ) - - ctx->eh_frame_p = p; // Remember start of FDE data - - /* - * Emit DWARF EH FDE (Frame Description Entry) - * - * The FDE describes unwinding information specific to this function. - * It references the CIE and provides function-specific CFI instructions. - * - * The PC-relative offset is calculated after the entire EH frame is built - * to ensure accurate positioning relative to the synthesized DSO layout. - */ - DWRF_SECTION(FDE, - DWRF_U32((uint32_t)(p - framep)); // Offset to CIE (backwards reference) - ctx->fde_p = p; // Remember where PC offset field is located for later calculation - DWRF_U32(0); // Placeholder for PC-relative offset (calculated at end of elf_init_ehframe) - DWRF_U32(ctx->code_size); // Address range covered by this FDE (code length) - DWRF_U8(0); // Augmentation data length (none) - - /* - * Architecture-specific CFI instructions - * - * These instructions describe how registers are saved and restored - * during function calls. Each architecture has different calling - * conventions and register usage patterns. - */ -#ifdef __x86_64__ - /* x86_64 calling convention unwinding rules with frame pointer */ -# if defined(__CET__) && (__CET__ & 1) - DWRF_U8(DWRF_CFA_advance_loc | 4); // Advance past endbr64 (4 bytes) -# endif - DWRF_U8(DWRF_CFA_advance_loc | 1); // Advance past push %rbp (1 byte) - DWRF_U8(DWRF_CFA_def_cfa_offset); // def_cfa_offset 16 - DWRF_UV(16); // New offset: SP + 16 - DWRF_U8(DWRF_CFA_offset | DWRF_REG_BP); // offset r6 at cfa-16 - DWRF_UV(2); // Offset factor: 2 * 8 = 16 bytes - DWRF_U8(DWRF_CFA_advance_loc | 3); // Advance past mov %rsp,%rbp (3 bytes) - DWRF_U8(DWRF_CFA_def_cfa_register); // def_cfa_register r6 - DWRF_UV(DWRF_REG_BP); // Use base pointer register - DWRF_U8(DWRF_CFA_advance_loc | 3); // Advance past call *%rcx (2 bytes) + pop %rbp (1 byte) = 3 - DWRF_U8(DWRF_CFA_def_cfa); // def_cfa r7 ofs 8 - DWRF_UV(DWRF_REG_SP); // Use stack pointer register - DWRF_UV(8); // New offset: SP + 8 -#elif defined(__aarch64__) && defined(__AARCH64EL__) && !defined(__ILP32__) - /* AArch64 calling convention unwinding rules */ - DWRF_U8(DWRF_CFA_advance_loc | 1); // Advance by 1 instruction (4 bytes) - DWRF_U8(DWRF_CFA_def_cfa_offset); // CFA = SP + 16 - DWRF_UV(16); // Stack pointer moved by 16 bytes - DWRF_U8(DWRF_CFA_offset | DWRF_REG_FP); // x29 (frame pointer) saved - DWRF_UV(2); // At CFA-16 (2 * 8 = 16 bytes from CFA) - DWRF_U8(DWRF_CFA_offset | DWRF_REG_RA); // x30 (link register) saved - DWRF_UV(1); // At CFA-8 (1 * 8 = 8 bytes from CFA) - DWRF_U8(DWRF_CFA_advance_loc | 3); // Advance by 3 instructions (12 bytes) - DWRF_U8(DWRF_CFA_restore | DWRF_REG_RA); // Restore x30 - NO DWRF_UV() after this! - DWRF_U8(DWRF_CFA_restore | DWRF_REG_FP); // Restore x29 - NO DWRF_UV() after this! - DWRF_U8(DWRF_CFA_def_cfa_offset); // CFA = SP + 0 (stack restored) - DWRF_UV(0); // Back to original stack position -#else -# error "Unsupported target architecture" -#endif - - DWRF_ALIGNNOP(sizeof(uintptr_t)); // Align to pointer boundary - ) - - ctx->p = p; // Update context pointer to end of generated data - - /* Calculate and update the PC-relative offset in the FDE - * - * When perf processes the jitdump, it creates a synthesized DSO with this layout: - * - * Synthesized DSO Memory Layout: - * ┌─────────────────────────────────────────────────────────────┐ < code_start - * │ Code Section │ - * │ (round_up(code_size, 8) bytes) │ - * ├─────────────────────────────────────────────────────────────┤ < start of EH frame data - * │ EH Frame Data │ - * │ ┌─────────────────────────────────────────────────────┐ │ - * │ │ CIE data │ │ - * │ └─────────────────────────────────────────────────────┘ │ - * │ ┌─────────────────────────────────────────────────────┐ │ - * │ │ FDE Header: │ │ - * │ │ - CIE offset (4 bytes) │ │ - * │ │ - PC offset (4 bytes) <─ fde_offset_in_frame ─────┼────┼─> points to code_start - * │ │ - address range (4 bytes) │ │ (this specific field) - * │ │ CFI Instructions... │ │ - * │ └─────────────────────────────────────────────────────┘ │ - * ├─────────────────────────────────────────────────────────────┤ < reference_point - * │ EhFrameHeader │ - * │ (navigation metadata) │ - * └─────────────────────────────────────────────────────────────┘ - * - * The PC offset field in the FDE must contain the distance from itself to code_start: - * - * distance = code_start - fde_pc_field - * - * Where: - * fde_pc_field_location = reference_point - eh_frame_size + fde_offset_in_frame - * code_start_location = reference_point - eh_frame_size - round_up(code_size, 8) - * - * Therefore: - * distance = code_start_location - fde_pc_field_location - * = (ref - eh_frame_size - rounded_code_size) - (ref - eh_frame_size + fde_offset_in_frame) - * = -rounded_code_size - fde_offset_in_frame - * = -(round_up(code_size, 8) + fde_offset_in_frame) - * - * Note: fde_offset_in_frame is the offset from EH frame start to the PC offset field, - * - */ - if (ctx->fde_p != NULL) { - int32_t fde_offset_in_frame = (ctx->fde_p - ctx->startp); - int32_t rounded_code_size = round_up(ctx->code_size, 8); - int32_t pc_relative_offset = -(rounded_code_size + fde_offset_in_frame); - - - // Update the PC-relative offset in the FDE - *(int32_t*)ctx->fde_p = pc_relative_offset; - } -} - // ============================================================================= // JITDUMP INITIALIZATION // ============================================================================= @@ -1040,6 +427,12 @@ static void elf_init_ehframe(ELFObjectContext* ctx) { * Returns: Pointer to initialized state, or NULL on failure */ static void* perf_map_jit_init(void) { + PyMutex_Lock(&perf_jit_map_state.map_lock); + if (perf_jit_map_state.perf_map != NULL) { + PyMutex_Unlock(&perf_jit_map_state.map_lock); + return &perf_jit_map_state; + } + char filename[100]; int pid = getpid(); @@ -1049,6 +442,7 @@ static void* perf_map_jit_init(void) { /* Create/open the jitdump file with appropriate permissions */ const int fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0666); if (fd == -1) { + PyMutex_Unlock(&perf_jit_map_state.map_lock); return NULL; // Failed to create file } @@ -1056,6 +450,7 @@ static void* perf_map_jit_init(void) { const long page_size = sysconf(_SC_PAGESIZE); if (page_size == -1) { close(fd); + PyMutex_Unlock(&perf_jit_map_state.map_lock); return NULL; // Failed to get page size } @@ -1081,10 +476,14 @@ static void* perf_map_jit_init(void) { 0 // Offset 0 (first page) ); - if (perf_jit_map_state.mapped_buffer == NULL) { + if (perf_jit_map_state.mapped_buffer == MAP_FAILED) { + perf_jit_map_state.mapped_buffer = NULL; close(fd); + PyMutex_Unlock(&perf_jit_map_state.map_lock); return NULL; // Memory mapping failed } + (void)_PyAnnotateMemoryMap(perf_jit_map_state.mapped_buffer, page_size, + "cpython:perf_jit_trampoline"); #endif perf_jit_map_state.mapped_size = page_size; @@ -1093,6 +492,7 @@ static void* perf_map_jit_init(void) { perf_jit_map_state.perf_map = fdopen(fd, "w+"); if (perf_jit_map_state.perf_map == NULL) { close(fd); + PyMutex_Unlock(&perf_jit_map_state.map_lock); return NULL; // Failed to create FILE* } @@ -1108,28 +508,18 @@ static void* perf_map_jit_init(void) { /* Write the jitdump file header */ perf_map_jit_write_header(pid, perf_jit_map_state.perf_map); - /* - * Initialize thread synchronization lock - * - * Multiple threads may attempt to write to the jitdump file - * simultaneously. This lock ensures thread-safe access to the - * global jitdump state. - */ - perf_jit_map_state.map_lock = PyThread_allocate_lock(); - if (perf_jit_map_state.map_lock == NULL) { - fclose(perf_jit_map_state.perf_map); - return NULL; // Failed to create lock - } - /* Initialize code ID counter */ perf_jit_map_state.code_id = 0; + perf_jit_map_state.build_id_salt = + ((uint64_t)pid << 32) ^ (uint64_t)get_current_monotonic_ticks(); /* Calculate padding size based on actual unwind info requirements */ - size_t eh_frame_size = calculate_eh_frame_size(); + size_t eh_frame_size = _PyJitUnwind_EhFrameSize(0); size_t unwind_data_size = sizeof(EhFrameHeader) + eh_frame_size; - trampoline_api.code_padding = round_up(unwind_data_size, 16); + trampoline_api.code_padding = _Py_SIZE_ROUND_UP(unwind_data_size, 16); trampoline_api.code_alignment = 32; + PyMutex_Unlock(&perf_jit_map_state.map_lock); return &perf_jit_map_state; } @@ -1138,54 +528,31 @@ static void* perf_map_jit_init(void) { // ============================================================================= /* - * Write a complete jitdump entry for a Python function - * - * This is the main function called by Python's trampoline system whenever - * a new piece of JIT-compiled code needs to be recorded. It writes both - * the unwinding information and the code load event to the jitdump file. - * - * The function performs these steps: - * 1. Initialize jitdump system if not already done - * 2. Extract function name and filename from Python code object - * 3. Generate DWARF unwinding information - * 4. Write unwinding info event to jitdump file - * 5. Write code load event to jitdump file - * - * Args: - * state: Jitdump state (currently unused, uses global state) - * code_addr: Address where the compiled code resides - * code_size: Size of the compiled code in bytes - * co: Python code object containing metadata + * Write a complete jitdump entry for a code region with a provided name. * - * IMPORTANT: This function signature is part of Python's internal API - * and must not be changed without coordinating with core Python development. + * This shares the same implementation as the trampoline callback, but + * allows callers that don't have a PyCodeObject to reuse the jitdump + * infrastructure. */ -static void perf_map_jit_write_entry(void *state, const void *code_addr, - unsigned int code_size, PyCodeObject *co) +static void perf_map_jit_write_entry_with_name( + void *state, + const void *code_addr, + size_t code_size, + const char *entry, + const char *filename +) { /* Initialize jitdump system on first use */ - if (perf_jit_map_state.perf_map == NULL) { - void* ret = perf_map_jit_init(); - if(ret == NULL){ - return; // Initialization failed, silently abort - } + void* ret = perf_map_jit_init(); + if (ret == NULL) { + return; // Initialization failed, silently abort } - /* - * Extract function information from Python code object - * - * We create a human-readable function name by combining the qualified - * name (includes class/module context) with the filename. This helps - * developers identify functions in perf reports. - */ - const char *entry = ""; - if (co->co_qualname != NULL) { - entry = PyUnicode_AsUTF8(co->co_qualname); + if (entry == NULL) { + entry = ""; } - - const char *filename = ""; - if (co->co_filename != NULL) { - filename = PyUnicode_AsUTF8(co->co_filename); + if (filename == NULL) { + filename = ""; } /* @@ -1213,15 +580,20 @@ static void perf_map_jit_write_entry(void *state, const void *code_addr, * Without it, perf cannot generate accurate call graphs, especially * in optimized code where frame pointers may be omitted. */ - ELFObjectContext ctx; - char buffer[1024]; // Buffer for DWARF data (1KB should be sufficient) - ctx.code_size = code_size; - ctx.startp = ctx.p = (uint8_t*)buffer; - ctx.fde_p = NULL; // Initialize to NULL, will be set when FDE is written + uint8_t buffer[1024]; // Buffer for DWARF data (1KB should be sufficient) + size_t eh_frame_size = _PyJitUnwind_BuildEhFrame( + buffer, sizeof(buffer), code_addr, code_size, 0); + if (eh_frame_size == 0) { + PyMem_RawFree(perf_map_entry); + return; + } - /* Generate EH frame (Exception Handling frame) data */ - elf_init_ehframe(&ctx); - int eh_frame_size = ctx.p - ctx.startp; + /* + * A logical jitdump entry is written as multiple records and also consumes + * a process-global code_id. Serialize the whole sequence so concurrent JIT + * compilation cannot interleave records or reuse an ID. + */ + PyMutex_Lock(&perf_jit_map_state.map_lock); /* * Write Code Unwinding Information Event @@ -1239,12 +611,12 @@ static void perf_map_jit_write_entry(void *state, const void *code_addr, assert(ev2.unwind_data_size <= (uint64_t)trampoline_api.code_padding); ev2.eh_frame_hdr_size = sizeof(EhFrameHeader); - ev2.mapped_size = round_up(ev2.unwind_data_size, 16); // 16-byte alignment + ev2.mapped_size = _Py_SIZE_ROUND_UP(ev2.unwind_data_size, 16); // 16-byte alignment /* Calculate total event size with padding */ - int content_size = sizeof(ev2) + sizeof(EhFrameHeader) + eh_frame_size; - int padding_size = round_up(content_size, 8) - content_size; // 8-byte align - ev2.base.size = content_size + padding_size; + int content_size = (int)(sizeof(ev2) + sizeof(EhFrameHeader) + eh_frame_size); + int padding_size = (int)_Py_SIZE_ROUND_UP((size_t)content_size, 8) - content_size; // 8-byte align + ev2.base.size = (uint32_t)(content_size + padding_size); /* Write the unwinding info event header */ perf_map_jit_write_fully(&ev2, sizeof(ev2)); @@ -1258,20 +630,21 @@ static void perf_map_jit_write_entry(void *state, const void *code_addr, */ EhFrameHeader f; f.version = 1; - f.eh_frame_ptr_enc = DwarfSData4 | DwarfPcRel; // PC-relative signed 4-byte - f.fde_count_enc = DwarfUData4; // Unsigned 4-byte count - f.table_enc = DwarfSData4 | DwarfDataRel; // Data-relative signed 4-byte + f.eh_frame_ptr_enc = DWRF_EH_PE_sdata4 | DWRF_EH_PE_pcrel; + f.fde_count_enc = DWRF_EH_PE_udata4; + f.table_enc = DWRF_EH_PE_sdata4 | DWRF_EH_PE_datarel; /* Calculate relative offsets for EH frame navigation */ - f.eh_frame_ptr = -(eh_frame_size + 4 * sizeof(unsigned char)); + f.eh_frame_ptr = -(int32_t)(eh_frame_size + 4 * sizeof(unsigned char)); f.eh_fde_count = 1; // We generate exactly one FDE per function - f.from = -(round_up(code_size, 8) + eh_frame_size); - - int cie_size = ctx.eh_frame_p - ctx.startp; - f.to = -(eh_frame_size - cie_size); + f.from = -(int32_t)(_Py_SIZE_ROUND_UP(code_size, 8) + eh_frame_size); + uint32_t cie_payload_size; + memcpy(&cie_payload_size, buffer, sizeof(cie_payload_size)); + int cie_size = (int)(sizeof(cie_payload_size) + cie_payload_size); + f.to = -(int32_t)(eh_frame_size - cie_size); /* Write EH frame data and header */ - perf_map_jit_write_fully(ctx.startp, eh_frame_size); + perf_map_jit_write_fully(buffer, eh_frame_size); perf_map_jit_write_fully(&f, sizeof(f)); /* Write padding to maintain alignment */ @@ -1293,7 +666,12 @@ static void perf_map_jit_write_entry(void *state, const void *code_addr, ev.base.time_stamp = get_current_monotonic_ticks(); ev.process_id = getpid(); #if defined(__APPLE__) - pthread_threadid_np(NULL, &ev.thread_id); + // The jitdump format defines the thread id field as a 32-bit value, but + // pthread_threadid_np() returns a 64-bit id. Truncate it to 32 bits to + // keep the record layout identical to other platforms. + uint64_t thread_id = 0; + pthread_threadid_np(NULL, &thread_id); + ev.thread_id = (uint32_t)thread_id; #else ev.thread_id = syscall(SYS_gettid); // Get thread ID via system call #endif @@ -1308,12 +686,86 @@ static void perf_map_jit_write_entry(void *state, const void *code_addr, /* Write code load event and associated data */ perf_map_jit_write_fully(&ev, sizeof(ev)); perf_map_jit_write_fully(perf_map_entry, name_length+1); // Include null terminator - perf_map_jit_write_fully((void*)(base), size); // Copy actual machine code + /* + * Ensure each synthetic DSO has unique .text bytes. + * + * perf merges DSOs that share a build-id. Since trampolines can share + * identical code and unwind bytes, perf may resolve all JIT frames to + * the first symbol it saw (including entries from previous runs when + * build-id caching is enabled). Patch a small marker in the emitted + * bytes to make the build-id depend on a per-process salt and code id + * without modifying the live code. + */ + uint64_t marker = perf_jit_map_state.build_id_salt ^ + ((uint64_t)perf_jit_map_state.code_id << 32) ^ + (uint64_t)code_size; + if (size >= sizeof(marker)) { + size_t prefix = size - sizeof(marker); + perf_map_jit_write_fully((void *)(base), prefix); + perf_map_jit_write_fully(&marker, sizeof(marker)); + } + else if (size > 0) { + uint8_t tmp[sizeof(marker)]; + memcpy(tmp, (void *)(base), size); + for (size_t i = 0; i < size; i++) { + tmp[i] ^= (uint8_t)(marker >> (i * 8)); + } + perf_map_jit_write_fully(tmp, size); + } /* Clean up allocated memory */ + PyMutex_Unlock(&perf_jit_map_state.map_lock); PyMem_RawFree(perf_map_entry); } +/* + * Write a complete jitdump entry for a Python function + * + * This is the main function called by Python's trampoline system whenever + * a new piece of JIT-compiled code needs to be recorded. It writes both + * the unwinding information and the code load event to the jitdump file. + * + * The function performs these steps: + * 1. Initialize jitdump system if not already done + * 2. Extract function name and filename from Python code object + * 3. Generate DWARF unwinding information + * 4. Write unwinding info event to jitdump file + * 5. Write code load event to jitdump file + * + * Args: + * state: Jitdump state (currently unused, uses global state) + * code_addr: Address where the compiled code resides + * code_size: Size of the compiled code in bytes + * co: Python code object containing metadata + * + * IMPORTANT: This function signature is part of Python's internal API + * and must not be changed without coordinating with core Python development. + */ +static void perf_map_jit_write_entry(void *state, const void *code_addr, + size_t code_size, PyCodeObject *co) +{ + const char *entry = ""; + const char *filename = ""; + if (co != NULL) { + if (co->co_qualname != NULL) { + entry = PyUnicode_AsUTF8(co->co_qualname); + } + if (co->co_filename != NULL) { + filename = PyUnicode_AsUTF8(co->co_filename); + } + } + perf_map_jit_write_entry_with_name(state, code_addr, code_size, + entry, filename); +} + +void +_PyPerfJit_WriteNamedCode(const void *code_addr, size_t code_size, + const char *entry, const char *filename) +{ + perf_map_jit_write_entry_with_name( + NULL, code_addr, code_size, entry, filename); +} + // ============================================================================= // CLEANUP AND FINALIZATION // ============================================================================= @@ -1341,15 +793,12 @@ static int perf_map_jit_fini(void* state) { * writing to the file when we close it. This prevents corruption * and ensures all data is properly flushed. */ + PyMutex_Lock(&perf_jit_map_state.map_lock); if (perf_jit_map_state.perf_map != NULL) { - PyThread_acquire_lock(perf_jit_map_state.map_lock, 1); fclose(perf_jit_map_state.perf_map); // This also flushes buffers - PyThread_release_lock(perf_jit_map_state.map_lock); - - /* Clean up synchronization primitive */ - PyThread_free_lock(perf_jit_map_state.map_lock); perf_jit_map_state.perf_map = NULL; } + PyMutex_Unlock(&perf_jit_map_state.map_lock); /* * Unmap the memory region diff --git a/Python/perf_trampoline.c b/Python/perf_trampoline.c index a2da3c7d56df50..d90b789c2b5712 100644 --- a/Python/perf_trampoline.c +++ b/Python/perf_trampoline.c @@ -132,6 +132,7 @@ any DWARF information available for them). #include "Python.h" #include "pycore_ceval.h" // _PyPerf_Callbacks #include "pycore_interpframe.h" // _PyFrame_GetCode() +#include "pycore_mmap.h" // _PyAnnotateMemoryMap() #include "pycore_runtime.h" // _PyRuntime @@ -202,10 +203,53 @@ enum perf_trampoline_type { #define perf_map_file _PyRuntime.ceval.perf.map_file #define persist_after_fork _PyRuntime.ceval.perf.persist_after_fork #define perf_trampoline_type _PyRuntime.ceval.perf.perf_trampoline_type +#define prev_eval_frame _PyRuntime.ceval.perf.prev_eval_frame +#define trampoline_refcount _PyRuntime.ceval.perf.trampoline_refcount +#define code_watcher_id _PyRuntime.ceval.perf.code_watcher_id + +static void free_code_arenas(void); + +static void +perf_trampoline_clear_code_watcher(void) +{ + if (code_watcher_id >= 0) { + PyCode_ClearWatcher(code_watcher_id); + code_watcher_id = -1; + } + extra_code_index = -1; +} + +static void +perf_trampoline_reset_state(void) +{ + free_code_arenas(); + perf_trampoline_clear_code_watcher(); +} + +static int +perf_trampoline_code_watcher(PyCodeEvent event, PyCodeObject *co) +{ + if (event != PY_CODE_EVENT_DESTROY) { + return 0; + } + if (extra_code_index == -1) { + return 0; + } + py_trampoline f = NULL; + int ret = _PyCode_GetExtra((PyObject *)co, extra_code_index, (void **)&f); + if (ret != 0 || f == NULL) { + return 0; + } + trampoline_refcount--; + if (trampoline_refcount == 0) { + perf_trampoline_reset_state(); + } + return 0; +} static void perf_map_write_entry(void *state, const void *code_addr, - unsigned int code_size, PyCodeObject *co) + size_t code_size, PyCodeObject *co) { const char *entry = ""; if (co->co_qualname != NULL) { @@ -289,6 +333,7 @@ new_code_arena(void) perf_status = PERF_STATUS_FAILED; return -1; } + (void)_PyAnnotateMemoryMap(memory, mem_size, "cpython:perf_trampoline"); void *start = &_Py_trampoline_func_start; void *end = &_Py_trampoline_func_end; size_t code_size = end - start; @@ -404,12 +449,16 @@ py_trampoline_evaluator(PyThreadState *ts, _PyInterpreterFrame *frame, perf_code_arena->code_size, co); _PyCode_SetExtra((PyObject *)co, extra_code_index, (void *)new_trampoline); + trampoline_refcount++; f = new_trampoline; } assert(f != NULL); - return f(ts, frame, throw, _PyEval_EvalFrameDefault); + return f(ts, frame, throw, prev_eval_frame != NULL ? prev_eval_frame : _PyEval_EvalFrameDefault); default_eval: // Something failed, fall back to the default evaluator. + if (prev_eval_frame) { + return prev_eval_frame(ts, frame, throw); + } return _PyEval_EvalFrameDefault(ts, frame, throw); } #endif // PY_HAVE_PERF_TRAMPOLINE @@ -427,6 +476,7 @@ int PyUnstable_PerfTrampoline_CompileCode(PyCodeObject *co) } trampoline_api.write_state(trampoline_api.state, new_trampoline, perf_code_arena->code_size, co); + trampoline_refcount++; return _PyCode_SetExtra((PyObject *)co, extra_code_index, (void *)new_trampoline); } @@ -481,18 +531,16 @@ _PyPerfTrampoline_Init(int activate) { #ifdef PY_HAVE_PERF_TRAMPOLINE PyThreadState *tstate = _PyThreadState_GET(); - if (tstate->interp->eval_frame && - tstate->interp->eval_frame != py_trampoline_evaluator) { - PyErr_SetString(PyExc_RuntimeError, - "Trampoline cannot be initialized as a custom eval " - "frame is already present"); - return -1; + if (code_watcher_id == 0) { + // Initialize to -1 since 0 is a valid watcher ID + code_watcher_id = -1; } if (!activate) { - _PyInterpreterState_SetEvalFrameFunc(tstate->interp, NULL); + _PyInterpreterState_SetEvalFrameFunc(tstate->interp, prev_eval_frame); perf_status = PERF_STATUS_NO_INIT; } - else { + else if (tstate->interp->eval_frame != py_trampoline_evaluator) { + prev_eval_frame = _PyInterpreterState_GetEvalFrameFunc(tstate->interp); _PyInterpreterState_SetEvalFrameFunc(tstate->interp, py_trampoline_evaluator); extra_code_index = _PyEval_RequestCodeExtraIndex(NULL); if (extra_code_index == -1) { @@ -504,6 +552,13 @@ _PyPerfTrampoline_Init(int activate) if (new_code_arena() < 0) { return -1; } + code_watcher_id = PyCode_AddWatcher(perf_trampoline_code_watcher); + if (code_watcher_id < 0) { + PyErr_FormatUnraisable("Failed to register code watcher for perf trampoline"); + free_code_arenas(); + return -1; + } + trampoline_refcount = 1; // Base refcount held by the system perf_status = PERF_STATUS_OK; } #endif @@ -525,17 +580,19 @@ _PyPerfTrampoline_Fini(void) trampoline_api.free_state(trampoline_api.state); perf_trampoline_type = PERF_TRAMPOLINE_UNSET; } - extra_code_index = -1; + + // Prevent new trampolines from being created perf_status = PERF_STATUS_NO_INIT; -#endif - return 0; -} -void _PyPerfTrampoline_FreeArenas(void) { -#ifdef PY_HAVE_PERF_TRAMPOLINE - free_code_arenas(); + // Decrement base refcount. If refcount reaches 0, all code objects are already + // dead so clean up now. Otherwise, watcher remains active to clean up when last + // code object dies; extra_code_index stays valid so watcher can identify them. + trampoline_refcount--; + if (trampoline_refcount == 0) { + perf_trampoline_reset_state(); + } #endif - return; + return 0; } int @@ -567,6 +624,13 @@ _PyPerfTrampoline_AfterFork_Child(void) int was_active = _PyIsPerfTrampolineActive(); _PyPerfTrampoline_Fini(); if (was_active) { + // After fork, Fini may leave the old code watcher registered + // if trampolined code objects from the parent still exist + // (trampoline_refcount > 0). Clear it unconditionally before + // Init registers a new one, but keep the old arenas mapped: the + // child may still need to return through trampoline frames that + // were on the C stack at fork(). + perf_trampoline_clear_code_watcher(); _PyPerfTrampoline_Init(1); } } diff --git a/Python/preconfig.c b/Python/preconfig.c index e4cd10d9e3d40d..2c8c18284c1d2d 100644 --- a/Python/preconfig.c +++ b/Python/preconfig.c @@ -584,7 +584,7 @@ _Py_get_xoption(const PyWideStringList *xoptions, const wchar_t *name) for (Py_ssize_t i=0; i < xoptions->length; i++) { const wchar_t *option = xoptions->items[i]; size_t len; - wchar_t *sep = wcschr(option, L'='); + const wchar_t *sep = wcschr(option, L'='); if (sep != NULL) { len = (sep - option); } @@ -615,7 +615,7 @@ preconfig_init_utf8_mode(PyPreConfig *config, const _PyPreCmdline *cmdline) const wchar_t *xopt; xopt = _Py_get_xoption(&cmdline->xoptions, L"utf8"); if (xopt) { - wchar_t *sep = wcschr(xopt, L'='); + const wchar_t *sep = wcschr(xopt, L'='); if (sep) { xopt = sep + 1; if (wcscmp(xopt, L"1") == 0) { @@ -928,7 +928,7 @@ _PyPreConfig_Write(const PyPreConfig *src_config) return status; } - if (_PyRuntime.core_initialized) { + if (_Py_IsCoreInitialized()) { /* bpo-34008: Calling this functions after Py_Initialize() ignores the new configuration. */ return _PyStatus_OK(); diff --git a/Python/pyhash.c b/Python/pyhash.c index 216f437dd9a2d4..1eb890794a7544 100644 --- a/Python/pyhash.c +++ b/Python/pyhash.c @@ -17,7 +17,7 @@ _Py_HashSecret_t _Py_HashSecret = {{0}}; #if Py_HASH_ALGORITHM == Py_HASH_EXTERNAL -extern PyHash_FuncDef PyHash_Func; +Py_DEPRECATED(3.15) extern PyHash_FuncDef PyHash_Func; #else static PyHash_FuncDef PyHash_Func; #endif @@ -29,7 +29,7 @@ static Py_ssize_t hashstats[Py_HASH_STATS_MAX + 1] = {0}; #endif /* For numeric types, the hash of a number x is based on the reduction - of x modulo the prime P = 2**_PyHASH_BITS - 1. It's designed so that + of x modulo the prime P = 2**PyHASH_BITS - 1. It's designed so that hash(x) == hash(y) whenever x and y are numerically equal, even if x and y have different types. @@ -52,8 +52,8 @@ static Py_ssize_t hashstats[Py_HASH_STATS_MAX + 1] = {0}; If the result of the reduction is infinity (this is impossible for integers, floats and Decimals) then use the predefined hash value - _PyHASH_INF for x >= 0, or -_PyHASH_INF for x < 0, instead. - _PyHASH_INF and -_PyHASH_INF are also used for the + PyHASH_INF for x >= 0, or -PyHASH_INF for x < 0, instead. + PyHASH_INF and -PyHASH_INF are also used for the hashes of float and Decimal infinities. NaNs hash with a pointer hash. Having distinct hash values prevents @@ -65,16 +65,16 @@ static Py_ssize_t hashstats[Py_HASH_STATS_MAX + 1] = {0}; efficiently, even if the exponent of the binary or decimal number is large. The key point is that - reduce(x * y) == reduce(x) * reduce(y) (modulo _PyHASH_MODULUS) + reduce(x * y) == reduce(x) * reduce(y) (modulo PyHASH_MODULUS) provided that {reduce(x), reduce(y)} != {0, infinity}. The reduction of a binary or decimal float is never infinity, since the denominator is a power of 2 (for binary) or a divisor of a power of 10 (for decimal). So we have, for nonnegative x, - reduce(x * 2**e) == reduce(x) * reduce(2**e) % _PyHASH_MODULUS + reduce(x * 2**e) == reduce(x) * reduce(2**e) % PyHASH_MODULUS - reduce(x * 10**e) == reduce(x) * reduce(10**e) % _PyHASH_MODULUS + reduce(x * 10**e) == reduce(x) * reduce(10**e) % PyHASH_MODULUS and reduce(10**e) can be computed efficiently by the usual modular exponentiation algorithm. For reduce(2**e) it's even better: since @@ -92,7 +92,7 @@ _Py_HashDouble(PyObject *inst, double v) if (!isfinite(v)) { if (isinf(v)) - return v > 0 ? _PyHASH_INF : -_PyHASH_INF; + return v > 0 ? PyHASH_INF : -PyHASH_INF; else return PyObject_GenericHash(inst); } @@ -109,19 +109,19 @@ _Py_HashDouble(PyObject *inst, double v) and hexadecimal floating point. */ x = 0; while (m) { - x = ((x << 28) & _PyHASH_MODULUS) | x >> (_PyHASH_BITS - 28); + x = ((x << 28) & PyHASH_MODULUS) | x >> (PyHASH_BITS - 28); m *= 268435456.0; /* 2**28 */ e -= 28; y = (Py_uhash_t)m; /* pull out integer part */ m -= y; x += y; - if (x >= _PyHASH_MODULUS) - x -= _PyHASH_MODULUS; + if (x >= PyHASH_MODULUS) + x -= PyHASH_MODULUS; } - /* adjust for the exponent; first reduce it modulo _PyHASH_BITS */ - e = e >= 0 ? e % _PyHASH_BITS : _PyHASH_BITS-1-((-1-e) % _PyHASH_BITS); - x = ((x << e) & _PyHASH_MODULUS) | x >> (_PyHASH_BITS - e); + /* adjust for the exponent; first reduce it modulo PyHASH_BITS */ + e = e >= 0 ? e % PyHASH_BITS : PyHASH_BITS-1-((-1-e) % PyHASH_BITS); + x = ((x << e) & PyHASH_MODULUS) | x >> (PyHASH_BITS - e); x = x * sign; if (x == (Py_uhash_t)-1) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index e22a9cc1c75050..2f7f10f523c2c1 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -15,9 +15,11 @@ #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_interpolation.h" // _PyInterpolation_InitTypes() #include "pycore_long.h" // _PyLong_InitTypes() +#include "pycore_moduleobject.h" // _PyModule_InitModuleDictWatcher() #include "pycore_object.h" // _PyDebug_PrintTotalRefs() #include "pycore_obmalloc.h" // _PyMem_init_obmalloc() #include "pycore_optimizer.h" // _Py_Executors_InvalidateAll +#include "pycore_parking_lot.h" // _PyParkingLot #include "pycore_pathconfig.h" // _PyPathConfig_UpdateGlobal() #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_pylifecycle.h" // _PyErr_Print() @@ -26,8 +28,11 @@ #include "pycore_runtime.h" // _Py_ID() #include "pycore_runtime_init.h" // _PyRuntimeState_INIT #include "pycore_setobject.h" // _PySet_NextEntry() +#include "pycore_stackref.h" // PyStackRef_FromPyObjectBorrow() +#include "pycore_stats.h" // _PyStats_InterpInit() #include "pycore_sysmodule.h" // _PySys_ClearAttrString() -#include "pycore_traceback.h" // _Py_DumpTracebackThreads() +#include "pycore_traceback.h" // PyUnstable_TracebackThreads() +#include "pycore_tuple.h" // _PyTuple_FromPair #include "pycore_typeobject.h" // _PyTypes_InitTypes() #include "pycore_typevarobject.h" // _Py_clear_generic_types() #include "pycore_unicodeobject.h" // _PyUnicode_InitTypes() @@ -35,6 +40,10 @@ #include "pycore_warnings.h" // _PyWarnings_InitState() #include "pycore_weakref.h" // _PyWeakref_GET_REF() +#if defined(PYMALLOC_USE_HUGEPAGES) && defined(MS_WINDOWS) +#include <Windows.h> +#endif + #include "opcode.h" #include <locale.h> // setlocale() @@ -160,13 +169,13 @@ int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t) = \ int _Py_IsCoreInitialized(void) { - return _PyRuntime.core_initialized; + return _PyRuntimeState_GetCoreInitialized(&_PyRuntime); } int Py_IsInitialized(void) { - return _PyRuntime.initialized; + return _PyRuntimeState_GetInitialized(&_PyRuntime); } @@ -209,7 +218,10 @@ _Py_LegacyLocaleDetected(int warn) * we may also want to check for that explicitly. */ const char *ctype_loc = setlocale(LC_CTYPE, NULL); - return ctype_loc != NULL && strcmp(ctype_loc, "C") == 0; + if (ctype_loc == NULL) { + return 0; + } + return (strcmp(ctype_loc, "C") == 0 || strcmp(ctype_loc, "POSIX") == 0); #else /* Windows uses code pages instead of locales, so no locale is legacy */ return 0; @@ -477,12 +489,47 @@ pyinit_core_reconfigure(_PyRuntimeState *runtime, return _PyStatus_OK(); } +#if defined(PYMALLOC_USE_HUGEPAGES) && defined(MS_WINDOWS) +static PyStatus +get_huge_pages_privilege(void) +{ + HANDLE hToken; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) + { + return _PyStatus_ERR("failed to open process token"); + } + TOKEN_PRIVILEGES tp; + if (!LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &tp.Privileges[0].Luid)) + { + CloseHandle(hToken); + return _PyStatus_ERR("failed to lookup SeLockMemoryPrivilege for huge pages"); + } + tp.PrivilegeCount = 1; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + // AdjustTokenPrivileges can return with nonzero status (i.e. success) + // but without having all privileges adjusted (ERROR_NOT_ALL_ASSIGNED). + BOOL status = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); + DWORD error = GetLastError(); + if (!status || (error != ERROR_SUCCESS)) + { + CloseHandle(hToken); + return _PyStatus_ERR( + "SeLockMemoryPrivilege not held; " + "grant it via Local Security Policy or disable PYTHON_PYMALLOC_HUGEPAGES"); + } + if (!CloseHandle(hToken)) + { + return _PyStatus_ERR("failed to close process token handle"); + } + return _PyStatus_OK(); +} +#endif static PyStatus pycore_init_runtime(_PyRuntimeState *runtime, const PyConfig *config) { - if (runtime->initialized) { + if (_PyRuntimeState_GetInitialized(runtime)) { return _PyStatus_ERR("main interpreter already initialized"); } @@ -491,6 +538,15 @@ pycore_init_runtime(_PyRuntimeState *runtime, return status; } +#if defined(PYMALLOC_USE_HUGEPAGES) && defined(MS_WINDOWS) + if (runtime->allocators.use_hugepages) { + status = get_huge_pages_privilege(); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + } +#endif + /* Py_Finalize leaves _Py_Finalizing set in order to help daemon * threads behave a little more gracefully at interpreter shutdown. * We clobber it here so the new interpreter can start with a clean @@ -503,6 +559,7 @@ pycore_init_runtime(_PyRuntimeState *runtime, _PyRuntimeState_SetFinalizing(runtime, NULL); _Py_InitVersion(); + _Py_DumpTraceback_Init(); status = _Py_HashRandomization_Init(config); if (_PyStatus_EXCEPTION(status)) { @@ -624,6 +681,11 @@ pycore_create_interpreter(_PyRuntimeState *runtime, _PyInterpreterState_SetWhence(interp, _PyInterpreterState_WHENCE_RUNTIME); interp->_ready = 1; + /* Initialize the module dict watcher early, before any modules are created */ + if (_PyModule_InitModuleDictWatcher(interp) != 0) { + return _PyStatus_ERR("failed to initialize module dict watcher"); + } + status = _PyConfig_Copy(&interp->config, src_config); if (_PyStatus_EXCEPTION(status)) { return status; @@ -652,6 +714,14 @@ pycore_create_interpreter(_PyRuntimeState *runtime, return status; } +#ifdef Py_STATS + // initialize pystats. This must be done after the settings are loaded. + status = _PyStats_InterpInit(interp); + if (_PyStatus_EXCEPTION(status)) { + return status; + } +#endif + // initialize the interp->obmalloc state. This must be done after // the settings are loaded (so that feature_flags are set) but before // any calls are made to obmalloc functions. @@ -684,8 +754,6 @@ pycore_init_global_objects(PyInterpreterState *interp) { PyStatus status; - _PyFloat_InitState(interp); - status = _PyUnicode_InitGlobalObjects(interp); if (_PyStatus_EXCEPTION(status)) { return status; @@ -811,14 +879,28 @@ pycore_init_builtins(PyThreadState *tstate) goto error; } - interp->common_consts[CONSTANT_ASSERTIONERROR] = PyExc_AssertionError; - interp->common_consts[CONSTANT_NOTIMPLEMENTEDERROR] = PyExc_NotImplementedError; - interp->common_consts[CONSTANT_BUILTIN_TUPLE] = (PyObject*)&PyTuple_Type; - interp->common_consts[CONSTANT_BUILTIN_ALL] = all; - interp->common_consts[CONSTANT_BUILTIN_ANY] = any; - - for (int i=0; i < NUM_COMMON_CONSTANTS; i++) { - assert(interp->common_consts[i] != NULL); + PyObject *common_objs[NUM_COMMON_CONSTANTS] = {NULL}; + common_objs[CONSTANT_ASSERTIONERROR] = PyExc_AssertionError; + common_objs[CONSTANT_NOTIMPLEMENTEDERROR] = PyExc_NotImplementedError; + common_objs[CONSTANT_BUILTIN_TUPLE] = (PyObject *)&PyTuple_Type; + common_objs[CONSTANT_BUILTIN_ALL] = all; + common_objs[CONSTANT_BUILTIN_ANY] = any; + common_objs[CONSTANT_BUILTIN_LIST] = (PyObject *)&PyList_Type; + common_objs[CONSTANT_BUILTIN_SET] = (PyObject *)&PySet_Type; + common_objs[CONSTANT_NONE] = Py_None; + common_objs[CONSTANT_EMPTY_STR] = + Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_STR); + common_objs[CONSTANT_TRUE] = Py_True; + common_objs[CONSTANT_FALSE] = Py_False; + common_objs[CONSTANT_MINUS_ONE] = + (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS - 1]; + common_objs[CONSTANT_BUILTIN_FROZENSET] = (PyObject *)&PyFrozenSet_Type; + common_objs[CONSTANT_EMPTY_TUPLE] = + Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_TUPLE); + for (int i = 0; i < NUM_COMMON_CONSTANTS; i++) { + assert(common_objs[i] != NULL); + _Py_SetImmortal(common_objs[i]); + interp->common_consts[i] = PyStackRef_FromPyObjectBorrow(common_objs[i]); } PyObject *list_append = _PyType_Lookup(&PyList_Type, &_Py_ID(append)); @@ -833,6 +915,10 @@ pycore_init_builtins(PyThreadState *tstate) } interp->callable_cache.object__getattribute__ = object__getattribute__; + if (_PyType_InitSlotDefs(interp) < 0) { + return _PyStatus_ERR("failed to init slotdefs"); + } + if (_PyBuiltins_AddExceptions(bimod) < 0) { return _PyStatus_ERR("failed to add exceptions to builtins"); } @@ -957,7 +1043,7 @@ pyinit_config(_PyRuntimeState *runtime, } /* Only when we get here is the runtime core fully initialized */ - runtime->core_initialized = 1; + _PyRuntimeState_SetCoreInitialized(runtime, 1); return _PyStatus_OK(); } @@ -1143,6 +1229,54 @@ pyinit_main_reconfigure(PyThreadState *tstate) #ifdef Py_DEBUG +// Equivalent to the Python code: +// +// for part in attr.split('.'): +// obj = getattr(obj, part) +static PyObject* +presite_resolve_name(PyObject *obj, PyObject *attr) +{ + obj = Py_NewRef(obj); + attr = Py_NewRef(attr); + PyObject *res; + + while (1) { + Py_ssize_t len = PyUnicode_GET_LENGTH(attr); + Py_ssize_t pos = PyUnicode_FindChar(attr, '.', 0, len, 1); + if (pos < 0) { + break; + } + + PyObject *name = PyUnicode_Substring(attr, 0, pos); + if (name == NULL) { + goto error; + } + res = PyObject_GetAttr(obj, name); + Py_DECREF(name); + if (res == NULL) { + goto error; + } + Py_SETREF(obj, res); + + PyObject *suffix = PyUnicode_Substring(attr, pos + 1, len); + if (suffix == NULL) { + goto error; + } + Py_SETREF(attr, suffix); + } + + res = PyObject_GetAttr(obj, attr); + Py_DECREF(obj); + Py_DECREF(attr); + return res; + +error: + Py_DECREF(obj); + Py_DECREF(attr); + return NULL; +} + + static void run_presite(PyThreadState *tstate) { @@ -1153,22 +1287,68 @@ run_presite(PyThreadState *tstate) return; } - PyObject *presite_modname = PyUnicode_FromWideChar( - config->run_presite, - wcslen(config->run_presite) - ); - if (presite_modname == NULL) { - fprintf(stderr, "Could not convert pre-site module name to unicode\n"); + PyObject *presite = PyUnicode_FromWideChar(config->run_presite, -1); + if (presite == NULL) { + fprintf(stderr, "Could not convert pre-site command to Unicode\n"); + _PyErr_Print(tstate); + return; + } + + // Accept "mod_name" and "mod_name:func_name" entry point syntax + Py_ssize_t len = PyUnicode_GET_LENGTH(presite); + Py_ssize_t pos = PyUnicode_FindChar(presite, ':', 0, len, 1); + PyObject *mod_name = NULL; + PyObject *func_name = NULL; + PyObject *module = NULL; + if (pos > 0) { + mod_name = PyUnicode_Substring(presite, 0, pos); + if (mod_name == NULL) { + goto error; + } + + func_name = PyUnicode_Substring(presite, pos + 1, len); + if (func_name == NULL) { + goto error; + } } else { - PyObject *presite = PyImport_Import(presite_modname); - if (presite == NULL) { - fprintf(stderr, "pre-site import failed:\n"); - _PyErr_Print(tstate); + mod_name = Py_NewRef(presite); + } + + // mod_name can contain dots (ex: "math.integer") + module = PyImport_Import(mod_name); + if (module == NULL) { + goto error; + } + + if (func_name != NULL) { + PyObject *func = presite_resolve_name(module, func_name); + if (func == NULL) { + goto error; } - Py_XDECREF(presite); - Py_DECREF(presite_modname); + + PyObject *res = PyObject_CallNoArgs(func); + Py_DECREF(func); + if (res == NULL) { + goto error; + } + Py_DECREF(res); } + + Py_DECREF(presite); + Py_DECREF(mod_name); + Py_XDECREF(func_name); + Py_DECREF(module); + return; + +error: + fprintf(stderr, "pre-site failed:\n"); + _PyErr_Print(tstate); + + Py_DECREF(presite); + Py_XDECREF(mod_name); + Py_XDECREF(func_name); + Py_XDECREF(module); } #endif @@ -1190,7 +1370,7 @@ init_interp_main(PyThreadState *tstate) * or pure Python code in the standard library won't work. */ if (is_main_interp) { - interp->runtime->initialized = 1; + _PyRuntimeState_SetInitialized(interp->runtime, 1); } return _PyStatus_OK(); } @@ -1302,8 +1482,6 @@ init_interp_main(PyThreadState *tstate) Py_XDECREF(warnings_module); } Py_XDECREF(warnoptions); - - interp->runtime->initialized = 1; } if (config->site_import) { @@ -1313,6 +1491,18 @@ init_interp_main(PyThreadState *tstate) } } + // Initialize lazy imports based on configuration. Do this after site + // module is imported to avoid circular imports during startup. + if (config->lazy_imports == 0) { + return _PyStatus_ERR("PyConfig.lazy_imports=0 is not supported"); + } + if (config->lazy_imports == 1) { + if (PyImport_SetLazyImportsMode(PyImport_LAZY_ALL) < 0) { + return _PyStatus_ERR("failed to set lazy imports mode"); + } + } + // If config->lazy_imports == -1, use the default mode, no change needed. + if (is_main_interp) { #ifndef MS_WINDOWS emit_stderr_warning_for_legacy_locale(interp->runtime); @@ -1383,6 +1573,10 @@ init_interp_main(PyThreadState *tstate) assert(!_PyErr_Occurred(tstate)); + if (is_main_interp) { + _PyRuntimeState_SetInitialized(interp->runtime, 1); + } + return _PyStatus_OK(); } @@ -1402,11 +1596,11 @@ static PyStatus pyinit_main(PyThreadState *tstate) { PyInterpreterState *interp = tstate->interp; - if (!interp->runtime->core_initialized) { + if (!_PyRuntimeState_GetCoreInitialized(interp->runtime)) { return _PyStatus_ERR("runtime core not initialized"); } - if (interp->runtime->initialized) { + if (_PyRuntimeState_GetInitialized(interp->runtime)) { return pyinit_main_reconfigure(tstate); } @@ -1454,19 +1648,12 @@ Py_InitializeFromConfig(const PyConfig *config) void Py_InitializeEx(int install_sigs) { - PyStatus status; - - status = _PyRuntime_Initialize(); - if (_PyStatus_EXCEPTION(status)) { - Py_ExitStatusException(status); - } - _PyRuntimeState *runtime = &_PyRuntime; - - if (runtime->initialized) { + if (Py_IsInitialized()) { /* bpo-33932: Calling Py_Initialize() twice does nothing. */ return; } + PyStatus status; PyConfig config; _PyConfig_InitCompatConfig(&config); @@ -1486,6 +1673,18 @@ Py_Initialize(void) } +PyStatus +_Py_InitializeMain(void) +{ + PyStatus status = _PyRuntime_Initialize(); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + PyThreadState *tstate = _PyThreadState_GET(); + return pyinit_main(tstate); +} + + static void finalize_modules_delete_special(PyThreadState *tstate, int verbose) { @@ -1559,7 +1758,7 @@ finalize_remove_modules(PyObject *modules, int verbose) if (weaklist != NULL) { \ PyObject *wr = PyWeakref_NewRef(mod, NULL); \ if (wr) { \ - PyObject *tup = PyTuple_Pack(2, name, wr); \ + PyObject *tup = _PyTuple_FromPair(name, wr); \ if (!tup || PyList_Append(weaklist, tup) < 0) { \ PyErr_FormatUnraisable("Exception ignored while removing modules"); \ } \ @@ -1600,6 +1799,7 @@ finalize_remove_modules(PyObject *modules, int verbose) PyObject *value = PyObject_GetItem(modules, key); if (value == NULL) { PyErr_FormatUnraisable("Exception ignored while removing modules"); + Py_DECREF(key); continue; } CLEAR_MODULE(key, value); @@ -1702,8 +1902,15 @@ finalize_modules(PyThreadState *tstate) // Invalidate all executors and turn off JIT: interp->jit = false; + interp->compiling = false; #ifdef _Py_TIER2 _Py_Executors_InvalidateAll(interp, 0); + PyMem_Free(interp->executor_blooms); + PyMem_Free(interp->executor_ptrs); + interp->executor_blooms = NULL; + interp->executor_ptrs = NULL; + interp->executor_count = 0; + interp->executor_capacity = 0; #endif // Stop watching __builtin__ modifications @@ -1778,6 +1985,9 @@ finalize_modules(PyThreadState *tstate) // initialization API) _PyImport_ClearModulesByIndex(interp); + // Clear the dict of lazily loaded module nname to submodule names + _PyImport_ClearLazyModules(interp); + // Clear and delete the modules directory. Actual modules will // still be there only if imported during the execution of some // destructor. @@ -1906,6 +2116,7 @@ finalize_interp_clear(PyThreadState *tstate) _PyXI_Fini(tstate->interp); _PyExc_ClearExceptionGroupType(tstate->interp); _Py_clear_generic_types(tstate->interp); + _PyTypes_FiniCachedDescriptors(tstate->interp); /* Clear interpreter state and all thread states */ _PyInterpreterState_Clear(tstate); @@ -1922,7 +2133,6 @@ finalize_interp_clear(PyThreadState *tstate) _PyArg_Fini(); _Py_ClearFileSystemEncoding(); _PyPerfTrampoline_Fini(); - _PyPerfTrampoline_FreeArenas(); } finalize_interp_types(tstate->interp); @@ -1999,6 +2209,7 @@ resolve_final_tstate(_PyRuntimeState *runtime) } else { /* Fall back to the current tstate. It's better than nothing. */ + // XXX No it's not main_tstate = tstate; } } @@ -2010,13 +2221,174 @@ resolve_final_tstate(_PyRuntimeState *runtime) return main_tstate; } +#ifdef Py_GIL_DISABLED +#define ASSERT_WORLD_STOPPED(interp) assert(interp->runtime->stoptheworld.world_stopped) +#else +#define ASSERT_WORLD_STOPPED(interp) +#endif + +static int +interp_has_threads(PyInterpreterState *interp) +{ + /* This needs to check for non-daemon threads only, otherwise we get stuck + * in an infinite loop. */ + assert(interp != NULL); + ASSERT_HEAD_IS_LOCKED(interp->runtime); + assert(interp->threads.head != NULL); + if (interp->threads.head->next == NULL) { + // No other threads active, easy way out. + return 0; + } + + _Py_FOR_EACH_TSTATE_UNLOCKED(interp, tstate) { + if (tstate->_whence == _PyThreadState_WHENCE_THREADING) { + return 1; + } + } + + return 0; +} + +static int +interp_has_pending_calls(PyInterpreterState *interp) +{ + assert(interp != NULL); + ASSERT_WORLD_STOPPED(interp); + return interp->ceval.pending.npending != 0; +} + +static int +interp_has_atexit_callbacks(PyInterpreterState *interp) +{ + assert(interp != NULL); + assert(interp->atexit.callbacks != NULL); + ASSERT_WORLD_STOPPED(interp); + assert(PyList_CheckExact(interp->atexit.callbacks)); + return PyList_GET_SIZE(interp->atexit.callbacks) != 0; +} + +static int +runtime_has_subinterpreters(_PyRuntimeState *runtime) +{ + assert(runtime != NULL); + PyInterpreterState *interp = runtime->interpreters.head; + return interp->next != NULL; +} + +static void +make_pre_finalization_calls(PyThreadState *tstate, int subinterpreters) +{ + assert(tstate != NULL); + PyInterpreterState *interp = tstate->interp; + assert(_Py_atomic_load_uintptr(&interp->finalization_guards) != _PyInterpreterGuard_GUARDS_NOT_ALLOWED); + /* Each of these functions can start one another, e.g. a pending call + * could start a thread or vice versa. To ensure that we properly clean + * call everything, we run these in a loop until none of them run anything. */ + for (;;) { + assert(!interp->runtime->stoptheworld.world_stopped); + + // Wrap up existing "threading"-module-created, non-daemon threads. + wait_for_thread_shutdown(tstate); + + // Make any remaining pending calls. + _Py_FinishPendingCalls(tstate); + + /* The interpreter is still entirely intact at this point, and the + * exit funcs may be relying on that. In particular, if some thread + * or exit func is still waiting to do an import, the import machinery + * expects Py_IsInitialized() to return true. So don't say the + * runtime is uninitialized until after the exit funcs have run. + * Note that Threading.py uses an exit func to do a join on all the + * threads created thru it, so this also protects pending imports in + * the threads created via Threading. + */ + + _PyAtExit_Call(tstate->interp); + + if (subinterpreters) { + /* Clean up any lingering subinterpreters. + * Two preconditions need to be met here: + * 1. This has to happen before _PyRuntimeState_SetFinalizing is + * called, or else threads might get prematurely blocked. + * 2. The world must not be stopped, as finalizers can run. + */ + finalize_subinterpreters(); + } + + // This is used as a throttle to prevent constant spinning while + // on finalization guards. + for (;;) { + uintptr_t num_guards = _Py_atomic_load_uintptr(&interp->finalization_guards); + if (num_guards == 0) { + break; + } + + int ret = _PyParkingLot_Park(&interp->finalization_guards, + &num_guards, sizeof(num_guards), -1, + NULL, /*detach=*/1); + if (ret == Py_PARK_OK) { + break; + } + else if (ret == Py_PARK_INTR) { + if (PyErr_CheckSignals() < 0) { + int fatal = PyErr_ExceptionMatches(PyExc_KeyboardInterrupt); + PyErr_FormatUnraisable("Exception ignored while waiting on finalization guards"); + if (fatal) { + fputs("Interrupted while waiting on finalization guards\n", stderr); + exit(1); + } + } + assert(!PyErr_Occurred()); + } + else { + assert(ret == Py_PARK_AGAIN); + } + } + + /* Stop the world to prevent other threads from creating threads or + * atexit callbacks. On the default build, this is simply locked by + * the GIL. For pending calls, we acquire the dedicated mutex, because + * Py_AddPendingCall() can be called without an attached thread state. + */ + PyMutex_Lock(&interp->ceval.pending.mutex); + _PyEval_StopTheWorldAll(interp->runtime); + + HEAD_LOCK(interp->runtime); + int has_subinterpreters = subinterpreters + ? runtime_has_subinterpreters(interp->runtime) + : 0; + uintptr_t guards_expected = 0; + int should_continue = (interp_has_threads(interp) + || interp_has_atexit_callbacks(interp) + || interp_has_pending_calls(interp) + || has_subinterpreters); + + if (!should_continue) { + // We only want to prevent new guards once we're sure that we + // won't be running another pre-finalization cycle. + if (_Py_atomic_compare_exchange_uintptr(&interp->finalization_guards, + &guards_expected, + _PyInterpreterGuard_GUARDS_NOT_ALLOWED) == 1) { + HEAD_UNLOCK(interp->runtime); + break; + } + } + HEAD_UNLOCK(interp->runtime); + _PyEval_StartTheWorldAll(interp->runtime); + PyMutex_Unlock(&interp->ceval.pending.mutex); + } + assert(PyMutex_IsLocked(&interp->ceval.pending.mutex)); + assert(_Py_atomic_load_uintptr(&interp->finalization_guards) == _PyInterpreterGuard_GUARDS_NOT_ALLOWED); + ASSERT_WORLD_STOPPED(interp); +} + static int _Py_Finalize(_PyRuntimeState *runtime) { int status = 0; /* Bail out early if already finalized (or never initialized). */ - if (!runtime->initialized) { + if (!_PyRuntimeState_GetInitialized(runtime)) { return status; } @@ -2026,23 +2398,8 @@ _Py_Finalize(_PyRuntimeState *runtime) // Block some operations. tstate->interp->finalizing = 1; - // Wrap up existing "threading"-module-created, non-daemon threads. - wait_for_thread_shutdown(tstate); - - // Make any remaining pending calls. - _Py_FinishPendingCalls(tstate); - - /* The interpreter is still entirely intact at this point, and the - * exit funcs may be relying on that. In particular, if some thread - * or exit func is still waiting to do an import, the import machinery - * expects Py_IsInitialized() to return true. So don't say the - * runtime is uninitialized until after the exit funcs have run. - * Note that Threading.py uses an exit func to do a join on all the - * threads created thru it, so this also protects pending imports in - * the threads created via Threading. - */ - - _PyAtExit_Call(tstate->interp); + // This call stops the world and takes the pending calls lock. + make_pre_finalization_calls(tstate, /*subinterpreters=*/1); assert(_PyThreadState_GET() == tstate); @@ -2060,14 +2417,14 @@ _Py_Finalize(_PyRuntimeState *runtime) #endif /* Ensure that remaining threads are detached */ - _PyEval_StopTheWorldAll(runtime); + ASSERT_WORLD_STOPPED(tstate->interp); /* Remaining daemon threads will be trapped in PyThread_hang_thread when they attempt to take the GIL (ex: PyEval_RestoreThread()). */ _PyInterpreterState_SetFinalizing(tstate->interp, tstate); _PyRuntimeState_SetFinalizing(runtime, tstate); - runtime->initialized = 0; - runtime->core_initialized = 0; + _PyRuntimeState_SetInitialized(runtime, 0); + _PyRuntimeState_SetCoreInitialized(runtime, 0); // XXX Call something like _PyImport_Disable() here? @@ -2081,6 +2438,7 @@ _Py_Finalize(_PyRuntimeState *runtime) _PyThreadState_SetShuttingDown(p); } _PyEval_StartTheWorldAll(runtime); + PyMutex_Unlock(&tstate->interp->ceval.pending.mutex); /* Clear frames of other threads to call objects destructors. Destructors will be called in the current Python thread. Since @@ -2131,12 +2489,10 @@ _Py_Finalize(_PyRuntimeState *runtime) _PyImport_FiniExternal(tstate->interp); finalize_modules(tstate); - /* Clean up any lingering subinterpreters. */ - finalize_subinterpreters(); - /* Print debug stats if any */ _PyEval_Fini(); + /* Flush sys.stdout and sys.stderr (again, in case more was printed) */ if (flush_std_files() < 0) { status = -1; @@ -2216,7 +2572,6 @@ _Py_Finalize(_PyRuntimeState *runtime) finalize_interp_clear(tstate); - #ifdef Py_TRACE_REFS /* Display addresses (& refcnts) of all objects still alive. * An address can be used to find the repr of the object, printed @@ -2290,25 +2645,29 @@ new_interpreter(PyThreadState **tstate_p, } _PyRuntimeState *runtime = &_PyRuntime; - if (!runtime->initialized) { + if (!_PyRuntimeState_GetInitialized(runtime)) { return _PyStatus_ERR("Py_Initialize must be called first"); } /* Issue #10915, #15751: The GIL API doesn't work with multiple interpreters: disable PyGILState_Check(). */ - runtime->gilstate.check_enabled = 0; + _Py_atomic_store_int_relaxed(&runtime->gilstate.check_enabled, 0); - PyInterpreterState *interp = PyInterpreterState_New(); + // XXX Might new_interpreter() have been called without the GIL held? + PyThreadState *save_tstate = _PyThreadState_GET(); + PyThreadState *tstate = NULL; + PyInterpreterState *interp; + status = _PyInterpreterState_New(save_tstate, &interp); if (interp == NULL) { - *tstate_p = NULL; - return _PyStatus_OK(); + goto error; } _PyInterpreterState_SetWhence(interp, whence); interp->_ready = 1; - // XXX Might new_interpreter() have been called without the GIL held? - PyThreadState *save_tstate = _PyThreadState_GET(); - PyThreadState *tstate = NULL; + /* Initialize the module dict watcher early, before any modules are created */ + if (_PyModule_InitModuleDictWatcher(interp) != 0) { + goto error; + } /* From this point until the init_interp_create_gil() call, we must not do anything that requires that the GIL be held @@ -2349,6 +2708,14 @@ new_interpreter(PyThreadState **tstate_p, return status; } +#ifdef Py_STATS + // initialize pystats. This must be done after the settings are loaded. + status = _PyStats_InterpInit(interp); + if (_PyStatus_EXCEPTION(status)) { + return status; + } +#endif + // initialize the interp->obmalloc state. This must be done after // the settings are loaded (so that feature_flags are set) but before // any calls are made to obmalloc functions. @@ -2385,7 +2752,7 @@ new_interpreter(PyThreadState **tstate_p, *tstate_p = NULL; if (tstate != NULL) { Py_EndInterpreter(tstate); - } else { + } else if (interp != NULL) { PyInterpreterState_Delete(interp); } if (save_tstate != NULL) { @@ -2415,9 +2782,8 @@ Py_NewInterpreter(void) return tstate; } -/* Delete an interpreter and its last thread. This requires that the - given thread state is current, that the thread has no remaining - frames, and that it is its interpreter's only remaining thread. +/* Delete an interpreter. This requires that the given thread state + is current, and that the thread has no remaining frames. It is a fatal error to violate these constraints. (Py_FinalizeEx() doesn't have these constraints -- it zaps @@ -2435,27 +2801,28 @@ Py_EndInterpreter(PyThreadState *tstate) if (tstate != _PyThreadState_GET()) { Py_FatalError("thread is not current"); } - if (tstate->current_frame != NULL) { + if (tstate->current_frame != tstate->base_frame) { Py_FatalError("thread still has a frame"); } interp->finalizing = 1; - // Wrap up existing "threading"-module-created, non-daemon threads. - wait_for_thread_shutdown(tstate); - - // Make any remaining pending calls. - _Py_FinishPendingCalls(tstate); - - _PyAtExit_Call(tstate->interp); - - if (tstate != interp->threads.head || tstate->next != NULL) { - Py_FatalError("not the last thread"); - } + // This call stops the world and takes the pending calls lock. + make_pre_finalization_calls(tstate, /*subinterpreters=*/0); + ASSERT_WORLD_STOPPED(interp); /* Remaining daemon threads will automatically exit when they attempt to take the GIL (ex: PyEval_RestoreThread()). */ _PyInterpreterState_SetFinalizing(interp, tstate); + PyThreadState *list = _PyThreadState_RemoveExcept(tstate); + for (PyThreadState *p = list; p != NULL; p = p->next) { + _PyThreadState_SetShuttingDown(p); + } + + _PyEval_StartTheWorldAll(interp->runtime); + PyMutex_Unlock(&interp->ceval.pending.mutex); + _PyThreadState_DeleteList(list, /*is_after_fork=*/0); + // XXX Call something like _PyImport_Disable() here? _PyImport_FiniExternal(tstate->interp); @@ -2485,6 +2852,8 @@ finalize_subinterpreters(void) PyInterpreterState *main_interp = _PyInterpreterState_Main(); assert(final_tstate->interp == main_interp); _PyRuntimeState *runtime = main_interp->runtime; + assert(!runtime->stoptheworld.world_stopped); + assert(_PyRuntimeState_GetFinalizing(runtime) == NULL); struct pyinterpreters *interpreters = &runtime->interpreters; /* Get the first interpreter in the list. */ @@ -2504,7 +2873,7 @@ finalize_subinterpreters(void) (void)PyErr_WarnEx( PyExc_RuntimeWarning, "remaining subinterpreters; " - "destroy them with _interpreters.destroy()", + "close them with Interpreter.close()", 0); /* Swap out the current tstate, which we know must belong @@ -2513,27 +2882,17 @@ finalize_subinterpreters(void) /* Clean up all remaining subinterpreters. */ while (interp != NULL) { - assert(!_PyInterpreterState_IsRunningMain(interp)); - - /* Find the tstate to use for fini. We assume the interpreter - will have at most one tstate at this point. */ - PyThreadState *tstate = interp->threads.head; - if (tstate != NULL) { - /* Ideally we would be able to use tstate as-is, and rely - on it being in a ready state: no exception set, not - running anything (tstate->current_frame), matching the - current thread ID (tstate->thread_id). To play it safe, - we always delete it and use a fresh tstate instead. */ - assert(tstate != final_tstate); - _PyThreadState_Attach(tstate); - PyThreadState_Clear(tstate); - _PyThreadState_Detach(tstate); - PyThreadState_Delete(tstate); - } - tstate = _PyThreadState_NewBound(interp, _PyThreadState_WHENCE_FINI); + /* Make a tstate for finalization. */ + PyThreadState *tstate = _PyThreadState_NewBound(interp, _PyThreadState_WHENCE_FINI); + if (tstate == NULL) { + // XXX Some graceful way to always get a thread state? + Py_FatalError("thread state allocation failed"); + } - /* Destroy the subinterpreter. */ + /* Enter the subinterpreter. */ _PyThreadState_Attach(tstate); + + /* Destroy the subinterpreter. */ Py_EndInterpreter(tstate); assert(_PyThreadState_GET() == NULL); @@ -2968,7 +3327,9 @@ apple_log_write_impl(PyObject *self, PyObject *args) // Pass the user-provided text through explicit %s formatting // to avoid % literals being interpreted as a formatting directive. - os_log_with_type(OS_LOG_DEFAULT, logtype, "%s", text); + // Using {public} ensures "dynamic" string messages are visible + // in the log without special configuration. + os_log_with_type(OS_LOG_DEFAULT, logtype, "%{public}s", text); Py_RETURN_NONE; } @@ -3027,9 +3388,9 @@ _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp, /* display the current Python stack */ #ifndef Py_GIL_DISABLED - _Py_DumpTracebackThreads(fd, interp, tstate); + PyUnstable_DumpTracebackThreads(fd, interp, tstate, 0); #else - _Py_DumpTraceback(fd, tstate); + PyUnstable_DumpTraceback(fd, tstate); #endif } @@ -3126,10 +3487,10 @@ fatal_error_dump_runtime(int fd, _PyRuntimeState *runtime) _Py_DumpHexadecimal(fd, (uintptr_t)finalizing, sizeof(finalizing) * 2); PUTS(fd, ")"); } - else if (runtime->initialized) { + else if (_PyRuntimeState_GetInitialized(runtime)) { PUTS(fd, "initialized"); } - else if (runtime->core_initialized) { + else if (_PyRuntimeState_GetCoreInitialized(runtime)) { PUTS(fd, "core initialized"); } else if (runtime->preinitialized) { @@ -3254,11 +3615,25 @@ _Py_DumpExtensionModules(int fd, PyInterpreterState *interp) Py_hash_t hash; // if stdlib_module_names is not NULL, it is always a frozenset. while (_PySet_NextEntry(stdlib_module_names, &i, &item, &hash)) { - if (PyUnicode_Check(item) - && PyUnicode_Compare(key, item) == 0) - { - is_stdlib_ext = 1; - break; + if (!PyUnicode_Check(item)) { + continue; + } + Py_ssize_t len = PyUnicode_GET_LENGTH(item); + if (PyUnicode_Tailmatch(key, item, 0, len, -1) == 1) { + Py_ssize_t key_len = PyUnicode_GET_LENGTH(key); + if (key_len == len) { + is_stdlib_ext = 1; + break; + } + assert(key_len > len); + + // Ignore sub-modules of stdlib packages. For example, + // ignore "math.integer" if key starts with "math.". + Py_UCS4 ch = PyUnicode_ReadChar(key, len); + if (ch == '.') { + is_stdlib_ext = 1; + break; + } } } if (is_stdlib_ext) { @@ -3356,7 +3731,9 @@ fatal_error(int fd, int header, const char *prefix, const char *msg, This function already did its best to display a traceback. Disable faulthandler to prevent writing a second traceback on abort(). */ - _PyFaulthandler_Fini(); + if (has_tstate_and_gil) { + _PyFaulthandler_Fini(); + } /* Check if the current Python thread hold the GIL */ if (has_tstate_and_gil) { @@ -3443,6 +3820,27 @@ Py_ExitStatusException(PyStatus status) } +static void +handle_thread_shutdown_exception(PyThreadState *tstate) +{ + assert(tstate != NULL); + assert(_PyErr_Occurred(tstate)); + PyInterpreterState *interp = tstate->interp; + assert(interp->threads.head != NULL); + _PyEval_StopTheWorld(interp); + + // We don't have to worry about locking this because the + // world is stopped. + _Py_FOR_EACH_TSTATE_UNLOCKED(interp, tstate) { + if (tstate->_whence == _PyThreadState_WHENCE_THREADING) { + tstate->_whence = _PyThreadState_WHENCE_THREADING_DAEMON; + } + } + + _PyEval_StartTheWorld(interp); + PyErr_FormatUnraisable("Exception ignored on threading shutdown"); +} + /* Wait until threading._shutdown completes, provided the threading module was imported in the first place. The shutdown routine will wait until all non-daemon @@ -3454,14 +3852,14 @@ wait_for_thread_shutdown(PyThreadState *tstate) PyObject *threading = PyImport_GetModule(&_Py_ID(threading)); if (threading == NULL) { if (_PyErr_Occurred(tstate)) { - PyErr_FormatUnraisable("Exception ignored on threading shutdown"); + handle_thread_shutdown_exception(tstate); } /* else: threading not imported */ return; } result = PyObject_CallMethodNoArgs(threading, &_Py_ID(_shutdown)); if (result == NULL) { - PyErr_FormatUnraisable("Exception ignored on threading shutdown"); + handle_thread_shutdown_exception(tstate); } else { Py_DECREF(result); @@ -3594,7 +3992,7 @@ PyOS_getsig(int sig) /* * All of the code in this function must only use async-signal-safe functions, - * listed at `man 7 signal` or + * listed at `man 7 signal-safety` or * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. */ PyOS_sighandler_t diff --git a/Python/pystate.c b/Python/pystate.c index 04ca6edb4aaa0e..fed1df0173bacf 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -4,16 +4,17 @@ #include "Python.h" #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_audit.h" // _Py_AuditHookEntry +#include "pycore_backoff.h" // JUMP_BACKWARD_INITIAL_VALUE, SIDE_EXIT_INITIAL_VALUE #include "pycore_ceval.h" // _PyEval_AcquireLock() #include "pycore_codecs.h" // _PyCodec_Fini() #include "pycore_critical_section.h" // _PyCriticalSection_Resume() #include "pycore_dtoa.h" // _dtoa_state_INIT() -#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() #include "pycore_freelist.h" // _PyObject_ClearFreeLists() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_interpframe.h" // _PyThreadState_HasStackSpace() -#include "pycore_object.h" // _PyType_InitCache() +#include "pycore_object.h" // _PyType_InitCache(), _Py_ClearImmortal() #include "pycore_obmalloc.h" // _PyMem_obmalloc_state_on_heap() +#include "pycore_opcode_utils.h" // NUM_COMMON_CONSTANTS #include "pycore_optimizer.h" // JIT_CLEANUP_THRESHOLD #include "pycore_parking_lot.h" // _PyParkingLot_AfterFork() #include "pycore_pyerrors.h" // _PyErr_Clear() @@ -21,7 +22,8 @@ #include "pycore_pymem.h" // _PyMem_DebugEnabled() #include "pycore_runtime.h" // _PyRuntime #include "pycore_runtime_init.h" // _PyRuntimeState_INIT -#include "pycore_stackref.h" // Py_STACKREF_DEBUG +#include "pycore_stackref.h" // PyStackRef_AsPyObjectBorrow() +#include "pycore_stats.h" // FT_STAT_WORLD_STOP_INC() #include "pycore_time.h" // _PyTime_Init() #include "pycore_uniqueid.h" // _PyObject_FinalizePerThreadRefcounts() @@ -68,47 +70,37 @@ to avoid the expense of doing their own locking). */ -#ifdef HAVE_THREAD_LOCAL /* The attached thread state for the current thread. */ _Py_thread_local PyThreadState *_Py_tss_tstate = NULL; /* The "bound" thread state used by PyGILState_Ensure(), also known as a "gilstate." */ _Py_thread_local PyThreadState *_Py_tss_gilstate = NULL; -#endif + +/* The interpreter of the attached thread state, + and is same as tstate->interp. */ +_Py_thread_local PyInterpreterState *_Py_tss_interp = NULL; static inline PyThreadState * current_fast_get(void) { -#ifdef HAVE_THREAD_LOCAL return _Py_tss_tstate; -#else - // XXX Fall back to the PyThread_tss_*() API. -# error "no supported thread-local variable storage classifier" -#endif } static inline void current_fast_set(_PyRuntimeState *Py_UNUSED(runtime), PyThreadState *tstate) { assert(tstate != NULL); -#ifdef HAVE_THREAD_LOCAL _Py_tss_tstate = tstate; -#else - // XXX Fall back to the PyThread_tss_*() API. -# error "no supported thread-local variable storage classifier" -#endif + assert(tstate->interp != NULL); + _Py_tss_interp = tstate->interp; } static inline void current_fast_clear(_PyRuntimeState *Py_UNUSED(runtime)) { -#ifdef HAVE_THREAD_LOCAL _Py_tss_tstate = NULL; -#else - // XXX Fall back to the PyThread_tss_*() API. -# error "no supported thread-local variable storage classifier" -#endif + _Py_tss_interp = NULL; } #define tstate_verify_not_active(tstate) \ @@ -324,12 +316,12 @@ _Py_COMP_DIAG_POP &(runtime)->unicode_state.ids.mutex, \ &(runtime)->imports.extensions.mutex, \ &(runtime)->ceval.pending_mainthread.mutex, \ - &(runtime)->ceval.sys_trace_profile_mutex, \ &(runtime)->atexit.mutex, \ &(runtime)->audit_hooks.mutex, \ &(runtime)->allocators.mutex, \ &(runtime)->_main_interpreter.types.mutex, \ &(runtime)->_main_interpreter.code_state.mutex, \ + &(runtime)->_main_interpreter.dict_state.watcher_mutex, \ } static void @@ -340,8 +332,8 @@ init_runtime(_PyRuntimeState *runtime, { assert(!runtime->preinitializing); assert(!runtime->preinitialized); - assert(!runtime->core_initialized); - assert(!runtime->initialized); + assert(!_PyRuntimeState_GetCoreInitialized(runtime)); + assert(!_PyRuntimeState_GetInitialized(runtime)); assert(!runtime->_initialized); runtime->open_code_hook = open_code_hook; @@ -354,11 +346,6 @@ init_runtime(_PyRuntimeState *runtime, runtime->main_thread = PyThread_get_thread_ident(); runtime->unicode_state.ids.next_index = unicode_next_index; - -#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) - _Py_EmscriptenTrampoline_Init(runtime); -#endif - runtime->_initialized = 1; } @@ -463,21 +450,30 @@ _PyInterpreterState_Enable(_PyRuntimeState *runtime) static PyInterpreterState * alloc_interpreter(void) { + // Aligned allocation for PyInterpreterState. + // the first word of the memory block is used to store + // the original pointer to be used later to free the memory. size_t alignment = _Alignof(PyInterpreterState); - size_t allocsize = sizeof(PyInterpreterState) + alignment - 1; + size_t allocsize = sizeof(PyInterpreterState) + sizeof(void *) + alignment - 1; void *mem = PyMem_RawCalloc(1, allocsize); if (mem == NULL) { return NULL; } - PyInterpreterState *interp = _Py_ALIGN_UP(mem, alignment); - assert(_Py_IS_ALIGNED(interp, alignment)); - interp->_malloced = mem; - return interp; + void *ptr = _Py_ALIGN_UP((char *)mem + sizeof(void *), alignment); + ((void **)ptr)[-1] = mem; + assert(_Py_IS_ALIGNED(ptr, alignment)); + return ptr; } static void free_interpreter(PyInterpreterState *interp) { +#ifdef Py_STATS + if (interp->pystats_struct) { + PyMem_RawFree(interp->pystats_struct); + interp->pystats_struct = NULL; + } +#endif // The main interpreter is statically allocated so // should not be freed. if (interp != &_PyRuntime._main_interpreter) { @@ -487,7 +483,7 @@ free_interpreter(PyInterpreterState *interp) interp->obmalloc = NULL; } assert(_Py_IS_ALIGNED(interp, _Alignof(PyInterpreterState))); - PyMem_RawFree(interp->_malloced); + PyMem_RawFree(((void **)interp)[-1]); } } @@ -514,6 +510,35 @@ static inline int check_interpreter_whence(long); main interpreter. We fix those fields here, in addition to the other dynamically initialized fields. */ + +static inline bool +is_env_enabled(const char *env_name) +{ + char *env = Py_GETENV(env_name); + return env && *env != '\0' && *env != '0'; +} + +static inline bool +is_env_disabled(const char *env_name) +{ + char *env = Py_GETENV(env_name); + return env != NULL && *env == '0'; +} + +static inline void +init_policy(uint16_t *target, const char *env_name, uint16_t default_value, + long min_value, long max_value) +{ + *target = default_value; + char *env = Py_GETENV(env_name); + if (env && *env != '\0') { + long value = atol(env); + if (value >= min_value && value <= max_value) { + *target = (uint16_t)value; + } + } +} + static PyStatus init_interpreter(PyInterpreterState *interp, _PyRuntimeState *runtime, int64_t id, @@ -552,6 +577,7 @@ init_interpreter(PyInterpreterState *interp, #ifdef Py_GIL_DISABLED _Py_brc_init_state(interp); #endif + llist_init(&interp->mem_free_queue.head); llist_init(&interp->asyncio_tasks_head); interp->asyncio_tasks_lock = (PyMutex){0}; @@ -565,14 +591,54 @@ init_interpreter(PyInterpreterState *interp, } interp->monitoring_tool_versions[t] = 0; } - interp->sys_profile_initialized = false; - interp->sys_trace_initialized = false; interp->_code_object_generation = 0; interp->jit = false; - interp->executor_list_head = NULL; + interp->compiling = false; + interp->executor_blooms = NULL; + interp->executor_ptrs = NULL; + interp->executor_count = 0; + interp->executor_capacity = 0; interp->executor_deletion_list_head = NULL; - interp->executor_deletion_list_remaining_capacity = 0; - interp->trace_run_counter = JIT_CLEANUP_THRESHOLD; + interp->executor_creation_counter = JIT_CLEANUP_THRESHOLD; + + // Initialize optimization configuration from environment variables + // PYTHON_JIT_STRESS sets aggressive defaults for testing, but can be overridden + uint16_t jump_default = JUMP_BACKWARD_INITIAL_VALUE; + uint16_t resume_default = RESUME_INITIAL_VALUE; + uint16_t side_exit_default = SIDE_EXIT_INITIAL_VALUE; + + if (is_env_enabled("PYTHON_JIT_STRESS")) { + jump_default = 63; + side_exit_default = 63; + resume_default = 127; + } + + init_policy(&interp->opt_config.jump_backward_initial_value, + "PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE", + jump_default, 1, MAX_VALUE); + init_policy(&interp->opt_config.jump_backward_initial_backoff, + "PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF", + JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF); + init_policy(&interp->opt_config.resume_initial_value, + "PYTHON_JIT_RESUME_INITIAL_VALUE", + resume_default, 1, MAX_VALUE); + init_policy(&interp->opt_config.resume_initial_backoff, + "PYTHON_JIT_RESUME_INITIAL_BACKOFF", + RESUME_INITIAL_BACKOFF, 0, MAX_BACKOFF); + init_policy(&interp->opt_config.side_exit_initial_value, + "PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE", + side_exit_default, 1, MAX_VALUE); + init_policy(&interp->opt_config.side_exit_initial_backoff, + "PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF", + SIDE_EXIT_INITIAL_BACKOFF, 0, MAX_BACKOFF); + + // Trace fitness configuration + init_policy(&interp->opt_config.fitness_initial, + "PYTHON_JIT_FITNESS_INITIAL", + FITNESS_INITIAL, EXIT_QUALITY_CLOSE_LOOP, FITNESS_INITIAL); + + interp->opt_config.specialization_enabled = !is_env_enabled("PYTHON_SPECIALIZATION_OFF"); + interp->opt_config.uops_optimize_enabled = !is_env_disabled("PYTHON_UOPS_OPTIMIZE"); if (interp != &runtime->_main_interpreter) { /* Fix the self-referential, statically initialized fields. */ interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp); @@ -713,6 +779,36 @@ extern void _Py_stackref_report_leaks(PyInterpreterState *interp); #endif +static int +common_const_is_initialized(_PyStackRef ref) +{ +#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG) + return !PyStackRef_IsNull(ref); +#else + return ref.bits != 0 && !PyStackRef_IsNull(ref); +#endif +} + + +static void +common_constants_clear(PyInterpreterState *interp) +{ + for (int i = 0; i < NUM_COMMON_CONSTANTS; i++) { + _PyStackRef ref = interp->common_consts[i]; + if (!common_const_is_initialized(ref)) { + continue; + } + PyObject *obj = PyStackRef_AsPyObjectBorrow(ref); + PyStackRef_XCLOSE(ref); + interp->common_consts[i] = PyStackRef_NULL; + // Refcount reclamation skips heap immortals; release manually. + if (_Py_IsImmortal(obj) && !_Py_IsStaticImmortal(obj)) { + _Py_ClearImmortal(obj); + } + } +} + + static void interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) { @@ -760,10 +856,11 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->audit_hooks); - // At this time, all the threads should be cleared so we don't need atomic - // operations for instrumentation_version or eval_breaker. + // gh-140257: Threads have already been cleared, but daemon threads may + // still access eval_breaker atomically via take_gil() right before they + // hang. Use an atomic store to prevent data races during finalization. interp->ceval.instrumentation_version = 0; - tstate->eval_breaker = 0; + _Py_atomic_store_uintptr(&tstate->eval_breaker, 0); for (int i = 0; i < _PY_MONITORING_UNGROUPED_EVENTS; i++) { interp->monitors.tools[i] = 0; @@ -773,8 +870,6 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->monitoring_callables[t][e]); } } - interp->sys_profile_initialized = false; - interp->sys_trace_initialized = false; for (int t = 0; t < PY_MONITORING_TOOL_IDS; t++) { Py_CLEAR(interp->monitoring_tool_names[t]); } @@ -805,7 +900,6 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) _Py_ClearExecutorDeletionList(interp); #endif _PyAST_Fini(interp); - _PyWarnings_Fini(interp); _PyAtExit_Fini(interp); // All Python types must be destroyed before the last GC collection. Python @@ -816,6 +910,24 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) _PyGC_CollectNoFail(tstate); _PyGC_Fini(interp); + // Finalize warnings after last gc so that any finalizers can + // access warnings state + _PyWarnings_Fini(interp); + struct _PyExecutorObject *cold = interp->cold_executor; + if (cold != NULL) { + interp->cold_executor = NULL; + assert(cold->vm_data.valid); + assert(!cold->vm_data.cold); + _PyExecutor_Free(cold); + } + + struct _PyExecutorObject *cold_dynamic = interp->cold_dynamic_executor; + if (cold_dynamic != NULL) { + interp->cold_dynamic_executor = NULL; + assert(cold_dynamic->vm_data.valid); + assert(!cold_dynamic->vm_data.cold); + _PyExecutor_Free(cold_dynamic); + } /* We don't clear sysdict and builtins until the end of this function. Because clearing other attributes can execute arbitrary Python code which requires sysdict and builtins. */ @@ -823,6 +935,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) PyDict_Clear(interp->builtins); Py_CLEAR(interp->sysdict); Py_CLEAR(interp->builtins); + common_constants_clear(interp); #if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG) # ifdef Py_STACKREF_CLOSE_DEBUG @@ -945,6 +1058,8 @@ PyInterpreterState_Delete(PyInterpreterState *interp) _PyObject_FiniState(interp); + PyConfig_Clear(&interp->config); + free_interpreter(interp); } @@ -1275,9 +1390,8 @@ _PyInterpreterState_RequireIDRef(PyInterpreterState *interp, int required) PyInterpreterState* PyInterpreterState_Get(void) { - PyThreadState *tstate = current_fast_get(); - _Py_EnsureTstateNotNULL(tstate); - PyInterpreterState *interp = tstate->interp; + _Py_AssertHoldsTstate(); + PyInterpreterState *interp = _Py_tss_interp; if (interp == NULL) { Py_FatalError("no current interpreter"); } @@ -1398,6 +1512,9 @@ static void free_threadstate(_PyThreadStateImpl *tstate) { PyInterpreterState *interp = tstate->base.interp; +#ifdef Py_STATS + _PyStats_ThreadFini(tstate); +#endif // The initial thread state of the interpreter is allocated // as part of the interpreter state so should not be freed. if (tstate == &interp->_initial_thread) { @@ -1447,7 +1564,7 @@ init_threadstate(_PyThreadStateImpl *_tstate, assert(tstate->prev == NULL); assert(tstate->_whence == _PyThreadState_WHENCE_NOTSET); - assert(whence >= 0 && whence <= _PyThreadState_WHENCE_EXEC); + assert(whence >= 0 && whence <= _PyThreadState_WHENCE_THREADING_DAEMON); tstate->_whence = whence; assert(id > 0); @@ -1463,21 +1580,53 @@ init_threadstate(_PyThreadStateImpl *_tstate, // This is cleared when PyGILState_Ensure() creates the thread state. tstate->gilstate_counter = 1; - tstate->current_frame = NULL; + // Initialize the embedded base frame - sentinel at the bottom of the frame stack + _tstate->base_frame.previous = NULL; + _tstate->base_frame.f_executable = PyStackRef_None; + _tstate->base_frame.f_funcobj = PyStackRef_NULL; + _tstate->base_frame.f_globals = NULL; + _tstate->base_frame.f_builtins = NULL; + _tstate->base_frame.f_locals = NULL; + _tstate->base_frame.frame_obj = NULL; + _tstate->base_frame.instr_ptr = NULL; + _tstate->base_frame.stackpointer = _tstate->base_frame.localsplus; + _tstate->base_frame.return_offset = 0; + _tstate->base_frame.owner = FRAME_OWNED_BY_INTERPRETER; + _tstate->base_frame.visited = 0; +#ifdef Py_DEBUG + _tstate->base_frame.lltrace = 0; +#endif +#ifdef Py_GIL_DISABLED + _tstate->base_frame.tlbc_index = 0; +#endif + _tstate->base_frame.localsplus[0] = PyStackRef_NULL; + + // current_frame starts pointing to the base frame + tstate->current_frame = &_tstate->base_frame; + // base_frame pointer for profilers to validate stack unwinding + tstate->base_frame = &_tstate->base_frame; tstate->datastack_chunk = NULL; tstate->datastack_top = NULL; tstate->datastack_limit = NULL; + tstate->datastack_cached_chunk = NULL; tstate->what_event = -1; tstate->current_executor = NULL; + tstate->jit_exit = NULL; tstate->dict_global_version = 0; _tstate->c_stack_soft_limit = UINTPTR_MAX; _tstate->c_stack_top = 0; _tstate->c_stack_hard_limit = 0; + _tstate->c_stack_init_base = 0; + _tstate->c_stack_init_top = 0; + _tstate->asyncio_running_loop = NULL; _tstate->asyncio_running_task = NULL; +#ifdef _Py_TIER2 + _tstate->jit_tracer_state = NULL; +#endif tstate->delete_later = NULL; llist_init(&_tstate->mem_free_queue); @@ -1525,6 +1674,13 @@ new_threadstate(PyInterpreterState *interp, int whence) return NULL; } #endif +#ifdef Py_STATS + // The PyStats structure is quite large and is allocated separated from tstate. + if (!_PyStats_ThreadInit(interp, tstate)) { + free_threadstate(tstate); + return NULL; + } +#endif /* We serialize concurrent creation to protect global state. */ HEAD_LOCK(interp->runtime); @@ -1603,6 +1759,11 @@ clear_datastack(PyThreadState *tstate) _PyObject_VirtualFree(chunk, chunk->size); chunk = prev; } + if (tstate->datastack_cached_chunk != NULL) { + _PyObject_VirtualFree(tstate->datastack_cached_chunk, + tstate->datastack_cached_chunk->size); + tstate->datastack_cached_chunk = NULL; + } } void @@ -1610,7 +1771,11 @@ PyThreadState_Clear(PyThreadState *tstate) { assert(tstate->_status.initialized && !tstate->_status.cleared); assert(current_fast_get()->interp == tstate->interp); - assert(!_PyThreadState_IsRunningMain(tstate)); + // GH-126016: In the _interpreters module, KeyboardInterrupt exceptions + // during PyEval_EvalCode() are sent to finalization, which doesn't let us + // mark threads as "not running main". So, for now this assertion is + // disabled. + // XXX assert(!_PyThreadState_IsRunningMain(tstate)); // XXX assert(!tstate->_status.bound || tstate->_status.unbound); tstate->_status.finalizing = 1; // just in case @@ -1623,7 +1788,7 @@ PyThreadState_Clear(PyThreadState *tstate) int verbose = _PyInterpreterState_GetConfig(tstate->interp)->verbose; - if (verbose && tstate->current_frame != NULL) { + if (verbose && tstate->current_frame != tstate->base_frame) { /* bpo-20526: After the main thread calls _PyInterpreterState_SetFinalizing() in Py_FinalizeEx() (or in Py_EndInterpreter() for subinterpreters), @@ -1682,23 +1847,15 @@ PyThreadState_Clear(PyThreadState *tstate) "PyThreadState_Clear: warning: thread still has a generator\n"); } -#ifdef Py_GIL_DISABLED - PyMutex_Lock(&_PyRuntime.ceval.sys_trace_profile_mutex); -#endif - if (tstate->c_profilefunc != NULL) { - tstate->interp->sys_profiling_threads--; + FT_ATOMIC_ADD_SSIZE(tstate->interp->sys_profiling_threads, -1); tstate->c_profilefunc = NULL; } if (tstate->c_tracefunc != NULL) { - tstate->interp->sys_tracing_threads--; + FT_ATOMIC_ADD_SSIZE(tstate->interp->sys_tracing_threads, -1); tstate->c_tracefunc = NULL; } -#ifdef Py_GIL_DISABLED - PyMutex_Unlock(&_PyRuntime.ceval.sys_trace_profile_mutex); -#endif - Py_CLEAR(tstate->c_profileobj); Py_CLEAR(tstate->c_traceobj); @@ -1712,16 +1869,23 @@ PyThreadState_Clear(PyThreadState *tstate) struct _Py_freelists *freelists = _Py_freelists_GET(); _PyObject_ClearFreeLists(freelists, 1); + // Flush the thread's local GC allocation count to the global count + // before the thread state is cleared, otherwise the count is lost. + _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate; + _Py_atomic_add_int(&tstate->interp->gc.young.count, + (int)tstate_impl->gc.alloc_count); + tstate_impl->gc.alloc_count = 0; + // Merge our thread-local refcounts into the type's own refcount and // free our local refcount array. - _PyObject_FinalizePerThreadRefcounts((_PyThreadStateImpl *)tstate); + _PyObject_FinalizePerThreadRefcounts(tstate_impl); // Remove ourself from the biased reference counting table of threads. _Py_brc_remove_thread(tstate); // Release our thread-local copies of the bytecode for reuse by another // thread - _Py_ClearTLBCIndex((_PyThreadStateImpl *)tstate); + _Py_ClearTLBCIndex(tstate_impl); #endif // Merge our queue of pointers to be freed into the interpreter queue. @@ -1729,6 +1893,10 @@ PyThreadState_Clear(PyThreadState *tstate) _PyThreadState_ClearMimallocHeaps(tstate); +#ifdef _Py_TIER2 + _PyJit_TracerFree((_PyThreadStateImpl *)tstate); +#endif + tstate->_status.cleared = 1; // XXX Call _PyThreadStateSwap(runtime, NULL) here if "current". @@ -1781,6 +1949,10 @@ tstate_delete_common(PyThreadState *tstate, int release_gil) assert(tstate_impl->refcounts.values == NULL); #endif +#if _Py_TIER2 + _PyJit_TracerFree((_PyThreadStateImpl *)tstate); +#endif + HEAD_UNLOCK(runtime); // XXX Unbind in PyThreadState_Clear(), or earlier @@ -1840,6 +2012,9 @@ _PyThreadState_DeleteCurrent(PyThreadState *tstate) _Py_EnsureTstateNotNULL(tstate); #ifdef Py_GIL_DISABLED _Py_qsbr_detach(((_PyThreadStateImpl *)tstate)->qsbr); +#endif +#ifdef Py_STATS + _PyStats_Detach((_PyThreadStateImpl *)tstate); #endif current_fast_clear(tstate->interp->runtime); tstate_delete_common(tstate, 1); // release GIL as part of call @@ -2014,6 +2189,10 @@ tstate_deactivate(PyThreadState *tstate) assert(tstate_is_bound(tstate)); assert(tstate->_status.active); +#if Py_STATS + _PyStats_Detach((_PyThreadStateImpl *)tstate); +#endif + tstate->_status.active = 0; // We do not unbind the gilstate tstate here. @@ -2117,6 +2296,10 @@ _PyThreadState_Attach(PyThreadState *tstate) _PyCriticalSection_Resume(tstate); } +#ifdef Py_STATS + _PyStats_Attach((_PyThreadStateImpl *)tstate); +#endif + #if defined(Py_DEBUG) errno = err; #endif @@ -2251,19 +2434,22 @@ stop_the_world(struct _stoptheworld_state *stw) { _PyRuntimeState *runtime = &_PyRuntime; - PyMutex_Lock(&stw->mutex); + // gh-137433: Acquire the rwmutex first to avoid deadlocks with daemon + // threads that may hang when blocked on lock acquisition. if (stw->is_global) { _PyRWMutex_Lock(&runtime->stoptheworld_mutex); } else { _PyRWMutex_RLock(&runtime->stoptheworld_mutex); } + PyMutex_Lock(&stw->mutex); HEAD_LOCK(runtime); stw->requested = 1; stw->thread_countdown = 0; stw->stop_event = (PyEvent){0}; // zero-initialize (unset) stw->requester = _PyThreadState_GET(); // may be NULL + FT_STAT_WORLD_STOP_INC(); _Py_FOR_EACH_STW_INTERP(stw, i) { _Py_FOR_EACH_TSTATE_UNLOCKED(i, t) { @@ -2323,13 +2509,13 @@ start_the_world(struct _stoptheworld_state *stw) } stw->requester = NULL; HEAD_UNLOCK(runtime); + PyMutex_Unlock(&stw->mutex); if (stw->is_global) { _PyRWMutex_Unlock(&runtime->stoptheworld_mutex); } else { _PyRWMutex_RUnlock(&runtime->stoptheworld_mutex); } - PyMutex_Unlock(&stw->mutex); } #endif // Py_GIL_DISABLED @@ -2475,14 +2661,10 @@ _PyThreadState_Bind(PyThreadState *tstate) uintptr_t _Py_GetThreadLocal_Addr(void) { -#ifdef HAVE_THREAD_LOCAL // gh-112535: Use the address of the thread-local PyThreadState variable as // a unique identifier for the current thread. Each thread has a unique // _Py_tss_tstate variable with a unique address. return (uintptr_t)&_Py_tss_tstate; -#else -# error "no supported thread-local variable storage classifier" -#endif } #endif @@ -2727,7 +2909,7 @@ int PyGILState_Check(void) { _PyRuntimeState *runtime = &_PyRuntime; - if (!runtime->gilstate.check_enabled) { + if (!_Py_atomic_load_int_relaxed(&runtime->gilstate.check_enabled)) { return 1; } @@ -2740,34 +2922,40 @@ PyGILState_Check(void) return (tstate == tcur); } +static PyInterpreterGuard * +get_main_interp_guard(void) +{ + PyInterpreterView *view = PyInterpreterView_FromMain(); + if (view == NULL) { + return NULL; + } + + PyInterpreterGuard *guard = PyInterpreterGuard_FromView(view); + PyInterpreterView_Close(view); + return guard; +} + PyGILState_STATE PyGILState_Ensure(void) { - _PyRuntimeState *runtime = &_PyRuntime; - /* Note that we do not auto-init Python here - apart from potential races with 2 threads auto-initializing, pep-311 spells out other issues. Embedders are expected to have called Py_Initialize(). */ - /* Ensure that _PyEval_InitThreads() and _PyGILState_Init() have been - called by Py_Initialize() - - TODO: This isn't thread-safe. There's no protection here against - concurrent finalization of the interpreter; it's simply a guard - for *after* the interpreter has finalized. - */ - if (!_PyEval_ThreadsInitialized() || runtime->gilstate.autoInterpreterState == NULL) { - PyThread_hang_thread(); - } - PyThreadState *tcur = gilstate_get(); int has_gil; if (tcur == NULL) { /* Create a new Python thread state for this thread */ - // XXX Use PyInterpreterState_EnsureThreadState()? - tcur = new_threadstate(runtime->gilstate.autoInterpreterState, - _PyThreadState_WHENCE_GILSTATE); + PyInterpreterGuard *guard = get_main_interp_guard(); + if (guard == NULL) { + // The main interpreter has finished, so we don't have + // any intepreter to make a thread state for. Hang the + // thread to act as failure. + PyThread_hang_thread(); + } + tcur = new_threadstate(guard->interp, + _PyThreadState_WHENCE_C_API); if (tcur == NULL) { Py_FatalError("Couldn't create thread-state for new thread"); } @@ -2779,6 +2967,7 @@ PyGILState_Ensure(void) assert(tcur->gilstate_counter == 1); tcur->gilstate_counter = 0; has_gil = 0; /* new thread state is never current */ + PyInterpreterGuard_Close(guard); } else { has_gil = holds_gil(tcur); @@ -2877,9 +3066,32 @@ _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, RARE_EVENT_INC(set_eval_frame_func); _PyEval_StopTheWorld(interp); interp->eval_frame = eval_frame; + // reset when evaluator is reset + interp->eval_frame_allow_specialization = 0; + _PyEval_StartTheWorld(interp); +} + +void +_PyInterpreterState_SetEvalFrameAllowSpecialization(PyInterpreterState *interp, + int allow_specialization) +{ + if (allow_specialization == interp->eval_frame_allow_specialization) { + return; + } + _Py_Executors_InvalidateAll(interp, 1); + RARE_EVENT_INC(set_eval_frame_func); + _PyEval_StopTheWorld(interp); + interp->eval_frame_allow_specialization = allow_specialization; _PyEval_StartTheWorld(interp); } +int +_PyInterpreterState_IsSpecializationEnabled(PyInterpreterState *interp) +{ + return interp->eval_frame == NULL + || interp->eval_frame_allow_specialization; +} + const PyConfig* _PyInterpreterState_GetConfig(PyInterpreterState *interp) @@ -2913,9 +3125,20 @@ push_chunk(PyThreadState *tstate, int size) while (allocate_size < (int)sizeof(PyObject*)*(size + MINIMUM_OVERHEAD)) { allocate_size *= 2; } - _PyStackChunk *new = allocate_chunk(allocate_size, tstate->datastack_chunk); - if (new == NULL) { - return NULL; + _PyStackChunk *new; + if (tstate->datastack_cached_chunk != NULL + && (size_t)allocate_size <= tstate->datastack_cached_chunk->size) + { + new = tstate->datastack_cached_chunk; + tstate->datastack_cached_chunk = NULL; + new->previous = tstate->datastack_chunk; + new->top = 0; + } + else { + new = allocate_chunk(allocate_size, tstate->datastack_chunk); + if (new == NULL) { + return NULL; + } } if (tstate->datastack_chunk) { tstate->datastack_chunk->top = tstate->datastack_top - @@ -2951,12 +3174,17 @@ _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame * frame) if (base == &tstate->datastack_chunk->data[0]) { _PyStackChunk *chunk = tstate->datastack_chunk; _PyStackChunk *previous = chunk->previous; + _PyStackChunk *cached = tstate->datastack_cached_chunk; // push_chunk ensures that the root chunk is never popped: assert(previous); tstate->datastack_top = &previous->data[previous->top]; tstate->datastack_chunk = previous; - _PyObject_VirtualFree(chunk, chunk->size); tstate->datastack_limit = (PyObject **)(((char *)previous) + previous->size); + chunk->previous = NULL; + if (cached != NULL) { + _PyObject_VirtualFree(cached, cached->size); + } + tstate->datastack_cached_chunk = chunk; } else { assert(tstate->datastack_top); @@ -3121,3 +3349,277 @@ _Py_GetMainConfig(void) } return _PyInterpreterState_GetConfig(interp); } + +Py_ssize_t +_PyInterpreterState_GuardCountdown(PyInterpreterState *interp) +{ + assert(interp != NULL); + Py_ssize_t count = _Py_atomic_load_uintptr(&interp->finalization_guards); + assert(count >= 0); + return count; +} + +PyInterpreterState * +_PyInterpreterGuard_GetInterpreter(PyInterpreterGuard *guard) +{ + assert(guard != NULL); + assert(guard->interp != NULL); + return guard->interp; +} + +static int +try_acquire_interp_guard(PyInterpreterState *interp, PyInterpreterGuard *guard) +{ + assert(interp != NULL); + + uintptr_t expected; + do { + expected = _Py_atomic_load_uintptr(&interp->finalization_guards); + if (expected == _PyInterpreterGuard_GUARDS_NOT_ALLOWED) { + return -1; + } + } while (_Py_atomic_compare_exchange_uintptr(&interp->finalization_guards, + &expected, + expected + 1) == 0); + assert(_Py_atomic_load_uintptr(&interp->finalization_guards) > 0); + assert(_Py_atomic_load_uintptr(&interp->finalization_guards) != _PyInterpreterGuard_GUARDS_NOT_ALLOWED); + + guard->interp = interp; + return 0; +} + +PyInterpreterGuard * +PyInterpreterGuard_FromCurrent(void) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + assert(interp != NULL); + + PyInterpreterGuard *guard = PyMem_RawMalloc(sizeof(PyInterpreterGuard)); + if (guard == NULL) { + PyErr_NoMemory(); + return NULL; + } + + if (try_acquire_interp_guard(interp, guard) < 0) { + PyMem_RawFree(guard); + PyErr_SetString(PyExc_PythonFinalizationError, + "cannot acquire finalization guard anymore"); + return NULL; + } + + return guard; +} + +void +PyInterpreterGuard_Close(PyInterpreterGuard *guard) +{ + PyInterpreterState *interp = guard->interp; + assert(interp != NULL); + + assert(_Py_atomic_load_uintptr(&interp->finalization_guards) != _PyInterpreterGuard_GUARDS_NOT_ALLOWED); + uintptr_t old_value = _Py_atomic_add_uintptr(&interp->finalization_guards, -1); + if (old_value == 1) { + _PyParkingLot_UnparkAll(&interp->finalization_guards); + } + + assert(old_value > 0); + PyMem_RawFree(guard); +} + +PyInterpreterView * +PyInterpreterView_FromCurrent(void) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + assert(interp != NULL); + + // PyInterpreterView_Close() can be called without an attached thread + // state, so we have to use the raw allocator. + PyInterpreterView *view = PyMem_RawMalloc(sizeof(PyInterpreterView)); + if (view == NULL) { + PyErr_NoMemory(); + return NULL; + } + + view->id = interp->id; + return view; +} + +void +PyInterpreterView_Close(PyInterpreterView *view) +{ + assert(view != NULL); + PyMem_RawFree(view); +} + +PyInterpreterGuard * +PyInterpreterGuard_FromView(PyInterpreterView *view) +{ + assert(view != NULL); + int64_t interp_id = view->id; + assert(interp_id >= 0); + + // This allocation has to happen before we acquire the runtime lock, because + // PyMem_RawMalloc() might call some weird callback (such as tracemalloc) + // that tries to re-entrantly acquire the lock. + PyInterpreterGuard *guard = PyMem_RawMalloc(sizeof(PyInterpreterGuard)); + if (guard == NULL) { + return NULL; + } + + // Interpreters cannot be deleted while we hold the runtime lock. + _PyRuntimeState *runtime = &_PyRuntime; + HEAD_LOCK(runtime); + PyInterpreterState *interp = interp_look_up_id(runtime, interp_id); + if (interp == NULL) { + HEAD_UNLOCK(runtime); + PyMem_RawFree(guard); + return NULL; + } + + int result = try_acquire_interp_guard(interp, guard); + HEAD_UNLOCK(runtime); + + if (result < 0) { + PyMem_RawFree(guard); + return NULL; + } + + assert(guard->interp != NULL); + return guard; +} + +PyInterpreterView * +PyInterpreterView_FromMain(void) +{ + PyInterpreterView *view = PyMem_RawMalloc(sizeof(PyInterpreterView)); + if (view == NULL) { + return NULL; + } + + // The main interpreter always has an ID of zero. + view->id = 0; + + return view; +} + +static const PyThreadStateToken *_no_tstate_sentinel = (const PyThreadStateToken *)&_no_tstate_sentinel; +#define NO_TSTATE_SENTINEL ((PyThreadStateToken *)_no_tstate_sentinel) + +PyThreadStateToken * +PyThreadState_Ensure(PyInterpreterGuard *guard) +{ + assert(guard != NULL); + PyInterpreterState *interp = guard->interp; + assert(interp != NULL); + PyThreadState *attached_tstate = current_fast_get(); + if (attached_tstate != NULL && attached_tstate->interp == interp) { + /* Yay! We already have an attached thread state that matches. */ + ++attached_tstate->ensure.counter; + return attached_tstate; + } + + PyThreadState *detached_gilstate = gilstate_get(); + if (detached_gilstate != NULL && detached_gilstate->interp == interp) { + /* There's a detached thread state that works. */ + assert(attached_tstate == NULL); + ++detached_gilstate->ensure.counter; + _PyThreadState_Attach(detached_gilstate); + return NO_TSTATE_SENTINEL; + } + + PyThreadState *fresh_tstate = _PyThreadState_NewBound(interp, + _PyThreadState_WHENCE_C_API); + if (fresh_tstate == NULL) { + return NULL; + } + fresh_tstate->ensure.counter = 1; + fresh_tstate->ensure.delete_on_release = 1; + + if (attached_tstate != NULL) { + return (PyThreadStateToken *)PyThreadState_Swap(fresh_tstate); + } + + _PyThreadState_Attach(fresh_tstate); + return NO_TSTATE_SENTINEL; +} + +PyThreadStateToken * +PyThreadState_EnsureFromView(PyInterpreterView *view) +{ + assert(view != NULL); + PyInterpreterGuard *guard = PyInterpreterGuard_FromView(view); + if (guard == NULL) { + return NULL; + } + + PyThreadStateToken *result = (PyThreadStateToken *)PyThreadState_Ensure(guard); + if (result == NULL) { + PyInterpreterGuard_Close(guard); + return NULL; + } + + PyThreadState *tstate = current_fast_get(); + assert(tstate != NULL); + + if (tstate->ensure.owned_guard != NULL) { + assert(tstate->ensure.owned_guard->interp == guard->interp); + PyInterpreterGuard_Close(guard); + } + else { + assert(tstate->ensure.owned_guard == NULL); + tstate->ensure.owned_guard = guard; + } + + return result; +} + +void +PyThreadState_Release(PyThreadStateToken *token) +{ + PyThreadState *tstate = current_fast_get(); + _Py_EnsureTstateNotNULL(tstate); + Py_ssize_t remaining = --tstate->ensure.counter; + if (remaining < 0) { + Py_FatalError("PyThreadState_Release() called more times than PyThreadState_Ensure()"); + } + + if (remaining != 0) { + // If the corresponding PyThreadState_Ensure() call used a detached + // thread state, we want to detach it again. + if (token == NO_TSTATE_SENTINEL) { + PyThreadState_Swap(NULL); + } + return; + } + + PyThreadState *to_restore; + if (token == NO_TSTATE_SENTINEL) { + to_restore = NULL; + } + else { + to_restore = (PyThreadState *)token; + } + + PyInterpreterGuard *owned_guard = tstate->ensure.owned_guard; + assert(tstate->ensure.delete_on_release == 1 || tstate->ensure.delete_on_release == 0); + if (tstate->ensure.delete_on_release) { + ++tstate->ensure.counter; + PyThreadState_Clear(tstate); + --tstate->ensure.counter; + } + else if (owned_guard != NULL) { + tstate->ensure.owned_guard = NULL; + } + + PyThreadState *check_tstate = PyThreadState_Swap(to_restore); + (void)check_tstate; + assert(check_tstate == tstate); + + if (tstate->ensure.delete_on_release) { + PyThreadState_Delete(tstate); + } + + if (owned_guard != NULL) { + PyInterpreterGuard_Close(owned_guard); + } +} diff --git a/Python/pystats.c b/Python/pystats.c new file mode 100644 index 00000000000000..2fac2db1b738c7 --- /dev/null +++ b/Python/pystats.c @@ -0,0 +1,820 @@ +#include "Python.h" + +#include "pycore_opcode_metadata.h" // _PyOpcode_Caches +#include "pycore_pyatomic_ft_wrappers.h" +#include "pycore_pylifecycle.h" // _PyOS_URandomNonblock() +#include "pycore_tstate.h" +#include "pycore_initconfig.h" // _PyStatus_OK() +#include "pycore_uop_metadata.h" // _PyOpcode_uop_name +#include "pycore_uop_ids.h" // MAX_UOP_REGS_ID +#include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_runtime.h" // NUM_GENERATIONS + +#include <stdlib.h> // rand() + +#ifdef Py_STATS + +PyStats * +_PyStats_GetLocal(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + if (tstate) { + return tstate->pystats; + } + return NULL; +} + +#ifdef Py_GIL_DISABLED +#define STATS_LOCK(interp) PyMutex_Lock(&interp->pystats_mutex) +#define STATS_UNLOCK(interp) PyMutex_Unlock(&interp->pystats_mutex) +#else +#define STATS_LOCK(interp) +#define STATS_UNLOCK(interp) +#endif + + +#if PYSTATS_MAX_UOP_ID < MAX_UOP_REGS_ID +#error "Not enough space allocated for pystats. Increase PYSTATS_MAX_UOP_ID to at least MAX_UOP_REGS_ID" +#endif + +#define ADD_STAT_TO_DICT(res, field) \ + do { \ + PyObject *val = PyLong_FromUnsignedLongLong(stats->field); \ + if (val == NULL) { \ + Py_DECREF(res); \ + return NULL; \ + } \ + if (PyDict_SetItemString(res, #field, val) == -1) { \ + Py_DECREF(res); \ + Py_DECREF(val); \ + return NULL; \ + } \ + Py_DECREF(val); \ + } while(0); + +static PyObject* +stats_to_dict(SpecializationStats *stats) +{ + PyObject *res = PyDict_New(); + if (res == NULL) { + return NULL; + } + ADD_STAT_TO_DICT(res, success); + ADD_STAT_TO_DICT(res, failure); + ADD_STAT_TO_DICT(res, hit); + ADD_STAT_TO_DICT(res, deferred); + ADD_STAT_TO_DICT(res, miss); + ADD_STAT_TO_DICT(res, deopt); + PyObject *failure_kinds = PyTuple_New(SPECIALIZATION_FAILURE_KINDS); + if (failure_kinds == NULL) { + Py_DECREF(res); + return NULL; + } + for (int i = 0; i < SPECIALIZATION_FAILURE_KINDS; i++) { + PyObject *stat = PyLong_FromUnsignedLongLong(stats->failure_kinds[i]); + if (stat == NULL) { + Py_DECREF(res); + Py_DECREF(failure_kinds); + return NULL; + } + PyTuple_SET_ITEM(failure_kinds, i, stat); + } + if (PyDict_SetItemString(res, "failure_kinds", failure_kinds)) { + Py_DECREF(res); + Py_DECREF(failure_kinds); + return NULL; + } + Py_DECREF(failure_kinds); + return res; +} +#undef ADD_STAT_TO_DICT + +static int +add_stat_dict( + PyStats *src, + PyObject *res, + int opcode, + const char *name) { + + SpecializationStats *stats = &src->opcode_stats[opcode].specialization; + PyObject *d = stats_to_dict(stats); + if (d == NULL) { + return -1; + } + int err = PyDict_SetItemString(res, name, d); + Py_DECREF(d); + return err; +} + +PyObject* +_Py_GetSpecializationStats(void) { + PyThreadState *tstate = _PyThreadState_GET(); + PyStats *src = FT_ATOMIC_LOAD_PTR_RELAXED(tstate->interp->pystats_struct); + if (src == NULL) { + Py_RETURN_NONE; + } + PyObject *stats = PyDict_New(); + if (stats == NULL) { + return NULL; + } + int err = 0; + err += add_stat_dict(src, stats, CONTAINS_OP, "contains_op"); + err += add_stat_dict(src, stats, LOAD_SUPER_ATTR, "load_super_attr"); + err += add_stat_dict(src, stats, LOAD_ATTR, "load_attr"); + err += add_stat_dict(src, stats, LOAD_GLOBAL, "load_global"); + err += add_stat_dict(src, stats, STORE_SUBSCR, "store_subscr"); + err += add_stat_dict(src, stats, STORE_ATTR, "store_attr"); + err += add_stat_dict(src, stats, JUMP_BACKWARD, "jump_backward"); + err += add_stat_dict(src, stats, CALL, "call"); + err += add_stat_dict(src, stats, CALL_KW, "call_kw"); + err += add_stat_dict(src, stats, BINARY_OP, "binary_op"); + err += add_stat_dict(src, stats, COMPARE_OP, "compare_op"); + err += add_stat_dict(src, stats, UNPACK_SEQUENCE, "unpack_sequence"); + err += add_stat_dict(src, stats, FOR_ITER, "for_iter"); + err += add_stat_dict(src, stats, TO_BOOL, "to_bool"); + err += add_stat_dict(src, stats, SEND, "send"); + if (err < 0) { + Py_DECREF(stats); + return NULL; + } + return stats; +} + + +#define PRINT_STAT(i, field) \ + if (stats[i].field) { \ + fprintf(out, " opcode[%s]." #field " : %" PRIu64 "\n", _PyOpcode_OpName[i], stats[i].field); \ + } + +static void +print_spec_stats(FILE *out, OpcodeStats *stats) +{ + /* Mark some opcodes as specializable for stats, + * even though we don't specialize them yet. */ + fprintf(out, "opcode[BINARY_SLICE].specializable : 1\n"); + fprintf(out, "opcode[STORE_SLICE].specializable : 1\n"); + fprintf(out, "opcode[GET_ITER].specializable : 1\n"); + for (int i = 0; i < 256; i++) { + if (_PyOpcode_Caches[i]) { + /* Ignore jumps as they cannot be specialized */ + switch (i) { + case POP_JUMP_IF_FALSE: + case POP_JUMP_IF_TRUE: + case POP_JUMP_IF_NONE: + case POP_JUMP_IF_NOT_NONE: + case JUMP_BACKWARD: + break; + default: + fprintf(out, "opcode[%s].specializable : 1\n", _PyOpcode_OpName[i]); + } + } + PRINT_STAT(i, specialization.success); + PRINT_STAT(i, specialization.failure); + PRINT_STAT(i, specialization.hit); + PRINT_STAT(i, specialization.deferred); + PRINT_STAT(i, specialization.miss); + PRINT_STAT(i, specialization.deopt); + PRINT_STAT(i, execution_count); + for (int j = 0; j < SPECIALIZATION_FAILURE_KINDS; j++) { + uint64_t val = stats[i].specialization.failure_kinds[j]; + if (val) { + fprintf(out, " opcode[%s].specialization.failure_kinds[%d] : %" + PRIu64 "\n", _PyOpcode_OpName[i], j, val); + } + } + for (int j = 0; j < 256; j++) { + if (stats[i].pair_count[j]) { + fprintf(out, "opcode[%s].pair_count[%s] : %" PRIu64 "\n", + _PyOpcode_OpName[i], _PyOpcode_OpName[j], stats[i].pair_count[j]); + } + } + } +} +#undef PRINT_STAT + + +static void +print_call_stats(FILE *out, CallStats *stats) +{ + fprintf(out, "Calls to PyEval_EvalDefault: %" PRIu64 "\n", stats->pyeval_calls); + fprintf(out, "Calls to Python functions inlined: %" PRIu64 "\n", stats->inlined_py_calls); + fprintf(out, "Frames pushed: %" PRIu64 "\n", stats->frames_pushed); + fprintf(out, "Frame objects created: %" PRIu64 "\n", stats->frame_objects_created); + for (int i = 0; i < EVAL_CALL_KINDS; i++) { + fprintf(out, "Calls via PyEval_EvalFrame[%d] : %" PRIu64 "\n", i, stats->eval_calls[i]); + } +} + +static void +print_object_stats(FILE *out, ObjectStats *stats) +{ + fprintf(out, "Object allocations from freelist: %" PRIu64 "\n", stats->from_freelist); + fprintf(out, "Object frees to freelist: %" PRIu64 "\n", stats->to_freelist); + fprintf(out, "Object allocations: %" PRIu64 "\n", stats->allocations); + fprintf(out, "Object allocations to 512 bytes: %" PRIu64 "\n", stats->allocations512); + fprintf(out, "Object allocations to 4 kbytes: %" PRIu64 "\n", stats->allocations4k); + fprintf(out, "Object allocations over 4 kbytes: %" PRIu64 "\n", stats->allocations_big); + fprintf(out, "Object frees: %" PRIu64 "\n", stats->frees); + fprintf(out, "Object inline values: %" PRIu64 "\n", stats->inline_values); + fprintf(out, "Object interpreter mortal increfs: %" PRIu64 "\n", stats->interpreter_increfs); + fprintf(out, "Object interpreter mortal decrefs: %" PRIu64 "\n", stats->interpreter_decrefs); + fprintf(out, "Object mortal increfs: %" PRIu64 "\n", stats->increfs); + fprintf(out, "Object mortal decrefs: %" PRIu64 "\n", stats->decrefs); + fprintf(out, "Object interpreter immortal increfs: %" PRIu64 "\n", stats->interpreter_immortal_increfs); + fprintf(out, "Object interpreter immortal decrefs: %" PRIu64 "\n", stats->interpreter_immortal_decrefs); + fprintf(out, "Object immortal increfs: %" PRIu64 "\n", stats->immortal_increfs); + fprintf(out, "Object immortal decrefs: %" PRIu64 "\n", stats->immortal_decrefs); + fprintf(out, "Object materialize dict (on request): %" PRIu64 "\n", stats->dict_materialized_on_request); + fprintf(out, "Object materialize dict (new key): %" PRIu64 "\n", stats->dict_materialized_new_key); + fprintf(out, "Object materialize dict (too big): %" PRIu64 "\n", stats->dict_materialized_too_big); + fprintf(out, "Object materialize dict (str subclass): %" PRIu64 "\n", stats->dict_materialized_str_subclass); + fprintf(out, "Object method cache hits: %" PRIu64 "\n", stats->type_cache_hits); + fprintf(out, "Object method cache misses: %" PRIu64 "\n", stats->type_cache_misses); + fprintf(out, "Object method cache collisions: %" PRIu64 "\n", stats->type_cache_collisions); + fprintf(out, "Object method cache dunder hits: %" PRIu64 "\n", stats->type_cache_dunder_hits); + fprintf(out, "Object method cache dunder misses: %" PRIu64 "\n", stats->type_cache_dunder_misses); +} + +static void +print_gc_stats(FILE *out, GCStats *stats) +{ + for (int i = 0; i < NUM_GENERATIONS; i++) { + fprintf(out, "GC[%d] collections: %" PRIu64 "\n", i, stats[i].collections); + fprintf(out, "GC[%d] object visits: %" PRIu64 "\n", i, stats[i].object_visits); + fprintf(out, "GC[%d] objects collected: %" PRIu64 "\n", i, stats[i].objects_collected); + fprintf(out, "GC[%d] objects reachable from roots: %" PRIu64 "\n", i, stats[i].objects_transitively_reachable); + fprintf(out, "GC[%d] objects not reachable from roots: %" PRIu64 "\n", i, stats[i].objects_not_transitively_reachable); + } +} + +#ifdef _Py_TIER2 +static void +print_histogram(FILE *out, const char *name, uint64_t hist[_Py_UOP_HIST_SIZE]) +{ + for (int i = 0; i < _Py_UOP_HIST_SIZE; i++) { + fprintf(out, "%s[%" PRIu64"]: %" PRIu64 "\n", name, (uint64_t)1 << i, hist[i]); + } +} + +extern const char *_PyUOpName(int index); + +static void +print_optimization_stats(FILE *out, OptimizationStats *stats) +{ + fprintf(out, "Optimization attempts: %" PRIu64 "\n", stats->attempts); + fprintf(out, "Optimization traces created: %" PRIu64 "\n", stats->traces_created); + fprintf(out, "Optimization traces executed: %" PRIu64 "\n", stats->traces_executed); + fprintf(out, "Optimization uops executed: %" PRIu64 "\n", stats->uops_executed); + fprintf(out, "Optimization trace stack overflow: %" PRIu64 "\n", stats->trace_stack_overflow); + fprintf(out, "Optimization trace stack underflow: %" PRIu64 "\n", stats->trace_stack_underflow); + fprintf(out, "Optimization trace too long: %" PRIu64 "\n", stats->trace_too_long); + fprintf(out, "Optimization trace too short: %" PRIu64 "\n", stats->trace_too_short); + fprintf(out, "Optimization inner loop: %" PRIu64 "\n", stats->inner_loop); + fprintf(out, "Optimization recursive call: %" PRIu64 "\n", stats->recursive_call); + fprintf(out, "Optimization low confidence: %" PRIu64 "\n", stats->low_confidence); + fprintf(out, "Optimization unknown callee: %" PRIu64 "\n", stats->unknown_callee); + fprintf(out, "Executors invalidated: %" PRIu64 "\n", stats->executors_invalidated); + fprintf(out, "Optimization fitness terminated: %" PRIu64 "\n", stats->fitness_terminated_traces); + + print_histogram(out, "Trace length", stats->trace_length_hist); + print_histogram(out, "Trace run length", stats->trace_run_length_hist); + print_histogram(out, "Optimized trace length", stats->optimized_trace_length_hist); + + fprintf(out, "Optimization optimizer attempts: %" PRIu64 "\n", stats->optimizer_attempts); + fprintf(out, "Optimization optimizer successes: %" PRIu64 "\n", stats->optimizer_successes); + fprintf(out, "Optimization optimizer failure no memory: %" PRIu64 "\n", + stats->optimizer_failure_reason_no_memory); + fprintf(out, "Optimizer remove globals builtins changed: %" PRIu64 "\n", stats->remove_globals_builtins_changed); + fprintf(out, "Optimizer remove globals incorrect keys: %" PRIu64 "\n", stats->remove_globals_incorrect_keys); + for (int i = 0; i <= MAX_UOP_REGS_ID; i++) { + if (stats->opcode[i].execution_count) { + fprintf(out, "uops[%s].execution_count : %" PRIu64 "\n", _PyUOpName(i), stats->opcode[i].execution_count); + } + if (stats->opcode[i].miss) { + fprintf(out, "uops[%s].specialization.miss : %" PRIu64 "\n", _PyUOpName(i), stats->opcode[i].miss); + } + } + for (int i = 0; i < 256; i++) { + if (stats->unsupported_opcode[i]) { + fprintf( + out, + "unsupported_opcode[%s].count : %" PRIu64 "\n", + _PyOpcode_OpName[i], + stats->unsupported_opcode[i] + ); + } + } + + for (int i = 1; i <= MAX_UOP_REGS_ID; i++){ + for (int j = 1; j <= MAX_UOP_REGS_ID; j++) { + if (stats->opcode[i].pair_count[j]) { + fprintf(out, "uop[%s].pair_count[%s] : %" PRIu64 "\n", + _PyOpcode_uop_name[i], _PyOpcode_uop_name[j], stats->opcode[i].pair_count[j]); + } + } + } + for (int i = 0; i < MAX_UOP_REGS_ID; i++) { + if (stats->error_in_opcode[i]) { + fprintf( + out, + "error_in_opcode[%s].count : %" PRIu64 "\n", + _PyUOpName(i), + stats->error_in_opcode[i] + ); + } + } + fprintf(out, "JIT total memory size: %" PRIu64 "\n", stats->jit_total_memory_size); + fprintf(out, "JIT code size: %" PRIu64 "\n", stats->jit_code_size); + fprintf(out, "JIT trampoline size: %" PRIu64 "\n", stats->jit_trampoline_size); + fprintf(out, "JIT data size: %" PRIu64 "\n", stats->jit_data_size); + fprintf(out, "JIT padding size: %" PRIu64 "\n", stats->jit_padding_size); + fprintf(out, "JIT freed memory size: %" PRIu64 "\n", stats->jit_freed_memory_size); + + print_histogram(out, "Trace total memory size", stats->trace_total_memory_hist); +} +#endif + +#ifdef Py_GIL_DISABLED +static void +print_ft_stats(FILE *out, FTStats *stats) +{ + fprintf(out, "Mutex sleeps (mutex_sleeps): %" PRIu64 "\n", stats->mutex_sleeps); + fprintf(out, "QSBR polls (qsbr_polls): %" PRIu64 "\n", stats->qsbr_polls); + fprintf(out, "World stops (world_stops): %" PRIu64 "\n", stats->world_stops); +} +#endif + +static void +print_rare_event_stats(FILE *out, RareEventStats *stats) +{ + fprintf(out, "Rare event (set_class): %" PRIu64 "\n", stats->set_class); + fprintf(out, "Rare event (set_bases): %" PRIu64 "\n", stats->set_bases); + fprintf(out, "Rare event (set_eval_frame_func): %" PRIu64 "\n", stats->set_eval_frame_func); + fprintf(out, "Rare event (builtin_dict): %" PRIu64 "\n", stats->builtin_dict); + fprintf(out, "Rare event (func_modification): %" PRIu64 "\n", stats->func_modification); + fprintf(out, "Rare event (watched_dict_modification): %" PRIu64 "\n", stats->watched_dict_modification); + fprintf(out, "Rare event (watched_globals_modification): %" PRIu64 "\n", stats->watched_globals_modification); +} + +static void +print_stats(FILE *out, PyStats *stats) +{ + print_spec_stats(out, stats->opcode_stats); + print_call_stats(out, &stats->call_stats); + print_object_stats(out, &stats->object_stats); + print_gc_stats(out, stats->gc_stats); +#ifdef _Py_TIER2 + print_optimization_stats(out, &stats->optimization_stats); +#endif +#ifdef Py_GIL_DISABLED + print_ft_stats(out, &stats->ft_stats); +#endif + print_rare_event_stats(out, &stats->rare_event_stats); +} + +#ifdef Py_GIL_DISABLED + +static void +merge_specialization_stats(SpecializationStats *dest, const SpecializationStats *src) +{ + dest->success += src->success; + dest->failure += src->failure; + dest->hit += src->hit; + dest->deferred += src->deferred; + dest->miss += src->miss; + dest->deopt += src->deopt; + for (int i = 0; i < SPECIALIZATION_FAILURE_KINDS; i++) { + dest->failure_kinds[i] += src->failure_kinds[i]; + } +} + +static void +merge_opcode_stats_array(OpcodeStats *dest, const OpcodeStats *src) +{ + for (int i = 0; i < 256; i++) { + merge_specialization_stats(&dest[i].specialization, &src[i].specialization); + dest[i].execution_count += src[i].execution_count; + for (int j = 0; j < 256; j++) { + dest[i].pair_count[j] += src[i].pair_count[j]; + } + } +} + +static void +merge_call_stats(CallStats *dest, const CallStats *src) +{ + dest->inlined_py_calls += src->inlined_py_calls; + dest->pyeval_calls += src->pyeval_calls; + dest->frames_pushed += src->frames_pushed; + dest->frame_objects_created += src->frame_objects_created; + for (int i = 0; i < EVAL_CALL_KINDS; i++) { + dest->eval_calls[i] += src->eval_calls[i]; + } +} + +static void +merge_object_stats(ObjectStats *dest, const ObjectStats *src) +{ + dest->increfs += src->increfs; + dest->decrefs += src->decrefs; + dest->interpreter_increfs += src->interpreter_increfs; + dest->interpreter_decrefs += src->interpreter_decrefs; + dest->immortal_increfs += src->immortal_increfs; + dest->immortal_decrefs += src->immortal_decrefs; + dest->interpreter_immortal_increfs += src->interpreter_immortal_increfs; + dest->interpreter_immortal_decrefs += src->interpreter_immortal_decrefs; + dest->allocations += src->allocations; + dest->allocations512 += src->allocations512; + dest->allocations4k += src->allocations4k; + dest->allocations_big += src->allocations_big; + dest->frees += src->frees; + dest->to_freelist += src->to_freelist; + dest->from_freelist += src->from_freelist; + dest->inline_values += src->inline_values; + dest->dict_materialized_on_request += src->dict_materialized_on_request; + dest->dict_materialized_new_key += src->dict_materialized_new_key; + dest->dict_materialized_too_big += src->dict_materialized_too_big; + dest->dict_materialized_str_subclass += src->dict_materialized_str_subclass; + dest->type_cache_hits += src->type_cache_hits; + dest->type_cache_misses += src->type_cache_misses; + dest->type_cache_dunder_hits += src->type_cache_dunder_hits; + dest->type_cache_dunder_misses += src->type_cache_dunder_misses; + dest->type_cache_collisions += src->type_cache_collisions; + dest->object_visits += src->object_visits; +} + +static void +merge_uop_stats_array(UOpStats *dest, const UOpStats *src) +{ + for (int i = 0; i <= PYSTATS_MAX_UOP_ID; i++) { + dest[i].execution_count += src[i].execution_count; + dest[i].miss += src[i].miss; + for (int j = 0; j <= PYSTATS_MAX_UOP_ID; j++) { + dest[i].pair_count[j] += src[i].pair_count[j]; + } + } +} + +static void +merge_optimization_stats(OptimizationStats *dest, const OptimizationStats *src) +{ + dest->attempts += src->attempts; + dest->traces_created += src->traces_created; + dest->traces_executed += src->traces_executed; + dest->uops_executed += src->uops_executed; + dest->trace_stack_overflow += src->trace_stack_overflow; + dest->trace_stack_underflow += src->trace_stack_underflow; + dest->trace_too_long += src->trace_too_long; + dest->trace_too_short += src->trace_too_short; + dest->inner_loop += src->inner_loop; + dest->recursive_call += src->recursive_call; + dest->low_confidence += src->low_confidence; + dest->unknown_callee += src->unknown_callee; + dest->executors_invalidated += src->executors_invalidated; + dest->optimizer_attempts += src->optimizer_attempts; + dest->optimizer_successes += src->optimizer_successes; + dest->optimizer_failure_reason_no_memory += src->optimizer_failure_reason_no_memory; + dest->remove_globals_builtins_changed += src->remove_globals_builtins_changed; + dest->remove_globals_incorrect_keys += src->remove_globals_incorrect_keys; + dest->jit_total_memory_size += src->jit_total_memory_size; + dest->jit_code_size += src->jit_code_size; + dest->jit_trampoline_size += src->jit_trampoline_size; + dest->jit_data_size += src->jit_data_size; + dest->jit_padding_size += src->jit_padding_size; + dest->jit_freed_memory_size += src->jit_freed_memory_size; + + merge_uop_stats_array(dest->opcode, src->opcode); + + for (int i = 0; i < 256; i++) { + dest->unsupported_opcode[i] += src->unsupported_opcode[i]; + } + for (int i = 0; i < _Py_UOP_HIST_SIZE; i++) { + dest->trace_length_hist[i] += src->trace_length_hist[i]; + dest->trace_run_length_hist[i] += src->trace_run_length_hist[i]; + dest->optimized_trace_length_hist[i] += src->optimized_trace_length_hist[i]; + dest->trace_total_memory_hist[i] += src->trace_total_memory_hist[i]; + } + for (int i = 0; i <= PYSTATS_MAX_UOP_ID; i++) { + dest->error_in_opcode[i] += src->error_in_opcode[i]; + } +} + +static void +merge_ft_stats(FTStats *dest, const FTStats *src) +{ + dest->mutex_sleeps = src->mutex_sleeps; + dest->qsbr_polls = src->qsbr_polls; + dest->world_stops = src->world_stops; +} + +static void +merge_rare_event_stats(RareEventStats *dest, const RareEventStats *src) +{ + dest->set_class += src->set_class; + dest->set_bases += src->set_bases; + dest->set_eval_frame_func += src->set_eval_frame_func; + dest->builtin_dict += src->builtin_dict; + dest->func_modification += src->func_modification; + dest->watched_dict_modification += src->watched_dict_modification; + dest->watched_globals_modification += src->watched_globals_modification; +} + +static void +merge_gc_stats_array(GCStats *dest, const GCStats *src) +{ + for (int i = 0; i < NUM_GENERATIONS; i++) { + dest[i].collections += src[i].collections; + dest[i].object_visits += src[i].object_visits; + dest[i].objects_collected += src[i].objects_collected; + dest[i].objects_transitively_reachable += src[i].objects_transitively_reachable; + dest[i].objects_not_transitively_reachable += src[i].objects_not_transitively_reachable; + } +} + +void +stats_zero_thread(_PyThreadStateImpl *tstate) +{ + // Zero the thread local stat counters + if (tstate->pystats_struct) { + memset(tstate->pystats_struct, 0, sizeof(PyStats)); + } +} + +// merge stats for a single thread into the global structure +void +stats_merge_thread(_PyThreadStateImpl *tstate) +{ + PyStats *src = tstate->pystats_struct; + PyStats *dest = ((PyThreadState *)tstate)->interp->pystats_struct; + + if (src == NULL || dest == NULL) { + return; + } + + // Merge each category of stats using the helper functions. + merge_opcode_stats_array(dest->opcode_stats, src->opcode_stats); + merge_call_stats(&dest->call_stats, &src->call_stats); + merge_object_stats(&dest->object_stats, &src->object_stats); + merge_optimization_stats(&dest->optimization_stats, &src->optimization_stats); + merge_ft_stats(&dest->ft_stats, &src->ft_stats); + merge_rare_event_stats(&dest->rare_event_stats, &src->rare_event_stats); + merge_gc_stats_array(dest->gc_stats, src->gc_stats); +} +#endif // Py_GIL_DISABLED + +// toggle stats collection on or off for all threads +static int +stats_toggle_on_off(PyThreadState *tstate, int on) +{ + bool changed = false; + PyInterpreterState *interp = tstate->interp; + STATS_LOCK(interp); + if (on && interp->pystats_struct == NULL) { + PyStats *s = PyMem_RawCalloc(1, sizeof(PyStats)); + if (s == NULL) { + STATS_UNLOCK(interp); + return -1; + } + FT_ATOMIC_STORE_PTR_RELAXED(interp->pystats_struct, s); + } + if (tstate->interp->pystats_enabled != on) { + FT_ATOMIC_STORE_INT_RELAXED(interp->pystats_enabled, on); + changed = true; + } + STATS_UNLOCK(interp); + if (!changed) { + return 0; + } + _PyEval_StopTheWorld(interp); + _Py_FOR_EACH_TSTATE_UNLOCKED(interp, ts) { + PyStats *s = NULL; + if (interp->pystats_enabled) { +#ifdef Py_GIL_DISABLED + _PyThreadStateImpl *ts_impl = (_PyThreadStateImpl *)ts; + if (ts_impl->pystats_struct == NULL) { + // first activation for this thread, allocate structure + ts_impl->pystats_struct = PyMem_RawCalloc(1, sizeof(PyStats)); + } + s = ts_impl->pystats_struct; +#else + s = ts->interp->pystats_struct; +#endif + } + ts->pystats = s; + } + _PyEval_StartTheWorld(interp); + return 0; +} + +// zero stats for all threads and for the interpreter +static void +stats_zero_all(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + if (tstate == NULL) { + return; + } + if (FT_ATOMIC_LOAD_PTR_RELAXED(tstate->interp->pystats_struct) == NULL) { + return; + } + PyInterpreterState *interp = tstate->interp; + _PyEval_StopTheWorld(interp); +#ifdef Py_GIL_DISABLED + _Py_FOR_EACH_TSTATE_UNLOCKED(interp, ts) { + stats_zero_thread((_PyThreadStateImpl *)ts); + } +#endif + if (interp->pystats_struct) { + memset(interp->pystats_struct, 0, sizeof(PyStats)); + } + _PyEval_StartTheWorld(interp); +} + +// merge stats for all threads into the per-interpreter structure +static void +stats_merge_all(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + if (tstate == NULL) { + return; + } + if (FT_ATOMIC_LOAD_PTR_RELAXED(tstate->interp->pystats_struct) == NULL) { + return; + } + PyInterpreterState *interp = tstate->interp; + _PyEval_StopTheWorld(interp); +#ifdef Py_GIL_DISABLED + _Py_FOR_EACH_TSTATE_UNLOCKED(interp, ts) { + stats_merge_thread((_PyThreadStateImpl *)ts); + stats_zero_thread((_PyThreadStateImpl *)ts); + } +#endif + _PyEval_StartTheWorld(interp); +} + +int +_Py_StatsOn(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + return stats_toggle_on_off(tstate, 1); +} + +void +_Py_StatsOff(void) +{ + PyThreadState *tstate = _PyThreadState_GET(); + stats_toggle_on_off(tstate, 0); +} + +void +_Py_StatsClear(void) +{ + stats_zero_all(); +} + +static int +mem_is_zero(unsigned char *ptr, size_t size) +{ + for (size_t i=0; i < size; i++) { + if (*ptr != 0) { + return 0; + } + ptr++; + } + return 1; +} + +int +_Py_PrintSpecializationStats(int to_file) +{ + assert(to_file); + stats_merge_all(); + PyThreadState *tstate = _PyThreadState_GET(); + STATS_LOCK(tstate->interp); + PyStats *stats = tstate->interp->pystats_struct; + if (stats == NULL) { + STATS_UNLOCK(tstate->interp); + return 0; + } +#define MEM_IS_ZERO(DATA) mem_is_zero((unsigned char*)DATA, sizeof(*(DATA))) + int is_zero = ( + MEM_IS_ZERO(stats->gc_stats) // is a pointer + && MEM_IS_ZERO(&stats->opcode_stats) + && MEM_IS_ZERO(&stats->call_stats) + && MEM_IS_ZERO(&stats->object_stats) + ); +#undef MEM_IS_ZERO + STATS_UNLOCK(tstate->interp); + if (is_zero) { + // gh-108753: -X pystats command line was used, but then _stats_off() + // and _stats_clear() have been called: in this case, avoid printing + // useless "all zeros" statistics. + return 0; + } + + FILE *out = stderr; + if (to_file) { + /* Write to a file instead of stderr. */ +# ifdef MS_WINDOWS + const char *dirname = "c:\\temp\\py_stats\\"; +# else + const char *dirname = "/tmp/py_stats/"; +# endif + /* Use random 160 bit number as file name, + * to avoid both accidental collisions and + * symlink attacks. */ + unsigned char rand[20]; + char hex_name[41]; + _PyOS_URandomNonblock(rand, 20); + for (int i = 0; i < 20; i++) { + hex_name[2*i] = Py_hexdigits[rand[i]&15]; + hex_name[2*i+1] = Py_hexdigits[(rand[i]>>4)&15]; + } + hex_name[40] = '\0'; + char buf[64]; + assert(strlen(dirname) + 40 + strlen(".txt") < 64); + sprintf(buf, "%s%s.txt", dirname, hex_name); + FILE *fout = fopen(buf, "w"); + if (fout) { + out = fout; + } + } + else { + fprintf(out, "Specialization stats:\n"); + } + STATS_LOCK(tstate->interp); + print_stats(out, stats); + STATS_UNLOCK(tstate->interp); + if (out != stderr) { + fclose(out); + } + return 1; +} + +PyStatus +_PyStats_InterpInit(PyInterpreterState *interp) +{ + if (interp->config._pystats) { + // start with pystats enabled, can be disabled via sys._stats_off() + // this needs to be set before the first tstate is created + interp->pystats_enabled = 1; + interp->pystats_struct = PyMem_RawCalloc(1, sizeof(PyStats)); + if (interp->pystats_struct == NULL) { + return _PyStatus_ERR("out-of-memory while initializing interpreter"); + } + } + return _PyStatus_OK(); +} + +bool +_PyStats_ThreadInit(PyInterpreterState *interp, _PyThreadStateImpl *tstate) +{ +#ifdef Py_GIL_DISABLED + if (FT_ATOMIC_LOAD_INT_RELAXED(interp->pystats_enabled)) { + assert(interp->pystats_struct != NULL); + tstate->pystats_struct = PyMem_RawCalloc(1, sizeof(PyStats)); + if (tstate->pystats_struct == NULL) { + return false; + } + } +#endif + return true; +} + +void +_PyStats_ThreadFini(_PyThreadStateImpl *tstate) +{ +#ifdef Py_GIL_DISABLED + STATS_LOCK(((PyThreadState *)tstate)->interp); + stats_merge_thread(tstate); + STATS_UNLOCK(((PyThreadState *)tstate)->interp); + PyMem_RawFree(tstate->pystats_struct); +#endif +} + +void +_PyStats_Attach(_PyThreadStateImpl *tstate_impl) +{ + PyStats *s; + PyThreadState *tstate = (PyThreadState *)tstate_impl; + PyInterpreterState *interp = tstate->interp; + if (FT_ATOMIC_LOAD_INT_RELAXED(interp->pystats_enabled)) { +#ifdef Py_GIL_DISABLED + s = ((_PyThreadStateImpl *)tstate)->pystats_struct; +#else + s = tstate->interp->pystats_struct; +#endif + } + else { + s = NULL; + } + tstate->pystats = s; +} + +void +_PyStats_Detach(_PyThreadStateImpl *tstate_impl) +{ + ((PyThreadState *)tstate_impl)->pystats = NULL; +} + +#endif // Py_STATS diff --git a/Python/pystrhex.c b/Python/pystrhex.c index 38484f5a7d4227..8fb1fa36f85e73 100644 --- a/Python/pystrhex.c +++ b/Python/pystrhex.c @@ -4,9 +4,118 @@ #include "pycore_strhex.h" // _Py_strhex_with_sep() #include "pycore_unicodeobject.h" // _PyUnicode_CheckConsistency() -static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen, - PyObject* sep, int bytes_per_sep_group, - const int return_bytes) +/* Scalar hexlify: convert len bytes to 2*len hex characters. + Uses table lookup via Py_hexdigits for the conversion. */ +static inline void +_Py_hexlify_scalar(const unsigned char *src, Py_UCS1 *dst, Py_ssize_t len) +{ + /* Various optimizations like using math instead of a table lookup, + manually unrolling the loop, storing the global table pointer locally, + and doing wider dst writes have been tried and benchmarked; all produced + nearly identical performance on gcc 15. Using a 256 entry uint16_t + table was a bit slower. So we keep our old simple and obvious code. */ + for (Py_ssize_t i = 0; i < len; i++) { + unsigned char c = src[i]; + *dst++ = Py_hexdigits[c >> 4]; + *dst++ = Py_hexdigits[c & 0x0f]; + } +} + +/* Portable SIMD optimization for hexlify using GCC/Clang vector extensions. + Uses __builtin_shufflevector for portable interleave that compiles to + native SIMD instructions (SSE2 punpcklbw/punpckhbw on x86-64 [always], + NEON zip1/zip2 on ARM64 [always], & vzip on ARM32 when compiler flags + for the target microarch allow it [try -march=native if running 32-bit + on an RPi3 or later]). + + Performance: + - For more common small data it varies between 1.1-3x faster. + - Up to 11x faster on larger data than the scalar code. + + While faster is possible for big data using AVX2 or AVX512, that + adds a ton of complication. Who ever really hexes huge data? + The 16-64 byte boosts align nicely with md5 - sha512 hexdigests. +*/ +#ifdef _Py_HAVE_EFFICIENT_BUILTIN_SHUFFLEVECTOR + +/* 128-bit vector of 16 unsigned bytes */ +typedef unsigned char v16u8 __attribute__((vector_size(16))); +/* 128-bit vector of 16 signed bytes - for efficient comparison. + Using signed comparison generates pcmpgtb on x86-64 instead of + the slower psubusb+pcmpeqb sequence from unsigned comparison. + ARM NEON performs the same either way. */ +typedef signed char v16s8 __attribute__((vector_size(16))); + +/* Splat a byte value across all 16 lanes */ +static inline v16u8 +v16u8_splat(unsigned char x) +{ + return (v16u8){x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x}; +} + +static inline v16s8 +v16s8_splat(signed char x) +{ + return (v16s8){x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x}; +} + +/* Portable SIMD hexlify: converts 16 bytes to 32 hex chars per iteration. + Compiles to native SSE2 on x86-64, NEON on ARM64 (and some ARM32). */ +static void +_Py_hexlify_simd(const unsigned char *src, Py_UCS1 *dst, Py_ssize_t len) +{ + const v16u8 mask_0f = v16u8_splat(0x0f); + const v16u8 ascii_0 = v16u8_splat('0'); + const v16u8 offset = v16u8_splat('a' - '0' - 10); /* 0x27 */ + const v16u8 four = v16u8_splat(4); + const v16s8 nine = v16s8_splat(9); + + Py_ssize_t i = 0; + + /* Process 16 bytes at a time */ + for (; i + 16 <= len; i += 16, dst += 32) { + /* Load 16 bytes (memcpy for safe unaligned access) */ + v16u8 data; + memcpy(&data, src + i, 16); + + /* Extract high and low nibbles using vector operators */ + v16u8 hi = (data >> four) & mask_0f; + v16u8 lo = data & mask_0f; + + /* Compare > 9 using signed comparison for efficient codegen. + Nibble values 0-15 are safely in signed byte range. + This generates pcmpgtb on x86-64, avoiding the slower + psubusb+pcmpeqb sequence from unsigned comparison. */ + v16u8 hi_gt9 = (v16u8)((v16s8)hi > nine); + v16u8 lo_gt9 = (v16u8)((v16s8)lo > nine); + + /* Convert nibbles to hex ASCII */ + hi = hi + ascii_0 + (hi_gt9 & offset); + lo = lo + ascii_0 + (lo_gt9 & offset); + + /* Interleave hi/lo nibbles using portable shufflevector. + This compiles to punpcklbw/punpckhbw on x86-64, zip1/zip2 on ARM64, + or vzip on ARM32. */ + v16u8 result0 = __builtin_shufflevector(hi, lo, + 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23); + v16u8 result1 = __builtin_shufflevector(hi, lo, + 8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31); + + /* Store 32 hex characters */ + memcpy(dst, &result0, 16); + memcpy(dst + 16, &result1, 16); + } + + /* Scalar fallback for remaining 0-15 bytes */ + _Py_hexlify_scalar(src + i, dst, len - i); +} + +#endif /* _Py_HAVE_EFFICIENT_BUILTIN_SHUFFLEVECTOR */ + +static PyObject * +_Py_strhex_impl(const char* argbuf, Py_ssize_t arglen, + PyObject* sep, Py_ssize_t bytes_per_sep_group, + int return_bytes) { assert(arglen >= 0); @@ -42,8 +151,7 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen, else { bytes_per_sep_group = 0; } - - unsigned int abs_bytes_per_sep = Py_ABS(bytes_per_sep_group); + size_t abs_bytes_per_sep = _Py_ABS_CAST(size_t, bytes_per_sep_group); Py_ssize_t resultlen = 0; if (bytes_per_sep_group && arglen > 0) { /* How many sep characters we'll be inserting. */ @@ -83,19 +191,21 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen, unsigned char c; if (bytes_per_sep_group == 0) { - for (i = j = 0; i < arglen; ++i) { - assert((j + 1) < resultlen); - c = argbuf[i]; - retbuf[j++] = Py_hexdigits[c >> 4]; - retbuf[j++] = Py_hexdigits[c & 0x0f]; +#ifdef _Py_HAVE_EFFICIENT_BUILTIN_SHUFFLEVECTOR + if (arglen >= 16) { + _Py_hexlify_simd((const unsigned char *)argbuf, retbuf, arglen); + } + else +#endif + { + _Py_hexlify_scalar((const unsigned char *)argbuf, retbuf, arglen); } - assert(j == resultlen); } else { /* The number of complete chunk+sep periods */ Py_ssize_t chunks = (arglen - 1) / abs_bytes_per_sep; Py_ssize_t chunk; - unsigned int k; + size_t k; if (bytes_per_sep_group < 0) { i = j = 0; @@ -143,30 +253,30 @@ static PyObject *_Py_strhex_impl(const char* argbuf, const Py_ssize_t arglen, return retval; } -PyObject * _Py_strhex(const char* argbuf, const Py_ssize_t arglen) +PyObject * _Py_strhex(const char* argbuf, Py_ssize_t arglen) { return _Py_strhex_impl(argbuf, arglen, NULL, 0, 0); } /* Same as above but returns a bytes() instead of str() to avoid the * need to decode the str() when bytes are needed. */ -PyObject* _Py_strhex_bytes(const char* argbuf, const Py_ssize_t arglen) +PyObject* _Py_strhex_bytes(const char* argbuf, Py_ssize_t arglen) { return _Py_strhex_impl(argbuf, arglen, NULL, 0, 1); } /* These variants include support for a separator between every N bytes: */ -PyObject* _Py_strhex_with_sep(const char* argbuf, const Py_ssize_t arglen, - PyObject* sep, const int bytes_per_group) +PyObject* _Py_strhex_with_sep(const char* argbuf, Py_ssize_t arglen, + PyObject* sep, Py_ssize_t bytes_per_group) { return _Py_strhex_impl(argbuf, arglen, sep, bytes_per_group, 0); } /* Same as above but returns a bytes() instead of str() to avoid the * need to decode the str() when bytes are needed. */ -PyObject* _Py_strhex_bytes_with_sep(const char* argbuf, const Py_ssize_t arglen, - PyObject* sep, const int bytes_per_group) +PyObject* _Py_strhex_bytes_with_sep(const char* argbuf, Py_ssize_t arglen, + PyObject* sep, Py_ssize_t bytes_per_group) { return _Py_strhex_impl(argbuf, arglen, sep, bytes_per_group, 1); } diff --git a/Python/pystrtod.c b/Python/pystrtod.c index 7b74f613ed563b..e8aca939d1fb98 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -43,7 +43,7 @@ _Py_parse_inf_or_nan(const char *p, char **endptr) s += 3; if (case_insensitive_match(s, "inity")) s += 5; - retval = negate ? -Py_INFINITY : Py_INFINITY; + retval = negate ? -INFINITY : INFINITY; } else if (case_insensitive_match(s, "nan")) { s += 3; @@ -286,7 +286,7 @@ _PyOS_ascii_strtod(const char *nptr, char **endptr) string, -1.0 is returned and again ValueError is raised. On overflow (e.g., when trying to convert '1e500' on an IEEE 754 machine), - if overflow_exception is NULL then +-Py_INFINITY is returned, and no Python + if overflow_exception is NULL then +-INFINITY is returned, and no Python exception is raised. Otherwise, overflow_exception should point to a Python exception, this exception will be raised, -1.0 will be returned, and *endptr will point just past the end of the converted value. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 8f1c78bf831863..971ab064777a41 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -478,9 +478,6 @@ _PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int closeit, if (PyDict_SetItemString(dict, "__file__", filename) < 0) { goto done; } - if (PyDict_SetItemString(dict, "__cached__", Py_None) < 0) { - goto done; - } set_file_name = 1; } @@ -535,9 +532,6 @@ _PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int closeit, if (PyDict_PopString(dict, "__file__", NULL) < 0) { PyErr_Print(); } - if (PyDict_PopString(dict, "__cached__", NULL) < 0) { - PyErr_Print(); - } } Py_XDECREF(main_module); return ret; @@ -573,6 +567,7 @@ _PyRun_SimpleStringFlagsWithName(const char *command, const char* name, PyCompil PyObject* the_name = PyUnicode_FromString(name); if (!the_name) { PyErr_Print(); + Py_DECREF(main_module); return -1; } res = _PyRun_StringFlagsWithName(command, the_name, Py_file_input, dict, dict, flags, 0); @@ -1151,6 +1146,7 @@ _PyErr_Display(PyObject *file, PyObject *unused, PyObject *value, PyObject *tb) "traceback", "_print_exception_bltin"); if (print_exception_fn == NULL || !PyCallable_Check(print_exception_fn)) { + Py_XDECREF(print_exception_fn); goto fallback; } @@ -1181,7 +1177,7 @@ _PyErr_Display(PyObject *file, PyObject *unused, PyObject *value, PyObject *tb) } if (print_exception_recursive(&ctx, value) < 0) { PyErr_Clear(); - _PyObject_Dump(value); + PyObject_Dump(value); fprintf(stderr, "lost sys.stderr\n"); } Py_XDECREF(ctx.seen); @@ -1199,14 +1195,14 @@ PyErr_Display(PyObject *unused, PyObject *value, PyObject *tb) PyObject *file; if (PySys_GetOptionalAttr(&_Py_ID(stderr), &file) < 0) { PyObject *exc = PyErr_GetRaisedException(); - _PyObject_Dump(value); + PyObject_Dump(value); fprintf(stderr, "lost sys.stderr\n"); - _PyObject_Dump(exc); + PyObject_Dump(exc); Py_DECREF(exc); return; } if (file == NULL) { - _PyObject_Dump(value); + PyObject_Dump(value); fprintf(stderr, "lost sys.stderr\n"); return; } @@ -1252,12 +1248,19 @@ _PyRun_StringFlagsWithName(const char *str, PyObject* name, int start, } else { name = &_Py_STR(anon_string); } + PyObject *module = NULL; + if (globals && PyDict_GetItemStringRef(globals, "__name__", &module) < 0) { + goto done; + } - mod = _PyParser_ASTFromString(str, name, start, flags, arena); + mod = _PyParser_ASTFromString(str, name, start, flags, arena, module); + Py_XDECREF(module); - if (mod != NULL) { + if (mod != NULL) { ret = run_mod(mod, name, globals, locals, flags, arena, source, generate_new_source); } + +done: Py_XDECREF(source); _PyArena_Free(arena); return ret; @@ -1346,8 +1349,9 @@ static PyObject * run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, PyObject *locals) { /* Set globals['__builtins__'] if it doesn't exist */ - if (!globals || !PyDict_Check(globals)) { - PyErr_SetString(PyExc_SystemError, "globals must be a real dict"); + if (!globals || !PyAnyDict_Check(globals)) { + PyErr_SetString(PyExc_SystemError, + "globals must be a real dict or a real frozendict"); return NULL; } int has_builtins = PyDict_ContainsString(globals, "__builtins__"); @@ -1365,6 +1369,29 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py return PyEval_EvalCode((PyObject*)co, globals, locals); } +static PyObject * +get_interactive_filename(PyObject *filename, Py_ssize_t count) +{ + PyObject *result; + Py_ssize_t len = PyUnicode_GET_LENGTH(filename); + + if (len >= 2 + && PyUnicode_ReadChar(filename, 0) == '<' + && PyUnicode_ReadChar(filename, len - 1) == '>') { + PyObject *middle = PyUnicode_Substring(filename, 1, len-1); + if (middle == NULL) { + return NULL; + } + result = PyUnicode_FromFormat("<%U-%zd>", middle, count); + Py_DECREF(middle); + } else { + result = PyUnicode_FromFormat( + "%U-%zd", filename, count); + } + return result; + +} + static PyObject * run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals, PyCompilerFlags *flags, PyArena *arena, PyObject* interactive_src, @@ -1375,8 +1402,8 @@ run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals, if (interactive_src) { PyInterpreterState *interp = tstate->interp; if (generate_new_source) { - interactive_filename = PyUnicode_FromFormat( - "%U-%d", filename, interp->_interactive_src_count++); + interactive_filename = get_interactive_filename( + filename, interp->_interactive_src_count++); } else { Py_INCREF(interactive_filename); } @@ -1384,8 +1411,17 @@ run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals, return NULL; } } + PyObject *module = NULL; + if (globals && PyDict_GetItemStringRef(globals, "__name__", &module) < 0) { + if (interactive_src) { + Py_DECREF(interactive_filename); + } + return NULL; + } - PyCodeObject *co = _PyAST_Compile(mod, interactive_filename, flags, -1, arena); + PyCodeObject *co = _PyAST_Compile(mod, interactive_filename, flags, -1, + arena, module); + Py_XDECREF(module); if (co == NULL) { if (interactive_src) { Py_DECREF(interactive_filename); @@ -1484,6 +1520,14 @@ run_pyc_file(FILE *fp, PyObject *globals, PyObject *locals, PyObject * Py_CompileStringObject(const char *str, PyObject *filename, int start, PyCompilerFlags *flags, int optimize) +{ + return _Py_CompileStringObjectWithModule(str, filename, start, + flags, optimize, NULL); +} + +PyObject * +_Py_CompileStringObjectWithModule(const char *str, PyObject *filename, int start, + PyCompilerFlags *flags, int optimize, PyObject *module) { PyCodeObject *co; mod_ty mod; @@ -1491,14 +1535,16 @@ Py_CompileStringObject(const char *str, PyObject *filename, int start, if (arena == NULL) return NULL; - mod = _PyParser_ASTFromString(str, filename, start, flags, arena); + mod = _PyParser_ASTFromString(str, filename, start, flags, arena, module); if (mod == NULL) { _PyArena_Free(arena); return NULL; } if (flags && (flags->cf_flags & PyCF_ONLY_AST)) { int syntax_check_only = ((flags->cf_flags & PyCF_OPTIMIZED_AST) == PyCF_ONLY_AST); /* unoptiomized AST */ - if (_PyCompile_AstPreprocess(mod, filename, flags, optimize, arena, syntax_check_only) < 0) { + if (_PyCompile_AstPreprocess(mod, filename, flags, optimize, arena, + syntax_check_only, module) < 0) + { _PyArena_Free(arena); return NULL; } @@ -1506,7 +1552,7 @@ Py_CompileStringObject(const char *str, PyObject *filename, int start, _PyArena_Free(arena); return result; } - co = _PyAST_Compile(mod, filename, flags, optimize, arena); + co = _PyAST_Compile(mod, filename, flags, optimize, arena, module); _PyArena_Free(arena); return (PyObject *)co; } diff --git a/Python/pytime.c b/Python/pytime.c index 67cf6437264490..399ff59ad01ab6 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -2,7 +2,7 @@ #include "pycore_initconfig.h" // _PyStatus_ERR #include "pycore_pystate.h" // _Py_AssertHoldsTstate() #include "pycore_runtime.h" // _PyRuntime -#include "pycore_time.h" // PyTime_t +#include "pycore_time.h" // export _PyLong_FromTime_t() #include <time.h> // gmtime_r() #ifdef HAVE_SYS_TIME_H @@ -273,6 +273,93 @@ _PyTime_AsCLong(PyTime_t t, long *t2) *t2 = (long)t; return 0; } + +// Seconds between 1601-01-01 and 1970-01-01: +// 369 years + 89 leap days. +#define SECS_BETWEEN_EPOCHS 11644473600LL +#define HUNDRED_NS_PER_SEC 10000000LL + +// Calculate day of year (0-365) from SYSTEMTIME +static int +_PyTime_calc_yday(const SYSTEMTIME *st) +{ + // Cumulative days before each month (non-leap year) + static const int days_before_month[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + int yday = days_before_month[st->wMonth - 1] + st->wDay - 1; + // Account for leap day if we're past February in a leap year. + if (st->wMonth > 2) { + // Leap year rules (Gregorian calendar): + // - Years divisible by 4 are leap years + // - EXCEPT years divisible by 100 are NOT leap years + // - EXCEPT years divisible by 400 ARE leap years + int year = st->wYear; + int is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); + yday += is_leap; + } + return yday; +} + +// Convert time_t to struct tm using Windows FILETIME API. +// If is_local is true, convert to local time. +// Fallback for negative timestamps that localtime_s/gmtime_s cannot handle. +// Return 0 on success. Return -1 on error. +static int +_PyTime_windows_filetime(time_t timer, struct tm *tm, int is_local) +{ + /* Check for underflow - FILETIME epoch is 1601-01-01 */ + if (timer < -SECS_BETWEEN_EPOCHS) { + PyErr_SetString(PyExc_OverflowError, "timestamp out of range for Windows FILETIME"); + return -1; + } + + /* Convert time_t to FILETIME (100-nanosecond intervals since 1601-01-01) */ + ULONGLONG ticks = ((ULONGLONG)timer + SECS_BETWEEN_EPOCHS) * HUNDRED_NS_PER_SEC; + FILETIME ft; + ft.dwLowDateTime = (DWORD)(ticks); // cast to DWORD truncates to low 32 bits + ft.dwHighDateTime = (DWORD)(ticks >> 32); + + /* Convert FILETIME to SYSTEMTIME (UTC) */ + SYSTEMTIME st_utc; + if (!FileTimeToSystemTime(&ft, &st_utc)) { + PyErr_SetFromWindowsErr(0); + return -1; + } + + SYSTEMTIME st_result; + if (is_local) { + /* Convert UTC SYSTEMTIME to local SYSTEMTIME. + * We use SystemTimeToTzSpecificLocalTime instead of + * FileTimeToLocalFileTime because the latter always applies the + * _current_ DST bias, whereas the former applies the correct + * DST rules for the date being converted (gh-80620). */ + if (!SystemTimeToTzSpecificLocalTime(NULL, &st_utc, &st_result)) { + PyErr_SetFromWindowsErr(0); + return -1; + } + } + else { + st_result = st_utc; + } + + /* Convert SYSTEMTIME to struct tm */ + tm->tm_year = st_result.wYear - 1900; + tm->tm_mon = st_result.wMonth - 1; /* SYSTEMTIME: 1-12, tm: 0-11 */ + tm->tm_mday = st_result.wDay; + tm->tm_hour = st_result.wHour; + tm->tm_min = st_result.wMinute; + tm->tm_sec = st_result.wSecond; + tm->tm_wday = st_result.wDayOfWeek; /* 0=Sunday */ + + // `time.gmtime` and `time.localtime` will return `struct_time` containing this + tm->tm_yday = _PyTime_calc_yday(&st_result); + + /* DST flag: -1 (unknown) for local time on historical dates, 0 for UTC */ + tm->tm_isdst = is_local ? -1 : 0; + + return 0; +} #endif @@ -368,8 +455,20 @@ pytime_object_to_denominator(PyObject *obj, time_t *sec, long *numerator, { assert(denominator >= 1); - if (PyFloat_Check(obj)) { + if (PyIndex_Check(obj)) { + *sec = _PyLong_AsTime_t(obj); + *numerator = 0; + if (*sec == (time_t)-1 && PyErr_Occurred()) { + return -1; + } + return 0; + } + else { double d = PyFloat_AsDouble(obj); + if (d == -1 && PyErr_Occurred()) { + *numerator = 0; + return -1; + } if (isnan(d)) { *numerator = 0; PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)"); @@ -378,30 +477,28 @@ pytime_object_to_denominator(PyObject *obj, time_t *sec, long *numerator, return pytime_double_to_denominator(d, sec, numerator, denominator, round); } - else { - *sec = _PyLong_AsTime_t(obj); - *numerator = 0; - if (*sec == (time_t)-1 && PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "argument must be int or float, not %T", obj); - } - return -1; - } - return 0; - } } int _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) { - if (PyFloat_Check(obj)) { + if (PyIndex_Check(obj)) { + *sec = _PyLong_AsTime_t(obj); + if (*sec == (time_t)-1 && PyErr_Occurred()) { + return -1; + } + return 0; + } + else { double intpart; /* volatile avoids optimization changing how numbers are rounded */ volatile double d; d = PyFloat_AsDouble(obj); + if (d == -1 && PyErr_Occurred()) { + return -1; + } if (isnan(d)) { PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)"); return -1; @@ -418,13 +515,6 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) *sec = (time_t)intpart; return 0; } - else { - *sec = _PyLong_AsTime_t(obj); - if (*sec == (time_t)-1 && PyErr_Occurred()) { - return -1; - } - return 0; - } } @@ -469,31 +559,6 @@ _PyTime_FromMicrosecondsClamp(PyTime_t us) } -int -_PyTime_FromLong(PyTime_t *tp, PyObject *obj) -{ - if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_TypeError, "expect int, got %s", - Py_TYPE(obj)->tp_name); - return -1; - } - - static_assert(sizeof(long long) == sizeof(PyTime_t), - "PyTime_t is not long long"); - long long nsec = PyLong_AsLongLong(obj); - if (nsec == -1 && PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { - pytime_overflow(); - } - return -1; - } - - PyTime_t t = (PyTime_t)nsec; - *tp = t; - return 0; -} - - #ifdef HAVE_CLOCK_GETTIME static int pytime_fromtimespec(PyTime_t *tp, const struct timespec *ts, int raise_exc) @@ -586,39 +651,38 @@ static int pytime_from_object(PyTime_t *tp, PyObject *obj, _PyTime_round_t round, long unit_to_ns) { - if (PyFloat_Check(obj)) { + if (PyIndex_Check(obj)) { + long long sec = PyLong_AsLongLong(obj); + if (sec == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + pytime_overflow(); + } + return -1; + } + + static_assert(sizeof(long long) <= sizeof(PyTime_t), + "PyTime_t is smaller than long long"); + PyTime_t ns = (PyTime_t)sec; + if (pytime_mul(&ns, unit_to_ns) < 0) { + pytime_overflow(); + return -1; + } + + *tp = ns; + return 0; + } + else { double d; d = PyFloat_AsDouble(obj); + if (d == -1 && PyErr_Occurred()) { + return -1; + } if (isnan(d)) { PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)"); return -1; } return pytime_from_double(tp, d, round, unit_to_ns); } - - long long sec = PyLong_AsLongLong(obj); - if (sec == -1 && PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) { - pytime_overflow(); - } - else if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "'%T' object cannot be interpreted as an integer or float", - obj); - } - return -1; - } - - static_assert(sizeof(long long) <= sizeof(PyTime_t), - "PyTime_t is smaller than long long"); - PyTime_t ns = (PyTime_t)sec; - if (pytime_mul(&ns, unit_to_ns) < 0) { - pytime_overflow(); - return -1; - } - - *tp = ns; - return 0; } @@ -656,14 +720,6 @@ PyTime_AsSecondsDouble(PyTime_t ns) } -PyObject * -_PyTime_AsLong(PyTime_t ns) -{ - static_assert(sizeof(long long) >= sizeof(PyTime_t), - "PyTime_t is larger than long long"); - return PyLong_FromLongLong((long long)ns); -} - int _PyTime_FromSecondsDouble(double seconds, _PyTime_round_t round, PyTime_t *result) { @@ -913,10 +969,8 @@ py_get_system_clock(PyTime_t *tp, _Py_clock_info_t *info, int raise_exc) GetSystemTimePreciseAsFileTime(&system_time); large.u.LowPart = system_time.dwLowDateTime; large.u.HighPart = system_time.dwHighDateTime; - /* 11,644,473,600,000,000,000: number of nanoseconds between - the 1st january 1601 and the 1st january 1970 (369 years + 89 leap - days). */ - PyTime_t ns = (large.QuadPart - 116444736000000000) * 100; + + PyTime_t ns = (large.QuadPart - SECS_BETWEEN_EPOCHS * HUNDRED_NS_PER_SEC) * 100; *tp = ns; if (info) { // GetSystemTimePreciseAsFileTime() is implemented using @@ -1273,15 +1327,19 @@ int _PyTime_localtime(time_t t, struct tm *tm) { #ifdef MS_WINDOWS - int error; - - error = localtime_s(tm, &t); - if (error != 0) { - errno = error; - PyErr_SetFromErrno(PyExc_OSError); - return -1; + if (t >= 0) { + /* For non-negative timestamps, use localtime_s() */ + int error = localtime_s(tm, &t); + if (error != 0) { + errno = error; + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; } - return 0; + + /* For negative timestamps, use FILETIME-based conversion */ + return _PyTime_windows_filetime(t, tm, 1); #else /* !MS_WINDOWS */ #if defined(_AIX) && (SIZEOF_TIME_T < 8) @@ -1312,15 +1370,19 @@ int _PyTime_gmtime(time_t t, struct tm *tm) { #ifdef MS_WINDOWS - int error; - - error = gmtime_s(tm, &t); - if (error != 0) { - errno = error; - PyErr_SetFromErrno(PyExc_OSError); - return -1; + /* For non-negative timestamps, use gmtime_s() */ + if (t >= 0) { + int error = gmtime_s(tm, &t); + if (error != 0) { + errno = error; + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; } - return 0; + + /* For negative timestamps, use FILETIME-based conversion */ + return _PyTime_windows_filetime(t, tm, 0); #else /* !MS_WINDOWS */ if (gmtime_r(&t, tm) == NULL) { #ifdef EINVAL diff --git a/Python/qsbr.c b/Python/qsbr.c index c992c285cb13e4..e9d935bfb40d84 100644 --- a/Python/qsbr.c +++ b/Python/qsbr.c @@ -36,6 +36,7 @@ #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_qsbr.h" #include "pycore_tstate.h" // _PyThreadStateImpl +#include "pycore_stats.h" // FT_STAT_QSBR_POLL_INC() // Starting size of the array of qsbr thread states @@ -56,7 +57,7 @@ qsbr_allocate(struct _qsbr_shared *shared) return qsbr; } -// Initialize (or reintialize) the freelist of QSBR thread states +// Initialize (or reinitialize) the freelist of QSBR thread states static void initialize_new_array(struct _qsbr_shared *shared) { @@ -84,22 +85,29 @@ grow_thread_array(struct _qsbr_shared *shared) new_size = MIN_ARRAY_SIZE; } - struct _qsbr_pad *array = PyMem_RawCalloc(new_size, sizeof(*array)); - if (array == NULL) { + // Overallocate by 63 bytes so we can align to a 64-byte boundary. + // This avoids potential false sharing between the first entry and other + // allocations. + size_t alignment = 64; + size_t alloc_size = (size_t)new_size * sizeof(struct _qsbr_pad) + alignment - 1; + void *raw = PyMem_RawCalloc(1, alloc_size); + if (raw == NULL) { return -1; } + struct _qsbr_pad *array = _Py_ALIGN_UP(raw, alignment); - struct _qsbr_pad *old = shared->array; - if (old != NULL) { + void *old_raw = shared->array_raw; + if (shared->array != NULL) { memcpy(array, shared->array, shared->size * sizeof(*array)); } shared->array = array; + shared->array_raw = raw; shared->size = new_size; shared->freelist = NULL; initialize_new_array(shared); - PyMem_RawFree(old); + PyMem_RawFree(old_raw); return 0; } @@ -158,7 +166,7 @@ _Py_qsbr_poll(struct _qsbr_thread_state *qsbr, uint64_t goal) if (_Py_qbsr_goal_reached(qsbr, goal)) { return true; } - + FT_STAT_QSBR_POLL_INC(); uint64_t rd_seq = qsbr_poll_scan(qsbr->shared); return QSBR_LEQ(goal, rd_seq); } @@ -256,8 +264,9 @@ void _Py_qsbr_fini(PyInterpreterState *interp) { struct _qsbr_shared *shared = &interp->qsbr; - PyMem_RawFree(shared->array); + PyMem_RawFree(shared->array_raw); shared->array = NULL; + shared->array_raw = NULL; shared->size = 0; shared->freelist = NULL; } diff --git a/Python/record_functions.c.h b/Python/record_functions.c.h new file mode 100644 index 00000000000000..98abe3d0505e20 --- /dev/null +++ b/Python/record_functions.c.h @@ -0,0 +1,335 @@ +// This file is generated by Tools/cases_generator/record_function_generator.py +// from: +// Python/bytecodes.c +// Do not edit! + +#ifdef TIER_ONE + #error "This file is for Tier 2 only" +#endif +void _PyOpcode_RecordFunction_TOS(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + _PyStackRef tos; + tos = stack_pointer[-1]; + *recorded_value = (PyObject *)PyStackRef_AsPyObjectBorrow(tos); + Py_INCREF(*recorded_value); +} + +void _PyOpcode_RecordFunction_TOS_TYPE(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + _PyStackRef tos; + tos = stack_pointer[-1]; + *recorded_value = (PyObject *)Py_TYPE(PyStackRef_AsPyObjectBorrow(tos)); + Py_INCREF(*recorded_value); +} + +void _PyOpcode_RecordFunction_NOS(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + _PyStackRef nos; + nos = stack_pointer[-2]; + *recorded_value = (PyObject *)PyStackRef_AsPyObjectBorrow(nos); + Py_INCREF(*recorded_value); +} + +void _PyOpcode_RecordFunction_NOS_TYPE(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + _PyStackRef nos; + nos = stack_pointer[-2]; + *recorded_value = (PyObject *)Py_TYPE(PyStackRef_AsPyObjectBorrow(nos)); + Py_INCREF(*recorded_value); +} + +void _PyOpcode_RecordFunction_NOS_GEN_FUNC(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + _PyStackRef nos; + nos = stack_pointer[-2]; + PyObject *obj = PyStackRef_AsPyObjectBorrow(nos); + if (PyGen_Check(obj)) { + PyGenObject *gen = (PyGenObject *)obj; + _PyStackRef func = gen->gi_iframe.f_funcobj; + if (!PyStackRef_IsNull(func)) { + *recorded_value = (PyObject *)PyStackRef_AsPyObjectBorrow(func); + Py_INCREF(*recorded_value); + } + } +} + +void _PyOpcode_RecordFunction_3OS_GEN_FUNC(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + _PyStackRef gen; + gen = stack_pointer[-3]; + PyObject *obj = PyStackRef_AsPyObjectBorrow(gen); + if (PyGen_Check(obj)) { + PyGenObject *gen_obj = (PyGenObject *)obj; + _PyStackRef func = gen_obj->gi_iframe.f_funcobj; + if (!PyStackRef_IsNull(func)) { + *recorded_value = (PyObject *)PyStackRef_AsPyObjectBorrow(func); + Py_INCREF(*recorded_value); + } + } +} + +void _PyOpcode_RecordFunction_4OS(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + _PyStackRef value; + value = stack_pointer[-4]; + *recorded_value = (PyObject *)PyStackRef_AsPyObjectBorrow(value); + Py_INCREF(*recorded_value); +} + +void _PyOpcode_RecordFunction_CALLABLE(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + _PyStackRef func; + func = stack_pointer[-2 - oparg]; + *recorded_value = (PyObject *)PyStackRef_AsPyObjectBorrow(func); + Py_INCREF(*recorded_value); +} + +void _PyOpcode_RecordFunction_CALLABLE_KW(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + _PyStackRef func; + func = stack_pointer[-3 - oparg]; + *recorded_value = (PyObject *)PyStackRef_AsPyObjectBorrow(func); + Py_INCREF(*recorded_value); +} + +void _PyOpcode_RecordFunction_BOUND_METHOD(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + _PyStackRef callable; + callable = stack_pointer[-2 - oparg]; + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (Py_TYPE(callable_o) == &PyMethod_Type) { + *recorded_value = (PyObject *)callable_o; + Py_INCREF(*recorded_value); + } +} + +void _PyOpcode_RecordFunction_CODE(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value) { + *recorded_value = (PyObject *)NULL; + Py_INCREF(*recorded_value); +} + +#define _RECORD_TOS_TYPE_INDEX 1 +#define _RECORD_NOS_INDEX 2 +#define _RECORD_NOS_TYPE_INDEX 3 +#define _RECORD_3OS_GEN_FUNC_INDEX 4 +#define _RECORD_TOS_INDEX 5 +#define _RECORD_CALLABLE_INDEX 6 +#define _RECORD_CALLABLE_KW_INDEX 7 +#define _RECORD_4OS_INDEX 8 + +const _PyOpcodeRecordEntry _PyOpcode_RecordEntries[256] = { + [TO_BOOL_BOOL] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [TO_BOOL_NONE] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [LOAD_SUPER_ATTR_ATTR] = {1, {_RECORD_NOS_INDEX}}, + [TO_BOOL] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [TO_BOOL_INT] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [TO_BOOL_LIST] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [TO_BOOL_STR] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [TO_BOOL_ALWAYS_TRUE] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_MULTIPLY_INT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_ADD_INT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_SUBTRACT_INT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_MULTIPLY_FLOAT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_ADD_FLOAT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_SUBTRACT_FLOAT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_ADD_UNICODE] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_EXTEND] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_INPLACE_ADD_UNICODE] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_SUBSCR_LIST_INT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_SUBSCR_LIST_SLICE] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_SUBSCR_STR_INT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_SUBSCR_USTR_INT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_SUBSCR_TUPLE_INT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_SUBSCR_DICT] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [BINARY_OP_SUBSCR_GETITEM] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, + [STORE_SUBSCR] = {1, {_RECORD_NOS_TYPE_INDEX}}, + [STORE_SUBSCR_LIST_INT] = {1, {_RECORD_NOS_TYPE_INDEX}}, + [STORE_SUBSCR_DICT] = {1, {_RECORD_NOS_TYPE_INDEX}}, + [SEND] = {1, {_RECORD_3OS_GEN_FUNC_INDEX}}, + [SEND_GEN] = {1, {_RECORD_3OS_GEN_FUNC_INDEX}}, + [FOR_ITER] = {1, {_RECORD_NOS_INDEX}}, + [SEND_VIRTUAL] = {1, {_RECORD_3OS_GEN_FUNC_INDEX}}, + [SEND_ASYNC_GEN] = {1, {_RECORD_3OS_GEN_FUNC_INDEX}}, + [STORE_ATTR] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [LOAD_SUPER_ATTR] = {1, {_RECORD_NOS_INDEX}}, + [LOAD_SUPER_ATTR_METHOD] = {1, {_RECORD_NOS_INDEX}}, + [LOAD_ATTR] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_INSTANCE_VALUE] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_MODULE] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_WITH_HINT] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_SLOT] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_CLASS] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_PROPERTY] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = {1, {_RECORD_TOS_INDEX}}, + [STORE_ATTR_INSTANCE_VALUE] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [STORE_ATTR_WITH_HINT] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [STORE_ATTR_SLOT] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [GET_ITER] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [GET_ITER_SELF] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [GET_ITER_VIRTUAL] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [FOR_ITER_VIRTUAL] = {1, {_RECORD_NOS_INDEX}}, + [FOR_ITER_LIST] = {1, {_RECORD_NOS_INDEX}}, + [FOR_ITER_TUPLE] = {1, {_RECORD_NOS_INDEX}}, + [FOR_ITER_RANGE] = {1, {_RECORD_NOS_INDEX}}, + [FOR_ITER_GEN] = {1, {_RECORD_NOS_INDEX}}, + [LOAD_SPECIAL] = {1, {_RECORD_TOS_TYPE_INDEX}}, + [LOAD_ATTR_METHOD_WITH_VALUES] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_METHOD_NO_DICT] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = {1, {_RECORD_TOS_INDEX}}, + [LOAD_ATTR_METHOD_LAZY_DICT] = {1, {_RECORD_TOS_INDEX}}, + [CALL] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_PY_GENERAL] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_BOUND_METHOD_GENERAL] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_NON_PY_GENERAL] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_BOUND_METHOD_EXACT_ARGS] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_PY_EXACT_ARGS] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_TYPE_1] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_STR_1] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_TUPLE_1] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_ALLOC_AND_ENTER_INIT] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_BUILTIN_CLASS] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_BUILTIN_O] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_BUILTIN_FAST] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_LEN] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_ISINSTANCE] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_LIST_APPEND] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_METHOD_DESCRIPTOR_O] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_METHOD_DESCRIPTOR_NOARGS] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_METHOD_DESCRIPTOR_FAST] = {1, {_RECORD_CALLABLE_INDEX}}, + [CALL_KW_PY] = {1, {_RECORD_CALLABLE_KW_INDEX}}, + [CALL_KW_BOUND_METHOD] = {1, {_RECORD_CALLABLE_KW_INDEX}}, + [CALL_KW] = {1, {_RECORD_CALLABLE_KW_INDEX}}, + [CALL_KW_NON_PY] = {1, {_RECORD_CALLABLE_KW_INDEX}}, + [CALL_FUNCTION_EX] = {1, {_RECORD_4OS_INDEX}}, + [CALL_EX_PY] = {1, {_RECORD_4OS_INDEX}}, + [CALL_EX_NON_PY_GENERAL] = {1, {_RECORD_4OS_INDEX}}, + [BINARY_OP] = {2, {_RECORD_NOS_INDEX, _RECORD_TOS_TYPE_INDEX}}, +}; + +const _PyOpcodeRecordSlotMap _PyOpcode_RecordSlotMaps[256] = { + [TO_BOOL_ALWAYS_TRUE] = {1, 0, {0}}, + [BINARY_OP_SUBSCR_DICT] = {1, 1, {0}}, + [BINARY_OP_SUBSCR_GETITEM] = {1, 0, {0}}, + [STORE_SUBSCR_DICT] = {1, 0, {0}}, + [SEND_GEN] = {1, 0, {0}}, + [FOR_ITER] = {1, 1, {0}}, + [LOAD_SUPER_ATTR_METHOD] = {1, 0, {0}}, + [LOAD_ATTR_INSTANCE_VALUE] = {1, 1, {0}}, + [LOAD_ATTR_WITH_HINT] = {1, 1, {0}}, + [LOAD_ATTR_SLOT] = {1, 1, {0}}, + [LOAD_ATTR_CLASS] = {1, 0, {0}}, + [LOAD_ATTR_CLASS_WITH_METACLASS_CHECK] = {1, 0, {0}}, + [LOAD_ATTR_PROPERTY] = {1, 1, {0}}, + [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = {1, 1, {0}}, + [STORE_ATTR_INSTANCE_VALUE] = {1, 0, {0}}, + [STORE_ATTR_WITH_HINT] = {1, 0, {0}}, + [STORE_ATTR_SLOT] = {1, 0, {0}}, + [GET_ITER] = {1, 0, {0}}, + [GET_ITER_SELF] = {1, 0, {0}}, + [GET_ITER_VIRTUAL] = {1, 0, {0}}, + [FOR_ITER_GEN] = {1, 1, {0}}, + [LOAD_SPECIAL] = {1, 0, {0}}, + [LOAD_ATTR_METHOD_WITH_VALUES] = {1, 1, {0}}, + [LOAD_ATTR_METHOD_NO_DICT] = {1, 1, {0}}, + [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = {1, 1, {0}}, + [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = {1, 1, {0}}, + [LOAD_ATTR_METHOD_LAZY_DICT] = {1, 1, {0}}, + [CALL_PY_GENERAL] = {1, 0, {0}}, + [CALL_BOUND_METHOD_GENERAL] = {1, 1, {0}}, + [CALL_NON_PY_GENERAL] = {1, 0, {0}}, + [CALL_BOUND_METHOD_EXACT_ARGS] = {1, 1, {0}}, + [CALL_PY_EXACT_ARGS] = {1, 0, {0}}, + [CALL_ALLOC_AND_ENTER_INIT] = {1, 0, {0}}, + [CALL_BUILTIN_CLASS] = {1, 0, {0}}, + [CALL_BUILTIN_O] = {1, 0, {0}}, + [CALL_BUILTIN_FAST] = {1, 0, {0}}, + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = {1, 0, {0}}, + [CALL_METHOD_DESCRIPTOR_O] = {1, 0, {0}}, + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = {1, 0, {0}}, + [CALL_METHOD_DESCRIPTOR_NOARGS] = {1, 0, {0}}, + [CALL_KW_PY] = {1, 0, {0}}, + [CALL_KW_BOUND_METHOD] = {1, 0, {0}}, + [CALL_EX_PY] = {1, 0, {0}}, + [BINARY_OP] = {2, 2, {1, 0}}, +}; + +const _Py_RecordFuncPtr _PyOpcode_RecordFunctions[9] = { + [0] = NULL, + [_RECORD_TOS_TYPE_INDEX] = _PyOpcode_RecordFunction_TOS_TYPE, + [_RECORD_NOS_INDEX] = _PyOpcode_RecordFunction_NOS, + [_RECORD_NOS_TYPE_INDEX] = _PyOpcode_RecordFunction_NOS_TYPE, + [_RECORD_3OS_GEN_FUNC_INDEX] = _PyOpcode_RecordFunction_3OS_GEN_FUNC, + [_RECORD_TOS_INDEX] = _PyOpcode_RecordFunction_TOS, + [_RECORD_CALLABLE_INDEX] = _PyOpcode_RecordFunction_CALLABLE, + [_RECORD_CALLABLE_KW_INDEX] = _PyOpcode_RecordFunction_CALLABLE_KW, + [_RECORD_4OS_INDEX] = _PyOpcode_RecordFunction_4OS, +}; + +static PyObject * +_PyOpcode_RecordTransform_NOS_TYPE(PyObject *recorded_value) +{ + PyObject *transformed_value = NULL; + _PyStackRef nos; + nos = PyStackRef_FromPyObjectBorrow(recorded_value); + transformed_value = (PyObject *)Py_TYPE(PyStackRef_AsPyObjectBorrow(nos)); + Py_XINCREF(transformed_value); + Py_DECREF(recorded_value); + return transformed_value; +} + +static PyObject * +_PyOpcode_RecordTransform_TOS_TYPE(PyObject *recorded_value) +{ + PyObject *transformed_value = NULL; + _PyStackRef tos; + tos = PyStackRef_FromPyObjectBorrow(recorded_value); + transformed_value = (PyObject *)Py_TYPE(PyStackRef_AsPyObjectBorrow(tos)); + Py_XINCREF(transformed_value); + Py_DECREF(recorded_value); + return transformed_value; +} + +static PyObject * +_PyOpcode_RecordTransform_NOS_GEN_FUNC(PyObject *recorded_value) +{ + PyObject *transformed_value = NULL; + _PyStackRef nos; + nos = PyStackRef_FromPyObjectBorrow(recorded_value); + PyObject *obj = PyStackRef_AsPyObjectBorrow(nos); + if (PyGen_Check(obj)) { + PyGenObject *gen = (PyGenObject *)obj; + _PyStackRef func = gen->gi_iframe.f_funcobj; + if (!PyStackRef_IsNull(func)) { + transformed_value = (PyObject *)PyStackRef_AsPyObjectBorrow(func); + Py_XINCREF(transformed_value); + } + } + Py_DECREF(recorded_value); + return transformed_value; +} + +static PyObject * +_PyOpcode_RecordTransform_BOUND_METHOD(PyObject *recorded_value) +{ + PyObject *transformed_value = NULL; + _PyStackRef callable; + callable = PyStackRef_FromPyObjectBorrow(recorded_value); + PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); + if (Py_TYPE(callable_o) == &PyMethod_Type) { + transformed_value = (PyObject *)callable_o; + Py_XINCREF(transformed_value); + } + Py_DECREF(recorded_value); + return transformed_value; +} + +PyObject * +_PyOpcode_RecordTransformValue(int uop, PyObject *value) +{ + switch (uop) { + case _RECORD_NOS_TYPE: + return _PyOpcode_RecordTransform_NOS_TYPE(value); + case _RECORD_TOS_TYPE: + return _PyOpcode_RecordTransform_TOS_TYPE(value); + case _RECORD_NOS_GEN_FUNC: + return _PyOpcode_RecordTransform_NOS_GEN_FUNC(value); + case _RECORD_BOUND_METHOD: + return _PyOpcode_RecordTransform_BOUND_METHOD(value); + default: + return value; + } +} diff --git a/Python/remote_debug.h b/Python/remote_debug.h index 5324a7aaa6f5e5..6fecc23502b46e 100644 --- a/Python/remote_debug.h +++ b/Python/remote_debug.h @@ -33,6 +33,9 @@ extern "C" { #ifdef __linux__ # include <elf.h> # include <sys/uio.h> +# include <sys/ptrace.h> +# include <sys/wait.h> +# include <dirent.h> # if INTPTR_MAX == INT64_MAX # define Elf_Ehdr Elf64_Ehdr # define Elf_Shdr Elf64_Shdr @@ -43,6 +46,17 @@ extern "C" { # define Elf_Phdr Elf32_Phdr # endif # include <sys/mman.h> + +// PTRACE options - define if not available +# ifndef PTRACE_SEIZE +# define PTRACE_SEIZE 0x4206 +# endif +# ifndef PTRACE_INTERRUPT +# define PTRACE_INTERRUPT 0x4207 +# endif +# ifndef PTRACE_EVENT_STOP +# define PTRACE_EVENT_STOP 128 +# endif #endif #if defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX @@ -50,9 +64,12 @@ extern "C" { # include <mach-o/fat.h> # include <mach-o/loader.h> # include <mach-o/nlist.h> +# include <mach/error.h> # include <mach/mach.h> # include <mach/mach_vm.h> # include <mach/machine.h> +# include <mach/task_info.h> +# include <mach/thread_act.h> # include <sys/mman.h> # include <sys/proc.h> # include <sys/sysctl.h> @@ -83,9 +100,16 @@ extern "C" { # define HAVE_PROCESS_VM_READV 0 #endif +static inline int +_Py_RemoteDebug_HasPermissionError(void) +{ + return PyErr_Occurred() + && PyErr_ExceptionMatches(PyExc_PermissionError); +} + #define _set_debug_exception_cause(exception, format, ...) \ do { \ - if (!PyErr_ExceptionMatches(PyExc_PermissionError)) { \ + if (!_Py_RemoteDebug_HasPermissionError()) { \ PyThreadState *tstate = _PyThreadState_GET(); \ if (!_PyErr_Occurred(tstate)) { \ _PyErr_Format(tstate, exception, format, ##__VA_ARGS__); \ @@ -95,6 +119,20 @@ extern "C" { } \ } while (0) +#define _set_debug_oserror_from_errno(err, format, ...) \ + do { \ + errno = (err); \ + PyErr_SetFromErrno(PyExc_OSError); \ + _set_debug_exception_cause(PyExc_OSError, format, ##__VA_ARGS__); \ + } while (0) + +#define _set_debug_oserror_from_errno_with_filename(err, filename, format, ...) \ + do { \ + errno = (err); \ + PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename); \ + _set_debug_exception_cause(PyExc_OSError, format, ##__VA_ARGS__); \ + } while (0) + static inline size_t get_page_size(void) { size_t page_size = 0; @@ -130,25 +168,57 @@ typedef struct { int memfd; #endif page_cache_entry_t pages[MAX_PAGES]; + int page_cache_count; Py_ssize_t page_size; } proc_handle_t; +// Forward declaration for use in validation function +static int +_Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address, size_t len, void* dst); + +// Optional callback to validate a candidate section address found during +// memory map searches. Returns 1 if the address is valid, 0 to skip it. +// This allows callers to filter out duplicate/stale mappings (e.g. from +// ctypes dlopen) whose sections were never initialized. +typedef int (*section_validator_t)(proc_handle_t *handle, uintptr_t address); + +// Validate that a candidate address starts with _Py_Debug_Cookie. +static int +_Py_RemoteDebug_ValidatePyRuntimeCookie(proc_handle_t *handle, uintptr_t address) +{ + if (address == 0) { + return 0; + } + char buf[sizeof(_Py_Debug_Cookie) - 1]; + if (_Py_RemoteDebug_ReadRemoteMemory(handle, address, sizeof(buf), buf) != 0) { + if (!_Py_RemoteDebug_HasPermissionError()) { + PyErr_Clear(); + } + return 0; + } + return memcmp(buf, _Py_Debug_Cookie, sizeof(buf)) == 0; +} + static void _Py_RemoteDebug_FreePageCache(proc_handle_t *handle) { for (int i = 0; i < MAX_PAGES; i++) { - PyMem_RawFree(handle->pages[i].data); + if (handle->pages[i].data) { + PyMem_RawFree(handle->pages[i].data); + } handle->pages[i].data = NULL; handle->pages[i].valid = 0; } + handle->page_cache_count = 0; } UNUSED static void _Py_RemoteDebug_ClearCache(proc_handle_t *handle) { - for (int i = 0; i < MAX_PAGES; i++) { + for (int i = 0; i < handle->page_cache_count; i++) { handle->pages[i].valid = 0; } + handle->page_cache_count = 0; } #if defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX @@ -156,8 +226,23 @@ static mach_port_t pid_to_task(pid_t pid); #endif // Initialize the process handle -static int +UNUSED static int _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, pid_t pid) { + handle->pid = 0; +#if defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX + handle->task = 0; +#elif defined(MS_WINDOWS) + handle->hProcess = NULL; +#elif defined(__linux__) + handle->memfd = -1; +#endif + handle->page_size = get_page_size(); + handle->page_cache_count = 0; + for (int i = 0; i < MAX_PAGES; i++) { + handle->pages[i].data = NULL; + handle->pages[i].valid = 0; + } + handle->pid = pid; #if defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX handle->task = pid_to_task(handle->pid); @@ -167,26 +252,20 @@ _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, pid_t pid) { } #elif defined(MS_WINDOWS) handle->hProcess = OpenProcess( - PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION, + PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION | PROCESS_SUSPEND_RESUME, FALSE, pid); if (handle->hProcess == NULL) { - PyErr_SetFromWindowsErr(0); + DWORD error = GetLastError(); + PyErr_SetFromWindowsErr(error); _set_debug_exception_cause(PyExc_RuntimeError, "Failed to initialize Windows process handle"); return -1; } -#elif defined(__linux__) - handle->memfd = -1; #endif - handle->page_size = get_page_size(); - for (int i = 0; i < MAX_PAGES; i++) { - handle->pages[i].data = NULL; - handle->pages[i].valid = 0; - } return 0; } // Clean up the process handle -static void +UNUSED static void _Py_RemoteDebug_CleanupProcHandle(proc_handle_t *handle) { #ifdef MS_WINDOWS if (handle->hProcess != NULL) { @@ -346,17 +425,19 @@ return_section_address_fat( size_t cpu_size = sizeof(cpu), abi64_size = sizeof(is_abi64); if (sysctlbyname("hw.cputype", &cpu, &cpu_size, NULL, 0) != 0) { - PyErr_Format(PyExc_OSError, + int err = errno; + _set_debug_oserror_from_errno(err, "Failed to determine CPU type via sysctlbyname " "for fat binary analysis at 0x%lx: %s", - base, strerror(errno)); + base, strerror(err)); return 0; } if (sysctlbyname("hw.cpu64bit_capable", &is_abi64, &abi64_size, NULL, 0) != 0) { - PyErr_Format(PyExc_OSError, + int err = errno; + _set_debug_oserror_from_errno(err, "Failed to determine CPU ABI capability via sysctlbyname " "for fat binary analysis at 0x%lx: %s", - base, strerror(errno)); + base, strerror(err)); return 0; } @@ -409,26 +490,29 @@ search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_ { int fd = open(path, O_RDONLY); if (fd == -1) { - PyErr_Format(PyExc_OSError, + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, path, "Cannot open binary file '%s' for section '%s' search: %s", - path, secname, strerror(errno)); + path, secname, strerror(err)); return 0; } struct stat fs; if (fstat(fd, &fs) == -1) { - PyErr_Format(PyExc_OSError, + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, path, "Cannot get file size for binary '%s' during section '%s' search: %s", - path, secname, strerror(errno)); + path, secname, strerror(err)); close(fd); return 0; } void* map = mmap(0, fs.st_size, PROT_READ, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { - PyErr_Format(PyExc_OSError, + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, path, "Cannot memory map binary file '%s' (size: %lld bytes) for section '%s' search: %s", - path, (long long)fs.st_size, secname, strerror(errno)); + path, (long long)fs.st_size, secname, strerror(err)); close(fd); return 0; } @@ -457,15 +541,21 @@ search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_ } if (munmap(map, fs.st_size) != 0) { - PyErr_Format(PyExc_OSError, - "Failed to unmap binary file '%s' (size: %lld bytes): %s", - path, (long long)fs.st_size, strerror(errno)); + if (!PyErr_Occurred()) { + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, path, + "Failed to unmap binary file '%s' (size: %lld bytes): %s", + path, (long long)fs.st_size, strerror(err)); + } result = 0; } if (close(fd) != 0) { - PyErr_Format(PyExc_OSError, - "Failed to close binary file '%s': %s", - path, strerror(errno)); + if (!PyErr_Occurred()) { + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, path, + "Failed to close binary file '%s': %s", + path, strerror(err)); + } result = 0; } return result; @@ -490,7 +580,8 @@ pid_to_task(pid_t pid) } static uintptr_t -search_map_for_section(proc_handle_t *handle, const char* secname, const char* substr) { +search_map_for_section(proc_handle_t *handle, const char* secname, const char* substr, + section_validator_t validator) { mach_vm_address_t address = 0; mach_vm_size_t size = 0; mach_msg_type_number_t count = sizeof(vm_region_basic_info_data_64_t); @@ -509,14 +600,15 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* s char map_filename[MAXPATHLEN + 1]; - while (mach_vm_region( - proc_ref, - &address, - &size, - VM_REGION_BASIC_INFO_64, - (vm_region_info_t)&region_info, - &count, - &object_name) == KERN_SUCCESS) + kern_return_t kr; + while ((kr = mach_vm_region( + proc_ref, + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&region_info, + &count, + &object_name)) == KERN_SUCCESS) { if ((region_info.protection & VM_PROT_READ) == 0 @@ -540,16 +632,32 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* s } if (strncmp(filename, substr, strlen(substr)) == 0) { + PyErr_Clear(); uintptr_t result = search_section_in_file( secname, map_filename, address, size, proc_ref); if (result != 0) { - return result; + if (validator == NULL || validator(handle, result)) { + return result; + } + if (_Py_RemoteDebug_HasPermissionError()) { + return 0; + } + } + else if (_Py_RemoteDebug_HasPermissionError()) { + return 0; } } address += size; } + if (kr != KERN_INVALID_ADDRESS && !PyErr_Occurred()) { + PyErr_Format(PyExc_RuntimeError, + "mach_vm_region failed while searching PID %d for section '%s' " + "(kern_return_t: %d)", + handle->pid, secname, kr); + } + return 0; } @@ -572,25 +680,29 @@ search_elf_file_for_section( int fd = open(elf_file, O_RDONLY); if (fd < 0) { - PyErr_Format(PyExc_OSError, + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, elf_file, "Cannot open ELF file '%s' for section '%s' search: %s", - elf_file, secname, strerror(errno)); + elf_file, secname, strerror(err)); goto exit; } struct stat file_stats; if (fstat(fd, &file_stats) != 0) { - PyErr_Format(PyExc_OSError, + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, elf_file, "Cannot get file size for ELF file '%s' during section '%s' search: %s", - elf_file, secname, strerror(errno)); + elf_file, secname, strerror(err)); goto exit; } file_memory = mmap(NULL, file_stats.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (file_memory == MAP_FAILED) { - PyErr_Format(PyExc_OSError, + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, elf_file, "Cannot memory map ELF file '%s' (size: %lld bytes) for section '%s' search: %s", - elf_file, (long long)file_stats.st_size, secname, strerror(errno)); + elf_file, (long long)file_stats.st_size, secname, strerror(err)); + file_memory = NULL; goto exit; } @@ -647,28 +759,41 @@ search_elf_file_for_section( exit: if (file_memory != NULL) { - munmap(file_memory, file_stats.st_size); + if (munmap(file_memory, file_stats.st_size) != 0) { + if (!PyErr_Occurred()) { + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, elf_file, + "Failed to unmap ELF file '%s' (size: %lld bytes): %s", + elf_file, (long long)file_stats.st_size, strerror(err)); + } + result = 0; + } } if (fd >= 0 && close(fd) != 0) { - PyErr_Format(PyExc_OSError, - "Failed to close ELF file '%s': %s", - elf_file, strerror(errno)); + if (!PyErr_Occurred()) { + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, elf_file, + "Failed to close ELF file '%s': %s", + elf_file, strerror(err)); + } result = 0; } return result; } static uintptr_t -search_linux_map_for_section(proc_handle_t *handle, const char* secname, const char* substr) +search_linux_map_for_section(proc_handle_t *handle, const char* secname, const char* substr, + section_validator_t validator) { char maps_file_path[64]; sprintf(maps_file_path, "/proc/%d/maps", handle->pid); FILE* maps_file = fopen(maps_file_path, "r"); if (maps_file == NULL) { - PyErr_Format(PyExc_OSError, + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, maps_file_path, "Cannot open process memory map file '%s' for PID %d section search: %s", - maps_file_path, handle->pid, strerror(errno)); + maps_file_path, handle->pid, strerror(err)); return 0; } @@ -720,6 +845,11 @@ search_linux_map_for_section(proc_handle_t *handle, const char* secname, const c } const char *path = line + path_pos; + if (path[0] == '[' && path[strlen(path)-1] == ']') { + // Skip [heap], [stack], [anon:cpython:pymalloc], etc. + continue; + } + const char *filename = strrchr(path, '/'); if (filename) { filename++; // Move past the '/' @@ -728,18 +858,39 @@ search_linux_map_for_section(proc_handle_t *handle, const char* secname, const c } if (strstr(filename, substr)) { + PyErr_Clear(); retval = search_elf_file_for_section(handle, secname, start, path); if (retval) { + if (validator == NULL || validator(handle, retval)) { + break; + } + if (_Py_RemoteDebug_HasPermissionError()) { + retval = 0; + break; + } + } + else if (_Py_RemoteDebug_HasPermissionError()) { break; } + retval = 0; } } + if (retval == 0 && !PyErr_Occurred() && ferror(maps_file)) { + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, maps_file_path, + "Failed to read process map file '%s' for PID %d section search: %s", + maps_file_path, handle->pid, strerror(err)); + } + PyMem_Free(line); if (fclose(maps_file) != 0) { - PyErr_Format(PyExc_OSError, - "Failed to close process map file '%s': %s", - maps_file_path, strerror(errno)); + if (!PyErr_Occurred()) { + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, maps_file_path, + "Failed to close process map file '%s': %s", + maps_file_path, strerror(err)); + } retval = 0; } @@ -762,9 +913,9 @@ static int is_process_alive(HANDLE hProcess) { static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char* secname) { HANDLE hFile = CreateFileW(mod_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { - PyErr_SetFromWindowsErr(0); DWORD error = GetLastError(); - PyErr_Format(PyExc_OSError, + PyErr_SetFromWindowsErr(error); + _set_debug_exception_cause(PyExc_OSError, "Cannot open PE file for section '%s' analysis (error %lu)", secname, error); return NULL; @@ -772,9 +923,9 @@ static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char* HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, 0); if (!hMap) { - PyErr_SetFromWindowsErr(0); DWORD error = GetLastError(); - PyErr_Format(PyExc_OSError, + PyErr_SetFromWindowsErr(error); + _set_debug_exception_cause(PyExc_OSError, "Cannot create file mapping for PE file section '%s' analysis (error %lu)", secname, error); CloseHandle(hFile); @@ -783,9 +934,9 @@ static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char* BYTE* mapView = (BYTE*)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); if (!mapView) { - PyErr_SetFromWindowsErr(0); DWORD error = GetLastError(); - PyErr_Format(PyExc_OSError, + PyErr_SetFromWindowsErr(error); + _set_debug_exception_cause(PyExc_OSError, "Cannot map view of PE file for section '%s' analysis (error %lu)", secname, error); CloseHandle(hMap); @@ -835,16 +986,17 @@ static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char* static uintptr_t -search_windows_map_for_section(proc_handle_t* handle, const char* secname, const wchar_t* substr) { +search_windows_map_for_section(proc_handle_t* handle, const char* secname, const wchar_t* substr, + section_validator_t validator) { HANDLE hProcSnap; do { hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, handle->pid); } while (hProcSnap == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH); if (hProcSnap == INVALID_HANDLE_VALUE) { - PyErr_SetFromWindowsErr(0); DWORD error = GetLastError(); - PyErr_Format(PyExc_PermissionError, + PyErr_SetFromWindowsErr(error); + _set_debug_exception_cause(PyExc_OSError, "Unable to create module snapshot for PID %d section '%s' " "search (error %lu). Check permissions or PID validity", handle->pid, secname, error); @@ -855,14 +1007,46 @@ search_windows_map_for_section(proc_handle_t* handle, const char* secname, const moduleEntry.dwSize = sizeof(moduleEntry); void* runtime_addr = NULL; - for (BOOL hasModule = Module32FirstW(hProcSnap, &moduleEntry); hasModule; hasModule = Module32NextW(hProcSnap, &moduleEntry)) { + if (!Module32FirstW(hProcSnap, &moduleEntry)) { + DWORD error = GetLastError(); + PyErr_SetFromWindowsErr(error); + _set_debug_exception_cause(PyExc_OSError, + "Unable to enumerate modules for PID %d section '%s' " + "search (error %lu)", + handle->pid, secname, error); + CloseHandle(hProcSnap); + return 0; + } + + do { // Look for either python executable or DLL if (wcsstr(moduleEntry.szModule, substr)) { - runtime_addr = analyze_pe(moduleEntry.szExePath, moduleEntry.modBaseAddr, secname); - if (runtime_addr != NULL) { + PyErr_Clear(); + void *candidate = analyze_pe(moduleEntry.szExePath, moduleEntry.modBaseAddr, secname); + if (candidate != NULL) { + if (validator == NULL || validator(handle, (uintptr_t)candidate)) { + runtime_addr = candidate; + break; + } + if (_Py_RemoteDebug_HasPermissionError()) { + break; + } + } + else if (_Py_RemoteDebug_HasPermissionError()) { break; } } + } while (Module32NextW(hProcSnap, &moduleEntry)); + + if (runtime_addr == NULL && !PyErr_Occurred()) { + DWORD error = GetLastError(); + if (error != ERROR_NO_MORE_FILES) { + PyErr_SetFromWindowsErr(error); + _set_debug_exception_cause(PyExc_OSError, + "Module enumeration failed for PID %d section '%s' " + "search (error %lu)", + handle->pid, secname, error); + } } CloseHandle(hProcSnap); @@ -873,50 +1057,59 @@ search_windows_map_for_section(proc_handle_t* handle, const char* secname, const #endif // MS_WINDOWS // Get the PyRuntime section address for any platform -static uintptr_t +UNUSED static uintptr_t _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle) { uintptr_t address; #ifdef MS_WINDOWS // On Windows, search for 'python' in executable or DLL - address = search_windows_map_for_section(handle, "PyRuntime", L"python"); + address = search_windows_map_for_section(handle, "PyRuntime", L"python", + _Py_RemoteDebug_ValidatePyRuntimeCookie); if (address == 0) { - // Error out: 'python' substring covers both executable and DLL - PyObject *exc = PyErr_GetRaisedException(); - PyErr_Format(PyExc_RuntimeError, - "Failed to find the PyRuntime section in process %d on Windows platform", - handle->pid); - _PyErr_ChainExceptions1(exc); + if (!_Py_RemoteDebug_HasPermissionError()) { + // Error out: 'python' substring covers both executable and DLL + PyObject *exc = PyErr_GetRaisedException(); + PyErr_Format(PyExc_RuntimeError, + "Failed to find the PyRuntime section in process %d on Windows platform", + handle->pid); + _PyErr_ChainExceptions1(exc); + } } -#elif defined(__linux__) +#elif defined(__linux__) && HAVE_PROCESS_VM_READV // On Linux, search for 'python' in executable or DLL - address = search_linux_map_for_section(handle, "PyRuntime", "python"); + address = search_linux_map_for_section(handle, "PyRuntime", "python", + _Py_RemoteDebug_ValidatePyRuntimeCookie); if (address == 0) { - // Error out: 'python' substring covers both executable and DLL - PyObject *exc = PyErr_GetRaisedException(); - PyErr_Format(PyExc_RuntimeError, - "Failed to find the PyRuntime section in process %d on Linux platform", - handle->pid); - _PyErr_ChainExceptions1(exc); + if (!_Py_RemoteDebug_HasPermissionError()) { + // Error out: 'python' substring covers both executable and DLL + PyObject *exc = PyErr_GetRaisedException(); + PyErr_Format(PyExc_RuntimeError, + "Failed to find the PyRuntime section in process %d on Linux platform", + handle->pid); + _PyErr_ChainExceptions1(exc); + } } #elif defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX // On macOS, try libpython first, then fall back to python const char* candidates[] = {"libpython", "python", "Python", NULL}; for (const char** candidate = candidates; *candidate; candidate++) { PyErr_Clear(); - address = search_map_for_section(handle, "PyRuntime", *candidate); - if (address != 0) { + address = search_map_for_section(handle, "PyRuntime", *candidate, + _Py_RemoteDebug_ValidatePyRuntimeCookie); + if (address != 0 || _Py_RemoteDebug_HasPermissionError()) { break; } } if (address == 0) { - PyObject *exc = PyErr_GetRaisedException(); - PyErr_Format(PyExc_RuntimeError, - "Failed to find the PyRuntime section in process %d " - "on macOS platform (tried both libpython and python)", - handle->pid); - _PyErr_ChainExceptions1(exc); + if (!_Py_RemoteDebug_HasPermissionError()) { + PyObject *exc = PyErr_GetRaisedException(); + PyErr_Format(PyExc_RuntimeError, + "Failed to find the PyRuntime section in process %d " + "on macOS platform (tried both libpython and python)", + handle->pid); + _PyErr_ChainExceptions1(exc); + } } #else _set_debug_exception_cause(PyExc_RuntimeError, @@ -937,9 +1130,9 @@ open_proc_mem_fd(proc_handle_t *handle) handle->memfd = open(mem_file_path, O_RDWR); if (handle->memfd == -1) { - PyErr_SetFromErrno(PyExc_OSError); - _set_debug_exception_cause(PyExc_OSError, - "failed to open file %s: %s", mem_file_path, strerror(errno)); + int err = errno; + _set_debug_oserror_from_errno_with_filename(err, mem_file_path, + "failed to open file %s: %s", mem_file_path, strerror(err)); return -1; } return 0; @@ -950,6 +1143,9 @@ open_proc_mem_fd(proc_handle_t *handle) static int read_remote_memory_fallback(proc_handle_t *handle, uintptr_t remote_address, size_t len, void* dst) { + if (len == 0) { + return 0; + } if (handle->memfd == -1) { if (open_proc_mem_fd(handle) < 0) { return -1; @@ -967,14 +1163,23 @@ read_remote_memory_fallback(proc_handle_t *handle, uintptr_t remote_address, siz read_bytes = preadv(handle->memfd, local, 1, offset); if (read_bytes < 0) { + int err = errno; + errno = err; PyErr_SetFromErrno(PyExc_OSError); _set_debug_exception_cause(PyExc_OSError, "preadv failed for PID %d at address 0x%lx " "(size %zu, partial read %zd bytes): %s", - handle->pid, remote_address + result, len - result, result, strerror(errno)); + handle->pid, remote_address + result, len - result, result, strerror(err)); return -1; } + if (read_bytes == 0) { + PyErr_Format(PyExc_OSError, + "preadv returned 0 bytes for PID %d at address 0x%lx " + "(size %zu, partial read %zd bytes)", + handle->pid, remote_address + result, len - result, result); + return -1; + } result += read_bytes; } while ((size_t)read_bytes != local[0].iov_len); return 0; @@ -986,11 +1191,15 @@ read_remote_memory_fallback(proc_handle_t *handle, uintptr_t remote_address, siz static int _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address, size_t len, void* dst) { + if (len == 0) { + return 0; + } #ifdef MS_WINDOWS SIZE_T read_bytes = 0; SIZE_T result = 0; do { if (!ReadProcessMemory(handle->hProcess, (LPCVOID)(remote_address + result), (char*)dst + result, len - result, &read_bytes)) { + DWORD error = GetLastError(); // Check if the process is still alive: we need to be able to tell our caller // that the process is dead and not just that the read failed. if (!is_process_alive(handle->hProcess)) { @@ -998,14 +1207,20 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address PyErr_SetFromErrno(PyExc_OSError); return -1; } - PyErr_SetFromWindowsErr(0); - DWORD error = GetLastError(); + PyErr_SetFromWindowsErr(error); _set_debug_exception_cause(PyExc_OSError, "ReadProcessMemory failed for PID %d at address 0x%lx " "(size %zu, partial read %zu bytes): Windows error %lu", handle->pid, remote_address + result, len - result, result, error); return -1; } + if (read_bytes == 0) { + PyErr_Format(PyExc_OSError, + "ReadProcessMemory returned 0 bytes for PID %d at address 0x%lx " + "(size %zu, partial read %zu bytes)", + handle->pid, remote_address + result, len - result, result); + return -1; + } result += read_bytes; } while (result < len); return 0; @@ -1026,45 +1241,74 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address read_bytes = process_vm_readv(handle->pid, local, 1, remote, 1, 0); if (read_bytes < 0) { - if (errno == ENOSYS) { + int err = errno; + if (err == ENOSYS) { return read_remote_memory_fallback(handle, remote_address, len, dst); } + errno = err; PyErr_SetFromErrno(PyExc_OSError); - if (errno == ESRCH) { + if (err == ESRCH) { return -1; } _set_debug_exception_cause(PyExc_OSError, "process_vm_readv failed for PID %d at address 0x%lx " "(size %zu, partial read %zd bytes): %s", - handle->pid, remote_address + result, len - result, result, strerror(errno)); + handle->pid, remote_address + result, len - result, result, strerror(err)); return -1; } + if (read_bytes == 0) { + PyErr_Format(PyExc_OSError, + "process_vm_readv returned 0 bytes for PID %d at address 0x%lx " + "(size %zu, partial read %zd bytes)", + handle->pid, remote_address + result, len - result, result); + return -1; + } result += read_bytes; } while ((size_t)read_bytes != local[0].iov_len); return 0; #elif defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX - Py_ssize_t result = -1; + mach_vm_size_t bytes_read = 0; kern_return_t kr = mach_vm_read_overwrite( handle->task, (mach_vm_address_t)remote_address, len, (mach_vm_address_t)dst, - (mach_vm_size_t*)&result); + &bytes_read); if (kr != KERN_SUCCESS) { - switch (kr) { + switch (err_get_code(kr)) { case KERN_PROTECTION_FAILURE: PyErr_Format(PyExc_PermissionError, "Memory protection failure reading from PID %d at address " "0x%lx (size %zu): insufficient permissions", handle->pid, remote_address, len); break; - case KERN_INVALID_ARGUMENT: - PyErr_Format(PyExc_ValueError, - "Invalid argument to mach_vm_read_overwrite for PID %d at " - "address 0x%lx (size %zu)", - handle->pid, remote_address, len); + case KERN_INVALID_ARGUMENT: { + // Perform a task_info check to see if the invalid argument is due + // to the process being terminated + task_basic_info_data_t task_basic_info; + mach_msg_type_number_t task_info_count = TASK_BASIC_INFO_COUNT; + kern_return_t task_valid_check = task_info(handle->task, TASK_BASIC_INFO, + (task_info_t)&task_basic_info, + &task_info_count); + if (task_valid_check == KERN_INVALID_ARGUMENT) { + PyErr_Format(PyExc_ProcessLookupError, + "Process %d is no longer accessible (process terminated)", + handle->pid); + } else { + PyErr_Format(PyExc_ValueError, + "Invalid argument to mach_vm_read_overwrite for PID %d at " + "address 0x%lx (size %zu) - check memory permissions", + handle->pid, remote_address, len); + } + break; + } + case KERN_NO_SPACE: + case KERN_MEMORY_ERROR: + PyErr_Format(PyExc_ProcessLookupError, + "Process %d memory space no longer available (process terminated)", + handle->pid); break; default: PyErr_Format(PyExc_RuntimeError, @@ -1074,6 +1318,153 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address } return -1; } + if (bytes_read != (mach_vm_size_t)len) { + PyErr_Format(PyExc_OSError, + "mach_vm_read_overwrite read %llu of %zu bytes for PID %d at " + "address 0x%lx", + (unsigned long long)bytes_read, len, handle->pid, remote_address); + return -1; + } + return 0; +#else + Py_UNREACHABLE(); +#endif +} + +#if defined(__linux__) && HAVE_PROCESS_VM_READV +// Fallback write using /proc/pid/mem +static int +_Py_RemoteDebug_WriteRemoteMemoryFallback(proc_handle_t *handle, uintptr_t remote_address, size_t len, const void* src) +{ + if (len == 0) { + return 0; + } + if (handle->memfd == -1) { + if (open_proc_mem_fd(handle) < 0) { + return -1; + } + } + + struct iovec local[1]; + Py_ssize_t result = 0; + Py_ssize_t written = 0; + + do { + local[0].iov_base = (char*)src + result; + local[0].iov_len = len - result; + off_t offset = remote_address + result; + + written = pwritev(handle->memfd, local, 1, offset); + if (written < 0) { + int err = errno; + errno = err; + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + if (written == 0) { + PyErr_Format(PyExc_OSError, + "pwritev wrote 0 bytes for PID %d at address 0x%lx " + "(size %zu, partial write %zd bytes)", + handle->pid, remote_address + result, len - result, result); + return -1; + } + result += written; + } while ((size_t)written != local[0].iov_len); + return 0; +} +#endif // __linux__ + +// Platform-independent memory write function +UNUSED static int +_Py_RemoteDebug_WriteRemoteMemory(proc_handle_t *handle, uintptr_t remote_address, size_t len, const void* src) +{ + if (len == 0) { + return 0; + } +#ifdef MS_WINDOWS + SIZE_T written = 0; + SIZE_T result = 0; + do { + if (!WriteProcessMemory(handle->hProcess, (LPVOID)(remote_address + result), (const char*)src + result, len - result, &written)) { + DWORD error = GetLastError(); + PyErr_SetFromWindowsErr(error); + _set_debug_exception_cause(PyExc_OSError, + "WriteProcessMemory failed for PID %d at address 0x%lx " + "(size %zu, partial write %zu bytes): Windows error %lu", + handle->pid, remote_address + result, len - result, result, error); + return -1; + } + if (written == 0) { + PyErr_Format(PyExc_OSError, + "WriteProcessMemory wrote 0 bytes for PID %d at address 0x%lx " + "(size %zu, partial write %zu bytes)", + handle->pid, remote_address + result, len - result, result); + return -1; + } + result += written; + } while (result < len); + return 0; +#elif defined(__linux__) && HAVE_PROCESS_VM_READV + if (handle->memfd != -1) { + return _Py_RemoteDebug_WriteRemoteMemoryFallback(handle, remote_address, len, src); + } + struct iovec local[1]; + struct iovec remote[1]; + Py_ssize_t result = 0; + Py_ssize_t written = 0; + + do { + local[0].iov_base = (void*)((char*)src + result); + local[0].iov_len = len - result; + remote[0].iov_base = (void*)((char*)remote_address + result); + remote[0].iov_len = len - result; + + written = process_vm_writev(handle->pid, local, 1, remote, 1, 0); + if (written < 0) { + int err = errno; + if (err == ENOSYS) { + return _Py_RemoteDebug_WriteRemoteMemoryFallback(handle, remote_address, len, src); + } + errno = err; + PyErr_SetFromErrno(PyExc_OSError); + _set_debug_exception_cause(PyExc_OSError, + "process_vm_writev failed for PID %d at address 0x%lx " + "(size %zu, partial write %zd bytes): %s", + handle->pid, remote_address + result, len - result, result, strerror(err)); + return -1; + } + + if (written == 0) { + PyErr_Format(PyExc_OSError, + "process_vm_writev wrote 0 bytes for PID %d at address 0x%lx " + "(size %zu, partial write %zd bytes)", + handle->pid, remote_address + result, len - result, result); + return -1; + } + result += written; + } while ((size_t)written != local[0].iov_len); + return 0; +#elif defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX + kern_return_t kr = mach_vm_write( + handle->task, + (mach_vm_address_t)remote_address, + (vm_offset_t)src, + (mach_msg_type_number_t)len); + + if (kr != KERN_SUCCESS) { + switch (kr) { + case KERN_PROTECTION_FAILURE: + PyErr_SetString(PyExc_PermissionError, "Not enough permissions to write memory"); + break; + case KERN_INVALID_ARGUMENT: + PyErr_SetString(PyExc_PermissionError, "Invalid argument to mach_vm_write"); + break; + default: + PyErr_Format(PyExc_RuntimeError, "Unknown error writing memory: %d", (int)kr); + } + return -1; + } return 0; #else Py_UNREACHABLE(); @@ -1094,8 +1485,9 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle, return _Py_RemoteDebug_ReadRemoteMemory(handle, addr, size, out); } - // Search for valid cached page - for (int i = 0; i < MAX_PAGES; i++) { + // Search only the pages used since the last clear. The cache is cleared + // between profiler samples, so entries are packed at the front. + for (int i = 0; i < handle->page_cache_count; i++) { page_cache_entry_t *entry = &handle->pages[i]; if (entry->valid && entry->page_addr == page_base) { memcpy(out, entry->data + offset_in_page, size); @@ -1103,32 +1495,31 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle, } } - // Find reusable slot - for (int i = 0; i < MAX_PAGES; i++) { - page_cache_entry_t *entry = &handle->pages[i]; - if (!entry->valid) { + if (handle->page_cache_count < MAX_PAGES) { + page_cache_entry_t *entry = &handle->pages[handle->page_cache_count]; + if (entry->data == NULL) { + entry->data = PyMem_RawMalloc(page_size); if (entry->data == NULL) { - entry->data = PyMem_RawMalloc(page_size); - if (entry->data == NULL) { - _set_debug_exception_cause(PyExc_MemoryError, - "Cannot allocate %zu bytes for page cache entry " - "during read from PID %d at address 0x%lx", - page_size, handle->pid, addr); - return -1; - } - } - - if (_Py_RemoteDebug_ReadRemoteMemory(handle, page_base, page_size, entry->data) < 0) { - // Try to just copy the exact ammount as a fallback - PyErr_Clear(); - goto fallback; + PyErr_NoMemory(); + _set_debug_exception_cause(PyExc_MemoryError, + "Cannot allocate %zu bytes for page cache entry " + "during read from PID %d at address 0x%lx", + page_size, handle->pid, addr); + return -1; } + } - entry->page_addr = page_base; - entry->valid = 1; - memcpy(out, entry->data + offset_in_page, size); - return 0; + if (_Py_RemoteDebug_ReadRemoteMemory(handle, page_base, page_size, entry->data) < 0) { + // Try to just copy the exact amount as a fallback + PyErr_Clear(); + goto fallback; } + + entry->page_addr = page_base; + entry->valid = 1; + handle->page_cache_count++; + memcpy(out, entry->data + offset_in_page, size); + return 0; } fallback: @@ -1136,7 +1527,50 @@ _Py_RemoteDebug_PagedReadRemoteMemory(proc_handle_t *handle, return _Py_RemoteDebug_ReadRemoteMemory(handle, addr, size, out); } -static int +typedef struct { + uintptr_t remote_addr; + void *local_buf; + size_t size; +} _Py_RemoteReadSegment; + +#define _PY_REMOTE_DEBUG_MAX_BATCHED_SEGMENTS 4 + +// Batched read of multiple remote regions in a single syscall when supported. +// Returns total bytes read (>= 0) on success, -1 if batched reads are +// unavailable or the syscall failed. Callers compare the return value against +// cumulative segment sizes to determine which segments were fully populated. +UNUSED static Py_ssize_t +_Py_RemoteDebug_BatchedReadRemoteMemory( + proc_handle_t *handle, + const _Py_RemoteReadSegment *segments, + int nsegs) +{ +#if defined(__linux__) && HAVE_PROCESS_VM_READV + if (handle->memfd == -1 + && nsegs > 0 + && nsegs <= _PY_REMOTE_DEBUG_MAX_BATCHED_SEGMENTS) { + struct iovec local[_PY_REMOTE_DEBUG_MAX_BATCHED_SEGMENTS]; + struct iovec remote[_PY_REMOTE_DEBUG_MAX_BATCHED_SEGMENTS]; + for (int i = 0; i < nsegs; i++) { + local[i].iov_base = segments[i].local_buf; + local[i].iov_len = segments[i].size; + remote[i].iov_base = (void *)segments[i].remote_addr; + remote[i].iov_len = segments[i].size; + } + ssize_t nread = process_vm_readv(handle->pid, local, nsegs, remote, nsegs, 0); + if (nread >= 0) { + return (Py_ssize_t)nread; + } + } +#else + (void)handle; + (void)segments; + (void)nsegs; +#endif + return -1; +} + +UNUSED static int _Py_RemoteDebug_ReadDebugOffsets( proc_handle_t *handle, uintptr_t *runtime_start_address, diff --git a/Python/remote_debugging.c b/Python/remote_debugging.c index 7aee87ef05a407..5b50b95db94a6d 100644 --- a/Python/remote_debugging.c +++ b/Python/remote_debugging.c @@ -19,109 +19,16 @@ cleanup_proc_handle(proc_handle_t *handle) { } static int -read_memory(proc_handle_t *handle, uint64_t remote_address, size_t len, void* dst) +read_memory(proc_handle_t *handle, uintptr_t remote_address, size_t len, void* dst) { return _Py_RemoteDebug_ReadRemoteMemory(handle, remote_address, len, dst); } -// Why is pwritev not guarded? Except on Android API level 23 (no longer -// supported), HAVE_PROCESS_VM_READV is sufficient. -#if defined(__linux__) && HAVE_PROCESS_VM_READV -static int -write_memory_fallback(proc_handle_t *handle, uintptr_t remote_address, size_t len, const void* src) -{ - if (handle->memfd == -1) { - if (open_proc_mem_fd(handle) < 0) { - return -1; - } - } - - struct iovec local[1]; - Py_ssize_t result = 0; - Py_ssize_t written = 0; - - do { - local[0].iov_base = (char*)src + result; - local[0].iov_len = len - result; - off_t offset = remote_address + result; - - written = pwritev(handle->memfd, local, 1, offset); - if (written < 0) { - PyErr_SetFromErrno(PyExc_OSError); - return -1; - } - - result += written; - } while ((size_t)written != local[0].iov_len); - return 0; -} -#endif // __linux__ - +// Use the shared write function from remote_debug.h static int write_memory(proc_handle_t *handle, uintptr_t remote_address, size_t len, const void* src) { -#ifdef MS_WINDOWS - SIZE_T written = 0; - SIZE_T result = 0; - do { - if (!WriteProcessMemory(handle->hProcess, (LPVOID)(remote_address + result), (const char*)src + result, len - result, &written)) { - PyErr_SetFromWindowsErr(0); - return -1; - } - result += written; - } while (result < len); - return 0; -#elif defined(__linux__) && HAVE_PROCESS_VM_READV - if (handle->memfd != -1) { - return write_memory_fallback(handle, remote_address, len, src); - } - struct iovec local[1]; - struct iovec remote[1]; - Py_ssize_t result = 0; - Py_ssize_t written = 0; - - do { - local[0].iov_base = (void*)((char*)src + result); - local[0].iov_len = len - result; - remote[0].iov_base = (void*)((char*)remote_address + result); - remote[0].iov_len = len - result; - - written = process_vm_writev(handle->pid, local, 1, remote, 1, 0); - if (written < 0) { - if (errno == ENOSYS) { - return write_memory_fallback(handle, remote_address, len, src); - } - PyErr_SetFromErrno(PyExc_OSError); - return -1; - } - - result += written; - } while ((size_t)written != local[0].iov_len); - return 0; -#elif defined(__APPLE__) && TARGET_OS_OSX - kern_return_t kr = mach_vm_write( - pid_to_task(handle->pid), - (mach_vm_address_t)remote_address, - (vm_offset_t)src, - (mach_msg_type_number_t)len); - - if (kr != KERN_SUCCESS) { - switch (kr) { - case KERN_PROTECTION_FAILURE: - PyErr_SetString(PyExc_PermissionError, "Not enough permissions to write memory"); - break; - case KERN_INVALID_ARGUMENT: - PyErr_SetString(PyExc_PermissionError, "Invalid argument to mach_vm_write"); - break; - default: - PyErr_Format(PyExc_RuntimeError, "Unknown error writing memory: %d", (int)kr); - } - return -1; - } - return 0; -#else - Py_UNREACHABLE(); -#endif + return _Py_RemoteDebug_WriteRemoteMemory(handle, remote_address, len, src); } static int @@ -235,7 +142,7 @@ send_exec_to_proc_handle(proc_handle_t *handle, int tid, const char *debugger_sc int is_remote_debugging_enabled = 0; if (0 != read_memory( handle, - interpreter_state_addr + debug_offsets.debugger_support.remote_debugging_enabled, + interpreter_state_addr + (uintptr_t)debug_offsets.debugger_support.remote_debugging_enabled, sizeof(int), &is_remote_debugging_enabled)) { @@ -255,7 +162,7 @@ send_exec_to_proc_handle(proc_handle_t *handle, int tid, const char *debugger_sc if (tid != 0) { if (0 != read_memory( handle, - interpreter_state_addr + debug_offsets.interpreter_state.threads_head, + interpreter_state_addr + (uintptr_t)debug_offsets.interpreter_state.threads_head, sizeof(void*), &thread_state_addr)) { @@ -264,7 +171,7 @@ send_exec_to_proc_handle(proc_handle_t *handle, int tid, const char *debugger_sc while (thread_state_addr != 0) { if (0 != read_memory( handle, - thread_state_addr + debug_offsets.thread_state.native_thread_id, + thread_state_addr + (uintptr_t)debug_offsets.thread_state.native_thread_id, sizeof(this_tid), &this_tid)) { @@ -277,7 +184,7 @@ send_exec_to_proc_handle(proc_handle_t *handle, int tid, const char *debugger_sc if (0 != read_memory( handle, - thread_state_addr + debug_offsets.thread_state.next, + thread_state_addr + (uintptr_t)debug_offsets.thread_state.next, sizeof(void*), &thread_state_addr)) { @@ -294,7 +201,7 @@ send_exec_to_proc_handle(proc_handle_t *handle, int tid, const char *debugger_sc } else { if (0 != read_memory( handle, - interpreter_state_addr + debug_offsets.interpreter_state.threads_main, + interpreter_state_addr + (uintptr_t)debug_offsets.interpreter_state.threads_main, sizeof(void*), &thread_state_addr)) { @@ -346,7 +253,7 @@ send_exec_to_proc_handle(proc_handle_t *handle, int tid, const char *debugger_sc uintptr_t eval_breaker; if (0 != read_memory( handle, - thread_state_addr + debug_offsets.debugger_support.eval_breaker, + thread_state_addr + (uintptr_t)debug_offsets.debugger_support.eval_breaker, sizeof(uintptr_t), &eval_breaker)) { diff --git a/Python/slots.c b/Python/slots.c new file mode 100644 index 00000000000000..6bc74c727230e0 --- /dev/null +++ b/Python/slots.c @@ -0,0 +1,404 @@ +/* Common handling of type/module slots + */ + +#include "Python.h" + +#include "pycore_slots.h" + +#include <stdio.h> + +// Iterating through a recursive structure doesn't look great in a debugger. +// Flip the #if to 1 to get a trace on stderr. +// (The messages can also serve as code comments.) +#if 0 +#define MSG(...) { \ + fprintf(stderr, "slotiter: " __VA_ARGS__); fprintf(stderr, "\n");} +#else +#define MSG(...) +#endif + +static char* +kind_name(_PySlot_KIND kind) +{ + switch (kind) { + case _PySlot_KIND_TYPE: return "type"; + case _PySlot_KIND_MOD: return "module"; + case _PySlot_KIND_COMPAT: return "compat"; + case _PySlot_KIND_SLOT: return "generic slot"; + } + Py_UNREACHABLE(); +} + +static void +init_with_kind(_PySlotIterator *it, const void *slots, + _PySlot_KIND result_kind, + _PySlot_KIND slot_struct_kind) +{ + MSG(""); + MSG("init (%s slot iterator)", kind_name(result_kind)); + it->state = it->states; + it->state->any_slot = slots; + it->state->slot_struct_kind = slot_struct_kind; + it->kind = result_kind; + it->name = NULL; + it->recursion_level = 0; + it->is_at_end = false; + it->is_first_run = true; + it->current.sl_id = 0; + memset(it->seen, 0, sizeof(it->seen)); +} + +void +_PySlotIterator_Init(_PySlotIterator *it, const PySlot *slots, + _PySlot_KIND result_kind) +{ + init_with_kind(it, slots, result_kind, _PySlot_KIND_SLOT); +} + +void +_PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots, + _PySlot_KIND kind) +{ + init_with_kind(it, slots, kind, kind); +} + +void +_PySlotIterator_Rewind(_PySlotIterator *it, const void *slots) +{ + MSG(""); + MSG("rewind (%s slot iterator)", kind_name(it->kind)); + assert (it->is_at_end); + assert (it->recursion_level == 0); + assert (it->state == it->states); + it->is_at_end = false; + it->state->any_slot = slots; + it->is_first_run = false; +} + +static Py_ssize_t +seen_index(uint16_t id) +{ + return id / _PySlot_SEEN_ENTRY_BITS; +} + +static unsigned int +seen_mask(uint16_t id) +{ + return ((unsigned int)1) << (id % _PySlot_SEEN_ENTRY_BITS); +} + +bool +_PySlotIterator_SawSlot(_PySlotIterator *it, int id) +{ + assert (id > 0); + assert (id < _Py_slot_COUNT); + return it->seen[seen_index(id)] & seen_mask(id); +} + +// Advance `it` to the next entry. Currently cannot fail. +static void +advance(_PySlotIterator *it) +{ + MSG("advance (at level %d)", (int)it->recursion_level); + switch (it->state->slot_struct_kind) { + case _PySlot_KIND_SLOT: it->state->slot++; break; + case _PySlot_KIND_TYPE: it->state->tp_slot++; break; + case _PySlot_KIND_MOD: it->state->mod_slot++; break; + default: + Py_UNREACHABLE(); + } +} + +static int handle_first_run(_PySlotIterator *it); + +bool +_PySlotIterator_Next(_PySlotIterator *it) +{ + MSG("next"); + assert(it); + assert(!it->is_at_end); + assert(!PyErr_Occurred()); + + it->current.sl_id = -1; + + while (true) { + if (it->state->slot == NULL) { + if (it->recursion_level == 0) { + MSG("end (initial nesting level done)"); + it->is_at_end = true; + return 0; + } + MSG("pop nesting level %d", (int)it->recursion_level); + it->recursion_level--; + it->state = &it->states[it->recursion_level]; + advance(it); + continue; + } + + switch (it->state->slot_struct_kind) { + case _PySlot_KIND_SLOT: { + MSG("copying PySlot structure"); + it->current = *it->state->slot; + } break; + case _PySlot_KIND_TYPE: { + MSG("converting PyType_Slot structure"); + memset(&it->current, 0, sizeof(it->current)); + it->current.sl_id = (uint16_t)it->state->tp_slot->slot; + it->current.sl_flags = PySlot_INTPTR; + it->current.sl_ptr = (void*)it->state->tp_slot->pfunc; + } break; + case _PySlot_KIND_MOD: { + MSG("converting PyModuleDef_Slot structure"); + memset(&it->current, 0, sizeof(it->current)); + it->current.sl_id = (uint16_t)it->state->mod_slot->slot; + it->current.sl_flags = PySlot_INTPTR; + it->current.sl_ptr = (void*)it->state->mod_slot->value; + } break; + default: { + Py_UNREACHABLE(); + } break; + } + + /* shorter local names */ + PySlot *const result = &it->current; + uint16_t flags = result->sl_flags; + + MSG("slot %d, flags 0x%x, from %p", + (int)result->sl_id, (unsigned)flags, it->state->slot); + + uint16_t orig_id = result->sl_id; + switch (it->kind) { + case _PySlot_KIND_TYPE: + result->sl_id = _PySlot_resolve_type_slot(result->sl_id); + break; + case _PySlot_KIND_MOD: + result->sl_id = _PySlot_resolve_mod_slot(result->sl_id); + break; + default: + Py_UNREACHABLE(); + } + MSG("resolved to slot %d (%s)", + (int)result->sl_id, _PySlot_GetName(result->sl_id)); + + if (result->sl_id == Py_slot_invalid) { + MSG("error (unknown/invalid slot)"); + if (flags & PySlot_OPTIONAL) { + advance(it); + continue; + } + _PySlot_err_bad_slot(kind_name(it->kind), orig_id); + goto error; + } + if (result->sl_id == Py_slot_end) { + MSG("sentinel slot, flags %x", (unsigned)flags); + if (flags & PySlot_OPTIONAL) { + MSG("error (bad flags on sentinel)"); + PyErr_Format(PyExc_SystemError, + "invalid flags for Py_slot_end: 0x%x", + (unsigned int)flags); + goto error; + } + it->state->slot = NULL; + continue; + } + + if (result->sl_id == Py_slot_subslots + || result->sl_id == Py_tp_slots + || result->sl_id == Py_mod_slots + ) { + if (result->sl_ptr == NULL) { + MSG("NULL subslots; skipping"); + advance(it); + continue; + } + if ((it->states[0].slot_struct_kind == _PySlot_KIND_MOD) + && (it->state->slot_struct_kind == _PySlot_KIND_SLOT) + && !(result->sl_flags & PySlot_STATIC)) + { + PyErr_Format(PyExc_SystemError, + "slots included from PyModuleDef must be static"); + goto error; + } + it->recursion_level++; + MSG("recursing into level %d", it->recursion_level); + if (it->recursion_level >= _PySlot_MAX_NESTING) { + MSG("error (too much nesting)"); + PyErr_Format(PyExc_SystemError, + "%s (slot %d): too many levels of nested slots", + _PySlot_GetName(result->sl_id), orig_id); + goto error; + } + it->state = &it->states[it->recursion_level]; + memset(it->state, 0, sizeof(_PySlotIterator_state)); + it->state->slot = result->sl_ptr; + switch (result->sl_id) { + case Py_slot_subslots: + it->state->slot_struct_kind = _PySlot_KIND_SLOT; break; + case Py_tp_slots: + it->state->slot_struct_kind = _PySlot_KIND_TYPE; break; + case Py_mod_slots: + it->state->slot_struct_kind = _PySlot_KIND_MOD; break; + } + continue; + } + + if (flags & PySlot_INTPTR) { + MSG("casting from intptr"); + /* this should compile to nothing on common architectures */ + switch (_PySlot_get_dtype(result->sl_id)) { + case _PySlot_DTYPE_SIZE: { + result->sl_size = (Py_ssize_t)(intptr_t)result->sl_ptr; + } break; + case _PySlot_DTYPE_INT64: { + result->sl_int64 = (int64_t)(intptr_t)result->sl_ptr; + } break; + case _PySlot_DTYPE_UINT64: { + result->sl_uint64 = (uint64_t)(intptr_t)result->sl_ptr; + } break; + case _PySlot_DTYPE_PTR: + case _PySlot_DTYPE_FUNC: + case _PySlot_DTYPE_VOID: + break; + } + } + + advance(it); + switch (_PySlot_get_dtype(result->sl_id)) { + case _PySlot_DTYPE_VOID: + case _PySlot_DTYPE_PTR: + MSG("result: %d (%s): %p", + (int)result->sl_id, _PySlot_GetName(result->sl_id), + (void*)result->sl_ptr); + break; + case _PySlot_DTYPE_FUNC: + MSG("result: %d (%s): %p", + (int)result->sl_id, _PySlot_GetName(result->sl_id), + (void*)result->sl_func); + break; + case _PySlot_DTYPE_SIZE: + MSG("result: %d (%s): %zd", + (int)result->sl_id, _PySlot_GetName(result->sl_id), + (Py_ssize_t)result->sl_size); + break; + case _PySlot_DTYPE_INT64: + MSG("result: %d (%s): %ld", + (int)result->sl_id, _PySlot_GetName(result->sl_id), + (long)result->sl_int64); + break; + case _PySlot_DTYPE_UINT64: + MSG("result: %d (%s): %lu (0x%lx)", + (int)result->sl_id, _PySlot_GetName(result->sl_id), + (unsigned long)result->sl_int64, + (unsigned long)result->sl_int64); + break; + } + assert (result->sl_id > 0); + assert (result->sl_id <= _Py_slot_COUNT); + if (it->is_first_run && (handle_first_run(it) < 0)) { + goto error; + } + return result->sl_id != Py_slot_end; + } + Py_UNREACHABLE(); + +error: + it->current.sl_id = Py_slot_invalid; + return true; +} + +/* Validate current slot, and do bookkeeping */ +static int +handle_first_run(_PySlotIterator *it) +{ + int id = it->current.sl_id; + + if (_PySlot_get_must_be_static(id)) { + if (!(it->current.sl_flags & PySlot_STATIC) + && (it->state->slot_struct_kind == _PySlot_KIND_SLOT)) + { + PyErr_Format( + PyExc_SystemError, + "%s requires PySlot_STATIC", + _PySlot_GetName(id)); + return -1; + } + } + + _PySlot_PROBLEM_HANDLING null_handling = _PySlot_get_null_handling(id); + if (null_handling != _PySlot_PROBLEM_ALLOW) { + bool is_null = false; + switch (_PySlot_get_dtype(id)) { + case _PySlot_DTYPE_PTR: { + is_null = it->current.sl_ptr == NULL; + } break; + case _PySlot_DTYPE_FUNC: { + is_null = it->current.sl_func == NULL; + } break; + default: { + //Py_UNREACHABLE(); + } break; + } + if (is_null) { + MSG("slot is NULL but shouldn't"); + if (null_handling == _PySlot_PROBLEM_REJECT) { + MSG("error (NULL rejected)"); + PyErr_Format(PyExc_SystemError, + "NULL not allowed for slot %s", + _PySlot_GetName(id)); + return -1; + } + if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) { + MSG("deprecated NULL"); + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, + 1, + "NULL value in slot %s is deprecated", + _PySlot_GetName(id)) < 0) + { + return -1; + } + } + else { + MSG("unwanted NULL in legacy struct"); + } + } + } + + _PySlot_PROBLEM_HANDLING duplicate_handling = _PySlot_get_duplicate_handling(id); + if (duplicate_handling != _PySlot_PROBLEM_ALLOW) { + if (_PySlotIterator_SawSlot(it, id)) { + MSG("slot was seen before but shouldn't be duplicated"); + if (duplicate_handling == _PySlot_PROBLEM_REJECT) { + MSG("error (duplicate rejected)"); + PyErr_Format( + PyExc_SystemError, + "%s%s%s has multiple %s (%d) slots", + kind_name(it->kind), + it->name ? " " : "", + it->name ? it->name : "", + _PySlot_GetName(id), + (int)it->current.sl_id); + return -1; + } + if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) { + MSG("deprecated duplicate"); + if (PyErr_WarnFormat( + PyExc_DeprecationWarning, + 0, + "%s%s%s has multiple %s (%d) slots. This is deprecated.", + kind_name(it->kind), + it->name ? " " : "", + it->name ? it->name : "", + _PySlot_GetName(id), + (int)it->current.sl_id) < 0) { + return -1; + } + } + else { + MSG("unwanted duplicate in legacy struct"); + } + } + } + it->seen[seen_index(id)] |= seen_mask(id); + return 0; +} diff --git a/Python/slots.toml b/Python/slots.toml new file mode 100644 index 00000000000000..626a44d2f2c80e --- /dev/null +++ b/Python/slots.toml @@ -0,0 +1,836 @@ +# This file lists all PySlot values +# This should only be used as input to Tools/build/generate_slots.py, +# its format can change at any time (e.g. we can switch to slots.csv) + +# Entries: +# name: name of the slot +# kind: +# - 'type', 'mod': slots to create a particular kind of object +# - 'slot': special slots applicable to any kind of object +# - 'compat': old IDs that need to be resolved +# dtype: data type (tag for the union of sl_ptr, sl_size, etc.) +# equivalents: for 'compat' slots; the slots to resolve to +# is_type_field: slot that corresponds to a field in the type object (or in +# an array like PyNumberMethods). +# functype: C function type, where needed +# duplicates, nulls: How to handle common "problems" -- duplicate slots with +# the same ID, and NULL pointers, respectively +# - 'allow': not a problem for this slot +# - 'deprecated': issue a deprecation warning. Don't use for new slots. +# (typically, the problem was disallowed in docs, but allowed in practice) +# - 'reject': raise error +# The default for duplicate slots is 'reject' +# The default for NULLs is 'reject' for pointer slots; 'allow' for +# non-pointer ones +# must_be_static: true if slot needs the PySlot_STATIC flag (in PySlot struct) + + +[0] +name = 'Py_slot_end' +kind = 'slot' +dtype = 'void' + +[1] +kind = 'compat' +equivalents = {type='Py_bf_getbuffer', mod='Py_mod_create'} + +[2] +kind = 'compat' +equivalents = {type='Py_bf_releasebuffer', mod='Py_mod_exec'} + +[3] +kind = 'compat' +equivalents = {type='Py_mp_ass_subscript', mod='Py_mod_multiple_interpreters'} + +[4] +kind = 'compat' +equivalents = {type='Py_mp_length', mod='Py_mod_gil'} + +[5] +name = 'Py_mp_subscript' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[6] +name = 'Py_nb_absolute' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[7] +name = 'Py_nb_add' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[8] +name = 'Py_nb_and' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[9] +name = 'Py_nb_bool' +kind = 'type' +is_type_field = true +functype = 'inquiry' +duplicates = 'deprecated' +nulls = 'deprecated' + +[10] +name = 'Py_nb_divmod' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[11] +name = 'Py_nb_float' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[12] +name = 'Py_nb_floor_divide' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[13] +name = 'Py_nb_index' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[14] +name = 'Py_nb_inplace_add' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[15] +name = 'Py_nb_inplace_and' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[16] +name = 'Py_nb_inplace_floor_divide' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[17] +name = 'Py_nb_inplace_lshift' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[18] +name = 'Py_nb_inplace_multiply' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[19] +name = 'Py_nb_inplace_or' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[20] +name = 'Py_nb_inplace_power' +kind = 'type' +is_type_field = true +functype = 'ternaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[21] +name = 'Py_nb_inplace_remainder' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[22] +name = 'Py_nb_inplace_rshift' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[23] +name = 'Py_nb_inplace_subtract' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[24] +name = 'Py_nb_inplace_true_divide' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[25] +name = 'Py_nb_inplace_xor' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[26] +name = 'Py_nb_int' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[27] +name = 'Py_nb_invert' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[28] +name = 'Py_nb_lshift' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[29] +name = 'Py_nb_multiply' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[30] +name = 'Py_nb_negative' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[31] +name = 'Py_nb_or' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[32] +name = 'Py_nb_positive' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[33] +name = 'Py_nb_power' +kind = 'type' +is_type_field = true +functype = 'ternaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[34] +name = 'Py_nb_remainder' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[35] +name = 'Py_nb_rshift' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[36] +name = 'Py_nb_subtract' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[37] +name = 'Py_nb_true_divide' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[38] +name = 'Py_nb_xor' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[39] +name = 'Py_sq_ass_item' +kind = 'type' +is_type_field = true +functype = 'ssizeobjargproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[40] +name = 'Py_sq_concat' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[41] +name = 'Py_sq_contains' +kind = 'type' +is_type_field = true +functype = 'objobjproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[42] +name = 'Py_sq_inplace_concat' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[43] +name = 'Py_sq_inplace_repeat' +kind = 'type' +is_type_field = true +functype = 'ssizeargfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[44] +name = 'Py_sq_item' +kind = 'type' +is_type_field = true +functype = 'ssizeargfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[45] +name = 'Py_sq_length' +kind = 'type' +is_type_field = true +functype = 'lenfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[46] +name = 'Py_sq_repeat' +kind = 'type' +is_type_field = true +functype = 'ssizeargfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[47] +name = 'Py_tp_alloc' +kind = 'type' +is_type_field = true +functype = 'allocfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[48] +name = 'Py_tp_base' +kind = 'type' +is_type_field = true +dtype = 'ptr' +duplicates = 'deprecated' +nulls = 'deprecated' + +[49] +name = 'Py_tp_bases' +kind = 'type' +is_type_field = true +dtype = 'ptr' +duplicates = 'deprecated' +nulls = 'deprecated' + +[50] +name = 'Py_tp_call' +kind = 'type' +is_type_field = true +functype = 'ternaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[51] +name = 'Py_tp_clear' +kind = 'type' +is_type_field = true +functype = 'inquiry' +duplicates = 'deprecated' +nulls = 'deprecated' + +[52] +name = 'Py_tp_dealloc' +kind = 'type' +is_type_field = true +functype = 'destructor' +duplicates = 'deprecated' +nulls = 'deprecated' + +[53] +name = 'Py_tp_del' +kind = 'type' +is_type_field = true +functype = 'destructor' +duplicates = 'deprecated' +nulls = 'deprecated' + +[54] +name = 'Py_tp_descr_get' +kind = 'type' +is_type_field = true +functype = 'descrgetfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[55] +name = 'Py_tp_descr_set' +kind = 'type' +is_type_field = true +functype = 'descrsetfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[56] +name = 'Py_tp_doc' +kind = 'type' +is_type_field = true +dtype = 'ptr' +nulls = 'allow' + +[57] +name = 'Py_tp_getattr' +kind = 'type' +is_type_field = true +functype = 'getattrfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[58] +name = 'Py_tp_getattro' +kind = 'type' +is_type_field = true +functype = 'getattrofunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[59] +name = 'Py_tp_hash' +kind = 'type' +is_type_field = true +functype = 'hashfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[60] +name = 'Py_tp_init' +kind = 'type' +is_type_field = true +functype = 'initproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[61] +name = 'Py_tp_is_gc' +kind = 'type' +is_type_field = true +functype = 'inquiry' +duplicates = 'deprecated' +nulls = 'deprecated' + +[62] +name = 'Py_tp_iter' +kind = 'type' +is_type_field = true +functype = 'getiterfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[63] +name = 'Py_tp_iternext' +kind = 'type' +is_type_field = true +functype = 'iternextfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[64] +name = 'Py_tp_methods' +kind = 'type' +is_type_field = true +dtype = 'ptr' +duplicates = 'deprecated' +nulls = 'deprecated' +must_be_static = true + +[65] +name = 'Py_tp_new' +kind = 'type' +is_type_field = true +functype = 'newfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[66] +name = 'Py_tp_repr' +kind = 'type' +is_type_field = true +functype = 'reprfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[67] +name = 'Py_tp_richcompare' +kind = 'type' +is_type_field = true +functype = 'richcmpfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[68] +name = 'Py_tp_setattr' +kind = 'type' +is_type_field = true +functype = 'setattrfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[69] +name = 'Py_tp_setattro' +kind = 'type' +is_type_field = true +functype = 'setattrofunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[70] +name = 'Py_tp_str' +kind = 'type' +is_type_field = true +functype = 'reprfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[71] +name = 'Py_tp_traverse' +kind = 'type' +is_type_field = true +functype = 'traverseproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[72] +name = 'Py_tp_members' +kind = 'type' +is_type_field = true +dtype = 'ptr' +nulls = 'reject' +must_be_static = true + +[73] +name = 'Py_tp_getset' +kind = 'type' +is_type_field = true +dtype = 'ptr' +duplicates = 'deprecated' +nulls = 'deprecated' +must_be_static = true + +[74] +name = 'Py_tp_free' +kind = 'type' +is_type_field = true +functype = 'freefunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[75] +name = 'Py_nb_matrix_multiply' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[76] +name = 'Py_nb_inplace_matrix_multiply' +kind = 'type' +is_type_field = true +functype = 'binaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[77] +name = 'Py_am_await' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[78] +name = 'Py_am_aiter' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[79] +name = 'Py_am_anext' +kind = 'type' +is_type_field = true +functype = 'unaryfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[80] +name = 'Py_tp_finalize' +kind = 'type' +is_type_field = true +functype = 'destructor' +duplicates = 'deprecated' +nulls = 'deprecated' + +[81] +name = 'Py_am_send' +kind = 'type' +is_type_field = true +functype = 'sendfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[82] +name = 'Py_tp_vectorcall' +kind = 'type' +is_type_field = true +functype = 'vectorcallfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[83] +name = 'Py_tp_token' +kind = 'type' +is_type_field = true +dtype = 'ptr' +field = 'ht_token' +duplicates = 'deprecated' +nulls = 'allow' + +[84] +name = 'Py_mod_create' +kind = 'mod' +dtype = 'func' +nulls = 'deprecated' + +[85] +name = 'Py_mod_exec' +kind = 'mod' +dtype = 'func' +duplicates = 'allow' # only alowed in PyModuleDef.m_slots +nulls = 'reject' + +[86] +name = 'Py_mod_multiple_interpreters' +kind = 'mod' +dtype = 'uint64' + +[87] +name = 'Py_mod_gil' +kind = 'mod' +dtype = 'uint64' + +[88] +name = 'Py_bf_getbuffer' +kind = 'type' +is_type_field = true +functype = 'getbufferproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[89] +name = 'Py_bf_releasebuffer' +kind = 'type' +is_type_field = true +functype = 'releasebufferproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[90] +name = 'Py_mp_ass_subscript' +kind = 'type' +is_type_field = true +functype = 'objobjargproc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[91] +name = 'Py_mp_length' +kind = 'type' +is_type_field = true +functype = 'lenfunc' +duplicates = 'deprecated' +nulls = 'deprecated' + +[92] +name = 'Py_slot_subslots' +kind = 'slot' +dtype = 'ptr' +nulls = 'allow' + +[93] +name = 'Py_tp_slots' +kind = 'type' +dtype = 'ptr' +nulls = 'allow' + +[94] +name = 'Py_mod_slots' +kind = 'mod' +dtype = 'ptr' +nulls = 'allow' + +[95] +name = 'Py_tp_name' +kind = 'type' +dtype = 'ptr' + +[96] +name = 'Py_tp_basicsize' +kind = 'type' +dtype = 'size' + +[97] +name = 'Py_tp_extra_basicsize' +kind = 'type' +dtype = 'size' + +[98] +name = 'Py_tp_itemsize' +kind = 'type' +dtype = 'size' + +[99] +name = 'Py_tp_flags' +kind = 'type' +dtype = 'uint64' + +[100] +name = 'Py_mod_name' +kind = 'mod' +dtype = 'ptr' + +[101] +name = 'Py_mod_doc' +kind = 'mod' +dtype = 'ptr' + +[102] +name = 'Py_mod_state_size' +kind = 'mod' +dtype = 'size' + +[103] +name = 'Py_mod_methods' +kind = 'mod' +dtype = 'ptr' +must_be_static = true + +[104] +name = 'Py_mod_state_traverse' +kind = 'mod' +dtype = 'func' + +[105] +name = 'Py_mod_state_clear' +kind = 'mod' +dtype = 'func' + +[106] +name = 'Py_mod_state_free' +kind = 'mod' +dtype = 'func' + +[107] +name = 'Py_tp_metaclass' +kind = 'type' +dtype = 'ptr' + +[108] +name = 'Py_tp_module' +kind = 'type' +dtype = 'ptr' + +[109] +name = 'Py_mod_abi' +kind = 'mod' +dtype = 'ptr' +duplicates = 'allow' + +[110] +name = 'Py_mod_token' +kind = 'mod' +dtype = 'ptr' diff --git a/Python/slots_generated.c b/Python/slots_generated.c new file mode 100644 index 00000000000000..b8da8fb7890d8c --- /dev/null +++ b/Python/slots_generated.c @@ -0,0 +1,119 @@ +/* Generated by Tools/build/generate_slots.py */ + +#include "Python.h" +#include "pycore_slots.h" // _PySlot_names + +const char *const _PySlot_names[] = { + "Py_slot_end", + "Py_bf_getbuffer/Py_mod_create", + "Py_bf_releasebuffer/Py_mod_exec", + "Py_mp_ass_subscript/Py_mod_multiple_interpreters", + "Py_mp_length/Py_mod_gil", + "Py_mp_subscript", + "Py_nb_absolute", + "Py_nb_add", + "Py_nb_and", + "Py_nb_bool", + "Py_nb_divmod", + "Py_nb_float", + "Py_nb_floor_divide", + "Py_nb_index", + "Py_nb_inplace_add", + "Py_nb_inplace_and", + "Py_nb_inplace_floor_divide", + "Py_nb_inplace_lshift", + "Py_nb_inplace_multiply", + "Py_nb_inplace_or", + "Py_nb_inplace_power", + "Py_nb_inplace_remainder", + "Py_nb_inplace_rshift", + "Py_nb_inplace_subtract", + "Py_nb_inplace_true_divide", + "Py_nb_inplace_xor", + "Py_nb_int", + "Py_nb_invert", + "Py_nb_lshift", + "Py_nb_multiply", + "Py_nb_negative", + "Py_nb_or", + "Py_nb_positive", + "Py_nb_power", + "Py_nb_remainder", + "Py_nb_rshift", + "Py_nb_subtract", + "Py_nb_true_divide", + "Py_nb_xor", + "Py_sq_ass_item", + "Py_sq_concat", + "Py_sq_contains", + "Py_sq_inplace_concat", + "Py_sq_inplace_repeat", + "Py_sq_item", + "Py_sq_length", + "Py_sq_repeat", + "Py_tp_alloc", + "Py_tp_base", + "Py_tp_bases", + "Py_tp_call", + "Py_tp_clear", + "Py_tp_dealloc", + "Py_tp_del", + "Py_tp_descr_get", + "Py_tp_descr_set", + "Py_tp_doc", + "Py_tp_getattr", + "Py_tp_getattro", + "Py_tp_hash", + "Py_tp_init", + "Py_tp_is_gc", + "Py_tp_iter", + "Py_tp_iternext", + "Py_tp_methods", + "Py_tp_new", + "Py_tp_repr", + "Py_tp_richcompare", + "Py_tp_setattr", + "Py_tp_setattro", + "Py_tp_str", + "Py_tp_traverse", + "Py_tp_members", + "Py_tp_getset", + "Py_tp_free", + "Py_nb_matrix_multiply", + "Py_nb_inplace_matrix_multiply", + "Py_am_await", + "Py_am_aiter", + "Py_am_anext", + "Py_tp_finalize", + "Py_am_send", + "Py_tp_vectorcall", + "Py_tp_token", + "Py_mod_create", + "Py_mod_exec", + "Py_mod_multiple_interpreters", + "Py_mod_gil", + "Py_bf_getbuffer", + "Py_bf_releasebuffer", + "Py_mp_ass_subscript", + "Py_mp_length", + "Py_slot_subslots", + "Py_tp_slots", + "Py_mod_slots", + "Py_tp_name", + "Py_tp_basicsize", + "Py_tp_extra_basicsize", + "Py_tp_itemsize", + "Py_tp_flags", + "Py_mod_name", + "Py_mod_doc", + "Py_mod_state_size", + "Py_mod_methods", + "Py_mod_state_traverse", + "Py_mod_state_clear", + "Py_mod_state_free", + "Py_tp_metaclass", + "Py_tp_module", + "Py_mod_abi", + "Py_mod_token", + NULL +}; diff --git a/Python/specialize.c b/Python/specialize.c index fe8d04cf3442f1..04aeb4a76d4046 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2,13 +2,16 @@ #include "opcode.h" +#include "pycore_bytesobject.h" // _PyBytes_Concat #include "pycore_code.h" #include "pycore_critical_section.h" #include "pycore_descrobject.h" // _PyMethodWrapper_Type #include "pycore_dict.h" // DICT_KEYS_UNICODE #include "pycore_function.h" // _PyFunction_GetVersionForCurrentState() #include "pycore_interpframe.h" // FRAME_SPECIALS_SIZE -#include "pycore_list.h" // _PyListIterObject +#include "pycore_lazyimportobject.h" // PyLazyImport_CheckExact +#include "pycore_list.h" // _PyListIterObject, _PyList_Concat +#include "pycore_tuple.h" // _PyTuple_Concat #include "pycore_long.h" // _PyLong_IsNonNegativeCompact() #include "pycore_moduleobject.h" #include "pycore_object.h" @@ -22,451 +25,56 @@ #include <stdlib.h> // rand() -extern const char *_PyUOpName(int index); - /* For guidance on adding or extending families of instructions see * InternalDocs/interpreter.md `Specialization` section. */ -#ifdef Py_STATS -GCStats _py_gc_stats[NUM_GENERATIONS] = { 0 }; -static PyStats _Py_stats_struct = { .gc_stats = _py_gc_stats }; -PyStats *_Py_stats = NULL; - -#if PYSTATS_MAX_UOP_ID < MAX_UOP_ID -#error "Not enough space allocated for pystats. Increase PYSTATS_MAX_UOP_ID to at least MAX_UOP_ID" -#endif - -#define ADD_STAT_TO_DICT(res, field) \ - do { \ - PyObject *val = PyLong_FromUnsignedLongLong(stats->field); \ - if (val == NULL) { \ - Py_DECREF(res); \ - return NULL; \ - } \ - if (PyDict_SetItemString(res, #field, val) == -1) { \ - Py_DECREF(res); \ - Py_DECREF(val); \ - return NULL; \ - } \ - Py_DECREF(val); \ - } while(0); - -static PyObject* -stats_to_dict(SpecializationStats *stats) -{ - PyObject *res = PyDict_New(); - if (res == NULL) { - return NULL; - } - ADD_STAT_TO_DICT(res, success); - ADD_STAT_TO_DICT(res, failure); - ADD_STAT_TO_DICT(res, hit); - ADD_STAT_TO_DICT(res, deferred); - ADD_STAT_TO_DICT(res, miss); - ADD_STAT_TO_DICT(res, deopt); - PyObject *failure_kinds = PyTuple_New(SPECIALIZATION_FAILURE_KINDS); - if (failure_kinds == NULL) { - Py_DECREF(res); - return NULL; - } - for (int i = 0; i < SPECIALIZATION_FAILURE_KINDS; i++) { - PyObject *stat = PyLong_FromUnsignedLongLong(stats->failure_kinds[i]); - if (stat == NULL) { - Py_DECREF(res); - Py_DECREF(failure_kinds); - return NULL; - } - PyTuple_SET_ITEM(failure_kinds, i, stat); - } - if (PyDict_SetItemString(res, "failure_kinds", failure_kinds)) { - Py_DECREF(res); - Py_DECREF(failure_kinds); - return NULL; - } - Py_DECREF(failure_kinds); - return res; -} -#undef ADD_STAT_TO_DICT - -static int -add_stat_dict( - PyObject *res, - int opcode, - const char *name) { - - SpecializationStats *stats = &_Py_stats_struct.opcode_stats[opcode].specialization; - PyObject *d = stats_to_dict(stats); - if (d == NULL) { - return -1; - } - int err = PyDict_SetItemString(res, name, d); - Py_DECREF(d); - return err; -} - -PyObject* -_Py_GetSpecializationStats(void) { - PyObject *stats = PyDict_New(); - if (stats == NULL) { - return NULL; - } - int err = 0; - err += add_stat_dict(stats, CONTAINS_OP, "contains_op"); - err += add_stat_dict(stats, LOAD_SUPER_ATTR, "load_super_attr"); - err += add_stat_dict(stats, LOAD_ATTR, "load_attr"); - err += add_stat_dict(stats, LOAD_GLOBAL, "load_global"); - err += add_stat_dict(stats, STORE_SUBSCR, "store_subscr"); - err += add_stat_dict(stats, STORE_ATTR, "store_attr"); - err += add_stat_dict(stats, JUMP_BACKWARD, "jump_backward"); - err += add_stat_dict(stats, CALL, "call"); - err += add_stat_dict(stats, CALL_KW, "call_kw"); - err += add_stat_dict(stats, BINARY_OP, "binary_op"); - err += add_stat_dict(stats, COMPARE_OP, "compare_op"); - err += add_stat_dict(stats, UNPACK_SEQUENCE, "unpack_sequence"); - err += add_stat_dict(stats, FOR_ITER, "for_iter"); - err += add_stat_dict(stats, TO_BOOL, "to_bool"); - err += add_stat_dict(stats, SEND, "send"); - if (err < 0) { - Py_DECREF(stats); - return NULL; - } - return stats; -} - - -#define PRINT_STAT(i, field) \ - if (stats[i].field) { \ - fprintf(out, " opcode[%s]." #field " : %" PRIu64 "\n", _PyOpcode_OpName[i], stats[i].field); \ - } - -static void -print_spec_stats(FILE *out, OpcodeStats *stats) -{ - /* Mark some opcodes as specializable for stats, - * even though we don't specialize them yet. */ - fprintf(out, "opcode[BINARY_SLICE].specializable : 1\n"); - fprintf(out, "opcode[STORE_SLICE].specializable : 1\n"); - fprintf(out, "opcode[GET_ITER].specializable : 1\n"); - for (int i = 0; i < 256; i++) { - if (_PyOpcode_Caches[i]) { - /* Ignore jumps as they cannot be specialized */ - switch (i) { - case POP_JUMP_IF_FALSE: - case POP_JUMP_IF_TRUE: - case POP_JUMP_IF_NONE: - case POP_JUMP_IF_NOT_NONE: - case JUMP_BACKWARD: - break; - default: - fprintf(out, "opcode[%s].specializable : 1\n", _PyOpcode_OpName[i]); - } - } - PRINT_STAT(i, specialization.success); - PRINT_STAT(i, specialization.failure); - PRINT_STAT(i, specialization.hit); - PRINT_STAT(i, specialization.deferred); - PRINT_STAT(i, specialization.miss); - PRINT_STAT(i, specialization.deopt); - PRINT_STAT(i, execution_count); - for (int j = 0; j < SPECIALIZATION_FAILURE_KINDS; j++) { - uint64_t val = stats[i].specialization.failure_kinds[j]; - if (val) { - fprintf(out, " opcode[%s].specialization.failure_kinds[%d] : %" - PRIu64 "\n", _PyOpcode_OpName[i], j, val); - } - } - for (int j = 0; j < 256; j++) { - if (stats[i].pair_count[j]) { - fprintf(out, "opcode[%s].pair_count[%s] : %" PRIu64 "\n", - _PyOpcode_OpName[i], _PyOpcode_OpName[j], stats[i].pair_count[j]); - } - } - } -} -#undef PRINT_STAT - - -static void -print_call_stats(FILE *out, CallStats *stats) -{ - fprintf(out, "Calls to PyEval_EvalDefault: %" PRIu64 "\n", stats->pyeval_calls); - fprintf(out, "Calls to Python functions inlined: %" PRIu64 "\n", stats->inlined_py_calls); - fprintf(out, "Frames pushed: %" PRIu64 "\n", stats->frames_pushed); - fprintf(out, "Frame objects created: %" PRIu64 "\n", stats->frame_objects_created); - for (int i = 0; i < EVAL_CALL_KINDS; i++) { - fprintf(out, "Calls via PyEval_EvalFrame[%d] : %" PRIu64 "\n", i, stats->eval_calls[i]); - } -} - -static void -print_object_stats(FILE *out, ObjectStats *stats) -{ - fprintf(out, "Object allocations from freelist: %" PRIu64 "\n", stats->from_freelist); - fprintf(out, "Object frees to freelist: %" PRIu64 "\n", stats->to_freelist); - fprintf(out, "Object allocations: %" PRIu64 "\n", stats->allocations); - fprintf(out, "Object allocations to 512 bytes: %" PRIu64 "\n", stats->allocations512); - fprintf(out, "Object allocations to 4 kbytes: %" PRIu64 "\n", stats->allocations4k); - fprintf(out, "Object allocations over 4 kbytes: %" PRIu64 "\n", stats->allocations_big); - fprintf(out, "Object frees: %" PRIu64 "\n", stats->frees); - fprintf(out, "Object inline values: %" PRIu64 "\n", stats->inline_values); - fprintf(out, "Object interpreter mortal increfs: %" PRIu64 "\n", stats->interpreter_increfs); - fprintf(out, "Object interpreter mortal decrefs: %" PRIu64 "\n", stats->interpreter_decrefs); - fprintf(out, "Object mortal increfs: %" PRIu64 "\n", stats->increfs); - fprintf(out, "Object mortal decrefs: %" PRIu64 "\n", stats->decrefs); - fprintf(out, "Object interpreter immortal increfs: %" PRIu64 "\n", stats->interpreter_immortal_increfs); - fprintf(out, "Object interpreter immortal decrefs: %" PRIu64 "\n", stats->interpreter_immortal_decrefs); - fprintf(out, "Object immortal increfs: %" PRIu64 "\n", stats->immortal_increfs); - fprintf(out, "Object immortal decrefs: %" PRIu64 "\n", stats->immortal_decrefs); - fprintf(out, "Object materialize dict (on request): %" PRIu64 "\n", stats->dict_materialized_on_request); - fprintf(out, "Object materialize dict (new key): %" PRIu64 "\n", stats->dict_materialized_new_key); - fprintf(out, "Object materialize dict (too big): %" PRIu64 "\n", stats->dict_materialized_too_big); - fprintf(out, "Object materialize dict (str subclass): %" PRIu64 "\n", stats->dict_materialized_str_subclass); - fprintf(out, "Object method cache hits: %" PRIu64 "\n", stats->type_cache_hits); - fprintf(out, "Object method cache misses: %" PRIu64 "\n", stats->type_cache_misses); - fprintf(out, "Object method cache collisions: %" PRIu64 "\n", stats->type_cache_collisions); - fprintf(out, "Object method cache dunder hits: %" PRIu64 "\n", stats->type_cache_dunder_hits); - fprintf(out, "Object method cache dunder misses: %" PRIu64 "\n", stats->type_cache_dunder_misses); -} - -static void -print_gc_stats(FILE *out, GCStats *stats) -{ - for (int i = 0; i < NUM_GENERATIONS; i++) { - fprintf(out, "GC[%d] collections: %" PRIu64 "\n", i, stats[i].collections); - fprintf(out, "GC[%d] object visits: %" PRIu64 "\n", i, stats[i].object_visits); - fprintf(out, "GC[%d] objects collected: %" PRIu64 "\n", i, stats[i].objects_collected); - fprintf(out, "GC[%d] objects reachable from roots: %" PRIu64 "\n", i, stats[i].objects_transitively_reachable); - fprintf(out, "GC[%d] objects not reachable from roots: %" PRIu64 "\n", i, stats[i].objects_not_transitively_reachable); - } -} - -#ifdef _Py_TIER2 -static void -print_histogram(FILE *out, const char *name, uint64_t hist[_Py_UOP_HIST_SIZE]) -{ - for (int i = 0; i < _Py_UOP_HIST_SIZE; i++) { - fprintf(out, "%s[%" PRIu64"]: %" PRIu64 "\n", name, (uint64_t)1 << i, hist[i]); - } -} - -static void -print_optimization_stats(FILE *out, OptimizationStats *stats) -{ - fprintf(out, "Optimization attempts: %" PRIu64 "\n", stats->attempts); - fprintf(out, "Optimization traces created: %" PRIu64 "\n", stats->traces_created); - fprintf(out, "Optimization traces executed: %" PRIu64 "\n", stats->traces_executed); - fprintf(out, "Optimization uops executed: %" PRIu64 "\n", stats->uops_executed); - fprintf(out, "Optimization trace stack overflow: %" PRIu64 "\n", stats->trace_stack_overflow); - fprintf(out, "Optimization trace stack underflow: %" PRIu64 "\n", stats->trace_stack_underflow); - fprintf(out, "Optimization trace too long: %" PRIu64 "\n", stats->trace_too_long); - fprintf(out, "Optimization trace too short: %" PRIu64 "\n", stats->trace_too_short); - fprintf(out, "Optimization inner loop: %" PRIu64 "\n", stats->inner_loop); - fprintf(out, "Optimization recursive call: %" PRIu64 "\n", stats->recursive_call); - fprintf(out, "Optimization low confidence: %" PRIu64 "\n", stats->low_confidence); - fprintf(out, "Optimization unknown callee: %" PRIu64 "\n", stats->unknown_callee); - fprintf(out, "Executors invalidated: %" PRIu64 "\n", stats->executors_invalidated); - - print_histogram(out, "Trace length", stats->trace_length_hist); - print_histogram(out, "Trace run length", stats->trace_run_length_hist); - print_histogram(out, "Optimized trace length", stats->optimized_trace_length_hist); - - fprintf(out, "Optimization optimizer attempts: %" PRIu64 "\n", stats->optimizer_attempts); - fprintf(out, "Optimization optimizer successes: %" PRIu64 "\n", stats->optimizer_successes); - fprintf(out, "Optimization optimizer failure no memory: %" PRIu64 "\n", - stats->optimizer_failure_reason_no_memory); - fprintf(out, "Optimizer remove globals builtins changed: %" PRIu64 "\n", stats->remove_globals_builtins_changed); - fprintf(out, "Optimizer remove globals incorrect keys: %" PRIu64 "\n", stats->remove_globals_incorrect_keys); - for (int i = 0; i <= MAX_UOP_ID; i++) { - if (stats->opcode[i].execution_count) { - fprintf(out, "uops[%s].execution_count : %" PRIu64 "\n", _PyUOpName(i), stats->opcode[i].execution_count); - } - if (stats->opcode[i].miss) { - fprintf(out, "uops[%s].specialization.miss : %" PRIu64 "\n", _PyUOpName(i), stats->opcode[i].miss); - } - } - for (int i = 0; i < 256; i++) { - if (stats->unsupported_opcode[i]) { - fprintf( - out, - "unsupported_opcode[%s].count : %" PRIu64 "\n", - _PyOpcode_OpName[i], - stats->unsupported_opcode[i] - ); - } - } - - for (int i = 1; i <= MAX_UOP_ID; i++){ - for (int j = 1; j <= MAX_UOP_ID; j++) { - if (stats->opcode[i].pair_count[j]) { - fprintf(out, "uop[%s].pair_count[%s] : %" PRIu64 "\n", - _PyOpcode_uop_name[i], _PyOpcode_uop_name[j], stats->opcode[i].pair_count[j]); - } - } - } - for (int i = 0; i < MAX_UOP_ID; i++) { - if (stats->error_in_opcode[i]) { - fprintf( - out, - "error_in_opcode[%s].count : %" PRIu64 "\n", - _PyUOpName(i), - stats->error_in_opcode[i] - ); - } - } - fprintf(out, "JIT total memory size: %" PRIu64 "\n", stats->jit_total_memory_size); - fprintf(out, "JIT code size: %" PRIu64 "\n", stats->jit_code_size); - fprintf(out, "JIT trampoline size: %" PRIu64 "\n", stats->jit_trampoline_size); - fprintf(out, "JIT data size: %" PRIu64 "\n", stats->jit_data_size); - fprintf(out, "JIT padding size: %" PRIu64 "\n", stats->jit_padding_size); - fprintf(out, "JIT freed memory size: %" PRIu64 "\n", stats->jit_freed_memory_size); - - print_histogram(out, "Trace total memory size", stats->trace_total_memory_hist); -} -#endif - -static void -print_rare_event_stats(FILE *out, RareEventStats *stats) -{ - fprintf(out, "Rare event (set_class): %" PRIu64 "\n", stats->set_class); - fprintf(out, "Rare event (set_bases): %" PRIu64 "\n", stats->set_bases); - fprintf(out, "Rare event (set_eval_frame_func): %" PRIu64 "\n", stats->set_eval_frame_func); - fprintf(out, "Rare event (builtin_dict): %" PRIu64 "\n", stats->builtin_dict); - fprintf(out, "Rare event (func_modification): %" PRIu64 "\n", stats->func_modification); - fprintf(out, "Rare event (watched_dict_modification): %" PRIu64 "\n", stats->watched_dict_modification); - fprintf(out, "Rare event (watched_globals_modification): %" PRIu64 "\n", stats->watched_globals_modification); -} - -static void -print_stats(FILE *out, PyStats *stats) -{ - print_spec_stats(out, stats->opcode_stats); - print_call_stats(out, &stats->call_stats); - print_object_stats(out, &stats->object_stats); - print_gc_stats(out, stats->gc_stats); -#ifdef _Py_TIER2 - print_optimization_stats(out, &stats->optimization_stats); -#endif - print_rare_event_stats(out, &stats->rare_event_stats); -} - -void -_Py_StatsOn(void) -{ - _Py_stats = &_Py_stats_struct; -} - -void -_Py_StatsOff(void) -{ - _Py_stats = NULL; -} - -void -_Py_StatsClear(void) -{ - memset(&_py_gc_stats, 0, sizeof(_py_gc_stats)); - memset(&_Py_stats_struct, 0, sizeof(_Py_stats_struct)); - _Py_stats_struct.gc_stats = _py_gc_stats; -} - -static int -mem_is_zero(unsigned char *ptr, size_t size) -{ - for (size_t i=0; i < size; i++) { - if (*ptr != 0) { - return 0; - } - ptr++; - } - return 1; -} - -int -_Py_PrintSpecializationStats(int to_file) -{ - PyStats *stats = &_Py_stats_struct; -#define MEM_IS_ZERO(DATA) mem_is_zero((unsigned char*)DATA, sizeof(*(DATA))) - int is_zero = ( - MEM_IS_ZERO(stats->gc_stats) // is a pointer - && MEM_IS_ZERO(&stats->opcode_stats) - && MEM_IS_ZERO(&stats->call_stats) - && MEM_IS_ZERO(&stats->object_stats) - ); -#undef MEM_IS_ZERO - if (is_zero) { - // gh-108753: -X pystats command line was used, but then _stats_off() - // and _stats_clear() have been called: in this case, avoid printing - // useless "all zeros" statistics. - return 0; - } - - FILE *out = stderr; - if (to_file) { - /* Write to a file instead of stderr. */ -# ifdef MS_WINDOWS - const char *dirname = "c:\\temp\\py_stats\\"; -# else - const char *dirname = "/tmp/py_stats/"; -# endif - /* Use random 160 bit number as file name, - * to avoid both accidental collisions and - * symlink attacks. */ - unsigned char rand[20]; - char hex_name[41]; - _PyOS_URandomNonblock(rand, 20); - for (int i = 0; i < 20; i++) { - hex_name[2*i] = Py_hexdigits[rand[i]&15]; - hex_name[2*i+1] = Py_hexdigits[(rand[i]>>4)&15]; - } - hex_name[40] = '\0'; - char buf[64]; - assert(strlen(dirname) + 40 + strlen(".txt") < 64); - sprintf(buf, "%s%s.txt", dirname, hex_name); - FILE *fout = fopen(buf, "w"); - if (fout) { - out = fout; - } - } - else { - fprintf(out, "Specialization stats:\n"); - } - print_stats(out, stats); - if (out != stderr) { - fclose(out); - } - return 1; -} - +#if Py_STATS #define SPECIALIZATION_FAIL(opcode, kind) \ do { \ - if (_Py_stats) { \ + PyStats *s = _PyStats_GET(); \ + if (s) { \ int _kind = (kind); \ assert(_kind < SPECIALIZATION_FAILURE_KINDS); \ - _Py_stats->opcode_stats[opcode].specialization.failure_kinds[_kind]++; \ + s->opcode_stats[opcode].specialization.failure_kinds[_kind]++; \ } \ } while (0) - +#else +# define SPECIALIZATION_FAIL(opcode, kind) ((void)0) #endif // Py_STATS - -#ifndef SPECIALIZATION_FAIL -# define SPECIALIZATION_FAIL(opcode, kind) ((void)0) -#endif +static void +fixup_getiter(_Py_CODEUNIT *instruction, int flags) +{ + // Compiler can't know if types.coroutine() will be called, + // so fix up here + if (instruction->op.arg) { + if (flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) { + instruction->op.arg = GET_ITER_YIELD_FROM_NO_CHECK; + } + else { + instruction->op.arg = GET_ITER_YIELD_FROM_CORO_CHECK; + } + } +} // Initialize warmup counters and optimize instructions. This cannot fail. void -_PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters) +_PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters, int flags) { - #if ENABLE_SPECIALIZATION_FT - _Py_BackoffCounter jump_counter, adaptive_counter; + #if ENABLE_SPECIALIZATION + _Py_BackoffCounter jump_counter, adaptive_counter, resume_counter; if (enable_counters) { - jump_counter = initial_jump_backoff_counter(); + PyThreadState *tstate = _PyThreadState_GET(); + PyInterpreterState *interp = tstate->interp; + jump_counter = initial_jump_backoff_counter(&interp->opt_config); adaptive_counter = adaptive_counter_warmup(); + resume_counter = initial_resume_backoff_counter(&interp->opt_config); } else { jump_counter = initial_unreachable_backoff_counter(); adaptive_counter = initial_unreachable_backoff_counter(); + resume_counter = initial_unreachable_backoff_counter(); } int opcode = 0; int oparg = 0; @@ -475,12 +83,18 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters opcode = instructions[i].op.code; int caches = _PyOpcode_Caches[opcode]; oparg = (oparg << 8) | instructions[i].op.arg; + if (opcode == GET_ITER) { + fixup_getiter(&instructions[i], flags); + } if (caches) { // The initial value depends on the opcode switch (opcode) { case JUMP_BACKWARD: instructions[i + 1].counter = jump_counter; break; + case RESUME: + instructions[i + 1].counter = resume_counter; + break; case POP_JUMP_IF_FALSE: case POP_JUMP_IF_TRUE: case POP_JUMP_IF_NONE: @@ -497,7 +111,14 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters oparg = 0; } } - #endif /* ENABLE_SPECIALIZATION_FT */ + #else + for (Py_ssize_t i = 0; i < size-1; i++) { + if (instructions[i].op.code == GET_ITER) { + fixup_getiter(&instructions[i], flags); + } + i += _PyOpcode_Caches[opcode]; + } + #endif /* ENABLE_SPECIALIZATION */ } #define SIMPLE_FUNCTION 0 @@ -541,6 +162,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters #define SPEC_FAIL_ATTR_BUILTIN_CLASS_METHOD 22 #define SPEC_FAIL_ATTR_CLASS_METHOD_OBJ 23 #define SPEC_FAIL_ATTR_OBJECT_SLOT 24 +#define SPEC_FAIL_ATTR_MODULE_LAZY_VALUE 25 #define SPEC_FAIL_ATTR_INSTANCE_ATTRIBUTE 26 #define SPEC_FAIL_ATTR_METACLASS_ATTRIBUTE 27 @@ -553,6 +175,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters #define SPEC_FAIL_ATTR_METACLASS_OVERRIDDEN 34 #define SPEC_FAIL_ATTR_SPLIT_DICT 35 #define SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED 36 +#define SPEC_FAIL_ATTR_SLOT_AFTER_ITEMS 37 /* Binary subscr and store subscr */ @@ -630,6 +253,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters #define SPEC_FAIL_CALL_INIT_NOT_PYTHON 21 #define SPEC_FAIL_CALL_PEP_523 22 #define SPEC_FAIL_CALL_BOUND_METHOD 23 +#define SPEC_FAIL_CALL_VECTORCALL 24 #define SPEC_FAIL_CALL_CLASS_MUTABLE 26 #define SPEC_FAIL_CALL_METHOD_WRAPPER 28 #define SPEC_FAIL_CALL_OPERATOR_WRAPPER 29 @@ -768,6 +392,21 @@ static int function_kind(PyCodeObject *code); static bool function_check_args(PyObject *o, int expected_argcount, int opcode); static uint32_t function_get_version(PyObject *o, int opcode); +#ifdef Py_GIL_DISABLED +static void +maybe_enable_deferred_ref_count(PyObject *op) +{ + if (!_Py_IsOwnedByCurrentThread(op) && _PyObject_GC_IS_TRACKED(op)) { + // For module level variables that are heavily used from multiple + // threads, deferred reference counting provides good scaling + // benefits. The downside is that the object will only be deallocated + // by a GC run. + PyUnstable_Object_EnableDeferredRefcount(op); + } +} +#endif + + static int specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, PyObject *name) { @@ -776,14 +415,13 @@ specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, P SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NON_STRING); return -1; } - Py_ssize_t index = _PyDict_LookupIndex(dict, &_Py_ID(__getattr__)); - assert(index != DKIX_ERROR); - if (index != DKIX_EMPTY) { - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_MODULE_ATTR_NOT_FOUND); + PyObject *value; + Py_ssize_t index = _PyDict_LookupIndexAndValue(dict, name, &value); + if (value != NULL && PyLazyImport_CheckExact(value)) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_MODULE_LAZY_VALUE); return -1; } - index = _PyDict_LookupIndex(dict, name); - assert (index != DKIX_ERROR); + assert(index != DKIX_ERROR); if (index != (uint16_t)index) { SPECIALIZATION_FAIL(LOAD_ATTR, index == DKIX_EMPTY ? @@ -797,6 +435,9 @@ specialize_module_load_attr_lock_held(PyDictObject *dict, _Py_CODEUNIT *instr, P SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); return -1; } +#ifdef Py_GIL_DISABLED + maybe_enable_deferred_ref_count(value); +#endif write_u32(cache->version, keys_version); cache->index = (uint16_t)index; specialize(instr, LOAD_ATTR_MODULE); @@ -828,7 +469,7 @@ _Py_Specialize_LoadSuperAttr(_PyStackRef global_super_st, _PyStackRef cls_st, _P PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st); PyObject *cls = PyStackRef_AsPyObjectBorrow(cls_st); - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[LOAD_SUPER_ATTR] == INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR); if (global_super != (PyObject *)&PySuper_Type) { SPECIALIZATION_FAIL(LOAD_SUPER_ATTR, SPEC_FAIL_SUPER_SHADOWED); @@ -935,8 +576,7 @@ analyze_descriptor_load(PyTypeObject *type, PyObject *name, PyObject **descr, un PyObject *getattr = _PyType_Lookup(type, &_Py_ID(__getattr__)); has_getattr = getattr != NULL; if (has_custom_getattribute) { - if (getattro_slot == _Py_slot_tp_getattro && - !has_getattr && + if (!has_getattr && Py_IS_TYPE(getattribute, &PyFunction_Type)) { *descr = getattribute; *tp_version = ga_version; @@ -1198,7 +838,7 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* return -1; } /* Don't specialize if PEP 523 is active */ - if (_PyInterpreterState_GET()->eval_frame) { + if (!_PyInterpreterState_IsSpecializationEnabled(_PyInterpreterState_GET())) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER); return -1; } @@ -1208,8 +848,13 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* return -1; } #endif + uint32_t func_version = function_get_version(fget, LOAD_ATTR); + if (func_version == 0) { + return -1; + } assert(tp_version != 0); write_u32(lm_cache->type_version, tp_version); + write_u32(lm_cache->keys_version, func_version); /* borrowed */ write_ptr(lm_cache->descr, fget); specialize(instr, LOAD_ATTR_PROPERTY); @@ -1224,6 +869,10 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR); return -1; } + if (dmem->flags & _Py_AFTER_ITEMS) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SLOT_AFTER_ITEMS); + return -1; + } if (dmem->flags & Py_AUDIT_READ) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_AUDITED_SLOT); return -1; @@ -1259,12 +908,6 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* return -1; case GETATTRIBUTE_IS_PYTHON_FUNCTION: { - #ifndef Py_GIL_DISABLED - // In free-threaded builds it's possible for tp_getattro to change - // after the call to analyze_descriptor. That is fine: the version - // guard will fail. - assert(type->tp_getattro == _Py_slot_tp_getattro); - #endif assert(Py_IS_TYPE(descr, &PyFunction_Type)); _PyLoadMethodCache *lm_cache = (_PyLoadMethodCache *)(instr + 1); if (!function_check_args(descr, 2, LOAD_ATTR)) { @@ -1279,7 +922,7 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* return -1; } /* Don't specialize if PEP 523 is active */ - if (_PyInterpreterState_GET()->eval_frame) { + if (!_PyInterpreterState_IsSpecializationEnabled(_PyInterpreterState_GET())) { SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER); return -1; } @@ -1352,7 +995,7 @@ _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *nam { PyObject *owner = PyStackRef_AsPyObjectBorrow(owner_st); - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[LOAD_ATTR] == INLINE_CACHE_ENTRIES_LOAD_ATTR); PyTypeObject *type = Py_TYPE(owner); bool fail; @@ -1383,7 +1026,7 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na { PyObject *owner = PyStackRef_AsPyObjectBorrow(owner_st); - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[STORE_ATTR] == INLINE_CACHE_ENTRIES_STORE_ATTR); PyObject *descr = NULL; _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); @@ -1424,6 +1067,10 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_EXPECTED_ERROR); goto fail; } + if (dmem->flags & _Py_AFTER_ITEMS) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SLOT_AFTER_ITEMS); + goto fail; + } if (dmem->flags & Py_READONLY) { SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_ATTR_READ_ONLY); goto fail; @@ -1561,22 +1208,33 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, } } switch (kind) { - case METHOD: - case NON_DESCRIPTOR: - #ifdef Py_GIL_DISABLED - if (!_PyObject_HasDeferredRefcount(descr)) { - SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED); + case MUTABLE: + // special case for enums which has Py_TYPE(descr) == cls + // so guarding on type version is sufficient + if (Py_TYPE(descr) != cls) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_MUTABLE_CLASS); Py_XDECREF(descr); return -1; } - #endif - write_u32(cache->type_version, tp_version); + if (Py_TYPE(descr)->tp_descr_get || Py_TYPE(descr)->tp_descr_set) { + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR); + Py_XDECREF(descr); + return -1; + } + _Py_FALLTHROUGH; + case METHOD: + case NON_DESCRIPTOR: +#ifdef Py_GIL_DISABLED + maybe_enable_deferred_ref_count(descr); +#endif write_ptr(cache->descr, descr); if (metaclass_check) { - write_u32(cache->keys_version, meta_version); + write_u32(cache->keys_version, tp_version); + write_u32(cache->type_version, meta_version); specialize(instr, LOAD_ATTR_CLASS_WITH_METACLASS_CHECK); } else { + write_u32(cache->type_version, tp_version); specialize(instr, LOAD_ATTR_CLASS); } Py_XDECREF(descr); @@ -1627,7 +1285,6 @@ specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr, SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); return 0; } - write_u32(cache->keys_version, shared_keys_version); specialize(instr, is_method ? LOAD_ATTR_METHOD_WITH_VALUES : LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES); } else { @@ -1684,13 +1341,12 @@ specialize_attr_loadclassattr(PyObject *owner, _Py_CODEUNIT *instr, return 1; } - static void specialize_load_global_lock_held( PyObject *globals, PyObject *builtins, _Py_CODEUNIT *instr, PyObject *name) { - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[LOAD_GLOBAL] == INLINE_CACHE_ENTRIES_LOAD_GLOBAL); /* Use inline cache */ _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)(instr + 1); @@ -1700,15 +1356,20 @@ specialize_load_global_lock_held( goto fail; } PyDictKeysObject * globals_keys = ((PyDictObject *)globals)->ma_keys; - if (!DK_IS_UNICODE(globals_keys)) { + if (globals_keys->dk_kind != DICT_KEYS_UNICODE) { SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_LOAD_GLOBAL_NON_STRING_OR_SPLIT); goto fail; } - Py_ssize_t index = _PyDictKeys_StringLookup(globals_keys, name); + PyObject *value; + Py_ssize_t index = _PyDict_LookupIndexAndValue((PyDictObject *)globals, name, &value); if (index == DKIX_ERROR) { SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_EXPECTED_ERROR); goto fail; } + if (value != NULL && PyLazyImport_CheckExact(value)) { + SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_ATTR_MODULE_LAZY_VALUE); + goto fail; + } PyInterpreterState *interp = _PyInterpreterState_GET(); if (index != DKIX_EMPTY) { if (index != (uint16_t)index) { @@ -1725,6 +1386,9 @@ specialize_load_global_lock_held( SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_OUT_OF_RANGE); goto fail; } +#ifdef Py_GIL_DISABLED + maybe_enable_deferred_ref_count(value); +#endif cache->index = (uint16_t)index; cache->module_keys_version = (uint16_t)keys_version; specialize(instr, LOAD_GLOBAL_MODULE); @@ -1735,7 +1399,7 @@ specialize_load_global_lock_held( goto fail; } PyDictKeysObject * builtin_keys = ((PyDictObject *)builtins)->ma_keys; - if (!DK_IS_UNICODE(builtin_keys)) { + if (builtin_keys->dk_kind != DICT_KEYS_UNICODE) { SPECIALIZATION_FAIL(LOAD_GLOBAL, SPEC_FAIL_LOAD_GLOBAL_NON_STRING_OR_SPLIT); goto fail; } @@ -1903,7 +1567,7 @@ _Py_Specialize_StoreSubscr(_PyStackRef container_st, _PyStackRef sub_st, _Py_COD PyObject *container = PyStackRef_AsPyObjectBorrow(container_st); PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st); - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); PyTypeObject *container_type = Py_TYPE(container); if (container_type == &PyList_Type) { if (PyLong_CheckExact(sub)) { @@ -1930,7 +1594,9 @@ _Py_Specialize_StoreSubscr(_PyStackRef container_st, _PyStackRef sub_st, _Py_COD return; } } - if (container_type == &PyDict_Type) { + if (container_type->tp_as_mapping != NULL && + container_type->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript) + { specialize(instr, STORE_SUBSCR_DICT); return; } @@ -2022,8 +1688,8 @@ specialize_class_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) } static int -specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr, - int nargs) +specialize_method_descriptor(PyMethodDescrObject *descr, PyObject *self_or_null, + _Py_CODEUNIT *instr, int nargs) { switch (descr->d_method->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | @@ -2043,12 +1709,13 @@ specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr, } PyInterpreterState *interp = _PyInterpreterState_GET(); PyObject *list_append = interp->callable_cache.list_append; - _Py_CODEUNIT next = instr[INLINE_CACHE_ENTRIES_CALL + 1]; - bool pop = (next.op.code == POP_TOP); int oparg = instr->op.arg; - if ((PyObject *)descr == list_append && oparg == 1 && pop) { - specialize(instr, CALL_LIST_APPEND); - return 0; + if ((PyObject *)descr == list_append && oparg == 1) { + assert(self_or_null != NULL); + if (PyList_CheckExact(self_or_null)) { + specialize(instr, CALL_LIST_APPEND); + return 0; + } } specialize(instr, CALL_METHOD_DESCRIPTOR_O); return 0; @@ -2074,10 +1741,14 @@ specialize_py_call(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs, PyCodeObject *code = (PyCodeObject *)func->func_code; int kind = function_kind(code); /* Don't specialize if PEP 523 is active */ - if (_PyInterpreterState_GET()->eval_frame) { + if (!_PyInterpreterState_IsSpecializationEnabled(_PyInterpreterState_GET())) { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_PEP_523); return -1; } + if (func->vectorcall != _PyFunction_Vectorcall) { + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_VECTORCALL); + return -1; + } int argcount = -1; if (kind == SPEC_FAIL_CODE_NOT_OPTIMIZED) { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CODE_NOT_OPTIMIZED); @@ -2113,10 +1784,14 @@ specialize_py_call_kw(PyFunctionObject *func, _Py_CODEUNIT *instr, int nargs, PyCodeObject *code = (PyCodeObject *)func->func_code; int kind = function_kind(code); /* Don't specialize if PEP 523 is active */ - if (_PyInterpreterState_GET()->eval_frame) { + if (!_PyInterpreterState_IsSpecializationEnabled(_PyInterpreterState_GET())) { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_PEP_523); return -1; } + if (func->vectorcall != _PyFunction_Vectorcall) { + SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CALL_VECTORCALL); + return -1; + } if (kind == SPEC_FAIL_CODE_NOT_OPTIMIZED) { SPECIALIZATION_FAIL(CALL, SPEC_FAIL_CODE_NOT_OPTIMIZED); return -1; @@ -2178,11 +1853,11 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs) } Py_NO_INLINE void -_Py_Specialize_Call(_PyStackRef callable_st, _Py_CODEUNIT *instr, int nargs) +_Py_Specialize_Call(_PyStackRef callable_st, _PyStackRef self_or_null_st, _Py_CODEUNIT *instr, int nargs) { PyObject *callable = PyStackRef_AsPyObjectBorrow(callable_st); - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[CALL] == INLINE_CACHE_ENTRIES_CALL); assert(_Py_OPCODE(*instr) != INSTRUMENTED_CALL); int fail; @@ -2196,7 +1871,9 @@ _Py_Specialize_Call(_PyStackRef callable_st, _Py_CODEUNIT *instr, int nargs) fail = specialize_class_call(callable, instr, nargs); } else if (Py_IS_TYPE(callable, &PyMethodDescr_Type)) { - fail = specialize_method_descriptor((PyMethodDescrObject *)callable, instr, nargs); + PyObject *self_or_null = PyStackRef_AsPyObjectBorrow(self_or_null_st); + fail = specialize_method_descriptor((PyMethodDescrObject *)callable, + self_or_null, instr, nargs); } else if (PyMethod_Check(callable)) { PyObject *func = ((PyMethodObject *)callable)->im_func; @@ -2222,7 +1899,7 @@ _Py_Specialize_CallKw(_PyStackRef callable_st, _Py_CODEUNIT *instr, int nargs) { PyObject *callable = PyStackRef_AsPyObjectBorrow(callable_st); - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[CALL_KW] == INLINE_CACHE_ENTRIES_CALL_KW); assert(_Py_OPCODE(*instr) != INSTRUMENTED_CALL_KW); int fail; @@ -2370,7 +2047,7 @@ binary_op_fail_kind(int oparg, PyObject *lhs, PyObject *rhs) return SPEC_FAIL_WRONG_NUMBER_ARGUMENTS; } - if (_PyInterpreterState_GET()->eval_frame) { + if (!_PyInterpreterState_IsSpecializationEnabled(_PyInterpreterState_GET())) { /* Don't specialize if PEP 523 is active */ Py_DECREF(descriptor); return SPEC_FAIL_OTHER; @@ -2446,6 +2123,24 @@ is_compactlong(PyObject *v) _PyLong_IsCompact((PyLongObject *)v); } +#define SEQ_INT_MULTIPLY_ACTION(NAME, REPEAT, SEQ, COUNT) \ + static PyObject * \ + (NAME)(PyObject *lhs, PyObject *rhs) \ + { \ + Py_ssize_t count = PyLong_AsSsize_t(COUNT); \ + if (count == -1 && PyErr_Occurred()) { \ + return NULL; \ + } \ + return REPEAT(SEQ, count); \ + } +SEQ_INT_MULTIPLY_ACTION(str_int_multiply, _PyUnicode_Repeat, lhs, rhs) +SEQ_INT_MULTIPLY_ACTION(int_str_multiply, _PyUnicode_Repeat, rhs, lhs) +SEQ_INT_MULTIPLY_ACTION(bytes_int_multiply, _PyBytes_Repeat, lhs, rhs) +SEQ_INT_MULTIPLY_ACTION(int_bytes_multiply, _PyBytes_Repeat, rhs, lhs) +SEQ_INT_MULTIPLY_ACTION(tuple_int_multiply, _PyTuple_Repeat, lhs, rhs) +SEQ_INT_MULTIPLY_ACTION(int_tuple_multiply, _PyTuple_Repeat, rhs, lhs) +#undef SEQ_INT_MULTIPLY_ACTION + static int compactlongs_guard(PyObject *lhs, PyObject *rhs) { @@ -2472,7 +2167,7 @@ float_compactlong_guard(PyObject *lhs, PyObject *rhs) { return ( PyFloat_CheckExact(lhs) && - !isnan(PyFloat_AsDouble(lhs)) && + !isnan(PyFloat_AS_DOUBLE(lhs)) && PyLong_CheckExact(rhs) && _PyLong_IsCompact((PyLongObject *)rhs) ); @@ -2482,7 +2177,7 @@ static inline int nonzero_float_compactlong_guard(PyObject *lhs, PyObject *rhs) { return ( - float_compactlong_guard(lhs, rhs) && !PyLong_IsZero(rhs) + float_compactlong_guard(lhs, rhs) && !_PyLong_IsZero((PyLongObject*)rhs) ); } @@ -2490,7 +2185,7 @@ nonzero_float_compactlong_guard(PyObject *lhs, PyObject *rhs) static PyObject * \ (NAME)(PyObject *lhs, PyObject *rhs) \ { \ - double lhs_val = PyFloat_AsDouble(lhs); \ + double lhs_val = PyFloat_AS_DOUBLE(lhs); \ Py_ssize_t rhs_val = _PyLong_CompactValue((PyLongObject *)rhs); \ return PyFloat_FromDouble(lhs_val OP rhs_val); \ } @@ -2509,7 +2204,7 @@ compactlong_float_guard(PyObject *lhs, PyObject *rhs) PyLong_CheckExact(lhs) && _PyLong_IsCompact((PyLongObject *)lhs) && PyFloat_CheckExact(rhs) && - !isnan(PyFloat_AsDouble(rhs)) + !isnan(PyFloat_AS_DOUBLE(rhs)) ); } @@ -2517,7 +2212,7 @@ static inline int nonzero_compactlong_float_guard(PyObject *lhs, PyObject *rhs) { return ( - compactlong_float_guard(lhs, rhs) && PyFloat_AsDouble(rhs) != 0.0 + compactlong_float_guard(lhs, rhs) && PyFloat_AS_DOUBLE(rhs) != 0.0 ); } @@ -2525,7 +2220,7 @@ nonzero_compactlong_float_guard(PyObject *lhs, PyObject *rhs) static PyObject * \ (NAME)(PyObject *lhs, PyObject *rhs) \ { \ - double rhs_val = PyFloat_AsDouble(rhs); \ + double rhs_val = PyFloat_AS_DOUBLE(rhs); \ Py_ssize_t lhs_val = _PyLong_CompactValue((PyLongObject *)lhs); \ return PyFloat_FromDouble(lhs_val OP rhs_val); \ } @@ -2536,25 +2231,71 @@ LONG_FLOAT_ACTION(compactlong_float_true_div, /) #undef LONG_FLOAT_ACTION static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = { - /* long-long arithmetic */ - {NB_OR, compactlongs_guard, compactlongs_or}, - {NB_AND, compactlongs_guard, compactlongs_and}, - {NB_XOR, compactlongs_guard, compactlongs_xor}, - {NB_INPLACE_OR, compactlongs_guard, compactlongs_or}, - {NB_INPLACE_AND, compactlongs_guard, compactlongs_and}, - {NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor}, - - /* float-long arithemetic */ - {NB_ADD, float_compactlong_guard, float_compactlong_add}, - {NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract}, - {NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div}, - {NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply}, - - /* float-float arithmetic */ - {NB_ADD, compactlong_float_guard, compactlong_float_add}, - {NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract}, - {NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div}, - {NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply}, + /* long-long arithmetic: guards also check _PyLong_IsCompact, so + type alone is not sufficient to eliminate the guard. */ + {NB_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1, NULL, NULL}, + {NB_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1, NULL, NULL}, + {NB_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1, NULL, NULL}, + {NB_INPLACE_OR, compactlongs_guard, compactlongs_or, &PyLong_Type, 1, NULL, NULL}, + {NB_INPLACE_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1, NULL, NULL}, + {NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1, NULL, NULL}, + + /* float-long arithmetic: guards also check NaN and compactness. */ + {NB_ADD, float_compactlong_guard, float_compactlong_add, &PyFloat_Type, 1, NULL, NULL}, + {NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract, &PyFloat_Type, 1, NULL, NULL}, + {NB_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div, &PyFloat_Type, 1, NULL, NULL}, + {NB_MULTIPLY, float_compactlong_guard, float_compactlong_multiply, &PyFloat_Type, 1, NULL, NULL}, + {NB_INPLACE_ADD, float_compactlong_guard, float_compactlong_add, &PyFloat_Type, 1, NULL, NULL}, + {NB_INPLACE_SUBTRACT, float_compactlong_guard, float_compactlong_subtract, &PyFloat_Type, 1, NULL, NULL}, + {NB_INPLACE_TRUE_DIVIDE, nonzero_float_compactlong_guard, float_compactlong_true_div, &PyFloat_Type, 1, NULL, NULL}, + {NB_INPLACE_MULTIPLY, float_compactlong_guard, float_compactlong_multiply, &PyFloat_Type, 1, NULL, NULL}, + + /* long-float arithmetic: guards also check NaN and compactness. */ + {NB_ADD, compactlong_float_guard, compactlong_float_add, &PyFloat_Type, 1, NULL, NULL}, + {NB_SUBTRACT, compactlong_float_guard, compactlong_float_subtract, &PyFloat_Type, 1, NULL, NULL}, + {NB_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div, &PyFloat_Type, 1, NULL, NULL}, + {NB_MULTIPLY, compactlong_float_guard, compactlong_float_multiply, &PyFloat_Type, 1, NULL, NULL}, + {NB_INPLACE_ADD, compactlong_float_guard, compactlong_float_add, &PyFloat_Type, 1, NULL, NULL}, + {NB_INPLACE_SUBTRACT, compactlong_float_guard, compactlong_float_subtract, &PyFloat_Type, 1, NULL, NULL}, + {NB_INPLACE_TRUE_DIVIDE, nonzero_compactlong_float_guard, compactlong_float_true_div, &PyFloat_Type, 1, NULL, NULL}, + {NB_INPLACE_MULTIPLY, compactlong_float_guard, compactlong_float_multiply, &PyFloat_Type, 1, NULL, NULL}, + + /* list-list concatenation: _PyList_Concat always allocates a new list */ + {NB_ADD, NULL, _PyList_Concat, &PyList_Type, 1, &PyList_Type, &PyList_Type}, + /* tuple-tuple concatenation: _PyTuple_Concat has a zero-length shortcut + that can return one of the operands, so the result is not guaranteed + to be a freshly allocated object. */ + {NB_ADD, NULL, _PyTuple_Concat, &PyTuple_Type, 0, &PyTuple_Type, &PyTuple_Type}, + + /* str * int / int * str: call _PyUnicode_Repeat directly. + _PyUnicode_Repeat returns the original when n == 1. */ + {NB_MULTIPLY, NULL, str_int_multiply, &PyUnicode_Type, 0, &PyUnicode_Type, &PyLong_Type}, + {NB_MULTIPLY, NULL, int_str_multiply, &PyUnicode_Type, 0, &PyLong_Type, &PyUnicode_Type}, + {NB_INPLACE_MULTIPLY, NULL, str_int_multiply, &PyUnicode_Type, 0, &PyUnicode_Type, &PyLong_Type}, + {NB_INPLACE_MULTIPLY, NULL, int_str_multiply, &PyUnicode_Type, 0, &PyLong_Type, &PyUnicode_Type}, + + /* bytes + bytes: bytes_concat may return an operand when one side + is empty, so result is not always unique. */ + {NB_ADD, NULL, _PyBytes_Concat, &PyBytes_Type, 0, &PyBytes_Type, &PyBytes_Type}, + {NB_INPLACE_ADD, NULL, _PyBytes_Concat, &PyBytes_Type, 0, &PyBytes_Type, &PyBytes_Type}, + + /* bytes * int / int * bytes: call _PyBytes_Repeat directly. + _PyBytes_Repeat returns the original when n == 1. */ + {NB_MULTIPLY, NULL, bytes_int_multiply, &PyBytes_Type, 0, &PyBytes_Type, &PyLong_Type}, + {NB_MULTIPLY, NULL, int_bytes_multiply, &PyBytes_Type, 0, &PyLong_Type, &PyBytes_Type}, + {NB_INPLACE_MULTIPLY, NULL, bytes_int_multiply, &PyBytes_Type, 0, &PyBytes_Type, &PyLong_Type}, + {NB_INPLACE_MULTIPLY, NULL, int_bytes_multiply, &PyBytes_Type, 0, &PyLong_Type, &PyBytes_Type}, + + /* tuple * int / int * tuple: call _PyTuple_Repeat directly. + _PyTuple_Repeat returns the original when n == 1. */ + {NB_MULTIPLY, NULL, tuple_int_multiply, &PyTuple_Type, 0, &PyTuple_Type, &PyLong_Type}, + {NB_MULTIPLY, NULL, int_tuple_multiply, &PyTuple_Type, 0, &PyLong_Type, &PyTuple_Type}, + {NB_INPLACE_MULTIPLY, NULL, tuple_int_multiply, &PyTuple_Type, 0, &PyTuple_Type, &PyLong_Type}, + {NB_INPLACE_MULTIPLY, NULL, int_tuple_multiply, &PyTuple_Type, 0, &PyLong_Type, &PyTuple_Type}, + + /* dict | dict */ + {NB_OR, NULL, _PyDict_Or, &PyDict_Type, 1, &PyDict_Type, &PyDict_Type}, + {NB_INPLACE_OR, NULL, _PyDict_IOr, &PyDict_Type, 0, &PyDict_Type, &PyDict_Type}, }; static int @@ -2564,7 +2305,13 @@ binary_op_extended_specialization(PyObject *lhs, PyObject *rhs, int oparg, size_t n = sizeof(binaryop_extend_descrs)/sizeof(_PyBinaryOpSpecializationDescr); for (size_t i = 0; i < n; i++) { _PyBinaryOpSpecializationDescr *d = &binaryop_extend_descrs[i]; - if (d->oparg == oparg && d->guard(lhs, rhs)) { + if (d->oparg != oparg) { + continue; + } + int match = (d->guard != NULL) + ? d->guard(lhs, rhs) + : (Py_TYPE(lhs) == d->lhs_type && Py_TYPE(rhs) == d->rhs_type); + if (match) { *descr = d; return 1; } @@ -2578,7 +2325,7 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in { PyObject *lhs = PyStackRef_AsPyObjectBorrow(lhs_st); PyObject *rhs = PyStackRef_AsPyObjectBorrow(rhs_st); - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[BINARY_OP] == INLINE_CACHE_ENTRIES_BINARY_OP); _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(instr + 1); @@ -2602,7 +2349,7 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in specialize(instr, BINARY_OP_ADD_UNICODE); return; } - if (PyLong_CheckExact(lhs)) { + if (_PyLong_CheckExactAndCompact(lhs) && _PyLong_CheckExactAndCompact(rhs)) { specialize(instr, BINARY_OP_ADD_INT); return; } @@ -2616,7 +2363,7 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) { break; } - if (PyLong_CheckExact(lhs)) { + if (_PyLong_CheckExactAndCompact(lhs) && _PyLong_CheckExactAndCompact(rhs)) { specialize(instr, BINARY_OP_MULTIPLY_INT); return; } @@ -2630,7 +2377,7 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) { break; } - if (PyLong_CheckExact(lhs)) { + if (_PyLong_CheckExactAndCompact(lhs) && _PyLong_CheckExactAndCompact(rhs)) { specialize(instr, BINARY_OP_SUBTRACT_INT); return; } @@ -2649,12 +2396,19 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in specialize(instr, BINARY_OP_SUBSCR_TUPLE_INT); return; } - if (PyUnicode_CheckExact(lhs)) { - specialize(instr, BINARY_OP_SUBSCR_STR_INT); - return; + if (PyUnicode_CheckExact(lhs) && _PyLong_IsNonNegativeCompact((PyLongObject*)rhs)) { + if (PyUnicode_IS_COMPACT_ASCII(lhs)) { + specialize(instr, BINARY_OP_SUBSCR_STR_INT); + return; + } else { + specialize(instr, BINARY_OP_SUBSCR_USTR_INT); + return; + } } } - if (PyDict_CheckExact(lhs)) { + if (Py_TYPE(lhs)->tp_as_mapping != NULL && + Py_TYPE(lhs)->tp_as_mapping->mp_subscript == _PyDict_Subscript) + { specialize(instr, BINARY_OP_SUBSCR_DICT); return; } @@ -2674,7 +2428,7 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in PyHeapTypeObject *ht = (PyHeapTypeObject *)container_type; if (kind == SIMPLE_FUNCTION && fcode->co_argcount == 2 && - !_PyInterpreterState_GET()->eval_frame && /* Don't specialize if PEP 523 is active */ + _PyInterpreterState_IsSpecializationEnabled(_PyInterpreterState_GET()) && /* Don't specialize if PEP 523 is active */ _PyType_CacheGetItemForSpecialization(ht, descriptor, (uint32_t)tp_version)) { specialize(instr, BINARY_OP_SUBSCR_GETITEM); @@ -2742,7 +2496,7 @@ _Py_Specialize_CompareOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *i PyObject *rhs = PyStackRef_AsPyObjectBorrow(rhs_st); uint8_t specialized_op; - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[COMPARE_OP] == INLINE_CACHE_ENTRIES_COMPARE_OP); // All of these specializations compute boolean values, so they're all valid // regardless of the fifth-lowest oparg bit. @@ -2802,7 +2556,7 @@ _Py_Specialize_UnpackSequence(_PyStackRef seq_st, _Py_CODEUNIT *instr, int oparg { PyObject *seq = PyStackRef_AsPyObjectBorrow(seq_st); - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[UNPACK_SEQUENCE] == INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE); if (PyTuple_CheckExact(seq)) { @@ -2907,34 +2661,32 @@ int Py_NO_INLINE void _Py_Specialize_ForIter(_PyStackRef iter, _PyStackRef null_or_index, _Py_CODEUNIT *instr, int oparg) { - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[FOR_ITER] == INLINE_CACHE_ENTRIES_FOR_ITER); PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter); PyTypeObject *tp = Py_TYPE(iter_o); if (PyStackRef_IsNull(null_or_index)) { + if (tp == &PyRangeIter_Type) { #ifdef Py_GIL_DISABLED - // Only specialize for uniquely referenced iterators, so that we know - // they're only referenced by this one thread. This is more limiting - // than we need (even `it = iter(mylist); for item in it:` won't get - // specialized) but we don't have a way to check whether we're the only - // _thread_ who has access to the object. - if (!_PyObject_IsUniquelyReferenced(iter_o)) { - goto failure; - } + // Only specialize for uniquely referenced iterators, so that we know + // they're only referenced by this one thread. This is more limiting + // than we need (even `it = iter(mylist); for item in it:` won't get + // specialized) but we don't have a way to check whether we're the only + // _thread_ who has access to the object. + if (!_PyObject_IsUniquelyReferenced(iter_o)) { + goto failure; + } #endif - if (tp == &PyRangeIter_Type) { specialize(instr, FOR_ITER_RANGE); return; } else if (tp == &PyGen_Type && oparg <= SHRT_MAX) { - // Generators are very much not thread-safe, so don't worry about - // the specialization not being thread-safe. assert(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == END_FOR || instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == INSTRUMENTED_END_FOR ); /* Don't specialize if PEP 523 is active */ - if (_PyInterpreterState_GET()->eval_frame) { + if (!_PyInterpreterState_IsSpecializationEnabled(_PyInterpreterState_GET())) { goto failure; } specialize(instr, FOR_ITER_GEN); @@ -2942,20 +2694,24 @@ _Py_Specialize_ForIter(_PyStackRef iter, _PyStackRef null_or_index, _Py_CODEUNIT } } else { - if (tp == &PyList_Type) { -#ifdef Py_GIL_DISABLED - // Only specialize for lists owned by this thread or shared - if (!_Py_IsOwnedByCurrentThread(iter_o) && !_PyObject_GC_IS_SHARED(iter_o)) { - goto failure; + if (tp->_tp_iteritem != NULL) { + if (tp == &PyList_Type) { + #ifdef Py_GIL_DISABLED + // Only specialize for lists owned by this thread or shared + if (!_Py_IsOwnedByCurrentThread(iter_o) && !_PyObject_GC_IS_SHARED(iter_o)) { + goto failure; + } + #endif + specialize(instr, FOR_ITER_LIST); + return; + } + else if (tp == &PyTuple_Type) { + specialize(instr, FOR_ITER_TUPLE); + return; } -#endif - specialize(instr, FOR_ITER_LIST); - return; - } - else if (tp == &PyTuple_Type) { - specialize(instr, FOR_ITER_TUPLE); - return; } + specialize(instr, FOR_ITER_VIRTUAL); + return; } failure: SPECIALIZATION_FAIL(FOR_ITER, @@ -2968,24 +2724,54 @@ _Py_Specialize_Send(_PyStackRef receiver_st, _Py_CODEUNIT *instr) { PyObject *receiver = PyStackRef_AsPyObjectBorrow(receiver_st); - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[SEND] == INLINE_CACHE_ENTRIES_SEND); PyTypeObject *tp = Py_TYPE(receiver); if (tp == &PyGen_Type || tp == &PyCoro_Type) { /* Don't specialize if PEP 523 is active */ - if (_PyInterpreterState_GET()->eval_frame) { + if (!_PyInterpreterState_IsSpecializationEnabled(_PyInterpreterState_GET())) { SPECIALIZATION_FAIL(SEND, SPEC_FAIL_OTHER); goto failure; } specialize(instr, SEND_GEN); return; } + if (tp->_tp_iteritem != NULL) { + specialize(instr, SEND_VIRTUAL); + return; + } + if (tp == &_PyAsyncGenASend_Type) { + specialize(instr, SEND_ASYNC_GEN); + return; + } SPECIALIZATION_FAIL(SEND, _PySpecialization_ClassifyIterator(receiver)); failure: unspecialize(instr); } +Py_NO_INLINE void +_Py_Specialize_CallFunctionEx(_PyStackRef func_st, _Py_CODEUNIT *instr) +{ + PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); + + assert(ENABLE_SPECIALIZATION); + assert(_PyOpcode_Caches[CALL_FUNCTION_EX] == INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX); + + if (Py_TYPE(func) == &PyFunction_Type && + ((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) { + if (!_PyInterpreterState_IsSpecializationEnabled(_PyInterpreterState_GET())) { + goto failure; + } + specialize(instr, CALL_EX_PY); + return; + } + specialize(instr, CALL_EX_NON_PY_GENERAL); + return; +failure: + unspecialize(instr); +} + #ifdef Py_STATS static int to_bool_fail_kind(PyObject *value) @@ -3036,7 +2822,7 @@ check_type_always_true(PyTypeObject *ty) Py_NO_INLINE void _Py_Specialize_ToBool(_PyStackRef value_o, _Py_CODEUNIT *instr) { - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[TO_BOOL] == INLINE_CACHE_ENTRIES_TO_BOOL); _PyToBoolCache *cache = (_PyToBoolCache *)(instr + 1); PyObject *value = PyStackRef_AsPyObjectBorrow(value_o); @@ -3112,9 +2898,9 @@ _Py_Specialize_ContainsOp(_PyStackRef value_st, _Py_CODEUNIT *instr) { PyObject *value = PyStackRef_AsPyObjectBorrow(value_st); - assert(ENABLE_SPECIALIZATION_FT); + assert(ENABLE_SPECIALIZATION); assert(_PyOpcode_Caches[CONTAINS_OP] == INLINE_CACHE_ENTRIES_COMPARE_OP); - if (PyDict_CheckExact(value)) { + if (PyAnyDict_CheckExact(value)) { specialize(instr, CONTAINS_OP_DICT); return; } @@ -3128,6 +2914,45 @@ _Py_Specialize_ContainsOp(_PyStackRef value_st, _Py_CODEUNIT *instr) return; } +void +_Py_Specialize_GetIter(_PyStackRef iterable, _Py_CODEUNIT *instr) +{ + assert(ENABLE_SPECIALIZATION); + PyTypeObject *tp = PyStackRef_TYPE(iterable); + if (tp->_tp_iteritem != NULL) { + specialize(instr, GET_ITER_VIRTUAL); + return; + } + if (tp->tp_iter == PyObject_SelfIter) { + specialize(instr, GET_ITER_SELF); + return; + } + SPECIALIZATION_FAIL(GET_ITER, + tp == &PyCoro_Type ? SPEC_FAIL_ITER_COROUTINE : SPEC_FAIL_OTHER); + unspecialize(instr); +} + +void +_Py_Specialize_Resume(_Py_CODEUNIT *instr, PyThreadState *tstate, _PyInterpreterFrame *frame) +{ + if (tstate->tracing == 0 && instr->op.code == RESUME) { + if (tstate->interp->jit) { + PyCodeObject *co = (PyCodeObject *)PyStackRef_AsPyObjectBorrow(frame->f_executable); + if (co != NULL && + PyCode_Check(co) && + (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) == 0) { + specialize(instr, RESUME_CHECK_JIT); + set_counter((_Py_BackoffCounter *)instr + 1, initial_resume_backoff_counter(&tstate->interp->opt_config)); + return; + } + } + specialize(instr, RESUME_CHECK); + return; + } + unspecialize(instr); + return; +} + #ifdef Py_STATS void _Py_GatherStats_GetIter(_PyStackRef iterable) @@ -3230,5 +3055,6 @@ const struct _PyCode8 _Py_InitCleanup = { EXIT_INIT_CHECK, 0, RETURN_VALUE, 0, RESUME, RESUME_AT_FUNC_START, + CACHE, 0, /* RESUME's cache */ } }; diff --git a/Python/stackrefs.c b/Python/stackrefs.c index ecc0012ef17b39..0c13cc65510587 100644 --- a/Python/stackrefs.c +++ b/Python/stackrefs.c @@ -19,6 +19,8 @@ typedef struct _table_entry { int linenumber; const char *filename_borrow; int linenumber_borrow; + int borrows; + _PyStackRef borrowed_from; } TableEntry; TableEntry * @@ -34,6 +36,8 @@ make_table_entry(PyObject *obj, const char *filename, int linenumber) result->linenumber = linenumber; result->filename_borrow = NULL; result->linenumber_borrow = 0; + result->borrows = 0; + result->borrowed_from = PyStackRef_NULL; return result; } @@ -47,11 +51,13 @@ _Py_stackref_get_object(_PyStackRef ref) PyInterpreterState *interp = PyInterpreterState_Get(); assert(interp != NULL); if (ref.index >= interp->next_stackref) { - _Py_FatalErrorFormat(__func__, "Garbled stack ref with ID %" PRIu64 "\n", ref.index); + _Py_FatalErrorFormat(__func__, + "Garbled stack ref with ID %" PRIu64 "\n", ref.index); } TableEntry *entry = _Py_hashtable_get(interp->open_stackrefs_table, (void *)ref.index); if (entry == NULL) { - _Py_FatalErrorFormat(__func__, "Accessing closed stack ref with ID %" PRIu64 "\n", ref.index); + _Py_FatalErrorFormat(__func__, + "Accessing closed stack ref with ID %" PRIu64 "\n", ref.index); } return entry->obj; } @@ -68,13 +74,16 @@ _Py_stackref_close(_PyStackRef ref, const char *filename, int linenumber) assert(!PyStackRef_IsError(ref)); PyInterpreterState *interp = PyInterpreterState_Get(); if (ref.index >= interp->next_stackref) { - _Py_FatalErrorFormat(__func__, "Invalid StackRef with ID %" PRIu64 " at %s:%d\n", (void *)ref.index, filename, linenumber); - + _Py_FatalErrorFormat(__func__, + "Invalid StackRef with ID %" PRIu64 " at %s:%d\n", + ref.index, filename, linenumber); } PyObject *obj; if (ref.index < INITIAL_STACKREF_INDEX) { if (ref.index == 0) { - _Py_FatalErrorFormat(__func__, "Passing NULL to PyStackRef_CLOSE at %s:%d\n", filename, linenumber); + _Py_FatalErrorFormat(__func__, + "Passing NULL to _Py_stackref_close at %s:%d\n", + filename, linenumber); } // Pre-allocated reference to None, False or True -- Do not clear TableEntry *entry = _Py_hashtable_get(interp->open_stackrefs_table, (void *)ref.index); @@ -88,10 +97,27 @@ _Py_stackref_close(_PyStackRef ref, const char *filename, int linenumber) if (entry != NULL) { _Py_FatalErrorFormat(__func__, "Double close of ref ID %" PRIu64 " at %s:%d. Referred to instance of %s at %p. Closed at %s:%d\n", - (void *)ref.index, filename, linenumber, entry->classname, entry->obj, entry->filename, entry->linenumber); + ref.index, filename, linenumber, entry->classname, entry->obj, entry->filename, entry->linenumber); } #endif - _Py_FatalErrorFormat(__func__, "Invalid StackRef with ID %" PRIu64 "\n", (void *)ref.index); + _Py_FatalErrorFormat(__func__, + "Invalid StackRef with ID %" PRIu64 " at %s:%d\n", + ref.index, filename, linenumber); + } + if (!PyStackRef_IsNull(entry->borrowed_from)) { + _PyStackRef borrowed_from = entry->borrowed_from; + TableEntry *entry_borrowed = _Py_hashtable_get(interp->open_stackrefs_table, (void *)borrowed_from.index); + if (entry_borrowed == NULL) { + _Py_FatalErrorFormat(__func__, + "Invalid borrowed StackRef with ID %" PRIu64 " at %s:%d\n", + borrowed_from.index, filename, linenumber); + } + entry_borrowed->borrows--; + } + if (entry->borrows > 0) { + _Py_FatalErrorFormat(__func__, + "StackRef with ID %" PRIu64 " closed with %d borrowed refs at %s:%d. Opened at %s:%d\n", + ref.index, entry->borrows, filename, linenumber, entry->filename, entry->linenumber); } obj = entry->obj; free(entry); @@ -109,18 +135,19 @@ _Py_stackref_close(_PyStackRef ref, const char *filename, int linenumber) } _PyStackRef -_Py_stackref_create(PyObject *obj, const char *filename, int linenumber) +_Py_stackref_create(PyObject *obj, uint16_t flags, const char *filename, int linenumber) { if (obj == NULL) { Py_FatalError("Cannot create a stackref for NULL"); } PyInterpreterState *interp = PyInterpreterState_Get(); uint64_t new_id = interp->next_stackref; - interp->next_stackref = new_id + 2; + interp->next_stackref = new_id + (1 << Py_TAGGED_SHIFT); TableEntry *entry = make_table_entry(obj, filename, linenumber); if (entry == NULL) { Py_FatalError("No memory left for stackref debug table"); } + new_id |= flags; if (_Py_hashtable_set(interp->open_stackrefs_table, (void *)new_id, entry) < 0) { Py_FatalError("No memory left for stackref debug table"); } @@ -142,15 +169,62 @@ _Py_stackref_record_borrow(_PyStackRef ref, const char *filename, int linenumber if (entry != NULL) { _Py_FatalErrorFormat(__func__, "Borrow of closed ref ID %" PRIu64 " at %s:%d. Referred to instance of %s at %p. Closed at %s:%d\n", - (void *)ref.index, filename, linenumber, entry->classname, entry->obj, entry->filename, entry->linenumber); + ref.index, filename, linenumber, entry->classname, entry->obj, entry->filename, entry->linenumber); } #endif - _Py_FatalErrorFormat(__func__, "Invalid StackRef with ID %" PRIu64 " at %s:%d\n", (void *)ref.index, filename, linenumber); + _Py_FatalErrorFormat(__func__, + "Invalid StackRef with ID %" PRIu64 " at %s:%d\n", + ref.index, filename, linenumber); } entry->filename_borrow = filename; entry->linenumber_borrow = linenumber; } +_PyStackRef +_Py_stackref_get_borrowed_from(_PyStackRef ref, const char *filename, int linenumber) +{ + assert(!PyStackRef_IsError(ref)); + PyInterpreterState *interp = PyInterpreterState_Get(); + + TableEntry *entry = _Py_hashtable_get(interp->open_stackrefs_table, (void *)ref.index); + if (entry == NULL) { + _Py_FatalErrorFormat(__func__, + "Invalid StackRef with ID %" PRIu64 " at %s:%d\n", + ref.index, filename, linenumber); + } + + return entry->borrowed_from; +} + +// This function should be used no more than once per ref. +void +_Py_stackref_set_borrowed_from(_PyStackRef ref, _PyStackRef borrowed_from, const char *filename, int linenumber) +{ + assert(!PyStackRef_IsError(ref)); + PyInterpreterState *interp = PyInterpreterState_Get(); + + TableEntry *entry = _Py_hashtable_get(interp->open_stackrefs_table, (void *)ref.index); + if (entry == NULL) { + _Py_FatalErrorFormat(__func__, + "Invalid StackRef (ref) with ID %" PRIu64 " at %s:%d\n", + ref.index, filename, linenumber); + } + + assert(PyStackRef_IsNull(entry->borrowed_from)); + if (PyStackRef_IsNull(borrowed_from)) { + return; + } + + TableEntry *entry_borrowed = _Py_hashtable_get(interp->open_stackrefs_table, (void *)borrowed_from.index); + if (entry_borrowed == NULL) { + _Py_FatalErrorFormat(__func__, + "Invalid StackRef (borrowed_from) with ID %" PRIu64 " at %s:%d\n", + borrowed_from.index, filename, linenumber); + } + + entry->borrowed_from = borrowed_from; + entry_borrowed->borrows++; +} void _Py_stackref_associate(PyInterpreterState *interp, PyObject *obj, _PyStackRef ref) @@ -194,16 +268,10 @@ _Py_stackref_report_leaks(PyInterpreterState *interp) } } -void -_PyStackRef_CLOSE_SPECIALIZED(_PyStackRef ref, destructor destruct, const char *filename, int linenumber) -{ - PyObject *obj = _Py_stackref_close(ref, filename, linenumber); - _Py_DECREF_SPECIALIZED(obj, destruct); -} - _PyStackRef PyStackRef_TagInt(intptr_t i) { - return (_PyStackRef){ .index = (i << 1) + 1 }; + assert(Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, (i << Py_TAGGED_SHIFT), Py_TAGGED_SHIFT) == i); + return (_PyStackRef){ .index = (i << Py_TAGGED_SHIFT) | Py_INT_TAG }; } intptr_t @@ -211,7 +279,7 @@ PyStackRef_UntagInt(_PyStackRef i) { assert(PyStackRef_IsTaggedInt(i)); intptr_t val = (intptr_t)i.index; - return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, val, 1); + return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, val, Py_TAGGED_SHIFT); } bool @@ -223,8 +291,9 @@ PyStackRef_IsNullOrInt(_PyStackRef ref) _PyStackRef PyStackRef_IncrementTaggedIntNoOverflow(_PyStackRef ref) { - assert(ref.index <= INT_MAX - 2); // No overflow - return (_PyStackRef){ .index = ref.index + 2 }; + assert(PyStackRef_IsTaggedInt(ref)); + assert((ref.index & (~Py_TAG_BITS)) != (INTPTR_MAX & (~Py_TAG_BITS))); // Isn't about to overflow + return (_PyStackRef){ .index = ref.index + (1 << Py_TAGGED_SHIFT) }; } diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index 63e4599c31efc3..8937e666bbbdd5 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -51,6 +51,7 @@ static const char* _Py_stdlib_module_names[] = { "_lsprof", "_lzma", "_markupbase", +"_math_integer", "_md5", "_multibytecodec", "_multiprocessing", @@ -215,6 +216,7 @@ static const char* _Py_stdlib_module_names[] = { "posixpath", "pprint", "profile", +"profiling", "pstats", "pty", "pwd", diff --git a/Python/structmember.c b/Python/structmember.c index 574acf296157f3..adea8216b8796b 100644 --- a/Python/structmember.c +++ b/Python/structmember.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_descrobject.h" // _PyMember_GetOffset() #include "pycore_long.h" // _PyLong_IsNegative() #include "pycore_object.h" // _Py_TryIncrefCompare(), FT_ATOMIC_*() #include "pycore_critical_section.h" @@ -20,6 +21,17 @@ member_get_object(const char *addr, const char *obj_addr, PyMemberDef *l) return v; } +void * +_PyMember_GetOffset(PyObject *obj, PyMemberDef *mp) +{ + unsigned char *addr = (unsigned char *)obj + mp->offset; + if (mp->flags & _Py_AFTER_ITEMS) { + PyTypeObject *type = Py_TYPE(obj); + addr += _Py_SIZE_ROUND_UP(Py_SIZE(obj) * type->tp_itemsize, SIZEOF_VOID_P); + } + return addr; +} + PyObject * PyMember_GetOne(const char *obj_addr, PyMemberDef *l) { @@ -31,7 +43,7 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) return NULL; } - const char* addr = obj_addr + l->offset; + const void *addr = _PyMember_GetOffset((PyObject *)obj_addr, l); switch (l->type) { case Py_T_BOOL: v = PyBool_FromLong(FT_ATOMIC_LOAD_CHAR_RELAXED(*(char*)addr)); @@ -80,7 +92,7 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) v = PyUnicode_FromString((char*)addr); break; case Py_T_CHAR: { - char char_val = FT_ATOMIC_LOAD_CHAR_RELAXED(*addr); + char char_val = FT_ATOMIC_LOAD_CHAR_RELAXED(*(char*)addr); v = PyUnicode_FromStringAndSize(&char_val, 1); break; } @@ -151,29 +163,18 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) return -1; } -#ifdef Py_GIL_DISABLED - PyObject *obj = (PyObject *) addr; -#endif - addr += l->offset; + PyObject *obj = (PyObject *)addr; + addr = _PyMember_GetOffset(obj, l); if ((l->flags & Py_READONLY)) { PyErr_SetString(PyExc_AttributeError, "readonly attribute"); return -1; } - if (v == NULL) { - if (l->type == Py_T_OBJECT_EX) { - /* Check if the attribute is set. */ - if (*(PyObject **)addr == NULL) { - PyErr_SetString(PyExc_AttributeError, l->name); - return -1; - } - } - else if (l->type != _Py_T_OBJECT) { - PyErr_SetString(PyExc_TypeError, - "can't delete numeric/char attribute"); - return -1; - } + if (v == NULL && l->type != Py_T_OBJECT_EX && l->type != _Py_T_OBJECT) { + PyErr_SetString(PyExc_TypeError, + "can't delete numeric/char attribute"); + return -1; } switch (l->type) { case Py_T_BOOL:{ @@ -324,6 +325,15 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) oldv = *(PyObject **)addr; FT_ATOMIC_STORE_PTR_RELEASE(*(PyObject **)addr, Py_XNewRef(v)); Py_END_CRITICAL_SECTION(); + if (v == NULL && oldv == NULL && l->type == Py_T_OBJECT_EX) { + // Raise an exception when attempting to delete an already deleted + // attribute. + // Differently from Py_T_OBJECT_EX, _Py_T_OBJECT does not raise an + // exception here (PyMember_GetOne will return Py_None instead of + // NULL). + PyErr_SetString(PyExc_AttributeError, l->name); + return -1; + } Py_XDECREF(oldv); break; case Py_T_CHAR: { diff --git a/Python/symtable.c b/Python/symtable.c index bcd7365f8e1f14..00ac510fe76b97 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -108,6 +108,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block, ste->ste_id = k; /* ste owns reference to k */ ste->ste_name = Py_NewRef(name); + ste->ste_function_name = NULL; ste->ste_symbols = NULL; ste->ste_varnames = NULL; @@ -141,6 +142,7 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block, ste->ste_needs_classdict = 0; ste->ste_has_conditional_annotations = 0; ste->ste_in_conditional_block = 0; + ste->ste_in_try_block = 0; ste->ste_in_unevaluated_annotation = 0; ste->ste_annotation_block = NULL; @@ -185,6 +187,7 @@ ste_dealloc(PyObject *op) ste->ste_table = NULL; Py_XDECREF(ste->ste_id); Py_XDECREF(ste->ste_name); + Py_XDECREF(ste->ste_function_name); Py_XDECREF(ste->ste_symbols); Py_XDECREF(ste->ste_varnames); Py_XDECREF(ste->ste_children); @@ -806,6 +809,8 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, PyObject *k, *v; Py_ssize_t pos = 0; int remove_dunder_class = 0; + int remove_dunder_classdict = 0; + int remove_dunder_cond_annotations = 0; while (PyDict_Next(comp->ste_symbols, &pos, &k, &v)) { // skip comprehension parameter @@ -828,15 +833,32 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, if (existing == NULL && PyErr_Occurred()) { return 0; } - // __class__ is never allowed to be free through a class scope (see - // drop_class_free) + // __class__, __classdict__ and __conditional_annotations__ are + // not allowed to be free through a class scope (see + // drop_class_free) unless children scopes need it if (scope == FREE && ste->ste_type == ClassBlock && - _PyUnicode_EqualToASCIIString(k, "__class__")) { + (_PyUnicode_EqualToASCIIString(k, "__class__") || + _PyUnicode_EqualToASCIIString(k, "__classdict__") || + _PyUnicode_EqualToASCIIString(k, "__conditional_annotations__"))) { scope = GLOBAL_IMPLICIT; - if (PySet_Discard(comp_free, k) < 0) { + int child_needs_free = is_free_in_any_child(comp, k); + if (child_needs_free < 0) { return 0; } - remove_dunder_class = 1; + if (!child_needs_free) { + if (PySet_Discard(comp_free, k) < 0) { + return 0; + } + } + if (_PyUnicode_EqualToASCIIString(k, "__class__")) { + remove_dunder_class = 1; + } + else if (_PyUnicode_EqualToASCIIString(k, "__conditional_annotations__")) { + remove_dunder_cond_annotations = 1; + } + else { + remove_dunder_classdict = 1; + } } if (!existing) { // name does not exist in scope, copy from comprehension @@ -876,6 +898,12 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, if (remove_dunder_class && PyDict_DelItemString(comp->ste_symbols, "__class__") < 0) { return 0; } + if (remove_dunder_classdict && PyDict_DelItemString(comp->ste_symbols, "__classdict__") < 0) { + return 0; + } + if (remove_dunder_cond_annotations && PyDict_DelItemString(comp->ste_symbols, "__conditional_annotations__") < 0) { + return 0; + } return 1; } @@ -1685,7 +1713,7 @@ symtable_enter_type_param_block(struct symtable *st, identifier name, return 1; } -/* VISIT, VISIT_SEQ and VIST_SEQ_TAIL take an ASDL type as their second argument. +/* VISIT, VISIT_SEQ and VISIT_SEQ_TAIL take an ASDL type as their second argument. They use the ASDL name to synthesize the name of the C type and the visit function. @@ -1747,6 +1775,13 @@ symtable_enter_type_param_block(struct symtable *st, identifier name, #define LEAVE_CONDITIONAL_BLOCK(ST) \ (ST)->st_cur->ste_in_conditional_block = in_conditional_block; +#define ENTER_TRY_BLOCK(ST) \ + int in_try_block = (ST)->st_cur->ste_in_try_block; \ + (ST)->st_cur->ste_in_try_block = 1; + +#define LEAVE_TRY_BLOCK(ST) \ + (ST)->st_cur->ste_in_try_block = in_try_block; + #define ENTER_RECURSIVE() \ if (Py_EnterRecursiveCall(" during compilation")) { \ return 0; \ @@ -1808,6 +1843,38 @@ check_import_from(struct symtable *st, stmt_ty s) return 1; } +static int +check_lazy_import_context(struct symtable *st, stmt_ty s, + const char* import_type) +{ + // Check if inside try/except block. + if (st->st_cur->ste_in_try_block) { + PyErr_Format(PyExc_SyntaxError, + "lazy %s not allowed inside try/except blocks", + import_type); + SET_ERROR_LOCATION(st->st_filename, LOCATION(s)); + return 0; + } + + // Check if inside function scope. + if (st->st_cur->ste_type == FunctionBlock) { + PyErr_Format(PyExc_SyntaxError, + "lazy %s not allowed inside functions", import_type); + SET_ERROR_LOCATION(st->st_filename, LOCATION(s)); + return 0; + } + + // Check if inside class scope. + if (st->st_cur->ste_type == ClassBlock) { + PyErr_Format(PyExc_SyntaxError, + "lazy %s not allowed inside classes", import_type); + SET_ERROR_LOCATION(st->st_filename, LOCATION(s)); + return 0; + } + + return 1; +} + static bool allows_top_level_await(struct symtable *st) { @@ -2076,19 +2143,23 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) break; case Try_kind: { ENTER_CONDITIONAL_BLOCK(st); + ENTER_TRY_BLOCK(st); VISIT_SEQ(st, stmt, s->v.Try.body); VISIT_SEQ(st, excepthandler, s->v.Try.handlers); VISIT_SEQ(st, stmt, s->v.Try.orelse); VISIT_SEQ(st, stmt, s->v.Try.finalbody); + LEAVE_TRY_BLOCK(st); LEAVE_CONDITIONAL_BLOCK(st); break; } case TryStar_kind: { ENTER_CONDITIONAL_BLOCK(st); + ENTER_TRY_BLOCK(st); VISIT_SEQ(st, stmt, s->v.TryStar.body); VISIT_SEQ(st, excepthandler, s->v.TryStar.handlers); VISIT_SEQ(st, stmt, s->v.TryStar.orelse); VISIT_SEQ(st, stmt, s->v.TryStar.finalbody); + LEAVE_TRY_BLOCK(st); LEAVE_CONDITIONAL_BLOCK(st); break; } @@ -2098,9 +2169,33 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s) VISIT(st, expr, s->v.Assert.msg); break; case Import_kind: + if (s->v.Import.is_lazy) { + if (!check_lazy_import_context(st, s, "import")) { + return 0; + } + } VISIT_SEQ(st, alias, s->v.Import.names); break; case ImportFrom_kind: + if (s->v.ImportFrom.is_lazy) { + if (!check_lazy_import_context(st, s, "from ... import")) { + return 0; + } + + // Check for import * + for (Py_ssize_t i = 0; i < asdl_seq_LEN(s->v.ImportFrom.names); + i++) { + alias_ty alias = (alias_ty)asdl_seq_GET( + s->v.ImportFrom.names, i); + if (alias->name && + _PyUnicode_EqualToASCIIString(alias->name, "*")) { + PyErr_SetString(PyExc_SyntaxError, + "lazy from ... import * is not allowed"); + SET_ERROR_LOCATION(st->st_filename, LOCATION(s)); + return 0; + } + } + } VISIT_SEQ(st, alias, s->v.ImportFrom.names); if (!check_import_from(st, s)) { return 0; @@ -2780,6 +2875,7 @@ symtable_visit_annotation(struct symtable *st, expr_ty annotation, void *key) int future_annotations = st->st_future->ff_features & CO_FUTURE_ANNOTATIONS; if (current_type == ClassBlock && !future_annotations) { st->st_cur->ste_can_see_class_scope = 1; + parent_ste->ste_needs_classdict = 1; if (!symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(annotation))) { return 0; } @@ -2830,6 +2926,7 @@ symtable_visit_annotations(struct symtable *st, stmt_ty o, arguments_ty a, expr_ (void *)a, LOCATION(o))) { return 0; } + Py_XSETREF(st->st_cur->ste_function_name, Py_NewRef(function_ste->ste_name)); if (is_in_class || current_type == ClassBlock) { st->st_cur->ste_can_see_class_scope = 1; if (!symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(o))) { @@ -3137,7 +3234,7 @@ symtable_raise_if_not_coroutine(struct symtable *st, const char *msg, _Py_Source struct symtable * _Py_SymtableStringObjectFlags(const char *str, PyObject *filename, - int start, PyCompilerFlags *flags) + int start, PyCompilerFlags *flags, PyObject *module) { struct symtable *st; mod_ty mod; @@ -3147,7 +3244,7 @@ _Py_SymtableStringObjectFlags(const char *str, PyObject *filename, if (arena == NULL) return NULL; - mod = _PyParser_ASTFromString(str, filename, start, flags, arena); + mod = _PyParser_ASTFromString(str, filename, start, flags, arena, module); if (mod == NULL) { _PyArena_Free(arena); return NULL; @@ -3183,6 +3280,27 @@ _Py_MaybeMangle(PyObject *privateobj, PySTEntryObject *ste, PyObject *name) return _Py_Mangle(privateobj, name); } +int +_Py_IsPrivateName(PyObject *ident) +{ + if (!PyUnicode_Check(ident)) { + return 0; + } + Py_ssize_t nlen = PyUnicode_GET_LENGTH(ident); + if (nlen < 3 || + PyUnicode_READ_CHAR(ident, 0) != '_' || + PyUnicode_READ_CHAR(ident, 1) != '_') + { + return 0; + } + if (PyUnicode_READ_CHAR(ident, nlen-1) == '_' && + PyUnicode_READ_CHAR(ident, nlen-2) == '_') + { + return 0; /* Don't mangle __whatever__ */ + } + return 1; +} + PyObject * _Py_Mangle(PyObject *privateobj, PyObject *ident) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index ae6cf306735939..f7e28086d84fab 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -34,6 +34,7 @@ Data members: #include "pycore_pymem.h" // _PyMem_DefaultRawFree() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_pystats.h" // _Py_PrintSpecializationStats() +#include "pycore_runtime.h" // _PyRuntimeState_Get*() #include "pycore_structseq.h" // _PyStructSequence_InitBuiltinWithFlags() #include "pycore_sysmodule.h" // export _PySys_GetSizeOf() #include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal() @@ -471,7 +472,7 @@ PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData) PySys_AddAuditHook() can be called before Python is initialized. */ _PyRuntimeState *runtime = &_PyRuntime; PyThreadState *tstate; - if (runtime->initialized) { + if (_PyRuntimeState_GetInitialized(runtime)) { tstate = _PyThreadState_GET(); } else { @@ -610,7 +611,7 @@ sys_breakpointhook(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyOb if (last_dot == NULL) { /* The breakpoint is a built-in, e.g. PYTHONBREAKPOINT=int */ - modulepath = PyUnicode_FromString("builtins"); + modulepath = &_Py_ID(builtins); attrname = envar; } else if (last_dot != envar) { @@ -1160,6 +1161,7 @@ sys_settrace(PyObject *module, PyObject *function) } /*[clinic input] +@permit_long_summary sys._settraceallthreads function as arg: object @@ -1173,7 +1175,7 @@ in the library manual. static PyObject * sys__settraceallthreads(PyObject *module, PyObject *arg) -/*[clinic end generated code: output=161cca30207bf3ca input=d4bde1f810d73675]*/ +/*[clinic end generated code: output=161cca30207bf3ca input=e5750f5dc01142eb]*/ { PyObject* argument = NULL; Py_tracefunc func = NULL; @@ -1183,9 +1185,10 @@ sys__settraceallthreads(PyObject *module, PyObject *arg) argument = arg; } - - PyEval_SetTraceAllThreads(func, argument); - + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_PyEval_SetTraceAllThreads(interp, func, argument) < 0) { + return NULL; + } Py_RETURN_NONE; } @@ -1240,6 +1243,7 @@ sys_setprofile(PyObject *module, PyObject *function) } /*[clinic input] +@permit_long_summary sys._setprofileallthreads function as arg: object @@ -1253,7 +1257,7 @@ chapter in the library manual. static PyObject * sys__setprofileallthreads(PyObject *module, PyObject *arg) -/*[clinic end generated code: output=2d61319e27b309fe input=a10589439ba20cee]*/ +/*[clinic end generated code: output=2d61319e27b309fe input=9a3dc3352c63b471]*/ { PyObject* argument = NULL; Py_tracefunc func = NULL; @@ -1263,8 +1267,10 @@ sys__setprofileallthreads(PyObject *module, PyObject *arg) argument = arg; } - PyEval_SetProfileAllThreads(func, argument); - + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_PyEval_SetProfileAllThreads(interp, func, argument) < 0) { + return NULL; + } Py_RETURN_NONE; } @@ -1582,10 +1588,10 @@ get_hash_info(PyThreadState *tstate) } while(0) SET_HASH_INFO_ITEM(PyLong_FromLong(8 * sizeof(Py_hash_t))); - SET_HASH_INFO_ITEM(PyLong_FromSsize_t(_PyHASH_MODULUS)); - SET_HASH_INFO_ITEM(PyLong_FromLong(_PyHASH_INF)); + SET_HASH_INFO_ITEM(PyLong_FromSsize_t(PyHASH_MODULUS)); + SET_HASH_INFO_ITEM(PyLong_FromLong(PyHASH_INF)); SET_HASH_INFO_ITEM(PyLong_FromLong(0)); // This is no longer used - SET_HASH_INFO_ITEM(PyLong_FromLong(_PyHASH_IMAG)); + SET_HASH_INFO_ITEM(PyLong_FromLong(PyHASH_IMAG)); SET_HASH_INFO_ITEM(PyUnicode_FromString(hashfunc->name)); SET_HASH_INFO_ITEM(PyLong_FromLong(hashfunc->hash_bits)); SET_HASH_INFO_ITEM(PyLong_FromLong(hashfunc->seed_bits)); @@ -1757,7 +1763,7 @@ sys_getwindowsversion_impl(PyObject *module) PyObject *realVersion = _sys_getwindowsversion_from_kernel32(); if (!realVersion) { if (!PyErr_ExceptionMatches(PyExc_WindowsError)) { - return NULL; + goto error; } PyErr_Clear(); @@ -1784,36 +1790,6 @@ sys_getwindowsversion_impl(PyObject *module) } #pragma warning(pop) - -/*[clinic input] -sys._enablelegacywindowsfsencoding - -Changes the default filesystem encoding to mbcs:replace. - -This is done for consistency with earlier versions of Python. See PEP -529 for more information. - -This is equivalent to defining the PYTHONLEGACYWINDOWSFSENCODING -environment variable before launching Python. -[clinic start generated code]*/ - -static PyObject * -sys__enablelegacywindowsfsencoding_impl(PyObject *module) -/*[clinic end generated code: output=f5c3855b45e24fe9 input=2bfa931a20704492]*/ -{ - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "sys._enablelegacywindowsfsencoding() is deprecated and will be " - "removed in Python 3.16. Use PYTHONLEGACYWINDOWSFSENCODING " - "instead.", 1)) - { - return NULL; - } - if (_PyUnicode_EnableLegacyWindowsFSEncoding() < 0) { - return NULL; - } - Py_RETURN_NONE; -} - #endif /* MS_WINDOWS */ #ifdef HAVE_DLOPEN @@ -1887,6 +1863,7 @@ sys_mdebug_impl(PyObject *module, int flag) /*[clinic input] +@permit_long_summary sys.get_int_max_str_digits Return the maximum string digits limit for non-binary int<->str conversions. @@ -1894,7 +1871,7 @@ Return the maximum string digits limit for non-binary int<->str conversions. static PyObject * sys_get_int_max_str_digits_impl(PyObject *module) -/*[clinic end generated code: output=0042f5e8ae0e8631 input=61bf9f99bc8b112d]*/ +/*[clinic end generated code: output=0042f5e8ae0e8631 input=77fb74e987ba7ecb]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); return PyLong_FromLong(interp->long_state.max_str_digits); @@ -1902,6 +1879,7 @@ sys_get_int_max_str_digits_impl(PyObject *module) /*[clinic input] +@permit_long_summary sys.set_int_max_str_digits maxdigits: int @@ -1911,7 +1889,7 @@ Set the maximum string digits limit for non-binary int<->str conversions. static PyObject * sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits) -/*[clinic end generated code: output=734d4c2511f2a56d input=d7e3f325db6910c5]*/ +/*[clinic end generated code: output=734d4c2511f2a56d input=d4c0bf50c466d57a]*/ { if (_PySys_SetIntMaxStrDigits(maxdigits) < 0) { return NULL; @@ -2123,6 +2101,7 @@ sys__getframe_impl(PyObject *module, int depth) } /*[clinic input] +@permit_long_summary sys._current_frames Return a dict mapping each thread's thread id to its current stack frame. @@ -2132,12 +2111,13 @@ This function should be used for specialized purposes only. static PyObject * sys__current_frames_impl(PyObject *module) -/*[clinic end generated code: output=d2a41ac0a0a3809a input=2a9049c5f5033691]*/ +/*[clinic end generated code: output=d2a41ac0a0a3809a input=e1ce34f43501e0d6]*/ { return _PyThread_CurrentFrames(); } /*[clinic input] +@permit_long_summary sys._current_exceptions Return a dict mapping each thread's identifier to its current raised exception. @@ -2147,7 +2127,7 @@ This function should be used for specialized purposes only. static PyObject * sys__current_exceptions_impl(PyObject *module) -/*[clinic end generated code: output=2ccfd838c746f0ba input=0e91818fbf2edc1f]*/ +/*[clinic end generated code: output=2ccfd838c746f0ba input=4ba429b6cfcd736d]*/ { return _PyThread_CurrentExceptions(); } @@ -2275,7 +2255,9 @@ static PyObject * sys__stats_on_impl(PyObject *module) /*[clinic end generated code: output=aca53eafcbb4d9fe input=43b5bfe145299e55]*/ { - _Py_StatsOn(); + if (_Py_StatsOn() < 0) { + return NULL; + } Py_RETURN_NONE; } @@ -2312,12 +2294,13 @@ sys._stats_dump -> bool Dump stats to file, and clears the stats. -Return False if no statistics were not dumped because stats gathering was off. +Return False if no statistics were not dumped because stats gathering +was off. [clinic start generated code]*/ static int sys__stats_dump_impl(PyObject *module) -/*[clinic end generated code: output=6e346b4ba0de4489 input=31a489e39418b2a5]*/ +/*[clinic end generated code: output=6e346b4ba0de4489 input=7f3b7758cb59d2ff]*/ { int res = _Py_PrintSpecializationStats(1); _Py_StatsClear(); @@ -2371,14 +2354,14 @@ sys_activate_stack_trampoline_impl(PyObject *module, const char *backend) return NULL; } } - else if (strcmp(backend, "perf_jit") == 0) { - _PyPerf_Callbacks cur_cb; - _PyPerfTrampoline_GetCallbacks(&cur_cb); - if (cur_cb.write_state != _Py_perfmap_jit_callbacks.write_state) { - if (_PyPerfTrampoline_SetCallbacks(&_Py_perfmap_jit_callbacks) < 0 ) { - PyErr_SetString(PyExc_ValueError, "can't activate perf jit trampoline"); - return NULL; - } + } + else if (strcmp(backend, "perf_jit") == 0) { + _PyPerf_Callbacks cur_cb; + _PyPerfTrampoline_GetCallbacks(&cur_cb); + if (cur_cb.write_state != _Py_perfmap_jit_callbacks.write_state) { + if (_PyPerfTrampoline_SetCallbacks(&_Py_perfmap_jit_callbacks) < 0 ) { + PyErr_SetString(PyExc_ValueError, "can't activate perf jit trampoline"); + return NULL; } } } @@ -2461,16 +2444,16 @@ sys.remote_exec Executes a file containing Python code in a given remote Python process. This function returns immediately, and the code will be executed by the -target process's main thread at the next available opportunity, similarly -to how signals are handled. There is no interface to determine when the -code has been executed. The caller is responsible for making sure that -the file still exists whenever the remote process tries to read it and that -it hasn't been overwritten. +target process's main thread at the next available opportunity, +similarly to how signals are handled. There is no interface to +determine when the code has been executed. The caller is responsible +for making sure that the file still exists whenever the remote process +tries to read it and that it hasn't been overwritten. -The remote process must be running a CPython interpreter of the same major -and minor version as the local process. If either the local or remote -interpreter is pre-release (alpha, beta, or release candidate) then the -local and remote interpreters must be the same exact version. +The remote process must be running a CPython interpreter of the same +major and minor version as the local process. If either the local or +remote interpreter is pre-release (alpha, beta, or release candidate) +then the local and remote interpreters must be the same exact version. Args: pid (int): The process ID of the target Python process. @@ -2480,7 +2463,7 @@ local and remote interpreters must be the same exact version. static PyObject * sys_remote_exec_impl(PyObject *module, int pid, PyObject *script) -/*[clinic end generated code: output=7d94c56afe4a52c0 input=39908ca2c5fe1eb0]*/ +/*[clinic end generated code: output=7d94c56afe4a52c0 input=7bd58f8da20cb74c]*/ { PyObject *path; const char *debugger_script_path; @@ -2490,7 +2473,7 @@ sys_remote_exec_impl(PyObject *module, int pid, PyObject *script) } if (PySys_Audit("sys.remote_exec", "iO", pid, script) < 0) { - return NULL; + goto error; } debugger_script_path = PyBytes_AS_STRING(path); @@ -2641,6 +2624,7 @@ sys__baserepl_impl(PyObject *module) Py_RETURN_NONE; } + /*[clinic input] sys._is_gil_enabled -> bool @@ -2696,7 +2680,7 @@ PyAPI_FUNC(int) PyUnstable_PerfMapState_Init(void) { PyAPI_FUNC(int) PyUnstable_WritePerfMapEntry( const void *code_addr, - unsigned int code_size, + size_t code_size, const char *entry_name ) { #ifndef MS_WINDOWS @@ -2707,7 +2691,7 @@ PyAPI_FUNC(int) PyUnstable_WritePerfMapEntry( } } PyThread_acquire_lock(perf_map_state.map_lock, 1); - fprintf(perf_map_state.perf_map, "%" PRIxPTR " %x %s\n", (uintptr_t) code_addr, code_size, entry_name); + fprintf(perf_map_state.perf_map, "%" PRIxPTR " %zx %s\n", (uintptr_t) code_addr, code_size, entry_name); fflush(perf_map_state.perf_map); PyThread_release_lock(perf_map_state.map_lock); #endif @@ -2743,20 +2727,31 @@ PyAPI_FUNC(int) PyUnstable_CopyPerfMapFile(const char* parent_filename) { } char buf[4096]; PyThread_acquire_lock(perf_map_state.map_lock, 1); - int fflush_result = 0, result = 0; + int result = 0; while (1) { size_t bytes_read = fread(buf, 1, sizeof(buf), from); + if (bytes_read == 0) { + if (ferror(from)) { + result = -1; + } + break; + } + size_t bytes_written = fwrite(buf, 1, bytes_read, perf_map_state.perf_map); - fflush_result = fflush(perf_map_state.perf_map); - if (fflush_result != 0 || bytes_read == 0 || bytes_written < bytes_read) { + if (bytes_written < bytes_read) { result = -1; - goto close_and_release; + break; } + + if (fflush(perf_map_state.perf_map) != 0) { + result = -1; + break; + } + if (bytes_read < sizeof(buf) && feof(from)) { - goto close_and_release; + break; } } -close_and_release: fclose(from); PyThread_release_lock(perf_map_state.map_lock); return result; @@ -2764,6 +2759,122 @@ PyAPI_FUNC(int) PyUnstable_CopyPerfMapFile(const char* parent_filename) { return 0; } +/*[clinic input] +sys.set_lazy_imports_filter + + filter: object + +Set the lazy imports filter callback. + +The filter is a callable which disables lazy imports when they +would otherwise be enabled. Returns True if the import is still enabled +or False to disable it. The callable is called with: + +(importing_module_name, resolved_imported_module_name, [fromlist]) + +Pass None to clear the filter. +[clinic start generated code]*/ + +static PyObject * +sys_set_lazy_imports_filter_impl(PyObject *module, PyObject *filter) +/*[clinic end generated code: output=10251d49469c278c input=fd51ed8df6ab54b7]*/ +{ + if (PyImport_SetLazyImportsFilter(filter) < 0) { + return NULL; + } + + Py_RETURN_NONE; +} + +/*[clinic input] +sys.get_lazy_imports_filter + +Get the current lazy imports filter callback. + +Returns the filter callable or None if no filter is set. +[clinic start generated code]*/ + +static PyObject * +sys_get_lazy_imports_filter_impl(PyObject *module) +/*[clinic end generated code: output=3bf73022892165af input=cf1e07cb8e203c94]*/ +{ + PyObject *filter = PyImport_GetLazyImportsFilter(); + if (filter == NULL) { + assert(!PyErr_Occurred()); + Py_RETURN_NONE; + } + return filter; +} + +/*[clinic input] +sys.set_lazy_imports + + mode: object + +Sets the global lazy imports mode. + +The mode parameter must be one of the following strings: +- "all": All top-level imports become potentially lazy +- "normal": Only explicitly marked imports (with 'lazy' keyword) are + lazy + +In addition to the mode, lazy imports can be controlled via the filter +provided to sys.set_lazy_imports_filter + +[clinic start generated code]*/ + +static PyObject * +sys_set_lazy_imports_impl(PyObject *module, PyObject *mode) +/*[clinic end generated code: output=1ff34ba6c4feaf73 input=db3242f0ff6e5dcc]*/ +{ + PyImport_LazyImportsMode lazy_mode; + if (!PyUnicode_Check(mode)) { + PyErr_SetString(PyExc_TypeError, + "mode must be a string: 'normal' or 'all'"); + return NULL; + } + if (PyUnicode_CompareWithASCIIString(mode, "normal") == 0) { + lazy_mode = PyImport_LAZY_NORMAL; + } + else if (PyUnicode_CompareWithASCIIString(mode, "all") == 0) { + lazy_mode = PyImport_LAZY_ALL; + } + else { + PyErr_SetString(PyExc_ValueError, + "mode must be 'normal' or 'all'"); + return NULL; + } + + if (PyImport_SetLazyImportsMode(lazy_mode)) { + return NULL; + } + Py_RETURN_NONE; +} + +/*[clinic input] +sys.get_lazy_imports + +Gets the global lazy imports mode. + +Returns "all" if all top level imports are potentially lazy. +Returns "normal" if only explicitly marked imports are lazy. + +[clinic start generated code]*/ + +static PyObject * +sys_get_lazy_imports_impl(PyObject *module) +/*[clinic end generated code: output=4147dec48c51ae99 input=6f8dd4f2c82893f2]*/ +{ + switch (PyImport_GetLazyImportsMode()) { + case PyImport_LAZY_NORMAL: + return PyUnicode_FromString("normal"); + case PyImport_LAZY_ALL: + return PyUnicode_FromString("all"); + default: + PyErr_SetString(PyExc_RuntimeError, "unknown lazy imports mode"); + return NULL; + } +} static PyMethodDef sys_methods[] = { /* Might as well keep this in alphabetic order */ @@ -2797,7 +2908,6 @@ static PyMethodDef sys_methods[] = { SYS__GETFRAME_METHODDEF SYS__GETFRAMEMODULENAME_METHODDEF SYS_GETWINDOWSVERSION_METHODDEF - SYS__ENABLELEGACYWINDOWSFSENCODING_METHODDEF SYS__IS_IMMORTAL_METHODDEF SYS_INTERN_METHODDEF SYS__IS_INTERNED_METHODDEF @@ -2829,6 +2939,10 @@ static PyMethodDef sys_methods[] = { SYS_UNRAISABLEHOOK_METHODDEF SYS_GET_INT_MAX_STR_DIGITS_METHODDEF SYS_SET_INT_MAX_STR_DIGITS_METHODDEF + SYS_GET_LAZY_IMPORTS_METHODDEF + SYS_SET_LAZY_IMPORTS_METHODDEF + SYS_GET_LAZY_IMPORTS_FILTER_METHODDEF + SYS_SET_LAZY_IMPORTS_FILTER_METHODDEF SYS__BASEREPL_METHODDEF #ifdef Py_STATS SYS__STATS_ON_METHODDEF @@ -3037,7 +3151,7 @@ get_warnoptions(PyThreadState *tstate) return warnoptions; } -void +PyAPI_FUNC(void) PySys_ResetWarnOptions(void) { PyThreadState *tstate = _PyThreadState_GET(); @@ -3260,6 +3374,7 @@ PyDoc_STR( "\n\ Static objects:\n\ \n\ +abi_info -- Python ABI information.\n\ builtin_module_names -- tuple of module names built into this interpreter\n\ copyright -- copyright notice pertaining to this interpreter\n\ exec_prefix -- prefix used to find the machine-specific Python library\n\ @@ -3287,13 +3402,6 @@ winver -- [Windows only] version number of the Python DLL\n\ " ) #endif /* MS_COREDLL */ -#ifdef MS_WINDOWS -/* concatenating string here */ -PyDoc_STR( -"_enablelegacywindowsfsencoding -- [Windows only]\n\ -" -) -#endif PyDoc_STR( "__stdin__ -- the original stdin; don't touch!\n\ __stdout__ -- the original stdout; don't touch!\n\ @@ -3347,11 +3455,13 @@ static PyStructSequence_Field flags_fields[] = { {"dev_mode", "-X dev"}, {"utf8_mode", "-X utf8"}, {"warn_default_encoding", "-X warn_default_encoding"}, - {"safe_path", "-P"}, + {"safe_path", "-P"}, {"int_max_str_digits", "-X int_max_str_digits"}, + // Fields below are only usable by sys.flags attribute name, not index: {"gil", "-X gil"}, {"thread_inherit_context", "-X thread_inherit_context"}, - {"context_aware_warnings", "-X context_aware_warnings"}, + {"context_aware_warnings", "-X context_aware_warnings"}, + {"lazy_imports", "-X lazy_imports"}, {0} }; @@ -3361,7 +3471,9 @@ static PyStructSequence_Desc flags_desc = { "sys.flags", /* name */ flags__doc__, /* doc */ flags_fields, /* fields */ - 18 + 18 /* NB - do not increase beyond 3.13's value of 18. */ + // New sys.flags fields should NOT be tuple addressable per + // https://github.com/python/cpython/issues/122575#issuecomment-2416497086 }; static void @@ -3454,6 +3566,7 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags) #endif SetFlag(config->thread_inherit_context); SetFlag(config->context_aware_warnings); + SetFlag(config->lazy_imports); #undef SetFlagObj #undef SetFlag return 0; @@ -3546,16 +3659,22 @@ make_version_info(PyThreadState *tstate) } /* sys.implementation values */ -#define NAME "cpython" -const char *_PySys_ImplName = NAME; +#ifndef _PY_IMPL_NAME +#define _PY_IMPL_NAME "cpython" +#endif +const char *_PySys_ImplName = _PY_IMPL_NAME; +#ifndef _PY_IMPL_CACHE_TAG #define MAJOR Py_STRINGIFY(PY_MAJOR_VERSION) #define MINOR Py_STRINGIFY(PY_MINOR_VERSION) -#define TAG NAME "-" MAJOR MINOR -const char *_PySys_ImplCacheTag = TAG; -#undef NAME +#define _PY_IMPL_CACHE_TAG _PY_IMPL_NAME "-" MAJOR MINOR +#endif +const char *_PySys_ImplCacheTag = _PY_IMPL_CACHE_TAG; +#ifdef MAJOR #undef MAJOR +#endif +#ifdef MINOR #undef MINOR -#undef TAG +#endif static PyObject * make_impl_info(PyObject *version_info) @@ -3577,9 +3696,12 @@ make_impl_info(PyObject *version_info) if (res < 0) goto error; - value = PyUnicode_FromString(_PySys_ImplCacheTag); - if (value == NULL) + value = _PySys_ImplCacheTag + ? PyUnicode_FromString(_PySys_ImplCacheTag) + : Py_NewRef(Py_None); + if (value == NULL) { goto error; + } res = PyDict_SetItemString(impl_info, "cache_tag", value); Py_DECREF(value); if (res < 0) @@ -3630,6 +3752,66 @@ make_impl_info(PyObject *version_info) return NULL; } + +static PyObject * +make_abi_info(void) +{ + // New entries should be added when needed for a supported platform, + // or by core dev consensus for enabling an unsupported one. + + PyObject *value; + PyObject *abi_info = PyDict_New(); + if (abi_info == NULL) { + return NULL; + } + + value = PyLong_FromLong(sizeof(void *) * 8); + if (value == NULL) { + goto error; + } + if (PyDict_SetItem(abi_info, &_Py_ID(pointer_bits), value) < 0) { + goto error; + } + Py_DECREF(value); + +#ifdef Py_GIL_DISABLED + value = Py_True; +#else + value = Py_False; +#endif + if (PyDict_SetItem(abi_info, &_Py_ID(free_threaded), value) < 0) { + goto error; + } + +#ifdef Py_DEBUG + value = Py_True; +#else + value = Py_False; +#endif + if (PyDict_SetItem(abi_info, &_Py_ID(debug), value) < 0) { + goto error; + } + +#if PY_BIG_ENDIAN + value = &_Py_ID(big); +#else + value = &_Py_ID(little); +#endif + if (PyDict_SetItem(abi_info, &_Py_ID(byteorder), value) < 0) { + goto error; + } + + PyObject *ns = _PyNamespace_New(abi_info); + Py_DECREF(abi_info); + return ns; + +error: + Py_DECREF(abi_info); + Py_XDECREF(value); + return NULL; +} + + #ifdef __EMSCRIPTEN__ PyDoc_STRVAR(emscripten_info__doc__, @@ -3656,20 +3838,38 @@ static PyStructSequence_Desc emscripten_info_desc = { EM_JS(char *, _Py_emscripten_runtime, (void), { var info; - if (typeof navigator == 'object') { + if (typeof process === "object") { + if (process.versions?.bun) { + info = `bun v${process.versions.bun}`; + } else if (process.versions?.deno) { + info = `deno v${process.versions.deno}`; + } else { + // As far as I can tell, every JavaScript runtime puts "node" in + // process.release.name. Pyodide once checked for + // + // process.release.name === "node" + // + // and this is apparently part of the reason other runtimes started + // lying about it. Similar to the situation with userAgent. + // + // But just in case some other JS runtime decides to tell us what it + // is, we'll pick it up. + const name = process.release?.name ?? "node"; + info = `${name} ${process.version}`; + } + // Include v8 version if we know it + if (process.versions?.v8) { + info += ` (v8 ${process.versions.v8})`; + } + } else if (typeof navigator === "object") { info = navigator.userAgent; - } else if (typeof process == 'object') { - info = "Node.js ".concat(process.version); } else { info = "UNKNOWN"; } - var len = lengthBytesUTF8(info) + 1; - var res = _malloc(len); - if (res) stringToUTF8(info, res, len); #if __wasm64__ - return BigInt(res); + return BigInt(stringToNewUTF8(info)); #else - return res; + return stringToNewUTF8(info); #endif }); @@ -3804,9 +4004,9 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) SET_SYS("builtin_module_names", list_builtin_module_names()); SET_SYS("stdlib_module_names", list_stdlib_module_names()); #if PY_BIG_ENDIAN - SET_SYS_FROM_STRING("byteorder", "big"); + SET_SYS("byteorder", &_Py_ID(big)); #else - SET_SYS_FROM_STRING("byteorder", "little"); + SET_SYS("byteorder", &_Py_ID(little)); #endif #ifdef MS_COREDLL @@ -3848,13 +4048,15 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) /* float repr style: 0.03 (short) vs 0.029999999999999999 (legacy) */ #if _PY_SHORT_FLOAT_REPR == 1 - SET_SYS_FROM_STRING("float_repr_style", "short"); + SET_SYS("float_repr_style", &_Py_ID(short)); #else - SET_SYS_FROM_STRING("float_repr_style", "legacy"); + SET_SYS("float_repr_style", &_Py_ID(legacy)); #endif SET_SYS("thread_info", PyThread_GetInfo()); + SET_SYS("abi_info", make_abi_info()); + /* initialize asyncgen_hooks */ if (_PyStructSequence_InitBuiltin(interp, &AsyncGenHooksType, &asyncgen_hooks_desc) < 0) @@ -4013,13 +4215,14 @@ module _jit PyDoc_STRVAR(_jit_doc, "Utilities for observing just-in-time compilation."); /*[clinic input] +@permit_long_summary _jit.is_available -> bool Return True if the current Python executable supports JIT compilation, and False otherwise. [clinic start generated code]*/ static int _jit_is_available_impl(PyObject *module) -/*[clinic end generated code: output=6849a9cd2ff4aac9 input=03add84aa8347cf1]*/ +/*[clinic end generated code: output=6849a9cd2ff4aac9 input=d009d751ae64c9ef]*/ { (void)module; #ifdef _Py_TIER2 @@ -4030,26 +4233,28 @@ _jit_is_available_impl(PyObject *module) } /*[clinic input] +@permit_long_summary _jit.is_enabled -> bool Return True if JIT compilation is enabled for the current Python process (implies sys._jit.is_available()), and False otherwise. [clinic start generated code]*/ static int _jit_is_enabled_impl(PyObject *module) -/*[clinic end generated code: output=55865f8de993fe42 input=02439394da8e873f]*/ +/*[clinic end generated code: output=55865f8de993fe42 input=0524151e857f4f3a]*/ { (void)module; return _PyInterpreterState_GET()->jit; } /*[clinic input] +@permit_long_summary _jit.is_active -> bool Return True if the topmost Python frame is currently executing JIT code (implies sys._jit.is_enabled()), and False otherwise. [clinic start generated code]*/ static int _jit_is_active_impl(PyObject *module) -/*[clinic end generated code: output=7facca06b10064d4 input=be2fcd8a269d9b72]*/ +/*[clinic end generated code: output=7facca06b10064d4 input=081ee32563dc2086]*/ { (void)module; return _PyThreadState_GET()->current_executor != NULL; @@ -4107,6 +4312,15 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p) goto error; } + PyObject *lazy_modules = _PyImport_InitLazyModules(interp); // borrowed reference + if (lazy_modules == NULL) { + goto error; + } + + if (PyDict_SetItemString(sysdict, "lazy_modules", lazy_modules) < 0) { + goto error; + } + PyStatus status = _PySys_SetPreliminaryStderr(sysdict); if (_PyStatus_EXCEPTION(status)) { return status; diff --git a/Python/thread.c b/Python/thread.c index 18c4af7f634c75..0365f977d82b0e 100644 --- a/Python/thread.c +++ b/Python/thread.c @@ -334,14 +334,12 @@ PyThread_GetInfo(void) #ifdef HAVE_PTHREAD_STUBS value = Py_NewRef(Py_None); -#elif defined(_POSIX_THREADS) +#else value = PyUnicode_FromString("pymutex"); if (value == NULL) { Py_DECREF(threadinfo); return NULL; } -#else - value = Py_NewRef(Py_None); #endif PyStructSequence_SET_ITEM(threadinfo, pos++, value); diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index 13992f95723866..8496f91db2eec2 100644 --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -30,6 +30,8 @@ # include <lwp.h> /* _lwp_self() */ #elif defined(__DragonFly__) # include <sys/lwp.h> /* lwp_gettid() */ +#elif defined(__sun__) && SIZEOF_LONG >= 8 +# include <thread.h> #endif /* The POSIX spec requires that use of pthread_attr_setstacksize @@ -399,6 +401,8 @@ PyThread_get_thread_native_id(void) #elif defined(__DragonFly__) lwpid_t native_id; native_id = lwp_gettid(); +#elif defined(__sun__) && SIZEOF_LONG >= 8 + unsigned long native_id = (unsigned long)getpid() << 32 | thr_self(); #endif return (unsigned long) native_id; } diff --git a/Python/traceback.c b/Python/traceback.c index 4f674eaf55715b..50a79d78d2e10e 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -41,7 +41,7 @@ #if defined(__STDC_NO_VLA__) && (__STDC_NO_VLA__ == 1) /* Use alloca() for VLAs. */ -# define VLA(type, name, size) type *name = alloca(size) +# define VLA(type, name, size) type *name = alloca(sizeof(type) * (size)) #elif !defined(__STDC_NO_VLA__) || (__STDC_NO_VLA__ == 0) /* Use actual C VLAs.*/ # define VLA(type, name, size) type name[size] @@ -55,7 +55,7 @@ #define MAX_STRING_LENGTH 500 #define MAX_FRAME_DEPTH 100 -#define MAX_NTHREADS 100 +#define DEFAULT_MAX_NTHREADS 100 /* Function from Parser/tokenizer/file_tokenizer.c */ extern char* _PyTokenizer_FindEncodingFilename(int, PyObject *); @@ -69,6 +69,13 @@ class traceback "PyTracebackObject *" "&PyTraceback_Type" #include "clinic/traceback.c.h" + +#ifdef MS_WINDOWS +typedef HRESULT (WINAPI *PF_GET_THREAD_DESCRIPTION)(HANDLE, PCWSTR*); +static PF_GET_THREAD_DESCRIPTION pGetThreadDescription = NULL; +#endif + + static PyObject * tb_create_raw(PyTracebackObject *next, PyFrameObject *frame, int lasti, int lineno) @@ -408,6 +415,9 @@ _Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject * npath = PyList_Size(syspath); open = PyObject_GetAttr(io, &_Py_ID(open)); + if (open == NULL) { + goto error; + } for (i = 0; i < npath; i++) { v = PyList_GetItem(syspath, i); if (v == NULL) { @@ -973,16 +983,72 @@ _Py_DumpASCII(int fd, PyObject *text) } } + +#ifdef MS_WINDOWS +static void +_Py_DumpWideString(int fd, wchar_t *str) +{ + Py_ssize_t size = wcslen(str); + int truncated; + if (MAX_STRING_LENGTH < size) { + size = MAX_STRING_LENGTH; + truncated = 1; + } + else { + truncated = 0; + } + + for (Py_ssize_t i=0; i < size; i++) { + Py_UCS4 ch = str[i]; + if (' ' <= ch && ch <= 126) { + /* printable ASCII character */ + dump_char(fd, (char)ch); + } + else if (ch <= 0xff) { + PUTS(fd, "\\x"); + _Py_DumpHexadecimal(fd, ch, 2); + } + else if (Py_UNICODE_IS_HIGH_SURROGATE(ch) + && Py_UNICODE_IS_LOW_SURROGATE(str[i+1])) { + ch = Py_UNICODE_JOIN_SURROGATES(ch, str[i+1]); + i++; // Skip the low surrogate character + PUTS(fd, "\\U"); + _Py_DumpHexadecimal(fd, ch, 8); + } + else { + Py_BUILD_ASSERT(sizeof(wchar_t) == 2); + PUTS(fd, "\\u"); + _Py_DumpHexadecimal(fd, ch, 4); + } + } + + if (truncated) { + PUTS(fd, "..."); + } +} +#endif + + /* Write a frame into the file fd: "File "xxx", line xxx in xxx". - This function is signal safe. */ + This function is signal safe. -static void + Return 0 on success. Return -1 if the frame is invalid. */ + +static int _Py_NO_SANITIZE_THREAD dump_frame(int fd, _PyInterpreterFrame *frame) { - assert(frame->owner < FRAME_OWNED_BY_INTERPRETER); + if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { + /* Ignore trampoline frames and base frame sentinel */ + return 0; + } + + PyCodeObject *code = _PyFrame_SafeGetCode(frame); + if (code == NULL) { + return -1; + } - PyCodeObject *code =_PyFrame_GetCode(frame); + int res = 0; PUTS(fd, " File "); if (code->co_filename != NULL && PyUnicode_Check(code->co_filename)) @@ -990,32 +1056,39 @@ dump_frame(int fd, _PyInterpreterFrame *frame) PUTS(fd, "\""); _Py_DumpASCII(fd, code->co_filename); PUTS(fd, "\""); - } else { + } + else { PUTS(fd, "???"); + res = -1; } - int lineno = PyUnstable_InterpreterFrame_GetLine(frame); PUTS(fd, ", line "); + int lasti = _PyFrame_SafeGetLasti(frame); + int lineno = -1; + if (lasti >= 0) { + lineno = _PyCode_SafeAddr2Line(code, lasti); + } if (lineno >= 0) { _Py_DumpDecimal(fd, (size_t)lineno); } else { PUTS(fd, "???"); + res = -1; } - PUTS(fd, " in "); - if (code->co_name != NULL - && PyUnicode_Check(code->co_name)) { + PUTS(fd, " in "); + if (code->co_name != NULL && PyUnicode_Check(code->co_name)) { _Py_DumpASCII(fd, code->co_name); } else { PUTS(fd, "???"); + res = -1; } - PUTS(fd, "\n"); + return res; } -static int +static int _Py_NO_SANITIZE_THREAD tstate_is_freed(PyThreadState *tstate) { if (_PyMem_IsPtrFreed(tstate)) { @@ -1024,18 +1097,21 @@ tstate_is_freed(PyThreadState *tstate) if (_PyMem_IsPtrFreed(tstate->interp)) { return 1; } + if (_PyMem_IsULongFreed(tstate->thread_id)) { + return 1; + } return 0; } -static int +static int _Py_NO_SANITIZE_THREAD interp_is_freed(PyInterpreterState *interp) { return _PyMem_IsPtrFreed(interp); } -static void +static void _Py_NO_SANITIZE_THREAD dump_traceback(int fd, PyThreadState *tstate, int write_header) { if (write_header) { @@ -1043,7 +1119,7 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) } if (tstate_is_freed(tstate)) { - PUTS(fd, " <tstate is freed>\n"); + PUTS(fd, " <freed thread state>\n"); return; } @@ -1055,17 +1131,6 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) unsigned int depth = 0; while (1) { - if (frame->owner == FRAME_OWNED_BY_INTERPRETER) { - /* Trampoline frame */ - frame = frame->previous; - if (frame == NULL) { - break; - } - - /* Can't have more than one shim frame in a row */ - assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); - } - if (MAX_FRAME_DEPTH <= depth) { if (MAX_FRAME_DEPTH < depth) { PUTS(fd, "plus "); @@ -1075,8 +1140,20 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) break; } - dump_frame(fd, frame); - frame = frame->previous; + if (_PyMem_IsPtrFreed(frame)) { + PUTS(fd, " <freed frame>\n"); + break; + } + // Read frame->previous early since memory can be freed during + // dump_frame() + _PyInterpreterFrame *previous = frame->previous; + + if (dump_frame(fd, frame) < 0) { + PUTS(fd, " <invalid frame>\n"); + break; + } + + frame = previous; if (frame == NULL) { break; } @@ -1090,10 +1167,11 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) The caller is responsible to call PyErr_CheckSignals() to call Python signal handlers if signals were received. */ -void -_Py_DumpTraceback(int fd, PyThreadState *tstate) +const char* +PyUnstable_DumpTraceback(int fd, PyThreadState *tstate) { dump_traceback(fd, tstate, 1); + return NULL; } #if defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP) @@ -1107,23 +1185,12 @@ _Py_DumpTraceback(int fd, PyThreadState *tstate) # endif #endif -/* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if - is_current is true, "Thread 0xHHHH:\n" otherwise. - This function is signal safe. */ - -static void -write_thread_id(int fd, PyThreadState *tstate, int is_current) +// Write the thread name +static void _Py_NO_SANITIZE_THREAD +write_thread_name(int fd, PyThreadState *tstate) { - if (is_current) - PUTS(fd, "Current thread 0x"); - else - PUTS(fd, "Thread 0x"); - _Py_DumpHexadecimal(fd, - tstate->thread_id, - sizeof(unsigned long) * 2); - - // Write the thread name +#ifndef MS_WINDOWS #if defined(HAVE_PTHREAD_GETNAME_NP) || defined(HAVE_PTHREAD_GET_NAME_NP) char name[100]; pthread_t thread = (pthread_t)tstate->thread_id; @@ -1142,6 +1209,51 @@ write_thread_id(int fd, PyThreadState *tstate, int is_current) } } #endif +#else + // Windows implementation + if (pGetThreadDescription == NULL) { + return; + } + + HANDLE thread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, tstate->thread_id); + if (thread == NULL) { + return; + } + + wchar_t *name; + HRESULT hr = pGetThreadDescription(thread, &name); + if (!FAILED(hr)) { + if (name[0] != 0) { + PUTS(fd, " ["); + _Py_DumpWideString(fd, name); + PUTS(fd, "]"); + } + LocalFree(name); + } + CloseHandle(thread); +#endif +} + + +/* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if + is_current is true, "Thread 0xHHHH:\n" otherwise. + + This function is signal safe (except on Windows). */ + +static void _Py_NO_SANITIZE_THREAD +write_thread_id(int fd, PyThreadState *tstate, int is_current) +{ + if (is_current) + PUTS(fd, "Current thread 0x"); + else + PUTS(fd, "Thread 0x"); + _Py_DumpHexadecimal(fd, + tstate->thread_id, + sizeof(unsigned long) * 2); + + if (!_PyMem_IsULongFreed(tstate->thread_id)) { + write_thread_name(fd, tstate); + } PUTS(fd, " (most recent call first):\n"); } @@ -1152,12 +1264,17 @@ write_thread_id(int fd, PyThreadState *tstate, int is_current) The caller is responsible to call PyErr_CheckSignals() to call Python signal handlers if signals were received. */ -const char* -_Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, - PyThreadState *current_tstate) +const char* _Py_NO_SANITIZE_THREAD +PyUnstable_DumpTracebackThreads(int fd, PyInterpreterState *interp, + PyThreadState *current_tstate, + Py_ssize_t max_threads) { + if (max_threads == 0) { + max_threads = DEFAULT_MAX_NTHREADS; + } + if (current_tstate == NULL) { - /* _Py_DumpTracebackThreads() is called from signal handlers by + /* PyUnstable_DumpTracebackThreads() is called from signal handlers by faulthandler. SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals @@ -1199,23 +1316,29 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, return "unable to get the thread head state"; /* Dump the traceback of each thread */ - tstate = PyInterpreterState_ThreadHead(interp); - unsigned int nthreads = 0; + Py_ssize_t nthreads = 0; _Py_BEGIN_SUPPRESS_IPH do { if (nthreads != 0) PUTS(fd, "\n"); - if (nthreads >= MAX_NTHREADS) { + if (nthreads >= max_threads) { PUTS(fd, "...\n"); break; } + + if (tstate_is_freed(tstate)) { + PUTS(fd, "<freed thread state>\n"); + break; + } + write_thread_id(fd, tstate, tstate == current_tstate); if (tstate == current_tstate && tstate->interp->gc.collecting) { PUTS(fd, " Garbage-collecting\n"); } dump_traceback(fd, tstate, 0); - tstate = PyThreadState_Next(tstate); + + tstate = tstate->next; nthreads++; } while (tstate != NULL); _Py_END_SUPPRESS_IPH @@ -1326,3 +1449,30 @@ _Py_DumpStack(int fd) PUTS(fd, " <cannot get C stack on this system>\n"); } #endif + +void +_Py_InitDumpStack(void) +{ +#ifdef CAN_C_BACKTRACE + // gh-137185: Call backtrace() once to force libgcc to be loaded early. + void *callstack[1]; + (void)backtrace(callstack, 1); +#endif +} + + +void +_Py_DumpTraceback_Init(void) +{ +#ifdef MS_WINDOWS + if (pGetThreadDescription != NULL) { + return; + } + + HMODULE kernelbase = GetModuleHandleW(L"kernelbase.dll"); + if (kernelbase != NULL) { + pGetThreadDescription = (PF_GET_THREAD_DESCRIPTION)GetProcAddress( + kernelbase, "GetThreadDescription"); + } +#endif +} diff --git a/Python/tracemalloc.c b/Python/tracemalloc.c index 7066a214f1065b..0afc84e021817c 100644 --- a/Python/tracemalloc.c +++ b/Python/tracemalloc.c @@ -46,7 +46,7 @@ typedef struct tracemalloc_frame frame_t; typedef struct tracemalloc_traceback traceback_t; #define TRACEBACK_SIZE(NFRAME) \ - (sizeof(traceback_t) + sizeof(frame_t) * (NFRAME - 1)) + (sizeof(traceback_t) + sizeof(frame_t) * (NFRAME)) static const int MAX_NFRAME = UINT16_MAX; @@ -224,13 +224,20 @@ tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame) assert(PyStackRef_CodeCheck(pyframe->f_executable)); frame->filename = &_Py_STR(anon_unknown); - int lineno = PyUnstable_InterpreterFrame_GetLine(pyframe); + int lineno = -1; + PyCodeObject *code = _PyFrame_GetCode(pyframe); + // PyUnstable_InterpreterFrame_GetLine() cannot but used, since it uses + // a critical section which can trigger a deadlock. + int lasti = _PyFrame_SafeGetLasti(pyframe); + if (lasti >= 0) { + lineno = _PyCode_SafeAddr2Line(code, lasti); + } if (lineno < 0) { lineno = 0; } frame->lineno = (unsigned int)lineno; - PyObject *filename = filename = _PyFrame_GetCode(pyframe)->co_filename; + PyObject *filename = code->co_filename; if (filename == NULL) { #ifdef TRACE_DEBUG tracemalloc_error("failed to get the filename of the code object"); @@ -329,8 +336,9 @@ traceback_new(void) traceback->nframe = 0; traceback->total_nframe = 0; traceback_get_frames(traceback); - if (traceback->nframe == 0) - return &tracemalloc_empty_traceback; + if (traceback->nframe == 0) { + return tracemalloc_empty_traceback; + } traceback->hash = traceback_hash(traceback); /* intern the traceback */ @@ -754,12 +762,18 @@ _PyTraceMalloc_Init(void) return _PyStatus_NO_MEMORY(); } - tracemalloc_empty_traceback.nframe = 1; - tracemalloc_empty_traceback.total_nframe = 1; + assert(tracemalloc_empty_traceback == NULL); + tracemalloc_empty_traceback = raw_malloc(TRACEBACK_SIZE(1)); + if (tracemalloc_empty_traceback == NULL) { + return _PyStatus_NO_MEMORY(); + } + + tracemalloc_empty_traceback->nframe = 1; + tracemalloc_empty_traceback->total_nframe = 1; /* borrowed reference */ - tracemalloc_empty_traceback.frames[0].filename = &_Py_STR(anon_unknown); - tracemalloc_empty_traceback.frames[0].lineno = 0; - tracemalloc_empty_traceback.hash = traceback_hash(&tracemalloc_empty_traceback); + tracemalloc_empty_traceback->frames[0].filename = &_Py_STR(anon_unknown); + tracemalloc_empty_traceback->frames[0].lineno = 0; + tracemalloc_empty_traceback->hash = traceback_hash(tracemalloc_empty_traceback); tracemalloc_config.initialized = TRACEMALLOC_INITIALIZED; return _PyStatus_OK(); @@ -782,6 +796,9 @@ tracemalloc_deinit(void) _Py_hashtable_destroy(tracemalloc_filenames); PyThread_tss_delete(&tracemalloc_reentrant_key); + + raw_free(tracemalloc_empty_traceback); + tracemalloc_empty_traceback = NULL; } @@ -840,7 +857,7 @@ _PyTraceMalloc_Start(int max_nframe) /* everything is ready: start tracing Python memory allocations */ TABLES_LOCK(); - tracemalloc_config.tracing = 1; + _Py_atomic_store_int_relaxed(&tracemalloc_config.tracing, 1); TABLES_UNLOCK(); return 0; @@ -853,11 +870,12 @@ _PyTraceMalloc_Stop(void) TABLES_LOCK(); if (!tracemalloc_config.tracing) { - goto done; + TABLES_UNLOCK(); + return; } /* stop tracing Python memory allocations */ - tracemalloc_config.tracing = 0; + _Py_atomic_store_int_relaxed(&tracemalloc_config.tracing, 0); /* unregister the hook on memory allocators */ PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw); @@ -870,10 +888,12 @@ _PyTraceMalloc_Stop(void) raw_free(tracemalloc_traceback); tracemalloc_traceback = NULL; - (void)PyRefTracer_SetTracer(NULL, NULL); - -done: TABLES_UNLOCK(); + + // Call it after TABLES_UNLOCK() since it calls _PyEval_StopTheWorldAll() + // which would lead to a deadlock with TABLES_LOCK() which doesn't detach + // the thread state. + (void)PyRefTracer_SetTracer(NULL, NULL); } @@ -1197,6 +1217,10 @@ int PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, size_t size) { + if (_Py_atomic_load_int_relaxed(&tracemalloc_config.tracing) == 0) { + /* tracemalloc is not tracing: do nothing */ + return -2; + } PyGILState_STATE gil_state = PyGILState_Ensure(); TABLES_LOCK(); @@ -1218,6 +1242,11 @@ PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, int PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr) { + if (_Py_atomic_load_int_relaxed(&tracemalloc_config.tracing) == 0) { + /* tracemalloc is not tracing: do nothing */ + return -2; + } + TABLES_LOCK(); int result; diff --git a/README.rst b/README.rst index baea5e0978d8fa..48f86cdb86ed1d 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.15.0 alpha 0 +This is Python version 3.16.0 alpha 0 ===================================== .. image:: https://github.com/python/cpython/actions/workflows/build.yml/badge.svg?branch=main&event=push @@ -136,7 +136,7 @@ What's New ---------- We have a comprehensive overview of the changes in the `What's new in Python -3.15 <https://docs.python.org/3.15/whatsnew/3.15.html>`_ document. For a more +3.16 <https://docs.python.org/3.16/whatsnew/3.16.html>`_ document. For a more detailed change log, read `Misc/NEWS <https://github.com/python/cpython/tree/main/Misc/NEWS.d>`_, but a full accounting of changes can only be gleaned from the `commit history @@ -149,11 +149,11 @@ entitled "Installing multiple versions". Documentation ------------- -`Documentation for Python 3.15 <https://docs.python.org/3.15/>`_ is online, +`Documentation for Python 3.16 <https://docs.python.org/3.16/>`_ is online, updated daily. It can also be downloaded in many formats for faster access. The documentation -is downloadable in HTML, PDF, and reStructuredText formats; the latter version +is downloadable in HTML, EPUB, and reStructuredText formats; the latter version is primarily for documentation authors, translators, and people with special formatting requirements. @@ -208,7 +208,7 @@ and ``make altinstall`` in the others. Release Schedule ---------------- -See `PEP 790 <https://peps.python.org/pep-0790/>`__ for Python 3.15 release details. +See `PEP 826 <https://peps.python.org/pep-0826/>`__ for Python 3.16 release details. Copyright and License Information @@ -232,4 +232,4 @@ This Python distribution contains *no* GNU General Public License (GPL) code, so it may be used in proprietary projects. There are interfaces to some GNU code but these are entirely optional. -All trademarks referenced herein are property of their respective holders. \ No newline at end of file +All trademarks referenced herein are property of their respective holders. diff --git a/Tools/README b/Tools/README index 09bd6fb4798950..90acb2614820ee 100644 --- a/Tools/README +++ b/Tools/README @@ -16,6 +16,8 @@ clinic A preprocessor for CPython C files in order to automate freeze Create a stand-alone executable from a Python program. +ftscalingbench Benchmarks for free-threading and finding bottlenecks. + gdb Python code to be run inside gdb, to make it easier to debug Python itself (by David Malcolm). @@ -26,6 +28,12 @@ i18n Tools for internationalization. pygettext.py importbench A set of micro-benchmarks for various import scenarios. +inspection Tooling for PEP-678 "Safe external debugger interface for CPython". + +jit Tooling for building the JIT. + +lockbench Benchmarks for PyMutex and critical sections. + msi Support for packaging Python as an MSI package on Windows. nuget Files for the NuGet package manager for .NET. @@ -35,13 +43,15 @@ patchcheck Tools for checking and applying patches to the Python source cod peg_generator PEG-based parser generator (pegen) used for new parser. +pixi-packages Pixi package definitions for downstream from-source builds. + scripts A number of useful single-file programs, e.g. run_tests.py which runs the Python test suite. ssl Scripts to generate ssl_data.h from OpenSSL sources, and run tests against multiple installations of OpenSSL and LibreSSL. -tz A script to dump timezone from /usr/share/zoneinfo. +tsan Utilities for building CPython with thread-sanitizer. unicode Tools for generating unicodedata and codecs from unicode.org and other mapping files (by Fredrik Lundh, Marc-Andre Lemburg diff --git a/Tools/build/.ruff.toml b/Tools/build/.ruff.toml index dcbf2936290f12..4a3dd618f6559f 100644 --- a/Tools/build/.ruff.toml +++ b/Tools/build/.ruff.toml @@ -29,7 +29,6 @@ ignore = [ "F541", # f-string without any placeholders "PYI024", # Use `typing.NamedTuple` instead of `collections.namedtuple` "PYI025", # Use `from collections.abc import Set as AbstractSet` - "UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)` ] [lint.per-file-ignores] @@ -39,3 +38,7 @@ ignore = [ "generate_{re_casefix,sre_constants,token}.py" = [ "UP031", # Use format specifiers instead of percent format ] +"generate_slots.py" = [ + "I001", # Import block is un-sorted + "ISC003", # Explicitly concatenated string +] diff --git a/Tools/build/check_extension_modules.py b/Tools/build/check_extension_modules.py index 9815bcfe27d995..c619a9a0c1c5a1 100644 --- a/Tools/build/check_extension_modules.py +++ b/Tools/build/check_extension_modules.py @@ -17,25 +17,32 @@ See --help for more information """ + +from __future__ import annotations + import _imp import argparse -import collections import enum +import json import logging import os import pathlib +import pprint import re import sys import sysconfig import warnings from collections.abc import Iterable -from importlib._bootstrap import _load as bootstrap_load +from importlib._bootstrap import ( # type: ignore[attr-defined] + _load as bootstrap_load, +) from importlib.machinery import ( BuiltinImporter, ExtensionFileLoader, ModuleSpec, ) from importlib.util import spec_from_file_location, spec_from_loader +from typing import NamedTuple SRC_DIR = pathlib.Path(__file__).parent.parent.parent @@ -111,7 +118,20 @@ help="Print a list of module names to stdout and exit", ) +parser.add_argument( + "--generate-missing-stdlib-info", + action="store_true", + help="Generate file with stdlib module info", +) +parser.add_argument( + "--with-missing-stdlib-config", + metavar="CONFIG_FILE", + help="Path to JSON config file with custom missing module messages", +) + + +@enum.unique class ModuleState(enum.Enum): # Makefile state "yes" BUILTIN = "builtin" @@ -123,11 +143,13 @@ class ModuleState(enum.Enum): # disabled by Setup / makesetup rule DISABLED_SETUP = "disabled_setup" - def __bool__(self): + def __bool__(self) -> bool: return self.value in {"builtin", "shared"} -ModuleInfo = collections.namedtuple("ModuleInfo", "name state") +class ModuleInfo(NamedTuple): + name: str + state: ModuleState class ModuleChecker: @@ -135,9 +157,9 @@ class ModuleChecker: setup_files = ( # see end of configure.ac - "Modules/Setup.local", - "Modules/Setup.stdlib", - "Modules/Setup.bootstrap", + pathlib.Path("Modules/Setup.local"), + pathlib.Path("Modules/Setup.stdlib"), + pathlib.Path("Modules/Setup.bootstrap"), SRC_DIR / "Modules/Setup", ) @@ -149,15 +171,15 @@ def __init__(self, cross_compiling: bool = False, strict: bool = False): self.builddir = self.get_builddir() self.modules = self.get_modules() - self.builtin_ok = [] - self.shared_ok = [] - self.failed_on_import = [] - self.missing = [] - self.disabled_configure = [] - self.disabled_setup = [] - self.notavailable = [] + self.builtin_ok: list[ModuleInfo] = [] + self.shared_ok: list[ModuleInfo] = [] + self.failed_on_import: list[ModuleInfo] = [] + self.missing: list[ModuleInfo] = [] + self.disabled_configure: list[ModuleInfo] = [] + self.disabled_setup: list[ModuleInfo] = [] + self.notavailable: list[ModuleInfo] = [] - def check(self): + def check(self) -> None: if not hasattr(_imp, 'create_dynamic'): logger.warning( ('Dynamic extensions not supported ' @@ -189,10 +211,10 @@ def check(self): assert modinfo.state == ModuleState.SHARED self.shared_ok.append(modinfo) - def summary(self, *, verbose: bool = False): + def summary(self, *, verbose: bool = False) -> None: longest = max([len(e.name) for e in self.modules], default=0) - def print_three_column(modinfos: list[ModuleInfo]): + def print_three_column(modinfos: list[ModuleInfo]) -> None: names = [modinfo.name for modinfo in modinfos] names.sort(key=str.lower) # guarantee zip() doesn't drop anything @@ -262,17 +284,50 @@ def print_three_column(modinfos: list[ModuleInfo]): f"{len(self.failed_on_import)} failed on import)" ) - def check_strict_build(self): + def check_strict_build(self) -> None: """Fail if modules are missing and it's a strict build""" if self.strict_extensions_build and (self.failed_on_import or self.missing): raise RuntimeError("Failed to build some stdlib modules") - def list_module_names(self, *, all: bool = False) -> set: + def list_module_names(self, *, all: bool = False) -> set[str]: names = {modinfo.name for modinfo in self.modules} if all: names.update(WINDOWS_MODULES) return names + def generate_missing_stdlib_info(self, config_path: str | None = None) -> None: + config_messages = {} + if config_path: + try: + with open(config_path, encoding='utf-8') as f: + config_messages = json.load(f) + except (FileNotFoundError, json.JSONDecodeError) as e: + raise RuntimeError(f"Failed to load missing stdlib config {config_path!r}") from e + + messages = {} + for name in WINDOWS_MODULES: + messages[name] = f"Unsupported platform for Windows-only standard library module {name!r}" + + for modinfo in self.modules: + if modinfo.state in (ModuleState.DISABLED, ModuleState.DISABLED_SETUP): + messages[modinfo.name] = f"Standard library module disabled during build {modinfo.name!r} was not found" + elif modinfo.state == ModuleState.NA: + messages[modinfo.name] = f"Unsupported platform for standard library module {modinfo.name!r}" + + messages.update(config_messages) + + content = f'''\ +# Standard library information used by the traceback module for more informative +# ModuleNotFound error messages. +# Generated by check_extension_modules.py + +_MISSING_STDLIB_MODULE_MESSAGES = {pprint.pformat(messages)} +''' + + output_path = self.builddir / "_missing_stdlib_info.py" + with open(output_path, "w", encoding="utf-8") as f: + f.write(content) + def get_builddir(self) -> pathlib.Path: try: with open(self.pybuilddir_txt, encoding="utf-8") as f: @@ -280,9 +335,9 @@ def get_builddir(self) -> pathlib.Path: except FileNotFoundError: logger.error("%s must be run from the top build directory", __file__) raise - builddir = pathlib.Path(builddir) - logger.debug("%s: %s", self.pybuilddir_txt, builddir) - return builddir + builddir_path = pathlib.Path(builddir) + logger.debug("%s: %s", self.pybuilddir_txt, builddir_path) + return builddir_path def get_modules(self) -> list[ModuleInfo]: """Get module info from sysconfig and Modules/Setup* files""" @@ -367,7 +422,7 @@ def parse_setup_file(self, setup_file: pathlib.Path) -> Iterable[ModuleInfo]: case ["*disabled*"]: state = ModuleState.DISABLED case ["*noconfig*"]: - state = None + continue case [*items]: if state == ModuleState.DISABLED: # *disabled* can disable multiple modules per line @@ -384,26 +439,33 @@ def parse_setup_file(self, setup_file: pathlib.Path) -> Iterable[ModuleInfo]: def get_spec(self, modinfo: ModuleInfo) -> ModuleSpec: """Get ModuleSpec for builtin or extension module""" if modinfo.state == ModuleState.SHARED: - location = os.fspath(self.get_location(modinfo)) + mod_location = self.get_location(modinfo) + assert mod_location is not None + location = os.fspath(mod_location) loader = ExtensionFileLoader(modinfo.name, location) - return spec_from_file_location(modinfo.name, location, loader=loader) + spec = spec_from_file_location(modinfo.name, location, loader=loader) + assert spec is not None + return spec elif modinfo.state == ModuleState.BUILTIN: - return spec_from_loader(modinfo.name, loader=BuiltinImporter) + spec = spec_from_loader(modinfo.name, loader=BuiltinImporter) + assert spec is not None + return spec else: raise ValueError(modinfo) - def get_location(self, modinfo: ModuleInfo) -> pathlib.Path: + def get_location(self, modinfo: ModuleInfo) -> pathlib.Path | None: """Get shared library location in build directory""" if modinfo.state == ModuleState.SHARED: return self.builddir / f"{modinfo.name}{self.ext_suffix}" else: return None - def _check_file(self, modinfo: ModuleInfo, spec: ModuleSpec): + def _check_file(self, modinfo: ModuleInfo, spec: ModuleSpec) -> None: """Check that the module file is present and not empty""" - if spec.loader is BuiltinImporter: - return + if spec.loader is BuiltinImporter: # type: ignore[comparison-overlap] + return # type: ignore[unreachable] try: + assert spec.origin is not None st = os.stat(spec.origin) except FileNotFoundError: logger.error("%s (%s) is missing", modinfo.name, spec.origin) @@ -411,7 +473,7 @@ def _check_file(self, modinfo: ModuleInfo, spec: ModuleSpec): if not st.st_size: raise ImportError(f"{spec.origin} is an empty file") - def check_module_import(self, modinfo: ModuleInfo): + def check_module_import(self, modinfo: ModuleInfo) -> None: """Attempt to import module and report errors""" spec = self.get_spec(modinfo) self._check_file(modinfo, spec) @@ -430,7 +492,7 @@ def check_module_import(self, modinfo: ModuleInfo): logger.exception("Importing extension '%s' failed!", modinfo.name) raise - def check_module_cross(self, modinfo: ModuleInfo): + def check_module_cross(self, modinfo: ModuleInfo) -> None: """Sanity check for cross compiling""" spec = self.get_spec(modinfo) self._check_file(modinfo, spec) @@ -443,6 +505,7 @@ def rename_module(self, modinfo: ModuleInfo) -> None: failed_name = f"{modinfo.name}_failed{self.ext_suffix}" builddir_path = self.get_location(modinfo) + assert builddir_path is not None if builddir_path.is_symlink(): symlink = builddir_path module_path = builddir_path.resolve().relative_to(os.getcwd()) @@ -466,7 +529,7 @@ def rename_module(self, modinfo: ModuleInfo) -> None: logger.debug("Rename '%s' -> '%s'", module_path, failed_path) -def main(): +def main() -> None: args = parser.parse_args() if args.debug: args.verbose = True @@ -483,6 +546,9 @@ def main(): names = checker.list_module_names(all=True) for name in sorted(names): print(name) + elif args.generate_missing_stdlib_info: + checker.check() + checker.generate_missing_stdlib_info(args.with_missing_stdlib_config) else: checker.check() checker.summary(verbose=args.verbose) diff --git a/Tools/build/check_warnings.py b/Tools/build/check_warnings.py index 3f49d8e7f2ee48..44ccf9708ad72f 100644 --- a/Tools/build/check_warnings.py +++ b/Tools/build/check_warnings.py @@ -8,21 +8,29 @@ import sys from collections import defaultdict from pathlib import Path -from typing import NamedTuple +from typing import NamedTuple, TypedDict class IgnoreRule(NamedTuple): file_path: str - count: int + count: int # type: ignore[assignment] ignore_all: bool = False is_directory: bool = False +class CompileWarning(TypedDict): + file: str + line: str + column: str + message: str + option: str + + def parse_warning_ignore_file(file_path: str) -> set[IgnoreRule]: """ Parses the warning ignore file and returns a set of IgnoreRules """ - files_with_expected_warnings = set() + files_with_expected_warnings: set[IgnoreRule] = set() with Path(file_path).open(encoding="UTF-8") as ignore_rules_file: files_with_expected_warnings = set() for i, line in enumerate(ignore_rules_file): @@ -46,7 +54,7 @@ def parse_warning_ignore_file(file_path: str) -> set[IgnoreRule]: ) sys.exit(1) if ignore_all: - count = 0 + count = "0" files_with_expected_warnings.add( IgnoreRule( @@ -61,7 +69,7 @@ def extract_warnings_from_compiler_output( compiler_output: str, compiler_output_type: str, path_prefix: str = "", -) -> list[dict]: +) -> list[CompileWarning]: """ Extracts warnings from the compiler output based on compiler output type. Removes path prefix from file paths if provided. @@ -78,8 +86,12 @@ def extract_warnings_from_compiler_output( r"(?P<file>.*):(?P<line>\d+):(?P<column>\d+): warning: " r"(?P<message>.*) (?P<option>\[-[^\]]+\])$" ) + else: + raise RuntimeError( + f"Unsupported compiler output type: {compiler_output_type}", + ) compiled_regex = re.compile(regex_pattern) - compiler_warnings = [] + compiler_warnings: list[CompileWarning] = [] for i, line in enumerate(compiler_output.splitlines(), start=1): if match := compiled_regex.match(line): try: @@ -100,7 +112,9 @@ def extract_warnings_from_compiler_output( return compiler_warnings -def get_warnings_by_file(warnings: list[dict]) -> dict[str, list[dict]]: +def get_warnings_by_file( + warnings: list[CompileWarning], +) -> dict[str, list[CompileWarning]]: """ Returns a dictionary where the key is the file and the data is the warnings in that file. Does not include duplicate warnings for a @@ -138,7 +152,7 @@ def is_file_ignored( def get_unexpected_warnings( ignore_rules: set[IgnoreRule], - files_with_warnings: set[IgnoreRule], + files_with_warnings: dict[str, list[CompileWarning]], ) -> int: """ Returns failure status if warnings discovered in list of warnings @@ -180,7 +194,7 @@ def get_unexpected_warnings( def get_unexpected_improvements( ignore_rules: set[IgnoreRule], - files_with_warnings: set[IgnoreRule], + files_with_warnings: dict[str, list[CompileWarning]], ) -> int: """ Returns failure status if the number of warnings for a file is greater diff --git a/Tools/build/compute-changes.py b/Tools/build/compute-changes.py index b5993d29b92972..53d7b8fe32f89e 100644 --- a/Tools/build/compute-changes.py +++ b/Tools/build/compute-changes.py @@ -11,7 +11,7 @@ import os import subprocess -from dataclasses import dataclass +from dataclasses import dataclass, fields from pathlib import Path TYPE_CHECKING = False @@ -19,14 +19,16 @@ from collections.abc import Set GITHUB_DEFAULT_BRANCH = os.environ["GITHUB_DEFAULT_BRANCH"] -GITHUB_CODEOWNERS_PATH = Path(".github/CODEOWNERS") GITHUB_WORKFLOWS_PATH = Path(".github/workflows") -CONFIGURATION_FILE_NAMES = frozenset({ - ".pre-commit-config.yaml", - ".ruff.toml", - "mypy.ini", +RUN_TESTS_IGNORE = frozenset({ + Path("Tools/check-c-api-docs/ignored_c_api.txt"), + Path(".github/CODEOWNERS"), + Path(".pre-commit-config.yaml"), + Path(".ruff.toml"), + Path("mypy.ini"), }) + UNIX_BUILD_SYSTEM_FILE_NAMES = frozenset({ Path("aclocal.m4"), Path("config.guess"), @@ -45,25 +47,99 @@ SUFFIXES_C_OR_CPP = frozenset({".c", ".h", ".cpp"}) SUFFIXES_DOCUMENTATION = frozenset({".rst", ".md"}) +ANDROID_DIRS = frozenset({"Android"}) +EMSCRIPTEN_DIRS = frozenset({Path("Platforms", "emscripten")}) +IOS_DIRS = frozenset({"Apple", "iOS"}) +MACOS_DIRS = frozenset({"Mac"}) +WASI_DIRS = frozenset({Path("Platforms", "WASI")}) + +LIBRARY_FUZZER_PATHS = frozenset({ + # All C/CPP fuzzers. + Path("configure"), + Path(".github/workflows/reusable-cifuzz.yml"), + # ast + Path("Lib/ast.py"), + Path("Python/ast.c"), + # configparser + Path("Lib/configparser.py"), + # csv + Path("Lib/csv.py"), + Path("Modules/_csv.c"), + # decode + Path("Lib/encodings/"), + Path("Modules/_codecsmodule.c"), + Path("Modules/cjkcodecs/"), + Path("Modules/unicodedata.c"), + Path("Modules/unicodedata_db.h"), + # difflib + Path("Lib/difflib.py"), + # email + Path("Lib/email/"), + # html + Path("Lib/html/"), + Path("Lib/_markupbase.py"), + # http.client + Path("Lib/http/client.py"), + # json + Path("Lib/json/"), + Path("Modules/_json.c"), + # plist + Path("Lib/plistlib.py"), + # re + Path("Lib/re/"), + Path("Modules/_sre/"), + # tarfile + Path("Lib/tarfile.py"), + # tomllib + Path("Lib/tomllib/"), + # xml + Path("Lib/xml/"), + Path("Lib/_markupbase.py"), + Path("Modules/expat/"), + Path("Modules/pyexpat.c"), + # zipfile + Path("Lib/zipfile/"), + # zoneinfo + Path("Lib/zoneinfo/"), + Path("Modules/_zoneinfo.c"), +}) + @dataclass(kw_only=True, slots=True) class Outputs: + run_android: bool = False run_ci_fuzz: bool = False + run_ci_fuzz_stdlib: bool = False run_docs: bool = False + run_emscripten: bool = False + run_ios: bool = False + run_macos: bool = False run_tests: bool = False + run_ubuntu: bool = False + run_wasi: bool = False run_windows_msi: bool = False run_windows_tests: bool = False def compute_changes() -> None: - target_branch, head_ref = git_refs() + target_ref, head_ref = git_refs() if os.environ.get("GITHUB_EVENT_NAME", "") == "pull_request": # Getting changed files only makes sense on a pull request - files = get_changed_files(target_branch, head_ref) + files = get_changed_files(target_ref, head_ref) outputs = process_changed_files(files) else: # Otherwise, just run the tests - outputs = Outputs(run_tests=True, run_windows_tests=True) + outputs = Outputs( + run_android=True, + run_emscripten=True, + run_ios=True, + run_macos=True, + run_tests=True, + run_ubuntu=True, + run_wasi=True, + run_windows_tests=True, + ) + target_branch = target_ref.removeprefix("origin/") outputs = process_target_branch(outputs, target_branch) if outputs.run_tests: @@ -76,6 +152,11 @@ def compute_changes() -> None: else: print("Branch too old for CIFuzz tests; or no C files were changed") + if outputs.run_ci_fuzz_stdlib: + print("Run CIFuzz tests for stdlib") + else: + print("Branch too old for CIFuzz tests; or no stdlib files were changed") + if outputs.run_docs: print("Build documentation") @@ -111,35 +192,85 @@ def get_changed_files( return frozenset(map(Path, filter(None, map(str.strip, changed_files)))) +def get_file_platform(file: Path) -> str | None: + if not file.parts: + return None + first_part = file.parts[0] + if first_part in MACOS_DIRS: + return "macos" + if first_part in IOS_DIRS: + return "ios" + if first_part in ANDROID_DIRS: + return "android" + if len(file.parts) >= 2 and Path(*file.parts[:2]) in EMSCRIPTEN_DIRS: + return "emscripten" + if len(file.parts) >= 2 and Path(*file.parts[:2]) in WASI_DIRS: + return "wasi" + return None + + +def is_fuzzable_library_file(file: Path) -> bool: + return any( + (file.is_relative_to(needs_fuzz) and needs_fuzz.is_dir()) + or (file == needs_fuzz and file.is_file()) + for needs_fuzz in LIBRARY_FUZZER_PATHS + ) + + def process_changed_files(changed_files: Set[Path]) -> Outputs: run_tests = False run_ci_fuzz = False + run_ci_fuzz_stdlib = False run_docs = False run_windows_tests = False run_windows_msi = False + platforms_changed = set() + has_platform_specific_change = True + for file in changed_files: # Documentation files doc_or_misc = file.parts[0] in {"Doc", "Misc"} doc_file = file.suffix in SUFFIXES_DOCUMENTATION or doc_or_misc if file.parent == GITHUB_WORKFLOWS_PATH: - if file.name == "build.yml": - run_tests = run_ci_fuzz = True - if file.name == "reusable-docs.yml": + if file.name in ("build.yml", "reusable-cifuzz.yml"): + run_tests = run_ci_fuzz = run_ci_fuzz_stdlib = run_windows_tests = True + has_platform_specific_change = False + continue + if file.name in ("reusable-docs.yml", "reusable-check-html-ids.yml"): run_docs = True + continue + if file.name == "reusable-windows.yml": + run_tests = True + run_windows_tests = True + continue if file.name == "reusable-windows-msi.yml": run_windows_msi = True - - if not ( - doc_file - or file == GITHUB_CODEOWNERS_PATH - or file.name in CONFIGURATION_FILE_NAMES - ): + continue + if file.name == "reusable-macos.yml": + run_tests = True + platforms_changed.add("macos") + continue + if file.name == "reusable-emscripten.yml": + run_tests = True + platforms_changed.add("emscripten") + continue + if file.name == "reusable-wasi.yml": + run_tests = True + platforms_changed.add("wasi") + continue + + if not doc_file and file not in RUN_TESTS_IGNORE: run_tests = True - if file not in UNIX_BUILD_SYSTEM_FILE_NAMES: - run_windows_tests = True + platform = get_file_platform(file) + if platform is not None: + platforms_changed.add(platform) + else: + has_platform_specific_change = False + if file not in UNIX_BUILD_SYSTEM_FILE_NAMES: + run_windows_tests = True # The fuzz tests are pretty slow so they are executed only for PRs # changing relevant files. @@ -150,6 +281,8 @@ def process_changed_files(changed_files: Set[Path]) -> Outputs: ("Modules", "_xxtestfuzz"), }: run_ci_fuzz = True + if not run_ci_fuzz_stdlib and is_fuzzable_library_file(file): + run_ci_fuzz_stdlib = True # Check for changed documentation-related files if doc_file: @@ -159,12 +292,43 @@ def process_changed_files(changed_files: Set[Path]) -> Outputs: if file.parts[:2] == ("Tools", "msi"): run_windows_msi = True + # Check which platform specific tests to run + if run_tests: + if not has_platform_specific_change or not platforms_changed: + run_android = True + run_emscripten = True + run_ios = True + run_macos = True + run_ubuntu = True + run_wasi = True + else: + run_android = "android" in platforms_changed + run_emscripten = "emscripten" in platforms_changed + run_ios = "ios" in platforms_changed + run_macos = "macos" in platforms_changed + run_ubuntu = False + run_wasi = "wasi" in platforms_changed + else: + run_android = False + run_emscripten = False + run_ios = False + run_macos = False + run_ubuntu = False + run_wasi = False + return Outputs( + run_android=run_android, run_ci_fuzz=run_ci_fuzz, + run_ci_fuzz_stdlib=run_ci_fuzz_stdlib, run_docs=run_docs, + run_emscripten=run_emscripten, + run_ios=run_ios, + run_macos=run_macos, run_tests=run_tests, - run_windows_tests=run_windows_tests, + run_ubuntu=run_ubuntu, + run_wasi=run_wasi, run_windows_msi=run_windows_msi, + run_windows_tests=run_windows_tests, ) @@ -191,11 +355,10 @@ def write_github_output(outputs: Outputs) -> None: return with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f: - f.write(f"run-ci-fuzz={bool_lower(outputs.run_ci_fuzz)}\n") - f.write(f"run-docs={bool_lower(outputs.run_docs)}\n") - f.write(f"run-tests={bool_lower(outputs.run_tests)}\n") - f.write(f"run-windows-tests={bool_lower(outputs.run_windows_tests)}\n") - f.write(f"run-windows-msi={bool_lower(outputs.run_windows_msi)}\n") + for field in fields(outputs): + name = field.name.replace("_", "-") + val = bool_lower(getattr(outputs, field.name)) + f.write(f"{name}={val}\n") def bool_lower(value: bool, /) -> str: diff --git a/Tools/build/consts_getter.py b/Tools/build/consts_getter.py new file mode 100644 index 00000000000000..4cd454448ba2a4 --- /dev/null +++ b/Tools/build/consts_getter.py @@ -0,0 +1,20 @@ +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +INTERNAL = ROOT / "Include" / "internal" + +def get_nsmallnegints_and_nsmallposints() -> tuple[int, int]: + nsmallposints = None + nsmallnegints = None + with open(INTERNAL / "pycore_runtime_structs.h") as infile: + for line in infile: + if line.startswith("#define _PY_NSMALLPOSINTS"): + nsmallposints = int(line.split()[-1]) + elif line.startswith("#define _PY_NSMALLNEGINTS"): + nsmallnegints = int(line.split()[-1]) + break + else: + raise NotImplementedError + assert nsmallposints + assert nsmallnegints + return nsmallnegints, nsmallposints diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py index 2b9f03aebb6d7e..477c3d0f5b30d5 100644 --- a/Tools/build/deepfreeze.py +++ b/Tools/build/deepfreeze.py @@ -17,6 +17,7 @@ import time import types +import consts_getter import umarshal TYPE_CHECKING = False @@ -362,7 +363,9 @@ def _generate_int_for_bits(self, name: str, i: int, digit: int) -> None: self.write(f".ob_digit = {{ {ds} }},") def generate_int(self, name: str, i: int) -> str: - if -5 <= i <= 256: + nsmallnegints, nsmallposints = consts_getter.get_nsmallnegints_and_nsmallposints() + + if -nsmallnegints <= i <= nsmallposints: return f"(PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + {i}]" if i >= 0: name = f"const_int_{i}" diff --git a/Tools/build/freeze_modules.py b/Tools/build/freeze_modules.py index 3c43f7e3bbe8ca..b8b17ceb4f4291 100644 --- a/Tools/build/freeze_modules.py +++ b/Tools/build/freeze_modules.py @@ -50,10 +50,10 @@ ('stdlib - startup, without site (python -S)', [ 'abc', 'codecs', - # For now we do not freeze the encodings, due # to the noise all - # those extra modules add to the text printed during the build. - # (See https://github.com/python/cpython/pull/28398#pullrequestreview-756856469.) - #'<encodings.*>', + '<encodings>', + 'encodings.aliases', + 'encodings.utf_8', + 'encodings._win_cp_codecs', 'io', ]), ('stdlib - startup, with site', [ @@ -66,6 +66,9 @@ 'site', 'stat', ]), + ('pythonrun - interactive', [ + 'linecache', + ]), ('runpy - run module with -m', [ "importlib.util", "importlib.machinery", diff --git a/Tools/build/generate-build-details.py b/Tools/build/generate-build-details.py index 8cd23e2f54f529..8272635bc627d6 100644 --- a/Tools/build/generate-build-details.py +++ b/Tools/build/generate-build-details.py @@ -48,14 +48,16 @@ def generate_data(schema_version: str) -> collections.defaultdict[str, Any]: #data['base_interpreter'] = sys._base_executable data['base_interpreter'] = os.path.join( sysconfig.get_path('scripts'), - 'python' + sysconfig.get_config_var('VERSION'), + "python" + + sysconfig.get_config_var('LDVERSION') + + sysconfig.get_config_var('EXE'), ) data['platform'] = sysconfig.get_platform() data['language']['version'] = sysconfig.get_python_version() data['language']['version_info'] = version_info_to_dict(sys.version_info) - data['implementation'] = vars(sys.implementation) + data['implementation'] = vars(sys.implementation).copy() data['implementation']['version'] = version_info_to_dict(sys.implementation.version) # Fix cross-compilation if '_multiarch' in data['implementation']: @@ -104,7 +106,7 @@ def generate_data(schema_version: str) -> collections.defaultdict[str, Any]: data['abi']['extension_suffix'] = sysconfig.get_config_var('EXT_SUFFIX') # EXTENSION_SUFFIXES has been constant for a long time, and currently we - # don't have a better information source to find the stable ABI suffix. + # don't have a better information source to find the stable ABI suffix. for suffix in importlib.machinery.EXTENSION_SUFFIXES: if suffix.startswith('.abi'): data['abi']['stable_abi_suffix'] = suffix @@ -133,33 +135,51 @@ def generate_data(schema_version: str) -> collections.defaultdict[str, Any]: def make_paths_relative(data: dict[str, Any], config_path: str | None = None) -> None: # Make base_prefix relative to the config_path directory if config_path: - data['base_prefix'] = os.path.relpath(data['base_prefix'], os.path.dirname(config_path)) + data['base_prefix'] = relative_path(data['base_prefix'], + os.path.dirname(config_path)) + base_prefix = data['base_prefix'] + # Update path values to make them relative to base_prefix - PATH_KEYS = [ + PATH_KEYS = ( 'base_interpreter', 'libpython.dynamic', 'libpython.dynamic_stableabi', 'libpython.static', 'c_api.headers', 'c_api.pkgconfig_path', - ] + ) for entry in PATH_KEYS: - parent, _, child = entry.rpartition('.') + *parents, child = entry.split('.') # Get the key container object try: container = data - for part in parent.split('.'): + for part in parents: container = container[part] + if child not in container: + raise KeyError(child) current_path = container[child] except KeyError: continue # Get the relative path - new_path = os.path.relpath(current_path, data['base_prefix']) + new_path = relative_path(current_path, base_prefix) # Join '.' so that the path is formated as './path' instead of 'path' new_path = os.path.join('.', new_path) container[child] = new_path +def relative_path(path: str, base: str) -> str: + if os.name != 'nt': + return os.path.relpath(path, base) + + # There are no relative paths between drives on Windows. + path_drv, _ = os.path.splitdrive(path) + base_drv, _ = os.path.splitdrive(base) + if path_drv.lower() == base_drv.lower(): + return os.path.relpath(path, base) + + return path + + def main() -> None: parser = argparse.ArgumentParser(exit_on_error=False) parser.add_argument('location') @@ -186,8 +206,9 @@ def main() -> None: make_paths_relative(data, args.config_file_path) json_output = json.dumps(data, indent=2) - with open(args.location, 'w') as f: - print(json_output, file=f) + with open(args.location, 'w', encoding='utf-8') as f: + f.write(json_output) + f.write('\n') if __name__ == '__main__': diff --git a/Tools/build/generate_global_objects.py b/Tools/build/generate_global_objects.py index 94905b3756d0d8..5b188e338bab36 100644 --- a/Tools/build/generate_global_objects.py +++ b/Tools/build/generate_global_objects.py @@ -3,6 +3,8 @@ import os.path import re +import consts_getter + SCRIPT_NAME = 'Tools/build/generate_global_objects.py' __file__ = os.path.abspath(__file__) ROOT = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) @@ -274,20 +276,7 @@ def generate_global_strings(identifiers, strings): def generate_runtime_init(identifiers, strings): - # First get some info from the declarations. - nsmallposints = None - nsmallnegints = None - with open(os.path.join(INTERNAL, 'pycore_runtime_structs.h')) as infile: - for line in infile: - if line.startswith('#define _PY_NSMALLPOSINTS'): - nsmallposints = int(line.split()[-1]) - elif line.startswith('#define _PY_NSMALLNEGINTS'): - nsmallnegints = int(line.split()[-1]) - break - else: - raise NotImplementedError - assert nsmallposints - assert nsmallnegints + nsmallnegints, nsmallposints = consts_getter.get_nsmallnegints_and_nsmallposints() # Then target the runtime initializer. filename = os.path.join(INTERNAL, 'pycore_runtime_init_generated.h') diff --git a/Tools/build/generate_levenshtein_examples.py b/Tools/build/generate_levenshtein_examples.py index 30dcc7cf1a1479..2396c8040ca539 100644 --- a/Tools/build/generate_levenshtein_examples.py +++ b/Tools/build/generate_levenshtein_examples.py @@ -13,7 +13,7 @@ _CASE_COST = 1 -def _substitution_cost(ch_a, ch_b): +def _substitution_cost(ch_a: str, ch_b: str) -> int: if ch_a == ch_b: return 0 if ch_a.lower() == ch_b.lower(): @@ -22,7 +22,7 @@ def _substitution_cost(ch_a, ch_b): @lru_cache(None) -def levenshtein(a, b): +def levenshtein(a: str, b: str) -> int: if not a or not b: return (len(a) + len(b)) * _MOVE_COST option1 = levenshtein(a[:-1], b[:-1]) + _substitution_cost(a[-1], b[-1]) @@ -31,7 +31,7 @@ def levenshtein(a, b): return min(option1, option2, option3) -def main(): +def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('output_path', metavar='FILE', type=str) parser.add_argument('--overwrite', dest='overwrite', action='store_const', @@ -48,7 +48,7 @@ def main(): ) return - examples = set() + examples: set[tuple[str, str, int]] = set() # Create a lot of non-empty examples, which should end up with a Gauss-like # distribution for even costs (moves) and odd costs (case substitutions). while len(examples) < 9990: diff --git a/Tools/build/generate_sbom.py b/Tools/build/generate_sbom.py index 968397728b2f67..6975d9687f815d 100644 --- a/Tools/build/generate_sbom.py +++ b/Tools/build/generate_sbom.py @@ -56,9 +56,6 @@ class PackageFiles(typing.NamedTuple): # values to 'exclude' if we create new files within tracked # directories that aren't sourced from third-party packages. PACKAGE_TO_FILES = { - "mpdecimal": PackageFiles( - include=["Modules/_decimal/libmpdec/**"] - ), "expat": PackageFiles( include=["Modules/expat/**"], exclude=[ @@ -242,14 +239,14 @@ def check_sbom_packages(sbom_data: dict[str, typing.Any]) -> None: ) # libexpat specifies its expected rev in a refresh script. - if package["name"] == "libexpat": + if package["name"] == "expat": libexpat_refresh_sh = (CPYTHON_ROOT_DIR / "Modules/expat/refresh.sh").read_text() libexpat_expected_version_match = re.search( r"expected_libexpat_version=\"([0-9]+\.[0-9]+\.[0-9]+)\"", libexpat_refresh_sh ) libexpat_expected_sha256_match = re.search( - r"expected_libexpat_sha256=\"[a-f0-9]{40}\"", + r"expected_libexpat_sha256=\"([a-f0-9]{64})\"", libexpat_refresh_sh ) libexpat_expected_version = libexpat_expected_version_match and libexpat_expected_version_match.group(1) diff --git a/Tools/build/generate_slots.py b/Tools/build/generate_slots.py new file mode 100755 index 00000000000000..1fdec6efa50933 --- /dev/null +++ b/Tools/build/generate_slots.py @@ -0,0 +1,399 @@ +#!/usr/bin/python +"""Generate type/module slot files +""" + +# See the input file (Python/slots.toml) for a description of its format. + +import io +import sys +import json +import tomllib +import argparse +import functools +import contextlib +import collections +from pathlib import Path + +GENERATED_BY = 'Generated by Tools/build/generate_slots.py' + +REPO_ROOT = Path(__file__).parent.parent.parent +DEFAULT_INPUT_PATH = REPO_ROOT / 'Python/slots.toml' +INCLUDE_PATH = REPO_ROOT / 'Include' +DEFAULT_PUBLIC_HEADER_PATH = INCLUDE_PATH / 'slots_generated.h' +DEFAULT_PRIVATE_HEADER_PATH = INCLUDE_PATH / 'internal/pycore_slots_generated.h' +DEFAULT_C_PATH = REPO_ROOT / 'Python/slots_generated.c' + +TABLES = { + 'tp': 'ht_type', + 'am': 'as_async', + 'nb': 'as_number', + 'mp': 'as_mapping', + 'sq': 'as_sequence', + 'bf': 'as_buffer', +} + + +class SlotInfo: + def __init__(self, id, data): + self.id = id + self.kind = data['kind'] + self._data = data + try: + self.name = data['name'] + except KeyError: + self.name = '/'.join(data["equivalents"].values()) + else: + assert self.name.isidentifier + + @functools.cached_property + def equivalents(self): + return self._data['equivalents'] + + @functools.cached_property + def dtype(self): + try: + return self._data['dtype'] + except KeyError: + if self.is_type_field: + return 'func' + raise + + @functools.cached_property + def functype(self): + return self._data['functype'] + + @functools.cached_property + def is_type_field(self): + return self._data.get('is_type_field') + + @functools.cached_property + def type_field(self): + assert self.is_type_field + return self._data.get('field', self.name.removeprefix('Py_')) + + @functools.cached_property + def type_table_ident(self): + assert self.is_type_field + return self._data.get('table', self.type_field[:2]) + + @functools.cached_property + def duplicate_handling(self): + return self._data.get('duplicates', 'reject') + + @functools.cached_property + def null_handling(self): + try: + return self._data['nulls'] + except KeyError: + if self.kind == 'compat': + return 'allow' + if self.dtype in {'ptr', 'func'}: + return 'reject' + return 'allow' + + @functools.cached_property + def must_be_static(self): + return self._data.get('must_be_static', False) + + +def parse_slots(file): + toml_contents = tomllib.load(file) + result = [None] * len(toml_contents) + for key, data in toml_contents.items(): + slot_id = int(key) + try: + if result[slot_id]: + raise ValueError(f'slot ID {slot_id} repeated') + result[slot_id] = SlotInfo(slot_id, data) + except Exception as e: + e.add_note(f'handling slot {slot_id}') + raise + return result + + +class CWriter: + """Simple helper for generating C code""" + + def __init__(self, file): + self.file = file + self.indent = '' + self(f'/* {GENERATED_BY} */') + self() + + def out(self, *args, **kwargs): + """print args to the file, with current indent at the start""" + print(self.indent, end='', file=self.file) + print(*args, file=self.file, **kwargs) + + __call__ = out + + @contextlib.contextmanager + def block(self, header=None, end=''): + """Context for a {}-enclosed block of C""" + if header is None: + self.out('{') + else: + self.out(header, '{') + old_indent = self.indent + self.indent += ' ' + yield + self.indent = old_indent + self.out('}' + end) + + +def write_public_header(f, slots): + out = CWriter(f) + out(f'#ifndef _PY_HAVE_SLOTS_GENERATED_H') + out(f'#define _PY_HAVE_SLOTS_GENERATED_H') + out() + out(f'#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)') + out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) NEW') + out(f'#else') + out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD') + out(f'#endif') + out() + compat_ids = {} + for slot in slots: + if slot.kind == 'compat': + for new_name in slot.equivalents.values(): + compat_ids[new_name] = slot.id + for slot in slots: + if slot.kind == 'compat': + continue + slot_id = slot.id + if compat := compat_ids.get(slot.name): + slot_id = f'_Py_SLOT_COMPAT_VALUE({compat}, {slot_id})' + out(f'#define {slot.name} {slot_id}') + out() + out(f'#define _Py_slot_COUNT {len(slots)}') + out(f'#endif /* _PY_HAVE_SLOTS_GENERATED_H */') + + +def write_private_header(f, slots): + out = CWriter(f) + + def add_case(slot): + out(out(f' case {slot.id}:')) + + slots_by_name = {slot.name: slot for slot in slots} + + out(f'#ifndef _PY_HAVE_INTERNAL_SLOTS_GENERATED_H') + out(f'#define _PY_HAVE_INTERNAL_SLOTS_GENERATED_H') + for kind in 'type', 'mod': + out() + out(f'static inline uint16_t') + out(f'_PySlot_resolve_{kind}_slot(uint16_t slot_id)') + with out.block(): + with out.block('switch (slot_id)'): + good_slots = [] + for slot in slots: + if slot.kind == 'compat': + new_slot = slots_by_name[slot.equivalents[kind]] + out(f'case {slot.id}:') + out(f' return {new_slot.name};') + elif slot.kind in {kind, 'slot'}: + good_slots.append(f'case {slot.name}:') + for case in good_slots: + out(case) + out(f' return slot_id;') + out(f'default:') + out(f' return Py_slot_invalid;') + out() + out(f'static inline void*') + out(f'_PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id)') + with out.block(): + with out.block('switch (slot_id)'): + for slot in slots: + if slot.is_type_field: + field = slot.type_field + table_ident = slot.type_table_ident + if table_ident == 'tp': + out(f'case {slot.name}:') + out(f' return (void*)tp->{field};') + else: + if table_ident == 'ht': + cond = 'tp->tp_flags & Py_TPFLAGS_HEAPTYPE' + val = f'((PyHeapTypeObject*)tp)->{field}' + else: + table = TABLES[table_ident] + cond = f'tp->tp_{table}' + val = f'tp->tp_{table}->{field}' + out(f'case {slot.name}:') + out(f' if (!({cond})) return NULL;') + out(f' return (void*){val};') + out(f'_PySlot_err_bad_slot("PyType_GetSlot", slot_id);') + out(f'return NULL;') + out() + out(f'static inline void') + out(f'_PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht,', + f'PySlot slot)') + with out.block(): + with out.block('switch (slot.sl_id)'): + for slot in slots: + if slot.is_type_field: + field = slot.type_field + table_ident = slot.type_table_ident + if table_ident == 'ht': + continue + table = TABLES[table_ident] + if slot.dtype == 'func': + functype = f'({slot.functype})' + else: + functype = '' + out(f'case {slot.name}:') + out(f' ht->{table}.{field} = {functype}slot.sl_{slot.dtype};') + out(f' break;') + out() + out(f'static inline _PySlot_DTYPE') + out(f'_PySlot_get_dtype(uint16_t slot_id)') + with out.block(): + with out.block('switch (slot_id)'): + for slot in slots: + if slot.kind == 'compat': + continue + dtype = slot.dtype + name = slot.name + out(f'case {name}: return _PySlot_DTYPE_{dtype.upper()};') + out(f'default: return _PySlot_DTYPE_VOID;') + out() + out(f'static inline _PySlot_PROBLEM_HANDLING') + out(f'_PySlot_get_duplicate_handling(uint16_t slot_id)') + with out.block(): + with out.block('switch (slot_id)'): + results = collections.defaultdict(list) + for slot in slots: + if slot.kind == 'compat': + continue + handling = slot.duplicate_handling + results[handling.upper()].append(f'case {slot.name}:') + results.pop('REJECT') + for handling, cases in results.items(): + for case in cases: + out(case) + out(f' return _PySlot_PROBLEM_{handling};') + out(f'default:') + out(f' return _PySlot_PROBLEM_REJECT;') + out() + out(f'static inline _PySlot_PROBLEM_HANDLING') + out(f'_PySlot_get_null_handling(uint16_t slot_id)') + with out.block(): + with out.block('switch (slot_id)'): + results = collections.defaultdict(list) + for slot in slots: + if slot.kind == 'compat': + continue + handling = slot.null_handling + if handling is None: + if slot.kind != 'compat' and slot.dtype in {'ptr', 'func'}: + handling = 'reject' + else: + handling = 'allow' + results[handling.upper()].append(f'case {slot.name}:') + results.pop('REJECT') + for handling, cases in results.items(): + for case in cases: + out(case) + out(f' return _PySlot_PROBLEM_{handling};') + out(f'default:') + out(f' return _PySlot_PROBLEM_REJECT;') + out() + out(f'static inline bool') + out(f'_PySlot_get_must_be_static(uint16_t slot_id)') + with out.block(): + with out.block('switch (slot_id)'): + cases = [] + for slot in slots: + if slot.must_be_static: + out(f'case {slot.name}: return true;') + out(f'return false;') + out() + out(f'#endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */') + + +def write_c(f, slots): + out = CWriter(f) + out('#include "Python.h"') + out('#include "pycore_slots.h" // _PySlot_names') + out() + with out.block(f'const char *const _PySlot_names[] =', end=';'): + for slot in slots: + out(f'"{slot.name}",') + out('NULL') + + +@contextlib.contextmanager +def replace_file(filename): + file_path = Path(filename) + with io.StringIO() as sio: + yield sio + try: + old_text = file_path.read_text() + except FileNotFoundError: + old_text = None + new_text = sio.getvalue() + if old_text == new_text: + print(f'{filename}: not modified', file=sys.stderr) + else: + print(f'{filename}: writing new content', file=sys.stderr) + file_path.write_text(new_text) + + +def main(argv): + if len(argv) == 1: + # No sens calling this with no arguments. + argv.append('--help') + + parser = argparse.ArgumentParser(prog=argv[0], description=__doc__) + parser.add_argument( + '-i', '--input', default=DEFAULT_INPUT_PATH, + help=f'the input file (default: {DEFAULT_INPUT_PATH})') + parser.add_argument( + '--generate-all', action=argparse.BooleanOptionalAction, + help='write all output files to their default locations') + parser.add_argument( + '-j', '--jsonl', action=argparse.BooleanOptionalAction, + help='write info to stdout in "JSON Lines" format (one JSON per line)') + outfile_group = parser.add_argument_group( + 'output files', + description='By default, no files are generated. Use --generate-all ' + + 'or the options below to generate them.') + outfile_group.add_argument( + '-H', '--public-header', + help='file into which to write the public header') + outfile_group.add_argument( + '-I', '--private-header', + help='file into which to write the private header') + outfile_group.add_argument( + '-C', '--cfile', + help='file into which to write internal C code') + args = parser.parse_args(argv[1:]) + + if args.generate_all: + if args.public_header is None: + args.public_header = DEFAULT_PUBLIC_HEADER_PATH + if args.private_header is None: + args.private_header = DEFAULT_PRIVATE_HEADER_PATH + if args.cfile is None: + args.cfile = DEFAULT_C_PATH + + with open(args.input, 'rb') as f: + slots = parse_slots(f) + + if args.jsonl: + for slot in slots: + print(json.dumps(slot.to_dict())) + + if args.public_header: + with replace_file(args.public_header) as f: + write_public_header(f, slots) + + if args.private_header: + with replace_file(args.private_header) as f: + write_private_header(f, slots) + + if args.cfile: + with replace_file(args.cfile) as f: + write_c(f, slots) + +if __name__ == "__main__": + main(sys.argv) diff --git a/Tools/build/generate_stdlib_module_names.py b/Tools/build/generate_stdlib_module_names.py index 88414cdbb37a8d..f8828a56b4c7da 100644 --- a/Tools/build/generate_stdlib_module_names.py +++ b/Tools/build/generate_stdlib_module_names.py @@ -1,9 +1,12 @@ # This script lists the names of standard library modules # to update Python/stdlib_module_names.h +from __future__ import annotations + import _imp import os.path import sys import sysconfig +from typing import TextIO from check_extension_modules import ModuleChecker @@ -39,6 +42,7 @@ 'test', 'xxlimited', 'xxlimited_35', + 'xxlimited_3_13', 'xxsubtype', } @@ -48,12 +52,12 @@ } # Built-in modules -def list_builtin_modules(names): +def list_builtin_modules(names: set[str]) -> None: names |= set(sys.builtin_module_names) # Pure Python modules (Lib/*.py) -def list_python_modules(names): +def list_python_modules(names: set[str]) -> None: for filename in os.listdir(STDLIB_PATH): if not filename.endswith(".py"): continue @@ -62,7 +66,7 @@ def list_python_modules(names): # Packages in Lib/ -def list_packages(names): +def list_packages(names: set[str]) -> None: for name in os.listdir(STDLIB_PATH): if name in IGNORE: continue @@ -76,16 +80,16 @@ def list_packages(names): # Built-in and extension modules built by Modules/Setup* # includes Windows and macOS extensions. -def list_modules_setup_extensions(names): +def list_modules_setup_extensions(names: set[str]) -> None: checker = ModuleChecker() names.update(checker.list_module_names(all=True)) # List frozen modules of the PyImport_FrozenModules list (Python/frozen.c). # Use the "./Programs/_testembed list_frozen" command. -def list_frozen(names): +def list_frozen(names: set[str]) -> None: submodules = set() - for name in _imp._frozen_module_names(): + for name in _imp._frozen_module_names(): # type: ignore[attr-defined] # To skip __hello__, __hello_alias__ and etc. if name.startswith('__'): continue @@ -101,8 +105,8 @@ def list_frozen(names): raise Exception(f'unexpected frozen submodules: {sorted(submodules)}') -def list_modules(): - names = set() +def list_modules() -> set[str]: + names: set[str] = set() list_builtin_modules(names) list_modules_setup_extensions(names) @@ -127,7 +131,7 @@ def list_modules(): return names -def write_modules(fp, names): +def write_modules(fp: TextIO, names: set[str]) -> None: print(f"// Auto-generated by {SCRIPT_NAME}.", file=fp) print("// List used to create sys.stdlib_module_names.", file=fp) @@ -138,7 +142,7 @@ def write_modules(fp, names): print("};", file=fp) -def main(): +def main() -> None: if not sysconfig.is_python_build(): print(f"ERROR: {sys.executable} is not a Python build", file=sys.stderr) diff --git a/Tools/build/mypy.ini b/Tools/build/mypy.ini index 123dc895f90a1f..485c9314cf7001 100644 --- a/Tools/build/mypy.ini +++ b/Tools/build/mypy.ini @@ -3,10 +3,15 @@ # Please, when adding new files here, also add them to: # .github/workflows/mypy.yml files = + Tools/build/check_extension_modules.py, + Tools/build/check_warnings.py, Tools/build/compute-changes.py, + Tools/build/consts_getter.py, Tools/build/deepfreeze.py, Tools/build/generate-build-details.py, + Tools/build/generate_levenshtein_examples.py, Tools/build/generate_sbom.py, + Tools/build/generate_stdlib_module_names.py, Tools/build/verify_ensurepip_wheels.py, Tools/build/update_file.py, Tools/build/umarshal.py @@ -19,8 +24,6 @@ python_version = 3.10 # ...And be strict: strict = True -strict_bytes = True -local_partial_types = True extra_checks = True enable_error_code = ignore-without-code,redundant-expr,truthy-bool,possibly-undefined warn_unreachable = True diff --git a/Tools/build/smelly.py b/Tools/build/smelly.py index 9a360412a73a4d..17547d4d916e9f 100755 --- a/Tools/build/smelly.py +++ b/Tools/build/smelly.py @@ -1,7 +1,14 @@ #!/usr/bin/env python -# Script checking that all symbols exported by libpython start with Py or _Py +"""Check exported symbols -import os.path +Check that all symbols exported by CPython (libpython, stdlib extension +modules, and similar) start with Py or _Py, or are covered by an exception. +""" + +import argparse +import dataclasses +import functools +import pathlib import subprocess import sys import sysconfig @@ -18,166 +25,174 @@ # "Legacy": some old symbols are prefixed by "PY_". EXCEPTIONS = frozenset({ 'PY_TIMEOUT_MAX', + '__jit_debug_descriptor', + '__jit_debug_register_code', }) IGNORED_EXTENSION = "_ctypes_test" -# Ignore constructor and destructor functions -IGNORED_SYMBOLS = {'_init', '_fini'} - -def is_local_symbol_type(symtype): - # Ignore local symbols. - - # If lowercase, the symbol is usually local; if uppercase, the symbol - # is global (external). There are however a few lowercase symbols that - # are shown for special global symbols ("u", "v" and "w"). - if symtype.islower() and symtype not in "uvw": - return True - # Ignore the initialized data section (d and D) and the BSS data - # section. For example, ignore "__bss_start (type: B)" - # and "_edata (type: D)". - if symtype in "bBdD": +@dataclasses.dataclass +class Library: + path: pathlib.Path + is_dynamic: bool + + @functools.cached_property + def is_ignored(self): + name_without_extemnsions = self.path.name.partition('.')[0] + return name_without_extemnsions == IGNORED_EXTENSION + + +@dataclasses.dataclass +class Symbol: + name: str + type: str + library: str + + def __str__(self): + return f"{self.name!r} (type {self.type}) from {self.library.path}" + + @functools.cached_property + def is_local(self): + # If lowercase, the symbol is usually local; if uppercase, the symbol + # is global (external). There are however a few lowercase symbols that + # are shown for special global symbols ("u", "v" and "w"). + if self.type.islower() and self.type not in "uvw": + return True + + return False + + @functools.cached_property + def is_smelly(self): + if self.is_local: + return False + if self.name.startswith(ALLOWED_PREFIXES): + return False + if self.name in EXCEPTIONS: + return False + if not self.library.is_dynamic and self.name.startswith( + ALLOWED_STATIC_PREFIXES): + return False + if self.library.is_ignored: + return False return True - return False + @functools.cached_property + def _sort_key(self): + return self.name, self.library.path + def __lt__(self, other_symbol): + return self._sort_key < other_symbol._sort_key -def get_exported_symbols(library, dynamic=False): - print(f"Check that {library} only exports symbols starting with Py or _Py") +def get_exported_symbols(library): # Only look at dynamic symbols args = ['nm', '--no-sort'] - if dynamic: + if library.is_dynamic: args.append('--dynamic') - args.append(library) - print(f"+ {' '.join(args)}") + args.append(library.path) proc = subprocess.run(args, stdout=subprocess.PIPE, encoding='utf-8') if proc.returncode: + print("+", args) sys.stdout.write(proc.stdout) sys.exit(proc.returncode) stdout = proc.stdout.rstrip() if not stdout: raise Exception("command output is empty") - return stdout - - -def get_smelly_symbols(stdout, dynamic=False): - smelly_symbols = [] - python_symbols = [] - local_symbols = [] + symbols = [] for line in stdout.splitlines(): - # Split line '0000000000001b80 D PyTextIOWrapper_Type' if not line: continue + # Split lines like '0000000000001b80 D PyTextIOWrapper_Type' parts = line.split(maxsplit=2) + # Ignore lines like ' U PyDict_SetItemString' + # and headers like 'pystrtod.o:' if len(parts) < 3: continue - symtype = parts[1].strip() - symbol = parts[-1] - result = f'{symbol} (type: {symtype})' + symbol = Symbol(name=parts[-1], type=parts[1], library=library) + if not symbol.is_local: + symbols.append(symbol) - if (symbol.startswith(ALLOWED_PREFIXES) or - symbol in EXCEPTIONS or - (not dynamic and symbol.startswith(ALLOWED_STATIC_PREFIXES))): - python_symbols.append(result) - continue + return symbols - if is_local_symbol_type(symtype): - local_symbols.append(result) - elif symbol in IGNORED_SYMBOLS: - local_symbols.append(result) - else: - smelly_symbols.append(result) - if local_symbols: - print(f"Ignore {len(local_symbols)} local symbols") - return smelly_symbols, python_symbols - - -def check_library(library, dynamic=False): - nm_output = get_exported_symbols(library, dynamic) - smelly_symbols, python_symbols = get_smelly_symbols(nm_output, dynamic) - - if not smelly_symbols: - print(f"OK: no smelly symbol found ({len(python_symbols)} Python symbols)") - return 0 - - print() - smelly_symbols.sort() - for symbol in smelly_symbols: - print(f"Smelly symbol: {symbol}") - - print() - print(f"ERROR: Found {len(smelly_symbols)} smelly symbols!") - return len(smelly_symbols) - - -def check_extensions(): - print(__file__) +def get_extension_libraries(): # This assumes pybuilddir.txt is in same directory as pyconfig.h. # In the case of out-of-tree builds, we can't assume pybuilddir.txt is # in the source folder. - config_dir = os.path.dirname(sysconfig.get_config_h_filename()) - filename = os.path.join(config_dir, "pybuilddir.txt") + config_dir = pathlib.Path(sysconfig.get_config_h_filename()).parent try: - with open(filename, encoding="utf-8") as fp: - pybuilddir = fp.readline() - except FileNotFoundError: - print(f"Cannot check extensions because {filename} does not exist") - return True - - print(f"Check extension modules from {pybuilddir} directory") - builddir = os.path.join(config_dir, pybuilddir) - nsymbol = 0 - for name in os.listdir(builddir): - if not name.endswith(".so"): + config_dir = config_dir.relative_to(pathlib.Path.cwd(), walk_up=True) + except ValueError: + pass + filename = config_dir / "pybuilddir.txt" + pybuilddir = filename.read_text().strip() + + builddir = config_dir / pybuilddir + result = [] + for path in sorted(builddir.glob('**/*.so')): + if path.stem == IGNORED_EXTENSION: continue - if IGNORED_EXTENSION in name: - print() - print(f"Ignore extension: {name}") - continue - - print() - filename = os.path.join(builddir, name) - nsymbol += check_library(filename, dynamic=True) + result.append(Library(path, is_dynamic=True)) - return nsymbol + return result def main(): - nsymbol = 0 + parser = argparse.ArgumentParser( + description=__doc__.split('\n', 1)[-1]) + parser.add_argument('-v', '--verbose', action='store_true', + help='be verbose (currently: print out all symbols)') + args = parser.parse_args() + + libraries = [] # static library - LIBRARY = sysconfig.get_config_var('LIBRARY') - if not LIBRARY: - raise Exception("failed to get LIBRARY variable from sysconfig") - if os.path.exists(LIBRARY): - nsymbol += check_library(LIBRARY) + try: + LIBRARY = pathlib.Path(sysconfig.get_config_var('LIBRARY')) + except TypeError as exc: + raise Exception("failed to get LIBRARY sysconfig variable") from exc + LIBRARY = pathlib.Path(LIBRARY) + if LIBRARY.exists(): + libraries.append(Library(LIBRARY, is_dynamic=False)) # dynamic library - LDLIBRARY = sysconfig.get_config_var('LDLIBRARY') - if not LDLIBRARY: - raise Exception("failed to get LDLIBRARY variable from sysconfig") + try: + LDLIBRARY = pathlib.Path(sysconfig.get_config_var('LDLIBRARY')) + except TypeError as exc: + raise Exception("failed to get LDLIBRARY sysconfig variable") from exc if LDLIBRARY != LIBRARY: - print() - nsymbol += check_library(LDLIBRARY, dynamic=True) + libraries.append(Library(LDLIBRARY, is_dynamic=True)) # Check extension modules like _ssl.cpython-310d-x86_64-linux-gnu.so - nsymbol += check_extensions() + libraries.extend(get_extension_libraries()) - if nsymbol: - print() - print(f"ERROR: Found {nsymbol} smelly symbols in total!") - sys.exit(1) + smelly_symbols = [] + for library in libraries: + symbols = get_exported_symbols(library) + if args.verbose: + print(f"{library.path}: {len(symbols)} symbol(s) found") + for symbol in sorted(symbols): + if args.verbose: + print(" -", symbol.name) + if symbol.is_smelly: + smelly_symbols.append(symbol) print() - print(f"OK: all exported symbols of all libraries " - f"are prefixed with {' or '.join(map(repr, ALLOWED_PREFIXES))}") + + if smelly_symbols: + print(f"Found {len(smelly_symbols)} smelly symbols in total!") + for symbol in sorted(smelly_symbols): + print(f" - {symbol.name} from {symbol.library.path}") + sys.exit(1) + + print(f"OK: all exported symbols of all libraries", + f"are prefixed with {' or '.join(map(repr, ALLOWED_PREFIXES))}", + f"or are covered by exceptions") if __name__ == "__main__": diff --git a/Tools/build/stable_abi.py b/Tools/build/stable_abi.py index 1ddd76cdd9bf64..39115b331ba642 100644 --- a/Tools/build/stable_abi.py +++ b/Tools/build/stable_abi.py @@ -232,7 +232,7 @@ def sort_key(item): 'data': 'data', 'struct': 'type', 'macro': 'macro', - # 'const': 'const', # all undocumented + 'const': 'macro', 'typedef': 'type', } diff --git a/Tools/c-analyzer/c_parser/parser/__init__.py b/Tools/c-analyzer/c_parser/parser/__init__.py index ff4f303c4a2bec..5b4b2b134584ad 100644 --- a/Tools/c-analyzer/c_parser/parser/__init__.py +++ b/Tools/c-analyzer/c_parser/parser/__init__.py @@ -116,6 +116,8 @@ * alt impl using a state machine (& tokenizer or split on delimiters) """ +import textwrap + from ..info import ParsedItem from ._info import SourceInfo @@ -208,7 +210,27 @@ def _iter_source(lines, *, maxtext=11_000, maxlines=200, showtext=False): return # At this point either the file ended prematurely # or there's "too much" text. - filename, lno, text = srcinfo.filename, srcinfo._start, srcinfo.text + filename, lno_from, lno_to = srcinfo.filename, srcinfo.start, srcinfo.end + text = srcinfo.text if len(text) > 500: text = text[:500] + '...' - raise Exception(f'unmatched text ({filename} starting at line {lno}):\n{text}') + + if srcinfo.too_much_text(maxtext): + msg = f''' + too much text, try to increase MAX_SIZES[MAXTEXT] in cpython/_parser.py + {filename} starting at line {lno_from} to {lno_to} + has code with length {len(srcinfo.text)} greater than {maxtext}: + {text} + ''' + raise RuntimeError(textwrap.dedent(msg)) + + if srcinfo.too_many_lines(maxlines): + msg = f''' + too many lines, try to increase MAX_SIZES[MAXLINES] in cpython/_parser.py + {filename} starting at line {lno_from} to {lno_to} + has code with number of lines {lno_to - lno_from} greater than {maxlines}: + {text} + ''' + raise RuntimeError(textwrap.dedent(msg)) + + raise RuntimeError(f'unmatched text ({filename} starting at line {lno_from}):\n{text}') diff --git a/Tools/c-analyzer/c_parser/parser/_info.py b/Tools/c-analyzer/c_parser/parser/_info.py index 340223db933c90..13b192e2ce982f 100644 --- a/Tools/c-analyzer/c_parser/parser/_info.py +++ b/Tools/c-analyzer/c_parser/parser/_info.py @@ -123,10 +123,16 @@ def resolve(self, kind, data, name, parent=None): def done(self): self._set_ready() + def too_much_text(self, maxtext): + return maxtext and len(self.text) > maxtext + + def too_many_lines(self, maxlines): + return maxlines and self.end - self.start > maxlines + def too_much(self, maxtext, maxlines): - if maxtext and len(self.text) > maxtext: + if self.too_much_text(maxtext): pass - elif maxlines and self.end - self.start > maxlines: + elif self.too_many_lines(maxlines): pass else: return False diff --git a/Tools/c-analyzer/c_parser/preprocessor/__init__.py b/Tools/c-analyzer/c_parser/preprocessor/__init__.py index 30a86cbd7dc494..f8d2f805cb1b19 100644 --- a/Tools/c-analyzer/c_parser/preprocessor/__init__.py +++ b/Tools/c-analyzer/c_parser/preprocessor/__init__.py @@ -16,6 +16,7 @@ from . import ( pure as _pure, gcc as _gcc, + clang as _clang, ) @@ -234,7 +235,7 @@ def handling_errors(ignore_exc=None, *, log_err=None): 'bcpp': None, # aliases/extras: 'gcc': _gcc.preprocess, - 'clang': None, + 'clang': _clang.preprocess, } diff --git a/Tools/c-analyzer/c_parser/preprocessor/clang.py b/Tools/c-analyzer/c_parser/preprocessor/clang.py new file mode 100644 index 00000000000000..738c261d1ecd80 --- /dev/null +++ b/Tools/c-analyzer/c_parser/preprocessor/clang.py @@ -0,0 +1,103 @@ +import os.path + +from . import common as _common +from . import gcc as _gcc + +_normpath = _gcc._normpath + +TOOL = 'clang' + +META_FILES = { + '<built-in>', + '<command line>', +} + + +def preprocess(filename, + incldirs=None, + includes=None, + macros=None, + samefiles=None, + cwd=None, + ): + if not cwd or not os.path.isabs(cwd): + cwd = os.path.abspath(cwd or '.') + filename = _normpath(filename, cwd) + + postargs = _gcc.POST_ARGS + basename = os.path.basename(filename) + dirname = os.path.basename(os.path.dirname(filename)) + if (basename not in _gcc.FILES_WITHOUT_INTERNAL_CAPI + and dirname not in _gcc.DIRS_WITHOUT_INTERNAL_CAPI): + postargs += ('-DPy_BUILD_CORE=1',) + + text = _common.preprocess( + TOOL, + filename, + incldirs=incldirs, + includes=includes, + macros=macros, + #preargs=PRE_ARGS, + postargs=postargs, + executable=['clang'], + compiler='unix', + cwd=cwd, + ) + return _iter_lines(text, filename, samefiles, cwd) + + +EXIT_MARKERS = {'# 2 "<built-in>" 2', '# 3 "<built-in>" 2', '# 4 "<built-in>" 2'} + + +def _iter_lines(text, reqfile, samefiles, cwd, raw=False): + lines = iter(text.splitlines()) + + # The first line is special. + # The subsequent lines are consistent. + firstlines = [ + f'# 1 "{reqfile}"', + '# 1 "<built-in>" 1', + '# 1 "<built-in>" 3', + '# 370 "<built-in>" 3', + '# 1 "<command line>" 1', + '# 1 "<built-in>" 2', + ] + for expected in firstlines: + line = next(lines) + if line != expected: + raise NotImplementedError((line, expected)) + + # Do all the CLI-provided includes. + filter_reqfile = (lambda f: _gcc._filter_reqfile(f, reqfile, samefiles)) + make_info = (lambda lno: _common.FileInfo(reqfile, lno)) + last = None + for line in lines: + assert last != reqfile, (last,) + # NOTE:condition is clang specific + if not line: + continue + lno, included, flags = _gcc._parse_marker_line(line, reqfile) + if not included: + raise NotImplementedError((line,)) + if included == reqfile: + # This will be the last one. + assert 2 in flags, (line, flags) + else: + # NOTE:first condition is specific to clang + if _normpath(included, cwd) == reqfile: + assert 1 in flags or 2 in flags, (line, flags, included, reqfile) + else: + assert 1 in flags, (line, flags, included, reqfile) + yield from _gcc._iter_top_include_lines( + lines, + _normpath(included, cwd), + cwd, + filter_reqfile, + make_info, + raw, + EXIT_MARKERS + ) + last = included + # The last one is always the requested file. + # NOTE:_normpath is clang specific + assert _normpath(included, cwd) == reqfile, (line,) diff --git a/Tools/c-analyzer/c_parser/preprocessor/gcc.py b/Tools/c-analyzer/c_parser/preprocessor/gcc.py index d20cd19f6e6d5e..92134bc1321e1b 100644 --- a/Tools/c-analyzer/c_parser/preprocessor/gcc.py +++ b/Tools/c-analyzer/c_parser/preprocessor/gcc.py @@ -11,6 +11,7 @@ '_testclinic_limited.c', 'xxlimited.c', 'xxlimited_35.c', + 'xxlimited_3_13.c', )) # C files in the fhe following directories must not be built with @@ -65,6 +66,8 @@ '-E', ) +EXIT_MARKERS = {'# 0 "<command-line>" 2', '# 1 "<command-line>" 2'} + def preprocess(filename, incldirs=None, @@ -138,6 +141,7 @@ def _iter_lines(text, reqfile, samefiles, cwd, raw=False): filter_reqfile, make_info, raw, + EXIT_MARKERS ) last = included # The last one is always the requested file. @@ -146,7 +150,7 @@ def _iter_lines(text, reqfile, samefiles, cwd, raw=False): def _iter_top_include_lines(lines, topfile, cwd, filter_reqfile, make_info, - raw): + raw, exit_markers): partial = 0 # depth files = [topfile] # We start at 1 in case there are source lines (including blank ones) @@ -154,12 +158,20 @@ def _iter_top_include_lines(lines, topfile, cwd, # _parse_marker_line() that the preprocessor reported lno as 1. lno = 1 for line in lines: - if line == '# 0 "<command-line>" 2' or line == '# 1 "<command-line>" 2': + if line in exit_markers: # We're done with this top-level include. return _lno, included, flags = _parse_marker_line(line) if included: + # HACK: + # Mixes curses.h and ncurses.h marker lines + # gcc silently passes this, while clang fails + # See: /Include/py_curses.h #if-elif directives + # And compare with preprocessor output + if os.path.basename(included) == 'curses.h': + included = os.path.join(os.path.dirname(included), 'ncurses.h') + lno = _lno included = _normpath(included, cwd) # We hit a marker line. diff --git a/Tools/c-analyzer/cpython/_analyzer.py b/Tools/c-analyzer/cpython/_analyzer.py index 6204353e9bd26a..404a81af11e39f 100644 --- a/Tools/c-analyzer/cpython/_analyzer.py +++ b/Tools/c-analyzer/cpython/_analyzer.py @@ -67,6 +67,7 @@ 'PyMethodDef', 'PyMethodDef[]', 'PyMemberDef[]', + 'PyGetSetDef', 'PyGetSetDef[]', 'PyNumberMethods', 'PySequenceMethods', @@ -75,6 +76,8 @@ 'PyBufferProcs', 'PyStructSequence_Field[]', 'PyStructSequence_Desc', + 'PyABIInfo', + 'PySlot[]', } # XXX We should normalize all cases to a single name, diff --git a/Tools/c-analyzer/cpython/_parser.py b/Tools/c-analyzer/cpython/_parser.py index 1e754040eaf1cc..d7248c34c59be4 100644 --- a/Tools/c-analyzer/cpython/_parser.py +++ b/Tools/c-analyzer/cpython/_parser.py @@ -1,5 +1,4 @@ import os.path -import re from c_parser.preprocessor import ( get_preprocessor as _get_preprocessor, @@ -18,16 +17,16 @@ def _abs(relfile): return os.path.join(REPO_ROOT, relfile) -def clean_lines(text): - """Clear out comments, blank lines, and leading/trailing whitespace.""" - lines = (line.strip() for line in text.splitlines()) - lines = (line.partition('#')[0].rstrip() - for line in lines - if line and not line.startswith('#')) - glob_all = f'{GLOB_ALL} ' - lines = (re.sub(r'^[*] ', glob_all, line) for line in lines) - lines = (_abs(line) for line in lines) - return list(lines) +def format_conf_lines(lines): + """Format conf entries for use in a TSV table.""" + return [_abs(entry) for entry in lines] + + +def format_tsv_lines(lines): + """Format entries for use in a TSV table.""" + lines = ((_abs(first), *rest) for first, *rest in lines) + lines = map('\t'.join, lines) + return list(map(str.rstrip, lines)) ''' @@ -47,255 +46,247 @@ def clean_lines(text): ''' # XXX Handle these. -# Tab separated: -EXCLUDED = clean_lines(''' -# @begin=conf@ - -# OSX -Modules/_scproxy.c # SystemConfiguration/SystemConfiguration.h - -# Windows -Modules/_winapi.c # windows.h -Modules/expat/winconfig.h -Modules/overlapped.c # winsock.h -Python/dynload_win.c # windows.h -Python/thread_nt.h - -# other OS-dependent -Python/dynload_aix.c # sys/ldr.h -Python/dynload_dl.c # dl.h -Python/dynload_hpux.c # dl.h -Python/emscripten_signal.c -Python/emscripten_syscalls.c -Python/thread_pthread.h -Python/thread_pthread_stubs.h - -# only huge constants (safe but parsing is slow) -Modules/_ssl_data_*.h -Modules/cjkcodecs/mappings_*.h -Modules/unicodedata_db.h -Modules/unicodename_db.h -Objects/unicodetype_db.h - -# generated -Python/deepfreeze/*.c -Python/frozen_modules/*.h -Python/generated_cases.c.h -Python/executor_cases.c.h -Python/optimizer_cases.c.h -# XXX: Throws errors if PY_VERSION_HEX is not mocked out -Modules/clinic/_testclinic_depr.c.h - -# not actually source -Python/bytecodes.c -Python/optimizer_bytecodes.c - -# mimalloc -Objects/mimalloc/*.c -Include/internal/mimalloc/*.h -Include/internal/mimalloc/mimalloc/*.h - -# @end=conf@ -''') +EXCLUDED = format_conf_lines([ + # macOS + 'Modules/_scproxy.c', # SystemConfiguration/SystemConfiguration.h + + # Windows + 'Modules/_winapi.c', # windows.h + 'Modules/expat/winconfig.h', + 'Modules/overlapped.c', # winsock.h + 'Python/dynload_win.c', # windows.h + 'Python/thread_nt.h', + + # other OS-dependent + 'Python/dynload_aix.c', # sys/ldr.h + 'Python/dynload_dl.c', # dl.h + 'Python/dynload_hpux.c', # dl.h + 'Python/emscripten_signal.c', + 'Python/emscripten_syscalls.c', + 'Python/emscripten_trampoline_inner.c', + 'Python/thread_pthread.h', + 'Python/thread_pthread_stubs.h', + + # only huge constants (safe but parsing is slow) + 'Modules/_ssl_data_*.h', + 'Modules/cjkcodecs/mappings_*.h', + 'Modules/unicodedata_db.h', + 'Modules/unicodename_db.h', + 'Objects/unicodetype_db.h', + + # generated + 'Python/deepfreeze/*.c', + 'Python/frozen_modules/*.h', + 'Python/generated_cases.c.h', + 'Python/executor_cases.c.h', + 'Python/optimizer_cases.c.h', + 'Python/record_functions.c.h', + 'Python/opcode_targets.h', + 'Modules/_testinternalcapi/test_targets.h', + 'Modules/_testinternalcapi/test_cases.c.h', + 'Modules/_testinternalcapi/interpreter.c', + # XXX: Throws errors if PY_VERSION_HEX is not mocked out + 'Modules/clinic/_testclinic_depr.c.h', + + # not actually source + 'Python/bytecodes.c', + 'Python/optimizer_bytecodes.c', + 'Modules/_testinternalcapi/testbytecodes.c', + + # mimalloc + 'Objects/mimalloc/*.c', + 'Include/internal/mimalloc/*.h', + 'Include/internal/mimalloc/mimalloc/*.h', +]) # XXX Fix the parser. -EXCLUDED += clean_lines(''' -# The tool should be able to parse these... - -# The problem with xmlparse.c is that something -# has gone wrong where # we handle "maybe inline actual" -# in Tools/c-analyzer/c_parser/parser/_global.py. -Modules/expat/internal.h -Modules/expat/xmlparse.c -''') - -INCL_DIRS = clean_lines(''' -# @begin=tsv@ - -glob dirname -* . -* ./Include -* ./Include/internal -* ./Include/internal/mimalloc - -Modules/_decimal/**/*.c Modules/_decimal/libmpdec -Modules/_elementtree.c Modules/expat -Modules/_hacl/*.c Modules/_hacl/include -Modules/_hacl/*.c Modules/_hacl/ -Modules/_hacl/*.h Modules/_hacl/include -Modules/_hacl/*.h Modules/_hacl/ -Modules/md5module.c Modules/_hacl/include -Modules/sha1module.c Modules/_hacl/include -Modules/sha2module.c Modules/_hacl/include -Modules/sha3module.c Modules/_hacl/include -Modules/blake2module.c Modules/_hacl/include -Modules/hmacmodule.c Modules/_hacl/include -Objects/stringlib/*.h Objects - -# possible system-installed headers, just in case -Modules/_tkinter.c /usr/include/tcl8.6 -Modules/_uuidmodule.c /usr/include/uuid -Modules/tkappinit.c /usr/include/tcl - -# @end=tsv@ -''')[1:] - -INCLUDES = clean_lines(''' -# @begin=tsv@ - -glob include - -**/*.h Python.h -Include/**/*.h object.h - -# for Py_HAVE_CONDVAR -Include/internal/pycore_gil.h pycore_condvar.h -Python/thread_pthread.h pycore_condvar.h - -# other - -Objects/stringlib/join.h stringlib/stringdefs.h -Objects/stringlib/ctype.h stringlib/stringdefs.h -Objects/stringlib/transmogrify.h stringlib/stringdefs.h -#Objects/stringlib/fastsearch.h stringlib/stringdefs.h -#Objects/stringlib/count.h stringlib/stringdefs.h -#Objects/stringlib/find.h stringlib/stringdefs.h -#Objects/stringlib/partition.h stringlib/stringdefs.h -#Objects/stringlib/split.h stringlib/stringdefs.h -Objects/stringlib/fastsearch.h stringlib/ucs1lib.h -Objects/stringlib/count.h stringlib/ucs1lib.h -Objects/stringlib/find.h stringlib/ucs1lib.h -Objects/stringlib/partition.h stringlib/ucs1lib.h -Objects/stringlib/split.h stringlib/ucs1lib.h -Objects/stringlib/find_max_char.h Objects/stringlib/ucs1lib.h -Objects/stringlib/count.h Objects/stringlib/fastsearch.h -Objects/stringlib/find.h Objects/stringlib/fastsearch.h -Objects/stringlib/partition.h Objects/stringlib/fastsearch.h -Objects/stringlib/replace.h Objects/stringlib/fastsearch.h -Objects/stringlib/repr.h Objects/stringlib/fastsearch.h -Objects/stringlib/split.h Objects/stringlib/fastsearch.h - -# @end=tsv@ -''')[1:] - -MACROS = clean_lines(''' -# @begin=tsv@ - -glob name value - -Include/internal/*.h Py_BUILD_CORE 1 -Python/**/*.c Py_BUILD_CORE 1 -Python/**/*.h Py_BUILD_CORE 1 -Parser/**/*.c Py_BUILD_CORE 1 -Parser/**/*.h Py_BUILD_CORE 1 -Objects/**/*.c Py_BUILD_CORE 1 -Objects/**/*.h Py_BUILD_CORE 1 - -Modules/_asynciomodule.c Py_BUILD_CORE 1 -Modules/_codecsmodule.c Py_BUILD_CORE 1 -Modules/_collectionsmodule.c Py_BUILD_CORE 1 -Modules/_ctypes/_ctypes.c Py_BUILD_CORE 1 -Modules/_ctypes/cfield.c Py_BUILD_CORE 1 -Modules/_cursesmodule.c Py_BUILD_CORE 1 -Modules/_datetimemodule.c Py_BUILD_CORE 1 -Modules/_functoolsmodule.c Py_BUILD_CORE 1 -Modules/_heapqmodule.c Py_BUILD_CORE 1 -Modules/_io/*.c Py_BUILD_CORE 1 -Modules/_io/*.h Py_BUILD_CORE 1 -Modules/_localemodule.c Py_BUILD_CORE 1 -Modules/_operator.c Py_BUILD_CORE 1 -Modules/_posixsubprocess.c Py_BUILD_CORE 1 -Modules/_sre/sre.c Py_BUILD_CORE 1 -Modules/_threadmodule.c Py_BUILD_CORE 1 -Modules/_tracemalloc.c Py_BUILD_CORE 1 -Modules/_weakref.c Py_BUILD_CORE 1 -Modules/_zoneinfo.c Py_BUILD_CORE 1 -Modules/atexitmodule.c Py_BUILD_CORE 1 -Modules/cmathmodule.c Py_BUILD_CORE 1 -Modules/faulthandler.c Py_BUILD_CORE 1 -Modules/gcmodule.c Py_BUILD_CORE 1 -Modules/getpath.c Py_BUILD_CORE 1 -Modules/getpath_noop.c Py_BUILD_CORE 1 -Modules/itertoolsmodule.c Py_BUILD_CORE 1 -Modules/main.c Py_BUILD_CORE 1 -Modules/mathmodule.c Py_BUILD_CORE 1 -Modules/posixmodule.c Py_BUILD_CORE 1 -Modules/sha256module.c Py_BUILD_CORE 1 -Modules/sha512module.c Py_BUILD_CORE 1 -Modules/signalmodule.c Py_BUILD_CORE 1 -Modules/symtablemodule.c Py_BUILD_CORE 1 -Modules/timemodule.c Py_BUILD_CORE 1 -Modules/unicodedata.c Py_BUILD_CORE 1 - -Modules/_json.c Py_BUILD_CORE_BUILTIN 1 -Modules/_pickle.c Py_BUILD_CORE_BUILTIN 1 -Modules/_testinternalcapi.c Py_BUILD_CORE_BUILTIN 1 - -Include/cpython/abstract.h Py_CPYTHON_ABSTRACTOBJECT_H 1 -Include/cpython/bytearrayobject.h Py_CPYTHON_BYTEARRAYOBJECT_H 1 -Include/cpython/bytesobject.h Py_CPYTHON_BYTESOBJECT_H 1 -Include/cpython/ceval.h Py_CPYTHON_CEVAL_H 1 -Include/cpython/code.h Py_CPYTHON_CODE_H 1 -Include/cpython/dictobject.h Py_CPYTHON_DICTOBJECT_H 1 -Include/cpython/fileobject.h Py_CPYTHON_FILEOBJECT_H 1 -Include/cpython/fileutils.h Py_CPYTHON_FILEUTILS_H 1 -Include/cpython/frameobject.h Py_CPYTHON_FRAMEOBJECT_H 1 -Include/cpython/import.h Py_CPYTHON_IMPORT_H 1 -Include/cpython/interpreteridobject.h Py_CPYTHON_INTERPRETERIDOBJECT_H 1 -Include/cpython/listobject.h Py_CPYTHON_LISTOBJECT_H 1 -Include/cpython/methodobject.h Py_CPYTHON_METHODOBJECT_H 1 -Include/cpython/object.h Py_CPYTHON_OBJECT_H 1 -Include/cpython/objimpl.h Py_CPYTHON_OBJIMPL_H 1 -Include/cpython/pyerrors.h Py_CPYTHON_ERRORS_H 1 -Include/cpython/pylifecycle.h Py_CPYTHON_PYLIFECYCLE_H 1 -Include/cpython/pymem.h Py_CPYTHON_PYMEM_H 1 -Include/cpython/pystate.h Py_CPYTHON_PYSTATE_H 1 -Include/cpython/sysmodule.h Py_CPYTHON_SYSMODULE_H 1 -Include/cpython/traceback.h Py_CPYTHON_TRACEBACK_H 1 -Include/cpython/tupleobject.h Py_CPYTHON_TUPLEOBJECT_H 1 -Include/cpython/unicodeobject.h Py_CPYTHON_UNICODEOBJECT_H 1 - -# implied include of <unistd.h> -Include/**/*.h _POSIX_THREADS 1 -Include/**/*.h HAVE_PTHREAD_H 1 - -# from pyconfig.h -Include/cpython/pthread_stubs.h HAVE_PTHREAD_STUBS 1 -Python/thread_pthread_stubs.h HAVE_PTHREAD_STUBS 1 - -# from Objects/bytesobject.c -Objects/stringlib/partition.h STRINGLIB_GET_EMPTY() bytes_get_empty() -Objects/stringlib/join.h STRINGLIB_MUTABLE 0 -Objects/stringlib/partition.h STRINGLIB_MUTABLE 0 -Objects/stringlib/split.h STRINGLIB_MUTABLE 0 -Objects/stringlib/transmogrify.h STRINGLIB_MUTABLE 0 - -# from Makefile -Modules/getpath.c PYTHONPATH 1 -Modules/getpath.c PREFIX ... -Modules/getpath.c EXEC_PREFIX ... -Modules/getpath.c VERSION ... -Modules/getpath.c VPATH ... -Modules/getpath.c PLATLIBDIR ... -#Modules/_dbmmodule.c USE_GDBM_COMPAT 1 -Modules/_dbmmodule.c USE_NDBM 1 -#Modules/_dbmmodule.c USE_BERKDB 1 - -# See: setup.py -Modules/_decimal/**/*.c CONFIG_64 1 -Modules/_decimal/**/*.c ASM 1 -Modules/expat/xmlparse.c HAVE_EXPAT_CONFIG_H 1 -Modules/expat/xmlparse.c XML_POOR_ENTROPY 1 -Modules/_dbmmodule.c HAVE_GDBM_DASH_NDBM_H 1 - -# others -Modules/_sre/sre_lib.h LOCAL(type) static inline type -Modules/_sre/sre_lib.h SRE(F) sre_ucs2_##F -Objects/stringlib/codecs.h STRINGLIB_IS_UNICODE 1 -Include/internal/pycore_crossinterp_data_registry.h Py_CORE_CROSSINTERP_DATA_REGISTRY_H 1 - -# @end=tsv@ -''')[1:] +EXCLUDED += format_conf_lines([ + # The tool should be able to parse these... + + # The problem with xmlparse.c is that something + # has gone wrong where # we handle "maybe inline actual" + # in Tools/c-analyzer/c_parser/parser/_global.py. + 'Modules/expat/internal.h', + 'Modules/expat/xmlparse.c', +]) + +INCL_DIRS = format_tsv_lines([ + # (glob, dirname) + + ('*', '.'), + ('*', './Include'), + ('*', './Include/internal'), + ('*', './Include/internal/mimalloc'), + + ('Modules/_elementtree.c', 'Modules/expat'), + ('Modules/pyexpat.c', 'Modules/expat'), + + ('Modules/_hacl/*.c', 'Modules/_hacl/include'), + ('Modules/_hacl/*.c', 'Modules/_hacl/'), + ('Modules/_hacl/*.h', 'Modules/_hacl/include'), + ('Modules/_hacl/*.h', 'Modules/_hacl/'), + ('Modules/md5module.c', 'Modules/_hacl/include'), + ('Modules/sha1module.c', 'Modules/_hacl/include'), + ('Modules/sha2module.c', 'Modules/_hacl/include'), + ('Modules/sha3module.c', 'Modules/_hacl/include'), + ('Modules/blake2module.c', 'Modules/_hacl/include'), + ('Modules/hmacmodule.c', 'Modules/_hacl/include'), + + ('Objects/stringlib/*.h', 'Objects'), + + # possible system-installed headers, just in case + ('Modules/_tkinter.c', '/usr/include/tcl8.6'), + ('Modules/_uuidmodule.c', '/usr/include/uuid'), + ('Modules/tkappinit.c', '/usr/include/tcl'), + +]) + +INCLUDES = format_tsv_lines([ + # (glob, include) + + ('**/*.h', 'Python.h'), + ('Include/**/*.h', 'object.h'), + + # for Py_HAVE_CONDVAR + ('Include/internal/pycore_gil.h', 'pycore_condvar.h'), + ('Python/thread_pthread.h', 'pycore_condvar.h'), + + # other + + ('Objects/stringlib/join.h', 'stringlib/stringdefs.h'), + ('Objects/stringlib/ctype.h', 'stringlib/stringdefs.h'), + ('Objects/stringlib/transmogrify.h', 'stringlib/stringdefs.h'), + # ('Objects/stringlib/fastsearch.h', 'stringlib/stringdefs.h'), + # ('Objects/stringlib/count.h', 'stringlib/stringdefs.h'), + # ('Objects/stringlib/find.h', 'stringlib/stringdefs.h'), + # ('Objects/stringlib/partition.h', 'stringlib/stringdefs.h'), + # ('Objects/stringlib/split.h', 'stringlib/stringdefs.h'), + ('Objects/stringlib/fastsearch.h', 'stringlib/ucs1lib.h'), + ('Objects/stringlib/count.h', 'stringlib/ucs1lib.h'), + ('Objects/stringlib/find.h', 'stringlib/ucs1lib.h'), + ('Objects/stringlib/partition.h', 'stringlib/ucs1lib.h'), + ('Objects/stringlib/split.h', 'stringlib/ucs1lib.h'), + ('Objects/stringlib/find_max_char.h', 'Objects/stringlib/ucs1lib.h'), + ('Objects/stringlib/count.h', 'Objects/stringlib/fastsearch.h'), + ('Objects/stringlib/find.h', 'Objects/stringlib/fastsearch.h'), + ('Objects/stringlib/partition.h', 'Objects/stringlib/fastsearch.h'), + ('Objects/stringlib/replace.h', 'Objects/stringlib/fastsearch.h'), + ('Objects/stringlib/repr.h', 'Objects/stringlib/fastsearch.h'), + ('Objects/stringlib/split.h', 'Objects/stringlib/fastsearch.h'), +]) + +MACROS = format_tsv_lines([ + # (glob, name, value) + + ('Include/internal/*.h', 'Py_BUILD_CORE', '1'), + ('Python/**/*.c', 'Py_BUILD_CORE', '1'), + ('Python/**/*.h', 'Py_BUILD_CORE', '1'), + ('Parser/**/*.c', 'Py_BUILD_CORE', '1'), + ('Parser/**/*.h', 'Py_BUILD_CORE', '1'), + ('Objects/**/*.c', 'Py_BUILD_CORE', '1'), + ('Objects/**/*.h', 'Py_BUILD_CORE', '1'), + + ('Modules/_asynciomodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/_codecsmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/_collectionsmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/_ctypes/_ctypes.c', 'Py_BUILD_CORE', '1'), + ('Modules/_ctypes/cfield.c', 'Py_BUILD_CORE', '1'), + ('Modules/_cursesmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/_datetimemodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/_functoolsmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/_heapqmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/_io/*.c', 'Py_BUILD_CORE', '1'), + ('Modules/_io/*.h', 'Py_BUILD_CORE', '1'), + ('Modules/_localemodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/_operator.c', 'Py_BUILD_CORE', '1'), + ('Modules/_posixsubprocess.c', 'Py_BUILD_CORE', '1'), + ('Modules/_sre/sre.c', 'Py_BUILD_CORE', '1'), + ('Modules/_threadmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/_tracemalloc.c', 'Py_BUILD_CORE', '1'), + ('Modules/_weakref.c', 'Py_BUILD_CORE', '1'), + ('Modules/_zoneinfo.c', 'Py_BUILD_CORE', '1'), + ('Modules/atexitmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/cmathmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/faulthandler.c', 'Py_BUILD_CORE', '1'), + ('Modules/gcmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/getpath.c', 'Py_BUILD_CORE', '1'), + ('Modules/getpath_noop.c', 'Py_BUILD_CORE', '1'), + ('Modules/itertoolsmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/main.c', 'Py_BUILD_CORE', '1'), + ('Modules/mathmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/posixmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/sha256module.c', 'Py_BUILD_CORE', '1'), + ('Modules/sha512module.c', 'Py_BUILD_CORE', '1'), + ('Modules/signalmodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/symtablemodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/timemodule.c', 'Py_BUILD_CORE', '1'), + ('Modules/unicodedata.c', 'Py_BUILD_CORE', '1'), + + ('Modules/_json.c', 'Py_BUILD_CORE_BUILTIN', '1'), + ('Modules/_pickle.c', 'Py_BUILD_CORE_BUILTIN', '1'), + ('Modules/_testinternalcapi.c', 'Py_BUILD_CORE_BUILTIN', '1'), + + ('Include/cpython/abstract.h', 'Py_CPYTHON_ABSTRACTOBJECT_H', '1'), + ('Include/cpython/bytearrayobject.h', 'Py_CPYTHON_BYTEARRAYOBJECT_H', '1'), + ('Include/cpython/bytesobject.h', 'Py_CPYTHON_BYTESOBJECT_H', '1'), + ('Include/cpython/ceval.h', 'Py_CPYTHON_CEVAL_H', '1'), + ('Include/cpython/code.h', 'Py_CPYTHON_CODE_H', '1'), + ('Include/cpython/dictobject.h', 'Py_CPYTHON_DICTOBJECT_H', '1'), + ('Include/cpython/fileobject.h', 'Py_CPYTHON_FILEOBJECT_H', '1'), + ('Include/cpython/fileutils.h', 'Py_CPYTHON_FILEUTILS_H', '1'), + ('Include/cpython/frameobject.h', 'Py_CPYTHON_FRAMEOBJECT_H', '1'), + ('Include/cpython/import.h', 'Py_CPYTHON_IMPORT_H', '1'), + ('Include/cpython/interpreteridobject.h', 'Py_CPYTHON_INTERPRETERIDOBJECT_H', '1'), + ('Include/cpython/listobject.h', 'Py_CPYTHON_LISTOBJECT_H', '1'), + ('Include/cpython/methodobject.h', 'Py_CPYTHON_METHODOBJECT_H', '1'), + ('Include/cpython/object.h', 'Py_CPYTHON_OBJECT_H', '1'), + ('Include/cpython/objimpl.h', 'Py_CPYTHON_OBJIMPL_H', '1'), + ('Include/cpython/pyerrors.h', 'Py_CPYTHON_ERRORS_H', '1'), + ('Include/cpython/pylifecycle.h', 'Py_CPYTHON_PYLIFECYCLE_H', '1'), + ('Include/cpython/pymem.h', 'Py_CPYTHON_PYMEM_H', '1'), + ('Include/cpython/pystate.h', 'Py_CPYTHON_PYSTATE_H', '1'), + ('Include/cpython/sysmodule.h', 'Py_CPYTHON_SYSMODULE_H', '1'), + ('Include/cpython/traceback.h', 'Py_CPYTHON_TRACEBACK_H', '1'), + ('Include/cpython/tupleobject.h', 'Py_CPYTHON_TUPLEOBJECT_H', '1'), + ('Include/cpython/unicodeobject.h', 'Py_CPYTHON_UNICODEOBJECT_H', '1'), + + # implied include of <unistd.h> + ('Include/**/*.h', '_POSIX_THREADS', '1'), + ('Include/**/*.h', 'HAVE_PTHREAD_H', '1'), + + # from pyconfig.h + ('Include/cpython/pthread_stubs.h', 'HAVE_PTHREAD_STUBS', '1'), + ('Python/thread_pthread_stubs.h', 'HAVE_PTHREAD_STUBS', '1'), + + # from Objects/bytesobject.c + ('Objects/stringlib/partition.h', 'STRINGLIB_GET_EMPTY()', 'bytes_get_empty()'), + ('Objects/stringlib/join.h', 'STRINGLIB_MUTABLE', '0'), + ('Objects/stringlib/partition.h', 'STRINGLIB_MUTABLE', '0'), + ('Objects/stringlib/split.h', 'STRINGLIB_MUTABLE', '0'), + ('Objects/stringlib/transmogrify.h', 'STRINGLIB_MUTABLE', '0'), + + # from Makefile + ('Modules/getpath.c', 'PYTHONPATH', '1'), + ('Modules/getpath.c', 'PREFIX', '...'), + ('Modules/getpath.c', 'EXEC_PREFIX', '...'), + ('Modules/getpath.c', 'VERSION', '...'), + ('Modules/getpath.c', 'VPATH', '...'), + ('Modules/getpath.c', 'PLATLIBDIR', '...'), + # ('Modules/_dbmmodule.c', 'USE_GDBM_COMPAT', '1'), + ('Modules/_dbmmodule.c', 'USE_NDBM', '1'), + # ('Modules/_dbmmodule.c', 'USE_BERKDB', '1'), + + # See: setup.py + ('Modules/expat/xmlparse.c', 'HAVE_EXPAT_CONFIG_H', '1'), + ('Modules/expat/xmlparse.c', 'XML_POOR_ENTROPY', '1'), + ('Modules/_dbmmodule.c', 'HAVE_GDBM_DASH_NDBM_H', '1'), + + # others + ('Modules/_sre/sre_lib.h', 'LOCAL(type)', 'static inline type'), + ('Modules/_sre/sre_lib.h', 'SRE(F)', 'sre_ucs2_##F'), + ('Objects/stringlib/codecs.h', 'STRINGLIB_IS_UNICODE', '1'), + ('Include/internal/pycore_crossinterp_data_registry.h', 'Py_CORE_CROSSINTERP_DATA_REGISTRY_H', '1'), +]) # -pthread # -Wno-unused-result @@ -323,15 +314,19 @@ def clean_lines(text): _abs('Modules/_hacl/*.c'): (200_000, 500), _abs('Modules/posixmodule.c'): (20_000, 500), _abs('Modules/termios.c'): (10_000, 800), + _abs('Modules/_remote_debugging/debug_offsets_validation.h'): (25_000, 1000), + _abs('Modules/_remote_debugging/*.h'): (20_000, 1000), _abs('Modules/_testcapimodule.c'): (20_000, 400), _abs('Modules/expat/expat.h'): (10_000, 400), _abs('Objects/stringlib/unicode_format.h'): (10_000, 400), - _abs('Objects/typeobject.c'): (35_000, 200), + _abs('Objects/typeobject.c'): (380_000, 13_000), _abs('Python/compile.c'): (20_000, 500), + _abs('Python/jit_unwind.c'): (20_000, 300), _abs('Python/optimizer.c'): (100_000, 5_000), _abs('Python/parking_lot.c'): (40_000, 1000), - _abs('Python/pylifecycle.c'): (500_000, 5000), - _abs('Python/pystate.c'): (500_000, 5000), + _abs('Python/perf_jit_trampoline.c'): (40_000, 1000), + _abs('Python/pylifecycle.c'): (750_000, 5000), + _abs('Python/pystate.c'): (750_000, 5000), _abs('Python/initconfig.c'): (50_000, 500), # Generated files: @@ -348,12 +343,17 @@ def clean_lines(text): _abs('Modules/_ssl_data_300.h'): (80_000, 10_000), _abs('Modules/_ssl_data_111.h'): (80_000, 10_000), _abs('Modules/cjkcodecs/mappings_*.h'): (160_000, 2_000), + _abs('Modules/clinic/_testclinic.c.h'): (125_000, 5_000), _abs('Modules/unicodedata_db.h'): (180_000, 3_000), _abs('Modules/unicodename_db.h'): (1_200_000, 15_000), _abs('Objects/unicodetype_db.h'): (240_000, 3_000), # Catch-alls: _abs('Include/**/*.h'): (5_000, 500), + + # Specific to clang + _abs('Modules/selectmodule.c'): (40_000, 3000), + _abs('Modules/_testcapi/pyatomic.c'): (30_000, 1000), } diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 3c3cb2f9c86f16..db575d870be5c5 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -59,6 +59,7 @@ Objects/interpolationobject.c - _PyInterpolation_Type - Objects/iterobject.c - PyCallIter_Type - Objects/iterobject.c - PySeqIter_Type - Objects/iterobject.c - _PyAnextAwaitable_Type - +Objects/lazyimportobject.c - PyLazyImport_Type - Objects/listobject.c - PyListIter_Type - Objects/listobject.c - PyListRevIter_Type - Objects/listobject.c - PyList_Type - @@ -82,6 +83,7 @@ Objects/picklebufobject.c - PyPickleBuffer_Type - Objects/rangeobject.c - PyLongRangeIter_Type - Objects/rangeobject.c - PyRangeIter_Type - Objects/rangeobject.c - PyRange_Type - +Objects/sentinelobject.c - PySentinel_Type - Objects/setobject.c - PyFrozenSet_Type - Objects/setobject.c - PySetIter_Type - Objects/setobject.c - PySet_Type - @@ -176,6 +178,7 @@ Objects/exceptions.c - _PyExc_StopIteration - Objects/exceptions.c - _PyExc_GeneratorExit - Objects/exceptions.c - _PyExc_SystemExit - Objects/exceptions.c - _PyExc_KeyboardInterrupt - +Objects/exceptions.c - _PyExc_ImportCycleError - Objects/exceptions.c - _PyExc_ImportError - Objects/exceptions.c - _PyExc_ModuleNotFoundError - Objects/exceptions.c - _PyExc_OSError - @@ -242,6 +245,7 @@ Objects/exceptions.c - PyExc_StopIteration - Objects/exceptions.c - PyExc_GeneratorExit - Objects/exceptions.c - PyExc_SystemExit - Objects/exceptions.c - PyExc_KeyboardInterrupt - +Objects/exceptions.c - PyExc_ImportCycleError - Objects/exceptions.c - PyExc_ImportError - Objects/exceptions.c - PyExc_ModuleNotFoundError - Objects/exceptions.c - PyExc_OSError - @@ -354,7 +358,7 @@ Modules/_testclinic.c - TestClass - ################################## ## global non-objects to fix in builtin modules -# <none> +Objects/memoryobject.c - bool_false - ################################## @@ -400,6 +404,7 @@ Modules/_tkinter.c - tcl_lock - Modules/_tkinter.c - excInCmd - Modules/_tkinter.c - valInCmd - Modules/_tkinter.c - trbInCmd - +Modules/socketmodule.c - netdb_lock - ################################## diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index dc626e4bea0f59..bf08e5568205e7 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -16,6 +16,7 @@ filename funcname name reason ## indicators for resource availability/capability # (set during first init) Python/bootstrap_hash.c py_getrandom getrandom_works - +Python/bootstrap_hash.c py_getentropy getentropy_works - Python/fileutils.c - _Py_open_cloexec_works - Python/fileutils.c set_inheritable ioctl_works - # (set lazily, *after* first init) @@ -24,6 +25,7 @@ Modules/posixmodule.c os_dup2_impl dup3_works - ## guards around resource init Python/thread_pthread.h PyThread__init_thread lib_initialized - +Modules/_struct.c - endian_tables_init_once - ##----------------------- ## other values (not Python-specific) @@ -56,7 +58,7 @@ Python/pyhash.c - _Py_HashSecret - Python/parking_lot.c - buckets - ## data needed for introspecting asyncio state from debuggers and profilers -Modules/_asynciomodule.c - _AsyncioDebug - +Modules/_asynciomodule.c - _Py_AsyncioDebug - ################################## @@ -194,6 +196,7 @@ Python/pyfpe.c - PyFPE_counter - Python/import.c - pkgcontext - Python/pystate.c - _Py_tss_tstate - Python/pystate.c - _Py_tss_gilstate - +Python/pystate.c - _Py_tss_interp - ##----------------------- ## should be const @@ -228,6 +231,7 @@ Modules/_datetimemodule.c datetime_isoformat specs - Modules/_datetimemodule.c parse_hh_mm_ss_ff correction - Modules/_datetimemodule.c time_isoformat specs - Modules/_datetimemodule.c - capi_types - +Modules/_datetimemodule.c normalize_century cache - Modules/_decimal/_decimal.c - cond_map_template - Modules/_decimal/_decimal.c - dec_signal_string - Modules/_decimal/_decimal.c - dflt_ctx - @@ -323,11 +327,15 @@ Modules/pyexpat.c - error_info_of - Modules/pyexpat.c - handler_info - Modules/termios.c - termios_constants - Modules/timemodule.c init_timezone YEAR - +Modules/unicodedata.c unicodedata_create_capi capi - Objects/bytearrayobject.c - _PyByteArray_empty_string - Objects/complexobject.c - c_1 - Objects/exceptions.c - static_exceptions - Objects/genobject.c - ASYNC_GEN_IGNORED_EXIT_MSG - Objects/genobject.c - NON_INIT_CORO_MSG - +Objects/genobject.c gen_getstate state_strings - +Objects/genobject.c cr_getstate state_strings - +Objects/genobject.c ag_getstate state_strings - Objects/longobject.c - _PyLong_DigitValue - Objects/longobject.c - PyLong_LAYOUT - Objects/object.c - _Py_SwappedOp - @@ -343,6 +351,8 @@ Objects/obmalloc.c - obmalloc_state_main - Objects/obmalloc.c - obmalloc_state_initialized - Objects/typeobject.c - name_op - Objects/typeobject.c - slotdefs - +# It initialized only once when main interpeter starts +Objects/typeobject.c - slotdefs_dups - Objects/unicodeobject.c - stripfuncnames - Objects/unicodeobject.c - utf7_category - Objects/unicodeobject.c unicode_decode_call_errorhandler_wchar argparse - @@ -354,6 +364,7 @@ Parser/parser.c - soft_keywords - Parser/lexer/lexer.c - type_comment_prefix - Python/ceval.c - _PyEval_BinaryOps - Python/ceval.c - _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS - +Python/ceval.c - _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR - Python/codecs.c - Py_hexdigits - Python/codecs.c - codecs_builtin_error_handlers - Python/codecs.c - ucnhash_capi - @@ -376,6 +387,8 @@ Python/intrinsics.c - _PyIntrinsics_UnaryFunctions - Python/intrinsics.c - _PyIntrinsics_BinaryFunctions - Python/lock.c - TIME_TO_BE_FAIR_NS - Python/opcode_targets.h - opcode_targets - +Python/jit_unwind.c - __jit_debug_descriptor - +Python/jit_unwind.c - _Py_jit_debug_mutex - Python/perf_trampoline.c - _Py_perfmap_callbacks - Python/perf_jit_trampoline.c - _Py_perfmap_jit_callbacks - Python/perf_jit_trampoline.c - perf_jit_map_state - @@ -450,10 +463,12 @@ Modules/_testcapi/exceptions.c - PyRecursingInfinitelyError_Type - Modules/_testcapi/heaptype.c - _testcapimodule - Modules/_testcapi/mem.c - FmData - Modules/_testcapi/mem.c - FmHook - +Modules/_testcapi/module.c module_from_def_nonstatic_nested subslots - Modules/_testcapi/object.c - MyObject_dealloc_called - Modules/_testcapi/object.c - MyType - Modules/_testcapi/structmember.c - test_structmembersType_OldAPI - Modules/_testcapi/watchers.c - g_dict_watch_events - +Modules/_testcapi/watchers.c - g_dict_watch_once - Modules/_testcapi/watchers.c - g_dict_watchers_installed - Modules/_testcapi/watchers.c - g_type_modified_events - Modules/_testcapi/watchers.c - g_type_watchers_installed - @@ -560,6 +575,11 @@ Modules/_testimportmultiple.c - _barmodule - Modules/_testimportmultiple.c - _foomodule - Modules/_testimportmultiple.c - _testimportmultiple - Modules/_testinternalcapi.c - pending_identify_result - +Modules/_testinternalcapi.c - Test_EvalFrame_Resumes - +Modules/_testinternalcapi.c - Test_EvalFrame_Loads - +Modules/_testinternalcapi/interpreter.c - Test_EvalFrame_Resumes - +Modules/_testinternalcapi/interpreter.c - Test_EvalFrame_Loads - +Modules/_testlimitedcapi/slots.c - TestMethods - Modules/_testmultiphase.c - Example_Type_slots - Modules/_testmultiphase.c - Example_Type_spec - Modules/_testmultiphase.c - Example_methods - @@ -602,6 +622,13 @@ Modules/_testmultiphase.c - slots_exec_unreported_exception - Modules/_testmultiphase.c - slots_nonmodule_with_exec_slots - Modules/_testmultiphase.c - testexport_methods - Modules/_testmultiphase.c - uninitialized_def - +Modules/_testmultiphase.c PyModExport__test_from_modexport slots - +Modules/_testmultiphase.c PyModExport__test_from_modexport_gil_used slots - +Modules/_testmultiphase.c PyModExport__test_from_modexport_create_nonmodule slots - +Modules/_testmultiphase.c PyModExport__test_from_modexport_create_nonmodule_gil_used slots - +Modules/_testmultiphase.c PyModExport__test_from_modexport_smoke slots - +Modules/_testmultiphase.c - modexport_empty_slots - +Modules/_testmultiphase.c - modexport_minimal_slots - Modules/_testsinglephase.c - global_state - Modules/_testsinglephase.c - static_module_circular - Modules/_xxtestfuzz/_xxtestfuzz.c - _fuzzmodule - @@ -755,7 +782,9 @@ Modules/clinic/md5module.c.h _md5_md5 _keywords - Modules/clinic/grpmodule.c.h grp_getgrgid _keywords - Modules/clinic/grpmodule.c.h grp_getgrnam _keywords - Objects/object.c - constants static PyObject*[] +Objects/dictobject.c - PyFrozenDict_Type - ## False positives Python/specialize.c - _Py_InitCleanup - +Python/pystate.c - _no_tstate_sentinel - diff --git a/Tools/c-analyzer/distutils/util.py b/Tools/c-analyzer/distutils/util.py index 89ca094336fdb8..c8e92658d95366 100644 --- a/Tools/c-analyzer/distutils/util.py +++ b/Tools/c-analyzer/distutils/util.py @@ -8,7 +8,6 @@ import re import string import sys -from distutils.errors import DistutilsPlatformError def get_host_platform(): """Return a string that identifies the current platform. This is used mainly to @@ -19,8 +18,8 @@ def get_host_platform(): particularly important. Examples of returned values: - linux-i586 - linux-alpha (?) + linux-x86_64 + linux-aarch64 solaris-2.6-sun4u Windows will return one of: diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 6466d2615cd14e..22a321b4953de7 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -3,9 +3,11 @@ import lexer import parser import re -from typing import Optional, Callable +from typing import Optional, Callable, Iterator -from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, WhileStmt +from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, WhileStmt, ForStmt, MacroIfStmt + +MAX_CACHED_REGISTER = 3 @dataclass class EscapingCall: @@ -20,19 +22,24 @@ class Properties: error_with_pop: bool error_without_pop: bool deopts: bool + deopts_periodic: bool oparg: bool jumps: bool eval_breaker: bool needs_this: bool always_exits: bool - stores_sp: bool + sync_sp: bool uses_co_consts: bool uses_co_names: bool uses_locals: bool has_free: bool side_exit: bool + side_exit_at_end: bool pure: bool uses_opcode: bool + needs_guard_ip: bool + unpredictable_jump: bool + records_value: bool tier: int | None = None const_oparg: int = -1 needs_prev: bool = False @@ -58,21 +65,26 @@ def from_list(properties: list["Properties"]) -> "Properties": error_with_pop=any(p.error_with_pop for p in properties), error_without_pop=any(p.error_without_pop for p in properties), deopts=any(p.deopts for p in properties), + deopts_periodic=any(p.deopts_periodic for p in properties), oparg=any(p.oparg for p in properties), jumps=any(p.jumps for p in properties), eval_breaker=any(p.eval_breaker for p in properties), needs_this=any(p.needs_this for p in properties), always_exits=any(p.always_exits for p in properties), - stores_sp=any(p.stores_sp for p in properties), + sync_sp=any(p.sync_sp for p in properties), uses_co_consts=any(p.uses_co_consts for p in properties), uses_co_names=any(p.uses_co_names for p in properties), uses_locals=any(p.uses_locals for p in properties), uses_opcode=any(p.uses_opcode for p in properties), has_free=any(p.has_free for p in properties), side_exit=any(p.side_exit for p in properties), + side_exit_at_end=any(p.side_exit_at_end for p in properties), pure=all(p.pure for p in properties), needs_prev=any(p.needs_prev for p in properties), no_save_ip=all(p.no_save_ip for p in properties), + needs_guard_ip=any(p.needs_guard_ip for p in properties), + unpredictable_jump=any(p.unpredictable_jump for p in properties), + records_value=any(p.records_value for p in properties), ) @property @@ -85,20 +97,25 @@ def infallible(self) -> bool: error_with_pop=False, error_without_pop=False, deopts=False, + deopts_periodic=False, oparg=False, jumps=False, eval_breaker=False, needs_this=False, always_exits=False, - stores_sp=False, + sync_sp=False, uses_co_consts=False, uses_co_names=False, uses_locals=False, uses_opcode=False, has_free=False, side_exit=False, + side_exit_at_end=False, pure=True, no_save_ip=False, + needs_guard_ip=False, + unpredictable_jump=False, + records_value=False, ) @@ -305,6 +322,16 @@ class Family: size: str members: list[Instruction] + def get_member_record_names(self) -> tuple[str, ...]: + seen: set[str] = set() + names: list[str] = [] + for member in self.members: + for part in member.parts: + if part.properties.records_value and part.name not in seen: + seen.add(part.name) + names.append(part.name) + return tuple(names) + def dump(self, indent: str) -> None: print(indent, self.name, "= ", ", ".join([m.name for m in self.members])) @@ -582,6 +609,7 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "PyStackRef_CLEAR", "PyStackRef_CLOSE_SPECIALIZED", "PyStackRef_DUP", + "PyStackRef_DupImmortal", "PyStackRef_False", "PyStackRef_FromPyObjectBorrow", "PyStackRef_FromPyObjectNew", @@ -599,12 +627,17 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "PyStackRef_RefcountOnObject", "PyStackRef_TYPE", "PyStackRef_True", + "PyBytes_GET_SIZE", + "PyDict_GET_SIZE", + "PySet_GET_SIZE", "PyTuple_GET_ITEM", "PyTuple_GET_SIZE", "PyType_HasFeature", "PyUnicode_Concat", "PyUnicode_GET_LENGTH", "PyUnicode_READ_CHAR", + "PyUnicode_IS_COMPACT_ASCII", + "PyUnicode_1BYTE_DATA", "Py_ARRAY_LENGTH", "Py_FatalError", "Py_INCREF", @@ -618,7 +651,6 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "_PyCode_CODE", "_PyDictValues_AddToInsertionOrder", "_PyErr_Occurred", - "_PyFloat_FromDouble_ConsumeInputs", "_PyFrame_GetBytecode", "_PyFrame_GetCode", "_PyFrame_IsIncomplete", @@ -627,6 +659,7 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "_PyFrame_StackPush", "_PyFunction_SetVersion", "_PyGen_GetGeneratorFromFrame", + "gen_try_set_executing", "_PyInterpreterState_GET", "_PyList_AppendTakeRef", "_PyList_ITEMS", @@ -657,7 +690,6 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "_PyUnicode_Equal", "_PyUnicode_JoinArray", "_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY", - "_Py_DECREF_NO_DEALLOC", "_Py_ID", "_Py_IsImmortal", "_Py_IsOwnedByCurrentThread", @@ -689,6 +721,17 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "PyStackRef_Wrap", "PyStackRef_Unwrap", "_PyLong_CheckExactAndCompact", + "_PyExecutor_FromExit", + "_PyJit_TryInitializeTracing", + "_Py_unset_eval_breaker_bit", + "_Py_set_eval_breaker_bit", + "trigger_backoff_counter", + "_PyThreadState_PopCStackRefSteal", + "doesnt_escape", + "_Py_GatherStats_GetIter", + "_PyStolenTuple_Free", + "PyObject_GC_UnTrack", + "_PyErr_ExceptionMatches", ) @@ -706,7 +749,7 @@ def visit(stmt: Stmt) -> None: in_if = 0 tkn_iter = iter(stmt.contents) for tkn in tkn_iter: - if tkn.kind == "IDENTIFIER" and tkn.text in ("DEOPT_IF", "ERROR_IF", "EXIT_IF"): + if tkn.kind == "IDENTIFIER" and tkn.text in ("DEOPT_IF", "ERROR_IF", "EXIT_IF", "HANDLE_PENDING_AND_DEOPT_IF", "AT_END_EXIT_IF"): in_if = 1 next(tkn_iter) elif tkn.kind == "LPAREN": @@ -723,53 +766,57 @@ def visit(stmt: Stmt) -> None: if error is not None: raise analysis_error(f"Escaping call '{error.text} in condition", error) +def escaping_call_in_simple_stmt(stmt: SimpleStmt, result: dict[SimpleStmt, EscapingCall]) -> None: + tokens = stmt.contents + for idx, tkn in enumerate(tokens): + try: + next_tkn = tokens[idx+1] + except IndexError: + break + if next_tkn.kind != lexer.LPAREN: + continue + if tkn.kind == lexer.IDENTIFIER: + if tkn.text.upper() == tkn.text: + # simple macro + continue + #if not tkn.text.startswith(("Py", "_Py", "monitor")): + # continue + if tkn.text.startswith(("sym_", "optimize_", "PyJitRef")): + # Optimize functions + continue + if tkn.text.endswith("Check"): + continue + if tkn.text.startswith("Py_Is"): + continue + if tkn.text.endswith("CheckExact"): + continue + if tkn.text in NON_ESCAPING_FUNCTIONS: + continue + elif tkn.kind == "RPAREN": + prev = tokens[idx-1] + if prev.text.endswith("_t") or prev.text == "*" or prev.text == "int": + #cast + continue + elif tkn.kind != "RBRACKET": + continue + if tkn.text in ("PyStackRef_CLOSE", "PyStackRef_XCLOSE"): + if len(tokens) <= idx+2: + raise analysis_error("Unexpected end of file", next_tkn) + kills = tokens[idx+2] + if kills.kind != "IDENTIFIER": + raise analysis_error(f"Expected identifier, got '{kills.text}'", kills) + else: + kills = None + result[stmt] = EscapingCall(stmt, tkn, kills) + + def find_escaping_api_calls(instr: parser.CodeDef) -> dict[SimpleStmt, EscapingCall]: result: dict[SimpleStmt, EscapingCall] = {} def visit(stmt: Stmt) -> None: if not isinstance(stmt, SimpleStmt): return - tokens = stmt.contents - for idx, tkn in enumerate(tokens): - try: - next_tkn = tokens[idx+1] - except IndexError: - break - if next_tkn.kind != lexer.LPAREN: - continue - if tkn.kind == lexer.IDENTIFIER: - if tkn.text.upper() == tkn.text: - # simple macro - continue - #if not tkn.text.startswith(("Py", "_Py", "monitor")): - # continue - if tkn.text.startswith(("sym_", "optimize_", "PyJitRef")): - # Optimize functions - continue - if tkn.text.endswith("Check"): - continue - if tkn.text.startswith("Py_Is"): - continue - if tkn.text.endswith("CheckExact"): - continue - if tkn.text in NON_ESCAPING_FUNCTIONS: - continue - elif tkn.kind == "RPAREN": - prev = tokens[idx-1] - if prev.text.endswith("_t") or prev.text == "*" or prev.text == "int": - #cast - continue - elif tkn.kind != "RBRACKET": - continue - if tkn.text in ("PyStackRef_CLOSE", "PyStackRef_XCLOSE"): - if len(tokens) <= idx+2: - raise analysis_error("Unexpected end of file", next_tkn) - kills = tokens[idx+2] - if kills.kind != "IDENTIFIER": - raise analysis_error(f"Expected identifier, got '{kills.text}'", kills) - else: - kills = None - result[stmt] = EscapingCall(stmt, tkn, kills) + escaping_call_in_simple_stmt(stmt, result) instr.block.accept(visit) check_escaping_calls(instr, result) @@ -822,6 +869,100 @@ def stack_effect_only_peeks(instr: parser.InstDef) -> bool: ) +def stmt_is_simple_exit(stmt: Stmt) -> bool: + if not isinstance(stmt, SimpleStmt): + return False + tokens = stmt.contents + if len(tokens) < 4: + return False + return ( + tokens[0].text in ("ERROR_IF", "DEOPT_IF", "EXIT_IF", "AT_END_EXIT_IF") + and + tokens[1].text == "(" + and + tokens[2].text in ("true", "1") + and + tokens[3].text == ")" + ) + + +def stmt_list_escapes(stmts: list[Stmt]) -> bool: + if not stmts: + return False + if stmt_is_simple_exit(stmts[-1]): + return False + for stmt in stmts: + if stmt_escapes(stmt): + return True + return False + + +def stmt_escapes(stmt: Stmt) -> bool: + if isinstance(stmt, BlockStmt): + return stmt_list_escapes(stmt.body) + elif isinstance(stmt, SimpleStmt): + for tkn in stmt.contents: + if tkn.text == "DECREF_INPUTS": + return True + d: dict[SimpleStmt, EscapingCall] = {} + escaping_call_in_simple_stmt(stmt, d) + return bool(d) + elif isinstance(stmt, IfStmt): + if stmt.else_body and stmt_escapes(stmt.else_body): + return True + return stmt_escapes(stmt.body) + elif isinstance(stmt, MacroIfStmt): + if stmt.else_body and stmt_list_escapes(stmt.else_body): + return True + return stmt_list_escapes(stmt.body) + elif isinstance(stmt, ForStmt): + return stmt_escapes(stmt.body) + elif isinstance(stmt, WhileStmt): + return stmt_escapes(stmt.body) + else: + assert False, "Unexpected statement type" + +def stmt_has_jump_on_unpredictable_path_body(stmts: list[Stmt] | None, branches_seen: int) -> tuple[bool, int]: + if not stmts: + return False, branches_seen + predict = False + seen = 0 + for st in stmts: + predict_body, seen_body = stmt_has_jump_on_unpredictable_path(st, branches_seen) + predict = predict or predict_body + seen += seen_body + return predict, seen + +def stmt_has_jump_on_unpredictable_path(stmt: Stmt, branches_seen: int) -> tuple[bool, int]: + if isinstance(stmt, BlockStmt): + return stmt_has_jump_on_unpredictable_path_body(stmt.body, branches_seen) + elif isinstance(stmt, SimpleStmt): + for tkn in stmt.contents: + if tkn.text == "JUMPBY": + return True, branches_seen + return False, branches_seen + elif isinstance(stmt, IfStmt): + predict, seen = stmt_has_jump_on_unpredictable_path(stmt.body, branches_seen) + if stmt.else_body: + predict_else, seen_else = stmt_has_jump_on_unpredictable_path(stmt.else_body, branches_seen) + return predict != predict_else, seen + seen_else + 1 + return predict, seen + 1 + elif isinstance(stmt, MacroIfStmt): + predict, seen = stmt_has_jump_on_unpredictable_path_body(stmt.body, branches_seen) + if stmt.else_body: + predict_else, seen_else = stmt_has_jump_on_unpredictable_path_body(stmt.else_body, branches_seen) + return predict != predict_else, seen + seen_else + return predict, seen + elif isinstance(stmt, ForStmt): + unpredictable, branches_seen = stmt_has_jump_on_unpredictable_path(stmt.body, branches_seen) + return unpredictable, branches_seen + 1 + elif isinstance(stmt, WhileStmt): + unpredictable, branches_seen = stmt_has_jump_on_unpredictable_path(stmt.body, branches_seen) + return unpredictable, branches_seen + 1 + else: + assert False, f"Unexpected statement type {stmt}" + + def compute_properties(op: parser.CodeDef) -> Properties: escaping_calls = find_escaping_api_calls(op) has_free = ( @@ -832,10 +973,13 @@ def compute_properties(op: parser.CodeDef) -> Properties: ) deopts_if = variable_used(op, "DEOPT_IF") exits_if = variable_used(op, "EXIT_IF") - if deopts_if and exits_if: + exit_if_at_end = variable_used(op, "AT_END_EXIT_IF") + deopts_periodic = variable_used(op, "HANDLE_PENDING_AND_DEOPT_IF") + exits_and_deopts = sum((deopts_if, exits_if, deopts_periodic)) + if exits_and_deopts > 1: tkn = op.tokens[0] raise lexer.make_syntax_error( - "Op cannot contain both EXIT_IF and DEOPT_IF", + "Op cannot contain more than one of EXIT_IF, DEOPT_IF and HANDLE_PENDING_AND_DEOPT_IF", tkn.filename, tkn.line, tkn.column, @@ -843,22 +987,26 @@ def compute_properties(op: parser.CodeDef) -> Properties: ) error_with_pop = has_error_with_pop(op) error_without_pop = has_error_without_pop(op) - escapes = bool(escaping_calls) or variable_used(op, "DECREF_INPUTS") + escapes = stmt_escapes(op.block) pure = False if isinstance(op, parser.LabelDef) else "pure" in op.annotations no_save_ip = False if isinstance(op, parser.LabelDef) else "no_save_ip" in op.annotations + unpredictable, branches_seen = stmt_has_jump_on_unpredictable_path(op.block, 0) + unpredictable_jump = False if isinstance(op, parser.LabelDef) else (unpredictable and branches_seen > 0) return Properties( escaping_calls=escaping_calls, escapes=escapes, error_with_pop=error_with_pop, error_without_pop=error_without_pop, deopts=deopts_if, + deopts_periodic=deopts_periodic, side_exit=exits_if, + side_exit_at_end=exit_if_at_end, oparg=oparg_used(op), jumps=variable_used(op, "JUMPBY"), eval_breaker="CHECK_PERIODIC" in op.name, needs_this=variable_used(op, "this_instr"), always_exits=always_exits(op), - stores_sp=variable_used(op, "SYNC_SP"), + sync_sp=variable_used(op, "SYNC_SP"), uses_co_consts=variable_used(op, "FRAME_CO_CONSTS"), uses_co_names=variable_used(op, "FRAME_CO_NAMES"), uses_locals=variable_used(op, "GETLOCAL") and not has_free, @@ -868,6 +1016,12 @@ def compute_properties(op: parser.CodeDef) -> Properties: no_save_ip=no_save_ip, tier=tier_variable(op), needs_prev=variable_used(op, "prev_instr"), + needs_guard_ip=(isinstance(op, parser.InstDef) + and (unpredictable_jump and "replaced" not in op.annotations)) + or variable_used(op, "LOAD_IP") + or variable_used(op, "DISPATCH_INLINED"), + unpredictable_jump=unpredictable_jump, + records_value=variable_used(op, "RECORD_VALUE") ) def expand(items: list[StackItem], oparg: int) -> list[StackItem]: @@ -993,6 +1147,7 @@ def add_macro( macro: parser.Macro, instructions: dict[str, Instruction], uops: dict[str, Uop] ) -> None: parts: list[Part] = [] + seen_real_uop = False for part in macro.uops: match part: case parser.OpName(): @@ -1003,7 +1158,16 @@ def add_macro( raise analysis_error( f"No Uop named {part.name}", macro.tokens[0] ) - parts.append(uops[part.name]) + uop = uops[part.name] + if uop.properties.records_value: + if seen_real_uop: + raise analysis_error( + f"Recording uop {part.name} must precede all " + f"non-recording, non-specializing uops in macro", + macro.tokens[0]) + elif "specializing" not in uop.annotations: + seen_real_uop = True + parts.append(uop) case parser.CacheEffect(): parts.append(Skip(part.size)) case _: @@ -1073,8 +1237,9 @@ def assign_opcodes( # This is an historical oddity. instmap["BINARY_OP_INPLACE_ADD_UNICODE"] = 3 - instmap["INSTRUMENTED_LINE"] = 254 - instmap["ENTER_EXECUTOR"] = 255 + instmap["INSTRUMENTED_LINE"] = 253 + instmap["ENTER_EXECUTOR"] = 254 + instmap["TRACE_RECORD"] = 255 instrumented = [name for name in instructions if name.startswith("INSTRUMENTED")] @@ -1099,7 +1264,7 @@ def assign_opcodes( # Specialized ops appear in their own section # Instrumented opcodes are at the end of the valid range min_internal = instmap["RESUME"] + 1 - min_instrumented = 254 - (len(instrumented) - 1) + min_instrumented = 254 - len(instrumented) assert min_internal + len(specialized) < min_instrumented next_opcode = 1 @@ -1217,6 +1382,59 @@ def analyze_forest(forest: list[parser.AstNode]) -> Analysis: ) +#Simple heuristic for size to avoid too much stencil duplication +def is_large(uop: Uop) -> bool: + return len(list(uop.body.tokens())) > 120 + + +def get_uop_cache_depths(uop: Uop) -> Iterator[tuple[int, int, int]]: + if uop.name == "_SPILL_OR_RELOAD": + for inputs in range(MAX_CACHED_REGISTER+1): + for outputs in range(MAX_CACHED_REGISTER+1): + if inputs != outputs: + yield inputs, outputs, inputs + return + if uop.name in ("_DEOPT", "_HANDLE_PENDING_AND_DEOPT", "_EXIT_TRACE", "_DYNAMIC_EXIT"): + for i in range(MAX_CACHED_REGISTER+1): + yield i, 0, 0 + return + if uop.name in ("_START_EXECUTOR", "_JUMP_TO_TOP", "_COLD_EXIT"): + yield 0, 0, 0 + return + if uop.name == "_ERROR_POP_N": + yield 0, 0, 0 + return + ideal_inputs = 0 + has_array = False + for item in reversed(uop.stack.inputs): + if item.size: + has_array = True + break + ideal_inputs += 1 + ideal_outputs = 0 + for item in reversed(uop.stack.outputs): + if item.size: + has_array = True + break + ideal_outputs += 1 + if ideal_inputs > MAX_CACHED_REGISTER: + ideal_inputs = MAX_CACHED_REGISTER + if ideal_outputs > MAX_CACHED_REGISTER: + ideal_outputs = MAX_CACHED_REGISTER + at_end = uop.properties.sync_sp or uop.properties.side_exit_at_end + exit_depth = ideal_outputs if at_end else ideal_inputs + if uop.properties.escapes or uop.properties.sync_sp or has_array or is_large(uop): + yield ideal_inputs, ideal_outputs, exit_depth + return + for inputs in range(MAX_CACHED_REGISTER + 1): + outputs = ideal_outputs - ideal_inputs + inputs + if outputs < ideal_outputs: + outputs = ideal_outputs + elif outputs > MAX_CACHED_REGISTER: + continue + yield inputs, outputs, outputs if at_end else inputs + + def analyze_files(filenames: list[str]) -> Analysis: return analyze_forest(parser.parse_files(filenames)) diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index 4c210fbf8d28e9..8c66ad4885ccfc 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -13,7 +13,6 @@ from lexer import Token from stack import Storage, StackError from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, ForStmt, WhileStmt, MacroIfStmt -from stack import PRINT_STACKS DEBUG = False class TokenIterator: @@ -107,11 +106,14 @@ class Emitter: labels: dict[str, Label] _replacers: dict[str, ReplacementFunctionType] cannot_escape: bool + jump_prefix: str - def __init__(self, out: CWriter, labels: dict[str, Label], cannot_escape: bool = False): + def __init__(self, out: CWriter, labels: dict[str, Label], cannot_escape: bool = False, jump_prefix: str = ""): self._replacers = { "EXIT_IF": self.exit_if, + "AT_END_EXIT_IF": self.exit_if_after, "DEOPT_IF": self.deopt_if, + "HANDLE_PENDING_AND_DEOPT_IF": self.periodic_if, "ERROR_IF": self.error_if, "ERROR_NO_POP": self.error_no_pop, "DECREF_INPUTS": self.decref_inputs, @@ -125,10 +127,15 @@ def __init__(self, out: CWriter, labels: dict[str, Label], cannot_escape: bool = "DISPATCH": self.dispatch, "INSTRUCTION_SIZE": self.instruction_size, "stack_pointer": self.stack_pointer, + "Py_UNREACHABLE": self.unreachable, + "TIER1_TO_TIER2": self.tier1_to_tier2, + "TIER2_TO_TIER2": self.tier2_to_tier2, + "GOTO_TIER_ONE": self.goto_tier_one } self.out = out self.labels = labels self.cannot_escape = cannot_escape + self.jump_prefix = jump_prefix def dispatch( self, @@ -144,6 +151,19 @@ def dispatch( self.emit(tkn) return False + def unreachable( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + self.emit(tkn) + emit_to(self.out, tkn_iter, "SEMI") + self.emit(";\n") + return False + def deopt_if( self, tkn: Token, @@ -165,18 +185,51 @@ def deopt_if( family_name = inst.family.name self.emit(f"UPDATE_MISS_STATS({family_name});\n") self.emit(f"assert(_PyOpcode_Deopt[opcode] == ({family_name}));\n") - self.emit(f"JUMP_TO_PREDICTED({family_name});\n") + self.emit(f"JUMP_TO_PREDICTED({self.jump_prefix}{family_name});\n") self.emit("}\n") return not always_true(first_tkn) exit_if = deopt_if + def periodic_if( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + raise NotImplementedError("HANDLE_PENDING_AND_DEOPT_IF not support in tier 1") + + def exit_if_after( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + storage.clear_inputs("in AT_END_EXIT_IF") + storage.flush(self.out) + storage.stack.clear(self.out) + return self.exit_if(tkn, tkn_iter, uop, storage, inst) + + def goto_tier_one( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + raise NotImplementedError("GOTO_TIER_ONE not supported in tier 1") + def goto_error(self, offset: int, storage: Storage) -> str: if offset > 0: - return f"JUMP_TO_LABEL(pop_{offset}_error);" + return f"{self.jump_prefix}JUMP_TO_LABEL(pop_{offset}_error);" if offset < 0: storage.copy().flush(self.out) - return f"JUMP_TO_LABEL(error);" + return f"{self.jump_prefix}JUMP_TO_LABEL(error);" def error_if( self, @@ -213,6 +266,24 @@ def error_if( self.out.emit("}\n") return not unconditional + def tier1_to_tier2( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + self.out.emit(tkn) + lparen = next(tkn_iter) + assert lparen.kind == "LPAREN" + self.emit(lparen) + emit_to(self.out, tkn_iter, "RPAREN") + self.out.emit(")") + return False + + tier2_to_tier2 = tier1_to_tier2 + def error_no_pop( self, tkn: Token, @@ -396,7 +467,7 @@ def goto_label(self, goto: Token, label: Token, storage: Storage) -> None: elif storage.spilled: raise analysis_error("Cannot jump from spilled label without reloading the stack pointer", goto) self.out.start_line() - self.out.emit("JUMP_TO_LABEL(") + self.out.emit(f"{self.jump_prefix}JUMP_TO_LABEL(") self.out.emit(label) self.out.emit(")") @@ -445,7 +516,7 @@ def instruction_size(self, """Replace the INSTRUCTION_SIZE macro with the size of the current instruction.""" if uop.instruction_size is None: raise analysis_error("The INSTRUCTION_SIZE macro requires uop.instruction_size to be set", tkn) - self.out.emit(f" {uop.instruction_size} ") + self.out.emit(f" {uop.instruction_size}u ") return True def _print_storage(self, reason:str, storage: Storage) -> None: @@ -533,10 +604,10 @@ def emit_MacroIfStmt( self.out.emit(stmt.condition) branch = stmt.else_ is not None reachable = True - if branch: - else_storage = storage.copy() + if_storage = storage + else_storage = storage.copy() for s in stmt.body: - r, tkn, storage = self._emit_stmt(s, uop, storage, inst) + r, tkn, if_storage = self._emit_stmt(s, uop, if_storage, inst) if tkn is not None: self.out.emit(tkn) if not r: @@ -551,7 +622,10 @@ def emit_MacroIfStmt( self.out.emit(tkn) if not r: reachable = False - else_storage.merge(storage, self.out) # type: ignore[possibly-undefined] + else_storage.merge(if_storage, self.out) + storage = if_storage + else: + if_storage.merge(else_storage, self.out) storage = else_storage self.out.emit(stmt.endif) return reachable, None, storage @@ -692,7 +766,9 @@ def cflags(p: Properties) -> str: flags.append("HAS_EVAL_BREAK_FLAG") if p.deopts: flags.append("HAS_DEOPT_FLAG") - if p.side_exit: + if p.deopts_periodic: + flags.append("HAS_PERIODIC_FLAG") + if p.side_exit or p.side_exit_at_end: flags.append("HAS_EXIT_FLAG") if not p.infallible: flags.append("HAS_ERROR_FLAG") @@ -704,6 +780,14 @@ def cflags(p: Properties) -> str: flags.append("HAS_PURE_FLAG") if p.no_save_ip: flags.append("HAS_NO_SAVE_IP_FLAG") + if p.sync_sp: + flags.append("HAS_SYNC_SP_FLAG") + if p.unpredictable_jump: + flags.append("HAS_UNPREDICTABLE_JUMP_FLAG") + if p.needs_guard_ip: + flags.append("HAS_NEEDS_GUARD_IP_FLAG") + if p.records_value: + flags.append("HAS_RECORDS_VALUE_FLAG") if flags: return " | ".join(flags) else: diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md index 72020133738fa5..29e4e74da72154 100644 --- a/Tools/cases_generator/interpreter_definition.md +++ b/Tools/cases_generator/interpreter_definition.md @@ -174,7 +174,13 @@ list of annotations and their meanings are as follows: * `override`. For external use by other interpreter definitions to override the current instruction definition. * `pure`. This instruction has no side effects. -* 'tierN'. This instruction is only used by the tier N interpreter. +* `tierN`. This instruction is only used by the tier N interpreter. +* `specializing`. A prefix for an instructions related to adaptive interpreter. +* `replaced`. This instruction will be replaced in the final bytecode by its directed + version (either forward or backward). +* `register`. Currently does nothing. +* `replicate(N)`. Replicate the instruction N times to store the oparg "inside" the instruction. +* `no_save_ip`. This instruction does not affect the instruction pointer. ### Special functions/macros diff --git a/Tools/cases_generator/opcode_metadata_generator.py b/Tools/cases_generator/opcode_metadata_generator.py index 0bcdc5395dcd8e..00f6804f1724b5 100644 --- a/Tools/cases_generator/opcode_metadata_generator.py +++ b/Tools/cases_generator/opcode_metadata_generator.py @@ -19,7 +19,6 @@ cflags, ) from cwriter import CWriter -from dataclasses import dataclass from typing import TextIO from stack import get_stack_effect @@ -53,10 +52,13 @@ "ESCAPES", "EXIT", "PURE", - "PASSTHROUGH", - "OPARG_AND_1", + "SYNC_SP", "ERROR_NO_POP", "NO_SAVE_IP", + "PERIODIC", + "UNPREDICTABLE_JUMP", + "NEEDS_GUARD_IP", + "RECORDS_VALUE", ] @@ -148,7 +150,7 @@ def generate_instruction_formats(analysis: Analysis, out: CWriter) -> None: def generate_deopt_table(analysis: Analysis, out: CWriter) -> None: - out.emit("extern const uint8_t _PyOpcode_Deopt[256];\n") + out.emit("PyAPI_DATA(const uint8_t) _PyOpcode_Deopt[256];\n") out.emit("#ifdef NEED_OPCODE_METADATA\n") out.emit("const uint8_t _PyOpcode_Deopt[256] = {\n") deopts: list[tuple[str, str]] = [] @@ -171,7 +173,7 @@ def generate_deopt_table(analysis: Analysis, out: CWriter) -> None: def generate_cache_table(analysis: Analysis, out: CWriter) -> None: - out.emit("extern const uint8_t _PyOpcode_Caches[256];\n") + out.emit("PyAPI_DATA(const uint8_t) _PyOpcode_Caches[256];\n") out.emit("#ifdef NEED_OPCODE_METADATA\n") out.emit("const uint8_t _PyOpcode_Caches[256] = {\n") for inst in analysis.instructions.values(): @@ -187,7 +189,7 @@ def generate_cache_table(analysis: Analysis, out: CWriter) -> None: def generate_name_table(analysis: Analysis, out: CWriter) -> None: table_size = 256 + len(analysis.pseudos) - out.emit(f"extern const char *_PyOpcode_OpName[{table_size}];\n") + out.emit(f"PyAPI_DATA(const char) *_PyOpcode_OpName[{table_size}];\n") out.emit("#ifdef NEED_OPCODE_METADATA\n") out.emit(f"const char *_PyOpcode_OpName[{table_size}] = {{\n") names = list(analysis.instructions) + list(analysis.pseudos) @@ -202,10 +204,10 @@ def generate_metadata_table(analysis: Analysis, out: CWriter) -> None: out.emit("struct opcode_metadata {\n") out.emit("uint8_t valid_entry;\n") out.emit("uint8_t instr_format;\n") - out.emit("uint16_t flags;\n") + out.emit("uint32_t flags;\n") out.emit("};\n\n") out.emit( - f"extern const struct opcode_metadata _PyOpcode_opcode_metadata[{table_size}];\n" + f"PyAPI_DATA(const struct opcode_metadata) _PyOpcode_opcode_metadata[{table_size}];\n" ) out.emit("#ifdef NEED_OPCODE_METADATA\n") out.emit( diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index ea9dd836d98e22..aa914783f7cdc2 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -173,6 +173,8 @@ def replace_opcode_if_evaluates_pure( if token.kind == "SEMI": break + output_identifier = input_identifiers.pop() + if len(input_identifiers) == 0: raise analysis_error( "To evaluate an operation as pure, it must have at least 1 input", @@ -190,6 +192,7 @@ def replace_opcode_if_evaluates_pure( input_identifiers_as_str = {tkn.text for tkn in input_identifiers} used_stack_inputs = [inp for inp in uop.stack.inputs if inp.name in input_identifiers_as_str] assert len(used_stack_inputs) > 0 + self.out.start_line() emitter = OptimizerConstantEmitter(self.out, {}, self.original_uop, self.stack.copy()) emitter.emit("if (\n") for inp in used_stack_inputs[:-1]: @@ -224,14 +227,64 @@ def replace_opcode_if_evaluates_pure( emitter.emit_tokens(self.original_uop, storage, inst=None, emit_braces=False) self.out.start_line() emitter.emit("/* End of uop copied from bytecodes for constant evaluation */\n") - # Finally, assign back the output stackrefs to symbolics. for outp in self.original_uop.stack.outputs: - # All new stackrefs are created from new references. - # That's how the stackref contract works. - if not outp.peek: - emitter.emit(f"{outp.name} = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal({outp.name}_stackref));\n") + if not outp.name == output_identifier.text: + emitter.emit(f"(void){outp.name}_stackref;\n") + + # Output stackref is created from new reference. + emitter.emit(f"{output_identifier.text} = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal({output_identifier.text}_stackref));\n") + + if self.original_uop.name.startswith('_'): + # Map input count to output index (from TOS) and the appropriate constant-loading uop + input_count_to_uop = { + 1: { + # (a -- res), usually for unary ops + 0: [("_POP_TOP", "0, 0"), + ("_LOAD_CONST_INLINE_BORROW", + "0, (uintptr_t)result")], + # (left -- res, left) + # usually for unary ops with passthrough references + 1: [("_LOAD_CONST_INLINE_BORROW", + "0, (uintptr_t)result"), + ("_SWAP", "2, 0")], + }, + 2: { + # (a, b -- res), usually for binary ops + 0: [("_POP_TOP", "0, 0"), + ("_POP_TOP", "0, 0"), + ("_LOAD_CONST_INLINE_BORROW", + "0, (uintptr_t)result")], + # (left, right -- res, left, right) + # usually for binary ops with passthrough references + 2: [("_LOAD_CONST_INLINE_BORROW", + "0, (uintptr_t)result"), + ("_RROT_3", "0, 0")], + }, + } + + output_index = -1 + for idx, outp in enumerate(reversed(uop.stack.outputs)): + if outp.name == output_identifier.text: + output_index = idx + break else: - emitter.emit(f"{outp.name} = sym_new_const(ctx, PyStackRef_AsPyObjectBorrow({outp.name}_stackref));\n") + raise analysis_error(f"Could not find output {output_identifier.text} in uop.", output_identifier) + assert output_index >= 0 + input_count = len(used_stack_inputs) + if input_count in input_count_to_uop and output_index in input_count_to_uop[input_count]: + ops = input_count_to_uop[input_count][output_index] + input_desc = "one input" if input_count == 1 else "two inputs" + ops_desc = " + ".join(op for op, _ in ops) + + emitter.emit(f"if (sym_is_const(ctx, {output_identifier.text})) {{\n") + emitter.emit(f"PyObject *result = sym_get_const(ctx, {output_identifier.text});\n") + emitter.emit(f"if (_Py_IsImmortal(result)) {{\n") + emitter.emit(f"// Replace with {ops_desc} since we have {input_desc} and an immortal result\n") + for op, args in ops: + emitter.emit(f"ADD_OP({op}, {args});\n") + emitter.emit("}\n") + emitter.emit("}\n") + storage.flush(self.out) emitter.emit("break;\n") emitter.emit("}\n") @@ -358,6 +411,7 @@ def write_uop( args.append(input.name) out.emit(f'DEBUG_PRINTF({", ".join(args)});\n') if override: + idx = 0 for cache in uop.caches: if cache.name != "unused": if cache.size == 4: @@ -365,7 +419,8 @@ def write_uop( else: type = f"uint{cache.size*16}_t " cast = f"uint{cache.size*16}_t" - out.emit(f"{type}{cache.name} = ({cast})this_instr->operand0;\n") + out.emit(f"{type}{cache.name} = ({cast})this_instr->operand{idx};\n") + idx += 1 if override: emitter = OptimizerEmitter(out, {}, uop, stack.copy()) # No reference management of inputs needed. @@ -400,7 +455,7 @@ def generate_abstract_interpreter( for abstract_uop_name in abstract.uops: if abstract_uop_name not in base_uop_names: raise ValueError(f"All abstract uops should override base uops, " - "but {abstract_uop_name} is not.") + f"but {abstract_uop_name} is not.") for uop in base.uops.values(): override: Uop | None = None @@ -421,7 +476,7 @@ def generate_abstract_interpreter( declare_variables(override, out, skip_inputs=False) else: declare_variables(uop, out, skip_inputs=True) - stack = Stack() + stack = Stack(check_stack_bounds=True) write_uop(override, uop, out, stack, debug, skip_inputs=(override is None)) out.start_line() out.emit("break;\n") diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index 4ec46d8cac6e4b..aa6c0b1446fb76 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -20,7 +20,6 @@ MacroIfStmt, ) -import pprint CodeDef = InstDef | LabelDef @@ -70,7 +69,6 @@ def parse_files(filenames: list[str]) -> list[AstNode]: assert node is not None result.append(node) # type: ignore[arg-type] if not psr.eof(): - pprint.pprint(result) psr.backup() raise psr.make_syntax_error( f"Extra stuff at the end of {filename}", psr.next(True) diff --git a/Tools/cases_generator/py_metadata_generator.py b/Tools/cases_generator/py_metadata_generator.py index 3ec06faf338488..6df72de44e78cb 100644 --- a/Tools/cases_generator/py_metadata_generator.py +++ b/Tools/cases_generator/py_metadata_generator.py @@ -30,35 +30,43 @@ def get_specialized(analysis: Analysis) -> set[str]: def generate_specializations(analysis: Analysis, out: CWriter) -> None: - out.emit("_specializations = {\n") + out.emit("_specializations = frozendict(\n") for family in analysis.families.values(): - out.emit(f'"{family.name}": [\n') + out.emit(f'{family.name}=(\n') + seen = set() for member in family.members: + if member.name in seen: + continue + seen.add(member.name) out.emit(f' "{member.name}",\n') - out.emit("],\n") - out.emit("}\n\n") + out.emit("),\n") + out.emit(")\n\n") def generate_specialized_opmap(analysis: Analysis, out: CWriter) -> None: - out.emit("_specialized_opmap = {\n") + out.emit("_specialized_opmap = frozendict(\n") names = [] + seen = set() for family in analysis.families.values(): for member in family.members: if member.name == family.name: continue + if member.name in seen: + continue + seen.add(member.name) names.append(member.name) for name in sorted(names): - out.emit(f"'{name}': {analysis.opmap[name]},\n") - out.emit("}\n\n") + out.emit(f"{name}={analysis.opmap[name]},\n") + out.emit(")\n\n") def generate_opmap(analysis: Analysis, out: CWriter) -> None: specialized = get_specialized(analysis) - out.emit("opmap = {\n") + out.emit("opmap = frozendict(\n") for inst, op in analysis.opmap.items(): if inst not in specialized: - out.emit(f"'{inst}': {analysis.opmap[inst]},\n") - out.emit("}\n\n") + out.emit(f"{inst}={analysis.opmap[inst]},\n") + out.emit(")\n\n") def generate_py_metadata( diff --git a/Tools/cases_generator/record_function_generator.py b/Tools/cases_generator/record_function_generator.py new file mode 100644 index 00000000000000..b5a028384adb49 --- /dev/null +++ b/Tools/cases_generator/record_function_generator.py @@ -0,0 +1,321 @@ + +import argparse + +from analyzer import ( + Analysis, + Family, + Instruction, + Uop, + analyze_files, + CodeSection, +) + +from generators_common import ( + DEFAULT_INPUT, + ROOT, + write_header, + emit_to, + TokenIterator, +) + +from cwriter import CWriter + +from tier1_generator import write_uop, Emitter, declare_variable +from lexer import Token +from stack import Stack, Storage + +DEFAULT_OUTPUT = ROOT / "Python/recorder_functions.c.h" + +# Must match MAX_RECORDED_VALUES in Include/internal/pycore_optimizer.h. +MAX_RECORDED_VALUES = 3 + +# Recorder uops whose slot kind differs from the leading word of their name. +_RECORD_SLOT_KIND_OVERRIDES: dict[str, str] = { + "_RECORD_BOUND_METHOD": "CALLABLE", +} + + +class RecorderEmitter(Emitter): + def __init__(self, out: CWriter, target: str, incref: str): + super().__init__(out, {}) + self._replacers["RECORD_VALUE"] = self.record_value + self.target = target + self.incref = incref + + def record_value( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + next(tkn_iter) + self.out.start_line() + self.emit(f"{self.target} = (PyObject *)") + emit_to(self.out, tkn_iter, "RPAREN") + next(tkn_iter) # Semi colon + self.emit(";\n") + self.emit(f"{self.incref}({self.target});\n") + return True + + +def get_record_slot_kind(record_name: str) -> str: + if record_name in _RECORD_SLOT_KIND_OVERRIDES: + return _RECORD_SLOT_KIND_OVERRIDES[record_name] + if not record_name.startswith("_RECORD_"): + return record_name + return record_name.removeprefix("_RECORD_").partition("_")[0] + + +def get_instruction_record_names(inst: Instruction) -> list[str]: + return [part.name for part in inst.parts if part.properties.records_value] + + +def get_family_record_names( + family: Family, + instruction_records: dict[str, list[str]], + record_slot_keys: dict[str, str], +) -> list[str]: + family_record_names = set(family.get_member_record_names()) + family_record_names.update(instruction_records[family.name]) + records: list[str] = [] + slot_index: dict[str, int] = {} + + def add(name: str) -> None: + kind = record_slot_keys[name] + # Prefer the raw recorder if any family instruction uses it. + raw = f"_RECORD_{kind}" + source = raw if raw in family_record_names else name + existing = slot_index.get(kind) + if existing is None: + slot_index[kind] = len(records) + records.append(source) + elif records[existing] != source: + if raw not in record_slot_keys: + raise ValueError( + f"Family {family.name} has incompatible recorders for " + f"slot {kind}: {records[existing]} and {source}, " + f"and no raw recorder {raw} exists to use as a base." + ) + records[existing] = raw + + for member in family.members: + for name in instruction_records[member.name]: + add(name) + # Family head supplies any slots no member exercises, and may also + # conflict with members (resolved via the raw recorder above). + for name in instruction_records[family.name]: + add(name) + return records + + +def get_record_consumer_layout( + inst_name: str, + source_records: list[str], + own_records: list[str], + record_slot_keys: dict[str, str], +) -> tuple[list[int], int, list[str]]: + used = [False] * len(source_records) + slot_map: list[int] = [] + transform_mask = 0 + transform_names: list[str] = [] + for i, own in enumerate(own_records): + own_kind = record_slot_keys[own] + for j, src in enumerate(source_records): + if not used[j] and record_slot_keys[src] == own_kind: + used[j] = True + slot_map.append(j) + if src != own: + transform_mask |= 1 << i + if own not in transform_names: + transform_names.append(own) + break + else: + raise ValueError( + f"Instruction {inst_name} has no compatible family slot for " + f"{own} in {source_records}" + ) + return slot_map, transform_mask, transform_names + + +def get_record_transform_input(uop: Uop) -> str: + inputs = [var for var in uop.stack.inputs if var.used] + if len(inputs) != 1 or inputs[0].is_array(): + raise ValueError( + f"Recorder transform for {uop.name} needs exactly one scalar input" + ) + return inputs[0].name + + +def generate_record_transform_function(uop: Uop, out: CWriter) -> None: + input_name = get_record_transform_input(uop) + out.emit("static PyObject *\n") + out.emit(f"_PyOpcode_RecordTransform{uop.name[7:]}(PyObject *recorded_value)\n") + out.emit("{\n") + out.emit("PyObject *transformed_value = NULL;\n") + for var in uop.stack.inputs: + if var.used: + declare_variable(var, out) + out.emit(f"{input_name} = PyStackRef_FromPyObjectBorrow(recorded_value);\n") + emitter = RecorderEmitter(out, "transformed_value", "Py_XINCREF") + emitter.emit_tokens(uop, Storage(Stack(), [], [], 0, False), None, False) + out.start_line() + out.emit("Py_DECREF(recorded_value);\n") + out.emit("return transformed_value;\n") + out.emit("}\n\n") + + +def generate_recorder_functions(filenames: list[str], analysis: Analysis, out: CWriter) -> None: + write_header(__file__, filenames, out.out) + out.out.write( + """ +#ifdef TIER_ONE + #error "This file is for Tier 2 only" +#endif +""" + ) + args = "_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, int oparg, PyObject **recorded_value" + emitter = RecorderEmitter(out, "*recorded_value", "Py_INCREF") + nop = analysis.instructions["NOP"] + for uop in analysis.uops.values(): + if not uop.properties.records_value: + continue + out.emit(f"void _PyOpcode_RecordFunction{uop.name[7:]}({args}) {{\n") + seen = {"unused"} + for var in uop.stack.inputs: + if var.used and var.name not in seen: + seen.add(var.name) + declare_variable(var, out) + stack = Stack() + write_uop(uop, emitter, 0, stack, nop, False) + out.start_line() + out.emit("}") + out.emit("\n\n") + +def generate_recorder_tables(analysis: Analysis, out: CWriter) -> None: + instruction_records = { + inst.name: get_instruction_record_names(inst) + for inst in analysis.instructions.values() + } + record_uop_names = [ + name for name, uop in analysis.uops.items() if uop.properties.records_value + ] + record_slot_keys = {name: get_record_slot_kind(name) for name in record_uop_names} + family_record_table = { + family.name: get_family_record_names( + family, + instruction_records, + record_slot_keys, + ) + for family in analysis.families.values() + } + + record_table: dict[str, list[str]] = {} + record_consumer_table: dict[str, tuple[list[int], int]] = {} + record_function_indexes: dict[str, int] = {} + record_transform_names: list[str] = [] + for inst in analysis.instructions.values(): + own_records = instruction_records[inst.name] + # TRACE_RECORD runs before execution, but specialization may rewrite + # the opcode before translation. Use the shared family recording shape + # so any opcode in the family can be translated from the same layout. + family = inst.family or analysis.families.get(inst.name) + records = family_record_table[family.name] if family is not None else own_records + if not records: + continue + if len(records) > MAX_RECORDED_VALUES: + raise ValueError( + f"Instruction {inst.name} has {len(records)} recording ops, " + f"exceeds MAX_RECORDED_VALUES ({MAX_RECORDED_VALUES})" + ) + record_table[inst.name] = records + for name in records: + if name not in record_function_indexes: + record_function_indexes[name] = len(record_function_indexes) + 1 + if own_records: + slots, mask, transform_names = get_record_consumer_layout( + inst.name, records, own_records, record_slot_keys + ) + record_consumer_table[inst.name] = (slots, mask) + for name in transform_names: + if name not in record_transform_names: + record_transform_names.append(name) + + for name, index in record_function_indexes.items(): + out.emit(f"#define {name}_INDEX {index}\n") + out.emit("\n") + + out.emit("const _PyOpcodeRecordEntry _PyOpcode_RecordEntries[256] = {\n") + for inst_name, records in record_table.items(): + indices = ", ".join(f"{name}_INDEX" for name in records) + out.emit(f" [{inst_name}] = {{{len(records)}, {{{indices}}}}},\n") + out.emit("};\n\n") + + out.emit("const _PyOpcodeRecordSlotMap _PyOpcode_RecordSlotMaps[256] = {\n") + for inst_name, (slots, mask) in record_consumer_table.items(): + slot_list = ", ".join(str(s) for s in slots) + out.emit( + f" [{inst_name}] = {{{len(slots)}, {mask}, {{{slot_list}}}}},\n" + ) + out.emit("};\n\n") + + out.emit( + f"const _Py_RecordFuncPtr _PyOpcode_RecordFunctions" + f"[{len(record_function_indexes) + 1}] = {{\n" + ) + out.emit(" [0] = NULL,\n") + for name in record_function_indexes: + out.emit(f" [{name}_INDEX] = _PyOpcode_RecordFunction{name[7:]},\n") + out.emit("};\n") + out.emit("\n") + for name in record_transform_names: + generate_record_transform_function(analysis.uops[name], out) + generate_record_transform_dispatcher(record_transform_names, out) + + +def generate_record_transform_dispatcher( + transform_names: list[str], out: CWriter +) -> None: + """Emit a switch that converts a family-recorded value for a recorder uop. + + Only `_RECORD_*` uops that need conversion get a case; the default returns + the input value unchanged. + """ + out.emit( + "PyObject *\n" + "_PyOpcode_RecordTransformValue(int uop, PyObject *value)\n" + "{\n" + ) + out.emit("switch (uop) {\n") + for name in transform_names: + out.emit(f"case {name}:\n") + out.emit(f" return _PyOpcode_RecordTransform{name[7:]}(value);\n") + out.emit("default:\n") + out.emit(" return value;\n") + out.emit("}\n") + out.emit("}\n") + + +arg_parser = argparse.ArgumentParser( + description="Generate the record functions for the trace recorder.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, +) + +arg_parser.add_argument( + "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT +) + +arg_parser.add_argument( + "input", nargs=argparse.REMAINDER, help="Instruction definition file(s)" +) + +if __name__ == "__main__": + args = arg_parser.parse_args() + if len(args.input) == 0: + args.input.append(DEFAULT_INPUT) + data = analyze_files(args.input) + with open(args.output, "w") as outfile: + out = CWriter(outfile, 0, False) + generate_recorder_functions(args.input, data, out) + generate_recorder_tables(data, out) diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 3a0e7e5d0d5636..efc534fb607b90 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -216,11 +216,18 @@ def array_or_scalar(var: StackItem | Local) -> str: return "array" if var.is_array() else "scalar" class Stack: - def __init__(self) -> None: + def __init__(self, check_stack_bounds: bool = False) -> None: self.base_offset = PointerOffset.zero() self.physical_sp = PointerOffset.zero() self.logical_sp = PointerOffset.zero() self.variables: list[Local] = [] + self.check_stack_bounds = check_stack_bounds + + def push_cache(self, cached_items:list[str], out: CWriter) -> None: + for i, name in enumerate(cached_items): + out.start_line() + out.emit(f"_PyStackRef _stack_item_{i} = {name};\n") + self.push(Local.register(f"_stack_item_{i}")) def drop(self, var: StackItem, check_liveness: bool) -> None: self.logical_sp = self.logical_sp.pop(var) @@ -296,7 +303,7 @@ def _save_physical_sp(self, out: CWriter) -> None: diff = self.logical_sp - self.physical_sp out.start_line() out.emit(f"stack_pointer += {diff.to_c()};\n") - out.emit(f"assert(WITHIN_STACK_BOUNDS());\n") + out.emit(f"ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);\n") self.physical_sp = self.logical_sp self._print(out) @@ -316,8 +323,17 @@ def save_variables(self, out: CWriter) -> None: self._print(out) var_offset = var_offset.push(var.item) + def stack_bound_check(self, out: CWriter) -> None: + if not self.check_stack_bounds: + return + if self.physical_sp != self.logical_sp: + diff = self.logical_sp - self.physical_sp + out.start_line() + out.emit(f"CHECK_STACK_BOUNDS({diff});\n") + def flush(self, out: CWriter) -> None: self._print(out) + self.stack_bound_check(out) self.save_variables(out) self._save_physical_sp(out) out.start_line() @@ -347,6 +363,7 @@ def copy(self) -> "Stack": other.physical_sp = self.physical_sp other.logical_sp = self.logical_sp other.variables = [var.copy() for var in self.variables] + other.check_stack_bounds = self.check_stack_bounds return other def __eq__(self, other: object) -> bool: @@ -475,7 +492,7 @@ def clear_dead_inputs(self) -> None: def _push_defined_outputs(self) -> None: defined_output = "" for output in self.outputs: - if output.in_local and not output.memory_offset: + if output.in_local and not output.memory_offset and not output.item.peek: defined_output = output.name if not defined_output: return @@ -499,6 +516,7 @@ def locals_cached(self) -> bool: return False def flush(self, out: CWriter) -> None: + self._print(out) self.clear_dead_inputs() self._push_defined_outputs() self.stack.flush(out) diff --git a/Tools/cases_generator/target_generator.py b/Tools/cases_generator/target_generator.py index ca151ff640a30c..e7cf6c223d22dc 100644 --- a/Tools/cases_generator/target_generator.py +++ b/Tools/cases_generator/target_generator.py @@ -26,19 +26,31 @@ def write_opcode_targets(analysis: Analysis, out: CWriter) -> None: for name, op in analysis.opmap.items(): if op < 256: targets[op] = f"&&TARGET_{name},\n" - out.emit("#if !Py_TAIL_CALL_INTERP\n") - out.emit("static void *opcode_targets[256] = {\n") + out.emit("#if !_Py_TAIL_CALL_INTERP\n") + out.emit("static void *opcode_targets_table[256] = {\n") for target in targets: out.emit(target) out.emit("};\n") - out.emit("#else /* Py_TAIL_CALL_INTERP */\n") + targets = ["&&_unknown_opcode,\n"] * 256 + for name, op in analysis.opmap.items(): + if op < 256: + targets[op] = f"&&TARGET_TRACE_RECORD,\n" + out.emit("#if _Py_TIER2\n") + out.emit("static void *opcode_tracing_targets_table[256] = {\n") + for target in targets: + out.emit(target) + out.emit("};\n") + out.emit(f"#endif\n") + out.emit("#else /* _Py_TAIL_CALL_INTERP */\n") def function_proto(name: str) -> str: - return f"Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_{name}(TAIL_CALL_PARAMS)" + return f"static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_{name}(TAIL_CALL_PARAMS)" def write_tailcall_dispatch_table(analysis: Analysis, out: CWriter) -> None: - out.emit("static py_tail_call_funcptr INSTRUCTION_TABLE[256];\n") + out.emit("static py_tail_call_funcptr instruction_funcptr_handler_table[256];\n") + out.emit("\n") + out.emit("static py_tail_call_funcptr instruction_funcptr_tracing_table[256];\n") out.emit("\n") # Emit function prototypes for labels. @@ -60,7 +72,7 @@ def write_tailcall_dispatch_table(analysis: Analysis, out: CWriter) -> None: out.emit("\n") # Emit the dispatch table. - out.emit("static py_tail_call_funcptr INSTRUCTION_TABLE[256] = {\n") + out.emit("static py_tail_call_funcptr instruction_funcptr_handler_table[256] = {\n") for name in sorted(analysis.instructions.keys()): out.emit(f"[{name}] = _TAIL_CALL_{name},\n") named_values = analysis.opmap.values() @@ -68,7 +80,17 @@ def write_tailcall_dispatch_table(analysis: Analysis, out: CWriter) -> None: if rest not in named_values: out.emit(f"[{rest}] = _TAIL_CALL_UNKNOWN_OPCODE,\n") out.emit("};\n") - outfile.write("#endif /* Py_TAIL_CALL_INTERP */\n") + + # Emit the tracing dispatch table. + out.emit("static py_tail_call_funcptr instruction_funcptr_tracing_table[256] = {\n") + for name in sorted(analysis.instructions.keys()): + out.emit(f"[{name}] = _TAIL_CALL_TRACE_RECORD,\n") + named_values = analysis.opmap.values() + for rest in range(256): + if rest not in named_values: + out.emit(f"[{rest}] = _TAIL_CALL_UNKNOWN_OPCODE,\n") + out.emit("};\n") + outfile.write("#endif /* _Py_TAIL_CALL_INTERP */\n") arg_parser = argparse.ArgumentParser( description="Generate the file with dispatch targets.", diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index 32dc346d5e891a..ebc914a6837e2e 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -9,8 +9,6 @@ Analysis, Instruction, Uop, - Label, - CodeSection, Part, analyze_files, Skip, @@ -24,13 +22,9 @@ write_header, type_and_null, Emitter, - TokenIterator, - always_true, - emit_to, ) from cwriter import CWriter from typing import TextIO -from lexer import Token from stack import Local, Stack, StackError, get_stack_effect, Storage DEFAULT_OUTPUT = ROOT / "Python/generated_cases.c.h" @@ -56,7 +50,7 @@ def declare_variables(inst: Instruction, out: CWriter) -> None: raise analysis_error(ex.args[0], inst.where) from None seen = {"unused"} for part in inst.parts: - if not isinstance(part, Uop): + if not isinstance(part, Uop) or part.properties.records_value: continue for var in part.stack.inputs: if var.used and var.name not in seen: @@ -132,7 +126,7 @@ def uses_this(inst: Instruction) -> bool: continue for tkn in uop.body.tokens(): if (tkn.kind == "IDENTIFIER" - and (tkn.text in {"DEOPT_IF", "EXIT_IF"})): + and (tkn.text in {"DEOPT_IF", "EXIT_IF", "AT_END_EXIT_IF"})): return True return False @@ -157,20 +151,20 @@ def generate_tier1( #define TIER_ONE 1 """) outfile.write(f""" -#if !Py_TAIL_CALL_INTERP +#if !_Py_TAIL_CALL_INTERP #if !USE_COMPUTED_GOTOS dispatch_opcode: - switch (opcode) + switch (dispatch_code) #endif {{ -#endif /* Py_TAIL_CALL_INTERP */ +#endif /* _Py_TAIL_CALL_INTERP */ {INSTRUCTION_START_MARKER} """ ) generate_tier1_cases(analysis, outfile, lines) outfile.write(f""" {INSTRUCTION_END_MARKER} -#if !Py_TAIL_CALL_INTERP +#if !_Py_TAIL_CALL_INTERP #if USE_COMPUTED_GOTOS _unknown_opcode: #else @@ -186,7 +180,7 @@ def generate_tier1( /* This should never be reached. Every opcode should end with DISPATCH() or goto error. */ Py_UNREACHABLE(); -#endif /* Py_TAIL_CALL_INTERP */ +#endif /* _Py_TAIL_CALL_INTERP */ {LABEL_START_MARKER} """) out = CWriter(outfile, 2, lines) @@ -203,6 +197,10 @@ def generate_tier1_labels( emitter.emit("\n") # Emit tail-callable labels as function defintions for name, label in analysis.labels.items(): + if name == 'stop_tracing': + emitter.emit("#if _Py_TAIL_CALL_INTERP && !defined(_Py_TIER2)\n") + emitter.emit("Py_GCC_ATTRIBUTE((unused))\n") + emitter.emit("#endif\n") emitter.emit(f"LABEL({name})\n") storage = Storage(Stack(), [], [], 0, False) if label.spilled: @@ -226,7 +224,7 @@ def generate_tier1_cases( popped = get_popped(inst, analysis) # We need to ifdef it because this breaks platforms # without computed gotos/tail calling. - out.emit(f"#if Py_TAIL_CALL_INTERP\n") + out.emit(f"#if _Py_TAIL_CALL_INTERP\n") out.emit(f"int opcode = {name};\n") out.emit(f"(void)(opcode);\n") out.emit(f"#endif\n") @@ -259,6 +257,8 @@ def generate_tier1_cases( offset = 1 # The instruction itself stack = Stack() for part in inst.parts: + if part.properties.records_value: + continue # Only emit braces if more than one uop insert_braces = len([p for p in inst.parts if isinstance(p, Uop)]) > 1 reachable, offset, stack = write_uop(part, emitter, offset, stack, inst, insert_braces) diff --git a/Tools/cases_generator/tier2_generator.py b/Tools/cases_generator/tier2_generator.py index fc3bc47286f7f6..b166b08b188dee 100644 --- a/Tools/cases_generator/tier2_generator.py +++ b/Tools/cases_generator/tier2_generator.py @@ -14,7 +14,11 @@ analyze_files, StackItem, analysis_error, + get_uop_cache_depths, + is_large, + MAX_CACHED_REGISTER, ) + from generators_common import ( DEFAULT_INPUT, ROOT, @@ -60,36 +64,20 @@ def declare_variables(uop: Uop, out: CWriter) -> None: class Tier2Emitter(Emitter): - def __init__(self, out: CWriter, labels: dict[str, Label]): + def __init__(self, out: CWriter, labels: dict[str, Label], exit_cache_depth: int): super().__init__(out, labels) self._replacers["oparg"] = self.oparg + self._replacers["IP_OFFSET_OF"] = self.ip_offset_of + self.exit_cache_depth = exit_cache_depth def goto_error(self, offset: int, storage: Storage) -> str: # To do: Add jump targets for popping values. if offset != 0: storage.copy().flush(self.out) - return f"JUMP_TO_ERROR();" - - def deopt_if( - self, - tkn: Token, - tkn_iter: TokenIterator, - uop: CodeSection, - storage: Storage, - inst: Instruction | None, - ) -> bool: - self.out.emit_at("if ", tkn) - lparen = next(tkn_iter) - self.emit(lparen) - assert lparen.kind == "LPAREN" - first_tkn = tkn_iter.peek() - emit_to(self.out, tkn_iter, "RPAREN") - next(tkn_iter) # Semi colon - self.emit(") {\n") - self.emit("UOP_STAT_INC(uopcode, miss);\n") - self.emit("JUMP_TO_JUMP_TARGET();\n") - self.emit("}\n") - return not always_true(first_tkn) + else: + storage.stack.copy().flush(self.out) + self.emit("SET_CURRENT_CACHED_VALUES(0);\n") + return "JUMP_TO_ERROR();" def exit_if( self, @@ -107,10 +95,15 @@ def exit_if( next(tkn_iter) # Semi colon self.emit(") {\n") self.emit("UOP_STAT_INC(uopcode, miss);\n") + storage = storage.copy() + self.cache_items(storage.stack, self.exit_cache_depth, False) + storage.stack.flush(self.out) self.emit("JUMP_TO_JUMP_TARGET();\n") self.emit("}\n") return not always_true(first_tkn) + periodic_if = deopt_if = exit_if + def oparg( self, tkn: Token, @@ -132,9 +125,91 @@ def oparg( self.out.emit_at(uop.name[-1], tkn) return True + def ip_offset_of( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + assert uop.name.startswith("_GUARD_IP") + # LPAREN + next(tkn_iter) + tok = next(tkn_iter) + self.emit(f" OFFSET_OF_{tok.text};\n") + # RPAREN + next(tkn_iter) + # SEMI + next(tkn_iter) + return True + + def tier2_to_tier2( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + assert self.exit_cache_depth == 0, uop.name + self.cache_items(storage.stack, self.exit_cache_depth, False) + storage.flush(self.out) + self.out.emit(tkn) + lparen = next(tkn_iter) + assert lparen.kind == "LPAREN" + self.emit(lparen) + emit_to(self.out, tkn_iter, "RPAREN") + self.out.emit(")") + return False + + goto_tier_one = tier2_to_tier2 + + def exit_if_after( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + self.out.emit_at("if ", tkn) + lparen = next(tkn_iter) + self.emit(lparen) + first_tkn = tkn_iter.peek() + emit_to(self.out, tkn_iter, "RPAREN") + next(tkn_iter) # Semi colon + self.emit(") {\n") + self.emit("UOP_STAT_INC(uopcode, miss);\n") + storage = storage.copy() + storage.clear_inputs("in AT_END_EXIT_IF") + self.cache_items(storage.stack, self.exit_cache_depth, False) + storage.flush(self.out) + self.emit("JUMP_TO_JUMP_TARGET();\n") + self.emit("}\n") + return not always_true(first_tkn) + + def cache_items(self, stack: Stack, cached_items: int, zero_regs: bool) -> None: + self.out.start_line() + i = cached_items + while i > 0: + self.out.start_line() + item = StackItem(f"_tos_cache{i-1}", "", False, True) + stack.pop(item, self.out) + i -= 1 + if zero_regs: + # TO DO -- For compilers that support it, + # replace this with a "clobber" to tell + # the compiler that these values are unused + # without having to emit any code. + for i in range(cached_items, MAX_CACHED_REGISTER): + self.out.emit(f"_tos_cache{i} = PyStackRef_ZERO_BITS;\n") + self.emit(f"SET_CURRENT_CACHED_VALUES({cached_items});\n") + -def write_uop(uop: Uop, emitter: Emitter, stack: Stack) -> Stack: +def write_uop(uop: Uop, emitter: Tier2Emitter, stack: Stack, cached_items: int = 0) -> tuple[bool, Stack]: locals: dict[str, Local] = {} + zero_regs = is_large(uop) or uop.properties.escapes try: emitter.out.start_line() if uop.properties.oparg: @@ -147,21 +222,30 @@ def write_uop(uop: Uop, emitter: Emitter, stack: Stack) -> Stack: idx = 0 for cache in uop.caches: if cache.name != "unused": + bits = cache.size*16 if cache.size == 4: type = cast = "PyObject *" else: - type = f"uint{cache.size*16}_t " - cast = f"uint{cache.size*16}_t" - emitter.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND{idx}();\n") + type = f"uint{bits}_t " + cast = f"uint{bits}_t" + emitter.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND{idx}_{bits}();\n") idx += 1 - _, storage = emitter.emit_tokens(uop, storage, None, False) - storage.flush(emitter.out) + reachable, storage = emitter.emit_tokens(uop, storage, None, False) + if reachable: + storage.stack._print(emitter.out) + emitter.cache_items(storage.stack, cached_items, zero_regs) + storage.flush(emitter.out) + return reachable, storage.stack except StackError as ex: raise analysis_error(ex.args[0], uop.body.open) from None - return storage.stack SKIPS = ("_EXTENDED_ARG",) +def is_for_iter_test(uop: Uop) -> bool: + return uop.name in ( + "_GUARD_NOT_EXHAUSTED_RANGE", "_GUARD_NOT_EXHAUSTED_LIST", + "_GUARD_NOT_EXHAUSTED_TUPLE", "_FOR_ITER_TIER_TWO" + ) def generate_tier2( filenames: list[str], analysis: Analysis, outfile: TextIO, lines: bool @@ -176,29 +260,40 @@ def generate_tier2( """ ) out = CWriter(outfile, 2, lines) - emitter = Tier2Emitter(out, analysis.labels) out.emit("\n") + for name, uop in analysis.uops.items(): if uop.properties.tier == 1: continue if uop.is_super(): continue + if uop.properties.records_value: + continue why_not_viable = uop.why_not_viable() if why_not_viable is not None: out.emit( f"/* {uop.name} is not a viable micro-op for tier 2 because it {why_not_viable} */\n\n" ) continue - out.emit(f"case {uop.name}: {{\n") - declare_variables(uop, out) - stack = Stack() - stack = write_uop(uop, emitter, stack) - out.start_line() - if not uop.properties.always_exits: - out.emit("break;\n") - out.start_line() - out.emit("}") - out.emit("\n\n") + for inputs, outputs, exit_depth in get_uop_cache_depths(uop): + emitter = Tier2Emitter(out, analysis.labels, exit_depth) + out.emit(f"case {uop.name}_r{inputs}{outputs}: {{\n") + out.emit(f"CHECK_CURRENT_CACHED_VALUES({inputs});\n") + out.emit("ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__);\n") + declare_variables(uop, out) + stack = Stack() + stack.push_cache([f"_tos_cache{i}" for i in range(inputs)], out) + stack._print(out) + reachable, stack = write_uop(uop, emitter, stack, outputs) + out.start_line() + if reachable: + out.emit("ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__);\n") + if not uop.properties.always_exits: + out.emit("break;\n") + out.start_line() + out.emit("}") + out.emit("\n\n") + out.emit("\n") outfile.write("#undef TIER_TWO\n") diff --git a/Tools/cases_generator/uop_id_generator.py b/Tools/cases_generator/uop_id_generator.py index aae89faaa928e1..b059a4a169c1ae 100644 --- a/Tools/cases_generator/uop_id_generator.py +++ b/Tools/cases_generator/uop_id_generator.py @@ -8,6 +8,7 @@ from analyzer import ( Analysis, analyze_files, + get_uop_cache_depths, ) from generators_common import ( DEFAULT_INPUT, @@ -38,9 +39,7 @@ def generate_uop_ids( uops = [(uop.name, uop) for uop in analysis.uops.values()] # Sort so that _BASE comes immediately before _BASE_0, etc. for name, uop in sorted(uops): - if name in PRE_DEFINED: - continue - if uop.properties.tier == 1: + if name in PRE_DEFINED or uop.is_super() or uop.properties.tier == 1: continue if uop.implicitly_created and not distinct_namespace and not uop.replicated: out.emit(f"#define {name} {name[1:]}\n") @@ -49,6 +48,15 @@ def generate_uop_ids( next_id += 1 out.emit(f"#define MAX_UOP_ID {next_id-1}\n") + for name, uop in sorted(uops): + if uop.properties.tier == 1: + continue + if uop.properties.records_value: + continue + for inputs, outputs, _ in sorted(get_uop_cache_depths(uop)): + out.emit(f"#define {name}_r{inputs}{outputs} {next_id}\n") + next_id += 1 + out.emit(f"#define MAX_UOP_REGS_ID {next_id-1}\n") arg_parser = argparse.ArgumentParser( diff --git a/Tools/cases_generator/uop_metadata_generator.py b/Tools/cases_generator/uop_metadata_generator.py index 1cc23837a72dea..4c24435cbdbe05 100644 --- a/Tools/cases_generator/uop_metadata_generator.py +++ b/Tools/cases_generator/uop_metadata_generator.py @@ -8,6 +8,9 @@ from analyzer import ( Analysis, analyze_files, + get_uop_cache_depths, + Uop, + MAX_CACHED_REGISTER, ) from generators_common import ( DEFAULT_INPUT, @@ -22,16 +25,57 @@ DEFAULT_OUTPUT = ROOT / "Include/internal/pycore_uop_metadata.h" +def uop_cache_info(uop: Uop) -> list[str] | None: + if uop.name == "_SPILL_OR_RELOAD": + return None + if uop.properties.records_value: + return None + default = "{ -1, -1, -1 },\n" + table_size = MAX_CACHED_REGISTER + 1 + entries = [ default ] * table_size + low = MAX_CACHED_REGISTER+1 + high = -1 + defined = [ False ] * 4 + for inputs, outputs, exit_depth in get_uop_cache_depths(uop): + entries[inputs] = f"{{ {outputs}, {exit_depth}, {uop.name}_r{inputs}{outputs} }},\n" + if inputs < low: + low = inputs + if inputs > high: + high = inputs + best = [ str(low if i < low else (high if high < i else i)) for i in range(MAX_CACHED_REGISTER+1) ] + + return [ f".best = {{ {', '.join(best)} }},\n", ".entries = {\n", ] + entries + [ "},\n" ] + + +CACHING_INFO_DECL = """ +typedef struct _pyuop_tos_cache_entry { + /* input depth is implicit in position */ + int8_t output; + int8_t exit; + int16_t opcode; +} _PyUopTOSentry; +typedef struct _PyUopCachingInfo { + uint8_t best[MAX_CACHED_REGISTER + 1]; + _PyUopTOSentry entries[MAX_CACHED_REGISTER + 1]; +} _PyUopCachingInfo; +extern const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1]; +""" + + def generate_names_and_flags(analysis: Analysis, out: CWriter) -> None: - out.emit("extern const uint16_t _PyUop_Flags[MAX_UOP_ID+1];\n") + out.emit(f"#define MAX_CACHED_REGISTER {MAX_CACHED_REGISTER}\n") + out.emit("extern const uint32_t _PyUop_Flags[MAX_UOP_ID+1];\n") out.emit("typedef struct _rep_range { uint8_t start; uint8_t stop; } ReplicationRange;\n") out.emit("extern const ReplicationRange _PyUop_Replication[MAX_UOP_ID+1];\n") - out.emit("extern const char * const _PyOpcode_uop_name[MAX_UOP_ID+1];\n\n") - out.emit("extern int _PyUop_num_popped(int opcode, int oparg);\n\n") + out.emit("extern const char * const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1];\n\n") + out.emit("extern int _PyUop_num_popped(int opcode, int oparg);\n") + out.emit(CACHING_INFO_DECL) + out.emit(f"extern const uint16_t _PyUop_SpillsAndReloads[{MAX_CACHED_REGISTER+1}][{MAX_CACHED_REGISTER+1}];\n") + out.emit("extern const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1];\n\n") out.emit("#ifdef NEED_OPCODE_METADATA\n") - out.emit("const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {\n") + out.emit("const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = {\n") for uop in analysis.uops.values(): - if uop.is_viable() and uop.properties.tier != 1: + if uop.is_viable() and uop.properties.tier != 1 and not uop.is_super(): out.emit(f"[{uop.name}] = {cflags(uop.properties)},\n") out.emit("};\n\n") @@ -42,16 +86,41 @@ def generate_names_and_flags(analysis: Analysis, out: CWriter) -> None: out.emit(f"[{uop.name}] = {{ {uop.replicated.start}, {uop.replicated.stop} }},\n") out.emit("};\n\n") - out.emit("const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {\n") + out.emit("const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = {\n") + for uop in analysis.uops.values(): + if uop.is_viable() and uop.properties.tier != 1 and not uop.is_super(): + info = uop_cache_info(uop) + if info is not None: + out.emit(f"[{uop.name}] = {{\n") + for line in info: + out.emit(line) + out.emit("},\n") + out.emit("};\n\n") + out.emit("const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = {\n"); + for uop in analysis.uops.values(): + if uop.is_viable() and uop.properties.tier != 1 and not uop.is_super() and not uop.properties.records_value: + for inputs, outputs, _ in get_uop_cache_depths(uop): + out.emit(f"[{uop.name}_r{inputs}{outputs}] = {uop.name},\n") + out.emit("};\n\n") + out.emit(f"const uint16_t _PyUop_SpillsAndReloads[{MAX_CACHED_REGISTER+1}][{MAX_CACHED_REGISTER+1}] = {{\n") + for i in range(MAX_CACHED_REGISTER+1): + for j in range(MAX_CACHED_REGISTER+1): + if i != j: + out.emit(f"[{i}][{j}] = _SPILL_OR_RELOAD_r{i}{j},\n") + out.emit("};\n\n") + out.emit("const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = {\n") for uop in sorted(analysis.uops.values(), key=lambda t: t.name): - if uop.is_viable() and uop.properties.tier != 1: + if uop.is_viable() and uop.properties.tier != 1 and not uop.is_super(): out.emit(f'[{uop.name}] = "{uop.name}",\n') + if not uop.properties.records_value: + for inputs, outputs, _ in get_uop_cache_depths(uop): + out.emit(f'[{uop.name}_r{inputs}{outputs}] = "{uop.name}_r{inputs}{outputs}",\n') out.emit("};\n") out.emit("int _PyUop_num_popped(int opcode, int oparg)\n{\n") out.emit("switch(opcode) {\n") null = CWriter.null() for uop in analysis.uops.values(): - if uop.is_viable() and uop.properties.tier != 1: + if uop.is_viable() and uop.properties.tier != 1 and not uop.is_super(): stack = Stack() for var in reversed(uop.stack.inputs): if var.peek: @@ -66,7 +135,6 @@ def generate_names_and_flags(analysis: Analysis, out: CWriter) -> None: out.emit("}\n\n") out.emit("#endif // NEED_OPCODE_METADATA\n\n") - def generate_uop_metadata( filenames: list[str], analysis: Analysis, outfile: TextIO ) -> None: diff --git a/Tools/check-c-api-docs/ignored_c_api.txt b/Tools/check-c-api-docs/ignored_c_api.txt new file mode 100644 index 00000000000000..fa53b205c4ff6a --- /dev/null +++ b/Tools/check-c-api-docs/ignored_c_api.txt @@ -0,0 +1,89 @@ +# pydtrace_probes.h +PyDTrace_AUDIT +PyDTrace_FUNCTION_ENTRY +PyDTrace_FUNCTION_RETURN +PyDTrace_GC_DONE +PyDTrace_GC_START +PyDTrace_IMPORT_FIND_LOAD_DONE +PyDTrace_IMPORT_FIND_LOAD_START +PyDTrace_INSTANCE_DELETE_DONE +PyDTrace_INSTANCE_DELETE_START +PyDTrace_INSTANCE_NEW_DONE +PyDTrace_INSTANCE_NEW_START +PyDTrace_LINE +# fileobject.h +Py_FileSystemDefaultEncodeErrors +Py_FileSystemDefaultEncoding +Py_HasFileSystemDefaultEncoding +Py_UTF8Mode +# pyhash.h +Py_HASH_EXTERNAL +# object.h +Py_INVALID_SIZE +# pyexpat.h +PyExpat_CAPI_MAGIC +PyExpat_CAPSULE_NAME +# pyport.h +PYLONG_BITS_IN_DIGIT +PY_DWORD_MAX +PY_BIG_ENDIAN +# cpython/methodobject.h +PyCFunction_GET_CLASS +# cpython/compile.h +PyCF_ALLOW_INCOMPLETE_INPUT +PyCF_COMPILE_MASK +PyCF_DONT_IMPLY_DEDENT +PyCF_IGNORE_COOKIE +PyCF_MASK +PyCF_MASK_OBSOLETE +PyCF_SOURCE_IS_UTF8 +# cpython/descrobject.h +PyDescr_NAME +PyDescr_TYPE +PyWrapperFlag_KEYWORDS +# cpython/fileobject.h +Py_UniversalNewlineFgets +# cpython/pyframe.h +PyUnstable_EXECUTABLE_KINDS +PyUnstable_EXECUTABLE_KIND_BUILTIN_FUNCTION +PyUnstable_EXECUTABLE_KIND_METHOD_DESCRIPTOR +PyUnstable_EXECUTABLE_KIND_PY_FUNCTION +PyUnstable_EXECUTABLE_KIND_SKIP +# cpython/pylifecycle.h +Py_FrozenMain +# pythonrun.h +PyErr_Display +# cpython/objimpl.h +PyObject_GET_WEAKREFS_LISTPTR +# cpython/pythonrun.h +PyOS_Readline +# cpython/warnings.h +PyErr_Warn +# fileobject.h +PY_STDIOTEXTMODE +# structmember.h +PY_WRITE_RESTRICTED +# pythread.h +PY_TIMEOUT_T +PY_TIMEOUT_MAX +# cpython/pyctype.h +PY_CTF_ALNUM +PY_CTF_ALPHA +PY_CTF_DIGIT +PY_CTF_LOWER +PY_CTF_SPACE +PY_CTF_UPPER +PY_CTF_XDIGIT +# cpython/code.h +PY_DEF_EVENT +PY_FOREACH_CODE_EVENT +# cpython/funcobject.h +PY_DEF_EVENT +PY_FOREACH_FUNC_EVENT +# cpython/monitoring.h +PY_MONITORING_EVENT_BRANCH +# cpython/dictobject.h +PY_DEF_EVENT +PY_FOREACH_DICT_EVENT +# cpython/pystats.h +PYSTATS_MAX_UOP_ID diff --git a/Tools/check-c-api-docs/main.py b/Tools/check-c-api-docs/main.py new file mode 100644 index 00000000000000..3debb9ed09da78 --- /dev/null +++ b/Tools/check-c-api-docs/main.py @@ -0,0 +1,188 @@ +import re +from pathlib import Path +import sys +import _colorize +import textwrap + +SIMPLE_FUNCTION_REGEX = re.compile(r"PyAPI_FUNC(.+) (\w+)\(") +SIMPLE_MACRO_REGEX = re.compile(r"# *define *(\w+)(\(.+\))? ") +SIMPLE_INLINE_REGEX = re.compile(r"static inline .+( |\n)(\w+)") +SIMPLE_DATA_REGEX = re.compile(r"PyAPI_DATA\(.+\) (\w+)") +API_NAME_REGEX = re.compile(r'\bP[yY][a-zA-Z0-9_]+') + +CPYTHON = Path(__file__).parent.parent.parent +INCLUDE = CPYTHON / "Include" +C_API_DOCS = CPYTHON / "Doc" / "c-api" +IGNORED = ( + (CPYTHON / "Tools" / "check-c-api-docs" / "ignored_c_api.txt") + .read_text() + .split("\n") +) + +for index, line in enumerate(IGNORED): + if line.startswith("#"): + IGNORED.pop(index) + +MISTAKE = """ +If this is a mistake and this script should not be failing, create an +issue and tag Peter (@ZeroIntensity) on it.\ +""" + + +def found_undocumented(singular: bool) -> str: + some = "an" if singular else "some" + s = "" if singular else "s" + these = "this" if singular else "these" + them = "it" if singular else "them" + were = "was" if singular else "were" + + return ( + textwrap.dedent( + f""" + Found {some} undocumented C API{s}! + + Python requires documentation on all public C API symbols, macros, and types. + If {these} API{s} {were} not meant to be public, prefix {them} with a + leading underscore (_PySomething_API) or move {them} to the internal C API + (pycore_*.h files). + + In exceptional cases, certain APIs can be ignored by adding them to + Tools/check-c-api-docs/ignored_c_api.txt + """ + ) + + MISTAKE + ) + + +def found_ignored_documented(singular: bool) -> str: + some = "a" if singular else "some" + s = "" if singular else "s" + them = "it" if singular else "them" + were = "was" if singular else "were" + they = "it" if singular else "they" + + return ( + textwrap.dedent( + f""" + Found {some} C API{s} listed in Tools/c-api-docs-check/ignored_c_api.txt, but + {they} {were} found in the documentation. To fix this, remove {them} from + ignored_c_api.txt. + """ + ) + + MISTAKE + ) + + +def scan_file_for_docs( + filename: str, + text: str, + names: set[str]) -> tuple[list[str], list[str]]: + """ + Scan a header file for C API functions. + """ + undocumented: list[str] = [] + documented_ignored: list[str] = [] + colors = _colorize.get_colors() + + def check_for_name(name: str) -> None: + documented = name in names + if documented and (name in IGNORED): + documented_ignored.append(name) + elif not documented and (name not in IGNORED): + undocumented.append(name) + + for function in SIMPLE_FUNCTION_REGEX.finditer(text): + name = function.group(2) + if not API_NAME_REGEX.fullmatch(name): + continue + + check_for_name(name) + + for macro in SIMPLE_MACRO_REGEX.finditer(text): + name = macro.group(1) + if not API_NAME_REGEX.fullmatch(name): + continue + + if "(" in name: + name = name[: name.index("(")] + + check_for_name(name) + + for inline in SIMPLE_INLINE_REGEX.finditer(text): + name = inline.group(2) + if not API_NAME_REGEX.fullmatch(name): + continue + + check_for_name(name) + + for data in SIMPLE_DATA_REGEX.finditer(text): + name = data.group(1) + if not API_NAME_REGEX.fullmatch(name): + continue + + check_for_name(name) + + # Remove duplicates and sort alphabetically to keep the output deterministic + undocumented = list(set(undocumented)) + undocumented.sort() + + if undocumented or documented_ignored: + print(f"{filename} {colors.RED}BAD{colors.RESET}") + for name in undocumented: + print(f"{colors.BOLD_RED}UNDOCUMENTED:{colors.RESET} {name}") + for name in documented_ignored: + print(f"{colors.BOLD_YELLOW}DOCUMENTED BUT IGNORED:{colors.RESET} {name}") + else: + print(f"{filename} {colors.GREEN}OK{colors.RESET}") + + return undocumented, documented_ignored + + +def main() -> None: + print("Gathering C API names from docs...") + names = set() + for path in C_API_DOCS.glob('**/*.rst'): + text = path.read_text(encoding="utf-8") + for name in API_NAME_REGEX.findall(text): + names.add(name) + print(f"Got {len(names)} names!") + + print("Scanning for undocumented C API functions...") + files = [*INCLUDE.iterdir(), *(INCLUDE / "cpython").iterdir()] + all_missing: list[str] = [] + all_found_ignored: list[str] = [] + + for file in files: + if file.is_dir(): + continue + assert file.exists() + text = file.read_text(encoding="utf-8") + missing, ignored = scan_file_for_docs(str(file.relative_to(INCLUDE)), text, names) + all_found_ignored += ignored + all_missing += missing + + fail = False + to_check = [ + (all_missing, "missing", found_undocumented(len(all_missing) == 1)), + ( + all_found_ignored, + "documented but ignored", + found_ignored_documented(len(all_found_ignored) == 1), + ), + ] + for name_list, what, message in to_check: + if not name_list: + continue + + s = "s" if len(name_list) != 1 else "" + print(f"-- {len(name_list)} {what} C API{s} --") + for name in name_list: + print(f" - {name}") + print(message) + fail = True + + sys.exit(1 if fail else 0) + + +if __name__ == "__main__": + main() diff --git a/Tools/check-c-api-docs/mypy.ini b/Tools/check-c-api-docs/mypy.ini new file mode 100644 index 00000000000000..f42eb2836e2fd8 --- /dev/null +++ b/Tools/check-c-api-docs/mypy.ini @@ -0,0 +1,19 @@ +[mypy] +files = Tools/check-c-api-docs/ +pretty = True + +# We need `_colorize` import: +mypy_path = $MYPY_CONFIG_FILE_DIR/../../Misc/mypy + +# Make sure Python can still be built +# using Python 3.13 for `PYTHON_FOR_REGEN`... +python_version = 3.13 + +# ...And be strict: +strict = True +extra_checks = True +enable_error_code = + ignore-without-code, + redundant-expr, + truthy-bool, + possibly-undefined, diff --git a/Tools/clinic/.ruff.toml b/Tools/clinic/.ruff.toml index 5033887df0c1cd..944d17ee3e9855 100644 --- a/Tools/clinic/.ruff.toml +++ b/Tools/clinic/.ruff.toml @@ -17,9 +17,6 @@ ignore = [ # Use f-strings instead of format specifiers. # Doesn't always make code more readable. "UP032", - # Use PEP-604 unions rather than tuples for isinstance() checks. - # Makes code slower and more verbose. https://github.com/astral-sh/ruff/issues/7871. - "UP038", ] unfixable = [ # The autofixes sometimes do the wrong things for these; diff --git a/Tools/clinic/libclinic/__init__.py b/Tools/clinic/libclinic/__init__.py index 7c5cede2396677..5ee165d0c138a8 100644 --- a/Tools/clinic/libclinic/__init__.py +++ b/Tools/clinic/libclinic/__init__.py @@ -7,7 +7,9 @@ ) from .formatting import ( SIG_END_MARKER, - c_repr, + c_str_repr, + c_bytes_repr, + c_unichar_repr, docstring_for_c_string, format_escape, indent_all_lines, @@ -26,7 +28,7 @@ from .utils import ( FormatCounterFormatter, NULL, - Null, + NullType, Sentinels, VersionTuple, compute_checksum, @@ -45,7 +47,9 @@ # Formatting helpers "SIG_END_MARKER", - "c_repr", + "c_str_repr", + "c_bytes_repr", + "c_unichar_repr", "docstring_for_c_string", "format_escape", "indent_all_lines", @@ -64,7 +68,7 @@ # Utility functions "FormatCounterFormatter", "NULL", - "Null", + "NullType", "Sentinels", "VersionTuple", "compute_checksum", @@ -84,6 +88,7 @@ "argsbuf", "fastargs", "kwargs", + "kwds", "kwnames", "nargs", "noptargs", diff --git a/Tools/clinic/libclinic/app.py b/Tools/clinic/libclinic/app.py index 632bed3ce53dde..9e8cec5320f877 100644 --- a/Tools/clinic/libclinic/app.py +++ b/Tools/clinic/libclinic/app.py @@ -255,14 +255,14 @@ def _module_and_class( cls: Class | None = None for idx, field in enumerate(fields): + fullname = ".".join(fields[:idx + 1]) if not isinstance(parent, Class): - if field in parent.modules: - parent = module = parent.modules[field] + if fullname in parent.modules: + parent = module = parent.modules[fullname] continue if field in parent.classes: parent = cls = parent.classes[field] else: - fullname = ".".join(fields[idx:]) fail(f"Parent class or module {fullname!r} does not exist.") return module, cls diff --git a/Tools/clinic/libclinic/clanguage.py b/Tools/clinic/libclinic/clanguage.py index 9e7fa7a7f58f95..7f02c7790f015a 100644 --- a/Tools/clinic/libclinic/clanguage.py +++ b/Tools/clinic/libclinic/clanguage.py @@ -6,7 +6,7 @@ from operator import attrgetter from collections.abc import Iterable -import libclinic +import libclinic.cpp from libclinic import ( unspecified, fail, Sentinels, VersionTuple) from libclinic.codegen import CRenderData, TemplateDict, CodeGen @@ -101,7 +101,7 @@ def compiler_deprecated_warning( code = self.COMPILER_DEPRECATION_WARNING_PROTOTYPE.format( major=minversion[0], minor=minversion[1], - message=libclinic.c_repr(message), + message=libclinic.c_str_repr(message), ) return libclinic.normalize_snippet(code) diff --git a/Tools/clinic/libclinic/converter.py b/Tools/clinic/libclinic/converter.py index 2c93dda3541030..c10235237d4b71 100644 --- a/Tools/clinic/libclinic/converter.py +++ b/Tools/clinic/libclinic/converter.py @@ -6,7 +6,7 @@ import libclinic from libclinic import fail -from libclinic import Sentinels, unspecified, unknown +from libclinic import Sentinels, unspecified, unknown, NULL from libclinic.codegen import CRenderData, Include, TemplateDict from libclinic.function import Function, Parameter @@ -83,9 +83,9 @@ class CConverter(metaclass=CConverterAutoRegister): # at runtime). default: object = unspecified - # If not None, default must be isinstance() of this type. + # default must be isinstance() of this type. # (You can also specify a tuple of types.) - default_type: bltns.type[object] | tuple[bltns.type[object], ...] | None = None + default_type: bltns.type[object] | tuple[bltns.type[object], ...] = object # "default" converted into a C value, as a string. # Or None if there is no default. @@ -95,6 +95,13 @@ class CConverter(metaclass=CConverterAutoRegister): # Or None if there is no default. py_default: str | None = None + # The default value used to initialize the C variable when + # there is no default. + # + # Every non-abstract subclass with non-trivial cleanup() should supply + # a valid value. + c_init_default: str = '' + # The default value used to initialize the C variable when # there is no default, but not specifying a default may # result in an "uninitialized variable" warning. This can @@ -105,7 +112,7 @@ class CConverter(metaclass=CConverterAutoRegister): # # This value is specified as a string. # Every non-abstract subclass should supply a valid value. - c_ignored_default: str = 'NULL' + c_ignored_default: str = '' # If true, wrap with Py_UNUSED. unused = False @@ -182,9 +189,25 @@ def __init__(self, self.unused = unused self._includes: list[Include] = [] + if c_default: + self.c_default = c_default + if py_default: + self.py_default = py_default + + if annotation is not unspecified: + fail("The 'annotation' parameter is not currently permitted.") + + # Make sure not to set self.function until after converter_init() has been called. + # This prevents you from caching information + # about the function in converter_init(). + # (That breaks if we get cloned.) + self.converter_init(**kwargs) + if default is not unspecified: - if (self.default_type - and default is not unknown + if self.default_type == (): + conv_name = self.__class__.__name__.removesuffix('_converter') + fail(f"A '{conv_name}' parameter cannot be marked optional.") + if (default is not unknown and not isinstance(default, self.default_type) ): if isinstance(self.default_type, type): @@ -197,19 +220,38 @@ def __init__(self, f"{name!r} is not of type {types_str!r}") self.default = default - if c_default: - self.c_default = c_default - if py_default: - self.py_default = py_default - - if annotation is not unspecified: - fail("The 'annotation' parameter is not currently permitted.") + if not self.c_default: + if default is unspecified: + if self.c_init_default: + self.c_default = self.c_init_default + elif default is NULL: + self.c_default = self.c_ignored_default or self.c_init_default + if not self.c_default: + cls_name = self.__class__.__name__ + fail(f"{cls_name}: c_default is required for " + f"default value NULL") + else: + assert default is not unknown + self.c_default_init() + if not self.c_default: + if default is None: + self.c_default = self.c_init_default + if not self.c_default: + cls_name = self.__class__.__name__ + fail(f"{cls_name}: c_default is required for " + f"default value None") + elif isinstance(default, str): + self.c_default = libclinic.c_str_repr(default) + elif isinstance(default, bytes): + self.c_default = libclinic.c_bytes_repr(default) + elif isinstance(default, (int, float)): + self.c_default = repr(default) + else: + cls_name = self.__class__.__name__ + fail(f"{cls_name}: c_default is required for " + f"default value {default!r}") + fail(f"Unsupported default value {default!r}.") - # Make sure not to set self.function until after converter_init() has been called. - # This prevents you from caching information - # about the function in converter_init(). - # (That breaks if we get cloned.) - self.converter_init(**kwargs) self.function = function # Add a custom __getattr__ method to improve the error message @@ -233,6 +275,9 @@ def __getattr__(self, attr): def converter_init(self) -> None: pass + def c_default_init(self) -> None: + return + def is_optional(self) -> bool: return (self.default is not unspecified) @@ -274,7 +319,7 @@ def _render_non_self( data.modifications.append('/* modifications for ' + name + ' */\n' + modifications.rstrip()) # keywords - if parameter.is_vararg(): + if parameter.is_variable_length(): pass elif parameter.is_positional_only(): data.keywords.append('') @@ -324,7 +369,7 @@ def parse_argument(self, args: list[str]) -> None: args.append(self.converter) if self.encoding: - args.append(libclinic.c_repr(self.encoding)) + args.append(libclinic.c_str_repr(self.encoding)) elif self.subclass_of: args.append(self.subclass_of) @@ -371,7 +416,7 @@ def declaration(self, *, in_parser: bool = False) -> str: declaration = [self.simple_declaration(in_parser=True)] default = self.c_default if not default and self.parameter.group: - default = self.c_ignored_default + default = self.c_ignored_default or self.c_init_default if default: declaration.append(" = ") declaration.append(default) diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index 6e89e8de7cccf1..76091a9eedc1bf 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -4,7 +4,7 @@ from types import NoneType from typing import Any -from libclinic import fail, Null, unspecified, unknown +from libclinic import fail, NullType, unspecified, NULL, c_bytes_repr, c_unichar_repr from libclinic.function import ( Function, Parameter, CALLABLE, STATIC_METHOD, CLASS_METHOD, METHOD_INIT, METHOD_NEW, @@ -19,6 +19,8 @@ class BaseUnsignedIntConverter(CConverter): bitwise = False + default_type = int + c_ignored_default = '0' def use_converter(self) -> None: if self.converter: @@ -107,12 +109,13 @@ class bool_converter(CConverter): def converter_init(self, *, accept: TypeSet = {object}) -> None: if accept == {int}: self.format_unit = 'i' + self.default_type = int # type: ignore[assignment] elif accept != {object}: fail(f"bool_converter: illegal 'accept' argument {accept!r}") - if self.default is not unspecified and self.default is not unknown: - self.default = bool(self.default) - if self.c_default in {'Py_True', 'Py_False'}: - self.c_default = str(int(self.default)) + + def c_default_init(self) -> None: + assert isinstance(self.default, int) + self.c_default = str(int(self.default)) def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'i': @@ -140,6 +143,7 @@ class defining_class_converter(CConverter): this is the default converter used for the defining class. """ type = 'PyTypeObject *' + default_type = () format_unit = '' show_in_signature = False specified_type: str | None = None @@ -156,7 +160,7 @@ def set_template_dict(self, template_dict: TemplateDict) -> None: class char_converter(CConverter): type = 'char' - default_type = (bytes, bytearray) + default_type = bytes format_unit = 'c' c_ignored_default = "'\0'" @@ -165,9 +169,18 @@ def converter_init(self) -> None: if len(self.default) != 1: fail(f"char_converter: illegal default value {self.default!r}") - self.c_default = repr(bytes(self.default))[1:] - if self.c_default == '"\'"': - self.c_default = r"'\''" + def c_default_init(self) -> None: + default = self.default + assert isinstance(default, bytes) + if default == b"'": + self.c_default = r"'\''" + elif default == b'"': + self.c_default = r"""'"'""" + elif default == b'\0': + self.c_default = r"'\0'" + else: + r = c_bytes_repr(default)[1:-1] + self.c_default = "'" + r + "'" def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'c': @@ -207,7 +220,6 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st @add_legacy_c_converter('B', bitwise=True) class unsigned_char_converter(BaseUnsignedIntConverter): type = 'unsigned char' - default_type = int format_unit = 'b' c_ignored_default = "'\0'" @@ -282,8 +294,6 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st class unsigned_short_converter(BaseUnsignedIntConverter): type = 'unsigned short' - default_type = int - c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: self.bitwise = bitwise @@ -305,11 +315,19 @@ def converter_init( ) -> None: if accept == {str}: self.format_unit = 'C' + self.default_type = str # type: ignore[assignment] + if isinstance(self.default, str): + if len(self.default) != 1: + fail(f"int_converter: illegal default value {self.default!r}") elif accept != {int}: fail(f"int_converter: illegal 'accept' argument {accept!r}") if type is not None: self.type = type + def c_default_init(self) -> None: + if isinstance(self.default, str): + self.c_default = c_unichar_repr(self.default) + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'i': return self.format_code(""" @@ -343,8 +361,6 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st class unsigned_int_converter(BaseUnsignedIntConverter): type = 'unsigned int' - default_type = int - c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: self.bitwise = bitwise @@ -374,8 +390,6 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st class unsigned_long_converter(BaseUnsignedIntConverter): type = 'unsigned long' - default_type = int - c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: self.bitwise = bitwise @@ -405,8 +419,6 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st class unsigned_long_long_converter(BaseUnsignedIntConverter): type = 'unsigned long long' - default_type = int - c_ignored_default = "0" def converter_init(self, *, bitwise: bool = False) -> None: self.bitwise = bitwise @@ -418,23 +430,42 @@ def converter_init(self, *, bitwise: bool = False) -> None: class Py_ssize_t_converter(CConverter): type = 'Py_ssize_t' + default_type = (int, NoneType) c_ignored_default = "0" - def converter_init(self, *, accept: TypeSet = {int}) -> None: + def converter_init(self, *, accept: TypeSet = {int}, + allow_negative: bool = True) -> None: + self.allow_negative = allow_negative if accept == {int}: self.format_unit = 'n' - self.default_type = int + self.default_type = int # type: ignore[assignment] elif accept == {int, NoneType}: - self.converter = '_Py_convert_optional_to_ssize_t' + if self.allow_negative: + self.converter = '_Py_convert_optional_to_ssize_t' + else: + self.converter = '_Py_convert_optional_to_non_negative_ssize_t' else: fail(f"Py_ssize_t_converter: illegal 'accept' argument {accept!r}") def use_converter(self) -> None: - if self.converter == '_Py_convert_optional_to_ssize_t': - self.add_include('pycore_abstract.h', - '_Py_convert_optional_to_ssize_t()') + if self.converter in { + '_Py_convert_optional_to_ssize_t', + '_Py_convert_optional_to_non_negative_ssize_t', + }: + self.add_include('pycore_abstract.h', f'{self.converter}()') def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + if self.allow_negative: + non_negative_check = '' + else: + non_negative_check = self.format_code(""" + if ({paramname} < 0) {{{{ + PyErr_SetString(PyExc_ValueError, + "{paramname} cannot be negative"); + goto exit; + }}}}""", + argname=argname, + ) if self.format_unit == 'n': if limited_capi: PyNumber_Index = 'PyNumber_Index' @@ -452,11 +483,13 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st if (ival == -1 && PyErr_Occurred()) {{{{ goto exit; }}}} - {paramname} = ival; + {paramname} = ival;{non_negative_check} }}}} """, argname=argname, - PyNumber_Index=PyNumber_Index) + PyNumber_Index=PyNumber_Index, + non_negative_check=non_negative_check, + ) if not limited_capi: return super().parse_arg(argname, displayname, limited_capi=limited_capi) return self.format_code(""" @@ -465,7 +498,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st {paramname} = PyNumber_AsSsize_t({argname}, PyExc_OverflowError); if ({paramname} == -1 && PyErr_Occurred()) {{{{ goto exit; - }}}} + }}}}{non_negative_check} }}}} else {{{{ {bad_argument} @@ -475,15 +508,19 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st """, argname=argname, bad_argument=self.bad_argument(displayname, 'integer or None', limited_capi=limited_capi), + non_negative_check=non_negative_check, ) class slice_index_converter(CConverter): type = 'Py_ssize_t' + default_type = (int, NoneType) + c_ignored_default = "0" def converter_init(self, *, accept: TypeSet = {int, NoneType}) -> None: if accept == {int}: self.converter = '_PyEval_SliceIndexNotNone' + self.default_type = int # type: ignore[assignment] self.nullable = False elif accept == {int, NoneType}: self.converter = '_PyEval_SliceIndex' @@ -533,7 +570,6 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st class size_t_converter(BaseUnsignedIntConverter): type = 'size_t' converter = '_PyLong_Size_t_Converter' - c_ignored_default = "0" def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'n': @@ -652,6 +688,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st class object_converter(CConverter): type = 'PyObject *' format_unit = 'O' + c_ignored_default = 'NULL' def converter_init( self, *, @@ -671,6 +708,10 @@ def converter_init( if type is not None: self.type = type + def c_default_init(self) -> None: + default = self.default + if default is None or isinstance(default, bool): + self.c_default = "Py_" + repr(default) # # We define three conventions for buffer types in the 'accept' argument: @@ -700,8 +741,9 @@ def str_converter_key( class str_converter(CConverter): type = 'const char *' - default_type = (str, Null, NoneType) + default_type = (str, bytes, NullType, NoneType) format_unit = 's' + c_ignored_default = 'NULL' def converter_init( self, @@ -719,14 +761,16 @@ def converter_init( self.format_unit = format_unit self.length = bool(zeroes) if encoding: - if self.default not in (Null, None, unspecified): + if self.default not in (NULL, None, unspecified): fail("str_converter: Argument Clinic doesn't support default values for encoded strings") self.encoding = encoding self.type = 'char *' # sorry, clinic can't support preallocated buffers # for es# and et# self.c_default = "NULL" - if NoneType in accept and self.c_default == "Py_None": + + def c_default_init(self) -> None: + if self.default is None: self.c_default = "NULL" def post_parsing(self) -> str: @@ -839,6 +883,7 @@ class PyBytesObject_converter(CConverter): type = 'PyBytesObject *' format_unit = 'S' # accept = {bytes} + c_ignored_default = 'NULL' def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'S': @@ -859,6 +904,7 @@ class PyByteArrayObject_converter(CConverter): type = 'PyByteArrayObject *' format_unit = 'Y' # accept = {bytearray} + c_ignored_default = 'NULL' def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'Y': @@ -877,8 +923,9 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st class unicode_converter(CConverter): type = 'PyObject *' - default_type = (str, Null, NoneType) + default_type = (str, NullType, NoneType) format_unit = 'U' + c_ignored_default = 'NULL' def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'U': @@ -895,13 +942,34 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st return super().parse_arg(argname, displayname, limited_capi=limited_capi) +class _unicode_fs_converter_base(CConverter): + type = 'PyObject *' + default_type = NullType + c_init_default = 'NULL' + + def c_default_init(self) -> None: + fail(f"{self.__class__.__name__} does not support default values") + + def cleanup(self) -> str: + return f"Py_XDECREF({self.parser_name});" + + +class unicode_fs_encoded_converter(_unicode_fs_converter_base): + converter = 'PyUnicode_FSConverter' + + +class unicode_fs_decoded_converter(_unicode_fs_converter_base): + converter = 'PyUnicode_FSDecoder' + + @add_legacy_c_converter('u') @add_legacy_c_converter('u#', zeroes=True) @add_legacy_c_converter('Z', accept={str, NoneType}) @add_legacy_c_converter('Z#', accept={str, NoneType}, zeroes=True) class Py_UNICODE_converter(CConverter): type = 'const wchar_t *' - default_type = (str, Null, NoneType) + default_type = (str, NullType, NoneType) + c_ignored_default = 'NULL' def converter_init( self, *, @@ -917,6 +985,7 @@ def converter_init( self.accept = accept if accept == {str}: self.converter = '_PyUnicode_WideCharString_Converter' + self.default_type = (str, NullType) # type: ignore[assignment] elif accept == {str, NoneType}: self.converter = '_PyUnicode_WideCharString_Opt_Converter' else: @@ -972,28 +1041,34 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st @add_legacy_c_converter('w*', accept={rwbuffer}) class Py_buffer_converter(CConverter): type = 'Py_buffer' + default_type = (str, bytes, NullType, NoneType) format_unit = 'y*' impl_by_reference = True - c_ignored_default = "{NULL, NULL}" + c_init_default = "{NULL, NULL}" def converter_init(self, *, accept: TypeSet = {buffer}) -> None: - if self.default not in (unspecified, None): - fail("The only legal default value for Py_buffer is None.") - - self.c_default = self.c_ignored_default - if accept == {str, buffer, NoneType}: - format_unit = 'z*' + self.format_unit = 'z*' + self.default_type = (str, bytes, NullType, NoneType) elif accept == {str, buffer}: - format_unit = 's*' + self.format_unit = 's*' + self.default_type = (str, bytes, NullType) # type: ignore[assignment] elif accept == {buffer}: - format_unit = 'y*' + self.format_unit = 'y*' + self.default_type = (bytes, NullType) # type: ignore[assignment] elif accept == {rwbuffer}: - format_unit = 'w*' + self.format_unit = 'w*' + self.default_type = NullType # type: ignore[assignment] else: fail("Py_buffer_converter: illegal combination of arguments") - self.format_unit = format_unit + def c_default_init(self) -> None: + default = self.default + if isinstance(default, bytes): + self.c_default = f'{{.buf = {c_bytes_repr(default)}, .obj = NULL, .len = {len(default)}}}' + elif isinstance(default, str): + default = default.encode() + self.c_default = f'{{.buf = {c_bytes_repr(default)}, .obj = NULL, .len = {len(default)}}}' def cleanup(self) -> str: name = self.name @@ -1074,6 +1149,7 @@ class self_converter(CConverter): this is the default converter used for "self". """ type: str | None = None + default_type = () format_unit = '' specified_type: str | None = None @@ -1188,6 +1264,7 @@ def use_pyobject_self(self, func: Function) -> bool: # Converters for var-positional parameter. class VarPosCConverter(CConverter): + default_type = () format_unit = '' def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: @@ -1200,8 +1277,7 @@ def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, class varpos_tuple_converter(VarPosCConverter): type = 'PyObject *' - format_unit = '' - c_default = 'NULL' + c_init_default = 'NULL' def cleanup(self) -> str: return f"""Py_XDECREF({self.parser_name});\n""" @@ -1225,13 +1301,12 @@ def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, }}}} """ else: - self.add_include('pycore_tuple.h', '_PyTuple_FromArray()') start = f'args + {max_pos}' if max_pos else 'args' size = f'nargs - {max_pos}' if max_pos else 'nargs' if min(pos_only, min_pos) < max_pos: return f""" {paramname} = nargs > {max_pos} - ? _PyTuple_FromArray({start}, {size}) + ? PyTuple_FromArray({start}, {size}) : PyTuple_New(0); if ({paramname} == NULL) {{{{ goto exit; @@ -1239,7 +1314,7 @@ def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, """ else: return f""" - {paramname} = _PyTuple_FromArray({start}, {size}); + {paramname} = PyTuple_FromArray({start}, {size}); if ({paramname} == NULL) {{{{ goto exit; }}}} @@ -1259,7 +1334,6 @@ def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, class varpos_array_converter(VarPosCConverter): type = 'PyObject * const *' length = True - c_ignored_default = '' def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, fastcall: bool, limited_capi: bool) -> str: @@ -1279,3 +1353,38 @@ def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int, {paramname} = {start}; {self.length_name} = {size}; """ + + +# Converters for var-keyword parameters. + +class VarKeywordCConverter(CConverter): + default_type = () + format_unit = '' + + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + raise AssertionError('should never be called') + + def parse_var_keyword(self) -> str: + raise NotImplementedError + + +class var_keyword_dict_converter(VarKeywordCConverter): + type = 'PyObject *' + c_init_default = 'NULL' + + def cleanup(self) -> str: + return f'Py_XDECREF({self.parser_name});\n' + + def parse_var_keyword(self) -> str: + param_name = self.parser_name + return f""" + if (kwargs == NULL) {{{{ + {param_name} = PyDict_New(); + if ({param_name} == NULL) {{{{ + goto exit; + }}}} + }}}} + else {{{{ + {param_name} = Py_NewRef(kwargs); + }}}} + """ diff --git a/Tools/clinic/libclinic/dsl_parser.py b/Tools/clinic/libclinic/dsl_parser.py index eca41531f7c8e9..90e2e0d3d9c928 100644 --- a/Tools/clinic/libclinic/dsl_parser.py +++ b/Tools/clinic/libclinic/dsl_parser.py @@ -7,7 +7,7 @@ import shlex import sys from collections.abc import Callable -from types import FunctionType, NoneType +from types import FunctionType from typing import TYPE_CHECKING, Any, NamedTuple import libclinic @@ -246,6 +246,7 @@ def dedent(self, line: str) -> str: class DSLParser: function: Function | None state: StateKeeper + expecting_parameters: bool keyword_only: bool positional_only: bool deprecated_positional: VersionTuple | None @@ -262,6 +263,8 @@ class DSLParser: target_critical_section: list[str] disable_fastcall: bool from_version_re = re.compile(r'([*/]) +\[from +(.+)\]') + permit_long_summary = False + permit_long_docstring_body = False def __init__(self, clinic: Clinic) -> None: self.clinic = clinic @@ -283,6 +286,7 @@ def __init__(self, clinic: Clinic) -> None: def reset(self) -> None: self.function = None self.state = self.state_dsl_start + self.expecting_parameters = True self.keyword_only = False self.positional_only = False self.deprecated_positional = None @@ -298,6 +302,8 @@ def reset(self) -> None: self.critical_section = False self.target_critical_section = [] self.disable_fastcall = False + self.permit_long_summary = False + self.permit_long_docstring_body = False def directive_module(self, name: str) -> None: fields = name.split('.')[:-1] @@ -470,6 +476,16 @@ def at_text_signature(self, text_signature: str) -> None: fail("Called @text_signature twice!") self.forced_text_signature = text_signature + def at_permit_long_summary(self) -> None: + if self.permit_long_summary: + fail("Called @permit_long_summary twice!") + self.permit_long_summary = True + + def at_permit_long_docstring_body(self) -> None: + if self.permit_long_docstring_body: + fail("Called @permit_long_docstring_body twice!") + self.permit_long_docstring_body = True + def parse(self, block: Block) -> None: self.reset() self.block = block @@ -862,6 +878,10 @@ def state_parameter(self, line: str) -> None: def parse_parameter(self, line: str) -> None: assert self.function is not None + if not self.expecting_parameters: + fail('Encountered parameter line when not expecting ' + f'parameters: {line}') + match self.parameter_state: case ParamState.START | ParamState.REQUIRED: self.to_required() @@ -895,35 +915,49 @@ def parse_parameter(self, line: str) -> None: if len(function_args.args) > 1: fail(f"Function {self.function.name!r} has an " f"invalid parameter declaration (comma?): {line!r}") - if function_args.kwarg: - fail(f"Function {self.function.name!r} has an " - f"invalid parameter declaration (**kwargs?): {line!r}") + is_vararg = is_var_keyword = False if function_args.vararg: self.check_previous_star() self.check_remaining_star() is_vararg = True parameter = function_args.vararg + elif function_args.kwarg: + # If the existing parameters are all positional only or ``*args`` + # (var-positional), then we allow ``**kwds`` (var-keyword). + # Currently, pos-or-keyword or keyword-only arguments are not + # allowed with the ``**kwds`` converter. + has_non_positional_param = any( + p.is_positional_or_keyword() or p.is_keyword_only() + for p in self.function.parameters.values() + ) + if has_non_positional_param: + fail(f"Function {self.function.name!r} has an " + f"invalid parameter declaration (**kwargs?): {line!r}") + is_var_keyword = True + parameter = function_args.kwarg else: - is_vararg = False parameter = function_args.args[0] parameter_name = parameter.arg name, legacy, kwargs = self.parse_converter(parameter.annotation) if is_vararg: - name = 'varpos_' + name + name = f'varpos_{name}' + elif is_var_keyword: + name = f'var_keyword_{name}' value: object + has_c_default = 'c_default' in kwargs if not function_args.defaults: - if is_vararg: - value = NULL - else: - if self.parameter_state is ParamState.OPTIONAL: - fail(f"Can't have a parameter without a default ({parameter_name!r}) " - "after a parameter with a default!") - value = unspecified + value = unspecified + if (not is_vararg and not is_var_keyword + and self.parameter_state is ParamState.OPTIONAL): + fail(f"Can't have a parameter without a default ({parameter_name!r}) " + "after a parameter with a default!") if 'py_default' in kwargs: fail("You can't specify py_default without specifying a default value!") + if has_c_default: + fail("You can't specify c_default without specifying a default value!") else: expr = function_args.defaults[0] default = ast_input[expr.col_offset: expr.end_col_offset].strip() @@ -932,7 +966,7 @@ def parse_parameter(self, line: str) -> None: self.parameter_state = ParamState.OPTIONAL bad = False try: - if 'c_default' not in kwargs: + if not has_c_default: # we can only represent very simple data values in C. # detect whether default is okay, via a denylist # of disallowed ast nodes. @@ -978,18 +1012,15 @@ def bad_node(self, node: ast.AST) -> None: fail(f"Unsupported expression as default value: {default!r}") # mild hack: explicitly support NULL as a default value - c_default: str | None if isinstance(expr, ast.Name) and expr.id == 'NULL': value = NULL py_default = '<unrepresentable>' - c_default = "NULL" elif (isinstance(expr, ast.BinOp) or (isinstance(expr, ast.UnaryOp) and not (isinstance(expr.operand, ast.Constant) and type(expr.operand.value) in {int, float, complex}) )): - c_default = kwargs.get("c_default") - if not (isinstance(c_default, str) and c_default): + if not has_c_default: fail(f"When you specify an expression ({default!r}) " f"as your default value, " f"you MUST specify a valid c_default.", @@ -1008,8 +1039,7 @@ def bad_node(self, node: ast.AST) -> None: a.append(n.id) py_default = ".".join(reversed(a)) - c_default = kwargs.get("c_default") - if not (isinstance(c_default, str) and c_default): + if not has_c_default: fail(f"When you specify a named constant ({py_default!r}) " "as your default value, " "you MUST specify a valid c_default.") @@ -1021,23 +1051,15 @@ def bad_node(self, node: ast.AST) -> None: else: value = ast.literal_eval(expr) py_default = repr(value) - if isinstance(value, (bool, NoneType)): - c_default = "Py_" + py_default - elif isinstance(value, str): - c_default = libclinic.c_repr(value) - else: - c_default = py_default except (ValueError, AttributeError): value = unknown - c_default = kwargs.get("c_default") py_default = default - if not (isinstance(c_default, str) and c_default): + if not has_c_default: fail("When you specify a named constant " f"({py_default!r}) as your default value, " "you MUST specify a valid c_default.") - kwargs.setdefault('c_default', c_default) kwargs.setdefault('py_default', py_default) dict = legacy_converters if legacy else converters @@ -1051,6 +1073,8 @@ def bad_node(self, node: ast.AST) -> None: kind: inspect._ParameterKind if is_vararg: kind = inspect.Parameter.VAR_POSITIONAL + elif is_var_keyword: + kind = inspect.Parameter.VAR_KEYWORD elif self.keyword_only: kind = inspect.Parameter.KEYWORD_ONLY else: @@ -1058,12 +1082,10 @@ def bad_node(self, node: ast.AST) -> None: if isinstance(converter, self_converter): if len(self.function.parameters) == 1: - if self.parameter_state is not ParamState.REQUIRED: - fail("A 'self' parameter cannot be marked optional.") - if value is not unspecified: - fail("A 'self' parameter cannot have a default value.") if self.group: fail("A 'self' parameter cannot be in an optional group.") + assert self.parameter_state is ParamState.REQUIRED + assert value is unspecified kind = inspect.Parameter.POSITIONAL_ONLY self.parameter_state = ParamState.START self.function.parameters.clear() @@ -1074,14 +1096,12 @@ def bad_node(self, node: ast.AST) -> None: if isinstance(converter, defining_class_converter): _lp = len(self.function.parameters) if _lp == 1: - if self.parameter_state is not ParamState.REQUIRED: - fail("A 'defining_class' parameter cannot be marked optional.") - if value is not unspecified: - fail("A 'defining_class' parameter cannot have a default value.") if self.group: fail("A 'defining_class' parameter cannot be in an optional group.") if self.function.cls is None: fail("A 'defining_class' parameter cannot be defined at module level.") + assert self.parameter_state is ParamState.REQUIRED + assert value is unspecified kind = inspect.Parameter.POSITIONAL_ONLY else: fail("A 'defining_class' parameter, if specified, must either " @@ -1104,6 +1124,8 @@ def bad_node(self, node: ast.AST) -> None: if is_vararg: self.keyword_only = True + if is_var_keyword: + self.expecting_parameters = False @staticmethod def parse_converter( @@ -1145,6 +1167,9 @@ def parse_star(self, function: Function, version: VersionTuple | None) -> None: The 'version' parameter signifies the future version from which the marker will take effect (None means it is already in effect). """ + if not self.expecting_parameters: + fail("Encountered '*' when not expecting parameters") + if version is None: self.check_previous_star() self.check_remaining_star() @@ -1200,6 +1225,9 @@ def parse_slash(self, function: Function, version: VersionTuple | None) -> None: The 'version' parameter signifies the future version from which the marker will take effect (None means it is already in effect). """ + if not self.expecting_parameters: + fail("Encountered '/' when not expecting parameters") + if version is None: if self.deprecated_keyword: fail(f"Function {function.name!r}: '/' must precede '/ [from ...]'") @@ -1436,11 +1464,13 @@ def add_parameter(text: str) -> None: if p.is_vararg(): p_lines.append("*") added_star = True + if p.is_var_keyword(): + p_lines.append("**") name = p.converter.signature_name or p.name p_lines.append(name) - if not p.is_vararg() and p.converter.is_optional(): + if not p.is_variable_length() and p.converter.is_optional(): p_lines.append('=') value = p.converter.py_default if not value: @@ -1515,6 +1545,30 @@ def format_docstring(self) -> str: # between it and the {parameters} we're about to add. lines.append('') + # Fail if the summary line is too long. + # Warn if any of the body lines are too long. + # Existing violations are recorded in OVERLONG_{SUMMARY,BODY}. + max_width = f.docstring_line_width + summary_len = len(lines[0]) + max_body = max(map(len, lines[1:])) + if summary_len > max_width: + if not self.permit_long_summary: + fail(f"Summary line for {f.full_name!r} is too long!\n" + f"The summary line must be no longer than {max_width} characters.") + else: + if self.permit_long_summary: + warn("Remove the @permit_long_summary decorator from " + f"{f.full_name!r}!\n") + + if max_body > max_width: + if not self.permit_long_docstring_body: + warn(f"Docstring lines for {f.full_name!r} are too long!\n" + f"Lines should be no longer than {max_width} characters.") + else: + if self.permit_long_docstring_body: + warn("Remove the @permit_long_docstring_body decorator from " + f"{f.full_name!r}!\n") + parameters_marker_count = len(f.docstring.split('{parameters}')) - 1 if parameters_marker_count > 1: fail('You may not specify {parameters} more than once in a docstring!') @@ -1545,8 +1599,11 @@ def check_remaining_star(self, lineno: int | None = None) -> None: for p in reversed(self.function.parameters.values()): if self.keyword_only: - if (p.kind == inspect.Parameter.KEYWORD_ONLY or - p.kind == inspect.Parameter.VAR_POSITIONAL): + if p.kind in { + inspect.Parameter.KEYWORD_ONLY, + inspect.Parameter.VAR_POSITIONAL, + inspect.Parameter.VAR_KEYWORD + }: return elif self.deprecated_positional: if p.deprecated_positional == self.deprecated_positional: diff --git a/Tools/clinic/libclinic/formatting.py b/Tools/clinic/libclinic/formatting.py index 873ece6210017a..264327818c1d19 100644 --- a/Tools/clinic/libclinic/formatting.py +++ b/Tools/clinic/libclinic/formatting.py @@ -39,8 +39,55 @@ def _quoted_for_c_string(text: str) -> str: return text -def c_repr(text: str) -> str: - return '"' + text + '"' +# Use octals, because \x... in C has arbitrary number of hexadecimal digits. +_c_repr = [chr(i) if 32 <= i < 127 else fr'\{i:03o}' for i in range(256)] +_c_repr[ord('"')] = r'\"' +_c_repr[ord('\\')] = r'\\' +_c_repr[ord('\a')] = r'\a' +_c_repr[ord('\b')] = r'\b' +_c_repr[ord('\f')] = r'\f' +_c_repr[ord('\n')] = r'\n' +_c_repr[ord('\r')] = r'\r' +_c_repr[ord('\t')] = r'\t' +_c_repr[ord('\v')] = r'\v' + +def _break_trigraphs(s: str) -> str: + # Prevent trigraphs from being interpreted inside string literals. + if '??' in s: + s = s.replace('??', r'?\?') + s = s.replace(r'\??', r'\?\?') + # Also Argument Clinic does not like comment-like sequences + # in string literals. + s = s.replace(r'/*', r'/\*') + s = s.replace(r'*/', r'*\/') + return s + +def c_bytes_repr(data: bytes) -> str: + r = ''.join(_c_repr[i] for i in data) + r = _break_trigraphs(r) + return '"' + r + '"' + +def c_str_repr(text: str) -> str: + r = ''.join(_c_repr[i] if i < 0x80 + else fr'\u{i:04x}' if i < 0x10000 + else fr'\U{i:08x}' + for i in map(ord, text)) + r = _break_trigraphs(r) + return '"' + r + '"' + +def c_unichar_repr(char: str) -> str: + if char == "'": + return r"'\''" + if char == '"': + return """'"'""" + if char == '\0': + return '0' + i = ord(char) + if i < 0x80: + r = _c_repr[i] + if not r.startswith((r'\0', r'\1')): + return "'" + r + "'" + return f'0x{i:02x}' def wrapped_c_string_literal( @@ -58,8 +105,8 @@ def wrapped_c_string_literal( drop_whitespace=False, break_on_hyphens=False, ) - separator = c_repr(suffix + "\n" + subsequent_indent * " ") - return initial_indent * " " + c_repr(separator.join(wrapped)) + separator = '"' + suffix + "\n" + subsequent_indent * " " + '"' + return initial_indent * " " + '"' + separator.join(wrapped) + '"' def _add_prefix_and_suffix(text: str, *, prefix: str = "", suffix: str = "") -> str: diff --git a/Tools/clinic/libclinic/function.py b/Tools/clinic/libclinic/function.py index e80e2f5f13f648..1c643caea98e3b 100644 --- a/Tools/clinic/libclinic/function.py +++ b/Tools/clinic/libclinic/function.py @@ -167,6 +167,19 @@ def methoddef_flags(self) -> str | None: flags.append('METH_COEXIST') return '|'.join(flags) + @property + def docstring_line_width(self) -> int: + """Return the maximum line width for docstring lines. + + Pydoc adds indentation when displaying functions and methods. + To keep the total width of within 80 characters, we use a + maximum of 72 characters for global functions and classes, + and 68 characters for methods. + """ + if self.cls is not None and not self.kind.new_or_init: + return 68 + return 72 + def __repr__(self) -> str: return f'<clinic.Function {self.name!r}>' @@ -207,9 +220,18 @@ def is_keyword_only(self) -> bool: def is_positional_only(self) -> bool: return self.kind == inspect.Parameter.POSITIONAL_ONLY + def is_positional_or_keyword(self) -> bool: + return self.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD + def is_vararg(self) -> bool: return self.kind == inspect.Parameter.VAR_POSITIONAL + def is_var_keyword(self) -> bool: + return self.kind == inspect.Parameter.VAR_KEYWORD + + def is_variable_length(self) -> bool: + return self.is_vararg() or self.is_var_keyword() + def is_optional(self) -> bool: return not self.is_vararg() and (self.default is not unspecified) diff --git a/Tools/clinic/libclinic/parse_args.py b/Tools/clinic/libclinic/parse_args.py index 0e15d2f163b816..bca87ecd75100c 100644 --- a/Tools/clinic/libclinic/parse_args.py +++ b/Tools/clinic/libclinic/parse_args.py @@ -36,7 +36,7 @@ def declare_parser( num_keywords = len([ p for p in f.parameters.values() - if not p.is_positional_only() and not p.is_vararg() + if p.is_positional_or_keyword() or p.is_keyword_only() ]) condition = '#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)' @@ -220,6 +220,7 @@ class ParseArgsCodeGen: max_pos: int = 0 min_kw_only: int = 0 varpos: Parameter | None = None + var_keyword: Parameter | None = None docstring_prototype: str docstring_definition: str @@ -255,13 +256,24 @@ def __init__(self, func: Function, codegen: CodeGen) -> None: del self.parameters[i] break + for i, p in enumerate(self.parameters): + if p.is_var_keyword(): + self.var_keyword = p + del self.parameters[i] + break + self.converters = [p.converter for p in self.parameters] if self.func.critical_section: self.codegen.add_include('pycore_critical_section.h', 'Py_BEGIN_CRITICAL_SECTION()') + + # Use fastcall if not disabled, except if in a __new__ or + # __init__ method, or if there is a **kwargs parameter. if self.func.disable_fastcall: self.fastcall = False + elif self.var_keyword is not None: + self.fastcall = False else: self.fastcall = not self.is_new_or_init() @@ -469,6 +481,12 @@ def _parse_vararg(self) -> str: fastcall=self.fastcall, limited_capi=self.limited_capi) + def _parse_kwarg(self) -> str: + assert self.var_keyword is not None + c = self.var_keyword.converter + assert isinstance(c, libclinic.converters.VarKeywordCConverter) + return c.parse_var_keyword() + def parse_pos_only(self) -> None: if self.fastcall: # positional-only, but no option groups @@ -564,6 +582,8 @@ def parse_pos_only(self) -> None: parser_code.append("skip_optional:") if self.varpos: parser_code.append(libclinic.normalize_snippet(self._parse_vararg(), indent=4)) + elif self.var_keyword: + parser_code.append(libclinic.normalize_snippet(self._parse_kwarg(), indent=4)) else: for parameter in self.parameters: parameter.converter.use_converter() @@ -590,6 +610,45 @@ def parse_pos_only(self) -> None: """, indent=4)] self.parser_body(*parser_code) + def parse_var_keyword(self) -> None: + self.flags = "METH_VARARGS|METH_KEYWORDS" + self.parser_prototype = PARSER_PROTOTYPE_KEYWORD + nargs = 'PyTuple_GET_SIZE(args)' + + parser_code = [] + max_args = NO_VARARG if self.varpos else self.max_pos + if self.varpos is None and self.min_pos == self.max_pos == 0: + self.codegen.add_include('pycore_modsupport.h', + '_PyArg_NoPositional()') + parser_code.append(libclinic.normalize_snippet(""" + if (!_PyArg_NoPositional("{name}", args)) {{ + goto exit; + }} + """, indent=4)) + elif self.min_pos or max_args != NO_VARARG: + self.codegen.add_include('pycore_modsupport.h', + '_PyArg_CheckPositional()') + parser_code.append(libclinic.normalize_snippet(f""" + if (!_PyArg_CheckPositional("{{name}}", {nargs}, {self.min_pos}, {max_args})) {{{{ + goto exit; + }}}} + """, indent=4)) + + for i, p in enumerate(self.parameters): + parse_arg = p.converter.parse_arg( + f'PyTuple_GET_ITEM(args, {i})', + p.get_displayname(i+1), + limited_capi=self.limited_capi, + ) + assert parse_arg is not None + parser_code.append(libclinic.normalize_snippet(parse_arg, indent=4)) + + if self.varpos: + parser_code.append(libclinic.normalize_snippet(self._parse_vararg(), indent=4)) + if self.var_keyword: + parser_code.append(libclinic.normalize_snippet(self._parse_kwarg(), indent=4)) + self.parser_body(*parser_code) + def parse_general(self, clang: CLanguage) -> None: parsearg: str | None deprecated_positionals: dict[int, Parameter] = {} @@ -921,12 +980,14 @@ def parse_args(self, clang: CLanguage) -> dict[str, str]: # previous call to parser_body. this is used for an awful hack. self.parser_body_fields: tuple[str, ...] = () - if not self.parameters and not self.varpos: + if not self.parameters and not self.varpos and not self.var_keyword: self.parse_no_args() elif self.use_meth_o(): self.parse_one_arg() elif self.has_option_groups(): self.parse_option_groups() + elif self.var_keyword is not None: + self.parse_var_keyword() elif (not self.requires_defining_class and self.pos_only == len(self.parameters)): self.parse_pos_only() diff --git a/Tools/clinic/libclinic/utils.py b/Tools/clinic/libclinic/utils.py index 17e8f35be73bf4..3df64f270dd074 100644 --- a/Tools/clinic/libclinic/utils.py +++ b/Tools/clinic/libclinic/utils.py @@ -85,9 +85,9 @@ def __repr__(self) -> str: # This one needs to be a distinct class, unlike the other two -class Null: +class NullType: def __repr__(self) -> str: return '<Null>' -NULL = Null() +NULL = NullType() diff --git a/Tools/ftscalingbench/ftscalingbench.py b/Tools/ftscalingbench/ftscalingbench.py index 1a59e25189d5dd..c8a914c22a9e13 100644 --- a/Tools/ftscalingbench/ftscalingbench.py +++ b/Tools/ftscalingbench/ftscalingbench.py @@ -21,13 +21,17 @@ # > echo "0" | sudo tee /sys/devices/system/cpu/cpufreq/boost # +import copy import math import os import queue import sys import threading import time +from collections import namedtuple +from dataclasses import dataclass from operator import methodcaller +from typing import NamedTuple # The iterations in individual benchmarks are scaled by this factor. WORK_SCALE = 100 @@ -176,6 +180,12 @@ def create_dict(): "key": "value", } +if hasattr(__builtins__, "frozendict"): + @register_benchmark + def create_frozendict(): + for i in range(1000 * WORK_SCALE): + d = frozendict(key="value") + thread_local = threading.local() @register_benchmark @@ -202,6 +212,120 @@ def method_caller(): for i in range(1000 * WORK_SCALE): mc(obj) +@dataclass +class MyDataClass: + x: int + y: int + z: int + +@register_benchmark +def instantiate_dataclass(): + for _ in range(1000 * WORK_SCALE): + obj = MyDataClass(x=1, y=2, z=3) + +MyNamedTuple = namedtuple("MyNamedTuple", ["x", "y", "z"]) + +@register_benchmark +def instantiate_namedtuple(): + for _ in range(1000 * WORK_SCALE): + obj = MyNamedTuple(x=1, y=2, z=3) + + +class MyTypingNamedTuple(NamedTuple): + x: int + y: int + z: int + +@register_benchmark +def instantiate_typing_namedtuple(): + for _ in range(1000 * WORK_SCALE): + obj = MyTypingNamedTuple(x=1, y=2, z=3) + +@register_benchmark +def super_call(): + # TODO: super() on the same class from multiple threads still doesn't + # scale well, so use a class per-thread here for now. + class Base: + def method(self): + return 1 + + class Derived(Base): + def method(self): + return super().method() + + obj = Derived() + for _ in range(1000 * WORK_SCALE): + obj.method() + + +class MyClassMethod: + @classmethod + def my_classmethod(cls): + return cls + + @staticmethod + def my_staticmethod(): + pass + +@register_benchmark +def classmethod_call(): + obj = MyClassMethod() + for _ in range(1000 * WORK_SCALE): + obj.my_classmethod() + +@register_benchmark +def staticmethod_call(): + obj = MyClassMethod() + for _ in range(1000 * WORK_SCALE): + obj.my_staticmethod() + + +class MyDescriptor: + def __get__(self, obj, objtype=None): + return 42 + + def __set__(self, obj, value): + pass + +class MyClassWithDescriptor: + attr = MyDescriptor() + +@register_benchmark +def descriptor(): + obj = MyClassWithDescriptor() + for _ in range(1000 * WORK_SCALE): + obj.attr + +@register_benchmark +def deepcopy(): + x = {'list': [1, 2], 'tuple': (1, None)} + for i in range(40 * WORK_SCALE): + copy.deepcopy(x) + +@register_benchmark +def setattr_non_interned(): + prefix = "prefix" + obj = MyObject() + for _ in range(1000 * WORK_SCALE): + setattr(obj, f"{prefix}_a", None) + setattr(obj, f"{prefix}_b", None) + setattr(obj, f"{prefix}_c", None) + + +from enum import Enum +class MyEnum(Enum): + X = 1 + Y = 2 + Z = 3 + +@register_benchmark +def enum_attr(): + for _ in range(1000 * WORK_SCALE): + MyEnum.X + MyEnum.Y + MyEnum.Z + + def bench_one_thread(func): t0 = time.perf_counter_ns() func() diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 27aa6b0cc266d3..ba52ea2a30e0be 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -152,6 +152,11 @@ def write(self, data): def getvalue(self): return self._val + +def _PyStackRef_AsPyObjectBorrow(gdbval): + return gdb.Value(int(gdbval['bits']) & ~USED_TAGS) + + class PyObjectPtr(object): """ Class wrapping a gdb.Value that's either a (PyObject*) within the @@ -170,7 +175,7 @@ def __init__(self, gdbval, cast_to=None): if gdbval.type.name == '_PyStackRef': if cast_to is None: cast_to = gdb.lookup_type('PyObject').pointer() - self._gdbval = gdb.Value(int(gdbval['bits']) & ~USED_TAGS).cast(cast_to) + self._gdbval = _PyStackRef_AsPyObjectBorrow(gdbval).cast(cast_to) elif cast_to: self._gdbval = gdbval.cast(cast_to) else: @@ -347,6 +352,7 @@ def subclass_from_type(cls, t): 'frame': PyFrameObjectPtr, 'set' : PySetObjectPtr, 'frozenset' : PySetObjectPtr, + 'frozendict' : PyDictObjectPtr, 'builtin_function_or_method' : PyCFunctionObjectPtr, 'method-wrapper': wrapperobject, } @@ -810,12 +816,20 @@ def proxyval(self, visited): return result def write_repr(self, out, visited): + tp_name = self.safe_tp_name() + is_frozendict = (tp_name == "frozendict") + # Guard against infinite loops: if self.as_address() in visited: - out.write('{...}') + if is_frozendict: + out.write(tp_name + '({...})') + else: + out.write('{...}') return visited.add(self.as_address()) + if is_frozendict: + out.write(tp_name + '(') out.write('{') first = True for pyop_key, pyop_value in self.iteritems(): @@ -826,6 +840,8 @@ def write_repr(self, out, visited): out.write(': ') pyop_value.write_repr(out, visited) out.write('}') + if is_frozendict: + out.write(')') @staticmethod def _get_entries(keys): @@ -1034,30 +1050,49 @@ def write_repr(self, out, visited): return return self._frame.write_repr(out, visited) - def print_traceback(self): - if self.is_optimized_out(): - sys.stdout.write(' %s\n' % FRAME_INFO_OPTIMIZED_OUT) - return - return self._frame.print_traceback() - class PyFramePtr: def __init__(self, gdbval): self._gdbval = gdbval + if self.is_optimized_out(): + return + self.co = self._f_code() + if self.is_shim(): + return + self.co_name = self.co.pyop_field('co_name') + self.co_filename = self.co.pyop_field('co_filename') - if not self.is_optimized_out(): + self.f_lasti = self._f_lasti() + self.co_nlocals = int_from_int(self.co.field('co_nlocals')) + pnames = self.co.field('co_localsplusnames') + self.co_localsplusnames = PyTupleObjectPtr.from_pyobject_ptr(pnames) + + @staticmethod + def get_thread_state(): + exprs = [ + '_Py_tss_gilstate', # 3.15+ + '_Py_tss_tstate', # 3.12+ (and not when GIL is released) + 'pthread_getspecific(_PyRuntime.autoTSSkey._key)', # only live programs + '((struct pthread*)$fs_base)->specific_1stblock[_PyRuntime.autoTSSkey._key].data' # x86-64 + ] + for expr in exprs: try: - self.co = self._f_code() - self.co_name = self.co.pyop_field('co_name') - self.co_filename = self.co.pyop_field('co_filename') - - self.f_lasti = self._f_lasti() - self.co_nlocals = int_from_int(self.co.field('co_nlocals')) - pnames = self.co.field('co_localsplusnames') - self.co_localsplusnames = PyTupleObjectPtr.from_pyobject_ptr(pnames) - self._is_code = True - except: - self._is_code = False + val = gdb.parse_and_eval(f'(PyThreadState*)({expr})') + except gdb.error: + continue + if int(val) != 0: + return val + return None + + @staticmethod + def get_thread_local_frame(): + thread_state = PyFramePtr.get_thread_state() + if thread_state is None: + return None + current_frame = thread_state['current_frame'] + if int(current_frame) == 0: + return None + return PyFramePtr(current_frame) def is_optimized_out(self): return self._gdbval.is_optimized_out @@ -1115,6 +1150,8 @@ def is_shim(self): return self._f_special("owner", int) == FRAME_OWNED_BY_INTERPRETER def previous(self): + if int(self._gdbval['previous']) == 0: + return None return self._f_special("previous", PyFramePtr) def iter_globals(self): @@ -1243,6 +1280,27 @@ def print_traceback(self): lineno, self.co_name.proxyval(visited))) + def print_traceback_until_shim(self, frame_index=None): + # Print traceback for _PyInterpreterFrame and return previous frame + interp_frame = self + while True: + if not interp_frame: + sys.stdout.write(' (unable to read python frame information)\n') + return None + if interp_frame.is_shim(): + return interp_frame.previous() + + if frame_index is not None: + line = interp_frame.get_truncated_repr(MAX_OUTPUT_LEN) + sys.stdout.write('#%i %s\n' % (frame_index, line)) + else: + interp_frame.print_traceback() + if not interp_frame.is_optimized_out(): + line = interp_frame.current_line() + if line is not None: + sys.stdout.write(' %s\n' % line.strip()) + interp_frame = interp_frame.previous() + def get_truncated_repr(self, maxlen): ''' Get a repr-like string for the data, but truncate it at "maxlen" bytes @@ -1855,20 +1913,10 @@ def get_selected_bytecode_frame(cls): def print_summary(self): if self.is_evalframe(): interp_frame = self.get_pyop() - while True: - if interp_frame: - if interp_frame.is_shim(): - break - line = interp_frame.get_truncated_repr(MAX_OUTPUT_LEN) - sys.stdout.write('#%i %s\n' % (self.get_index(), line)) - if not interp_frame.is_optimized_out(): - line = interp_frame.current_line() - if line is not None: - sys.stdout.write(' %s\n' % line.strip()) - else: - sys.stdout.write('#%i (unable to read python frame information)\n' % self.get_index()) - break - interp_frame = interp_frame.previous() + if interp_frame: + interp_frame.print_traceback_until_shim(self.get_index()) + else: + sys.stdout.write('#%i (unable to read python frame information)\n' % self.get_index()) else: info = self.is_other_python_frame() if info: @@ -1876,29 +1924,6 @@ def print_summary(self): else: sys.stdout.write('#%i\n' % self.get_index()) - def print_traceback(self): - if self.is_evalframe(): - interp_frame = self.get_pyop() - while True: - if interp_frame: - if interp_frame.is_shim(): - break - interp_frame.print_traceback() - if not interp_frame.is_optimized_out(): - line = interp_frame.current_line() - if line is not None: - sys.stdout.write(' %s\n' % line.strip()) - else: - sys.stdout.write(' (unable to read python frame information)\n') - break - interp_frame = interp_frame.previous() - else: - info = self.is_other_python_frame() - if info: - sys.stdout.write(' %s\n' % info) - else: - sys.stdout.write(' (not a python frame)\n') - class PyList(gdb.Command): '''List the current Python source code, if any @@ -2042,6 +2067,41 @@ def invoke(self, args, from_tty): PyUp() PyDown() + +def print_traceback_helper(full_info): + frame = Frame.get_selected_python_frame() + interp_frame = PyFramePtr.get_thread_local_frame() + if not frame and not interp_frame: + print('Unable to locate python frame') + return + + sys.stdout.write('Traceback (most recent call first):\n') + if frame: + while frame: + frame_index = frame.get_index() if full_info else None + if frame.is_evalframe(): + pyop = frame.get_pyop() + if pyop is not None: + # Use the _PyInterpreterFrame from the gdb frame + interp_frame = pyop + if interp_frame: + interp_frame = interp_frame.print_traceback_until_shim(frame_index) + else: + sys.stdout.write(' (unable to read python frame information)\n') + else: + info = frame.is_other_python_frame() + if full_info: + if info: + sys.stdout.write('#%i %s\n' % (frame_index, info)) + elif info: + sys.stdout.write(' %s\n' % info) + frame = frame.older() + else: + # Fall back to just using the thread-local frame + while interp_frame: + interp_frame = interp_frame.print_traceback_until_shim() + + class PyBacktraceFull(gdb.Command): 'Display the current python frame and all the frames within its call stack (if any)' def __init__(self): @@ -2052,15 +2112,7 @@ def __init__(self): def invoke(self, args, from_tty): - frame = Frame.get_selected_python_frame() - if not frame: - print('Unable to locate python frame') - return - - while frame: - if frame.is_python_frame(): - frame.print_summary() - frame = frame.older() + print_traceback_helper(full_info=True) PyBacktraceFull() @@ -2072,18 +2124,8 @@ def __init__(self): gdb.COMMAND_STACK, gdb.COMPLETE_NONE) - def invoke(self, args, from_tty): - frame = Frame.get_selected_python_frame() - if not frame: - print('Unable to locate python frame') - return - - sys.stdout.write('Traceback (most recent call first):\n') - while frame: - if frame.is_python_frame(): - frame.print_traceback() - frame = frame.older() + print_traceback_helper(full_info=False) PyBacktrace() diff --git a/Tools/i18n/.ruff.toml b/Tools/i18n/.ruff.toml new file mode 100644 index 00000000000000..a8f4f2f5a96d7b --- /dev/null +++ b/Tools/i18n/.ruff.toml @@ -0,0 +1,10 @@ +extend = "../../.ruff.toml" # Inherit the project-wide settings + +target-version = "py313" + +[lint] +select = [ + "F", # pyflakes + "I", # isort + "UP", # pyupgrade +] diff --git a/Tools/i18n/makelocalealias.py b/Tools/i18n/makelocalealias.py index 02af1caff7d499..f825862ffdf350 100755 --- a/Tools/i18n/makelocalealias.py +++ b/Tools/i18n/makelocalealias.py @@ -8,6 +8,7 @@ """ import locale import sys + _locale = locale # Location of the X11 alias file. @@ -44,6 +45,13 @@ def parse(filename): # Ignore one letter locale mappings (except for 'c') if len(locale) == 1 and locale != 'c': continue + if '@' in locale and '@' not in alias: + # Do not simply remove the "@euro" modifier. + # Glibc generates separate locales with the "@euro" modifier, and + # not always generates a locale without it with the same encoding. + # It can also affect collation. + if locale.endswith('@euro') and not locale.endswith('.utf-8@euro'): + alias += '@euro' # Normalize encoding, if given if '.' in locale: lang, encoding = locale.split('.')[:2] @@ -51,6 +59,10 @@ def parse(filename): encoding = encoding.replace('_', '') locale = lang + '.' + encoding data[locale] = alias + # Conflict with glibc. + data.pop('el_gr@euro', None) + data.pop('uz_uz@cyrillic', None) + data.pop('uz_uz.utf8@cyrillic', None) return data def parse_glibc_supported(filename): @@ -81,7 +93,7 @@ def parse_glibc_supported(filename): # Add an encoding to alias alias, _, modifier = alias.partition('@') alias = _locale._replace_encoding(alias, alias_encoding) - if modifier and not (modifier == 'euro' and alias_encoding == 'ISO-8859-15'): + if modifier: alias += '@' + modifier data[locale] = alias return data @@ -89,16 +101,15 @@ def parse_glibc_supported(filename): def pprint(data): items = sorted(data.items()) for k, v in items: - print(' %-40s%a,' % ('%a:' % k, v)) + print(f" {k!a:<40}{v!a},") def print_differences(data, olddata): items = sorted(olddata.items()) for k, v in items: if k not in data: - print('# removed %a' % k) + print(f'# removed {k!a}') elif olddata[k] != data[k]: - print('# updated %a -> %a to %a' % \ - (k, olddata[k], data[k])) + print(f'# updated {k!a} -> {olddata[k]!a} to {data[k]!a}') # Additions are not mentioned def optimize(data): @@ -121,7 +132,7 @@ def check(data): errors = 0 for k, v in data.items(): if locale.normalize(k) != v: - print('ERROR: %a -> %a != %a' % (k, locale.normalize(k), v), + print(f'ERROR: {k!a} -> {locale.normalize(k)!a} != {v!a}', file=sys.stderr) errors += 1 return errors @@ -131,10 +142,10 @@ def check(data): parser = argparse.ArgumentParser() parser.add_argument('--locale-alias', default=LOCALE_ALIAS, help='location of the X11 alias file ' - '(default: %a)' % LOCALE_ALIAS) + f'(default: {LOCALE_ALIAS})') parser.add_argument('--glibc-supported', default=SUPPORTED, help='location of the glibc SUPPORTED locales file ' - '(default: %a)' % SUPPORTED) + f'(default: {SUPPORTED})') args = parser.parse_args() data = locale.locale_alias.copy() diff --git a/Tools/i18n/msgfmt.py b/Tools/i18n/msgfmt.py index cd5f1ed9f3e268..3351511bbc8f2b 100755 --- a/Tools/i18n/msgfmt.py +++ b/Tools/i18n/msgfmt.py @@ -5,8 +5,7 @@ This program converts a textual Uniforum-style message catalog (.po file) into a binary GNU catalog (.mo file). This is essentially the same function as the -GNU msgfmt program, however, it is a simpler implementation. Currently it -does not handle plural forms but it does handle message contexts. +GNU msgfmt program, however, it is a simpler implementation. Usage: msgfmt.py [OPTIONS] filename.po @@ -25,14 +24,14 @@ Display version information and exit. """ -import os -import sys +import array import ast +import codecs import getopt +import os import struct -import array +import sys from email.parser import HeaderParser -import codecs __version__ = "1.2" @@ -114,7 +113,7 @@ def make(filename, outfile): try: with open(infile, 'rb') as f: lines = f.readlines() - except IOError as msg: + except OSError as msg: print(msg, file=sys.stderr) sys.exit(1) @@ -127,6 +126,7 @@ def make(filename, outfile): sys.exit(1) section = msgctxt = None + msgid = msgstr = b'' fuzzy = 0 # Start off assuming Latin-1, so everything decodes without failure, @@ -178,7 +178,7 @@ def make(filename, outfile): # This is a message with plural forms elif l.startswith('msgid_plural'): if section != ID: - print('msgid_plural not preceded by msgid on %s:%d' % (infile, lno), + print(f'msgid_plural not preceded by msgid on {infile}:{lno}', file=sys.stderr) sys.exit(1) l = l[12:] @@ -189,7 +189,7 @@ def make(filename, outfile): section = STR if l.startswith('msgstr['): if not is_plural: - print('plural without msgid_plural on %s:%d' % (infile, lno), + print(f'plural without msgid_plural on {infile}:{lno}', file=sys.stderr) sys.exit(1) l = l.split(']', 1)[1] @@ -197,7 +197,7 @@ def make(filename, outfile): msgstr += b'\0' # Separator of the various plural forms else: if is_plural: - print('indexed msgstr required for plural on %s:%d' % (infile, lno), + print(f'indexed msgstr required for plural on {infile}:{lno}', file=sys.stderr) sys.exit(1) l = l[6:] @@ -213,8 +213,7 @@ def make(filename, outfile): elif section == STR: msgstr += l.encode(encoding) else: - print('Syntax error on %s:%d' % (infile, lno), \ - 'before:', file=sys.stderr) + print(f'Syntax error on {infile}:{lno} before:', file=sys.stderr) print(l, file=sys.stderr) sys.exit(1) # Add last entry @@ -227,7 +226,7 @@ def make(filename, outfile): try: with open(outfile,"wb") as f: f.write(output) - except IOError as msg: + except OSError as msg: print(msg, file=sys.stderr) diff --git a/Tools/i18n/pygettext.py b/Tools/i18n/pygettext.py index f46b05067d7fde..ddf4474d2bce55 100755 --- a/Tools/i18n/pygettext.py +++ b/Tools/i18n/pygettext.py @@ -193,7 +193,7 @@ def make_escapes(pass_nonascii): escape = escape_ascii else: escape = escape_nonascii - escapes = [r"\%03o" % i for i in range(256)] + escapes = [fr"\{i:03o}" for i in range(256)] for i in range(32, 127): escapes[i] = chr(i) escapes[ord('\\')] = r'\\' @@ -796,7 +796,7 @@ class Options: try: with open(options.excludefilename) as fp: options.toexclude = fp.readlines() - except IOError: + except OSError: print(f"Can't read --exclude-file: {options.excludefilename}", file=sys.stderr) sys.exit(1) diff --git a/Tools/inspection/benchmark_external_inspection.py b/Tools/inspection/benchmark_external_inspection.py index 0ac7ac4d385792..b7aa0e5de7ed99 100644 --- a/Tools/inspection/benchmark_external_inspection.py +++ b/Tools/inspection/benchmark_external_inspection.py @@ -4,7 +4,6 @@ import sys import contextlib import tempfile -import os import argparse from _colorize import get_colors, can_colorize @@ -151,6 +150,45 @@ def create_threads(n): time.sleep(0.05) ''' +ASYNC_CODE = '''\ +import asyncio +import contextlib +import math + +def compute_slice(seed): + result = 0.0 + for i in range(2000): + result += math.sin(seed + i) * math.sqrt(i + 1) + return result + +async def leaf_task(seed): + total = 0.0 + while True: + total += compute_slice(seed) + await asyncio.sleep(0) + +async def parent_task(seed): + child = asyncio.create_task(leaf_task(seed + 1000), name=f"leaf-{seed}") + try: + while True: + compute_slice(seed) + await asyncio.sleep(0.001) + finally: + child.cancel() + with contextlib.suppress(asyncio.CancelledError): + await child + +async def main(): + tasks = [ + asyncio.create_task(parent_task(i), name=f"parent-{i}") + for i in range(8) + ] + await asyncio.gather(*tasks) + +if __name__ == "__main__": + asyncio.run(main()) +''' + CODE_EXAMPLES = { "basic": { "code": CODE, @@ -164,10 +202,29 @@ def create_threads(n): "code": CODE_WITH_TONS_OF_THREADS, "description": "Tons of threads doing mixed CPU/IO work", }, + "asyncio": { + "code": ASYNC_CODE, + "description": "Asyncio tasks with active and awaited coroutine chains", + }, +} + +OPERATIONS = { + "stack_trace": { + "method": "get_stack_trace", + "label": "get_stack_trace()", + }, + "async_stack_trace": { + "method": "get_async_stack_trace", + "label": "get_async_stack_trace()", + }, + "all_awaited_by": { + "method": "get_all_awaited_by", + "label": "get_all_awaited_by()", + }, } -def benchmark(unwinder, duration_seconds=10): +def benchmark(unwinder, duration_seconds=10, blocking=False, operation="stack_trace"): """Benchmark mode - measure raw sampling speed for specified duration""" sample_count = 0 fail_count = 0 @@ -175,11 +232,14 @@ def benchmark(unwinder, duration_seconds=10): start_time = time.perf_counter() end_time = start_time + duration_seconds total_attempts = 0 + operation_info = OPERATIONS[operation] + operation_method = getattr(unwinder, operation_info["method"]) colors = get_colors(can_colorize()) print( - f"{colors.BOLD_BLUE}Benchmarking sampling speed for {duration_seconds} seconds...{colors.RESET}" + f"{colors.BOLD_BLUE}Benchmarking {operation_info['label']} speed " + f"for {duration_seconds} seconds...{colors.RESET}" ) try: @@ -187,9 +247,15 @@ def benchmark(unwinder, duration_seconds=10): total_attempts += 1 work_start = time.perf_counter() try: - stack_trace = unwinder.get_stack_trace() - if stack_trace: - sample_count += 1 + if blocking: + unwinder.pause_threads() + try: + sample = operation_method() + if sample: + sample_count += 1 + finally: + if blocking: + unwinder.resume_threads() except (OSError, RuntimeError, UnicodeDecodeError) as e: fail_count += 1 @@ -233,6 +299,7 @@ def benchmark(unwinder, duration_seconds=10): (sample_count / total_attempts) * 100 if total_attempts > 0 else 0 ), "total_work_time": total_work_time, + "operation": operation_info["label"], "avg_work_time_us": ( (total_work_time / total_attempts) * 1e6 if total_attempts > 0 else 0 ), @@ -246,7 +313,7 @@ def print_benchmark_results(results): colors = get_colors(can_colorize()) print(f"\n{colors.BOLD_GREEN}{'='*60}{colors.RESET}") - print(f"{colors.BOLD_GREEN}get_stack_trace() Benchmark Results{colors.RESET}") + print(f"{colors.BOLD_GREEN}{results['operation']} Benchmark Results{colors.RESET}") print(f"{colors.BOLD_GREEN}{'='*60}{colors.RESET}") # Basic statistics @@ -323,6 +390,8 @@ def parse_arguments(): %(prog)s -d 60 # Run basic benchmark for 60 seconds %(prog)s --code deep_static # Run deep static call stack benchmark %(prog)s --code deep_static -d 30 # Run deep static benchmark for 30 seconds + %(prog)s --operation async_stack_trace + %(prog)s --operation all_awaited_by Available code examples: {examples_desc} @@ -342,8 +411,15 @@ def parse_arguments(): "--code", "-c", choices=list(CODE_EXAMPLES.keys()), - default="basic", - help="Code example to benchmark (default: basic)", + default=None, + help="Code example to benchmark (default: basic, or asyncio for async operations)", + ) + + parser.add_argument( + "--operation", + choices=list(OPERATIONS.keys()), + default="stack_trace", + help="Remote unwinder operation to benchmark (default: stack_trace)", ) parser.add_argument( @@ -353,7 +429,16 @@ def parse_arguments(): help="Which threads to include in the benchmark (default: all)", ) - return parser.parse_args() + parser.add_argument( + "--blocking", + action="store_true", + help="Stop all threads before sampling for consistent snapshots", + ) + + args = parser.parse_args() + if args.code is None: + args.code = "asyncio" if args.operation != "stack_trace" else "basic" + return args def create_target_process(temp_file, code_example="basic"): @@ -408,6 +493,12 @@ def main(): print( f"{colors.CYAN}Benchmark Duration:{colors.RESET} {colors.YELLOW}{args.duration}{colors.RESET} seconds" ) + print( + f"{colors.CYAN}Operation:{colors.RESET} {colors.GREEN}{OPERATIONS[args.operation]['label']}{colors.RESET}" + ) + print( + f"{colors.CYAN}Blocking Mode:{colors.RESET} {colors.GREEN if args.blocking else colors.YELLOW}{'enabled' if args.blocking else 'disabled'}{colors.RESET}" + ) process = None temp_file_path = None @@ -434,9 +525,14 @@ def main(): elif args.threads == "only_active": kwargs["only_active_thread"] = True unwinder = _remote_debugging.RemoteUnwinder( - process.pid, **kwargs + process.pid, cache_frames=True, **kwargs + ) + results = benchmark( + unwinder, + duration_seconds=args.duration, + blocking=args.blocking, + operation=args.operation, ) - results = benchmark(unwinder, duration_seconds=args.duration) finally: cleanup_process(process, temp_file_path) diff --git a/Tools/jit/README.md b/Tools/jit/README.md index 8e817574b4d72b..2a687bb9e899e7 100644 --- a/Tools/jit/README.md +++ b/Tools/jit/README.md @@ -9,32 +9,37 @@ Python 3.11 or newer is required to build the JIT. The JIT compiler does not require end users to install any third-party dependencies, but part of it must be *built* using LLVM[^why-llvm]. You are *not* required to build the rest of CPython using LLVM, or even the same version of LLVM (in fact, this is uncommon). -LLVM version 19 is required. Both `clang` and `llvm-readobj` need to be installed and discoverable (version suffixes, like `clang-19`, are okay). It's highly recommended that you also have `llvm-objdump` available, since this allows the build script to dump human-readable assembly for the generated code. +LLVM version 21 is the officially supported version. The tools `clang`, `llvm-readobj`, `llvm-objdump`, and `llvm-dwarfdump` need to be installed and discoverable (version suffixes, like `clang-21`, are okay). + +You can customize the LLVM configuration using environment variables before running configure: + +- LLVM_VERSION: Specify a different LLVM version (default: 21) +- LLVM_TOOLS_INSTALL_DIR: Point to a specific LLVM installation prefix when multiple installations exist (the tools are expected in `<dir>/bin`) It's easy to install all of the required tools: ### Linux -Install LLVM 19 on Ubuntu/Debian: +Install LLVM 21 on Ubuntu/Debian: ```sh wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh -sudo ./llvm.sh 19 +sudo ./llvm.sh 21 ``` -Install LLVM 19 on Fedora Linux 40 or newer: +Install LLVM 21 on Fedora Linux 40 or newer: ```sh -sudo dnf install 'clang(major) = 19' 'llvm(major) = 19' +sudo dnf install 'clang(major) = 21' 'llvm(major) = 21' ``` ### macOS -Install LLVM 19 with [Homebrew](https://brew.sh): +Install LLVM 21 with [Homebrew](https://brew.sh): ```sh -brew install llvm@19 +brew install llvm@21 ``` Homebrew won't add any of the tools to your `$PATH`. That's okay; the build script knows how to find them. @@ -43,14 +48,27 @@ Homebrew won't add any of the tools to your `$PATH`. That's okay; the build scri LLVM is downloaded automatically (along with other external binary dependencies) by `PCbuild\build.bat`. -Otherwise, you can install LLVM 19 [by searching for it on LLVM's GitHub releases page](https://github.com/llvm/llvm-project/releases?q=19), clicking on "Assets", downloading the appropriate Windows installer for your platform (likely the file ending with `-win64.exe`), and running it. **When installing, be sure to select the option labeled "Add LLVM to the system PATH".** +By default, the architecture of the LLVM tools is auto-detected based on the host machine. To override this, set the `PreferredToolArchitecture` environment variable before building: + +```sh +set PreferredToolArchitecture=AMD64 +PCbuild\build.bat --experimental-jit +``` + +Valid values are`x64`, `x86` and `ARM64`. + +Otherwise, you can install LLVM 21 [by searching for it on LLVM's GitHub releases page](https://github.com/llvm/llvm-project/releases?q=21), clicking on "Assets", downloading the appropriate Windows installer for your platform (likely the file ending with `-win64.exe`), and running it. **When installing, be sure to select the option labeled "Add LLVM to the system PATH".** Alternatively, you can use [chocolatey](https://chocolatey.org): ```sh -choco install llvm --version=19.1.0 +choco install llvm --version=21.1.0 ``` +### Dev Containers + +If you are working on CPython in a [Codespaces instance](https://devguide.python.org/getting-started/setup-building/#using-codespaces), there's no +need to install LLVM as the Fedora 43 base image includes LLVM 21 out of the box. ## Building @@ -62,6 +80,14 @@ Otherwise, just configure and build as you normally would. Cross-compiling "just The JIT can also be enabled or disabled using the `PYTHON_JIT` environment variable, even on builds where it is enabled or disabled by default. More details about configuring CPython with the JIT and optional values for `--enable-experimental-jit` can be found [here](https://docs.python.org/dev/using/configure.html#cmdoption-enable-experimental-jit). +## Miscellaneous +If you're looking for information on how to update the JIT build dependencies, see [JIT Build Infrastructure](jit_infra.md). + [^pep-744]: [PEP 744](https://peps.python.org/pep-0744/) -[^why-llvm]: Clang is specifically needed because it's the only C compiler with support for guaranteed tail calls (`musttail`), which are required by CPython's continuation-passing-style approach to JIT compilation. Since LLVM also includes other functionalities we need (namely, object file parsing and disassembly), it's convenient to only support one toolchain at this time. +[^why-llvm]: Clang is specifically needed because it's the only C compiler with support for guaranteed tail calls (`musttail`), which are required by CPython's continuation-passing-style approach to JIT compilation. Since LLVM also includes other functionalities we need (namely, object file parsing, disassembly, and DWARF inspection), it's convenient to only support one toolchain at this time. + +### Understanding JIT behavior + +The [example_trace_dump.py](./example_trace_dump.py) script will (when configured as described in the script) dump out the +executors for a range of tiny programs to show the behavior of the JIT front-end. \ No newline at end of file diff --git a/Tools/jit/_dwarf.py b/Tools/jit/_dwarf.py new file mode 100644 index 00000000000000..5b6b148562e109 --- /dev/null +++ b/Tools/jit/_dwarf.py @@ -0,0 +1,236 @@ +"""Utilities for deriving JIT unwind information from DWARF CFI.""" + +import dataclasses +import pathlib +import re +import typing + +_LLVMRun = typing.Callable[..., typing.Awaitable[str]] + + +@dataclasses.dataclass(frozen=True) +class UnwindInfo: + code_alignment_factor: int + data_alignment_factor: int + return_address_register: int + cfa_register: int + cfa_offset: int + frame_pointer_register: int + frame_pointer_offset: int + return_address_offset: int + + +@dataclasses.dataclass(frozen=True) +class ELFUnwindConfig: + frame_pointer: str + return_address: str + register_numbers: typing.Mapping[str, int] + call_instruction_prefixes: tuple[str, ...] + + def is_call_instruction(self, instruction: str) -> bool: + return instruction.startswith(self.call_instruction_prefixes) + + +@dataclasses.dataclass(frozen=True) +class _UnwindRow: + pc: int + cfa_register: str + cfa_offset: int + saved_registers: dict[str, int] + + +class ELFUnwindInfo: + def __init__( + self, + target_name: str, + *, + config: ELFUnwindConfig, + verbose: bool = False, + llvm_version: str, + llvm_tools_install_dir: str | None = None, + llvm_run: _LLVMRun, + ) -> None: + self.target_name = target_name + self.config = config + self.verbose = verbose + self.llvm_version = llvm_version + self.llvm_tools_install_dir = llvm_tools_install_dir + self.llvm_run = llvm_run + + @staticmethod + def _parse_dwarfdump_int( + dump: str, field: str, *, required: bool = True + ) -> int | None: + match = re.search(rf"^\s*{field}:\s+(-?\d+)$", dump, re.MULTILINE) + if match is None: + if required: + raise ValueError(f"missing {field} in llvm-dwarfdump output") + return None + return int(match.group(1)) + + @staticmethod + def _parse_dwarfdump_rows(dump: str) -> list[_UnwindRow]: + row_pattern = re.compile( + r"^\s*0x(?P<pc>[0-9a-f]+):\s+" + r"CFA=(?P<cfa_register>[A-Z][A-Z0-9]*)" + r"(?P<cfa_offset>[+-]\d+)?" + r"(?::\s*(?P<saved>.*))?$" + ) + saved_pattern = re.compile( + r"(?P<register>[A-Z][A-Z0-9]*)=\[CFA(?P<offset>[+-]\d+)?\]" + ) + rows = [] + for line in dump.splitlines(): + row_match = row_pattern.match(line) + if row_match is None: + continue + saved_registers = {} + saved = row_match["saved"] + if saved: + for saved_match in saved_pattern.finditer(saved): + offset = saved_match["offset"] + saved_registers[saved_match["register"]] = ( + int(offset) if offset is not None else 0 + ) + cfa_offset = row_match["cfa_offset"] + rows.append( + _UnwindRow( + pc=int(row_match["pc"], 16), + cfa_register=row_match["cfa_register"], + cfa_offset=int(cfa_offset) if cfa_offset is not None else 0, + saved_registers=saved_registers, + ) + ) + if not rows: + raise ValueError("missing interpreted CFI rows in llvm-dwarfdump output") + return rows + + @staticmethod + def _parse_objdump_instructions(dump: str) -> list[tuple[int, str]]: + instructions = [] + for line in dump.splitlines(): + match = re.match( + r"^\s*(?P<pc>[0-9a-f]+):\s+" + r"(?:(?:[0-9a-f]{2}|[0-9a-f]{8})\s+)+" + r"(?P<instruction>.+)$", + line, + ) + if match: + instructions.append( + ( + int(match["pc"], 16), + re.sub(r"\s+", " ", match["instruction"].strip()), + ) + ) + if not instructions: + raise ValueError("missing instructions in llvm-objdump output") + return instructions + + def _reg_number(self, register: str) -> int: + try: + return self.config.register_numbers[register] + except KeyError as exc: + raise ValueError( + f"unsupported register {register!r} in llvm-dwarfdump output" + ) from exc + + @staticmethod + def _encoded_cfa_offset(byte_offset: int, data_alignment_factor: int) -> int: + if data_alignment_factor == 0: + raise ValueError("DWARF data alignment factor must not be zero") + if byte_offset % data_alignment_factor: + raise ValueError( + f"offset {byte_offset} is not a multiple of " + f"data alignment factor {data_alignment_factor}" + ) + return byte_offset // data_alignment_factor + + async def _read_objdump(self, output: pathlib.Path) -> str: + return await self.llvm_run( + "llvm-objdump", + ["-d", f"{output}"], + echo=self.verbose, + llvm_version=self.llvm_version, + llvm_tools_install_dir=self.llvm_tools_install_dir, + ) + + async def _read_eh_frame(self, output: pathlib.Path) -> str: + return await self.llvm_run( + "llvm-dwarfdump", + ["--eh-frame", f"{output}"], + echo=self.verbose, + llvm_version=self.llvm_version, + llvm_tools_install_dir=self.llvm_tools_install_dir, + ) + + def _executor_call_pc(self, disassembly: str) -> int: + calls = [ + pc + for pc, instruction in self._parse_objdump_instructions(disassembly) + if self.config.is_call_instruction(instruction) + ] + if len(calls) != 1: + raise ValueError( + f"{self.target_name} JIT shim should contain exactly one executor call" + ) + call_pc = calls[0] + return call_pc + + def _active_row(self, eh_frame: str, call_pc: int) -> _UnwindRow: + rows = self._parse_dwarfdump_rows(eh_frame) + active_rows = [row for row in rows if row.pc <= call_pc] + if not active_rows: + raise ValueError( + f"{self.target_name} JIT shim has no CFI row for executor call " + f"at 0x{call_pc:x}" + ) + return max(active_rows, key=lambda row: row.pc) + + def _check_saved_registers(self, row: _UnwindRow) -> None: + if ( + self.config.frame_pointer not in row.saved_registers + or self.config.return_address not in row.saved_registers + ): + raise ValueError( + f"{self.target_name} JIT shim CFI row at 0x{row.pc:x} " + f"does not save {self.config.frame_pointer} and " + f"{self.config.return_address}" + ) + + def _build_unwind_info(self, eh_frame: str, active_row: _UnwindRow) -> UnwindInfo: + code_alignment_factor = self._parse_dwarfdump_int( + eh_frame, "Code alignment factor" + ) + data_alignment_factor = self._parse_dwarfdump_int( + eh_frame, "Data alignment factor" + ) + return_address_register = self._parse_dwarfdump_int( + eh_frame, "Return address column" + ) + assert code_alignment_factor is not None + assert data_alignment_factor is not None + assert return_address_register is not None + return UnwindInfo( + code_alignment_factor=code_alignment_factor, + data_alignment_factor=data_alignment_factor, + return_address_register=return_address_register, + cfa_register=self._reg_number(active_row.cfa_register), + cfa_offset=active_row.cfa_offset, + frame_pointer_register=self._reg_number(self.config.frame_pointer), + frame_pointer_offset=self._encoded_cfa_offset( + active_row.saved_registers[self.config.frame_pointer], + data_alignment_factor, + ), + return_address_offset=self._encoded_cfa_offset( + active_row.saved_registers[self.config.return_address], + data_alignment_factor, + ), + ) + + async def extract(self, output: pathlib.Path) -> UnwindInfo: + disassembly = await self._read_objdump(output) + call_pc = self._executor_call_pc(disassembly) + eh_frame = await self._read_eh_frame(output) + active_row = self._active_row(eh_frame, call_pc) + self._check_saved_registers(active_row) + return self._build_unwind_info(eh_frame, active_row) diff --git a/Tools/jit/_llvm.py b/Tools/jit/_llvm.py index f09a8404871b24..96cf5fc4714737 100644 --- a/Tools/jit/_llvm.py +++ b/Tools/jit/_llvm.py @@ -10,9 +10,8 @@ import _targets -_LLVM_VERSION = 19 -_LLVM_VERSION_PATTERN = re.compile(rf"version\s+{_LLVM_VERSION}\.\d+\.\d+\S*\s+") -_EXTERNALS_LLVM_TAG = "llvm-19.1.7.0" +_LLVM_VERSION = "21" +_EXTERNALS_LLVM_TAG = "llvm-21.1.4.0" _P = typing.ParamSpec("_P") _R = typing.TypeVar("_R") @@ -43,9 +42,19 @@ async def _run(tool: str, args: typing.Iterable[str], echo: bool = False) -> str async with _CORES: if echo: print(shlex.join(command)) + + if os.name == "nt": + # When building with /p:PlatformToolset=ClangCL, the VS build + # system puts that clang's include path into INCLUDE. The JIT's + # clang may be a different version, and mismatched headers cause + # build errors. See https://github.com/python/cpython/issues/146210. + env = os.environ.copy() + env.pop("INCLUDE", None) + else: + env = None try: process = await asyncio.create_subprocess_exec( - *command, stdout=subprocess.PIPE + *command, stdout=subprocess.PIPE, env=env ) except FileNotFoundError: return None @@ -56,53 +65,92 @@ async def _run(tool: str, args: typing.Iterable[str], echo: bool = False) -> str @_async_cache -async def _check_tool_version(name: str, *, echo: bool = False) -> bool: +async def _check_tool_version( + name: str, llvm_version: str, *, echo: bool = False +) -> bool: output = await _run(name, ["--version"], echo=echo) - return bool(output and _LLVM_VERSION_PATTERN.search(output)) + _llvm_version_pattern = re.compile( + rf"(?<!Apple )(LLVM|clang) version\s+{llvm_version}\.\d+\.\d+\S*\s+" + ) + return bool(output and _llvm_version_pattern.search(output)) @_async_cache -async def _get_brew_llvm_prefix(*, echo: bool = False) -> str | None: - output = await _run("brew", ["--prefix", f"llvm@{_LLVM_VERSION}"], echo=echo) +async def _get_brew_llvm_prefix(llvm_version: str, *, echo: bool = False) -> str | None: + output = await _run("brew", ["--prefix", f"llvm@{llvm_version}"], echo=echo) return output and output.removesuffix("\n") @_async_cache -async def _find_tool(tool: str, *, echo: bool = False) -> str | None: +async def _find_tool( + tool: str, + llvm_version: str, + llvm_tools_install_dir: str | None, + *, + echo: bool = False, +) -> str | None: + # Explicitly defined LLVM installation location + if llvm_tools_install_dir: + path = os.path.join(llvm_tools_install_dir, "bin", tool) + if await _check_tool_version(path, llvm_version, echo=echo): + return path # Unversioned executables: path = tool - if await _check_tool_version(path, echo=echo): + if await _check_tool_version(path, llvm_version, echo=echo): return path # Versioned executables: - path = f"{tool}-{_LLVM_VERSION}" - if await _check_tool_version(path, echo=echo): + path = f"{tool}-{llvm_version}" + if await _check_tool_version(path, llvm_version, echo=echo): return path # PCbuild externals: externals = os.environ.get("EXTERNALS_DIR", _targets.EXTERNALS) path = os.path.join(externals, _EXTERNALS_LLVM_TAG, "bin", tool) - if await _check_tool_version(path, echo=echo): + # On Windows, executables need .exe extension + if os.name == "nt" and not path.endswith(".exe"): + path_with_exe = path + ".exe" + if os.path.exists(path_with_exe): + path = path_with_exe + if await _check_tool_version(path, llvm_version, echo=echo): return path # Homebrew-installed executables: - prefix = await _get_brew_llvm_prefix(echo=echo) + prefix = await _get_brew_llvm_prefix(llvm_version, echo=echo) if prefix is not None: path = os.path.join(prefix, "bin", tool) - if await _check_tool_version(path, echo=echo): + if await _check_tool_version(path, llvm_version, echo=echo): return path # Nothing found: return None async def maybe_run( - tool: str, args: typing.Iterable[str], echo: bool = False + tool: str, + args: typing.Iterable[str], + echo: bool = False, + llvm_version: str = _LLVM_VERSION, + llvm_tools_install_dir: str | None = None, ) -> str | None: """Run an LLVM tool if it can be found. Otherwise, return None.""" - path = await _find_tool(tool, echo=echo) + + path = await _find_tool(tool, llvm_version, llvm_tools_install_dir, echo=echo) return path and await _run(path, args, echo=echo) -async def run(tool: str, args: typing.Iterable[str], echo: bool = False) -> str: +async def run( + tool: str, + args: typing.Iterable[str], + echo: bool = False, + llvm_version: str = _LLVM_VERSION, + llvm_tools_install_dir: str | None = None, +) -> str: """Run an LLVM tool if it can be found. Otherwise, raise RuntimeError.""" - output = await maybe_run(tool, args, echo=echo) + + output = await maybe_run( + tool, + args, + echo=echo, + llvm_version=llvm_version, + llvm_tools_install_dir=llvm_tools_install_dir, + ) if output is None: - raise RuntimeError(f"Can't find {tool}-{_LLVM_VERSION}!") + raise RuntimeError(f"Can't find {tool}-{llvm_version}!") return output diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index 33db110b728dba..f192783a55950c 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -1,6 +1,7 @@ """Low-level optimization of textual assembly.""" import dataclasses +import enum import pathlib import re import typing @@ -9,7 +10,7 @@ _RE_NEVER_MATCH = re.compile(r"(?!)") # Dictionary mapping branch instructions to their inverted branch instructions. # If a branch cannot be inverted, the value is None: -_X86_BRANCHES = { +_X86_BRANCH_NAMES = { # https://www.felixcloutier.com/x86/jcc "ja": "jna", "jae": "jnae", @@ -37,16 +38,112 @@ "loopz": None, } # Update with all of the inverted branches, too: -_X86_BRANCHES |= {v: k for k, v in _X86_BRANCHES.items() if v} +_X86_BRANCH_NAMES |= {v: k for k, v in _X86_BRANCH_NAMES.items() if v} +# No custom relocations needed +_X86_BRANCHES: dict[str, tuple[str | None, str | None]] = { + k: (v, None) for k, v in _X86_BRANCH_NAMES.items() +} + +_AARCH64_COND_CODES = { + # https://developer.arm.com/documentation/dui0801/b/CJAJIHAD?lang=en + "eq": "ne", + "ne": "eq", + "lt": "ge", + "ge": "lt", + "gt": "le", + "le": "gt", + "vs": "vc", + "vc": "vs", + "mi": "pl", + "pl": "mi", + "cs": "cc", + "cc": "cs", + "hs": "lo", + "lo": "hs", + "hi": "ls", + "ls": "hi", +} +# MyPy doesn't understand that a invariant variable can be initialized by a covariant value +CUSTOM_AARCH64_BRANCH19: str | None = "CUSTOM_AARCH64_BRANCH19" + +_AARCH64_SHORT_BRANCHES = { + "tbz": "tbnz", + "tbnz": "tbz", +} + +# Branches are either b.{cond}, bc.{cond}, cbz, cbnz, tbz or tbnz +_AARCH64_BRANCHES: dict[str, tuple[str | None, str | None]] = ( + { + "b." + cond: (("b." + inverse if inverse else None), CUSTOM_AARCH64_BRANCH19) + for (cond, inverse) in _AARCH64_COND_CODES.items() + } + | { + "bc." + cond: (("bc." + inverse if inverse else None), CUSTOM_AARCH64_BRANCH19) + for (cond, inverse) in _AARCH64_COND_CODES.items() + } + | { + "cbz": ("cbnz", CUSTOM_AARCH64_BRANCH19), + "cbnz": ("cbz", CUSTOM_AARCH64_BRANCH19), + } + | {cond: (inverse, None) for (cond, inverse) in _AARCH64_SHORT_BRANCHES.items()} +) + + +@enum.unique +class InstructionKind(enum.Enum): + + JUMP = enum.auto() + LONG_BRANCH = enum.auto() + SHORT_BRANCH = enum.auto() + CALL = enum.auto() + RETURN = enum.auto() + SMALL_CONST_1 = enum.auto() + SMALL_CONST_2 = enum.auto() + SMALL_CONST_MASK = enum.auto() + LARGE_CONST_1 = enum.auto() + LARGE_CONST_2 = enum.auto() + OTHER = enum.auto() @dataclasses.dataclass +class Instruction: + kind: InstructionKind + name: str + text: str + register: str | None + target: str | None + + def is_branch(self) -> bool: + return self.kind in (InstructionKind.LONG_BRANCH, InstructionKind.SHORT_BRANCH) + + def update_target(self, target: str) -> "Instruction": + assert self.target is not None + return Instruction( + self.kind, + self.name, + self.text.replace(self.target, target), + self.register, + target, + ) + + def update_name_and_target(self, name: str, target: str) -> "Instruction": + assert self.target is not None + return Instruction( + self.kind, + name, + self.text.replace(self.name, name).replace(self.target, target), + self.register, + target, + ) + + +@dataclasses.dataclass(eq=False) class _Block: label: str | None = None # Non-instruction lines like labels, directives, and comments: noninstructions: list[str] = dataclasses.field(default_factory=list) # Instruction lines: - instructions: list[str] = dataclasses.field(default_factory=list) + instructions: list[Instruction] = dataclasses.field(default_factory=list) # If this block ends in a jump, where to? target: typing.Self | None = None # The next block in the linked list: @@ -73,6 +170,8 @@ class Optimizer: # Prefixes used to mangle local labels and symbols: label_prefix: str symbol_prefix: str + re_global: re.Pattern[str] + frame_pointers: bool # The first block in the linked list: _root: _Block = dataclasses.field(init=False, default_factory=_Block) _labels: dict[str, _Block] = dataclasses.field(init=False, default_factory=dict) @@ -85,28 +184,49 @@ class Optimizer: r'\s*(?P<label>[\w."$?@]+):' ) # Override everything that follows in subclasses: - _branches: typing.ClassVar[dict[str, str | None]] = {} + _supports_external_relocations = True + supports_small_constants = False + _branches: typing.ClassVar[dict[str, tuple[str | None, str | None]]] = {} + # Short branches are instructions that can branch within a micro-op, + # but might not have the reach to branch anywhere within a trace. + _short_branches: typing.ClassVar[dict[str, str]] = {} # Two groups (instruction and target): _re_branch: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH # One group (target): + _re_call: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH + # One group (target): _re_jump: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH # No groups: _re_return: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH + text: str = "" + globals: set[str] = dataclasses.field(default_factory=set) + _re_small_const_1 = _RE_NEVER_MATCH + _re_small_const_2 = _RE_NEVER_MATCH + _re_small_const_mask = _RE_NEVER_MATCH + _re_large_const_1 = _RE_NEVER_MATCH + _re_large_const_2 = _RE_NEVER_MATCH + const_reloc = "<Not supported>" + _frame_pointer_modify: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH + label_index: int = 0 def __post_init__(self) -> None: # Split the code into a linked list of basic blocks. A basic block is an # optional label, followed by zero or more non-instruction lines, # followed by zero or more instruction lines (only the last of which may # be a branch, jump, or return): - text = self._preprocess(self.path.read_text()) + self.text = self._preprocess(self.path.read_text()) block = self._root - for line in text.splitlines(): + for line in self.text.splitlines(): # See if we need to start a new block: if match := self._re_label.match(line): # Label. New block: block.link = block = self._lookup_label(match["label"]) block.noninstructions.append(line) continue + if match := self.re_global.match(line): + self.globals.add(match["label"]) + block.noninstructions.append(line) + continue if self._re_noninstructions.match(line): if block.instructions: # Non-instruction lines. New block: @@ -116,16 +236,24 @@ def __post_init__(self) -> None: if block.target or not block.fallthrough: # Current block ends with a branch, jump, or return. New block: block.link = block = _Block() - block.instructions.append(line) - if match := self._re_branch.match(line): + inst = self._parse_instruction(line) + block.instructions.append(inst) + if inst.is_branch(): # A block ending in a branch has a target and fallthrough: - block.target = self._lookup_label(match["target"]) + assert inst.target is not None + block.target = self._lookup_label(inst.target) + assert block.fallthrough + elif inst.kind == InstructionKind.CALL: + # A block ending in a call has a target and return point after call: + assert inst.target is not None + block.target = self._lookup_label(inst.target) assert block.fallthrough - elif match := self._re_jump.match(line): + elif inst.kind == InstructionKind.JUMP: # A block ending in a jump has a target and no fallthrough: - block.target = self._lookup_label(match["target"]) + assert inst.target is not None + block.target = self._lookup_label(inst.target) block.fallthrough = False - elif self._re_return.match(line): + elif inst.kind == InstructionKind.RETURN: # A block ending in a return has no target and fallthrough: assert not block.target block.fallthrough = False @@ -138,36 +266,79 @@ def _preprocess(self, text: str) -> str: continue_label = f"{self.label_prefix}_JIT_CONTINUE" return re.sub(continue_symbol, continue_label, text) - @classmethod - def _invert_branch(cls, line: str, target: str) -> str | None: - match = cls._re_branch.match(line) - assert match - inverted = cls._branches.get(match["instruction"]) + def _parse_instruction(self, line: str) -> Instruction: + target = None + reg = None + if match := self._re_branch.match(line): + target = match["target"] + name = match["instruction"] + if name in self._short_branches: + kind = InstructionKind.SHORT_BRANCH + else: + kind = InstructionKind.LONG_BRANCH + elif match := self._re_jump.match(line): + target = match["target"] + name = line[: -len(target)].strip() + kind = InstructionKind.JUMP + elif match := self._re_call.match(line): + target = match["target"] + name = line[: -len(target)].strip() + kind = InstructionKind.CALL + elif match := self._re_return.match(line): + name = line + kind = InstructionKind.RETURN + elif match := self._re_small_const_1.match(line): + target = match["value"] + name = match["instruction"] + reg = match["register"] + kind = InstructionKind.SMALL_CONST_1 + elif match := self._re_small_const_2.match(line): + target = match["value"] + name = match["instruction"] + reg = match["register"] + kind = InstructionKind.SMALL_CONST_2 + elif match := self._re_small_const_mask.match(line): + target = match["value"] + name = match["instruction"] + reg = match["register"] + if reg.startswith("w"): + reg = "x" + reg[1:] + kind = InstructionKind.SMALL_CONST_MASK + elif match := self._re_large_const_1.match(line): + target = match["value"] + name = match["instruction"] + reg = match["register"] + kind = InstructionKind.LARGE_CONST_1 + elif match := self._re_large_const_2.match(line): + target = match["value"] + name = match["instruction"] + reg = match["register"] + kind = InstructionKind.LARGE_CONST_2 + else: + name, *_ = line.split(" ") + kind = InstructionKind.OTHER + return Instruction(kind, name, line, reg, target) + + def _invert_branch(self, inst: Instruction, target: str) -> Instruction | None: + assert inst.is_branch() + if inst.kind == InstructionKind.SHORT_BRANCH and self._is_far_target(target): + return None + inverted_reloc = self._branches.get(inst.name) + if inverted_reloc is None: + return None + inverted = inverted_reloc[0] if not inverted: return None - (a, b), (c, d) = match.span("instruction"), match.span("target") - # Before: - # je FOO - # After: - # jne BAR - return "".join([line[:a], inverted, line[b:c], target, line[d:]]) - - @classmethod - def _update_jump(cls, line: str, target: str) -> str: - match = cls._re_jump.match(line) - assert match - a, b = match.span("target") - # Before: - # jmp FOO - # After: - # jmp BAR - return "".join([line[:a], target, line[b:]]) + return inst.update_name_and_target(inverted, target) def _lookup_label(self, label: str) -> _Block: if label not in self._labels: self._labels[label] = _Block(label) return self._labels[label] + def _is_far_target(self, label: str) -> bool: + return not label.startswith(self.label_prefix) + def _blocks(self) -> typing.Generator[_Block, None, None]: block: _Block | None = self._root while block: @@ -175,7 +346,7 @@ def _blocks(self) -> typing.Generator[_Block, None, None]: block = block.link def _body(self) -> str: - lines = [] + lines = ["#" + line for line in self.text.splitlines()] hot = True for block in self._blocks(): if hot != block.hot: @@ -183,7 +354,8 @@ def _body(self) -> str: # Make it easy to tell at a glance where cold code is: lines.append(f"# JIT: {'HOT' if hot else 'COLD'} ".ljust(80, "#")) lines.extend(block.noninstructions) - lines.extend(block.instructions) + for inst in block.instructions: + lines.append(inst.text) return "\n".join(lines) def _predecessors(self, block: _Block) -> typing.Generator[_Block, None, None]: @@ -250,8 +422,8 @@ def _invert_hot_branches(self) -> None: if inverted is None: continue branch.instructions[-1] = inverted - jump.instructions[-1] = self._update_jump( - jump.instructions[-1], branch.target.label + jump.instructions[-1] = jump.instructions[-1].update_target( + branch.target.label ) branch.target, jump.target = jump.target, branch.target jump.hot = True @@ -260,44 +432,357 @@ def _remove_redundant_jumps(self) -> None: # Zero-length jumps can be introduced by _insert_continue_label and # _invert_hot_branches: for block in self._blocks(): + target = block.target + if target is None: + continue + target = target.resolve() # Before: # jmp FOO # FOO: # After: # FOO: - if ( - block.target - and block.link - and block.target.resolve() is block.link.resolve() - ): + if block.link and target is block.link.resolve(): block.target = None block.fallthrough = True block.instructions.pop() + # Before: + # branch FOO: + # ... + # FOO: + # jump BAR + # After: + # br cond BAR + # ... + elif ( + len(target.instructions) == 1 + and target.instructions[0].kind == InstructionKind.JUMP + ): + assert target.target is not None + assert target.target.label is not None + if block.instructions[ + -1 + ].kind == InstructionKind.SHORT_BRANCH and self._is_far_target( + target.target.label + ): + continue + block.target = target.target + block.instructions[-1] = block.instructions[-1].update_target( + target.target.label + ) + + def _find_live_blocks(self) -> set[_Block]: + live: set[_Block] = set() + # Externally reachable blocks are live + todo: set[_Block] = {b for b in self._blocks() if b.label in self.globals} + while todo: + block = todo.pop() + live.add(block) + if block.fallthrough: + next = block.link + if next is not None and next not in live: + todo.add(next) + next = block.target + if next is not None and next not in live: + todo.add(next) + return live + + def _remove_unreachable(self) -> None: + live = self._find_live_blocks() + continuation = self._lookup_label(f"{self.label_prefix}_JIT_CONTINUE") + # Keep blocks after continuation as they may contain data and + # metadata that the assembler needs + prev: _Block | None = None + block = self._root + while block is not continuation: + next = block.link + assert next is not None + if not block in live and prev: + prev.link = next + else: + prev = block + block = next + assert prev.link is block + + def _fixup_external_labels(self) -> None: + if self._supports_external_relocations: + # Nothing to fix up + return + for index, block in enumerate(self._blocks()): + if block.target and block.fallthrough: + branch = block.instructions[-1] + if branch.kind == InstructionKind.CALL: + continue + assert branch.is_branch() + target = branch.target + assert target is not None + reloc = self._branches[branch.name][1] + if reloc is not None and self._is_far_target(target): + name = target[len(self.symbol_prefix) :] + label = f"{self.symbol_prefix}{reloc}_JIT_RELOCATION_{name}_JIT_RELOCATION_{index}:" + block.instructions[-1] = Instruction( + InstructionKind.OTHER, "", label, None, None + ) + block.instructions.append(branch.update_target("0")) + + def _fixup_constants(self) -> None: + "Fixup loading of constants. Overridden by OptimizerAArch64" + pass + + def _validate(self) -> None: + for block in self._blocks(): + if not block.instructions: + continue + for inst in block.instructions: + if self.frame_pointers: + assert ( + self._frame_pointer_modify.match(inst.text) is None + ), "Frame pointer should not be modified" def run(self) -> None: """Run this optimizer.""" self._insert_continue_label() self._mark_hot_blocks() - self._invert_hot_branches() - self._remove_redundant_jumps() + # Removing branches can expose opportunities for more branch removal. + # Repeat a few times. 2 would probably do, but it's fast enough with 4. + for _ in range(4): + self._invert_hot_branches() + self._remove_redundant_jumps() + self._remove_unreachable() + self._fixup_external_labels() + self._fixup_constants() + self._validate() self.path.write_text(self._body()) class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods - """aarch64-apple-darwin/aarch64-pc-windows-msvc/aarch64-unknown-linux-gnu""" + """aarch64-pc-windows-msvc/aarch64-apple-darwin/aarch64-unknown-linux-gnu""" + + _branches = _AARCH64_BRANCHES + _short_branches = _AARCH64_SHORT_BRANCHES + # Mach-O does not support the 19 bit branch locations needed for branch reordering + _supports_external_relocations = False + _branch_patterns = [name.replace(".", r"\.") for name in _AARCH64_BRANCHES] + _re_branch = re.compile( + rf"\s*(?P<instruction>{'|'.join(_branch_patterns)})\s+(.+,\s+)*(?P<target>[\w.]+)" + ) + # https://developer.arm.com/documentation/ddi0406/b/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/BL--BLX--immediate- + _re_call = re.compile(r"\s*blx?\s+(?P<target>[\w.]+)") # https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/B--Branch- _re_jump = re.compile(r"\s*b\s+(?P<target>[\w.]+)") + # https://developer.arm.com/documentation/ddi0602/2025-09/Base-Instructions/RET--Return-from-subroutine- + _re_return = re.compile(r"\s*ret\b") + + supports_small_constants = True + _re_small_const_1 = re.compile( + r"\s*(?P<instruction>adrp)\s+(?P<register>x\d\d?),.*(?P<value>_JIT_OP(ARG|ERAND(0|1))_(16|32)).*" + ) + _re_small_const_2 = re.compile( + r"\s*(?P<instruction>ldr)\s+(?P<register>x\d\d?),.*(?P<value>_JIT_OP(ARG|ERAND(0|1))_(16|32)).*" + ) + _re_small_const_mask = re.compile( + r"\s*(?P<instruction>and)\s+[xw]\d\d?, *(?P<register>[xw]\d\d?).*(?P<value>0xffff)" + ) + _re_large_const_1 = re.compile( + r"\s*(?P<instruction>adrp)\s+(?P<register>x\d\d?),.*:got:(?P<value>[_A-Za-z0-9]+).*" + ) + _re_large_const_2 = re.compile( + r"\s*(?P<instruction>ldr)\s+(?P<register>x\d\d?),.*:got_lo12:(?P<value>[_A-Za-z0-9]+).*" + ) + const_reloc = "CUSTOM_AARCH64_CONST" + _frame_pointer_modify = re.compile(r"\s*stp\s+x29.*") + + def _make_temp_label(self, note: object = None) -> Instruction: + marker = f"jit_temp_{self.label_index}:" + if note is not None: + marker = f"{marker[:-1]}_{note}:" + self.label_index += 1 + return Instruction(InstructionKind.OTHER, "", marker, None, None) + + def _both_registers_same(self, inst: Instruction) -> bool: + reg = inst.register + assert reg is not None + if reg not in inst.text: + reg = "w" + reg[1:] + return inst.text.count(reg) == 2 + + def _fixup_small_constant_pair( + self, output: list[Instruction], label_index: int, inst: Instruction + ) -> str | None: + first = output[label_index + 1] + reg = first.register + if reg is None or inst.register != reg: + output.append( + Instruction(InstructionKind.OTHER, "", "# registers differ", None, None) + ) + output.append(inst) + return None + assert first.target is not None + if first.target != inst.target: + output.append( + Instruction(InstructionKind.OTHER, "", "# targets differ", None, None) + ) + output.append(inst) + return None + if not self._both_registers_same(inst): + output.append( + Instruction( + InstructionKind.OTHER, "", "# not same register", None, None + ) + ) + output.append(inst) + return None + pre, _ = first.text.split(first.name) + output[label_index + 1] = Instruction( + InstructionKind.OTHER, + "movz", + f"{pre}movz {reg}, 0", + reg, + None, + ) + label_text = f"{self.const_reloc}16a_JIT_RELOCATION_CONST{first.target[:-3]}_JIT_RELOCATION_{self.label_index}:" + self.label_index += 1 + output[label_index] = Instruction( + InstructionKind.OTHER, "", label_text, None, None + ) + assert first.target.endswith("16") or first.target.endswith("32") + if first.target.endswith("32"): + label_text = f"{self.const_reloc}16b_JIT_RELOCATION_CONST{first.target[:-3]}_JIT_RELOCATION_{self.label_index}:" + self.label_index += 1 + output.append( + Instruction(InstructionKind.OTHER, "", label_text, None, None) + ) + pre, _ = inst.text.split(inst.name) + output.append( + Instruction( + InstructionKind.OTHER, + "movk", + f"{pre}movk {reg}, 0, lsl #16", + reg, + None, + ) + ) + return reg + + def may_use_reg(self, inst: Instruction, reg: str | None) -> bool: + "Return False if `reg` is not explicitly used by this instruction" + if reg is None: + return False + assert reg.startswith("w") or reg.startswith("x") + xreg = f"x{reg[1:]}" + wreg = f"w{reg[1:]}" + if wreg in inst.text: + return True + if xreg in inst.text: + # Exclude false positives like 0x80 for x8 + count = inst.text.count(xreg) + number_count = inst.text.count("0" + xreg) + return count > number_count + return False + + def _fixup_large_constant_pair( + self, output: list[Instruction], label_index: int, inst: Instruction + ) -> None: + first = output[label_index + 1] + reg = first.register + if reg is None or inst.register != reg: + output.append(inst) + return + assert first.target is not None + if first.target != inst.target: + output.append(inst) + return + label = f"{self.const_reloc}33a_JIT_PAIR_{first.target}_JIT_PAIR_{self.label_index}:" + output[label_index] = Instruction(InstructionKind.OTHER, "", label, None, None) + label = ( + f"{self.const_reloc}33b_JIT_PAIR_{inst.target}_JIT_PAIR_{self.label_index}:" + ) + self.label_index += 1 + output.append(Instruction(InstructionKind.OTHER, "", label, None, None)) + output.append(inst) + + def _fixup_mask(self, output: list[Instruction], inst: Instruction) -> None: + if self._both_registers_same(inst): + # Nop + pass + else: + output.append(inst) + + def _fixup_constants(self) -> None: + for block in self._blocks(): + fixed: list[Instruction] = [] + small_const_part: dict[str, int | None] = {} + small_const_whole: dict[str, str | None] = {} + large_const_part: dict[str, int | None] = {} + for inst in block.instructions: + if inst.kind == InstructionKind.SMALL_CONST_1: + assert inst.register is not None + small_const_part[inst.register] = len(fixed) + small_const_whole[inst.register] = None + large_const_part[inst.register] = None + fixed.append(self._make_temp_label(inst.register)) + fixed.append(inst) + elif inst.kind == InstructionKind.SMALL_CONST_2: + assert inst.register is not None + index = small_const_part.get(inst.register) + small_const_part[inst.register] = None + if index is None: + fixed.append(inst) + continue + small_const_whole[inst.register] = self._fixup_small_constant_pair( + fixed, index, inst + ) + small_const_part[inst.register] = None + elif inst.kind == InstructionKind.SMALL_CONST_MASK: + assert inst.register is not None + reg = small_const_whole.get(inst.register) + if reg is not None: + self._fixup_mask(fixed, inst) + else: + fixed.append(inst) + elif inst.kind == InstructionKind.LARGE_CONST_1: + assert inst.register is not None + small_const_part[inst.register] = None + small_const_whole[inst.register] = None + large_const_part[inst.register] = len(fixed) + fixed.append(self._make_temp_label()) + fixed.append(inst) + elif inst.kind == InstructionKind.LARGE_CONST_2: + assert inst.register is not None + small_const_part[inst.register] = None + small_const_whole[inst.register] = None + index = large_const_part.get(inst.register) + large_const_part[inst.register] = None + if index is None: + fixed.append(inst) + continue + self._fixup_large_constant_pair(fixed, index, inst) + else: + for reg in small_const_part: + if self.may_use_reg(inst, reg): + small_const_part[reg] = None + for reg in small_const_whole: + if self.may_use_reg(inst, reg): + small_const_whole[reg] = None + for reg in small_const_part: + if self.may_use_reg(inst, reg): + large_const_part[reg] = None + fixed.append(inst) + block.instructions = fixed class OptimizerX86(Optimizer): # pylint: disable = too-few-public-methods """i686-pc-windows-msvc/x86_64-apple-darwin/x86_64-unknown-linux-gnu""" _branches = _X86_BRANCHES + _short_branches = {} _re_branch = re.compile( rf"\s*(?P<instruction>{'|'.join(_X86_BRANCHES)})\s+(?P<target>[\w.]+)" ) + # https://www.felixcloutier.com/x86/call + _re_call = re.compile(r"\s*callq?\s+(?P<target>[\w.]+)") # https://www.felixcloutier.com/x86/jmp _re_jump = re.compile(r"\s*jmp\s+(?P<target>[\w.]+)") # https://www.felixcloutier.com/x86/ret - _re_return = re.compile(r"\s*ret\b") + _re_return = re.compile(r"\s*retq?\b") + _frame_pointer_modify = re.compile(r"\s*movq?\s+%(\w+),\s+%rbp.*") diff --git a/Tools/jit/_schema.py b/Tools/jit/_schema.py index 228fc389584dd7..964e8bdbdc1010 100644 --- a/Tools/jit/_schema.py +++ b/Tools/jit/_schema.py @@ -9,7 +9,11 @@ "ARM64_RELOC_PAGE21", "ARM64_RELOC_PAGEOFF12", "ARM64_RELOC_UNSIGNED", + "CUSTOM_AARCH64_BRANCH19", + "CUSTOM_AARCH64_CONST_16", + "CUSTOM_AARCH64_CONST_32", "IMAGE_REL_AMD64_REL32", + "IMAGE_REL_ARM64_BRANCH19", "IMAGE_REL_ARM64_BRANCH26", "IMAGE_REL_ARM64_PAGEBASE_REL21", "IMAGE_REL_ARM64_PAGEOFFSET_12A", @@ -20,6 +24,7 @@ "R_AARCH64_ADR_GOT_PAGE", "R_AARCH64_ADR_PREL_PG_HI21", "R_AARCH64_CALL26", + "R_AARCH64_CONDBR19", "R_AARCH64_JUMP26", "R_AARCH64_ADD_ABS_LO12_NC", "R_AARCH64_LD64_GOT_LO12_NC", @@ -87,6 +92,7 @@ class COFFSection(typing.TypedDict): Characteristics: dict[ typing.Literal["Flags"], list[dict[typing.Literal["Name"], str]] ] + Name: dict[typing.Literal["Value"], str] Number: int RawDataSize: int Relocations: list[dict[typing.Literal["Relocation"], COFFRelocation]] diff --git a/Tools/jit/_stencils.py b/Tools/jit/_stencils.py index 1d82f5366f6ce0..d4547dc8e8e3c1 100644 --- a/Tools/jit/_stencils.py +++ b/Tools/jit/_stencils.py @@ -2,7 +2,6 @@ import dataclasses import enum -import sys import typing import _schema @@ -19,8 +18,6 @@ class HoleValue(enum.Enum): CODE = enum.auto() # The base address of the read-only data for this uop: DATA = enum.auto() - # The address of the current executor (exposed as _JIT_EXECUTOR): - EXECUTOR = enum.auto() # The base address of the "global" offset table located in the read-only data. # Shouldn't be present in the final stencils, since these are all replaced with # equivalent DATA values: @@ -32,6 +29,12 @@ class HoleValue(enum.Enum): # The current uop's operand0 on 32-bit platforms (exposed as _JIT_OPERAND0_HI/LO): OPERAND0_HI = enum.auto() OPERAND0_LO = enum.auto() + # 16 and 32 bit versions of OPARG, OPERAND0 and OPERAND1 + OPARG_16 = enum.auto() + OPERAND0_16 = enum.auto() + OPERAND1_16 = enum.auto() + OPERAND0_32 = enum.auto() + OPERAND1_32 = enum.auto() # The current uop's operand1 on 64-bit platforms (exposed as _JIT_OPERAND1): OPERAND1 = enum.auto() # The current uop's operand1 on 32-bit platforms (exposed as _JIT_OPERAND1_HI/LO): @@ -58,9 +61,14 @@ class HoleValue(enum.Enum): "ARM64_RELOC_PAGE21": "patch_aarch64_21r", "ARM64_RELOC_PAGEOFF12": "patch_aarch64_12", "ARM64_RELOC_UNSIGNED": "patch_64", + # custom aarch64, both darwin and linux: + "CUSTOM_AARCH64_BRANCH19": "patch_aarch64_19r", + "CUSTOM_AARCH64_CONST16a": "patch_aarch64_16a", + "CUSTOM_AARCH64_CONST16b": "patch_aarch64_16b", # x86_64-pc-windows-msvc: "IMAGE_REL_AMD64_REL32": "patch_x86_64_32rx", # aarch64-pc-windows-msvc: + "IMAGE_REL_ARM64_BRANCH19": "patch_aarch64_19r", "IMAGE_REL_ARM64_BRANCH26": "patch_aarch64_26r", "IMAGE_REL_ARM64_PAGEBASE_REL21": "patch_aarch64_21rx", "IMAGE_REL_ARM64_PAGEOFFSET_12A": "patch_aarch64_12", @@ -74,6 +82,7 @@ class HoleValue(enum.Enum): "R_AARCH64_ADR_GOT_PAGE": "patch_aarch64_21rx", "R_AARCH64_ADR_PREL_PG_HI21": "patch_aarch64_21r", "R_AARCH64_CALL26": "patch_aarch64_26r", + "R_AARCH64_CONDBR19": "patch_aarch64_19r", "R_AARCH64_JUMP26": "patch_aarch64_26r", "R_AARCH64_LD64_GOT_LO12_NC": "patch_aarch64_12x", "R_AARCH64_MOVW_UABS_G0_NC": "patch_aarch64_16a", @@ -92,18 +101,23 @@ class HoleValue(enum.Enum): "X86_64_RELOC_SIGNED": "patch_32r", "X86_64_RELOC_UNSIGNED": "patch_64", } + # Translate HoleValues to C expressions: _HOLE_EXPRS = { HoleValue.CODE: "(uintptr_t)code", HoleValue.DATA: "(uintptr_t)data", - HoleValue.EXECUTOR: "(uintptr_t)executor", + HoleValue.GOT: "", # These should all have been turned into DATA values by process_relocations: - # HoleValue.GOT: "", HoleValue.OPARG: "instruction->oparg", + HoleValue.OPARG_16: "instruction->oparg", HoleValue.OPERAND0: "instruction->operand0", + HoleValue.OPERAND0_16: "instruction->operand0", + HoleValue.OPERAND0_32: "instruction->operand0", HoleValue.OPERAND0_HI: "(instruction->operand0 >> 32)", HoleValue.OPERAND0_LO: "(instruction->operand0 & UINT32_MAX)", HoleValue.OPERAND1: "instruction->operand1", + HoleValue.OPERAND1_16: "instruction->operand1", + HoleValue.OPERAND1_32: "instruction->operand1", HoleValue.OPERAND1_HI: "(instruction->operand1 >> 32)", HoleValue.OPERAND1_LO: "(instruction->operand1 & UINT32_MAX)", HoleValue.TARGET: "instruction->target", @@ -112,6 +126,24 @@ class HoleValue(enum.Enum): HoleValue.ZERO: "", } +_AARCH64_GOT_RELOCATIONS = { + "R_AARCH64_ADR_GOT_PAGE", + "R_AARCH64_LD64_GOT_LO12_NC", + "ARM64_RELOC_GOT_LOAD_PAGE21", + "ARM64_RELOC_GOT_LOAD_PAGEOFF12", + "IMAGE_REL_ARM64_PAGEBASE_REL21", + "IMAGE_REL_ARM64_PAGEOFFSET_12L", + "IMAGE_REL_ARM64_PAGEOFFSET_12A", +} + +_X86_GOT_RELOCATIONS = { + "R_X86_64_GOTPCRELX", + "R_X86_64_REX_GOTPCRELX", + "X86_64_RELOC_GOT", + "X86_64_RELOC_GOT_LOAD", + "IMAGE_REL_AMD64_REL32", +} + @dataclasses.dataclass class Hole: @@ -130,59 +162,57 @@ class Hole: # ...plus this addend: addend: int need_state: bool = False + custom_location: str = "" + custom_value: str = "" func: str = dataclasses.field(init=False) + offset2: int = -1 + void: bool = False # Convenience method: replace = dataclasses.replace def __post_init__(self) -> None: self.func = _PATCH_FUNCS[self.kind] - def fold( - self, - other: typing.Self, - body: bytes | bytearray, - ) -> typing.Self | None: - """Combine two holes into a single hole, if possible.""" - instruction_a = int.from_bytes( - body[self.offset : self.offset + 4], byteorder=sys.byteorder - ) - instruction_b = int.from_bytes( - body[other.offset : other.offset + 4], byteorder=sys.byteorder - ) - reg_a = instruction_a & 0b11111 - reg_b1 = instruction_b & 0b11111 - reg_b2 = (instruction_b >> 5) & 0b11111 - - if ( - self.offset + 4 == other.offset - and self.value == other.value - and self.symbol == other.symbol - and self.addend == other.addend - and self.func == "patch_aarch64_21rx" - and other.func == "patch_aarch64_12x" - and reg_a == reg_b1 == reg_b2 - ): - # These can *only* be properly relaxed when they appear together and - # patch the same value: - folded = self.replace() - folded.func = "patch_aarch64_33rx" - return folded - return None + def fold(self, other: typing.Self) -> None: + """Combine two holes into a single hole.""" + assert ( + self.func == "patch_aarch64_12x" and other.func == "patch_aarch64_21rx" + ), (self.func, other.func) + assert self.value == other.value + assert self.symbol == other.symbol + assert self.addend == other.addend + self.func = "patch_aarch64_33rx" + self.offset2 = other.offset + other.void = True def as_c(self, where: str) -> str: """Dump this hole as a call to a patch_* function.""" - location = f"{where} + {self.offset:#x}" - value = _HOLE_EXPRS[self.value] - if self.symbol: - if value: - value += " + " - value += f"(uintptr_t)&{self.symbol}" - if _signed(self.addend) or not value: - if value: - value += " + " - value += f"{_signed(self.addend):#x}" + if self.void: + return "" + if self.custom_location: + location = self.custom_location + else: + location = f"{where} + {self.offset:#x}" + if self.custom_value: + value = self.custom_value + else: + value = _HOLE_EXPRS[self.value] + if self.symbol: + if value: + value += " + " + if self.symbol.startswith("CONST"): + value += f"instruction->{self.symbol[10:].lower()}" + else: + value += f"(uintptr_t)&{self.symbol}" + if _signed(self.addend) or not value: + if value: + value += " + " + value += f"{_signed(self.addend):#x}" if self.need_state: return f"{self.func}({location}, {value}, state);" + if self.offset2 >= 0: + first_location = f"{where} + {self.offset2:#x}" + return f"{self.func}({first_location}, {location}, {value});" return f"{self.func}({location}, {value});" @@ -220,8 +250,36 @@ class StencilGroup: symbols: dict[int | str, tuple[HoleValue, int]] = dataclasses.field( default_factory=dict, init=False ) - _got: dict[str, int] = dataclasses.field(default_factory=dict, init=False) + _jit_symbol_table: dict[str, int] = dataclasses.field( + default_factory=dict, init=False + ) _trampolines: set[int] = dataclasses.field(default_factory=set, init=False) + _got_entries: set[int] = dataclasses.field(default_factory=set, init=False) + + def convert_labels_to_relocations(self) -> None: + holes_by_offset: dict[int, Hole] = {} + first_in_pair: dict[str, Hole] = {} + for hole in self.code.holes: + holes_by_offset[hole.offset] = hole + for name, hole_plus in self.symbols.items(): + if isinstance(name, str) and "_JIT_RELOCATION_" in name: + _, offset = hole_plus + reloc, target, _ = name.split("_JIT_RELOCATION_") + value, symbol = symbol_to_value(target) + hole = Hole( + int(offset), typing.cast(_schema.HoleKind, reloc), value, symbol, 0 + ) + self.code.holes.append(hole) + elif isinstance(name, str) and "_JIT_PAIR_" in name: + _, offset = hole_plus + reloc, target, index = name.split("_JIT_PAIR_") + if offset in holes_by_offset: + hole = holes_by_offset[offset] + if "33a" in reloc: + first_in_pair[index] = hole + elif "33b" in reloc and index in first_in_pair: + first = first_in_pair[index] + hole.fold(first) def process_relocations(self, known_symbols: dict[str, int]) -> None: """Fix up all GOT and internal relocations for this stencil group.""" @@ -243,13 +301,56 @@ def process_relocations(self, known_symbols: dict[str, int]) -> None: self._trampolines.add(ordinal) hole.addend = ordinal hole.symbol = None + # x86_64 Darwin trampolines for external symbols + elif ( + hole.kind == "X86_64_RELOC_BRANCH" + and hole.value is HoleValue.ZERO + and hole.symbol not in self.symbols + ): + hole.func = "patch_x86_64_trampoline" + hole.need_state = True + assert hole.symbol is not None + if hole.symbol in known_symbols: + ordinal = known_symbols[hole.symbol] + else: + ordinal = len(known_symbols) + known_symbols[hole.symbol] = ordinal + self._trampolines.add(ordinal) + hole.addend = ordinal + hole.symbol = None + elif ( + hole.kind in _AARCH64_GOT_RELOCATIONS | _X86_GOT_RELOCATIONS + and hole.symbol + and "_JIT_" not in hole.symbol + and hole.value is HoleValue.GOT + ): + if hole.symbol in known_symbols: + ordinal = known_symbols[hole.symbol] + else: + ordinal = len(known_symbols) + known_symbols[hole.symbol] = ordinal + self._got_entries.add(ordinal) self.data.pad(8) for stencil in [self.code, self.data]: for hole in stencil.holes: if hole.value is HoleValue.GOT: assert hole.symbol is not None - hole.value = HoleValue.DATA - hole.addend += self._global_offset_table_lookup(hole.symbol) + if "_JIT_" in hole.symbol: + # Relocations for local symbols + hole.value = HoleValue.DATA + hole.addend += self._jit_symbol_table_lookup(hole.symbol) + else: + _ordinal = known_symbols[hole.symbol] + _custom_value = f"got_symbol_address({_ordinal:#x}, state)" + if hole.kind in _X86_GOT_RELOCATIONS: + # When patching on x86, subtract the addend -4 + # that is used to compute the 32 bit RIP relative + # displacement to the GOT entry + _custom_value = ( + f"got_symbol_address({_ordinal:#x}, state) - 4" + ) + hole.addend = _ordinal + hole.custom_value = _custom_value hole.symbol = None elif hole.symbol in self.symbols: hole.value, addend = self.symbols[hole.symbol] @@ -262,16 +363,19 @@ def process_relocations(self, known_symbols: dict[str, int]) -> None: raise ValueError( f"Add PyAPI_FUNC(...) or PyAPI_DATA(...) to declaration of {hole.symbol}!" ) + self._emit_jit_symbol_table() self._emit_global_offset_table() self.code.holes.sort(key=lambda hole: hole.offset) self.data.holes.sort(key=lambda hole: hole.offset) - def _global_offset_table_lookup(self, symbol: str) -> int: - return len(self.data.body) + self._got.setdefault(symbol, 8 * len(self._got)) + def _jit_symbol_table_lookup(self, symbol: str) -> int: + return len(self.data.body) + self._jit_symbol_table.setdefault( + symbol, 8 * len(self._jit_symbol_table) + ) - def _emit_global_offset_table(self) -> None: + def _emit_jit_symbol_table(self) -> None: got = len(self.data.body) - for s, offset in self._got.items(): + for s, offset in self._jit_symbol_table.items(): if s in self.symbols: value, addend = self.symbols[s] symbol = None @@ -295,20 +399,35 @@ def _emit_global_offset_table(self) -> None: ) self.data.body.extend([0] * 8) - def _get_trampoline_mask(self) -> str: + def _emit_global_offset_table(self) -> None: + for hole in self.code.holes: + if hole.value is HoleValue.GOT: + _got_hole = Hole(0, "R_X86_64_64", hole.value, None, hole.addend) + _got_hole.func = "patch_got_symbol" + _got_hole.custom_location = "state" + if _got_hole not in self.data.holes: + self.data.holes.append(_got_hole) + + def _get_symbol_mask(self, ordinals: set[int]) -> str: bitmask: int = 0 - trampoline_mask: list[str] = [] - for ordinal in self._trampolines: + symbol_mask: list[str] = [] + for ordinal in ordinals: bitmask |= 1 << ordinal while bitmask: word = bitmask & ((1 << 32) - 1) - trampoline_mask.append(f"{word:#04x}") + symbol_mask.append(f"{word:#04x}") bitmask >>= 32 - return "{" + (", ".join(trampoline_mask) or "0") + "}" + return "{" + (", ".join(symbol_mask) or "0") + "}" + + def _get_trampoline_mask(self) -> str: + return self._get_symbol_mask(self._trampolines) + + def _get_got_mask(self) -> str: + return self._get_symbol_mask(self._got_entries) def as_c(self, opname: str) -> str: """Dump this hole as a StencilGroup initializer.""" - return f"{{emit_{opname}, {len(self.code.body)}, {len(self.data.body)}, {self._get_trampoline_mask()}}}" + return f"{{emit_{opname}, {len(self.code.body)}, {len(self.data.body)}, {self._get_trampoline_mask()}, {self._get_got_mask()}}}" def symbol_to_value(symbol: str) -> tuple[HoleValue, str | None]: diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 3883671e92aa39..ceee383ea680d7 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -12,6 +12,7 @@ import typing import shlex +import _dwarf import _llvm import _optimizers import _schema @@ -37,6 +38,27 @@ ) +_ELF_UNWIND_AARCH64 = _dwarf.ELFUnwindConfig( + frame_pointer="W29", + return_address="W30", + register_numbers={ + "W29": 29, + "W30": 30, + }, + call_instruction_prefixes=("blr ",), +) + +_ELF_UNWIND_X86_64 = _dwarf.ELFUnwindConfig( + frame_pointer="RBP", + return_address="RIP", + register_numbers={ + "RBP": 6, + "RIP": 16, + }, + call_instruction_prefixes=("callq ", "call "), +) + + @dataclasses.dataclass class _Target(typing.Generic[_S, _R]): triple: str @@ -46,13 +68,24 @@ class _Target(typing.Generic[_S, _R]): optimizer: type[_optimizers.Optimizer] = _optimizers.Optimizer label_prefix: typing.ClassVar[str] symbol_prefix: typing.ClassVar[str] + re_global: typing.ClassVar[re.Pattern[str]] stable: bool = False debug: bool = False verbose: bool = False cflags: str = "" + frame_pointers: bool = False + unwind: _dwarf.ELFUnwindConfig | None = None + llvm_version: str = _llvm._LLVM_VERSION + llvm_tools_install_dir: str | None = None known_symbols: dict[str, int] = dataclasses.field(default_factory=dict) pyconfig_dir: pathlib.Path = pathlib.Path.cwd().resolve() + def _compile_args(self) -> list[str]: + return list(self.args) + + def _shim_compile_args(self) -> list[str]: + return [] + def _get_nop(self) -> bytes: if re.fullmatch(r"aarch64-.*", self.triple): nop = b"\x1f\x20\x03\xd5" @@ -71,14 +104,49 @@ def _compute_digest(self) -> str: hasher.update(PYTHON_EXECUTOR_CASES_C_H.read_bytes()) hasher.update((self.pyconfig_dir / "pyconfig.h").read_bytes()) for dirpath, _, filenames in sorted(os.walk(TOOLS_JIT)): - for filename in filenames: + # Exclude cache files from digest computation to ensure reproducible builds. + if dirpath.endswith("__pycache__"): + continue + for filename in sorted(filenames): hasher.update(pathlib.Path(dirpath, filename).read_bytes()) return hasher.hexdigest() + def _write_generated_header( + self, + output: pathlib.Path, + *, + digest: str, + comment: str, + lines: typing.Iterable[str], + ) -> None: + output_new = output.with_name(f"{output.name}.new") + try: + with output_new.open("w") as file: + file.write(digest) + if comment: + file.write(f"// {comment}\n") + file.write("\n") + for line in lines: + file.write(f"{line}\n") + try: + output_new.replace(output) + except FileNotFoundError: + # another process probably already moved the file + if not output.is_file(): + raise + finally: + output_new.unlink(missing_ok=True) + async def _parse(self, path: pathlib.Path) -> _stencils.StencilGroup: group = _stencils.StencilGroup() args = ["--disassemble", "--reloc", f"{path}"] - output = await _llvm.maybe_run("llvm-objdump", args, echo=self.verbose) + output = await _llvm.maybe_run( + "llvm-objdump", + args, + echo=self.verbose, + llvm_version=self.llvm_version, + llvm_tools_install_dir=self.llvm_tools_install_dir, + ) if output is not None: # Make sure that full paths don't leak out (for reproducibility): long, short = str(path), str(path.name) @@ -96,7 +164,13 @@ async def _parse(self, path: pathlib.Path) -> _stencils.StencilGroup: "--sections", f"{path}", ] - output = await _llvm.run("llvm-readobj", args, echo=self.verbose) + output = await _llvm.run( + "llvm-readobj", + args, + echo=self.verbose, + llvm_version=self.llvm_version, + llvm_tools_install_dir=self.llvm_tools_install_dir, + ) # --elf-output-style=JSON is only *slightly* broken on Mach-O... output = output.replace("PrivateExtern\n", "\n") output = output.replace("Extern\n", "\n") @@ -116,19 +190,16 @@ def _handle_section(self, section: _S, group: _stencils.StencilGroup) -> None: raise NotImplementedError(type(self)) def _handle_relocation( - self, base: int, relocation: _R, raw: bytes | bytearray + self, base: int, relocation: _R, raw: bytearray ) -> _stencils.Hole: raise NotImplementedError(type(self)) - async def _compile( - self, opname: str, c: pathlib.Path, tempdir: pathlib.Path - ) -> _stencils.StencilGroup: - s = tempdir / f"{opname}.s" - o = tempdir / f"{opname}.o" - args_s = [ + def _base_clang_args(self, opname: str, tempdir: pathlib.Path) -> list[str]: + return [ f"--target={self.triple}", "-DPy_BUILD_CORE_MODULE", "-D_DEBUG" if self.debug else "-DNDEBUG", + f"-DSUPPORTS_SMALL_CONSTS={1 if self.optimizer.supports_small_constants else 0}", f"-D_JIT_OPCODE={opname}", "-D_PyJIT_ACTIVE", "-D_Py_JIT", @@ -147,39 +218,93 @@ async def _compile( # generates better code than -O2 (and -O2 usually generates better # code than -O3). As a nice benefit, it uses less memory too: "-Os", - "-S", # Shorten full absolute file paths in the generated code (like the # __FILE__ macro and assert failure messages) for reproducibility: f"-ffile-prefix-map={CPYTHON}=.", f"-ffile-prefix-map={tempdir}=.", - # This debug info isn't necessary, and bloats out the JIT'ed code. - # We *may* be able to re-enable this, process it, and JIT it for a - # nicer debugging experience... but that needs a lot more research: - "-fno-asynchronous-unwind-tables", # Don't call built-in functions that we can't find or patch: "-fno-builtin", - # Emit relaxable 64-bit calls/jumps, so we don't have to worry about - # about emitting in-range trampolines for out-of-range targets. - # We can probably remove this and emit trampolines in the future: - "-fno-plt", # Don't call stack-smashing canaries that we can't find or patch: "-fno-stack-protector", "-std=c11", + ] + + async def _build_stencil_group( + self, opname: str, c: pathlib.Path, tempdir: pathlib.Path + ) -> _stencils.StencilGroup: + s = tempdir / f"{opname}.s" + o = tempdir / f"{opname}.o" + args_s = self._base_clang_args(opname, tempdir) + args_s += [ + "-S", + # Stencils do not need unwind info, and the optimizer does not + # preserve .cfi_* directives correctly. On Darwin, + # -fno-asynchronous-unwind-tables alone still leaves synchronous + # unwind directives in the assembly, so disable both forms here. + "-fno-unwind-tables", + "-fno-asynchronous-unwind-tables", "-o", f"{s}", f"{c}", - *self.args, - # Allow user-provided CFLAGS to override any defaults - *shlex.split(self.cflags), ] - await _llvm.run("clang", args_s, echo=self.verbose) + if self.frame_pointers: + args_s += ["-Xclang", "-mframe-pointer=reserved"] + args_s += self._compile_args() + # Allow user-provided CFLAGS to override any defaults + args_s += shlex.split(self.cflags) + await _llvm.run( + "clang", + args_s, + echo=self.verbose, + llvm_version=self.llvm_version, + llvm_tools_install_dir=self.llvm_tools_install_dir, + ) self.optimizer( - s, label_prefix=self.label_prefix, symbol_prefix=self.symbol_prefix + s, + label_prefix=self.label_prefix, + symbol_prefix=self.symbol_prefix, + re_global=self.re_global, + frame_pointers=self.frame_pointers, ).run() args_o = [f"--target={self.triple}", "-c", "-o", f"{o}", f"{s}"] - await _llvm.run("clang", args_o, echo=self.verbose) + await _llvm.run( + "clang", + args_o, + echo=self.verbose, + llvm_version=self.llvm_version, + llvm_tools_install_dir=self.llvm_tools_install_dir, + ) return await self._parse(o) + async def _build_shim_object(self, output: pathlib.Path) -> None: + with tempfile.TemporaryDirectory() as tempdir: + work = pathlib.Path(tempdir).resolve() + args_o = self._base_clang_args("shim", work) + args_o += self._shim_compile_args() + args_o += [ + "-c", + # The shim is a real function in the final binary, so + # keep unwind info for debuggers and stack walkers. + "-fasynchronous-unwind-tables", + ] + if self.frame_pointers: + args_o += ["-Xclang", "-mframe-pointer=all"] + args_o += self._compile_args() + args_o += shlex.split(self.cflags) + args_o += ["-o", f"{output}", f"{TOOLS_JIT / 'shim.c'}"] + await _llvm.run( + "clang", + args_o, + echo=self.verbose, + llvm_version=self.llvm_version, + llvm_tools_install_dir=self.llvm_tools_install_dir, + ) + + async def _get_shim_unwind_info( + self, output: pathlib.Path + ) -> _dwarf.UnwindInfo | None: + return None + async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]: generated_cases = PYTHON_EXECUTOR_CASES_C_H.read_text() cases_and_opnames = sorted( @@ -188,11 +313,12 @@ async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]: ) ) tasks = [] + # If you need to see the generated assembly files, + # uncomment line below (and comment out line below that) + # with tempfile.TemporaryDirectory("-stencils-assembly", delete=False) as tempdir: with tempfile.TemporaryDirectory() as tempdir: work = pathlib.Path(tempdir).resolve() async with asyncio.TaskGroup() as group: - coro = self._compile("shim", TOOLS_JIT / "shim.c", work) - tasks.append(group.create_task(coro, name="shim")) template = TOOLS_JIT_TEMPLATE_C.read_text() for case, opname in cases_and_opnames: # Write out a copy of the template with *only* this case @@ -202,10 +328,11 @@ async def _build_stencils(self) -> dict[str, _stencils.StencilGroup]: # all of the other cases): c = work / f"{opname}.c" c.write_text(template.replace("CASE", case)) - coro = self._compile(opname, c, work) + coro = self._build_stencil_group(opname, c, work) tasks.append(group.create_task(coro, name=opname)) stencil_groups = {task.get_name(): task.result() for task in tasks} for stencil_group in stencil_groups.values(): + stencil_group.convert_labels_to_relocations() stencil_group.process_relocations(self.known_symbols) return stencil_groups @@ -215,47 +342,67 @@ def build( comment: str = "", force: bool = False, jit_stencils: pathlib.Path, + jit_shim_object: pathlib.Path, + jit_unwind_info: pathlib.Path, ) -> None: - """Build jit_stencils.h in the given directory.""" + """Build jit_stencils.h and the shim object in the given directory.""" jit_stencils.parent.mkdir(parents=True, exist_ok=True) if not self.stable: warning = f"JIT support for {self.triple} is still experimental!" request = "Please report any issues you encounter.".center(len(warning)) + if self.llvm_version != _llvm._LLVM_VERSION: + request = f"Warning! Building with an LLVM version other than {_llvm._LLVM_VERSION} is not supported." outline = "=" * len(warning) print("\n".join(["", outline, warning, request, outline, ""])) digest = f"// {self._compute_digest()}\n" + # The generated headers include the input digest as their first line. + # If every generated artifact is current, skip the expensive rebuild. if ( not force and jit_stencils.exists() and jit_stencils.read_text().startswith(digest) + and jit_shim_object.exists() + and jit_unwind_info.exists() + and jit_unwind_info.read_text().startswith(digest) ): return + # Build the shim first so its compiled DWARF CFI can be used to derive + # the unwind rules emitted into jit_unwind_info-<triple>.h. + ASYNCIO_RUNNER.run(self._build_shim_object(jit_shim_object)) + unwind_info = ASYNCIO_RUNNER.run(self._get_shim_unwind_info(jit_shim_object)) + self._write_generated_header( + jit_unwind_info, + digest=digest, + comment=comment, + lines=_writer.dump_unwind_info(unwind_info), + ) + # Build the uop stencils after the shim metadata has been emitted. stencil_groups = ASYNCIO_RUNNER.run(self._build_stencils()) - jit_stencils_new = jit_stencils.parent / "jit_stencils.h.new" - try: - with jit_stencils_new.open("w") as file: - file.write(digest) - if comment: - file.write(f"// {comment}\n") - file.write("\n") - for line in _writer.dump(stencil_groups, self.known_symbols): - file.write(f"{line}\n") - try: - jit_stencils_new.replace(jit_stencils) - except FileNotFoundError: - # another process probably already moved the file - if not jit_stencils.is_file(): - raise - finally: - jit_stencils_new.unlink(missing_ok=True) + self._write_generated_header( + jit_stencils, + digest=digest, + comment=comment, + lines=_writer.dump(stencil_groups, self.known_symbols), + ) class _COFF( _Target[_schema.COFFSection, _schema.COFFRelocation] ): # pylint: disable = too-few-public-methods + def _shim_compile_args(self) -> list[str]: + # The shim is part of pythoncore, not a shared extension. + # On Windows, Py_BUILD_CORE_MODULE makes public APIs import from + # pythonXY.lib, which creates a self-dependency when linking + # pythoncore.dll. Build the shim with builtin/core semantics. + return ["-UPy_BUILD_CORE_MODULE", "-DPy_BUILD_CORE_BUILTIN"] + def _handle_section( self, section: _schema.COFFSection, group: _stencils.StencilGroup ) -> None: + name = section["Name"]["Value"] + if name == ".debug$S": + # skip debug sections + return flags = {flag["Name"] for flag in section["Characteristics"]["Flags"]} if "SectionData" in section: section_data_bytes = section["SectionData"]["Bytes"] @@ -294,10 +441,7 @@ def _unwrap_dllimport(self, name: str) -> tuple[_stencils.HoleValue, str | None] return _stencils.symbol_to_value(name) def _handle_relocation( - self, - base: int, - relocation: _schema.COFFRelocation, - raw: bytes | bytearray, + self, base: int, relocation: _schema.COFFRelocation, raw: bytearray ) -> _stencils.Hole: match relocation: case { @@ -324,7 +468,8 @@ def _handle_relocation( "Offset": offset, "Symbol": s, "Type": { - "Name": "IMAGE_REL_ARM64_BRANCH26" + "Name": "IMAGE_REL_ARM64_BRANCH19" + | "IMAGE_REL_ARM64_BRANCH26" | "IMAGE_REL_ARM64_PAGEBASE_REL21" | "IMAGE_REL_ARM64_PAGEOFFSET_12A" | "IMAGE_REL_ARM64_PAGEOFFSET_12L" as kind @@ -342,12 +487,18 @@ class _COFF32(_COFF): # These mangle like Mach-O and other "older" formats: label_prefix = "L" symbol_prefix = "_" + re_global = re.compile(r'\s*\.def\s+(?P<label>[\w."$?@]+);') class _COFF64(_COFF): # These mangle like ELF and other "newer" formats: label_prefix = ".L" symbol_prefix = "" + re_global = re.compile(r'\s*\.def\s+(?P<label>[\w."$?@]+);') + + def _compile_args(self) -> list[str]: + runtime = "-fms-runtime-lib=dll_dbg" if self.debug else "-fms-runtime-lib=dll" + return [runtime, *self.args] class _ELF( @@ -355,6 +506,20 @@ class _ELF( ): # pylint: disable = too-few-public-methods label_prefix = ".L" symbol_prefix = "" + re_global = re.compile(r'\s*\.globl\s+(?P<label>[\w."$?@]+)(\s+.*)?') + + async def _get_shim_unwind_info( + self, output: pathlib.Path + ) -> _dwarf.UnwindInfo | None: + assert self.unwind is not None + return await _dwarf.ELFUnwindInfo( + self.triple, + config=self.unwind, + verbose=self.verbose, + llvm_version=self.llvm_version, + llvm_tools_install_dir=self.llvm_tools_install_dir, + llvm_run=_llvm.run, + ).extract(output) def _handle_section( self, section: _schema.ELFSection, group: _stencils.StencilGroup @@ -407,10 +572,7 @@ def _handle_section( }, section_type def _handle_relocation( - self, - base: int, - relocation: _schema.ELFRelocation, - raw: bytes | bytearray, + self, base: int, relocation: _schema.ELFRelocation, raw: bytearray ) -> _stencils.Hole: symbol: str | None match relocation: @@ -448,6 +610,7 @@ class _MachO( ): # pylint: disable = too-few-public-methods label_prefix = "L" symbol_prefix = "_" + re_global = re.compile(r'\s*\.globl\s+(?P<label>[\w."$?@]+)(\s+.*)?') def _handle_section( self, section: _schema.MachOSection, group: _stencils.StencilGroup @@ -489,10 +652,7 @@ def _handle_section( stencil.holes.append(hole) def _handle_relocation( - self, - base: int, - relocation: _schema.MachORelocation, - raw: bytes | bytearray, + self, base: int, relocation: _schema.MachORelocation, raw: bytearray ) -> _stencils.Hole: symbol: str | None match relocation: @@ -557,40 +717,61 @@ def get_target(host: str) -> _COFF32 | _COFF64 | _ELF | _MachO: optimizer: type[_optimizers.Optimizer] target: _COFF32 | _COFF64 | _ELF | _MachO if re.fullmatch(r"aarch64-apple-darwin.*", host): + host = "aarch64-apple-darwin" condition = "defined(__aarch64__) && defined(__APPLE__)" optimizer = _optimizers.OptimizerAArch64 target = _MachO(host, condition, optimizer=optimizer) elif re.fullmatch(r"aarch64-pc-windows-msvc", host): - args = ["-fms-runtime-lib=dll", "-fplt"] + host = "aarch64-pc-windows-msvc" condition = "defined(_M_ARM64)" optimizer = _optimizers.OptimizerAArch64 - target = _COFF64(host, condition, args=args, optimizer=optimizer) + target = _COFF64(host, condition, optimizer=optimizer) elif re.fullmatch(r"aarch64-.*-linux-gnu", host): + host = "aarch64-unknown-linux-gnu" + condition = "defined(__aarch64__) && defined(__linux__)" # -mno-outline-atomics: Keep intrinsics from being emitted. args = ["-fpic", "-mno-outline-atomics"] - condition = "defined(__aarch64__) && defined(__linux__)" optimizer = _optimizers.OptimizerAArch64 - target = _ELF(host, condition, args=args, optimizer=optimizer) + target = _ELF( + host, + condition, + args=args, + optimizer=optimizer, + frame_pointers=True, + unwind=_ELF_UNWIND_AARCH64, + ) elif re.fullmatch(r"i686-pc-windows-msvc", host): + host = "i686-pc-windows-msvc" + condition = "defined(_M_IX86)" # -Wno-ignored-attributes: __attribute__((preserve_none)) is not supported here. - args = ["-DPy_NO_ENABLE_SHARED", "-Wno-ignored-attributes"] + # -mno-sse: Use x87 FPU instead of SSE for float math. The COFF32 + # stencil converter cannot handle _xmm register references. + args = ["-DPy_NO_ENABLE_SHARED", "-Wno-ignored-attributes", "-mno-sse"] optimizer = _optimizers.OptimizerX86 - condition = "defined(_M_IX86)" target = _COFF32(host, condition, args=args, optimizer=optimizer) elif re.fullmatch(r"x86_64-apple-darwin.*", host): + host = "x86_64-apple-darwin" condition = "defined(__x86_64__) && defined(__APPLE__)" optimizer = _optimizers.OptimizerX86 target = _MachO(host, condition, optimizer=optimizer) elif re.fullmatch(r"x86_64-pc-windows-msvc", host): - args = ["-fms-runtime-lib=dll"] + host = "x86_64-pc-windows-msvc" condition = "defined(_M_X64)" optimizer = _optimizers.OptimizerX86 - target = _COFF64(host, condition, args=args, optimizer=optimizer) + target = _COFF64(host, condition, optimizer=optimizer) elif re.fullmatch(r"x86_64-.*-linux-gnu", host): - args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0"] + host = "x86_64-unknown-linux-gnu" condition = "defined(__x86_64__) && defined(__linux__)" + args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0", "-fno-plt"] optimizer = _optimizers.OptimizerX86 - target = _ELF(host, condition, args=args, optimizer=optimizer) + target = _ELF( + host, + condition, + args=args, + optimizer=optimizer, + frame_pointers=True, + unwind=_ELF_UNWIND_X86_64, + ) else: raise ValueError(host) return target diff --git a/Tools/jit/_writer.py b/Tools/jit/_writer.py index 090b52660f009c..6f6865d4ab2704 100644 --- a/Tools/jit/_writer.py +++ b/Tools/jit/_writer.py @@ -1,9 +1,9 @@ -"""Utilities for writing StencilGroups out to a C header file.""" +"""Utilities for writing JIT build artifacts out to C header files.""" -import itertools import typing import math +import _dwarf import _stencils @@ -20,14 +20,11 @@ def _dump_footer( yield " size_t code_size;" yield " size_t data_size;" yield " symbol_mask trampoline_mask;" + yield " symbol_mask got_mask;" yield "} StencilGroup;" yield "" - yield f"static const StencilGroup shim = {groups['shim'].as_c('shim')};" - yield "" - yield "static const StencilGroup stencil_groups[MAX_UOP_ID + 1] = {" + yield "static const StencilGroup stencil_groups[MAX_UOP_REGS_ID + 1] = {" for opname, group in sorted(groups.items()): - if opname == "shim": - continue yield f" [{opname}] = {group.as_c(opname)}," yield "};" yield "" @@ -60,15 +57,8 @@ def _dump_stencil(opname: str, group: _stencils.StencilGroup) -> typing.Iterator for part, stencil in [("data", group.data), ("code", group.code)]: if stencil.body.rstrip(b"\x00"): yield f" memcpy({part}, {part}_body, sizeof({part}_body));" - skip = False stencil.holes.sort(key=lambda hole: hole.offset) - for hole, pair in itertools.zip_longest(stencil.holes, stencil.holes[1:]): - if skip: - skip = False - continue - if pair and (folded := hole.fold(pair, stencil.body)): - skip = True - hole = folded + for hole in stencil.holes: yield f" {hole.as_c(part)}" yield "}" yield "" @@ -81,3 +71,26 @@ def dump( for opname, group in groups.items(): yield from _dump_stencil(opname, group) yield from _dump_footer(groups, symbols) + + +def dump_unwind_info( + unwind_info: _dwarf.UnwindInfo | None, +) -> typing.Iterator[str]: + """Yield JIT unwind information line-by-line as a C header file.""" + if unwind_info is None: + yield "#define JIT_UNWIND_INFO_SUPPORTED 0" + return + + yield "#define JIT_UNWIND_INFO_SUPPORTED 1" + fields = [ + ("JIT_UNWIND_CODE_ALIGNMENT_FACTOR", unwind_info.code_alignment_factor), + ("JIT_UNWIND_DATA_ALIGNMENT_FACTOR", unwind_info.data_alignment_factor), + ("JIT_UNWIND_RA_REG", unwind_info.return_address_register), + ("JIT_UNWIND_CFA_REG", unwind_info.cfa_register), + ("JIT_UNWIND_CFA_OFFSET", unwind_info.cfa_offset), + ("JIT_UNWIND_FP_REG", unwind_info.frame_pointer_register), + ("JIT_UNWIND_FP_OFFSET", unwind_info.frame_pointer_offset), + ("JIT_UNWIND_RA_OFFSET", unwind_info.return_address_offset), + ] + for name, value in fields: + yield f"#define {name} {value}" diff --git a/Tools/jit/build.py b/Tools/jit/build.py index a0733005929bf2..16b8f4ac9c9086 100644 --- a/Tools/jit/build.py +++ b/Tools/jit/build.py @@ -4,9 +4,32 @@ import pathlib import shlex import sys +import typing import _targets + +def _write_target_dispatcher( + output: pathlib.Path, + targets: typing.Iterable[_targets._Target[typing.Any, typing.Any]], + comment: str, + header_prefix: str, +) -> None: + lines = [f"// {comment}\n"] + guard = "#if" + for target in targets: + lines.append(f"{guard} {target.condition}\n") + lines.append(f'#include "{header_prefix}-{target.triple}.h"\n') + guard = "#elif" + lines.append("#else\n") + lines.append('#error "unexpected target"\n') + lines.append("#endif\n") + body = "".join(lines) + # Don't touch the file if it hasn't changed (so we don't trigger a rebuild): + if not output.is_file() or output.read_text() != body: + output.write_text(body) + + if __name__ == "__main__": comment = f"$ {shlex.join([pathlib.Path(sys.executable).name] + sys.argv)}" parser = argparse.ArgumentParser(description=__doc__) @@ -42,6 +65,10 @@ parser.add_argument( "--cflags", help="additional flags to pass to the compiler", default="" ) + parser.add_argument("--llvm-version", help="LLVM version to use") + parser.add_argument( + "--llvm-tools-install-dir", help="Installation location of LLVM tools" + ) args = parser.parse_args() for target in args.target: target.debug = args.debug @@ -49,22 +76,31 @@ target.verbose = args.verbose target.cflags = args.cflags target.pyconfig_dir = args.pyconfig_dir + if args.llvm_version: + target.llvm_version = args.llvm_version + if args.llvm_tools_install_dir: + target.llvm_tools_install_dir = args.llvm_tools_install_dir + # Build this target's stencils, shim object, and target-specific + # unwind metadata before writing the generic dispatcher headers below. target.build( comment=comment, force=args.force, jit_stencils=args.output_dir / f"jit_stencils-{target.triple}.h", + jit_shim_object=args.output_dir / f"jit_shim-{target.triple}.o", + jit_unwind_info=args.output_dir / f"jit_unwind_info-{target.triple}.h", ) - jit_stencils_h = args.output_dir / "jit_stencils.h" - lines = [f"// {comment}\n"] - guard = "#if" - for target in args.target: - lines.append(f"{guard} {target.condition}\n") - lines.append(f'#include "jit_stencils-{target.triple}.h"\n') - guard = "#elif" - lines.append("#else\n") - lines.append('#error "unexpected target"\n') - lines.append("#endif\n") - body = "".join(lines) - # Don't touch the file if it hasn't changed (so we don't trigger a rebuild): - if not jit_stencils_h.is_file() or jit_stencils_h.read_text() != body: - jit_stencils_h.write_text(body) + # Write the target dispatcher that includes the right stencil header for + # the platform compiling Python/jit.c. + _write_target_dispatcher( + args.output_dir / "jit_stencils.h", + args.target, + comment, + "jit_stencils", + ) + # Write the matching dispatcher for generated JIT unwind constants. + _write_target_dispatcher( + args.output_dir / "jit_unwind_info.h", + args.target, + comment, + "jit_unwind_info", + ) diff --git a/Tools/jit/example_trace_dump.py b/Tools/jit/example_trace_dump.py new file mode 100644 index 00000000000000..e3c3df94059044 --- /dev/null +++ b/Tools/jit/example_trace_dump.py @@ -0,0 +1,191 @@ +# This script is best run with pystats enabled to help visualize the shape of the traces. +# ./configure --enable-experimental-jit=interpreter -C --with-pydebug --enable-pystats + +# The resulting images can be visualize on linux as follows: +# $ cd folder_with_gv_files +# $ dot -Tsvg -Osvg *.gv +# $ firefox *.gv.svg + +# type: ignore + +import sys +import os.path +from types import FunctionType + +# All functions declared in this module will be run to generate +# a .gv file of the executors, unless the name starts with an underscore. + + +def _gen(n): + for _ in range(n): + yield n + + +def gen_in_loop(n): + t = 0 + for n in _gen(n): + t += n + return n + + +def short_loop(n): + t = 0 + for _ in range(n): + t += 1 + t += 1 + t += 1 + t += 1 + t += 1 + return t + + +exec( + "\n".join( + ["def mid_loop(n):"] + + [" t = 0"] + + [" for _ in range(n):"] + + [" t += 1"] * 20 + + [" return t"] + ), + globals(), +) + +exec( + "\n".join( + ["def long_loop(n):"] + + [" t = 0"] + + [" for _ in range(n):"] + + [" t += 1"] * 100 + + [" return t"] + ), + globals(), +) + + +def _add(a, b): + return a + b + + +def short_loop_with_calls(n): + t = 0 + for _ in range(n): + t = _add(t, 1) + t = _add(t, 1) + t = _add(t, 1) + t = _add(t, 1) + t = _add(t, 1) + return t + + +exec( + "\n".join( + ["def mid_loop_with_calls(n):"] + + [" t = 0"] + + [" for _ in range(n):"] + + [" t = _add(t, 1)"] * 20 + + [" return t"] + ), + globals(), +) + +exec( + "\n".join( + ["def long_loop_with_calls(n):"] + + [" t = 0"] + + [" for _ in range(n):"] + + [" t = _add(t, 1)"] * 100 + + [" return t"] + ), + globals(), +) + + +def short_loop_with_side_exits(n): + t = 0 + for i in range(n): + if t < 0: + break + t += 1 + if t < 0: + break + t += 1 + if t < 0: + break + t += 1 + if t < 0: + break + t += 1 + if t < 0: + break + t += 1 + return t + + +exec( + "\n".join( + ["def mid_loop_with_side_exits(n):"] + + [" t = 0"] + + [" for _ in range(n):"] + + [" if t < 0:", " break", " t += 1"] * 20 + + [" return t"] + ), + globals(), +) + +exec( + "\n".join( + ["def long_loop_with_side_exits(n):"] + + [" t = 0"] + + [" for _ in range(n):"] + + [" if t < 0:", " break", " t += 1"] * 100 + + [" return t"] + ), + globals(), +) + + +def short_branchy_loop(n): + # Branches are correlated and exit 1 time in 4. + t = 0 + for i in range(n): + # Start with a few operations to form a viable trace + t += 1 + t += 1 + t += 1 + if not t & 6: + continue + t += 1 + if not t & 12: + continue + t += 1 + if not t & 24: + continue + t += 1 + if not t & 48: + continue + t += 1 + return t + + +def _run_and_dump(func, n, outdir): + sys._clear_internal_caches() + func(n) + sys._dump_tracelets(os.path.join(outdir, f"{func.__name__}.gv")) + + +def _main(): + if len(sys.argv) < 2 or len(sys.argv) > 3: + print(f"Usage: {sys.argv[0] if sys.argv else " "} OUTDIR [loops]") + outdir = sys.argv[1] + n = int(sys.argv[2]) if len(sys.argv) > 2 else 5000 + functions = [ + func + for func in globals().values() + if isinstance(func, FunctionType) and not func.__name__.startswith("_") + ] + for func in functions: + _run_and_dump(func, n, outdir) + + +if __name__ == "__main__": + _main() diff --git a/Tools/jit/jit.h b/Tools/jit/jit.h index 10829654eabb38..05e73ac6b39e8b 100644 --- a/Tools/jit/jit.h +++ b/Tools/jit/jit.h @@ -9,4 +9,5 @@ typedef jit_func __attribute__((preserve_none)) jit_func_preserve_none; #define DECLARE_TARGET(NAME) \ _Py_CODEUNIT *__attribute__((preserve_none, visibility("hidden"))) \ - NAME(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate); + NAME(_PyExecutorObject *executor, _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, \ + _PyStackRef _tos_cache0, _PyStackRef _tos_cache1, _PyStackRef _tos_cache2); diff --git a/Tools/jit/jit_infra.md b/Tools/jit/jit_infra.md new file mode 100644 index 00000000000000..0b851c67d65c83 --- /dev/null +++ b/Tools/jit/jit_infra.md @@ -0,0 +1,35 @@ +# JIT Build Infrastructure + +This document includes details about the intricacies of the JIT build infrastructure. + +## Updating LLVM + +When we update LLVM, we need to also update the LLVM release artifact for Windows builds. This is because Windows builds automatically pull prebuilt LLVM binaries in our pipelines (e.g. notice that `.github/workflows/jit.yml` does not explicitly download LLVM or build it from source). + +To update the LLVM release artifact for Windows builds, follow these steps: +1. Go to the [LLVM releases page](https://github.com/llvm/llvm-project/releases). +1. Download Windows artifacts for the desired LLVM version (e.g. `clang+llvm-21.1.4-x86_64-pc-windows-msvc.tar.xz` and `clang+llvm-21.1.4-aarch64-pc-windows-msvc.tar.xz`). +1. Extract and repackage each tarball with the correct directory structure. For example: + ```bash + # For x86_64 (AMD64) + tar -xf clang+llvm-21.1.4-x86_64-pc-windows-msvc.tar.xz + mv clang+llvm-21.1.4-x86_64-pc-windows-msvc llvm-21.1.4.0 + tar -cf - llvm-21.1.4.0 | pv | xz > llvm-21.1.4.0-x64.tar.xz + rm -rf llvm-21.1.4.0 + + # For ARM64 + tar -xf clang+llvm-21.1.4-aarch64-pc-windows-msvc.tar.xz + mv clang+llvm-21.1.4-aarch64-pc-windows-msvc llvm-21.1.4.0 + tar -cf - llvm-21.1.4.0 | pv | xz > llvm-21.1.4.0-ARM64.tar.xz + ``` + Each tarball must contain a top-level directory named `llvm-{version}.0/`. +1. Go to [cpython-bin-deps](https://github.com/python/cpython-bin-deps). +1. Create a new release with the LLVM artifacts. + - Create a new tag to match the LLVM version (e.g. `llvm-21.1.4.0`). + - Specify the release title (e.g. `LLVM 21.1.4`). + - Upload both platform-specific assets to the same release. + +### Other notes +- You must make sure that the name of the artifact matches exactly what is expected in `Tools/jit/_llvm.py` and `PCbuild/get_externals.py`. +- The artifact filename must include the architecture suffix (e.g. `llvm-21.1.4.0-x64.tar.xz`, `llvm-21.1.4.0-ARM64.tar.xz`). +- You must have permissions to create releases in the `cpython-bin-deps` repository. If you don't have permissions, you should contact one of the organization admins. \ No newline at end of file diff --git a/Tools/jit/shim.c b/Tools/jit/shim.c index 0c7feb746c9679..34d771c61bcbeb 100644 --- a/Tools/jit/shim.c +++ b/Tools/jit/shim.c @@ -7,9 +7,11 @@ #include "jit.h" _Py_CODEUNIT * -_JIT_ENTRY(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate) -{ - // Note that this is *not* a tail call: - DECLARE_TARGET(_JIT_CONTINUE); - return _JIT_CONTINUE(frame, stack_pointer, tstate); +_PyJIT_Entry( + _PyExecutorObject *exec, _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate +) { + // Note that this is *not* a tail call + jit_func_preserve_none jitted = (jit_func_preserve_none)exec->jit_code; + return jitted(exec, frame, stack_pointer, tstate, + PyStackRef_ZERO_BITS, PyStackRef_ZERO_BITS, PyStackRef_ZERO_BITS); } diff --git a/Tools/jit/template.c b/Tools/jit/template.c index d07f56e9ce6b42..f1e2e25aaecdad 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -1,5 +1,11 @@ + #include "Python.h" +#ifndef NDEBUG +#undef assert +#define assert(TEST) ((TEST) ? 0 : _Py_jit_assertion_failure(__LINE__)) +#endif + #include "pycore_backoff.h" #include "pycore_call.h" #include "pycore_cell.h" @@ -12,9 +18,11 @@ #include "pycore_frame.h" #include "pycore_function.h" #include "pycore_genobject.h" +#include "pycore_import.h" #include "pycore_interpframe.h" #include "pycore_interpolation.h" #include "pycore_intrinsics.h" +#include "pycore_lazyimportobject.h" #include "pycore_jit.h" #include "pycore_list.h" #include "pycore_long.h" @@ -34,35 +42,60 @@ #include "jit.h" +#ifndef NDEBUG +#undef assert +#define assert(TEST) ((TEST) ? 0 : _Py_jit_assertion_failure(__LINE__)) +#endif + +#undef CURRENT_OPERAND0_64 +#define CURRENT_OPERAND0_64() (_operand0_64) + +#undef CURRENT_OPERAND1_64 +#define CURRENT_OPERAND1_64() (_operand1_64) + + #undef CURRENT_OPARG +#undef CURRENT_OPERAND0_16 +#undef CURRENT_OPERAND0_32 +#undef CURRENT_OPERAND1_16 +#undef CURRENT_OPERAND1_32 + +#if SUPPORTS_SMALL_CONSTS + +#define CURRENT_OPARG() (_oparg_16) +#define CURRENT_OPERAND0_32() (_operand0_32) +#define CURRENT_OPERAND0_16() (_operand0_16) +#define CURRENT_OPERAND1_32() (_operand1_32) +#define CURRENT_OPERAND1_16() (_operand1_16) + +#else + #define CURRENT_OPARG() (_oparg) +#define CURRENT_OPERAND0_32() (_operand0_64) +#define CURRENT_OPERAND0_16() (_operand0_64) +#define CURRENT_OPERAND1_32() (_operand1_64) +#define CURRENT_OPERAND1_16() (_operand1_64) -#undef CURRENT_OPERAND0 -#define CURRENT_OPERAND0() (_operand0) +#endif -#undef CURRENT_OPERAND1 -#define CURRENT_OPERAND1() (_operand1) #undef CURRENT_TARGET #define CURRENT_TARGET() (_target) -#undef GOTO_TIER_TWO -#define GOTO_TIER_TWO(EXECUTOR) \ +#undef TIER2_TO_TIER2 +#define TIER2_TO_TIER2(EXECUTOR) \ do { \ OPT_STAT_INC(traces_executed); \ _PyExecutorObject *_executor = (EXECUTOR); \ - tstate->current_executor = (PyObject *)_executor; \ - jit_func_preserve_none jitted = _executor->jit_side_entry; \ - __attribute__((musttail)) return jitted(frame, stack_pointer, tstate); \ + jit_func_preserve_none jitted = _executor->jit_code; \ + __attribute__((musttail)) return jitted(_executor, frame, stack_pointer, tstate, \ + _tos_cache0, _tos_cache1, _tos_cache2); \ } while (0) -#undef GOTO_TIER_ONE -#define GOTO_TIER_ONE(TARGET) \ -do { \ - tstate->current_executor = NULL; \ - _PyFrame_SetStackPointer(frame, stack_pointer); \ - return TARGET; \ -} while (0) +#undef GOTO_TIER_ONE_SETUP +#define GOTO_TIER_ONE_SETUP \ + tstate->current_executor = NULL; \ + _PyFrame_SetStackPointer(frame, stack_pointer); #undef LOAD_IP #define LOAD_IP(UNUSED) \ @@ -70,14 +103,17 @@ do { \ } while (0) #undef LLTRACE_RESUME_FRAME -#define LLTRACE_RESUME_FRAME() \ - do { \ - } while (0) +#ifdef Py_DEBUG +#define LLTRACE_RESUME_FRAME() (frame->lltrace = 0) +#else +#define LLTRACE_RESUME_FRAME() do {} while (0) +#endif #define PATCH_JUMP(ALIAS) \ do { \ DECLARE_TARGET(ALIAS); \ - __attribute__((musttail)) return ALIAS(frame, stack_pointer, tstate); \ + __attribute__((musttail)) return ALIAS(current_executor, frame, stack_pointer, tstate, \ + _tos_cache0, _tos_cache1, _tos_cache2); \ } while (0) #undef JUMP_TO_JUMP_TARGET @@ -88,27 +124,43 @@ do { \ #define TIER_TWO 2 +#ifdef Py_DEBUG +#define ASSERT_WITHIN_STACK_BOUNDS(F, L) _Py_jit_assert_within_stack_bounds(frame, stack_pointer, (L)) +#else +#define ASSERT_WITHIN_STACK_BOUNDS(F, L) (void)0 +#endif + __attribute__((preserve_none)) _Py_CODEUNIT * -_JIT_ENTRY(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate) -{ +_JIT_ENTRY( + _PyExecutorObject *executor, _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate, + _PyStackRef _tos_cache0, _PyStackRef _tos_cache1, _PyStackRef _tos_cache2 +) { // Locals that the instruction implementations expect to exist: - PATCH_VALUE(_PyExecutorObject *, current_executor, _JIT_EXECUTOR) + _PyExecutorObject *current_executor = executor; int oparg; int uopcode = _JIT_OPCODE; _Py_CODEUNIT *next_instr; // Other stuff we need handy: - PATCH_VALUE(uint16_t, _oparg, _JIT_OPARG) #if SIZEOF_VOID_P == 8 - PATCH_VALUE(uint64_t, _operand0, _JIT_OPERAND0) - PATCH_VALUE(uint64_t, _operand1, _JIT_OPERAND1) + PATCH_VALUE(uint64_t, _operand0_64, _JIT_OPERAND0) + PATCH_VALUE(uint64_t, _operand1_64, _JIT_OPERAND1) #else assert(SIZEOF_VOID_P == 4); PATCH_VALUE(uint32_t, _operand0_hi, _JIT_OPERAND0_HI) PATCH_VALUE(uint32_t, _operand0_lo, _JIT_OPERAND0_LO) - uint64_t _operand0 = ((uint64_t)_operand0_hi << 32) | _operand0_lo; + uint64_t _operand0_64 = ((uint64_t)_operand0_hi << 32) | _operand0_lo; PATCH_VALUE(uint32_t, _operand1_hi, _JIT_OPERAND1_HI) PATCH_VALUE(uint32_t, _operand1_lo, _JIT_OPERAND1_LO) - uint64_t _operand1 = ((uint64_t)_operand1_hi << 32) | _operand1_lo; + uint64_t _operand1_64 = ((uint64_t)_operand1_hi << 32) | _operand1_lo; +#endif +#if SUPPORTS_SMALL_CONSTS + PATCH_VALUE(uint32_t, _operand0_32, _JIT_OPERAND0_32) + PATCH_VALUE(uint32_t, _operand1_32, _JIT_OPERAND1_32) + PATCH_VALUE(uint16_t, _operand0_16, _JIT_OPERAND0_16) + PATCH_VALUE(uint16_t, _operand1_16, _JIT_OPERAND1_16) + PATCH_VALUE(uint16_t, _oparg_16, _JIT_OPARG_16) +#else + PATCH_VALUE(uint16_t, _oparg, _JIT_OPARG) #endif PATCH_VALUE(uint32_t, _target, _JIT_TARGET) OPT_STAT_INC(uops_executed); diff --git a/Tools/lockbench/lockbench.py b/Tools/lockbench/lockbench.py index 9833d703e00cbb..d2608797f3a4d5 100644 --- a/Tools/lockbench/lockbench.py +++ b/Tools/lockbench/lockbench.py @@ -1,14 +1,28 @@ -# Measure the performance of PyMutex and PyThread_type_lock locks -# with short critical sections. +# Measure the performance of PyMutex locks with short critical sections. # -# Usage: python Tools/lockbench/lockbench.py [CRITICAL_SECTION_LENGTH] +# Usage: python Tools/lockbench/lockbench.py [options] +# +# Options: +# --work-inside N Units of work inside the critical section (default: 1). +# --work-outside N Units of work outside the critical section (default: 0). +# Each unit of work is a dependent floating-point +# addition, which takes about 0.4 ns on modern +# Intel / AMD processors. +# --num-locks N Number of independent locks (default: 1). Threads are +# assigned to locks round-robin. +# --random-locks Each thread picks a random lock per acquisition instead +# of using a fixed assignment. Requires --num-locks > 1. +# --acquisitions N Lock acquisitions per loop iteration (default: 1). +# --total-iters N Fixed iterations per thread (default: 0 = time-based). +# Useful for measuring fairness: the benchmark runs until +# the slowest thread finishes. # # How to interpret the results: # # Acquisitions (kHz): Reports the total number of lock acquisitions in # thousands of acquisitions per second. This is the most important metric, # particularly for the 1 thread case because even in multithreaded programs, -# most locks acquisitions are not contended. Values for 2+ threads are +# most lock acquisitions are not contended. Values for 2+ threads are # only meaningful for `--disable-gil` builds, because the GIL prevents most # situations where there is lock contention with short critical sections. # @@ -19,14 +33,15 @@ # See https://en.wikipedia.org/wiki/Fairness_measure#Jain's_fairness_index from _testinternalcapi import benchmark_locks -import sys - -# Max number of threads to test -MAX_THREADS = 10 +import argparse -# How much "work" to do while holding the lock -CRITICAL_SECTION_LENGTH = 1 +def parse_threads(value): + if '-' in value: + lo, hi = value.split('-', 1) + lo, hi = int(lo), int(hi) + return range(lo, hi + 1) + return range(int(value), int(value) + 1) def jains_fairness(values): # Jain's fairness index @@ -34,20 +49,44 @@ def jains_fairness(values): return (sum(values) ** 2) / (len(values) * sum(x ** 2 for x in values)) def main(): - print("Lock Type Threads Acquisitions (kHz) Fairness") - for lock_type in ["PyMutex", "PyThread_type_lock"]: - use_pymutex = (lock_type == "PyMutex") - for num_threads in range(1, MAX_THREADS + 1): - acquisitions, thread_iters = benchmark_locks( - num_threads, use_pymutex, CRITICAL_SECTION_LENGTH) + parser = argparse.ArgumentParser(description="Benchmark PyMutex locks") + parser.add_argument("--work-inside", type=int, default=1, + help="units of work inside the critical section") + parser.add_argument("--work-outside", type=int, default=0, + help="units of work outside the critical section") + parser.add_argument("--acquisitions", type=int, default=1, + help="lock acquisitions per loop iteration") + parser.add_argument("--total-iters", type=int, default=0, + help="fixed iterations per thread (0 = time-based)") + parser.add_argument("--num-locks", type=int, default=1, + help="number of independent locks (round-robin assignment)") + parser.add_argument("--random-locks", action="store_true", + help="pick a random lock per acquisition") + parser.add_argument("threads", type=parse_threads, nargs='?', + default=range(1, 11), + help="Number of threads: N or MIN-MAX (default: 1-10)") + args = parser.parse_args() + + header = f"{'Threads': <10}{'Acq (kHz)': >12}{'Fairness': >10}" + if args.total_iters: + header += f"{'Wall (ms)': >12}" + print(header) + for num_threads in args.threads: + acquisitions, thread_iters, elapsed_ns = \ + benchmark_locks( + num_threads, args.work_inside, args.work_outside, + 1000, args.acquisitions, args.total_iters, + args.num_locks, args.random_locks) - acquisitions /= 1000 # report in kHz for readability - fairness = jains_fairness(thread_iters) + wall_ms = elapsed_ns / 1e6 + acquisitions /= 1000 # report in kHz for readability + fairness = jains_fairness(thread_iters) - print(f"{lock_type: <20}{num_threads: <18}{acquisitions: >5.0f}{fairness: >20.2f}") + line = f"{num_threads: <10}{acquisitions: >12.0f}{fairness: >10.2f}" + if args.total_iters: + line += f"{wall_ms: >12.1f}" + print(line) if __name__ == "__main__": - if len(sys.argv) > 1: - CRITICAL_SECTION_LENGTH = int(sys.argv[1]) main() diff --git a/Tools/msi/README.txt b/Tools/msi/README.txt index 8ae156450d5240..4b3de9e82f6f1a 100644 --- a/Tools/msi/README.txt +++ b/Tools/msi/README.txt @@ -528,4 +528,3 @@ explicitly handled by the installer. Python packages installed later using a tool like pip will not be removed. Some components may be installed by other installers and these will not be removed if another product has a dependency on them. - diff --git a/Tools/msi/bundle/Default.wxl b/Tools/msi/bundle/Default.wxl index 0bd3644b58b747..335c1d922d97b5 100644 --- a/Tools/msi/bundle/Default.wxl +++ b/Tools/msi/bundle/Default.wxl @@ -92,7 +92,7 @@ Select Customize to review current options.</String> <String Id="PrecompileLabel">&amp;Precompile standard library</String> <String Id="Include_symbolsLabel">Download debugging &amp;symbols</String> <String Id="Include_debugLabel">Download debu&amp;g binaries (requires VS 2017 or later)</String> - <String Id="Include_freethreadedLabel">Download &amp;free-threaded binaries (experimental)</String> + <String Id="Include_freethreadedLabel">Download &amp;free-threaded binaries</String> <String Id="ProgressHeader">[ActionLikeInstallation] Progress</String> <String Id="ProgressLabel">[ActionLikeInstalling]:</String> diff --git a/Tools/msi/bundle/bootstrap/pythonba.vcxproj b/Tools/msi/bundle/bootstrap/pythonba.vcxproj index 3970e857894ba5..4b38582f0d31e5 100644 --- a/Tools/msi/bundle/bootstrap/pythonba.vcxproj +++ b/Tools/msi/bundle/bootstrap/pythonba.vcxproj @@ -21,6 +21,7 @@ <PropertyGroup Label="Globals"> <Configuration Condition="'$(Configuration)' == ''">Release</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <PlatformToolset Condition="'$(PlatformToolset)' == '' and '$(VisualStudioVersion)' == '18.0'">v145</PlatformToolset> <PlatformToolset Condition="'$(PlatformToolset)' == '' and '$(VisualStudioVersion)' == '17.0'">v143</PlatformToolset> <PlatformToolset Condition="'$(PlatformToolset)' == '' and '$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset> <PlatformToolset Condition="'$(PlatformToolset)' == '' and ('$(MSBuildToolsVersion)' == '15.0' or '$(VisualStudioVersion)' == '15.0')">v141</PlatformToolset> diff --git a/Tools/msi/bundle/bundle.wxs b/Tools/msi/bundle/bundle.wxs index abfeb88784890c..3fcb00553f5edd 100644 --- a/Tools/msi/bundle/bundle.wxs +++ b/Tools/msi/bundle/bundle.wxs @@ -106,11 +106,11 @@ <Variable Name="SimpleInstallDescription" Value="" bal:Overridable="yes" /> <Chain ParallelCache="yes"> + <PackageGroupRef Id="core" /> + <PackageGroupRef Id="exe" /> <?if $(var.Platform)!="ARM64" ?> <PackageGroupRef Id="crt" /> <?endif ?> - <PackageGroupRef Id="core" /> - <PackageGroupRef Id="exe" /> <PackageGroupRef Id="dev" /> <PackageGroupRef Id="lib" /> <?if $(var.IncludeFreethreaded)~="true" ?> diff --git a/Tools/msi/common.wxs b/Tools/msi/common.wxs index 54fa749ab17cdd..73da474e4181f1 100644 --- a/Tools/msi/common.wxs +++ b/Tools/msi/common.wxs @@ -75,6 +75,12 @@ </Fragment> <!-- Top-level directories --> + <Fragment> + <DirectoryRef Id="InstallDirectory"> + <Directory Id="abi3t_compat" Name="abi3t-compat" /> + </DirectoryRef> + </Fragment> + <Fragment> <DirectoryRef Id="InstallDirectory"> <Directory Id="DLLs" Name="DLLs"> diff --git a/Tools/msi/core/core_files.wxs b/Tools/msi/core/core_files.wxs index 145e1471247aa1..8b21501078ea2e 100644 --- a/Tools/msi/core/core_files.wxs +++ b/Tools/msi/core/core_files.wxs @@ -2,6 +2,9 @@ <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Fragment> <ComponentGroup Id="core_dll"> + <Component Id="python_abi3tcompat.dll" Directory="abi3t_compat" Guid="*"> + <File Id="python_abi3tcompat.dll" Name="python$(var.MajorVersionNumber)t.dll" KeyPath="yes" /> + </Component> <Component Id="python_stable.dll" Directory="InstallDirectory" Guid="*"> <File Id="python_stable.dll" Name="python$(var.MajorVersionNumber).dll" KeyPath="yes" /> </Component> @@ -19,6 +22,9 @@ </Fragment> <Fragment> <ComponentGroup Id="core_dll_d"> + <Component Id="python_abi3tcompat_d.dll" Directory="abi3t_compat" Guid="*"> + <File Id="python_abi3tcompat_d.dll" Name="python$(var.MajorVersionNumber)t_d.dll" KeyPath="yes" /> + </Component> <Component Id="python_stable_d.dll" Directory="InstallDirectory" Guid="*"> <File Id="python_stable_d.dll" Name="python$(var.MajorVersionNumber)_d.dll" KeyPath="yes" /> </Component> diff --git a/Tools/msi/dev/dev_files.wxs b/Tools/msi/dev/dev_files.wxs index 21f9c848cc6be5..a9039d03f5f6fa 100644 --- a/Tools/msi/dev/dev_files.wxs +++ b/Tools/msi/dev/dev_files.wxs @@ -13,6 +13,9 @@ <Component Id="libs_python3.lib" Directory="libs" Guid="*"> <File Id="libs_python_stable.lib" Name="python$(var.MajorVersionNumber).lib" KeyPath="yes" /> </Component> + <Component Id="libs_python3t.lib" Directory="libs" Guid="*"> + <File Id="libs_python_abi3tcompat.lib" Name="python$(var.MajorVersionNumber)t.lib" KeyPath="yes" /> + </Component> <Component Id="libs_python.lib" Directory="libs" Guid="*"> <File Id="libs_python.lib" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber).lib" KeyPath="yes" /> </Component> @@ -24,6 +27,9 @@ <Component Id="libs_python3_d.lib" Directory="libs" Guid="*"> <File Id="libs_python_stable_d.lib" Name="python$(var.MajorVersionNumber)_d.lib" /> </Component> + <Component Id="libs_python3t_d.lib" Directory="libs" Guid="*"> + <File Id="libs_python_abi3tcompat_d.lib" Name="python$(var.MajorVersionNumber)t_d.lib" /> + </Component> <Component Id="libs_python_d.lib" Directory="libs" Guid="*"> <File Id="libs_python_d.lib" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)_d.lib" /> </Component> diff --git a/Tools/msi/freethreaded/freethreaded_files.wxs b/Tools/msi/freethreaded/freethreaded_files.wxs index 0707e77b5e9ab2..fdbcaea38eb317 100644 --- a/Tools/msi/freethreaded/freethreaded_files.wxs +++ b/Tools/msi/freethreaded/freethreaded_files.wxs @@ -29,7 +29,7 @@ <ComponentGroup Id="freethreaded_exe"> <Component Id="freethreaded_python.exe" Directory="InstallDirectory" Guid="$(var.FreethreadedPythonExeComponentGuid)"> - <File Name="python$(var.ShortVersion)t.exe" KeyPath="yes" /> + <File Name="python$(var.ShortVersion)t.exe" Source="!(bindpath.build_t)\python.exe" KeyPath="yes" /> <RegistryKey Root="HKMU" Key="[FREETHREADED_REGISTRYKEY]"> <RegistryValue Key="InstallPath" Type="string" Value="[InstallDirectory]" KeyPath="no" /> @@ -37,68 +37,72 @@ </RegistryKey> </Component> <Component Id="freethreaded_pythonw.exe" Directory="InstallDirectory" Guid="$(var.FreethreadedPythonwExeComponentGuid)"> - <File Name="pythonw$(var.ShortVersion)t.exe" KeyPath="yes" /> + <File Name="pythonw$(var.ShortVersion)t.exe" Source="!(bindpath.build_t)\pythonw.exe" KeyPath="yes" /> <RegistryKey Root="HKMU" Key="[FREETHREADED_REGISTRYKEY]"> <RegistryValue Key="InstallPath" Name="WindowedExecutablePath" Type="string" Value="[#pythonw$(var.ShortVersion)t.exe]" KeyPath="no" /> </RegistryKey> </Component> <Component Id="freethreaded_python_stable.dll" Directory="InstallDirectory" Guid="*"> - <File Id="freethreaded_python_stable.dll" Name="python$(var.MajorVersionNumber)t.dll" KeyPath="yes" /> + <File Id="freethreaded_python_stable.dll" Name="python$(var.MajorVersionNumber)t.dll" Source="!(bindpath.build_t)\python$(var.MajorVersionNumber)t.dll" KeyPath="yes" /> </Component> <Component Id="freethreaded_python.dll" Directory="InstallDirectory" Guid="*"> - <File Id="freethreaded_python.dll" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t.dll" KeyPath="yes" /> + <File Id="freethreaded_python.dll" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t.dll" Source="!(bindpath.build_t)\python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t.dll" KeyPath="yes" /> </Component> <Component Id="freethreaded_python_stable.lib" Directory="libs" Guid="*"> - <File Id="freethreaded_python_stable.lib" Name="python$(var.MajorVersionNumber)t.lib" KeyPath="yes" /> + <File Id="freethreaded_python_stable.lib" Name="python$(var.MajorVersionNumber)t.lib" Source="!(bindpath.build_t)\python$(var.MajorVersionNumber)t.lib" KeyPath="yes" /> </Component> <Component Id="freethreaded_python.lib" Directory="libs" Guid="*"> - <File Id="freethreaded_python.lib" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t.lib" KeyPath="yes" /> + <File Id="freethreaded_python.lib" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t.lib" Source="!(bindpath.build_t)\python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t.lib" KeyPath="yes" /> </Component> </ComponentGroup> </Fragment> <Fragment> <ComponentGroup Id="freethreaded_symbols"> <Component Id="freethreaded_python_dll.pdb" Directory="InstallDirectory" Guid="*"> - <File Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t.pdb" KeyPath="yes" /> + <File Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t.pdb" Source="!(bindpath.build_t)\python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t.pdb" KeyPath="yes" /> </Component> + <!-- + Renaming the PDB like this will break automatic connection by debuggers, + but short of making people stop using this installer there's no real option. + --> <Component Id="freethreaded_python.pdb" Directory="InstallDirectory" Guid="*"> - <File Name="python$(var.ShortVersion)t.pdb" /> + <File Name="python$(var.ShortVersion)t.pdb" Source="!(bindpath.build_t)\python.pdb" /> </Component> <Component Id="freethreaded_pythonw.pdb" Directory="InstallDirectory" Guid="*"> - <File Name="pythonw$(var.ShortVersion)t.pdb" /> + <File Name="pythonw$(var.ShortVersion)t.pdb" Source="!(bindpath.build_t)\pythonw.pdb" /> </Component> </ComponentGroup> </Fragment> <Fragment> <ComponentGroup Id="freethreaded_dll_d"> <Component Id="freethreaded_python_stable_d.dll" Directory="InstallDirectory" Guid="*"> - <File Id="freethreaded_python_stable_d.dll" Name="python$(var.MajorVersionNumber)t_d.dll" KeyPath="yes" /> + <File Id="freethreaded_python_stable_d.dll" Name="python$(var.MajorVersionNumber)t_d.dll" Source="!(bindpath.build_t)\python$(var.MajorVersionNumber)t_d.dll" KeyPath="yes" /> </Component> <Component Id="freethreaded_python_d.dll" Directory="InstallDirectory" Guid="*"> - <File Id="freethreaded_python_d.dll" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t_d.dll" KeyPath="yes" /> - <File Id="freethreaded_python_d.pdb" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t_d.pdb" KeyPath="no" /> + <File Id="freethreaded_python_d.dll" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t_d.dll" Source="!(bindpath.build_t)\python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t_d.dll" KeyPath="yes" /> + <File Id="freethreaded_python_d.pdb" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t_d.pdb" Source="!(bindpath.build_t)\python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t_d.pdb" KeyPath="no" /> </Component> <Component Id="freethreaded_python_stable_d.lib" Directory="libs" Guid="*"> - <File Id="freethreaded_python_stable_d.lib" Name="python$(var.MajorVersionNumber)t_d.lib" KeyPath="yes" /> + <File Id="freethreaded_python_stable_d.lib" Name="python$(var.MajorVersionNumber)t_d.lib" Source="!(bindpath.build_t)\python$(var.MajorVersionNumber)t_d.lib" KeyPath="yes" /> </Component> <Component Id="freethreaded_python_d.lib" Directory="libs" Guid="*"> - <File Id="freethreaded_python_d.lib" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t_d.lib" KeyPath="yes" /> + <File Id="freethreaded_python_d.lib" Name="python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t_d.lib" Source="!(bindpath.build_t)\python$(var.MajorVersionNumber)$(var.MinorVersionNumber)t_d.lib" KeyPath="yes" /> </Component> </ComponentGroup> </Fragment> <Fragment> <ComponentGroup Id="freethreaded_exe_d"> <Component Id="freethreaded_python_d.exe" Directory="InstallDirectory" Guid="*"> - <File Name="python$(var.ShortVersion)t_d.exe" /> + <File Name="python$(var.ShortVersion)t_d.exe" Source="!(bindpath.build_t)python_d.exe" /> </Component> <Component Id="freethreaded_python_d.pdb" Directory="InstallDirectory" Guid="*"> - <File Name="python$(var.ShortVersion)t_d.pdb" /> + <File Name="python$(var.ShortVersion)t_d.pdb" Source="!(bindpath.build_t)python_d.pdb" /> </Component> <Component Id="freethreaded_pythonw_d.exe" Directory="InstallDirectory" Guid="*"> - <File Name="pythonw$(var.ShortVersion)t_d.exe" /> + <File Name="pythonw$(var.ShortVersion)t_d.exe" Source="!(bindpath.build_t)pythonw_d.exe" /> </Component> <Component Id="freethreaded_pythonw_d.pdb" Directory="InstallDirectory" Guid="*"> - <File Name="pythonw$(var.ShortVersion)t_d.pdb" /> + <File Name="pythonw$(var.ShortVersion)t_d.pdb" Source="!(bindpath.build_t)pythonw_d.pdb" /> </Component> </ComponentGroup> </Fragment> @@ -111,16 +115,16 @@ <?foreach ext in $(var.exts)?> <Component Id="freethreaded_$(var.ext).pyd" Directory="DLLs" Guid="*"> - <File Name="$(var.ext)$(var.FreethreadedPydTag).pyd" KeyPath="yes" /> + <File Name="$(var.ext)$(var.FreethreadedPydTag).pyd" Source="!(bindpath.build_t)\$(var.ext)$(var.FreethreadedPydTag).pyd" KeyPath="yes" /> </Component> <?endforeach ?> <Component Id="venvlaunchert.exe" Directory="Lib_venv_scripts_nt__freethreaded" Guid="*"> - <File Name="venvlaunchert.exe" KeyPath="yes" /> + <File Name="venvlaunchert.exe" Source="!(bindpath.build_t)\venvlaunchert.exe" KeyPath="yes" /> </Component> <Component Id="venvwlaunchert.exe" Directory="Lib_venv_scripts_nt__freethreaded" Guid="*"> - <File Name="venvwlaunchert.exe" KeyPath="yes" /> + <File Name="venvwlaunchert.exe" Source="!(bindpath.build_t)\venvwlaunchert.exe" KeyPath="yes" /> </Component> </ComponentGroup> </Fragment> @@ -132,16 +136,16 @@ <?foreach ext in $(var.exts)?> <Component Id="freethreaded_$(var.ext).pdb" Directory="DLLs" Guid="*"> - <File Name="$(var.ext)$(var.FreethreadedPydTag).pdb" /> + <File Name="$(var.ext)$(var.FreethreadedPydTag).pdb" Source="!(bindpath.build_t)\$(var.ext)$(var.FreethreadedPydTag).pdb" /> </Component> <?endforeach ?> <Component Id="venvlaunchert.pdb" Directory="Lib_venv_scripts_nt__freethreaded" Guid="*"> - <File Name="venvlaunchert.pdb" KeyPath="yes" /> + <File Name="venvlaunchert.pdb" Source="!(bindpath.build_t)\venvlaunchert.pdb" KeyPath="yes" /> </Component> <Component Id="venvwlaunchert.pdb" Directory="Lib_venv_scripts_nt__freethreaded" Guid="*"> - <File Name="venvwlaunchert.pdb" KeyPath="yes" /> + <File Name="venvwlaunchert.pdb" Source="!(bindpath.build_t)\venvwlaunchert.pdb" KeyPath="yes" /> </Component> </ComponentGroup> </Fragment> @@ -151,21 +155,21 @@ <?foreach ext in $(var.exts)?> <Component Id="freethreaded_$(var.ext)_d.pyd" Directory="DLLs" Guid="*"> - <File Name="$(var.ext)_d$(var.FreethreadedPydTag).pyd" /> + <File Name="$(var.ext)_d$(var.FreethreadedPydTag).pyd" Source="!(bindpath.build_t)\$(var.ext)_d$(var.FreethreadedPydTag).pyd" /> </Component> <Component Id="freethreaded_$(var.ext)_d.pdb" Directory="DLLs" Guid="*"> - <File Name="$(var.ext)_d$(var.FreethreadedPydTag).pdb" /> + <File Name="$(var.ext)_d$(var.FreethreadedPydTag).pdb" Source="!(bindpath.build_t)\$(var.ext)_d$(var.FreethreadedPydTag).pdb" /> </Component> <?endforeach ?> <Component Id="venvlaunchert_d.exe" Directory="Lib_venv_scripts_nt__freethreaded" Guid="*"> - <File Name="venvlaunchert_d.exe" KeyPath="yes" /> - <File Name="venvlaunchert_d.pdb" /> + <File Name="venvlaunchert_d.exe" Source="!(bindpath.build_t)\venvlaunchert_d.exe" KeyPath="yes" /> + <File Name="venvlaunchert_d.pdb" Source="!(bindpath.build_t)\venvlaunchert_d.pdb" /> </Component> <Component Id="venvwlaunchert_d.exe" Directory="Lib_venv_scripts_nt__freethreaded" Guid="*"> - <File Name="venvwlaunchert_d.exe" KeyPath="yes" /> - <File Name="venvwlaunchert_d.pdb" /> + <File Name="venvwlaunchert_d.exe" Source="!(bindpath.build_t)\venvwlaunchert_d.exe" KeyPath="yes" /> + <File Name="venvwlaunchert_d.pdb" Source="!(bindpath.build_t)\venvwlaunchert_d.pdb" /> </Component> </ComponentGroup> </Fragment> diff --git a/Tools/msi/msi.props b/Tools/msi/msi.props index 372c4823bce07f..097af60715448f 100644 --- a/Tools/msi/msi.props +++ b/Tools/msi/msi.props @@ -122,6 +122,30 @@ <LinkerBindInputPaths Include="$(PGOBuildPath);$(BuildPath)"> <BindName></BindName> </LinkerBindInputPaths> + <!-- + This looks repetitive, but we need to make sure that we override any + environment variable that changes the paths for free-threading so that + the binds are correct. Otherwise, we'd rely on defaults. + --> + <LinkerBindInputPaths Include="$(BuildPath32)" Condition="$(Platform) == 'x86'"> + <BindName>build</BindName> + </LinkerBindInputPaths> + <LinkerBindInputPaths Include="$(BuildPath64)" Condition="$(Platform) == 'x64'"> + <BindName>build</BindName> + </LinkerBindInputPaths> + <LinkerBindInputPaths Include="$(BuildPathARM64)" Condition="$(Platform) == 'ARM64'"> + <BindName>build</BindName> + </LinkerBindInputPaths> + <LinkerBindInputPaths Include="$(BuildPath32t)" Condition="$(Platform) == 'x86'"> + <BindName>build_t</BindName> + </LinkerBindInputPaths> + <LinkerBindInputPaths Include="$(BuildPath64t)" Condition="$(Platform) == 'x64'"> + <BindName>build_t</BindName> + </LinkerBindInputPaths> + <LinkerBindInputPaths Include="$(BuildPathARM64t)" Condition="$(Platform) == 'ARM64'"> + <BindName>build_t</BindName> + </LinkerBindInputPaths> + <LinkerBindInputPaths Include="$(PySourcePath)"> <BindName>src</BindName> </LinkerBindInputPaths> @@ -131,6 +155,7 @@ <LinkerBindInputPaths Include="$(CRTRedist)"> <BindName>redist</BindName> </LinkerBindInputPaths> + <LinkerBindInputPaths Include="$(BuildPath32)"> <BindName>build32</BindName> </LinkerBindInputPaths> @@ -140,6 +165,15 @@ <LinkerBindInputPaths Include="$(BuildPathARM64)"> <BindName>buildarm64</BindName> </LinkerBindInputPaths> + <LinkerBindInputPaths Include="$(BuildPath32t)"> + <BindName>build32t</BindName> + </LinkerBindInputPaths> + <LinkerBindInputPaths Include="$(BuildPath64t)"> + <BindName>build64t</BindName> + </LinkerBindInputPaths> + <LinkerBindInputPaths Include="$(BuildPathARM64t)"> + <BindName>buildarm64t</BindName> + </LinkerBindInputPaths> </ItemGroup> <Target Name="_ValidateMsiProps" BeforeTargets="PrepareForBuild"> diff --git a/Tools/msi/tcltk/tcltk_files.wxs b/Tools/msi/tcltk/tcltk_files.wxs index 5dad7c98d4f048..7c7784741d9178 100644 --- a/Tools/msi/tcltk/tcltk_files.wxs +++ b/Tools/msi/tcltk/tcltk_files.wxs @@ -10,11 +10,14 @@ <Component Id="_tkinter.lib" Directory="libs" Guid="*"> <File Name="_tkinter.lib" KeyPath="yes" /> </Component> - <Component Id="tcl86t.dll" Directory="DLLs" Guid="*"> - <File Name="tcl86t.dll" KeyPath="yes" /> + <Component Id="tcl90.dll" Directory="DLLs" Guid="*"> + <File Name="tcl90.dll" KeyPath="yes" /> </Component> - <Component Id="tk86t.dll" Directory="DLLs" Guid="*"> - <File Name="tk86t.dll" KeyPath="yes" /> + <Component Id="tcl9tk90.dll" Directory="DLLs" Guid="*"> + <File Name="tcl9tk90.dll" KeyPath="yes" /> + </Component> + <Component Id="libtommath.dll" Directory="DLLs" Guid="*"> + <File Name="libtommath.dll" KeyPath="yes" /> </Component> <Component Id="zlib1.dll" Directory="DLLs" Guid="*"> <File Name="zlib1.dll" KeyPath="yes" /> diff --git a/Tools/msi/testrelease.bat b/Tools/msi/testrelease.bat index 02bcca943cf79b..db98f690151196 100644 --- a/Tools/msi/testrelease.bat +++ b/Tools/msi/testrelease.bat @@ -88,9 +88,7 @@ exit /B 0 ) @if not errorlevel 1 ( @echo Testing Tcl/tk - @set TCL_LIBRARY=%~2\Python\tcl\tcl8.6 "%~2\Python\python.exe" -m test -uall -v test_ttk_guionly test_tk test_idle > "%~2\tcltk.txt" 2>&1 - @set TCL_LIBRARY= ) @set EXITCODE=%ERRORLEVEL% diff --git a/Tools/patchcheck/patchcheck.py b/Tools/patchcheck/patchcheck.py index afd010a52544a3..1a5797f74223bd 100755 --- a/Tools/patchcheck/patchcheck.py +++ b/Tools/patchcheck/patchcheck.py @@ -176,12 +176,6 @@ def docs_modified(file_paths): return bool(file_paths) -@status("Misc/ACKS updated", modal=True) -def credit_given(file_paths): - """Check if Misc/ACKS has been changed.""" - return os.path.join('Misc', 'ACKS') in file_paths - - @status("Misc/NEWS.d updated with `blurb`", modal=True) def reported_news(file_paths): """Check if Misc/NEWS.d has been changed.""" @@ -215,8 +209,6 @@ def main(): misc_files = {p for p in file_paths if p.startswith('Misc')} # Docs updated. docs_modified(has_doc_files) - # Misc/ACKS changed. - credit_given(misc_files) # Misc/NEWS changed. reported_news(misc_files) # Regenerated configure, if necessary. diff --git a/Tools/peg_generator/.ruff.toml b/Tools/peg_generator/.ruff.toml new file mode 100644 index 00000000000000..bcf57248713df4 --- /dev/null +++ b/Tools/peg_generator/.ruff.toml @@ -0,0 +1,22 @@ +extend = "../../.ruff.toml" # Inherit the project-wide settings + +extend-exclude = [ + # Generated files: + "Tools/peg_generator/pegen/grammar_parser.py", +] + +[lint] +select = [ + "F", # pyflakes + "I", # isort + "UP", # pyupgrade + "RUF100", # Ban unused `# noqa` comments + "PGH004", # Ban blanket `# noqa` comments (only ignore specific error codes) +] +unfixable = [ + # The autofixes sometimes do the wrong things for these; + # it's better to have to manually look at the code and see how it needs fixing + "F841", # Detects unused variables + "F601", # Detects dictionaries that have duplicate keys + "F602", # Also detects dictionaries that have duplicate keys +] diff --git a/Tools/peg_generator/peg_extension/peg_extension.c b/Tools/peg_generator/peg_extension/peg_extension.c index 1587d53d59472e..2fec5b0512940f 100644 --- a/Tools/peg_generator/peg_extension/peg_extension.c +++ b/Tools/peg_generator/peg_extension/peg_extension.c @@ -8,7 +8,7 @@ _build_return_object(mod_ty module, int mode, PyObject *filename_ob, PyArena *ar PyObject *result = NULL; if (mode == 2) { - result = (PyObject *)_PyAST_Compile(module, filename_ob, NULL, -1, arena); + result = (PyObject *)_PyAST_Compile(module, filename_ob, NULL, -1, arena, NULL); } else if (mode == 1) { result = PyAST_mod2obj(module); } else { @@ -93,7 +93,7 @@ parse_string(PyObject *self, PyObject *args, PyObject *kwds) PyCompilerFlags flags = _PyCompilerFlags_INIT; mod_ty res = _PyPegen_run_parser_from_string(the_string, Py_file_input, filename_ob, - &flags, arena); + &flags, arena, NULL); if (res == NULL) { goto error; } diff --git a/Tools/peg_generator/pegen/__main__.py b/Tools/peg_generator/pegen/__main__.py index 0b0b4b291c2b0e..7a82ad861b4cb5 100755 --- a/Tools/peg_generator/pegen/__main__.py +++ b/Tools/peg_generator/pegen/__main__.py @@ -10,7 +10,6 @@ import time import token import traceback -from typing import Tuple from pegen.grammar import Grammar from pegen.parser import Parser @@ -21,7 +20,7 @@ def generate_c_code( args: argparse.Namespace, -) -> Tuple[Grammar, Parser, Tokenizer, ParserGenerator]: +) -> tuple[Grammar, Parser, Tokenizer, ParserGenerator]: from pegen.build import build_c_parser_and_generator verbose = args.verbose @@ -50,7 +49,7 @@ def generate_c_code( def generate_python_code( args: argparse.Namespace, -) -> Tuple[Grammar, Parser, Tokenizer, ParserGenerator]: +) -> tuple[Grammar, Parser, Tokenizer, ParserGenerator]: from pegen.build import build_python_parser_and_generator verbose = args.verbose @@ -188,7 +187,7 @@ def main() -> None: if __name__ == "__main__": - if sys.version_info < (3, 8): - print("ERROR: using pegen requires at least Python 3.8!", file=sys.stderr) + if sys.version_info < (3, 10): # noqa: UP036 + print("ERROR: using pegen requires at least Python 3.10!", file=sys.stderr) sys.exit(1) main() diff --git a/Tools/peg_generator/pegen/ast_dump.py b/Tools/peg_generator/pegen/ast_dump.py index 07f8799c114f5d..60dc1b04672e2f 100644 --- a/Tools/peg_generator/pegen/ast_dump.py +++ b/Tools/peg_generator/pegen/ast_dump.py @@ -6,7 +6,7 @@ TODO: Remove the above-described hack. """ -from typing import Any, Optional, Tuple +from typing import Any def ast_dump( @@ -14,9 +14,9 @@ def ast_dump( annotate_fields: bool = True, include_attributes: bool = False, *, - indent: Optional[str] = None, + indent: str | None = None, ) -> str: - def _format(node: Any, level: int = 0) -> Tuple[str, bool]: + def _format(node: Any, level: int = 0) -> tuple[str, bool]: if indent is not None: level += 1 prefix = "\n" + indent * level @@ -41,7 +41,7 @@ def _format(node: Any, level: int = 0) -> Tuple[str, bool]: value, simple = _format(value, level) allsimple = allsimple and simple if keywords: - args.append("%s=%s" % (name, value)) + args.append(f"{name}={value}") else: args.append(value) if include_attributes and node._attributes: @@ -54,16 +54,16 @@ def _format(node: Any, level: int = 0) -> Tuple[str, bool]: continue value, simple = _format(value, level) allsimple = allsimple and simple - args.append("%s=%s" % (name, value)) + args.append(f"{name}={value}") if allsimple and len(args) <= 3: - return "%s(%s)" % (node.__class__.__name__, ", ".join(args)), not args - return "%s(%s%s)" % (node.__class__.__name__, prefix, sep.join(args)), False + return "{}({})".format(node.__class__.__name__, ", ".join(args)), not args + return f"{node.__class__.__name__}({prefix}{sep.join(args)})", False elif isinstance(node, list): if not node: return "[]", True - return "[%s%s]" % (prefix, sep.join(_format(x, level)[0] for x in node)), False + return f"[{prefix}{sep.join(_format(x, level)[0] for x in node)}]", False return repr(node), True if all(cls.__name__ != "AST" for cls in node.__class__.__mro__): - raise TypeError("expected AST, got %r" % node.__class__.__name__) + raise TypeError(f"expected AST, got {node.__class__.__name__!r}") return _format(node)[0] diff --git a/Tools/peg_generator/pegen/build.py b/Tools/peg_generator/pegen/build.py index be289c352de585..bc3657067a8da2 100644 --- a/Tools/peg_generator/pegen/build.py +++ b/Tools/peg_generator/pegen/build.py @@ -6,7 +6,7 @@ import sysconfig import tempfile import tokenize -from typing import IO, Any, Dict, List, Optional, Set, Tuple +from typing import IO, Any from pegen.c_generator import CParserGenerator from pegen.grammar import Grammar @@ -18,11 +18,11 @@ MOD_DIR = pathlib.Path(__file__).resolve().parent -TokenDefinitions = Tuple[Dict[int, str], Dict[str, int], Set[str]] +TokenDefinitions = tuple[dict[int, str], dict[str, int], set[str]] Incomplete = Any # TODO: install `types-setuptools` and remove this alias -def get_extra_flags(compiler_flags: str, compiler_py_flags_nodist: str) -> List[str]: +def get_extra_flags(compiler_flags: str, compiler_py_flags_nodist: str) -> list[str]: flags = sysconfig.get_config_var(compiler_flags) py_flags_nodist = sysconfig.get_config_var(compiler_py_flags_nodist) if flags is None or py_flags_nodist is None: @@ -71,11 +71,11 @@ def fixup_build_ext(cmd: Incomplete) -> None: def compile_c_extension( generated_source_path: str, - build_dir: Optional[str] = None, + build_dir: str | None = None, verbose: bool = False, keep_asserts: bool = True, disable_optimization: bool = False, - library_dir: Optional[str] = None, + library_dir: str | None = None, ) -> pathlib.Path: """Compile the generated source for a parser generator into an extension module. @@ -93,11 +93,10 @@ def compile_c_extension( """ import setuptools.command.build_ext import setuptools.logging - - from setuptools import Extension, Distribution - from setuptools.modified import newer_group + from setuptools import Distribution, Extension from setuptools._distutils.ccompiler import new_compiler from setuptools._distutils.sysconfig import customize_compiler + from setuptools.modified import newer_group if verbose: setuptools.logging.set_threshold(logging.DEBUG) @@ -146,10 +145,15 @@ def compile_c_extension( str(MOD_DIR.parent.parent.parent / "Parser" / "lexer"), str(MOD_DIR.parent.parent.parent / "Parser" / "tokenizer"), ] + library_dirs: list[str] = [] if sys.platform == "win32": # HACK: The location of pyconfig.h has moved within our build, and # setuptools hasn't updated for it yet. So add the path manually for now - include_dirs.append(pathlib.Path(sysconfig.get_config_h_filename()).parent) + include_dirs.append(str(pathlib.Path(sysconfig.get_config_h_filename()).parent)) + if sysconfig.is_python_build(): + # HACK: Our output directory for free-threaded builds has moved, and so + # tests running in-tree require our sys.executable directory for libs + library_dirs.append(str(pathlib.Path(sys.executable).parent)) extension = Extension( extension_name, sources=[generated_source_path], @@ -162,6 +166,7 @@ def compile_c_extension( fixup_build_ext(cmd) cmd.build_lib = str(source_file_path.parent) cmd.include_dirs = include_dirs + cmd.library_dirs = library_dirs if build_dir: cmd.build_temp = build_dir cmd.ensure_finalized() @@ -241,7 +246,7 @@ def compile_c_extension( def build_parser( grammar_file: str, verbose_tokenizer: bool = False, verbose_parser: bool = False -) -> Tuple[Grammar, Parser, Tokenizer]: +) -> tuple[Grammar, Parser, Tokenizer]: with open(grammar_file) as file: tokenizer = Tokenizer(tokenize.generate_tokens(file.readline), verbose=verbose_tokenizer) parser = GrammarParser(tokenizer, verbose=verbose_parser) @@ -292,7 +297,7 @@ def build_c_generator( keep_asserts_in_extension: bool = True, skip_actions: bool = False, ) -> ParserGenerator: - with open(tokens_file, "r") as tok_file: + with open(tokens_file) as tok_file: all_tokens, exact_tok, non_exact_tok = generate_token_definitions(tok_file) with open(output_file, "w") as file: gen: ParserGenerator = CParserGenerator( @@ -333,7 +338,7 @@ def build_c_parser_and_generator( verbose_c_extension: bool = False, keep_asserts_in_extension: bool = True, skip_actions: bool = False, -) -> Tuple[Grammar, Parser, Tokenizer, ParserGenerator]: +) -> tuple[Grammar, Parser, Tokenizer, ParserGenerator]: """Generate rules, C parser, tokenizer, parser generator for a given grammar Args: @@ -373,7 +378,7 @@ def build_python_parser_and_generator( verbose_tokenizer: bool = False, verbose_parser: bool = False, skip_actions: bool = False, -) -> Tuple[Grammar, Parser, Tokenizer, ParserGenerator]: +) -> tuple[Grammar, Parser, Tokenizer, ParserGenerator]: """Generate rules, python parser, tokenizer, parser generator for a given grammar Args: diff --git a/Tools/peg_generator/pegen/c_generator.py b/Tools/peg_generator/pegen/c_generator.py index 04f66eec1a0154..d9236dfb22835b 100644 --- a/Tools/peg_generator/pegen/c_generator.py +++ b/Tools/peg_generator/pegen/c_generator.py @@ -1,9 +1,10 @@ import ast import os.path import re +from collections.abc import Callable from dataclasses import dataclass, field from enum import Enum -from typing import IO, Any, Callable, Dict, List, Optional, Set, Text, Tuple +from typing import IO, Any from pegen import grammar from pegen.grammar import ( @@ -30,6 +31,7 @@ EXTENSION_PREFIX = """\ #include "pegen.h" +#include "pycore_ceval.h" #if defined(Py_DEBUG) && defined(Py_BUILD_CORE) # define D(x) if (p->debug) { x; } @@ -86,13 +88,13 @@ class NodeTypes(Enum): @dataclass class FunctionCall: function: str - arguments: List[Any] = field(default_factory=list) - assigned_variable: Optional[str] = None - assigned_variable_type: Optional[str] = None - return_type: Optional[str] = None - nodetype: Optional[NodeTypes] = None + arguments: list[Any] = field(default_factory=list) + assigned_variable: str | None = None + assigned_variable_type: str | None = None + return_type: str | None = None + nodetype: NodeTypes | None = None force_true: bool = False - comment: Optional[str] = None + comment: str | None = None def __str__(self) -> str: parts = [] @@ -124,14 +126,14 @@ class CCallMakerVisitor(GrammarVisitor): def __init__( self, parser_generator: ParserGenerator, - exact_tokens: Dict[str, int], - non_exact_tokens: Set[str], + exact_tokens: dict[str, int], + non_exact_tokens: set[str], ): self.gen = parser_generator self.exact_tokens = exact_tokens self.non_exact_tokens = non_exact_tokens - self.cache: Dict[str, str] = {} - self.cleanup_statements: List[str] = [] + self.cache: dict[str, str] = {} + self.cleanup_statements: list[str] = [] def keyword_helper(self, keyword: str) -> FunctionCall: return FunctionCall( @@ -167,7 +169,7 @@ def visit_NameLeaf(self, node: NameLeaf) -> FunctionCall: ) return FunctionCall( assigned_variable=f"{name.lower()}_var", - function=f"_PyPegen_expect_token", + function="_PyPegen_expect_token", arguments=["p", name], nodetype=NodeTypes.GENERIC_TOKEN, return_type="Token *", @@ -199,7 +201,7 @@ def visit_StringLeaf(self, node: StringLeaf) -> FunctionCall: type = self.exact_tokens[val] return FunctionCall( assigned_variable="_literal", - function=f"_PyPegen_expect_token", + function="_PyPegen_expect_token", arguments=["p", type], nodetype=NodeTypes.GENERIC_TOKEN, return_type="Token *", @@ -271,7 +273,7 @@ def visit_Forced(self, node: Forced) -> FunctionCall: type = self.exact_tokens[val] return FunctionCall( assigned_variable="_literal", - function=f"_PyPegen_expect_forced_token", + function="_PyPegen_expect_forced_token", arguments=["p", type, f'"{val}"'], nodetype=NodeTypes.GENERIC_TOKEN, return_type="Token *", @@ -283,7 +285,7 @@ def visit_Forced(self, node: Forced) -> FunctionCall: call.comment = None return FunctionCall( assigned_variable="_literal", - function=f"_PyPegen_expect_forced_result", + function="_PyPegen_expect_forced_result", arguments=["p", str(call), f'"{node.node.rhs!s}"'], return_type="void *", comment=f"forced_token=({node.node.rhs!s})", @@ -306,7 +308,7 @@ def _generate_artificial_rule_call( node: Any, prefix: str, rule_generation_func: Callable[[], str], - return_type: Optional[str] = None, + return_type: str | None = None, ) -> FunctionCall: node_str = f"{node}" key = f"{prefix}_{node_str}" @@ -377,10 +379,10 @@ class CParserGenerator(ParserGenerator, GrammarVisitor): def __init__( self, grammar: grammar.Grammar, - tokens: Dict[int, str], - exact_tokens: Dict[str, int], - non_exact_tokens: Set[str], - file: Optional[IO[Text]], + tokens: dict[int, str], + exact_tokens: dict[str, int], + non_exact_tokens: set[str], + file: IO[str] | None, debug: bool = False, skip_actions: bool = False, ): @@ -391,7 +393,7 @@ def __init__( self._varname_counter = 0 self.debug = debug self.skip_actions = skip_actions - self.cleanup_statements: List[str] = [] + self.cleanup_statements: list[str] = [] def add_level(self) -> None: self.print("if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) {") @@ -427,12 +429,12 @@ def call_with_errorcheck_goto(self, call_text: str, goto_target: str) -> None: self.print(f"if ({error_var}) {{") with self.indent(): self.print(f"goto {goto_target};") - self.print(f"}}") + self.print("}") def out_of_memory_return( self, expr: str, - cleanup_code: Optional[str] = None, + cleanup_code: str | None = None, ) -> None: self.print(f"if ({expr}) {{") with self.indent(): @@ -441,14 +443,14 @@ def out_of_memory_return( self.print("p->error_indicator = 1;") self.print("PyErr_NoMemory();") self.add_return("NULL") - self.print(f"}}") + self.print("}") def out_of_memory_goto(self, expr: str, goto_target: str) -> None: self.print(f"if ({expr}) {{") with self.indent(): self.print("PyErr_NoMemory();") self.print(f"goto {goto_target};") - self.print(f"}}") + self.print("}") def generate(self, filename: str) -> None: self.collect_rules() @@ -491,8 +493,8 @@ def generate(self, filename: str) -> None: if trailer: self.print(trailer.rstrip("\n") % dict(mode=mode, modulename=modulename)) - def _group_keywords_by_length(self) -> Dict[int, List[Tuple[str, int]]]: - groups: Dict[int, List[Tuple[str, int]]] = {} + def _group_keywords_by_length(self) -> dict[int, list[tuple[str, int]]]: + groups: dict[int, list[tuple[str, int]]] = {} for keyword_str, keyword_type in self.keywords.items(): length = len(keyword_str) if length in groups: @@ -584,17 +586,17 @@ def _set_up_rule_memoization(self, node: Rule, result_type: str) -> None: self.print("if (_raw == NULL || p->mark <= _resmark)") with self.indent(): self.print("break;") - self.print(f"_resmark = p->mark;") + self.print("_resmark = p->mark;") self.print("_res = _raw;") self.print("}") - self.print(f"p->mark = _resmark;") + self.print("p->mark = _resmark;") self.add_return("_res") self.print("}") self.print(f"static {result_type}") self.print(f"{node.name}_raw(Parser *p)") def _should_memoize(self, node: Rule) -> bool: - return node.memo and not node.left_recursive + return "memo" in node.flags and not node.left_recursive def _handle_default_rule_body(self, node: Rule, rhs: Rhs, result_type: str) -> None: memoize = self._should_memoize(node) @@ -643,7 +645,7 @@ def _handle_loop_rule_body(self, node: Rule, rhs: Rhs) -> None: if memoize: self.print("int _start_mark = p->mark;") self.print("void **_children = PyMem_Malloc(sizeof(void *));") - self.out_of_memory_return(f"!_children") + self.out_of_memory_return("!_children") self.print("Py_ssize_t _children_capacity = 1;") self.print("Py_ssize_t _n = 0;") if any(alt.action and "EXTRA" in alt.action for alt in rhs.alts): @@ -661,7 +663,7 @@ def _handle_loop_rule_body(self, node: Rule, rhs: Rhs) -> None: self.add_return("NULL") self.print("}") self.print("asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);") - self.out_of_memory_return(f"!_seq", cleanup_code="PyMem_Free(_children);") + self.out_of_memory_return("!_seq", cleanup_code="PyMem_Free(_children);") self.print("for (Py_ssize_t i = 0; i < _n; i++) asdl_seq_SET_UNTYPED(_seq, i, _children[i]);") self.print("PyMem_Free(_children);") if memoize and node.name: @@ -715,7 +717,7 @@ def visit_NamedItem(self, node: NamedItem) -> None: self.print(call) def visit_Rhs( - self, node: Rhs, is_loop: bool, is_gather: bool, rulename: Optional[str] + self, node: Rhs, is_loop: bool, is_gather: bool, rulename: str | None ) -> None: if is_loop: assert len(node.alts) == 1 @@ -734,10 +736,10 @@ def join_conditions(self, keyword: str, node: Any) -> None: self.visit(item) self.print(")") - def emit_action(self, node: Alt, cleanup_code: Optional[str] = None) -> None: + def emit_action(self, node: Alt, cleanup_code: str | None = None) -> None: self.print(f"_res = {node.action};") - self.print("if (_res == NULL && PyErr_Occurred()) {") + self.print("if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) {") with self.indent(): self.print("p->error_indicator = 1;") if cleanup_code: @@ -776,7 +778,7 @@ def emit_default_action(self, is_gather: bool, node: Alt) -> None: def emit_dummy_action(self) -> None: self.print("_res = _PyPegen_dummy_name(p);") - def handle_alt_normal(self, node: Alt, is_gather: bool, rulename: Optional[str]) -> None: + def handle_alt_normal(self, node: Alt, is_gather: bool, rulename: str | None) -> None: self.join_conditions(keyword="if", node=node) self.print("{") # We have parsed successfully all the conditions for the option. @@ -796,10 +798,10 @@ def handle_alt_normal(self, node: Alt, is_gather: bool, rulename: Optional[str]) self.emit_default_action(is_gather, node) # As the current option has parsed correctly, do not continue with the rest. - self.print(f"goto done;") + self.print("goto done;") self.print("}") - def handle_alt_loop(self, node: Alt, is_gather: bool, rulename: Optional[str]) -> None: + def handle_alt_loop(self, node: Alt, is_gather: bool, rulename: str | None) -> None: # Condition of the main body of the alternative self.join_conditions(keyword="while", node=node) self.print("{") @@ -823,7 +825,7 @@ def handle_alt_loop(self, node: Alt, is_gather: bool, rulename: Optional[str]) - self.print( "void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));" ) - self.out_of_memory_return(f"!_new_children", cleanup_code="PyMem_Free(_children);") + self.out_of_memory_return("!_new_children", cleanup_code="PyMem_Free(_children);") self.print("_children = _new_children;") self.print("}") self.print("_children[_n++] = _res;") @@ -831,7 +833,7 @@ def handle_alt_loop(self, node: Alt, is_gather: bool, rulename: Optional[str]) - self.print("}") def visit_Alt( - self, node: Alt, is_loop: bool, is_gather: bool, rulename: Optional[str] + self, node: Alt, is_loop: bool, is_gather: bool, rulename: str | None ) -> None: if len(node.items) == 1 and str(node.items[0]).startswith("invalid_"): self.print(f"if (p->call_invalid_rules) {{ // {node}") @@ -875,7 +877,7 @@ def visit_Alt( self.print("}") self.print("}") - def collect_vars(self, node: Alt) -> Dict[Optional[str], Optional[str]]: + def collect_vars(self, node: Alt) -> dict[str | None, str | None]: types = {} with self.local_variable_context(): for item in node.items: @@ -883,7 +885,7 @@ def collect_vars(self, node: Alt) -> Dict[Optional[str], Optional[str]]: types[name] = type return types - def add_var(self, node: NamedItem) -> Tuple[Optional[str], Optional[str]]: + def add_var(self, node: NamedItem) -> tuple[str | None, str | None]: call = self.callmakervisitor.generate_call(node.item) name = node.name if node.name else call.assigned_variable if name is not None: diff --git a/Tools/peg_generator/pegen/first_sets.py b/Tools/peg_generator/pegen/first_sets.py index 6d794ffa4bfa8b..f6b83c0ca31e3f 100755 --- a/Tools/peg_generator/pegen/first_sets.py +++ b/Tools/peg_generator/pegen/first_sets.py @@ -3,7 +3,6 @@ import argparse import pprint import sys -from typing import Dict, Set from pegen.build import build_parser from pegen.grammar import ( @@ -33,20 +32,20 @@ class FirstSetCalculator(GrammarVisitor): - def __init__(self, rules: Dict[str, Rule]) -> None: + def __init__(self, rules: dict[str, Rule]) -> None: self.rules = rules self.nullables = compute_nullables(rules) - self.first_sets: Dict[str, Set[str]] = dict() - self.in_process: Set[str] = set() + self.first_sets: dict[str, set[str]] = dict() + self.in_process: set[str] = set() - def calculate(self) -> Dict[str, Set[str]]: + def calculate(self) -> dict[str, set[str]]: for name, rule in self.rules.items(): self.visit(rule) return self.first_sets - def visit_Alt(self, item: Alt) -> Set[str]: - result: Set[str] = set() - to_remove: Set[str] = set() + def visit_Alt(self, item: Alt) -> set[str]: + result: set[str] = set() + to_remove: set[str] = set() for other in item.items: new_terminals = self.visit(other) if isinstance(other.item, NegativeLookahead): @@ -71,34 +70,34 @@ def visit_Alt(self, item: Alt) -> Set[str]: return result - def visit_Cut(self, item: Cut) -> Set[str]: + def visit_Cut(self, item: Cut) -> set[str]: return set() - def visit_Group(self, item: Group) -> Set[str]: + def visit_Group(self, item: Group) -> set[str]: return self.visit(item.rhs) - def visit_PositiveLookahead(self, item: Lookahead) -> Set[str]: + def visit_PositiveLookahead(self, item: Lookahead) -> set[str]: return self.visit(item.node) - def visit_NegativeLookahead(self, item: NegativeLookahead) -> Set[str]: + def visit_NegativeLookahead(self, item: NegativeLookahead) -> set[str]: return self.visit(item.node) - def visit_NamedItem(self, item: NamedItem) -> Set[str]: + def visit_NamedItem(self, item: NamedItem) -> set[str]: return self.visit(item.item) - def visit_Opt(self, item: Opt) -> Set[str]: + def visit_Opt(self, item: Opt) -> set[str]: return self.visit(item.node) - def visit_Gather(self, item: Gather) -> Set[str]: + def visit_Gather(self, item: Gather) -> set[str]: return self.visit(item.node) - def visit_Repeat0(self, item: Repeat0) -> Set[str]: + def visit_Repeat0(self, item: Repeat0) -> set[str]: return self.visit(item.node) - def visit_Repeat1(self, item: Repeat1) -> Set[str]: + def visit_Repeat1(self, item: Repeat1) -> set[str]: return self.visit(item.node) - def visit_NameLeaf(self, item: NameLeaf) -> Set[str]: + def visit_NameLeaf(self, item: NameLeaf) -> set[str]: if item.value not in self.rules: return {item.value} @@ -110,16 +109,16 @@ def visit_NameLeaf(self, item: NameLeaf) -> Set[str]: return self.first_sets[item.value] - def visit_StringLeaf(self, item: StringLeaf) -> Set[str]: + def visit_StringLeaf(self, item: StringLeaf) -> set[str]: return {item.value} - def visit_Rhs(self, item: Rhs) -> Set[str]: - result: Set[str] = set() + def visit_Rhs(self, item: Rhs) -> set[str]: + result: set[str] = set() for alt in item.alts: result |= self.visit(alt) return result - def visit_Rule(self, item: Rule) -> Set[str]: + def visit_Rule(self, item: Rule) -> set[str]: if item.name in self.in_process: return set() elif item.name not in self.first_sets: @@ -138,7 +137,7 @@ def main() -> None: try: grammar, parser, tokenizer = build_parser(args.grammar_file) except Exception as err: - print("ERROR: Failed to parse grammar file", file=sys.stderr) + print("ERROR: Failed to parse grammar file", err, file=sys.stderr) sys.exit(1) firs_sets = FirstSetCalculator(grammar.rules).calculate() diff --git a/Tools/peg_generator/pegen/grammar.py b/Tools/peg_generator/pegen/grammar.py index 1ee9d100b61f49..d3c2eca6615a9f 100644 --- a/Tools/peg_generator/pegen/grammar.py +++ b/Tools/peg_generator/pegen/grammar.py @@ -1,15 +1,7 @@ from __future__ import annotations -from typing import ( - AbstractSet, - Any, - Iterable, - Iterator, - List, - Optional, - Tuple, - Union, -) +from collections.abc import Iterable, Iterator, Set +from typing import Any class GrammarError(Exception): @@ -34,7 +26,7 @@ def generic_visit(self, node: Iterable[Any], *args: Any, **kwargs: Any) -> Any: class Grammar: - def __init__(self, rules: Iterable[Rule], metas: Iterable[Tuple[str, Optional[str]]]): + def __init__(self, rules: Iterable[Rule], metas: Iterable[tuple[str, str | None]]): # Check if there are repeated rules in "rules" all_rules = {} for rule in rules: @@ -66,11 +58,11 @@ def __iter__(self) -> Iterator[Rule]: class Rule: - def __init__(self, name: str, type: Optional[str], rhs: Rhs, memo: Optional[object] = None): + def __init__(self, name: str, type: str | None, rhs: Rhs, flags: frozenset[str] | None = None): self.name = name self.type = type self.rhs = rhs - self.memo = bool(memo) + self.flags = flags or frozenset() self.left_recursive = False self.leader = False @@ -141,9 +133,8 @@ def __repr__(self) -> str: class Rhs: - def __init__(self, alts: List[Alt]): + def __init__(self, alts: list[Alt]): self.alts = alts - self.memo: Optional[Tuple[Optional[str], str]] = None def __str__(self) -> str: return " | ".join(str(alt) for alt in self.alts) @@ -151,7 +142,7 @@ def __str__(self) -> str: def __repr__(self) -> str: return f"Rhs({self.alts!r})" - def __iter__(self) -> Iterator[List[Alt]]: + def __iter__(self) -> Iterator[list[Alt]]: yield self.alts @property @@ -165,7 +156,7 @@ def can_be_inlined(self) -> bool: class Alt: - def __init__(self, items: List[NamedItem], *, icut: int = -1, action: Optional[str] = None): + def __init__(self, items: list[NamedItem], *, icut: int = -1, action: str | None = None): self.items = items self.icut = icut self.action = action @@ -185,12 +176,12 @@ def __repr__(self) -> str: args.append(f"action={self.action!r}") return f"Alt({', '.join(args)})" - def __iter__(self) -> Iterator[List[NamedItem]]: + def __iter__(self) -> Iterator[list[NamedItem]]: yield self.items class NamedItem: - def __init__(self, name: Optional[str], item: Item, type: Optional[str] = None): + def __init__(self, name: str | None, item: Item, type: str | None = None): self.name = name self.item = item self.type = type @@ -271,7 +262,6 @@ class Repeat: def __init__(self, node: Plain): self.node = node - self.memo: Optional[Tuple[Optional[str], str]] = None def __iter__(self) -> Iterator[Plain]: yield self.node @@ -334,12 +324,12 @@ def __init__(self) -> None: pass def __repr__(self) -> str: - return f"Cut()" + return "Cut()" def __str__(self) -> str: - return f"~" + return "~" - def __iter__(self) -> Iterator[Tuple[str, str]]: + def __iter__(self) -> Iterator[tuple[str, str]]: yield from () def __eq__(self, other: object) -> bool: @@ -347,15 +337,15 @@ def __eq__(self, other: object) -> bool: return NotImplemented return True - def initial_names(self) -> AbstractSet[str]: + def initial_names(self) -> Set[str]: return set() -Plain = Union[Leaf, Group] -Item = Union[Plain, Opt, Repeat, Forced, Lookahead, Rhs, Cut] -RuleName = Tuple[str, Optional[str]] -MetaTuple = Tuple[str, Optional[str]] -MetaList = List[MetaTuple] -RuleList = List[Rule] -NamedItemList = List[NamedItem] -LookaheadOrCut = Union[Lookahead, Cut] +Plain = Leaf | Group +Item = Plain | Opt | Repeat | Forced | Lookahead | Rhs | Cut +RuleName = tuple[str, str | None] +MetaTuple = tuple[str, str | None] +MetaList = list[MetaTuple] +RuleList = list[Rule] +NamedItemList = list[NamedItem] +LookaheadOrCut = Lookahead | Cut diff --git a/Tools/peg_generator/pegen/grammar_parser.py b/Tools/peg_generator/pegen/grammar_parser.py index 2e3a607f7209b0..4fa2739270773f 100644 --- a/Tools/peg_generator/pegen/grammar_parser.py +++ b/Tools/peg_generator/pegen/grammar_parser.py @@ -147,12 +147,12 @@ def rules(self) -> Optional[RuleList]: @memoize def rule(self) -> Optional[Rule]: - # rule: rulename memoflag? ":" alts NEWLINE INDENT more_alts DEDENT | rulename memoflag? ":" NEWLINE INDENT more_alts DEDENT | rulename memoflag? ":" alts NEWLINE + # rule: rulename flags? ":" alts NEWLINE INDENT more_alts DEDENT | rulename flags? ":" NEWLINE INDENT more_alts DEDENT | rulename flags? ":" alts NEWLINE mark = self._mark() if ( (rulename := self.rulename()) and - (opt := self.memoflag(),) + (flags := self.flags(),) and (literal := self.expect(":")) and @@ -166,12 +166,12 @@ def rule(self) -> Optional[Rule]: and (_dedent := self.expect('DEDENT')) ): - return Rule ( rulename [0] , rulename [1] , Rhs ( alts . alts + more_alts . alts ) , memo = opt ) + return Rule ( rulename [0] , rulename [1] , Rhs ( alts . alts + more_alts . alts ) , flags = flags ) self._reset(mark) if ( (rulename := self.rulename()) and - (opt := self.memoflag(),) + (flags := self.flags(),) and (literal := self.expect(":")) and @@ -183,12 +183,12 @@ def rule(self) -> Optional[Rule]: and (_dedent := self.expect('DEDENT')) ): - return Rule ( rulename [0] , rulename [1] , more_alts , memo = opt ) + return Rule ( rulename [0] , rulename [1] , more_alts , flags = flags ) self._reset(mark) if ( (rulename := self.rulename()) and - (opt := self.memoflag(),) + (flags := self.flags(),) and (literal := self.expect(":")) and @@ -196,7 +196,7 @@ def rule(self) -> Optional[Rule]: and (_newline := self.expect('NEWLINE')) ): - return Rule ( rulename [0] , rulename [1] , alts , memo = opt ) + return Rule ( rulename [0] , rulename [1] , alts , flags = flags ) self._reset(mark) return None @@ -219,17 +219,28 @@ def rulename(self) -> Optional[RuleName]: return None @memoize - def memoflag(self) -> Optional[str]: - # memoflag: '(' "memo" ')' + def flags(self) -> Optional[frozenset [str]]: + # flags: '(' ','.flag+ ')' mark = self._mark() if ( (literal := self.expect('(')) and - (literal_1 := self.expect("memo")) + (a := self._gather_2()) and - (literal_2 := self.expect(')')) + (literal_1 := self.expect(')')) + ): + return frozenset ( a ) + self._reset(mark) + return None + + @memoize + def flag(self) -> Optional[str]: + # flag: NAME + mark = self._mark() + if ( + (name := self.name()) ): - return "memo" + return name . string self._reset(mark) return None @@ -661,8 +672,38 @@ def target_atom(self) -> Optional[str]: self._reset(mark) return None + @memoize + def _loop0_1(self) -> Optional[Any]: + # _loop0_1: ',' flag + mark = self._mark() + children = [] + while ( + (literal := self.expect(',')) + and + (elem := self.flag()) + ): + children.append(elem) + mark = self._mark() + self._reset(mark) + return children + + @memoize + def _gather_2(self) -> Optional[Any]: + # _gather_2: flag _loop0_1 + mark = self._mark() + if ( + (elem := self.flag()) + is not None + and + (seq := self._loop0_1()) + is not None + ): + return [elem] + seq + self._reset(mark) + return None + KEYWORDS = () - SOFT_KEYWORDS = ('memo',) + SOFT_KEYWORDS = () if __name__ == '__main__': diff --git a/Tools/peg_generator/pegen/grammar_visualizer.py b/Tools/peg_generator/pegen/grammar_visualizer.py index 11f784f45b66b8..eadb6987389b88 100644 --- a/Tools/peg_generator/pegen/grammar_visualizer.py +++ b/Tools/peg_generator/pegen/grammar_visualizer.py @@ -1,6 +1,7 @@ import argparse import sys -from typing import Any, Callable, Iterator +from collections.abc import Callable, Iterator +from typing import Any from pegen.build import build_parser from pegen.grammar import Grammar, Rule @@ -52,7 +53,7 @@ def main() -> None: try: grammar, parser, tokenizer = build_parser(args.filename) except Exception as err: - print("ERROR: Failed to parse grammar file", file=sys.stderr) + print("ERROR: Failed to parse grammar file", err, file=sys.stderr) sys.exit(1) visitor = ASTGrammarPrinter() diff --git a/Tools/peg_generator/pegen/metagrammar.gram b/Tools/peg_generator/pegen/metagrammar.gram index f484c4781823bc..cae91ab9c4165b 100644 --- a/Tools/peg_generator/pegen/metagrammar.gram +++ b/Tools/peg_generator/pegen/metagrammar.gram @@ -50,19 +50,21 @@ rules[RuleList]: | rule { [rule] } rule[Rule]: - | rulename memoflag? ":" alts NEWLINE INDENT more_alts DEDENT { - Rule(rulename[0], rulename[1], Rhs(alts.alts + more_alts.alts), memo=opt) } - | rulename memoflag? ":" NEWLINE INDENT more_alts DEDENT { - Rule(rulename[0], rulename[1], more_alts, memo=opt) } - | rulename memoflag? ":" alts NEWLINE { Rule(rulename[0], rulename[1], alts, memo=opt) } + | rulename flags=flags? ":" alts NEWLINE INDENT more_alts DEDENT { + Rule(rulename[0], rulename[1], Rhs(alts.alts + more_alts.alts), flags=flags) } + | rulename flags=flags? ":" NEWLINE INDENT more_alts DEDENT { + Rule(rulename[0], rulename[1], more_alts, flags=flags) } + | rulename flags=flags? ":" alts NEWLINE { Rule(rulename[0], rulename[1], alts, flags=flags) } rulename[RuleName]: | NAME annotation { (name.string, annotation) } | NAME { (name.string, None) } -# In the future this may return something more complicated -memoflag[str]: - | '(' "memo" ')' { "memo" } +flags[frozenset[str]]: + | '(' a=','.flag+ ')' { frozenset(a) } + +flag[str]: + | NAME { name.string } alts[Rhs]: | alt "|" alts { Rhs([alt] + alts.alts)} diff --git a/Tools/peg_generator/pegen/parser.py b/Tools/peg_generator/pegen/parser.py index a987d30a9d6438..806003c8350c03 100644 --- a/Tools/peg_generator/pegen/parser.py +++ b/Tools/peg_generator/pegen/parser.py @@ -5,7 +5,8 @@ import tokenize import traceback from abc import abstractmethod -from typing import Any, Callable, ClassVar, Dict, Optional, Tuple, Type, TypeVar, cast +from collections.abc import Callable +from typing import Any, ClassVar, TypeVar, cast from pegen.tokenizer import Mark, Tokenizer, exact_token_types @@ -74,12 +75,12 @@ def memoize_wrapper(self: "Parser", *args: object) -> Any: def memoize_left_rec( - method: Callable[["Parser"], Optional[T]] -) -> Callable[["Parser"], Optional[T]]: + method: Callable[["Parser"], T | None] +) -> Callable[["Parser"], T | None]: """Memoize a left-recursive symbol method.""" method_name = method.__name__ - def memoize_left_rec_wrapper(self: "Parser") -> Optional[T]: + def memoize_left_rec_wrapper(self: "Parser") -> T | None: mark = self._mark() key = mark, method_name, () # Fast path: cache hit, and not verbose. @@ -160,15 +161,15 @@ def memoize_left_rec_wrapper(self: "Parser") -> Optional[T]: class Parser: """Parsing base class.""" - KEYWORDS: ClassVar[Tuple[str, ...]] + KEYWORDS: ClassVar[tuple[str, ...]] - SOFT_KEYWORDS: ClassVar[Tuple[str, ...]] + SOFT_KEYWORDS: ClassVar[tuple[str, ...]] def __init__(self, tokenizer: Tokenizer, *, verbose: bool = False): self._tokenizer = tokenizer self._verbose = verbose self._level = 0 - self._cache: Dict[Tuple[Mark, str, Tuple[Any, ...]], Tuple[Any, Mark]] = {} + self._cache: dict[tuple[Mark, str, tuple[Any, ...]], tuple[Any, Mark]] = {} # Integer tracking whether we are in a left recursive rule or not. Can be useful # for error reporting. self.in_recursive_rule = 0 @@ -185,28 +186,28 @@ def showpeek(self) -> str: return f"{tok.start[0]}.{tok.start[1]}: {token.tok_name[tok.type]}:{tok.string!r}" @memoize - def name(self) -> Optional[tokenize.TokenInfo]: + def name(self) -> tokenize.TokenInfo | None: tok = self._tokenizer.peek() if tok.type == token.NAME and tok.string not in self.KEYWORDS: return self._tokenizer.getnext() return None @memoize - def number(self) -> Optional[tokenize.TokenInfo]: + def number(self) -> tokenize.TokenInfo | None: tok = self._tokenizer.peek() if tok.type == token.NUMBER: return self._tokenizer.getnext() return None @memoize - def string(self) -> Optional[tokenize.TokenInfo]: + def string(self) -> tokenize.TokenInfo | None: tok = self._tokenizer.peek() if tok.type == token.STRING: return self._tokenizer.getnext() return None @memoize - def fstring_start(self) -> Optional[tokenize.TokenInfo]: + def fstring_start(self) -> tokenize.TokenInfo | None: FSTRING_START = getattr(token, "FSTRING_START", None) if not FSTRING_START: return None @@ -216,7 +217,7 @@ def fstring_start(self) -> Optional[tokenize.TokenInfo]: return None @memoize - def fstring_middle(self) -> Optional[tokenize.TokenInfo]: + def fstring_middle(self) -> tokenize.TokenInfo | None: FSTRING_MIDDLE = getattr(token, "FSTRING_MIDDLE", None) if not FSTRING_MIDDLE: return None @@ -226,7 +227,7 @@ def fstring_middle(self) -> Optional[tokenize.TokenInfo]: return None @memoize - def fstring_end(self) -> Optional[tokenize.TokenInfo]: + def fstring_end(self) -> tokenize.TokenInfo | None: FSTRING_END = getattr(token, "FSTRING_END", None) if not FSTRING_END: return None @@ -236,28 +237,28 @@ def fstring_end(self) -> Optional[tokenize.TokenInfo]: return None @memoize - def op(self) -> Optional[tokenize.TokenInfo]: + def op(self) -> tokenize.TokenInfo | None: tok = self._tokenizer.peek() if tok.type == token.OP: return self._tokenizer.getnext() return None @memoize - def type_comment(self) -> Optional[tokenize.TokenInfo]: + def type_comment(self) -> tokenize.TokenInfo | None: tok = self._tokenizer.peek() if tok.type == token.TYPE_COMMENT: return self._tokenizer.getnext() return None @memoize - def soft_keyword(self) -> Optional[tokenize.TokenInfo]: + def soft_keyword(self) -> tokenize.TokenInfo | None: tok = self._tokenizer.peek() if tok.type == token.NAME and tok.string in self.SOFT_KEYWORDS: return self._tokenizer.getnext() return None @memoize - def expect(self, type: str) -> Optional[tokenize.TokenInfo]: + def expect(self, type: str) -> tokenize.TokenInfo | None: tok = self._tokenizer.peek() if tok.string == type: return self._tokenizer.getnext() @@ -271,7 +272,7 @@ def expect(self, type: str) -> Optional[tokenize.TokenInfo]: return self._tokenizer.getnext() return None - def expect_forced(self, res: Any, expectation: str) -> Optional[tokenize.TokenInfo]: + def expect_forced(self, res: Any, expectation: str) -> tokenize.TokenInfo | None: if res is None: raise self.make_syntax_error(f"expected {expectation}") return res @@ -293,7 +294,7 @@ def make_syntax_error(self, message: str, filename: str = "<unknown>") -> Syntax return SyntaxError(message, (filename, tok.start[0], 1 + tok.start[1], tok.line)) -def simple_parser_main(parser_class: Type[Parser]) -> None: +def simple_parser_main(parser_class: type[Parser]) -> None: argparser = argparse.ArgumentParser() argparser.add_argument( "-v", @@ -330,7 +331,7 @@ def simple_parser_main(parser_class: Type[Parser]) -> None: endpos = 0 else: endpos = file.tell() - except IOError: + except OSError: endpos = 0 finally: if file is not sys.stdin: diff --git a/Tools/peg_generator/pegen/parser_generator.py b/Tools/peg_generator/pegen/parser_generator.py index 7dd56f98a652cc..81314b0cc073f9 100644 --- a/Tools/peg_generator/pegen/parser_generator.py +++ b/Tools/peg_generator/pegen/parser_generator.py @@ -1,22 +1,10 @@ -import sys import ast import contextlib import re +import sys from abc import abstractmethod -from typing import ( - IO, - AbstractSet, - Any, - Dict, - Iterable, - Iterator, - List, - Optional, - Set, - Text, - Tuple, - Union, -) +from collections.abc import Iterable, Iterator, Set +from typing import IO, Any from pegen import sccutils from pegen.grammar import ( @@ -44,8 +32,7 @@ class RuleCollectorVisitor(GrammarVisitor): """Visitor that invokes a provided callmaker visitor with just the NamedItem nodes""" - def __init__(self, rules: Dict[str, Rule], callmakervisitor: GrammarVisitor) -> None: - self.rulses = rules + def __init__(self, callmakervisitor: GrammarVisitor) -> None: self.callmaker = callmakervisitor def visit_Rule(self, rule: Rule) -> None: @@ -58,7 +45,7 @@ def visit_NamedItem(self, item: NamedItem) -> None: class KeywordCollectorVisitor(GrammarVisitor): """Visitor that collects all the keywords and soft keywords in the Grammar""" - def __init__(self, gen: "ParserGenerator", keywords: Dict[str, int], soft_keywords: Set[str]): + def __init__(self, gen: "ParserGenerator", keywords: dict[str, int], soft_keywords: set[str]): self.generator = gen self.keywords = keywords self.soft_keywords = soft_keywords @@ -73,7 +60,7 @@ def visit_StringLeaf(self, node: StringLeaf) -> None: class RuleCheckingVisitor(GrammarVisitor): - def __init__(self, rules: Dict[str, Rule], tokens: Set[str]): + def __init__(self, rules: dict[str, Rule], tokens: set[str]): self.rules = rules self.tokens = tokens # If python < 3.12 add the virtual fstring tokens @@ -100,11 +87,11 @@ def visit_NamedItem(self, node: NamedItem) -> None: class ParserGenerator: callmakervisitor: GrammarVisitor - def __init__(self, grammar: Grammar, tokens: Set[str], file: Optional[IO[Text]]): + def __init__(self, grammar: Grammar, tokens: set[str], file: IO[str] | None): self.grammar = grammar self.tokens = tokens - self.keywords: Dict[str, int] = {} - self.soft_keywords: Set[str] = set() + self.keywords: dict[str, int] = {} + self.soft_keywords: set[str] = set() self.rules = grammar.rules self.validate_rule_names() if "trailer" not in grammar.metas and "start" not in self.rules: @@ -117,8 +104,8 @@ def __init__(self, grammar: Grammar, tokens: Set[str], file: Optional[IO[Text]]) self.first_graph, self.first_sccs = compute_left_recursives(self.rules) self.counter = 0 # For name_rule()/name_loop() self.keyword_counter = 499 # For keyword_type() - self.all_rules: Dict[str, Rule] = self.rules.copy() # Rules + temporal rules - self._local_variable_stack: List[List[str]] = [] + self.all_rules: dict[str, Rule] = self.rules.copy() # Rules + temporal rules + self._local_variable_stack: list[list[str]] = [] def validate_rule_names(self) -> None: for rule in self.rules: @@ -132,7 +119,7 @@ def local_variable_context(self) -> Iterator[None]: self._local_variable_stack.pop() @property - def local_variable_names(self) -> List[str]: + def local_variable_names(self) -> list[str]: return self._local_variable_stack[-1] @abstractmethod @@ -163,8 +150,8 @@ def collect_rules(self) -> None: for rule in self.all_rules.values(): keyword_collector.visit(rule) - rule_collector = RuleCollectorVisitor(self.rules, self.callmakervisitor) - done: Set[str] = set() + rule_collector = RuleCollectorVisitor(self.callmakervisitor) + done: set[str] = set() while True: computed_rules = list(self.all_rules) todo = [i for i in computed_rules if i not in done] @@ -229,10 +216,10 @@ def dedupe(self, name: str) -> str: class NullableVisitor(GrammarVisitor): - def __init__(self, rules: Dict[str, Rule]) -> None: + def __init__(self, rules: dict[str, Rule]) -> None: self.rules = rules - self.visited: Set[Any] = set() - self.nullables: Set[Union[Rule, NamedItem]] = set() + self.visited: set[Any] = set() + self.nullables: set[Rule | NamedItem] = set() def visit_Rule(self, rule: Rule) -> bool: if rule in self.visited: @@ -294,7 +281,7 @@ def visit_StringLeaf(self, node: StringLeaf) -> bool: return not node.value -def compute_nullables(rules: Dict[str, Rule]) -> Set[Any]: +def compute_nullables(rules: dict[str, Rule]) -> set[Any]: """Compute which rules in a grammar are nullable. Thanks to TatSu (tatsu/leftrec.py) for inspiration. @@ -306,12 +293,12 @@ def compute_nullables(rules: Dict[str, Rule]) -> Set[Any]: class InitialNamesVisitor(GrammarVisitor): - def __init__(self, rules: Dict[str, Rule]) -> None: + def __init__(self, rules: dict[str, Rule]) -> None: self.rules = rules self.nullables = compute_nullables(rules) - def generic_visit(self, node: Iterable[Any], *args: Any, **kwargs: Any) -> Set[Any]: - names: Set[str] = set() + def generic_visit(self, node: Iterable[Any], *args: Any, **kwargs: Any) -> set[Any]: + names: set[str] = set() for value in node: if isinstance(value, list): for item in value: @@ -320,33 +307,33 @@ def generic_visit(self, node: Iterable[Any], *args: Any, **kwargs: Any) -> Set[A names |= self.visit(value, *args, **kwargs) return names - def visit_Alt(self, alt: Alt) -> Set[Any]: - names: Set[str] = set() + def visit_Alt(self, alt: Alt) -> set[Any]: + names: set[str] = set() for item in alt.items: names |= self.visit(item) if item not in self.nullables: break return names - def visit_Forced(self, force: Forced) -> Set[Any]: + def visit_Forced(self, force: Forced) -> set[Any]: return set() - def visit_LookAhead(self, lookahead: Lookahead) -> Set[Any]: + def visit_LookAhead(self, lookahead: Lookahead) -> set[Any]: return set() - def visit_Cut(self, cut: Cut) -> Set[Any]: + def visit_Cut(self, cut: Cut) -> set[Any]: return set() - def visit_NameLeaf(self, node: NameLeaf) -> Set[Any]: + def visit_NameLeaf(self, node: NameLeaf) -> set[Any]: return {node.value} - def visit_StringLeaf(self, node: StringLeaf) -> Set[Any]: + def visit_StringLeaf(self, node: StringLeaf) -> set[Any]: return set() def compute_left_recursives( - rules: Dict[str, Rule] -) -> Tuple[Dict[str, AbstractSet[str]], List[AbstractSet[str]]]: + rules: dict[str, Rule] +) -> tuple[dict[str, Set[str]], list[Set[str]]]: graph = make_first_graph(rules) sccs = list(sccutils.strongly_connected_components(graph.keys(), graph)) for scc in sccs: @@ -374,7 +361,7 @@ def compute_left_recursives( return graph, sccs -def make_first_graph(rules: Dict[str, Rule]) -> Dict[str, AbstractSet[str]]: +def make_first_graph(rules: dict[str, Rule]) -> dict[str, Set[str]]: """Compute the graph of left-invocations. There's an edge from A to B if A may invoke B at its initial @@ -384,7 +371,7 @@ def make_first_graph(rules: Dict[str, Rule]) -> Dict[str, AbstractSet[str]]: """ initial_name_visitor = InitialNamesVisitor(rules) graph = {} - vertices: Set[str] = set() + vertices: set[str] = set() for rulename, rhs in rules.items(): graph[rulename] = names = initial_name_visitor.visit(rhs) vertices |= names diff --git a/Tools/peg_generator/pegen/python_generator.py b/Tools/peg_generator/pegen/python_generator.py index 4bb26480ebc0af..e69ea4b5f4e14c 100644 --- a/Tools/peg_generator/pegen/python_generator.py +++ b/Tools/peg_generator/pegen/python_generator.py @@ -1,6 +1,7 @@ import os.path import token -from typing import IO, Any, Callable, Dict, Optional, Sequence, Set, Text, Tuple +from collections.abc import Callable, Sequence +from typing import IO, Any from pegen import grammar from pegen.grammar import ( @@ -74,10 +75,10 @@ def visit_NegativeLookahead(self, node: NegativeLookahead) -> bool: def visit_Opt(self, node: Opt) -> bool: return self.visit(node.node) - def visit_Repeat(self, node: Repeat0) -> Tuple[str, str]: + def visit_Repeat(self, node: Repeat0) -> tuple[str, str]: return self.visit(node.node) - def visit_Gather(self, node: Gather) -> Tuple[str, str]: + def visit_Gather(self, node: Gather) -> tuple[str, str]: return self.visit(node.node) def visit_Group(self, node: Group) -> bool: @@ -93,9 +94,9 @@ def visit_Forced(self, node: Forced) -> bool: class PythonCallMakerVisitor(GrammarVisitor): def __init__(self, parser_generator: ParserGenerator): self.gen = parser_generator - self.cache: Dict[str, Tuple[str, str]] = {} + self.cache: dict[str, tuple[str, str]] = {} - def visit_NameLeaf(self, node: NameLeaf) -> Tuple[Optional[str], str]: + def visit_NameLeaf(self, node: NameLeaf) -> tuple[str | None, str]: name = node.value if name == "SOFT_KEYWORD": return "soft_keyword", "self.soft_keyword()" @@ -108,31 +109,31 @@ def visit_NameLeaf(self, node: NameLeaf) -> Tuple[Optional[str], str]: return "_" + name.lower(), f"self.expect({name!r})" return name, f"self.{name}()" - def visit_StringLeaf(self, node: StringLeaf) -> Tuple[str, str]: + def visit_StringLeaf(self, node: StringLeaf) -> tuple[str, str]: return "literal", f"self.expect({node.value})" - def visit_NamedItem(self, node: NamedItem) -> Tuple[Optional[str], str]: + def visit_NamedItem(self, node: NamedItem) -> tuple[str | None, str]: name, call = self.visit(node.item) if node.name: name = node.name return name, call - def lookahead_call_helper(self, node: Lookahead) -> Tuple[str, str]: + def lookahead_call_helper(self, node: Lookahead) -> tuple[str, str]: name, call = self.visit(node.node) head, tail = call.split("(", 1) assert tail[-1] == ")" tail = tail[:-1] return head, tail - def visit_PositiveLookahead(self, node: PositiveLookahead) -> Tuple[None, str]: + def visit_PositiveLookahead(self, node: PositiveLookahead) -> tuple[None, str]: head, tail = self.lookahead_call_helper(node) return None, f"self.positive_lookahead({head}, {tail})" - def visit_NegativeLookahead(self, node: NegativeLookahead) -> Tuple[None, str]: + def visit_NegativeLookahead(self, node: NegativeLookahead) -> tuple[None, str]: head, tail = self.lookahead_call_helper(node) return None, f"self.negative_lookahead({head}, {tail})" - def visit_Opt(self, node: Opt) -> Tuple[str, str]: + def visit_Opt(self, node: Opt) -> tuple[str, str]: name, call = self.visit(node.node) # Note trailing comma (the call may already have one comma # at the end, for example when rules have both repeat0 and optional @@ -148,7 +149,7 @@ def _generate_artificial_rule_call( prefix: str, call_by_name_func: Callable[[str], str], rule_generation_func: Callable[[], str], - ) -> Tuple[str, str]: + ) -> tuple[str, str]: node_str = f"{node}" key = f"{prefix}_{node_str}" if key in self.cache: @@ -159,7 +160,7 @@ def _generate_artificial_rule_call( self.cache[key] = name, call return self.cache[key] - def visit_Rhs(self, node: Rhs) -> Tuple[str, str]: + def visit_Rhs(self, node: Rhs) -> tuple[str, str]: if len(node.alts) == 1 and len(node.alts[0].items) == 1: return self.visit(node.alts[0].items[0]) @@ -170,7 +171,7 @@ def visit_Rhs(self, node: Rhs) -> Tuple[str, str]: lambda: self.gen.artificial_rule_from_rhs(node), ) - def visit_Repeat0(self, node: Repeat0) -> Tuple[str, str]: + def visit_Repeat0(self, node: Repeat0) -> tuple[str, str]: return self._generate_artificial_rule_call( node, "repeat0", @@ -178,7 +179,7 @@ def visit_Repeat0(self, node: Repeat0) -> Tuple[str, str]: lambda: self.gen.artificial_rule_from_repeat(node.node, is_repeat1=False), ) - def visit_Repeat1(self, node: Repeat1) -> Tuple[str, str]: + def visit_Repeat1(self, node: Repeat1) -> tuple[str, str]: return self._generate_artificial_rule_call( node, "repeat1", @@ -186,7 +187,7 @@ def visit_Repeat1(self, node: Repeat1) -> Tuple[str, str]: lambda: self.gen.artificial_rule_from_repeat(node.node, is_repeat1=True), ) - def visit_Gather(self, node: Gather) -> Tuple[str, str]: + def visit_Gather(self, node: Gather) -> tuple[str, str]: return self._generate_artificial_rule_call( node, "gather", @@ -194,13 +195,13 @@ def visit_Gather(self, node: Gather) -> Tuple[str, str]: lambda: self.gen.artificial_rule_from_gather(node), ) - def visit_Group(self, node: Group) -> Tuple[Optional[str], str]: + def visit_Group(self, node: Group) -> tuple[str | None, str]: return self.visit(node.rhs) - def visit_Cut(self, node: Cut) -> Tuple[str, str]: + def visit_Cut(self, node: Cut) -> tuple[str, str]: return "cut", "True" - def visit_Forced(self, node: Forced) -> Tuple[str, str]: + def visit_Forced(self, node: Forced) -> tuple[str, str]: if isinstance(node.node, Group): _, val = self.visit(node.node.rhs) return "forced", f"self.expect_forced({val}, '''({node.node.rhs!s})''')" @@ -215,10 +216,10 @@ class PythonParserGenerator(ParserGenerator, GrammarVisitor): def __init__( self, grammar: grammar.Grammar, - file: Optional[IO[Text]], - tokens: Set[str] = set(token.tok_name.values()), - location_formatting: Optional[str] = None, - unreachable_formatting: Optional[str] = None, + file: IO[str] | None, + tokens: set[str] = set(token.tok_name.values()), + location_formatting: str | None = None, + unreachable_formatting: str | None = None, ): tokens.add("SOFT_KEYWORD") super().__init__(grammar, tokens, file) @@ -355,7 +356,7 @@ def visit_Alt(self, node: Alt, is_loop: bool, is_gather: bool) -> None: if is_loop: self.print(f"children.append({action})") - self.print(f"mark = self._mark()") + self.print("mark = self._mark()") else: if "UNREACHABLE" in action: action = action.replace("UNREACHABLE", self.unreachable_formatting) diff --git a/Tools/peg_generator/pegen/sccutils.py b/Tools/peg_generator/pegen/sccutils.py index da4c9331625dd9..db30fc283465b8 100644 --- a/Tools/peg_generator/pegen/sccutils.py +++ b/Tools/peg_generator/pegen/sccutils.py @@ -1,11 +1,11 @@ # Adapted from mypy (mypy/build.py) under the MIT license. -from typing import * +from collections.abc import Iterable, Iterator, Set def strongly_connected_components( - vertices: AbstractSet[str], edges: Dict[str, AbstractSet[str]] -) -> Iterator[AbstractSet[str]]: + vertices: Set[str], edges: dict[str, Set[str]] +) -> Iterator[Set[str]]: """Compute Strongly Connected Components of a directed graph. Args: @@ -20,12 +20,12 @@ def strongly_connected_components( From https://code.activestate.com/recipes/578507-strongly-connected-components-of-a-directed-graph/. """ - identified: Set[str] = set() - stack: List[str] = [] - index: Dict[str, int] = {} - boundaries: List[int] = [] + identified: set[str] = set() + stack: list[str] = [] + index: dict[str, int] = {} + boundaries: list[int] = [] - def dfs(v: str) -> Iterator[Set[str]]: + def dfs(v: str) -> Iterator[set[str]]: index[v] = len(stack) stack.append(v) boundaries.append(index[v]) @@ -49,57 +49,9 @@ def dfs(v: str) -> Iterator[Set[str]]: yield from dfs(v) -def topsort( - data: Dict[AbstractSet[str], Set[AbstractSet[str]]] -) -> Iterable[AbstractSet[AbstractSet[str]]]: - """Topological sort. - - Args: - data: A map from SCCs (represented as frozen sets of strings) to - sets of SCCs, its dependencies. NOTE: This data structure - is modified in place -- for normalization purposes, - self-dependencies are removed and entries representing - orphans are added. - - Returns: - An iterator yielding sets of SCCs that have an equivalent - ordering. NOTE: The algorithm doesn't care about the internal - structure of SCCs. - - Example: - Suppose the input has the following structure: - - {A: {B, C}, B: {D}, C: {D}} - - This is normalized to: - - {A: {B, C}, B: {D}, C: {D}, D: {}} - - The algorithm will yield the following values: - - {D} - {B, C} - {A} - - From https://code.activestate.com/recipes/577413-topological-sort/history/1/. - """ - # TODO: Use a faster algorithm? - for k, v in data.items(): - v.discard(k) # Ignore self dependencies. - for item in set.union(*data.values()) - set(data.keys()): - data[item] = set() - while True: - ready = {item for item, dep in data.items() if not dep} - if not ready: - break - yield ready - data = {item: (dep - ready) for item, dep in data.items() if item not in ready} - assert not data, "A cyclic dependency exists amongst %r" % data - - def find_cycles_in_scc( - graph: Dict[str, AbstractSet[str]], scc: AbstractSet[str], start: str -) -> Iterable[List[str]]: + graph: dict[str, Set[str]], scc: Set[str], start: str +) -> Iterable[list[str]]: """Find cycles in SCC emanating from start. Yields lists of the form ['A', 'B', 'C', 'A'], which means there's @@ -117,7 +69,7 @@ def find_cycles_in_scc( assert start in graph # Recursive helper that yields cycles. - def dfs(node: str, path: List[str]) -> Iterator[List[str]]: + def dfs(node: str, path: list[str]) -> Iterator[list[str]]: if node in path: yield path + [node] return diff --git a/Tools/peg_generator/pegen/testutil.py b/Tools/peg_generator/pegen/testutil.py index 0e85b844ef152b..46f6c7fec1bda0 100644 --- a/Tools/peg_generator/pegen/testutil.py +++ b/Tools/peg_generator/pegen/testutil.py @@ -6,7 +6,7 @@ import textwrap import token import tokenize -from typing import IO, Any, Dict, Final, Optional, Type, cast +from typing import IO, Any, Final, cast from pegen.build import compile_c_extension from pegen.c_generator import CParserGenerator @@ -23,19 +23,19 @@ } -def generate_parser(grammar: Grammar) -> Type[Parser]: +def generate_parser(grammar: Grammar) -> type[Parser]: # Generate a parser. out = io.StringIO() genr = PythonParserGenerator(grammar, out) genr.generate("<string>") # Load the generated parser class. - ns: Dict[str, Any] = {} + ns: dict[str, Any] = {} exec(out.getvalue(), ns) return ns["GeneratedParser"] -def run_parser(file: IO[bytes], parser_class: Type[Parser], *, verbose: bool = False) -> Any: +def run_parser(file: IO[bytes], parser_class: type[Parser], *, verbose: bool = False) -> Any: # Run a parser on a file (stream). tokenizer = Tokenizer(tokenize.generate_tokens(file.readline)) # type: ignore[arg-type] # typeshed issue #3515 parser = parser_class(tokenizer, verbose=verbose) @@ -46,7 +46,7 @@ def run_parser(file: IO[bytes], parser_class: Type[Parser], *, verbose: bool = F def parse_string( - source: str, parser_class: Type[Parser], *, dedent: bool = True, verbose: bool = False + source: str, parser_class: type[Parser], *, dedent: bool = True, verbose: bool = False ) -> Any: # Run the parser on a string. if dedent: @@ -55,7 +55,7 @@ def parse_string( return run_parser(file, parser_class, verbose=verbose) # type: ignore[arg-type] # typeshed issue #3515 -def make_parser(source: str) -> Type[Parser]: +def make_parser(source: str) -> type[Parser]: # Combine parse_string() and generate_parser(). grammar = parse_string(source, GrammarParser) return generate_parser(grammar) @@ -86,7 +86,7 @@ def generate_parser_c_extension( grammar: Grammar, path: pathlib.PurePath, debug: bool = False, - library_dir: Optional[str] = None, + library_dir: str | None = None, ) -> Any: """Generate a parser c extension for the given grammar in the given path diff --git a/Tools/peg_generator/pegen/tokenizer.py b/Tools/peg_generator/pegen/tokenizer.py index 7ee49e1432b77a..fba5bac9517895 100644 --- a/Tools/peg_generator/pegen/tokenizer.py +++ b/Tools/peg_generator/pegen/tokenizer.py @@ -1,6 +1,6 @@ import token import tokenize -from typing import Dict, Iterator, List +from collections.abc import Iterator Mark = int # NewType('Mark', int) @@ -8,7 +8,11 @@ def shorttok(tok: tokenize.TokenInfo) -> str: - return "%-25.25s" % f"{tok.start[0]}.{tok.start[1]}: {token.tok_name[tok.type]}:{tok.string!r}" + formatted = ( + f"{tok.start[0]}.{tok.start[1]}: " + f"{token.tok_name[tok.type]}:{tok.string!r}" + ) + return f"{formatted:<25.25}" class Tokenizer: @@ -17,7 +21,7 @@ class Tokenizer: This is pretty tied to Python's syntax. """ - _tokens: List[tokenize.TokenInfo] + _tokens: list[tokenize.TokenInfo] def __init__( self, tokengen: Iterator[tokenize.TokenInfo], *, path: str = "", verbose: bool = False @@ -26,7 +30,7 @@ def __init__( self._tokens = [] self._index = 0 self._verbose = verbose - self._lines: Dict[int, str] = {} + self._lines: dict[int, str] = {} self._path = path if verbose: self.report(False, False) @@ -72,7 +76,7 @@ def get_last_non_whitespace_token(self) -> tokenize.TokenInfo: break return tok - def get_lines(self, line_numbers: List[int]) -> List[str]: + def get_lines(self, line_numbers: list[int]) -> list[str]: """Retrieve source lines corresponding to line numbers.""" if self._lines: lines = self._lines diff --git a/Tools/peg_generator/pegen/validator.py b/Tools/peg_generator/pegen/validator.py index 4699d5712d9522..5e2bc238a1e966 100644 --- a/Tools/peg_generator/pegen/validator.py +++ b/Tools/peg_generator/pegen/validator.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Any from pegen import grammar from pegen.grammar import Alt, GrammarVisitor, Rhs, Rule @@ -11,7 +11,7 @@ class ValidationError(Exception): class GrammarValidator(GrammarVisitor): def __init__(self, grammar: grammar.Grammar) -> None: self.grammar = grammar - self.rulename: Optional[str] = None + self.rulename: str | None = None def validate_rule(self, rulename: str, node: Rule) -> None: self.rulename = rulename @@ -46,6 +46,37 @@ def visit_Alt(self, node: Alt) -> None: ) +class CutValidator(GrammarValidator): + """Fail if Cut is not directly in a rule. + + For simplicity, we currently document that a Cut affects alternatives + of the *rule* it is in. + However, the implementation makes cuts local to enclosing Rhs + (e.g. parenthesized list of choices). + Additionally, in academic papers about PEG, repeats and optional items + are "desugared" to choices with an empty alternative, and thus contain + a Cut's effect. + + Please update documentation and tests when adding this cut, + then get rid of this validator. + + See gh-143054. + """ + + def visit(self, node: Any, parents: tuple[Any, ...] = ()) -> None: + super().visit(node, parents=(*parents, node)) + + def visit_Cut(self, node: Alt, parents: tuple[Any, ...] = ()) -> None: + parent_types = [type(p).__name__ for p in parents] + if parent_types != ['Rule', 'Rhs', 'Alt', 'NamedItem', 'Cut']: + raise ValidationError( + f"Rule {self.rulename!r} contains cut that's not on the " + "top level. " + "The intended semantics of such cases need " + "to be clarified; see the CutValidator docstring." + f"\nThe cut is inside: {parent_types}" + ) + def validate_grammar(the_grammar: grammar.Grammar) -> None: for validator_cls in GrammarValidator.__subclasses__(): validator = validator_cls(the_grammar) diff --git a/Tools/picklebench/README.md b/Tools/picklebench/README.md new file mode 100644 index 00000000000000..7d52485c386350 --- /dev/null +++ b/Tools/picklebench/README.md @@ -0,0 +1,232 @@ +# Pickle Chunked Reading Benchmark + +This benchmark measures the performance impact of the chunked reading optimization in GH PR #119204 for the pickle module. + +## What This Tests + +The PR adds chunked reading (1MB chunks) to prevent memory exhaustion when unpickling large objects: +- **BINBYTES8** - Large bytes objects (protocol 4+) +- **BINUNICODE8** - Large strings (protocol 4+) +- **BYTEARRAY8** - Large bytearrays (protocol 5) +- **FRAME** - Large frames +- **LONG4** - Large integers +- An antagonistic mode that tests using memory denial of service inducing malicious pickles. + +## Quick Start + +```bash +# Run full benchmark suite (1MiB → 200MiB, takes several minutes) +build/python Tools/picklebench/memory_dos_impact.py + +# Test just a few sizes (quick test: 1, 10, 50 MiB) +build/python Tools/picklebench/memory_dos_impact.py --sizes 1 10 50 + +# Test smaller range for faster results +build/python Tools/picklebench/memory_dos_impact.py --sizes 1 5 10 + +# Output as markdown for reports +build/python Tools/picklebench/memory_dos_impact.py --format markdown > results.md + +# Test with protocol 4 instead of 5 +build/python Tools/picklebench/memory_dos_impact.py --protocol 4 +``` + +**Note:** Sizes are specified in MiB. Use `--sizes 1 2 5` for 1MiB, 2MiB, 5MiB objects. + +## Antagonistic Mode (DoS Protection Test) + +The `--antagonistic` flag tests **malicious pickles** that demonstrate the memory DoS protection: + +```bash +# Quick DoS protection test (claims 10, 50, 100 MB but provides 1KB) +build/python Tools/picklebench/memory_dos_impact.py --antagonistic --sizes 10 50 100 + +# Full DoS test (default: 10, 50, 100, 500, 1000, 5000 MB claimed) +build/python Tools/picklebench/memory_dos_impact.py --antagonistic +``` + +### What This Tests + +Unlike normal benchmarks that test **legitimate pickles**, antagonistic mode tests: +- **Truncated BINBYTES8**: Claims 100MB but provides only 1KB (will fail to unpickle) +- **Truncated BINUNICODE8**: Same for strings +- **Truncated BYTEARRAY8**: Same for bytearrays +- **Sparse memo attacks**: PUT at index 1 billion (would allocate huge array before PR) + +**Key difference:** +- **Normal mode**: Tests real data, shows ~5% time overhead +- **Antagonistic mode**: Tests malicious data, shows ~99% memory savings + +### Expected Results + +``` +100MB Claimed (actual: 1KB) + binbytes8_100MB_claim + Peak memory: 1.00 MB (claimed: 100 MB, saved: 99.00 MB, 99.0%) + Error: UnpicklingError ← Expected! + +Summary: + Average claimed: 126.2 MB + Average peak: 0.54 MB + Average saved: 125.7 MB (99.6% reduction) +Protection Status: ✓ Memory DoS attacks mitigated by chunked reading +``` + +**Before PR**: Would allocate full claimed size (100MB+), potentially crash +**After PR**: Allocates 1MB chunks, fails fast with minimal memory + +This demonstrates the **security improvement** - protection against memory exhaustion attacks. + +## Before/After Comparison + +The benchmark includes an automatic comparison feature that runs the same tests on both a baseline and current Python build. + +### Option 1: Automatic Comparison (Recommended) + +Build both versions, then use `--baseline` to automatically compare: + +```bash +# Build the baseline (main branch without PR) +git checkout main +mkdir -p build-main +cd build-main && ../configure && make -j $(nproc) && cd .. + +# Build the current version (with PR) +git checkout unpickle-overallocate +mkdir -p build +cd build && ../configure && make -j $(nproc) && cd .. + +# Run automatic comparison (quick test with a few sizes) +build/python Tools/picklebench/memory_dos_impact.py \ + --baseline build-main/python \ + --sizes 1 10 50 + +# Full comparison (all default sizes) +build/python Tools/picklebench/memory_dos_impact.py \ + --baseline build-main/python +``` + +The comparison output shows: +- Side-by-side metrics (Current vs Baseline) +- Percentage change for time and memory +- Overall summary statistics + +### Interpreting Comparison Results + +- **Time change**: Small positive % is expected (chunking adds overhead, typically 5-10%) +- **Memory change**: Negative % is good (chunking saves memory, especially for large objects) +- **Trade-off**: Slightly slower but much safer against memory exhaustion attacks + +### Option 2: Manual Comparison + +Save results separately and compare manually: + +```bash +# Baseline results +build-main/python Tools/picklebench/memory_dos_impact.py --format json > baseline.json + +# Current results +build/python Tools/picklebench/memory_dos_impact.py --format json > current.json + +# Manual comparison +diff -y <(jq '.' baseline.json) <(jq '.' current.json) +``` + +## Understanding the Results + +### Critical Sizes + +The default test suite includes: +- **< 1MiB (999,000 bytes)**: No chunking, allocates full size upfront +- **= 1MiB (1,048,576 bytes)**: Threshold, chunking just starts +- **> 1MiB (1,048,577 bytes)**: Chunked reading engaged +- **1, 2, 5, 10MiB**: Show scaling behavior with chunking +- **20, 50, 100, 200MiB**: Stress test large object handling + +**Note:** The full suite may require more than 16GiB of RAM. + +### Key Metrics + +- **Time (mean)**: Average unpickling time - should be similar before/after +- **Time (stdev)**: Consistency - lower is better +- **Peak Memory**: Maximum memory during unpickling - **expected to be LOWER after PR** +- **Pickle Size**: Size of the serialized data on disk + +### Test Types + +| Test | What It Stresses | +|------|------------------| +| `bytes_*` | BINBYTES8 opcode, raw binary data | +| `string_ascii_*` | BINUNICODE8 with simple ASCII | +| `string_utf8_*` | BINUNICODE8 with multibyte UTF-8 (€ chars) | +| `bytearray_*` | BYTEARRAY8 opcode (protocol 5) | +| `list_large_items_*` | Multiple chunked reads in sequence | +| `dict_large_values_*` | Chunking in dict deserialization | +| `nested_*` | Realistic mixed data structures | +| `tuple_*` | Immutable structures | + +## Expected Results + +### Before PR (main branch) +- Single large allocation per object +- Risk of memory exhaustion with malicious pickles + +### After PR (unpickle-overallocate branch) +- Chunked allocation (1MB at a time) +- **Slightly higher CPU time** (multiple allocations + resizing) +- **Significantly lower peak memory** (no large pre-allocation) +- Protection against DoS via memory exhaustion + +## Advanced Usage + +### Test Specific Sizes + +```bash +# Test only 5MiB and 10MiB objects +build/python Tools/picklebench/memory_dos_impact.py --sizes 5 10 + +# Test large objects: 50, 100, 200 MiB +build/python Tools/picklebench/memory_dos_impact.py --sizes 50 100 200 +``` + +### More Iterations for Stable Timing + +```bash +# Run 10 iterations per test for better statistics +build/python Tools/picklebench/memory_dos_impact.py --iterations 10 --sizes 1 10 +``` + +### JSON Output for Analysis + +```bash +# Generate JSON for programmatic analysis +build/python Tools/picklebench/memory_dos_impact.py --format json | python -m json.tool +``` + +## Interpreting Memory Results + +The **peak memory** metric shows the maximum memory allocated during unpickling: + +- **Without chunking**: Allocates full size immediately + - 10MB object → 10MB allocation upfront + +- **With chunking**: Allocates in 1MB chunks, grows geometrically + - 10MB object → starts with 1MB, grows: 2MB, 4MB, 8MB (final: ~10MB total) + - Peak is lower because allocation is incremental + +## Typical Results + +On a system with the PR applied, you should see: + +``` +1.00MiB Test Results + bytes_1.00MiB: ~0.3ms, 1.00MiB peak (just at threshold) + +2.00MiB Test Results + bytes_2.00MiB: ~0.8ms, 2.00MiB peak (chunked: 1MiB → 2MiB) + +10.00MiB Test Results + bytes_10.00MiB: ~3-5ms, 10.00MiB peak (chunked: 1→2→4→8→10 MiB) +``` + +Time overhead is minimal (~10-20% for very large objects), but memory safety is significantly improved. diff --git a/Tools/picklebench/memory_dos_impact.py b/Tools/picklebench/memory_dos_impact.py new file mode 100755 index 00000000000000..0a7cef8668565c --- /dev/null +++ b/Tools/picklebench/memory_dos_impact.py @@ -0,0 +1,1067 @@ +#!/usr/bin/env python3 +# +# Author: Claude Sonnet 4.5 as driven by gpshead +# +""" +Microbenchmark for pickle module chunked reading performance (GH PR #119204). + +This script generates Python data structures that act as antagonistic load +tests for the chunked reading code introduced to prevent memory exhaustion when +unpickling large objects. + +The PR adds chunked reading (1MB chunks) for: +- BINBYTES8 (large bytes) +- BINUNICODE8 (large strings) +- BYTEARRAY8 (large bytearrays) +- FRAME (large frames) +- LONG4 (large integers) + +Including an antagonistic mode that exercies memory denial of service pickles. + +Usage: + python memory_dos_impact.py --help +""" + +import argparse +import gc +import json +import os +import pickle +import statistics +import struct +import subprocess +import sys +import tracemalloc +from pathlib import Path +from time import perf_counter +from typing import Any, Dict, List, Tuple + + +# Configuration +MIN_READ_BUF_SIZE = 1 << 20 # 1MB - matches pickle.py _MIN_READ_BUF_SIZE + +# Test sizes in MiB +DEFAULT_SIZES_MIB = [1, 2, 5, 10, 20, 50, 100, 200] + +# Convert to bytes, plus threshold boundary tests +DEFAULT_SIZES = ( + [999_000] # Below 1MiB (no chunking) + + [size * (1 << 20) for size in DEFAULT_SIZES_MIB] # MiB to bytes + + [1_048_577] # Just above 1MiB (minimal chunking overhead) +) +DEFAULT_SIZES.sort() + +# Baseline benchmark configuration +BASELINE_BENCHMARK_TIMEOUT_SECONDS = 600 # 10 minutes + +# Sparse memo attack test configuration +# Format: test_name -> (memo_index, baseline_memory_note) +SPARSE_MEMO_TESTS = { + "sparse_memo_1M": (1_000_000, "~8 MB array"), + "sparse_memo_100M": (100_000_000, "~800 MB array"), + "sparse_memo_1B": (1_000_000_000, "~8 GB array"), +} + + +# Utility functions + +def _extract_size_mb(size_key: str) -> float: + """Extract numeric MiB value from size_key like '10.00MB' or '1.00MiB'. + + Returns 0.0 for non-numeric keys (they'll be sorted last). + """ + try: + return float(size_key.replace('MB', '').replace('MiB', '')) + except ValueError: + return 999999.0 # Put non-numeric keys last + + +def _format_output(results: Dict[str, Dict[str, Any]], format_type: str, is_antagonistic: bool) -> str: + """Format benchmark results according to requested format. + + Args: + results: Benchmark results dictionary + format_type: Output format ('text', 'markdown', or 'json') + is_antagonistic: Whether these are antagonistic (DoS) test results + + Returns: + Formatted output string + """ + if format_type == 'json': + return Reporter.format_json(results) + elif is_antagonistic: + # Antagonistic mode uses specialized formatter for text/markdown + return Reporter.format_antagonistic(results) + elif format_type == 'text': + return Reporter.format_text(results) + elif format_type == 'markdown': + return Reporter.format_markdown(results) + else: + # Default to text format + return Reporter.format_text(results) + + +class AntagonisticGenerator: + """Generate malicious/truncated pickles for DoS protection testing. + + These pickles claim large sizes but provide minimal data, causing them to fail + during unpickling. They demonstrate the memory protection of chunked reading. + """ + + @staticmethod + def truncated_binbytes8(claimed_size: int, actual_size: int = 1024) -> bytes: + """BINBYTES8 claiming `claimed_size` but providing only `actual_size` bytes. + + This will fail with UnpicklingError but demonstrates peak memory usage. + Before PR: Allocates full claimed_size + After PR: Allocates in 1MB chunks, fails fast + """ + return b'\x8e' + struct.pack('<Q', claimed_size) + b'x' * actual_size + + @staticmethod + def truncated_binunicode8(claimed_size: int, actual_size: int = 1024) -> bytes: + """BINUNICODE8 claiming `claimed_size` but providing only `actual_size` bytes.""" + return b'\x8d' + struct.pack('<Q', claimed_size) + b'x' * actual_size + + @staticmethod + def truncated_bytearray8(claimed_size: int, actual_size: int = 1024) -> bytes: + """BYTEARRAY8 claiming `claimed_size` but providing only `actual_size` bytes.""" + return b'\x96' + struct.pack('<Q', claimed_size) + b'x' * actual_size + + @staticmethod + def truncated_frame(claimed_size: int) -> bytes: + """FRAME claiming `claimed_size` but providing minimal data.""" + return b'\x95' + struct.pack('<Q', claimed_size) + b'N.' + + @staticmethod + def sparse_memo_attack(index: int) -> bytes: + """LONG_BINPUT with huge sparse index. + + Before PR: Tries to allocate array with `index` slots (OOM) + After PR: Uses dict-based memo for sparse indices + """ + return (b'(]r' + struct.pack('<I', index & 0xFFFFFFFF) + + b'j' + struct.pack('<I', index & 0xFFFFFFFF) + b't.') + + @staticmethod + def multi_claim_attack(count: int, size_each: int) -> bytes: + """Multiple BINBYTES8 claims in sequence. + + Tests that multiple large claims don't accumulate memory. + """ + data = b'(' # MARK + for _ in range(count): + data += b'\x8e' + struct.pack('<Q', size_each) + b'x' * 1024 + data += b't.' # TUPLE + STOP + return data + + +class DataGenerator: + """Generate various types of large data structures for pickle testing.""" + + @staticmethod + def large_bytes(size: int) -> bytes: + """Generate random bytes of specified size.""" + return os.urandom(size) + + @staticmethod + def large_string_ascii(size: int) -> str: + """Generate ASCII string of specified size.""" + return 'x' * size + + @staticmethod + def large_string_multibyte(size: int) -> str: + """Generate multibyte UTF-8 string (3 bytes per char for €).""" + # Each € is 3 bytes in UTF-8 + return '€' * (size // 3) + + @staticmethod + def large_bytearray(size: int) -> bytearray: + """Generate bytearray of specified size.""" + return bytearray(os.urandom(size)) + + @staticmethod + def list_of_large_bytes(item_size: int, count: int) -> List[bytes]: + """Generate list containing multiple large bytes objects.""" + return [os.urandom(item_size) for _ in range(count)] + + @staticmethod + def dict_with_large_values(value_size: int, count: int) -> Dict[str, bytes]: + """Generate dict with large bytes values.""" + return { + f'key_{i}': os.urandom(value_size) + for i in range(count) + } + + @staticmethod + def nested_structure(size: int) -> Dict[str, Any]: + """Generate nested structure with various large objects.""" + chunk_size = size // 4 + return { + 'name': 'test_object', + 'data': { + 'bytes': os.urandom(chunk_size), + 'string': 's' * chunk_size, + 'bytearray': bytearray(b'b' * chunk_size), + }, + 'items': [os.urandom(chunk_size // 4) for _ in range(4)], + 'metadata': { + 'size': size, + 'type': 'nested', + }, + } + + @staticmethod + def tuple_of_large_objects(size: int) -> Tuple[bytes, str, bytearray]: + """Generate tuple with large objects (immutable, different pickle path).""" + chunk_size = size // 3 + return ( + os.urandom(chunk_size), + 'x' * chunk_size, + bytearray(b'y' * chunk_size), + ) + + +class PickleBenchmark: + """Benchmark pickle unpickling performance and memory usage.""" + + def __init__(self, obj: Any, protocol: int = 5, iterations: int = 3): + self.obj = obj + self.protocol = protocol + self.iterations = iterations + self.pickle_data = pickle.dumps(obj, protocol=protocol) + self.pickle_size = len(self.pickle_data) + + def benchmark_time(self) -> Dict[str, float]: + """Measure unpickling time over multiple iterations.""" + times = [] + + for _ in range(self.iterations): + start = perf_counter() + result = pickle.loads(self.pickle_data) + elapsed = perf_counter() - start + times.append(elapsed) + + # Verify correctness (first iteration only) + if len(times) == 1: + if result != self.obj: + raise ValueError("Unpickled object doesn't match original!") + + return { + 'mean': statistics.mean(times), + 'median': statistics.median(times), + 'stdev': statistics.stdev(times) if len(times) > 1 else 0.0, + 'min': min(times), + 'max': max(times), + } + + def benchmark_memory(self) -> int: + """Measure peak memory usage during unpickling.""" + tracemalloc.start() + + # Warmup + pickle.loads(self.pickle_data) + + # Actual measurement + gc.collect() + tracemalloc.reset_peak() + result = pickle.loads(self.pickle_data) + current, peak = tracemalloc.get_traced_memory() + + tracemalloc.stop() + + # Verify correctness + if result != self.obj: + raise ValueError("Unpickled object doesn't match original!") + + return peak + + def run_all(self) -> Dict[str, Any]: + """Run all benchmarks and return comprehensive results.""" + time_stats = self.benchmark_time() + peak_memory = self.benchmark_memory() + + return { + 'pickle_size_bytes': self.pickle_size, + 'pickle_size_mb': self.pickle_size / (1 << 20), + 'protocol': self.protocol, + 'time': time_stats, + 'memory_peak_bytes': peak_memory, + 'memory_peak_mb': peak_memory / (1 << 20), + 'iterations': self.iterations, + } + + +class AntagonisticBenchmark: + """Benchmark antagonistic/malicious pickles that demonstrate DoS protection. + + These pickles are designed to FAIL unpickling, but we measure peak memory + usage before the failure to demonstrate the memory protection. + """ + + def __init__(self, pickle_data: bytes, name: str): + self.pickle_data = pickle_data + self.name = name + + def measure_peak_memory(self, expect_success: bool = False) -> Dict[str, Any]: + """Measure peak memory when attempting to unpickle antagonistic data. + + Args: + expect_success: If True, test expects successful unpickling (e.g., sparse memo). + If False, test expects failure (e.g., truncated data). + """ + tracemalloc.start() + gc.collect() + tracemalloc.reset_peak() + + error_type = None + error_msg = None + succeeded = False + + try: + result = pickle.loads(self.pickle_data) + succeeded = True + if expect_success: + error_type = "Success (expected)" + else: + error_type = "WARNING: Expected failure but succeeded" + except (pickle.UnpicklingError, EOFError, ValueError, OverflowError) as e: + if expect_success: + error_type = f"UNEXPECTED FAILURE: {type(e).__name__}" + error_msg = str(e)[:100] + else: + # Expected failure for truncated data tests + error_type = type(e).__name__ + error_msg = str(e)[:100] + + current, peak = tracemalloc.get_traced_memory() + tracemalloc.stop() + + return { + 'test_name': self.name, + 'peak_memory_bytes': peak, + 'peak_memory_mb': peak / (1 << 20), + 'error_type': error_type, + 'error_msg': error_msg, + 'pickle_size_bytes': len(self.pickle_data), + 'expected_outcome': 'success' if expect_success else 'failure', + 'succeeded': succeeded, + } + + +class AntagonisticTestSuite: + """Manage a suite of antagonistic (DoS protection) tests.""" + + # Default sizes in MB to claim (will provide only 1KB actual data) + DEFAULT_ANTAGONISTIC_SIZES_MB = [10, 50, 100, 500, 1000, 5000] + + def __init__(self, claimed_sizes_mb: List[int]): + self.claimed_sizes_mb = claimed_sizes_mb + + def _run_truncated_test( + self, + test_type: str, + generator_func, + claimed_bytes: int, + claimed_mb: int, + size_key: str, + all_results: Dict[str, Dict[str, Any]] + ) -> None: + """Run a single truncated data test and store results. + + Args: + test_type: Type identifier (e.g., 'binbytes8', 'binunicode8') + generator_func: Function to generate malicious pickle data + claimed_bytes: Size claimed in the pickle (bytes) + claimed_mb: Size claimed in the pickle (MB) + size_key: Result key for this size (e.g., '10MB') + all_results: Dictionary to store results in + """ + test_name = f"{test_type}_{size_key}_claim" + data = generator_func(claimed_bytes) + bench = AntagonisticBenchmark(data, test_name) + result = bench.measure_peak_memory(expect_success=False) + result['claimed_mb'] = claimed_mb + all_results[size_key][test_name] = result + + def run_all_tests(self) -> Dict[str, Dict[str, Any]]: + """Run comprehensive antagonistic test suite.""" + all_results = {} + + for claimed_mb in self.claimed_sizes_mb: + claimed_bytes = claimed_mb << 20 + size_key = f"{claimed_mb}MB" + all_results[size_key] = {} + + # Run truncated data tests (expect failure) + self._run_truncated_test('binbytes8', AntagonisticGenerator.truncated_binbytes8, + claimed_bytes, claimed_mb, size_key, all_results) + self._run_truncated_test('binunicode8', AntagonisticGenerator.truncated_binunicode8, + claimed_bytes, claimed_mb, size_key, all_results) + self._run_truncated_test('bytearray8', AntagonisticGenerator.truncated_bytearray8, + claimed_bytes, claimed_mb, size_key, all_results) + self._run_truncated_test('frame', AntagonisticGenerator.truncated_frame, + claimed_bytes, claimed_mb, size_key, all_results) + + # Test 5: Sparse memo (expect success - dict-based memo works!) + all_results["Sparse Memo (Success Expected)"] = {} + for test_name, (index, baseline_note) in SPARSE_MEMO_TESTS.items(): + data = AntagonisticGenerator.sparse_memo_attack(index) + bench = AntagonisticBenchmark(data, test_name) + result = bench.measure_peak_memory(expect_success=True) + result['claimed_mb'] = "N/A" + result['baseline_note'] = f"Without PR: {baseline_note}" + all_results["Sparse Memo (Success Expected)"][test_name] = result + + # Test 6: Multi-claim attack (expect failure) + test_name = "multi_claim_10x100MB" + data = AntagonisticGenerator.multi_claim_attack(10, 100 << 20) + bench = AntagonisticBenchmark(data, test_name) + result = bench.measure_peak_memory(expect_success=False) + result['claimed_mb'] = 1000 # 10 * 100MB + all_results["Multi-Claim (Failure Expected)"] = {test_name: result} + + return all_results + + +class TestSuite: + """Manage a suite of benchmark tests.""" + + def __init__(self, sizes: List[int], protocol: int = 5, iterations: int = 3): + self.sizes = sizes + self.protocol = protocol + self.iterations = iterations + self.results = {} + + def run_test(self, name: str, obj: Any) -> Dict[str, Any]: + """Run benchmark for a single test object.""" + bench = PickleBenchmark(obj, self.protocol, self.iterations) + results = bench.run_all() + results['test_name'] = name + results['object_type'] = type(obj).__name__ + return results + + def run_all_tests(self) -> Dict[str, Dict[str, Any]]: + """Run comprehensive test suite across all sizes and types.""" + all_results = {} + + for size in self.sizes: + size_key = f"{size / (1 << 20):.2f}MB" + all_results[size_key] = {} + + # Test 1: Large bytes object (BINBYTES8) + test_name = f"bytes_{size_key}" + obj = DataGenerator.large_bytes(size) + all_results[size_key][test_name] = self.run_test(test_name, obj) + + # Test 2: Large ASCII string (BINUNICODE8) + test_name = f"string_ascii_{size_key}" + obj = DataGenerator.large_string_ascii(size) + all_results[size_key][test_name] = self.run_test(test_name, obj) + + # Test 3: Large multibyte UTF-8 string + if size >= 3: + test_name = f"string_utf8_{size_key}" + obj = DataGenerator.large_string_multibyte(size) + all_results[size_key][test_name] = self.run_test(test_name, obj) + + # Test 4: Large bytearray (BYTEARRAY8, protocol 5) + if self.protocol >= 5: + test_name = f"bytearray_{size_key}" + obj = DataGenerator.large_bytearray(size) + all_results[size_key][test_name] = self.run_test(test_name, obj) + + # Test 5: List of large objects (repeated chunking) + if size >= MIN_READ_BUF_SIZE * 2: + test_name = f"list_large_items_{size_key}" + item_size = size // 5 + obj = DataGenerator.list_of_large_bytes(item_size, 5) + all_results[size_key][test_name] = self.run_test(test_name, obj) + + # Test 6: Dict with large values + if size >= MIN_READ_BUF_SIZE * 2: + test_name = f"dict_large_values_{size_key}" + value_size = size // 3 + obj = DataGenerator.dict_with_large_values(value_size, 3) + all_results[size_key][test_name] = self.run_test(test_name, obj) + + # Test 7: Nested structure + if size >= MIN_READ_BUF_SIZE: + test_name = f"nested_{size_key}" + obj = DataGenerator.nested_structure(size) + all_results[size_key][test_name] = self.run_test(test_name, obj) + + # Test 8: Tuple (immutable) + if size >= 3: + test_name = f"tuple_{size_key}" + obj = DataGenerator.tuple_of_large_objects(size) + all_results[size_key][test_name] = self.run_test(test_name, obj) + + return all_results + + +class Comparator: + """Compare benchmark results between current and baseline interpreters.""" + + @staticmethod + def _extract_json_from_output(output: str) -> Dict[str, Dict[str, Any]]: + """Extract JSON data from subprocess output. + + Skips any print statements before the JSON output and parses the JSON. + + Args: + output: Raw stdout from subprocess + + Returns: + Parsed JSON as dictionary + + Raises: + SystemExit: If JSON cannot be found or parsed + """ + output_lines = output.strip().split('\n') + json_start = -1 + for i, line in enumerate(output_lines): + if line.strip().startswith('{'): + json_start = i + break + + if json_start == -1: + print("Error: Could not find JSON output from baseline", file=sys.stderr) + sys.exit(1) + + json_output = '\n'.join(output_lines[json_start:]) + try: + return json.loads(json_output) + except json.JSONDecodeError as e: + print(f"Error: Could not parse baseline JSON output: {e}", file=sys.stderr) + sys.exit(1) + + @staticmethod + def run_baseline_benchmark(baseline_python: str, args: argparse.Namespace) -> Dict[str, Dict[str, Any]]: + """Run the benchmark using the baseline Python interpreter.""" + # Build command to run this script with baseline Python + cmd = [ + baseline_python, + __file__, + '--format', 'json', + '--protocol', str(args.protocol), + '--iterations', str(args.iterations), + ] + + if args.sizes is not None: + cmd.extend(['--sizes'] + [str(s) for s in args.sizes]) + + if args.antagonistic: + cmd.append('--antagonistic') + + print(f"\nRunning baseline benchmark with: {baseline_python}") + print(f"Command: {' '.join(cmd)}\n") + + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + timeout=BASELINE_BENCHMARK_TIMEOUT_SECONDS, + ) + + if result.returncode != 0: + print(f"Error running baseline benchmark:", file=sys.stderr) + print(result.stderr, file=sys.stderr) + sys.exit(1) + + # Extract and parse JSON from output + return Comparator._extract_json_from_output(result.stdout) + + except subprocess.TimeoutExpired: + print("Error: Baseline benchmark timed out", file=sys.stderr) + sys.exit(1) + + @staticmethod + def calculate_change(baseline_value: float, current_value: float) -> float: + """Calculate percentage change from baseline to current.""" + if baseline_value == 0: + return 0.0 + return ((current_value - baseline_value) / baseline_value) * 100 + + @staticmethod + def format_comparison( + current_results: Dict[str, Dict[str, Any]], + baseline_results: Dict[str, Dict[str, Any]] + ) -> str: + """Format comparison results as readable text.""" + lines = [] + lines.append("=" * 100) + lines.append("Pickle Unpickling Benchmark Comparison") + lines.append("=" * 100) + lines.append("") + lines.append("Legend: Current vs Baseline | % Change (+ is slower/more memory, - is faster/less memory)") + lines.append("") + + # Sort size keys numerically + for size_key in sorted(current_results.keys(), key=_extract_size_mb): + if size_key not in baseline_results: + continue + + lines.append(f"\n{size_key} Comparison") + lines.append("-" * 100) + + current_tests = current_results[size_key] + baseline_tests = baseline_results[size_key] + + for test_name in sorted(current_tests.keys()): + if test_name not in baseline_tests: + continue + + curr = current_tests[test_name] + base = baseline_tests[test_name] + + time_change = Comparator.calculate_change( + base['time']['mean'], curr['time']['mean'] + ) + mem_change = Comparator.calculate_change( + base['memory_peak_mb'], curr['memory_peak_mb'] + ) + + lines.append(f"\n {curr['test_name']}") + lines.append(f" Time: {curr['time']['mean']*1000:6.2f}ms vs {base['time']['mean']*1000:6.2f}ms | " + f"{time_change:+6.1f}%") + lines.append(f" Memory: {curr['memory_peak_mb']:6.2f}MB vs {base['memory_peak_mb']:6.2f}MB | " + f"{mem_change:+6.1f}%") + + lines.append("\n" + "=" * 100) + lines.append("\nSummary:") + + # Calculate overall statistics + time_changes = [] + mem_changes = [] + + for size_key in current_results.keys(): + if size_key not in baseline_results: + continue + for test_name in current_results[size_key].keys(): + if test_name not in baseline_results[size_key]: + continue + curr = current_results[size_key][test_name] + base = baseline_results[size_key][test_name] + + time_changes.append(Comparator.calculate_change( + base['time']['mean'], curr['time']['mean'] + )) + mem_changes.append(Comparator.calculate_change( + base['memory_peak_mb'], curr['memory_peak_mb'] + )) + + if time_changes: + lines.append(f" Time change: mean={statistics.mean(time_changes):+.1f}%, " + f"median={statistics.median(time_changes):+.1f}%") + if mem_changes: + lines.append(f" Memory change: mean={statistics.mean(mem_changes):+.1f}%, " + f"median={statistics.median(mem_changes):+.1f}%") + + lines.append("=" * 100) + return "\n".join(lines) + + @staticmethod + def format_antagonistic_comparison( + current_results: Dict[str, Dict[str, Any]], + baseline_results: Dict[str, Dict[str, Any]] + ) -> str: + """Format antagonistic benchmark comparison results.""" + lines = [] + lines.append("=" * 100) + lines.append("Antagonistic Pickle Benchmark Comparison (Memory DoS Protection)") + lines.append("=" * 100) + lines.append("") + lines.append("Legend: Current vs Baseline | Memory Change (- is better, shows memory saved)") + lines.append("") + lines.append("This compares TWO types of DoS protection:") + lines.append(" 1. Truncated data → Baseline allocates full claimed size, Current uses chunked reading") + lines.append(" 2. Sparse memo → Baseline uses huge arrays, Current uses dict-based memo") + lines.append("") + + # Track statistics + truncated_memory_changes = [] + sparse_memory_changes = [] + + # Sort size keys numerically + for size_key in sorted(current_results.keys(), key=_extract_size_mb): + if size_key not in baseline_results: + continue + + lines.append(f"\n{size_key} Comparison") + lines.append("-" * 100) + + current_tests = current_results[size_key] + baseline_tests = baseline_results[size_key] + + for test_name in sorted(current_tests.keys()): + if test_name not in baseline_tests: + continue + + curr = current_tests[test_name] + base = baseline_tests[test_name] + + curr_peak_mb = curr['peak_memory_mb'] + base_peak_mb = base['peak_memory_mb'] + expected_outcome = curr.get('expected_outcome', 'failure') + + mem_change = Comparator.calculate_change(base_peak_mb, curr_peak_mb) + mem_saved_mb = base_peak_mb - curr_peak_mb + + lines.append(f"\n {curr['test_name']}") + lines.append(f" Memory: {curr_peak_mb:6.2f}MB vs {base_peak_mb:6.2f}MB | " + f"{mem_change:+6.1f}% ({mem_saved_mb:+.2f}MB saved)") + + # Track based on test type + if expected_outcome == 'success': + sparse_memory_changes.append(mem_change) + if curr.get('baseline_note'): + lines.append(f" Note: {curr['baseline_note']}") + else: + truncated_memory_changes.append(mem_change) + claimed_mb = curr.get('claimed_mb', 'N/A') + if claimed_mb != 'N/A': + lines.append(f" Claimed: {claimed_mb:,}MB") + + # Show status + curr_status = curr.get('error_type', 'Unknown') + base_status = base.get('error_type', 'Unknown') + if curr_status != base_status: + lines.append(f" Status: {curr_status} (baseline: {base_status})") + else: + lines.append(f" Status: {curr_status}") + + lines.append("\n" + "=" * 100) + lines.append("\nSummary:") + lines.append("") + + if truncated_memory_changes: + lines.append(" Truncated Data Protection (chunked reading):") + lines.append(f" Mean memory change: {statistics.mean(truncated_memory_changes):+.1f}%") + lines.append(f" Median memory change: {statistics.median(truncated_memory_changes):+.1f}%") + avg_change = statistics.mean(truncated_memory_changes) + if avg_change < -50: + lines.append(f" Result: ✓ Dramatic memory reduction ({avg_change:.1f}%) - DoS protection working!") + elif avg_change < 0: + lines.append(f" Result: ✓ Memory reduced ({avg_change:.1f}%)") + else: + lines.append(f" Result: ⚠ Memory increased ({avg_change:.1f}%) - unexpected!") + lines.append("") + + if sparse_memory_changes: + lines.append(" Sparse Memo Protection (dict-based memo):") + lines.append(f" Mean memory change: {statistics.mean(sparse_memory_changes):+.1f}%") + lines.append(f" Median memory change: {statistics.median(sparse_memory_changes):+.1f}%") + avg_change = statistics.mean(sparse_memory_changes) + if avg_change < -50: + lines.append(f" Result: ✓ Dramatic memory reduction ({avg_change:.1f}%) - Dict optimization working!") + elif avg_change < 0: + lines.append(f" Result: ✓ Memory reduced ({avg_change:.1f}%)") + else: + lines.append(f" Result: ⚠ Memory increased ({avg_change:.1f}%) - unexpected!") + + lines.append("") + lines.append("=" * 100) + return "\n".join(lines) + + +class Reporter: + """Format and display benchmark results.""" + + @staticmethod + def format_text(results: Dict[str, Dict[str, Any]]) -> str: + """Format results as readable text.""" + lines = [] + lines.append("=" * 80) + lines.append("Pickle Unpickling Benchmark Results") + lines.append("=" * 80) + lines.append("") + + for size_key, tests in results.items(): + lines.append(f"\n{size_key} Test Results") + lines.append("-" * 80) + + for test_name, data in tests.items(): + lines.append(f"\n Test: {data['test_name']}") + lines.append(f" Type: {data['object_type']}") + lines.append(f" Pickle size: {data['pickle_size_mb']:.2f} MB") + lines.append(f" Time (mean): {data['time']['mean']*1000:.2f} ms") + lines.append(f" Time (stdev): {data['time']['stdev']*1000:.2f} ms") + lines.append(f" Peak memory: {data['memory_peak_mb']:.2f} MB") + lines.append(f" Protocol: {data['protocol']}") + + lines.append("\n" + "=" * 80) + return "\n".join(lines) + + @staticmethod + def format_markdown(results: Dict[str, Dict[str, Any]]) -> str: + """Format results as markdown table.""" + lines = [] + lines.append("# Pickle Unpickling Benchmark Results\n") + + for size_key, tests in results.items(): + lines.append(f"## {size_key}\n") + lines.append("| Test | Type | Pickle Size (MB) | Time (ms) | Stdev (ms) | Peak Memory (MB) |") + lines.append("|------|------|------------------|-----------|------------|------------------|") + + for test_name, data in tests.items(): + lines.append( + f"| {data['test_name']} | " + f"{data['object_type']} | " + f"{data['pickle_size_mb']:.2f} | " + f"{data['time']['mean']*1000:.2f} | " + f"{data['time']['stdev']*1000:.2f} | " + f"{data['memory_peak_mb']:.2f} |" + ) + lines.append("") + + return "\n".join(lines) + + @staticmethod + def format_json(results: Dict[str, Dict[str, Any]]) -> str: + """Format results as JSON.""" + import json + return json.dumps(results, indent=2) + + @staticmethod + def format_antagonistic(results: Dict[str, Dict[str, Any]]) -> str: + """Format antagonistic benchmark results.""" + lines = [] + lines.append("=" * 100) + lines.append("Antagonistic Pickle Benchmark (Memory DoS Protection Test)") + lines.append("=" * 100) + lines.append("") + lines.append("This benchmark tests TWO types of DoS protection:") + lines.append(" 1. Truncated data attacks → Expect FAILURE with minimal memory before failure") + lines.append(" 2. Sparse memo attacks → Expect SUCCESS with dict-based memo (vs huge array)") + lines.append("") + + # Sort size keys numerically + for size_key in sorted(results.keys(), key=_extract_size_mb): + tests = results[size_key] + + # Determine test type from first test + if tests: + first_test = next(iter(tests.values())) + expected_outcome = first_test.get('expected_outcome', 'failure') + claimed_mb = first_test.get('claimed_mb', 'N/A') + + # Header varies by test type + if "Sparse Memo" in size_key: + lines.append(f"\n{size_key}") + lines.append("-" * 100) + elif "Multi-Claim" in size_key: + lines.append(f"\n{size_key}") + lines.append("-" * 100) + elif claimed_mb != 'N/A': + lines.append(f"\n{size_key} Claimed (actual: 1KB) - Expect Failure") + lines.append("-" * 100) + else: + lines.append(f"\n{size_key}") + lines.append("-" * 100) + + for test_name, data in tests.items(): + peak_mb = data['peak_memory_mb'] + claimed = data.get('claimed_mb', 'N/A') + expected_outcome = data.get('expected_outcome', 'failure') + succeeded = data.get('succeeded', False) + baseline_note = data.get('baseline_note', '') + + lines.append(f" {data['test_name']}") + + # Format output based on test type + if expected_outcome == 'success': + # Sparse memo test - show success with dict + status_icon = "✓" if succeeded else "✗" + lines.append(f" Peak memory: {peak_mb:8.2f} MB {status_icon}") + lines.append(f" Status: {data['error_type']}") + if baseline_note: + lines.append(f" {baseline_note}") + else: + # Truncated data test - show savings before failure + if claimed != 'N/A': + saved_mb = claimed - peak_mb + savings_pct = (saved_mb / claimed * 100) if claimed > 0 else 0 + lines.append(f" Peak memory: {peak_mb:8.2f} MB (claimed: {claimed:,} MB, saved: {saved_mb:.2f} MB, {savings_pct:.1f}%)") + else: + lines.append(f" Peak memory: {peak_mb:8.2f} MB") + lines.append(f" Status: {data['error_type']}") + + lines.append("\n" + "=" * 100) + + # Calculate statistics by test type + truncated_claimed = 0 + truncated_peak = 0 + truncated_count = 0 + + sparse_peak_total = 0 + sparse_count = 0 + + for size_key, tests in results.items(): + for test_name, data in tests.items(): + expected_outcome = data.get('expected_outcome', 'failure') + + if expected_outcome == 'failure': + # Truncated data test + claimed = data.get('claimed_mb', 0) + if claimed != 'N/A' and claimed > 0: + truncated_claimed += claimed + truncated_peak += data['peak_memory_mb'] + truncated_count += 1 + else: + # Sparse memo test + sparse_peak_total += data['peak_memory_mb'] + sparse_count += 1 + + lines.append("\nSummary:") + lines.append("") + + if truncated_count > 0: + avg_claimed = truncated_claimed / truncated_count + avg_peak = truncated_peak / truncated_count + avg_saved = avg_claimed - avg_peak + avg_savings_pct = (avg_saved / avg_claimed * 100) if avg_claimed > 0 else 0 + + lines.append(" Truncated Data Protection (chunked reading):") + lines.append(f" Average claimed: {avg_claimed:,.1f} MB") + lines.append(f" Average peak: {avg_peak:,.2f} MB") + lines.append(f" Average saved: {avg_saved:,.2f} MB ({avg_savings_pct:.1f}% reduction)") + lines.append(f" Status: ✓ Fails fast with minimal memory") + lines.append("") + + if sparse_count > 0: + avg_sparse_peak = sparse_peak_total / sparse_count + lines.append(" Sparse Memo Protection (dict-based memo):") + lines.append(f" Average peak: {avg_sparse_peak:,.2f} MB") + lines.append(f" Status: ✓ Succeeds with dict (vs GB-sized arrays without PR)") + lines.append(f" Note: Compare with --baseline to see actual memory savings") + + lines.append("") + lines.append("=" * 100) + return "\n".join(lines) + + +def main(): + parser = argparse.ArgumentParser( + description="Benchmark pickle unpickling performance for large objects" + ) + parser.add_argument( + '--sizes', + type=int, + nargs='+', + default=None, + metavar='MiB', + help=f'Object sizes to test in MiB (default: {DEFAULT_SIZES_MIB})' + ) + parser.add_argument( + '--protocol', + type=int, + default=5, + choices=[0, 1, 2, 3, 4, 5], + help='Pickle protocol version (default: 5)' + ) + parser.add_argument( + '--iterations', + type=int, + default=3, + help='Number of benchmark iterations (default: 3)' + ) + parser.add_argument( + '--format', + choices=['text', 'markdown', 'json'], + default='text', + help='Output format (default: text)' + ) + parser.add_argument( + '--baseline', + type=str, + metavar='PYTHON', + help='Path to baseline Python interpreter for comparison (e.g., ../main-build/python)' + ) + parser.add_argument( + '--antagonistic', + action='store_true', + help='Run antagonistic/malicious pickle tests (DoS protection benchmark)' + ) + + args = parser.parse_args() + + # Handle antagonistic mode + if args.antagonistic: + # Antagonistic mode uses claimed sizes in MB, not actual data sizes + if args.sizes is None: + claimed_sizes_mb = AntagonisticTestSuite.DEFAULT_ANTAGONISTIC_SIZES_MB + else: + claimed_sizes_mb = args.sizes + + print(f"Running ANTAGONISTIC pickle benchmark (DoS protection test)...") + print(f"Claimed sizes: {claimed_sizes_mb} MiB (actual data: 1KB each)") + print(f"NOTE: These pickles will FAIL to unpickle (expected)") + print() + + # Run antagonistic benchmark suite + suite = AntagonisticTestSuite(claimed_sizes_mb) + results = suite.run_all_tests() + + # Format and display results + if args.baseline: + # Verify baseline Python exists + baseline_path = Path(args.baseline) + if not baseline_path.exists(): + print(f"Error: Baseline Python not found: {args.baseline}", file=sys.stderr) + return 1 + + # Run baseline benchmark + baseline_results = Comparator.run_baseline_benchmark(args.baseline, args) + + # Show comparison + comparison_output = Comparator.format_antagonistic_comparison(results, baseline_results) + print(comparison_output) + else: + # Format and display results + output = _format_output(results, args.format, is_antagonistic=True) + print(output) + + else: + # Normal mode: legitimate pickle benchmarks + # Convert sizes from MiB to bytes + if args.sizes is None: + sizes_bytes = DEFAULT_SIZES + else: + sizes_bytes = [size * (1 << 20) for size in args.sizes] + + print(f"Running pickle benchmark with protocol {args.protocol}...") + print(f"Test sizes: {[f'{s/(1<<20):.2f}MiB' for s in sizes_bytes]}") + print(f"Iterations per test: {args.iterations}") + print() + + # Run benchmark suite + suite = TestSuite(sizes_bytes, args.protocol, args.iterations) + results = suite.run_all_tests() + + # If baseline comparison requested, run baseline and compare + if args.baseline: + # Verify baseline Python exists + baseline_path = Path(args.baseline) + if not baseline_path.exists(): + print(f"Error: Baseline Python not found: {args.baseline}", file=sys.stderr) + return 1 + + # Run baseline benchmark + baseline_results = Comparator.run_baseline_benchmark(args.baseline, args) + + # Show comparison + comparison_output = Comparator.format_comparison(results, baseline_results) + print(comparison_output) + + else: + # Format and display results + output = _format_output(results, args.format, is_antagonistic=False) + print(output) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/Tools/pixi-packages/README.md b/Tools/pixi-packages/README.md new file mode 100644 index 00000000000000..d818fddaac6a1e --- /dev/null +++ b/Tools/pixi-packages/README.md @@ -0,0 +1,55 @@ +# CPython Pixi packages + +This directory contains definitions for [Pixi +packages](https://pixi.sh/latest/reference/pixi_manifest/#the-package-section) which can +be built from the CPython source code. + +Downstream developers can make use of these packages by adding them as Git dependencies +in a [Pixi workspace](https://pixi.sh/latest/first_workspace/), like: + +```toml +[dependencies] +python.git = "https://github.com/python/cpython" +python.subdirectory = "Tools/pixi-packages/asan" +``` + +This is particularly useful when developers need to build CPython from source +(for example, for an ASan or TSan-instrumented build), as it does not require any manual +clone or build steps. Instead, Pixi will automatically handle both the build +and installation of the package. + +Each package definition is contained in a subdirectory, but they share the build script +`build.sh` in this directory. Currently defined package variants: + +- `default` +- `freethreading` +- `asan`: ASan-instrumented build +- `tsan-freethreading`: TSan-instrumented free-threading build + +## Maintenance + +- Keep the `abi_tag` and `version` fields in each `variants.yaml` up to date with the + Python version +- Update `build.sh` for any breaking changes in the `configure` and `make` workflow + +## Opportunities for future improvement + +- More package variants (such as UBSan) +- Support for Windows +- Using a single `pixi.toml` for all package variants is blocked on + [pixi#5248](https://github.com/prefix-dev/pixi/issues/5248) + +## Troubleshooting + +TSan builds may crash on Linux with +``` +FATAL: ThreadSanitizer: unexpected memory mapping 0x7977bd072000-0x7977bd500000 +``` +To fix it, try reducing `mmap_rnd_bits`: + +```console +$ sudo sysctl vm.mmap_rnd_bits +vm.mmap_rnd_bits = 32 # too high for TSan +$ sudo sysctl vm.mmap_rnd_bits=28 # reduce it +vm.mmap_rnd_bits = 28 +``` diff --git a/Tools/pixi-packages/asan/pixi.toml b/Tools/pixi-packages/asan/pixi.toml new file mode 100644 index 00000000000000..bf9841e18677ca --- /dev/null +++ b/Tools/pixi-packages/asan/pixi.toml @@ -0,0 +1,15 @@ +# NOTE: Please always only modify default/pixi.toml and then run clone-recipe.sh to +# propagate the changes to the other variants. + +[workspace] +channels = ["https://prefix.dev/conda-forge"] +platforms = ["linux-64", "linux-aarch64", "osx-64", "osx-arm64"] +preview = ["pixi-build"] +requires-pixi = ">=0.66.0" + +[package.build.backend] +name = "pixi-build-rattler-build" +version = "*" + +[package.build.config] +recipe = "../default/recipe.yaml" diff --git a/Tools/pixi-packages/asan/variants.yaml b/Tools/pixi-packages/asan/variants.yaml new file mode 100644 index 00000000000000..2404948457e6bb --- /dev/null +++ b/Tools/pixi-packages/asan/variants.yaml @@ -0,0 +1,6 @@ +variant: + - asan +abi_tag: + - asan_cp315 +version: + - 3.15 diff --git a/Tools/pixi-packages/build.sh b/Tools/pixi-packages/build.sh new file mode 100644 index 00000000000000..7e22e6243a5f77 --- /dev/null +++ b/Tools/pixi-packages/build.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +echo "PYTHON_VARIANT: ${PYTHON_VARIANT}" + +if [[ "${PYTHON_VARIANT}" == "freethreading" ]]; then + CONFIGURE_EXTRA="--disable-gil" +elif [[ "${PYTHON_VARIANT}" == "asan" ]]; then + CONFIGURE_EXTRA="--with-address-sanitizer" + export ASAN_OPTIONS="strict_init_order=true" +elif [[ "${PYTHON_VARIANT}" == "tsan-freethreading" ]]; then + CONFIGURE_EXTRA="--disable-gil --with-thread-sanitizer" + export TSAN_OPTIONS="suppressions=${SRC_DIR}/Tools/tsan/suppressions_free_threading.txt" +elif [[ "${PYTHON_VARIANT}" == "default" ]]; then + CONFIGURE_EXTRA="" +else + echo "Unknown PYTHON_VARIANT: ${PYTHON_VARIANT}" + exit 1 +fi + +# rattler-build by default set a target of 10.9 +# override it to at least 10.12 +case ${MACOSX_DEPLOYMENT_TARGET:-10.12} in + 10.12|10.13|10.14|10.15|10.16) + ;; + 10.*) + export CPPFLAGS=${CPPFLAGS/-mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}/-mmacosx-version-min=10.12} + export MACOSX_DEPLOYMENT_TARGET=10.12 + ;; + *) + ;; +esac + +BUILD_DIR="../build_${PYTHON_VARIANT}" +mkdir -p "${BUILD_DIR}" +cd "${BUILD_DIR}" + +if [[ -f configure-done ]]; then + echo "Skipping configure step, already done." +else + "${SRC_DIR}/configure" \ + --prefix="${PREFIX}" \ + --oldincludedir="${BUILD_PREFIX}/${HOST}/sysroot/usr/include" \ + --enable-shared \ + --srcdir="${SRC_DIR}" \ + --with-system-expat \ + ${CONFIGURE_EXTRA} +fi + +touch configure-done + +make -j"${CPU_COUNT}" install +ln -sf "${PREFIX}/bin/python3" "${PREFIX}/bin/python" diff --git a/Tools/pixi-packages/clone-recipe.sh b/Tools/pixi-packages/clone-recipe.sh new file mode 100755 index 00000000000000..25ceaf85c35f56 --- /dev/null +++ b/Tools/pixi-packages/clone-recipe.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Please always only modify default/recipe.yaml and default/pixi.toml and then run this +# script to propagate the changes to the other variants. +set -o errexit +cd "$(dirname "$0")" + +for variant in asan freethreading tsan-freethreading; do + cp -av default/pixi.toml ${variant}/ +done diff --git a/Tools/pixi-packages/default/pixi.toml b/Tools/pixi-packages/default/pixi.toml new file mode 100644 index 00000000000000..bf9841e18677ca --- /dev/null +++ b/Tools/pixi-packages/default/pixi.toml @@ -0,0 +1,15 @@ +# NOTE: Please always only modify default/pixi.toml and then run clone-recipe.sh to +# propagate the changes to the other variants. + +[workspace] +channels = ["https://prefix.dev/conda-forge"] +platforms = ["linux-64", "linux-aarch64", "osx-64", "osx-arm64"] +preview = ["pixi-build"] +requires-pixi = ">=0.66.0" + +[package.build.backend] +name = "pixi-build-rattler-build" +version = "*" + +[package.build.config] +recipe = "../default/recipe.yaml" diff --git a/Tools/pixi-packages/default/recipe.yaml b/Tools/pixi-packages/default/recipe.yaml new file mode 100644 index 00000000000000..30d0d5a2ed2e04 --- /dev/null +++ b/Tools/pixi-packages/default/recipe.yaml @@ -0,0 +1,94 @@ +# NOTE: Please always only modify default/recipe.yaml and then run clone-recipe.sh to +# propagate the changes to the other variants. + +context: + # Keep up to date + freethreading_tag: ${{ "t" if "freethreading" in variant else "" }} + +recipe: + name: python + +source: + - path: ../../.. + +outputs: +- package: + name: python_abi + version: ${{ version }} + build: + string: "0_${{ abi_tag }}" + requirements: + run_constraints: + - python ${{ version }}.* *_${{ abi_tag }} + +- package: + name: python + version: ${{ version }} + build: + string: "0_${{ abi_tag }}" + files: + exclude: + - "*.o" + script: + file: ../build.sh + env: + PYTHON_VARIANT: ${{ variant }} + python: + site_packages_path: "lib/python${{ version }}${{ freethreading_tag }}/site-packages" + + # derived from https://github.com/conda-forge/python-feedstock/blob/main/recipe/meta.yaml + requirements: + build: + - ${{ compiler('c') }} + - ${{ compiler('cxx') }} + # Note that we are not using stdlib arguments which means the packages + # are built for the build settings and are not relocatable to a different + # machine that has a older system version. (eg: macOS/glibc version) + - make + - pkg-config + # configure script looks for llvm-ar for lto + - if: osx + then: + - llvm-tools + + host: + - bzip2 + - sqlite + - liblzma-devel + - zlib + - zstd + - openssl + - readline + - tk + # These two are just to get the headers needed for tk.h, but is unused + - xorg-libx11 + - xorg-xorgproto + - ncurses + - libffi + - if: linux + then: + - libuuid + - libmpdec-devel + - expat + - if: linux and "san" in variant + then: + - libsanitizer + - if: osx and "san" in variant + then: + - libcompiler-rt + + ignore_run_exports: + from_package: + - xorg-libx11 + - xorg-xorgproto + + run_exports: + noarch: + - python + weak: + - python_abi ${{ version }}.* *_${{ abi_tag }} + +about: + homepage: https://www.python.org/ + license: Python-2.0 + license_file: LICENSE diff --git a/Tools/pixi-packages/default/variants.yaml b/Tools/pixi-packages/default/variants.yaml new file mode 100644 index 00000000000000..f66e9e7a2226ba --- /dev/null +++ b/Tools/pixi-packages/default/variants.yaml @@ -0,0 +1,6 @@ +variant: + - default +abi_tag: + - cp315 +version: + - 3.15 diff --git a/Tools/pixi-packages/freethreading/pixi.toml b/Tools/pixi-packages/freethreading/pixi.toml new file mode 100644 index 00000000000000..bf9841e18677ca --- /dev/null +++ b/Tools/pixi-packages/freethreading/pixi.toml @@ -0,0 +1,15 @@ +# NOTE: Please always only modify default/pixi.toml and then run clone-recipe.sh to +# propagate the changes to the other variants. + +[workspace] +channels = ["https://prefix.dev/conda-forge"] +platforms = ["linux-64", "linux-aarch64", "osx-64", "osx-arm64"] +preview = ["pixi-build"] +requires-pixi = ">=0.66.0" + +[package.build.backend] +name = "pixi-build-rattler-build" +version = "*" + +[package.build.config] +recipe = "../default/recipe.yaml" diff --git a/Tools/pixi-packages/freethreading/variants.yaml b/Tools/pixi-packages/freethreading/variants.yaml new file mode 100644 index 00000000000000..022833d04c3821 --- /dev/null +++ b/Tools/pixi-packages/freethreading/variants.yaml @@ -0,0 +1,6 @@ +variant: + - freethreading +abi_tag: + - cp315t +version: + - 3.15 diff --git a/Tools/pixi-packages/tsan-freethreading/pixi.toml b/Tools/pixi-packages/tsan-freethreading/pixi.toml new file mode 100644 index 00000000000000..bf9841e18677ca --- /dev/null +++ b/Tools/pixi-packages/tsan-freethreading/pixi.toml @@ -0,0 +1,15 @@ +# NOTE: Please always only modify default/pixi.toml and then run clone-recipe.sh to +# propagate the changes to the other variants. + +[workspace] +channels = ["https://prefix.dev/conda-forge"] +platforms = ["linux-64", "linux-aarch64", "osx-64", "osx-arm64"] +preview = ["pixi-build"] +requires-pixi = ">=0.66.0" + +[package.build.backend] +name = "pixi-build-rattler-build" +version = "*" + +[package.build.config] +recipe = "../default/recipe.yaml" diff --git a/Tools/pixi-packages/tsan-freethreading/variants.yaml b/Tools/pixi-packages/tsan-freethreading/variants.yaml new file mode 100644 index 00000000000000..6ed09fcc9b656b --- /dev/null +++ b/Tools/pixi-packages/tsan-freethreading/variants.yaml @@ -0,0 +1,6 @@ +variant: + - tsan-freethreading +abi_tag: + - tsan_cp315t +version: + - 3.15 diff --git a/Tools/requirements-dev.txt b/Tools/requirements-dev.txt index 0beaab2d3e7157..46381ea58a1238 100644 --- a/Tools/requirements-dev.txt +++ b/Tools/requirements-dev.txt @@ -1,7 +1,7 @@ # Requirements file for external linters and checks we run on # Tools/clinic, Tools/cases_generator/, and Tools/peg_generator/ in CI -mypy==1.16.1 +mypy==2.1.0 # needed for peg_generator: -types-psutil==7.0.0.20250601 -types-setuptools==80.9.0.20250529 +types-psutil==7.2.2.20260508 +types-setuptools==82.0.0.20260508 diff --git a/Tools/requirements-hypothesis.txt b/Tools/requirements-hypothesis.txt index e5deac497fbe3f..8ecf796ec7343a 100644 --- a/Tools/requirements-hypothesis.txt +++ b/Tools/requirements-hypothesis.txt @@ -1,4 +1,4 @@ # Requirements file for hypothesis that # we use to run our property-based tests in CI. -hypothesis==6.135.26 +hypothesis==6.151.9 diff --git a/Tools/scripts/README b/Tools/scripts/README index a078bfbf662a37..4e52cda38e8d88 100644 --- a/Tools/scripts/README +++ b/Tools/scripts/README @@ -1,8 +1,6 @@ This directory contains a collection of executable Python scripts that are useful while building, extending or managing Python. -checkpip.py Checks the version of the projects bundled in ensurepip - are the latest available combinerefs.py A helper for analyzing PYTHONDUMPREFS output divmod_threshold.py Determine threshold for switching from longobject.c divmod to _pylong.int_divmod() diff --git a/Tools/scripts/checkpip.py b/Tools/scripts/checkpip.py deleted file mode 100755 index a4a9ddfa6f324a..00000000000000 --- a/Tools/scripts/checkpip.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -""" -Checks that the version of the projects bundled in ensurepip are the latest -versions available. -""" -import ensurepip -import json -import urllib.request -import sys - - -def main(): - outofdate = False - - for project, version in ensurepip._PROJECTS: - data = json.loads(urllib.request.urlopen( - "https://pypi.org/pypi/{}/json".format(project), - cadefault=True, - ).read().decode("utf8")) - upstream_version = data["info"]["version"] - - if version != upstream_version: - outofdate = True - print("The latest version of {} on PyPI is {}, but ensurepip " - "has {}".format(project, upstream_version, version)) - - if outofdate: - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/Tools/ssl/make_ssl_data.py b/Tools/ssl/make_ssl_data.py index 286f0e5f54a779..439dbaf882db68 100755 --- a/Tools/ssl/make_ssl_data.py +++ b/Tools/ssl/make_ssl_data.py @@ -17,8 +17,8 @@ git tag --list 'openssl-*' git switch --detach openssl-3.4.1 -After generating the definitions, compare the result with newest pre-existing file. -You can use a command like: +After generating the definitions, compare the result with the newest +pre-existing file. You can use a command like: git diff --no-index Modules/_ssl_data_340.h Modules/_ssl_data_341.h diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py index f4c8fde8346fd9..1a213187b897d1 100755 --- a/Tools/ssl/multissltests.py +++ b/Tools/ssl/multissltests.py @@ -44,14 +44,17 @@ OPENSSL_OLD_VERSIONS = [ "1.1.1w", + "3.1.8", + "3.2.6", + "3.3.7", ] OPENSSL_RECENT_VERSIONS = [ - "3.0.16", - "3.1.8", - "3.2.4", - "3.3.3", - "3.4.1", + "3.0.21", + "3.4.6", + "3.5.7", + "3.6.3", + "4.0.1", # See make_ssl_data.py for notes on adding a new version. ] @@ -62,7 +65,7 @@ ] AWSLC_RECENT_VERSIONS = [ - "1.55.0", + "5.0.0", ] # store files in ../multissl @@ -74,8 +77,7 @@ parser = argparse.ArgumentParser( prog='multissl', description=( - "Run CPython tests with multiple cryptography libraries" - "versions." + "Run CPython tests with multiple cryptography libraries/versions." ), ) parser.add_argument( @@ -107,7 +109,10 @@ ).format(LIBRESSL_RECENT_VERSIONS, LIBRESSL_OLD_VERSIONS) ) parser.add_argument( + '--aws-lc', + # Soft-deprecated alias '--awslc', + dest='awslc', nargs='+', default=(), help=( @@ -306,7 +311,7 @@ def _unpack_src(self): raise ValueError(member.name, base) member.name = member.name[len(base):].lstrip('/') log.info("Unpacking files to {}".format(self.build_dir)) - tf.extractall(self.build_dir, members) + tf.extractall(self.build_dir, members, filter='data') def _build_src(self, config_args=()): """Now build openssl""" @@ -425,9 +430,11 @@ class BuildOpenSSL(AbstractBuilder): def _post_install(self): if self.version.startswith("3."): self._post_install_3xx() + elif self.version.startswith("4."): + self._post_install_4xx() def _build_src(self, config_args=()): - if self.version.startswith("3."): + if self.version.startswith(("3.", "4.")): config_args += ("enable-fips",) super()._build_src(config_args) @@ -443,6 +450,9 @@ def _post_install_3xx(self): lib64 = self.lib_dir + "64" os.symlink(lib64, self.lib_dir) + def _post_install_4xx(self): + self._post_install_3xx() + @property def short_version(self): """Short version for OpenSSL download URL""" diff --git a/Tools/tsan/suppressions.txt b/Tools/tsan/suppressions.txt index 6bda5ecd570889..7a32f4e0ba26c5 100644 --- a/Tools/tsan/suppressions.txt +++ b/Tools/tsan/suppressions.txt @@ -1,5 +1,10 @@ # This file contains suppressions for the default (with GIL) build. -# reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions +# +# Reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions +# +# When adding a suppression, include a comment referencing a GitHub issue +# that describes how to reproduce the race and includes the relevant TSan +# output. -# https://gist.github.com/mpage/daaf32b39180c1989572957b943eb665 -thread:pthread_create +# There are currently no active suppressions. This file is kept so tooling +# that expects it can still find it. diff --git a/Tools/tsan/suppressions_free_threading.txt b/Tools/tsan/suppressions_free_threading.txt index 93421b623b92f9..f7ce11792e51b9 100644 --- a/Tools/tsan/suppressions_free_threading.txt +++ b/Tools/tsan/suppressions_free_threading.txt @@ -1,50 +1,12 @@ -# This file contains suppressions for the free-threaded build. It contains the -# suppressions for the default build and additional suppressions needed only in +# This file contains suppressions for the free-threaded build. It includes the +# default build suppressions plus any additional suppressions needed only in # the free-threaded build. # -# reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions - -## Free-threaded suppressions - - -# These entries are for warnings that trigger in a library function, as called -# by a CPython function. - -# These warnings trigger directly in a CPython function. - -race_top:dump_traceback -race_top:fatal_error -race_top:_PyFrame_GetCode -race_top:_PyFrame_Initialize -race_top:_PyObject_TryGetInstanceAttribute -race_top:PyUnstable_InterpreterFrame_GetLine -race_top:write_thread_id - -# gh-129068: race on shared range iterators (test_free_threading.test_zip.ZipThreading.test_threading) -race_top:rangeiter_next - -# gh-129748: test.test_free_threading.test_slots.TestSlots.test_object -race_top:mi_block_set_nextx - -# https://gist.github.com/mpage/6962e8870606cfc960e159b407a0cb40 -thread:pthread_create - -# Range iteration is not thread-safe yet (issue #129068) -race_top:rangeiter_next - -# List resizing happens through different paths ending in memcpy or memmove -# (for efficiency), which will probably need to rewritten as explicit loops -# of ptr-sized copies to be thread-safe. (Issue #129069) -race:list_ass_slice_lock_held -race:list_inplace_repeat_lock_held - -# PyObject_Realloc internally does memcpy which isn't atomic so can race -# with non-locking reads. See #132070 -race:PyObject_Realloc +# Reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions +# +# When adding a suppression, include a comment referencing a GitHub issue +# that describes how to reproduce the race and includes the relevant TSan +# output. -# gh-133467. Some of these could be hard to trigger. -race_top:_Py_slot_tp_getattr_hook -race_top:slot_tp_descr_get -race_top:type_set_name -race_top:set_tp_bases -race_top:type_set_bases_unlocked +# There are currently no active suppressions. This file is kept so tooling +# that expects it can still find it. diff --git a/Tools/tz/zdump.py b/Tools/tz/zdump.py deleted file mode 100644 index 39de0a416d0251..00000000000000 --- a/Tools/tz/zdump.py +++ /dev/null @@ -1,81 +0,0 @@ -import sys -import os -import struct -from array import array -from collections import namedtuple -from datetime import datetime - -ttinfo = namedtuple('ttinfo', ['tt_gmtoff', 'tt_isdst', 'tt_abbrind']) - -class TZInfo: - def __init__(self, transitions, type_indices, ttis, abbrs): - self.transitions = transitions - self.type_indices = type_indices - self.ttis = ttis - self.abbrs = abbrs - - @classmethod - def fromfile(cls, fileobj): - if fileobj.read(4).decode() != "TZif": - raise ValueError("not a zoneinfo file") - fileobj.seek(20) - header = fileobj.read(24) - tzh = (tzh_ttisgmtcnt, tzh_ttisstdcnt, tzh_leapcnt, - tzh_timecnt, tzh_typecnt, tzh_charcnt) = struct.unpack(">6l", header) - transitions = array('i') - transitions.fromfile(fileobj, tzh_timecnt) - if sys.byteorder != 'big': - transitions.byteswap() - - type_indices = array('B') - type_indices.fromfile(fileobj, tzh_timecnt) - - ttis = [] - for i in range(tzh_typecnt): - ttis.append(ttinfo._make(struct.unpack(">lbb", fileobj.read(6)))) - - abbrs = fileobj.read(tzh_charcnt) - - self = cls(transitions, type_indices, ttis, abbrs) - self.tzh = tzh - - return self - - def dump(self, stream, start=None, end=None): - for j, (trans, i) in enumerate(zip(self.transitions, self.type_indices)): - utc = datetime.utcfromtimestamp(trans) - tti = self.ttis[i] - lmt = datetime.utcfromtimestamp(trans + tti.tt_gmtoff) - abbrind = tti.tt_abbrind - abbr = self.abbrs[abbrind:self.abbrs.find(0, abbrind)].decode() - if j > 0: - prev_tti = self.ttis[self.type_indices[j - 1]] - shift = " %+g" % ((tti.tt_gmtoff - prev_tti.tt_gmtoff) / 3600) - else: - shift = '' - print("%s UTC = %s %-5s isdst=%d" % (utc, lmt, abbr, tti[1]) + shift, file=stream) - - @classmethod - def zonelist(cls, zonedir='/usr/share/zoneinfo'): - zones = [] - for root, _, files in os.walk(zonedir): - for f in files: - p = os.path.join(root, f) - with open(p, 'rb') as o: - magic = o.read(4) - if magic == b'TZif': - zones.append(p[len(zonedir) + 1:]) - return zones - -if __name__ == '__main__': - if len(sys.argv) < 2: - zones = TZInfo.zonelist() - for z in zones: - print(z) - sys.exit() - filepath = sys.argv[1] - if not filepath.startswith('/'): - filepath = os.path.join('/usr/share/zoneinfo', filepath) - with open(filepath, 'rb') as fileobj: - tzi = TZInfo.fromfile(fileobj) - tzi.dump(sys.stdout) diff --git a/Tools/ubsan/suppressions.txt b/Tools/ubsan/suppressions.txt new file mode 100644 index 00000000000000..a00e256b333618 --- /dev/null +++ b/Tools/ubsan/suppressions.txt @@ -0,0 +1,25 @@ +# This file contains suppressions for the UndefinedBehaviour sanitizer. +# +# Reference: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#runtime-suppressions +# +# When adding a suppression, include a comment referencing a GitHub issue +# that describes how to reproduce the race and includes the relevant UBSan +# output. + +# Objects/object.c:97:5: runtime error: member access within null pointer of type 'PyThreadState' (aka 'struct _ts') +null:Objects/object.c + +# Modules/_ctypes/cfield.c:644:1: runtime error: left shift of 1 by 63 places cannot be represented in type 'int64_t' (aka 'long') +shift-base:Modules/_ctypes/cfield.c + +# Modules/_ctypes/cfield.c:640:1: runtime error: signed integer overflow: -2147483648 - 1 cannot be represented in type 'int' +signed-integer-overflow:Modules/_ctypes/cfield.c + +# Modules/_zstd/decompressor.c:598:56: runtime error: applying non-zero offset 18446744073709551615 to null pointer +pointer-overflow:Modules/_zstd/decompressor.c + +# Modules/_io/stringio.c:350:24: runtime error: addition of unsigned offset to 0x7fd01ec25850 overflowed to 0x7fd01ec2584c +pointer-overflow:Modules/_io/stringio.c + +# Objects/bytesobject.c:1190:25: runtime error: applying zero offset to null pointer +pointer-overflow:Objects/bytesobject.c diff --git a/Tools/unicode/gen_expat_table.py b/Tools/unicode/gen_expat_table.py new file mode 100644 index 00000000000000..ff08fb4c87dab3 --- /dev/null +++ b/Tools/unicode/gen_expat_table.py @@ -0,0 +1,107 @@ +import sys + + +def info(msg, *args): + print(msg % args, file=sys.stderr) + +def fail(msg, *args): + info(msg, *args) + sys.exit(2) + +# ASCII characters that can appear in a well-formed XML document +# except the characters "$@\^`{}~". +compulsory_chars = (set(b'\t\n\r') | set(range(32, 127))) - set(br'$@\^`{}~') + +def check_compatibility(encoding): + for i in compulsory_chars: + c = chr(i) + b = bytes([i]) + try: + decoded = b.decode(encoding) + except UnicodeDecodeError: + info('Cannot decode byte %#04x (%a)', i, c) + return False + if decoded != c: + info('Incompatible encoding: %#04x (%a) -> %a', i, c, decoded) + return False + return True + +def create_table(encoding): + mapping = [-1] * 256 + + non_bmp = None + for i in range(0x110000): + char = chr(i) + try: + encoded = char.encode(encoding) + except UnicodeEncodeError: + continue + if i >= 0x10000 and non_bmp is None: + non_bmp = i + info('Non-BMP character: %r (U+%04X)', char, i) + length = len(encoded) + k = encoded[0] + v = mapping[k] + if length == 1: + if v == -1: + mapping[k] = i + elif v < 0: + fail('Ambiguous mapping for %#04x: ' + '%r (U+%04X) and %d-byte sequence', + k, c, i, -v) + else: + info('Ambiguous mapping for %#04x: ' + '%r (U+%04X) and %r (U+%04X)', + k, chr(v), v, char, i) + else: + if length > 4: + fail('Too long encoding for %r (U+%04X): %r', + char, i, encoded) + if v == -1: + mapping[k] = -length + elif v != -length: + if v < 0: + fail('Ambiguous mapping for %#04x: ' + '%d-byte sequence and %d-byte sequence %r', + k, -v, length, encoded) + else: + fail('Ambiguous mapping for %#04x: ' + '%r (U+%04X) and %d-byte sequence %r', + k, chr(v), v, length, encoded) + return mapping + +def print_table(mapping): + print(' '*8 + '_expat_decoding_table=(', end='') + if mapping[:128] == list(range(128)): + print('*range(128),') + start = 128 + else: + print() + start = 0 + line = '' + pos = 0 + i = start + while True: + if i % 8 == 0: + if len(line) > 68: + print(' '*12 + line[:pos].rstrip()) + line = line[pos:] + pos = len(line) + if i == 256: + break + v = mapping[i] + if v >= 0: + v = hex(v) + line += f'{v}, ' + i += 1 + print(' '*12 + line.rstrip().rstrip(',') + '),') + + +encoding = sys.argv[1] + +if not check_compatibility(encoding): + print(' '*8 + '_expat_decoding_table=False,') + sys.exit(1) + +mapping = create_table(encoding) +print_table(mapping) diff --git a/Tools/unicode/makeunicodedata.py b/Tools/unicode/makeunicodedata.py index d4cca68c3e3e71..76283d6b794a0b 100644 --- a/Tools/unicode/makeunicodedata.py +++ b/Tools/unicode/makeunicodedata.py @@ -29,6 +29,7 @@ import dataclasses import os import sys +import re import zipfile from functools import partial @@ -44,11 +45,12 @@ # * Doc/library/stdtypes.rst, and # * Doc/library/unicodedata.rst # * Doc/reference/lexical_analysis.rst (three occurrences) -UNIDATA_VERSION = "16.0.0" +UNIDATA_VERSION = "17.0.0" UNICODE_DATA = "UnicodeData%s.txt" COMPOSITION_EXCLUSIONS = "CompositionExclusions%s.txt" EASTASIAN_WIDTH = "EastAsianWidth%s.txt" UNIHAN = "Unihan%s.zip" +DERIVED_BIDI_CLASS = "extracted/DerivedBidiClass%s.txt" DERIVED_CORE_PROPERTIES = "DerivedCoreProperties%s.txt" DERIVEDNORMALIZATION_PROPS = "DerivedNormalizationProps%s.txt" LINE_BREAK = "LineBreak%s.txt" @@ -56,6 +58,9 @@ NAMED_SEQUENCES = "NamedSequences%s.txt" SPECIAL_CASING = "SpecialCasing%s.txt" CASE_FOLDING = "CaseFolding%s.txt" +GRAPHEME_CLUSTER_BREAK = "auxiliary/GraphemeBreakProperty%s.txt" +EMOJI_DATA = "emoji/emoji-data%s.txt" +BLOCKS = "Blocks%s.txt" # Private Use Areas -- in planes 1, 15, 16 PUA_1 = range(0xE000, 0xF900) @@ -77,6 +82,41 @@ "PDF", "EN", "ES", "ET", "AN", "CS", "NSM", "BN", "B", "S", "WS", "ON", "LRI", "RLI", "FSI", "PDI" ] +# https://www.unicode.org/reports/tr44/#BC_Values_Table +BIDI_LONG_NAMES = { + 'Left_To_Right': 'L', + 'Right_To_Left': 'R', + 'Arabic_Letter': 'AL', + 'European_Number': 'EN', + 'European_Separator': 'ES', + 'European_Terminator': 'ET', + 'Arabic_Number': 'AN', + 'Common_Separator': 'CS', + 'Nonspacing_Mark': 'NSM', + 'Boundary_Neutral': 'BN', + 'Paragraph_Separator': 'B', + 'Segment_Separator': 'S', + 'White_Space': 'WS', + 'Other_Neutral': 'ON', + 'Left_To_Right_Embedding': 'LRE', + 'Left_To_Right_Override': 'LRO', + 'Right_To_Left_Embedding': 'RLE', + 'Right_To_Left_Override': 'RLO', + 'Pop_Directional_Format': 'PDF', + 'Left_To_Right_Isolate': 'LRI', + 'Right_To_Left_Isolate': 'RLI', + 'First_Strong_Isolate': 'FSI', + 'Pop_Directional_Isolate': 'PDI', +} + +# "Other" needs to be the first entry, see the comment in makeunicodedata +GRAPHEME_CLUSTER_NAMES = [ 'Other', 'Prepend', 'CR', 'LF', 'Control', + 'Extend', 'Regional_Indicator', 'SpacingMark', 'L', 'V', 'T', 'LV', 'LVT', + 'ZWJ' ] + +# "None" needs to be the first entry, see the comment in makeunicodedata +INDIC_CONJUNCT_BREAK_NAMES = [ 'None', 'Linker', 'Consonant', 'Extend' ] + # "N" needs to be the first entry, see the comment in makeunicodedata EASTASIANWIDTH_NAMES = [ "N", "H", "W", "Na", "A", "F" ] @@ -99,18 +139,13 @@ CASED_MASK = 0x2000 EXTENDED_CASE_MASK = 0x4000 -# these ranges need to match unicodedata.c:is_unified_ideograph -cjk_ranges = [ - ('3400', '4DBF'), # CJK Ideograph Extension A CJK - ('4E00', '9FFF'), # CJK Ideograph - ('20000', '2A6DF'), # CJK Ideograph Extension B - ('2A700', '2B739'), # CJK Ideograph Extension C - ('2B740', '2B81D'), # CJK Ideograph Extension D - ('2B820', '2CEA1'), # CJK Ideograph Extension E - ('2CEB0', '2EBE0'), # CJK Ideograph Extension F - ('2EBF0', '2EE5D'), # CJK Ideograph Extension I - ('30000', '3134A'), # CJK Ideograph Extension G - ('31350', '323AF'), # CJK Ideograph Extension H +# Maps the range names in UnicodeData.txt to prefixes for +# derived names specified by rule NR2. +# Hangul should always be at index 0, since it uses special format. +derived_name_range_names = [ + ("Hangul Syllable", "HANGUL SYLLABLE "), + ("CJK Ideograph", "CJK UNIFIED IDEOGRAPH-"), + ("Tangut Ideograph", "TANGUT IDEOGRAPH-"), ] @@ -124,7 +159,7 @@ def maketables(trace=0): for version in old_versions: print("--- Reading", UNICODE_DATA % ("-"+version), "...") - old_unicode = UnicodeData(version, cjk_check=False) + old_unicode = UnicodeData(version, ideograph_check=False) print(len(list(filter(None, old_unicode.table))), "characters") merge_old_version(version, unicode, old_unicode) @@ -146,7 +181,9 @@ def makeunicodedata(unicode, trace): # EastAsianWidth.txt # see https://unicode.org/reports/tr11/#Unassigned assert EASTASIANWIDTH_NAMES[0] == "N" - dummy = (0, 0, 0, 0, 0, 0) + assert GRAPHEME_CLUSTER_NAMES[0] == "Other" + assert INDIC_CONJUNCT_BREAK_NAMES[0] == "None" + dummy = (0, 0, 0, 0, 0, 0, 0, 0, 0) table = [dummy] cache = {0: dummy} index = [0] * len(unicode.chars) @@ -159,25 +196,27 @@ def makeunicodedata(unicode, trace): for char in unicode.chars: record = unicode.table[char] + eastasianwidth = EASTASIANWIDTH_NAMES.index(unicode.widths[char] or 'N') + graphemebreak = GRAPHEME_CLUSTER_NAMES.index(unicode.grapheme_breaks[char] or 'Other') + extpict = unicode.ext_picts[char] + bidirectional = BIDIRECTIONAL_NAMES.index(unicode.bidi_classes[char]) if record: # extract database properties category = CATEGORY_NAMES.index(record.general_category) combining = int(record.canonical_combining_class) - bidirectional = BIDIRECTIONAL_NAMES.index(record.bidi_class) mirrored = record.bidi_mirrored == "Y" - eastasianwidth = EASTASIANWIDTH_NAMES.index(record.east_asian_width) normalizationquickcheck = record.quick_check + incb = INDIC_CONJUNCT_BREAK_NAMES.index(record.incb) item = ( category, combining, bidirectional, mirrored, eastasianwidth, - normalizationquickcheck + normalizationquickcheck, graphemebreak, incb, extpict, ) - elif unicode.widths[char] is not None: - # an unassigned but reserved character, with a known - # east_asian_width - eastasianwidth = EASTASIANWIDTH_NAMES.index(unicode.widths[char]) - item = (0, 0, 0, 0, eastasianwidth, 0) else: - continue + if eastasianwidth or graphemebreak or extpict or bidirectional: + item = (0, 0, bidirectional, 0, eastasianwidth, + 0, graphemebreak, 0, extpict) + else: + continue # add entry to index and item tables i = cache.get(item) @@ -295,7 +334,7 @@ def makeunicodedata(unicode, trace): fprint("/* a list of unique database records */") fprint("const _PyUnicode_DatabaseRecord _PyUnicode_Database_Records[] = {") for item in table: - fprint(" {%d, %d, %d, %d, %d, %d}," % item) + fprint(" {%d, %d, %d, %d, %d, %d, %d, %d, %d}," % item) fprint("};") fprint() @@ -303,15 +342,21 @@ def makeunicodedata(unicode, trace): fprint("#define TOTAL_FIRST",total_first) fprint("#define TOTAL_LAST",total_last) fprint("struct reindex{int start;short count,index;};") + # The reindex tables are read only by find_nfc_index(), which scans + # forward while .start <= code. The trailing sentinel's .start must + # exceed every codepoint (so the scan stops with a single comparison) + # and fit the signed int .start field. + nfc_sentinel = 0x7fffffff + assert sys.maxunicode < nfc_sentinel <= 0x7fffffff fprint("static struct reindex nfc_first[] = {") for start,end in comp_first_ranges: fprint(" { %d, %d, %d}," % (start,end-start,comp_first[start])) - fprint(" {0,0,0}") + fprint(" {0x%x, 0, 0}" % nfc_sentinel) fprint("};\n") fprint("static struct reindex nfc_last[] = {") for start,end in comp_last_ranges: fprint(" { %d, %d, %d}," % (start,end-start,comp_last[start])) - fprint(" {0,0,0}") + fprint(" {0x%x, 0, 0}" % nfc_sentinel) fprint("};\n") # FIXME: <fl> the following tables could be made static, and @@ -336,6 +381,52 @@ def makeunicodedata(unicode, trace): fprint(" NULL") fprint("};") + for i, name in enumerate(GRAPHEME_CLUSTER_NAMES): + fprint("#define GCB_%s %d" % (name, i)) + + fprint("const char * const _PyUnicode_GraphemeBreakNames[] = {") + for name in GRAPHEME_CLUSTER_NAMES: + fprint(' "%s",' % name) + fprint(" NULL") + fprint("};") + + for i, name in enumerate(INDIC_CONJUNCT_BREAK_NAMES): + fprint("#define InCB_%s %d" % (name, i)) + + fprint("const char * const _PyUnicode_IndicConjunctBreakNames[] = {") + for name in INDIC_CONJUNCT_BREAK_NAMES: + fprint(' "%s",' % name) + fprint(" NULL") + fprint("};") + + # Generate block tables + names = [] + name_to_index = {} + blocks = [] + for start, end, name in unicode.blocks: + if name not in name_to_index: + name_to_index[name] = len(names) + names.append(name) + blocks.append((start, end, name_to_index[name])) + + fprint("static const char * const _PyUnicode_BlockNames[] = {") + for name in names: + fprint(' "%s",' % name) + fprint("};") + + fprint("typedef struct {") + fprint(" Py_UCS4 start;") + fprint(" Py_UCS4 end;") + fprint(" unsigned short name;") + fprint("} _PyUnicode_Block;") + + fprint("static const _PyUnicode_Block _PyUnicode_Blocks[] = {") + for start, end, name in blocks: + fprint(" {0x%04X, 0x%04X, %d}," % (start, end, name)) + fprint("};") + fprint(f"#define BLOCK_COUNT {len(blocks)}") + fprint() + fprint("static const char *decomp_prefix[] = {") for name in decomp_prefix: fprint(" \"%s\"," % name) @@ -430,7 +521,7 @@ def makeunicodetype(unicode, trace): if record: # extract database properties category = record.general_category - bidirectional = record.bidi_class + bidirectional = unicode.bidi_classes[char] properties = record.binary_properties flags = 0 if category in ["Lm", "Lt", "Lu", "Ll", "Lo"]: @@ -698,6 +789,23 @@ def makeunicodename(unicode, trace): fprint(' {%d, {%s}},' % (len(sequence), seq_str)) fprint('};') + fprint(dedent(""" + typedef struct { + Py_UCS4 first; + Py_UCS4 last; + int prefixid; + } derived_name_range; + """)) + + fprint('static const derived_name_range derived_name_ranges[] = {') + for name_range in unicode.derived_name_ranges: + fprint(' {0x%s, 0x%s, %d},' % tuple(name_range)) + fprint('};') + + fprint('static const char * const derived_name_prefixes[] = {') + for prefix in unicode.derived_name_prefixes: + fprint(' "%s",' % prefix) + fprint('};') def merge_old_version(version, new, old): # Changes to exclusion file not implemented yet @@ -726,6 +834,8 @@ def merge_old_version(version, new, old): # category 0 is "unassigned" category_changes[i] = 0 continue + if old.bidi_classes[i] != new.bidi_classes[i]: + bidir_changes[i] = BIDIRECTIONAL_NAMES.index(old.bidi_classes[i]) # check characters that differ if old.table[i] != new.table[i]: for k, field in enumerate(dataclasses.fields(UcdRecord)): @@ -739,7 +849,8 @@ def merge_old_version(version, new, old): elif k == 2: category_changes[i] = CATEGORY_NAMES.index(value) elif k == 4: - bidir_changes[i] = BIDIRECTIONAL_NAMES.index(value) + # bidi_class changes handled via bidi_classes + pass elif k == 5: # We assume that all normalization changes are in 1:1 mappings assert " " not in value @@ -782,6 +893,10 @@ def merge_old_version(version, new, old): # normalization quickchecks are not performed # for older versions pass + elif k == 18: + # The Indic_Conjunct_Break property did not exist for + # older versions + pass else: class Difference(Exception):pass raise Difference(hex(i), k, old.table[i], new.table[i]) @@ -803,7 +918,7 @@ def open_data(template, version): url = ('https://www.unicode.org/Public/3.2-Update/'+template) % ('-'+version,) else: url = ('https://www.unicode.org/Public/%s/ucd/'+template) % (version, '') - os.makedirs(DATA_DIR, exist_ok=True) + os.makedirs(os.path.dirname(local), exist_ok=True) urllib.request.urlretrieve(url, filename=local) if local.endswith('.txt'): return open(local, encoding='utf-8') @@ -891,9 +1006,13 @@ class UcdRecord: # We store them as a bitmask. quick_check: int + # The Indic_Conjunct_Break property from DerivedCoreProperties.txt. See: + # https://www.unicode.org/reports/tr44/#DerivedCoreProperties.txt + incb: str + def from_row(row: List[str]) -> UcdRecord: - return UcdRecord(*row, None, set(), 0) + return UcdRecord(*row, None, set(), 0, "None") # -------------------------------------------------------------------- @@ -905,14 +1024,18 @@ def from_row(row: List[str]) -> UcdRecord: class UnicodeData: # table: List[Optional[UcdRecord]] # index is codepoint; None means unassigned - def __init__(self, version, cjk_check=True): + def __init__(self, version, ideograph_check=True): self.changed = [] table = [None] * 0x110000 for s in UcdFile(UNICODE_DATA, version): char = int(s[0], 16) table[char] = from_row(s) - cjk_ranges_found = [] + self.derived_name_ranges = [] + self.derived_name_prefixes = { + prefix: i + for i, (_, prefix) in enumerate(derived_name_range_names) + } # expand first-last ranges field = None @@ -926,15 +1049,31 @@ def __init__(self, version, cjk_check=True): s.name = "" field = dataclasses.astuple(s)[:15] elif s.name[-5:] == "Last>": - if s.name.startswith("<CJK Ideograph"): - cjk_ranges_found.append((field[0], - s.codepoint)) + for j, (rangename, _) in enumerate(derived_name_range_names): + if s.name.startswith("<" + rangename): + self.derived_name_ranges.append( + (field[0], s.codepoint, j)) + break s.name = "" field = None + else: + codepoint = s.codepoint + if s.name.endswith(codepoint): + prefix = s.name[:-len(codepoint)] + j = self.derived_name_prefixes.get(prefix) + if j is None: + j = len(self.derived_name_prefixes) + self.derived_name_prefixes[prefix] = j + if (self.derived_name_ranges + and self.derived_name_ranges[-1][2] == j + and int(self.derived_name_ranges[-1][1], 16) == i - 1): + self.derived_name_ranges[-1][1] = codepoint + else: + self.derived_name_ranges.append( + [codepoint, codepoint, j]) + s.name = "" elif field: - table[i] = from_row(('%X' % i,) + field[1:]) - if cjk_check and cjk_ranges != cjk_ranges_found: - raise ValueError("CJK ranges deviate: have %r" % cjk_ranges_found) + table[i] = from_row(('%04X' % i,) + field[1:]) # public attributes self.filename = UNICODE_DATA % '' @@ -990,15 +1129,37 @@ def __init__(self, version, cjk_check=True): table[i].east_asian_width = widths[i] self.widths = widths - for char, (propname, *propinfo) in UcdFile(DERIVED_CORE_PROPERTIES, version).expanded(): - if propinfo: - # this is not a binary property, ignore it - continue + # Read DerivedBidiClass.txt for bidi classes + # see https://www.unicode.org/reports/tr44/#Missing_Conventions + bidi_classes = [None] * 0x110000 + for i in range(0, 0x110000): + if table[i] is not None: + bidi_classes[i] = table[i].bidi_class + if version != '3.2.0': + missing_re = re.compile( + r'# @missing: ([\dA-F]+\.\.[\dA-F]+); (\w+)' + ) + with open_data(DERIVED_BIDI_CLASS, version) as f: + for l in f: + m = missing_re.match(l) + if not m: + continue + name = BIDI_LONG_NAMES[m[2]] + for i in expand_range(m[1]): + bidi_classes[i] = name + for char, (bidi,) in UcdFile(DERIVED_BIDI_CLASS, version).expanded(): + bidi_classes[char] = bidi + self.bidi_classes = bidi_classes - if table[char]: - # Some properties (e.g. Default_Ignorable_Code_Point) - # apply to unassigned code points; ignore them - table[char].binary_properties.add(propname) + for char, (propname, *propinfo) in UcdFile(DERIVED_CORE_PROPERTIES, version).expanded(): + if not propinfo: + # binary property + if table[char]: + # Some properties (e.g. Default_Ignorable_Code_Point) + # apply to unassigned code points; ignore them + table[char].binary_properties.add(propname) + elif propname == 'InCB': # Indic_Conjunct_Break + table[char].incb, = propinfo for char_range, value in UcdFile(LINE_BREAK, version): if value not in MANDATORY_LINE_BREAKS: @@ -1067,6 +1228,26 @@ def __init__(self, version, cjk_check=True): c = int(data[0], 16) cf[c] = [int(char, 16) for char in data[2].split()] + if version != "3.2.0": + grapheme_breaks = [None] * 0x110000 + for char, (prop,) in UcdFile(GRAPHEME_CLUSTER_BREAK, version).expanded(): + grapheme_breaks[char] = prop + self.grapheme_breaks = grapheme_breaks + + ext_picts = [False] * 0x110000 + for char, (prop,) in UcdFile(EMOJI_DATA, version).expanded(): + if prop == 'Extended_Pictographic': + ext_picts[char] = True + self.ext_picts = ext_picts + + # See https://www.unicode.org/versions/Unicode17.0.0/core-spec/chapter-3/#G64189 + self.blocks = [] + for record in UcdFile(BLOCKS, version).records(): + start_end, name = record + start, end = [int(c, 16) for c in start_end.split('..')] + self.blocks.append((start, end, name)) + self.blocks.sort() + def uselatin1(self): # restrict character range to ISO Latin 1 self.chars = list(range(256)) diff --git a/Tools/wasm/.ruff.toml b/Tools/wasm/.ruff.toml new file mode 100644 index 00000000000000..3d8e59fa3f22c4 --- /dev/null +++ b/Tools/wasm/.ruff.toml @@ -0,0 +1,25 @@ +extend = "../../.ruff.toml" # Inherit the project-wide settings + +[format] +preview = true +docstring-code-format = true + +[lint] +select = [ + "C4", # flake8-comprehensions + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "PGH", # pygrep-hooks + "PT", # flake8-pytest-style + "PYI", # flake8-pyi + "RUF100", # Ban unused `# noqa` comments + "UP", # pyupgrade + "W", # pycodestyle + "YTT", # flake8-2020 +] +ignore = [ + "E501", # Line too long +] diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md deleted file mode 100644 index efe9a3550c3e19..00000000000000 --- a/Tools/wasm/README.md +++ /dev/null @@ -1,335 +0,0 @@ -# Python WebAssembly (WASM) build - -**WASI support is [tier 2](https://peps.python.org/pep-0011/#tier-2).** -**Emscripten support is [tier 3](https://peps.python.org/pep-0011/#tier-3).** - -This directory contains configuration and helpers to facilitate cross -compilation of CPython to WebAssembly (WASM). Python supports Emscripten -(*wasm32-emscripten*) and WASI (*wasm32-wasi*) targets. Emscripten builds -run in modern browsers and JavaScript runtimes like *Node.js*. WASI builds -use WASM runtimes such as *wasmtime*. - -Users and developers are encouraged to use the script -`Tools/wasm/wasm_build.py`. The tool automates the build process and provides -assistance with installation of SDKs, running tests, etc. - -**NOTE**: If you are looking for information that is not directly related to -building CPython for WebAssembly (or the resulting build), please see -https://github.com/psf/webassembly for more information. - -## wasm32-emscripten - -### Build - -To cross compile to the ``wasm32-emscripten`` platform you need -[the Emscripten compiler toolchain](https://emscripten.org/), -a Python interpreter, and an installation of Node version 18 or newer. -Emscripten version 4.0.2 is recommended; newer versions may also work, but all -official testing is performed with that version. All commands below are relative -to a checkout of the Python repository. - -#### Install [the Emscripten compiler toolchain](https://emscripten.org/docs/getting_started/downloads.html) - -You can install the Emscripten toolchain as follows: -```shell -git clone https://github.com/emscripten-core/emsdk.git --depth 1 -./emsdk/emsdk install latest -./emsdk/emsdk activate latest -``` -To add the Emscripten compiler to your path: -```shell -source ./emsdk/emsdk_env.sh -``` -This adds `emcc` and `emconfigure` to your path. - -##### Optionally: enable ccache for EMSDK - -The ``EM_COMPILER_WRAPPER`` must be set after the EMSDK environment is -sourced. Otherwise the source script removes the environment variable. - -```shell -export EM_COMPILER_WRAPPER=ccache -``` - -#### Compile and build Python interpreter - -You can use `python Tools/wasm/emscripten` to compile and build targeting -Emscripten. You can do everything at once with: -```shell -python Tools/wasm/emscripten build -``` -or you can break it out into four separate steps: -```shell -python Tools/wasm/emscripten configure-build-python -python Tools/wasm/emscripten make-build-python -python Tools/wasm/emscripten make-libffi -python Tools/wasm/emscripten configure-host -python Tools/wasm/emscripten make-host -``` -Extra arguments to the configure steps are passed along to configure. For -instance, to do a debug build, you can use: -```shell -python Tools/wasm/emscripten build --with-py-debug -``` - -### Running from node - -If you want to run the normal Python CLI, you can use `python.sh`. It takes the -same options as the normal Python CLI entrypoint, though the REPL does not -function and will crash. - -`python.sh` invokes `node_entry.mjs` which imports the Emscripten module for the -Python process and starts it up with the appropriate settings. If you wish to -make a node application that "embeds" the interpreter instead of acting like the -CLI you will need to write your own alternative to `node_entry.mjs`. - - -### Running tests - -After building, you can run the full test suite with: -```shell -./cross-build/wasm32-emscripten/build/python/python.sh -m test -uall -``` -You can run the browser smoke test with: -```shell -./Tools/wasm/emscripten/browser_test/run_test.sh -``` - -### The Web Example - -When building for Emscripten, the web example will be built automatically. It -is in the ``web_example`` directory. To run the web example, ``cd`` into the -``web_example`` directory, then run ``python server.py``. This will start a web -server; you can then visit ``http://localhost:8000/`` in a browser to see a -simple REPL example. - -The web example relies on a bug fix in Emscripten version 3.1.73 so if you build -with earlier versions of Emscripten it may not work. The web example uses -``SharedArrayBuffer``. For security reasons browsers only provide -``SharedArrayBuffer`` in secure environments with cross-origin isolation. The -webserver must send cross-origin headers and correct MIME types for the -JavaScript and WebAssembly files. Otherwise the terminal will fail to load with -an error message like ``ReferenceError: SharedArrayBuffer is not defined``. See -more information here: -https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements - -Note that ``SharedArrayBuffer`` is _not required_ to use Python itself, only the -web example. If cross-origin isolation is not appropriate for your use case you -may make your own application embedding `python.mjs` which does not use -``SharedArrayBuffer`` and serve it without the cross-origin isolation headers. - -### Embedding Python in a custom JavaScript application - -You can look at `python.worker.mjs` and `node_entry.mjs` for inspiration. At a -minimum you must import ``createEmscriptenModule`` and you need to call -``createEmscriptenModule`` with an appropriate settings object. This settings -object will need a prerun hook that installs the Python standard library into -the Emscripten file system. - -#### NodeJs - -In Node, you can use the NodeFS to mount the standard library in your native -file system into the Emscripten file system: -```js -import createEmscriptenModule from "./python.mjs"; - -await createEmscriptenModule({ - preRun(Module) { - Module.FS.mount( - Module.FS.filesystems.NODEFS, - { root: "/path/to/python/stdlib" }, - "/lib/", - ); - }, -}); -``` - -#### Browser - -In the browser, the simplest approach is to put the standard library in a zip -file it and install it. With Python 3.14 this could look like: -```js -import createEmscriptenModule from "./python.mjs"; - -await createEmscriptenModule({ - async preRun(Module) { - Module.FS.mkdirTree("/lib/python3.14/lib-dynload/"); - Module.addRunDependency("install-stdlib"); - const resp = await fetch("python3.14.zip"); - const stdlibBuffer = await resp.arrayBuffer(); - Module.FS.writeFile(`/lib/python314.zip`, new Uint8Array(stdlibBuffer), { - canOwn: true, - }); - Module.removeRunDependency("install-stdlib"); - }, -}); -``` - -### Limitations and issues - -#### Network stack - -- Python's socket module does not work with Emscripten's emulated POSIX - sockets yet. Network modules like ``asyncio``, ``urllib``, ``selectors``, - etc. are not available. -- Only ``AF_INET`` and ``AF_INET6`` with ``SOCK_STREAM`` (TCP) or - ``SOCK_DGRAM`` (UDP) are available. ``AF_UNIX`` is not supported. -- ``socketpair`` does not work. -- Blocking sockets are not available and non-blocking sockets don't work - correctly, e.g. ``socket.accept`` crashes the runtime. ``gethostbyname`` - does not resolve to a real IP address. IPv6 is not available. -- The ``select`` module is limited. ``select.select()`` crashes the runtime - due to lack of exectfd support. - -#### processes, signals - -- Processes are not supported. System calls like fork, popen, and subprocess - fail with ``ENOSYS`` or ``ENOSUP``. -- Signal support is limited. ``signal.alarm``, ``itimer``, ``sigaction`` - are not available or do not work correctly. ``SIGTERM`` exits the runtime. -- Keyboard interrupt (CTRL+C) handling is not implemented yet. -- Resource-related functions like ``os.nice`` and most functions of the - ``resource`` module are not available. - -#### threading - -- Threading is disabled by default. The ``configure`` option - ``--enable-wasm-pthreads`` adds compiler flag ``-pthread`` and - linker flags ``-sUSE_PTHREADS -sPROXY_TO_PTHREAD``. -- pthread support requires WASM threads and SharedArrayBuffer (bulk memory). - The Node.JS runtime keeps a pool of web workers around. Each web worker - uses several file descriptors (eventfd, epoll, pipe). -- It's not advised to enable threading when building for browsers or with - dynamic linking support; there are performance and stability issues. - -#### file system - -- Most user, group, and permission related function and modules are not - supported or don't work as expected, e.g.``pwd`` module, ``grp`` module, - ``os.setgroups``, ``os.chown``, and so on. ``lchown`` and ``lchmod`` are - not available. -- ``umask`` is a no-op. -- hard links (``os.link``) are not supported. -- Offset and iovec I/O functions (e.g. ``os.pread``, ``os.preadv``) are not - available. -- ``os.mknod`` and ``os.mkfifo`` - [don't work](https://github.com/emscripten-core/emscripten/issues/16158) - and are disabled. -- Large file support crashes the runtime and is disabled. -- ``mmap`` module is unstable. flush (``msync``) can crash the runtime. - -#### Misc - -- Heap memory and stack size are limited. Recursion or extensive memory - consumption can crash Python. -- Most stdlib modules with a dependency on external libraries are missing, - e.g. ``ctypes``, ``readline``, ``ssl``, and more. -- Shared extension modules are not implemented yet. All extension modules - are statically linked into the main binary. The experimental configure - option ``--enable-wasm-dynamic-linking`` enables dynamic extensions - supports. It's currently known to crash in combination with threading. -- glibc extensions for date and time formatting are not available. -- ``locales`` module is affected by musl libc issues, - [gh-90548](https://github.com/python/cpython/issues/90548). -- Python's object allocator ``obmalloc`` is disabled by default. -- ``ensurepip`` is not available. - -#### In the browser - -- The interactive shell does not handle copy 'n paste and unicode support - well. -- The bundled stdlib is limited. Network-related modules, - multiprocessing, dbm, tests and similar modules - are not shipped. All other modules are bundled as pre-compiled - ``pyc`` files. -- In-memory file system (MEMFS) is not persistent and limited. -- Test modules are disabled by default. Use ``--enable-test-modules`` build - test modules like ``_testcapi``. - -## WASI (wasm32-wasi) - -See [the devguide on how to build and run for WASI](https://devguide.python.org/getting-started/setup-building/#wasi). - -## Detecting WebAssembly builds - -### Python code - -```python -import os, sys - -if sys.platform == "emscripten": - # Python on Emscripten - ... -if sys.platform == "wasi": - # Python on WASI - ... - -if os.name == "posix": - # WASM platforms identify as POSIX-like. - # Windows does not provide os.uname(). - machine = os.uname().machine - if machine.startswith("wasm"): - # WebAssembly (wasm32, wasm64 potentially in the future) -``` - -```python ->>> import os, sys ->>> os.uname() -posix.uname_result( - sysname='Emscripten', - nodename='emscripten', - release='4.0.2', - version='#1', - machine='wasm32' -) ->>> os.name -'posix' ->>> sys.platform -'emscripten' ->>> sys._emscripten_info -sys._emscripten_info( - emscripten_version=(3, 1, 10), - runtime='Mozilla/5.0 (X11; Linux x86_64; rv:104.0) Gecko/20100101 Firefox/104.0', - pthreads=False, - shared_memory=False -) -``` - -```python ->>> sys._emscripten_info -sys._emscripten_info( - emscripten_version=(3, 1, 19), - runtime='Node.js v14.18.2', - pthreads=True, - shared_memory=True -) -``` - -```python ->>> import os, sys ->>> os.uname() -posix.uname_result( - sysname='wasi', - nodename='(none)', - release='0.0.0', - version='0.0.0', - machine='wasm32' -) ->>> os.name -'posix' ->>> sys.platform -'wasi' -``` - -### C code - -Emscripten SDK and WASI SDK define several built-in macros. You can dump a -full list of built-ins with ``emcc -dM -E - < /dev/null`` and -``/path/to/wasi-sdk/bin/clang -dM -E - < /dev/null``. - -* WebAssembly ``__wasm__`` (also ``__wasm``) -* wasm32 ``__wasm32__`` (also ``__wasm32``) -* wasm64 ``__wasm64__`` -* Emscripten ``__EMSCRIPTEN__`` (also ``EMSCRIPTEN``) -* Emscripten version ``__EMSCRIPTEN_major__``, ``__EMSCRIPTEN_minor__``, ``__EMSCRIPTEN_tiny__`` -* WASI ``__wasi__`` diff --git a/Tools/wasm/emscripten/__main__.py b/Tools/wasm/emscripten/__main__.py index 202dd298199b56..29890cc1a2f365 100644 --- a/Tools/wasm/emscripten/__main__.py +++ b/Tools/wasm/emscripten/__main__.py @@ -1,467 +1,14 @@ -#!/usr/bin/env python3 - -import argparse -import contextlib -import functools -import os -import shutil -import subprocess -import sys -import sysconfig -import hashlib -import tempfile -from urllib.request import urlopen -from pathlib import Path -from textwrap import dedent - -try: - from os import process_cpu_count as cpu_count -except ImportError: - from os import cpu_count - - -EMSCRIPTEN_DIR = Path(__file__).parent -CHECKOUT = EMSCRIPTEN_DIR.parent.parent.parent - -CROSS_BUILD_DIR = CHECKOUT / "cross-build" -NATIVE_BUILD_DIR = CROSS_BUILD_DIR / "build" -HOST_TRIPLE = "wasm32-emscripten" - -DOWNLOAD_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "build" -HOST_BUILD_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "build" -HOST_DIR = HOST_BUILD_DIR / "python" -PREFIX_DIR = CROSS_BUILD_DIR / HOST_TRIPLE / "prefix" - -LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" -LOCAL_SETUP_MARKER = "# Generated by Tools/wasm/emscripten.py\n".encode("utf-8") - - -def updated_env(updates={}): - """Create a new dict representing the environment to use. - - The changes made to the execution environment are printed out. - """ - env_defaults = {} - # https://reproducible-builds.org/docs/source-date-epoch/ - git_epoch_cmd = ["git", "log", "-1", "--pretty=%ct"] - try: - epoch = subprocess.check_output(git_epoch_cmd, encoding="utf-8").strip() - env_defaults["SOURCE_DATE_EPOCH"] = epoch - except subprocess.CalledProcessError: - pass # Might be building from a tarball. - # This layering lets SOURCE_DATE_EPOCH from os.environ takes precedence. - environment = env_defaults | os.environ | updates - - env_diff = {} - for key, value in environment.items(): - if os.environ.get(key) != value: - env_diff[key] = value - - print("🌎 Environment changes:") - for key in sorted(env_diff.keys()): - print(f" {key}={env_diff[key]}") - - return environment - - -def subdir(working_dir, *, clean_ok=False): - """Decorator to change to a working directory.""" - - def decorator(func): - @functools.wraps(func) - def wrapper(context): - try: - tput_output = subprocess.check_output( - ["tput", "cols"], encoding="utf-8" - ) - terminal_width = int(tput_output.strip()) - except subprocess.CalledProcessError: - terminal_width = 80 - print("⎯" * terminal_width) - print("📁", working_dir) - if clean_ok and getattr(context, "clean", False) and working_dir.exists(): - print("🚮 Deleting directory (--clean)...") - shutil.rmtree(working_dir) - - working_dir.mkdir(parents=True, exist_ok=True) - - with contextlib.chdir(working_dir): - return func(context, working_dir) - - return wrapper - - return decorator - - -def call(command, *, quiet, **kwargs): - """Execute a command. - - If 'quiet' is true, then redirect stdout and stderr to a temporary file. - """ - print("❯", " ".join(map(str, command))) - if not quiet: - stdout = None - stderr = None - else: - stdout = tempfile.NamedTemporaryFile( - "w", - encoding="utf-8", - delete=False, - prefix="cpython-emscripten-", - suffix=".log", - ) - stderr = subprocess.STDOUT - print(f"📝 Logging output to {stdout.name} (--quiet)...") - - subprocess.check_call(command, **kwargs, stdout=stdout, stderr=stderr) - - -def build_platform(): - """The name of the build/host platform.""" - # Can also be found via `config.guess`.` - return sysconfig.get_config_var("BUILD_GNU_TYPE") - - -def build_python_path(): - """The path to the build Python binary.""" - binary = NATIVE_BUILD_DIR / "python" - if not binary.is_file(): - binary = binary.with_suffix(".exe") - if not binary.is_file(): - raise FileNotFoundError("Unable to find `python(.exe)` in " f"{NATIVE_BUILD_DIR}") - - return binary - - -@subdir(NATIVE_BUILD_DIR, clean_ok=True) -def configure_build_python(context, working_dir): - """Configure the build/host Python.""" - if LOCAL_SETUP.exists(): - print(f"👍 {LOCAL_SETUP} exists ...") - else: - print(f"📝 Touching {LOCAL_SETUP} ...") - LOCAL_SETUP.write_bytes(LOCAL_SETUP_MARKER) - - configure = [os.path.relpath(CHECKOUT / "configure", working_dir)] - if context.args: - configure.extend(context.args) - - call(configure, quiet=context.quiet) - - -@subdir(NATIVE_BUILD_DIR) -def make_build_python(context, working_dir): - """Make/build the build Python.""" - call(["make", "--jobs", str(cpu_count()), "all"], quiet=context.quiet) - - binary = build_python_path() - cmd = [ - binary, - "-c", - "import sys; " "print(f'{sys.version_info.major}.{sys.version_info.minor}')", - ] - version = subprocess.check_output(cmd, encoding="utf-8").strip() - - print(f"🎉 {binary} {version}") - - -def check_shasum(file: str, expected_shasum: str): - with open(file, "rb") as f: - digest = hashlib.file_digest(f, "sha256") - if digest.hexdigest() != expected_shasum: - raise RuntimeError(f"Unexpected shasum for {file}") - - -def download_and_unpack(working_dir: Path, url: str, expected_shasum: str): - with tempfile.NamedTemporaryFile(suffix=".tar.gz", delete_on_close=False) as tmp_file: - with urlopen(url) as response: - shutil.copyfileobj(response, tmp_file) - tmp_file.close() - check_shasum(tmp_file.name, expected_shasum) - shutil.unpack_archive(tmp_file.name, working_dir) - - -@subdir(HOST_BUILD_DIR, clean_ok=True) -def make_emscripten_libffi(context, working_dir): - ver = "3.4.6" - libffi_dir = working_dir / f"libffi-{ver}" - shutil.rmtree(libffi_dir, ignore_errors=True) - download_and_unpack(working_dir, f"https://github.com/libffi/libffi/releases/download/v{ver}/libffi-{ver}.tar.gz", "b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e") - call( - [EMSCRIPTEN_DIR / "make_libffi.sh"], - env=updated_env({"PREFIX": PREFIX_DIR}), - cwd=libffi_dir, - quiet=context.quiet, - ) - - -@subdir(HOST_BUILD_DIR, clean_ok=True) -def make_mpdec(context, working_dir): - ver = "4.0.1" - mpdec_dir = working_dir / f"mpdecimal-{ver}" - shutil.rmtree(mpdec_dir, ignore_errors=True) - download_and_unpack(working_dir, f"https://www.bytereef.org/software/mpdecimal/releases/mpdecimal-{ver}.tar.gz", "96d33abb4bb0070c7be0fed4246cd38416188325f820468214471938545b1ac8") - call( - [ - "emconfigure", - mpdec_dir / "configure", - "CFLAGS=-fPIC", - "--prefix", - PREFIX_DIR, - "--disable-shared", - ], - cwd=mpdec_dir, - quiet=context.quiet, - ) - call( - [ - "make", - "install" - ], - cwd=mpdec_dir, - quiet=context.quiet, - ) - - -@subdir(HOST_DIR, clean_ok=True) -def configure_emscripten_python(context, working_dir): - """Configure the emscripten/host build.""" - config_site = os.fsdecode( - EMSCRIPTEN_DIR / "config.site-wasm32-emscripten" - ) - - emscripten_build_dir = working_dir.relative_to(CHECKOUT) - - python_build_dir = NATIVE_BUILD_DIR / "build" - lib_dirs = list(python_build_dir.glob("lib.*")) - assert ( - len(lib_dirs) == 1 - ), f"Expected a single lib.* directory in {python_build_dir}" - lib_dir = os.fsdecode(lib_dirs[0]) - pydebug = lib_dir.endswith("-pydebug") - python_version = lib_dir.removesuffix("-pydebug").rpartition("-")[-1] - sysconfig_data = ( - f"{emscripten_build_dir}/build/lib.emscripten-wasm32-{python_version}" - ) - if pydebug: - sysconfig_data += "-pydebug" - - host_runner = context.host_runner - if node_version := os.environ.get("PYTHON_NODE_VERSION", None): - res = subprocess.run( - [ - "bash", - "-c", - f"source ~/.nvm/nvm.sh && nvm which {node_version}", - ], - text=True, - capture_output=True, - ) - host_runner = res.stdout.strip() - pkg_config_path_dir = (PREFIX_DIR / "lib/pkgconfig/").resolve() - env_additions = { - "CONFIG_SITE": config_site, - "HOSTRUNNER": host_runner, - "EM_PKG_CONFIG_PATH": str(pkg_config_path_dir), - } - build_python = os.fsdecode(build_python_path()) - configure = [ - "emconfigure", - os.path.relpath(CHECKOUT / "configure", working_dir), - "CFLAGS=-DPY_CALL_TRAMPOLINE -sUSE_BZIP2", - "PKG_CONFIG=pkg-config", - f"--host={HOST_TRIPLE}", - f"--build={build_platform()}", - f"--with-build-python={build_python}", - "--without-pymalloc", - "--disable-shared", - "--disable-ipv6", - "--enable-big-digits=30", - "--enable-wasm-dynamic-linking", - f"--prefix={PREFIX_DIR}", - ] - if pydebug: - configure.append("--with-pydebug") - if context.args: - configure.extend(context.args) - call( - configure, - env=updated_env(env_additions), - quiet=context.quiet, - ) - - shutil.copy(EMSCRIPTEN_DIR / "node_entry.mjs", working_dir / "node_entry.mjs") - - node_entry = working_dir / "node_entry.mjs" - exec_script = working_dir / "python.sh" - exec_script.write_text( - dedent( - f"""\ - #!/bin/sh - - # Macs come with FreeBSD coreutils which doesn't have the -s option - # so feature detect and work around it. - if which grealpath > /dev/null 2>&1; then - # It has brew installed gnu core utils, use that - REALPATH="grealpath -s" - elif which realpath > /dev/null 2>&1 && realpath --version > /dev/null 2>&1 && realpath --version | grep GNU > /dev/null 2>&1; then - # realpath points to GNU realpath so use it. - REALPATH="realpath -s" - else - # Shim for macs without GNU coreutils - abs_path () {{ - echo "$(cd "$(dirname "$1")" || exit; pwd)/$(basename "$1")" - }} - REALPATH=abs_path - fi - - # Before node 24, --experimental-wasm-jspi uses different API, - # After node 24 JSPI is on by default. - ARGS=$({host_runner} -e "$(cat <<"EOF" - const major_version = Number(process.version.split(".")[0].slice(1)); - if (major_version === 24) {{ - process.stdout.write("--experimental-wasm-jspi"); - }} - EOF - )") - - # We compute our own path, not following symlinks and pass it in so that - # node_entry.mjs can set sys.executable correctly. - # Intentionally allow word splitting on NODEFLAGS. - exec {host_runner} $NODEFLAGS $ARGS {node_entry} --this-program="$($REALPATH "$0")" "$@" - """ - ) - ) - exec_script.chmod(0o755) - print(f"🏃‍♀️ Created {exec_script} ... ") - sys.stdout.flush() - - -@subdir(HOST_DIR) -def make_emscripten_python(context, working_dir): - """Run `make` for the emscripten/host build.""" - call( - ["make", "--jobs", str(cpu_count()), "all"], - env=updated_env(), - quiet=context.quiet, - ) - - exec_script = working_dir / "python.sh" - subprocess.check_call([exec_script, "--version"]) - - -def build_all(context): - """Build everything.""" - steps = [ - configure_build_python, - make_build_python, - make_emscripten_libffi, - make_mpdec, - configure_emscripten_python, - make_emscripten_python, - ] - for step in steps: - step(context) - - -def clean_contents(context): - """Delete all files created by this script.""" - if CROSS_BUILD_DIR.exists(): - print(f"🧹 Deleting {CROSS_BUILD_DIR} ...") - shutil.rmtree(CROSS_BUILD_DIR) - - if LOCAL_SETUP.exists(): - with LOCAL_SETUP.open("rb") as file: - if file.read(len(LOCAL_SETUP_MARKER)) == LOCAL_SETUP_MARKER: - print(f"🧹 Deleting generated {LOCAL_SETUP} ...") - - -def main(): - default_host_runner = "node" +if __name__ == "__main__": + import pathlib + import runpy + import sys - parser = argparse.ArgumentParser() - subcommands = parser.add_subparsers(dest="subcommand") - build = subcommands.add_parser("build", help="Build everything") - configure_build = subcommands.add_parser( - "configure-build-python", help="Run `configure` for the " "build Python" - ) - make_mpdec_cmd = subcommands.add_parser( - "make-mpdec", help="Clone mpdec repo, configure and build it for emscripten" + print( + "⚠️ WARNING: This script is deprecated and slated for removal in Python 3.20; " + "execute the `Platforms/emscripten/` directory instead (i.e. `python Platforms/emscripten`)\n", + file=sys.stderr, ) - make_libffi_cmd = subcommands.add_parser( - "make-libffi", help="Clone libffi repo, configure and build it for emscripten" - ) - make_build = subcommands.add_parser( - "make-build-python", help="Run `make` for the build Python" - ) - configure_host = subcommands.add_parser( - "configure-host", - help="Run `configure` for the host/emscripten (pydebug builds are inferred from the build Python)", - ) - make_host = subcommands.add_parser( - "make-host", help="Run `make` for the host/emscripten" - ) - clean = subcommands.add_parser( - "clean", help="Delete files and directories created by this script" - ) - for subcommand in ( - build, - configure_build, - make_libffi_cmd, - make_mpdec_cmd, - make_build, - configure_host, - make_host, - ): - subcommand.add_argument( - "--quiet", - action="store_true", - default=False, - dest="quiet", - help="Redirect output from subprocesses to a log file", - ) - for subcommand in 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_build, configure_host: - subcommand.add_argument( - "args", nargs="*", help="Extra arguments to pass to `configure`" - ) - for subcommand in build, configure_host: - subcommand.add_argument( - "--host-runner", - action="store", - default=default_host_runner, - dest="host_runner", - help="Command template for running the emscripten host" - f"`{default_host_runner}`)", - ) - context = parser.parse_args() - - dispatch = { - "make-libffi": make_emscripten_libffi, - "make-mpdec": make_mpdec, - "configure-build-python": configure_build_python, - "make-build-python": make_build_python, - "configure-host": configure_emscripten_python, - "make-host": make_emscripten_python, - "build": build_all, - "clean": clean_contents, - } - - if not context.subcommand: - # No command provided, display help and exit - print("Expected one of", ", ".join(sorted(dispatch.keys())), file=sys.stderr) - parser.print_help(sys.stderr) - sys.exit(1) - dispatch[context.subcommand](context) - - -if __name__ == "__main__": - main() + checkout = pathlib.Path(__file__).parents[3] + emscripten_dir = (checkout / "Platforms/emscripten").absolute() + runpy.run_path(str(emscripten_dir), run_name="__main__") diff --git a/Tools/wasm/emscripten/browser_test/package-lock.json b/Tools/wasm/emscripten/browser_test/package-lock.json deleted file mode 100644 index 044e3c19ce15f7..00000000000000 --- a/Tools/wasm/emscripten/browser_test/package-lock.json +++ /dev/null @@ -1,672 +0,0 @@ -{ - "name": "browser_test", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "browser_test", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@playwright/test": "^1.54.1", - "@types/node": "^24.1.0", - "http-server": "^14.1.1", - "playwright": "^1.54.1" - } - }, - "node_modules/@playwright/test": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz", - "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==", - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.54.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@types/node": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", - "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.8.0" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-server": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", - "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", - "license": "MIT", - "dependencies": { - "basic-auth": "^2.0.1", - "chalk": "^4.1.2", - "corser": "^2.0.1", - "he": "^1.2.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy": "^1.18.1", - "mime": "^1.6.0", - "minimist": "^1.2.6", - "opener": "^1.5.1", - "portfinder": "^1.0.28", - "secure-compare": "3.0.1", - "union": "~0.5.0", - "url-join": "^4.0.1" - }, - "bin": { - "http-server": "bin/http-server" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/playwright": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz", - "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==", - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.54.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz", - "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==", - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/portfinder": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.37.tgz", - "integrity": "sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==", - "license": "MIT", - "dependencies": { - "async": "^3.2.6", - "debug": "^4.3.6" - }, - "engines": { - "node": ">= 10.12" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", - "license": "MIT" - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "license": "MIT" - }, - "node_modules/union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dependencies": { - "qs": "^6.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "license": "MIT" - }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - } - } -} diff --git a/Tools/wasm/emscripten/browser_test/playwright.config.ts b/Tools/wasm/emscripten/browser_test/playwright.config.ts deleted file mode 100644 index 81d53ce11cb050..00000000000000 --- a/Tools/wasm/emscripten/browser_test/playwright.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { defineConfig, devices } from '@playwright/test'; - -export default defineConfig({ - testDir: '.', - forbidOnly: true, - retries: 2, - reporter: process.env.CI ? 'dot' : 'html', - use: { - baseURL: 'http://localhost:8787', - trace: 'on-first-retry', - }, - projects: [ - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, - }, - ], - webServer: { - command: 'npx http-server ../../../../cross-build/wasm32-emscripten/build/python/web_example_pyrepl_jspi/ -p 8787', - url: 'http://localhost:8787', - }, -}); diff --git a/Tools/wasm/emscripten/browser_test/run_test.sh b/Tools/wasm/emscripten/browser_test/run_test.sh index 9166e0d740585e..ed8cae7bf23b29 100755 --- a/Tools/wasm/emscripten/browser_test/run_test.sh +++ b/Tools/wasm/emscripten/browser_test/run_test.sh @@ -1,10 +1,3 @@ #!/bin/bash -set -euo pipefail -cd "$(dirname "$0")" -rm -f test_log.txt -echo "Installing node packages" | tee test_log.txt -npm ci >> test_log.txt 2>&1 -echo "Installing playwright browsers" | tee test_log.txt -npx playwright install 2>> test_log.txt -echo "Running tests" | tee test_log.txt -CI=1 npx playwright test | tee test_log.txt +# Redirect to new location +exec "$(dirname "$0")/../../../../Platforms/emscripten/browser_test/run_test.sh" "$@" diff --git a/Tools/wasm/wasi.py b/Tools/wasm/wasi.py index b49b27cbbbe66e..72a708c45c02af 100644 --- a/Tools/wasm/wasi.py +++ b/Tools/wasm/wasi.py @@ -1,10 +1,5 @@ -if __name__ == "__main__": +if __name__ == "__main__": import pathlib import runpy - import sys - - print("⚠️ WARNING: This script is deprecated and slated for removal in Python 3.20; " - "execute the `wasi/` directory instead (i.e. `python Tools/wasm/wasi`)\n", - file=sys.stderr) runpy.run_path(pathlib.Path(__file__).parent / "wasi", run_name="__main__") diff --git a/Tools/wasm/wasi/__main__.py b/Tools/wasm/wasi/__main__.py index f3c97ff3fd11a0..e6126eddde1f84 100644 --- a/Tools/wasm/wasi/__main__.py +++ b/Tools/wasm/wasi/__main__.py @@ -1,377 +1,14 @@ -#!/usr/bin/env python3 +if __name__ == "__main__": + import pathlib + import runpy + import sys -import argparse -import contextlib -import functools -import os -try: - from os import process_cpu_count as cpu_count -except ImportError: - from os import cpu_count -import pathlib -import shutil -import subprocess -import sys -import sysconfig -import tempfile - - -CHECKOUT = pathlib.Path(__file__).parent.parent.parent.parent -assert (CHECKOUT / "configure").is_file(), "Please update the location of the file" - -CROSS_BUILD_DIR = CHECKOUT / "cross-build" -BUILD_DIR = CROSS_BUILD_DIR / "build" - -LOCAL_SETUP = CHECKOUT / "Modules" / "Setup.local" -LOCAL_SETUP_MARKER = ("# Generated by Tools/wasm/wasi .\n" - "# Required to statically build extension modules.").encode("utf-8") - -WASMTIME_VAR_NAME = "WASMTIME" -WASMTIME_HOST_RUNNER_VAR = f"{{{WASMTIME_VAR_NAME}}}" - - -def updated_env(updates={}): - """Create a new dict representing the environment to use. - - The changes made to the execution environment are printed out. - """ - env_defaults = {} - # https://reproducible-builds.org/docs/source-date-epoch/ - git_epoch_cmd = ["git", "log", "-1", "--pretty=%ct"] - try: - epoch = subprocess.check_output(git_epoch_cmd, encoding="utf-8").strip() - env_defaults["SOURCE_DATE_EPOCH"] = epoch - except subprocess.CalledProcessError: - pass # Might be building from a tarball. - # This layering lets SOURCE_DATE_EPOCH from os.environ takes precedence. - environment = env_defaults | os.environ | updates - - env_diff = {} - for key, value in environment.items(): - if os.environ.get(key) != value: - env_diff[key] = value - - print("🌎 Environment changes:") - for key in sorted(env_diff.keys()): - print(f" {key}={env_diff[key]}") - - return environment - - -def subdir(working_dir, *, clean_ok=False): - """Decorator to change to a working directory.""" - def decorator(func): - @functools.wraps(func) - def wrapper(context): - nonlocal working_dir - - if callable(working_dir): - working_dir = working_dir(context) - try: - tput_output = subprocess.check_output(["tput", "cols"], - encoding="utf-8") - except subprocess.CalledProcessError: - terminal_width = 80 - else: - terminal_width = int(tput_output.strip()) - print("⎯" * terminal_width) - print("📁", working_dir) - if (clean_ok and getattr(context, "clean", False) and - working_dir.exists()): - print(f"🚮 Deleting directory (--clean)...") - shutil.rmtree(working_dir) - - working_dir.mkdir(parents=True, exist_ok=True) - - with contextlib.chdir(working_dir): - return func(context, working_dir) - - return wrapper - - return decorator - - -def call(command, *, quiet, **kwargs): - """Execute a command. - - If 'quiet' is true, then redirect stdout and stderr to a temporary file. - """ - print("❯", " ".join(map(str, command))) - if not quiet: - stdout = None - stderr = None - else: - stdout = tempfile.NamedTemporaryFile("w", encoding="utf-8", - delete=False, - prefix="cpython-wasi-", - suffix=".log") - stderr = subprocess.STDOUT - print(f"📝 Logging output to {stdout.name} (--quiet)...") - - subprocess.check_call(command, **kwargs, stdout=stdout, stderr=stderr) - - -def build_platform(): - """The name of the build/host platform.""" - # Can also be found via `config.guess`. - return sysconfig.get_config_var("BUILD_GNU_TYPE") - - -def build_python_path(): - """The path to the build Python binary.""" - binary = BUILD_DIR / "python" - if not binary.is_file(): - binary = binary.with_suffix(".exe") - if not binary.is_file(): - raise FileNotFoundError("Unable to find `python(.exe)` in " - f"{BUILD_DIR}") - - return binary - - -def build_python_is_pydebug(): - """Find out if the build Python is a pydebug build.""" - test = "import sys, test.support; sys.exit(test.support.Py_DEBUG)" - result = subprocess.run([build_python_path(), "-c", test], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - return bool(result.returncode) - - -@subdir(BUILD_DIR, clean_ok=True) -def configure_build_python(context, working_dir): - """Configure the build/host Python.""" - if LOCAL_SETUP.exists(): - if LOCAL_SETUP.read_bytes() == LOCAL_SETUP_MARKER: - print(f"👍 {LOCAL_SETUP} exists ...") - else: - print(f"⚠️ {LOCAL_SETUP} exists, but has unexpected contents") - else: - print(f"📝 Creating {LOCAL_SETUP} ...") - LOCAL_SETUP.write_bytes(LOCAL_SETUP_MARKER) - - configure = [os.path.relpath(CHECKOUT / 'configure', working_dir)] - if context.args: - configure.extend(context.args) - - call(configure, quiet=context.quiet) - - -@subdir(BUILD_DIR) -def make_build_python(context, working_dir): - """Make/build the build Python.""" - call(["make", "--jobs", str(cpu_count()), "all"], - quiet=context.quiet) - - binary = build_python_path() - cmd = [binary, "-c", - "import sys; " - "print(f'{sys.version_info.major}.{sys.version_info.minor}')"] - version = subprocess.check_output(cmd, encoding="utf-8").strip() - - print(f"🎉 {binary} {version}") - - -def find_wasi_sdk(): - """Find the path to wasi-sdk.""" - if wasi_sdk_path := os.environ.get("WASI_SDK_PATH"): - return pathlib.Path(wasi_sdk_path) - elif (default_path := pathlib.Path("/opt/wasi-sdk")).exists(): - return default_path - - -def wasi_sdk_env(context): - """Calculate environment variables for building with wasi-sdk.""" - wasi_sdk_path = context.wasi_sdk_path - sysroot = wasi_sdk_path / "share" / "wasi-sysroot" - env = {"CC": "clang", "CPP": "clang-cpp", "CXX": "clang++", - "AR": "llvm-ar", "RANLIB": "ranlib"} - - for env_var, binary_name in list(env.items()): - env[env_var] = os.fsdecode(wasi_sdk_path / "bin" / binary_name) - - if wasi_sdk_path != pathlib.Path("/opt/wasi-sdk"): - for compiler in ["CC", "CPP", "CXX"]: - env[compiler] += f" --sysroot={sysroot}" - - env["PKG_CONFIG_PATH"] = "" - env["PKG_CONFIG_LIBDIR"] = os.pathsep.join( - map(os.fsdecode, - [sysroot / "lib" / "pkgconfig", - sysroot / "share" / "pkgconfig"])) - env["PKG_CONFIG_SYSROOT_DIR"] = os.fsdecode(sysroot) - - env["WASI_SDK_PATH"] = os.fsdecode(wasi_sdk_path) - env["WASI_SYSROOT"] = os.fsdecode(sysroot) - - env["PATH"] = os.pathsep.join([os.fsdecode(wasi_sdk_path / "bin"), - os.environ["PATH"]]) - - return env - - -@subdir(lambda context: CROSS_BUILD_DIR / context.host_triple, clean_ok=True) -def configure_wasi_python(context, working_dir): - """Configure the WASI/host build.""" - if not context.wasi_sdk_path or not context.wasi_sdk_path.exists(): - raise ValueError("WASI-SDK not found; " - "download from " - "https://github.com/WebAssembly/wasi-sdk and/or " - "specify via $WASI_SDK_PATH or --wasi-sdk") - - config_site = os.fsdecode(CHECKOUT / "Tools" / "wasm" / "wasi" / "config.site-wasm32-wasi") - - wasi_build_dir = working_dir.relative_to(CHECKOUT) - - python_build_dir = BUILD_DIR / "build" - lib_dirs = list(python_build_dir.glob("lib.*")) - assert len(lib_dirs) == 1, f"Expected a single lib.* directory in {python_build_dir}" - lib_dir = os.fsdecode(lib_dirs[0]) - python_version = lib_dir.rpartition("-")[-1] - sysconfig_data_dir = f"{wasi_build_dir}/build/lib.wasi-wasm32-{python_version}" - - # Use PYTHONPATH to include sysconfig data which must be anchored to the - # WASI guest's `/` directory. - args = {"GUEST_DIR": "/", - "HOST_DIR": CHECKOUT, - "ENV_VAR_NAME": "PYTHONPATH", - "ENV_VAR_VALUE": f"/{sysconfig_data_dir}", - "PYTHON_WASM": working_dir / "python.wasm"} - # Check dynamically for wasmtime in case it was specified manually via - # `--host-runner`. - if WASMTIME_HOST_RUNNER_VAR in context.host_runner: - if wasmtime := shutil.which("wasmtime"): - args[WASMTIME_VAR_NAME] = wasmtime - else: - raise FileNotFoundError("wasmtime not found; download from " - "https://github.com/bytecodealliance/wasmtime") - host_runner = context.host_runner.format_map(args) - env_additions = {"CONFIG_SITE": config_site, "HOSTRUNNER": host_runner} - build_python = os.fsdecode(build_python_path()) - # The path to `configure` MUST be relative, else `python.wasm` is unable - # to find the stdlib due to Python not recognizing that it's being - # executed from within a checkout. - configure = [os.path.relpath(CHECKOUT / 'configure', working_dir), - f"--host={context.host_triple}", - f"--build={build_platform()}", - f"--with-build-python={build_python}"] - if build_python_is_pydebug(): - configure.append("--with-pydebug") - if context.args: - configure.extend(context.args) - call(configure, - env=updated_env(env_additions | wasi_sdk_env(context)), - quiet=context.quiet) - - python_wasm = working_dir / "python.wasm" - exec_script = working_dir / "python.sh" - with exec_script.open("w", encoding="utf-8") as file: - file.write(f'#!/bin/sh\nexec {host_runner} {python_wasm} "$@"\n') - exec_script.chmod(0o755) - print(f"🏃‍♀️ Created {exec_script} (--host-runner)... ") - sys.stdout.flush() - - -@subdir(lambda context: CROSS_BUILD_DIR / context.host_triple) -def make_wasi_python(context, working_dir): - """Run `make` for the WASI/host build.""" - call(["make", "--jobs", str(cpu_count()), "all"], - env=updated_env(), - quiet=context.quiet) - - exec_script = working_dir / "python.sh" - call([exec_script, "--version"], quiet=False) print( - f"🎉 Use `{exec_script.relative_to(context.init_dir)}` " - "to run CPython w/ the WASI host specified by --host-runner" + "⚠️ WARNING: This script is deprecated and slated for removal in Python 3.20; " + "execute the `Platforms/WASI/` directory instead (i.e. `python Platforms/WASI`)\n", + file=sys.stderr, ) + checkout = pathlib.Path(__file__).parent.parent.parent.parent -def build_all(context): - """Build everything.""" - steps = [configure_build_python, make_build_python, configure_wasi_python, - make_wasi_python] - for step in steps: - step(context) - -def clean_contents(context): - """Delete all files created by this script.""" - if CROSS_BUILD_DIR.exists(): - print(f"🧹 Deleting {CROSS_BUILD_DIR} ...") - shutil.rmtree(CROSS_BUILD_DIR) - - if LOCAL_SETUP.exists(): - if LOCAL_SETUP.read_bytes() == LOCAL_SETUP_MARKER: - print(f"🧹 Deleting generated {LOCAL_SETUP} ...") - - -def main(): - default_host_runner = (f"{WASMTIME_HOST_RUNNER_VAR} run " - # Make sure the stack size will work for a pydebug - # build. - # Use 16 MiB stack. - "--wasm max-wasm-stack=16777216 " - # Enable thread support; causes use of preview1. - #"--wasm threads=y --wasi threads=y " - # Map the checkout to / to load the stdlib from /Lib. - "--dir {HOST_DIR}::{GUEST_DIR} " - # Set PYTHONPATH to the sysconfig data. - "--env {ENV_VAR_NAME}={ENV_VAR_VALUE}") - - parser = argparse.ArgumentParser() - subcommands = parser.add_subparsers(dest="subcommand") - build = subcommands.add_parser("build", help="Build everything") - configure_build = subcommands.add_parser("configure-build-python", - help="Run `configure` for the " - "build Python") - make_build = subcommands.add_parser("make-build-python", - help="Run `make` for the build Python") - configure_host = subcommands.add_parser("configure-host", - help="Run `configure` for the " - "host/WASI (pydebug builds " - "are inferred from the build " - "Python)") - make_host = subcommands.add_parser("make-host", - help="Run `make` for the host/WASI") - clean = subcommands.add_parser("clean", help="Delete files and directories " - "created by this script") - for subcommand in build, configure_build, make_build, configure_host, make_host: - subcommand.add_argument("--quiet", action="store_true", default=False, - dest="quiet", - help="Redirect output from subprocesses to a log file") - for subcommand in 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_build, configure_host: - subcommand.add_argument("args", nargs="*", - help="Extra arguments to pass to `configure`") - for subcommand in build, configure_host: - subcommand.add_argument("--wasi-sdk", type=pathlib.Path, - dest="wasi_sdk_path", - default=find_wasi_sdk(), - help="Path to wasi-sdk; defaults to " - "$WASI_SDK_PATH or /opt/wasi-sdk") - subcommand.add_argument("--host-runner", action="store", - default=default_host_runner, dest="host_runner", - help="Command template for running the WASI host " - "(default designed for wasmtime 14 or newer: " - f"`{default_host_runner}`)") - for subcommand in build, configure_host, make_host: - subcommand.add_argument("--host-triple", action="store", default="wasm32-wasip1", - help="The target triple for the WASI host build") - - context = parser.parse_args() - context.init_dir = pathlib.Path().absolute() - - dispatch = {"configure-build-python": configure_build_python, - "make-build-python": make_build_python, - "configure-host": configure_wasi_python, - "make-host": make_wasi_python, - "build": build_all, - "clean": clean_contents} - dispatch[context.subcommand](context) - - -if __name__ == "__main__": - main() + runpy.run_path(checkout / "Platforms" / "WASI", run_name="__main__") diff --git a/configure b/configure index 74df430d10d6bd..0cd98a193030dd 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.72 for python 3.15. +# Generated by GNU Autoconf 2.72 for python 3.16. # # Report bugs to <https://github.com/python/cpython/issues/>. # @@ -604,8 +604,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='python' PACKAGE_TARNAME='python' -PACKAGE_VERSION='3.15' -PACKAGE_STRING='python 3.15' +PACKAGE_VERSION='3.16' +PACKAGE_STRING='python 3.16' PACKAGE_BUGREPORT='https://github.com/python/cpython/issues/' PACKAGE_URL='' @@ -642,8 +642,14 @@ ac_includes_default="\ #endif" ac_header_c_list= +ac_func_c_list= ac_subst_vars='LTLIBOBJS MODULE_BLOCK +JIT_SHIM_BUILD_O +JIT_SHIM_O +JIT_STENCILS_H +MODULE_XXLIMITED_3_13_FALSE +MODULE_XXLIMITED_3_13_TRUE MODULE_XXLIMITED_35_FALSE MODULE_XXLIMITED_35_TRUE MODULE_XXLIMITED_FALSE @@ -813,6 +819,8 @@ MODULE__BISECT_FALSE MODULE__BISECT_TRUE MODULE__ASYNCIO_FALSE MODULE__ASYNCIO_TRUE +MODULE__MATH_INTEGER_FALSE +MODULE__MATH_INTEGER_TRUE MODULE_ARRAY_FALSE MODULE_ARRAY_TRUE MODULE_TIME_FALSE @@ -821,11 +829,13 @@ MODULE__IO_FALSE MODULE__IO_TRUE MODULE_BUILDTYPE _PYTHREAD_NAME_MAXLEN +BUILD_DETAILS TEST_MODULES OPENSSL_LDFLAGS OPENSSL_LIBS OPENSSL_INCLUDES ENSUREPIP +CFLAGS_CEVAL SRCDIRS THREADHEADERS PANEL_LIBS @@ -839,8 +849,9 @@ LIBREADLINE_CFLAGS WHEEL_PKG_DIR LIBPL PY_ENABLE_SHARED -PLATLIBDIR BINLIBDEST +LIBDEST +PLATLIBDIR LIBPYTHON MODULE_DEPS_SHARED EXT_SUFFIX @@ -854,6 +865,8 @@ HAVE_GETHOSTBYNAME_R_3_ARG HAVE_GETHOSTBYNAME_R_5_ARG HAVE_GETHOSTBYNAME_R_6_ARG LIBOBJS +REMOTE_DEBUGGING_LIBS +REMOTE_DEBUGGING_CFLAGS LIBZSTD_LIBS LIBZSTD_CFLAGS LIBLZMA_LIBS @@ -882,7 +895,6 @@ TCLTK_LIBS TCLTK_CFLAGS LIBSQLITE3_LIBS LIBSQLITE3_CFLAGS -LIBMPDEC_INTERNAL LIBMPDEC_LIBS LIBMPDEC_CFLAGS MODULE__CTYPES_MALLOC_CLOSURE @@ -904,12 +916,12 @@ LDSHARED SHLIB_SUFFIX DSYMUTIL_PATH DSYMUTIL -JIT_STENCILS_H REGEN_JIT_COMMAND UNIVERSAL_ARCH_FLAGS WASM_STDLIB WASM_ASSETS_DIR LDFLAGS_NOLTO +EXE_LDFLAGS LDFLAGS_NODIST CFLAGS_NODIST BASECFLAGS @@ -965,6 +977,7 @@ LDLIBRARY LIBRARY BUILDEXEEXT NO_AS_NEEDED +_Py_STACK_GROWS_DOWN MULTIARCH_CPPFLAGS PLATFORM_TRIPLET MULTIARCH @@ -1009,6 +1022,7 @@ UNIVERSALSDK host_exec_prefix host_prefix MACHDEP +MISSING_STDLIB_CONFIG PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG @@ -1080,6 +1094,7 @@ ac_user_opts=' enable_option_checking with_build_python with_pkg_config +with_missing_stdlib_config enable_universalsdk with_universal_archs with_framework_name @@ -1090,6 +1105,7 @@ enable_wasm_pthreads with_suffix enable_shared with_static_libpython +enable_static_libpython_for_interpreter enable_profiling enable_gil with_pydebug @@ -1102,6 +1118,7 @@ enable_bolt with_strict_overflow enable_safety enable_slower_safety +with_frame_pointers enable_experimental_jit with_dsymutil with_address_sanitizer @@ -1112,7 +1129,6 @@ with_hash_algorithm with_tzpath with_libs with_system_expat -with_system_libmpdec with_decimal_contextvar enable_loadable_sqlite_extensions with_dbmliborder @@ -1120,9 +1136,11 @@ enable_ipv6 with_doc_strings with_mimalloc with_pymalloc +with_pymalloc_hugepages with_c_locale_coercion with_valgrind with_dtrace +enable_epoll with_libm with_libc enable_big_digits @@ -1138,6 +1156,7 @@ with_openssl_rpath with_ssl_default_suites with_builtin_hashlib_hashes enable_test_modules +with_build_details_suffix ' ac_precious_vars='build_alias host_alias @@ -1734,7 +1753,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -'configure' configures python 3.15 to adapt to many kinds of systems. +'configure' configures python 3.16 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1800,7 +1819,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of python 3.15:";; + short | recursive ) echo "Configuration of python 3.16:";; esac cat <<\_ACEOF @@ -1819,11 +1838,16 @@ Optional Features: no) --enable-wasm-dynamic-linking Enable dynamic linking support for WebAssembly - (default is no) + (default is no); WASI requires an external dynamic + loader to handle imports --enable-wasm-pthreads Enable pthread emulation for WebAssembly (default is no) --enable-shared enable building a shared Python library (default is no) + --enable-static-libpython-for-interpreter + even with --enable-shared, statically link libpython + into the interpreter (default is to use the shared + library) --enable-profiling enable C-level code profiling with gprof (default is no) --disable-gil enable support for running without the GIL (default @@ -1845,6 +1869,7 @@ Optional Features: see Doc/library/sqlite3.rst (default is no) --enable-ipv6 enable ipv6 (with ipv4) support, see Doc/library/socket.rst (default is yes if supported) + --disable-epoll disable epoll (default is yes if supported) --enable-big-digits[=15|30] use big digits (30 or 15 bits) for Python longs (default is 30)] @@ -1853,12 +1878,15 @@ Optional Features: Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-build-python=python3.15 + --with-build-python=python3.16 path to build python binary for cross compiling - (default: _bootstrap_python or python3.15) + (default: _bootstrap_python or python3.16) --with-pkg-config=[yes|no|check] use pkg-config to detect build options (default is check) + --with-missing-stdlib-config=FILE + File with custom module error messages for missing + stdlib modules --with-universal-archs=ARCH specify the kind of macOS universal binary that should be created. This option is only valid when @@ -1888,6 +1916,8 @@ Optional Packages: is no) --with-strict-overflow if 'yes', add -fstrict-overflow to CFLAGS, else add -fno-strict-overflow (default is no) + --without-frame-pointers + build without frame pointers (default is no) --with-dsymutil link debug information into final executable with dsymutil in macOS (default is no) --with-address-sanitizer @@ -1909,9 +1939,6 @@ Optional Packages: --with-libs='lib1 ...' link against additional libs (default is no) --with-system-expat build pyexpat module using an installed expat library, see Doc/library/pyexpat.rst (default is no) - --with-system-libmpdec build _decimal module using an installed mpdecimal - library, see Doc/library/decimal.rst (default is - yes) --with-decimal-contextvar build _decimal module using a coroutine-local rather than a thread-local context (default is yes) @@ -1923,6 +1950,9 @@ Optional Packages: --with-mimalloc build with mimalloc memory allocator (default is yes if C11 stdatomic.h is available.) --with-pymalloc enable specialized mallocs (default is yes) + --with-pymalloc-hugepages + enable huge page support for pymalloc arenas + (default is no) --with-c-locale-coercion enable C locale coercion to a UTF-8 based locale (default is yes) @@ -1962,6 +1992,10 @@ Optional Packages: --with-builtin-hashlib-hashes=md5,sha1,sha2,sha3,blake2 builtin hash modules, md5, sha1, sha2, sha3 (with shake), blake2 + --with-build-details-suffix= + rename build-details.json to permit multiple + colocated Python installs; optionally specify a + custom suffix (default: no) Some influential environment variables: PKG_CONFIG path to pkg-config utility @@ -2103,7 +2137,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -python configure 3.15 +python configure 3.16 generated by GNU Autoconf 2.72 Copyright (C) 2023 Free Software Foundation, Inc. @@ -2323,6 +2357,60 @@ fi } # ac_fn_c_try_run +# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR +# ------------------------------------------------------------------ +# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR +# accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. +ac_fn_check_decl () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + as_decl_name=`echo $2|sed 's/ *(.*//'` + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +printf %s "checking whether $as_decl_name is declared... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + eval ac_save_FLAGS=\$$6 + as_fn_append $6 " $5" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main (void) +{ +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + eval "$3=yes" +else case e in #( + e) eval "$3=no" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + eval $6=\$ac_save_FLAGS + ;; +esac +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_check_decl + # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache @@ -2641,60 +2729,6 @@ printf "%s\n" "$ac_res" >&6; } } # ac_fn_c_check_func -# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR -# ------------------------------------------------------------------ -# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR -# accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. -ac_fn_check_decl () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - as_decl_name=`echo $2|sed 's/ *(.*//'` - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 -printf %s "checking whether $as_decl_name is declared... " >&6; } -if eval test \${$3+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` - eval ac_save_FLAGS=\$$6 - as_fn_append $6 " $5" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main (void) -{ -#ifndef $as_decl_name -#ifdef __cplusplus - (void) $as_decl_use; -#else - (void) $as_decl_name; -#endif -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - eval "$3=yes" -else case e in #( - e) eval "$3=no" ;; -esac -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - eval $6=\$ac_save_FLAGS - ;; -esac -fi -eval ac_res=\$$3 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -printf "%s\n" "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_check_decl - # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including @@ -2781,7 +2815,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by python $as_me 3.15, which was +It was created by python $as_me 3.16, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -3388,6 +3422,13 @@ as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" as_fn_append ac_header_c_list " wchar.h wchar_h HAVE_WCHAR_H" as_fn_append ac_header_c_list " minix/config.h minix_config_h HAVE_MINIX_CONFIG_H" +as_fn_append ac_func_c_list " acospi HAVE_ACOSPI" +as_fn_append ac_func_c_list " asinpi HAVE_ASINPI" +as_fn_append ac_func_c_list " atanpi HAVE_ATANPI" +as_fn_append ac_func_c_list " atan2pi HAVE_ATAN2PI" +as_fn_append ac_func_c_list " cospi HAVE_COSPI" +as_fn_append ac_func_c_list " sinpi HAVE_SINPI" +as_fn_append ac_func_c_list " tanpi HAVE_TANPI" # Auxiliary files required by this configure script. ac_aux_files="install-sh config.guess config.sub" @@ -3569,7 +3610,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu if test "$srcdir" != . -a "$srcdir" != "$(pwd)"; then # If we're building out-of-tree, we need to make sure the following # resources get picked up before their $srcdir counterparts. - # Objects/ -> typeslots.inc + # Objects/ -> slots_generated.c # Include/ -> Python.h # (A side effect of this is that these resources will automatically be # regenerated when building out-of-tree, regardless of whether or not @@ -3816,7 +3857,7 @@ fi -for ac_prog in python$PACKAGE_VERSION python3.13 python3.12 python3.11 python3.10 python3 python +for ac_prog in python$PACKAGE_VERSION python3.16 python3.15 python3.14 python3.13 python3.12 python3.11 python3.10 python3 python do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -3892,7 +3933,7 @@ rm confdefs.h mv confdefs.h.new confdefs.h -VERSION=3.15 +VERSION=3.16 # Version number of Python's own shared library file. @@ -4092,6 +4133,19 @@ if test "$with_pkg_config" = yes -a -z "$PKG_CONFIG"; then as_fn_error $? "pkg-config is required" "$LINENO" 5] fi + +# Check whether --with-missing-stdlib-config was given. +if test ${with_missing_stdlib_config+y} +then : + withval=$with_missing_stdlib_config; MISSING_STDLIB_CONFIG="$withval" +else case e in #( + e) MISSING_STDLIB_CONFIG="" + ;; +esac +fi + + + # Set name for machine-dependent library files { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking MACHDEP" >&5 @@ -4111,7 +4165,7 @@ then ac_sys_system=Linux ;; *-*-cygwin*) - ac_sys_system=Cygwin + ac_sys_system=CYGWIN ;; *-apple-ios*) ac_sys_system=iOS @@ -4119,6 +4173,9 @@ then *-*-darwin*) ac_sys_system=Darwin ;; + *-gnu) + ac_sys_system=GNU + ;; *-*-vxworks*) ac_sys_system=VxWorks ;; @@ -4359,7 +4416,7 @@ then : yes) case $ac_sys_system in Darwin) enableval=/Library/Frameworks ;; - iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; + iOS) enableval=Platforms/Apple/iOS/Frameworks/\$\(MULTIARCH\) ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac @@ -4470,9 +4527,9 @@ then : prefix=$PYTHONFRAMEWORKPREFIX PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" - RESSRCDIR=iOS/Resources + RESSRCDIR=Platforms/Apple/iOS/Resources - ac_config_files="$ac_config_files iOS/Resources/Info.plist" + ac_config_files="$ac_config_files Platforms/Apple/iOS/Resources/Info.plist" ;; *) @@ -4589,6 +4646,9 @@ if test "$cross_compiling" = yes; then _host_ident=$host_cpu esac ;; + *-gnu) + _host_ident=$host_cpu + ;; *-*-cygwin*) _host_ident= ;; @@ -4728,9 +4788,9 @@ esac if test $define_xopen_source = yes then - # X/Open 7, incorporating POSIX.1-2008 + # X/Open 8, incorporating POSIX.1-2024 -printf "%s\n" "#define _XOPEN_SOURCE 700" >>confdefs.h +printf "%s\n" "#define _XOPEN_SOURCE 800" >>confdefs.h # On Tru64 Unix 4.0F, defining _XOPEN_SOURCE also requires @@ -4742,7 +4802,7 @@ printf "%s\n" "#define _XOPEN_SOURCE_EXTENDED 1" >>confdefs.h -printf "%s\n" "#define _POSIX_C_SOURCE 200809L" >>confdefs.h +printf "%s\n" "#define _POSIX_C_SOURCE 202405L" >>confdefs.h fi @@ -6594,7 +6654,7 @@ else case e in #( ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in notfound +for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( @@ -6643,7 +6703,7 @@ else case e in #( ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in notfound +for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( @@ -6675,7 +6735,7 @@ printf "%s\n" "no" >&6; } fi if test "x$ac_pt_CXX" = x; then - CXX="g++" + CXX="notfound" else case $cross_compiling:$ac_tool_warned in yes:) @@ -6704,7 +6764,7 @@ else case e in #( ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in notfound +for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( @@ -6753,7 +6813,7 @@ else case e in #( ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in notfound +for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( @@ -6785,7 +6845,7 @@ printf "%s\n" "no" >&6; } fi if test "x$ac_pt_CXX" = x; then - CXX="c++" + CXX="notfound" else case $cross_compiling:$ac_tool_warned in yes:) @@ -6814,7 +6874,7 @@ else case e in #( ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in notfound +for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( @@ -6863,7 +6923,7 @@ else case e in #( ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in notfound +for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( @@ -6895,7 +6955,7 @@ printf "%s\n" "no" >&6; } fi if test "x$ac_pt_CXX" = x; then - CXX="clang++" + CXX="notfound" else case $cross_compiling:$ac_tool_warned in yes:) @@ -6924,7 +6984,7 @@ else case e in #( ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in notfound +for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( @@ -6973,7 +7033,7 @@ else case e in #( ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in notfound +for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( @@ -7005,7 +7065,7 @@ printf "%s\n" "no" >&6; } fi if test "x$ac_pt_CXX" = x; then - CXX="icpc" + CXX="notfound" else case $cross_compiling:$ac_tool_warned in yes:) @@ -7211,6 +7271,18 @@ if test x$MULTIARCH != x; then fi +# Guess C stack direction +case $host in #( + hppa*) : + _Py_STACK_GROWS_DOWN=0 ;; #( + *) : + _Py_STACK_GROWS_DOWN=1 ;; +esac + +printf "%s\n" "#define _Py_STACK_GROWS_DOWN $_Py_STACK_GROWS_DOWN" >>confdefs.h + + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for PEP 11 support tier" >&5 printf %s "checking for PEP 11 support tier... " >&6; } case $host/$ac_cv_cc_name in #( @@ -7382,7 +7454,7 @@ then : Emscripten) : ;; #( WASI) : - as_fn_error $? "WASI dynamic linking is not implemented yet." "$LINENO" 5 ;; #( + ;; #( *) : as_fn_error $? "--enable-wasm-dynamic-linking only applies to Emscripten and WASI" "$LINENO" 5 ;; @@ -7637,6 +7709,22 @@ fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --enable-static-libpython-for-interpreter" >&5 +printf %s "checking for --enable-static-libpython-for-interpreter... " >&6; } +# Check whether --enable-static-libpython-for-interpreter was given. +if test ${enable_static_libpython_for_interpreter+y} +then : + enableval=$enable_static_libpython_for_interpreter; +fi + + +if test -z "$enable_static_libpython_for_interpreter" +then + enable_static_libpython_for_interpreter="no" +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_static_libpython_for_interpreter" >&5 +printf "%s\n" "$enable_static_libpython_for_interpreter" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --enable-profiling" >&5 printf %s "checking for --enable-profiling... " >&6; } # Check whether --enable-profiling was given. @@ -7709,7 +7797,7 @@ printf "%s\n" "#define Py_ENABLE_SHARED 1" >>confdefs.h CYGWIN*) LDLIBRARY='libpython$(LDVERSION).dll.a' BLDLIBRARY='-L. -lpython$(LDVERSION)' - DLLLIBRARY='libpython$(LDVERSION).dll' + DLLLIBRARY='cygpython$(LDVERSION).dll' ;; SunOS*) LDLIBRARY='libpython$(LDVERSION).so' @@ -7768,17 +7856,13 @@ else # shared is disabled case $ac_sys_system in CYGWIN*) BLDLIBRARY='$(LIBRARY)' - LDLIBRARY='libpython$(LDVERSION).dll.a' + LDLIBRARY='libpython$(LDVERSION).a' ;; esac fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LDLIBRARY" >&5 printf "%s\n" "$LDLIBRARY" >&6; } -if test "$cross_compiling" = yes; then - RUNSHARED= -fi - # HOSTRUNNER - Program to run CPython for the host platform { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking HOSTRUNNER" >&5 printf %s "checking HOSTRUNNER... " >&6; } @@ -7903,8 +7987,10 @@ then : as_fn_append HOSTRUNNER " --experimental-wasm-memory64" fi ;; #( - WASI) : - HOSTRUNNER='wasmtime run --wasm max-wasm-stack=16777216 --wasi preview2=n --env PYTHONPATH=/$(shell realpath --relative-to $(abs_srcdir) $(abs_builddir))/$(shell cat pybuilddir.txt):/Lib --dir $(srcdir)::/' ;; #( + WASI) : + + as_fn_error $? "HOSTRUNNER must be set when cross-compiling to WASI" "$LINENO" 5 + ;; #( *) : HOSTRUNNER='' ;; @@ -7928,7 +8014,11 @@ if test "$PY_ENABLE_SHARED" = 1 || test "$enable_framework" ; then LIBRARY_DEPS="\$(LIBRARY) $LIBRARY_DEPS" fi # Link Python program to the shared library - LINK_PYTHON_OBJS='$(BLDLIBRARY)' + if test "$enable_static_libpython_for_interpreter" = "yes"; then + LINK_PYTHON_OBJS='$(LIBRARY_OBJS)' + else + LINK_PYTHON_OBJS='$(BLDLIBRARY)' + fi else if test "$STATIC_LIBPYTHON" = 0; then # Build Python needs object files but don't need to build @@ -8407,6 +8497,13 @@ fi if test "$Py_OPT" = 'true' ; then + # Check for conflicting CFLAGS=-O0 and --enable-optimizations + case "$CFLAGS" in + *-O0*) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: CFLAGS contains -O0 which may conflict with --enable-optimizations. Consider removing -O0 from CFLAGS for optimal performance." >&5 +printf "%s\n" "$as_me: WARNING: CFLAGS contains -O0 which may conflict with --enable-optimizations. Consider removing -O0 from CFLAGS for optimal performance." >&2;} + ;; + esac # Intentionally not forcing Py_LTO='true' here. Too many toolchains do not # compile working code using it and both test_distutils and test_gdb are # broken when you do manage to get a toolchain that works with it. People @@ -9036,7 +9133,100 @@ case "$ac_cv_cc_name" in fi ;; gcc) + # Check for 32-bit x86 ISA + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for i686" >&5 +printf %s "checking for i686... " >&6; } +if test ${ac_cv_i686+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + #ifdef __i386__ + # error "i386" + #endif + +int +main (void) +{ + + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_i686=no +else case e in #( + e) ac_cv_i686=yes ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_i686" >&5 +printf "%s\n" "$ac_cv_i686" >&6; } + PGO_PROF_GEN_FLAG="-fprofile-generate" + + # Use -fprofile-update=atomic to fix a random GCC internal error on PGO + # build (gh-145801) caused by corruption of profile data (.gcda files). + # + # gh-148535: On i686, using -fprofile-update=atomic makes the PGO build + # way slower (up to 47x slower). So far, the GCC internal error on PGO + # build was not seen on i686, so don't use this flag on i686. + if test "x$ac_cv_i686" = xno +then : + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fprofile-update=atomic" >&5 +printf %s "checking whether C compiler accepts -fprofile-update=atomic... " >&6; } +if test ${ax_cv_check_cflags___fprofile_update_atomic+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -fprofile-update=atomic" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ax_cv_check_cflags___fprofile_update_atomic=yes +else case e in #( + e) ax_cv_check_cflags___fprofile_update_atomic=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + CFLAGS=$ax_check_save_flags ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fprofile_update_atomic" >&5 +printf "%s\n" "$ax_cv_check_cflags___fprofile_update_atomic" >&6; } +if test "x$ax_cv_check_cflags___fprofile_update_atomic" = xyes +then : + PGO_PROF_GEN_FLAG="$PGO_PROF_GEN_FLAG -fprofile-update=atomic" +else case e in #( + e) : ;; +esac +fi + + +fi + PGO_PROF_USE_FLAG="-fprofile-use -fprofile-correction" LLVM_PROF_MERGER="true" LLVM_PROF_FILE="" @@ -9377,7 +9567,12 @@ BOLT_BINARIES='$(BUILDPYTHON)' if test "x$enable_shared" = xyes then : - BOLT_BINARIES="${BOLT_BINARIES} \$(INSTSONAME)" + if test "x$enable_static_libpython_for_interpreter" = xno +then : + + BOLT_BINARIES="${BOLT_BINARIES} \$(INSTSONAME)" + +fi fi @@ -9387,7 +9582,7 @@ fi printf %s "checking BOLT_COMMON_FLAGS... " >&6; } if test -z "${BOLT_COMMON_FLAGS}" then - BOLT_COMMON_FLAGS=" -update-debug-sections -skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1 " + BOLT_COMMON_FLAGS=" -update-debug-sections -skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1,sre_ucs1_match.lto_priv.0/1,sre_ucs2_match.lto_priv.0/1,sre_ucs4_match.lto_priv.0/1 " fi @@ -9406,7 +9601,7 @@ printf "%s\n" "$BOLT_INSTRUMENT_FLAGS" >&6; } printf %s "checking BOLT_APPLY_FLAGS... " >&6; } if test -z "${BOLT_APPLY_FLAGS}" then - BOLT_APPLY_FLAGS=" ${BOLT_COMMON_FLAGS} -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions -icf=1 -inline-all -split-eh -reorder-functions-use-hot-size -peepholes=none -jump-tables=aggressive -inline-ap -indirect-call-promotion=all -dyno-stats -use-gnu-stack -frame-opt=hot " + BOLT_APPLY_FLAGS=" ${BOLT_COMMON_FLAGS} -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions -icf=0 -inline-all -split-eh -reorder-functions-use-hot-size -peepholes=none -jump-tables=aggressive -inline-ap -indirect-call-promotion=all -dyno-stats -use-gnu-stack -frame-opt=hot " fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BOLT_APPLY_FLAGS" >&5 @@ -9603,8 +9798,8 @@ fi as_fn_append LDFLAGS_NODIST " -sWASM_BIGINT" as_fn_append LINKFORSHARED " -sFORCE_FILESYSTEM -lidbfs.js -lnodefs.js -lproxyfs.js -lworkerfs.js" - as_fn_append LINKFORSHARED " -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32,TTY" - as_fn_append LINKFORSHARED " -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,__PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET,_PyGILState_GetThisThreadState,__Py_DumpTraceback" + as_fn_append LINKFORSHARED " -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32,TTY,ERRNO_CODES" + as_fn_append LINKFORSHARED " -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,_PyGILState_GetThisThreadState,__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET" as_fn_append LINKFORSHARED " -sSTACK_SIZE=5MB" as_fn_append LINKFORSHARED " -sTEXTDECODER=2" @@ -9680,6 +9875,61 @@ fi ;; esac +if test "$ac_sys_system" = "Linux" -a "$cross_compiling" = no; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for thread stack size" >&5 +printf %s "checking for thread stack size... " >&6; } +if test ${ac_cv_thread_stack_size+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) + cat > conftest.c <<EOF +#include <pthread.h> + +int main() +{ + pthread_attr_t attrs; + size_t size; + + int rc = pthread_attr_init(&attrs); + if (rc != 0) { + return 2; + } + + rc = pthread_attr_getstacksize(&attrs, &size); + if (rc != 0) { + return 2; + } + + if (size < 1024 * 1024) { + return 1; + } + return 0; +} +EOF + + ac_cv_thread_stack_size=unknown + if $CC -pthread $CFLAGS conftest.c -o conftest &>/dev/null; then + ./conftest &>/dev/null + exitcode=$? + if test $exitcode -eq 1; then + ac_cv_thread_stack_size=1048576 + elif test $exitcode -eq 0; then + ac_cv_thread_stack_size="default" + fi + fi + rm -f conftest.c conftest + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_thread_stack_size" >&5 +printf "%s\n" "$ac_cv_thread_stack_size" >&6; } + + if test "$ac_cv_thread_stack_size" != "default" -a "$ac_cv_thread_stack_size" != "unknown"; then + LDFLAGS="$LDFLAGS -Wl,-z,stack-size=$ac_cv_thread_stack_size" + fi +fi + case $enable_wasm_dynamic_linking in #( yes) : ac_cv_func_dlopen=yes ;; #( @@ -9699,6 +9949,7 @@ esac + # The -arch flags for universal builds on macOS UNIVERSAL_ARCH_FLAGS= @@ -10059,9 +10310,269 @@ fi fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build with frame pointers" >&5 +printf %s "checking whether to build with frame pointers... " >&6; } + +# Check whether --with-frame-pointers was given. +if test ${with_frame_pointers+y} +then : + withval=$with_frame_pointers; +else case e in #( + e) with_frame_pointers=yes ;; +esac +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_frame_pointers" >&5 +printf "%s\n" "$with_frame_pointers" >&6; } + if test "x$ac_cv_gcc_compat" = xyes then : + frame_pointer_cflags= + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fno-omit-frame-pointer" >&5 +printf %s "checking whether C compiler accepts -fno-omit-frame-pointer... " >&6; } +if test ${ax_cv_check_cflags__Werror__fno_omit_frame_pointer+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Werror -fno-omit-frame-pointer" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ax_cv_check_cflags__Werror__fno_omit_frame_pointer=yes +else case e in #( + e) ax_cv_check_cflags__Werror__fno_omit_frame_pointer=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + CFLAGS=$ax_check_save_flags ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__fno_omit_frame_pointer" >&5 +printf "%s\n" "$ax_cv_check_cflags__Werror__fno_omit_frame_pointer" >&6; } +if test "x$ax_cv_check_cflags__Werror__fno_omit_frame_pointer" = xyes +then : + + frame_pointer_cflags="-fno-omit-frame-pointer" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -mno-omit-leaf-frame-pointer" >&5 +printf %s "checking whether C compiler accepts -mno-omit-leaf-frame-pointer... " >&6; } +if test ${ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Werror -mno-omit-leaf-frame-pointer" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer=yes +else case e in #( + e) ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + CFLAGS=$ax_check_save_flags ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer" >&5 +printf "%s\n" "$ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer" >&6; } +if test "x$ax_cv_check_cflags__Werror__mno_omit_leaf_frame_pointer" = xyes +then : + + frame_pointer_cflags="$frame_pointer_cflags -mno-omit-leaf-frame-pointer" + +else case e in #( + e) : ;; +esac +fi + + case $host_cpu in #( + arm|armv*) : + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -marm" >&5 +printf %s "checking whether C compiler accepts -marm... " >&6; } +if test ${ax_cv_check_cflags__Werror__marm+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Werror -marm" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ax_cv_check_cflags__Werror__marm=yes +else case e in #( + e) ax_cv_check_cflags__Werror__marm=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + CFLAGS=$ax_check_save_flags ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__marm" >&5 +printf "%s\n" "$ax_cv_check_cflags__Werror__marm" >&6; } +if test "x$ax_cv_check_cflags__Werror__marm" = xyes +then : + + frame_pointer_cflags="$frame_pointer_cflags -marm" + +else case e in #( + e) : ;; +esac +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -mno-thumb" >&5 +printf %s "checking whether C compiler accepts -mno-thumb... " >&6; } +if test ${ax_cv_check_cflags__Werror__mno_thumb+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Werror -mno-thumb" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ax_cv_check_cflags__Werror__mno_thumb=yes +else case e in #( + e) ax_cv_check_cflags__Werror__mno_thumb=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + CFLAGS=$ax_check_save_flags ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__mno_thumb" >&5 +printf "%s\n" "$ax_cv_check_cflags__Werror__mno_thumb" >&6; } +if test "x$ax_cv_check_cflags__Werror__mno_thumb" = xyes +then : + + frame_pointer_cflags="$frame_pointer_cflags -mno-thumb" + +else case e in #( + e) : ;; +esac +fi + + ;; #( + *) : + ;; +esac + case $host_cpu in #( + powerpc64le) : + + frame_pointer_cflags="" + ;; #( + *) : + ;; +esac + case $host_cpu in #( + s390*) : + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -mbackchain" >&5 +printf %s "checking whether C compiler accepts -mbackchain... " >&6; } +if test ${ax_cv_check_cflags__Werror__mbackchain+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Werror -mbackchain" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ax_cv_check_cflags__Werror__mbackchain=yes +else case e in #( + e) ax_cv_check_cflags__Werror__mbackchain=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + CFLAGS=$ax_check_save_flags ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__mbackchain" >&5 +printf "%s\n" "$ax_cv_check_cflags__Werror__mbackchain" >&6; } +if test "x$ax_cv_check_cflags__Werror__mbackchain" = xyes +then : + + frame_pointer_cflags="-mbackchain" + +else case e in #( + e) : ;; +esac +fi + + ;; #( + *) : + ;; +esac + +else case e in #( + e) : ;; +esac +fi + + if test -n "$frame_pointer_cflags" && test "x$with_frame_pointers" != xno; then + BASECFLAGS="$frame_pointer_cflags $BASECFLAGS" + +printf "%s\n" "#define _Py_WITH_FRAME_POINTERS 1" >>confdefs.h + + fi + CFLAGS_NODIST="$CFLAGS_NODIST -std=c11" @@ -10866,8 +11377,7 @@ then : else case e in #( e) as_fn_append CFLAGS_NODIST " $jit_flags" - REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\"" - JIT_STENCILS_H="jit_stencils.h" + REGEN_JIT_COMMAND="\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\" --llvm-version=\"$LLVM_VERSION\" --llvm-tools-install-dir=\"$LLVM_TOOLS_INSTALL_DIR\"" if test "x$Py_DEBUG" = xtrue then : as_fn_append REGEN_JIT_COMMAND " --debug" @@ -10875,14 +11385,14 @@ fi ;; esac fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $tier2_flags $jit_flags" >&5 printf "%s\n" "$tier2_flags $jit_flags" >&6; } if test "$disable_gil" = "yes" -a "$enable_experimental_jit" != "no"; then # GH-133171: This configuration builds the JIT but never actually uses it, # which is surprising (and strictly worse than not building it at all): - as_fn_error $? "--enable-experimental-jit cannot be used with --disable-gil." "$LINENO" 5 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: --enable-experimental-jit does not work correctly with --disable-gil." >&5 +printf "%s\n" "$as_me: WARNING: --enable-experimental-jit does not work correctly with --disable-gil." >&2;} fi case "$ac_cv_cc_name" in @@ -11414,12 +11924,6 @@ if test "x$ac_cv_header_spawn_h" = xyes then : printf "%s\n" "#define HAVE_SPAWN_H 1" >>confdefs.h -fi -ac_fn_c_check_header_compile "$LINENO" "stropts.h" "ac_cv_header_stropts_h" "$ac_includes_default" -if test "x$ac_cv_header_stropts_h" = xyes -then : - printf "%s\n" "#define HAVE_STROPTS_H 1" >>confdefs.h - fi ac_fn_c_check_header_compile "$LINENO" "sys/audioio.h" "ac_cv_header_sys_audioio_h" "$ac_includes_default" if test "x$ac_cv_header_sys_audioio_h" = xyes @@ -11904,6 +12408,105 @@ fi fi +# On Linux, stropts.h may be empty +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 +printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } +if test ${ac_cv_c_undeclared_builtin_options+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) ac_save_CFLAGS=$CFLAGS + ac_cv_c_undeclared_builtin_options='cannot detect' + for ac_arg in '' -fno-builtin; do + CFLAGS="$ac_save_CFLAGS $ac_arg" + # This test program should *not* compile successfully. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ +(void) strchr; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + +else case e in #( + e) # This test program should compile successfully. + # No library function is consistently available on + # freestanding implementations, so test against a dummy + # declaration. Include always-available headers on the + # off chance that they somehow elicit warnings. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <float.h> +#include <limits.h> +#include <stdarg.h> +#include <stddef.h> +extern void ac_decl (int, char *); + +int +main (void) +{ +(void) ac_decl (0, (char *) 0); + (void) ac_decl; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + if test x"$ac_arg" = x +then : + ac_cv_c_undeclared_builtin_options='none needed' +else case e in #( + e) ac_cv_c_undeclared_builtin_options=$ac_arg ;; +esac +fi + break +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + done + CFLAGS=$ac_save_CFLAGS + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 +printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } + case $ac_cv_c_undeclared_builtin_options in #( + 'cannot detect') : + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} +as_fn_error $? "cannot make $CC report undeclared builtins +See 'config.log' for more details" "$LINENO" 5; } ;; #( + 'none needed') : + ac_c_undeclared_builtin_options='' ;; #( + *) : + ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; +esac + +ac_fn_check_decl "$LINENO" "I_PUSH" "ac_cv_have_decl_I_PUSH" " + #ifdef HAVE_SYS_TYPES_H + # include <sys/types.h> + #endif + #include <stropts.h> + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl_I_PUSH" = xyes +then : + + +printf "%s\n" "#define HAVE_STROPTS_H 1" >>confdefs.h + +fi + # bluetooth/bluetooth.h has been known to not compile with -std=c99. # http://permalink.gmane.org/gmane.linux.bluez.kernel/22294 SAVE_CFLAGS=$CFLAGS @@ -11995,7 +12598,7 @@ then : fi -# On Linux, can.h, can/bcm.h, can/j1939.h, can/raw.h require sys/socket.h +# On Linux, can.h, can/bcm.h, can/isotp.h, can/j1939.h, can/raw.h require sys/socket.h # On NetBSD, netcan/can.h requires sys/socket.h ac_fn_c_check_header_compile "$LINENO" "linux/can.h" "ac_cv_header_linux_can_h" " #ifdef HAVE_SYS_SOCKET_H @@ -12018,6 +12621,17 @@ if test "x$ac_cv_header_linux_can_bcm_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_CAN_BCM_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "linux/can/isotp.h" "ac_cv_header_linux_can_isotp_h" " +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +" +if test "x$ac_cv_header_linux_can_isotp_h" = xyes +then : + printf "%s\n" "#define HAVE_LINUX_CAN_ISOTP_H 1" >>confdefs.h + fi ac_fn_c_check_header_compile "$LINENO" "linux/can/j1939.h" "ac_cv_header_linux_can_j1939_h" " #ifdef HAVE_SYS_SOCKET_H @@ -13626,8 +14240,8 @@ then : fi;; CYGWIN*) - LDSHARED="gcc -shared -Wl,--enable-auto-image-base" - LDCXXSHARED="g++ -shared -Wl,--enable-auto-image-base";; + LDSHARED='$(CC) -shared -Wl,--enable-auto-image-base' + LDCXXSHARED='$(CXX) -shared -Wl,--enable-auto-image-base';; *) LDSHARED="ld";; esac fi @@ -13811,13 +14425,37 @@ printf "%s\n" "$SHLIBS" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking perf trampoline" >&5 printf %s "checking perf trampoline... " >&6; } +PERF_TRAMPOLINE_OBJ="" case $PLATFORM_TRIPLET in #( x86_64-linux-gnu) : - perf_trampoline=yes ;; #( + perf_trampoline=yes + PERF_TRAMPOLINE_OBJ=Python/asm_trampoline_x86_64.o ;; #( aarch64-linux-gnu) : - perf_trampoline=yes ;; #( + perf_trampoline=yes + PERF_TRAMPOLINE_OBJ=Python/asm_trampoline_aarch64.o ;; #( darwin) : - perf_trampoline=yes ;; #( + case $MACOSX_DEPLOYMENT_TARGET in #( + 10.[0-9]|10.1[0-1]) : + perf_trampoline=no ;; #( + *) : + perf_trampoline=yes + if test "${enable_universalsdk}" && test "$UNIVERSAL_ARCHS" = "universal2"; then + PERF_TRAMPOLINE_OBJ=Python/asm_trampoline_universal2.o + else + case "$host_cpu" in + x86_64) + PERF_TRAMPOLINE_OBJ=Python/asm_trampoline_x86_64.o + ;; + aarch64|arm64) + PERF_TRAMPOLINE_OBJ=Python/asm_trampoline_aarch64.o + ;; + *) + perf_trampoline=no + ;; + esac + fi + ;; +esac ;; #( *) : perf_trampoline=no ;; @@ -13831,14 +14469,6 @@ then : printf "%s\n" "#define PY_HAVE_PERF_TRAMPOLINE 1" >>confdefs.h - PERF_TRAMPOLINE_OBJ=Python/asm_trampoline.o - - if test "x$Py_DEBUG" = xtrue -then : - - as_fn_append BASECFLAGS " -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer" - -fi fi @@ -14031,6 +14661,52 @@ fi done +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libgcc frame registration functions" >&5 +printf %s "checking for libgcc frame registration functions... " >&6; } +if test ${ac_cv_have_libgcc_eh_frame_registration+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +void __register_frame(const void *); +void __deregister_frame(const void *); + +int +main (void) +{ + +__register_frame(0); +__deregister_frame(0); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_have_libgcc_eh_frame_registration=yes +else case e in #( + e) ac_cv_have_libgcc_eh_frame_registration=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_libgcc_eh_frame_registration" >&5 +printf "%s\n" "$ac_cv_have_libgcc_eh_frame_registration" >&6; } +if test "x$ac_cv_have_libgcc_eh_frame_registration" = xyes +then : + + +printf "%s\n" "#define _Py_HAVE_LIBGCC_EH_FRAME_REGISTRATION 1" >>confdefs.h + + +fi + if test "x$ac_cv_require_ldl" = xyes then : @@ -15751,32 +16427,6 @@ printf "%s\n" "#define _Py_FFI_SUPPORT_C_COMPLEX 1" >>confdefs.h fi -# Check for use of the system libmpdec library -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-system-libmpdec" >&5 -printf %s "checking for --with-system-libmpdec... " >&6; } - - -# Check whether --with-system_libmpdec was given. -if test ${with_system_libmpdec+y} -then : - withval=$with_system_libmpdec; if test "x$with_system_libmpdec" = xno -then : - LIBMPDEC_CFLAGS="-I\$(srcdir)/Modules/_decimal/libmpdec" - LIBMPDEC_LIBS="-lm \$(LIBMPDEC_A)" - LIBMPDEC_INTERNAL="\$(LIBMPDEC_HEADERS) \$(LIBMPDEC_A)" - have_mpdec=yes - with_system_libmpdec=no -fi -else case e in #( - e) with_system_libmpdec="yes" ;; -esac -fi - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_system_libmpdec" >&5 -printf "%s\n" "$with_system_libmpdec" >&6; } - -if test "x$with_system_libmpdec" = xyes -then : pkg_failed=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libmpdec >= 2.5.0" >&5 @@ -15836,26 +16486,122 @@ fi # Put the nasty error message in config.log where it belongs echo "$LIBMPDEC_PKG_ERRORS" >&5 - LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} - LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} - LIBMPDEC_INTERNAL= -elif test $pkg_failed = untried; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } - LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} - LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} - LIBMPDEC_INTERNAL= -else - LIBMPDEC_CFLAGS=$pkg_cv_LIBMPDEC_CFLAGS - LIBMPDEC_LIBS=$pkg_cv_LIBMPDEC_LIBS - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } + save_CFLAGS=$CFLAGS +save_CPPFLAGS=$CPPFLAGS +save_LDFLAGS=$LDFLAGS +save_LIBS=$LIBS + + + CPPFLAGS="$CPPFLAGS $LIBMPDEC_CFLAGS" + LIBS="$LIBS $LIBMPDEC_LIBS" + ac_fn_c_check_header_compile "$LINENO" "mpdecimal.h" "ac_cv_header_mpdecimal_h" "$ac_includes_default" +if test "x$ac_cv_header_mpdecimal_h" = xyes +then : + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for mpd_version in -lmpdec" >&5 +printf %s "checking for mpd_version in -lmpdec... " >&6; } +if test ${ac_cv_lib_mpdec_mpd_version+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS +LIBS="-lmpdec $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char mpd_version (void); +int +main (void) +{ +return mpd_version (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_mpdec_mpd_version=yes +else case e in #( + e) ac_cv_lib_mpdec_mpd_version=no ;; +esac fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS ;; +esac fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mpdec_mpd_version" >&5 +printf "%s\n" "$ac_cv_lib_mpdec_mpd_version" >&6; } +if test "x$ac_cv_lib_mpdec_mpd_version" = xyes +then : -if test "x$with_system_libmpdec" = xyes + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + #include <mpdecimal.h> + #if MPD_VERSION_HEX < 0x02050000 + # error "mpdecimal 2.5.0 or higher required" + #endif + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" then : + have_mpdec=yes +else case e in #( + e) have_mpdec=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + +else case e in #( + e) have_mpdec=no ;; +esac +fi + + +else case e in #( + e) have_mpdec=no ;; +esac +fi + + + if test "x$have_mpdec" = xyes +then : + + LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} + LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} + +fi + +CFLAGS=$save_CFLAGS +CPPFLAGS=$save_CPPFLAGS +LDFLAGS=$save_LDFLAGS +LIBS=$save_LIBS + + + +elif test $pkg_failed = untried; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + save_CFLAGS=$CFLAGS save_CPPFLAGS=$CPPFLAGS save_LDFLAGS=$LDFLAGS @@ -15864,33 +16610,101 @@ save_LIBS=$LIBS CPPFLAGS="$CPPFLAGS $LIBMPDEC_CFLAGS" LIBS="$LIBS $LIBMPDEC_LIBS" + ac_fn_c_check_header_compile "$LINENO" "mpdecimal.h" "ac_cv_header_mpdecimal_h" "$ac_includes_default" +if test "x$ac_cv_header_mpdecimal_h" = xyes +then : - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for mpd_version in -lmpdec" >&5 +printf %s "checking for mpd_version in -lmpdec... " >&6; } +if test ${ac_cv_lib_mpdec_mpd_version+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) ac_check_lib_save_LIBS=$LIBS +LIBS="-lmpdec $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. + The 'extern "C"' is for builds by C++ compilers; + although this is not generally supported in C code supporting it here + has little cost and some practical benefit (sr 110532). */ +#ifdef __cplusplus +extern "C" +#endif +char mpd_version (void); +int +main (void) +{ +return mpd_version (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_mpdec_mpd_version=yes +else case e in #( + e) ac_cv_lib_mpdec_mpd_version=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mpdec_mpd_version" >&5 +printf "%s\n" "$ac_cv_lib_mpdec_mpd_version" >&6; } +if test "x$ac_cv_lib_mpdec_mpd_version" = xyes +then : + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - #include <mpdecimal.h> - #if MPD_VERSION_HEX < 0x02050000 - # error "mpdecimal 2.5.0 or higher required" - #endif + + #include <mpdecimal.h> + #if MPD_VERSION_HEX < 0x02050000 + # error "mpdecimal 2.5.0 or higher required" + #endif int main (void) { -const char *x = mpd_version(); + ; return 0; } _ACEOF -if ac_fn_c_try_link "$LINENO" +if ac_fn_c_try_compile "$LINENO" then : have_mpdec=yes else case e in #( e) have_mpdec=no ;; esac fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + +else case e in #( + e) have_mpdec=no ;; +esac +fi + + +else case e in #( + e) have_mpdec=no ;; +esac +fi + + + if test "x$have_mpdec" = xyes +then : + + LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} + LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} + +fi CFLAGS=$save_CFLAGS CPPFLAGS=$save_CPPFLAGS @@ -15898,12 +16712,13 @@ LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS -fi -# Disable forced inlining in debug builds, see GH-94847 -if test "x$with_pydebug" = xyes -then : - as_fn_append LIBMPDEC_CFLAGS " -DTEST_COVERAGE" +else + LIBMPDEC_CFLAGS=$pkg_cv_LIBMPDEC_CFLAGS + LIBMPDEC_LIBS=$pkg_cv_LIBMPDEC_LIBS + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } + have_mpdec=yes fi # Check whether _decimal should use a coroutine-local or thread-local context @@ -15930,90 +16745,6 @@ fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_decimal_contextvar" >&5 printf "%s\n" "$with_decimal_contextvar" >&6; } -if test "x$with_system_libmpdec" = xno -then : - # Check for libmpdec machine flavor - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for decimal libmpdec machine" >&5 -printf %s "checking for decimal libmpdec machine... " >&6; } - case $ac_sys_system in #( - Darwin*) : - libmpdec_system=Darwin ;; #( - SunOS*) : - libmpdec_system=sunos ;; #( - *) : - libmpdec_system=other - ;; -esac - - libmpdec_machine=unknown - if test "$libmpdec_system" = Darwin; then - # universal here means: build libmpdec with the same arch options - # the python interpreter was built with - libmpdec_machine=universal - elif test $ac_cv_sizeof_size_t -eq 8; then - if test "$ac_cv_gcc_asm_for_x64" = yes; then - libmpdec_machine=x64 - elif test "$ac_cv_type___uint128_t" = yes; then - libmpdec_machine=uint128 - else - libmpdec_machine=ansi64 - fi - elif test $ac_cv_sizeof_size_t -eq 4; then - if test "$ac_cv_gcc_asm_for_x87" = yes -a "$libmpdec_system" != sunos; then - case $ac_cv_cc_name in #( - *gcc*) : - libmpdec_machine=ppro ;; #( - *clang*) : - libmpdec_machine=ppro ;; #( - *) : - libmpdec_machine=ansi32 - ;; -esac - else - libmpdec_machine=ansi32 - fi - fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libmpdec_machine" >&5 -printf "%s\n" "$libmpdec_machine" >&6; } - - case $libmpdec_machine in #( - x64) : - as_fn_append LIBMPDEC_CFLAGS " -DCONFIG_64=1 -DASM=1" ;; #( - uint128) : - as_fn_append LIBMPDEC_CFLAGS " -DCONFIG_64=1 -DANSI=1 -DHAVE_UINT128_T=1" ;; #( - ansi64) : - as_fn_append LIBMPDEC_CFLAGS " -DCONFIG_64=1 -DANSI=1" ;; #( - ppro) : - as_fn_append LIBMPDEC_CFLAGS " -DCONFIG_32=1 -DANSI=1 -DASM=1 -Wno-unknown-pragmas" ;; #( - ansi32) : - as_fn_append LIBMPDEC_CFLAGS " -DCONFIG_32=1 -DANSI=1" ;; #( - ansi-legacy) : - as_fn_append LIBMPDEC_CFLAGS " -DCONFIG_32=1 -DANSI=1 -DLEGACY_COMPILER=1" ;; #( - universal) : - as_fn_append LIBMPDEC_CFLAGS " -DUNIVERSAL=1" ;; #( - *) : - as_fn_error $? "_decimal: unsupported architecture" "$LINENO" 5 - ;; -esac -fi - -if test "$have_ipa_pure_const_bug" = yes; then - # Some versions of gcc miscompile inline asm: - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491 - # https://gcc.gnu.org/ml/gcc/2010-11/msg00366.html - as_fn_append LIBMPDEC_CFLAGS " -fno-ipa-pure-const" -fi - -if test "$have_glibc_memmove_bug" = yes; then - # _FORTIFY_SOURCE wrappers for memmove and bcopy are incorrect: - # https://sourceware.org/ml/libc-alpha/2010-12/msg00009.html - as_fn_append LIBMPDEC_CFLAGS " -U_FORTIFY_SOURCE" -fi - - - - - @@ -18138,6 +18869,7 @@ else case e in #( if (pthread_attr_init(&attr)) return (-1); if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) return (-1); if (pthread_create(&id, &attr, foo, NULL)) return (-1); + if (pthread_join(id, NULL)) return (-1); return (0); } _ACEOF @@ -18826,6 +19558,72 @@ then : printf "%s\n" "#define HAVE_BUILTIN_ATOMIC 1" >>confdefs.h +fi + +# Check for __builtin_shufflevector with 128-bit vector support on an +# architecture where it compiles to worthwhile native SIMD instructions. +# Used for SIMD-accelerated bytes.hex() in Python/pystrhex.c. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __builtin_shufflevector" >&5 +printf %s "checking for __builtin_shufflevector... " >&6; } +if test ${ac_cv_efficient_builtin_shufflevector+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + /* __builtin_shufflevector is available on many platforms, but 128-bit + vector code is only worthwhile on architectures with native SIMD: + x86-64 (SSE2, always available), ARM64 (NEON, always available), + or ARM32 when NEON is enabled via compiler flags (e.g. -march=native + on RPi3+). On ARM32 without NEON (e.g. armv6 builds), the compiler + has the builtin but generates slow scalar code instead. */ + #if !defined(__x86_64__) && !defined(__aarch64__) && \ + !(defined(__arm__) && defined(__ARM_NEON)) + # error "128-bit vector SIMD not worthwhile on this architecture" + #endif + typedef unsigned char v16u8 __attribute__((vector_size(16))); + +int +main (void) +{ + + v16u8 a = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + v16u8 b = {16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}; + v16u8 c = __builtin_shufflevector(a, b, + 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23); + (void)c; + return 0; + + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_efficient_builtin_shufflevector=yes +else case e in #( + e) ac_cv_efficient_builtin_shufflevector=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_efficient_builtin_shufflevector" >&5 +printf "%s\n" "$ac_cv_efficient_builtin_shufflevector" >&6; } + +if test "x$ac_cv_efficient_builtin_shufflevector" = xyes +then : + + +printf "%s\n" "#define _Py_HAVE_EFFICIENT_BUILTIN_SHUFFLEVECTOR 1" >>confdefs.h + + fi # --with-mimalloc @@ -18896,6 +19694,49 @@ fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_pymalloc" >&5 printf "%s\n" "$with_pymalloc" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-pymalloc-hugepages" >&5 +printf %s "checking for --with-pymalloc-hugepages... " >&6; } + +# Check whether --with-pymalloc-hugepages was given. +if test ${with_pymalloc_hugepages+y} +then : + withval=$with_pymalloc_hugepages; +fi + +if test "$with_pymalloc_hugepages" = "yes" +then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include <sys/mman.h> + +int +main (void) +{ + +int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB; +(void)flags; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + +printf "%s\n" "#define PYMALLOC_USE_HUGEPAGES 1" >>confdefs.h + +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: --with-pymalloc-hugepages requested but MAP_HUGETLB not found" >&5 +printf "%s\n" "$as_me: WARNING: --with-pymalloc-hugepages requested but MAP_HUGETLB not found" >&2;} + with_pymalloc_hugepages=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${with_pymalloc_hugepages:-no}" >&5 +printf "%s\n" "${with_pymalloc_hugepages:-no}" >&6; } + # Check for --with-c-locale-coercion { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-c-locale-coercion" >&5 printf %s "checking for --with-c-locale-coercion... " >&6; } @@ -19037,15 +19878,27 @@ printf "%s\n" "#define WITH_DTRACE 1" >>confdefs.h # linked into the binary. Correspondingly, dtrace(1) is missing the ELF # generation flag '-G'. We check for presence of this flag, rather than # hardcoding support by OS, in the interest of robustness. + # + # NetBSD DTrace requires the -x nolibs flag to avoid system library conflicts + # and uses header generation for testing instead of object generation. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether DTrace probes require linking" >&5 printf %s "checking whether DTrace probes require linking... " >&6; } if test ${ac_cv_dtrace_link+y} then : printf %s "(cached) " >&6 else case e in #( - e) ac_cv_dtrace_link=no + e) + ac_cv_dtrace_link=no echo 'BEGIN{}' > conftest.d - "$DTRACE" $DFLAGS -G -s conftest.d -o conftest.o > /dev/null 2>&1 && \ + case $host in + *netbsd*) + DTRACE_TEST_FLAGS="-x nolibs -h" + ;; + *) + DTRACE_TEST_FLAGS="-G" + ;; + esac + "$DTRACE" $DFLAGS $DTRACE_TEST_FLAGS -s conftest.d -o conftest.o > /dev/null 2>&1 && \ ac_cv_dtrace_link=yes ;; esac @@ -19055,6 +19908,12 @@ printf "%s\n" "$ac_cv_dtrace_link" >&6; } if test "$ac_cv_dtrace_link" = "yes"; then DTRACE_OBJS="Python/pydtrace.o" fi + # Set NetBSD-specific DTrace flags in DFLAGS + case $host in + *netbsd*) + DFLAGS="$DFLAGS -x nolibs" + ;; + esac fi PLATFORM_HEADERS= @@ -19063,7 +19922,7 @@ PLATFORM_OBJS= case $ac_sys_system in #( Emscripten) : - as_fn_append PLATFORM_OBJS ' Python/emscripten_signal.o Python/emscripten_trampoline.o Python/emscripten_syscalls.o' + as_fn_append PLATFORM_OBJS ' Python/emscripten_signal.o Python/emscripten_trampoline.o Python/emscripten_trampoline_wasm.o Python/emscripten_syscalls.o' as_fn_append PLATFORM_HEADERS ' $(srcdir)/Include/internal/pycore_emscripten_signal.h $(srcdir)/Include/internal/pycore_emscripten_trampoline.h' ;; #( *) : @@ -19188,6 +20047,12 @@ if test "x$ac_cv_func_chown" = xyes then : printf "%s\n" "#define HAVE_CHOWN 1" >>confdefs.h +fi +ac_fn_c_check_func "$LINENO" "clearenv" "ac_cv_func_clearenv" +if test "x$ac_cv_func_clearenv" = xyes +then : + printf "%s\n" "#define HAVE_CLEARENV 1" >>confdefs.h + fi ac_fn_c_check_func "$LINENO" "clock" "ac_cv_func_clock" if test "x$ac_cv_func_clock" = xyes @@ -19236,12 +20101,6 @@ if test "x$ac_cv_func_dup" = xyes then : printf "%s\n" "#define HAVE_DUP 1" >>confdefs.h -fi -ac_fn_c_check_func "$LINENO" "dup3" "ac_cv_func_dup3" -if test "x$ac_cv_func_dup3" = xyes -then : - printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h - fi ac_fn_c_check_func "$LINENO" "execv" "ac_cv_func_execv" if test "x$ac_cv_func_execv" = xyes @@ -19716,12 +20575,6 @@ if test "x$ac_cv_func_pipe" = xyes then : printf "%s\n" "#define HAVE_PIPE 1" >>confdefs.h -fi -ac_fn_c_check_func "$LINENO" "pipe2" "ac_cv_func_pipe2" -if test "x$ac_cv_func_pipe2" = xyes -then : - printf "%s\n" "#define HAVE_PIPE2 1" >>confdefs.h - fi ac_fn_c_check_func "$LINENO" "plock" "ac_cv_func_plock" if test "x$ac_cv_func_plock" = xyes @@ -19734,6 +20587,12 @@ if test "x$ac_cv_func_poll" = xyes then : printf "%s\n" "#define HAVE_POLL 1" >>confdefs.h +fi +ac_fn_c_check_func "$LINENO" "ppoll" "ac_cv_func_ppoll" +if test "x$ac_cv_func_ppoll" = xyes +then : + printf "%s\n" "#define HAVE_PPOLL 1" >>confdefs.h + fi ac_fn_c_check_func "$LINENO" "posix_fadvise" "ac_cv_func_posix_fadvise" if test "x$ac_cv_func_posix_fadvise" = xyes @@ -20349,6 +21208,22 @@ then : fi +# os.statx uses Linux's statx function. AIX also has a function named statx, +# but it's unrelated. Check only on Linux (including Android). +case $ac_sys_system in #( + Linux*) : + ac_fn_c_check_func "$LINENO" "statx" "ac_cv_func_statx" +if test "x$ac_cv_func_statx" = xyes +then : + printf "%s\n" "#define HAVE_STATX 1" >>confdefs.h + +fi + + ;; #( + *) : + ;; +esac + # Force lchmod off for Linux. Linux disallows changing the mode of symbolic # links. Some libc implementations have a stub lchmod implementation that always # returns an error. @@ -20367,7 +21242,13 @@ fi # header definition prevents usage - autoconf doesn't use the headers), or # raise an error if used at runtime. Force these symbols off. if test "$ac_sys_system" != "iOS" ; then - ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" + ac_fn_c_check_func "$LINENO" "dup3" "ac_cv_func_dup3" +if test "x$ac_cv_func_dup3" = xyes +then : + printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h + +fi +ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : printf "%s\n" "#define HAVE_GETENTROPY 1" >>confdefs.h @@ -20378,6 +21259,12 @@ if test "x$ac_cv_func_getgroups" = xyes then : printf "%s\n" "#define HAVE_GETGROUPS 1" >>confdefs.h +fi +ac_fn_c_check_func "$LINENO" "pipe2" "ac_cv_func_pipe2" +if test "x$ac_cv_func_pipe2" = xyes +then : + printf "%s\n" "#define HAVE_PIPE2 1" >>confdefs.h + fi ac_fn_c_check_func "$LINENO" "system" "ac_cv_func_system" if test "x$ac_cv_func_system" = xyes @@ -20388,89 +21275,6 @@ fi fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 -printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } -if test ${ac_cv_c_undeclared_builtin_options+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) ac_save_CFLAGS=$CFLAGS - ac_cv_c_undeclared_builtin_options='cannot detect' - for ac_arg in '' -fno-builtin; do - CFLAGS="$ac_save_CFLAGS $ac_arg" - # This test program should *not* compile successfully. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ -(void) strchr; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - -else case e in #( - e) # This test program should compile successfully. - # No library function is consistently available on - # freestanding implementations, so test against a dummy - # declaration. Include always-available headers on the - # off chance that they somehow elicit warnings. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <float.h> -#include <limits.h> -#include <stdarg.h> -#include <stddef.h> -extern void ac_decl (int, char *); - -int -main (void) -{ -(void) ac_decl (0, (char *) 0); - (void) ac_decl; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - if test x"$ac_arg" = x -then : - ac_cv_c_undeclared_builtin_options='none needed' -else case e in #( - e) ac_cv_c_undeclared_builtin_options=$ac_arg ;; -esac -fi - break -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; -esac -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - done - CFLAGS=$ac_save_CFLAGS - ;; -esac -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 -printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } - case $ac_cv_c_undeclared_builtin_options in #( - 'cannot detect') : - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} -as_fn_error $? "cannot make $CC report undeclared builtins -See 'config.log' for more details" "$LINENO" 5; } ;; #( - 'none needed') : - ac_c_undeclared_builtin_options='' ;; #( - *) : - ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; -esac - ac_fn_check_decl "$LINENO" "dirfd" "ac_cv_have_decl_dirfd" "#include <sys/types.h> #include <dirent.h> " "$ac_c_undeclared_builtin_options" "CFLAGS" @@ -20730,6 +21534,29 @@ fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --disable-epoll" >&5 +printf %s "checking for --disable-epoll... " >&6; } +# Check whether --enable-epoll was given. +if test ${enable_epoll+y} +then : + enableval=$enable_epoll; if test "x$enable_epoll" = xno +then : + disable_epoll=yes +else case e in #( + e) disable_epoll=no ;; +esac +fi +else case e in #( + e) disable_epoll=no + ;; +esac +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $disable_epoll" >&5 +printf "%s\n" "$disable_epoll" >&6; } +if test "$disable_epoll" = "no" +then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for epoll_create" >&5 printf %s "checking for epoll_create... " >&6; } @@ -20811,6 +21638,8 @@ fi +fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for kqueue" >&5 @@ -21693,19 +22522,19 @@ fi pkg_failed=no -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib >= 1.2.0" >&5 -printf %s "checking for zlib >= 1.2.0... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib >= 1.2.2.1" >&5 +printf %s "checking for zlib >= 1.2.2.1... " >&6; } if test -n "$ZLIB_CFLAGS"; then pkg_cv_ZLIB_CFLAGS="$ZLIB_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.0\""; } >&5 - ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.0") 2>&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.2.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.2.1") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib >= 1.2.0" 2>/dev/null` + pkg_cv_ZLIB_CFLAGS=`$PKG_CONFIG --cflags "zlib >= 1.2.2.1" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -21717,12 +22546,12 @@ if test -n "$ZLIB_LIBS"; then pkg_cv_ZLIB_LIBS="$ZLIB_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.0\""; } >&5 - ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.0") 2>&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.2.1\""; } >&5 + ($PKG_CONFIG --exists --print-errors "zlib >= 1.2.2.1") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib >= 1.2.0" 2>/dev/null` + pkg_cv_ZLIB_LIBS=`$PKG_CONFIG --libs "zlib >= 1.2.2.1" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -21743,9 +22572,9 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.0" 2>&1` + ZLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.2.1" 2>&1` else - ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.0" 2>&1` + ZLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.2.1" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$ZLIB_PKG_ERRORS" >&5 @@ -22933,6 +23762,22 @@ printf "%s\n" "yes" >&6; } have_libzstd=yes fi +if test "x$have_libzstd" = xyes +then : + + REMOTE_DEBUGGING_CFLAGS="-DHAVE_ZSTD $LIBZSTD_CFLAGS" + REMOTE_DEBUGGING_LIBS="$LIBZSTD_LIBS" + +else case e in #( + e) + REMOTE_DEBUGGING_CFLAGS="" + REMOTE_DEBUGGING_LIBS="" + ;; +esac +fi + + + @@ -23857,7 +24702,30 @@ printf "%s\n" "#define HAVE_UT_NAMESIZE 1" >>confdefs.h fi +# musl libc redefines struct prctl_mm_map and conflicts with linux/prctl.h +if test "$ac_cv_libc" != musl +then : + +ac_fn_check_decl "$LINENO" "PR_SET_VMA_ANON_NAME" "ac_cv_have_decl_PR_SET_VMA_ANON_NAME" "#include <linux/prctl.h> + #include <sys/prctl.h> +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl_PR_SET_VMA_ANON_NAME" = xyes +then : + ac_have_decl=1 +else case e in #( + e) ac_have_decl=0 ;; +esac +fi +printf "%s\n" "#define HAVE_DECL_PR_SET_VMA_ANON_NAME $ac_have_decl" >>confdefs.h +if test $ac_have_decl = 1 +then : + +printf "%s\n" "#define _Py_HAVE_PR_SET_VMA_ANON_NAME 1" >>confdefs.h +fi + + +fi # check for openpty, login_tty, and forkpty @@ -25084,6 +25952,72 @@ printf "%s\n" "#define HAVE_SIGINFO_T_SI_BAND 1" >>confdefs.h fi +if test "$ac_cv_func_statx" = yes; then + # Some systems have the definitions of the mask bits without having the + # corresponding members in struct statx. Check for members added after Linux + # 4.11 (when statx itself was added). + ac_fn_c_check_member "$LINENO" "struct statx" "stx_mnt_id" "ac_cv_member_struct_statx_stx_mnt_id" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_mnt_id" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_MNT_ID 1" >>confdefs.h + + +fi + + ac_fn_c_check_member "$LINENO" "struct statx" "stx_dio_mem_align" "ac_cv_member_struct_statx_stx_dio_mem_align" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_dio_mem_align" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN 1" >>confdefs.h + + +fi + + # stx_dio_offset_align was added together with stx_dio_mem_align + ac_fn_c_check_member "$LINENO" "struct statx" "stx_subvol" "ac_cv_member_struct_statx_stx_subvol" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_subvol" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_SUBVOL 1" >>confdefs.h + + +fi + + ac_fn_c_check_member "$LINENO" "struct statx" "stx_atomic_write_unit_min" "ac_cv_member_struct_statx_stx_atomic_write_unit_min" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_atomic_write_unit_min" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN 1" >>confdefs.h + + +fi + + # stx_atomic_write_unit_max and stx_atomic_write_segments_max were added + # together with stx_atomic_write_unit_min + ac_fn_c_check_member "$LINENO" "struct statx" "stx_dio_read_offset_align" "ac_cv_member_struct_statx_stx_dio_read_offset_align" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_dio_read_offset_align" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN 1" >>confdefs.h + + +fi + + # stx_atomic_write_unit_max_opt was added in Linux 6.16, but is controlled by + # the STATX_WRITE_ATOMIC mask bit added in Linux 6.11, so having the mask bit + # doesn't imply having the member. + ac_fn_c_check_member "$LINENO" "struct statx" "stx_atomic_write_unit_max_opt" "ac_cv_member_struct_statx_stx_atomic_write_unit_max_opt" "$ac_includes_default" +if test "x$ac_cv_member_struct_statx_stx_atomic_write_unit_max_opt" = xyes +then : + +printf "%s\n" "#define HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT 1" >>confdefs.h + + +fi + +fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for time.h that defines altzone" >&5 printf %s "checking for time.h that defines altzone... " >&6; } if test ${ac_cv_header_time_altzone+y} @@ -25877,20 +26811,7 @@ printf "%s\n" "#define DOUBLE_IS_BIG_ENDIAN_IEEE754 1" >>confdefs.h printf "%s\n" "#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1" >>confdefs.h ;; *) - case $host_cpu in #( - *arm*) : - # Some ARM platforms use a mixed-endian representation for - # doubles. While Python doesn't currently have full support - # for these platforms (see e.g., issue 1762561), we can at - # least make sure that float <-> string conversions work. - # FLOAT_WORDS_BIGENDIAN doesn't actually detect this case, - # but if it's not big or little, then it must be this? - -printf "%s\n" "#define DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754 1" >>confdefs.h - ;; #( - *) : as_fn_error $? "Unknown float word ordering. You need to manually preset ax_cv_c_float_words_bigendian=no (or yes) according to your system." "$LINENO" 5 ;; -esac ;; esac @@ -25965,8 +26886,8 @@ main (void) { unsigned int fpcr; - __asm__ __volatile__ ("fmove.l %%fpcr,%0" : "=g" (fpcr)); - __asm__ __volatile__ ("fmove.l %0,%%fpcr" : : "g" (fpcr)); + __asm__ __volatile__ ("fmove.l %%fpcr,%0" : "=dm" (fpcr)); + __asm__ __volatile__ ("fmove.l %0,%%fpcr" : : "dm" (fpcr)); ; return 0; @@ -26090,6 +27011,27 @@ esac fi done + +ac_func= +for ac_item in $ac_func_c_list +do + if test $ac_func; then + ac_fn_c_check_func "$LINENO" $ac_func ac_cv_func_$ac_func + if eval test \"x\$ac_cv_func_$ac_func\" = xyes; then + echo "#define $ac_item 1" >> confdefs.h + fi + ac_func= + else + ac_func=$ac_item + fi +done + + + + + + + LIBS=$LIBS_SAVE { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether POSIX semaphores are enabled" >&5 @@ -26768,15 +27710,10 @@ if test "$ac_sys_system" = "iOS"; then MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi - - -BINLIBDEST='$(LIBDIR)/python$(VERSION)$(ABI_THREAD)' - - # Check for --with-platlibdir # /usr/$PLATLIBDIR/python$(VERSION)$(ABI_THREAD) -PLATLIBDIR="lib" +PLATLIBDIR="lib" # XXX: We should probably calculate the defauly from libdir, if defined. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-platlibdir" >&5 printf %s "checking for --with-platlibdir... " >&6; } @@ -26793,7 +27730,6 @@ then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } PLATLIBDIR="$withval" - BINLIBDEST='${exec_prefix}/${PLATLIBDIR}/python$(VERSION)$(ABI_THREAD)' else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } @@ -26807,10 +27743,14 @@ fi +LIBDEST='${prefix}/${PLATLIBDIR}/python$(VERSION)$(ABI_THREAD)' +BINLIBDEST='${exec_prefix}/${PLATLIBDIR}/python$(VERSION)$(ABI_THREAD)' + + if test x$PLATFORM_TRIPLET = x; then - LIBPL='$(prefix)'"/${PLATLIBDIR}/python${VERSION}${ABI_THREAD}/config-${LDVERSION}" + LIBPL='$(LIBDEST)'"/config-${LDVERSION}" else - LIBPL='$(prefix)'"/${PLATLIBDIR}/python${VERSION}${ABI_THREAD}/config-${LDVERSION}-${PLATFORM_TRIPLET}" + LIBPL='$(LIBDEST)'"/config-${LDVERSION}-${PLATFORM_TRIPLET}" fi @@ -28172,110 +29112,6 @@ printf "%s\n" "#define HAVE_STAT_TV_NSEC2 1" >>confdefs.h fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether year with century should be normalized for strftime" >&5 -printf %s "checking whether year with century should be normalized for strftime... " >&6; } -if test ${ac_cv_normalize_century+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) -if test "$cross_compiling" = yes -then : - ac_cv_normalize_century=yes -else case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include <time.h> -#include <string.h> - -int main(void) -{ - char year[5]; - struct tm date = { - .tm_year = -1801, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { - return 1; - } - return 0; -} - -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - ac_cv_normalize_century=yes -else case e in #( - e) ac_cv_normalize_century=no ;; -esac -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext ;; -esac -fi - ;; -esac -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_normalize_century" >&5 -printf "%s\n" "$ac_cv_normalize_century" >&6; } -if test "$ac_cv_normalize_century" = yes -then - -printf "%s\n" "#define _Py_NORMALIZE_CENTURY 1" >>confdefs.h - -fi - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C99-compatible strftime specifiers are supported" >&5 -printf %s "checking whether C99-compatible strftime specifiers are supported... " >&6; } -if test ${ac_cv_strftime_c99_support+y} -then : - printf %s "(cached) " >&6 -else case e in #( - e) -if test "$cross_compiling" = yes -then : - ac_cv_strftime_c99_support= -else case e in #( - e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include <time.h> -#include <string.h> - -int main(void) -{ - char full_date[11]; - struct tm date = { - .tm_year = 0, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) { - return 0; - } - return 1; -} - -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - ac_cv_strftime_c99_support=yes -else case e in #( - e) as_fn_error $? "Python requires C99-compatible strftime specifiers" "$LINENO" 5 ;; -esac -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext ;; -esac -fi - ;; -esac -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_strftime_c99_support" >&5 -printf "%s\n" "$ac_cv_strftime_c99_support" >&6; } - have_curses=no have_panel=no @@ -29880,7 +30716,7 @@ then : if test "$withval" = yes then -printf "%s\n" "#define Py_TAIL_CALL_INTERP 1" >>confdefs.h +printf "%s\n" "#define _Py_TAIL_CALL_INTERP 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } @@ -29888,7 +30724,7 @@ fi if test "$withval" = no then -printf "%s\n" "#define Py_TAIL_CALL_INTERP 0" >>confdefs.h +printf "%s\n" "#define _Py_TAIL_CALL_INTERP 0" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } @@ -29947,10 +30783,10 @@ SRCDIRS="\ Modules \ Modules/_ctypes \ Modules/_decimal \ - Modules/_decimal/libmpdec \ Modules/_hacl \ Modules/_io \ Modules/_multiprocessing \ + Modules/_remote_debugging \ Modules/_sqlite \ Modules/_sre \ Modules/_testcapi \ @@ -30068,6 +30904,52 @@ printf "%s\n" "#define HAVE_GLIBC_MEMMOVE_BUG 1" >>confdefs.h fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we need to manually block large inlining in ceval.c" >&5 +printf %s "checking if we need to manually block large inlining in ceval.c... " >&6; } +if test "$cross_compiling" = yes +then : + block_huge_inlining_in_ceval=undefined +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int main(void) { +// See gh-148284: Clang 22 seems to have interactions with inlining +// and the stackref buffer which cause 40 kB of stack usage on x86-64 +// in buggy versions of _PyEval_EvalFrameDefault() in computed goto +// interpreter. The normal usage seen is normally 1-2 kB. +#if defined(__clang__) && (__clang_major__ == 22) + return 1; +#else + return 0; +#endif +} + +_ACEOF +if ac_fn_c_try_run "$LINENO" +then : + block_huge_inlining_in_ceval=no +else case e in #( + e) block_huge_inlining_in_ceval=yes ;; +esac +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $block_huge_inlining_in_ceval" >&5 +printf "%s\n" "$block_huge_inlining_in_ceval" >&6; } + +if test "$block_huge_inlining_in_ceval" = yes && test "$ac_cv_computed_gotos" = yes; then + # gh-148284: Suppress inlining of functions whose stack size exceeds + # 512 bytes. This number should be tuned to follow the C stack + # consumption in _PyEval_EvalFrameDefault() on computed goto + # interpreter. + CFLAGS_CEVAL="$CFLAGS_CEVAL -finline-max-stacksize=512" +fi + + if test "$ac_cv_gcc_asm_for_x87" = yes; then # Some versions of gcc miscompile inline asm: # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491 @@ -30999,6 +31881,50 @@ fi printf "%s\n" "$TEST_MODULES" >&6; } +# Check for --with-build-details-suffix +BUILD_DETAILS=build-details.json + +# Check whether --with-build-details-suffix was given. +if test ${with_build_details_suffix+y} +then : + withval=$with_build_details_suffix; + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-build-details-suffix" >&5 +printf %s "checking for --with-build-details-suffix... " >&6; } + if test "x$with_build_details_suffix" = xno +then : + as_fn_error $? "invalid --with-build-details-suffix option: expected custom suffix or \"yes\", not \"no\"" "$LINENO" 5 + +fi + if test "x$with_build_details_suffix" = xyes +then : + + colocated_install=yes + threading_suffix="" + if [ "$ABI_THREAD" = "t" ]; then + threading_suffix=-free-threading + fi + debug_suffix="" + if [ "$Py_DEBUG" = "true" ]; then + debug_suffix=-debug + fi + BUILD_DETAILS=build-details.$MULTIARCH$threading_suffix$debug_suffix.json + +else case e in #( + e) + BUILD_DETAILS=build-details.$with_build_details_suffix.json + + ;; +esac +fi + + +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_build_details_suffix" >&5 +printf "%s\n" "$with_build_details_suffix" >&6; } +BUILD_DETAILS=$BUILD_DETAILS + + # gh-109054: Check if -latomic is needed to get <pyatomic.h> atomic functions. # On Linux aarch64, GCC may require programs and libraries to be linked # explicitly to libatomic. Call _Py_atomic_or_uint64() which may require @@ -31093,6 +32019,7 @@ case "$ac_sys_system" in iOS) _PYTHREAD_NAME_MAXLEN=63;; FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 + CYGWIN*) _PYTHREAD_NAME_MAXLEN=16;; *) _PYTHREAD_NAME_MAXLEN=;; esac if test -n "$_PYTHREAD_NAME_MAXLEN"; then @@ -31220,6 +32147,7 @@ case $ac_sys_system in #( py_cv_module_termios=n/a py_cv_module_xxlimited=n/a py_cv_module_xxlimited_35=n/a + py_cv_module_xxlimited_3_13=n/a py_cv_module_=n/a ;; #( @@ -31313,6 +32241,28 @@ then : +fi + + + if test "$py_cv_module__math_integer" != "n/a" +then : + py_cv_module__math_integer=yes +fi + if test "$py_cv_module__math_integer" = yes; then + MODULE__MATH_INTEGER_TRUE= + MODULE__MATH_INTEGER_FALSE='#' +else + MODULE__MATH_INTEGER_TRUE='#' + MODULE__MATH_INTEGER_FALSE= +fi + + as_fn_append MODULE_BLOCK "MODULE__MATH_INTEGER_STATE=$py_cv_module__math_integer$as_nl" + if test "x$py_cv_module__math_integer" = xyes +then : + + + + fi @@ -31552,8 +32502,8 @@ fi if test "x$py_cv_module__remote_debugging" = xyes then : - - + as_fn_append MODULE_BLOCK "MODULE__REMOTE_DEBUGGING_CFLAGS=$REMOTE_DEBUGGING_CFLAGS$as_nl" + as_fn_append MODULE_BLOCK "MODULE__REMOTE_DEBUGGING_LDFLAGS=$REMOTE_DEBUGGING_LIBS$as_nl" fi @@ -32730,7 +33680,7 @@ fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for HACL* library linking type" >&5 printf %s "checking for HACL* library linking type... " >&6; } -if test "$ac_sys_system" = "WASI"; then +if test "$ac_sys_system" = "WASI" || test "$MODULE_BUILDTYPE" = "static"; then LIBHACL_LDEPS_LIBTYPE=STATIC { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: static" >&5 printf "%s\n" "static" >&6; } @@ -33181,12 +34131,7 @@ fi printf "%s\n" "$py_cv_module__decimal" >&6; } -if test "x$with_system_libmpdec" = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: the bundled copy of libmpdec is scheduled for removal in Python 3.16; consider using a system installed mpdecimal library." >&5 -printf "%s\n" "$as_me: WARNING: the bundled copy of libmpdec is scheduled for removal in Python 3.16; consider using a system installed mpdecimal library." >&2;} -fi -if test "$with_system_libmpdec" = "yes" && test "$have_mpdec" = "no" +if test "$have_mpdec" = "no" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: no system libmpdec found; falling back to pure-Python version for the decimal module" >&5 printf "%s\n" "$as_me: WARNING: no system libmpdec found; falling back to pure-Python version for the decimal module" >&2;} @@ -33697,6 +34642,15 @@ fi printf "%s\n" "$py_cv_module__hashlib" >&6; } +case $ac_sys_system in #( + # On FreeBSD, _testcapi.get_process_memory_usage() calls kvm_openfiles() + # and so needs libkvm. + FreeBSD*) : + LIBKVM="-lkvm" + ;; #( + *) : + ;; +esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module _testcapi" >&5 printf %s "checking for stdlib extension module _testcapi... " >&6; } @@ -33723,7 +34677,7 @@ fi then : - as_fn_append MODULE_BLOCK "MODULE__TESTCAPI_LDFLAGS=$LIBATOMIC$as_nl" + as_fn_append MODULE_BLOCK "MODULE__TESTCAPI_LDFLAGS=$LIBATOMIC $LIBKVM$as_nl" fi if test "$py_cv_module__testcapi" = yes; then @@ -34259,6 +35213,98 @@ fi printf "%s\n" "$py_cv_module_xxlimited_35" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module xxlimited_3_13" >&5 +printf %s "checking for stdlib extension module xxlimited_3_13... " >&6; } + if test "$py_cv_module_xxlimited_3_13" != "n/a" +then : + + if test "$TEST_MODULES" = yes +then : + if test "$ac_cv_func_dlopen" = yes +then : + py_cv_module_xxlimited_3_13=yes +else case e in #( + e) py_cv_module_xxlimited_3_13=missing ;; +esac +fi +else case e in #( + e) py_cv_module_xxlimited_3_13=disabled ;; +esac +fi + +fi + as_fn_append MODULE_BLOCK "MODULE_XXLIMITED_3_13_STATE=$py_cv_module_xxlimited_3_13$as_nl" + if test "x$py_cv_module_xxlimited_3_13" = xyes +then : + + + + +fi + if test "$py_cv_module_xxlimited_3_13" = yes; then + MODULE_XXLIMITED_3_13_TRUE= + MODULE_XXLIMITED_3_13_FALSE='#' +else + MODULE_XXLIMITED_3_13_TRUE='#' + MODULE_XXLIMITED_3_13_FALSE= +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $py_cv_module_xxlimited_3_13" >&5 +printf "%s\n" "$py_cv_module_xxlimited_3_13" >&6; } + + +# Determine JIT stencils header files based on target platform +JIT_STENCILS_H="" +JIT_SHIM_O="" +JIT_SHIM_BUILD_O="" +if ${jit_flags:+false} : +then : + +else case e in #( + e) if test "${enable_universalsdk}" && test "$UNIVERSAL_ARCHS" = "universal2"; then + JIT_STENCILS_H="jit_stencils-aarch64-apple-darwin.h jit_stencils-x86_64-apple-darwin.h" + JIT_SHIM_O="jit_shim-universal2-apple-darwin.o" + JIT_SHIM_BUILD_O="jit_shim-aarch64-apple-darwin.o jit_shim-x86_64-apple-darwin.o" + else + case "$host" in + aarch64-apple-darwin*) + JIT_STENCILS_H="jit_stencils-aarch64-apple-darwin.h" + JIT_SHIM_O="jit_shim-aarch64-apple-darwin.o" + ;; + x86_64-apple-darwin*) + JIT_STENCILS_H="jit_stencils-x86_64-apple-darwin.h" + JIT_SHIM_O="jit_shim-x86_64-apple-darwin.o" + ;; + aarch64-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-aarch64-pc-windows-msvc.h" + JIT_SHIM_O="jit_shim-aarch64-pc-windows-msvc.o" + ;; + i686-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-i686-pc-windows-msvc.h" + JIT_SHIM_O="jit_shim-i686-pc-windows-msvc.o" + ;; + x86_64-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-x86_64-pc-windows-msvc.h" + JIT_SHIM_O="jit_shim-x86_64-pc-windows-msvc.o" + ;; + aarch64-*-linux-gnu) + JIT_STENCILS_H="jit_stencils-aarch64-unknown-linux-gnu.h" + JIT_SHIM_O="jit_shim-aarch64-unknown-linux-gnu.o" + ;; + x86_64-*-linux-gnu) + JIT_STENCILS_H="jit_stencils-x86_64-unknown-linux-gnu.h" + JIT_SHIM_O="jit_shim-x86_64-unknown-linux-gnu.o" + ;; + esac + JIT_SHIM_BUILD_O="$JIT_SHIM_O" + fi ;; +esac +fi + + + + + # substitute multiline block, must come after last PY_STDLIB_MOD() @@ -34392,6 +35438,10 @@ if test -z "${MODULE_ARRAY_TRUE}" && test -z "${MODULE_ARRAY_FALSE}"; then as_fn_error $? "conditional \"MODULE_ARRAY\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${MODULE__MATH_INTEGER_TRUE}" && test -z "${MODULE__MATH_INTEGER_FALSE}"; then + as_fn_error $? "conditional \"MODULE__MATH_INTEGER\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${MODULE__ASYNCIO_TRUE}" && test -z "${MODULE__ASYNCIO_FALSE}"; then as_fn_error $? "conditional \"MODULE__ASYNCIO\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -34716,6 +35766,10 @@ if test -z "${MODULE_XXLIMITED_35_TRUE}" && test -z "${MODULE_XXLIMITED_35_FALSE as_fn_error $? "conditional \"MODULE_XXLIMITED_35\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${MODULE_XXLIMITED_3_13_TRUE}" && test -z "${MODULE_XXLIMITED_3_13_FALSE}"; then + as_fn_error $? "conditional \"MODULE_XXLIMITED_3_13\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 @@ -35109,7 +36163,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by python $as_me 3.15, which was +This file was extended by python $as_me 3.16, which was generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -35173,7 +36227,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -python config.status 3.15 +python config.status 3.16 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" @@ -35301,7 +36355,7 @@ do "Mac/PythonLauncher/Makefile") CONFIG_FILES="$CONFIG_FILES Mac/PythonLauncher/Makefile" ;; "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; - "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; + "Platforms/Apple/iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES Platforms/Apple/iOS/Resources/Info.plist" ;; "Makefile.pre") CONFIG_FILES="$CONFIG_FILES Makefile.pre" ;; "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; "Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;; diff --git a/configure.ac b/configure.ac index 29e36f217402f1..d3320f0e45fe14 100644 --- a/configure.ac +++ b/configure.ac @@ -10,7 +10,7 @@ dnl to regenerate the configure script. dnl # Set VERSION so we only need to edit in one place (i.e., here) -m4_define([PYTHON_VERSION], [3.15]) +m4_define([PYTHON_VERSION], [3.16]) AC_PREREQ([2.72]) @@ -99,7 +99,7 @@ AC_SUBST([BASECPPFLAGS]) if test "$srcdir" != . -a "$srcdir" != "$(pwd)"; then # If we're building out-of-tree, we need to make sure the following # resources get picked up before their $srcdir counterparts. - # Objects/ -> typeslots.inc + # Objects/ -> slots_generated.c # Include/ -> Python.h # (A side effect of this is that these resources will automatically be # regenerated when building out-of-tree, regardless of whether or not @@ -205,7 +205,7 @@ AC_SUBST([FREEZE_MODULE_DEPS]) AC_SUBST([PYTHON_FOR_BUILD_DEPS]) AC_CHECK_PROGS([PYTHON_FOR_REGEN], - [python$PACKAGE_VERSION python3.13 python3.12 python3.11 python3.10 python3 python], + [python$PACKAGE_VERSION python3.16 python3.15 python3.14 python3.13 python3.12 python3.11 python3.10 python3 python], [python3]) AC_SUBST([PYTHON_FOR_REGEN]) @@ -307,6 +307,15 @@ if test "$with_pkg_config" = yes -a -z "$PKG_CONFIG"; then AC_MSG_ERROR([pkg-config is required])] fi +dnl Allow distributors to provide custom missing stdlib module error messages +AC_ARG_WITH([missing-stdlib-config], + [AS_HELP_STRING([--with-missing-stdlib-config=FILE], + [File with custom module error messages for missing stdlib modules])], + [MISSING_STDLIB_CONFIG="$withval"], + [MISSING_STDLIB_CONFIG=""] +) +AC_SUBST([MISSING_STDLIB_CONFIG]) + # Set name for machine-dependent library files AC_ARG_VAR([MACHDEP], [name for machine-dependent library files]) AC_MSG_CHECKING([MACHDEP]) @@ -325,7 +334,7 @@ then ac_sys_system=Linux ;; *-*-cygwin*) - ac_sys_system=Cygwin + ac_sys_system=CYGWIN ;; *-apple-ios*) ac_sys_system=iOS @@ -333,6 +342,9 @@ then *-*-darwin*) ac_sys_system=Darwin ;; + *-gnu) + ac_sys_system=GNU + ;; *-*-vxworks*) ac_sys_system=VxWorks ;; @@ -559,7 +571,7 @@ AC_ARG_ENABLE([framework], yes) case $ac_sys_system in Darwin) enableval=/Library/Frameworks ;; - iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; + iOS) enableval=Platforms/Apple/iOS/Frameworks/\$\(MULTIARCH\) ;; *) AC_MSG_ERROR([Unknown platform for framework build]) esac esac @@ -666,9 +678,9 @@ AC_ARG_ENABLE([framework], prefix=$PYTHONFRAMEWORKPREFIX PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" - RESSRCDIR=iOS/Resources + RESSRCDIR=Platforms/Apple/iOS/Resources - AC_CONFIG_FILES([iOS/Resources/Info.plist]) + AC_CONFIG_FILES([Platforms/Apple/iOS/Resources/Info.plist]) ;; *) AC_MSG_ERROR([Unknown platform for framework build]) @@ -771,6 +783,9 @@ if test "$cross_compiling" = yes; then _host_ident=$host_cpu esac ;; + *-gnu) + _host_ident=$host_cpu + ;; *-*-cygwin*) _host_ident= ;; @@ -907,8 +922,8 @@ esac if test $define_xopen_source = yes then - # X/Open 7, incorporating POSIX.1-2008 - AC_DEFINE([_XOPEN_SOURCE], [700], + # X/Open 8, incorporating POSIX.1-2024 + AC_DEFINE([_XOPEN_SOURCE], [800], [Define to the level of X/Open that your system supports]) # On Tru64 Unix 4.0F, defining _XOPEN_SOURCE also requires @@ -918,8 +933,8 @@ then AC_DEFINE([_XOPEN_SOURCE_EXTENDED], [1], [Define to activate Unix95-and-earlier features]) - AC_DEFINE([_POSIX_C_SOURCE], [200809L], - [Define to activate features from IEEE Stds 1003.1-2008]) + AC_DEFINE([_POSIX_C_SOURCE], [202405L], + [Define to activate features from IEEE Std 1003.1-2024]) fi # On HP-UX mbstate_t requires _INCLUDE__STDC_A1_SOURCE @@ -1122,10 +1137,10 @@ preset_cxx="$CXX" if test -z "$CXX" then case "$ac_cv_cc_name" in - gcc) AC_PATH_TOOL([CXX], [g++], [g++], [notfound]) ;; - cc) AC_PATH_TOOL([CXX], [c++], [c++], [notfound]) ;; - clang) AC_PATH_TOOL([CXX], [clang++], [clang++], [notfound]) ;; - icc) AC_PATH_TOOL([CXX], [icpc], [icpc], [notfound]) ;; + gcc) AC_PATH_TOOL([CXX], [g++], [notfound]) ;; + cc) AC_PATH_TOOL([CXX], [c++], [notfound]) ;; + clang) AC_PATH_TOOL([CXX], [clang++], [notfound]) ;; + icc) AC_PATH_TOOL([CXX], [icpc], [notfound]) ;; esac if test "$CXX" = "notfound" then @@ -1202,6 +1217,14 @@ if test x$MULTIARCH != x; then fi AC_SUBST([MULTIARCH_CPPFLAGS]) +# Guess C stack direction +AS_CASE([$host], + [hppa*], [_Py_STACK_GROWS_DOWN=0], + [_Py_STACK_GROWS_DOWN=1]) +AC_DEFINE_UNQUOTED([_Py_STACK_GROWS_DOWN], [$_Py_STACK_GROWS_DOWN], + [Define to 1 if the machine stack grows down (default); 0 if it grows up.]) +AC_SUBST([_Py_STACK_GROWS_DOWN]) + dnl Support tiers according to https://peps.python.org/pep-0011/ dnl dnl NOTE: Windows support tiers are defined in PC/pyconfig.h. @@ -1306,11 +1329,11 @@ dnl See https://emscripten.org/docs/compiling/Dynamic-Linking.html AC_MSG_CHECKING([for --enable-wasm-dynamic-linking]) AC_ARG_ENABLE([wasm-dynamic-linking], [AS_HELP_STRING([--enable-wasm-dynamic-linking], - [Enable dynamic linking support for WebAssembly (default is no)])], + [Enable dynamic linking support for WebAssembly (default is no); WASI requires an external dynamic loader to handle imports])], [ AS_CASE([$ac_sys_system], [Emscripten], [], - [WASI], [AC_MSG_ERROR([WASI dynamic linking is not implemented yet.])], + [WASI], [], [AC_MSG_ERROR([--enable-wasm-dynamic-linking only applies to Emscripten and WASI])] ) ], [ @@ -1503,6 +1526,17 @@ fi], [AC_MSG_RESULT([yes])]) AC_SUBST([STATIC_LIBPYTHON]) +AC_MSG_CHECKING([for --enable-static-libpython-for-interpreter]) +AC_ARG_ENABLE([static-libpython-for-interpreter], + AS_HELP_STRING([--enable-static-libpython-for-interpreter], + [even with --enable-shared, statically link libpython into the interpreter (default is to use the shared library)])) + +if test -z "$enable_static_libpython_for_interpreter" +then + enable_static_libpython_for_interpreter="no" +fi +AC_MSG_RESULT([$enable_static_libpython_for_interpreter]) + AC_MSG_CHECKING([for --enable-profiling]) AC_ARG_ENABLE([profiling], AS_HELP_STRING([--enable-profiling], [enable C-level code profiling with gprof (default is no)])) @@ -1557,7 +1591,7 @@ if test $enable_shared = "yes"; then CYGWIN*) LDLIBRARY='libpython$(LDVERSION).dll.a' BLDLIBRARY='-L. -lpython$(LDVERSION)' - DLLLIBRARY='libpython$(LDVERSION).dll' + DLLLIBRARY='cygpython$(LDVERSION).dll' ;; SunOS*) LDLIBRARY='libpython$(LDVERSION).so' @@ -1616,16 +1650,12 @@ else # shared is disabled case $ac_sys_system in CYGWIN*) BLDLIBRARY='$(LIBRARY)' - LDLIBRARY='libpython$(LDVERSION).dll.a' + LDLIBRARY='libpython$(LDVERSION).a' ;; esac fi AC_MSG_RESULT([$LDLIBRARY]) -if test "$cross_compiling" = yes; then - RUNSHARED= -fi - # HOSTRUNNER - Program to run CPython for the host platform AC_MSG_CHECKING([HOSTRUNNER]) if test -z "$HOSTRUNNER" @@ -1636,10 +1666,9 @@ then HOSTRUNNER="$NODE" AS_VAR_IF([host_cpu], [wasm64], [AS_VAR_APPEND([HOSTRUNNER], [" --experimental-wasm-memory64"])]) ], - dnl TODO: support other WASI runtimes - dnl wasmtime starts the process with "/" as CWD. For OOT builds add the - dnl directory containing _sysconfigdata to PYTHONPATH. - [WASI], [HOSTRUNNER='wasmtime run --wasm max-wasm-stack=16777216 --wasi preview2=n --env PYTHONPATH=/$(shell realpath --relative-to $(abs_srcdir) $(abs_builddir))/$(shell cat pybuilddir.txt):/Lib --dir $(srcdir)::/'], + [WASI], [ + AC_MSG_ERROR([HOSTRUNNER must be set when cross-compiling to WASI]) + ], [HOSTRUNNER=''] ) fi @@ -1661,7 +1690,11 @@ if test "$PY_ENABLE_SHARED" = 1 || test "$enable_framework" ; then LIBRARY_DEPS="\$(LIBRARY) $LIBRARY_DEPS" fi # Link Python program to the shared library - LINK_PYTHON_OBJS='$(BLDLIBRARY)' + if test "$enable_static_libpython_for_interpreter" = "yes"; then + LINK_PYTHON_OBJS='$(LIBRARY_OBJS)' + else + LINK_PYTHON_OBJS='$(BLDLIBRARY)' + fi else if test "$STATIC_LIBPYTHON" = 0; then # Build Python needs object files but don't need to build @@ -1823,6 +1856,14 @@ fi], [AC_MSG_RESULT([no])]) if test "$Py_OPT" = 'true' ; then + # Check for conflicting CFLAGS=-O0 and --enable-optimizations + case "$CFLAGS" in + *-O0*) + AC_MSG_WARN([m4_normalize([ + CFLAGS contains -O0 which may conflict with --enable-optimizations. + Consider removing -O0 from CFLAGS for optimal performance.])]) + ;; + esac # Intentionally not forcing Py_LTO='true' here. Too many toolchains do not # compile working code using it and both test_distutils and test_gdb are # broken when you do manage to get a toolchain that works with it. People @@ -2064,7 +2105,32 @@ case "$ac_cv_cc_name" in fi ;; gcc) + # Check for 32-bit x86 ISA + AC_CACHE_CHECK([for i686], [ac_cv_i686], [ + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([ + #ifdef __i386__ + # error "i386" + #endif + ], []) + ],[ac_cv_i686=no],[ac_cv_i686=yes]) + ]) + PGO_PROF_GEN_FLAG="-fprofile-generate" + + # Use -fprofile-update=atomic to fix a random GCC internal error on PGO + # build (gh-145801) caused by corruption of profile data (.gcda files). + # + # gh-148535: On i686, using -fprofile-update=atomic makes the PGO build + # way slower (up to 47x slower). So far, the GCC internal error on PGO + # build was not seen on i686, so don't use this flag on i686. + AS_VAR_IF([ac_cv_i686], [no], [ + AX_CHECK_COMPILE_FLAG( + [-fprofile-update=atomic], + [PGO_PROF_GEN_FLAG="$PGO_PROF_GEN_FLAG -fprofile-update=atomic"], + []) + ]) + PGO_PROF_USE_FLAG="-fprofile-use -fprofile-correction" LLVM_PROF_MERGER="true" LLVM_PROF_FILE="" @@ -2132,11 +2198,14 @@ if test "$Py_BOLT" = 'true' ; then fi fi -dnl Enable BOLT of libpython if built. +dnl Enable BOLT of libpython if built and used by the python3 binary. +dnl (If it is built but not used, we cannot profile it.) AC_SUBST([BOLT_BINARIES]) BOLT_BINARIES='$(BUILDPYTHON)' AS_VAR_IF([enable_shared], [yes], [ - BOLT_BINARIES="${BOLT_BINARIES} \$(INSTSONAME)" + AS_VAR_IF([enable_static_libpython_for_interpreter], [no], [ + BOLT_BINARIES="${BOLT_BINARIES} \$(INSTSONAME)" + ]) ]) AC_ARG_VAR( @@ -2155,7 +2224,8 @@ then dnl At least LLVM 19.x doesn't support computed gotos in PIC compiled code. dnl Exclude functions containing computed gotos. dnl TODO this may be fixed in LLVM 20.x via https://github.com/llvm/llvm-project/pull/120267. - [-skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1] + dnl GCC's LTO creates .lto_priv.0 clones of these functions. + [-skip-funcs=_PyEval_EvalFrameDefault,sre_ucs1_match/1,sre_ucs2_match/1,sre_ucs4_match/1,sre_ucs1_match.lto_priv.0/1,sre_ucs2_match.lto_priv.0/1,sre_ucs4_match.lto_priv.0/1] ")] ) fi @@ -2185,7 +2255,7 @@ then -reorder-blocks=ext-tsp -reorder-functions=cdsort -split-functions - -icf=1 + -icf=0 -inline-all -split-eh -reorder-functions-use-hot-size @@ -2335,8 +2405,8 @@ AS_CASE([$ac_sys_system], dnl Include file system support AS_VAR_APPEND([LINKFORSHARED], [" -sFORCE_FILESYSTEM -lidbfs.js -lnodefs.js -lproxyfs.js -lworkerfs.js"]) - AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32,TTY"]) - AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,__PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET,_PyGILState_GetThisThreadState,__Py_DumpTraceback"]) + AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32,TTY,ERRNO_CODES"]) + AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,_PyGILState_GetThisThreadState,__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET"]) AS_VAR_APPEND([LINKFORSHARED], [" -sSTACK_SIZE=5MB"]) dnl Avoid bugs in JS fallback string decoding path AS_VAR_APPEND([LINKFORSHARED], [" -sTEXTDECODER=2"]) @@ -2392,6 +2462,54 @@ AS_CASE([$ac_sys_system], ] ) +dnl On Linux, check the thread stack size. musl (ex: Alpine Linux) uses +dnl a default thread stack size of 128 kB, whereas the glibc uses 8 MiB. +dnl Python needs at least 1 MiB. +if test "$ac_sys_system" = "Linux" -a "$cross_compiling" = no; then + AC_CACHE_CHECK([for thread stack size], [ac_cv_thread_stack_size], [ + cat > conftest.c <<EOF +#include <pthread.h> + +int main() +{ + pthread_attr_t attrs; + size_t size; + + int rc = pthread_attr_init(&attrs); + if (rc != 0) { + return 2; + } + + rc = pthread_attr_getstacksize(&attrs, &size); + if (rc != 0) { + return 2; + } + + if (size < 1024 * 1024) { + return 1; + } + return 0; +} +EOF + + ac_cv_thread_stack_size=unknown + if $CC -pthread $CFLAGS conftest.c -o conftest &>/dev/null; then + ./conftest &>/dev/null + exitcode=$? + if test $exitcode -eq 1; then + ac_cv_thread_stack_size=1048576 + elif test $exitcode -eq 0; then + ac_cv_thread_stack_size="default" + fi + fi + rm -f conftest.c conftest + ]) + + if test "$ac_cv_thread_stack_size" != "default" -a "$ac_cv_thread_stack_size" != "unknown"; then + LDFLAGS="$LDFLAGS -Wl,-z,stack-size=$ac_cv_thread_stack_size" + fi +fi + AS_CASE([$enable_wasm_dynamic_linking], [yes], [ac_cv_func_dlopen=yes], [no], [ac_cv_func_dlopen=no], @@ -2401,6 +2519,7 @@ AS_CASE([$enable_wasm_dynamic_linking], AC_SUBST([BASECFLAGS]) AC_SUBST([CFLAGS_NODIST]) AC_SUBST([LDFLAGS_NODIST]) +AC_SUBST([EXE_LDFLAGS]) AC_SUBST([LDFLAGS_NOLTO]) AC_SUBST([WASM_ASSETS_DIR]) AC_SUBST([WASM_STDLIB]) @@ -2458,7 +2577,51 @@ then AX_CHECK_COMPILE_FLAG([-D_FORTIFY_SOURCE=3], [CFLAGS_NODIST="$CFLAGS_NODIST -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3"], [AC_MSG_WARN([-D_FORTIFY_SOURCE=3 not supported])], [-Werror]) fi +AC_MSG_CHECKING([whether to build with frame pointers]) +AC_ARG_WITH([frame-pointers], + [AS_HELP_STRING([--without-frame-pointers], + [build without frame pointers (default is no)])], + [], + [with_frame_pointers=yes]) +AC_MSG_RESULT([$with_frame_pointers]) + AS_VAR_IF([ac_cv_gcc_compat], [yes], [ + dnl Keep frame pointers in CPython, stdlib objects, and third-party + dnl extensions built against this Python (BASECFLAGS propagates via + dnl sysconfig) so native profilers can unwind interpreter frames and + dnl generated trampolines without DWARF. + frame_pointer_cflags= + AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer], [ + frame_pointer_cflags="-fno-omit-frame-pointer" + AX_CHECK_COMPILE_FLAG([-mno-omit-leaf-frame-pointer], [ + frame_pointer_cflags="$frame_pointer_cflags -mno-omit-leaf-frame-pointer" + ], [], [-Werror]) + AS_CASE([$host_cpu], [arm|armv*], [ + dnl GCC uses "-marm"; clang uses "-mno-thumb" + AX_CHECK_COMPILE_FLAG([-marm], [ + frame_pointer_cflags="$frame_pointer_cflags -marm" + ], [], [-Werror]) + AX_CHECK_COMPILE_FLAG([-mno-thumb], [ + frame_pointer_cflags="$frame_pointer_cflags -mno-thumb" + ], [], [-Werror]) + ]) + AS_CASE([$host_cpu], [powerpc64le], [ + frame_pointer_cflags="" + ]) + AS_CASE([$host_cpu], [s390*], [ + AX_CHECK_COMPILE_FLAG([-mbackchain], [ + dnl Do not use no-omit-frame-pointer; see gh-149362 + frame_pointer_cflags="-mbackchain" + ], [], [-Werror]) + ]) + ], [], [-Werror]) + if test -n "$frame_pointer_cflags" && test "x$with_frame_pointers" != xno; then + BASECFLAGS="$frame_pointer_cflags $BASECFLAGS" + AC_DEFINE([_Py_WITH_FRAME_POINTERS], [1], + [Define to 1 if frame unwinding via pointers is expected + to work, 0 if not. Leave undefined if unknown.]) + fi + CFLAGS_NODIST="$CFLAGS_NODIST -std=c11" PY_CHECK_CC_WARNING([enable], [extra], [if we can add -Wextra]) @@ -2779,20 +2942,18 @@ AS_VAR_IF([jit_flags], [], [AS_VAR_APPEND([CFLAGS_NODIST], [" $jit_flags"]) AS_VAR_SET([REGEN_JIT_COMMAND], - ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\""]) - AS_VAR_SET([JIT_STENCILS_H], ["jit_stencils.h"]) + ["\$(PYTHON_FOR_REGEN) \$(srcdir)/Tools/jit/build.py ${ARCH_TRIPLES:-$host} --output-dir . --pyconfig-dir . --cflags=\"$CFLAGS_JIT\" --llvm-version=\"$LLVM_VERSION\" --llvm-tools-install-dir=\"$LLVM_TOOLS_INSTALL_DIR\""]) AS_VAR_IF([Py_DEBUG], [true], [AS_VAR_APPEND([REGEN_JIT_COMMAND], [" --debug"])], [])]) AC_SUBST([REGEN_JIT_COMMAND]) -AC_SUBST([JIT_STENCILS_H]) AC_MSG_RESULT([$tier2_flags $jit_flags]) if test "$disable_gil" = "yes" -a "$enable_experimental_jit" != "no"; then # GH-133171: This configuration builds the JIT but never actually uses it, # which is surprising (and strictly worse than not building it at all): - AC_MSG_ERROR([--enable-experimental-jit cannot be used with --disable-gil.]) + AC_MSG_WARN([--enable-experimental-jit does not work correctly with --disable-gil.]) fi case "$ac_cv_cc_name" in @@ -2991,7 +3152,7 @@ AC_CHECK_HEADERS([ \ io.h langinfo.h libintl.h libutil.h linux/auxvec.h sys/auxv.h linux/fs.h linux/limits.h linux/memfd.h \ linux/netfilter_ipv4.h linux/random.h linux/soundcard.h linux/sched.h \ linux/tipc.h linux/wait.h netdb.h net/ethernet.h netinet/in.h netpacket/packet.h poll.h process.h pthread.h pty.h \ - sched.h setjmp.h shadow.h signal.h spawn.h stropts.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \ + sched.h setjmp.h shadow.h signal.h spawn.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \ sys/endian.h sys/epoll.h sys/event.h sys/eventfd.h sys/file.h sys/ioctl.h sys/kern_control.h \ sys/loadavg.h sys/lock.h sys/memfd.h sys/mkdev.h sys/mman.h sys/modem.h sys/param.h sys/pidfd.h sys/poll.h \ sys/random.h sys/resource.h sys/select.h sys/sendfile.h sys/socket.h sys/soundcard.h sys/stat.h \ @@ -3002,6 +3163,16 @@ AC_CHECK_HEADERS([ \ AC_HEADER_DIRENT AC_HEADER_MAJOR +# On Linux, stropts.h may be empty +AC_CHECK_DECL([I_PUSH], [ + AC_DEFINE([HAVE_STROPTS_H], [1], + [Define to 1 if you have the <stropts.h> header file.])], [], [ + #ifdef HAVE_SYS_TYPES_H + # include <sys/types.h> + #endif + #include <stropts.h> +]) + # bluetooth/bluetooth.h has been known to not compile with -std=c99. # http://permalink.gmane.org/gmane.linux.bluez.kernel/22294 SAVE_CFLAGS=$CFLAGS @@ -3046,10 +3217,10 @@ AC_CHECK_HEADERS([linux/vm_sockets.h], [], [], [ #endif ]) -# On Linux, can.h, can/bcm.h, can/j1939.h, can/raw.h require sys/socket.h +# On Linux, can.h, can/bcm.h, can/isotp.h, can/j1939.h, can/raw.h require sys/socket.h # On NetBSD, netcan/can.h requires sys/socket.h AC_CHECK_HEADERS( -[linux/can.h linux/can/bcm.h linux/can/j1939.h linux/can/raw.h netcan/can.h], +[linux/can.h linux/can/bcm.h linux/can/isotp.h linux/can/j1939.h linux/can/raw.h netcan/can.h], [], [], [ #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> @@ -3523,8 +3694,8 @@ then dnl not implemented yet ]);; CYGWIN*) - LDSHARED="gcc -shared -Wl,--enable-auto-image-base" - LDCXXSHARED="g++ -shared -Wl,--enable-auto-image-base";; + LDSHARED='$(CC) -shared -Wl,--enable-auto-image-base' + LDCXXSHARED='$(CXX) -shared -Wl,--enable-auto-image-base';; *) LDSHARED="ld";; esac fi @@ -3695,22 +3866,37 @@ AC_MSG_RESULT([$SHLIBS]) dnl perf trampoline is Linux and macOS specific and requires an arch-specific dnl trampoline in assembly. AC_MSG_CHECKING([perf trampoline]) +PERF_TRAMPOLINE_OBJ="" AS_CASE([$PLATFORM_TRIPLET], - [x86_64-linux-gnu], [perf_trampoline=yes], - [aarch64-linux-gnu], [perf_trampoline=yes], - [darwin], [perf_trampoline=yes], + [x86_64-linux-gnu], [perf_trampoline=yes + PERF_TRAMPOLINE_OBJ=Python/asm_trampoline_x86_64.o], + [aarch64-linux-gnu], [perf_trampoline=yes + PERF_TRAMPOLINE_OBJ=Python/asm_trampoline_aarch64.o], + [darwin], [AS_CASE([$MACOSX_DEPLOYMENT_TARGET], + [[10.[0-9]|10.1[0-1]]], [perf_trampoline=no], + [perf_trampoline=yes + if test "${enable_universalsdk}" && test "$UNIVERSAL_ARCHS" = "universal2"; then + PERF_TRAMPOLINE_OBJ=Python/asm_trampoline_universal2.o + else + case "$host_cpu" in + x86_64) + PERF_TRAMPOLINE_OBJ=Python/asm_trampoline_x86_64.o + ;; + aarch64|arm64) + PERF_TRAMPOLINE_OBJ=Python/asm_trampoline_aarch64.o + ;; + *) + perf_trampoline=no + ;; + esac + fi] + )], [perf_trampoline=no] ) AC_MSG_RESULT([$perf_trampoline]) AS_VAR_IF([perf_trampoline], [yes], [ AC_DEFINE([PY_HAVE_PERF_TRAMPOLINE], [1], [Define to 1 if you have the perf trampoline.]) - PERF_TRAMPOLINE_OBJ=Python/asm_trampoline.o - - dnl perf needs frame pointers for unwinding, include compiler option in debug builds - AS_VAR_IF([Py_DEBUG], [true], [ - AS_VAR_APPEND([BASECFLAGS], [" -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"]) - ]) ]) AC_SUBST([PERF_TRAMPOLINE_OBJ]) @@ -3728,6 +3914,24 @@ AC_CHECK_HEADERS([execinfo.h link.h dlfcn.h], [ ]) ]) +dnl for JIT GNU backtrace unwind registration +AC_CACHE_CHECK([for libgcc frame registration functions], + [ac_cv_have_libgcc_eh_frame_registration], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +void __register_frame(const void *); +void __deregister_frame(const void *); +]], [[ +__register_frame(0); +__deregister_frame(0); +]])], + [ac_cv_have_libgcc_eh_frame_registration=yes], + [ac_cv_have_libgcc_eh_frame_registration=no]) + ]) +AS_VAR_IF([ac_cv_have_libgcc_eh_frame_registration], [yes], [ + AC_DEFINE([_Py_HAVE_LIBGCC_EH_FRAME_REGISTRATION], [1], + [Define to 1 if libgcc __register_frame and __deregister_frame are linkable.]) +]) + dnl only add -ldl to LDFLAGS if it isn't already part of LIBS (GH-133081) AS_VAR_IF([ac_cv_require_ldl], [yes], [ AS_VAR_IF([ac_cv_lib_dl_dlopen], [yes], [], [ @@ -4171,53 +4375,31 @@ if test "$ac_cv_ffi_complex_double_supported" = "yes"; then [Defined if _Complex C type can be used with libffi.]) fi -# Check for use of the system libmpdec library -AC_MSG_CHECKING([for --with-system-libmpdec]) -AC_DEFUN([USE_BUNDLED_LIBMPDEC], - [LIBMPDEC_CFLAGS="-I\$(srcdir)/Modules/_decimal/libmpdec" - LIBMPDEC_LIBS="-lm \$(LIBMPDEC_A)" - LIBMPDEC_INTERNAL="\$(LIBMPDEC_HEADERS) \$(LIBMPDEC_A)" - have_mpdec=yes - with_system_libmpdec=no]) -AC_ARG_WITH( - [system_libmpdec], - [AS_HELP_STRING( - [--with-system-libmpdec], - [build _decimal module using an installed mpdecimal library, see Doc/library/decimal.rst (default is yes)] - )], - [AS_IF([test "x$with_system_libmpdec" = xno], - [USE_BUNDLED_LIBMPDEC()])], - [with_system_libmpdec="yes"]) -AC_MSG_RESULT([$with_system_libmpdec]) - -AS_VAR_IF( - [with_system_libmpdec], [yes], - [PKG_CHECK_MODULES( - [LIBMPDEC], [libmpdec >= 2.5.0], [], - [LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} - LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} - LIBMPDEC_INTERNAL=])]) - -AS_VAR_IF([with_system_libmpdec], [yes], - [WITH_SAVE_ENV([ +dnl Check for libmpdec >= 2.5.0 +PKG_CHECK_MODULES([LIBMPDEC], [libmpdec >= 2.5.0], [have_mpdec=yes], [ + WITH_SAVE_ENV([ CPPFLAGS="$CPPFLAGS $LIBMPDEC_CFLAGS" LIBS="$LIBS $LIBMPDEC_LIBS" - - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([ - #include <mpdecimal.h> - #if MPD_VERSION_HEX < 0x02050000 - # error "mpdecimal 2.5.0 or higher required" - #endif - ], [const char *x = mpd_version();])], - [have_mpdec=yes], - [have_mpdec=no]) - ])]) - -# Disable forced inlining in debug builds, see GH-94847 -AS_VAR_IF( - [with_pydebug], [yes], - [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DTEST_COVERAGE"])]) + AC_CHECK_HEADER([mpdecimal.h], [ + AC_CHECK_LIB([mpdec], [mpd_version], [ + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([ + #include <mpdecimal.h> + #if MPD_VERSION_HEX < 0x02050000 + # error "mpdecimal 2.5.0 or higher required" + #endif + ], [])], + [have_mpdec=yes], + [have_mpdec=no]) + ], [have_mpdec=no]) + ], [have_mpdec=no]) + + AS_VAR_IF([have_mpdec], [yes], [ + LIBMPDEC_CFLAGS=${LIBMPDEC_CFLAGS-""} + LIBMPDEC_LIBS=${LIBMPDEC_LIBS-"-lmpdec -lm"} + ]) + ]) +]) # Check whether _decimal should use a coroutine-local or thread-local context AC_MSG_CHECKING([for --with-decimal-contextvar]) @@ -4238,70 +4420,6 @@ fi AC_MSG_RESULT([$with_decimal_contextvar]) -AS_VAR_IF( - [with_system_libmpdec], [no], - [# Check for libmpdec machine flavor - AC_MSG_CHECKING([for decimal libmpdec machine]) - AS_CASE([$ac_sys_system], - [Darwin*], [libmpdec_system=Darwin], - [SunOS*], [libmpdec_system=sunos], - [libmpdec_system=other] - ) - - libmpdec_machine=unknown - if test "$libmpdec_system" = Darwin; then - # universal here means: build libmpdec with the same arch options - # the python interpreter was built with - libmpdec_machine=universal - elif test $ac_cv_sizeof_size_t -eq 8; then - if test "$ac_cv_gcc_asm_for_x64" = yes; then - libmpdec_machine=x64 - elif test "$ac_cv_type___uint128_t" = yes; then - libmpdec_machine=uint128 - else - libmpdec_machine=ansi64 - fi - elif test $ac_cv_sizeof_size_t -eq 4; then - if test "$ac_cv_gcc_asm_for_x87" = yes -a "$libmpdec_system" != sunos; then - AS_CASE([$ac_cv_cc_name], - [*gcc*], [libmpdec_machine=ppro], - [*clang*], [libmpdec_machine=ppro], - [libmpdec_machine=ansi32] - ) - else - libmpdec_machine=ansi32 - fi - fi - AC_MSG_RESULT([$libmpdec_machine]) - - AS_CASE([$libmpdec_machine], - [x64], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_64=1 -DASM=1"])], - [uint128], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_64=1 -DANSI=1 -DHAVE_UINT128_T=1"])], - [ansi64], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_64=1 -DANSI=1"])], - [ppro], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_32=1 -DANSI=1 -DASM=1 -Wno-unknown-pragmas"])], - [ansi32], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_32=1 -DANSI=1"])], - [ansi-legacy], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DCONFIG_32=1 -DANSI=1 -DLEGACY_COMPILER=1"])], - [universal], [AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -DUNIVERSAL=1"])], - [AC_MSG_ERROR([_decimal: unsupported architecture])] - )]) - -if test "$have_ipa_pure_const_bug" = yes; then - # Some versions of gcc miscompile inline asm: - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491 - # https://gcc.gnu.org/ml/gcc/2010-11/msg00366.html - AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -fno-ipa-pure-const"]) -fi - -if test "$have_glibc_memmove_bug" = yes; then - # _FORTIFY_SOURCE wrappers for memmove and bcopy are incorrect: - # https://sourceware.org/ml/libc-alpha/2010-12/msg00009.html - AS_VAR_APPEND([LIBMPDEC_CFLAGS], [" -U_FORTIFY_SOURCE"]) -fi - -AC_SUBST([LIBMPDEC_CFLAGS]) -AC_SUBST([LIBMPDEC_INTERNAL]) - - dnl detect sqlite3 from Emscripten emport PY_CHECK_EMSCRIPTEN_PORT([LIBSQLITE3], [-sUSE_SQLITE3]) @@ -4741,6 +4859,7 @@ if test "$posix_threads" = "yes"; then if (pthread_attr_init(&attr)) return (-1); if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)) return (-1); if (pthread_create(&id, &attr, foo, NULL)) return (-1); + if (pthread_join(id, NULL)) return (-1); return (0); }]])], [ac_cv_pthread_system_supported=yes], @@ -4994,6 +5113,41 @@ AS_VAR_IF([ac_cv_builtin_atomic], [yes], [ AC_DEFINE(HAVE_BUILTIN_ATOMIC, 1, [Has builtin __atomic_load_n() and __atomic_store_n() functions]) ]) +# Check for __builtin_shufflevector with 128-bit vector support on an +# architecture where it compiles to worthwhile native SIMD instructions. +# Used for SIMD-accelerated bytes.hex() in Python/pystrhex.c. +AC_CACHE_CHECK([for __builtin_shufflevector], [ac_cv_efficient_builtin_shufflevector], [ +AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[ + /* __builtin_shufflevector is available on many platforms, but 128-bit + vector code is only worthwhile on architectures with native SIMD: + x86-64 (SSE2, always available), ARM64 (NEON, always available), + or ARM32 when NEON is enabled via compiler flags (e.g. -march=native + on RPi3+). On ARM32 without NEON (e.g. armv6 builds), the compiler + has the builtin but generates slow scalar code instead. */ + #if !defined(__x86_64__) && !defined(__aarch64__) && \ + !(defined(__arm__) && defined(__ARM_NEON)) + # error "128-bit vector SIMD not worthwhile on this architecture" + #endif + typedef unsigned char v16u8 __attribute__((vector_size(16))); + ]], [[ + v16u8 a = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + v16u8 b = {16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}; + v16u8 c = __builtin_shufflevector(a, b, + 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23); + (void)c; + return 0; + ]]) +],[ac_cv_efficient_builtin_shufflevector=yes],[ac_cv_efficient_builtin_shufflevector=no]) +]) + +AS_VAR_IF([ac_cv_efficient_builtin_shufflevector], [yes], [ + AC_DEFINE([_Py_HAVE_EFFICIENT_BUILTIN_SHUFFLEVECTOR], [1], + [Define if compiler supports __builtin_shufflevector with 128-bit + vectors AND the target architecture has native SIMD (not just API + availability)]) +]) + # --with-mimalloc AC_MSG_CHECKING([for --with-mimalloc]) AC_ARG_WITH([mimalloc], @@ -5041,6 +5195,29 @@ then fi AC_MSG_RESULT([$with_pymalloc]) +AC_MSG_CHECKING([for --with-pymalloc-hugepages]) +AC_ARG_WITH( + [pymalloc-hugepages], + [AS_HELP_STRING([--with-pymalloc-hugepages], + [enable huge page support for pymalloc arenas (default is no)])]) +if test "$with_pymalloc_hugepages" = "yes" +then + dnl configure only runs on Unix-like systems; Windows uses MEM_LARGE_PAGES + dnl via VirtualAlloc but does not use configure. Only check MAP_HUGETLB here. + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ +#include <sys/mman.h> + ]], [[ +int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB; +(void)flags; + ]])], + [AC_DEFINE([PYMALLOC_USE_HUGEPAGES], [1], + [Define to use huge pages for pymalloc arenas])], + [AC_MSG_WARN([--with-pymalloc-hugepages requested but MAP_HUGETLB not found]) + with_pymalloc_hugepages=no]) +fi +AC_MSG_RESULT([${with_pymalloc_hugepages:-no}]) + # Check for --with-c-locale-coercion AC_MSG_CHECKING([for --with-c-locale-coercion]) AC_ARG_WITH( @@ -5104,16 +5281,33 @@ then # linked into the binary. Correspondingly, dtrace(1) is missing the ELF # generation flag '-G'. We check for presence of this flag, rather than # hardcoding support by OS, in the interest of robustness. + # + # NetBSD DTrace requires the -x nolibs flag to avoid system library conflicts + # and uses header generation for testing instead of object generation. AC_CACHE_CHECK([whether DTrace probes require linking], - [ac_cv_dtrace_link], [dnl + [ac_cv_dtrace_link], [ ac_cv_dtrace_link=no echo 'BEGIN{}' > conftest.d - "$DTRACE" $DFLAGS -G -s conftest.d -o conftest.o > /dev/null 2>&1 && \ + case $host in + *netbsd*) + DTRACE_TEST_FLAGS="-x nolibs -h" + ;; + *) + DTRACE_TEST_FLAGS="-G" + ;; + esac + "$DTRACE" $DFLAGS $DTRACE_TEST_FLAGS -s conftest.d -o conftest.o > /dev/null 2>&1 && \ ac_cv_dtrace_link=yes ]) if test "$ac_cv_dtrace_link" = "yes"; then DTRACE_OBJS="Python/pydtrace.o" fi + # Set NetBSD-specific DTrace flags in DFLAGS + case $host in + *netbsd*) + DFLAGS="$DFLAGS -x nolibs" + ;; + esac fi dnl Platform-specific C and header files. @@ -5122,7 +5316,7 @@ PLATFORM_OBJS= AS_CASE([$ac_sys_system], [Emscripten], [ - AS_VAR_APPEND([PLATFORM_OBJS], [' Python/emscripten_signal.o Python/emscripten_trampoline.o Python/emscripten_syscalls.o']) + AS_VAR_APPEND([PLATFORM_OBJS], [' Python/emscripten_signal.o Python/emscripten_trampoline.o Python/emscripten_trampoline_wasm.o Python/emscripten_syscalls.o']) AS_VAR_APPEND([PLATFORM_HEADERS], [' $(srcdir)/Include/internal/pycore_emscripten_signal.h $(srcdir)/Include/internal/pycore_emscripten_trampoline.h']) ], ) @@ -5204,8 +5398,9 @@ fi # checks for library functions AC_CHECK_FUNCS([ \ - accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ - copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \ + accept4 alarm bind_textdomain_codeset chmod chown clearenv \ + clock closefrom close_range confstr \ + copy_file_range ctermid dladdr dup execv explicit_bzero explicit_memset \ faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ @@ -5215,11 +5410,11 @@ AC_CHECK_FUNCS([ \ getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \ lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ - pipe2 plock poll posix_fadvise posix_fallocate posix_openpt posix_spawn posix_spawnp \ + plock poll ppoll posix_fadvise posix_fallocate posix_openpt posix_spawn posix_spawnp \ posix_spawn_file_actions_addclosefrom_np \ pread preadv preadv2 process_vm_readv \ pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ - pthread_kill pthread_get_name_np pthread_getname_np pthread_set_name_np + pthread_kill pthread_get_name_np pthread_getname_np pthread_set_name_np \ pthread_setname_np pthread_getattr_np \ ptsname ptsname_r pwrite pwritev pwritev2 readlink readlinkat readv realpath renameat \ rtpSpawn sched_get_priority_max sched_rr_get_interval sched_setaffinity \ @@ -5234,6 +5429,12 @@ AC_CHECK_FUNCS([ \ wait wait3 wait4 waitid waitpid wcscoll wcsftime wcsxfrm wmemcmp writev \ ]) +# os.statx uses Linux's statx function. AIX also has a function named statx, +# but it's unrelated. Check only on Linux (including Android). +AS_CASE([$ac_sys_system], + [Linux*], [AC_CHECK_FUNCS([statx])] +) + # Force lchmod off for Linux. Linux disallows changing the mode of symbolic # links. Some libc implementations have a stub lchmod implementation that always # returns an error. @@ -5246,7 +5447,7 @@ fi # header definition prevents usage - autoconf doesn't use the headers), or # raise an error if used at runtime. Force these symbols off. if test "$ac_sys_system" != "iOS" ; then - AC_CHECK_FUNCS([getentropy getgroups system]) + AC_CHECK_FUNCS([dup3 getentropy getgroups pipe2 system]) fi AC_CHECK_DECL([dirfd], @@ -5264,8 +5465,20 @@ PY_CHECK_FUNC([symlink], [@%:@include <unistd.h>]) PY_CHECK_FUNC([fchdir], [@%:@include <unistd.h>]) PY_CHECK_FUNC([fsync], [@%:@include <unistd.h>]) PY_CHECK_FUNC([fdatasync], [@%:@include <unistd.h>]) -PY_CHECK_FUNC([epoll_create], [@%:@include <sys/epoll.h>], [HAVE_EPOLL]) -PY_CHECK_FUNC([epoll_create1], [@%:@include <sys/epoll.h>]) + +AC_MSG_CHECKING([for --disable-epoll]) +AC_ARG_ENABLE([epoll], + [AS_HELP_STRING([--disable-epoll], [disable epoll (default is yes if supported)])], + [AS_VAR_IF([enable_epoll], [no], [disable_epoll=yes], [disable_epoll=no])], + [disable_epoll=no] +) +AC_MSG_RESULT([$disable_epoll]) +if test "$disable_epoll" = "no" +then + PY_CHECK_FUNC([epoll_create], [@%:@include <sys/epoll.h>], [HAVE_EPOLL]) + PY_CHECK_FUNC([epoll_create1], [@%:@include <sys/epoll.h>]) +fi + PY_CHECK_FUNC([kqueue],[ #include <sys/types.h> #include <sys/event.h> @@ -5401,7 +5614,7 @@ AH_TEMPLATE([HAVE_ZLIB_COPY], [Define if the zlib library has inflateCopy]) dnl detect zlib from Emscripten emport PY_CHECK_EMSCRIPTEN_PORT([ZLIB], [-sUSE_ZLIB]) -PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.0], [ +PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.2.1], [ have_zlib=yes dnl zlib 1.2.0 (2003) added inflateCopy AC_DEFINE([HAVE_ZLIB_COPY], [1]) @@ -5486,6 +5699,18 @@ PKG_CHECK_MODULES([LIBZSTD], [libzstd >= 1.4.5], [have_libzstd=yes], [ ]) ]) +dnl _remote_debugging module: optional zstd compression support +dnl The module always builds, but zstd compression is only available when libzstd is found +AS_VAR_IF([have_libzstd], [yes], [ + REMOTE_DEBUGGING_CFLAGS="-DHAVE_ZSTD $LIBZSTD_CFLAGS" + REMOTE_DEBUGGING_LIBS="$LIBZSTD_LIBS" +], [ + REMOTE_DEBUGGING_CFLAGS="" + REMOTE_DEBUGGING_LIBS="" +]) +AC_SUBST([REMOTE_DEBUGGING_CFLAGS]) +AC_SUBST([REMOTE_DEBUGGING_LIBS]) + dnl PY_CHECK_NETDB_FUNC(FUNCTION) AC_DEFUN([PY_CHECK_NETDB_FUNC], [PY_CHECK_FUNC([$1], [@%:@include <netdb.h>])]) @@ -5539,7 +5764,15 @@ AC_CHECK_DECLS([UT_NAMESIZE], [Define if you have the 'HAVE_UT_NAMESIZE' constant.])], [], [@%:@include <utmp.h>]) - +# musl libc redefines struct prctl_mm_map and conflicts with linux/prctl.h +AS_IF([test "$ac_cv_libc" != musl], [ +AC_CHECK_DECLS([PR_SET_VMA_ANON_NAME], + [AC_DEFINE([_Py_HAVE_PR_SET_VMA_ANON_NAME], [1], + [Define if you have the 'PR_SET_VMA_ANON_NAME' constant.])], + [], + [@%:@include <linux/prctl.h> + @%:@include <sys/prctl.h>]) +]) # check for openpty, login_tty, and forkpty AC_CHECK_FUNCS([openpty], [], @@ -5796,6 +6029,24 @@ AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_passwd], [], [], [[ # Issue #21085: In Cygwin, siginfo_t does not have si_band field. AC_CHECK_MEMBERS([siginfo_t.si_band], [], [], [[@%:@include <signal.h>]]) +if test "$ac_cv_func_statx" = yes; then + # Some systems have the definitions of the mask bits without having the + # corresponding members in struct statx. Check for members added after Linux + # 4.11 (when statx itself was added). + AC_CHECK_MEMBERS([struct statx.stx_mnt_id]) + AC_CHECK_MEMBERS([struct statx.stx_dio_mem_align]) + # stx_dio_offset_align was added together with stx_dio_mem_align + AC_CHECK_MEMBERS([struct statx.stx_subvol]) + AC_CHECK_MEMBERS([struct statx.stx_atomic_write_unit_min]) + # stx_atomic_write_unit_max and stx_atomic_write_segments_max were added + # together with stx_atomic_write_unit_min + AC_CHECK_MEMBERS([struct statx.stx_dio_read_offset_align]) + # stx_atomic_write_unit_max_opt was added in Linux 6.16, but is controlled by + # the STATX_WRITE_ATOMIC mask bit added in Linux 6.11, so having the mask bit + # doesn't imply having the member. + AC_CHECK_MEMBERS([struct statx.stx_atomic_write_unit_max_opt]) +fi + AC_CACHE_CHECK([for time.h that defines altzone], [ac_cv_header_time_altzone], [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <time.h>]], [[return altzone;]])], [ac_cv_header_time_altzone=yes], @@ -6024,21 +6275,11 @@ AX_C_FLOAT_WORDS_BIGENDIAN( [AC_DEFINE([DOUBLE_IS_LITTLE_ENDIAN_IEEE754], [1], [Define if C doubles are 64-bit IEEE 754 binary format, stored with the least significant byte first])], - [AS_CASE([$host_cpu], - [*arm*], [# Some ARM platforms use a mixed-endian representation for - # doubles. While Python doesn't currently have full support - # for these platforms (see e.g., issue 1762561), we can at - # least make sure that float <-> string conversions work. - # FLOAT_WORDS_BIGENDIAN doesn't actually detect this case, - # but if it's not big or little, then it must be this? - AC_DEFINE([DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754], [1], - [Define if C doubles are 64-bit IEEE 754 binary format, - stored in ARM mixed-endian order (byte order 45670123)])], - [AC_MSG_ERROR([m4_normalize([ + [AC_MSG_ERROR([m4_normalize([ Unknown float word ordering. You need to manually preset ax_cv_c_float_words_bigendian=no (or yes) according to your system. - ])])])]) + ])])]) # The short float repr introduced in Python 3.1 requires the # correctly-rounded string <-> double conversion functions from @@ -6065,8 +6306,8 @@ AS_VAR_IF([ac_cv_gcc_asm_for_x87], [yes], [ AC_CACHE_CHECK([whether we can use gcc inline assembler to get and set mc68881 fpcr], [ac_cv_gcc_asm_for_mc68881], [ AC_LINK_IFELSE( [AC_LANG_PROGRAM([[]], [[ unsigned int fpcr; - __asm__ __volatile__ ("fmove.l %%fpcr,%0" : "=g" (fpcr)); - __asm__ __volatile__ ("fmove.l %0,%%fpcr" : : "g" (fpcr)); + __asm__ __volatile__ ("fmove.l %%fpcr,%0" : "=dm" (fpcr)); + __asm__ __volatile__ ("fmove.l %0,%%fpcr" : : "dm" (fpcr)); ]])],[ac_cv_gcc_asm_for_mc68881=yes],[ac_cv_gcc_asm_for_mc68881=no]) ]) AS_VAR_IF([ac_cv_gcc_asm_for_mc68881], [yes], [ @@ -6126,6 +6367,8 @@ AC_CHECK_FUNCS( [], [AC_MSG_ERROR([Python requires C99 compatible libm])] ) + +AC_CHECK_FUNCS_ONCE(acospi asinpi atanpi atan2pi cospi sinpi tanpi) LIBS=$LIBS_SAVE dnl For multiprocessing module, check that sem_open @@ -6352,15 +6595,10 @@ if test "$ac_sys_system" = "iOS"; then MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi - -AC_SUBST([BINLIBDEST]) -BINLIBDEST='$(LIBDIR)/python$(VERSION)$(ABI_THREAD)' - - # Check for --with-platlibdir # /usr/$PLATLIBDIR/python$(VERSION)$(ABI_THREAD) AC_SUBST([PLATLIBDIR]) -PLATLIBDIR="lib" +PLATLIBDIR="lib" # XXX: We should probably calculate the defauly from libdir, if defined. AC_MSG_CHECKING([for --with-platlibdir]) AC_ARG_WITH( [platlibdir], @@ -6377,19 +6615,22 @@ if test -n "$withval" -a "$withval" != yes -a "$withval" != no then AC_MSG_RESULT([yes]) PLATLIBDIR="$withval" - BINLIBDEST='${exec_prefix}/${PLATLIBDIR}/python$(VERSION)$(ABI_THREAD)' else AC_MSG_RESULT([no]) fi], [AC_MSG_RESULT([no])]) +AC_SUBST([LIBDEST]) +AC_SUBST([BINLIBDEST]) +LIBDEST='${prefix}/${PLATLIBDIR}/python$(VERSION)$(ABI_THREAD)' +BINLIBDEST='${exec_prefix}/${PLATLIBDIR}/python$(VERSION)$(ABI_THREAD)' dnl define LIBPL after ABIFLAGS and LDVERSION is defined. AC_SUBST([PY_ENABLE_SHARED]) if test x$PLATFORM_TRIPLET = x; then - LIBPL='$(prefix)'"/${PLATLIBDIR}/python${VERSION}${ABI_THREAD}/config-${LDVERSION}" + LIBPL='$(LIBDEST)'"/config-${LDVERSION}" else - LIBPL='$(prefix)'"/${PLATLIBDIR}/python${VERSION}${ABI_THREAD}/config-${LDVERSION}-${PLATFORM_TRIPLET}" + LIBPL='$(LIBDEST)'"/config-${LDVERSION}-${PLATFORM_TRIPLET}" fi AC_SUBST([LIBPL]) @@ -6783,57 +7024,6 @@ then [Define if you have struct stat.st_mtimensec]) fi -AC_CACHE_CHECK([whether year with century should be normalized for strftime], [ac_cv_normalize_century], [ -AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include <time.h> -#include <string.h> - -int main(void) -{ - char year[5]; - struct tm date = { - .tm_year = -1801, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { - return 1; - } - return 0; -} -]])], -[ac_cv_normalize_century=yes], -[ac_cv_normalize_century=no], -[ac_cv_normalize_century=yes])]) -if test "$ac_cv_normalize_century" = yes -then - AC_DEFINE([_Py_NORMALIZE_CENTURY], [1], - [Define if year with century should be normalized for strftime.]) -fi - -AC_CACHE_CHECK([whether C99-compatible strftime specifiers are supported], [ac_cv_strftime_c99_support], [ -AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include <time.h> -#include <string.h> - -int main(void) -{ - char full_date[11]; - struct tm date = { - .tm_year = 0, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) { - return 0; - } - return 1; -} -]])], -[ac_cv_strftime_c99_support=yes], -[AC_MSG_ERROR([Python requires C99-compatible strftime specifiers])], -[ac_cv_strftime_c99_support=])]) - dnl check for ncursesw/ncurses and panelw/panel dnl NOTE: old curses is not detected. dnl have_curses=[no, yes] @@ -7134,13 +7324,13 @@ AC_ARG_WITH( [ if test "$withval" = yes then - AC_DEFINE([Py_TAIL_CALL_INTERP], [1], + AC_DEFINE([_Py_TAIL_CALL_INTERP], [1], [Define if you want to use tail-calling interpreters in CPython.]) AC_MSG_RESULT([yes]) fi if test "$withval" = no then - AC_DEFINE([Py_TAIL_CALL_INTERP], [0], + AC_DEFINE([_Py_TAIL_CALL_INTERP], [0], [Define if you want to use tail-calling interpreters in CPython.]) AC_MSG_RESULT([no]) fi @@ -7185,10 +7375,10 @@ SRCDIRS="\ Modules \ Modules/_ctypes \ Modules/_decimal \ - Modules/_decimal/libmpdec \ Modules/_hacl \ Modules/_io \ Modules/_multiprocessing \ + Modules/_remote_debugging \ Modules/_sqlite \ Modules/_sre \ Modules/_testcapi \ @@ -7258,6 +7448,34 @@ if test "$have_glibc_memmove_bug" = yes; then for memmove and bcopy.]) fi +AC_MSG_CHECKING([if we need to manually block large inlining in ceval.c]) +AC_RUN_IFELSE([AC_LANG_SOURCE([[ +int main(void) { +// See gh-148284: Clang 22 seems to have interactions with inlining +// and the stackref buffer which cause 40 kB of stack usage on x86-64 +// in buggy versions of _PyEval_EvalFrameDefault() in computed goto +// interpreter. The normal usage seen is normally 1-2 kB. +#if defined(__clang__) && (__clang_major__ == 22) + return 1; +#else + return 0; +#endif +} +]])], +[block_huge_inlining_in_ceval=no], +[block_huge_inlining_in_ceval=yes], +[block_huge_inlining_in_ceval=undefined]) +AC_MSG_RESULT([$block_huge_inlining_in_ceval]) + +if test "$block_huge_inlining_in_ceval" = yes && test "$ac_cv_computed_gotos" = yes; then + # gh-148284: Suppress inlining of functions whose stack size exceeds + # 512 bytes. This number should be tuned to follow the C stack + # consumption in _PyEval_EvalFrameDefault() on computed goto + # interpreter. + CFLAGS_CEVAL="$CFLAGS_CEVAL -finline-max-stacksize=512" +fi +AC_SUBST([CFLAGS_CEVAL]) + if test "$ac_cv_gcc_asm_for_x87" = yes; then # Some versions of gcc miscompile inline asm: # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46491 @@ -7622,6 +7840,40 @@ AC_ARG_ENABLE([test-modules], AC_MSG_RESULT([$TEST_MODULES]) AC_SUBST([TEST_MODULES]) +# Check for --with-build-details-suffix +BUILD_DETAILS=build-details.json +AC_ARG_WITH([build-details-suffix], + [AS_HELP_STRING( + [--with-build-details-suffix=], + [rename build-details.json to permit multiple colocated Python installs; optionally specify a custom suffix (default: no)] + )], + [ + AC_MSG_CHECKING([for --with-build-details-suffix]) + AS_VAR_IF( + [with_build_details_suffix], [no], + [AC_MSG_ERROR([invalid --with-build-details-suffix option: expected custom suffix or "yes", not "no"])] + ) + AS_VAR_IF( + [with_build_details_suffix], [yes], [ + colocated_install=yes + threading_suffix="" + if [[ "$ABI_THREAD" = "t" ]]; then + threading_suffix=-free-threading + fi + debug_suffix="" + if [[ "$Py_DEBUG" = "true" ]]; then + debug_suffix=-debug + fi + BUILD_DETAILS=build-details.$MULTIARCH$threading_suffix$debug_suffix.json + ], [ + BUILD_DETAILS=build-details.$with_build_details_suffix.json + ] + ) + ] +) +AC_MSG_RESULT([$with_build_details_suffix]) +AC_SUBST([BUILD_DETAILS], [$BUILD_DETAILS]) + # gh-109054: Check if -latomic is needed to get <pyatomic.h> atomic functions. # On Linux aarch64, GCC may require programs and libraries to be linked # explicitly to libatomic. Call _Py_atomic_or_uint64() which may require @@ -7697,6 +7949,7 @@ case "$ac_sys_system" in iOS) _PYTHREAD_NAME_MAXLEN=63;; FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 + CYGWIN*) _PYTHREAD_NAME_MAXLEN=16;; *) _PYTHREAD_NAME_MAXLEN=;; esac if test -n "$_PYTHREAD_NAME_MAXLEN"; then @@ -7809,6 +8062,7 @@ AS_CASE([$ac_sys_system], [termios], [xxlimited], [xxlimited_35], + [xxlimited_3_13], ) ], [PY_STDLIB_MOD_SET_NA([_scproxy])] @@ -7884,6 +8138,7 @@ PY_STDLIB_MOD_SIMPLE([time], [], [$TIMEMODULE_LIB]) dnl always enabled extension modules PY_STDLIB_MOD_SIMPLE([array]) +PY_STDLIB_MOD_SIMPLE([_math_integer]) PY_STDLIB_MOD_SIMPLE([_asyncio]) PY_STDLIB_MOD_SIMPLE([_bisect]) PY_STDLIB_MOD_SIMPLE([_csv]) @@ -7894,7 +8149,7 @@ PY_STDLIB_MOD_SIMPLE([_pickle]) PY_STDLIB_MOD_SIMPLE([_posixsubprocess]) PY_STDLIB_MOD_SIMPLE([_queue]) PY_STDLIB_MOD_SIMPLE([_random]) -PY_STDLIB_MOD_SIMPLE([_remote_debugging]) +PY_STDLIB_MOD_SIMPLE([_remote_debugging], [$REMOTE_DEBUGGING_CFLAGS], [$REMOTE_DEBUGGING_LIBS]) PY_STDLIB_MOD_SIMPLE([select]) PY_STDLIB_MOD_SIMPLE([_struct]) PY_STDLIB_MOD_SIMPLE([_types]) @@ -8075,7 +8330,7 @@ AC_SUBST([LIBHACL_BLAKE2_SIMD256_OBJS]) # HACL*-based cryptographic primitives AC_MSG_CHECKING([for HACL* library linking type]) -if test "$ac_sys_system" = "WASI"; then +if test "$ac_sys_system" = "WASI" || test "$MODULE_BUILDTYPE" = "static"; then LIBHACL_LDEPS_LIBTYPE=STATIC AC_MSG_RESULT([static]) else @@ -8129,11 +8384,7 @@ PY_STDLIB_MOD([_decimal], [], [test "$have_mpdec" = "yes"], [$LIBMPDEC_CFLAGS], [$LIBMPDEC_LIBS]) -AS_VAR_IF([with_system_libmpdec], [no], - [AC_MSG_WARN([m4_normalize([ - the bundled copy of libmpdec is scheduled for removal in Python 3.16; - consider using a system installed mpdecimal library.])])]) -AS_IF([test "$with_system_libmpdec" = "yes" && test "$have_mpdec" = "no"], +AS_IF([test "$have_mpdec" = "no"], [AC_MSG_WARN([m4_normalize([ no system libmpdec found; falling back to pure-Python version for the decimal module])])]) @@ -8177,10 +8428,15 @@ PY_STDLIB_MOD([_hashlib], [], [test "$ac_cv_working_openssl_hashlib" = yes], [$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $LIBCRYPTO_LIBS]) dnl test modules +AS_CASE([$ac_sys_system], + # On FreeBSD, _testcapi.get_process_memory_usage() calls kvm_openfiles() + # and so needs libkvm. + [FreeBSD*], [LIBKVM="-lkvm"] +) PY_STDLIB_MOD([_testcapi], [test "$TEST_MODULES" = yes], dnl Modules/_testcapi needs -latomic for 32bit AIX build - [], [], [$LIBATOMIC]) + [], [], [$LIBATOMIC $LIBKVM]) PY_STDLIB_MOD([_testclinic], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([_testclinic_limited], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([_testlimitedcapi], [test "$TEST_MODULES" = yes]) @@ -8199,6 +8455,56 @@ dnl Limited API template modules. dnl Emscripten does not support shared libraries yet. PY_STDLIB_MOD([xxlimited], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) PY_STDLIB_MOD([xxlimited_35], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) +PY_STDLIB_MOD([xxlimited_3_13], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) + +# Determine JIT stencils header files based on target platform +JIT_STENCILS_H="" +JIT_SHIM_O="" +JIT_SHIM_BUILD_O="" +AS_VAR_IF([jit_flags], + [], + [], + [if test "${enable_universalsdk}" && test "$UNIVERSAL_ARCHS" = "universal2"; then + JIT_STENCILS_H="jit_stencils-aarch64-apple-darwin.h jit_stencils-x86_64-apple-darwin.h" + JIT_SHIM_O="jit_shim-universal2-apple-darwin.o" + JIT_SHIM_BUILD_O="jit_shim-aarch64-apple-darwin.o jit_shim-x86_64-apple-darwin.o" + else + case "$host" in + aarch64-apple-darwin*) + JIT_STENCILS_H="jit_stencils-aarch64-apple-darwin.h" + JIT_SHIM_O="jit_shim-aarch64-apple-darwin.o" + ;; + x86_64-apple-darwin*) + JIT_STENCILS_H="jit_stencils-x86_64-apple-darwin.h" + JIT_SHIM_O="jit_shim-x86_64-apple-darwin.o" + ;; + aarch64-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-aarch64-pc-windows-msvc.h" + JIT_SHIM_O="jit_shim-aarch64-pc-windows-msvc.o" + ;; + i686-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-i686-pc-windows-msvc.h" + JIT_SHIM_O="jit_shim-i686-pc-windows-msvc.o" + ;; + x86_64-pc-windows-msvc) + JIT_STENCILS_H="jit_stencils-x86_64-pc-windows-msvc.h" + JIT_SHIM_O="jit_shim-x86_64-pc-windows-msvc.o" + ;; + aarch64-*-linux-gnu) + JIT_STENCILS_H="jit_stencils-aarch64-unknown-linux-gnu.h" + JIT_SHIM_O="jit_shim-aarch64-unknown-linux-gnu.o" + ;; + x86_64-*-linux-gnu) + JIT_STENCILS_H="jit_stencils-x86_64-unknown-linux-gnu.h" + JIT_SHIM_O="jit_shim-x86_64-unknown-linux-gnu.o" + ;; + esac + JIT_SHIM_BUILD_O="$JIT_SHIM_O" + fi]) + +AC_SUBST([JIT_STENCILS_H]) +AC_SUBST([JIT_SHIM_O]) +AC_SUBST([JIT_SHIM_BUILD_O]) # substitute multiline block, must come after last PY_STDLIB_MOD() AC_SUBST([MODULE_BLOCK]) diff --git a/iOS/README.rst b/iOS/README.rst deleted file mode 100644 index f0979ba152eb20..00000000000000 --- a/iOS/README.rst +++ /dev/null @@ -1,375 +0,0 @@ -==================== -Python on iOS README -==================== - -:Authors: - Russell Keith-Magee (2023-11) - -This document provides a quick overview of some iOS specific features in the -Python distribution. - -These instructions are only needed if you're planning to compile Python for iOS -yourself. Most users should *not* need to do this. If you're looking to -experiment with writing an iOS app in Python, tools such as `BeeWare's Briefcase -<https://briefcase.readthedocs.io>`__ and `Kivy's Buildozer -<https://buildozer.readthedocs.io>`__ will provide a much more approachable -user experience. - -Compilers for building on iOS -============================= - -Building for iOS requires the use of Apple's Xcode tooling. It is strongly -recommended that you use the most recent stable release of Xcode. This will -require the use of the most (or second-most) recently released macOS version, -as Apple does not maintain Xcode for older macOS versions. The Xcode Command -Line Tools are not sufficient for iOS development; you need a *full* Xcode -install. - -If you want to run your code on the iOS simulator, you'll also need to install -an iOS Simulator Platform. You should be prompted to select an iOS Simulator -Platform when you first run Xcode. Alternatively, you can add an iOS Simulator -Platform by selecting an open the Platforms tab of the Xcode Settings panel. - -iOS specific arguments to configure -=================================== - -* ``--enable-framework[=DIR]`` - - This argument specifies the location where the Python.framework will be - installed. If ``DIR`` is not specified, the framework will be installed into - a subdirectory of the ``iOS/Frameworks`` folder. - - This argument *must* be provided when configuring iOS builds. iOS does not - support non-framework builds. - -* ``--with-framework-name=NAME`` - - Specify the name for the Python framework; defaults to ``Python``. - - .. admonition:: Use this option with care! - - Unless you know what you're doing, changing the name of the Python - framework on iOS is not advised. If you use this option, you won't be able - to run the ``make testios`` target without making significant manual - alterations, and you won't be able to use any binary packages unless you - compile them yourself using your own framework name. - -Building Python on iOS -====================== - -ABIs and Architectures ----------------------- - -iOS apps can be deployed on physical devices, and on the iOS simulator. Although -the API used on these devices is identical, the ABI is different - you need to -link against different libraries for an iOS device build (``iphoneos``) or an -iOS simulator build (``iphonesimulator``). - -Apple uses the ``XCframework`` format to allow specifying a single dependency -that supports multiple ABIs. An ``XCframework`` is a wrapper around multiple -ABI-specific frameworks that share a common API. - -iOS can also support different CPU architectures within each ABI. At present, -there is only a single supported architecture on physical devices - ARM64. -However, the *simulator* supports 2 architectures - ARM64 (for running on Apple -Silicon machines), and x86_64 (for running on older Intel-based machines). - -To support multiple CPU architectures on a single platform, Apple uses a "fat -binary" format - a single physical file that contains support for multiple -architectures. It is possible to compile and use a "thin" single architecture -version of a binary for testing purposes; however, the "thin" binary will not be -portable to machines using other architectures. - -Building a single-architecture framework ----------------------------------------- - -The Python build system will create a ``Python.framework`` that supports a -*single* ABI with a *single* architecture. Unlike macOS, iOS does not allow a -framework to contain non-library content, so the iOS build will produce a -``bin`` and ``lib`` folder in the same output folder as ``Python.framework``. -The ``lib`` folder will be needed at runtime to support the Python library. - -If you want to use Python in a real iOS project, you need to produce multiple -``Python.framework`` builds, one for each ABI and architecture. iOS builds of -Python *must* be constructed as framework builds. To support this, you must -provide the ``--enable-framework`` flag when configuring the build. The build -also requires the use of cross-compilation. The minimal commands for building -Python for the ARM64 iOS simulator will look something like:: - - $ export PATH="$(pwd)/iOS/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin" - $ ./configure \ - --enable-framework \ - --host=arm64-apple-ios-simulator \ - --build=arm64-apple-darwin \ - --with-build-python=/path/to/python.exe - $ make - $ make install - -In this invocation: - -* ``iOS/Resources/bin`` has been added to the path, providing some shims for the - compilers and linkers needed by the build. Xcode requires the use of ``xcrun`` - to invoke compiler tooling. However, if ``xcrun`` is pre-evaluated and the - result passed to ``configure``, these results can embed user- and - version-specific paths into the sysconfig data, which limits the portability - of the compiled Python. Alternatively, if ``xcrun`` is used *as* the compiler, - it requires that compiler variables like ``CC`` include spaces, which can - cause significant problems with many C configuration systems which assume that - ``CC`` will be a single executable. - - To work around this problem, the ``iOS/Resources/bin`` folder contains some - wrapper scripts that present as simple compilers and linkers, but wrap - underlying calls to ``xcrun``. This allows configure to use a ``CC`` - definition without spaces, and without user- or version-specific paths, while - retaining the ability to adapt to the local Xcode install. These scripts are - included in the ``bin`` directory of an iOS install. - - These scripts will, by default, use the currently active Xcode installation. - If you want to use a different Xcode installation, you can use - ``xcode-select`` to set a new default Xcode globally, or you can use the - ``DEVELOPER_DIR`` environment variable to specify an Xcode install. The - scripts will use the default ``iphoneos``/``iphonesimulator`` SDK version for - the select Xcode install; if you want to use a different SDK, you can set the - ``IOS_SDK_VERSION`` environment variable. (e.g, setting - ``IOS_SDK_VERSION=17.1`` would cause the scripts to use the ``iphoneos17.1`` - and ``iphonesimulator17.1`` SDKs, regardless of the Xcode default.) - - The path has also been cleared of any user customizations. A common source of - bugs is for tools like Homebrew to accidentally leak macOS binaries into an iOS - build. Resetting the path to a known "bare bones" value is the easiest way to - avoid these problems. - -* ``--host`` is the architecture and ABI that you want to build, in GNU compiler - triple format. This will be one of: - - - ``arm64-apple-ios`` for ARM64 iOS devices. - - ``arm64-apple-ios-simulator`` for the iOS simulator running on Apple - Silicon devices. - - ``x86_64-apple-ios-simulator`` for the iOS simulator running on Intel - devices. - -* ``--build`` is the GNU compiler triple for the machine that will be running - the compiler. This is one of: - - - ``arm64-apple-darwin`` for Apple Silicon devices. - - ``x86_64-apple-darwin`` for Intel devices. - -* ``/path/to/python.exe`` is the path to a Python binary on the machine that - will be running the compiler. This is needed because the Python compilation - process involves running some Python code. On a normal desktop build of - Python, you can compile a python interpreter and then use that interpreter to - run Python code. However, the binaries produced for iOS won't run on macOS, so - you need to provide an external Python interpreter. This interpreter must be - the same version as the Python that is being compiled. To be completely safe, - this should be the *exact* same commit hash. However, the longer a Python - release has been stable, the more likely it is that this constraint can be - relaxed - the same micro version will often be sufficient. - -* The ``install`` target for iOS builds is slightly different to other - platforms. On most platforms, ``make install`` will install the build into - the final runtime location. This won't be the case for iOS, as the final - runtime location will be on a physical device. - - However, you still need to run the ``install`` target for iOS builds, as it - performs some final framework assembly steps. The location specified with - ``--enable-framework`` will be the location where ``make install`` will - assemble the complete iOS framework. This completed framework can then - be copied and relocated as required. - -For a full CPython build, you also need to specify the paths to iOS builds of -the binary libraries that CPython depends on (XZ, BZip2, LibFFI and OpenSSL). -This can be done by defining the ``LIBLZMA_CFLAGS``, ``LIBLZMA_LIBS``, -``BZIP2_CFLAGS``, ``BZIP2_LIBS``, ``LIBFFI_CFLAGS``, and ``LIBFFI_LIBS`` -environment variables, and the ``--with-openssl`` configure option. Versions of -these libraries pre-compiled for iOS can be found in `this repository -<https://github.com/beeware/cpython-apple-source-deps/releases>`__. LibFFI is -especially important, as many parts of the standard library (including the -``platform``, ``sysconfig`` and ``webbrowser`` modules) require the use of the -``ctypes`` module at runtime. - -By default, Python will be compiled with an iOS deployment target (i.e., the -minimum supported iOS version) of 13.0. To specify a different deployment -target, provide the version number as part of the ``--host`` argument - for -example, ``--host=arm64-apple-ios15.4-simulator`` would compile an ARM64 -simulator build with a deployment target of 15.4. - -Merge thin frameworks into fat frameworks ------------------------------------------ - -Once you've built a ``Python.framework`` for each ABI and architecture, you -must produce a "fat" framework for each ABI that contains all the architectures -for that ABI. - -The ``iphoneos`` build only needs to support a single architecture, so it can be -used without modification. - -If you only want to support a single simulator architecture, (e.g., only support -ARM64 simulators), you can use a single architecture ``Python.framework`` build. -However, if you want to create ``Python.xcframework`` that supports *all* -architectures, you'll need to merge the ``iphonesimulator`` builds for ARM64 and -x86_64 into a single "fat" framework. - -The "fat" framework can be constructed by performing a directory merge of the -content of the two "thin" ``Python.framework`` directories, plus the ``bin`` and -``lib`` folders for each thin framework. When performing this merge: - -* The pure Python standard library content is identical for each architecture, - except for a handful of platform-specific files (such as the ``sysconfig`` - module). Ensure that the "fat" framework has the union of all standard library - files. - -* Any binary files in the standard library, plus the main - ``libPython3.X.dylib``, can be merged using the ``lipo`` tool, provide by - Xcode:: - - $ lipo -create -output module.dylib path/to/x86_64/module.dylib path/to/arm64/module.dylib - -* The header files will be identical on both architectures, except for - ``pyconfig.h``. Copy all the headers from one platform (say, arm64), rename - ``pyconfig.h`` to ``pyconfig-arm64.h``, and copy the ``pyconfig.h`` for the - other architecture into the merged header folder as ``pyconfig-x86_64.h``. - Then copy the ``iOS/Resources/pyconfig.h`` file from the CPython sources into - the merged headers folder. This will allow the two Python architectures to - share a common ``pyconfig.h`` header file. - -At this point, you should have 2 Python.framework folders - one for ``iphoneos``, -and one for ``iphonesimulator`` that is a merge of x86+64 and ARM64 content. - -Merge frameworks into an XCframework ------------------------------------- - -Now that we have 2 (potentially fat) ABI-specific frameworks, we can merge those -frameworks into a single ``XCframework``. - -The initial skeleton of an ``XCframework`` is built using:: - - xcodebuild -create-xcframework -output Python.xcframework -framework path/to/iphoneos/Python.framework -framework path/to/iphonesimulator/Python.framework - -Then, copy the ``bin`` and ``lib`` folders into the architecture-specific slices of -the XCframework:: - - cp path/to/iphoneos/bin Python.xcframework/ios-arm64 - cp path/to/iphoneos/lib Python.xcframework/ios-arm64 - - cp path/to/iphonesimulator/bin Python.xcframework/ios-arm64_x86_64-simulator - cp path/to/iphonesimulator/lib Python.xcframework/ios-arm64_x86_64-simulator - -Note that the name of the architecture-specific slice for the simulator will -depend on the CPU architecture(s) that you build. - -You now have a Python.xcframework that can be used in a project. - -Testing Python on iOS -===================== - -The ``iOS/testbed`` folder that contains an Xcode project that is able to run -the iOS test suite. This project converts the Python test suite into a single -test case in Xcode's XCTest framework. The single XCTest passes if the test -suite passes. - -To run the test suite, configure a Python build for an iOS simulator (i.e., -``--host=arm64-apple-ios-simulator`` or ``--host=x86_64-apple-ios-simulator`` -), specifying a framework build (i.e. ``--enable-framework``). Ensure that your -``PATH`` has been configured to include the ``iOS/Resources/bin`` folder and -exclude any non-iOS tools, then run:: - - $ make all - $ make install - $ make testios - -This will: - -* Build an iOS framework for your chosen architecture; -* Finalize the single-platform framework; -* Make a clean copy of the testbed project; -* Install the Python iOS framework into the copy of the testbed project; and -* Run the test suite on an "iPhone SE (3rd generation)" simulator. - -On success, the test suite will exit and report successful completion of the -test suite. On a 2022 M1 MacBook Pro, the test suite takes approximately 15 -minutes to run; a couple of extra minutes is required to compile the testbed -project, and then boot and prepare the iOS simulator. - -Debugging test failures ------------------------ - -Running ``make test`` generates a standalone version of the ``iOS/testbed`` -project, and runs the full test suite. It does this using ``iOS/testbed`` -itself - the folder is an executable module that can be used to create and run -a clone of the testbed project. - -You can generate your own standalone testbed instance by running:: - - $ python iOS/testbed clone --framework iOS/Frameworks/arm64-iphonesimulator my-testbed - -This invocation assumes that ``iOS/Frameworks/arm64-iphonesimulator`` is the -path to the iOS simulator framework for your platform (ARM64 in this case); -``my-testbed`` is the name of the folder for the new testbed clone. - -You can then use the ``my-testbed`` folder to run the Python test suite, -passing in any command line arguments you may require. For example, if you're -trying to diagnose a failure in the ``os`` module, you might run:: - - $ python my-testbed run -- test -W test_os - -This is the equivalent of running ``python -m test -W test_os`` on a desktop -Python build. Any arguments after the ``--`` will be passed to testbed as if -they were arguments to ``python -m`` on a desktop machine. - -You can also open the testbed project in Xcode by running:: - - $ open my-testbed/iOSTestbed.xcodeproj - -This will allow you to use the full Xcode suite of tools for debugging. - -Testing on an iOS device -^^^^^^^^^^^^^^^^^^^^^^^^ - -To test on an iOS device, the app needs to be signed with known developer -credentials. To obtain these credentials, you must have an iOS Developer -account, and your Xcode install will need to be logged into your account (see -the Accounts tab of the Preferences dialog). - -Once the project is open, and you're signed into your Apple Developer account, -select the root node of the project tree (labeled "iOSTestbed"), then the -"Signing & Capabilities" tab in the details page. Select a development team -(this will likely be your own name), and plug in a physical device to your -macOS machine with a USB cable. You should then be able to select your physical -device from the list of targets in the pulldown in the Xcode titlebar. - -Running specific tests -^^^^^^^^^^^^^^^^^^^^^^ - -As the test suite is being executed on an iOS simulator, it is not possible to -pass in command line arguments to configure test suite operation. To work -around this limitation, the arguments that would normally be passed as command -line arguments are configured as part of the ``iOSTestbed-Info.plist`` file -that is used to configure the iOS testbed app. In this file, the ``TestArgs`` -key is an array containing the arguments that would be passed to ``python -m`` -on the command line (including ``test`` in position 0, the name of the test -module to be executed). - -Disabling automated breakpoints -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -By default, Xcode will inserts an automatic breakpoint whenever a signal is -raised. The Python test suite raises many of these signals as part of normal -operation; unless you are trying to diagnose an issue with signals, the -automatic breakpoints can be inconvenient. However, they can be disabled by -creating a symbolic breakpoint that is triggered at the start of the test run. - -Select "Debug > Breakpoints > Create Symbolic Breakpoint" from the Xcode menu, and -populate the new brewpoint with the following details: - -* **Name**: IgnoreSignals -* **Symbol**: UIApplicationMain -* **Action**: Add debugger commands for: - - ``process handle SIGINT -n true -p true -s false`` - - ``process handle SIGUSR1 -n true -p true -s false`` - - ``process handle SIGUSR2 -n true -p true -s false`` - - ``process handle SIGXFSZ -n true -p true -s false`` -* Check the "Automatically continue after evaluating" box. - -All other details can be left blank. When the process executes the -``UIApplicationMain`` entry point, the breakpoint will trigger, run the debugger -commands to disable the automatic breakpoints, and automatically resume. diff --git a/iOS/Resources/dylib-Info-template.plist b/iOS/Resources/dylib-Info-template.plist deleted file mode 100644 index f652e272f71c88..00000000000000 --- a/iOS/Resources/dylib-Info-template.plist +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleDevelopmentRegion</key> - <string>en</string> - <key>CFBundleExecutable</key> - <string></string> - <key>CFBundleIdentifier</key> - <string></string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundlePackageType</key> - <string>APPL</string> - <key>CFBundleShortVersionString</key> - <string>1.0</string> - <key>CFBundleSupportedPlatforms</key> - <array> - <string>iPhoneOS</string> - </array> - <key>MinimumOSVersion</key> - <string>12.0</string> - <key>CFBundleVersion</key> - <string>1</string> -</dict> -</plist> diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py deleted file mode 100644 index 1146bf3b988cda..00000000000000 --- a/iOS/testbed/__main__.py +++ /dev/null @@ -1,548 +0,0 @@ -import argparse -import asyncio -import fcntl -import json -import os -import plistlib -import re -import shutil -import subprocess -import sys -import tempfile -from contextlib import asynccontextmanager -from datetime import datetime -from pathlib import Path - - -DECODE_ARGS = ("UTF-8", "backslashreplace") - -# The system log prefixes each line: -# 2025-01-17 16:14:29.090 Df iOSTestbed[23987:1fd393b4] (Python) ... -# 2025-01-17 16:14:29.090 E iOSTestbed[23987:1fd393b4] (Python) ... - -LOG_PREFIX_REGEX = re.compile( - r"^\d{4}-\d{2}-\d{2}" # YYYY-MM-DD - r"\s+\d+:\d{2}:\d{2}\.\d+" # HH:MM:SS.sss - r"\s+\w+" # Df/E - r"\s+iOSTestbed\[\d+:\w+\]" # Process/thread ID - r"\s+\(Python\)\s" # Logger name -) - - -# Work around a bug involving sys.exit and TaskGroups -# (https://github.com/python/cpython/issues/101515). -def exit(*args): - raise MySystemExit(*args) - - -class MySystemExit(Exception): - pass - - -class SimulatorLock: - # An fcntl-based filesystem lock that can be used to ensure that - def __init__(self, timeout): - self.filename = Path(tempfile.gettempdir()) / "python-ios-testbed" - self.timeout = timeout - - self.fd = None - - async def acquire(self): - # Ensure the lockfile exists - self.filename.touch(exist_ok=True) - - # Try `timeout` times to acquire the lock file, with a 1 second pause - # between each attempt. Report status every 10 seconds. - for i in range(0, self.timeout): - try: - fd = os.open(self.filename, os.O_RDWR | os.O_TRUNC, 0o644) - fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) - except OSError: - os.close(fd) - if i % 10 == 0: - print("... waiting", flush=True) - await asyncio.sleep(1) - else: - self.fd = fd - return - - # If we reach the end of the loop, we've exceeded the allowed number of - # attempts. - raise ValueError("Unable to obtain lock on iOS simulator creation") - - def release(self): - # If a lock is held, release it. - if self.fd is not None: - # Release the lock. - fcntl.flock(self.fd, fcntl.LOCK_UN) - os.close(self.fd) - self.fd = None - - -# All subprocesses are executed through this context manager so that no matter -# what happens, they can always be cancelled from another task, and they will -# always be cleaned up on exit. -@asynccontextmanager -async def async_process(*args, **kwargs): - process = await asyncio.create_subprocess_exec(*args, **kwargs) - try: - yield process - finally: - if process.returncode is None: - # Allow a reasonably long time for Xcode to clean itself up, - # because we don't want stale emulators left behind. - timeout = 10 - process.terminate() - try: - await asyncio.wait_for(process.wait(), timeout) - except TimeoutError: - print( - f"Command {args} did not terminate after {timeout} seconds " - f" - sending SIGKILL" - ) - process.kill() - - # Even after killing the process we must still wait for it, - # otherwise we'll get the warning "Exception ignored in __del__". - await asyncio.wait_for(process.wait(), timeout=1) - - -async def async_check_output(*args, **kwargs): - async with async_process( - *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs - ) as process: - stdout, stderr = await process.communicate() - if process.returncode == 0: - return stdout.decode(*DECODE_ARGS) - else: - raise subprocess.CalledProcessError( - process.returncode, - args, - stdout.decode(*DECODE_ARGS), - stderr.decode(*DECODE_ARGS), - ) - - -# Select a simulator device to use. -async def select_simulator_device(): - # List the testing simulators, in JSON format - raw_json = await async_check_output( - "xcrun", "simctl", "list", "-j" - ) - json_data = json.loads(raw_json) - - # Any device will do; we'll look for "SE" devices - but the name isn't - # consistent over time. Older Xcode versions will use "iPhone SE (Nth - # generation)"; As of 2025, they've started using "iPhone 16e". - # - # When Xcode is updated after a new release, new devices will be available - # and old ones will be dropped from the set available on the latest iOS - # version. Select the one with the highest minimum runtime version - this - # is an indicator of the "newest" released device, which should always be - # supported on the "most recent" iOS version. - se_simulators = sorted( - (devicetype["minRuntimeVersion"], devicetype["name"]) - for devicetype in json_data["devicetypes"] - if devicetype["productFamily"] == "iPhone" - and ( - ("iPhone " in devicetype["name"] and devicetype["name"].endswith("e")) - or "iPhone SE " in devicetype["name"] - ) - ) - - return se_simulators[-1][1] - - -# Return a list of UDIDs associated with booted simulators -async def list_devices(): - try: - # List the testing simulators, in JSON format - raw_json = await async_check_output( - "xcrun", "simctl", "--set", "testing", "list", "-j" - ) - json_data = json.loads(raw_json) - - # Filter out the booted iOS simulators - return [ - simulator["udid"] - for runtime, simulators in json_data["devices"].items() - for simulator in simulators - if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" - ] - except subprocess.CalledProcessError as e: - # If there's no ~/Library/Developer/XCTestDevices folder (which is the - # case on fresh installs, and in some CI environments), `simctl list` - # returns error code 1, rather than an empty list. Handle that case, - # but raise all other errors. - if e.returncode == 1: - return [] - else: - raise - - -async def find_device(initial_devices, lock): - while True: - new_devices = set(await list_devices()).difference(initial_devices) - if len(new_devices) == 0: - await asyncio.sleep(1) - elif len(new_devices) == 1: - udid = new_devices.pop() - print(f"{datetime.now():%Y-%m-%d %H:%M:%S}: New test simulator detected") - print(f"UDID: {udid}", flush=True) - lock.release() - return udid - else: - exit(f"Found more than one new device: {new_devices}") - - -async def log_stream_task(initial_devices, lock): - # Wait up to 5 minutes for the build to complete and the simulator to boot. - udid = await asyncio.wait_for(find_device(initial_devices, lock), 5 * 60) - - # Stream the iOS device's logs, filtering out messages that come from the - # XCTest test suite (catching NSLog messages from the test method), or - # Python itself (catching stdout/stderr content routed to the system log - # with config->use_system_logger). - args = [ - "xcrun", - "simctl", - "--set", - "testing", - "spawn", - udid, - "log", - "stream", - "--style", - "compact", - "--predicate", - ( - 'senderImagePath ENDSWITH "/iOSTestbedTests.xctest/iOSTestbedTests"' - ' OR senderImagePath ENDSWITH "/Python.framework/Python"' - ), - ] - - async with async_process( - *args, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) as process: - suppress_dupes = False - while line := (await process.stdout.readline()).decode(*DECODE_ARGS): - # Strip the prefix from each log line - line = LOG_PREFIX_REGEX.sub("", line) - # The iOS log streamer can sometimes lag; when it does, it outputs - # a warning about messages being dropped... often multiple times. - # Only print the first of these duplicated warnings. - if line.startswith("=== Messages dropped "): - if not suppress_dupes: - suppress_dupes = True - sys.stdout.write(line) - else: - suppress_dupes = False - sys.stdout.write(line) - sys.stdout.flush() - - -async def xcode_test(location, simulator, verbose): - # Run the test suite on the named simulator - print("Starting xcodebuild...", flush=True) - args = [ - "xcodebuild", - "test", - "-project", - str(location / "iOSTestbed.xcodeproj"), - "-scheme", - "iOSTestbed", - "-destination", - f"platform=iOS Simulator,name={simulator}", - "-resultBundlePath", - str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"), - "-derivedDataPath", - str(location / "DerivedData"), - ] - if not verbose: - args += ["-quiet"] - - async with async_process( - *args, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) as process: - while line := (await process.stdout.readline()).decode(*DECODE_ARGS): - sys.stdout.write(line) - sys.stdout.flush() - - status = await asyncio.wait_for(process.wait(), timeout=1) - exit(status) - - -def clone_testbed( - source: Path, - target: Path, - framework: Path, - apps: list[Path], -) -> None: - if target.exists(): - print(f"{target} already exists; aborting without creating project.") - sys.exit(10) - - if framework is None: - if not ( - source / "Python.xcframework/ios-arm64_x86_64-simulator/bin" - ).is_dir(): - print( - f"The testbed being cloned ({source}) does not contain " - f"a simulator framework. Re-run with --framework" - ) - sys.exit(11) - else: - if not framework.is_dir(): - print(f"{framework} does not exist.") - sys.exit(12) - elif not ( - framework.suffix == ".xcframework" - or (framework / "Python.framework").is_dir() - ): - print( - f"{framework} is not an XCframework, " - f"or a simulator slice of a framework build." - ) - sys.exit(13) - - print("Cloning testbed project:") - print(f" Cloning {source}...", end="", flush=True) - shutil.copytree(source, target, symlinks=True) - print(" done") - - xc_framework_path = target / "Python.xcframework" - sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator" - if framework is not None: - if framework.suffix == ".xcframework": - print(" Installing XCFramework...", end="", flush=True) - if xc_framework_path.is_dir(): - shutil.rmtree(xc_framework_path) - else: - xc_framework_path.unlink(missing_ok=True) - xc_framework_path.symlink_to( - framework.relative_to(xc_framework_path.parent, walk_up=True) - ) - print(" done") - else: - print(" Installing simulator framework...", end="", flush=True) - if sim_framework_path.is_dir(): - shutil.rmtree(sim_framework_path) - else: - sim_framework_path.unlink(missing_ok=True) - sim_framework_path.symlink_to( - framework.relative_to(sim_framework_path.parent, walk_up=True) - ) - print(" done") - else: - if ( - xc_framework_path.is_symlink() - and not xc_framework_path.readlink().is_absolute() - ): - # XCFramework is a relative symlink. Rewrite the symlink relative - # to the new location. - print(" Rewriting symlink to XCframework...", end="", flush=True) - orig_xc_framework_path = ( - source - / xc_framework_path.readlink() - ).resolve() - xc_framework_path.unlink() - xc_framework_path.symlink_to( - orig_xc_framework_path.relative_to( - xc_framework_path.parent, walk_up=True - ) - ) - print(" done") - elif ( - sim_framework_path.is_symlink() - and not sim_framework_path.readlink().is_absolute() - ): - print(" Rewriting symlink to simulator framework...", end="", flush=True) - # Simulator framework is a relative symlink. Rewrite the symlink - # relative to the new location. - orig_sim_framework_path = ( - source - / "Python.XCframework" - / sim_framework_path.readlink() - ).resolve() - sim_framework_path.unlink() - sim_framework_path.symlink_to( - orig_sim_framework_path.relative_to( - sim_framework_path.parent, walk_up=True - ) - ) - print(" done") - else: - print(" Using pre-existing iOS framework.") - - for app_src in apps: - print(f" Installing app {app_src.name!r}...", end="", flush=True) - app_target = target / f"iOSTestbed/app/{app_src.name}" - if app_target.is_dir(): - shutil.rmtree(app_target) - shutil.copytree(app_src, app_target) - print(" done") - - print(f"Successfully cloned testbed: {target.resolve()}") - - -def update_plist(testbed_path, args): - # Add the test runner arguments to the testbed's Info.plist file. - info_plist = testbed_path / "iOSTestbed" / "iOSTestbed-Info.plist" - with info_plist.open("rb") as f: - info = plistlib.load(f) - - info["TestArgs"] = args - - with info_plist.open("wb") as f: - plistlib.dump(info, f) - - -async def run_testbed(simulator: str | None, args: list[str], verbose: bool=False): - location = Path(__file__).parent - print("Updating plist...", end="", flush=True) - update_plist(location, args) - print(" done.", flush=True) - - if simulator is None: - simulator = await select_simulator_device() - print(f"Running test on {simulator}", flush=True) - - # We need to get an exclusive lock on simulator creation, to avoid issues - # with multiple simulators starting and being unable to tell which - # simulator is due to which testbed instance. See - # https://github.com/python/cpython/issues/130294 for details. Wait up to - # 10 minutes for a simulator to boot. - print("Obtaining lock on simulator creation...", flush=True) - simulator_lock = SimulatorLock(timeout=10*60) - await simulator_lock.acquire() - print("Simulator lock acquired.", flush=True) - - # Get the list of devices that are booted at the start of the test run. - # The simulator started by the test suite will be detected as the new - # entry that appears on the device list. - initial_devices = await list_devices() - - try: - async with asyncio.TaskGroup() as tg: - tg.create_task(log_stream_task(initial_devices, simulator_lock)) - tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose)) - except* MySystemExit as e: - raise SystemExit(*e.exceptions[0].args) from None - except* subprocess.CalledProcessError as e: - # Extract it from the ExceptionGroup so it can be handled by `main`. - raise e.exceptions[0] - finally: - simulator_lock.release() - - -def main(): - parser = argparse.ArgumentParser( - description=( - "Manages the process of testing a Python project in the iOS simulator." - ), - ) - - subcommands = parser.add_subparsers(dest="subcommand") - - clone = subcommands.add_parser( - "clone", - description=( - "Clone the testbed project, copying in an iOS Python framework and" - "any specified application code." - ), - help="Clone a testbed project to a new location.", - ) - clone.add_argument( - "--framework", - help=( - "The location of the XCFramework (or simulator-only slice of an " - "XCFramework) to use when running the testbed" - ), - ) - clone.add_argument( - "--app", - dest="apps", - action="append", - default=[], - help="The location of any code to include in the testbed project", - ) - clone.add_argument( - "location", - help="The path where the testbed will be cloned.", - ) - - run = subcommands.add_parser( - "run", - usage="%(prog)s [-h] [--simulator SIMULATOR] -- <test arg> [<test arg> ...]", - description=( - "Run a testbed project. The arguments provided after `--` will be " - "passed to the running iOS process as if they were arguments to " - "`python -m`." - ), - help="Run a testbed project", - ) - run.add_argument( - "--simulator", - help=( - "The name of the simulator to use (eg: 'iPhone 16e'). Defaults to ", - "the most recently released 'entry level' iPhone device." - ) - ) - run.add_argument( - "-v", "--verbose", - action="store_true", - help="Enable verbose output", - ) - - try: - pos = sys.argv.index("--") - testbed_args = sys.argv[1:pos] - test_args = sys.argv[pos + 1 :] - except ValueError: - testbed_args = sys.argv[1:] - test_args = [] - - context = parser.parse_args(testbed_args) - - if context.subcommand == "clone": - clone_testbed( - source=Path(__file__).parent.resolve(), - target=Path(context.location).resolve(), - framework=Path(context.framework).resolve() if context.framework else None, - apps=[Path(app) for app in context.apps], - ) - elif context.subcommand == "run": - if test_args: - if not ( - Path(__file__).parent / "Python.xcframework/ios-arm64_x86_64-simulator/bin" - ).is_dir(): - print( - f"Testbed does not contain a compiled iOS framework. Use " - f"`python {sys.argv[0]} clone ...` to create a runnable " - f"clone of this testbed." - ) - sys.exit(20) - - asyncio.run( - run_testbed( - simulator=context.simulator, - verbose=context.verbose, - args=test_args, - ) - ) - else: - print(f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)") - print() - parser.print_help(sys.stderr) - sys.exit(21) - else: - parser.print_help(sys.stderr) - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/iOS/testbed/iOSTestbed/app/README b/iOS/testbed/iOSTestbed/app/README deleted file mode 100644 index af22c685f87976..00000000000000 --- a/iOS/testbed/iOSTestbed/app/README +++ /dev/null @@ -1,7 +0,0 @@ -This folder can contain any Python application code. - -During the build, any binary modules found in this folder will be processed into -iOS Framework form. - -When the test suite runs, this folder will be on the PYTHONPATH, and will be the -working directory for the test suite. diff --git a/iOS/testbed/iOSTestbed/app_packages/README b/iOS/testbed/iOSTestbed/app_packages/README deleted file mode 100644 index 42d7fdeb813250..00000000000000 --- a/iOS/testbed/iOSTestbed/app_packages/README +++ /dev/null @@ -1,7 +0,0 @@ -This folder can be a target for installing any Python dependencies needed by the -test suite. - -During the build, any binary modules found in this folder will be processed into -iOS Framework form. - -When the test suite runs, this folder will be on the PYTHONPATH. diff --git a/iOS/testbed/iOSTestbed/dylib-Info-template.plist b/iOS/testbed/iOSTestbed/dylib-Info-template.plist deleted file mode 100644 index f652e272f71c88..00000000000000 --- a/iOS/testbed/iOSTestbed/dylib-Info-template.plist +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleDevelopmentRegion</key> - <string>en</string> - <key>CFBundleExecutable</key> - <string></string> - <key>CFBundleIdentifier</key> - <string></string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundlePackageType</key> - <string>APPL</string> - <key>CFBundleShortVersionString</key> - <string>1.0</string> - <key>CFBundleSupportedPlatforms</key> - <array> - <string>iPhoneOS</string> - </array> - <key>MinimumOSVersion</key> - <string>12.0</string> - <key>CFBundleVersion</key> - <string>1</string> -</dict> -</plist> diff --git a/iOS/testbed/iOSTestbed/iOSTestbed-Info.plist b/iOS/testbed/iOSTestbed/iOSTestbed-Info.plist deleted file mode 100644 index a582f42a212783..00000000000000 --- a/iOS/testbed/iOSTestbed/iOSTestbed-Info.plist +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleDevelopmentRegion</key> - <string>en</string> - <key>CFBundleDisplayName</key> - <string>${PRODUCT_NAME}</string> - <key>CFBundleExecutable</key> - <string>${EXECUTABLE_NAME}</string> - <key>CFBundleIdentifier</key> - <string>org.python.iOSTestbed</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>${PRODUCT_NAME}</string> - <key>CFBundlePackageType</key> - <string>APPL</string> - <key>CFBundleShortVersionString</key> - <string>1.0</string> - <key>CFBundleSignature</key> - <string>????</string> - <key>CFBundleVersion</key> - <string>1</string> - <key>LSRequiresIPhoneOS</key> - <true/> - <key>UIRequiresFullScreen</key> - <true/> - <key>UILaunchStoryboardName</key> - <string>Launch Screen</string> - <key>UISupportedInterfaceOrientations</key> - <array> - <string>UIInterfaceOrientationPortrait</string> - <string>UIInterfaceOrientationLandscapeLeft</string> - <string>UIInterfaceOrientationLandscapeRight</string> - </array> - <key>UISupportedInterfaceOrientations~ipad</key> - <array> - <string>UIInterfaceOrientationPortrait</string> - <string>UIInterfaceOrientationPortraitUpsideDown</string> - <string>UIInterfaceOrientationLandscapeLeft</string> - <string>UIInterfaceOrientationLandscapeRight</string> - </array> - <key>TestArgs</key> - <array> - <string>test</string> <!-- Invoke "python -m test" --> - <string>-uall</string> <!-- Enable all resources --> - <string>--single-process</string> <!-- always run all tests sequentially in a single process --> - <string>--rerun</string> <!-- Re-run failed tests in verbose mode --> - <string>-W</string> <!-- Display test output on failure --> - <!-- To run a subset of tests, add the test names below; e.g., - <string>test_os</string> - <string>test_sys</string> - --> - </array> - <key>UIApplicationSceneManifest</key> - <dict> - <key>UIApplicationSupportsMultipleScenes</key> - <false/> - <key>UISceneConfigurations</key> - <dict/> - </dict> -</dict> -</plist> diff --git a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m deleted file mode 100644 index b502a6eb277b0b..00000000000000 --- a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m +++ /dev/null @@ -1,193 +0,0 @@ -#import <XCTest/XCTest.h> -#import <Python/Python.h> - -@interface iOSTestbedTests : XCTestCase - -@end - -@implementation iOSTestbedTests - - -- (void)testPython { - const char **argv; - int exit_code; - int failed; - PyStatus status; - PyPreConfig preconfig; - PyConfig config; - PyObject *app_packages_path; - PyObject *method_args; - PyObject *result; - PyObject *site_module; - PyObject *site_addsitedir_attr; - PyObject *sys_module; - PyObject *sys_path_attr; - NSArray *test_args; - NSString *python_home; - NSString *path; - wchar_t *wtmp_str; - - NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; - - // Set some other common environment indicators to disable color, as the - // Xcode log can't display color. Stdout will report that it is *not* a - // TTY. - setenv("NO_COLOR", "1", true); - setenv("PYTHON_COLORS", "0", true); - - // Arguments to pass into the test suite runner. - // argv[0] must identify the process; any subsequent arg - // will be handled as if it were an argument to `python -m test` - test_args = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"TestArgs"]; - if (test_args == NULL) { - NSLog(@"Unable to identify test arguments."); - } - argv = malloc(sizeof(char *) * ([test_args count] + 1)); - argv[0] = "iOSTestbed"; - for (int i = 1; i < [test_args count]; i++) { - argv[i] = [[test_args objectAtIndex:i] UTF8String]; - } - NSLog(@"Test command: %@", test_args); - - // Generate an isolated Python configuration. - NSLog(@"Configuring isolated Python..."); - PyPreConfig_InitIsolatedConfig(&preconfig); - PyConfig_InitIsolatedConfig(&config); - - // Configure the Python interpreter: - // Enforce UTF-8 encoding for stderr, stdout, file-system encoding and locale. - // See https://docs.python.org/3/library/os.html#python-utf-8-mode. - preconfig.utf8_mode = 1; - // Use the system logger for stdout/err - config.use_system_logger = 1; - // Don't buffer stdio. We want output to appears in the log immediately - config.buffered_stdio = 0; - // Don't write bytecode; we can't modify the app bundle - // after it has been signed. - config.write_bytecode = 0; - // Ensure that signal handlers are installed - config.install_signal_handlers = 1; - // Run the test module. - config.run_module = Py_DecodeLocale([[test_args objectAtIndex:0] UTF8String], NULL); - // For debugging - enable verbose mode. - // config.verbose = 1; - - NSLog(@"Pre-initializing Python runtime..."); - status = Py_PreInitialize(&preconfig); - if (PyStatus_Exception(status)) { - XCTFail(@"Unable to pre-initialize Python interpreter: %s", status.err_msg); - PyConfig_Clear(&config); - return; - } - - // Set the home for the Python interpreter - python_home = [NSString stringWithFormat:@"%@/python", resourcePath, nil]; - NSLog(@"PythonHome: %@", python_home); - wtmp_str = Py_DecodeLocale([python_home UTF8String], NULL); - status = PyConfig_SetString(&config, &config.home, wtmp_str); - if (PyStatus_Exception(status)) { - XCTFail(@"Unable to set PYTHONHOME: %s", status.err_msg); - PyConfig_Clear(&config); - return; - } - PyMem_RawFree(wtmp_str); - - // Read the site config - status = PyConfig_Read(&config); - if (PyStatus_Exception(status)) { - XCTFail(@"Unable to read site config: %s", status.err_msg); - PyConfig_Clear(&config); - return; - } - - NSLog(@"Configure argc/argv..."); - status = PyConfig_SetBytesArgv(&config, [test_args count], (char**) argv); - if (PyStatus_Exception(status)) { - XCTFail(@"Unable to configure argc/argv: %s", status.err_msg); - PyConfig_Clear(&config); - return; - } - - NSLog(@"Initializing Python runtime..."); - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - XCTFail(@"Unable to initialize Python interpreter: %s", status.err_msg); - PyConfig_Clear(&config); - return; - } - - // Add app_packages as a site directory. This both adds to sys.path, - // and ensures that any .pth files in that directory will be executed. - site_module = PyImport_ImportModule("site"); - if (site_module == NULL) { - XCTFail(@"Could not import site module"); - return; - } - - site_addsitedir_attr = PyObject_GetAttrString(site_module, "addsitedir"); - if (site_addsitedir_attr == NULL || !PyCallable_Check(site_addsitedir_attr)) { - XCTFail(@"Could not access site.addsitedir"); - return; - } - - path = [NSString stringWithFormat:@"%@/app_packages", resourcePath, nil]; - NSLog(@"App packages path: %@", path); - wtmp_str = Py_DecodeLocale([path UTF8String], NULL); - app_packages_path = PyUnicode_FromWideChar(wtmp_str, wcslen(wtmp_str)); - if (app_packages_path == NULL) { - XCTFail(@"Could not convert app_packages path to unicode"); - return; - } - PyMem_RawFree(wtmp_str); - - method_args = Py_BuildValue("(O)", app_packages_path); - if (method_args == NULL) { - XCTFail(@"Could not create arguments for site.addsitedir"); - return; - } - - result = PyObject_CallObject(site_addsitedir_attr, method_args); - if (result == NULL) { - XCTFail(@"Could not add app_packages directory using site.addsitedir"); - return; - } - - // Add test code to sys.path - sys_module = PyImport_ImportModule("sys"); - if (sys_module == NULL) { - XCTFail(@"Could not import sys module"); - return; - } - - sys_path_attr = PyObject_GetAttrString(sys_module, "path"); - if (sys_path_attr == NULL) { - XCTFail(@"Could not access sys.path"); - return; - } - - path = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; - NSLog(@"App path: %@", path); - wtmp_str = Py_DecodeLocale([path UTF8String], NULL); - failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String])); - if (failed) { - XCTFail(@"Unable to add app to sys.path"); - return; - } - PyMem_RawFree(wtmp_str); - - // Ensure the working directory is the app folder. - chdir([path UTF8String]); - - // Start the test suite. Print a separator to differentiate Python startup logs from app logs - NSLog(@"---------------------------------------------------------------------------"); - - exit_code = Py_RunMain(); - XCTAssertEqual(exit_code, 0, @"Test suite did not pass"); - - NSLog(@"---------------------------------------------------------------------------"); - - Py_Finalize(); -} - - -@end diff --git a/pyconfig.h.in b/pyconfig.h.in index 1c533b2bfb7fb4..dc2404fe85fa23 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -32,10 +32,6 @@ /* The Android API level. */ #undef ANDROID_API_LEVEL -/* Define if C doubles are 64-bit IEEE 754 binary format, stored in ARM - mixed-endian order (byte order 45670123) */ -#undef DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754 - /* Define if C doubles are 64-bit IEEE 754 binary format, stored with the most significant byte first */ #undef DOUBLE_IS_BIG_ENDIAN_IEEE754 @@ -59,6 +55,9 @@ /* Define to 1 if you have the 'acosh' function. */ #undef HAVE_ACOSH +/* Define to 1 if you have the 'acospi' function. */ +#undef HAVE_ACOSPI + /* struct addrinfo (netdb.h) */ #undef HAVE_ADDRINFO @@ -77,12 +76,21 @@ /* Define to 1 if you have the 'asinh' function. */ #undef HAVE_ASINH +/* Define to 1 if you have the 'asinpi' function. */ +#undef HAVE_ASINPI + /* Define to 1 if you have the <asm/types.h> header file. */ #undef HAVE_ASM_TYPES_H +/* Define to 1 if you have the 'atan2pi' function. */ +#undef HAVE_ATAN2PI + /* Define to 1 if you have the 'atanh' function. */ #undef HAVE_ATANH +/* Define to 1 if you have the 'atanpi' function. */ +#undef HAVE_ATANPI + /* Define to 1 if you have the 'backtrace' function. */ #undef HAVE_BACKTRACE @@ -141,6 +149,9 @@ /* Define if you have the 'chroot' function. */ #undef HAVE_CHROOT +/* Define to 1 if you have the 'clearenv' function. */ +#undef HAVE_CLEARENV + /* Define to 1 if you have the 'clock' function. */ #undef HAVE_CLOCK @@ -180,6 +191,9 @@ /* Define to 1 if you have the 'copy_file_range' function. */ #undef HAVE_COPY_FILE_RANGE +/* Define to 1 if you have the 'cospi' function. */ +#undef HAVE_COSPI + /* Define to 1 if you have the 'ctermid' function. */ #undef HAVE_CTERMID @@ -225,6 +239,10 @@ /* Define to 1 if you have the <db.h> header file. */ #undef HAVE_DB_H +/* Define to 1 if you have the declaration of 'PR_SET_VMA_ANON_NAME', and to 0 + if you don't. */ +#undef HAVE_DECL_PR_SET_VMA_ANON_NAME + /* Define to 1 if you have the declaration of 'RTLD_DEEPBIND', and to 0 if you don't. */ #undef HAVE_DECL_RTLD_DEEPBIND @@ -727,6 +745,9 @@ /* Define to 1 if you have the <linux/can.h> header file. */ #undef HAVE_LINUX_CAN_H +/* Define to 1 if you have the <linux/can/isotp.h> header file. */ +#undef HAVE_LINUX_CAN_ISOTP_H + /* Define to 1 if you have the <linux/can/j1939.h> header file. */ #undef HAVE_LINUX_CAN_J1939_H @@ -969,6 +990,9 @@ function. */ #undef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP +/* Define to 1 if you have the 'ppoll' function. */ +#undef HAVE_PPOLL + /* Define to 1 if you have the 'pread' function. */ #undef HAVE_PREAD @@ -1246,6 +1270,9 @@ /* Define to 1 if you have the 'sigwaitinfo' function. */ #undef HAVE_SIGWAITINFO +/* Define to 1 if you have the 'sinpi' function. */ +#undef HAVE_SINPI + /* Define to 1 if you have the 'snprintf' function. */ #undef HAVE_SNPRINTF @@ -1279,6 +1306,9 @@ /* Define to 1 if you have the 'statvfs' function. */ #undef HAVE_STATVFS +/* Define to 1 if you have the 'statx' function. */ +#undef HAVE_STATX + /* Define if you have struct stat.st_mtim.tv_nsec */ #undef HAVE_STAT_TV_NSEC @@ -1321,6 +1351,27 @@ /* Define to 1 if 'pw_passwd' is a member of 'struct passwd'. */ #undef HAVE_STRUCT_PASSWD_PW_PASSWD +/* Define to 1 if 'stx_atomic_write_unit_max_opt' is a member of 'struct + statx'. */ +#undef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT + +/* Define to 1 if 'stx_atomic_write_unit_min' is a member of 'struct statx'. + */ +#undef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MIN + +/* Define to 1 if 'stx_dio_mem_align' is a member of 'struct statx'. */ +#undef HAVE_STRUCT_STATX_STX_DIO_MEM_ALIGN + +/* Define to 1 if 'stx_dio_read_offset_align' is a member of 'struct statx'. + */ +#undef HAVE_STRUCT_STATX_STX_DIO_READ_OFFSET_ALIGN + +/* Define to 1 if 'stx_mnt_id' is a member of 'struct statx'. */ +#undef HAVE_STRUCT_STATX_STX_MNT_ID + +/* Define to 1 if 'stx_subvol' is a member of 'struct statx'. */ +#undef HAVE_STRUCT_STATX_STX_SUBVOL + /* Define to 1 if 'st_birthtime' is a member of 'struct stat'. */ #undef HAVE_STRUCT_STAT_ST_BIRTHTIME @@ -1494,6 +1545,9 @@ /* Define to 1 if you have the <sys/xattr.h> header file. */ #undef HAVE_SYS_XATTR_H +/* Define to 1 if you have the 'tanpi' function. */ +#undef HAVE_TANPI + /* Define to 1 if you have the 'tcgetpgrp' function. */ #undef HAVE_TCGETPGRP @@ -1699,6 +1753,9 @@ /* Define as the preferred size in bits of long digits */ #undef PYLONG_BITS_IN_DIGIT +/* Define to use huge pages for pymalloc arenas */ +#undef PYMALLOC_USE_HUGEPAGES + /* enabled builtin hash modules */ #undef PY_BUILTIN_HASHLIB_HASHES @@ -1749,9 +1806,6 @@ /* The version of SunOS/Solaris as reported by `uname -r' without the dot. */ #undef Py_SUNOS_VERSION -/* Define if you want to use tail-calling interpreters in CPython. */ -#undef Py_TAIL_CALL_INTERP - /* Define if you want to enable tracing references for debugging purpose */ #undef Py_TRACE_REFS @@ -2002,7 +2056,7 @@ /* Define on NetBSD to activate all library features */ #undef _NETBSD_SOURCE -/* Define to activate features from IEEE Stds 1003.1-2008 */ +/* Define to activate features from IEEE Std 1003.1-2024 */ #undef _POSIX_C_SOURCE /* Define if you have POSIX threads, and your system does not define that. */ @@ -2023,8 +2077,26 @@ /* HACL* library can compile SIMD256 implementations */ #undef _Py_HACL_CAN_COMPILE_VEC256 -/* Define if year with century should be normalized for strftime. */ -#undef _Py_NORMALIZE_CENTURY +/* Define if compiler supports __builtin_shufflevector with 128-bit vectors + AND the target architecture has native SIMD (not just API availability) */ +#undef _Py_HAVE_EFFICIENT_BUILTIN_SHUFFLEVECTOR + +/* Define to 1 if libgcc __register_frame and __deregister_frame are linkable. + */ +#undef _Py_HAVE_LIBGCC_EH_FRAME_REGISTRATION + +/* Define if you have the 'PR_SET_VMA_ANON_NAME' constant. */ +#undef _Py_HAVE_PR_SET_VMA_ANON_NAME + +/* Define to 1 if the machine stack grows down (default); 0 if it grows up. */ +#undef _Py_STACK_GROWS_DOWN + +/* Define if you want to use tail-calling interpreters in CPython. */ +#undef _Py_TAIL_CALL_INTERP + +/* Define to 1 if frame unwinding via pointers is expected to work, 0 if not. + Leave undefined if unknown. */ +#undef _Py_WITH_FRAME_POINTERS /* Define to force use of thread-safe errno, h_errno, and other functions */ #undef _REENTRANT